xref: /openbsd-src/sbin/fsck_ffs/utilities.c (revision 5b133f3f277e80f096764111e64f3a1284acb179)
1*5b133f3fSguenther /*	$OpenBSD: utilities.c,v 1.55 2023/03/08 04:43:06 guenther Exp $	*/
287304b87Stholo /*	$NetBSD: utilities.c,v 1.18 1996/09/27 22:45:20 christos Exp $	*/
3df930be7Sderaadt 
4df930be7Sderaadt /*
5df930be7Sderaadt  * Copyright (c) 1980, 1986, 1993
6df930be7Sderaadt  *	The Regents of the University of California.  All rights reserved.
7df930be7Sderaadt  *
8df930be7Sderaadt  * Redistribution and use in source and binary forms, with or without
9df930be7Sderaadt  * modification, are permitted provided that the following conditions
10df930be7Sderaadt  * are met:
11df930be7Sderaadt  * 1. Redistributions of source code must retain the above copyright
12df930be7Sderaadt  *    notice, this list of conditions and the following disclaimer.
13df930be7Sderaadt  * 2. Redistributions in binary form must reproduce the above copyright
14df930be7Sderaadt  *    notice, this list of conditions and the following disclaimer in the
15df930be7Sderaadt  *    documentation and/or other materials provided with the distribution.
161ef0d710Smillert  * 3. Neither the name of the University nor the names of its contributors
17df930be7Sderaadt  *    may be used to endorse or promote products derived from this software
18df930be7Sderaadt  *    without specific prior written permission.
19df930be7Sderaadt  *
20df930be7Sderaadt  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21df930be7Sderaadt  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22df930be7Sderaadt  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23df930be7Sderaadt  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24df930be7Sderaadt  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25df930be7Sderaadt  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26df930be7Sderaadt  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27df930be7Sderaadt  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28df930be7Sderaadt  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29df930be7Sderaadt  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30df930be7Sderaadt  * SUCH DAMAGE.
31df930be7Sderaadt  */
32df930be7Sderaadt 
3378eb0b7eSderaadt #include <sys/param.h>	/* DEV_BSIZE isset setbit clrbit */
34df930be7Sderaadt #include <sys/time.h>
35af9e537cSd #include <sys/uio.h>
36df930be7Sderaadt #include <ufs/ufs/dinode.h>
37df930be7Sderaadt #include <ufs/ufs/dir.h>
38df930be7Sderaadt #include <ufs/ffs/fs.h>
3949f464a0Sotto #include <signal.h>
40df930be7Sderaadt #include <stdio.h>
41df930be7Sderaadt #include <stdlib.h>
42df930be7Sderaadt #include <string.h>
43df930be7Sderaadt #include <ctype.h>
44df930be7Sderaadt #include <unistd.h>
45b9fc9a72Sderaadt #include <limits.h>
46d4a55c7bSderaadt #include <errno.h>
47af9e537cSd #include <fcntl.h>
48af9e537cSd #include <paths.h>
49df930be7Sderaadt 
5087304b87Stholo #include "fsutil.h"
51df930be7Sderaadt #include "fsck.h"
52df930be7Sderaadt #include "extern.h"
53df930be7Sderaadt 
54df930be7Sderaadt long				diskreads, totalreads;	/* Disk cache statistics */
552062fcb1Sotto static struct bufarea		cgblk;			/* backup buffer for cylinder group blocks */
56df930be7Sderaadt 
571abdbfdeSderaadt static void rwerror(char *, daddr_t);
5887304b87Stholo 
59df930be7Sderaadt int
ftypeok(union dinode * dp)602fffe0e0Smillert ftypeok(union dinode *dp)
61df930be7Sderaadt {
622fffe0e0Smillert 	switch (DIP(dp, di_mode) & IFMT) {
63df930be7Sderaadt 	case IFDIR:
64df930be7Sderaadt 	case IFREG:
65df930be7Sderaadt 	case IFBLK:
66df930be7Sderaadt 	case IFCHR:
67df930be7Sderaadt 	case IFLNK:
68df930be7Sderaadt 	case IFSOCK:
69df930be7Sderaadt 	case IFIFO:
70df930be7Sderaadt 		return (1);
71df930be7Sderaadt 	default:
72df930be7Sderaadt 		if (debug)
732fffe0e0Smillert 			printf("bad file type 0%o\n", DIP(dp, di_mode));
74df930be7Sderaadt 		return (0);
75df930be7Sderaadt 	}
76df930be7Sderaadt }
77df930be7Sderaadt 
78df930be7Sderaadt int
reply(char * question)7960d6b16fSgluk reply(char *question)
80df930be7Sderaadt {
813883fb93Sderaadt 	int persevere, c;
82df930be7Sderaadt 
83df930be7Sderaadt 	if (preen)
84df930be7Sderaadt 		pfatal("INTERNAL ERROR: GOT TO reply()");
85df930be7Sderaadt 	persevere = !strcmp(question, "CONTINUE");
86df930be7Sderaadt 	printf("\n");
87df930be7Sderaadt 	if (!persevere && (nflag || fswritefd < 0)) {
88df930be7Sderaadt 		printf("%s? no\n\n", question);
89767b3369Sart 		resolved = 0;
90df930be7Sderaadt 		return (0);
91df930be7Sderaadt 	}
92df930be7Sderaadt 	if (yflag || (persevere && nflag)) {
93df930be7Sderaadt 		printf("%s? yes\n\n", question);
94df930be7Sderaadt 		return (1);
95df930be7Sderaadt 	}
963883fb93Sderaadt 
97df930be7Sderaadt 	do {
98daaa95b6Sderaadt 		printf("%s? [Fyn?] ", question);
99df930be7Sderaadt 		(void) fflush(stdout);
100df930be7Sderaadt 		c = getc(stdin);
101daaa95b6Sderaadt 		if (c == 'F') {
102daaa95b6Sderaadt 			yflag = 1;
103daaa95b6Sderaadt 			return (1);
104daaa95b6Sderaadt 		}
105767b3369Sart 		while (c != '\n' && getc(stdin) != '\n') {
106767b3369Sart 			if (feof(stdin)) {
107767b3369Sart 				resolved = 0;
108df930be7Sderaadt 				return (0);
109767b3369Sart 			}
110767b3369Sart 		}
111df930be7Sderaadt 	} while (c != 'y' && c != 'Y' && c != 'n' && c != 'N');
112df930be7Sderaadt 	printf("\n");
113df930be7Sderaadt 	if (c == 'y' || c == 'Y')
114df930be7Sderaadt 		return (1);
115767b3369Sart 	resolved = 0;
116df930be7Sderaadt 	return (0);
117df930be7Sderaadt }
118df930be7Sderaadt 
119df930be7Sderaadt /*
1204d91232bSotto  * Look up state information for an inode.
1214d91232bSotto  */
1224d91232bSotto struct inostat *
inoinfo(ino_t inum)1234d91232bSotto inoinfo(ino_t inum)
1244d91232bSotto {
1254d91232bSotto 	static struct inostat unallocated = { USTATE, 0, 0 };
1264d91232bSotto 	struct inostatlist *ilp;
1274d91232bSotto 	int iloff;
1284d91232bSotto 
1294d91232bSotto 	if (inum > maxino)
1303b92bd08Sderaadt 		errexit("inoinfo: inumber %llu out of range",
1313b92bd08Sderaadt 		    (unsigned long long)inum);
1324d91232bSotto 	ilp = &inostathead[inum / sblock.fs_ipg];
1334d91232bSotto 	iloff = inum % sblock.fs_ipg;
1344d91232bSotto 	if (iloff >= ilp->il_numalloced)
1354d91232bSotto 		return (&unallocated);
1364d91232bSotto 	return (&ilp->il_stat[iloff]);
1374d91232bSotto }
1384d91232bSotto 
1394d91232bSotto /*
140df930be7Sderaadt  * Malloc buffers and set up cache.
141df930be7Sderaadt  */
142df930be7Sderaadt void
bufinit(void)14360d6b16fSgluk bufinit(void)
144df930be7Sderaadt {
145e073c79dSmpech 	struct bufarea *bp;
146df930be7Sderaadt 	long bufcnt, i;
147df930be7Sderaadt 	char *bufp;
148df930be7Sderaadt 
14989203baaSkstailey 	pbp = pdirbp = NULL;
150df930be7Sderaadt 	bufp = malloc((unsigned int)sblock.fs_bsize);
151df930be7Sderaadt 	if (bufp == 0)
152df930be7Sderaadt 		errexit("cannot allocate buffer pool\n");
153df930be7Sderaadt 	cgblk.b_un.b_buf = bufp;
154df930be7Sderaadt 	initbarea(&cgblk);
155df930be7Sderaadt 	bufhead.b_next = bufhead.b_prev = &bufhead;
156df930be7Sderaadt 	bufcnt = MAXBUFSPACE / sblock.fs_bsize;
157df930be7Sderaadt 	if (bufcnt < MINBUFS)
158df930be7Sderaadt 		bufcnt = MINBUFS;
159df930be7Sderaadt 	for (i = 0; i < bufcnt; i++) {
16060d6b16fSgluk 		bp = malloc(sizeof(struct bufarea));
161df930be7Sderaadt 		bufp = malloc((unsigned int)sblock.fs_bsize);
162df930be7Sderaadt 		if (bp == NULL || bufp == NULL) {
163d9d929a5Sdhill 			free(bp);
164d9d929a5Sdhill 			free(bufp);
1654435eaddSderaadt 			if (i >= MINBUFS)
166df930be7Sderaadt 				break;
167df930be7Sderaadt 			errexit("cannot allocate buffer pool\n");
168df930be7Sderaadt 		}
169df930be7Sderaadt 		bp->b_un.b_buf = bufp;
170df930be7Sderaadt 		bp->b_prev = &bufhead;
171df930be7Sderaadt 		bp->b_next = bufhead.b_next;
172df930be7Sderaadt 		bufhead.b_next->b_prev = bp;
173df930be7Sderaadt 		bufhead.b_next = bp;
174df930be7Sderaadt 		initbarea(bp);
175df930be7Sderaadt 	}
176df930be7Sderaadt 	bufhead.b_size = i;	/* save number of buffers */
177df930be7Sderaadt }
178df930be7Sderaadt 
179df930be7Sderaadt /*
1802062fcb1Sotto  * Manage cylinder group buffers.
1812062fcb1Sotto  */
1822062fcb1Sotto static struct bufarea *cgbufs;	/* header for cylinder group cache */
1832062fcb1Sotto static int flushtries;		/* number of tries to reclaim memory */
1842062fcb1Sotto struct bufarea *
cglookup(u_int cg)1852062fcb1Sotto cglookup(u_int cg)
1862062fcb1Sotto {
1872062fcb1Sotto 	struct bufarea *cgbp;
1882062fcb1Sotto 	struct cg *cgp;
1892062fcb1Sotto 
1902062fcb1Sotto 	if (cgbufs == NULL) {
1912062fcb1Sotto 		cgbufs = calloc(sblock.fs_ncg, sizeof(struct bufarea));
1922062fcb1Sotto 		if (cgbufs == NULL)
1932062fcb1Sotto 			errexit("cannot allocate cylinder group buffers");
1942062fcb1Sotto 	}
1952062fcb1Sotto 	cgbp = &cgbufs[cg];
1962062fcb1Sotto 	if (cgbp->b_un.b_cg != NULL)
1972062fcb1Sotto 		return (cgbp);
1982062fcb1Sotto 	cgp = NULL;
1992062fcb1Sotto 	if (flushtries == 0)
2002062fcb1Sotto 		cgp = malloc((unsigned int)sblock.fs_cgsize);
2012062fcb1Sotto 	if (cgp == NULL) {
2022062fcb1Sotto 		getblk(&cgblk, cgtod(&sblock, cg), sblock.fs_cgsize);
2032062fcb1Sotto 		return (&cgblk);
2042062fcb1Sotto 	}
2052062fcb1Sotto 	cgbp->b_un.b_cg = cgp;
2062062fcb1Sotto 	initbarea(cgbp);
2072062fcb1Sotto 	getblk(cgbp, cgtod(&sblock, cg), sblock.fs_cgsize);
2082062fcb1Sotto 	return (cgbp);
2092062fcb1Sotto }
2102062fcb1Sotto 
2112062fcb1Sotto 
2122062fcb1Sotto /*
213df930be7Sderaadt  * Manage a cache of directory blocks.
214df930be7Sderaadt  */
215df930be7Sderaadt struct bufarea *
getdatablk(daddr_t blkno,long size)2161abdbfdeSderaadt getdatablk(daddr_t blkno, long size)
217df930be7Sderaadt {
218e073c79dSmpech 	struct bufarea *bp;
219df930be7Sderaadt 
220df930be7Sderaadt 	for (bp = bufhead.b_next; bp != &bufhead; bp = bp->b_next)
221df930be7Sderaadt 		if (bp->b_bno == fsbtodb(&sblock, blkno))
222df930be7Sderaadt 			goto foundit;
223df930be7Sderaadt 	for (bp = bufhead.b_prev; bp != &bufhead; bp = bp->b_prev)
224df930be7Sderaadt 		if ((bp->b_flags & B_INUSE) == 0)
225df930be7Sderaadt 			break;
226df930be7Sderaadt 	if (bp == &bufhead)
227df930be7Sderaadt 		errexit("deadlocked buffer pool\n");
228df930be7Sderaadt 	getblk(bp, blkno, size);
229898bdbabStedu 	/* FALLTHROUGH */
230df930be7Sderaadt foundit:
231df930be7Sderaadt 	totalreads++;
232df930be7Sderaadt 	bp->b_prev->b_next = bp->b_next;
233df930be7Sderaadt 	bp->b_next->b_prev = bp->b_prev;
234df930be7Sderaadt 	bp->b_prev = &bufhead;
235df930be7Sderaadt 	bp->b_next = bufhead.b_next;
236df930be7Sderaadt 	bufhead.b_next->b_prev = bp;
237df930be7Sderaadt 	bufhead.b_next = bp;
238df930be7Sderaadt 	bp->b_flags |= B_INUSE;
239df930be7Sderaadt 	return (bp);
240df930be7Sderaadt }
241df930be7Sderaadt 
242df930be7Sderaadt void
getblk(struct bufarea * bp,daddr_t blk,long size)2431abdbfdeSderaadt getblk(struct bufarea *bp, daddr_t blk, long size)
244df930be7Sderaadt {
2451abdbfdeSderaadt 	daddr_t dblk;
246df930be7Sderaadt 
247df930be7Sderaadt 	dblk = fsbtodb(&sblock, blk);
248df930be7Sderaadt 	if (bp->b_bno != dblk) {
249df930be7Sderaadt 		flush(fswritefd, bp);
250df930be7Sderaadt 		diskreads++;
251df930be7Sderaadt 		bp->b_errs = bread(fsreadfd, bp->b_un.b_buf, dblk, size);
252df930be7Sderaadt 		bp->b_bno = dblk;
253df930be7Sderaadt 		bp->b_size = size;
254df930be7Sderaadt 	}
255df930be7Sderaadt }
256df930be7Sderaadt 
257df930be7Sderaadt void
flush(int fd,struct bufarea * bp)25860d6b16fSgluk flush(int fd, struct bufarea *bp)
259df930be7Sderaadt {
260e073c79dSmpech 	int i, j;
261df930be7Sderaadt 
262df930be7Sderaadt 	if (!bp->b_dirty)
263df930be7Sderaadt 		return;
264df930be7Sderaadt 	if (bp->b_errs != 0)
2654f17ce5eSotto 		pfatal("WRITING %sZERO'ED BLOCK %lld TO DISK\n",
26617af71c1Skrw 		    (bp->b_errs == bp->b_size / DEV_BSIZE) ? "" : "PARTIALLY ",
267bb4f4faeSkrw 		    (long long)bp->b_bno);
268df930be7Sderaadt 	bp->b_dirty = 0;
269df930be7Sderaadt 	bp->b_errs = 0;
270df930be7Sderaadt 	bwrite(fd, bp->b_un.b_buf, bp->b_bno, (long)bp->b_size);
271df930be7Sderaadt 	if (bp != &sblk)
272df930be7Sderaadt 		return;
273df930be7Sderaadt 	for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) {
274b2e9ad3dSgluk 		bwrite(fswritefd, (char *)sblock.fs_csp + i,
2752fffe0e0Smillert 		    fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag),
276df930be7Sderaadt 		    sblock.fs_cssize - i < sblock.fs_bsize ?
277df930be7Sderaadt 		    sblock.fs_cssize - i : sblock.fs_bsize);
278df930be7Sderaadt 	}
279df930be7Sderaadt }
280df930be7Sderaadt 
28187304b87Stholo static void
rwerror(char * mesg,daddr_t blk)2821abdbfdeSderaadt rwerror(char *mesg, daddr_t blk)
283df930be7Sderaadt {
284df930be7Sderaadt 
285df930be7Sderaadt 	if (preen == 0)
286df930be7Sderaadt 		printf("\n");
287bb4f4faeSkrw 	pfatal("CANNOT %s: BLK %lld", mesg, (long long)blk);
288df930be7Sderaadt 	if (reply("CONTINUE") == 0)
289df930be7Sderaadt 		errexit("Program terminated\n");
290df930be7Sderaadt }
291df930be7Sderaadt 
292df930be7Sderaadt void
ckfini(int markclean)29360d6b16fSgluk ckfini(int markclean)
294df930be7Sderaadt {
295e073c79dSmpech 	struct bufarea *bp, *nbp;
296df930be7Sderaadt 	int cnt = 0;
29749f464a0Sotto 	sigset_t oset, nset;
2982fffe0e0Smillert 	int64_t sblockloc;
29949f464a0Sotto 
30049f464a0Sotto 	sigemptyset(&nset);
30149f464a0Sotto 	sigaddset(&nset, SIGINT);
30249f464a0Sotto 	sigprocmask(SIG_BLOCK, &nset, &oset);
303df930be7Sderaadt 
304df930be7Sderaadt 	if (fswritefd < 0) {
305df930be7Sderaadt 		(void)close(fsreadfd);
3063883fb93Sderaadt 		fsreadfd = -1;
30749f464a0Sotto 		sigprocmask(SIG_SETMASK, &oset, NULL);
308df930be7Sderaadt 		return;
309df930be7Sderaadt 	}
3102fffe0e0Smillert 	if (sblock.fs_magic == FS_UFS1_MAGIC) {
3112fffe0e0Smillert 		sblockloc = SBLOCK_UFS1;
3122fffe0e0Smillert 		sblock.fs_ffs1_time = sblock.fs_time;
3132fffe0e0Smillert 		sblock.fs_ffs1_size = sblock.fs_size;
3142fffe0e0Smillert 		sblock.fs_ffs1_dsize = sblock.fs_dsize;
3152fffe0e0Smillert 		sblock.fs_ffs1_csaddr = sblock.fs_csaddr;
3162fffe0e0Smillert 		sblock.fs_ffs1_cstotal.cs_ndir = sblock.fs_cstotal.cs_ndir;
3172fffe0e0Smillert 		sblock.fs_ffs1_cstotal.cs_nbfree = sblock.fs_cstotal.cs_nbfree;
3182fffe0e0Smillert 		sblock.fs_ffs1_cstotal.cs_nifree = sblock.fs_cstotal.cs_nifree;
3192fffe0e0Smillert 		sblock.fs_ffs1_cstotal.cs_nffree = sblock.fs_cstotal.cs_nffree;
320363b8579Spedro 		/* Force update on next mount */
321363b8579Spedro 		sblock.fs_ffs1_flags &= ~FS_FLAGS_UPDATED;
3222fffe0e0Smillert 	} else
3232fffe0e0Smillert 		sblockloc = SBLOCK_UFS2;
324df930be7Sderaadt 	flush(fswritefd, &sblk);
32517af71c1Skrw 	if (havesb && sblk.b_bno != sblockloc / DEV_BSIZE && !preen &&
3262fffe0e0Smillert 	    reply("UPDATE STANDARD SUPERBLOCK")) {
32717af71c1Skrw 		sblk.b_bno = sblockloc / DEV_BSIZE;
328df930be7Sderaadt 		sbdirty();
329df930be7Sderaadt 		flush(fswritefd, &sblk);
330df930be7Sderaadt 	}
331df930be7Sderaadt 	flush(fswritefd, &cgblk);
332df930be7Sderaadt 	free(cgblk.b_un.b_buf);
333df930be7Sderaadt 	for (bp = bufhead.b_prev; bp && bp != &bufhead; bp = nbp) {
334df930be7Sderaadt 		cnt++;
335df930be7Sderaadt 		flush(fswritefd, bp);
336df930be7Sderaadt 		nbp = bp->b_prev;
337df930be7Sderaadt 		free(bp->b_un.b_buf);
33860d6b16fSgluk 		free(bp);
339df930be7Sderaadt 	}
340df930be7Sderaadt 	if (bufhead.b_size != cnt)
341df930be7Sderaadt 		errexit("Panic: lost %d buffers\n", bufhead.b_size - cnt);
3422062fcb1Sotto 	if (cgbufs != NULL) {
3432062fcb1Sotto 		for (cnt = 0; cnt < sblock.fs_ncg; cnt++) {
3442062fcb1Sotto 			if (cgbufs[cnt].b_un.b_cg == NULL)
3452062fcb1Sotto 				continue;
3462062fcb1Sotto 			flush(fswritefd, &cgbufs[cnt]);
3472062fcb1Sotto 			free(cgbufs[cnt].b_un.b_cg);
3482062fcb1Sotto 		}
3492062fcb1Sotto 		free(cgbufs);
3502062fcb1Sotto 	}
35189203baaSkstailey 	pbp = pdirbp = NULL;
352df930be7Sderaadt 	if (markclean && (sblock.fs_clean & FS_ISCLEAN) == 0) {
353df930be7Sderaadt 		/*
354df930be7Sderaadt 		 * Mark the file system as clean, and sync the superblock.
355df930be7Sderaadt 		 */
356df930be7Sderaadt 		if (preen)
357df930be7Sderaadt 			pwarn("MARKING FILE SYSTEM CLEAN\n");
358df930be7Sderaadt 		else if (!reply("MARK FILE SYSTEM CLEAN"))
359df930be7Sderaadt 			markclean = 0;
360df930be7Sderaadt 		if (markclean) {
361df930be7Sderaadt 			sblock.fs_clean = FS_ISCLEAN;
362df930be7Sderaadt 			sbdirty();
363df930be7Sderaadt 			flush(fswritefd, &sblk);
364df930be7Sderaadt 		}
365df930be7Sderaadt 	}
366df930be7Sderaadt 	if (debug)
367df930be7Sderaadt 		printf("cache missed %ld of %ld (%d%%)\n", diskreads,
368df930be7Sderaadt 		    totalreads, (int)(diskreads * 100 / totalreads));
369df930be7Sderaadt 	(void)close(fsreadfd);
3703883fb93Sderaadt 	fsreadfd = -1;
371df930be7Sderaadt 	(void)close(fswritefd);
3723883fb93Sderaadt 	fswritefd = -1;
37349f464a0Sotto 	sigprocmask(SIG_SETMASK, &oset, NULL);
374df930be7Sderaadt }
375df930be7Sderaadt 
376df930be7Sderaadt int
bread(int fd,char * buf,daddr_t blk,long size)3771abdbfdeSderaadt bread(int fd, char *buf, daddr_t blk, long size)
378df930be7Sderaadt {
379df930be7Sderaadt 	char *cp;
380df930be7Sderaadt 	int i, errs;
381df930be7Sderaadt 	off_t offset;
382df930be7Sderaadt 
383df930be7Sderaadt 	offset = blk;
38417af71c1Skrw 	offset *= DEV_BSIZE;
3851593d282Skrw 	if (pread(fd, buf, size, offset) == size)
386df930be7Sderaadt 		return (0);
387df930be7Sderaadt 	rwerror("READ", blk);
388df930be7Sderaadt 	errs = 0;
389df930be7Sderaadt 	memset(buf, 0, (size_t)size);
390df930be7Sderaadt 	printf("THE FOLLOWING DISK SECTORS COULD NOT BE READ:");
391df930be7Sderaadt 	for (cp = buf, i = 0; i < size; i += secsize, cp += secsize) {
3921593d282Skrw 		if (pread(fd, cp, secsize, offset + i) != secsize) {
39317af71c1Skrw 			if (secsize != DEV_BSIZE)
3944f17ce5eSotto 				printf(" %lld (%lld),",
3955480ff59Skrw 				    (long long)(offset + i) / secsize,
3965480ff59Skrw 				    (long long)blk + i / DEV_BSIZE);
397df930be7Sderaadt 			else
3985480ff59Skrw 				printf(" %lld,", (long long)blk +
3995480ff59Skrw 				    i / DEV_BSIZE);
400df930be7Sderaadt 			errs++;
401df930be7Sderaadt 		}
402df930be7Sderaadt 	}
403df930be7Sderaadt 	printf("\n");
404df930be7Sderaadt 	return (errs);
405df930be7Sderaadt }
406df930be7Sderaadt 
407df930be7Sderaadt void
bwrite(int fd,char * buf,daddr_t blk,long size)4081abdbfdeSderaadt bwrite(int fd, char *buf, daddr_t blk, long size)
409df930be7Sderaadt {
410df930be7Sderaadt 	int i;
411df930be7Sderaadt 	char *cp;
412df930be7Sderaadt 	off_t offset;
413df930be7Sderaadt 
414df930be7Sderaadt 	if (fd < 0)
415df930be7Sderaadt 		return;
416df930be7Sderaadt 	offset = blk;
41717af71c1Skrw 	offset *= DEV_BSIZE;
4181593d282Skrw 	if (pwrite(fd, buf, size, offset) == size) {
419df930be7Sderaadt 		fsmodified = 1;
420df930be7Sderaadt 		return;
421df930be7Sderaadt 	}
422df930be7Sderaadt 	rwerror("WRITE", blk);
423df930be7Sderaadt 	printf("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:");
42417af71c1Skrw 	for (cp = buf, i = 0; i < size; i += secsize, cp += secsize)
4251593d282Skrw 		if (pwrite(fd, cp, secsize, offset + i) != secsize) {
42617af71c1Skrw 			if (secsize != DEV_BSIZE)
42717af71c1Skrw 				printf(" %lld (%lld),",
4285480ff59Skrw 				    (long long)(offset + i) / secsize,
4295480ff59Skrw 				    (long long)blk + i / DEV_BSIZE);
43017af71c1Skrw 			else
4315480ff59Skrw 				printf(" %lld,", (long long)blk +
4325480ff59Skrw 				    i / DEV_BSIZE);
433df930be7Sderaadt 		}
434df930be7Sderaadt 	printf("\n");
435df930be7Sderaadt 	return;
436df930be7Sderaadt }
437df930be7Sderaadt 
438df930be7Sderaadt /*
439df930be7Sderaadt  * allocate a data block with the specified number of fragments
440df930be7Sderaadt  */
4416a0422b3Sguenther daddr_t
allocblk(int frags)4426a0422b3Sguenther allocblk(int frags)
443df930be7Sderaadt {
4446a0422b3Sguenther 	daddr_t i, baseblk;
4456a0422b3Sguenther 	int j, k, cg;
4462062fcb1Sotto 	struct bufarea *cgbp;
4472062fcb1Sotto 	struct cg *cgp;
448df930be7Sderaadt 
449df930be7Sderaadt 	if (frags <= 0 || frags > sblock.fs_frag)
450df930be7Sderaadt 		return (0);
451df930be7Sderaadt 	for (i = 0; i < maxfsblock - sblock.fs_frag; i += sblock.fs_frag) {
452df930be7Sderaadt 		for (j = 0; j <= sblock.fs_frag - frags; j++) {
453df930be7Sderaadt 			if (testbmap(i + j))
454df930be7Sderaadt 				continue;
455df930be7Sderaadt 			for (k = 1; k < frags; k++)
456df930be7Sderaadt 				if (testbmap(i + j + k))
457df930be7Sderaadt 					break;
458df930be7Sderaadt 			if (k < frags) {
459df930be7Sderaadt 				j += k;
460df930be7Sderaadt 				continue;
461df930be7Sderaadt 			}
462767b3369Sart 			cg = dtog(&sblock, i + j);
4632062fcb1Sotto 			cgbp = cglookup(cg);
4642062fcb1Sotto 			cgp = cgbp->b_un.b_cg;
465767b3369Sart 			if (!cg_chkmagic(cgp))
466767b3369Sart 				pfatal("CG %d: BAD MAGIC NUMBER\n", cg);
467767b3369Sart 			baseblk = dtogd(&sblock, i + j);
468767b3369Sart 
469767b3369Sart 			for (k = 0; k < frags; k++) {
470df930be7Sderaadt 				setbmap(i + j + k);
471767b3369Sart 				clrbit(cg_blksfree(cgp), baseblk + k);
472767b3369Sart 			}
473df930be7Sderaadt 			n_blks += frags;
474767b3369Sart 			if (frags == sblock.fs_frag)
475767b3369Sart 				cgp->cg_cs.cs_nbfree--;
476767b3369Sart 			else
477767b3369Sart 				cgp->cg_cs.cs_nffree -= frags;
478df930be7Sderaadt 			return (i + j);
479df930be7Sderaadt 		}
480df930be7Sderaadt 	}
481df930be7Sderaadt 	return (0);
482df930be7Sderaadt }
483df930be7Sderaadt 
484df930be7Sderaadt /*
485df930be7Sderaadt  * Free a previously allocated block
486df930be7Sderaadt  */
487df930be7Sderaadt void
freeblk(daddr_t blkno,int frags)4886a0422b3Sguenther freeblk(daddr_t blkno, int frags)
489df930be7Sderaadt {
490df930be7Sderaadt 	struct inodesc idesc;
491df930be7Sderaadt 
492df930be7Sderaadt 	idesc.id_blkno = blkno;
493df930be7Sderaadt 	idesc.id_numfrags = frags;
494df930be7Sderaadt 	(void)pass4check(&idesc);
495df930be7Sderaadt }
496df930be7Sderaadt 
497df930be7Sderaadt /*
498df930be7Sderaadt  * Find a pathname
499df930be7Sderaadt  */
500df930be7Sderaadt void
getpathname(char * namebuf,size_t namebuflen,ino_t curdir,ino_t ino)50159b30be9Sderaadt getpathname(char *namebuf, size_t namebuflen, ino_t curdir, ino_t ino)
502df930be7Sderaadt {
503df930be7Sderaadt 	int len;
504e073c79dSmpech 	char *cp;
505df930be7Sderaadt 	struct inodesc idesc;
506df930be7Sderaadt 	static int busy = 0;
507df930be7Sderaadt 
508df930be7Sderaadt 	if (curdir == ino && ino == ROOTINO) {
50959b30be9Sderaadt 		(void)strlcpy(namebuf, "/", namebuflen);
510df930be7Sderaadt 		return;
511df930be7Sderaadt 	}
512df930be7Sderaadt 	if (busy ||
5134aab0ea5Sotto 	    (GET_ISTATE(curdir) != DSTATE && GET_ISTATE(curdir) != DFOUND)) {
51459b30be9Sderaadt 		(void)strlcpy(namebuf, "?", namebuflen);
515df930be7Sderaadt 		return;
516df930be7Sderaadt 	}
517df930be7Sderaadt 	busy = 1;
518df930be7Sderaadt 	memset(&idesc, 0, sizeof(struct inodesc));
519df930be7Sderaadt 	idesc.id_type = DATA;
520df930be7Sderaadt 	idesc.id_fix = IGNORE;
521b9fc9a72Sderaadt 	cp = &namebuf[PATH_MAX - 1];
522df930be7Sderaadt 	*cp = '\0';
523df930be7Sderaadt 	if (curdir != ino) {
524df930be7Sderaadt 		idesc.id_parent = curdir;
525df930be7Sderaadt 		goto namelookup;
526df930be7Sderaadt 	}
527df930be7Sderaadt 	while (ino != ROOTINO) {
528df930be7Sderaadt 		idesc.id_number = ino;
529df930be7Sderaadt 		idesc.id_func = findino;
530df930be7Sderaadt 		idesc.id_name = "..";
531df930be7Sderaadt 		if ((ckinode(ginode(ino), &idesc) & FOUND) == 0)
532df930be7Sderaadt 			break;
533df930be7Sderaadt 	namelookup:
534df930be7Sderaadt 		idesc.id_number = idesc.id_parent;
535df930be7Sderaadt 		idesc.id_parent = ino;
536df930be7Sderaadt 		idesc.id_func = findname;
537df930be7Sderaadt 		idesc.id_name = namebuf;
538df930be7Sderaadt 		if ((ckinode(ginode(idesc.id_number), &idesc)&FOUND) == 0)
539df930be7Sderaadt 			break;
540df930be7Sderaadt 		len = strlen(namebuf);
541df930be7Sderaadt 		cp -= len;
542b3f5508eSmillert 		memmove(cp, namebuf, (size_t)len);
543df930be7Sderaadt 		*--cp = '/';
544df930be7Sderaadt 		if (cp < &namebuf[MAXNAMLEN])
545df930be7Sderaadt 			break;
546df930be7Sderaadt 		ino = idesc.id_number;
547df930be7Sderaadt 	}
548df930be7Sderaadt 	busy = 0;
549df930be7Sderaadt 	if (ino != ROOTINO)
550df930be7Sderaadt 		*--cp = '?';
551b3f5508eSmillert 	memmove(namebuf, cp, (size_t)(&namebuf[PATH_MAX] - cp));
552df930be7Sderaadt }
553df930be7Sderaadt 
554df930be7Sderaadt void
catch(int signo)55519988433Sderaadt catch(int signo)
556df930be7Sderaadt {
5576d85caccSderaadt 	ckfini(0);			/* XXX signal race */
5586d85caccSderaadt 	_exit(12);
559df930be7Sderaadt }
560df930be7Sderaadt 
561df930be7Sderaadt /*
562df930be7Sderaadt  * When preening, allow a single quit to signal
563df930be7Sderaadt  * a special exit after filesystem checks complete
564df930be7Sderaadt  * so that reboot sequence may be interrupted.
565df930be7Sderaadt  */
566df930be7Sderaadt void
catchquit(int signo)56719988433Sderaadt catchquit(int signo)
568df930be7Sderaadt {
56942dc680dSderaadt 	extern volatile sig_atomic_t returntosingle;
5709d5ab9dfSguenther 	static const char message[] =
5719d5ab9dfSguenther 	    "returning to single-user after filesystem check\n";
572df930be7Sderaadt 
5739d5ab9dfSguenther 	write(STDOUT_FILENO, message, sizeof(message)-1);
574df930be7Sderaadt 	returntosingle = 1;
575df930be7Sderaadt 	(void)signal(SIGQUIT, SIG_DFL);
576df930be7Sderaadt }
577df930be7Sderaadt 
578df930be7Sderaadt /*
579df930be7Sderaadt  * Ignore a single quit signal; wait and flush just in case.
580df930be7Sderaadt  * Used by child processes in preen.
581df930be7Sderaadt  */
582df930be7Sderaadt void
voidquit(int signo)58319988433Sderaadt voidquit(int signo)
584df930be7Sderaadt {
58519988433Sderaadt 	int save_errno = errno;
586df930be7Sderaadt 
587df930be7Sderaadt 	sleep(1);
588df930be7Sderaadt 	(void)signal(SIGQUIT, SIG_IGN);
589df930be7Sderaadt 	(void)signal(SIGQUIT, SIG_DFL);
59019988433Sderaadt 	errno = save_errno;
591df930be7Sderaadt }
592df930be7Sderaadt 
593df930be7Sderaadt /*
594df930be7Sderaadt  * determine whether an inode should be fixed.
595df930be7Sderaadt  */
596df930be7Sderaadt int
dofix(struct inodesc * idesc,char * msg)59760d6b16fSgluk dofix(struct inodesc *idesc, char *msg)
598df930be7Sderaadt {
599df930be7Sderaadt 	switch (idesc->id_fix) {
600df930be7Sderaadt 
601df930be7Sderaadt 	case DONTKNOW:
602df930be7Sderaadt 		if (idesc->id_type == DATA)
603df930be7Sderaadt 			direrror(idesc->id_number, msg);
604df930be7Sderaadt 		else
6052cc28a79Saaron 			pwarn("%s", msg);
606df930be7Sderaadt 		if (preen) {
607df930be7Sderaadt 			printf(" (SALVAGED)\n");
608df930be7Sderaadt 			idesc->id_fix = FIX;
609df930be7Sderaadt 			return (ALTERED);
610df930be7Sderaadt 		}
611df930be7Sderaadt 		if (reply("SALVAGE") == 0) {
612df930be7Sderaadt 			idesc->id_fix = NOFIX;
613df930be7Sderaadt 			return (0);
614df930be7Sderaadt 		}
615df930be7Sderaadt 		idesc->id_fix = FIX;
616df930be7Sderaadt 		return (ALTERED);
617df930be7Sderaadt 
618df930be7Sderaadt 	case FIX:
619df930be7Sderaadt 		return (ALTERED);
620df930be7Sderaadt 
621df930be7Sderaadt 	case NOFIX:
622df930be7Sderaadt 	case IGNORE:
623df930be7Sderaadt 		return (0);
624df930be7Sderaadt 
625df930be7Sderaadt 	default:
626abbb3558Sotto 		errexit("UNKNOWN INODESC FIX MODE %u\n", idesc->id_fix);
627df930be7Sderaadt 	}
628df930be7Sderaadt 	/* NOTREACHED */
629df930be7Sderaadt }
630af9e537cSd 
631379d1969Sderaadt int (* info_fn)(char *, size_t) = NULL;
632af9e537cSd char *info_filesys = "?";
633af9e537cSd 
634af9e537cSd void
catchinfo(int signo)635379d1969Sderaadt catchinfo(int signo)
636af9e537cSd {
6373aa8caa7Sderaadt 	static int info_fd;
6383aa8caa7Sderaadt 	int save_errno = errno;
639af9e537cSd 	struct iovec iov[4];
6403883fb93Sderaadt 	char buf[1024];
641af9e537cSd 
6423aa8caa7Sderaadt 	if (signo == 0) {
6433aa8caa7Sderaadt 		info_fd = open(_PATH_TTY, O_WRONLY);
6443aa8caa7Sderaadt 		signal(SIGINFO, catchinfo);
6453aa8caa7Sderaadt 	} else if (info_fd > 0 && info_fn != NULL && info_fn(buf, sizeof buf)) {
646af9e537cSd 		iov[0].iov_base = info_filesys;
647af9e537cSd 		iov[0].iov_len = strlen(info_filesys);
648af9e537cSd 		iov[1].iov_base = ": ";
649af9e537cSd 		iov[1].iov_len = sizeof ": " - 1;
650af9e537cSd 		iov[2].iov_base = buf;
651af9e537cSd 		iov[2].iov_len = strlen(buf);
652af9e537cSd 		iov[3].iov_base = "\n";
653af9e537cSd 		iov[3].iov_len = sizeof "\n" - 1;
654af9e537cSd 
6553aa8caa7Sderaadt 		writev(info_fd, iov, 4);
656af9e537cSd 	}
657d4a55c7bSderaadt 	errno = save_errno;
658af9e537cSd }
6592062fcb1Sotto /*
6602062fcb1Sotto  * Attempt to flush a cylinder group cache entry.
6612062fcb1Sotto  * Return whether the flush was successful.
6622062fcb1Sotto  */
6632062fcb1Sotto static int
flushentry(void)6642062fcb1Sotto flushentry(void)
6652062fcb1Sotto {
6662062fcb1Sotto 	struct bufarea *cgbp;
6672062fcb1Sotto 
6682062fcb1Sotto 	if (flushtries == sblock.fs_ncg || cgbufs == NULL)
6692062fcb1Sotto 		return (0);
6702062fcb1Sotto 	cgbp = &cgbufs[flushtries++];
6712062fcb1Sotto 	if (cgbp->b_un.b_cg == NULL)
6722062fcb1Sotto 		return (0);
6732062fcb1Sotto 	flush(fswritefd, cgbp);
6742062fcb1Sotto 	free(cgbp->b_un.b_buf);
6752062fcb1Sotto 	cgbp->b_un.b_buf = NULL;
6762062fcb1Sotto 	return (1);
6772062fcb1Sotto }
6782062fcb1Sotto 
6792062fcb1Sotto /*
6802062fcb1Sotto  * Wrapper for malloc() that flushes the cylinder group cache to try
6812062fcb1Sotto  * to get space.
6822062fcb1Sotto  */
6832062fcb1Sotto void *
Malloc(size_t size)6842062fcb1Sotto Malloc(size_t size)
6852062fcb1Sotto {
6862062fcb1Sotto 	void *retval;
6872062fcb1Sotto 
6882062fcb1Sotto 	while ((retval = malloc(size)) == NULL)
6892062fcb1Sotto 		if (flushentry() == 0)
6902062fcb1Sotto 			break;
6912062fcb1Sotto 	return (retval);
6922062fcb1Sotto }
6932062fcb1Sotto 
6942062fcb1Sotto /*
6952062fcb1Sotto  * Wrapper for calloc() that flushes the cylinder group cache to try
6962062fcb1Sotto  * to get space.
6972062fcb1Sotto  */
6982062fcb1Sotto void*
Calloc(size_t cnt,size_t size)6992062fcb1Sotto Calloc(size_t cnt, size_t size)
7002062fcb1Sotto {
7012062fcb1Sotto 	void *retval;
7022062fcb1Sotto 
7032062fcb1Sotto 	while ((retval = calloc(cnt, size)) == NULL)
7042062fcb1Sotto 		if (flushentry() == 0)
7052062fcb1Sotto 			break;
7062062fcb1Sotto 	return (retval);
7072062fcb1Sotto }
7082062fcb1Sotto 
7092062fcb1Sotto /*
7102062fcb1Sotto  * Wrapper for reallocarray() that flushes the cylinder group cache to try
7112062fcb1Sotto  * to get space.
7122062fcb1Sotto  */
7132062fcb1Sotto void*
Reallocarray(void * p,size_t cnt,size_t size)7142062fcb1Sotto Reallocarray(void *p, size_t cnt, size_t size)
7152062fcb1Sotto {
7162062fcb1Sotto 	void *retval;
7172062fcb1Sotto 
7182062fcb1Sotto 	while ((retval = reallocarray(p, cnt, size)) == NULL)
7192062fcb1Sotto 		if (flushentry() == 0)
7202062fcb1Sotto 			break;
7212062fcb1Sotto 	return (retval);
7222062fcb1Sotto }
723