xref: /illumos-gate/usr/src/cmd/fs.d/ufs/fsck/pass1.c (revision ce37393a)
17c478bd9Sstevel@tonic-gate /*
2*ce37393aSowenr  * Copyright 2008 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 
30355d6bb5Sswilcox #include <stdio.h>
31355d6bb5Sswilcox #include <stdlib.h>
32355d6bb5Sswilcox #include <unistd.h>
33355d6bb5Sswilcox #include <string.h>
347c478bd9Sstevel@tonic-gate #include <sys/param.h>
357c478bd9Sstevel@tonic-gate #include <sys/types.h>
367c478bd9Sstevel@tonic-gate #include <sys/mntent.h>
377c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_fs.h>
387c478bd9Sstevel@tonic-gate #include <sys/vnode.h>
39355d6bb5Sswilcox #define	_KERNEL
40355d6bb5Sswilcox #include <sys/fs/ufs_fsdir.h>
41355d6bb5Sswilcox #undef _KERNEL
427c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_inode.h>
437c478bd9Sstevel@tonic-gate #include "fsck.h"
447c478bd9Sstevel@tonic-gate 
457c478bd9Sstevel@tonic-gate /*
46355d6bb5Sswilcox  * for each large file (size > MAXOFF_T), the global largefile_count
47355d6bb5Sswilcox  * gets incremented during this pass.
487c478bd9Sstevel@tonic-gate  */
497c478bd9Sstevel@tonic-gate 
50355d6bb5Sswilcox static uint32_t badblk;		/* number seen for the current inode */
51355d6bb5Sswilcox static uint32_t dupblk;		/* number seen for the current inode */
527c478bd9Sstevel@tonic-gate 
53355d6bb5Sswilcox static void clear_attr_acl(fsck_ino_t, fsck_ino_t, char *);
54355d6bb5Sswilcox static void verify_inode(fsck_ino_t, struct inodesc *, fsck_ino_t);
55355d6bb5Sswilcox static void check_dirholes(fsck_ino_t, struct inodesc *);
56355d6bb5Sswilcox static void collapse_dirhole(fsck_ino_t, struct inodesc *);
57355d6bb5Sswilcox static void note_used(daddr32_t);
587c478bd9Sstevel@tonic-gate 
59355d6bb5Sswilcox void
pass1(void)60355d6bb5Sswilcox pass1(void)
617c478bd9Sstevel@tonic-gate {
62355d6bb5Sswilcox 	uint_t c, i;
63355d6bb5Sswilcox 	daddr32_t cgd;
647c478bd9Sstevel@tonic-gate 	struct inodesc idesc;
65355d6bb5Sswilcox 	fsck_ino_t inumber;
66355d6bb5Sswilcox 	fsck_ino_t maxinumber;
677c478bd9Sstevel@tonic-gate 
687c478bd9Sstevel@tonic-gate 	/*
697c478bd9Sstevel@tonic-gate 	 * Set file system reserved blocks in used block map.
707c478bd9Sstevel@tonic-gate 	 */
717c478bd9Sstevel@tonic-gate 	for (c = 0; c < sblock.fs_ncg; c++) {
727c478bd9Sstevel@tonic-gate 		cgd = cgdmin(&sblock, c);
737c478bd9Sstevel@tonic-gate 		if (c == 0) {
74355d6bb5Sswilcox 			/*
75355d6bb5Sswilcox 			 * Doing the first cylinder group, account for
76355d6bb5Sswilcox 			 * the cg summaries as well.
77355d6bb5Sswilcox 			 */
787c478bd9Sstevel@tonic-gate 			i = cgbase(&sblock, c);
797c478bd9Sstevel@tonic-gate 			cgd += howmany(sblock.fs_cssize, sblock.fs_fsize);
80355d6bb5Sswilcox 		} else {
817c478bd9Sstevel@tonic-gate 			i = cgsblock(&sblock, c);
82355d6bb5Sswilcox 		}
83355d6bb5Sswilcox 		for (; i < cgd; i++) {
84355d6bb5Sswilcox 			note_used(i);
85355d6bb5Sswilcox 		}
867c478bd9Sstevel@tonic-gate 	}
877c478bd9Sstevel@tonic-gate 	/*
88355d6bb5Sswilcox 	 * Note blocks being used by the log, so we don't declare
89355d6bb5Sswilcox 	 * them as available and some time in the future we get a
90355d6bb5Sswilcox 	 * freeing free block panic.
917c478bd9Sstevel@tonic-gate 	 */
92355d6bb5Sswilcox 	if (islog && islogok && sblock.fs_logbno)
9339542a18Sabalfour 		examinelog(&note_used);
947c478bd9Sstevel@tonic-gate 
957c478bd9Sstevel@tonic-gate 	/*
96355d6bb5Sswilcox 	 * Find all allocated blocks.  This must be completed before
97355d6bb5Sswilcox 	 * we read the contents of any directories, as dirscan() et al
98355d6bb5Sswilcox 	 * don't want to know about block allocation holes.  So, part
99355d6bb5Sswilcox 	 * of this pass is to truncate any directories with holes to
100355d6bb5Sswilcox 	 * just before those holes, so dirscan() can remain blissfully
101355d6bb5Sswilcox 	 * ignorant.
1027c478bd9Sstevel@tonic-gate 	 */
1037c478bd9Sstevel@tonic-gate 	inumber = 0;
1047c478bd9Sstevel@tonic-gate 	n_files = n_blks = 0;
1057c478bd9Sstevel@tonic-gate 	resetinodebuf();
1067c478bd9Sstevel@tonic-gate 	maxinumber = sblock.fs_ncg * sblock.fs_ipg;
1077c478bd9Sstevel@tonic-gate 	for (c = 0; c < sblock.fs_ncg; c++) {
1087c478bd9Sstevel@tonic-gate 		for (i = 0; i < sblock.fs_ipg; i++, inumber++) {
1097c478bd9Sstevel@tonic-gate 			if (inumber < UFSROOTINO)
1107c478bd9Sstevel@tonic-gate 				continue;
111355d6bb5Sswilcox 			init_inodesc(&idesc);
112355d6bb5Sswilcox 			idesc.id_type = ADDR;
113355d6bb5Sswilcox 			idesc.id_func = pass1check;
114355d6bb5Sswilcox 			verify_inode(inumber, &idesc, maxinumber);
115355d6bb5Sswilcox 		}
116355d6bb5Sswilcox 	}
117355d6bb5Sswilcox 	freeinodebuf();
118355d6bb5Sswilcox }
119355d6bb5Sswilcox 
120355d6bb5Sswilcox /*
121355d6bb5Sswilcox  * Perform checks on an inode and setup/track the state of the inode
122355d6bb5Sswilcox  * in maps (statemap[], lncntp[]) for future reference and validation.
123355d6bb5Sswilcox  * Initiate the calls to ckinode and in turn pass1check() to handle
124355d6bb5Sswilcox  * further validation.
125355d6bb5Sswilcox  */
126355d6bb5Sswilcox static void
verify_inode(fsck_ino_t inumber,struct inodesc * idesc,fsck_ino_t maxinumber)127355d6bb5Sswilcox verify_inode(fsck_ino_t inumber, struct inodesc *idesc, fsck_ino_t maxinumber)
128355d6bb5Sswilcox {
129355d6bb5Sswilcox 	int j, clear, flags;
130355d6bb5Sswilcox 	int isdir;
131355d6bb5Sswilcox 	char *err;
132355d6bb5Sswilcox 	fsck_ino_t shadow, attrinode;
133355d6bb5Sswilcox 	daddr32_t ndb;
134355d6bb5Sswilcox 	struct dinode *dp;
135355d6bb5Sswilcox 	struct inoinfo *iip;
136355d6bb5Sswilcox 
137355d6bb5Sswilcox 	dp = getnextinode(inumber);
138355d6bb5Sswilcox 	if ((dp->di_mode & IFMT) == 0) {
139355d6bb5Sswilcox 		/* mode and type of file is not set */
140355d6bb5Sswilcox 		if ((memcmp((void *)dp->di_db, (void *)zino.di_db,
141*ce37393aSowenr 		    NDADDR * sizeof (daddr32_t)) != 0) ||
142355d6bb5Sswilcox 		    (memcmp((void *)dp->di_ib, (void *)zino.di_ib,
143*ce37393aSowenr 		    NIADDR * sizeof (daddr32_t)) != 0) ||
144355d6bb5Sswilcox 		    (dp->di_mode != 0) || (dp->di_size != 0)) {
145355d6bb5Sswilcox 			pfatal("PARTIALLY ALLOCATED INODE I=%u", inumber);
146355d6bb5Sswilcox 			if (reply("CLEAR") == 1) {
1477c478bd9Sstevel@tonic-gate 				dp = ginode(inumber);
148355d6bb5Sswilcox 				clearinode(dp);
1497c478bd9Sstevel@tonic-gate 				inodirty();
150355d6bb5Sswilcox 			} else {
151355d6bb5Sswilcox 				iscorrupt = 1;
1527c478bd9Sstevel@tonic-gate 			}
153355d6bb5Sswilcox 		}
154355d6bb5Sswilcox 		statemap[inumber] = USTATE;
155355d6bb5Sswilcox 		return;
156355d6bb5Sswilcox 	}
157355d6bb5Sswilcox 
158355d6bb5Sswilcox 	isdir = ((dp->di_mode & IFMT) == IFDIR) ||
159*ce37393aSowenr 	    ((dp->di_mode & IFMT) == IFATTRDIR);
160355d6bb5Sswilcox 
161355d6bb5Sswilcox 	lastino = inumber;
162355d6bb5Sswilcox 	if (dp->di_size > (u_offset_t)UFS_MAXOFFSET_T) {
163355d6bb5Sswilcox 		pfatal("NEGATIVE SIZE %lld I=%d",
164355d6bb5Sswilcox 		    (longlong_t)dp->di_size, inumber);
165355d6bb5Sswilcox 		goto bogus;
166355d6bb5Sswilcox 	}
167355d6bb5Sswilcox 
168355d6bb5Sswilcox 	/*
169355d6bb5Sswilcox 	 * A more precise test of the type is done later on.  Just get
170355d6bb5Sswilcox 	 * rid of the blatantly-wrong ones before we do any
171355d6bb5Sswilcox 	 * significant work.
172355d6bb5Sswilcox 	 */
173355d6bb5Sswilcox 	if ((dp->di_mode & IFMT) == IFMT) {
174355d6bb5Sswilcox 		pfatal("BAD MODE 0%o I=%d",
175355d6bb5Sswilcox 		    dp->di_mode & IFMT, inumber);
176355d6bb5Sswilcox 		if (reply("BAD MODE: MAKE IT A FILE") == 1) {
177355d6bb5Sswilcox 			statemap[inumber] = FSTATE;
178355d6bb5Sswilcox 			dp = ginode(inumber);
179355d6bb5Sswilcox 			dp->di_mode = IFREG | 0600;
180355d6bb5Sswilcox 			inodirty();
181355d6bb5Sswilcox 			truncino(inumber, sblock.fs_fsize, TI_NOPARENT);
182355d6bb5Sswilcox 			dp = getnextrefresh();
183355d6bb5Sswilcox 		} else {
184355d6bb5Sswilcox 			iscorrupt = 1;
185355d6bb5Sswilcox 		}
186355d6bb5Sswilcox 	}
187355d6bb5Sswilcox 
188355d6bb5Sswilcox 	ndb = howmany(dp->di_size, (u_offset_t)sblock.fs_bsize);
189355d6bb5Sswilcox 	if (ndb < 0) {
190355d6bb5Sswilcox 		/* extra space to distinguish from previous pfatal() */
191355d6bb5Sswilcox 		pfatal("NEGATIVE SIZE %lld  I=%d",
192355d6bb5Sswilcox 		    (longlong_t)dp->di_size, inumber);
193355d6bb5Sswilcox 		goto bogus;
194355d6bb5Sswilcox 	}
195355d6bb5Sswilcox 
196355d6bb5Sswilcox 	if ((dp->di_mode & IFMT) == IFBLK ||
197355d6bb5Sswilcox 	    (dp->di_mode & IFMT) == IFCHR) {
198355d6bb5Sswilcox 		if (dp->di_size != 0) {
199355d6bb5Sswilcox 			pfatal("SPECIAL FILE WITH NON-ZERO LENGTH %lld I=%d",
200355d6bb5Sswilcox 			    (longlong_t)dp->di_size, inumber);
201355d6bb5Sswilcox 			goto bogus;
202355d6bb5Sswilcox 		}
203355d6bb5Sswilcox 
204355d6bb5Sswilcox 		for (j = 0; j < NDADDR; j++) {
205355d6bb5Sswilcox 			/*
206355d6bb5Sswilcox 			 * It's a device, so all the block pointers
207355d6bb5Sswilcox 			 * should be zero except for di_ordev.
208355d6bb5Sswilcox 			 * di_ordev is overlayed on the block array,
209355d6bb5Sswilcox 			 * but where varies between big and little
210355d6bb5Sswilcox 			 * endian, so make sure that the only non-zero
211355d6bb5Sswilcox 			 * element is the correct one.  There can be
212355d6bb5Sswilcox 			 * a device whose ordev is zero, so we can't
213355d6bb5Sswilcox 			 * check for the reverse.
214355d6bb5Sswilcox 			 */
215355d6bb5Sswilcox 			if (dp->di_db[j] != 0 &&
216355d6bb5Sswilcox 			    &dp->di_db[j] != &dp->di_ordev) {
2177c478bd9Sstevel@tonic-gate 				if (debug) {
218355d6bb5Sswilcox 					(void) printf(
219355d6bb5Sswilcox 					    "spec file di_db[%d] has %d\n",
220355d6bb5Sswilcox 					    j, dp->di_db[j]);
2217c478bd9Sstevel@tonic-gate 				}
222355d6bb5Sswilcox 				pfatal(
223355d6bb5Sswilcox 			    "SPECIAL FILE WITH NON-ZERO FRAGMENT LIST  I=%d",
224355d6bb5Sswilcox 				    inumber);
225355d6bb5Sswilcox 				goto bogus;
226355d6bb5Sswilcox 			}
227355d6bb5Sswilcox 		}
2287c478bd9Sstevel@tonic-gate 
229355d6bb5Sswilcox 		for (j = 0; j < NIADDR; j++) {
230355d6bb5Sswilcox 			if (dp->di_ib[j] != 0) {
231355d6bb5Sswilcox 				if (debug)
232355d6bb5Sswilcox 					(void) printf(
233355d6bb5Sswilcox 					    "special has %d at ib[%d]\n",
234355d6bb5Sswilcox 					    dp->di_ib[j], j);
235355d6bb5Sswilcox 				pfatal(
236355d6bb5Sswilcox 			    "SPECIAL FILE WITH NON-ZERO FRAGMENT LIST  I=%d",
237355d6bb5Sswilcox 				    inumber);
238355d6bb5Sswilcox 				goto bogus;
2397c478bd9Sstevel@tonic-gate 			}
240355d6bb5Sswilcox 		}
241355d6bb5Sswilcox 	} else {
242355d6bb5Sswilcox 		/*
243355d6bb5Sswilcox 		 * This assignment is mostly here to appease lint, but
244355d6bb5Sswilcox 		 * doesn't hurt.
245355d6bb5Sswilcox 		 */
246355d6bb5Sswilcox 		err = "Internal error: unexpected variant of having "
247355d6bb5Sswilcox 		    "blocks past end of file  I=%d";
248355d6bb5Sswilcox 
249355d6bb5Sswilcox 		clear = 0;
250355d6bb5Sswilcox 
251355d6bb5Sswilcox 		/*
252355d6bb5Sswilcox 		 * If it's not a device, it has to follow the
253355d6bb5Sswilcox 		 * rules for files.  In particular, no blocks after
254355d6bb5Sswilcox 		 * the last one that di_size says is in use.
255355d6bb5Sswilcox 		 */
256355d6bb5Sswilcox 		for (j = ndb; j < NDADDR; j++) {
257355d6bb5Sswilcox 			if (dp->di_db[j] != 0) {
258355d6bb5Sswilcox 				if (debug) {
259355d6bb5Sswilcox 					(void) printf("bad file direct "
260355d6bb5Sswilcox 					    "addr[%d]: block 0x%x "
261355d6bb5Sswilcox 					    "format: 0%o\n",
262355d6bb5Sswilcox 					    j, dp->di_db[j],
263355d6bb5Sswilcox 					    dp->di_mode & IFMT);
2647c478bd9Sstevel@tonic-gate 				}
265355d6bb5Sswilcox 				err = "FILE WITH FRAGMENTS PAST END  I=%d";
266355d6bb5Sswilcox 				clear = 1;
267355d6bb5Sswilcox 				break;
2687c478bd9Sstevel@tonic-gate 			}
269355d6bb5Sswilcox 		}
270355d6bb5Sswilcox 
271355d6bb5Sswilcox 		/*
272355d6bb5Sswilcox 		 * Find last indirect pointer that should be in use,
273355d6bb5Sswilcox 		 * and make sure any after it are clear.
274355d6bb5Sswilcox 		 */
275355d6bb5Sswilcox 		if (!clear) {
276355d6bb5Sswilcox 			for (j = 0, ndb -= NDADDR; ndb > 0; j++) {
2777c478bd9Sstevel@tonic-gate 				ndb /= NINDIR(&sblock);
278355d6bb5Sswilcox 			}
279355d6bb5Sswilcox 			for (; j < NIADDR; j++) {
2807c478bd9Sstevel@tonic-gate 				if (dp->di_ib[j] != 0) {
2817c478bd9Sstevel@tonic-gate 					if (debug) {
282355d6bb5Sswilcox 						(void) printf("bad file "
283355d6bb5Sswilcox 						    "indirect addr: block %d\n",
284355d6bb5Sswilcox 						    dp->di_ib[j]);
2857c478bd9Sstevel@tonic-gate 					}
286355d6bb5Sswilcox 					err =
287355d6bb5Sswilcox 					    "FILE WITH FRAGMENTS PAST END I=%d";
288355d6bb5Sswilcox 					clear = 2;
289355d6bb5Sswilcox 					break;
2907c478bd9Sstevel@tonic-gate 				}
2917c478bd9Sstevel@tonic-gate 			}
292355d6bb5Sswilcox 		}
293355d6bb5Sswilcox 
294355d6bb5Sswilcox 		if (clear) {
2957c478bd9Sstevel@tonic-gate 			/*
296355d6bb5Sswilcox 			 * The discarded blocks will be garbage-
297355d6bb5Sswilcox 			 * collected in pass5.  If we're told not to
298355d6bb5Sswilcox 			 * discard them, it's just lost blocks, which
299355d6bb5Sswilcox 			 * isn't worth setting iscorrupt for.
3007c478bd9Sstevel@tonic-gate 			 */
301355d6bb5Sswilcox 			pwarn(err, inumber);
302355d6bb5Sswilcox 			if (preen || reply("DISCARD EXCESS FRAGMENTS") == 1) {
303355d6bb5Sswilcox 				dp = ginode(inumber);
304355d6bb5Sswilcox 				if (clear == 1) {
305355d6bb5Sswilcox 					for (; j < NDADDR; j++)
306355d6bb5Sswilcox 						dp->di_db[j] = 0;
307355d6bb5Sswilcox 					j = 0;
3087c478bd9Sstevel@tonic-gate 				}
309355d6bb5Sswilcox 				for (; j < NIADDR; j++)
310355d6bb5Sswilcox 					dp->di_ib[j] = 0;
311355d6bb5Sswilcox 				inodirty();
312355d6bb5Sswilcox 				dp = getnextrefresh();
313355d6bb5Sswilcox 				if (preen)
314355d6bb5Sswilcox 					(void) printf(" (TRUNCATED)");
3157c478bd9Sstevel@tonic-gate 			}
316355d6bb5Sswilcox 		}
317355d6bb5Sswilcox 	}
3187c478bd9Sstevel@tonic-gate 
319355d6bb5Sswilcox 	if (ftypeok(dp) == 0) {
320355d6bb5Sswilcox 		pfatal("UNKNOWN FILE TYPE 0%o  I=%d", dp->di_mode, inumber);
321355d6bb5Sswilcox 		goto bogus;
322355d6bb5Sswilcox 	}
323355d6bb5Sswilcox 	n_files++;
324355d6bb5Sswilcox 	TRACK_LNCNTP(inumber, lncntp[inumber] = dp->di_nlink);
3257c478bd9Sstevel@tonic-gate 
326355d6bb5Sswilcox 	/*
327355d6bb5Sswilcox 	 * We can't do anything about it right now, so note that its
328355d6bb5Sswilcox 	 * processing is being delayed.  Otherwise, we'd be changing
329355d6bb5Sswilcox 	 * the block allocations out from under ourselves, which causes
330355d6bb5Sswilcox 	 * no end of confusion.
331355d6bb5Sswilcox 	 */
332355d6bb5Sswilcox 	flags = statemap[inumber] & INDELAYD;
3337c478bd9Sstevel@tonic-gate 
334355d6bb5Sswilcox 	/*
335355d6bb5Sswilcox 	 * if errorlocked or logging, then open deleted files will
336355d6bb5Sswilcox 	 * manifest as di_nlink <= 0 and di_mode != 0
337355d6bb5Sswilcox 	 * so skip them; they're ok.
338*ce37393aSowenr 	 * Also skip anything already marked to be cleared.
339355d6bb5Sswilcox 	 */
340355d6bb5Sswilcox 	if (dp->di_nlink <= 0 &&
341*ce37393aSowenr 	    !((errorlocked || islog) && dp->di_mode == 0) &&
342*ce37393aSowenr 	    !(flags & INCLEAR)) {
343355d6bb5Sswilcox 		flags |= INZLINK;
344355d6bb5Sswilcox 		if (debug)
345355d6bb5Sswilcox 			(void) printf(
346355d6bb5Sswilcox 		    "marking i=%d INZLINK; nlink %d, mode 0%o, islog %d\n",
347355d6bb5Sswilcox 			    inumber, dp->di_nlink, dp->di_mode, islog);
348355d6bb5Sswilcox 	}
3497c478bd9Sstevel@tonic-gate 
350355d6bb5Sswilcox 	switch (dp->di_mode & IFMT) {
351355d6bb5Sswilcox 	case IFDIR:
352355d6bb5Sswilcox 	case IFATTRDIR:
353355d6bb5Sswilcox 		if (dp->di_size == 0) {
3547c478bd9Sstevel@tonic-gate 			/*
355355d6bb5Sswilcox 			 * INCLEAR means it will be ignored by passes 2 & 3.
3567c478bd9Sstevel@tonic-gate 			 */
357355d6bb5Sswilcox 			if ((dp->di_mode & IFMT) == IFDIR)
358355d6bb5Sswilcox 				(void) printf("ZERO-LENGTH DIR  I=%d\n",
359355d6bb5Sswilcox 				    inumber);
360355d6bb5Sswilcox 			else
361355d6bb5Sswilcox 				(void) printf("ZERO-LENGTH ATTRDIR  I=%d\n",
362355d6bb5Sswilcox 				    inumber);
363355d6bb5Sswilcox 			add_orphan_dir(inumber);
364355d6bb5Sswilcox 			flags |= INCLEAR;
365*ce37393aSowenr 			flags &= ~INZLINK;	/* It will be cleared anyway */
366355d6bb5Sswilcox 		}
367355d6bb5Sswilcox 		statemap[inumber] = DSTATE | flags;
368355d6bb5Sswilcox 		cacheino(dp, inumber);
369355d6bb5Sswilcox 		countdirs++;
370355d6bb5Sswilcox 		break;
3717c478bd9Sstevel@tonic-gate 
372355d6bb5Sswilcox 	case IFSHAD:
373355d6bb5Sswilcox 		if (dp->di_size == 0) {
374355d6bb5Sswilcox 			(void) printf("ZERO-LENGTH SHADOW  I=%d\n", inumber);
375355d6bb5Sswilcox 			flags |= INCLEAR;
376*ce37393aSowenr 			flags &= ~INZLINK;	/* It will be cleared anyway */
377355d6bb5Sswilcox 		}
378355d6bb5Sswilcox 		statemap[inumber] = SSTATE | flags;
379355d6bb5Sswilcox 		cacheacl(dp, inumber);
380355d6bb5Sswilcox 		break;
3817c478bd9Sstevel@tonic-gate 
382355d6bb5Sswilcox 	default:
383355d6bb5Sswilcox 		statemap[inumber] = FSTATE | flags;
384355d6bb5Sswilcox 	}
385355d6bb5Sswilcox 
386355d6bb5Sswilcox 	badblk = 0;
387355d6bb5Sswilcox 	dupblk = 0;
388355d6bb5Sswilcox 	idesc->id_number = inumber;
389355d6bb5Sswilcox 	idesc->id_fix = DONTKNOW;
390355d6bb5Sswilcox 	if (dp->di_size > (u_offset_t)MAXOFF_T) {
391355d6bb5Sswilcox 		largefile_count++;
392355d6bb5Sswilcox 	}
393355d6bb5Sswilcox 
394355d6bb5Sswilcox 	(void) ckinode(dp, idesc, CKI_TRAVERSE);
395355d6bb5Sswilcox 	if (isdir && (idesc->id_firsthole >= 0))
396355d6bb5Sswilcox 		check_dirholes(inumber, idesc);
397355d6bb5Sswilcox 
398355d6bb5Sswilcox 	if (dp->di_blocks != idesc->id_entryno) {
399355d6bb5Sswilcox 		/*
400355d6bb5Sswilcox 		 * The kernel releases any blocks it finds in the lists,
401355d6bb5Sswilcox 		 * ignoring the block count itself.  So, a bad count is
402355d6bb5Sswilcox 		 * not grounds for setting iscorrupt.
403355d6bb5Sswilcox 		 */
404355d6bb5Sswilcox 		pwarn("INCORRECT DISK BLOCK COUNT I=%u (%d should be %d)",
405355d6bb5Sswilcox 		    inumber, (uint32_t)dp->di_blocks, idesc->id_entryno);
406355d6bb5Sswilcox 		if (!preen && (reply("CORRECT") == 0))
407355d6bb5Sswilcox 			return;
408355d6bb5Sswilcox 		dp = ginode(inumber);
409355d6bb5Sswilcox 		dp->di_blocks = idesc->id_entryno;
410355d6bb5Sswilcox 		iip = getinoinfo(inumber);
411355d6bb5Sswilcox 		if (iip != NULL)
412355d6bb5Sswilcox 			iip->i_isize = dp->di_size;
413355d6bb5Sswilcox 		inodirty();
414355d6bb5Sswilcox 		if (preen)
415355d6bb5Sswilcox 			(void) printf(" (CORRECTED)\n");
416355d6bb5Sswilcox 	}
417355d6bb5Sswilcox 	if (isdir && (dp->di_blocks == 0)) {
418355d6bb5Sswilcox 		/*
419355d6bb5Sswilcox 		 * INCLEAR will cause passes 2 and 3 to skip it.
420355d6bb5Sswilcox 		 */
421355d6bb5Sswilcox 		(void) printf("DIR WITH ZERO BLOCKS  I=%d\n", inumber);
422355d6bb5Sswilcox 		statemap[inumber] = DCLEAR;
423355d6bb5Sswilcox 		add_orphan_dir(inumber);
424355d6bb5Sswilcox 	}
425355d6bb5Sswilcox 
426355d6bb5Sswilcox 	/*
427355d6bb5Sswilcox 	 * Check that the ACL is on a valid file type
428355d6bb5Sswilcox 	 */
429355d6bb5Sswilcox 	shadow = dp->di_shadow;
430355d6bb5Sswilcox 	if (shadow != 0) {
431355d6bb5Sswilcox 		if (acltypeok(dp) == 0) {
432355d6bb5Sswilcox 			clear_attr_acl(inumber, -1,
433355d6bb5Sswilcox 			    "NON-ZERO ACL REFERENCE, I=%d\n");
434355d6bb5Sswilcox 		} else if ((shadow <= UFSROOTINO) ||
435355d6bb5Sswilcox 		    (shadow > maxinumber)) {
436355d6bb5Sswilcox 			clear_attr_acl(inumber, -1,
437355d6bb5Sswilcox 			    "BAD ACL REFERENCE I=%d\n");
438355d6bb5Sswilcox 		} else {
439355d6bb5Sswilcox 			registershadowclient(shadow,
440355d6bb5Sswilcox 			    inumber, &shadowclientinfo);
441355d6bb5Sswilcox 		}
442355d6bb5Sswilcox 	}
443355d6bb5Sswilcox 
444355d6bb5Sswilcox 	attrinode = dp->di_oeftflag;
445355d6bb5Sswilcox 	if (attrinode != 0) {
446