xref: /netbsd-src/sbin/fsck_ffs/main.c (revision cac8e449158efc7261bebc8657cbb0125a2cfdde)
1 /*	$NetBSD: main.c,v 1.68 2008/07/20 01:20:22 lukem 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.68 2008/07/20 01:20:22 lukem 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 <fstab.h>
59 #include <string.h>
60 #include <time.h>
61 #include <stdio.h>
62 #include <stdlib.h>
63 #include <unistd.h>
64 #include <signal.h>
65 
66 #include "fsck.h"
67 #include "extern.h"
68 #include "fsutil.h"
69 #include "exitvalues.h"
70 
71 int	progress = 0;
72 int	returntosingle = 0;
73 
74 static int	argtoi(int, const char *, const char *, int);
75 static int	checkfilesys(const char *, char *, long, int);
76 static void	usage(void);
77 
78 int
79 main(int argc, char *argv[])
80 {
81 	struct rlimit r;
82 	int ch;
83 	int ret = FSCK_EXIT_OK;
84 
85 	if (getrlimit(RLIMIT_DATA, &r) == 0) {
86 		r.rlim_cur = r.rlim_max;
87 		(void) setrlimit(RLIMIT_DATA, &r);
88 	}
89 	sync();
90 	skipclean = 1;
91 	markclean = 1;
92 	forceimage = 0;
93 	endian = 0;
94 	isappleufs = 0;
95 	while ((ch = getopt(argc, argv, "aB:b:c:dFfm:npPqy")) != -1) {
96 		switch (ch) {
97 		case 'a':
98 			isappleufs = 1;
99 			break;
100 
101 		case 'B':
102 			if (strcmp(optarg, "be") == 0)
103 				endian = BIG_ENDIAN;
104 			else if (strcmp(optarg, "le") == 0)
105 				endian = LITTLE_ENDIAN;
106 			else usage();
107 			break;
108 
109 		case 'b':
110 			skipclean = 0;
111 			bflag = argtoi('b', "number", optarg, 10);
112 			printf("Alternate super block location: %d\n", bflag);
113 			break;
114 
115 		case 'c':
116 			skipclean = 0;
117 			cvtlevel = argtoi('c', "conversion level", optarg, 10);
118 			if (cvtlevel > 4) {
119 				cvtlevel = 4;
120 				warnx("Using maximum conversion level of %d\n",
121 				    cvtlevel);
122 			}
123 			break;
124 
125 		case 'd':
126 			debug++;
127 			break;
128 
129 		case 'F':
130 			forceimage = 1;
131 			break;
132 
133 		case 'f':
134 			skipclean = 0;
135 			break;
136 
137 		case 'm':
138 			lfmode = argtoi('m', "mode", optarg, 8);
139 			if (lfmode &~ 07777)
140 				errx(FSCK_EXIT_USAGE, "bad mode to -m: %o",
141 				    lfmode);
142 			printf("** lost+found creation mode %o\n", lfmode);
143 			break;
144 
145 		case 'n':
146 			nflag++;
147 			yflag = 0;
148 			break;
149 
150 		case 'p':
151 			preen++;
152 			break;
153 
154 		case 'P':
155 			progress = 1;
156 			break;
157 
158 		case 'q':
159 			quiet++;
160 			break;
161 
162 		case 'y':
163 			yflag++;
164 			nflag = 0;
165 			break;
166 
167 		default:
168 			usage();
169 		}
170 	}
171 
172 	argc -= optind;
173 	argv += optind;
174 
175 	if (!argc)
176 		usage();
177 
178 	if (debug)
179 		progress = 0;
180 
181 	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
182 		(void)signal(SIGINT, catch);
183 	if (preen)
184 		(void)signal(SIGQUIT, catchquit);
185 #ifdef PROGRESS
186 	if (progress) {
187 		progress_ttywidth(0);
188 		(void)signal(SIGWINCH, progress_ttywidth);
189 	}
190 #endif /* ! PROGRESS */
191 	signal(SIGINFO, infohandler);
192 
193 	while (argc-- > 0) {
194 		const char *path = blockcheck(*argv);
195 
196 		if (path == NULL)
197 			pfatal("Can't check %s\n", *argv);
198 		else {
199 			int nret = checkfilesys(blockcheck(*argv), 0, 0L, 0);
200 			if (ret < nret)
201 				ret = nret;
202 		}
203 		argv++;
204 	}
205 
206 	return returntosingle ? FSCK_EXIT_UNRESOLVED : ret;
207 }
208 
209 static int
210 argtoi(int flag, const char *req, const char *str, int base)
211 {
212 	char *cp;
213 	int ret;
214 
215 	ret = (int)strtol(str, &cp, base);
216 	if (cp == str || *cp)
217 		errx(FSCK_EXIT_USAGE, "-%c flag requires a %s",
218 		    flag, req);
219 	return (ret);
220 }
221 
222 /*
223  * Check the specified filesystem.
224  */
225 /* ARGSUSED */
226 static int
227 checkfilesys(const char *filesys, char *mntpt, long auxdata, int child)
228 {
229 	daddr_t n_ffree, n_bfree;
230 	struct dups *dp;
231 	struct zlncnt *zlnp;
232 	int cylno;
233 #ifdef LITE2BORKEN
234 	int flags;
235 #endif
236 #ifdef PROGRESS
237 	/*
238 	 * In prune mode, how far does the progress bar travel during
239 	 * each pass?  (In non-prune mode, each pass has a separate
240 	 * progress bar that travels from 0 to 100%.)
241 	 *
242 	 * The numbers below are percentages, intended to correspond
243 	 * roughly to the cumulative time up to the end of each pass.
244 	 * They don't have to be accurate.  In reality, on a large
245 	 * file system, Pass 1 and Pass 2 together are likely to use
246 	 * significantly more than the 95% reflected below, so users
247 	 * will get a pleasant surprise when the last 5% of the progress
248 	 * bar runs more quickly than they had expected.
249 	 */
250 	static int progress_limits[] = {0, 20, 95, 96, 97, 100};
251 #endif /* PROGRESS */
252 
253 	if (preen && child)
254 		(void)signal(SIGQUIT, voidquit);
255 	setcdevname(filesys, preen);
256 	if (debug && preen)
257 		pwarn("starting\n");
258 	switch (setup(filesys)) {
259 	case 0:
260 		if (preen)
261 			pfatal("CAN'T CHECK FILE SYSTEM.");
262 		/* fall through */
263 	case -1:
264 		return FSCK_EXIT_OK;
265 	}
266 	/*
267 	 * Cleared if any questions answered no. Used to decide if
268 	 * the superblock should be marked clean.
269 	 */
270 	resolved = 1;
271 
272 #ifdef PROGRESS
273 	progress_switch(progress);
274 	progress_init();
275 #endif /* PROGRESS */
276 
277 	/*
278 	 * 1: scan inodes tallying blocks used
279 	 */
280 	if (preen == 0) {
281 		pwarn("** Last Mounted on %s\n", sblock->fs_fsmnt);
282 		if (hotroot())
283 			pwarn("** Root file system\n");
284 		pwarn("** Phase 1 - Check Blocks and Sizes\n");
285 	}
286 #ifdef PROGRESS
287 	if (preen)
288 		progress_setrange(0, progress_limits[1]);
289 #endif /* PROGRESS */
290 	pass1();
291 
292 	/*
293 	 * 1b: locate first references to duplicates, if any
294 	 */
295 	if (duplist) {
296 		if (preen)
297 			pfatal("INTERNAL ERROR: dups with -p\n");
298 		if (usedsoftdep)
299 			pfatal("INTERNAL ERROR: dups with softdep\n");
300 		pwarn("** Phase 1b - Rescan For More DUPS\n");
301 		pass1b();
302 	}
303 
304 	/*
305 	 * 2: traverse directories from root to mark all connected directories
306 	 */
307 	if (preen == 0)
308 		pwarn("** Phase 2 - Check Pathnames\n");
309 #ifdef PROGRESS
310 	if (preen)
311 		progress_sethighlim(progress_limits[2]);
312 #endif /* PROGRESS */
313 	pass2();
314 
315 	/*
316 	 * 3: scan inodes looking for disconnected directories
317 	 */
318 	if (preen == 0)
319 		pwarn("** Phase 3 - Check Connectivity\n");
320 #ifdef PROGRESS
321 	if (preen)
322 		progress_sethighlim(progress_limits[3]);
323 #endif /* PROGRESS */
324 	pass3();
325 
326 	/*
327 	 * 4: scan inodes looking for disconnected files; check reference counts
328 	 */
329 	if (preen == 0)
330 		pwarn("** Phase 4 - Check Reference Counts\n");
331 #ifdef PROGRESS
332 	if (preen)
333 		progress_sethighlim(progress_limits[4]);
334 #endif /* PROGRESS */
335 	pass4();
336 
337 	/*
338 	 * 5: check and repair resource counts in cylinder groups
339 	 */
340 	if (preen == 0)
341 		pwarn("** Phase 5 - Check Cyl groups\n");
342 #ifdef PROGRESS
343 	if (preen)
344 		progress_sethighlim(progress_limits[5]);
345 #endif /* PROGRESS */
346 	pass5();
347 
348 	/*
349 	 * print out summary statistics
350 	 */
351 	n_ffree = sblock->fs_cstotal.cs_nffree;
352 	n_bfree = sblock->fs_cstotal.cs_nbfree;
353 	pwarn("%llu files, %lld used, %lld free ",
354 	    (unsigned long long)n_files, (long long)n_blks,
355 	    (long long)(n_ffree + sblock->fs_frag * n_bfree));
356 	printf("(%lld frags, %lld blocks, %lld.%lld%% fragmentation)\n",
357 	    (long long)n_ffree, (long long)n_bfree,
358 	    (long long)(n_ffree * 100 / (daddr_t)sblock->fs_dsize),
359 	    (long long)(((n_ffree * 1000 + (daddr_t)sblock->fs_dsize / 2)
360 		/ (daddr_t)sblock->fs_dsize) % 10));
361 	if (debug &&
362 	    (n_files -= maxino - ROOTINO - sblock->fs_cstotal.cs_nifree))
363 		printf("%llu files missing\n", (unsigned long long)n_files);
364 	if (debug) {
365 		n_blks += sblock->fs_ncg *
366 			(cgdmin(sblock, 0) - cgsblock(sblock, 0));
367 		n_blks += cgsblock(sblock, 0) - cgbase(sblock, 0);
368 		n_blks += howmany(sblock->fs_cssize, sblock->fs_fsize);
369 		if (n_blks -= maxfsblock - (n_ffree + sblock->fs_frag * n_bfree))
370 			printf("%lld blocks missing\n", (long long)n_blks);
371 		if (duplist != NULL) {
372 			printf("The following duplicate blocks remain:");
373 			for (dp = duplist; dp; dp = dp->next)
374 				printf(" %lld,", (long long)dp->dup);
375 			printf("\n");
376 		}
377 		if (zlnhead != NULL) {
378 			printf("The following zero link count inodes remain:");
379 			for (zlnp = zlnhead; zlnp; zlnp = zlnp->next)
380 				printf(" %llu,",
381 				    (unsigned long long)zlnp->zlncnt);
382 			printf("\n");
383 		}
384 	}
385 	zlnhead = (struct zlncnt *)0;
386 	duplist = (struct dups *)0;
387 	muldup = (struct dups *)0;
388 	inocleanup();
389 	if (fsmodified) {
390 		sblock->fs_time = time(NULL);
391 		sbdirty();
392 	}
393 	if (rerun)
394 		markclean = 0;
395 #if LITE2BORKEN
396 	if (!hotroot()) {
397 		ckfini();
398 	} else {
399 		struct statvfs stfs_buf;
400 		/*
401 		 * Check to see if root is mounted read-write.
402 		 */
403 		if (statvfs("/", &stfs_buf) == 0)
404 			flags = stfs_buf.f_flag;
405 		else
406 			flags = 0;
407 		if (markclean)
408 			markclean = flags & MNT_RDONLY;
409 		ckfini();
410 	}
411 #else
412 	ckfini();
413 #endif
414 	for (cylno = 0; cylno < sblock->fs_ncg; cylno++)
415 		if (inostathead[cylno].il_stat != NULL)
416 			free(inostathead[cylno].il_stat);
417 	free(inostathead);
418 	inostathead = NULL;
419 
420 	if (!resolved || rerun) {
421 		pwarn("\n***** UNRESOLVED INCONSISTENCIES REMAIN *****\n");
422 		returntosingle = 1;
423 	}
424 	if (!fsmodified)
425 		return FSCK_EXIT_OK;
426 	if (!preen)
427 		pwarn("\n***** FILE SYSTEM WAS MODIFIED *****\n");
428 	if (rerun)
429 		pwarn("\n***** PLEASE RERUN FSCK *****\n");
430 	if (hotroot()) {
431 		struct statvfs stfs_buf;
432 		/*
433 		 * We modified the root.  Do a mount update on
434 		 * it, unless it is read-write, so we can continue.
435 		 */
436 		if (statvfs("/", &stfs_buf) == 0) {
437 			long flags = stfs_buf.f_flag;
438 			struct ufs_args args;
439 
440 			if (flags & MNT_RDONLY) {
441 				args.fspec = 0;
442 				flags |= MNT_UPDATE | MNT_RELOAD;
443 				if (mount(MOUNT_FFS, "/", flags,
444 				    &args, sizeof args) == 0)
445 					return FSCK_EXIT_OK;
446 			}
447 		}
448 		if (!preen)
449 			pwarn("\n***** REBOOT NOW *****\n");
450 		sync();
451 		return FSCK_EXIT_ROOT_CHANGED;
452 	}
453 	return FSCK_EXIT_OK;
454 }
455 
456 static void
457 usage(void)
458 {
459 
460 	(void) fprintf(stderr,
461 	    "usage: %s [-adFfnPpqy] [-B be|le] [-b block] [-c level] [-m mode]"
462 	    " filesystem ...\n",
463 	    getprogname());
464 	exit(FSCK_EXIT_USAGE);
465 }
466 
467