diff options
Diffstat (limited to 'sbin/fsck_ffs/dir.c')
| -rw-r--r-- | sbin/fsck_ffs/dir.c | 195 |
1 files changed, 163 insertions, 32 deletions
diff --git a/sbin/fsck_ffs/dir.c b/sbin/fsck_ffs/dir.c index 42ecf4112253..3eb0b63c0988 100644 --- a/sbin/fsck_ffs/dir.c +++ b/sbin/fsck_ffs/dir.c @@ -29,14 +29,6 @@ * SUCH DAMAGE. */ -#if 0 -#ifndef lint -static const char sccsid[] = "@(#)dir.c 8.8 (Berkeley) 4/28/95"; -#endif /* not lint */ -#endif -#include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - #include <sys/param.h> #include <sys/time.h> #include <sys/types.h> @@ -63,7 +55,6 @@ static struct dirtemplate dirhead = { static int chgino(struct inodesc *); static int dircheck(struct inodesc *, struct bufarea *, struct direct *); static int expanddir(struct inode *ip, char *name); -static void freedir(ino_t ino, ino_t parent); static struct direct *fsck_readdir(struct inodesc *); static struct bufarea *getdirblk(ufs2_daddr_t blkno, long size); static int lftempname(char *bufp, ino_t ino); @@ -89,6 +80,7 @@ propagate(void) if (inoinfo(inp->i_parent)->ino_state == DFOUND && INO_IS_DUNFOUND(inp->i_number)) { inoinfo(inp->i_number)->ino_state = DFOUND; + check_dirdepth(inp); change++; } } @@ -96,6 +88,105 @@ propagate(void) } /* + * Check that the recorded depth of the directory is correct. + */ +void +check_dirdepth(struct inoinfo *inp) +{ + struct inoinfo *parentinp; + struct inode ip; + union dinode *dp; + int saveresolved; + size_t size; + static int updateasked, dirdepthupdate; + + if ((parentinp = getinoinfo(inp->i_parent)) == NULL) { + pfatal("check_dirdepth: UNKNOWN PARENT DIR"); + return; + } + /* + * If depth is correct, nothing to do. + */ + if (parentinp->i_depth + 1 == inp->i_depth) + return; + /* + * Only the root inode should have depth of 0, so if any other + * directory has a depth of 0 then this is an old filesystem + * that has not been tracking directory depth. Ask just once + * whether it should start tracking directory depth. + */ + if (inp->i_depth == 0 && updateasked == 0) { + updateasked = 1; + if (preen) { + pwarn("UPDATING FILESYSTEM TO TRACK DIRECTORY DEPTH\n"); + dirdepthupdate = 1; + } else { + /* + * The file system can be marked clean even if + * a directory does not have the right depth. + * Hence, resolved should not be cleared when + * the filesystem does not update directory depths. + */ + saveresolved = resolved; + dirdepthupdate = + reply("UPDATE FILESYSTEM TO TRACK DIRECTORY DEPTH"); + resolved = saveresolved; + } + } + /* + * If we are not converting or we are running in no-write mode + * there is nothing more to do. + */ + if ((inp->i_depth == 0 && dirdepthupdate == 0) || + (fswritefd < 0 && bkgrdflag == 0)) + return; + /* + * Individual directory at wrong depth. Report it and correct if + * in preen mode or ask if in interactive mode. Note that if a + * directory is renamed to a new location that is at a different + * level in the tree, its depth will be recalculated, but none of + * the directories that it contains will be updated. Thus it is + * not unexpected to find directories with incorrect depths. No + * operational harm will come from this though new directory + * placement in the subtree may not be as optimal until the depths + * of the affected directories are corrected. + * + * To avoid much spurious output on otherwise clean filesystems + * we only generate detailed output when the debug flag is given. + */ + ginode(inp->i_number, &ip); + dp = ip.i_dp; + if (inp->i_depth != 0 && debug) { + pwarn("DIRECTORY"); + prtinode(&ip); + printf(" DEPTH %d SHOULD BE %d", inp->i_depth, + parentinp->i_depth + 1); + if (preen == 0 && reply("ADJUST") == 0) { + irelse(&ip); + return; + } + if (preen) + printf(" (ADJUSTED)\n"); + } + inp->i_depth = parentinp->i_depth + 1; + if (bkgrdflag == 0) { + DIP_SET(dp, di_dirdepth, inp->i_depth); + inodirty(&ip); + } else { + cmd.value = inp->i_number; + cmd.size = (int64_t)inp->i_depth - DIP(dp, di_dirdepth); + if (debug) + printf("adjdepth ino %ld amt %jd\n", (long)cmd.value, + (intmax_t)cmd.size); + size = MIBSIZE; + if (sysctlnametomib("vfs.ffs.adjdepth", adjdepth, &size) < 0 || + sysctl(adjdepth, MIBSIZE, 0, 0, &cmd, sizeof cmd) == -1) + rwerror("ADJUST INODE DEPTH", cmd.value); + } + irelse(&ip); +} + +/* * Scan each entry in a directory block. */ int @@ -161,7 +252,7 @@ fsck_readdir(struct inodesc *idesc) dp = (struct direct *)(bp->b_un.b_buf + idesc->id_loc); /* * Only need to check current entry if it is the first in the - * the block, as later entries will have been checked in the + * block, as later entries will have been checked in the * previous call to this function. */ if (idesc->id_loc % DIRBLKSIZ != 0 || dircheck(idesc, bp, dp) != 0) { @@ -338,7 +429,7 @@ fileerror(ino_t cwd, ino_t ino, const char *errmesg) char pathbuf[MAXPATHLEN + 1]; pwarn("%s ", errmesg); - if (ino < UFS_ROOTINO || ino > maxino) { + if (ino < UFS_ROOTINO || ino >= maxino) { pfatal("out-of-range inode number %ju", (uintmax_t)ino); return; } @@ -422,7 +513,8 @@ adjust(struct inodesc *idesc, int lcnt) (long long)cmd.size); if (sysctl(adjrefcnt, MIBSIZE, 0, 0, &cmd, sizeof cmd) == -1) - rwerror("ADJUST INODE", cmd.value); + rwerror("ADJUST INODE LINK COUNT", + cmd.value); } } } @@ -472,8 +564,9 @@ linkup(ino_t orphan, ino_t parentdir, char *name) { struct inode ip; union dinode *dp; - int lostdir; + int lostdir, depth; ino_t oldlfdir; + struct inoinfo *inp; struct inodesc idesc; char tempname[BUFSIZ]; @@ -516,7 +609,7 @@ linkup(ino_t orphan, ino_t parentdir, char *name) if (preen) printf(" (CREATED)\n"); } else { - freedir(lfdir, UFS_ROOTINO); + freedirino(lfdir, UFS_ROOTINO); lfdir = 0; if (preen) printf("\n"); @@ -525,6 +618,7 @@ linkup(ino_t orphan, ino_t parentdir, char *name) } } irelse(&ip); + free(idesc.id_name); if (lfdir == 0) { pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY"); printf("\n\n"); @@ -545,7 +639,7 @@ linkup(ino_t orphan, ino_t parentdir, char *name) irelse(&ip); return (0); } - if ((changeino(UFS_ROOTINO, lfname, lfdir) & ALTERED) == 0) { + if ((changeino(UFS_ROOTINO, lfname, lfdir, 1) & ALTERED) == 0) { pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); irelse(&ip); return (0); @@ -574,23 +668,27 @@ linkup(ino_t orphan, ino_t parentdir, char *name) } inoinfo(orphan)->ino_linkcnt--; if (lostdir) { - if ((changeino(orphan, "..", lfdir) & ALTERED) == 0 && + depth = DIP(dp, di_dirdepth) + 1; + if ((changeino(orphan, "..", lfdir, depth) & ALTERED) == 0 && parentdir != (ino_t)-1) (void)makeentry(orphan, lfdir, ".."); DIP_SET(dp, di_nlink, DIP(dp, di_nlink) + 1); inodirty(&ip); inoinfo(lfdir)->ino_linkcnt++; pwarn("DIR I=%lu CONNECTED. ", (u_long)orphan); - if (parentdir != (ino_t)-1) { + inp = getinoinfo(parentdir); + if (parentdir != (ino_t)-1 && inp != NULL) { printf("PARENT WAS I=%lu\n", (u_long)parentdir); /* - * The parent directory, because of the ordering + * If the parent directory did not have to + * be replaced then because of the ordering * guarantees, has had the link count incremented * for the child, but no entry was made. This * fixes the parent link count so that fsck does * not need to be rerun. */ - inoinfo(parentdir)->ino_linkcnt++; + if ((inp->i_flags & INFO_NEW) != 0) + inoinfo(parentdir)->ino_linkcnt++; } if (preen == 0) printf("\n"); @@ -603,7 +701,7 @@ linkup(ino_t orphan, ino_t parentdir, char *name) * fix an entry in a directory. */ int -changeino(ino_t dir, const char *name, ino_t newnum) +changeino(ino_t dir, const char *name, ino_t newnum, int depth) { struct inodesc idesc; struct inode ip; @@ -617,7 +715,12 @@ changeino(ino_t dir, const char *name, ino_t newnum) idesc.id_name = strdup(name); idesc.id_parent = newnum; /* new value for name */ ginode(dir, &ip); - error = ckinode(ip.i_dp, &idesc); + if (((error = ckinode(ip.i_dp, &idesc)) & ALTERED) && newnum != 0) { + DIP_SET(ip.i_dp, di_dirdepth, depth); + inodirty(&ip); + getinoinfo(dir)->i_depth = depth; + } + free(idesc.id_name); irelse(&ip); return (error); } @@ -652,15 +755,18 @@ makeentry(ino_t parent, ino_t ino, const char *name) } if ((ckinode(dp, &idesc) & ALTERED) != 0) { irelse(&ip); + free(idesc.id_name); return (1); } getpathname(pathbuf, parent, parent); if (expanddir(&ip, pathbuf) == 0) { irelse(&ip); + free(idesc.id_name); return (0); } retval = ckinode(dp, &idesc) & ALTERED; irelse(&ip); + free(idesc.id_name); return (retval); } @@ -675,14 +781,17 @@ expanddir(struct inode *ip, char *name) struct bufarea *bp, *nbp; struct inodesc idesc; union dinode *dp; - int indiralloced; + long cg, indiralloced; char *cp; nbp = NULL; indiralloced = newblk = indirblk = 0; + memset(&idesc, 0, sizeof(struct inodesc)); + idesc.id_type = ADDR; pwarn("NO SPACE LEFT IN %s", name); if (!preen && reply("EXPAND") == 0) return (0); + cg = ino_to_cg(&sblock, ip->i_number); dp = ip->i_dp; filesize = DIP(dp, di_size); lastlbn = lblkno(&sblock, filesize); @@ -701,7 +810,8 @@ expanddir(struct inode *ip, char *name) bp = getdirblk(oldblk, lastlbnsize); if (bp->b_errs) goto bad; - if ((newblk = allocblk(sblock.fs_frag)) == 0) + newblk = allocblk(cg, sblock.fs_frag, std_checkblkavail); + if (newblk == 0) goto bad; nbp = getdatablk(newblk, sblock.fs_bsize, BT_DIRDATA); if (nbp->b_errs) @@ -720,6 +830,7 @@ expanddir(struct inode *ip, char *name) memmove(cp, &emptydir, sizeof emptydir); dirty(nbp); brelse(nbp); + binval(bp); idesc.id_blkno = oldblk; idesc.id_numfrags = numfrags(&sblock, lastlbnsize); (void)freeblock(&idesc); @@ -727,7 +838,7 @@ expanddir(struct inode *ip, char *name) printf(" (EXPANDED)\n"); return (1); } - if ((newblk = allocblk(sblock.fs_frag)) == 0) + if ((newblk = allocblk(cg, sblock.fs_frag, std_checkblkavail)) == 0) goto bad; bp = getdirblk(newblk, sblock.fs_bsize); if (bp->b_errs) @@ -745,8 +856,12 @@ expanddir(struct inode *ip, char *name) * Allocate indirect block if needed. */ if ((indirblk = DIP(dp, di_ib[0])) == 0) { - if ((indirblk = allocblk(sblock.fs_frag)) == 0) + indirblk = allocblk(cg, sblock.fs_frag, + std_checkblkavail); + if (indirblk == 0) { + binval(bp); goto bad; + } indiralloced = 1; } nbp = getdatablk(indirblk, sblock.fs_bsize, BT_LEVEL1); @@ -757,6 +872,7 @@ expanddir(struct inode *ip, char *name) DIP_SET(dp, di_ib[0], indirblk); DIP_SET(dp, di_blocks, DIP(dp, di_blocks) + btodb(sblock.fs_bsize)); + inodirty(ip); } IBLK_SET(nbp, lastlbn - UFS_NDADDR, newblk); dirty(nbp); @@ -770,8 +886,10 @@ expanddir(struct inode *ip, char *name) return (1); bad: pfatal(" (EXPANSION FAILED)\n"); - if (nbp != NULL) + if (nbp != NULL) { + binval(bp); brelse(nbp); + } if (newblk != 0) { idesc.id_blkno = newblk; idesc.id_numfrags = sblock.fs_frag; @@ -796,8 +914,8 @@ allocdir(ino_t parent, ino_t request, int mode) struct inode ip; union dinode *dp; struct bufarea *bp; - struct inoinfo *inp; struct dirtemplate *dirp; + struct inoinfo *inp, *parentinp; ino = allocino(request, IFDIR|mode); if (ino == 0) @@ -822,8 +940,12 @@ allocdir(ino_t parent, ino_t request, int mode) DIP_SET(dp, di_nlink, 2); inodirty(&ip); if (ino == UFS_ROOTINO) { + inp = cacheino(dp, ino); + inp->i_parent = parent; + inp->i_dotdot = parent; + inp->i_flags |= INFO_NEW; + inoinfo(ino)->ino_type = DT_DIR; inoinfo(ino)->ino_linkcnt = DIP(dp, di_nlink); - cacheino(dp, ino); irelse(&ip); return(ino); } @@ -832,10 +954,18 @@ allocdir(ino_t parent, ino_t request, int mode) irelse(&ip); return (0); } - cacheino(dp, ino); - inp = getinoinfo(ino); + inp = cacheino(dp, ino); inp->i_parent = parent; inp->i_dotdot = parent; + inp->i_flags |= INFO_NEW; + if ((parentinp = getinoinfo(inp->i_parent)) == NULL) { + pfatal("allocdir: UNKNOWN PARENT DIR"); + } else { + inp->i_depth = parentinp->i_depth + 1; + DIP_SET(dp, di_dirdepth, inp->i_depth); + inodirty(&ip); + } + inoinfo(ino)->ino_type = DT_DIR; inoinfo(ino)->ino_state = inoinfo(parent)->ino_state; if (inoinfo(ino)->ino_state == DSTATE) { inoinfo(ino)->ino_linkcnt = DIP(dp, di_nlink); @@ -853,8 +983,8 @@ allocdir(ino_t parent, ino_t request, int mode) /* * free a directory inode */ -static void -freedir(ino_t ino, ino_t parent) +void +freedirino(ino_t ino, ino_t parent) { struct inode ip; union dinode *dp; @@ -866,6 +996,7 @@ freedir(ino_t ino, ino_t parent) inodirty(&ip); irelse(&ip); } + removecachedino(ino); freeino(ino); } |
