Whamcloud - gitweb
Ignore generated files.
[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-2003 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 "xtio.h"
57 #include "sysio.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 /*
80  * Initialization. Must be called before any other routine in this module.
81  */
82 int
83 _sysio_mount_init()
84 {
85
86         LIST_INIT(&mounts);
87 #ifdef AUTOMOUNT_FILE_NAME
88         _sysio_next_component(AUTOMOUNT_FILE_NAME, &_sysio_mount_file_name);
89 #endif
90
91         return 0;
92 }
93
94 /*
95  * Mount rooted sub-tree somewhere in the existing name space.
96  */
97 int
98 _sysio_do_mount(struct filesys *fs,
99                 struct pnode_base *rootpb,
100                 unsigned flags,
101                 struct pnode *tocover,
102                 struct mount **mntp)
103 {
104         struct mount *mnt;
105         int     err;
106
107         /*
108          * It's really poor form to allow the new root to be a
109          * descendant of the pnode being covered.
110          */
111         if (tocover) {
112                 struct pnode_base *pb;
113
114                 for (pb = rootpb;
115                      pb && pb != tocover->p_base;
116                      pb = pb->pb_parent)
117                         ;
118                 if (pb == tocover->p_base)
119                         return -EBUSY;
120         }
121
122         /*
123          * Alloc
124          */
125         mnt = malloc(sizeof(struct mount));
126         if (!mnt)
127                 return -ENOMEM;
128         err = 0;
129         /*
130          * Init enough to make the mount record usable to the path node
131          * generation routines.
132          */
133         mnt->mnt_fs = fs;
134         if (fs->fs_flags & FS_F_RO) {
135                 /*
136                  * Propagate the read-only flag -- Whether they set it or not.
137                  */
138                 flags |= MOUNT_F_RO;
139         }
140         mnt->mnt_flags = flags;
141         /*
142          * Get alias for the new root.
143          */
144         mnt->mnt_root =
145             _sysio_p_new_alias(tocover ? tocover->p_parent : NULL, rootpb, mnt);
146         if (!mnt->mnt_root) {
147                 err = -ENOMEM;
148                 goto error;
149         }
150         /*
151          * It may have been a while since the root inode was validated;
152          * better validate again.  And it better be a directory!
153          */
154         err = _sysio_p_validate(mnt->mnt_root, NULL, NULL);
155         if (err)
156                 goto error;
157
158         if (!S_ISDIR(mnt->mnt_root->p_base->pb_ino->i_mode)) {
159                 err = -ENOTDIR;
160                 goto error;
161         }
162         /*
163          * Cover up the mount point.
164          */
165         mnt->mnt_covers = tocover;
166         if (!mnt->mnt_covers) {
167                 /*
168                  * New graph; It covers itself.
169                  */
170                 mnt->mnt_covers = tocover = mnt->mnt_root;
171         }
172         tocover->p_cover = mnt->mnt_root;
173
174         LIST_INSERT_HEAD(&mounts, mnt, mnt_link);
175
176         *mntp = mnt;
177         return 0;
178
179 error:
180         if (mnt->mnt_root) {
181                 P_RELE(mnt->mnt_root);
182                 _sysio_p_prune(mnt->mnt_root);
183         }
184         free(mnt);
185         return err;
186 }
187
188 /*
189  * Remove mounted sub-tree from the system.
190  */
191 int
192 _sysio_do_unmount(struct mount *mnt)
193 {
194         struct pnode *root;
195         struct filesys *fs;
196
197         root = mnt->mnt_root;
198         if (root->p_cover && root->p_cover != root) {
199                 /*
200                  * Active mount.
201                  */
202                 return -EBUSY;
203         }
204         assert(mnt->mnt_covers->p_cover == root);
205         if (_sysio_p_prune(root) != 1) {
206                 /*
207                  * Active aliases.
208                  */
209                 return -EBUSY;
210         }
211         /*
212          * We're committed.
213          *
214          * Drop ref of covered pnode and break linkage in name space.
215          */
216         if (root->p_cover != root)
217                 P_RELE(mnt->mnt_covers);
218         mnt->mnt_covers->p_cover = NULL;
219         LIST_REMOVE(mnt, mnt_link);
220         /*
221          * Kill the root.
222          */
223         P_RELE(root);
224         root->p_cover = NULL;
225         _sysio_p_gone(root);
226         /*
227          * Release mount record resource.
228          */
229         fs = mnt->mnt_fs;
230         free(mnt);
231         FS_RELE(fs);
232
233         return 0;
234 }
235
236 /*
237  * Establish the system name space.
238  */
239 int
240 _sysio_mount_root(const char *source,
241                   const char *fstype,
242                   unsigned flags,
243                   const void *data)
244 {
245         struct fsswent *fssw;
246         int     err;
247         struct mount *mnt;
248
249         if (_sysio_root)
250                 return -EBUSY;
251
252         fssw = _sysio_fssw_lookup(fstype);
253         if (!fssw)
254                 return -ENODEV;
255
256         err = (*fssw->fssw_ops.fsswop_mount)(source, flags, data, NULL, &mnt);
257         if (err)
258                 return err;
259
260         _sysio_root = mnt->mnt_root;
261         /*
262          * It is very annoying to have to set the current working directory.
263          * So... If it isn't set, make it the root now.
264          */
265         if (!_sysio_cwd) {
266                 _sysio_cwd = _sysio_root;
267                 P_REF(_sysio_cwd);
268         }
269
270         return 0;
271 }
272
273 int
274 _sysio_mount(struct pnode *cwd,
275              const char *source,
276              const char *target,
277              const char *filesystemtype,
278              unsigned long mountflags,
279              const void *data)
280 {
281         int     err;
282         struct fsswent *fssw;
283         struct intent intent;
284         struct pnode *tgt;
285         struct mount *mnt;
286
287         /*
288          * Find the file system switch entry specified.
289          */
290         fssw = _sysio_fssw_lookup(filesystemtype);
291         if (!fssw)
292                 return -ENODEV;
293
294         /*
295          * Look up the target path node.
296          */
297         INTENT_INIT(&intent, INT_GETATTR, NULL, NULL);
298         err = _sysio_namei(cwd, target, 0, &intent, &tgt);
299         if (err)
300                 return err;
301
302         if (tgt == _sysio_root) {
303                 /*
304                  * Attempting to mount over root.
305                  */
306                 err = -EBUSY;
307         } else {
308                 /*
309                  * Do the deed.
310                  */
311                 err =
312                     (*fssw->fssw_ops.fsswop_mount)(source,
313                                                    mountflags,
314                                                    data,
315                                                    tgt,
316                                                    &mnt);
317         }
318         if (err)
319                 P_RELE(tgt);
320         return err;
321 }
322
323 int
324 SYSIO_INTERFACE_NAME(mount)(const char *source,
325       const char *target,
326       const char *filesystemtype,
327       unsigned long mountflags,
328       const void *data)
329 {
330         int     err;
331         SYSIO_INTERFACE_DISPLAY_BLOCK;
332
333         SYSIO_INTERFACE_ENTER;
334         err =
335             _sysio_mount(_sysio_cwd,
336                          source,
337                          target,
338                          filesystemtype,
339                          mountflags,
340                          data);
341         SYSIO_INTERFACE_RETURN(err ? -1 : 0, err);
342 }
343
344 int
345 SYSIO_INTERFACE_NAME(umount)(const char *target)
346 {
347         int     err;
348         struct pnode *pno;
349         SYSIO_INTERFACE_DISPLAY_BLOCK;
350
351         SYSIO_INTERFACE_ENTER;
352         /*
353          * Look up the target path node.
354          */
355         err = _sysio_namei(_sysio_cwd, target, 0, NULL, &pno);
356         if (err)
357                 goto out;
358         P_RELE(pno);                            /* was ref'd */
359
360         /*
361          * Do the deed.
362          */
363 #if 0   
364         if (!pno->p_cover) {
365                 err = -EINVAL;
366                 goto error;
367         }
368 #endif
369         assert(pno->p_mount);
370         err = _sysio_do_unmount(pno->p_mount);
371
372 out:
373         SYSIO_INTERFACE_RETURN(err ? -1 : 0, err);
374 }
375
376 /*
377  * Unmount all file systems -- Usually as part of shutting everything down.
378  */
379 int
380 _sysio_unmount_all()
381 {
382         int     err;
383         struct mount *mnt, *nxt;
384         struct pnode *pno;
385
386         err = 0;
387         nxt = mounts.lh_first;
388         while ((mnt = nxt)) {
389                 nxt = mnt->mnt_link.le_next;
390                 pno = mnt->mnt_root;
391                 /*
392                  * If this is an automount generated mount, the root
393                  * has no reference. We can cause the dismount with a
394                  * simple prune.
395                  */
396                 if (!_sysio_p_prune(pno))
397                         continue;
398 #ifdef notdef
399                 /*
400                  * Need a ref but only if this is not the root of a
401                  * disconnected graph. If it is, then it is covered by itself
402                  * and, so, already referenced.
403                  */
404                 if (pno->p_cover != pno)
405                         P_REF(pno);
406 #endif
407                 err = _sysio_do_unmount(mnt);
408                 if (err) {
409 #ifdef notdef
410                         if (pno->p_cover != pno)
411                                 P_RELE(pno);
412 #endif
413                         break;
414                 }
415                 if (pno == _sysio_root)
416                         _sysio_root = NULL;
417         }
418
419         return err;
420 }
421
422 #ifdef AUTOMOUNT_FILE_NAME
423 /*
424  * Parse automount specification formatted as:
425  *
426  * <fstype>:<source>[[ \t]+<comma-separated-mount-options>]
427  *
428  * NB:
429  * The buffer sent is (almost) always modified.
430  */
431 static int
432 parse_automount_spec(char *s, char **fstyp, char **srcp, char **optsp)
433 {
434         int     err;
435         char    *cp;
436         char    *fsty, *src, *opts;
437
438         err = 0;
439
440         /*
441          * Eat leading white.
442          */
443         while (*s && *s == ' ' && *s == '\t')
444                 s++;
445         /*
446          * Get fstype.
447          */
448         fsty = cp = s;
449         while (*cp &&
450                *cp != ':' &&
451                *cp != ' ' &&
452                *cp != '\t' &&
453                *cp != '\r' &&
454                *cp != '\n')
455                 cp++;
456         if (fsty == cp || *cp != ':')
457                 goto error;
458         *cp++ = '\0';
459
460         s = cp;
461         /*
462          * Eat leading white.
463          */
464         while (*s && *s == ' ' && *s == '\t')
465                 s++;
466         /*
467          * Get source.
468          */
469         src = cp = s;
470         while (*cp &&
471                *cp != ' ' &&
472                *cp != '\t' &&
473                *cp != '\r' &&
474                *cp != '\n')
475                 cp++;
476         if (src == cp)
477                 goto error;
478         if (*cp)
479                 *cp++ = '\0';
480
481         s = cp;
482         /*
483          * Eat leading white.
484          */
485         while (*s && *s == ' ' && *s == '\t')
486                 s++;
487         /*
488          * Get opts.
489          */
490         opts = cp = s;
491         while (*cp &&
492                *cp != ' ' &&
493                *cp != '\t' &&
494                *cp != '\r' &&
495                *cp != '\n')
496                 cp++;
497         if (opts == cp)
498                 opts = NULL;
499         if (*cp)
500                 *cp++ = '\0';
501
502         if (*cp)
503                 goto error;
504
505         *fstyp = fsty;
506         *srcp = src;
507         *optsp = opts;
508         return 0;
509
510 error:
511         return -EINVAL;
512 }
513
514 /*
515  * Parse (and strip) system mount options.
516  */
517 static char *
518 parse_opts(char *opts, unsigned *flagsp)
519 {
520         unsigned flags;
521         char    *src, *dst;
522         char    *cp;
523
524         flags = 0;
525         src = dst = opts;
526         for (;;) {
527                 cp = src;
528                 while (*cp && *cp != ',')
529                         cp++;
530                 if (src + 2 == cp && strncmp(src, "rw", 2) == 0) {
531                         /*
532                          * Do nothing. This is the default.
533                          */
534                         src += 2;
535                 } else if (src + 2 == cp && strncmp(src, "ro", 2) == 0) {
536                         /*
537                          * Read-only.
538                          */
539                         flags |= MOUNT_F_RO;
540                         src += 2;
541                 }
542                 else if (src + 4 == cp && strncmp(src, "auto", 4) == 0) {
543                         /*
544                          * Enable automounts.
545                          */
546                         flags |= MOUNT_F_AUTO;
547                         src += 4;
548                 }
549                 if (src < cp) {
550                         /*
551                          * Copy what we didn't consume.
552                          */
553                         if (dst != opts)
554                                 *dst++ = ',';
555                         do
556                                 *dst++ = *src++;
557                         while (src != cp);
558                 }
559                 if (!*src)
560                         break;
561                 *dst = '\0';
562                 src++;                                  /* skip comma */
563         }
564         *dst = '\0';
565
566         *flagsp = flags;
567         return opts;
568 }
569
570 /*
571  * Attempt automount over the given directory.
572  */
573 int
574 _sysio_automount(struct pnode *mntpno)
575 {
576         int     err;
577         struct inode *ino;
578         struct intnl_stat stbuf;
579         struct iovec iovec;
580         struct ioctx iocontext;
581         struct intnl_xtvec xtvec;
582         ssize_t cc;
583         char    *fstype, *source, *opts;
584         unsigned flags;
585         struct fsswent *fssw;
586         struct mount *mnt;
587
588         /*
589          * Revalidate -- Paranoia.
590          */
591         err = _sysio_p_validate(mntpno, NULL, NULL);
592         if (err)
593                 return err;
594
595         /*
596          * Read file content.
597          */
598         ino = mntpno->p_base->pb_ino;
599         err = (*ino->i_ops.inop_getattr)(mntpno, ino, &stbuf);
600         if (err)
601                 return err;
602         if (stbuf.st_size > 64 * 1024) {
603                 /*
604                  * Let's be reasonable.
605                  */
606                 return -EINVAL;
607         }
608         iovec.iov_base = malloc(stbuf.st_size + 1);
609         if (!iovec.iov_base)
610                 return -ENOMEM;
611         iovec.iov_len = stbuf.st_size;
612         err = _sysio_open(mntpno, O_RDONLY, 0);
613         if (err)
614                 goto out;
615         xtvec.xtv_off = 0;
616         xtvec.xtv_len = stbuf.st_size;
617         IOCTX_INIT(&iocontext,
618                    1,
619                    0,
620                    ino,
621                    &iovec, 1,
622                    &xtvec, 1);
623         _sysio_ioctx_enter(&iocontext);
624         err = (*ino->i_ops.inop_read)(ino, &iocontext);
625         if (err) {
626                 _sysio_ioctx_complete(&iocontext);
627                 (void )(*ino->i_ops.inop_close)(ino);
628                 goto out;
629         }
630         cc = _sysio_ioctx_wait(&iocontext);
631         err = (*ino->i_ops.inop_close)(ino);
632         if (err)
633                 goto out;
634         if (cc < 0) {
635                 err = (int )cc;
636                 goto out;
637         }
638         ((char *)iovec.iov_base)[cc] = '\0';
639
640         /*
641          * Parse.
642          */
643         err = parse_automount_spec(iovec.iov_base, &fstype, &source, &opts);
644         if (err)
645                 goto out;
646         flags = 0;
647         if (opts)
648                 opts = parse_opts(opts, &flags);
649
650         /*
651          * Find the file system switch entry specified.
652          */
653         fssw = _sysio_fssw_lookup(fstype);
654         if (!fssw) {
655                 err = -ENODEV;
656                 goto out;
657         }
658
659         /*
660          * Do the deed.
661          */
662         P_REF(mntpno->p_parent);
663         err =
664             (*fssw->fssw_ops.fsswop_mount)(source,
665                                            flags,
666                                            opts,
667                                            mntpno->p_parent,
668                                            &mnt);
669         if (err)
670                 P_RELE(mntpno->p_parent);
671
672 out:
673         if (iovec.iov_base)
674                 free(iovec.iov_base);
675         return err;
676 }
677 #endif