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