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