diff options
author | Federico G. Schwindt <fgsch@cvs.openbsd.org> | 2003-08-14 16:55:25 +0000 |
---|---|---|
committer | Federico G. Schwindt <fgsch@cvs.openbsd.org> | 2003-08-14 16:55:25 +0000 |
commit | aaafb0d8ff6924de40a673e0174f9f030cafd15f (patch) | |
tree | b9d84155837cb34d271e10cd15a0325e15638ac7 /sys | |
parent | 441657454d08b32b0efca0253811b1c7ca6bf509 (diff) |
in some fs (ie. procfs), readdir won't return a cookie, so calculate
it based on the offset and reclen. with this, a emulated ls binary
(ie. linux) correctly list the files in such filesystems. also plug some
memory leak and remove a unneeded panic.
adapted from FreeBSD.
henning@ tedu@ ok. some other people ok'ed this in the past as well.
Diffstat (limited to 'sys')
-rw-r--r-- | sys/compat/common/compat_dir.c | 80 |
1 files changed, 54 insertions, 26 deletions
diff --git a/sys/compat/common/compat_dir.c b/sys/compat/common/compat_dir.c index 366e23097ea..79a66431d10 100644 --- a/sys/compat/common/compat_dir.c +++ b/sys/compat/common/compat_dir.c @@ -1,4 +1,4 @@ -/* $OpenBSD: compat_dir.c,v 1.3 2002/03/14 01:26:49 millert Exp $ */ +/* $OpenBSD: compat_dir.c,v 1.4 2003/08/14 16:55:24 fgsch Exp $ */ /* * Copyright (c) 2000 Constantine Sapuntzakis @@ -48,12 +48,13 @@ readdir_with_callback(fp, off, nbytes, appendfunc, arg) int (*appendfunc)(void *, struct dirent *, off_t); void *arg; { + struct dirent *bdp; caddr_t inp, buf; int buflen; struct uio auio; struct iovec aiov; int eofflag = 0; - u_long *cookiebuf = NULL, *cookie; + u_long *cookies = NULL, *cookiep; int ncookies = 0; int error, len, reclen; off_t newoff = *off; @@ -65,7 +66,7 @@ readdir_with_callback(fp, off, nbytes, appendfunc, arg) vp = (struct vnode *)fp->f_data; - if (vp->v_type != VDIR) /* XXX vnode readdir op should do this */ + if (vp->v_type != VDIR) return (EINVAL); if ((error = VOP_GETATTR(vp, &va, fp->f_cred, curproc)) != 0) @@ -88,57 +89,84 @@ again: auio.uio_procp = curproc; auio.uio_resid = buflen; auio.uio_offset = newoff; - + + if (cookies) { + free(cookies, M_TEMP); + cookies = NULL; + } + error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag, &ncookies, - &cookiebuf); + &cookies); if (error) goto out; - if ((len = buflen - auio.uio_resid) == 0) + if ((len = buflen - auio.uio_resid) <= 0) goto eof; - if (cookiebuf == NULL) { - error = EPERM; - goto out; + cookiep = cookies; + inp = buf; + + 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 <= newoff) { + bdp = (struct dirent *)inp; + len -= bdp->d_reclen; + inp += bdp->d_reclen; + cookiep++; + ncookies--; + } } - cookie = cookiebuf; - inp = buf; + for (; len > 0; len -= reclen, inp += reclen) { + if (cookiep && ncookies == 0) + break; - for (; - len > 0; - len -= reclen, inp += reclen, ++cookie) { - struct dirent *bdp = (struct dirent *)inp; + bdp = (struct dirent *)inp; reclen = bdp->d_reclen; - if (len < reclen) break; - if (reclen & 3) - panic("readdir_with_callback: bad reclen"); + if (len < reclen) + break; + + if (reclen & 3) { + error = EFAULT; + goto out; + } /* Skip holes */ if (bdp->d_fileno != 0) { - if ((error = (*appendfunc) (arg, bdp, *cookie)) - != 0) { + if ((error = (*appendfunc) (arg, bdp, + (cookiep) ? *cookiep : (newoff + reclen))) != 0) { if (error == ENOMEM) error = 0; break; } } - newoff = *cookie; + if (cookiep) { + newoff = *cookiep++; + ncookies--; + } else + newoff += reclen; } if (len <= 0 && !eofflag) goto again; - eof: - out: - if (error == 0) +eof: +out: + if (error == 0) *off = newoff; + if (cookies) + free(cookies, M_TEMP); + VOP_UNLOCK(vp, 0, curproc); - if (cookiebuf) - free(cookiebuf, M_TEMP); free(buf, M_TEMP); return (error); } |