aboutsummaryrefslogtreecommitdiff
path: root/sbin/fsck_ffs/dir.c
diff options
context:
space:
mode:
Diffstat (limited to 'sbin/fsck_ffs/dir.c')
-rw-r--r--sbin/fsck_ffs/dir.c195
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);
}