xref: /csrg-svn/sbin/fsck/main.c (revision 40377)
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.20 (Berkeley) 03/09/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 (strcmp(fsp->fs_vfstype, "ufs"))
147 				continue;
148 			if (preen == 0 ||
149 			    passno == 1 && fsp->fs_passno == 1) {
150 				name = blockcheck(fsp->fs_spec);
151 				if (name != NULL)
152 					checkfilesys(name);
153 				else if (preen)
154 					exit(8);
155 			} else if (passno == 2 && fsp->fs_passno > 1) {
156 				name = blockcheck(fsp->fs_spec);
157 				if (name == NULL) {
158 					pwarn("BAD DISK NAME %s\n",
159 						fsp->fs_spec);
160 					sumstatus |= 8;
161 					continue;
162 				}
163 				addpart(name, fsp->fs_file);
164 			}
165 		}
166 	}
167 	if (preen) {
168 		union wait status;
169 
170 		if (maxrun == 0)
171 			maxrun = ndisks;
172 		if (maxrun > ndisks)
173 			maxrun = ndisks;
174 		nextdisk = disks;
175 		for (passno = 0; passno < maxrun; ++passno) {
176 			startdisk(nextdisk);
177 			nextdisk = nextdisk->next;
178 		}
179 		while ((pid = wait(&status)) != -1) {
180 			for (dk = disks; dk; dk = dk->next)
181 				if (dk->pid == pid)
182 					break;
183 			if (dk == 0) {
184 				printf("Unknown pid %d\n", pid);
185 				continue;
186 			}
187 			if (status.w_termsig) {
188 				printf("%s (%s): EXITED WITH SIGNAL %d\n",
189 					dk->part->name, dk->part->fsname,
190 					status.w_termsig);
191 				status.w_retcode = 8;
192 			}
193 			if (status.w_retcode != 0) {
194 				sumstatus |= status.w_retcode;
195 				*badnext = dk->part;
196 				badnext = &dk->part->next;
197 				dk->part = dk->part->next;
198 				*badnext = NULL;
199 			} else
200 				dk->part = dk->part->next;
201 			dk->pid = 0;
202 			nrun--;
203 			if (dk->part == NULL)
204 				ndisks--;
205 
206 			if (nextdisk == NULL) {
207 				if (dk->part)
208 					startdisk(dk);
209 			} else if (nrun < maxrun && nrun < ndisks) {
210 				for ( ;; ) {
211 					if ((nextdisk = nextdisk->next) == NULL)
212 						nextdisk = disks;
213 					if (nextdisk->part != NULL &&
214 					    nextdisk->pid == 0)
215 						break;
216 				}
217 				startdisk(nextdisk);
218 			}
219 		}
220 	}
221 	if (sumstatus) {
222 		if (badlist == 0)
223 			exit(8);
224 		printf("THE FOLLOWING FILE SYSTEM%s HAD AN %s\n\t",
225 			badlist->next ? "S" : "", "UNEXPECTED INCONSISTENCY:");
226 		for (pt = badlist; pt; pt = pt->next)
227 			printf("%s (%s)%s", pt->name, pt->fsname,
228 			    pt->next ? ", " : "\n");
229 		exit(8);
230 	}
231 	(void)endfsent();
232 	if (returntosingle)
233 		exit(2);
234 	exit(0);
235 }
236 
237 struct disk *
238 finddisk(name)
239 	char *name;
240 {
241 	register struct disk *dk, **dkp;
242 	register char *p;
243 	int len;
244 
245 	for (p = name + strlen(name) - 1; p >= name; --p)
246 		if (isdigit(*p)) {
247 			len = p - name + 1;
248 			break;
249 		}
250 	if (p < name)
251 		len = strlen(name);
252 
253 	for (dk = disks, dkp = &disks; dk; dkp = &dk->next, dk = dk->next) {
254 		if (strncmp(dk->name, name, len) == 0 &&
255 		    dk->name[len] == 0)
256 			return (dk);
257 	}
258 	if ((*dkp = (struct disk *)malloc(sizeof(struct disk))) == NULL)
259 		errexit("out of memory");
260 	dk = *dkp;
261 	if ((dk->name = malloc((unsigned int)len + 1)) == NULL)
262 		errexit("out of memory");
263 	strncpy(dk->name, name, len);
264 	dk->name[len] = '\0';
265 	dk->part = NULL;
266 	dk->next = NULL;
267 	dk->pid = 0;
268 	ndisks++;
269 	return (dk);
270 }
271 
272 addpart(name, fsname)
273 	char *name, *fsname;
274 {
275 	struct disk *dk = finddisk(name);
276 	register struct part *pt, **ppt = &dk->part;
277 
278 	for (pt = dk->part; pt; ppt = &pt->next, pt = pt->next)
279 		if (strcmp(pt->name, name) == 0) {
280 			printf("%s in fstab more than once!\n", name);
281 			return;
282 		}
283 	if ((*ppt = (struct part *)malloc(sizeof(struct part))) == NULL)
284 		errexit("out of memory");
285 	pt = *ppt;
286 	if ((pt->name = malloc((unsigned int)strlen(name) + 1)) == NULL)
287 		errexit("out of memory");
288 	strcpy(pt->name, name);
289 	if ((pt->fsname = malloc((unsigned int)strlen(fsname) + 1)) == NULL)
290 		errexit("out of memory");
291 	strcpy(pt->fsname, fsname);
292 	pt->next = NULL;
293 }
294 
295 startdisk(dk)
296 	register struct disk *dk;
297 {
298 
299 	nrun++;
300 	dk->pid = fork();
301 	if (dk->pid < 0) {
302 		perror("fork");
303 		exit(8);
304 	}
305 	if (dk->pid == 0) {
306 		(void)signal(SIGQUIT, voidquit);
307 		checkfilesys(dk->part->name);
308 		exit(0);
309 	}
310 }
311 
312 checkfilesys(filesys)
313 	char *filesys;
314 {
315 	daddr_t n_ffree, n_bfree;
316 	struct dups *dp;
317 	struct zlncnt *zlnp;
318 
319 	devname = filesys;
320 	if (debug && preen)
321 		pwarn("starting\n");
322 	if (setup(filesys) == 0) {
323 		if (preen)
324 			pfatal("CAN'T CHECK FILE SYSTEM.");
325 		return;
326 	}
327 	/*
328 	 * 1: scan inodes tallying blocks used
329 	 */
330 	if (preen == 0) {
331 		printf("** Last Mounted on %s\n", sblock.fs_fsmnt);
332 		if (hotroot)
333 			printf("** Root file system\n");
334 		printf("** Phase 1 - Check Blocks and Sizes\n");
335 	}
336 	pass1();
337 
338 	/*
339 	 * 1b: locate first references to duplicates, if any
340 	 */
341 	if (duplist) {
342 		if (preen)
343 			pfatal("INTERNAL ERROR: dups with -p");
344 		printf("** Phase 1b - Rescan For More DUPS\n");
345 		pass1b();
346 	}
347 
348 	/*
349 	 * 2: traverse directories from root to mark all connected directories
350 	 */
351 	if (preen == 0)
352 		printf("** Phase 2 - Check Pathnames\n");
353 	pass2();
354 
355 	/*
356 	 * 3: scan inodes looking for disconnected directories
357 	 */
358 	if (preen == 0)
359 		printf("** Phase 3 - Check Connectivity\n");
360 	pass3();
361 
362 	/*
363 	 * 4: scan inodes looking for disconnected files; check reference counts
364 	 */
365 	if (preen == 0)
366 		printf("** Phase 4 - Check Reference Counts\n");
367 	pass4();
368 
369 	/*
370 	 * 5: check and repair resource counts in cylinder groups
371 	 */
372 	if (preen == 0)
373 		printf("** Phase 5 - Check Cyl groups\n");
374 	pass5();
375 
376 	/*
377 	 * print out summary statistics
378 	 */
379 	n_ffree = sblock.fs_cstotal.cs_nffree;
380 	n_bfree = sblock.fs_cstotal.cs_nbfree;
381 	pwarn("%d files, %d used, %d free ",
382 	    n_files, n_blks, n_ffree + sblock.fs_frag * n_bfree);
383 	printf("(%d frags, %d blocks, %.1f%% fragmentation)\n",
384 	    n_ffree, n_bfree, (float)(n_ffree * 100) / sblock.fs_dsize);
385 	if (debug &&
386 	    (n_files -= maxino - ROOTINO - sblock.fs_cstotal.cs_nifree))
387 		printf("%d files missing\n", n_files);
388 	if (debug) {
389 		n_blks += sblock.fs_ncg *
390 			(cgdmin(&sblock, 0) - cgsblock(&sblock, 0));
391 		n_blks += cgsblock(&sblock, 0) - cgbase(&sblock, 0);
392 		n_blks += howmany(sblock.fs_cssize, sblock.fs_fsize);
393 		if (n_blks -= maxfsblock - (n_ffree + sblock.fs_frag * n_bfree))
394 			printf("%d blocks missing\n", n_blks);
395 		if (duplist != NULL) {
396 			printf("The following duplicate blocks remain:");
397 			for (dp = duplist; dp; dp = dp->next)
398 				printf(" %d,", dp->dup);
399 			printf("\n");
400 		}
401 		if (zlnhead != NULL) {
402 			printf("The following zero link count inodes remain:");
403 			for (zlnp = zlnhead; zlnp; zlnp = zlnp->next)
404 				printf(" %d,", zlnp->zlncnt);
405 			printf("\n");
406 		}
407 	}
408 	zlnhead = (struct zlncnt *)0;
409 	duplist = (struct dups *)0;
410 	inocleanup();
411 	if (fsmodified) {
412 		(void)time(&sblock.fs_time);
413 		sbdirty();
414 	}
415 	ckfini();
416 	free(blockmap);
417 	free(statemap);
418 	free((char *)lncntp);
419 	if (!fsmodified)
420 		return;
421 	if (!preen) {
422 		printf("\n***** FILE SYSTEM WAS MODIFIED *****\n");
423 		if (hotroot)
424 			printf("\n***** REBOOT UNIX *****\n");
425 	}
426 	if (hotroot) {
427 		sync();
428 		exit(4);
429 	}
430 }
431 
432 char *
433 blockcheck(name)
434 	char *name;
435 {
436 	struct stat stslash, stblock, stchar;
437 	char *raw;
438 	int retried = 0;
439 
440 	hotroot = 0;
441 	if (stat("/", &stslash) < 0) {
442 		perror("/");
443 		printf("Can't stat root\n");
444 		return (0);
445 	}
446 retry:
447 	if (stat(name, &stblock) < 0) {
448 		perror(name);
449 		printf("Can't stat %s\n", name);
450 		return (0);
451 	}
452 	if ((stblock.st_mode & S_IFMT) == S_IFBLK) {
453 		if (stslash.st_dev == stblock.st_rdev)
454 			hotroot++;
455 		raw = rawname(name);
456 		if (stat(raw, &stchar) < 0) {
457 			perror(raw);
458 			printf("Can't stat %s\n", raw);
459 			return (name);
460 		}
461 		if ((stchar.st_mode & S_IFMT) == S_IFCHR) {
462 			return (raw);
463 		} else {
464 			printf("%s is not a character device\n", raw);
465 			return (name);
466 		}
467 	} else if ((stblock.st_mode & S_IFMT) == S_IFCHR && !retried) {
468 		name = unrawname(name);
469 		retried++;
470 		goto retry;
471 	}
472 	printf("Can't make sense out of name %s\n", name);
473 	return (0);
474 }
475 
476 char *
477 unrawname(name)
478 	char *name;
479 {
480 	char *dp;
481 	struct stat stb;
482 
483 	if ((dp = rindex(name, '/')) == 0)
484 		return (name);
485 	if (stat(name, &stb) < 0)
486 		return (name);
487 	if ((stb.st_mode & S_IFMT) != S_IFCHR)
488 		return (name);
489 	if (*(dp + 1) != 'r')
490 		return (name);
491 	(void)strcpy(dp + 1, dp + 2);
492 	return (name);
493 }
494 
495 char *
496 rawname(name)
497 	char *name;
498 {
499 	static char rawbuf[32];
500 	char *dp;
501 
502 	if ((dp = rindex(name, '/')) == 0)
503 		return (0);
504 	*dp = 0;
505 	(void)strcpy(rawbuf, name);
506 	*dp = '/';
507 	(void)strcat(rawbuf, "/r");
508 	(void)strcat(rawbuf, dp + 1);
509 	return (rawbuf);
510 }
511