/* ARGSUSED */
static int
zfsctl_shares_readdir(vnode_t *vp, uio_t *uiop, cred_t *cr, int *eofp,
    caller_context_t *ct, int flags)
{
	zfsvfs_t *zfsvfs = vp->v_vfsp->vfs_data;
	znode_t *dzp;
	int error;

	ZFS_ENTER(zfsvfs);

	if (zfsvfs->z_shares_dir == 0) {
		ZFS_EXIT(zfsvfs);
		return (ENOTSUP);
	}
	if ((error = zfs_zget(zfsvfs, zfsvfs->z_shares_dir, &dzp)) == 0) {
		error = VOP_READDIR(ZTOV(dzp), uiop, cr, eofp, ct, flags);
		VN_RELE(ZTOV(dzp));
	} else {
		*eofp = 1;
		error = ENOENT;
	}

	ZFS_EXIT(zfsvfs);
	return (error);
}
示例#2
0
static void
walk_dir(struct vnode *dvp, void *arg, int (*callback)(char *, void *))
{
	char    *nm;
	int eof, error;
	struct iovec iov;
	struct uio uio;
	struct dirent64 *dp;
	dirent64_t *dbuf;
	size_t dbuflen, dlen;

	ASSERT(dvp);

	dlen = 4096;
	dbuf = kmem_zalloc(dlen, KM_SLEEP);

	uio.uio_iov = &iov;
	uio.uio_iovcnt = 1;
	uio.uio_segflg = UIO_SYSSPACE;
	uio.uio_fmode = 0;
	uio.uio_extflg = UIO_COPY_CACHED;
	uio.uio_loffset = 0;
	uio.uio_llimit = MAXOFFSET_T;

	eof = 0;
	error = 0;
	while (!error && !eof) {
		uio.uio_resid = dlen;
		iov.iov_base = (char *)dbuf;
		iov.iov_len = dlen;
		(void) VOP_RWLOCK(dvp, V_WRITELOCK_FALSE, NULL);
		error = VOP_READDIR(dvp, &uio, kcred, &eof, NULL, 0);
		VOP_RWUNLOCK(dvp, V_WRITELOCK_FALSE, NULL);

		dbuflen = dlen - uio.uio_resid;
		if (error || dbuflen == 0)
			break;
		for (dp = dbuf; ((intptr_t)dp <
		    (intptr_t)dbuf + dbuflen);
		    dp = (dirent64_t *)((intptr_t)dp + dp->d_reclen)) {
			nm = dp->d_name;

			if (strcmp(nm, ".") == 0 ||
			    strcmp(nm, "..") == 0)
				continue;

			if (callback(nm, arg) == WALK_DIR_TERMINATE)
				goto end;
		}
	}

end:
	kmem_free(dbuf, dlen);
}
示例#3
0
int
RUMP_VOP_READDIR(struct vnode *vp,
    struct uio *uio,
    struct kauth_cred *cred,
    int *eofflag,
    off_t **cookies,
    int *ncookies)
{
	int error;

	rump_schedule();
	error = VOP_READDIR(vp, uio, cred, eofflag, cookies, ncookies);
	rump_unschedule();

	return error;
}
示例#4
0
int
getdents64(int fd, void *buf, size_t count)
{
	vnode_t *vp;
	file_t *fp;
	struct uio auio;
	struct iovec aiov;
	register int error;
	int sink;

	if (count < sizeof (struct dirent64))
		return (set_errno(EINVAL));

	/*
	 * Don't let the user overcommit kernel resources.
	 */
	if (count > MAXGETDENTS_SIZE)
		count = MAXGETDENTS_SIZE;

	if ((fp = getf(fd)) == NULL)
		return (set_errno(EBADF));
	vp = fp->f_vnode;
	if (vp->v_type != VDIR) {
		releasef(fd);
		return (set_errno(ENOTDIR));
	}
	aiov.iov_base = buf;
	aiov.iov_len = count;
	auio.uio_iov = &aiov;
	auio.uio_iovcnt = 1;
	auio.uio_loffset = fp->f_offset;
	auio.uio_segflg = UIO_USERSPACE;
	auio.uio_resid = count;
	auio.uio_fmode = 0;
	auio.uio_extflg = UIO_COPY_CACHED;
	(void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, NULL);
	error = VOP_READDIR(vp, &auio, fp->f_cred, &sink, NULL, 0);
	VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL);
	if (error) {
		releasef(fd);
		return (set_errno(error));
	}
	count = count - auio.uio_resid;
	fp->f_offset = auio.uio_loffset;
	releasef(fd);
	return (count);
}
示例#5
0
/*
 * smb_vop_readdir()
 *
 * Collects an SMB_MINLEN_RDDIR_BUF "page" of directory entries.
 * The directory entries are returned in an fs-independent format by the
 * underlying file system.  That is, the "page" of information returned is
 * not literally stored on-disk in the format returned.
 * If the file system supports extended directory entries (has features
 * VFSFT_DIRENTFLAGS), set V_RDDIR_ENTFLAGS to cause the buffer to be
 * filled with edirent_t structures, instead of dirent64_t structures.
 * If the file system supports access based enumeration (abe), set
 * V_RDDIR_ACCFILTER to filter directory entries based on user cred.
 */
int
smb_vop_readdir(vnode_t *vp, uint32_t offset,
    void *buf, int *count, int *eof, uint32_t rddir_flag, cred_t *cr)
{
	int error = 0;
	int flags = 0;
	int rdirent_size;
	struct uio auio;
	struct iovec aiov;

	if (vp->v_type != VDIR)
		return (ENOTDIR);

	if (vfs_has_feature(vp->v_vfsp, VFSFT_DIRENTFLAGS)) {
		flags |= V_RDDIR_ENTFLAGS;
		rdirent_size = sizeof (edirent_t);
	} else {
		rdirent_size = sizeof (dirent64_t);
	}

	if (*count < rdirent_size)
		return (EINVAL);

	if (rddir_flag & SMB_ABE)
		flags |= V_RDDIR_ACCFILTER;

	aiov.iov_base = buf;
	aiov.iov_len = *count;
	auio.uio_iov = &aiov;
	auio.uio_iovcnt = 1;
	auio.uio_loffset = (uint64_t)offset;
	auio.uio_segflg = UIO_SYSSPACE;
	auio.uio_resid = *count;
	auio.uio_fmode = 0;

	(void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, &smb_ct);
	error = VOP_READDIR(vp, &auio, cr, eof, &smb_ct, flags);
	VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, &smb_ct);

	if (error == 0)
		*count = *count - auio.uio_resid;

	return (error);
}
示例#6
0
        /* reset the file position */
        file_p->f_pos = (loff_t)uios.uio_offset;
        dir_ctx->pos = (loff_t) uios.uio_offset;
    }
    return err;
}
#else /* LINUX_VERSION_CODE > KERNEL_VERSION(3,10,0) */
int
vnode_fop_readdir(
    FILE_T *file_p,
    void *dirent_p,
    filldir_t filldir_func
)
{
    uio_t uios;
    INODE_T *inode;
    DENT_T *dentry;
    int err;
    CALL_DATA_T cd;
    struct readdir_ctx ctx;

    dentry = file_p->f_dentry;
    inode = dentry->d_inode;

    ASSERT(MDKI_INOISMVFS(inode));

    ctx.file = file_p;
    ctx.done = FALSE;

    BZERO(&uios, sizeof(uios));
    uios.uio_offset = (loff_t)file_p->f_pos;
    /* This value cannot be larger than value in the view_v4_procinfo table 
     * It is used for maximum size of the return data.
     */
    uios.uio_resid = MVFS_LINUX_MAXRPCDATA;
    uios.uio_buff = dirent_p;
    uios.uio_func = filldir_func;

    mdki_linux_init_call_data(&cd);
    err = VOP_READDIR(ITOV(inode), &uios, &cd, NULL, &ctx);
    err = mdki_errno_unix_to_linux(err);
    mdki_linux_destroy_call_data(&cd);

    if (!ctx.done)
        /* reset the file position */
        file_p->f_pos = (loff_t)uios.uio_offset;

    return err;
}
示例#7
0
static int
VMBlockReaddir(struct vnode *vp,       // IN: Vnode of directory to read
               struct uio *uiop,       // IN: User's read request
               struct cred *cr,        // IN: Credentials of caller
               int *eofp               // OUT: Indicates we are done
#if OS_VFS_VERSION >= 5
             , caller_context_t *ctx   // IN: Caller's context
             , int flags               // IN: flags
#endif
               )
{
   VMBlockMountInfo *mip;

   Debug(VMBLOCK_ENTRY_LOGLEVEL, "VMBlockReaddir: entry\n");

   mip = (VMBlockMountInfo *)vp->v_vfsp->vfs_data;
   return VOP_READDIR(mip->redirectVnode, uiop, cr, eofp
#if OS_VFS_VERSION >= 5
                     , ctx, flags
#endif
                     );
}
示例#8
0
STATIC int
xfs_file_readdir(
	struct file	*filp,
	void		*dirent,
	filldir_t	filldir)
{
	int		error = 0;
	vnode_t		*vp = vn_from_inode(filp->f_dentry->d_inode);
	uio_t		uio;
	iovec_t		iov;
	int		eof = 0;
	caddr_t		read_buf;
	int		namelen, size = 0;
	size_t		rlen = PAGE_CACHE_SIZE;
	xfs_off_t	start_offset, curr_offset;
	xfs_dirent_t	*dbp = NULL;

	/* Try fairly hard to get memory */
	do {
		if ((read_buf = (caddr_t)kmalloc(rlen, GFP_KERNEL)))
			break;
		rlen >>= 1;
	} while (rlen >= 1024);

	if (read_buf == NULL)
		return -ENOMEM;

	uio.uio_iov = &iov;
	uio.uio_segflg = UIO_SYSSPACE;
	curr_offset = filp->f_pos;
	if (filp->f_pos != 0x7fffffff)
		uio.uio_offset = filp->f_pos;
	else
		uio.uio_offset = 0xffffffff;

	while (!eof) {
		uio.uio_resid = iov.iov_len = rlen;
		iov.iov_base = read_buf;
		uio.uio_iovcnt = 1;

		start_offset = uio.uio_offset;

		VOP_READDIR(vp, &uio, NULL, &eof, error);
		if ((uio.uio_offset == start_offset) || error) {
			size = 0;
			break;
		}

		size = rlen - uio.uio_resid;
		dbp = (xfs_dirent_t *)read_buf;
		while (size > 0) {
			namelen = strlen(dbp->d_name);

			if (filldir(dirent, dbp->d_name, namelen,
					(loff_t) curr_offset & 0x7fffffff,
					(ino_t) dbp->d_ino,
					DT_UNKNOWN)) {
				goto done;
			}
			size -= dbp->d_reclen;
			curr_offset = (loff_t)dbp->d_off /* & 0x7fffffff */;
			dbp = (xfs_dirent_t *)((char *)dbp + dbp->d_reclen);
		}
	}
done:
	if (!error) {
		if (size == 0)
			filp->f_pos = uio.uio_offset & 0x7fffffff;
		else if (dbp)
			filp->f_pos = curr_offset;
	}

	kfree(read_buf);
	return -error;
}
示例#9
0
/*
 * Find the entry in the directory corresponding to the target vnode.
 */
int
dirfindvp(vnode_t *vrootp, vnode_t *dvp, vnode_t *tvp, cred_t *cr, char *dbuf,
    size_t dlen, dirent64_t **rdp)
{
	size_t dbuflen;
	struct iovec iov;
	struct uio uio;
	int error;
	int eof;
	vnode_t *cmpvp;
	struct dirent64 *dp;
	pathname_t pnp;

	ASSERT(dvp->v_type == VDIR);

	/*
	 * This is necessary because of the strange semantics of VOP_LOOKUP().
	 */
	bzero(&pnp, sizeof (pnp));

	eof = 0;

	uio.uio_iov = &iov;
	uio.uio_iovcnt = 1;
	uio.uio_segflg = UIO_SYSSPACE;
	uio.uio_fmode = 0;
	uio.uio_extflg = UIO_COPY_CACHED;
	uio.uio_loffset = 0;

	if ((error = VOP_ACCESS(dvp, VREAD, 0, cr, NULL)) != 0)
		return (error);

	while (!eof) {
		uio.uio_resid = dlen;
		iov.iov_base = dbuf;
		iov.iov_len = dlen;

		(void) VOP_RWLOCK(dvp, V_WRITELOCK_FALSE, NULL);
		error = VOP_READDIR(dvp, &uio, cr, &eof, NULL, 0);
		VOP_RWUNLOCK(dvp, V_WRITELOCK_FALSE, NULL);

		dbuflen = dlen - uio.uio_resid;

		if (error || dbuflen == 0)
			break;

		dp = (dirent64_t *)dbuf;
		while ((intptr_t)dp < (intptr_t)dbuf + dbuflen) {
			/*
			 * Ignore '.' and '..' entries
			 */
			if (strcmp(dp->d_name, ".") == 0 ||
			    strcmp(dp->d_name, "..") == 0) {
				dp = (dirent64_t *)((intptr_t)dp +
				    dp->d_reclen);
				continue;
			}

			error = VOP_LOOKUP(dvp, dp->d_name, &cmpvp, &pnp, 0,
			    vrootp, cr, NULL, NULL, NULL);

			/*
			 * We only want to bail out if there was an error other
			 * than ENOENT.  Otherwise, it could be that someone
			 * just removed an entry since the readdir() call, and
			 * the entry we want is further on in the directory.
			 */
			if (error == 0) {
				if (vnode_match(tvp, cmpvp, cr)) {
					VN_RELE(cmpvp);
					*rdp = dp;
					return (0);
				}

				VN_RELE(cmpvp);
			} else if (error != ENOENT) {
				return (error);
			}

			dp = (dirent64_t *)((intptr_t)dp + dp->d_reclen);
		}
	}

	/*
	 * Something strange has happened, this directory does not contain the
	 * specified vnode.  This should never happen in the normal case, since
	 * we ensured that dvp is the parent of vp.  This is possible in some
	 * rare conditions (races and the special .zfs directory).
	 */
	if (error == 0) {
		error = VOP_LOOKUP(dvp, ".zfs", &cmpvp, &pnp, 0, vrootp, cr,
		    NULL, NULL, NULL);
		if (error == 0) {
			if (vnode_match(tvp, cmpvp, cr)) {
				(void) strcpy(dp->d_name, ".zfs");
				dp->d_reclen = strlen(".zfs");
				dp->d_off = 2;
				dp->d_ino = 1;
				*rdp = dp;
			} else {
				error = ENOENT;
			}
			VN_RELE(cmpvp);
		}
	}

	return (error);
}
示例#10
0
static int
xattr_dir_readdir(vnode_t *dvp, uio_t *uiop, cred_t *cr, int *eofp,
    caller_context_t *ct, int flags)
{
	vnode_t *pvp;
	int error;
	int local_eof;
	int reset_off = 0;
	int has_xattrs = 0;

	if (eofp == NULL) {
		eofp = &local_eof;
	}
	*eofp = 0;

	/*
	 * See if there is a real extended attribute directory.
	 */
	error = xattr_dir_realdir(dvp, &pvp, LOOKUP_XATTR, cr, ct);
	if (error == 0) {
		has_xattrs = 1;
	}

	/*
	 * Start by reading up the static entries.
	 */
	if (uiop->uio_loffset == 0) {
		ino64_t pino, ino;
		offset_t off;
		gfs_dir_t *dp = dvp->v_data;
		gfs_readdir_state_t gstate;

		if (has_xattrs) {
			/*
			 * If there is a real xattr dir, skip . and ..
			 * in the GFS dir.  We'll pick them up below
			 * when we call into the underlying fs.
			 */
			uiop->uio_loffset = GFS_STATIC_ENTRY_OFFSET;
		}
		error = gfs_get_parent_ino(dvp, cr, ct, &pino, &ino);
		if (error == 0) {
			error = gfs_readdir_init(&gstate, dp->gfsd_maxlen, 1,
			    uiop, pino, ino, flags);
		}
		if (error) {
			return (error);
		}

		while ((error = gfs_readdir_pred(&gstate, uiop, &off)) == 0 &&
		    !*eofp) {
			if (off >= 0 && off < dp->gfsd_nstatic) {
				int eflags;

				/*
				 * Check to see if this sysattr set name has a
				 * case-insensitive conflict with a real xattr
				 * name.
				 */
				eflags = 0;
				if ((flags & V_RDDIR_ENTFLAGS) && has_xattrs) {
					error = readdir_xattr_casecmp(pvp,
					    dp->gfsd_static[off].gfse_name,
					    cr, ct, &eflags);
					if (error)
						break;
				}
				ino = dp->gfsd_inode(dvp, off);

				error = gfs_readdir_emit(&gstate, uiop, off,
				    ino, dp->gfsd_static[off].gfse_name,
				    eflags);
				if (error)
					break;
			} else {
				*eofp = 1;
			}
		}

		error = gfs_readdir_fini(&gstate, error, eofp, *eofp);
		if (error) {
			return (error);
		}

		/*
		 * We must read all of the static entries in the first
		 * call.  Otherwise we won't know if uio_loffset in a
		 * subsequent call refers to the static entries or to those
		 * in an underlying fs.
		 */
		if (*eofp == 0)
			return (EINVAL);
		reset_off = 1;
	}

	if (!has_xattrs) {
		*eofp = 1;
		return (0);
	}

	*eofp = 0;
	if (reset_off) {
		uiop->uio_loffset = 0;
	}
	(void) VOP_RWLOCK(pvp, V_WRITELOCK_FALSE, NULL);
	error = VOP_READDIR(pvp, uiop, cr, eofp, ct, flags);
	VOP_RWUNLOCK(pvp, V_WRITELOCK_FALSE, NULL);

	return (error);
}
示例#11
0
int
vn_readdir(file_t *fp, char *bf, int segflg, u_int count, int *done,
    struct lwp *l, off_t **cookies, int *ncookies)
{
	struct vnode *vp = (struct vnode *)fp->f_data;
	struct iovec aiov;
	struct uio auio;
	int error, eofflag;

	/* Limit the size on any kernel buffers used by VOP_READDIR */
	count = min(MAXBSIZE, count);

unionread:
	if (vp->v_type != VDIR)
		return (EINVAL);
	aiov.iov_base = bf;
	aiov.iov_len = count;
	auio.uio_iov = &aiov;
	auio.uio_iovcnt = 1;
	auio.uio_rw = UIO_READ;
	if (segflg == UIO_SYSSPACE) {
		UIO_SETUP_SYSSPACE(&auio);
	} else {
		KASSERT(l == curlwp);
		auio.uio_vmspace = l->l_proc->p_vmspace;
	}
	auio.uio_resid = count;
	vn_lock(vp, LK_SHARED | LK_RETRY);
	auio.uio_offset = fp->f_offset;
	error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag, cookies,
		    ncookies);
	mutex_enter(&fp->f_lock);
	fp->f_offset = auio.uio_offset;
	mutex_exit(&fp->f_lock);
	VOP_UNLOCK(vp);
	if (error)
		return (error);

	if (count == auio.uio_resid && vn_union_readdir_hook) {
		struct vnode *ovp = vp;

		error = (*vn_union_readdir_hook)(&vp, fp, l);
		if (error)
			return (error);
		if (vp != ovp)
			goto unionread;
	}

	if (count == auio.uio_resid && (vp->v_vflag & VV_ROOT) &&
	    (vp->v_mount->mnt_flag & MNT_UNION)) {
		struct vnode *tvp = vp;
		vp = vp->v_mount->mnt_vnodecovered;
		vref(vp);
		mutex_enter(&fp->f_lock);
		fp->f_data = vp;
		fp->f_offset = 0;
		mutex_exit(&fp->f_lock);
		vrele(tvp);
		goto unionread;
	}
	*done = count - auio.uio_resid;
	return error;
}
示例#12
0
/* Find parent vnode of *lvpp, return in *uvpp */
int
vfs_getcwd_scandir(struct vnode **lvpp, struct vnode **uvpp, char **bpp,
                   char *bufp, struct proc *p)
{
    int eofflag, tries, dirbuflen, len, reclen, error = 0;
    off_t off;
    struct uio uio;
    struct iovec iov;
    char *dirbuf = NULL;
    ino_t fileno;
    struct vattr va;
    struct vnode *uvp = NULL;
    struct vnode *lvp = *lvpp;
    struct componentname cn;

    tries = 0;

    /*
     * If we want the filename, get some info we need while the
     * current directory is still locked.
     */
    if (bufp != NULL) {
        error = VOP_GETATTR(lvp, &va, p->p_ucred, p);
        if (error) {
            vput(lvp);
            *lvpp = NULL;
            *uvpp = NULL;
            return (error);
        }
    }

    cn.cn_nameiop = LOOKUP;
    cn.cn_flags = ISLASTCN | ISDOTDOT | RDONLY;
    cn.cn_proc = p;
    cn.cn_cred = p->p_ucred;
    cn.cn_pnbuf = NULL;
    cn.cn_nameptr = "..";
    cn.cn_namelen = 2;
    cn.cn_consume = 0;

    /* Get parent vnode using lookup of '..' */
    error = VOP_LOOKUP(lvp, uvpp, &cn);
    if (error) {
        vput(lvp);
        *lvpp = NULL;
        *uvpp = NULL;
        return (error);
    }

    uvp = *uvpp;

    /* If we don't care about the pathname, we're done */
    if (bufp == NULL) {
        vrele(lvp);
        *lvpp = NULL;
        return (0);
    }

    fileno = va.va_fileid;

    dirbuflen = DIRBLKSIZ;

    if (dirbuflen < va.va_blocksize)
        dirbuflen = va.va_blocksize;

    dirbuf = malloc(dirbuflen, M_TEMP, M_WAITOK);

    off = 0;

    do {
        char   *cpos;
        struct dirent *dp;

        iov.iov_base = dirbuf;
        iov.iov_len = dirbuflen;

        uio.uio_iov = &iov;
        uio.uio_iovcnt = 1;
        uio.uio_offset = off;
        uio.uio_resid = dirbuflen;
        uio.uio_segflg = UIO_SYSSPACE;
        uio.uio_rw = UIO_READ;
        uio.uio_procp = p;

        eofflag = 0;

        /* Call VOP_READDIR of parent */
        error = VOP_READDIR(uvp, &uio, p->p_ucred, &eofflag);

        off = uio.uio_offset;

        /* Try again if NFS tosses its cookies */
        if (error == EINVAL && tries < 3) {
            tries++;
            off = 0;
            continue;
        } else if (error) {
            goto out; /* Old userland getcwd() behaviour */
        }

        cpos = dirbuf;
        tries = 0;

        /* Scan directory page looking for matching vnode */
        for (len = (dirbuflen - uio.uio_resid); len > 0;
                len -= reclen) {
            dp = (struct dirent *)cpos;
            reclen = dp->d_reclen;

            /* Check for malformed directory */
            if (reclen < DIRENT_RECSIZE(1)) {
                error = EINVAL;
                goto out;
            }

            if (dp->d_fileno == fileno) {
                char *bp = *bpp;
                bp -= dp->d_namlen;

                if (bp <= bufp) {
                    error = ERANGE;
                    goto out;
                }

                memmove(bp, dp->d_name, dp->d_namlen);
                error = 0;
                *bpp = bp;

                goto out;
            }

            cpos += reclen;
        }

    } while (!eofflag);

    error = ENOENT;

out:

    vrele(lvp);
    *lvpp = NULL;

    free(dirbuf, M_TEMP);

    return (error);
}
示例#13
0
static int
unionfs_readdir(void *v)
{
	struct vop_readdir_args *ap = v;
	int		error;
	int		eofflag;
	int		locked;
	struct unionfs_node *unp;
	struct unionfs_node_status *unsp;
	struct uio     *uio;
	struct vnode   *uvp;
	struct vnode   *lvp;
	struct vattr    va;

	int		ncookies_bk;
	off_t          *cookies_bk;

	UNIONFS_INTERNAL_DEBUG("unionfs_readdir: enter\n");

	error = 0;
	eofflag = 0;
	locked = 0;
	unp = VTOUNIONFS(ap->a_vp);
	uio = ap->a_uio;
	uvp = unp->un_uppervp;
	lvp = unp->un_lowervp;
	ncookies_bk = 0;
	cookies_bk = NULL;

	if (ap->a_vp->v_type != VDIR)
		return (ENOTDIR);

	/* check opaque */
	if (uvp != NULLVP && lvp != NULLVP) {
		if ((error = VOP_GETATTR(uvp, &va, ap->a_cred)) != 0)
			goto unionfs_readdir_exit;
		if (va.va_flags & OPAQUE)
			lvp = NULLVP;
	}

	/* check the open count. unionfs needs to open before readdir. */
	VOP_UNLOCK(ap->a_vp);
	vn_lock(ap->a_vp, LK_EXCLUSIVE | LK_RETRY);
	unionfs_get_node_status(unp, &unsp);
	if ((uvp != NULLVP && unsp->uns_upper_opencnt <= 0) ||
	    (lvp != NULLVP && unsp->uns_lower_opencnt <= 0)) {
		unionfs_tryrem_node_status(unp, unsp);
		error = EBADF;
	}
	if (error != 0)
		goto unionfs_readdir_exit;

	/* upper only */
	if (uvp != NULLVP && lvp == NULLVP) {
		error = VOP_READDIR(uvp, uio, ap->a_cred, ap->a_eofflag,
		    ap->a_cookies, ap->a_ncookies);
		unsp->uns_readdir_status = 0;

		goto unionfs_readdir_exit;
	}

	/* lower only */
	if (uvp == NULLVP && lvp != NULLVP) {
		error = VOP_READDIR(lvp, uio, ap->a_cred, ap->a_eofflag,
		    ap->a_cookies, ap->a_ncookies);
		unsp->uns_readdir_status = 2;

		goto unionfs_readdir_exit;
	}

	/*
	 * readdir upper and lower
	 */
	KASSERT(uvp != NULLVP);
	KASSERT(lvp != NULLVP);
	if (uio->uio_offset == 0)
		unsp->uns_readdir_status = 0;

	if (unsp->uns_readdir_status == 0) {
		/* read upper */
		error = VOP_READDIR(uvp, uio, ap->a_cred, &eofflag,
				    ap->a_cookies, ap->a_ncookies);

		if (error != 0 || eofflag == 0)
			goto unionfs_readdir_exit;
		unsp->uns_readdir_status = 1;

		/*
		 * ufs(and other fs) needs size of uio_resid larger than
		 * DIRBLKSIZ.
		 * size of DIRBLKSIZ equals DEV_BSIZE.
		 * (see: ufs/ufs/ufs_vnops.c ufs_readdir func , ufs/ufs/dir.h)
		 */
		if (uio->uio_resid <= (uio->uio_resid & (DEV_BSIZE -1)))
			goto unionfs_readdir_exit;

		/*
		 * backup cookies
		 * It prepares to readdir in lower.
		 */
		if (ap->a_ncookies != NULL) {
			ncookies_bk = *(ap->a_ncookies);
			*(ap->a_ncookies) = 0;
		}
		if (ap->a_cookies != NULL) {
			cookies_bk = *(ap->a_cookies);
			*(ap->a_cookies) = NULL;
		}
	}

	/* initialize for readdir in lower */
	if (unsp->uns_readdir_status == 1) {
		unsp->uns_readdir_status = 2;
		uio->uio_offset = 0;
	}

	if (lvp == NULLVP) {
		error = EBADF;
		goto unionfs_readdir_exit;
	}
	/* read lower */
	error = VOP_READDIR(lvp, uio, ap->a_cred, ap->a_eofflag,
			    ap->a_cookies, ap->a_ncookies);

	if (cookies_bk != NULL) {
		/* merge cookies */
		int		size;
		off_t         *newcookies, *pos;

		size = *(ap->a_ncookies) + ncookies_bk;
		newcookies = (off_t *) malloc(size * sizeof(off_t),
		    M_TEMP, M_WAITOK);
		pos = newcookies;

		memcpy(pos, cookies_bk, ncookies_bk * sizeof(off_t));
		pos += ncookies_bk * sizeof(off_t);
		memcpy(pos, *(ap->a_cookies), *(ap->a_ncookies) * sizeof(off_t));
		free(cookies_bk, M_TEMP);
		free(*(ap->a_cookies), M_TEMP);
		*(ap->a_ncookies) = size;
		*(ap->a_cookies) = newcookies;
	}

unionfs_readdir_exit:
	if (error != 0 && ap->a_eofflag != NULL)
		*(ap->a_eofflag) = 1;

	UNIONFS_INTERNAL_DEBUG("unionfs_readdir: leave (%d)\n", error);

	return (error);
}
示例#14
0
int
readdir_with_callback(struct file *fp, off_t *off, u_long nbytes,
    int (*appendfunc)(void *, struct dirent *), void *arg)
{
	struct dirent *bdp;
	caddr_t inp, buf;
	int buflen;
	struct uio auio;
	struct iovec aiov;
	int eofflag = 0;
	int error, len, reclen;
	off_t newoff = *off;
	struct vnode *vp;
	struct vattr va;
		
	if ((fp->f_flag & FREAD) == 0)
		return (EBADF);

	vp = (struct vnode *)fp->f_data;

	if (vp->v_type != VDIR)
		return (EINVAL);

	if ((error = VOP_GETATTR(vp, &va, fp->f_cred, curproc)) != 0)
		return (error);

	buflen = min(MAXBSIZE, nbytes);
	buflen = max(buflen, va.va_blocksize);
	buf = malloc(buflen, M_TEMP, M_WAITOK);
	error = vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, curproc);
	if (error) {
		free(buf, M_TEMP, 0);
		return (error);
	}

again:
	aiov.iov_base = buf;
	aiov.iov_len = buflen;
	auio.uio_iov = &aiov;
	auio.uio_iovcnt = 1;
	auio.uio_rw = UIO_READ;
	auio.uio_segflg = UIO_SYSSPACE;
	auio.uio_procp = curproc;
	auio.uio_resid = buflen;
	auio.uio_offset = newoff;

	error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag);
	*off = auio.uio_offset;
	if (error)
		goto out;

	if ((len = buflen - auio.uio_resid) <= 0)
		goto eof;	

	inp = buf;

	for (; len > 0; len -= reclen, inp += reclen) {
		bdp = (struct dirent *)inp;
		reclen = bdp->d_reclen;

		if (len < reclen)
			break;

		if (reclen & 3) {
			error = EFAULT;
			goto out;
		}

		/* Skip holes */
		if (bdp->d_fileno != 0) {
			if ((error = (*appendfunc) (arg, bdp)) != 0) {
				if (error == ENOMEM)
					error = 0;
				break;
			}
		}
	}

	if (len <= 0 && !eofflag)
		goto again;

eof:
out:
	VOP_UNLOCK(vp, 0, curproc);
	free(buf, M_TEMP, 0);
	return (error);
}
示例#15
0
/*
 * Native 32-bit system call for non-large-file applications.
 */
int
getdents32(int fd, void *buf, size_t count)
{
	vnode_t *vp;
	file_t *fp;
	struct uio auio;
	struct iovec aiov;
	register int error;
	int sink;
	char *newbuf;
	char *obuf;
	int bufsize;
	int osize, nsize;
	struct dirent64 *dp;
	struct dirent32 *op;

	if (count < sizeof (struct dirent32))
		return (set_errno(EINVAL));

	if ((fp = getf(fd)) == NULL)
		return (set_errno(EBADF));
	vp = fp->f_vnode;
	if (vp->v_type != VDIR) {
		releasef(fd);
		return (set_errno(ENOTDIR));
	}

	/*
	 * Don't let the user overcommit kernel resources.
	 */
	if (count > MAXGETDENTS_SIZE)
		count = MAXGETDENTS_SIZE;

	bufsize = count;
	newbuf = kmem_alloc(bufsize, KM_SLEEP);
	obuf = kmem_alloc(bufsize, KM_SLEEP);

	aiov.iov_base = newbuf;
	aiov.iov_len = count;
	auio.uio_iov = &aiov;
	auio.uio_iovcnt = 1;
	auio.uio_loffset = fp->f_offset;
	auio.uio_segflg = UIO_SYSSPACE;
	auio.uio_resid = count;
	auio.uio_fmode = 0;
	auio.uio_extflg = UIO_COPY_CACHED;
	(void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, NULL);
	error = VOP_READDIR(vp, &auio, fp->f_cred, &sink, NULL, 0);
	VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL);
	if (error)
		goto out;
	count = count - auio.uio_resid;
	fp->f_offset = auio.uio_loffset;

	dp = (struct dirent64 *)newbuf;
	op = (struct dirent32 *)obuf;
	osize = 0;
	nsize = 0;

	while (nsize < count) {
		uint32_t reclen, namlen;

		/*
		 * This check ensures that the 64 bit d_ino and d_off
		 * fields will fit into their 32 bit equivalents.
		 *
		 * Although d_off is a signed value, the check is done
		 * against the full 32 bits because certain file systems,
		 * NFS for one, allow directory cookies to use the full
		 * 32 bits.  We use uint64_t because there is no exact
		 * unsigned analog to the off64_t type of dp->d_off.
		 */
		if (dp->d_ino > (ino64_t)UINT32_MAX ||
		    dp->d_off > (uint64_t)UINT32_MAX) {
			error = EOVERFLOW;
			goto out;
		}
		op->d_ino = (ino32_t)dp->d_ino;
		op->d_off = (off32_t)dp->d_off;
		namlen = strlen(dp->d_name);
		reclen = DIRENT32_RECLEN(namlen);
		op->d_reclen = (uint16_t)reclen;

		/* use strncpy(9f) to zero out uninitialized bytes */

		(void) strncpy(op->d_name, dp->d_name,
		    DIRENT32_NAMELEN(reclen));
		nsize += (uint_t)dp->d_reclen;
		osize += (uint_t)op->d_reclen;
		dp = (struct dirent64 *)((char *)dp + (uint_t)dp->d_reclen);
		op = (struct dirent32 *)((char *)op + (uint_t)op->d_reclen);
	}

	ASSERT(osize <= count);
	ASSERT((char *)op <= (char *)obuf + bufsize);
	ASSERT((char *)dp <= (char *)newbuf + bufsize);

	if ((error = copyout(obuf, buf, osize)) < 0)
		error = EFAULT;
out:
	kmem_free(newbuf, bufsize);
	kmem_free(obuf, bufsize);

	if (error) {
		releasef(fd);
		return (set_errno(error));
	}

	releasef(fd);
	return (osize);
}
/*
 * Read a block of directory entries in a file system independent format.
 */
int
compat_43_sys_getdirentries(struct lwp *l, const struct compat_43_sys_getdirentries_args *uap, register_t *retval)
{
	/* {
		syscallarg(int) fd;
		syscallarg(char *) buf;
		syscallarg(u_int) count;
		syscallarg(long *) basep;
	} */
	struct vnode *vp;
	struct file *fp;
	struct uio auio, kuio;
	struct iovec aiov, kiov;
	struct dirent *dp, *edp;
	char *dirbuf;
	size_t count = min(MAXBSIZE, (size_t)SCARG(uap, count));

	int error, eofflag, readcnt;
	long loff;

	/* fd_getvnode() will use the descriptor for us */
	if ((error = fd_getvnode(SCARG(uap, fd), &fp)) != 0)
		return (error);
	if ((fp->f_flag & FREAD) == 0) {
		error = EBADF;
		goto out;
	}
	vp = (struct vnode *)fp->f_data;
unionread:
	if (vp->v_type != VDIR) {
		error = EINVAL;
		goto out;
	}
	aiov.iov_base = SCARG(uap, buf);
	aiov.iov_len = count;
	auio.uio_iov = &aiov;
	auio.uio_iovcnt = 1;
	auio.uio_rw = UIO_READ;
	auio.uio_resid = count;
	KASSERT(l == curlwp);
	auio.uio_vmspace = curproc->p_vmspace;
	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
	loff = auio.uio_offset = fp->f_offset;
#	if (BYTE_ORDER != LITTLE_ENDIAN)
		if ((vp->v_mount->mnt_iflag & IMNT_DTYPE) == 0) {
			error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag,
			    (off_t **)0, (int *)0);
			fp->f_offset = auio.uio_offset;
		} else
#	endif
	{
		kuio = auio;
		kuio.uio_iov = &kiov;
		kiov.iov_len = count;
		dirbuf = malloc(count, M_TEMP, M_WAITOK);
		kiov.iov_base = dirbuf;
		UIO_SETUP_SYSSPACE(&kuio);
		error = VOP_READDIR(vp, &kuio, fp->f_cred, &eofflag,
			    (off_t **)0, (int *)0);
		fp->f_offset = kuio.uio_offset;
		if (error == 0) {
			readcnt = count - kuio.uio_resid;
			edp = (struct dirent *)&dirbuf[readcnt];
			for (dp = (struct dirent *)dirbuf; dp < edp; ) {
#				if (BYTE_ORDER == LITTLE_ENDIAN)
					/*
					 * The expected low byte of
					 * dp->d_namlen is our dp->d_type.
					 * The high MBZ byte of dp->d_namlen
					 * is our dp->d_namlen.
					 */
					dp->d_type = dp->d_namlen;
					dp->d_namlen = 0;
#				else
					/*
					 * The dp->d_type is the high byte
					 * of the expected dp->d_namlen,
					 * so must be zero'ed.
					 */
					dp->d_type = 0;
#				endif
				if (dp->d_reclen > 0) {
					dp = (struct dirent *)
					    ((char *)dp + dp->d_reclen);
				} else {
					error = EIO;
					break;
				}
			}
			if (dp >= edp)
				error = uiomove(dirbuf, readcnt, &auio);
		}
		free(dirbuf, M_TEMP);
	}
	VOP_UNLOCK(vp);
	if (error)
		goto out;

	if ((count == auio.uio_resid) &&
	    (vp->v_vflag & VV_ROOT) &&
	    (vp->v_mount->mnt_flag & MNT_UNION)) {
		struct vnode *tvp = vp;
		vp = vp->v_mount->mnt_vnodecovered;
		vref(vp);
		fp->f_data = (void *) vp;
		fp->f_offset = 0;
		vrele(tvp);
		goto unionread;
	}
	error = copyout((void *)&loff, (void *)SCARG(uap, basep),
	    sizeof(long));
	*retval = count - auio.uio_resid;
 out:
	fd_putfile(SCARG(uap, fd));
	return (error);
}
/*
 * Read a block of directory entries in a file system independent format.
 */
int
compat_43_sys_getdirentries(struct lwp *l, const struct compat_43_sys_getdirentries_args *uap, register_t *retval)
{
	/* {
		syscallarg(int) fd;
		syscallarg(char *) buf;
		syscallarg(u_int) count;
		syscallarg(long *) basep;
	} */
	struct dirent *bdp;
	struct vnode *vp;
	char *inp, *tbuf;		/* Current-format */
	int len, reclen;		/* Current-format */
	char *outp;			/* Dirent12-format */
	int resid, old_reclen = 0;	/* Dirent12-format */
	struct file *fp;
	struct uio auio;
	struct iovec aiov;
	struct dirent43 idb;
	off_t off;		/* true file offset */
	int buflen, error, eofflag, nbytes;
	struct vattr va;
	off_t *cookiebuf = NULL, *cookie;
	int ncookies;
	long loff;
		 
	/* fd_getvnode() will use the descriptor for us */
	if ((error = fd_getvnode(SCARG(uap, fd), &fp)) != 0)
		return (error);

	if ((fp->f_flag & FREAD) == 0) {
		error = EBADF;
		goto out1;
	}

	vp = (struct vnode *)fp->f_data;
	if (vp->v_type != VDIR) {
		error = ENOTDIR;
		goto out1;
	}

	vn_lock(vp, LK_SHARED | LK_RETRY);
	error = VOP_GETATTR(vp, &va, l->l_cred);
	VOP_UNLOCK(vp);
	if (error)
		goto out1;

	loff = fp->f_offset;
	nbytes = SCARG(uap, count);
	buflen = min(MAXBSIZE, nbytes);
	if (buflen < va.va_blocksize)
		buflen = va.va_blocksize;
	tbuf = malloc(buflen, M_TEMP, M_WAITOK);

	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
	off = fp->f_offset;
again:
	aiov.iov_base = tbuf;
	aiov.iov_len = buflen;
	auio.uio_iov = &aiov;
	auio.uio_iovcnt = 1;
	auio.uio_rw = UIO_READ;
	auio.uio_resid = buflen;
	auio.uio_offset = off;
	UIO_SETUP_SYSSPACE(&auio);
	/*
         * 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, &cookiebuf,
	    &ncookies);
	if (error)
		goto out;

	inp = tbuf;
	outp = SCARG(uap, buf);
	resid = nbytes;
	if ((len = buflen - auio.uio_resid) == 0)
		goto eof;

	for (cookie = cookiebuf; len > 0; len -= reclen) {
		bdp = (struct dirent *)inp;
		reclen = bdp->d_reclen;
		if (reclen & 3)
			panic(__func__);
		if (bdp->d_fileno == 0) {
			inp += reclen;	/* it is a hole; squish it out */
			if (cookie)
				off = *cookie++;
			else
				off += reclen;
			continue;
		}
		old_reclen = _DIRENT_RECLEN(&idb, bdp->d_namlen);
		if (reclen > len || resid < old_reclen) {
			/* entry too big for buffer, so just stop */
			outp++;
			break;
		}
		/*
		 * Massage in place to make a Dirent12-shaped dirent (otherwise
		 * we have to worry about touching user memory outside of
		 * the copyout() call).
		 */
		idb.d_fileno = (uint32_t)bdp->d_fileno;
		idb.d_reclen = (uint16_t)old_reclen;
		idb.d_namlen = (uint16_t)bdp->d_namlen;
		strcpy(idb.d_name, bdp->d_name);
		if ((error = copyout(&idb, outp, old_reclen)))
			goto out;
		/* advance past this real entry */
		inp += reclen;
		if (cookie)
			off = *cookie++; /* each entry points to itself */
		else
			off += reclen;
		/* advance output past Dirent12-shaped entry */
		outp += old_reclen;
		resid -= old_reclen;
	}

	/* if we squished out the whole block, try again */
	if (outp == SCARG(uap, buf)) {
		if (cookiebuf)
			free(cookiebuf, M_TEMP);
		cookiebuf = NULL;
		goto again;
	}
	fp->f_offset = off;	/* update the vnode offset */

eof:
	*retval = nbytes - resid;
out:
	VOP_UNLOCK(vp);
	if (cookiebuf)
		free(cookiebuf, M_TEMP);
	free(tbuf, M_TEMP);
out1:
	fd_putfile(SCARG(uap, fd));
	if (error)
		return error;
	return copyout(&loff, SCARG(uap, basep), sizeof(long));
}
示例#18
0
文件: xfs_file.c 项目: crossmeta/sgi
STATIC int linvfs_readdir(
	struct file *filp,
	void *dirent,
	filldir_t filldir)
{
	int			error = 0;
	vnode_t			*vp;
	uio_t			uio;
	iovec_t			iov;
	int			eof = 0;
	cred_t			cred;		/* Temporary cred workaround */
	caddr_t			read_buf;
	int			namelen, size = 0;
	size_t			rlen = PAGE_CACHE_SIZE << 2;
	off_t			start_offset;
	dirent_t		*dbp = NULL;

        vp = LINVFS_GET_VP(filp->f_dentry->d_inode);

	ASSERT(vp);
	/* Try fairly hard to get memory */
	do {
		if ((read_buf = (caddr_t)kmalloc(rlen, GFP_KERNEL)))
			break;
		rlen >>= 1;
	} while (rlen >= 1024);

	if (read_buf == NULL)
		return -ENOMEM;

	uio.uio_iov = &iov;
	uio.uio_fmode = filp->f_mode;
	uio.uio_segflg = UIO_SYSSPACE;
	uio.uio_offset = filp->f_pos;

	while (!eof) {
		uio.uio_resid = iov.iov_len = rlen;
		iov.iov_base = read_buf;
		uio.uio_iovcnt = 1;

		start_offset = uio.uio_offset;
		
		VOP_READDIR(vp, &uio, &cred, &eof, error);
		if ((uio.uio_offset == start_offset) || error) {
			size = 0;
			break;
		}

		size = rlen - uio.uio_resid;
		dbp = (dirent_t *)read_buf;
		while (size > 0) {
			namelen = strlen(dbp->d_name);

			if (filldir(dirent, dbp->d_name, namelen,
					(off_t) dbp->d_off,
					(linux_ino_t) dbp->d_ino,
					DT_UNKNOWN)) {
				goto done;
			}
			size -= dbp->d_reclen;
			dbp = nextdp(dbp);
		}
	}
done:
	if (!error) {
		if (size == 0)
			filp->f_pos = uio.uio_offset;
		else if (dbp)
			filp->f_pos = dbp->d_off;
	}

	kfree(read_buf);
	return -error;
}
示例#19
0
static int zfsfuse_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi)
{
	vnode_t *vp = ((file_info_t *)(uintptr_t) fi->fh)->vp;
	ASSERT(vp != NULL);
	ASSERT(VTOZ(vp) != NULL);
	ASSERT(VTOZ(vp)->z_id == ino);

	if(vp->v_type != VDIR)
		return ENOTDIR;

	vfs_t *vfs = (vfs_t *) fuse_req_userdata(req);
	zfsvfs_t *zfsvfs = vfs->vfs_data;

	char *outbuf = kmem_alloc(size, KM_NOSLEEP);
	if(outbuf == NULL)
		return ENOMEM;

	ZFS_ENTER(zfsvfs);

	cred_t cred;
	zfsfuse_getcred(req, &cred);

	union {
		char buf[DIRENT64_RECLEN(MAXNAMELEN)];
		struct dirent64 dirent;
	} entry;

	struct stat fstat = { 0 };

	iovec_t iovec;
	uio_t uio;
	uio.uio_iov = &iovec;
	uio.uio_iovcnt = 1;
	uio.uio_segflg = UIO_SYSSPACE;
	uio.uio_fmode = 0;
	uio.uio_llimit = RLIM64_INFINITY;

	int eofp = 0;

	int outbuf_off = 0;
	int outbuf_resid = size;

	off_t next = off;

	int error;

	for(;;) {
		iovec.iov_base = entry.buf;
		iovec.iov_len = sizeof(entry.buf);
		uio.uio_resid = iovec.iov_len;
		uio.uio_loffset = next;

		error = VOP_READDIR(vp, &uio, &cred, &eofp, NULL, 0);
		if(error)
			goto out;

		/* No more directory entries */
		if(iovec.iov_base == entry.buf)
			break;

		fstat.st_ino = entry.dirent.d_ino;
		fstat.st_mode = 0;

		int dsize = fuse_dirent_size(strlen(entry.dirent.d_name));
		if(dsize > outbuf_resid)
			break;

		fuse_add_dirent(outbuf + outbuf_off, entry.dirent.d_name, &fstat, entry.dirent.d_off);

		outbuf_off += dsize;
		outbuf_resid -= dsize;
		next = entry.dirent.d_off;
	}

out:
	ZFS_EXIT(zfsvfs);

	if(!error)
		fuse_reply_buf(req, outbuf, outbuf_off);

	kmem_free(outbuf, size);

	return error;
}
示例#20
0
/*
 * Linux 'readdir' call. This code is mostly taken from the
 * SunOS getdents call (see compat/sunos/sunos_misc.c), though
 * an attempt has been made to keep it a little cleaner.
 *
 * The d_off field contains the offset of the next valid entry,
 * unless the older Linux getdents(2), which used to have it set
 * to the offset of the entry itself. This function also doesn't
 * need to deal with the old count == 1 glibc problem.
 *
 * Read in BSD-style entries, convert them, and copy them out.
 *
 * Note that this doesn't handle union-mounted filesystems.
 */
int
linux_sys_getdents64(struct lwp *l, const struct linux_sys_getdents64_args *uap, register_t *retval)
{
	/* {
		syscallarg(int) fd;
		syscallarg(struct linux_dirent64 *) dent;
		syscallarg(unsigned int) count;
	} */
	struct dirent *bdp;
	struct vnode *vp;
	char *inp, *tbuf;		/* BSD-format */
	int len, reclen;		/* BSD-format */
	char *outp;			/* Linux-format */
	int resid, linux_reclen = 0;	/* Linux-format */
	file_t *fp;
	struct uio auio;
	struct iovec aiov;
	struct linux_dirent64 idb;
	off_t off;		/* true file offset */
	int buflen, error, eofflag, nbytes;
	struct vattr va;
	off_t *cookiebuf = NULL, *cookie;
	int ncookies;

	/* fd_getvnode() will use the descriptor for us */
	if ((error = fd_getvnode(SCARG(uap, fd), &fp)) != 0)
		return (error);

	if ((fp->f_flag & FREAD) == 0) {
		error = EBADF;
		goto out1;
	}

	vp = (struct vnode *)fp->f_data;
	if (vp->v_type != VDIR) {
		error = EINVAL;
		goto out1;
	}

	if ((error = VOP_GETATTR(vp, &va, l->l_cred)))
		goto out1;

	nbytes = SCARG(uap, count);
	buflen = min(MAXBSIZE, nbytes);
	if (buflen < va.va_blocksize)
		buflen = va.va_blocksize;
	tbuf = malloc(buflen, M_TEMP, M_WAITOK);

	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
	off = fp->f_offset;
again:
	aiov.iov_base = tbuf;
	aiov.iov_len = buflen;
	auio.uio_iov = &aiov;
	auio.uio_iovcnt = 1;
	auio.uio_rw = UIO_READ;
	auio.uio_resid = buflen;
	auio.uio_offset = off;
	UIO_SETUP_SYSSPACE(&auio);
	/*
         * 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, &cookiebuf,
	    &ncookies);
	if (error)
		goto out;

	inp = tbuf;
	outp = (void *)SCARG(uap, dent);
	resid = nbytes;
	if ((len = buflen - auio.uio_resid) == 0)
		goto eof;

	for (cookie = cookiebuf; len > 0; len -= reclen) {
		bdp = (struct dirent *)inp;
		reclen = bdp->d_reclen;
		if (reclen & 3)
			panic("linux_readdir");
		if (bdp->d_fileno == 0) {
			inp += reclen;	/* it is a hole; squish it out */
			if (cookie)
				off = *cookie++;
			else
				off += reclen;
			continue;
		}
		linux_reclen = LINUX_RECLEN(&idb, bdp->d_namlen);
		if (reclen > len || resid < linux_reclen) {
			/* entry too big for buffer, so just stop */
			outp++;
			break;
		}
		if (cookie)
			off = *cookie++;	/* each entry points to next */
		else
			off += reclen;
		/*
		 * Massage in place to make a Linux-shaped dirent (otherwise
		 * we have to worry about touching user memory outside of
		 * the copyout() call).
		 */
		idb.d_ino = bdp->d_fileno;
		idb.d_type = bdp->d_type;
		idb.d_off = off;
		idb.d_reclen = (u_short)linux_reclen;
		strcpy(idb.d_name, bdp->d_name);
		if ((error = copyout((void *)&idb, outp, linux_reclen)))
			goto out;
		/* advance past this real entry */
		inp += reclen;
		/* advance output past Linux-shaped entry */
		outp += linux_reclen;
		resid -= linux_reclen;
	}

	/* if we squished out the whole block, try again */
	if (outp == (void *)SCARG(uap, dent)) {
		if (cookiebuf)
			free(cookiebuf, M_TEMP);
		cookiebuf = NULL;
		goto again;
	}
	fp->f_offset = off;	/* update the vnode offset */

eof:
	*retval = nbytes - resid;
out:
	VOP_UNLOCK(vp, 0);
	if (cookiebuf)
		free(cookiebuf, M_TEMP);
	free(tbuf, M_TEMP);
out1:
	fd_putfile(SCARG(uap, fd));
	return error;
}