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