xref: /onnv-gate/usr/src/uts/common/fs/zfs/zfs_acl.c (revision 12620:12fcd99a642d)
1789Sahrens /*
2789Sahrens  * CDDL HEADER START
3789Sahrens  *
4789Sahrens  * The contents of this file are subject to the terms of the
51544Seschrock  * Common Development and Distribution License (the "License").
61544Seschrock  * You may not use this file except in compliance with the License.
7789Sahrens  *
8789Sahrens  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9789Sahrens  * or http://www.opensolaris.org/os/licensing.
10789Sahrens  * See the License for the specific language governing permissions
11789Sahrens  * and limitations under the License.
12789Sahrens  *
13789Sahrens  * When distributing Covered Code, include this CDDL HEADER in each
14789Sahrens  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15789Sahrens  * If applicable, add the following below this CDDL HEADER, with the
16789Sahrens  * fields enclosed by brackets "[]" replaced with your own identifying
17789Sahrens  * information: Portions Copyright [yyyy] [name of copyright owner]
18789Sahrens  *
19789Sahrens  * CDDL HEADER END
20789Sahrens  */
21789Sahrens /*
2212164SMark.Shellenbaum@Sun.COM  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
23789Sahrens  */
24789Sahrens 
25789Sahrens #include <sys/types.h>
26789Sahrens #include <sys/param.h>
27789Sahrens #include <sys/time.h>
28789Sahrens #include <sys/systm.h>
29789Sahrens #include <sys/sysmacros.h>
30789Sahrens #include <sys/resource.h>
31789Sahrens #include <sys/vfs.h>
32789Sahrens #include <sys/vnode.h>
335331Samw #include <sys/sid.h>
34789Sahrens #include <sys/file.h>
35789Sahrens #include <sys/stat.h>
36789Sahrens #include <sys/kmem.h>
37789Sahrens #include <sys/cmn_err.h>
38789Sahrens #include <sys/errno.h>
39789Sahrens #include <sys/unistd.h>
401576Smarks #include <sys/sdt.h>
41789Sahrens #include <sys/fs/zfs.h>
42789Sahrens #include <sys/mode.h>
43789Sahrens #include <sys/policy.h>
44789Sahrens #include <sys/zfs_znode.h>
455331Samw #include <sys/zfs_fuid.h>
46789Sahrens #include <sys/zfs_acl.h>
47789Sahrens #include <sys/zfs_dir.h>
48789Sahrens #include <sys/zfs_vfsops.h>
49789Sahrens #include <sys/dmu.h>
505331Samw #include <sys/dnode.h>
51789Sahrens #include <sys/zap.h>
5211935SMark.Shellenbaum@Sun.COM #include <sys/sa.h>
53789Sahrens #include "fs/fs_subr.h"
54789Sahrens #include <acl/acl_common.h>
55789Sahrens 
56789Sahrens #define	ALLOW	ACE_ACCESS_ALLOWED_ACE_TYPE
57789Sahrens #define	DENY	ACE_ACCESS_DENIED_ACE_TYPE
585331Samw #define	MAX_ACE_TYPE	ACE_SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE
597559SMark.Shellenbaum@Sun.COM #define	MIN_ACE_TYPE	ALLOW
60789Sahrens 
61789Sahrens #define	OWNING_GROUP		(ACE_GROUP|ACE_IDENTIFIER_GROUP)
62789Sahrens #define	EVERYONE_ALLOW_MASK (ACE_READ_ACL|ACE_READ_ATTRIBUTES | \
63789Sahrens     ACE_READ_NAMED_ATTRS|ACE_SYNCHRONIZE)
64789Sahrens #define	EVERYONE_DENY_MASK (ACE_WRITE_ACL|ACE_WRITE_OWNER | \
65789Sahrens     ACE_WRITE_ATTRIBUTES|ACE_WRITE_NAMED_ATTRS)
66789Sahrens #define	OWNER_ALLOW_MASK (ACE_WRITE_ACL | ACE_WRITE_OWNER | \
67789Sahrens     ACE_WRITE_ATTRIBUTES|ACE_WRITE_NAMED_ATTRS)
685331Samw 
695331Samw #define	ZFS_CHECKED_MASKS (ACE_READ_ACL|ACE_READ_ATTRIBUTES|ACE_READ_DATA| \
705331Samw     ACE_READ_NAMED_ATTRS|ACE_WRITE_DATA|ACE_WRITE_ATTRIBUTES| \
715331Samw     ACE_WRITE_NAMED_ATTRS|ACE_APPEND_DATA|ACE_EXECUTE|ACE_WRITE_OWNER| \
725331Samw     ACE_WRITE_ACL|ACE_DELETE|ACE_DELETE_CHILD|ACE_SYNCHRONIZE)
735331Samw 
748952SMark.Shellenbaum@Sun.COM #define	WRITE_MASK_DATA (ACE_WRITE_DATA|ACE_APPEND_DATA|ACE_WRITE_NAMED_ATTRS)
758952SMark.Shellenbaum@Sun.COM #define	WRITE_MASK_ATTRS (ACE_WRITE_ACL|ACE_WRITE_OWNER|ACE_WRITE_ATTRIBUTES| \
768952SMark.Shellenbaum@Sun.COM     ACE_DELETE|ACE_DELETE_CHILD)
778952SMark.Shellenbaum@Sun.COM #define	WRITE_MASK (WRITE_MASK_DATA|WRITE_MASK_ATTRS)
78789Sahrens 
79789Sahrens #define	OGE_CLEAR	(ACE_READ_DATA|ACE_LIST_DIRECTORY|ACE_WRITE_DATA| \
80789Sahrens     ACE_ADD_FILE|ACE_APPEND_DATA|ACE_ADD_SUBDIRECTORY|ACE_EXECUTE)
81789Sahrens 
82789Sahrens #define	OKAY_MASK_BITS (ACE_READ_DATA|ACE_LIST_DIRECTORY|ACE_WRITE_DATA| \
83789Sahrens     ACE_ADD_FILE|ACE_APPEND_DATA|ACE_ADD_SUBDIRECTORY|ACE_EXECUTE)
84789Sahrens 
85789Sahrens #define	ALL_INHERIT	(ACE_FILE_INHERIT_ACE|ACE_DIRECTORY_INHERIT_ACE | \
865331Samw     ACE_NO_PROPAGATE_INHERIT_ACE|ACE_INHERIT_ONLY_ACE|ACE_INHERITED_ACE)
87789Sahrens 
886385Smarks #define	RESTRICTED_CLEAR	(ACE_WRITE_ACL|ACE_WRITE_OWNER)
89789Sahrens 
905331Samw #define	V4_ACL_WIDE_FLAGS (ZFS_ACL_AUTO_INHERIT|ZFS_ACL_DEFAULTED|\
915331Samw     ZFS_ACL_PROTECTED)
925331Samw 
935331Samw #define	ZFS_ACL_WIDE_FLAGS (V4_ACL_WIDE_FLAGS|ZFS_ACL_TRIVIAL|ZFS_INHERIT_ACE|\
945331Samw     ZFS_ACL_OBJ_ACE)
955331Samw 
9610143STim.Haley@Sun.COM #define	ALL_MODE_EXECS (S_IXUSR | S_IXGRP | S_IXOTH)
9710143STim.Haley@Sun.COM 
985331Samw static uint16_t
995331Samw zfs_ace_v0_get_type(void *acep)
1005331Samw {
1015331Samw 	return (((zfs_oldace_t *)acep)->z_type);
1025331Samw }
1035331Samw 
1045331Samw static uint16_t
1055331Samw zfs_ace_v0_get_flags(void *acep)
1065331Samw {
1075331Samw 	return (((zfs_oldace_t *)acep)->z_flags);
1085331Samw }
1095331Samw 
1105331Samw static uint32_t
1115331Samw zfs_ace_v0_get_mask(void *acep)
1125331Samw {
1135331Samw 	return (((zfs_oldace_t *)acep)->z_access_mask);
1145331Samw }
1155331Samw 
1165331Samw static uint64_t
1175331Samw zfs_ace_v0_get_who(void *acep)
1185331Samw {
1195331Samw 	return (((zfs_oldace_t *)acep)->z_fuid);
1205331Samw }
1215331Samw 
1225331Samw static void
1235331Samw zfs_ace_v0_set_type(void *acep, uint16_t type)
1245331Samw {
1255331Samw 	((zfs_oldace_t *)acep)->z_type = type;
1265331Samw }
1275331Samw 
1285331Samw static void
1295331Samw zfs_ace_v0_set_flags(void *acep, uint16_t flags)
1305331Samw {
1315331Samw 	((zfs_oldace_t *)acep)->z_flags = flags;
1325331Samw }
1335331Samw 
1345331Samw static void
1355331Samw zfs_ace_v0_set_mask(void *acep, uint32_t mask)
1365331Samw {
1375331Samw 	((zfs_oldace_t *)acep)->z_access_mask = mask;
1385331Samw }
1395331Samw 
1405331Samw static void
1415331Samw zfs_ace_v0_set_who(void *acep, uint64_t who)
1425331Samw {
1435331Samw 	((zfs_oldace_t *)acep)->z_fuid = who;
1445331Samw }
1455331Samw 
1465331Samw /*ARGSUSED*/
1475331Samw static size_t
1485331Samw zfs_ace_v0_size(void *acep)
1495331Samw {
1505331Samw 	return (sizeof (zfs_oldace_t));
1515331Samw }
1525331Samw 
1535331Samw static size_t
1545331Samw zfs_ace_v0_abstract_size(void)
1555331Samw {
1565331Samw 	return (sizeof (zfs_oldace_t));
1575331Samw }
1585331Samw 
1595331Samw static int
1605331Samw zfs_ace_v0_mask_off(void)
1615331Samw {
1625331Samw 	return (offsetof(zfs_oldace_t, z_access_mask));
1635331Samw }
1645331Samw 
1655331Samw /*ARGSUSED*/
1665331Samw static int
1675331Samw zfs_ace_v0_data(void *acep, void **datap)
1685331Samw {
1695331Samw 	*datap = NULL;
1705331Samw 	return (0);
1715331Samw }
1725331Samw 
1735331Samw static acl_ops_t zfs_acl_v0_ops = {
1745331Samw 	zfs_ace_v0_get_mask,
1755331Samw 	zfs_ace_v0_set_mask,
1765331Samw 	zfs_ace_v0_get_flags,
1775331Samw 	zfs_ace_v0_set_flags,
1785331Samw 	zfs_ace_v0_get_type,
1795331Samw 	zfs_ace_v0_set_type,
1805331Samw 	zfs_ace_v0_get_who,
1815331Samw 	zfs_ace_v0_set_who,
1825331Samw 	zfs_ace_v0_size,
1835331Samw 	zfs_ace_v0_abstract_size,
1845331Samw 	zfs_ace_v0_mask_off,
1855331Samw 	zfs_ace_v0_data
1865331Samw };
1875331Samw 
1885331Samw static uint16_t
1895331Samw zfs_ace_fuid_get_type(void *acep)
1905331Samw {
1915331Samw 	return (((zfs_ace_hdr_t *)acep)->z_type);
1925331Samw }
1935331Samw 
1945331Samw static uint16_t
1955331Samw zfs_ace_fuid_get_flags(void *acep)
1965331Samw {
1975331Samw 	return (((zfs_ace_hdr_t *)acep)->z_flags);
1985331Samw }
1995331Samw 
2005331Samw static uint32_t
2015331Samw zfs_ace_fuid_get_mask(void *acep)
2025331Samw {
2035331Samw 	return (((zfs_ace_hdr_t *)acep)->z_access_mask);
2045331Samw }
2055331Samw 
2065331Samw static uint64_t
2075331Samw zfs_ace_fuid_get_who(void *args)
2085331Samw {
2095331Samw 	uint16_t entry_type;
2105331Samw 	zfs_ace_t *acep = args;
2115331Samw 
2125331Samw 	entry_type = acep->z_hdr.z_flags & ACE_TYPE_FLAGS;
213789Sahrens 
2145331Samw 	if (entry_type == ACE_OWNER || entry_type == OWNING_GROUP ||
2155331Samw 	    entry_type == ACE_EVERYONE)
2165331Samw 		return (-1);
2175331Samw 	return (((zfs_ace_t *)acep)->z_fuid);
2185331Samw }
2195331Samw 
2205331Samw static void
2215331Samw zfs_ace_fuid_set_type(void *acep, uint16_t type)
2225331Samw {
2235331Samw 	((zfs_ace_hdr_t *)acep)->z_type = type;
2245331Samw }
2255331Samw 
2265331Samw static void
2275331Samw zfs_ace_fuid_set_flags(void *acep, uint16_t flags)
2285331Samw {
2295331Samw 	((zfs_ace_hdr_t *)acep)->z_flags = flags;
2305331Samw }
2315331Samw 
2325331Samw static void
2335331Samw zfs_ace_fuid_set_mask(void *acep, uint32_t mask)
2345331Samw {
2355331Samw 	((zfs_ace_hdr_t *)acep)->z_access_mask = mask;
2365331Samw }
2375331Samw 
2385331Samw static void
2395331Samw zfs_ace_fuid_set_who(void *arg, uint64_t who)
2405331Samw {
2415331Samw 	zfs_ace_t *acep = arg;
2425331Samw 
2435331Samw 	uint16_t entry_type = acep->z_hdr.z_flags & ACE_TYPE_FLAGS;
2445331Samw 
2455331Samw 	if (entry_type == ACE_OWNER || entry_type == OWNING_GROUP ||
2465331Samw 	    entry_type == ACE_EVERYONE)
2475331Samw 		return;
2485331Samw 	acep->z_fuid = who;
2495331Samw }
2505331Samw 
2515331Samw static size_t
2525331Samw zfs_ace_fuid_size(void *acep)
2535331Samw {
2545331Samw 	zfs_ace_hdr_t *zacep = acep;
2555331Samw 	uint16_t entry_type;
2565331Samw 
2575331Samw 	switch (zacep->z_type) {
2585331Samw 	case ACE_ACCESS_ALLOWED_OBJECT_ACE_TYPE:
2595331Samw 	case ACE_ACCESS_DENIED_OBJECT_ACE_TYPE:
2605331Samw 	case ACE_SYSTEM_AUDIT_OBJECT_ACE_TYPE:
2615331Samw 	case ACE_SYSTEM_ALARM_OBJECT_ACE_TYPE:
2625331Samw 		return (sizeof (zfs_object_ace_t));
2635331Samw 	case ALLOW:
2645331Samw 	case DENY:
2655331Samw 		entry_type =
2665331Samw 		    (((zfs_ace_hdr_t *)acep)->z_flags & ACE_TYPE_FLAGS);
2675331Samw 		if (entry_type == ACE_OWNER ||
2687328SMark.Shellenbaum@Sun.COM 		    entry_type == OWNING_GROUP ||
2695331Samw 		    entry_type == ACE_EVERYONE)
2705331Samw 			return (sizeof (zfs_ace_hdr_t));
2715331Samw 		/*FALLTHROUGH*/
2725331Samw 	default:
2735331Samw 		return (sizeof (zfs_ace_t));
2745331Samw 	}
2755331Samw }
2765331Samw 
2775331Samw static size_t
2785331Samw zfs_ace_fuid_abstract_size(void)
2795331Samw {
2805331Samw 	return (sizeof (zfs_ace_hdr_t));
2815331Samw }
2825331Samw 
2835331Samw static int
2845331Samw zfs_ace_fuid_mask_off(void)
2855331Samw {
2865331Samw 	return (offsetof(zfs_ace_hdr_t, z_access_mask));
2875331Samw }
2885331Samw 
2895331Samw static int
2905331Samw zfs_ace_fuid_data(void *acep, void **datap)
2915331Samw {
2925331Samw 	zfs_ace_t *zacep = acep;
2935331Samw 	zfs_object_ace_t *zobjp;
2945331Samw 
2955331Samw 	switch (zacep->z_hdr.z_type) {
2965331Samw 	case ACE_ACCESS_ALLOWED_OBJECT_ACE_TYPE:
2975331Samw 	case ACE_ACCESS_DENIED_OBJECT_ACE_TYPE:
2985331Samw 	case ACE_SYSTEM_AUDIT_OBJECT_ACE_TYPE:
2995331Samw 	case ACE_SYSTEM_ALARM_OBJECT_ACE_TYPE:
3005331Samw 		zobjp = acep;
3015331Samw 		*datap = (caddr_t)zobjp + sizeof (zfs_ace_t);
3025331Samw 		return (sizeof (zfs_object_ace_t) - sizeof (zfs_ace_t));
3035331Samw 	default:
3045331Samw 		*datap = NULL;
3055331Samw 		return (0);
3065331Samw 	}
3075331Samw }
3085331Samw 
3095331Samw static acl_ops_t zfs_acl_fuid_ops = {
3105331Samw 	zfs_ace_fuid_get_mask,
3115331Samw 	zfs_ace_fuid_set_mask,
3125331Samw 	zfs_ace_fuid_get_flags,
3135331Samw 	zfs_ace_fuid_set_flags,
3145331Samw 	zfs_ace_fuid_get_type,
3155331Samw 	zfs_ace_fuid_set_type,
3165331Samw 	zfs_ace_fuid_get_who,
3175331Samw 	zfs_ace_fuid_set_who,
3185331Samw 	zfs_ace_fuid_size,
3195331Samw 	zfs_ace_fuid_abstract_size,
3205331Samw 	zfs_ace_fuid_mask_off,
3215331Samw 	zfs_ace_fuid_data
3225331Samw };
3235331Samw 
32411935SMark.Shellenbaum@Sun.COM /*
32511935SMark.Shellenbaum@Sun.COM  * The following three functions are provided for compatibility with
32611935SMark.Shellenbaum@Sun.COM  * older ZPL version in order to determine if the file use to have
32711935SMark.Shellenbaum@Sun.COM  * an external ACL and what version of ACL previously existed on the
32811935SMark.Shellenbaum@Sun.COM  * file.  Would really be nice to not need this, sigh.
32911935SMark.Shellenbaum@Sun.COM  */
33011935SMark.Shellenbaum@Sun.COM uint64_t
33111935SMark.Shellenbaum@Sun.COM zfs_external_acl(znode_t *zp)
33211935SMark.Shellenbaum@Sun.COM {
33311935SMark.Shellenbaum@Sun.COM 	zfs_acl_phys_t acl_phys;
334*12620SMark.Shellenbaum@Oracle.COM 	int error;
33511935SMark.Shellenbaum@Sun.COM 
33611935SMark.Shellenbaum@Sun.COM 	if (zp->z_is_sa)
33711935SMark.Shellenbaum@Sun.COM 		return (0);
33811935SMark.Shellenbaum@Sun.COM 
339*12620SMark.Shellenbaum@Oracle.COM 	/*
340*12620SMark.Shellenbaum@Oracle.COM 	 * Need to deal with a potential
341*12620SMark.Shellenbaum@Oracle.COM 	 * race where zfs_sa_upgrade could cause
342*12620SMark.Shellenbaum@Oracle.COM 	 * z_isa_sa to change.
343*12620SMark.Shellenbaum@Oracle.COM 	 *
344*12620SMark.Shellenbaum@Oracle.COM 	 * If the lookup fails then the state of z_is_sa should have
345*12620SMark.Shellenbaum@Oracle.COM 	 * changed.
346*12620SMark.Shellenbaum@Oracle.COM 	 */
347*12620SMark.Shellenbaum@Oracle.COM 
348*12620SMark.Shellenbaum@Oracle.COM 	if ((error = sa_lookup(zp->z_sa_hdl, SA_ZPL_ZNODE_ACL(zp->z_zfsvfs),
349*12620SMark.Shellenbaum@Oracle.COM 	    &acl_phys, sizeof (acl_phys))) == 0)
350*12620SMark.Shellenbaum@Oracle.COM 		return (acl_phys.z_acl_extern_obj);
351*12620SMark.Shellenbaum@Oracle.COM 	else {
352*12620SMark.Shellenbaum@Oracle.COM 		/*
353*12620SMark.Shellenbaum@Oracle.COM 		 * after upgrade the SA_ZPL_ZNODE_ACL should have been
354*12620SMark.Shellenbaum@Oracle.COM 		 * removed
355*12620SMark.Shellenbaum@Oracle.COM 		 */
356*12620SMark.Shellenbaum@Oracle.COM 		VERIFY(zp->z_is_sa && error == ENOENT);
357*12620SMark.Shellenbaum@Oracle.COM 		return (0);
358*12620SMark.Shellenbaum@Oracle.COM 	}
35911935SMark.Shellenbaum@Sun.COM }
36011935SMark.Shellenbaum@Sun.COM 
36111935SMark.Shellenbaum@Sun.COM /*
36211935SMark.Shellenbaum@Sun.COM  * Determine size of ACL in bytes
36311935SMark.Shellenbaum@Sun.COM  *
36411935SMark.Shellenbaum@Sun.COM  * This is more complicated than it should be since we have to deal
36511935SMark.Shellenbaum@Sun.COM  * with old external ACLs.
36611935SMark.Shellenbaum@Sun.COM  */
36711935SMark.Shellenbaum@Sun.COM static int
36811935SMark.Shellenbaum@Sun.COM zfs_acl_znode_info(znode_t *zp, int *aclsize, int *aclcount,
36911935SMark.Shellenbaum@Sun.COM     zfs_acl_phys_t *aclphys)
37011935SMark.Shellenbaum@Sun.COM {
37111935SMark.Shellenbaum@Sun.COM 	zfsvfs_t *zfsvfs = zp->z_zfsvfs;
37211935SMark.Shellenbaum@Sun.COM 	uint64_t acl_count;
37311935SMark.Shellenbaum@Sun.COM 	int size;
37411935SMark.Shellenbaum@Sun.COM 	int error;
37511935SMark.Shellenbaum@Sun.COM 
376*12620SMark.Shellenbaum@Oracle.COM 	ASSERT(MUTEX_HELD(&zp->z_acl_lock));
37711935SMark.Shellenbaum@Sun.COM 	if (zp->z_is_sa) {
37811935SMark.Shellenbaum@Sun.COM 		if ((error = sa_size(zp->z_sa_hdl, SA_ZPL_DACL_ACES(zfsvfs),
37911935SMark.Shellenbaum@Sun.COM 		    &size)) != 0)
38011935SMark.Shellenbaum@Sun.COM 			return (error);
38111935SMark.Shellenbaum@Sun.COM 		*aclsize = size;
38211935SMark.Shellenbaum@Sun.COM 		if ((error = sa_lookup(zp->z_sa_hdl, SA_ZPL_DACL_COUNT(zfsvfs),
38311935SMark.Shellenbaum@Sun.COM 		    &acl_count, sizeof (acl_count))) != 0)
38411935SMark.Shellenbaum@Sun.COM 			return (error);
38511935SMark.Shellenbaum@Sun.COM 		*aclcount = acl_count;
38611935SMark.Shellenbaum@Sun.COM 	} else {
38711935SMark.Shellenbaum@Sun.COM 		if ((error = sa_lookup(zp->z_sa_hdl, SA_ZPL_ZNODE_ACL(zfsvfs),
38811935SMark.Shellenbaum@Sun.COM 		    aclphys, sizeof (*aclphys))) != 0)
38911935SMark.Shellenbaum@Sun.COM 			return (error);
39011935SMark.Shellenbaum@Sun.COM 
39111935SMark.Shellenbaum@Sun.COM 		if (aclphys->z_acl_version == ZFS_ACL_VERSION_INITIAL) {
39211935SMark.Shellenbaum@Sun.COM 			*aclsize = ZFS_ACL_SIZE(aclphys->z_acl_size);
39311935SMark.Shellenbaum@Sun.COM 			*aclcount = aclphys->z_acl_size;
39411935SMark.Shellenbaum@Sun.COM 		} else {
39511935SMark.Shellenbaum@Sun.COM 			*aclsize = aclphys->z_acl_size;
39611935SMark.Shellenbaum@Sun.COM 			*aclcount = aclphys->z_acl_count;
39711935SMark.Shellenbaum@Sun.COM 		}
39811935SMark.Shellenbaum@Sun.COM 	}
39911935SMark.Shellenbaum@Sun.COM 	return (0);
40011935SMark.Shellenbaum@Sun.COM }
40111935SMark.Shellenbaum@Sun.COM 
40211935SMark.Shellenbaum@Sun.COM int
40311935SMark.Shellenbaum@Sun.COM zfs_znode_acl_version(znode_t *zp)
40411935SMark.Shellenbaum@Sun.COM {
40511935SMark.Shellenbaum@Sun.COM 	zfs_acl_phys_t acl_phys;
40611935SMark.Shellenbaum@Sun.COM 
407*12620SMark.Shellenbaum@Oracle.COM 	if (zp->z_is_sa)
40811935SMark.Shellenbaum@Sun.COM 		return (ZFS_ACL_VERSION_FUID);
409*12620SMark.Shellenbaum@Oracle.COM 	else {
410*12620SMark.Shellenbaum@Oracle.COM 		int error;
411*12620SMark.Shellenbaum@Oracle.COM 
412*12620SMark.Shellenbaum@Oracle.COM 		/*
413*12620SMark.Shellenbaum@Oracle.COM 		 * Need to deal with a potential
414*12620SMark.Shellenbaum@Oracle.COM 		 * race where zfs_sa_upgrade could cause
415*12620SMark.Shellenbaum@Oracle.COM 		 * z_isa_sa to change.
416*12620SMark.Shellenbaum@Oracle.COM 		 *
417*12620SMark.Shellenbaum@Oracle.COM 		 * If the lookup fails then the state of z_is_sa should have
418*12620SMark.Shellenbaum@Oracle.COM 		 * changed.
419*12620SMark.Shellenbaum@Oracle.COM 		 */
420*12620SMark.Shellenbaum@Oracle.COM 		if ((error = sa_lookup(zp->z_sa_hdl,
42111935SMark.Shellenbaum@Sun.COM 		    SA_ZPL_ZNODE_ACL(zp->z_zfsvfs),
422*12620SMark.Shellenbaum@Oracle.COM 		    &acl_phys, sizeof (acl_phys))) == 0)
423*12620SMark.Shellenbaum@Oracle.COM 			return (acl_phys.z_acl_version);
424*12620SMark.Shellenbaum@Oracle.COM 		else {
425*12620SMark.Shellenbaum@Oracle.COM 			/*
426*12620SMark.Shellenbaum@Oracle.COM 			 * After upgrade SA_ZPL_ZNODE_ACL should have
427*12620SMark.Shellenbaum@Oracle.COM 			 * been removed.
428*12620SMark.Shellenbaum@Oracle.COM 			 */
429*12620SMark.Shellenbaum@Oracle.COM 			VERIFY(zp->z_is_sa && error == ENOENT);
430*12620SMark.Shellenbaum@Oracle.COM 			return (ZFS_ACL_VERSION_FUID);
431*12620SMark.Shellenbaum@Oracle.COM 		}
43211935SMark.Shellenbaum@Sun.COM 	}
43311935SMark.Shellenbaum@Sun.COM }
43411935SMark.Shellenbaum@Sun.COM 
4355331Samw static int
4365331Samw zfs_acl_version(int version)
4375331Samw {
4385331Samw 	if (version < ZPL_VERSION_FUID)
4395331Samw 		return (ZFS_ACL_VERSION_INITIAL);
4405331Samw 	else
4415331Samw 		return (ZFS_ACL_VERSION_FUID);
4425331Samw }
4435331Samw 
4445331Samw static int
4455331Samw zfs_acl_version_zp(znode_t *zp)
4465331Samw {
4475331Samw 	return (zfs_acl_version(zp->z_zfsvfs->z_version));
4485331Samw }
449789Sahrens 
45011935SMark.Shellenbaum@Sun.COM zfs_acl_t *
4515331Samw zfs_acl_alloc(int vers)
452789Sahrens {
453789Sahrens 	zfs_acl_t *aclp;
454789Sahrens 
455789Sahrens 	aclp = kmem_zalloc(sizeof (zfs_acl_t), KM_SLEEP);
4565331Samw 	list_create(&aclp->z_acl, sizeof (zfs_acl_node_t),
4575331Samw 	    offsetof(zfs_acl_node_t, z_next));
4585331Samw 	aclp->z_version = vers;
4595331Samw 	if (vers == ZFS_ACL_VERSION_FUID)
4605331Samw 		aclp->z_ops = zfs_acl_fuid_ops;
4615331Samw 	else
4625331Samw 		aclp->z_ops = zfs_acl_v0_ops;
4635331Samw 	return (aclp);
4645331Samw }
4655331Samw 
46611935SMark.Shellenbaum@Sun.COM zfs_acl_node_t *
4675331Samw zfs_acl_node_alloc(size_t bytes)
4685331Samw {
4695331Samw 	zfs_acl_node_t *aclnode;
4705331Samw 
4715331Samw 	aclnode = kmem_zalloc(sizeof (zfs_acl_node_t), KM_SLEEP);
4725331Samw 	if (bytes) {
4735331Samw 		aclnode->z_acldata = kmem_alloc(bytes, KM_SLEEP);
4745331Samw 		aclnode->z_allocdata = aclnode->z_acldata;
4755331Samw 		aclnode->z_allocsize = bytes;
4765331Samw 		aclnode->z_size = bytes;
477789Sahrens 	}
4785331Samw 
4795331Samw 	return (aclnode);
4805331Samw }
4815331Samw 
4825331Samw static void
4835331Samw zfs_acl_node_free(zfs_acl_node_t *aclnode)
4845331Samw {
4855331Samw 	if (aclnode->z_allocsize)
4865331Samw 		kmem_free(aclnode->z_allocdata, aclnode->z_allocsize);
4875331Samw 	kmem_free(aclnode, sizeof (zfs_acl_node_t));
488789Sahrens }
489789Sahrens 
4905489Smarks static void
4915489Smarks zfs_acl_release_nodes(zfs_acl_t *aclp)
492789Sahrens {
4935331Samw 	zfs_acl_node_t *aclnode;
4945331Samw 
4955331Samw 	while (aclnode = list_head(&aclp->z_acl)) {
4965331Samw 		list_remove(&aclp->z_acl, aclnode);
4975331Samw 		zfs_acl_node_free(aclnode);
498789Sahrens 	}
4995489Smarks 	aclp->z_acl_count = 0;
5005489Smarks 	aclp->z_acl_bytes = 0;
5015489Smarks }
5025331Samw 
5035489Smarks void
5045489Smarks zfs_acl_free(zfs_acl_t *aclp)
5055489Smarks {
5065489Smarks 	zfs_acl_release_nodes(aclp);
5075331Samw 	list_destroy(&aclp->z_acl);
508789Sahrens 	kmem_free(aclp, sizeof (zfs_acl_t));
509789Sahrens }
510789Sahrens 
5115331Samw static boolean_t
5127559SMark.Shellenbaum@Sun.COM zfs_acl_valid_ace_type(uint_t type, uint_t flags)
5137559SMark.Shellenbaum@Sun.COM {
5147559SMark.Shellenbaum@Sun.COM 	uint16_t entry_type;
5157559SMark.Shellenbaum@Sun.COM 
5167559SMark.Shellenbaum@Sun.COM 	switch (type) {
5177559SMark.Shellenbaum@Sun.COM 	case ALLOW:
5187559SMark.Shellenbaum@Sun.COM 	case DENY:
5197559SMark.Shellenbaum@Sun.COM 	case ACE_SYSTEM_AUDIT_ACE_TYPE:
5207559SMark.Shellenbaum@Sun.COM 	case ACE_SYSTEM_ALARM_ACE_TYPE:
5217559SMark.Shellenbaum@Sun.COM 		entry_type = flags & ACE_TYPE_FLAGS;
5227559SMark.Shellenbaum@Sun.COM 		return (entry_type == ACE_OWNER ||
5237559SMark.Shellenbaum@Sun.COM 		    entry_type == OWNING_GROUP ||
5247559SMark.Shellenbaum@Sun.COM 		    entry_type == ACE_EVERYONE || entry_type == 0 ||
5257559SMark.Shellenbaum@Sun.COM 		    entry_type == ACE_IDENTIFIER_GROUP);
5267559SMark.Shellenbaum@Sun.COM 	default:
5277559SMark.Shellenbaum@Sun.COM 		if (type >= MIN_ACE_TYPE && type <= MAX_ACE_TYPE)
5287559SMark.Shellenbaum@Sun.COM 			return (B_TRUE);
5297559SMark.Shellenbaum@Sun.COM 	}
5307559SMark.Shellenbaum@Sun.COM 	return (B_FALSE);
5317559SMark.Shellenbaum@Sun.COM }
5327559SMark.Shellenbaum@Sun.COM 
5337559SMark.Shellenbaum@Sun.COM static boolean_t
5345331Samw zfs_ace_valid(vtype_t obj_type, zfs_acl_t *aclp, uint16_t type, uint16_t iflags)
535789Sahrens {
5365331Samw 	/*
5375331Samw 	 * first check type of entry
5385331Samw 	 */
5395331Samw 
5407559SMark.Shellenbaum@Sun.COM 	if (!zfs_acl_valid_ace_type(type, iflags))
5415331Samw 		return (B_FALSE);
5425331Samw 
5435331Samw 	switch (type) {
5445331Samw 	case ACE_ACCESS_ALLOWED_OBJECT_ACE_TYPE:
5455331Samw 	case ACE_ACCESS_DENIED_OBJECT_ACE_TYPE:
5465331Samw 	case ACE_SYSTEM_AUDIT_OBJECT_ACE_TYPE:
5475331Samw 	case ACE_SYSTEM_ALARM_OBJECT_ACE_TYPE:
5485331Samw 		if (aclp->z_version < ZFS_ACL_VERSION_FUID)
5495331Samw 			return (B_FALSE);
5505331Samw 		aclp->z_hints |= ZFS_ACL_OBJ_ACE;
5515331Samw 	}
552789Sahrens 
5537559SMark.Shellenbaum@Sun.COM 	/*
5547559SMark.Shellenbaum@Sun.COM 	 * next check inheritance level flags
5557559SMark.Shellenbaum@Sun.COM 	 */
5567559SMark.Shellenbaum@Sun.COM 
5577057Smarks 	if (obj_type == VDIR &&
5587057Smarks 	    (iflags & (ACE_FILE_INHERIT_ACE|ACE_DIRECTORY_INHERIT_ACE)))
5595331Samw 		aclp->z_hints |= ZFS_INHERIT_ACE;
5605331Samw 
5615331Samw 	if (iflags & (ACE_INHERIT_ONLY_ACE|ACE_NO_PROPAGATE_INHERIT_ACE)) {
5625331Samw 		if ((iflags & (ACE_FILE_INHERIT_ACE|
5635331Samw 		    ACE_DIRECTORY_INHERIT_ACE)) == 0) {
5645331Samw 			return (B_FALSE);
5655331Samw 		}
5665331Samw 	}
5675331Samw 
5685331Samw 	return (B_TRUE);
5695331Samw }
5705331Samw 
5715331Samw static void *
5725331Samw zfs_acl_next_ace(zfs_acl_t *aclp, void *start, uint64_t *who,
5735331Samw     uint32_t *access_mask, uint16_t *iflags, uint16_t *type)
5745331Samw {
5755331Samw 	zfs_acl_node_t *aclnode;
5765331Samw 
57711935SMark.Shellenbaum@Sun.COM 	ASSERT(aclp);
57811935SMark.Shellenbaum@Sun.COM 
5795331Samw 	if (start == NULL) {
5805331Samw 		aclnode = list_head(&aclp->z_acl);
5815331Samw 		if (aclnode == NULL)
5825331Samw 			return (NULL);
5835331Samw 
5845331Samw 		aclp->z_next_ace = aclnode->z_acldata;
5855331Samw 		aclp->z_curr_node = aclnode;
5865331Samw 		aclnode->z_ace_idx = 0;
5875331Samw 	}
5885331Samw 
5895331Samw 	aclnode = aclp->z_curr_node;
5905331Samw 
5915331Samw 	if (aclnode == NULL)
5925331Samw 		return (NULL);
5935331Samw 
5945331Samw 	if (aclnode->z_ace_idx >= aclnode->z_ace_count) {
5955331Samw 		aclnode = list_next(&aclp->z_acl, aclnode);
5965331Samw 		if (aclnode == NULL)
5975331Samw 			return (NULL);
5985331Samw 		else {
5995331Samw 			aclp->z_curr_node = aclnode;
6005331Samw 			aclnode->z_ace_idx = 0;
6015331Samw 			aclp->z_next_ace = aclnode->z_acldata;
6025331Samw 		}
6035331Samw 	}
6045331Samw 
6055331Samw 	if (aclnode->z_ace_idx < aclnode->z_ace_count) {
6065331Samw 		void *acep = aclp->z_next_ace;
6077559SMark.Shellenbaum@Sun.COM 		size_t ace_size;
6087559SMark.Shellenbaum@Sun.COM 
6097559SMark.Shellenbaum@Sun.COM 		/*
6107559SMark.Shellenbaum@Sun.COM 		 * Make sure we don't overstep our bounds
6117559SMark.Shellenbaum@Sun.COM 		 */
6127559SMark.Shellenbaum@Sun.COM 		ace_size = aclp->z_ops.ace_size(acep);
6137559SMark.Shellenbaum@Sun.COM 
6147559SMark.Shellenbaum@Sun.COM 		if (((caddr_t)acep + ace_size) >
6157559SMark.Shellenbaum@Sun.COM 		    ((caddr_t)aclnode->z_acldata + aclnode->z_size)) {
6167559SMark.Shellenbaum@Sun.COM 			return (NULL);
6177559SMark.Shellenbaum@Sun.COM 		}
6187559SMark.Shellenbaum@Sun.COM 
6195331Samw 		*iflags = aclp->z_ops.ace_flags_get(acep);
6205331Samw 		*type = aclp->z_ops.ace_type_get(acep);
6215331Samw 		*access_mask = aclp->z_ops.ace_mask_get(acep);
6225331Samw 		*who = aclp->z_ops.ace_who_get(acep);
6237559SMark.Shellenbaum@Sun.COM 		aclp->z_next_ace = (caddr_t)aclp->z_next_ace + ace_size;
6245331Samw 		aclnode->z_ace_idx++;
62511935SMark.Shellenbaum@Sun.COM 
6265331Samw 		return ((void *)acep);
6275331Samw 	}
6285331Samw 	return (NULL);
6295331Samw }
6305331Samw 
6315331Samw /*ARGSUSED*/
6325331Samw static uint64_t
6335331Samw zfs_ace_walk(void *datap, uint64_t cookie, int aclcnt,
6345331Samw     uint16_t *flags, uint16_t *type, uint32_t *mask)
6355331Samw {
6365331Samw 	zfs_acl_t *aclp = datap;
6375331Samw 	zfs_ace_hdr_t *acep = (zfs_ace_hdr_t *)(uintptr_t)cookie;
6385331Samw 	uint64_t who;
6395331Samw 
6405331Samw 	acep = zfs_acl_next_ace(aclp, acep, &who, mask,
6415331Samw 	    flags, type);
6425331Samw 	return ((uint64_t)(uintptr_t)acep);
6435331Samw }
6445331Samw 
6455331Samw static zfs_acl_node_t *
6465331Samw zfs_acl_curr_node(zfs_acl_t *aclp)
6475331Samw {
6485331Samw 	ASSERT(aclp->z_curr_node);
6495331Samw 	return (aclp->z_curr_node);
6505331Samw }
6515331Samw 
6525331Samw /*
6535331Samw  * Copy ACE to internal ZFS format.
6545331Samw  * While processing the ACL each ACE will be validated for correctness.
6555331Samw  * ACE FUIDs will be created later.
6565331Samw  */
6575331Samw int
6589179SMark.Shellenbaum@Sun.COM zfs_copy_ace_2_fuid(zfsvfs_t *zfsvfs, vtype_t obj_type, zfs_acl_t *aclp,
65911935SMark.Shellenbaum@Sun.COM     void *datap, zfs_ace_t *z_acl, uint64_t aclcnt, size_t *size,
6609179SMark.Shellenbaum@Sun.COM     zfs_fuid_info_t **fuidp, cred_t *cr)
6615331Samw {
6625331Samw 	int i;
6635331Samw 	uint16_t entry_type;
6645331Samw 	zfs_ace_t *aceptr = z_acl;
6655331Samw 	ace_t *acep = datap;
6665331Samw 	zfs_object_ace_t *zobjacep;
6675331Samw 	ace_object_t *aceobjp;
6685331Samw 
6695331Samw 	for (i = 0; i != aclcnt; i++) {
6705331Samw 		aceptr->z_hdr.z_access_mask = acep->a_access_mask;
6715331Samw 		aceptr->z_hdr.z_flags = acep->a_flags;
6725331Samw 		aceptr->z_hdr.z_type = acep->a_type;
6735331Samw 		entry_type = aceptr->z_hdr.z_flags & ACE_TYPE_FLAGS;
6745331Samw 		if (entry_type != ACE_OWNER && entry_type != OWNING_GROUP &&
6755824Smarks 		    entry_type != ACE_EVERYONE) {
6769179SMark.Shellenbaum@Sun.COM 			aceptr->z_fuid = zfs_fuid_create(zfsvfs, acep->a_who,
6779179SMark.Shellenbaum@Sun.COM 			    cr, (entry_type == 0) ?
6789179SMark.Shellenbaum@Sun.COM 			    ZFS_ACE_USER : ZFS_ACE_GROUP, fuidp);
6795824Smarks 		}
6805824Smarks 
6815331Samw 		/*
6825331Samw 		 * Make sure ACE is valid
6835331Samw 		 */
6845331Samw 		if (zfs_ace_valid(obj_type, aclp, aceptr->z_hdr.z_type,
6855331Samw 		    aceptr->z_hdr.z_flags) != B_TRUE)
6865331Samw 			return (EINVAL);
6875331Samw 
6885331Samw 		switch (acep->a_type) {
6895331Samw 		case ACE_ACCESS_ALLOWED_OBJECT_ACE_TYPE:
6905331Samw 		case ACE_ACCESS_DENIED_OBJECT_ACE_TYPE:
6915331Samw 		case ACE_SYSTEM_AUDIT_OBJECT_ACE_TYPE:
6925331Samw 		case ACE_SYSTEM_ALARM_OBJECT_ACE_TYPE:
6935331Samw 			zobjacep = (zfs_object_ace_t *)aceptr;
6945331Samw 			aceobjp = (ace_object_t *)acep;
6955331Samw 
6965331Samw 			bcopy(aceobjp->a_obj_type, zobjacep->z_object_type,
6975331Samw 			    sizeof (aceobjp->a_obj_type));
6985331Samw 			bcopy(aceobjp->a_inherit_obj_type,
6995331Samw 			    zobjacep->z_inherit_type,
7005331Samw 			    sizeof (aceobjp->a_inherit_obj_type));
7015331Samw 			acep = (ace_t *)((caddr_t)acep + sizeof (ace_object_t));
7025331Samw 			break;
7035331Samw 		default:
7045331Samw 			acep = (ace_t *)((caddr_t)acep + sizeof (ace_t));
7055331Samw 		}
7065331Samw 
7075331Samw 		aceptr = (zfs_ace_t *)((caddr_t)aceptr +
7085331Samw 		    aclp->z_ops.ace_size(aceptr));
7095331Samw 	}
7105331Samw 
7115331Samw 	*size = (caddr_t)aceptr - (caddr_t)z_acl;
712789Sahrens 
7135331Samw 	return (0);
7145331Samw }
7155331Samw 
7165331Samw /*
7175331Samw  * Copy ZFS ACEs to fixed size ace_t layout
7185331Samw  */
7195331Samw static void
7205771Sjp151216 zfs_copy_fuid_2_ace(zfsvfs_t *zfsvfs, zfs_acl_t *aclp, cred_t *cr,
7215771Sjp151216     void *datap, int filter)
7225331Samw {
7235331Samw 	uint64_t who;
7245331Samw 	uint32_t access_mask;
7255331Samw 	uint16_t iflags, type;
7265331Samw 	zfs_ace_hdr_t *zacep = NULL;
7275331Samw 	ace_t *acep = datap;
7285331Samw 	ace_object_t *objacep;
7295331Samw 	zfs_object_ace_t *zobjacep;
7305331Samw 	size_t ace_size;
7315331Samw 	uint16_t entry_type;
7325331Samw 
7335331Samw 	while (zacep = zfs_acl_next_ace(aclp, zacep,
7345331Samw 	    &who, &access_mask, &iflags, &type)) {
7355331Samw 
7365331Samw 		switch (type) {
7375331Samw 		case ACE_ACCESS_ALLOWED_OBJECT_ACE_TYPE:
7385331Samw 		case ACE_ACCESS_DENIED_OBJECT_ACE_TYPE:
7395331Samw 		case ACE_SYSTEM_AUDIT_OBJECT_ACE_TYPE:
7405331Samw 		case ACE_SYSTEM_ALARM_OBJECT_ACE_TYPE:
7415331Samw 			if (filter) {
7425331Samw 				continue;
7435331Samw 			}
7445331Samw 			zobjacep = (zfs_object_ace_t *)zacep;
7455331Samw 			objacep = (ace_object_t *)acep;
7465331Samw 			bcopy(zobjacep->z_object_type,
7475331Samw 			    objacep->a_obj_type,
7485331Samw 			    sizeof (zobjacep->z_object_type));
7495331Samw 			bcopy(zobjacep->z_inherit_type,
7505331Samw 			    objacep->a_inherit_obj_type,
7515331Samw 			    sizeof (zobjacep->z_inherit_type));
7525331Samw 			ace_size = sizeof (ace_object_t);
7535331Samw 			break;
7545331Samw 		default:
7555331Samw 			ace_size = sizeof (ace_t);
7565331Samw 			break;
7575331Samw 		}
7585331Samw 
7595331Samw 		entry_type = (iflags & ACE_TYPE_FLAGS);
7605331Samw 		if ((entry_type != ACE_OWNER &&
7617328SMark.Shellenbaum@Sun.COM 		    entry_type != OWNING_GROUP &&
7625959Smarks 		    entry_type != ACE_EVERYONE)) {
7635959Smarks 			acep->a_who = zfs_fuid_map_id(zfsvfs, who,
7645959Smarks 			    cr, (entry_type & ACE_IDENTIFIER_GROUP) ?
7655959Smarks 			    ZFS_ACE_GROUP : ZFS_ACE_USER);
7665959Smarks 		} else {
7675331Samw 			acep->a_who = (uid_t)(int64_t)who;
7685959Smarks 		}
7695331Samw 		acep->a_access_mask = access_mask;
7705331Samw 		acep->a_flags = iflags;
7715331Samw 		acep->a_type = type;
7725331Samw 		acep = (ace_t *)((caddr_t)acep + ace_size);
7735331Samw 	}
7745331Samw }
7755331Samw 
7765331Samw static int
7775331Samw zfs_copy_ace_2_oldace(vtype_t obj_type, zfs_acl_t *aclp, ace_t *acep,
7785331Samw     zfs_oldace_t *z_acl, int aclcnt, size_t *size)
7795331Samw {
7805331Samw 	int i;
7815331Samw 	zfs_oldace_t *aceptr = z_acl;
7825331Samw 
7835331Samw 	for (i = 0; i != aclcnt; i++, aceptr++) {
7845331Samw 		aceptr->z_access_mask = acep[i].a_access_mask;
7855331Samw 		aceptr->z_type = acep[i].a_type;
7865331Samw 		aceptr->z_flags = acep[i].a_flags;
7875331Samw 		aceptr->z_fuid = acep[i].a_who;
7885331Samw 		/*
7895331Samw 		 * Make sure ACE is valid
7905331Samw 		 */
7915331Samw 		if (zfs_ace_valid(obj_type, aclp, aceptr->z_type,
7925331Samw 		    aceptr->z_flags) != B_TRUE)
7935331Samw 			return (EINVAL);
7945331Samw 	}
7955331Samw 	*size = (caddr_t)aceptr - (caddr_t)z_acl;
7965331Samw 	return (0);
7975331Samw }
7985331Samw 
7995331Samw /*
8005331Samw  * convert old ACL format to new
8015331Samw  */
8025331Samw void
8039179SMark.Shellenbaum@Sun.COM zfs_acl_xform(znode_t *zp, zfs_acl_t *aclp, cred_t *cr)
8045331Samw {
8055331Samw 	zfs_oldace_t *oldaclp;
8065331Samw 	int i;
8075331Samw 	uint16_t type, iflags;
8085331Samw 	uint32_t access_mask;
8095331Samw 	uint64_t who;
8105331Samw 	void *cookie = NULL;
8115489Smarks 	zfs_acl_node_t *newaclnode;
8125331Samw 
8135331Samw 	ASSERT(aclp->z_version == ZFS_ACL_VERSION_INITIAL);
8145331Samw 	/*
8155331Samw 	 * First create the ACE in a contiguous piece of memory
8165331Samw 	 * for zfs_copy_ace_2_fuid().
8175331Samw 	 *
8185331Samw 	 * We only convert an ACL once, so this won't happen
8195331Samw 	 * everytime.
8205331Samw 	 */
8215331Samw 	oldaclp = kmem_alloc(sizeof (zfs_oldace_t) * aclp->z_acl_count,
8225331Samw 	    KM_SLEEP);
8235331Samw 	i = 0;
8245331Samw 	while (cookie = zfs_acl_next_ace(aclp, cookie, &who,
8255331Samw 	    &access_mask, &iflags, &type)) {
8265331Samw 		oldaclp[i].z_flags = iflags;
8275331Samw 		oldaclp[i].z_type = type;
8285331Samw 		oldaclp[i].z_fuid = who;
8295331Samw 		oldaclp[i++].z_access_mask = access_mask;
8305331Samw 	}
8315331Samw 
8325331Samw 	newaclnode = zfs_acl_node_alloc(aclp->z_acl_count *
8335331Samw 	    sizeof (zfs_object_ace_t));
8345331Samw 	aclp->z_ops = zfs_acl_fuid_ops;
8359179SMark.Shellenbaum@Sun.COM 	VERIFY(zfs_copy_ace_2_fuid(zp->z_zfsvfs, ZTOV(zp)->v_type, aclp,
8369179SMark.Shellenbaum@Sun.COM 	    oldaclp, newaclnode->z_acldata, aclp->z_acl_count,
8379179SMark.Shellenbaum@Sun.COM 	    &newaclnode->z_size, NULL, cr) == 0);
8385331Samw 	newaclnode->z_ace_count = aclp->z_acl_count;
8395331Samw 	aclp->z_version = ZFS_ACL_VERSION;
8405331Samw 	kmem_free(oldaclp, aclp->z_acl_count * sizeof (zfs_oldace_t));
8415331Samw 
8425331Samw 	/*
8435331Samw 	 * Release all previous ACL nodes
8445331Samw 	 */
8455331Samw 
8465489Smarks 	zfs_acl_release_nodes(aclp);
8475489Smarks 
8485331Samw 	list_insert_head(&aclp->z_acl, newaclnode);
8495489Smarks 
8505489Smarks 	aclp->z_acl_bytes = newaclnode->z_size;
8515489Smarks 	aclp->z_acl_count = newaclnode->z_ace_count;
8525489Smarks 
853789Sahrens }
854789Sahrens 
855789Sahrens /*
856789Sahrens  * Convert unix access mask to v4 access mask
857789Sahrens  */
858789Sahrens static uint32_t
859789Sahrens zfs_unix_to_v4(uint32_t access_mask)
860789Sahrens {
861789Sahrens 	uint32_t new_mask = 0;
862789Sahrens 
8635331Samw 	if (access_mask & S_IXOTH)
8645331Samw 		new_mask |= ACE_EXECUTE;
8655331Samw 	if (access_mask & S_IWOTH)
8665331Samw 		new_mask |= ACE_WRITE_DATA;
8675331Samw 	if (access_mask & S_IROTH)
868789Sahrens 		new_mask |= ACE_READ_DATA;
869789Sahrens 	return (new_mask);
870789Sahrens }
871789Sahrens 
872789Sahrens static void
8735331Samw zfs_set_ace(zfs_acl_t *aclp, void *acep, uint32_t access_mask,
8745331Samw     uint16_t access_type, uint64_t fuid, uint16_t entry_type)
875789Sahrens {
8765331Samw 	uint16_t type = entry_type & ACE_TYPE_FLAGS;
8775331Samw 
8785331Samw 	aclp->z_ops.ace_mask_set(acep, access_mask);
8795331Samw 	aclp->z_ops.ace_type_set(acep, access_type);
8805331Samw 	aclp->z_ops.ace_flags_set(acep, entry_type);
8817328SMark.Shellenbaum@Sun.COM 	if ((type != ACE_OWNER && type != OWNING_GROUP &&
8825331Samw 	    type != ACE_EVERYONE))
8835331Samw 		aclp->z_ops.ace_who_set(acep, fuid);
884789Sahrens }
885789Sahrens 
8865331Samw /*
8875331Samw  * Determine mode of file based on ACL.
8885331Samw  * Also, create FUIDs for any User/Group ACEs
8895331Samw  */
89011935SMark.Shellenbaum@Sun.COM uint64_t
89112164SMark.Shellenbaum@Sun.COM zfs_mode_compute(uint64_t fmode, zfs_acl_t *aclp,
89212164SMark.Shellenbaum@Sun.COM     uint64_t *pflags, uint64_t fuid, uint64_t fgid)
893789Sahrens {
8945331Samw 	int		entry_type;
8955331Samw 	mode_t		mode;
8965331Samw 	mode_t		seen = 0;
8975331Samw 	zfs_ace_hdr_t 	*acep = NULL;
8985331Samw 	uint64_t	who;
8995331Samw 	uint16_t	iflags, type;
9005331Samw 	uint32_t	access_mask;
9019981STim.Haley@Sun.COM 	boolean_t	an_exec_denied = B_FALSE;
902789Sahrens 
90311935SMark.Shellenbaum@Sun.COM 	mode = (fmode & (S_IFMT | S_ISUID | S_ISGID | S_ISVTX));
9045331Samw 
9055331Samw 	while (acep = zfs_acl_next_ace(aclp, acep, &who,
9065331Samw 	    &access_mask, &iflags, &type)) {
9074869Smarks 
9087559SMark.Shellenbaum@Sun.COM 		if (!zfs_acl_valid_ace_type(type, iflags))
9097559SMark.Shellenbaum@Sun.COM 			continue;
9107559SMark.Shellenbaum@Sun.COM 
9117328SMark.Shellenbaum@Sun.COM 		entry_type = (iflags & ACE_TYPE_FLAGS);
9127328SMark.Shellenbaum@Sun.COM 
9134869Smarks 		/*
9147328SMark.Shellenbaum@Sun.COM 		 * Skip over owner@, group@ or everyone@ inherit only ACEs
9154869Smarks 		 */
9167328SMark.Shellenbaum@Sun.COM 		if ((iflags & ACE_INHERIT_ONLY_ACE) &&
9177328SMark.Shellenbaum@Sun.COM 		    (entry_type == ACE_OWNER || entry_type == ACE_EVERYONE ||
9187328SMark.Shellenbaum@Sun.COM 		    entry_type == OWNING_GROUP))
9194869Smarks 			continue;
9204869Smarks 
92112164SMark.Shellenbaum@Sun.COM 		if (entry_type == ACE_OWNER || (entry_type == 0 &&
92212164SMark.Shellenbaum@Sun.COM 		    who == fuid)) {
9235331Samw 			if ((access_mask & ACE_READ_DATA) &&
924789Sahrens 			    (!(seen & S_IRUSR))) {
925789Sahrens 				seen |= S_IRUSR;
9265331Samw 				if (type == ALLOW) {
927789Sahrens 					mode |= S_IRUSR;
928789Sahrens 				}
929789Sahrens 			}
9305331Samw 			if ((access_mask & ACE_WRITE_DATA) &&
931789Sahrens 			    (!(seen & S_IWUSR))) {
932789Sahrens 				seen |= S_IWUSR;
9335331Samw 				if (type == ALLOW) {
934789Sahrens 					mode |= S_IWUSR;
935789Sahrens 				}
936789Sahrens 			}
9375331Samw 			if ((access_mask & ACE_EXECUTE) &&
938789Sahrens 			    (!(seen & S_IXUSR))) {
939789Sahrens 				seen |= S_IXUSR;
9405331Samw 				if (type == ALLOW) {
941789Sahrens 					mode |= S_IXUSR;
942789Sahrens 				}
943789Sahrens 			}
94412164SMark.Shellenbaum@Sun.COM 		} else if (entry_type == OWNING_GROUP ||
94512164SMark.Shellenbaum@Sun.COM 		    (entry_type == ACE_IDENTIFIER_GROUP && who == fgid)) {
9465331Samw 			if ((access_mask & ACE_READ_DATA) &&
947789Sahrens 			    (!(seen & S_IRGRP))) {
948789Sahrens 				seen |= S_IRGRP;
9495331Samw 				if (type == ALLOW) {
950789Sahrens 					mode |= S_IRGRP;
951789Sahrens 				}
952789Sahrens 			}
9535331Samw 			if ((access_mask & ACE_WRITE_DATA) &&
954789Sahrens 			    (!(seen & S_IWGRP))) {
955789Sahrens 				seen |= S_IWGRP;
9565331Samw 				if (type == ALLOW) {
957789Sahrens 					mode |= S_IWGRP;
958789Sahrens 				}
959789Sahrens 			}
9605331Samw 			if ((access_mask & ACE_EXECUTE) &&
961789Sahrens 			    (!(seen & S_IXGRP))) {
962789Sahrens 				seen |= S_IXGRP;
9635331Samw 				if (type == ALLOW) {
964789Sahrens 					mode |= S_IXGRP;
965789Sahrens 				}
966789Sahrens 			}
967789Sahrens 		} else if (entry_type == ACE_EVERYONE) {
9685331Samw 			if ((access_mask & ACE_READ_DATA)) {
969789Sahrens 				if (!(seen & S_IRUSR)) {
970789Sahrens 					seen |= S_IRUSR;
9715331Samw 					if (type == ALLOW) {
972789Sahrens 						mode |= S_IRUSR;
973789Sahrens 					}
974789Sahrens 				}
975789Sahrens 				if (!(seen & S_IRGRP)) {
976789Sahrens 					seen |= S_IRGRP;
9775331Samw 					if (type == ALLOW) {
978789Sahrens 						mode |= S_IRGRP;
979789Sahrens 					}
980789Sahrens 				}
981789Sahrens 				if (!(seen & S_IROTH)) {
982789Sahrens 					seen |= S_IROTH;
9835331Samw 					if (type == ALLOW) {
984789Sahrens 						mode |= S_IROTH;
985789Sahrens 					}
986789Sahrens 				}
987789Sahrens 			}
9885331Samw 			if ((access_mask & ACE_WRITE_DATA)) {
989789Sahrens 				if (!(seen & S_IWUSR)) {
990789Sahrens 					seen |= S_IWUSR;
9915331Samw 					if (type == ALLOW) {
992789Sahrens 						mode |= S_IWUSR;
993789Sahrens 					}
994789Sahrens 				}
995789Sahrens 				if (!(seen & S_IWGRP)) {
996789Sahrens 					seen |= S_IWGRP;
9975331Samw 					if (type == ALLOW) {
998789Sahrens 						mode |= S_IWGRP;
999789Sahrens 					}
1000789Sahrens 				}
1001789Sahrens 				if (!(seen & S_IWOTH)) {
1002789Sahrens 					seen |= S_IWOTH;
10035331Samw 					if (type == ALLOW) {
1004789Sahrens 						mode |= S_IWOTH;
1005789Sahrens 					}
1006789Sahrens 				}
1007789Sahrens 			}
10085331Samw 			if ((access_mask & ACE_EXECUTE)) {
1009789Sahrens 				if (!(seen & S_IXUSR)) {
1010789Sahrens 					seen |= S_IXUSR;
10115331Samw 					if (type == ALLOW) {
1012789Sahrens 						mode |= S_IXUSR;
1013789Sahrens 					}
1014789Sahrens 				}
1015789Sahrens 				if (!(seen & S_IXGRP)) {
1016789Sahrens 					seen |= S_IXGRP;
10175331Samw 					if (type == ALLOW) {
1018789Sahrens 						mode |= S_IXGRP;
1019789Sahrens 					}
1020789Sahrens 				}
1021789Sahrens 				if (!(seen & S_IXOTH)) {
1022789Sahrens 					seen |= S_IXOTH;
10235331Samw 					if (type == ALLOW) {
1024789Sahrens 						mode |= S_IXOTH;
1025789Sahrens 					}
1026789Sahrens 				}
1027789Sahrens 			}
10289981STim.Haley@Sun.COM 		} else {
10299981STim.Haley@Sun.COM 			/*
10309981STim.Haley@Sun.COM 			 * Only care if this IDENTIFIER_GROUP or
10319981STim.Haley@Sun.COM 			 * USER ACE denies execute access to someone,
10329981STim.Haley@Sun.COM 			 * mode is not affected
10339981STim.Haley@Sun.COM 			 */
10349981STim.Haley@Sun.COM 			if ((access_mask & ACE_EXECUTE) && type == DENY)
10359981STim.Haley@Sun.COM 				an_exec_denied = B_TRUE;
1036789Sahrens 		}
1037789Sahrens 	}
10389981STim.Haley@Sun.COM 
103910143STim.Haley@Sun.COM 	/*
104010143STim.Haley@Sun.COM 	 * Failure to allow is effectively a deny, so execute permission
104110143STim.Haley@Sun.COM 	 * is denied if it was never mentioned or if we explicitly
104210143STim.Haley@Sun.COM 	 * weren't allowed it.
104310143STim.Haley@Sun.COM 	 */
104410143STim.Haley@Sun.COM 	if (!an_exec_denied &&
104510143STim.Haley@Sun.COM 	    ((seen & ALL_MODE_EXECS) != ALL_MODE_EXECS ||
104610143STim.Haley@Sun.COM 	    (mode & ALL_MODE_EXECS) != ALL_MODE_EXECS))
10479981STim.Haley@Sun.COM 		an_exec_denied = B_TRUE;
10489981STim.Haley@Sun.COM 
10499981STim.Haley@Sun.COM 	if (an_exec_denied)
105011935SMark.Shellenbaum@Sun.COM 		*pflags &= ~ZFS_NO_EXECS_DENIED;
10519981STim.Haley@Sun.COM 	else
105211935SMark.Shellenbaum@Sun.COM 		*pflags |= ZFS_NO_EXECS_DENIED;
10539981STim.Haley@Sun.COM 
1054789Sahrens 	return (mode);
1055789Sahrens }
1056789Sahrens 
1057789Sahrens /*
105810143STim.Haley@Sun.COM  * Read an external acl object.  If the intent is to modify, always
105910143STim.Haley@Sun.COM  * create a new acl and leave any cached acl in place.
1060789Sahrens  */
10611544Seschrock static int
1062*12620SMark.Shellenbaum@Oracle.COM zfs_acl_node_read(znode_t *zp, boolean_t have_lock, zfs_acl_t **aclpp,
1063*12620SMark.Shellenbaum@Oracle.COM     boolean_t will_modify)
1064789Sahrens {
1065789Sahrens 	zfs_acl_t	*aclp;
106611935SMark.Shellenbaum@Sun.COM 	int		aclsize;
106711935SMark.Shellenbaum@Sun.COM 	int		acl_count;
10685331Samw 	zfs_acl_node_t	*aclnode;
106911935SMark.Shellenbaum@Sun.COM 	zfs_acl_phys_t	znode_acl;
107011935SMark.Shellenbaum@Sun.COM 	int		version;
107111935SMark.Shellenbaum@Sun.COM 	int		error;
1072*12620SMark.Shellenbaum@Oracle.COM 	boolean_t	drop_lock = B_FALSE;
1073789Sahrens 
1074789Sahrens 	ASSERT(MUTEX_HELD(&zp->z_acl_lock));
1075789Sahrens 
107610143STim.Haley@Sun.COM 	if (zp->z_acl_cached && !will_modify) {
10779981STim.Haley@Sun.COM 		*aclpp = zp->z_acl_cached;
10789981STim.Haley@Sun.COM 		return (0);
10799981STim.Haley@Sun.COM 	}
10809981STim.Haley@Sun.COM 
1081*12620SMark.Shellenbaum@Oracle.COM 	/*
1082*12620SMark.Shellenbaum@Oracle.COM 	 * close race where znode could be upgrade while trying to
1083*12620SMark.Shellenbaum@Oracle.COM 	 * read the znode attributes.
1084*12620SMark.Shellenbaum@Oracle.COM 	 *
1085*12620SMark.Shellenbaum@Oracle.COM 	 * But this could only happen if the file isn't already an SA
1086*12620SMark.Shellenbaum@Oracle.COM 	 * znode
1087*12620SMark.Shellenbaum@Oracle.COM 	 */
1088*12620SMark.Shellenbaum@Oracle.COM 	if (!zp->z_is_sa && !have_lock) {
1089*12620SMark.Shellenbaum@Oracle.COM 		mutex_enter(&zp->z_lock);
1090*12620SMark.Shellenbaum@Oracle.COM 		drop_lock = B_TRUE;
1091*12620SMark.Shellenbaum@Oracle.COM 	}
1092*12620SMark.Shellenbaum@Oracle.COM 	version = zfs_znode_acl_version(zp);
109311935SMark.Shellenbaum@Sun.COM 
109411935SMark.Shellenbaum@Sun.COM 	if ((error = zfs_acl_znode_info(zp, &aclsize,
1095*12620SMark.Shellenbaum@Oracle.COM 	    &acl_count, &znode_acl)) != 0) {
1096*12620SMark.Shellenbaum@Oracle.COM 		goto done;
1097*12620SMark.Shellenbaum@Oracle.COM 	}
109811935SMark.Shellenbaum@Sun.COM 
109911935SMark.Shellenbaum@Sun.COM 	aclp = zfs_acl_alloc(version);
110011935SMark.Shellenbaum@Sun.COM 
11015331Samw 	aclp->z_acl_count = acl_count;
11025331Samw 	aclp->z_acl_bytes = aclsize;
11035331Samw 
110411935SMark.Shellenbaum@Sun.COM 	aclnode = zfs_acl_node_alloc(aclsize);
110511935SMark.Shellenbaum@Sun.COM 	aclnode->z_ace_count = aclp->z_acl_count;
110611935SMark.Shellenbaum@Sun.COM 	aclnode->z_size = aclsize;
110711935SMark.Shellenbaum@Sun.COM 
110811935SMark.Shellenbaum@Sun.COM 	if (!zp->z_is_sa) {
110911935SMark.Shellenbaum@Sun.COM 		if (znode_acl.z_acl_extern_obj) {
111011935SMark.Shellenbaum@Sun.COM 			error = dmu_read(zp->z_zfsvfs->z_os,
111111935SMark.Shellenbaum@Sun.COM 			    znode_acl.z_acl_extern_obj, 0, aclnode->z_size,
111211935SMark.Shellenbaum@Sun.COM 			    aclnode->z_acldata, DMU_READ_PREFETCH);
111311935SMark.Shellenbaum@Sun.COM 		} else {
111411935SMark.Shellenbaum@Sun.COM 			bcopy(znode_acl.z_ace_data, aclnode->z_acldata,
111511935SMark.Shellenbaum@Sun.COM 			    aclnode->z_size);
111611935SMark.Shellenbaum@Sun.COM 		}
111711935SMark.Shellenbaum@Sun.COM 	} else {
111811935SMark.Shellenbaum@Sun.COM 		error = sa_lookup(zp->z_sa_hdl, SA_ZPL_DACL_ACES(zp->z_zfsvfs),
111911935SMark.Shellenbaum@Sun.COM 		    aclnode->z_acldata, aclnode->z_size);
112011935SMark.Shellenbaum@Sun.COM 	}
112111935SMark.Shellenbaum@Sun.COM 
11221544Seschrock 	if (error != 0) {
11231544Seschrock 		zfs_acl_free(aclp);
112411935SMark.Shellenbaum@Sun.COM 		zfs_acl_node_free(aclnode);
11257294Sperrin 		/* convert checksum errors into IO errors */
11267294Sperrin 		if (error == ECKSUM)
11277294Sperrin 			error = EIO;
1128*12620SMark.Shellenbaum@Oracle.COM 		goto done;
11291544Seschrock 	}
1130789Sahrens 
113111935SMark.Shellenbaum@Sun.COM 	list_insert_head(&aclp->z_acl, aclnode);
113211935SMark.Shellenbaum@Sun.COM 
113310143STim.Haley@Sun.COM 	*aclpp = aclp;
113410143STim.Haley@Sun.COM 	if (!will_modify)
113510143STim.Haley@Sun.COM 		zp->z_acl_cached = aclp;
1136*12620SMark.Shellenbaum@Oracle.COM done:
1137*12620SMark.Shellenbaum@Oracle.COM 	if (drop_lock)
1138*12620SMark.Shellenbaum@Oracle.COM 		mutex_exit(&zp->z_lock);
1139*12620SMark.Shellenbaum@Oracle.COM 	return (error);
1140789Sahrens }
1141789Sahrens 
114211935SMark.Shellenbaum@Sun.COM /*ARGSUSED*/
114311935SMark.Shellenbaum@Sun.COM void
114411935SMark.Shellenbaum@Sun.COM zfs_acl_data_locator(void **dataptr, uint32_t *length, uint32_t buflen,
114511935SMark.Shellenbaum@Sun.COM     boolean_t start, void *userdata)
114611935SMark.Shellenbaum@Sun.COM {
114711935SMark.Shellenbaum@Sun.COM 	zfs_acl_locator_cb_t *cb = (zfs_acl_locator_cb_t *)userdata;
114811935SMark.Shellenbaum@Sun.COM 
114911935SMark.Shellenbaum@Sun.COM 	if (start) {
115011935SMark.Shellenbaum@Sun.COM 		cb->cb_acl_node = list_head(&cb->cb_aclp->z_acl);
115111935SMark.Shellenbaum@Sun.COM 	} else {
115211935SMark.Shellenbaum@Sun.COM 		cb->cb_acl_node = list_next(&cb->cb_aclp->z_acl,
115311935SMark.Shellenbaum@Sun.COM 		    cb->cb_acl_node);
115411935SMark.Shellenbaum@Sun.COM 	}
115511935SMark.Shellenbaum@Sun.COM 	*dataptr = cb->cb_acl_node->z_acldata;
115611935SMark.Shellenbaum@Sun.COM 	*length = cb->cb_acl_node->z_size;
115711935SMark.Shellenbaum@Sun.COM }
115811935SMark.Shellenbaum@Sun.COM 
115912164SMark.Shellenbaum@Sun.COM 
116012164SMark.Shellenbaum@Sun.COM static int
116112164SMark.Shellenbaum@Sun.COM zfs_acl_get_owner_fuids(znode_t *zp, uint64_t *fuid, uint64_t *fgid)
116212164SMark.Shellenbaum@Sun.COM {
116312164SMark.Shellenbaum@Sun.COM 	int count = 0;
116412164SMark.Shellenbaum@Sun.COM 	sa_bulk_attr_t	bulk[2];
116512164SMark.Shellenbaum@Sun.COM 	int error;
116612164SMark.Shellenbaum@Sun.COM 
116712164SMark.Shellenbaum@Sun.COM 	if (IS_EPHEMERAL(zp->z_uid) || IS_EPHEMERAL(zp->z_gid)) {
116812164SMark.Shellenbaum@Sun.COM 		SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_UID(zp->z_zfsvfs), NULL,
116912164SMark.Shellenbaum@Sun.COM 		    &fuid, sizeof (fuid));
117012164SMark.Shellenbaum@Sun.COM 		SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_GID(zp->z_zfsvfs), NULL,
117112164SMark.Shellenbaum@Sun.COM 		    &fgid, sizeof (fuid));
117212164SMark.Shellenbaum@Sun.COM 		if ((error = sa_bulk_lookup(zp->z_sa_hdl, bulk, count)) != 0) {
117312164SMark.Shellenbaum@Sun.COM 			return (error);
117412164SMark.Shellenbaum@Sun.COM 		}
117512164SMark.Shellenbaum@Sun.COM 	} else {
117612164SMark.Shellenbaum@Sun.COM 		*fuid = zp->z_uid;
117712164SMark.Shellenbaum@Sun.COM 		*fgid = zp->z_gid;
117812164SMark.Shellenbaum@Sun.COM 	}
117912164SMark.Shellenbaum@Sun.COM 	return (0);
118012164SMark.Shellenbaum@Sun.COM }
118112164SMark.Shellenbaum@Sun.COM 
118212164SMark.Shellenbaum@Sun.COM int
118312164SMark.Shellenbaum@Sun.COM zfs_acl_chown_setattr(znode_t *zp)
118412164SMark.Shellenbaum@Sun.COM {
118512164SMark.Shellenbaum@Sun.COM 	int error;
118612164SMark.Shellenbaum@Sun.COM 	zfs_acl_t *aclp;
118712164SMark.Shellenbaum@Sun.COM 	uint64_t fuid, fgid;
118812164SMark.Shellenbaum@Sun.COM 
1189*12620SMark.Shellenbaum@Oracle.COM 	ASSERT(MUTEX_HELD(&zp->z_lock));
1190*12620SMark.Shellenbaum@Oracle.COM 	ASSERT(MUTEX_HELD(&zp->z_acl_lock));
119112164SMark.Shellenbaum@Sun.COM 	if ((error = zfs_acl_get_owner_fuids(zp, &fuid, &fgid)) != 0)
119212164SMark.Shellenbaum@Sun.COM 		return (error);
119312164SMark.Shellenbaum@Sun.COM 
1194*12620SMark.Shellenbaum@Oracle.COM 	if ((error = zfs_acl_node_read(zp, B_TRUE, &aclp, B_FALSE)) == 0)
119512164SMark.Shellenbaum@Sun.COM 		zp->z_mode = zfs_mode_compute(zp->z_mode, aclp,
119612164SMark.Shellenbaum@Sun.COM 		    &zp->z_pflags, fuid, fgid);
119712164SMark.Shellenbaum@Sun.COM 	return (error);
119812164SMark.Shellenbaum@Sun.COM }
119912164SMark.Shellenbaum@Sun.COM 
1200789Sahrens /*
12015331Samw  * common code for setting ACLs.
1202789Sahrens  *
1203789Sahrens  * This function is called from zfs_mode_update, zfs_perm_init, and zfs_setacl.
1204789Sahrens  * zfs_setacl passes a non-NULL inherit pointer (ihp) to indicate that it's
1205789Sahrens  * already checked the acl and knows whether to inherit.
1206789Sahrens  */
1207789Sahrens int
12089179SMark.Shellenbaum@Sun.COM zfs_aclset_common(znode_t *zp, zfs_acl_t *aclp, cred_t *cr, dmu_tx_t *tx)
1209789Sahrens {
121011935SMark.Shellenbaum@Sun.COM 	int			error;
121111935SMark.Shellenbaum@Sun.COM 	zfsvfs_t		*zfsvfs = zp->z_zfsvfs;
121211935SMark.Shellenbaum@Sun.COM 	dmu_object_type_t	otype;
121311935SMark.Shellenbaum@Sun.COM 	zfs_acl_locator_cb_t	locate = { 0 };
121411935SMark.Shellenbaum@Sun.COM 	uint64_t		mode;
121511935SMark.Shellenbaum@Sun.COM 	sa_bulk_attr_t		bulk[5];
121611935SMark.Shellenbaum@Sun.COM 	uint64_t		ctime[2];
121711935SMark.Shellenbaum@Sun.COM 	int			count = 0;
121812164SMark.Shellenbaum@Sun.COM 	uint64_t		fuid, fgid;
121911935SMark.Shellenbaum@Sun.COM 
122011935SMark.Shellenbaum@Sun.COM 	mode = zp->z_mode;
122112164SMark.Shellenbaum@Sun.COM 
122212164SMark.Shellenbaum@Sun.COM 	if ((error = zfs_acl_get_owner_fuids(zp, &fuid, &fgid)) != 0)
122312164SMark.Shellenbaum@Sun.COM 		return (error);
122412164SMark.Shellenbaum@Sun.COM 
122512164SMark.Shellenbaum@Sun.COM 	mode = zfs_mode_compute(mode, aclp, &zp->z_pflags, fuid, fgid);
122611935SMark.Shellenbaum@Sun.COM 
122711935SMark.Shellenbaum@Sun.COM 	zp->z_mode = mode;
122811935SMark.Shellenbaum@Sun.COM 	SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_MODE(zfsvfs), NULL,
122911935SMark.Shellenbaum@Sun.COM 	    &mode, sizeof (mode));
123011935SMark.Shellenbaum@Sun.COM 	SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_FLAGS(zfsvfs), NULL,
123111935SMark.Shellenbaum@Sun.COM 	    &zp->z_pflags, sizeof (zp->z_pflags));
123211935SMark.Shellenbaum@Sun.COM 	SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_CTIME(zfsvfs), NULL,
123311935SMark.Shellenbaum@Sun.COM 	    &ctime, sizeof (ctime));
1234789Sahrens 
123510143STim.Haley@Sun.COM 	if (zp->z_acl_cached) {
12369981STim.Haley@Sun.COM 		zfs_acl_free(zp->z_acl_cached);
12379981STim.Haley@Sun.COM 		zp->z_acl_cached = NULL;
12389981STim.Haley@Sun.COM 	}
12399981STim.Haley@Sun.COM 
1240789Sahrens 	/*
124111935SMark.Shellenbaum@Sun.COM 	 * Upgrade needed?
1242789Sahrens 	 */
12435331Samw 	if (!zfsvfs->z_use_fuids) {
12445331Samw 		otype = DMU_OT_OLDACL;
12455331Samw 	} else {
12465331Samw 		if ((aclp->z_version == ZFS_ACL_VERSION_INITIAL) &&
12475331Samw 		    (zfsvfs->z_version >= ZPL_VERSION_FUID))
12489179SMark.Shellenbaum@Sun.COM 			zfs_acl_xform(zp, aclp, cr);
12495331Samw 		ASSERT(aclp->z_version >= ZFS_ACL_VERSION_FUID);
12505331Samw 		otype = DMU_OT_ACL;
12515331Samw 	}
12525331Samw 
125311935SMark.Shellenbaum@Sun.COM 	/*
125411935SMark.Shellenbaum@Sun.COM 	 * Arrgh, we have to handle old on disk format
125511935SMark.Shellenbaum@Sun.COM 	 * as well as newer (preferred) SA format.
125611935SMark.Shellenbaum@Sun.COM 	 */
125711935SMark.Shellenbaum@Sun.COM 
125811935SMark.Shellenbaum@Sun.COM 	if (zp->z_is_sa) { /* the easy case, just update the ACL attribute */
125911935SMark.Shellenbaum@Sun.COM 		locate.cb_aclp = aclp;
126011935SMark.Shellenbaum@Sun.COM 		SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_DACL_ACES(zfsvfs),
126111935SMark.Shellenbaum@Sun.COM 		    zfs_acl_data_locator, &locate, aclp->z_acl_bytes);
126211935SMark.Shellenbaum@Sun.COM 		SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_DACL_COUNT(zfsvfs),
126311935SMark.Shellenbaum@Sun.COM 		    NULL, &aclp->z_acl_count, sizeof (uint64_t));
126411935SMark.Shellenbaum@Sun.COM 	} else { /* Painful legacy way */
126511935SMark.Shellenbaum@Sun.COM 		zfs_acl_node_t *aclnode;
126611935SMark.Shellenbaum@Sun.COM 		uint64_t off = 0;
126711935SMark.Shellenbaum@Sun.COM 		zfs_acl_phys_t acl_phys;
126811935SMark.Shellenbaum@Sun.COM 		uint64_t aoid;
126911935SMark.Shellenbaum@Sun.COM 
127011935SMark.Shellenbaum@Sun.COM 		if ((error = sa_lookup(zp->z_sa_hdl, SA_ZPL_ZNODE_ACL(zfsvfs),
127111935SMark.Shellenbaum@Sun.COM 		    &acl_phys, sizeof (acl_phys))) != 0)
127211935SMark.Shellenbaum@Sun.COM 			return (error);
127311935SMark.Shellenbaum@Sun.COM 
127411935SMark.Shellenbaum@Sun.COM 		aoid = acl_phys.z_acl_extern_obj;
127511935SMark.Shellenbaum@Sun.COM 
127611935SMark.Shellenbaum@Sun.COM 		if (aclp->z_acl_bytes > ZFS_ACE_SPACE) {
127711935SMark.Shellenbaum@Sun.COM 			/*
127811935SMark.Shellenbaum@Sun.COM 			 * If ACL was previously external and we are now
127911935SMark.Shellenbaum@Sun.COM 			 * converting to new ACL format then release old
128011935SMark.Shellenbaum@Sun.COM 			 * ACL object and create a new one.
128111935SMark.Shellenbaum@Sun.COM 			 */
128211935SMark.Shellenbaum@Sun.COM 			if (aoid &&
128311935SMark.Shellenbaum@Sun.COM 			    aclp->z_version != acl_phys.z_acl_version) {
128411935SMark.Shellenbaum@Sun.COM 				error = dmu_object_free(zfsvfs->z_os, aoid, tx);
128511935SMark.Shellenbaum@Sun.COM 				if (error)
128611935SMark.Shellenbaum@Sun.COM 					return (error);
128711935SMark.Shellenbaum@Sun.COM 				aoid = 0;
128811935SMark.Shellenbaum@Sun.COM 			}
128911935SMark.Shellenbaum@Sun.COM 			if (aoid == 0) {
129011935SMark.Shellenbaum@Sun.COM 				aoid = dmu_object_alloc(zfsvfs->z_os,
129111935SMark.Shellenbaum@Sun.COM 				    otype, aclp->z_acl_bytes,
129211935SMark.Shellenbaum@Sun.COM 				    otype == DMU_OT_ACL ?
129311935SMark.Shellenbaum@Sun.COM 				    DMU_OT_SYSACL : DMU_OT_NONE,
129411935SMark.Shellenbaum@Sun.COM 				    otype == DMU_OT_ACL ?
129511935SMark.Shellenbaum@Sun.COM 				    DN_MAX_BONUSLEN : 0, tx);
129611935SMark.Shellenbaum@Sun.COM 			} else {
129711935SMark.Shellenbaum@Sun.COM 				(void) dmu_object_set_blocksize(zfsvfs->z_os,
129811935SMark.Shellenbaum@Sun.COM 				    aoid, aclp->z_acl_bytes, 0, tx);
129911935SMark.Shellenbaum@Sun.COM 			}
130011935SMark.Shellenbaum@Sun.COM 			acl_phys.z_acl_extern_obj = aoid;
130111935SMark.Shellenbaum@Sun.COM 			for (aclnode = list_head(&aclp->z_acl); aclnode;
130211935SMark.Shellenbaum@Sun.COM 			    aclnode = list_next(&aclp->z_acl, aclnode)) {
130311935SMark.Shellenbaum@Sun.COM 				if (aclnode->z_ace_count == 0)
130411935SMark.Shellenbaum@Sun.COM 					continue;
130511935SMark.Shellenbaum@Sun.COM 				dmu_write(zfsvfs->z_os, aoid, off,
130611935SMark.Shellenbaum@Sun.COM 				    aclnode->z_size, aclnode->z_acldata, tx);
130711935SMark.Shellenbaum@Sun.COM 				off += aclnode->z_size;
130811935SMark.Shellenbaum@Sun.COM 			}
1309789Sahrens 		} else {
131011935SMark.Shellenbaum@Sun.COM 			void *start = acl_phys.z_ace_data;
131111935SMark.Shellenbaum@Sun.COM 			/*
131211935SMark.Shellenbaum@Sun.COM 			 * Migrating back embedded?
131311935SMark.Shellenbaum@Sun.COM 			 */
131411935SMark.Shellenbaum@Sun.COM 			if (acl_phys.z_acl_extern_obj) {
131511935SMark.Shellenbaum@Sun.COM 				error = dmu_object_free(zfsvfs->z_os,
131611935SMark.Shellenbaum@Sun.COM 				    acl_phys.z_acl_extern_obj, tx);
131711935SMark.Shellenbaum@Sun.COM 				if (error)
131811935SMark.Shellenbaum@Sun.COM 					return (error);
131911935SMark.Shellenbaum@Sun.COM 				acl_phys.z_acl_extern_obj = 0;
132011935SMark.Shellenbaum@Sun.COM 			}
132111935SMark.Shellenbaum@Sun.COM 
132211935SMark.Shellenbaum@Sun.COM 			for (aclnode = list_head(&aclp->z_acl); aclnode;
132311935SMark.Shellenbaum@Sun.COM 			    aclnode = list_next(&aclp->z_acl, aclnode)) {
132411935SMark.Shellenbaum@Sun.COM 				if (aclnode->z_ace_count == 0)
132511935SMark.Shellenbaum@Sun.COM 					continue;
132611935SMark.Shellenbaum@Sun.COM 				bcopy(aclnode->z_acldata, start,
132711935SMark.Shellenbaum@Sun.COM 				    aclnode->z_size);
132811935SMark.Shellenbaum@Sun.COM 				start = (caddr_t)start + aclnode->z_size;
132911935SMark.Shellenbaum@Sun.COM 			}
13305331Samw 		}
1331789Sahrens 		/*
133211935SMark.Shellenbaum@Sun.COM 		 * If Old version then swap count/bytes to match old
133311935SMark.Shellenbaum@Sun.COM 		 * layout of znode_acl_phys_t.
1334789Sahrens 		 */
133511935SMark.Shellenbaum@Sun.COM 		if (aclp->z_version == ZFS_ACL_VERSION_INITIAL) {
133611935SMark.Shellenbaum@Sun.COM 			acl_phys.z_acl_size = aclp->z_acl_count;
133711935SMark.Shellenbaum@Sun.COM 			acl_phys.z_acl_count = aclp->z_acl_bytes;
133811935SMark.Shellenbaum@Sun.COM 		} else {
133911935SMark.Shellenbaum@Sun.COM 			acl_phys.z_acl_size = aclp->z_acl_bytes;
134011935SMark.Shellenbaum@Sun.COM 			acl_phys.z_acl_count = aclp->z_acl_count;
1341789Sahrens 		}
134211935SMark.Shellenbaum@Sun.COM 		acl_phys.z_acl_version = aclp->z_version;
134311935SMark.Shellenbaum@Sun.COM 
134411935SMark.Shellenbaum@Sun.COM 		SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_ZNODE_ACL(zfsvfs), NULL,
134511935SMark.Shellenbaum@Sun.COM 		    &acl_phys, sizeof (acl_phys));
1346789Sahrens 	}
1347905Smarks 
13485331Samw 	/*
13495331Samw 	 * Replace ACL wide bits, but first clear them.
13505331Samw 	 */
135111935SMark.Shellenbaum@Sun.COM 	zp->z_pflags &= ~ZFS_ACL_WIDE_FLAGS;
135211935SMark.Shellenbaum@Sun.COM 
135311935SMark.Shellenbaum@Sun.COM 	zp->z_pflags |= aclp->z_hints;
13545331Samw 
13555331Samw 	if (ace_trivial_common(aclp, 0, zfs_ace_walk) == 0)
135611935SMark.Shellenbaum@Sun.COM 		zp->z_pflags |= ZFS_ACL_TRIVIAL;
135711935SMark.Shellenbaum@Sun.COM 
135811935SMark.Shellenbaum@Sun.COM 	zfs_tstamp_update_setup(zp, STATE_CHANGED, NULL, ctime, B_TRUE);
135911935SMark.Shellenbaum@Sun.COM 	return (sa_bulk_update(zp->z_sa_hdl, bulk, count, tx));
1360789Sahrens }
1361789Sahrens 
1362789Sahrens /*
1363789Sahrens  * Update access mask for prepended ACE
1364789Sahrens  *
1365789Sahrens  * This applies the "groupmask" value for aclmode property.
1366789Sahrens  */
1367789Sahrens static void
13685331Samw zfs_acl_prepend_fixup(zfs_acl_t *aclp, void  *acep, void  *origacep,
13695331Samw     mode_t mode, uint64_t owner)
1370789Sahrens {
1371789Sahrens 	int	rmask, wmask, xmask;
1372789Sahrens 	int	user_ace;
13735331Samw 	uint16_t aceflags;
13745331Samw 	uint32_t origmask, acepmask;
13755331Samw 	uint64_t fuid;
1376789Sahrens 
13775331Samw 	aceflags = aclp->z_ops.ace_flags_get(acep);
13785331Samw 	fuid = aclp->z_ops.ace_who_get(acep);
13795331Samw 	origmask = aclp->z_ops.ace_mask_get(origacep);
13805331Samw 	acepmask = aclp->z_ops.ace_mask_get(acep);
13815331Samw 
13825331Samw 	user_ace = (!(aceflags &
1383789Sahrens 	    (ACE_OWNER|ACE_GROUP|ACE_IDENTIFIER_GROUP)));
1384789Sahrens 
13855331Samw 	if (user_ace && (fuid == owner)) {
1386789Sahrens 		rmask = S_IRUSR;
1387789Sahrens 		wmask = S_IWUSR;
1388789Sahrens 		xmask = S_IXUSR;
1389789Sahrens 	} else {
1390789Sahrens 		rmask = S_IRGRP;
1391789Sahrens 		wmask = S_IWGRP;
1392789Sahrens 		xmask = S_IXGRP;
1393789Sahrens 	}
1394789Sahrens 
13955331Samw 	if (origmask & ACE_READ_DATA) {
13965331Samw 		if (mode & rmask) {
13975331Samw 			acepmask &= ~ACE_READ_DATA;
13985331Samw 		} else {
13995331Samw 			acepmask |= ACE_READ_DATA;
14005331Samw 		}
1401789Sahrens 	}
1402789Sahrens 
14035331Samw 	if (origmask & ACE_WRITE_DATA) {
14045331Samw 		if (mode & wmask) {
14055331Samw 			acepmask &= ~ACE_WRITE_DATA;
14065331Samw 		} else {
14075331Samw 			acepmask |= ACE_WRITE_DATA;
14085331Samw 		}
1409789Sahrens 	}
1410789Sahrens 
14115331Samw 	if (origmask & ACE_APPEND_DATA) {
14125331Samw 		if (mode & wmask) {
14135331Samw 			acepmask &= ~ACE_APPEND_DATA;
14145331Samw 		} else {
14155331Samw 			acepmask |= ACE_APPEND_DATA;
14165331Samw 		}
1417789Sahrens 	}
1418789Sahrens 
14195331Samw 	if (origmask & ACE_EXECUTE) {
14205331Samw 		if (mode & xmask) {
14215331Samw 			acepmask &= ~ACE_EXECUTE;
14225331Samw 		} else {
14235331Samw 			acepmask |= ACE_EXECUTE;
14245331Samw 		}
1425789Sahrens 	}
14265331Samw 	aclp->z_ops.ace_mask_set(acep, acepmask);
1427789Sahrens }
1428789Sahrens 
1429789Sahrens static void
143012164SMark.Shellenbaum@Sun.COM zfs_acl_chmod(zfsvfs_t *zfsvfs, uint64_t mode, zfs_acl_t *aclp)
1431789Sahrens {
143212164SMark.Shellenbaum@Sun.COM 	void		*acep = NULL;
14335331Samw 	uint64_t	who;
143412164SMark.Shellenbaum@Sun.COM 	int		new_count, new_bytes;
143512164SMark.Shellenbaum@Sun.COM 	int		ace_size;
1436789Sahrens 	int 		entry_type;
14375331Samw 	uint16_t	iflags, type;
14385331Samw 	uint32_t	access_mask;
143912164SMark.Shellenbaum@Sun.COM 	zfs_acl_node_t	*newnode;
144012164SMark.Shellenbaum@Sun.COM 	size_t 		abstract_size = aclp->z_ops.ace_abstract_size();
144112164SMark.Shellenbaum@Sun.COM 	void 		*zacep;
144212164SMark.Shellenbaum@Sun.COM 	uint32_t 	owner, group, everyone;
144312164SMark.Shellenbaum@Sun.COM 	uint32_t	deny1, deny2, allow0;
1444789Sahrens 
144512164SMark.Shellenbaum@Sun.COM 	new_count = new_bytes = 0;
144612164SMark.Shellenbaum@Sun.COM 
144712164SMark.Shellenbaum@Sun.COM 	acl_trivial_access_masks((mode_t)mode, &allow0, &deny1, &deny2,
144812164SMark.Shellenbaum@Sun.COM 	    &owner, &group, &everyone);
144912164SMark.Shellenbaum@Sun.COM 
145012164SMark.Shellenbaum@Sun.COM 	newnode = zfs_acl_node_alloc((abstract_size * 6) + aclp->z_acl_bytes);
145112164SMark.Shellenbaum@Sun.COM 
145212164SMark.Shellenbaum@Sun.COM 	zacep = newnode->z_acldata;
145312164SMark.Shellenbaum@Sun.COM 	if (allow0) {
145412164SMark.Shellenbaum@Sun.COM 		zfs_set_ace(aclp, zacep, allow0, ALLOW, -1, ACE_OWNER);
145512164SMark.Shellenbaum@Sun.COM 		zacep = (void *)((uintptr_t)zacep + abstract_size);
145612164SMark.Shellenbaum@Sun.COM 		new_count++;
145712164SMark.Shellenbaum@Sun.COM 		new_bytes += abstract_size;
145812164SMark.Shellenbaum@Sun.COM 	} if (deny1) {
145912164SMark.Shellenbaum@Sun.COM 		zfs_set_ace(aclp, zacep, deny1, DENY, -1, ACE_OWNER);
146012164SMark.Shellenbaum@Sun.COM 		zacep = (void *)((uintptr_t)zacep + abstract_size);
146112164SMark.Shellenbaum@Sun.COM 		new_count++;
146212164SMark.Shellenbaum@Sun.COM 		new_bytes += abstract_size;
146312164SMark.Shellenbaum@Sun.COM 	}
146412164SMark.Shellenbaum@Sun.COM 	if (deny2) {
146512164SMark.Shellenbaum@Sun.COM 		zfs_set_ace(aclp, zacep, deny2, DENY, -1, OWNING_GROUP);
146612164SMark.Shellenbaum@Sun.COM 		zacep = (void *)((uintptr_t)zacep + abstract_size);
146712164SMark.Shellenbaum@Sun.COM 		new_count++;
146812164SMark.Shellenbaum@Sun.COM 		new_bytes += abstract_size;
146912164SMark.Shellenbaum@Sun.COM 	}
14705489Smarks 
14715331Samw 	while (acep = zfs_acl_next_ace(aclp, acep, &who, &access_mask,
14725331Samw 	    &iflags, &type)) {
147312164SMark.Shellenbaum@Sun.COM 		uint16_t inherit_flags;
14745331Samw 
14755331Samw 		entry_type = (iflags & ACE_TYPE_FLAGS);
147612164SMark.Shellenbaum@Sun.COM 		inherit_flags = (iflags & ALL_INHERIT);
147712164SMark.Shellenbaum@Sun.COM 
147812164SMark.Shellenbaum@Sun.COM 		if ((entry_type == ACE_OWNER || entry_type == ACE_EVERYONE ||
147912164SMark.Shellenbaum@Sun.COM 		    (entry_type == OWNING_GROUP)) &&
148012164SMark.Shellenbaum@Sun.COM 		    ((inherit_flags & ACE_INHERIT_ONLY_ACE) == 0)) {
148112164SMark.Shellenbaum@Sun.COM 			continue;
148212164SMark.Shellenbaum@Sun.COM 		}
1483789Sahrens 
14845331Samw 		if ((type != ALLOW && type != DENY) ||
148512164SMark.Shellenbaum@Sun.COM 		    (inherit_flags & ACE_INHERIT_ONLY_ACE)) {
148612164SMark.Shellenbaum@Sun.COM 			if (inherit_flags)
14875331Samw 				aclp->z_hints |= ZFS_INHERIT_ACE;
14885331Samw 			switch (type) {
14895331Samw 			case ACE_ACCESS_ALLOWED_OBJECT_ACE_TYPE:
14905331Samw 			case ACE_ACCESS_DENIED_OBJECT_ACE_TYPE:
14915331Samw 			case ACE_SYSTEM_AUDIT_OBJECT_ACE_TYPE:
14925331Samw 			case ACE_SYSTEM_ALARM_OBJECT_ACE_TYPE:
14935331Samw 				aclp->z_hints |= ZFS_ACL_OBJ_ACE;
14945331Samw 				break;
14955331Samw 			}
1496789Sahrens 		} else {
1497789Sahrens 
149812164SMark.Shellenbaum@Sun.COM 			/*
149912164SMark.Shellenbaum@Sun.COM 			 * Limit permissions to be no greater than
150012164SMark.Shellenbaum@Sun.COM 			 * group permissions
150112164SMark.Shellenbaum@Sun.COM 			 */
150212164SMark.Shellenbaum@Sun.COM 			if (zfsvfs->z_acl_inherit == ZFS_ACL_RESTRICTED) {
150312164SMark.Shellenbaum@Sun.COM 				if (!(mode & S_IRGRP))
150412164SMark.Shellenbaum@Sun.COM 					access_mask &= ~ACE_READ_DATA;
150512164SMark.Shellenbaum@Sun.COM 				if (!(mode & S_IWGRP))
150612164SMark.Shellenbaum@Sun.COM 					access_mask &=
150712164SMark.Shellenbaum@Sun.COM 					    ~(ACE_WRITE_DATA|ACE_APPEND_DATA);
150812164SMark.Shellenbaum@Sun.COM 				if (!(mode & S_IXGRP))
150912164SMark.Shellenbaum@Sun.COM 					access_mask &= ~ACE_EXECUTE;
151012164SMark.Shellenbaum@Sun.COM 				access_mask &=
151112164SMark.Shellenbaum@Sun.COM 				    ~(ACE_WRITE_OWNER|ACE_WRITE_ACL|
151212164SMark.Shellenbaum@Sun.COM 				    ACE_WRITE_ATTRIBUTES|ACE_WRITE_NAMED_ATTRS);
1513789Sahrens 			}
1514789Sahrens 		}
151512164SMark.Shellenbaum@Sun.COM 		zfs_set_ace(aclp, zacep, access_mask, type, who, iflags);
151612164SMark.Shellenbaum@Sun.COM 		ace_size = aclp->z_ops.ace_size(acep);
151712164SMark.Shellenbaum@Sun.COM 		zacep = (void *)((uintptr_t)zacep + ace_size);
151812164SMark.Shellenbaum@Sun.COM 		new_count++;
151912164SMark.Shellenbaum@Sun.COM 		new_bytes += ace_size;
1520789Sahrens 	}
152112164SMark.Shellenbaum@Sun.COM 	zfs_set_ace(aclp, zacep, owner, 0, -1, ACE_OWNER);
152212164SMark.Shellenbaum@Sun.COM 	zacep = (void *)((uintptr_t)zacep + abstract_size);
152312164SMark.Shellenbaum@Sun.COM 	zfs_set_ace(aclp, zacep, group, 0, -1, OWNING_GROUP);
152412164SMark.Shellenbaum@Sun.COM 	zacep = (void *)((uintptr_t)zacep + abstract_size);
152512164SMark.Shellenbaum@Sun.COM 	zfs_set_ace(aclp, zacep, everyone, 0, -1, ACE_EVERYONE);
1526789Sahrens 
152712164SMark.Shellenbaum@Sun.COM 	new_count += 3;
152812164SMark.Shellenbaum@Sun.COM 	new_bytes += abstract_size * 3;
152912164SMark.Shellenbaum@Sun.COM 	zfs_acl_release_nodes(aclp);
153012164SMark.Shellenbaum@Sun.COM 	aclp->z_acl_count = new_count;
153112164SMark.Shellenbaum@Sun.COM 	aclp->z_acl_bytes = new_bytes;
153212164SMark.Shellenbaum@Sun.COM 	newnode->z_ace_count = new_count;
153312164SMark.Shellenbaum@Sun.COM 	newnode->z_size = new_bytes;
153412164SMark.Shellenbaum@Sun.COM 	list_insert_tail(&aclp->z_acl, newnode);
1535789Sahrens }
1536789Sahrens 
1537789Sahrens int
15385824Smarks zfs_acl_chmod_setattr(znode_t *zp, zfs_acl_t **aclp, uint64_t mode)
1539789Sahrens {
1540*12620SMark.Shellenbaum@Oracle.COM 	mutex_enter(&zp->z_acl_lock);
15415824Smarks 	mutex_enter(&zp->z_lock);
154212164SMark.Shellenbaum@Sun.COM 	*aclp = zfs_acl_alloc(zfs_acl_version_zp(zp));
154312164SMark.Shellenbaum@Sun.COM 	(*aclp)->z_hints = zp->z_pflags & V4_ACL_WIDE_FLAGS;
154412164SMark.Shellenbaum@Sun.COM 	zfs_acl_chmod(zp->z_zfsvfs, mode, *aclp);
1545*12620SMark.Shellenbaum@Oracle.COM 	mutex_exit(&zp->z_lock);
1546789Sahrens 	mutex_exit(&zp->z_acl_lock);
154712164SMark.Shellenbaum@Sun.COM 	ASSERT(*aclp);
154812164SMark.Shellenbaum@Sun.COM 	return (0);
1549789Sahrens }
1550789Sahrens 
1551789Sahrens /*
1552789Sahrens  * strip off write_owner and write_acl
1553789Sahrens  */
1554789Sahrens static void
15556385Smarks zfs_restricted_update(zfsvfs_t *zfsvfs, zfs_acl_t *aclp, void *acep)
1556789Sahrens {
15575331Samw 	uint32_t mask = aclp->z_ops.ace_mask_get(acep);
15585331Samw 
15596385Smarks 	if ((zfsvfs->z_acl_inherit == ZFS_ACL_RESTRICTED) &&
15605331Samw 	    (aclp->z_ops.ace_type_get(acep) == ALLOW)) {
15616385Smarks 		mask &= ~RESTRICTED_CLEAR;
15625331Samw 		aclp->z_ops.ace_mask_set(acep, mask);
15635331Samw 	}
15645331Samw }
15655331Samw 
15665331Samw /*
15675331Samw  * Should ACE be inherited?
15685331Samw  */
15695331Samw static int
15709179SMark.Shellenbaum@Sun.COM zfs_ace_can_use(vtype_t vtype, uint16_t acep_flags)
15715331Samw {
15725331Samw 	int	iflags = (acep_flags & 0xf);
15735331Samw 
15745331Samw 	if ((vtype == VDIR) && (iflags & ACE_DIRECTORY_INHERIT_ACE))
15755331Samw 		return (1);
15765331Samw 	else if (iflags & ACE_FILE_INHERIT_ACE)
15775331Samw 		return (!((vtype == VDIR) &&
15785331Samw 		    (iflags & ACE_NO_PROPAGATE_INHERIT_ACE)));
15795331Samw 	return (0);
1580789Sahrens }
1581789Sahrens 
1582789Sahrens /*
1583789Sahrens  * inherit inheritable ACEs from parent
1584789Sahrens  */
1585789Sahrens static zfs_acl_t *
15869179SMark.Shellenbaum@Sun.COM zfs_acl_inherit(zfsvfs_t *zfsvfs, vtype_t vtype, zfs_acl_t *paclp,
15879179SMark.Shellenbaum@Sun.COM     uint64_t mode, boolean_t *need_chmod)
1588789Sahrens {
15895331Samw 	void		*pacep;
159012164SMark.Shellenbaum@Sun.COM 	void		*acep;
159112164SMark.Shellenbaum@Sun.COM 	zfs_acl_node_t  *aclnode;
1592789Sahrens 	zfs_acl_t	*aclp = NULL;
15935331Samw 	uint64_t	who;
15945331Samw 	uint32_t	access_mask;
15955331Samw 	uint16_t	iflags, newflags, type;
15965331Samw 	size_t		ace_size;
15975331Samw 	void		*data1, *data2;
15985331Samw 	size_t		data1sz, data2sz;
15999179SMark.Shellenbaum@Sun.COM 	boolean_t	vdir = vtype == VDIR;
16009179SMark.Shellenbaum@Sun.COM 	boolean_t	vreg = vtype == VREG;
16018053SMark.Shellenbaum@Sun.COM 	boolean_t	passthrough, passthrough_x, noallow;
16028053SMark.Shellenbaum@Sun.COM 
16038053SMark.Shellenbaum@Sun.COM 	passthrough_x =
16048053SMark.Shellenbaum@Sun.COM 	    zfsvfs->z_acl_inherit == ZFS_ACL_PASSTHROUGH_X;
16058053SMark.Shellenbaum@Sun.COM 	passthrough = passthrough_x ||
16068053SMark.Shellenbaum@Sun.COM 	    zfsvfs->z_acl_inherit == ZFS_ACL_PASSTHROUGH;
16078053SMark.Shellenbaum@Sun.COM 	noallow =
16088053SMark.Shellenbaum@Sun.COM 	    zfsvfs->z_acl_inherit == ZFS_ACL_NOALLOW;
1609789Sahrens 
16106385Smarks 	*need_chmod = B_TRUE;
16115331Samw 	pacep = NULL;
16127559SMark.Shellenbaum@Sun.COM 	aclp = zfs_acl_alloc(paclp->z_version);
161311935SMark.Shellenbaum@Sun.COM 	if (zfsvfs->z_acl_inherit == ZFS_ACL_DISCARD || vtype == VLNK)
16148053SMark.Shellenbaum@Sun.COM 		return (aclp);
16158053SMark.Shellenbaum@Sun.COM 	while (pacep = zfs_acl_next_ace(paclp, pacep, &who,
16168053SMark.Shellenbaum@Sun.COM 	    &access_mask, &iflags, &type)) {
16178053SMark.Shellenbaum@Sun.COM 
16188053SMark.Shellenbaum@Sun.COM 		/*
16198053SMark.Shellenbaum@Sun.COM 		 * don't inherit bogus ACEs
16208053SMark.Shellenbaum@Sun.COM 		 */
16218053SMark.Shellenbaum@Sun.COM 		if (!zfs_acl_valid_ace_type(type, iflags))
16228053SMark.Shellenbaum@Sun.COM 			continue;
16238053SMark.Shellenbaum@Sun.COM 
16248053SMark.Shellenbaum@Sun.COM 		if (noallow && type == ALLOW)
16258053SMark.Shellenbaum@Sun.COM 			continue;
16268053SMark.Shellenbaum@Sun.COM 
16278053SMark.Shellenbaum@Sun.COM 		ace_size = aclp->z_ops.ace_size(pacep);
16288053SMark.Shellenbaum@Sun.COM 
16299179SMark.Shellenbaum@Sun.COM 		if (!zfs_ace_can_use(vtype, iflags))
16308053SMark.Shellenbaum@Sun.COM 			continue;
1631789Sahrens 
16328053SMark.Shellenbaum@Sun.COM 		/*
16338053SMark.Shellenbaum@Sun.COM 		 * If owner@, group@, or everyone@ inheritable
16348053SMark.Shellenbaum@Sun.COM 		 * then zfs_acl_chmod() isn't needed.
16358053SMark.Shellenbaum@Sun.COM 		 */
16368053SMark.Shellenbaum@Sun.COM 		if (passthrough &&
16378053SMark.Shellenbaum@Sun.COM 		    ((iflags & (ACE_OWNER|ACE_EVERYONE)) ||
16388053SMark.Shellenbaum@Sun.COM 		    ((iflags & OWNING_GROUP) ==
16398053SMark.Shellenbaum@Sun.COM 		    OWNING_GROUP)) && (vreg || (vdir && (iflags &
16408053SMark.Shellenbaum@Sun.COM 		    ACE_DIRECTORY_INHERIT_ACE)))) {
16418053SMark.Shellenbaum@Sun.COM 			*need_chmod = B_FALSE;
164211969SMark.Shellenbaum@Sun.COM 		}
164311969SMark.Shellenbaum@Sun.COM 
164411969SMark.Shellenbaum@Sun.COM 		if (!vdir && passthrough_x &&
164511969SMark.Shellenbaum@Sun.COM 		    ((mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0)) {
164611969SMark.Shellenbaum@Sun.COM 			access_mask &= ~ACE_EXECUTE;
16478053SMark.Shellenbaum@Sun.COM 		}
1648789Sahrens 
16498053SMark.Shellenbaum@Sun.COM 		aclnode = zfs_acl_node_alloc(ace_size);
16508053SMark.Shellenbaum@Sun.COM 		list_insert_tail(&aclp->z_acl, aclnode);
16518053SMark.Shellenbaum@Sun.COM 		acep = aclnode->z_acldata;
16526385Smarks 
16538053SMark.Shellenbaum@Sun.COM 		zfs_set_ace(aclp, acep, access_mask, type,
16548053SMark.Shellenbaum@Sun.COM 		    who, iflags|ACE_INHERITED_ACE);
16558053SMark.Shellenbaum@Sun.COM 
16568053SMark.Shellenbaum@Sun.COM 		/*
16578053SMark.Shellenbaum@Sun.COM 		 * Copy special opaque data if any
16588053SMark.Shellenbaum@Sun.COM 		 */
16598053SMark.Shellenbaum@Sun.COM 		if ((data1sz = paclp->z_ops.ace_data(pacep, &data1)) != 0) {
16608053SMark.Shellenbaum@Sun.COM 			VERIFY((data2sz = aclp->z_ops.ace_data(acep,
16618053SMark.Shellenbaum@Sun.COM 			    &data2)) == data1sz);
16628053SMark.Shellenbaum@Sun.COM 			bcopy(data1, data2, data2sz);
16638053SMark.Shellenbaum@Sun.COM 		}
166412164SMark.Shellenbaum@Sun.COM 
16658053SMark.Shellenbaum@Sun.COM 		aclp->z_acl_count++;
16668053SMark.Shellenbaum@Sun.COM 		aclnode->z_ace_count++;
16678053SMark.Shellenbaum@Sun.COM 		aclp->z_acl_bytes += aclnode->z_size;
16688053SMark.Shellenbaum@Sun.COM 		newflags = aclp->z_ops.ace_flags_get(acep);
16698053SMark.Shellenbaum@Sun.COM 
16708053SMark.Shellenbaum@Sun.COM 		if (vdir)
16718053SMark.Shellenbaum@Sun.COM 			aclp->z_hints |= ZFS_INHERIT_ACE;
16726385Smarks 
16738053SMark.Shellenbaum@Sun.COM 		if ((iflags & ACE_NO_PROPAGATE_INHERIT_ACE) || !vdir) {
16748053SMark.Shellenbaum@Sun.COM 			newflags &= ~ALL_INHERIT;
16758053SMark.Shellenbaum@Sun.COM 			aclp->z_ops.ace_flags_set(acep,
16768053SMark.Shellenbaum@Sun.COM 			    newflags|ACE_INHERITED_ACE);
16778053SMark.Shellenbaum@Sun.COM 			zfs_restricted_update(zfsvfs, aclp, acep);
16788053SMark.Shellenbaum@Sun.COM 			continue;
16798053SMark.Shellenbaum@Sun.COM 		}
16808053SMark.Shellenbaum@Sun.COM 
16818053SMark.Shellenbaum@Sun.COM 		ASSERT(vdir);
16828053SMark.Shellenbaum@Sun.COM 
168312164SMark.Shellenbaum@Sun.COM 		/*
168412164SMark.Shellenbaum@Sun.COM 		 * If only FILE_INHERIT is set then turn on
168512164SMark.Shellenbaum@Sun.COM 		 * inherit_only
168612164SMark.Shellenbaum@Sun.COM 		 */
16878053SMark.Shellenbaum@Sun.COM 		if ((iflags & (ACE_FILE_INHERIT_ACE |
168812322SMark.Shellenbaum@Sun.COM 		    ACE_DIRECTORY_INHERIT_ACE)) == ACE_FILE_INHERIT_ACE) {
16898053SMark.Shellenbaum@Sun.COM 			newflags |= ACE_INHERIT_ONLY_ACE;
16908053SMark.Shellenbaum@Sun.COM 			aclp->z_ops.ace_flags_set(acep,
16918053SMark.Shellenbaum@Sun.COM 			    newflags|ACE_INHERITED_ACE);
169212322SMark.Shellenbaum@Sun.COM 		} else {
169312322SMark.Shellenbaum@Sun.COM 			newflags &= ~ACE_INHERIT_ONLY_ACE;
169412322SMark.Shellenbaum@Sun.COM 			aclp->z_ops.ace_flags_set(acep,
169512322SMark.Shellenbaum@Sun.COM 			    newflags|ACE_INHERITED_ACE);
1696789Sahrens 		}
1697789Sahrens 	}
1698789Sahrens 	return (aclp);
1699789Sahrens }
1700789Sahrens 
1701789Sahrens /*
1702789Sahrens  * Create file system object initial permissions
1703789Sahrens  * including inheritable ACEs.
1704789Sahrens  */
17059179SMark.Shellenbaum@Sun.COM int
17069179SMark.Shellenbaum@Sun.COM zfs_acl_ids_create(znode_t *dzp, int flag, vattr_t *vap, cred_t *cr,
17079179SMark.Shellenbaum@Sun.COM     vsecattr_t *vsecp, zfs_acl_ids_t *acl_ids)
1708789Sahrens {
1709789Sahrens 	int		error;
17109179SMark.Shellenbaum@Sun.COM 	zfsvfs_t	*zfsvfs = dzp->z_zfsvfs;
17115331Samw 	zfs_acl_t	*paclp;
17125959Smarks 	gid_t		gid;
17136385Smarks 	boolean_t	need_chmod = B_TRUE;
171411935SMark.Shellenbaum@Sun.COM 	boolean_t	inherited = B_FALSE;
171511935SMark.Shellenbaum@Sun.COM 	uint64_t	parentgid;
17165331Samw 
17179179SMark.Shellenbaum@Sun.COM 	bzero(acl_ids, sizeof (zfs_acl_ids_t));
17189179SMark.Shellenbaum@Sun.COM 	acl_ids->z_mode = MAKEIMODE(vap->va_type, vap->va_mode);
1719789Sahrens 
17209179SMark.Shellenbaum@Sun.COM 	if (vsecp)
17219179SMark.Shellenbaum@Sun.COM 		if ((error = zfs_vsec_2_aclp(zfsvfs, vap->va_type, vsecp, cr,
17229179SMark.Shellenbaum@Sun.COM 		    &acl_ids->z_fuidp, &acl_ids->z_aclp)) != 0)
17239179SMark.Shellenbaum@Sun.COM 			return (error);
1724789Sahrens 	/*
1725789Sahrens 	 * Determine uid and gid.
1726789Sahrens 	 */
172711249SMark.Shellenbaum@Sun.COM 	if ((flag & IS_ROOT_NODE) || zfsvfs->z_replay ||
1728789Sahrens 	    ((flag & IS_XATTR) && (vap->va_type == VDIR))) {
17299179SMark.Shellenbaum@Sun.COM 		acl_ids->z_fuid = zfs_fuid_create(zfsvfs,
17309179SMark.Shellenbaum@Sun.COM 		    (uint64_t)vap->va_uid, cr,
17319179SMark.Shellenbaum@Sun.COM 		    ZFS_OWNER, &acl_ids->z_fuidp);
17329179SMark.Shellenbaum@Sun.COM 		acl_ids->z_fgid = zfs_fuid_create(zfsvfs,
17339179SMark.Shellenbaum@Sun.COM 		    (uint64_t)vap->va_gid, cr,
17349179SMark.Shellenbaum@Sun.COM 		    ZFS_GROUP, &acl_ids->z_fuidp);
17355959Smarks 		gid = vap->va_gid;
1736789Sahrens 	} else {
173711935SMark.Shellenbaum@Sun.COM 		if (IS_EPHEMERAL(dzp->z_gid))
173811935SMark.Shellenbaum@Sun.COM 			VERIFY(0 == sa_lookup(dzp->z_sa_hdl, SA_ZPL_GID(zfsvfs),
173911935SMark.Shellenbaum@Sun.COM 			    &parentgid, sizeof (parentgid)));
174011935SMark.Shellenbaum@Sun.COM 		else
174111935SMark.Shellenbaum@Sun.COM 			parentgid = (uint64_t)dzp->z_gid;
174211935SMark.Shellenbaum@Sun.COM 
17439179SMark.Shellenbaum@Sun.COM 		acl_ids->z_fuid = zfs_fuid_create_cred(zfsvfs, ZFS_OWNER,
17449179SMark.Shellenbaum@Sun.COM 		    cr, &acl_ids->z_fuidp);
17459179SMark.Shellenbaum@Sun.COM 		acl_ids->z_fgid = 0;
17465959Smarks 		if (vap->va_mask & AT_GID)  {
17479179SMark.Shellenbaum@Sun.COM 			acl_ids->z_fgid = zfs_fuid_create(zfsvfs,
17489179SMark.Shellenbaum@Sun.COM 			    (uint64_t)vap->va_gid,
17499179SMark.Shellenbaum@Sun.COM 			    cr, ZFS_GROUP, &acl_ids->z_fuidp);
17505959Smarks 			gid = vap->va_gid;
175111935SMark.Shellenbaum@Sun.COM 			if (acl_ids->z_fgid != parentgid &&
17525959Smarks 			    !groupmember(vap->va_gid, cr) &&
17535959Smarks 			    secpolicy_vnode_create_gid(cr) != 0)
17549179SMark.Shellenbaum@Sun.COM 				acl_ids->z_fgid = 0;
17555959Smarks 		}
17569179SMark.Shellenbaum@Sun.COM 		if (acl_ids->z_fgid == 0) {
175711935SMark.Shellenbaum@Sun.COM 			if (dzp->z_mode & S_ISGID) {
175811574SJohn.Harres@Sun.COM 				char		*domain;
175911574SJohn.Harres@Sun.COM 				uint32_t	rid;
176011574SJohn.Harres@Sun.COM 
176111935SMark.Shellenbaum@Sun.COM 				acl_ids->z_fgid = parentgid;
17629179SMark.Shellenbaum@Sun.COM 				gid = zfs_fuid_map_id(zfsvfs, acl_ids->z_fgid,
17635959Smarks 				    cr, ZFS_GROUP);
176411574SJohn.Harres@Sun.COM 
176511574SJohn.Harres@Sun.COM 				if (zfsvfs->z_use_fuids &&
176611574SJohn.Harres@Sun.COM 				    IS_EPHEMERAL(acl_ids->z_fgid)) {
176711574SJohn.Harres@Sun.COM 					domain = zfs_fuid_idx_domain(
176811574SJohn.Harres@Sun.COM 					    &zfsvfs->z_fuid_idx,
176911574SJohn.Harres@Sun.COM 					    FUID_INDEX(acl_ids->z_fgid));
177011574SJohn.Harres@Sun.COM 					rid = FUID_RID(acl_ids->z_fgid);
177111574SJohn.Harres@Sun.COM 					zfs_fuid_node_add(&acl_ids->z_fuidp,
177211574SJohn.Harres@Sun.COM 					    domain, rid,
177311574SJohn.Harres@Sun.COM 					    FUID_INDEX(acl_ids->z_fgid),
177411574SJohn.Harres@Sun.COM 					    acl_ids->z_fgid, ZFS_GROUP);
177511574SJohn.Harres@Sun.COM 				}
17765959Smarks 			} else {
17779179SMark.Shellenbaum@Sun.COM 				acl_ids->z_fgid = zfs_fuid_create_cred(zfsvfs,
17789179SMark.Shellenbaum@Sun.COM 				    ZFS_GROUP, cr, &acl_ids->z_fuidp);
17795959Smarks 				gid = crgetgid(cr);
17805959Smarks 			}
17815331Samw 		}
1782789Sahrens 	}
1783789Sahrens 
1784789Sahrens 	/*
1785789Sahrens 	 * If we're creating a directory, and the parent directory has the
1786789Sahrens 	 * set-GID bit set, set in on the new directory.
1787789Sahrens 	 * Otherwise, if the user is neither privileged nor a member of the
1788789Sahrens 	 * file's new group, clear the file's set-GID bit.
1789789Sahrens 	 */
1790789Sahrens 
179111935SMark.Shellenbaum@Sun.COM 	if (!(flag & IS_ROOT_NODE) && (dzp->z_mode & S_ISGID) &&
17929179SMark.Shellenbaum@Sun.COM 	    (vap->va_type == VDIR)) {
17939179SMark.Shellenbaum@Sun.COM 		acl_ids->z_mode |= S_ISGID;
17945959Smarks 	} else {
17959179SMark.Shellenbaum@Sun.COM 		if ((acl_ids->z_mode & S_ISGID) &&
1796789Sahrens 		    secpolicy_vnode_setids_setgids(cr, gid) != 0)
17979179SMark.Shellenbaum@Sun.COM 			acl_ids->z_mode &= ~S_ISGID;
1798789Sahrens 	}
1799789Sahrens 
18009179SMark.Shellenbaum@Sun.COM 	if (acl_ids->z_aclp == NULL) {
1801*12620SMark.Shellenbaum@Oracle.COM 		mutex_enter(&dzp->z_acl_lock);
18029179SMark.Shellenbaum@Sun.COM 		mutex_enter(&dzp->z_lock);
18039179SMark.Shellenbaum@Sun.COM 		if (!(flag & IS_ROOT_NODE) && (ZTOV(dzp)->v_type == VDIR &&
180411935SMark.Shellenbaum@Sun.COM 		    (dzp->z_pflags & ZFS_INHERIT_ACE)) &&
180511935SMark.Shellenbaum@Sun.COM 		    !(dzp->z_pflags & ZFS_XATTR)) {
1806*12620SMark.Shellenbaum@Oracle.COM 			VERIFY(0 == zfs_acl_node_read(dzp, B_TRUE,
1807*12620SMark.Shellenbaum@Oracle.COM 			    &paclp, B_FALSE));
18089179SMark.Shellenbaum@Sun.COM 			acl_ids->z_aclp = zfs_acl_inherit(zfsvfs,
18099179SMark.Shellenbaum@Sun.COM 			    vap->va_type, paclp, acl_ids->z_mode, &need_chmod);
181011935SMark.Shellenbaum@Sun.COM 			inherited = B_TRUE;
18115331Samw 		} else {
18129179SMark.Shellenbaum@Sun.COM 			acl_ids->z_aclp =
18139179SMark.Shellenbaum@Sun.COM 			    zfs_acl_alloc(zfs_acl_version_zp(dzp));
181411935SMark.Shellenbaum@Sun.COM 			acl_ids->z_aclp->z_hints |= ZFS_ACL_TRIVIAL;
18155331Samw 		}
18169179SMark.Shellenbaum@Sun.COM 		mutex_exit(&dzp->z_lock);
1817*12620SMark.Shellenbaum@Oracle.COM 		mutex_exit(&dzp->z_acl_lock);
18189179SMark.Shellenbaum@Sun.COM 		if (need_chmod) {
181911935SMark.Shellenbaum@Sun.COM 			acl_ids->z_aclp->z_hints |= (vap->va_type == VDIR) ?
18209179SMark.Shellenbaum@Sun.COM 			    ZFS_ACL_AUTO_INHERIT : 0;
182112164SMark.Shellenbaum@Sun.COM 			zfs_acl_chmod(zfsvfs, acl_ids->z_mode, acl_ids->z_aclp);
18229179SMark.Shellenbaum@Sun.COM 		}
1823789Sahrens 	}
18245331Samw 
182511935SMark.Shellenbaum@Sun.COM 	if (inherited || vsecp) {
182611935SMark.Shellenbaum@Sun.COM 		acl_ids->z_mode = zfs_mode_compute(acl_ids->z_mode,
182712164SMark.Shellenbaum@Sun.COM 		    acl_ids->z_aclp, &acl_ids->z_aclp->z_hints,
182812164SMark.Shellenbaum@Sun.COM 		    acl_ids->z_fuid, acl_ids->z_fgid);
182911935SMark.Shellenbaum@Sun.COM 		if (ace_trivial_common(acl_ids->z_aclp, 0, zfs_ace_walk) == 0)
183011935SMark.Shellenbaum@Sun.COM 			acl_ids->z_aclp->z_hints |= ZFS_ACL_TRIVIAL;
183111935SMark.Shellenbaum@Sun.COM 	}
183211935SMark.Shellenbaum@Sun.COM 
18339179SMark.Shellenbaum@Sun.COM 	return (0);
18349179SMark.Shellenbaum@Sun.COM }
18355331Samw 
18369179SMark.Shellenbaum@Sun.COM /*
18379179SMark.Shellenbaum@Sun.COM  * Free ACL and fuid_infop, but not the acl_ids structure
18389179SMark.Shellenbaum@Sun.COM  */
18399179SMark.Shellenbaum@Sun.COM void
18409179SMark.Shellenbaum@Sun.COM zfs_acl_ids_free(zfs_acl_ids_t *acl_ids)
18419179SMark.Shellenbaum@Sun.COM {
18429179SMark.Shellenbaum@Sun.COM 	if (acl_ids->z_aclp)
18439179SMark.Shellenbaum@Sun.COM 		zfs_acl_free(acl_ids->z_aclp);
18449179SMark.Shellenbaum@Sun.COM 	if (acl_ids->z_fuidp)
18459179SMark.Shellenbaum@Sun.COM 		zfs_fuid_info_free(acl_ids->z_fuidp);
18469179SMark.Shellenbaum@Sun.COM 	acl_ids->z_aclp = NULL;
18479179SMark.Shellenbaum@Sun.COM 	acl_ids->z_fuidp = NULL;
18489179SMark.Shellenbaum@Sun.COM }
18495331Samw 
18509396SMatthew.Ahrens@Sun.COM boolean_t
18519396SMatthew.Ahrens@Sun.COM zfs_acl_ids_overquota(zfsvfs_t *zfsvfs, zfs_acl_ids_t *acl_ids)
18529396SMatthew.Ahrens@Sun.COM {
185311935SMark.Shellenbaum@Sun.COM 	return (zfs_fuid_overquota(zfsvfs, B_FALSE, acl_ids->z_fuid) ||
185411935SMark.Shellenbaum@Sun.COM 	    zfs_fuid_overquota(zfsvfs, B_TRUE, acl_ids->z_fgid));
18559396SMatthew.Ahrens@Sun.COM }
1856789Sahrens 
1857789Sahrens /*
1858789Sahrens  * Retrieve a files ACL
1859789Sahrens  */
1860789Sahrens int
18615331Samw zfs_getacl(znode_t *zp, vsecattr_t *vsecp, boolean_t skipaclchk, cred_t *cr)
1862789Sahrens {
1863789Sahrens 	zfs_acl_t	*aclp;
18645331Samw 	ulong_t		mask;
1865789Sahrens 	int		error;
18665331Samw 	int 		count = 0;
18675331Samw 	int		largeace = 0;
1868789Sahrens 
18695331Samw 	mask = vsecp->vsa_mask & (VSA_ACE | VSA_ACECNT |
18705331Samw 	    VSA_ACE_ACLFLAGS | VSA_ACE_ALLTYPES);
18715331Samw 
187211935SMark.Shellenbaum@Sun.COM 	if (mask == 0)
187311935SMark.Shellenbaum@Sun.COM 		return (ENOSYS);
187411935SMark.Shellenbaum@Sun.COM 
18755331Samw 	if (error = zfs_zaccess(zp, ACE_READ_ACL, 0, skipaclchk, cr))
18765331Samw 		return (error);
1877789Sahrens 
1878789Sahrens 	mutex_enter(&zp->z_acl_lock);
1879789Sahrens 
1880*12620SMark.Shellenbaum@Oracle.COM 	error = zfs_acl_node_read(zp, B_FALSE, &aclp, B_FALSE);
18811544Seschrock 	if (error != 0) {
18821544Seschrock 		mutex_exit(&zp->z_acl_lock);
18831544Seschrock 		return (error);
18841544Seschrock 	}
18851544Seschrock 
18865331Samw 	/*
18875331Samw 	 * Scan ACL to determine number of ACEs
18885331Samw 	 */
188911935SMark.Shellenbaum@Sun.COM 	if ((zp->z_pflags & ZFS_ACL_OBJ_ACE) && !(mask & VSA_ACE_ALLTYPES)) {
18905331Samw 		void *zacep = NULL;
18915331Samw 		uint64_t who;
18925331Samw 		uint32_t access_mask;
18935331Samw 		uint16_t type, iflags;
18945331Samw 
18955331Samw 		while (zacep = zfs_acl_next_ace(aclp, zacep,
18965331Samw 		    &who, &access_mask, &iflags, &type)) {
18975331Samw 			switch (type) {
18985331Samw 			case ACE_ACCESS_ALLOWED_OBJECT_ACE_TYPE:
18995331Samw 			case ACE_ACCESS_DENIED_OBJECT_ACE_TYPE:
19005331Samw 			case ACE_SYSTEM_AUDIT_OBJECT_ACE_TYPE:
19015331Samw 			case ACE_SYSTEM_ALARM_OBJECT_ACE_TYPE:
19025331Samw 				largeace++;
19035331Samw 				continue;
19045331Samw 			default:
19055331Samw 				count++;
19065331Samw 			}
19075331Samw 		}
19085331Samw 		vsecp->vsa_aclcnt = count;
19095331Samw 	} else
191011935SMark.Shellenbaum@Sun.COM 		count = (int)aclp->z_acl_count;
1911789Sahrens 
1912789Sahrens 	if (mask & VSA_ACECNT) {
19135331Samw 		vsecp->vsa_aclcnt = count;
1914789Sahrens 	}
1915789Sahrens 
1916789Sahrens 	if (mask & VSA_ACE) {
19175331Samw 		size_t aclsz;
19185331Samw 
19195331Samw 		aclsz = count * sizeof (ace_t) +
19205331Samw 		    sizeof (ace_object_t) * largeace;
19215331Samw 
19225331Samw 		vsecp->vsa_aclentp = kmem_alloc(aclsz, KM_SLEEP);
19235331Samw 		vsecp->vsa_aclentsz = aclsz;
19245331Samw 
19255331Samw 		if (aclp->z_version == ZFS_ACL_VERSION_FUID)
19265771Sjp151216 			zfs_copy_fuid_2_ace(zp->z_zfsvfs, aclp, cr,
19275331Samw 			    vsecp->vsa_aclentp, !(mask & VSA_ACE_ALLTYPES));
19285331Samw 		else {
192910295SMark.Shellenbaum@Sun.COM 			zfs_acl_node_t *aclnode;
193010295SMark.Shellenbaum@Sun.COM 			void *start = vsecp->vsa_aclentp;
193110295SMark.Shellenbaum@Sun.COM 
193210295SMark.Shellenbaum@Sun.COM 			for (aclnode = list_head(&aclp->z_acl); aclnode;
193310295SMark.Shellenbaum@Sun.COM 			    aclnode = list_next(&aclp->z_acl, aclnode)) {
193410295SMark.Shellenbaum@Sun.COM 				bcopy(aclnode->z_acldata, start,
193510295SMark.Shellenbaum@Sun.COM 				    aclnode->z_size);
193610295SMark.Shellenbaum@Sun.COM 				start = (caddr_t)start + aclnode->z_size;
193710295SMark.Shellenbaum@Sun.COM 			}
193810295SMark.Shellenbaum@Sun.COM 			ASSERT((caddr_t)start - (caddr_t)vsecp->vsa_aclentp ==
193910295SMark.Shellenbaum@Sun.COM 			    aclp->z_acl_bytes);
19405331Samw 		}
19415331Samw 	}
19425331Samw 	if (mask & VSA_ACE_ACLFLAGS) {
19435331Samw 		vsecp->vsa_aclflags = 0;
194411935SMark.Shellenbaum@Sun.COM 		if (zp->z_pflags & ZFS_ACL_DEFAULTED)
19455331Samw 			vsecp->vsa_aclflags |= ACL_DEFAULTED;
194611935SMark.Shellenbaum@Sun.COM 		if (zp->z_pflags & ZFS_ACL_PROTECTED)
19475331Samw 			vsecp->vsa_aclflags |= ACL_PROTECTED;
194811935SMark.Shellenbaum@Sun.COM 		if (zp->z_pflags & ZFS_ACL_AUTO_INHERIT)
19495331Samw 			vsecp->vsa_aclflags |= ACL_AUTO_INHERIT;
1950789Sahrens 	}
1951789Sahrens 
1952789Sahrens 	mutex_exit(&zp->z_acl_lock);
1953789Sahrens 
1954789Sahrens 	return (0);
1955789Sahrens }
1956789Sahrens 
19575331Samw int
19585331Samw zfs_vsec_2_aclp(zfsvfs_t *zfsvfs, vtype_t obj_type,
19599179SMark.Shellenbaum@Sun.COM     vsecattr_t *vsecp, cred_t *cr, zfs_fuid_info_t **fuidp, zfs_acl_t **zaclp)
19605331Samw {
19615331Samw 	zfs_acl_t *aclp;
19625331Samw 	zfs_acl_node_t *aclnode;
19635331Samw 	int aclcnt = vsecp->vsa_aclcnt;
19645331Samw 	int error;
19655331Samw 
19665331Samw 	if (vsecp->vsa_aclcnt > MAX_ACL_ENTRIES || vsecp->vsa_aclcnt <= 0)
19675331Samw 		return (EINVAL);
19685331Samw 
19695331Samw 	aclp = zfs_acl_alloc(zfs_acl_version(zfsvfs->z_version));
19705331Samw 
19715331Samw 	aclp->z_hints = 0;
19725331Samw 	aclnode = zfs_acl_node_alloc(aclcnt * sizeof (zfs_object_ace_t));
19735331Samw 	if (aclp->z_version == ZFS_ACL_VERSION_INITIAL) {
19745331Samw 		if ((error = zfs_copy_ace_2_oldace(obj_type, aclp,
19755331Samw 		    (ace_t *)vsecp->vsa_aclentp, aclnode->z_acldata,
19765331Samw 		    aclcnt, &aclnode->z_size)) != 0) {
19775331Samw 			zfs_acl_free(aclp);
19785331Samw 			zfs_acl_node_free(aclnode);
19795331Samw 			return (error);
19805331Samw 		}
19815331Samw 	} else {
19829179SMark.Shellenbaum@Sun.COM 		if ((error = zfs_copy_ace_2_fuid(zfsvfs, obj_type, aclp,
19835331Samw 		    vsecp->vsa_aclentp, aclnode->z_acldata, aclcnt,
19849179SMark.Shellenbaum@Sun.COM 		    &aclnode->z_size, fuidp, cr)) != 0) {
19855331Samw 			zfs_acl_free(aclp);
19865331Samw 			zfs_acl_node_free(aclnode);
19875331Samw 			return (error);
19885331Samw 		}
19895331Samw 	}
19905331Samw 	aclp->z_acl_bytes = aclnode->z_size;
19915331Samw 	aclnode->z_ace_count = aclcnt;
19925331Samw 	aclp->z_acl_count = aclcnt;
19935331Samw 	list_insert_head(&aclp->z_acl, aclnode);
19945331Samw 
19955331Samw 	/*
19965331Samw 	 * If flags are being set then add them to z_hints
19975331Samw 	 */
19985331Samw 	if (vsecp->vsa_mask & VSA_ACE_ACLFLAGS) {
19995331Samw 		if (vsecp->vsa_aclflags & ACL_PROTECTED)
20005331Samw 			aclp->z_hints |= ZFS_ACL_PROTECTED;
20015331Samw 		if (vsecp->vsa_aclflags & ACL_DEFAULTED)
20025331Samw 			aclp->z_hints |= ZFS_ACL_DEFAULTED;
20035331Samw 		if (vsecp->vsa_aclflags & ACL_AUTO_INHERIT)
20045331Samw 			aclp->z_hints |= ZFS_ACL_AUTO_INHERIT;
20055331Samw 	}
20065331Samw 
20075331Samw 	*zaclp = aclp;
20085331Samw 
20095331Samw 	return (0);
20105331Samw }
20115331Samw 
2012789Sahrens /*
2013789Sahrens  * Set a files ACL
2014789Sahrens  */
2015789Sahrens int
20165331Samw zfs_setacl(znode_t *zp, vsecattr_t *vsecp, boolean_t skipaclchk, cred_t *cr)
2017789Sahrens {
2018789Sahrens 	zfsvfs_t	*zfsvfs = zp->z_zfsvfs;
2019789Sahrens 	zilog_t		*zilog = zfsvfs->z_log;
2020789Sahrens 	ulong_t		mask = vsecp->vsa_mask & (VSA_ACE | VSA_ACECNT);
2021789Sahrens 	dmu_tx_t	*tx;
2022789Sahrens 	int		error;
2023789Sahrens 	zfs_acl_t	*aclp;
20245331Samw 	zfs_fuid_info_t	*fuidp = NULL;
20259179SMark.Shellenbaum@Sun.COM 	boolean_t	fuid_dirtied;
2026*12620SMark.Shellenbaum@Oracle.COM 	uint64_t	acl_obj;
2027789Sahrens 
2028789Sahrens 	if (mask == 0)
20294300Smarks 		return (ENOSYS);
2030789Sahrens 
203111935SMark.Shellenbaum@Sun.COM 	if (zp->z_pflags & ZFS_IMMUTABLE)
20325331Samw 		return (EPERM);
20335331Samw 
20345331Samw 	if (error = zfs_zaccess(zp, ACE_WRITE_ACL, 0, skipaclchk, cr))
20355331Samw 		return (error);
20365331Samw 
20379179SMark.Shellenbaum@Sun.COM 	error = zfs_vsec_2_aclp(zfsvfs, ZTOV(zp)->v_type, vsecp, cr, &fuidp,
20389179SMark.Shellenbaum@Sun.COM 	    &aclp);
20395331Samw 	if (error)
20405331Samw 		return (error);
20415331Samw 
20425331Samw 	/*
20435331Samw 	 * If ACL wide flags aren't being set then preserve any
20445331Samw 	 * existing flags.
20455331Samw 	 */
20465331Samw 	if (!(vsecp->vsa_mask & VSA_ACE_ACLFLAGS)) {
204711935SMark.Shellenbaum@Sun.COM 		aclp->z_hints |=
204811935SMark.Shellenbaum@Sun.COM 		    (zp->z_pflags & V4_ACL_WIDE_FLAGS);
20495331Samw 	}
2050789Sahrens top:
2051*12620SMark.Shellenbaum@Oracle.COM 	mutex_enter(&zp->z_acl_lock);
2052789Sahrens 	mutex_enter(&zp->z_lock);
2053789Sahrens 
2054789Sahrens 	tx = dmu_tx_create(zfsvfs->z_os);
205511935SMark.Shellenbaum@Sun.COM 
205611935SMark.Shellenbaum@Sun.COM 	dmu_tx_hold_sa(tx, zp->z_sa_hdl, B_TRUE);
205711935SMark.Shellenbaum@Sun.COM 
20589179SMark.Shellenbaum@Sun.COM 	fuid_dirtied = zfsvfs->z_fuid_dirty;
20599396SMatthew.Ahrens@Sun.COM 	if (fuid_dirtied)
20609396SMatthew.Ahrens@Sun.COM 		zfs_fuid_txhold(zfsvfs, tx);
2061789Sahrens 
206211935SMark.Shellenbaum@Sun.COM 	/*
206311935SMark.Shellenbaum@Sun.COM 	 * If old version and ACL won't fit in bonus and we aren't
206411935SMark.Shellenbaum@Sun.COM 	 * upgrading then take out necessary DMU holds
206511935SMark.Shellenbaum@Sun.COM 	 */
206611935SMark.Shellenbaum@Sun.COM 
2067*12620SMark.Shellenbaum@Oracle.COM 	if ((acl_obj = zfs_external_acl(zp)) != 0) {
206811935SMark.Shellenbaum@Sun.COM 		if (zfsvfs->z_version <= ZPL_VERSION_SA &&
2069*12620SMark.Shellenbaum@Oracle.COM 		    zfs_znode_acl_version(zp) <= ZFS_ACL_VERSION_INITIAL) {
2070*12620SMark.Shellenbaum@Oracle.COM 			dmu_tx_hold_free(tx, acl_obj, 0,
207111935SMark.Shellenbaum@Sun.COM 			    DMU_OBJECT_END);
207211935SMark.Shellenbaum@Sun.COM 		} else {
2073*12620SMark.Shellenbaum@Oracle.COM 			dmu_tx_hold_write(tx, acl_obj, 0, aclp->z_acl_bytes);
207411935SMark.Shellenbaum@Sun.COM 		}
207511935SMark.Shellenbaum@Sun.COM 	} else if (!zp->z_is_sa && aclp->z_acl_bytes > ZFS_ACE_SPACE) {
207611935SMark.Shellenbaum@Sun.COM 		dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0, aclp->z_acl_bytes);
207711935SMark.Shellenbaum@Sun.COM 	}
207811935SMark.Shellenbaum@Sun.COM 
207911935SMark.Shellenbaum@Sun.COM 	zfs_sa_upgrade_txholds(tx, zp);
20808227SNeil.Perrin@Sun.COM 	error = dmu_tx_assign(tx, TXG_NOWAIT);
2081789Sahrens 	if (error) {
2082789Sahrens 		mutex_exit(&zp->z_acl_lock);
2083789Sahrens 		mutex_exit(&zp->z_lock);
2084789Sahrens 
20858227SNeil.Perrin@Sun.COM 		if (error == ERESTART) {
20862113Sahrens 			dmu_tx_wait(tx);
20872113Sahrens 			dmu_tx_abort(tx);
2088789Sahrens 			goto top;
2089789Sahrens 		}
20902113Sahrens 		dmu_tx_abort(tx);
20915331Samw 		zfs_acl_free(aclp);
2092789Sahrens 		return (error);
2093789Sahrens 	}
2094789Sahrens 
20959179SMark.Shellenbaum@Sun.COM 	error = zfs_aclset_common(zp, aclp, cr, tx);
2096789Sahrens 	ASSERT(error == 0);
209710143STim.Haley@Sun.COM 	zp->z_acl_cached = aclp;
2098789Sahrens 
20999179SMark.Shellenbaum@Sun.COM 	if (fuid_dirtied)
21009179SMark.Shellenbaum@Sun.COM 		zfs_fuid_sync(zfsvfs, tx);
21019179SMark.Shellenbaum@Sun.COM 
21025331Samw 	zfs_log_acl(zilog, tx, zp, vsecp, fuidp);
21035331Samw 
21045331Samw 	if (fuidp)
21055331Samw 		zfs_fuid_info_free(fuidp);
2106789Sahrens 	dmu_tx_commit(tx);
2107789Sahrens done:
2108*12620SMark.Shellenbaum@Oracle.COM 	mutex_exit(&zp->z_lock);
2109789Sahrens 	mutex_exit(&zp->z_acl_lock);
2110789Sahrens 
2111789Sahrens 	return (error);
2112789Sahrens }
2113789Sahrens 
21145331Samw /*
21159749STim.Haley@Sun.COM  * Check accesses of interest (AoI) against attributes of the dataset
21169749STim.Haley@Sun.COM  * such as read-only.  Returns zero if no AoI conflict with dataset
21179749STim.Haley@Sun.COM  * attributes, otherwise an appropriate errno is returned.
21185331Samw  */
2119789Sahrens static int
21209749STim.Haley@Sun.COM zfs_zaccess_dataset_check(znode_t *zp, uint32_t v4_mode)
2121789Sahrens {
21229749STim.Haley@Sun.COM 	if ((v4_mode & WRITE_MASK) &&
21239749STim.Haley@Sun.COM 	    (zp->z_zfsvfs->z_vfs->vfs_flag & VFS_RDONLY) &&
21249749STim.Haley@Sun.COM 	    (!IS_DEVVP(ZTOV(zp)) ||
21259749STim.Haley@Sun.COM 	    (IS_DEVVP(ZTOV(zp)) && (v4_mode & WRITE_MASK_ATTRS)))) {
21269749STim.Haley@Sun.COM 		return (EROFS);
21279749STim.Haley@Sun.COM 	}
21289749STim.Haley@Sun.COM 
21299749STim.Haley@Sun.COM 	/*
21309749STim.Haley@Sun.COM 	 * Only check for READONLY on non-directories.
21319749STim.Haley@Sun.COM 	 */
21329749STim.Haley@Sun.COM 	if ((v4_mode & WRITE_MASK_DATA) &&
21339749STim.Haley@Sun.COM 	    (((ZTOV(zp)->v_type != VDIR) &&
213411935SMark.Shellenbaum@Sun.COM 	    (zp->z_pflags & (ZFS_READONLY | ZFS_IMMUTABLE))) ||
21359749STim.Haley@Sun.COM 	    (ZTOV(zp)->v_type == VDIR &&
213611935SMark.Shellenbaum@Sun.COM 	    (zp->z_pflags & ZFS_IMMUTABLE)))) {
21379749STim.Haley@Sun.COM 		return (EPERM);
21389749STim.Haley@Sun.COM 	}
21399749STim.Haley@Sun.COM 
21409749STim.Haley@Sun.COM 	if ((v4_mode & (ACE_DELETE | ACE_DELETE_CHILD)) &&
214111935SMark.Shellenbaum@Sun.COM 	    (zp->z_pflags & ZFS_NOUNLINK)) {
21429749STim.Haley@Sun.COM 		return (EPERM);
21439749STim.Haley@Sun.COM 	}
21449749STim.Haley@Sun.COM 
21459749STim.Haley@Sun.COM 	if (((v4_mode & (ACE_READ_DATA|ACE_EXECUTE)) &&
214611935SMark.Shellenbaum@Sun.COM 	    (zp->z_pflags & ZFS_AV_QUARANTINED))) {
21479749STim.Haley@Sun.COM 		return (EACCES);
21489749STim.Haley@Sun.COM 	}
21499749STim.Haley@Sun.COM 
21509749STim.Haley@Sun.COM 	return (0);
21519749STim.Haley@Sun.COM }
21529749STim.Haley@Sun.COM 
21539749STim.Haley@Sun.COM /*
21549749STim.Haley@Sun.COM  * The primary usage of this function is to loop through all of the
21559749STim.Haley@Sun.COM  * ACEs in the znode, determining what accesses of interest (AoI) to
21569749STim.Haley@Sun.COM  * the caller are allowed or denied.  The AoI are expressed as bits in
21579749STim.Haley@Sun.COM  * the working_mode parameter.  As each ACE is processed, bits covered
21589749STim.Haley@Sun.COM  * by that ACE are removed from the working_mode.  This removal
21599749STim.Haley@Sun.COM  * facilitates two things.  The first is that when the working mode is
21609749STim.Haley@Sun.COM  * empty (= 0), we know we've looked at all the AoI. The second is
21619749STim.Haley@Sun.COM  * that the ACE interpretation rules don't allow a later ACE to undo
21629749STim.Haley@Sun.COM  * something granted or denied by an earlier ACE.  Removing the
21639749STim.Haley@Sun.COM  * discovered access or denial enforces this rule.  At the end of
21649749STim.Haley@Sun.COM  * processing the ACEs, all AoI that were found to be denied are
21659749STim.Haley@Sun.COM  * placed into the working_mode, giving the caller a mask of denied
21669749STim.Haley@Sun.COM  * accesses.  Returns:
21679749STim.Haley@Sun.COM  *	0		if all AoI granted
21689749STim.Haley@Sun.COM  *	EACCESS 	if the denied mask is non-zero
21699749STim.Haley@Sun.COM  *	other error	if abnormal failure (e.g., IO error)
21709749STim.Haley@Sun.COM  *
21719749STim.Haley@Sun.COM  * A secondary usage of the function is to determine if any of the
21729749STim.Haley@Sun.COM  * AoI are granted.  If an ACE grants any access in
21739749STim.Haley@Sun.COM  * the working_mode, we immediately short circuit out of the function.
21749749STim.Haley@Sun.COM  * This mode is chosen by setting anyaccess to B_TRUE.  The
21759749STim.Haley@Sun.COM  * working_mode is not a denied access mask upon exit if the function
21769749STim.Haley@Sun.COM  * is used in this manner.
21779749STim.Haley@Sun.COM  */
21789749STim.Haley@Sun.COM static int
21799749STim.Haley@Sun.COM zfs_zaccess_aces_check(znode_t *zp, uint32_t *working_mode,
21809749STim.Haley@Sun.COM     boolean_t anyaccess, cred_t *cr)
21819749STim.Haley@Sun.COM {
21829749STim.Haley@Sun.COM 	zfsvfs_t	*zfsvfs = zp->z_zfsvfs;
2183789Sahrens 	zfs_acl_t	*aclp;
21841544Seschrock 	int		error;
2185789Sahrens 	uid_t		uid = crgetuid(cr);
21865331Samw 	uint64_t 	who;
21875331Samw 	uint16_t	type, iflags;
21885331Samw 	uint16_t	entry_type;
21895331Samw 	uint32_t	access_mask;
21906056Smarks 	uint32_t	deny_mask = 0;
21915331Samw 	zfs_ace_hdr_t	*acep = NULL;
21925331Samw 	boolean_t	checkit;
219311935SMark.Shellenbaum@Sun.COM 	uint64_t	gowner;
21945331Samw 
2195789Sahrens 	mutex_enter(&zp->z_acl_lock);
2196789Sahrens 
2197*12620SMark.Shellenbaum@Oracle.COM 	error = zfs_acl_node_read(zp, B_FALSE, &aclp, B_FALSE);
21981544Seschrock 	if (error != 0) {
21991544Seschrock 		mutex_exit(&zp->z_acl_lock);
22001544Seschrock 		return (error);
22011544Seschrock 	}
22021544Seschrock 
220311935SMark.Shellenbaum@Sun.COM 	ASSERT(zp->z_acl_cached);
220411935SMark.Shellenbaum@Sun.COM 
220511935SMark.Shellenbaum@Sun.COM 	if ((error = sa_lookup(zp->z_sa_hdl, SA_ZPL_GID(zfsvfs),
220612218SMark.Shellenbaum@Sun.COM 	    &gowner, sizeof (gowner))) != 0) {
220712218SMark.Shellenbaum@Sun.COM 		mutex_exit(&zp->z_acl_lock);
220811935SMark.Shellenbaum@Sun.COM 		return (error);
220912218SMark.Shellenbaum@Sun.COM 	}
221011935SMark.Shellenbaum@Sun.COM 
22115331Samw 	while (acep = zfs_acl_next_ace(aclp, acep, &who, &access_mask,
22125331Samw 	    &iflags, &type)) {
22139749STim.Haley@Sun.COM 		uint32_t mask_matched;
2214789Sahrens 
22157559SMark.Shellenbaum@Sun.COM 		if (!zfs_acl_valid_ace_type(type, iflags))
22167559SMark.Shellenbaum@Sun.COM 			continue;
22177559SMark.Shellenbaum@Sun.COM 
22187057Smarks 		if (ZTOV(zp)->v_type == VDIR && (iflags & ACE_INHERIT_ONLY_ACE))
2219789Sahrens 			continue;
2220789Sahrens 
22219749STim.Haley@Sun.COM 		/* Skip ACE if it does not affect any AoI */
22229749STim.Haley@Sun.COM 		mask_matched = (access_mask & *working_mode);
22239749STim.Haley@Sun.COM 		if (!mask_matched)
22249749STim.Haley@Sun.COM 			continue;
22259749STim.Haley@Sun.COM 
22265331Samw 		entry_type = (iflags & ACE_TYPE_FLAGS);
22275331Samw 
22285331Samw 		checkit = B_FALSE;
22295331Samw 
2230789Sahrens 		switch (entry_type) {
2231789Sahrens 		case ACE_OWNER:
223211935SMark.Shellenbaum@Sun.COM 			if (uid == zp->z_uid)
22335331Samw 				checkit = B_TRUE;
2234789Sahrens 			break;
22355331Samw 		case OWNING_GROUP:
22365331Samw 			who = gowner;
22375331Samw 			/*FALLTHROUGH*/
2238789Sahrens 		case ACE_IDENTIFIER_GROUP:
22395331Samw 			checkit = zfs_groupmember(zfsvfs, who, cr);
2240789Sahrens 			break;
2241789Sahrens 		case ACE_EVERYONE:
22425331Samw 			checkit = B_TRUE;
2243789Sahrens 			break;
2244789Sahrens 
2245789Sahrens 		/* USER Entry */
2246789Sahrens 		default:
2247789Sahrens 			if (entry_type == 0) {
22485331Samw 				uid_t newid;
22495331Samw 
22505959Smarks 				newid = zfs_fuid_map_id(zfsvfs, who, cr,
22515959Smarks 				    ZFS_ACE_USER);
22525331Samw 				if (newid != IDMAP_WK_CREATOR_OWNER_UID &&
22535331Samw 				    uid == newid)
22545331Samw 					checkit = B_TRUE;
2255789Sahrens 				break;
22565331Samw 			} else {
22575331Samw 				mutex_exit(&zp->z_acl_lock);
22585331Samw 				return (EIO);
2259789Sahrens 			}
22605331Samw 		}
22615331Samw 
22625331Samw 		if (checkit) {
22639749STim.Haley@Sun.COM 			if (type == DENY) {
22649749STim.Haley@Sun.COM 				DTRACE_PROBE3(zfs__ace__denies,
22659749STim.Haley@Sun.COM 				    znode_t *, zp,
22669749STim.Haley@Sun.COM 				    zfs_ace_hdr_t *, acep,
22679749STim.Haley@Sun.COM 				    uint32_t, mask_matched);
22689749STim.Haley@Sun.COM 				deny_mask |= mask_matched;
22699749STim.Haley@Sun.COM 			} else {
22709749STim.Haley@Sun.COM 				DTRACE_PROBE3(zfs__ace__allows,
22719749STim.Haley@Sun.COM 				    znode_t *, zp,
22729749STim.Haley@Sun.COM 				    zfs_ace_hdr_t *, acep,
22739749STim.Haley@Sun.COM 				    uint32_t, mask_matched);
22749749STim.Haley@Sun.COM 				if (anyaccess) {
22759749STim.Haley@Sun.COM 					mutex_exit(&zp->z_acl_lock);
22769749STim.Haley@Sun.COM 					return (0);
22779749STim.Haley@Sun.COM 				}
22785331Samw 			}
22799749STim.Haley@Sun.COM 			*working_mode &= ~mask_matched;
2280789Sahrens 		}
2281789Sahrens 
22826056Smarks 		/* Are we done? */
22836056Smarks 		if (*working_mode == 0)
2284789Sahrens 			break;
2285789Sahrens 	}
2286789Sahrens 
2287789Sahrens 	mutex_exit(&zp->z_acl_lock);
22886056Smarks 
22896056Smarks 	/* Put the found 'denies' back on the working mode */
22907163Smarks 	if (deny_mask) {
22917163Smarks 		*working_mode |= deny_mask;
22926056Smarks 		return (EACCES);
22937163Smarks 	} else if (*working_mode) {
22947163Smarks 		return (-1);
22957163Smarks 	}
22966056Smarks 
22976056Smarks 	return (0);
2298789Sahrens }
2299789Sahrens 
23009749STim.Haley@Sun.COM /*
23019749STim.Haley@Sun.COM  * Return true if any access whatsoever granted, we don't actually
23029749STim.Haley@Sun.COM  * care what access is granted.
23039749STim.Haley@Sun.COM  */
23049749STim.Haley@Sun.COM boolean_t
23059749STim.Haley@Sun.COM zfs_has_access(znode_t *zp, cred_t *cr)
23069749STim.Haley@Sun.COM {
23079749STim.Haley@Sun.COM 	uint32_t have = ACE_ALL_PERMS;
23089749STim.Haley@Sun.COM 
23099749STim.Haley@Sun.COM 	if (zfs_zaccess_aces_check(zp, &have, B_TRUE, cr) != 0) {
231012273SCasper.Dik@Sun.COM 		return (secpolicy_vnode_any_access(cr, ZTOV(zp),
231112273SCasper.Dik@Sun.COM 		    zp->z_uid) == 0);
23129749STim.Haley@Sun.COM 	}
23139749STim.Haley@Sun.COM 	return (B_TRUE);
23149749STim.Haley@Sun.COM }
23159749STim.Haley@Sun.COM 
23169749STim.Haley@Sun.COM static int
23179749STim.Haley@Sun.COM zfs_zaccess_common(znode_t *zp, uint32_t v4_mode, uint32_t *working_mode,
23189749STim.Haley@Sun.COM     boolean_t *check_privs, boolean_t skipaclchk, cred_t *cr)
23199749STim.Haley@Sun.COM {
23209749STim.Haley@Sun.COM 	zfsvfs_t *zfsvfs = zp->z_zfsvfs;
23219749STim.Haley@Sun.COM 	int err;
23229749STim.Haley@Sun.COM 
23239749STim.Haley@Sun.COM 	*working_mode = v4_mode;
23249749STim.Haley@Sun.COM 	*check_privs = B_TRUE;
23259749STim.Haley@Sun.COM 
23269749STim.Haley@Sun.COM 	/*
23279749STim.Haley@Sun.COM 	 * Short circuit empty requests
23289749STim.Haley@Sun.COM 	 */
23299749STim.Haley@Sun.COM 	if (v4_mode == 0 || zfsvfs->z_replay) {
23309749STim.Haley@Sun.COM 		*working_mode = 0;
23319749STim.Haley@Sun.COM 		return (0);
23329749STim.Haley@Sun.COM 	}
23339749STim.Haley@Sun.COM 
23349749STim.Haley@Sun.COM 	if ((err = zfs_zaccess_dataset_check(zp, v4_mode)) != 0) {
23359749STim.Haley@Sun.COM 		*check_privs = B_FALSE;
23369749STim.Haley@Sun.COM 		return (err);
23379749STim.Haley@Sun.COM 	}
23389749STim.Haley@Sun.COM 
23399749STim.Haley@Sun.COM 	/*
23409749STim.Haley@Sun.COM 	 * The caller requested that the ACL check be skipped.  This
23419749STim.Haley@Sun.COM 	 * would only happen if the caller checked VOP_ACCESS() with a
23429749STim.Haley@Sun.COM 	 * 32 bit ACE mask and already had the appropriate permissions.
23439749STim.Haley@Sun.COM 	 */
23449749STim.Haley@Sun.COM 	if (skipaclchk) {
23459749STim.Haley@Sun.COM 		*working_mode = 0;
23469749STim.Haley@Sun.COM 		return (0);
23479749STim.Haley@Sun.COM 	}
23489749STim.Haley@Sun.COM 
23499749STim.Haley@Sun.COM 	return (zfs_zaccess_aces_check(zp, working_mode, B_FALSE, cr));
23509749STim.Haley@Sun.COM }
23519749STim.Haley@Sun.COM 
23525331Samw static int
23535331Samw zfs_zaccess_append(znode_t *zp, uint32_t *working_mode, boolean_t *check_privs,
23545331Samw     cred_t *cr)
23555331Samw {
23565331Samw 	if (*working_mode != ACE_WRITE_DATA)
23575331Samw 		return (EACCES);
23585331Samw 
23595331Samw 	return (zfs_zaccess_common(zp, ACE_APPEND_DATA, working_mode,
23605331Samw 	    check_privs, B_FALSE, cr));
23615331Samw }
2362789Sahrens 
23639981STim.Haley@Sun.COM int
23649981STim.Haley@Sun.COM zfs_fastaccesschk_execute(znode_t *zdp, cred_t *cr)
23659981STim.Haley@Sun.COM {
23669981STim.Haley@Sun.COM 	boolean_t owner = B_FALSE;
23679981STim.Haley@Sun.COM 	boolean_t groupmbr = B_FALSE;
23689981STim.Haley@Sun.COM 	boolean_t is_attr;
23699981STim.Haley@Sun.COM 	uid_t uid = crgetuid(cr);
23709981STim.Haley@Sun.COM 	int error;
23719981STim.Haley@Sun.COM 
237211935SMark.Shellenbaum@Sun.COM 	if (zdp->z_pflags & ZFS_AV_QUARANTINED)
23739981STim.Haley@Sun.COM 		return (EACCES);
23749981STim.Haley@Sun.COM 
237511935SMark.Shellenbaum@Sun.COM 	is_attr = ((zdp->z_pflags & ZFS_XATTR) &&
23769981STim.Haley@Sun.COM 	    (ZTOV(zdp)->v_type == VDIR));
23779981STim.Haley@Sun.COM 	if (is_attr)
23789981STim.Haley@Sun.COM 		goto slow;
23799981STim.Haley@Sun.COM 
238011935SMark.Shellenbaum@Sun.COM 
23819981STim.Haley@Sun.COM 	mutex_enter(&zdp->z_acl_lock);
23829981STim.Haley@Sun.COM 
238311935SMark.Shellenbaum@Sun.COM 	if (zdp->z_pflags & ZFS_NO_EXECS_DENIED) {
23849981STim.Haley@Sun.COM 		mutex_exit(&zdp->z_acl_lock);
23859981STim.Haley@Sun.COM 		return (0);
23869981STim.Haley@Sun.COM 	}
23879981STim.Haley@Sun.COM 
238811935SMark.Shellenbaum@Sun.COM 	if (IS_EPHEMERAL(zdp->z_uid) != 0 || IS_EPHEMERAL(zdp->z_gid) != 0) {
23899981STim.Haley@Sun.COM 		mutex_exit(&zdp->z_acl_lock);
23909981STim.Haley@Sun.COM 		goto slow;
23919981STim.Haley@Sun.COM 	}
23929981STim.Haley@Sun.COM 
239311935SMark.Shellenbaum@Sun.COM 	if (uid == zdp->z_uid) {
23949981STim.Haley@Sun.COM 		owner = B_TRUE;
239511935SMark.Shellenbaum@Sun.COM 		if (zdp->z_mode & S_IXUSR) {
23969981STim.Haley@Sun.COM 			mutex_exit(&zdp->z_acl_lock);
23979981STim.Haley@Sun.COM 			return (0);
239810232STim.Haley@Sun.COM 		} else {
239910232STim.Haley@Sun.COM 			mutex_exit(&zdp->z_acl_lock);
240010232STim.Haley@Sun.COM 			goto slow;
24019981STim.Haley@Sun.COM 		}
24029981STim.Haley@Sun.COM 	}
240311935SMark.Shellenbaum@Sun.COM 	if (groupmember(zdp->z_gid, cr)) {
24049981STim.Haley@Sun.COM 		groupmbr = B_TRUE;
240511935SMark.Shellenbaum@Sun.COM 		if (zdp->z_mode & S_IXGRP) {
24069981STim.Haley@Sun.COM 			mutex_exit(&zdp->z_acl_lock);
24079981STim.Haley@Sun.COM 			return (0);
240810232STim.Haley@Sun.COM 		} else {
240910232STim.Haley@Sun.COM 			mutex_exit(&zdp->z_acl_lock);
241010232STim.Haley@Sun.COM 			goto slow;
24119981STim.Haley@Sun.COM 		}
24129981STim.Haley@Sun.COM 	}
24139981STim.Haley@Sun.COM 	if (!owner && !groupmbr) {
241411935SMark.Shellenbaum@Sun.COM 		if (zdp->z_mode & S_IXOTH) {
24159981STim.Haley@Sun.COM 			mutex_exit(&zdp->z_acl_lock);
24169981STim.Haley@Sun.COM 			return (0);
24179981STim.Haley@Sun.COM 		}
24189981STim.Haley@Sun.COM 	}
24199981STim.Haley@Sun.COM 
24209981STim.Haley@Sun.COM 	mutex_exit(&zdp->z_acl_lock);
24219981STim.Haley@Sun.COM 
24229981STim.Haley@Sun.COM slow:
24239981STim.Haley@Sun.COM 	DTRACE_PROBE(zfs__fastpath__execute__access__miss);
24249981STim.Haley@Sun.COM 	ZFS_ENTER(zdp->z_zfsvfs);
24259981STim.Haley@Sun.COM 	error = zfs_zaccess(zdp, ACE_EXECUTE, 0, B_FALSE, cr);
24269981STim.Haley@Sun.COM 	ZFS_EXIT(zdp->z_zfsvfs);
24279981STim.Haley@Sun.COM 	return (error);
24289981STim.Haley@Sun.COM }
24299981STim.Haley@Sun.COM 
2430789Sahrens /*
243112273SCasper.Dik@Sun.COM  * Determine whether Access should be granted/denied.
243212273SCasper.Dik@Sun.COM  * The least priv subsytem is always consulted as a basic privilege
243312273SCasper.Dik@Sun.COM  * can define any form of access.
2434789Sahrens  */
2435789Sahrens int
24365331Samw zfs_zaccess(znode_t *zp, int mode, int flags, boolean_t skipaclchk, cred_t *cr)
2437789Sahrens {
24385331Samw 	uint32_t	working_mode;
24395331Samw 	int		error;
24405331Samw 	int		is_attr;
24415331Samw 	boolean_t 	check_privs;
24425331Samw 	znode_t		*xzp;
24435331Samw 	znode_t 	*check_zp = zp;
244412273SCasper.Dik@Sun.COM 	mode_t		needed_bits;
2445789Sahrens 
244611935SMark.Shellenbaum@Sun.COM 	is_attr = ((zp->z_pflags & ZFS_XATTR) && (ZTOV(zp)->v_type == VDIR));
2447789Sahrens 
2448789Sahrens 	/*
2449789Sahrens 	 * If attribute then validate against base file
2450789Sahrens 	 */
2451789Sahrens 	if (is_attr) {
245211935SMark.Shellenbaum@Sun.COM 		uint64_t	parent;
245311935SMark.Shellenbaum@Sun.COM 
245411935SMark.Shellenbaum@Sun.COM 		if ((error = sa_lookup(zp->z_sa_hdl,
245511935SMark.Shellenbaum@Sun.COM 		    SA_ZPL_PARENT(zp->z_zfsvfs), &parent,
245611935SMark.Shellenbaum@Sun.COM 		    sizeof (parent))) != 0)
245711935SMark.Shellenbaum@Sun.COM 			return (error);
245811935SMark.Shellenbaum@Sun.COM 
2459789Sahrens 		if ((error = zfs_zget(zp->z_zfsvfs,
246011935SMark.Shellenbaum@Sun.COM 		    parent, &xzp)) != 0)	{
2461789Sahrens 			return (error);
2462789Sahrens 		}
24635331Samw 
2464789Sahrens 		check_zp = xzp;
24655331Samw 
2466789Sahrens 		/*
2467789Sahrens 		 * fixup mode to map to xattr perms
2468789Sahrens 		 */
2469789Sahrens 
2470789Sahrens 		if (mode & (ACE_WRITE_DATA|ACE_APPEND_DATA)) {
2471789Sahrens 			mode &= ~(ACE_WRITE_DATA|ACE_APPEND_DATA);
2472789Sahrens 			mode |= ACE_WRITE_NAMED_ATTRS;
2473789Sahrens 		}
2474789Sahrens 
2475789Sahrens 		if (mode & (ACE_READ_DATA|ACE_EXECUTE)) {
2476789Sahrens 			mode &= ~(ACE_READ_DATA|ACE_EXECUTE);
2477789Sahrens 			mode |= ACE_READ_NAMED_ATTRS;
2478789Sahrens 		}
2479789Sahrens 	}
2480789Sahrens 
248112273SCasper.Dik@Sun.COM 	/*
248212273SCasper.Dik@Sun.COM 	 * Map the bits required to the standard vnode flags VREAD|VWRITE|VEXEC
248312273SCasper.Dik@Sun.COM 	 * in needed_bits.  Map the bits mapped by working_mode (currently
248412273SCasper.Dik@Sun.COM 	 * missing) in missing_bits.
248512273SCasper.Dik@Sun.COM 	 * Call secpolicy_vnode_access2() with (needed_bits & ~checkmode),
248612273SCasper.Dik@Sun.COM 	 * needed_bits.
248712273SCasper.Dik@Sun.COM 	 */
248812273SCasper.Dik@Sun.COM 	needed_bits = 0;
248912273SCasper.Dik@Sun.COM 
249012273SCasper.Dik@Sun.COM 	working_mode = mode;
249112273SCasper.Dik@Sun.COM 	if ((working_mode & (ACE_READ_ACL|ACE_READ_ATTRIBUTES)) &&
249212273SCasper.Dik@Sun.COM 	    zp->z_uid == crgetuid(cr))
249312273SCasper.Dik@Sun.COM 		working_mode &= ~(ACE_READ_ACL|ACE_READ_ATTRIBUTES);
249412273SCasper.Dik@Sun.COM 
249512273SCasper.Dik@Sun.COM 	if (working_mode & (ACE_READ_DATA|ACE_READ_NAMED_ATTRS|
249612273SCasper.Dik@Sun.COM 	    ACE_READ_ACL|ACE_READ_ATTRIBUTES|ACE_SYNCHRONIZE))
249712273SCasper.Dik@Sun.COM 		needed_bits |= VREAD;
249812273SCasper.Dik@Sun.COM 	if (working_mode & (ACE_WRITE_DATA|ACE_WRITE_NAMED_ATTRS|
249912273SCasper.Dik@Sun.COM 	    ACE_APPEND_DATA|ACE_WRITE_ATTRIBUTES|ACE_SYNCHRONIZE))
250012273SCasper.Dik@Sun.COM 		needed_bits |= VWRITE;
250112273SCasper.Dik@Sun.COM 	if (working_mode & ACE_EXECUTE)
250212273SCasper.Dik@Sun.COM 		needed_bits |= VEXEC;
250312273SCasper.Dik@Sun.COM 
25045331Samw 	if ((error = zfs_zaccess_common(check_zp, mode, &working_mode,
25055331Samw 	    &check_privs, skipaclchk, cr)) == 0) {
25065331Samw 		if (is_attr)
25075331Samw 			VN_RELE(ZTOV(xzp));
250812273SCasper.Dik@Sun.COM 		return (secpolicy_vnode_access2(cr, ZTOV(zp), zp->z_uid,
250912273SCasper.Dik@Sun.COM 		    needed_bits, needed_bits));
25105331Samw 	}
2511789Sahrens 
25125959Smarks 	if (error && !check_privs) {
2513789Sahrens 		if (is_attr)
2514789Sahrens 			VN_RELE(ZTOV(xzp));
2515789Sahrens 		return (error);
2516789Sahrens 	}
2517789Sahrens 
25185331Samw 	if (error && (flags & V_APPEND)) {
25195331Samw 		error = zfs_zaccess_append(zp, &working_mode, &check_privs, cr);
25205331Samw 	}
25215331Samw 
25225331Samw 	if (error && check_privs) {
25235331Samw 		mode_t		checkmode = 0;
25245331Samw 
25255331Samw 		/*
25265331Samw 		 * First check for implicit owner permission on
25275331Samw 		 * read_acl/read_attributes
25285331Samw 		 */
25295331Samw 
25305331Samw 		error = 0;
25315331Samw 		ASSERT(working_mode != 0);
25325331Samw 
25335331Samw 		if ((working_mode & (ACE_READ_ACL|ACE_READ_ATTRIBUTES) &&
253411935SMark.Shellenbaum@Sun.COM 		    zp->z_uid == crgetuid(cr)))
25355331Samw 			working_mode &= ~(ACE_READ_ACL|ACE_READ_ATTRIBUTES);
25365331Samw 
25375331Samw 		if (working_mode & (ACE_READ_DATA|ACE_READ_NAMED_ATTRS|
25387624SMark.Shellenbaum@Sun.COM 		    ACE_READ_ACL|ACE_READ_ATTRIBUTES|ACE_SYNCHRONIZE))
25395331Samw 			checkmode |= VREAD;
25405331Samw 		if (working_mode & (ACE_WRITE_DATA|ACE_WRITE_NAMED_ATTRS|
25417624SMark.Shellenbaum@Sun.COM 		    ACE_APPEND_DATA|ACE_WRITE_ATTRIBUTES|ACE_SYNCHRONIZE))
25425331Samw 			checkmode |= VWRITE;
25435331Samw 		if (working_mode & ACE_EXECUTE)
25445331Samw 			checkmode |= VEXEC;
25455331Samw 
254612273SCasper.Dik@Sun.COM 		error = secpolicy_vnode_access2(cr, ZTOV(check_zp), zp->z_uid,
254712273SCasper.Dik@Sun.COM 		    needed_bits & ~checkmode, needed_bits);
25485331Samw 
25495331Samw 		if (error == 0 && (working_mode & ACE_WRITE_OWNER))
255011935SMark.Shellenbaum@Sun.COM 			error = secpolicy_vnode_chown(cr, zp->z_uid);
25515331Samw 		if (error == 0 && (working_mode & ACE_WRITE_ACL))
255211935SMark.Shellenbaum@Sun.COM 			error = secpolicy_vnode_setdac(cr, zp->z_uid);
25535331Samw 
25545331Samw 		if (error == 0 && (working_mode &
25555331Samw 		    (ACE_DELETE|ACE_DELETE_CHILD)))
25565331Samw 			error = secpolicy_vnode_remove(cr);
25575331Samw 
25587624SMark.Shellenbaum@Sun.COM 		if (error == 0 && (working_mode & ACE_SYNCHRONIZE)) {
255911935SMark.Shellenbaum@Sun.COM 			error = secpolicy_vnode_chown(cr, zp->z_uid);
25607624SMark.Shellenbaum@Sun.COM 		}
25615331Samw 		if (error == 0) {
25625331Samw 			/*
25635331Samw 			 * See if any bits other than those already checked
25645331Samw 			 * for are still present.  If so then return EACCES
25655331Samw 			 */
25665331Samw 			if (working_mode & ~(ZFS_CHECKED_MASKS)) {
25675331Samw 				error = EACCES;
25685331Samw 			}
25695331Samw 		}
257012273SCasper.Dik@Sun.COM 	} else if (error == 0) {
257112273SCasper.Dik@Sun.COM 		error = secpolicy_vnode_access2(cr, ZTOV(zp), zp->z_uid,
257212273SCasper.Dik@Sun.COM 		    needed_bits, needed_bits);
2573789Sahrens 	}
2574789Sahrens 
257512273SCasper.Dik@Sun.COM 
2576789Sahrens 	if (is_attr)
2577789Sahrens 		VN_RELE(ZTOV(xzp));
2578789Sahrens 
2579789Sahrens 	return (error);
2580789Sahrens }
2581789Sahrens 
2582789Sahrens /*
25835331Samw  * Translate traditional unix VREAD/VWRITE/VEXEC mode into
25845331Samw  * native ACL format and call zfs_zaccess()
2585789Sahrens  */
2586789Sahrens int
25875331Samw zfs_zaccess_rwx(znode_t *zp, mode_t mode, int flags, cred_t *cr)
2588789Sahrens {
25895331Samw 	return (zfs_zaccess(zp, zfs_unix_to_v4(mode >> 6), flags, B_FALSE, cr));
2590789Sahrens }
2591789Sahrens 
2592789Sahrens /*
25935331Samw  * Access function for secpolicy_vnode_setattr
2594789Sahrens  */
2595789Sahrens int
25965331Samw zfs_zaccess_unix(znode_t *zp, mode_t mode, cred_t *cr)
2597789Sahrens {
2598789Sahrens 	int v4_mode = zfs_unix_to_v4(mode >> 6);
2599789Sahrens 
26005331Samw 	return (zfs_zaccess(zp, v4_mode, 0, B_FALSE, cr));
2601789Sahrens }
2602789Sahrens 
26032604Smarks static int
26046257Smarks zfs_delete_final_check(znode_t *zp, znode_t *dzp,
260512273SCasper.Dik@Sun.COM     mode_t available_perms, cred_t *cr)
26062604Smarks {
26072604Smarks 	int error;
260811935SMark.Shellenbaum@Sun.COM 
260912273SCasper.Dik@Sun.COM 	error = secpolicy_vnode_access2(cr, ZTOV(dzp),
261012273SCasper.Dik@Sun.COM 	    dzp->z_uid, available_perms, VWRITE|VEXEC);
26112604Smarks 
26122604Smarks 	if (error == 0)
26132604Smarks 		error = zfs_sticky_remove_access(dzp, zp, cr);
26142604Smarks 
26152604Smarks 	return (error);
26162604Smarks }
26172604Smarks 
2618789Sahrens /*
2619789Sahrens  * Determine whether Access should be granted/deny, without
2620789Sahrens  * consulting least priv subsystem.
2621789Sahrens  *
2622789Sahrens  *
2623789Sahrens  * The following chart is the recommended NFSv4 enforcement for
2624789Sahrens  * ability to delete an object.
2625789Sahrens  *
2626789Sahrens  *      -------------------------------------------------------
2627789Sahrens  *      |   Parent Dir  |           Target Object Permissions |
2628789Sahrens  *      |  permissions  |                                     |
2629789Sahrens  *      -------------------------------------------------------
2630789Sahrens  *      |               | ACL Allows | ACL Denies| Delete     |
2631789Sahrens  *      |               |  Delete    |  Delete   | unspecified|
2632789Sahrens  *      -------------------------------------------------------
2633789Sahrens  *      |  ACL Allows   | Permit     | Permit    | Permit     |
2634789Sahrens  *      |  DELETE_CHILD |                                     |
2635789Sahrens  *      -------------------------------------------------------
2636789Sahrens  *      |  ACL Denies   | Permit     | Deny      | Deny       |
2637789Sahrens  *      |  DELETE_CHILD |            |           |            |
2638789Sahrens  *      -------------------------------------------------------
2639789Sahrens  *      | ACL specifies |            |           |            |
2640789Sahrens  *      | only allow    | Permit     | Permit    | Permit     |
2641789Sahrens  *      | write and     |            |           |            |
2642789Sahrens  *      | execute       |            |           |            |
2643789Sahrens  *      -------------------------------------------------------
2644789Sahrens  *      | ACL denies    |            |           |            |
2645789Sahrens  *      | write and     | Permit     | Deny      | Deny       |
2646789Sahrens  *      | execute       |            |           |            |
2647789Sahrens  *      -------------------------------------------------------
2648789Sahrens  *         ^
2649789Sahrens  *         |
2650789Sahrens  *         No search privilege, can't even look up file?
2651789Sahrens  *
2652789Sahrens  */
2653789Sahrens int
2654789Sahrens zfs_zaccess_delete(znode_t *dzp, znode_t *zp, cred_t *cr)
2655789Sahrens {
26565331Samw 	uint32_t dzp_working_mode = 0;
26575331Samw 	uint32_t zp_working_mode = 0;
2658789Sahrens 	int dzp_error, zp_error;
265912273SCasper.Dik@Sun.COM 	mode_t available_perms;
26605331Samw 	boolean_t dzpcheck_privs = B_TRUE;
26615331Samw 	boolean_t zpcheck_privs = B_TRUE;
2662789Sahrens 
2663789Sahrens 	/*
26646257Smarks 	 * We want specific DELETE permissions to
2665789Sahrens 	 * take precedence over WRITE/EXECUTE.  We don't
2666789Sahrens 	 * want an ACL such as this to mess us up.
26672604Smarks 	 * user:joe:write_data:deny,user:joe:delete:allow
2668789Sahrens 	 *
2669789Sahrens 	 * However, deny permissions may ultimately be overridden
2670789Sahrens 	 * by secpolicy_vnode_access().
26716257Smarks 	 *
26726257Smarks 	 * We will ask for all of the necessary permissions and then
26736257Smarks 	 * look at the working modes from the directory and target object
26746257Smarks 	 * to determine what was found.
2675789Sahrens 	 */
2676789Sahrens 
267711935SMark.Shellenbaum@Sun.COM 	if (zp->z_pflags & (ZFS_IMMUTABLE | ZFS_NOUNLINK))
26785331Samw 		return (EPERM);
26795331Samw 
26806257Smarks 	/*
26817163Smarks 	 * First row
26826257Smarks 	 * If the directory permissions allow the delete, we are done.
26836257Smarks 	 */
26847163Smarks 	if ((dzp_error = zfs_zaccess_common(dzp, ACE_DELETE_CHILD,
26856257Smarks 	    &dzp_working_mode, &dzpcheck_privs, B_FALSE, cr)) == 0)
26866257Smarks 		return (0);
2687789Sahrens 
26886257Smarks 	/*
26896257Smarks 	 * If target object has delete permission then we are done
26906257Smarks 	 */
26916257Smarks 	if ((zp_error = zfs_zaccess_common(zp, ACE_DELETE, &zp_working_mode,
26926257Smarks 	    &zpcheck_privs, B_FALSE, cr)) == 0)
26936257Smarks 		return (0);
26946257Smarks 
26957163Smarks 	ASSERT(dzp_error && zp_error);
26967163Smarks 
26976257Smarks 	if (!dzpcheck_privs)
2698789Sahrens 		return (dzp_error);
26997163Smarks 	if (!zpcheck_privs)
27006257Smarks 		return (zp_error);
2701789Sahrens 
2702789Sahrens 	/*
2703789Sahrens 	 * Second row
27047163Smarks 	 *
27057163Smarks 	 * If directory returns EACCES then delete_child was denied
27067163Smarks 	 * due to deny delete_child.  In this case send the request through
27077163Smarks 	 * secpolicy_vnode_remove().  We don't use zfs_delete_final_check()
27087163Smarks 	 * since that *could* allow the delete based on write/execute permission
27097163Smarks 	 * and we want delete permissions to override write/execute.
2710789Sahrens 	 */
2711789Sahrens 
27122604Smarks 	if (dzp_error == EACCES)
27137163Smarks 		return (secpolicy_vnode_remove(cr));
27142604Smarks 
27152604Smarks 	/*
2716789Sahrens 	 * Third Row
27176257Smarks 	 * only need to see if we have write/execute on directory.
2718789Sahrens 	 */
2719789Sahrens 
272012273SCasper.Dik@Sun.COM 	dzp_error = zfs_zaccess_common(dzp, ACE_EXECUTE|ACE_WRITE_DATA,
272112273SCasper.Dik@Sun.COM 	    &dzp_working_mode, &dzpcheck_privs, B_FALSE, cr);
2722789Sahrens 
272312273SCasper.Dik@Sun.COM 	if (dzp_error != 0 && !dzpcheck_privs)
27247163Smarks 		return (dzp_error);
27257163Smarks 
2726789Sahrens 	/*
27277163Smarks 	 * Fourth row
2728789Sahrens 	 */
2729789Sahrens 
273012273SCasper.Dik@Sun.COM 	available_perms = (dzp_working_mode & ACE_WRITE_DATA) ? 0 : VWRITE;
273112273SCasper.Dik@Sun.COM 	available_perms |= (dzp_working_mode & ACE_EXECUTE) ? 0 : VEXEC;
27327163Smarks 
273312273SCasper.Dik@Sun.COM 	return (zfs_delete_final_check(zp, dzp, available_perms, cr));
27347163Smarks 
2735789Sahrens }
2736789Sahrens 
2737789Sahrens int
2738789Sahrens zfs_zaccess_rename(znode_t *sdzp, znode_t *szp, znode_t *tdzp,
2739789Sahrens     znode_t *tzp, cred_t *cr)
2740789Sahrens {
2741789Sahrens 	int add_perm;
2742789Sahrens 	int error;
2743789Sahrens 
274411935SMark.Shellenbaum@Sun.COM 	if (szp->z_pflags & ZFS_AV_QUARANTINED)
27455331Samw 		return (EACCES);
27465331Samw 
2747789Sahrens 	add_perm = (ZTOV(szp)->v_type == VDIR) ?
2748789Sahrens 	    ACE_ADD_SUBDIRECTORY : ACE_ADD_FILE;
2749789Sahrens 
2750789Sahrens 	/*
2751789Sahrens 	 * Rename permissions are combination of delete permission +
2752789Sahrens 	 * add file/subdir permission.
2753789Sahrens 	 */
2754789Sahrens 
2755789Sahrens 	/*
2756789Sahrens 	 * first make sure we do the delete portion.
2757789Sahrens 	 *
2758789Sahrens 	 * If that succeeds then check for add_file/add_subdir permissions
2759789Sahrens 	 */
2760789Sahrens 
2761789Sahrens 	if (error = zfs_zaccess_delete(sdzp, szp, cr))
2762789Sahrens 		return (error);
2763789Sahrens 
2764789Sahrens 	/*
2765789Sahrens 	 * If we have a tzp, see if we can delete it?
2766789Sahrens 	 */
2767789Sahrens 	if (tzp) {
2768789Sahrens 		if (error = zfs_zaccess_delete(tdzp, tzp, cr))
2769789Sahrens 			return (error);
2770789Sahrens 	}
2771789Sahrens 
2772789Sahrens 	/*
2773789Sahrens 	 * Now check for add permissions
2774789Sahrens 	 */
27755331Samw 	error = zfs_zaccess(tdzp, add_perm, 0, B_FALSE, cr);
2776789Sahrens 
2777789Sahrens 	return (error);
2778789Sahrens }
2779