xref: /illumos-gate/usr/src/cmd/fs.d/ufs/fsck/inode.c (revision 2ee92411)
17c478bd9Sstevel@tonic-gate /*
2*2ee92411SAndrew Balfour  * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
37c478bd9Sstevel@tonic-gate  */
47c478bd9Sstevel@tonic-gate 
57c478bd9Sstevel@tonic-gate /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
67c478bd9Sstevel@tonic-gate /*	  All Rights Reserved  	*/
77c478bd9Sstevel@tonic-gate 
87c478bd9Sstevel@tonic-gate /*
97c478bd9Sstevel@tonic-gate  * Copyright (c) 1980, 1986, 1990 The Regents of the University of California.
107c478bd9Sstevel@tonic-gate  * All rights reserved.
117c478bd9Sstevel@tonic-gate  *
127c478bd9Sstevel@tonic-gate  * Redistribution and use in source and binary forms are permitted
137c478bd9Sstevel@tonic-gate  * provided that: (1) source distributions retain this entire copyright
147c478bd9Sstevel@tonic-gate  * notice and comment, and (2) distributions including binaries display
157c478bd9Sstevel@tonic-gate  * the following acknowledgement:  ``This product includes software
167c478bd9Sstevel@tonic-gate  * developed by the University of California, Berkeley and its contributors''
177c478bd9Sstevel@tonic-gate  * in the documentation or other materials provided with the distribution
187c478bd9Sstevel@tonic-gate  * and in all advertising materials mentioning features or use of this
197c478bd9Sstevel@tonic-gate  * software. Neither the name of the University nor the names of its
207c478bd9Sstevel@tonic-gate  * contributors may be used to endorse or promote products derived
217c478bd9Sstevel@tonic-gate  * from this software without specific prior written permission.
227c478bd9Sstevel@tonic-gate  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
237c478bd9Sstevel@tonic-gate  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
247c478bd9Sstevel@tonic-gate  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate 
287c478bd9Sstevel@tonic-gate #include <stdio.h>
297c478bd9Sstevel@tonic-gate #include <string.h>
307c478bd9Sstevel@tonic-gate #include <stdlib.h>
317c478bd9Sstevel@tonic-gate #include <unistd.h>
327c478bd9Sstevel@tonic-gate #include <time.h>
33355d6bb5Sswilcox #include <limits.h>
347c478bd9Sstevel@tonic-gate #include <sys/param.h>
357c478bd9Sstevel@tonic-gate #include <sys/types.h>
367c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
377c478bd9Sstevel@tonic-gate #include <sys/mntent.h>
387c478bd9Sstevel@tonic-gate #include <sys/vnode.h>
397c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_inode.h>
407c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_fs.h>
41355d6bb5Sswilcox #define	_KERNEL
427c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_fsdir.h>
43355d6bb5Sswilcox #undef _KERNEL
447c478bd9Sstevel@tonic-gate #include <pwd.h>
457c478bd9Sstevel@tonic-gate #include "fsck.h"
467c478bd9Sstevel@tonic-gate 
47355d6bb5Sswilcox static int get_indir_offsets(int, daddr_t, int *, int *);
48355d6bb5Sswilcox static int clearanentry(struct inodesc *);
49355d6bb5Sswilcox static void pdinode(struct dinode *);
50355d6bb5Sswilcox static void inoflush(void);
51355d6bb5Sswilcox static void mark_delayed_inodes(fsck_ino_t, daddr32_t);
52355d6bb5Sswilcox static int iblock(struct inodesc *, int, u_offset_t, enum cki_action);
53355d6bb5Sswilcox static struct inoinfo *search_cache(struct inoinfo *, fsck_ino_t);
54355d6bb5Sswilcox static int ckinode_common(struct dinode *, struct inodesc *, enum cki_action);
55355d6bb5Sswilcox static int lookup_dotdot_ino(fsck_ino_t);
56355d6bb5Sswilcox 
57355d6bb5Sswilcox /*
58355d6bb5Sswilcox  * ckinode() essentially traverses the blocklist of the provided
59355d6bb5Sswilcox  * inode.  For each block either the caller-supplied callback (id_func
60355d6bb5Sswilcox  * in the provided struct inodesc) or dirscan() is invoked.  Which is
61355d6bb5Sswilcox  * chosen is controlled by what type of traversal was requested
62355d6bb5Sswilcox  * (id_type) - if it was for an ADDR or ACL, use the callback,
63355d6bb5Sswilcox  * otherwise it is assumed to be DATA (i.e., a directory) whose
64355d6bb5Sswilcox  * contents need to be scanned.
65355d6bb5Sswilcox  *
66355d6bb5Sswilcox  * Note that a directory inode can get passed in with a type of ADDR;
67355d6bb5Sswilcox  * the type field is orthogonal to the IFMT value.  This is so that
68355d6bb5Sswilcox  * the file aspects (no duplicate blocks, etc) of a directory can be
69355d6bb5Sswilcox  * verified just like is done for any other file, or the actual
70355d6bb5Sswilcox  * contents can be scanned so that connectivity and such can be
71355d6bb5Sswilcox  * investigated.
72355d6bb5Sswilcox  *
73355d6bb5Sswilcox  * The traversal is controlled by flags in the return value of
74355d6bb5Sswilcox  * dirscan() or the callback.  Five flags are defined, STOP, SKIP,
75355d6bb5Sswilcox  * KEEPON, ALTERED, and FOUND.  Their semantics are:
76355d6bb5Sswilcox  *
77355d6bb5Sswilcox  *     STOP -    no further processing of this inode is desired/possible/
78355d6bb5Sswilcox  *               feasible/etc.  This can mean that whatever the scan
79355d6bb5Sswilcox  *               was searching for was found, or a serious
80355d6bb5Sswilcox  *               inconsistency was encountered, or anything else
81355d6bb5Sswilcox  *               appropriate.
82355d6bb5Sswilcox  *
83355d6bb5Sswilcox  *     SKIP -    something that made it impossible to continue was
84355d6bb5Sswilcox  *               encountered, and the caller should go on to the next
85355d6bb5Sswilcox  *               inode.  This is more for i/o failures than for
86355d6bb5Sswilcox  *               logical inconsistencies.  Nothing actually looks for
87355d6bb5Sswilcox  *               this.
88355d6bb5Sswilcox  *
89355d6bb5Sswilcox  *     KEEPON -  no more blocks of this inode need to be scanned, but
90355d6bb5Sswilcox  *               nothing's wrong, so keep on going with the next
91355d6bb5Sswilcox  *               inode.  It is similar to STOP, except that
92355d6bb5Sswilcox  *               ckinode()'s caller will typically advance to the next
93355d6bb5Sswilcox  *               inode for KEEPON, whereas it ceases scanning through
94355d6bb5Sswilcox  *               the inodes completely for STOP.
95355d6bb5Sswilcox  *
96355d6bb5Sswilcox  *     ALTERED - a change was made to the inode.  If the caller sees
97355d6bb5Sswilcox  *               this set, it should make sure to flush out the
98355d6bb5Sswilcox  *               changes.  Note that any data blocks read in by the
99355d6bb5Sswilcox  *               function need to be marked dirty by it directly;
100355d6bb5Sswilcox  *               flushing of those will happen automatically later.
101355d6bb5Sswilcox  *
102355d6bb5Sswilcox  *     FOUND -   whatever was being searched for was located.
103355d6bb5Sswilcox  *               Typically combined with STOP to avoid wasting time
104355d6bb5Sswilcox  *               doing additional looking.
105355d6bb5Sswilcox  *
106355d6bb5Sswilcox  * During a traversal, some state needs to be carried around.  At the
107355d6bb5Sswilcox  * least, the callback functions need to know what inode they're
108355d6bb5Sswilcox  * working on, which logical block, and whether or not fixing problems
109355d6bb5Sswilcox  * when they're encountered is desired.  Rather than try to guess what
110355d6bb5Sswilcox  * else might be needed (and thus end up passing way more arguments
111355d6bb5Sswilcox  * than is reasonable), all the possibilities have been bundled in
112355d6bb5Sswilcox  * struct inodesc.  About half of the fields are specific to directory
113355d6bb5Sswilcox  * traversals, and the rest are pretty much generic to any traversal.
114355d6bb5Sswilcox  *
115355d6bb5Sswilcox  * The general fields are:
116355d6bb5Sswilcox  *
117355d6bb5Sswilcox  *     id_fix        What to do when an error is found.  Generally, this
118355d6bb5Sswilcox  *                   is set to DONTKNOW before a traversal.  If a
119355d6bb5Sswilcox  *                   problem is encountered, it is changed to either FIX
120355d6bb5Sswilcox  *                   or NOFIX by the dofix() query function.  If id_fix
121355d6bb5Sswilcox  *                   has already been set to FIX when dofix() is called, then
122355d6bb5Sswilcox  *                   it includes the ALTERED flag (see above) in its return
123355d6bb5Sswilcox  *                   value; the net effect is that the inode's buffer
124355d6bb5Sswilcox  *                   will get marked dirty and written to disk at some
125355d6bb5Sswilcox  *                   point.  If id_fix is DONTKNOW, then dofix() will
126355d6bb5Sswilcox  *                   query the user.  If it is NOFIX, then dofix()
127355d6bb5Sswilcox  *                   essentially does nothing.  A few routines set NOFIX
128355d6bb5Sswilcox  *                   as the initial value, as they are performing a best-
129355d6bb5Sswilcox  *                   effort informational task, rather than an actual
130355d6bb5Sswilcox  *                   repair operation.
131355d6bb5Sswilcox  *
132355d6bb5Sswilcox  *     id_func       This is the function that will be called for every
133355d6bb5Sswilcox  *                   logical block in the file (assuming id_type is not
134355d6bb5Sswilcox  *                   DATA).  The logical block may represent a hole, so
135355d6bb5Sswilcox  *                   the callback needs to be prepared to handle that
136355d6bb5Sswilcox  *                   case.  Its return value is a combination of the flags
137355d6bb5Sswilcox  *                   described above (SKIP, ALTERED, etc).
138355d6bb5Sswilcox  *
139355d6bb5Sswilcox  *     id_number     The inode number whose block list or data is being
140355d6bb5Sswilcox  *                   scanned.
141355d6bb5Sswilcox  *
142355d6bb5Sswilcox  *     id_parent     When id_type is DATA, this is the inode number for
143355d6bb5Sswilcox  *                   the parent of id_number.  Otherwise, it is
144355d6bb5Sswilcox  *                   available for use as an extra parameter or return
145355d6bb5Sswilcox  *                   value between the callback and ckinode()'s caller.
146355d6bb5Sswilcox  *                   Which, if either, of those is left completely up to
147355d6bb5Sswilcox  *                   the two routines involved, so nothing can generally
148355d6bb5Sswilcox  *                   be assumed about the id_parent value for non-DATA
149355d6bb5Sswilcox  *                   traversals.
150355d6bb5Sswilcox  *
151355d6bb5Sswilcox  *     id_lbn        This is the current logical block (not fragment)
152355d6bb5Sswilcox  *                   number being visited by the traversal.
153355d6bb5Sswilcox  *
154355d6bb5Sswilcox  *     id_blkno      This is the physical block corresponding to id_lbn.
155355d6bb5Sswilcox  *
156355d6bb5Sswilcox  *     id_numfrags   This defines how large a block is being processed in
157355d6bb5Sswilcox  *                   this particular invocation of the callback.
158355d6bb5Sswilcox  *                   Usually, it will be the same as sblock.fs_frag.
159355d6bb5Sswilcox  *                   However, if a direct block is being processed and
160355d6bb5Sswilcox  *                   it is less than a full filesystem block,
161355d6bb5Sswilcox  *                   id_numfrags will indicate just how many fragments
162355d6bb5Sswilcox  *                   (starting from id_lbn) are actually part of the
163355d6bb5Sswilcox  *                   file.
164355d6bb5Sswilcox  *
165355d6bb5Sswilcox  *     id_truncto    The pass 4 callback is used in several places to
166355d6bb5Sswilcox  *                   free the blocks of a file (the `FILE HAS PROBLEM
167355d6bb5Sswilcox  *                   FOO; CLEAR?' scenario).  This has been generalized
168355d6bb5Sswilcox  *                   to allow truncating a file to a particular length
169355d6bb5Sswilcox  *                   rather than always completely discarding it.  If
170355d6bb5Sswilcox  *                   id_truncto is -1, then the entire file is released,
171355d6bb5Sswilcox  *                   otherwise it is logical block number to truncate
172355d6bb5Sswilcox  *                   to.  This generalized interface was motivated by a
173355d6bb5Sswilcox  *                   desire to be able to discard everything after a
174355d6bb5Sswilcox  *                   hole in a directory, rather than the entire
175355d6bb5Sswilcox  *                   directory.
176355d6bb5Sswilcox  *
177355d6bb5Sswilcox  *     id_type       Selects the type of traversal.  DATA for dirscan(),
178355d6bb5Sswilcox  *                   ADDR or ACL for using the provided callback.
179355d6bb5Sswilcox  *
180355d6bb5Sswilcox  * There are several more fields used just for dirscan() traversals:
181355d6bb5Sswilcox  *
182355d6bb5Sswilcox  *     id_filesize   The number of bytes in the overall directory left to
183355d6bb5Sswilcox  *                   process.
184355d6bb5Sswilcox  *
185355d6bb5Sswilcox  *     id_loc        Byte position within the directory block.  Should always
186355d6bb5Sswilcox  *                   point to the start of a directory entry.
187355d6bb5Sswilcox  *
188355d6bb5Sswilcox  *     id_entryno    Which logical directory entry is being processed (0
189355d6bb5Sswilcox  *                   is `.', 1 is `..', 2 and on are normal entries).
190355d6bb5Sswilcox  *                   This field is primarily used to enable special
191355d6bb5Sswilcox  *                   checks when looking at the first two entries.
192355d6bb5Sswilcox  *
193355d6bb5Sswilcox  *                   The exception (there's always an exception in fsck)
194355d6bb5Sswilcox  *                   is that in pass 1, it tracks how many fragments are
195355d6bb5Sswilcox  *                   being used by a particular inode.
196355d6bb5Sswilcox  *
197355d6bb5Sswilcox  *     id_firsthole  The first logical block number that was found to
198355d6bb5Sswilcox  *                   be zero.  As directories are not supposed to have
199355d6bb5Sswilcox  *                   holes, this marks where a directory should be
200355d6bb5Sswilcox  *                   truncated down to.  A value of -1 indicates that
201355d6bb5Sswilcox  *                   no holes were found.
202355d6bb5Sswilcox  *
203355d6bb5Sswilcox  *     id_dirp       A pointer to the in-memory copy of the current
204355d6bb5Sswilcox  *                   directory entry (as identified by id_loc).
205355d6bb5Sswilcox  *
206355d6bb5Sswilcox  *     id_name       This is a directory entry name to either create
207355d6bb5Sswilcox  *                   (callback is mkentry) or locate (callback is
208355d6bb5Sswilcox  *                   chgino, findino, or findname).
209355d6bb5Sswilcox  */
210355d6bb5Sswilcox int
ckinode(struct dinode * dp,struct inodesc * idesc,enum cki_action action)211355d6bb5Sswilcox ckinode(struct dinode *dp, struct inodesc *idesc, enum cki_action action)
212355d6bb5Sswilcox {
213355d6bb5Sswilcox 	struct inodesc cleardesc;
214355d6bb5Sswilcox 	mode_t	mode;
215355d6bb5Sswilcox 
216355d6bb5Sswilcox 	if (idesc->id_filesize == 0)
217355d6bb5Sswilcox 		idesc->id_filesize = (offset_t)dp->di_size;
2187c478bd9Sstevel@tonic-gate 
219355d6bb5Sswilcox 	/*
220355d6bb5Sswilcox 	 * Our caller should be filtering out completely-free inodes
221355d6bb5Sswilcox 	 * (mode == zero), so we'll work on the assumption that what
222355d6bb5Sswilcox 	 * we're given has some basic validity.
223355d6bb5Sswilcox 	 *
224355d6bb5Sswilcox 	 * The kernel is inconsistent about MAXPATHLEN including the
225355d6bb5Sswilcox 	 * trailing \0, so allow the more-generous length for symlinks.
226355d6bb5Sswilcox 	 */
227355d6bb5Sswilcox 	mode = dp->di_mode & IFMT;
228355d6bb5Sswilcox 	if (mode == IFBLK || mode == IFCHR)
229355d6bb5Sswilcox 		return (KEEPON);
230355d6bb5Sswilcox 	if (mode == IFLNK && dp->di_size > MAXPATHLEN) {
231*2ee92411SAndrew Balfour 		pwarn("I=%d  Symlink longer than supported maximum\n",
232355d6bb5Sswilcox 		    idesc->id_number);
233355d6bb5Sswilcox 		init_inodesc(&cleardesc);
234355d6bb5Sswilcox 		cleardesc.id_type = ADDR;
235355d6bb5Sswilcox 		cleardesc.id_number = idesc->id_number;
236355d6bb5Sswilcox 		cleardesc.id_fix = DONTKNOW;
237355d6bb5Sswilcox 		clri(&cleardesc, "BAD", CLRI_VERBOSE, CLRI_NOP_CORRUPT);
238355d6bb5Sswilcox 		return (STOP);
239355d6bb5Sswilcox 	}
240355d6bb5Sswilcox 	return (ckinode_common(dp, idesc, action));
241355d6bb5Sswilcox }
2427c478bd9Sstevel@tonic-gate 
243355d6bb5Sswilcox /*
244355d6bb5Sswilcox  * This was split out from ckinode() to allow it to be used
245355d6bb5Sswilcox  * without having to pass in kludge flags to suppress the
246355d6bb5Sswilcox  * wrong-for-deletion initialization and irrelevant checks.
247355d6bb5Sswilcox  * This feature is no longer needed, but is being kept in case
248355d6bb5Sswilcox  * the need comes back.
249355d6bb5Sswilcox  */
250355d6bb5Sswilcox static int
ckinode_common(struct dinode * dp,struct inodesc * idesc,enum cki_action action)251355d6bb5Sswilcox ckinode_common(struct dinode *dp, struct inodesc *idesc,
252355d6bb5Sswilcox 	enum cki_action action)
2537c478bd9Sstevel@tonic-gate {
254355d6bb5Sswilcox 	offset_t offset;
2557c478bd9Sstevel@tonic-gate 	struct dinode dino;
256355d6bb5Sswilcox 	daddr_t ndb;
257355d6bb5Sswilcox 	int indir_data_blks, last_indir_blk;
258355d6bb5Sswilcox 	int ret, i, frags;
2597c478bd9Sstevel@tonic-gate 
260355d6bb5Sswilcox 	(void) memmove(&dino, dp, sizeof (struct dinode));
2617c478bd9Sstevel@tonic-gate 	ndb = howmany(dino.di_size, (u_offset_t)sblock.fs_bsize);
262355d6bb5Sswilcox 
263355d6bb5Sswilcox 	for (i = 0; i < NDADDR; i++) {
264355d6bb5Sswilcox 		idesc->id_lbn++;
265355d6bb5Sswilcox 		offset = blkoff(&sblock, dino.di_size);
266355d6bb5Sswilcox 		if ((--ndb == 0) && (offset != 0)) {
2677c478bd9Sstevel@tonic-gate 			idesc->id_numfrags =
2687c478bd9Sstevel@tonic-gate 			    numfrags(&sblock, fragroundup(&sblock, offset));
269355d6bb5Sswilcox 		} else {
2707c478bd9Sstevel@tonic-gate 			idesc->id_numfrags = sblock.fs_frag;
271355d6bb5Sswilcox 		}
272355d6bb5Sswilcox 		if (dino.di_db[i] == 0) {
273355d6bb5Sswilcox 			if ((ndb > 0) && (idesc->id_firsthole < 0)) {
274355d6bb5Sswilcox 				idesc->id_firsthole = i;
275355d6bb5Sswilcox 			}
2767c478bd9Sstevel@tonic-gate 			continue;
277355d6bb5Sswilcox 		}
278355d6bb5Sswilcox 		idesc->id_blkno = dino.di_db[i];
2797c478bd9Sstevel@tonic-gate 		if (idesc->id_type == ADDR || idesc->id_type == ACL)
2807c478bd9Sstevel@tonic-gate 			ret = (*idesc->id_func)(idesc);
2817c478bd9Sstevel@tonic-gate 		else
2827c478bd9Sstevel@tonic-gate 			ret = dirscan(idesc);
283355d6bb5Sswilcox 
284355d6bb5Sswilcox 		/*
285355d6bb5Sswilcox 		 * Need to clear the entry, now that we're done with
286355d6bb5Sswilcox 		 * it.  We depend on freeblk() ignoring a request to
287355d6bb5Sswilcox 		 * free already-free fragments to handle the problem of
288355d6bb5Sswilcox 		 * a partial block.
289355d6bb5Sswilcox 		 */
290355d6bb5Sswilcox 		if ((action == CKI_TRUNCATE) &&
291355d6bb5Sswilcox 		    (idesc->id_truncto >= 0) &&
292355d6bb5Sswilcox 		    (idesc->id_lbn >= idesc->id_truncto)) {
293355d6bb5Sswilcox 			dp = ginode(idesc->id_number);
294355d6bb5Sswilcox 			/*
295355d6bb5Sswilcox 			 * The (int) cast is safe, in that if di_size won't
296355d6bb5Sswilcox 			 * fit, it'll be a multiple of any legal fs_frag,
297355d6bb5Sswilcox 			 * thus giving a zero result.  That value, in turn
298355d6bb5Sswilcox 			 * means we're doing an entire block.
299355d6bb5Sswilcox 			 */
300355d6bb5Sswilcox 			frags = howmany((int)dp->di_size, sblock.fs_fsize) %
301355d6bb5Sswilcox 			    sblock.fs_frag;
302355d6bb5Sswilcox 			if (frags == 0)
303355d6bb5Sswilcox 				frags = sblock.fs_frag;
304355d6bb5Sswilcox 			freeblk(idesc->id_number, dp->di_db[i],
305355d6bb5Sswilcox 			    frags);
306355d6bb5Sswilcox 			dp = ginode(idesc->id_number);
307355d6bb5Sswilcox 			dp->di_db[i] = 0;
308355d6bb5Sswilcox 			inodirty();
309355d6bb5Sswilcox 			ret |= ALTERED;
310355d6bb5Sswilcox 		}
311355d6bb5Sswilcox 
3127c478bd9Sstevel@tonic-gate 		if (ret & STOP)
3137c478bd9Sstevel@tonic-gate 			return (ret);
3147c478bd9Sstevel@tonic-gate 	}
3157c478bd9Sstevel@tonic-gate 
316355d6bb5Sswilcox #ifdef lint
3177c478bd9Sstevel@tonic-gate 	/*
318355d6bb5Sswilcox 	 * Cure a lint complaint of ``possible use before set''.
319355d6bb5Sswilcox 	 * Apparently it can't quite figure out the switch statement.
3207c478bd9Sstevel@tonic-gate 	 */
321355d6bb5Sswilcox 	indir_data_blks = 0;
322355d6bb5Sswilcox #endif
323355d6bb5Sswilcox 	/*
324355d6bb5Sswilcox 	 * indir_data_blks contains the number of data blocks in all
325355d6bb5Sswilcox 	 * the previous levels for this iteration.  E.g., for the
326355d6bb5Sswilcox 	 * single indirect case (i = 0, di_ib[i] != 0), NDADDR's worth
327355d6bb5Sswilcox 	 * of blocks have already been covered by the direct blocks
328355d6bb5Sswilcox 	 * (di_db[]).  At the triple indirect level (i = NIADDR - 1),
329355d6bb5Sswilcox 	 * it is all of the number of data blocks that were covered
330355d6bb5Sswilcox 	 * by the second indirect, single indirect, and direct block
331355d6bb5Sswilcox 	 * levels.
332355d6bb5Sswilcox 	 */
333355d6bb5Sswilcox 	idesc->id_numfrags = sblock.fs_frag;
334355d6bb5Sswilcox 	ndb = howmany(dino.di_size, (u_offset_t)sblock.fs_bsize);
335355d6bb5Sswilcox 	for (i = 0; i < NIADDR; i++) {
336355d6bb5Sswilcox 		(void) get_indir_offsets(i, ndb, &indir_data_blks,
337355d6bb5Sswilcox 		    &last_indir_blk);
338355d6bb5Sswilcox 		if (dino.di_ib[i] != 0) {
339355d6bb5Sswilcox 			/*
340355d6bb5Sswilcox 			 * We'll only clear di_ib[i] if the first entry (and
341355d6bb5Sswilcox 			 * therefore all of them) is to be cleared, since we
342355d6bb5Sswilcox 			 * only go through this code on the first entry of
343355d6bb5Sswilcox 			 * each level of indirection.  The +1 is to account
344355d6bb5Sswilcox 			 * for the fact that we don't modify id_lbn until
345355d6bb5Sswilcox 			 * we actually start processing on a data block.
346355d6bb5Sswilcox 			 */
347355d6bb5Sswilcox 			idesc->id_blkno = dino.di_ib[i];
348355d6bb5Sswilcox 			ret = iblock(idesc, i + 1,
3497c478bd9Sstevel@tonic-gate 			    (u_offset_t)howmany(dino.di_size,
350592beac6Smc 			    (u_offset_t)sblock.fs_bsize) - indir_data_blks,
351592beac6Smc 			    action);
352355d6bb5Sswilcox 			if ((action == CKI_TRUNCATE) &&
353355d6bb5Sswilcox 			    (idesc->id_truncto <= indir_data_blks) &&
354355d6bb5Sswilcox 			    ((idesc->id_lbn + 1) >= indir_data_blks) &&
355355d6bb5Sswilcox 			    ((idesc->id_lbn + 1) <= last_indir_blk)) {
356355d6bb5Sswilcox 				dp = ginode(idesc->id_number);
357355d6bb5Sswilcox 				if (dp->di_ib[i] != 0) {
358355d6bb5Sswilcox 					freeblk(idesc->id_number, dp->di_ib[i],
359355d6bb5Sswilcox 					    sblock.fs_frag);
360355d6bb5Sswilcox 				}
361355d6bb5Sswilcox 			}
3627c478bd9Sstevel@tonic-gate 			if (ret & STOP)
3637c478bd9Sstevel@tonic-gate 				return (ret);
3647c478bd9Sstevel@tonic-gate 		} else {
365355d6bb5Sswilcox 			/*
366355d6bb5Sswilcox 			 * Need to know which of the file's logical blocks
367355d6bb5Sswilcox 			 * reside in the missing indirect block.  However, the
368355d6bb5Sswilcox 			 * precise location is only needed for truncating
369355d6bb5Sswilcox 			 * directories, and level-of-indirection precision is
370355d6bb5Sswilcox 			 * sufficient for that.
371355d6bb5Sswilcox 			 */
372355d6bb5Sswilcox 			if ((indir_data_blks < ndb) &&
373355d6bb5Sswilcox 			    (idesc->id_firsthole < 0)) {
374355d6bb5Sswilcox 				idesc->id_firsthole = indir_data_blks;
375355d6bb5Sswilcox 			}
3767c478bd9Sstevel@tonic-gate 		}
3777c478bd9Sstevel@tonic-gate 	}
3787c478bd9Sstevel@tonic-gate 	return (KEEPON);
3797c478bd9Sstevel@tonic-gate }
3807c478bd9Sstevel@tonic-gate 
381355d6bb5Sswilcox static int
get_indir_offsets(int ilevel_wanted,daddr_t ndb,int * data_blks,int * last_blk)382355d6bb5Sswilcox get_indir_offsets(int ilevel_wanted, daddr_t ndb, int *data_blks,
383355d6bb5Sswilcox 	int *last_blk)
384355d6bb5Sswilcox {
385355d6bb5Sswilcox 	int ndb_ilevel = -1;
3867c478bd9Sstevel@tonic-gate 	int ilevel;
387355d6bb5Sswilcox 	int dblks, lblk;
388355d6bb5Sswilcox 
389355d6bb5Sswilcox 	for (ilevel = 0; ilevel < NIADDR; ilevel++) {
390355d6bb5Sswilcox 		switch (ilevel) {
391355d6bb5Sswilcox 		case 0:	/* SINGLE */
392355d6bb5Sswilcox 			dblks = NDADDR;
393355d6bb5Sswilcox 			lblk = dblks + NINDIR(&sblock) - 1;
394355d6bb5Sswilcox 			break;
395355d6bb5Sswilcox 		case 1:	/* DOUBLE */
396355d6bb5Sswilcox 			dblks = NDADDR + NINDIR(&sblock);
397355d6bb5Sswilcox 			lblk = dblks + (NINDIR(&sblock) * NINDIR(&sblock)) - 1;
398355d6bb5Sswilcox 			break;
399355d6bb5Sswilcox 		case 2:	/* TRIPLE */
400355d6bb5Sswilcox 			dblks = NDADDR + NINDIR(&sblock) +
401355d6bb5Sswilcox 			    (NINDIR(&sblock) * NINDIR(&sblock));
402355d6bb5Sswilcox 			lblk = dblks + (NINDIR(&sblock) * NINDIR(&sblock) *
403355d6bb5Sswilcox 			    NINDIR(&sblock)) - 1;
404355d6bb5Sswilcox 			break;
405355d6bb5Sswilcox 		default:
406355d6bb5Sswilcox 			exitstat = EXERRFATAL;
407355d6bb5Sswilcox 			/*
408355d6bb5Sswilcox 			 * Translate from zero-based array to
409355d6bb5Sswilcox 			 * one-based human-style counting.
410355d6bb5Sswilcox 			 */
411355d6bb5Sswilcox 			errexit("panic: indirection level %d not 1, 2, or 3",
412355d6bb5Sswilcox 			    ilevel + 1);
413355d6bb5Sswilcox 			/* NOTREACHED */
414355d6bb5Sswilcox 		}
415355d6bb5Sswilcox 
416355d6bb5Sswilcox 		if (dblks < ndb && ndb <= lblk)
417355d6bb5Sswilcox 			ndb_ilevel = ilevel;
418355d6bb5Sswilcox 
419355d6bb5Sswilcox 		if (ilevel == ilevel_wanted) {
420355d6bb5Sswilcox 			if (data_blks != NULL)
421355d6bb5Sswilcox 				*data_blks = dblks;
422355d6bb5Sswilcox 			if (last_blk != NULL)
423355d6bb5Sswilcox 				*last_blk = lblk;
424355d6bb5Sswilcox 		}
425355d6bb5Sswilcox 	}
426355d6bb5Sswilcox 
427355d6bb5Sswilcox 	return (ndb_ilevel);
428355d6bb5Sswilcox }
429355d6bb5Sswilcox 
430355d6bb5Sswilcox static int
iblock(struct inodesc * idesc,int ilevel,u_offset_t iblks,enum cki_action action)431