diff --git a/module/os/freebsd/zfs/zfs_vnops_os.c b/module/os/freebsd/zfs/zfs_vnops_os.c index 812d8641b26b..b85dc6b0faf0 100644 --- a/module/os/freebsd/zfs/zfs_vnops_os.c +++ b/module/os/freebsd/zfs/zfs_vnops_os.c @@ -5576,56 +5576,28 @@ struct vop_setextattr { }; #endif -/* - * Vnode operation to set a named attribute. - */ static int -zfs_setextattr(struct vop_setextattr_args *ap) +zfs_setextattr_dir(struct vop_setextattr_args *ap, const char *attrname) { - zfsvfs_t *zfsvfs = VTOZ(ap->a_vp)->z_zfsvfs; struct thread *td = ap->a_td; struct nameidata nd; - char attrname[255]; struct vattr va; vnode_t *xvp = NULL, *vp; int error, flags; - /* - * 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, VWRITE); - if (error != 0) - return (error); - error = zfs_create_attrname(ap->a_attrnamespace, ap->a_name, attrname, - sizeof (attrname)); - if (error != 0) - return (error); - - ZFS_ENTER(zfsvfs); - error = zfs_lookup(ap->a_vp, NULL, &xvp, NULL, 0, ap->a_cred, td, LOOKUP_XATTR | CREATE_XATTR_DIR, B_FALSE); - if (error != 0) { - ZFS_EXIT(zfsvfs); + if (error != 0) return (error); - } flags = FFLAGS(O_WRONLY | O_CREAT); - NDINIT_ATVP(&nd, LOOKUP, NOFOLLOW, UIO_SYSSPACE, attrname, - xvp, td); + NDINIT_ATVP(&nd, LOOKUP, NOFOLLOW, UIO_SYSSPACE, attrname, xvp, td); error = vn_open_cred(&nd, &flags, 0600, VN_OPEN_INVFS, ap->a_cred, NULL); vp = nd.ni_vp; NDFREE(&nd, NDF_ONLY_PNBUF); - if (error != 0) { - ZFS_EXIT(zfsvfs); + if (error != 0) return (error); - } VATTR_NULL(&va); va.va_size = 0; @@ -5635,6 +5607,102 @@ zfs_setextattr(struct vop_setextattr_args *ap) VOP_UNLOCK1(vp); vn_close(vp, flags, ap->a_cred, td); + return (error); +} + +static int +zfs_setextattr_sa(struct vop_setextattr_args *ap, const char *attrname) +{ + znode_t *zp = VTOZ(ap->a_vp); + nvlist_t *nvl; + size_t sa_size; + int error; + + error = zfs_ensure_xattr_cached(zp); + if (error != 0) + return (error); + + ASSERT(RW_WRITE_HELD(&zp->z_xattr_lock)); + ASSERT3P(zp->z_xattr_cached, !=, NULL); + + nvl = zp->z_xattr_cached; + size_t entry_size = ap->a_uio->uio_resid; + if (entry_size > DXATTR_MAX_ENTRY_SIZE) + return (SET_ERROR(EFBIG)); + error = nvlist_size(nvl, &sa_size, NV_ENCODE_XDR); + if (error != 0) + return (error); + if (sa_size > DXATTR_MAX_SA_SIZE) + return (SET_ERROR(EFBIG)); + uchar_t *buf = kmem_alloc(entry_size, KM_SLEEP); + error = uiomove(buf, entry_size, ap->a_uio); + if (error == 0) + error = nvlist_add_byte_array(nvl, attrname, buf, entry_size); + kmem_free(buf, entry_size); + if (error == 0) + error = zfs_sa_set_xattr(zp); + if (error != 0) { + zp->z_xattr_cached = NULL; + nvlist_free(nvl); + } + return (error); +} + +/* + * Vnode operation to set a named attribute. + */ +static int +zfs_setextattr(struct vop_setextattr_args *ap) +{ + znode_t *zp = VTOZ(ap->a_vp); + zfsvfs_t *zfsvfs = ZTOZSB(zp); + char attrname[EXTATTR_MAXNAMELEN+1]; + int error; + + /* + * 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, VWRITE); + if (error != 0) + return (error); + + error = zfs_create_attrname(ap->a_attrnamespace, ap->a_name, attrname, + sizeof (attrname)); + if (error != 0) + return (error); + + struct vop_deleteextattr_args vda = { + .a_vp = ap->a_vp, + .a_cred = ap->a_cred, + .a_td = ap->a_td, + }; + error = ENOENT; + ZFS_ENTER(zfsvfs); + ZFS_VERIFY_ZP(zp); + rw_enter(&zp->z_xattr_lock, RW_WRITER); + if (zfsvfs->z_use_sa && zp->z_is_sa && zfsvfs->z_xattr_sa) { + error = zfs_setextattr_sa(ap, attrname); + if (error == 0) + /* + * Successfully put into SA, we need to clear the one + * in dir if present. + */ + zfs_deleteextattr_dir(&vda, attrname); + } + if (error) { + error = zfs_setextattr_dir(ap, attrname); + if (error == 0) + /* + * Successfully put into dir, we need to clear the one + * in SA if present. + */ + zfs_deleteextattr_sa(&vda, attrname); + } + rw_exit(&zp->z_xattr_lock); ZFS_EXIT(zfsvfs); return (error); }