xref: /illumos-gate/usr/src/cmd/fs.d/ufs/fsck/utilities.c (revision 355d6bb5)
17c478bd9Sstevel@tonic-gate /*
2*355d6bb5Sswilcox  * Copyright 2005 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>
31*355d6bb5Sswilcox #include <stdlib.h>
32*355d6bb5Sswilcox #include <unistd.h>
33*355d6bb5Sswilcox #include <stdarg.h>
34*355d6bb5Sswilcox #include <libadm.h>
35*355d6bb5Sswilcox #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>
43*355d6bb5Sswilcox #include <sys/fs/ufs_inode.h>
44*355d6bb5Sswilcox #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>
51*355d6bb5Sswilcox #include <fcntl.h>
52*355d6bb5Sswilcox #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>
58*355d6bb5Sswilcox #include <sys/cmn_err.h>
59*355d6bb5Sswilcox #include <sys/dkio.h>
60*355d6bb5Sswilcox #include <sys/vtoc.h>
61*355d6bb5Sswilcox #include <sys/efi_partition.h>
62*355d6bb5Sswilcox #include <fslib.h>
63*355d6bb5Sswilcox #include <inttypes.h>
64*355d6bb5Sswilcox #include "fsck.h"
657c478bd9Sstevel@tonic-gate 
66*355d6bb5Sswilcox caddr_t mount_point = NULL;
67*355d6bb5Sswilcox 
68*355d6bb5Sswilcox static int64_t diskreads, totalreads;	/* Disk cache statistics */
69*355d6bb5Sswilcox 
70*355d6bb5Sswilcox static int log_checksum(int32_t *, int32_t *, int);
71*355d6bb5Sswilcox static void vdirerror(fsck_ino_t, caddr_t, va_list);
72*355d6bb5Sswilcox static struct mnttab *search_mnttab(caddr_t, caddr_t, caddr_t, size_t);
73*355d6bb5Sswilcox static struct vfstab *search_vfstab(caddr_t, caddr_t, caddr_t, size_t);
74*355d6bb5Sswilcox static void vpwarn(caddr_t, va_list);
75*355d6bb5Sswilcox static int getline(FILE *, caddr_t, int);
76*355d6bb5Sswilcox static struct bufarea *alloc_bufarea(void);
77*355d6bb5Sswilcox static void rwerror(caddr_t, diskaddr_t, int rval);
78*355d6bb5Sswilcox static void debugclean(void);
79*355d6bb5Sswilcox static void report_io_prob(caddr_t, diskaddr_t, size_t, ssize_t);
80*355d6bb5Sswilcox static void freelogblk(daddr32_t);
81*355d6bb5Sswilcox static void verrexit(caddr_t, va_list);
82*355d6bb5Sswilcox static void vpfatal(caddr_t, va_list);
83*355d6bb5Sswilcox static diskaddr_t get_device_size(int, caddr_t);
84*355d6bb5Sswilcox static diskaddr_t brute_force_get_device_size(int);
85*355d6bb5Sswilcox static void cg_constants(int, daddr32_t *, daddr32_t *, daddr32_t *,
86*355d6bb5Sswilcox 	    daddr32_t *, daddr32_t *, daddr32_t *);
877c478bd9Sstevel@tonic-gate 
88*355d6bb5Sswilcox int
89*355d6bb5Sswilcox 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)
106*355d6bb5Sswilcox 			(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 
111*355d6bb5Sswilcox int
112*355d6bb5Sswilcox 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)
118*355d6bb5Sswilcox 		(void) printf("bad file type for acl I=%d: 0%o\n",
119*355d6bb5Sswilcox 		    dp->di_shadow, dp->di_mode);
1207c478bd9Sstevel@tonic-gate 	return (0);
1217c478bd9Sstevel@tonic-gate }
1227c478bd9Sstevel@tonic-gate 
123*355d6bb5Sswilcox NOTE(PRINTFLIKE(1))
124*355d6bb5Sswilcox int
125*355d6bb5Sswilcox reply(caddr_t fmt, ...)
1267c478bd9Sstevel@tonic-gate {
127*355d6bb5Sswilcox 	va_list ap;
1287c478bd9Sstevel@tonic-gate 	char line[80];
1297c478bd9Sstevel@tonic-gate 
1307c478bd9Sstevel@tonic-gate 	if (preen)
131*355d6bb5Sswilcox 		pfatal("INTERNAL ERROR: GOT TO reply() in preen mode");
1327c478bd9Sstevel@tonic-gate 
1337c478bd9Sstevel@tonic-gate 	if (mflag) {
134*355d6bb5Sswilcox 		/*
135*355d6bb5Sswilcox 		 * We don't know what's going on, so don't potentially
136*355d6bb5Sswilcox 		 * make things worse by having errexit() write stuff
137*355d6bb5Sswilcox 		 * out to disk.
138*355d6bb5Sswilcox 		 */
139*355d6bb5Sswilcox 		(void) printf(
140*355d6bb5Sswilcox 		    "\n%s: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY.\n",
141*355d6bb5Sswilcox 		    devname);
142*355d6bb5Sswilcox 		exit(EXERRFATAL);
1437c478bd9Sstevel@tonic-gate 	}
1447c478bd9Sstevel@tonic-gate 
145*355d6bb5Sswilcox 	va_start(ap, fmt);
146*355d6bb5Sswilcox 	(void) putchar('\n');
147*355d6bb5Sswilcox 	(void) vprintf(fmt, ap);
148*355d6bb5Sswilcox 	(void) putchar('?');
149*355d6bb5Sswilcox 	(void) putchar(' ');
150*355d6bb5Sswilcox 	va_end(ap);
151*355d6bb5Sswilcox 
1527c478bd9Sstevel@tonic-gate 	if (nflag || fswritefd < 0) {
153*355d6bb5Sswilcox 		(void) printf(" no\n\n");
1547c478bd9Sstevel@tonic-gate 		return (0);
1557c478bd9Sstevel@tonic-gate 	}
1567c478bd9Sstevel@tonic-gate 	if (yflag) {
157*355d6bb5Sswilcox 		(void) printf(" yes\n\n");
1587c478bd9Sstevel@tonic-gate 		return (1);
1597c478bd9Sstevel@tonic-gate 	}
160*355d6bb5Sswilcox 	(void) fflush(stdout);
1617c478bd9Sstevel@tonic-gate 	if (getline(stdin, line, sizeof (line)) == EOF)
1627c478bd9Sstevel@tonic-gate 		errexit("\n");
163*355d6bb5Sswilcox 	(void) printf("\n");
164*355d6bb5Sswilcox 	if (line[0] == 'y' || line[0] == 'Y') {
1657c478bd9Sstevel@tonic-gate 		return (1);
166*355d6bb5Sswilcox 	} else {
1677c478bd9Sstevel@tonic-gate 		return (0);
1687c478bd9Sstevel@tonic-gate 	}
1697c478bd9Sstevel@tonic-gate }
1707c478bd9Sstevel@tonic-gate 
171*355d6bb5Sswilcox int
172*355d6bb5Sswilcox getline(FILE *fp, caddr_t loc, int maxlen)
1737c478bd9Sstevel@tonic-gate {
1747c478bd9Sstevel@tonic-gate 	int n;
175*355d6bb5Sswilcox 	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)
183*355d6bb5Sswilcox 			*p++ = (char)n;
1847c478bd9Sstevel@tonic-gate 	}
185*355d6bb5Sswilcox 	*p = '\0';
186*355d6bb5Sswilcox 	/* LINTED pointer difference won't overflow */
1877c478bd9Sstevel@tonic-gate 	return (p - loc);
1887c478bd9Sstevel@tonic-gate }
189*355d6bb5Sswilcox 
1907c478bd9Sstevel@tonic-gate /*
1917c478bd9Sstevel@tonic-gate  * Malloc buffers and set up cache.
1927c478bd9Sstevel@tonic-gate  */
193*355d6bb5Sswilcox void
194*355d6bb5Sswilcox bufinit(void)
1957c478bd9Sstevel@tonic-gate {
1967c478bd9Sstevel@tonic-gate 	struct bufarea *bp;
1977c478bd9Sstevel@tonic-gate 	int bufcnt, i;
198*355d6bb5Sswilcox 	caddr_t bufp;
1997c478bd9Sstevel@tonic-gate 
200*355d6bb5Sswilcox 	bufp = malloc((size_t)sblock.fs_bsize);
201*355d6bb5Sswilcox 	if (bufp == NULL)
202*355d6bb5Sswilcox 		goto nomem;
2037c478bd9Sstevel@tonic-gate 	initbarea(&cgblk);
204*355d6bb5Sswilcox 	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));
211*355d6bb5Sswilcox 		if (bp == NULL) {
2127c478bd9Sstevel@tonic-gate 			if (i >= MINBUFS)
213*355d6bb5Sswilcox 				goto noalloc;
214*355d6bb5Sswilcox 			goto nomem;
215*355d6bb5Sswilcox 		}
216*355d6bb5Sswilcox 
217*355d6bb5Sswilcox 		bufp = malloc((size_t)sblock.fs_bsize);
218*355d6bb5Sswilcox 		if (bufp == NULL) {
219*355d6bb5Sswilcox 			free((void *)bp);
220*355d6bb5Sswilcox 			if (i >= MINBUFS)
221*355d6bb5Sswilcox 				goto noalloc;
222*355d6bb5Sswilcox 			goto nomem;
2237c478bd9Sstevel@tonic-gate 		}
224*355d6bb5Sswilcox 		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 	}
231*355d6bb5Sswilcox noalloc:
2327c478bd9Sstevel@tonic-gate 	bufhead.b_size = i;	/* save number of buffers */
2337c478bd9Sstevel@tonic-gate 	pbp = pdirbp = NULL;
234*355d6bb5Sswilcox 	return;
235*355d6bb5Sswilcox 
236*355d6bb5Sswilcox nomem:
237*355d6bb5Sswilcox 	errexit("cannot allocate buffer pool\n");
238*355d6bb5Sswilcox 	/* NOTREACHED */
239*355d6bb5Sswilcox }
240*355d6bb5Sswilcox 
241*355d6bb5Sswilcox /*
242*355d6bb5Sswilcox  * Undo a bufinit().
243*355d6bb5Sswilcox  */
244*355d6bb5Sswilcox void
245*355d6bb5Sswilcox unbufinit(void)
246*355d6bb5Sswilcox {
247*355d6bb5Sswilcox 	int cnt;
248*355d6bb5Sswilcox 	struct bufarea *bp, *nbp;
249*355d6bb5Sswilcox 
250*355d6bb5Sswilcox 	cnt = 0;
251*355d6bb5Sswilcox 	for (bp = bufhead.b_prev; bp != NULL && bp != &bufhead; bp = nbp) {
252*355d6bb5Sswilcox 		cnt++;
253*355d6bb5Sswilcox 		flush(fswritefd, bp);
254*355d6bb5Sswilcox 		nbp = bp->b_prev;
255*355d6bb5Sswilcox 		/*
256*355d6bb5Sswilcox 		 * We're discarding the entire chain, so this isn't
257*355d6bb5Sswilcox 		 * technically necessary.  However, it doesn't hurt
258*355d6bb5Sswilcox 		 * and lint's data flow analysis is much happier
259*355d6bb5Sswilcox 		 * (this prevents it from thinking there's a chance
260*355d6bb5Sswilcox 		 * of our using memory elsewhere after it's been released).
261*355d6bb5Sswilcox 		 */
262*355d6bb5Sswilcox 		nbp->b_next = bp->b_next;
263*355d6bb5Sswilcox 		bp->b_next->b_prev = nbp;
264*355d6bb5Sswilcox 		free((void *)bp->b_un.b_buf);
265*355d6bb5Sswilcox 		free((void *)bp);
266*355d6bb5Sswilcox 	}
267*355d6bb5Sswilcox 
268*355d6bb5Sswilcox 	if (bufhead.b_size != cnt)
269*355d6bb5Sswilcox 		errexit("Panic: cache lost %d buffers\n",
270*355d6bb5Sswilcox 			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 *
277*355d6bb5Sswilcox 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)
282*355d6bb5Sswilcox 		if (bp->b_bno == fsbtodb(&sblock, blkno)) {
2837c478bd9Sstevel@tonic-gate 			goto foundit;
284*355d6bb5Sswilcox 		}
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();
290*355d6bb5Sswilcox 		if (bp == NULL) {
2917c478bd9Sstevel@tonic-gate 			errexit("deadlocked buffer pool\n");
292*355d6bb5Sswilcox 			/* NOTREACHED */
293*355d6bb5Sswilcox 		}
2947c478bd9Sstevel@tonic-gate 	}
295*355d6bb5Sswilcox 	/*
296*355d6bb5Sswilcox 	 * We're at the same logical level as getblk(), so if there
297*355d6bb5Sswilcox 	 * are any errors, we'll let our caller handle them.
298*355d6bb5Sswilcox 	 */
299*355d6bb5Sswilcox 	diskreads++;
300*355d6bb5Sswilcox 	(void) getblk(bp, blkno, size);
301*355d6bb5Sswilcox 
3027c478bd9Sstevel@tonic-gate foundit:
3037c478bd9Sstevel@tonic-gate 	totalreads++;
3047c478bd9Sstevel@tonic-gate 	bp->b_cnt++;
3057c478bd9Sstevel@tonic-gate 	/*
306*355d6bb5Sswilcox 	 * 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 
321*355d6bb5Sswilcox 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 *
331*355d6bb5Sswilcox 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);
339*355d6bb5Sswilcox 	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 
345*355d6bb5Sswilcox void
346*355d6bb5Sswilcox 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;
354*355d6bb5Sswilcox 
355*355d6bb5Sswilcox 	/*
356*355d6bb5Sswilcox 	 * It's not our buf, so if there are errors, let whoever
357*355d6bb5Sswilcox 	 * acquired it deal with the actual problem.
358*355d6bb5Sswilcox 	 */
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);
364*355d6bb5Sswilcox 	if (bp != &sblk) {
3657c478bd9Sstevel@tonic-gate 		return;
366*355d6bb5Sswilcox 	}
367*355d6bb5Sswilcox 
368*355d6bb5Sswilcox 	/*
369*355d6bb5Sswilcox 	 * We're flushing the superblock, so make sure all the
370*355d6bb5Sswilcox 	 * ancillary bits go out as well.
371*355d6bb5Sswilcox 	 */
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 
383*355d6bb5Sswilcox static void
384*355d6bb5Sswilcox rwerror(caddr_t mesg, diskaddr_t blk, int rval)
3857c478bd9Sstevel@tonic-gate {
386*355d6bb5Sswilcox 	int olderr = errno;
387*355d6bb5Sswilcox 
388*355d6bb5Sswilcox 	if (!preen)
389*355d6bb5Sswilcox 		(void) printf("\n");
3907c478bd9Sstevel@tonic-gate 
391*355d6bb5Sswilcox 	if (rval == -1)
392*355d6bb5Sswilcox 		pfatal("CANNOT %s: DISK BLOCK %lld: %s",
393*355d6bb5Sswilcox 		    mesg, blk, strerror(olderr));
394*355d6bb5Sswilcox 	else
395*355d6bb5Sswilcox 		pfatal("CANNOT %s: DISK BLOCK %lld", mesg, blk);
396*355d6bb5Sswilcox 
397*355d6bb5Sswilcox 	if (reply("CONTINUE") == 0) {
398*355d6bb5Sswilcox 		exitstat = EXERRFATAL;
3997c478bd9Sstevel@tonic-gate 		errexit("Program terminated\n");
400*355d6bb5Sswilcox 	}
4017c478bd9Sstevel@tonic-gate }
4027c478bd9Sstevel@tonic-gate 
403*355d6bb5Sswilcox void
404*355d6bb5Sswilcox ckfini(void)
4057c478bd9Sstevel@tonic-gate {
406*355d6bb5Sswilcox 	int64_t percentage;
407*355d6bb5Sswilcox 
408*355d6bb5Sswilcox 	if (fswritefd < 0)
409*355d6bb5Sswilcox 		return;
4107c478bd9Sstevel@tonic-gate 
411*355d6bb5Sswilcox 	flush(fswritefd, &sblk);
4127c478bd9Sstevel@tonic-gate 	/*
413*355d6bb5Sswilcox 	 * Were we using a backup superblock?
4147c478bd9Sstevel@tonic-gate 	 */
4157c478bd9Sstevel@tonic-gate 	if (havesb && sblk.b_bno != SBOFF / dev_bsize) {
416*355d6bb5Sswilcox 		if (preen || reply("UPDATE STANDARD SUPERBLOCK") == 1) {
417*355d6bb5Sswilcox 			sblk.b_bno = SBOFF / dev_bsize;
418*355d6bb5Sswilcox 			sbdirty();
419*355d6bb5Sswilcox 			flush(fswritefd, &sblk);
420*355d6bb5Sswilcox 		}
4217c478bd9Sstevel@tonic-gate 	}
4227c478bd9Sstevel@tonic-gate 	flush(fswritefd, &cgblk);
423*355d6bb5Sswilcox 	if (cgblk.b_un.b_buf != NULL) {
424*355d6bb5Sswilcox 		free((void *)cgblk.b_un.b_buf);
4257c478bd9Sstevel@tonic-gate 		cgblk.b_un.b_buf = NULL;
4267c478bd9Sstevel@tonic-gate 	}
427*355d6bb5Sswilcox 	unbufinit();
428*355d6bb5Sswilcox 	pbp = NULL;
429*355d6bb5Sswilcox 	pdirbp = NULL;
430*355d6bb5Sswilcox 	if (debug) {
431*355d6bb5Sswilcox 		/*
432*355d6bb5Sswilcox 		 * Note that we only count cache-related reads.
433*355d6bb5Sswilcox 		 * Anything that called fsck_bread() or getblk()
434*355d6bb5Sswilcox 		 * directly are explicitly not cached, so they're not
435*355d6bb5Sswilcox 		 * included here.
436*355d6bb5Sswilcox 		 */
437*355d6bb5Sswilcox 		if (totalreads != 0)
438*355d6bb5Sswilcox 			percentage = diskreads * 100 / totalreads;
439*355d6bb5Sswilcox 		else
440*355d6bb5Sswilcox 			percentage = 0;
441*355d6bb5Sswilcox 
442*355d6bb5Sswilcox 		(void) printf("cache missed %lld of %lld reads (%lld%%)\n",
443*355d6bb5Sswilcox 		    (longlong_t)diskreads, (longlong_t)totalreads,
444*355d6bb5Sswilcox 		    (longlong_t)percentage);
4457c478bd9Sstevel@tonic-gate 	}
446*355d6bb5Sswilcox 
4477c478bd9Sstevel@tonic-gate 	(void) close(fsreadfd);
4487c478bd9Sstevel@tonic-gate 	(void) close(fswritefd);
449*355d6bb5Sswilcox 	fsreadfd = -1;
450*355d6bb5Sswilcox 	fswritefd = -1;
4517c478bd9Sstevel@tonic-gate }
4527c478bd9Sstevel@tonic-gate 
453*355d6bb5Sswilcox int
454*355d6bb5Sswilcox fsck_bread(int fd, caddr_t buf, diskaddr_t blk, size_t size)
4557c478bd9Sstevel@tonic-gate {
456*355d6bb5Sswilcox 	caddr_t cp;
457*355d6bb5Sswilcox 	int i;
4587c478bd9Sstevel@tonic-gate 	int errs;
4597c478bd9Sstevel@tonic-gate 	offset_t offset = ldbtob(blk);
4607c478bd9Sstevel@tonic-gate 	offset_t addr;
4617c478bd9Sstevel@tonic-gate 
462*355d6bb5Sswilcox 	/*
463*355d6bb5Sswilcox 	 * In our universe, nothing exists before the superblock, so
464*355d6bb5Sswilcox 	 * just pretend it's always zeros.  This is the complement of
465*355d6bb5Sswilcox 	 * bwrite()'s ignoring write requests into that space.
466*355d6bb5Sswilcox 	 */
467*355d6bb5Sswilcox 	if (blk < SBLOCK) {
468*355d6bb5Sswilcox 		if (debug)
469*355d6bb5Sswilcox 			(void) printf(
470*355d6bb5Sswilcox 			    "WARNING: fsck_bread() passed blkno < %d (%lld)\n",
471*355d6bb5Sswilcox 			    SBLOCK, (longlong_t)blk);
472*355d6bb5Sswilcox 		(void) memset(buf, 0, (size_t)size);
473*355d6bb5Sswilcox 		return (1);
4747c478bd9Sstevel@tonic-gate 	}
475*355d6bb5Sswilcox 
4767c478bd9Sstevel@tonic-gate 	if (llseek(fd, offset, 0) < 0) {
477*355d6bb5Sswilcox 		rwerror("SEEK", blk, -1);
478*355d6bb5Sswilcox 	}
479*355d6bb5Sswilcox 
480*355d6bb5Sswilcox 	if ((i = read(fd, buf, size)) == size) {
4817c478bd9Sstevel@tonic-gate 		return (0);
482*355d6bb5Sswilcox 	}
483*355d6bb5Sswilcox 	rwerror("READ", blk, i);
4847c478bd9Sstevel@tonic-gate 	if (llseek(fd, offset, 0) < 0) {
485*355d6bb5Sswilcox 		rwerror("SEEK", blk, -1);
4867c478bd9Sstevel@tonic-gate 	}
4877c478bd9Sstevel@tonic-gate 	errs = 0;
488*355d6bb5Sswilcox 	(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);
4927c478bd9Sstevel@tonic-gate 		if (llseek(fd, addr, SEEK_CUR) < 0 ||
4937c478bd9Sstevel@tonic-gate 		    read(fd, cp, (int)secsize) < 0) {
494*355d6bb5Sswilcox 			iscorrupt = 1;
495*355d6bb5Sswilcox 			(void) printf(" %llu", blk + (u_longlong_t)i);
4967c478bd9Sstevel@tonic-gate 			errs++;
4977c478bd9Sstevel@tonic-gate 		}
4987c478bd9Sstevel@tonic-gate 	}
499*355d6bb5Sswilcox 	(void) printf("\n");
5007c478bd9Sstevel@tonic-gate 	return (errs);
5017c478bd9Sstevel@tonic-gate }
5027c478bd9Sstevel@tonic-gate 
503*355d6bb5Sswilcox void
504*355d6bb5Sswilcox bwrite(int fd, caddr_t buf, diskaddr_t blk, int64_t size)
5057c478bd9Sstevel@tonic-gate {
506*355d6bb5Sswilcox 	int i;
5077c478bd9Sstevel@tonic-gate 	int n;
508*355d6bb5Sswilcox 	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)
516*355d6bb5Sswilcox 			(void) printf(
517*355d6bb5Sswilcox 		    "WARNING: Attempt to write illegal blkno %lld on %s\n",
518*355d6bb5Sswilcox 			    (longlong_t)blk, devname);
5197c478bd9Sstevel@tonic-gate 		return;
5207c478bd9Sstevel@tonic-gate 	}
5217c478bd9Sstevel@tonic-gate 	if (llseek(fd, offset, 0) < 0) {
522*355d6bb5Sswilcox 		rwerror("SEEK", blk, -1);
523*355d6bb5Sswilcox 	}
524*355d6bb5Sswilcox 	if ((i = write(fd, buf, (int)size)) == size) {
5257c478bd9Sstevel@tonic-gate 		fsmodified = 1;
5267c478bd9Sstevel@tonic-gate 		return;
5277c478bd9Sstevel@tonic-gate 	}
528*355d6bb5Sswilcox 	rwerror("WRITE", blk, i);
5297c478bd9Sstevel@tonic-gate 	if (llseek(fd, offset, 0) < 0) {
530*355d6bb5Sswilcox 		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);
5367c478bd9Sstevel@tonic-gate 		if (llseek(fd, addr, SEEK_CUR) < 0 ||
5377c478bd9Sstevel@tonic-gate 		    (n = write(fd, cp, DEV_BSIZE)) < 0) {
538*355d6bb5Sswilcox 			iscorrupt = 1;
539*355d6bb5Sswilcox 			(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 	}
545*355d6bb5Sswilcox 	(void) printf("\n");
5467c478bd9Sstevel@tonic-gate }
5477c478bd9Sstevel@tonic-gate 
5487c478bd9Sstevel@tonic-gate /*
549*355d6bb5Sswilcox  * Allocates the specified number of contiguous fragments.
5507c478bd9Sstevel@tonic-gate  */
5517c478bd9Sstevel@tonic-gate daddr32_t
552*355d6bb5Sswilcox allocblk(int wantedfrags)
5537c478bd9Sstevel@tonic-gate {
554*355d6bb5Sswilcox 	int block, leadfrag, tailfrag;
555*355d6bb5Sswilcox 	daddr32_t selected;
556*355d6bb5Sswilcox 	size_t size;
557*355d6bb5Sswilcox 	struct bufarea *bp;
5587c478bd9Sstevel@tonic-gate 
559*355d6bb5Sswilcox 	/*
560*355d6bb5Sswilcox 	 * It's arguable whether we should just fail, or instead
561*355d6bb5Sswilcox 	 * error out here.  Since we should only ever be asked for
562*355d6bb5Sswilcox 	 * a single fragment or an entire block (i.e., sblock.fs_frag),
563*355d6bb5Sswilcox 	 * we'll fail out because anything else means somebody
564*355d6bb5Sswilcox 	 * changed code without considering all of the ramifications.
565*355d6bb5Sswilcox 	 */
566*355d6bb5Sswilcox 	if (wantedfrags <= 0 || wantedfrags > sblock.fs_frag) {
567*355d6bb5Sswilcox 		exitstat = EXERRFATAL;
568*355d6bb5Sswilcox 		errexit("allocblk() asked for %d frags.  "
569*355d6bb5Sswilcox 			"Legal range is 1 to %d",
570*355d6bb5Sswilcox 			wantedfrags, sblock.fs_frag);
571*355d6bb5Sswilcox 	}
572*355d6bb5Sswilcox 
573*355d6bb5Sswilcox 	/*
574*355d6bb5Sswilcox 	 * For each filesystem block, look at every possible starting
575*355d6bb5Sswilcox 	 * offset within the block such that we can get the number of
576*355d6bb5Sswilcox 	 * contiguous fragments that we need.  This is a drastically
577*355d6bb5Sswilcox 	 * simplified version of the kernel's mapsearch() and alloc*().
578*355d6bb5Sswilcox 	 * It's also correspondingly slower.
579*355d6bb5Sswilcox 	 */
580*355d6bb5Sswilcox 	for (block = 0; block < maxfsblock - sblock.fs_frag;
581*355d6bb5Sswilcox 	    block += sblock.fs_frag) {
582*355d6bb5Sswilcox 		for (leadfrag = 0; leadfrag <= sblock.fs_frag - wantedfrags;
583*355d6bb5Sswilcox 		    leadfrag++) {
584*355d6bb5Sswilcox 			/*
585*355d6bb5Sswilcox 			 * Is first fragment of candidate run available?
586*355d6bb5Sswilcox 			 */
587*355d6bb5Sswilcox 			if (testbmap(block + leadfrag))
5887c478bd9Sstevel@tonic-gate 				continue;
589*355d6bb5Sswilcox 			/*
590*355d6bb5Sswilcox 			 * Are the rest of them available?
591*355d6bb5Sswilcox 			 */
592*355d6bb5Sswilcox 			for (tailfrag = 1; tailfrag < wantedfrags; tailfrag++)
593*355d6bb5Sswilcox 				if (testbmap(block + leadfrag + tailfrag))
5947c478bd9Sstevel@tonic-gate 					break;
595*355d6bb5Sswilcox 			if (tailfrag < wantedfrags) {
596*355d6bb5Sswilcox 				/*
597*355d6bb5Sswilcox 				 * No, skip the known-unusable run.
598*355d6bb5Sswilcox 				 */
599*355d6bb5Sswilcox 				leadfrag += tailfrag;
6007c478bd9Sstevel@tonic-gate 				continue;
6017c478bd9Sstevel@tonic-gate 			}
602*355d6bb5Sswilcox 			/*
603*355d6bb5Sswilcox 			 * Found what we need, so claim them.
604*355d6bb5Sswilcox 			 */
605*355d6bb5Sswilcox 			for (tailfrag = 0; tailfrag < wantedfrags; tailfrag++)
606*355d6bb5Sswilcox 				setbmap(block + leadfrag + tailfrag);
607*355d6bb5Sswilcox 			n_blks += wantedfrags;
608*355d6bb5Sswilcox 			size = wantedfrags * sblock.fs_fsize;
609*355d6bb5Sswilcox 			selected = block + leadfrag;
610*355d6bb5Sswilcox 			bp = getdatablk(selected, size);
611*355d6bb5Sswilcox 			(void) memset((void *)bp->b_un.b_buf, 0, size);
612*355d6bb5Sswilcox 			dirty(bp);
613*355d6bb5Sswilcox 			brelse(bp);
614*355d6bb5Sswilcox 			if (debug)
615*355d6bb5Sswilcox 				(void) printf(
616*355d6bb5Sswilcox 		    "allocblk: selected %d (in block %d), frags %d, size %d\n",
617*355d6bb5Sswilcox 				    selected, selected % sblock.fs_bsize,
618*355d6bb5Sswilcox 				    wantedfrags, (int)size);
619*355d6bb5Sswilcox 			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  */
628*355d6bb5Sswilcox void
629*355d6bb5Sswilcox freeblk(fsck_ino_t ino, daddr32_t blkno, int frags)
6307c478bd9Sstevel@tonic-gate {
6317c478bd9Sstevel@tonic-gate 	struct inodesc idesc;
6327c478bd9Sstevel@tonic-gate 
633*355d6bb5Sswilcox 	if (debug)
634*355d6bb5Sswilcox 		(void) printf("debug: freeing %d fragments starting at %d\n",
635*355d6bb5Sswilcox 		    frags, blkno);
636*355d6bb5Sswilcox 
637*355d6bb5Sswilcox 	init_inodesc(&idesc);
638*355d6bb5Sswilcox 
639*355d6bb5Sswilcox 	idesc.id_number = ino;
6407c478bd9Sstevel@tonic-gate 	idesc.id_blkno = blkno;
6417c478bd9Sstevel@tonic-gate 	idesc.id_numfrags = frags;
642*355d6bb5Sswilcox 	idesc.id_truncto = -1;
643*355d6bb5Sswilcox 
644*355d6bb5Sswilcox 	/*
645*355d6bb5Sswilcox 	 * Nothing in the return status has any relevance to how
646*355d6bb5Sswilcox 	 * we're using pass4check(), so just ignore it.
647*355d6bb5Sswilcox 	 */
648*355d6bb5Sswilcox 	(void) pass4check(&idesc);
6497c478bd9Sstevel@tonic-gate }
6507c478bd9Sstevel@tonic-gate 
6517c478bd9Sstevel@tonic-gate /*
652*355d6bb5Sswilcox  * Fill NAMEBUF with a path starting in CURDIR for INO.  Assumes
653*355d6bb5Sswilcox  * that the given buffer is at least MAXPATHLEN + 1 characters.
6547c478bd9Sstevel@tonic-gate  */
655*355d6bb5Sswilcox void
656*355d6bb5Sswilcox getpathname(caddr_t namebuf, fsck_ino_t curdir, fsck_ino_t ino)
6577c478bd9Sstevel@tonic-gate {
6587c478bd9Sstevel@tonic-gate 	int len;
659*355d6bb5Sswilcox 	caddr_t cp;
660*355d6bb5Sswilcox 	struct dinode *dp;
6617c478bd9Sstevel@tonic-gate 	struct inodesc idesc;
6627c478bd9Sstevel@tonic-gate 	struct inoinfo *inp;
6637c478bd9Sstevel@tonic-gate 
664*355d6bb5Sswilcox 	if (debug)
665*355d6bb5Sswilcox 		(void) printf("debug: getpathname(curdir %d, ino %d)\n",
666*355d6bb5Sswilcox 		    curdir, ino);
667*355d6bb5Sswilcox 
668*355d6bb5Sswilcox 	if ((curdir == 0) || (!INO_IS_DVALID(curdir))) {
669*355d6bb5Sswilcox 		(void) strcpy(namebuf, "?");
670*355d6bb5Sswilcox 		return;
671*355d6bb5Sswilcox 	}
672*355d6bb5Sswilcox 
673*355d6bb5Sswilcox 	if ((curdir == UFSROOTINO) && (ino == UFSROOTINO)) {
674*355d6bb5Sswilcox 		(void) strcpy(namebuf, "/");
6757c478bd9Sstevel@tonic-gate 		return;
6767c478bd9Sstevel@tonic-gate 	}
677*355d6bb5Sswilcox 
678*355d6bb5Sswilcox 	init_inodesc(&idesc);
6797c478bd9Sstevel@tonic-gate 	idesc.id_type = DATA;
6807c478bd9Sstevel@tonic-gate 	cp = &namebuf[MAXPATHLEN - 1];
6817c478bd9Sstevel@tonic-gate 	*cp = '\0';
682*355d6bb5Sswilcox 
683*355d6bb5Sswilcox 	/*
684*355d6bb5Sswilcox 	 * In the case of extended attributes, our
685*355d6bb5Sswilcox 	 * parent won't necessarily be a directory, so just
686*355d6bb5Sswilcox 	 * return what we've found with a prefix indicating
687*355d6bb5Sswilcox 	 * that it's an XATTR.  Presumably our caller will
688*355d6bb5Sswilcox 	 * know what's going on and do something useful, like
689*355d6bb5Sswilcox 	 * work out the path of the parent and then combine
690*355d6bb5Sswilcox 	 * the two names.
691*355d6bb5Sswilcox 	 *
692*355d6bb5Sswilcox 	 * Can't use strcpy(), etc, because we've probably
693*355d6bb5Sswilcox 	 * already got some name information in the buffer and
694*355d6bb5Sswilcox 	 * the usual trailing \0 would lose it.
695*355d6bb5Sswilcox 	 */
696*355d6bb5Sswilcox 	dp = ginode(curdir);
697*355d6bb5Sswilcox 	if ((dp->di_mode & IFMT) == IFATTRDIR) {
698*355d6bb5Sswilcox 		idesc.id_number = curdir;
699*355d6bb5Sswilcox 		idesc.id_parent = ino;
700*355d6bb5Sswilcox 		idesc.id_func = findname;
701*355d6bb5Sswilcox 		idesc.id_name = namebuf;
702*355d6bb5Sswilcox 		idesc.id_fix = NOFIX;
703*355d6bb5Sswilcox 		if ((ckinode(dp, &idesc, CKI_TRAVERSE) & FOUND) == 0) {
704*355d6bb5Sswilcox 			*cp-- = '?';
705*355d6bb5Sswilcox 		}
706*355d6bb5Sswilcox 
707*355d6bb5Sswilcox 		len = sizeof (XATTR_DIR_NAME) - 1;
708*355d6bb5Sswilcox 		cp -= len;
709*355d6bb5Sswilcox 		(void) memmove(cp, XATTR_DIR_NAME, len);
710*355d6bb5Sswilcox 		goto attrname;
711*355d6bb5Sswilcox 	}
712*355d6bb5Sswilcox 
713*355d6bb5Sswilcox 	/*
714*355d6bb5Sswilcox 	 * If curdir == ino, need to get a handle on .. so we
715*355d6bb5Sswilcox 	 * can search it for ino's name.  Otherwise, just search
716*355d6bb5Sswilcox 	 * the given directory for ino.  Repeat until out of space
717*355d6bb5Sswilcox 	 * or a full path has been built.
718*355d6bb5Sswilcox 	 */
7197c478bd9Sstevel@tonic-gate 	if (curdir != ino) {
7207c478bd9Sstevel@tonic-gate 		idesc.id_parent = curdir;
7217c478bd9Sstevel@tonic-gate 		goto namelookup;
7227c478bd9Sstevel@tonic-gate 	}
723*355d6bb5Sswilcox 	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;
728*355d6bb5Sswilcox 		if ((ckinode(ginode(ino), &idesc, CKI_TRAVERSE) & FOUND) == 0) {
7297c478bd9Sstevel@tonic-gate 			inp = getinoinfo(ino);
730*355d6bb5Sswilcox 			if ((inp == NULL) || (inp->i_parent == 0)) {
7317c478bd9Sstevel@tonic-gate 				break;
732*355d6bb5Sswilcox 			}
7337c478bd9Sstevel@tonic-gate 			idesc.id_parent = inp->i_parent;
7347c478bd9Sstevel@tonic-gate 		}
735*355d6bb5Sswilcox 
736*355d6bb5Sswilcox 		/*
737*355d6bb5Sswilcox 		 * To get this far, id_parent must have the inode
738*355d6bb5Sswilcox 		 * number for `..' in it.  By definition, that's got
739*355d6bb5Sswilcox 		 * to be a directory, so search it for the inode of
740*355d6bb5Sswilcox 		 * interest.
741*355d6bb5Sswilcox 		 */
742*355d6bb5Sswilcox 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;
748*355d6bb5Sswilcox 		if ((ckinode(ginode(idesc.id_number),
749*355d6bb5Sswilcox 		    &idesc, CKI_TRAVERSE) & FOUND) == 0) {
7507c478bd9Sstevel@tonic-gate 			break;
751*355d6bb5Sswilcox 		}
752*355d6bb5Sswilcox 		/*
753*355d6bb5Sswilcox 		 * Prepend to what we've accumulated so far.  If
754*355d6bb5Sswilcox 		 * there's not enough room for even one more path element
755*355d6bb5Sswilcox 		 * (of the worst-case length), then bail out.
756*355d6bb5Sswilcox 		 */
7577c478bd9Sstevel@tonic-gate 		len = strlen(namebuf);
7587c478bd9Sstevel@tonic-gate 		cp -= len;
7597c478bd9Sstevel@tonic-gate 		if (cp < &namebuf[MAXNAMLEN])
7607c478bd9Sstevel@tonic-gate 			break;
761*355d6bb5Sswilcox 		(void) memmove(cp, namebuf, len);
7627c478bd9Sstevel@tonic-gate 		*--cp = '/';
763*355d6bb5Sswilcox 
764*355d6bb5Sswilcox 		/*
765*355d6bb5Sswilcox 		 * Corner case for a looped-to-itself directory.
766*355d6bb5Sswilcox 		 */
767*355d6bb5Sswilcox 		if (ino == idesc.id_number)
768*355d6bb5Sswilcox 			break;
769*355d6bb5Sswilcox 
770*355d6bb5Sswilcox 		/*
771*355d6bb5Sswilcox 		 * Climb one level of the hierarchy.  In other words,
772*355d6bb5Sswilcox 		 * the current .. becomes the inode to search for and
773*355d6bb5Sswilcox 		 * its parent becomes the directory to search in.
774*355d6bb5Sswilcox 		 */
7757c478bd9Sstevel@tonic-gate 		ino = idesc.id_number;
7767c478bd9Sstevel@tonic-gate 	}
777*355d6bb5Sswilcox 
778*355d6bb5Sswilcox 	/*
779*355d6bb5Sswilcox 	 * If we hit a discontinuity in the hierarchy, indicate it by
780*355d6bb5Sswilcox 	 * prefixing the path so far with `?'.  Otherwise, the first
781*355d6bb5Sswilcox 	 * character will be `/' as a side-effect of the *--cp above.
782*355d6bb5Sswilcox 	 *
783*355d6bb5Sswilcox 	 * The special case is to handle the situation where we're
784*355d6bb5Sswilcox 	 * trying to look something up in UFSROOTINO, but didn't find
785*355d6bb5Sswilcox 	 * it.
786*355d6bb5Sswilcox 	 */
787*355d6bb5Sswilcox 	if (ino != UFSROOTINO || cp == &namebuf[MAXPATHLEN - 1]) {
788*355d6bb5Sswilcox 		if (cp > namebuf)
789*355d6bb5Sswilcox 			cp--;
790*355d6bb5Sswilcox 		*cp = '?';
7917c478bd9Sstevel@tonic-gate 	}
792*355d6bb5Sswilcox 
793*355d6bb5Sswilcox 	/*
794*355d6bb5Sswilcox 	 * The invariants being used for buffer integrity are:
795*355d6bb5Sswilcox 	 * - namebuf[] is terminated with \0 before anything else
796*355d6bb5Sswilcox 	 * - cp is always <= the last element of namebuf[]
797*355d6bb5Sswilcox 	 * - the new path element is always stored at the
798*355d6bb5Sswilcox 	 *   beginning of namebuf[], and is no more than MAXNAMLEN-1
799*355d6bb5Sswilcox 	 *   characters
800*355d6bb5Sswilcox 	 * - cp is is decremented by the number of characters in
801*355d6bb5Sswilcox 	 *   the new path element
802*355d6bb5Sswilcox 	 * - if, after the above accounting for the new element's
803*355d6bb5Sswilcox 	 *   size, there is no longer enough room at the beginning of
804*355d6bb5Sswilcox 	 *   namebuf[] for a full-sized path element and a slash,
805*355d6bb5Sswilcox 	 *   terminate the loop.  cp is in the range
806*355d6bb5Sswilcox 	 *   &namebuf[0]..&namebuf[MAXNAMLEN - 1]
807*355d6bb5Sswilcox 	 */
808*355d6bb5Sswilcox attrname:
809*355d6bb5Sswilcox 	/* LINTED per the above discussion */
810*355d6bb5Sswilcox 	(void) memmove(namebuf, cp, &namebuf[MAXPATHLEN] - cp);
8117c478bd9Sstevel@tonic-gate }
8127c478bd9Sstevel@tonic-gate 
813*355d6bb5Sswilcox /* ARGSUSED */
8147c478bd9Sstevel@tonic-gate void
815*355d6bb5Sswilcox catch(int dummy)
8167c478bd9Sstevel@tonic-gate {
8177c478bd9Sstevel@tonic-gate 	ckfini();
818*355d6bb5Sswilcox 	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  */
826*355d6bb5Sswilcox /* ARGSUSED */
8277c478bd9Sstevel@tonic-gate void
828*355d6bb5Sswilcox catchquit(int dummy)
8297c478bd9Sstevel@tonic-gate {
830*355d6bb5Sswilcox 	(void) printf("returning to single-user after filesystem check\n");
831*355d6bb5Sswilcox 	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  */
839*355d6bb5Sswilcox NOTE(PRINTFLIKE(2))
840*355d6bb5Sswilcox int
841*355d6bb5Sswilcox dofix(struct inodesc *idesc, caddr_t msg, ...)
8427c478bd9Sstevel@tonic-gate {
843*355d6bb5Sswilcox 	int rval = 0;
844*355d6bb5Sswilcox 	va_list ap;
845*355d6bb5Sswilcox 
846*355d6bb5Sswilcox 	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)
852*355d6bb5Sswilcox 			vdirerror(idesc->id_number, msg, ap);
8537c478bd9Sstevel@tonic-gate 		else
854*355d6bb5Sswilcox 			vpwarn(msg, ap);
8557c478bd9Sstevel@tonic-gate 		if (preen) {
8567c478bd9Sstevel@tonic-gate 			idesc->id_fix = FIX;
857*355d6bb5Sswilcox 			rval = ALTERED;
858*355d6bb5Sswilcox 			break;
8597c478bd9Sstevel@tonic-gate 		}
8607c478bd9Sstevel@tonic-gate 		if (reply("SALVAGE") == 0) {
8617c478bd9Sstevel@tonic-gate 			idesc->id_fix = NOFIX;
862*355d6bb5Sswilcox 			break;
8637c478bd9Sstevel@tonic-gate 		}
8647c478bd9Sstevel@tonic-gate 		idesc->id_fix = FIX;
865*355d6bb5Sswilcox 		rval = ALTERED;
866*355d6bb5Sswilcox 		break;
8677c478bd9Sstevel@tonic-gate 
8687c478bd9Sstevel@tonic-gate 	case FIX:
869*355d6bb5Sswilcox 		rval = ALTERED;
870*355d6bb5Sswilcox 		break;
8717c478bd9Sstevel@tonic-gate 
8727c478bd9Sstevel@tonic-gate 	case NOFIX:
873*355d6bb5Sswilcox 		break;
8747c478bd9Sstevel@tonic-gate 
8757c478bd9Sstevel@tonic-gate 	default:
876*355d6bb5Sswilcox 		errexit("UNKNOWN INODESC FIX MODE %d\n", (int)idesc->id_fix);
8777c478bd9Sstevel@tonic-gate 	}
878*355d6bb5Sswilcox 
879*355d6bb5Sswilcox 	va_end(ap);
880*355d6bb5Sswilcox 	return (rval);
8817c478bd9Sstevel@tonic-gate }
8827c478bd9Sstevel@tonic-gate 
883*355d6bb5Sswilcox NOTE(PRINTFLIKE(1))
884*355d6bb5Sswilcox void
885*355d6bb5Sswilcox errexit(caddr_t fmt, ...)
8867c478bd9Sstevel@tonic-gate {
887*355d6bb5Sswilcox 	va_list ap;
8887c478bd9Sstevel@tonic-gate 
889*355d6bb5Sswilcox 	va_start(ap, fmt);
890*355d6bb5Sswilcox 	verrexit(fmt, ap);
891*355d6bb5Sswilcox 	/* NOTREACHED */
892*355d6bb5Sswilcox }
893*355d6bb5Sswilcox 
894*355d6bb5Sswilcox NOTE(PRINTFLIKE(1))
895*355d6bb5Sswilcox static void
896*355d6bb5Sswilcox verrexit(caddr_t fmt, va_list ap)
897*355d6bb5Sswilcox {
898*355d6bb5Sswilcox 	static int recursing = 0;
899*355d6bb5Sswilcox 
900*355d6bb5Sswilcox 	if (!recursing) {
901*355d6bb5Sswilcox 		recursing = 1;
902*355d6bb5Sswilcox 		if (errorlocked || iscorrupt) {
903*355d6bb5Sswilcox 			if (havesb) {
904*355d6bb5Sswilcox 				sblock.fs_clean = FSBAD;
905*355d6bb5Sswilcox 				sblock.fs_state = FSOKAY - (long)sblock.fs_time;
906*355d6bb5Sswilcox 				sblock.fs_state = -sblock.fs_state;
907*355d6bb5Sswilcox 				sbdirty();
908*355d6bb5Sswilcox 				write_altsb(fswritefd);
909*355d6bb5Sswilcox 				flush(fswritefd, &sblk);
910*355d6bb5Sswilcox 			}
9117c478bd9Sstevel@tonic-gate 		}
912*355d6bb5Sswilcox 		ckfini();
913*355d6bb5Sswilcox 		recursing = 0;
9147c478bd9Sstevel@tonic-gate 	}
915*355d6bb5Sswilcox 	(void) vprintf(fmt, ap);
916*355d6bb5Sswilcox 	if (fmt[strlen(fmt) - 1] != '\n')
917*355d6bb5Sswilcox 		(void) putchar('\n');
918*355d6bb5Sswilcox 	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  */
925*355d6bb5Sswilcox NOTE(PRINTFLIKE(1))
926*355d6bb5Sswilcox void
927*355d6bb5Sswilcox pfatal(caddr_t fmt, ...)
928*355d6bb5Sswilcox {
929*355d6bb5Sswilcox 	va_list ap;
930*355d6bb5Sswilcox 
931*355d6bb5Sswilcox 	va_start(ap, fmt);
932*355d6bb5Sswilcox 	vpfatal(fmt, ap);
933*355d6bb5Sswilcox 	va_end(ap);
934*355d6bb5Sswilcox }
935*355d6bb5Sswilcox 
936*355d6bb5Sswilcox NOTE(PRINTFLIKE(1))
937*355d6bb5Sswilcox static void
938*355d6bb5Sswilcox vpfatal(caddr_t fmt, va_list ap)
9397c478bd9Sstevel@tonic-gate {
9407c478bd9Sstevel@tonic-gate 	if (preen) {
941*355d6bb5Sswilcox 		if (*fmt != '\0') {
942*355d6bb5Sswilcox 			(void) printf("%s: ", devname);
943*355d6bb5Sswilcox 			(void) vprintf(fmt, ap);
944*355d6bb5Sswilcox 			(void) printf("\n");
945*355d6bb5Sswilcox 		}
946*355d6bb5Sswilcox 		(void) printf(
947*355d6bb5Sswilcox 		    "%s: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY.\n",
948*355d6bb5Sswilcox 		    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 		}
955*355d6bb5Sswilcox 		/*
956*355d6bb5Sswilcox 		 * We're exiting, it doesn't really matter that our
957*355d6bb5Sswilcox 		 * caller doesn't get to call va_end().
958*355d6bb5Sswilcox 		 */
959*355d6bb5Sswilcox 		if (exitstat == 0)
960*355d6bb5Sswilcox 			exitstat = EXFNDERRS;
961*355d6bb5Sswilcox 		exit(exitstat);
962*355d6bb5Sswilcox 	}
963*355d6bb5Sswilcox 	if (*fmt != '\0') {
964*355d6bb5Sswilcox 		(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  */
972*355d6bb5Sswilcox NOTE(PRINTFLIKE(1))
973*355d6bb5Sswilcox void
974*355d6bb5Sswilcox pwarn(caddr_t fmt, ...)
9757c478bd9Sstevel@tonic-gate {
976*355d6bb5Sswilcox 	va_list ap;
9777c478bd9Sstevel@tonic-gate 
978*355d6bb5Sswilcox 	va_start(ap, fmt);
979*355d6bb5Sswilcox 	vpwarn(fmt, ap);
980*355d6bb5Sswilcox 	va_end(ap);
981*355d6bb5Sswilcox }
982*355d6bb5Sswilcox 
983*355d6bb5Sswilcox NOTE(PRINTFLIKE(1))
984*355d6bb5Sswilcox static void
985*355d6bb5Sswilcox vpwarn(caddr_t fmt, va_list ap)
986*355d6bb5Sswilcox {
987*355d6bb5Sswilcox 	if (*fmt != '\0') {
988*355d6bb5Sswilcox 		if (preen)
989*355d6bb5Sswilcox 			(void) printf("%s: ", devname);
990*355d6bb5Sswilcox 		(void) vprintf(fmt, ap);
991*355d6bb5Sswilcox 	}
9927c478bd9Sstevel@tonic-gate }
9937c478bd9Sstevel@tonic-gate 
9947c478bd9Sstevel@tonic-gate /*
995*355d6bb5Sswilcox  * Like sprintf(), except the buffer is dynamically allocated
996*355d6bb5Sswilcox  * and returned, instead of being passed in.  A pointer to the
997*355d6bb5Sswilcox  * buffer is stored in *RET, and FMT is the usual format string.
998*355d6bb5Sswilcox  * The number of characters in *RET (excluding the trailing \0,
999*355d6bb5Sswilcox  * to be consistent with the other *printf() routines) is returned.
1000*355d6bb5Sswilcox  *
1001*355d6bb5Sswilcox  * Solaris doesn't have asprintf(3C) yet, unfortunately.
10027c478bd9Sstevel@tonic-gate  */
1003*355d6bb5Sswilcox NOTE(PRINTFLIKE(2))
1004*355d6bb5Sswilcox int
1005*355d6bb5Sswilcox fsck_asprintf(caddr_t *ret, caddr_t fmt, ...)
10067c478bd9Sstevel@tonic-gate {
1007*355d6bb5Sswilcox 	int len;
1008*355d6bb5Sswilcox 	caddr_t buffer;
1009*355d6bb5Sswilcox 	va_list ap;
10107c478bd9Sstevel@tonic-gate 
1011*355d6bb5Sswilcox 	va_start(ap, fmt);
1012*355d6bb5Sswilcox 	len = vsnprintf(NULL, 0, fmt, ap);
1013*355d6bb5Sswilcox 	va_end(ap);
1014*355d6bb5Sswilcox 
1015*355d6bb5Sswilcox 	buffer = malloc((len + 1) * sizeof (char));
1016*355d6bb5Sswilcox 	if (buffer == NULL) {
1017*355d6bb5Sswilcox 		errexit("Out of memory in asprintf\n");
1018*355d6bb5Sswilcox 		/* NOTREACHED */
1019*355d6bb5Sswilcox 	}
1020*355d6bb5Sswilcox 
1021*355d6bb5Sswilcox 	va_start(ap, fmt);
1022*355d6bb5Sswilcox 	(void) vsnprintf(buffer, len + 1, fmt, ap);
1023*355d6bb5Sswilcox 	va_end(ap);
1024*355d6bb5Sswilcox 
1025*355d6bb5Sswilcox 	*ret = buffer;
1026*355d6bb5Sswilcox 	return (len);
10277c478bd9Sstevel@tonic-gate }
1028*355d6bb5Sswilcox 
1029*355d6bb5Sswilcox /*
1030*355d6bb5Sswilcox  * So we can take advantage of kernel routines in ufs_subr.c.
1031*355d6bb5Sswilcox  */
1032*355d6bb5Sswilcox /* PRINTFLIKE2 */
10337c478bd9Sstevel@tonic-gate void
1034*355d6bb5Sswilcox cmn_err(int level, caddr_t fmt, ...)
10357c478bd9Sstevel@tonic-gate {
1036*355d6bb5Sswilcox 	va_list ap;
10377c478bd9Sstevel@tonic-gate 
1038*355d6bb5Sswilcox 	va_start(ap, fmt);
10397c478bd9Sstevel@tonic-gate 	if (level == CE_PANIC) {
1040*355d6bb5Sswilcox 		(void) printf("INTERNAL INCONSISTENCY:");
1041*355d6bb5Sswilcox 		verrexit(fmt, ap);
1042*355d6bb5Sswilcox 	} else {
1043*355d6bb5Sswilcox 		(void) vprintf(fmt, ap);
10447c478bd9Sstevel@tonic-gate 	}
1045*355d6bb5Sswilcox 	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.
1050*355d6bb5Sswilcox  * Updates devstr with the device name if devstr is not NULL
1051*355d6bb5Sswilcox  * and str_size is positive.
10527c478bd9Sstevel@tonic-gate  */
1053*355d6bb5Sswilcox int
1054*355d6bb5Sswilcox mounted(caddr_t name, caddr_t devstr, size_t str_size)
10557c478bd9Sstevel@tonic-gate {
1056*355d6bb5Sswilcox 	int found;
1057*355d6bb5Sswilcox 	struct mnttab *mntent;
10587c478bd9Sstevel@tonic-gate 
1059*355d6bb5Sswilcox 	mntent = search_mnttab(NULL, unrawname(name), devstr, str_size);
1060*355d6bb5Sswilcox 	if (mntent == NULL)
1061*355d6bb5Sswilcox 		return (M_NOMNT);
1062*355d6bb5Sswilcox 
1063*355d6bb5Sswilcox 	/*
1064*355d6bb5Sswilcox 	 * It's mounted.  With or without write access?
1065*355d6bb5Sswilcox 	 */
1066*355d6bb5Sswilcox 	if (hasmntopt(mntent, MNTOPT_RO) != 0)
1067*355d6bb5Sswilcox 		found = M_RO;	/* mounted as RO */
1068*355d6bb5Sswilcox 	else
1069*355d6bb5Sswilcox 		found = M_RW; 	/* mounted as R/W */
1070*355d6bb5Sswilcox 
1071*355d6bb5Sswilcox 	if (mount_point == NULL) {
1072*355d6bb5Sswilcox 		mount_point = strdup(mntent->mnt_mountp);
1073*355d6bb5Sswilcox 		if (mount_point == NULL) {
1074*355d6bb5Sswilcox 			errexit("fsck: memory allocation failure: %s",
1075*355d6bb5Sswilcox 				strerror(errno));
1076*355d6bb5Sswilcox 			/* NOTREACHED */
10777c478bd9Sstevel@tonic-gate 		}
1078*355d6bb5Sswilcox 
1079*355d6bb5Sswilcox 		if (devstr != NULL && str_size > 0)
1080*355d6bb5Sswilcox 			(void) strlcpy(devstr, mntent->mnt_special, str_size);
10817c478bd9Sstevel@tonic-gate 	}
1082*355d6bb5Sswilcox 
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  */
1090*355d6bb5Sswilcox int
1091*355d6bb5Sswilcox writable(caddr_t name)
10927c478bd9Sstevel@tonic-gate {
10937c478bd9Sstevel@tonic-gate 	int rw = 1;
1094*355d6bb5Sswilcox 	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) {
1099*355d6bb5Sswilcox 		(void) printf("can't open %s\n", VFSTAB);
11007c478bd9Sstevel@tonic-gate 		return (1);
11017c478bd9Sstevel@tonic-gate 	}
1102*355d6bb5Sswilcox 	(void) memset((void *)&vfskey, 0, sizeof (vfskey));
1103*355d6bb5Sswilcox 	vfsnull(&vfskey);
1104*355d6bb5Sswilcox 	vfskey.vfs_special = unrawname(name);
1105*355d6bb5Sswilcox 	vfskey.vfs_fstype = MNTTYPE_UFS;
1106*355d6bb5Sswilcox 	if ((getvfsany(vfstab, &vfsbuf, &vfskey) == 0) &&
11077c478bd9Sstevel@tonic-gate 	    (hasvfsopt(&vfsbuf, MNTOPT_RO))) {
11087c478bd9Sstevel@tonic-gate 		rw = 0;
11097c478bd9Sstevel@tonic-gate 	}
1110*355d6bb5Sswilcox 	(void) fclose(vfstab);
11117c478bd9Sstevel@tonic-gate 	return (rw);
11127c478bd9Sstevel@tonic-gate }
11137c478bd9Sstevel@tonic-gate 
11147c478bd9Sstevel@tonic-gate /*
11157c478bd9Sstevel@tonic-gate  * debugclean
11167c478bd9Sstevel@tonic-gate  */
1117*355d6bb5Sswilcox static void
1118*355d6bb5Sswilcox debugclean(void)
11197c478bd9Sstevel@tonic-gate {
1120*355d6bb5Sswilcox 	if (!debug)
11217c478bd9Sstevel@tonic-gate 		return;
11227c478bd9Sstevel@tonic-gate 
11237c478bd9Sstevel@tonic-gate 	if ((iscorrupt == 0) && (isdirty == 0))
11247c478bd9Sstevel@tonic-gate 		return;
11257c478bd9Sstevel@tonic-gate 
1126*355d6bb5Sswilcox 	if ((sblock.fs_clean == FSSTABLE) || (sblock.fs_clean == FSCLEAN) ||
1127*355d6bb5Sswilcox 	    (sblock.fs_clean == FSLOG && islog && islogok) ||
1128*355d6bb5Sswilcox 	    ((FSOKAY == (sblock.fs_state + sblock.fs_time)) && !errorlocked))
11297c478bd9Sstevel@tonic-gate 		return;
11307c478bd9Sstevel@tonic-gate 
1131*355d6bb5Sswilcox 	(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" :
1134*355d6bb5Sswilcox 	    sblock.fs_clean == FSFIX ? "being fixed" : "clean",
1135*355d6bb5Sswilcox 	    devname);
11367c478bd9Sstevel@tonic-gate }
11377c478bd9Sstevel@tonic-gate 
11387c478bd9Sstevel@tonic-gate /*
11397c478bd9Sstevel@tonic-gate  * updateclean
11407c478bd9Sstevel@tonic-gate  *	Carefully and transparently update the clean flag.
1141*355d6bb5Sswilcox  *
1142*355d6bb5Sswilcox  * `iscorrupt' has to be in its final state before this is called.
11437c478bd9Sstevel@tonic-gate  */
1144*355d6bb5Sswilcox int
1145*355d6bb5Sswilcox updateclean(void)
1146*355d6bb5Sswilcox {
1147*355d6bb5Sswilcox 	int freedlog = 0;
1148*355d6bb5Sswilcox 	struct bufarea cleanbuf;
1149*355d6bb5Sswilcox 	size_t size;
1150*355d6bb5Sswilcox 	ssize_t io_res;
1151*355d6bb5Sswilcox 	diskaddr_t bno;
1152*355d6bb5Sswilcox 	char fsclean;
1153*355d6bb5Sswilcox 	int fsreclaim;
1154*355d6bb5Sswilcox 	char fsflags;
1155*355d6bb5Sswilcox 	int flags_ok;
1156*355d6bb5Sswilcox 	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;
1172*355d6bb5Sswilcox 	if (FSOKAY != (sblock.fs_state + sblock.fs_time) && !errorlocked) {
11737c478bd9Sstevel@tonic-gate 		fsclean = FSACTIVE;
1174*355d6bb5Sswilcox 	}
1175*355d6bb5Sswilcox 	/*
1176*355d6bb5Sswilcox 	 * If ufs log is not okay, note that we need to clear it.
1177*355d6bb5Sswilcox 	 */
1178*355d6bb5Sswilcox 	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:
1198*355d6bb5Sswilcox 		if (iscorrupt) {
11997c478bd9Sstevel@tonic-gate 			fsclean = FSACTIVE;
1200*355d6bb5Sswilcox 		} else {
12017c478bd9Sstevel@tonic-gate 			fsreclaim = 0;
1202*355d6bb5Sswilcox 		}
12037c478bd9Sstevel@tonic-gate 		break;
12047c478bd9Sstevel@tonic-gate 
12057c478bd9Sstevel@tonic-gate 	case FSLOG:
1206*355d6bb5Sswilcox 		if (iscorrupt) {
12077c478bd9Sstevel@tonic-gate 			fsclean = FSACTIVE;
1208*355d6bb5Sswilcox 		} else if (!islog || fslogbno == 0) {
12097c478bd9Sstevel@tonic-gate 			fsclean = FSSTABLE;
12107c478bd9Sstevel@tonic-gate 			fsreclaim = 0;
1211*355d6bb5Sswilcox 		} else if (fflag) {
1212*355d6bb5Sswilcox 			fsreclaim = 0;
1213*355d6bb5Sswilcox 		}
12147c478bd9Sstevel@tonic-gate 		break;
12157c478bd9Sstevel@tonic-gate 
12167c478bd9Sstevel@tonic-gate 	case FSFIX:
12177c478bd9Sstevel@tonic-gate 		fsclean = FSBAD;
12187c478bd9Sstevel@tonic-gate 		if (errorlocked && !iscorrupt) {
1219*355d6bb5Sswilcox 			fsclean = islog ? FSLOG : FSCLEAN;
12207c478bd9Sstevel@tonic-gate 		}
12217c478bd9Sstevel@tonic-gate 		break;
12227c478bd9Sstevel@tonic-gate 
12237c478bd9Sstevel@tonic-gate 	default:
1224*355d6bb5Sswilcox 		if (iscorrupt) {
12257c478bd9Sstevel@tonic-gate 			fsclean = FSACTIVE;
1226*355d6bb5Sswilcox 		} 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 	/*
1238*355d6bb5Sswilcox 	 * If the only flag difference is that the superblock thinks
1239*355d6bb5Sswilcox 	 * there are largefiles, but we didn't find any, then ignore
1240*355d6bb5Sswilcox 	 * the discrepancy.  The kernel never clears the flag, it just
1241*355d6bb5Sswilcox 	 * sets it whenever a largefile is created.  Since it is harmless
1242*355d6bb5Sswilcox 	 * to have the flag set when it's not actually true, that by
1243*355d6bb5Sswilcox 	 * itself is not grounds for declaring the superblock to be
1244*355d6bb5Sswilcox 	 * in the wrong state.
1245*355d6bb5Sswilcox 	 *
1246*355d6bb5Sswilcox 	 * This could, in theory, prevent a filesystem from being
1247*355d6bb5Sswilcox 	 * mounted, if the existing superblock claims such files are
1248*355d6bb5Sswilcox 	 * out there and the user uses the nolargefiles option.  So,
1249*355d6bb5Sswilcox 	 * if we were forced to scan the filesystem, go ahead and
1250*355d6bb5Sswilcox 	 * take FSLARGEFILES into account as well.
12517c478bd9Sstevel@tonic-gate 	 */
1252*355d6bb5Sswilcox 	if (fflag)
1253*355d6bb5Sswilcox 		flags_ok = 0;
1254*355d6bb5Sswilcox 	else
1255*355d6bb5Sswilcox 		flags_ok = (sblock.fs_flags & ~FSLARGEFILES) == fsflags;
1256*355d6bb5Sswilcox 
12577c478bd9Sstevel@tonic-gate 	if (debug)
1258*355d6bb5Sswilcox 		(void) printf(
1259*355d6bb5Sswilcox 		    "** largefile count=%d, fs.fs_flags=%x, flags_ok %d\n",
1260*355d6bb5Sswilcox 		    largefile_count, sblock.fs_flags, flags_ok);
12617c478bd9Sstevel@tonic-gate 
1262*355d6bb5Sswilcox 	/*
1263*355d6bb5Sswilcox 	 * If fs is unchanged, do nothing.
1264*355d6bb5Sswilcox 	 */
1265*355d6bb5Sswilcox 	if ((!isdirty) && (flags_ok) &&
12667c478bd9Sstevel@tonic-gate 	    (fslogbno == sblock.fs_logbno) &&
1267*355d6bb5Sswilcox 	    (sblock.fs_clean == fsclean) &&
1268*355d6bb5Sswilcox 	    (sblock.fs_reclaim == fsreclaim) &&
12697c478bd9Sstevel@tonic-gate 	    (FSOKAY == (sblock.fs_state + sblock.fs_time))) {
12707c478bd9Sstevel@tonic-gate 		if (errorlocked) {
12717c478bd9Sstevel@tonic-gate 			if (!do_errorlock(LOCKFS_ULOCK))
12727c478bd9Sstevel@tonic-gate 				pwarn(
12737c478bd9Sstevel@tonic-gate 		    "updateclean(unchanged): unlock(LOCKFS_ULOCK) failed\n");
12747c478bd9Sstevel@tonic-gate 		}
1275*355d6bb5Sswilcox 		return (freedlog);
12767c478bd9Sstevel@tonic-gate 	}
12777c478bd9Sstevel@tonic-gate 
12787c478bd9Sstevel@tonic-gate 	/*
12797c478bd9Sstevel@tonic-gate 	 * if user allows, update superblock state
12807c478bd9Sstevel@tonic-gate 	 */
1281*355d6bb5Sswilcox 	if (debug) {
1282*355d6bb5Sswilcox 		(void) printf(
1283*355d6bb5Sswilcox 	    "superblock: flags 0x%x logbno %d clean %d reclaim %d state 0x%x\n",
1284*355d6bb5Sswilcox 		    sblock.fs_flags, sblock.fs_logbno,
1285*355d6bb5Sswilcox 		    sblock.fs_clean, sblock.fs_reclaim,
1286*355d6bb5Sswilcox 		    sblock.fs_state + sblock.fs_time);
1287*355d6bb5Sswilcox 		(void) printf(
1288*355d6bb5Sswilcox 	    "calculated: flags 0x%x logbno %d clean %d reclaim %d state 0x%x\n",
1289*355d6bb5Sswilcox 		    fsflags, fslogbno, fsclean, fsreclaim, FSOKAY);
1290*355d6bb5Sswilcox 	}
1291*355d6bb5Sswilcox 	if (!isdirty && !preen && !rerun &&
12927c478bd9Sstevel@tonic-gate 	    (reply("FILE SYSTEM STATE IN SUPERBLOCK IS WRONG; FIX") == 0))
1293*355d6bb5Sswilcox 		return (freedlog);
12947c478bd9Sstevel@tonic-gate 
12957c478bd9Sstevel@tonic-gate 	(void) time(&t);
12967c478bd9Sstevel@tonic-gate 	sblock.fs_time = (time32_t)t;
12977c478bd9Sstevel@tonic-gate 	if (debug)
12987c478bd9Sstevel@tonic-gate 		printclean();
1299*355d6bb5Sswilcox 
1300*355d6bb5Sswilcox 	if (sblock.fs_logbno != fslogbno) {
1301*355d6bb5Sswilcox 		examinelog(sblock.fs_logbno, &freelogblk);
1302*355d6bb5Sswilcox 		freedlog++;
1303*355d6bb5Sswilcox 	}
1304*355d6bb5Sswilcox 
13057c478bd9Sstevel@tonic-gate 	sblock.fs_logbno = fslogbno;
13067c478bd9Sstevel@tonic-gate 	sblock.fs_clean = fsclean;
13077c478bd9Sstevel@tonic-gate 	sblock.fs_state = FSOKAY - (long)sblock.fs_time;
13087c478bd9Sstevel@tonic-gate 	sblock.fs_reclaim = fsreclaim;
13097c478bd9Sstevel@tonic-gate 	sblock.fs_flags = fsflags;
13107c478bd9Sstevel@tonic-gate 
13117c478bd9Sstevel@tonic-gate 	/*
13127c478bd9Sstevel@tonic-gate 	 * if superblock can't be written, return
13137c478bd9Sstevel@tonic-gate 	 */
13147c478bd9Sstevel@tonic-gate 	if (fswritefd < 0)
1315*355d6bb5Sswilcox 		return (freedlog);
13167c478bd9Sstevel@tonic-gate 
13177c478bd9Sstevel@tonic-gate 	/*
1318*355d6bb5Sswilcox 	 * Read private copy of superblock, update clean flag, and write it.
13197c478bd9Sstevel@tonic-gate 	 */
13207c478bd9Sstevel@tonic-gate 	bno  = sblk.b_bno;
13217c478bd9Sstevel@tonic-gate 	size = sblk.b_size;
13227c478bd9Sstevel@tonic-gate 
13237c478bd9Sstevel@tonic-gate 	sblkoff = ldbtob(bno);
13247c478bd9Sstevel@tonic-gate 
13257c478bd9Sstevel@tonic-gate 	if ((cleanbuf.b_un.b_buf = malloc(size)) == NULL)
13267c478bd9Sstevel@tonic-gate 		errexit("out of memory");
1327*355d6bb5Sswilcox 	if (llseek(fsreadfd, sblkoff, 0) == -1) {
1328*355d6bb5Sswilcox 		(void) printf("COULD NOT SEEK TO SUPERBLOCK AT %lld: %s\n",
1329*355d6bb5Sswilcox 		    (longlong_t)bno, strerror(errno));
1330*355d6bb5Sswilcox 		goto out;
1331*355d6bb5Sswilcox 	}
13327c478bd9Sstevel@tonic-gate 
1333*355d6bb5Sswilcox 	if ((io_res = read(fsreadfd, cleanbuf.b_un.b_buf, size)) != size) {
1334*355d6bb5Sswilcox 		report_io_prob("READ FROM", bno, size, io_res);
1335*355d6bb5Sswilcox 		goto out;
1336*355d6bb5Sswilcox 	}
13377c478bd9Sstevel@tonic-gate 
13387c478bd9Sstevel@tonic-gate 	cleanbuf.b_un.b_fs->fs_logbno  = sblock.fs_logbno;
13397c478bd9Sstevel@tonic-gate 	cleanbuf.b_un.b_fs->fs_clean   = sblock.fs_clean;
13407c478bd9Sstevel@tonic-gate 	cleanbuf.b_un.b_fs->fs_state   = sblock.fs_state;
13417c478bd9Sstevel@tonic-gate 	cleanbuf.b_un.b_fs->fs_time    = sblock.fs_time;
13427c478bd9Sstevel@tonic-gate 	cleanbuf.b_un.b_fs->fs_reclaim = sblock.fs_reclaim;
13437c478bd9Sstevel@tonic-gate 	cleanbuf.b_un.b_fs->fs_flags   = sblock.fs_flags;
13447c478bd9Sstevel@tonic-gate 
1345*355d6bb5Sswilcox 	if (llseek(fswritefd, sblkoff, 0) == -1) {
1346*355d6bb5Sswilcox 		(void) printf("COULD NOT SEEK TO SUPERBLOCK AT %lld: %s\n",
1347*355d6bb5Sswilcox 		    (longlong_t)bno, strerror(errno));
1348*355d6bb5Sswilcox 		goto out;
1349*355d6bb5Sswilcox 	}
1350*355d6bb5Sswilcox 
1351*355d6bb5Sswilcox 	if ((io_res = write(fswritefd, cleanbuf.b_un.b_buf, size)) != size) {
1352*355d6bb5Sswilcox 		report_io_prob("WRITE TO", bno, size, io_res);
1353*355d6bb5Sswilcox 		goto out;
1354*355d6bb5Sswilcox 	}
13557c478bd9Sstevel@tonic-gate 
13567c478bd9Sstevel@tonic-gate 	/*
13577c478bd9Sstevel@tonic-gate 	 * 1208040
13587c478bd9Sstevel@tonic-gate 	 * If we had to use -b to grab an alternate superblock, then we
13597c478bd9Sstevel@tonic-gate 	 * likely had to do so because of unacceptable differences between
1360*355d6bb5Sswilcox 	 * the main and alternate superblocks.  So, we had better update
13617c478bd9Sstevel@tonic-gate 	 * the alternate superblock as well, or we'll just fail again
13627c478bd9Sstevel@tonic-gate 	 * the next time we attempt to run fsck!
13637c478bd9Sstevel@tonic-gate 	 */
1364*355d6bb5Sswilcox 	if (bflag != 0) {
1365*355d6bb5Sswilcox 		write_altsb(fswritefd);
13667c478bd9Sstevel@tonic-gate 	}
13677c478bd9Sstevel@tonic-gate 
13687c478bd9Sstevel@tonic-gate 	if (errorlocked) {
13697c478bd9Sstevel@tonic-gate 		if (!do_errorlock(LOCKFS_ULOCK))
13707c478bd9Sstevel@tonic-gate 			pwarn(
13717c478bd9Sstevel@tonic-gate 		    "updateclean(changed): unlock(LOCKFS_ULOCK) failed\n");
13727c478bd9Sstevel@tonic-gate 	}
1373*355d6bb5Sswilcox 
1374*355d6bb5Sswilcox out:
1375*355d6bb5Sswilcox 	if (cleanbuf.b_un.b_buf != NULL) {
1376*355d6bb5Sswilcox 		free((void *)cleanbuf.b_un.b_buf);
1377*355d6bb5Sswilcox 	}
1378*355d6bb5Sswilcox 
1379*355d6bb5Sswilcox 	return (freedlog);
1380*355d6bb5Sswilcox }
1381*355d6bb5Sswilcox 
1382*355d6bb5Sswilcox static void
1383*355d6bb5Sswilcox report_io_prob(caddr_t what, diskaddr_t bno, size_t expected, ssize_t failure)
1384*355d6bb5Sswilcox {
1385*355d6bb5Sswilcox 	if (failure < 0)
1386*355d6bb5Sswilcox 		(void) printf("COULD NOT %s SUPERBLOCK AT %d: %s\n",
1387*355d6bb5Sswilcox 		    what, (int)bno, strerror(errno));
1388*355d6bb5Sswilcox 	else if (failure == 0)
1389*355d6bb5Sswilcox 		(void) printf("COULD NOT %s SUPERBLOCK AT %d: EOF\n",
1390*355d6bb5Sswilcox 		    what, (int)bno);
1391*355d6bb5Sswilcox 	else
1392*355d6bb5Sswilcox 		(void) printf("SHORT %s SUPERBLOCK AT %d: %u out of %u bytes\n",
1393*355d6bb5Sswilcox 		    what, (int)bno, (unsigned)failure, (unsigned)expected);
13947c478bd9Sstevel@tonic-gate }
13957c478bd9Sstevel@tonic-gate 
13967c478bd9Sstevel@tonic-gate /*
13977c478bd9Sstevel@tonic-gate  * print out clean info
13987c478bd9Sstevel@tonic-gate  */
1399*355d6bb5Sswilcox void
1400*355d6bb5Sswilcox printclean(void)
14017c478bd9Sstevel@tonic-gate {
1402*355d6bb5Sswilcox 	caddr_t s;
14037c478bd9Sstevel@tonic-gate 
14047c478bd9Sstevel@tonic-gate 	if (FSOKAY != (sblock.fs_state + sblock.fs_time) && !errorlocked)
14057c478bd9Sstevel@tonic-gate 		s = "unknown";
14067c478bd9Sstevel@tonic-gate 	else
14077c478bd9Sstevel@tonic-gate 		switch (sblock.fs_clean) {
14087c478bd9Sstevel@tonic-gate 
14097c478bd9Sstevel@tonic-gate 		case FSACTIVE:
14107c478bd9Sstevel@tonic-gate 			s = "active";
14117c478bd9Sstevel@tonic-gate 			break;
14127c478bd9Sstevel@tonic-gate 
14137c478bd9Sstevel@tonic-gate 		case FSCLEAN:
14147c478bd9Sstevel@tonic-gate 			s = "clean";
14157c478bd9Sstevel@tonic-gate 			break;
14167c478bd9Sstevel@tonic-gate 
14177c478bd9Sstevel@tonic-gate 		case FSSTABLE:
14187c478bd9Sstevel@tonic-gate 			s = "stable";
14197c478bd9Sstevel@tonic-gate 			break;
14207c478bd9Sstevel@tonic-gate 
14217c478bd9Sstevel@tonic-gate 		case FSLOG:
14227c478bd9Sstevel@tonic-gate 			s = "logging";
14237c478bd9Sstevel@tonic-gate 			break;
14247c478bd9Sstevel@tonic-gate 
14257c478bd9Sstevel@tonic-gate 		case FSBAD:
14267c478bd9Sstevel@tonic-gate 			s = "is bad";
14277c478bd9Sstevel@tonic-gate 			break;
14287c478bd9Sstevel@tonic-gate 
14297c478bd9Sstevel@tonic-gate 		case FSFIX:
14307c478bd9Sstevel@tonic-gate 			s = "being fixed";
14317c478bd9Sstevel@tonic-gate 			break;
14327c478bd9Sstevel@tonic-gate 
14337c478bd9Sstevel@tonic-gate 		default:
14347c478bd9Sstevel@tonic-gate 			s = "unknown";
14357c478bd9Sstevel@tonic-gate 		}
14367c478bd9Sstevel@tonic-gate 
14377c478bd9Sstevel@tonic-gate 	if (preen)
14387c478bd9Sstevel@tonic-gate 		pwarn("is %s.\n", s);
14397c478bd9Sstevel@tonic-gate 	else
1440*355d6bb5Sswilcox 		(void) printf("** %s is %s.\n", devname, s);
14417c478bd9Sstevel@tonic-gate }
14427c478bd9Sstevel@tonic-gate 
1443*355d6bb5Sswilcox int
1444*355d6bb5Sswilcox is_errorlocked(caddr_t fs)
14457c478bd9Sstevel@tonic-gate {
1446*355d6bb5Sswilcox 	int		retval;
1447*355d6bb5Sswilcox 	struct stat64	statb;
1448*355d6bb5Sswilcox 	caddr_t		mountp;
1449*355d6bb5Sswilcox 	struct mnttab	*mntent;
14507c478bd9Sstevel@tonic-gate 
1451*355d6bb5Sswilcox 	retval = 0;
14527c478bd9Sstevel@tonic-gate 
14537c478bd9Sstevel@tonic-gate 	if (!fs)
14547c478bd9Sstevel@tonic-gate 		return (0);
14557c478bd9Sstevel@tonic-gate 
14567c478bd9Sstevel@tonic-gate 	if (stat64(fs, &statb) < 0)
14577c478bd9Sstevel@tonic-gate 		return (0);
14587c478bd9Sstevel@tonic-gate 
14597c478bd9Sstevel@tonic-gate 	if (S_ISDIR(statb.st_mode)) {
14607c478bd9Sstevel@tonic-gate 		mountp = fs;
14617c478bd9Sstevel@tonic-gate 	} else if (S_ISBLK(statb.st_mode) || S_ISCHR(statb.st_mode)) {
1462*355d6bb5Sswilcox 		mntent = search_mnttab(NULL, fs, NULL, 0);
1463*355d6bb5Sswilcox 		if (mntent == NULL)
1464*355d6bb5Sswilcox 			return (0);
1465*355d6bb5Sswilcox 		mountp = mntent->mnt_mountp;
1466*355d6bb5Sswilcox 		if (mountp == NULL) /* theoretically a can't-happen */
14677c478bd9Sstevel@tonic-gate 			return (0);
14687c478bd9Sstevel@tonic-gate 	} else {
14697c478bd9Sstevel@tonic-gate 		return (0);
14707c478bd9Sstevel@tonic-gate 	}
14717c478bd9Sstevel@tonic-gate 
1472*355d6bb5Sswilcox 	/*
1473*355d6bb5Sswilcox 	 * From here on, must `goto out' to avoid memory leakage.
1474*355d6bb5Sswilcox 	 */
1475*355d6bb5Sswilcox 
1476*355d6bb5Sswilcox 	if (elock_combuf == NULL)
14777c478bd9Sstevel@tonic-gate 		elock_combuf =
1478*355d6bb5Sswilcox 			(caddr_t)calloc(LOCKFS_MAXCOMMENTLEN, sizeof (char));
1479*355d6bb5Sswilcox 	else
14807c478bd9Sstevel@tonic-gate 		elock_combuf =
1481*355d6bb5Sswilcox 			(caddr_t)realloc(elock_combuf, LOCKFS_MAXCOMMENTLEN);
14827c478bd9Sstevel@tonic-gate 
14837c478bd9Sstevel@tonic-gate 	if (elock_combuf == NULL)
1484*355d6bb5Sswilcox 		goto out;
14857c478bd9Sstevel@tonic-gate 
1486*355d6bb5Sswilcox 	(void) memset((void *)elock_combuf, 0, LOCKFS_MAXCOMMENTLEN);
1487*355d6bb5Sswilcox 
1488*355d6bb5Sswilcox 	if (elock_mountp != NULL) {
1489*355d6bb5Sswilcox 		free(elock_mountp);
1490*355d6bb5Sswilcox 	}
14917c478bd9Sstevel@tonic-gate 
14927c478bd9Sstevel@tonic-gate 	elock_mountp = strdup(mountp);
1493*355d6bb5Sswilcox 	if (elock_mountp == NULL)
1494*355d6bb5Sswilcox 		goto out;
14957c478bd9Sstevel@tonic-gate 
14967c478bd9Sstevel@tonic-gate 	if (mountfd < 0) {
14977c478bd9Sstevel@tonic-gate 		if ((mountfd = open64(mountp, O_RDONLY)) == -1)
1498*355d6bb5Sswilcox 			goto out;
14997c478bd9Sstevel@tonic-gate 	}
15007c478bd9Sstevel@tonic-gate 
1501*355d6bb5Sswilcox 	if (lfp == NULL) {
15027c478bd9Sstevel@tonic-gate 		lfp = (struct lockfs *)malloc(sizeof (struct lockfs));
1503*355d6bb5Sswilcox 		if (lfp == NULL)
1504*355d6bb5Sswilcox 			goto out;
1505*355d6bb5Sswilcox 		(void) memset((void *)lfp, 0, sizeof (struct lockfs));
15067c478bd9Sstevel@tonic-gate 	}
15077c478bd9Sstevel@tonic-gate 
15087c478bd9Sstevel@tonic-gate 	lfp->lf_comlen = LOCKFS_MAXCOMMENTLEN;
15097c478bd9Sstevel@tonic-gate 	lfp->lf_comment = elock_combuf;
15107c478bd9Sstevel@tonic-gate 
15117c478bd9Sstevel@tonic-gate 	if (ioctl(mountfd, _FIOLFSS, lfp) == -1)
1512*355d6bb5Sswilcox 		goto out;
1513*355d6bb5Sswilcox 
1514*355d6bb5Sswilcox 	/*
1515*355d6bb5Sswilcox 	 * lint believes that the ioctl() (or any other function
1516*355d6bb5Sswilcox 	 * taking lfp as an arg) could free lfp.  This is not the
1517*355d6bb5Sswilcox 	 * case, however.
1518*355d6bb5Sswilcox 	 */
1519*355d6bb5Sswilcox 	retval = LOCKFS_IS_ELOCK(lfp);
15207c478bd9Sstevel@tonic-gate 
1521*355d6bb5Sswilcox out:
1522*355d6bb5Sswilcox 	return (retval);
15237c478bd9Sstevel@tonic-gate }
15247c478bd9Sstevel@tonic-gate 
1525*355d6bb5Sswilcox /*
1526*355d6bb5Sswilcox  * Given a name which is known to be a directory, see if it appears
1527*355d6bb5Sswilcox  * in the vfstab.  If so, return the entry's block (special) device
1528*355d6bb5Sswilcox  * field via devstr.
1529*355d6bb5Sswilcox  */
1530*355d6bb5Sswilcox int
1531*355d6bb5Sswilcox check_vfstab(caddr_t name, caddr_t devstr, size_t str_size)
1532*355d6bb5Sswilcox {
1533*355d6bb5Sswilcox 	return (NULL != search_vfstab(name, NULL, devstr, str_size));
1534*355d6bb5Sswilcox }
15357c478bd9Sstevel@tonic-gate 
1536*355d6bb5Sswilcox /*
1537*355d6bb5Sswilcox  * Given a name which is known to be a directory, see if it appears
1538*355d6bb5Sswilcox  * in the mnttab.  If so, return the entry's block (special) device
1539*355d6bb5Sswilcox  * field via devstr.
1540*355d6bb5Sswilcox  */
1541*355d6bb5Sswilcox int
1542*355d6bb5Sswilcox check_mnttab(caddr_t name, caddr_t devstr, size_t str_size)
1543*355d6bb5Sswilcox {
1544*355d6bb5Sswilcox 	return (NULL != search_mnttab(name, NULL, devstr, str_size));
15457c478bd9Sstevel@tonic-gate }
15467c478bd9Sstevel@tonic-gate 
1547*355d6bb5Sswilcox /*
1548*355d6bb5Sswilcox  * Search for mount point and/or special device in the given file.
1549*355d6bb5Sswilcox  * The first matching entry is returned.
1550*355d6bb5Sswilcox  *
1551*355d6bb5Sswilcox  * If an entry is found and str_size is greater than zero, then
1552*355d6bb5Sswilcox  * up to size_str bytes of the special device name from the entry
1553*355d6bb5Sswilcox  * are copied to devstr.
1554*355d6bb5Sswilcox  */
1555*355d6bb5Sswilcox 
1556*355d6bb5Sswilcox #define	SEARCH_TAB_BODY(st_type, st_file, st_mount, st_special, \
1557*355d6bb5Sswilcox 			st_nuller, st_init, st_searcher) \
1558*355d6bb5Sswilcox 	{ \
1559*355d6bb5Sswilcox 		FILE *fp; \
1560*355d6bb5Sswilcox 		struct st_type *retval = NULL; \
1561*355d6bb5Sswilcox 		struct st_type key; \
1562*355d6bb5Sswilcox 		static struct st_type buffer; \
1563*355d6bb5Sswilcox 		\
1564*355d6bb5Sswilcox 		/* LINTED ``assigned value never used'' */ \
1565*355d6bb5Sswilcox 		st_nuller(&key); \
1566*355d6bb5Sswilcox 		key.st_mount = mountp; \
1567*355d6bb5Sswilcox 		key.st_special = special; \
1568*355d6bb5Sswilcox 		st_init; \
1569*355d6bb5Sswilcox 		\
1570*355d6bb5Sswilcox 		if ((fp = fopen(st_file, "r")) == NULL) \
1571*355d6bb5Sswilcox 			return (NULL); \
1572*355d6bb5Sswilcox 		\
1573*355d6bb5Sswilcox 		if (st_searcher(fp, &buffer, &key) == 0) { \
1574*355d6bb5Sswilcox 			retval = &buffer; \
1575*355d6bb5Sswilcox 			if (devstr != NULL && str_size > 0 && \
1576*355d6bb5Sswilcox 			    buffer.st_special != NULL) { \
1577*355d6bb5Sswilcox 				(void) strlcpy(devstr, buffer.st_special, \
1578*355d6bb5Sswilcox 				    str_size); \
1579*355d6bb5Sswilcox 			} \
1580*355d6bb5Sswilcox 		} \
1581*355d6bb5Sswilcox 		(void) fclose(fp); \
1582*355d6bb5Sswilcox 		return (retval); \
1583*355d6bb5Sswilcox 	}
1584*355d6bb5Sswilcox 
1585*355d6bb5Sswilcox static struct vfstab *
1586*355d6bb5Sswilcox search_vfstab(caddr_t mountp, caddr_t special, caddr_t devstr, size_t str_size)
1587*355d6bb5Sswilcox SEARCH_TAB_BODY(vfstab, VFSTAB, vfs_mountp, vfs_special, vfsnull,
1588*355d6bb5Sswilcox 		(retval = retval), getvfsany)
1589*355d6bb5Sswilcox 
1590*355d6bb5Sswilcox static struct mnttab *
1591*355d6bb5Sswilcox search_mnttab(caddr_t mountp, caddr_t special, caddr_t devstr, size_t str_size)
1592*355d6bb5Sswilcox SEARCH_TAB_BODY(mnttab, MNTTAB, mnt_mountp, mnt_special, mntnull,
1593*355d6bb5Sswilcox 		(key.mnt_fstype = MNTTYPE_UFS), getmntany)
1594*355d6bb5Sswilcox 
1595*355d6bb5Sswilcox int
15967c478bd9Sstevel@tonic-gate do_errorlock(int lock_type)
15977c478bd9Sstevel@tonic-gate {
1598*355d6bb5Sswilcox 	caddr_t	   buf;
1599*355d6bb5Sswilcox 	time_t	   now;
1600*355d6bb5Sswilcox 	struct tm *local;
1601*355d6bb5Sswilcox 	int	   rc;
16027c478bd9Sstevel@tonic-gate 
1603*355d6bb5Sswilcox 	if (elock_combuf == NULL)
16047c478bd9Sstevel@tonic-gate 		errexit("do_errorlock(%s, %d): unallocated elock_combuf\n",
1605*355d6bb5Sswilcox 			elock_mountp ? elock_mountp : "<null>",
1606*355d6bb5Sswilcox 			lock_type);
16077c478bd9Sstevel@tonic-gate 
1608*355d6bb5Sswilcox 	if ((buf = (caddr_t)calloc(LOCKFS_MAXCOMMENTLEN, sizeof (char))) ==
1609*355d6bb5Sswilcox 	    NULL) {
16107c478bd9Sstevel@tonic-gate 		errexit("Couldn't alloc memory for temp. lock status buffer\n");
1611*355d6bb5Sswilcox 	}
1612*355d6bb5Sswilcox 	if (lfp == NULL) {
16137c478bd9Sstevel@tonic-gate 		errexit("do_errorlock(%s, %d): lockfs status unallocated\n",
16147c478bd9Sstevel@tonic-gate 					elock_mountp, lock_type);
16157c478bd9Sstevel@tonic-gate 	}
16167c478bd9Sstevel@tonic-gate 
1617*355d6bb5Sswilcox 	(void) memmove((void *)buf, (void *)elock_combuf,
1618*355d6bb5Sswilcox 	    LOCKFS_MAXCOMMENTLEN-1);
16197c478bd9Sstevel@tonic-gate 
16207c478bd9Sstevel@tonic-gate 	switch (lock_type) {
16217c478bd9Sstevel@tonic-gate 	case LOCKFS_ELOCK:
1622*355d6bb5Sswilcox 		/*
1623*355d6bb5Sswilcox 		 * Note that if it is error-locked, we won't get an
1624*355d6bb5Sswilcox 		 * error back if we try to error-lock it again.
1625*355d6bb5Sswilcox 		 */
16267c478bd9Sstevel@tonic-gate 		if (time(&now) != (time_t)-1) {
16277c478bd9Sstevel@tonic-gate 			if ((local = localtime(&now)) != NULL)
1628*355d6bb5Sswilcox 				(void) snprintf(buf, LOCKFS_MAXCOMMENTLEN,
16297c478bd9Sstevel@tonic-gate 		    "%s [pid:%d fsck start:%02d/%02d/%02d %02d:%02d:%02d",
1630*355d6bb5Sswilcox 				    elock_combuf, (int)pid,
1631*355d6bb5Sswilcox 				    local->tm_mon + 1, local->tm_mday,
16327c478bd9Sstevel@tonic-gate 				    (local->tm_year % 100), local->tm_hour,
16337c478bd9Sstevel@tonic-gate 				    local->tm_min, local->tm_sec);
16347c478bd9Sstevel@tonic-gate 			else
1635*355d6bb5Sswilcox 				(void) snprintf(buf, LOCKFS_MAXCOMMENTLEN,
1636*355d6bb5Sswilcox 				    "%s [fsck pid %d", elock_combuf, pid);
16377c478bd9Sstevel@tonic-gate 
16387c478bd9Sstevel@tonic-gate 		} else {
1639*355d6bb5Sswilcox 			(void) snprintf(buf, LOCKFS_MAXCOMMENTLEN,
1640*355d6bb5Sswilcox 			    "%s [fsck pid %d", elock_combuf, pid);
16417c478bd9Sstevel@tonic-gate 		}
16427c478bd9Sstevel@tonic-gate 		break;
16437c478bd9Sstevel@tonic-gate 
16447c478bd9Sstevel@tonic-gate 	case LOCKFS_ULOCK:
16457c478bd9Sstevel@tonic-gate 		if (time(&now) != (time_t)-1) {
16467c478bd9Sstevel@tonic-gate 			if ((local = localtime(&now)) != NULL) {
1647*355d6bb5Sswilcox 				(void) snprintf(buf, LOCKFS_MAXCOMMENTLEN,
16487c478bd9Sstevel@tonic-gate 				    "%s, done:%02d/%02d/%02d %02d:%02d:%02d]",
16497c478bd9Sstevel@tonic-gate 				    elock_combuf,
1650*355d6bb5Sswilcox 				    local->tm_mon + 1, local->tm_mday,
16517c478bd9Sstevel@tonic-gate 				    (local->tm_year % 100), local->tm_hour,
16527c478bd9Sstevel@tonic-gate 				    local->tm_min, local->tm_sec);
16537c478bd9Sstevel@tonic-gate 			} else {
1654*355d6bb5Sswilcox 				(void) snprintf(buf, LOCKFS_MAXCOMMENTLEN,
1655*355d6bb5Sswilcox 				    "%s]", elock_combuf);
16567c478bd9Sstevel@tonic-gate 			}
16577c478bd9Sstevel@tonic-gate 		} else {
1658*355d6bb5Sswilcox 			(void) snprintf(buf, LOCKFS_MAXCOMMENTLEN,
1659*355d6bb5Sswilcox 			    "%s]", elock_combuf);
16607c478bd9Sstevel@tonic-gate 		}
16617c478bd9Sstevel@tonic-gate 		if ((rc = ioctl(mountfd, _FIOLFSS, lfp)) == -1) {
1662*355d6bb5Sswilcox 			pwarn("do_errorlock: unlock failed: %s\n",
1663*355d6bb5Sswilcox 			    strerror(errno));
16647c478bd9Sstevel@tonic-gate 			goto out;
16657c478bd9Sstevel@tonic-gate 		}
16667c478bd9Sstevel@tonic-gate 		break;
16677c478bd9Sstevel@tonic-gate 
16687c478bd9Sstevel@tonic-gate 	default:
16697c478bd9Sstevel@tonic-gate 		break;
16707c478bd9Sstevel@tonic-gate 	}
16717c478bd9Sstevel@tonic-gate 
1672*355d6bb5Sswilcox 	(void) memmove((void *)elock_combuf, (void *)buf,
1673*355d6bb5Sswilcox 	    LOCKFS_MAXCOMMENTLEN - 1);
16747c478bd9Sstevel@tonic-gate 
1675*355d6bb5Sswilcox 	lfp->lf_lock = lock_type;
1676*355d6bb5Sswilcox 	lfp->lf_comlen = LOCKFS_MAXCOMMENTLEN;
1677*355d6bb5Sswilcox 	lfp->lf_comment = elock_combuf;
1678*355d6bb5Sswilcox 	lfp->lf_flags = 0;
1679*355d6bb5Sswilcox 	errno = 0;
16807c478bd9Sstevel@tonic-gate 
16817c478bd9Sstevel@tonic-gate 	if ((rc = ioctl(mountfd, _FIOLFS, lfp)) == -1) {
16827c478bd9Sstevel@tonic-gate 		if (errno == EINVAL) {
16837c478bd9Sstevel@tonic-gate 			pwarn("Another fsck active?\n");
16847c478bd9Sstevel@tonic-gate 			iscorrupt = 0;	/* don't go away mad, just go away */
16857c478bd9Sstevel@tonic-gate 		} else {
1686*355d6bb5Sswilcox 			pwarn("do_errorlock(lock_type:%d, %s) failed: %s\n",
1687*355d6bb5Sswilcox 			    lock_type, elock_combuf, strerror(errno));
16887c478bd9Sstevel@tonic-gate 		}
16897c478bd9Sstevel@tonic-gate 	}
16907c478bd9Sstevel@tonic-gate out:
1691*355d6bb5Sswilcox 	if (buf != NULL) {
1692*355d6bb5Sswilcox 		free((void *)buf);
1693*355d6bb5Sswilcox 	}
16947c478bd9Sstevel@tonic-gate 
16957c478bd9Sstevel@tonic-gate 	return (rc != -1);
16967c478bd9Sstevel@tonic-gate }
16977c478bd9Sstevel@tonic-gate 
16987c478bd9Sstevel@tonic-gate /*
1699*355d6bb5Sswilcox  * Shadow inode support.  To register a shadow with a client is to note
1700*355d6bb5Sswilcox  * that an inode (the client) refers to the shadow.
17017c478bd9Sstevel@tonic-gate  */
17027c478bd9Sstevel@tonic-gate 
17037c478bd9Sstevel@tonic-gate static struct shadowclients *
17047c478bd9Sstevel@tonic-gate newshadowclient(struct shadowclients *prev)
17057c478bd9Sstevel@tonic-gate {
17067c478bd9Sstevel@tonic-gate 	struct shadowclients *rc;
17077c478bd9Sstevel@tonic-gate 
17087c478bd9Sstevel@tonic-gate 	rc = (struct shadowclients *)malloc(sizeof (*rc));
17097c478bd9Sstevel@tonic-gate 	if (rc == NULL)
1710*355d6bb5Sswilcox 		errexit("newshadowclient: cannot malloc shadow client");
17117c478bd9Sstevel@tonic-gate 	rc->next = prev;
17127c478bd9Sstevel@tonic-gate 	rc->nclients = 0;
17137c478bd9Sstevel@tonic-gate 
1714*355d6bb5Sswilcox 	rc->client = (fsck_ino_t *)malloc(sizeof (fsck_ino_t) *
1715*355d6bb5Sswilcox 	    maxshadowclients);
17167c478bd9Sstevel@tonic-gate 	if (rc->client == NULL)
1717*355d6bb5Sswilcox 		errexit("newshadowclient: cannot malloc client array");
17187c478bd9Sstevel@tonic-gate 	return (rc);
17197c478bd9Sstevel@tonic-gate }
17207c478bd9Sstevel@tonic-gate 
17217c478bd9Sstevel@tonic-gate void
1722*355d6bb5Sswilcox registershadowclient(fsck_ino_t shadow, fsck_ino_t client,
1723*355d6bb5Sswilcox 	struct shadowclientinfo **info)
17247c478bd9Sstevel@tonic-gate {
17257c478bd9Sstevel@tonic-gate 	struct shadowclientinfo *sci;
17267c478bd9Sstevel@tonic-gate 	struct shadowclients *scc;
17277c478bd9Sstevel@tonic-gate 
1728*355d6bb5Sswilcox 	/*
1729*355d6bb5Sswilcox 	 * Already have a record for this shadow?
1730*355d6bb5Sswilcox 	 */
1731*355d6bb5Sswilcox 	for (sci = *info; sci != NULL; sci = sci->next)
17327c478bd9Sstevel@tonic-gate 		if (sci->shadow == shadow)
17337c478bd9Sstevel@tonic-gate 			break;
17347c478bd9Sstevel@tonic-gate 	if (sci == NULL) {
1735*355d6bb5Sswilcox 		/*
1736*355d6bb5Sswilcox 		 * It's a new shadow, add it to the list
1737*355d6bb5Sswilcox 		 */
17387c478bd9Sstevel@tonic-gate 		sci = (struct shadowclientinfo *)malloc(sizeof (*sci));
17397c478bd9Sstevel@tonic-gate 		if (sci == NULL)
17407c478bd9Sstevel@tonic-gate 			errexit("registershadowclient: cannot malloc");
17417c478bd9Sstevel@tonic-gate 		sci->next = *info;
17427c478bd9Sstevel@tonic-gate 		*info = sci;
17437c478bd9Sstevel@tonic-gate 		sci->shadow = shadow;
17447c478bd9Sstevel@tonic-gate 		sci->totalClients = 0;
17457c478bd9Sstevel@tonic-gate 		sci->clients = newshadowclient(NULL);
17467c478bd9Sstevel@tonic-gate 	}
17477c478bd9Sstevel@tonic-gate 
17487c478bd9Sstevel@tonic-gate 	sci->totalClients++;
17497c478bd9Sstevel@tonic-gate 	scc = sci->clients;
17507c478bd9Sstevel@tonic-gate 	if (scc->nclients >= maxshadowclients) {
17517c478bd9Sstevel@tonic-gate 		scc = newshadowclient(sci->clients);
17527c478bd9Sstevel@tonic-gate 		sci->clients = scc;
17537c478bd9Sstevel@tonic-gate 	}
17547c478bd9Sstevel@tonic-gate 
17557c478bd9Sstevel@tonic-gate 	scc->client[scc->nclients++] = client;
17567c478bd9Sstevel@tonic-gate }
17577c478bd9Sstevel@tonic-gate 
1758*355d6bb5Sswilcox /*
1759*355d6bb5Sswilcox  * Locate and discard a shadow.
1760*355d6bb5Sswilcox  */
1761*355d6bb5Sswilcox void
1762*355d6bb5Sswilcox clearshadow(fsck_ino_t shadow, struct shadowclientinfo **info)
1763*355d6bb5Sswilcox {
1764*355d6bb5Sswilcox 	struct shadowclientinfo *sci, *prev;
1765*355d6bb5Sswilcox 
1766*355d6bb5Sswilcox 	/*
1767*355d6bb5Sswilcox 	 * Do we have a record for this shadow?
1768*355d6bb5Sswilcox 	 */
1769*355d6bb5Sswilcox 	prev = NULL;
1770*355d6bb5Sswilcox 	for (sci = *info; sci != NULL; sci = sci->next) {
1771*355d6bb5Sswilcox 		if (sci->shadow == shadow)
1772*355d6bb5Sswilcox 			break;
1773*355d6bb5Sswilcox 		prev = sci;
1774*355d6bb5Sswilcox 	}
1775*355d6bb5Sswilcox 
1776*355d6bb5Sswilcox 	if (sci != NULL) {
1777*355d6bb5Sswilcox 		/*
1778*355d6bb5Sswilcox 		 * First, pull it off the list, since we know there
1779*355d6bb5Sswilcox 		 * shouldn't be any future references to this one.
1780*355d6bb5Sswilcox 		 */
1781*355d6bb5Sswilcox 		if (prev == NULL)
1782*355d6bb5Sswilcox 			*info = sci->next;
1783*355d6bb5Sswilcox 		else
1784*355d6bb5Sswilcox 			prev->next = sci->next;
1785*355d6bb5Sswilcox 		deshadow(sci, clearattrref);
1786*355d6bb5Sswilcox 	}
1787*355d6bb5Sswilcox }
1788*355d6bb5Sswilcox 
1789*355d6bb5Sswilcox /*
1790*355d6bb5Sswilcox  * Discard all memory used to track clients of a shadow.
1791*355d6bb5Sswilcox  */
1792*355d6bb5Sswilcox void
1793*355d6bb5Sswilcox deshadow(struct shadowclientinfo *sci, void (*cb)(fsck_ino_t))
1794*355d6bb5Sswilcox {
1795*355d6bb5Sswilcox 	struct shadowclients *clients, *discard;
1796*355d6bb5Sswilcox 	int idx;
1797*355d6bb5Sswilcox 
1798*355d6bb5Sswilcox 	clients = sci->clients;
1799*355d6bb5Sswilcox 	while (clients != NULL) {
1800*355d6bb5Sswilcox 		discard = clients;
1801*355d6bb5Sswilcox 		clients = clients->next;
1802*355d6bb5Sswilcox 		if (discard->client != NULL) {
1803*355d6bb5Sswilcox 			if (cb != NULL) {
1804*355d6bb5Sswilcox 				for (idx = 0; idx < discard->nclients; idx++)
1805*355d6bb5Sswilcox 					(*cb)(discard->client[idx]);
1806*355d6bb5Sswilcox 			}
1807*355d6bb5Sswilcox 			free((void *)discard->client);
1808*355d6bb5Sswilcox 		}
1809*355d6bb5Sswilcox 		free((void *)discard);
1810*355d6bb5Sswilcox 	}
1811*355d6bb5Sswilcox 
1812*355d6bb5Sswilcox 	free((void *)sci);
1813*355d6bb5Sswilcox }
1814*355d6bb5Sswilcox 
18157c478bd9Sstevel@tonic-gate /*
18167c478bd9Sstevel@tonic-gate  * Allocate more buffer as need arises but allocate one at a time.
18177c478bd9Sstevel@tonic-gate  * This is done to make sure that fsck does not exit with error if it
1818*355d6bb5Sswilcox  * needs more buffer to complete its task.
18197c478bd9Sstevel@tonic-gate  */
18207c478bd9Sstevel@tonic-gate static struct bufarea *
1821*355d6bb5Sswilcox alloc_bufarea(void)
18227c478bd9Sstevel@tonic-gate {
1823*355d6bb5Sswilcox 	struct bufarea *newbp;
1824*355d6bb5Sswilcox 	caddr_t bufp;
18257c478bd9Sstevel@tonic-gate 
18267c478bd9Sstevel@tonic-gate 	bufp = malloc((unsigned int)sblock.fs_bsize);
1827*355d6bb5Sswilcox 	if (bufp == NULL)
1828*355d6bb5Sswilcox 		return (NULL);
1829*355d6bb5Sswilcox 
1830*355d6bb5Sswilcox 	newbp = (struct bufarea *)malloc(sizeof (struct bufarea));
1831*355d6bb5Sswilcox 	if (newbp == NULL) {
1832*355d6bb5Sswilcox 		free((void *)bufp);
18337c478bd9Sstevel@tonic-gate 		return (NULL);
18347c478bd9Sstevel@tonic-gate 	}
1835*355d6bb5Sswilcox 
1836*355d6bb5Sswilcox 	initbarea(newbp);
1837*355d6bb5Sswilcox 	newbp->b_un.b_buf = bufp;
1838*355d6bb5Sswilcox 	newbp->b_prev = &bufhead;
1839*355d6bb5Sswilcox 	newbp->b_next = bufhead.b_next;
1840*355d6bb5Sswilcox 	bufhead.b_next->b_prev = newbp;
1841*355d6bb5Sswilcox 	bufhead.b_next = newbp;
18427c478bd9Sstevel@tonic-gate 	bufhead.b_size++;
1843*355d6bb5Sswilcox 	return (newbp);
1844*355d6bb5Sswilcox }
1845*355d6bb5Sswilcox 
1846*355d6bb5Sswilcox /*
1847*355d6bb5Sswilcox  * We length-limit in both unrawname() and rawname() to avoid
1848*355d6bb5Sswilcox  * overflowing our arrays or those of our naive, trusting callers.
1849*355d6bb5Sswilcox  */
1850*355d6bb5Sswilcox 
1851*355d6bb5Sswilcox caddr_t
1852*355d6bb5Sswilcox unrawname(caddr_t name)
1853*355d6bb5Sswilcox {
1854*355d6bb5Sswilcox 	caddr_t dp;
1855*355d6bb5Sswilcox 	static char fullname[MAXPATHLEN + 1];
1856*355d6bb5Sswilcox 
1857*355d6bb5Sswilcox 	if ((dp = getfullblkname(name)) == NULL)
1858*355d6bb5Sswilcox 		return ("");
1859*355d6bb5Sswilcox 
1860*355d6bb5Sswilcox 	(void) strlcpy(fullname, dp, sizeof (fullname));
1861*355d6bb5Sswilcox 	/*
1862*355d6bb5Sswilcox 	 * Not reporting under debug, as the allocation isn't
1863*355d6bb5Sswilcox 	 * reported by getfullblkname.  The idea is that we
1864*355d6bb5Sswilcox 	 * produce balanced alloc/free instances.
1865*355d6bb5Sswilcox 	 */
1866*355d6bb5Sswilcox 	free(dp);
1867*355d6bb5Sswilcox 
1868*355d6bb5Sswilcox 	return (fullname);
1869*355d6bb5Sswilcox }
1870*355d6bb5Sswilcox 
1871*355d6bb5Sswilcox caddr_t
1872*355d6bb5Sswilcox rawname(caddr_t name)
1873*355d6bb5Sswilcox {
1874*355d6bb5Sswilcox 	caddr_t dp;
1875*355d6bb5Sswilcox 	static char fullname[MAXPATHLEN + 1];
1876*355d6bb5Sswilcox 
1877*355d6bb5Sswilcox 	if ((dp = getfullrawname(name)) == NULL)
1878*355d6bb5Sswilcox 		return ("");
1879*355d6bb5Sswilcox 
1880*355d6bb5Sswilcox 	(void) strlcpy(fullname, dp, sizeof (fullname));
1881*355d6bb5Sswilcox 	/*
1882*355d6bb5Sswilcox 	 * Not reporting under debug, as the allocation isn't
1883*355d6bb5Sswilcox 	 * reported by getfullblkname.  The idea is that we
1884*355d6bb5Sswilcox 	 * produce balanced alloc/free instances.
1885*355d6bb5Sswilcox 	 */
1886*355d6bb5Sswilcox 	free(dp);
1887*355d6bb5Sswilcox 
1888*355d6bb5Sswilcox 	return (fullname);
1889*355d6bb5Sswilcox }
1890*355d6bb5Sswilcox 
1891*355d6bb5Sswilcox /*
1892*355d6bb5Sswilcox  * Make sure that a cg header looks at least moderately reasonable.
1893*355d6bb5Sswilcox  * We want to be able to trust the contents enough to be able to use
1894*355d6bb5Sswilcox  * the standard accessor macros.  So, besides looking at the obvious
1895*355d6bb5Sswilcox  * such as the magic number, we verify that the offset field values
1896*355d6bb5Sswilcox  * are properly aligned and not too big or small.
1897*355d6bb5Sswilcox  *
1898*355d6bb5Sswilcox  * Returns a NULL pointer if the cg is sane enough for our needs, else
1899*355d6bb5Sswilcox  * a dynamically-allocated string describing all of its faults.
1900*355d6bb5Sswilcox  */
1901*355d6bb5Sswilcox #define	Append_Error(full, full_len, addition, addition_len) \
1902*355d6bb5Sswilcox 	if (full == NULL) { \
1903*355d6bb5Sswilcox 		full = addition; \
1904*355d6bb5Sswilcox 		full_len = addition_len; \
1905*355d6bb5Sswilcox 	} else { \
1906*355d6bb5Sswilcox 		/* lint doesn't think realloc() understands NULLs */ \
1907*355d6bb5Sswilcox 		full = realloc(full, full_len + addition_len + 1); \
1908*355d6bb5Sswilcox 		if (full == NULL) { \
1909*355d6bb5Sswilcox 			errexit("Out of memory in cg_sanity"); \
1910*355d6bb5Sswilcox 			/* NOTREACHED */ \
1911*355d6bb5Sswilcox 		} \
1912*355d6bb5Sswilcox 		(void) strcpy(full + full_len, addition); \
1913*355d6bb5Sswilcox 		full_len += addition_len; \
1914*355d6bb5Sswilcox 		free(addition); \
1915*355d6bb5Sswilcox 	}
1916*355d6bb5Sswilcox 
1917*355d6bb5Sswilcox caddr_t
1918*355d6bb5Sswilcox cg_sanity(struct cg *cgp, int cgno, int *is_fatal)
1919*355d6bb5Sswilcox {
1920*355d6bb5Sswilcox 	caddr_t full_err;
1921*355d6bb5Sswilcox 	caddr_t this_err = NULL;
1922*355d6bb5Sswilcox 	int full_len, this_len;
1923*355d6bb5Sswilcox 	daddr32_t ndblk;
1924*355d6bb5Sswilcox 	daddr32_t exp_btotoff, exp_boff, exp_iusedoff;
1925*355d6bb5Sswilcox 	daddr32_t exp_freeoff, exp_nextfreeoff;
1926*355d6bb5Sswilcox 
1927*355d6bb5Sswilcox 	cg_constants(cgno, &exp_btotoff, &exp_boff, &exp_iusedoff,
1928*355d6bb5Sswilcox 	    &exp_freeoff, &exp_nextfreeoff, &ndblk);
1929*355d6bb5Sswilcox 
1930*355d6bb5Sswilcox 	full_err = NULL;
1931*355d6bb5Sswilcox 	full_len = 0;
1932*355d6bb5Sswilcox 	*is_fatal = 0;
1933*355d6bb5Sswilcox 
1934*355d6bb5Sswilcox 	if (!cg_chkmagic(cgp)) {
1935*355d6bb5Sswilcox 		this_len = fsck_asprintf(&this_err,
1936*355d6bb5Sswilcox 		    "BAD CG MAGIC NUMBER (0x%x should be 0x%x)\n",
1937*355d6bb5Sswilcox 		    cgp->cg_magic, CG_MAGIC);
1938*355d6bb5Sswilcox 		Append_Error(full_err, full_len, this_err, this_len);
1939*355d6bb5Sswilcox 		*is_fatal = 1;
1940*355d6bb5Sswilcox 	}
1941*355d6bb5Sswilcox 
1942*355d6bb5Sswilcox 	if (cgp->cg_cgx != cgno) {
1943*355d6bb5Sswilcox 		this_len = fsck_asprintf(&this_err,
1944*355d6bb5Sswilcox 		    "WRONG CG NUMBER (%d should be %d)\n",
1945*355d6bb5Sswilcox 		    cgp->cg_cgx, cgno);
1946*355d6bb5Sswilcox 		Append_Error(full_err, full_len, this_err, this_len);
1947*355d6bb5Sswilcox 	}
1948*355d6bb5Sswilcox 
1949*355d6bb5Sswilcox 	if ((cgp->cg_btotoff & 3) != 0) {
1950*355d6bb5Sswilcox 		this_len = fsck_asprintf(&this_err,
1951*355d6bb5Sswilcox 		    "BLOCK TOTALS OFFSET %d NOT FOUR-BYTE ALIGNED\n",
1952*355d6bb5Sswilcox 		    cgp->cg_btotoff);
1953*355d6bb5Sswilcox 		Append_Error(full_err, full_len, this_err, this_len);
1954*355d6bb5Sswilcox 	}
1955*355d6bb5Sswilcox 
1956*355d6bb5Sswilcox 	if ((cgp->cg_boff & 1) != 0) {
1957*355d6bb5Sswilcox 		this_len = fsck_asprintf(&this_err,
1958*355d6bb5Sswilcox 	    "FREE BLOCK POSITIONS TABLE OFFSET %d NOT TWO-BYTE ALIGNED\n",
1959*355d6bb5Sswilcox 		    cgp->cg_boff);
1960*355d6bb5Sswilcox 		Append_Error(full_err, full_len, this_err, this_len);
1961*355d6bb5Sswilcox 	}
1962*355d6bb5Sswilcox 
1963*355d6bb5Sswilcox 	if ((cgp->cg_ncyl < 1) || (cgp->cg_ncyl > sblock.fs_cpg)) {
1964*355d6bb5Sswilcox 		if (cgp->cg_ncyl < 1) {
1965*355d6bb5Sswilcox 			this_len = fsck_asprintf(&this_err,
1966*355d6bb5Sswilcox 	    "IMPOSSIBLE NUMBER OF CYLINDERS IN GROUP (%d is less than 1)\n",
1967*355d6bb5Sswilcox 			    cgp->cg_ncyl);
1968*355d6bb5Sswilcox 		} else {
1969*355d6bb5Sswilcox 			this_len = fsck_asprintf(&this_err,
1970*355d6bb5Sswilcox 	    "IMPOSSIBLE NUMBER OF CYLINDERS IN GROUP (%d is greater than %d)\n",
1971*355d6bb5Sswilcox 			    cgp->cg_ncyl, sblock.fs_cpg);
1972*355d6bb5Sswilcox 		}
1973*355d6bb5Sswilcox 		Append_Error(full_err, full_len, this_err, this_len);
1974*355d6bb5Sswilcox 	}
1975*355d6bb5Sswilcox 
1976*355d6bb5Sswilcox 	if (cgp->cg_niblk != sblock.fs_ipg) {
1977*355d6bb5Sswilcox 		this_len = fsck_asprintf(&this_err,
1978*355d6bb5Sswilcox 		    "INCORRECT NUMBER OF INODES IN GROUP (%d should be %d)\n",
1979*355d6bb5Sswilcox 		    cgp->cg_niblk, sblock.fs_ipg);
1980*355d6bb5Sswilcox 		Append_Error(full_err, full_len, this_err, this_len);
1981*355d6bb5Sswilcox 	}
1982*355d6bb5Sswilcox 
1983*355d6bb5Sswilcox 	if (cgp->cg_ndblk != ndblk) {
1984*355d6bb5Sswilcox 		this_len = fsck_asprintf(&this_err,
1985*355d6bb5Sswilcox 	    "INCORRECT NUMBER OF DATA BLOCKS IN GROUP (%d should be %d)\n",
1986*355d6bb5Sswilcox 		    cgp->cg_ndblk, ndblk);
1987*355d6bb5Sswilcox 		Append_Error(full_err, full_len, this_err, this_len);
1988*355d6bb5Sswilcox 	}
1989*355d6bb5Sswilcox 
1990*355d6bb5Sswilcox 	if ((cgp->cg_rotor < 0) || (cgp->cg_rotor >= ndblk)) {
1991*355d6bb5Sswilcox 		this_len = fsck_asprintf(&this_err,
1992*355d6bb5Sswilcox 		    "IMPOSSIBLE BLOCK ALLOCATION ROTOR POSITION "
1993*355d6bb5Sswilcox 		    "(%d should be at least 0 and less than %d)\n",
1994*355d6bb5Sswilcox 		    cgp->cg_rotor, ndblk);
1995*355d6bb5Sswilcox 		Append_Error(full_err, full_len, this_err, this_len);
1996*355d6bb5Sswilcox 	}
1997*355d6bb5Sswilcox 
1998*355d6bb5Sswilcox 	if ((cgp->cg_frotor < 0) || (cgp->cg_frotor >= ndblk)) {
1999*355d6bb5Sswilcox 		this_len = fsck_asprintf(&this_err,
2000*355d6bb5Sswilcox 		    "IMPOSSIBLE FRAGMENT ALLOCATION ROTOR POSITION "
2001*355d6bb5Sswilcox 		    "(%d should be at least 0 and less than %d)\n",
2002*355d6bb5Sswilcox 		    cgp->cg_frotor, ndblk);
2003*355d6bb5Sswilcox 		Append_Error(full_err, full_len, this_err, this_len);
2004*355d6bb5Sswilcox 	}
2005*355d6bb5Sswilcox 
2006*355d6bb5Sswilcox 	if ((cgp->cg_irotor < 0) || (cgp->cg_irotor >= sblock.fs_ipg)) {
2007*355d6bb5Sswilcox 		this_len = fsck_asprintf(&this_err,
2008*355d6bb5Sswilcox 		    "IMPOSSIBLE INODE ALLOCATION ROTOR POSITION "
2009*355d6bb5Sswilcox 		    "(%d should be at least 0 and less than %d)\n",
2010*355d6bb5Sswilcox 		    cgp->cg_irotor, sblock.fs_ipg);
2011*355d6bb5Sswilcox 		Append_Error(full_err, full_len, this_err, this_len);
2012*355d6bb5Sswilcox 	}
2013*355d6bb5Sswilcox 
2014*355d6bb5Sswilcox 	if (cgp->cg_btotoff != exp_btotoff) {
2015*355d6bb5Sswilcox 		this_len = fsck_asprintf(&this_err,
2016*355d6bb5Sswilcox 		    "INCORRECT BLOCK TOTALS OFFSET (%d should be %d)\n",
2017*355d6bb5Sswilcox 		    cgp->cg_btotoff, exp_btotoff);
2018*355d6bb5Sswilcox 		Append_Error(full_err, full_len, this_err, this_len);
2019*355d6bb5Sswilcox 	}
2020*355d6bb5Sswilcox 
2021*355d6bb5Sswilcox 	if (cgp->cg_boff != exp_boff) {
2022*355d6bb5Sswilcox 		this_len = fsck_asprintf(&this_err,
2023*355d6bb5Sswilcox 		    "BAD FREE BLOCK POSITIONS TABLE OFFSET (%d should %d)\n",
2024*355d6bb5Sswilcox 		    cgp->cg_boff, exp_boff);
2025*355d6bb5Sswilcox 		Append_Error(full_err, full_len, this_err, this_len);
2026*355d6bb5Sswilcox 	}
2027*355d6bb5Sswilcox 
2028*355d6bb5Sswilcox 	if (cgp->cg_iusedoff != exp_iusedoff) {
2029*355d6bb5Sswilcox 		this_len = fsck_asprintf(&this_err,
2030*355d6bb5Sswilcox 		    "INCORRECT USED INODE MAP OFFSET (%d should be %d)\n",
2031*355d6bb5Sswilcox 		    cgp->cg_iusedoff, exp_iusedoff);
2032*355d6bb5Sswilcox 		Append_Error(full_err, full_len, this_err, this_len);
2033*355d6bb5Sswilcox 	}
2034*355d6bb5Sswilcox 
2035*355d6bb5Sswilcox 	if (cgp->cg_freeoff != exp_freeoff) {
2036*355d6bb5Sswilcox 		this_len = fsck_asprintf(&this_err,
2037*355d6bb5Sswilcox 		    "INCORRECT FREE FRAGMENT MAP OFFSET (%d should be %d)\n",
2038*355d6bb5Sswilcox 		    cgp->cg_freeoff, exp_freeoff);
2039*355d6bb5Sswilcox 		Append_Error(full_err, full_len, this_err, this_len);
2040*355d6bb5Sswilcox 	}
2041*355d6bb5Sswilcox 
2042*355d6bb5Sswilcox 	if (cgp->cg_nextfreeoff != exp_nextfreeoff) {
2043*355d6bb5Sswilcox 		this_len = fsck_asprintf(&this_err,
2044*355d6bb5Sswilcox 		    "END OF HEADER POSITION INCORRECT (%d should be %d)\n",
2045*355d6bb5Sswilcox 		    cgp->cg_nextfreeoff, exp_nextfreeoff);
2046*355d6bb5Sswilcox 		Append_Error(full_err, full_len, this_err, this_len);
2047*355d6bb5Sswilcox 	}
2048*355d6bb5Sswilcox 
2049*355d6bb5Sswilcox 	return (full_err);
2050*355d6bb5Sswilcox }
2051*355d6bb5Sswilcox 
2052*355d6bb5Sswilcox #undef	Append_Error
2053*355d6bb5Sswilcox 
2054*355d6bb5Sswilcox /*
2055*355d6bb5Sswilcox  * This is taken from mkfs, and is what is used to come up with the
2056*355d6bb5Sswilcox  * original values for a struct cg.  This implies that, since these
2057*355d6bb5Sswilcox  * are all constants, recalculating them now should give us the same
2058*355d6bb5Sswilcox  * thing as what's on disk.
2059*355d6bb5Sswilcox  */
2060*355d6bb5Sswilcox static void
2061*355d6bb5Sswilcox cg_constants(int cgno, daddr32_t *btotoff, daddr32_t *boff,
2062*355d6bb5Sswilcox 	daddr32_t *iusedoff, daddr32_t *freeoff, daddr32_t *nextfreeoff,
2063*355d6bb5Sswilcox 	daddr32_t *ndblk)
2064*355d6bb5Sswilcox {
2065*355d6bb5Sswilcox 	daddr32_t cbase, dmax;
2066*355d6bb5Sswilcox 	struct cg *cgp;
2067*355d6bb5Sswilcox 
2068*355d6bb5Sswilcox 	(void) getblk(&cgblk, (diskaddr_t)cgtod(&sblock, cgno),
2069*355d6bb5Sswilcox 	    (size_t)sblock.fs_cgsize);
2070*355d6bb5Sswilcox 	cgp = cgblk.b_un.b_cg;
2071*355d6bb5Sswilcox 
2072*355d6bb5Sswilcox 	cbase = cgbase(&sblock, cgno);
2073*355d6bb5Sswilcox 	dmax = cbase + sblock.fs_fpg;
2074*355d6bb5Sswilcox 	if (dmax > sblock.fs_size)
2075*355d6bb5Sswilcox 		dmax = sblock.fs_size;
2076*355d6bb5Sswilcox 
2077*355d6bb5Sswilcox 	/* LINTED pointer difference won't overflow */
2078*355d6bb5Sswilcox 	*btotoff = &cgp->cg_space[0] - (uchar_t *)(&cgp->cg_link);
2079*355d6bb5Sswilcox 	*boff = *btotoff + sblock.fs_cpg * sizeof (daddr32_t);
2080*355d6bb5Sswilcox 	*iusedoff = *boff + sblock.fs_cpg * sblock.fs_nrpos * sizeof (int16_t);
2081*355d6bb5Sswilcox 	*freeoff = *iusedoff + howmany(sblock.fs_ipg, NBBY);
2082*355d6bb5Sswilcox 	*nextfreeoff = *freeoff +
2083*355d6bb5Sswilcox 		howmany(sblock.fs_cpg * sblock.fs_spc / NSPF(&sblock), NBBY);
2084*355d6bb5Sswilcox 	*ndblk = dmax - cbase;
2085*355d6bb5Sswilcox }
2086*355d6bb5Sswilcox 
2087*355d6bb5Sswilcox /*
2088*355d6bb5Sswilcox  * Corrects all fields in the cg that can be done with the available
2089*355d6bb5Sswilcox  * redundant data.
2090*355d6bb5Sswilcox  */
2091*355d6bb5Sswilcox void
2092*355d6bb5Sswilcox fix_cg(struct cg *cgp, int cgno)
2093*355d6bb5Sswilcox {
2094*355d6bb5Sswilcox 	daddr32_t exp_btotoff, exp_boff, exp_iusedoff;
2095*355d6bb5Sswilcox 	daddr32_t exp_freeoff, exp_nextfreeoff;
2096*355d6bb5Sswilcox 	daddr32_t ndblk;
2097*355d6bb5Sswilcox 
2098*355d6bb5Sswilcox 	cg_constants(cgno, &exp_btotoff, &exp_boff, &exp_iusedoff,
2099*355d6bb5Sswilcox 	    &exp_freeoff, &exp_nextfreeoff, &ndblk);
2100*355d6bb5Sswilcox 
2101*355d6bb5Sswilcox 	if (cgp->cg_cgx != cgno) {
2102*355d6bb5Sswilcox 		cgp->cg_cgx = cgno;
2103*355d6bb5Sswilcox 	}
2104*355d6bb5Sswilcox 
2105*355d6bb5Sswilcox 	if ((cgp->cg_ncyl < 1) || (cgp->cg_ncyl > sblock.fs_cpg)) {
2106*355d6bb5Sswilcox 		if (cgno == sblock.fs_ncg) {
2107*355d6bb5Sswilcox 			cgp->cg_ncyl = sblock.fs_ncyl -
2108*355d6bb5Sswilcox 				(sblock.fs_ncg * (cgno - 1));
2109*355d6bb5Sswilcox 		} else {
2110*355d6bb5Sswilcox 			cgp->cg_ncyl = sblock.fs_cpg;
2111*355d6bb5Sswilcox 		}
2112*355d6bb5Sswilcox 	}
2113*355d6bb5Sswilcox 
2114*355d6bb5Sswilcox 	if (cgp->cg_niblk != sblock.fs_ipg) {
2115*355d6bb5Sswilcox 		/*
2116*355d6bb5Sswilcox 		 * This is not used by the kernel, so it's pretty
2117*355d6bb5Sswilcox 		 * harmless if it's wrong.
2118*355d6bb5Sswilcox 		 */
2119*355d6bb5Sswilcox 		cgp->cg_niblk = sblock.fs_ipg;
2120*355d6bb5Sswilcox 	}
2121*355d6bb5Sswilcox 
2122*355d6bb5Sswilcox 	if (cgp->cg_ndblk != ndblk) {
2123*355d6bb5Sswilcox 		cgp->cg_ndblk = ndblk;
2124*355d6bb5Sswilcox 	}
2125*355d6bb5Sswilcox 
2126*355d6bb5Sswilcox 	/*
2127*355d6bb5Sswilcox 	 * For the rotors, any position's valid, so pick the one we know
2128*355d6bb5Sswilcox 	 * will always exist.
2129*355d6bb5Sswilcox 	 */
2130*355d6bb5Sswilcox 	if ((cgp->cg_rotor < 0) || (cgp->cg_rotor >= cgp->cg_ndblk)) {
2131*355d6bb5Sswilcox 		cgp->cg_rotor = 0;
2132*355d6bb5Sswilcox 	}
2133*355d6bb5Sswilcox 
2134*355d6bb5Sswilcox 	if ((cgp->cg_frotor < 0) || (cgp->cg_frotor >= cgp->cg_ndblk)) {
2135*355d6bb5Sswilcox 		cgp->cg_frotor = 0;
2136*355d6bb5Sswilcox 	}
2137*355d6bb5Sswilcox 
2138*355d6bb5Sswilcox 	if ((cgp->cg_irotor < 0) || (cgp->cg_irotor >= sblock.fs_ipg)) {
2139*355d6bb5Sswilcox 		cgp->cg_irotor = 0;
2140*355d6bb5Sswilcox 	}
2141*355d6bb5Sswilcox 
2142*355d6bb5Sswilcox 	/*
2143*355d6bb5Sswilcox 	 * For btotoff and boff, if they're misaligned they won't
2144*355d6bb5Sswilcox 	 * match the expected values, so we're catching both cases
2145*355d6bb5Sswilcox 	 * here.  Of course, if any of these are off, it seems likely
2146*355d6bb5Sswilcox 	 * that the tables really won't be where we calculate they
2147*355d6bb5Sswilcox 	 * should be anyway.
2148*355d6bb5Sswilcox 	 */
2149*355d6bb5Sswilcox 	if (cgp->cg_btotoff != exp_btotoff) {
2150*355d6bb5Sswilcox 		cgp->cg_btotoff = exp_btotoff;
2151*355d6bb5Sswilcox 	}
2152*355d6bb5Sswilcox 
2153*355d6bb5Sswilcox 	if (cgp->cg_boff != exp_boff) {
2154*355d6bb5Sswilcox 		cgp->cg_boff = exp_boff;
2155*355d6bb5Sswilcox 	}
2156*355d6bb5Sswilcox 
2157*355d6bb5Sswilcox 	if (cgp->cg_iusedoff != exp_iusedoff) {
2158*355d6bb5Sswilcox 		cgp->cg_iusedoff = exp_iusedoff;
2159*355d6bb5Sswilcox 	}
2160*355d6bb5Sswilcox 
2161*355d6bb5Sswilcox 	if (cgp->cg_freeoff != exp_freeoff) {
2162*355d6bb5Sswilcox 		cgp->cg_freeoff = exp_freeoff;
2163*355d6bb5Sswilcox 	}
2164*355d6bb5Sswilcox 
2165*355d6bb5Sswilcox 	if (cgp->cg_nextfreeoff != exp_nextfreeoff) {
2166*355d6bb5Sswilcox 		cgp->cg_nextfreeoff = exp_nextfreeoff;
2167*355d6bb5Sswilcox 	}
2168*355d6bb5Sswilcox 
2169*355d6bb5Sswilcox 	/*
2170*355d6bb5Sswilcox 	 * We know there was at least one correctable problem,
2171*355d6bb5Sswilcox 	 * or else we wouldn't have been called.  So instead of
2172*355d6bb5Sswilcox 	 * marking the buffer dirty N times above, just do it
2173*355d6bb5Sswilcox 	 * once here.
2174*355d6bb5Sswilcox 	 */
2175*355d6bb5Sswilcox 	cgdirty();
2176*355d6bb5Sswilcox }
2177*355d6bb5Sswilcox 
2178*355d6bb5Sswilcox void
2179*355d6bb5Sswilcox examinelog(daddr32_t start, void (*cb)(daddr32_t))
2180*355d6bb5Sswilcox {
2181*355d6bb5Sswilcox 	struct bufarea *bp;
2182*355d6bb5Sswilcox 	extent_block_t *ebp;
2183*355d6bb5Sswilcox 	extent_t *ep;
2184*355d6bb5Sswilcox 	daddr32_t nfno, fno;
2185*355d6bb5Sswilcox 	int i;
2186*355d6bb5Sswilcox 	int j;
2187*355d6bb5Sswilcox 
2188*355d6bb5Sswilcox 	if (start < SBLOCK)
2189*355d6bb5Sswilcox 		return;
2190*355d6bb5Sswilcox 
2191*355d6bb5Sswilcox 	/*
2192*355d6bb5Sswilcox 	 * Read errors will return zeros, which will cause us
2193*355d6bb5Sswilcox 	 * to do nothing harmful, so don't need to handle it.
2194*355d6bb5Sswilcox 	 */
2195*355d6bb5Sswilcox 	bp = getdatablk(logbtofrag(&sblock, sblock.fs_logbno),
2196*355d6bb5Sswilcox 			(size_t)sblock.fs_bsize);
2197*355d6bb5Sswilcox 	ebp = (void *)bp->b_un.b_buf;
2198*355d6bb5Sswilcox 
2199*355d6bb5Sswilcox 	/*
2200*355d6bb5Sswilcox 	 * Does it look like a log allocation table?
2201*355d6bb5Sswilcox 	 */
2202*355d6bb5Sswilcox 	/* LINTED pointer cast is aligned */
2203*355d6bb5Sswilcox 	if (!log_checksum(&ebp->chksum, (int32_t *)bp->b_un.b_buf,
2204*355d6bb5Sswilcox 	    sblock.fs_bsize))
2205*355d6bb5Sswilcox 		return;
2206*355d6bb5Sswilcox 	if (ebp->type != LUFS_EXTENTS || ebp->nextents == 0)
2207*355d6bb5Sswilcox 		return;
2208*355d6bb5Sswilcox 
2209*355d6bb5Sswilcox 	ep = &ebp->extents[0];
2210*355d6bb5Sswilcox 	for (i = 0; i < ebp->nextents; ++i, ++ep) {
2211*355d6bb5Sswilcox 		fno = logbtofrag(&sblock, ep->pbno);
2212*355d6bb5Sswilcox 		nfno = dbtofsb(&sblock, ep->nbno);
2213*355d6bb5Sswilcox 		for (j = 0; j < nfno; ++j, ++fno) {
2214*355d6bb5Sswilcox 			/*
2215*355d6bb5Sswilcox 			 * Invoke the callback first, so that pass1 can
2216*355d6bb5Sswilcox 			 * mark the log blocks in-use.  Then, if any
2217*355d6bb5Sswilcox 			 * subsequent pass over the log shows us that a
2218*355d6bb5Sswilcox 			 * block got freed (say, it was also claimed by
2219*355d6bb5Sswilcox 			 * an inode that we cleared), we can safely declare
2220*355d6bb5Sswilcox 			 * the log bad.
2221*355d6bb5Sswilcox 			 */
2222*355d6bb5Sswilcox 			if (cb != NULL)
2223*355d6bb5Sswilcox 				(*cb)(fno);
2224*355d6bb5Sswilcox 			if (!testbmap(fno))
2225*355d6bb5Sswilcox 				islogok = 0;
2226*355d6bb5Sswilcox 		}
2227*355d6bb5Sswilcox 	}
2228*355d6bb5Sswilcox 	brelse(bp);
2229*355d6bb5Sswilcox 
2230*355d6bb5Sswilcox 	if (cb != NULL) {
2231*355d6bb5Sswilcox 		fno = logbtofrag(&sblock, sblock.fs_logbno);
2232*355d6bb5Sswilcox 		for (j = 0; j < sblock.fs_frag; ++j, ++fno)
2233*355d6bb5Sswilcox 			(*cb)(fno);
2234*355d6bb5Sswilcox 	}
2235*355d6bb5Sswilcox }
2236*355d6bb5Sswilcox 
2237*355d6bb5Sswilcox static void
2238*355d6bb5Sswilcox freelogblk(daddr32_t frag)
2239*355d6bb5Sswilcox {
2240*355d6bb5Sswilcox 	freeblk(sblock.fs_logbno, frag, 1);
2241*355d6bb5Sswilcox }
2242*355d6bb5Sswilcox 
2243*355d6bb5Sswilcox caddr_t
2244*355d6bb5Sswilcox file_id(fsck_ino_t inum, mode_t mode)
2245*355d6bb5Sswilcox {
2246*355d6bb5Sswilcox 	static char name[MAXPATHLEN + 1];
2247*355d6bb5Sswilcox 
2248*355d6bb5Sswilcox 	if (lfdir == inum) {
2249*355d6bb5Sswilcox 		return (lfname);
2250*355d6bb5Sswilcox 	}
2251*355d6bb5Sswilcox 
2252*355d6bb5Sswilcox 	if ((mode & IFMT) == IFDIR) {
2253*355d6bb5Sswilcox 		(void) strcpy(name, "DIR");
2254*355d6bb5Sswilcox 	} else if ((mode & IFMT) == IFATTRDIR) {
2255*355d6bb5Sswilcox 		(void) strcpy(name, "ATTR DIR");
2256*355d6bb5Sswilcox 	} else if ((mode & IFMT) == IFSHAD) {
2257*355d6bb5Sswilcox 		(void) strcpy(name, "ACL");
2258*355d6bb5Sswilcox 	} else {
2259*355d6bb5Sswilcox 		(void) strcpy(name, "FILE");
2260*355d6bb5Sswilcox 	}
2261*355d6bb5Sswilcox 
2262*355d6bb5Sswilcox 	return (name);
2263*355d6bb5Sswilcox }
2264*355d6bb5Sswilcox 
2265*355d6bb5Sswilcox /*
2266*355d6bb5Sswilcox  * Simple initializer for inodesc structures, so users of only a few
2267*355d6bb5Sswilcox  * fields don't have to worry about getting the right defaults for
2268*355d6bb5Sswilcox  * everything out.
2269*355d6bb5Sswilcox  */
2270*355d6bb5Sswilcox void
2271*355d6bb5Sswilcox init_inodesc(struct inodesc *idesc)
2272*355d6bb5Sswilcox {
2273*355d6bb5Sswilcox 	/*
2274*355d6bb5Sswilcox 	 * Most fields should be zero, just hit the special cases.
2275*355d6bb5Sswilcox 	 */
2276*355d6bb5Sswilcox 	(void) memset((void *)idesc, 0, sizeof (struct inodesc));
2277*355d6bb5Sswilcox 	idesc->id_fix = DONTKNOW;
2278*355d6bb5Sswilcox 	idesc->id_lbn = -1;
2279*355d6bb5Sswilcox 	idesc->id_truncto = -1;
2280*355d6bb5Sswilcox 	idesc->id_firsthole = -1;
2281*355d6bb5Sswilcox }
2282*355d6bb5Sswilcox 
2283*355d6bb5Sswilcox /*
2284*355d6bb5Sswilcox  * Compare routine for tsearch(C) to use on ino_t instances.
2285*355d6bb5Sswilcox  */
2286*355d6bb5Sswilcox int
2287*355d6bb5Sswilcox ino_t_cmp(const void *left, const void *right)
2288*355d6bb5Sswilcox {
2289*355d6bb5Sswilcox 	const fsck_ino_t lino = (const fsck_ino_t)left;
2290*355d6bb5Sswilcox 	const fsck_ino_t rino = (const fsck_ino_t)right;
2291*355d6bb5Sswilcox 
2292*355d6bb5Sswilcox 	return (lino - rino);
2293*355d6bb5Sswilcox }
2294*355d6bb5Sswilcox 
2295*355d6bb5Sswilcox int
2296*355d6bb5Sswilcox cgisdirty(void)
2297*355d6bb5Sswilcox {
2298*355d6bb5Sswilcox 	return (cgblk.b_dirty);
2299*355d6bb5Sswilcox }
2300*355d6bb5Sswilcox 
2301*355d6bb5Sswilcox void
2302*355d6bb5Sswilcox cgflush(void)
2303*355d6bb5Sswilcox {
2304*355d6bb5Sswilcox 	flush(fswritefd, &cgblk);
2305*355d6bb5Sswilcox }
2306*355d6bb5Sswilcox 
2307*355d6bb5Sswilcox void
2308*355d6bb5Sswilcox dirty(struct bufarea *bp)
2309*355d6bb5Sswilcox {
2310*355d6bb5Sswilcox 	if (fswritefd < 0) {
2311*355d6bb5Sswilcox 		pfatal("SETTING DIRTY FLAG IN READ_ONLY MODE\n");
2312*355d6bb5Sswilcox 	} else {
2313*355d6bb5Sswilcox 		(bp)->b_dirty = 1;
2314*355d6bb5Sswilcox 		isdirty = 1;
2315*355d6bb5Sswilcox 	}
2316*355d6bb5Sswilcox }
2317*355d6bb5Sswilcox 
2318*355d6bb5Sswilcox void
2319*355d6bb5Sswilcox initbarea(struct bufarea *bp)
2320*355d6bb5Sswilcox {
2321*355d6bb5Sswilcox 	(bp)->b_dirty = 0;
2322*355d6bb5Sswilcox 	(bp)->b_bno = (diskaddr_t)-1LL;
2323*355d6bb5Sswilcox 	(bp)->b_flags = 0;
2324*355d6bb5Sswilcox 	(bp)->b_cnt = 0;
2325*355d6bb5Sswilcox 	(bp)->b_errs = 0;
2326*355d6bb5Sswilcox }
2327*355d6bb5Sswilcox 
2328*355d6bb5Sswilcox /*
2329*355d6bb5Sswilcox  * Partition-sizing routines adapted from ../newfs/newfs.c.
2330*355d6bb5Sswilcox  * Needed because calcsb() needs to use mkfs to work out what the
2331*355d6bb5Sswilcox  * superblock should be, and mkfs insists on being told how many
2332*355d6bb5Sswilcox  * sectors to use.
2333*355d6bb5Sswilcox  *
2334*355d6bb5Sswilcox  * Error handling assumes we're never called while preening.
2335*355d6bb5Sswilcox  *
2336*355d6bb5Sswilcox  * XXX This should be extracted into a ../ufslib.{c,h},
2337*355d6bb5Sswilcox  *     in the same spirit to ../../fslib.{c,h}.  Once that is
2338*355d6bb5Sswilcox  *     done, both fsck and newfs should be modified to link
2339*355d6bb5Sswilcox  *     against it.
2340*355d6bb5Sswilcox  */
2341*355d6bb5Sswilcox 
2342*355d6bb5Sswilcox static int label_type;
2343*355d6bb5Sswilcox 
2344*355d6bb5Sswilcox #define	LABEL_TYPE_VTOC		1
2345*355d6bb5Sswilcox #define	LABEL_TYPE_EFI		2
2346*355d6bb5Sswilcox #define	LABEL_TYPE_OTHER	3
2347*355d6bb5Sswilcox 
2348*355d6bb5Sswilcox #define	MB			(1024 * 1024)
2349*355d6bb5Sswilcox #define	SECTORS_PER_TERABYTE	(1LL << 31)
2350*355d6bb5Sswilcox #define	FS_SIZE_UPPER_LIMIT	0x100000000000LL
2351*355d6bb5Sswilcox 
2352*355d6bb5Sswilcox diskaddr_t
2353*355d6bb5Sswilcox getdisksize(caddr_t disk, int fd)
2354*355d6bb5Sswilcox {
2355*355d6bb5Sswilcox 	int rpm;
2356*355d6bb5Sswilcox 	struct dk_geom g;
2357*355d6bb5Sswilcox 	struct dk_cinfo ci;
2358*355d6bb5Sswilcox 	diskaddr_t actual_size;
2359*355d6bb5Sswilcox 
2360*355d6bb5Sswilcox 	/*
2361*355d6bb5Sswilcox 	 * get_device_size() determines the actual size of the
2362*355d6bb5Sswilcox 	 * device, and also the disk's attributes, such as geometry.
2363*355d6bb5Sswilcox 	 */
2364*355d6bb5Sswilcox 	actual_size = get_device_size(fd, disk);
2365*355d6bb5Sswilcox 
2366*355d6bb5Sswilcox 	if (label_type == LABEL_TYPE_VTOC) {
2367*355d6bb5Sswilcox 		if (ioctl(fd, DKIOCGGEOM, &g)) {
2368*355d6bb5Sswilcox 			pwarn("%s: Unable to read Disk geometry", disk);
2369*355d6bb5Sswilcox 			return (0);
2370*355d6bb5Sswilcox 		}
2371*355d6bb5Sswilcox 		if (sblock.fs_nsect == 0)
2372*355d6bb5Sswilcox 			sblock.fs_nsect = g.dkg_nsect;
2373*355d6bb5Sswilcox 		if (sblock.fs_ntrak == 0)
2374*355d6bb5Sswilcox 			sblock.fs_ntrak = g.dkg_nhead;
2375*355d6bb5Sswilcox 		if (sblock.fs_rps == 0) {
2376*355d6bb5Sswilcox 			rpm = ((int)g.dkg_rpm <= 0) ? 3600: g.dkg_rpm;
2377*355d6bb5Sswilcox 			sblock.fs_rps = rpm / 60;
2378*355d6bb5Sswilcox 		}
2379*355d6bb5Sswilcox 	}
2380*355d6bb5Sswilcox 
2381*355d6bb5Sswilcox 	if (sblock.fs_bsize == 0)
2382*355d6bb5Sswilcox 		sblock.fs_bsize = MAXBSIZE;
2383*355d6bb5Sswilcox 
2384*355d6bb5Sswilcox 	/*
2385*355d6bb5Sswilcox 	 * Adjust maxcontig by the device's maxtransfer. If maxtransfer
2386*355d6bb5Sswilcox 	 * information is not available, default to the min of a MB and
2387*355d6bb5Sswilcox 	 * maxphys.
2388*355d6bb5Sswilcox 	 */
2389*355d6bb5Sswilcox 	if (sblock.fs_maxcontig == -1 && ioctl(fd, DKIOCINFO, &ci) == 0) {
2390*355d6bb5Sswilcox 		sblock.fs_maxcontig = ci.dki_maxtransfer * DEV_BSIZE;
2391*355d6bb5Sswilcox 		if (sblock.fs_maxcontig < 0) {
2392*355d6bb5Sswilcox 			int gotit, maxphys;
2393*355d6bb5Sswilcox 
2394*355d6bb5Sswilcox 			gotit = fsgetmaxphys(&maxphys, NULL);
2395*355d6bb5Sswilcox 
2396*355d6bb5Sswilcox 			/*
2397*355d6bb5Sswilcox 			 * If we cannot get the maxphys value, default
2398*355d6bb5Sswilcox 			 * to ufs_maxmaxphys (MB).
2399*355d6bb5Sswilcox 			 */
2400*355d6bb5Sswilcox 			if (gotit) {
2401*355d6bb5Sswilcox 				sblock.fs_maxcontig = MIN(maxphys, MB);
2402*355d6bb5Sswilcox 			} else {
2403*355d6bb5Sswilcox 				sblock.fs_maxcontig = MB;
2404*355d6bb5Sswilcox 			}
2405*355d6bb5Sswilcox 		}
2406*355d6bb5Sswilcox 		sblock.fs_maxcontig /= sblock.fs_bsize;
2407*355d6bb5Sswilcox 	}
2408*355d6bb5Sswilcox 
2409*355d6bb5Sswilcox 	return (actual_size);
2410*355d6bb5Sswilcox }
2411*355d6bb5Sswilcox 
2412*355d6bb5Sswilcox /*
2413*355d6bb5Sswilcox  * Figure out how big the partition we're dealing with is.
2414*355d6bb5Sswilcox  */
2415*355d6bb5Sswilcox static diskaddr_t
2416*355d6bb5Sswilcox get_device_size(int fd, caddr_t name)
2417*355d6bb5Sswilcox {
2418*355d6bb5Sswilcox 	struct vtoc vtoc;
2419*355d6bb5Sswilcox 	struct dk_gpt *efi_vtoc;
2420*355d6bb5Sswilcox 	diskaddr_t slicesize = 0;
2421*355d6bb5Sswilcox 
2422*355d6bb5Sswilcox 	int index = read_vtoc(fd, &vtoc);
2423*355d6bb5Sswilcox 
2424*355d6bb5Sswilcox 	if (index >= 0) {
2425*355d6bb5Sswilcox 		label_type = LABEL_TYPE_VTOC;
2426*355d6bb5Sswilcox 	} else {
2427*355d6bb5Sswilcox 		if (index == VT_ENOTSUP || index == VT_ERROR) {
2428*355d6bb5Sswilcox 			/* it might be an EFI label */
2429*355d6bb5Sswilcox 			index = efi_alloc_and_read(fd, &efi_vtoc);
2430*355d6bb5Sswilcox 			if (index >= 0)
2431*355d6bb5Sswilcox 				label_type = LABEL_TYPE_EFI;
2432*355d6bb5Sswilcox 		}
2433*355d6bb5Sswilcox 	}
2434*355d6bb5Sswilcox 
2435*355d6bb5Sswilcox 	if (index < 0) {
2436*355d6bb5Sswilcox 		/*
2437*355d6bb5Sswilcox 		 * Since both attempts to read the label failed, we're
2438*355d6bb5Sswilcox 		 * going to fall back to a brute force approach to
2439*355d6bb5Sswilcox 		 * determining the device's size:  see how far out we can
2440*355d6bb5Sswilcox 		 * perform reads on the device.
2441*355d6bb5Sswilcox 		 */
2442*355d6bb5Sswilcox 
2443*355d6bb5Sswilcox 		slicesize = brute_force_get_device_size(fd);
2444*355d6bb5Sswilcox 		if (slicesize == 0) {
2445*355d6bb5Sswilcox 			switch (index) {
2446*355d6bb5Sswilcox 			case VT_ERROR:
2447*355d6bb5Sswilcox 				pwarn("%s: %s\n", name, strerror(errno));
2448*355d6bb5Sswilcox 				break;
2449*355d6bb5Sswilcox 			case VT_EIO:
2450*355d6bb5Sswilcox 				pwarn("%s: I/O error accessing VTOC", name);
2451*355d6bb5Sswilcox 				break;
2452*355d6bb5Sswilcox 			case VT_EINVAL:
2453*355d6bb5Sswilcox 				pwarn("%s: Invalid field in VTOC", name);
2454*355d6bb5Sswilcox 				break;
2455*355d6bb5Sswilcox 			default:
2456*355d6bb5Sswilcox 				pwarn("%s: unknown error %d accessing VTOC",
2457*355d6bb5Sswilcox 				    name, index);
2458*355d6bb5Sswilcox 				break;
2459*355d6bb5Sswilcox 			}
2460*355d6bb5Sswilcox 			return (0);
2461*355d6bb5Sswilcox 		} else {
2462*355d6bb5Sswilcox 			label_type = LABEL_TYPE_OTHER;
2463*355d6bb5Sswilcox 		}
2464*355d6bb5Sswilcox 	}
2465*355d6bb5Sswilcox 
2466*355d6bb5Sswilcox 	if (label_type == LABEL_TYPE_EFI) {
2467*355d6bb5Sswilcox 		slicesize = efi_vtoc->efi_parts[index].p_size;
2468*355d6bb5Sswilcox 		efi_free(efi_vtoc);
2469*355d6bb5Sswilcox 	} else if (label_type == LABEL_TYPE_VTOC) {
2470*355d6bb5Sswilcox 		/*
2471*355d6bb5Sswilcox 		 * In the vtoc struct, p_size is a 32-bit signed quantity.
2472*355d6bb5Sswilcox 		 * In the dk_gpt struct (efi's version of the vtoc), p_size
2473*355d6bb5Sswilcox 		 * is an unsigned 64-bit quantity.  By casting the vtoc's
2474*355d6bb5Sswilcox 		 * psize to an unsigned 32-bit quantity, it will be copied
2475*355d6bb5Sswilcox 		 * to 'slicesize' (an unsigned 64-bit diskaddr_t) without
2476*355d6bb5Sswilcox 		 * sign extension.
2477*355d6bb5Sswilcox 		 */
2478*355d6bb5Sswilcox 
2479*355d6bb5Sswilcox 		slicesize = (uint32_t)vtoc.v_part[index].p_size;
2480*355d6bb5Sswilcox 	}
2481*355d6bb5Sswilcox 
2482*355d6bb5Sswilcox 	return (slicesize);
2483*355d6bb5Sswilcox }
2484*355d6bb5Sswilcox 
2485*355d6bb5Sswilcox /*
2486*355d6bb5Sswilcox  * brute_force_get_device_size
2487*355d6bb5Sswilcox  *
2488*355d6bb5Sswilcox  * Determine the size of the device by seeing how far we can
2489*355d6bb5Sswilcox  * read.  Doing an llseek( , , SEEK_END) would probably work
2490*355d6bb5Sswilcox  * in most cases, but we've seen at least one third-party driver
2491*355d6bb5Sswilcox  * which doesn't correctly support the SEEK_END option when the
2492*355d6bb5Sswilcox  * the device is greater than a terabyte.
2493*355d6bb5Sswilcox  */
2494*355d6bb5Sswilcox 
2495*355d6bb5Sswilcox static diskaddr_t
2496*355d6bb5Sswilcox brute_force_get_device_size(int fd)
2497*355d6bb5Sswilcox {
2498*355d6bb5Sswilcox 	diskaddr_t	min_fail = 0;
2499*355d6bb5Sswilcox 	diskaddr_t	max_succeed = 0;
2500*355d6bb5Sswilcox 	diskaddr_t	cur_db_off;
2501*355d6bb5Sswilcox 	char 		buf[DEV_BSIZE];
2502*355d6bb5Sswilcox 
2503*355d6bb5Sswilcox 	/*
2504*355d6bb5Sswilcox 	 * First, see if we can read the device at all, just to
2505*355d6bb5Sswilcox 	 * eliminate errors that have nothing to do with the
2506*355d6bb5Sswilcox 	 * device's size.
2507*355d6bb5Sswilcox 	 */
2508*355d6bb5Sswilcox 
2509*355d6bb5Sswilcox 	if (((llseek(fd, (offset_t)0, SEEK_SET)) == -1) ||
2510*355d6bb5Sswilcox 	    ((read(fd, buf, DEV_BSIZE)) == -1))
2511*355d6bb5Sswilcox 		return (0);  /* can't determine size */
2512*355d6bb5Sswilcox 
2513*355d6bb5Sswilcox 	/*
2514*355d6bb5Sswilcox 	 * Now, go sequentially through the multiples of 4TB
2515*355d6bb5Sswilcox 	 * to find the first read that fails (this isn't strictly
2516*355d6bb5Sswilcox 	 * the most efficient way to find the actual size if the
2517*355d6bb5Sswilcox 	 * size really could be anything between 0 and 2**64 bytes.
2518*355d6bb5Sswilcox 	 * We expect the sizes to be less than 16 TB for some time,
2519*355d6bb5Sswilcox 	 * so why do a bunch of reads that are larger than that?
2520*355d6bb5Sswilcox 	 * However, this algorithm *will* work for sizes of greater
2521*355d6bb5Sswilcox 	 * than 16 TB.  We're just not optimizing for those sizes.)
2522*355d6bb5Sswilcox 	 */
2523*355d6bb5Sswilcox 
2524*355d6bb5Sswilcox 	/*
2525*355d6bb5Sswilcox 	 * XXX lint uses 32-bit arithmetic for doing flow analysis.
2526*355d6bb5Sswilcox 	 * We're using > 32-bit constants here.  Therefore, its flow
2527*355d6bb5Sswilcox 	 * analysis is wrong.  For the time being, ignore complaints
2528*355d6bb5Sswilcox 	 * from it about the body of the for() being unreached.
2529*355d6bb5Sswilcox 	 */
2530*355d6bb5Sswilcox 	for (cur_db_off = SECTORS_PER_TERABYTE * 4;
2531*355d6bb5Sswilcox 	    (min_fail == 0) && (cur_db_off < FS_SIZE_UPPER_LIMIT);
2532*355d6bb5Sswilcox 	    cur_db_off += 4 * SECTORS_PER_TERABYTE) {
2533*355d6bb5Sswilcox 		if ((llseek(fd, (offset_t)(cur_db_off * DEV_BSIZE),
2534*355d6bb5Sswilcox 		    SEEK_SET) == -1) ||
2535*355d6bb5Sswilcox 		    (read(fd, buf, DEV_BSIZE) != DEV_BSIZE))
2536*355d6bb5Sswilcox 			min_fail = cur_db_off;
2537*355d6bb5Sswilcox 		else
2538*355d6bb5Sswilcox 			max_succeed = cur_db_off;
2539*355d6bb5Sswilcox 	}
2540*355d6bb5Sswilcox 
2541*355d6bb5Sswilcox 	/*
2542*355d6bb5Sswilcox 	 * XXX Same lint flow analysis problem as above.
2543*355d6bb5Sswilcox 	 */
2544*355d6bb5Sswilcox 	if (min_fail == 0)
2545*355d6bb5Sswilcox 		return (0);
2546*355d6bb5Sswilcox 
2547*355d6bb5Sswilcox 	/*
2548*355d6bb5Sswilcox 	 * We now know that the size of the device is less than
2549*355d6bb5Sswilcox 	 * min_fail and greater than or equal to max_succeed.  Now
2550*355d6bb5Sswilcox 	 * keep splitting the difference until the actual size in
2551*355d6bb5Sswilcox 	 * sectors in known.  We also know that the difference
2552*355d6bb5Sswilcox 	 * between max_succeed and min_fail at this time is
2553*355d6bb5Sswilcox 	 * 4 * SECTORS_PER_TERABYTE, which is a power of two, which
2554*355d6bb5Sswilcox 	 * simplifies the math below.
2555*355d6bb5Sswilcox 	 */
2556*355d6bb5Sswilcox 
2557*355d6bb5Sswilcox 	while (min_fail - max_succeed > 1) {
2558*355d6bb5Sswilcox 		cur_db_off = max_succeed + (min_fail - max_succeed)/2;
2559*355d6bb5Sswilcox 		if (((llseek(fd, (offset_t)(cur_db_off * DEV_BSIZE),
2560*355d6bb5Sswilcox 		    SEEK_SET)) == -1) ||
2561*355d6bb5Sswilcox 		    ((read(fd, buf, DEV_BSIZE)) != DEV_BSIZE))
2562*355d6bb5Sswilcox 			min_fail = cur_db_off;
2563*355d6bb5Sswilcox 		else
2564*355d6bb5Sswilcox 			max_succeed = cur_db_off;
2565*355d6bb5Sswilcox 	}
2566*355d6bb5Sswilcox 
2567*355d6bb5Sswilcox 	/* the size is the last successfully read sector offset plus one */
2568*355d6bb5Sswilcox 	return (max_succeed + 1);
2569*355d6bb5Sswilcox }
2570*355d6bb5Sswilcox 
2571*355d6bb5Sswilcox static void
2572*355d6bb5Sswilcox vfileerror(fsck_ino_t cwd, fsck_ino_t ino, caddr_t fmt, va_list ap)
2573*355d6bb5Sswilcox {
2574*355d6bb5Sswilcox 	struct dinode *dp;
2575*355d6bb5Sswilcox 	char pathbuf[MAXPATHLEN + 1];
2576*355d6bb5Sswilcox 
2577*355d6bb5Sswilcox 	vpwarn(fmt, ap);
2578*355d6bb5Sswilcox 	(void) putchar(' ');
2579*355d6bb5Sswilcox 	pinode(ino);
2580*355d6bb5Sswilcox 	(void) printf("\n");
2581*355d6bb5Sswilcox 	getpathname(pathbuf, cwd, ino);
2582*355d6bb5Sswilcox 	if (ino < UFSROOTINO || ino > maxino) {
2583*355d6bb5Sswilcox 		pfatal("NAME=%s\n", pathbuf);
2584*355d6bb5Sswilcox 		return;
2585*355d6bb5Sswilcox 	}
2586*355d6bb5Sswilcox 	dp = ginode(ino);
2587*355d6bb5Sswilcox 	if (ftypeok(dp))
2588*355d6bb5Sswilcox 		pfatal("%s=%s\n", file_id(ino, dp->di_mode), pathbuf);
2589*355d6bb5Sswilcox 	else
2590*355d6bb5Sswilcox 		pfatal("NAME=%s\n", pathbuf);
2591*355d6bb5Sswilcox }
2592*355d6bb5Sswilcox 
2593*355d6bb5Sswilcox void
2594*355d6bb5Sswilcox direrror(fsck_ino_t ino, caddr_t fmt, ...)
2595*355d6bb5Sswilcox {
2596*355d6bb5Sswilcox 	va_list ap;
2597*355d6bb5Sswilcox 
2598*355d6bb5Sswilcox 	va_start(ap, fmt);
2599*355d6bb5Sswilcox 	vfileerror(ino, ino, fmt, ap);
2600*355d6bb5Sswilcox 	va_end(ap);
2601*355d6bb5Sswilcox }
2602*355d6bb5Sswilcox 
2603*355d6bb5Sswilcox static void
2604*355d6bb5Sswilcox vdirerror(fsck_ino_t ino, caddr_t fmt, va_list ap)
2605*355d6bb5Sswilcox {
2606*355d6bb5Sswilcox 	vfileerror(ino, ino, fmt, ap);
2607*355d6bb5Sswilcox }
2608*355d6bb5Sswilcox 
2609*355d6bb5Sswilcox void
2610*355d6bb5Sswilcox fileerror(fsck_ino_t cwd, fsck_ino_t ino, caddr_t fmt, ...)
2611*355d6bb5Sswilcox {
2612*355d6bb5Sswilcox 	va_list ap;
2613*355d6bb5Sswilcox 
2614*355d6bb5Sswilcox 	va_start(ap, fmt);
2615*355d6bb5Sswilcox 	vfileerror(cwd, ino, fmt, ap);
2616*355d6bb5Sswilcox 	va_end(ap);
2617*355d6bb5Sswilcox }
2618*355d6bb5Sswilcox 
2619*355d6bb5Sswilcox /*
2620*355d6bb5Sswilcox  * Adds the given inode to the orphaned-directories list, limbo_dirs.
2621*355d6bb5Sswilcox  * Assumes that the caller has set INCLEAR in the inode's statemap[]
2622*355d6bb5Sswilcox  * entry.
2623*355d6bb5Sswilcox  *
2624*355d6bb5Sswilcox  * With INCLEAR set, the inode will get ignored by passes 2 and 3,
2625*355d6bb5Sswilcox  * meaning it's effectively an orphan.  It needs to be noted now, so
2626*355d6bb5Sswilcox  * it will be remembered in pass 4.
2627*355d6bb5Sswilcox  */
2628*355d6bb5Sswilcox 
2629*355d6bb5Sswilcox void
2630*355d6bb5Sswilcox add_orphan_dir(fsck_ino_t ino)
2631*355d6bb5Sswilcox {
2632*355d6bb5Sswilcox 	if (tsearch((void *)ino, &limbo_dirs, ino_t_cmp) == NULL)
2633*355d6bb5Sswilcox 		errexit("add_orphan_dir: out of memory");
2634*355d6bb5Sswilcox }
2635*355d6bb5Sswilcox 
2636*355d6bb5Sswilcox /*
2637*355d6bb5Sswilcox  * Remove an inode from the orphaned-directories list, presumably
2638*355d6bb5Sswilcox  * because it's been cleared.
2639*355d6bb5Sswilcox  */
2640*355d6bb5Sswilcox void
2641*355d6bb5Sswilcox remove_orphan_dir(fsck_ino_t ino)
2642*355d6bb5Sswilcox {
2643*355d6bb5Sswilcox 	(void) tdelete((void *)ino, &limbo_dirs, ino_t_cmp);
2644*355d6bb5Sswilcox }
2645*355d6bb5Sswilcox 
2646*355d6bb5Sswilcox /*
2647*355d6bb5Sswilcox  * log_setsum() and log_checksum() are equivalent to lufs.c:setsum()
2648*355d6bb5Sswilcox  * and lufs.c:checksum().
2649*355d6bb5Sswilcox  */
2650*355d6bb5Sswilcox static void
2651*355d6bb5Sswilcox log_setsum(int32_t *sp, int32_t *lp, int nb)
2652*355d6bb5Sswilcox {
2653*355d6bb5Sswilcox 	int32_t csum = 0;
2654*355d6bb5Sswilcox 
2655*355d6bb5Sswilcox 	*sp = 0;
2656*355d6bb5Sswilcox 	nb /= sizeof (int32_t);
2657*355d6bb5Sswilcox 	while (nb--)
2658*355d6bb5Sswilcox 		csum += *lp++;
2659*355d6bb5Sswilcox 	*sp = csum;
2660*355d6bb5Sswilcox }
2661*355d6bb5Sswilcox 
2662*355d6bb5Sswilcox static int
2663*355d6bb5Sswilcox log_checksum(int32_t *sp, int32_t *lp, int nb)
2664*355d6bb5Sswilcox {
2665*355d6bb5Sswilcox 	int32_t ssum = *sp;
2666*355d6bb5Sswilcox 
2667*355d6bb5Sswilcox 	log_setsum(sp, lp, nb);
2668*355d6bb5Sswilcox 	if (ssum != *sp) {
2669*355d6bb5Sswilcox 		*sp = ssum;
2670*355d6bb5Sswilcox 		return (0);
2671*355d6bb5Sswilcox 	}
2672*355d6bb5Sswilcox 	return (1);
26737c478bd9Sstevel@tonic-gate }
2674