diff options
author | iedowse <iedowse@FreeBSD.org> | 2001-08-26 01:25:12 +0000 |
---|---|---|
committer | iedowse <iedowse@FreeBSD.org> | 2001-08-26 01:25:12 +0000 |
commit | c8ef91ce6cf3cd4ae61ef32fb9d9bdfb7cfb4603 (patch) | |
tree | c0efb627e00a6fa545506e51afb35b29836a816a /sys/ufs | |
parent | 8638e55f32ea751d1be5fac53c95af56bdf048ba (diff) | |
download | FreeBSD-src-c8ef91ce6cf3cd4ae61ef32fb9d9bdfb7cfb4603.zip FreeBSD-src-c8ef91ce6cf3cd4ae61ef32fb9d9bdfb7cfb4603.tar.gz |
When compacting directories, ufs_direnter() always trusted DIRSIZ()
to supply the number of bytes to be bcopy()'d to move an entry. If
d_ino == 0 however, DIRSIZ() is not guaranteed to return a sensible
length, so ufs_direnter could end up corrupting a directory during
compaction. In practice I believe this can only happen after fsck_ffs
has fixed a previously-corrupted directory.
We now deal with any mid-block unused entries specially to avoid
using DIRSIZ() or bcopy() on such entries. We also ensure that the
variables 'dsize' and 'spacefree' contain meaningful values at all
times. Add a few comments to describe better this intricate piece
of code.
The special handling of mid-block unused entries makes the dirhash-
specific bugfix in the previous revision (1.53) now uncecessary,
so this change removes it.
Reviewed by: mckusick
Diffstat (limited to 'sys/ufs')
-rw-r--r-- | sys/ufs/ufs/ufs_lookup.c | 40 |
1 files changed, 29 insertions, 11 deletions
diff --git a/sys/ufs/ufs/ufs_lookup.c b/sys/ufs/ufs/ufs_lookup.c index f968388..a062330 100644 --- a/sys/ufs/ufs/ufs_lookup.c +++ b/sys/ufs/ufs/ufs_lookup.c @@ -869,26 +869,39 @@ ufs_direnter(dvp, tvp, dirp, cnp, newdirbp) * dp->i_offset + dp->i_count would yield the space. */ ep = (struct direct *)dirbuf; - dsize = DIRSIZ(OFSFMT(dvp), ep); + dsize = ep->d_ino ? DIRSIZ(OFSFMT(dvp), ep) : 0; spacefree = ep->d_reclen - dsize; for (loc = ep->d_reclen; loc < dp->i_count; ) { nep = (struct direct *)(dirbuf + loc); - if (ep->d_ino) { - /* trim the existing slot */ - ep->d_reclen = dsize; - ep = (struct direct *)((char *)ep + dsize); - } else { - /* overwrite; nothing there; header is ours */ - spacefree += dsize; + + /* Trim the existing slot (NB: dsize may be zero). */ + ep->d_reclen = dsize; + ep = (struct direct *)((char *)ep + dsize); + + /* Read nep->d_reclen now as the bcopy() may clobber it. */ + loc += nep->d_reclen; + if (nep->d_ino == 0) { + /* + * A mid-block unused entry. Such entries are + * never created by the kernel, but fsck_ffs + * can create them (and it doesn't fix them). + * + * Add up the free space, and initialise the + * relocated entry since we don't bcopy it. + */ + spacefree += nep->d_reclen; + ep->d_ino = 0; + dsize = 0; + continue; } dsize = DIRSIZ(OFSFMT(dvp), nep); spacefree += nep->d_reclen - dsize; #ifdef UFS_DIRHASH - if (dp->i_dirhash != NULL && nep->d_ino) - ufsdirhash_move(dp, nep, dp->i_offset + loc, + if (dp->i_dirhash != NULL) + ufsdirhash_move(dp, nep, + dp->i_offset + ((char *)nep - dirbuf), dp->i_offset + ((char *)ep - dirbuf)); #endif - loc += nep->d_reclen; if (DOINGSOFTDEP(dvp)) softdep_change_directoryentry_offset(dp, dirbuf, (caddr_t)nep, (caddr_t)ep, dsize); @@ -896,6 +909,11 @@ ufs_direnter(dvp, tvp, dirp, cnp, newdirbp) bcopy((caddr_t)nep, (caddr_t)ep, dsize); } /* + * Here, `ep' points to a directory entry containing `dsize' in-use + * bytes followed by `spacefree' unused bytes. If ep->d_ino == 0, + * then the entry is completely unused (dsize == 0). The value + * of ep->d_reclen is always indeterminate. + * * Update the pointer fields in the previous entry (if any), * copy in the new entry, and write out the block. */ |