Whamcloud - gitweb
LU-2730 mdt: fix erroneous LASSERT in mdt_reint_opcode
[fs/lustre-release.git] / libsysio / src / mount.c
1 /*
2  *    This Cplant(TM) source code is the property of Sandia National
3  *    Laboratories.
4  *
5  *    This Cplant(TM) source code is copyrighted by Sandia National
6  *    Laboratories.
7  *
8  *    The redistribution of this Cplant(TM) source code is subject to the
9  *    terms of the GNU Lesser General Public License
10  *    (see cit/LGPL or http://www.gnu.org/licenses/lgpl.html)
11  *
12  *    Cplant(TM) Copyright 1998-2004 Sandia Corporation. 
13  *    Under the terms of Contract DE-AC04-94AL85000, there is a non-exclusive
14  *    license for use of this work by or on behalf of the US Government.
15  *    Export of this program may require a license from the United States
16  *    Government.
17  */
18
19 /*
20  * This library is free software; you can redistribute it and/or
21  * modify it under the terms of the GNU Lesser General Public
22  * License as published by the Free Software Foundation; either
23  * version 2.1 of the License, or (at your option) any later version.
24  * 
25  * This library is distributed in the hope that it will be useful,
26  * but WITHOUT ANY WARRANTY; without even the implied warranty of
27  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
28  * Lesser General Public License for more details.
29  * 
30  * You should have received a copy of the GNU Lesser General Public
31  * License along with this library; if not, write to the Free Software
32  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
33  *
34  * Questions or comments about this library should be sent to:
35  *
36  * Lee Ward
37  * Sandia National Laboratories, New Mexico
38  * P.O. Box 5800
39  * Albuquerque, NM 87185-1110
40  *
41  * lee@sandia.gov
42  */
43
44 #include <stdlib.h>
45 #include <string.h>
46 #include <errno.h>
47 #include <assert.h>
48 #include <sys/types.h>
49 #include <sys/stat.h>
50 #ifdef AUTOMOUNT_FILE_NAME
51 #include <fcntl.h>
52 #include <sys/uio.h>
53 #endif
54 #include <sys/queue.h>
55
56 #include "sysio.h"
57 #include "xtio.h"
58 #include "fs.h"
59 #include "mount.h"
60 #include "inode.h"
61
62 /*
63  * File system and volume mount support.
64  */
65
66 #ifdef AUTOMOUNT_FILE_NAME
67 /*
68  * Name of autmount specification file in a directory with
69  * the sticky-bit set.
70  */
71 struct qstr _sysio_mount_file_name = { "", 0, 0 };
72 #endif
73
74 /*
75  * Active mounts.
76  */
77 static LIST_HEAD(, mount) mounts;
78
79 static int _sysio_sub_fsswop_mount(const char *source,
80                                    unsigned flags,
81                                    const void *data,
82                                    struct pnode *tocover,
83                                    struct mount **mntp);
84
85 static struct fssw_ops _sysio_sub_fssw_ops = {
86         _sysio_sub_fsswop_mount
87 };
88
89 /*
90  * Initialization. Must be called before any other routine in this module.
91  */
92 int
93 _sysio_mount_init()
94 {
95         int     err;
96
97         LIST_INIT(&mounts);
98 #ifdef AUTOMOUNT_FILE_NAME
99         _sysio_next_component(AUTOMOUNT_FILE_NAME, &_sysio_mount_file_name);
100 #endif
101
102         /*
103          * Register the sub-trees "file system" driver.
104          */
105         err = _sysio_fssw_register("sub", &_sysio_sub_fssw_ops);
106         if (err)
107                 return err;
108
109         return 0;
110 }
111
112 /*
113  * Mount rooted sub-tree somewhere in the existing name space.
114  */
115 int
116 _sysio_do_mount(struct filesys *fs,
117                 struct pnode_base *rootpb,
118                 unsigned flags,
119                 struct pnode *tocover,
120                 struct mount **mntp)
121 {
122         struct mount *mnt;
123         int     err;
124
125         /*
126          * It's really poor form to allow the new root to be a
127          * descendant of the pnode being covered.
128          */
129         if (tocover) {
130                 struct pnode_base *pb;
131
132                 for (pb = rootpb;
133                      pb && pb != tocover->p_base;
134                      pb = pb->pb_parent)
135                         ;
136                 if (pb == tocover->p_base)
137                         return -EBUSY;
138         }
139
140         /*
141          * Alloc
142          */
143         mnt = malloc(sizeof(struct mount));
144         if (!mnt)
145                 return -ENOMEM;
146         err = 0;
147         /*
148          * Init enough to make the mount record usable to the path node
149          * generation routines.
150          */
151         mnt->mnt_fs = fs;
152         if (fs->fs_flags & FS_F_RO) {
153                 /*
154                  * Propagate the read-only flag -- Whether they set it or not.
155                  */
156                 flags |= MOUNT_F_RO;
157         }
158         mnt->mnt_flags = flags;
159         /*
160          * Get alias for the new root.
161          */
162         mnt->mnt_root =
163             _sysio_p_new_alias(tocover ? tocover->p_parent : NULL, rootpb, mnt);
164         if (!mnt->mnt_root) {
165                 err = -ENOMEM;
166                 goto error;
167         }
168         /*
169          * It may have been a while since the root inode was validated;
170          * better validate again.  And it better be a directory!
171          */
172         err = _sysio_p_validate(mnt->mnt_root, NULL, NULL);
173         if (err)
174                 goto error;
175
176         if (!S_ISDIR(mnt->mnt_root->p_base->pb_ino->i_stbuf.st_mode)) {
177                 err = -ENOTDIR;
178                 goto error;
179         }
180         /*
181          * Cover up the mount point.
182          */
183         mnt->mnt_covers = tocover;
184         if (!mnt->mnt_covers) {
185                 /*
186                  * New graph; It covers itself.
187                  */
188                 mnt->mnt_covers = tocover = mnt->mnt_root;
189         }
190         assert(!tocover->p_cover);
191         tocover->p_cover = mnt->mnt_root;
192
193         LIST_INSERT_HEAD(&mounts, mnt, mnt_link);
194
195         *mntp = mnt;
196         return 0;
197
198 error:
199         if (mnt->mnt_root) {
200                 P_RELE(mnt->mnt_root);
201                 _sysio_p_prune(mnt->mnt_root);
202         }
203         free(mnt);
204         return err;
205 }
206
207 /*
208  * Remove mounted sub-tree from the system.
209  */
210 int
211 _sysio_do_unmount(struct mount *mnt)
212 {
213         struct pnode *root;
214         struct filesys *fs;
215
216         root = mnt->mnt_root;
217         if (root->p_cover && root->p_cover != root) {
218                 /*
219                  * Active mount.
220                  */
221                 return -EBUSY;
222         }
223         assert(mnt->mnt_covers->p_cover == root);
224         if (_sysio_p_prune(root) != 1) {
225                 /*
226                  * Active aliases.
227                  */
228                 return -EBUSY;
229         }
230         /*
231          * We're committed.
232          *
233          * Drop ref of covered pnode and break linkage in name space.
234          */
235         if (root->p_cover != root)
236                 P_RELE(mnt->mnt_covers);
237         mnt->mnt_covers->p_cover = NULL;
238         LIST_REMOVE(mnt, mnt_link);
239         /*
240          * Kill the root.
241          */
242         P_RELE(root);
243         root->p_cover = NULL;
244         _sysio_p_gone(root);
245         /*
246          * Release mount record resource.
247          */
248         fs = mnt->mnt_fs;
249         free(mnt);
250         FS_RELE(fs);
251
252         return 0;
253 }
254
255 /*
256  * Establish the system name space.
257  */
258 int
259 _sysio_mount_root(const char *source,
260                   const char *fstype,
261                   unsigned flags,
262                   const void *data)
263 {
264         struct fsswent *fssw;
265         int     err;
266         struct mount *mnt;
267
268         if (_sysio_root)
269                 return -EBUSY;
270
271         fssw = _sysio_fssw_lookup(fstype);
272         if (!fssw)
273                 return -ENODEV;
274
275         err = (*fssw->fssw_ops.fsswop_mount)(source, flags, data, NULL, &mnt);
276         if (err)
277                 return err;
278
279         _sysio_root = mnt->mnt_root;
280 #ifndef DEFER_INIT_CWD
281         /*
282          * It is very annoying to have to set the current working directory.
283          * So... If it isn't set, make it the root now.
284          */
285         if (!_sysio_cwd) {
286                 _sysio_cwd = _sysio_root;
287                 P_REF(_sysio_cwd);
288         }
289 #endif
290
291         return 0;
292 }
293
294 int
295 _sysio_mount(struct pnode *cwd,
296              const char *source,
297              const char *target,
298              const char *filesystemtype,
299              unsigned long mountflags,
300              const void *data)
301 {
302         int     err;
303         struct fsswent *fssw;
304         struct intent intent;
305         struct pnode *tgt;
306         struct mount *mnt;
307
308         /*
309          * Find the file system switch entry specified.
310          */
311         fssw = _sysio_fssw_lookup(filesystemtype);
312         if (!fssw)
313                 return -ENODEV;
314
315         /*
316          * Look up the target path node.
317          */
318         INTENT_INIT(&intent, INT_GETATTR, NULL, NULL);
319         err = _sysio_namei(cwd, target, 0, &intent, &tgt);
320         if (err)
321                 return err;
322
323         if (tgt == _sysio_root) {
324                 /*
325                  * Attempting to mount over root.
326                  */
327                 err = -EBUSY;
328         } else {
329                 /*
330                  * Do the deed.
331                  */
332                 err =
333                     (*fssw->fssw_ops.fsswop_mount)(source,
334                                                    mountflags,
335                                                    data,
336                                                    tgt,
337                                                    &mnt);
338         }
339         if (err)
340                 P_RELE(tgt);
341         return err;
342 }
343
344 int
345 SYSIO_INTERFACE_NAME(mount)(const char *source,
346       const char *target,
347       const char *filesystemtype,
348       unsigned long mountflags,
349       const void *data)
350 {
351         int     err;
352         SYSIO_INTERFACE_DISPLAY_BLOCK;
353
354         SYSIO_INTERFACE_ENTER;
355         err =
356             _sysio_mount(_sysio_cwd,
357                          source,
358                          target,
359                          filesystemtype,
360                          mountflags,
361                          data);
362         SYSIO_INTERFACE_RETURN(err ? -1 : 0, err);
363 }
364
365 int
366 SYSIO_INTERFACE_NAME(umount)(const char *target)
367 {
368         int     err;
369         struct pnode *pno;
370         SYSIO_INTERFACE_DISPLAY_BLOCK;
371
372         SYSIO_INTERFACE_ENTER;
373         /*
374          * Look up the target path node.
375          */
376         err = _sysio_namei(_sysio_cwd, target, 0, NULL, &pno);
377         if (err)
378                 goto out;
379         P_RELE(pno);                            /* was ref'd */
380
381         /*
382          * Do the deed.
383          */
384 #if 0   
385         if (!pno->p_cover) {
386                 err = -EINVAL;
387                 goto error;
388         }
389 #endif
390         assert(pno->p_mount);
391         err = _sysio_do_unmount(pno->p_mount);
392
393 out:
394         SYSIO_INTERFACE_RETURN(err ? -1 : 0, err);
395 }
396
397 /*
398  * Unmount all file systems -- Usually as part of shutting everything down.
399  */
400 int
401 _sysio_unmount_all()
402 {
403         int     err;
404         struct mount *mnt, *nxt;
405         struct pnode *pno;
406
407         err = 0;
408         nxt = mounts.lh_first;
409         while ((mnt = nxt)) {
410                 nxt = mnt->mnt_link.le_next;
411                 pno = mnt->mnt_root;
412                 /*
413                  * If this is an automount generated mount, the root
414                  * has no reference. We can cause the dismount with a
415                  * simple prune.
416                  */
417                 if (!_sysio_p_prune(pno))
418                         continue;
419 #ifdef notdef
420                 /*
421                  * Need a ref but only if this is not the root of a
422                  * disconnected graph. If it is, then it is covered by itself
423                  * and, so, already referenced.
424                  */
425                 if (pno->p_cover != pno)
426                         P_REF(pno);
427 #endif
428                 err = _sysio_do_unmount(mnt);
429                 if (err) {
430 #ifdef notdef
431                         if (pno->p_cover != pno)
432                                 P_RELE(pno);
433 #endif
434                         break;
435                 }
436                 if (pno == _sysio_root)
437                         _sysio_root = NULL;
438         }
439
440         return err;
441 }
442
443 static int
444 _sysio_sub_fsswop_mount(const char *source,
445                         unsigned flags,
446                         const void *data __IS_UNUSED,
447                         struct pnode *tocover,
448                         struct mount **mntp)
449 {
450         int     err;
451         struct nameidata nameidata;
452         struct mount *mnt;
453
454         /*
455          * How can we make a sub-mount from nothing?
456          */
457         if (!_sysio_root)
458                 return -EBUSY;
459
460         /*
461          * Lookup the source.
462          */
463         ND_INIT(&nameidata, 0, source, _sysio_root, NULL);
464         err = _sysio_path_walk(_sysio_root, &nameidata);
465         if (err)
466                 return err;
467
468         /*
469          * Mount the rooted sub-tree at the given position.
470          */
471         err =
472             _sysio_do_mount(nameidata.nd_pno->p_mount->mnt_fs,
473                             nameidata.nd_pno->p_base,
474                             nameidata.nd_pno->p_mount->mnt_flags & flags,
475                             tocover,
476                             &mnt);
477
478         /*
479          * Clean up and return.
480          */
481         if (!err) {
482                 FS_REF(nameidata.nd_pno->p_mount->mnt_fs);
483                 *mntp = mnt;
484         }
485         P_RELE(nameidata.nd_pno);
486         return err;
487 }
488
489 #ifdef AUTOMOUNT_FILE_NAME
490 /*
491  * Parse automount specification formatted as:
492  *
493  * <fstype>:<source>[[ \t]+<comma-separated-mount-options>]
494  *
495  * NB:
496  * The buffer sent is (almost) always modified.
497  */
498 static int
499 parse_automount_spec(char *s, char **fstyp, char **srcp, char **optsp)
500 {
501         int     err;
502         char    *cp;
503         char    *fsty, *src, *opts;
504
505         err = 0;
506
507         /*
508          * Eat leading white.
509          */
510         while (*s && *s == ' ' && *s == '\t')
511                 s++;
512         /*
513          * Get fstype.
514          */
515         fsty = cp = s;
516         while (*cp &&
517                *cp != ':' &&
518                *cp != ' ' &&
519                *cp != '\t' &&
520                *cp != '\r' &&
521                *cp != '\n')
522                 cp++;
523         if (fsty == cp || *cp != ':')
524                 goto error;
525         *cp++ = '\0';
526
527         s = cp;
528         /*
529          * Eat leading white.
530          */
531         while (*s && *s == ' ' && *s == '\t')
532                 s++;
533         /*
534          * Get source.
535          */
536         src = cp = s;
537         while (*cp &&
538                *cp != ' ' &&
539                *cp != '\t' &&
540                *cp != '\r' &&
541                *cp != '\n')
542                 cp++;
543         if (src == cp)
544                 goto error;
545         if (*cp)
546                 *cp++ = '\0';
547
548         s = cp;
549         /*
550          * Eat leading white.
551          */
552         while (*s && *s == ' ' && *s == '\t')
553                 s++;
554         /*
555          * Get opts.
556          */
557         opts = cp = s;
558         while (*cp &&
559                *cp != ' ' &&
560                *cp != '\t' &&
561                *cp != '\r' &&
562                *cp != '\n')
563                 cp++;
564         if (opts == cp)
565                 opts = NULL;
566         if (*cp)
567                 *cp++ = '\0';
568
569         if (*cp)
570                 goto error;
571
572         *fstyp = fsty;
573         *srcp = src;
574         *optsp = opts;
575         return 0;
576
577 error:
578         return -EINVAL;
579 }
580
581 /*
582  * Parse (and strip) system mount options.
583  */
584 static char *
585 parse_opts(char *opts, unsigned *flagsp)
586 {
587         unsigned flags;
588         char    *src, *dst;
589         char    *cp;
590
591         flags = 0;
592         src = dst = opts;
593         for (;;) {
594                 cp = src;
595                 while (*cp && *cp != ',')
596                         cp++;
597                 if (src + 2 == cp && strncmp(src, "rw", 2) == 0) {
598                         /*
599                          * Do nothing. This is the default.
600                          */
601                         src += 2;
602                 } else if (src + 2 == cp && strncmp(src, "ro", 2) == 0) {
603                         /*
604                          * Read-only.
605                          */
606                         flags |= MOUNT_F_RO;
607                         src += 2;
608                 }
609                 else if (src + 4 == cp && strncmp(src, "auto", 4) == 0) {
610                         /*
611                          * Enable automounts.
612                          */
613                         flags |= MOUNT_F_AUTO;
614                         src += 4;
615                 }
616                 if (src < cp) {
617                         /*
618                          * Copy what we didn't consume.
619                          */
620                         if (dst != opts)
621                                 *dst++ = ',';
622                         do
623                                 *dst++ = *src++;
624                         while (src != cp);
625                 }
626                 if (!*src)
627                         break;
628                 *dst = '\0';
629                 src++;                                  /* skip comma */
630         }
631         *dst = '\0';
632
633         *flagsp = flags;
634         return opts;
635 }
636
637 /*
638  * Attempt automount over the given directory.
639  */
640 int
641 _sysio_automount(struct pnode *mntpno)
642 {
643         int     err;
644         struct inode *ino;
645         struct iovec iovec;
646         struct ioctx iocontext;
647         struct intnl_xtvec xtvec;
648         ssize_t cc;
649         char    *fstype, *source, *opts;
650         unsigned flags;
651         struct fsswent *fssw;
652         struct mount *mnt;
653
654         /*
655          * Revalidate -- Paranoia.
656          */
657         err = _sysio_p_validate(mntpno, NULL, NULL);
658         if (err)
659                 return err;
660
661         /*
662          * Read file content.
663          */
664         ino = mntpno->p_base->pb_ino;
665         if (ino->i_stbuf.st_size > 64 * 1024) {
666                 /*
667                  * Let's be reasonable.
668                  */
669                 return -EINVAL;
670         }
671         iovec.iov_base = malloc(ino->i_stbuf.st_size + 1);
672         if (!iovec.iov_base)
673                 return -ENOMEM;
674         iovec.iov_len = ino->i_stbuf.st_size;
675         err = _sysio_open(mntpno, O_RDONLY, 0);
676         if (err)
677                 goto out;
678         xtvec.xtv_off = 0;
679         xtvec.xtv_len = ino->i_stbuf.st_size;
680         IOCTX_INIT(&iocontext,
681                    1,
682                    0,
683                    ino,
684                    &iovec, 1,
685                    &xtvec, 1);
686         _sysio_ioctx_enter(&iocontext);
687         err = (*ino->i_ops.inop_read)(ino, &iocontext);
688         if (err) {
689                 _sysio_ioctx_complete(&iocontext);
690                 (void )(*ino->i_ops.inop_close)(ino);
691                 goto out;
692         }
693         cc = _sysio_ioctx_wait(&iocontext);
694         err = (*ino->i_ops.inop_close)(ino);
695         if (err)
696                 goto out;
697         if (cc < 0) {
698                 err = (int )cc;
699                 goto out;
700         }
701         ((char *)iovec.iov_base)[cc] = '\0';
702
703         /*
704          * Parse.
705          */
706         err = parse_automount_spec(iovec.iov_base, &fstype, &source, &opts);
707         if (err)
708                 goto out;
709         flags = 0;
710         if (opts)
711                 opts = parse_opts(opts, &flags);
712
713         /*
714          * Find the file system switch entry specified.
715          */
716         fssw = _sysio_fssw_lookup(fstype);
717         if (!fssw) {
718                 err = -ENODEV;
719                 goto out;
720         }
721
722         /*
723          * Do the deed.
724          */
725         P_REF(mntpno->p_parent);
726         err =
727             (*fssw->fssw_ops.fsswop_mount)(source,
728                                            flags,
729                                            opts,
730                                            mntpno->p_parent,
731                                            &mnt);
732         if (err)
733                 P_RELE(mntpno->p_parent);
734
735 out:
736         if (iovec.iov_base)
737                 free(iovec.iov_base);
738         return err;
739 }
740 #endif