1: /*-
2: * Copyright (c) 1980, 1989, 1993, 1994
3: * The Regents of the University of California. All rights reserved.
4: *
5: * Redistribution and use in source and binary forms, with or without
6: * modification, are permitted provided that the following conditions
7: * are met:
8: * 1. Redistributions of source code must retain the above copyright
9: * notice, this list of conditions and the following disclaimer.
10: * 2. Redistributions in binary form must reproduce the above copyright
11: * notice, this list of conditions and the following disclaimer in the
12: * documentation and/or other materials provided with the distribution.
13: * 3. All advertising materials mentioning features or use of this software
14: * must display the following acknowledgement:
15: * This product includes software developed by the University of
16: * California, Berkeley and its contributors.
17: * 4. Neither the name of the University nor the names of its contributors
18: * may be used to endorse or promote products derived from this software
19: * without specific prior written permission.
20: *
21: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31: * SUCH DAMAGE.
32: *
33: * @(#) Copyright (c) 1980, 1989, 1993, 1994 The Regents of the University of California. All rights reserved.
34: * @(#)mount.c 8.25 (Berkeley) 5/8/95
35: * $FreeBSD: src/sbin/mount/mount.c,v 1.39.2.3 2001/08/01 08:26:23 obrien Exp $
36: * $DragonFly: src/sbin/mount/mount.c,v 1.5 2003/11/03 19:51:05 eirikn Exp $
37: */
38:
39: #include <sys/param.h>
40: #include <sys/mount.h>
41: #include <sys/stat.h>
42: #include <sys/wait.h>
43:
44: #include <err.h>
45: #include <errno.h>
46: #include <fstab.h>
47: #include <pwd.h>
48: #include <signal.h>
49: #include <stdio.h>
50: #include <stdlib.h>
51: #include <string.h>
52: #include <unistd.h>
53:
54: #include "extern.h"
55: #include "mntopts.h"
56: #include "pathnames.h"
57:
58: /* `meta' options */
59: #define MOUNT_META_OPTION_FSTAB "fstab"
60: #define MOUNT_META_OPTION_CURRENT "current"
61:
62: int debug, fstab_style, verbose;
63:
64: char *catopt(char *, const char *);
65: struct statfs
66: *getmntpt(const char *);
67: int hasopt(const char *, const char *);
68: int ismounted(struct fstab *, struct statfs *, int);
69: int isremountable(const char *);
70: void mangle(char *, int *, const char **);
71: char *update_options(char *, char *, int);
72: int mountfs(const char *, const char *, const char *,
73: int, const char *, const char *);
74: void remopt(char *, const char *);
75: void prmount(struct statfs *);
76: void putfsent(const struct statfs *);
77: void usage(void);
78: char *flags2opts(int);
79:
80: /* Map from mount options to printable formats. */
81: static struct opt {
82: int o_opt;
83: const char *o_name;
84: } optnames[] = {
85: { MNT_ASYNC, "asynchronous" },
86: { MNT_EXPORTED, "NFS exported" },
87: { MNT_LOCAL, "local" },
88: { MNT_NOATIME, "noatime" },
89: { MNT_NODEV, "nodev" },
90: { MNT_NOEXEC, "noexec" },
91: { MNT_NOSUID, "nosuid" },
92: { MNT_NOSYMFOLLOW, "nosymfollow" },
93: { MNT_QUOTA, "with quotas" },
94: { MNT_RDONLY, "read-only" },
95: { MNT_SYNCHRONOUS, "synchronous" },
96: { MNT_UNION, "union" },
97: { MNT_NOCLUSTERR, "noclusterr" },
98: { MNT_NOCLUSTERW, "noclusterw" },
99: { MNT_SUIDDIR, "suiddir" },
100: { MNT_SOFTDEP, "soft-updates" },
101: { 0, NULL }
102: };
103:
104: /*
105: * List of VFS types that can be remounted without becoming mounted on top
106: * of each other.
107: * XXX Is this list correct?
108: */
109: static const char *
110: remountable_fs_names[] = {
111: "ufs", "ffs", "ext2fs",
112: 0
113: };
114:
115: int
116: main(int argc, char **argv)
117: {
118: const char *mntfromname, **vfslist, *vfstype;
119: struct fstab *fs;
120: struct statfs *mntbuf;
121: FILE *mountdfp;
122: pid_t pid;
123: int all, ch, i, init_flags, mntsize, rval, have_fstab;
124: char *options;
125:
126: all = init_flags = 0;
127: options = NULL;
128: vfslist = NULL;
129: vfstype = "ufs";
130: while ((ch = getopt(argc, argv, "adfo:prwt:uv")) != -1)
131: switch (ch) {
132: case 'a':
133: all = 1;
134: break;
135: case 'd':
136: debug = 1;
137: break;
138: case 'f':
139: init_flags |= MNT_FORCE;
140: break;
141: case 'o':
142: if (*optarg)
143: options = catopt(options, optarg);
144: break;
145: case 'p':
146: fstab_style = 1;
147: verbose = 1;
148: break;
149: case 'r':
150: options = catopt(options, "ro");
151: break;
152: case 't':
153: if (vfslist != NULL)
154: errx(1, "only one -t option may be specified");
155: vfslist = makevfslist(optarg);
156: vfstype = optarg;
157: break;
158: case 'u':
159: init_flags |= MNT_UPDATE;
160: break;
161: case 'v':
162: verbose = 1;
163: break;
164: case 'w':
165: options = catopt(options, "noro");
166: break;
167: case '?':
168: default:
169: usage();
170: /* NOTREACHED */
171: }
172: argc -= optind;
173: argv += optind;
174:
175: #define BADTYPE(type) \
176: (strcmp(type, FSTAB_RO) && \
177: strcmp(type, FSTAB_RW) && strcmp(type, FSTAB_RQ))
178:
179: rval = 0;
180: switch (argc) {
181: case 0:
182: if ((mntsize = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0)
183: err(1, "getmntinfo");
184: if (all) {
185: while ((fs = getfsent()) != NULL) {
186: if (BADTYPE(fs->fs_type))
187: continue;
188: if (checkvfsname(fs->fs_vfstype, vfslist))
189: continue;
190: if (hasopt(fs->fs_mntops, "noauto"))
191: continue;
192: if (!(init_flags & MNT_UPDATE) &&
193: ismounted(fs, mntbuf, mntsize))
194: continue;
195: if (mountfs(fs->fs_vfstype, fs->fs_spec,
196: fs->fs_file, init_flags, options,
197: fs->fs_mntops))
198: rval = 1;
199: }
200: } else if (fstab_style) {
201: for (i = 0; i < mntsize; i++) {
202: if (checkvfsname(mntbuf[i].f_fstypename, vfslist))
203: continue;
204: putfsent(&mntbuf[i]);
205: }
206: } else {
207: for (i = 0; i < mntsize; i++) {
208: if (checkvfsname(mntbuf[i].f_fstypename,
209: vfslist))
210: continue;
211: prmount(&mntbuf[i]);
212: }
213: }
214: exit(rval);
215: case 1:
216: if (vfslist != NULL)
217: usage();
218:
219: if (init_flags & MNT_UPDATE) {
220: mntfromname = NULL;
221: have_fstab = 0;
222: if ((mntbuf = getmntpt(*argv)) == NULL)
223: errx(1, "not currently mounted %s", *argv);
224: /*
225: * Only get the mntflags from fstab if both mntpoint
226: * and mntspec are identical. Also handle the special
227: * case where just '/' is mounted and 'spec' is not
228: * identical with the one from fstab ('/dev' is missing
229: * in the spec-string at boot-time).
230: */
231: if ((fs = getfsfile(mntbuf->f_mntonname)) != NULL) {
232: if (strcmp(fs->fs_spec,
233: mntbuf->f_mntfromname) == 0 &&
234: strcmp(fs->fs_file,
235: mntbuf->f_mntonname) == 0) {
236: have_fstab = 1;
237: mntfromname = mntbuf->f_mntfromname;
238: } else if (argv[0][0] == '/' &&
239: argv[0][1] == '\0') {
240: fs = getfsfile("/");
241: have_fstab = 1;
242: mntfromname = fs->fs_spec;
243: }
244: }
245: if (have_fstab) {
246: options = update_options(options, fs->fs_mntops,
247: mntbuf->f_flags);
248: } else {
249: mntfromname = mntbuf->f_mntfromname;
250: options = update_options(options, NULL,
251: mntbuf->f_flags);
252: }
253: rval = mountfs(mntbuf->f_fstypename, mntfromname,
254: mntbuf->f_mntonname, init_flags, options, 0);
255: break;
256: }
257: rmslashes(*argv, *argv);
258: if ((fs = getfsfile(*argv)) == NULL &&
259: (fs = getfsspec(*argv)) == NULL)
260: errx(1, "%s: unknown special file or file system",
261: *argv);
262: if (BADTYPE(fs->fs_type))
263: errx(1, "%s has unknown file system type",
264: *argv);
265: rval = mountfs(fs->fs_vfstype, fs->fs_spec, fs->fs_file,
266: init_flags, options, fs->fs_mntops);
267: break;
268: case 2:
269: /*
270: * If -t flag has not been specified, the path cannot be
271: * found, spec contains either a ':' or a '@', and the
272: * spec is not a file with those characters, then assume
273: * that an NFS filesystem is being specified ala Sun.
274: */
275: if (vfslist == NULL && strpbrk(argv[0], ":@") != NULL &&
276: access(argv[0], 0) == -1)
277: vfstype = "nfs";
278: rval = mountfs(vfstype,
279: argv[0], argv[1], init_flags, options, NULL);
280: break;
281: default:
282: usage();
283: /* NOTREACHED */
284: }
285:
286: /*
287: * If the mount was successfully, and done by root, tell mountd the
288: * good news. Pid checks are probably unnecessary, but don't hurt.
289: */
290: if (rval == 0 && getuid() == 0 &&
291: (mountdfp = fopen(_PATH_MOUNTDPID, "r")) != NULL) {
292: if (fscanf(mountdfp, "%d", &pid) == 1 &&
293: pid > 0 && kill(pid, SIGHUP) == -1 && errno != ESRCH)
294: err(1, "signal mountd");
295: (void)fclose(mountdfp);
296: }
297:
298: exit(rval);
299: }
300:
301: int
302: ismounted(struct fstab *fs, struct statfs *mntbuf, int mntsize)
303: {
304: int i;
305:
306: if (fs->fs_file[0] == '/' && fs->fs_file[1] == '\0')
307: /* the root file system can always be remounted */
308: return (0);
309:
310: for (i = mntsize - 1; i >= 0; --i)
311: if (strcmp(fs->fs_file, mntbuf[i].f_mntonname) == 0 &&
312: (!isremountable(fs->fs_vfstype) ||
313: strcmp(fs->fs_spec, mntbuf[i].f_mntfromname) == 0))
314: return (1);
315: return (0);
316: }
317:
318: int
319: isremountable(const char *vfsname)
320: {
321: const char **cp;
322:
323: for (cp = remountable_fs_names; *cp; cp++)
324: if (strcmp(*cp, vfsname) == 0)
325: return (1);
326: return (0);
327: }
328:
329: int
330: hasopt(const char *mntopts, const char *option)
331: {
332: int negative, found;
333: char *opt, *optbuf;
334:
335: if (option[0] == 'n' && option[1] == 'o') {
336: negative = 1;
337: option += 2;
338: } else
339: negative = 0;
340: optbuf = strdup(mntopts);
341: found = 0;
342: for (opt = optbuf; (opt = strtok(opt, ",")) != NULL; opt = NULL) {
343: if (opt[0] == 'n' && opt[1] == 'o') {
344: if (!strcasecmp(opt + 2, option))
345: found = negative;
346: } else if (!strcasecmp(opt, option))
347: found = !negative;
348: }
349: free(optbuf);
350: return (found);
351: }
352:
353: int
354: mountfs(const char *vfstype, const char *spec, const char *name, int flags,
355: const char *options, const char *mntopts)
356: {
357: /* List of directories containing mount_xxx subcommands. */
358: static const char *edirs[] = {
359: _PATH_SBIN,
360: _PATH_USRSBIN,
361: NULL
362: };
363: const char *argv[100], **edir;
364: struct statfs sf;
365: pid_t pid;
366: int argc, i, status;
367: char *optbuf, execname[MAXPATHLEN + 1], mntpath[MAXPATHLEN];
368:
369: #if __GNUC__
370: (void)&optbuf;
371: (void)&name;
372: #endif
373:
374: /* resolve the mountpoint with realpath(3) */
375: (void)checkpath(name, mntpath);
376: name = mntpath;
377:
378: if (mntopts == NULL)
379: mntopts = "";
380: if (options == NULL) {
381: if (*mntopts == '\0') {
382: options = "rw";
383: } else {
384: options = mntopts;
385: mntopts = "";
386: }
387: }
388: optbuf = catopt(strdup(mntopts), options);
389:
390: if (strcmp(name, "/") == 0)
391: flags |= MNT_UPDATE;
392: if (flags & MNT_FORCE)
393: optbuf = catopt(optbuf, "force");
394: if (flags & MNT_RDONLY)
395: optbuf = catopt(optbuf, "ro");
396: /*
397: * XXX
398: * The mount_mfs (newfs) command uses -o to select the
399: * optimization mode. We don't pass the default "-o rw"
400: * for that reason.
401: */
402: if (flags & MNT_UPDATE)
403: optbuf = catopt(optbuf, "update");
404:
405: argc = 0;
406: argv[argc++] = vfstype;
407: mangle(optbuf, &argc, argv);
408: argv[argc++] = spec;
409: argv[argc++] = name;
410: argv[argc] = NULL;
411:
412: if (debug) {
413: (void)printf("exec: mount_%s", vfstype);
414: for (i = 1; i < argc; i++)
415: (void)printf(" %s", argv[i]);
416: (void)printf("\n");
417: return (0);
418: }
419:
420: switch (pid = fork()) {
421: case -1: /* Error. */
422: warn("fork");
423: free(optbuf);
424: return (1);
425: case 0: /* Child. */
426: if (strcmp(vfstype, "ufs") == 0)
427: exit(mount_ufs(argc, (char * const *) argv));
428:
429: /* Go find an executable. */
430: for (edir = edirs; *edir; edir++) {
431: (void)snprintf(execname,
432: sizeof(execname), "%s/mount_%s", *edir, vfstype);
433: execv(execname, (char * const *)argv);
434: }
435: if (errno == ENOENT) {
436: int len = 0;
437: char *cp;
438: for (edir = edirs; *edir; edir++)
439: len += strlen(*edir) + 2; /* ", " */
440: if ((cp = malloc(len)) == NULL)
441: errx(1, "malloc failed");
442: cp[0] = '\0';
443: for (edir = edirs; *edir; edir++) {
444: strcat(cp, *edir);
445: if (edir[1] != NULL)
446: strcat(cp, ", ");
447: }
448: warn("exec mount_%s not found in %s", vfstype, cp);
449: }
450: exit(1);
451: /* NOTREACHED */
452: default: /* Parent. */
453: free(optbuf);
454:
455: if (waitpid(pid, &status, 0) < 0) {
456: warn("waitpid");
457: return (1);
458: }
459:
460: if (WIFEXITED(status)) {
461: if (WEXITSTATUS(status) != 0)
462: return (WEXITSTATUS(status));
463: } else if (WIFSIGNALED(status)) {
464: warnx("%s: %s", name, sys_siglist[WTERMSIG(status)]);
465: return (1);
466: }
467:
468: if (verbose) {
469: if (statfs(name, &sf) < 0) {
470: warn("statfs %s", name);
471: return (1);
472: }
473: if (fstab_style)
474: putfsent(&sf);
475: else
476: prmount(&sf);
477: }
478: break;
479: }
480:
481: return (0);
482: }
483:
484: void
485: prmount(struct statfs *sfp)
486: {
487: int flags;
488: struct opt *o;
489: struct passwd *pw;
490:
491: (void)printf("%s on %s (%s", sfp->f_mntfromname, sfp->f_mntonname,
492: sfp->f_fstypename);
493:
494: flags = sfp->f_flags & MNT_VISFLAGMASK;
495: for (o = optnames; flags && o->o_opt; o++)
496: if (flags & o->o_opt) {
497: (void)printf(", %s", o->o_name);
498: flags &= ~o->o_opt;
499: }
500: if (sfp->f_owner) {
501: (void)printf(", mounted by ");
502: if ((pw = getpwuid(sfp->f_owner)) != NULL)
503: (void)printf("%s", pw->pw_name);
504: else
505: (void)printf("%d", sfp->f_owner);
506: }
507: if (verbose) {
508: if (sfp->f_syncwrites != 0 || sfp->f_asyncwrites != 0)
509: (void)printf(", writes: sync %ld async %ld",
510: sfp->f_syncwrites, sfp->f_asyncwrites);
511: if (sfp->f_syncreads != 0 || sfp->f_asyncreads != 0)
512: (void)printf(", reads: sync %ld async %ld",
513: sfp->f_syncreads, sfp->f_asyncreads);
514: }
515: (void)printf(")\n");
516: }
517:
518: struct statfs *
519: getmntpt(const char *name)
520: {
521: struct statfs *mntbuf;
522: int i, mntsize;
523:
524: mntsize = getmntinfo(&mntbuf, MNT_NOWAIT);
525: for (i = mntsize - 1; i >= 0; i--) {
526: if (strcmp(mntbuf[i].f_mntfromname, name) == 0 ||
527: strcmp(mntbuf[i].f_mntonname, name) == 0)
528: return (&mntbuf[i]);
529: }
530: return (NULL);
531: }
532:
533: char *
534: catopt(char *s0, const char *s1)
535: {
536: size_t i;
537: char *cp;
538:
539: if (s1 == NULL || *s1 == '\0')
540: return s0;
541:
542: if (s0 && *s0) {
543: i = strlen(s0) + strlen(s1) + 1 + 1;
544: if ((cp = malloc(i)) == NULL)
545: errx(1, "malloc failed");
546: (void)snprintf(cp, i, "%s,%s", s0, s1);
547: } else
548: cp = strdup(s1);
549:
550: if (s0)
551: free(s0);
552: return (cp);
553: }
554:
555: void
556: mangle(char *options, int *argcp, const char **argv)
557: {
558: char *p, *s;
559: int argc;
560:
561: argc = *argcp;
562: for (s = options; (p = strsep(&s, ",")) != NULL;)
563: if (*p != '\0') {
564: if (*p == '-') {
565: argv[argc++] = p;
566: p = strchr(p, '=');
567: if (p) {
568: *p = '\0';
569: argv[argc++] = p+1;
570: }
571: } else if (strcmp(p, "rw") != 0) {
572: argv[argc++] = "-o";
573: argv[argc++] = p;
574: }
575: }
576:
577: *argcp = argc;
578: }
579:
580:
581: char *
582: update_options(char *opts, char *fstab, int curflags)
583: {
584: char *o, *p;
585: char *cur;
586: char *expopt, *newopt, *tmpopt;
587:
588: if (opts == NULL)
589: return strdup("");
590:
591: /* remove meta options from list */
592: remopt(fstab, MOUNT_META_OPTION_FSTAB);
593: remopt(fstab, MOUNT_META_OPTION_CURRENT);
594: cur = flags2opts(curflags);
595:
596: /*
597: * Expand all meta-options passed to us first.
598: */
599: expopt = NULL;
600: for (p = opts; (o = strsep(&p, ",")) != NULL;) {
601: if (strcmp(MOUNT_META_OPTION_FSTAB, o) == 0)
602: expopt = catopt(expopt, fstab);
603: else if (strcmp(MOUNT_META_OPTION_CURRENT, o) == 0)
604: expopt = catopt(expopt, cur);
605: else
606: expopt = catopt(expopt, o);
607: }
608: free(cur);
609: free(opts);
610:
611: /*
612: * Remove previous contradictory arguments. Given option "foo" we
613: * remove all the "nofoo" options. Given "nofoo" we remove "nonofoo"
614: * and "foo" - so we can deal with possible options like "notice".
615: */
616: newopt = NULL;
617: for (p = expopt; (o = strsep(&p, ",")) != NULL;) {
618: if ((tmpopt = malloc( strlen(o) + 2 + 1 )) == NULL)
619: errx(1, "malloc failed");
620:
621: strcpy(tmpopt, "no");
622: strcat(tmpopt, o);
623: remopt(newopt, tmpopt);
624: free(tmpopt);
625:
626: if (strncmp("no", o, 2) == 0)
627: remopt(newopt, o+2);
628:
629: newopt = catopt(newopt, o);
630: }
631: free(expopt);
632:
633: return newopt;
634: }
635:
636: void
637: remopt(char *string, const char *opt)
638: {
639: char *o, *p, *r;
640:
641: if (string == NULL || *string == '\0' || opt == NULL || *opt == '\0')
642: return;
643:
644: r = string;
645:
646: for (p = string; (o = strsep(&p, ",")) != NULL;) {
647: if (strcmp(opt, o) != 0) {
648: if (*r == ',' && *o != '\0')
649: r++;
650: while ((*r++ = *o++) != '\0')
651: ;
652: *--r = ',';
653: }
654: }
655: *r = '\0';
656: }
657:
658: void
659: usage(void)
660: {
661:
662: (void)fprintf(stderr, "%s\n%s\n%s\n",
663: "usage: mount [-dfpruvw] [-o options] [-t ufs | external_type] special node",
664: " mount [-adfpruvw] [-t ufs | external_type]",
665: " mount [-dfpruvw] special | node");
666: exit(1);
667: }
668:
669: void
670: putfsent(const struct statfs *ent)
671: {
672: struct fstab *fst;
673: char *opts;
674:
675: opts = flags2opts(ent->f_flags);
676: printf("%s\t%s\t%s %s", ent->f_mntfromname, ent->f_mntonname,
677: ent->f_fstypename, opts);
678: free(opts);
679:
680: if ((fst = getfsspec(ent->f_mntfromname)))
681: printf("\t%u %u\n", fst->fs_freq, fst->fs_passno);
682: else if ((fst = getfsfile(ent->f_mntonname)))
683: printf("\t%u %u\n", fst->fs_freq, fst->fs_passno);
684: else if (strcmp(ent->f_fstypename, "ufs") == 0) {
685: if (strcmp(ent->f_mntonname, "/") == 0)
686: printf("\t1 1\n");
687: else
688: printf("\t2 2\n");
689: } else
690: printf("\t0 0\n");
691: }
692:
693:
694: char *
695: flags2opts(int flags)
696: {
697: char *res;
698:
699: res = NULL;
700:
701: res = catopt(res, (flags & MNT_RDONLY) ? "ro" : "rw");
702:
703: if (flags & MNT_SYNCHRONOUS) res = catopt(res, "sync");
704: if (flags & MNT_NOEXEC) res = catopt(res, "noexec");
705: if (flags & MNT_NOSUID) res = catopt(res, "nosuid");
706: if (flags & MNT_NODEV) res = catopt(res, "nodev");
707: if (flags & MNT_UNION) res = catopt(res, "union");
708: if (flags & MNT_ASYNC) res = catopt(res, "async");
709: if (flags & MNT_NOATIME) res = catopt(res, "noatime");
710: if (flags & MNT_NOCLUSTERR) res = catopt(res, "noclusterr");
711: if (flags & MNT_NOCLUSTERW) res = catopt(res, "noclusterw");
712: if (flags & MNT_NOSYMFOLLOW) res = catopt(res, "nosymfollow");
713: if (flags & MNT_SUIDDIR) res = catopt(res, "suiddir");
714:
715: return res;
716: }