xref: /netbsd-src/sbin/fsck/fsck.c (revision fdecd6a253f999ae92b139670d9e15cc9df4497c)
1 /*	$NetBSD: fsck.c,v 1.11 1997/06/23 01:03:35 mikel 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. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *	This product includes software developed by the University of
19  *	California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  *
36  * From: @(#)mount.c	8.19 (Berkeley) 4/19/94
37  * From: NetBSD: mount.c,v 1.24 1995/11/18 03:34:29 cgd Exp
38  *
39  */
40 
41 static char rcsid[] = "$NetBSD: fsck.c,v 1.11 1997/06/23 01:03:35 mikel Exp $";
42 
43 #include <sys/param.h>
44 #include <sys/mount.h>
45 #include <sys/queue.h>
46 #include <sys/wait.h>
47 #define DKTYPENAMES
48 #include <sys/disklabel.h>
49 #include <sys/ioctl.h>
50 
51 #include <err.h>
52 #include <errno.h>
53 #include <fstab.h>
54 #include <fcntl.h>
55 #include <signal.h>
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <string.h>
59 #include <unistd.h>
60 
61 #include "pathnames.h"
62 #include "fsutil.h"
63 
64 static enum { IN_LIST, NOT_IN_LIST } which = NOT_IN_LIST;
65 
66 TAILQ_HEAD(fstypelist, entry) opthead, selhead;
67 
68 struct entry {
69 	char *type;
70 	char *options;
71 	TAILQ_ENTRY(entry) entries;
72 };
73 
74 static int maxrun = 0;
75 static char *options = NULL;
76 static int flags = 0;
77 
78 int main __P((int, char *[]));
79 
80 static int checkfs __P((const char *, const char *, const char *, void *,
81     pid_t *));
82 static int selected __P((const char *));
83 static void addoption __P((char *));
84 static const char *getoptions __P((const char *));
85 static void addentry __P((struct fstypelist *, const char *, const char *));
86 static void maketypelist __P((char *));
87 static char *catopt __P((char *, const char *, int));
88 static void mangle __P((char *, int *, const char ***, int *));
89 static char *getfslab __P((const char *));
90 static void usage __P((void));
91 static void *isok __P((struct fstab *));
92 
93 int
94 main(argc, argv)
95 	int argc;
96 	char *argv[];
97 {
98 	struct fstab *fs;
99 	int i, rval = 0;
100 	char *vfstype = NULL;
101 	char globopt[3];
102 
103 	globopt[0] = '-';
104 	globopt[2] = '\0';
105 
106 	TAILQ_INIT(&selhead);
107 	TAILQ_INIT(&opthead);
108 
109 	while ((i = getopt(argc, argv, "dvpfnyl:t:T:")) != -1)
110 		switch (i) {
111 		case 'd':
112 			flags |= CHECK_DEBUG;
113 			break;
114 
115 		case 'v':
116 			flags |= CHECK_VERBOSE;
117 			break;
118 
119 		case 'p':
120 			flags |= CHECK_PREEN;
121 			/*FALLTHROUGH*/
122 		case 'n':
123 		case 'f':
124 		case 'y':
125 			globopt[1] = i;
126 			options = catopt(options, globopt, 1);
127 			break;
128 
129 		case 'l':
130 			maxrun = atoi(optarg);
131 			break;
132 
133 		case 'T':
134 			if (*optarg)
135 				addoption(optarg);
136 			break;
137 
138 		case 't':
139 			if (selhead.tqh_first != NULL)
140 				errx(1, "only one -t option may be specified.");
141 
142 			maketypelist(optarg);
143 			vfstype = optarg;
144 			break;
145 
146 		case '?':
147 		default:
148 			usage();
149 			/* NOTREACHED */
150 		}
151 
152 	argc -= optind;
153 	argv += optind;
154 
155 	if (argc == 0)
156 		return checkfstab(flags, maxrun, isok, checkfs);
157 
158 #define	BADTYPE(type)							\
159 	(strcmp(type, FSTAB_RO) &&					\
160 	    strcmp(type, FSTAB_RW) && strcmp(type, FSTAB_RQ))
161 
162 
163 	for (; argc--; argv++) {
164 		char *spec, *type;
165 
166 		if ((fs = getfsfile(*argv)) == NULL &&
167 		    (fs = getfsspec(*argv)) == NULL) {
168 			if (vfstype == NULL)
169 				vfstype = getfslab(*argv);
170 			spec = *argv;
171 			type = vfstype;
172 		}
173 		else {
174 			spec = fs->fs_spec;
175 			type = fs->fs_vfstype;
176 			if (BADTYPE(fs->fs_type))
177 				errx(1, "%s has unknown file system type.",
178 				    *argv);
179 		}
180 
181 		rval |= checkfs(type, blockcheck(spec), *argv, NULL, NULL);
182 	}
183 
184 	return rval;
185 }
186 
187 
188 static void *
189 isok(fs)
190 	struct fstab *fs;
191 {
192 	if (fs->fs_passno == 0)
193 		return NULL;
194 
195 	if (BADTYPE(fs->fs_type))
196 		return NULL;
197 
198 	if (!selected(fs->fs_vfstype))
199 		return NULL;
200 
201 	return fs;
202 }
203 
204 
205 static int
206 checkfs(vfstype, spec, mntpt, auxarg, pidp)
207 	const char *vfstype, *spec, *mntpt;
208 	void *auxarg;
209 	pid_t *pidp;
210 {
211 	/* List of directories containing fsck_xxx subcommands. */
212 	static const char *edirs[] = {
213 		_PATH_SBIN,
214 		_PATH_USRSBIN,
215 		NULL
216 	};
217 	char execbase[MAXPATHLEN];
218 	const char **argv, **edir;
219 	pid_t pid;
220 	int argc = 1, i, status, maxargc;
221 	char *optbuf = NULL, execname[MAXPATHLEN + 1];
222 	const char *extra = getoptions(vfstype);
223 
224 
225 #ifdef __GNUC__
226 	/* Avoid vfork clobbering */
227 	(void) &optbuf;
228 	(void) &vfstype;
229 #endif
230 
231 	if (strcmp(vfstype, "ufs") == 0)
232 		vfstype = MOUNT_UFS;
233 
234 	maxargc = 100;
235 	argv = emalloc(sizeof(char *) * maxargc);
236 
237 	/* construct basename of executable and argv[0] simultaneously */
238 	(void) snprintf(execbase, sizeof(execbase), "fsck_%s", vfstype);
239 	argv[0] = vfstype;
240 
241 	if (options) {
242 		if (extra != NULL)
243 			optbuf = catopt(options, extra, 0);
244 		else
245 			optbuf = estrdup(options);
246 	}
247 	else if (extra)
248 		optbuf = estrdup(extra);
249 
250 	if (optbuf)
251 		mangle(optbuf, &argc, &argv, &maxargc);
252 
253 	argv[argc++] = spec;
254 	argv[argc] = NULL;
255 
256 	if (flags & (CHECK_DEBUG|CHECK_VERBOSE)) {
257 		(void)printf("start %s %swait", mntpt,
258 			pidp ? "no" : "");
259 		for (i = 0; i < argc; i++)
260 			(void)printf(" %s", argv[i]);
261 		(void)printf("\n");
262 	}
263 
264 	switch (pid = vfork()) {
265 	case -1:				/* Error. */
266 		warn("vfork");
267 		if (optbuf)
268 			free(optbuf);
269 		return (1);
270 
271 	case 0:					/* Child. */
272 		if (flags & CHECK_DEBUG)
273 			_exit(0);
274 
275 		/* Go find an executable. */
276 		edir = edirs;
277 		do {
278 			(void)snprintf(execname,
279 			    sizeof(execname), "%s/%s", *edir, execbase);
280 			execv(execname, (char * const *)argv);
281 			if (errno != ENOENT)
282 				if (spec)
283 					warn("exec %s for %s", execname, spec);
284 				else
285 					warn("exec %s", execname);
286 		} while (*++edir != NULL);
287 
288 		if (errno == ENOENT)
289 			if (spec)
290 				warn("exec %s for %s", execname, spec);
291 			else
292 				warn("exec %s", execname);
293 		_exit(1);
294 		/* NOTREACHED */
295 
296 	default:				/* Parent. */
297 		if (optbuf)
298 			free(optbuf);
299 
300 		if (pidp) {
301 			*pidp = pid;
302 			return 0;
303 		}
304 
305 		if (waitpid(pid, &status, 0) < 0) {
306 			warn("waitpid");
307 			return (1);
308 		}
309 
310 		if (WIFEXITED(status)) {
311 			if (WEXITSTATUS(status) != 0)
312 				return (WEXITSTATUS(status));
313 		}
314 		else if (WIFSIGNALED(status)) {
315 			warnx("%s: %s", spec, strsignal(WTERMSIG(status)));
316 			return (1);
317 		}
318 		break;
319 	}
320 
321 	return (0);
322 }
323 
324 
325 static int
326 selected(type)
327 	const char *type;
328 {
329 	struct entry *e;
330 
331 	/* If no type specified, it's always selected. */
332 	for (e = selhead.tqh_first; e != NULL; e = e->entries.tqe_next)
333 		if (!strncmp(e->type, type, MFSNAMELEN))
334 			return which == IN_LIST ? 1 : 0;
335 
336 	return which == IN_LIST ? 0 : 1;
337 }
338 
339 
340 static const char *
341 getoptions(type)
342 	const char *type;
343 {
344 	struct entry *e;
345 
346 	for (e = opthead.tqh_first; e != NULL; e = e->entries.tqe_next)
347 		if (!strncmp(e->type, type, MFSNAMELEN))
348 			return e->options;
349 	return "";
350 }
351 
352 
353 static void
354 addoption(optstr)
355 	char *optstr;
356 {
357 	char *newoptions;
358 	struct entry *e;
359 
360 	if ((newoptions = strchr(optstr, ':')) == NULL)
361 		errx(1, "Invalid option string");
362 
363 	*newoptions++ = '\0';
364 
365 	for (e = opthead.tqh_first; e != NULL; e = e->entries.tqe_next)
366 		if (!strncmp(e->type, optstr, MFSNAMELEN)) {
367 			e->options = catopt(e->options, newoptions, 1);
368 			return;
369 		}
370 	addentry(&opthead, optstr, newoptions);
371 }
372 
373 
374 static void
375 addentry(list, type, opts)
376 	struct fstypelist *list;
377 	const char *type;
378 	const char *opts;
379 {
380 	struct entry *e;
381 
382 	e = emalloc(sizeof(struct entry));
383 	e->type = estrdup(type);
384 	e->options = estrdup(opts);
385 	TAILQ_INSERT_TAIL(list, e, entries);
386 }
387 
388 
389 static void
390 maketypelist(fslist)
391 	char *fslist;
392 {
393 	char *ptr;
394 
395 	if ((fslist == NULL) || (fslist[0] == '\0'))
396 		errx(1, "empty type list");
397 
398 	if (fslist[0] == 'n' && fslist[1] == 'o') {
399 		fslist += 2;
400 		which = NOT_IN_LIST;
401 	}
402 	else
403 		which = IN_LIST;
404 
405 	while ((ptr = strsep(&fslist, ",")) != NULL)
406 		addentry(&selhead, ptr, "");
407 
408 }
409 
410 
411 static char *
412 catopt(s0, s1, fr)
413 	char *s0;
414 	const char *s1;
415 	int fr;
416 {
417 	size_t i;
418 	char *cp;
419 
420 	if (s0 && *s0) {
421 		i = strlen(s0) + strlen(s1) + 1 + 1;
422 		cp = emalloc(i);
423 		(void)snprintf(cp, i, "%s,%s", s0, s1);
424 	}
425 	else
426 		cp = estrdup(s1);
427 
428 	if (s0 && fr)
429 		free(s0);
430 	return (cp);
431 }
432 
433 
434 static void
435 mangle(opts, argcp, argvp, maxargcp)
436 	char *opts;
437 	int *argcp;
438 	const char ***argvp;
439 	int *maxargcp;
440 {
441 	char *p, *s;
442 	int argc = *argcp, maxargc = *maxargcp;
443 	const char **argv = *argvp;
444 
445 	argc = *argcp;
446 	maxargc = *maxargcp;
447 
448 	for (s = opts; (p = strsep(&s, ",")) != NULL;) {
449 		/* always leave space for one more argument and the NULL */
450 		if (argc >= maxargc - 3) {
451 			maxargc += 50;
452 			argv = erealloc(argv, maxargc * sizeof(char *));
453 		}
454 		if (*p != '\0')
455 			if (*p == '-') {
456 				argv[argc++] = p;
457 				p = strchr(p, '=');
458 				if (p) {
459 					*p = '\0';
460 					argv[argc++] = p+1;
461 				}
462 			}
463 			else {
464 				argv[argc++] = "-o";
465 				argv[argc++] = p;
466 			}
467 	}
468 
469 	*argcp = argc;
470 	*argvp = argv;
471 	*maxargcp = maxargc;
472 }
473 
474 
475 static char *
476 getfslab(str)
477 	const char *str;
478 {
479 	struct disklabel dl;
480 	int fd;
481 	char p, *vfstype;
482 	u_char t;
483 
484 	/* deduce the filesystem type from the disk label */
485 	if ((fd = open(str, O_RDONLY)) == -1)
486 		err(1, "cannot open `%s'", str);
487 
488 	if (ioctl(fd, DIOCGDINFO, &dl) == -1)
489 		err(1, "cannot get disklabel for `%s'", str);
490 
491 	(void) close(fd);
492 
493 	p = str[strlen(str) - 1];
494 
495 	if ((p - 'a') >= dl.d_npartitions)
496 		errx(1, "partition `%s' is not defined on disk", str);
497 
498 	if ((t = dl.d_partitions[p - 'a'].p_fstype) >= DKMAXTYPES)
499 		errx(1, "partition `%s' is not of a legal vfstype",
500 		    str);
501 
502 	if ((vfstype = fscknames[t]) == NULL)
503 		errx(1, "vfstype `%s' on partition `%s' is not supported",
504 		    fstypenames[t], str);
505 
506 	return vfstype;
507 }
508 
509 
510 static void
511 usage()
512 {
513 	extern char *__progname;
514 	static const char common[] =
515 	    "[-dpvlyn] [-T fstype:fsoptions] [-t fstype]";
516 
517 	(void)fprintf(stderr, "Usage: %s %s [special|node]...\n",
518 	    __progname, common);
519 	exit(1);
520 }
521