1/*	$NetBSD: fsck.c,v 1.30 2003/08/07 10:04:15 agc Exp $	*/
2
3/*-
4 * SPDX-License-Identifier: BSD-3-Clause
5 *
6 * Copyright (c) 1996 Christos Zoulas. All rights reserved.
7 * Copyright (c) 1980, 1989, 1993, 1994
8 *	The Regents of the University of California.  All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 *    may be used to endorse or promote products derived from this software
20 *    without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 *
34 * From: @(#)mount.c	8.19 (Berkeley) 4/19/94
35 * From: $NetBSD: mount.c,v 1.24 1995/11/18 03:34:29 cgd Exp
36 * $NetBSD: fsck.c,v 1.30 2003/08/07 10:04:15 agc Exp $
37 */
38
39#include <sys/cdefs.h>
40__FBSDID("$FreeBSD$");
41
42#include <sys/param.h>
43#include <sys/mount.h>
44#include <sys/queue.h>
45#include <sys/wait.h>
46#include <sys/disk.h>
47#include <sys/ioctl.h>
48
49#include <ctype.h>
50#include <err.h>
51#include <fstab.h>
52#include <fcntl.h>
53#include <paths.h>
54#include <signal.h>
55#include <stdio.h>
56#include <stdlib.h>
57#include <string.h>
58#include <unistd.h>
59
60#include "fsutil.h"
61
62static enum { IN_LIST, NOT_IN_LIST } which = NOT_IN_LIST;
63
64static TAILQ_HEAD(fstypelist, entry) opthead, selhead;
65
66struct entry {
67	char *type;
68	char *options;
69	TAILQ_ENTRY(entry) entries;
70};
71
72static char *options = NULL;
73static int flags = 0;
74static int forceflag = 0;
75
76static int checkfs(const char *, const char *, const char *, const char *, pid_t *);
77static int selected(const char *);
78static void addoption(char *);
79static const char *getoptions(const char *);
80static void addentry(struct fstypelist *, const char *, const char *);
81static void maketypelist(char *);
82static void catopt(char **, const char *);
83static void mangle(char *, int *, const char ** volatile *, int *);
84static const char *getfstype(const char *);
85static void usage(void) __dead2;
86static int isok(struct fstab *);
87
88static struct {
89	const char *ptype;
90	const char *name;
91} ptype_map[] = {
92	{ "ufs",	"ffs" },
93	{ "ffs",	"ffs" },
94	{ "fat",	"msdosfs" },
95	{ "efi",	"msdosfs" },
96	{ NULL,		NULL },
97};
98
99int
100main(int argc, char *argv[])
101{
102	struct fstab *fs;
103	int i, rval = 0;
104	const char *vfstype = NULL;
105	char globopt[3];
106	const char *etc_fstab;
107
108	globopt[0] = '-';
109	globopt[2] = '\0';
110
111	TAILQ_INIT(&selhead);
112	TAILQ_INIT(&opthead);
113
114	etc_fstab = NULL;
115	while ((i = getopt(argc, argv, "BCdvpfFnyl:t:T:c:")) != -1)
116		switch (i) {
117		case 'B':
118			if (flags & CHECK_BACKGRD)
119				errx(1, "Cannot specify -B and -F.");
120			flags |= DO_BACKGRD;
121			break;
122
123		case 'd':
124			flags |= CHECK_DEBUG;
125			break;
126
127		case 'v':
128			flags |= CHECK_VERBOSE;
129			break;
130
131		case 'F':
132			if (flags & DO_BACKGRD)
133				errx(1, "Cannot specify -B and -F.");
134			flags |= CHECK_BACKGRD;
135			break;
136
137		case 'p':
138			flags |= CHECK_PREEN;
139			/*FALLTHROUGH*/
140		case 'C':
141			flags |= CHECK_CLEAN;
142			/*FALLTHROUGH*/
143		case 'n':
144		case 'y':
145			globopt[1] = i;
146			catopt(&options, globopt);
147			break;
148
149		case 'f':
150			forceflag = 1;
151			globopt[1] = i;
152			catopt(&options, globopt);
153			break;
154
155		case 'l':
156			warnx("Ignoring obsolete -l option\n");
157			break;
158
159		case 'T':
160			if (*optarg)
161				addoption(optarg);
162			break;
163
164		case 't':
165			if (!TAILQ_EMPTY(&selhead))
166				errx(1, "only one -t option may be specified.");
167
168			maketypelist(optarg);
169			vfstype = optarg;
170			break;
171
172		case 'c':
173			etc_fstab = optarg;
174			break;
175
176		case '?':
177		default:
178			usage();
179			/* NOTREACHED */
180		}
181
182	argc -= optind;
183	argv += optind;
184
185	if (etc_fstab != NULL)
186		setfstab(etc_fstab);
187
188	if (argc == 0)
189		return checkfstab(flags, isok, checkfs);
190
191#define	BADTYPE(type)							\
192	(strcmp(type, FSTAB_RO) &&					\
193	    strcmp(type, FSTAB_RW) && strcmp(type, FSTAB_RQ))
194
195
196	for (; argc--; argv++) {
197		const char *spec, *mntpt, *type, *cp;
198		char device[MAXPATHLEN];
199		struct statfs *mntp;
200
201		mntpt = NULL;
202		spec = *argv;
203		cp = strrchr(spec, '/');
204		if (cp == NULL) {
205			(void)snprintf(device, sizeof(device), "%s%s",
206				_PATH_DEV, spec);
207			spec = device;
208		}
209		mntp = getmntpt(spec);
210		if (mntp != NULL) {
211			spec = mntp->f_mntfromname;
212			mntpt = mntp->f_mntonname;
213		}
214		if ((fs = getfsfile(spec)) == NULL &&
215		    (fs = getfsspec(spec)) == NULL) {
216			if (vfstype == NULL)
217				vfstype = getfstype(spec);
218			if (vfstype == NULL)
219				vfstype = "ufs";
220			type = vfstype;
221			devcheck(spec);
222		} else {
223			spec = fs->fs_spec;
224			type = fs->fs_vfstype;
225			mntpt = fs->fs_file;
226			if (BADTYPE(fs->fs_type))
227				errx(1, "%s has unknown file system type.",
228				    spec);
229		}
230		if ((flags & CHECK_BACKGRD) &&
231		    checkfs(type, spec, mntpt, "-F", NULL) == 0) {
232			printf("%s: DEFER FOR BACKGROUND CHECKING\n", *argv);
233			continue;
234		}
235		if ((flags & DO_BACKGRD) && forceflag == 0 &&
236		    checkfs(type, spec, mntpt, "-F", NULL) != 0)
237			continue;
238
239		rval |= checkfs(type, spec, mntpt, NULL, NULL);
240	}
241
242	return rval;
243}
244
245
246static int
247isok(struct fstab *fs)
248{
249	int i;
250
251	if (fs->fs_passno == 0)
252		return (0);
253	if (BADTYPE(fs->fs_type))
254		return (0);
255	if (!selected(fs->fs_vfstype))
256		return (0);
257	/*
258	 * If the -B flag has been given, then process the needed
259	 * background checks. Background checks cannot be run on
260	 * file systems that will be mounted read-only or that were
261	 * not mounted at boot time (typically those marked `noauto').
262	 * If these basic tests are passed, check with the file system
263	 * itself to see if it is willing to do background checking
264	 * by invoking its check program with the -F flag.
265	 */
266	if (flags & DO_BACKGRD) {
267		if (!strcmp(fs->fs_type, FSTAB_RO))
268			return (0);
269		if (getmntpt(fs->fs_spec) == NULL)
270			return (0);
271		if (checkfs(fs->fs_vfstype, fs->fs_spec, fs->fs_file, "-F", 0))
272			return (0);
273		return (1);
274	}
275	/*
276	 * If the -F flag has been given, then consider deferring the
277	 * check to background. Background checks cannot be run on
278	 * file systems that will be mounted read-only or that will
279	 * not be mounted at boot time (e.g., marked `noauto'). If
280	 * these basic tests are passed, check with the file system
281	 * itself to see if it is willing to defer to background
282	 * checking by invoking its check program with the -F flag.
283	 */
284	if ((flags & CHECK_BACKGRD) == 0 || !strcmp(fs->fs_type, FSTAB_RO))
285		return (1);
286	for (i = strlen(fs->fs_mntops) - 6; i >= 0; i--)
287		if (!strncmp(&fs->fs_mntops[i], "noauto", 6))
288			break;
289	if (i >= 0)
290		return (1);
291	if (checkfs(fs->fs_vfstype, fs->fs_spec, fs->fs_file, "-F", NULL) != 0)
292		return (1);
293	printf("%s: DEFER FOR BACKGROUND CHECKING\n", fs->fs_spec);
294	return (0);
295}
296
297
298static int
299checkfs(const char *pvfstype, const char *spec, const char *mntpt,
300    const char *auxopt, pid_t *pidp)
301{
302	const char ** volatile argv;
303	pid_t pid;
304	int argc, i, status, maxargc;
305	char *optbuf, execbase[MAXPATHLEN];
306	char *vfstype = NULL;
307	const char *extra = NULL;
308
309#ifdef __GNUC__
310	/* Avoid vfork clobbering */
311	(void) &optbuf;
312	(void) &vfstype;
313#endif
314	/*
315	 * We convert the vfstype to lowercase and any spaces to underscores
316	 * to not confuse the issue
317	 *
318	 * XXX This is a kludge to make automatic filesystem type guessing
319	 * from the disklabel work for "4.2BSD" filesystems.  It does a
320	 * very limited subset of transliteration to a normalised form of
321	 * filesystem name, and we do not seem to enforce a filesystem
322	 * name character set.
323	 */
324	vfstype = strdup(pvfstype);
325	if (vfstype == NULL)
326		perr("strdup(pvfstype)");
327	for (i = 0; i < (int)strlen(vfstype); i++) {
328		vfstype[i] = tolower(vfstype[i]);
329		if (vfstype[i] == ' ')
330			vfstype[i] = '_';
331	}
332
333	extra = getoptions(vfstype);
334	optbuf = NULL;
335	if (options)
336		catopt(&optbuf, options);
337	if (extra)
338		catopt(&optbuf, extra);
339	if (auxopt)
340		catopt(&optbuf, auxopt);
341	else if (flags & DO_BACKGRD)
342		catopt(&optbuf, "-B");
343
344	maxargc = 64;
345	argv = emalloc(sizeof(char *) * maxargc);
346
347	(void) snprintf(execbase, sizeof(execbase), "fsck_%s", vfstype);
348	argc = 0;
349	argv[argc++] = execbase;
350	if (optbuf)
351		mangle(optbuf, &argc, &argv, &maxargc);
352	argv[argc++] = spec;
353	argv[argc] = NULL;
354
355	if (flags & (CHECK_DEBUG|CHECK_VERBOSE)) {
356		(void)printf("start %s %swait", mntpt,
357			pidp ? "no" : "");
358		for (i = 0; i < argc; i++)
359			(void)printf(" %s", argv[i]);
360		(void)printf("\n");
361	}
362
363	switch (pid = vfork()) {
364	case -1:				/* Error. */
365		warn("vfork");
366		if (optbuf)
367			free(optbuf);
368		free(vfstype);
369		return (1);
370
371	case 0:					/* Child. */
372		if ((flags & CHECK_DEBUG) && auxopt == NULL)
373			_exit(0);
374
375		/* Go find an executable. */
376		execvP(execbase, _PATH_SYSPATH, __DECONST(char * const *, argv));
377		if (spec)
378			warn("exec %s for %s in %s", execbase, spec, _PATH_SYSPATH);
379		else
380			warn("exec %s in %s", execbase, _PATH_SYSPATH);
381		_exit(1);
382		/* NOTREACHED */
383
384	default:				/* Parent. */
385		if (optbuf)
386			free(optbuf);
387
388		free(vfstype);
389
390		if (pidp) {
391			*pidp = pid;
392			return 0;
393		}
394
395		if (waitpid(pid, &status, 0) < 0) {
396			warn("waitpid");
397			return (1);
398		}
399
400		if (WIFEXITED(status)) {
401			if (WEXITSTATUS(status) != 0)
402				return (WEXITSTATUS(status));
403		}
404		else if (WIFSIGNALED(status)) {
405			warnx("%s: %s", spec, strsignal(WTERMSIG(status)));
406			return (1);
407		}
408		break;
409	}
410
411	return (0);
412}
413
414
415static int
416selected(const char *type)
417{
418	struct entry *e;
419
420	/* If no type specified, it's always selected. */
421	TAILQ_FOREACH(e, &selhead, entries)
422		if (!strncmp(e->type, type, MFSNAMELEN))
423			return which == IN_LIST ? 1 : 0;
424
425	return which == IN_LIST ? 0 : 1;
426}
427
428
429static const char *
430getoptions(const char *type)
431{
432	struct entry *e;
433
434	TAILQ_FOREACH(e, &opthead, entries)
435		if (!strncmp(e->type, type, MFSNAMELEN))
436			return e->options;
437	return "";
438}
439
440
441static void
442addoption(char *optstr)
443{
444	char *newoptions;
445	struct entry *e;
446
447	if ((newoptions = strchr(optstr, ':')) == NULL)
448		errx(1, "Invalid option string");
449
450	*newoptions++ = '\0';
451
452	TAILQ_FOREACH(e, &opthead, entries)
453		if (!strncmp(e->type, optstr, MFSNAMELEN)) {
454			catopt(&e->options, newoptions);
455			return;
456		}
457	addentry(&opthead, optstr, newoptions);
458}
459
460
461static void
462addentry(struct fstypelist *list, const char *type, const char *opts)
463{
464	struct entry *e;
465
466	e = emalloc(sizeof(struct entry));
467	e->type = estrdup(type);
468	e->options = estrdup(opts);
469	TAILQ_INSERT_TAIL(list, e, entries);
470}
471
472
473static void
474maketypelist(char *fslist)
475{
476	char *ptr;
477
478	if ((fslist == NULL) || (fslist[0] == '\0'))
479		errx(1, "empty type list");
480
481	if (fslist[0] == 'n' && fslist[1] == 'o') {
482		fslist += 2;
483		which = NOT_IN_LIST;
484	}
485	else
486		which = IN_LIST;
487
488	while ((ptr = strsep(&fslist, ",")) != NULL)
489		addentry(&selhead, ptr, "");
490
491}
492
493
494static void
495catopt(char **sp, const char *o)
496{
497	char *s;
498	size_t i, j;
499
500	s = *sp;
501	if (s) {
502		i = strlen(s);
503		j = i + 1 + strlen(o) + 1;
504		s = erealloc(s, j);
505		(void)snprintf(s + i, j, ",%s", o);
506	} else
507		s = estrdup(o);
508	*sp = s;
509}
510
511
512static void
513mangle(char *opts, int *argcp, const char ** volatile *argvp, int *maxargcp)
514{
515	char *p, *s;
516	int argc, maxargc;
517	const char **argv;
518
519	argc = *argcp;
520	argv = *argvp;
521	maxargc = *maxargcp;
522
523	for (s = opts; (p = strsep(&s, ",")) != NULL;) {
524		/* Always leave space for one more argument and the NULL. */
525		if (argc >= maxargc - 3) {
526			maxargc <<= 1;
527			argv = erealloc(argv, maxargc * sizeof(char *));
528		}
529		if (*p != '\0')  {
530			if (*p == '-') {
531				argv[argc++] = p;
532				p = strchr(p, '=');
533				if (p) {
534					*p = '\0';
535					argv[argc++] = p+1;
536				}
537			} else {
538				argv[argc++] = "-o";
539				argv[argc++] = p;
540			}
541		}
542	}
543
544	*argcp = argc;
545	*argvp = argv;
546	*maxargcp = maxargc;
547}
548
549static const char *
550getfstype(const char *str)
551{
552	struct diocgattr_arg attr;
553	int fd, i;
554
555	if ((fd = open(str, O_RDONLY)) == -1)
556		err(1, "cannot open `%s'", str);
557
558	strncpy(attr.name, "PART::type", sizeof(attr.name));
559	memset(&attr.value, 0, sizeof(attr.value));
560	attr.len = sizeof(attr.value);
561	if (ioctl(fd, DIOCGATTR, &attr) == -1) {
562		(void) close(fd);
563		return(NULL);
564	}
565	(void) close(fd);
566	for (i = 0; ptype_map[i].ptype != NULL; i++)
567		if (strstr(attr.value.str, ptype_map[i].ptype) != NULL)
568			return (ptype_map[i].name);
569	return (NULL);
570}
571
572
573static void
574usage(void)
575{
576	static const char common[] =
577	    "[-Cdfnpvy] [-B | -F] [-T fstype:fsoptions] [-t fstype] [-c fstab]";
578
579	(void)fprintf(stderr, "usage: %s %s [special | node] ...\n",
580	    getprogname(), common);
581	exit(1);
582}
583