xref: /csrg-svn/sbin/fsck/utilities.c (revision 34482)
1 /*
2  * Copyright (c) 1980 Regents of the University of California.
3  * All rights reserved.  The Berkeley software License Agreement
4  * specifies the terms and conditions for redistribution.
5  */
6 
7 #ifndef lint
8 static char sccsid[] = "@(#)utilities.c	5.12 (Berkeley) 05/25/88";
9 #endif not lint
10 
11 #include <stdio.h>
12 #include <ctype.h>
13 #include <sys/param.h>
14 #include <sys/inode.h>
15 #include <sys/fs.h>
16 #include <sys/dir.h>
17 #include "fsck.h"
18 
19 long	diskreads, totalreads;	/* Disk cache statistics */
20 long	lseek();
21 
22 ftypeok(dp)
23 	DINODE *dp;
24 {
25 	switch (dp->di_mode & IFMT) {
26 
27 	case IFDIR:
28 	case IFREG:
29 	case IFBLK:
30 	case IFCHR:
31 	case IFLNK:
32 	case IFSOCK:
33 		return (1);
34 
35 	default:
36 		if (debug)
37 			printf("bad file type 0%o\n", dp->di_mode);
38 		return (0);
39 	}
40 }
41 
42 reply(s)
43 	char *s;
44 {
45 	char line[80];
46 	int cont = (strcmp(s, "CONTINUE") == 0);
47 
48 	if (preen)
49 		pfatal("INTERNAL ERROR: GOT TO reply()");
50 	printf("\n%s? ", s);
51 	if (!cont && (nflag || dfile.wfdes < 0)) {
52 		printf(" no\n\n");
53 		return (0);
54 	}
55 	if (yflag || (cont && nflag)) {
56 		printf(" yes\n\n");
57 		return (1);
58 	}
59 	if (getline(stdin, line, sizeof(line)) == EOF)
60 		errexit("\n");
61 	printf("\n");
62 	if (line[0] == 'y' || line[0] == 'Y')
63 		return (1);
64 	else
65 		return (0);
66 }
67 
68 getline(fp, loc, maxlen)
69 	FILE *fp;
70 	char *loc;
71 {
72 	register n;
73 	register char *p, *lastloc;
74 
75 	p = loc;
76 	lastloc = &p[maxlen-1];
77 	while ((n = getc(fp)) != '\n') {
78 		if (n == EOF)
79 			return (EOF);
80 		if (!isspace(n) && p < lastloc)
81 			*p++ = n;
82 	}
83 	*p = 0;
84 	return (p - loc);
85 }
86 
87 /*
88  * Malloc buffers and set up cache.
89  */
90 bufinit()
91 {
92 	register BUFAREA *bp;
93 	long bufcnt, i;
94 	char *bufp;
95 
96 	bufp = (char *)malloc(sblock.fs_bsize);
97 	if (bufp == 0)
98 		errexit("cannot allocate buffer pool\n");
99 	cgblk.b_un.b_buf = bufp;
100 	initbarea(&cgblk);
101 	bufhead.b_next = bufhead.b_prev = &bufhead;
102 	bufcnt = MAXBUFSPACE / sblock.fs_bsize;
103 	if (bufcnt < MINBUFS)
104 		bufcnt = MINBUFS;
105 	for (i = 0; i < bufcnt; i++) {
106 		bp = (BUFAREA *)malloc(sizeof(BUFAREA));
107 		bufp = (char *)malloc(sblock.fs_bsize);
108 		if (bp == 0 || bufp == 0) {
109 			if (i >= MINBUFS)
110 				break;
111 			errexit("cannot allocate buffer pool\n");
112 		}
113 		bp->b_un.b_buf = bufp;
114 		bp->b_prev = &bufhead;
115 		bp->b_next = bufhead.b_next;
116 		bufhead.b_next->b_prev = bp;
117 		bufhead.b_next = bp;
118 		initbarea(bp);
119 	}
120 	bufhead.b_size = i;	/* save number of buffers */
121 }
122 
123 /*
124  * Manage a cache of directory blocks.
125  */
126 BUFAREA *
127 getdatablk(blkno, size)
128 	daddr_t blkno;
129 	long size;
130 {
131 	register BUFAREA *bp;
132 
133 	for (bp = bufhead.b_next; bp != &bufhead; bp = bp->b_next)
134 		if (bp->b_bno == blkno)
135 			goto foundit;
136 	for (bp = bufhead.b_prev; bp != &bufhead; bp = bp->b_prev)
137 		if ((bp->b_flags & B_INUSE) == 0)
138 			break;
139 	if (bp == &bufhead)
140 		errexit("deadlocked buffer pool\n");
141 	getblk(bp, blkno, size);
142 	/* fall through */
143 foundit:
144 	totalreads++;
145 	bp->b_prev->b_next = bp->b_next;
146 	bp->b_next->b_prev = bp->b_prev;
147 	bp->b_prev = &bufhead;
148 	bp->b_next = bufhead.b_next;
149 	bufhead.b_next->b_prev = bp;
150 	bufhead.b_next = bp;
151 	bp->b_flags |= B_INUSE;
152 	return (bp);
153 }
154 
155 BUFAREA *
156 getblk(bp, blk, size)
157 	register BUFAREA *bp;
158 	daddr_t blk;
159 	long size;
160 {
161 	register struct filecntl *fcp;
162 	daddr_t dblk;
163 
164 	fcp = &dfile;
165 	if (bp->b_bno == blk)
166 		return (bp);
167 	flush(fcp, bp);
168 	diskreads++;
169 	bp->b_errs = bread(fcp, bp->b_un.b_buf, fsbtodb(&sblock, blk), size);
170 	bp->b_bno = blk;
171 	bp->b_size = size;
172 	return (bp);
173 }
174 
175 flush(fcp, bp)
176 	struct filecntl *fcp;
177 	register BUFAREA *bp;
178 {
179 	register int i, j;
180 
181 	if (!bp->b_dirty)
182 		return;
183 	if (bp->b_errs != 0)
184 		pfatal("WRITING %sZERO'ED BLOCK %d TO DISK\n",
185 		    (bp->b_errs == bp->b_size / dev_bsize) ? "" : "PARTIALLY ",
186 		    bp->b_bno);
187 	bp->b_dirty = 0;
188 	bp->b_errs = 0;
189 	bwrite(fcp, bp->b_un.b_buf, bp->b_bno, (long)bp->b_size);
190 	if (bp != &sblk)
191 		return;
192 	for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) {
193 		bwrite(&dfile, (char *)sblock.fs_csp[j],
194 		    fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag),
195 		    sblock.fs_cssize - i < sblock.fs_bsize ?
196 		    sblock.fs_cssize - i : sblock.fs_bsize);
197 	}
198 }
199 
200 rwerr(s, blk)
201 	char *s;
202 	daddr_t blk;
203 {
204 
205 	if (preen == 0)
206 		printf("\n");
207 	pfatal("CANNOT %s: BLK %ld", s, blk);
208 	if (reply("CONTINUE") == 0)
209 		errexit("Program terminated\n");
210 }
211 
212 ckfini()
213 {
214 	register BUFAREA *bp;
215 	int cnt = 0;
216 
217 	flush(&dfile, &sblk);
218 	if (havesb && sblk.b_bno != SBOFF / dev_bsize &&
219 	    !preen && reply("UPDATE STANDARD SUPERBLOCK")) {
220 		sblk.b_bno = SBOFF / dev_bsize;
221 		sbdirty();
222 		flush(&dfile, &sblk);
223 	}
224 	flush(&dfile, &cgblk);
225 	for (bp = bufhead.b_prev; bp != &bufhead; bp = bp->b_prev) {
226 		cnt++;
227 		flush(&dfile, bp);
228 	}
229 	if (bufhead.b_size != cnt)
230 		errexit("Panic: lost %d buffers\n", bufhead.b_size - cnt);
231 	if (debug)
232 		printf("cache missed %d of %d (%d%%)\n", diskreads,
233 		    totalreads, diskreads * 100 / totalreads);
234 	(void)close(dfile.rfdes);
235 	(void)close(dfile.wfdes);
236 }
237 
238 bread(fcp, buf, blk, size)
239 	register struct filecntl *fcp;
240 	char *buf;
241 	daddr_t blk;
242 	long size;
243 {
244 	char *cp;
245 	int i, errs;
246 
247 	if (lseek(fcp->rfdes, blk * dev_bsize, 0) < 0)
248 		rwerr("SEEK", blk);
249 	else if (read(fcp->rfdes, buf, (int)size) == size)
250 		return (0);
251 	rwerr("READ", blk);
252 	if (lseek(fcp->rfdes, blk * dev_bsize, 0) < 0)
253 		rwerr("SEEK", blk);
254 	errs = 0;
255 	bzero(buf, size);
256 	printf("THE FOLLOWING DISK SECTORS COULD NOT BE READ:");
257 	for (cp = buf, i = 0; i < size; i += secsize, cp += secsize) {
258 		if (read(fcp->rfdes, cp, secsize) < 0) {
259 			lseek(fcp->rfdes, blk * dev_bsize + i + secsize, 0);
260 			if (secsize != dev_bsize && dev_bsize != 1)
261 				printf(" %d (%d),",
262 				    (blk * dev_bsize + i) / secsize,
263 				    blk + i / dev_bsize);
264 			else
265 				printf(" %d,", blk + i / dev_bsize);
266 			errs++;
267 		}
268 	}
269 	printf("\n");
270 	return (errs);
271 }
272 
273 bwrite(fcp, buf, blk, size)
274 	register struct filecntl *fcp;
275 	char *buf;
276 	daddr_t blk;
277 	long size;
278 {
279 	int i;
280 	char *cp;
281 
282 	if (fcp->wfdes < 0)
283 		return;
284 	if (lseek(fcp->wfdes, blk * dev_bsize, 0) < 0)
285 		rwerr("SEEK", blk);
286 	else if (write(fcp->wfdes, buf, (int)size) == size) {
287 		fcp->mod = 1;
288 		return;
289 	}
290 	rwerr("WRITE", blk);
291 	if (lseek(fcp->wfdes, blk * dev_bsize, 0) < 0)
292 		rwerr("SEEK", blk);
293 	printf("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:");
294 	for (cp = buf, i = 0; i < size; i += dev_bsize, cp += dev_bsize)
295 		if (write(fcp->wfdes, cp, dev_bsize) < 0) {
296 			lseek(fcp->rfdes, blk * dev_bsize + i + dev_bsize, 0);
297 			printf(" %d,", blk + i / dev_bsize);
298 		}
299 	printf("\n");
300 	return;
301 }
302 
303 /*
304  * allocate a data block with the specified number of fragments
305  */
306 allocblk(frags)
307 	int frags;
308 {
309 	register int i, j, k;
310 
311 	if (frags <= 0 || frags > sblock.fs_frag)
312 		return (0);
313 	for (i = 0; i < fmax - sblock.fs_frag; i += sblock.fs_frag) {
314 		for (j = 0; j <= sblock.fs_frag - frags; j++) {
315 			if (getbmap(i + j))
316 				continue;
317 			for (k = 1; k < frags; k++)
318 				if (getbmap(i + j + k))
319 					break;
320 			if (k < frags) {
321 				j += k;
322 				continue;
323 			}
324 			for (k = 0; k < frags; k++)
325 				setbmap(i + j + k);
326 			n_blks += frags;
327 			return (i + j);
328 		}
329 	}
330 	return (0);
331 }
332 
333 /*
334  * Free a previously allocated block
335  */
336 freeblk(blkno, frags)
337 	daddr_t blkno;
338 	int frags;
339 {
340 	struct inodesc idesc;
341 
342 	idesc.id_blkno = blkno;
343 	idesc.id_numfrags = frags;
344 	pass4check(&idesc);
345 }
346 
347 /*
348  * Find a pathname
349  */
350 getpathname(namebuf, curdir, ino)
351 	char *namebuf;
352 	ino_t curdir, ino;
353 {
354 	int len;
355 	register char *cp;
356 	struct inodesc idesc;
357 	extern int findname();
358 
359 	if (statemap[ino] != DSTATE && statemap[ino] != DFOUND) {
360 		strcpy(namebuf, "?");
361 		return;
362 	}
363 	bzero(&idesc, sizeof(struct inodesc));
364 	idesc.id_type = DATA;
365 	cp = &namebuf[BUFSIZ - 1];
366 	*cp = '\0';
367 	if (curdir != ino) {
368 		idesc.id_parent = curdir;
369 		goto namelookup;
370 	}
371 	while (ino != ROOTINO) {
372 		idesc.id_number = ino;
373 		idesc.id_func = findino;
374 		idesc.id_name = "..";
375 		if ((ckinode(ginode(ino), &idesc) & FOUND) == 0)
376 			break;
377 	namelookup:
378 		idesc.id_number = idesc.id_parent;
379 		idesc.id_parent = ino;
380 		idesc.id_func = findname;
381 		idesc.id_name = namebuf;
382 		if ((ckinode(ginode(idesc.id_number), &idesc) & FOUND) == 0)
383 			break;
384 		len = strlen(namebuf);
385 		cp -= len;
386 		if (cp < &namebuf[MAXNAMLEN])
387 			break;
388 		bcopy(namebuf, cp, len);
389 		*--cp = '/';
390 		ino = idesc.id_number;
391 	}
392 	if (ino != ROOTINO) {
393 		strcpy(namebuf, "?");
394 		return;
395 	}
396 	bcopy(cp, namebuf, &namebuf[BUFSIZ] - cp);
397 }
398 
399 catch()
400 {
401 
402 	ckfini();
403 	exit(12);
404 }
405 
406 /*
407  * When preening, allow a single quit to signal
408  * a special exit after filesystem checks complete
409  * so that reboot sequence may be interrupted.
410  */
411 catchquit()
412 {
413 	extern returntosingle;
414 
415 	printf("returning to single-user after filesystem check\n");
416 	returntosingle = 1;
417 	(void)signal(SIGQUIT, SIG_DFL);
418 }
419 
420 /*
421  * Ignore a single quit signal; wait and flush just in case.
422  * Used by child processes in preen.
423  */
424 voidquit()
425 {
426 
427 	sleep(1);
428 	(void)signal(SIGQUIT, SIG_IGN);
429 	(void)signal(SIGQUIT, SIG_DFL);
430 }
431 
432 /*
433  * determine whether an inode should be fixed.
434  */
435 dofix(idesc, msg)
436 	register struct inodesc *idesc;
437 	char *msg;
438 {
439 
440 	switch (idesc->id_fix) {
441 
442 	case DONTKNOW:
443 		if (idesc->id_type == DATA)
444 			direrr(idesc->id_number, msg);
445 		else
446 			pwarn(msg);
447 		if (preen) {
448 			printf(" (SALVAGED)\n");
449 			idesc->id_fix = FIX;
450 			return (ALTERED);
451 		}
452 		if (reply("SALVAGE") == 0) {
453 			idesc->id_fix = NOFIX;
454 			return (0);
455 		}
456 		idesc->id_fix = FIX;
457 		return (ALTERED);
458 
459 	case FIX:
460 		return (ALTERED);
461 
462 	case NOFIX:
463 		return (0);
464 
465 	default:
466 		errexit("UNKNOWN INODESC FIX MODE %d\n", idesc->id_fix);
467 	}
468 	/* NOTREACHED */
469 }
470 
471 /* VARARGS1 */
472 errexit(s1, s2, s3, s4)
473 	char *s1;
474 {
475 	printf(s1, s2, s3, s4);
476 	exit(8);
477 }
478 
479 /*
480  * An inconsistency occured which shouldn't during normal operations.
481  * Die if preening, otherwise just printf.
482  */
483 /* VARARGS1 */
484 pfatal(s, a1, a2, a3)
485 	char *s;
486 {
487 
488 	if (preen) {
489 		printf("%s: ", devname);
490 		printf(s, a1, a2, a3);
491 		printf("\n");
492 		printf("%s: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY.\n",
493 			devname);
494 		exit(8);
495 	}
496 	printf(s, a1, a2, a3);
497 }
498 
499 /*
500  * Pwarn is like printf when not preening,
501  * or a warning (preceded by filename) when preening.
502  */
503 /* VARARGS1 */
504 pwarn(s, a1, a2, a3, a4, a5, a6)
505 	char *s;
506 {
507 
508 	if (preen)
509 		printf("%s: ", devname);
510 	printf(s, a1, a2, a3, a4, a5, a6);
511 }
512 
513 #ifndef lint
514 /*
515  * Stub for routines from kernel.
516  */
517 panic(s)
518 	char *s;
519 {
520 
521 	pfatal("INTERNAL INCONSISTENCY:");
522 	errexit(s);
523 }
524 #endif
525