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