xref: /csrg-svn/sbin/fsck/utilities.c (revision 38342)
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.15 (Berkeley) 06/27/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, *nbp;
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 	free(cgblk.b_un.b_buf);
229 	for (bp = bufhead.b_prev; bp != &bufhead; bp = nbp) {
230 		cnt++;
231 		flush(&dfile, bp);
232 		nbp = bp->b_prev;
233 		free(bp->b_un.b_buf);
234 		free((char *)bp);
235 	}
236 	if (bufhead.b_size != cnt)
237 		errexit("Panic: lost %d buffers\n", bufhead.b_size - cnt);
238 	if (debug)
239 		printf("cache missed %d of %d (%d%%)\n", diskreads,
240 		    totalreads, diskreads * 100 / totalreads);
241 	(void)close(dfile.rfdes);
242 	(void)close(dfile.wfdes);
243 }
244 
245 bread(fcp, buf, blk, size)
246 	register struct filecntl *fcp;
247 	char *buf;
248 	daddr_t blk;
249 	long size;
250 {
251 	char *cp;
252 	int i, errs;
253 
254 	if (lseek(fcp->rfdes, blk * dev_bsize, 0) < 0)
255 		rwerr("SEEK", blk);
256 	else if (read(fcp->rfdes, buf, (int)size) == size)
257 		return (0);
258 	rwerr("READ", blk);
259 	if (lseek(fcp->rfdes, blk * dev_bsize, 0) < 0)
260 		rwerr("SEEK", blk);
261 	errs = 0;
262 	bzero(buf, size);
263 	printf("THE FOLLOWING DISK SECTORS COULD NOT BE READ:");
264 	for (cp = buf, i = 0; i < size; i += secsize, cp += secsize) {
265 		if (read(fcp->rfdes, cp, secsize) < 0) {
266 			lseek(fcp->rfdes, blk * dev_bsize + i + secsize, 0);
267 			if (secsize != dev_bsize && dev_bsize != 1)
268 				printf(" %d (%d),",
269 				    (blk * dev_bsize + i) / secsize,
270 				    blk + i / dev_bsize);
271 			else
272 				printf(" %d,", blk + i / dev_bsize);
273 			errs++;
274 		}
275 	}
276 	printf("\n");
277 	return (errs);
278 }
279 
280 bwrite(fcp, buf, blk, size)
281 	register struct filecntl *fcp;
282 	char *buf;
283 	daddr_t blk;
284 	long size;
285 {
286 	int i;
287 	char *cp;
288 
289 	if (fcp->wfdes < 0)
290 		return;
291 	if (lseek(fcp->wfdes, blk * dev_bsize, 0) < 0)
292 		rwerr("SEEK", blk);
293 	else if (write(fcp->wfdes, buf, (int)size) == size) {
294 		fcp->mod = 1;
295 		return;
296 	}
297 	rwerr("WRITE", blk);
298 	if (lseek(fcp->wfdes, blk * dev_bsize, 0) < 0)
299 		rwerr("SEEK", blk);
300 	printf("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:");
301 	for (cp = buf, i = 0; i < size; i += dev_bsize, cp += dev_bsize)
302 		if (write(fcp->wfdes, cp, dev_bsize) < 0) {
303 			lseek(fcp->rfdes, blk * dev_bsize + i + dev_bsize, 0);
304 			printf(" %d,", blk + i / dev_bsize);
305 		}
306 	printf("\n");
307 	return;
308 }
309 
310 /*
311  * allocate a data block with the specified number of fragments
312  */
313 allocblk(frags)
314 	int frags;
315 {
316 	register int i, j, k;
317 
318 	if (frags <= 0 || frags > sblock.fs_frag)
319 		return (0);
320 	for (i = 0; i < fmax - sblock.fs_frag; i += sblock.fs_frag) {
321 		for (j = 0; j <= sblock.fs_frag - frags; j++) {
322 			if (getbmap(i + j))
323 				continue;
324 			for (k = 1; k < frags; k++)
325 				if (getbmap(i + j + k))
326 					break;
327 			if (k < frags) {
328 				j += k;
329 				continue;
330 			}
331 			for (k = 0; k < frags; k++)
332 				setbmap(i + j + k);
333 			n_blks += frags;
334 			return (i + j);
335 		}
336 	}
337 	return (0);
338 }
339 
340 /*
341  * Free a previously allocated block
342  */
343 freeblk(blkno, frags)
344 	daddr_t blkno;
345 	int frags;
346 {
347 	struct inodesc idesc;
348 
349 	idesc.id_blkno = blkno;
350 	idesc.id_numfrags = frags;
351 	pass4check(&idesc);
352 }
353 
354 /*
355  * Find a pathname
356  */
357 getpathname(namebuf, curdir, ino)
358 	char *namebuf;
359 	ino_t curdir, ino;
360 {
361 	int len;
362 	register char *cp;
363 	struct inodesc idesc;
364 	extern int findname();
365 
366 	if (statemap[ino] != DSTATE && statemap[ino] != DFOUND) {
367 		strcpy(namebuf, "?");
368 		return;
369 	}
370 	bzero(&idesc, sizeof(struct inodesc));
371 	idesc.id_type = DATA;
372 	cp = &namebuf[BUFSIZ - 1];
373 	*cp = '\0';
374 	if (curdir != ino) {
375 		idesc.id_parent = curdir;
376 		goto namelookup;
377 	}
378 	while (ino != ROOTINO) {
379 		idesc.id_number = ino;
380 		idesc.id_func = findino;
381 		idesc.id_name = "..";
382 		if ((ckinode(ginode(ino), &idesc) & FOUND) == 0)
383 			break;
384 	namelookup:
385 		idesc.id_number = idesc.id_parent;
386 		idesc.id_parent = ino;
387 		idesc.id_func = findname;
388 		idesc.id_name = namebuf;
389 		if ((ckinode(ginode(idesc.id_number), &idesc) & FOUND) == 0)
390 			break;
391 		len = strlen(namebuf);
392 		cp -= len;
393 		if (cp < &namebuf[MAXNAMLEN])
394 			break;
395 		bcopy(namebuf, cp, len);
396 		*--cp = '/';
397 		ino = idesc.id_number;
398 	}
399 	if (ino != ROOTINO) {
400 		strcpy(namebuf, "?");
401 		return;
402 	}
403 	bcopy(cp, namebuf, &namebuf[BUFSIZ] - cp);
404 }
405 
406 catch()
407 {
408 
409 	ckfini();
410 	exit(12);
411 }
412 
413 /*
414  * When preening, allow a single quit to signal
415  * a special exit after filesystem checks complete
416  * so that reboot sequence may be interrupted.
417  */
418 catchquit()
419 {
420 	extern returntosingle;
421 
422 	printf("returning to single-user after filesystem check\n");
423 	returntosingle = 1;
424 	(void)signal(SIGQUIT, SIG_DFL);
425 }
426 
427 /*
428  * Ignore a single quit signal; wait and flush just in case.
429  * Used by child processes in preen.
430  */
431 voidquit()
432 {
433 
434 	sleep(1);
435 	(void)signal(SIGQUIT, SIG_IGN);
436 	(void)signal(SIGQUIT, SIG_DFL);
437 }
438 
439 /*
440  * determine whether an inode should be fixed.
441  */
442 dofix(idesc, msg)
443 	register struct inodesc *idesc;
444 	char *msg;
445 {
446 
447 	switch (idesc->id_fix) {
448 
449 	case DONTKNOW:
450 		if (idesc->id_type == DATA)
451 			direrr(idesc->id_number, msg);
452 		else
453 			pwarn(msg);
454 		if (preen) {
455 			printf(" (SALVAGED)\n");
456 			idesc->id_fix = FIX;
457 			return (ALTERED);
458 		}
459 		if (reply("SALVAGE") == 0) {
460 			idesc->id_fix = NOFIX;
461 			return (0);
462 		}
463 		idesc->id_fix = FIX;
464 		return (ALTERED);
465 
466 	case FIX:
467 		return (ALTERED);
468 
469 	case NOFIX:
470 		return (0);
471 
472 	default:
473 		errexit("UNKNOWN INODESC FIX MODE %d\n", idesc->id_fix);
474 	}
475 	/* NOTREACHED */
476 }
477 
478 /* VARARGS1 */
479 errexit(s1, s2, s3, s4)
480 	char *s1;
481 {
482 	printf(s1, s2, s3, s4);
483 	exit(8);
484 }
485 
486 /*
487  * An inconsistency occured which shouldn't during normal operations.
488  * Die if preening, otherwise just printf.
489  */
490 /* VARARGS1 */
491 pfatal(s, a1, a2, a3)
492 	char *s;
493 {
494 
495 	if (preen) {
496 		printf("%s: ", devname);
497 		printf(s, a1, a2, a3);
498 		printf("\n");
499 		printf("%s: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY.\n",
500 			devname);
501 		exit(8);
502 	}
503 	printf(s, a1, a2, a3);
504 }
505 
506 /*
507  * Pwarn is like printf when not preening,
508  * or a warning (preceded by filename) when preening.
509  */
510 /* VARARGS1 */
511 pwarn(s, a1, a2, a3, a4, a5, a6)
512 	char *s;
513 {
514 
515 	if (preen)
516 		printf("%s: ", devname);
517 	printf(s, a1, a2, a3, a4, a5, a6);
518 }
519 
520 #ifndef lint
521 /*
522  * Stub for routines from kernel.
523  */
524 panic(s)
525 	char *s;
526 {
527 
528 	pfatal("INTERNAL INCONSISTENCY:");
529 	errexit(s);
530 }
531 #endif
532