summaryrefslogtreecommitdiff
path: root/sys/compat/netbsd/netbsd_getdents.c
diff options
context:
space:
mode:
authorkstailey <kstailey@cvs.openbsd.org>1999-09-15 20:41:17 +0000
committerkstailey <kstailey@cvs.openbsd.org>1999-09-15 20:41:17 +0000
commitddb5ef5c9cb1d4a0946100674ff3f9ecc28bf445 (patch)
tree141386bb6b3e11f350bbcafd5062d6f7ac982857 /sys/compat/netbsd/netbsd_getdents.c
parent7e23bd46da3539da5029e7b8a8d827ba3cb55ab6 (diff)
getdents(2)
Diffstat (limited to 'sys/compat/netbsd/netbsd_getdents.c')
-rw-r--r--sys/compat/netbsd/netbsd_getdents.c130
1 files changed, 130 insertions, 0 deletions
diff --git a/sys/compat/netbsd/netbsd_getdents.c b/sys/compat/netbsd/netbsd_getdents.c
new file mode 100644
index 00000000000..2163c15442c
--- /dev/null
+++ b/sys/compat/netbsd/netbsd_getdents.c
@@ -0,0 +1,130 @@
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/mount.h>
+#include <sys/proc.h>
+#include <sys/uio.h>
+#include <sys/vnode.h>
+
+#include <compat/netbsd/netbsd_signal.h>
+#include <compat/netbsd/netbsd_syscallargs.h>
+
+static int netbsd_vn_readdir __P((struct file *, char *, int, u_int, int *,
+ struct proc *, off_t **, int *));
+
+static int
+netbsd_vn_readdir(fp, buf, segflg, count, done, p, cookies, ncookies)
+ struct file *fp;
+ char *buf;
+ int segflg, *done, *ncookies;
+ u_int count;
+ struct proc *p;
+ off_t **cookies;
+{
+ struct vnode *vp = (struct vnode *)fp->f_data;
+ struct iovec aiov;
+ struct uio auio;
+ int error, eofflag;
+
+unionread:
+ if (vp->v_type != VDIR)
+ return (EINVAL);
+ aiov.iov_base = buf;
+ aiov.iov_len = count;
+ auio.uio_iov = &aiov;
+ auio.uio_iovcnt = 1;
+ auio.uio_rw = UIO_READ;
+ auio.uio_segflg = segflg;
+ auio.uio_procp = p;
+ auio.uio_resid = count;
+ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
+ auio.uio_offset = fp->f_offset;
+ error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag, ncookies,
+ (u_long **)cookies); /* XXX 32-bit? */
+ fp->f_offset = auio.uio_offset;
+ VOP_UNLOCK(vp, 0, p);
+ if (error)
+ return (error);
+
+#ifdef UNION
+{
+ extern int (**union_vnodeop_p) __P((void *));
+ extern struct vnode *union_dircache __P((struct vnode *));
+
+ if (count == auio.uio_resid && (vp->v_op == union_vnodeop_p)) {
+ struct vnode *lvp;
+
+ lvp = union_dircache(vp);
+ if (lvp != NULLVP) {
+ struct vattr va;
+
+ /*
+ * If the directory is opaque,
+ * then don't show lower entries
+ */
+ error = VOP_GETATTR(vp, &va, fp->f_cred, p);
+ if (va.va_flags & OPAQUE) {
+ vput(lvp);
+ lvp = NULL;
+ }
+ }
+
+ if (lvp != NULLVP) {
+ error = VOP_OPEN(lvp, FREAD, fp->f_cred, p);
+ if (error) {
+ vput(lvp);
+ return (error);
+ }
+ VOP_UNLOCK(lvp, 0);
+ fp->f_data = (caddr_t) lvp;
+ fp->f_offset = 0;
+ error = vn_close(vp, FREAD, fp->f_cred, p);
+ if (error)
+ return (error);
+ vp = lvp;
+ goto unionread;
+ }
+ }
+}
+#endif /* UNION */
+
+ if (count == auio.uio_resid && (vp->v_flag & VROOT) &&
+ (vp->v_mount->mnt_flag & MNT_UNION)) {
+ struct vnode *tvp = vp;
+ vp = vp->v_mount->mnt_vnodecovered;
+ VREF(vp);
+ fp->f_data = (caddr_t) vp;
+ fp->f_offset = 0;
+ vrele(tvp);
+ goto unionread;
+ }
+ *done = count - auio.uio_resid;
+ return error;
+}
+
+/*
+ * Read a block of directory entries in a file system independent format.
+ */
+int
+netbsd_sys_getdents(p, v, retval)
+ struct proc *p;
+ void *v;
+ register_t *retval;
+{
+ register struct netbsd_sys_getdents_args /* {
+ syscallarg(int) fd;
+ syscallarg(char *) buf;
+ syscallarg(size_t) count;
+ } */ *uap = v;
+ struct file *fp;
+ int error, done;
+
+ if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0)
+ return (error);
+ if ((fp->f_flag & FREAD) == 0)
+ return (EBADF);
+ error = netbsd_vn_readdir(fp, SCARG(uap, buf), UIO_USERSPACE,
+ SCARG(uap, count), &done, p, 0, 0);
+ *retval = done;
+ return (error);
+}
+