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