Skip to content

Commit

Permalink
FreeBSD: Implement zfs_listextattr_sa
Browse files Browse the repository at this point in the history
Signed-off-by: Ryan Moeller <ryan@iXsystems.com>
  • Loading branch information
Ryan Moeller committed May 5, 2021
1 parent 2f38723 commit d0f84dc
Showing 1 changed file with 101 additions and 55 deletions.
156 changes: 101 additions & 55 deletions module/os/freebsd/zfs/zfs_vnops_os.c
Original file line number Diff line number Diff line change
Expand Up @@ -5718,55 +5718,20 @@ struct vop_listextattr {
};
#endif

/*
* Vnode operation to retrieve extended attributes on a vnode.
*/
static int
zfs_listextattr(struct vop_listextattr_args *ap)
zfs_listextattr_dir(struct vop_listextattr_args *ap, const char *attrprefix)
{
zfsvfs_t *zfsvfs = VTOZ(ap->a_vp)->z_zfsvfs;
struct thread *td = ap->a_td;
struct nameidata nd;
char attrprefix[16];
uint8_t dirbuf[sizeof (struct dirent)];
struct dirent *dp;
struct iovec aiov;
struct uio auio;
size_t *sizep = ap->a_size;
size_t plen;
vnode_t *xvp = NULL, *vp;
int done, error, eof, pos;
zfs_uio_t uio;

zfs_uio_init(&uio, ap->a_uio);

/*
* If the xattr property is off, refuse the request.
*/
if (!(zfsvfs->z_flags & ZSB_XATTR)) {
return (SET_ERROR(EOPNOTSUPP));
}

error = extattr_check_cred(ap->a_vp, ap->a_attrnamespace,
ap->a_cred, ap->a_td, VREAD);
if (error != 0)
return (error);

error = zfs_create_attrname(ap->a_attrnamespace, "", attrprefix,
sizeof (attrprefix));
if (error != 0)
return (error);
plen = strlen(attrprefix);

ZFS_ENTER(zfsvfs);

if (sizep != NULL)
*sizep = 0;
int error, eof;

error = zfs_lookup(ap->a_vp, NULL, &xvp, NULL, 0, ap->a_cred, td,
LOOKUP_XATTR, B_FALSE);
if (error != 0) {
ZFS_EXIT(zfsvfs);
/*
* ENOATTR means that the EA directory does not yet exist,
* i.e. there are no extended attributes there.
Expand All @@ -5781,10 +5746,8 @@ zfs_listextattr(struct vop_listextattr_args *ap)
error = namei(&nd);
vp = nd.ni_vp;
NDFREE(&nd, NDF_ONLY_PNBUF);
if (error != 0) {
ZFS_EXIT(zfsvfs);
if (error != 0)
return (error);
}

auio.uio_iov = &aiov;
auio.uio_iovcnt = 1;
Expand All @@ -5793,43 +5756,42 @@ zfs_listextattr(struct vop_listextattr_args *ap)
auio.uio_rw = UIO_READ;
auio.uio_offset = 0;

do {
uint8_t nlen;
size_t plen = strlen(attrprefix);

do {
aiov.iov_base = (void *)dirbuf;
aiov.iov_len = sizeof (dirbuf);
auio.uio_resid = sizeof (dirbuf);
error = VOP_READDIR(vp, &auio, ap->a_cred, &eof, NULL, NULL);
done = sizeof (dirbuf) - auio.uio_resid;
if (error != 0)
break;
for (pos = 0; pos < done; ) {
dp = (struct dirent *)(dirbuf + pos);
int done = sizeof (dirbuf) - auio.uio_resid;
for (int pos = 0; pos < done; ) {
struct dirent *dp = (struct dirent *)(dirbuf + pos);
pos += dp->d_reclen;
/*
* XXX: Temporarily we also accept DT_UNKNOWN, as this
* is what we get when attribute was created on Solaris.
*/
if (dp->d_type != DT_REG && dp->d_type != DT_UNKNOWN)
continue;
if (plen == 0 &&
else if (plen == 0 &&
strncmp(dp->d_name, "freebsd:", 8) == 0)
continue;
else if (strncmp(dp->d_name, attrprefix, plen) != 0)
continue;
nlen = dp->d_namlen - plen;
if (sizep != NULL)
*sizep += 1 + nlen;
else if (GET_UIO_STRUCT(&uio) != NULL) {
uint8_t nlen = dp->d_namlen - plen;
if (ap->a_size != NULL) {
*ap->a_size += 1 + nlen;
} else if (ap->a_uio != NULL) {
/*
* Format of extattr name entry is one byte for
* length and the rest for name.
*/
error = zfs_uiomove(&nlen, 1, zfs_uio_rw(&uio),
&uio);
error = uiomove(&nlen, 1, ap->a_uio);
if (error == 0) {
error = zfs_uiomove(dp->d_name + plen,
nlen, zfs_uio_rw(&uio), &uio);
char *namep = dp->d_name + plen;
error = uiomove(namep, nlen, ap->a_uio);
}
if (error != 0)
break;
Expand All @@ -5838,8 +5800,92 @@ zfs_listextattr(struct vop_listextattr_args *ap)
} while (!eof && error == 0);

vput(vp);
ZFS_EXIT(zfsvfs);
return (error);
}

static int
zfs_listextattr_sa(struct vop_listextattr_args *ap, const char *attrprefix)
{
znode_t *zp = VTOZ(ap->a_vp);
int error;

error = zfs_ensure_xattr_cached(zp);
if (error != 0)
return (error);

ASSERT(RW_LOCK_HELD(&zp->z_xattr_lock));
ASSERT3P(zp->z_xattr_cached, !=, NULL);

size_t plen = strlen(attrprefix);
nvpair_t *nvp = NULL;
while ((nvp = nvlist_next_nvpair(zp->z_xattr_cached, nvp)) != NULL) {
ASSERT3U(nvpair_type(nvp), ==, DATA_TYPE_BYTE_ARRAY);

const char *name = nvpair_name(nvp);
if (plen == 0 && strncmp(name, "freebsd:", 8) == 0)
continue;
else if (strncmp(name, attrprefix, plen) != 0)
continue;
uint8_t nlen = strlen(name) - plen;
if (ap->a_size != NULL) {
*ap->a_size += 1 + nlen;
} else if (ap->a_uio != NULL) {
/*
* Format of extattr name entry is one byte for
* length and the rest for name.
*/
error = uiomove(&nlen, 1, ap->a_uio);
if (error == 0) {
char *namep = __DECONST(char *, name) + plen;
error = uiomove(namep, nlen, ap->a_uio);
}
if (error != 0)
break;
}
}

return (error);
}

/*
* Vnode operation to retrieve extended attributes on a vnode.
*/
static int
zfs_listextattr(struct vop_listextattr_args *ap)
{
znode_t *zp = VTOZ(ap->a_vp);
zfsvfs_t *zfsvfs = ZTOZSB(zp);
char attrprefix[16];
int error;

if (ap->a_size != NULL)
*ap->a_size = 0;

/*
* If the xattr property is off, refuse the request.
*/
if (!(zfsvfs->z_flags & ZSB_XATTR))
return (SET_ERROR(EOPNOTSUPP));

error = extattr_check_cred(ap->a_vp, ap->a_attrnamespace,
ap->a_cred, ap->a_td, VREAD);
if (error != 0)
return (error);

error = zfs_create_attrname(ap->a_attrnamespace, "", attrprefix,
sizeof (attrprefix));
if (error != 0)
return (error);

ZFS_ENTER(zfsvfs);
ZFS_VERIFY_ZP(zp);
rw_enter(&zp->z_xattr_lock, RW_READER);
if (zfsvfs->z_use_sa && zp->z_is_sa)
error = zfs_listextattr_sa(ap, attrprefix);
if (error == 0)
error = zfs_listextattr_dir(ap, attrprefix);
rw_exit(&zp->z_xattr_lock);
ZFS_EXIT(zfsvfs);
return (error);
}

Expand Down

0 comments on commit d0f84dc

Please sign in to comment.