xref: /illumos-gate/usr/src/cmd/fs.d/ufs/fsck/utilities.c (revision bfbf29e2)
17c478bd9Sstevel@tonic-gate /*
223a1cceaSRoger A. Faulkner  * Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved.
348bbca81SDaniel Hoffman  * Copyright (c) 2016 by Delphix. All rights reserved.
47c478bd9Sstevel@tonic-gate  */
67c478bd9Sstevel@tonic-gate /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
7*bfbf29e2SToomas Soome /*	  All Rights Reserved	*/
97c478bd9Sstevel@tonic-gate /*
107c478bd9Sstevel@tonic-gate  * Copyright (c) 1980, 1986, 1990 The Regents of the University of California.
117c478bd9Sstevel@tonic-gate  * All rights reserved.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * Redistribution and use in source and binary forms are permitted
147c478bd9Sstevel@tonic-gate  * provided that: (1) source distributions retain this entire copyright
157c478bd9Sstevel@tonic-gate  * notice and comment, and (2) distributions including binaries display
167c478bd9Sstevel@tonic-gate  * the following acknowledgement:  ``This product includes software
177c478bd9Sstevel@tonic-gate  * developed by the University of California, Berkeley and its contributors''
187c478bd9Sstevel@tonic-gate  * in the documentation or other materials provided with the distribution
197c478bd9Sstevel@tonic-gate  * and in all advertising materials mentioning features or use of this
207c478bd9Sstevel@tonic-gate  * software. Neither the name of the University nor the names of its
217c478bd9Sstevel@tonic-gate  * contributors may be used to endorse or promote products derived
227c478bd9Sstevel@tonic-gate  * from this software without specific prior written permission.
267c478bd9Sstevel@tonic-gate  */
287c478bd9Sstevel@tonic-gate #include <stdio.h>
29355d6bb5Sswilcox #include <stdlib.h>
30355d6bb5Sswilcox #include <unistd.h>
31355d6bb5Sswilcox #include <stdarg.h>
32355d6bb5Sswilcox #include <libadm.h>
33355d6bb5Sswilcox #include <note.h>
347c478bd9Sstevel@tonic-gate #include <sys/param.h>
357c478bd9Sstevel@tonic-gate #include <sys/types.h>
367c478bd9Sstevel@tonic-gate #include <sys/mntent.h>
377c478bd9Sstevel@tonic-gate #include <sys/filio.h>
387c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_fs.h>
397c478bd9Sstevel@tonic-gate #include <sys/vnode.h>
407c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_acl.h>
41355d6bb5Sswilcox #include <sys/fs/ufs_inode.h>
42355d6bb5Sswilcox #include <sys/fs/ufs_log.h>
437c478bd9Sstevel@tonic-gate #define	_KERNEL
447c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_fsdir.h>
457c478bd9Sstevel@tonic-gate #undef _KERNEL
467c478bd9Sstevel@tonic-gate #include <sys/mnttab.h>
477c478bd9Sstevel@tonic-gate #include <sys/types.h>
487c478bd9Sstevel@tonic-gate #include <sys/stat.h>
49355d6bb5Sswilcox #include <fcntl.h>
50355d6bb5Sswilcox #include <signal.h>
517c478bd9Sstevel@tonic-gate #include <string.h>
527c478bd9Sstevel@tonic-gate #include <ctype.h>
537c478bd9Sstevel@tonic-gate #include <sys/vfstab.h>
547c478bd9Sstevel@tonic-gate #include <sys/lockfs.h>
557c478bd9Sstevel@tonic-gate #include <errno.h>
56355d6bb5Sswilcox #include <sys/cmn_err.h>
57355d6bb5Sswilcox #include <sys/dkio.h>
58355d6bb5Sswilcox #include <sys/vtoc.h>
59355d6bb5Sswilcox #include <sys/efi_partition.h>
60355d6bb5Sswilcox #include <fslib.h>
61355d6bb5Sswilcox #include <inttypes.h>
62355d6bb5Sswilcox #include "fsck.h"
64*bfbf29e2SToomas Soome struct bufarea *pbp;
65*bfbf29e2SToomas Soome struct bufarea *pdirbp;
66355d6bb5Sswilcox caddr_t mount_point = NULL;
67*bfbf29e2SToomas Soome static struct bufarea bufhead;	/* head of list of other blks in filesys */
68*bfbf29e2SToomas Soome char *elock_combuf;
69*bfbf29e2SToomas Soome char *elock_mountp;
70*bfbf29e2SToomas Soome static struct lockfs *lfp;		/* current lockfs status */
72355d6bb5Sswilcox static int64_t diskreads, totalreads;	/* Disk cache statistics */
74355d6bb5Sswilcox static int log_checksum(int32_t *, int32_t *, int);
75355d6bb5Sswilcox static void vdirerror(fsck_ino_t, caddr_t, va_list);
76355d6bb5Sswilcox static struct mnttab *search_mnttab(caddr_t, caddr_t, caddr_t, size_t);
77355d6bb5Sswilcox static struct vfstab *search_vfstab(caddr_t, caddr_t, caddr_t, size_t);
78355d6bb5Sswilcox static void vpwarn(caddr_t, va_list);
7923a1cceaSRoger A. Faulkner static int getaline(FILE *, caddr_t, int);
80355d6bb5Sswilcox static struct bufarea *alloc_bufarea(void);
81355d6bb5Sswilcox static void rwerror(caddr_t, diskaddr_t, int rval);
82355d6bb5Sswilcox static void debugclean(void);
83355d6bb5Sswilcox static void report_io_prob(caddr_t, diskaddr_t, size_t, ssize_t);
84355d6bb5Sswilcox static void freelogblk(daddr32_t);
85355d6bb5Sswilcox static void verrexit(caddr_t, va_list);
86355d6bb5Sswilcox static void vpfatal(caddr_t, va_list);
87355d6bb5Sswilcox static diskaddr_t get_device_size(int, caddr_t);
88355d6bb5Sswilcox static diskaddr_t brute_force_get_device_size(int);
89355d6bb5Sswilcox static void cg_constants(int, daddr32_t *, daddr32_t *, daddr32_t *,
90355d6bb5Sswilcox 	    daddr32_t *, daddr32_t *, daddr32_t *);
92355d6bb5Sswilcox int
ftypeok(struct dinode * dp)93355d6bb5Sswilcox ftypeok(struct dinode *dp)
947c478bd9Sstevel@tonic-gate {
957c478bd9Sstevel@tonic-gate 	switch (dp->di_mode & IFMT) {
977c478bd9Sstevel@tonic-gate 	case IFDIR:
987c478bd9Sstevel@tonic-gate 	case IFREG:
997c478bd9Sstevel@tonic-gate 	case IFBLK:
1007c478bd9Sstevel@tonic-gate 	case IFCHR:
1017c478bd9Sstevel@tonic-gate 	case IFLNK:
1027c478bd9Sstevel@tonic-gate 	case IFSOCK:
1037c478bd9Sstevel@tonic-gate 	case IFIFO:
1047c478bd9Sstevel@tonic-gate 	case IFSHAD:
1057c478bd9Sstevel@tonic-gate 	case IFATTRDIR:
1067c478bd9Sstevel@tonic-gate 		return (1);
1087c478bd9Sstevel@tonic-gate 	default:
1097c478bd9Sstevel@tonic-gate 		if (debug)
110355d6bb5Sswilcox 			(void) printf("bad file type 0%o\n", dp->di_mode);
1117c478bd9Sstevel@tonic-gate 		return (0);
1127c478bd9Sstevel@tonic-gate 	}
1137c478bd9Sstevel@tonic-gate }
115355d6bb5Sswilcox int
acltypeok(struct dinode * dp)116355d6bb5Sswilcox acltypeok(struct dinode *dp)
1177c478bd9Sstevel@tonic-gate {
1187c478bd9Sstevel@tonic-gate 	if (CHECK_ACL_ALLOWED(dp->di_mode & IFMT))
1197c478bd9Sstevel@tonic-gate 		return (1);
1217c478bd9Sstevel@tonic-gate 	if (debug)
122355d6bb5Sswilcox 		(void) printf("bad file type for acl I=%d: 0%o\n",
123355d6bb5Sswilcox 		    dp->di_shadow, dp->di_mode);
1247c478bd9Sstevel@tonic-gate 	return (0);
1257c478bd9Sstevel@tonic-gate }
127355d6bb5Sswilcox NOTE(PRINTFLIKE(1))
128355d6bb5Sswilcox int
reply(caddr_t fmt,...)129355d6bb5Sswilcox reply(caddr_t fmt, ...)
1307c478bd9Sstevel@tonic-gate {
131355d6bb5Sswilcox 	va_list ap;
1327c478bd9Sstevel@tonic-gate 	char line[80];
1347c478bd9Sstevel@tonic-gate 	if (preen)
135355d6bb5Sswilcox 		pfatal("INTERNAL ERROR: GOT TO reply() in preen mode");
1377c478bd9Sstevel@tonic-gate 	if (mflag) {
138355d6bb5Sswilcox 		/*
139355d6bb5Sswilcox 		 * We don't know what's going on, so don't potentially
140355d6bb5Sswilcox 		 * make things worse by having errexit() write stuff
141355d6bb5Sswilcox 		 * out to disk.
142355d6bb5Sswilcox 		 */
143355d6bb5Sswilcox 		(void) printf(
144355d6bb5Sswilcox 		    "\n%s: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY.\n",
145355d6bb5Sswilcox 		    devname);
146355d6bb5Sswilcox 		exit(EXERRFATAL);
1477c478bd9Sstevel@tonic-gate 	}
149355d6bb5Sswilcox 	va_start(ap, fmt);
150355d6bb5Sswilcox 	(void) putchar('\n');
151355d6bb5Sswilcox 	(void) vprintf(fmt, ap);
152355d6bb5Sswilcox 	(void) putchar('?');
153355d6bb5Sswilcox 	(void) putchar(' ');
154355d6bb5Sswilcox 	va_end(ap);
1567c478bd9Sstevel@tonic-gate 	if (nflag || fswritefd < 0) {
157355d6bb5Sswilcox 		(void) printf(" no\n\n");
1587c478bd9Sstevel@tonic-gate 		return (0);
1597c478bd9Sstevel@tonic-gate 	}
1607c478bd9Sstevel@tonic-gate 	if (yflag) {
161355d6bb5Sswilcox 		(void) printf(" yes\n\n");
1627c478bd9Sstevel@tonic-gate 		return (1);
1637c478bd9Sstevel@tonic-gate 	}
164355d6bb5Sswilcox 	(void) fflush(stdout);
16523a1cceaSRoger A. Faulkner 	if (getaline(stdin, line, sizeof (line)) == EOF)
1667c478bd9Sstevel@tonic-gate 		errexit("\n");
167355d6bb5Sswilcox 	(void) printf("\n");
168355d6bb5Sswilcox 	if (line[0] == 'y' || line[0] == 'Y') {
1697c478bd9Sstevel@tonic-gate 		return (1);
170355d6bb5Sswilcox 	} else {
1717c478bd9Sstevel@tonic-gate 		return (0);
1727c478bd9Sstevel@tonic-gate 	}
1737c478bd9Sstevel@tonic-gate }
175355d6bb5Sswilcox int
getaline(FILE * fp,caddr_t loc,int maxlen)17623a1cceaSRoger A. Faulkner getaline(FILE *fp, caddr_t loc, int maxlen)
1777c478bd9Sstevel@tonic-gate {
1787c478bd9Sstevel@tonic-gate 	int n;
179355d6bb5Sswilcox 	caddr_t p, lastloc;
1817c478bd9Sstevel@tonic-gate 	p = loc;
1827c478bd9Sstevel@tonic-gate 	lastloc = &p[maxlen-1];
1837c478bd9Sstevel@tonic-gate 	while ((n = getc(fp)) != '\n') {
1847c478bd9Sstevel@tonic-gate 		if (n == EOF)
1857c478bd9Sstevel@tonic-gate 			return (EOF);
1867c478bd9Sstevel@tonic-gate 		if (!isspace(n) && p < lastloc)
187355d6bb5Sswilcox 			*p++ = (char)n;
1887c478bd9Sstevel@tonic-gate 	}
189355d6bb5Sswilcox 	*p = '\0';
190355d6bb5Sswilcox 	/* LINTED pointer difference won't overflow */
1917c478bd9Sstevel@tonic-gate 	return (p - loc);
1927c478bd9Sstevel@tonic-gate }
1947c478bd9Sstevel@tonic-gate /*
1957c478bd9Sstevel@tonic-gate  * Malloc buffers and set up cache.
1967c478bd9Sstevel@tonic-gate  */
197355d6bb5Sswilcox void
bufinit(void)198355d6bb5Sswilcox bufinit(void)
1997c478bd9Sstevel@tonic-gate {
2007c478bd9Sstevel@tonic-gate 	struct bufarea *bp;
2017c478bd9Sstevel@tonic-gate 	int bufcnt, i;
202355d6bb5Sswilcox 	caddr_t bufp;
204355d6bb5Sswilcox 	bufp = malloc((size_t)sblock.fs_bsize);
205355d6bb5Sswilcox 	if (bufp == NULL)
206355d6bb5Sswilcox 		goto nomem;
2077c478bd9Sstevel@tonic-gate 	initbarea(&cgblk);
208355d6bb5Sswilcox 	cgblk.b_un.b_buf = bufp;
2097c478bd9Sstevel@tonic-gate 	bufhead.b_next = bufhead.b_prev = &bufhead;
2107c478bd9Sstevel@tonic-gate 	bufcnt = MAXBUFSPACE / sblock.fs_bsize;
2117c478bd9Sstevel@tonic-gate 	if (bufcnt < MINBUFS)
2127c478bd9Sstevel@tonic-gate 		bufcnt = MINBUFS;
2137c478bd9Sstevel@tonic-gate 	for (i = 0; i < bufcnt; i++) {
2147c478bd9Sstevel@tonic-gate 		bp = (struct bufarea *)malloc(sizeof (struct bufarea));
215355d6bb5Sswilcox 		if (bp == NULL) {
2167c478bd9Sstevel@tonic-gate 			if (i >= MINBUFS)
217355d6bb5Sswilcox 				goto noalloc;
218355d6bb5Sswilcox 			goto nomem;
219355d6bb5Sswilcox 		}
221355d6bb5Sswilcox 		bufp = malloc((size_t)sblock.fs_bsize);
222355d6bb5Sswilcox 		if (bufp == NULL) {
223355d6bb5Sswilcox 			free((void *)bp);
224355d6bb5Sswilcox 			if (i >= MINBUFS)
225355d6bb5Sswilcox 				goto noalloc;
226355d6bb5Sswilcox 			goto nomem;
2277c478bd9Sstevel@tonic-gate 		}
228355d6bb5Sswilcox 		initbarea(bp);
2297c478bd9Sstevel@tonic-gate 		bp->b_un.b_buf = bufp;
2307c478bd9Sstevel@tonic-gate 		bp->b_prev = &bufhead;
2317c478bd9Sstevel@tonic-gate 		bp->b_next = bufhead.b_next;
2327c478bd9Sstevel@tonic-gate 		bufhead.b_next->b_prev = bp;
2337c478bd9Sstevel@tonic-gate 		bufhead.b_next = bp;
2347c478bd9Sstevel@tonic-gate 	}
235355d6bb5Sswilcox noalloc:
2367c478bd9Sstevel@tonic-gate 	bufhead.b_size = i;	/* save number of buffers */
2377c478bd9Sstevel@tonic-gate 	pbp = pdirbp = NULL;
238355d6bb5Sswilcox 	return;
240355d6bb5Sswilcox nomem:
241355d6bb5Sswilcox 	errexit("cannot allocate buffer pool\n");
242355d6bb5Sswilcox 	/* NOTREACHED */
243355d6bb5Sswilcox }
245355d6bb5Sswilcox /*
246355d6bb5Sswilcox  * Undo a bufinit().
247355d6bb5Sswilcox  */
248355d6bb5Sswilcox void
unbufinit(void)249355d6bb5Sswilcox unbufinit(void)
250355d6bb5Sswilcox {
251355d6bb5Sswilcox 	int cnt;
252355d6bb5Sswilcox 	struct bufarea *bp, *nbp;
254355d6bb5Sswilcox 	cnt = 0;
255355d6bb5Sswilcox 	for (bp = bufhead.b_prev; bp != NULL && bp != &bufhead; bp = nbp) {
256355d6bb5Sswilcox 		cnt++;
257355d6bb5Sswilcox 		flush(fswritefd, bp);
258355d6bb5Sswilcox 		nbp = bp->b_prev;
259355d6bb5Sswilcox 		/*
260355d6bb5Sswilcox 		 * We're discarding the entire chain, so this isn't
261355d6bb5Sswilcox 		 * technically necessary.  However, it doesn't hurt
262355d6bb5Sswilcox 		 * and lint's data flow analysis is much happier
263355d6bb5Sswilcox 		 * (this prevents it from thinking there's a chance
264355d6bb5Sswilcox 		 * of our using memory elsewhere after it's been released).
265355d6bb5Sswilcox 		 */
266355d6bb5Sswilcox 		nbp->b_next = bp->b_next;
267355d6bb5Sswilcox 		bp->b_next->b_prev = nbp;
268355d6bb5Sswilcox 		free((void *)bp->b_un.b_buf);
269355d6bb5Sswilcox 		free((void *)bp);
270355d6bb5Sswilcox 	}
272355d6bb5Sswilcox 	if (bufhead.b_size != cnt)
273355d6bb5Sswilcox 		errexit("Panic: cache lost %d buffers\n",
2741493b746SMilan Cermak 		    bufhead.b_size - cnt);
2757c478bd9Sstevel@tonic-gate }
2777c478bd9Sstevel@tonic-gate /*
2787c478bd9Sstevel@tonic-gate  * Manage a cache of directory blocks.
2797c478bd9Sstevel@tonic-gate  */
2807c478bd9Sstevel@tonic-gate struct bufarea *
getdatablk(daddr32_t blkno,size_t size)281355d6bb5Sswilcox getdatablk(daddr32_t blkno, size_t size)
2827c478bd9Sstevel@tonic-gate {
2837c478bd9Sstevel@tonic-gate 	struct bufarea *bp;
2857c478bd9Sstevel@tonic-gate 	for (bp = bufhead.b_next; bp != &bufhead; bp = bp->b_next)
286355d6bb5Sswilcox 		if (bp->b_bno == fsbtodb(&sblock, blkno)) {
2877c478bd9Sstevel@tonic-gate 			goto foundit;
288355d6bb5Sswilcox 		}
2897c478bd9Sstevel@tonic-gate 	for (bp = bufhead.b_prev; bp != &bufhead; bp = bp->b_prev)
2907c478bd9Sstevel@tonic-gate 		if ((bp->b_flags & B_INUSE) == 0)
2917c478bd9Sstevel@tonic-gate 			break;
2927c478bd9Sstevel@tonic-gate 	if (bp == &bufhead) {
2937c478bd9Sstevel@tonic-gate 		bp = alloc_bufarea();
294355d6bb5Sswilcox 		if (bp == NULL) {
2957c478bd9Sstevel@tonic-gate 			errexit("deadlocked buffer pool\n");
296355d6bb5Sswilcox 			/* NOTREACHED */
297355d6bb5Sswilcox 		}
2987c478bd9Sstevel@tonic-gate 	}
299355d6bb5Sswilcox 	/*
300355d6bb5Sswilcox 	 * We're at the same logical level as getblk(), so if there
301355d6bb5Sswilcox 	 * are any errors, we'll let our caller handle them.
302355d6bb5Sswilcox 	 */
303355d6bb5Sswilcox 	diskreads++;
304355d6bb5Sswilcox 	(void) getblk(bp, blkno, size);
3067c478bd9Sstevel@tonic-gate foundit:
3077c478bd9Sstevel@tonic-gate 	totalreads++;
3087c478bd9Sstevel@tonic-gate 	bp->b_cnt++;
3097c478bd9Sstevel@tonic-gate 	/*
310355d6bb5Sswilcox 	 * Move the buffer to head of linked list if it isn't
3117c478bd9Sstevel@tonic-gate 	 * already there.
3127c478bd9Sstevel@tonic-gate 	 */
3137c478bd9Sstevel@tonic-gate 	if (bufhead.b_next != bp) {
3147c478bd9Sstevel@tonic-gate 		bp->b_prev->b_next = bp->b_next;
3157c478bd9Sstevel@tonic-gate 		bp->b_next->b_prev = bp->b_prev;
3167c478bd9Sstevel@tonic-gate 		bp->b_prev = &bufhead;
3177c478bd9Sstevel@tonic-gate 		bp->b_next = bufhead.b_next;
3187c478bd9Sstevel@tonic-gate 		bufhead.b_next->b_prev = bp;
3197c478bd9Sstevel@tonic-gate 		bufhead.b_next = bp;
3207c478bd9Sstevel@tonic-gate 	}
3217c478bd9Sstevel@tonic-gate 	bp->b_flags |= B_INUSE;
3227c478bd9Sstevel@tonic-gate 	return (bp);
3237c478bd9Sstevel@tonic-gate }
325355d6bb5Sswilcox void
brelse(struct bufarea * bp)3267c478bd9Sstevel@tonic-gate brelse(struct bufarea *bp)
3277c478bd9Sstevel@tonic-gate {
3287c478bd9Sstevel@tonic-gate 	bp->b_cnt--;
3297c478bd9Sstevel@tonic-gate 	if (bp->b_cnt == 0) {
3307c478bd9Sstevel@tonic-gate 		bp->b_flags &= ~B_INUSE;
3317c478bd9Sstevel@tonic-gate 	}
3327c478bd9Sstevel@tonic-gate }
3347c478bd9Sstevel@tonic-gate struct bufarea *
getblk(struct bufarea * bp,daddr32_t blk,size_t size)335355d6bb5Sswilcox getblk(struct bufarea *bp, daddr32_t blk, size_t size)
3367c478bd9Sstevel@tonic-gate {
3377c478bd9Sstevel@tonic-gate 	diskaddr_t dblk;
3397c478bd9Sstevel@tonic-gate 	dblk = fsbtodb(&sblock, blk);
3407c478bd9Sstevel@tonic-gate 	if (bp->b_bno == dblk)
3417c478bd9Sstevel@tonic-gate 		return (bp);
3427c478bd9Sstevel@tonic-gate 	flush(fswritefd, bp);
343355d6bb5Sswilcox 	bp->b_errs = fsck_bread(fsreadfd, bp->b_un.b_buf, dblk, size);
3447c478bd9Sstevel@tonic-gate 	bp->b_bno = dblk;
3457c478bd9Sstevel@tonic-gate 	bp->b_size = size;
3467c478bd9Sstevel@tonic-gate 	return (bp);
3477c478bd9Sstevel@tonic-gate }
349355d6bb5Sswilcox void
flush(int fd,struct bufarea * bp)350355d6bb5Sswilcox flush(int fd, struct bufarea *bp)
3517c478bd9Sstevel@tonic-gate {
3527c478bd9Sstevel@tonic-gate 	int i, j;
3537c478bd9Sstevel@tonic-gate 	caddr_t sip;
3547c478bd9Sstevel@tonic-gate 	long size;
3567c478bd9Sstevel@tonic-gate 	if (!bp->b_dirty)
3577c478bd9Sstevel@tonic-gate 		return;
359355d6bb5Sswilcox 	/*
360355d6bb5Sswilcox 	 * It's not our buf, so if there are errors, let whoever
361355d6bb5Sswilcox 	 * acquired it deal with the actual problem.
362355d6bb5Sswilcox 	 */
3637c478bd9Sstevel@tonic-gate 	if (bp->b_errs != 0)
3647c478bd9Sstevel@tonic-gate 		pfatal("WRITING ZERO'ED BLOCK %lld TO DISK\n", bp->b_bno);
3657c478bd9Sstevel@tonic-gate 	bp->b_dirty = 0;
3667c478bd9Sstevel@tonic-gate 	bp->b_errs = 0;
3677c478bd9Sstevel@tonic-gate 	bwrite(fd, bp->b_un.b_buf, bp->b_bno, (long)bp->b_size);
368355d6bb5Sswilcox 	if (bp != &sblk) {
3697c478bd9Sstevel@tonic-gate 		return;
370355d6bb5Sswilcox 	}
372355d6bb5Sswilcox 	/*
373355d6bb5Sswilcox 	 * We're flushing the superblock, so make sure all the
374355d6bb5Sswilcox 	 * ancillary bits go out as well.
375355d6bb5Sswilcox 	 */
3767c478bd9Sstevel@tonic-gate 	sip = (caddr_t)sblock.fs_u.fs_csp;
3777c478bd9Sstevel@tonic-gate 	for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) {
3787c478bd9Sstevel@tonic-gate 		size = sblock.fs_cssize - i < sblock.fs_bsize ?
3797c478bd9Sstevel@tonic-gate 		    sblock.fs_cssize - i : sblock.fs_bsize;
3807c478bd9Sstevel@tonic-gate 		bwrite(fswritefd, sip,
3817c478bd9Sstevel@tonic-gate 		    fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag),
3827c478bd9Sstevel@tonic-gate 		    size);
3837c478bd9Sstevel@tonic-gate 		sip += size;
3847c478bd9Sstevel@tonic-gate 	}
3857c478bd9Sstevel@tonic-gate }
387355d6bb5Sswilcox static void
rwerror(caddr_t mesg,diskaddr_t blk,int rval)388355d6bb5Sswilcox rwerror(caddr_t mesg, diskaddr_t blk, int rval)
3897c478bd9Sstevel@tonic-gate {
390355d6bb5Sswilcox 	int olderr = errno;
392355d6bb5Sswilcox 	if (!preen)
393355d6bb5Sswilcox 		(void) printf("\n");
395355d6bb5Sswilcox 	if (rval == -1)
396355d6bb5Sswilcox 		pfatal("CANNOT %s: DISK BLOCK %lld: %s",
397355d6bb5Sswilcox 		    mesg, blk, strerror(olderr));
398355d6bb5Sswilcox 	else
399355d6bb5Sswilcox 		pfatal("CANNOT %s: DISK BLOCK %lld", mesg, blk);
401355d6bb5Sswilcox 	if (reply("CONTINUE") == 0) {
402355d6bb5Sswilcox 		exitstat = EXERRFATAL;
4037c478bd9Sstevel@tonic-gate 		errexit("Program terminated\n");
404355d6bb5Sswilcox 	}
4057c478bd9Sstevel@tonic-gate }
407355d6bb5Sswilcox void
ckfini(void)408355d6bb5Sswilcox ckfini(void)
4097c478bd9Sstevel@tonic-gate {
410355d6bb5Sswilcox 	int64_t percentage;
412355d6bb5Sswilcox 	if (fswritefd < 0)
413355d6bb5Sswilcox 		return;
415355d6bb5Sswilcox 	flush(fswritefd, &sblk);
4167c478bd9Sstevel@tonic-gate 	/*
417355d6bb5Sswilcox 	 * Were we using a backup superblock?
4187c478bd9Sstevel@tonic-gate 	 */
4197c478bd9Sstevel@tonic-gate 	if (havesb && sblk.b_bno != SBOFF / dev_bsize) {
420355d6bb5Sswilcox 		if (preen || reply("UPDATE STANDARD SUPERBLOCK") == 1) {
421355d6bb5Sswilcox 			sblk.b_bno = SBOFF / dev_bsize;
422355d6bb5Sswilcox 			sbdirty();
423355d6bb5Sswilcox 			flush(fswritefd, &sblk);
424355d6bb5Sswilcox 		}
4257c478bd9Sstevel@tonic-gate 	}
4267c478bd9Sstevel@tonic-gate 	flush(fswritefd, &cgblk);
427355d6bb5Sswilcox 	if (cgblk.b_un.b_buf != NULL) {
428355d6bb5Sswilcox 		free((void *)cgblk.b_un.b_buf);
4297c478bd9Sstevel@tonic-gate 		cgblk.b_un.b_buf = NULL;
4307c478bd9Sstevel@tonic-gate 	}
431355d6bb5Sswilcox 	unbufinit();
432355d6bb5Sswilcox 	pbp = NULL;
433355d6bb5Sswilcox 	pdirbp = NULL;
434355d6bb5Sswilcox 	if (debug) {
435355d6bb5Sswilcox 		/*
436355d6bb5Sswilcox 		 * Note that we only count cache-related reads.
437355d6bb5Sswilcox 		 * Anything that called fsck_bread() or getblk()
438355d6bb5Sswilcox 		 * directly are explicitly not cached, so they're not
439355d6bb5Sswilcox 		 * included here.
440355d6bb5Sswilcox 		 */
441355d6bb5Sswilcox 		if (totalreads != 0)
442355d6bb5Sswilcox 			percentage = diskreads * 100 / totalreads;
443355d6bb5Sswilcox 		else
444355d6bb5Sswilcox 			percentage = 0;
446355d6bb5Sswilcox 		(void) printf("cache missed %lld of %lld reads (%lld%%)\n",
447355d6bb5Sswilcox 		    (longlong_t)diskreads, (longlong_t)totalreads,
448355d6bb5Sswilcox 		    (longlong_t)percentage);
4497c478bd9Sstevel@tonic-gate 	}
4517c478bd9Sstevel@tonic-gate 	(void) close(fsreadfd);
4527c478bd9Sstevel@tonic-gate 	(void) close(fswritefd);
453355d6bb5Sswilcox 	fsreadfd = -1;
454355d6bb5Sswilcox 	fswritefd = -1;
4557c478bd9Sstevel@tonic-gate }
457355d6bb5Sswilcox int
fsck_bread(int fd,caddr_t buf,diskaddr_t blk,size_t size)458355d6bb5Sswilcox fsck_bread(int fd, caddr_t buf, diskaddr_t blk, size_t size)
4597c478bd9Sstevel@tonic-gate {
460355d6bb5Sswilcox 	caddr_t cp;
461355d6bb5Sswilcox 	int i;
4627c478bd9Sstevel@tonic-gate 	int errs;
4637c478bd9Sstevel@tonic-gate 	offset_t offset = ldbtob(blk);
4647c478bd9Sstevel@tonic-gate 	offset_t addr;
466355d6bb5Sswilcox 	/*
467355d6bb5Sswilcox 	 * In our universe, nothing exists before the superblock, so
468355d6bb5Sswilcox 	 * just pretend it's always zeros.  This is the complement of
469355d6bb5Sswilcox 	 * bwrite()'s ignoring write requests into that space.
470355d6bb5Sswilcox 	 */
471355d6bb5Sswilcox 	if (blk < SBLOCK) {
472355d6bb5Sswilcox 		if (debug)
473355d6bb5Sswilcox 			(void) printf(
474355d6bb5Sswilcox 			    "WARNING: fsck_bread() passed blkno < %d (%lld)\n",
475355d6bb5Sswilcox 			    SBLOCK, (longlong_t)blk);
476355d6bb5Sswilcox 		(void) memset(buf, 0, (size_t)size);
477355d6bb5Sswilcox 		return (1);
4787c478bd9Sstevel@tonic-gate 	}
480f763a6cdScasper 	if (llseek(fd, offset, SEEK_SET) < 0) {
481355d6bb5Sswilcox 		rwerror("SEEK", blk, -1);
482355d6bb5Sswilcox 	}
484355d6bb5Sswilcox 	if ((i = read(fd, buf, size)) == size) {
4857c478bd9Sstevel@tonic-gate 		return (0);
486355d6bb5Sswilcox 	}
487355d6bb5Sswilcox 	rwerror("READ", blk, i);
488f763a6cdScasper 	if (llseek(fd, offset, SEEK_SET) < 0) {
489355d6bb5Sswilcox 		rwerror("SEEK", blk, -1);
4907c478bd9Sstevel@tonic-gate 	}
4917c478bd9Sstevel@tonic-gate 	errs = 0;
492355d6bb5Sswilcox 	(void) memset(buf, 0, (size_t)size);
4937c478bd9Sstevel@tonic-gate 	pwarn("THE FOLLOWING SECTORS COULD NOT BE READ:");
4947c478bd9Sstevel@tonic-gate 	for (cp = buf, i = 0; i < btodb(size); i++, cp += DEV_BSIZE) {
4957c478bd9Sstevel@tonic-gate 		addr = ldbtob(blk + i);
496f763a6cdScasper 		if (llseek(fd, addr, SEEK_SET) < 0 ||
4977c478bd9Sstevel@tonic-gate 		    read(fd, cp, (int)secsize) < 0) {
498355d6bb5Sswilcox 			iscorrupt = 1;
499355d6bb5Sswilcox 			(void) printf(" %llu", blk + (u_longlong_t)i);
5007c478bd9Sstevel@tonic-gate 			errs++;
5017c478bd9Sstevel@tonic-gate 		}
5027c478bd9Sstevel@tonic-gate 	}
503355d6bb5Sswilcox 	(void) printf("\n");
5047c478bd9Sstevel@tonic-gate 	return (errs);
5057c478bd9Sstevel@tonic-gate }
507355d6bb5Sswilcox void
bwrite(int fd,caddr_t buf,diskaddr_t blk,int64_t size)508355d6bb5Sswilcox bwrite(int fd, caddr_t buf, diskaddr_t blk, int64_t size)
5097c478bd9Sstevel@tonic-gate {
510355d6bb5Sswilcox 	int i;
5117c478bd9Sstevel@tonic-gate 	int n;
512355d6bb5Sswilcox 	caddr_t cp;
5137c478bd9Sstevel@tonic-gate 	offset_t offset = ldbtob(blk);
5147c478bd9Sstevel@tonic-gate 	offset_t addr;
5167c478bd9Sstevel@tonic-gate 	if (fd < 0)
5177c478bd9Sstevel@tonic-gate 		return;
5187c478bd9Sstevel@tonic-gate 	if (blk < SBLOCK) {
5197c478bd9Sstevel@tonic-gate 		if (debug)
520355d6bb5Sswilcox 			(void) printf(
521355d6bb5Sswilcox 		    "WARNING: Attempt to write illegal blkno %lld on %s\n",
522355d6bb5Sswilcox 			    (longlong_t)blk, devname);
5237c478bd9Sstevel@tonic-gate 		return;
5247c478bd9Sstevel@tonic-gate 	}
525f763a6cdScasper 	if (llseek(fd, offset, SEEK_SET) < 0) {
526355d6bb5Sswilcox 		rwerror("SEEK", blk, -1);
527355d6bb5Sswilcox 	}
528355d6bb5Sswilcox 	if ((i = write(fd, buf, (int)size)) == size) {
5297c478bd9Sstevel@tonic-gate 		fsmodified = 1;
5307c478bd9Sstevel@tonic-gate 		return;
5317c478bd9Sstevel@tonic-gate 	}
532355d6bb5Sswilcox 	rwerror("WRITE", blk, i);
533f763a6cdScasper 	if (llseek(fd, offset, SEEK_SET) < 0) {
534355d6bb5Sswilcox 		rwerror("SEEK", blk, -1);
5357c478bd9Sstevel@tonic-gate 	}
5367c478bd9Sstevel@tonic-gate 	pwarn("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:");
5377c478bd9Sstevel@tonic-gate 	for (cp = buf, i = 0; i < btodb(size); i++, cp += DEV_BSIZE) {
5387c478bd9Sstevel@tonic-gate 		n = 0;
5397c478bd9Sstevel@tonic-gate 		addr = ldbtob(blk + i);
540f763a6cdScasper 		if (llseek(fd, addr, SEEK_SET) < 0 ||
5417c478bd9Sstevel@tonic-gate 		    (n = write(fd, cp, DEV_BSIZE)) < 0) {
542355d6bb5Sswilcox 			iscorrupt = 1;
543355d6bb5Sswilcox 			(void) printf(" %llu", blk + (u_longlong_t)i);
5447c478bd9Sstevel@tonic-gate 		} else if (n > 0) {
5457c478bd9Sstevel@tonic-gate 			fsmodified = 1;
5467c478bd9Sstevel@tonic-gate 		}
5487c478bd9Sstevel@tonic-gate 	}
549355d6bb5Sswilcox 	(void) printf("\n");
5507c478bd9Sstevel@tonic-gate }
5527c478bd9Sstevel@tonic-gate /*
553355d6bb5Sswilcox  * Allocates the specified number of contiguous fragments.
5547c478bd9Sstevel@tonic-gate  */
5557c478bd9Sstevel@tonic-gate daddr32_t
allocblk(int wantedfrags)556355d6bb5Sswilcox allocblk(int wantedfrags)
5577c478bd9Sstevel@tonic-gate {
558355d6bb5Sswilcox 	int block, leadfrag, tailfrag;
559355d6bb5Sswilcox 	daddr32_t selected;
560355d6bb5Sswilcox 	size_t size;
561355d6bb5Sswilcox 	struct bufarea *bp;
563355d6bb5Sswilcox 	/*
564355d6bb5Sswilcox 	 * It's arguable whether we should just fail, or instead
565355d6bb5Sswilcox 	 * error out here.  Since we should only ever be asked for
566355d6bb5Sswilcox 	 * a single fragment or an entire block (i.e., sblock.fs_frag),
567355d6bb5Sswilcox 	 * we'll fail out because anything else means somebody
568355d6bb5Sswilcox 	 * changed code without considering all of the ramifications.