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