1/*-
2 * SPDX-License-Identifier: BSD-3-Clause
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#ifndef lint
33static const char copyright[] =
34"@(#) Copyright (c) 1980, 1989, 1993, 1994\n\
35	The Regents of the University of California.  All rights reserved.\n";
36#if 0
37static char sccsid[] = "@(#)mount.c	8.25 (Berkeley) 5/8/95";
38#endif
39#endif /* not lint */
40
41#include <sys/cdefs.h>
42__FBSDID("$FreeBSD$");
43
44#include <sys/param.h>
45#define _WANT_MNTOPTNAMES
46#include <sys/mount.h>
47#include <sys/stat.h>
48#include <sys/wait.h>
49
50#include <ctype.h>
51#include <err.h>
52#include <errno.h>
53#include <fstab.h>
54#include <paths.h>
55#include <pwd.h>
56#include <signal.h>
57#include <stdint.h>
58#include <stdio.h>
59#include <stdlib.h>
60#include <string.h>
61#include <unistd.h>
62#include <libutil.h>
63
64#include "extern.h"
65#include "mntopts.h"
66#include "pathnames.h"
67
68/* `meta' options */
69#define MOUNT_META_OPTION_FSTAB		"fstab"
70#define MOUNT_META_OPTION_CURRENT	"current"
71
72static int debug, fstab_style, verbose;
73
74struct cpa {
75	char	**a;
76	ssize_t	sz;
77	int	c;
78};
79
80char   *catopt(char *, const char *);
81struct statfs *getmntpt(const char *);
82int	hasopt(const char *, const char *);
83int	ismounted(struct fstab *, struct statfs *, int);
84int	isremountable(const char *);
85void	mangle(char *, struct cpa *);
86char   *update_options(char *, char *, int);
87int	mountfs(const char *, const char *, const char *,
88			int, const char *, const char *);
89void	remopt(char *, const char *);
90void	prmount(struct statfs *);
91void	putfsent(struct statfs *);
92void	usage(void);
93char   *flags2opts(int);
94
95/* Map from mount options to printable formats. */
96static struct mntoptnames optnames[] = {
97	MNTOPT_NAMES
98};
99
100/*
101 * List of VFS types that can be remounted without becoming mounted on top
102 * of each other.
103 * XXX Is this list correct?
104 */
105static const char *
106remountable_fs_names[] = {
107	"ufs", "ffs", "ext2fs",
108	0
109};
110
111static const char userquotaeq[] = "userquota=";
112static const char groupquotaeq[] = "groupquota=";
113
114static char *mountprog = NULL;
115
116static int
117use_mountprog(const char *vfstype)
118{
119	/* XXX: We need to get away from implementing external mount
120	 *      programs for every filesystem, and move towards having
121	 *	each filesystem properly implement the nmount() system call.
122	 */
123	unsigned int i;
124	const char *fs[] = {
125	"cd9660", "mfs", "msdosfs", "nfs",
126	"nullfs", "smbfs", "udf", "unionfs",
127	NULL
128	};
129
130	if (mountprog != NULL)
131		return (1);
132
133	for (i = 0; fs[i] != NULL; ++i) {
134		if (strcmp(vfstype, fs[i]) == 0)
135			return (1);
136	}
137
138	return (0);
139}
140
141static int
142exec_mountprog(const char *name, const char *execname, char *const argv[])
143{
144	pid_t pid;
145	int status;
146
147	switch (pid = fork()) {
148	case -1:				/* Error. */
149		warn("fork");
150		exit (1);
151	case 0:					/* Child. */
152		/* Go find an executable. */
153		execvP(execname, _PATH_SYSPATH, argv);
154		if (errno == ENOENT) {
155			warn("exec %s not found", execname);
156			if (execname[0] != '/') {
157				warnx("in path: %s", _PATH_SYSPATH);
158			}
159		}
160		exit(1);
161	default:				/* Parent. */
162		if (waitpid(pid, &status, 0) < 0) {
163			warn("waitpid");
164			return (1);
165		}
166
167		if (WIFEXITED(status)) {
168			if (WEXITSTATUS(status) != 0)
169				return (WEXITSTATUS(status));
170		} else if (WIFSIGNALED(status)) {
171			warnx("%s: %s", name, sys_siglist[WTERMSIG(status)]);
172			return (1);
173		}
174		break;
175	}
176
177	return (0);
178}
179
180static int
181specified_ro(const char *arg)
182{
183	char *optbuf, *opt;
184	int ret = 0;
185
186	optbuf = strdup(arg);
187	if (optbuf == NULL)
188		 err(1, NULL);
189
190	for (opt = optbuf; (opt = strtok(opt, ",")) != NULL; opt = NULL) {
191		if (strcmp(opt, "ro") == 0) {
192			ret = 1;
193			break;
194		}
195	}
196	free(optbuf);
197	return (ret);
198}
199
200static void
201restart_mountd(void)
202{
203	struct pidfh *pfh;
204	pid_t mountdpid;
205
206	mountdpid = 0;
207	pfh = pidfile_open(_PATH_MOUNTDPID, 0600, &mountdpid);
208	if (pfh != NULL) {
209		/* Mountd is not running. */
210		pidfile_remove(pfh);
211		return;
212	}
213	if (errno != EEXIST) {
214		/* Cannot open pidfile for some reason. */
215		return;
216	}
217
218	/*
219	 * Refuse to send broadcast or group signals, this has
220	 * happened due to the bugs in pidfile(3).
221	 */
222	if (mountdpid <= 0) {
223		warnx("mountd pid %d, refusing to send SIGHUP", mountdpid);
224		return;
225	}
226
227	/* We have mountd(8) PID in mountdpid varible, let's signal it. */
228	if (kill(mountdpid, SIGHUP) == -1)
229		err(1, "signal mountd");
230}
231
232int
233main(int argc, char *argv[])
234{
235	const char *mntfromname, **vfslist, *vfstype;
236	struct fstab *fs;
237	struct statfs *mntbuf;
238	int all, ch, i, init_flags, late, failok, mntsize, rval, have_fstab, ro;
239	int onlylate;
240	char *cp, *ep, *options;
241
242	all = init_flags = late = onlylate = 0;
243	ro = 0;
244	options = NULL;
245	vfslist = NULL;
246	vfstype = "ufs";
247	while ((ch = getopt(argc, argv, "adF:fLlno:prt:uvw")) != -1)
248		switch (ch) {
249		case 'a':
250			all = 1;
251			break;
252		case 'd':
253			debug = 1;
254			break;
255		case 'F':
256			setfstab(optarg);
257			break;
258		case 'f':
259			init_flags |= MNT_FORCE;
260			break;
261		case 'L':
262			onlylate = 1;
263			late = 1;
264			break;
265		case 'l':
266			late = 1;
267			break;
268		case 'n':
269			/* For compatibility with the Linux version of mount. */
270			break;
271		case 'o':
272			if (*optarg) {
273				options = catopt(options, optarg);
274				if (specified_ro(optarg))
275					ro = 1;
276			}
277			break;
278		case 'p':
279			fstab_style = 1;
280			verbose = 1;
281			break;
282		case 'r':
283			options = catopt(options, "ro");
284			ro = 1;
285			break;
286		case 't':
287			if (vfslist != NULL)
288				errx(1, "only one -t option may be specified");
289			vfslist = makevfslist(optarg);
290			vfstype = optarg;
291			break;
292		case 'u':
293			init_flags |= MNT_UPDATE;
294			break;
295		case 'v':
296			verbose = 1;
297			break;
298		case 'w':
299			options = catopt(options, "noro");
300			break;
301		case '?':
302		default:
303			usage();
304			/* NOTREACHED */
305		}
306	argc -= optind;
307	argv += optind;
308
309#define	BADTYPE(type)							\
310	(strcmp(type, FSTAB_RO) &&					\
311	    strcmp(type, FSTAB_RW) && strcmp(type, FSTAB_RQ))
312
313	if ((init_flags & MNT_UPDATE) && (ro == 0))
314		options = catopt(options, "noro");
315
316	rval = 0;
317	switch (argc) {
318	case 0:
319		if ((mntsize = getmntinfo(&mntbuf,
320		     verbose ? MNT_WAIT : MNT_NOWAIT)) == 0)
321			err(1, "getmntinfo");
322		if (all) {
323			while ((fs = getfsent()) != NULL) {
324				if (BADTYPE(fs->fs_type))
325					continue;
326				if (checkvfsname(fs->fs_vfstype, vfslist))
327					continue;
328				if (hasopt(fs->fs_mntops, "noauto"))
329					continue;
330				if (!hasopt(fs->fs_mntops, "late") && onlylate)
331					continue;
332				if (hasopt(fs->fs_mntops, "late") && !late)
333					continue;
334				if (hasopt(fs->fs_mntops, "failok"))
335					failok = 1;
336				else
337					failok = 0;
338				if (!(init_flags & MNT_UPDATE) &&
339				    !hasopt(fs->fs_mntops, "update") &&
340				    ismounted(fs, mntbuf, mntsize))
341					continue;
342				options = update_options(options, fs->fs_mntops,
343				    mntbuf->f_flags);
344				if (mountfs(fs->fs_vfstype, fs->fs_spec,
345				    fs->fs_file, init_flags, options,
346				    fs->fs_mntops) && !failok)
347					rval = 1;
348			}
349		} else if (fstab_style) {
350			for (i = 0; i < mntsize; i++) {
351				if (checkvfsname(mntbuf[i].f_fstypename, vfslist))
352					continue;
353				putfsent(&mntbuf[i]);
354			}
355		} else {
356			for (i = 0; i < mntsize; i++) {
357				if (checkvfsname(mntbuf[i].f_fstypename,
358				    vfslist))
359					continue;
360				if (!verbose &&
361				    (mntbuf[i].f_flags & MNT_IGNORE) != 0)
362					continue;
363				prmount(&mntbuf[i]);
364			}
365		}
366		exit(rval);
367	case 1:
368		if (vfslist != NULL)
369			usage();
370
371		rmslashes(*argv, *argv);
372		if (init_flags & MNT_UPDATE) {
373			mntfromname = NULL;
374			have_fstab = 0;
375			if ((mntbuf = getmntpt(*argv)) == NULL)
376				errx(1, "not currently mounted %s", *argv);
377			/*
378			 * Only get the mntflags from fstab if both mntpoint
379			 * and mntspec are identical. Also handle the special
380			 * case where just '/' is mounted and 'spec' is not
381			 * identical with the one from fstab ('/dev' is missing
382			 * in the spec-string at boot-time).
383			 */
384			if ((fs = getfsfile(mntbuf->f_mntonname)) != NULL) {
385				if (strcmp(fs->fs_spec,
386				    mntbuf->f_mntfromname) == 0 &&
387				    strcmp(fs->fs_file,
388				    mntbuf->f_mntonname) == 0) {
389					have_fstab = 1;
390					mntfromname = mntbuf->f_mntfromname;
391				} else if (argv[0][0] == '/' &&
392				    argv[0][1] == '\0' &&
393				    strcmp(fs->fs_vfstype,
394				    mntbuf->f_fstypename) == 0) {
395					fs = getfsfile("/");
396					have_fstab = 1;
397					mntfromname = fs->fs_spec;
398				}
399			}
400			if (have_fstab) {
401				options = update_options(options, fs->fs_mntops,
402				    mntbuf->f_flags);
403			} else {
404				mntfromname = mntbuf->f_mntfromname;
405				options = update_options(options, NULL,
406				    mntbuf->f_flags);
407			}
408			rval = mountfs(mntbuf->f_fstypename, mntfromname,
409			    mntbuf->f_mntonname, init_flags, options, 0);
410			break;
411		}
412		if ((fs = getfsfile(*argv)) == NULL &&
413		    (fs = getfsspec(*argv)) == NULL)
414			errx(1, "%s: unknown special file or file system",
415			    *argv);
416		if (BADTYPE(fs->fs_type))
417			errx(1, "%s has unknown file system type",
418			    *argv);
419		rval = mountfs(fs->fs_vfstype, fs->fs_spec, fs->fs_file,
420		    init_flags, options, fs->fs_mntops);
421		break;
422	case 2:
423		/*
424		 * If -t flag has not been specified, the path cannot be
425		 * found, spec contains either a ':' or a '@', then assume
426		 * that an NFS file system is being specified ala Sun.
427		 * Check if the hostname contains only allowed characters
428		 * to reduce false positives.  IPv6 addresses containing
429		 * ':' will be correctly parsed only if the separator is '@'.
430		 * The definition of a valid hostname is taken from RFC 1034.
431		 */
432		if (vfslist == NULL && ((ep = strchr(argv[0], '@')) != NULL ||
433		    (ep = strchr(argv[0], ':')) != NULL)) {
434			if (*ep == '@') {
435				cp = ep + 1;
436				ep = cp + strlen(cp);
437			} else
438				cp = argv[0];
439			while (cp != ep) {
440				if (!isdigit(*cp) && !isalpha(*cp) &&
441				    *cp != '.' && *cp != '-' && *cp != ':')
442					break;
443				cp++;
444			}
445			if (cp == ep)
446				vfstype = "nfs";
447		}
448		rval = mountfs(vfstype,
449		    argv[0], argv[1], init_flags, options, NULL);
450		break;
451	default:
452		usage();
453		/* NOTREACHED */
454	}
455
456	/*
457	 * If the mount was successfully, and done by root, tell mountd the
458	 * good news.
459	 */
460	if (rval == 0 && getuid() == 0)
461		restart_mountd();
462
463	exit(rval);
464}
465
466int
467ismounted(struct fstab *fs, struct statfs *mntbuf, int mntsize)
468{
469	char realfsfile[PATH_MAX];
470	int i;
471
472	if (fs->fs_file[0] == '/' && fs->fs_file[1] == '\0')
473		/* the root file system can always be remounted */
474		return (0);
475
476	/* The user may have specified a symlink in fstab, resolve the path */
477	if (realpath(fs->fs_file, realfsfile) == NULL) {
478		/* Cannot resolve the path, use original one */
479		strlcpy(realfsfile, fs->fs_file, sizeof(realfsfile));
480	}
481
482	/*
483	 * Consider the filesystem to be mounted if:
484	 * It has the same mountpoint as a mounted filesytem, and
485	 * It has the same type as that same mounted filesystem, and
486	 * It has the same device name as that same mounted filesystem, OR
487	 *     It is a nonremountable filesystem
488	 */
489	for (i = mntsize - 1; i >= 0; --i)
490		if (strcmp(realfsfile, mntbuf[i].f_mntonname) == 0 &&
491		    strcmp(fs->fs_vfstype, mntbuf[i].f_fstypename) == 0 &&
492		    (!isremountable(fs->fs_vfstype) ||
493		     (strcmp(fs->fs_spec, mntbuf[i].f_mntfromname) == 0)))
494			return (1);
495	return (0);
496}
497
498int
499isremountable(const char *vfsname)
500{
501	const char **cp;
502
503	for (cp = remountable_fs_names; *cp; cp++)
504		if (strcmp(*cp, vfsname) == 0)
505			return (1);
506	return (0);
507}
508
509int
510hasopt(const char *mntopts, const char *option)
511{
512	int negative, found;
513	char *opt, *optbuf;
514
515	if (option[0] == 'n' && option[1] == 'o') {
516		negative = 1;
517		option += 2;
518	} else
519		negative = 0;
520	optbuf = strdup(mntopts);
521	found = 0;
522	for (opt = optbuf; (opt = strtok(opt, ",")) != NULL; opt = NULL) {
523		if (opt[0] == 'n' && opt[1] == 'o') {
524			if (!strcasecmp(opt + 2, option))
525				found = negative;
526		} else if (!strcasecmp(opt, option))
527			found = !negative;
528	}
529	free(optbuf);
530	return (found);
531}
532
533static void
534append_arg(struct cpa *sa, char *arg)
535{
536	if (sa->c + 1 == sa->sz) {
537		sa->sz = sa->sz == 0 ? 8 : sa->sz * 2;
538		sa->a = realloc(sa->a, sizeof(*sa->a) * sa->sz);
539		if (sa->a == NULL)
540			errx(1, "realloc failed");
541	}
542	sa->a[++sa->c] = arg;
543}
544
545int
546mountfs(const char *vfstype, const char *spec, const char *name, int flags,
547	const char *options, const char *mntopts)
548{
549	struct statfs sf;
550	int i, ret;
551	char *optbuf, execname[PATH_MAX], mntpath[PATH_MAX];
552	static struct cpa mnt_argv;
553
554	/* resolve the mountpoint with realpath(3) */
555	if (checkpath(name, mntpath) != 0) {
556		warn("%s", mntpath);
557		return (1);
558	}
559	name = mntpath;
560
561	if (mntopts == NULL)
562		mntopts = "";
563	optbuf = catopt(strdup(mntopts), options);
564
565	if (strcmp(name, "/") == 0)
566		flags |= MNT_UPDATE;
567	if (flags & MNT_FORCE)
568		optbuf = catopt(optbuf, "force");
569	if (flags & MNT_RDONLY)
570		optbuf = catopt(optbuf, "ro");
571	/*
572	 * XXX
573	 * The mount_mfs (newfs) command uses -o to select the
574	 * optimization mode.  We don't pass the default "-o rw"
575	 * for that reason.
576	 */
577	if (flags & MNT_UPDATE)
578		optbuf = catopt(optbuf, "update");
579
580	/* Compatibility glue. */
581	if (strcmp(vfstype, "msdos") == 0)
582		vfstype = "msdosfs";
583
584	/* Construct the name of the appropriate mount command */
585	(void)snprintf(execname, sizeof(execname), "mount_%s", vfstype);
586
587	mnt_argv.c = -1;
588	append_arg(&mnt_argv, execname);
589	mangle(optbuf, &mnt_argv);
590	if (mountprog != NULL)
591		strlcpy(execname, mountprog, sizeof(execname));
592
593	append_arg(&mnt_argv, strdup(spec));
594	append_arg(&mnt_argv, strdup(name));
595	append_arg(&mnt_argv, NULL);
596
597	if (debug) {
598		if (use_mountprog(vfstype))
599			printf("exec: %s", execname);
600		else
601			printf("mount -t %s", vfstype);
602		for (i = 1; i < mnt_argv.c; i++)
603			(void)printf(" %s", mnt_argv.a[i]);
604		(void)printf("\n");
605		free(optbuf);
606		free(mountprog);
607		mountprog = NULL;
608		return (0);
609	}
610
611	if (use_mountprog(vfstype)) {
612		ret = exec_mountprog(name, execname, mnt_argv.a);
613	} else {
614		ret = mount_fs(vfstype, mnt_argv.c, mnt_argv.a);
615	}
616
617	free(optbuf);
618	free(mountprog);
619	mountprog = NULL;
620
621	if (verbose) {
622		if (statfs(name, &sf) < 0) {
623			warn("statfs %s", name);
624			return (1);
625		}
626		if (fstab_style)
627			putfsent(&sf);
628		else
629			prmount(&sf);
630	}
631
632	return (ret);
633}
634
635void
636prmount(struct statfs *sfp)
637{
638	uint64_t flags;
639	unsigned int i;
640	struct mntoptnames *o;
641	struct passwd *pw;
642
643	(void)printf("%s on %s (%s", sfp->f_mntfromname, sfp->f_mntonname,
644	    sfp->f_fstypename);
645
646	flags = sfp->f_flags & MNT_VISFLAGMASK;
647	for (o = optnames; flags != 0 && o->o_opt != 0; o++)
648		if (flags & o->o_opt) {
649			(void)printf(", %s", o->o_name);
650			flags &= ~o->o_opt;
651		}
652	/*
653	 * Inform when file system is mounted by an unprivileged user
654	 * or privileged non-root user.
655	 */
656	if ((flags & MNT_USER) != 0 || sfp->f_owner != 0) {
657		(void)printf(", mounted by ");
658		if ((pw = getpwuid(sfp->f_owner)) != NULL)
659			(void)printf("%s", pw->pw_name);
660		else
661			(void)printf("%d", sfp->f_owner);
662	}
663	if (verbose) {
664		if (sfp->f_syncwrites != 0 || sfp->f_asyncwrites != 0)
665			(void)printf(", writes: sync %ju async %ju",
666			    (uintmax_t)sfp->f_syncwrites,
667			    (uintmax_t)sfp->f_asyncwrites);
668		if (sfp->f_syncreads != 0 || sfp->f_asyncreads != 0)
669			(void)printf(", reads: sync %ju async %ju",
670			    (uintmax_t)sfp->f_syncreads,
671			    (uintmax_t)sfp->f_asyncreads);
672		if (sfp->f_fsid.val[0] != 0 || sfp->f_fsid.val[1] != 0) {
673			(void)printf(", fsid ");
674			for (i = 0; i < sizeof(sfp->f_fsid); i++)
675				(void)printf("%02x", ((u_char *)&sfp->f_fsid)[i]);
676		}
677	}
678	(void)printf(")\n");
679}
680
681struct statfs *
682getmntpt(const char *name)
683{
684	struct statfs *mntbuf;
685	int i, mntsize;
686
687	mntsize = getmntinfo(&mntbuf, MNT_NOWAIT);
688	for (i = mntsize - 1; i >= 0; i--) {
689		if (strcmp(mntbuf[i].f_mntfromname, name) == 0 ||
690		    strcmp(mntbuf[i].f_mntonname, name) == 0)
691			return (&mntbuf[i]);
692	}
693	return (NULL);
694}
695
696char *
697catopt(char *s0, const char *s1)
698{
699	char *cp;
700
701	if (s1 == NULL || *s1 == '\0')
702		return (s0);
703
704	if (s0 && *s0) {
705		if (asprintf(&cp, "%s,%s", s0, s1) == -1)
706			errx(1, "asprintf failed");
707	} else
708		cp = strdup(s1);
709
710	if (s0)
711		free(s0);
712	return (cp);
713}
714
715void
716mangle(char *options, struct cpa *a)
717{
718	char *p, *s, *val;
719
720	for (s = options; (p = strsep(&s, ",")) != NULL;)
721		if (*p != '\0') {
722			if (strcmp(p, "noauto") == 0) {
723				/*
724				 * Do not pass noauto option to nmount().
725				 * or external mount program.  noauto is
726				 * only used to prevent mounting a filesystem
727				 * when 'mount -a' is specified, and is
728				 * not a real mount option.
729				 */
730				continue;
731			} else if (strcmp(p, "late") == 0) {
732				/*
733				 * "late" is used to prevent certain file
734				 * systems from being mounted before late
735				 * in the boot cycle; for instance,
736				 * loopback NFS mounts can't be mounted
737				 * before mountd starts.
738				 */
739				continue;
740			} else if (strcmp(p, "failok") == 0) {
741				/*
742				 * "failok" is used to prevent certain file
743				 * systems from being causing the system to
744				 * drop into single user mode in the boot
745				 * cycle, and is not a real mount option.
746				 */
747				continue;
748			} else if (strncmp(p, "mountprog", 9) == 0) {
749				/*
750				 * "mountprog" is used to force the use of
751				 * userland mount programs.
752				 */
753				val = strchr(p, '=');
754                        	if (val != NULL) {
755                                	++val;
756					if (*val != '\0')
757						mountprog = strdup(val);
758				}
759
760				if (mountprog == NULL) {
761					errx(1, "Need value for -o mountprog");
762				}
763				continue;
764			} else if (strcmp(p, "userquota") == 0) {
765				continue;
766			} else if (strncmp(p, userquotaeq,
767			    sizeof(userquotaeq) - 1) == 0) {
768				continue;
769			} else if (strcmp(p, "groupquota") == 0) {
770				continue;
771			} else if (strncmp(p, groupquotaeq,
772			    sizeof(groupquotaeq) - 1) == 0) {
773				continue;
774			} else if (*p == '-') {
775				append_arg(a, p);
776				p = strchr(p, '=');
777				if (p != NULL) {
778					*p = '\0';
779					append_arg(a, p + 1);
780				}
781			} else {
782				append_arg(a, strdup("-o"));
783				append_arg(a, p);
784			}
785		}
786}
787
788
789char *
790update_options(char *opts, char *fstab, int curflags)
791{
792	char *o, *p;
793	char *cur;
794	char *expopt, *newopt, *tmpopt;
795
796	if (opts == NULL)
797		return (strdup(""));
798
799	/* remove meta options from list */
800	remopt(fstab, MOUNT_META_OPTION_FSTAB);
801	remopt(fstab, MOUNT_META_OPTION_CURRENT);
802	cur = flags2opts(curflags);
803
804	/*
805	 * Expand all meta-options passed to us first.
806	 */
807	expopt = NULL;
808	for (p = opts; (o = strsep(&p, ",")) != NULL;) {
809		if (strcmp(MOUNT_META_OPTION_FSTAB, o) == 0)
810			expopt = catopt(expopt, fstab);
811		else if (strcmp(MOUNT_META_OPTION_CURRENT, o) == 0)
812			expopt = catopt(expopt, cur);
813		else
814			expopt = catopt(expopt, o);
815	}
816	free(cur);
817	free(opts);
818
819	/*
820	 * Remove previous contradictory arguments. Given option "foo" we
821	 * remove all the "nofoo" options. Given "nofoo" we remove "nonofoo"
822	 * and "foo" - so we can deal with possible options like "notice".
823	 */
824	newopt = NULL;
825	for (p = expopt; (o = strsep(&p, ",")) != NULL;) {
826		if ((tmpopt = malloc( strlen(o) + 2 + 1 )) == NULL)
827			errx(1, "malloc failed");
828
829		strcpy(tmpopt, "no");
830		strcat(tmpopt, o);
831		remopt(newopt, tmpopt);
832		free(tmpopt);
833
834		if (strncmp("no", o, 2) == 0)
835			remopt(newopt, o+2);
836
837		newopt = catopt(newopt, o);
838	}
839	free(expopt);
840
841	return (newopt);
842}
843
844void
845remopt(char *string, const char *opt)
846{
847	char *o, *p, *r;
848
849	if (string == NULL || *string == '\0' || opt == NULL || *opt == '\0')
850		return;
851
852	r = string;
853
854	for (p = string; (o = strsep(&p, ",")) != NULL;) {
855		if (strcmp(opt, o) != 0) {
856			if (*r == ',' && *o != '\0')
857				r++;
858			while ((*r++ = *o++) != '\0')
859			    ;
860			*--r = ',';
861		}
862	}
863	*r = '\0';
864}
865
866void
867usage(void)
868{
869
870	(void)fprintf(stderr, "%s\n%s\n%s\n",
871"usage: mount [-adflpruvw] [-F fstab] [-o options] [-t ufs | external_type]",
872"       mount [-dfpruvw] special | node",
873"       mount [-dfpruvw] [-o options] [-t ufs | external_type] special node");
874	exit(1);
875}
876
877void
878putfsent(struct statfs *ent)
879{
880	struct fstab *fst;
881	char *opts, *rw;
882	int l;
883
884	opts = NULL;
885	/* flags2opts() doesn't return the "rw" option. */
886	if ((ent->f_flags & MNT_RDONLY) != 0)
887		rw = NULL;
888	else
889		rw = catopt(NULL, "rw");
890
891	opts = flags2opts(ent->f_flags);
892	opts = catopt(rw, opts);
893
894	if (strncmp(ent->f_mntfromname, "<below>", 7) == 0 ||
895	    strncmp(ent->f_mntfromname, "<above>", 7) == 0) {
896		strlcpy(ent->f_mntfromname,
897		    (strnstr(ent->f_mntfromname, ":", 8) +1),
898		    sizeof(ent->f_mntfromname));
899	}
900
901	l = strlen(ent->f_mntfromname);
902	printf("%s%s%s%s", ent->f_mntfromname,
903	    l < 8 ? "\t" : "",
904	    l < 16 ? "\t" : "",
905	    l < 24 ? "\t" : " ");
906	l = strlen(ent->f_mntonname);
907	printf("%s%s%s%s", ent->f_mntonname,
908	    l < 8 ? "\t" : "",
909	    l < 16 ? "\t" : "",
910	    l < 24 ? "\t" : " ");
911	printf("%s\t", ent->f_fstypename);
912	l = strlen(opts);
913	printf("%s%s", opts,
914	    l < 8 ? "\t" : " ");
915	free(opts);
916
917	if ((fst = getfsspec(ent->f_mntfromname)))
918		printf("\t%u %u\n", fst->fs_freq, fst->fs_passno);
919	else if ((fst = getfsfile(ent->f_mntonname)))
920		printf("\t%u %u\n", fst->fs_freq, fst->fs_passno);
921	else if (strcmp(ent->f_fstypename, "ufs") == 0) {
922		if (strcmp(ent->f_mntonname, "/") == 0)
923			printf("\t1 1\n");
924		else
925			printf("\t2 2\n");
926	} else
927		printf("\t0 0\n");
928}
929
930
931char *
932flags2opts(int flags)
933{
934	char *res;
935
936	res = NULL;
937
938	if (flags & MNT_RDONLY)		res = catopt(res, "ro");
939	if (flags & MNT_SYNCHRONOUS)	res = catopt(res, "sync");
940	if (flags & MNT_NOEXEC)		res = catopt(res, "noexec");
941	if (flags & MNT_NOSUID)		res = catopt(res, "nosuid");
942	if (flags & MNT_UNION)		res = catopt(res, "union");
943	if (flags & MNT_ASYNC)		res = catopt(res, "async");
944	if (flags & MNT_NOATIME)	res = catopt(res, "noatime");
945	if (flags & MNT_NOCLUSTERR)	res = catopt(res, "noclusterr");
946	if (flags & MNT_NOCLUSTERW)	res = catopt(res, "noclusterw");
947	if (flags & MNT_NOSYMFOLLOW)	res = catopt(res, "nosymfollow");
948	if (flags & MNT_SUIDDIR)	res = catopt(res, "suiddir");
949	if (flags & MNT_MULTILABEL)	res = catopt(res, "multilabel");
950	if (flags & MNT_ACLS)		res = catopt(res, "acls");
951	if (flags & MNT_NFS4ACLS)	res = catopt(res, "nfsv4acls");
952	if (flags & MNT_UNTRUSTED)	res = catopt(res, "untrusted");
953	if (flags & MNT_NOCOVER)	res = catopt(res, "nocover");
954	if (flags & MNT_EMPTYDIR)	res = catopt(res, "emptydir");
955
956	return (res);
957}
958