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