FreeBSD: satisfy VFS requirements for readdir()

zfsctl_root_readdir(): properly set eof.
readdir(): set *eofp to 1 on eof.
If there were no dirents to copy out, return EINVAL same as UFS.

Reviewed-by: Alexander Motin <alexander.motin@TrueNAS.com>
Signed-off-by: Konstantin Belousov <kib@FreeBSD.org>
Closes #17655
This commit is contained in:
Konstantin Belousov 2025-08-22 16:14:36 +03:00 committed by GitHub
parent 94413bc75d
commit 28ff57505b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 32 additions and 16 deletions

View File

@ -674,6 +674,7 @@ zfsctl_root_readdir(struct vop_readdir_args *ap)
zfs_uio_t uio;
int *eofp = ap->a_eofflag;
off_t dots_offset;
ssize_t orig_resid;
int error;
zfs_uio_init(&uio, ap->a_uio);
@ -688,16 +689,16 @@ zfsctl_root_readdir(struct vop_readdir_args *ap)
* count to return is 0.
*/
if (zfs_uio_offset(&uio) == 3 * sizeof (entry)) {
if (eofp != NULL)
*eofp = 1;
return (0);
}
orig_resid = zfs_uio_resid(&uio);
error = sfs_readdir_common(zfsvfs->z_root, ZFSCTL_INO_ROOT, ap, &uio,
&dots_offset);
if (error != 0) {
if (error == ENAMETOOLONG) /* ran out of destination space */
error = 0;
return (error);
}
if (error != 0)
goto err;
if (zfs_uio_offset(&uio) != dots_offset)
return (SET_ERROR(EINVAL));
@ -710,8 +711,11 @@ zfsctl_root_readdir(struct vop_readdir_args *ap)
entry.d_reclen = sizeof (entry);
error = vfs_read_dirent(ap, &entry, zfs_uio_offset(&uio));
if (error != 0) {
if (error == ENAMETOOLONG)
error = 0;
err:
if (error == ENAMETOOLONG) {
error = orig_resid == zfs_uio_resid(&uio) ?
EINVAL : 0;
}
return (SET_ERROR(error));
}
if (eofp != NULL)
@ -1056,17 +1060,21 @@ zfsctl_snapdir_readdir(struct vop_readdir_args *ap)
zfs_uio_t uio;
int *eofp = ap->a_eofflag;
off_t dots_offset;
ssize_t orig_resid;
int error;
zfs_uio_init(&uio, ap->a_uio);
orig_resid = zfs_uio_resid(&uio);
ASSERT3S(vp->v_type, ==, VDIR);
error = sfs_readdir_common(ZFSCTL_INO_ROOT, ZFSCTL_INO_SNAPDIR, ap,
&uio, &dots_offset);
if (error != 0) {
if (error == ENAMETOOLONG) /* ran out of destination space */
error = 0;
if (error == ENAMETOOLONG) { /* ran out of destination space */
error = orig_resid == zfs_uio_resid(&uio) ?
EINVAL : 0;
}
return (error);
}
@ -1084,9 +1092,13 @@ zfsctl_snapdir_readdir(struct vop_readdir_args *ap)
dsl_pool_config_exit(dmu_objset_pool(zfsvfs->z_os), FTAG);
if (error != 0) {
if (error == ENOENT) {
if (eofp != NULL)
*eofp = 1;
error = 0;
if (orig_resid == zfs_uio_resid(&uio)) {
error = EINVAL;
} else {
error = 0;
if (eofp != NULL)
*eofp = 1;
}
}
zfs_exit(zfsvfs, FTAG);
return (error);
@ -1099,8 +1111,10 @@ zfsctl_snapdir_readdir(struct vop_readdir_args *ap)
entry.d_reclen = sizeof (entry);
error = vfs_read_dirent(ap, &entry, zfs_uio_offset(&uio));
if (error != 0) {
if (error == ENAMETOOLONG)
error = 0;
if (error == ENAMETOOLONG) {
error = orig_resid == zfs_uio_resid(&uio) ?
EINVAL : 0;
}
zfs_exit(zfsvfs, FTAG);
return (SET_ERROR(error));
}

View File

@ -1695,6 +1695,7 @@ zfs_readdir(vnode_t *vp, zfs_uio_t *uio, cred_t *cr, int *eofp,
objset_t *os;
caddr_t outbuf;
size_t bufsize;
ssize_t orig_resid;
zap_cursor_t zc;
zap_attribute_t *zap;
uint_t bytes_wanted;
@ -1735,7 +1736,7 @@ zfs_readdir(vnode_t *vp, zfs_uio_t *uio, cred_t *cr, int *eofp,
/*
* Quit if directory has been removed (posix)
*/
if ((*eofp = zp->z_unlinked) != 0) {
if ((*eofp = (zp->z_unlinked != 0)) != 0) {
zfs_exit(zfsvfs, FTAG);
return (0);
}
@ -1743,6 +1744,7 @@ zfs_readdir(vnode_t *vp, zfs_uio_t *uio, cred_t *cr, int *eofp,
error = 0;
os = zfsvfs->z_os;
offset = zfs_uio_offset(uio);
orig_resid = zfs_uio_resid(uio);
prefetch = zp->z_zn_prefetch;
zap = zap_attribute_long_alloc();
@ -1922,7 +1924,7 @@ update:
kmem_free(outbuf, bufsize);
if (error == ENOENT)
error = 0;
error = orig_resid == zfs_uio_resid(uio) ? EINVAL : 0;
ZFS_ACCESSTIME_STAMP(zfsvfs, zp);