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