xref: /netbsd-src/sbin/fsck/fsck.c (revision 404fbe5fb94ca1e054339640cabb2801ce52dd30)
1 /*	$NetBSD: fsck.c,v 1.47 2008/02/23 21:41:47 christos 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.47 2008/02/23 21:41:47 christos 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 
68 static enum { IN_LIST, NOT_IN_LIST } which = NOT_IN_LIST;
69 
70 TAILQ_HEAD(fstypelist, entry) opthead, selhead;
71 
72 struct entry {
73 	char *type;
74 	char *options;
75 	TAILQ_ENTRY(entry) entries;
76 };
77 
78 static int maxrun = 0;
79 static char *options = NULL;
80 static int flags = 0;
81 
82 static int checkfs(const char *, const char *, const char *, void *, pid_t *);
83 static int selected(const char *);
84 static void addoption(char *);
85 static const char *getoptions(const char *);
86 static void addentry(struct fstypelist *, const char *, const char *);
87 static void maketypelist(char *);
88 static void catopt(char **, const char *);
89 static void mangle(char *, int *, const char ** volatile *, int *);
90 static const char *getfslab(const char *);
91 static void usage(void);
92 static void *isok(struct fstab *);
93 
94 int
95 main(int argc, char *argv[])
96 {
97 	struct fstab *fs;
98 	int i, rval;
99 	const char *vfstype = NULL;
100 	char globopt[3];
101 	int ret = FSCK_EXIT_OK;
102 
103 	globopt[0] = '-';
104 	globopt[2] = '\0';
105 
106 	TAILQ_INIT(&selhead);
107 	TAILQ_INIT(&opthead);
108 
109 	while ((i = getopt(argc, argv, "dfl:nPpqT:t:vy")) != -1) {
110 		switch (i) {
111 		case 'd':
112 			flags |= CHECK_DEBUG;
113 			continue;
114 
115 		case 'f':
116 			flags |= CHECK_FORCE;
117 			break;
118 
119 		case 'n':
120 			flags |= CHECK_NOFIX;
121 			break;
122 
123 		case 'p':
124 			flags |= CHECK_PREEN;
125 			break;
126 
127 		case 'P':
128 			flags |= CHECK_PROGRESS;
129 			break;
130 
131 		case 'q':
132 			break;
133 
134 		case 'l':
135 			maxrun = atoi(optarg);
136 			continue;
137 
138 		case 'T':
139 			if (*optarg)
140 				addoption(optarg);
141 			continue;
142 
143 		case 't':
144 			if (TAILQ_FIRST(&selhead) != NULL)
145 				errx(1, "only one -t option may be specified.");
146 
147 			maketypelist(optarg);
148 			vfstype = optarg;
149 			continue;
150 
151 		case 'v':
152 			flags |= CHECK_VERBOSE;
153 			continue;
154 
155 		case 'y':
156 			break;
157 
158 		case '?':
159 		default:
160 			usage();
161 			/* NOTREACHED */
162 		}
163 
164 		/* Pass option to fsck_xxxfs */
165 		globopt[1] = i;
166 		catopt(&options, globopt);
167 	}
168 
169 	/* Don't do progress meters if we're debugging. */
170 	if (flags & CHECK_DEBUG)
171 		flags &= ~CHECK_PROGRESS;
172 
173 	/*
174 	 * If progress meters are being used, force max parallel to 1
175 	 * so the progress meter outputs don't interfere with one another.
176 	 */
177 	if (flags & CHECK_PROGRESS)
178 		maxrun = 1;
179 
180 	argc -= optind;
181 	argv += optind;
182 
183 	if (argc == 0)
184 		return checkfstab(flags, maxrun, isok, checkfs);
185 
186 #define	BADTYPE(type)							\
187 	(strcmp(type, FSTAB_RO) &&					\
188 	    strcmp(type, FSTAB_RW) && strcmp(type, FSTAB_RQ))
189 
190 
191 	for (; argc--; argv++) {
192 		const char *spec, *type, *cp;
193 		char	device[MAXPATHLEN];
194 
195 		spec = *argv;
196 		cp = strrchr(spec, '/');
197 		if (cp == 0) {
198 			(void)snprintf(device, sizeof(device), "%s%s",
199 				_PATH_DEV, spec);
200 			spec = device;
201 		}
202 		if ((fs = getfsfile(spec)) == NULL &&
203 		    (fs = getfsspec(spec)) == NULL) {
204 			if (vfstype == NULL)
205 				vfstype = getfslab(spec);
206 			type = vfstype;
207 		}
208 		else {
209 			spec = fs->fs_spec;
210 			type = fs->fs_vfstype;
211 			if (BADTYPE(fs->fs_type))
212 				errx(FSCK_EXIT_CHECK_FAILED,
213 				    "%s has unknown file system type.",
214 				    spec);
215 		}
216 
217 		rval = checkfs(type, blockcheck(spec), *argv, NULL, NULL);
218 		if (rval > ret)
219 			ret = rval;
220 	}
221 
222 	return ret;
223 }
224 
225 
226 static void *
227 isok(struct fstab *fs)
228 {
229 
230 	if (fs->fs_passno == 0)
231 		return NULL;
232 
233 	if (BADTYPE(fs->fs_type))
234 		return NULL;
235 
236 	if (!selected(fs->fs_vfstype))
237 		return NULL;
238 
239 	return fs;
240 }
241 
242 
243 static int
244 checkfs(const char *vfst, const char *spec, const char *mntpt, void *auxarg,
245     pid_t *pidp)
246 {
247 	/* List of directories containing fsck_xxx subcommands. */
248 	static const char *edirs[] = {
249 #ifdef RESCUEDIR
250 		RESCUEDIR,
251 #endif
252 		_PATH_SBIN,
253 		_PATH_USRSBIN,
254 		NULL
255 	};
256 	const char ** volatile argv, **edir;
257 	const char * volatile vfstype = vfst;
258 	pid_t pid;
259 	int argc, i, status, maxargc;
260 	char *optb;
261 	char *volatile optbuf;
262 	char execname[MAXPATHLEN + 1], execbase[MAXPATHLEN];
263 	const char *extra = getoptions(vfstype);
264 
265 	if (!strcmp(vfstype, "ufs"))
266 		vfstype = MOUNT_UFS;
267 
268 	optb = NULL;
269 	if (options)
270 		catopt(&optb, options);
271 	if (extra)
272 		catopt(&optb, extra);
273 	optbuf = optb;
274 
275 	maxargc = 64;
276 	argv = emalloc(sizeof(char *) * maxargc);
277 
278 	(void) snprintf(execbase, sizeof(execbase), "fsck_%s", vfstype);
279 	argc = 0;
280 	argv[argc++] = execbase;
281 	if (optbuf)
282 		mangle(optbuf, &argc, &argv, &maxargc);
283 	argv[argc++] = spec;
284 	argv[argc] = NULL;
285 
286 	if (flags & (CHECK_DEBUG|CHECK_VERBOSE)) {
287 		(void)printf("start %s %swait", mntpt,
288 			pidp ? "no" : "");
289 		for (i = 0; i < argc; i++)
290 			(void)printf(" %s", argv[i]);
291 		(void)printf("\n");
292 	}
293 
294 	switch (pid = vfork()) {
295 	case -1:				/* Error. */
296 		warn("vfork");
297 		if (optbuf)
298 			free(optbuf);
299 		free(argv);
300 		return FSCK_EXIT_CHECK_FAILED;
301 
302 	case 0:					/* Child. */
303 		if ((flags & CHECK_FORCE) == 0) {
304 			struct statvfs	sfs;
305 
306 				/*
307 				 * if mntpt is a mountpoint of a mounted file
308 				 * system and it's mounted read-write, skip it
309 				 * unless -f is given.
310 				 */
311 			if ((statvfs(mntpt, &sfs) == 0) &&
312 			    (strcmp(mntpt, sfs.f_mntonname) == 0) &&
313 			    ((sfs.f_flag & MNT_RDONLY) == 0)) {
314 				printf(
315 		"%s: file system is mounted read-write on %s; not checking\n",
316 				    spec, mntpt);
317 				if ((flags & CHECK_PREEN) && auxarg != NULL)
318 					_exit(FSCK_EXIT_OK);	/* fsck -p */
319 				else
320 					_exit(FSCK_EXIT_CHECK_FAILED);	/* fsck [[-p] ...] */
321 			}
322 		}
323 
324 		if (flags & CHECK_DEBUG)
325 			_exit(FSCK_EXIT_OK);
326 
327 		/* Go find an executable. */
328 		edir = edirs;
329 		do {
330 			(void)snprintf(execname,
331 			    sizeof(execname), "%s/%s", *edir, execbase);
332 			execv(execname, (char * const *)__UNCONST(argv));
333 			if (errno != ENOENT) {
334 				if (spec)
335 					warn("exec %s for %s", execname, spec);
336 				else
337 					warn("exec %s", execname);
338 			}
339 		} while (*++edir != NULL);
340 
341 		if (errno == ENOENT) {
342 			if (spec)
343 				warn("exec %s for %s", execname, spec);
344 			else
345 				warn("exec %s", execname);
346 		}
347 		_exit(FSCK_EXIT_CHECK_FAILED);
348 		/* NOTREACHED */
349 
350 	default:				/* Parent. */
351 		if (optbuf)
352 			free(optbuf);
353 		free(argv);
354 
355 		if (pidp) {
356 			*pidp = pid;
357 			return FSCK_EXIT_OK;
358 		}
359 
360 		if (waitpid(pid, &status, 0) < 0) {
361 			warn("waitpid");
362 			return FSCK_EXIT_CHECK_FAILED;
363 		}
364 
365 		if (WIFEXITED(status)) {
366 			if (WEXITSTATUS(status) != 0)
367 				return WEXITSTATUS(status);
368 		}
369 		else if (WIFSIGNALED(status)) {
370 			warnx("%s: %s", spec, strsignal(WTERMSIG(status)));
371 			return FSCK_EXIT_CHECK_FAILED;
372 		}
373 		break;
374 	}
375 
376 	return FSCK_EXIT_OK;
377 }
378 
379 
380 static int
381 selected(const char *type)
382 {
383 	struct entry *e;
384 
385 	/* If no type specified, it's always selected. */
386 	TAILQ_FOREACH(e, &selhead, entries)
387 		if (!strcmp(e->type, type))
388 			return which == IN_LIST ? 1 : 0;
389 
390 	return which == IN_LIST ? 0 : 1;
391 }
392 
393 
394 static const char *
395 getoptions(const char *type)
396 {
397 	struct entry *e;
398 
399 	TAILQ_FOREACH(e, &opthead, entries)
400 		if (!strcmp(e->type, type))
401 			return e->options;
402 	return "";
403 }
404 
405 
406 static void
407 addoption(char *optstr)
408 {
409 	char *newoptions;
410 	struct entry *e;
411 
412 	if ((newoptions = strchr(optstr, ':')) == NULL)
413 		errx(1, "Invalid option string");
414 
415 	*newoptions++ = '\0';
416 
417 	TAILQ_FOREACH(e, &opthead, entries)
418 		if (!strcmp(e->type, optstr)) {
419 			catopt(&e->options, newoptions);
420 			return;
421 		}
422 	addentry(&opthead, optstr, newoptions);
423 }
424 
425 
426 static void
427 addentry(struct fstypelist *list, const char *type, const char *opts)
428 {
429 	struct entry *e;
430 
431 	e = emalloc(sizeof(struct entry));
432 	e->type = estrdup(type);
433 	e->options = estrdup(opts);
434 	TAILQ_INSERT_TAIL(list, e, entries);
435 }
436 
437 
438 static void
439 maketypelist(char *fslist)
440 {
441 	char *ptr;
442 
443 	if ((fslist == NULL) || (fslist[0] == '\0'))
444 		errx(1, "empty type list");
445 
446 	if (fslist[0] == 'n' && fslist[1] == 'o') {
447 		fslist += 2;
448 		which = NOT_IN_LIST;
449 	}
450 	else
451 		which = IN_LIST;
452 
453 	while ((ptr = strsep(&fslist, ",")) != NULL)
454 		addentry(&selhead, ptr, "");
455 
456 }
457 
458 
459 static void
460 catopt(char **sp, const char *o)
461 {
462 	char *s;
463 	size_t i, j;
464 
465 	s = *sp;
466 	if (s) {
467 		i = strlen(s);
468 		j = i + 1 + strlen(o) + 1;
469 		s = erealloc(s, j);
470 		(void)snprintf(s + i, j, ",%s", o);
471 	} else
472 		s = estrdup(o);
473 	*sp = s;
474 }
475 
476 
477 static void
478 mangle(char *opts, int *argcp, const char ** volatile *argvp, int *maxargcp)
479 {
480 	char *p, *s;
481 	int argc, maxargc;
482 	const char **argv;
483 
484 	argc = *argcp;
485 	argv = *argvp;
486 	maxargc = *maxargcp;
487 
488 	for (s = opts; (p = strsep(&s, ",")) != NULL;) {
489 		/* Always leave space for one more argument and the NULL. */
490 		if (argc >= maxargc - 3) {
491 			maxargc <<= 1;
492 			argv = erealloc(argv, maxargc * sizeof(char *));
493 		}
494 		if (*p != '\0')  {
495 			if (*p == '-') {
496 				argv[argc++] = p;
497 				p = strchr(p, '=');
498 				if (p) {
499 					*p = '\0';
500 					argv[argc++] = p+1;
501 				}
502 			} else {
503 				argv[argc++] = "-o";
504 				argv[argc++] = p;
505 			}
506 		}
507 	}
508 
509 	*argcp = argc;
510 	*argvp = argv;
511 	*maxargcp = maxargc;
512 }
513 
514 static const char *
515 getfslab(const char *str)
516 {
517 	static struct dkwedge_info dkw;
518 	struct disklabel dl;
519 	int fd;
520 	char p;
521 	const char *vfstype;
522 	u_char t;
523 
524 	/* deduce the file system type from the disk label */
525 	if ((fd = open(str, O_RDONLY)) == -1)
526 		err(1, "cannot open `%s'", str);
527 
528 	/* First check to see if it's a wedge. */
529 	if (ioctl(fd, DIOCGWEDGEINFO, &dkw) == 0) {
530 		/* Yup, this is easy. */
531 		(void) close(fd);
532 		return (dkw.dkw_ptype);
533 	}
534 
535 	if (ioctl(fd, DIOCGDINFO, &dl) == -1)
536 		err(1, "cannot get disklabel for `%s'", str);
537 
538 	(void) close(fd);
539 
540 	p = str[strlen(str) - 1];
541 
542 	if ((p - 'a') >= dl.d_npartitions)
543 		errx(1, "partition `%s' is not defined on disk", str);
544 
545 	if ((t = dl.d_partitions[p - 'a'].p_fstype) >= FSMAXTYPES)
546 		errx(1, "partition `%s' is not of a legal vfstype",
547 		    str);
548 
549 	if ((vfstype = fscknames[t]) == NULL)
550 		errx(1, "vfstype `%s' on partition `%s' is not supported",
551 		    fstypenames[t], str);
552 
553 	return vfstype;
554 }
555 
556 
557 static void
558 usage(void)
559 {
560 	static const char common[] =
561 	    "[-dfnPpqvy] [-l maxparallel] [-T fstype:fsoptions]\n\t\t[-t fstype]";
562 
563 	(void)fprintf(stderr, "usage: %s %s [special|node]...\n",
564 	    getprogname(), common);
565 	exit(FSCK_EXIT_USAGE);
566 }
567