Rebase master to b117
[zfs.git] / module / zfs / zfs_acl.c
index fdf92a1..734bd83 100644 (file)
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
     ACE_WRITE_ATTRIBUTES|ACE_WRITE_NAMED_ATTRS)
 #define        OWNER_ALLOW_MASK (ACE_WRITE_ACL | ACE_WRITE_OWNER | \
     ACE_WRITE_ATTRIBUTES|ACE_WRITE_NAMED_ATTRS)
-#define        WRITE_MASK_DATA (ACE_WRITE_DATA|ACE_APPEND_DATA|ACE_WRITE_NAMED_ATTRS)
 
 #define        ZFS_CHECKED_MASKS (ACE_READ_ACL|ACE_READ_ATTRIBUTES|ACE_READ_DATA| \
     ACE_READ_NAMED_ATTRS|ACE_WRITE_DATA|ACE_WRITE_ATTRIBUTES| \
     ACE_WRITE_NAMED_ATTRS|ACE_APPEND_DATA|ACE_EXECUTE|ACE_WRITE_OWNER| \
     ACE_WRITE_ACL|ACE_DELETE|ACE_DELETE_CHILD|ACE_SYNCHRONIZE)
 
-#define        WRITE_MASK (WRITE_MASK_DATA|ACE_WRITE_ATTRIBUTES|ACE_WRITE_ACL|\
-    ACE_WRITE_OWNER|ACE_DELETE|ACE_DELETE_CHILD)
+#define        WRITE_MASK_DATA (ACE_WRITE_DATA|ACE_APPEND_DATA|ACE_WRITE_NAMED_ATTRS)
+#define        WRITE_MASK_ATTRS (ACE_WRITE_ACL|ACE_WRITE_OWNER|ACE_WRITE_ATTRIBUTES| \
+    ACE_DELETE|ACE_DELETE_CHILD)
+#define        WRITE_MASK (WRITE_MASK_DATA|WRITE_MASK_ATTRS)
 
 #define        OGE_CLEAR       (ACE_READ_DATA|ACE_LIST_DIRECTORY|ACE_WRITE_DATA| \
     ACE_ADD_FILE|ACE_APPEND_DATA|ACE_ADD_SUBDIRECTORY|ACE_EXECUTE)
@@ -538,8 +539,9 @@ zfs_acl_curr_node(zfs_acl_t *aclp)
  * ACE FUIDs will be created later.
  */
 int
-zfs_copy_ace_2_fuid(vtype_t obj_type, zfs_acl_t *aclp, void *datap,
-    zfs_ace_t *z_acl, int aclcnt, size_t *size)
+zfs_copy_ace_2_fuid(zfsvfs_t *zfsvfs, vtype_t obj_type, zfs_acl_t *aclp,
+    void *datap, zfs_ace_t *z_acl, int aclcnt, size_t *size,
+    zfs_fuid_info_t **fuidp, cred_t *cr)
 {
        int i;
        uint16_t entry_type;
@@ -555,9 +557,9 @@ zfs_copy_ace_2_fuid(vtype_t obj_type, zfs_acl_t *aclp, void *datap,
                entry_type = aceptr->z_hdr.z_flags & ACE_TYPE_FLAGS;
                if (entry_type != ACE_OWNER && entry_type != OWNING_GROUP &&
                    entry_type != ACE_EVERYONE) {
-                       if (!aclp->z_has_fuids)
-                               aclp->z_has_fuids = IS_EPHEMERAL(acep->a_who);
-                       aceptr->z_fuid = (uint64_t)acep->a_who;
+                       aceptr->z_fuid = zfs_fuid_create(zfsvfs, acep->a_who,
+                           cr, (entry_type == 0) ?
+                           ZFS_ACE_USER : ZFS_ACE_GROUP, fuidp);
                }
 
                /*
@@ -682,7 +684,7 @@ zfs_copy_ace_2_oldace(vtype_t obj_type, zfs_acl_t *aclp, ace_t *acep,
  * convert old ACL format to new
  */
 void
-zfs_acl_xform(znode_t *zp, zfs_acl_t *aclp)
+zfs_acl_xform(znode_t *zp, zfs_acl_t *aclp, cred_t *cr)
 {
        zfs_oldace_t *oldaclp;
        int i;
@@ -714,9 +716,9 @@ zfs_acl_xform(znode_t *zp, zfs_acl_t *aclp)
        newaclnode = zfs_acl_node_alloc(aclp->z_acl_count *
            sizeof (zfs_object_ace_t));
        aclp->z_ops = zfs_acl_fuid_ops;
-       VERIFY(zfs_copy_ace_2_fuid(ZTOV(zp)->v_type, aclp, oldaclp,
-           newaclnode->z_acldata, aclp->z_acl_count,
-           &newaclnode->z_size) == 0);
+       VERIFY(zfs_copy_ace_2_fuid(zp->z_zfsvfs, ZTOV(zp)->v_type, aclp,
+           oldaclp, newaclnode->z_acldata, aclp->z_acl_count,
+           &newaclnode->z_size, NULL, cr) == 0);
        newaclnode->z_ace_count = aclp->z_acl_count;
        aclp->z_version = ZFS_ACL_VERSION;
        kmem_free(oldaclp, aclp->z_acl_count * sizeof (zfs_oldace_t));
@@ -770,8 +772,7 @@ zfs_set_ace(zfs_acl_t *aclp, void *acep, uint32_t access_mask,
  * Also, create FUIDs for any User/Group ACEs
  */
 static uint64_t
-zfs_mode_fuid_compute(znode_t *zp, zfs_acl_t *aclp, cred_t *cr,
-    zfs_fuid_info_t **fuidp, dmu_tx_t *tx)
+zfs_mode_compute(znode_t *zp, zfs_acl_t *aclp)
 {
        int             entry_type;
        mode_t          mode;
@@ -905,15 +906,6 @@ zfs_mode_fuid_compute(znode_t *zp, zfs_acl_t *aclp, cred_t *cr,
                                }
                        }
                }
-               /*
-                * Now handle FUID create for user/group ACEs
-                */
-               if (entry_type == 0 || entry_type == ACE_IDENTIFIER_GROUP) {
-                       aclp->z_ops.ace_who_set(acep,
-                           zfs_fuid_create(zp->z_zfsvfs, who, cr,
-                           (entry_type == 0) ? ZFS_ACE_USER : ZFS_ACE_GROUP,
-                           tx, fuidp));
-               }
        }
        return (mode);
 }
@@ -989,7 +981,7 @@ zfs_acl_node_read(znode_t *zp, zfs_acl_t **aclpp, boolean_t will_modify)
        aclnode = zfs_acl_node_alloc(aclsize);
        list_insert_head(&aclp->z_acl, aclnode);
        error = dmu_read(zp->z_zfsvfs->z_os, extacl, 0,
-           aclsize, aclnode->z_acldata);
+           aclsize, aclnode->z_acldata, DMU_READ_PREFETCH);
        aclnode->z_ace_count = acl_count;
        aclp->z_acl_count = acl_count;
        aclp->z_acl_bytes = aclsize;
@@ -1014,8 +1006,7 @@ zfs_acl_node_read(znode_t *zp, zfs_acl_t **aclpp, boolean_t will_modify)
  * already checked the acl and knows whether to inherit.
  */
 int
-zfs_aclset_common(znode_t *zp, zfs_acl_t *aclp, cred_t *cr,
-    zfs_fuid_info_t **fuidp, dmu_tx_t *tx)
+zfs_aclset_common(znode_t *zp, zfs_acl_t *aclp, cred_t *cr, dmu_tx_t *tx)
 {
        int             error;
        znode_phys_t    *zphys = zp->z_phys;
@@ -1026,12 +1017,9 @@ zfs_aclset_common(znode_t *zp, zfs_acl_t *aclp, cred_t *cr,
        dmu_object_type_t otype;
        zfs_acl_node_t  *aclnode;
 
-       ASSERT(MUTEX_HELD(&zp->z_lock));
-       ASSERT(MUTEX_HELD(&zp->z_acl_lock));
-
        dmu_buf_will_dirty(zp->z_dbuf, tx);
 
-       zphys->zp_mode = zfs_mode_fuid_compute(zp, aclp, cr, fuidp, tx);
+       zphys->zp_mode = zfs_mode_compute(zp, aclp);
 
        /*
         * Decide which opbject type to use.  If we are forced to
@@ -1043,7 +1031,7 @@ zfs_aclset_common(znode_t *zp, zfs_acl_t *aclp, cred_t *cr,
        } else {
                if ((aclp->z_version == ZFS_ACL_VERSION_INITIAL) &&
                    (zfsvfs->z_version >= ZPL_VERSION_FUID))
-                       zfs_acl_xform(zp, aclp);
+                       zfs_acl_xform(zp, aclp, cr);
                ASSERT(aclp->z_version >= ZFS_ACL_VERSION_FUID);
                otype = DMU_OT_ACL;
        }
@@ -1125,7 +1113,6 @@ zfs_aclset_common(znode_t *zp, zfs_acl_t *aclp, cred_t *cr,
        if (ace_trivial_common(aclp, 0, zfs_ace_walk) == 0)
                zp->z_phys->zp_flags |= ZFS_ACL_TRIVIAL;
 
-       zfs_time_stamper_locked(zp, STATE_CHANGED, tx);
        return (0);
 }
 
@@ -1336,7 +1323,7 @@ zfs_acl_ace_insert(zfs_acl_t *aclp, void  *acep)
  * Prepend deny ACE
  */
 static void *
-zfs_acl_prepend_deny(znode_t *zp, zfs_acl_t *aclp, void *acep,
+zfs_acl_prepend_deny(uint64_t uid, zfs_acl_t *aclp, void *acep,
     mode_t mode)
 {
        zfs_acl_node_t *aclnode;
@@ -1349,7 +1336,7 @@ zfs_acl_prepend_deny(znode_t *zp, zfs_acl_t *aclp, void *acep,
        fuid = aclp->z_ops.ace_who_get(acep);
        flags = aclp->z_ops.ace_flags_get(acep);
        zfs_set_ace(aclp, newacep, 0, DENY, fuid, (flags & ACE_TYPE_FLAGS));
-       zfs_acl_prepend_fixup(aclp, newacep, acep, mode, zp->z_phys->zp_uid);
+       zfs_acl_prepend_fixup(aclp, newacep, acep, mode, uid);
 
        return (newacep);
 }
@@ -1473,9 +1460,9 @@ zfs_fixup_group_entries(zfs_acl_t *aclp, void *acep, void *prevacep,
  * in PSARC/2002/240
  */
 static void
-zfs_acl_chmod(znode_t *zp, uint64_t mode, zfs_acl_t *aclp)
+zfs_acl_chmod(zfsvfs_t *zfsvfs, uint64_t uid,
+    uint64_t mode, zfs_acl_t *aclp)
 {
-       zfsvfs_t        *zfsvfs = zp->z_zfsvfs;
        void            *acep = NULL, *prevacep = NULL;
        uint64_t        who;
        int             i;
@@ -1485,11 +1472,6 @@ zfs_acl_chmod(znode_t *zp, uint64_t mode, zfs_acl_t *aclp)
        uint16_t        iflags, type;
        uint32_t        access_mask;
 
-       ASSERT(MUTEX_HELD(&zp->z_acl_lock));
-       ASSERT(MUTEX_HELD(&zp->z_lock));
-
-       aclp->z_hints = (zp->z_phys->zp_flags & V4_ACL_WIDE_FLAGS);
-
        /*
         * If discard then just discard all ACL nodes which
         * represent the ACEs.
@@ -1554,17 +1536,15 @@ zfs_acl_chmod(znode_t *zp, uint64_t mode, zfs_acl_t *aclp)
 
                                        if (!reuse_deny) {
                                                prevacep =
-                                                   zfs_acl_prepend_deny(zp,
+                                                   zfs_acl_prepend_deny(uid,
                                                    aclp, acep, mode);
                                        } else {
                                                zfs_acl_prepend_fixup(
                                                    aclp, prevacep,
-                                                   acep, mode,
-                                                   zp->z_phys->zp_uid);
+                                                   acep, mode, uid);
                                        }
                                        zfs_fixup_group_entries(aclp, acep,
                                            prevacep, mode);
-
                                }
                        }
                }
@@ -1623,8 +1603,10 @@ zfs_acl_chmod_setattr(znode_t *zp, zfs_acl_t **aclp, uint64_t mode)
        mutex_enter(&zp->z_acl_lock);
        *aclp = NULL;
        error = zfs_acl_node_read(zp, aclp, B_TRUE);
-       if (error == 0)
-               zfs_acl_chmod(zp, mode, *aclp);
+       if (error == 0) {
+               (*aclp)->z_hints = zp->z_phys->zp_flags & V4_ACL_WIDE_FLAGS;
+               zfs_acl_chmod(zp->z_zfsvfs, zp->z_phys->zp_uid, mode, *aclp);
+       }
        mutex_exit(&zp->z_acl_lock);
        mutex_exit(&zp->z_lock);
        return (error);
@@ -1649,9 +1631,8 @@ zfs_restricted_update(zfsvfs_t *zfsvfs, zfs_acl_t *aclp, void *acep)
  * Should ACE be inherited?
  */
 static int
-zfs_ace_can_use(znode_t *zp, uint16_t acep_flags)
+zfs_ace_can_use(vtype_t vtype, uint16_t acep_flags)
 {
-       int vtype = ZTOV(zp)->v_type;
        int     iflags = (acep_flags & 0xf);
 
        if ((vtype == VDIR) && (iflags & ACE_DIRECTORY_INHERIT_ACE))
@@ -1666,10 +1647,9 @@ zfs_ace_can_use(znode_t *zp, uint16_t acep_flags)
  * inherit inheritable ACEs from parent
  */
 static zfs_acl_t *
-zfs_acl_inherit(znode_t *zp, zfs_acl_t *paclp, uint64_t mode,
-    boolean_t *need_chmod)
+zfs_acl_inherit(zfsvfs_t *zfsvfs, vtype_t vtype, zfs_acl_t *paclp,
+    uint64_t mode, boolean_t *need_chmod)
 {
-       zfsvfs_t        *zfsvfs = zp->z_zfsvfs;
        void            *pacep;
        void            *acep, *acep2;
        zfs_acl_node_t  *aclnode, *aclnode2;
@@ -1680,8 +1660,8 @@ zfs_acl_inherit(znode_t *zp, zfs_acl_t *paclp, uint64_t mode,
        size_t          ace_size;
        void            *data1, *data2;
        size_t          data1sz, data2sz;
-       boolean_t       vdir = ZTOV(zp)->v_type == VDIR;
-       boolean_t       vreg = ZTOV(zp)->v_type == VREG;
+       boolean_t       vdir = vtype == VDIR;
+       boolean_t       vreg = vtype == VREG;
        boolean_t       passthrough, passthrough_x, noallow;
 
        passthrough_x =
@@ -1710,7 +1690,7 @@ zfs_acl_inherit(znode_t *zp, zfs_acl_t *paclp, uint64_t mode,
 
                ace_size = aclp->z_ops.ace_size(pacep);
 
-               if (!zfs_ace_can_use(zp, iflags))
+               if (!zfs_ace_can_use(vtype, iflags))
                        continue;
 
                /*
@@ -1806,55 +1786,58 @@ zfs_acl_inherit(znode_t *zp, zfs_acl_t *paclp, uint64_t mode,
  * Create file system object initial permissions
  * including inheritable ACEs.
  */
-void
-zfs_perm_init(znode_t *zp, znode_t *parent, int flag,
-    vattr_t *vap, dmu_tx_t *tx, cred_t *cr,
-    zfs_acl_t *setaclp, zfs_fuid_info_t **fuidp)
+int
+zfs_acl_ids_create(znode_t *dzp, int flag, vattr_t *vap, cred_t *cr,
+    vsecattr_t *vsecp, zfs_acl_ids_t *acl_ids)
 {
-       uint64_t        mode, fuid, fgid;
        int             error;
-       zfsvfs_t        *zfsvfs = zp->z_zfsvfs;
-       zfs_acl_t       *aclp = NULL;
+       zfsvfs_t        *zfsvfs = dzp->z_zfsvfs;
        zfs_acl_t       *paclp;
-       xvattr_t        *xvap = (xvattr_t *)vap;
        gid_t           gid;
        boolean_t       need_chmod = B_TRUE;
 
-       if (setaclp)
-               aclp = setaclp;
+       bzero(acl_ids, sizeof (zfs_acl_ids_t));
+       acl_ids->z_mode = MAKEIMODE(vap->va_type, vap->va_mode);
 
-       mode = MAKEIMODE(vap->va_type, vap->va_mode);
+       if (vsecp)
+               if ((error = zfs_vsec_2_aclp(zfsvfs, vap->va_type, vsecp, cr,
+                   &acl_ids->z_fuidp, &acl_ids->z_aclp)) != 0)
+                       return (error);
 
        /*
         * Determine uid and gid.
         */
        if ((flag & (IS_ROOT_NODE | IS_REPLAY)) ||
            ((flag & IS_XATTR) && (vap->va_type == VDIR))) {
-               fuid = zfs_fuid_create(zfsvfs, vap->va_uid, cr,
-                   ZFS_OWNER, tx, fuidp);
-               fgid = zfs_fuid_create(zfsvfs, vap->va_gid, cr,
-                   ZFS_GROUP, tx, fuidp);
+               acl_ids->z_fuid = zfs_fuid_create(zfsvfs,
+                   (uint64_t)vap->va_uid, cr,
+                   ZFS_OWNER, &acl_ids->z_fuidp);
+               acl_ids->z_fgid = zfs_fuid_create(zfsvfs,
+                   (uint64_t)vap->va_gid, cr,
+                   ZFS_GROUP, &acl_ids->z_fuidp);
                gid = vap->va_gid;
        } else {
-               fuid = zfs_fuid_create_cred(zfsvfs, ZFS_OWNER, tx, cr, fuidp);
-               fgid = 0;
+               acl_ids->z_fuid = zfs_fuid_create_cred(zfsvfs, ZFS_OWNER,
+                   cr, &acl_ids->z_fuidp);
+               acl_ids->z_fgid = 0;
                if (vap->va_mask & AT_GID)  {
-                       fgid = zfs_fuid_create(zfsvfs, vap->va_gid, cr,
-                           ZFS_GROUP, tx, fuidp);
+                       acl_ids->z_fgid = zfs_fuid_create(zfsvfs,
+                           (uint64_t)vap->va_gid,
+                           cr, ZFS_GROUP, &acl_ids->z_fuidp);
                        gid = vap->va_gid;
-                       if (fgid != parent->z_phys->zp_gid &&
+                       if (acl_ids->z_fgid != dzp->z_phys->zp_gid &&
                            !groupmember(vap->va_gid, cr) &&
                            secpolicy_vnode_create_gid(cr) != 0)
-                               fgid = 0;
+                               acl_ids->z_fgid = 0;
                }
-               if (fgid == 0) {
-                       if (parent->z_phys->zp_mode & S_ISGID) {
-                               fgid = parent->z_phys->zp_gid;
-                               gid = zfs_fuid_map_id(zfsvfs, fgid,
+               if (acl_ids->z_fgid == 0) {
+                       if (dzp->z_phys->zp_mode & S_ISGID) {
+                               acl_ids->z_fgid = dzp->z_phys->zp_gid;
+                               gid = zfs_fuid_map_id(zfsvfs, acl_ids->z_fgid,
                                    cr, ZFS_GROUP);
                        } else {
-                               fgid = zfs_fuid_create_cred(zfsvfs,
-                                   ZFS_GROUP, tx, cr, fuidp);
+                               acl_ids->z_fgid = zfs_fuid_create_cred(zfsvfs,
+                                   ZFS_GROUP, cr, &acl_ids->z_fuidp);
                                gid = crgetgid(cr);
                        }
                }
@@ -1867,57 +1850,61 @@ zfs_perm_init(znode_t *zp, znode_t *parent, int flag,
         * file's new group, clear the file's set-GID bit.
         */
 
-       if ((parent->z_phys->zp_mode & S_ISGID) && (vap->va_type == VDIR)) {
-               mode |= S_ISGID;
+       if (!(flag & IS_ROOT_NODE) && (dzp->z_phys->zp_mode & S_ISGID) &&
+           (vap->va_type == VDIR)) {
+               acl_ids->z_mode |= S_ISGID;
        } else {
-               if ((mode & S_ISGID) &&
+               if ((acl_ids->z_mode & S_ISGID) &&
                    secpolicy_vnode_setids_setgids(cr, gid) != 0)
-                       mode &= ~S_ISGID;
-       }
-
-       zp->z_phys->zp_uid = fuid;
-       zp->z_phys->zp_gid = fgid;
-       zp->z_phys->zp_mode = mode;
-
-       if (aclp == NULL) {
-               mutex_enter(&parent->z_lock);
-               if ((ZTOV(parent)->v_type == VDIR &&
-                   (parent->z_phys->zp_flags & ZFS_INHERIT_ACE)) &&
-                   !(zp->z_phys->zp_flags & ZFS_XATTR)) {
-                       mutex_enter(&parent->z_acl_lock);
-                       VERIFY(0 == zfs_acl_node_read(parent, &paclp, B_FALSE));
-                       mutex_exit(&parent->z_acl_lock);
-                       aclp = zfs_acl_inherit(zp, paclp, mode, &need_chmod);
+                       acl_ids->z_mode &= ~S_ISGID;
+       }
+
+       if (acl_ids->z_aclp == NULL) {
+               mutex_enter(&dzp->z_lock);
+               if (!(flag & IS_ROOT_NODE) && (ZTOV(dzp)->v_type == VDIR &&
+                   (dzp->z_phys->zp_flags & ZFS_INHERIT_ACE)) &&
+                   !(dzp->z_phys->zp_flags & ZFS_XATTR)) {
+                       mutex_enter(&dzp->z_acl_lock);
+                       VERIFY(0 == zfs_acl_node_read(dzp, &paclp, B_FALSE));
+                       mutex_exit(&dzp->z_acl_lock);
+                       acl_ids->z_aclp = zfs_acl_inherit(zfsvfs,
+                           vap->va_type, paclp, acl_ids->z_mode, &need_chmod);
                        zfs_acl_free(paclp);
                } else {
-                       aclp = zfs_acl_alloc(zfs_acl_version_zp(zp));
+                       acl_ids->z_aclp =
+                           zfs_acl_alloc(zfs_acl_version_zp(dzp));
+               }
+               mutex_exit(&dzp->z_lock);
+               if (need_chmod) {
+                       acl_ids->z_aclp->z_hints = (vap->va_type == VDIR) ?
+                           ZFS_ACL_AUTO_INHERIT : 0;
+                       zfs_acl_chmod(zfsvfs, acl_ids->z_fuid,
+                           acl_ids->z_mode, acl_ids->z_aclp);
                }
-               mutex_exit(&parent->z_lock);
-               mutex_enter(&zp->z_lock);
-               mutex_enter(&zp->z_acl_lock);
-               if (need_chmod)
-                       zfs_acl_chmod(zp, mode, aclp);
-       } else {
-               mutex_enter(&zp->z_lock);
-               mutex_enter(&zp->z_acl_lock);
        }
 
-       /* Force auto_inherit on all new directory objects */
-       if (vap->va_type == VDIR)
-               aclp->z_hints |= ZFS_ACL_AUTO_INHERIT;
-
-       error = zfs_aclset_common(zp, aclp, cr, fuidp, tx);
-
-       /* Set optional attributes if any */
-       if (vap->va_mask & AT_XVATTR)
-               zfs_xvattr_set(zp, xvap);
+       return (0);
+}
 
-       mutex_exit(&zp->z_lock);
-       mutex_exit(&zp->z_acl_lock);
-       ASSERT3U(error, ==, 0);
+/*
+ * Free ACL and fuid_infop, but not the acl_ids structure
+ */
+void
+zfs_acl_ids_free(zfs_acl_ids_t *acl_ids)
+{
+       if (acl_ids->z_aclp)
+               zfs_acl_free(acl_ids->z_aclp);
+       if (acl_ids->z_fuidp)
+               zfs_fuid_info_free(acl_ids->z_fuidp);
+       acl_ids->z_aclp = NULL;
+       acl_ids->z_fuidp = NULL;
+}
 
-       if (aclp != setaclp)
-               zfs_acl_free(aclp);
+boolean_t
+zfs_acl_ids_overquota(zfsvfs_t *zfsvfs, zfs_acl_ids_t *acl_ids)
+{
+       return (zfs_usergroup_overquota(zfsvfs, B_FALSE, acl_ids->z_fuid) ||
+           zfs_usergroup_overquota(zfsvfs, B_TRUE, acl_ids->z_fgid));
 }
 
 /*
@@ -2018,7 +2005,7 @@ zfs_getacl(znode_t *zp, vsecattr_t *vsecp, boolean_t skipaclchk, cred_t *cr)
 
 int
 zfs_vsec_2_aclp(zfsvfs_t *zfsvfs, vtype_t obj_type,
-    vsecattr_t *vsecp, zfs_acl_t **zaclp)
+    vsecattr_t *vsecp, cred_t *cr, zfs_fuid_info_t **fuidp, zfs_acl_t **zaclp)
 {
        zfs_acl_t *aclp;
        zfs_acl_node_t *aclnode;
@@ -2041,9 +2028,9 @@ zfs_vsec_2_aclp(zfsvfs_t *zfsvfs, vtype_t obj_type,
                        return (error);
                }
        } else {
-               if ((error = zfs_copy_ace_2_fuid(obj_type, aclp,
+               if ((error = zfs_copy_ace_2_fuid(zfsvfs, obj_type, aclp,
                    vsecp->vsa_aclentp, aclnode->z_acldata, aclcnt,
-                   &aclnode->z_size)) != 0) {
+                   &aclnode->z_size, fuidp, cr)) != 0) {
                        zfs_acl_free(aclp);
                        zfs_acl_node_free(aclnode);
                        return (error);
@@ -2084,6 +2071,7 @@ zfs_setacl(znode_t *zp, vsecattr_t *vsecp, boolean_t skipaclchk, cred_t *cr)
        int             error;
        zfs_acl_t       *aclp;
        zfs_fuid_info_t *fuidp = NULL;
+       boolean_t       fuid_dirtied;
 
        if (mask == 0)
                return (ENOSYS);
@@ -2094,7 +2082,8 @@ zfs_setacl(znode_t *zp, vsecattr_t *vsecp, boolean_t skipaclchk, cred_t *cr)
        if (error = zfs_zaccess(zp, ACE_WRITE_ACL, 0, skipaclchk, cr))
                return (error);
 
-       error = zfs_vsec_2_aclp(zfsvfs, ZTOV(zp)->v_type, vsecp, &aclp);
+       error = zfs_vsec_2_aclp(zfsvfs, ZTOV(zp)->v_type, vsecp, cr, &fuidp,
+           &aclp);
        if (error)
                return (error);
 
@@ -2135,18 +2124,9 @@ top:
        } else if (aclp->z_acl_bytes > ZFS_ACE_SPACE) {
                dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0, aclp->z_acl_bytes);
        }
-       if (aclp->z_has_fuids) {
-               if (zfsvfs->z_fuid_obj == 0) {
-                       dmu_tx_hold_bonus(tx, DMU_NEW_OBJECT);
-                       dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0,
-                           FUID_SIZE_ESTIMATE(zfsvfs));
-                       dmu_tx_hold_zap(tx, MASTER_NODE_OBJ, FALSE, NULL);
-               } else {
-                       dmu_tx_hold_bonus(tx, zfsvfs->z_fuid_obj);
-                       dmu_tx_hold_write(tx, zfsvfs->z_fuid_obj, 0,
-                           FUID_SIZE_ESTIMATE(zfsvfs));
-               }
-       }
+       fuid_dirtied = zfsvfs->z_fuid_dirty;
+       if (fuid_dirtied)
+               zfs_fuid_txhold(zfsvfs, tx);
 
        error = dmu_tx_assign(tx, TXG_NOWAIT);
        if (error) {
@@ -2163,9 +2143,13 @@ top:
                return (error);
        }
 
-       error = zfs_aclset_common(zp, aclp, cr, &fuidp, tx);
+       error = zfs_aclset_common(zp, aclp, cr, tx);
        ASSERT(error == 0);
 
+       if (fuid_dirtied)
+               zfs_fuid_sync(zfsvfs, tx);
+
+       zfs_time_stamper_locked(zp, STATE_CHANGED, tx);
        zfs_log_acl(zilog, tx, zp, vsecp, fuidp);
 
        if (fuidp)
@@ -2180,45 +2164,17 @@ done:
 }
 
 /*
- * working_mode returns the permissions that were not granted
+ * Check accesses of interest (AoI) against attributes of the dataset
+ * such as read-only.  Returns zero if no AoI conflict with dataset
+ * attributes, otherwise an appropriate errno is returned.
  */
 static int
-zfs_zaccess_common(znode_t *zp, uint32_t v4_mode, uint32_t *working_mode,
-    boolean_t *check_privs, boolean_t skipaclchk, cred_t *cr)
+zfs_zaccess_dataset_check(znode_t *zp, uint32_t v4_mode)
 {
-       zfs_acl_t       *aclp;
-       zfsvfs_t        *zfsvfs = zp->z_zfsvfs;
-       int             error;
-       uid_t           uid = crgetuid(cr);
-       uint64_t        who;
-       uint16_t        type, iflags;
-       uint16_t        entry_type;
-       uint32_t        access_mask;
-       uint32_t        deny_mask = 0;
-       zfs_ace_hdr_t   *acep = NULL;
-       boolean_t       checkit;
-       uid_t           fowner;
-       uid_t           gowner;
-
-       /*
-        * Short circuit empty requests
-        */
-       if (v4_mode == 0)
-               return (0);
-
-       *check_privs = B_TRUE;
-
-       if (zfsvfs->z_replay) {
-               *working_mode = 0;
-               return (0);
-       }
-
-       *working_mode = v4_mode;
-
        if ((v4_mode & WRITE_MASK) &&
            (zp->z_zfsvfs->z_vfs->vfs_flag & VFS_RDONLY) &&
-           (!IS_DEVVP(ZTOV(zp)))) {
-               *check_privs = B_FALSE;
+           (!IS_DEVVP(ZTOV(zp)) ||
+           (IS_DEVVP(ZTOV(zp)) && (v4_mode & WRITE_MASK_ATTRS)))) {
                return (EROFS);
        }
 
@@ -2230,31 +2186,64 @@ zfs_zaccess_common(znode_t *zp, uint32_t v4_mode, uint32_t *working_mode,
            (zp->z_phys->zp_flags & (ZFS_READONLY | ZFS_IMMUTABLE))) ||
            (ZTOV(zp)->v_type == VDIR &&
            (zp->z_phys->zp_flags & ZFS_IMMUTABLE)))) {
-               *check_privs = B_FALSE;
                return (EPERM);
        }
 
        if ((v4_mode & (ACE_DELETE | ACE_DELETE_CHILD)) &&
            (zp->z_phys->zp_flags & ZFS_NOUNLINK)) {
-               *check_privs = B_FALSE;
                return (EPERM);
        }
 
        if (((v4_mode & (ACE_READ_DATA|ACE_EXECUTE)) &&
            (zp->z_phys->zp_flags & ZFS_AV_QUARANTINED))) {
-               *check_privs = B_FALSE;
                return (EACCES);
        }
 
-       /*
-        * The caller requested that the ACL check be skipped.  This
-        * would only happen if the caller checked VOP_ACCESS() with a
-        * 32 bit ACE mask and already had the appropriate permissions.
-        */
-       if (skipaclchk) {
-               *working_mode = 0;
-               return (0);
-       }
+       return (0);
+}
+
+/*
+ * The primary usage of this function is to loop through all of the
+ * ACEs in the znode, determining what accesses of interest (AoI) to
+ * the caller are allowed or denied.  The AoI are expressed as bits in
+ * the working_mode parameter.  As each ACE is processed, bits covered
+ * by that ACE are removed from the working_mode.  This removal
+ * facilitates two things.  The first is that when the working mode is
+ * empty (= 0), we know we've looked at all the AoI. The second is
+ * that the ACE interpretation rules don't allow a later ACE to undo
+ * something granted or denied by an earlier ACE.  Removing the
+ * discovered access or denial enforces this rule.  At the end of
+ * processing the ACEs, all AoI that were found to be denied are
+ * placed into the working_mode, giving the caller a mask of denied
+ * accesses.  Returns:
+ *     0               if all AoI granted
+ *     EACCESS         if the denied mask is non-zero
+ *     other error     if abnormal failure (e.g., IO error)
+ *
+ * A secondary usage of the function is to determine if any of the
+ * AoI are granted.  If an ACE grants any access in
+ * the working_mode, we immediately short circuit out of the function.
+ * This mode is chosen by setting anyaccess to B_TRUE.  The
+ * working_mode is not a denied access mask upon exit if the function
+ * is used in this manner.
+ */
+static int
+zfs_zaccess_aces_check(znode_t *zp, uint32_t *working_mode,
+    boolean_t anyaccess, cred_t *cr)
+{
+       zfsvfs_t        *zfsvfs = zp->z_zfsvfs;
+       zfs_acl_t       *aclp;
+       int             error;
+       uid_t           uid = crgetuid(cr);
+       uint64_t        who;
+       uint16_t        type, iflags;
+       uint16_t        entry_type;
+       uint32_t        access_mask;
+       uint32_t        deny_mask = 0;
+       zfs_ace_hdr_t   *acep = NULL;
+       boolean_t       checkit;
+       uid_t           fowner;
+       uid_t           gowner;
 
        zfs_fuid_map_ids(zp, cr, &fowner, &gowner);
 
@@ -2268,6 +2257,7 @@ zfs_zaccess_common(znode_t *zp, uint32_t v4_mode, uint32_t *working_mode,
 
        while (acep = zfs_acl_next_ace(aclp, acep, &who, &access_mask,
            &iflags, &type)) {
+               uint32_t mask_matched;
 
                if (!zfs_acl_valid_ace_type(type, iflags))
                        continue;
@@ -2275,6 +2265,11 @@ zfs_zaccess_common(znode_t *zp, uint32_t v4_mode, uint32_t *working_mode,
                if (ZTOV(zp)->v_type == VDIR && (iflags & ACE_INHERIT_ONLY_ACE))
                        continue;
 
+               /* Skip ACE if it does not affect any AoI */
+               mask_matched = (access_mask & *working_mode);
+               if (!mask_matched)
+                       continue;
+
                entry_type = (iflags & ACE_TYPE_FLAGS);
 
                checkit = B_FALSE;
@@ -2313,14 +2308,24 @@ zfs_zaccess_common(znode_t *zp, uint32_t v4_mode, uint32_t *working_mode,
                }
 
                if (checkit) {
-                       uint32_t mask_matched = (access_mask & *working_mode);
-
-                       if (mask_matched) {
-                               if (type == DENY)
-                                       deny_mask |= mask_matched;
-
-                               *working_mode &= ~mask_matched;
+                       if (type == DENY) {
+                               DTRACE_PROBE3(zfs__ace__denies,
+                                   znode_t *, zp,
+                                   zfs_ace_hdr_t *, acep,
+                                   uint32_t, mask_matched);
+                               deny_mask |= mask_matched;
+                       } else {
+                               DTRACE_PROBE3(zfs__ace__allows,
+                                   znode_t *, zp,
+                                   zfs_ace_hdr_t *, acep,
+                                   uint32_t, mask_matched);
+                               if (anyaccess) {
+                                       mutex_exit(&zp->z_acl_lock);
+                                       zfs_acl_free(aclp);
+                                       return (0);
+                               }
                        }
+                       *working_mode &= ~mask_matched;
                }
 
                /* Are we done? */
@@ -2342,6 +2347,69 @@ zfs_zaccess_common(znode_t *zp, uint32_t v4_mode, uint32_t *working_mode,
        return (0);
 }
 
+/*
+ * Return true if any access whatsoever granted, we don't actually
+ * care what access is granted.
+ */
+boolean_t
+zfs_has_access(znode_t *zp, cred_t *cr)
+{
+       uint32_t have = ACE_ALL_PERMS;
+
+       if (zfs_zaccess_aces_check(zp, &have, B_TRUE, cr) != 0) {
+               uid_t           owner;
+
+               owner = zfs_fuid_map_id(zp->z_zfsvfs,
+                   zp->z_phys->zp_uid, cr, ZFS_OWNER);
+
+               return (
+                   secpolicy_vnode_access(cr, ZTOV(zp), owner, VREAD) == 0 ||
+                   secpolicy_vnode_access(cr, ZTOV(zp), owner, VWRITE) == 0 ||
+                   secpolicy_vnode_access(cr, ZTOV(zp), owner, VEXEC) == 0 ||
+                   secpolicy_vnode_chown(cr, B_TRUE) == 0 ||
+                   secpolicy_vnode_chown(cr, B_FALSE) == 0 ||
+                   secpolicy_vnode_setdac(cr, owner) == 0 ||
+                   secpolicy_vnode_remove(cr) == 0);
+       }
+       return (B_TRUE);
+}
+
+static int
+zfs_zaccess_common(znode_t *zp, uint32_t v4_mode, uint32_t *working_mode,
+    boolean_t *check_privs, boolean_t skipaclchk, cred_t *cr)
+{
+       zfsvfs_t *zfsvfs = zp->z_zfsvfs;
+       int err;
+
+       *working_mode = v4_mode;
+       *check_privs = B_TRUE;
+
+       /*
+        * Short circuit empty requests
+        */
+       if (v4_mode == 0 || zfsvfs->z_replay) {
+               *working_mode = 0;
+               return (0);
+       }
+
+       if ((err = zfs_zaccess_dataset_check(zp, v4_mode)) != 0) {
+               *check_privs = B_FALSE;
+               return (err);
+       }
+
+       /*
+        * The caller requested that the ACL check be skipped.  This
+        * would only happen if the caller checked VOP_ACCESS() with a
+        * 32 bit ACE mask and already had the appropriate permissions.
+        */
+       if (skipaclchk) {
+               *working_mode = 0;
+               return (0);
+       }
+
+       return (zfs_zaccess_aces_check(zp, working_mode, B_FALSE, cr));
+}
+
 static int
 zfs_zaccess_append(znode_t *zp, uint32_t *working_mode, boolean_t *check_privs,
     cred_t *cr)