1241806Suqs/*	$NetBSD: fsck.c,v 1.30 2003/08/07 10:04:15 agc Exp $	*/
266868Sadrian
3331722Seadler/*
466868Sadrian * Copyright (c) 1996 Christos Zoulas. All rights reserved.
566868Sadrian * Copyright (c) 1980, 1989, 1993, 1994
666868Sadrian *	The Regents of the University of California.  All rights reserved.
766868Sadrian *
866868Sadrian * Redistribution and use in source and binary forms, with or without
966868Sadrian * modification, are permitted provided that the following conditions
1066868Sadrian * are met:
1166868Sadrian * 1. Redistributions of source code must retain the above copyright
1266868Sadrian *    notice, this list of conditions and the following disclaimer.
1366868Sadrian * 2. Redistributions in binary form must reproduce the above copyright
1466868Sadrian *    notice, this list of conditions and the following disclaimer in the
1566868Sadrian *    documentation and/or other materials provided with the distribution.
16241806Suqs * 3. Neither the name of the University nor the names of its contributors
1766868Sadrian *    may be used to endorse or promote products derived from this software
1866868Sadrian *    without specific prior written permission.
1966868Sadrian *
2066868Sadrian * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2166868Sadrian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2266868Sadrian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2366868Sadrian * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2466868Sadrian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2566868Sadrian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2666868Sadrian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2766868Sadrian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2866868Sadrian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2966868Sadrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3066868Sadrian * SUCH DAMAGE.
3166868Sadrian *
3266868Sadrian * From: @(#)mount.c	8.19 (Berkeley) 4/19/94
3366918Sobrien * From: $NetBSD: mount.c,v 1.24 1995/11/18 03:34:29 cgd Exp
34241806Suqs * $NetBSD: fsck.c,v 1.30 2003/08/07 10:04:15 agc Exp $
3566868Sadrian */
3666868Sadrian
3766868Sadrian#include <sys/cdefs.h>
3893102Smarkm__FBSDID("$FreeBSD: stable/11/sbin/fsck/fsck.c 315546 2017-03-19 14:05:56Z trasz $");
3966868Sadrian
4066868Sadrian#include <sys/param.h>
4166868Sadrian#include <sys/mount.h>
4266868Sadrian#include <sys/queue.h>
4366868Sadrian#include <sys/wait.h>
44274770Simp#include <sys/disk.h>
4566868Sadrian#include <sys/ioctl.h>
4666868Sadrian
4793102Smarkm#include <ctype.h>
4866868Sadrian#include <err.h>
4966868Sadrian#include <errno.h>
5066868Sadrian#include <fstab.h>
5166868Sadrian#include <fcntl.h>
5266868Sadrian#include <paths.h>
5366868Sadrian#include <signal.h>
5466868Sadrian#include <stdio.h>
5566868Sadrian#include <stdlib.h>
5666868Sadrian#include <string.h>
5766868Sadrian#include <unistd.h>
5866868Sadrian
5966868Sadrian#include "fsutil.h"
6066868Sadrian
6166868Sadrianstatic enum { IN_LIST, NOT_IN_LIST } which = NOT_IN_LIST;
6266868Sadrian
63227081Sedstatic TAILQ_HEAD(fstypelist, entry) opthead, selhead;
6466868Sadrian
6566868Sadrianstruct entry {
6666868Sadrian	char *type;
6766868Sadrian	char *options;
6866868Sadrian	TAILQ_ENTRY(entry) entries;
6966868Sadrian};
7066868Sadrian
7166868Sadrianstatic char *options = NULL;
7266868Sadrianstatic int flags = 0;
7375936Smckusickstatic int forceflag = 0;
7466868Sadrian
75241807Suqsstatic int checkfs(const char *, const char *, const char *, const char *, pid_t *);
7692839Simpstatic int selected(const char *);
7792839Simpstatic void addoption(char *);
7892839Simpstatic const char *getoptions(const char *);
7992839Simpstatic void addentry(struct fstypelist *, const char *, const char *);
8092839Simpstatic void maketypelist(char *);
8192839Simpstatic void catopt(char **, const char *);
82241807Suqsstatic void mangle(char *, int *, const char ** volatile *, int *);
83274770Simpstatic const char *getfstype(const char *);
8492839Simpstatic void usage(void) __dead2;
8592839Simpstatic int isok(struct fstab *);
8666868Sadrian
87274770Simpstatic struct {
88274770Simp	const char *ptype;
89274770Simp	const char *name;
90274770Simp} ptype_map[] = {
91274770Simp	{ "ufs",	"ffs" },
92274770Simp	{ "ffs",	"ffs" },
93274770Simp	{ "fat",	"msdosfs" },
94274770Simp	{ "efi",	"msdosfs" },
95274770Simp	{ NULL,		NULL },
96274770Simp};
97274770Simp
9866868Sadrianint
9992839Simpmain(int argc, char *argv[])
10066868Sadrian{
10166868Sadrian	struct fstab *fs;
10266868Sadrian	int i, rval = 0;
10366868Sadrian	const char *vfstype = NULL;
10466868Sadrian	char globopt[3];
105226711Ssobomax	const char *etc_fstab;
10666868Sadrian
10766868Sadrian	globopt[0] = '-';
10866868Sadrian	globopt[2] = '\0';
10966868Sadrian
11066868Sadrian	TAILQ_INIT(&selhead);
11166868Sadrian	TAILQ_INIT(&opthead);
11266868Sadrian
113226711Ssobomax	etc_fstab = NULL;
114226711Ssobomax	while ((i = getopt(argc, argv, "BCdvpfFnyl:t:T:c:")) != -1)
11566868Sadrian		switch (i) {
11675936Smckusick		case 'B':
11775936Smckusick			if (flags & CHECK_BACKGRD)
11875936Smckusick				errx(1, "Cannot specify -B and -F.");
11975936Smckusick			flags |= DO_BACKGRD;
12075936Smckusick			break;
12175936Smckusick
12266868Sadrian		case 'd':
12366868Sadrian			flags |= CHECK_DEBUG;
12466868Sadrian			break;
12566868Sadrian
12666868Sadrian		case 'v':
12766868Sadrian			flags |= CHECK_VERBOSE;
12866868Sadrian			break;
12966868Sadrian
13075936Smckusick		case 'F':
13175936Smckusick			if (flags & DO_BACKGRD)
13275936Smckusick				errx(1, "Cannot specify -B and -F.");
13375936Smckusick			flags |= CHECK_BACKGRD;
13475936Smckusick			break;
13575936Smckusick
13666868Sadrian		case 'p':
13766868Sadrian			flags |= CHECK_PREEN;
13866868Sadrian			/*FALLTHROUGH*/
139187931Sobrien		case 'C':
140187931Sobrien			flags |= CHECK_CLEAN;
141187931Sobrien			/*FALLTHROUGH*/
14266868Sadrian		case 'n':
14366868Sadrian		case 'y':
14466868Sadrian			globopt[1] = i;
14566868Sadrian			catopt(&options, globopt);
14666868Sadrian			break;
14766868Sadrian
14875936Smckusick		case 'f':
14975936Smckusick			forceflag = 1;
15075936Smckusick			globopt[1] = i;
15175936Smckusick			catopt(&options, globopt);
15275936Smckusick			break;
15375936Smckusick
15466868Sadrian		case 'l':
15575015Sphk			warnx("Ignoring obsolete -l option\n");
15666868Sadrian			break;
15766868Sadrian
15866868Sadrian		case 'T':
15966868Sadrian			if (*optarg)
16066868Sadrian				addoption(optarg);
16166868Sadrian			break;
16266868Sadrian
16366868Sadrian		case 't':
16470522Sphk			if (!TAILQ_EMPTY(&selhead))
16566868Sadrian				errx(1, "only one -t option may be specified.");
16666868Sadrian
16766868Sadrian			maketypelist(optarg);
16866868Sadrian			vfstype = optarg;
16966868Sadrian			break;
17066868Sadrian
171226711Ssobomax		case 'c':
172226711Ssobomax			etc_fstab = optarg;
173226711Ssobomax			break;
174226711Ssobomax
17566868Sadrian		case '?':
17666868Sadrian		default:
17766868Sadrian			usage();
17866868Sadrian			/* NOTREACHED */
17966868Sadrian		}
18066868Sadrian
18166868Sadrian	argc -= optind;
18266868Sadrian	argv += optind;
18366868Sadrian
184226711Ssobomax	if (etc_fstab != NULL)
185226711Ssobomax		setfstab(etc_fstab);
186226711Ssobomax
18766868Sadrian	if (argc == 0)
18875015Sphk		return checkfstab(flags, isok, checkfs);
18966868Sadrian
19066868Sadrian#define	BADTYPE(type)							\
19166868Sadrian	(strcmp(type, FSTAB_RO) &&					\
19266868Sadrian	    strcmp(type, FSTAB_RW) && strcmp(type, FSTAB_RQ))
19366868Sadrian
19466868Sadrian
19566868Sadrian	for (; argc--; argv++) {
19675936Smckusick		const char *spec, *mntpt, *type, *cp;
19775936Smckusick		char device[MAXPATHLEN];
19875936Smckusick		struct statfs *mntp;
19966868Sadrian
200241807Suqs		mntpt = NULL;
20166868Sadrian		spec = *argv;
20266868Sadrian		cp = strrchr(spec, '/');
203298194Saraujo		if (cp == NULL) {
20466868Sadrian			(void)snprintf(device, sizeof(device), "%s%s",
20566868Sadrian				_PATH_DEV, spec);
20666868Sadrian			spec = device;
20766868Sadrian		}
20875936Smckusick		mntp = getmntpt(spec);
20975936Smckusick		if (mntp != NULL) {
21075936Smckusick			spec = mntp->f_mntfromname;
21175936Smckusick			mntpt = mntp->f_mntonname;
21275936Smckusick		}
21366868Sadrian		if ((fs = getfsfile(spec)) == NULL &&
21466868Sadrian		    (fs = getfsspec(spec)) == NULL) {
21566868Sadrian			if (vfstype == NULL)
216274770Simp				vfstype = getfstype(spec);
217106254Sphk			if (vfstype == NULL)
218315546Strasz				vfstype = "ufs";
21966868Sadrian			type = vfstype;
22075936Smckusick			devcheck(spec);
22175936Smckusick		} else {
22266868Sadrian			spec = fs->fs_spec;
22366868Sadrian			type = fs->fs_vfstype;
22475936Smckusick			mntpt = fs->fs_file;
22566868Sadrian			if (BADTYPE(fs->fs_type))
226102231Strhodes				errx(1, "%s has unknown file system type.",
22766868Sadrian				    spec);
22866868Sadrian		}
22975936Smckusick		if ((flags & CHECK_BACKGRD) &&
23075936Smckusick		    checkfs(type, spec, mntpt, "-F", NULL) == 0) {
23175936Smckusick			printf("%s: DEFER FOR BACKGROUND CHECKING\n", *argv);
23275936Smckusick			continue;
23375936Smckusick		}
23475936Smckusick		if ((flags & DO_BACKGRD) && forceflag == 0 &&
23575936Smckusick		    checkfs(type, spec, mntpt, "-F", NULL) != 0)
23675936Smckusick			continue;
23766868Sadrian
23875936Smckusick		rval |= checkfs(type, spec, mntpt, NULL, NULL);
23966868Sadrian	}
24066868Sadrian
24166868Sadrian	return rval;
24266868Sadrian}
24366868Sadrian
24466868Sadrian
24575936Smckusickstatic int
24692839Simpisok(struct fstab *fs)
24766868Sadrian{
24875936Smckusick	int i;
24975936Smckusick
25066868Sadrian	if (fs->fs_passno == 0)
25175936Smckusick		return (0);
25266868Sadrian	if (BADTYPE(fs->fs_type))
25375936Smckusick		return (0);
25466868Sadrian	if (!selected(fs->fs_vfstype))
25575936Smckusick		return (0);
25675936Smckusick	/*
25775936Smckusick	 * If the -B flag has been given, then process the needed
25875936Smckusick	 * background checks. Background checks cannot be run on
259102231Strhodes	 * file systems that will be mounted read-only or that were
26075936Smckusick	 * not mounted at boot time (typically those marked `noauto').
261102231Strhodes	 * If these basic tests are passed, check with the file system
26275936Smckusick	 * itself to see if it is willing to do background checking
26375936Smckusick	 * by invoking its check program with the -F flag.
26475936Smckusick	 */
26575936Smckusick	if (flags & DO_BACKGRD) {
26675936Smckusick		if (!strcmp(fs->fs_type, FSTAB_RO))
26775936Smckusick			return (0);
26875936Smckusick		if (getmntpt(fs->fs_spec) == NULL)
26975936Smckusick			return (0);
27075936Smckusick		if (checkfs(fs->fs_vfstype, fs->fs_spec, fs->fs_file, "-F", 0))
27175936Smckusick			return (0);
27275936Smckusick		return (1);
27375936Smckusick	}
27475936Smckusick	/*
27575936Smckusick	 * If the -F flag has been given, then consider deferring the
27675936Smckusick	 * check to background. Background checks cannot be run on
277102231Strhodes	 * file systems that will be mounted read-only or that will
27875936Smckusick	 * not be mounted at boot time (e.g., marked `noauto'). If
279102231Strhodes	 * these basic tests are passed, check with the file system
28075936Smckusick	 * itself to see if it is willing to defer to background
28175936Smckusick	 * checking by invoking its check program with the -F flag.
28275936Smckusick	 */
28375936Smckusick	if ((flags & CHECK_BACKGRD) == 0 || !strcmp(fs->fs_type, FSTAB_RO))
28475936Smckusick		return (1);
28575936Smckusick	for (i = strlen(fs->fs_mntops) - 6; i >= 0; i--)
28675936Smckusick		if (!strncmp(&fs->fs_mntops[i], "noauto", 6))
28775936Smckusick			break;
28875936Smckusick	if (i >= 0)
28975936Smckusick		return (1);
29075936Smckusick	if (checkfs(fs->fs_vfstype, fs->fs_spec, fs->fs_file, "-F", NULL) != 0)
29175936Smckusick		return (1);
29275936Smckusick	printf("%s: DEFER FOR BACKGROUND CHECKING\n", fs->fs_spec);
29375936Smckusick	return (0);
29466868Sadrian}
29566868Sadrian
29666868Sadrian
29766868Sadrianstatic int
298113994Sjmallettcheckfs(const char *pvfstype, const char *spec, const char *mntpt,
299241807Suqs    const char *auxopt, pid_t *pidp)
30066868Sadrian{
301241807Suqs	const char ** volatile argv;
30266868Sadrian	pid_t pid;
30366868Sadrian	int argc, i, status, maxargc;
304117031Sgordon	char *optbuf, execbase[MAXPATHLEN];
305113994Sjmallett	char *vfstype = NULL;
30666868Sadrian	const char *extra = NULL;
30766868Sadrian
30866868Sadrian#ifdef __GNUC__
30966868Sadrian	/* Avoid vfork clobbering */
31066868Sadrian	(void) &optbuf;
31166868Sadrian	(void) &vfstype;
31266868Sadrian#endif
313113994Sjmallett	/*
314113994Sjmallett	 * We convert the vfstype to lowercase and any spaces to underscores
315113994Sjmallett	 * to not confuse the issue
316113994Sjmallett	 *
317113994Sjmallett	 * XXX This is a kludge to make automatic filesystem type guessing
318113994Sjmallett	 * from the disklabel work for "4.2BSD" filesystems.  It does a
319113994Sjmallett	 * very limited subset of transliteration to a normalised form of
320113994Sjmallett	 * filesystem name, and we do not seem to enforce a filesystem
321113994Sjmallett	 * name character set.
322113994Sjmallett	 */
323113994Sjmallett	vfstype = strdup(pvfstype);
324113994Sjmallett	if (vfstype == NULL)
325241806Suqs		perr("strdup(pvfstype)");
326241807Suqs	for (i = 0; i < (int)strlen(vfstype); i++) {
327113994Sjmallett		vfstype[i] = tolower(vfstype[i]);
328113994Sjmallett		if (vfstype[i] == ' ')
329113994Sjmallett			vfstype[i] = '_';
330113994Sjmallett	}
33166868Sadrian
33266868Sadrian	extra = getoptions(vfstype);
33366868Sadrian	optbuf = NULL;
33466868Sadrian	if (options)
33566868Sadrian		catopt(&optbuf, options);
33666868Sadrian	if (extra)
33766868Sadrian		catopt(&optbuf, extra);
33875936Smckusick	if (auxopt)
33975936Smckusick		catopt(&optbuf, auxopt);
34075936Smckusick	else if (flags & DO_BACKGRD)
34175936Smckusick		catopt(&optbuf, "-B");
34266868Sadrian
34366868Sadrian	maxargc = 64;
34466868Sadrian	argv = emalloc(sizeof(char *) * maxargc);
34566868Sadrian
34666868Sadrian	(void) snprintf(execbase, sizeof(execbase), "fsck_%s", vfstype);
34766868Sadrian	argc = 0;
34866868Sadrian	argv[argc++] = execbase;
34966868Sadrian	if (optbuf)
35066868Sadrian		mangle(optbuf, &argc, &argv, &maxargc);
35166868Sadrian	argv[argc++] = spec;
35266868Sadrian	argv[argc] = NULL;
35366868Sadrian
35466868Sadrian	if (flags & (CHECK_DEBUG|CHECK_VERBOSE)) {
35566868Sadrian		(void)printf("start %s %swait", mntpt,
35666868Sadrian			pidp ? "no" : "");
35766868Sadrian		for (i = 0; i < argc; i++)
35866868Sadrian			(void)printf(" %s", argv[i]);
35966868Sadrian		(void)printf("\n");
36066868Sadrian	}
36166868Sadrian
36266868Sadrian	switch (pid = vfork()) {
36366868Sadrian	case -1:				/* Error. */
36466868Sadrian		warn("vfork");
36566868Sadrian		if (optbuf)
36666868Sadrian			free(optbuf);
367113994Sjmallett		free(vfstype);
36866868Sadrian		return (1);
36966868Sadrian
37066868Sadrian	case 0:					/* Child. */
37175936Smckusick		if ((flags & CHECK_DEBUG) && auxopt == NULL)
37266868Sadrian			_exit(0);
37366868Sadrian
37466868Sadrian		/* Go find an executable. */
375241807Suqs		execvP(execbase, _PATH_SYSPATH, __DECONST(char * const *, argv));
376117031Sgordon		if (spec)
377117031Sgordon			warn("exec %s for %s in %s", execbase, spec, _PATH_SYSPATH);
378117031Sgordon		else
379117031Sgordon			warn("exec %s in %s", execbase, _PATH_SYSPATH);
38066868Sadrian		_exit(1);
38166868Sadrian		/* NOTREACHED */
38266868Sadrian
38366868Sadrian	default:				/* Parent. */
38466868Sadrian		if (optbuf)
38566868Sadrian			free(optbuf);
38666868Sadrian
387113994Sjmallett		free(vfstype);
388113994Sjmallett
38966868Sadrian		if (pidp) {
39066868Sadrian			*pidp = pid;
39166868Sadrian			return 0;
39266868Sadrian		}
39366868Sadrian
39466868Sadrian		if (waitpid(pid, &status, 0) < 0) {
39566868Sadrian			warn("waitpid");
39666868Sadrian			return (1);
39766868Sadrian		}
39866868Sadrian
39966868Sadrian		if (WIFEXITED(status)) {
40066868Sadrian			if (WEXITSTATUS(status) != 0)
40166868Sadrian				return (WEXITSTATUS(status));
40266868Sadrian		}
40366868Sadrian		else if (WIFSIGNALED(status)) {
40466868Sadrian			warnx("%s: %s", spec, strsignal(WTERMSIG(status)));
40566868Sadrian			return (1);
40666868Sadrian		}
40766868Sadrian		break;
40866868Sadrian	}
40966868Sadrian
41066868Sadrian	return (0);
41166868Sadrian}
41266868Sadrian
41366868Sadrian
41466868Sadrianstatic int
41592839Simpselected(const char *type)
41666868Sadrian{
41766868Sadrian	struct entry *e;
41866868Sadrian
41966868Sadrian	/* If no type specified, it's always selected. */
42070522Sphk	TAILQ_FOREACH(e, &selhead, entries)
42166868Sadrian		if (!strncmp(e->type, type, MFSNAMELEN))
42266868Sadrian			return which == IN_LIST ? 1 : 0;
42366868Sadrian
42466868Sadrian	return which == IN_LIST ? 0 : 1;
42566868Sadrian}
42666868Sadrian
42766868Sadrian
42866868Sadrianstatic const char *
42992839Simpgetoptions(const char *type)
43066868Sadrian{
43166868Sadrian	struct entry *e;
43266868Sadrian
43370522Sphk	TAILQ_FOREACH(e, &opthead, entries)
43466868Sadrian		if (!strncmp(e->type, type, MFSNAMELEN))
43566868Sadrian			return e->options;
43666868Sadrian	return "";
43766868Sadrian}
43866868Sadrian
43966868Sadrian
44066868Sadrianstatic void
44192839Simpaddoption(char *optstr)
44266868Sadrian{
44366868Sadrian	char *newoptions;
44466868Sadrian	struct entry *e;
44566868Sadrian
44666868Sadrian	if ((newoptions = strchr(optstr, ':')) == NULL)
44766868Sadrian		errx(1, "Invalid option string");
44866868Sadrian
44966868Sadrian	*newoptions++ = '\0';
45066868Sadrian
45170522Sphk	TAILQ_FOREACH(e, &opthead, entries)
45266868Sadrian		if (!strncmp(e->type, optstr, MFSNAMELEN)) {
45366868Sadrian			catopt(&e->options, newoptions);
45466868Sadrian			return;
45566868Sadrian		}
45666868Sadrian	addentry(&opthead, optstr, newoptions);
45766868Sadrian}
45866868Sadrian
45966868Sadrian
46066868Sadrianstatic void
46192839Simpaddentry(struct fstypelist *list, const char *type, const char *opts)
46266868Sadrian{
46366868Sadrian	struct entry *e;
46466868Sadrian
46566868Sadrian	e = emalloc(sizeof(struct entry));
46666868Sadrian	e->type = estrdup(type);
46766868Sadrian	e->options = estrdup(opts);
46866868Sadrian	TAILQ_INSERT_TAIL(list, e, entries);
46966868Sadrian}
47066868Sadrian
47166868Sadrian
47266868Sadrianstatic void
47392839Simpmaketypelist(char *fslist)
47466868Sadrian{
47566868Sadrian	char *ptr;
47666868Sadrian
47766868Sadrian	if ((fslist == NULL) || (fslist[0] == '\0'))
47866868Sadrian		errx(1, "empty type list");
47966868Sadrian
48066868Sadrian	if (fslist[0] == 'n' && fslist[1] == 'o') {
48166868Sadrian		fslist += 2;
48266868Sadrian		which = NOT_IN_LIST;
48366868Sadrian	}
48466868Sadrian	else
48566868Sadrian		which = IN_LIST;
48666868Sadrian
48766868Sadrian	while ((ptr = strsep(&fslist, ",")) != NULL)
48866868Sadrian		addentry(&selhead, ptr, "");
48966868Sadrian
49066868Sadrian}
49166868Sadrian
49266868Sadrian
49366868Sadrianstatic void
49492839Simpcatopt(char **sp, const char *o)
49566868Sadrian{
49666868Sadrian	char *s;
49766868Sadrian	size_t i, j;
49866868Sadrian
49966868Sadrian	s = *sp;
50066868Sadrian	if (s) {
50166868Sadrian		i = strlen(s);
50266868Sadrian		j = i + 1 + strlen(o) + 1;
50366868Sadrian		s = erealloc(s, j);
50466868Sadrian		(void)snprintf(s + i, j, ",%s", o);
50566868Sadrian	} else
50666868Sadrian		s = estrdup(o);
50766868Sadrian	*sp = s;
50866868Sadrian}
50966868Sadrian
51066868Sadrian
51166868Sadrianstatic void
512241807Suqsmangle(char *opts, int *argcp, const char ** volatile *argvp, int *maxargcp)
51366868Sadrian{
51466868Sadrian	char *p, *s;
51566868Sadrian	int argc, maxargc;
51666868Sadrian	const char **argv;
51766868Sadrian
51866868Sadrian	argc = *argcp;
51966868Sadrian	argv = *argvp;
52066868Sadrian	maxargc = *maxargcp;
52166868Sadrian
522241806Suqs	for (s = opts; (p = strsep(&s, ",")) != NULL;) {
52366868Sadrian		/* Always leave space for one more argument and the NULL. */
52466868Sadrian		if (argc >= maxargc - 3) {
52566868Sadrian			maxargc <<= 1;
52666868Sadrian			argv = erealloc(argv, maxargc * sizeof(char *));
52766868Sadrian		}
52866868Sadrian		if (*p != '\0')  {
52966868Sadrian			if (*p == '-') {
53066868Sadrian				argv[argc++] = p;
53166868Sadrian				p = strchr(p, '=');
53266868Sadrian				if (p) {
53366868Sadrian					*p = '\0';
53466868Sadrian					argv[argc++] = p+1;
53566868Sadrian				}
53666868Sadrian			} else {
53766868Sadrian				argv[argc++] = "-o";
53866868Sadrian				argv[argc++] = p;
53966868Sadrian			}
54066868Sadrian		}
54166868Sadrian	}
54266868Sadrian
54366868Sadrian	*argcp = argc;
54466868Sadrian	*argvp = argv;
54566868Sadrian	*maxargcp = maxargc;
54666868Sadrian}
54766868Sadrian
548241807Suqsstatic const char *
549274770Simpgetfstype(const char *str)
55066868Sadrian{
551274770Simp	struct diocgattr_arg attr;
552274770Simp	int fd, i;
55366868Sadrian
55466868Sadrian	if ((fd = open(str, O_RDONLY)) == -1)
55566868Sadrian		err(1, "cannot open `%s'", str);
55666868Sadrian
557274770Simp	strncpy(attr.name, "PART::type", sizeof(attr.name));
558274770Simp	memset(&attr.value, 0, sizeof(attr.value));
559274770Simp	attr.len = sizeof(attr.value);
560274770Simp	if (ioctl(fd, DIOCGATTR, &attr) == -1) {
561199582Snetchild		(void) close(fd);
562106254Sphk		return(NULL);
563199582Snetchild	}
56466868Sadrian	(void) close(fd);
565274770Simp	for (i = 0; ptype_map[i].ptype != NULL; i++)
566274770Simp		if (strstr(attr.value.str, ptype_map[i].ptype) != NULL)
567274770Simp			return (ptype_map[i].name);
568274770Simp	return (NULL);
56966868Sadrian}
57066868Sadrian
57166868Sadrian
57266868Sadrianstatic void
57392839Simpusage(void)
57466868Sadrian{
57566868Sadrian	static const char common[] =
576226711Ssobomax	    "[-Cdfnpvy] [-B | -F] [-T fstype:fsoptions] [-t fstype] [-c fstab]";
57766868Sadrian
578141611Sru	(void)fprintf(stderr, "usage: %s %s [special | node] ...\n",
57993102Smarkm	    getprogname(), common);
58066868Sadrian	exit(1);
58166868Sadrian}
582