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