xref: /netbsd-src/sbin/fsck/fsck.c (revision 19722ca2cf5ff9b0a3906f19a0ed7445635ccddc)
1*19722ca2Smlelstv /*	$NetBSD: fsck.c,v 1.53 2023/01/24 08:09:37 mlelstv Exp $	*/
2bf07c871Sagc 
3bf07c871Sagc /*
47570f6e8Schristos  * Copyright (c) 1996 Christos Zoulas. All rights reserved.
5bf07c871Sagc  * Copyright (c) 1980, 1989, 1993, 1994
6bf07c871Sagc  *	The Regents of the University of California.  All rights reserved.
7bf07c871Sagc  *
8bf07c871Sagc  * Redistribution and use in source and binary forms, with or without
9bf07c871Sagc  * modification, are permitted provided that the following conditions
10bf07c871Sagc  * are met:
11bf07c871Sagc  * 1. Redistributions of source code must retain the above copyright
12bf07c871Sagc  *    notice, this list of conditions and the following disclaimer.
13bf07c871Sagc  * 2. Redistributions in binary form must reproduce the above copyright
14bf07c871Sagc  *    notice, this list of conditions and the following disclaimer in the
15bf07c871Sagc  *    documentation and/or other materials provided with the distribution.
16bf07c871Sagc  * 3. Neither the name of the University nor the names of its contributors
17bf07c871Sagc  *    may be used to endorse or promote products derived from this software
18bf07c871Sagc  *    without specific prior written permission.
19bf07c871Sagc  *
20bf07c871Sagc  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21bf07c871Sagc  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22bf07c871Sagc  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23bf07c871Sagc  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24bf07c871Sagc  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25bf07c871Sagc  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26bf07c871Sagc  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27bf07c871Sagc  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28bf07c871Sagc  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29bf07c871Sagc  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30bf07c871Sagc  * SUCH DAMAGE.
31bf07c871Sagc  *
32bf07c871Sagc  * From: @(#)mount.c	8.19 (Berkeley) 4/19/94
33bf07c871Sagc  * From: NetBSD: mount.c,v 1.24 1995/11/18 03:34:29 cgd Exp
34bf07c871Sagc  *
35bf07c871Sagc  */
3680411b33Schristos 
3726ba0ba0Slukem #include <sys/cdefs.h>
3826ba0ba0Slukem #ifndef lint
39*19722ca2Smlelstv __RCSID("$NetBSD: fsck.c,v 1.53 2023/01/24 08:09:37 mlelstv Exp $");
4026ba0ba0Slukem #endif /* not lint */
4180411b33Schristos 
4280411b33Schristos #include <sys/param.h>
4380411b33Schristos #include <sys/mount.h>
4480411b33Schristos #include <sys/queue.h>
4580411b33Schristos #include <sys/wait.h>
467b57bf6dSchristos #define FSTYPENAMES
477b57bf6dSchristos #define FSCKNAMES
48ef92b0deSthorpej #include <sys/disk.h>
49468b0bc4Schristos #include <sys/disklabel.h>
50468b0bc4Schristos #include <sys/ioctl.h>
5180411b33Schristos 
5280411b33Schristos #include <err.h>
5380411b33Schristos #include <errno.h>
5480411b33Schristos #include <fstab.h>
55468b0bc4Schristos #include <fcntl.h>
566d094b5bSabs #include <paths.h>
5780411b33Schristos #include <signal.h>
5880411b33Schristos #include <stdio.h>
5980411b33Schristos #include <stdlib.h>
6080411b33Schristos #include <string.h>
6180411b33Schristos #include <unistd.h>
622a1607d0Schristos #include <util.h>
6380411b33Schristos 
6480411b33Schristos #include "pathnames.h"
657dfca760Schristos #include "fsutil.h"
66742b48d5Schristos #include "exitvalues.h"
6780411b33Schristos 
68fdf6ea6cSchristos static enum { IN_LIST, NOT_IN_LIST } which = NOT_IN_LIST;
6980411b33Schristos 
TAILQ_HEAD(fstypelist,entry)70639f5975Schristos TAILQ_HEAD(fstypelist, entry) opthead, selhead, omhead;
7180411b33Schristos 
7280411b33Schristos struct entry {
7380411b33Schristos 	char *type;
74fdf6ea6cSchristos 	char *options;
7580411b33Schristos 	TAILQ_ENTRY(entry) entries;
7680411b33Schristos };
7780411b33Schristos 
787dfca760Schristos static int maxrun = 0;
79fdf6ea6cSchristos static char *options = NULL;
807dfca760Schristos static int flags = 0;
8180411b33Schristos 
82f97f5096Slukem static int checkfs(const char *, const char *, const char *, void *, pid_t *);
83f97f5096Slukem static int selected(const char *);
84639f5975Schristos static int omitted(const char *);
85f97f5096Slukem static void addoption(char *);
86f97f5096Slukem static const char *getoptions(const char *);
87f97f5096Slukem static void addentry(struct fstypelist *, const char *, const char *);
88f97f5096Slukem static void maketypelist(char *);
89f97f5096Slukem static void catopt(char **, const char *);
9056641252Schristos static void mangle(char *, int *, const char ** volatile *, int *);
91f97f5096Slukem static const char *getfslab(const char *);
926923d07cSjoerg __dead static void usage(void);
93f97f5096Slukem static void *isok(struct fstab *);
9480411b33Schristos 
9580411b33Schristos int
main(int argc,char * argv[])96f97f5096Slukem main(int argc, char *argv[])
9780411b33Schristos {
9880411b33Schristos 	struct fstab *fs;
99742b48d5Schristos 	int i, rval;
10048ba78aaSmycroft 	const char *vfstype = NULL;
101c623b74bSchristos 	char globopt[3];
102742b48d5Schristos 	int ret = FSCK_EXIT_OK;
1035727faddSchristos 	char buf[MAXPATHLEN];
104c623b74bSchristos 
105c623b74bSchristos 	globopt[0] = '-';
106c623b74bSchristos 	globopt[2] = '\0';
10780411b33Schristos 
108fdf6ea6cSchristos 	TAILQ_INIT(&selhead);
109fdf6ea6cSchristos 	TAILQ_INIT(&opthead);
110639f5975Schristos 	TAILQ_INIT(&omhead);
111fdf6ea6cSchristos 
112639f5975Schristos 	while ((i = getopt(argc, argv, "dfl:nPpqT:t:vx:y")) != -1) {
11380411b33Schristos 		switch (i) {
11480411b33Schristos 		case 'd':
1157dfca760Schristos 			flags |= CHECK_DEBUG;
116e69ce3e4Sdsl 			continue;
117e69ce3e4Sdsl 
118e69ce3e4Sdsl 		case 'f':
119e69ce3e4Sdsl 			flags |= CHECK_FORCE;
12080411b33Schristos 			break;
12180411b33Schristos 
122e69ce3e4Sdsl 		case 'n':
1235f101a47Schristos 			flags |= CHECK_NOFIX;
12480411b33Schristos 			break;
12580411b33Schristos 
126c623b74bSchristos 		case 'p':
127c623b74bSchristos 			flags |= CHECK_PREEN;
128e69ce3e4Sdsl 			break;
129e69ce3e4Sdsl 
130a73c2bd5Schristos 		case 'P':
131a73c2bd5Schristos 			flags |= CHECK_PROGRESS;
132a73c2bd5Schristos 			break;
133a73c2bd5Schristos 
134e69ce3e4Sdsl 		case 'q':
135fdf6ea6cSchristos 			break;
136fdf6ea6cSchristos 
137fdf6ea6cSchristos 		case 'l':
138fdf6ea6cSchristos 			maxrun = atoi(optarg);
139e69ce3e4Sdsl 			continue;
14080411b33Schristos 
141fdf6ea6cSchristos 		case 'T':
14280411b33Schristos 			if (*optarg)
143fdf6ea6cSchristos 				addoption(optarg);
144e69ce3e4Sdsl 			continue;
14580411b33Schristos 
14680411b33Schristos 		case 't':
147dacdbbf6Slukem 			if (TAILQ_FIRST(&selhead) != NULL)
14880411b33Schristos 				errx(1, "only one -t option may be specified.");
149fdf6ea6cSchristos 
15080411b33Schristos 			maketypelist(optarg);
15180411b33Schristos 			vfstype = optarg;
152e69ce3e4Sdsl 			continue;
153e69ce3e4Sdsl 
154e69ce3e4Sdsl 		case 'v':
155e69ce3e4Sdsl 			flags |= CHECK_VERBOSE;
156e69ce3e4Sdsl 			continue;
157e69ce3e4Sdsl 
158639f5975Schristos 		case 'x':
159639f5975Schristos 			addentry(&omhead, optarg, "");
160626b19f8Shannken 			continue;
161639f5975Schristos 
162e69ce3e4Sdsl 		case 'y':
16380411b33Schristos 			break;
16480411b33Schristos 
16580411b33Schristos 		case '?':
16680411b33Schristos 		default:
16780411b33Schristos 			usage();
16880411b33Schristos 			/* NOTREACHED */
16980411b33Schristos 		}
17080411b33Schristos 
171e69ce3e4Sdsl 		/* Pass option to fsck_xxxfs */
172e69ce3e4Sdsl 		globopt[1] = i;
173e69ce3e4Sdsl 		catopt(&options, globopt);
174e69ce3e4Sdsl 	}
175e69ce3e4Sdsl 
176a73c2bd5Schristos 	/* Don't do progress meters if we're debugging. */
177a73c2bd5Schristos 	if (flags & CHECK_DEBUG)
178a73c2bd5Schristos 		flags &= ~CHECK_PROGRESS;
179a73c2bd5Schristos 
180a73c2bd5Schristos 	/*
181a73c2bd5Schristos 	 * If progress meters are being used, force max parallel to 1
182a73c2bd5Schristos 	 * so the progress meter outputs don't interfere with one another.
183a73c2bd5Schristos 	 */
184a73c2bd5Schristos 	if (flags & CHECK_PROGRESS)
185a73c2bd5Schristos 		maxrun = 1;
186a73c2bd5Schristos 
18780411b33Schristos 	argc -= optind;
18880411b33Schristos 	argv += optind;
18980411b33Schristos 
190fdf6ea6cSchristos 	if (argc == 0)
1917dfca760Schristos 		return checkfstab(flags, maxrun, isok, checkfs);
192fdf6ea6cSchristos 
19380411b33Schristos #define	BADTYPE(type)							\
19480411b33Schristos 	(strcmp(type, FSTAB_RO) &&					\
19580411b33Schristos 	    strcmp(type, FSTAB_RW) && strcmp(type, FSTAB_RQ))
19680411b33Schristos 
19780411b33Schristos 
19880411b33Schristos 	for (; argc--; argv++) {
199a62f667aSmlelstv 		const char *spec, *spec2, *mntpt, *type, *cp;
2006d094b5bSabs 		char	device[MAXPATHLEN];
20180411b33Schristos 
202a62f667aSmlelstv 		spec = mntpt = *argv;
203a62f667aSmlelstv 		spec2 = getfsspecname(buf, sizeof(buf), spec);
204a62f667aSmlelstv 		if (spec2 == NULL)
205a62f667aSmlelstv 			spec2 = spec;
206a62f667aSmlelstv 
207a62f667aSmlelstv 		cp = strrchr(spec2, '/');
2086d094b5bSabs 		if (cp == 0) {
2096d094b5bSabs 			(void)snprintf(device, sizeof(device), "%s%s",
210a62f667aSmlelstv 				_PATH_DEV, spec2);
211a62f667aSmlelstv 			spec2 = device;
2126d094b5bSabs 		}
213a62f667aSmlelstv 
214a62f667aSmlelstv 		fs = getfsfile(spec);
215a62f667aSmlelstv 		if (fs == NULL)
216a62f667aSmlelstv 		    fs = getfsspec(spec);
217a62f667aSmlelstv 		if (fs == NULL && spec != spec2) {
218a62f667aSmlelstv 		    fs = getfsspec(spec2);
219a62f667aSmlelstv 		    spec = spec2;
22080411b33Schristos 		}
221a62f667aSmlelstv 
222a62f667aSmlelstv 		if (fs) {
2235727faddSchristos 			spec = getfsspecname(buf, sizeof(buf), fs->fs_spec);
2245727faddSchristos 			if (spec == NULL)
2255727faddSchristos 				err(FSCK_EXIT_CHECK_FAILED, "%s", buf);
22680411b33Schristos 			type = fs->fs_vfstype;
22780411b33Schristos 			if (BADTYPE(fs->fs_type))
228742b48d5Schristos 				errx(FSCK_EXIT_CHECK_FAILED,
229742b48d5Schristos 				    "%s has unknown file system type.",
2306d094b5bSabs 				    spec);
231a62f667aSmlelstv 		} else {
232a62f667aSmlelstv 			if (vfstype == NULL)
233a62f667aSmlelstv 				vfstype = getfslab(spec);
234a62f667aSmlelstv 			type = vfstype;
23580411b33Schristos 		}
23680411b33Schristos 
237742b48d5Schristos 		rval = checkfs(type, blockcheck(spec), *argv, NULL, NULL);
238742b48d5Schristos 		if (rval > ret)
239742b48d5Schristos 			ret = rval;
24080411b33Schristos 	}
24180411b33Schristos 
242742b48d5Schristos 	return ret;
24380411b33Schristos }
24480411b33Schristos 
245fdf6ea6cSchristos 
2467dfca760Schristos static void *
isok(struct fstab * fs)247f97f5096Slukem isok(struct fstab *fs)
248fdf6ea6cSchristos {
249f97f5096Slukem 
250fdf6ea6cSchristos 	if (fs->fs_passno == 0)
2517dfca760Schristos 		return NULL;
252fdf6ea6cSchristos 
253fdf6ea6cSchristos 	if (BADTYPE(fs->fs_type))
2547dfca760Schristos 		return NULL;
255fdf6ea6cSchristos 
256fdf6ea6cSchristos 	if (!selected(fs->fs_vfstype))
2577dfca760Schristos 		return NULL;
258fdf6ea6cSchristos 
259639f5975Schristos 	if (omitted(fs->fs_file))
260639f5975Schristos 		return NULL;
261639f5975Schristos 
2627dfca760Schristos 	return fs;
263fdf6ea6cSchristos }
264fdf6ea6cSchristos 
265fdf6ea6cSchristos 
266fdf6ea6cSchristos static int
checkfs(const char * vfst,const char * spec,const char * mntpt,void * auxarg,pid_t * pidp)267df99a4ddSchristos checkfs(const char *vfst, const char *spec, const char *mntpt, void *auxarg,
268f97f5096Slukem     pid_t *pidp)
26980411b33Schristos {
27080411b33Schristos 	/* List of directories containing fsck_xxx subcommands. */
27180411b33Schristos 	static const char *edirs[] = {
272ea369b96Schristos #ifdef RESCUEDIR
273ea369b96Schristos 		RESCUEDIR,
2748f87d646Slukem #endif
27580411b33Schristos 		_PATH_SBIN,
27680411b33Schristos 		_PATH_USRSBIN,
27780411b33Schristos 		NULL
27880411b33Schristos 	};
27956641252Schristos 	const char ** volatile argv, **edir;
280df99a4ddSchristos 	const char * volatile vfstype = vfst;
28180411b33Schristos 	pid_t pid;
282e49dfa57Smycroft 	int argc, i, status, maxargc;
283df99a4ddSchristos 	char *optb;
284df99a4ddSchristos 	char *volatile optbuf;
285df99a4ddSchristos 	char execname[MAXPATHLEN + 1], execbase[MAXPATHLEN];
286fdf6ea6cSchristos 	const char *extra = getoptions(vfstype);
28780411b33Schristos 
288e49dfa57Smycroft 	if (!strcmp(vfstype, "ufs"))
28946243af4Scgd 		vfstype = MOUNT_UFS;
29046243af4Scgd 
291df99a4ddSchristos 	optb = NULL;
2923cd66a15Smycroft 	if (options)
293df99a4ddSchristos 		catopt(&optb, options);
2943cd66a15Smycroft 	if (extra)
295df99a4ddSchristos 		catopt(&optb, extra);
296df99a4ddSchristos 	optbuf = optb;
297fdf6ea6cSchristos 
298e49dfa57Smycroft 	maxargc = 64;
299e49dfa57Smycroft 	argv = emalloc(sizeof(char *) * maxargc);
300e49dfa57Smycroft 
301e49dfa57Smycroft 	(void) snprintf(execbase, sizeof(execbase), "fsck_%s", vfstype);
302e49dfa57Smycroft 	argc = 0;
303e49dfa57Smycroft 	argv[argc++] = execbase;
304fdf6ea6cSchristos 	if (optbuf)
305c623b74bSchristos 		mangle(optbuf, &argc, &argv, &maxargc);
30680411b33Schristos 	argv[argc++] = spec;
307c623b74bSchristos 	argv[argc] = NULL;
30880411b33Schristos 
3097dfca760Schristos 	if (flags & (CHECK_DEBUG|CHECK_VERBOSE)) {
310468b0bc4Schristos 		(void)printf("start %s %swait", mntpt,
311468b0bc4Schristos 			pidp ? "no" : "");
312468b0bc4Schristos 		for (i = 0; i < argc; i++)
31380411b33Schristos 			(void)printf(" %s", argv[i]);
31480411b33Schristos 		(void)printf("\n");
31580411b33Schristos 	}
31680411b33Schristos 
31780411b33Schristos 	switch (pid = vfork()) {
31880411b33Schristos 	case -1:				/* Error. */
31980411b33Schristos 		warn("vfork");
32080411b33Schristos 		if (optbuf)
32180411b33Schristos 			free(optbuf);
322635d7297Schristos 		free(argv);
323742b48d5Schristos 		return FSCK_EXIT_CHECK_FAILED;
32480411b33Schristos 
32580411b33Schristos 	case 0:					/* Child. */
326172a636bSlukem 		if ((flags & CHECK_FORCE) == 0) {
3276bd1d6d4Schristos 			struct statvfs	sfs;
328172a636bSlukem 
329172a636bSlukem 				/*
330172a636bSlukem 				 * if mntpt is a mountpoint of a mounted file
331172a636bSlukem 				 * system and it's mounted read-write, skip it
332172a636bSlukem 				 * unless -f is given.
333172a636bSlukem 				 */
3346bd1d6d4Schristos 			if ((statvfs(mntpt, &sfs) == 0) &&
335172a636bSlukem 			    (strcmp(mntpt, sfs.f_mntonname) == 0) &&
3366bd1d6d4Schristos 			    ((sfs.f_flag & MNT_RDONLY) == 0)) {
337172a636bSlukem 				printf(
338172a636bSlukem 		"%s: file system is mounted read-write on %s; not checking\n",
339172a636bSlukem 				    spec, mntpt);
340172a636bSlukem 				if ((flags & CHECK_PREEN) && auxarg != NULL)
341742b48d5Schristos 					_exit(FSCK_EXIT_OK);	/* fsck -p */
342172a636bSlukem 				else
343742b48d5Schristos 					_exit(FSCK_EXIT_CHECK_FAILED);	/* fsck [[-p] ...] */
344172a636bSlukem 			}
345172a636bSlukem 		}
346172a636bSlukem 
3477dfca760Schristos 		if (flags & CHECK_DEBUG)
348742b48d5Schristos 			_exit(FSCK_EXIT_OK);
349fdf6ea6cSchristos 
35080411b33Schristos 		/* Go find an executable. */
35180411b33Schristos 		edir = edirs;
35280411b33Schristos 		do {
35380411b33Schristos 			(void)snprintf(execname,
354468b0bc4Schristos 			    sizeof(execname), "%s/%s", *edir, execbase);
3552c6eadc9Schristos 			execv(execname, (char * const *)__UNCONST(argv));
356029a64ccSross 			if (errno != ENOENT) {
35780411b33Schristos 				if (spec)
35880411b33Schristos 					warn("exec %s for %s", execname, spec);
35980411b33Schristos 				else
36080411b33Schristos 					warn("exec %s", execname);
361029a64ccSross 			}
36280411b33Schristos 		} while (*++edir != NULL);
36380411b33Schristos 
364029a64ccSross 		if (errno == ENOENT) {
36580411b33Schristos 			if (spec)
36680411b33Schristos 				warn("exec %s for %s", execname, spec);
36780411b33Schristos 			else
36880411b33Schristos 				warn("exec %s", execname);
369029a64ccSross 		}
370742b48d5Schristos 		_exit(FSCK_EXIT_CHECK_FAILED);
37180411b33Schristos 		/* NOTREACHED */
37280411b33Schristos 
37380411b33Schristos 	default:				/* Parent. */
37480411b33Schristos 		if (optbuf)
37580411b33Schristos 			free(optbuf);
376635d7297Schristos 		free(argv);
37780411b33Schristos 
378fdf6ea6cSchristos 		if (pidp) {
379fdf6ea6cSchristos 			*pidp = pid;
380742b48d5Schristos 			return FSCK_EXIT_OK;
381fdf6ea6cSchristos 		}
382fdf6ea6cSchristos 
38380411b33Schristos 		if (waitpid(pid, &status, 0) < 0) {
38480411b33Schristos 			warn("waitpid");
385742b48d5Schristos 			return FSCK_EXIT_CHECK_FAILED;
38680411b33Schristos 		}
38780411b33Schristos 
38880411b33Schristos 		if (WIFEXITED(status)) {
38980411b33Schristos 			if (WEXITSTATUS(status) != 0)
390742b48d5Schristos 				return WEXITSTATUS(status);
39180411b33Schristos 		}
39280411b33Schristos 		else if (WIFSIGNALED(status)) {
39380411b33Schristos 			warnx("%s: %s", spec, strsignal(WTERMSIG(status)));
394742b48d5Schristos 			return FSCK_EXIT_CHECK_FAILED;
39580411b33Schristos 		}
39680411b33Schristos 		break;
39780411b33Schristos 	}
39880411b33Schristos 
399742b48d5Schristos 	return FSCK_EXIT_OK;
40080411b33Schristos }
40180411b33Schristos 
40280411b33Schristos 
40380411b33Schristos static int
selected(const char * type)404f97f5096Slukem selected(const char *type)
40580411b33Schristos {
40680411b33Schristos 	struct entry *e;
40780411b33Schristos 
40880411b33Schristos 	/* If no type specified, it's always selected. */
409dacdbbf6Slukem 	TAILQ_FOREACH(e, &selhead, entries)
410fede0a77Schristos 		if (!strcmp(e->type, type))
41180411b33Schristos 			return which == IN_LIST ? 1 : 0;
41280411b33Schristos 
41380411b33Schristos 	return which == IN_LIST ? 0 : 1;
41480411b33Schristos }
41580411b33Schristos 
41680411b33Schristos 
417639f5975Schristos static int
omitted(const char * mountedon)418639f5975Schristos omitted(const char *mountedon)
419639f5975Schristos {
420639f5975Schristos 	struct entry *e;
421639f5975Schristos 
422639f5975Schristos 	/* If no type specified, it's always selected. */
423639f5975Schristos 	TAILQ_FOREACH(e, &omhead, entries)
424639f5975Schristos 		if (!strcmp(e->type, mountedon))
425639f5975Schristos 			return 1;
426639f5975Schristos 
427639f5975Schristos 	return 0;
428639f5975Schristos }
429639f5975Schristos 
430639f5975Schristos 
431fdf6ea6cSchristos static const char *
getoptions(const char * type)432f97f5096Slukem getoptions(const char *type)
43380411b33Schristos {
43480411b33Schristos 	struct entry *e;
43580411b33Schristos 
436dacdbbf6Slukem 	TAILQ_FOREACH(e, &opthead, entries)
437fede0a77Schristos 		if (!strcmp(e->type, type))
438fdf6ea6cSchristos 			return e->options;
439fdf6ea6cSchristos 	return "";
440fdf6ea6cSchristos }
441fdf6ea6cSchristos 
442fdf6ea6cSchristos 
443fdf6ea6cSchristos static void
addoption(char * optstr)444f97f5096Slukem addoption(char *optstr)
445fdf6ea6cSchristos {
446fdf6ea6cSchristos 	char *newoptions;
447fdf6ea6cSchristos 	struct entry *e;
448fdf6ea6cSchristos 
449fdf6ea6cSchristos 	if ((newoptions = strchr(optstr, ':')) == NULL)
450fdf6ea6cSchristos 		errx(1, "Invalid option string");
451fdf6ea6cSchristos 
452fdf6ea6cSchristos 	*newoptions++ = '\0';
453fdf6ea6cSchristos 
454dacdbbf6Slukem 	TAILQ_FOREACH(e, &opthead, entries)
455fede0a77Schristos 		if (!strcmp(e->type, optstr)) {
4563cd66a15Smycroft 			catopt(&e->options, newoptions);
457fdf6ea6cSchristos 			return;
458fdf6ea6cSchristos 		}
459fdf6ea6cSchristos 	addentry(&opthead, optstr, newoptions);
460fdf6ea6cSchristos }
461fdf6ea6cSchristos 
462fdf6ea6cSchristos 
463fdf6ea6cSchristos static void
addentry(struct fstypelist * list,const char * type,const char * opts)464f97f5096Slukem addentry(struct fstypelist *list, const char *type, const char *opts)
465fdf6ea6cSchristos {
466fdf6ea6cSchristos 	struct entry *e;
467fdf6ea6cSchristos 
46880411b33Schristos 	e = emalloc(sizeof(struct entry));
46980411b33Schristos 	e->type = estrdup(type);
470fdf6ea6cSchristos 	e->options = estrdup(opts);
471fdf6ea6cSchristos 	TAILQ_INSERT_TAIL(list, e, entries);
47280411b33Schristos }
47380411b33Schristos 
47480411b33Schristos 
47580411b33Schristos static void
maketypelist(char * fslist)476f97f5096Slukem maketypelist(char *fslist)
47780411b33Schristos {
47880411b33Schristos 	char *ptr;
47980411b33Schristos 
48080411b33Schristos 	if ((fslist == NULL) || (fslist[0] == '\0'))
48180411b33Schristos 		errx(1, "empty type list");
48280411b33Schristos 
48380411b33Schristos 	if (fslist[0] == 'n' && fslist[1] == 'o') {
48480411b33Schristos 		fslist += 2;
48580411b33Schristos 		which = NOT_IN_LIST;
48680411b33Schristos 	}
48780411b33Schristos 	else
48880411b33Schristos 		which = IN_LIST;
48980411b33Schristos 
49080411b33Schristos 	while ((ptr = strsep(&fslist, ",")) != NULL)
491fdf6ea6cSchristos 		addentry(&selhead, ptr, "");
49280411b33Schristos 
49380411b33Schristos }
49480411b33Schristos 
49580411b33Schristos 
4963cd66a15Smycroft static void
catopt(char ** sp,const char * o)497f97f5096Slukem catopt(char **sp, const char *o)
49880411b33Schristos {
4993cd66a15Smycroft 	char *s;
5003cd66a15Smycroft 	size_t i, j;
50180411b33Schristos 
5023cd66a15Smycroft 	s = *sp;
5033cd66a15Smycroft 	if (s) {
5043cd66a15Smycroft 		i = strlen(s);
5053cd66a15Smycroft 		j = i + 1 + strlen(o) + 1;
5063cd66a15Smycroft 		s = erealloc(s, j);
5073cd66a15Smycroft 		(void)snprintf(s + i, j, ",%s", o);
508e49dfa57Smycroft 	} else
5093cd66a15Smycroft 		s = estrdup(o);
5103cd66a15Smycroft 	*sp = s;
51180411b33Schristos }
51280411b33Schristos 
51380411b33Schristos 
51480411b33Schristos static void
mangle(char * opts,int * argcp,const char ** volatile * argvp,int * maxargcp)51556641252Schristos mangle(char *opts, int *argcp, const char ** volatile *argvp, int *maxargcp)
51680411b33Schristos {
51780411b33Schristos 	char *p, *s;
518e49dfa57Smycroft 	int argc, maxargc;
519e49dfa57Smycroft 	const char **argv;
52080411b33Schristos 
52180411b33Schristos 	argc = *argcp;
522e49dfa57Smycroft 	argv = *argvp;
523c623b74bSchristos 	maxargc = *maxargcp;
524c623b74bSchristos 
5254475e174Slukem 	for (s = opts; (p = strsep(&s, ",")) != NULL;) {
526e49dfa57Smycroft 		/* Always leave space for one more argument and the NULL. */
527c623b74bSchristos 		if (argc >= maxargc - 3) {
528e49dfa57Smycroft 			maxargc <<= 1;
529c623b74bSchristos 			argv = erealloc(argv, maxargc * sizeof(char *));
530c623b74bSchristos 		}
531029a64ccSross 		if (*p != '\0')  {
53280411b33Schristos 			if (*p == '-') {
53380411b33Schristos 				argv[argc++] = p;
53480411b33Schristos 				p = strchr(p, '=');
53580411b33Schristos 				if (p) {
53680411b33Schristos 					*p = '\0';
53780411b33Schristos 					argv[argc++] = p+1;
53880411b33Schristos 				}
539e49dfa57Smycroft 			} else {
54080411b33Schristos 				argv[argc++] = "-o";
54180411b33Schristos 				argv[argc++] = p;
54280411b33Schristos 			}
543c623b74bSchristos 		}
544029a64ccSross 	}
54580411b33Schristos 
54680411b33Schristos 	*argcp = argc;
547c623b74bSchristos 	*argvp = argv;
548c623b74bSchristos 	*maxargcp = maxargc;
54980411b33Schristos }
55080411b33Schristos 
55156641252Schristos static const char *
getfslab(const char * str)552f97f5096Slukem getfslab(const char *str)
553468b0bc4Schristos {
554ef92b0deSthorpej 	static struct dkwedge_info dkw;
555468b0bc4Schristos 	struct disklabel dl;
556468b0bc4Schristos 	int fd;
55748ba78aaSmycroft 	char p;
55848ba78aaSmycroft 	const char *vfstype;
559468b0bc4Schristos 	u_char t;
560*19722ca2Smlelstv 	char buf[MAXPATHLEN];
561468b0bc4Schristos 
562468b0bc4Schristos 	/* deduce the file system type from the disk label */
563*19722ca2Smlelstv         if ((fd = opendisk(str, O_RDONLY, buf, sizeof(buf), 0)) == -1)
564*19722ca2Smlelstv 		err(1, "cannot open `%s'", buf);
565468b0bc4Schristos 
566ef92b0deSthorpej 	/* First check to see if it's a wedge. */
567ef92b0deSthorpej 	if (ioctl(fd, DIOCGWEDGEINFO, &dkw) == 0) {
568ef92b0deSthorpej 		/* Yup, this is easy. */
569ef92b0deSthorpej 		(void) close(fd);
570ef92b0deSthorpej 		return (dkw.dkw_ptype);
571ef92b0deSthorpej 	}
572ef92b0deSthorpej 
573468b0bc4Schristos 	if (ioctl(fd, DIOCGDINFO, &dl) == -1)
574*19722ca2Smlelstv 		err(1, "cannot get disklabel for `%s'", buf);
575468b0bc4Schristos 
576468b0bc4Schristos 	(void) close(fd);
577468b0bc4Schristos 
578*19722ca2Smlelstv 	p = buf[strlen(buf) - 1];
579468b0bc4Schristos 
580468b0bc4Schristos 	if ((p - 'a') >= dl.d_npartitions)
581*19722ca2Smlelstv 		errx(1, "partition `%s' is not defined on disk", buf);
582468b0bc4Schristos 
5837cdf2a0dSbouyer 	if ((t = dl.d_partitions[p - 'a'].p_fstype) >= FSMAXTYPES)
584468b0bc4Schristos 		errx(1, "partition `%s' is not of a legal vfstype",
585*19722ca2Smlelstv 		    buf);
586468b0bc4Schristos 
587468b0bc4Schristos 	if ((vfstype = fscknames[t]) == NULL)
588468b0bc4Schristos 		errx(1, "vfstype `%s' on partition `%s' is not supported",
589*19722ca2Smlelstv 		    fstypenames[t], buf);
590468b0bc4Schristos 
591468b0bc4Schristos 	return vfstype;
592468b0bc4Schristos }
593468b0bc4Schristos 
594468b0bc4Schristos 
59580411b33Schristos static void
usage(void)596f97f5096Slukem usage(void)
59780411b33Schristos {
598fdf6ea6cSchristos 	static const char common[] =
599639f5975Schristos 	    "[-dfnPpqvy] [-x excludemount] [-l maxparallel] [-T fstype:fsoptions]\n\t\t[-t fstype]";
60080411b33Schristos 
601b635f565Sjmmv 	(void)fprintf(stderr, "usage: %s %s [special|node]...\n",
6028a986b2eScgd 	    getprogname(), common);
603742b48d5Schristos 	exit(FSCK_EXIT_USAGE);
60480411b33Schristos }
605