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