summaryrefslogtreecommitdiffstats
path: root/sys/ufs
diff options
context:
space:
mode:
authoriedowse <iedowse@FreeBSD.org>2002-03-11 19:13:22 +0000
committeriedowse <iedowse@FreeBSD.org>2002-03-11 19:13:22 +0000
commitcf446bea56795da1b463bad98883efc41181b48f (patch)
tree513042be847a836abbcbfd41ef542dbf397ea2d7 /sys/ufs
parenta5126a967f2db5a17f2668702a5fc8ac5726466a (diff)
downloadFreeBSD-src-cf446bea56795da1b463bad98883efc41181b48f.zip
FreeBSD-src-cf446bea56795da1b463bad98883efc41181b48f.tar.gz
Fix a bug in ufsdirhash_adjfree() that caused it to incorrectly
update the free-space statistics in some cases. The problem affected directory blocks when the free space dropped below the size of the maximum allowed entry size. When this happened, the free-space summary information could claim that there are no further blocks that can fit a maximum-size entry, even if there are. The effect of this bug is that the directory may be enlarged even though there is space within the directory for the new entry. This wastes disk space and has a negative impact on performance. Fix it by correctly computing the dh_firstfree array index, adding a helper macro for clarity. Put an extra sanity check into ufsdirhash_checkblock() to detect the situation in future. Found by: dwmalone Reviewed by: dwmalone MFC after: 1 week
Diffstat (limited to 'sys/ufs')
-rw-r--r--sys/ufs/ufs/ufs_dirhash.c17
1 files changed, 7 insertions, 10 deletions
diff --git a/sys/ufs/ufs/ufs_dirhash.c b/sys/ufs/ufs/ufs_dirhash.c
index f147ccb..137a8c6 100644
--- a/sys/ufs/ufs/ufs_dirhash.c
+++ b/sys/ufs/ufs/ufs_dirhash.c
@@ -57,6 +57,7 @@
#define WRAPINCR(val, limit) (((val) + 1 == (limit)) ? 0 : ((val) + 1))
#define OFSFMT(vp) ((vp)->v_mount->mnt_maxsymlinklen <= 0)
+#define BLKFREE2IDX(n) ((n) > DH_NFSTATS ? DH_NFSTATS : (n))
static MALLOC_DEFINE(M_DIRHASH, "UFS dirhash", "UFS directory hash tables");
@@ -845,12 +846,12 @@ ufsdirhash_checkblock(struct inode *ip, char *buf, doff_t offset)
if (dh->dh_blkfree[block] * DIRALIGN != nfree)
panic("ufsdirhash_checkblock: bad free count");
- ffslot = nfree / DIRALIGN;
- if (ffslot > DH_NFSTATS)
- ffslot = DH_NFSTATS;
+ ffslot = BLKFREE2IDX(nfree / DIRALIGN);
for (i = 0; i <= DH_NFSTATS; i++)
if (dh->dh_firstfree[i] == block && i != ffslot)
panic("ufsdirhash_checkblock: bad first-free");
+ if (dh->dh_firstfree[ffslot] == -1)
+ panic("ufsdirhash_checkblock: missing first-free entry");
mtx_unlock(&dh->dh_mtx);
}
@@ -880,20 +881,16 @@ ufsdirhash_adjfree(struct dirhash *dh, doff_t offset, int diff)
block = offset / DIRBLKSIZ;
KASSERT(block < dh->dh_nblk && block < dh->dh_dirblks,
("dirhash bad offset"));
- ofidx = dh->dh_blkfree[block];
- if (ofidx > DH_NFSTATS)
- ofidx = DH_NFSTATS;
+ ofidx = BLKFREE2IDX(dh->dh_blkfree[block]);
dh->dh_blkfree[block] = (int)dh->dh_blkfree[block] + (diff / DIRALIGN);
- nfidx = dh->dh_blkfree[block];
- if (nfidx > DH_NFSTATS)
- nfidx = DH_NFSTATS;
+ nfidx = BLKFREE2IDX(dh->dh_blkfree[block]);
/* Update the `first free' list if necessary. */
if (ofidx != nfidx) {
/* If removing, scan forward for the next block. */
if (dh->dh_firstfree[ofidx] == block) {
for (i = block + 1; i < dh->dh_dirblks; i++)
- if (dh->dh_blkfree[i] == ofidx)
+ if (BLKFREE2IDX(dh->dh_blkfree[i]) == ofidx)
break;
dh->dh_firstfree[ofidx] = (i < dh->dh_dirblks) ? i : -1;
}
OpenPOWER on IntegriCloud