xref: /titanic_44/usr/src/cmd/fs.d/ufs/fsck/inode.c (revision 2ee92411ae65ea7cb80c2a46adfc22b983dcea7f)
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);
567c478bd9Sstevel@tonic-gate 
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)
2127c478bd9Sstevel@tonic-gate {
213355d6bb5Sswilcox 	struct inodesc cleardesc;
214355d6bb5Sswilcox 	mode_t	mode;
2157c478bd9Sstevel@tonic-gate 
216355d6bb5Sswilcox 	if (idesc->id_filesize == 0)
2177c478bd9Sstevel@tonic-gate 		idesc->id_filesize = (offset_t)dp->di_size;
218355d6bb5Sswilcox 
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)
2297c478bd9Sstevel@tonic-gate 		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 }
242355d6bb5Sswilcox 
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)
253355d6bb5Sswilcox {
254355d6bb5Sswilcox 	offset_t offset;
255355d6bb5Sswilcox 	struct dinode dino;
256355d6bb5Sswilcox 	daddr_t ndb;
257355d6bb5Sswilcox 	int indir_data_blks, last_indir_blk;
258355d6bb5Sswilcox 	int ret, i, frags;
259355d6bb5Sswilcox 
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,
350592beac6Smc208700 			    (u_offset_t)sblock.fs_bsize) - indir_data_blks,
351355d6bb5Sswilcox 			    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)
3847c478bd9Sstevel@tonic-gate {
385355d6bb5Sswilcox 	int ndb_ilevel = -1;
386355d6bb5Sswilcox 	int ilevel;
387355d6bb5Sswilcox 	int dblks, lblk;
3887c478bd9Sstevel@tonic-gate 
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)431355d6bb5Sswilcox iblock(struct inodesc *idesc, int ilevel, u_offset_t iblks,
432355d6bb5Sswilcox 	enum cki_action action)
433355d6bb5Sswilcox {
434355d6bb5Sswilcox 	struct bufarea *bp;
435355d6bb5Sswilcox 	int i, n;
436355d6bb5Sswilcox 	int (*func)(struct inodesc *) = NULL;
437355d6bb5Sswilcox 	u_offset_t fsbperindirb;
438355d6bb5Sswilcox 	daddr32_t last_lbn;
439355d6bb5Sswilcox 	int nif;
440355d6bb5Sswilcox 	char buf[BUFSIZ];
441355d6bb5Sswilcox 
442355d6bb5Sswilcox 	n = KEEPON;
443355d6bb5Sswilcox 
444355d6bb5Sswilcox 	switch (idesc->id_type) {
445355d6bb5Sswilcox 	case ADDR:
4467c478bd9Sstevel@tonic-gate 		func = idesc->id_func;
4477c478bd9Sstevel@tonic-gate 		if (((n = (*func)(idesc)) & KEEPON) == 0)
4487c478bd9Sstevel@tonic-gate 				return (n);
449355d6bb5Sswilcox 		break;
450355d6bb5Sswilcox 	case ACL:
4517c478bd9Sstevel@tonic-gate 		func = idesc->id_func;
452355d6bb5Sswilcox 		break;
453355d6bb5Sswilcox 	case DATA:
4547c478bd9Sstevel@tonic-gate 		func = dirscan;
455355d6bb5Sswilcox 		break;
456355d6bb5Sswilcox 	default:
457355d6bb5Sswilcox 		errexit("unknown inodesc type %d in iblock()", idesc->id_type);
458355d6bb5Sswilcox 		/* NOTREACHED */
459355d6bb5Sswilcox 	}
460355d6bb5Sswilcox 	if (chkrange(idesc->id_blkno, idesc->id_numfrags)) {
461355d6bb5Sswilcox 		return ((idesc->id_type == ACL) ? STOP : SKIP);
462355d6bb5Sswilcox 	}
463355d6bb5Sswilcox 
464355d6bb5Sswilcox 	bp = getdatablk(idesc->id_blkno, (size_t)sblock.fs_bsize);
465355d6bb5Sswilcox 	if (bp->b_errs != 0) {
466355d6bb5Sswilcox 		brelse(bp);
4677c478bd9Sstevel@tonic-gate 		return (SKIP);
4687c478bd9Sstevel@tonic-gate 	}
469355d6bb5Sswilcox 
4707c478bd9Sstevel@tonic-gate 	ilevel--;
471355d6bb5Sswilcox 	/*
472355d6bb5Sswilcox 	 * Trivia note: the BSD fsck has the number of bytes remaining
473355d6bb5Sswilcox 	 * as the third argument to iblock(), so the equivalent of
474355d6bb5Sswilcox 	 * fsbperindirb starts at fs_bsize instead of one.  We're
475355d6bb5Sswilcox 	 * working in units of filesystem blocks here, not bytes or
476355d6bb5Sswilcox 	 * fragments.
477355d6bb5Sswilcox 	 */
4787c478bd9Sstevel@tonic-gate 	for (fsbperindirb = 1, i = 0; i < ilevel; i++) {
4797c478bd9Sstevel@tonic-gate 		fsbperindirb *= (u_offset_t)NINDIR(&sblock);
4807c478bd9Sstevel@tonic-gate 	}
4817c478bd9Sstevel@tonic-gate 	/*
4827c478bd9Sstevel@tonic-gate 	 * nif indicates the next "free" pointer (as an array index) in this
4837c478bd9Sstevel@tonic-gate 	 * indirect block, based on counting the blocks remaining in the
4847c478bd9Sstevel@tonic-gate 	 * file after subtracting all previously processed blocks.
4857c478bd9Sstevel@tonic-gate 	 * This figure is based on the size field of the inode.
4867c478bd9Sstevel@tonic-gate 	 *
487355d6bb5Sswilcox 	 * Note that in normal operation, nif may initially be calculated
488355d6bb5Sswilcox 	 * as larger than the number of pointers in this block (as when
489355d6bb5Sswilcox 	 * there are more indirect blocks following); if that is
4907c478bd9Sstevel@tonic-gate 	 * the case, nif is limited to the max number of pointers per
4917c478bd9Sstevel@tonic-gate 	 * indirect block.
4927c478bd9Sstevel@tonic-gate 	 *
493355d6bb5Sswilcox 	 * Also note that if an inode is inconsistent (has more blocks
4947c478bd9Sstevel@tonic-gate 	 * allocated to it than the size field would indicate), the sweep
4957c478bd9Sstevel@tonic-gate 	 * through any indirect blocks directly pointed at by the inode
4967c478bd9Sstevel@tonic-gate 	 * continues. Since the block offset of any data blocks referenced
4977c478bd9Sstevel@tonic-gate 	 * by these indirect blocks is greater than the size of the file,
4987c478bd9Sstevel@tonic-gate 	 * the index nif may be computed as a negative value.
4997c478bd9Sstevel@tonic-gate 	 * In this case, we reset nif to indicate that all pointers in
5007c478bd9Sstevel@tonic-gate 	 * this retrieval block should be zeroed and the resulting
501355d6bb5Sswilcox 	 * unreferenced data and/or retrieval blocks will be recovered
5027c478bd9Sstevel@tonic-gate 	 * through garbage collection later.
5037c478bd9Sstevel@tonic-gate 	 */
5047c478bd9Sstevel@tonic-gate 	nif = (offset_t)howmany(iblks, fsbperindirb);
5057c478bd9Sstevel@tonic-gate 	if (nif > NINDIR(&sblock))
5067c478bd9Sstevel@tonic-gate 		nif = NINDIR(&sblock);
5077c478bd9Sstevel@tonic-gate 	else if (nif < 0)
5087c478bd9Sstevel@tonic-gate 		nif = 0;
5097c478bd9Sstevel@tonic-gate 	/*
5107c478bd9Sstevel@tonic-gate 	 * first pass: all "free" retrieval pointers (from [nif] thru
5117c478bd9Sstevel@tonic-gate 	 * 	the end of the indirect block) should be zero. (This
5127c478bd9Sstevel@tonic-gate 	 *	assertion does not hold for directories, which may be
5137c478bd9Sstevel@tonic-gate 	 *	truncated without releasing their allocated space)
5147c478bd9Sstevel@tonic-gate 	 */
515355d6bb5Sswilcox 	if (nif < NINDIR(&sblock) && (idesc->id_func == pass1check ||
516355d6bb5Sswilcox 	    idesc->id_func == pass3bcheck)) {
517355d6bb5Sswilcox 		for (i = nif; i < NINDIR(&sblock); i++) {
518355d6bb5Sswilcox 			if (bp->b_un.b_indir[i] == 0)
5197c478bd9Sstevel@tonic-gate 				continue;
520355d6bb5Sswilcox 			(void) sprintf(buf, "PARTIALLY TRUNCATED INODE I=%lu",
521355d6bb5Sswilcox 			    (ulong_t)idesc->id_number);
522355d6bb5Sswilcox 			if (preen) {
523355d6bb5Sswilcox 				pfatal(buf);
524355d6bb5Sswilcox 			} else if (dofix(idesc, buf)) {
525355d6bb5Sswilcox 				freeblk(idesc->id_number,
526355d6bb5Sswilcox 				    bp->b_un.b_indir[i],
527355d6bb5Sswilcox 				    sblock.fs_frag);
528355d6bb5Sswilcox 				bp->b_un.b_indir[i] = 0;
5297c478bd9Sstevel@tonic-gate 				dirty(bp);
5307c478bd9Sstevel@tonic-gate 			}
5317c478bd9Sstevel@tonic-gate 		}
5327c478bd9Sstevel@tonic-gate 		flush(fswritefd, bp);
5337c478bd9Sstevel@tonic-gate 	}
5347c478bd9Sstevel@tonic-gate 	/*
535355d6bb5Sswilcox 	 * second pass: all retrieval pointers referring to blocks within
5367c478bd9Sstevel@tonic-gate 	 *	a valid range [0..filesize] (both indirect and data blocks)
537355d6bb5Sswilcox 	 *	are examined in the same manner as ckinode() checks the
538355d6bb5Sswilcox 	 *	direct blocks in the inode.  Sweep through from
5397c478bd9Sstevel@tonic-gate 	 *	the first pointer in this retrieval block to [nif-1].
5407c478bd9Sstevel@tonic-gate 	 */
541355d6bb5Sswilcox 	last_lbn = howmany(idesc->id_filesize, sblock.fs_bsize);
542355d6bb5Sswilcox 	for (i = 0; i < nif; i++) {
543355d6bb5Sswilcox 		if (ilevel == 0)
544355d6bb5Sswilcox 			idesc->id_lbn++;
545355d6bb5Sswilcox 		if (bp->b_un.b_indir[i] != 0) {
546355d6bb5Sswilcox 			idesc->id_blkno = bp->b_un.b_indir[i];
5477c478bd9Sstevel@tonic-gate 			if (ilevel > 0) {
548355d6bb5Sswilcox 				n = iblock(idesc, ilevel, iblks, action);
5497c478bd9Sstevel@tonic-gate 				/*
550355d6bb5Sswilcox 				 * Each iteration decreases "remaining block
551355d6bb5Sswilcox 				 * count" by the number of blocks accessible
5527c478bd9Sstevel@tonic-gate 				 * by a pointer at this indirect block level.
5537c478bd9Sstevel@tonic-gate 				 */
5547c478bd9Sstevel@tonic-gate 				iblks -= fsbperindirb;
5557c478bd9Sstevel@tonic-gate 			} else {
5567c478bd9Sstevel@tonic-gate 				/*
557355d6bb5Sswilcox 				 * If we're truncating, func will discard
558355d6bb5Sswilcox 				 * the data block for us.
5597c478bd9Sstevel@tonic-gate 				 */
5607c478bd9Sstevel@tonic-gate 				n = (*func)(idesc);
5617c478bd9Sstevel@tonic-gate 			}
562355d6bb5Sswilcox 
563355d6bb5Sswilcox 			if ((action == CKI_TRUNCATE) &&
564355d6bb5Sswilcox 			    (idesc->id_truncto >= 0) &&
565355d6bb5Sswilcox 			    (idesc->id_lbn >= idesc->id_truncto)) {
566355d6bb5Sswilcox 				freeblk(idesc->id_number,  bp->b_un.b_indir[i],
567355d6bb5Sswilcox 				    sblock.fs_frag);
568355d6bb5Sswilcox 			}
569355d6bb5Sswilcox 
570355d6bb5Sswilcox 			/*
571355d6bb5Sswilcox 			 * Note that truncation never gets STOP back
572355d6bb5Sswilcox 			 * under normal circumstances.  Abnormal would
573355d6bb5Sswilcox 			 * be a bad acl short-circuit in iblock() or
574355d6bb5Sswilcox 			 * an out-of-range failure in pass4check().
575355d6bb5Sswilcox 			 * We still want to keep going when truncating
576355d6bb5Sswilcox 			 * under those circumstances, since the whole
577355d6bb5Sswilcox 			 * point of truncating is to get rid of all
578355d6bb5Sswilcox 			 * that.
579355d6bb5Sswilcox 			 */
580355d6bb5Sswilcox 			if ((n & STOP) && (action != CKI_TRUNCATE)) {
5817c478bd9Sstevel@tonic-gate 				brelse(bp);
5827c478bd9Sstevel@tonic-gate 				return (n);
5837c478bd9Sstevel@tonic-gate 			}
5847c478bd9Sstevel@tonic-gate 		} else {
585355d6bb5Sswilcox 			if ((idesc->id_lbn < last_lbn) &&
586355d6bb5Sswilcox 			    (idesc->id_firsthole < 0)) {
587355d6bb5Sswilcox 				idesc->id_firsthole = idesc->id_lbn;
588355d6bb5Sswilcox 			}
589355d6bb5Sswilcox 			if (idesc->id_type == DATA) {
590355d6bb5Sswilcox 				/*
591355d6bb5Sswilcox 				 * No point in continuing in the indirect
592355d6bb5Sswilcox 				 * blocks of a directory, since they'll just
593355d6bb5Sswilcox 				 * get freed anyway.
594355d6bb5Sswilcox 				 */
595355d6bb5Sswilcox 				brelse(bp);
596355d6bb5Sswilcox 				return ((n & ~KEEPON) | STOP);
5977c478bd9Sstevel@tonic-gate 			}
5987c478bd9Sstevel@tonic-gate 		}
599355d6bb5Sswilcox 	}
600355d6bb5Sswilcox 
6017c478bd9Sstevel@tonic-gate 	brelse(bp);
6027c478bd9Sstevel@tonic-gate 	return (KEEPON);
6037c478bd9Sstevel@tonic-gate }
6047c478bd9Sstevel@tonic-gate 
6057c478bd9Sstevel@tonic-gate /*
6067c478bd9Sstevel@tonic-gate  * Check that a block is a legal block number.
6077c478bd9Sstevel@tonic-gate  * Return 0 if in range, 1 if out of range.
6087c478bd9Sstevel@tonic-gate  */
609355d6bb5Sswilcox int
chkrange(daddr32_t blk,int cnt)610355d6bb5Sswilcox chkrange(daddr32_t blk, int cnt)
6117c478bd9Sstevel@tonic-gate {
6127c478bd9Sstevel@tonic-gate 	int c;
6137c478bd9Sstevel@tonic-gate 
614355d6bb5Sswilcox 	if (cnt <= 0 || blk <= 0 || ((unsigned)blk >= (unsigned)maxfsblock) ||
615355d6bb5Sswilcox 	    ((cnt - 1) > (maxfsblock - blk))) {
616355d6bb5Sswilcox 		if (debug)
617355d6bb5Sswilcox 			(void) printf(
618355d6bb5Sswilcox 			    "Bad fragment range: should be 1 <= %d..%d < %d\n",
619355d6bb5Sswilcox 			    blk, blk + cnt, maxfsblock);
6207c478bd9Sstevel@tonic-gate 		return (1);
621355d6bb5Sswilcox 	}
622355d6bb5Sswilcox 	if ((cnt > sblock.fs_frag) ||
623355d6bb5Sswilcox 	    ((fragnum(&sblock, blk) + cnt) > sblock.fs_frag)) {
624355d6bb5Sswilcox 		if (debug)
625355d6bb5Sswilcox 			(void) printf("Bad fragment size: size %d\n", cnt);
626355d6bb5Sswilcox 		return (1);
627355d6bb5Sswilcox 	}
6287c478bd9Sstevel@tonic-gate 	c = dtog(&sblock, blk);
6297c478bd9Sstevel@tonic-gate 	if (blk < cgdmin(&sblock, c)) {
6307c478bd9Sstevel@tonic-gate 		if ((unsigned)(blk + cnt) > (unsigned)cgsblock(&sblock, c)) {
631355d6bb5Sswilcox 			if (debug)
632355d6bb5Sswilcox 				(void) printf(
633355d6bb5Sswilcox 	    "Bad fragment position: %d..%d spans start of cg metadata\n",
634355d6bb5Sswilcox 				    blk, blk + cnt);
6357c478bd9Sstevel@tonic-gate 			return (1);
6367c478bd9Sstevel@tonic-gate 		}
6377c478bd9Sstevel@tonic-gate 	} else {
6387c478bd9Sstevel@tonic-gate 		if ((unsigned)(blk + cnt) > (unsigned)cgbase(&sblock, c+1)) {
639355d6bb5Sswilcox 			if (debug)
640355d6bb5Sswilcox 				(void) printf(
641355d6bb5Sswilcox 				    "Bad frag pos: %d..%d crosses end of cg\n",
642355d6bb5Sswilcox 				    blk, blk + cnt);
6437c478bd9Sstevel@tonic-gate 			return (1);
6447c478bd9Sstevel@tonic-gate 		}
6457c478bd9Sstevel@tonic-gate 	}
6467c478bd9Sstevel@tonic-gate 	return (0);
6477c478bd9Sstevel@tonic-gate }
6487c478bd9Sstevel@tonic-gate 
6497c478bd9Sstevel@tonic-gate /*
6507c478bd9Sstevel@tonic-gate  * General purpose interface for reading inodes.
6517c478bd9Sstevel@tonic-gate  */
652355d6bb5Sswilcox 
653355d6bb5Sswilcox /*
654355d6bb5Sswilcox  * Note that any call to ginode() can potentially invalidate any
655355d6bb5Sswilcox  * dinode pointers previously acquired from it.  To avoid pain,
656355d6bb5Sswilcox  * make sure to always call inodirty() immediately after modifying
657355d6bb5Sswilcox  * an inode, if there's any chance of ginode() being called after
658355d6bb5Sswilcox  * that.  Also, always call ginode() right before you need to access
659355d6bb5Sswilcox  * an inode, so that there won't be any surprises from functions
660355d6bb5Sswilcox  * called between the previous ginode() invocation and the dinode
661355d6bb5Sswilcox  * use.
662355d6bb5Sswilcox  *
663355d6bb5Sswilcox  * Despite all that, we aren't doing the amount of i/o that's implied,
664355d6bb5Sswilcox  * as we use the buffer cache that getdatablk() and friends maintain.
665355d6bb5Sswilcox  */
666355d6bb5Sswilcox static fsck_ino_t startinum = -1;
667355d6bb5Sswilcox 
6687c478bd9Sstevel@tonic-gate struct dinode *
ginode(fsck_ino_t inum)669355d6bb5Sswilcox ginode(fsck_ino_t inum)
6707c478bd9Sstevel@tonic-gate {
6717c478bd9Sstevel@tonic-gate 	daddr32_t iblk;
6727c478bd9Sstevel@tonic-gate 	struct dinode *dp;
6737c478bd9Sstevel@tonic-gate 
674355d6bb5Sswilcox 	if (inum < UFSROOTINO || inum > maxino) {
675355d6bb5Sswilcox 		errexit("bad inode number %d to ginode\n", inum);
676355d6bb5Sswilcox 	}
677355d6bb5Sswilcox 	if (startinum == -1 ||
678355d6bb5Sswilcox 	    pbp == NULL ||
679355d6bb5Sswilcox 	    inum < startinum ||
680355d6bb5Sswilcox 	    inum >= (fsck_ino_t)(startinum + (fsck_ino_t)INOPB(&sblock))) {
681355d6bb5Sswilcox 		iblk = itod(&sblock, inum);
682355d6bb5Sswilcox 		if (pbp != NULL) {
6837c478bd9Sstevel@tonic-gate 			brelse(pbp);
6847c478bd9Sstevel@tonic-gate 		}
685355d6bb5Sswilcox 		/*
686355d6bb5Sswilcox 		 * We don't check for errors here, because we can't
687355d6bb5Sswilcox 		 * tell our caller about it, and the zeros that will
688355d6bb5Sswilcox 		 * be in the buffer are just as good as anything we
689355d6bb5Sswilcox 		 * could fake.
690355d6bb5Sswilcox 		 */
691355d6bb5Sswilcox 		pbp = getdatablk(iblk, (size_t)sblock.fs_bsize);
6927c478bd9Sstevel@tonic-gate 		startinum =
693355d6bb5Sswilcox 		    (fsck_ino_t)((inum / INOPB(&sblock)) * INOPB(&sblock));
6947c478bd9Sstevel@tonic-gate 	}
695355d6bb5Sswilcox 	dp = &pbp->b_un.b_dinode[inum % INOPB(&sblock)];
696355d6bb5Sswilcox 	if (dp->di_suid != UID_LONG)
697355d6bb5Sswilcox 		dp->di_uid = dp->di_suid;
698355d6bb5Sswilcox 	if (dp->di_sgid != GID_LONG)
699355d6bb5Sswilcox 		dp->di_gid = dp->di_sgid;
7007c478bd9Sstevel@tonic-gate 	return (dp);
7017c478bd9Sstevel@tonic-gate }
7027c478bd9Sstevel@tonic-gate 
7037c478bd9Sstevel@tonic-gate /*
7047c478bd9Sstevel@tonic-gate  * Special purpose version of ginode used to optimize first pass
705355d6bb5Sswilcox  * over all the inodes in numerical order.  It bypasses the buffer
706355d6bb5Sswilcox  * system used by ginode(), etc in favour of reading the bulk of a
707355d6bb5Sswilcox  * cg's inodes at one time.
7087c478bd9Sstevel@tonic-gate  */
709355d6bb5Sswilcox static fsck_ino_t nextino, lastinum;
710355d6bb5Sswilcox static int64_t readcnt, readpercg, fullcnt, inobufsize;
711355d6bb5Sswilcox static int64_t partialcnt, partialsize;
712355d6bb5Sswilcox static size_t lastsize;
713355d6bb5Sswilcox static struct dinode *inodebuf;
714355d6bb5Sswilcox static diskaddr_t currentdblk;
715355d6bb5Sswilcox static struct dinode *currentinode;
7167c478bd9Sstevel@tonic-gate 
7177c478bd9Sstevel@tonic-gate struct dinode *
getnextinode(fsck_ino_t inum)718355d6bb5Sswilcox getnextinode(fsck_ino_t inum)
7197c478bd9Sstevel@tonic-gate {
720355d6bb5Sswilcox 	size_t size;
7217c478bd9Sstevel@tonic-gate 	diskaddr_t dblk;
7227c478bd9Sstevel@tonic-gate 	static struct dinode *dp;
7237c478bd9Sstevel@tonic-gate 
724355d6bb5Sswilcox 	if (inum != nextino++ || inum > maxino)
725355d6bb5Sswilcox 		errexit("bad inode number %d to nextinode\n", inum);
726355d6bb5Sswilcox 
727355d6bb5Sswilcox 	/*
728355d6bb5Sswilcox 	 * Will always go into the if() the first time we're called,
729355d6bb5Sswilcox 	 * so dp will always be valid.
730355d6bb5Sswilcox 	 */
731355d6bb5Sswilcox 	if (inum >= lastinum) {
7327c478bd9Sstevel@tonic-gate 		readcnt++;
7337c478bd9Sstevel@tonic-gate 		dblk = fsbtodb(&sblock, itod(&sblock, lastinum));
734355d6bb5Sswilcox 		currentdblk = dblk;
7357c478bd9Sstevel@tonic-gate 		if (readcnt % readpercg == 0) {
736355d6bb5Sswilcox 			if (partialsize > SIZE_MAX)
737355d6bb5Sswilcox 				errexit(
738355d6bb5Sswilcox 				    "Internal error: partialsize overflow");
739355d6bb5Sswilcox 			size = (size_t)partialsize;
7407c478bd9Sstevel@tonic-gate 			lastinum += partialcnt;
7417c478bd9Sstevel@tonic-gate 		} else {
742355d6bb5Sswilcox 			if (inobufsize > SIZE_MAX)
743355d6bb5Sswilcox 				errexit("Internal error: inobufsize overflow");
744355d6bb5Sswilcox 			size = (size_t)inobufsize;
7457c478bd9Sstevel@tonic-gate 			lastinum += fullcnt;
7467c478bd9Sstevel@tonic-gate 		}
747355d6bb5Sswilcox 		/*
748355d6bb5Sswilcox 		 * If fsck_bread() returns an error, it will already have
749355d6bb5Sswilcox 		 * zeroed out the buffer, so we do not need to do so here.
750355d6bb5Sswilcox 		 */
751355d6bb5Sswilcox 		(void) fsck_bread(fsreadfd, (caddr_t)inodebuf, dblk, size);
752355d6bb5Sswilcox 		lastsize = size;
7537c478bd9Sstevel@tonic-gate 		dp = inodebuf;
7547c478bd9Sstevel@tonic-gate 	}
755355d6bb5Sswilcox 	currentinode = dp;
7567c478bd9Sstevel@tonic-gate 	return (dp++);
7577c478bd9Sstevel@tonic-gate }
7587c478bd9Sstevel@tonic-gate 
759355d6bb5Sswilcox /*
760355d6bb5Sswilcox  * Reread the current getnext() buffer.  This allows for changing inodes
761355d6bb5Sswilcox  * other than the current one via ginode()/inodirty()/inoflush().
762355d6bb5Sswilcox  *
763355d6bb5Sswilcox  * Just reuses all the interesting variables that getnextinode() set up
764355d6bb5Sswilcox  * last time it was called.  This shouldn't get called often, so we don't
765355d6bb5Sswilcox  * try to figure out if the caller's actually touched an inode in the
766355d6bb5Sswilcox  * range we have cached.  There could have been an arbitrary number of
767355d6bb5Sswilcox  * them, after all.
768355d6bb5Sswilcox  */
769355d6bb5Sswilcox struct dinode *
getnextrefresh(void)770355d6bb5Sswilcox getnextrefresh(void)
7717c478bd9Sstevel@tonic-gate {
772355d6bb5Sswilcox 	if (inodebuf == NULL) {
773355d6bb5Sswilcox 		return (NULL);
774355d6bb5Sswilcox 	}
7757c478bd9Sstevel@tonic-gate 
776355d6bb5Sswilcox 	inoflush();
777355d6bb5Sswilcox 	(void) fsck_bread(fsreadfd, (caddr_t)inodebuf, currentdblk, lastsize);
778355d6bb5Sswilcox 	return (currentinode);
779355d6bb5Sswilcox }
780355d6bb5Sswilcox 
781355d6bb5Sswilcox void
resetinodebuf(void)782355d6bb5Sswilcox resetinodebuf(void)
783355d6bb5Sswilcox {
7847c478bd9Sstevel@tonic-gate 	startinum = 0;
7857c478bd9Sstevel@tonic-gate 	nextino = 0;
7867c478bd9Sstevel@tonic-gate 	lastinum = 0;
7877c478bd9Sstevel@tonic-gate 	readcnt = 0;
7887c478bd9Sstevel@tonic-gate 	inobufsize = blkroundup(&sblock, INOBUFSIZE);
7897c478bd9Sstevel@tonic-gate 	fullcnt = inobufsize / sizeof (struct dinode);
7907c478bd9Sstevel@tonic-gate 	readpercg = sblock.fs_ipg / fullcnt;
7917c478bd9Sstevel@tonic-gate 	partialcnt = sblock.fs_ipg % fullcnt;
7927c478bd9Sstevel@tonic-gate 	partialsize = partialcnt * sizeof (struct dinode);
7937c478bd9Sstevel@tonic-gate 	if (partialcnt != 0) {
7947c478bd9Sstevel@tonic-gate 		readpercg++;
7957c478bd9Sstevel@tonic-gate 	} else {
7967c478bd9Sstevel@tonic-gate 		partialcnt = fullcnt;
7977c478bd9Sstevel@tonic-gate 		partialsize = inobufsize;
7987c478bd9Sstevel@tonic-gate 	}
7997c478bd9Sstevel@tonic-gate 	if (inodebuf == NULL &&
8007c478bd9Sstevel@tonic-gate 	    (inodebuf = (struct dinode *)malloc((unsigned)inobufsize)) == NULL)
8017c478bd9Sstevel@tonic-gate 		errexit("Cannot allocate space for inode buffer\n");
8027c478bd9Sstevel@tonic-gate 	while (nextino < UFSROOTINO)
8037c478bd9Sstevel@tonic-gate 		(void) getnextinode(nextino);
8047c478bd9Sstevel@tonic-gate }
8057c478bd9Sstevel@tonic-gate 
806355d6bb5Sswilcox void
freeinodebuf(void)807355d6bb5Sswilcox freeinodebuf(void)
8087c478bd9Sstevel@tonic-gate {
809355d6bb5Sswilcox 	if (inodebuf != NULL) {
810355d6bb5Sswilcox 		free((void *)inodebuf);
811355d6bb5Sswilcox 	}
8127c478bd9Sstevel@tonic-gate 	inodebuf = NULL;
8137c478bd9Sstevel@tonic-gate }
8147c478bd9Sstevel@tonic-gate 
8157c478bd9Sstevel@tonic-gate /*
8167c478bd9Sstevel@tonic-gate  * Routines to maintain information about directory inodes.
8177c478bd9Sstevel@tonic-gate  * This is built during the first pass and used during the
8187c478bd9Sstevel@tonic-gate  * second and third passes.
8197c478bd9Sstevel@tonic-gate  *
8207c478bd9Sstevel@tonic-gate  * Enter inodes into the cache.
8217c478bd9Sstevel@tonic-gate  */
822355d6bb5Sswilcox void
cacheino(struct dinode * dp,fsck_ino_t inum)823355d6bb5Sswilcox cacheino(struct dinode *dp, fsck_ino_t inum)
8247c478bd9Sstevel@tonic-gate {
8257c478bd9Sstevel@tonic-gate 	struct inoinfo *inp;
8267c478bd9Sstevel@tonic-gate 	struct inoinfo **inpp;
8277c478bd9Sstevel@tonic-gate 	uint_t blks;
8287c478bd9Sstevel@tonic-gate 
8297c478bd9Sstevel@tonic-gate 	blks = NDADDR + NIADDR;
8307c478bd9Sstevel@tonic-gate 	inp = (struct inoinfo *)
8317c478bd9Sstevel@tonic-gate 	    malloc(sizeof (*inp) + (blks - 1) * sizeof (daddr32_t));
8327c478bd9Sstevel@tonic-gate 	if (inp == NULL)
833355d6bb5Sswilcox 		errexit("Cannot increase directory list\n");
834355d6bb5Sswilcox 	init_inoinfo(inp, dp, inum); /* doesn't touch i_nextlist or i_number */
835355d6bb5Sswilcox 	inpp = &inphead[inum % numdirs];
836355d6bb5Sswilcox 	inp->i_nextlist = *inpp;
8377c478bd9Sstevel@tonic-gate 	*inpp = inp;
838355d6bb5Sswilcox 	inp->i_number = inum;
8397c478bd9Sstevel@tonic-gate 	if (inplast == listmax) {
8407c478bd9Sstevel@tonic-gate 		listmax += 100;
841355d6bb5Sswilcox 		inpsort = (struct inoinfo **)realloc((void *)inpsort,
8427c478bd9Sstevel@tonic-gate 		    (unsigned)listmax * sizeof (struct inoinfo *));
8437c478bd9Sstevel@tonic-gate 		if (inpsort == NULL)
8447c478bd9Sstevel@tonic-gate 			errexit("cannot increase directory list");
8457c478bd9Sstevel@tonic-gate 	}
8467c478bd9Sstevel@tonic-gate 	inpsort[inplast++] = inp;
8477c478bd9Sstevel@tonic-gate }
8487c478bd9Sstevel@tonic-gate 
8497c478bd9Sstevel@tonic-gate /*
8507c478bd9Sstevel@tonic-gate  * Look up an inode cache structure.
8517c478bd9Sstevel@tonic-gate  */
8527c478bd9Sstevel@tonic-gate struct inoinfo *
getinoinfo(fsck_ino_t inum)853355d6bb5Sswilcox getinoinfo(fsck_ino_t inum)
8547c478bd9Sstevel@tonic-gate {
8557c478bd9Sstevel@tonic-gate 	struct inoinfo *inp;
8567c478bd9Sstevel@tonic-gate 
857355d6bb5Sswilcox 	inp = search_cache(inphead[inum % numdirs], inum);
8587c478bd9Sstevel@tonic-gate 	return (inp);
8597c478bd9Sstevel@tonic-gate }
8607c478bd9Sstevel@tonic-gate 
8617c478bd9Sstevel@tonic-gate /*
8627c478bd9Sstevel@tonic-gate  * Determine whether inode is in cache.
8637c478bd9Sstevel@tonic-gate  */
864355d6bb5Sswilcox int
inocached(fsck_ino_t inum)865355d6bb5Sswilcox inocached(fsck_ino_t inum)
8667c478bd9Sstevel@tonic-gate {
867355d6bb5Sswilcox 	return (search_cache(inphead[inum % numdirs], inum) != NULL);
8687c478bd9Sstevel@tonic-gate }
8697c478bd9Sstevel@tonic-gate 
8707c478bd9Sstevel@tonic-gate /*
8717c478bd9Sstevel@tonic-gate  * Clean up all the inode cache structure.
8727c478bd9Sstevel@tonic-gate  */
873355d6bb5Sswilcox void
inocleanup(void)874355d6bb5Sswilcox inocleanup(void)
8757c478bd9Sstevel@tonic-gate {
8767c478bd9Sstevel@tonic-gate 	struct inoinfo **inpp;
8777c478bd9Sstevel@tonic-gate 
8787c478bd9Sstevel@tonic-gate 	if (inphead == NULL)
8797c478bd9Sstevel@tonic-gate 		return;
880355d6bb5Sswilcox 	for (inpp = &inpsort[inplast - 1]; inpp >= inpsort; inpp--) {
881355d6bb5Sswilcox 		free((void *)(*inpp));
882355d6bb5Sswilcox 	}
883355d6bb5Sswilcox 	free((void *)inphead);
884355d6bb5Sswilcox 	free((void *)inpsort);
8857c478bd9Sstevel@tonic-gate 	inphead = inpsort = NULL;
8867c478bd9Sstevel@tonic-gate }
8877c478bd9Sstevel@tonic-gate 
8887c478bd9Sstevel@tonic-gate /*
8897c478bd9Sstevel@tonic-gate  * Routines to maintain information about acl inodes.
8907c478bd9Sstevel@tonic-gate  * This is built during the first pass and used during the
8917c478bd9Sstevel@tonic-gate  * second and third passes.
8927c478bd9Sstevel@tonic-gate  *
8937c478bd9Sstevel@tonic-gate  * Enter acl inodes into the cache.
8947c478bd9Sstevel@tonic-gate  */
895355d6bb5Sswilcox void
cacheacl(struct dinode * dp,fsck_ino_t inum)896355d6bb5Sswilcox cacheacl(struct dinode *dp, fsck_ino_t inum)
8977c478bd9Sstevel@tonic-gate {
898355d6bb5Sswilcox 	struct inoinfo *aclp;
899355d6bb5Sswilcox 	struct inoinfo **aclpp;
9007c478bd9Sstevel@tonic-gate 	uint_t blks;
9017c478bd9Sstevel@tonic-gate 
9027c478bd9Sstevel@tonic-gate 	blks = NDADDR + NIADDR;
903355d6bb5Sswilcox 	aclp = (struct inoinfo *)
9047c478bd9Sstevel@tonic-gate 	    malloc(sizeof (*aclp) + (blks - 1) * sizeof (daddr32_t));
9057c478bd9Sstevel@tonic-gate 	if (aclp == NULL)
9067c478bd9Sstevel@tonic-gate 		return;
907355d6bb5Sswilcox 	aclpp = &aclphead[inum % numacls];
908355d6bb5Sswilcox 	aclp->i_nextlist = *aclpp;
9097c478bd9Sstevel@tonic-gate 	*aclpp = aclp;
910355d6bb5Sswilcox 	aclp->i_number = inum;
9117c478bd9Sstevel@tonic-gate 	aclp->i_isize = (offset_t)dp->di_size;
912355d6bb5Sswilcox 	aclp->i_blkssize = (size_t)(blks * sizeof (daddr32_t));
913355d6bb5Sswilcox 	(void) memmove(&aclp->i_blks[0], &dp->di_db[0], aclp->i_blkssize);
9147c478bd9Sstevel@tonic-gate 	if (aclplast == aclmax) {
9157c478bd9Sstevel@tonic-gate 		aclmax += 100;
916355d6bb5Sswilcox 		aclpsort = (struct inoinfo **)realloc((char *)aclpsort,
917355d6bb5Sswilcox 		    (unsigned)aclmax * sizeof (struct inoinfo *));
9187c478bd9Sstevel@tonic-gate 		if (aclpsort == NULL)
9197c478bd9Sstevel@tonic-gate 			errexit("cannot increase acl list");
9207c478bd9Sstevel@tonic-gate 	}
9217c478bd9Sstevel@tonic-gate 	aclpsort[aclplast++] = aclp;
9227c478bd9Sstevel@tonic-gate }
9237c478bd9Sstevel@tonic-gate 
9247c478bd9Sstevel@tonic-gate 
9257c478bd9Sstevel@tonic-gate /*
926355d6bb5Sswilcox  * Generic cache search function.
927355d6bb5Sswilcox  * ROOT is the first entry in a hash chain (the caller is expected
928355d6bb5Sswilcox  * to have done the initial bucket lookup).  KEY is what's being
929355d6bb5Sswilcox  * searched for.
930355d6bb5Sswilcox  *
931355d6bb5Sswilcox  * Returns a pointer to the entry if it is found, NULL otherwise.
9327c478bd9Sstevel@tonic-gate  */
933355d6bb5Sswilcox static struct inoinfo *
search_cache(struct inoinfo * element,fsck_ino_t key)934355d6bb5Sswilcox search_cache(struct inoinfo *element, fsck_ino_t key)
9357c478bd9Sstevel@tonic-gate {
936355d6bb5Sswilcox 	while (element != NULL) {
937355d6bb5Sswilcox 		if (element->i_number == key)
938355d6bb5Sswilcox 			break;
939355d6bb5Sswilcox 		element = element->i_nextlist;
9407c478bd9Sstevel@tonic-gate 	}
9417c478bd9Sstevel@tonic-gate 
942355d6bb5Sswilcox 	return (element);
943355d6bb5Sswilcox }
9447c478bd9Sstevel@tonic-gate 
945355d6bb5Sswilcox void
inodirty(void)946355d6bb5Sswilcox inodirty(void)
947355d6bb5Sswilcox {
9487c478bd9Sstevel@tonic-gate 	dirty(pbp);
9497c478bd9Sstevel@tonic-gate }
9507c478bd9Sstevel@tonic-gate 
951355d6bb5Sswilcox static void
inoflush(void)952355d6bb5Sswilcox inoflush(void)
9537c478bd9Sstevel@tonic-gate {
954355d6bb5Sswilcox 	if (pbp != NULL)
955355d6bb5Sswilcox 		flush(fswritefd, pbp);
956355d6bb5Sswilcox }
957355d6bb5Sswilcox 
958355d6bb5Sswilcox /*
959355d6bb5Sswilcox  * Interactive wrapper for freeino(), for those times when we're
960355d6bb5Sswilcox  * not sure if we should throw something away.
961355d6bb5Sswilcox  */
962355d6bb5Sswilcox void
clri(struct inodesc * idesc,char * type,int verbose,int corrupting)963355d6bb5Sswilcox clri(struct inodesc *idesc, char *type, int verbose, int corrupting)
964355d6bb5Sswilcox {
965355d6bb5Sswilcox 	int need_parent;
9667c478bd9Sstevel@tonic-gate 	struct dinode *dp;
9677c478bd9Sstevel@tonic-gate 
968355d6bb5Sswilcox 	if (statemap[idesc->id_number] == USTATE)
969355d6bb5Sswilcox 		return;
970355d6bb5Sswilcox 
9717c478bd9Sstevel@tonic-gate 	dp = ginode(idesc->id_number);
972355d6bb5Sswilcox 	if (verbose == CLRI_VERBOSE) {
973355d6bb5Sswilcox 		pwarn("%s %s", type, file_id(idesc->id_number, dp->di_mode));
9747c478bd9Sstevel@tonic-gate 		pinode(idesc->id_number);
9757c478bd9Sstevel@tonic-gate 	}
976355d6bb5Sswilcox 	if (preen || (reply("CLEAR") == 1)) {
977355d6bb5Sswilcox 		need_parent = (corrupting == CLRI_NOP_OK) ?
978355d6bb5Sswilcox 		    TI_NOPARENT : TI_PARENT;
979355d6bb5Sswilcox 		freeino(idesc->id_number, need_parent);
9807c478bd9Sstevel@tonic-gate 		if (preen)
981355d6bb5Sswilcox 			(void) printf(" (CLEARED)\n");
982355d6bb5Sswilcox 		remove_orphan_dir(idesc->id_number);
983355d6bb5Sswilcox 	} else if (corrupting == CLRI_NOP_CORRUPT) {
984355d6bb5Sswilcox 		iscorrupt = 1;
9857c478bd9Sstevel@tonic-gate 	}
986355d6bb5Sswilcox 	(void) printf("\n");
9877c478bd9Sstevel@tonic-gate }
9887c478bd9Sstevel@tonic-gate 
989355d6bb5Sswilcox /*
990355d6bb5Sswilcox  * Find the directory entry for the inode noted in id_parent (which is
991355d6bb5Sswilcox  * not necessarily the parent of anything, we're just using a convenient
992355d6bb5Sswilcox  * field.
993355d6bb5Sswilcox  */
994355d6bb5Sswilcox int
findname(struct inodesc * idesc)995355d6bb5Sswilcox findname(struct inodesc *idesc)
9967c478bd9Sstevel@tonic-gate {
9977c478bd9Sstevel@tonic-gate 	struct direct *dirp = idesc->id_dirp;
9987c478bd9Sstevel@tonic-gate 
9997c478bd9Sstevel@tonic-gate 	if (dirp->d_ino != idesc->id_parent)
10007c478bd9Sstevel@tonic-gate 		return (KEEPON);
1001355d6bb5Sswilcox 	(void) memmove(idesc->id_name, dirp->d_name,
10027c478bd9Sstevel@tonic-gate 	    MIN(dirp->d_namlen, MAXNAMLEN) + 1);
10037c478bd9Sstevel@tonic-gate 	return (STOP|FOUND);
10047c478bd9Sstevel@tonic-gate }
10057c478bd9Sstevel@tonic-gate 
1006355d6bb5Sswilcox /*
1007355d6bb5Sswilcox  * Find the inode number associated with the given name.
1008355d6bb5Sswilcox  */
1009355d6bb5Sswilcox int
findino(struct inodesc * idesc)1010355d6bb5Sswilcox findino(struct inodesc *idesc)
10117c478bd9Sstevel@tonic-gate {
10127c478bd9Sstevel@tonic-gate 	struct direct *dirp = idesc->id_dirp;
10137c478bd9Sstevel@tonic-gate 
10147c478bd9Sstevel@tonic-gate 	if (dirp->d_ino == 0)
10157c478bd9Sstevel@tonic-gate 		return (KEEPON);
10167c478bd9Sstevel@tonic-gate 	if (strcmp(dirp->d_name, idesc->id_name) == 0 &&
10177c478bd9Sstevel@tonic-gate 	    dirp->d_ino >= UFSROOTINO && dirp->d_ino <= maxino) {
10187c478bd9Sstevel@tonic-gate 		idesc->id_parent = dirp->d_ino;
10197c478bd9Sstevel@tonic-gate 		return (STOP|FOUND);
10207c478bd9Sstevel@tonic-gate 	}
10217c478bd9Sstevel@tonic-gate 	return (KEEPON);
10227c478bd9Sstevel@tonic-gate }
10237c478bd9Sstevel@tonic-gate 
1024355d6bb5Sswilcox int
cleardirentry(fsck_ino_t parentdir,fsck_ino_t target)1025355d6bb5Sswilcox cleardirentry(fsck_ino_t parentdir, fsck_ino_t target)
1026355d6bb5Sswilcox {
1027355d6bb5Sswilcox 	struct inodesc idesc;
1028355d6bb5Sswilcox 	struct dinode *dp;
1029355d6bb5Sswilcox 
1030355d6bb5Sswilcox 	dp = ginode(parentdir);
1031355d6bb5Sswilcox 	init_inodesc(&idesc);
1032355d6bb5Sswilcox 	idesc.id_func = clearanentry;
1033355d6bb5Sswilcox 	idesc.id_parent = target;
1034355d6bb5Sswilcox 	idesc.id_type = DATA;
1035355d6bb5Sswilcox 	idesc.id_fix = NOFIX;
1036355d6bb5Sswilcox 	return (ckinode(dp, &idesc, CKI_TRAVERSE));
1037355d6bb5Sswilcox }
1038355d6bb5Sswilcox 
1039355d6bb5Sswilcox static int
clearanentry(struct inodesc * idesc)1040355d6bb5Sswilcox clearanentry(struct inodesc *idesc)
1041355d6bb5Sswilcox {
1042355d6bb5Sswilcox 	struct direct *dirp = idesc->id_dirp;
1043355d6bb5Sswilcox 
1044355d6bb5Sswilcox 	if (dirp->d_ino != idesc->id_parent || idesc->id_entryno < 2) {
1045355d6bb5Sswilcox 		idesc->id_entryno++;
1046355d6bb5Sswilcox 		return (KEEPON);
1047355d6bb5Sswilcox 	}
1048355d6bb5Sswilcox 	dirp->d_ino = 0;
1049355d6bb5Sswilcox 	return (STOP|FOUND|ALTERED);
1050355d6bb5Sswilcox }
1051355d6bb5Sswilcox 
1052355d6bb5Sswilcox void
pinode(fsck_ino_t ino)1053355d6bb5Sswilcox pinode(fsck_ino_t ino)
10547c478bd9Sstevel@tonic-gate {
10557c478bd9Sstevel@tonic-gate 	struct dinode *dp;
1056355d6bb5Sswilcox 
1057355d6bb5Sswilcox 	(void) printf(" I=%lu ", (ulong_t)ino);
1058355d6bb5Sswilcox 	if (ino < UFSROOTINO || ino > maxino)
1059355d6bb5Sswilcox 		return;
1060355d6bb5Sswilcox 	dp = ginode(ino);
1061355d6bb5Sswilcox 	pdinode(dp);
1062355d6bb5Sswilcox }
1063355d6bb5Sswilcox 
1064355d6bb5Sswilcox static void
pdinode(struct dinode * dp)1065355d6bb5Sswilcox pdinode(struct dinode *dp)
1066355d6bb5Sswilcox {
10677c478bd9Sstevel@tonic-gate 	char *p;
10687c478bd9Sstevel@tonic-gate 	struct passwd *pw;
10697c478bd9Sstevel@tonic-gate 	time_t t;
10707c478bd9Sstevel@tonic-gate 
1071355d6bb5Sswilcox 	(void) printf(" OWNER=");
10727c478bd9Sstevel@tonic-gate 	if ((pw = getpwuid((int)dp->di_uid)) != 0)
1073355d6bb5Sswilcox 		(void) printf("%s ", pw->pw_name);
10747c478bd9Sstevel@tonic-gate 	else
1075355d6bb5Sswilcox 		(void) printf("%lu ", (ulong_t)dp->di_uid);
1076355d6bb5Sswilcox 	(void) printf("MODE=%o\n", dp->di_mode);
10777c478bd9Sstevel@tonic-gate 	if (preen)
1078355d6bb5Sswilcox 		(void) printf("%s: ", devname);
1079355d6bb5Sswilcox 	(void) printf("SIZE=%lld ", (longlong_t)dp->di_size);
1080355d6bb5Sswilcox 
1081355d6bb5Sswilcox 	/* ctime() ignores LOCALE, so this is safe */
10827c478bd9Sstevel@tonic-gate 	t = (time_t)dp->di_mtime;
10837c478bd9Sstevel@tonic-gate 	p = ctime(&t);
1084355d6bb5Sswilcox 	(void) printf("MTIME=%12.12s %4.4s ", p + 4, p + 20);
10857c478bd9Sstevel@tonic-gate }
10867c478bd9Sstevel@tonic-gate 
1087355d6bb5Sswilcox void
blkerror(fsck_ino_t ino,char * type,daddr32_t blk,daddr32_t lbn)1088355d6bb5Sswilcox blkerror(fsck_ino_t ino, char *type, daddr32_t blk, daddr32_t lbn)
10897c478bd9Sstevel@tonic-gate {
1090355d6bb5Sswilcox 	pfatal("FRAGMENT %d %s I=%u LFN %d", blk, type, ino, lbn);
1091355d6bb5Sswilcox 	(void) printf("\n");
10927c478bd9Sstevel@tonic-gate 
1093355d6bb5Sswilcox 	switch (statemap[ino] & ~INDELAYD) {
10947c478bd9Sstevel@tonic-gate 
10957c478bd9Sstevel@tonic-gate 	case FSTATE:
1096355d6bb5Sswilcox 	case FZLINK:
10977c478bd9Sstevel@tonic-gate 		statemap[ino] = FCLEAR;
10987c478bd9Sstevel@tonic-gate 		return;
10997c478bd9Sstevel@tonic-gate 
1100355d6bb5Sswilcox 	case DFOUND:
11017c478bd9Sstevel@tonic-gate 	case DSTATE:
1102355d6bb5Sswilcox 	case DZLINK:
11037c478bd9Sstevel@tonic-gate 		statemap[ino] = DCLEAR;
1104355d6bb5Sswilcox 		add_orphan_dir(ino);
11057c478bd9Sstevel@tonic-gate 		return;
11067c478bd9Sstevel@tonic-gate 
11077c478bd9Sstevel@tonic-gate 	case SSTATE:
11087c478bd9Sstevel@tonic-gate 		statemap[ino] = SCLEAR;
11097c478bd9Sstevel@tonic-gate 		return;
11107c478bd9Sstevel@tonic-gate 
11117c478bd9Sstevel@tonic-gate 	case FCLEAR:
11127c478bd9Sstevel@tonic-gate 	case DCLEAR:
11137c478bd9Sstevel@tonic-gate 	case SCLEAR:
11147c478bd9Sstevel@tonic-gate 		return;
11157c478bd9Sstevel@tonic-gate 
11167c478bd9Sstevel@tonic-gate 	default:
1117355d6bb5Sswilcox 		errexit("BAD STATE 0x%x TO BLKERR\n", statemap[ino]);
11187c478bd9Sstevel@tonic-gate 		/* NOTREACHED */
11197c478bd9Sstevel@tonic-gate 	}
11207c478bd9Sstevel@tonic-gate }
11217c478bd9Sstevel@tonic-gate 
11227c478bd9Sstevel@tonic-gate /*
11237c478bd9Sstevel@tonic-gate  * allocate an unused inode
11247c478bd9Sstevel@tonic-gate  */
1125355d6bb5Sswilcox fsck_ino_t
allocino(fsck_ino_t request,int type)1126355d6bb5Sswilcox allocino(fsck_ino_t request, int type)
11277c478bd9Sstevel@tonic-gate {
1128355d6bb5Sswilcox 	fsck_ino_t ino;
11297c478bd9Sstevel@tonic-gate 	struct dinode *dp;
1130355d6bb5Sswilcox 	struct cg *cgp = &cgrp;
113177a343abSabalfour 	int cg;
11327c478bd9Sstevel@tonic-gate 	time_t t;
1133355d6bb5Sswilcox 	caddr_t err;
11347c478bd9Sstevel@tonic-gate 
1135355d6bb5Sswilcox 	if (debug && (request != 0) && (request != UFSROOTINO))
1136355d6bb5Sswilcox 		errexit("assertion failed: allocino() asked for "
1137355d6bb5Sswilcox 		    "inode %d instead of 0 or %d",
1138355d6bb5Sswilcox 		    (int)request, (int)UFSROOTINO);
1139355d6bb5Sswilcox 
1140355d6bb5Sswilcox 	/*
1141355d6bb5Sswilcox 	 * We know that we're only going to get requests for UFSROOTINO
1142355d6bb5Sswilcox 	 * or 0.  If UFSROOTINO is wanted, then it better be available
1143355d6bb5Sswilcox 	 * because our caller is trying to recreate the root directory.
1144355d6bb5Sswilcox 	 * If we're asked for 0, then which one we return doesn't matter.
1145355d6bb5Sswilcox 	 * We know that inodes 0 and 1 are never valid to return, so we
1146355d6bb5Sswilcox 	 * the start at the lowest-legal inode number.
1147355d6bb5Sswilcox 	 *
1148355d6bb5Sswilcox 	 * If we got a request for UFSROOTINO, then request != 0, and
1149355d6bb5Sswilcox 	 * this pair of conditionals is the only place that treats
1150355d6bb5Sswilcox 	 * UFSROOTINO specially.
1151355d6bb5Sswilcox 	 */
11527c478bd9Sstevel@tonic-gate 	if (request == 0)
11537c478bd9Sstevel@tonic-gate 		request = UFSROOTINO;
11547c478bd9Sstevel@tonic-gate 	else if (statemap[request] != USTATE)
11557c478bd9Sstevel@tonic-gate 		return (0);
1156355d6bb5Sswilcox 
1157355d6bb5Sswilcox 	/*
1158355d6bb5Sswilcox 	 * Doesn't do wrapping, since we know we started at
1159355d6bb5Sswilcox 	 * the smallest inode.
1160355d6bb5Sswilcox 	 */
11617c478bd9Sstevel@tonic-gate 	for (ino = request; ino < maxino; ino++)
11627c478bd9Sstevel@tonic-gate 		if (statemap[ino] == USTATE)
11637c478bd9Sstevel@tonic-gate 			break;
11647c478bd9Sstevel@tonic-gate 	if (ino == maxino)
11657c478bd9Sstevel@tonic-gate 		return (0);
1166355d6bb5Sswilcox 
1167355d6bb5Sswilcox 	/*
1168355d6bb5Sswilcox 	 * In pass5, we'll calculate the bitmaps and counts all again from
1169355d6bb5Sswilcox 	 * scratch and do a comparison, but for that to work the cg has
1170355d6bb5Sswilcox 	 * to know what in-memory changes we've made to it.  If we have
1171355d6bb5Sswilcox 	 * trouble reading the cg, cg_sanity() should kick it out so
1172355d6bb5Sswilcox 	 * we can skip explicit i/o error checking here.
1173355d6bb5Sswilcox 	 */
1174355d6bb5Sswilcox 	cg = itog(&sblock, ino);
1175355d6bb5Sswilcox 	(void) getblk(&cgblk, cgtod(&sblock, cg), (size_t)sblock.fs_cgsize);
117677a343abSabalfour 	err = cg_sanity(cgp, cg);
1177355d6bb5Sswilcox 	if (err != NULL) {
1178355d6bb5Sswilcox 		pfatal("CG %d: %s\n", cg, err);
1179355d6bb5Sswilcox 		free((void *)err);
1180355d6bb5Sswilcox 		if (reply("REPAIR") == 0)
1181355d6bb5Sswilcox 			errexit("Program terminated.");
1182355d6bb5Sswilcox 		fix_cg(cgp, cg);
1183355d6bb5Sswilcox 	}
1184355d6bb5Sswilcox 	setbit(cg_inosused(cgp), ino % sblock.fs_ipg);
1185355d6bb5Sswilcox 	cgp->cg_cs.cs_nifree--;
1186355d6bb5Sswilcox 	cgdirty();
1187355d6bb5Sswilcox 
1188355d6bb5Sswilcox 	if (lastino < ino)
1189355d6bb5Sswilcox 		lastino = ino;
1190355d6bb5Sswilcox 
1191355d6bb5Sswilcox 	/*
1192355d6bb5Sswilcox 	 * Don't currently support IFATTRDIR or any of the other
1193355d6bb5Sswilcox 	 * types, as they aren't needed.
1194355d6bb5Sswilcox 	 */
11957c478bd9Sstevel@tonic-gate 	switch (type & IFMT) {
11967c478bd9Sstevel@tonic-gate 	case IFDIR:
11977c478bd9Sstevel@tonic-gate 		statemap[ino] = DSTATE;
1198355d6bb5Sswilcox 		cgp->cg_cs.cs_ndir++;
11997c478bd9Sstevel@tonic-gate 		break;
12007c478bd9Sstevel@tonic-gate 	case IFREG:
12017c478bd9Sstevel@tonic-gate 	case IFLNK:
12027c478bd9Sstevel@tonic-gate 		statemap[ino] = FSTATE;
12037c478bd9Sstevel@tonic-gate 		break;
12047c478bd9Sstevel@tonic-gate 	default:
1205355d6bb5Sswilcox 		/*
1206355d6bb5Sswilcox 		 * Pretend nothing ever happened.  This clears the
1207355d6bb5Sswilcox 		 * dirty flag, among other things.
1208355d6bb5Sswilcox 		 */
1209355d6bb5Sswilcox 		initbarea(&cgblk);
1210355d6bb5Sswilcox 		if (debug)
1211355d6bb5Sswilcox 			(void) printf("allocino: unknown type 0%o\n",
1212355d6bb5Sswilcox 			    type & IFMT);
12137c478bd9Sstevel@tonic-gate 		return (0);
12147c478bd9Sstevel@tonic-gate 	}
1215355d6bb5Sswilcox 
1216355d6bb5Sswilcox 	/*
1217355d6bb5Sswilcox 	 * We're allocating what should be a completely-unused inode,
1218355d6bb5Sswilcox 	 * so make sure we don't inherit anything from any previous
1219355d6bb5Sswilcox 	 * incarnations.
1220355d6bb5Sswilcox 	 */
12217c478bd9Sstevel@tonic-gate 	dp = ginode(ino);
1222355d6bb5Sswilcox 	(void) memset((void *)dp, 0, sizeof (struct dinode));
12237c478bd9Sstevel@tonic-gate 	dp->di_db[0] = allocblk(1);
12247c478bd9Sstevel@tonic-gate 	if (dp->di_db[0] == 0) {
12257c478bd9Sstevel@tonic-gate 		statemap[ino] = USTATE;
12267c478bd9Sstevel@tonic-gate 		return (0);
12277c478bd9Sstevel@tonic-gate 	}
1228355d6bb5Sswilcox 	dp->di_mode = (mode_t)type;
1229355d6bb5Sswilcox 	(void) time(&t);
12307c478bd9Sstevel@tonic-gate 	dp->di_atime = (time32_t)t;
1231355d6bb5Sswilcox 	dp->di_ctime = dp->di_atime;
1232355d6bb5Sswilcox 	dp->di_mtime = dp->di_ctime;
12337c478bd9Sstevel@tonic-gate 	dp->di_size = (u_offset_t)sblock.fs_fsize;
12347c478bd9Sstevel@tonic-gate 	dp->di_blocks = btodb(sblock.fs_fsize);
12357c478bd9Sstevel@tonic-gate 	n_files++;
12367c478bd9Sstevel@tonic-gate 	inodirty();
12377c478bd9Sstevel@tonic-gate 	return (ino);
12387c478bd9Sstevel@tonic-gate }
12397c478bd9Sstevel@tonic-gate 
12407c478bd9Sstevel@tonic-gate /*
1241355d6bb5Sswilcox  * Release some or all of the blocks of an inode.
1242355d6bb5Sswilcox  * Only truncates down.  Assumes new_length is appropriately aligned
1243355d6bb5Sswilcox  * to a block boundary (or a directory block boundary, if it's a
1244355d6bb5Sswilcox  * directory).
1245355d6bb5Sswilcox  *
1246355d6bb5Sswilcox  * If this is a directory, discard all of its contents first, so
1247355d6bb5Sswilcox  * we don't create a bunch of orphans that would need another fsck
1248355d6bb5Sswilcox  * run to clean up.
1249355d6bb5Sswilcox  *
1250355d6bb5Sswilcox  * Even if truncating to zero length, the inode remains allocated.
12517c478bd9Sstevel@tonic-gate  */
1252355d6bb5Sswilcox void
truncino(fsck_ino_t ino,offset_t new_length,int update)1253355d6bb5Sswilcox truncino(fsck_ino_t ino, offset_t new_length, int update)
12547c478bd9Sstevel@tonic-gate {
12557c478bd9Sstevel@tonic-gate 	struct inodesc idesc;
1256355d6bb5Sswilcox 	struct inoinfo *iip;
12577c478bd9Sstevel@tonic-gate 	struct dinode *dp;
1258355d6bb5Sswilcox 	fsck_ino_t parent;
1259355d6bb5Sswilcox 	mode_t mode;
1260355d6bb5Sswilcox 	caddr_t message;
1261*2ee92411SAndrew Balfour 	int isdir, islink;
1262355d6bb5Sswilcox 	int ilevel, dblk;
12637c478bd9Sstevel@tonic-gate 
1264355d6bb5Sswilcox 	dp = ginode(ino);
1265355d6bb5Sswilcox 	mode = (dp->di_mode & IFMT);
1266355d6bb5Sswilcox 	isdir = (mode == IFDIR) || (mode == IFATTRDIR);
1267*2ee92411SAndrew Balfour 	islink = (mode == IFLNK);
1268355d6bb5Sswilcox 
1269355d6bb5Sswilcox 	if (isdir) {
1270355d6bb5Sswilcox 		/*
1271355d6bb5Sswilcox 		 * Go with the parent we found by chasing references,
1272355d6bb5Sswilcox 		 * if we've gotten that far.  Otherwise, use what the
1273355d6bb5Sswilcox 		 * directory itself claims.  If there's no ``..'' entry
1274355d6bb5Sswilcox 		 * in it, give up trying to get the link counts right.
1275355d6bb5Sswilcox 		 */
1276355d6bb5Sswilcox 		if (update == TI_NOPARENT) {
1277355d6bb5Sswilcox 			parent = -1;
1278355d6bb5Sswilcox 		} else {
1279355d6bb5Sswilcox 			iip = getinoinfo(ino);
1280355d6bb5Sswilcox 			if (iip != NULL) {
1281355d6bb5Sswilcox 				parent = iip->i_parent;
1282355d6bb5Sswilcox 			} else {
1283355d6bb5Sswilcox 				parent = lookup_dotdot_ino(ino);
1284355d6bb5Sswilcox 				if (parent != 0) {
1285355d6bb5Sswilcox 					/*
1286355d6bb5Sswilcox 					 * Make sure that the claimed
1287355d6bb5Sswilcox 					 * parent actually has a
1288355d6bb5Sswilcox 					 * reference to us.
1289355d6bb5Sswilcox 					 */
1290355d6bb5Sswilcox 					dp = ginode(parent);
1291355d6bb5Sswilcox 					idesc.id_name = lfname;
1292355d6bb5Sswilcox 					idesc.id_type = DATA;
1293355d6bb5Sswilcox 					idesc.id_func = findino;
1294355d6bb5Sswilcox 					idesc.id_number = ino;
1295355d6bb5Sswilcox 					idesc.id_fix = DONTKNOW;
1296355d6bb5Sswilcox 					if ((ckinode(dp, &idesc,
1297355d6bb5Sswilcox 					    CKI_TRAVERSE) & FOUND) == 0)
1298355d6bb5Sswilcox 						parent = 0;
1299355d6bb5Sswilcox 				}
1300355d6bb5Sswilcox 			}
1301355d6bb5Sswilcox 		}
1302355d6bb5Sswilcox 
1303355d6bb5Sswilcox 		mark_delayed_inodes(ino, numfrags(&sblock, new_length));
1304355d6bb5Sswilcox 		if (parent > 0) {
1305355d6bb5Sswilcox 			dp = ginode(parent);
1306355d6bb5Sswilcox 			LINK_RANGE(message, dp->di_nlink, -1);
1307355d6bb5Sswilcox 			if (message != NULL) {
1308355d6bb5Sswilcox 				LINK_CLEAR(message, parent, dp->di_mode,
1309355d6bb5Sswilcox 				    &idesc);
1310355d6bb5Sswilcox 				if (statemap[parent] == USTATE)
1311355d6bb5Sswilcox 					goto no_parent_update;
1312355d6bb5Sswilcox 			}
1313355d6bb5Sswilcox 			TRACK_LNCNTP(parent, lncntp[parent]--);
1314355d6bb5Sswilcox 		} else if ((mode == IFDIR) && (parent == 0)) {
1315355d6bb5Sswilcox 			/*
1316355d6bb5Sswilcox 			 * Currently don't have a good way to
1317355d6bb5Sswilcox 			 * handle this, so throw up our hands.
1318355d6bb5Sswilcox 			 * However, we know that we can still
1319355d6bb5Sswilcox 			 * do some good if we continue, so
1320355d6bb5Sswilcox 			 * don't actually exit yet.
1321355d6bb5Sswilcox 			 *
1322355d6bb5Sswilcox 			 * We don't do it for attrdirs,
1323355d6bb5Sswilcox 			 * because there aren't link counts
1324355d6bb5Sswilcox 			 * between them and their parents.
1325355d6bb5Sswilcox 			 */
1326355d6bb5Sswilcox 			pwarn("Could not determine former parent of "
1327355d6bb5Sswilcox 			    "inode %d, link counts are possibly\n"
1328355d6bb5Sswilcox 			    "incorrect.  Please rerun fsck(1M) to "
1329355d6bb5Sswilcox 			    "correct this.\n",
1330355d6bb5Sswilcox 			    ino);
1331355d6bb5Sswilcox 			iscorrupt = 1;
1332355d6bb5Sswilcox 		}
1333355d6bb5Sswilcox 		/*
1334355d6bb5Sswilcox 		 * ...else if it's a directory with parent == -1, then
1335355d6bb5Sswilcox 		 * we've not gotten far enough to know connectivity,
1336355d6bb5Sswilcox 		 * and it'll get handled automatically later.
1337355d6bb5Sswilcox 		 */
1338355d6bb5Sswilcox 	}
1339355d6bb5Sswilcox 
1340355d6bb5Sswilcox no_parent_update:
1341355d6bb5Sswilcox 	init_inodesc(&idesc);
13427c478bd9Sstevel@tonic-gate 	idesc.id_type = ADDR;
13437c478bd9Sstevel@tonic-gate 	idesc.id_func = pass4check;
13447c478bd9Sstevel@tonic-gate 	idesc.id_number = ino;
13457c478bd9Sstevel@tonic-gate 	idesc.id_fix = DONTKNOW;
1346355d6bb5Sswilcox 	idesc.id_truncto = howmany(new_length, sblock.fs_bsize);
13477c478bd9Sstevel@tonic-gate 	dp = ginode(ino);
1348*2ee92411SAndrew Balfour 	if (!islink && ckinode(dp, &idesc, CKI_TRUNCATE) & ALTERED)
1349355d6bb5Sswilcox 		inodirty();
1350355d6bb5Sswilcox 
1351355d6bb5Sswilcox 	/*
1352355d6bb5Sswilcox 	 * This has to be done after ckinode(), so that all of
1353355d6bb5Sswilcox 	 * the fragments get visited.  Note that we assume we're
1354355d6bb5Sswilcox 	 * always truncating to a block boundary, rather than a
1355355d6bb5Sswilcox 	 * fragment boundary.
1356355d6bb5Sswilcox 	 */
1357355d6bb5Sswilcox 	dp = ginode(ino);
1358355d6bb5Sswilcox 	dp->di_size = new_length;
1359355d6bb5Sswilcox 
1360355d6bb5Sswilcox 	/*
1361355d6bb5Sswilcox 	 * Clear now-obsolete pointers.
1362355d6bb5Sswilcox 	 */
1363355d6bb5Sswilcox 	for (dblk = idesc.id_truncto + 1; dblk < NDADDR; dblk++) {
1364355d6bb5Sswilcox 		dp->di_db[dblk] = 0;
1365355d6bb5Sswilcox 	}
1366355d6bb5Sswilcox 
1367355d6bb5Sswilcox 	ilevel = get_indir_offsets(-1, idesc.id_truncto, NULL, NULL);
1368355d6bb5Sswilcox 	for (ilevel++; ilevel < NIADDR; ilevel++) {
1369355d6bb5Sswilcox 		dp->di_ib[ilevel] = 0;
1370355d6bb5Sswilcox 	}
1371355d6bb5Sswilcox 
1372355d6bb5Sswilcox 	inodirty();
1373355d6bb5Sswilcox }
1374355d6bb5Sswilcox 
1375355d6bb5Sswilcox /*
1376355d6bb5Sswilcox  * Release an inode's resources, then release the inode itself.
1377355d6bb5Sswilcox  */
1378355d6bb5Sswilcox void
freeino(fsck_ino_t ino,int update_parent)1379355d6bb5Sswilcox freeino(fsck_ino_t ino, int update_parent)
1380355d6bb5Sswilcox {
1381355d6bb5Sswilcox 	int cg;
1382355d6bb5Sswilcox 	struct dinode *dp;
1383355d6bb5Sswilcox 	struct cg *cgp;
1384355d6bb5Sswilcox 
1385355d6bb5Sswilcox 	n_files--;
1386355d6bb5Sswilcox 	dp = ginode(ino);
1387c0ab6c5eSjk201079 	/*
1388c0ab6c5eSjk201079 	 * We need to make sure that the file is really a large file.
1389c0ab6c5eSjk201079 	 * Everything bigger than UFS_MAXOFFSET_T is treated as a file with
1390c0ab6c5eSjk201079 	 * negative size, which shall be cleared. (see verify_inode() in
1391c0ab6c5eSjk201079 	 * pass1.c)
1392c0ab6c5eSjk201079 	 */
1393c0ab6c5eSjk201079 	if (dp->di_size > (u_offset_t)MAXOFF_T &&
1394592beac6Smc208700 	    dp->di_size <= (u_offset_t)UFS_MAXOFFSET_T &&
1395592beac6Smc208700 	    ftypeok(dp) &&
1396592beac6Smc208700 	    (dp->di_mode & IFMT) != IFBLK &&
1397592beac6Smc208700 	    (dp->di_mode & IFMT) != IFCHR) {
1398355d6bb5Sswilcox 		largefile_count--;
1399355d6bb5Sswilcox 	}
1400355d6bb5Sswilcox 	truncino(ino, 0, update_parent);
1401355d6bb5Sswilcox 
1402355d6bb5Sswilcox 	dp = ginode(ino);
1403355d6bb5Sswilcox 	if ((dp->di_mode & IFMT) == IFATTRDIR) {
1404355d6bb5Sswilcox 		clearshadow(ino, &attrclientinfo);
1405355d6bb5Sswilcox 		dp = ginode(ino);
1406355d6bb5Sswilcox 	}
1407355d6bb5Sswilcox 
14087c478bd9Sstevel@tonic-gate 	clearinode(dp);
14097c478bd9Sstevel@tonic-gate 	inodirty();
14107c478bd9Sstevel@tonic-gate 	statemap[ino] = USTATE;
1411355d6bb5Sswilcox 
1412355d6bb5Sswilcox 	/*
1413355d6bb5Sswilcox 	 * Keep the disk in sync with us so that pass5 doesn't get
1414355d6bb5Sswilcox 	 * upset about spurious inconsistencies.
1415355d6bb5Sswilcox 	 */
1416355d6bb5Sswilcox 	cg = itog(&sblock, ino);
1417355d6bb5Sswilcox 	(void) getblk(&cgblk, (diskaddr_t)cgtod(&sblock, cg),
1418355d6bb5Sswilcox 	    (size_t)sblock.fs_cgsize);
1419355d6bb5Sswilcox 	cgp = cgblk.b_un.b_cg;
1420355d6bb5Sswilcox 	clrbit(cg_inosused(cgp), ino % sblock.fs_ipg);
1421355d6bb5Sswilcox 	cgp->cg_cs.cs_nifree += 1;
1422355d6bb5Sswilcox 	cgdirty();
1423355d6bb5Sswilcox 	sblock.fs_cstotal.cs_nifree += 1;
1424355d6bb5Sswilcox 	sbdirty();
1425355d6bb5Sswilcox }
1426355d6bb5Sswilcox 
1427355d6bb5Sswilcox void
init_inoinfo(struct inoinfo * inp,struct dinode * dp,fsck_ino_t inum)1428355d6bb5Sswilcox init_inoinfo(struct inoinfo *inp, struct dinode *dp, fsck_ino_t inum)
1429355d6bb5Sswilcox {
1430355d6bb5Sswilcox 	inp->i_parent = ((inum == UFSROOTINO) ? UFSROOTINO : (fsck_ino_t)0);
1431355d6bb5Sswilcox 	inp->i_dotdot = (fsck_ino_t)0;
1432355d6bb5Sswilcox 	inp->i_isize = (offset_t)dp->di_size;
1433355d6bb5Sswilcox 	inp->i_blkssize = (NDADDR + NIADDR) * sizeof (daddr32_t);
1434355d6bb5Sswilcox 	inp->i_extattr = dp->di_oeftflag;
1435355d6bb5Sswilcox 	(void) memmove((void *)&inp->i_blks[0], (void *)&dp->di_db[0],
1436355d6bb5Sswilcox 	    inp->i_blkssize);
1437355d6bb5Sswilcox }
1438355d6bb5Sswilcox 
1439355d6bb5Sswilcox /*
1440355d6bb5Sswilcox  * Return the inode number in the ".." entry of the provided
1441355d6bb5Sswilcox  * directory inode.
1442355d6bb5Sswilcox  */
1443355d6bb5Sswilcox static int
lookup_dotdot_ino(fsck_ino_t ino)1444355d6bb5Sswilcox lookup_dotdot_ino(fsck_ino_t ino)
1445355d6bb5Sswilcox {
1446355d6bb5Sswilcox 	struct inodesc idesc;
1447355d6bb5Sswilcox 
1448355d6bb5Sswilcox 	init_inodesc(&idesc);
1449355d6bb5Sswilcox 	idesc.id_type = DATA;
1450355d6bb5Sswilcox 	idesc.id_func = findino;
1451355d6bb5Sswilcox 	idesc.id_name = "..";
1452355d6bb5Sswilcox 	idesc.id_number = ino;
1453355d6bb5Sswilcox 	idesc.id_fix = NOFIX;
1454355d6bb5Sswilcox 
1455355d6bb5Sswilcox 	if ((ckinode(ginode(ino), &idesc, CKI_TRAVERSE) & FOUND) != 0) {
1456355d6bb5Sswilcox 		return (idesc.id_parent);
1457355d6bb5Sswilcox 	}
1458355d6bb5Sswilcox 
1459355d6bb5Sswilcox 	return (0);
1460355d6bb5Sswilcox }
1461355d6bb5Sswilcox 
1462355d6bb5Sswilcox /*
1463355d6bb5Sswilcox  * Convenience wrapper around ckinode(findino()).
1464355d6bb5Sswilcox  */
1465355d6bb5Sswilcox int
lookup_named_ino(fsck_ino_t dir,caddr_t name)1466355d6bb5Sswilcox lookup_named_ino(fsck_ino_t dir, caddr_t name)
1467355d6bb5Sswilcox {
1468355d6bb5Sswilcox 	struct inodesc idesc;
1469355d6bb5Sswilcox 
1470355d6bb5Sswilcox 	init_inodesc(&idesc);
1471355d6bb5Sswilcox 	idesc.id_type = DATA;
1472355d6bb5Sswilcox 	idesc.id_func = findino;
1473355d6bb5Sswilcox 	idesc.id_name = name;
1474355d6bb5Sswilcox 	idesc.id_number = dir;
1475355d6bb5Sswilcox 	idesc.id_fix = NOFIX;
1476355d6bb5Sswilcox 
1477355d6bb5Sswilcox 	if ((ckinode(ginode(dir), &idesc, CKI_TRAVERSE) & FOUND) != 0) {
1478355d6bb5Sswilcox 		return (idesc.id_parent);
1479355d6bb5Sswilcox 	}
1480355d6bb5Sswilcox 
1481355d6bb5Sswilcox 	return (0);
1482355d6bb5Sswilcox }
1483355d6bb5Sswilcox 
1484355d6bb5Sswilcox /*
1485355d6bb5Sswilcox  * Marks inodes that are being orphaned and might need to be reconnected
1486355d6bb5Sswilcox  * by pass4().  The inode we're traversing is the directory whose
1487355d6bb5Sswilcox  * contents will be reconnected later.  id_parent is the lfn at which
1488355d6bb5Sswilcox  * to start looking at said contents.
1489355d6bb5Sswilcox  */
1490355d6bb5Sswilcox static int
mark_a_delayed_inode(struct inodesc * idesc)1491355d6bb5Sswilcox mark_a_delayed_inode(struct inodesc *idesc)
1492355d6bb5Sswilcox {
1493355d6bb5Sswilcox 	struct direct *dirp = idesc->id_dirp;
1494355d6bb5Sswilcox 
1495355d6bb5Sswilcox 	if (idesc->id_lbn < idesc->id_parent) {
1496355d6bb5Sswilcox 		return (KEEPON);
1497355d6bb5Sswilcox 	}
1498355d6bb5Sswilcox 
1499355d6bb5Sswilcox 	if (dirp->d_ino != 0 &&
1500355d6bb5Sswilcox 	    strcmp(dirp->d_name, ".") != 0 &&
1501355d6bb5Sswilcox 	    strcmp(dirp->d_name, "..") != 0) {
1502355d6bb5Sswilcox 		statemap[dirp->d_ino] &= ~INFOUND;
1503355d6bb5Sswilcox 		statemap[dirp->d_ino] |= INDELAYD;
1504355d6bb5Sswilcox 	}
1505355d6bb5Sswilcox 
1506355d6bb5Sswilcox 	return (KEEPON);
1507355d6bb5Sswilcox }
1508355d6bb5Sswilcox 
1509355d6bb5Sswilcox static void
mark_delayed_inodes(fsck_ino_t ino,daddr32_t first_lfn)1510355d6bb5Sswilcox mark_delayed_inodes(fsck_ino_t ino, daddr32_t first_lfn)
1511355d6bb5Sswilcox {
1512355d6bb5Sswilcox 	struct dinode *dp;
1513355d6bb5Sswilcox 	struct inodesc idelayed;
1514355d6bb5Sswilcox 
1515355d6bb5Sswilcox 	init_inodesc(&idelayed);
1516355d6bb5Sswilcox 	idelayed.id_number = ino;
1517355d6bb5Sswilcox 	idelayed.id_type = DATA;
1518355d6bb5Sswilcox 	idelayed.id_fix = NOFIX;
1519355d6bb5Sswilcox 	idelayed.id_func = mark_a_delayed_inode;
1520355d6bb5Sswilcox 	idelayed.id_parent = first_lfn;
1521355d6bb5Sswilcox 	idelayed.id_entryno = 2;
1522355d6bb5Sswilcox 
1523355d6bb5Sswilcox 	dp = ginode(ino);
1524355d6bb5Sswilcox 	(void) ckinode(dp, &idelayed, CKI_TRAVERSE);
1525355d6bb5Sswilcox }
1526355d6bb5Sswilcox 
1527355d6bb5Sswilcox /*
1528355d6bb5Sswilcox  * Clear the i_oeftflag/extended attribute pointer from INO.
1529355d6bb5Sswilcox  */
1530355d6bb5Sswilcox void
clearattrref(fsck_ino_t ino)1531355d6bb5Sswilcox clearattrref(fsck_ino_t ino)
1532355d6bb5Sswilcox {
1533355d6bb5Sswilcox 	struct dinode *dp;
1534355d6bb5Sswilcox 
1535355d6bb5Sswilcox 	dp = ginode(ino);
1536355d6bb5Sswilcox 	if (debug) {
1537355d6bb5Sswilcox 		if (dp->di_oeftflag == 0)
1538355d6bb5Sswilcox 			(void) printf("clearattref: no attr to clear on %d\n",
1539355d6bb5Sswilcox 			    ino);
1540355d6bb5Sswilcox 	}
1541355d6bb5Sswilcox 
1542355d6bb5Sswilcox 	dp->di_oeftflag = 0;
1543355d6bb5Sswilcox 	inodirty();
15447c478bd9Sstevel@tonic-gate }
1545