diff --git a/include/sys/zfs_acl.h b/include/sys/zfs_acl.h index e19288528849..a137393548d8 100644 --- a/include/sys/zfs_acl.h +++ b/include/sys/zfs_acl.h @@ -211,6 +211,8 @@ void zfs_acl_ids_free(zfs_acl_ids_t *); boolean_t zfs_acl_ids_overquota(struct zfsvfs *, zfs_acl_ids_t *, uint64_t); int zfs_getacl(struct znode *, vsecattr_t *, boolean_t, cred_t *); int zfs_setacl(struct znode *, vsecattr_t *, boolean_t, cred_t *); +int zfs_stripacl(struct znode *, cred_t *); + void zfs_acl_rele(void *); void zfs_oldace_byteswap(ace_t *, int); void zfs_ace_byteswap(void *, size_t, boolean_t); diff --git a/module/os/freebsd/zfs/zfs_acl.c b/module/os/freebsd/zfs/zfs_acl.c index 20466aeaaa05..fb47013d06ef 100644 --- a/module/os/freebsd/zfs/zfs_acl.c +++ b/module/os/freebsd/zfs/zfs_acl.c @@ -2029,6 +2029,12 @@ zfs_setacl(znode_t *zp, vsecattr_t *vsecp, boolean_t skipaclchk, cred_t *cr) return (error); } +int +zfs_stripacl(znode_t *zp, cred_t *cr) +{ + return (SET_ERROR(EOPNOTSUPP)); +} + /* * Check accesses of interest (AoI) against attributes of the dataset * such as read-only. Returns zero if no AoI conflict with dataset diff --git a/module/os/linux/zfs/zfs_acl.c b/module/os/linux/zfs/zfs_acl.c index a1fd3c9856cc..9be13d50c1e7 100644 --- a/module/os/linux/zfs/zfs_acl.c +++ b/module/os/linux/zfs/zfs_acl.c @@ -46,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -1963,8 +1964,8 @@ zfs_acl_ids_overquota(zfsvfs_t *zv, zfs_acl_ids_t *acl_ids, uint64_t projid) /* * Retrieve a file's ACL */ -int -zfs_getacl(znode_t *zp, vsecattr_t *vsecp, boolean_t skipaclchk, cred_t *cr) +static int +zfs_getacl_impl(znode_t *zp, vsecattr_t *vsecp, boolean_t stripped, cred_t *cr) { zfs_acl_t *aclp; ulong_t mask; @@ -1975,21 +1976,21 @@ zfs_getacl(znode_t *zp, vsecattr_t *vsecp, boolean_t skipaclchk, cred_t *cr) mask = vsecp->vsa_mask & (VSA_ACE | VSA_ACECNT | VSA_ACE_ACLFLAGS | VSA_ACE_ALLTYPES); - if (mask == 0) - return (SET_ERROR(ENOSYS)); - - if ((error = zfs_zaccess(zp, ACE_READ_ACL, 0, skipaclchk, cr, - zfs_init_idmap))) - return (error); - - mutex_enter(&zp->z_acl_lock); - - error = zfs_acl_node_read(zp, B_FALSE, &aclp, B_FALSE); - if (error != 0) { - mutex_exit(&zp->z_acl_lock); - return (error); + if (stripped) { + mode_t mode = ZTOI(zp)->i_mode; + aclp = zfs_acl_alloc(zfs_acl_version_zp(zp)); + (aclp)->z_hints = zp->z_pflags & V4_ACL_WIDE_FLAGS; + zfs_acl_chmod(S_ISDIR(mode), mode, B_TRUE, + (ZTOZSB(zp)->z_acl_mode == ZFS_ACL_GROUPMASK), aclp); + } else { + error = zfs_acl_node_read(zp, B_FALSE, &aclp, B_FALSE); + if (error != 0) + return (error); } + mask = vsecp->vsa_mask & (VSA_ACE | VSA_ACECNT | + VSA_ACE_ACLFLAGS | VSA_ACE_ALLTYPES); + /* * Scan ACL to determine number of ACEs */ @@ -2038,7 +2039,7 @@ zfs_getacl(znode_t *zp, vsecattr_t *vsecp, boolean_t skipaclchk, cred_t *cr) for (aclnode = list_head(&aclp->z_acl); aclnode; aclnode = list_next(&aclp->z_acl, aclnode)) { - memcpy(start, aclnode->z_acldata, + bcopy(aclnode->z_acldata, start, aclnode->z_size); start = (caddr_t)start + aclnode->z_size; } @@ -2056,9 +2057,29 @@ zfs_getacl(znode_t *zp, vsecattr_t *vsecp, boolean_t skipaclchk, cred_t *cr) vsecp->vsa_aclflags |= ACL_AUTO_INHERIT; } + return (0); +} + +int +zfs_getacl(znode_t *zp, vsecattr_t *vsecp, boolean_t skipaclchk, cred_t *cr) +{ + int error; + ulong_t mask; + + mask = vsecp->vsa_mask & (VSA_ACE | VSA_ACECNT | + VSA_ACE_ACLFLAGS | VSA_ACE_ALLTYPES); + + if (mask == 0) + return (SET_ERROR(ENOSYS)); + + if ((error = zfs_zaccess(zp, ACE_READ_ACL, 0, skipaclchk, cr))) + return (error); + + mutex_enter(&zp->z_acl_lock); + error = zfs_getacl_impl(zp, vsecp, B_FALSE, cr); mutex_exit(&zp->z_acl_lock); - return (0); + return (error); } int @@ -2119,12 +2140,11 @@ zfs_vsec_2_aclp(zfsvfs_t *zfsvfs, umode_t obj_mode, /* * Set a file's ACL */ -int -zfs_setacl(znode_t *zp, vsecattr_t *vsecp, boolean_t skipaclchk, cred_t *cr) +static int +zfs_setacl_impl(znode_t *zp, vsecattr_t *vsecp, cred_t *cr) { zfsvfs_t *zfsvfs = ZTOZSB(zp); zilog_t *zilog = zfsvfs->z_log; - ulong_t mask = vsecp->vsa_mask & (VSA_ACE | VSA_ACECNT); dmu_tx_t *tx; int error; zfs_acl_t *aclp; @@ -2132,16 +2152,6 @@ zfs_setacl(znode_t *zp, vsecattr_t *vsecp, boolean_t skipaclchk, cred_t *cr) boolean_t fuid_dirtied; uint64_t acl_obj; - if (mask == 0) - return (SET_ERROR(ENOSYS)); - - if (zp->z_pflags & ZFS_IMMUTABLE) - return (SET_ERROR(EPERM)); - - if ((error = zfs_zaccess(zp, ACE_WRITE_ACL, 0, skipaclchk, cr, - zfs_init_idmap))) - return (error); - error = zfs_vsec_2_aclp(zfsvfs, ZTOI(zp)->i_mode, vsecp, cr, &fuidp, &aclp); if (error) @@ -2156,9 +2166,6 @@ zfs_setacl(znode_t *zp, vsecattr_t *vsecp, boolean_t skipaclchk, cred_t *cr) (zp->z_pflags & V4_ACL_WIDE_FLAGS); } top: - mutex_enter(&zp->z_acl_lock); - mutex_enter(&zp->z_lock); - tx = dmu_tx_create(zfsvfs->z_os); dmu_tx_hold_sa(tx, zp->z_sa_hdl, B_TRUE); @@ -2189,12 +2196,15 @@ zfs_setacl(znode_t *zp, vsecattr_t *vsecp, boolean_t skipaclchk, cred_t *cr) zfs_sa_upgrade_txholds(tx, zp); error = dmu_tx_assign(tx, TXG_NOWAIT); if (error) { - mutex_exit(&zp->z_acl_lock); - mutex_exit(&zp->z_lock); - if (error == ERESTART) { + mutex_exit(&zp->z_acl_lock); + mutex_exit(&zp->z_lock); + dmu_tx_wait(tx); dmu_tx_abort(tx); + + mutex_enter(&zp->z_acl_lock); + mutex_enter(&zp->z_lock); goto top; } dmu_tx_abort(tx); @@ -2216,9 +2226,84 @@ zfs_setacl(znode_t *zp, vsecattr_t *vsecp, boolean_t skipaclchk, cred_t *cr) zfs_fuid_info_free(fuidp); dmu_tx_commit(tx); + return (error); +} + +int +zfs_setacl(znode_t *zp, vsecattr_t *vsecp, boolean_t skipaclchk, cred_t *cr) +{ + ulong_t mask = vsecp->vsa_mask & (VSA_ACE | VSA_ACECNT); + int error; + + if (mask == 0) + return (SET_ERROR(ENOSYS)); + + if (zp->z_pflags & ZFS_IMMUTABLE) + return (SET_ERROR(EPERM)); + + if ((error = zfs_zaccess(zp, ACE_WRITE_ACL, 0, skipaclchk, cr))) + return (error); + + mutex_enter(&zp->z_acl_lock); + mutex_enter(&zp->z_lock); + + error = zfs_setacl_impl(zp, vsecp, cr); + mutex_exit(&zp->z_lock); mutex_exit(&zp->z_acl_lock); + return (error); +} + +int +zfs_stripacl(znode_t *zp, cred_t *cr) +{ + int error; + zfsvfs_t *zfsvfs = ZTOZSB(zp); + + vsecattr_t vsec = { + .vsa_mask = VSA_ACE_ALLTYPES | VSA_ACECNT | VSA_ACE | + VSA_ACE_ACLFLAGS + }; + + ZFS_ENTER(zfsvfs); + ZFS_VERIFY_ZP(zp); + + if (zp->z_pflags & ZFS_IMMUTABLE) { + error = SET_ERROR(EPERM); + goto done; + } + + if ((error = zfs_zaccess(zp, ACE_WRITE_ACL, 0, B_FALSE, cr))) + goto done; + + if (zp->z_pflags & ZFS_ACL_TRIVIAL) { + // ACL is already stripped. Nothing to do. + error = 0; + goto done; + } + + mutex_enter(&zp->z_acl_lock); + error = zfs_getacl_impl(zp, &vsec, B_TRUE, cr); + if (error) { + mutex_exit(&zp->z_acl_lock); + goto done; + } + + mutex_enter(&zp->z_lock); + + error = zfs_setacl_impl(zp, &vsec, cr); + + mutex_exit(&zp->z_lock); + mutex_exit(&zp->z_acl_lock); + + kmem_free(vsec.vsa_aclentp, vsec.vsa_aclentsz); + + if (zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS) + zil_commit(zfsvfs->z_log, 0); + +done: + ZFS_EXIT(zfsvfs); return (error); } diff --git a/module/os/linux/zfs/zpl_xattr.c b/module/os/linux/zfs/zpl_xattr.c index 11f07dbb5b9a..6528874c57e6 100644 --- a/module/os/linux/zfs/zpl_xattr.c +++ b/module/os/linux/zfs/zpl_xattr.c @@ -1859,12 +1859,12 @@ __zpl_xattr_nfs41acl_set(struct inode *ip, const char *name, if (ITOZSB(ip)->z_acl_type != ZFS_ACLTYPE_NFSV4) return (-EOPNOTSUPP); - /* - * TODO: we may receive NULL value and size 0 - * when rmxattr() on our special xattr is called. - * A function to "strip" the ACL needs to be added - * to avoid POLA violation. - */ + if (value == NULL && size == 0) { + crhold(cr); + error = zfs_stripacl(ITOZ(ip), cr); + crfree(cr); + return (error); + } /* xdr data is 4-byte aligned */ if (((ulong_t)value % 4) != 0) {