diff options
author | julian <julian@FreeBSD.org> | 2015-05-15 15:49:24 +0000 |
---|---|---|
committer | julian <julian@FreeBSD.org> | 2015-05-15 15:49:24 +0000 |
commit | 821292122271421ddd5428aa2be82c4b7ef04c0f (patch) | |
tree | 19a980e4a2a4a547b18b1f52fc3ea044c462d5d9 /lib/libc/gen/telldir.c | |
parent | 01231d5424a73de86a133ae557ed6c706adcd86f (diff) | |
download | FreeBSD-src-821292122271421ddd5428aa2be82c4b7ef04c0f.zip FreeBSD-src-821292122271421ddd5428aa2be82c4b7ef04c0f.tar.gz |
MFH: r282485
Tweak seekdir, telldir and readdir so that when htere are deletes going on,
as seek to teh last location saved will still work. This is needed for Samba
to be able to correctly handle delete requests from windows. This does not
completely fix seekdir when deletes are present but fixes the worst of the
problems. The real solution must involve some changes to the API for eh VFS
and getdirentries(2).
Obtained from: Panzura inc
MFH: r282550 (jhb@)
A few style fixes and expand the comment a bit on what _fixtelldir() is
doing.
MFH: r282560 (jhb@)
Tweak the comment here some more. In particular, the previous opening
sentence was a bit confusing.
Noted by: kib
Diffstat (limited to 'lib/libc/gen/telldir.c')
-rw-r--r-- | lib/libc/gen/telldir.c | 37 |
1 files changed, 37 insertions, 0 deletions
diff --git a/lib/libc/gen/telldir.c b/lib/libc/gen/telldir.c index d72b500..19cd6ee 100644 --- a/lib/libc/gen/telldir.c +++ b/lib/libc/gen/telldir.c @@ -101,9 +101,22 @@ _seekdir(dirp, loc) return; if (lp->loc_loc == dirp->dd_loc && lp->loc_seek == dirp->dd_seek) return; + + /* If it's within the same chunk of data, don't bother reloading. */ + if (lp->loc_seek == dirp->dd_seek) { + /* + * If we go back to 0 don't make the next readdir + * trigger a call to getdirentries(). + */ + if (lp->loc_loc == 0) + dirp->dd_flags |= __DTF_SKIPREAD; + dirp->dd_loc = lp->loc_loc; + return; + } (void) lseek(dirp->dd_fd, (off_t)lp->loc_seek, SEEK_SET); dirp->dd_seek = lp->loc_seek; dirp->dd_loc = 0; + dirp->dd_flags &= ~__DTF_SKIPREAD; /* current contents are invalid */ while (dirp->dd_loc < lp->loc_loc) { dp = _readdir_unlocked(dirp, 0); if (dp == NULL) @@ -112,6 +125,30 @@ _seekdir(dirp, loc) } /* + * After readdir returns the last entry in a block, a call to telldir + * returns a location that is after the end of that last entry. + * However, that location doesn't refer to a valid directory entry. + * Ideally, the call to telldir would return a location that refers to + * the first entry in the next block. That location is not known + * until the next block is read, so readdir calls this function after + * fetching a new block to fix any such telldir locations. + */ +void +_fixtelldir(DIR *dirp, long oldseek, long oldloc) +{ + struct ddloc *lp; + + lp = LIST_FIRST(&dirp->dd_td->td_locq); + if (lp != NULL) { + if (lp->loc_loc == oldloc && + lp->loc_seek == oldseek) { + lp->loc_seek = dirp->dd_seek; + lp->loc_loc = dirp->dd_loc; + } + } +} + +/* * Reclaim memory for telldir cookies which weren't used. */ void |