xref: /netbsd-src/sbin/fsck_ffs/main.c (revision daf6c4152fcddc27c445489775ed1f66ab4ea9a9)
1 /*	$NetBSD: main.c,v 1.77 2011/02/06 12:02:59 njoly Exp $	*/
2 
3 /*
4  * Copyright (c) 1980, 1986, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 #ifndef lint
34 __COPYRIGHT("@(#) Copyright (c) 1980, 1986, 1993\
35  The Regents of the University of California.  All rights reserved.");
36 #endif /* not lint */
37 
38 #ifndef lint
39 #if 0
40 static char sccsid[] = "@(#)main.c	8.6 (Berkeley) 5/14/95";
41 #else
42 __RCSID("$NetBSD: main.c,v 1.77 2011/02/06 12:02:59 njoly Exp $");
43 #endif
44 #endif /* not lint */
45 
46 #include <sys/param.h>
47 #include <sys/time.h>
48 #include <sys/mount.h>
49 #include <sys/resource.h>
50 
51 #include <ufs/ufs/dinode.h>
52 #include <ufs/ufs/ufsmount.h>
53 #include <ufs/ffs/fs.h>
54 #include <ufs/ffs/ffs_extern.h>
55 
56 #include <ctype.h>
57 #include <err.h>
58 #include <errno.h>
59 #include <fstab.h>
60 #include <string.h>
61 #include <time.h>
62 #include <stdio.h>
63 #include <stdlib.h>
64 #include <unistd.h>
65 #include <signal.h>
66 
67 #include "fsck.h"
68 #include "extern.h"
69 #include "fsutil.h"
70 #include "exitvalues.h"
71 #include "snapshot.h"
72 
73 int	progress = 0;
74 volatile sig_atomic_t	returntosingle = 0;
75 
76 static int	argtoi(int, const char *, const char *, int);
77 static int	checkfilesys(const char *, const char *, int);
78 static void	usage(void);
79 
80 int
81 main(int argc, char *argv[])
82 {
83 	struct rlimit r;
84 	int ch;
85 	int ret = FSCK_EXIT_OK;
86 	char *snap_backup = NULL;
87 	int snap_internal = 0;
88 
89 	if (getrlimit(RLIMIT_DATA, &r) == 0) {
90 		r.rlim_cur = r.rlim_max;
91 		(void) setrlimit(RLIMIT_DATA, &r);
92 	}
93 	sync();
94 	skipclean = 1;
95 	markclean = 1;
96 	forceimage = 0;
97 	endian = 0;
98 	isappleufs = 0;
99 	while ((ch = getopt(argc, argv, "aB:b:c:dFfm:npPqUyx:X")) != -1) {
100 		switch (ch) {
101 		case 'a':
102 			isappleufs = 1;
103 			break;
104 
105 		case 'B':
106 			if (strcmp(optarg, "be") == 0)
107 				endian = BIG_ENDIAN;
108 			else if (strcmp(optarg, "le") == 0)
109 				endian = LITTLE_ENDIAN;
110 			else usage();
111 			break;
112 
113 		case 'b':
114 			skipclean = 0;
115 			bflag = argtoi('b', "number", optarg, 10);
116 			printf("Alternate super block location: %d\n", bflag);
117 			break;
118 
119 		case 'c':
120 			skipclean = 0;
121 			cvtlevel = argtoi('c', "conversion level", optarg, 10);
122 			if (cvtlevel > 4) {
123 				cvtlevel = 4;
124 				warnx("Using maximum conversion level of %d\n",
125 				    cvtlevel);
126 			}
127 			break;
128 
129 		case 'd':
130 			debug++;
131 			break;
132 
133 		case 'F':
134 			forceimage = 1;
135 			break;
136 
137 		case 'f':
138 			skipclean = 0;
139 			break;
140 
141 		case 'm':
142 			lfmode = argtoi('m', "mode", optarg, 8);
143 			if (lfmode &~ 07777)
144 				errx(FSCK_EXIT_USAGE, "bad mode to -m: %o",
145 				    lfmode);
146 			printf("** lost+found creation mode %o\n", lfmode);
147 			break;
148 
149 		case 'n':
150 			nflag++;
151 			yflag = 0;
152 			break;
153 
154 		case 'p':
155 			preen++;
156 			break;
157 
158 		case 'P':
159 			progress = 1;
160 			break;
161 
162 		case 'q':
163 			quiet++;
164 			break;
165 #ifndef SMALL
166 		case 'U':
167 			Uflag++;
168 			break;
169 #endif
170 
171 		case 'y':
172 			yflag++;
173 			nflag = 0;
174 			break;
175 		case 'x':
176 			snap_backup = optarg;
177 			break;
178 		case 'X':
179 			snap_internal = 1;
180 			break;
181 
182 		default:
183 			usage();
184 		}
185 	}
186 
187 	if (snap_backup || snap_internal) {
188 		if (!nflag || yflag) {
189 			warnx("Cannot use -x or -X without -n\n");
190 			snap_backup = NULL;
191 			snap_internal = 0;
192 		}
193 	}
194 
195 
196 	argc -= optind;
197 	argv += optind;
198 
199 	if (!argc)
200 		usage();
201 
202 	if (debug)
203 		progress = 0;
204 
205 	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
206 		(void)signal(SIGINT, catch);
207 	if (preen)
208 		(void)signal(SIGQUIT, catchquit);
209 #ifdef PROGRESS
210 	if (progress) {
211 		progress_ttywidth(0);
212 		(void)signal(SIGWINCH, progress_ttywidth);
213 	}
214 #endif /* ! PROGRESS */
215 	signal(SIGINFO, infohandler);
216 
217 	while (argc-- > 0) {
218 		int nret;
219 		char *path;
220 
221 		if (!forceimage)
222 			path = strdup(blockcheck(*argv));
223 		else
224 			path = strdup(*argv);
225 
226 		if (path == NULL)
227 			pfatal("Can't check %s\n", *argv);
228 
229 		if (snap_backup || snap_internal) {
230 			char *snap_dev;
231 			int snapfd;
232 
233 			snapfd = snap_open(*argv, snap_backup, NULL, &snap_dev);
234 			if (snapfd < 0) {
235 				warn("can't take snapshot of %s", *argv);
236 				goto next;
237 			}
238 			nret = checkfilesys(blockcheck(snap_dev), path, 0);
239 			if (ret < nret)
240 				ret = nret;
241 			close(snapfd);
242 		} else {
243 			nret = checkfilesys(path, path, 0);
244 			if (ret < nret)
245 				ret = nret;
246 		}
247 next:
248 		free(path);
249 		argv++;
250 	}
251 
252 	return returntosingle ? FSCK_EXIT_UNRESOLVED : ret;
253 }
254 
255 static int
256 argtoi(int flag, const char *req, const char *str, int base)
257 {
258 	char *cp;
259 	int ret;
260 
261 	ret = (int)strtol(str, &cp, base);
262 	if (cp == str || *cp)
263 		errx(FSCK_EXIT_USAGE, "-%c flag requires a %s",
264 		    flag, req);
265 	return (ret);
266 }
267 
268 /*
269  * Check the specified filesystem.
270  */
271 /* ARGSUSED */
272 static int
273 checkfilesys(const char *filesys, const char *origfs, int child)
274 {
275 	daddr_t n_ffree, n_bfree;
276 	struct dups *dp;
277 	struct zlncnt *zlnp;
278 	int cylno;
279 #ifdef LITE2BORKEN
280 	int flags;
281 #endif
282 #ifdef PROGRESS
283 	/*
284 	 * In prune mode, how far does the progress bar travel during
285 	 * each pass?  (In non-prune mode, each pass has a separate
286 	 * progress bar that travels from 0 to 100%.)
287 	 *
288 	 * The numbers below are percentages, intended to correspond
289 	 * roughly to the cumulative time up to the end of each pass.
290 	 * They don't have to be accurate.  In reality, on a large
291 	 * file system, Pass 1 and Pass 2 together are likely to use
292 	 * significantly more than the 95% reflected below, so users
293 	 * will get a pleasant surprise when the last 5% of the progress
294 	 * bar runs more quickly than they had expected.
295 	 */
296 	static int progress_limits[] = {0, 20, 95, 96, 97, 100};
297 #endif /* PROGRESS */
298 
299 	if (preen && child)
300 		(void)signal(SIGQUIT, voidquit);
301 	setcdevname(filesys, preen);
302 	if (debug && preen)
303 		pwarn("starting\n");
304 	switch (setup(filesys, origfs)) {
305 	case 0:
306 		if (preen)
307 			pfatal("CAN'T CHECK FILE SYSTEM.");
308 		/* fall through */
309 	case -1:
310 		return FSCK_EXIT_OK;
311 	}
312 	/*
313 	 * Cleared if any questions answered no. Used to decide if
314 	 * the superblock should be marked clean.
315 	 */
316 	resolved = 1;
317 
318 #ifdef PROGRESS
319 	progress_switch(progress);
320 	progress_init();
321 #endif /* PROGRESS */
322 
323 	/*
324 	 * 1: scan inodes tallying blocks used
325 	 */
326 	if (preen == 0) {
327 		pwarn("** Last Mounted on %s\n", sblock->fs_fsmnt);
328 		if (hotroot())
329 			pwarn("** Root file system\n");
330 		pwarn("** Phase 1 - Check Blocks and Sizes\n");
331 	}
332 #ifdef PROGRESS
333 	if (preen)
334 		progress_setrange(0, progress_limits[1]);
335 #endif /* PROGRESS */
336 	pass1();
337 
338 	/*
339 	 * 1b: locate first references to duplicates, if any
340 	 */
341 	if (duplist) {
342 		if (preen)
343 			pfatal("INTERNAL ERROR: dups with -p\n");
344 		if (usedsoftdep)
345 			pfatal("INTERNAL ERROR: dups with softdep\n");
346 		pwarn("** Phase 1b - Rescan For More DUPS\n");
347 		pass1b();
348 	}
349 
350 	/*
351 	 * 2: traverse directories from root to mark all connected directories
352 	 */
353 	if (preen == 0)
354 		pwarn("** Phase 2 - Check Pathnames\n");
355 #ifdef PROGRESS
356 	if (preen)
357 		progress_sethighlim(progress_limits[2]);
358 #endif /* PROGRESS */
359 	pass2();
360 
361 	/*
362 	 * 3: scan inodes looking for disconnected directories
363 	 */
364 	if (preen == 0)
365 		pwarn("** Phase 3 - Check Connectivity\n");
366 #ifdef PROGRESS
367 	if (preen)
368 		progress_sethighlim(progress_limits[3]);
369 #endif /* PROGRESS */
370 	pass3();
371 
372 	/*
373 	 * 4: scan inodes looking for disconnected files; check reference counts
374 	 */
375 	if (preen == 0)
376 		pwarn("** Phase 4 - Check Reference Counts\n");
377 #ifdef PROGRESS
378 	if (preen)
379 		progress_sethighlim(progress_limits[4]);
380 #endif /* PROGRESS */
381 	pass4();
382 
383 	/*
384 	 * 5: check and repair resource counts in cylinder groups
385 	 */
386 	if (preen == 0)
387 		pwarn("** Phase 5 - Check Cyl groups\n");
388 #ifdef PROGRESS
389 	if (preen)
390 		progress_sethighlim(progress_limits[5]);
391 #endif /* PROGRESS */
392 	pass5();
393 
394 	/*
395 	 * print out summary statistics
396 	 */
397 	n_ffree = sblock->fs_cstotal.cs_nffree;
398 	n_bfree = sblock->fs_cstotal.cs_nbfree;
399 	pwarn("%llu files, %lld used, %lld free ",
400 	    (unsigned long long)n_files, (long long)n_blks,
401 	    (long long)(n_ffree + sblock->fs_frag * n_bfree));
402 	printf("(%lld frags, %lld blocks, %lld.%lld%% fragmentation)\n",
403 	    (long long)n_ffree, (long long)n_bfree,
404 	    (long long)(n_ffree * 100 / (daddr_t)sblock->fs_dsize),
405 	    (long long)(((n_ffree * 1000 + (daddr_t)sblock->fs_dsize / 2)
406 		/ (daddr_t)sblock->fs_dsize) % 10));
407 	if (debug &&
408 	    (n_files -= maxino - ROOTINO - sblock->fs_cstotal.cs_nifree))
409 		printf("%llu files missing\n", (unsigned long long)n_files);
410 	if (debug) {
411 		n_blks += sblock->fs_ncg *
412 			(cgdmin(sblock, 0) - cgsblock(sblock, 0));
413 		n_blks += cgsblock(sblock, 0) - cgbase(sblock, 0);
414 		n_blks += howmany(sblock->fs_cssize, sblock->fs_fsize);
415 		if (n_blks -= maxfsblock - (n_ffree + sblock->fs_frag * n_bfree))
416 			printf("%lld blocks missing\n", (long long)n_blks);
417 		if (duplist != NULL) {
418 			printf("The following duplicate blocks remain:");
419 			for (dp = duplist; dp; dp = dp->next)
420 				printf(" %lld,", (long long)dp->dup);
421 			printf("\n");
422 		}
423 		if (zlnhead != NULL) {
424 			printf("The following zero link count inodes remain:");
425 			for (zlnp = zlnhead; zlnp; zlnp = zlnp->next)
426 				printf(" %llu,",
427 				    (unsigned long long)zlnp->zlncnt);
428 			printf("\n");
429 		}
430 	}
431 	zlnhead = (struct zlncnt *)0;
432 	duplist = (struct dups *)0;
433 	muldup = (struct dups *)0;
434 	inocleanup();
435 	if (fsmodified) {
436 		sblock->fs_time = time(NULL);
437 		sbdirty();
438 	}
439 	if (rerun)
440 		markclean = 0;
441 #if LITE2BORKEN
442 	if (!hotroot()) {
443 		ckfini();
444 	} else {
445 		struct statvfs stfs_buf;
446 		/*
447 		 * Check to see if root is mounted read-write.
448 		 */
449 		if (statvfs("/", &stfs_buf) == 0)
450 			flags = stfs_buf.f_flag;
451 		else
452 			flags = 0;
453 		if (markclean)
454 			markclean = flags & MNT_RDONLY;
455 		ckfini();
456 	}
457 #else
458 	ckfini();
459 #endif
460 	for (cylno = 0; cylno < sblock->fs_ncg; cylno++)
461 		if (inostathead[cylno].il_stat != NULL)
462 			free(inostathead[cylno].il_stat);
463 	free(inostathead);
464 	inostathead = NULL;
465 
466 	if (!resolved || rerun) {
467 		pwarn("\n***** UNRESOLVED INCONSISTENCIES REMAIN *****\n");
468 		returntosingle = 1;
469 	}
470 	if (!fsmodified)
471 		return FSCK_EXIT_OK;
472 	if (!preen)
473 		pwarn("\n***** FILE SYSTEM WAS MODIFIED *****\n");
474 	if (rerun)
475 		pwarn("\n***** PLEASE RERUN FSCK *****\n");
476 	if (hotroot()) {
477 		struct statvfs stfs_buf;
478 		/*
479 		 * We modified the root.  Do a mount update on
480 		 * it, unless it is read-write, so we can continue.
481 		 */
482 		if (statvfs("/", &stfs_buf) == 0) {
483 			long flags = stfs_buf.f_flag;
484 			struct ufs_args args;
485 
486 			if (flags & MNT_RDONLY) {
487 				args.fspec = 0;
488 				flags |= MNT_UPDATE | MNT_RELOAD;
489 				if (mount(MOUNT_FFS, "/", flags,
490 				    &args, sizeof args) == 0)
491 					return FSCK_EXIT_OK;
492 			}
493 		}
494 		if (!preen)
495 			pwarn("\n***** REBOOT NOW *****\n");
496 		sync();
497 		return FSCK_EXIT_ROOT_CHANGED;
498 	}
499 	return FSCK_EXIT_OK;
500 }
501 
502 static void
503 usage(void)
504 {
505 
506 	(void) fprintf(stderr,
507 	    "usage: %s [-adFfPpqUX] [-B byteorder] [-b block] [-c level] "
508 	    "[-m mode]\n"
509 	    "\t[-x snap-backup] [-y | -n] filesystem ...\n",
510 	    getprogname());
511 	exit(FSCK_EXIT_USAGE);
512 }
513