xref: /illumos-gate/usr/src/cmd/fs.d/ufs/fsck/utilities.c (revision 48bbca81)
17c478bd9Sstevel@tonic-gate /*
223a1cceaSRoger A. Faulkner  * Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved.
3*48bbca81SDaniel 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	*/
77c478bd9Sstevel@tonic-gate /*	  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"
64355d6bb5Sswilcox caddr_t mount_point = NULL;
66355d6bb5Sswilcox static int64_t diskreads, totalreads;	/* Disk cache statistics */
68355d6bb5Sswilcox static int log_checksum(int32_t *, int32_t *, int);
69355d6bb5Sswilcox static void vdirerror(fsck_ino_t, caddr_t, va_list);
70355d6bb5Sswilcox static struct mnttab *search_mnttab(caddr_t, caddr_t, caddr_t, size_t);
71355d6bb5Sswilcox static struct vfstab *search_vfstab(caddr_t, caddr_t, caddr_t, size_t);
72355d6bb5Sswilcox static void vpwarn(caddr_t, va_list);
7323a1cceaSRoger A. Faulkner static int getaline(FILE *, caddr_t, int);
74355d6bb5Sswilcox static struct bufarea *alloc_bufarea(void);
75355d6bb5Sswilcox static void rwerror(caddr_t, diskaddr_t, int rval);
76355d6bb5Sswilcox static void debugclean(void);
77355d6bb5Sswilcox static void report_io_prob(caddr_t, diskaddr_t, size_t, ssize_t);
78355d6bb5Sswilcox static void freelogblk(daddr32_t);
79355d6bb5Sswilcox static void verrexit(caddr_t, va_list);
80355d6bb5Sswilcox static void vpfatal(caddr_t, va_list);
81355d6bb5Sswilcox static diskaddr_t get_device_size(int, caddr_t);
82355d6bb5Sswilcox static diskaddr_t brute_force_get_device_size(int);
83355d6bb5Sswilcox static void cg_constants(int, daddr32_t *, daddr32_t *, daddr32_t *,
84355d6bb5Sswilcox 	    daddr32_t *, daddr32_t *, daddr32_t *);
86355d6bb5Sswilcox int
ftypeok(struct dinode * dp)87355d6bb5Sswilcox ftypeok(struct dinode *dp)
887c478bd9Sstevel@tonic-gate {
897c478bd9Sstevel@tonic-gate 	switch (dp->di_mode & IFMT) {
917c478bd9Sstevel@tonic-gate 	case IFDIR:
927c478bd9Sstevel@tonic-gate 	case IFREG:
937c478bd9Sstevel@tonic-gate 	case IFBLK:
947c478bd9Sstevel@tonic-gate 	case IFCHR:
957c478bd9Sstevel@tonic-gate 	case IFLNK:
967c478bd9Sstevel@tonic-gate 	case IFSOCK:
977c478bd9Sstevel@tonic-gate 	case IFIFO:
987c478bd9Sstevel@tonic-gate 	case IFSHAD:
997c478bd9Sstevel@tonic-gate 	case IFATTRDIR:
1007c478bd9Sstevel@tonic-gate 		return (1);
1027c478bd9Sstevel@tonic-gate 	default:
1037c478bd9Sstevel@tonic-gate 		if (debug)
104355d6bb5Sswilcox 			(void) printf("bad file type 0%o\n", dp->di_mode);
1057c478bd9Sstevel@tonic-gate 		return (0);
1067c478bd9Sstevel@tonic-gate 	}
1077c478bd9Sstevel@tonic-gate }
109355d6bb5Sswilcox int
acltypeok(struct dinode * dp)110355d6bb5Sswilcox acltypeok(struct dinode *dp)
1117c478bd9Sstevel@tonic-gate {
1127c478bd9Sstevel@tonic-gate 	if (CHECK_ACL_ALLOWED(dp->di_mode & IFMT))
1137c478bd9Sstevel@tonic-gate 		return (1);
1157c478bd9Sstevel@tonic-gate 	if (debug)
116355d6bb5Sswilcox 		(void) printf("bad file type for acl I=%d: 0%o\n",
117355d6bb5Sswilcox 		    dp->di_shadow, dp->di_mode);
1187c478bd9Sstevel@tonic-gate 	return (0);
1197c478bd9Sstevel@tonic-gate }
121355d6bb5Sswilcox NOTE(PRINTFLIKE(1))
122355d6bb5Sswilcox int
reply(caddr_t fmt,...)123355d6bb5Sswilcox reply(caddr_t fmt, ...)
1247c478bd9Sstevel@tonic-gate {
125355d6bb5Sswilcox 	va_list ap;
1267c478bd9Sstevel@tonic-gate 	char line[80];
1287c478bd9Sstevel@tonic-gate 	if (preen)
129355d6bb5Sswilcox 		pfatal("INTERNAL ERROR: GOT TO reply() in preen mode");
1317c478bd9Sstevel@tonic-gate 	if (mflag) {
132355d6bb5Sswilcox 		/*
133355d6bb5Sswilcox 		 * We don't know what's going on, so don't potentially
134355d6bb5Sswilcox 		 * make things worse by having errexit() write stuff
135355d6bb5Sswilcox 		 * out to disk.
136355d6bb5Sswilcox 		 */
137355d6bb5Sswilcox 		(void) printf(
138355d6bb5Sswilcox 		    "\n%s: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY.\n",
139355d6bb5Sswilcox 		    devname);
140355d6bb5Sswilcox 		exit(EXERRFATAL);
1417c478bd9Sstevel@tonic-gate 	}
143355d6bb5Sswilcox 	va_start(ap, fmt);
144355d6bb5Sswilcox 	(void) putchar('\n');
145355d6bb5Sswilcox 	(void) vprintf(fmt, ap);
146355d6bb5Sswilcox 	(void) putchar('?');
147355d6bb5Sswilcox 	(void) putchar(' ');
148355d6bb5Sswilcox 	va_end(ap);
1507c478bd9Sstevel@tonic-gate 	if (nflag || fswritefd < 0) {
151355d6bb5Sswilcox 		(void) printf(" no\n\n");
1527c478bd9Sstevel@tonic-gate 		return (0);
1537c478bd9Sstevel@tonic-gate 	}
1547c478bd9Sstevel@tonic-gate 	if (yflag) {
155355d6bb5Sswilcox 		(void) printf(" yes\n\n");
1567c478bd9Sstevel@tonic-gate 		return (1);
1577c478bd9Sstevel@tonic-gate 	}
158355d6bb5Sswilcox 	(void) fflush(stdout);
15923a1cceaSRoger A. Faulkner 	if (getaline(stdin, line, sizeof (line)) == EOF)
1607c478bd9Sstevel@tonic-gate 		errexit("\n");
161355d6bb5Sswilcox 	(void) printf("\n");
162355d6bb5Sswilcox 	if (line[0] == 'y' || line[0] == 'Y') {
1637c478bd9Sstevel@tonic-gate 		return (1);
164355d6bb5Sswilcox 	} else {
1657c478bd9Sstevel@tonic-gate 		return (0);
1667c478bd9Sstevel@tonic-gate 	}
1677c478bd9Sstevel@tonic-gate }
169355d6bb5Sswilcox int
getaline(FILE * fp,caddr_t loc,int maxlen)17023a1cceaSRoger A. Faulkner getaline(FILE *fp, caddr_t loc, int maxlen)
1717c478bd9Sstevel@tonic-gate {
1727c478bd9Sstevel@tonic-gate 	int n;
173355d6bb5Sswilcox 	caddr_t p, lastloc;
1757c478bd9Sstevel@tonic-gate 	p = loc;
1767c478bd9Sstevel@tonic-gate 	lastloc = &p[maxlen-1];
1777c478bd9Sstevel@tonic-gate 	while ((n = getc(fp)) != '\n') {
1787c478bd9Sstevel@tonic-gate 		if (n == EOF)
1797c478bd9Sstevel@tonic-gate 			return (EOF);
1807c478bd9Sstevel@tonic-gate 		if (!isspace(n) && p < lastloc)
181355d6bb5Sswilcox 			*p++ = (char)n;
1827c478bd9Sstevel@tonic-gate 	}
183355d6bb5Sswilcox 	*p = '\0';
184355d6bb5Sswilcox 	/* LINTED pointer difference won't overflow */
1857c478bd9Sstevel@tonic-gate 	return (p - loc);
1867c478bd9Sstevel@tonic-gate }
1887c478bd9Sstevel@tonic-gate /*
1897c478bd9Sstevel@tonic-gate  * Malloc buffers and set up cache.
1907c478bd9Sstevel@tonic-gate  */
191355d6bb5Sswilcox void
bufinit(void)192355d6bb5Sswilcox bufinit(void)
1937c478bd9Sstevel@tonic-gate {
1947c478bd9Sstevel@tonic-gate 	struct bufarea *bp;
1957c478bd9Sstevel@tonic-gate 	int bufcnt, i;
196355d6bb5Sswilcox 	caddr_t bufp;
198355d6bb5Sswilcox 	bufp = malloc((size_t)sblock.fs_bsize);
199355d6bb5Sswilcox 	if (bufp == NULL)
200355d6bb5Sswilcox 		goto nomem;
2017c478bd9Sstevel@tonic-gate 	initbarea(&cgblk);
202355d6bb5Sswilcox 	cgblk.b_un.b_buf = bufp;
2037c478bd9Sstevel@tonic-gate 	bufhead.b_next = bufhead.b_prev = &bufhead;
2047c478bd9Sstevel@tonic-gate 	bufcnt = MAXBUFSPACE / sblock.fs_bsize;
2057c478bd9Sstevel@tonic-gate 	if (bufcnt < MINBUFS)
2067c478bd9Sstevel@tonic-gate 		bufcnt = MINBUFS;
2077c478bd9Sstevel@tonic-gate 	for (i = 0; i < bufcnt; i++) {
2087c478bd9Sstevel@tonic-gate 		bp = (struct bufarea *)malloc(sizeof (struct bufarea));
209355d6bb5Sswilcox 		if (bp == NULL) {
2107c478bd9Sstevel@tonic-gate 			if (i >= MINBUFS)
211355d6bb5Sswilcox 				goto noalloc;
212355d6bb5Sswilcox 			goto nomem;
213355d6bb5Sswilcox 		}
215355d6bb5Sswilcox 		bufp = malloc((size_t)sblock.fs_bsize);
216355d6bb5Sswilcox 		if (bufp == NULL) {
217355d6bb5Sswilcox 			free((void *)bp);
218355d6bb5Sswilcox 			if (i >= MINBUFS)
219355d6bb5Sswilcox 				goto noalloc;
220355d6bb5Sswilcox 			goto nomem;
2217c478bd9Sstevel@tonic-gate 		}
222355d6bb5Sswilcox 		initbarea(bp);
2237c478bd9Sstevel@tonic-gate 		bp->b_un.b_buf = bufp;
2247c478bd9Sstevel@tonic-gate 		bp->b_prev = &bufhead;
2257c478bd9Sstevel@tonic-gate 		bp->b_next = bufhead.b_next;
2267c478bd9Sstevel@tonic-gate 		bufhead.b_next->b_prev = bp;
2277c478bd9Sstevel@tonic-gate 		bufhead.b_next = bp;
2287c478bd9Sstevel@tonic-gate 	}
229355d6bb5Sswilcox noalloc:
2307c478bd9Sstevel@tonic-gate 	bufhead.b_size = i;	/* save number of buffers */
2317c478bd9Sstevel@tonic-gate 	pbp = pdirbp = NULL;
232355d6bb5Sswilcox 	return;
234355d6bb5Sswilcox nomem:
235355d6bb5Sswilcox 	errexit("cannot allocate buffer pool\n");
236355d6bb5Sswilcox 	/* NOTREACHED */
237355d6bb5Sswilcox }
239355d6bb5Sswilcox /*
240355d6bb5Sswilcox  * Undo a bufinit().
241355d6bb5Sswilcox  */
242355d6bb5Sswilcox void
unbufinit(void)243355d6bb5Sswilcox unbufinit(void)
244355d6bb5Sswilcox {
245355d6bb5Sswilcox 	int cnt;
246355d6bb5Sswilcox 	struct bufarea *bp, *nbp;
248355d6bb5Sswilcox 	cnt = 0;
249355d6bb5Sswilcox 	for (bp = bufhead.b_prev; bp != NULL && bp != &bufhead; bp = nbp) {
250355d6bb5Sswilcox 		cnt++;
251355d6bb5Sswilcox 		flush(fswritefd, bp);
252355d6bb5Sswilcox 		nbp = bp->b_prev;
253355d6bb5Sswilcox 		/*
254355d6bb5Sswilcox 		 * We're discarding the entire chain, so this isn't
255355d6bb5Sswilcox 		 * technically necessary.  However, it doesn't hurt
256355d6bb5Sswilcox 		 * and lint's data flow analysis is much happier
257355d6bb5Sswilcox 		 * (this prevents it from thinking there's a chance
258355d6bb5Sswilcox 		 * of our using memory elsewhere after it's been released).
259355d6bb5Sswilcox 		 */
260355d6bb5Sswilcox 		nbp->b_next = bp->b_next;
261355d6bb5Sswilcox 		bp->b_next->b_prev = nbp;
262355d6bb5Sswilcox 		free((void *)bp->b_un.b_buf);
263355d6bb5Sswilcox 		free((void *)bp);
264355d6bb5Sswilcox 	}
266355d6bb5Sswilcox 	if (bufhead.b_size != cnt)
267355d6bb5Sswilcox 		errexit("Panic: cache lost %d buffers\n",
2681493b746SMilan Cermak 		    bufhead.b_size - cnt);
2697c478bd9Sstevel@tonic-gate }
2717c478bd9Sstevel@tonic-gate /*
2727c478bd9Sstevel@tonic-gate  * Manage a cache of directory blocks.
2737c478bd9Sstevel@tonic-gate  */
2747c478bd9Sstevel@tonic-gate struct bufarea *
getdatablk(daddr32_t blkno,size_t size)275355d6bb5Sswilcox getdatablk(daddr32_t blkno, size_t size)
2767c478bd9Sstevel@tonic-gate {
2777c478bd9Sstevel@tonic-gate 	struct bufarea *bp;
2797c478bd9Sstevel@tonic-gate 	for (bp = bufhead.b_next; bp != &bufhead; bp = bp->b_next)
280355d6bb5Sswilcox 		if (bp->b_bno == fsbtodb(&sblock, blkno)) {
2817c478bd9Sstevel@tonic-gate 			goto foundit;
282355d6bb5Sswilcox 		}
2837c478bd9Sstevel@tonic-gate 	for (bp = bufhead.b_prev; bp != &bufhead; bp = bp->b_prev)
2847c478bd9Sstevel@tonic-gate 		if ((bp->b_flags & B_INUSE) == 0)
2857c478bd9Sstevel@tonic-gate 			break;
2867c478bd9Sstevel@tonic-gate 	if (bp == &bufhead) {
2877c478bd9Sstevel@tonic-gate 		bp = alloc_bufarea();
288355d6bb5Sswilcox 		if (bp == NULL) {
2897c478bd9Sstevel@tonic-gate 			errexit("deadlocked buffer pool\n");
290355d6bb5Sswilcox 			/* NOTREACHED */
291355d6bb5Sswilcox 		}
2927c478bd9Sstevel@tonic-gate 	}
293355d6bb5Sswilcox 	/*
294355d6bb5Sswilcox 	 * We're at the same logical level as getblk(), so if there
295355d6bb5Sswilcox 	 * are any errors, we'll let our caller handle them.
296355d6bb5Sswilcox 	 */
297355d6bb5Sswilcox 	diskreads++;
298355d6bb5Sswilcox 	(void) getblk(bp, blkno, size);
3007c478bd9Sstevel@tonic-gate foundit:
3017c478bd9Sstevel@tonic-gate 	totalreads++;
3027c478bd9Sstevel@tonic-gate 	bp->b_cnt++;
3037c478bd9Sstevel@tonic-gate 	/*
304355d6bb5Sswilcox 	 * Move the buffer to head of linked list if it isn't
3057c478bd9Sstevel@tonic-gate 	 * already there.
3067c478bd9Sstevel@tonic-gate 	 */
3077c478bd9Sstevel@tonic-gate 	if (bufhead.b_next != bp) {
3087c478bd9Sstevel@tonic-gate 		bp->b_prev->b_next = bp->b_next;
3097c478bd9Sstevel@tonic-gate 		bp->b_next->b_prev = bp->b_prev;
3107c478bd9Sstevel@tonic-gate 		bp->b_prev = &bufhead;
3117c478bd9Sstevel@tonic-gate 		bp->b_next = bufhead.b_next;
3127c478bd9Sstevel@tonic-gate 		bufhead.b_next->b_prev = bp;
3137c478bd9Sstevel@tonic-gate 		bufhead.b_next = bp;
3147c478bd9Sstevel@tonic-gate 	}
3157c478bd9Sstevel@tonic-gate 	bp->b_flags |= B_INUSE;
3167c478bd9Sstevel@tonic-gate 	return (bp);
3177c478bd9Sstevel@tonic-gate }
319355d6bb5Sswilcox void
brelse(struct bufarea * bp)3207c478bd9Sstevel@tonic-gate brelse(struct bufarea *bp)
3217c478bd9Sstevel@tonic-gate {
3227c478bd9Sstevel@tonic-gate 	bp->b_cnt--;
3237c478bd9Sstevel@tonic-gate 	if (bp->b_cnt == 0) {
3247c478bd9Sstevel@tonic-gate 		bp->b_flags &= ~B_INUSE;
3257c478bd9Sstevel@tonic-gate 	}
3267c478bd9Sstevel@tonic-gate }
3287c478bd9Sstevel@tonic-gate struct bufarea *
getblk(struct bufarea * bp,daddr32_t blk,size_t size)329355d6bb5Sswilcox getblk(struct bufarea *bp, daddr32_t blk, size_t size)
3307c478bd9Sstevel@tonic-gate {
3317c478bd9Sstevel@tonic-gate 	diskaddr_t dblk;
3337c478bd9Sstevel@tonic-gate 	dblk = fsbtodb(&sblock, blk);
3347c478bd9Sstevel@tonic-gate 	if (bp->b_bno == dblk)
3357c478bd9Sstevel@tonic-gate 		return (bp);
3367c478bd9Sstevel@tonic-gate 	flush(fswritefd, bp);
337355d6bb5Sswilcox 	bp->b_errs = fsck_bread(fsreadfd, bp->b_un.b_buf, dblk, size);
3387c478bd9Sstevel@tonic-gate 	bp->b_bno = dblk;
3397c478bd9Sstevel@tonic-gate 	bp->b_size = size;
3407c478bd9Sstevel@tonic-gate 	return (bp);
3417c478bd9Sstevel@tonic-gate }
343355d6bb5Sswilcox void
flush(int fd,struct bufarea * bp)344355d6bb5Sswilcox flush(int fd, struct bufarea *bp)
3457c478bd9Sstevel@tonic-gate {
3467c478bd9Sstevel@tonic-gate 	int i, j;
3477c478bd9Sstevel@tonic-gate 	caddr_t sip;
3487c478bd9Sstevel@tonic-gate 	long size;
3507c478bd9Sstevel@tonic-gate 	if (!bp->b_dirty)
3517c478bd9Sstevel@tonic-gate 		return;
353355d6bb5Sswilcox 	/*
354355d6bb5Sswilcox 	 * It's not our buf, so if there are errors, let whoever
355355d6bb5Sswilcox 	 * acquired it deal with the actual problem.
356355d6bb5Sswilcox 	 */
3577c478bd9Sstevel@tonic-gate 	if (bp->b_errs != 0)
3587c478bd9Sstevel@tonic-gate 		pfatal("WRITING ZERO'ED BLOCK %lld TO DISK\n", bp->b_bno);
3597c478bd9Sstevel@tonic-gate 	bp->b_dirty = 0;
3607c478bd9Sstevel@tonic-gate 	bp->b_errs = 0;
3617c478bd9Sstevel@tonic-gate 	bwrite(fd, bp->b_un.b_buf, bp->b_bno, (long)bp->b_size);
362355d6bb5Sswilcox 	if (bp != &sblk) {
3637c478bd9Sstevel@tonic-gate 		return;
364355d6bb5Sswilcox 	}
366355d6bb5Sswilcox 	/*
367355d6bb5Sswilcox 	 * We're flushing the superblock, so make sure all the
368355d6bb5Sswilcox 	 * ancillary bits go out as well.
369355d6bb5Sswilcox 	 */
3707c478bd9Sstevel@tonic-gate 	sip = (caddr_t)sblock.fs_u.fs_csp;
3717c478bd9Sstevel@tonic-gate 	for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) {
3727c478bd9Sstevel@tonic-gate 		size = sblock.fs_cssize - i < sblock.fs_bsize ?
3737c478bd9Sstevel@tonic-gate 		    sblock.fs_cssize - i : sblock.fs_bsize;
3747c478bd9Sstevel@tonic-gate 		bwrite(fswritefd, sip,
3757c478bd9Sstevel@tonic-gate 		    fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag),
3767c478bd9Sstevel@tonic-gate 		    size);
3777c478bd9Sstevel@tonic-gate 		sip += size;
3787c478bd9Sstevel@tonic-gate 	}
3797c478bd9Sstevel@tonic-gate }
381355d6bb5Sswilcox static void
rwerror(caddr_t mesg,diskaddr_t blk,int rval)382355d6bb5Sswilcox rwerror(caddr_t mesg, diskaddr_t blk, int rval)
3837c478bd9Sstevel@tonic-gate {
384355d6bb5Sswilcox 	int olderr = errno;
386355d6bb5Sswilcox 	if (!preen)
387355d6bb5Sswilcox 		(void) printf("\n");
389355d6bb5Sswilcox 	if (rval == -1)
390355d6bb5Sswilcox 		pfatal("CANNOT %s: DISK BLOCK %lld: %s",
391355d6bb5Sswilcox 		    mesg, blk, strerror(olderr));
392355d6bb5Sswilcox 	else
393355d6bb5Sswilcox 		pfatal("CANNOT %s: DISK BLOCK %lld", mesg, blk);
395355d6bb5Sswilcox 	if (reply("CONTINUE") == 0) {
396355d6bb5Sswilcox 		exitstat = EXERRFATAL;
3977c478bd9Sstevel@tonic-gate 		errexit("Program terminated\n");
398355d6bb5Sswilcox 	}
3997c478bd9Sstevel@tonic-gate }
401355d6bb5Sswilcox void
ckfini(void)402355d6bb5Sswilcox ckfini(void)
4037c478bd9Sstevel@tonic-gate {
404355d6bb5Sswilcox 	int64_t percentage;
406355d6bb5Sswilcox 	if (fswritefd < 0)
407355d6bb5Sswilcox 		return;
409355d6bb5Sswilcox 	flush(fswritefd, &sblk);
4107c478bd9Sstevel@tonic-gate 	/*
411355d6bb5Sswilcox 	 * Were we using a backup superblock?
4127c478bd9Sstevel@tonic-gate 	 */
4137c478bd9Sstevel@tonic-gate 	if (havesb && sblk.b_bno != SBOFF / dev_bsize) {
414355d6bb5Sswilcox 		if (preen || reply("UPDATE STANDARD SUPERBLOCK") == 1) {
415355d6bb5Sswilcox 			sblk.b_bno = SBOFF / dev_bsize;
416355d6bb5Sswilcox 			sbdirty();
417355d6bb5Sswilcox 			flush(fswritefd, &sblk);
418355d6bb5Sswilcox 		}
4197c478bd9Sstevel@tonic-gate 	}
4207c478bd9Sstevel@tonic-gate 	flush(fswritefd, &cgblk);
421355d6bb5Sswilcox 	if (cgblk.b_un.b_buf != NULL) {
422355d6bb5Sswilcox 		free((void *)cgblk.b_un.b_buf);
4237c478bd9Sstevel@tonic-gate 		cgblk.b_un.b_buf = NULL;
4247c478bd9Sstevel@tonic-gate 	}
425355d6bb5Sswilcox 	unbufinit();
426355d6bb5Sswilcox 	pbp = NULL;
427355d6bb5Sswilcox 	pdirbp = NULL;
428355d6bb5Sswilcox 	if (debug) {
429355d6bb5Sswilcox 		/*
430355d6bb5Sswilcox 		 * Note that we only count cache-related reads.
431355d6bb5Sswilcox 		 * Anything that called fsck_bread() or getblk()
432355d6bb5Sswilcox 		 * directly are explicitly not cached, so they're not
433355d6bb5Sswilcox 		 * included here.
434355d6bb5Sswilcox 		 */
435355d6bb5Sswilcox 		if (totalreads != 0)
436355d6bb5Sswilcox 			percentage = diskreads * 100 / totalreads;
437355d6bb5Sswilcox 		else
438355d6bb5Sswilcox 			percentage = 0;
440355d6bb5Sswilcox 		(void) printf("cache missed %lld of %lld reads (%lld%%)\n",
441355d6bb5Sswilcox 		    (longlong_t)diskreads, (longlong_t)totalreads,
442355d6bb5Sswilcox 		    (longlong_t)percentage);
4437c478bd9Sstevel@tonic-gate 	}
4457c478bd9Sstevel@tonic-gate 	(void) close(fsreadfd);
4467c478bd9Sstevel@tonic-gate 	(void) close(fswritefd);
447355d6bb5Sswilcox 	fsreadfd = -1;
448355d6bb5Sswilcox 	fswritefd = -1;
4497c478bd9Sstevel@tonic-gate }
451355d6bb5Sswilcox int
fsck_bread(int fd,caddr_t buf,diskaddr_t blk,size_t size)452355d6bb5Sswilcox fsck_bread(int fd, caddr_t buf, diskaddr_t blk, size_t size)
4537c478bd9Sstevel@tonic-gate {
454355d6bb5Sswilcox 	caddr_t cp;
455355d6bb5Sswilcox 	int i;
4567c478bd9Sstevel@tonic-gate 	int errs;
4577c478bd9Sstevel@tonic-gate 	offset_t offset = ldbtob(blk);
4587c478bd9Sstevel@tonic-gate 	offset_t addr;
460355d6bb5Sswilcox 	/*
461355d6bb5Sswilcox 	 * In our universe, nothing exists before the superblock, so
462355d6bb5Sswilcox 	 * just pretend it's always zeros.  This is the complement of
463355d6bb5Sswilcox 	 * bwrite()'s ignoring write requests into that space.
464355d6bb5Sswilcox 	 */
465355d6bb5Sswilcox 	if (blk < SBLOCK) {
466355d6bb5Sswilcox 		if (debug)
467355d6bb5Sswilcox 			(void) printf(
468355d6bb5Sswilcox 			    "WARNING: fsck_bread() passed blkno < %d (%lld)\n",
469355d6bb5Sswilcox 			    SBLOCK, (longlong_t)blk);
470355d6bb5Sswilcox 		(void) memset(buf, 0, (size_t)size);
471355d6bb5Sswilcox 		return (1);
4727c478bd9Sstevel@tonic-gate 	}
474f763a6cdScasper 	if (llseek(fd, offset, SEEK_SET) < 0) {
475355d6bb5Sswilcox 		rwerror("SEEK", blk, -1);
476355d6bb5Sswilcox 	}
478355d6bb5Sswilcox 	if ((i = read(fd, buf, size)) == size) {
4797c478bd9Sstevel@tonic-gate 		return (0);
480355d6bb5Sswilcox 	}
481355d6bb5Sswilcox 	rwerror("READ", blk, i);
482f763a6cdScasper 	if (llseek(fd, offset, SEEK_SET) < 0) {
483355d6bb5Sswilcox 		rwerror("SEEK", blk, -1);
4847c478bd9Sstevel@tonic-gate 	}
4857c478bd9Sstevel@tonic-gate 	errs = 0;
486355d6bb5Sswilcox 	(void) memset(buf, 0, (size_t)size);
4877c478bd9Sstevel@tonic-gate 	pwarn("THE FOLLOWING SECTORS COULD NOT BE READ:");
4887c478bd9Sstevel@tonic-gate 	for (cp = buf, i = 0; i < btodb(size); i++, cp += DEV_BSIZE) {
4897c478bd9Sstevel@tonic-gate 		addr = ldbtob(blk + i);
490f763a6cdScasper 		if (llseek(fd, addr, SEEK_SET) < 0 ||
4917c478bd9Sstevel@tonic-gate 		    read(fd, cp, (int)secsize) < 0) {
492355d6bb5Sswilcox 			iscorrupt = 1;
493355d6bb5Sswilcox 			(void) printf(" %llu", blk + (u_longlong_t)i);
4947c478bd9Sstevel@tonic-gate 			errs++;
4957c478bd9Sstevel@tonic-gate 		}
4967c478bd9Sstevel@tonic-gate 	}
497355d6bb5Sswilcox 	(void) printf("\n");
4987c478bd9Sstevel@tonic-gate 	return (errs);
4997c478bd9Sstevel@tonic-gate }
501355d6bb5Sswilcox void
bwrite(int fd,caddr_t buf,diskaddr_t blk,int64_t size)502355d6bb5Sswilcox bwrite(int fd, caddr_t buf, diskaddr_t blk, int64_t size)
5037c478bd9Sstevel@tonic-gate {
504355d6bb5Sswilcox 	int i;
5057c478bd9Sstevel@tonic-gate 	int n;
506355d6bb5Sswilcox 	caddr_t cp;
5077c478bd9Sstevel@tonic-gate 	offset_t offset = ldbtob(blk);
5087c478bd9Sstevel@tonic-gate 	offset_t addr;
5107c478bd9Sstevel@tonic-gate 	if (fd < 0)
5117c478bd9Sstevel@tonic-gate 		return;
5127c478bd9Sstevel@tonic-gate 	if (blk < SBLOCK) {
5137c478bd9Sstevel@tonic-gate 		if (debug)
514355d6bb5Sswilcox 			(void) printf(
515355d6bb5Sswilcox 		    "WARNING: Attempt to write illegal blkno %lld on %s\n",
516355d6bb5Sswilcox 			    (longlong_t)blk, devname);
5177c478bd9Sstevel@tonic-gate 		return;
5187c478bd9Sstevel@tonic-gate 	}
519f763a6cdScasper 	if (llseek(fd, offset, SEEK_SET) < 0) {
520355d6bb5Sswilcox 		rwerror("SEEK", blk, -1);
521355d6bb5Sswilcox 	}
522355d6bb5Sswilcox 	if ((i = write(fd, buf, (int)size)) == size) {
5237c478bd9Sstevel@tonic-gate 		fsmodified = 1;
5247c478bd9Sstevel@tonic-gate 		return;
5257c478bd9Sstevel@tonic-gate 	}
526355d6bb5Sswilcox 	rwerror("WRITE", blk, i);
527f763a6cdScasper 	if (llseek(fd, offset, SEEK_SET) < 0) {
528355d6bb5Sswilcox 		rwerror("SEEK", blk, -1);
5297c478bd9Sstevel@tonic-gate 	}
5307c478bd9Sstevel@tonic-gate 	pwarn("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:");
5317c478bd9Sstevel@tonic-gate 	for (cp = buf, i = 0; i < btodb(size); i++, cp += DEV_BSIZE) {
5327c478bd9Sstevel@tonic-gate 		n = 0;
5337c478bd9Sstevel@tonic-gate 		addr = ldbtob(blk + i);
534f763a6cdScasper 		if (llseek(fd, addr, SEEK_SET) < 0 ||
5357c478bd9Sstevel@tonic-gate 		    (n = write(fd, cp, DEV_BSIZE)) < 0) {
536355d6bb5Sswilcox 			iscorrupt = 1;
537355d6bb5Sswilcox 			(void) printf(" %llu", blk + (u_longlong_t)i);
5387c478bd9Sstevel@tonic-gate 		} else if (n > 0) {
5397c478bd9Sstevel@tonic-gate 			fsmodified = 1;
5407c478bd9Sstevel@tonic-gate 		}
5427c478bd9Sstevel@tonic-gate 	}
543355d6bb5Sswilcox 	(void) printf("\n");
5447c478bd9Sstevel@tonic-gate }
5467c478bd9Sstevel@tonic-gate /*
547355d6bb5Sswilcox  * Allocates the specified number of contiguous fragments.
5487c478bd9Sstevel@tonic-gate  */
5497c478bd9Sstevel@tonic-gate daddr32_t
allocblk(int wantedfrags)550355d6bb5Sswilcox allocblk(int wantedfrags)
5517c478bd9Sstevel@tonic-gate {
552355d6bb5Sswilcox 	int block, leadfrag, tailfrag;
553355d6bb5Sswilcox 	daddr32_t selected;
554355d6bb5Sswilcox 	size_t size;
555355d6bb5Sswilcox 	struct bufarea *bp;
557355d6bb5Sswilcox 	/*
558355d6bb5Sswilcox 	 * It's arguable whether we should just fail, or instead
559355d6bb5Sswilcox 	 * error out here.  Since we should only ever be asked for
560355d6bb5Sswilcox 	 * a single fragment or an entire block (i.e., sblock.fs_frag),
561355d6bb5Sswilcox 	 * we'll fail out because anything else means somebody
562355d6bb5Sswilcox 	 * changed code without considering all of the ramifications.
563355d6bb5Sswilcox 	 */
564355d6bb5Sswilcox 	if (wantedfrags <= 0 || wantedfrags > sblock.fs_frag) {
565355d6bb5Sswilcox 		exitstat = EXERRFATAL;