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