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