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