Пример #1
0
struct dirent *
glob_readdir(RST_DIR *dirp)
{
	enum {
		ADIRENT_STORAGE_LEN = _DIRENT_RECLEN(NAME_MAX),
	};
	static union {
		uint8_t storage[ADIRENT_STORAGE_LEN];
		struct dirent alignment;
	} adirent;
	struct direct *dp;
	struct dirent *adp;

	while ((dp = rst_readdir(dirp)) != NULL) {
		if (!vflag && dp->d_ino == UFS_WINO)
			continue;
		if (dflag || TSTINO(dp->d_ino, dumpmap))
			break;
	}
	if (dp == NULL)
		return (NULL);
	adp = (struct dirent *)&adirent;
	adp->d_fileno = dp->d_ino;
	adp->d_namlen = dp->d_namlen;
	strcpy(adp->d_name, dp->d_name);
	return (adp);
}
Пример #2
0
static struct dirent *
gl_readdir(void *v)
{
	static struct dirent dir;
	struct gl_dir *dd = v;
	if (dd->pos < dd->len) {
		const struct gl_file *f = &dd->dir[dd->pos++];
		strcpy(dir.d_name, f->name);
		dir.d_namlen = strlen(f->name);
		dir.d_ino = dd->pos;
		dir.d_type = f->dir ? DT_DIR : DT_REG;
		DPRINTF(("readdir %s %d\n", dir.d_name, dir.d_type));
#ifdef __FreeBSD__
		dir.d_reclen = -1; /* Does not have _DIRENT_RECLEN */
#else
		dir.d_reclen = _DIRENT_RECLEN(&dir, dir.d_namlen);
#endif
		return &dir;
	}
	return NULL;
}
Пример #3
0
int
puffs_nextdent(struct dirent **dent, const char *name, ino_t id, uint8_t dtype,
	size_t *reslen)
{
	struct dirent *d = *dent;
        unsigned int len, reclen;
        int o;
	char *cp;

	/* check if we have enough room for our dent-aligned dirent */
	if (_DIRENT_RECLEN(d, strlen(name)) > *reslen)
		return 0;

	d->d_ino = id;

	/* Compute the length of the name */
	cp = memchr(name, '\0', NAME_MAX);
	if (cp == NULL)
		len = NAME_MAX;
	else
		len = cp - (name);

	/* Compute record length */
	reclen = offsetof(struct dirent, d_name) + len + 1;
	o = (reclen % sizeof(long));
	if (o != 0)
		reclen += sizeof(long) - o;

        /* FIXME: Set d_off?
         * dep->d_off = 
         */
	d->d_reclen = (unsigned short) reclen;
        (void)memcpy(d->d_name, name, (size_t)len);
        d->d_name[len] = '\0';

	*reslen -= d->d_reclen;
	*dent = _DIRENT_NEXT(d);

	return 1;
}
Пример #4
0
/*
 * Vnode op for reading directories.
 *
 * This routine handles converting from the on-disk directory format
 * "struct direct" to the in-memory format "struct dirent" as well as
 * byte swapping the entries if necessary.
 */
int
ufs_readdir(void *v)
{
	struct vop_readdir_args /* {
		struct vnode	*a_vp;
		struct uio	*a_uio;
		kauth_cred_t	a_cred;
		int		*a_eofflag;
		off_t		**a_cookies;
		int		*ncookies;
	} */ *ap = v;
	struct vnode	*vp = ap->a_vp;
	struct direct	*cdp, *ecdp;
	struct dirent	*ndp;
	char		*cdbuf, *ndbuf, *endp;
	struct uio	auio, *uio;
	struct iovec	aiov;
	int		error;
	size_t		count, ccount, rcount, cdbufsz, ndbufsz;
	off_t		off, *ccp;
	off_t		startoff;
	size_t		skipbytes;
	struct ufsmount	*ump = VFSTOUFS(vp->v_mount);
	int nswap = UFS_MPNEEDSWAP(ump);
#if BYTE_ORDER == LITTLE_ENDIAN
	int needswap = ump->um_maxsymlinklen <= 0 && nswap == 0;
#else
	int needswap = ump->um_maxsymlinklen <= 0 && nswap != 0;
#endif
	uio = ap->a_uio;
	count = uio->uio_resid;
	rcount = count - ((uio->uio_offset + count) & (ump->um_dirblksiz - 1));

	if (rcount < _DIRENT_MINSIZE(cdp) || count < _DIRENT_MINSIZE(ndp))
		return EINVAL;

	startoff = uio->uio_offset & ~(ump->um_dirblksiz - 1);
	skipbytes = uio->uio_offset - startoff;
	rcount += skipbytes;

	auio.uio_iov = &aiov;
	auio.uio_iovcnt = 1;
	auio.uio_offset = startoff;
	auio.uio_resid = rcount;
	UIO_SETUP_SYSSPACE(&auio);
	auio.uio_rw = UIO_READ;
	cdbufsz = rcount;
	cdbuf = kmem_alloc(cdbufsz, KM_SLEEP);
	aiov.iov_base = cdbuf;
	aiov.iov_len = rcount;
	error = VOP_READ(vp, &auio, 0, ap->a_cred);
	if (error != 0) {
		kmem_free(cdbuf, cdbufsz);
		return error;
	}

	rcount -= auio.uio_resid;

	cdp = (struct direct *)(void *)cdbuf;
	ecdp = (struct direct *)(void *)&cdbuf[rcount];

	ndbufsz = count;
	ndbuf = kmem_alloc(ndbufsz, KM_SLEEP);
	ndp = (struct dirent *)(void *)ndbuf;
	endp = &ndbuf[count];

	off = uio->uio_offset;
	if (ap->a_cookies) {
		ccount = rcount / _DIRENT_RECLEN(cdp, 1);
		ccp = *(ap->a_cookies) = malloc(ccount * sizeof(*ccp),
		    M_TEMP, M_WAITOK);
	} else {
		/* XXX: GCC */
		ccount = 0;
		ccp = NULL;
	}

	while (cdp < ecdp) {
		cdp->d_reclen = ufs_rw16(cdp->d_reclen, nswap);
		if (skipbytes > 0) {
			if (cdp->d_reclen <= skipbytes) {
				skipbytes -= cdp->d_reclen;
				cdp = _DIRENT_NEXT(cdp);
				continue;
			}
			/*
			 * invalid cookie.
			 */
			error = EINVAL;
			goto out;
		}
		if (cdp->d_reclen == 0) {
			struct dirent *ondp = ndp;
			ndp->d_reclen = _DIRENT_MINSIZE(ndp);
			ndp = _DIRENT_NEXT(ndp);
			ondp->d_reclen = 0;
			cdp = ecdp;
			break;
		}
		if (needswap) {
			ndp->d_type = cdp->d_namlen;
			ndp->d_namlen = cdp->d_type;
		} else {
			ndp->d_type = cdp->d_type;
			ndp->d_namlen = cdp->d_namlen;
		}
		ndp->d_reclen = _DIRENT_RECLEN(ndp, ndp->d_namlen);
		if ((char *)(void *)ndp + ndp->d_reclen +
		    _DIRENT_MINSIZE(ndp) > endp)
			break;
		ndp->d_fileno = ufs_rw32(cdp->d_ino, nswap);
		(void)memcpy(ndp->d_name, cdp->d_name, ndp->d_namlen);
		memset(&ndp->d_name[ndp->d_namlen], 0,
		    ndp->d_reclen - _DIRENT_NAMEOFF(ndp) - ndp->d_namlen);
		off += cdp->d_reclen;
		if (ap->a_cookies) {
			KASSERT(ccp - *(ap->a_cookies) < ccount);
			*(ccp++) = off;
		}
		ndp = _DIRENT_NEXT(ndp);
		cdp = _DIRENT_NEXT(cdp);
	}

	count = ((char *)(void *)ndp - ndbuf);
	error = uiomove(ndbuf, count, uio);
out:
	if (ap->a_cookies) {
		if (error) {
			free(*(ap->a_cookies), M_TEMP);
			*(ap->a_cookies) = NULL;
			*(ap->a_ncookies) = 0;
		} else {
			*ap->a_ncookies = ccp - *(ap->a_cookies);
		}
	}
	uio->uio_offset = off;
	kmem_free(ndbuf, ndbufsz);
	kmem_free(cdbuf, cdbufsz);
	*ap->a_eofflag = VTOI(vp)->i_size <= uio->uio_offset;
	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));
}
Пример #6
0
static int
puffs_vnop_readdir(struct vop_readdir_args *ap)
{
    PUFFS_MSG_VARS(vn, readdir);
    struct vnode *vp = ap->a_vp;
    struct puffs_mount *pmp = MPTOPUFFSMP(vp->v_mount);
    size_t argsize, tomove, cookiemem, cookiesmax;
    struct uio *uio = ap->a_uio;
    size_t howmuch, resid;
    int error;

    if (!EXISTSOP(pmp, READDIR))
        return EOPNOTSUPP;

    /*
     * ok, so we need: resid + cookiemem = maxreq
     * => resid + cookiesize * (resid/minsize) = maxreq
     * => resid + cookiesize/minsize * resid = maxreq
     * => (cookiesize/minsize + 1) * resid = maxreq
     * => resid = maxreq / (cookiesize/minsize + 1)
     *
     * Since cookiesize <= minsize and we're not very big on floats,
     * we approximate that to be 1.  Therefore:
     *
     * resid = maxreq / 2;
     *
     * Well, at least we didn't have to use differential equations
     * or the Gram-Schmidt process.
     *
     * (yes, I'm very afraid of this)
     */
    KKASSERT(CSIZE <= _DIRENT_RECLEN(1));

    if (ap->a_cookies) {
        KKASSERT(ap->a_ncookies != NULL);
        if (pmp->pmp_args.pa_fhsize == 0)
            return EOPNOTSUPP;
        resid = PUFFS_TOMOVE(uio->uio_resid, pmp) / 2;
        cookiesmax = resid/_DIRENT_RECLEN(1);
        cookiemem = ALIGN(cookiesmax*CSIZE); /* play safe */
    } else {
        resid = PUFFS_TOMOVE(uio->uio_resid, pmp);
        cookiesmax = 0;
        cookiemem = 0;
    }

    argsize = sizeof(struct puffs_vnmsg_readdir);
    tomove = resid + cookiemem;
    puffs_msgmem_alloc(argsize + tomove, &park_readdir,
                       (void *)&readdir_msg, 1);

    puffs_credcvt(&readdir_msg->pvnr_cred, ap->a_cred);
    readdir_msg->pvnr_offset = uio->uio_offset;
    readdir_msg->pvnr_resid = resid;
    readdir_msg->pvnr_ncookies = cookiesmax;
    readdir_msg->pvnr_eofflag = 0;
    readdir_msg->pvnr_dentoff = cookiemem;
    puffs_msg_setinfo(park_readdir, PUFFSOP_VN,
                      PUFFS_VN_READDIR, VPTOPNC(vp));
    puffs_msg_setdelta(park_readdir, tomove);

    PUFFS_MSG_ENQUEUEWAIT2(pmp, park_readdir, vp->v_data, NULL, error);
    error = checkerr(pmp, error, __func__);
    if (error)
        goto out;

    /* userspace is cheating? */
    if (readdir_msg->pvnr_resid > resid) {
        puffs_senderr(pmp, PUFFS_ERR_READDIR, E2BIG,
                      "resid grew", VPTOPNC(vp));
        ERROUT(EPROTO);
    }
    if (readdir_msg->pvnr_ncookies > cookiesmax) {
        puffs_senderr(pmp, PUFFS_ERR_READDIR, E2BIG,
                      "too many cookies", VPTOPNC(vp));
        ERROUT(EPROTO);
    }

    /* check eof */
    if (readdir_msg->pvnr_eofflag)
        *ap->a_eofflag = 1;

    /* bouncy-wouncy with the directory data */
    howmuch = resid - readdir_msg->pvnr_resid;

    /* force eof if no data was returned (getcwd() needs this) */
    if (howmuch == 0) {
        *ap->a_eofflag = 1;
        goto out;
    }

    error = uiomove(readdir_msg->pvnr_data + cookiemem, howmuch, uio);
    if (error)
        goto out;

    /* provide cookies to caller if so desired */
    if (ap->a_cookies) {
        *ap->a_cookies = kmalloc(readdir_msg->pvnr_ncookies*CSIZE,
                                 M_TEMP, M_WAITOK);
        *ap->a_ncookies = readdir_msg->pvnr_ncookies;
        memcpy(*ap->a_cookies, readdir_msg->pvnr_data,
               *ap->a_ncookies*CSIZE);
    }

    /* next readdir starts here */
    uio->uio_offset = readdir_msg->pvnr_offset;

out:
    puffs_msgmem_release(park_readdir);
    return error;
}
Пример #7
0
static int
smbfs_readvdir(struct vnode *vp, struct uio *uio, struct ucred *cred)
{
	struct smb_cred scred;
	struct smbfs_fctx *ctx;
	struct vnode *newvp;
	struct smbnode *np;
	int error, offset, retval;

	np = VTOSMB(vp);
	SMBVDEBUG("dirname='%s'\n", np->n_name);
	smb_makescred(&scred, uio->uio_td, cred);

	if (uio->uio_offset < 0 || uio->uio_offset > INT_MAX)
		return(EINVAL);

	error = 0;
	offset = uio->uio_offset;

	if (uio->uio_resid > 0 && offset < 1) {
		if (vop_write_dirent(&error, uio, np->n_ino, DT_DIR, 1, "."))
			goto done;
		if (error)
			goto done;
		++offset;
	}

	if (uio->uio_resid > 0 && offset < 2) {
		if (vop_write_dirent(&error, uio,
		    np->n_parent ? VTOSMB(np->n_parent)->n_ino : 2,
		    DT_DIR, 2, ".."))
			goto done;
		if (error)
			goto done;
		++offset;
	}

	if (uio->uio_resid == 0)
		goto done;

	if (offset != np->n_dirofs || np->n_dirseq == NULL) {
		SMBVDEBUG("Reopening search %ld:%ld\n", offset, np->n_dirofs);
		if (np->n_dirseq) {
			smbfs_findclose(np->n_dirseq, &scred);
			np->n_dirseq = NULL;
		}
		np->n_dirofs = 2;
		error = smbfs_findopen(np, "*", 1,
		    SMB_FA_SYSTEM | SMB_FA_HIDDEN | SMB_FA_DIR,
		    &scred, &ctx);
		if (error) {
			SMBVDEBUG("can not open search, error = %d", error);
			return error;
		}
		np->n_dirseq = ctx;
	} else {
		ctx = np->n_dirseq;
	}
	while (np->n_dirofs < offset) {
		error = smbfs_findnext(ctx, offset - np->n_dirofs, &scred);
		++np->n_dirofs;
		if (error) {
			smbfs_findclose(np->n_dirseq, &scred);
			np->n_dirseq = NULL;
			return error == ENOENT ? 0 : error;
		}
	}
	error = 0;
	while (uio->uio_resid > 0 && !error) {
		/*
		 * Overestimate the size of a record a bit, doesn't really
		 * hurt to be wrong here.
		 */
		error = smbfs_findnext(ctx, uio->uio_resid / _DIRENT_RECLEN(255) + 1, &scred);
		if (error)
			break;
		np->n_dirofs++;
		++offset;

		retval = vop_write_dirent(&error, uio, ctx->f_attr.fa_ino,
		    (ctx->f_attr.fa_attr & SMB_FA_DIR) ? DT_DIR : DT_REG,
		    ctx->f_nmlen, ctx->f_name);
		if (retval)
			break;
		if (smbfs_fastlookup && !error) {
			error = smbfs_nget(vp->v_mount, vp, ctx->f_name,
			    ctx->f_nmlen, &ctx->f_attr, &newvp);
			if (!error)
				vput(newvp);
		}
	}
	if (error == ENOENT)
		error = 0;
done:
	uio->uio_offset = offset;
	return error;
}
/*
 * Vnode op for reading directories.
 *
 * This routine handles converting from the on-disk directory format
 * "struct lfs_direct" to the in-memory format "struct dirent" as well as
 * byte swapping the entries if necessary.
 */
int
ulfs_readdir(void *v)
{
	struct vop_readdir_args /* {
		struct vnode	*a_vp;
		struct uio	*a_uio;
		kauth_cred_t	a_cred;
		int		*a_eofflag;
		off_t		**a_cookies;
		int		*ncookies;
	} */ *ap = v;
	struct vnode	*vp = ap->a_vp;
	LFS_DIRHEADER	*cdp, *ecdp;
	struct dirent	*ndp;
	char		*cdbuf, *ndbuf, *endp;
	struct uio	auio, *uio;
	struct iovec	aiov;
	int		error;
	size_t		count, ccount, rcount, cdbufsz, ndbufsz;
	off_t		off, *ccp;
	off_t		startoff;
	size_t		skipbytes;
	struct ulfsmount *ump = VFSTOULFS(vp->v_mount);
	struct lfs *fs = ump->um_lfs;
	uio = ap->a_uio;
	count = uio->uio_resid;
	rcount = count - ((uio->uio_offset + count) & (fs->um_dirblksiz - 1));

	if (rcount < LFS_DIRECTSIZ(fs, 0) || count < _DIRENT_MINSIZE(ndp))
		return EINVAL;

	startoff = uio->uio_offset & ~(fs->um_dirblksiz - 1);
	skipbytes = uio->uio_offset - startoff;
	rcount += skipbytes;

	auio.uio_iov = &aiov;
	auio.uio_iovcnt = 1;
	auio.uio_offset = startoff;
	auio.uio_resid = rcount;
	UIO_SETUP_SYSSPACE(&auio);
	auio.uio_rw = UIO_READ;
	cdbufsz = rcount;
	cdbuf = kmem_alloc(cdbufsz, KM_SLEEP);
	aiov.iov_base = cdbuf;
	aiov.iov_len = rcount;
	error = VOP_READ(vp, &auio, 0, ap->a_cred);
	if (error != 0) {
		kmem_free(cdbuf, cdbufsz);
		return error;
	}

	rcount -= auio.uio_resid;

	cdp = (LFS_DIRHEADER *)(void *)cdbuf;
	ecdp = (LFS_DIRHEADER *)(void *)&cdbuf[rcount];

	ndbufsz = count;
	ndbuf = kmem_alloc(ndbufsz, KM_SLEEP);
	ndp = (struct dirent *)(void *)ndbuf;
	endp = &ndbuf[count];

	off = uio->uio_offset;
	if (ap->a_cookies) {
		ccount = rcount / _DIRENT_RECLEN(ndp, 1);
		ccp = *(ap->a_cookies) = malloc(ccount * sizeof(*ccp),
		    M_TEMP, M_WAITOK);
	} else {
		/* XXX: GCC */
		ccount = 0;
		ccp = NULL;
	}

	while (cdp < ecdp) {
		if (skipbytes > 0) {
			if (lfs_dir_getreclen(fs, cdp) <= skipbytes) {
				skipbytes -= lfs_dir_getreclen(fs, cdp);
				cdp = LFS_NEXTDIR(fs, cdp);
				continue;
			}
			/*
			 * invalid cookie.
			 */
			error = EINVAL;
			goto out;
		}
		if (lfs_dir_getreclen(fs, cdp) == 0) {
			struct dirent *ondp = ndp;
			ndp->d_reclen = _DIRENT_MINSIZE(ndp);
			ndp = _DIRENT_NEXT(ndp);
			ondp->d_reclen = 0;
			cdp = ecdp;
			break;
		}
		ndp->d_type = lfs_dir_gettype(fs, cdp);
		ndp->d_namlen = lfs_dir_getnamlen(fs, cdp);
		ndp->d_reclen = _DIRENT_RECLEN(ndp, ndp->d_namlen);
		if ((char *)(void *)ndp + ndp->d_reclen +
		    _DIRENT_MINSIZE(ndp) > endp)
			break;
		ndp->d_fileno = lfs_dir_getino(fs, cdp);
		(void)memcpy(ndp->d_name, lfs_dir_nameptr(fs, cdp),
			     ndp->d_namlen);
		memset(&ndp->d_name[ndp->d_namlen], 0,
		    ndp->d_reclen - _DIRENT_NAMEOFF(ndp) - ndp->d_namlen);
		off += lfs_dir_getreclen(fs, cdp);
		if (ap->a_cookies) {
			KASSERT(ccp - *(ap->a_cookies) < ccount);
			*(ccp++) = off;
		}
		ndp = _DIRENT_NEXT(ndp);
		cdp = LFS_NEXTDIR(fs, cdp);
	}

	count = ((char *)(void *)ndp - ndbuf);
	error = uiomove(ndbuf, count, uio);
out:
	if (ap->a_cookies) {
		if (error) {
			free(*(ap->a_cookies), M_TEMP);
			*(ap->a_cookies) = NULL;
			*(ap->a_ncookies) = 0;
		} else {
			*ap->a_ncookies = ccp - *(ap->a_cookies);
		}
	}
	uio->uio_offset = off;
	kmem_free(ndbuf, ndbufsz);
	kmem_free(cdbuf, cdbufsz);
	*ap->a_eofflag = VTOI(vp)->i_size <= uio->uio_offset;
	return error;
}