xref: /original-bsd/sbin/fsck/main.c (revision f662c168)
1 static	char *sccsid = "@(#)main.c	1.26 (Berkeley) 03/17/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 	struct dirstuff dirp;
1055 	int blksiz, dsize, n;
1056 	char dbuf[DIRBLKSIZ];
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 		dsize = dp->d_reclen;
1068 		copy(dp, dbuf, dsize);
1069 		if ((n = (*pfunc)(dbuf)) & ALTERD) {
1070 			if (getblk(&fileblk, blk, blksiz) != NULL) {
1071 				copy(dbuf, dp, dsize);
1072 				dirty(&fileblk);
1073 				sbdirty();
1074 			} else
1075 				n &= ~ALTERD;
1076 		}
1077 		if (n & STOP)
1078 			return (n);
1079 	}
1080 	return (filsize > 0 ? KEEPON : STOP);
1081 }
1082 
1083 /*
1084  * get next entry in a directory.
1085  */
1086 DIRECT *
1087 readdir(dirp)
1088 	register struct dirstuff *dirp;
1089 {
1090 	register DIRECT *dp, *ndp;
1091 
1092 	if (getblk(&fileblk, dirp->blkno, dirp->blksiz) == NULL) {
1093 		filsize -= dirp->blksiz - dirp->loc;
1094 		return NULL;
1095 	}
1096 	for (;;) {
1097 		if (filsize <= 0 || dirp->loc >= dirp->blksiz)
1098 			return NULL;
1099 		dp = (DIRECT *)(dirblk.b_buf + dirp->loc);
1100 		dirp->loc += dp->d_reclen;
1101 		filsize -= dp->d_reclen;
1102 		ndp = (DIRECT *)(dirblk.b_buf + dirp->loc);
1103 		if (dirp->loc < dirp->blksiz &&
1104 		    (ndp->d_ino > imax || ndp->d_namlen > MAXNAMLEN ||
1105 		    ndp->d_reclen <= 0 ||
1106 		    ndp->d_reclen > DIRBLKSIZ - (dirp->loc % DIRBLKSIZ))) {
1107 			pwarn("DIRECTORY CORRUPTED");
1108 			if (preen)
1109 				printf(" (SALVAGED)\n");
1110 			else if (reply("SALVAGE") == 0) {
1111 				dirp->loc +=
1112 				    DIRBLKSIZ - (dirp->loc % DIRBLKSIZ);
1113 				filsize -= DIRBLKSIZ - (dirp->loc % DIRBLKSIZ);
1114 				return(dp);
1115 			}
1116 			dirp->loc -= dp->d_reclen;
1117 			filsize += dp->d_reclen;
1118 			dp->d_reclen = DIRBLKSIZ - (dirp->loc % DIRBLKSIZ);
1119 			dirty(&fileblk);
1120 			continue;
1121 		}
1122 		return (dp);
1123 	}
1124 }
1125 
1126 direrr(s)
1127 	char *s;
1128 {
1129 	register DINODE *dp;
1130 
1131 	pwarn("%s ", s);
1132 	pinode();
1133 	printf("\n");
1134 	if ((dp = ginode()) != NULL && ftypeok(dp))
1135 		pfatal("%s=%s", DIRCT?"DIR":"FILE", pathname);
1136 	else
1137 		pfatal("NAME=%s", pathname);
1138 	return (reply("REMOVE"));
1139 }
1140 
1141 adjust(lcnt)
1142 	register short lcnt;
1143 {
1144 	register DINODE *dp;
1145 
1146 	if ((dp = ginode()) == NULL)
1147 		return;
1148 	if (dp->di_nlink == lcnt) {
1149 		if (linkup() == 0)
1150 			clri("UNREF", 0);
1151 	}
1152 	else {
1153 		pwarn("LINK COUNT %s",
1154 			(lfdir==inum)?lfname:(DIRCT?"DIR":"FILE"));
1155 		pinode();
1156 		printf(" COUNT %d SHOULD BE %d",
1157 			dp->di_nlink, dp->di_nlink-lcnt);
1158 		if (preen) {
1159 			if (lcnt < 0) {
1160 				printf("\n");
1161 				preendie();
1162 			}
1163 			printf(" (ADJUSTED)\n");
1164 		}
1165 		if (preen || reply("ADJUST") == 1) {
1166 			dp->di_nlink -= lcnt;
1167 			inodirty();
1168 		}
1169 	}
1170 }
1171 
1172 clri(s, flg)
1173 	char *s;
1174 {
1175 	register DINODE *dp;
1176 
1177 	if ((dp = ginode()) == NULL)
1178 		return;
1179 	if (flg == 1) {
1180 		pwarn("%s %s", s, DIRCT?"DIR":"FILE");
1181 		pinode();
1182 	}
1183 	if (preen || reply("CLEAR") == 1) {
1184 		if (preen)
1185 			printf(" (CLEARED)\n");
1186 		n_files--;
1187 		pfunc = pass4;
1188 		ckinode(dp, ADDR);
1189 		zapino(dp);
1190 		setstate(USTATE);
1191 		inodirty();
1192 		inosumbad++;
1193 	}
1194 }
1195 
1196 setup(dev)
1197 	char *dev;
1198 {
1199 	dev_t rootdev;
1200 	struct ostat statb;
1201 	int super = bflag ? bflag : SBLOCK;
1202 
1203 	bflag = 0;
1204 	if (stat("/", &statb) < 0)
1205 		errexit("Can't stat root\n");
1206 	rootdev = statb.st_dev;
1207 	if (stat(dev, &statb) < 0) {
1208 		error("Can't stat %s\n", dev);
1209 		return (0);
1210 	}
1211 	rawflg = 0;
1212 	if ((statb.st_mode & S_IFMT) == S_IFBLK)
1213 		;
1214 	else if ((statb.st_mode & S_IFMT) == S_IFCHR)
1215 		rawflg++;
1216 	else {
1217 		if (reply("file is not a block or character device; OK") == 0)
1218 			return (0);
1219 	}
1220 	if (rootdev == statb.st_rdev)
1221 		hotroot++;
1222 	if ((dfile.rfdes = open(dev, 0)) < 0) {
1223 		error("Can't open %s\n", dev);
1224 		return (0);
1225 	}
1226 	if (preen == 0)
1227 		printf("** %s", dev);
1228 	if (nflag || (dfile.wfdes = open(dev, 1)) < 0) {
1229 		dfile.wfdes = -1;
1230 		if (preen)
1231 			pfatal("NO WRITE ACCESS");
1232 		printf(" (NO WRITE)");
1233 	}
1234 	if (preen == 0)
1235 		printf("\n");
1236 	fixcg = 0; inosumbad = 0; offsumbad = 0; frsumbad = 0;
1237 	dfile.mod = 0;
1238 	n_files = n_blks = n_ffree = n_bfree = 0;
1239 	muldup = enddup = &duplist[0];
1240 	badlnp = &badlncnt[0];
1241 	lfdir = 0;
1242 	rplyflag = 0;
1243 	initbarea(&sblk);
1244 	initbarea(&fileblk);
1245 	initbarea(&inoblk);
1246 	initbarea(&cgblk);
1247 	if (bread(&dfile, &sblock, super, SBSIZE) == 0)
1248 		return (0);
1249 	sblk.b_bno = super;
1250 	sblk.b_size = SBSIZE;
1251 	/*
1252 	 * run a few consistency checks of the super block
1253 	 */
1254 	if (sblock.fs_magic != FS_MAGIC)
1255 		{ badsb("MAGIC NUMBER WRONG"); return (0); }
1256 	if (sblock.fs_ncg < 1)
1257 		{ badsb("NCG OUT OF RANGE"); return (0); }
1258 	if (sblock.fs_cpg < 1 || sblock.fs_cpg > MAXCPG)
1259 		{ badsb("CPG OUT OF RANGE"); return (0); }
1260 	if (sblock.fs_nsect < 1)
1261 		{ badsb("NSECT < 1"); return (0); }
1262 	if (sblock.fs_ntrak < 1)
1263 		{ badsb("NTRAK < 1"); return (0); }
1264 	if (sblock.fs_spc != sblock.fs_nsect * sblock.fs_ntrak)
1265 		{ badsb("SPC DOES NOT JIVE w/NTRAK*NSECT"); return (0); }
1266 	if (sblock.fs_ipg % INOPB(&sblock))
1267 		{ badsb("INODES NOT MULTIPLE OF A BLOCK"); return (0); }
1268 	if (cgdmin(&sblock, 0) >= sblock.fs_cpg * sblock.fs_spc / NSPF(&sblock))
1269 		{ badsb("IMPLIES MORE INODE THAN DATA BLOCKS"); return (0); }
1270 	if (sblock.fs_ncg * sblock.fs_cpg < sblock.fs_ncyl ||
1271 	    (sblock.fs_ncg - 1) * sblock.fs_cpg >= sblock.fs_ncyl)
1272 		{ badsb("NCYL DOES NOT JIVE WITH NCG*CPG"); return (0); }
1273 	if (sblock.fs_fpg != sblock.fs_cpg * sblock.fs_spc / NSPF(&sblock))
1274 		{ badsb("FPG DOES NOT JIVE WITH CPG & SPC"); return (0); }
1275 	if (sblock.fs_size * NSPF(&sblock) <=
1276 	    (sblock.fs_ncyl - 1) * sblock.fs_spc)
1277 		{ badsb("SIZE PREPOSTEROUSLY SMALL"); return (0); }
1278 	if (sblock.fs_size * NSPF(&sblock) > sblock.fs_ncyl * sblock.fs_spc)
1279 		{ badsb("SIZE PREPOSTEROUSLY LARGE"); return (0); }
1280 	/* rest we COULD repair... */
1281 	if (sblock.fs_sblkno != SBLOCK)
1282 		{ badsb("SBLKNO CORRUPTED"); return (0); }
1283 	if (sblock.fs_cblkno !=
1284 	    roundup(howmany(BBSIZE + SBSIZE, sblock.fs_fsize), sblock.fs_frag))
1285 		{ badsb("CBLKNO CORRUPTED"); return (0); }
1286 	if (sblock.fs_iblkno != sblock.fs_cblkno + sblock.fs_frag)
1287 		{ badsb("IBLKNO CORRUPTED"); return (0); }
1288 	if (sblock.fs_dblkno !=
1289 	    sblock.fs_iblkno + sblock.fs_ipg / INOPF(&sblock))
1290 		{ badsb("DBLKNO CORRUPTED"); return (0); }
1291 	if (sblock.fs_cgsize != fragroundup(&sblock,
1292 	    sizeof(struct cg) + howmany(sblock.fs_fpg, NBBY)))
1293 		{ badsb("CGSIZE INCORRECT"); return (0); }
1294 	if (sblock.fs_cssize != sblock.fs_ncg * sizeof(struct csum))
1295 		{ badsb("CSSIZE INCORRECT"); return (0); }
1296 	fmax = sblock.fs_size;
1297 	imax = sblock.fs_ncg * sblock.fs_ipg;
1298 	/*
1299 	 * allocate the necessary maps
1300 	 */
1301 	bmapsz = roundup(howmany(fmax, NBBY), sizeof(short));
1302 	blkmap = (char *)calloc(bmapsz, sizeof (char));
1303 	freemap = (char *)calloc(bmapsz, sizeof (char));
1304 	statemap = (char *)calloc(imax+1, sizeof(char));
1305 	lncntp = (short *)calloc(imax+1, sizeof(short));
1306 
1307 	startinum = imax + 1;
1308 	return (1);
1309 
1310 badsb:
1311 	ckfini();
1312 	return (0);
1313 }
1314 
1315 badsb(s)
1316 	char *s;
1317 {
1318 
1319 	if (preen)
1320 		printf("%s: ", devname);
1321 	printf("BAD SUPER BLOCK: %s\n", s);
1322 	pwarn("USE -b OPTION TO FSCK TO SPECIFY LOCATION OF AN ALTERNATE\n");
1323 	pfatal("SUPER-BLOCK TO SUPPLY NEEDED INFORMATION; SEE fsck(8).\n");
1324 }
1325 
1326 DINODE *
1327 ginode()
1328 {
1329 	daddr_t iblk;
1330 
1331 	if (inum < ROOTINO || inum > imax)
1332 		return (NULL);
1333 	if (inum < startinum || inum >= startinum + INOPB(&sblock)) {
1334 		iblk = itod(&sblock, inum);
1335 		if (getblk(&inoblk, iblk, sblock.fs_bsize) == NULL) {
1336 			return (NULL);
1337 		}
1338 		startinum = (inum / INOPB(&sblock)) * INOPB(&sblock);
1339 	}
1340 	return (&inoblk.b_un.b_dinode[inum % INOPB(&sblock)]);
1341 }
1342 
1343 ftypeok(dp)
1344 	DINODE *dp;
1345 {
1346 	switch (dp->di_mode & IFMT) {
1347 
1348 	case IFDIR:
1349 	case IFREG:
1350 	case IFBLK:
1351 	case IFCHR:
1352 	case IFMPC:
1353 	case IFMPB:
1354 		return (1);
1355 
1356 	default:
1357 		return (0);
1358 	}
1359 }
1360 
1361 reply(s)
1362 	char *s;
1363 {
1364 	char line[80];
1365 
1366 	if (preen)
1367 		pfatal("INTERNAL ERROR: GOT TO reply()");
1368 	rplyflag = 1;
1369 	printf("\n%s? ", s);
1370 	if (nflag || dfile.wfdes < 0) {
1371 		printf(" no\n\n");
1372 		return (0);
1373 	}
1374 	if (yflag) {
1375 		printf(" yes\n\n");
1376 		return (1);
1377 	}
1378 	if (getline(stdin, line, sizeof(line)) == EOF)
1379 		errexit("\n");
1380 	printf("\n");
1381 	if (line[0] == 'y' || line[0] == 'Y')
1382 		return (1);
1383 	else
1384 		return (0);
1385 }
1386 
1387 getline(fp, loc, maxlen)
1388 	FILE *fp;
1389 	char *loc;
1390 {
1391 	register n;
1392 	register char *p, *lastloc;
1393 
1394 	p = loc;
1395 	lastloc = &p[maxlen-1];
1396 	while ((n = getc(fp)) != '\n') {
1397 		if (n == EOF)
1398 			return (EOF);
1399 		if (!isspace(n) && p < lastloc)
1400 			*p++ = n;
1401 	}
1402 	*p = 0;
1403 	return (p - loc);
1404 }
1405 
1406 BUFAREA *
1407 getblk(bp, blk, size)
1408 	daddr_t blk;
1409 	register BUFAREA *bp;
1410 	int size;
1411 {
1412 	register struct filecntl *fcp;
1413 	daddr_t dblk;
1414 
1415 	fcp = &dfile;
1416 	dblk = fsbtodb(&sblock, blk);
1417 	if (bp->b_bno == dblk)
1418 		return (bp);
1419 	flush(fcp, bp);
1420 	if (bread(fcp, bp->b_un.b_buf, dblk, size) != 0) {
1421 		bp->b_bno = dblk;
1422 		bp->b_size = size;
1423 		return (bp);
1424 	}
1425 	bp->b_bno = (daddr_t)-1;
1426 	return (NULL);
1427 }
1428 
1429 flush(fcp, bp)
1430 	struct filecntl *fcp;
1431 	register BUFAREA *bp;
1432 {
1433 
1434 	if (bp->b_dirty)
1435 		bwrite(fcp, bp->b_un.b_buf, bp->b_bno, bp->b_size);
1436 	bp->b_dirty = 0;
1437 }
1438 
1439 rwerr(s, blk)
1440 	char *s;
1441 	daddr_t blk;
1442 {
1443 
1444 	if (preen == 0)
1445 		printf("\n");
1446 	pfatal("CANNOT %s: BLK %ld", s, blk);
1447 	if (reply("CONTINUE") == 0)
1448 		errexit("Program terminated\n");
1449 }
1450 
1451 ckfini()
1452 {
1453 
1454 	flush(&dfile, &fileblk);
1455 	flush(&dfile, &sblk);
1456 	if (sblk.b_bno != SBLOCK) {
1457 		sblk.b_bno = SBLOCK;
1458 		sbdirty();
1459 		flush(&dfile, &sblk);
1460 	}
1461 	flush(&dfile, &inoblk);
1462 	close(dfile.rfdes);
1463 	close(dfile.wfdes);
1464 }
1465 
1466 pinode()
1467 {
1468 	register DINODE *dp;
1469 	register char *p;
1470 	char uidbuf[BUFSIZ];
1471 	char *ctime();
1472 
1473 	printf(" I=%u ", inum);
1474 	if ((dp = ginode()) == NULL)
1475 		return;
1476 	printf(" OWNER=");
1477 	if (getpw((int)dp->di_uid, uidbuf) == 0) {
1478 		for (p = uidbuf; *p != ':'; p++);
1479 		*p = 0;
1480 		printf("%s ", uidbuf);
1481 	}
1482 	else {
1483 		printf("%d ", dp->di_uid);
1484 	}
1485 	printf("MODE=%o\n", dp->di_mode);
1486 	if (preen)
1487 		printf("%s: ", devname);
1488 	printf("SIZE=%ld ", dp->di_size);
1489 	p = ctime(&dp->di_mtime);
1490 	printf("MTIME=%12.12s %4.4s ", p+4, p+20);
1491 }
1492 
1493 copy(fp, tp, size)
1494 	register char *tp, *fp;
1495 	unsigned size;
1496 {
1497 
1498 	while (size--)
1499 		*tp++ = *fp++;
1500 }
1501 
1502 makecg()
1503 {
1504 	int c, blk;
1505 	daddr_t dbase, d, dmin, dmax;
1506 	long i, j, s;
1507 	register struct csum *cs;
1508 	register DINODE *dp;
1509 
1510 	sblock.fs_cstotal.cs_nbfree = 0;
1511 	sblock.fs_cstotal.cs_nffree = 0;
1512 	sblock.fs_cstotal.cs_nifree = 0;
1513 	sblock.fs_cstotal.cs_ndir = 0;
1514 	for (i = 0; i < howmany(sblock.fs_cssize, sblock.fs_bsize); i++) {
1515 		sblock.fs_csp[i] = (struct csum *)calloc(1, sblock.fs_bsize);
1516 		getblk((char *)sblock.fs_csp[i],
1517 		    sblock.fs_csaddr + (i * sblock.fs_frag), sblock.fs_bsize);
1518 	}
1519 	for (c = 0; c < sblock.fs_ncg; c++) {
1520 		dbase = cgbase(&sblock, c);
1521 		dmax = dbase + sblock.fs_fpg;
1522 		if (dmax > sblock.fs_size) {
1523 			for ( ; dmax >= sblock.fs_size; dmax--)
1524 				clrbit(cgrp.cg_free, dmax - dbase);
1525 			dmax++;
1526 		}
1527 		dmin = sblock.fs_dblkno;
1528 		cs = &sblock.fs_cs(&sblock, c);
1529 		cgrp.cg_time = time(0);
1530 		cgrp.cg_magic = CG_MAGIC;
1531 		cgrp.cg_cgx = c;
1532 		cgrp.cg_ncyl = sblock.fs_cpg;
1533 		cgrp.cg_niblk = sblock.fs_ipg;
1534 		cgrp.cg_ndblk = dmax - dbase;
1535 		cgrp.cg_cs.cs_ndir = 0;
1536 		cgrp.cg_cs.cs_nffree = 0;
1537 		cgrp.cg_cs.cs_nbfree = 0;
1538 		cgrp.cg_cs.cs_nifree = 0;
1539 		cgrp.cg_rotor = dmin;
1540 		cgrp.cg_frotor = dmin;
1541 		cgrp.cg_irotor = 0;
1542 		for (i = 0; i < sblock.fs_frag; i++)
1543 			cgrp.cg_frsum[i] = 0;
1544 		inum = sblock.fs_ipg * c;
1545 		for (i = 0; i < sblock.fs_ipg; inum++, i++) {
1546 			cgrp.cg_cs.cs_nifree++;
1547 			clrbit(cgrp.cg_iused, i);
1548 			dp = ginode();
1549 			if (dp == NULL)
1550 				continue;
1551 			if (ALLOC) {
1552 				if (DIRCT)
1553 					cgrp.cg_cs.cs_ndir++;
1554 				cgrp.cg_cs.cs_nifree--;
1555 				setbit(cgrp.cg_iused, i);
1556 				continue;
1557 			}
1558 		}
1559 		while (i < MAXIPG) {
1560 			clrbit(cgrp.cg_iused, i);
1561 			i++;
1562 		}
1563 		if (c == 0)
1564 			for (i = 0; i < ROOTINO; i++) {
1565 				setbit(cgrp.cg_iused, i);
1566 				cgrp.cg_cs.cs_nifree--;
1567 			}
1568 		for (s = 0; s < MAXCPG; s++) {
1569 			cgrp.cg_btot[s] = 0;
1570 			for (i = 0; i < NRPOS; i++)
1571 				cgrp.cg_b[s][i] = 0;
1572 		}
1573 		if (c == 0) {
1574 			dmin += howmany(sblock.fs_cssize, sblock.fs_bsize) *
1575 			    sblock.fs_frag;
1576 		}
1577 		for (d = 0; d < dmin; d++)
1578 			clrbit(cgrp.cg_free, d);
1579 		for (; (d + sblock.fs_frag) <= dmax - dbase; d += sblock.fs_frag) {
1580 			j = 0;
1581 			for (i = 0; i < sblock.fs_frag; i++) {
1582 				if (!getbmap(dbase+d+i)) {
1583 					setbit(cgrp.cg_free, d+i);
1584 					j++;
1585 				} else
1586 					clrbit(cgrp.cg_free, d+i);
1587 			}
1588 			if (j == sblock.fs_frag) {
1589 				cgrp.cg_cs.cs_nbfree++;
1590 				cgrp.cg_btot[cbtocylno(&sblock, d)]++;
1591 				cgrp.cg_b[cbtocylno(&sblock, d)]
1592 				    [cbtorpos(&sblock, d)]++;
1593 			} else if (j > 0) {
1594 				cgrp.cg_cs.cs_nffree += j;
1595 				blk = ((cgrp.cg_free[d / NBBY] >> (d % NBBY)) &
1596 				       (0xff >> (NBBY - sblock.fs_frag)));
1597 				fragacct(&sblock, blk, cgrp.cg_frsum, 1);
1598 			}
1599 		}
1600 		for (j = d; d < dmax - dbase; d++) {
1601 			if (!getbmap(dbase+d)) {
1602 				setbit(cgrp.cg_free, d);
1603 				cgrp.cg_cs.cs_nffree++;
1604 			} else
1605 				clrbit(cgrp.cg_free, d);
1606 		}
1607 		if (j != d) {
1608 			blk = ((cgrp.cg_free[j / NBBY] >> (j % NBBY)) &
1609 			       (0xff >> (NBBY - sblock.fs_frag)));
1610 			fragacct(&sblock, blk, cgrp.cg_frsum, 1);
1611 		}
1612 		for (; d < MAXBPG(&sblock); d++)
1613 			clrbit(cgrp.cg_free, d);
1614 		sblock.fs_cstotal.cs_nffree += cgrp.cg_cs.cs_nffree;
1615 		sblock.fs_cstotal.cs_nbfree += cgrp.cg_cs.cs_nbfree;
1616 		sblock.fs_cstotal.cs_nifree += cgrp.cg_cs.cs_nifree;
1617 		sblock.fs_cstotal.cs_ndir += cgrp.cg_cs.cs_ndir;
1618 		*cs = cgrp.cg_cs;
1619 		bwrite(&dfile, &cgrp, fsbtodb(&sblock, cgtod(&sblock, c)),
1620 		    sblock.fs_cgsize);
1621 	}
1622 	for (i = 0; i < howmany(sblock.fs_cssize, sblock.fs_bsize); i++) {
1623 		bwrite(&dfile, (char *)sblock.fs_csp[i],
1624 		    fsbtodb(&sblock, sblock.fs_csaddr + (i * sblock.fs_frag)),
1625 		    sblock.fs_bsize);
1626 	}
1627 	sblock.fs_ronly = 0;
1628 	sblock.fs_fmod = 0;
1629 	sbdirty();
1630 }
1631 
1632 /*
1633  * update the frsum fields to reflect addition or deletion
1634  * of some frags
1635  */
1636 fragacct(fs, fragmap, fraglist, cnt)
1637 	struct fs *fs;
1638 	int fragmap;
1639 	long fraglist[];
1640 	int cnt;
1641 {
1642 	int inblk;
1643 	register int field, subfield;
1644 	register int siz, pos;
1645 
1646 	inblk = (int)(fragtbl[fs->fs_frag][fragmap] << 1);
1647 	fragmap <<= 1;
1648 	for (siz = 1; siz < fs->fs_frag; siz++) {
1649 		if (((1 << siz) & inblk) == 0)
1650 			continue;
1651 		field = around[siz];
1652 		subfield = inside[siz];
1653 		for (pos = siz; pos <= fs->fs_frag; pos++) {
1654 			if ((fragmap & field) == subfield) {
1655 				fraglist[siz] += cnt;
1656 				pos += siz;
1657 				field <<= siz;
1658 				subfield <<= siz;
1659 			}
1660 			field <<= 1;
1661 			subfield <<= 1;
1662 		}
1663 	}
1664 }
1665 
1666 findino(dirp)
1667 	register DIRECT *dirp;
1668 {
1669 	if (dirp->d_ino == 0)
1670 		return (KEEPON);
1671 	if (!strcmp(dirp->d_name, srchname)) {
1672 		if (dirp->d_ino >= ROOTINO && dirp->d_ino <= imax)
1673 			parentdir = dirp->d_ino;
1674 		return (STOP);
1675 	}
1676 	return (KEEPON);
1677 }
1678 
1679 mkentry(dirp)
1680 	register DIRECT *dirp;
1681 {
1682 	register ino_t in;
1683 	register char *p;
1684 	DIRECT newent;
1685 	int newlen, oldlen;
1686 
1687 	newent.d_namlen = 11;
1688 	newlen = DIRSIZ(&newent);
1689 	if (dirp->d_ino != 0)
1690 		oldlen = DIRSIZ(dirp);
1691 	else
1692 		oldlen = 0;
1693 	if (dirp->d_reclen - oldlen < newlen)
1694 		return (KEEPON);
1695 	newent.d_reclen = dirp->d_reclen - oldlen;
1696 	dirp->d_reclen = oldlen;
1697 	dirp = (struct direct *)(((char *)dirp) + oldlen);
1698 	dirp->d_ino = orphan;
1699 	dirp->d_reclen = newent.d_reclen;
1700 	p = &dirp->d_name[2];
1701 	for (in = imax; in > 0; in /= 10)
1702 		p++;
1703 	*--p = 0;
1704 	dirp->d_namlen = p - dirp->d_name;
1705 	in = orphan;
1706 	while (p > dirp->d_name) {
1707 		*--p = (in % 10) + '0';
1708 		in /= 10;
1709 	}
1710 	*p = '#';
1711 	return (ALTERD|STOP);
1712 }
1713 
1714 chgdd(dirp)
1715 	register DIRECT *dirp;
1716 {
1717 	if (dirp->d_name[0] == '.' && dirp->d_name[1] == '.' &&
1718 	dirp->d_name[2] == 0) {
1719 		dirp->d_ino = lfdir;
1720 		return (ALTERD|STOP);
1721 	}
1722 	return (KEEPON);
1723 }
1724 
1725 linkup()
1726 {
1727 	register DINODE *dp;
1728 	register lostdir;
1729 	register ino_t pdir;
1730 
1731 	if ((dp = ginode()) == NULL)
1732 		return (0);
1733 	lostdir = DIRCT;
1734 	pdir = parentdir;
1735 	pwarn("UNREF %s ", lostdir ? "DIR" : "FILE");
1736 	pinode();
1737 	if (preen && dp->di_size == 0)
1738 		return (0);
1739 	if (preen)
1740 		printf(" (RECONNECTED)\n");
1741 	else
1742 		if (reply("RECONNECT") == 0)
1743 			return (0);
1744 	orphan = inum;
1745 	if (lfdir == 0) {
1746 		inum = ROOTINO;
1747 		if ((dp = ginode()) == NULL) {
1748 			inum = orphan;
1749 			return (0);
1750 		}
1751 		pfunc = findino;
1752 		srchname = lfname;
1753 		filsize = dp->di_size;
1754 		parentdir = 0;
1755 		ckinode(dp, DATA);
1756 		inum = orphan;
1757 		if ((lfdir = parentdir) == 0) {
1758 			pfatal("SORRY. NO lost+found DIRECTORY");
1759 			printf("\n\n");
1760 			return (0);
1761 		}
1762 	}
1763 	inum = lfdir;
1764 	if ((dp = ginode()) == NULL || !DIRCT || getstate() != FSTATE) {
1765 		inum = orphan;
1766 		pfatal("SORRY. NO lost+found DIRECTORY");
1767 		printf("\n\n");
1768 		return (0);
1769 	}
1770 	if (fragoff(&sblock, dp->di_size)) {
1771 		dp->di_size = fragroundup(&sblock, dp->di_size);
1772 		inodirty();
1773 	}
1774 	filsize = dp->di_size;
1775 	inum = orphan;
1776 	pfunc = mkentry;
1777 	if ((ckinode(dp, DATA) & ALTERD) == 0) {
1778 		pfatal("SORRY. NO SPACE IN lost+found DIRECTORY");
1779 		printf("\n\n");
1780 		return (0);
1781 	}
1782 	declncnt();
1783 	if (lostdir) {
1784 		pfunc = chgdd;
1785 		dp = ginode();
1786 		filsize = dp->di_size;
1787 		ckinode(dp, DATA);
1788 		inum = lfdir;
1789 		if ((dp = ginode()) != NULL) {
1790 			dp->di_nlink++;
1791 			inodirty();
1792 			setlncnt(getlncnt()+1);
1793 		}
1794 		inum = orphan;
1795 		pwarn("DIR I=%u CONNECTED. ", orphan);
1796 		printf("PARENT WAS I=%u\n", pdir);
1797 		if (preen == 0)
1798 			printf("\n");
1799 	}
1800 	return (1);
1801 }
1802 
1803 bread(fcp, buf, blk, size)
1804 	daddr_t blk;
1805 	register struct filecntl *fcp;
1806 	register size;
1807 	char *buf;
1808 {
1809 	if (lseek(fcp->rfdes, blk * DEV_BSIZE, 0) < 0)
1810 		rwerr("SEEK", blk);
1811 	else if (read(fcp->rfdes, buf, size) == size)
1812 		return (1);
1813 	rwerr("READ", blk);
1814 	return (0);
1815 }
1816 
1817 bwrite(fcp, buf, blk, size)
1818 	daddr_t blk;
1819 	register struct filecntl *fcp;
1820 	register size;
1821 	char *buf;
1822 {
1823 
1824 	if (fcp->wfdes < 0)
1825 		return (0);
1826 	if (lseek(fcp->wfdes, blk * DEV_BSIZE, 0) < 0)
1827 		rwerr("SEEK", blk);
1828 	else if (write(fcp->wfdes, buf, size) == size) {
1829 		fcp->mod = 1;
1830 		return (1);
1831 	}
1832 	rwerr("WRITE", blk);
1833 	return (0);
1834 }
1835 
1836 catch()
1837 {
1838 
1839 	ckfini();
1840 	exit(12);
1841 }
1842 
1843 /*
1844  * block operations
1845  */
1846 
1847 isblock(fs, cp, h)
1848 	struct fs *fs;
1849 	unsigned char *cp;
1850 	int h;
1851 {
1852 	unsigned char mask;
1853 
1854 	switch (fs->fs_frag) {
1855 	case 8:
1856 		return (cp[h] == 0xff);
1857 	case 4:
1858 		mask = 0x0f << ((h & 0x1) << 2);
1859 		return ((cp[h >> 1] & mask) == mask);
1860 	case 2:
1861 		mask = 0x03 << ((h & 0x3) << 1);
1862 		return ((cp[h >> 2] & mask) == mask);
1863 	case 1:
1864 		mask = 0x01 << (h & 0x7);
1865 		return ((cp[h >> 3] & mask) == mask);
1866 	default:
1867 		error("isblock bad fs_frag %d\n", fs->fs_frag);
1868 		return (0);
1869 	}
1870 }
1871