xref: /openbsd/sbin/mount/mount.c (revision fd84ef7e)
1 /*	$OpenBSD: mount.c,v 1.23 2001/12/16 22:45:37 miod Exp $	*/
2 /*	$NetBSD: mount.c,v 1.24 1995/11/18 03:34:29 cgd Exp $	*/
3 
4 /*
5  * Copyright (c) 1980, 1989, 1993, 1994
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *	This product includes software developed by the University of
19  *	California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36 
37 #ifndef lint
38 static char copyright[] =
39 "@(#) Copyright (c) 1980, 1989, 1993, 1994\n\
40 	The Regents of the University of California.  All rights reserved.\n";
41 #endif /* not lint */
42 
43 #ifndef lint
44 #if 0
45 static char sccsid[] = "@(#)mount.c	8.19 (Berkeley) 4/19/94";
46 #else
47 static char rcsid[] = "$OpenBSD: mount.c,v 1.23 2001/12/16 22:45:37 miod Exp $";
48 #endif
49 #endif /* not lint */
50 
51 #include <sys/param.h>
52 #include <sys/mount.h>
53 #include <sys/socket.h>
54 #include <sys/wait.h>
55 
56 #include <nfs/rpcv2.h>
57 #include <nfs/nfsproto.h>
58 #define _KERNEL
59 #include <nfs/nfs.h>
60 #undef _KERNEL
61 
62 #include <err.h>
63 #include <errno.h>
64 #include <fstab.h>
65 #include <netdb.h>
66 #include <signal.h>
67 #include <stdio.h>
68 #include <stdlib.h>
69 #include <string.h>
70 #include <unistd.h>
71 #include <util.h>
72 
73 #include "pathnames.h"
74 
75 int	debug, verbose;
76 char	**typelist = NULL;
77 
78 int	selected __P((const char *));
79 char   *catopt __P((char *, const char *));
80 struct statfs
81        *getmntpt __P((const char *));
82 int	hasopt __P((const char *, const char *));
83 void	maketypelist __P((char *));
84 void	mangle __P((char *, int *, const char **));
85 int	mountfs __P((const char *, const char *, const char *,
86 			int, const char *, const char *, int));
87 void	prmount __P((struct statfs *));
88 int	disklabelcheck __P((struct fstab *));
89 void	usage __P((void));
90 
91 /* Map from mount options to printable formats. */
92 static 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_NOATIME,		0,	"noaccesstime" },
106 	{ MNT_NODEV,		0,	"nodev" },
107 	{ MNT_NOEXEC,		0,	"noexec" },
108 	{ MNT_NOSUID,		0,	"nosuid" },
109 	{ MNT_QUOTA,		0,	"with quotas" },
110 	{ MNT_RDONLY,		0,	"read-only" },
111 	{ MNT_ROOTFS,		1,	"root file system" },
112 	{ MNT_SYNCHRONOUS,	0,	"synchronous" },
113 	{ MNT_SOFTDEP,		0,	"softdep" },
114 	{ MNT_UNION,		0,	"union" },
115 	{ NULL }
116 };
117 
118 int
119 main(argc, argv)
120 	int argc;
121 	char * const argv[];
122 {
123 	const char *mntonname, *vfstype;
124 	struct fstab *fs;
125 	struct statfs *mntbuf;
126 	FILE *mountdfp;
127 	pid_t pid;
128 	int all, ch, forceall, i, init_flags, mntsize, rval;
129 	char *options;
130 
131 	all = forceall = init_flags = 0;
132 	options = NULL;
133 	vfstype = "ffs";
134 	while ((ch = getopt(argc, argv, "Aadfo:rwt:uv")) != -1)
135 		switch (ch) {
136 		case 'A':
137 			all = forceall = 1;
138 			break;
139 		case 'a':
140 			all = 1;
141 			break;
142 		case 'd':
143 			debug = 1;
144 			break;
145 		case 'f':
146 			init_flags |= MNT_FORCE;
147 			break;
148 		case 'o':
149 			if (*optarg)
150 				options = catopt(options, optarg);
151 			break;
152 		case 'r':
153 			init_flags |= MNT_RDONLY;
154 			break;
155 		case 't':
156 			if (typelist != NULL)
157 				errx(1, "only one -t option may be specified.");
158 			maketypelist(optarg);
159 			vfstype = optarg;
160 			break;
161 		case 'u':
162 			init_flags |= MNT_UPDATE;
163 			break;
164 		case 'v':
165 			verbose = 1;
166 			break;
167 		case 'w':
168 			init_flags &= ~MNT_RDONLY;
169 			break;
170 		case '?':
171 		default:
172 			usage();
173 			/* NOTREACHED */
174 		}
175 	argc -= optind;
176 	argv += optind;
177 
178 #define	BADTYPE(type)							\
179 	(strcmp(type, FSTAB_RO) &&					\
180 	    strcmp(type, FSTAB_RW) && strcmp(type, FSTAB_RQ))
181 
182 	rval = 0;
183 	switch (argc) {
184 	case 0:
185 		if (all)
186 			while ((fs = getfsent()) != NULL) {
187 				if (BADTYPE(fs->fs_type))
188 					continue;
189 				if (!selected(fs->fs_vfstype))
190 					continue;
191 				if (hasopt(fs->fs_mntops, "noauto"))
192 					continue;
193 				if (disklabelcheck(fs))
194 					continue;
195 				if (mountfs(fs->fs_vfstype, fs->fs_spec,
196 				    fs->fs_file, init_flags, options,
197 				    fs->fs_mntops, !forceall))
198 					rval = 1;
199 			}
200 		else {
201 			if ((mntsize = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0)
202 				err(1, "getmntinfo");
203 			for (i = 0; i < mntsize; i++) {
204 				if (!selected(mntbuf[i].f_fstypename))
205 					continue;
206 				prmount(&mntbuf[i]);
207 			}
208 		}
209 		exit(rval);
210 	case 1:
211 		if (typelist != NULL)
212 			usage();
213 
214 		if (init_flags & MNT_UPDATE) {
215 			if ((mntbuf = getmntpt(*argv)) == NULL)
216 				errx(1,
217 				    "unknown special file or file system %s.",
218 				    *argv);
219 			if ((fs = getfsfile(mntbuf->f_mntonname)) == NULL)
220 				errx(1, "can't find fstab entry for %s.",
221 				    *argv);
222 			/* If it's an update, ignore the fstab file options. */
223 			fs->fs_mntops = NULL;
224 			mntonname = mntbuf->f_mntonname;
225 		} else {
226 			if ((fs = getfsfile(*argv)) == NULL &&
227 			    (fs = getfsspec(*argv)) == NULL)
228 				errx(1,
229 				    "%s: unknown special file or file system.",
230 				    *argv);
231 			if (BADTYPE(fs->fs_type))
232 				errx(1, "%s has unknown file system type.",
233 				    *argv);
234 			mntonname = fs->fs_file;
235 		}
236 		rval = mountfs(fs->fs_vfstype, fs->fs_spec,
237 		    mntonname, init_flags, options, fs->fs_mntops, 0);
238 		break;
239 	case 2:
240 		/*
241 		 * If -t flag has not been specified, and spec contains either
242 		 * a ':' or a '@' then assume that an NFS filesystem is being
243 		 * specified ala Sun.  If not, check the disklabel for a
244 		 * known filesystem type.
245 		 */
246 		if (typelist == NULL) {
247 			if (strpbrk(argv[0], ":@") != NULL)
248 				vfstype = "nfs";
249 			else {
250 				char *labelfs = readlabelfs(argv[0], 0);
251 				if (labelfs != NULL)
252 					vfstype = labelfs;
253 			}
254 		}
255 
256 		rval = mountfs(vfstype,
257 		    argv[0], argv[1], init_flags, options, NULL, 0);
258 		break;
259 	default:
260 		usage();
261 		/* NOTREACHED */
262 	}
263 
264 	/*
265 	 * If the mount was successfully, and done by root, tell mountd the
266 	 * good news.  Pid checks are probably unnecessary, but don't hurt.
267 	 */
268 	if (rval == 0 && getuid() == 0 &&
269 	    (mountdfp = fopen(_PATH_MOUNTDPID, "r")) != NULL) {
270 		if (fscanf(mountdfp, "%d", &pid) == 1 &&
271 		     pid > 0 && kill(pid, SIGHUP) == -1 && errno != ESRCH)
272 			err(1, "signal mountd");
273 		(void)fclose(mountdfp);
274 	}
275 
276 	exit(rval);
277 }
278 
279 int
280 hasopt(mntopts, option)
281 	const char *mntopts, *option;
282 {
283 	int negative, found;
284 	char *opt, *optbuf;
285 
286 	if (option[0] == 'n' && option[1] == 'o') {
287 		negative = 1;
288 		option += 2;
289 	} else
290 		negative = 0;
291 	optbuf = strdup(mntopts);
292 	found = 0;
293 	for (opt = optbuf; (opt = strtok(opt, ",")) != NULL; opt = NULL) {
294 		if (opt[0] == 'n' && opt[1] == 'o') {
295 			if (!strcasecmp(opt + 2, option))
296 				found = negative;
297 		} else if (!strcasecmp(opt, option))
298 			found = !negative;
299 	}
300 	free(optbuf);
301 	return (found);
302 }
303 
304 int
305 mountfs(vfstype, spec, name, flags, options, mntopts, skipmounted)
306 	const char *vfstype, *spec, *name, *options, *mntopts;
307 	int flags, skipmounted;
308 {
309 	/* List of directories containing mount_xxx subcommands. */
310 	static const char *edirs[] = {
311 		_PATH_SBIN,
312 		_PATH_USRSBIN,
313 		NULL
314 	};
315 	const char *argv[100], **edir;
316 	struct statfs sf;
317 	pid_t pid;
318 	int argc, i, status;
319 	char *optbuf, execname[MAXPATHLEN], mntpath[MAXPATHLEN];
320 	char mountname[MAXPATHLEN];
321 
322 	if (realpath(name, mntpath) == NULL) {
323 		warn("realpath %s", name);
324 		return (1);
325 	}
326 
327 	name = mntpath;
328 
329 	if (mntopts == NULL)
330 		mntopts = "";
331 	if (options == NULL) {
332 		if (*mntopts == '\0')
333 			options = "rw";
334 		else {
335 			options = mntopts;
336 			mntopts = "";
337 		}
338 	}
339 	optbuf = catopt(strdup(mntopts), options);
340 
341 	if (strcmp(name, "/") == 0)
342 		flags |= MNT_UPDATE;
343 	else if (skipmounted) {
344 		if (statfs(name, &sf) < 0) {
345 			warn("statfs %s", name);
346 			return (1);
347 		}
348 		/* XXX can't check f_mntfromname, thanks to mfs, union, etc. */
349 		if (strncmp(name, sf.f_mntonname, MNAMELEN) == 0 &&
350 		    strncmp(vfstype, sf.f_fstypename, MFSNAMELEN) == 0) {
351 			if (verbose)
352 				(void)printf("%s on %s type %.*s: %s\n",
353 				    sf.f_mntfromname, sf.f_mntonname,
354 			            MFSNAMELEN, sf.f_fstypename,
355 				    "already mounted");
356 			return (0);
357 		}
358 	}
359 	if (flags & MNT_FORCE)
360 		optbuf = catopt(optbuf, "force");
361 	if (flags & MNT_RDONLY)
362 		optbuf = catopt(optbuf, "ro");
363 	/*
364 	 * XXX
365 	 * The mount_mfs (newfs) command uses -o to select the
366 	 * optimisation mode.  We don't pass the default "-o rw"
367 	 * for that reason.
368 	 */
369 	if (flags & MNT_UPDATE)
370 		optbuf = catopt(optbuf, "update");
371 
372 	(void)snprintf(mountname,
373 	    sizeof(mountname), "mount_%s", vfstype);
374 
375 	argc = 0;
376 	argv[argc++] = mountname;
377 	mangle(optbuf, &argc, argv);
378 	argv[argc++] = spec;
379 	argv[argc++] = name;
380 	argv[argc] = NULL;
381 
382 	if (debug) {
383 		(void)printf("exec: mount_%s", vfstype);
384 		for (i = 1; i < argc; i++)
385 			(void)printf(" %s", argv[i]);
386 		(void)printf("\n");
387 		return (0);
388 	}
389 
390 	switch ((pid = fork())) {
391 	case -1:				/* Error. */
392 		warn("fork");
393 		free(optbuf);
394 		return (1);
395 	case 0:					/* Child. */
396 		/* Go find an executable. */
397 		edir = edirs;
398 		do {
399 			(void)snprintf(execname,
400 			    sizeof(execname), "%s/mount_%s", *edir, vfstype);
401 			execv(execname, (char * const *)argv);
402 			if (errno != ENOENT)
403 				warn("exec %s for %s", execname, name);
404 		} while (*++edir != NULL);
405 
406 		if (errno == ENOENT)
407 			warn("no mount helper program found for %s", vfstype);
408 		exit(1);
409 		/* NOTREACHED */
410 	default:				/* Parent. */
411 		free(optbuf);
412 
413 		if (waitpid(pid, &status, 0) < 0) {
414 			warn("waitpid");
415 			return (1);
416 		}
417 
418 		if (WIFEXITED(status)) {
419 			if (WEXITSTATUS(status) != 0)
420 				return (WEXITSTATUS(status));
421 		} else if (WIFSIGNALED(status)) {
422 			warnx("%s: %s", name, strsignal(WTERMSIG(status)));
423 			return (1);
424 		}
425 
426 		if (verbose) {
427 			if (statfs(name, &sf) < 0) {
428 				warn("statfs %s", name);
429 				return (1);
430 			}
431 			prmount(&sf);
432 		}
433 		break;
434 	}
435 
436 	return (0);
437 }
438 
439 void
440 prmount(sf)
441 	struct statfs *sf;
442 {
443 	int flags;
444 	struct opt *o;
445 	int f = 0;
446 
447 	(void)printf("%s on %s type %.*s", sf->f_mntfromname, sf->f_mntonname,
448 	    MFSNAMELEN, sf->f_fstypename);
449 
450 	flags = sf->f_flags & MNT_VISFLAGMASK;
451 	if (verbose && !(flags & MNT_RDONLY))
452 		(void)printf("%s%s", !f++ ? " (" : ", ", "rw");
453 	for (o = optnames; flags && o->o_opt; o++)
454 		if (flags & o->o_opt) {
455 			if (!o->o_silent)
456 				(void)printf("%s%s", !f++ ? " (" : ", ",
457 				    o->o_name);
458 			flags &= ~o->o_opt;
459 		}
460 	if (flags)
461 		(void)printf("%sunknown flag%s %#x", !f++ ? " (" : ", ",
462 		    flags & (flags - 1) ? "s" : "", flags);
463 
464 	/*
465 	 * Filesystem-specific options
466 	 * We only print the "interesting" values unless in verboser
467 	 * mode in order to keep the signal/noise ratio high.
468 	 */
469 	if (strcmp(sf->f_fstypename, MOUNT_NFS) == 0) {
470 		struct protoent *pr;
471 		struct nfs_args *nfs_args = &sf->mount_info.nfs_args;
472 
473 		(void)printf("%s%s", !f++ ? " (" : ", ",
474 		    (nfs_args->flags & NFSMNT_NFSV3) ? "v3" : "v2");
475 		if (nfs_args->proto && (pr = getprotobynumber(nfs_args->proto)))
476 			(void)printf("%s%s", !f++ ? " (" : ", ", pr->p_name);
477 		else
478 			(void)printf("%s%s", !f++ ? " (" : ", ",
479 			    (nfs_args->sotype == SOCK_DGRAM) ? "udp" : "tcp");
480 		if (nfs_args->flags & NFSMNT_SOFT)
481 			(void)printf("%s%s", !f++ ? " (" : ", ", "soft");
482 		else if (verbose)
483 			(void)printf("%s%s", !f++ ? " (" : ", ", "hard");
484 		if (nfs_args->flags & NFSMNT_INT)
485 			(void)printf("%s%s", !f++ ? " (" : ", ", "intr");
486 		if (nfs_args->flags & NFSMNT_NOCONN)
487 			(void)printf("%s%s", !f++ ? " (" : ", ", "noconn");
488 		if (verbose || nfs_args->wsize != NFS_WSIZE)
489 			(void)printf("%s%s=%d", !f++ ? " (" : ", ",
490 			    "wsize", nfs_args->wsize);
491 		if (verbose || nfs_args->rsize != NFS_RSIZE)
492 			(void)printf("%s%s=%d", !f++ ? " (" : ", ",
493 			    "rsize", nfs_args->rsize);
494 		if (verbose || nfs_args->readdirsize != NFS_READDIRSIZE)
495 			(void)printf("%s%s=%d", !f++ ? " (" : ", ",
496 			    "rdirsize", nfs_args->readdirsize);
497 		if (verbose || nfs_args->timeo != 10) /* XXX */
498 			(void)printf("%s%s=%d", !f++ ? " (" : ", ",
499 			    "timeo", nfs_args->timeo);
500 		if (verbose || nfs_args->retrans != NFS_RETRANS)
501 			(void)printf("%s%s=%d", !f++ ? " (" : ", ",
502 			    "retrans", nfs_args->retrans);
503 		if (verbose || nfs_args->maxgrouplist != NFS_MAXGRPS)
504 			(void)printf("%s%s=%d", !f++ ? " (" : ", ",
505 			    "maxgrouplist", nfs_args->maxgrouplist);
506 		if (verbose || nfs_args->readahead != NFS_DEFRAHEAD)
507 			(void)printf("%s%s=%d", !f++ ? " (" : ", ",
508 			    "readahead", nfs_args->readahead);
509 	} else if (strcmp(sf->f_fstypename, MOUNT_MFS) == 0) {
510 		int headerlen;
511 		long blocksize;
512 		char *header;
513 
514 		header = getbsize(&headerlen, &blocksize);
515 		(void)printf("%s%s=%lu %s", !f++ ? " (" : ", ",
516 		    "size", sf->mount_info.mfs_args.size / blocksize, header);
517 	} else if (strcmp(sf->f_fstypename, MOUNT_ADOSFS) == 0) {
518 		struct adosfs_args *adosfs_args = &sf->mount_info.adosfs_args;
519 
520 		if (verbose || adosfs_args->uid || adosfs_args->gid)
521 			(void)printf("%s%s=%u, %s=%u", !f++ ? " (" : ", ",
522 			    "uid", adosfs_args->uid, "gid", adosfs_args->gid);
523 		if (verbose || adosfs_args->mask != 0755)
524 			(void)printf("%s%s=0%o", !f++ ? " (" : ", ",
525 			    "mask", adosfs_args->mask);
526 	} else if (strcmp(sf->f_fstypename, MOUNT_MSDOS) == 0) {
527 		struct msdosfs_args *msdosfs_args = &sf->mount_info.msdosfs_args;
528 
529 		if (verbose || msdosfs_args->uid || msdosfs_args->gid)
530 			(void)printf("%s%s=%u, %s=%u", !f++ ? " (" : ", ",
531 			    "uid", msdosfs_args->uid, "gid", msdosfs_args->gid);
532 		if (verbose || msdosfs_args->mask != 0755)
533 			(void)printf("%s%s=0%o", !f++ ? " (" : ", ",
534 			    "mask", msdosfs_args->mask);
535 		if (msdosfs_args->flags & MSDOSFSMNT_SHORTNAME)
536 			(void)printf("%s%s", !f++ ? " (" : ", ", "short");
537 		if (msdosfs_args->flags & MSDOSFSMNT_LONGNAME)
538 			(void)printf("%s%s", !f++ ? " (" : ", ", "long");
539 		if (msdosfs_args->flags & MSDOSFSMNT_NOWIN95)
540 			(void)printf("%s%s", !f++ ? " (" : ", ", "nowin95");
541 		if (msdosfs_args->flags & MSDOSFSMNT_GEMDOSFS)
542 			(void)printf("%s%s", !f++ ? " (" : ", ", "gem");
543 	} else if (strcmp(sf->f_fstypename, MOUNT_CD9660) == 0) {
544 		struct iso_args *iso_args = &sf->mount_info.iso_args;
545 
546 		if (iso_args->flags & ISOFSMNT_NORRIP)
547 			(void)printf("%s%s", !f++ ? " (" : ", ", "norrip");
548 		if (iso_args->flags & ISOFSMNT_GENS)
549 			(void)printf("%s%s", !f++ ? " (" : ", ", "gens");
550 		if (iso_args->flags & ISOFSMNT_EXTATT)
551 			(void)printf("%s%s", !f++ ? " (" : ", ", "extatt");
552 	}
553 	(void)printf(f ? ")\n" : "\n");
554 }
555 
556 struct statfs *
557 getmntpt(name)
558 	const char *name;
559 {
560 	struct statfs *mntbuf;
561 	int i, mntsize;
562 
563 	mntsize = getmntinfo(&mntbuf, MNT_NOWAIT);
564 	for (i = 0; i < mntsize; i++)
565 		if (strcmp(mntbuf[i].f_mntfromname, name) == 0 ||
566 		    strcmp(mntbuf[i].f_mntonname, name) == 0)
567 			return (&mntbuf[i]);
568 	return (NULL);
569 }
570 
571 static enum { IN_LIST, NOT_IN_LIST } which;
572 
573 int
574 selected(type)
575 	const char *type;
576 {
577 	char **av;
578 
579 	/* If no type specified, it's always selected. */
580 	if (typelist == NULL)
581 		return (1);
582 	for (av = typelist; *av != NULL; ++av)
583 		if (!strncmp(type, *av, MFSNAMELEN))
584 			return (which == IN_LIST ? 1 : 0);
585 	return (which == IN_LIST ? 0 : 1);
586 }
587 
588 void
589 maketypelist(fslist)
590 	char *fslist;
591 {
592 	int i;
593 	char *nextcp, **av;
594 
595 	if ((fslist == NULL) || (fslist[0] == '\0'))
596 		errx(1, "empty type list");
597 
598 	/*
599 	 * XXX
600 	 * Note: the syntax is "noxxx,yyy" for no xxx's and
601 	 * no yyy's, not the more intuitive "noyyy,noyyy".
602 	 */
603 	if (fslist[0] == 'n' && fslist[1] == 'o') {
604 		fslist += 2;
605 		which = NOT_IN_LIST;
606 	} else
607 		which = IN_LIST;
608 
609 	/* Count the number of types. */
610 	for (i = 1, nextcp = fslist; (nextcp = strchr(nextcp, ',')); i++)
611 		++nextcp;
612 
613 	/* Build an array of that many types. */
614 	if ((av = typelist = malloc((i + 1) * sizeof(char *))) == NULL)
615 		err(1, NULL);
616 	av[0] = fslist;
617 	for (i = 1, nextcp = fslist; (nextcp = strchr(nextcp, ',')); i++) {
618 		*nextcp = '\0';
619 		av[i] = ++nextcp;
620 	}
621 	/* Terminate the array. */
622 	av[i] = NULL;
623 }
624 
625 char *
626 catopt(s0, s1)
627 	char *s0;
628 	const char *s1;
629 {
630 	size_t i;
631 	char *cp;
632 
633 	if (s0 && *s0) {
634 		i = strlen(s0) + strlen(s1) + 1 + 1;
635 		if ((cp = malloc(i)) == NULL)
636 			err(1, NULL);
637 		(void)snprintf(cp, i, "%s,%s", s0, s1);
638 	} else
639 		cp = strdup(s1);
640 
641 	if (s0)
642 		free(s0);
643 	return (cp);
644 }
645 
646 void
647 mangle(options, argcp, argv)
648 	char *options;
649 	int *argcp;
650 	const char **argv;
651 {
652 	char *p, *s;
653 	int argc;
654 
655 	argc = *argcp;
656 	for (s = options; (p = strsep(&s, ",")) != NULL;)
657 		if (*p != '\0') {
658 			if (*p == '-') {
659 				argv[argc++] = p;
660 				p = strchr(p, '=');
661 				if (p) {
662 					*p = '\0';
663 					argv[argc++] = p+1;
664 				}
665 			} else if (strcmp(p, "rw") != 0) {
666 				argv[argc++] = "-o";
667 				argv[argc++] = p;
668 			}
669 		}
670 
671 	*argcp = argc;
672 }
673 
674 void
675 usage()
676 {
677 
678 	(void)fprintf(stderr,
679 		"usage: mount %s %s\n       mount %s\n       mount %s\n",
680 		"[-dfruvw] [-o options] [-t ffs | external_type]",
681 			"special node",
682 		"[-adfruvw] [-t ffs | external_type]",
683 		"[-dfruvw] special | node");
684 	exit(1);
685 }
686 
687 int
688 disklabelcheck(fs)
689 	struct fstab *fs;
690 {
691 	char *labelfs;
692 
693 	if (strcmp(fs->fs_vfstype, "nfs") != 0 ||
694 	    strpbrk(fs->fs_spec, ":@") == NULL) {
695 		labelfs = readlabelfs(fs->fs_spec, 0);
696 		if (labelfs == NULL ||
697 		    strcmp(labelfs, fs->fs_vfstype) == 0)
698 			return (0);
699 		if (strcmp(fs->fs_vfstype, "ufs") == 0 &&
700 		    strcmp(labelfs, "ffs") == 0) {
701 			warnx("%s: fstab uses outdated type 'ufs' -- fix please",
702 			    fs->fs_spec);
703 			return (0);
704 		}
705 		warnx("%s: fstab type %s != disklabel type %s",
706 		    fs->fs_spec, fs->fs_vfstype, labelfs);
707 		return (1);
708 	}
709 	return (0);
710 }
711 
712