diff options
author | newton <newton@FreeBSD.org> | 1999-12-12 11:25:33 +0000 |
---|---|---|
committer | newton <newton@FreeBSD.org> | 1999-12-12 11:25:33 +0000 |
commit | 9b6cd98a6787478178465f676e3089da31d9fa11 (patch) | |
tree | 0566ffd45134ea8de8c288c5d440c3e333c2e6b7 /sys/compat/svr4/svr4_misc.c | |
parent | fe01fbadb8a6de3b79d5b9a7bd3ce5f1bc85d67d (diff) | |
download | FreeBSD-src-9b6cd98a6787478178465f676e3089da31d9fa11.zip FreeBSD-src-9b6cd98a6787478178465f676e3089da31d9fa11.tar.gz |
Replace the svr4_sys_getdents64() routine with a port of linux_getdents() --
differences between the VFS interface between FreeBSD and NetBSD make
it easier to pick up the Linux one than to continue development with the
NetBSD port.
This patch fixes a bug which caused duplicate filenames to be seen by
callers to svr4_sys_getdents64(), leading to malformed directory listings
from Solaris client programs.
Obtained from: The Linuxulator, with a pointer from marcel
Diffstat (limited to 'sys/compat/svr4/svr4_misc.c')
-rw-r--r-- | sys/compat/svr4/svr4_misc.c | 209 |
1 files changed, 124 insertions, 85 deletions
diff --git a/sys/compat/svr4/svr4_misc.c b/sys/compat/svr4/svr4_misc.c index 7d97a24..2abacee 100644 --- a/sys/compat/svr4/svr4_misc.c +++ b/sys/compat/svr4/svr4_misc.c @@ -225,47 +225,65 @@ svr4_sys_time(p, v) /* * Read SVR4-style directory entries. We suck them into kernel space so - * that they can be massaged before being copied out to user code. Like - * SunOS, we squish out `empty' entries. + * that they can be massaged before being copied out to user code. * - * This is quite ugly, but what do you expect from compatibility code? + * This code is ported from the Linux emulator: Changes to the VFS interface + * between FreeBSD and NetBSD have made it simpler to port it from there than + * to adapt the NetBSD version. */ - int svr4_sys_getdents64(p, uap) struct proc *p; struct svr4_sys_getdents64_args *uap; { - struct dirent *bdp; + register struct dirent *bdp; struct vnode *vp; - caddr_t inp, buf; /* BSD-format */ - int len, reclen; /* BSD-format */ - caddr_t outp; /* SVR4-format */ - int resid, svr4_reclen; /* SVR4-format */ + caddr_t inp, buf; /* BSD-format */ + int len, reclen; /* BSD-format */ + caddr_t outp; /* SVR4-format */ + int resid, svr4reclen=0; /* SVR4-format */ struct file *fp; struct uio auio; struct iovec aiov; - struct svr4_dirent64 idb; - off_t off; /* true file offset */ - int buflen, error, eofflag; - u_long *cookiebuf = NULL, *cookie; - int ncookies = 0, retval = 0, offcnt = 0; - - if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) + struct vattr va; + off_t off; + struct svr4_dirent64 svr4_dirent; + int buflen, error, eofflag, nbytes, justone; + u_long *cookies = NULL, *cookiep; + int ncookies; + + DPRINTF(("svr4_sys_getdents64(%d, *, %d)\n", + p->p_pid, SCARG(uap, fd), SCARG(uap, nbytes))); + if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) { return (error); + } if ((fp->f_flag & FREAD) == 0) return (EBADF); - vp = (struct vnode *)fp->f_data; + vp = (struct vnode *) fp->f_data; + if (vp->v_type != VDIR) return (EINVAL); - buflen = min(MAXBSIZE, SCARG(uap, nbytes)); - DPRINTF(("buflen = %d, spec = %d\n", buflen, SCARG(uap, nbytes))); + if ((error = VOP_GETATTR(vp, &va, p->p_ucred, p))) { + return error; + } + + nbytes = SCARG(uap, nbytes); + if (nbytes == 1) { + nbytes = sizeof (struct svr4_dirent64); + justone = 1; + } + else + justone = 0; + + off = fp->f_offset; +#define DIRBLKSIZ 512 /* XXX we used to use ufs's DIRBLKSIZ */ + buflen = max(DIRBLKSIZ, nbytes); + buflen = min(buflen, MAXBSIZE); buf = malloc(buflen, M_TEMP, M_WAITOK); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); - off = fp->f_offset; again: aiov.iov_base = buf; aiov.iov_len = buflen; @@ -276,90 +294,111 @@ again: auio.uio_procp = p; auio.uio_resid = buflen; auio.uio_offset = off; - DPRINTF((">>> off = %d\n", off)); - /* - * First we read into the malloc'ed buffer, then - * we massage it into user space, one record at a time. - */ - error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag, &ncookies, - &cookiebuf); - if (error) + 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; - outp = (caddr_t)SCARG(uap, dp); - resid = SCARG(uap, nbytes); - if ((len = buflen - auio.uio_resid) == 0) + outp = (caddr_t) SCARG(uap, dp); + resid = nbytes; + if ((len = buflen - auio.uio_resid) <= 0) { goto eof; + } - for (cookie = cookiebuf; len > 0; len -= reclen) { - bdp = (struct dirent *)inp; + 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) - panic("svr4_getdents64: bad reclen"); + if (reclen & 3) { + DPRINTF(("svr4_readdir: reclen=%d\n", reclen)); + error = EFAULT; + goto out; + } + if (bdp->d_fileno == 0) { - inp += reclen; /* it is a hole; squish it out */ -#if 0 - off = *cookie++; -#else - off += reclen; -#endif - DPRINTF(("+++ off = %d\n", off)); + inp += reclen; + if (cookiep) { + off = *cookiep++; + ncookies--; + } else + off += reclen; + len -= reclen; continue; } - svr4_reclen = SVR4_RECLEN(&idb, (bdp->d_namlen)); - - if (reclen > len || resid < svr4_reclen) { - /* entry too big for buffer, so just stop */ + svr4reclen = SVR4_RECLEN(&svr4_dirent, bdp->d_namlen); + if (reclen > len || resid < svr4reclen) { outp++; - DPRINTF(("+++ off = %d\n", off)); break; } -#if 0 - off = *cookie++; /* each entry points to the next */ -#else - off += reclen; -#endif - DPRINTF(("+++ off = %d\n", off)); - - /* - * Massage in place to make a SVR4-shaped dirent (otherwise - * we have to worry about touching user memory outside of - * the copyout() call). - */ - idb.d_ino = (svr4_ino64_t)bdp->d_fileno; - idb.d_off = (svr4_off64_t)off; - idb.d_reclen = (u_short)svr4_reclen; - strcpy(idb.d_name, bdp->d_name); - if ((error = copyout((caddr_t)&idb, outp, svr4_reclen))) + svr4_dirent.d_ino = (long) bdp->d_fileno; + if (justone) { + /* + * old svr4-style readdir usage. + */ + svr4_dirent.d_off = (svr4_off_t) svr4reclen; + svr4_dirent.d_reclen = (u_short) bdp->d_namlen; + } else { + svr4_dirent.d_off = (svr4_off_t)(off + reclen); + svr4_dirent.d_reclen = (u_short) svr4reclen; + } + strcpy(svr4_dirent.d_name, bdp->d_name); + if ((error = copyout((caddr_t)&svr4_dirent, outp, svr4reclen))) goto out; - DPRINTF(("d_ino = %d\nd_off = %d\nd_reclen = %d\n", - idb.d_ino, idb.d_off, (u_short)idb.d_reclen)); - DPRINTF(("d_name = %s\n", idb.d_name)); - DPRINTF(("(bdp->d_type = %d, reclen = %d, bdp->d_namelen = %d)\n", bdp->d_type, svr4_reclen, bdp->d_namlen)); - /* advance past this real entry */ inp += reclen; - /* advance output past SVR4-shaped entry */ - outp += svr4_reclen; - resid -= svr4_reclen; - offcnt += svr4_reclen; - } - DPRINTF(("block finished\n")); - /* if we squished out the whole block, try again */ - fp->f_offset = off; /* update the vnode offset */ - if (outp == (caddr_t)SCARG(uap, dp)) + if (cookiep) { + off = *cookiep++; + ncookies--; + } else + off += reclen; + outp += svr4reclen; + resid -= svr4reclen; + len -= reclen; + if (justone) + break; + } + + if (outp == (caddr_t) SCARG(uap, dp)) goto again; + fp->f_offset = off; + + if (justone) + nbytes = resid + svr4reclen; eof: - retval = offcnt; /* SCARG(uap, nbytes) - resid;*/ + p->p_retval[0] = nbytes - resid; out: + if (cookies) + free(cookies, M_TEMP); VOP_UNLOCK(vp, 0, p); - if (cookiebuf) - free(cookiebuf, M_TEMP); free(buf, M_TEMP); - DPRINTF(("\t\treturning %d\n", retval)); - p->p_retval[0] = retval; return error; } @@ -427,10 +466,10 @@ again: bdp = (struct dirent *)inp; reclen = bdp->d_reclen; if (reclen & 3) - panic("svr4_getdents: bad reclen"); + panic("svr4_sys_getdents64: bad reclen"); off = *cookie++; /* each entry points to the next */ if ((off >> 32) != 0) { - uprintf("svr4_getdents: dir offset too large for emulated program"); + uprintf("svr4_sys_getdents64: dir offset too large for emulated program"); error = EINVAL; goto out; } |