xref: /csrg-svn/sbin/fsck/utilities.c (revision 39973)
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.19 (Berkeley) 02/01/90";
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 	struct 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(mesg)
44 	char *mesg;
45 {
46 	char line[80];
47 	int cont = (strcmp(mesg, "CONTINUE") == 0);
48 
49 	if (preen)
50 		pfatal("INTERNAL ERROR: GOT TO reply()");
51 	printf("\n%s? ", mesg);
52 	if (!cont && (nflag || fswritefd < 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 	int maxlen;
73 {
74 	register long 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 struct bufarea *bp;
95 	long bufcnt, i;
96 	char *bufp;
97 
98 	bufp = malloc((unsigned int)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 = (struct bufarea *)malloc(sizeof(struct bufarea));
109 		bufp = malloc((unsigned int)sblock.fs_bsize);
110 		if (bp == NULL || bufp == NULL) {
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 struct bufarea *
129 getdatablk(blkno, size)
130 	daddr_t blkno;
131 	long size;
132 {
133 	register struct 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 struct bufarea *
158 getblk(bp, blk, size)
159 	register struct bufarea *bp;
160 	daddr_t blk;
161 	long size;
162 {
163 	daddr_t dblk;
164 
165 	dblk = fsbtodb(&sblock, blk);
166 	if (bp->b_bno == dblk)
167 		return (bp);
168 	flush(fswritefd, bp);
169 	diskreads++;
170 	bp->b_errs = bread(fsreadfd, bp->b_un.b_buf, dblk, size);
171 	bp->b_bno = dblk;
172 	bp->b_size = size;
173 	return (bp);
174 }
175 
176 flush(fd, bp)
177 	int fd;
178 	register struct bufarea *bp;
179 {
180 	register int i, j;
181 
182 	if (!bp->b_dirty)
183 		return;
184 	if (bp->b_errs != 0)
185 		pfatal("WRITING %sZERO'ED BLOCK %d TO DISK\n",
186 		    (bp->b_errs == bp->b_size / dev_bsize) ? "" : "PARTIALLY ",
187 		    bp->b_bno);
188 	bp->b_dirty = 0;
189 	bp->b_errs = 0;
190 	bwrite(fd, bp->b_un.b_buf, bp->b_bno, (long)bp->b_size);
191 	if (bp != &sblk)
192 		return;
193 	for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) {
194 		bwrite(fswritefd, (char *)sblock.fs_csp[j],
195 		    fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag),
196 		    sblock.fs_cssize - i < sblock.fs_bsize ?
197 		    sblock.fs_cssize - i : sblock.fs_bsize);
198 	}
199 }
200 
201 rwerror(mesg, blk)
202 	char *mesg;
203 	daddr_t blk;
204 {
205 
206 	if (preen == 0)
207 		printf("\n");
208 	pfatal("CANNOT %s: BLK %ld", mesg, blk);
209 	if (reply("CONTINUE") == 0)
210 		errexit("Program terminated\n");
211 }
212 
213 ckfini()
214 {
215 	register struct bufarea *bp, *nbp;
216 	int cnt = 0;
217 
218 	flush(fswritefd, &sblk);
219 	if (havesb && sblk.b_bno != SBOFF / dev_bsize &&
220 	    !preen && reply("UPDATE STANDARD SUPERBLOCK")) {
221 		sblk.b_bno = SBOFF / dev_bsize;
222 		sbdirty();
223 		flush(fswritefd, &sblk);
224 	}
225 	flush(fswritefd, &cgblk);
226 	free(cgblk.b_un.b_buf);
227 	for (bp = bufhead.b_prev; bp != &bufhead; bp = nbp) {
228 		cnt++;
229 		flush(fswritefd, bp);
230 		nbp = bp->b_prev;
231 		free(bp->b_un.b_buf);
232 		free((char *)bp);
233 	}
234 	if (bufhead.b_size != cnt)
235 		errexit("Panic: lost %d buffers\n", bufhead.b_size - cnt);
236 	if (debug)
237 		printf("cache missed %d of %d (%d%%)\n", diskreads,
238 		    totalreads, diskreads * 100 / totalreads);
239 	(void)close(fsreadfd);
240 	(void)close(fswritefd);
241 }
242 
243 bread(fd, buf, blk, size)
244 	int fd;
245 	char *buf;
246 	daddr_t blk;
247 	long size;
248 {
249 	char *cp;
250 	int i, errs;
251 
252 	if (lseek(fd, blk * dev_bsize, 0) < 0)
253 		rwerror("SEEK", blk);
254 	else if (read(fd, buf, (int)size) == size)
255 		return (0);
256 	rwerror("READ", blk);
257 	if (lseek(fd, blk * dev_bsize, 0) < 0)
258 		rwerror("SEEK", blk);
259 	errs = 0;
260 	bzero(buf, (int)size);
261 	printf("THE FOLLOWING DISK SECTORS COULD NOT BE READ:");
262 	for (cp = buf, i = 0; i < size; i += secsize, cp += secsize) {
263 		if (read(fd, cp, (int)secsize) < 0) {
264 			lseek(fd, blk * dev_bsize + i + secsize, 0);
265 			if (secsize != dev_bsize && dev_bsize != 1)
266 				printf(" %d (%d),",
267 				    (blk * dev_bsize + i) / secsize,
268 				    blk + i / dev_bsize);
269 			else
270 				printf(" %d,", blk + i / dev_bsize);
271 			errs++;
272 		}
273 	}
274 	printf("\n");
275 	return (errs);
276 }
277 
278 bwrite(fd, buf, blk, size)
279 	int fd;
280 	char *buf;
281 	daddr_t blk;
282 	long size;
283 {
284 	int i;
285 	char *cp;
286 
287 	if (fd < 0)
288 		return;
289 	if (lseek(fd, blk * dev_bsize, 0) < 0)
290 		rwerror("SEEK", blk);
291 	else if (write(fd, buf, (int)size) == size) {
292 		fsmodified = 1;
293 		return;
294 	}
295 	rwerror("WRITE", blk);
296 	if (lseek(fd, blk * dev_bsize, 0) < 0)
297 		rwerror("SEEK", blk);
298 	printf("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:");
299 	for (cp = buf, i = 0; i < size; i += dev_bsize, cp += dev_bsize)
300 		if (write(fd, cp, (int)dev_bsize) < 0) {
301 			lseek(fd, blk * dev_bsize + i + dev_bsize, 0);
302 			printf(" %d,", blk + i / dev_bsize);
303 		}
304 	printf("\n");
305 	return;
306 }
307 
308 /*
309  * allocate a data block with the specified number of fragments
310  */
311 allocblk(frags)
312 	long frags;
313 {
314 	register int i, j, k;
315 
316 	if (frags <= 0 || frags > sblock.fs_frag)
317 		return (0);
318 	for (i = 0; i < maxfsblock - sblock.fs_frag; i += sblock.fs_frag) {
319 		for (j = 0; j <= sblock.fs_frag - frags; j++) {
320 			if (testbmap(i + j))
321 				continue;
322 			for (k = 1; k < frags; k++)
323 				if (testbmap(i + j + k))
324 					break;
325 			if (k < frags) {
326 				j += k;
327 				continue;
328 			}
329 			for (k = 0; k < frags; k++)
330 				setbmap(i + j + k);
331 			n_blks += frags;
332 			return (i + j);
333 		}
334 	}
335 	return (0);
336 }
337 
338 /*
339  * Free a previously allocated block
340  */
341 freeblk(blkno, frags)
342 	daddr_t blkno;
343 	long frags;
344 {
345 	struct inodesc idesc;
346 
347 	idesc.id_blkno = blkno;
348 	idesc.id_numfrags = frags;
349 	pass4check(&idesc);
350 }
351 
352 /*
353  * Find a pathname
354  */
355 getpathname(namebuf, curdir, ino)
356 	char *namebuf;
357 	ino_t curdir, ino;
358 {
359 	int len;
360 	register char *cp;
361 	struct inodesc idesc;
362 	extern int findname();
363 
364 	if (statemap[ino] != DSTATE && statemap[ino] != DFOUND) {
365 		strcpy(namebuf, "?");
366 		return;
367 	}
368 	bzero((char *)&idesc, sizeof(struct inodesc));
369 	idesc.id_type = DATA;
370 	cp = &namebuf[BUFSIZ - 1];
371 	*cp = '\0';
372 	if (curdir != ino) {
373 		idesc.id_parent = curdir;
374 		goto namelookup;
375 	}
376 	while (ino != ROOTINO) {
377 		idesc.id_number = ino;
378 		idesc.id_func = findino;
379 		idesc.id_name = "..";
380 		if ((ckinode(ginode(ino), &idesc) & FOUND) == 0)
381 			break;
382 	namelookup:
383 		idesc.id_number = idesc.id_parent;
384 		idesc.id_parent = ino;
385 		idesc.id_func = findname;
386 		idesc.id_name = namebuf;
387 		if ((ckinode(ginode(idesc.id_number), &idesc) & FOUND) == 0)
388 			break;
389 		len = strlen(namebuf);
390 		cp -= len;
391 		if (cp < &namebuf[MAXNAMLEN])
392 			break;
393 		bcopy(namebuf, cp, len);
394 		*--cp = '/';
395 		ino = idesc.id_number;
396 	}
397 	if (ino != ROOTINO) {
398 		strcpy(namebuf, "?");
399 		return;
400 	}
401 	bcopy(cp, namebuf, &namebuf[BUFSIZ] - cp);
402 }
403 
404 void
405 catch()
406 {
407 	ckfini();
408 	exit(12);
409 }
410 
411 /*
412  * When preening, allow a single quit to signal
413  * a special exit after filesystem checks complete
414  * so that reboot sequence may be interrupted.
415  */
416 void
417 catchquit()
418 {
419 	extern returntosingle;
420 
421 	printf("returning to single-user after filesystem check\n");
422 	returntosingle = 1;
423 	(void)signal(SIGQUIT, SIG_DFL);
424 }
425 
426 /*
427  * Ignore a single quit signal; wait and flush just in case.
428  * Used by child processes in preen.
429  */
430 void
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 			direrror(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 unexpected inconsistency occured.
488  * Die if preening, otherwise just print message and continue.
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 just prints a message 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