xref: /original-bsd/sbin/fsck/main.c (revision 950ddd82)
1 char version[] = "@(#)main.c	2.20	(Berkeley)	01/19/83";
2 
3 #include <stdio.h>
4 #include <ctype.h>
5 #include <sys/param.h>
6 #include <sys/fs.h>
7 #include <sys/inode.h>
8 #include <dir.h>
9 #include <sys/stat.h>
10 #include <fstab.h>
11 
12 /* RECONSTRUCT ONLY BAD CG IN PASS 6 */
13 
14 typedef	int	(*SIG_TYP)();
15 
16 #define	MAXNINDIR	(MAXBSIZE / sizeof (daddr_t))
17 #define	MAXINOPB	(MAXBSIZE / sizeof (struct dinode))
18 #define	SPERB		(MAXBSIZE / sizeof(short))
19 
20 #define	MAXDUP	10		/* limit on dup blks (per inode) */
21 #define	MAXBAD	10		/* limit on bad blks (per inode) */
22 
23 #define	USTATE	0		/* inode not allocated */
24 #define	FSTATE	01		/* inode is file */
25 #define	DSTATE	02		/* inode is directory */
26 #define	CLEAR	03		/* inode is to be cleared */
27 
28 typedef struct dinode	DINODE;
29 typedef struct direct	DIRECT;
30 
31 #define	ALLOC	((dp->di_mode & IFMT) != 0)
32 #define	DIRCT	((dp->di_mode & IFMT) == IFDIR)
33 #define	REG	((dp->di_mode & IFMT) == IFREG)
34 #define	BLK	((dp->di_mode & IFMT) == IFBLK)
35 #define	CHR	((dp->di_mode & IFMT) == IFCHR)
36 #define	LNK	((dp->di_mode & IFMT) == IFLNK)
37 #define	SPECIAL	(BLK || CHR)
38 
39 ino_t	startinum;		/* blk num of first in raw area */
40 
41 struct bufarea {
42 	struct bufarea	*b_next;		/* must be first */
43 	daddr_t	b_bno;
44 	int	b_size;
45 	union {
46 		char	b_buf[MAXBSIZE];	/* buffer space */
47 		short	b_lnks[SPERB];		/* link counts */
48 		daddr_t	b_indir[MAXNINDIR];	/* indirect block */
49 		struct	fs b_fs;		/* super block */
50 		struct	cg b_cg;		/* cylinder group */
51 		struct dinode b_dinode[MAXINOPB]; /* inode block */
52 	} b_un;
53 	char	b_dirty;
54 };
55 
56 typedef struct bufarea BUFAREA;
57 
58 BUFAREA	inoblk;			/* inode blocks */
59 BUFAREA	fileblk;		/* other blks in filesys */
60 BUFAREA	sblk;			/* file system superblock */
61 BUFAREA	cgblk;
62 
63 #define	initbarea(x)	(x)->b_dirty = 0;(x)->b_bno = (daddr_t)-1
64 #define	dirty(x)	(x)->b_dirty = 1
65 #define	inodirty()	inoblk.b_dirty = 1
66 #define	sbdirty()	sblk.b_dirty = 1
67 #define	cgdirty()	cgblk.b_dirty = 1
68 
69 #define	dirblk		fileblk.b_un
70 #define	sblock		sblk.b_un.b_fs
71 #define	cgrp		cgblk.b_un.b_cg
72 
73 struct filecntl {
74 	int	rfdes;
75 	int	wfdes;
76 	int	mod;
77 } dfile;			/* file descriptors for filesys */
78 
79 #define	DUPTBLSIZE	100	/* num of dup blocks to remember */
80 daddr_t	duplist[DUPTBLSIZE];	/* dup block table */
81 daddr_t	*enddup;		/* next entry in dup table */
82 daddr_t	*muldup;		/* multiple dups part of table */
83 
84 #define	MAXLNCNT	500	/* num zero link cnts to remember */
85 ino_t	badlncnt[MAXLNCNT];	/* table of inos with zero link cnts */
86 ino_t	*badlnp;		/* next entry in table */
87 
88 char	rawflg;
89 char	nflag;			/* assume a no response */
90 char	yflag;			/* assume a yes response */
91 int	bflag;			/* location of alternate super block */
92 int	debug;			/* output debugging info */
93 char	preen;			/* just fix normal inconsistencies */
94 char	rplyflag;		/* any questions asked? */
95 char	hotroot;		/* checking root device */
96 char	fixcg;			/* corrupted free list bit maps */
97 
98 char	*blockmap;		/* ptr to primary blk allocation map */
99 char	*freemap;		/* ptr to secondary blk allocation map */
100 char	*statemap;		/* ptr to inode state table */
101 short	*lncntp;		/* ptr to link count table */
102 
103 char	*pathp;			/* pointer to pathname position */
104 char	*thisname;		/* ptr to current pathname component */
105 char	*srchname;		/* name being searched for in dir */
106 char	pathname[BUFSIZ];
107 
108 char	*lfname = "lost+found";
109 
110 ino_t	inum;			/* inode we are currently working on */
111 ino_t	dnum;			/* directory inode currently being worked on */
112 ino_t	imax;			/* number of inodes */
113 ino_t	parentdir;		/* i number of parent directory */
114 ino_t	lastino;		/* hiwater mark of inodes */
115 ino_t	lfdir;			/* lost & found directory */
116 ino_t	orphan;			/* orphaned inode */
117 
118 off_t	filsize;		/* num blks seen in file */
119 off_t	maxblk;			/* largest logical blk in file */
120 off_t	bmapsz;			/* num chars in blockmap */
121 
122 daddr_t	n_ffree;		/* number of small free blocks */
123 daddr_t	n_bfree;		/* number of large free blocks */
124 daddr_t	n_blks;			/* number of blocks used */
125 daddr_t	n_files;		/* number of files seen */
126 daddr_t	n_index;
127 daddr_t	n_bad;
128 daddr_t	fmax;			/* number of blocks in the volume */
129 
130 daddr_t	badblk;
131 daddr_t	dupblk;
132 
133 int	inosumbad;
134 int	offsumbad;
135 int	frsumbad;
136 int	sbsumbad;
137 
138 #define	zapino(x)	(*(x) = zino)
139 struct	dinode zino;
140 
141 #define	setbmap(x)	setbit(blockmap, x)
142 #define	getbmap(x)	isset(blockmap, x)
143 #define	clrbmap(x)	clrbit(blockmap, x)
144 
145 #define	setfmap(x)	setbit(freemap, x)
146 #define	getfmap(x)	isset(freemap, x)
147 #define	clrfmap(x)	clrbit(freemap, x)
148 
149 #define	DATA	1
150 #define	ADDR	0
151 
152 #define	ALTERD	010
153 #define	KEEPON	04
154 #define	SKIP	02
155 #define	STOP	01
156 
157 int	(*signal())();
158 long	lseek();
159 time_t	time();
160 DINODE	*ginode();
161 BUFAREA	*getblk();
162 int	dirscan();
163 int	findino();
164 int	catch();
165 int	mkentry();
166 int	chgdd();
167 int	pass1check(), pass1bcheck(), pass2check(), pass4check(), pass5check();
168 int	(*pfunc)();
169 char	*rawname(), *rindex(), *unrawname();
170 extern int inside[], around[];
171 extern unsigned char *fragtbl[];
172 
173 char	*devname;
174 
175 main(argc, argv)
176 	int	argc;
177 	char	*argv[];
178 {
179 	struct fstab *fsp;
180 	int pid, passno, anygtr, sumstatus;
181 
182 	sync();
183 	while (--argc > 0 && **++argv == '-') {
184 		switch (*++*argv) {
185 
186 		case 'p':
187 			preen++;
188 			break;
189 
190 		case 'b':
191 			bflag = atoi(argv[0]+1);
192 			printf("Alternate super block location: %d\n", bflag);
193 			break;
194 
195 		case 'd':
196 			debug++;
197 			break;
198 
199 		case 'n':	/* default no answer flag */
200 		case 'N':
201 			nflag++;
202 			yflag = 0;
203 			break;
204 
205 		case 'y':	/* default yes answer flag */
206 		case 'Y':
207 			yflag++;
208 			nflag = 0;
209 			break;
210 
211 		default:
212 			errexit("%c option?\n", **argv);
213 		}
214 	}
215 	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
216 		signal(SIGINT, catch);
217 	if (argc) {
218 		while (argc-- > 0) {
219 			hotroot = 0;
220 			checkfilesys(*argv++);
221 		}
222 		exit(0);
223 	}
224 	sumstatus = 0;
225 	passno = 1;
226 	do {
227 		anygtr = 0;
228 		if (setfsent() == 0)
229 			errexit("Can't open checklist file: %s\n", FSTAB);
230 		while ((fsp = getfsent()) != 0) {
231 			if (strcmp(fsp->fs_type, FSTAB_RW) &&
232 			    strcmp(fsp->fs_type, FSTAB_RO))
233 				continue;
234 			if (preen == 0 ||
235 			    passno == 1 && fsp->fs_passno == passno) {
236 				if (blockcheck(fsp->fs_spec) == 0 && preen)
237 					exit(8);
238 			} else if (fsp->fs_passno > passno)
239 				anygtr = 1;
240 			else if (fsp->fs_passno == passno) {
241 				pid = fork();
242 				if (pid < 0) {
243 					perror("fork");
244 					exit(8);
245 				}
246 				if (pid == 0)
247 					if (blockcheck(fsp->fs_spec)==0)
248 						exit(8);
249 					else
250 						exit(0);
251 			}
252 		}
253 		if (preen) {
254 			int status;
255 			while (wait(&status) != -1)
256 				sumstatus |= status;
257 		}
258 		passno++;
259 	} while (anygtr);
260 	if (sumstatus)
261 		exit(8);
262 	endfsent();
263 	exit(0);
264 }
265 
266 blockcheck(name)
267 	char *name;
268 {
269 	struct stat stslash, stblock, stchar;
270 	char *raw;
271 	int looped = 0;
272 
273 	hotroot = 0;
274 	if (stat("/", &stslash) < 0){
275 		error("Can't stat root\n");
276 		return (0);
277 	}
278 retry:
279 	if (stat(name, &stblock) < 0){
280 		error("Can't stat %s\n", name);
281 		return (0);
282 	}
283 	if (stblock.st_mode & S_IFBLK) {
284 		raw = rawname(name);
285 		if (stat(raw, &stchar) < 0){
286 			error("Can't stat %s\n", raw);
287 			return (0);
288 		}
289 		if (stchar.st_mode & S_IFCHR) {
290 			if (stslash.st_dev == stblock.st_rdev) {
291 				hotroot++;
292 				raw = rawname(name);
293 			}
294 			checkfilesys(raw);
295 			return (1);
296 		} else {
297 			error("%s is not a character device\n", raw);
298 			return (0);
299 		}
300 	} else if (stblock.st_mode & S_IFCHR) {
301 		if (looped) {
302 			error("Can't make sense out of name %s\n", name);
303 			return (0);
304 		}
305 		name = unrawname(name);
306 		looped++;
307 		goto retry;
308 	}
309 	error("Can't make sense out of name %s\n", name);
310 	return (0);
311 }
312 
313 checkfilesys(filesys)
314 	char *filesys;
315 {
316 	register DINODE *dp;
317 	register ino_t *blp;
318 	register int i, n;
319 	ino_t savino;
320 	int b, c, j, partial, ndb;
321 	daddr_t d, s;
322 
323 	devname = filesys;
324 	if (setup(filesys) == 0) {
325 		if (preen)
326 			pfatal("CAN'T CHECK FILE SYSTEM.");
327 		return;
328 	}
329 /* 1: scan inodes tallying blocks used */
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 /* 1b: locate first references to duplicates, if any */
339 	if (enddup != &duplist[0]) {
340 		if (preen)
341 			pfatal("INTERNAL ERROR: dups with -p");
342 		printf("** Phase 1b - Rescan For More DUPS\n");
343 		pass1b();
344 	}
345 
346 /* 2: traverse directories to check reference counts */
347 	if (preen == 0)
348 		printf("** Phase 2 - Check Pathnames\n");
349 	pass2();
350 
351 /* 3 */
352 	if (preen == 0)
353 		printf("** Phase 3 - Check Connectivity\n");
354 	pass3();
355 
356 /* 4 */
357 	if (preen == 0)
358 		printf("** Phase 4 - Check Reference Counts\n");
359 	pass4();
360 
361 /* 5 */
362 	if (preen == 0)
363 		printf("** Phase 5 - Check Cyl groups\n");
364 	pass5();
365 
366 	if (fixcg) {
367 		if (preen == 0)
368 			printf("** Phase 6 - Salvage Cylinder Groups\n");
369 		makecg();
370 		n_ffree = sblock.fs_cstotal.cs_nffree;
371 		n_bfree = sblock.fs_cstotal.cs_nbfree;
372 	}
373 
374 	pwarn("%d files, %d used, %d free (%d frags, %d blocks)\n",
375 	    n_files, n_blks - howmany(sblock.fs_cssize, sblock.fs_fsize),
376 	    n_ffree + sblock.fs_frag * n_bfree, n_ffree, n_bfree);
377 	if (dfile.mod) {
378 		time(&sblock.fs_time);
379 		sbdirty();
380 	}
381 	ckfini();
382 	free(blockmap);
383 	free(freemap);
384 	free(statemap);
385 	free(lncntp);
386 	if (dfile.mod) {
387 		if (preen) {
388 			if (hotroot)
389 				exit(4);
390 		} else {
391 			printf("\n***** FILE SYSTEM WAS MODIFIED *****\n");
392 			if (hotroot) {
393 				printf("\n***** BOOT UNIX (NO SYNC!) *****\n");
394 				exit(4);
395 			}
396 		}
397 	}
398 	sync();			/* ??? */
399 }
400 
401 setup(dev)
402 	char *dev;
403 {
404 	dev_t rootdev;
405 	struct stat statb;
406 	int super = bflag ? bflag : SBLOCK;
407 	int i, j, size;
408 	int c, d, cgd;
409 
410 	bflag = 0;
411 	if (stat("/", &statb) < 0)
412 		errexit("Can't stat root\n");
413 	rootdev = statb.st_dev;
414 	if (stat(dev, &statb) < 0) {
415 		error("Can't stat %s\n", dev);
416 		return (0);
417 	}
418 	rawflg = 0;
419 	if ((statb.st_mode & S_IFMT) == S_IFBLK)
420 		;
421 	else if ((statb.st_mode & S_IFMT) == S_IFCHR)
422 		rawflg++;
423 	else {
424 		if (reply("file is not a block or character device; OK") == 0)
425 			return (0);
426 	}
427 	if (rootdev == statb.st_rdev)
428 		hotroot++;
429 	if ((dfile.rfdes = open(dev, 0)) < 0) {
430 		error("Can't open %s\n", dev);
431 		return (0);
432 	}
433 	if (preen == 0)
434 		printf("** %s", dev);
435 	if (nflag || (dfile.wfdes = open(dev, 1)) < 0) {
436 		dfile.wfdes = -1;
437 		if (preen)
438 			pfatal("NO WRITE ACCESS");
439 		printf(" (NO WRITE)");
440 	}
441 	if (preen == 0)
442 		printf("\n");
443 	fixcg = 0; inosumbad = 0; offsumbad = 0; frsumbad = 0; sbsumbad = 0;
444 	dfile.mod = 0;
445 	n_files = n_blks = n_ffree = n_bfree = 0;
446 	muldup = enddup = &duplist[0];
447 	badlnp = &badlncnt[0];
448 	lfdir = 0;
449 	rplyflag = 0;
450 	initbarea(&sblk);
451 	initbarea(&fileblk);
452 	initbarea(&inoblk);
453 	initbarea(&cgblk);
454 	/*
455 	 * Read in the super block and its summary info.
456 	 */
457 	if (bread(&dfile, &sblock, super, SBSIZE) == 0)
458 		return (0);
459 	sblk.b_bno = super;
460 	sblk.b_size = SBSIZE;
461 	/*
462 	 * run a few consistency checks of the super block
463 	 */
464 	if (sblock.fs_magic != FS_MAGIC)
465 		{ badsb("MAGIC NUMBER WRONG"); return (0); }
466 	if (sblock.fs_ncg < 1)
467 		{ badsb("NCG OUT OF RANGE"); return (0); }
468 	if (sblock.fs_cpg < 1 || sblock.fs_cpg > MAXCPG)
469 		{ badsb("CPG OUT OF RANGE"); return (0); }
470 	if (sblock.fs_nsect < 1)
471 		{ badsb("NSECT < 1"); return (0); }
472 	if (sblock.fs_ntrak < 1)
473 		{ badsb("NTRAK < 1"); return (0); }
474 	if (sblock.fs_spc != sblock.fs_nsect * sblock.fs_ntrak)
475 		{ badsb("SPC DOES NOT JIVE w/NTRAK*NSECT"); return (0); }
476 	if (sblock.fs_ipg % INOPB(&sblock))
477 		{ badsb("INODES NOT MULTIPLE OF A BLOCK"); return (0); }
478 	if (cgdmin(&sblock, 0) >= sblock.fs_cpg * sblock.fs_spc / NSPF(&sblock))
479 		{ badsb("IMPLIES MORE INODE THAN DATA BLOCKS"); return (0); }
480 	if (sblock.fs_ncg * sblock.fs_cpg < sblock.fs_ncyl ||
481 	    (sblock.fs_ncg - 1) * sblock.fs_cpg >= sblock.fs_ncyl)
482 		{ badsb("NCYL DOES NOT JIVE WITH NCG*CPG"); return (0); }
483 	if (sblock.fs_fpg != sblock.fs_cpg * sblock.fs_spc / NSPF(&sblock))
484 		{ badsb("FPG DOES NOT JIVE WITH CPG & SPC"); return (0); }
485 	if (sblock.fs_size * NSPF(&sblock) <=
486 	    (sblock.fs_ncyl - 1) * sblock.fs_spc)
487 		{ badsb("SIZE PREPOSTEROUSLY SMALL"); return (0); }
488 	if (sblock.fs_size * NSPF(&sblock) > sblock.fs_ncyl * sblock.fs_spc)
489 		{ badsb("SIZE PREPOSTEROUSLY LARGE"); return (0); }
490 	/* rest we COULD repair... */
491 	if (sblock.fs_cgsize != fragroundup(&sblock,
492 	    sizeof(struct cg) + howmany(sblock.fs_fpg, NBBY)))
493 		{ badsb("CGSIZE INCORRECT"); return (0); }
494 	if (sblock.fs_cssize !=
495 	    fragroundup(&sblock, sblock.fs_ncg * sizeof(struct csum)))
496 		{ badsb("CSSIZE INCORRECT"); return (0); }
497 	fmax = sblock.fs_size;
498 	imax = sblock.fs_ncg * sblock.fs_ipg;
499 	n_bad = cgsblock(&sblock, 0); /* boot block plus dedicated sblock */
500 	/*
501 	 * read in the summary info.
502 	 */
503 	for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) {
504 		size = sblock.fs_cssize - i < sblock.fs_bsize ?
505 		    sblock.fs_cssize - i : sblock.fs_bsize;
506 		sblock.fs_csp[j] = (struct csum *)calloc(1, size);
507 		bread(&dfile, (char *)sblock.fs_csp[j],
508 		    fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag),
509 		    size);
510 	}
511 	/*
512 	 * allocate and initialize the necessary maps
513 	 */
514 	bmapsz = roundup(howmany(fmax, NBBY), sizeof(short));
515 	blockmap = (char *)calloc(bmapsz, sizeof (char));
516 	if (blockmap == NULL) {
517 		printf("cannot alloc %d bytes for blockmap\n", bmapsz);
518 		exit(1);
519 	}
520 	freemap = (char *)calloc(bmapsz, sizeof (char));
521 	if (freemap == NULL) {
522 		printf("cannot alloc %d bytes for freemap\n", bmapsz);
523 		exit(1);
524 	}
525 	statemap = (char *)calloc(imax+1, sizeof(char));
526 	if (statemap == NULL) {
527 		printf("cannot alloc %d bytes for statemap\n", imax + 1);
528 		exit(1);
529 	}
530 	lncntp = (short *)calloc(imax+1, sizeof(short));
531 	if (lncntp == NULL) {
532 		printf("cannot alloc %d bytes for lncntp\n",
533 		    (imax + 1) * sizeof(short));
534 		exit(1);
535 	}
536 	for (c = 0; c < sblock.fs_ncg; c++) {
537 		cgd = cgdmin(&sblock, c);
538 		if (c == 0) {
539 			d = cgbase(&sblock, c);
540 			cgd += howmany(sblock.fs_cssize, sblock.fs_fsize);
541 		} else
542 			d = cgsblock(&sblock, c);
543 		for (; d < cgd; d++)
544 			setbmap(d);
545 	}
546 
547 	startinum = imax + 1;
548 	return (1);
549 
550 badsb:
551 	ckfini();
552 	return (0);
553 }
554 
555 pass1()
556 {
557 	register int c, i, n, j;
558 	register DINODE *dp;
559 	int savino, ndb, partial;
560 
561 	pfunc = pass1check;
562 	inum = 0;
563 	n_blks += howmany(sblock.fs_cssize, sblock.fs_fsize);
564 	for (c = 0; c < sblock.fs_ncg; c++) {
565 		if (getblk(&cgblk, cgtod(&sblock, c), sblock.fs_cgsize) == 0)
566 			continue;
567 		if (cgrp.cg_magic != CG_MAGIC) {
568 			pfatal("cg %d: bad magic number\n", c);
569 			bzero((caddr_t)&cgrp, sblock.fs_cgsize);
570 		}
571 		n = 0;
572 		for (i = 0; i < sblock.fs_ipg; i++, inum++) {
573 			dp = ginode();
574 			if (dp == NULL)
575 				continue;
576 			n++;
577 			if (ALLOC) {
578 				if (!isset(cgrp.cg_iused, i)) {
579 					if (debug)
580 						printf("%d bad, not used\n",
581 						    inum);
582 					inosumbad++;
583 				}
584 				n--;
585 				lastino = inum;
586 				if (ftypeok(dp) == 0)
587 					goto unknown;
588 				if (dp->di_size < 0) {
589 					if (debug)
590 						printf("bad size %d:",
591 							dp->di_size);
592 					goto unknown;
593 				}
594 				ndb = howmany(dp->di_size, sblock.fs_bsize);
595 				if (SPECIAL)
596 					ndb++;
597 				for (j = ndb; j < NDADDR; j++)
598 					if (dp->di_db[j] != 0) {
599 						if (debug)
600 							printf("bad direct addr:");
601 						goto unknown;
602 					}
603 				for (j = 0, ndb -= NDADDR; ndb > 0; j++)
604 					ndb /= NINDIR(&sblock);
605 				for (; j < NIADDR; j++)
606 					if (dp->di_ib[j] != 0) {
607 						if (debug)
608 							printf("bad indirect addr:");
609 						goto unknown;
610 					}
611 				n_files++;
612 				lncntp[inum] = dp->di_nlink;
613 				if (dp->di_nlink <= 0) {
614 					if (badlnp < &badlncnt[MAXLNCNT])
615 						*badlnp++ = inum;
616 					else {
617 						pfatal("LINK COUNT TABLE OVERFLOW");
618 						if (reply("CONTINUE") == 0)
619 							errexit("");
620 					}
621 				}
622 				statemap[inum] = DIRCT ? DSTATE : FSTATE;
623 				badblk = dupblk = 0; filsize = 0; maxblk = 0;
624 				ckinode(dp, ADDR);
625 				continue;
626 		unknown:
627 				pfatal("UNKNOWN FILE TYPE I=%u", inum);
628 				if (reply("CLEAR") == 1) {
629 					zapino(dp);
630 					inodirty();
631 					inosumbad++;
632 				}
633 			} else {
634 				if (isset(cgrp.cg_iused, i)) {
635 					if (debug)
636 						printf("%d bad, marked used\n",
637 						    inum);
638 					inosumbad++;
639 					n--;
640 				}
641 				partial = 0;
642 				for (j = 0; j < NDADDR; j++)
643 					if (dp->di_db[j] != 0)
644 						partial++;
645 				for (j = 0; j < NIADDR; j++)
646 					if (dp->di_ib[j] != 0)
647 						partial++;
648 				if (partial || dp->di_mode != 0 ||
649 				    dp->di_size != 0) {
650 					pfatal("PARTIALLY ALLOCATED INODE I=%u", inum);
651 					if (reply("CLEAR") == 1) {
652 						zapino(dp);
653 						inodirty();
654 						inosumbad++;
655 					}
656 				}
657 			}
658 		}
659 		if (n != cgrp.cg_cs.cs_nifree) {
660 			if (debug)
661 				printf("cg[%d].cg_cs.cs_nifree is %d; calc %d\n",
662 				    c, cgrp.cg_cs.cs_nifree, n);
663 			inosumbad++;
664 		}
665 		if (cgrp.cg_cs.cs_nbfree != sblock.fs_cs(&sblock, c).cs_nbfree
666 		  || cgrp.cg_cs.cs_nffree != sblock.fs_cs(&sblock, c).cs_nffree
667 		  || cgrp.cg_cs.cs_nifree != sblock.fs_cs(&sblock, c).cs_nifree
668 		  || cgrp.cg_cs.cs_ndir != sblock.fs_cs(&sblock, c).cs_ndir)
669 			sbsumbad++;
670 	}
671 }
672 
673 pass1check(blk, size)
674 	daddr_t blk;
675 	int size;
676 {
677 	register daddr_t *dlp;
678 	int res = KEEPON;
679 	int anyout;
680 
681 	anyout = outrange(blk, size);
682 	for (; size > 0; blk++, size--) {
683 		if (anyout && outrange(blk, 1)) {
684 			blkerr("BAD", blk);
685 			if (++badblk >= MAXBAD) {
686 				pwarn("EXCESSIVE BAD BLKS I=%u", inum);
687 				if (preen)
688 					printf(" (SKIPPING)\n");
689 				else if (reply("CONTINUE") == 0)
690 					errexit("");
691 				return (STOP);
692 			}
693 			res = SKIP;
694 		} else if (getbmap(blk)) {
695 			blkerr("DUP", blk);
696 			if (++dupblk >= MAXDUP) {
697 				pwarn("EXCESSIVE DUP BLKS I=%u", inum);
698 				if (preen)
699 					printf(" (SKIPPING)\n");
700 				else if (reply("CONTINUE") == 0)
701 					errexit("");
702 				return (STOP);
703 			}
704 			if (enddup >= &duplist[DUPTBLSIZE]) {
705 				pfatal("DUP TABLE OVERFLOW.");
706 				if (reply("CONTINUE") == 0)
707 					errexit("");
708 				return (STOP);
709 			}
710 			for (dlp = duplist; dlp < muldup; dlp++)
711 				if (*dlp == blk) {
712 					*enddup++ = blk;
713 					break;
714 				}
715 			if (dlp >= muldup) {
716 				*enddup++ = *muldup;
717 				*muldup++ = blk;
718 			}
719 		} else {
720 			n_blks++;
721 			setbmap(blk);
722 		}
723 		filsize++;
724 	}
725 	return (res);
726 }
727 
728 pass1b()
729 {
730 	register int c, i;
731 	register DINODE *dp;
732 
733 	pfunc = pass1bcheck;
734 	inum = 0;
735 	for (c = 0; c < sblock.fs_ncg; c++) {
736 		for (i = 0; i < sblock.fs_ipg; i++, inum++) {
737 			dp = ginode();
738 			if (dp == NULL)
739 				continue;
740 			if (statemap[inum] != USTATE &&
741 			    (ckinode(dp, ADDR) & STOP))
742 				goto out1b;
743 		}
744 	}
745 out1b:
746 	flush(&dfile, &inoblk);
747 }
748 
749 pass1bcheck(blk, size)
750 	daddr_t blk;
751 	int size;
752 {
753 	register daddr_t *dlp;
754 	int res = KEEPON;
755 
756 	for (; size > 0; blk++, size--) {
757 		if (outrange(blk, 1))
758 			res = SKIP;
759 		for (dlp = duplist; dlp < muldup; dlp++)
760 			if (*dlp == blk) {
761 				blkerr("DUP", blk);
762 				*dlp = *--muldup;
763 				*muldup = blk;
764 				if (muldup == duplist)
765 					return (STOP);
766 			}
767 	}
768 	return (res);
769 }
770 
771 pass2()
772 {
773 	register DINODE *dp;
774 
775 	inum = ROOTINO;
776 	thisname = pathp = pathname;
777 	pfunc = pass2check;
778 	switch (statemap[inum]) {
779 
780 	case USTATE:
781 		errexit("ROOT INODE UNALLOCATED. TERMINATING.\n");
782 
783 	case FSTATE:
784 		pfatal("ROOT INODE NOT DIRECTORY");
785 		if (reply("FIX") == 0 || (dp = ginode()) == NULL)
786 			errexit("");
787 		dp->di_mode &= ~IFMT;
788 		dp->di_mode |= IFDIR;
789 		inodirty();
790 		inosumbad++;
791 		statemap[inum] = DSTATE;
792 		/* fall into ... */
793 
794 	case DSTATE:
795 		descend();
796 		break;
797 
798 	case CLEAR:
799 		pfatal("DUPS/BAD IN ROOT INODE");
800 		printf("\n");
801 		if (reply("CONTINUE") == 0)
802 			errexit("");
803 		statemap[inum] = DSTATE;
804 		descend();
805 	}
806 }
807 
808 pass2check(dirp)
809 	register DIRECT *dirp;
810 {
811 	register char *p;
812 	register n;
813 	DINODE *dp;
814 
815 	if ((inum = dirp->d_ino) == 0)
816 		return (KEEPON);
817 	thisname = pathp;
818 	for (p = dirp->d_name; p < &dirp->d_name[MAXNAMLEN]; )
819 		if ((*pathp++ = *p++) == 0) {
820 			--pathp;
821 			break;
822 		}
823 	*pathp = 0;
824 	n = 0;
825 	if (inum > imax || inum <= 0)
826 		n = direrr("I OUT OF RANGE");
827 	else {
828 again:
829 		switch (statemap[inum]) {
830 		case USTATE:
831 			n = direrr("UNALLOCATED");
832 			break;
833 
834 		case CLEAR:
835 			if ((n = direrr("DUP/BAD")) == 1)
836 				break;
837 			if ((dp = ginode()) == NULL)
838 				break;
839 			statemap[inum] = DIRCT ? DSTATE : FSTATE;
840 			goto again;
841 
842 		case FSTATE:
843 			lncntp[inum]--;
844 			break;
845 
846 		case DSTATE:
847 			lncntp[inum]--;
848 			descend();
849 			break;
850 		}
851 	}
852 	pathp = thisname;
853 	if (n == 0)
854 		return (KEEPON);
855 	dirp->d_ino = 0;
856 	return (KEEPON|ALTERD);
857 }
858 
859 pass3()
860 {
861 	ino_t savino;
862 	register DINODE *dp;
863 
864 	for (inum = ROOTINO; inum <= lastino; inum++) {
865 		if (statemap[inum] == DSTATE) {
866 			pfunc = findino;
867 			srchname = "..";
868 			savino = inum;
869 			do {
870 				orphan = inum;
871 				if ((dp = ginode()) == NULL)
872 					break;
873 				filsize = dp->di_size;
874 				parentdir = 0;
875 				ckinode(dp, DATA);
876 				if ((inum = parentdir) == 0)
877 					break;
878 			} while (statemap[inum] == DSTATE);
879 			inum = orphan;
880 			if (linkup() == 1) {
881 				thisname = pathp = pathname;
882 				*pathp++ = '?';
883 				pfunc = pass2check;
884 				descend();
885 			}
886 			inum = savino;
887 		}
888 	}
889 }
890 
891 pass4()
892 {
893 	register int n;
894 	register ino_t *blp;
895 
896 	pfunc = pass4check;
897 	for (inum = ROOTINO; inum <= lastino; inum++) {
898 		switch (statemap[inum]) {
899 
900 		case FSTATE:
901 			n = lncntp[inum];
902 			if (n)
903 				adjust((short)n);
904 			else {
905 				for (blp = badlncnt;blp < badlnp; blp++)
906 					if (*blp == inum) {
907 						clri("UNREF", 1);
908 						break;
909 					}
910 			}
911 			break;
912 
913 		case DSTATE:
914 			clri("UNREF", 1);
915 			break;
916 
917 		case CLEAR:
918 			clri("BAD/DUP", 1);
919 			break;
920 		}
921 	}
922 	if (imax - ROOTINO - n_files != sblock.fs_cstotal.cs_nifree) {
923 		pwarn("FREE INODE COUNT WRONG IN SUPERBLK");
924 		if (preen)
925 			printf(" (FIXED)\n");
926 		if (preen || reply("FIX") == 1) {
927 			sblock.fs_cstotal.cs_nifree = imax - ROOTINO - n_files;
928 			sbdirty();
929 		}
930 	}
931 	flush(&dfile, &fileblk);
932 }
933 
934 pass4check(blk, size)
935 	daddr_t blk;
936 {
937 	register daddr_t *dlp;
938 	int res = KEEPON;
939 
940 	for (; size > 0; blk++, size--) {
941 		if (outrange(blk, 1))
942 			res = SKIP;
943 		else if (getbmap(blk)) {
944 			for (dlp = duplist; dlp < enddup; dlp++)
945 				if (*dlp == blk) {
946 					*dlp = *--enddup;
947 					return (KEEPON);
948 				}
949 			clrbmap(blk);
950 			n_blks--;
951 		}
952 	}
953 	return (res);
954 }
955 
956 pass5()
957 {
958 	register int c, n, i, b, d;
959 	short bo[MAXCPG][NRPOS];
960 	long botot[MAXCPG];
961 	long frsum[MAXFRAG];
962 	int blk;
963 	daddr_t cbase;
964 	int blockbits = (1<<sblock.fs_frag)-1;
965 
966 	bcopy(blockmap, freemap, (unsigned)bmapsz);
967 	dupblk = 0;
968 	n_index = sblock.fs_ncg * (cgdmin(&sblock, 0) - cgtod(&sblock, 0));
969 	for (c = 0; c < sblock.fs_ncg; c++) {
970 		cbase = cgbase(&sblock, c);
971 		bzero(botot, sizeof (botot));
972 		bzero(bo, sizeof (bo));
973 		bzero(frsum, sizeof (frsum));
974 		/*
975 		 * need to account for the super blocks
976 		 * which appear (inaccurately) bad
977 		 */
978 		n_bad += cgtod(&sblock, c) - cgsblock(&sblock, c);
979 		if (getblk(&cgblk, cgtod(&sblock, c), sblock.fs_cgsize) == 0)
980 			continue;
981 		if (cgrp.cg_magic != CG_MAGIC) {
982 			pfatal("cg %d: bad magic number\n", c);
983 			bzero((caddr_t)&cgrp, sblock.fs_cgsize);
984 		}
985 		for (b = 0; b < sblock.fs_fpg; b += sblock.fs_frag) {
986 			blk = blkmap(&sblock, cgrp.cg_free, b);
987 			if (blk == 0)
988 				continue;
989 			if (blk == blockbits) {
990 				if (pass5check(cbase+b, sblock.fs_frag) == STOP)
991 					goto out5;
992 				/* this is clumsy ... */
993 				n_ffree -= sblock.fs_frag;
994 				n_bfree++;
995 				botot[cbtocylno(&sblock, b)]++;
996 				bo[cbtocylno(&sblock, b)]
997 				    [cbtorpos(&sblock, b)]++;
998 				continue;
999 			}
1000 			for (d = 0; d < sblock.fs_frag; d++)
1001 				if ((blk & (1<<d)) &&
1002 				    pass5check(cbase+b+d,1) == STOP)
1003 					goto out5;
1004 			fragacct(&sblock, blk, frsum, 1);
1005 		}
1006 		if (bcmp(cgrp.cg_frsum, frsum, sizeof (frsum))) {
1007 			if (debug)
1008 			for (i = 0; i < sblock.fs_frag; i++)
1009 				if (cgrp.cg_frsum[i] != frsum[i])
1010 				printf("cg[%d].cg_frsum[%d] have %d calc %d\n",
1011 				    c, i, cgrp.cg_frsum[i], frsum[i]);
1012 			frsumbad++;
1013 		}
1014 		if (bcmp(cgrp.cg_btot, botot, sizeof (botot))) {
1015 			if (debug)
1016 			for (n = 0; n < sblock.fs_cpg; n++)
1017 				if (botot[n] != cgrp.cg_btot[n])
1018 				printf("cg[%d].cg_btot[%d] have %d calc %d\n",
1019 				    c, n, cgrp.cg_btot[n], botot[n]);
1020 			offsumbad++;
1021 		}
1022 		if (bcmp(cgrp.cg_b, bo, sizeof (bo))) {
1023 			if (debug)
1024 			for (i = 0; i < NRPOS; i++)
1025 				if (bo[n][i] != cgrp.cg_b[n][i])
1026 				printf("cg[%d].cg_b[%d][%d] have %d calc %d\n",
1027 				    c, n, i, cgrp.cg_b[n][i], bo[n][i]);
1028 			offsumbad++;
1029 		}
1030 	}
1031 out5:
1032 	if (dupblk)
1033 		pwarn("%d DUP BLKS IN BIT MAPS\n", dupblk);
1034 	if (fixcg == 0) {
1035 		if ((b = n_blks+n_ffree+sblock.fs_frag*n_bfree+n_index+n_bad) != fmax) {
1036 			pwarn("%ld BLK(S) MISSING\n", fmax - b);
1037 			fixcg = 1;
1038 		} else if (inosumbad + offsumbad + frsumbad + sbsumbad) {
1039 			pwarn("SUMMARY INFORMATION %s%s%s%sBAD\n",
1040 			    inosumbad ? "(INODE FREE) " : "",
1041 			    offsumbad ? "(BLOCK OFFSETS) " : "",
1042 			    frsumbad ? "(FRAG SUMMARIES) " : "",
1043 			    sbsumbad ? "(SUPER BLOCK SUMMARIES) " : "");
1044 			fixcg = 1;
1045 		} else if (n_ffree != sblock.fs_cstotal.cs_nffree ||
1046 		    n_bfree != sblock.fs_cstotal.cs_nbfree) {
1047 			pwarn("FREE BLK COUNT(S) WRONG IN SUPERBLK");
1048 			if (preen)
1049 				printf(" (FIXED)\n");
1050 			if (preen || reply("FIX") == 1) {
1051 				sblock.fs_cstotal.cs_nffree = n_ffree;
1052 				sblock.fs_cstotal.cs_nbfree = n_bfree;
1053 				sbdirty();
1054 			}
1055 		}
1056 	}
1057 	if (fixcg) {
1058 		pwarn("BAD CYLINDER GROUPS");
1059 		if (preen)
1060 			printf(" (SALVAGED)\n");
1061 		else if (reply("SALVAGE") == 0)
1062 			fixcg = 0;
1063 	}
1064 }
1065 
1066 pass5check(blk, size)
1067 	daddr_t blk;
1068 	int size;
1069 {
1070 
1071 	if (outrange(blk, size)) {
1072 		fixcg = 1;
1073 		if (preen)
1074 			pfatal("BAD BLOCKS IN BIT MAPS.");
1075 		if (++badblk >= MAXBAD) {
1076 			printf("EXCESSIVE BAD BLKS IN BIT MAPS.");
1077 			if (reply("CONTINUE") == 0)
1078 				errexit("");
1079 			return (STOP);
1080 		}
1081 	}
1082 	for (; size > 0; blk++, size--)
1083 		if (getfmap(blk)) {
1084 			fixcg = 1;
1085 			++dupblk;
1086 		} else {
1087 			n_ffree++;
1088 			setfmap(blk);
1089 		}
1090 	return (KEEPON);
1091 }
1092 
1093 ckinode(dp, flg)
1094 	DINODE *dp;
1095 	register flg;
1096 {
1097 	register daddr_t *ap;
1098 	register ret;
1099 	int (*func)(), n, ndb, size, offset;
1100 	ino_t number = inum;
1101 	DINODE dino;
1102 
1103 	if (SPECIAL)
1104 		return (KEEPON);
1105 	dino = *dp;
1106 	func = (flg == ADDR) ? pfunc : dirscan;
1107 	ndb = howmany(dino.di_size, sblock.fs_bsize);
1108 	for (ap = &dino.di_db[0]; ap < &dino.di_db[NDADDR]; ap++) {
1109 		if (--ndb == 0 && (offset = blkoff(&sblock, dino.di_size)) != 0)
1110 			size = numfrags(&sblock, fragroundup(&sblock, offset));
1111 		else
1112 			size = sblock.fs_frag;
1113 		dnum = number;
1114 		if (*ap && (ret = (*func)(*ap, size)) & STOP)
1115 			return (ret);
1116 	}
1117 	for (ap = &dino.di_ib[0], n = 1; n <= 2; ap++, n++) {
1118 		dnum = number;
1119 		if (*ap) {
1120 			ret = iblock(*ap, n, flg,
1121 			    dino.di_size - sblock.fs_bsize * NDADDR);
1122 			if (ret & STOP)
1123 				return (ret);
1124 		}
1125 	}
1126 	return (KEEPON);
1127 }
1128 
1129 iblock(blk, ilevel, flg, isize)
1130 	daddr_t blk;
1131 	register ilevel;
1132 	int isize;
1133 {
1134 	register daddr_t *ap;
1135 	register daddr_t *aplim;
1136 	register int i, n;
1137 	int (*func)(), nif;
1138 	BUFAREA ib;
1139 
1140 	if (flg == ADDR) {
1141 		func = pfunc;
1142 		if (((n = (*func)(blk, sblock.fs_frag)) & KEEPON) == 0)
1143 			return (n);
1144 	} else
1145 		func = dirscan;
1146 	if (outrange(blk, sblock.fs_frag))		/* protect thyself */
1147 		return (SKIP);
1148 	initbarea(&ib);
1149 	if (getblk(&ib, blk, sblock.fs_bsize) == NULL)
1150 		return (SKIP);
1151 	ilevel--;
1152 	if (ilevel == 0) {
1153 		nif = lblkno(&sblock, isize) + 1;
1154 	} else /* ilevel == 1 */ {
1155 		nif = isize / (sblock.fs_bsize * NINDIR(&sblock)) + 1;
1156 	}
1157 	if (nif > NINDIR(&sblock))
1158 		nif = NINDIR(&sblock);
1159 	aplim = &ib.b_un.b_indir[nif];
1160 	for (ap = ib.b_un.b_indir, i = 1; ap < aplim; ap++, i++)
1161 		if (*ap) {
1162 			if (ilevel > 0)
1163 				n = iblock(*ap, ilevel, flg,
1164 				    isize - i*NINDIR(&sblock)*sblock.fs_bsize);
1165 			else
1166 				n = (*func)(*ap, sblock.fs_frag);
1167 			if (n & STOP)
1168 				return (n);
1169 		}
1170 	return (KEEPON);
1171 }
1172 
1173 outrange(blk, cnt)
1174 	daddr_t blk;
1175 	int cnt;
1176 {
1177 	register int c;
1178 
1179 	if ((unsigned)(blk+cnt) > fmax)
1180 		return (1);
1181 	c = dtog(&sblock, blk);
1182 	if (blk < cgdmin(&sblock, c)) {
1183 		if ((blk+cnt) > cgsblock(&sblock, c)) {
1184 			if (debug) {
1185 				printf("blk %d < cgdmin %d;",
1186 				    blk, cgdmin(&sblock, c));
1187 				printf(" blk+cnt %d > cgsbase %d\n",
1188 				    blk+cnt, cgsblock(&sblock, c));
1189 			}
1190 			return (1);
1191 		}
1192 	} else {
1193 		if ((blk+cnt) > cgbase(&sblock, c+1)) {
1194 			if (debug)  {
1195 				printf("blk %d >= cgdmin %d;",
1196 				    blk, cgdmin(&sblock, c));
1197 				printf(" blk+cnt %d > sblock.fs_fpg %d\n",
1198 				    blk+cnt, sblock.fs_fpg);
1199 			}
1200 			return (1);
1201 		}
1202 	}
1203 	return (0);
1204 }
1205 
1206 blkerr(s, blk)
1207 	daddr_t blk;
1208 	char *s;
1209 {
1210 
1211 	pfatal("%ld %s I=%u", blk, s, inum);
1212 	printf("\n");
1213 	statemap[inum] = CLEAR;
1214 }
1215 
1216 descend()
1217 {
1218 	register DINODE *dp;
1219 	register char *savname;
1220 	off_t savsize;
1221 
1222 	statemap[inum] = FSTATE;
1223 	if ((dp = ginode()) == NULL)
1224 		return;
1225 	savname = thisname;
1226 	*pathp++ = '/';
1227 	savsize = filsize;
1228 	filsize = dp->di_size;
1229 	ckinode(dp, DATA);
1230 	thisname = savname;
1231 	*--pathp = 0;
1232 	filsize = savsize;
1233 }
1234 
1235 struct dirstuff {
1236 	int loc;
1237 	int blkno;
1238 	int blksiz;
1239 	ino_t number;
1240 	enum {DONTKNOW, NOFIX, FIX} fix;
1241 };
1242 
1243 dirscan(blk, nf)
1244 	daddr_t blk;
1245 	int nf;
1246 {
1247 	register DIRECT *dp;
1248 	struct dirstuff dirp;
1249 	int blksiz, dsize, n;
1250 	char dbuf[DIRBLKSIZ];
1251 
1252 	if (outrange(blk, 1)) {
1253 		filsize -= sblock.fs_bsize;
1254 		return (SKIP);
1255 	}
1256 	blksiz = nf * sblock.fs_fsize;
1257 	dirp.loc = 0;
1258 	dirp.blkno = blk;
1259 	dirp.blksiz = blksiz;
1260 	if (dirp.number != dnum) {
1261 		dirp.number = dnum;
1262 		dirp.fix = DONTKNOW;
1263 	}
1264 	for (dp = readdir(&dirp); dp != NULL; dp = readdir(&dirp)) {
1265 		dsize = dp->d_reclen;
1266 		bcopy(dp, dbuf, dsize);
1267 		if ((n = (*pfunc)(dbuf)) & ALTERD) {
1268 			if (getblk(&fileblk, blk, blksiz) != NULL) {
1269 				bcopy(dbuf, dp, dsize);
1270 				dirty(&fileblk);
1271 				sbdirty();
1272 			} else
1273 				n &= ~ALTERD;
1274 		}
1275 		if (n & STOP)
1276 			return (n);
1277 	}
1278 	return (filsize > 0 ? KEEPON : STOP);
1279 }
1280 
1281 /*
1282  * get next entry in a directory.
1283  */
1284 DIRECT *
1285 readdir(dirp)
1286 	register struct dirstuff *dirp;
1287 {
1288 	register DIRECT *dp, *ndp;
1289 	long size;
1290 
1291 	if (getblk(&fileblk, dirp->blkno, dirp->blksiz) == NULL) {
1292 		filsize -= dirp->blksiz - dirp->loc;
1293 		return NULL;
1294 	}
1295 	while (dirp->loc % DIRBLKSIZ == 0 && filsize > 0 &&
1296 	    dirp->loc < dirp->blksiz) {
1297 		dp = (DIRECT *)(dirblk.b_buf + dirp->loc);
1298 	   	if (dp->d_ino < imax &&
1299 		    dp->d_namlen <= MAXNAMLEN && dp->d_namlen >= 0 &&
1300 		    dp->d_reclen > 0 && dp->d_reclen <= DIRBLKSIZ)
1301 			break;
1302 		dirp->loc += DIRBLKSIZ;
1303 		filsize -= DIRBLKSIZ;
1304 		if (dirp->fix == DONTKNOW) {
1305 			pfatal("DIRECTORY %D CORRUPTED", dirp->number);
1306 			dirp->fix = NOFIX;
1307 			if (reply("SALVAGE") != 0)
1308 				dirp->fix = FIX;
1309 		}
1310 		if (dirp->fix != FIX)
1311 			continue;
1312 		dp->d_reclen = DIRBLKSIZ;
1313 		dp->d_ino = 0;
1314 		dp->d_namlen = 0;
1315 		dirty(&fileblk);
1316 	}
1317 	if (filsize <= 0 || dirp->loc >= dirp->blksiz)
1318 		return NULL;
1319 	dp = (DIRECT *)(dirblk.b_buf + dirp->loc);
1320 	dirp->loc += dp->d_reclen;
1321 	filsize -= dp->d_reclen;
1322 	ndp = (DIRECT *)(dirblk.b_buf + dirp->loc);
1323 	if ((filsize <= 0 && dirp->loc % DIRBLKSIZ != 0) ||
1324 	    (dirp->loc < dirp->blksiz && filsize > 0 &&
1325 	    (ndp->d_ino >= imax ||
1326 	    ndp->d_namlen > MAXNAMLEN || ndp->d_namlen < 0 ||
1327 	    ndp->d_reclen <= 0 ||
1328 	    ndp->d_reclen > DIRBLKSIZ - (dirp->loc % DIRBLKSIZ)))) {
1329 		size = DIRBLKSIZ - (dirp->loc % DIRBLKSIZ);
1330 		dirp->loc += size;
1331 		filsize -= size;
1332 		if (dirp->fix == DONTKNOW) {
1333 			pfatal("DIRECTORY %D CORRUPTED", dirp->number);
1334 			dirp->fix = NOFIX;
1335 			if (reply("SALVAGE") != 0)
1336 				dirp->fix = FIX;
1337 		}
1338 		if (dirp->fix == FIX) {
1339 			dp->d_reclen += size;
1340 			dirty(&fileblk);
1341 		}
1342 	}
1343 	return (dp);
1344 }
1345 
1346 direrr(s)
1347 	char *s;
1348 {
1349 	register DINODE *dp;
1350 
1351 	pwarn("%s ", s);
1352 	pinode();
1353 	printf("\n");
1354 	if ((dp = ginode()) != NULL && ftypeok(dp))
1355 		pfatal("%s=%s", DIRCT?"DIR":"FILE", pathname);
1356 	else
1357 		pfatal("NAME=%s", pathname);
1358 	return (reply("REMOVE"));
1359 }
1360 
1361 adjust(lcnt)
1362 	register short lcnt;
1363 {
1364 	register DINODE *dp;
1365 
1366 	if ((dp = ginode()) == NULL)
1367 		return;
1368 	if (dp->di_nlink == lcnt) {
1369 		if (linkup() == 0)
1370 			clri("UNREF", 0);
1371 	}
1372 	else {
1373 		pwarn("LINK COUNT %s",
1374 			(lfdir==inum)?lfname:(DIRCT?"DIR":"FILE"));
1375 		pinode();
1376 		printf(" COUNT %d SHOULD BE %d",
1377 			dp->di_nlink, dp->di_nlink-lcnt);
1378 		if (preen) {
1379 			if (lcnt < 0) {
1380 				printf("\n");
1381 				preendie();
1382 			}
1383 			printf(" (ADJUSTED)\n");
1384 		}
1385 		if (preen || reply("ADJUST") == 1) {
1386 			dp->di_nlink -= lcnt;
1387 			inodirty();
1388 		}
1389 	}
1390 }
1391 
1392 clri(s, flg)
1393 	char *s;
1394 {
1395 	register DINODE *dp;
1396 
1397 	if ((dp = ginode()) == NULL)
1398 		return;
1399 	if (flg == 1) {
1400 		pwarn("%s %s", s, DIRCT?"DIR":"FILE");
1401 		pinode();
1402 	}
1403 	if (preen || reply("CLEAR") == 1) {
1404 		if (preen)
1405 			printf(" (CLEARED)\n");
1406 		n_files--;
1407 		pfunc = pass4check;
1408 		ckinode(dp, ADDR);
1409 		zapino(dp);
1410 		statemap[inum] = USTATE;
1411 		inodirty();
1412 		inosumbad++;
1413 	}
1414 }
1415 
1416 badsb(s)
1417 	char *s;
1418 {
1419 
1420 	if (preen)
1421 		printf("%s: ", devname);
1422 	printf("BAD SUPER BLOCK: %s\n", s);
1423 	pwarn("USE -b OPTION TO FSCK TO SPECIFY LOCATION OF AN ALTERNATE\n");
1424 	pfatal("SUPER-BLOCK TO SUPPLY NEEDED INFORMATION; SEE fsck(8).\n");
1425 }
1426 
1427 DINODE *
1428 ginode()
1429 {
1430 	daddr_t iblk;
1431 
1432 	if (inum < ROOTINO || inum > imax) {
1433 		if (debug && (inum < 0 || inum > imax))
1434 			printf("inum out of range (%d)\n", inum);
1435 		return (NULL);
1436 	}
1437 	if (inum < startinum || inum >= startinum + INOPB(&sblock)) {
1438 		iblk = itod(&sblock, inum);
1439 		if (getblk(&inoblk, iblk, sblock.fs_bsize) == NULL) {
1440 			return (NULL);
1441 		}
1442 		startinum = (inum / INOPB(&sblock)) * INOPB(&sblock);
1443 	}
1444 	return (&inoblk.b_un.b_dinode[inum % INOPB(&sblock)]);
1445 }
1446 
1447 ftypeok(dp)
1448 	DINODE *dp;
1449 {
1450 	switch (dp->di_mode & IFMT) {
1451 
1452 	case IFDIR:
1453 	case IFREG:
1454 	case IFBLK:
1455 	case IFCHR:
1456 	case IFLNK:
1457 		return (1);
1458 
1459 	default:
1460 		if (debug)
1461 			printf("bad file type 0%o\n", dp->di_mode);
1462 		return (0);
1463 	}
1464 }
1465 
1466 reply(s)
1467 	char *s;
1468 {
1469 	char line[80];
1470 
1471 	if (preen)
1472 		pfatal("INTERNAL ERROR: GOT TO reply()");
1473 	rplyflag = 1;
1474 	printf("\n%s? ", s);
1475 	if (nflag || dfile.wfdes < 0) {
1476 		printf(" no\n\n");
1477 		return (0);
1478 	}
1479 	if (yflag) {
1480 		printf(" yes\n\n");
1481 		return (1);
1482 	}
1483 	if (getline(stdin, line, sizeof(line)) == EOF)
1484 		errexit("\n");
1485 	printf("\n");
1486 	if (line[0] == 'y' || line[0] == 'Y')
1487 		return (1);
1488 	else
1489 		return (0);
1490 }
1491 
1492 getline(fp, loc, maxlen)
1493 	FILE *fp;
1494 	char *loc;
1495 {
1496 	register n;
1497 	register char *p, *lastloc;
1498 
1499 	p = loc;
1500 	lastloc = &p[maxlen-1];
1501 	while ((n = getc(fp)) != '\n') {
1502 		if (n == EOF)
1503 			return (EOF);
1504 		if (!isspace(n) && p < lastloc)
1505 			*p++ = n;
1506 	}
1507 	*p = 0;
1508 	return (p - loc);
1509 }
1510 
1511 BUFAREA *
1512 getblk(bp, blk, size)
1513 	daddr_t blk;
1514 	register BUFAREA *bp;
1515 	int size;
1516 {
1517 	register struct filecntl *fcp;
1518 	daddr_t dblk;
1519 
1520 	fcp = &dfile;
1521 	dblk = fsbtodb(&sblock, blk);
1522 	if (bp->b_bno == dblk)
1523 		return (bp);
1524 	flush(fcp, bp);
1525 	if (bread(fcp, bp->b_un.b_buf, dblk, size) != 0) {
1526 		bp->b_bno = dblk;
1527 		bp->b_size = size;
1528 		return (bp);
1529 	}
1530 	bp->b_bno = (daddr_t)-1;
1531 	return (NULL);
1532 }
1533 
1534 flush(fcp, bp)
1535 	struct filecntl *fcp;
1536 	register BUFAREA *bp;
1537 {
1538 
1539 	if (bp->b_dirty)
1540 		bwrite(fcp, bp->b_un.b_buf, bp->b_bno, bp->b_size);
1541 	bp->b_dirty = 0;
1542 }
1543 
1544 rwerr(s, blk)
1545 	char *s;
1546 	daddr_t blk;
1547 {
1548 
1549 	if (preen == 0)
1550 		printf("\n");
1551 	pfatal("CANNOT %s: BLK %ld", s, blk);
1552 	if (reply("CONTINUE") == 0)
1553 		errexit("Program terminated\n");
1554 }
1555 
1556 ckfini()
1557 {
1558 
1559 	flush(&dfile, &fileblk);
1560 	flush(&dfile, &sblk);
1561 	if (sblk.b_bno != SBLOCK) {
1562 		sblk.b_bno = SBLOCK;
1563 		sbdirty();
1564 		flush(&dfile, &sblk);
1565 	}
1566 	flush(&dfile, &inoblk);
1567 	close(dfile.rfdes);
1568 	close(dfile.wfdes);
1569 }
1570 
1571 pinode()
1572 {
1573 	register DINODE *dp;
1574 	register char *p;
1575 	char uidbuf[BUFSIZ];
1576 	char *ctime();
1577 
1578 	printf(" I=%u ", inum);
1579 	if ((dp = ginode()) == NULL)
1580 		return;
1581 	printf(" OWNER=");
1582 	if (getpw((int)dp->di_uid, uidbuf) == 0) {
1583 		for (p = uidbuf; *p != ':'; p++);
1584 		*p = 0;
1585 		printf("%s ", uidbuf);
1586 	}
1587 	else {
1588 		printf("%d ", dp->di_uid);
1589 	}
1590 	printf("MODE=%o\n", dp->di_mode);
1591 	if (preen)
1592 		printf("%s: ", devname);
1593 	printf("SIZE=%ld ", dp->di_size);
1594 	p = ctime(&dp->di_mtime);
1595 	printf("MTIME=%12.12s %4.4s ", p+4, p+20);
1596 }
1597 
1598 makecg()
1599 {
1600 	int c, blk;
1601 	daddr_t dbase, d, dlower, dupper, dmax;
1602 	long i, j, s;
1603 	register struct csum *cs;
1604 	register DINODE *dp;
1605 
1606 	sblock.fs_cstotal.cs_nbfree = 0;
1607 	sblock.fs_cstotal.cs_nffree = 0;
1608 	sblock.fs_cstotal.cs_nifree = 0;
1609 	sblock.fs_cstotal.cs_ndir = 0;
1610 	for (c = 0; c < sblock.fs_ncg; c++) {
1611 		dbase = cgbase(&sblock, c);
1612 		dmax = dbase + sblock.fs_fpg;
1613 		if (dmax > sblock.fs_size) {
1614 			for ( ; dmax >= sblock.fs_size; dmax--)
1615 				clrbit(cgrp.cg_free, dmax - dbase);
1616 			dmax++;
1617 		}
1618 		dlower = cgsblock(&sblock, c) - dbase;
1619 		dupper = cgdmin(&sblock, c) - dbase;
1620 		cs = &sblock.fs_cs(&sblock, c);
1621 		cgrp.cg_time = time(0);
1622 		cgrp.cg_magic = CG_MAGIC;
1623 		cgrp.cg_cgx = c;
1624 		if (c == sblock.fs_ncg - 1)
1625 			cgrp.cg_ncyl = sblock.fs_ncyl % sblock.fs_cpg;
1626 		else
1627 			cgrp.cg_ncyl = sblock.fs_cpg;
1628 		cgrp.cg_niblk = sblock.fs_ipg;
1629 		cgrp.cg_ndblk = dmax - dbase;
1630 		cgrp.cg_cs.cs_ndir = 0;
1631 		cgrp.cg_cs.cs_nffree = 0;
1632 		cgrp.cg_cs.cs_nbfree = 0;
1633 		cgrp.cg_cs.cs_nifree = 0;
1634 		cgrp.cg_rotor = 0;
1635 		cgrp.cg_frotor = 0;
1636 		cgrp.cg_irotor = 0;
1637 		for (i = 0; i < sblock.fs_frag; i++)
1638 			cgrp.cg_frsum[i] = 0;
1639 		inum = sblock.fs_ipg * c;
1640 		for (i = 0; i < sblock.fs_ipg; inum++, i++) {
1641 			cgrp.cg_cs.cs_nifree++;
1642 			clrbit(cgrp.cg_iused, i);
1643 			dp = ginode();
1644 			if (dp == NULL)
1645 				continue;
1646 			if (ALLOC) {
1647 				if (DIRCT)
1648 					cgrp.cg_cs.cs_ndir++;
1649 				cgrp.cg_cs.cs_nifree--;
1650 				setbit(cgrp.cg_iused, i);
1651 				continue;
1652 			}
1653 		}
1654 		while (i < MAXIPG) {
1655 			clrbit(cgrp.cg_iused, i);
1656 			i++;
1657 		}
1658 		if (c == 0)
1659 			for (i = 0; i < ROOTINO; i++) {
1660 				setbit(cgrp.cg_iused, i);
1661 				cgrp.cg_cs.cs_nifree--;
1662 			}
1663 		for (s = 0; s < MAXCPG; s++) {
1664 			cgrp.cg_btot[s] = 0;
1665 			for (i = 0; i < NRPOS; i++)
1666 				cgrp.cg_b[s][i] = 0;
1667 		}
1668 		if (c == 0) {
1669 			dupper += howmany(sblock.fs_cssize, sblock.fs_fsize);
1670 		}
1671 		for (d = dlower; d < dupper; d++)
1672 			clrbit(cgrp.cg_free, d);
1673 		for (d = 0; (d + sblock.fs_frag) <= dmax - dbase;
1674 		    d += sblock.fs_frag) {
1675 			j = 0;
1676 			for (i = 0; i < sblock.fs_frag; i++) {
1677 				if (!getbmap(dbase + d + i)) {
1678 					setbit(cgrp.cg_free, d + i);
1679 					j++;
1680 				} else
1681 					clrbit(cgrp.cg_free, d+i);
1682 			}
1683 			if (j == sblock.fs_frag) {
1684 				cgrp.cg_cs.cs_nbfree++;
1685 				cgrp.cg_btot[cbtocylno(&sblock, d)]++;
1686 				cgrp.cg_b[cbtocylno(&sblock, d)]
1687 				    [cbtorpos(&sblock, d)]++;
1688 			} else if (j > 0) {
1689 				cgrp.cg_cs.cs_nffree += j;
1690 				blk = blkmap(&sblock, cgrp.cg_free, d);
1691 				fragacct(&sblock, blk, cgrp.cg_frsum, 1);
1692 			}
1693 		}
1694 		for (j = d; d < dmax - dbase; d++) {
1695 			if (!getbmap(dbase + d)) {
1696 				setbit(cgrp.cg_free, d);
1697 				cgrp.cg_cs.cs_nffree++;
1698 			} else
1699 				clrbit(cgrp.cg_free, d);
1700 		}
1701 		for (; d % sblock.fs_frag != 0; d++)
1702 			clrbit(cgrp.cg_free, d);
1703 		if (j != d) {
1704 			blk = blkmap(&sblock, cgrp.cg_free, j);
1705 			fragacct(&sblock, blk, cgrp.cg_frsum, 1);
1706 		}
1707 		for (d /= sblock.fs_frag; d < MAXBPG(&sblock); d ++)
1708 			clrblock(&sblock, cgrp.cg_free, d);
1709 		sblock.fs_cstotal.cs_nffree += cgrp.cg_cs.cs_nffree;
1710 		sblock.fs_cstotal.cs_nbfree += cgrp.cg_cs.cs_nbfree;
1711 		sblock.fs_cstotal.cs_nifree += cgrp.cg_cs.cs_nifree;
1712 		sblock.fs_cstotal.cs_ndir += cgrp.cg_cs.cs_ndir;
1713 		*cs = cgrp.cg_cs;
1714 		bwrite(&dfile, &cgrp, fsbtodb(&sblock, cgtod(&sblock, c)),
1715 		    sblock.fs_cgsize);
1716 	}
1717 	for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) {
1718 		bwrite(&dfile, (char *)sblock.fs_csp[j],
1719 		    fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag),
1720 		    sblock.fs_cssize - i < sblock.fs_bsize ?
1721 		    sblock.fs_cssize - i : sblock.fs_bsize);
1722 	}
1723 	sblock.fs_ronly = 0;
1724 	sblock.fs_fmod = 0;
1725 	sbdirty();
1726 }
1727 
1728 findino(dirp)
1729 	register DIRECT *dirp;
1730 {
1731 	if (dirp->d_ino == 0)
1732 		return (KEEPON);
1733 	if (!strcmp(dirp->d_name, srchname)) {
1734 		if (dirp->d_ino >= ROOTINO && dirp->d_ino <= imax)
1735 			parentdir = dirp->d_ino;
1736 		return (STOP);
1737 	}
1738 	return (KEEPON);
1739 }
1740 
1741 mkentry(dirp)
1742 	register DIRECT *dirp;
1743 {
1744 	register ino_t in;
1745 	register char *p;
1746 	DIRECT newent;
1747 	int newlen, oldlen;
1748 
1749 	newent.d_namlen = 11;
1750 	newlen = DIRSIZ(&newent);
1751 	if (dirp->d_ino != 0)
1752 		oldlen = DIRSIZ(dirp);
1753 	else
1754 		oldlen = 0;
1755 	if (dirp->d_reclen - oldlen < newlen)
1756 		return (KEEPON);
1757 	newent.d_reclen = dirp->d_reclen - oldlen;
1758 	dirp->d_reclen = oldlen;
1759 	dirp = (struct direct *)(((char *)dirp) + oldlen);
1760 	dirp->d_ino = orphan;
1761 	dirp->d_reclen = newent.d_reclen;
1762 	p = &dirp->d_name[2];
1763 	for (in = imax; in > 0; in /= 10)
1764 		p++;
1765 	*--p = 0;
1766 	dirp->d_namlen = p - dirp->d_name;
1767 	in = orphan;
1768 	while (p > dirp->d_name) {
1769 		*--p = (in % 10) + '0';
1770 		in /= 10;
1771 	}
1772 	*p = '#';
1773 	return (ALTERD|STOP);
1774 }
1775 
1776 chgdd(dirp)
1777 	register DIRECT *dirp;
1778 {
1779 	if (dirp->d_name[0] == '.' && dirp->d_name[1] == '.' &&
1780 	dirp->d_name[2] == 0) {
1781 		dirp->d_ino = lfdir;
1782 		return (ALTERD|STOP);
1783 	}
1784 	return (KEEPON);
1785 }
1786 
1787 linkup()
1788 {
1789 	register DINODE *dp;
1790 	register lostdir;
1791 	register ino_t pdir;
1792 
1793 	if ((dp = ginode()) == NULL)
1794 		return (0);
1795 	lostdir = DIRCT;
1796 	pdir = parentdir;
1797 	pwarn("UNREF %s ", lostdir ? "DIR" : "FILE");
1798 	pinode();
1799 	if (preen && dp->di_size == 0)
1800 		return (0);
1801 	if (preen)
1802 		printf(" (RECONNECTED)\n");
1803 	else
1804 		if (reply("RECONNECT") == 0)
1805 			return (0);
1806 	orphan = inum;
1807 	if (lfdir == 0) {
1808 		inum = ROOTINO;
1809 		if ((dp = ginode()) == NULL) {
1810 			inum = orphan;
1811 			return (0);
1812 		}
1813 		pfunc = findino;
1814 		srchname = lfname;
1815 		filsize = dp->di_size;
1816 		parentdir = 0;
1817 		ckinode(dp, DATA);
1818 		inum = orphan;
1819 		if ((lfdir = parentdir) == 0) {
1820 			pfatal("SORRY. NO lost+found DIRECTORY");
1821 			printf("\n\n");
1822 			return (0);
1823 		}
1824 	}
1825 	inum = lfdir;
1826 	if ((dp = ginode()) == NULL || !DIRCT || statemap[inum] != FSTATE) {
1827 		inum = orphan;
1828 		pfatal("SORRY. NO lost+found DIRECTORY");
1829 		printf("\n\n");
1830 		return (0);
1831 	}
1832 	if (fragoff(&sblock, dp->di_size)) {
1833 		dp->di_size = fragroundup(&sblock, dp->di_size);
1834 		inodirty();
1835 	}
1836 	filsize = dp->di_size;
1837 	inum = orphan;
1838 	pfunc = mkentry;
1839 	if ((ckinode(dp, DATA) & ALTERD) == 0) {
1840 		pfatal("SORRY. NO SPACE IN lost+found DIRECTORY");
1841 		printf("\n\n");
1842 		return (0);
1843 	}
1844 	lncntp[inum]--;
1845 	if (lostdir) {
1846 		pfunc = chgdd;
1847 		dp = ginode();
1848 		filsize = dp->di_size;
1849 		ckinode(dp, DATA);
1850 		inum = lfdir;
1851 		if ((dp = ginode()) != NULL) {
1852 			dp->di_nlink++;
1853 			inodirty();
1854 			lncntp[inum]++;
1855 		}
1856 		inum = orphan;
1857 		pwarn("DIR I=%u CONNECTED. ", orphan);
1858 		printf("PARENT WAS I=%u\n", pdir);
1859 		if (preen == 0)
1860 			printf("\n");
1861 	}
1862 	return (1);
1863 }
1864 
1865 bread(fcp, buf, blk, size)
1866 	daddr_t blk;
1867 	register struct filecntl *fcp;
1868 	register size;
1869 	char *buf;
1870 {
1871 	if (lseek(fcp->rfdes, blk * DEV_BSIZE, 0) < 0)
1872 		rwerr("SEEK", blk);
1873 	else if (read(fcp->rfdes, buf, size) == size)
1874 		return (1);
1875 	rwerr("READ", blk);
1876 	return (0);
1877 }
1878 
1879 bwrite(fcp, buf, blk, size)
1880 	daddr_t blk;
1881 	register struct filecntl *fcp;
1882 	register size;
1883 	char *buf;
1884 {
1885 
1886 	if (fcp->wfdes < 0)
1887 		return (0);
1888 	if (lseek(fcp->wfdes, blk * DEV_BSIZE, 0) < 0)
1889 		rwerr("SEEK", blk);
1890 	else if (write(fcp->wfdes, buf, size) == size) {
1891 		fcp->mod = 1;
1892 		return (1);
1893 	}
1894 	rwerr("WRITE", blk);
1895 	return (0);
1896 }
1897 
1898 catch()
1899 {
1900 
1901 	ckfini();
1902 	exit(12);
1903 }
1904 
1905 char *
1906 unrawname(cp)
1907 	char *cp;
1908 {
1909 	char *dp = rindex(cp, '/');
1910 	struct stat stb;
1911 
1912 	if (dp == 0)
1913 		return (cp);
1914 	if (stat(cp, &stb) < 0)
1915 		return (cp);
1916 	if ((stb.st_mode&S_IFMT) != S_IFCHR)
1917 		return (cp);
1918 	if (*(dp+1) != 'r')
1919 		return (cp);
1920 	strcpy(dp+1, dp+2);
1921 	return (cp);
1922 }
1923 
1924 char *
1925 rawname(cp)
1926 	char *cp;
1927 {
1928 	static char rawbuf[32];
1929 	char *dp = rindex(cp, '/');
1930 
1931 	if (dp == 0)
1932 		return (0);
1933 	*dp = 0;
1934 	strcpy(rawbuf, cp);
1935 	*dp = '/';
1936 	strcat(rawbuf, "/r");
1937 	strcat(rawbuf, dp+1);
1938 	return (rawbuf);
1939 }
1940 
1941 /* VARARGS1 */
1942 error(s1, s2, s3, s4)
1943 	char *s1;
1944 {
1945 
1946 	printf(s1, s2, s3, s4);
1947 }
1948 
1949 /* VARARGS1 */
1950 errexit(s1, s2, s3, s4)
1951 	char *s1;
1952 {
1953 	error(s1, s2, s3, s4);
1954 	exit(8);
1955 }
1956 
1957 /*
1958  * An inconsistency occured which shouldn't during normal operations.
1959  * Die if preening, otw just printf.
1960  */
1961 /* VARARGS1 */
1962 pfatal(s, a1, a2, a3)
1963 	char *s;
1964 {
1965 
1966 	if (preen) {
1967 		printf("%s: ", devname);
1968 		printf(s, a1, a2, a3);
1969 		printf("\n");
1970 		preendie();
1971 	}
1972 	printf(s, a1, a2, a3);
1973 }
1974 
1975 preendie()
1976 {
1977 
1978 	printf("%s: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY.\n", devname);
1979 	exit(8);
1980 }
1981 
1982 /*
1983  * Pwarn is like printf when not preening,
1984  * or a warning (preceded by filename) when preening.
1985  */
1986 /* VARARGS1 */
1987 pwarn(s, a1, a2, a3, a4, a5, a6)
1988 	char *s;
1989 {
1990 
1991 	if (preen)
1992 		printf("%s: ", devname);
1993 	printf(s, a1, a2, a3, a4, a5, a6);
1994 }
1995 
1996 panic(s)
1997 	char *s;
1998 {
1999 
2000 	pfatal("internal inconsistency: %s\n");
2001 	exit(12);
2002 }
2003