xref: /illumos-gate/usr/src/cmd/fs.d/ufs/fsck/utilities.c (revision f763a6cd)
17c478bd9Sstevel@tonic-gate /*
2*f763a6cdScasper  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
37c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
47c478bd9Sstevel@tonic-gate  */
57c478bd9Sstevel@tonic-gate 
67c478bd9Sstevel@tonic-gate /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
77c478bd9Sstevel@tonic-gate /*	  All Rights Reserved  	*/
87c478bd9Sstevel@tonic-gate 
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.
237c478bd9Sstevel@tonic-gate  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
247c478bd9Sstevel@tonic-gate  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
257c478bd9Sstevel@tonic-gate  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
267c478bd9Sstevel@tonic-gate  */
277c478bd9Sstevel@tonic-gate 
287c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
297c478bd9Sstevel@tonic-gate 
307c478bd9Sstevel@tonic-gate #include <stdio.h>
31355d6bb5Sswilcox #include <stdlib.h>
32355d6bb5Sswilcox #include <unistd.h>
33355d6bb5Sswilcox #include <stdarg.h>
34355d6bb5Sswilcox #include <libadm.h>
35355d6bb5Sswilcox #include <note.h>
367c478bd9Sstevel@tonic-gate #include <sys/param.h>
377c478bd9Sstevel@tonic-gate #include <sys/types.h>
387c478bd9Sstevel@tonic-gate #include <sys/mntent.h>
397c478bd9Sstevel@tonic-gate #include <sys/filio.h>
407c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_fs.h>
417c478bd9Sstevel@tonic-gate #include <sys/vnode.h>
427c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_acl.h>
43355d6bb5Sswilcox #include <sys/fs/ufs_inode.h>
44355d6bb5Sswilcox #include <sys/fs/ufs_log.h>
457c478bd9Sstevel@tonic-gate #define	_KERNEL
467c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_fsdir.h>
477c478bd9Sstevel@tonic-gate #undef _KERNEL
487c478bd9Sstevel@tonic-gate #include <sys/mnttab.h>
497c478bd9Sstevel@tonic-gate #include <sys/types.h>
507c478bd9Sstevel@tonic-gate #include <sys/stat.h>
51355d6bb5Sswilcox #include <fcntl.h>
52355d6bb5Sswilcox #include <signal.h>
537c478bd9Sstevel@tonic-gate #include <string.h>
547c478bd9Sstevel@tonic-gate #include <ctype.h>
557c478bd9Sstevel@tonic-gate #include <sys/vfstab.h>
567c478bd9Sstevel@tonic-gate #include <sys/lockfs.h>
577c478bd9Sstevel@tonic-gate #include <errno.h>
58355d6bb5Sswilcox #include <sys/cmn_err.h>
59355d6bb5Sswilcox #include <sys/dkio.h>
60355d6bb5Sswilcox #include <sys/vtoc.h>
61355d6bb5Sswilcox #include <sys/efi_partition.h>
62355d6bb5Sswilcox #include <fslib.h>
63355d6bb5Sswilcox #include <inttypes.h>
64355d6bb5Sswilcox #include "fsck.h"
657c478bd9Sstevel@tonic-gate 
66355d6bb5Sswilcox caddr_t mount_point = NULL;
67355d6bb5Sswilcox 
68355d6bb5Sswilcox static int64_t diskreads, totalreads;	/* Disk cache statistics */
69355d6bb5Sswilcox 
70355d6bb5Sswilcox static int log_checksum(int32_t *, int32_t *, int);
71355d6bb5Sswilcox static void vdirerror(fsck_ino_t, caddr_t, va_list);
72355d6bb5Sswilcox static struct mnttab *search_mnttab(caddr_t, caddr_t, caddr_t, size_t);
73355d6bb5Sswilcox static struct vfstab *search_vfstab(caddr_t, caddr_t, caddr_t, size_t);
74355d6bb5Sswilcox static void vpwarn(caddr_t, va_list);
75355d6bb5Sswilcox static int getline(FILE *, caddr_t, int);
76355d6bb5Sswilcox static struct bufarea *alloc_bufarea(void);
77355d6bb5Sswilcox static void rwerror(caddr_t, diskaddr_t, int rval);
78355d6bb5Sswilcox static void debugclean(void);
79355d6bb5Sswilcox static void report_io_prob(caddr_t, diskaddr_t, size_t, ssize_t);
80355d6bb5Sswilcox static void freelogblk(daddr32_t);
81355d6bb5Sswilcox static void verrexit(caddr_t, va_list);
82355d6bb5Sswilcox static void vpfatal(caddr_t, va_list);
83355d6bb5Sswilcox static diskaddr_t get_device_size(int, caddr_t);
84355d6bb5Sswilcox static diskaddr_t brute_force_get_device_size(int);
85355d6bb5Sswilcox static void cg_constants(int, daddr32_t *, daddr32_t *, daddr32_t *,
86355d6bb5Sswilcox 	    daddr32_t *, daddr32_t *, daddr32_t *);
877c478bd9Sstevel@tonic-gate 
88355d6bb5Sswilcox int
89355d6bb5Sswilcox ftypeok(struct dinode *dp)
907c478bd9Sstevel@tonic-gate {
917c478bd9Sstevel@tonic-gate 	switch (dp->di_mode & IFMT) {
927c478bd9Sstevel@tonic-gate 
937c478bd9Sstevel@tonic-gate 	case IFDIR:
947c478bd9Sstevel@tonic-gate 	case IFREG:
957c478bd9Sstevel@tonic-gate 	case IFBLK:
967c478bd9Sstevel@tonic-gate 	case IFCHR:
977c478bd9Sstevel@tonic-gate 	case IFLNK:
987c478bd9Sstevel@tonic-gate 	case IFSOCK:
997c478bd9Sstevel@tonic-gate 	case IFIFO:
1007c478bd9Sstevel@tonic-gate 	case IFSHAD:
1017c478bd9Sstevel@tonic-gate 	case IFATTRDIR:
1027c478bd9Sstevel@tonic-gate 		return (1);
1037c478bd9Sstevel@tonic-gate 
1047c478bd9Sstevel@tonic-gate 	default:
1057c478bd9Sstevel@tonic-gate 		if (debug)
106355d6bb5Sswilcox 			(void) printf("bad file type 0%o\n", dp->di_mode);
1077c478bd9Sstevel@tonic-gate 		return (0);
1087c478bd9Sstevel@tonic-gate 	}
1097c478bd9Sstevel@tonic-gate }
1107c478bd9Sstevel@tonic-gate 
111355d6bb5Sswilcox int
112355d6bb5Sswilcox acltypeok(struct dinode *dp)
1137c478bd9Sstevel@tonic-gate {
1147c478bd9Sstevel@tonic-gate 	if (CHECK_ACL_ALLOWED(dp->di_mode & IFMT))
1157c478bd9Sstevel@tonic-gate 		return (1);
1167c478bd9Sstevel@tonic-gate 
1177c478bd9Sstevel@tonic-gate 	if (debug)
118355d6bb5Sswilcox 		(void) printf("bad file type for acl I=%d: 0%o\n",
119355d6bb5Sswilcox 		    dp->di_shadow, dp->di_mode);
1207c478bd9Sstevel@tonic-gate 	return (0);
1217c478bd9Sstevel@tonic-gate }
1227c478bd9Sstevel@tonic-gate 
123355d6bb5Sswilcox NOTE(PRINTFLIKE(1))
124355d6bb5Sswilcox int
125355d6bb5Sswilcox reply(caddr_t fmt, ...)
1267c478bd9Sstevel@tonic-gate {
127355d6bb5Sswilcox 	va_list ap;
1287c478bd9Sstevel@tonic-gate 	char line[80];
1297c478bd9Sstevel@tonic-gate 
1307c478bd9Sstevel@tonic-gate 	if (preen)
131355d6bb5Sswilcox 		pfatal("INTERNAL ERROR: GOT TO reply() in preen mode");
1327c478bd9Sstevel@tonic-gate 
1337c478bd9Sstevel@tonic-gate 	if (mflag) {
134355d6bb5Sswilcox 		/*
135355d6bb5Sswilcox 		 * We don't know what's going on, so don't potentially
136355d6bb5Sswilcox 		 * make things worse by having errexit() write stuff
137355d6bb5Sswilcox 		 * out to disk.
138355d6bb5Sswilcox 		 */
139355d6bb5Sswilcox 		(void) printf(
140355d6bb5Sswilcox 		    "\n%s: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY.\n",
141355d6bb5Sswilcox 		    devname);
142355d6bb5Sswilcox 		exit(EXERRFATAL);
1437c478bd9Sstevel@tonic-gate 	}
1447c478bd9Sstevel@tonic-gate 
145355d6bb5Sswilcox 	va_start(ap, fmt);
146355d6bb5Sswilcox 	(void) putchar('\n');
147355d6bb5Sswilcox 	(void) vprintf(fmt, ap);
148355d6bb5Sswilcox 	(void) putchar('?');
149355d6bb5Sswilcox 	(void) putchar(' ');
150355d6bb5Sswilcox 	va_end(ap);
151355d6bb5Sswilcox 
1527c478bd9Sstevel@tonic-gate 	if (nflag || fswritefd < 0) {
153355d6bb5Sswilcox 		(void) printf(" no\n\n");
1547c478bd9Sstevel@tonic-gate 		return (0);
1557c478bd9Sstevel@tonic-gate 	}
1567c478bd9Sstevel@tonic-gate 	if (yflag) {
157355d6bb5Sswilcox 		(void) printf(" yes\n\n");
1587c478bd9Sstevel@tonic-gate 		return (1);
1597c478bd9Sstevel@tonic-gate 	}
160355d6bb5Sswilcox 	(void) fflush(stdout);
1617c478bd9Sstevel@tonic-gate 	if (getline(stdin, line, sizeof (line)) == EOF)
1627c478bd9Sstevel@tonic-gate 		errexit("\n");
163355d6bb5Sswilcox 	(void) printf("\n");
164355d6bb5Sswilcox 	if (line[0] == 'y' || line[0] == 'Y') {
1657c478bd9Sstevel@tonic-gate 		return (1);
166355d6bb5Sswilcox 	} else {
1677c478bd9Sstevel@tonic-gate 		return (0);
1687c478bd9Sstevel@tonic-gate 	}
1697c478bd9Sstevel@tonic-gate }
1707c478bd9Sstevel@tonic-gate 
171355d6bb5Sswilcox int
172355d6bb5Sswilcox getline(FILE *fp, caddr_t loc, int maxlen)
1737c478bd9Sstevel@tonic-gate {
1747c478bd9Sstevel@tonic-gate 	int n;
175355d6bb5Sswilcox 	caddr_t p, lastloc;
1767c478bd9Sstevel@tonic-gate 
1777c478bd9Sstevel@tonic-gate 	p = loc;
1787c478bd9Sstevel@tonic-gate 	lastloc = &p[maxlen-1];
1797c478bd9Sstevel@tonic-gate 	while ((n = getc(fp)) != '\n') {
1807c478bd9Sstevel@tonic-gate 		if (n == EOF)
1817c478bd9Sstevel@tonic-gate 			return (EOF);
1827c478bd9Sstevel@tonic-gate 		if (!isspace(n) && p < lastloc)
183355d6bb5Sswilcox 			*p++ = (char)n;
1847c478bd9Sstevel@tonic-gate 	}
185355d6bb5Sswilcox 	*p = '\0';
186355d6bb5Sswilcox 	/* LINTED pointer difference won't overflow */
1877c478bd9Sstevel@tonic-gate 	return (p - loc);
1887c478bd9Sstevel@tonic-gate }
189355d6bb5Sswilcox 
1907c478bd9Sstevel@tonic-gate /*
1917c478bd9Sstevel@tonic-gate  * Malloc buffers and set up cache.
1927c478bd9Sstevel@tonic-gate  */
193355d6bb5Sswilcox void
194355d6bb5Sswilcox bufinit(void)
1957c478bd9Sstevel@tonic-gate {
1967c478bd9Sstevel@tonic-gate 	struct bufarea *bp;
1977c478bd9Sstevel@tonic-gate 	int bufcnt, i;
198355d6bb5Sswilcox 	caddr_t bufp;
1997c478bd9Sstevel@tonic-gate 
200355d6bb5Sswilcox 	bufp = malloc((size_t)sblock.fs_bsize);
201355d6bb5Sswilcox 	if (bufp == NULL)
202355d6bb5Sswilcox 		goto nomem;
2037c478bd9Sstevel@tonic-gate 	initbarea(&cgblk);
204355d6bb5Sswilcox 	cgblk.b_un.b_buf = bufp;
2057c478bd9Sstevel@tonic-gate 	bufhead.b_next = bufhead.b_prev = &bufhead;
2067c478bd9Sstevel@tonic-gate 	bufcnt = MAXBUFSPACE / sblock.fs_bsize;
2077c478bd9Sstevel@tonic-gate 	if (bufcnt < MINBUFS)
2087c478bd9Sstevel@tonic-gate 		bufcnt = MINBUFS;
2097c478bd9Sstevel@tonic-gate 	for (i = 0; i < bufcnt; i++) {
2107c478bd9Sstevel@tonic-gate 		bp = (struct bufarea *)malloc(sizeof (struct bufarea));
211355d6bb5Sswilcox 		if (bp == NULL) {
2127c478bd9Sstevel@tonic-gate 			if (i >= MINBUFS)
213355d6bb5Sswilcox 				goto noalloc;
214355d6bb5Sswilcox 			goto nomem;
215355d6bb5Sswilcox 		}
216355d6bb5Sswilcox 
217355d6bb5Sswilcox 		bufp = malloc((size_t)sblock.fs_bsize);
218355d6bb5Sswilcox 		if (bufp == NULL) {
219355d6bb5Sswilcox 			free((void *)bp);
220355d6bb5Sswilcox 			if (i >= MINBUFS)
221355d6bb5Sswilcox 				goto noalloc;
222355d6bb5Sswilcox 			goto nomem;
2237c478bd9Sstevel@tonic-gate 		}
224355d6bb5Sswilcox 		initbarea(bp);
2257c478bd9Sstevel@tonic-gate 		bp->b_un.b_buf = bufp;
2267c478bd9Sstevel@tonic-gate 		bp->b_prev = &bufhead;
2277c478bd9Sstevel@tonic-gate 		bp->b_next = bufhead.b_next;
2287c478bd9Sstevel@tonic-gate 		bufhead.b_next->b_prev = bp;
2297c478bd9Sstevel@tonic-gate 		bufhead.b_next = bp;
2307c478bd9Sstevel@tonic-gate 	}
231355d6bb5Sswilcox noalloc:
2327c478bd9Sstevel@tonic-gate 	bufhead.b_size = i;	/* save number of buffers */
2337c478bd9Sstevel@tonic-gate 	pbp = pdirbp = NULL;
234355d6bb5Sswilcox 	return;
235355d6bb5Sswilcox 
236355d6bb5Sswilcox nomem:
237355d6bb5Sswilcox 	errexit("cannot allocate buffer pool\n");
238355d6bb5Sswilcox 	/* NOTREACHED */
239355d6bb5Sswilcox }
240355d6bb5Sswilcox 
241355d6bb5Sswilcox /*
242355d6bb5Sswilcox  * Undo a bufinit().
243355d6bb5Sswilcox  */
244355d6bb5Sswilcox void
245355d6bb5Sswilcox unbufinit(void)
246355d6bb5Sswilcox {
247355d6bb5Sswilcox 	int cnt;
248355d6bb5Sswilcox 	struct bufarea *bp, *nbp;
249355d6bb5Sswilcox 
250355d6bb5Sswilcox 	cnt = 0;
251355d6bb5Sswilcox 	for (bp = bufhead.b_prev; bp != NULL && bp != &bufhead; bp = nbp) {
252355d6bb5Sswilcox 		cnt++;
253355d6bb5Sswilcox 		flush(fswritefd, bp);
254355d6bb5Sswilcox 		nbp = bp->b_prev;
255355d6bb5Sswilcox 		/*
256355d6bb5Sswilcox 		 * We're discarding the entire chain, so this isn't
257355d6bb5Sswilcox 		 * technically necessary.  However, it doesn't hurt
258355d6bb5Sswilcox 		 * and lint's data flow analysis is much happier
259355d6bb5Sswilcox 		 * (this prevents it from thinking there's a chance
260355d6bb5Sswilcox 		 * of our using memory elsewhere after it's been released).
261355d6bb5Sswilcox 		 */
262355d6bb5Sswilcox 		nbp->b_next = bp->b_next;
263355d6bb5Sswilcox 		bp->b_next->b_prev = nbp;
264355d6bb5Sswilcox 		free((void *)bp->b_un.b_buf);
265355d6bb5Sswilcox 		free((void *)bp);
266355d6bb5Sswilcox 	}
267355d6bb5Sswilcox 
268355d6bb5Sswilcox 	if (bufhead.b_size != cnt)
269355d6bb5Sswilcox 		errexit("Panic: cache lost %d buffers\n",
270355d6bb5Sswilcox 			bufhead.b_size - cnt);
2717c478bd9Sstevel@tonic-gate }
2727c478bd9Sstevel@tonic-gate 
2737c478bd9Sstevel@tonic-gate /*
2747c478bd9Sstevel@tonic-gate  * Manage a cache of directory blocks.
2757c478bd9Sstevel@tonic-gate  */
2767c478bd9Sstevel@tonic-gate struct bufarea *
277355d6bb5Sswilcox getdatablk(daddr32_t blkno, size_t size)
2787c478bd9Sstevel@tonic-gate {
2797c478bd9Sstevel@tonic-gate 	struct bufarea *bp;
2807c478bd9Sstevel@tonic-gate 
2817c478bd9Sstevel@tonic-gate 	for (bp = bufhead.b_next; bp != &bufhead; bp = bp->b_next)
282355d6bb5Sswilcox 		if (bp->b_bno == fsbtodb(&sblock, blkno)) {
2837c478bd9Sstevel@tonic-gate 			goto foundit;
284355d6bb5Sswilcox 		}
2857c478bd9Sstevel@tonic-gate 	for (bp = bufhead.b_prev; bp != &bufhead; bp = bp->b_prev)
2867c478bd9Sstevel@tonic-gate 		if ((bp->b_flags & B_INUSE) == 0)
2877c478bd9Sstevel@tonic-gate 			break;
2887c478bd9Sstevel@tonic-gate 	if (bp == &bufhead) {
2897c478bd9Sstevel@tonic-gate 		bp = alloc_bufarea();
290355d6bb5Sswilcox 		if (bp == NULL) {
2917c478bd9Sstevel@tonic-gate 			errexit("deadlocked buffer pool\n");
292355d6bb5Sswilcox 			/* NOTREACHED */
293355d6bb5Sswilcox 		}
2947c478bd9Sstevel@tonic-gate 	}
295355d6bb5Sswilcox 	/*
296355d6bb5Sswilcox 	 * We're at the same logical level as getblk(), so if there
297355d6bb5Sswilcox 	 * are any errors, we'll let our caller handle them.
298355d6bb5Sswilcox 	 */
299355d6bb5Sswilcox 	diskreads++;
300355d6bb5Sswilcox 	(void) getblk(bp, blkno, size);
301355d6bb5Sswilcox 
3027c478bd9Sstevel@tonic-gate foundit:
3037c478bd9Sstevel@tonic-gate 	totalreads++;
3047c478bd9Sstevel@tonic-gate 	bp->b_cnt++;
3057c478bd9Sstevel@tonic-gate 	/*
306355d6bb5Sswilcox 	 * Move the buffer to head of linked list if it isn't
3077c478bd9Sstevel@tonic-gate 	 * already there.
3087c478bd9Sstevel@tonic-gate 	 */
3097c478bd9Sstevel@tonic-gate 	if (bufhead.b_next != bp) {
3107c478bd9Sstevel@tonic-gate 		bp->b_prev->b_next = bp->b_next;
3117c478bd9Sstevel@tonic-gate 		bp->b_next->b_prev = bp->b_prev;
3127c478bd9Sstevel@tonic-gate 		bp->b_prev = &bufhead;
3137c478bd9Sstevel@tonic-gate 		bp->b_next = bufhead.b_next;
3147c478bd9Sstevel@tonic-gate 		bufhead.b_next->b_prev = bp;
3157c478bd9Sstevel@tonic-gate 		bufhead.b_next = bp;
3167c478bd9Sstevel@tonic-gate 	}
3177c478bd9Sstevel@tonic-gate 	bp->b_flags |= B_INUSE;
3187c478bd9Sstevel@tonic-gate 	return (bp);
3197c478bd9Sstevel@tonic-gate }
3207c478bd9Sstevel@tonic-gate 
321355d6bb5Sswilcox void
3227c478bd9Sstevel@tonic-gate brelse(struct bufarea *bp)
3237c478bd9Sstevel@tonic-gate {
3247c478bd9Sstevel@tonic-gate 	bp->b_cnt--;
3257c478bd9Sstevel@tonic-gate 	if (bp->b_cnt == 0) {
3267c478bd9Sstevel@tonic-gate 		bp->b_flags &= ~B_INUSE;
3277c478bd9Sstevel@tonic-gate 	}
3287c478bd9Sstevel@tonic-gate }
3297c478bd9Sstevel@tonic-gate 
3307c478bd9Sstevel@tonic-gate struct bufarea *
331355d6bb5Sswilcox getblk(struct bufarea *bp, daddr32_t blk, size_t size)
3327c478bd9Sstevel@tonic-gate {
3337c478bd9Sstevel@tonic-gate 	diskaddr_t dblk;
3347c478bd9Sstevel@tonic-gate 
3357c478bd9Sstevel@tonic-gate 	dblk = fsbtodb(&sblock, blk);
3367c478bd9Sstevel@tonic-gate 	if (bp->b_bno == dblk)
3377c478bd9Sstevel@tonic-gate 		return (bp);
3387c478bd9Sstevel@tonic-gate 	flush(fswritefd, bp);
339355d6bb5Sswilcox 	bp->b_errs = fsck_bread(fsreadfd, bp->b_un.b_buf, dblk, size);
3407c478bd9Sstevel@tonic-gate 	bp->b_bno = dblk;
3417c478bd9Sstevel@tonic-gate 	bp->b_size = size;
3427c478bd9Sstevel@tonic-gate 	return (bp);
3437c478bd9Sstevel@tonic-gate }
3447c478bd9Sstevel@tonic-gate 
345355d6bb5Sswilcox void
346355d6bb5Sswilcox flush(int fd, struct bufarea *bp)
3477c478bd9Sstevel@tonic-gate {
3487c478bd9Sstevel@tonic-gate 	int i, j;
3497c478bd9Sstevel@tonic-gate 	caddr_t sip;
3507c478bd9Sstevel@tonic-gate 	long size;
3517c478bd9Sstevel@tonic-gate 
3527c478bd9Sstevel@tonic-gate 	if (!bp->b_dirty)
3537c478bd9Sstevel@tonic-gate 		return;
354355d6bb5Sswilcox 
355355d6bb5Sswilcox 	/*
356355d6bb5Sswilcox 	 * It's not our buf, so if there are errors, let whoever
357355d6bb5Sswilcox 	 * acquired it deal with the actual problem.
358355d6bb5Sswilcox 	 */
3597c478bd9Sstevel@tonic-gate 	if (bp->b_errs != 0)
3607c478bd9Sstevel@tonic-gate 		pfatal("WRITING ZERO'ED BLOCK %lld TO DISK\n", bp->b_bno);
3617c478bd9Sstevel@tonic-gate 	bp->b_dirty = 0;
3627c478bd9Sstevel@tonic-gate 	bp->b_errs = 0;
3637c478bd9Sstevel@tonic-gate 	bwrite(fd, bp->b_un.b_buf, bp->b_bno, (long)bp->b_size);
364355d6bb5Sswilcox 	if (bp != &sblk) {
3657c478bd9Sstevel@tonic-gate 		return;
366355d6bb5Sswilcox 	}
367355d6bb5Sswilcox 
368355d6bb5Sswilcox 	/*
369355d6bb5Sswilcox 	 * We're flushing the superblock, so make sure all the
370355d6bb5Sswilcox 	 * ancillary bits go out as well.
371355d6bb5Sswilcox 	 */
3727c478bd9Sstevel@tonic-gate 	sip = (caddr_t)sblock.fs_u.fs_csp;
3737c478bd9Sstevel@tonic-gate 	for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) {
3747c478bd9Sstevel@tonic-gate 		size = sblock.fs_cssize - i < sblock.fs_bsize ?
3757c478bd9Sstevel@tonic-gate 		    sblock.fs_cssize - i : sblock.fs_bsize;
3767c478bd9Sstevel@tonic-gate 		bwrite(fswritefd, sip,
3777c478bd9Sstevel@tonic-gate 		    fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag),
3787c478bd9Sstevel@tonic-gate 		    size);
3797c478bd9Sstevel@tonic-gate 		sip += size;
3807c478bd9Sstevel@tonic-gate 	}
3817c478bd9Sstevel@tonic-gate }
3827c478bd9Sstevel@tonic-gate 
383355d6bb5Sswilcox static void
384355d6bb5Sswilcox rwerror(caddr_t mesg, diskaddr_t blk, int rval)
3857c478bd9Sstevel@tonic-gate {
386355d6bb5Sswilcox 	int olderr = errno;
387355d6bb5Sswilcox 
388355d6bb5Sswilcox 	if (!preen)
389355d6bb5Sswilcox 		(void) printf("\n");
3907c478bd9Sstevel@tonic-gate 
391355d6bb5Sswilcox 	if (rval == -1)
392355d6bb5Sswilcox 		pfatal("CANNOT %s: DISK BLOCK %lld: %s",
393355d6bb5Sswilcox 		    mesg, blk, strerror(olderr));
394355d6bb5Sswilcox 	else
395355d6bb5Sswilcox 		pfatal("CANNOT %s: DISK BLOCK %lld", mesg, blk);
396355d6bb5Sswilcox 
397355d6bb5Sswilcox 	if (reply("CONTINUE") == 0) {
398355d6bb5Sswilcox 		exitstat = EXERRFATAL;
3997c478bd9Sstevel@tonic-gate 		errexit("Program terminated\n");
400355d6bb5Sswilcox 	}
4017c478bd9Sstevel@tonic-gate }
4027c478bd9Sstevel@tonic-gate 
403355d6bb5Sswilcox void
404355d6bb5Sswilcox ckfini(void)
4057c478bd9Sstevel@tonic-gate {
406355d6bb5Sswilcox 	int64_t percentage;
407355d6bb5Sswilcox 
408355d6bb5Sswilcox 	if (fswritefd < 0)
409355d6bb5Sswilcox 		return;
4107c478bd9Sstevel@tonic-gate 
411355d6bb5Sswilcox 	flush(fswritefd, &sblk);
4127c478bd9Sstevel@tonic-gate 	/*
413355d6bb5Sswilcox 	 * Were we using a backup superblock?
4147c478bd9Sstevel@tonic-gate 	 */
4157c478bd9Sstevel@tonic-gate 	if (havesb && sblk.b_bno != SBOFF / dev_bsize) {
416355d6bb5Sswilcox 		if (preen || reply("UPDATE STANDARD SUPERBLOCK") == 1) {
417355d6bb5Sswilcox 			sblk.b_bno = SBOFF / dev_bsize;
418355d6bb5Sswilcox 			sbdirty();
419355d6bb5Sswilcox 			flush(fswritefd, &sblk);
420355d6bb5Sswilcox 		}
4217c478bd9Sstevel@tonic-gate 	}
4227c478bd9Sstevel@tonic-gate 	flush(fswritefd, &cgblk);
423355d6bb5Sswilcox 	if (cgblk.b_un.b_buf != NULL) {
424355d6bb5Sswilcox 		free((void *)cgblk.b_un.b_buf);
4257c478bd9Sstevel@tonic-gate 		cgblk.b_un.b_buf = NULL;
4267c478bd9Sstevel@tonic-gate 	}
427355d6bb5Sswilcox 	unbufinit();
428355d6bb5Sswilcox 	pbp = NULL;
429355d6bb5Sswilcox 	pdirbp = NULL;
430355d6bb5Sswilcox 	if (debug) {
431355d6bb5Sswilcox 		/*
432355d6bb5Sswilcox 		 * Note that we only count cache-related reads.
433355d6bb5Sswilcox 		 * Anything that called fsck_bread() or getblk()
434355d6bb5Sswilcox 		 * directly are explicitly not cached, so they're not
435355d6bb5Sswilcox 		 * included here.
436355d6bb5Sswilcox 		 */
437355d6bb5Sswilcox 		if (totalreads != 0)
438355d6bb5Sswilcox 			percentage = diskreads * 100 / totalreads;
439355d6bb5Sswilcox 		else
440355d6bb5Sswilcox 			percentage = 0;
441355d6bb5Sswilcox 
442355d6bb5Sswilcox 		(void) printf("cache missed %lld of %lld reads (%lld%%)\n",
443355d6bb5Sswilcox 		    (longlong_t)diskreads, (longlong_t)totalreads,
444355d6bb5Sswilcox 		    (longlong_t)percentage);
4457c478bd9Sstevel@tonic-gate 	}
446355d6bb5Sswilcox 
4477c478bd9Sstevel@tonic-gate 	(void) close(fsreadfd);
4487c478bd9Sstevel@tonic-gate 	(void) close(fswritefd);
449355d6bb5Sswilcox 	fsreadfd = -1;
450355d6bb5Sswilcox 	fswritefd = -1;
4517c478bd9Sstevel@tonic-gate }
4527c478bd9Sstevel@tonic-gate 
453355d6bb5Sswilcox int
454355d6bb5Sswilcox fsck_bread(int fd, caddr_t buf, diskaddr_t blk, size_t size)
4557c478bd9Sstevel@tonic-gate {
456355d6bb5Sswilcox 	caddr_t cp;
457355d6bb5Sswilcox 	int i;
4587c478bd9Sstevel@tonic-gate 	int errs;
4597c478bd9Sstevel@tonic-gate 	offset_t offset = ldbtob(blk);
4607c478bd9Sstevel@tonic-gate 	offset_t addr;
4617c478bd9Sstevel@tonic-gate 
462355d6bb5Sswilcox 	/*
463355d6bb5Sswilcox 	 * In our universe, nothing exists before the superblock, so
464355d6bb5Sswilcox 	 * just pretend it's always zeros.  This is the complement of
465355d6bb5Sswilcox 	 * bwrite()'s ignoring write requests into that space.
466355d6bb5Sswilcox 	 */
467355d6bb5Sswilcox 	if (blk < SBLOCK) {
468355d6bb5Sswilcox 		if (debug)
469355d6bb5Sswilcox 			(void) printf(
470355d6bb5Sswilcox 			    "WARNING: fsck_bread() passed blkno < %d (%lld)\n",
471355d6bb5Sswilcox 			    SBLOCK, (longlong_t)blk);
472355d6bb5Sswilcox 		(void) memset(buf, 0, (size_t)size);
473355d6bb5Sswilcox 		return (1);
4747c478bd9Sstevel@tonic-gate 	}
475355d6bb5Sswilcox 
476*f763a6cdScasper 	if (llseek(fd, offset, SEEK_SET) < 0) {
477355d6bb5Sswilcox 		rwerror("SEEK", blk, -1);
478355d6bb5Sswilcox 	}
479355d6bb5Sswilcox 
480355d6bb5Sswilcox 	if ((i = read(fd, buf, size)) == size) {
4817c478bd9Sstevel@tonic-gate 		return (0);
482355d6bb5Sswilcox 	}
483355d6bb5Sswilcox 	rwerror("READ", blk, i);
484*f763a6cdScasper 	if (llseek(fd, offset, SEEK_SET) < 0) {
485355d6bb5Sswilcox 		rwerror("SEEK", blk, -1);
4867c478bd9Sstevel@tonic-gate 	}
4877c478bd9Sstevel@tonic-gate 	errs = 0;
488355d6bb5Sswilcox 	(void) memset(buf, 0, (size_t)size);
4897c478bd9Sstevel@tonic-gate 	pwarn("THE FOLLOWING SECTORS COULD NOT BE READ:");
4907c478bd9Sstevel@tonic-gate 	for (cp = buf, i = 0; i < btodb(size); i++, cp += DEV_BSIZE) {
4917c478bd9Sstevel@tonic-gate 		addr = ldbtob(blk + i);
492*f763a6cdScasper 		if (llseek(fd, addr, SEEK_SET) < 0 ||
4937c478bd9Sstevel@tonic-gate 		    read(fd, cp, (int)secsize) < 0) {
494355d6bb5Sswilcox 			iscorrupt = 1;
495355d6bb5Sswilcox 			(void) printf(" %llu", blk + (u_longlong_t)i);
4967c478bd9Sstevel@tonic-gate 			errs++;
4977c478bd9Sstevel@tonic-gate 		}
4987c478bd9Sstevel@tonic-gate 	}
499355d6bb5Sswilcox 	(void) printf("\n");
5007c478bd9Sstevel@tonic-gate 	return (errs);
5017c478bd9Sstevel@tonic-gate }
5027c478bd9Sstevel@tonic-gate 
503355d6bb5Sswilcox void
504355d6bb5Sswilcox bwrite(int fd, caddr_t buf, diskaddr_t blk, int64_t size)
5057c478bd9Sstevel@tonic-gate {
506355d6bb5Sswilcox 	int i;
5077c478bd9Sstevel@tonic-gate 	int n;
508355d6bb5Sswilcox 	caddr_t cp;
5097c478bd9Sstevel@tonic-gate 	offset_t offset = ldbtob(blk);
5107c478bd9Sstevel@tonic-gate 	offset_t addr;
5117c478bd9Sstevel@tonic-gate 
5127c478bd9Sstevel@tonic-gate 	if (fd < 0)
5137c478bd9Sstevel@tonic-gate 		return;
5147c478bd9Sstevel@tonic-gate 	if (blk < SBLOCK) {
5157c478bd9Sstevel@tonic-gate 		if (debug)
516355d6bb5Sswilcox 			(void) printf(
517355d6bb5Sswilcox 		    "WARNING: Attempt to write illegal blkno %lld on %s\n",
518355d6bb5Sswilcox 			    (longlong_t)blk, devname);
5197c478bd9Sstevel@tonic-gate 		return;
5207c478bd9Sstevel@tonic-gate 	}
521*f763a6cdScasper 	if (llseek(fd, offset, SEEK_SET) < 0) {
522355d6bb5Sswilcox 		rwerror("SEEK", blk, -1);
523355d6bb5Sswilcox 	}
524355d6bb5Sswilcox 	if ((i = write(fd, buf, (int)size)) == size) {
5257c478bd9Sstevel@tonic-gate 		fsmodified = 1;
5267c478bd9Sstevel@tonic-gate 		return;
5277c478bd9Sstevel@tonic-gate 	}
528355d6bb5Sswilcox 	rwerror("WRITE", blk, i);
529*f763a6cdScasper 	if (llseek(fd, offset, SEEK_SET) < 0) {
530355d6bb5Sswilcox 		rwerror("SEEK", blk, -1);
5317c478bd9Sstevel@tonic-gate 	}
5327c478bd9Sstevel@tonic-gate 	pwarn("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:");
5337c478bd9Sstevel@tonic-gate 	for (cp = buf, i = 0; i < btodb(size); i++, cp += DEV_BSIZE) {
5347c478bd9Sstevel@tonic-gate 		n = 0;
5357c478bd9Sstevel@tonic-gate 		addr = ldbtob(blk + i);
536*f763a6cdScasper 		if (llseek(fd, addr, SEEK_SET) < 0 ||
5377c478bd9Sstevel@tonic-gate 		    (n = write(fd, cp, DEV_BSIZE)) < 0) {
538355d6bb5Sswilcox 			iscorrupt = 1;
539355d6bb5Sswilcox 			(void) printf(" %llu", blk + (u_longlong_t)i);
5407c478bd9Sstevel@tonic-gate 		} else if (n > 0) {
5417c478bd9Sstevel@tonic-gate 			fsmodified = 1;
5427c478bd9Sstevel@tonic-gate 		}
5437c478bd9Sstevel@tonic-gate 
5447c478bd9Sstevel@tonic-gate 	}
545355d6bb5Sswilcox 	(void) printf("\n");
5467c478bd9Sstevel@tonic-gate }
5477c478bd9Sstevel@tonic-gate 
5487c478bd9Sstevel@tonic-gate /*
549355d6bb5Sswilcox  * Allocates the specified number of contiguous fragments.
5507c478bd9Sstevel@tonic-gate  */
5517c478bd9Sstevel@tonic-gate daddr32_t
552355d6bb5Sswilcox allocblk(int wantedfrags)
5537c478bd9Sstevel@tonic-gate {
554355d6bb5Sswilcox 	int block, leadfrag, tailfrag;
555355d6bb5Sswilcox 	daddr32_t selected;
556355d6bb5Sswilcox 	size_t size;
557355d6bb5Sswilcox 	struct bufarea *bp;
5587c478bd9Sstevel@tonic-gate 
559355d6bb5Sswilcox 	/*
560355d6bb5Sswilcox 	 * It's arguable whether we should just fail, or instead
561355d6bb5Sswilcox 	 * error out here.  Since we should only ever be asked for
562355d6bb5Sswilcox 	 * a single fragment or an entire block (i.e., sblock.fs_frag),
563355d6bb5Sswilcox 	 * we'll fail out because anything else means somebody
564355d6bb5Sswilcox 	 * changed code without considering all of the ramifications.
565355d6bb5Sswilcox 	 */
566355d6bb5Sswilcox 	if (wantedfrags <= 0 || wantedfrags > sblock.fs_frag) {
567355d6bb5Sswilcox 		exitstat = EXERRFATAL;
568355d6bb5Sswilcox 		errexit("allocblk() asked for %d frags.  "
569355d6bb5Sswilcox 			"Legal range is 1 to %d",
570355d6bb5Sswilcox 			wantedfrags, sblock.fs_frag);
571355d6bb5Sswilcox 	}
572355d6bb5Sswilcox 
573355d6bb5Sswilcox 	/*
574355d6bb5Sswilcox 	 * For each filesystem block, look at every possible starting
575355d6bb5Sswilcox 	 * offset within the block such that we can get the number of
576355d6bb5Sswilcox 	 * contiguous fragments that we need.  This is a drastically
577355d6bb5Sswilcox 	 * simplified version of the kernel's mapsearch() and alloc*().
578355d6bb5Sswilcox 	 * It's also correspondingly slower.
579355d6bb5Sswilcox 	 */
580355d6bb5Sswilcox 	for (block = 0; block < maxfsblock - sblock.fs_frag;
581355d6bb5Sswilcox 	    block += sblock.fs_frag) {
582355d6bb5Sswilcox 		for (leadfrag = 0; leadfrag <= sblock.fs_frag - wantedfrags;
583355d6bb5Sswilcox 		    leadfrag++) {
584355d6bb5Sswilcox 			/*
585355d6bb5Sswilcox 			 * Is first fragment of candidate run available?
586355d6bb5Sswilcox 			 */
587355d6bb5Sswilcox 			if (testbmap(block + leadfrag))
5887c478bd9Sstevel@tonic-gate 				continue;
589355d6bb5Sswilcox 			/*
590355d6bb5Sswilcox 			 * Are the rest of them available?
591355d6bb5Sswilcox 			 */
592355d6bb5Sswilcox 			for (tailfrag = 1; tailfrag < wantedfrags; tailfrag++)
593355d6bb5Sswilcox 				if (testbmap(block + leadfrag + tailfrag))
5947c478bd9Sstevel@tonic-gate 					break;
595355d6bb5Sswilcox 			if (tailfrag < wantedfrags) {
596355d6bb5Sswilcox 				/*
597355d6bb5Sswilcox 				 * No, skip the known-unusable run.
598355d6bb5Sswilcox 				 */
599355d6bb5Sswilcox 				leadfrag += tailfrag;
6007c478bd9Sstevel@tonic-gate 				continue;
6017c478bd9Sstevel@tonic-gate 			}
602355d6bb5Sswilcox 			/*
603355d6bb5Sswilcox 			 * Found what we need, so claim them.
604355d6bb5Sswilcox 			 */
605355d6bb5Sswilcox 			for (tailfrag = 0; tailfrag < wantedfrags; tailfrag++)
606355d6bb5Sswilcox 				setbmap(block + leadfrag + tailfrag);
607355d6bb5Sswilcox 			n_blks += wantedfrags;
608355d6bb5Sswilcox 			size = wantedfrags * sblock.fs_fsize;
609355d6bb5Sswilcox 			selected = block + leadfrag;
610355d6bb5Sswilcox 			bp = getdatablk(selected, size);
611355d6bb5Sswilcox 			(void) memset((void *)bp->b_un.b_buf, 0, size);
612355d6bb5Sswilcox 			dirty(bp);
613355d6bb5Sswilcox 			brelse(bp);
614355d6bb5Sswilcox 			if (debug)
615355d6bb5Sswilcox 				(void) printf(
616355d6bb5Sswilcox 		    "allocblk: selected %d (in block %d), frags %d, size %d\n",
617355d6bb5Sswilcox 				    selected, selected % sblock.fs_bsize,
618355d6bb5Sswilcox 				    wantedfrags, (int)size);
619355d6bb5Sswilcox 			return (selected);
6207c478bd9Sstevel@tonic-gate 		}
6217c478bd9Sstevel@tonic-gate 	}
6227c478bd9Sstevel@tonic-gate 	return (0);
6237c478bd9Sstevel@tonic-gate }
6247c478bd9Sstevel@tonic-gate 
6257c478bd9Sstevel@tonic-gate /*
6267c478bd9Sstevel@tonic-gate  * Free a previously allocated block
6277c478bd9Sstevel@tonic-gate  */
628355d6bb5Sswilcox void
629355d6bb5Sswilcox freeblk(fsck_ino_t ino, daddr32_t blkno, int frags)
6307c478bd9Sstevel@tonic-gate {
6317c478bd9Sstevel@tonic-gate 	struct inodesc idesc;
6327c478bd9Sstevel@tonic-gate 
633355d6bb5Sswilcox 	if (debug)
634355d6bb5Sswilcox 		(void) printf("debug: freeing %d fragments starting at %d\n",
635355d6bb5Sswilcox 		    frags, blkno);
636355d6bb5Sswilcox 
637355d6bb5Sswilcox 	init_inodesc(&idesc);
638355d6bb5Sswilcox 
639355d6bb5Sswilcox 	idesc.id_number = ino;
6407c478bd9Sstevel@tonic-gate 	idesc.id_blkno = blkno;
6417c478bd9Sstevel@tonic-gate 	idesc.id_numfrags = frags;
642355d6bb5Sswilcox 	idesc.id_truncto = -1;
643355d6bb5Sswilcox 
644355d6bb5Sswilcox 	/*
645355d6bb5Sswilcox 	 * Nothing in the return status has any relevance to how
646355d6bb5Sswilcox 	 * we're using pass4check(), so just ignore it.
647355d6bb5Sswilcox 	 */
648355d6bb5Sswilcox 	(void) pass4check(&idesc);
6497c478bd9Sstevel@tonic-gate }
6507c478bd9Sstevel@tonic-gate 
6517c478bd9Sstevel@tonic-gate /*
652355d6bb5Sswilcox  * Fill NAMEBUF with a path starting in CURDIR for INO.  Assumes
653355d6bb5Sswilcox  * that the given buffer is at least MAXPATHLEN + 1 characters.
6547c478bd9Sstevel@tonic-gate  */
655355d6bb5Sswilcox void
656355d6bb5Sswilcox getpathname(caddr_t namebuf, fsck_ino_t curdir, fsck_ino_t ino)
6577c478bd9Sstevel@tonic-gate {
6587c478bd9Sstevel@tonic-gate 	int len;
659355d6bb5Sswilcox 	caddr_t cp;
660355d6bb5Sswilcox 	struct dinode *dp;
6617c478bd9Sstevel@tonic-gate 	struct inodesc idesc;
6627c478bd9Sstevel@tonic-gate 	struct inoinfo *inp;
6637c478bd9Sstevel@tonic-gate 
664355d6bb5Sswilcox 	if (debug)
665355d6bb5Sswilcox 		(void) printf("debug: getpathname(curdir %d, ino %d)\n",
666355d6bb5Sswilcox 		    curdir, ino);
667355d6bb5Sswilcox 
668355d6bb5Sswilcox 	if ((curdir == 0) || (!INO_IS_DVALID(curdir))) {
669355d6bb5Sswilcox 		(void) strcpy(namebuf, "?");
670355d6bb5Sswilcox 		return;
671355d6bb5Sswilcox 	}
672355d6bb5Sswilcox 
673355d6bb5Sswilcox 	if ((curdir == UFSROOTINO) && (ino == UFSROOTINO)) {
674355d6bb5Sswilcox 		(void) strcpy(namebuf, "/");
6757c478bd9Sstevel@tonic-gate 		return;
6767c478bd9Sstevel@tonic-gate 	}
677355d6bb5Sswilcox 
678355d6bb5Sswilcox 	init_inodesc(&idesc);
6797c478bd9Sstevel@tonic-gate 	idesc.id_type = DATA;
6807c478bd9Sstevel@tonic-gate 	cp = &namebuf[MAXPATHLEN - 1];
6817c478bd9Sstevel@tonic-gate 	*cp = '\0';
682355d6bb5Sswilcox 
683355d6bb5Sswilcox 	/*
684355d6bb5Sswilcox 	 * In the case of extended attributes, our
685355d6bb5Sswilcox 	 * parent won't necessarily be a directory, so just
686355d6bb5Sswilcox 	 * return what we've found with a prefix indicating
687355d6bb5Sswilcox 	 * that it's an XATTR.  Presumably our caller will
688355d6bb5Sswilcox 	 * know what's going on and do something useful, like
689355d6bb5Sswilcox 	 * work out the path of the parent and then combine
690355d6bb5Sswilcox 	 * the two names.
691355d6bb5Sswilcox 	 *
692355d6bb5Sswilcox 	 * Can't use strcpy(), etc, because we've probably
693355d6bb5Sswilcox 	 * already got some name information in the buffer and
694355d6bb5Sswilcox 	 * the usual trailing \0 would lose it.
695355d6bb5Sswilcox 	 */
696355d6bb5Sswilcox 	dp = ginode(curdir);
697355d6bb5Sswilcox 	if ((dp->di_mode & IFMT) == IFATTRDIR) {
698355d6bb5Sswilcox 		idesc.id_number = curdir;
699355d6bb5Sswilcox 		idesc.id_parent = ino;
700355d6bb5Sswilcox 		idesc.id_func = findname;
701355d6bb5Sswilcox 		idesc.id_name = namebuf;
702355d6bb5Sswilcox 		idesc.id_fix = NOFIX;
703355d6bb5Sswilcox 		if ((ckinode(dp, &idesc, CKI_TRAVERSE) & FOUND) == 0) {
704355d6bb5Sswilcox 			*cp-- = '?';
705355d6bb5Sswilcox 		}
706355d6bb5Sswilcox 
707355d6bb5Sswilcox 		len = sizeof (XATTR_DIR_NAME) - 1;
708355d6bb5Sswilcox 		cp -= len;
709355d6bb5Sswilcox 		(void) memmove(cp, XATTR_DIR_NAME, len);
710355d6bb5Sswilcox 		goto attrname;
711355d6bb5Sswilcox 	}
712355d6bb5Sswilcox 
713355d6bb5Sswilcox 	/*
714355d6bb5Sswilcox 	 * If curdir == ino, need to get a handle on .. so we
715355d6bb5Sswilcox 	 * can search it for ino's name.  Otherwise, just search
716355d6bb5Sswilcox 	 * the given directory for ino.  Repeat until out of space
717355d6bb5Sswilcox 	 * or a full path has been built.
718355d6bb5Sswilcox 	 */
7197c478bd9Sstevel@tonic-gate 	if (curdir != ino) {
7207c478bd9Sstevel@tonic-gate 		idesc.id_parent = curdir;
7217c478bd9Sstevel@tonic-gate 		goto namelookup;
7227c478bd9Sstevel@tonic-gate 	}
723355d6bb5Sswilcox 	while (ino != UFSROOTINO && ino != 0) {
7247c478bd9Sstevel@tonic-gate 		idesc.id_number = ino;
7257c478bd9Sstevel@tonic-gate 		idesc.id_func = findino;
7267c478bd9Sstevel@tonic-gate 		idesc.id_name = "..";
7277c478bd9Sstevel@tonic-gate 		idesc.id_fix = NOFIX;
728355d6bb5Sswilcox 		if ((ckinode(ginode(ino), &idesc, CKI_TRAVERSE) & FOUND) == 0) {
7297c478bd9Sstevel@tonic-gate 			inp = getinoinfo(ino);
730355d6bb5Sswilcox 			if ((inp == NULL) || (inp->i_parent == 0)) {
7317c478bd9Sstevel@tonic-gate 				break;
732355d6bb5Sswilcox 			}
7337c478bd9Sstevel@tonic-gate 			idesc.id_parent = inp->i_parent;
7347c478bd9Sstevel@tonic-gate 		}
735355d6bb5Sswilcox 
736355d6bb5Sswilcox 		/*
737355d6bb5Sswilcox 		 * To get this far, id_parent must have the inode
738355d6bb5Sswilcox 		 * number for `..' in it.  By definition, that's got
739355d6bb5Sswilcox 		 * to be a directory, so search it for the inode of
740355d6bb5Sswilcox 		 * interest.
741355d6bb5Sswilcox 		 */
742355d6bb5Sswilcox namelookup:
7437c478bd9Sstevel@tonic-gate 		idesc.id_number = idesc.id_parent;
7447c478bd9Sstevel@tonic-gate 		idesc.id_parent = ino;
7457c478bd9Sstevel@tonic-gate 		idesc.id_func = findname;
7467c478bd9Sstevel@tonic-gate 		idesc.id_name = namebuf;
7477c478bd9Sstevel@tonic-gate 		idesc.id_fix = NOFIX;
748355d6bb5Sswilcox 		if ((ckinode(ginode(idesc.id_number),
749355d6bb5Sswilcox 		    &idesc, CKI_TRAVERSE) & FOUND) == 0) {
7507c478bd9Sstevel@tonic-gate 			break;
751355d6bb5Sswilcox 		}
752355d6bb5Sswilcox 		/*
753355d6bb5Sswilcox 		 * Prepend to what we've accumulated so far.  If
754355d6bb5Sswilcox 		 * there's not enough room for even one more path element
755355d6bb5Sswilcox 		 * (of the worst-case length), then bail out.
756355d6bb5Sswilcox 		 */
7577c478bd9Sstevel@tonic-gate 		len = strlen(namebuf);
7587c478bd9Sstevel@tonic-gate 		cp -= len;
7597c478bd9Sstevel@tonic-gate 		if (cp < &namebuf[MAXNAMLEN])
7607c478bd9Sstevel@tonic-gate 			break;
761355d6bb5Sswilcox 		(void) memmove(cp, namebuf, len);
7627c478bd9Sstevel@tonic-gate 		*--cp = '/';
763355d6bb5Sswilcox 
764355d6bb5Sswilcox 		/*
765355d6bb5Sswilcox 		 * Corner case for a looped-to-itself directory.
766355d6bb5Sswilcox 		 */
767355d6bb5Sswilcox 		if (ino == idesc.id_number)
768355d6bb5Sswilcox 			break;
769355d6bb5Sswilcox 
770355d6bb5Sswilcox 		/*
771355d6bb5Sswilcox 		 * Climb one level of the hierarchy.  In other words,
772355d6bb5Sswilcox 		 * the current .. becomes the inode to search for and
773355d6bb5Sswilcox 		 * its parent becomes the directory to search in.
774355d6bb5Sswilcox 		 */
7757c478bd9Sstevel@tonic-gate 		ino = idesc.id_number;
7767c478bd9Sstevel@tonic-gate 	}
777355d6bb5Sswilcox 
778355d6bb5Sswilcox 	/*
779355d6bb5Sswilcox 	 * If we hit a discontinuity in the hierarchy, indicate it by
780355d6bb5Sswilcox 	 * prefixing the path so far with `?'.  Otherwise, the first
781355d6bb5Sswilcox 	 * character will be `/' as a side-effect of the *--cp above.
782355d6bb5Sswilcox 	 *
783355d6bb5Sswilcox 	 * The special case is to handle the situation where we're
784355d6bb5Sswilcox 	 * trying to look something up in UFSROOTINO, but didn't find
785355d6bb5Sswilcox 	 * it.
786355d6bb5Sswilcox 	 */
787355d6bb5Sswilcox 	if (ino != UFSROOTINO || cp == &namebuf[MAXPATHLEN - 1]) {
788355d6bb5Sswilcox 		if (cp > namebuf)
789355d6bb5Sswilcox 			cp--;
790355d6bb5Sswilcox 		*cp = '?';
7917c478bd9Sstevel@tonic-gate 	}
792355d6bb5Sswilcox 
793355d6bb5Sswilcox 	/*
794355d6bb5Sswilcox 	 * The invariants being used for buffer integrity are:
795355d6bb5Sswilcox 	 * - namebuf[] is terminated with \0 before anything else
796355d6bb5Sswilcox 	 * - cp is always <= the last element of namebuf[]
797355d6bb5Sswilcox 	 * - the new path element is always stored at the
798355d6bb5Sswilcox 	 *   beginning of namebuf[], and is no more than MAXNAMLEN-1
799355d6bb5Sswilcox 	 *   characters
800355d6bb5Sswilcox 	 * - cp is is decremented by the number of characters in
801355d6bb5Sswilcox 	 *   the new path element
802355d6bb5Sswilcox 	 * - if, after the above accounting for the new element's
803355d6bb5Sswilcox 	 *   size, there is no longer enough room at the beginning of
804355d6bb5Sswilcox 	 *   namebuf[] for a full-sized path element and a slash,
805355d6bb5Sswilcox 	 *   terminate the loop.  cp is in the range
806355d6bb5Sswilcox 	 *   &namebuf[0]..&namebuf[MAXNAMLEN - 1]
807355d6bb5Sswilcox 	 */
808355d6bb5Sswilcox attrname:
809355d6bb5Sswilcox 	/* LINTED per the above discussion */
810355d6bb5Sswilcox 	(void) memmove(namebuf, cp, &namebuf[MAXPATHLEN] - cp);
8117c478bd9Sstevel@tonic-gate }
8127c478bd9Sstevel@tonic-gate 
813355d6bb5Sswilcox /* ARGSUSED */
8147c478bd9Sstevel@tonic-gate void
815355d6bb5Sswilcox catch(int dummy)
8167c478bd9Sstevel@tonic-gate {
8177c478bd9Sstevel@tonic-gate 	ckfini();
818355d6bb5Sswilcox 	exit(EXSIGNAL);
8197c478bd9Sstevel@tonic-gate }
8207c478bd9Sstevel@tonic-gate 
8217c478bd9Sstevel@tonic-gate /*
8227c478bd9Sstevel@tonic-gate  * When preening, allow a single quit to signal
8237c478bd9Sstevel@tonic-gate  * a special exit after filesystem checks complete
8247c478bd9Sstevel@tonic-gate  * so that reboot sequence may be interrupted.
8257c478bd9Sstevel@tonic-gate  */
826355d6bb5Sswilcox /* ARGSUSED */
8277c478bd9Sstevel@tonic-gate void
828355d6bb5Sswilcox catchquit(int dummy)
8297c478bd9Sstevel@tonic-gate {
830355d6bb5Sswilcox 	(void) printf("returning to single-user after filesystem check\n");
831355d6bb5Sswilcox 	interrupted = 1;
8327c478bd9Sstevel@tonic-gate 	(void) signal(SIGQUIT, SIG_DFL);
8337c478bd9Sstevel@tonic-gate }
8347c478bd9Sstevel@tonic-gate 
8357c478bd9Sstevel@tonic-gate 
8367c478bd9Sstevel@tonic-gate /*
8377c478bd9Sstevel@tonic-gate  * determine whether an inode should be fixed.
8387c478bd9Sstevel@tonic-gate  */
839355d6bb5Sswilcox NOTE(PRINTFLIKE(2))
840355d6bb5Sswilcox int
841355d6bb5Sswilcox dofix(struct inodesc *idesc, caddr_t msg, ...)
8427c478bd9Sstevel@tonic-gate {
843355d6bb5Sswilcox 	int rval = 0;
844355d6bb5Sswilcox 	va_list ap;
845355d6bb5Sswilcox 
846355d6bb5Sswilcox 	va_start(ap, msg);
8477c478bd9Sstevel@tonic-gate 
8487c478bd9Sstevel@tonic-gate 	switch (idesc->id_fix) {
8497c478bd9Sstevel@tonic-gate 
8507c478bd9Sstevel@tonic-gate 	case DONTKNOW:
8517c478bd9Sstevel@tonic-gate 		if (idesc->id_type == DATA)
852355d6bb5Sswilcox 			vdirerror(idesc->id_number, msg, ap);
8537c478bd9Sstevel@tonic-gate 		else
854355d6bb5Sswilcox 			vpwarn(msg, ap);
8557c478bd9Sstevel@tonic-gate 		if (preen) {
8567c478bd9Sstevel@tonic-gate 			idesc->id_fix = FIX;
857355d6bb5Sswilcox 			rval = ALTERED;
858355d6bb5Sswilcox 			break;
8597c478bd9Sstevel@tonic-gate 		}
8607c478bd9Sstevel@tonic-gate 		if (reply("SALVAGE") == 0) {
8617c478bd9Sstevel@tonic-gate 			idesc->id_fix = NOFIX;
862355d6bb5Sswilcox 			break;
8637c478bd9Sstevel@tonic-gate 		}
8647c478bd9Sstevel@tonic-gate 		idesc->id_fix = FIX;
865355d6bb5Sswilcox 		rval = ALTERED;
866355d6bb5Sswilcox 		break;
8677c478bd9Sstevel@tonic-gate 
8687c478bd9Sstevel@tonic-gate 	case FIX:
869355d6bb5Sswilcox 		rval = ALTERED;
870355d6bb5Sswilcox 		break;
8717c478bd9Sstevel@tonic-gate 
8727c478bd9Sstevel@tonic-gate 	case NOFIX:
873355d6bb5Sswilcox 		break;
8747c478bd9Sstevel@tonic-gate 
8757c478bd9Sstevel@tonic-gate 	default:
876355d6bb5Sswilcox 		errexit("UNKNOWN INODESC FIX MODE %d\n", (int)idesc->id_fix);
8777c478bd9Sstevel@tonic-gate 	}
878355d6bb5Sswilcox 
879355d6bb5Sswilcox 	va_end(ap);
880355d6bb5Sswilcox 	return (rval);
8817c478bd9Sstevel@tonic-gate }
8827c478bd9Sstevel@tonic-gate 
883355d6bb5Sswilcox NOTE(PRINTFLIKE(1))
884355d6bb5Sswilcox void
885355d6bb5Sswilcox errexit(caddr_t fmt, ...)
8867c478bd9Sstevel@tonic-gate {
887355d6bb5Sswilcox 	va_list ap;
8887c478bd9Sstevel@tonic-gate 
889355d6bb5Sswilcox 	va_start(ap, fmt);
890355d6bb5Sswilcox 	verrexit(fmt, ap);
891355d6bb5Sswilcox 	/* NOTREACHED */
892355d6bb5Sswilcox }
893355d6bb5Sswilcox 
894355d6bb5Sswilcox NOTE(PRINTFLIKE(1))
895355d6bb5Sswilcox static void
896355d6bb5Sswilcox verrexit(caddr_t fmt, va_list ap)
897355d6bb5Sswilcox {
898355d6bb5Sswilcox 	static int recursing = 0;
899355d6bb5Sswilcox 
900355d6bb5Sswilcox 	if (!recursing) {
901355d6bb5Sswilcox 		recursing = 1;
902355d6bb5Sswilcox 		if (errorlocked || iscorrupt) {
903355d6bb5Sswilcox 			if (havesb) {
904355d6bb5Sswilcox 				sblock.fs_clean = FSBAD;
905355d6bb5Sswilcox 				sblock.fs_state = FSOKAY - (long)sblock.fs_time;
906355d6bb5Sswilcox 				sblock.fs_state = -sblock.fs_state;
907355d6bb5Sswilcox 				sbdirty();
908355d6bb5Sswilcox 				write_altsb(fswritefd);
909355d6bb5Sswilcox 				flush(fswritefd, &sblk);
910355d6bb5Sswilcox 			}
9117c478bd9Sstevel@tonic-gate 		}
912355d6bb5Sswilcox 		ckfini();
913355d6bb5Sswilcox 		recursing = 0;
9147c478bd9Sstevel@tonic-gate 	}
915355d6bb5Sswilcox 	(void) vprintf(fmt, ap);
916355d6bb5Sswilcox 	if (fmt[strlen(fmt) - 1] != '\n')
917355d6bb5Sswilcox 		(void) putchar('\n');
918355d6bb5Sswilcox 	exit((exitstat != 0) ? exitstat : EXERRFATAL);
9197c478bd9Sstevel@tonic-gate }
9207c478bd9Sstevel@tonic-gate 
9217c478bd9Sstevel@tonic-gate /*
9227c478bd9Sstevel@tonic-gate  * An unexpected inconsistency occured.
9237c478bd9Sstevel@tonic-gate  * Die if preening, otherwise just print message and continue.
9247c478bd9Sstevel@tonic-gate  */
925355d6bb5Sswilcox NOTE(PRINTFLIKE(1))
926355d6bb5Sswilcox void
927355d6bb5Sswilcox pfatal(caddr_t fmt, ...)
928355d6bb5Sswilcox {
929355d6bb5Sswilcox 	va_list ap;
930355d6bb5Sswilcox 
931355d6bb5Sswilcox 	va_start(ap, fmt);
932355d6bb5Sswilcox 	vpfatal(fmt, ap);
933355d6bb5Sswilcox 	va_end(ap);
934355d6bb5Sswilcox }
935355d6bb5Sswilcox 
936355d6bb5Sswilcox NOTE(PRINTFLIKE(1))
937355d6bb5Sswilcox static void
938355d6bb5Sswilcox vpfatal(caddr_t fmt, va_list ap)
9397c478bd9Sstevel@tonic-gate {
9407c478bd9Sstevel@tonic-gate 	if (preen) {
941355d6bb5Sswilcox 		if (*fmt != '\0') {
942355d6bb5Sswilcox 			(void) printf("%s: ", devname);
943355d6bb5Sswilcox 			(void) vprintf(fmt, ap);
944355d6bb5Sswilcox 			(void) printf("\n");
945355d6bb5Sswilcox 		}
946355d6bb5Sswilcox 		(void) printf(
947355d6bb5Sswilcox 		    "%s: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY.\n",
948355d6bb5Sswilcox 		    devname);
9497c478bd9Sstevel@tonic-gate 		if (havesb) {
9507c478bd9Sstevel@tonic-gate 			sblock.fs_clean = FSBAD;
9517c478bd9Sstevel@tonic-gate 			sblock.fs_state = -(FSOKAY - (long)sblock.fs_time);
9527c478bd9Sstevel@tonic-gate 			sbdirty();
9537c478bd9Sstevel@tonic-gate 			flush(fswritefd, &sblk);
9547c478bd9Sstevel@tonic-gate 		}
955355d6bb5Sswilcox 		/*
956355d6bb5Sswilcox 		 * We're exiting, it doesn't really matter that our
957355d6bb5Sswilcox 		 * caller doesn't get to call va_end().
958355d6bb5Sswilcox 		 */
959355d6bb5Sswilcox 		if (exitstat == 0)
960355d6bb5Sswilcox 			exitstat = EXFNDERRS;
961355d6bb5Sswilcox 		exit(exitstat);
962355d6bb5Sswilcox 	}
963355d6bb5Sswilcox 	if (*fmt != '\0') {
964355d6bb5Sswilcox 		(void) vprintf(fmt, ap);
9657c478bd9Sstevel@tonic-gate 	}
9667c478bd9Sstevel@tonic-gate }
9677c478bd9Sstevel@tonic-gate 
9687c478bd9Sstevel@tonic-gate /*
9697c478bd9Sstevel@tonic-gate  * Pwarn just prints a message when not preening,
9707c478bd9Sstevel@tonic-gate  * or a warning (preceded by filename) when preening.
9717c478bd9Sstevel@tonic-gate  */
972355d6bb5Sswilcox NOTE(PRINTFLIKE(1))
973355d6bb5Sswilcox void
974355d6bb5Sswilcox pwarn(caddr_t fmt, ...)
9757c478bd9Sstevel@tonic-gate {
976355d6bb5Sswilcox 	va_list ap;
9777c478bd9Sstevel@tonic-gate 
978355d6bb5Sswilcox 	va_start(ap, fmt);
979355d6bb5Sswilcox 	vpwarn(fmt, ap);
980355d6bb5Sswilcox 	va_end(ap);
981355d6bb5Sswilcox }
982355d6bb5Sswilcox 
983355d6bb5Sswilcox NOTE(PRINTFLIKE(1))
984355d6bb5Sswilcox static void
985355d6bb5Sswilcox vpwarn(caddr_t fmt, va_list ap)
986355d6bb5Sswilcox {
987355d6bb5Sswilcox 	if (*fmt != '\0') {
988355d6bb5Sswilcox 		if (preen)
989355d6bb5Sswilcox 			(void) printf("%s: ", devname);
990355d6bb5Sswilcox 		(void) vprintf(fmt, ap);
991355d6bb5Sswilcox 	}
9927c478bd9Sstevel@tonic-gate }
9937c478bd9Sstevel@tonic-gate 
9947c478bd9Sstevel@tonic-gate /*
995355d6bb5Sswilcox  * Like sprintf(), except the buffer is dynamically allocated
996355d6bb5Sswilcox  * and returned, instead of being passed in.  A pointer to the
997355d6bb5Sswilcox  * buffer is stored in *RET, and FMT is the usual format string.
998355d6bb5Sswilcox  * The number of characters in *RET (excluding the trailing \0,
999355d6bb5Sswilcox  * to be consistent with the other *printf() routines) is returned.
1000355d6bb5Sswilcox  *
1001355d6bb5Sswilcox  * Solaris doesn't have asprintf(3C) yet, unfortunately.
10027c478bd9Sstevel@tonic-gate  */
1003355d6bb5Sswilcox NOTE(PRINTFLIKE(2))
1004355d6bb5Sswilcox int
1005355d6bb5Sswilcox fsck_asprintf(caddr_t *ret, caddr_t fmt, ...)
10067c478bd9Sstevel@tonic-gate {
1007355d6bb5Sswilcox 	int len;
1008355d6bb5Sswilcox 	caddr_t buffer;
1009355d6bb5Sswilcox 	va_list ap;
10107c478bd9Sstevel@tonic-gate 
1011355d6bb5Sswilcox 	va_start(ap, fmt);
1012355d6bb5Sswilcox 	len = vsnprintf(NULL, 0, fmt, ap);
1013355d6bb5Sswilcox 	va_end(ap);
1014355d6bb5Sswilcox 
1015355d6bb5Sswilcox 	buffer = malloc((len + 1) * sizeof (char));
1016355d6bb5Sswilcox 	if (buffer == NULL) {
1017355d6bb5Sswilcox 		errexit("Out of memory in asprintf\n");
1018355d6bb5Sswilcox 		/* NOTREACHED */
1019355d6bb5Sswilcox 	}
1020355d6bb5Sswilcox 
1021355d6bb5Sswilcox 	va_start(ap, fmt);
1022355d6bb5Sswilcox 	(void) vsnprintf(buffer, len + 1, fmt, ap);
1023355d6bb5Sswilcox 	va_end(ap);
1024355d6bb5Sswilcox 
1025355d6bb5Sswilcox 	*ret = buffer;
1026355d6bb5Sswilcox 	return (len);
10277c478bd9Sstevel@tonic-gate }
1028355d6bb5Sswilcox 
1029355d6bb5Sswilcox /*
1030355d6bb5Sswilcox  * So we can take advantage of kernel routines in ufs_subr.c.
1031355d6bb5Sswilcox  */
1032355d6bb5Sswilcox /* PRINTFLIKE2 */
10337c478bd9Sstevel@tonic-gate void
1034355d6bb5Sswilcox cmn_err(int level, caddr_t fmt, ...)
10357c478bd9Sstevel@tonic-gate {
1036355d6bb5Sswilcox 	va_list ap;
10377c478bd9Sstevel@tonic-gate 
1038355d6bb5Sswilcox 	va_start(ap, fmt);
10397c478bd9Sstevel@tonic-gate 	if (level == CE_PANIC) {
1040355d6bb5Sswilcox 		(void) printf("INTERNAL INCONSISTENCY:");
1041355d6bb5Sswilcox 		verrexit(fmt, ap);
1042355d6bb5Sswilcox 	} else {
1043355d6bb5Sswilcox 		(void) vprintf(fmt, ap);
10447c478bd9Sstevel@tonic-gate 	}
1045355d6bb5Sswilcox 	va_end(ap);
10467c478bd9Sstevel@tonic-gate }
10477c478bd9Sstevel@tonic-gate 
10487c478bd9Sstevel@tonic-gate /*
10497c478bd9Sstevel@tonic-gate  * Check to see if unraw version of name is already mounted.
1050355d6bb5Sswilcox  * Updates devstr with the device name if devstr is not NULL
1051355d6bb5Sswilcox  * and str_size is positive.
10527c478bd9Sstevel@tonic-gate  */
1053355d6bb5Sswilcox int
1054355d6bb5Sswilcox mounted(caddr_t name, caddr_t devstr, size_t str_size)
10557c478bd9Sstevel@tonic-gate {
1056355d6bb5Sswilcox 	int found;
1057355d6bb5Sswilcox 	struct mnttab *mntent;
10587c478bd9Sstevel@tonic-gate 
1059355d6bb5Sswilcox 	mntent = search_mnttab(NULL, unrawname(name), devstr, str_size);
1060355d6bb5Sswilcox 	if (mntent == NULL)
1061355d6bb5Sswilcox 		return (M_NOMNT);
1062355d6bb5Sswilcox 
1063355d6bb5Sswilcox 	/*
1064355d6bb5Sswilcox 	 * It's mounted.  With or without write access?
1065355d6bb5Sswilcox 	 */
1066355d6bb5Sswilcox 	if (hasmntopt(mntent, MNTOPT_RO) != 0)
1067355d6bb5Sswilcox 		found = M_RO;	/* mounted as RO */
1068355d6bb5Sswilcox 	else
1069355d6bb5Sswilcox 		found = M_RW; 	/* mounted as R/W */
1070355d6bb5Sswilcox 
1071355d6bb5Sswilcox 	if (mount_point == NULL) {
1072355d6bb5Sswilcox 		mount_point = strdup(mntent->mnt_mountp);
1073355d6bb5Sswilcox 		if (mount_point == NULL) {
1074355d6bb5Sswilcox 			errexit("fsck: memory allocation failure: %s",
1075355d6bb5Sswilcox 				strerror(errno));
1076355d6bb5Sswilcox 			/* NOTREACHED */
10777c478bd9Sstevel@tonic-gate 		}
1078355d6bb5Sswilcox 
1079355d6bb5Sswilcox 		if (devstr != NULL && str_size > 0)
1080355d6bb5Sswilcox 			(void) strlcpy(devstr, mntent->mnt_special, str_size);
10817c478bd9Sstevel@tonic-gate 	}
1082355d6bb5Sswilcox 
10837c478bd9Sstevel@tonic-gate 	return (found);
10847c478bd9Sstevel@tonic-gate }
10857c478bd9Sstevel@tonic-gate 
10867c478bd9Sstevel@tonic-gate /*
10877c478bd9Sstevel@tonic-gate  * Check to see if name corresponds to an entry in vfstab, and that the entry
10887c478bd9Sstevel@tonic-gate  * does not have option ro.
10897c478bd9Sstevel@tonic-gate  */
1090355d6bb5Sswilcox int
1091355d6bb5Sswilcox writable(caddr_t name)
10927c478bd9Sstevel@tonic-gate {
10937c478bd9Sstevel@tonic-gate 	int rw = 1;
1094355d6bb5Sswilcox 	struct vfstab vfsbuf, vfskey;
10957c478bd9Sstevel@tonic-gate 	FILE *vfstab;
10967c478bd9Sstevel@tonic-gate 
10977c478bd9Sstevel@tonic-gate 	vfstab = fopen(VFSTAB, "r");
10987c478bd9Sstevel@tonic-gate 	if (vfstab == NULL) {
1099355d6bb5Sswilcox 		(void) printf("can't open %s\n", VFSTAB);
11007c478bd9Sstevel@tonic-gate 		return (1);
11017c478bd9Sstevel@tonic-gate 	}
1102355d6bb5Sswilcox 	(void) memset((void *)&vfskey, 0, sizeof (vfskey));
1103355d6bb5Sswilcox 	vfsnull(&vfskey);
1104355d6bb5Sswilcox 	vfskey.vfs_special = unrawname(name);
1105355d6bb5Sswilcox 	vfskey.vfs_fstype = MNTTYPE_UFS;
1106355d6bb5Sswilcox 	if ((getvfsany(vfstab, &vfsbuf, &vfskey) == 0) &&
11077c478bd9Sstevel@tonic-gate 	    (hasvfsopt(&vfsbuf, MNTOPT_RO))) {
11087c478bd9Sstevel@tonic-gate 		rw = 0;
11097c478bd9Sstevel@tonic-gate 	}
1110355d6bb5Sswilcox 	(void) fclose(vfstab);
11117c478bd9Sstevel@tonic-gate 	return (rw);
11127c478bd9Sstevel@tonic-gate }
11137c478bd9Sstevel@tonic-gate 
11147c478bd9Sstevel@tonic-gate /*
11157c478bd9Sstevel@tonic-gate  * debugclean
11167c478bd9Sstevel@tonic-gate  */
1117355d6bb5Sswilcox static void
1118355d6bb5Sswilcox debugclean(void)
11197c478bd9Sstevel@tonic-gate {
1120355d6bb5Sswilcox 	if (!debug)
11217c478bd9Sstevel@tonic-gate 		return;
11227c478bd9Sstevel@tonic-gate 
11237c478bd9Sstevel@tonic-gate 	if ((iscorrupt == 0) && (isdirty == 0))
11247c478bd9Sstevel@tonic-gate 		return;
11257c478bd9Sstevel@tonic-gate 
1126355d6bb5Sswilcox 	if ((sblock.fs_clean == FSSTABLE) || (sblock.fs_clean == FSCLEAN) ||
1127355d6bb5Sswilcox 	    (sblock.fs_clean == FSLOG && islog && islogok) ||
1128355d6bb5Sswilcox 	    ((FSOKAY == (sblock.fs_state + sblock.fs_time)) && !errorlocked))
11297c478bd9Sstevel@tonic-gate 		return;
11307c478bd9Sstevel@tonic-gate 
1131355d6bb5Sswilcox 	(void) printf("WARNING: inconsistencies detected on %s filesystem %s\n",
11327c478bd9Sstevel@tonic-gate 	    sblock.fs_clean == FSSTABLE ? "stable" :
11337c478bd9Sstevel@tonic-gate 	    sblock.fs_clean == FSLOG ? "logging" :
1134355d6bb5Sswilcox 	    sblock.fs_clean == FSFIX ? "being fixed" : "clean",
1135355d6bb5Sswilcox 	    devname);
11367c478bd9Sstevel@tonic-gate }
11377c478bd9Sstevel@tonic-gate 
11387c478bd9Sstevel@tonic-gate /*
11397c478bd9Sstevel@tonic-gate  * updateclean
11407c478bd9Sstevel@tonic-gate  *	Carefully and transparently update the clean flag.
1141355d6bb5Sswilcox  *
1142355d6bb5Sswilcox  * `iscorrupt' has to be in its final state before this is called.
11437c478bd9Sstevel@tonic-gate  */
1144355d6bb5Sswilcox int
1145355d6bb5Sswilcox updateclean(void)
1146355d6bb5Sswilcox {
1147355d6bb5Sswilcox 	int freedlog = 0;
1148355d6bb5Sswilcox 	struct bufarea cleanbuf;
1149355d6bb5Sswilcox 	size_t size;
1150355d6bb5Sswilcox 	ssize_t io_res;
1151355d6bb5Sswilcox 	diskaddr_t bno;
1152355d6bb5Sswilcox 	char fsclean;
1153355d6bb5Sswilcox 	int fsreclaim;
1154355d6bb5Sswilcox 	char fsflags;
115520a1ae8aSjkennedy 	int flags_ok = 1;
1156355d6bb5Sswilcox 	daddr32_t fslogbno;
11577c478bd9Sstevel@tonic-gate 	offset_t sblkoff;
11587c478bd9Sstevel@tonic-gate 	time_t t;
11597c478bd9Sstevel@tonic-gate 
11607c478bd9Sstevel@tonic-gate 	/*
11617c478bd9Sstevel@tonic-gate 	 * debug stuff
11627c478bd9Sstevel@tonic-gate 	 */
11637c478bd9Sstevel@tonic-gate 	debugclean();
11647c478bd9Sstevel@tonic-gate 
11657c478bd9Sstevel@tonic-gate 	/*
11667c478bd9Sstevel@tonic-gate 	 * set fsclean to its appropriate value
11677c478bd9Sstevel@tonic-gate 	 */
11687c478bd9Sstevel@tonic-gate 	fslogbno = sblock.fs_logbno;
11697c478bd9Sstevel@tonic-gate 	fsclean = sblock.fs_clean;
11707c478bd9Sstevel@tonic-gate 	fsreclaim = sblock.fs_reclaim;
11717c478bd9Sstevel@tonic-gate 	fsflags = sblock.fs_flags;
1172355d6bb5Sswilcox 	if (FSOKAY != (sblock.fs_state + sblock.fs_time) && !errorlocked) {
11737c478bd9Sstevel@tonic-gate 		fsclean = FSACTIVE;
1174355d6bb5Sswilcox 	}
1175355d6bb5Sswilcox 	/*
1176355d6bb5Sswilcox 	 * If ufs log is not okay, note that we need to clear it.
1177355d6bb5Sswilcox 	 */
1178355d6bb5Sswilcox 	examinelog(sblock.fs_logbno, NULL);
11797c478bd9Sstevel@tonic-gate 	if (fslogbno && !(islog && islogok)) {
11807c478bd9Sstevel@tonic-gate 		fsclean = FSACTIVE;
11817c478bd9Sstevel@tonic-gate 		fslogbno = 0;
11827c478bd9Sstevel@tonic-gate 	}
11837c478bd9Sstevel@tonic-gate 
11847c478bd9Sstevel@tonic-gate 	/*
11857c478bd9Sstevel@tonic-gate 	 * if necessary, update fs_clean and fs_state
11867c478bd9Sstevel@tonic-gate 	 */
11877c478bd9Sstevel@tonic-gate 	switch (fsclean) {
11887c478bd9Sstevel@tonic-gate 
11897c478bd9Sstevel@tonic-gate 	case FSACTIVE:
11907c478bd9Sstevel@tonic-gate 		if (!iscorrupt) {
11917c478bd9Sstevel@tonic-gate 			fsclean = FSSTABLE;
11927c478bd9Sstevel@tonic-gate 			fsreclaim = 0;
11937c478bd9Sstevel@tonic-gate 		}
11947c478bd9Sstevel@tonic-gate 		break;
11957c478bd9Sstevel@tonic-gate 
11967c478bd9Sstevel@tonic-gate 	case FSCLEAN:
11977c478bd9Sstevel@tonic-gate 	case FSSTABLE:
1198355d6bb5Sswilcox 		if (iscorrupt) {
11997c478bd9Sstevel@tonic-gate 			fsclean = FSACTIVE;
1200355d6bb5Sswilcox 		} else {
12017c478bd9Sstevel@tonic-gate 			fsreclaim = 0;
1202355d6bb5Sswilcox 		}
12037c478bd9Sstevel@tonic-gate 		break;
12047c478bd9Sstevel@tonic-gate 
12057c478bd9Sstevel@tonic-gate 	case FSLOG:
1206355d6bb5Sswilcox 		if (iscorrupt) {
12077c478bd9Sstevel@tonic-gate 			fsclean = FSACTIVE;
1208355d6bb5Sswilcox 		} else if (!islog || fslogbno == 0) {
12097c478bd9Sstevel@tonic-gate 			fsclean = FSSTABLE;
12107c478bd9Sstevel@tonic-gate 			fsreclaim = 0;
1211355d6bb5Sswilcox 		} else if (fflag) {
1212355d6bb5Sswilcox 			fsreclaim = 0;
1213355d6bb5Sswilcox 		}
12147c478bd9Sstevel@tonic-gate 		break;
12157c478bd9Sstevel@tonic-gate 
12167c478bd9Sstevel@tonic-gate 	case FSFIX:
12177c478bd9Sstevel@tonic-gate 		fsclean = FSBAD;
12187c478bd9Sstevel@tonic-gate 		if (errorlocked && !iscorrupt) {
1219355d6bb5Sswilcox 			fsclean = islog ? FSLOG : FSCLEAN;
12207c478bd9Sstevel@tonic-gate 		}
12217c478bd9Sstevel@tonic-gate 		break;
12227c478bd9Sstevel@tonic-gate 
12237c478bd9Sstevel@tonic-gate 	default:
1224355d6bb5Sswilcox 		if (iscorrupt) {
12257c478bd9Sstevel@tonic-gate 			fsclean = FSACTIVE;
1226355d6bb5Sswilcox 		} else {
12277c478bd9Sstevel@tonic-gate 			fsclean = FSSTABLE;
12287c478bd9Sstevel@tonic-gate 			fsreclaim = 0;
12297c478bd9Sstevel@tonic-gate 		}
12307c478bd9Sstevel@tonic-gate 	}
12317c478bd9Sstevel@tonic-gate 
12327c478bd9Sstevel@tonic-gate 	if (largefile_count > 0)
12337c478bd9Sstevel@tonic-gate 		fsflags |= FSLARGEFILES;
12347c478bd9Sstevel@tonic-gate 	else
12357c478bd9Sstevel@tonic-gate 		fsflags &= ~FSLARGEFILES;
12367c478bd9Sstevel@tonic-gate 
12377c478bd9Sstevel@tonic-gate 	/*
123820a1ae8aSjkennedy 	 * There can be two discrepencies here.  A) The superblock
123920a1ae8aSjkennedy 	 * shows no largefiles but we found some while scanning.
124020a1ae8aSjkennedy 	 * B) The superblock indicates the presence of largefiles,
124120a1ae8aSjkennedy 	 * but none are present.  Note that if preening, the superblock
124220a1ae8aSjkennedy 	 * is silently corrected.
12437c478bd9Sstevel@tonic-gate 	 */
124420a1ae8aSjkennedy 	if ((fsflags == FSLARGEFILES && sblock.fs_flags != FSLARGEFILES) ||
124520a1ae8aSjkennedy 	    (fsflags != FSLARGEFILES && sblock.fs_flags == FSLARGEFILES))
1246355d6bb5Sswilcox 		flags_ok = 0;
1247355d6bb5Sswilcox 
12487c478bd9Sstevel@tonic-gate 	if (debug)
1249355d6bb5Sswilcox 		(void) printf(
1250355d6bb5Sswilcox 		    "** largefile count=%d, fs.fs_flags=%x, flags_ok %d\n",
1251355d6bb5Sswilcox 		    largefile_count, sblock.fs_flags, flags_ok);
12527c478bd9Sstevel@tonic-gate 
1253355d6bb5Sswilcox 	/*
1254355d6bb5Sswilcox 	 * If fs is unchanged, do nothing.
1255355d6bb5Sswilcox 	 */
1256355d6bb5Sswilcox 	if ((!isdirty) && (flags_ok) &&
12577c478bd9Sstevel@tonic-gate 	    (fslogbno == sblock.fs_logbno) &&
1258355d6bb5Sswilcox 	    (sblock.fs_clean == fsclean) &&
1259355d6bb5Sswilcox 	    (sblock.fs_reclaim == fsreclaim) &&
12607c478bd9Sstevel@tonic-gate 	    (FSOKAY == (sblock.fs_state + sblock.fs_time))) {
12617c478bd9Sstevel@tonic-gate 		if (errorlocked) {
12627c478bd9Sstevel@tonic-gate 			if (!do_errorlock(LOCKFS_ULOCK))
12637c478bd9Sstevel@tonic-gate 				pwarn(
12647c478bd9Sstevel@tonic-gate 		    "updateclean(unchanged): unlock(LOCKFS_ULOCK) failed\n");
12657c478bd9Sstevel@tonic-gate 		}
1266355d6bb5Sswilcox 		return (freedlog);
12677c478bd9Sstevel@tonic-gate 	}
12687c478bd9Sstevel@tonic-gate 
12697c478bd9Sstevel@tonic-gate 	/*
12707c478bd9Sstevel@tonic-gate 	 * if user allows, update superblock state
12717c478bd9Sstevel@tonic-gate 	 */
1272355d6bb5Sswilcox 	if (debug) {
1273355d6bb5Sswilcox 		(void) printf(
1274355d6bb5Sswilcox 	    "superblock: flags 0x%x logbno %d clean %d reclaim %d state 0x%x\n",
1275355d6bb5Sswilcox 		    sblock.fs_flags, sblock.fs_logbno,
1276355d6bb5Sswilcox 		    sblock.fs_clean, sblock.fs_reclaim,
1277355d6bb5Sswilcox 		    sblock.fs_state + sblock.fs_time);
1278355d6bb5Sswilcox 		(void) printf(
1279355d6bb5Sswilcox 	    "calculated: flags 0x%x logbno %d clean %d reclaim %d state 0x%x\n",
1280355d6bb5Sswilcox 		    fsflags, fslogbno, fsclean, fsreclaim, FSOKAY);
1281355d6bb5Sswilcox 	}
1282355d6bb5Sswilcox 	if (!isdirty && !preen && !rerun &&
12837c478bd9Sstevel@tonic-gate 	    (reply("FILE SYSTEM STATE IN SUPERBLOCK IS WRONG; FIX") == 0))
1284355d6bb5Sswilcox 		return (freedlog);
12857c478bd9Sstevel@tonic-gate 
12867c478bd9Sstevel@tonic-gate 	(void) time(&t);
12877c478bd9Sstevel@tonic-gate 	sblock.fs_time = (time32_t)t;
12887c478bd9Sstevel@tonic-gate 	if (debug)
12897c478bd9Sstevel@tonic-gate 		printclean();
1290355d6bb5Sswilcox 
1291355d6bb5Sswilcox 	if (sblock.fs_logbno != fslogbno) {
1292355d6bb5Sswilcox 		examinelog(sblock.fs_logbno, &freelogblk);
1293355d6bb5Sswilcox 		freedlog++;
1294355d6bb5Sswilcox 	}
1295355d6bb5Sswilcox 
12967c478bd9Sstevel@tonic-gate 	sblock.fs_logbno = fslogbno;
12977c478bd9Sstevel@tonic-gate 	sblock.fs_clean = fsclean;
12987c478bd9Sstevel@tonic-gate 	sblock.fs_state = FSOKAY - (long)sblock.fs_time;
12997c478bd9Sstevel@tonic-gate 	sblock.fs_reclaim = fsreclaim;
13007c478bd9Sstevel@tonic-gate 	sblock.fs_flags = fsflags;
13017c478bd9Sstevel@tonic-gate 
13027c478bd9Sstevel@tonic-gate 	/*
13037c478bd9Sstevel@tonic-gate 	 * if superblock can't be written, return
13047c478bd9Sstevel@tonic-gate 	 */
13057c478bd9Sstevel@tonic-gate 	if (fswritefd < 0)
1306355d6bb5Sswilcox 		return (freedlog);
13077c478bd9Sstevel@tonic-gate 
13087c478bd9Sstevel@tonic-gate 	/*
1309355d6bb5Sswilcox 	 * Read private copy of superblock, update clean flag, and write it.
13107c478bd9Sstevel@tonic-gate 	 */
13117c478bd9Sstevel@tonic-gate 	bno  = sblk.b_bno;
13127c478bd9Sstevel@tonic-gate 	size = sblk.b_size;
13137c478bd9Sstevel@tonic-gate 
13147c478bd9Sstevel@tonic-gate 	sblkoff = ldbtob(bno);
13157c478bd9Sstevel@tonic-gate 
13167c478bd9Sstevel@tonic-gate 	if ((cleanbuf.b_un.b_buf = malloc(size)) == NULL)
13177c478bd9Sstevel@tonic-gate 		errexit("out of memory");
1318*f763a6cdScasper 	if (llseek(fsreadfd, sblkoff, SEEK_SET) == -1) {
1319355d6bb5Sswilcox 		(void) printf("COULD NOT SEEK TO SUPERBLOCK AT %lld: %s\n",
1320355d6bb5Sswilcox 		    (longlong_t)bno, strerror(errno));
1321355d6bb5Sswilcox 		goto out;
1322355d6bb5Sswilcox 	}
13237c478bd9Sstevel@tonic-gate 
1324355d6bb5Sswilcox 	if ((io_res = read(fsreadfd, cleanbuf.b_un.b_buf, size)) != size) {
1325355d6bb5Sswilcox 		report_io_prob("READ FROM", bno, size, io_res);
1326355d6bb5Sswilcox 		goto out;
1327355d6bb5Sswilcox 	}
13287c478bd9Sstevel@tonic-gate 
13297c478bd9Sstevel@tonic-gate 	cleanbuf.b_un.b_fs->fs_logbno  = sblock.fs_logbno;
13307c478bd9Sstevel@tonic-gate 	cleanbuf.b_un.b_fs->fs_clean   = sblock.fs_clean;
13317c478bd9Sstevel@tonic-gate 	cleanbuf.b_un.b_fs->fs_state   = sblock.fs_state;
13327c478bd9Sstevel@tonic-gate 	cleanbuf.b_un.b_fs->fs_time    = sblock.fs_time;
13337c478bd9Sstevel@tonic-gate 	cleanbuf.b_un.b_fs->fs_reclaim = sblock.fs_reclaim;
13347c478bd9Sstevel@tonic-gate 	cleanbuf.b_un.b_fs->fs_flags   = sblock.fs_flags;
13357c478bd9Sstevel@tonic-gate 
1336*f763a6cdScasper 	if (llseek(fswritefd, sblkoff, SEEK_SET) == -1) {
1337355d6bb5Sswilcox 		(void) printf("COULD NOT SEEK TO SUPERBLOCK AT %lld: %s\n",
1338355d6bb5Sswilcox 		    (longlong_t)bno, strerror(errno));
1339355d6bb5Sswilcox 		goto out;
1340355d6bb5Sswilcox 	}
1341355d6bb5Sswilcox 
1342355d6bb5Sswilcox 	if ((io_res = write(fswritefd, cleanbuf.b_un.b_buf, size)) != size) {
1343355d6bb5Sswilcox 		report_io_prob("WRITE TO", bno, size, io_res);
1344355d6bb5Sswilcox 		goto out;
1345355d6bb5Sswilcox 	}
13467c478bd9Sstevel@tonic-gate 
13477c478bd9Sstevel@tonic-gate 	/*
13487c478bd9Sstevel@tonic-gate 	 * 1208040
13497c478bd9Sstevel@tonic-gate 	 * If we had to use -b to grab an alternate superblock, then we
13507c478bd9Sstevel@tonic-gate 	 * likely had to do so because of unacceptable differences between
1351355d6bb5Sswilcox 	 * the main and alternate superblocks.  So, we had better update
13527c478bd9Sstevel@tonic-gate 	 * the alternate superblock as well, or we'll just fail again
13537c478bd9Sstevel@tonic-gate 	 * the next time we attempt to run fsck!
13547c478bd9Sstevel@tonic-gate 	 */
1355355d6bb5Sswilcox 	if (bflag != 0) {
1356355d6bb5Sswilcox 		write_altsb(fswritefd);
13577c478bd9Sstevel@tonic-gate 	}
13587c478bd9Sstevel@tonic-gate 
13597c478bd9Sstevel@tonic-gate 	if (errorlocked) {
13607c478bd9Sstevel@tonic-gate 		if (!do_errorlock(LOCKFS_ULOCK))
13617c478bd9Sstevel@tonic-gate 			pwarn(
13627c478bd9Sstevel@tonic-gate 		    "updateclean(changed): unlock(LOCKFS_ULOCK) failed\n");
13637c478bd9Sstevel@tonic-gate 	}
1364355d6bb5Sswilcox 
1365355d6bb5Sswilcox out:
1366355d6bb5Sswilcox 	if (cleanbuf.b_un.b_buf != NULL) {
1367355d6bb5Sswilcox 		free((void *)cleanbuf.b_un.b_buf);
1368355d6bb5Sswilcox 	}
1369355d6bb5Sswilcox 
1370355d6bb5Sswilcox 	return (freedlog);
1371355d6bb5Sswilcox }
1372355d6bb5Sswilcox 
1373355d6bb5Sswilcox static void
1374355d6bb5Sswilcox report_io_prob(caddr_t what, diskaddr_t bno, size_t expected, ssize_t failure)
1375355d6bb5Sswilcox {
1376355d6bb5Sswilcox 	if (failure < 0)
1377355d6bb5Sswilcox 		(void) printf("COULD NOT %s SUPERBLOCK AT %d: %s\n",
1378355d6bb5Sswilcox 		    what, (int)bno, strerror(errno));
1379355d6bb5Sswilcox 	else if (failure == 0)
1380355d6bb5Sswilcox 		(void) printf("COULD NOT %s SUPERBLOCK AT %d: EOF\n",
1381355d6bb5Sswilcox 		    what, (int)bno);
1382355d6bb5Sswilcox 	else
1383355d6bb5Sswilcox 		(void) printf("SHORT %s SUPERBLOCK AT %d: %u out of %u bytes\n",
1384355d6bb5Sswilcox 		    what, (int)bno, (unsigned)failure, (unsigned)expected);
13857c478bd9Sstevel@tonic-gate }
13867c478bd9Sstevel@tonic-gate 
13877c478bd9Sstevel@tonic-gate /*
13887c478bd9Sstevel@tonic-gate  * print out clean info
13897c478bd9Sstevel@tonic-gate  */
1390355d6bb5Sswilcox void
1391355d6bb5Sswilcox printclean(void)
13927c478bd9Sstevel@tonic-gate {
1393355d6bb5Sswilcox 	caddr_t s;
13947c478bd9Sstevel@tonic-gate 
13957c478bd9Sstevel@tonic-gate 	if (FSOKAY != (sblock.fs_state + sblock.fs_time) && !errorlocked)
13967c478bd9Sstevel@tonic-gate 		s = "unknown";
13977c478bd9Sstevel@tonic-gate 	else
13987c478bd9Sstevel@tonic-gate 		switch (sblock.fs_clean) {
13997c478bd9Sstevel@tonic-gate 
14007c478bd9Sstevel@tonic-gate 		case FSACTIVE:
14017c478bd9Sstevel@tonic-gate 			s = "active";
14027c478bd9Sstevel@tonic-gate 			break;
14037c478bd9Sstevel@tonic-gate 
14047c478bd9Sstevel@tonic-gate 		case FSCLEAN:
14057c478bd9Sstevel@tonic-gate 			s = "clean";
14067c478bd9Sstevel@tonic-gate 			break;
14077c478bd9Sstevel@tonic-gate 
14087c478bd9Sstevel@tonic-gate 		case FSSTABLE:
14097c478bd9Sstevel@tonic-gate 			s = "stable";
14107c478bd9Sstevel@tonic-gate 			break;
14117c478bd9Sstevel@tonic-gate 
14127c478bd9Sstevel@tonic-gate 		case FSLOG:
14137c478bd9Sstevel@tonic-gate 			s = "logging";
14147c478bd9Sstevel@tonic-gate 			break;
14157c478bd9Sstevel@tonic-gate 
14167c478bd9Sstevel@tonic-gate 		case FSBAD:
14177c478bd9Sstevel@tonic-gate 			s = "is bad";
14187c478bd9Sstevel@tonic-gate 			break;
14197c478bd9Sstevel@tonic-gate 
14207c478bd9Sstevel@tonic-gate 		case FSFIX:
14217c478bd9Sstevel@tonic-gate 			s = "being fixed";
14227c478bd9Sstevel@tonic-gate 			break;
14237c478bd9Sstevel@tonic-gate 
14247c478bd9Sstevel@tonic-gate 		default:
14257c478bd9Sstevel@tonic-gate 			s = "unknown";
14267c478bd9Sstevel@tonic-gate 		}
14277c478bd9Sstevel@tonic-gate 
14287c478bd9Sstevel@tonic-gate 	if (preen)
14297c478bd9Sstevel@tonic-gate 		pwarn("is %s.\n", s);
14307c478bd9Sstevel@tonic-gate 	else
1431355d6bb5Sswilcox 		(void) printf("** %s is %s.\n", devname, s);
14327c478bd9Sstevel@tonic-gate }
14337c478bd9Sstevel@tonic-gate 
1434355d6bb5Sswilcox int
1435355d6bb5Sswilcox is_errorlocked(caddr_t fs)
14367c478bd9Sstevel@tonic-gate {
1437355d6bb5Sswilcox 	int		retval;
1438355d6bb5Sswilcox 	struct stat64	statb;
1439355d6bb5Sswilcox 	caddr_t		mountp;
1440355d6bb5Sswilcox 	struct mnttab	*mntent;
14417c478bd9Sstevel@tonic-gate 
1442355d6bb5Sswilcox 	retval = 0;
14437c478bd9Sstevel@tonic-gate 
14447c478bd9Sstevel@tonic-gate 	if (!fs)
14457c478bd9Sstevel@tonic-gate 		return (0);
14467c478bd9Sstevel@tonic-gate 
14477c478bd9Sstevel@tonic-gate 	if (stat64(fs, &statb) < 0)
14487c478bd9Sstevel@tonic-gate 		return (0);
14497c478bd9Sstevel@tonic-gate 
14507c478bd9Sstevel@tonic-gate 	if (S_ISDIR(statb.st_mode)) {
14517c478bd9Sstevel@tonic-gate 		mountp = fs;
14527c478bd9Sstevel@tonic-gate 	} else if (S_ISBLK(statb.st_mode) || S_ISCHR(statb.st_mode)) {
1453355d6bb5Sswilcox 		mntent = search_mnttab(NULL, fs, NULL, 0);
1454355d6bb5Sswilcox 		if (mntent == NULL)
1455355d6bb5Sswilcox 			return (0);
1456355d6bb5Sswilcox 		mountp = mntent->mnt_mountp;
1457355d6bb5Sswilcox 		if (mountp == NULL) /* theoretically a can't-happen */
14587c478bd9Sstevel@tonic-gate 			return (0);
14597c478bd9Sstevel@tonic-gate 	} else {
14607c478bd9Sstevel@tonic-gate 		return (0);
14617c478bd9Sstevel@tonic-gate 	}
14627c478bd9Sstevel@tonic-gate 
1463355d6bb5Sswilcox 	/*
1464355d6bb5Sswilcox 	 * From here on, must `goto out' to avoid memory leakage.
1465355d6bb5Sswilcox 	 */
1466355d6bb5Sswilcox 
1467355d6bb5Sswilcox 	if (elock_combuf == NULL)
14687c478bd9Sstevel@tonic-gate 		elock_combuf =
1469355d6bb5Sswilcox 			(caddr_t)calloc(LOCKFS_MAXCOMMENTLEN, sizeof (char));
1470355d6bb5Sswilcox 	else
14717c478bd9Sstevel@tonic-gate 		elock_combuf =
1472355d6bb5Sswilcox 			(caddr_t)realloc(elock_combuf, LOCKFS_MAXCOMMENTLEN);
14737c478bd9Sstevel@tonic-gate 
14747c478bd9Sstevel@tonic-gate 	if (elock_combuf == NULL)
1475355d6bb5Sswilcox 		goto out;
14767c478bd9Sstevel@tonic-gate 
1477355d6bb5Sswilcox 	(void) memset((void *)elock_combuf, 0, LOCKFS_MAXCOMMENTLEN);
1478355d6bb5Sswilcox 
1479355d6bb5Sswilcox 	if (elock_mountp != NULL) {
1480355d6bb5Sswilcox 		free(elock_mountp);
1481355d6bb5Sswilcox 	}
14827c478bd9Sstevel@tonic-gate 
14837c478bd9Sstevel@tonic-gate 	elock_mountp = strdup(mountp);
1484355d6bb5Sswilcox 	if (elock_mountp == NULL)
1485355d6bb5Sswilcox 		goto out;
14867c478bd9Sstevel@tonic-gate 
14877c478bd9Sstevel@tonic-gate 	if (mountfd < 0) {
14887c478bd9Sstevel@tonic-gate 		if ((mountfd = open64(mountp, O_RDONLY)) == -1)
1489355d6bb5Sswilcox 			goto out;
14907c478bd9Sstevel@tonic-gate 	}
14917c478bd9Sstevel@tonic-gate 
1492355d6bb5Sswilcox 	if (lfp == NULL) {
14937c478bd9Sstevel@tonic-gate 		lfp = (struct lockfs *)malloc(sizeof (struct lockfs));
1494355d6bb5Sswilcox 		if (lfp == NULL)
1495355d6bb5Sswilcox 			goto out;
1496355d6bb5Sswilcox 		(void) memset((void *)lfp, 0, sizeof (struct lockfs));
14977c478bd9Sstevel@tonic-gate 	}
14987c478bd9Sstevel@tonic-gate 
14997c478bd9Sstevel@tonic-gate 	lfp->lf_comlen = LOCKFS_MAXCOMMENTLEN;
15007c478bd9Sstevel@tonic-gate 	lfp->lf_comment = elock_combuf;
15017c478bd9Sstevel@tonic-gate 
15027c478bd9Sstevel@tonic-gate 	if (ioctl(mountfd, _FIOLFSS, lfp) == -1)
1503355d6bb5Sswilcox 		goto out;
1504355d6bb5Sswilcox 
1505355d6bb5Sswilcox 	/*
1506355d6bb5Sswilcox 	 * lint believes that the ioctl() (or any other function
1507355d6bb5Sswilcox 	 * taking lfp as an arg) could free lfp.  This is not the
1508355d6bb5Sswilcox 	 * case, however.
1509355d6bb5Sswilcox 	 */
1510355d6bb5Sswilcox 	retval = LOCKFS_IS_ELOCK(lfp);
15117c478bd9Sstevel@tonic-gate 
1512355d6bb5Sswilcox out:
1513355d6bb5Sswilcox 	return (retval);
15147c478bd9Sstevel@tonic-gate }
15157c478bd9Sstevel@tonic-gate 
1516355d6bb5Sswilcox /*
1517355d6bb5Sswilcox  * Given a name which is known to be a directory, see if it appears
1518355d6bb5Sswilcox  * in the vfstab.  If so, return the entry's block (special) device
1519355d6bb5Sswilcox  * field via devstr.
1520355d6bb5Sswilcox  */
1521355d6bb5Sswilcox int
1522355d6bb5Sswilcox check_vfstab(caddr_t name, caddr_t devstr, size_t str_size)
1523355d6bb5Sswilcox {
1524355d6bb5Sswilcox 	return (NULL != search_vfstab(name, NULL, devstr, str_size));
1525355d6bb5Sswilcox }
15267c478bd9Sstevel@tonic-gate 
1527355d6bb5Sswilcox /*
1528355d6bb5Sswilcox  * Given a name which is known to be a directory, see if it appears
1529355d6bb5Sswilcox  * in the mnttab.  If so, return the entry's block (special) device
1530355d6bb5Sswilcox  * field via devstr.
1531355d6bb5Sswilcox  */
1532355d6bb5Sswilcox int
1533355d6bb5Sswilcox check_mnttab(caddr_t name, caddr_t devstr, size_t str_size)
1534355d6bb5Sswilcox {
1535355d6bb5Sswilcox 	return (NULL != search_mnttab(name, NULL, devstr, str_size));
15367c478bd9Sstevel@tonic-gate }
15377c478bd9Sstevel@tonic-gate 
1538355d6bb5Sswilcox /*
1539355d6bb5Sswilcox  * Search for mount point and/or special device in the given file.
1540355d6bb5Sswilcox  * The first matching entry is returned.
1541355d6bb5Sswilcox  *
1542355d6bb5Sswilcox  * If an entry is found and str_size is greater than zero, then
1543355d6bb5Sswilcox  * up to size_str bytes of the special device name from the entry
1544355d6bb5Sswilcox  * are copied to devstr.
1545355d6bb5Sswilcox  */
1546355d6bb5Sswilcox 
1547355d6bb5Sswilcox #define	SEARCH_TAB_BODY(st_type, st_file, st_mount, st_special, \
1548355d6bb5Sswilcox 			st_nuller, st_init, st_searcher) \
1549355d6bb5Sswilcox 	{ \
1550355d6bb5Sswilcox 		FILE *fp; \
1551355d6bb5Sswilcox 		struct st_type *retval = NULL; \
1552355d6bb5Sswilcox 		struct st_type key; \
1553355d6bb5Sswilcox 		static struct st_type buffer; \
1554355d6bb5Sswilcox 		\
1555355d6bb5Sswilcox 		/* LINTED ``assigned value never used'' */ \
1556355d6bb5Sswilcox 		st_nuller(&key); \
1557355d6bb5Sswilcox 		key.st_mount = mountp; \
1558355d6bb5Sswilcox 		key.st_special = special; \
1559355d6bb5Sswilcox 		st_init; \
1560355d6bb5Sswilcox 		\
1561355d6bb5Sswilcox 		if ((fp = fopen(st_file, "r")) == NULL) \
1562355d6bb5Sswilcox 			return (NULL); \
1563355d6bb5Sswilcox 		\
1564355d6bb5Sswilcox 		if (st_searcher(fp, &buffer, &key) == 0) { \
1565355d6bb5Sswilcox 			retval = &buffer; \
1566355d6bb5Sswilcox 			if (devstr != NULL && str_size > 0 && \
1567355d6bb5Sswilcox 			    buffer.st_special != NULL) { \
1568355d6bb5Sswilcox 				(void) strlcpy(devstr, buffer.st_special, \
1569355d6bb5Sswilcox 				    str_size); \
1570355d6bb5Sswilcox 			} \
1571355d6bb5Sswilcox 		} \
1572355d6bb5Sswilcox 		(void) fclose(fp); \
1573355d6bb5Sswilcox 		return (retval); \
1574355d6bb5Sswilcox 	}
1575355d6bb5Sswilcox 
1576355d6bb5Sswilcox static struct vfstab *
1577355d6bb5Sswilcox search_vfstab(caddr_t mountp, caddr_t special, caddr_t devstr, size_t str_size)
1578355d6bb5Sswilcox SEARCH_TAB_BODY(vfstab, VFSTAB, vfs_mountp, vfs_special, vfsnull,
1579355d6bb5Sswilcox 		(retval = retval), getvfsany)
1580355d6bb5Sswilcox 
1581355d6bb5Sswilcox static struct mnttab *
1582355d6bb5Sswilcox search_mnttab(caddr_t mountp, caddr_t special, caddr_t devstr, size_t str_size)
1583355d6bb5Sswilcox SEARCH_TAB_BODY(mnttab, MNTTAB, mnt_mountp, mnt_special, mntnull,
1584355d6bb5Sswilcox 		(key.mnt_fstype = MNTTYPE_UFS), getmntany)
1585355d6bb5Sswilcox 
1586355d6bb5Sswilcox int
15877c478bd9Sstevel@tonic-gate do_errorlock(int lock_type)
15887c478bd9Sstevel@tonic-gate {
1589355d6bb5Sswilcox 	caddr_t	   buf;
1590355d6bb5Sswilcox 	time_t	   now;
1591355d6bb5Sswilcox 	struct tm *local;
1592355d6bb5Sswilcox 	int	   rc;
15937c478bd9Sstevel@tonic-gate 
1594355d6bb5Sswilcox 	if (elock_combuf == NULL)
15957c478bd9Sstevel@tonic-gate 		errexit("do_errorlock(%s, %d): unallocated elock_combuf\n",
1596355d6bb5Sswilcox 			elock_mountp ? elock_mountp : "<null>",
1597355d6bb5Sswilcox 			lock_type);
15987c478bd9Sstevel@tonic-gate 
1599355d6bb5Sswilcox 	if ((buf = (caddr_t)calloc(LOCKFS_MAXCOMMENTLEN, sizeof (char))) ==
1600355d6bb5Sswilcox 	    NULL) {
16017c478bd9Sstevel@tonic-gate 		errexit("Couldn't alloc memory for temp. lock status buffer\n");
1602355d6bb5Sswilcox 	}
1603355d6bb5Sswilcox 	if (lfp == NULL) {
16047c478bd9Sstevel@tonic-gate 		errexit("do_errorlock(%s, %d): lockfs status unallocated\n",
16057c478bd9Sstevel@tonic-gate 					elock_mountp, lock_type);
16067c478bd9Sstevel@tonic-gate 	}
16077c478bd9Sstevel@tonic-gate 
1608355d6bb5Sswilcox 	(void) memmove((void *)buf, (void *)elock_combuf,
1609355d6bb5Sswilcox 	    LOCKFS_MAXCOMMENTLEN-1);
16107c478bd9Sstevel@tonic-gate 
16117c478bd9Sstevel@tonic-gate 	switch (lock_type) {
16127c478bd9Sstevel@tonic-gate 	case LOCKFS_ELOCK:
1613355d6bb5Sswilcox 		/*
1614355d6bb5Sswilcox 		 * Note that if it is error-locked, we won't get an
1615355d6bb5Sswilcox 		 * error back if we try to error-lock it again.
1616355d6bb5Sswilcox 		 */
16177c478bd9Sstevel@tonic-gate 		if (time(&now) != (time_t)-1) {
16187c478bd9Sstevel@tonic-gate 			if ((local = localtime(&now)) != NULL)
1619355d6bb5Sswilcox 				(void) snprintf(buf, LOCKFS_MAXCOMMENTLEN,
16207c478bd9Sstevel@tonic-gate 		    "%s [pid:%d fsck start:%02d/%02d/%02d %02d:%02d:%02d",
1621355d6bb5Sswilcox 				    elock_combuf, (int)pid,
1622355d6bb5Sswilcox 				    local->tm_mon + 1, local->tm_mday,
16237c478bd9Sstevel@tonic-gate 				    (local->tm_year % 100), local->tm_hour,
16247c478bd9Sstevel@tonic-gate 				    local->tm_min, local->tm_sec);
16257c478bd9Sstevel@tonic-gate 			else
1626355d6bb5Sswilcox 				(void) snprintf(buf, LOCKFS_MAXCOMMENTLEN,
1627355d6bb5Sswilcox 				    "%s [fsck pid %d", elock_combuf, pid);
16287c478bd9Sstevel@tonic-gate 
16297c478bd9Sstevel@tonic-gate 		} else {
1630355d6bb5Sswilcox 			(void) snprintf(buf, LOCKFS_MAXCOMMENTLEN,
1631355d6bb5Sswilcox 			    "%s [fsck pid %d", elock_combuf, pid);
16327c478bd9Sstevel@tonic-gate 		}
16337c478bd9Sstevel@tonic-gate 		break;
16347c478bd9Sstevel@tonic-gate 
16357c478bd9Sstevel@tonic-gate 	case LOCKFS_ULOCK:
16367c478bd9Sstevel@tonic-gate 		if (time(&now) != (time_t)-1) {
16377c478bd9Sstevel@tonic-gate 			if ((local = localtime(&now)) != NULL) {
1638355d6bb5Sswilcox 				(void) snprintf(buf, LOCKFS_MAXCOMMENTLEN,
16397c478bd9Sstevel@tonic-gate 				    "%s, done:%02d/%02d/%02d %02d:%02d:%02d]",
16407c478bd9Sstevel@tonic-gate 				    elock_combuf,
1641355d6bb5Sswilcox 				    local->tm_mon + 1, local->tm_mday,
16427c478bd9Sstevel@tonic-gate 				    (local->tm_year % 100), local->tm_hour,
16437c478bd9Sstevel@tonic-gate 				    local->tm_min, local->tm_sec);
16447c478bd9Sstevel@tonic-gate 			} else {
1645355d6bb5Sswilcox 				(void) snprintf(buf, LOCKFS_MAXCOMMENTLEN,
1646355d6bb5Sswilcox 				    "%s]", elock_combuf);
16477c478bd9Sstevel@tonic-gate 			}
16487c478bd9Sstevel@tonic-gate 		} else {
1649355d6bb5Sswilcox 			(void) snprintf(buf, LOCKFS_MAXCOMMENTLEN,
1650355d6bb5Sswilcox 			    "%s]", elock_combuf);
16517c478bd9Sstevel@tonic-gate 		}
16527c478bd9Sstevel@tonic-gate 		if ((rc = ioctl(mountfd, _FIOLFSS, lfp)) == -1) {
1653355d6bb5Sswilcox 			pwarn("do_errorlock: unlock failed: %s\n",
1654355d6bb5Sswilcox 			    strerror(errno));
16557c478bd9Sstevel@tonic-gate 			goto out;
16567c478bd9Sstevel@tonic-gate 		}
16577c478bd9Sstevel@tonic-gate 		break;
16587c478bd9Sstevel@tonic-gate 
16597c478bd9Sstevel@tonic-gate 	default:
16607c478bd9Sstevel@tonic-gate 		break;
16617c478bd9Sstevel@tonic-gate 	}
16627c478bd9Sstevel@tonic-gate 
1663355d6bb5Sswilcox 	(void) memmove((void *)elock_combuf, (void *)buf,
1664355d6bb5Sswilcox 	    LOCKFS_MAXCOMMENTLEN - 1);
16657c478bd9Sstevel@tonic-gate 
1666355d6bb5Sswilcox 	lfp->lf_lock = lock_type;
1667355d6bb5Sswilcox 	lfp->lf_comlen = LOCKFS_MAXCOMMENTLEN;
1668355d6bb5Sswilcox 	lfp->lf_comment = elock_combuf;
1669355d6bb5Sswilcox 	lfp->lf_flags = 0;
1670355d6bb5Sswilcox 	errno = 0;
16717c478bd9Sstevel@tonic-gate 
16727c478bd9Sstevel@tonic-gate 	if ((rc = ioctl(mountfd, _FIOLFS, lfp)) == -1) {
16737c478bd9Sstevel@tonic-gate 		if (errno == EINVAL) {
16747c478bd9Sstevel@tonic-gate 			pwarn("Another fsck active?\n");
16757c478bd9Sstevel@tonic-gate 			iscorrupt = 0;	/* don't go away mad, just go away */
16767c478bd9Sstevel@tonic-gate 		} else {
1677355d6bb5Sswilcox 			pwarn("do_errorlock(lock_type:%d, %s) failed: %s\n",
1678355d6bb5Sswilcox 			    lock_type, elock_combuf, strerror(errno));
16797c478bd9Sstevel@tonic-gate 		}
16807c478bd9Sstevel@tonic-gate 	}
16817c478bd9Sstevel@tonic-gate out:
1682355d6bb5Sswilcox 	if (buf != NULL) {
1683355d6bb5Sswilcox 		free((void *)buf);
1684355d6bb5Sswilcox 	}
16857c478bd9Sstevel@tonic-gate 
16867c478bd9Sstevel@tonic-gate 	return (rc != -1);
16877c478bd9Sstevel@tonic-gate }
16887c478bd9Sstevel@tonic-gate 
16897c478bd9Sstevel@tonic-gate /*
1690355d6bb5Sswilcox  * Shadow inode support.  To register a shadow with a client is to note
1691355d6bb5Sswilcox  * that an inode (the client) refers to the shadow.
16927c478bd9Sstevel@tonic-gate  */
16937c478bd9Sstevel@tonic-gate 
16947c478bd9Sstevel@tonic-gate static struct shadowclients *
16957c478bd9Sstevel@tonic-gate newshadowclient(struct shadowclients *prev)
16967c478bd9Sstevel@tonic-gate {
16977c478bd9Sstevel@tonic-gate 	struct shadowclients *rc;
16987c478bd9Sstevel@tonic-gate 
16997c478bd9Sstevel@tonic-gate 	rc = (struct shadowclients *)malloc(sizeof (*rc));
17007c478bd9Sstevel@tonic-gate 	if (rc == NULL)
1701355d6bb5Sswilcox 		errexit("newshadowclient: cannot malloc shadow client");
17027c478bd9Sstevel@tonic-gate 	rc->next = prev;
17037c478bd9Sstevel@tonic-gate 	rc->nclients = 0;
17047c478bd9Sstevel@tonic-gate 
1705355d6bb5Sswilcox 	rc->client = (fsck_ino_t *)malloc(sizeof (fsck_ino_t) *
1706355d6bb5Sswilcox 	    maxshadowclients);
17077c478bd9Sstevel@tonic-gate 	if (rc->client == NULL)
1708355d6bb5Sswilcox 		errexit("newshadowclient: cannot malloc client array");
17097c478bd9Sstevel@tonic-gate 	return (rc);
17107c478bd9Sstevel@tonic-gate }
17117c478bd9Sstevel@tonic-gate 
17127c478bd9Sstevel@tonic-gate void
1713355d6bb5Sswilcox registershadowclient(fsck_ino_t shadow, fsck_ino_t client,
1714355d6bb5Sswilcox 	struct shadowclientinfo **info)
17157c478bd9Sstevel@tonic-gate {
17167c478bd9Sstevel@tonic-gate 	struct shadowclientinfo *sci;
17177c478bd9Sstevel@tonic-gate 	struct shadowclients *scc;
17187c478bd9Sstevel@tonic-gate 
1719355d6bb5Sswilcox 	/*
1720355d6bb5Sswilcox 	 * Already have a record for this shadow?
1721355d6bb5Sswilcox 	 */
1722355d6bb5Sswilcox 	for (sci = *info; sci != NULL; sci = sci->next)
17237c478bd9Sstevel@tonic-gate 		if (sci->shadow == shadow)
17247c478bd9Sstevel@tonic-gate 			break;
17257c478bd9Sstevel@tonic-gate 	if (sci == NULL) {
1726355d6bb5Sswilcox 		/*
1727355d6bb5Sswilcox 		 * It's a new shadow, add it to the list
1728355d6bb5Sswilcox 		 */
17297c478bd9Sstevel@tonic-gate 		sci = (struct shadowclientinfo *)malloc(sizeof (*sci));
17307c478bd9Sstevel@tonic-gate 		if (sci == NULL)
17317c478bd9Sstevel@tonic-gate 			errexit("registershadowclient: cannot malloc");
17327c478bd9Sstevel@tonic-gate 		sci->next = *info;
17337c478bd9Sstevel@tonic-gate 		*info = sci;
17347c478bd9Sstevel@tonic-gate 		sci->shadow = shadow;
17357c478bd9Sstevel@tonic-gate 		sci->totalClients = 0;
17367c478bd9Sstevel@tonic-gate 		sci->clients = newshadowclient(NULL);
17377c478bd9Sstevel@tonic-gate 	}
17387c478bd9Sstevel@tonic-gate 
17397c478bd9Sstevel@tonic-gate 	sci->totalClients++;
17407c478bd9Sstevel@tonic-gate 	scc = sci->clients;
17417c478bd9Sstevel@tonic-gate 	if (scc->nclients >= maxshadowclients) {
17427c478bd9Sstevel@tonic-gate 		scc = newshadowclient(sci->clients);
17437c478bd9Sstevel@tonic-gate 		sci->clients = scc;
17447c478bd9Sstevel@tonic-gate 	}
17457c478bd9Sstevel@tonic-gate 
17467c478bd9Sstevel@tonic-gate 	scc->client[scc->nclients++] = client;
17477c478bd9Sstevel@tonic-gate }
17487c478bd9Sstevel@tonic-gate 
1749355d6bb5Sswilcox /*
1750355d6bb5Sswilcox  * Locate and discard a shadow.
1751355d6bb5Sswilcox  */
1752355d6bb5Sswilcox void
1753355d6bb5Sswilcox clearshadow(fsck_ino_t shadow, struct shadowclientinfo **info)
1754355d6bb5Sswilcox {
1755355d6bb5Sswilcox 	struct shadowclientinfo *sci, *prev;
1756355d6bb5Sswilcox 
1757355d6bb5Sswilcox 	/*
1758355d6bb5Sswilcox 	 * Do we have a record for this shadow?
1759355d6bb5Sswilcox 	 */
1760355d6bb5Sswilcox 	prev = NULL;
1761355d6bb5Sswilcox 	for (sci = *info; sci != NULL; sci = sci->next) {
1762355d6bb5Sswilcox 		if (sci->shadow == shadow)
1763355d6bb5Sswilcox 			break;
1764355d6bb5Sswilcox 		prev = sci;
1765355d6bb5Sswilcox 	}
1766355d6bb5Sswilcox 
1767355d6bb5Sswilcox 	if (sci != NULL) {
1768355d6bb5Sswilcox 		/*
1769355d6bb5Sswilcox 		 * First, pull it off the list, since we know there
1770355d6bb5Sswilcox 		 * shouldn't be any future references to this one.
1771355d6bb5Sswilcox 		 */
1772355d6bb5Sswilcox 		if (prev == NULL)
1773355d6bb5Sswilcox 			*info = sci->next;
1774355d6bb5Sswilcox 		else
1775355d6bb5Sswilcox 			prev->next = sci->next;
1776355d6bb5Sswilcox 		deshadow(sci, clearattrref);
1777355d6bb5Sswilcox 	}
1778355d6bb5Sswilcox }
1779355d6bb5Sswilcox 
1780355d6bb5Sswilcox /*
1781355d6bb5Sswilcox  * Discard all memory used to track clients of a shadow.
1782355d6bb5Sswilcox  */
1783355d6bb5Sswilcox void
1784355d6bb5Sswilcox deshadow(struct shadowclientinfo *sci, void (*cb)(fsck_ino_t))
1785355d6bb5Sswilcox {
1786355d6bb5Sswilcox 	struct shadowclients *clients, *discard;
1787355d6bb5Sswilcox 	int idx;
1788355d6bb5Sswilcox 
1789355d6bb5Sswilcox 	clients = sci->clients;
1790355d6bb5Sswilcox 	while (clients != NULL) {
1791355d6bb5Sswilcox 		discard = clients;
1792355d6bb5Sswilcox 		clients = clients->next;
1793355d6bb5Sswilcox 		if (discard->client != NULL) {
1794355d6bb5Sswilcox 			if (cb != NULL) {
1795355d6bb5Sswilcox 				for (idx = 0; idx < discard->nclients; idx++)
1796355d6bb5Sswilcox 					(*cb)(discard->client[idx]);
1797355d6bb5Sswilcox 			}
1798355d6bb5Sswilcox 			free((void *)discard->client);
1799355d6bb5Sswilcox 		}
1800355d6bb5Sswilcox 		free((void *)discard);
1801355d6bb5Sswilcox 	}
1802355d6bb5Sswilcox 
1803355d6bb5Sswilcox 	free((void *)sci);
1804355d6bb5Sswilcox }
1805355d6bb5Sswilcox 
18067c478bd9Sstevel@tonic-gate /*
18077c478bd9Sstevel@tonic-gate  * Allocate more buffer as need arises but allocate one at a time.
18087c478bd9Sstevel@tonic-gate  * This is done to make sure that fsck does not exit with error if it
1809355d6bb5Sswilcox  * needs more buffer to complete its task.
18107c478bd9Sstevel@tonic-gate  */
18117c478bd9Sstevel@tonic-gate static struct bufarea *
1812355d6bb5Sswilcox alloc_bufarea(void)
18137c478bd9Sstevel@tonic-gate {
1814355d6bb5Sswilcox 	struct bufarea *newbp;
1815355d6bb5Sswilcox 	caddr_t bufp;
18167c478bd9Sstevel@tonic-gate 
18177c478bd9Sstevel@tonic-gate 	bufp = malloc((unsigned int)sblock.fs_bsize);
1818355d6bb5Sswilcox 	if (bufp == NULL)
1819355d6bb5Sswilcox 		return (NULL);
1820355d6bb5Sswilcox 
1821355d6bb5Sswilcox 	newbp = (struct bufarea *)malloc(sizeof (struct bufarea));
1822355d6bb5Sswilcox 	if (newbp == NULL) {
1823355d6bb5Sswilcox 		free((void *)bufp);
18247c478bd9Sstevel@tonic-gate 		return (NULL);
18257c478bd9Sstevel@tonic-gate 	}
1826355d6bb5Sswilcox 
1827355d6bb5Sswilcox 	initbarea(newbp);
1828355d6bb5Sswilcox 	newbp->b_un.b_buf = bufp;
1829355d6bb5Sswilcox 	newbp->b_prev = &bufhead;
1830355d6bb5Sswilcox 	newbp->b_next = bufhead.b_next;
1831355d6bb5Sswilcox 	bufhead.b_next->b_prev = newbp;
1832355d6bb5Sswilcox 	bufhead.b_next = newbp;
18337c478bd9Sstevel@tonic-gate 	bufhead.b_size++;
1834355d6bb5Sswilcox 	return (newbp);
1835355d6bb5Sswilcox }
1836355d6bb5Sswilcox 
1837355d6bb5Sswilcox /*
1838355d6bb5Sswilcox  * We length-limit in both unrawname() and rawname() to avoid
1839355d6bb5Sswilcox  * overflowing our arrays or those of our naive, trusting callers.
1840355d6bb5Sswilcox  */
1841355d6bb5Sswilcox 
1842355d6bb5Sswilcox caddr_t
1843355d6bb5Sswilcox unrawname(caddr_t name)
1844355d6bb5Sswilcox {
1845355d6bb5Sswilcox 	caddr_t dp;
1846355d6bb5Sswilcox 	static char fullname[MAXPATHLEN + 1];
1847355d6bb5Sswilcox 
1848355d6bb5Sswilcox 	if ((dp = getfullblkname(name)) == NULL)
1849355d6bb5Sswilcox 		return ("");
1850355d6bb5Sswilcox 
1851355d6bb5Sswilcox 	(void) strlcpy(fullname, dp, sizeof (fullname));
1852355d6bb5Sswilcox 	/*
1853355d6bb5Sswilcox 	 * Not reporting under debug, as the allocation isn't
1854355d6bb5Sswilcox 	 * reported by getfullblkname.  The idea is that we
1855355d6bb5Sswilcox 	 * produce balanced alloc/free instances.
1856355d6bb5Sswilcox 	 */
1857355d6bb5Sswilcox 	free(dp);
1858355d6bb5Sswilcox 
1859355d6bb5Sswilcox 	return (fullname);
1860355d6bb5Sswilcox }
1861355d6bb5Sswilcox 
1862355d6bb5Sswilcox caddr_t
1863355d6bb5Sswilcox rawname(caddr_t name)
1864355d6bb5Sswilcox {
1865355d6bb5Sswilcox 	caddr_t dp;
1866355d6bb5Sswilcox 	static char fullname[MAXPATHLEN + 1];
1867355d6bb5Sswilcox 
1868355d6bb5Sswilcox 	if ((dp = getfullrawname(name)) == NULL)
1869355d6bb5Sswilcox 		return ("");
1870355d6bb5Sswilcox 
1871355d6bb5Sswilcox 	(void) strlcpy(fullname, dp, sizeof (fullname));
1872355d6bb5Sswilcox 	/*
1873355d6bb5Sswilcox 	 * Not reporting under debug, as the allocation isn't
1874355d6bb5Sswilcox 	 * reported by getfullblkname.  The idea is that we
1875355d6bb5Sswilcox 	 * produce balanced alloc/free instances.
1876355d6bb5Sswilcox 	 */
1877355d6bb5Sswilcox 	free(dp);
1878355d6bb5Sswilcox 
1879355d6bb5Sswilcox 	return (fullname);
1880355d6bb5Sswilcox }
1881355d6bb5Sswilcox 
1882355d6bb5Sswilcox /*
1883355d6bb5Sswilcox  * Make sure that a cg header looks at least moderately reasonable.
1884355d6bb5Sswilcox  * We want to be able to trust the contents enough to be able to use
1885355d6bb5Sswilcox  * the standard accessor macros.  So, besides looking at the obvious
1886355d6bb5Sswilcox  * such as the magic number, we verify that the offset field values
1887355d6bb5Sswilcox  * are properly aligned and not too big or small.
1888355d6bb5Sswilcox  *
1889355d6bb5Sswilcox  * Returns a NULL pointer if the cg is sane enough for our needs, else
1890355d6bb5Sswilcox  * a dynamically-allocated string describing all of its faults.
1891355d6bb5Sswilcox  */
1892355d6bb5Sswilcox #define	Append_Error(full, full_len, addition, addition_len) \
1893355d6bb5Sswilcox 	if (full == NULL) { \
1894355d6bb5Sswilcox 		full = addition; \
1895355d6bb5Sswilcox 		full_len = addition_len; \
1896355d6bb5Sswilcox 	} else { \
1897355d6bb5Sswilcox 		/* lint doesn't think realloc() understands NULLs */ \
1898355d6bb5Sswilcox 		full = realloc(full, full_len + addition_len + 1); \
1899355d6bb5Sswilcox 		if (full == NULL) { \
1900355d6bb5Sswilcox 			errexit("Out of memory in cg_sanity"); \
1901355d6bb5Sswilcox 			/* NOTREACHED */ \
1902355d6bb5Sswilcox 		} \
1903355d6bb5Sswilcox 		(void) strcpy(full + full_len, addition); \
1904355d6bb5Sswilcox 		full_len += addition_len; \
1905355d6bb5Sswilcox 		free(addition); \
1906355d6bb5Sswilcox 	}
1907355d6bb5Sswilcox 
1908355d6bb5Sswilcox caddr_t
190977a343abSabalfour cg_sanity(struct cg *cgp, int cgno)
1910355d6bb5Sswilcox {
1911355d6bb5Sswilcox 	caddr_t full_err;
1912355d6bb5Sswilcox 	caddr_t this_err = NULL;
1913355d6bb5Sswilcox 	int full_len, this_len;
1914355d6bb5Sswilcox 	daddr32_t ndblk;
1915355d6bb5Sswilcox 	daddr32_t exp_btotoff, exp_boff, exp_iusedoff;
1916355d6bb5Sswilcox 	daddr32_t exp_freeoff, exp_nextfreeoff;
1917355d6bb5Sswilcox 
1918355d6bb5Sswilcox 	cg_constants(cgno, &exp_btotoff, &exp_boff, &exp_iusedoff,
1919355d6bb5Sswilcox 	    &exp_freeoff, &exp_nextfreeoff, &ndblk);
1920355d6bb5Sswilcox 
1921355d6bb5Sswilcox 	full_err = NULL;
1922355d6bb5Sswilcox 	full_len = 0;
1923355d6bb5Sswilcox 
1924355d6bb5Sswilcox 	if (!cg_chkmagic(cgp)) {
1925355d6bb5Sswilcox 		this_len = fsck_asprintf(&this_err,
1926355d6bb5Sswilcox 		    "BAD CG MAGIC NUMBER (0x%x should be 0x%x)\n",
1927355d6bb5Sswilcox 		    cgp->cg_magic, CG_MAGIC);
1928355d6bb5Sswilcox 		Append_Error(full_err, full_len, this_err, this_len);
1929355d6bb5Sswilcox 	}
1930355d6bb5Sswilcox 
1931355d6bb5Sswilcox 	if (cgp->cg_cgx != cgno) {
1932355d6bb5Sswilcox 		this_len = fsck_asprintf(&this_err,
1933355d6bb5Sswilcox 		    "WRONG CG NUMBER (%d should be %d)\n",
1934355d6bb5Sswilcox 		    cgp->cg_cgx, cgno);
1935355d6bb5Sswilcox 		Append_Error(full_err, full_len, this_err, this_len);
1936355d6bb5Sswilcox 	}
1937355d6bb5Sswilcox 
1938355d6bb5Sswilcox 	if ((cgp->cg_btotoff & 3) != 0) {
1939355d6bb5Sswilcox 		this_len = fsck_asprintf(&this_err,
1940355d6bb5Sswilcox 		    "BLOCK TOTALS OFFSET %d NOT FOUR-BYTE ALIGNED\n",
1941355d6bb5Sswilcox 		    cgp->cg_btotoff);
1942355d6bb5Sswilcox 		Append_Error(full_err, full_len, this_err, this_len);
1943355d6bb5Sswilcox 	}
1944355d6bb5Sswilcox 
1945355d6bb5Sswilcox 	if ((cgp->cg_boff & 1) != 0) {
1946355d6bb5Sswilcox 		this_len = fsck_asprintf(&this_err,
1947355d6bb5Sswilcox 	    "FREE BLOCK POSITIONS TABLE OFFSET %d NOT TWO-BYTE ALIGNED\n",
1948355d6bb5Sswilcox 		    cgp->cg_boff);
1949355d6bb5Sswilcox 		Append_Error(full_err, full_len, this_err, this_len);
1950355d6bb5Sswilcox 	}
1951355d6bb5Sswilcox 
1952355d6bb5Sswilcox 	if ((cgp->cg_ncyl < 1) || (cgp->cg_ncyl > sblock.fs_cpg)) {
1953355d6bb5Sswilcox 		if (cgp->cg_ncyl < 1) {
1954355d6bb5Sswilcox 			this_len = fsck_asprintf(&this_err,
1955355d6bb5Sswilcox 	    "IMPOSSIBLE NUMBER OF CYLINDERS IN GROUP (%d is less than 1)\n",
1956355d6bb5Sswilcox 			    cgp->cg_ncyl);
1957355d6bb5Sswilcox 		} else {
1958355d6bb5Sswilcox 			this_len = fsck_asprintf(&this_err,
1959355d6bb5Sswilcox 	    "IMPOSSIBLE NUMBER OF CYLINDERS IN GROUP (%d is greater than %d)\n",
1960355d6bb5Sswilcox 			    cgp->cg_ncyl, sblock.fs_cpg);
1961355d6bb5Sswilcox 		}
1962355d6bb5Sswilcox 		Append_Error(full_err, full_len, this_err, this_len);
1963355d6bb5Sswilcox 	}
1964355d6bb5Sswilcox 
1965355d6bb5Sswilcox 	if (cgp->cg_niblk != sblock.fs_ipg) {
1966355d6bb5Sswilcox 		this_len = fsck_asprintf(&this_err,
1967355d6bb5Sswilcox 		    "INCORRECT NUMBER OF INODES IN GROUP (%d should be %d)\n",
1968355d6bb5Sswilcox 		    cgp->cg_niblk, sblock.fs_ipg);
1969355d6bb5Sswilcox 		Append_Error(full_err, full_len, this_err, this_len);
1970355d6bb5Sswilcox 	}
1971355d6bb5Sswilcox 
1972355d6bb5Sswilcox 	if (cgp->cg_ndblk != ndblk) {
1973355d6bb5Sswilcox 		this_len = fsck_asprintf(&this_err,
1974355d6bb5Sswilcox 	    "INCORRECT NUMBER OF DATA BLOCKS IN GROUP (%d should be %d)\n",
1975355d6bb5Sswilcox 		    cgp->cg_ndblk, ndblk);
1976355d6bb5Sswilcox 		Append_Error(full_err, full_len, this_err, this_len);
1977355d6bb5Sswilcox 	}
1978355d6bb5Sswilcox 
1979355d6bb5Sswilcox 	if ((cgp->cg_rotor < 0) || (cgp->cg_rotor >= ndblk)) {
1980355d6bb5Sswilcox 		this_len = fsck_asprintf(&this_err,
1981355d6bb5Sswilcox 		    "IMPOSSIBLE BLOCK ALLOCATION ROTOR POSITION "
1982355d6bb5Sswilcox 		    "(%d should be at least 0 and less than %d)\n",
1983355d6bb5Sswilcox 		    cgp->cg_rotor, ndblk);
1984355d6bb5Sswilcox 		Append_Error(full_err, full_len, this_err, this_len);
1985355d6bb5Sswilcox 	}
1986355d6bb5Sswilcox 
1987355d6bb5Sswilcox 	if ((cgp->cg_frotor < 0) || (cgp->cg_frotor >= ndblk)) {
1988355d6bb5Sswilcox 		this_len = fsck_asprintf(&this_err,
1989355d6bb5Sswilcox 		    "IMPOSSIBLE FRAGMENT ALLOCATION ROTOR POSITION "
1990355d6bb5Sswilcox 		    "(%d should be at least 0 and less than %d)\n",
1991355d6bb5Sswilcox 		    cgp->cg_frotor, ndblk);
1992355d6bb5Sswilcox 		Append_Error(full_err, full_len, this_err, this_len);
1993355d6bb5Sswilcox 	}
1994355d6bb5Sswilcox 
1995355d6bb5Sswilcox 	if ((cgp->cg_irotor < 0) || (cgp->cg_irotor >= sblock.fs_ipg)) {
1996355d6bb5Sswilcox 		this_len = fsck_asprintf(&this_err,
1997355d6bb5Sswilcox 		    "IMPOSSIBLE INODE ALLOCATION ROTOR POSITION "
1998355d6bb5Sswilcox 		    "(%d should be at least 0 and less than %d)\n",
1999355d6bb5Sswilcox 		    cgp->cg_irotor, sblock.fs_ipg);
2000355d6bb5Sswilcox 		Append_Error(full_err, full_len, this_err, this_len);
2001355d6bb5Sswilcox 	}
2002355d6bb5Sswilcox 
2003355d6bb5Sswilcox 	if (cgp->cg_btotoff != exp_btotoff) {
2004355d6bb5Sswilcox 		this_len = fsck_asprintf(&this_err,
2005355d6bb5Sswilcox 		    "INCORRECT BLOCK TOTALS OFFSET (%d should be %d)\n",
2006355d6bb5Sswilcox 		    cgp->cg_btotoff, exp_btotoff);
2007355d6bb5Sswilcox 		Append_Error(full_err, full_len, this_err, this_len);
2008355d6bb5Sswilcox 	}
2009355d6bb5Sswilcox 
2010355d6bb5Sswilcox 	if (cgp->cg_boff != exp_boff) {
2011355d6bb5Sswilcox 		this_len = fsck_asprintf(&this_err,
2012355d6bb5Sswilcox 		    "BAD FREE BLOCK POSITIONS TABLE OFFSET (%d should %d)\n",
2013355d6bb5Sswilcox 		    cgp->cg_boff, exp_boff);
2014355d6bb5Sswilcox 		Append_Error(full_err, full_len, this_err, this_len);
2015355d6bb5Sswilcox 	}
2016355d6bb5Sswilcox 
2017355d6bb5Sswilcox 	if (cgp->cg_iusedoff != exp_iusedoff) {
2018355d6bb5Sswilcox 		this_len = fsck_asprintf(&this_err,
2019355d6bb5Sswilcox 		    "INCORRECT USED INODE MAP OFFSET (%d should be %d)\n",
2020355d6bb5Sswilcox 		    cgp->cg_iusedoff, exp_iusedoff);
2021355d6bb5Sswilcox 		Append_Error(full_err, full_len, this_err, this_len);
2022355d6bb5Sswilcox 	}
2023355d6bb5Sswilcox 
2024355d6bb5Sswilcox 	if (cgp->cg_freeoff != exp_freeoff) {
2025355d6bb5Sswilcox 		this_len = fsck_asprintf(&this_err,
2026355d6bb5Sswilcox 		    "INCORRECT FREE FRAGMENT MAP OFFSET (%d should be %d)\n",
2027355d6bb5Sswilcox 		    cgp->cg_freeoff, exp_freeoff);
2028355d6bb5Sswilcox 		Append_Error(full_err, full_len, this_err, this_len);
2029355d6bb5Sswilcox 	}
2030355d6bb5Sswilcox 
2031355d6bb5Sswilcox 	if (cgp->cg_nextfreeoff != exp_nextfreeoff) {
2032355d6bb5Sswilcox 		this_len = fsck_asprintf(&this_err,
2033355d6bb5Sswilcox 		    "END OF HEADER POSITION INCORRECT (%d should be %d)\n",
2034355d6bb5Sswilcox 		    cgp->cg_nextfreeoff, exp_nextfreeoff);
2035355d6bb5Sswilcox 		Append_Error(full_err, full_len, this_err, this_len);
2036355d6bb5Sswilcox 	}
2037355d6bb5Sswilcox 
2038355d6bb5Sswilcox 	return (full_err);
2039355d6bb5Sswilcox }
2040355d6bb5Sswilcox 
2041355d6bb5Sswilcox #undef	Append_Error
2042355d6bb5Sswilcox 
2043355d6bb5Sswilcox /*
2044355d6bb5Sswilcox  * This is taken from mkfs, and is what is used to come up with the
2045355d6bb5Sswilcox  * original values for a struct cg.  This implies that, since these
2046355d6bb5Sswilcox  * are all constants, recalculating them now should give us the same
2047355d6bb5Sswilcox  * thing as what's on disk.
2048355d6bb5Sswilcox  */
2049355d6bb5Sswilcox static void
2050355d6bb5Sswilcox cg_constants(int cgno, daddr32_t *btotoff, daddr32_t *boff,
2051355d6bb5Sswilcox 	daddr32_t *iusedoff, daddr32_t *freeoff, daddr32_t *nextfreeoff,
2052355d6bb5Sswilcox 	daddr32_t *ndblk)
2053355d6bb5Sswilcox {
2054355d6bb5Sswilcox 	daddr32_t cbase, dmax;
2055355d6bb5Sswilcox 	struct cg *cgp;
2056355d6bb5Sswilcox 
2057355d6bb5Sswilcox 	(void) getblk(&cgblk, (diskaddr_t)cgtod(&sblock, cgno),
2058355d6bb5Sswilcox 	    (size_t)sblock.fs_cgsize);
2059355d6bb5Sswilcox 	cgp = cgblk.b_un.b_cg;
2060355d6bb5Sswilcox 
2061355d6bb5Sswilcox 	cbase = cgbase(&sblock, cgno);
2062355d6bb5Sswilcox 	dmax = cbase + sblock.fs_fpg;
2063355d6bb5Sswilcox 	if (dmax > sblock.fs_size)
2064355d6bb5Sswilcox 		dmax = sblock.fs_size;
2065355d6bb5Sswilcox 
2066355d6bb5Sswilcox 	/* LINTED pointer difference won't overflow */
2067355d6bb5Sswilcox 	*btotoff = &cgp->cg_space[0] - (uchar_t *)(&cgp->cg_link);
2068355d6bb5Sswilcox 	*boff = *btotoff + sblock.fs_cpg * sizeof (daddr32_t);
2069355d6bb5Sswilcox 	*iusedoff = *boff + sblock.fs_cpg * sblock.fs_nrpos * sizeof (int16_t);
2070355d6bb5Sswilcox 	*freeoff = *iusedoff + howmany(sblock.fs_ipg, NBBY);
2071355d6bb5Sswilcox 	*nextfreeoff = *freeoff +
2072355d6bb5Sswilcox 		howmany(sblock.fs_cpg * sblock.fs_spc / NSPF(&sblock), NBBY);
2073355d6bb5Sswilcox 	*ndblk = dmax - cbase;
2074355d6bb5Sswilcox }
2075355d6bb5Sswilcox 
2076355d6bb5Sswilcox /*
2077355d6bb5Sswilcox  * Corrects all fields in the cg that can be done with the available
2078355d6bb5Sswilcox  * redundant data.
2079355d6bb5Sswilcox  */
2080355d6bb5Sswilcox void
2081355d6bb5Sswilcox fix_cg(struct cg *cgp, int cgno)
2082355d6bb5Sswilcox {
2083355d6bb5Sswilcox 	daddr32_t exp_btotoff, exp_boff, exp_iusedoff;
2084355d6bb5Sswilcox 	daddr32_t exp_freeoff, exp_nextfreeoff;
2085355d6bb5Sswilcox 	daddr32_t ndblk;
2086355d6bb5Sswilcox 
2087355d6bb5Sswilcox 	cg_constants(cgno, &exp_btotoff, &exp_boff, &exp_iusedoff,
2088355d6bb5Sswilcox 	    &exp_freeoff, &exp_nextfreeoff, &ndblk);
2089355d6bb5Sswilcox 
2090355d6bb5Sswilcox 	if (cgp->cg_cgx != cgno) {
2091355d6bb5Sswilcox 		cgp->cg_cgx = cgno;
2092355d6bb5Sswilcox 	}
2093355d6bb5Sswilcox 
2094355d6bb5Sswilcox 	if ((cgp->cg_ncyl < 1) || (cgp->cg_ncyl > sblock.fs_cpg)) {
2095ef31a55cSvk 		if (cgno == (sblock.fs_ncg - 1)) {
2096355d6bb5Sswilcox 			cgp->cg_ncyl = sblock.fs_ncyl -
2097ef31a55cSvk 				(sblock.fs_cpg * cgno);
2098355d6bb5Sswilcox 		} else {
2099355d6bb5Sswilcox 			cgp->cg_ncyl = sblock.fs_cpg;
2100355d6bb5Sswilcox 		}
2101355d6bb5Sswilcox 	}
2102355d6bb5Sswilcox 
2103355d6bb5Sswilcox 	if (cgp->cg_niblk != sblock.fs_ipg) {
2104355d6bb5Sswilcox 		/*
2105355d6bb5Sswilcox 		 * This is not used by the kernel, so it's pretty
2106355d6bb5Sswilcox 		 * harmless if it's wrong.
2107355d6bb5Sswilcox 		 */
2108355d6bb5Sswilcox 		cgp->cg_niblk = sblock.fs_ipg;
2109355d6bb5Sswilcox 	}
2110355d6bb5Sswilcox 
2111355d6bb5Sswilcox 	if (cgp->cg_ndblk != ndblk) {
2112355d6bb5Sswilcox 		cgp->cg_ndblk = ndblk;
2113355d6bb5Sswilcox 	}
2114355d6bb5Sswilcox 
2115355d6bb5Sswilcox 	/*
2116355d6bb5Sswilcox 	 * For the rotors, any position's valid, so pick the one we know
2117355d6bb5Sswilcox 	 * will always exist.
2118355d6bb5Sswilcox 	 */
2119355d6bb5Sswilcox 	if ((cgp->cg_rotor < 0) || (cgp->cg_rotor >= cgp->cg_ndblk)) {
2120355d6bb5Sswilcox 		cgp->cg_rotor = 0;
2121355d6bb5Sswilcox 	}
2122355d6bb5Sswilcox 
2123355d6bb5Sswilcox 	if ((cgp->cg_frotor < 0) || (cgp->cg_frotor >= cgp->cg_ndblk)) {
2124355d6bb5Sswilcox 		cgp->cg_frotor = 0;
2125355d6bb5Sswilcox 	}
2126355d6bb5Sswilcox 
2127355d6bb5Sswilcox 	if ((cgp->cg_irotor < 0) || (cgp->cg_irotor >= sblock.fs_ipg)) {
2128355d6bb5Sswilcox 		cgp->cg_irotor = 0;
2129355d6bb5Sswilcox 	}
2130355d6bb5Sswilcox 
2131355d6bb5Sswilcox 	/*
2132355d6bb5Sswilcox 	 * For btotoff and boff, if they're misaligned they won't
2133355d6bb5Sswilcox 	 * match the expected values, so we're catching both cases
2134355d6bb5Sswilcox 	 * here.  Of course, if any of these are off, it seems likely
2135355d6bb5Sswilcox 	 * that the tables really won't be where we calculate they
2136355d6bb5Sswilcox 	 * should be anyway.
2137355d6bb5Sswilcox 	 */
2138355d6bb5Sswilcox 	if (cgp->cg_btotoff != exp_btotoff) {
2139355d6bb5Sswilcox 		cgp->cg_btotoff = exp_btotoff;
2140355d6bb5Sswilcox 	}
2141355d6bb5Sswilcox 
2142355d6bb5Sswilcox 	if (cgp->cg_boff != exp_boff) {
2143355d6bb5Sswilcox 		cgp->cg_boff = exp_boff;
2144355d6bb5Sswilcox 	}
2145355d6bb5Sswilcox 
2146355d6bb5Sswilcox 	if (cgp->cg_iusedoff != exp_iusedoff) {
2147355d6bb5Sswilcox 		cgp->cg_iusedoff = exp_iusedoff;
2148355d6bb5Sswilcox 	}
2149355d6bb5Sswilcox 
2150355d6bb5Sswilcox 	if (cgp->cg_freeoff != exp_freeoff) {
2151355d6bb5Sswilcox 		cgp->cg_freeoff = exp_freeoff;
2152355d6bb5Sswilcox 	}
2153355d6bb5Sswilcox 
2154355d6bb5Sswilcox 	if (cgp->cg_nextfreeoff != exp_nextfreeoff) {
2155355d6bb5Sswilcox 		cgp->cg_nextfreeoff = exp_nextfreeoff;
2156355d6bb5Sswilcox 	}
2157355d6bb5Sswilcox 
215877a343abSabalfour 	/*
215977a343abSabalfour 	 * Reset the magic, as we've recreated this cg, also
216077a343abSabalfour 	 * update the cg_time, as we're writing out the cg
216177a343abSabalfour 	 */
216277a343abSabalfour 	cgp->cg_magic = CG_MAGIC;
216377a343abSabalfour 	cgp->cg_time = time(NULL);
216477a343abSabalfour 
2165355d6bb5Sswilcox 	/*
2166355d6bb5Sswilcox 	 * We know there was at least one correctable problem,
2167355d6bb5Sswilcox 	 * or else we wouldn't have been called.  So instead of
2168355d6bb5Sswilcox 	 * marking the buffer dirty N times above, just do it
2169355d6bb5Sswilcox 	 * once here.
2170355d6bb5Sswilcox 	 */
2171355d6bb5Sswilcox 	cgdirty();
2172355d6bb5Sswilcox }
2173355d6bb5Sswilcox 
2174355d6bb5Sswilcox void
2175355d6bb5Sswilcox examinelog(daddr32_t start, void (*cb)(daddr32_t))
2176355d6bb5Sswilcox {
2177355d6bb5Sswilcox 	struct bufarea *bp;
2178355d6bb5Sswilcox 	extent_block_t *ebp;
2179355d6bb5Sswilcox 	extent_t *ep;
2180355d6bb5Sswilcox 	daddr32_t nfno, fno;
2181355d6bb5Sswilcox 	int i;
2182355d6bb5Sswilcox 	int j;
2183355d6bb5Sswilcox 
2184355d6bb5Sswilcox 	if (start < SBLOCK)
2185355d6bb5Sswilcox 		return;
2186355d6bb5Sswilcox 
2187355d6bb5Sswilcox 	/*
2188355d6bb5Sswilcox 	 * Read errors will return zeros, which will cause us
2189355d6bb5Sswilcox 	 * to do nothing harmful, so don't need to handle it.
2190355d6bb5Sswilcox 	 */
2191355d6bb5Sswilcox 	bp = getdatablk(logbtofrag(&sblock, sblock.fs_logbno),
2192355d6bb5Sswilcox 			(size_t)sblock.fs_bsize);
2193355d6bb5Sswilcox 	ebp = (void *)bp->b_un.b_buf;
2194355d6bb5Sswilcox 
2195355d6bb5Sswilcox 	/*
2196355d6bb5Sswilcox 	 * Does it look like a log allocation table?
2197355d6bb5Sswilcox 	 */
2198355d6bb5Sswilcox 	/* LINTED pointer cast is aligned */
2199355d6bb5Sswilcox 	if (!log_checksum(&ebp->chksum, (int32_t *)bp->b_un.b_buf,
2200355d6bb5Sswilcox 	    sblock.fs_bsize))
2201355d6bb5Sswilcox 		return;
2202355d6bb5Sswilcox 	if (ebp->type != LUFS_EXTENTS || ebp->nextents == 0)
2203355d6bb5Sswilcox 		return;
2204355d6bb5Sswilcox 
2205355d6bb5Sswilcox 	ep = &ebp->extents[0];
2206355d6bb5Sswilcox 	for (i = 0; i < ebp->nextents; ++i, ++ep) {
2207355d6bb5Sswilcox 		fno = logbtofrag(&sblock, ep->pbno);
2208355d6bb5Sswilcox 		nfno = dbtofsb(&sblock, ep->nbno);
2209355d6bb5Sswilcox 		for (j = 0; j < nfno; ++j, ++fno) {
2210355d6bb5Sswilcox 			/*
2211355d6bb5Sswilcox 			 * Invoke the callback first, so that pass1 can
2212355d6bb5Sswilcox 			 * mark the log blocks in-use.  Then, if any
2213355d6bb5Sswilcox 			 * subsequent pass over the log shows us that a
2214355d6bb5Sswilcox 			 * block got freed (say, it was also claimed by
2215355d6bb5Sswilcox 			 * an inode that we cleared), we can safely declare
2216355d6bb5Sswilcox 			 * the log bad.
2217355d6bb5Sswilcox 			 */
2218355d6bb5Sswilcox 			if (cb != NULL)
2219355d6bb5Sswilcox 				(*cb)(fno);
2220355d6bb5Sswilcox 			if (!testbmap(fno))
2221355d6bb5Sswilcox 				islogok = 0;
2222355d6bb5Sswilcox 		}
2223355d6bb5Sswilcox 	}
2224355d6bb5Sswilcox 	brelse(bp);
2225355d6bb5Sswilcox 
2226355d6bb5Sswilcox 	if (cb != NULL) {
2227355d6bb5Sswilcox 		fno = logbtofrag(&sblock, sblock.fs_logbno);
2228355d6bb5Sswilcox 		for (j = 0; j < sblock.fs_frag; ++j, ++fno)
2229355d6bb5Sswilcox 			(*cb)(fno);
2230355d6bb5Sswilcox 	}
2231355d6bb5Sswilcox }
2232355d6bb5Sswilcox 
2233355d6bb5Sswilcox static void
2234355d6bb5Sswilcox freelogblk(daddr32_t frag)
2235355d6bb5Sswilcox {
2236355d6bb5Sswilcox 	freeblk(sblock.fs_logbno, frag, 1);
2237355d6bb5Sswilcox }
2238355d6bb5Sswilcox 
2239355d6bb5Sswilcox caddr_t
2240355d6bb5Sswilcox file_id(fsck_ino_t inum, mode_t mode)
2241355d6bb5Sswilcox {
2242355d6bb5Sswilcox 	static char name[MAXPATHLEN + 1];
2243355d6bb5Sswilcox 
2244355d6bb5Sswilcox 	if (lfdir == inum) {
2245355d6bb5Sswilcox 		return (lfname);
2246355d6bb5Sswilcox 	}
2247355d6bb5Sswilcox 
2248355d6bb5Sswilcox 	if ((mode & IFMT) == IFDIR) {
2249355d6bb5Sswilcox 		(void) strcpy(name, "DIR");
2250355d6bb5Sswilcox 	} else if ((mode & IFMT) == IFATTRDIR) {
2251355d6bb5Sswilcox 		(void) strcpy(name, "ATTR DIR");
2252355d6bb5Sswilcox 	} else if ((mode & IFMT) == IFSHAD) {
2253355d6bb5Sswilcox 		(void) strcpy(name, "ACL");
2254355d6bb5Sswilcox 	} else {
2255355d6bb5Sswilcox 		(void) strcpy(name, "FILE");
2256355d6bb5Sswilcox 	}
2257355d6bb5Sswilcox 
2258355d6bb5Sswilcox 	return (name);
2259355d6bb5Sswilcox }
2260355d6bb5Sswilcox 
2261355d6bb5Sswilcox /*
2262355d6bb5Sswilcox  * Simple initializer for inodesc structures, so users of only a few
2263355d6bb5Sswilcox  * fields don't have to worry about getting the right defaults for
2264355d6bb5Sswilcox  * everything out.
2265355d6bb5Sswilcox  */
2266355d6bb5Sswilcox void
2267355d6bb5Sswilcox init_inodesc(struct inodesc *idesc)
2268355d6bb5Sswilcox {
2269355d6bb5Sswilcox 	/*
2270355d6bb5Sswilcox 	 * Most fields should be zero, just hit the special cases.
2271355d6bb5Sswilcox 	 */
2272355d6bb5Sswilcox 	(void) memset((void *)idesc, 0, sizeof (struct inodesc));
2273355d6bb5Sswilcox 	idesc->id_fix = DONTKNOW;
2274355d6bb5Sswilcox 	idesc->id_lbn = -1;
2275355d6bb5Sswilcox 	idesc->id_truncto = -1;
2276355d6bb5Sswilcox 	idesc->id_firsthole = -1;
2277355d6bb5Sswilcox }
2278355d6bb5Sswilcox 
2279355d6bb5Sswilcox /*
2280355d6bb5Sswilcox  * Compare routine for tsearch(C) to use on ino_t instances.
2281355d6bb5Sswilcox  */
2282355d6bb5Sswilcox int
2283355d6bb5Sswilcox ino_t_cmp(const void *left, const void *right)
2284355d6bb5Sswilcox {
2285355d6bb5Sswilcox 	const fsck_ino_t lino = (const fsck_ino_t)left;
2286355d6bb5Sswilcox 	const fsck_ino_t rino = (const fsck_ino_t)right;
2287355d6bb5Sswilcox 
2288355d6bb5Sswilcox 	return (lino - rino);
2289355d6bb5Sswilcox }
2290355d6bb5Sswilcox 
2291355d6bb5Sswilcox int
2292355d6bb5Sswilcox cgisdirty(void)
2293355d6bb5Sswilcox {
2294355d6bb5Sswilcox 	return (cgblk.b_dirty);
2295355d6bb5Sswilcox }
2296355d6bb5Sswilcox 
2297355d6bb5Sswilcox void
2298355d6bb5Sswilcox cgflush(void)
2299355d6bb5Sswilcox {
2300355d6bb5Sswilcox 	flush(fswritefd, &cgblk);
2301355d6bb5Sswilcox }
2302355d6bb5Sswilcox 
2303355d6bb5Sswilcox void
2304355d6bb5Sswilcox dirty(struct bufarea *bp)
2305355d6bb5Sswilcox {
2306355d6bb5Sswilcox 	if (fswritefd < 0) {
2307355d6bb5Sswilcox 		pfatal("SETTING DIRTY FLAG IN READ_ONLY MODE\n");
2308355d6bb5Sswilcox 	} else {
2309355d6bb5Sswilcox 		(bp)->b_dirty = 1;
2310355d6bb5Sswilcox 		isdirty = 1;
2311355d6bb5Sswilcox 	}
2312355d6bb5Sswilcox }
2313355d6bb5Sswilcox 
2314355d6bb5Sswilcox void
2315355d6bb5Sswilcox initbarea(struct bufarea *bp)
2316355d6bb5Sswilcox {
2317355d6bb5Sswilcox 	(bp)->b_dirty = 0;
2318355d6bb5Sswilcox 	(bp)->b_bno = (diskaddr_t)-1LL;
2319355d6bb5Sswilcox 	(bp)->b_flags = 0;
2320355d6bb5Sswilcox 	(bp)->b_cnt = 0;
2321355d6bb5Sswilcox 	(bp)->b_errs = 0;
2322355d6bb5Sswilcox }
2323355d6bb5Sswilcox 
2324355d6bb5Sswilcox /*
2325355d6bb5Sswilcox  * Partition-sizing routines adapted from ../newfs/newfs.c.
2326355d6bb5Sswilcox  * Needed because calcsb() needs to use mkfs to work out what the
2327355d6bb5Sswilcox  * superblock should be, and mkfs insists on being told how many
2328355d6bb5Sswilcox  * sectors to use.
2329355d6bb5Sswilcox  *
2330355d6bb5Sswilcox  * Error handling assumes we're never called while preening.
2331355d6bb5Sswilcox  *
2332355d6bb5Sswilcox  * XXX This should be extracted into a ../ufslib.{c,h},
2333355d6bb5Sswilcox  *     in the same spirit to ../../fslib.{c,h}.  Once that is
2334355d6bb5Sswilcox  *     done, both fsck and newfs should be modified to link
2335355d6bb5Sswilcox  *     against it.
2336355d6bb5Sswilcox  */
2337355d6bb5Sswilcox 
2338355d6bb5Sswilcox static int label_type;
2339355d6bb5Sswilcox 
2340355d6bb5Sswilcox #define	LABEL_TYPE_VTOC		1
2341355d6bb5Sswilcox #define	LABEL_TYPE_EFI		2
2342355d6bb5Sswilcox #define	LABEL_TYPE_OTHER	3
2343355d6bb5Sswilcox 
2344355d6bb5Sswilcox #define	MB			(1024 * 1024)
2345355d6bb5Sswilcox #define	SECTORS_PER_TERABYTE	(1LL << 31)
2346355d6bb5Sswilcox #define	FS_SIZE_UPPER_LIMIT	0x100000000000LL
2347355d6bb5Sswilcox 
2348355d6bb5Sswilcox diskaddr_t
2349355d6bb5Sswilcox getdisksize(caddr_t disk, int fd)
2350355d6bb5Sswilcox {
2351355d6bb5Sswilcox 	int rpm;
2352355d6bb5Sswilcox 	struct dk_geom g;
2353355d6bb5Sswilcox 	struct dk_cinfo ci;
2354355d6bb5Sswilcox 	diskaddr_t actual_size;
2355355d6bb5Sswilcox 
2356355d6bb5Sswilcox 	/*
2357355d6bb5Sswilcox 	 * get_device_size() determines the actual size of the
2358355d6bb5Sswilcox 	 * device, and also the disk's attributes, such as geometry.
2359355d6bb5Sswilcox 	 */
2360355d6bb5Sswilcox 	actual_size = get_device_size(fd, disk);
2361355d6bb5Sswilcox 
2362355d6bb5Sswilcox 	if (label_type == LABEL_TYPE_VTOC) {
2363355d6bb5Sswilcox 		if (ioctl(fd, DKIOCGGEOM, &g)) {
2364355d6bb5Sswilcox 			pwarn("%s: Unable to read Disk geometry", disk);
2365355d6bb5Sswilcox 			return (0);
2366355d6bb5Sswilcox 		}
2367355d6bb5Sswilcox 		if (sblock.fs_nsect == 0)
2368355d6bb5Sswilcox 			sblock.fs_nsect = g.dkg_nsect;
2369355d6bb5Sswilcox 		if (sblock.fs_ntrak == 0)
2370355d6bb5Sswilcox 			sblock.fs_ntrak = g.dkg_nhead;
2371355d6bb5Sswilcox 		if (sblock.fs_rps == 0) {
2372355d6bb5Sswilcox 			rpm = ((int)g.dkg_rpm <= 0) ? 3600: g.dkg_rpm;
2373355d6bb5Sswilcox 			sblock.fs_rps = rpm / 60;
2374355d6bb5Sswilcox 		}
2375355d6bb5Sswilcox 	}
2376355d6bb5Sswilcox 
2377355d6bb5Sswilcox 	if (sblock.fs_bsize == 0)
2378355d6bb5Sswilcox 		sblock.fs_bsize = MAXBSIZE;
2379355d6bb5Sswilcox 
2380355d6bb5Sswilcox 	/*
2381355d6bb5Sswilcox 	 * Adjust maxcontig by the device's maxtransfer. If maxtransfer
2382355d6bb5Sswilcox 	 * information is not available, default to the min of a MB and
2383355d6bb5Sswilcox 	 * maxphys.
2384355d6bb5Sswilcox 	 */
2385355d6bb5Sswilcox 	if (sblock.fs_maxcontig == -1 && ioctl(fd, DKIOCINFO, &ci) == 0) {
2386355d6bb5Sswilcox 		sblock.fs_maxcontig = ci.dki_maxtransfer * DEV_BSIZE;
2387355d6bb5Sswilcox 		if (sblock.fs_maxcontig < 0) {
2388355d6bb5Sswilcox 			int gotit, maxphys;
2389355d6bb5Sswilcox 
2390355d6bb5Sswilcox 			gotit = fsgetmaxphys(&maxphys, NULL);
2391355d6bb5Sswilcox 
2392355d6bb5Sswilcox 			/*
2393355d6bb5Sswilcox 			 * If we cannot get the maxphys value, default
2394355d6bb5Sswilcox 			 * to ufs_maxmaxphys (MB).
2395355d6bb5Sswilcox 			 */
2396355d6bb5Sswilcox 			if (gotit) {
2397355d6bb5Sswilcox 				sblock.fs_maxcontig = MIN(maxphys, MB);
2398355d6bb5Sswilcox 			} else {
2399355d6bb5Sswilcox 				sblock.fs_maxcontig = MB;
2400355d6bb5Sswilcox 			}
2401355d6bb5Sswilcox 		}
2402355d6bb5Sswilcox 		sblock.fs_maxcontig /= sblock.fs_bsize;
2403355d6bb5Sswilcox 	}
2404355d6bb5Sswilcox 
2405355d6bb5Sswilcox 	return (actual_size);
2406355d6bb5Sswilcox }
2407355d6bb5Sswilcox 
2408355d6bb5Sswilcox /*
2409355d6bb5Sswilcox  * Figure out how big the partition we're dealing with is.
2410355d6bb5Sswilcox  */
2411355d6bb5Sswilcox static diskaddr_t
2412355d6bb5Sswilcox get_device_size(int fd, caddr_t name)
2413355d6bb5Sswilcox {
2414355d6bb5Sswilcox 	struct vtoc vtoc;
2415355d6bb5Sswilcox 	struct dk_gpt *efi_vtoc;
2416355d6bb5Sswilcox 	diskaddr_t slicesize = 0;
2417355d6bb5Sswilcox 
2418355d6bb5Sswilcox 	int index = read_vtoc(fd, &vtoc);
2419355d6bb5Sswilcox 
2420355d6bb5Sswilcox 	if (index >= 0) {
2421355d6bb5Sswilcox 		label_type = LABEL_TYPE_VTOC;
2422355d6bb5Sswilcox 	} else {
2423355d6bb5Sswilcox 		if (index == VT_ENOTSUP || index == VT_ERROR) {
2424355d6bb5Sswilcox 			/* it might be an EFI label */
2425355d6bb5Sswilcox 			index = efi_alloc_and_read(fd, &efi_vtoc);
2426355d6bb5Sswilcox 			if (index >= 0)
2427355d6bb5Sswilcox 				label_type = LABEL_TYPE_EFI;
2428355d6bb5Sswilcox 		}
2429355d6bb5Sswilcox 	}
2430355d6bb5Sswilcox 
2431355d6bb5Sswilcox 	if (index < 0) {
2432355d6bb5Sswilcox 		/*
2433355d6bb5Sswilcox 		 * Since both attempts to read the label failed, we're
2434355d6bb5Sswilcox 		 * going to fall back to a brute force approach to
2435355d6bb5Sswilcox 		 * determining the device's size:  see how far out we can
2436355d6bb5Sswilcox 		 * perform reads on the device.
2437355d6bb5Sswilcox 		 */
2438355d6bb5Sswilcox 
2439355d6bb5Sswilcox 		slicesize = brute_force_get_device_size(fd);
2440355d6bb5Sswilcox 		if (slicesize == 0) {
2441355d6bb5Sswilcox 			switch (index) {
2442355d6bb5Sswilcox 			case VT_ERROR:
2443355d6bb5Sswilcox 				pwarn("%s: %s\n", name, strerror(errno));
2444355d6bb5Sswilcox 				break;
2445355d6bb5Sswilcox 			case VT_EIO:
2446355d6bb5Sswilcox 				pwarn("%s: I/O error accessing VTOC", name);
2447355d6bb5Sswilcox 				break;
2448355d6bb5Sswilcox 			case VT_EINVAL:
2449355d6bb5Sswilcox 				pwarn("%s: Invalid field in VTOC", name);
2450355d6bb5Sswilcox 				break;
2451355d6bb5Sswilcox 			default:
2452355d6bb5Sswilcox 				pwarn("%s: unknown error %d accessing VTOC",
2453355d6bb5Sswilcox 				    name, index);
2454355d6bb5Sswilcox 				break;
2455355d6bb5Sswilcox 			}
2456355d6bb5Sswilcox 			return (0);
2457355d6bb5Sswilcox 		} else {
2458355d6bb5Sswilcox 			label_type = LABEL_TYPE_OTHER;
2459355d6bb5Sswilcox 		}
2460355d6bb5Sswilcox 	}
2461355d6bb5Sswilcox 
2462355d6bb5Sswilcox 	if (label_type == LABEL_TYPE_EFI) {
2463355d6bb5Sswilcox 		slicesize = efi_vtoc->efi_parts[index].p_size;
2464355d6bb5Sswilcox 		efi_free(efi_vtoc);
2465355d6bb5Sswilcox 	} else if (label_type == LABEL_TYPE_VTOC) {
2466355d6bb5Sswilcox 		/*
2467355d6bb5Sswilcox 		 * In the vtoc struct, p_size is a 32-bit signed quantity.
2468355d6bb5Sswilcox 		 * In the dk_gpt struct (efi's version of the vtoc), p_size
2469355d6bb5Sswilcox 		 * is an unsigned 64-bit quantity.  By casting the vtoc's
2470355d6bb5Sswilcox 		 * psize to an unsigned 32-bit quantity, it will be copied
2471355d6bb5Sswilcox 		 * to 'slicesize' (an unsigned 64-bit diskaddr_t) without
2472355d6bb5Sswilcox 		 * sign extension.
2473355d6bb5Sswilcox 		 */
2474355d6bb5Sswilcox 
2475355d6bb5Sswilcox 		slicesize = (uint32_t)vtoc.v_part[index].p_size;
2476355d6bb5Sswilcox 	}
2477355d6bb5Sswilcox 
2478355d6bb5Sswilcox 	return (slicesize);
2479355d6bb5Sswilcox }
2480355d6bb5Sswilcox 
2481355d6bb5Sswilcox /*
2482355d6bb5Sswilcox  * brute_force_get_device_size
2483355d6bb5Sswilcox  *
2484355d6bb5Sswilcox  * Determine the size of the device by seeing how far we can
2485355d6bb5Sswilcox  * read.  Doing an llseek( , , SEEK_END) would probably work
2486355d6bb5Sswilcox  * in most cases, but we've seen at least one third-party driver
2487355d6bb5Sswilcox  * which doesn't correctly support the SEEK_END option when the
2488355d6bb5Sswilcox  * the device is greater than a terabyte.
2489355d6bb5Sswilcox  */
2490355d6bb5Sswilcox 
2491355d6bb5Sswilcox static diskaddr_t
2492355d6bb5Sswilcox brute_force_get_device_size(int fd)
2493355d6bb5Sswilcox {
2494355d6bb5Sswilcox 	diskaddr_t	min_fail = 0;
2495355d6bb5Sswilcox 	diskaddr_t	max_succeed = 0;
2496355d6bb5Sswilcox 	diskaddr_t	cur_db_off;
2497355d6bb5Sswilcox 	char 		buf[DEV_BSIZE];
2498355d6bb5Sswilcox 
2499355d6bb5Sswilcox 	/*
2500355d6bb5Sswilcox 	 * First, see if we can read the device at all, just to
2501355d6bb5Sswilcox 	 * eliminate errors that have nothing to do with the
2502355d6bb5Sswilcox 	 * device's size.
2503355d6bb5Sswilcox 	 */
2504355d6bb5Sswilcox 
2505355d6bb5Sswilcox 	if (((llseek(fd, (offset_t)0, SEEK_SET)) == -1) ||
2506355d6bb5Sswilcox 	    ((read(fd, buf, DEV_BSIZE)) == -1))
2507355d6bb5Sswilcox 		return (0);  /* can't determine size */
2508355d6bb5Sswilcox 
2509355d6bb5Sswilcox 	/*
2510355d6bb5Sswilcox 	 * Now, go sequentially through the multiples of 4TB
2511355d6bb5Sswilcox 	 * to find the first read that fails (this isn't strictly
2512355d6bb5Sswilcox 	 * the most efficient way to find the actual size if the
2513355d6bb5Sswilcox 	 * size really could be anything between 0 and 2**64 bytes.
2514355d6bb5Sswilcox 	 * We expect the sizes to be less than 16 TB for some time,
2515355d6bb5Sswilcox 	 * so why do a bunch of reads that are larger than that?
2516355d6bb5Sswilcox 	 * However, this algorithm *will* work for sizes of greater
2517355d6bb5Sswilcox 	 * than 16 TB.  We're just not optimizing for those sizes.)
2518355d6bb5Sswilcox 	 */
2519355d6bb5Sswilcox 
2520355d6bb5Sswilcox 	/*
2521355d6bb5Sswilcox 	 * XXX lint uses 32-bit arithmetic for doing flow analysis.
2522355d6bb5Sswilcox 	 * We're using > 32-bit constants here.  Therefore, its flow
2523355d6bb5Sswilcox 	 * analysis is wrong.  For the time being, ignore complaints
2524355d6bb5Sswilcox 	 * from it about the body of the for() being unreached.
2525355d6bb5Sswilcox 	 */
2526355d6bb5Sswilcox 	for (cur_db_off = SECTORS_PER_TERABYTE * 4;
2527355d6bb5Sswilcox 	    (min_fail == 0) && (cur_db_off < FS_SIZE_UPPER_LIMIT);
2528355d6bb5Sswilcox 	    cur_db_off += 4 * SECTORS_PER_TERABYTE) {
2529355d6bb5Sswilcox 		if ((llseek(fd, (offset_t)(cur_db_off * DEV_BSIZE),
2530355d6bb5Sswilcox 		    SEEK_SET) == -1) ||
2531355d6bb5Sswilcox 		    (read(fd, buf, DEV_BSIZE) != DEV_BSIZE))
2532355d6bb5Sswilcox 			min_fail = cur_db_off;
2533355d6bb5Sswilcox 		else
2534355d6bb5Sswilcox 			max_succeed = cur_db_off;
2535355d6bb5Sswilcox 	}
2536355d6bb5Sswilcox 
2537355d6bb5Sswilcox 	/*
2538355d6bb5Sswilcox 	 * XXX Same lint flow analysis problem as above.
2539355d6bb5Sswilcox 	 */
2540355d6bb5Sswilcox 	if (min_fail == 0)
2541355d6bb5Sswilcox 		return (0);
2542355d6bb5Sswilcox 
2543355d6bb5Sswilcox 	/*
2544355d6bb5Sswilcox 	 * We now know that the size of the device is less than
2545355d6bb5Sswilcox 	 * min_fail and greater than or equal to max_succeed.  Now
2546355d6bb5Sswilcox 	 * keep splitting the difference until the actual size in
2547355d6bb5Sswilcox 	 * sectors in known.  We also know that the difference
2548355d6bb5Sswilcox 	 * between max_succeed and min_fail at this time is
2549355d6bb5Sswilcox 	 * 4 * SECTORS_PER_TERABYTE, which is a power of two, which
2550355d6bb5Sswilcox 	 * simplifies the math below.
2551355d6bb5Sswilcox 	 */
2552355d6bb5Sswilcox 
2553355d6bb5Sswilcox 	while (min_fail - max_succeed > 1) {
2554355d6bb5Sswilcox 		cur_db_off = max_succeed + (min_fail - max_succeed)/2;
2555355d6bb5Sswilcox 		if (((llseek(fd, (offset_t)(cur_db_off * DEV_BSIZE),
2556355d6bb5Sswilcox 		    SEEK_SET)) == -1) ||
2557355d6bb5Sswilcox 		    ((read(fd, buf, DEV_BSIZE)) != DEV_BSIZE))
2558355d6bb5Sswilcox 			min_fail = cur_db_off;
2559355d6bb5Sswilcox 		else
2560355d6bb5Sswilcox 			max_succeed = cur_db_off;
2561355d6bb5Sswilcox 	}
2562355d6bb5Sswilcox 
2563355d6bb5Sswilcox 	/* the size is the last successfully read sector offset plus one */
2564355d6bb5Sswilcox 	return (max_succeed + 1);
2565355d6bb5Sswilcox }
2566355d6bb5Sswilcox 
2567355d6bb5Sswilcox static void
2568355d6bb5Sswilcox vfileerror(fsck_ino_t cwd, fsck_ino_t ino, caddr_t fmt, va_list ap)
2569355d6bb5Sswilcox {
2570355d6bb5Sswilcox 	struct dinode *dp;
2571355d6bb5Sswilcox 	char pathbuf[MAXPATHLEN + 1];
2572355d6bb5Sswilcox 
2573355d6bb5Sswilcox 	vpwarn(fmt, ap);
2574355d6bb5Sswilcox 	(void) putchar(' ');
2575355d6bb5Sswilcox 	pinode(ino);
2576355d6bb5Sswilcox 	(void) printf("\n");
2577355d6bb5Sswilcox 	getpathname(pathbuf, cwd, ino);
2578355d6bb5Sswilcox 	if (ino < UFSROOTINO || ino > maxino) {
2579355d6bb5Sswilcox 		pfatal("NAME=%s\n", pathbuf);
2580355d6bb5Sswilcox 		return;
2581355d6bb5Sswilcox 	}
2582355d6bb5Sswilcox 	dp = ginode(ino);
2583355d6bb5Sswilcox 	if (ftypeok(dp))
2584355d6bb5Sswilcox 		pfatal("%s=%s\n", file_id(ino, dp->di_mode), pathbuf);
2585355d6bb5Sswilcox 	else
2586355d6bb5Sswilcox 		pfatal("NAME=%s\n", pathbuf);
2587355d6bb5Sswilcox }
2588355d6bb5Sswilcox 
2589355d6bb5Sswilcox void
2590355d6bb5Sswilcox direrror(fsck_ino_t ino, caddr_t fmt, ...)
2591355d6bb5Sswilcox {
2592355d6bb5Sswilcox 	va_list ap;
2593355d6bb5Sswilcox 
2594355d6bb5Sswilcox 	va_start(ap, fmt);
2595355d6bb5Sswilcox 	vfileerror(ino, ino, fmt, ap);
2596355d6bb5Sswilcox 	va_end(ap);
2597355d6bb5Sswilcox }
2598355d6bb5Sswilcox 
2599355d6bb5Sswilcox static void
2600355d6bb5Sswilcox vdirerror(fsck_ino_t ino, caddr_t fmt, va_list ap)
2601355d6bb5Sswilcox {
2602355d6bb5Sswilcox 	vfileerror(ino, ino, fmt, ap);
2603355d6bb5Sswilcox }
2604355d6bb5Sswilcox 
2605355d6bb5Sswilcox void
2606355d6bb5Sswilcox fileerror(fsck_ino_t cwd, fsck_ino_t ino, caddr_t fmt, ...)
2607355d6bb5Sswilcox {
2608355d6bb5Sswilcox 	va_list ap;
2609355d6bb5Sswilcox 
2610355d6bb5Sswilcox 	va_start(ap, fmt);
2611355d6bb5Sswilcox 	vfileerror(cwd, ino, fmt, ap);
2612355d6bb5Sswilcox 	va_end(ap);
2613355d6bb5Sswilcox }
2614355d6bb5Sswilcox 
2615355d6bb5Sswilcox /*
2616355d6bb5Sswilcox  * Adds the given inode to the orphaned-directories list, limbo_dirs.
2617355d6bb5Sswilcox  * Assumes that the caller has set INCLEAR in the inode's statemap[]
2618355d6bb5Sswilcox  * entry.
2619355d6bb5Sswilcox  *
2620355d6bb5Sswilcox  * With INCLEAR set, the inode will get ignored by passes 2 and 3,
2621355d6bb5Sswilcox  * meaning it's effectively an orphan.  It needs to be noted now, so
2622355d6bb5Sswilcox  * it will be remembered in pass 4.
2623355d6bb5Sswilcox  */
2624355d6bb5Sswilcox 
2625355d6bb5Sswilcox void
2626355d6bb5Sswilcox add_orphan_dir(fsck_ino_t ino)
2627355d6bb5Sswilcox {
2628355d6bb5Sswilcox 	if (tsearch((void *)ino, &limbo_dirs, ino_t_cmp) == NULL)
2629355d6bb5Sswilcox 		errexit("add_orphan_dir: out of memory");
2630355d6bb5Sswilcox }
2631355d6bb5Sswilcox 
2632355d6bb5Sswilcox /*
2633355d6bb5Sswilcox  * Remove an inode from the orphaned-directories list, presumably
2634355d6bb5Sswilcox  * because it's been cleared.
2635355d6bb5Sswilcox  */
2636355d6bb5Sswilcox void
2637355d6bb5Sswilcox remove_orphan_dir(fsck_ino_t ino)
2638355d6bb5Sswilcox {
2639355d6bb5Sswilcox 	(void) tdelete((void *)ino, &limbo_dirs, ino_t_cmp);
2640355d6bb5Sswilcox }
2641355d6bb5Sswilcox 
2642355d6bb5Sswilcox /*
2643355d6bb5Sswilcox  * log_setsum() and log_checksum() are equivalent to lufs.c:setsum()
2644355d6bb5Sswilcox  * and lufs.c:checksum().
2645355d6bb5Sswilcox  */
2646355d6bb5Sswilcox static void
2647355d6bb5Sswilcox log_setsum(int32_t *sp, int32_t *lp, int nb)
2648355d6bb5Sswilcox {
2649355d6bb5Sswilcox 	int32_t csum = 0;
2650355d6bb5Sswilcox 
2651355d6bb5Sswilcox 	*sp = 0;
2652355d6bb5Sswilcox 	nb /= sizeof (int32_t);
2653355d6bb5Sswilcox 	while (nb--)
2654355d6bb5Sswilcox 		csum += *lp++;
2655355d6bb5Sswilcox 	*sp = csum;
2656355d6bb5Sswilcox }
2657355d6bb5Sswilcox 
2658355d6bb5Sswilcox static int
2659355d6bb5Sswilcox log_checksum(int32_t *sp, int32_t *lp, int nb)
2660355d6bb5Sswilcox {
2661355d6bb5Sswilcox 	int32_t ssum = *sp;
2662355d6bb5Sswilcox 
2663355d6bb5Sswilcox 	log_setsum(sp, lp, nb);
2664355d6bb5Sswilcox 	if (ssum != *sp) {
2665355d6bb5Sswilcox 		*sp = ssum;
2666355d6bb5Sswilcox 		return (0);
2667355d6bb5Sswilcox 	}
2668355d6bb5Sswilcox 	return (1);
26697c478bd9Sstevel@tonic-gate }
2670