xref: /freebsd/sbin/fsck_ffs/fsutil.c (revision a3e8fd0b7f663db7eafff527d5c3ca3bcfa8a537)
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 
34 #ifndef lint
35 #if 0
36 static const char sccsid[] = "@(#)utilities.c	8.6 (Berkeley) 5/19/95";
37 #endif
38 static const char rcsid[] =
39   "$FreeBSD$";
40 #endif /* not lint */
41 
42 #include <sys/param.h>
43 #include <sys/types.h>
44 #include <sys/sysctl.h>
45 #include <sys/disklabel.h>
46 #include <sys/stat.h>
47 #include <sys/disklabel.h>
48 
49 #include <ufs/ufs/dinode.h>
50 #include <ufs/ufs/dir.h>
51 #include <ufs/ffs/fs.h>
52 
53 #include <err.h>
54 #include <errno.h>
55 #include <string.h>
56 #include <ctype.h>
57 #include <fstab.h>
58 #include <stdint.h>
59 #include <stdio.h>
60 #include <stdlib.h>
61 #include <unistd.h>
62 
63 #include "fsck.h"
64 
65 long	diskreads, totalreads;	/* Disk cache statistics */
66 
67 int
68 ftypeok(union dinode *dp)
69 {
70 	switch (DIP(dp, di_mode) & IFMT) {
71 
72 	case IFDIR:
73 	case IFREG:
74 	case IFBLK:
75 	case IFCHR:
76 	case IFLNK:
77 	case IFSOCK:
78 	case IFIFO:
79 		return (1);
80 
81 	default:
82 		if (debug)
83 			printf("bad file type 0%o\n", DIP(dp, di_mode));
84 		return (0);
85 	}
86 }
87 
88 int
89 reply(const char *question)
90 {
91 	int persevere;
92 	char c;
93 
94 	if (preen)
95 		pfatal("INTERNAL ERROR: GOT TO reply()");
96 	persevere = !strcmp(question, "CONTINUE");
97 	printf("\n");
98 	if (!persevere && (nflag || (fswritefd < 0 && bkgrdflag == 0))) {
99 		printf("%s? no\n\n", question);
100 		resolved = 0;
101 		return (0);
102 	}
103 	if (yflag || (persevere && nflag)) {
104 		printf("%s? yes\n\n", question);
105 		return (1);
106 	}
107 	do	{
108 		printf("%s? [yn] ", question);
109 		(void) fflush(stdout);
110 		c = getc(stdin);
111 		while (c != '\n' && getc(stdin) != '\n') {
112 			if (feof(stdin)) {
113 				resolved = 0;
114 				return (0);
115 			}
116 		}
117 	} while (c != 'y' && c != 'Y' && c != 'n' && c != 'N');
118 	printf("\n");
119 	if (c == 'y' || c == 'Y')
120 		return (1);
121 	resolved = 0;
122 	return (0);
123 }
124 
125 /*
126  * Look up state information for an inode.
127  */
128 struct inostat *
129 inoinfo(ino_t inum)
130 {
131 	static struct inostat unallocated = { USTATE, 0, 0 };
132 	struct inostatlist *ilp;
133 	int iloff;
134 
135 	if (inum > maxino)
136 		errx(EEXIT, "inoinfo: inumber %d out of range", inum);
137 	ilp = &inostathead[inum / sblock.fs_ipg];
138 	iloff = inum % sblock.fs_ipg;
139 	if (iloff >= ilp->il_numalloced)
140 		return (&unallocated);
141 	return (&ilp->il_stat[iloff]);
142 }
143 
144 /*
145  * Malloc buffers and set up cache.
146  */
147 void
148 bufinit(void)
149 {
150 	struct bufarea *bp;
151 	long bufcnt, i;
152 	char *bufp;
153 
154 	pbp = pdirbp = (struct bufarea *)0;
155 	bufp = malloc((unsigned int)sblock.fs_bsize);
156 	if (bufp == 0)
157 		errx(EEXIT, "cannot allocate buffer pool");
158 	cgblk.b_un.b_buf = bufp;
159 	initbarea(&cgblk);
160 	bufhead.b_next = bufhead.b_prev = &bufhead;
161 	bufcnt = MAXBUFSPACE / sblock.fs_bsize;
162 	if (bufcnt < MINBUFS)
163 		bufcnt = MINBUFS;
164 	for (i = 0; i < bufcnt; i++) {
165 		bp = (struct bufarea *)malloc(sizeof(struct bufarea));
166 		bufp = malloc((unsigned int)sblock.fs_bsize);
167 		if (bp == NULL || bufp == NULL) {
168 			if (i >= MINBUFS)
169 				break;
170 			errx(EEXIT, "cannot allocate buffer pool");
171 		}
172 		bp->b_un.b_buf = bufp;
173 		bp->b_prev = &bufhead;
174 		bp->b_next = bufhead.b_next;
175 		bufhead.b_next->b_prev = bp;
176 		bufhead.b_next = bp;
177 		initbarea(bp);
178 	}
179 	bufhead.b_size = i;	/* save number of buffers */
180 }
181 
182 /*
183  * Manage a cache of directory blocks.
184  */
185 struct bufarea *
186 getdatablk(ufs2_daddr_t blkno, long size)
187 {
188 	struct bufarea *bp;
189 
190 	for (bp = bufhead.b_next; bp != &bufhead; bp = bp->b_next)
191 		if (bp->b_bno == fsbtodb(&sblock, blkno))
192 			goto foundit;
193 	for (bp = bufhead.b_prev; bp != &bufhead; bp = bp->b_prev)
194 		if ((bp->b_flags & B_INUSE) == 0)
195 			break;
196 	if (bp == &bufhead)
197 		errx(EEXIT, "deadlocked buffer pool");
198 	getblk(bp, blkno, size);
199 	/* fall through */
200 foundit:
201 	bp->b_prev->b_next = bp->b_next;
202 	bp->b_next->b_prev = bp->b_prev;
203 	bp->b_prev = &bufhead;
204 	bp->b_next = bufhead.b_next;
205 	bufhead.b_next->b_prev = bp;
206 	bufhead.b_next = bp;
207 	bp->b_flags |= B_INUSE;
208 	return (bp);
209 }
210 
211 void
212 getblk(struct bufarea *bp, ufs2_daddr_t blk, long size)
213 {
214 	ufs2_daddr_t dblk;
215 
216 	totalreads++;
217 	dblk = fsbtodb(&sblock, blk);
218 	if (bp->b_bno != dblk) {
219 		flush(fswritefd, bp);
220 		diskreads++;
221 		bp->b_errs = bread(fsreadfd, bp->b_un.b_buf, dblk, size);
222 		bp->b_bno = dblk;
223 		bp->b_size = size;
224 	}
225 }
226 
227 void
228 flush(int fd, struct bufarea *bp)
229 {
230 	int i, j;
231 
232 	if (!bp->b_dirty)
233 		return;
234 	bp->b_dirty = 0;
235 	if (fswritefd < 0) {
236 		pfatal("WRITING IN READ_ONLY MODE.\n");
237 		return;
238 	}
239 	if (bp->b_errs != 0)
240 		pfatal("WRITING %sZERO'ED BLOCK %lld TO DISK\n",
241 		    (bp->b_errs == bp->b_size / dev_bsize) ? "" : "PARTIALLY ",
242 		    (long long)bp->b_bno);
243 	bp->b_errs = 0;
244 	bwrite(fd, bp->b_un.b_buf, bp->b_bno, (long)bp->b_size);
245 	if (bp != &sblk)
246 		return;
247 	for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) {
248 		bwrite(fswritefd, (char *)sblock.fs_csp + i,
249 		    fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag),
250 		    sblock.fs_cssize - i < sblock.fs_bsize ?
251 		    sblock.fs_cssize - i : sblock.fs_bsize);
252 	}
253 }
254 
255 void
256 rwerror(const char *mesg, ufs2_daddr_t blk)
257 {
258 
259 	if (bkgrdcheck)
260 		exit(EEXIT);
261 	if (preen == 0)
262 		printf("\n");
263 	pfatal("CANNOT %s: %ld", mesg, (long)blk);
264 	if (reply("CONTINUE") == 0)
265 		exit(EEXIT);
266 }
267 
268 void
269 ckfini(int markclean)
270 {
271 	struct bufarea *bp, *nbp;
272 	int ofsmodified, cnt = 0;
273 
274 	if (bkgrdflag) {
275 		unlink(snapname);
276 		if ((!(sblock.fs_flags & FS_UNCLEAN)) != markclean) {
277 			cmd.value = FS_UNCLEAN;
278 			cmd.size = markclean ? -1 : 1;
279 			if (sysctlbyname("vfs.ffs.setflags", 0, 0,
280 			    &cmd, sizeof cmd) == -1)
281 				rwerror("SET FILESYSTEM FLAGS", FS_UNCLEAN);
282 			if (!preen) {
283 				printf("\n***** FILE SYSTEM MARKED %s *****\n",
284 				    markclean ? "CLEAN" : "DIRTY");
285 				if (!markclean)
286 					rerun = 1;
287 			}
288 		} else if (!preen && !markclean) {
289 			printf("\n***** FILE SYSTEM STILL DIRTY *****\n");
290 			rerun = 1;
291 		}
292 	}
293 	if (fswritefd < 0) {
294 		(void)close(fsreadfd);
295 		return;
296 	}
297 	flush(fswritefd, &sblk);
298 	if (havesb && cursnapshot == 0 && sblock.fs_magic == FS_UFS2_MAGIC &&
299 	    sblk.b_bno != fsbtodb(&sblock, sblock.fs_sblockloc) &&
300 	    !preen && reply("UPDATE STANDARD SUPERBLOCK")) {
301 		sblk.b_bno = fsbtodb(&sblock, sblock.fs_sblockloc);
302 		sbdirty();
303 		flush(fswritefd, &sblk);
304 	}
305 	flush(fswritefd, &cgblk);
306 	free(cgblk.b_un.b_buf);
307 	for (bp = bufhead.b_prev; bp && bp != &bufhead; bp = nbp) {
308 		cnt++;
309 		flush(fswritefd, bp);
310 		nbp = bp->b_prev;
311 		free(bp->b_un.b_buf);
312 		free((char *)bp);
313 	}
314 	if (bufhead.b_size != cnt)
315 		errx(EEXIT, "panic: lost %d buffers", bufhead.b_size - cnt);
316 	pbp = pdirbp = (struct bufarea *)0;
317 	if (cursnapshot == 0 && sblock.fs_clean != markclean) {
318 		if ((sblock.fs_clean = markclean) != 0) {
319 			sblock.fs_flags &= ~(FS_UNCLEAN | FS_NEEDSFSCK);
320 			sblock.fs_pendingblocks = 0;
321 			sblock.fs_pendinginodes = 0;
322 		}
323 		sbdirty();
324 		ofsmodified = fsmodified;
325 		flush(fswritefd, &sblk);
326 		fsmodified = ofsmodified;
327 		if (!preen) {
328 			printf("\n***** FILE SYSTEM MARKED %s *****\n",
329 			    markclean ? "CLEAN" : "DIRTY");
330 			if (!markclean)
331 				rerun = 1;
332 		}
333 	} else if (!preen && !markclean) {
334 		printf("\n***** FILE SYSTEM STILL DIRTY *****\n");
335 		rerun = 1;
336 	}
337 	if (debug && totalreads > 0)
338 		printf("cache missed %ld of %ld (%d%%)\n", diskreads,
339 		    totalreads, (int)(diskreads * 100 / totalreads));
340 	(void)close(fsreadfd);
341 	(void)close(fswritefd);
342 }
343 
344 int
345 bread(int fd, char *buf, ufs2_daddr_t blk, long size)
346 {
347 	char *cp;
348 	int i, errs;
349 	off_t offset;
350 
351 	offset = blk;
352 	offset *= dev_bsize;
353 	if (lseek(fd, offset, 0) < 0)
354 		rwerror("SEEK BLK", blk);
355 	else if (read(fd, buf, (int)size) == size)
356 		return (0);
357 	rwerror("READ BLK", blk);
358 	if (lseek(fd, offset, 0) < 0)
359 		rwerror("SEEK BLK", blk);
360 	errs = 0;
361 	memset(buf, 0, (size_t)size);
362 	printf("THE FOLLOWING DISK SECTORS COULD NOT BE READ:");
363 	for (cp = buf, i = 0; i < size; i += secsize, cp += secsize) {
364 		if (read(fd, cp, (int)secsize) != secsize) {
365 			(void)lseek(fd, offset + i + secsize, 0);
366 			if (secsize != dev_bsize && dev_bsize != 1)
367 				printf(" %jd (%jd),",
368 				    (intmax_t)(blk * dev_bsize + i) / secsize,
369 				    (intmax_t)blk + i / dev_bsize);
370 			else
371 				printf(" %jd,", (intmax_t)blk + i / dev_bsize);
372 			errs++;
373 		}
374 	}
375 	printf("\n");
376 	if (errs)
377 		resolved = 0;
378 	return (errs);
379 }
380 
381 void
382 bwrite(int fd, char *buf, ufs2_daddr_t blk, long size)
383 {
384 	int i;
385 	char *cp;
386 	off_t offset;
387 
388 	if (fd < 0)
389 		return;
390 	offset = blk;
391 	offset *= dev_bsize;
392 	if (lseek(fd, offset, 0) < 0)
393 		rwerror("SEEK BLK", blk);
394 	else if (write(fd, buf, (int)size) == size) {
395 		fsmodified = 1;
396 		return;
397 	}
398 	resolved = 0;
399 	rwerror("WRITE BLK", blk);
400 	if (lseek(fd, offset, 0) < 0)
401 		rwerror("SEEK BLK", blk);
402 	printf("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:");
403 	for (cp = buf, i = 0; i < size; i += dev_bsize, cp += dev_bsize)
404 		if (write(fd, cp, (int)dev_bsize) != dev_bsize) {
405 			(void)lseek(fd, offset + i + dev_bsize, 0);
406 			printf(" %jd,", (intmax_t)blk + i / dev_bsize);
407 		}
408 	printf("\n");
409 	return;
410 }
411 
412 /*
413  * allocate a data block with the specified number of fragments
414  */
415 ufs2_daddr_t
416 allocblk(long frags)
417 {
418 	int i, j, k, cg, baseblk;
419 	struct cg *cgp = &cgrp;
420 
421 	if (frags <= 0 || frags > sblock.fs_frag)
422 		return (0);
423 	for (i = 0; i < maxfsblock - sblock.fs_frag; i += sblock.fs_frag) {
424 		for (j = 0; j <= sblock.fs_frag - frags; j++) {
425 			if (testbmap(i + j))
426 				continue;
427 			for (k = 1; k < frags; k++)
428 				if (testbmap(i + j + k))
429 					break;
430 			if (k < frags) {
431 				j += k;
432 				continue;
433 			}
434 			cg = dtog(&sblock, i + j);
435 			getblk(&cgblk, cgtod(&sblock, cg), sblock.fs_cgsize);
436 			if (!cg_chkmagic(cgp))
437 				pfatal("CG %d: BAD MAGIC NUMBER\n", cg);
438 			baseblk = dtogd(&sblock, i + j);
439 			for (k = 0; k < frags; k++) {
440 				setbmap(i + j + k);
441 				clrbit(cg_blksfree(cgp), baseblk + k);
442 			}
443 			n_blks += frags;
444 			if (frags == sblock.fs_frag)
445 				cgp->cg_cs.cs_nbfree--;
446 			else
447 				cgp->cg_cs.cs_nffree -= frags;
448 			cgdirty();
449 			return (i + j);
450 		}
451 	}
452 	return (0);
453 }
454 
455 /*
456  * Free a previously allocated block
457  */
458 void
459 freeblk(ufs2_daddr_t blkno, long frags)
460 {
461 	struct inodesc idesc;
462 
463 	idesc.id_blkno = blkno;
464 	idesc.id_numfrags = frags;
465 	(void)pass4check(&idesc);
466 }
467 
468 /*
469  * Find a pathname
470  */
471 void
472 getpathname(char *namebuf, ino_t curdir, ino_t ino)
473 {
474 	int len;
475 	char *cp;
476 	struct inodesc idesc;
477 	static int busy = 0;
478 
479 	if (curdir == ino && ino == ROOTINO) {
480 		(void)strcpy(namebuf, "/");
481 		return;
482 	}
483 	if (busy ||
484 	    (inoinfo(curdir)->ino_state != DSTATE &&
485 	     inoinfo(curdir)->ino_state != DFOUND)) {
486 		(void)strcpy(namebuf, "?");
487 		return;
488 	}
489 	busy = 1;
490 	memset(&idesc, 0, sizeof(struct inodesc));
491 	idesc.id_type = DATA;
492 	idesc.id_fix = IGNORE;
493 	cp = &namebuf[MAXPATHLEN - 1];
494 	*cp = '\0';
495 	if (curdir != ino) {
496 		idesc.id_parent = curdir;
497 		goto namelookup;
498 	}
499 	while (ino != ROOTINO) {
500 		idesc.id_number = ino;
501 		idesc.id_func = findino;
502 		idesc.id_name = strdup("..");
503 		if ((ckinode(ginode(ino), &idesc) & FOUND) == 0)
504 			break;
505 	namelookup:
506 		idesc.id_number = idesc.id_parent;
507 		idesc.id_parent = ino;
508 		idesc.id_func = findname;
509 		idesc.id_name = namebuf;
510 		if ((ckinode(ginode(idesc.id_number), &idesc)&FOUND) == 0)
511 			break;
512 		len = strlen(namebuf);
513 		cp -= len;
514 		memmove(cp, namebuf, (size_t)len);
515 		*--cp = '/';
516 		if (cp < &namebuf[MAXNAMLEN])
517 			break;
518 		ino = idesc.id_number;
519 	}
520 	busy = 0;
521 	if (ino != ROOTINO)
522 		*--cp = '?';
523 	memmove(namebuf, cp, (size_t)(&namebuf[MAXPATHLEN] - cp));
524 }
525 
526 void
527 catch(int sig __unused)
528 {
529 
530 	ckfini(0);
531 	exit(12);
532 }
533 
534 /*
535  * When preening, allow a single quit to signal
536  * a special exit after file system checks complete
537  * so that reboot sequence may be interrupted.
538  */
539 void
540 catchquit(int sig __unused)
541 {
542 	printf("returning to single-user after file system check\n");
543 	returntosingle = 1;
544 	(void)signal(SIGQUIT, SIG_DFL);
545 }
546 
547 /*
548  * determine whether an inode should be fixed.
549  */
550 int
551 dofix(struct inodesc *idesc, const char *msg)
552 {
553 
554 	switch (idesc->id_fix) {
555 
556 	case DONTKNOW:
557 		if (idesc->id_type == DATA)
558 			direrror(idesc->id_number, msg);
559 		else
560 			pwarn("%s", msg);
561 		if (preen) {
562 			printf(" (SALVAGED)\n");
563 			idesc->id_fix = FIX;
564 			return (ALTERED);
565 		}
566 		if (reply("SALVAGE") == 0) {
567 			idesc->id_fix = NOFIX;
568 			return (0);
569 		}
570 		idesc->id_fix = FIX;
571 		return (ALTERED);
572 
573 	case FIX:
574 		return (ALTERED);
575 
576 	case NOFIX:
577 	case IGNORE:
578 		return (0);
579 
580 	default:
581 		errx(EEXIT, "UNKNOWN INODESC FIX MODE %d", idesc->id_fix);
582 	}
583 	/* NOTREACHED */
584 	return (0);
585 }
586 
587 #include <stdarg.h>
588 
589 /*
590  * An unexpected inconsistency occured.
591  * Die if preening or file system is running with soft dependency protocol,
592  * otherwise just print message and continue.
593  */
594 void
595 pfatal(const char *fmt, ...)
596 {
597 	va_list ap;
598 	va_start(ap, fmt);
599 	if (!preen) {
600 		(void)vfprintf(stdout, fmt, ap);
601 		va_end(ap);
602 		if (usedsoftdep)
603 			(void)fprintf(stdout,
604 			    "\nUNEXPECTED SOFT UPDATE INCONSISTENCY\n");
605 		/*
606 		 * Force foreground fsck to clean up inconsistency.
607 		 */
608 		if (bkgrdflag) {
609 			cmd.value = FS_NEEDSFSCK;
610 			cmd.size = 1;
611 			if (sysctlbyname("vfs.ffs.setflags", 0, 0,
612 			    &cmd, sizeof cmd) == -1)
613 				pwarn("CANNOT SET FS_NEEDSFSCK FLAG\n");
614 			fprintf(stdout, "CANNOT RUN IN BACKGROUND\n");
615 			ckfini(0);
616 			exit(EEXIT);
617 		}
618 		return;
619 	}
620 	if (cdevname == NULL)
621 		cdevname = strdup("fsck");
622 	(void)fprintf(stdout, "%s: ", cdevname);
623 	(void)vfprintf(stdout, fmt, ap);
624 	(void)fprintf(stdout,
625 	    "\n%s: UNEXPECTED%sINCONSISTENCY; RUN fsck MANUALLY.\n",
626 	    cdevname, usedsoftdep ? " SOFT UPDATE " : " ");
627 	/*
628 	 * Force foreground fsck to clean up inconsistency.
629 	 */
630 	if (bkgrdflag) {
631 		cmd.value = FS_NEEDSFSCK;
632 		cmd.size = 1;
633 		if (sysctlbyname("vfs.ffs.setflags", 0, 0,
634 		    &cmd, sizeof cmd) == -1)
635 			pwarn("CANNOT SET FS_NEEDSFSCK FLAG\n");
636 	}
637 	ckfini(0);
638 	exit(EEXIT);
639 }
640 
641 /*
642  * Pwarn just prints a message when not preening or running soft dependency
643  * protocol, or a warning (preceded by filename) when preening.
644  */
645 void
646 pwarn(const char *fmt, ...)
647 {
648 	va_list ap;
649 	va_start(ap, fmt);
650 	if (preen)
651 		(void)fprintf(stdout, "%s: ", cdevname);
652 	(void)vfprintf(stdout, fmt, ap);
653 	va_end(ap);
654 }
655 
656 /*
657  * Stub for routines from kernel.
658  */
659 void
660 panic(const char *fmt, ...)
661 {
662 	va_list ap;
663 	va_start(ap, fmt);
664 	pfatal("INTERNAL INCONSISTENCY:");
665 	(void)vfprintf(stdout, fmt, ap);
666 	va_end(ap);
667 	exit(EEXIT);
668 }
669