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.
This commit is contained in:
dfr 1997-04-05 14:50:56 +00:00
parent 478060edda
commit 415b2611ae
2 changed files with 92 additions and 20 deletions

View File

@ -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 @@ linux_getdents(struct proc *p, struct linux_getdents_args *args, int *retval)
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 @@ linux_getdents(struct proc *p, struct linux_getdents_args *args, int *retval)
if (bdp->d_fileno == 0) {
inp += reclen;
off += reclen;
if (cookiep) {
off = *cookiep++;
ncookies--;
} else
off += reclen;
len -= reclen;
continue;
}
@ -510,7 +540,11 @@ linux_getdents(struct proc *p, struct linux_getdents_args *args, int *retval)
goto out;
}
inp += reclen;
off += reclen;
if (cookiep) {
off = *cookiep++;
ncookies--;
} else
off += reclen;
outp += linuxreclen;
resid -= linuxreclen;
len -= reclen;
@ -528,6 +562,8 @@ linux_getdents(struct proc *p, struct linux_getdents_args *args, int *retval)
eof:
*retval = nbytes - resid;
out:
if (cookies)
free(cookies, M_TEMP);
VOP_UNLOCK(vp, 0, p);
free(buf, M_TEMP);
return error;

View File

@ -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 @@ linux_getdents(struct proc *p, struct linux_getdents_args *args, int *retval)
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 @@ linux_getdents(struct proc *p, struct linux_getdents_args *args, int *retval)
if (bdp->d_fileno == 0) {
inp += reclen;
off += reclen;
if (cookiep) {
off = *cookiep++;
ncookies--;
} else
off += reclen;
len -= reclen;
continue;
}
@ -510,7 +540,11 @@ linux_getdents(struct proc *p, struct linux_getdents_args *args, int *retval)
goto out;
}
inp += reclen;
off += reclen;
if (cookiep) {
off = *cookiep++;
ncookies--;
} else
off += reclen;
outp += linuxreclen;
resid -= linuxreclen;
len -= reclen;
@ -528,6 +562,8 @@ linux_getdents(struct proc *p, struct linux_getdents_args *args, int *retval)
eof:
*retval = nbytes - resid;
out:
if (cookies)
free(cookies, M_TEMP);
VOP_UNLOCK(vp, 0, p);
free(buf, M_TEMP);
return error;