xref: /dragonfly/sbin/fsck/utilities.c (revision f9a239ec)
1 /*
2  * Copyright (c) 1980, 1986, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by the University of
16  *	California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * @(#)utilities.c	8.6 (Berkeley) 5/19/95
34  * $FreeBSD: src/sbin/fsck/utilities.c,v 1.11.2.3 2001/01/23 23:11:07 iedowse Exp $
35  * $DragonFly: src/sbin/fsck/utilities.c,v 1.7 2004/12/18 21:43:38 swildner Exp $
36  */
37 
38 #include <sys/param.h>
39 
40 #include <vfs/ufs/dinode.h>
41 #include <vfs/ufs/dir.h>
42 #include <vfs/ufs/fs.h>
43 
44 #include <err.h>
45 #include <string.h>
46 
47 #include "fsck.h"
48 
49 long	diskreads, totalreads;	/* Disk cache statistics */
50 
51 static void rwerror(char *mesg, ufs_daddr_t blk);
52 
53 int
54 ftypeok(struct dinode *dp)
55 {
56 	switch (dp->di_mode & IFMT) {
57 
58 	case IFDIR:
59 	case IFREG:
60 	case IFBLK:
61 	case IFCHR:
62 	case IFLNK:
63 	case IFSOCK:
64 	case IFIFO:
65 		return (1);
66 
67 	default:
68 		if (debug)
69 			printf("bad file type 0%o\n", dp->di_mode);
70 		return (0);
71 	}
72 }
73 
74 int
75 reply(char *question)
76 {
77 	int persevere;
78 	char c;
79 
80 	if (preen)
81 		pfatal("INTERNAL ERROR: GOT TO reply()");
82 	persevere = !strcmp(question, "CONTINUE");
83 	printf("\n");
84 	if (!persevere && (nflag || fswritefd < 0)) {
85 		printf("%s? no\n\n", question);
86 		resolved = 0;
87 		return (0);
88 	}
89 	if (yflag || (persevere && nflag)) {
90 		printf("%s? yes\n\n", question);
91 		return (1);
92 	}
93 	do	{
94 		printf("%s? [yn] ", question);
95 		fflush(stdout);
96 		c = getc(stdin);
97 		while (c != '\n' && getc(stdin) != '\n') {
98 			if (feof(stdin)) {
99 				resolved = 0;
100 				return (0);
101 			}
102 		}
103 	} while (c != 'y' && c != 'Y' && c != 'n' && c != 'N');
104 	printf("\n");
105 	if (c == 'y' || c == 'Y')
106 		return (1);
107 	resolved = 0;
108 	return (0);
109 }
110 
111 /*
112  * Look up state information for an inode.
113  */
114 struct inostat *
115 inoinfo(ino_t inum)
116 {
117 	static struct inostat unallocated = { USTATE, 0, 0 };
118 	struct inostatlist *ilp;
119 	int iloff;
120 
121 	if (inum > maxino)
122 		errx(EEXIT, "inoinfo: inumber %d out of range", inum);
123 	ilp = &inostathead[inum / sblock.fs_ipg];
124 	iloff = inum % sblock.fs_ipg;
125 	if (iloff >= ilp->il_numalloced)
126 		return (&unallocated);
127 	return (&ilp->il_stat[iloff]);
128 }
129 
130 /*
131  * Malloc buffers and set up cache.
132  */
133 void
134 bufinit(void)
135 {
136 	register struct bufarea *bp;
137 	long bufcnt, i;
138 	char *bufp;
139 
140 	pbp = pdirbp = (struct bufarea *)0;
141 	bufp = malloc((unsigned int)sblock.fs_bsize);
142 	if (bufp == 0)
143 		errx(EEXIT, "cannot allocate buffer pool");
144 	cgblk.b_un.b_buf = bufp;
145 	initbarea(&cgblk);
146 	bufhead.b_next = bufhead.b_prev = &bufhead;
147 	bufcnt = MAXBUFSPACE / sblock.fs_bsize;
148 	if (bufcnt < MINBUFS)
149 		bufcnt = MINBUFS;
150 	for (i = 0; i < bufcnt; i++) {
151 		bp = (struct bufarea *)malloc(sizeof(struct bufarea));
152 		bufp = malloc((unsigned int)sblock.fs_bsize);
153 		if (bp == NULL || bufp == NULL) {
154 			if (i >= MINBUFS)
155 				break;
156 			errx(EEXIT, "cannot allocate buffer pool");
157 		}
158 		bp->b_un.b_buf = bufp;
159 		bp->b_prev = &bufhead;
160 		bp->b_next = bufhead.b_next;
161 		bufhead.b_next->b_prev = bp;
162 		bufhead.b_next = bp;
163 		initbarea(bp);
164 	}
165 	bufhead.b_size = i;	/* save number of buffers */
166 }
167 
168 /*
169  * Manage a cache of directory blocks.
170  */
171 struct bufarea *
172 getdatablk(ufs_daddr_t blkno, long size)
173 {
174 	register struct bufarea *bp;
175 
176 	for (bp = bufhead.b_next; bp != &bufhead; bp = bp->b_next)
177 		if (bp->b_bno == fsbtodb(&sblock, blkno))
178 			goto foundit;
179 	for (bp = bufhead.b_prev; bp != &bufhead; bp = bp->b_prev)
180 		if ((bp->b_flags & B_INUSE) == 0)
181 			break;
182 	if (bp == &bufhead)
183 		errx(EEXIT, "deadlocked buffer pool");
184 	getblk(bp, blkno, size);
185 	/* fall through */
186 foundit:
187 	totalreads++;
188 	bp->b_prev->b_next = bp->b_next;
189 	bp->b_next->b_prev = bp->b_prev;
190 	bp->b_prev = &bufhead;
191 	bp->b_next = bufhead.b_next;
192 	bufhead.b_next->b_prev = bp;
193 	bufhead.b_next = bp;
194 	bp->b_flags |= B_INUSE;
195 	return (bp);
196 }
197 
198 void
199 getblk(register struct bufarea *bp, ufs_daddr_t blk, long size)
200 {
201 	ufs_daddr_t dblk;
202 
203 	dblk = fsbtodb(&sblock, blk);
204 	if (bp->b_bno != dblk) {
205 		flush(fswritefd, bp);
206 		diskreads++;
207 		bp->b_errs = bread(fsreadfd, bp->b_un.b_buf, dblk, size);
208 		bp->b_bno = dblk;
209 		bp->b_size = size;
210 	}
211 }
212 
213 void
214 flush(int fd, register struct bufarea *bp)
215 {
216 	register int i, j;
217 
218 	if (!bp->b_dirty)
219 		return;
220 	if (bp->b_errs != 0)
221 		pfatal("WRITING %sZERO'ED BLOCK %d TO DISK\n",
222 		    (bp->b_errs == bp->b_size / dev_bsize) ? "" : "PARTIALLY ",
223 		    bp->b_bno);
224 	bp->b_dirty = 0;
225 	bp->b_errs = 0;
226 	bwrite(fd, bp->b_un.b_buf, bp->b_bno, (long)bp->b_size);
227 	if (bp != &sblk)
228 		return;
229 	for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) {
230 		bwrite(fswritefd, (char *)sblock.fs_csp + i,
231 		    fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag),
232 		    sblock.fs_cssize - i < sblock.fs_bsize ?
233 		    sblock.fs_cssize - i : sblock.fs_bsize);
234 	}
235 }
236 
237 static void
238 rwerror(char *mesg, ufs_daddr_t blk)
239 {
240 
241 	if (preen == 0)
242 		printf("\n");
243 	pfatal("CANNOT %s: BLK %ld", mesg, blk);
244 	if (reply("CONTINUE") == 0)
245 		exit(EEXIT);
246 }
247 
248 void
249 ckfini(int markclean)
250 {
251 	register struct bufarea *bp, *nbp;
252 	int ofsmodified, cnt = 0;
253 
254 	if (fswritefd < 0) {
255 		close(fsreadfd);
256 		return;
257 	}
258 	flush(fswritefd, &sblk);
259 	if (havesb && sblk.b_bno != SBOFF / dev_bsize &&
260 	    !preen && reply("UPDATE STANDARD SUPERBLOCK")) {
261 		sblk.b_bno = SBOFF / dev_bsize;
262 		sbdirty();
263 		flush(fswritefd, &sblk);
264 	}
265 	flush(fswritefd, &cgblk);
266 	free(cgblk.b_un.b_buf);
267 	for (bp = bufhead.b_prev; bp && bp != &bufhead; bp = nbp) {
268 		cnt++;
269 		flush(fswritefd, bp);
270 		nbp = bp->b_prev;
271 		free(bp->b_un.b_buf);
272 		free((char *)bp);
273 	}
274 	if (bufhead.b_size != cnt)
275 		errx(EEXIT, "panic: lost %d buffers", bufhead.b_size - cnt);
276 	pbp = pdirbp = (struct bufarea *)0;
277 	if (sblock.fs_clean != markclean) {
278 		sblock.fs_clean = markclean;
279 		sbdirty();
280 		ofsmodified = fsmodified;
281 		flush(fswritefd, &sblk);
282 		fsmodified = ofsmodified;
283 		if (!preen) {
284 			printf("\n***** FILE SYSTEM MARKED %s *****\n",
285 			    markclean ? "CLEAN" : "DIRTY");
286 			if (!markclean)
287 				rerun = 1;
288 		}
289 	} else if (!preen && !markclean) {
290 		printf("\n***** FILE SYSTEM STILL DIRTY *****\n");
291 		rerun = 1;
292 	}
293 	if (debug)
294 		printf("cache missed %ld of %ld (%d%%)\n", diskreads,
295 		    totalreads, (int)(diskreads * 100 / totalreads));
296 	close(fsreadfd);
297 	close(fswritefd);
298 }
299 
300 int
301 bread(int fd, char *buf, ufs_daddr_t blk, long size)
302 {
303 	char *cp;
304 	int i, errs;
305 	off_t offset;
306 
307 	offset = blk;
308 	offset *= dev_bsize;
309 	if (lseek(fd, offset, 0) < 0)
310 		rwerror("SEEK", blk);
311 	else if (read(fd, buf, (int)size) == size)
312 		return (0);
313 	rwerror("READ", blk);
314 	if (lseek(fd, offset, 0) < 0)
315 		rwerror("SEEK", blk);
316 	errs = 0;
317 	memset(buf, 0, (size_t)size);
318 	printf("THE FOLLOWING DISK SECTORS COULD NOT BE READ:");
319 	for (cp = buf, i = 0; i < size; i += secsize, cp += secsize) {
320 		if (read(fd, cp, (int)secsize) != secsize) {
321 			lseek(fd, offset + i + secsize, 0);
322 			if (secsize != dev_bsize && dev_bsize != 1)
323 				printf(" %ld (%ld),",
324 				    (blk * dev_bsize + i) / secsize,
325 				    blk + i / dev_bsize);
326 			else
327 				printf(" %ld,", blk + i / dev_bsize);
328 			errs++;
329 		}
330 	}
331 	printf("\n");
332 	if (errs)
333 		resolved = 0;
334 	return (errs);
335 }
336 
337 void
338 bwrite(int fd, char *buf, ufs_daddr_t blk, long size)
339 {
340 	int i;
341 	char *cp;
342 	off_t offset;
343 
344 	if (fd < 0)
345 		return;
346 	offset = blk;
347 	offset *= dev_bsize;
348 	if (lseek(fd, offset, 0) < 0)
349 		rwerror("SEEK", blk);
350 	else if (write(fd, buf, (int)size) == size) {
351 		fsmodified = 1;
352 		return;
353 	}
354 	resolved = 0;
355 	rwerror("WRITE", blk);
356 	if (lseek(fd, offset, 0) < 0)
357 		rwerror("SEEK", blk);
358 	printf("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:");
359 	for (cp = buf, i = 0; i < size; i += dev_bsize, cp += dev_bsize)
360 		if (write(fd, cp, (int)dev_bsize) != dev_bsize) {
361 			lseek(fd, offset + i + dev_bsize, 0);
362 			printf(" %ld,", blk + i / dev_bsize);
363 		}
364 	printf("\n");
365 	return;
366 }
367 
368 /*
369  * allocate a data block with the specified number of fragments
370  */
371 ufs_daddr_t
372 allocblk(long frags)
373 {
374 	int i, j, k, cg, baseblk;
375 	struct cg *cgp = &cgrp;
376 
377 	if (frags <= 0 || frags > sblock.fs_frag)
378 		return (0);
379 	for (i = 0; i < maxfsblock - sblock.fs_frag; i += sblock.fs_frag) {
380 		for (j = 0; j <= sblock.fs_frag - frags; j++) {
381 			if (testbmap(i + j))
382 				continue;
383 			for (k = 1; k < frags; k++)
384 				if (testbmap(i + j + k))
385 					break;
386 			if (k < frags) {
387 				j += k;
388 				continue;
389 			}
390 			cg = dtog(&sblock, i + j);
391 			getblk(&cgblk, cgtod(&sblock, cg), sblock.fs_cgsize);
392 			if (!cg_chkmagic(cgp))
393 				pfatal("CG %d: BAD MAGIC NUMBER\n", cg);
394 			baseblk = dtogd(&sblock, i + j);
395 			for (k = 0; k < frags; k++) {
396 				setbmap(i + j + k);
397 				clrbit(cg_blksfree(cgp), baseblk + k);
398 			}
399 			n_blks += frags;
400 			if (frags == sblock.fs_frag)
401 				cgp->cg_cs.cs_nbfree--;
402 			else
403 				cgp->cg_cs.cs_nffree -= frags;
404 			cgdirty();
405 			return (i + j);
406 		}
407 	}
408 	return (0);
409 }
410 
411 /*
412  * Free a previously allocated block
413  */
414 void
415 freeblk(ufs_daddr_t blkno, long frags)
416 {
417 	struct inodesc idesc;
418 
419 	idesc.id_blkno = blkno;
420 	idesc.id_numfrags = frags;
421 	pass4check(&idesc);
422 }
423 
424 /*
425  * Find a pathname
426  */
427 void
428 getpathname(char *namebuf, ino_t curdir, ino_t ino)
429 {
430 	int len;
431 	register char *cp;
432 	struct inodesc idesc;
433 	static int busy = 0;
434 
435 	if (curdir == ino && ino == ROOTINO) {
436 		strcpy(namebuf, "/");
437 		return;
438 	}
439 	if (busy ||
440 	    (inoinfo(curdir)->ino_state != DSTATE &&
441 	     inoinfo(curdir)->ino_state != DFOUND)) {
442 		strcpy(namebuf, "?");
443 		return;
444 	}
445 	busy = 1;
446 	memset(&idesc, 0, sizeof(struct inodesc));
447 	idesc.id_type = DATA;
448 	idesc.id_fix = IGNORE;
449 	cp = &namebuf[MAXPATHLEN - 1];
450 	*cp = '\0';
451 	if (curdir != ino) {
452 		idesc.id_parent = curdir;
453 		goto namelookup;
454 	}
455 	while (ino != ROOTINO) {
456 		idesc.id_number = ino;
457 		idesc.id_func = findino;
458 		idesc.id_name = "..";
459 		if ((ckinode(ginode(ino), &idesc) & FOUND) == 0)
460 			break;
461 	namelookup:
462 		idesc.id_number = idesc.id_parent;
463 		idesc.id_parent = ino;
464 		idesc.id_func = findname;
465 		idesc.id_name = namebuf;
466 		if ((ckinode(ginode(idesc.id_number), &idesc)&FOUND) == 0)
467 			break;
468 		len = strlen(namebuf);
469 		cp -= len;
470 		memmove(cp, namebuf, (size_t)len);
471 		*--cp = '/';
472 		if (cp < &namebuf[MAXNAMLEN])
473 			break;
474 		ino = idesc.id_number;
475 	}
476 	busy = 0;
477 	if (ino != ROOTINO)
478 		*--cp = '?';
479 	memmove(namebuf, cp, (size_t)(&namebuf[MAXPATHLEN] - cp));
480 }
481 
482 /* ARGSUSED */
483 void
484 catch(int sig)
485 {
486 	if (!doinglevel2)
487 		ckfini(0);
488 	exit(12);
489 }
490 
491 /*
492  * When preening, allow a single quit to signal
493  * a special exit after filesystem checks complete
494  * so that reboot sequence may be interrupted.
495  */
496 /* ARGSUSED */
497 void
498 catchquit(int sig)
499 {
500 	printf("returning to single-user after filesystem check\n");
501 	returntosingle = 1;
502 	signal(SIGQUIT, SIG_DFL);
503 }
504 
505 /*
506  * Ignore a single quit signal; wait and flush just in case.
507  * Used by child processes in preen.
508  */
509 /* ARGSUSED */
510 void
511 voidquit(int sig)
512 {
513 
514 	sleep(1);
515 	signal(SIGQUIT, SIG_IGN);
516 	signal(SIGQUIT, SIG_DFL);
517 }
518 
519 /* ARGSUSED */
520 void
521 infohandler(int sig)
522 {
523 	got_siginfo = 1;
524 }
525 
526 /*
527  * determine whether an inode should be fixed.
528  */
529 int
530 dofix(register struct inodesc *idesc, char *msg)
531 {
532 
533 	switch (idesc->id_fix) {
534 
535 	case DONTKNOW:
536 		if (idesc->id_type == DATA)
537 			direrror(idesc->id_number, msg);
538 		else
539 			pwarn("%s", msg);
540 		if (preen) {
541 			printf(" (SALVAGED)\n");
542 			idesc->id_fix = FIX;
543 			return (ALTERED);
544 		}
545 		if (reply("SALVAGE") == 0) {
546 			idesc->id_fix = NOFIX;
547 			return (0);
548 		}
549 		idesc->id_fix = FIX;
550 		return (ALTERED);
551 
552 	case FIX:
553 		return (ALTERED);
554 
555 	case NOFIX:
556 	case IGNORE:
557 		return (0);
558 
559 	default:
560 		errx(EEXIT, "UNKNOWN INODESC FIX MODE %d", idesc->id_fix);
561 	}
562 	/* NOTREACHED */
563 	return (0);
564 }
565 
566 #include <stdarg.h>
567 
568 /*
569  * An unexpected inconsistency occured.
570  * Die if preening or filesystem is running with soft dependency protocol,
571  * otherwise just print message and continue.
572  */
573 void
574 pfatal(const char *fmt, ...)
575 {
576 	va_list ap;
577 	va_start(ap, fmt);
578 	if (!preen) {
579 		vfprintf(stderr, fmt, ap);
580 		va_end(ap);
581 		if (usedsoftdep)
582 			fprintf(stderr,
583 			    "\nUNEXPECTED SOFT UPDATE INCONSISTENCY\n");
584 		return;
585 	}
586 	if (cdevname == NULL)
587 		cdevname = "fsck";
588 	fprintf(stderr, "%s: ", cdevname);
589 	vfprintf(stderr, fmt, ap);
590 	fprintf(stderr,
591 	    "\n%s: UNEXPECTED%sINCONSISTENCY; RUN fsck MANUALLY.\n",
592 	    cdevname, usedsoftdep ? " SOFT UPDATE " : " ");
593 	ckfini(0);
594 	exit(EEXIT);
595 }
596 
597 /*
598  * Pwarn just prints a message when not preening or running soft dependency
599  * protocol, or a warning (preceded by filename) when preening.
600  */
601 void
602 pwarn(const char *fmt, ...)
603 {
604 	va_list ap;
605 	va_start(ap, fmt);
606 	if (preen)
607 		fprintf(stderr, "%s: ", cdevname);
608 	vfprintf(stderr, fmt, ap);
609 	va_end(ap);
610 }
611 
612 /*
613  * Stub for routines from kernel.
614  */
615 void
616 panic(const char *fmt, ...)
617 {
618 	va_list ap;
619 
620 	va_start(ap, fmt);
621 	pfatal("INTERNAL INCONSISTENCY:");
622 	vfprintf(stderr, fmt, ap);
623 	va_end(ap);
624 	exit(EEXIT);
625 }
626