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 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 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 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 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 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 * 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 * 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 * 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 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 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 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 * 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 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 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 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 * 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 946355d6bb5Sswilcox inodirty(void) 947355d6bb5Sswilcox { 9487c478bd9Sstevel@tonic-gate dirty(pbp); 9497c478bd9Sstevel@tonic-gate } 9507c478bd9Sstevel@tonic-gate 951355d6bb5Sswilcox static 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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