mount.c revision 1.53 1 /* $NetBSD: mount.c,v 1.53 2000/11/01 04:01:45 enami Exp $ */
2
3 /*
4 * Copyright (c) 1980, 1989, 1993, 1994
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by the University of
18 * California, Berkeley and its contributors.
19 * 4. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36 #include <sys/cdefs.h>
37 #ifndef lint
38 __COPYRIGHT("@(#) Copyright (c) 1980, 1989, 1993, 1994\n\
39 The Regents of the University of California. All rights reserved.\n");
40 #endif /* not lint */
41
42 #ifndef lint
43 #if 0
44 static char sccsid[] = "@(#)mount.c 8.25 (Berkeley) 5/8/95";
45 #else
46 __RCSID("$NetBSD: mount.c,v 1.53 2000/11/01 04:01:45 enami Exp $");
47 #endif
48 #endif /* not lint */
49
50 #include <sys/param.h>
51 #include <sys/mount.h>
52 #include <sys/wait.h>
53
54 #include <err.h>
55 #include <errno.h>
56 #include <fstab.h>
57 #include <pwd.h>
58 #include <signal.h>
59 #include <stdio.h>
60 #include <stdlib.h>
61 #include <string.h>
62 #include <unistd.h>
63
64 #define MOUNTNAMES
65 #include <fcntl.h>
66 #include <sys/disklabel.h>
67 #include <sys/ioctl.h>
68
69 #include "pathnames.h"
70
71 static int debug, verbose;
72
73 static void catopt __P((char **, const char *));
74 static const char *
75 getfslab __P((const char *str));
76 static struct statfs *
77 getmntpt __P((const char *));
78 static int hasopt __P((const char *, const char *));
79 static void mangle __P((char *, int *, const char ***, int *));
80 static int mountfs __P((const char *, const char *, const char *,
81 int, const char *, const char *, int));
82 static void prmount __P((struct statfs *));
83 static void usage __P((void));
84
85 void checkname __P((int, char *[]));
86 int checkvfsname __P((const char *, const char **));
87 int main __P((int, char *[]));
88 const char **
89 makevfslist __P((char *));
90
91 /* Map from mount otions to printable formats. */
92 static const struct opt {
93 int o_opt;
94 int o_silent;
95 const char *o_name;
96 } optnames[] = {
97 { MNT_ASYNC, 0, "asynchronous" },
98 { MNT_DEFEXPORTED, 1, "exported to the world" },
99 { MNT_EXKERB, 1, "kerberos uid mapping" },
100 { MNT_EXPORTED, 0, "NFS exported" },
101 { MNT_EXPORTANON, 1, "anon uid mapping" },
102 { MNT_EXRDONLY, 1, "exported read-only" },
103 { MNT_LOCAL, 0, "local" },
104 { MNT_NOATIME, 0, "noatime" },
105 { MNT_NOCOREDUMP, 0, "nocoredump" },
106 { MNT_NODEV, 0, "nodev" },
107 { MNT_NODEVMTIME, 0, "nodevmtime" },
108 { MNT_NOEXEC, 0, "noexec" },
109 { MNT_NOSUID, 0, "nosuid" },
110 { MNT_QUOTA, 0, "with quotas" },
111 { MNT_RDONLY, 0, "read-only" },
112 { MNT_ROOTFS, 1, "root file system" },
113 { MNT_SYMPERM, 0, "symperm" },
114 { MNT_SYNCHRONOUS, 0, "synchronous" },
115 { MNT_UNION, 0, "union" },
116 { MNT_SOFTDEP, 0, "soft dependencies" },
117 { 0 }
118 };
119
120 static char ffs_fstype[] = "ffs";
121
122 int
123 main(argc, argv)
124 int argc;
125 char *argv[];
126 {
127 const char *mntfromname, *mntonname, **vfslist, *vfstype;
128 struct fstab *fs;
129 struct statfs *mntbuf;
130 FILE *mountdfp;
131 int all, ch, forceall, i, init_flags, mntsize, rval;
132 char *options;
133 const char *mountopts, *fstypename;
134
135 /* if called as specific mount, call it's main mount routine */
136 checkname(argc, argv);
137
138 /* started as "mount" */
139 all = forceall = init_flags = 0;
140 options = NULL;
141 vfslist = NULL;
142 vfstype = ffs_fstype;
143 while ((ch = getopt(argc, argv, "Aadfo:rwt:uv")) != -1)
144 switch (ch) {
145 case 'A':
146 all = forceall = 1;
147 break;
148 case 'a':
149 all = 1;
150 break;
151 case 'd':
152 debug = 1;
153 break;
154 case 'f':
155 init_flags |= MNT_FORCE;
156 break;
157 case 'o':
158 if (*optarg)
159 catopt(&options, optarg);
160 break;
161 case 'r':
162 init_flags |= MNT_RDONLY;
163 break;
164 case 't':
165 if (vfslist != NULL)
166 errx(1,
167 "only one -t option may be specified.");
168 vfslist = makevfslist(optarg);
169 vfstype = optarg;
170 break;
171 case 'u':
172 init_flags |= MNT_UPDATE;
173 break;
174 case 'v':
175 verbose = 1;
176 break;
177 case 'w':
178 init_flags &= ~MNT_RDONLY;
179 break;
180 case '?':
181 default:
182 usage();
183 /* NOTREACHED */
184 }
185 argc -= optind;
186 argv += optind;
187
188 #define BADTYPE(type) \
189 (strcmp(type, FSTAB_RO) && \
190 strcmp(type, FSTAB_RW) && strcmp(type, FSTAB_RQ))
191
192 rval = 0;
193 switch (argc) {
194 case 0:
195 if (all)
196 while ((fs = getfsent()) != NULL) {
197 if (BADTYPE(fs->fs_type))
198 continue;
199 if (checkvfsname(fs->fs_vfstype, vfslist))
200 continue;
201 if (hasopt(fs->fs_mntops, "noauto"))
202 continue;
203 if (mountfs(fs->fs_vfstype, fs->fs_spec,
204 fs->fs_file, init_flags, options,
205 fs->fs_mntops, !forceall))
206 rval = 1;
207 }
208 else {
209 if ((mntsize = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0)
210 err(1, "getmntinfo");
211 for (i = 0; i < mntsize; i++) {
212 if (checkvfsname(mntbuf[i].f_fstypename,
213 vfslist))
214 continue;
215 prmount(&mntbuf[i]);
216 }
217 }
218 exit(rval);
219 /* NOTREACHED */
220 case 1:
221 if (vfslist != NULL) {
222 usage();
223 /* NOTREACHED */
224 }
225
226 if (init_flags & MNT_UPDATE) {
227 if ((mntbuf = getmntpt(*argv)) == NULL)
228 errx(1,
229 "unknown special file or file system %s.",
230 *argv);
231 if ((fs = getfsfile(mntbuf->f_mntonname)) != NULL) {
232 mntfromname = fs->fs_spec;
233 /* ignore the fstab file options. */
234 fs->fs_mntops = NULL;
235 } else
236 mntfromname = mntbuf->f_mntfromname;
237 mntonname = mntbuf->f_mntonname;
238 fstypename = mntbuf->f_fstypename;
239 mountopts = NULL;
240 } else {
241 if ((fs = getfsfile(*argv)) == NULL &&
242 (fs = getfsspec(*argv)) == NULL)
243 errx(1,
244 "%s: unknown special file or file system.",
245 *argv);
246 if (BADTYPE(fs->fs_type))
247 errx(1, "%s has unknown file system type.",
248 *argv);
249 mntfromname = fs->fs_spec;
250 mntonname = fs->fs_file;
251 fstypename = fs->fs_vfstype;
252 mountopts = fs->fs_mntops;
253 }
254 rval = mountfs(fstypename, mntfromname,
255 mntonname, init_flags, options, mountopts, 0);
256 break;
257 case 2:
258 /*
259 * If -t flag has not been specified, and spec contains either
260 * a ':' or a '@' then assume that an NFS filesystem is being
261 * specified ala Sun.
262 */
263 if (vfslist == NULL) {
264 if (strpbrk(argv[0], ":@") != NULL)
265 vfstype = "nfs";
266 else {
267 vfstype = getfslab(argv[0]);
268 if (vfstype == NULL)
269 vfstype = ffs_fstype;
270 }
271 }
272 rval = mountfs(vfstype,
273 argv[0], argv[1], init_flags, options, NULL, 0);
274 break;
275 default:
276 usage();
277 /* NOTREACHED */
278 }
279
280 /*
281 * If the mount was successfully, and done by root, tell mountd the
282 * good news. Pid checks are probably unnecessary, but don't hurt.
283 */
284 if (rval == 0 && getuid() == 0 &&
285 (mountdfp = fopen(_PATH_MOUNTDPID, "r")) != NULL) {
286 int pid;
287
288 if (fscanf(mountdfp, "%d", &pid) == 1 &&
289 pid > 0 && kill(pid, SIGHUP) == -1 && errno != ESRCH)
290 err(1, "signal mountd");
291 (void)fclose(mountdfp);
292 }
293
294 exit(rval);
295 /* NOTREACHED */
296 }
297
298 int
299 hasopt(mntopts, option)
300 const char *mntopts, *option;
301 {
302 int negative, found;
303 char *opt, *optbuf;
304
305 if (option[0] == 'n' && option[1] == 'o') {
306 negative = 1;
307 option += 2;
308 } else
309 negative = 0;
310 optbuf = strdup(mntopts);
311 found = 0;
312 for (opt = optbuf; (opt = strtok(opt, ",")) != NULL; opt = NULL) {
313 if (opt[0] == 'n' && opt[1] == 'o') {
314 if (!strcasecmp(opt + 2, option))
315 found = negative;
316 } else if (!strcasecmp(opt, option))
317 found = !negative;
318 }
319 free(optbuf);
320 return (found);
321 }
322
323 static int
324 mountfs(vfstype, spec, name, flags, options, mntopts, skipmounted)
325 const char *vfstype, *spec, *name, *options, *mntopts;
326 int flags, skipmounted;
327 {
328 /* List of directories containing mount_xxx subcommands. */
329 static const char *edirs[] = {
330 _PATH_SBIN,
331 _PATH_USRSBIN,
332 NULL
333 };
334 const char **argv, **edir;
335 struct statfs *sfp, sf;
336 pid_t pid;
337 int argc, numfs, i, status, maxargc;
338 char *optbuf, execname[MAXPATHLEN + 1], execbase[MAXPATHLEN],
339 mntpath[MAXPATHLEN];
340
341 #ifdef __GNUC__
342 (void) &name;
343 (void) &optbuf;
344 (void) &vfstype;
345 #endif
346
347 if (realpath(name, mntpath) == NULL) {
348 warn("realpath %s", name);
349 return (1);
350 }
351
352 name = mntpath;
353
354 optbuf = NULL;
355 if (mntopts)
356 catopt(&optbuf, mntopts);
357 if (options)
358 catopt(&optbuf, options);
359 if (!mntopts && !options)
360 catopt(&optbuf, "rw");
361
362 if (!strcmp(name, "/"))
363 flags |= MNT_UPDATE;
364 else if (skipmounted) {
365 if ((numfs = getmntinfo(&sfp, MNT_WAIT)) == 0) {
366 warn("getmntinfo");
367 return (1);
368 }
369 for(i = 0; i < numfs; i++) {
370 /*
371 * XXX can't check f_mntfromname,
372 * thanks to mfs, union, etc.
373 */
374 if (strncmp(name, sfp[i].f_mntonname, MNAMELEN) == 0 &&
375 strncmp(vfstype, sfp[i].f_fstypename,
376 MFSNAMELEN) == 0) {
377 if (verbose)
378 (void)printf("%s on %s type %.*s: "
379 "%s\n",
380 sfp[i].f_mntfromname,
381 sfp[i].f_mntonname,
382 MFSNAMELEN,
383 sfp[i].f_fstypename,
384 "already mounted");
385 return (0);
386 }
387 }
388 }
389 if (flags & MNT_FORCE)
390 catopt(&optbuf, "force");
391 if (flags & MNT_RDONLY)
392 catopt(&optbuf, "ro");
393
394 if (flags & MNT_UPDATE) {
395 catopt(&optbuf, "update");
396 /* Figure out the fstype only if we defaulted to ffs */
397 if (vfstype == ffs_fstype && statfs(name, &sf) != -1)
398 vfstype = sf.f_fstypename;
399 }
400
401 maxargc = 64;
402 argv = malloc(sizeof(char *) * maxargc);
403
404 (void) snprintf(execbase, sizeof(execbase), "mount_%s", vfstype);
405 argc = 0;
406 argv[argc++] = execbase;
407 if (optbuf)
408 mangle(optbuf, &argc, &argv, &maxargc);
409 argv[argc++] = spec;
410 argv[argc++] = name;
411 argv[argc] = NULL;
412
413 if (verbose) {
414 (void)printf("exec:");
415 for (i = 0; i < argc; i++)
416 (void)printf(" %s", argv[i]);
417 (void)printf("\n");
418 }
419
420 switch (pid = vfork()) {
421 case -1: /* Error. */
422 warn("vfork");
423 if (optbuf)
424 free(optbuf);
425 return (1);
426
427 case 0: /* Child. */
428 if (debug)
429 _exit(0);
430
431 /* Go find an executable. */
432 edir = edirs;
433 do {
434 (void)snprintf(execname,
435 sizeof(execname), "%s/%s", *edir, execbase);
436 (void)execv(execname, (char * const *)argv);
437 if (errno != ENOENT)
438 warn("exec %s for %s", execname, name);
439 } while (*++edir != NULL);
440
441 if (errno == ENOENT)
442 warnx("%s not found for %s", execbase, name);
443 _exit(1);
444 /* NOTREACHED */
445
446 default: /* Parent. */
447 if (optbuf)
448 free(optbuf);
449
450 if (waitpid(pid, &status, 0) < 0) {
451 warn("waitpid");
452 return (1);
453 }
454
455 if (WIFEXITED(status)) {
456 if (WEXITSTATUS(status) != 0)
457 return (WEXITSTATUS(status));
458 } else if (WIFSIGNALED(status)) {
459 warnx("%s: %s", name, strsignal(WTERMSIG(status)));
460 return (1);
461 }
462
463 if (verbose) {
464 if (statfs(name, &sf) < 0) {
465 warn("statfs %s", name);
466 return (1);
467 }
468 prmount(&sf);
469 }
470 break;
471 }
472
473 return (0);
474 }
475
476 static void
477 prmount(sfp)
478 struct statfs *sfp;
479 {
480 int flags;
481 const struct opt *o;
482 struct passwd *pw;
483 int f;
484
485 (void)printf("%s on %s type %.*s", sfp->f_mntfromname,
486 sfp->f_mntonname, MFSNAMELEN, sfp->f_fstypename);
487
488 flags = sfp->f_flags & MNT_VISFLAGMASK;
489 for (f = 0, o = optnames; flags && o->o_opt; o++)
490 if (flags & o->o_opt) {
491 if (!o->o_silent)
492 (void)printf("%s%s", !f++ ? " (" : ", ",
493 o->o_name);
494 flags &= ~o->o_opt;
495 }
496 if (flags)
497 (void)printf("%sunknown flag%s %#x", !f++ ? " (" : ", ",
498 flags & (flags - 1) ? "s" : "", flags);
499 if (sfp->f_owner) {
500 (void)printf("%smounted by ", !f++ ? " (" : ", ");
501 if ((pw = getpwuid(sfp->f_owner)) != NULL)
502 (void)printf("%s", pw->pw_name);
503 else
504 (void)printf("%d", sfp->f_owner);
505 }
506 if (verbose)
507 (void)printf("%swrites: sync %ld async %ld)\n",
508 !f++ ? " (" : ", ", sfp->f_syncwrites, sfp->f_asyncwrites);
509 else
510 (void)printf("%s", f ? ")\n" : "\n");
511 }
512
513 static struct statfs *
514 getmntpt(name)
515 const char *name;
516 {
517 struct statfs *mntbuf;
518 int i, mntsize;
519
520 mntsize = getmntinfo(&mntbuf, MNT_NOWAIT);
521 for (i = 0; i < mntsize; i++)
522 if (strcmp(mntbuf[i].f_mntfromname, name) == 0 ||
523 strcmp(mntbuf[i].f_mntonname, name) == 0)
524 return (&mntbuf[i]);
525 return (NULL);
526 }
527
528 static void
529 catopt(sp, o)
530 char **sp;
531 const char *o;
532 {
533 char *s;
534 size_t i, j;
535
536 s = *sp;
537 if (s) {
538 i = strlen(s);
539 j = i + 1 + strlen(o) + 1;
540 s = realloc(s, j);
541 (void)snprintf(s + i, j, ",%s", o);
542 } else
543 s = strdup(o);
544 *sp = s;
545 }
546
547 static void
548 mangle(options, argcp, argvp, maxargcp)
549 char *options;
550 int *argcp, *maxargcp;
551 const char ***argvp;
552 {
553 char *p, *s;
554 int argc, maxargc;
555 const char **argv;
556
557 argc = *argcp;
558 argv = *argvp;
559 maxargc = *maxargcp;
560
561 for (s = options; (p = strsep(&s, ",")) != NULL;) {
562 /* Always leave space for one more argument and the NULL. */
563 if (argc >= maxargc - 4) {
564 maxargc <<= 1;
565 argv = realloc(argv, maxargc * sizeof(char *));
566 }
567 if (*p != '\0') {
568 if (*p == '-') {
569 argv[argc++] = p;
570 p = strchr(p, '=');
571 if (p) {
572 *p = '\0';
573 argv[argc++] = p+1;
574 }
575 } else if (strcmp(p, "rw") != 0) {
576 argv[argc++] = "-o";
577 argv[argc++] = p;
578 }
579 }
580 }
581
582 *argcp = argc;
583 *argvp = argv;
584 *maxargcp = maxargc;
585 }
586
587 /* Deduce the filesystem type from the disk label. */
588 static const char *
589 getfslab(str)
590 const char *str;
591 {
592 struct disklabel dl;
593 int fd;
594 int part;
595 const char *vfstype;
596 u_char fstype;
597 char buf[MAXPATHLEN + 1];
598 char *sp, *ep;
599
600 if ((fd = open(str, O_RDONLY)) == -1) {
601 /*
602 * Iff we get EBUSY try the raw device. Since mount always uses
603 * the block device we know we are never passed a raw device.
604 */
605 if (errno != EBUSY)
606 err(1, "cannot open `%s'", str);
607 strlcpy(buf, str, MAXPATHLEN);
608 if ((sp = strrchr(buf, '/')) != NULL)
609 ++sp;
610 else
611 sp = buf;
612 for (ep = sp + strlen(sp) + 1; ep > sp; ep--)
613 *ep = *(ep - 1);
614 *sp = 'r';
615
616 /* Silently fail here - mount call can display error */
617 if ((fd = open(buf, O_RDONLY)) == -1)
618 return (NULL);
619 }
620
621 if (ioctl(fd, DIOCGDINFO, &dl) == -1) {
622 warn("cannot get disklabel for `%s'", str);
623 return (NULL);
624 }
625
626 (void) close(fd);
627
628 part = str[strlen(str) - 1] - 'a';
629
630 if (part < 0 || part >= dl.d_npartitions)
631 errx(1, "partition `%s' is not defined on disk", str);
632
633 /* Return NULL for unknown types - caller can fall back to ffs */
634 if ((fstype = dl.d_partitions[part].p_fstype) >= FSMAXMOUNTNAMES)
635 vfstype = NULL;
636 else
637 vfstype = mountnames[fstype];
638
639 return (vfstype);
640 }
641
642 static void
643 usage()
644 {
645
646 (void)fprintf(stderr,
647 "usage: mount %s\n mount %s\n mount %s\n",
648 "[-dfruvw] [-o options] [-t ffs | external_type] special node",
649 "[-adfruvw] [-t ffs | external_type]",
650 "[-dfruvw] special | node");
651 exit(1);
652 /* NOTREACHED */
653 }
654