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:
Doug Rabson 1997-04-05 14:50:56 +00:00
parent 8e71151378
commit d41979a063
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=24654
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 @@ 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;

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 @@ 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;