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