xref: /original-bsd/sbin/fsck/main.c (revision 957a0273)
1 static	char sccsid[] = "@(#)main.c	2.16	(Berkeley)	10/13/82";
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	50	/* 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 = unrawname(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 					goto unknown;
590 				ndb = howmany(dp->di_size, sblock.fs_bsize);
591 				if (SPECIAL)
592 					ndb++;
593 				for (j = ndb; j < NDADDR; j++)
594 					if (dp->di_db[j] != 0)
595 						goto unknown;
596 				for (j = 0, ndb -= NDADDR; ndb > 0; j++)
597 					ndb /= NINDIR(&sblock);
598 				for (; j < NIADDR; j++)
599 					if (dp->di_ib[j] != 0)
600 						goto unknown;
601 				n_files++;
602 				lncntp[inum] = dp->di_nlink;
603 				if (dp->di_nlink <= 0) {
604 					if (badlnp < &badlncnt[MAXLNCNT])
605 						*badlnp++ = inum;
606 					else {
607 						pfatal("LINK COUNT TABLE OVERFLOW");
608 						if (reply("CONTINUE") == 0)
609 							errexit("");
610 					}
611 				}
612 				statemap[inum] = DIRCT ? DSTATE : FSTATE;
613 				badblk = dupblk = 0; filsize = 0; maxblk = 0;
614 				ckinode(dp, ADDR);
615 				continue;
616 		unknown:
617 				pfatal("UNKNOWN FILE TYPE I=%u", inum);
618 				if (reply("CLEAR") == 1) {
619 					zapino(dp);
620 					inodirty();
621 					inosumbad++;
622 				}
623 			} else {
624 				if (isset(cgrp.cg_iused, i)) {
625 					if (debug)
626 						printf("%d bad, marked used\n",
627 						    inum);
628 					inosumbad++;
629 					n--;
630 				}
631 				partial = 0;
632 				for (j = 0; j < NDADDR; j++)
633 					if (dp->di_db[j] != 0)
634 						partial++;
635 				for (j = 0; j < NIADDR; j++)
636 					if (dp->di_ib[j] != 0)
637 						partial++;
638 				if (partial || dp->di_mode != 0 ||
639 				    dp->di_size != 0) {
640 					pfatal("PARTIALLY ALLOCATED INODE I=%u", inum);
641 					if (reply("CLEAR") == 1) {
642 						zapino(dp);
643 						inodirty();
644 						inosumbad++;
645 					}
646 				}
647 			}
648 		}
649 		if (n != cgrp.cg_cs.cs_nifree) {
650 			if (debug)
651 				printf("cg[%d].cg_cs.cs_nifree is %d; calc %d\n",
652 				    c, cgrp.cg_cs.cs_nifree, n);
653 			inosumbad++;
654 		}
655 		if (cgrp.cg_cs.cs_nbfree != sblock.fs_cs(&sblock, c).cs_nbfree
656 		  || cgrp.cg_cs.cs_nffree != sblock.fs_cs(&sblock, c).cs_nffree
657 		  || cgrp.cg_cs.cs_nifree != sblock.fs_cs(&sblock, c).cs_nifree
658 		  || cgrp.cg_cs.cs_ndir != sblock.fs_cs(&sblock, c).cs_ndir)
659 			sbsumbad++;
660 	}
661 }
662 
663 pass1check(blk, size)
664 	daddr_t blk;
665 	int size;
666 {
667 	register daddr_t *dlp;
668 	int res = KEEPON;
669 	int anyout;
670 
671 	anyout = outrange(blk, size);
672 	for (; size > 0; blk++, size--) {
673 		if (anyout && outrange(blk, 1)) {
674 			blkerr("BAD", blk);
675 			if (++badblk >= MAXBAD) {
676 				pwarn("EXCESSIVE BAD BLKS I=%u", inum);
677 				if (preen)
678 					printf(" (SKIPPING)\n");
679 				else if (reply("CONTINUE") == 0)
680 					errexit("");
681 				return (STOP);
682 			}
683 			res = SKIP;
684 		} else if (getbmap(blk)) {
685 			blkerr("DUP", blk);
686 			if (++dupblk >= MAXDUP) {
687 				pwarn("EXCESSIVE DUP BLKS I=%u", inum);
688 				if (preen)
689 					printf(" (SKIPPING)\n");
690 				else if (reply("CONTINUE") == 0)
691 					errexit("");
692 				return (STOP);
693 			}
694 			if (enddup >= &duplist[DUPTBLSIZE]) {
695 				pfatal("DUP TABLE OVERFLOW.");
696 				if (reply("CONTINUE") == 0)
697 					errexit("");
698 				return (STOP);
699 			}
700 			for (dlp = duplist; dlp < muldup; dlp++)
701 				if (*dlp == blk) {
702 					*enddup++ = blk;
703 					break;
704 				}
705 			if (dlp >= muldup) {
706 				*enddup++ = *muldup;
707 				*muldup++ = blk;
708 			}
709 		} else {
710 			n_blks++;
711 			setbmap(blk);
712 		}
713 		filsize++;
714 	}
715 	return (res);
716 }
717 
718 pass1b()
719 {
720 	register int c, i;
721 	register DINODE *dp;
722 
723 	pfunc = pass1bcheck;
724 	inum = 0;
725 	for (c = 0; c < sblock.fs_ncg; c++) {
726 		for (i = 0; i < sblock.fs_ipg; i++, inum++) {
727 			dp = ginode();
728 			if (dp == NULL)
729 				continue;
730 			if (statemap[inum] != USTATE &&
731 			    (ckinode(dp, ADDR) & STOP))
732 				goto out1b;
733 		}
734 	}
735 out1b:
736 	flush(&dfile, &inoblk);
737 }
738 
739 pass1bcheck(blk, size)
740 	daddr_t blk;
741 	int size;
742 {
743 	register daddr_t *dlp;
744 	int res = KEEPON;
745 
746 	for (; size > 0; blk++, size--) {
747 		if (outrange(blk, 1))
748 			res = SKIP;
749 		for (dlp = duplist; dlp < muldup; dlp++)
750 			if (*dlp == blk) {
751 				blkerr("DUP", blk);
752 				*dlp = *--muldup;
753 				*muldup = blk;
754 				if (muldup == duplist)
755 					return (STOP);
756 			}
757 	}
758 	return (res);
759 }
760 
761 pass2()
762 {
763 	register DINODE *dp;
764 
765 	inum = ROOTINO;
766 	thisname = pathp = pathname;
767 	pfunc = pass2check;
768 	switch (statemap[inum]) {
769 
770 	case USTATE:
771 		errexit("ROOT INODE UNALLOCATED. TERMINATING.\n");
772 
773 	case FSTATE:
774 		pfatal("ROOT INODE NOT DIRECTORY");
775 		if (reply("FIX") == 0 || (dp = ginode()) == NULL)
776 			errexit("");
777 		dp->di_mode &= ~IFMT;
778 		dp->di_mode |= IFDIR;
779 		inodirty();
780 		inosumbad++;
781 		statemap[inum] = DSTATE;
782 		/* fall into ... */
783 
784 	case DSTATE:
785 		descend();
786 		break;
787 
788 	case CLEAR:
789 		pfatal("DUPS/BAD IN ROOT INODE");
790 		printf("\n");
791 		if (reply("CONTINUE") == 0)
792 			errexit("");
793 		statemap[inum] = DSTATE;
794 		descend();
795 	}
796 }
797 
798 pass2check(dirp)
799 	register DIRECT *dirp;
800 {
801 	register char *p;
802 	register n;
803 	DINODE *dp;
804 
805 	if ((inum = dirp->d_ino) == 0)
806 		return (KEEPON);
807 	thisname = pathp;
808 	for (p = dirp->d_name; p < &dirp->d_name[MAXNAMLEN]; )
809 		if ((*pathp++ = *p++) == 0) {
810 			--pathp;
811 			break;
812 		}
813 	*pathp = 0;
814 	n = 0;
815 	if (inum > imax || inum <= 0)
816 		n = direrr("I OUT OF RANGE");
817 	else {
818 again:
819 		switch (statemap[inum]) {
820 		case USTATE:
821 			n = direrr("UNALLOCATED");
822 			break;
823 
824 		case CLEAR:
825 			if ((n = direrr("DUP/BAD")) == 1)
826 				break;
827 			if ((dp = ginode()) == NULL)
828 				break;
829 			statemap[inum] = DIRCT ? DSTATE : FSTATE;
830 			goto again;
831 
832 		case FSTATE:
833 			lncntp[inum]--;
834 			break;
835 
836 		case DSTATE:
837 			lncntp[inum]--;
838 			descend();
839 			break;
840 		}
841 	}
842 	pathp = thisname;
843 	if (n == 0)
844 		return (KEEPON);
845 	dirp->d_ino = 0;
846 	return (KEEPON|ALTERD);
847 }
848 
849 pass3()
850 {
851 	ino_t savino;
852 	register DINODE *dp;
853 
854 	for (inum = ROOTINO; inum <= lastino; inum++) {
855 		if (statemap[inum] == DSTATE) {
856 			pfunc = findino;
857 			srchname = "..";
858 			savino = inum;
859 			do {
860 				orphan = inum;
861 				if ((dp = ginode()) == NULL)
862 					break;
863 				filsize = dp->di_size;
864 				parentdir = 0;
865 				ckinode(dp, DATA);
866 				if ((inum = parentdir) == 0)
867 					break;
868 			} while (statemap[inum] == DSTATE);
869 			inum = orphan;
870 			if (linkup() == 1) {
871 				thisname = pathp = pathname;
872 				*pathp++ = '?';
873 				pfunc = pass2check;
874 				descend();
875 			}
876 			inum = savino;
877 		}
878 	}
879 }
880 
881 pass4()
882 {
883 	register int n;
884 	register ino_t *blp;
885 
886 	pfunc = pass4check;
887 	for (inum = ROOTINO; inum <= lastino; inum++) {
888 		switch (statemap[inum]) {
889 
890 		case FSTATE:
891 			n = lncntp[inum];
892 			if (n)
893 				adjust((short)n);
894 			else {
895 				for (blp = badlncnt;blp < badlnp; blp++)
896 					if (*blp == inum) {
897 						clri("UNREF", 1);
898 						break;
899 					}
900 			}
901 			break;
902 
903 		case DSTATE:
904 			clri("UNREF", 1);
905 			break;
906 
907 		case CLEAR:
908 			clri("BAD/DUP", 1);
909 			break;
910 		}
911 	}
912 	if (imax - ROOTINO - n_files != sblock.fs_cstotal.cs_nifree) {
913 		pwarn("FREE INODE COUNT WRONG IN SUPERBLK");
914 		if (preen)
915 			printf(" (FIXED)\n");
916 		if (preen || reply("FIX") == 1) {
917 			sblock.fs_cstotal.cs_nifree = imax - ROOTINO - n_files;
918 			sbdirty();
919 		}
920 	}
921 	flush(&dfile, &fileblk);
922 }
923 
924 pass4check(blk, size)
925 	daddr_t blk;
926 {
927 	register daddr_t *dlp;
928 	int res = KEEPON;
929 
930 	for (; size > 0; blk++, size--) {
931 		if (outrange(blk, 1))
932 			res = SKIP;
933 		else if (getbmap(blk)) {
934 			for (dlp = duplist; dlp < enddup; dlp++)
935 				if (*dlp == blk) {
936 					*dlp = *--enddup;
937 					return (KEEPON);
938 				}
939 			clrbmap(blk);
940 			n_blks--;
941 		}
942 	}
943 	return (res);
944 }
945 
946 pass5()
947 {
948 	register int c, n, i, b, d;
949 	short bo[MAXCPG][NRPOS];
950 	long botot[MAXCPG];
951 	long frsum[MAXFRAG];
952 	int blk;
953 	daddr_t cbase;
954 	int blockbits = (1<<sblock.fs_frag)-1;
955 
956 	blkcpy((unsigned)bmapsz, blockmap, freemap);
957 	dupblk = 0;
958 	n_index = sblock.fs_ncg * (cgdmin(&sblock, 0) - cgtod(&sblock, 0));
959 	for (c = 0; c < sblock.fs_ncg; c++) {
960 		cbase = cgbase(&sblock, c);
961 		bzero(botot, sizeof (botot));
962 		bzero(bo, sizeof (bo));
963 		bzero(frsum, sizeof (frsum));
964 		/*
965 		 * need to account for the super blocks
966 		 * which appear (inaccurately) bad
967 		 */
968 		n_bad += cgtod(&sblock, c) - cgsblock(&sblock, c);
969 		if (getblk(&cgblk, cgtod(&sblock, c), sblock.fs_cgsize) == 0)
970 			continue;
971 		if (cgrp.cg_magic != CG_MAGIC) {
972 			pfatal("cg %d: bad magic number\n", c);
973 			bzero((caddr_t)&cgrp, sblock.fs_cgsize);
974 		}
975 		for (b = 0; b < sblock.fs_fpg; b += sblock.fs_frag) {
976 			blk = blkmap(&sblock, cgrp.cg_free, b);
977 			if (blk == 0)
978 				continue;
979 			if (blk == blockbits) {
980 				if (pass5check(cbase+b, sblock.fs_frag) == STOP)
981 					goto out5;
982 				/* this is clumsy ... */
983 				n_ffree -= sblock.fs_frag;
984 				n_bfree++;
985 				botot[cbtocylno(&sblock, b)]++;
986 				bo[cbtocylno(&sblock, b)]
987 				    [cbtorpos(&sblock, b)]++;
988 				continue;
989 			}
990 			for (d = 0; d < sblock.fs_frag; d++)
991 				if ((blk & (1<<d)) &&
992 				    pass5check(cbase+b+d,1) == STOP)
993 					goto out5;
994 			fragacct(&sblock, blk, frsum, 1);
995 		}
996 		if (bcmp(cgrp.cg_frsum, frsum, sizeof (frsum))) {
997 			if (debug)
998 			for (i = 0; i < sblock.fs_frag; i++)
999 				if (cgrp.cg_frsum[i] != frsum[i])
1000 				printf("cg[%d].cg_frsum[%d] have %d calc %d\n",
1001 				    c, i, cgrp.cg_frsum[i], frsum[i]);
1002 			frsumbad++;
1003 		}
1004 		if (bcmp(cgrp.cg_btot, botot, sizeof (botot))) {
1005 			if (debug)
1006 			for (n = 0; n < sblock.fs_cpg; n++)
1007 				if (botot[n] != cgrp.cg_btot[n])
1008 				printf("cg[%d].cg_btot[%d] have %d calc %d\n",
1009 				    c, n, cgrp.cg_btot[n], botot[n]);
1010 			offsumbad++;
1011 		}
1012 		if (bcmp(cgrp.cg_b, bo, sizeof (bo))) {
1013 			if (debug)
1014 			for (i = 0; i < NRPOS; i++)
1015 				if (bo[n][i] != cgrp.cg_b[n][i])
1016 				printf("cg[%d].cg_b[%d][%d] have %d calc %d\n",
1017 				    c, n, i, cgrp.cg_b[n][i], bo[n][i]);
1018 			offsumbad++;
1019 		}
1020 	}
1021 out5:
1022 	if (dupblk)
1023 		pwarn("%d DUP BLKS IN BIT MAPS\n", dupblk);
1024 	if (fixcg == 0) {
1025 		if ((b = n_blks+n_ffree+sblock.fs_frag*n_bfree+n_index+n_bad) != fmax) {
1026 			pwarn("%ld BLK(S) MISSING\n", fmax - b);
1027 			fixcg = 1;
1028 		} else if (inosumbad + offsumbad + frsumbad + sbsumbad) {
1029 			pwarn("SUMMARY INFORMATION %s%s%s%sBAD\n",
1030 			    inosumbad ? "(INODE FREE) " : "",
1031 			    offsumbad ? "(BLOCK OFFSETS) " : "",
1032 			    frsumbad ? "(FRAG SUMMARIES) " : "",
1033 			    sbsumbad ? "(SUPER BLOCK SUMMARIES) " : "");
1034 			fixcg = 1;
1035 		} else if (n_ffree != sblock.fs_cstotal.cs_nffree ||
1036 		    n_bfree != sblock.fs_cstotal.cs_nbfree) {
1037 			pwarn("FREE BLK COUNT(S) WRONG IN SUPERBLK");
1038 			if (preen)
1039 				printf(" (FIXED)\n");
1040 			if (preen || reply("FIX") == 1) {
1041 				sblock.fs_cstotal.cs_nffree = n_ffree;
1042 				sblock.fs_cstotal.cs_nbfree = n_bfree;
1043 				sbdirty();
1044 			}
1045 		}
1046 	}
1047 	if (fixcg) {
1048 		pwarn("BAD CYLINDER GROUPS");
1049 		if (preen)
1050 			printf(" (SALVAGED)\n");
1051 		else if (reply("SALVAGE") == 0)
1052 			fixcg = 0;
1053 	}
1054 }
1055 
1056 pass5check(blk, size)
1057 	daddr_t blk;
1058 	int size;
1059 {
1060 
1061 	if (outrange(blk, size)) {
1062 		fixcg = 1;
1063 		if (preen)
1064 			pfatal("BAD BLOCKS IN BIT MAPS.");
1065 		if (++badblk >= MAXBAD) {
1066 			printf("EXCESSIVE BAD BLKS IN BIT MAPS.");
1067 			if (reply("CONTINUE") == 0)
1068 				errexit("");
1069 			return (STOP);
1070 		}
1071 	}
1072 	for (; size > 0; blk++, size--)
1073 		if (getfmap(blk)) {
1074 			fixcg = 1;
1075 			++dupblk;
1076 		} else {
1077 			n_ffree++;
1078 			setfmap(blk);
1079 		}
1080 	return (KEEPON);
1081 }
1082 
1083 ckinode(dp, flg)
1084 	DINODE *dp;
1085 	register flg;
1086 {
1087 	register daddr_t *ap;
1088 	register ret;
1089 	int (*func)(), n, ndb, size, offset;
1090 	ino_t number = inum;
1091 	DINODE dino;
1092 
1093 	if (SPECIAL)
1094 		return (KEEPON);
1095 	dino = *dp;
1096 	func = (flg == ADDR) ? pfunc : dirscan;
1097 	ndb = howmany(dino.di_size, sblock.fs_bsize);
1098 	for (ap = &dino.di_db[0]; ap < &dino.di_db[NDADDR]; ap++) {
1099 		if (--ndb == 0 && (offset = blkoff(&sblock, dino.di_size)) != 0)
1100 			size = numfrags(&sblock, fragroundup(&sblock, offset));
1101 		else
1102 			size = sblock.fs_frag;
1103 		dnum = number;
1104 		if (*ap && (ret = (*func)(*ap, size)) & STOP)
1105 			return (ret);
1106 	}
1107 	for (ap = &dino.di_ib[0], n = 1; n <= 2; ap++, n++) {
1108 		dnum = number;
1109 		if (*ap) {
1110 			ret = iblock(*ap, n, flg,
1111 			    dino.di_size - sblock.fs_bsize * NDADDR);
1112 			if (ret & STOP)
1113 				return (ret);
1114 		}
1115 	}
1116 	return (KEEPON);
1117 }
1118 
1119 iblock(blk, ilevel, flg, isize)
1120 	daddr_t blk;
1121 	register ilevel;
1122 	int isize;
1123 {
1124 	register daddr_t *ap;
1125 	register daddr_t *aplim;
1126 	register int i, n;
1127 	int (*func)(), nif;
1128 	BUFAREA ib;
1129 
1130 	if (flg == ADDR) {
1131 		func = pfunc;
1132 		if (((n = (*func)(blk, sblock.fs_frag)) & KEEPON) == 0)
1133 			return (n);
1134 	} else
1135 		func = dirscan;
1136 	if (outrange(blk, sblock.fs_frag))		/* protect thyself */
1137 		return (SKIP);
1138 	initbarea(&ib);
1139 	if (getblk(&ib, blk, sblock.fs_bsize) == NULL)
1140 		return (SKIP);
1141 	ilevel--;
1142 	if (ilevel == 0) {
1143 		nif = lblkno(&sblock, isize) + 1;
1144 	} else /* ilevel == 1 */ {
1145 		nif = isize / (sblock.fs_bsize * NINDIR(&sblock)) + 1;
1146 	}
1147 	if (nif > NINDIR(&sblock))
1148 		nif = NINDIR(&sblock);
1149 	aplim = &ib.b_un.b_indir[nif];
1150 	for (ap = ib.b_un.b_indir, i = 1; ap < aplim; ap++, i++)
1151 		if (*ap) {
1152 			if (ilevel > 0)
1153 				n = iblock(*ap, ilevel, flg,
1154 				    isize - i*NINDIR(&sblock)*sblock.fs_bsize);
1155 			else
1156 				n = (*func)(*ap, sblock.fs_frag);
1157 			if (n & STOP)
1158 				return (n);
1159 		}
1160 	return (KEEPON);
1161 }
1162 
1163 outrange(blk, cnt)
1164 	daddr_t blk;
1165 	int cnt;
1166 {
1167 	register int c;
1168 
1169 	if ((unsigned)(blk+cnt) > fmax)
1170 		return (1);
1171 	c = dtog(&sblock, blk);
1172 	if (blk < cgdmin(&sblock, c)) {
1173 		if ((blk+cnt) > cgsblock(&sblock, c)) {
1174 			if (debug) {
1175 				printf("blk %d < cgdmin %d;",
1176 				    blk, cgdmin(&sblock, c));
1177 				printf(" blk+cnt %d > cgsbase %d\n",
1178 				    blk+cnt, cgsblock(&sblock, c));
1179 			}
1180 			return (1);
1181 		}
1182 	} else {
1183 		if ((blk+cnt) > cgbase(&sblock, c+1)) {
1184 			if (debug)  {
1185 				printf("blk %d >= cgdmin %d;",
1186 				    blk, cgdmin(&sblock, c));
1187 				printf(" blk+cnt %d > sblock.fs_fpg %d\n",
1188 				    blk+cnt, sblock.fs_fpg);
1189 			}
1190 			return (1);
1191 		}
1192 	}
1193 	return (0);
1194 }
1195 
1196 blkerr(s, blk)
1197 	daddr_t blk;
1198 	char *s;
1199 {
1200 
1201 	pfatal("%ld %s I=%u", blk, s, inum);
1202 	printf("\n");
1203 	statemap[inum] = CLEAR;
1204 }
1205 
1206 descend()
1207 {
1208 	register DINODE *dp;
1209 	register char *savname;
1210 	off_t savsize;
1211 
1212 	statemap[inum] = FSTATE;
1213 	if ((dp = ginode()) == NULL)
1214 		return;
1215 	savname = thisname;
1216 	*pathp++ = '/';
1217 	savsize = filsize;
1218 	filsize = dp->di_size;
1219 	ckinode(dp, DATA);
1220 	thisname = savname;
1221 	*--pathp = 0;
1222 	filsize = savsize;
1223 }
1224 
1225 struct dirstuff {
1226 	int loc;
1227 	int blkno;
1228 	int blksiz;
1229 	ino_t number;
1230 	enum {DONTKNOW, NOFIX, FIX} fix;
1231 };
1232 
1233 dirscan(blk, nf)
1234 	daddr_t blk;
1235 	int nf;
1236 {
1237 	register DIRECT *dp;
1238 	struct dirstuff dirp;
1239 	int blksiz, dsize, n;
1240 	char dbuf[DIRBLKSIZ];
1241 
1242 	if (outrange(blk, 1)) {
1243 		filsize -= sblock.fs_bsize;
1244 		return (SKIP);
1245 	}
1246 	blksiz = nf * sblock.fs_fsize;
1247 	dirp.loc = 0;
1248 	dirp.blkno = blk;
1249 	dirp.blksiz = blksiz;
1250 	if (dirp.number != dnum) {
1251 		dirp.number = dnum;
1252 		dirp.fix = DONTKNOW;
1253 	}
1254 	for (dp = readdir(&dirp); dp != NULL; dp = readdir(&dirp)) {
1255 		dsize = dp->d_reclen;
1256 		bcopy(dp, dbuf, dsize);
1257 		if ((n = (*pfunc)(dbuf)) & ALTERD) {
1258 			if (getblk(&fileblk, blk, blksiz) != NULL) {
1259 				bcopy(dbuf, dp, dsize);
1260 				dirty(&fileblk);
1261 				sbdirty();
1262 			} else
1263 				n &= ~ALTERD;
1264 		}
1265 		if (n & STOP)
1266 			return (n);
1267 	}
1268 	return (filsize > 0 ? KEEPON : STOP);
1269 }
1270 
1271 /*
1272  * get next entry in a directory.
1273  */
1274 DIRECT *
1275 readdir(dirp)
1276 	register struct dirstuff *dirp;
1277 {
1278 	register DIRECT *dp, *ndp;
1279 	long size;
1280 
1281 	if (getblk(&fileblk, dirp->blkno, dirp->blksiz) == NULL) {
1282 		filsize -= dirp->blksiz - dirp->loc;
1283 		return NULL;
1284 	}
1285 	while (dirp->loc % DIRBLKSIZ == 0 && filsize > 0 &&
1286 	    dirp->loc < dirp->blksiz) {
1287 		dp = (DIRECT *)(dirblk.b_buf + dirp->loc);
1288 	   	if (dp->d_ino < imax &&
1289 		    dp->d_namlen <= MAXNAMLEN && dp->d_namlen >= 0 &&
1290 		    dp->d_reclen > 0 && dp->d_reclen <= DIRBLKSIZ)
1291 			break;
1292 		dirp->loc += DIRBLKSIZ;
1293 		filsize -= DIRBLKSIZ;
1294 		if (dirp->fix == DONTKNOW) {
1295 			pwarn("DIRECTORY %D CORRUPTED", dirp->number);
1296 			dirp->fix = NOFIX;
1297 			if (preen) {
1298 				printf(" (SALVAGED)\n");
1299 				dirp->fix = FIX;
1300 			} else if (reply("SALVAGE") != 0)
1301 				dirp->fix = FIX;
1302 		}
1303 		if (dirp->fix != FIX)
1304 			continue;
1305 		dp->d_reclen = DIRBLKSIZ;
1306 		dp->d_ino = 0;
1307 		dp->d_namlen = 0;
1308 		dirty(&fileblk);
1309 	}
1310 	if (filsize <= 0 || dirp->loc >= dirp->blksiz)
1311 		return NULL;
1312 	dp = (DIRECT *)(dirblk.b_buf + dirp->loc);
1313 	dirp->loc += dp->d_reclen;
1314 	filsize -= dp->d_reclen;
1315 	ndp = (DIRECT *)(dirblk.b_buf + dirp->loc);
1316 	if ((filsize <= 0 && dirp->loc % DIRBLKSIZ != 0) ||
1317 	    (dirp->loc < dirp->blksiz && filsize > 0 &&
1318 	    (ndp->d_ino >= imax ||
1319 	    ndp->d_namlen > MAXNAMLEN || ndp->d_namlen < 0 ||
1320 	    ndp->d_reclen <= 0 ||
1321 	    ndp->d_reclen > DIRBLKSIZ - (dirp->loc % DIRBLKSIZ)))) {
1322 		size = DIRBLKSIZ - (dirp->loc % DIRBLKSIZ);
1323 		dirp->loc += size;
1324 		filsize -= size;
1325 		if (dirp->fix == DONTKNOW) {
1326 			pwarn("DIRECTORY %D CORRUPTED", dirp->number);
1327 			dirp->fix = NOFIX;
1328 			if (preen) {
1329 				printf(" (SALVAGED)\n");
1330 				dirp->fix = FIX;
1331 			} else if (reply("SALVAGE") != 0)
1332 				dirp->fix = FIX;
1333 		}
1334 		if (dirp->fix == FIX) {
1335 			dp->d_reclen += size;
1336 			dirty(&fileblk);
1337 		}
1338 	}
1339 	return (dp);
1340 }
1341 
1342 direrr(s)
1343 	char *s;
1344 {
1345 	register DINODE *dp;
1346 
1347 	pwarn("%s ", s);
1348 	pinode();
1349 	printf("\n");
1350 	if ((dp = ginode()) != NULL && ftypeok(dp))
1351 		pfatal("%s=%s", DIRCT?"DIR":"FILE", pathname);
1352 	else
1353 		pfatal("NAME=%s", pathname);
1354 	return (reply("REMOVE"));
1355 }
1356 
1357 adjust(lcnt)
1358 	register short lcnt;
1359 {
1360 	register DINODE *dp;
1361 
1362 	if ((dp = ginode()) == NULL)
1363 		return;
1364 	if (dp->di_nlink == lcnt) {
1365 		if (linkup() == 0)
1366 			clri("UNREF", 0);
1367 	}
1368 	else {
1369 		pwarn("LINK COUNT %s",
1370 			(lfdir==inum)?lfname:(DIRCT?"DIR":"FILE"));
1371 		pinode();
1372 		printf(" COUNT %d SHOULD BE %d",
1373 			dp->di_nlink, dp->di_nlink-lcnt);
1374 		if (preen) {
1375 			if (lcnt < 0) {
1376 				printf("\n");
1377 				preendie();
1378 			}
1379 			printf(" (ADJUSTED)\n");
1380 		}
1381 		if (preen || reply("ADJUST") == 1) {
1382 			dp->di_nlink -= lcnt;
1383 			inodirty();
1384 		}
1385 	}
1386 }
1387 
1388 clri(s, flg)
1389 	char *s;
1390 {
1391 	register DINODE *dp;
1392 
1393 	if ((dp = ginode()) == NULL)
1394 		return;
1395 	if (flg == 1) {
1396 		pwarn("%s %s", s, DIRCT?"DIR":"FILE");
1397 		pinode();
1398 	}
1399 	if (preen || reply("CLEAR") == 1) {
1400 		if (preen)
1401 			printf(" (CLEARED)\n");
1402 		n_files--;
1403 		pfunc = pass4check;
1404 		ckinode(dp, ADDR);
1405 		zapino(dp);
1406 		statemap[inum] = USTATE;
1407 		inodirty();
1408 		inosumbad++;
1409 	}
1410 }
1411 
1412 badsb(s)
1413 	char *s;
1414 {
1415 
1416 	if (preen)
1417 		printf("%s: ", devname);
1418 	printf("BAD SUPER BLOCK: %s\n", s);
1419 	pwarn("USE -b OPTION TO FSCK TO SPECIFY LOCATION OF AN ALTERNATE\n");
1420 	pfatal("SUPER-BLOCK TO SUPPLY NEEDED INFORMATION; SEE fsck(8).\n");
1421 }
1422 
1423 DINODE *
1424 ginode()
1425 {
1426 	daddr_t iblk;
1427 
1428 	if (inum < ROOTINO || inum > imax) {
1429 		if (debug && (inum < 0 || inum > imax))
1430 			printf("inum out of range (%d)\n", inum);
1431 		return (NULL);
1432 	}
1433 	if (inum < startinum || inum >= startinum + INOPB(&sblock)) {
1434 		iblk = itod(&sblock, inum);
1435 		if (getblk(&inoblk, iblk, sblock.fs_bsize) == NULL) {
1436 			return (NULL);
1437 		}
1438 		startinum = (inum / INOPB(&sblock)) * INOPB(&sblock);
1439 	}
1440 	return (&inoblk.b_un.b_dinode[inum % INOPB(&sblock)]);
1441 }
1442 
1443 ftypeok(dp)
1444 	DINODE *dp;
1445 {
1446 	switch (dp->di_mode & IFMT) {
1447 
1448 	case IFDIR:
1449 	case IFREG:
1450 	case IFBLK:
1451 	case IFCHR:
1452 	case IFLNK:
1453 		return (1);
1454 
1455 	default:
1456 		return (0);
1457 	}
1458 }
1459 
1460 reply(s)
1461 	char *s;
1462 {
1463 	char line[80];
1464 
1465 	if (preen)
1466 		pfatal("INTERNAL ERROR: GOT TO reply()");
1467 	rplyflag = 1;
1468 	printf("\n%s? ", s);
1469 	if (nflag || dfile.wfdes < 0) {
1470 		printf(" no\n\n");
1471 		return (0);
1472 	}
1473 	if (yflag) {
1474 		printf(" yes\n\n");
1475 		return (1);
1476 	}
1477 	if (getline(stdin, line, sizeof(line)) == EOF)
1478 		errexit("\n");
1479 	printf("\n");
1480 	if (line[0] == 'y' || line[0] == 'Y')
1481 		return (1);
1482 	else
1483 		return (0);
1484 }
1485 
1486 getline(fp, loc, maxlen)
1487 	FILE *fp;
1488 	char *loc;
1489 {
1490 	register n;
1491 	register char *p, *lastloc;
1492 
1493 	p = loc;
1494 	lastloc = &p[maxlen-1];
1495 	while ((n = getc(fp)) != '\n') {
1496 		if (n == EOF)
1497 			return (EOF);
1498 		if (!isspace(n) && p < lastloc)
1499 			*p++ = n;
1500 	}
1501 	*p = 0;
1502 	return (p - loc);
1503 }
1504 
1505 BUFAREA *
1506 getblk(bp, blk, size)
1507 	daddr_t blk;
1508 	register BUFAREA *bp;
1509 	int size;
1510 {
1511 	register struct filecntl *fcp;
1512 	daddr_t dblk;
1513 
1514 	fcp = &dfile;
1515 	dblk = fsbtodb(&sblock, blk);
1516 	if (bp->b_bno == dblk)
1517 		return (bp);
1518 	flush(fcp, bp);
1519 	if (bread(fcp, bp->b_un.b_buf, dblk, size) != 0) {
1520 		bp->b_bno = dblk;
1521 		bp->b_size = size;
1522 		return (bp);
1523 	}
1524 	bp->b_bno = (daddr_t)-1;
1525 	return (NULL);
1526 }
1527 
1528 flush(fcp, bp)
1529 	struct filecntl *fcp;
1530 	register BUFAREA *bp;
1531 {
1532 
1533 	if (bp->b_dirty)
1534 		bwrite(fcp, bp->b_un.b_buf, bp->b_bno, bp->b_size);
1535 	bp->b_dirty = 0;
1536 }
1537 
1538 rwerr(s, blk)
1539 	char *s;
1540 	daddr_t blk;
1541 {
1542 
1543 	if (preen == 0)
1544 		printf("\n");
1545 	pfatal("CANNOT %s: BLK %ld", s, blk);
1546 	if (reply("CONTINUE") == 0)
1547 		errexit("Program terminated\n");
1548 }
1549 
1550 ckfini()
1551 {
1552 
1553 	flush(&dfile, &fileblk);
1554 	flush(&dfile, &sblk);
1555 	if (sblk.b_bno != SBLOCK) {
1556 		sblk.b_bno = SBLOCK;
1557 		sbdirty();
1558 		flush(&dfile, &sblk);
1559 	}
1560 	flush(&dfile, &inoblk);
1561 	close(dfile.rfdes);
1562 	close(dfile.wfdes);
1563 }
1564 
1565 pinode()
1566 {
1567 	register DINODE *dp;
1568 	register char *p;
1569 	char uidbuf[BUFSIZ];
1570 	char *ctime();
1571 
1572 	printf(" I=%u ", inum);
1573 	if ((dp = ginode()) == NULL)
1574 		return;
1575 	printf(" OWNER=");
1576 	if (getpw((int)dp->di_uid, uidbuf) == 0) {
1577 		for (p = uidbuf; *p != ':'; p++);
1578 		*p = 0;
1579 		printf("%s ", uidbuf);
1580 	}
1581 	else {
1582 		printf("%d ", dp->di_uid);
1583 	}
1584 	printf("MODE=%o\n", dp->di_mode);
1585 	if (preen)
1586 		printf("%s: ", devname);
1587 	printf("SIZE=%ld ", dp->di_size);
1588 	p = ctime(&dp->di_mtime);
1589 	printf("MTIME=%12.12s %4.4s ", p+4, p+20);
1590 }
1591 
1592 makecg()
1593 {
1594 	int c, blk;
1595 	daddr_t dbase, d, dlower, dupper, dmax;
1596 	long i, j, s;
1597 	register struct csum *cs;
1598 	register DINODE *dp;
1599 
1600 	sblock.fs_cstotal.cs_nbfree = 0;
1601 	sblock.fs_cstotal.cs_nffree = 0;
1602 	sblock.fs_cstotal.cs_nifree = 0;
1603 	sblock.fs_cstotal.cs_ndir = 0;
1604 	for (c = 0; c < sblock.fs_ncg; c++) {
1605 		dbase = cgbase(&sblock, c);
1606 		dmax = dbase + sblock.fs_fpg;
1607 		if (dmax > sblock.fs_size) {
1608 			for ( ; dmax >= sblock.fs_size; dmax--)
1609 				clrbit(cgrp.cg_free, dmax - dbase);
1610 			dmax++;
1611 		}
1612 		dlower = cgsblock(&sblock, c) - dbase;
1613 		dupper = cgdmin(&sblock, c) - dbase;
1614 		cs = &sblock.fs_cs(&sblock, c);
1615 		cgrp.cg_time = time(0);
1616 		cgrp.cg_magic = CG_MAGIC;
1617 		cgrp.cg_cgx = c;
1618 		cgrp.cg_ncyl = sblock.fs_cpg;
1619 		cgrp.cg_niblk = sblock.fs_ipg;
1620 		cgrp.cg_ndblk = dmax - dbase;
1621 		cgrp.cg_cs.cs_ndir = 0;
1622 		cgrp.cg_cs.cs_nffree = 0;
1623 		cgrp.cg_cs.cs_nbfree = 0;
1624 		cgrp.cg_cs.cs_nifree = 0;
1625 		cgrp.cg_rotor = 0;
1626 		cgrp.cg_frotor = 0;
1627 		cgrp.cg_irotor = 0;
1628 		for (i = 0; i < sblock.fs_frag; i++)
1629 			cgrp.cg_frsum[i] = 0;
1630 		inum = sblock.fs_ipg * c;
1631 		for (i = 0; i < sblock.fs_ipg; inum++, i++) {
1632 			cgrp.cg_cs.cs_nifree++;
1633 			clrbit(cgrp.cg_iused, i);
1634 			dp = ginode();
1635 			if (dp == NULL)
1636 				continue;
1637 			if (ALLOC) {
1638 				if (DIRCT)
1639 					cgrp.cg_cs.cs_ndir++;
1640 				cgrp.cg_cs.cs_nifree--;
1641 				setbit(cgrp.cg_iused, i);
1642 				continue;
1643 			}
1644 		}
1645 		while (i < MAXIPG) {
1646 			clrbit(cgrp.cg_iused, i);
1647 			i++;
1648 		}
1649 		if (c == 0)
1650 			for (i = 0; i < ROOTINO; i++) {
1651 				setbit(cgrp.cg_iused, i);
1652 				cgrp.cg_cs.cs_nifree--;
1653 			}
1654 		for (s = 0; s < MAXCPG; s++) {
1655 			cgrp.cg_btot[s] = 0;
1656 			for (i = 0; i < NRPOS; i++)
1657 				cgrp.cg_b[s][i] = 0;
1658 		}
1659 		if (c == 0) {
1660 			dupper += howmany(sblock.fs_cssize, sblock.fs_fsize);
1661 		}
1662 		for (d = dlower; d < dupper; d++)
1663 			clrbit(cgrp.cg_free, d);
1664 		for (d = 0; (d + sblock.fs_frag) <= dmax - dbase;
1665 		    d += sblock.fs_frag) {
1666 			j = 0;
1667 			for (i = 0; i < sblock.fs_frag; i++) {
1668 				if (!getbmap(dbase + d + i)) {
1669 					setbit(cgrp.cg_free, d + i);
1670 					j++;
1671 				} else
1672 					clrbit(cgrp.cg_free, d+i);
1673 			}
1674 			if (j == sblock.fs_frag) {
1675 				cgrp.cg_cs.cs_nbfree++;
1676 				cgrp.cg_btot[cbtocylno(&sblock, d)]++;
1677 				cgrp.cg_b[cbtocylno(&sblock, d)]
1678 				    [cbtorpos(&sblock, d)]++;
1679 			} else if (j > 0) {
1680 				cgrp.cg_cs.cs_nffree += j;
1681 				blk = blkmap(&sblock, cgrp.cg_free, d);
1682 				fragacct(&sblock, blk, cgrp.cg_frsum, 1);
1683 			}
1684 		}
1685 		for (j = d; d < dmax - dbase; d++) {
1686 			if (!getbmap(dbase + d)) {
1687 				setbit(cgrp.cg_free, d);
1688 				cgrp.cg_cs.cs_nffree++;
1689 			} else
1690 				clrbit(cgrp.cg_free, d);
1691 		}
1692 		if (j != d) {
1693 			blk = blkmap(&sblock, cgrp.cg_free, j);
1694 			fragacct(&sblock, blk, cgrp.cg_frsum, 1);
1695 		}
1696 		for (; d < MAXBPG(&sblock); d++)
1697 			clrbit(cgrp.cg_free, d);
1698 		sblock.fs_cstotal.cs_nffree += cgrp.cg_cs.cs_nffree;
1699 		sblock.fs_cstotal.cs_nbfree += cgrp.cg_cs.cs_nbfree;
1700 		sblock.fs_cstotal.cs_nifree += cgrp.cg_cs.cs_nifree;
1701 		sblock.fs_cstotal.cs_ndir += cgrp.cg_cs.cs_ndir;
1702 		*cs = cgrp.cg_cs;
1703 		bwrite(&dfile, &cgrp, fsbtodb(&sblock, cgtod(&sblock, c)),
1704 		    sblock.fs_cgsize);
1705 	}
1706 	for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) {
1707 		bwrite(&dfile, (char *)sblock.fs_csp[j],
1708 		    fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag),
1709 		    sblock.fs_cssize - i < sblock.fs_bsize ?
1710 		    sblock.fs_cssize - i : sblock.fs_bsize);
1711 	}
1712 	sblock.fs_ronly = 0;
1713 	sblock.fs_fmod = 0;
1714 	sbdirty();
1715 }
1716 
1717 findino(dirp)
1718 	register DIRECT *dirp;
1719 {
1720 	if (dirp->d_ino == 0)
1721 		return (KEEPON);
1722 	if (!strcmp(dirp->d_name, srchname)) {
1723 		if (dirp->d_ino >= ROOTINO && dirp->d_ino <= imax)
1724 			parentdir = dirp->d_ino;
1725 		return (STOP);
1726 	}
1727 	return (KEEPON);
1728 }
1729 
1730 mkentry(dirp)
1731 	register DIRECT *dirp;
1732 {
1733 	register ino_t in;
1734 	register char *p;
1735 	DIRECT newent;
1736 	int newlen, oldlen;
1737 
1738 	newent.d_namlen = 11;
1739 	newlen = DIRSIZ(&newent);
1740 	if (dirp->d_ino != 0)
1741 		oldlen = DIRSIZ(dirp);
1742 	else
1743 		oldlen = 0;
1744 	if (dirp->d_reclen - oldlen < newlen)
1745 		return (KEEPON);
1746 	newent.d_reclen = dirp->d_reclen - oldlen;
1747 	dirp->d_reclen = oldlen;
1748 	dirp = (struct direct *)(((char *)dirp) + oldlen);
1749 	dirp->d_ino = orphan;
1750 	dirp->d_reclen = newent.d_reclen;
1751 	p = &dirp->d_name[2];
1752 	for (in = imax; in > 0; in /= 10)
1753 		p++;
1754 	*--p = 0;
1755 	dirp->d_namlen = p - dirp->d_name;
1756 	in = orphan;
1757 	while (p > dirp->d_name) {
1758 		*--p = (in % 10) + '0';
1759 		in /= 10;
1760 	}
1761 	*p = '#';
1762 	return (ALTERD|STOP);
1763 }
1764 
1765 chgdd(dirp)
1766 	register DIRECT *dirp;
1767 {
1768 	if (dirp->d_name[0] == '.' && dirp->d_name[1] == '.' &&
1769 	dirp->d_name[2] == 0) {
1770 		dirp->d_ino = lfdir;
1771 		return (ALTERD|STOP);
1772 	}
1773 	return (KEEPON);
1774 }
1775 
1776 linkup()
1777 {
1778 	register DINODE *dp;
1779 	register lostdir;
1780 	register ino_t pdir;
1781 
1782 	if ((dp = ginode()) == NULL)
1783 		return (0);
1784 	lostdir = DIRCT;
1785 	pdir = parentdir;
1786 	pwarn("UNREF %s ", lostdir ? "DIR" : "FILE");
1787 	pinode();
1788 	if (preen && dp->di_size == 0)
1789 		return (0);
1790 	if (preen)
1791 		printf(" (RECONNECTED)\n");
1792 	else
1793 		if (reply("RECONNECT") == 0)
1794 			return (0);
1795 	orphan = inum;
1796 	if (lfdir == 0) {
1797 		inum = ROOTINO;
1798 		if ((dp = ginode()) == NULL) {
1799 			inum = orphan;
1800 			return (0);
1801 		}
1802 		pfunc = findino;
1803 		srchname = lfname;
1804 		filsize = dp->di_size;
1805 		parentdir = 0;
1806 		ckinode(dp, DATA);
1807 		inum = orphan;
1808 		if ((lfdir = parentdir) == 0) {
1809 			pfatal("SORRY. NO lost+found DIRECTORY");
1810 			printf("\n\n");
1811 			return (0);
1812 		}
1813 	}
1814 	inum = lfdir;
1815 	if ((dp = ginode()) == NULL || !DIRCT || statemap[inum] != FSTATE) {
1816 		inum = orphan;
1817 		pfatal("SORRY. NO lost+found DIRECTORY");
1818 		printf("\n\n");
1819 		return (0);
1820 	}
1821 	if (fragoff(&sblock, dp->di_size)) {
1822 		dp->di_size = fragroundup(&sblock, dp->di_size);
1823 		inodirty();
1824 	}
1825 	filsize = dp->di_size;
1826 	inum = orphan;
1827 	pfunc = mkentry;
1828 	if ((ckinode(dp, DATA) & ALTERD) == 0) {
1829 		pfatal("SORRY. NO SPACE IN lost+found DIRECTORY");
1830 		printf("\n\n");
1831 		return (0);
1832 	}
1833 	lncntp[inum]--;
1834 	if (lostdir) {
1835 		pfunc = chgdd;
1836 		dp = ginode();
1837 		filsize = dp->di_size;
1838 		ckinode(dp, DATA);
1839 		inum = lfdir;
1840 		if ((dp = ginode()) != NULL) {
1841 			dp->di_nlink++;
1842 			inodirty();
1843 			lncntp[inum]++;
1844 		}
1845 		inum = orphan;
1846 		pwarn("DIR I=%u CONNECTED. ", orphan);
1847 		printf("PARENT WAS I=%u\n", pdir);
1848 		if (preen == 0)
1849 			printf("\n");
1850 	}
1851 	return (1);
1852 }
1853 
1854 bread(fcp, buf, blk, size)
1855 	daddr_t blk;
1856 	register struct filecntl *fcp;
1857 	register size;
1858 	char *buf;
1859 {
1860 	if (lseek(fcp->rfdes, blk * DEV_BSIZE, 0) < 0)
1861 		rwerr("SEEK", blk);
1862 	else if (read(fcp->rfdes, buf, size) == size)
1863 		return (1);
1864 	rwerr("READ", blk);
1865 	return (0);
1866 }
1867 
1868 bwrite(fcp, buf, blk, size)
1869 	daddr_t blk;
1870 	register struct filecntl *fcp;
1871 	register size;
1872 	char *buf;
1873 {
1874 
1875 	if (fcp->wfdes < 0)
1876 		return (0);
1877 	if (lseek(fcp->wfdes, blk * DEV_BSIZE, 0) < 0)
1878 		rwerr("SEEK", blk);
1879 	else if (write(fcp->wfdes, buf, size) == size) {
1880 		fcp->mod = 1;
1881 		return (1);
1882 	}
1883 	rwerr("WRITE", blk);
1884 	return (0);
1885 }
1886 
1887 catch()
1888 {
1889 
1890 	ckfini();
1891 	exit(12);
1892 }
1893 
1894 char *
1895 unrawname(cp)
1896 	char *cp;
1897 {
1898 	char *dp = rindex(cp, '/');
1899 	struct stat stb;
1900 
1901 	if (dp == 0)
1902 		return (cp);
1903 	if (stat(cp, &stb) < 0)
1904 		return (cp);
1905 	if ((stb.st_mode&S_IFMT) != S_IFCHR)
1906 		return (cp);
1907 	if (*(dp+1) != 'r')
1908 		return (cp);
1909 	strcpy(dp+1, dp+2);
1910 	return (cp);
1911 }
1912 
1913 char *
1914 rawname(cp)
1915 	char *cp;
1916 {
1917 	static char rawbuf[32];
1918 	char *dp = rindex(cp, '/');
1919 
1920 	if (dp == 0)
1921 		return (0);
1922 	*dp = 0;
1923 	strcpy(rawbuf, cp);
1924 	*dp = '/';
1925 	strcat(rawbuf, "/r");
1926 	strcat(rawbuf, dp+1);
1927 	return (rawbuf);
1928 }
1929 
1930 /* VARARGS1 */
1931 error(s1, s2, s3, s4)
1932 	char *s1;
1933 {
1934 
1935 	printf(s1, s2, s3, s4);
1936 }
1937 
1938 /* VARARGS1 */
1939 errexit(s1, s2, s3, s4)
1940 	char *s1;
1941 {
1942 	error(s1, s2, s3, s4);
1943 	exit(8);
1944 }
1945 
1946 /*
1947  * An inconsistency occured which shouldn't during normal operations.
1948  * Die if preening, otw just printf.
1949  */
1950 /* VARARGS1 */
1951 pfatal(s, a1, a2, a3)
1952 	char *s;
1953 {
1954 
1955 	if (preen) {
1956 		printf("%s: ", devname);
1957 		printf(s, a1, a2, a3);
1958 		printf("\n");
1959 		preendie();
1960 	}
1961 	printf(s, a1, a2, a3);
1962 }
1963 
1964 preendie()
1965 {
1966 
1967 	printf("%s: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY.\n", devname);
1968 	exit(8);
1969 }
1970 
1971 /*
1972  * Pwarn is like printf when not preening,
1973  * or a warning (preceded by filename) when preening.
1974  */
1975 /* VARARGS1 */
1976 pwarn(s, a1, a2, a3, a4, a5, a6)
1977 	char *s;
1978 {
1979 
1980 	if (preen)
1981 		printf("%s: ", devname);
1982 	printf(s, a1, a2, a3, a4, a5, a6);
1983 }
1984 
1985 panic(s)
1986 	char *s;
1987 {
1988 
1989 	pfatal("internal inconsistency: %s\n");
1990 	exit(12);
1991 }
1992