diff options
author | dfr <dfr@FreeBSD.org> | 1997-04-05 14:50:56 +0000 |
---|---|---|
committer | dfr <dfr@FreeBSD.org> | 1997-04-05 14:50:56 +0000 |
commit | e4e23cf66545d6fde10e8385022dc46d69b1289f (patch) | |
tree | ea17072b2e29de49eece57273eaa394781ea24b5 /sys/compat | |
parent | a751ade0a48ce0469fb54d7b84223d56892393bb (diff) | |
download | FreeBSD-src-e4e23cf66545d6fde10e8385022dc46d69b1289f.zip FreeBSD-src-e4e23cf66545d6fde10e8385022dc46d69b1289f.tar.gz |
Fix linux_getdents so that it can cope with filesystems which translate
the directory format (ext2fs, cd9660). For these filesystems, it must use
cookies to find the correct offset to use for subsequent reads. Without it,
linux /bin/ls tends to loop re-reading the same block over and over again.
2.2 candidate.
Diffstat (limited to 'sys/compat')
-rw-r--r-- | sys/compat/linux/linux_file.c | 56 |
1 files changed, 46 insertions, 10 deletions
diff --git a/sys/compat/linux/linux_file.c b/sys/compat/linux/linux_file.c index e2e59ca..e6e71e9 100644 --- a/sys/compat/linux/linux_file.c +++ b/sys/compat/linux/linux_file.c @@ -25,7 +25,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $Id: linux_file.c,v 1.11 1997/02/22 09:38:20 peter Exp $ + * $Id: linux_file.c,v 1.12 1997/03/24 11:24:29 bde Exp $ */ #include <sys/param.h> @@ -414,7 +414,9 @@ linux_getdents(struct proc *p, struct linux_getdents_args *args, int *retval) struct vattr va; off_t off; struct linux_dirent linux_dirent; - int buflen, error, eofflag, nbytes, justone, blockoff; + int buflen, error, eofflag, nbytes, justone; + u_long *cookies = NULL, *cookiep; + int ncookies; #ifdef DEBUG printf("Linux-emul(%d): getdents(%d, *, %d)\n", @@ -445,8 +447,7 @@ linux_getdents(struct proc *p, struct linux_getdents_args *args, int *retval) justone = 0; off = fp->f_offset; - blockoff = off % DIRBLKSIZ; - buflen = max(DIRBLKSIZ, nbytes + blockoff); + buflen = max(DIRBLKSIZ, nbytes); buflen = min(buflen, MAXBSIZE); buf = malloc(buflen, M_TEMP, M_WAITOK); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); @@ -459,22 +460,47 @@ again: auio.uio_segflg = UIO_SYSSPACE; auio.uio_procp = p; auio.uio_resid = buflen; - auio.uio_offset = off - (off_t)blockoff; + auio.uio_offset = off; - error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag, NULL, NULL); + if (cookies) { + free(cookies, M_TEMP); + cookies = NULL; + } + + error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag, &ncookies, &cookies); if (error) { goto out; } inp = buf; - inp += blockoff; outp = (caddr_t) args->dent; resid = nbytes; - if ((len = buflen - auio.uio_resid - blockoff) == 0) { + if ((len = buflen - auio.uio_resid) <= 0) { goto eof; } + cookiep = cookies; + + if (cookies) { + /* + * When using cookies, the vfs has the option of reading from + * a different offset than that supplied (UFS truncates the + * offset to a block boundary to make sure that it never reads + * partway through a directory entry, even if the directory + * has been compacted). + */ + while (len > 0 && ncookies > 0 && *cookiep <= off) { + bdp = (struct dirent *) inp; + len -= bdp->d_reclen; + inp += bdp->d_reclen; + cookiep++; + ncookies--; + } + } + while (len > 0) { + if (cookiep && ncookies == 0) + break; bdp = (struct dirent *) inp; reclen = bdp->d_reclen; if (reclen & 3) { @@ -485,7 +511,11 @@ again: if (bdp->d_fileno == 0) { inp += reclen; - off += reclen; + if (cookiep) { + off = *cookiep++; + ncookies--; + } else + off += reclen; len -= reclen; continue; } @@ -510,7 +540,11 @@ again: goto out; } inp += reclen; - off += reclen; + if (cookiep) { + off = *cookiep++; + ncookies--; + } else + off += reclen; outp += linuxreclen; resid -= linuxreclen; len -= reclen; @@ -528,6 +562,8 @@ again: eof: *retval = nbytes - resid; out: + if (cookies) + free(cookies, M_TEMP); VOP_UNLOCK(vp, 0, p); free(buf, M_TEMP); return error; |