xref: /netbsd-src/sbin/mount/mount.c (revision 3cec974c61d7fac0a37c0377723a33214a458c8b)
1 /*	$NetBSD: mount.c,v 1.56 2001/02/18 06:15:49 tsutsui 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.56 2001/02/18 06:15:49 tsutsui 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 #include "vfslist.h"
71 
72 static int	debug, verbose;
73 
74 static void	catopt __P((char **, const char *));
75 static const char *
76 		getfslab __P((const char *str));
77 static struct statfs *
78 		getmntpt __P((const char *));
79 static int	hasopt __P((const char *, const char *));
80 static void	mangle __P((char *, int *, const char ***, int *));
81 static int	mountfs __P((const char *, const char *, const char *,
82 		    int, const char *, const char *, int));
83 static void	prmount __P((struct statfs *));
84 static void	usage __P((void));
85 
86 #ifndef NO_MOUNT_PROGS
87 void	checkname __P((int, char *[]));
88 #endif
89 int	main __P((int, 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 #ifndef NO_MOUNT_PROGS
136 	/* if called as specific mount, call it's main mount routine */
137 	checkname(argc, argv);
138 #endif
139 
140 	/* started as "mount" */
141 	all = forceall = init_flags = 0;
142 	options = NULL;
143 	vfslist = NULL;
144 	vfstype = ffs_fstype;
145 	while ((ch = getopt(argc, argv, "Aadfo:rwt:uv")) != -1)
146 		switch (ch) {
147 		case 'A':
148 			all = forceall = 1;
149 			break;
150 		case 'a':
151 			all = 1;
152 			break;
153 		case 'd':
154 			debug = 1;
155 			break;
156 		case 'f':
157 			init_flags |= MNT_FORCE;
158 			break;
159 		case 'o':
160 			if (*optarg)
161 				catopt(&options, optarg);
162 			break;
163 		case 'r':
164 			init_flags |= MNT_RDONLY;
165 			break;
166 		case 't':
167 			if (vfslist != NULL)
168 				errx(1,
169 				    "only one -t option may be specified.");
170 			vfslist = makevfslist(optarg);
171 			vfstype = optarg;
172 			break;
173 		case 'u':
174 			init_flags |= MNT_UPDATE;
175 			break;
176 		case 'v':
177 			verbose = 1;
178 			break;
179 		case 'w':
180 			init_flags &= ~MNT_RDONLY;
181 			break;
182 		case '?':
183 		default:
184 			usage();
185 			/* NOTREACHED */
186 		}
187 	argc -= optind;
188 	argv += optind;
189 
190 #define	BADTYPE(type)							\
191 	(strcmp(type, FSTAB_RO) &&					\
192 	    strcmp(type, FSTAB_RW) && strcmp(type, FSTAB_RQ))
193 
194 	rval = 0;
195 	switch (argc) {
196 	case 0:
197 		if (all)
198 			while ((fs = getfsent()) != NULL) {
199 				if (BADTYPE(fs->fs_type))
200 					continue;
201 				if (checkvfsname(fs->fs_vfstype, vfslist))
202 					continue;
203 				if (hasopt(fs->fs_mntops, "noauto"))
204 					continue;
205 				if (mountfs(fs->fs_vfstype, fs->fs_spec,
206 				    fs->fs_file, init_flags, options,
207 				    fs->fs_mntops, !forceall))
208 					rval = 1;
209 			}
210 		else {
211 			if ((mntsize = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0)
212 				err(1, "getmntinfo");
213 			for (i = 0; i < mntsize; i++) {
214 				if (checkvfsname(mntbuf[i].f_fstypename,
215 				    vfslist))
216 					continue;
217 				prmount(&mntbuf[i]);
218 			}
219 		}
220 		exit(rval);
221 		/* NOTREACHED */
222 	case 1:
223 		if (vfslist != NULL) {
224 			usage();
225 			/* NOTREACHED */
226 		}
227 
228 		if (init_flags & MNT_UPDATE) {
229 			if ((mntbuf = getmntpt(*argv)) == NULL)
230 				errx(1,
231 				    "unknown special file or file system %s.",
232 				    *argv);
233 			if ((fs = getfsfile(mntbuf->f_mntonname)) != NULL) {
234 				mntfromname = fs->fs_spec;
235 				/* ignore the fstab file options.  */
236 				fs->fs_mntops = NULL;
237 			} else
238 				mntfromname = mntbuf->f_mntfromname;
239 			mntonname  = mntbuf->f_mntonname;
240 			fstypename = mntbuf->f_fstypename;
241 			mountopts  = NULL;
242 		} else {
243 			if ((fs = getfsfile(*argv)) == NULL &&
244 			    (fs = getfsspec(*argv)) == NULL)
245 				errx(1,
246 				    "%s: unknown special file or file system.",
247 				    *argv);
248 			if (BADTYPE(fs->fs_type))
249 				errx(1, "%s has unknown file system type.",
250 				    *argv);
251 			mntfromname = fs->fs_spec;
252 			mntonname   = fs->fs_file;
253 			fstypename  = fs->fs_vfstype;
254 			mountopts   = fs->fs_mntops;
255 		}
256 		rval = mountfs(fstypename, mntfromname,
257 		    mntonname, init_flags, options, mountopts, 0);
258 		break;
259 	case 2:
260 		/*
261 		 * If -t flag has not been specified, and spec contains either
262 		 * a ':' or a '@' then assume that an NFS filesystem is being
263 		 * specified ala Sun.
264 		 */
265 		if (vfslist == NULL) {
266 			if (strpbrk(argv[0], ":@") != NULL)
267 				vfstype = "nfs";
268 			else {
269 				vfstype = getfslab(argv[0]);
270 				if (vfstype == NULL)
271 					vfstype = ffs_fstype;
272 			}
273 		}
274 		rval = mountfs(vfstype,
275 		    argv[0], argv[1], init_flags, options, NULL, 0);
276 		break;
277 	default:
278 		usage();
279 		/* NOTREACHED */
280 	}
281 
282 	/*
283 	 * If the mount was successfully, and done by root, tell mountd the
284 	 * good news.  Pid checks are probably unnecessary, but don't hurt.
285 	 */
286 	if (rval == 0 && getuid() == 0 &&
287 	    (mountdfp = fopen(_PATH_MOUNTDPID, "r")) != NULL) {
288 		int pid;
289 
290 		if (fscanf(mountdfp, "%d", &pid) == 1 &&
291 		    pid > 0 && kill(pid, SIGHUP) == -1 && errno != ESRCH)
292 			err(1, "signal mountd");
293 		(void)fclose(mountdfp);
294 	}
295 
296 	exit(rval);
297 	/* NOTREACHED */
298 }
299 
300 int
301 hasopt(mntopts, option)
302 	const char *mntopts, *option;
303 {
304 	int negative, found;
305 	char *opt, *optbuf;
306 
307 	if (option[0] == 'n' && option[1] == 'o') {
308 		negative = 1;
309 		option += 2;
310 	} else
311 		negative = 0;
312 	optbuf = strdup(mntopts);
313 	found = 0;
314 	for (opt = optbuf; (opt = strtok(opt, ",")) != NULL; opt = NULL) {
315 		if (opt[0] == 'n' && opt[1] == 'o') {
316 			if (!strcasecmp(opt + 2, option))
317 				found = negative;
318 		} else if (!strcasecmp(opt, option))
319 			found = !negative;
320 	}
321 	free(optbuf);
322 	return (found);
323 }
324 
325 static int
326 mountfs(vfstype, spec, name, flags, options, mntopts, skipmounted)
327 	const char *vfstype, *spec, *name, *options, *mntopts;
328 	int flags, skipmounted;
329 {
330 	/* List of directories containing mount_xxx subcommands. */
331 	static const char *edirs[] = {
332 		_PATH_SBIN,
333 		_PATH_USRSBIN,
334 		NULL
335 	};
336 	const char **argv, **edir;
337 	struct statfs *sfp, sf;
338 	pid_t pid;
339 	int argc, numfs, i, status, maxargc;
340 	char *optbuf, execname[MAXPATHLEN + 1], execbase[MAXPATHLEN],
341 	    mntpath[MAXPATHLEN];
342 
343 #ifdef __GNUC__
344 	(void) &name;
345 	(void) &optbuf;
346 	(void) &vfstype;
347 #endif
348 
349 	if (realpath(name, mntpath) == NULL) {
350 		warn("realpath %s", name);
351 		return (1);
352 	}
353 
354 	name = mntpath;
355 
356 	optbuf = NULL;
357 	if (mntopts)
358 		catopt(&optbuf, mntopts);
359 	if (options)
360 		catopt(&optbuf, options);
361 	if (!mntopts && !options)
362 		catopt(&optbuf, "rw");
363 
364 	if (!strcmp(name, "/"))
365 		flags |= MNT_UPDATE;
366 	else if (skipmounted) {
367 		if ((numfs = getmntinfo(&sfp, MNT_WAIT)) == 0) {
368 			warn("getmntinfo");
369 			return (1);
370 		}
371 		for(i = 0; i < numfs; i++) {
372 			/*
373 			 * XXX can't check f_mntfromname,
374 			 * thanks to mfs, union, etc.
375 			 */
376 			if (strncmp(name, sfp[i].f_mntonname, MNAMELEN) == 0 &&
377 			    strncmp(vfstype, sfp[i].f_fstypename,
378 				MFSNAMELEN) == 0) {
379 				if (verbose)
380 					(void)printf("%s on %s type %.*s: "
381 					    "%s\n",
382 					    sfp[i].f_mntfromname,
383 					    sfp[i].f_mntonname,
384 					    MFSNAMELEN,
385 					    sfp[i].f_fstypename,
386 					    "already mounted");
387 				return (0);
388 			}
389 		}
390 	}
391 	if (flags & MNT_FORCE)
392 		catopt(&optbuf, "force");
393 	if (flags & MNT_RDONLY)
394 		catopt(&optbuf, "ro");
395 
396 	if (flags & MNT_UPDATE) {
397 		catopt(&optbuf, "update");
398 		/* Figure out the fstype only if we defaulted to ffs */
399 		if (vfstype == ffs_fstype && statfs(name, &sf) != -1)
400 			vfstype = sf.f_fstypename;
401 	}
402 
403 	maxargc = 64;
404 	argv = malloc(sizeof(char *) * maxargc);
405 
406 	(void) snprintf(execbase, sizeof(execbase), "mount_%s", vfstype);
407 	argc = 0;
408 	argv[argc++] = execbase;
409 	if (optbuf)
410 		mangle(optbuf, &argc, &argv, &maxargc);
411 	argv[argc++] = spec;
412 	argv[argc++] = name;
413 	argv[argc] = NULL;
414 
415 	if (verbose) {
416 		(void)printf("exec:");
417 		for (i = 0; i < argc; i++)
418 			(void)printf(" %s", argv[i]);
419 		(void)printf("\n");
420 	}
421 
422 	switch (pid = vfork()) {
423 	case -1:				/* Error. */
424 		warn("vfork");
425 		if (optbuf)
426 			free(optbuf);
427 		return (1);
428 
429 	case 0:					/* Child. */
430 		if (debug)
431 			_exit(0);
432 
433 		/* Go find an executable. */
434 		edir = edirs;
435 		do {
436 			(void)snprintf(execname,
437 			    sizeof(execname), "%s/%s", *edir, execbase);
438 			(void)execv(execname, (char * const *)argv);
439 			if (errno != ENOENT)
440 				warn("exec %s for %s", execname, name);
441 		} while (*++edir != NULL);
442 
443 		if (errno == ENOENT)
444 			warnx("%s not found for %s", execbase, name);
445 		_exit(1);
446 		/* NOTREACHED */
447 
448 	default:				/* Parent. */
449 		if (optbuf)
450 			free(optbuf);
451 
452 		if (waitpid(pid, &status, 0) < 0) {
453 			warn("waitpid");
454 			return (1);
455 		}
456 
457 		if (WIFEXITED(status)) {
458 			if (WEXITSTATUS(status) != 0)
459 				return (WEXITSTATUS(status));
460 		} else if (WIFSIGNALED(status)) {
461 			warnx("%s: %s", name, strsignal(WTERMSIG(status)));
462 			return (1);
463 		}
464 
465 		if (verbose) {
466 			if (statfs(name, &sf) < 0) {
467 				warn("statfs %s", name);
468 				return (1);
469 			}
470 			prmount(&sf);
471 		}
472 		break;
473 	}
474 
475 	return (0);
476 }
477 
478 static void
479 prmount(sfp)
480 	struct statfs *sfp;
481 {
482 	int flags;
483 	const struct opt *o;
484 	struct passwd *pw;
485 	int f;
486 
487 	(void)printf("%s on %s type %.*s", sfp->f_mntfromname,
488 	    sfp->f_mntonname, MFSNAMELEN, sfp->f_fstypename);
489 
490 	flags = sfp->f_flags & MNT_VISFLAGMASK;
491 	for (f = 0, o = optnames; flags && o->o_opt; o++)
492 		if (flags & o->o_opt) {
493 			if (!o->o_silent)
494 				(void)printf("%s%s", !f++ ? " (" : ", ",
495 				    o->o_name);
496 			flags &= ~o->o_opt;
497 		}
498 	if (flags)
499 		(void)printf("%sunknown flag%s %#x", !f++ ? " (" : ", ",
500 		    flags & (flags - 1) ? "s" : "", flags);
501 	if (sfp->f_owner) {
502 		(void)printf("%smounted by ", !f++ ? " (" : ", ");
503 		if ((pw = getpwuid(sfp->f_owner)) != NULL)
504 			(void)printf("%s", pw->pw_name);
505 		else
506 			(void)printf("%d", sfp->f_owner);
507 	}
508 	if (verbose)
509 		(void)printf("%swrites: sync %ld async %ld)\n",
510 		    !f++ ? " (" : ", ", sfp->f_syncwrites, sfp->f_asyncwrites);
511 	else
512 		(void)printf("%s", f ? ")\n" : "\n");
513 }
514 
515 static struct statfs *
516 getmntpt(name)
517 	const char *name;
518 {
519 	struct statfs *mntbuf;
520 	int i, mntsize;
521 
522 	mntsize = getmntinfo(&mntbuf, MNT_NOWAIT);
523 	for (i = 0; i < mntsize; i++)
524 		if (strcmp(mntbuf[i].f_mntfromname, name) == 0 ||
525 		    strcmp(mntbuf[i].f_mntonname, name) == 0)
526 			return (&mntbuf[i]);
527 	return (NULL);
528 }
529 
530 static void
531 catopt(sp, o)
532 	char **sp;
533 	const char *o;
534 {
535 	char *s;
536 	size_t i, j;
537 
538 	s = *sp;
539 	if (s) {
540 		i = strlen(s);
541 		j = i + 1 + strlen(o) + 1;
542 		s = realloc(s, j);
543 		(void)snprintf(s + i, j, ",%s", o);
544 	} else
545 		s = strdup(o);
546 	*sp = s;
547 }
548 
549 static void
550 mangle(options, argcp, argvp, maxargcp)
551 	char *options;
552 	int *argcp, *maxargcp;
553 	const char ***argvp;
554 {
555 	char *p, *s;
556 	int argc, maxargc;
557 	const char **argv;
558 
559 	argc = *argcp;
560 	argv = *argvp;
561 	maxargc = *maxargcp;
562 
563 	for (s = options; (p = strsep(&s, ",")) != NULL;) {
564 		/* Always leave space for one more argument and the NULL. */
565 		if (argc >= maxargc - 4) {
566 			maxargc <<= 1;
567 			argv = realloc(argv, maxargc * sizeof(char *));
568 		}
569 		if (*p != '\0') {
570 			if (*p == '-') {
571 				argv[argc++] = p;
572 				p = strchr(p, '=');
573 				if (p) {
574 					*p = '\0';
575 					argv[argc++] = p+1;
576 				}
577 			} else if (strcmp(p, "rw") != 0) {
578 				argv[argc++] = "-o";
579 				argv[argc++] = p;
580 			}
581 		}
582 	}
583 
584 	*argcp = argc;
585 	*argvp = argv;
586 	*maxargcp = maxargc;
587 }
588 
589 /* Deduce the filesystem type from the disk label. */
590 static const char *
591 getfslab(str)
592 	const char *str;
593 {
594 	struct disklabel dl;
595 	int fd;
596 	int part;
597 	const char *vfstype;
598 	u_char fstype;
599 	char buf[MAXPATHLEN + 1];
600 	char *sp, *ep;
601 
602 	if ((fd = open(str, O_RDONLY)) == -1) {
603 		/*
604 		 * Iff we get EBUSY try the raw device. Since mount always uses
605 		 * the block device we know we are never passed a raw device.
606 		 */
607 		if (errno != EBUSY)
608 			err(1, "cannot open `%s'", str);
609 		strlcpy(buf, str, MAXPATHLEN);
610 		if ((sp = strrchr(buf, '/')) != NULL)
611 			++sp;
612 		else
613 			sp = buf;
614 		for (ep = sp + strlen(sp) + 1;  ep > sp; ep--)
615 			*ep = *(ep - 1);
616 		*sp = 'r';
617 
618 		/* Silently fail here - mount call can display error */
619 		if ((fd = open(buf, O_RDONLY)) == -1)
620 			return (NULL);
621 	}
622 
623 	if (ioctl(fd, DIOCGDINFO, &dl) == -1) {
624 		(void) close(fd);
625 		return (NULL);
626 	}
627 
628 	(void) close(fd);
629 
630 	part = str[strlen(str) - 1] - 'a';
631 
632 	if (part < 0 || part >= dl.d_npartitions)
633 		errx(1, "partition `%s' is not defined on disk", str);
634 
635 	/* Return NULL for unknown types - caller can fall back to ffs */
636 	if ((fstype = dl.d_partitions[part].p_fstype) >= FSMAXMOUNTNAMES)
637 		vfstype = NULL;
638 	else
639 		vfstype = mountnames[fstype];
640 
641 	return (vfstype);
642 }
643 
644 static void
645 usage()
646 {
647 
648 	(void)fprintf(stderr,
649 	    "usage: mount %s\n       mount %s\n       mount %s\n",
650 	    "[-dfruvw] [-o options] [-t ffs | external_type] special node",
651 	    "[-adfruvw] [-t ffs | external_type]",
652 	    "[-dfruvw] special | node");
653 	exit(1);
654 	/* NOTREACHED */
655 }
656