mount.c revision 125933
1/*-
2 * Copyright (c) 1980, 1989, 1993, 1994
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by the University of
16 *	California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#ifndef lint
35static const char copyright[] =
36"@(#) Copyright (c) 1980, 1989, 1993, 1994\n\
37	The Regents of the University of California.  All rights reserved.\n";
38#endif /* not lint */
39
40#ifndef lint
41#if 0
42static char sccsid[] = "@(#)mount.c	8.25 (Berkeley) 5/8/95";
43#endif
44static const char rcsid[] =
45  "$FreeBSD: head/sbin/mount/mount.c 125933 2004-02-17 08:43:05Z grog $";
46#endif /* not lint */
47
48#include <sys/param.h>
49#include <sys/mount.h>
50#include <sys/stat.h>
51#include <sys/wait.h>
52
53#include <ufs/ufs/ufsmount.h>
54#include <ufs/ufs/dinode.h>
55#include <ufs/ffs/fs.h>
56
57#include <ctype.h>
58#include <err.h>
59#include <errno.h>
60#include <fstab.h>
61#include <paths.h>
62#include <pwd.h>
63#include <signal.h>
64#include <stdint.h>
65#include <stdio.h>
66#include <stdlib.h>
67#include <string.h>
68#include <unistd.h>
69#include <libufs.h>
70
71#include "extern.h"
72#include "mntopts.h"
73#include "pathnames.h"
74
75/* `meta' options */
76#define MOUNT_META_OPTION_FSTAB		"fstab"
77#define MOUNT_META_OPTION_CURRENT	"current"
78
79int debug, fstab_style, verbose;
80
81char   *catopt(char *, const char *);
82struct statfs *getmntpt(const char *);
83int	hasopt(const char *, const char *);
84int	ismounted(struct fstab *, struct statfs *, int);
85int	isremountable(const char *);
86void	mangle(char *, int *, const char **);
87char   *update_options(char *, char *, int);
88int	mountfs(const char *, const char *, const char *,
89			int, const char *, const char *);
90void	remopt(char *, const char *);
91void	prmount(struct statfs *);
92void	putfsent(const struct statfs *);
93void	usage(void);
94char   *flags2opts(int);
95
96/* Map from mount options to printable formats. */
97static struct opt {
98	int o_opt;
99	const char *o_name;
100} optnames[] = {
101	{ MNT_ASYNC,		"asynchronous" },
102	{ MNT_EXPORTED,		"NFS exported" },
103	{ MNT_LOCAL,		"local" },
104	{ MNT_NOATIME,		"noatime" },
105	{ MNT_NODEV,		"nodev" },
106	{ MNT_NOEXEC,		"noexec" },
107	{ MNT_NOSUID,		"nosuid" },
108	{ MNT_NOSYMFOLLOW,	"nosymfollow" },
109	{ MNT_QUOTA,		"with quotas" },
110	{ MNT_RDONLY,		"read-only" },
111	{ MNT_SYNCHRONOUS,	"synchronous" },
112	{ MNT_UNION,		"union" },
113	{ MNT_NOCLUSTERR,	"noclusterr" },
114	{ MNT_NOCLUSTERW,	"noclusterw" },
115	{ MNT_SUIDDIR,		"suiddir" },
116	{ MNT_SOFTDEP,		"soft-updates" },
117	{ MNT_MULTILABEL,	"multilabel" },
118	{ MNT_ACLS,		"acls" },
119	{ 0, NULL }
120};
121
122/*
123 * List of VFS types that can be remounted without becoming mounted on top
124 * of each other.
125 * XXX Is this list correct?
126 */
127static const char *
128remountable_fs_names[] = {
129	"ufs", "ffs", "ext2fs",
130	0
131};
132
133int
134main(argc, argv)
135	int argc;
136	char * const argv[];
137{
138	const char *mntfromname, **vfslist, *vfstype;
139	struct fstab *fs;
140	struct statfs *mntbuf;
141	FILE *mountdfp;
142	pid_t pid;
143	int all, ch, i, init_flags, mntsize, rval, have_fstab;
144	char *cp, *ep, *options;
145
146	all = init_flags = 0;
147	options = NULL;
148	vfslist = NULL;
149	vfstype = "ufs";
150	while ((ch = getopt(argc, argv, "adF:fo:prwt:uv")) != -1)
151		switch (ch) {
152		case 'a':
153			all = 1;
154			break;
155		case 'd':
156			debug = 1;
157			break;
158		case 'F':
159			setfstab(optarg);
160			break;
161		case 'f':
162			init_flags |= MNT_FORCE;
163			break;
164		case 'o':
165			if (*optarg)
166				options = catopt(options, optarg);
167			break;
168		case 'p':
169			fstab_style = 1;
170			verbose = 1;
171			break;
172		case 'r':
173			options = catopt(options, "ro");
174			break;
175		case 't':
176			if (vfslist != NULL)
177				errx(1, "only one -t option may be specified");
178			vfslist = makevfslist(optarg);
179			vfstype = optarg;
180			break;
181		case 'u':
182			init_flags |= MNT_UPDATE;
183			break;
184		case 'v':
185			verbose = 1;
186			break;
187		case 'w':
188			options = catopt(options, "noro");
189			break;
190		case '?':
191		default:
192			usage();
193			/* NOTREACHED */
194		}
195	argc -= optind;
196	argv += optind;
197
198#define	BADTYPE(type)							\
199	(strcmp(type, FSTAB_RO) &&					\
200	    strcmp(type, FSTAB_RW) && strcmp(type, FSTAB_RQ))
201
202	rval = 0;
203	switch (argc) {
204	case 0:
205		if ((mntsize = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0)
206			err(1, "getmntinfo");
207		if (all) {
208			while ((fs = getfsent()) != NULL) {
209				if (BADTYPE(fs->fs_type))
210					continue;
211				if (checkvfsname(fs->fs_vfstype, vfslist))
212					continue;
213				if (hasopt(fs->fs_mntops, "noauto"))
214					continue;
215				if (!(init_flags & MNT_UPDATE) &&
216				    ismounted(fs, mntbuf, mntsize))
217					continue;
218				options = update_options(options, fs->fs_mntops,
219				    mntbuf->f_flags);
220				if (mountfs(fs->fs_vfstype, fs->fs_spec,
221				    fs->fs_file, init_flags, options,
222				    fs->fs_mntops))
223					rval = 1;
224			}
225		} else if (fstab_style) {
226			for (i = 0; i < mntsize; i++) {
227				if (checkvfsname(mntbuf[i].f_fstypename, vfslist))
228					continue;
229				putfsent(&mntbuf[i]);
230			}
231		} else {
232			for (i = 0; i < mntsize; i++) {
233				if (checkvfsname(mntbuf[i].f_fstypename,
234				    vfslist))
235					continue;
236				prmount(&mntbuf[i]);
237			}
238		}
239		exit(rval);
240	case 1:
241		if (vfslist != NULL)
242			usage();
243
244		rmslashes(*argv, *argv);
245		if (init_flags & MNT_UPDATE) {
246			mntfromname = NULL;
247			have_fstab = 0;
248			if ((mntbuf = getmntpt(*argv)) == NULL)
249				errx(1, "not currently mounted %s", *argv);
250			/*
251			 * Only get the mntflags from fstab if both mntpoint
252			 * and mntspec are identical. Also handle the special
253			 * case where just '/' is mounted and 'spec' is not
254			 * identical with the one from fstab ('/dev' is missing
255			 * in the spec-string at boot-time).
256			 */
257			if ((fs = getfsfile(mntbuf->f_mntonname)) != NULL) {
258				if (strcmp(fs->fs_spec,
259				    mntbuf->f_mntfromname) == 0 &&
260				    strcmp(fs->fs_file,
261				    mntbuf->f_mntonname) == 0) {
262					have_fstab = 1;
263					mntfromname = mntbuf->f_mntfromname;
264				} else if (argv[0][0] == '/' &&
265				    argv[0][1] == '\0') {
266					fs = getfsfile("/");
267					have_fstab = 1;
268					mntfromname = fs->fs_spec;
269				}
270			}
271			if (have_fstab) {
272				options = update_options(options, fs->fs_mntops,
273				    mntbuf->f_flags);
274			} else {
275				mntfromname = mntbuf->f_mntfromname;
276				options = update_options(options, NULL,
277				    mntbuf->f_flags);
278			}
279			rval = mountfs(mntbuf->f_fstypename, mntfromname,
280			    mntbuf->f_mntonname, init_flags, options, 0);
281			break;
282		}
283		if ((fs = getfsfile(*argv)) == NULL &&
284		    (fs = getfsspec(*argv)) == NULL)
285			errx(1, "%s: unknown special file or file system",
286			    *argv);
287		if (BADTYPE(fs->fs_type))
288			errx(1, "%s has unknown file system type",
289			    *argv);
290		rval = mountfs(fs->fs_vfstype, fs->fs_spec, fs->fs_file,
291		    init_flags, options, fs->fs_mntops);
292		break;
293	case 2:
294		/*
295		 * If -t flag has not been specified, the path cannot be
296		 * found, spec contains either a ':' or a '@', then assume
297		 * that an NFS file system is being specified ala Sun.
298		 * Check if the hostname contains only allowed characters
299		 * to reduce false positives.  IPv6 addresses containing
300		 * ':' will be correctly parsed only if the separator is '@'.
301		 * The definition of a valid hostname is taken from RFC 1034.
302		 */
303		if (vfslist == NULL && ((ep = strchr(argv[0], '@')) != NULL ||
304		    (ep = strchr(argv[0], ':')) != NULL)) {
305			if (*ep == '@') {
306				cp = ep + 1;
307				ep = cp + strlen(cp);
308			} else
309				cp = argv[0];
310			while (cp != ep) {
311				if (!isdigit(*cp) && !isalpha(*cp) &&
312				    *cp != '.' && *cp != '-' && *cp != ':')
313					break;
314				cp++;
315			}
316			if (cp == ep)
317				vfstype = "nfs";
318		}
319		rval = mountfs(vfstype,
320		    argv[0], argv[1], init_flags, options, NULL);
321		break;
322	default:
323		usage();
324		/* NOTREACHED */
325	}
326
327	/*
328	 * If the mount was successfully, and done by root, tell mountd the
329	 * good news.  Pid checks are probably unnecessary, but don't hurt.
330	 */
331	if (rval == 0 && getuid() == 0 &&
332	    (mountdfp = fopen(_PATH_MOUNTDPID, "r")) != NULL) {
333		if (fscanf(mountdfp, "%d", &pid) == 1 &&
334		     pid > 0 && kill(pid, SIGHUP) == -1 && errno != ESRCH)
335			err(1, "signal mountd");
336		(void)fclose(mountdfp);
337	}
338
339	exit(rval);
340}
341
342int
343ismounted(fs, mntbuf, mntsize)
344	struct fstab *fs;
345	struct statfs *mntbuf;
346	int mntsize;
347{
348	int i;
349
350	if (fs->fs_file[0] == '/' && fs->fs_file[1] == '\0')
351		/* the root file system can always be remounted */
352		return (0);
353
354	for (i = mntsize - 1; i >= 0; --i)
355		if (strcmp(fs->fs_file, mntbuf[i].f_mntonname) == 0 &&
356		    (!isremountable(fs->fs_vfstype) ||
357		     strcmp(fs->fs_spec, mntbuf[i].f_mntfromname) == 0))
358			return (1);
359	return (0);
360}
361
362int
363isremountable(vfsname)
364	const char *vfsname;
365{
366	const char **cp;
367
368	for (cp = remountable_fs_names; *cp; cp++)
369		if (strcmp(*cp, vfsname) == 0)
370			return (1);
371	return (0);
372}
373
374int
375hasopt(mntopts, option)
376	const char *mntopts, *option;
377{
378	int negative, found;
379	char *opt, *optbuf;
380
381	if (option[0] == 'n' && option[1] == 'o') {
382		negative = 1;
383		option += 2;
384	} else
385		negative = 0;
386	optbuf = strdup(mntopts);
387	found = 0;
388	for (opt = optbuf; (opt = strtok(opt, ",")) != NULL; opt = NULL) {
389		if (opt[0] == 'n' && opt[1] == 'o') {
390			if (!strcasecmp(opt + 2, option))
391				found = negative;
392		} else if (!strcasecmp(opt, option))
393			found = !negative;
394	}
395	free(optbuf);
396	return (found);
397}
398
399int
400mountfs(vfstype, spec, name, flags, options, mntopts)
401	const char *vfstype, *spec, *name, *options, *mntopts;
402	int flags;
403{
404	const char *argv[100];
405	struct statfs sf;
406	pid_t pid;
407	int argc, i, status;
408	char *optbuf, execname[PATH_MAX], mntpath[PATH_MAX];
409
410#if __GNUC__
411	(void)&optbuf;
412	(void)&name;
413#endif
414
415	/* resolve the mountpoint with realpath(3) */
416	(void)checkpath(name, mntpath);
417	name = mntpath;
418
419	if (mntopts == NULL)
420		mntopts = "";
421	if (options == NULL) {
422		if (*mntopts == '\0') {
423			options = "rw";
424		} else {
425			options = mntopts;
426			mntopts = "";
427		}
428	}
429	optbuf = catopt(strdup(mntopts), options);
430
431	if (strcmp(name, "/") == 0)
432		flags |= MNT_UPDATE;
433	if (flags & MNT_FORCE)
434		optbuf = catopt(optbuf, "force");
435	if (flags & MNT_RDONLY)
436		optbuf = catopt(optbuf, "ro");
437	/*
438	 * XXX
439	 * The mount_mfs (newfs) command uses -o to select the
440	 * optimization mode.  We don't pass the default "-o rw"
441	 * for that reason.
442	 */
443	if (flags & MNT_UPDATE)
444		optbuf = catopt(optbuf, "update");
445
446	/* Compatibility glue. */
447	if (strcmp(vfstype, "msdos") == 0)
448		vfstype = "msdosfs";
449
450	argc = 0;
451	argv[argc++] = vfstype;
452	mangle(optbuf, &argc, argv);
453	argv[argc++] = spec;
454	argv[argc++] = name;
455	argv[argc] = NULL;
456
457	if (debug) {
458		(void)printf("exec: mount_%s", vfstype);
459		for (i = 1; i < argc; i++)
460			(void)printf(" %s", argv[i]);
461		(void)printf("\n");
462		return (0);
463	}
464
465	switch (pid = fork()) {
466	case -1:				/* Error. */
467		warn("fork");
468		free(optbuf);
469		return (1);
470	case 0:					/* Child. */
471		if (strcmp(vfstype, "ufs") == 0)
472			exit(mount_ufs(argc, (char * const *) argv));
473
474		/* Go find an executable. */
475		(void)snprintf(execname, sizeof(execname), "mount_%s", vfstype);
476		execvP(execname, _PATH_SYSPATH, (char * const *)argv);
477		if (errno == ENOENT) {
478			warn("exec mount_%s not found in %s", vfstype,
479			    _PATH_SYSPATH);
480		}
481		exit(1);
482		/* NOTREACHED */
483	default:				/* Parent. */
484		free(optbuf);
485
486		if (waitpid(pid, &status, 0) < 0) {
487			warn("waitpid");
488			return (1);
489		}
490
491		if (WIFEXITED(status)) {
492			if (WEXITSTATUS(status) != 0)
493				return (WEXITSTATUS(status));
494		} else if (WIFSIGNALED(status)) {
495			warnx("%s: %s", name, sys_siglist[WTERMSIG(status)]);
496			return (1);
497		}
498
499		if (verbose) {
500			if (statfs(name, &sf) < 0) {
501				warn("statfs %s", name);
502				return (1);
503			}
504			if (fstab_style)
505				putfsent(&sf);
506			else
507				prmount(&sf);
508		}
509		break;
510	}
511
512	return (0);
513}
514
515void
516prmount(sfp)
517	struct statfs *sfp;
518{
519	int flags, i;
520	struct opt *o;
521	struct passwd *pw;
522	struct uufsd disk;
523
524	(void)printf("%s on %s (%s", sfp->f_mntfromname, sfp->f_mntonname,
525	    sfp->f_fstypename);
526
527	if (strncmp(sfp->f_fstypename, "ufs", 3) == 0) {
528		ufs_disk_fillout(&disk, sfp->f_mntonname);
529		printf("%s", (disk.d_ufs == 2) ? "2" : "");
530	}
531
532	flags = sfp->f_flags & MNT_VISFLAGMASK;
533	for (o = optnames; flags && o->o_opt; o++)
534		if (flags & o->o_opt) {
535			(void)printf(", %s", o->o_name);
536			flags &= ~o->o_opt;
537		}
538	/*
539	 * Inform when file system is mounted by an unprivileged user
540	 * or privileged non-root user.
541	 */
542	if ((flags & MNT_USER) != 0 || sfp->f_owner != 0) {
543		(void)printf(", mounted by ");
544		if ((pw = getpwuid(sfp->f_owner)) != NULL)
545			(void)printf("%s", pw->pw_name);
546		else
547			(void)printf("%d", sfp->f_owner);
548	}
549	if (verbose) {
550		if (sfp->f_syncwrites != 0 || sfp->f_asyncwrites != 0)
551			(void)printf(", writes: sync %ju async %ju",
552			    (uintmax_t)sfp->f_syncwrites,
553			    (uintmax_t)sfp->f_asyncwrites);
554		if (sfp->f_syncreads != 0 || sfp->f_asyncreads != 0)
555			(void)printf(", reads: sync %ju async %ju",
556			    (uintmax_t)sfp->f_syncreads,
557			    (uintmax_t)sfp->f_asyncreads);
558		if (sfp->f_fsid.val[0] != 0 || sfp->f_fsid.val[1] != 0) {
559			printf(", fsid ");
560			for (i = 0; i < sizeof(sfp->f_fsid); i++)
561				printf("%02x", ((u_char *)&sfp->f_fsid)[i]);
562		}
563	}
564	(void)printf(")\n");
565}
566
567struct statfs *
568getmntpt(name)
569	const char *name;
570{
571	struct statfs *mntbuf;
572	int i, mntsize;
573
574	mntsize = getmntinfo(&mntbuf, MNT_NOWAIT);
575	for (i = mntsize - 1; i >= 0; i--) {
576		if (strcmp(mntbuf[i].f_mntfromname, name) == 0 ||
577		    strcmp(mntbuf[i].f_mntonname, name) == 0)
578			return (&mntbuf[i]);
579	}
580	return (NULL);
581}
582
583char *
584catopt(s0, s1)
585	char *s0;
586	const char *s1;
587{
588	size_t i;
589	char *cp;
590
591	if (s1 == NULL || *s1 == '\0')
592		return s0;
593
594	if (s0 && *s0) {
595		i = strlen(s0) + strlen(s1) + 1 + 1;
596		if ((cp = malloc(i)) == NULL)
597			errx(1, "malloc failed");
598		(void)snprintf(cp, i, "%s,%s", s0, s1);
599	} else
600		cp = strdup(s1);
601
602	if (s0)
603		free(s0);
604	return (cp);
605}
606
607void
608mangle(options, argcp, argv)
609	char *options;
610	int *argcp;
611	const char **argv;
612{
613	char *p, *s;
614	int argc;
615
616	argc = *argcp;
617	for (s = options; (p = strsep(&s, ",")) != NULL;)
618		if (*p != '\0') {
619			if (*p == '-') {
620				argv[argc++] = p;
621				p = strchr(p, '=');
622				if (p != NULL) {
623					*p = '\0';
624					argv[argc++] = p+1;
625				}
626			} else if (strcmp(p, "rw") != 0) {
627				argv[argc++] = "-o";
628				argv[argc++] = p;
629			}
630		}
631
632	*argcp = argc;
633}
634
635
636char *
637update_options(opts, fstab, curflags)
638	char *opts;
639	char *fstab;
640	int curflags;
641{
642	char *o, *p;
643	char *cur;
644	char *expopt, *newopt, *tmpopt;
645
646	if (opts == NULL)
647		return strdup("");
648
649	/* remove meta options from list */
650	remopt(fstab, MOUNT_META_OPTION_FSTAB);
651	remopt(fstab, MOUNT_META_OPTION_CURRENT);
652	cur = flags2opts(curflags);
653
654	/*
655	 * Expand all meta-options passed to us first.
656	 */
657	expopt = NULL;
658	for (p = opts; (o = strsep(&p, ",")) != NULL;) {
659		if (strcmp(MOUNT_META_OPTION_FSTAB, o) == 0)
660			expopt = catopt(expopt, fstab);
661		else if (strcmp(MOUNT_META_OPTION_CURRENT, o) == 0)
662			expopt = catopt(expopt, cur);
663		else
664			expopt = catopt(expopt, o);
665	}
666	free(cur);
667	free(opts);
668
669	/*
670	 * Remove previous contradictory arguments. Given option "foo" we
671	 * remove all the "nofoo" options. Given "nofoo" we remove "nonofoo"
672	 * and "foo" - so we can deal with possible options like "notice".
673	 */
674	newopt = NULL;
675	for (p = expopt; (o = strsep(&p, ",")) != NULL;) {
676		if ((tmpopt = malloc( strlen(o) + 2 + 1 )) == NULL)
677			errx(1, "malloc failed");
678
679		strcpy(tmpopt, "no");
680		strcat(tmpopt, o);
681		remopt(newopt, tmpopt);
682		free(tmpopt);
683
684		if (strncmp("no", o, 2) == 0)
685			remopt(newopt, o+2);
686
687		newopt = catopt(newopt, o);
688	}
689	free(expopt);
690
691	return newopt;
692}
693
694void
695remopt(string, opt)
696	char *string;
697 	const char *opt;
698{
699	char *o, *p, *r;
700
701	if (string == NULL || *string == '\0' || opt == NULL || *opt == '\0')
702		return;
703
704	r = string;
705
706	for (p = string; (o = strsep(&p, ",")) != NULL;) {
707		if (strcmp(opt, o) != 0) {
708			if (*r == ',' && *o != '\0')
709				r++;
710			while ((*r++ = *o++) != '\0')
711			    ;
712			*--r = ',';
713		}
714	}
715	*r = '\0';
716}
717
718void
719usage()
720{
721
722	(void)fprintf(stderr, "%s\n%s\n%s\n",
723"usage: mount [-dfpruvw] [-o options] [-t ufs | external_type] special node",
724"       mount [-adfpruvw] [ -F fstab] [-o options] [-t ufs | external_type]",
725"       mount [-dfpruvw] special | node");
726	exit(1);
727}
728
729void
730putfsent(ent)
731	const struct statfs *ent;
732{
733	struct fstab *fst;
734	char *opts;
735
736	opts = flags2opts(ent->f_flags);
737	printf("%s\t%s\t%s %s", ent->f_mntfromname, ent->f_mntonname,
738	    ent->f_fstypename, opts);
739	free(opts);
740
741	if ((fst = getfsspec(ent->f_mntfromname)))
742		printf("\t%u %u\n", fst->fs_freq, fst->fs_passno);
743	else if ((fst = getfsfile(ent->f_mntonname)))
744		printf("\t%u %u\n", fst->fs_freq, fst->fs_passno);
745	else if (strcmp(ent->f_fstypename, "ufs") == 0) {
746		if (strcmp(ent->f_mntonname, "/") == 0)
747			printf("\t1 1\n");
748		else
749			printf("\t2 2\n");
750	} else
751		printf("\t0 0\n");
752}
753
754
755char *
756flags2opts(flags)
757	int flags;
758{
759	char *res;
760
761	res = NULL;
762
763	res = catopt(res, (flags & MNT_RDONLY) ? "ro" : "rw");
764
765	if (flags & MNT_SYNCHRONOUS)	res = catopt(res, "sync");
766	if (flags & MNT_NOEXEC)		res = catopt(res, "noexec");
767	if (flags & MNT_NOSUID)		res = catopt(res, "nosuid");
768	if (flags & MNT_NODEV)		res = catopt(res, "nodev");
769	if (flags & MNT_UNION)		res = catopt(res, "union");
770	if (flags & MNT_ASYNC)		res = catopt(res, "async");
771	if (flags & MNT_NOATIME)	res = catopt(res, "noatime");
772	if (flags & MNT_NOCLUSTERR)	res = catopt(res, "noclusterr");
773	if (flags & MNT_NOCLUSTERW)	res = catopt(res, "noclusterw");
774	if (flags & MNT_NOSYMFOLLOW)	res = catopt(res, "nosymfollow");
775	if (flags & MNT_SUIDDIR)	res = catopt(res, "suiddir");
776	if (flags & MNT_MULTILABEL)	res = catopt(res, "multilabel");
777	if (flags & MNT_ACLS)		res = catopt(res, "acls");
778
779	return res;
780}
781