xref: /csrg-svn/sbin/fsck/main.c (revision 39976)
1 /*
2  * Copyright (c) 1980, 1986 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that the above copyright notice and this paragraph are
7  * duplicated in all such forms and that any documentation,
8  * advertising materials, and other materials related to such
9  * distribution and use acknowledge that the software was developed
10  * by the University of California, Berkeley.  The name of the
11  * University may not be used to endorse or promote products derived
12  * from this software without specific prior written permission.
13  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16  */
17 
18 #ifndef lint
19 char copyright[] =
20 "@(#) Copyright (c) 1980, 1986 The Regents of the University of California.\n\
21  All rights reserved.\n";
22 #endif /* not lint */
23 
24 #ifndef lint
25 static char sccsid[] = "@(#)main.c	5.17 (Berkeley) 02/01/90";
26 #endif /* not lint */
27 
28 #include <sys/param.h>
29 #include <ufs/dinode.h>
30 #include <ufs/fs.h>
31 #include <sys/stat.h>
32 #include <sys/wait.h>
33 #include <fstab.h>
34 #include <strings.h>
35 #include <ctype.h>
36 #include "fsck.h"
37 
38 char	*rawname(), *unrawname(), *blockcheck(), *malloc();
39 void	catch(), catchquit(), voidquit();
40 int	returntosingle;
41 
42 struct part {
43 	char	*name;			/* device name */
44 	char	*fsname;		/* mounted filesystem name */
45 	struct	part *next;		/* forward link of partitions on disk */
46 } *badlist, **badnext = &badlist;
47 
48 struct disk {
49 	char	*name;			/* disk base name */
50 	struct	disk *next;		/* forward link for list of disks */
51 	struct	part *part;		/* head of list of partitions on disk */
52 	int	pid;			/* If != 0, pid of proc working on */
53 } *disks;
54 
55 int	nrun, ndisks, maxrun;
56 
57 main(argc, argv)
58 	int	argc;
59 	char	*argv[];
60 {
61 	struct fstab *fsp;
62 	int pid, passno, sumstatus;
63 	char *name;
64 	register struct disk *dk, *nextdisk;
65 	register struct part *pt;
66 
67 	sync();
68 	while (--argc > 0 && **++argv == '-') {
69 		switch (*++*argv) {
70 
71 		case 'p':
72 			preen++;
73 			break;
74 
75 		case 'b':
76 			if (argv[0][1] != '\0') {
77 				bflag = atoi(argv[0]+1);
78 			} else {
79 				bflag = atoi(*++argv);
80 				argc--;
81 			}
82 			printf("Alternate super block location: %d\n", bflag);
83 			break;
84 
85 		case 'c':
86 			cvtflag++;
87 			break;
88 
89 		case 'd':
90 			debug++;
91 			break;
92 
93 		case 'l':
94 			if (!isdigit(argv[1][0]))
95 				errexit("-l flag requires a number\n");
96 			maxrun = atoi(*++argv);
97 			argc--;
98 			break;
99 
100 		case 'm':
101 			if (!isdigit(argv[1][0]))
102 				errexit("-m flag requires a mode\n");
103 			sscanf(*++argv, "%o", &lfmode);
104 			if (lfmode &~ 07777)
105 				errexit("bad mode to -m: %o\n", lfmode);
106 			argc--;
107 			printf("** lost+found creation mode %o\n", lfmode);
108 			break;
109 
110 		case 'n':
111 		case 'N':
112 			nflag++;
113 			yflag = 0;
114 			break;
115 
116 		case 'y':
117 		case 'Y':
118 			yflag++;
119 			nflag = 0;
120 			break;
121 
122 		default:
123 			errexit("%c option?\n", **argv);
124 		}
125 	}
126 	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
127 		(void)signal(SIGINT, catch);
128 	if (preen)
129 		(void)signal(SIGQUIT, catchquit);
130 	if (argc) {
131 		while (argc-- > 0) {
132 			hotroot = 0;
133 			checkfilesys(*argv++);
134 		}
135 		exit(0);
136 	}
137 	sumstatus = 0;
138 	for (passno = 1; passno <= 2; passno++) {
139 		if (setfsent() == 0)
140 			errexit("Can't open checklist file: %s\n", _PATH_FSTAB);
141 		while ((fsp = getfsent()) != 0) {
142 			if (strcmp(fsp->fs_type, FSTAB_RW) &&
143 			    strcmp(fsp->fs_type, FSTAB_RO) &&
144 			    strcmp(fsp->fs_type, FSTAB_RQ))
145 				continue;
146 			if (preen == 0 ||
147 			    passno == 1 && fsp->fs_passno == 1) {
148 				name = blockcheck(fsp->fs_spec);
149 				if (name != NULL)
150 					checkfilesys(name);
151 				else if (preen)
152 					exit(8);
153 			} else if (passno == 2 && fsp->fs_passno > 1) {
154 				name = blockcheck(fsp->fs_spec);
155 				if (name == NULL) {
156 					pwarn("BAD DISK NAME %s\n",
157 						fsp->fs_spec);
158 					sumstatus |= 8;
159 					continue;
160 				}
161 				addpart(name, fsp->fs_file);
162 			}
163 		}
164 	}
165 	if (preen) {
166 		union wait status;
167 
168 		if (maxrun == 0)
169 			maxrun = ndisks;
170 		if (maxrun > ndisks)
171 			maxrun = ndisks;
172 		nextdisk = disks;
173 		for (passno = 0; passno < maxrun; ++passno) {
174 			startdisk(nextdisk);
175 			nextdisk = nextdisk->next;
176 		}
177 		while ((pid = wait(&status)) != -1) {
178 			for (dk = disks; dk; dk = dk->next)
179 				if (dk->pid == pid)
180 					break;
181 			if (dk == 0) {
182 				printf("Unknown pid %d\n", pid);
183 				continue;
184 			}
185 			if (status.w_termsig) {
186 				printf("%s (%s): EXITED WITH SIGNAL %d\n",
187 					dk->part->name, dk->part->fsname,
188 					status.w_termsig);
189 				status.w_retcode = 8;
190 			}
191 			if (status.w_retcode != 0) {
192 				sumstatus |= status.w_retcode;
193 				*badnext = dk->part;
194 				badnext = &dk->part->next;
195 				dk->part = dk->part->next;
196 				*badnext = NULL;
197 			} else
198 				dk->part = dk->part->next;
199 			dk->pid = 0;
200 			nrun--;
201 			if (dk->part == NULL)
202 				ndisks--;
203 
204 			if (nextdisk == NULL) {
205 				if (dk->part)
206 					startdisk(dk);
207 			} else if (nrun < maxrun && nrun < ndisks) {
208 				for ( ;; ) {
209 					if ((nextdisk = nextdisk->next) == NULL)
210 						nextdisk = disks;
211 					if (nextdisk->part != NULL &&
212 					    nextdisk->pid == 0)
213 						break;
214 				}
215 				startdisk(nextdisk);
216 			}
217 		}
218 	}
219 	if (sumstatus) {
220 		if (badlist == 0)
221 			exit(8);
222 		printf("THE FOLLOWING FILE SYSTEM%s HAD AN %s\n\t",
223 			badlist->next ? "S" : "", "UNEXPECTED INCONSISTENCY:");
224 		for (pt = badlist; pt; pt = pt->next)
225 			printf("%s (%s)%s", pt->name, pt->fsname,
226 			    pt->next ? ", " : "\n");
227 		exit(8);
228 	}
229 	(void)endfsent();
230 	if (returntosingle)
231 		exit(2);
232 	exit(0);
233 }
234 
235 struct disk *
236 finddisk(name)
237 	char *name;
238 {
239 	register struct disk *dk, **dkp;
240 	register char *p;
241 	int len;
242 
243 	for (p = name + strlen(name) - 1; p >= name; --p)
244 		if (isdigit(*p)) {
245 			len = p - name + 1;
246 			break;
247 		}
248 	if (p < name)
249 		len = strlen(name);
250 
251 	for (dk = disks, dkp = &disks; dk; dkp = &dk->next, dk = dk->next) {
252 		if (strncmp(dk->name, name, len) == 0 &&
253 		    dk->name[len] == 0)
254 			return (dk);
255 	}
256 	if ((*dkp = (struct disk *)malloc(sizeof(struct disk))) == NULL)
257 		errexit("out of memory");
258 	dk = *dkp;
259 	if ((dk->name = malloc((unsigned int)len + 1)) == NULL)
260 		errexit("out of memory");
261 	strncpy(dk->name, name, len);
262 	dk->name[len] = '\0';
263 	dk->part = NULL;
264 	dk->next = NULL;
265 	dk->pid = 0;
266 	ndisks++;
267 	return (dk);
268 }
269 
270 addpart(name, fsname)
271 	char *name, *fsname;
272 {
273 	struct disk *dk = finddisk(name);
274 	register struct part *pt, **ppt = &dk->part;
275 
276 	for (pt = dk->part; pt; ppt = &pt->next, pt = pt->next)
277 		if (strcmp(pt->name, name) == 0) {
278 			printf("%s in fstab more than once!\n", name);
279 			return;
280 		}
281 	if ((*ppt = (struct part *)malloc(sizeof(struct part))) == NULL)
282 		errexit("out of memory");
283 	pt = *ppt;
284 	if ((pt->name = malloc((unsigned int)strlen(name) + 1)) == NULL)
285 		errexit("out of memory");
286 	strcpy(pt->name, name);
287 	if ((pt->fsname = malloc((unsigned int)strlen(fsname) + 1)) == NULL)
288 		errexit("out of memory");
289 	strcpy(pt->fsname, fsname);
290 	pt->next = NULL;
291 }
292 
293 startdisk(dk)
294 	register struct disk *dk;
295 {
296 
297 	nrun++;
298 	dk->pid = fork();
299 	if (dk->pid < 0) {
300 		perror("fork");
301 		exit(8);
302 	}
303 	if (dk->pid == 0) {
304 		(void)signal(SIGQUIT, voidquit);
305 		checkfilesys(dk->part->name);
306 		exit(0);
307 	}
308 }
309 
310 checkfilesys(filesys)
311 	char *filesys;
312 {
313 	daddr_t n_ffree, n_bfree;
314 	struct dups *dp;
315 	struct zlncnt *zlnp;
316 
317 	devname = filesys;
318 	if (debug && preen)
319 		pwarn("starting\n");
320 	if (setup(filesys) == 0) {
321 		if (preen)
322 			pfatal("CAN'T CHECK FILE SYSTEM.");
323 		return;
324 	}
325 	/*
326 	 * 1: scan inodes tallying blocks used
327 	 */
328 	if (preen == 0) {
329 		printf("** Last Mounted on %s\n", sblock.fs_fsmnt);
330 		if (hotroot)
331 			printf("** Root file system\n");
332 		printf("** Phase 1 - Check Blocks and Sizes\n");
333 	}
334 	pass1();
335 
336 	/*
337 	 * 1b: locate first references to duplicates, if any
338 	 */
339 	if (duplist) {
340 		if (preen)
341 			pfatal("INTERNAL ERROR: dups with -p");
342 		printf("** Phase 1b - Rescan For More DUPS\n");
343 		pass1b();
344 	}
345 
346 	/*
347 	 * 2: traverse directories from root to mark all connected directories
348 	 */
349 	if (preen == 0)
350 		printf("** Phase 2 - Check Pathnames\n");
351 	pass2();
352 
353 	/*
354 	 * 3: scan inodes looking for disconnected directories
355 	 */
356 	if (preen == 0)
357 		printf("** Phase 3 - Check Connectivity\n");
358 	pass3();
359 
360 	/*
361 	 * 4: scan inodes looking for disconnected files; check reference counts
362 	 */
363 	if (preen == 0)
364 		printf("** Phase 4 - Check Reference Counts\n");
365 	pass4();
366 
367 	/*
368 	 * 5: check and repair resource counts in cylinder groups
369 	 */
370 	if (preen == 0)
371 		printf("** Phase 5 - Check Cyl groups\n");
372 	pass5();
373 
374 	/*
375 	 * print out summary statistics
376 	 */
377 	n_ffree = sblock.fs_cstotal.cs_nffree;
378 	n_bfree = sblock.fs_cstotal.cs_nbfree;
379 	pwarn("%d files, %d used, %d free ",
380 	    n_files, n_blks, n_ffree + sblock.fs_frag * n_bfree);
381 	printf("(%d frags, %d blocks, %.1f%% fragmentation)\n",
382 	    n_ffree, n_bfree, (float)(n_ffree * 100) / sblock.fs_dsize);
383 	if (debug &&
384 	    (n_files -= maxino - ROOTINO - sblock.fs_cstotal.cs_nifree))
385 		printf("%d files missing\n", n_files);
386 	if (debug) {
387 		n_blks += sblock.fs_ncg *
388 			(cgdmin(&sblock, 0) - cgsblock(&sblock, 0));
389 		n_blks += cgsblock(&sblock, 0) - cgbase(&sblock, 0);
390 		n_blks += howmany(sblock.fs_cssize, sblock.fs_fsize);
391 		if (n_blks -= maxfsblock - (n_ffree + sblock.fs_frag * n_bfree))
392 			printf("%d blocks missing\n", n_blks);
393 		if (duplist != NULL) {
394 			printf("The following duplicate blocks remain:");
395 			for (dp = duplist; dp; dp = dp->next)
396 				printf(" %d,", dp->dup);
397 			printf("\n");
398 		}
399 		if (zlnhead != NULL) {
400 			printf("The following zero link count inodes remain:");
401 			for (zlnp = zlnhead; zlnp; zlnp = zlnp->next)
402 				printf(" %d,", zlnp->zlncnt);
403 			printf("\n");
404 		}
405 	}
406 	zlnhead = (struct zlncnt *)0;
407 	duplist = (struct dups *)0;
408 	if (fsmodified) {
409 		(void)time(&sblock.fs_time);
410 		sbdirty();
411 	}
412 	ckfini();
413 	free(blockmap);
414 	free(statemap);
415 	free((char *)lncntp);
416 	if (!fsmodified)
417 		return;
418 	if (!preen) {
419 		printf("\n***** FILE SYSTEM WAS MODIFIED *****\n");
420 		if (hotroot)
421 			printf("\n***** REBOOT UNIX *****\n");
422 	}
423 	if (hotroot) {
424 		sync();
425 		exit(4);
426 	}
427 }
428 
429 char *
430 blockcheck(name)
431 	char *name;
432 {
433 	struct stat stslash, stblock, stchar;
434 	char *raw;
435 	int retried = 0;
436 
437 	hotroot = 0;
438 	if (stat("/", &stslash) < 0) {
439 		perror("/");
440 		printf("Can't stat root\n");
441 		return (0);
442 	}
443 retry:
444 	if (stat(name, &stblock) < 0) {
445 		perror(name);
446 		printf("Can't stat %s\n", name);
447 		return (0);
448 	}
449 	if ((stblock.st_mode & S_IFMT) == S_IFBLK) {
450 		if (stslash.st_dev == stblock.st_rdev) {
451 			hotroot++;
452 			return (name);
453 		}
454 		raw = rawname(name);
455 		if (stat(raw, &stchar) < 0) {
456 			perror(raw);
457 			printf("Can't stat %s\n", raw);
458 			return (name);
459 		}
460 		if ((stchar.st_mode & S_IFMT) == S_IFCHR) {
461 			return (raw);
462 		} else {
463 			printf("%s is not a character device\n", raw);
464 			return (name);
465 		}
466 	} else if ((stblock.st_mode & S_IFMT) == S_IFCHR && !retried) {
467 		name = unrawname(name);
468 		retried++;
469 		goto retry;
470 	}
471 	printf("Can't make sense out of name %s\n", name);
472 	return (0);
473 }
474 
475 char *
476 unrawname(name)
477 	char *name;
478 {
479 	char *dp;
480 	struct stat stb;
481 
482 	if ((dp = rindex(name, '/')) == 0)
483 		return (name);
484 	if (stat(name, &stb) < 0)
485 		return (name);
486 	if ((stb.st_mode & S_IFMT) != S_IFCHR)
487 		return (name);
488 	if (*(dp + 1) != 'r')
489 		return (name);
490 	(void)strcpy(dp + 1, dp + 2);
491 	return (name);
492 }
493 
494 char *
495 rawname(name)
496 	char *name;
497 {
498 	static char rawbuf[32];
499 	char *dp;
500 
501 	if ((dp = rindex(name, '/')) == 0)
502 		return (0);
503 	*dp = 0;
504 	(void)strcpy(rawbuf, name);
505 	*dp = '/';
506 	(void)strcat(rawbuf, "/r");
507 	(void)strcat(rawbuf, dp + 1);
508 	return (rawbuf);
509 }
510