xref: /illumos-gate/usr/src/cmd/fs.d/ufs/fsck/pass1.c (revision 2a8bcb4e)
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 
28355d6bb5Sswilcox #include <stdio.h>
29355d6bb5Sswilcox #include <stdlib.h>
30355d6bb5Sswilcox #include <unistd.h>
31355d6bb5Sswilcox #include <string.h>
327c478bd9Sstevel@tonic-gate #include <sys/param.h>
337c478bd9Sstevel@tonic-gate #include <sys/types.h>
347c478bd9Sstevel@tonic-gate #include <sys/mntent.h>
357c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_fs.h>
367c478bd9Sstevel@tonic-gate #include <sys/vnode.h>
37355d6bb5Sswilcox #define	_KERNEL
38355d6bb5Sswilcox #include <sys/fs/ufs_fsdir.h>
39355d6bb5Sswilcox #undef _KERNEL
407c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_inode.h>
417c478bd9Sstevel@tonic-gate #include "fsck.h"
427c478bd9Sstevel@tonic-gate 
437c478bd9Sstevel@tonic-gate /*
44355d6bb5Sswilcox  * for each large file (size > MAXOFF_T), the global largefile_count
45355d6bb5Sswilcox  * gets incremented during this pass.
467c478bd9Sstevel@tonic-gate  */
477c478bd9Sstevel@tonic-gate 
48355d6bb5Sswilcox static uint32_t badblk;		/* number seen for the current inode */
49355d6bb5Sswilcox static uint32_t dupblk;		/* number seen for the current inode */
507c478bd9Sstevel@tonic-gate 
51355d6bb5Sswilcox static void clear_attr_acl(fsck_ino_t, fsck_ino_t, char *);
52355d6bb5Sswilcox static void verify_inode(fsck_ino_t, struct inodesc *, fsck_ino_t);
53355d6bb5Sswilcox static void check_dirholes(fsck_ino_t, struct inodesc *);
54355d6bb5Sswilcox static void collapse_dirhole(fsck_ino_t, struct inodesc *);
55355d6bb5Sswilcox static void note_used(daddr32_t);
567c478bd9Sstevel@tonic-gate 
57355d6bb5Sswilcox void
pass1(void)58355d6bb5Sswilcox pass1(void)
597c478bd9Sstevel@tonic-gate {
60355d6bb5Sswilcox 	uint_t c, i;
61355d6bb5Sswilcox 	daddr32_t cgd;
627c478bd9Sstevel@tonic-gate 	struct inodesc idesc;
63355d6bb5Sswilcox 	fsck_ino_t inumber;
64355d6bb5Sswilcox 	fsck_ino_t maxinumber;
657c478bd9Sstevel@tonic-gate 
667c478bd9Sstevel@tonic-gate 	/*
677c478bd9Sstevel@tonic-gate 	 * Set file system reserved blocks in used block map.
687c478bd9Sstevel@tonic-gate 	 */
697c478bd9Sstevel@tonic-gate 	for (c = 0; c < sblock.fs_ncg; c++) {
707c478bd9Sstevel@tonic-gate 		cgd = cgdmin(&sblock, c);
717c478bd9Sstevel@tonic-gate 		if (c == 0) {
72355d6bb5Sswilcox 			/*
73355d6bb5Sswilcox 			 * Doing the first cylinder group, account for
74355d6bb5Sswilcox 			 * the cg summaries as well.
75355d6bb5Sswilcox 			 */
767c478bd9Sstevel@tonic-gate 			i = cgbase(&sblock, c);
777c478bd9Sstevel@tonic-gate 			cgd += howmany(sblock.fs_cssize, sblock.fs_fsize);
78355d6bb5Sswilcox 		} else {
797c478bd9Sstevel@tonic-gate 			i = cgsblock(&sblock, c);
80355d6bb5Sswilcox 		}
81355d6bb5Sswilcox 		for (; i < cgd; i++) {
82355d6bb5Sswilcox 			note_used(i);
83355d6bb5Sswilcox 		}
847c478bd9Sstevel@tonic-gate 	}
857c478bd9Sstevel@tonic-gate 	/*
86355d6bb5Sswilcox 	 * Note blocks being used by the log, so we don't declare
87355d6bb5Sswilcox 	 * them as available and some time in the future we get a
88355d6bb5Sswilcox 	 * freeing free block panic.
897c478bd9Sstevel@tonic-gate 	 */
90355d6bb5Sswilcox 	if (islog && islogok && sblock.fs_logbno)
9139542a18Sabalfour 		examinelog(&note_used);
927c478bd9Sstevel@tonic-gate 
937c478bd9Sstevel@tonic-gate 	/*
94355d6bb5Sswilcox 	 * Find all allocated blocks.  This must be completed before
95355d6bb5Sswilcox 	 * we read the contents of any directories, as dirscan() et al
96355d6bb5Sswilcox 	 * don't want to know about block allocation holes.  So, part
97355d6bb5Sswilcox 	 * of this pass is to truncate any directories with holes to
98355d6bb5Sswilcox 	 * just before those holes, so dirscan() can remain blissfully
99355d6bb5Sswilcox 	 * ignorant.
1007c478bd9Sstevel@tonic-gate 	 */
1017c478bd9Sstevel@tonic-gate 	inumber = 0;
1027c478bd9Sstevel@tonic-gate 	n_files = n_blks = 0;
1037c478bd9Sstevel@tonic-gate 	resetinodebuf();
1047c478bd9Sstevel@tonic-gate 	maxinumber = sblock.fs_ncg * sblock.fs_ipg;
1057c478bd9Sstevel@tonic-gate 	for (c = 0; c < sblock.fs_ncg; c++) {
1067c478bd9Sstevel@tonic-gate 		for (i = 0; i < sblock.fs_ipg; i++, inumber++) {
1077c478bd9Sstevel@tonic-gate 			if (inumber < UFSROOTINO)
1087c478bd9Sstevel@tonic-gate 				continue;
109355d6bb5Sswilcox 			init_inodesc(&idesc);
110355d6bb5Sswilcox 			idesc.id_type = ADDR;
111355d6bb5Sswilcox 			idesc.id_func = pass1check;
112355d6bb5Sswilcox 			verify_inode(inumber, &idesc, maxinumber);
113355d6bb5Sswilcox 		}
114355d6bb5Sswilcox 	}
115355d6bb5Sswilcox 	freeinodebuf();
116355d6bb5Sswilcox }
117355d6bb5Sswilcox 
118355d6bb5Sswilcox /*
119355d6bb5Sswilcox  * Perform checks on an inode and setup/track the state of the inode
120355d6bb5Sswilcox  * in maps (statemap[], lncntp[]) for future reference and validation.
121355d6bb5Sswilcox  * Initiate the calls to ckinode and in turn pass1check() to handle
122355d6bb5Sswilcox  * further validation.
123355d6bb5Sswilcox  */
124355d6bb5Sswilcox static void
verify_inode(fsck_ino_t inumber,struct inodesc * idesc,fsck_ino_t maxinumber)125355d6bb5Sswilcox verify_inode(fsck_ino_t inumber, struct inodesc *idesc, fsck_ino_t maxinumber)
126355d6bb5Sswilcox {
127355d6bb5Sswilcox 	int j, clear, flags;
128355d6bb5Sswilcox 	int isdir;
129355d6bb5Sswilcox 	char *err;
130355d6bb5Sswilcox 	fsck_ino_t shadow, attrinode;
131355d6bb5Sswilcox 	daddr32_t ndb;
132355d6bb5Sswilcox 	struct dinode *dp;
133355d6bb5Sswilcox 	struct inoinfo *iip;
134355d6bb5Sswilcox 
135355d6bb5Sswilcox 	dp = getnextinode(inumber);
136355d6bb5Sswilcox 	if ((dp->di_mode & IFMT) == 0) {
137355d6bb5Sswilcox 		/* mode and type of file is not set */
138355d6bb5Sswilcox 		if ((memcmp((void *)dp->di_db, (void *)zino.di_db,
139*ce37393aSowenr 		    NDADDR * sizeof (daddr32_t)) != 0) ||
140355d6bb5Sswilcox 		    (memcmp((void *)dp->di_ib, (void *)zino.di_ib,
141*ce37393aSowenr 		    NIADDR * sizeof (daddr32_t)) != 0) ||
142355d6bb5Sswilcox 		    (dp->di_mode != 0) || (dp->di_size != 0)) {
143355d6bb5Sswilcox 			pfatal("PARTIALLY ALLOCATED INODE I=%u", inumber);
144355d6bb5Sswilcox 			if (reply("CLEAR") == 1) {
1457c478bd9Sstevel@tonic-gate 				dp = ginode(inumber);
146355d6bb5Sswilcox 				clearinode(dp);
1477c478bd9Sstevel@tonic-gate 				inodirty();
148355d6bb5Sswilcox 			} else {
149355d6bb5Sswilcox 				iscorrupt = 1;
1507c478bd9Sstevel@tonic-gate 			}
151355d6bb5Sswilcox 		}
152355d6bb5Sswilcox 		statemap[inumber] = USTATE;
153355d6bb5Sswilcox 		return;
154355d6bb5Sswilcox 	}
155355d6bb5Sswilcox 
156355d6bb5Sswilcox 	isdir = ((dp->di_mode & IFMT) == IFDIR) ||
157*ce37393aSowenr 	    ((dp->di_mode & IFMT) == IFATTRDIR);
158355d6bb5Sswilcox 
159355d6bb5Sswilcox 	lastino = inumber;
160355d6bb5Sswilcox 	if (dp->di_size > (u_offset_t)UFS_MAXOFFSET_T) {
161355d6bb5Sswilcox 		pfatal("NEGATIVE SIZE %lld I=%d",
162355d6bb5Sswilcox 		    (longlong_t)dp->di_size, inumber);
163355d6bb5Sswilcox 		goto bogus;
164355d6bb5Sswilcox 	}
165355d6bb5Sswilcox 
166355d6bb5Sswilcox 	/*
167355d6bb5Sswilcox 	 * A more precise test of the type is done later on.  Just get
168355d6bb5Sswilcox 	 * rid of the blatantly-wrong ones before we do any
169355d6bb5Sswilcox 	 * significant work.
170355d6bb5Sswilcox 	 */
171355d6bb5Sswilcox 	if ((dp->di_mode & IFMT) == IFMT) {
172355d6bb5Sswilcox 		pfatal("BAD MODE 0%o I=%d",
173355d6bb5Sswilcox 		    dp->di_mode & IFMT, inumber);
174355d6bb5Sswilcox 		if (reply("BAD MODE: MAKE IT A FILE") == 1) {
175355d6bb5Sswilcox 			statemap[inumber] = FSTATE;
176355d6bb5Sswilcox 			dp = ginode(inumber);
177355d6bb5Sswilcox 			dp->di_mode = IFREG | 0600;
178355d6bb5Sswilcox 			inodirty();
179355d6bb5Sswilcox 			truncino(inumber, sblock.fs_fsize, TI_NOPARENT);
180355d6bb5Sswilcox 			dp = getnextrefresh();
181355d6bb5Sswilcox 		} else {
182355d6bb5Sswilcox 			iscorrupt = 1;
183355d6bb5Sswilcox 		}
184355d6bb5Sswilcox 	}
185355d6bb5Sswilcox 
186355d6bb5Sswilcox 	ndb = howmany(dp->di_size, (u_offset_t)sblock.fs_bsize);
187355d6bb5Sswilcox 	if (ndb < 0) {
188355d6bb5Sswilcox 		/* extra space to distinguish from previous pfatal() */
189355d6bb5Sswilcox 		pfatal("NEGATIVE SIZE %lld  I=%d",
190355d6bb5Sswilcox 		    (longlong_t)dp->di_size, inumber);
191355d6bb5Sswilcox 		goto bogus;
192355d6bb5Sswilcox 	}
193355d6bb5Sswilcox 
194355d6bb5Sswilcox 	if ((dp->di_mode & IFMT) == IFBLK ||
195355d6bb5Sswilcox 	    (dp->di_mode & IFMT) == IFCHR) {
196355d6bb5Sswilcox 		if (dp->di_size != 0) {
197355d6bb5Sswilcox 			pfatal("SPECIAL FILE WITH NON-ZERO LENGTH %lld I=%d",
198355d6bb5Sswilcox 			    (longlong_t)dp->di_size, inumber);
199355d6bb5Sswilcox 			goto bogus;
200355d6bb5Sswilcox 		}
201355d6bb5Sswilcox 
202355d6bb5Sswilcox 		for (j = 0; j < NDADDR; j++) {
203355d6bb5Sswilcox 			/*
204355d6bb5Sswilcox 			 * It's a device, so all the block pointers
205355d6bb5Sswilcox 			 * should be zero except for di_ordev.
206355d6bb5Sswilcox 			 * di_ordev is overlayed on the block array,
207355d6bb5Sswilcox 			 * but where varies between big and little
208355d6bb5Sswilcox 			 * endian, so make sure that the only non-zero
209355d6bb5Sswilcox 			 * element is the correct one.  There can be
210355d6bb5Sswilcox 			 * a device whose ordev is zero, so we can't
211355d6bb5Sswilcox 			 * check for the reverse.
212355d6bb5Sswilcox 			 */
213355d6bb5Sswilcox 			if (dp->di_db[j] != 0 &&
214355d6bb5Sswilcox 			    &dp->di_db[j] != &dp->di_ordev) {
2157c478bd9Sstevel@tonic-gate 				if (debug) {
216355d6bb5Sswilcox 					(void) printf(
217355d6bb5Sswilcox 					    "spec file di_db[%d] has %d\n",
218355d6bb5Sswilcox 					    j, dp->di_db[j]);
2197c478bd9Sstevel@tonic-gate 				}
220355d6bb5Sswilcox 				pfatal(
221355d6bb5Sswilcox 			    "SPECIAL FILE WITH NON-ZERO FRAGMENT LIST  I=%d",
222355d6bb5Sswilcox 				    inumber);
223355d6bb5Sswilcox 				goto bogus;
224355d6bb5Sswilcox 			}
225355d6bb5Sswilcox 		}
2267c478bd9Sstevel@tonic-gate 
227355d6bb5Sswilcox 		for (j = 0; j < NIADDR; j++) {
228355d6bb5Sswilcox 			if (dp->di_ib[j] != 0) {
229355d6bb5Sswilcox 				if (debug)
230355d6bb5Sswilcox 					(void) printf(
231355d6bb5Sswilcox 					    "special has %d at ib[%d]\n",
232355d6bb5Sswilcox 					    dp->di_ib[j], j);
233355d6bb5Sswilcox 				pfatal(
234355d6bb5Sswilcox 			    "SPECIAL FILE WITH NON-ZERO FRAGMENT LIST  I=%d",
235355d6bb5Sswilcox 				    inumber);
236355d6bb5Sswilcox 				goto bogus;
2377c478bd9Sstevel@tonic-gate 			}
238355d6bb5Sswilcox 		}
239355d6bb5Sswilcox 	} else {
240355d6bb5Sswilcox 		/*
241355d6bb5Sswilcox 		 * This assignment is mostly here to appease lint, but
242355d6bb5Sswilcox 		 * doesn't hurt.
243355d6bb5Sswilcox 		 */
244355d6bb5Sswilcox 		err = "Internal error: unexpected variant of having "
245355d6bb5Sswilcox 		    "blocks past end of file  I=%d";
246355d6bb5Sswilcox 
247355d6bb5Sswilcox 		clear = 0;
248355d6bb5Sswilcox 
249355d6bb5Sswilcox 		/*
250355d6bb5Sswilcox 		 * If it's not a device, it has to follow the
251355d6bb5Sswilcox 		 * rules for files.  In particular, no blocks after
252355d6bb5Sswilcox 		 * the last one that di_size says is in use.
253355d6bb5Sswilcox 		 */
254355d6bb5Sswilcox 		for (j = ndb; j < NDADDR; j++) {
255355d6bb5Sswilcox 			if (dp->di_db[j] != 0) {
256355d6bb5Sswilcox 				if (debug) {
257355d6bb5Sswilcox 					(void) printf("bad file direct "
258355d6bb5Sswilcox 					    "addr[%d]: block 0x%x "
259355d6bb5Sswilcox 					    "format: 0%o\n",
260355d6bb5Sswilcox 					    j, dp->di_db[j],
261355d6bb5Sswilcox 					    dp->di_mode & IFMT);
2627c478bd9Sstevel@tonic-gate 				}
263355d6bb5Sswilcox 				err = "FILE WITH FRAGMENTS PAST END  I=%d";
264355d6bb5Sswilcox 				clear = 1;
265355d6bb5Sswilcox 				break;
2667c478bd9Sstevel@tonic-gate 			}
267355d6bb5Sswilcox 		}
268355d6bb5Sswilcox 
269355d6bb5Sswilcox 		/*
270355d6bb5Sswilcox 		 * Find last indirect pointer that should be in use,
271355d6bb5Sswilcox 		 * and make sure any after it are clear.
272355d6bb5Sswilcox 		 */
273355d6bb5Sswilcox 		if (!clear) {
274355d6bb5Sswilcox 			for (j = 0, ndb -= NDADDR; ndb > 0; j++) {
2757c478bd9Sstevel@tonic-gate 				ndb /= NINDIR(&sblock);
276355d6bb5Sswilcox 			}
277355d6bb5Sswilcox 			for (; j < NIADDR; j++) {
2787c478bd9Sstevel@tonic-gate 				if (dp->di_ib[j] != 0) {
2797c478bd9Sstevel@tonic-gate 					if (debug) {
280355d6bb5Sswilcox 						(void) printf("bad file "
281355d6bb5Sswilcox 						    "indirect addr: block %d\n",
282355d6bb5Sswilcox 						    dp->di_ib[j]);
2837c478bd9Sstevel@tonic-gate 					}
284355d6bb5Sswilcox 					err =
285355d6bb5Sswilcox 					    "FILE WITH FRAGMENTS PAST END I=%d";
286355d6bb5Sswilcox 					clear = 2;
287355d6bb5Sswilcox 					break;
2887c478bd9Sstevel@tonic-gate 				}
2897c478bd9Sstevel@tonic-gate 			}
290355d6bb5Sswilcox 		}
291355d6bb5Sswilcox 
292355d6bb5Sswilcox 		if (clear) {
2937c478bd9Sstevel@tonic-gate 			/*
294355d6bb5Sswilcox 			 * The discarded blocks will be garbage-
295355d6bb5Sswilcox 			 * collected in pass5.  If we're told not to
296355d6bb5Sswilcox 			 * discard them, it's just lost blocks, which
297355d6bb5Sswilcox 			 * isn't worth setting iscorrupt for.
2987c478bd9Sstevel@tonic-gate 			 */
299355d6bb5Sswilcox 			pwarn(err, inumber);
300355d6bb5Sswilcox 			if (preen || reply("DISCARD EXCESS FRAGMENTS") == 1) {
301355d6bb5Sswilcox 				dp = ginode(inumber);
302355d6bb5Sswilcox 				if (clear == 1) {
303355d6bb5Sswilcox 					for (; j < NDADDR; j++)
304355d6bb5Sswilcox 						dp->di_db[j] = 0;
305355d6bb5Sswilcox 					j = 0;
3067c478bd9Sstevel@tonic-gate 				}
307355d6bb5Sswilcox 				for (; j < NIADDR; j++)
308355d6bb5Sswilcox 					dp->di_ib[j] = 0;
309355d6bb5Sswilcox 				inodirty();
310355d6bb5Sswilcox 				dp = getnextrefresh();
311355d6bb5Sswilcox 				if (preen)
312355d6bb5Sswilcox 					(void) printf(" (TRUNCATED)");
3137c478bd9Sstevel@tonic-gate 			}
314355d6bb5Sswilcox 		}
315355d6bb5Sswilcox 	}
3167c478bd9Sstevel@tonic-gate 
317355d6bb5Sswilcox 	if (ftypeok(dp) == 0) {
318355d6bb5Sswilcox 		pfatal("UNKNOWN FILE TYPE 0%o  I=%d", dp->di_mode, inumber);
319355d6bb5Sswilcox 		goto bogus;
320355d6bb5Sswilcox 	}
321355d6bb5Sswilcox 	n_files++;
322355d6bb5Sswilcox 	TRACK_LNCNTP(inumber, lncntp[inumber] = dp->di_nlink);
3237c478bd9Sstevel@tonic-gate 
324355d6bb5Sswilcox 	/*
325355d6bb5Sswilcox 	 * We can't do anything about it right now, so note that its
326355d6bb5Sswilcox 	 * processing is being delayed.  Otherwise, we'd be changing
327355d6bb5Sswilcox 	 * the block allocations out from under ourselves, which causes
328355d6bb5Sswilcox 	 * no end of confusion.
329355d6bb5Sswilcox 	 */
330355d6bb5Sswilcox 	flags = statemap[inumber] & INDELAYD;
3317c478bd9Sstevel@tonic-gate 
332355d6bb5Sswilcox 	/*
333355d6bb5Sswilcox 	 * if errorlocked or logging, then open deleted files will
334355d6bb5Sswilcox 	 * manifest as di_nlink <= 0 and di_mode != 0
335355d6bb5Sswilcox 	 * so skip them; they're ok.
336*ce37393aSowenr 	 * Also skip anything already marked to be cleared.
337355d6bb5Sswilcox 	 */
338355d6bb5Sswilcox 	if (dp->di_nlink <= 0 &&
339*ce37393aSowenr 	    !((errorlocked || islog) && dp->di_mode == 0) &&
340*ce37393aSowenr 	    !(flags & INCLEAR)) {
341355d6bb5Sswilcox 		flags |= INZLINK;
342355d6bb5Sswilcox 		if (debug)
343355d6bb5Sswilcox 			(void) printf(
344355d6bb5Sswilcox 		    "marking i=%d INZLINK; nlink %d, mode 0%o, islog %d\n",
345355d6bb5Sswilcox 			    inumber, dp->di_nlink, dp->di_mode, islog);
346355d6bb5Sswilcox 	}
3477c478bd9Sstevel@tonic-gate 
348355d6bb5Sswilcox 	switch (dp->di_mode & IFMT) {
349355d6bb5Sswilcox 	case IFDIR:
350355d6bb5Sswilcox 	case IFATTRDIR:
351355d6bb5Sswilcox 		if (dp->di_size == 0) {
3527c478bd9Sstevel@tonic-gate 			/*
353355d6bb5Sswilcox 			 * INCLEAR means it will be ignored by passes 2 & 3.
3547c478bd9Sstevel@tonic-gate 			 */
355355d6bb5Sswilcox 			if ((dp->di_mode & IFMT) == IFDIR)
356355d6bb5Sswilcox 				(void) printf("ZERO-LENGTH DIR  I=%d\n",
357355d6bb5Sswilcox 				    inumber);
358355d6bb5Sswilcox 			else
359355d6bb5Sswilcox 				(void) printf("ZERO-LENGTH ATTRDIR  I=%d\n",
360355d6bb5Sswilcox 				    inumber);
361355d6bb5Sswilcox 			add_orphan_dir(inumber);
362355d6bb5Sswilcox 			flags |= INCLEAR;
363*ce37393aSowenr 			flags &= ~INZLINK;	/* It will be cleared anyway */
364355d6bb5Sswilcox 		}
365355d6bb5Sswilcox 		statemap[inumber] = DSTATE | flags;
366355d6bb5Sswilcox 		cacheino(dp, inumber);
367355d6bb5Sswilcox 		countdirs++;
368355d6bb5Sswilcox 		break;
3697c478bd9Sstevel@tonic-gate 
370355d6bb5Sswilcox 	case IFSHAD:
371355d6bb5Sswilcox 		if (dp->di_size == 0) {
372355d6bb5Sswilcox 			(void) printf("ZERO-LENGTH SHADOW  I=%d\n", inumber);
373355d6bb5Sswilcox 			flags |= INCLEAR;
374*ce37393aSowenr 			flags &= ~INZLINK;	/* It will be cleared anyway */
375355d6bb5Sswilcox 		}
376355d6bb5Sswilcox 		statemap[inumber] = SSTATE | flags;
377355d6bb5Sswilcox 		cacheacl(dp, inumber);
378355d6bb5Sswilcox 		break;
3797c478bd9Sstevel@tonic-gate 
380355d6bb5Sswilcox 	default:
381355d6bb5Sswilcox 		statemap[inumber] = FSTATE | flags;
382355d6bb5Sswilcox 	}
383355d6bb5Sswilcox 
384355d6bb5Sswilcox 	badblk = 0;
385355d6bb5Sswilcox 	dupblk = 0;
386355d6bb5Sswilcox 	idesc->id_number = inumber;
387355d6bb5Sswilcox 	idesc->id_fix = DONTKNOW;
388355d6bb5Sswilcox 	if (dp->di_size > (u_offset_t)MAXOFF_T) {
389355d6bb5Sswilcox 		largefile_count++;
390355d6bb5Sswilcox 	}
391355d6bb5Sswilcox 
392355d6bb5Sswilcox 	(void) ckinode(dp, idesc, CKI_TRAVERSE);
393355d6bb5Sswilcox 	if (isdir && (idesc->id_firsthole >= 0))
394355d6bb5Sswilcox 		check_dirholes(inumber, idesc);
395355d6bb5Sswilcox 
396355d6bb5Sswilcox 	if (dp->di_blocks != idesc->id_entryno) {
397355d6bb5Sswilcox 		/*
398355d6bb5Sswilcox 		 * The kernel releases any blocks it finds in the lists,
399355d6bb5Sswilcox 		 * ignoring the block count itself.  So, a bad count is
400355d6bb5Sswilcox 		 * not grounds for setting iscorrupt.
401355d6bb5Sswilcox 		 */
402355d6bb5Sswilcox 		pwarn("INCORRECT DISK BLOCK COUNT I=%u (%d should be %d)",
403355d6bb5Sswilcox 		    inumber, (uint32_t)dp->di_blocks, idesc->id_entryno);
404355d6bb5Sswilcox 		if (!preen && (reply("CORRECT") == 0))
405355d6bb5Sswilcox 			return;
406355d6bb5Sswilcox 		dp = ginode(inumber);
407355d6bb5Sswilcox 		dp->di_blocks = idesc->id_entryno;
408355d6bb5Sswilcox 		iip = getinoinfo(inumber);
409355d6bb5Sswilcox 		if (iip != NULL)
410355d6bb5Sswilcox 			iip->i_isize = dp->di_size;
411355d6bb5Sswilcox 		inodirty();
412355d6bb5Sswilcox 		if (preen)
413355d6bb5Sswilcox 			(void) printf(" (CORRECTED)\n");
414355d6bb5Sswilcox 	}
415355d6bb5Sswilcox 	if (isdir && (dp->di_blocks == 0)) {
416355d6bb5Sswilcox 		/*
417355d6bb5Sswilcox 		 * INCLEAR will cause passes 2 and 3 to skip it.
418355d6bb5Sswilcox 		 */
419355d6bb5Sswilcox 		(void) printf("DIR WITH ZERO BLOCKS  I=%d\n", inumber);
420355d6bb5Sswilcox 		statemap[inumber] = DCLEAR;
421355d6bb5Sswilcox 		add_orphan_dir(inumber);
422355d6bb5Sswilcox 	}
423355d6bb5Sswilcox 
424355d6bb5Sswilcox 	/*
425355d6bb5Sswilcox 	 * Check that the ACL is on a valid file type
426355d6bb5Sswilcox 	 */
427355d6bb5Sswilcox 	shadow = dp->di_shadow;
428355d6bb5Sswilcox 	if (shadow != 0) {
429355d6bb5Sswilcox 		if (acltypeok(dp) == 0) {
430355d6bb5Sswilcox 			clear_attr_acl(inumber, -1,
431355d6bb5Sswilcox 			    "NON-ZERO ACL REFERENCE, I=%d\n");
432355d6bb5Sswilcox 		} else if ((shadow <= UFSROOTINO) ||
433355d6bb5Sswilcox 		    (shadow > maxinumber)) {
434355d6bb5Sswilcox 			clear_attr_acl(inumber, -1,
435355d6bb5Sswilcox 			    "BAD ACL REFERENCE I=%d\n");
436355d6bb5Sswilcox 		} else {
437355d6bb5Sswilcox 			registershadowclient(shadow,
438355d6bb5Sswilcox 			    inumber, &shadowclientinfo);
439355d6bb5Sswilcox 		}
440355d6bb5Sswilcox 	}
441355d6bb5Sswilcox 
442355d6bb5Sswilcox 	attrinode = dp->di_oeftflag;
443355d6bb5Sswilcox 	if (attrinode != 0) {
444355d6bb5Sswilcox 		if ((attrinode <= UFSROOTINO) ||
445355d6bb5Sswilcox 		    (attrinode > maxinumber)) {
446355d6bb5Sswilcox 			clear_attr_acl(attrinode, inumber,
447355d6bb5Sswilcox 			    "BAD ATTRIBUTE REFERENCE TO I=%d FROM I=%d\n");
448355d6bb5Sswilcox 		} else {
449355d6bb5Sswilcox 			dp = ginode(attrinode);
450355d6bb5Sswilcox 			if ((dp->di_mode & IFMT) != IFATTRDIR) {
451355d6bb5Sswilcox 				clear_attr_acl(attrinode, inumber,
452355d6bb5Sswilcox 			    "BAD ATTRIBUTE DIR REF TO I=%d FROM I=%d\n");
453355d6bb5Sswilcox 			} else if (dp->di_size == 0) {
454355d6bb5Sswilcox 				clear_attr_acl(attrinode, inumber,
455355d6bb5Sswilcox 		    "REFERENCE TO ZERO-LENGTH ATTRIBUTE DIR I=%d from I=%d\n");
456355d6bb5Sswilcox 			} else {
457355d6bb5Sswilcox 				registershadowclient(attrinode, inumber,
458355d6bb5Sswilcox 				    &attrclientinfo);
4597c478bd9Sstevel@tonic-gate 			}
4607c478bd9Sstevel@tonic-gate 		}
4617c478bd9Sstevel@tonic-gate 	}
462355d6bb5Sswilcox 	return;
463355d6bb5Sswilcox 
464355d6bb5Sswilcox 	/*
465355d6bb5Sswilcox 	 * If we got here, we've not had the chance to see if a
466355d6bb5Sswilcox 	 * directory has holes, but we know the directory's bad,
467355d6bb5Sswilcox 	 * so it's safe to always return false (no holes found).
468355d6bb5Sswilcox 	 *
469355d6bb5Sswilcox 	 * Also, a pfatal() is always done before jumping here, so
470355d6bb5Sswilcox 	 * we know we're not in preen mode.
471355d6bb5Sswilcox 	 */
472355d6bb5Sswilcox bogus:
473355d6bb5Sswilcox 	if (isdir) {
474355d6bb5Sswilcox 		/*
475355d6bb5Sswilcox 		 * INCLEAR makes passes 2 & 3 skip it.
476355d6bb5Sswilcox 		 */
477355d6bb5Sswilcox 		statemap[inumber] = DCLEAR;
478355d6bb5Sswilcox 		add_orphan_dir(inumber);
479355d6bb5Sswilcox 		cacheino(dp, inumber);
480355d6bb5Sswilcox 	} else {
481355d6bb5Sswilcox 		statemap[inumber] = FCLEAR;
482355d6bb5Sswilcox 	}
483355d6bb5Sswilcox 	if (reply("CLEAR") == 1) {
484355d6bb5Sswilcox 		(void) tdelete((void *)inumber, &limbo_dirs, ino_t_cmp);
485355d6bb5Sswilcox 		freeino(inumber, TI_PARENT);
486355d6bb5Sswilcox 		inodirty();
487355d6bb5Sswilcox 	} else {
488355d6bb5Sswilcox 		iscorrupt = 1;
489355d6bb5Sswilcox 	}
490355d6bb5Sswilcox }
491355d6bb5Sswilcox 
492355d6bb5Sswilcox /*
493355d6bb5Sswilcox  * Do fixup for bad acl/attr references.  If PARENT is -1, then
494355d6bb5Sswilcox  * we assume we're working on a shadow, otherwise an extended attribute.
495355d6bb5Sswilcox  * FMT must be a printf format string, with one %d directive for
496355d6bb5Sswilcox  * the inode number.
497355d6bb5Sswilcox  */
498355d6bb5Sswilcox static void
clear_attr_acl(fsck_ino_t inumber,fsck_ino_t parent,char * fmt)499355d6bb5Sswilcox clear_attr_acl(fsck_ino_t inumber, fsck_ino_t parent, char *fmt)
500355d6bb5Sswilcox {
501355d6bb5Sswilcox 	fsck_ino_t victim = inumber;
502355d6bb5Sswilcox 	struct dinode *dp;
503355d6bb5Sswilcox 
504355d6bb5Sswilcox 	if (parent != -1)
505355d6bb5Sswilcox 		victim = parent;
506355d6bb5Sswilcox 
507355d6bb5Sswilcox 	if (fmt != NULL) {
508355d6bb5Sswilcox 		if (parent == -1)
509355d6bb5Sswilcox 			pwarn(fmt, (int)inumber);
510355d6bb5Sswilcox 		else
511355d6bb5Sswilcox 			pwarn(fmt, (int)inumber, (int)parent);
512355d6bb5Sswilcox 	}
513355d6bb5Sswilcox 
514355d6bb5Sswilcox 	if (debug)
515355d6bb5Sswilcox 		(void) printf("parent file/dir I=%d\nvictim I=%d",
516355d6bb5Sswilcox 		    (int)parent, (int)victim);
517355d6bb5Sswilcox 
518355d6bb5Sswilcox 	if (!preen && (reply("REMOVE REFERENCE") == 0)) {
519355d6bb5Sswilcox 		iscorrupt = 1;
520355d6bb5Sswilcox 		return;
521355d6bb5Sswilcox 	}
522355d6bb5Sswilcox 
523355d6bb5Sswilcox 	dp = ginode(victim);
524355d6bb5Sswilcox 	if (parent == -1) {
525355d6bb5Sswilcox 		/*
526355d6bb5Sswilcox 		 * The file had a bad shadow/acl, so lock it down
527355d6bb5Sswilcox 		 * until someone can protect it the way they need it
528355d6bb5Sswilcox 		 * to be (i.e., be conservatively paranoid).
529355d6bb5Sswilcox 		 */
530355d6bb5Sswilcox 		dp->di_shadow = 0;
531355d6bb5Sswilcox 		dp->di_mode &= IFMT;
532355d6bb5Sswilcox 	} else {
533355d6bb5Sswilcox 		dp->di_oeftflag = 0;
534355d6bb5Sswilcox 	}
535355d6bb5Sswilcox 
536355d6bb5Sswilcox 	inodirty();
537355d6bb5Sswilcox 	if (preen)
538355d6bb5Sswilcox 		(void) printf(" (CORRECTED)\n");
539355d6bb5Sswilcox }
540355d6bb5Sswilcox 
541355d6bb5Sswilcox /*
542355d6bb5Sswilcox  * Check if we have holes in the directory's indirect
543355d6bb5Sswilcox  * blocks.  If there are, get rid of everything after
544355d6bb5Sswilcox  * the first hole.
545355d6bb5Sswilcox  */
546355d6bb5Sswilcox static void
check_dirholes(fsck_ino_t inumber,struct inodesc * idesc)547355d6bb5Sswilcox check_dirholes(fsck_ino_t inumber, struct inodesc *idesc)
548355d6bb5Sswilcox {
549355d6bb5Sswilcox 	char pathbuf[MAXPATHLEN + 1];
550355d6bb5Sswilcox 
551355d6bb5Sswilcox 	getpathname(pathbuf, idesc->id_number, idesc->id_number);
552355d6bb5Sswilcox 	pfatal("I=%d  DIRECTORY %s: CONTAINS EMPTY BLOCKS",
553355d6bb5Sswilcox 	    idesc->id_number, pathbuf);
554355d6bb5Sswilcox 	if (reply("TRUNCATE AT FIRST EMPTY BLOCK") == 1) {
555355d6bb5Sswilcox 		/*
556355d6bb5Sswilcox 		 * We found a hole, so get rid of it.
557355d6bb5Sswilcox 		 */
558355d6bb5Sswilcox 		collapse_dirhole(inumber, idesc);
559355d6bb5Sswilcox 
560355d6bb5Sswilcox 		if (preen)
561355d6bb5Sswilcox 			(void) printf(" (TRUNCATED)\n");
562355d6bb5Sswilcox 	} else {
563355d6bb5Sswilcox 		iscorrupt = 1;
564355d6bb5Sswilcox 	}
565355d6bb5Sswilcox }
566355d6bb5Sswilcox 
567355d6bb5Sswilcox /*
568355d6bb5Sswilcox  * Truncate a directory to its first hole.  If there are non-holes
569355d6bb5Sswilcox  * in the direct blocks after the problem block, move them down so
570355d6bb5Sswilcox  * that there's somewhat less lossage.  Doing this for indirect blocks
571355d6bb5Sswilcox  * is left as an exercise for the reader.
572355d6bb5Sswilcox  */
573355d6bb5Sswilcox static void
collapse_dirhole(fsck_ino_t inumber,struct inodesc * idesc)574355d6bb5Sswilcox collapse_dirhole(fsck_ino_t inumber, struct inodesc *idesc)
575355d6bb5Sswilcox {
576355d6bb5Sswilcox 	offset_t new_size;
577355d6bb5Sswilcox 	int blocks;
578355d6bb5Sswilcox 
579355d6bb5Sswilcox 	if (idesc->id_firsthole < 0) {
580355d6bb5Sswilcox 		return;
581355d6bb5Sswilcox 	}
582355d6bb5Sswilcox 
583355d6bb5Sswilcox 	/*
584355d6bb5Sswilcox 	 * Since truncino() adjusts the size, we don't need to do that here,
585355d6bb5Sswilcox 	 * but we have to tell it what final size we want.
586355d6bb5Sswilcox 	 *
587355d6bb5Sswilcox 	 * We need to count from block zero up through the last block
588355d6bb5Sswilcox 	 * before the hole.  If the hole is in the indirect blocks, chop at
589355d6bb5Sswilcox 	 * the start of the nearest level of indirection.  Orphans will
590355d6bb5Sswilcox 	 * get reconnected, so we're not actually losing anything by doing
591355d6bb5Sswilcox 	 * it this way, and we're simplifying truncation significantly.
592355d6bb5Sswilcox 	 */
593355d6bb5Sswilcox 	new_size = idesc->id_firsthole * (offset_t)sblock.fs_bsize;
594355d6bb5Sswilcox 	blocks = howmany(new_size, sblock.fs_bsize);
595355d6bb5Sswilcox 	if (blocks > NDADDR) {
596355d6bb5Sswilcox 		if (blocks < (NDADDR + NINDIR(&sblock)))
597355d6bb5Sswilcox 			blocks = NDADDR;
598355d6bb5Sswilcox 		else if (blocks < (NDADDR + NINDIR(&sblock) +
599355d6bb5Sswilcox 		    (NINDIR(&sblock) * NINDIR(&sblock))))
600355d6bb5Sswilcox 			blocks = NDADDR + NINDIR(&sblock);
601355d6bb5Sswilcox 		else
602355d6bb5Sswilcox 			blocks = NDADDR + NINDIR(&sblock) +
603*ce37393aSowenr 			    (NINDIR(&sblock) * NINDIR(&sblock));
604355d6bb5Sswilcox 		new_size = blocks * sblock.fs_bsize;
605355d6bb5Sswilcox 		if (debug)
606355d6bb5Sswilcox 			(void) printf("to %lld (blocks %d)\n",
607355d6bb5Sswilcox 			    (longlong_t)new_size, blocks);
608355d6bb5Sswilcox 	}
609355d6bb5Sswilcox 	truncino(inumber, new_size, TI_NOPARENT);
610355d6bb5Sswilcox 
611355d6bb5Sswilcox 	/*
612355d6bb5Sswilcox 	 * Technically, there are still the original number of fragments
613355d6bb5Sswilcox 	 * associated with the object.  However, that number is not used
614355d6bb5Sswilcox 	 * to control anything, so we can do the in-memory truncation of
615355d6bb5Sswilcox 	 * it without bad things happening.
616355d6bb5Sswilcox 	 */
617355d6bb5Sswilcox 	idesc->id_entryno = btodb(new_size);
6187c478bd9Sstevel@tonic-gate }
6197c478bd9Sstevel@tonic-gate 
620355d6bb5Sswilcox int
pass1check(struct inodesc * idesc)621355d6bb5Sswilcox pass1check(struct inodesc *idesc)
6227c478bd9Sstevel@tonic-gate {
6237c478bd9Sstevel@tonic-gate 	int res = KEEPON;
6247c478bd9Sstevel@tonic-gate 	int anyout;
625355d6bb5Sswilcox 	int nfrags;
626355d6bb5Sswilcox 	daddr32_t lbn;
627355d6bb5Sswilcox 	daddr32_t fragno = idesc->id_blkno;
628355d6bb5Sswilcox 	struct dinode *dp;
629355d6bb5Sswilcox 
630303bf60bSsdebnath 	/*
631303bf60bSsdebnath 	 * If this is a fallocate'd file, block numbers may be stored
632303bf60bSsdebnath 	 * as negative. In that case negate the negative numbers.
633303bf60bSsdebnath 	 */
634303bf60bSsdebnath 	dp = ginode(idesc->id_number);
635303bf60bSsdebnath 	if (dp->di_cflags & IFALLOCATE && fragno < 0)
636303bf60bSsdebnath 		fragno = -fragno;
637303bf60bSsdebnath 
638355d6bb5Sswilcox 	if ((anyout = chkrange(fragno, idesc->id_numfrags)) != 0) {
639355d6bb5Sswilcox 		/*
640355d6bb5Sswilcox 		 * Note that blkerror() exits when preening.
641355d6bb5Sswilcox 		 */
642355d6bb5Sswilcox 		blkerror(idesc->id_number, "OUT OF RANGE",
643355d6bb5Sswilcox 		    fragno, idesc->id_lbn * sblock.fs_frag);
644355d6bb5Sswilcox 
645355d6bb5Sswilcox 		dp = ginode(idesc->id_number);
646355d6bb5Sswilcox 		if ((((dp->di_mode & IFMT) == IFDIR) ||
647355d6bb5Sswilcox 		    ((dp->di_mode & IFMT) == IFATTRDIR)) &&
648355d6bb5Sswilcox 		    (idesc->id_firsthole < 0)) {
649355d6bb5Sswilcox 			idesc->id_firsthole = idesc->id_lbn;
650355d6bb5Sswilcox 		}
6517c478bd9Sstevel@tonic-gate 
6527c478bd9Sstevel@tonic-gate 		if (++badblk >= MAXBAD) {
653355d6bb5Sswilcox 			pwarn("EXCESSIVE BAD FRAGMENTS I=%u",
654355d6bb5Sswilcox 			    idesc->id_number);
655355d6bb5Sswilcox 			if (reply("CONTINUE") == 0)
656355d6bb5Sswilcox 				errexit("Program terminated.");
657355d6bb5Sswilcox 			/*
658355d6bb5Sswilcox 			 * See discussion below as to why we don't
659355d6bb5Sswilcox 			 * want to short-circuit the processing of
660355d6bb5Sswilcox 			 * this inode.  However, we know that this
661355d6bb5Sswilcox 			 * particular block is bad, so we don't need
662355d6bb5Sswilcox 			 * to go through the dup check loop.
663355d6bb5Sswilcox 			 */
664355d6bb5Sswilcox 			return (SKIP | STOP);
6657c478bd9Sstevel@tonic-gate 		}
6667c478bd9Sstevel@tonic-gate 	}
667355d6bb5Sswilcox 
668355d6bb5Sswilcox 	/*
669355d6bb5Sswilcox 	 * For each fragment, verify that it is a legal one (either
670355d6bb5Sswilcox 	 * by having already found the entire run to be legal, or by
671355d6bb5Sswilcox 	 * individual inspection), and if it is legal, see if we've
672355d6bb5Sswilcox 	 * seen it before or not.  If we haven't, note that we've seen
673355d6bb5Sswilcox 	 * it and continue on.  If we have (our in-core bitmap shows
674355d6bb5Sswilcox 	 * it as already being busy), then this must be a duplicate
675355d6bb5Sswilcox 	 * allocation.  Whine and moan accordingly.
676355d6bb5Sswilcox 	 *
677355d6bb5Sswilcox 	 * Note that for full-block allocations, this will produce
678355d6bb5Sswilcox 	 * a complaint for each fragment making up the block (i.e.,
679355d6bb5Sswilcox 	 * fs_frags' worth).  Among other things, this could be
680355d6bb5Sswilcox 	 * considered artificially inflating the dup-block count.
681355d6bb5Sswilcox 	 * However, since it is possible that one file has a full
682355d6bb5Sswilcox 	 * fs block allocated, but another is only claiming a frag
683355d6bb5Sswilcox 	 * or two out of the middle, we'll just live it.
684355d6bb5Sswilcox 	 */
685355d6bb5Sswilcox 	for (nfrags = 0; nfrags < idesc->id_numfrags; fragno++, nfrags++) {
686355d6bb5Sswilcox 		if (anyout && chkrange(fragno, 1)) {
687355d6bb5Sswilcox 			/* bad fragment number */
6887c478bd9Sstevel@tonic-gate 			res = SKIP;
689355d6bb5Sswilcox 		} else if (!testbmap(fragno)) {
690355d6bb5Sswilcox 			/* no other claims seen as yet */
691355d6bb5Sswilcox 			note_used(fragno);
6927c478bd9Sstevel@tonic-gate 		} else {
693355d6bb5Sswilcox 			/*
694355d6bb5Sswilcox 			 * We have a duplicate claim for the same fragment.
695355d6bb5Sswilcox 			 *
696355d6bb5Sswilcox 			 * blkerror() exits when preening.
697355d6bb5Sswilcox 			 *
698355d6bb5Sswilcox 			 * We want to report all the dups up until
699355d6bb5Sswilcox 			 * hitting MAXDUP.  Fortunately, blkerror()'s
700355d6bb5Sswilcox 			 * side-effects on statemap[] are idempotent,
701355d6bb5Sswilcox 			 * so the ``extra'' calls are harmless.
702355d6bb5Sswilcox 			 */
703355d6bb5Sswilcox 			lbn = idesc->id_lbn * sblock.fs_frag + nfrags;
704355d6bb5Sswilcox 			if (dupblk < MAXDUP)
705355d6bb5Sswilcox 				blkerror(idesc->id_number, "DUP", fragno, lbn);
706355d6bb5Sswilcox 
707355d6bb5Sswilcox 			/*
708355d6bb5Sswilcox 			 * Use ==, so we only complain once, no matter
709355d6bb5Sswilcox 			 * how far over the limit we end up going.
710355d6bb5Sswilcox 			 */
711355d6bb5Sswilcox 			if (++dupblk == MAXDUP) {
712355d6bb5Sswilcox 				pwarn("EXCESSIVE DUPLICATE FRAGMENTS I=%u",
713*ce37393aSowenr 				    idesc->id_number);
7147c478bd9Sstevel@tonic-gate 				if (reply("CONTINUE") == 0)
715355d6bb5Sswilcox 					errexit("Program terminated.");
716355d6bb5Sswilcox 
717355d6bb5Sswilcox 				/*
718355d6bb5Sswilcox 				 * If we stop the traversal here, then
719355d6bb5Sswilcox 				 * there may be more dups in the
720355d6bb5Sswilcox 				 * inode's block list that don't get
721355d6bb5Sswilcox 				 * flagged.  Later, if we're told to
722355d6bb5Sswilcox 				 * clear one of the files claiming
723355d6bb5Sswilcox 				 * these blocks, but not the other, we
724355d6bb5Sswilcox 				 * will release blocks that are
725355d6bb5Sswilcox 				 * actually still in use.  An additional
726355d6bb5Sswilcox 				 * fsck run would be necessary to undo
727355d6bb5Sswilcox 				 * the damage.  So, instead of the
728355d6bb5Sswilcox 				 * traditional return (STOP) when told
729355d6bb5Sswilcox 				 * to continue, we really do just continue.
730355d6bb5Sswilcox 				 */
7317c478bd9Sstevel@tonic-gate 			}
732355d6bb5Sswilcox 			(void) find_dup_ref(fragno, idesc->id_number, lbn,
733*ce37393aSowenr 			    DB_CREATE | DB_INCR);
7347c478bd9Sstevel@tonic-gate 		}
7357c478bd9Sstevel@tonic-gate 		/*
736355d6bb5Sswilcox 		 * id_entryno counts the number of disk blocks found.
7377c478bd9Sstevel@tonic-gate 		 */
738355d6bb5Sswilcox 		idesc->id_entryno += btodb(sblock.fs_fsize);
7397c478bd9Sstevel@tonic-gate 	}
7407c478bd9Sstevel@tonic-gate 	return (res);
7417c478bd9Sstevel@tonic-gate }
742355d6bb5Sswilcox 
743355d6bb5Sswilcox static void
note_used(daddr32_t frag)744355d6bb5Sswilcox note_used(daddr32_t frag)
745355d6bb5Sswilcox {
746355d6bb5Sswilcox 	n_blks++;
747355d6bb5Sswilcox 	setbmap(frag);
748355d6bb5Sswilcox }
749