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