1/*	$NetBSD: fsck.c,v 1.53 2023/01/24 08:09:37 mlelstv Exp $	*/
2
3/*
4 * Copyright (c) 1996 Christos Zoulas. All rights reserved.
5 * Copyright (c) 1980, 1989, 1993, 1994
6 *	The Regents of the University of California.  All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 * From: @(#)mount.c	8.19 (Berkeley) 4/19/94
33 * From: NetBSD: mount.c,v 1.24 1995/11/18 03:34:29 cgd Exp
34 *
35 */
36
37#include <sys/cdefs.h>
38#ifndef lint
39__RCSID("$NetBSD: fsck.c,v 1.53 2023/01/24 08:09:37 mlelstv Exp $");
40#endif /* not lint */
41
42#include <sys/param.h>
43#include <sys/mount.h>
44#include <sys/queue.h>
45#include <sys/wait.h>
46#define FSTYPENAMES
47#define FSCKNAMES
48#include <sys/disk.h>
49#include <sys/disklabel.h>
50#include <sys/ioctl.h>
51
52#include <err.h>
53#include <errno.h>
54#include <fstab.h>
55#include <fcntl.h>
56#include <paths.h>
57#include <signal.h>
58#include <stdio.h>
59#include <stdlib.h>
60#include <string.h>
61#include <unistd.h>
62#include <util.h>
63
64#include "pathnames.h"
65#include "fsutil.h"
66#include "exitvalues.h"
67
68static enum { IN_LIST, NOT_IN_LIST } which = NOT_IN_LIST;
69
70TAILQ_HEAD(fstypelist, entry) opthead, selhead, omhead;
71
72struct entry {
73	char *type;
74	char *options;
75	TAILQ_ENTRY(entry) entries;
76};
77
78static int maxrun = 0;
79static char *options = NULL;
80static int flags = 0;
81
82static int checkfs(const char *, const char *, const char *, void *, pid_t *);
83static int selected(const char *);
84static int omitted(const char *);
85static void addoption(char *);
86static const char *getoptions(const char *);
87static void addentry(struct fstypelist *, const char *, const char *);
88static void maketypelist(char *);
89static void catopt(char **, const char *);
90static void mangle(char *, int *, const char ** volatile *, int *);
91static const char *getfslab(const char *);
92__dead static void usage(void);
93static void *isok(struct fstab *);
94
95int
96main(int argc, char *argv[])
97{
98	struct fstab *fs;
99	int i, rval;
100	const char *vfstype = NULL;
101	char globopt[3];
102	int ret = FSCK_EXIT_OK;
103	char buf[MAXPATHLEN];
104
105	globopt[0] = '-';
106	globopt[2] = '\0';
107
108	TAILQ_INIT(&selhead);
109	TAILQ_INIT(&opthead);
110	TAILQ_INIT(&omhead);
111
112	while ((i = getopt(argc, argv, "dfl:nPpqT:t:vx:y")) != -1) {
113		switch (i) {
114		case 'd':
115			flags |= CHECK_DEBUG;
116			continue;
117
118		case 'f':
119			flags |= CHECK_FORCE;
120			break;
121
122		case 'n':
123			flags |= CHECK_NOFIX;
124			break;
125
126		case 'p':
127			flags |= CHECK_PREEN;
128			break;
129
130		case 'P':
131			flags |= CHECK_PROGRESS;
132			break;
133
134		case 'q':
135			break;
136
137		case 'l':
138			maxrun = atoi(optarg);
139			continue;
140
141		case 'T':
142			if (*optarg)
143				addoption(optarg);
144			continue;
145
146		case 't':
147			if (TAILQ_FIRST(&selhead) != NULL)
148				errx(1, "only one -t option may be specified.");
149
150			maketypelist(optarg);
151			vfstype = optarg;
152			continue;
153
154		case 'v':
155			flags |= CHECK_VERBOSE;
156			continue;
157
158		case 'x':
159			addentry(&omhead, optarg, "");
160			continue;
161
162		case 'y':
163			break;
164
165		case '?':
166		default:
167			usage();
168			/* NOTREACHED */
169		}
170
171		/* Pass option to fsck_xxxfs */
172		globopt[1] = i;
173		catopt(&options, globopt);
174	}
175
176	/* Don't do progress meters if we're debugging. */
177	if (flags & CHECK_DEBUG)
178		flags &= ~CHECK_PROGRESS;
179
180	/*
181	 * If progress meters are being used, force max parallel to 1
182	 * so the progress meter outputs don't interfere with one another.
183	 */
184	if (flags & CHECK_PROGRESS)
185		maxrun = 1;
186
187	argc -= optind;
188	argv += optind;
189
190	if (argc == 0)
191		return checkfstab(flags, maxrun, isok, checkfs);
192
193#define	BADTYPE(type)							\
194	(strcmp(type, FSTAB_RO) &&					\
195	    strcmp(type, FSTAB_RW) && strcmp(type, FSTAB_RQ))
196
197
198	for (; argc--; argv++) {
199		const char *spec, *spec2, *mntpt, *type, *cp;
200		char	device[MAXPATHLEN];
201
202		spec = mntpt = *argv;
203		spec2 = getfsspecname(buf, sizeof(buf), spec);
204		if (spec2 == NULL)
205			spec2 = spec;
206
207		cp = strrchr(spec2, '/');
208		if (cp == 0) {
209			(void)snprintf(device, sizeof(device), "%s%s",
210				_PATH_DEV, spec2);
211			spec2 = device;
212		}
213
214		fs = getfsfile(spec);
215		if (fs == NULL)
216		    fs = getfsspec(spec);
217		if (fs == NULL && spec != spec2) {
218		    fs = getfsspec(spec2);
219		    spec = spec2;
220		}
221
222		if (fs) {
223			spec = getfsspecname(buf, sizeof(buf), fs->fs_spec);
224			if (spec == NULL)
225				err(FSCK_EXIT_CHECK_FAILED, "%s", buf);
226			type = fs->fs_vfstype;
227			if (BADTYPE(fs->fs_type))
228				errx(FSCK_EXIT_CHECK_FAILED,
229				    "%s has unknown file system type.",
230				    spec);
231		} else {
232			if (vfstype == NULL)
233				vfstype = getfslab(spec);
234			type = vfstype;
235		}
236
237		rval = checkfs(type, blockcheck(spec), *argv, NULL, NULL);
238		if (rval > ret)
239			ret = rval;
240	}
241
242	return ret;
243}
244
245
246static void *
247isok(struct fstab *fs)
248{
249
250	if (fs->fs_passno == 0)
251		return NULL;
252
253	if (BADTYPE(fs->fs_type))
254		return NULL;
255
256	if (!selected(fs->fs_vfstype))
257		return NULL;
258
259	if (omitted(fs->fs_file))
260		return NULL;
261
262	return fs;
263}
264
265
266static int
267checkfs(const char *vfst, const char *spec, const char *mntpt, void *auxarg,
268    pid_t *pidp)
269{
270	/* List of directories containing fsck_xxx subcommands. */
271	static const char *edirs[] = {
272#ifdef RESCUEDIR
273		RESCUEDIR,
274#endif
275		_PATH_SBIN,
276		_PATH_USRSBIN,
277		NULL
278	};
279	const char ** volatile argv, **edir;
280	const char * volatile vfstype = vfst;
281	pid_t pid;
282	int argc, i, status, maxargc;
283	char *optb;
284	char *volatile optbuf;
285	char execname[MAXPATHLEN + 1], execbase[MAXPATHLEN];
286	const char *extra = getoptions(vfstype);
287
288	if (!strcmp(vfstype, "ufs"))
289		vfstype = MOUNT_UFS;
290
291	optb = NULL;
292	if (options)
293		catopt(&optb, options);
294	if (extra)
295		catopt(&optb, extra);
296	optbuf = optb;
297
298	maxargc = 64;
299	argv = emalloc(sizeof(char *) * maxargc);
300
301	(void) snprintf(execbase, sizeof(execbase), "fsck_%s", vfstype);
302	argc = 0;
303	argv[argc++] = execbase;
304	if (optbuf)
305		mangle(optbuf, &argc, &argv, &maxargc);
306	argv[argc++] = spec;
307	argv[argc] = NULL;
308
309	if (flags & (CHECK_DEBUG|CHECK_VERBOSE)) {
310		(void)printf("start %s %swait", mntpt,
311			pidp ? "no" : "");
312		for (i = 0; i < argc; i++)
313			(void)printf(" %s", argv[i]);
314		(void)printf("\n");
315	}
316
317	switch (pid = vfork()) {
318	case -1:				/* Error. */
319		warn("vfork");
320		if (optbuf)
321			free(optbuf);
322		free(argv);
323		return FSCK_EXIT_CHECK_FAILED;
324
325	case 0:					/* Child. */
326		if ((flags & CHECK_FORCE) == 0) {
327			struct statvfs	sfs;
328
329				/*
330				 * if mntpt is a mountpoint of a mounted file
331				 * system and it's mounted read-write, skip it
332				 * unless -f is given.
333				 */
334			if ((statvfs(mntpt, &sfs) == 0) &&
335			    (strcmp(mntpt, sfs.f_mntonname) == 0) &&
336			    ((sfs.f_flag & MNT_RDONLY) == 0)) {
337				printf(
338		"%s: file system is mounted read-write on %s; not checking\n",
339				    spec, mntpt);
340				if ((flags & CHECK_PREEN) && auxarg != NULL)
341					_exit(FSCK_EXIT_OK);	/* fsck -p */
342				else
343					_exit(FSCK_EXIT_CHECK_FAILED);	/* fsck [[-p] ...] */
344			}
345		}
346
347		if (flags & CHECK_DEBUG)
348			_exit(FSCK_EXIT_OK);
349
350		/* Go find an executable. */
351		edir = edirs;
352		do {
353			(void)snprintf(execname,
354			    sizeof(execname), "%s/%s", *edir, execbase);
355			execv(execname, (char * const *)__UNCONST(argv));
356			if (errno != ENOENT) {
357				if (spec)
358					warn("exec %s for %s", execname, spec);
359				else
360					warn("exec %s", execname);
361			}
362		} while (*++edir != NULL);
363
364		if (errno == ENOENT) {
365			if (spec)
366				warn("exec %s for %s", execname, spec);
367			else
368				warn("exec %s", execname);
369		}
370		_exit(FSCK_EXIT_CHECK_FAILED);
371		/* NOTREACHED */
372
373	default:				/* Parent. */
374		if (optbuf)
375			free(optbuf);
376		free(argv);
377
378		if (pidp) {
379			*pidp = pid;
380			return FSCK_EXIT_OK;
381		}
382
383		if (waitpid(pid, &status, 0) < 0) {
384			warn("waitpid");
385			return FSCK_EXIT_CHECK_FAILED;
386		}
387
388		if (WIFEXITED(status)) {
389			if (WEXITSTATUS(status) != 0)
390				return WEXITSTATUS(status);
391		}
392		else if (WIFSIGNALED(status)) {
393			warnx("%s: %s", spec, strsignal(WTERMSIG(status)));
394			return FSCK_EXIT_CHECK_FAILED;
395		}
396		break;
397	}
398
399	return FSCK_EXIT_OK;
400}
401
402
403static int
404selected(const char *type)
405{
406	struct entry *e;
407
408	/* If no type specified, it's always selected. */
409	TAILQ_FOREACH(e, &selhead, entries)
410		if (!strcmp(e->type, type))
411			return which == IN_LIST ? 1 : 0;
412
413	return which == IN_LIST ? 0 : 1;
414}
415
416
417static int
418omitted(const char *mountedon)
419{
420	struct entry *e;
421
422	/* If no type specified, it's always selected. */
423	TAILQ_FOREACH(e, &omhead, entries)
424		if (!strcmp(e->type, mountedon))
425			return 1;
426
427	return 0;
428}
429
430
431static const char *
432getoptions(const char *type)
433{
434	struct entry *e;
435
436	TAILQ_FOREACH(e, &opthead, entries)
437		if (!strcmp(e->type, type))
438			return e->options;
439	return "";
440}
441
442
443static void
444addoption(char *optstr)
445{
446	char *newoptions;
447	struct entry *e;
448
449	if ((newoptions = strchr(optstr, ':')) == NULL)
450		errx(1, "Invalid option string");
451
452	*newoptions++ = '\0';
453
454	TAILQ_FOREACH(e, &opthead, entries)
455		if (!strcmp(e->type, optstr)) {
456			catopt(&e->options, newoptions);
457			return;
458		}
459	addentry(&opthead, optstr, newoptions);
460}
461
462
463static void
464addentry(struct fstypelist *list, const char *type, const char *opts)
465{
466	struct entry *e;
467
468	e = emalloc(sizeof(struct entry));
469	e->type = estrdup(type);
470	e->options = estrdup(opts);
471	TAILQ_INSERT_TAIL(list, e, entries);
472}
473
474
475static void
476maketypelist(char *fslist)
477{
478	char *ptr;
479
480	if ((fslist == NULL) || (fslist[0] == '\0'))
481		errx(1, "empty type list");
482
483	if (fslist[0] == 'n' && fslist[1] == 'o') {
484		fslist += 2;
485		which = NOT_IN_LIST;
486	}
487	else
488		which = IN_LIST;
489
490	while ((ptr = strsep(&fslist, ",")) != NULL)
491		addentry(&selhead, ptr, "");
492
493}
494
495
496static void
497catopt(char **sp, const char *o)
498{
499	char *s;
500	size_t i, j;
501
502	s = *sp;
503	if (s) {
504		i = strlen(s);
505		j = i + 1 + strlen(o) + 1;
506		s = erealloc(s, j);
507		(void)snprintf(s + i, j, ",%s", o);
508	} else
509		s = estrdup(o);
510	*sp = s;
511}
512
513
514static void
515mangle(char *opts, int *argcp, const char ** volatile *argvp, int *maxargcp)
516{
517	char *p, *s;
518	int argc, maxargc;
519	const char **argv;
520
521	argc = *argcp;
522	argv = *argvp;
523	maxargc = *maxargcp;
524
525	for (s = opts; (p = strsep(&s, ",")) != NULL;) {
526		/* Always leave space for one more argument and the NULL. */
527		if (argc >= maxargc - 3) {
528			maxargc <<= 1;
529			argv = erealloc(argv, maxargc * sizeof(char *));
530		}
531		if (*p != '\0')  {
532			if (*p == '-') {
533				argv[argc++] = p;
534				p = strchr(p, '=');
535				if (p) {
536					*p = '\0';
537					argv[argc++] = p+1;
538				}
539			} else {
540				argv[argc++] = "-o";
541				argv[argc++] = p;
542			}
543		}
544	}
545
546	*argcp = argc;
547	*argvp = argv;
548	*maxargcp = maxargc;
549}
550
551static const char *
552getfslab(const char *str)
553{
554	static struct dkwedge_info dkw;
555	struct disklabel dl;
556	int fd;
557	char p;
558	const char *vfstype;
559	u_char t;
560	char buf[MAXPATHLEN];
561
562	/* deduce the file system type from the disk label */
563        if ((fd = opendisk(str, O_RDONLY, buf, sizeof(buf), 0)) == -1)
564		err(1, "cannot open `%s'", buf);
565
566	/* First check to see if it's a wedge. */
567	if (ioctl(fd, DIOCGWEDGEINFO, &dkw) == 0) {
568		/* Yup, this is easy. */
569		(void) close(fd);
570		return (dkw.dkw_ptype);
571	}
572
573	if (ioctl(fd, DIOCGDINFO, &dl) == -1)
574		err(1, "cannot get disklabel for `%s'", buf);
575
576	(void) close(fd);
577
578	p = buf[strlen(buf) - 1];
579
580	if ((p - 'a') >= dl.d_npartitions)
581		errx(1, "partition `%s' is not defined on disk", buf);
582
583	if ((t = dl.d_partitions[p - 'a'].p_fstype) >= FSMAXTYPES)
584		errx(1, "partition `%s' is not of a legal vfstype",
585		    buf);
586
587	if ((vfstype = fscknames[t]) == NULL)
588		errx(1, "vfstype `%s' on partition `%s' is not supported",
589		    fstypenames[t], buf);
590
591	return vfstype;
592}
593
594
595static void
596usage(void)
597{
598	static const char common[] =
599	    "[-dfnPpqvy] [-x excludemount] [-l maxparallel] [-T fstype:fsoptions]\n\t\t[-t fstype]";
600
601	(void)fprintf(stderr, "usage: %s %s [special|node]...\n",
602	    getprogname(), common);
603	exit(FSCK_EXIT_USAGE);
604}
605