xref: /titanic_44/usr/src/lib/libsec/common/aclutils.c (revision 0157963dec6f442e8566c5903063c99c18ac358b)
1fa9e4066Sahrens /*
2fa9e4066Sahrens  * CDDL HEADER START
3fa9e4066Sahrens  *
4fa9e4066Sahrens  * The contents of this file are subject to the terms of the
53eb3c573Smarks  * Common Development and Distribution License (the "License").
63eb3c573Smarks  * You may not use this file except in compliance with the License.
7fa9e4066Sahrens  *
8fa9e4066Sahrens  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9fa9e4066Sahrens  * or http://www.opensolaris.org/os/licensing.
10fa9e4066Sahrens  * See the License for the specific language governing permissions
11fa9e4066Sahrens  * and limitations under the License.
12fa9e4066Sahrens  *
13fa9e4066Sahrens  * When distributing Covered Code, include this CDDL HEADER in each
14fa9e4066Sahrens  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15fa9e4066Sahrens  * If applicable, add the following below this CDDL HEADER, with the
16fa9e4066Sahrens  * fields enclosed by brackets "[]" replaced with your own identifying
17fa9e4066Sahrens  * information: Portions Copyright [yyyy] [name of copyright owner]
18fa9e4066Sahrens  *
19fa9e4066Sahrens  * CDDL HEADER END
20fa9e4066Sahrens  */
21fa9e4066Sahrens /*
22d2443e76Smarks  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23fa9e4066Sahrens  * Use is subject to license terms.
24fa9e4066Sahrens  */
25fa9e4066Sahrens 
26fa9e4066Sahrens #pragma ident	"%Z%%M%	%I%	%E% SMI"
27fa9e4066Sahrens 
28fa9e4066Sahrens #include <stdlib.h>
29fa9e4066Sahrens #include <string.h>
30fa9e4066Sahrens #include <unistd.h>
31fa9e4066Sahrens #include <limits.h>
32fa9e4066Sahrens #include <grp.h>
33fa9e4066Sahrens #include <pwd.h>
343eb3c573Smarks #include <strings.h>
35fa9e4066Sahrens #include <sys/types.h>
36fa9e4066Sahrens #include <sys/acl.h>
37fa9e4066Sahrens #include <errno.h>
38fa9e4066Sahrens #include <sys/stat.h>
395a5eeccaSmarks #include <sys/varargs.h>
40fa9e4066Sahrens #include <locale.h>
41fa9e4066Sahrens #include <aclutils.h>
42fa9e4066Sahrens #include <acl_common.h>
433eb3c573Smarks #include <sys/avl.h>
443eb3c573Smarks 
453eb3c573Smarks #define	offsetof(s, m)	((size_t)(&(((s *)0)->m)))
46fa9e4066Sahrens 
47fa9e4066Sahrens #define	ACL_PATH	0
48fa9e4066Sahrens #define	ACL_FD		1
49fa9e4066Sahrens 
50fa9e4066Sahrens #define	ACE_POSIX_SUPPORTED_BITS (ACE_READ_DATA | \
51fa9e4066Sahrens     ACE_WRITE_DATA | ACE_APPEND_DATA | ACE_EXECUTE | \
52fa9e4066Sahrens     ACE_READ_ATTRIBUTES | ACE_READ_ACL | ACE_WRITE_ACL)
53fa9e4066Sahrens 
54fa9e4066Sahrens 
55fa9e4066Sahrens #define	ACL_SYNCHRONIZE_SET_DENY		0x0000001
563eb3c573Smarks #define	ACL_SYNCHRONIZE_SET_ALLOW		0x0000002
573eb3c573Smarks #define	ACL_SYNCHRONIZE_ERR_DENY		0x0000004
583eb3c573Smarks #define	ACL_SYNCHRONIZE_ERR_ALLOW		0x0000008
59fa9e4066Sahrens 
60fa9e4066Sahrens #define	ACL_WRITE_OWNER_SET_DENY		0x0000010
613eb3c573Smarks #define	ACL_WRITE_OWNER_SET_ALLOW		0x0000020
623eb3c573Smarks #define	ACL_WRITE_OWNER_ERR_DENY		0x0000040
633eb3c573Smarks #define	ACL_WRITE_OWNER_ERR_ALLOW		0x0000080
64fa9e4066Sahrens 
653eb3c573Smarks #define	ACL_DELETE_SET_DENY			0x0000100
663eb3c573Smarks #define	ACL_DELETE_SET_ALLOW			0x0000200
673eb3c573Smarks #define	ACL_DELETE_ERR_DENY			0x0000400
683eb3c573Smarks #define	ACL_DELETE_ERR_ALLOW			0x0000800
693eb3c573Smarks 
70fa9e4066Sahrens #define	ACL_WRITE_ATTRS_OWNER_SET_DENY		0x0001000
713eb3c573Smarks #define	ACL_WRITE_ATTRS_OWNER_SET_ALLOW		0x0002000
723eb3c573Smarks #define	ACL_WRITE_ATTRS_OWNER_ERR_DENY		0x0004000
733eb3c573Smarks #define	ACL_WRITE_ATTRS_OWNER_ERR_ALLOW		0x0008000
74fa9e4066Sahrens 
75fa9e4066Sahrens #define	ACL_WRITE_ATTRS_WRITER_SET_DENY		0x0010000
76fa9e4066Sahrens #define	ACL_WRITE_ATTRS_WRITER_SET_ALLOW	0x0020000
773eb3c573Smarks #define	ACL_WRITE_ATTRS_WRITER_ERR_DENY		0x0040000
783eb3c573Smarks #define	ACL_WRITE_ATTRS_WRITER_ERR_ALLOW	0x0080000
79fa9e4066Sahrens 
803eb3c573Smarks #define	ACL_WRITE_NAMED_WRITER_SET_DENY		0x0100000
813eb3c573Smarks #define	ACL_WRITE_NAMED_WRITER_SET_ALLOW	0x0200000
823eb3c573Smarks #define	ACL_WRITE_NAMED_WRITER_ERR_DENY		0x0400000
833eb3c573Smarks #define	ACL_WRITE_NAMED_WRITER_ERR_ALLOW	0x0800000
843eb3c573Smarks 
85fa9e4066Sahrens #define	ACL_READ_NAMED_READER_SET_DENY		0x1000000
863eb3c573Smarks #define	ACL_READ_NAMED_READER_SET_ALLOW		0x2000000
873eb3c573Smarks #define	ACL_READ_NAMED_READER_ERR_DENY		0x4000000
883eb3c573Smarks #define	ACL_READ_NAMED_READER_ERR_ALLOW		0x8000000
893eb3c573Smarks 
903eb3c573Smarks 
913eb3c573Smarks #define	ACE_VALID_MASK_BITS (\
923eb3c573Smarks     ACE_READ_DATA | \
933eb3c573Smarks     ACE_LIST_DIRECTORY | \
943eb3c573Smarks     ACE_WRITE_DATA | \
953eb3c573Smarks     ACE_ADD_FILE | \
963eb3c573Smarks     ACE_APPEND_DATA | \
973eb3c573Smarks     ACE_ADD_SUBDIRECTORY | \
983eb3c573Smarks     ACE_READ_NAMED_ATTRS | \
993eb3c573Smarks     ACE_WRITE_NAMED_ATTRS | \
1003eb3c573Smarks     ACE_EXECUTE | \
1013eb3c573Smarks     ACE_DELETE_CHILD | \
1023eb3c573Smarks     ACE_READ_ATTRIBUTES | \
1033eb3c573Smarks     ACE_WRITE_ATTRIBUTES | \
1043eb3c573Smarks     ACE_DELETE | \
1053eb3c573Smarks     ACE_READ_ACL | \
1063eb3c573Smarks     ACE_WRITE_ACL | \
1073eb3c573Smarks     ACE_WRITE_OWNER | \
1083eb3c573Smarks     ACE_SYNCHRONIZE)
1093eb3c573Smarks 
1103eb3c573Smarks #define	ACE_MASK_UNDEFINED			0x80000000
1113eb3c573Smarks 
1123eb3c573Smarks #define	ACE_VALID_FLAG_BITS (ACE_FILE_INHERIT_ACE | \
1133eb3c573Smarks     ACE_DIRECTORY_INHERIT_ACE | \
1143eb3c573Smarks     ACE_NO_PROPAGATE_INHERIT_ACE | ACE_INHERIT_ONLY_ACE | \
1153eb3c573Smarks     ACE_SUCCESSFUL_ACCESS_ACE_FLAG | ACE_FAILED_ACCESS_ACE_FLAG | \
1163eb3c573Smarks     ACE_IDENTIFIER_GROUP | ACE_OWNER | ACE_GROUP | ACE_EVERYONE)
1173eb3c573Smarks 
1183eb3c573Smarks /*
1193eb3c573Smarks  * ACL conversion helpers
1203eb3c573Smarks  */
1213eb3c573Smarks 
1223eb3c573Smarks typedef enum {
1233eb3c573Smarks 	ace_unused,
1243eb3c573Smarks 	ace_user_obj,
1253eb3c573Smarks 	ace_user,
1263eb3c573Smarks 	ace_group, /* includes GROUP and GROUP_OBJ */
1273eb3c573Smarks 	ace_other_obj
1283eb3c573Smarks } ace_to_aent_state_t;
1293eb3c573Smarks 
1303eb3c573Smarks typedef struct acevals {
1313eb3c573Smarks 	uid_t key;
1323eb3c573Smarks 	avl_node_t avl;
1333eb3c573Smarks 	uint32_t mask;
1343eb3c573Smarks 	uint32_t allowed;
1353eb3c573Smarks 	uint32_t denied;
1363eb3c573Smarks 	int aent_type;
1373eb3c573Smarks } acevals_t;
1383eb3c573Smarks 
1393eb3c573Smarks typedef struct ace_list {
1403eb3c573Smarks 	acevals_t user_obj;
1413eb3c573Smarks 	avl_tree_t user;
1423eb3c573Smarks 	int numusers;
1433eb3c573Smarks 	acevals_t group_obj;
1443eb3c573Smarks 	avl_tree_t group;
1453eb3c573Smarks 	int numgroups;
1463eb3c573Smarks 	acevals_t other_obj;
1473eb3c573Smarks 	uint32_t acl_mask;
1483eb3c573Smarks 	int hasmask;
1493eb3c573Smarks 	int dfacl_flag;
1503eb3c573Smarks 	ace_to_aent_state_t state;
1513eb3c573Smarks 	int seen; /* bitmask of all aclent_t a_type values seen */
1523eb3c573Smarks } ace_list_t;
153d2443e76Smarks 
154fa9e4066Sahrens typedef union {
155fa9e4066Sahrens 	const char *file;
156fa9e4066Sahrens 	int  fd;
157fa9e4066Sahrens } acl_inp;
158fa9e4066Sahrens 
159fa9e4066Sahrens acl_t *
160fa9e4066Sahrens acl_alloc(enum acl_type type)
161fa9e4066Sahrens {
162fa9e4066Sahrens 	acl_t *aclp;
163fa9e4066Sahrens 
164fa9e4066Sahrens 	aclp = malloc(sizeof (acl_t));
165fa9e4066Sahrens 
166fa9e4066Sahrens 	if (aclp == NULL)
167fa9e4066Sahrens 		return (NULL);
168fa9e4066Sahrens 
169fa9e4066Sahrens 	aclp->acl_aclp = NULL;
170fa9e4066Sahrens 	aclp->acl_cnt = 0;
171fa9e4066Sahrens 
172fa9e4066Sahrens 	switch (type) {
173fa9e4066Sahrens 	case ACE_T:
174fa9e4066Sahrens 		aclp->acl_type = ACE_T;
175fa9e4066Sahrens 		aclp->acl_entry_size = sizeof (ace_t);
176fa9e4066Sahrens 		break;
177fa9e4066Sahrens 	case ACLENT_T:
178fa9e4066Sahrens 		aclp->acl_type = ACLENT_T;
179fa9e4066Sahrens 		aclp->acl_entry_size = sizeof (aclent_t);
180fa9e4066Sahrens 		break;
181fa9e4066Sahrens 	default:
182fa9e4066Sahrens 		acl_free(aclp);
183fa9e4066Sahrens 		aclp = NULL;
184fa9e4066Sahrens 	}
185fa9e4066Sahrens 	return (aclp);
186fa9e4066Sahrens }
187fa9e4066Sahrens 
188fa9e4066Sahrens /*
189fa9e4066Sahrens  * Free acl_t structure
190fa9e4066Sahrens  */
191fa9e4066Sahrens void
192fa9e4066Sahrens acl_free(acl_t *aclp)
193fa9e4066Sahrens {
194fa9e4066Sahrens 	if (aclp == NULL)
195fa9e4066Sahrens 		return;
196fa9e4066Sahrens 
197fa9e4066Sahrens 	if (aclp->acl_aclp)
198fa9e4066Sahrens 		free(aclp->acl_aclp);
199fa9e4066Sahrens 	free(aclp);
200fa9e4066Sahrens }
201fa9e4066Sahrens 
202fa9e4066Sahrens /*
203fa9e4066Sahrens  * Determine whether a file has a trivial ACL
204fa9e4066Sahrens  * returns: 	0 = trivial
205fa9e4066Sahrens  *		1 = nontrivial
206fa9e4066Sahrens  *		<0 some other system failure, such as ENOENT or EPERM
207fa9e4066Sahrens  */
208fa9e4066Sahrens int
209fa9e4066Sahrens acl_trivial(const char *filename)
210fa9e4066Sahrens {
211fa9e4066Sahrens 	int acl_flavor;
212fa9e4066Sahrens 	int aclcnt;
213fa9e4066Sahrens 	int cntcmd;
214fa9e4066Sahrens 	int val = 0;
215fa9e4066Sahrens 	ace_t *acep;
216fa9e4066Sahrens 
217fa9e4066Sahrens 	acl_flavor = pathconf(filename, _PC_ACL_ENABLED);
218fa9e4066Sahrens 	if (acl_flavor == -1)
219fa9e4066Sahrens 		return (-1);
220fa9e4066Sahrens 
221fa9e4066Sahrens 	if (acl_flavor == _ACL_ACE_ENABLED)
222fa9e4066Sahrens 		cntcmd = ACE_GETACLCNT;
223fa9e4066Sahrens 	else
224fa9e4066Sahrens 		cntcmd = GETACLCNT;
225fa9e4066Sahrens 
226fa9e4066Sahrens 	aclcnt = acl(filename, cntcmd, 0, NULL);
227fa9e4066Sahrens 	if (aclcnt > 0) {
228fa9e4066Sahrens 		if (acl_flavor == _ACL_ACE_ENABLED) {
229fa9e4066Sahrens 			acep = malloc(sizeof (ace_t) * aclcnt);
230fa9e4066Sahrens 			if (acep == NULL)
231fa9e4066Sahrens 				return (-1);
232fa9e4066Sahrens 			if (acl(filename, ACE_GETACL,
233fa9e4066Sahrens 			    aclcnt, acep) < 0) {
234fa9e4066Sahrens 				free(acep);
235fa9e4066Sahrens 				return (-1);
236fa9e4066Sahrens 			}
237fa9e4066Sahrens 
238fa9e4066Sahrens 			val = ace_trivial(acep, aclcnt);
239fa9e4066Sahrens 			free(acep);
240d2443e76Smarks 
241fa9e4066Sahrens 		} else if (aclcnt > MIN_ACL_ENTRIES)
242fa9e4066Sahrens 			val = 1;
243fa9e4066Sahrens 	}
244fa9e4066Sahrens 	return (val);
245fa9e4066Sahrens }
246fa9e4066Sahrens 
247fa9e4066Sahrens static uint32_t
248fa9e4066Sahrens access_mask_set(int haswriteperm, int hasreadperm, int isowner, int isallow)
249fa9e4066Sahrens {
250fa9e4066Sahrens 	uint32_t access_mask = 0;
251fa9e4066Sahrens 	int acl_produce;
252fa9e4066Sahrens 	int synchronize_set = 0, write_owner_set = 0;
253fa9e4066Sahrens 	int delete_set = 0, write_attrs_set = 0;
254fa9e4066Sahrens 	int read_named_set = 0, write_named_set = 0;
255fa9e4066Sahrens 
256fa9e4066Sahrens 	acl_produce = (ACL_SYNCHRONIZE_SET_ALLOW |
257fa9e4066Sahrens 	    ACL_WRITE_ATTRS_OWNER_SET_ALLOW |
258fa9e4066Sahrens 	    ACL_WRITE_ATTRS_WRITER_SET_DENY);
259fa9e4066Sahrens 
260fa9e4066Sahrens 	if (isallow) {
261fa9e4066Sahrens 		synchronize_set = ACL_SYNCHRONIZE_SET_ALLOW;
262fa9e4066Sahrens 		write_owner_set = ACL_WRITE_OWNER_SET_ALLOW;
263fa9e4066Sahrens 		delete_set = ACL_DELETE_SET_ALLOW;
264fa9e4066Sahrens 		if (hasreadperm)
265fa9e4066Sahrens 			read_named_set = ACL_READ_NAMED_READER_SET_ALLOW;
266fa9e4066Sahrens 		if (haswriteperm)
267fa9e4066Sahrens 			write_named_set = ACL_WRITE_NAMED_WRITER_SET_ALLOW;
268fa9e4066Sahrens 		if (isowner)
269fa9e4066Sahrens 			write_attrs_set = ACL_WRITE_ATTRS_OWNER_SET_ALLOW;
270fa9e4066Sahrens 		else if (haswriteperm)
271fa9e4066Sahrens 			write_attrs_set = ACL_WRITE_ATTRS_WRITER_SET_ALLOW;
272fa9e4066Sahrens 	} else {
273fa9e4066Sahrens 
274fa9e4066Sahrens 		synchronize_set = ACL_SYNCHRONIZE_SET_DENY;
275fa9e4066Sahrens 		write_owner_set = ACL_WRITE_OWNER_SET_DENY;
276fa9e4066Sahrens 		delete_set = ACL_DELETE_SET_DENY;
277fa9e4066Sahrens 		if (hasreadperm)
278fa9e4066Sahrens 			read_named_set = ACL_READ_NAMED_READER_SET_DENY;
279fa9e4066Sahrens 		if (haswriteperm)
280fa9e4066Sahrens 			write_named_set = ACL_WRITE_NAMED_WRITER_SET_DENY;
281fa9e4066Sahrens 		if (isowner)
282fa9e4066Sahrens 			write_attrs_set = ACL_WRITE_ATTRS_OWNER_SET_DENY;
283fa9e4066Sahrens 		else if (haswriteperm)
284fa9e4066Sahrens 			write_attrs_set = ACL_WRITE_ATTRS_WRITER_SET_DENY;
285fa9e4066Sahrens 		else
286fa9e4066Sahrens 			/*
287fa9e4066Sahrens 			 * If the entity is not the owner and does not
288fa9e4066Sahrens 			 * have write permissions ACE_WRITE_ATTRIBUTES will
289fa9e4066Sahrens 			 * always go in the DENY ACE.
290fa9e4066Sahrens 			 */
291fa9e4066Sahrens 			access_mask |= ACE_WRITE_ATTRIBUTES;
292fa9e4066Sahrens 	}
293fa9e4066Sahrens 
294fa9e4066Sahrens 	if (acl_produce & synchronize_set)
295fa9e4066Sahrens 		access_mask |= ACE_SYNCHRONIZE;
296fa9e4066Sahrens 	if (acl_produce & write_owner_set)
297fa9e4066Sahrens 		access_mask |= ACE_WRITE_OWNER;
298fa9e4066Sahrens 	if (acl_produce & delete_set)
299fa9e4066Sahrens 		access_mask |= ACE_DELETE;
300fa9e4066Sahrens 	if (acl_produce & write_attrs_set)
301fa9e4066Sahrens 		access_mask |= ACE_WRITE_ATTRIBUTES;
302fa9e4066Sahrens 	if (acl_produce & read_named_set)
303fa9e4066Sahrens 		access_mask |= ACE_READ_NAMED_ATTRS;
304fa9e4066Sahrens 	if (acl_produce & write_named_set)
305fa9e4066Sahrens 		access_mask |= ACE_WRITE_NAMED_ATTRS;
306fa9e4066Sahrens 
307fa9e4066Sahrens 	return (access_mask);
308fa9e4066Sahrens }
309fa9e4066Sahrens 
310fa9e4066Sahrens /*
311fa9e4066Sahrens  * Given an mode_t, convert it into an access_mask as used
312fa9e4066Sahrens  * by nfsace, assuming aclent_t -> nfsace semantics.
313fa9e4066Sahrens  */
314fa9e4066Sahrens static uint32_t
315fa9e4066Sahrens mode_to_ace_access(mode_t mode, int isdir, int isowner, int isallow)
316fa9e4066Sahrens {
317fa9e4066Sahrens 	uint32_t access = 0;
318fa9e4066Sahrens 	int haswriteperm = 0;
319fa9e4066Sahrens 	int hasreadperm = 0;
320fa9e4066Sahrens 
321fa9e4066Sahrens 	if (isallow) {
322fa9e4066Sahrens 		haswriteperm = (mode & 02);
323fa9e4066Sahrens 		hasreadperm = (mode & 04);
324fa9e4066Sahrens 	} else {
325fa9e4066Sahrens 		haswriteperm = !(mode & 02);
326fa9e4066Sahrens 		hasreadperm = !(mode & 04);
327fa9e4066Sahrens 	}
328fa9e4066Sahrens 
329fa9e4066Sahrens 	/*
330fa9e4066Sahrens 	 * The following call takes care of correctly setting the following
331fa9e4066Sahrens 	 * mask bits in the access_mask:
332fa9e4066Sahrens 	 * ACE_SYNCHRONIZE, ACE_WRITE_OWNER, ACE_DELETE,
333fa9e4066Sahrens 	 * ACE_WRITE_ATTRIBUTES, ACE_WRITE_NAMED_ATTRS, ACE_READ_NAMED_ATTRS
334fa9e4066Sahrens 	 */
335fa9e4066Sahrens 	access = access_mask_set(haswriteperm, hasreadperm, isowner, isallow);
336fa9e4066Sahrens 
337fa9e4066Sahrens 	if (isallow) {
338fa9e4066Sahrens 		access |= ACE_READ_ACL | ACE_READ_ATTRIBUTES;
339fa9e4066Sahrens 		if (isowner)
340fa9e4066Sahrens 			access |= ACE_WRITE_ACL;
341fa9e4066Sahrens 	} else {
342fa9e4066Sahrens 		if (! isowner)
343fa9e4066Sahrens 			access |= ACE_WRITE_ACL;
344fa9e4066Sahrens 	}
345fa9e4066Sahrens 
346fa9e4066Sahrens 	/* read */
347fa9e4066Sahrens 	if (mode & 04) {
348fa9e4066Sahrens 		access |= ACE_READ_DATA;
349fa9e4066Sahrens 	}
350fa9e4066Sahrens 	/* write */
351fa9e4066Sahrens 	if (mode & 02) {
352fa9e4066Sahrens 		access |= ACE_WRITE_DATA |
353fa9e4066Sahrens 		    ACE_APPEND_DATA;
354fa9e4066Sahrens 		if (isdir)
355fa9e4066Sahrens 			access |= ACE_DELETE_CHILD;
356fa9e4066Sahrens 	}
357fa9e4066Sahrens 	/* exec */
358fa9e4066Sahrens 	if (mode & 01) {
359fa9e4066Sahrens 		access |= ACE_EXECUTE;
360fa9e4066Sahrens 	}
361fa9e4066Sahrens 
362fa9e4066Sahrens 	return (access);
363fa9e4066Sahrens }
364fa9e4066Sahrens 
365fa9e4066Sahrens /*
366fa9e4066Sahrens  * Given an nfsace (presumably an ALLOW entry), make a
367fa9e4066Sahrens  * corresponding DENY entry at the address given.
368fa9e4066Sahrens  */
369fa9e4066Sahrens static void
370fa9e4066Sahrens ace_make_deny(ace_t *allow, ace_t *deny, int isdir, int isowner)
371fa9e4066Sahrens {
372fa9e4066Sahrens 	(void) memcpy(deny, allow, sizeof (ace_t));
373fa9e4066Sahrens 
374fa9e4066Sahrens 	deny->a_who = allow->a_who;
375fa9e4066Sahrens 
376fa9e4066Sahrens 	deny->a_type = ACE_ACCESS_DENIED_ACE_TYPE;
377fa9e4066Sahrens 	deny->a_access_mask ^= ACE_POSIX_SUPPORTED_BITS;
378fa9e4066Sahrens 	if (isdir)
379fa9e4066Sahrens 		deny->a_access_mask ^= ACE_DELETE_CHILD;
380fa9e4066Sahrens 
381fa9e4066Sahrens 	deny->a_access_mask &= ~(ACE_SYNCHRONIZE | ACE_WRITE_OWNER |
382fa9e4066Sahrens 	    ACE_DELETE | ACE_WRITE_ATTRIBUTES | ACE_READ_NAMED_ATTRS |
383fa9e4066Sahrens 	    ACE_WRITE_NAMED_ATTRS);
384fa9e4066Sahrens 	deny->a_access_mask |= access_mask_set((allow->a_access_mask &
385fa9e4066Sahrens 	    ACE_WRITE_DATA), (allow->a_access_mask & ACE_READ_DATA), isowner,
386fa9e4066Sahrens 	    B_FALSE);
387fa9e4066Sahrens }
388fa9e4066Sahrens /*
389fa9e4066Sahrens  * Make an initial pass over an array of aclent_t's.  Gather
390fa9e4066Sahrens  * information such as an ACL_MASK (if any), number of users,
391fa9e4066Sahrens  * number of groups, and whether the array needs to be sorted.
392fa9e4066Sahrens  */
393fa9e4066Sahrens static int
394fa9e4066Sahrens ln_aent_preprocess(aclent_t *aclent, int n,
395fa9e4066Sahrens     int *hasmask, mode_t *mask,
396fa9e4066Sahrens     int *numuser, int *numgroup, int *needsort)
397fa9e4066Sahrens {
398fa9e4066Sahrens 	int error = 0;
399fa9e4066Sahrens 	int i;
400fa9e4066Sahrens 	int curtype = 0;
401fa9e4066Sahrens 
402fa9e4066Sahrens 	*hasmask = 0;
403fa9e4066Sahrens 	*mask = 07;
404fa9e4066Sahrens 	*needsort = 0;
405fa9e4066Sahrens 	*numuser = 0;
406fa9e4066Sahrens 	*numgroup = 0;
407fa9e4066Sahrens 
408fa9e4066Sahrens 	for (i = 0; i < n; i++) {
409fa9e4066Sahrens 		if (aclent[i].a_type < curtype)
410fa9e4066Sahrens 			*needsort = 1;
411fa9e4066Sahrens 		else if (aclent[i].a_type > curtype)
412fa9e4066Sahrens 			curtype = aclent[i].a_type;
413fa9e4066Sahrens 		if (aclent[i].a_type & USER)
414fa9e4066Sahrens 			(*numuser)++;
415fa9e4066Sahrens 		if (aclent[i].a_type & (GROUP | GROUP_OBJ))
416fa9e4066Sahrens 			(*numgroup)++;
417fa9e4066Sahrens 		if (aclent[i].a_type & CLASS_OBJ) {
418fa9e4066Sahrens 			if (*hasmask) {
419fa9e4066Sahrens 				error = EINVAL;
420fa9e4066Sahrens 				goto out;
421fa9e4066Sahrens 			} else {
422fa9e4066Sahrens 				*hasmask = 1;
423fa9e4066Sahrens 				*mask = aclent[i].a_perm;
424fa9e4066Sahrens 			}
425fa9e4066Sahrens 		}
426fa9e4066Sahrens 	}
427fa9e4066Sahrens 
428fa9e4066Sahrens 	if ((! *hasmask) && (*numuser + *numgroup > 1)) {
429fa9e4066Sahrens 		error = EINVAL;
430fa9e4066Sahrens 		goto out;
431fa9e4066Sahrens 	}
432fa9e4066Sahrens 
433fa9e4066Sahrens out:
434fa9e4066Sahrens 	return (error);
435fa9e4066Sahrens }
436fa9e4066Sahrens 
437fa9e4066Sahrens /*
438fa9e4066Sahrens  * Convert an array of aclent_t into an array of nfsace entries,
439fa9e4066Sahrens  * following POSIX draft -> nfsv4 conversion semantics as outlined in
440fa9e4066Sahrens  * the IETF draft.
441fa9e4066Sahrens  */
442fa9e4066Sahrens static int
443fa9e4066Sahrens ln_aent_to_ace(aclent_t *aclent, int n, ace_t **acepp, int *rescount, int isdir)
444fa9e4066Sahrens {
445fa9e4066Sahrens 	int error = 0;
446fa9e4066Sahrens 	mode_t mask;
447fa9e4066Sahrens 	int numuser, numgroup, needsort;
448fa9e4066Sahrens 	int resultsize = 0;
449fa9e4066Sahrens 	int i, groupi = 0, skip;
450fa9e4066Sahrens 	ace_t *acep, *result = NULL;
451fa9e4066Sahrens 	int hasmask;
452fa9e4066Sahrens 
453fa9e4066Sahrens 	error = ln_aent_preprocess(aclent, n, &hasmask, &mask,
454fa9e4066Sahrens 	    &numuser, &numgroup, &needsort);
455fa9e4066Sahrens 	if (error != 0)
456fa9e4066Sahrens 		goto out;
457fa9e4066Sahrens 
458fa9e4066Sahrens 	/* allow + deny for each aclent */
459fa9e4066Sahrens 	resultsize = n * 2;
460fa9e4066Sahrens 	if (hasmask) {
461fa9e4066Sahrens 		/*
462fa9e4066Sahrens 		 * stick extra deny on the group_obj and on each
463fa9e4066Sahrens 		 * user|group for the mask (the group_obj was added
464fa9e4066Sahrens 		 * into the count for numgroup)
465fa9e4066Sahrens 		 */
466fa9e4066Sahrens 		resultsize += numuser + numgroup;
467fa9e4066Sahrens 		/* ... and don't count the mask itself */
468fa9e4066Sahrens 		resultsize -= 2;
469fa9e4066Sahrens 	}
470fa9e4066Sahrens 
471fa9e4066Sahrens 	/* sort the source if necessary */
472fa9e4066Sahrens 	if (needsort)
473fa9e4066Sahrens 		ksort((caddr_t)aclent, n, sizeof (aclent_t), cmp2acls);
474fa9e4066Sahrens 
475fa9e4066Sahrens 	result = acep = calloc(1, resultsize * sizeof (ace_t));
476fa9e4066Sahrens 	if (result == NULL)
477fa9e4066Sahrens 		goto out;
478fa9e4066Sahrens 
479fa9e4066Sahrens 	for (i = 0; i < n; i++) {
480fa9e4066Sahrens 		/*
481fa9e4066Sahrens 		 * don't process CLASS_OBJ (mask); mask was grabbed in
482fa9e4066Sahrens 		 * ln_aent_preprocess()
483fa9e4066Sahrens 		 */
484fa9e4066Sahrens 		if (aclent[i].a_type & CLASS_OBJ)
485fa9e4066Sahrens 			continue;
486fa9e4066Sahrens 
487fa9e4066Sahrens 		/* If we need an ACL_MASK emulator, prepend it now */
488fa9e4066Sahrens 		if ((hasmask) &&
489fa9e4066Sahrens 		    (aclent[i].a_type & (USER | GROUP | GROUP_OBJ))) {
490fa9e4066Sahrens 			acep->a_type = ACE_ACCESS_DENIED_ACE_TYPE;
491fa9e4066Sahrens 			acep->a_flags = 0;
492fa9e4066Sahrens 			if (aclent[i].a_type & GROUP_OBJ) {
493fa9e4066Sahrens 				acep->a_who = -1;
494fa9e4066Sahrens 				acep->a_flags |=
495fa9e4066Sahrens 				    (ACE_IDENTIFIER_GROUP|ACE_GROUP);
496fa9e4066Sahrens 			} else if (aclent[i].a_type & USER) {
497fa9e4066Sahrens 				acep->a_who = aclent[i].a_id;
498fa9e4066Sahrens 			} else {
499fa9e4066Sahrens 				acep->a_who = aclent[i].a_id;
500fa9e4066Sahrens 				acep->a_flags |= ACE_IDENTIFIER_GROUP;
501fa9e4066Sahrens 			}
502fa9e4066Sahrens 			if (aclent[i].a_type & ACL_DEFAULT) {
503fa9e4066Sahrens 				acep->a_flags |= ACE_INHERIT_ONLY_ACE |
504fa9e4066Sahrens 				    ACE_FILE_INHERIT_ACE |
505fa9e4066Sahrens 				    ACE_DIRECTORY_INHERIT_ACE;
506fa9e4066Sahrens 			}
507fa9e4066Sahrens 			/*
508fa9e4066Sahrens 			 * Set the access mask for the prepended deny
509fa9e4066Sahrens 			 * ace.  To do this, we invert the mask (found
510fa9e4066Sahrens 			 * in ln_aent_preprocess()) then convert it to an
511fa9e4066Sahrens 			 * DENY ace access_mask.
512fa9e4066Sahrens 			 */
513fa9e4066Sahrens 			acep->a_access_mask = mode_to_ace_access((mask ^ 07),
514fa9e4066Sahrens 			    isdir, 0, 0);
515fa9e4066Sahrens 			acep += 1;
516fa9e4066Sahrens 		}
517fa9e4066Sahrens 
518fa9e4066Sahrens 		/* handle a_perm -> access_mask */
519fa9e4066Sahrens 		acep->a_access_mask = mode_to_ace_access(aclent[i].a_perm,
520fa9e4066Sahrens 		    isdir, aclent[i].a_type & USER_OBJ, 1);
521fa9e4066Sahrens 
522fa9e4066Sahrens 		/* emulate a default aclent */
523fa9e4066Sahrens 		if (aclent[i].a_type & ACL_DEFAULT) {
524fa9e4066Sahrens 			acep->a_flags |= ACE_INHERIT_ONLY_ACE |
525fa9e4066Sahrens 			    ACE_FILE_INHERIT_ACE |
526fa9e4066Sahrens 			    ACE_DIRECTORY_INHERIT_ACE;
527fa9e4066Sahrens 		}
528fa9e4066Sahrens 
529fa9e4066Sahrens 		/*
530fa9e4066Sahrens 		 * handle a_perm and a_id
531fa9e4066Sahrens 		 *
532fa9e4066Sahrens 		 * this must be done last, since it involves the
533fa9e4066Sahrens 		 * corresponding deny aces, which are handled
534fa9e4066Sahrens 		 * differently for each different a_type.
535fa9e4066Sahrens 		 */
536fa9e4066Sahrens 		if (aclent[i].a_type & USER_OBJ) {
537fa9e4066Sahrens 			acep->a_who = -1;
538fa9e4066Sahrens 			acep->a_flags |= ACE_OWNER;
539fa9e4066Sahrens 			ace_make_deny(acep, acep + 1, isdir, B_TRUE);
540fa9e4066Sahrens 			acep += 2;
541fa9e4066Sahrens 		} else if (aclent[i].a_type & USER) {
542fa9e4066Sahrens 			acep->a_who = aclent[i].a_id;
543fa9e4066Sahrens 			ace_make_deny(acep, acep + 1, isdir, B_FALSE);
544fa9e4066Sahrens 			acep += 2;
545fa9e4066Sahrens 		} else if (aclent[i].a_type & (GROUP_OBJ | GROUP)) {
546fa9e4066Sahrens 			if (aclent[i].a_type & GROUP_OBJ) {
547fa9e4066Sahrens 				acep->a_who = -1;
548fa9e4066Sahrens 				acep->a_flags |= ACE_GROUP;
549fa9e4066Sahrens 			} else {
550fa9e4066Sahrens 				acep->a_who = aclent[i].a_id;
551fa9e4066Sahrens 			}
552fa9e4066Sahrens 			acep->a_flags |= ACE_IDENTIFIER_GROUP;
553fa9e4066Sahrens 			/*
554fa9e4066Sahrens 			 * Set the corresponding deny for the group ace.
555fa9e4066Sahrens 			 *
556fa9e4066Sahrens 			 * The deny aces go after all of the groups, unlike
557fa9e4066Sahrens 			 * everything else, where they immediately follow
558fa9e4066Sahrens 			 * the allow ace.
559fa9e4066Sahrens 			 *
560fa9e4066Sahrens 			 * We calculate "skip", the number of slots to
561fa9e4066Sahrens 			 * skip ahead for the deny ace, here.
562fa9e4066Sahrens 			 *
563fa9e4066Sahrens 			 * The pattern is:
564fa9e4066Sahrens 			 * MD1 A1 MD2 A2 MD3 A3 D1 D2 D3
565fa9e4066Sahrens 			 * thus, skip is
566fa9e4066Sahrens 			 * (2 * numgroup) - 1 - groupi
567fa9e4066Sahrens 			 * (2 * numgroup) to account for MD + A
568fa9e4066Sahrens 			 * - 1 to account for the fact that we're on the
569fa9e4066Sahrens 			 * access (A), not the mask (MD)
570fa9e4066Sahrens 			 * - groupi to account for the fact that we have
571fa9e4066Sahrens 			 * passed up groupi number of MD's.
572fa9e4066Sahrens 			 */
573fa9e4066Sahrens 			skip = (2 * numgroup) - 1 - groupi;
574fa9e4066Sahrens 			ace_make_deny(acep, acep + skip, isdir, B_FALSE);
575fa9e4066Sahrens 			/*
576fa9e4066Sahrens 			 * If we just did the last group, skip acep past
577fa9e4066Sahrens 			 * all of the denies; else, just move ahead one.
578fa9e4066Sahrens 			 */
579fa9e4066Sahrens 			if (++groupi >= numgroup)
580fa9e4066Sahrens 				acep += numgroup + 1;
581fa9e4066Sahrens 			else
582fa9e4066Sahrens 				acep += 1;
583fa9e4066Sahrens 		} else if (aclent[i].a_type & OTHER_OBJ) {
584fa9e4066Sahrens 			acep->a_who = -1;
585fa9e4066Sahrens 			acep->a_flags |= ACE_EVERYONE;
586fa9e4066Sahrens 			ace_make_deny(acep, acep + 1, isdir, B_FALSE);
587fa9e4066Sahrens 			acep += 2;
588fa9e4066Sahrens 		} else {
589fa9e4066Sahrens 			error = EINVAL;
590fa9e4066Sahrens 			goto out;
591fa9e4066Sahrens 		}
592fa9e4066Sahrens 	}
593fa9e4066Sahrens 
594fa9e4066Sahrens 	*acepp = result;
595fa9e4066Sahrens 	*rescount = resultsize;
596fa9e4066Sahrens 
597fa9e4066Sahrens out:
598fa9e4066Sahrens 	if (error != 0) {
599fa9e4066Sahrens 		if ((result != NULL) && (resultsize > 0)) {
600fa9e4066Sahrens 			free(result);
601fa9e4066Sahrens 		}
602fa9e4066Sahrens 	}
603fa9e4066Sahrens 
604fa9e4066Sahrens 	return (error);
605fa9e4066Sahrens }
606fa9e4066Sahrens 
607fa9e4066Sahrens static int
608fa9e4066Sahrens convert_aent_to_ace(aclent_t *aclentp, int aclcnt, int isdir,
609fa9e4066Sahrens     ace_t **retacep, int *retacecnt)
610fa9e4066Sahrens {
611fa9e4066Sahrens 	ace_t *acep;
612fa9e4066Sahrens 	ace_t *dfacep;
613fa9e4066Sahrens 	int acecnt = 0;
614fa9e4066Sahrens 	int dfacecnt = 0;
615fa9e4066Sahrens 	int dfaclstart = 0;
616fa9e4066Sahrens 	int dfaclcnt = 0;
617fa9e4066Sahrens 	aclent_t *aclp;
618fa9e4066Sahrens 	int i;
619fa9e4066Sahrens 	int error;
620fa9e4066Sahrens 
621fa9e4066Sahrens 	ksort((caddr_t)aclentp, aclcnt, sizeof (aclent_t), cmp2acls);
622fa9e4066Sahrens 
623fa9e4066Sahrens 	for (i = 0, aclp = aclentp; i < aclcnt; aclp++, i++) {
624fa9e4066Sahrens 		if (aclp->a_type & ACL_DEFAULT)
625fa9e4066Sahrens 			break;
626fa9e4066Sahrens 	}
627fa9e4066Sahrens 
628fa9e4066Sahrens 	if (i < aclcnt) {
6293eb3c573Smarks 		dfaclstart = i;
6303eb3c573Smarks 		dfaclcnt = aclcnt - i;
631fa9e4066Sahrens 	}
632fa9e4066Sahrens 
633fa9e4066Sahrens 	if (dfaclcnt && isdir == 0) {
634fa9e4066Sahrens 		return (-1);
635fa9e4066Sahrens 	}
636fa9e4066Sahrens 
637fa9e4066Sahrens 	error = ln_aent_to_ace(aclentp, i,  &acep, &acecnt, isdir);
638fa9e4066Sahrens 	if (error)
639fa9e4066Sahrens 		return (-1);
640fa9e4066Sahrens 
641fa9e4066Sahrens 	if (dfaclcnt) {
642fa9e4066Sahrens 		error = ln_aent_to_ace(&aclentp[dfaclstart], dfaclcnt,
643fa9e4066Sahrens 		    &dfacep, &dfacecnt, isdir);
644fa9e4066Sahrens 		if (error) {
645fa9e4066Sahrens 			if (acep) {
646fa9e4066Sahrens 				free(acep);
647fa9e4066Sahrens 			}
648fa9e4066Sahrens 			return (-1);
649fa9e4066Sahrens 		}
650fa9e4066Sahrens 	}
651fa9e4066Sahrens 
6523eb3c573Smarks 	if (dfacecnt != 0) {
6533eb3c573Smarks 		acep = realloc(acep, sizeof (ace_t) * (acecnt + dfacecnt));
6543eb3c573Smarks 		if (acep == NULL)
655fa9e4066Sahrens 			return (-1);
656fa9e4066Sahrens 		if (dfaclcnt) {
6573eb3c573Smarks 			(void) memcpy(acep + acecnt, dfacep,
658fa9e4066Sahrens 			    sizeof (ace_t) * dfacecnt);
659fa9e4066Sahrens 		}
6603eb3c573Smarks 	}
661fa9e4066Sahrens 	if (dfaclcnt)
662fa9e4066Sahrens 		free(dfacep);
663fa9e4066Sahrens 
664fa9e4066Sahrens 	*retacecnt = acecnt + dfacecnt;
6653eb3c573Smarks 	*retacep = acep;
666fa9e4066Sahrens 	return (0);
667fa9e4066Sahrens }
668fa9e4066Sahrens 
6693eb3c573Smarks static void
6703eb3c573Smarks acevals_init(acevals_t *vals, uid_t key)
6713eb3c573Smarks {
6723eb3c573Smarks 	bzero(vals, sizeof (*vals));
6733eb3c573Smarks 	vals->allowed = ACE_MASK_UNDEFINED;
6743eb3c573Smarks 	vals->denied = ACE_MASK_UNDEFINED;
6753eb3c573Smarks 	vals->mask = ACE_MASK_UNDEFINED;
6763eb3c573Smarks 	vals->key = key;
6773eb3c573Smarks }
6783eb3c573Smarks 
6793eb3c573Smarks static void
6803eb3c573Smarks ace_list_init(ace_list_t *al, int dfacl_flag)
6813eb3c573Smarks {
6823eb3c573Smarks 	acevals_init(&al->user_obj, NULL);
6833eb3c573Smarks 	acevals_init(&al->group_obj, NULL);
6843eb3c573Smarks 	acevals_init(&al->other_obj, NULL);
6853eb3c573Smarks 	al->numusers = 0;
6863eb3c573Smarks 	al->numgroups = 0;
6873eb3c573Smarks 	al->acl_mask = 0;
6883eb3c573Smarks 	al->hasmask = 0;
6893eb3c573Smarks 	al->state = ace_unused;
6903eb3c573Smarks 	al->seen = 0;
6913eb3c573Smarks 	al->dfacl_flag = dfacl_flag;
6923eb3c573Smarks }
6933eb3c573Smarks 
6943eb3c573Smarks /*
6953eb3c573Smarks  * Find or create an acevals holder for a given id and avl tree.
6963eb3c573Smarks  *
6973eb3c573Smarks  * Note that only one thread will ever touch these avl trees, so
6983eb3c573Smarks  * there is no need for locking.
6993eb3c573Smarks  */
7003eb3c573Smarks static acevals_t *
7013eb3c573Smarks acevals_find(ace_t *ace, avl_tree_t *avl, int *num)
7023eb3c573Smarks {
7033eb3c573Smarks 	acevals_t key, *rc;
7043eb3c573Smarks 	avl_index_t where;
7053eb3c573Smarks 
7063eb3c573Smarks 	key.key = ace->a_who;
7073eb3c573Smarks 	rc = avl_find(avl, &key, &where);
7083eb3c573Smarks 	if (rc != NULL)
7093eb3c573Smarks 		return (rc);
7103eb3c573Smarks 
7113eb3c573Smarks 	/* this memory is freed by ln_ace_to_aent()->ace_list_free() */
7123eb3c573Smarks 	rc = calloc(1, sizeof (acevals_t));
7133eb3c573Smarks 	if (rc == NULL)
7143eb3c573Smarks 		return (rc);
7153eb3c573Smarks 	acevals_init(rc, ace->a_who);
7163eb3c573Smarks 	avl_insert(avl, rc, where);
7173eb3c573Smarks 	(*num)++;
7183eb3c573Smarks 
7193eb3c573Smarks 	return (rc);
7203eb3c573Smarks }
721fa9e4066Sahrens 
722fa9e4066Sahrens static int
7233eb3c573Smarks access_mask_check(ace_t *acep, int mask_bit, int isowner)
7243eb3c573Smarks {
7253eb3c573Smarks 	int set_deny, err_deny;
7263eb3c573Smarks 	int set_allow, err_allow;
7273eb3c573Smarks 	int acl_consume;
7283eb3c573Smarks 	int haswriteperm, hasreadperm;
7293eb3c573Smarks 
7303eb3c573Smarks 	if (acep->a_type == ACE_ACCESS_DENIED_ACE_TYPE) {
7313eb3c573Smarks 		haswriteperm = (acep->a_access_mask & ACE_WRITE_DATA) ? 0 : 1;
7323eb3c573Smarks 		hasreadperm = (acep->a_access_mask & ACE_READ_DATA) ? 0 : 1;
7333eb3c573Smarks 	} else {
7343eb3c573Smarks 		haswriteperm = (acep->a_access_mask & ACE_WRITE_DATA) ? 1 : 0;
7353eb3c573Smarks 		hasreadperm = (acep->a_access_mask & ACE_READ_DATA) ? 1 : 0;
7363eb3c573Smarks 	}
7373eb3c573Smarks 
7383eb3c573Smarks 	acl_consume = (ACL_SYNCHRONIZE_ERR_DENY |
7393eb3c573Smarks 	    ACL_DELETE_ERR_DENY |
7403eb3c573Smarks 	    ACL_WRITE_OWNER_ERR_DENY |
7413eb3c573Smarks 	    ACL_WRITE_OWNER_ERR_ALLOW |
7423eb3c573Smarks 	    ACL_WRITE_ATTRS_OWNER_SET_ALLOW |
7433eb3c573Smarks 	    ACL_WRITE_ATTRS_OWNER_ERR_DENY |
7443eb3c573Smarks 	    ACL_WRITE_ATTRS_WRITER_SET_DENY |
7453eb3c573Smarks 	    ACL_WRITE_ATTRS_WRITER_ERR_ALLOW |
7463eb3c573Smarks 	    ACL_WRITE_NAMED_WRITER_ERR_DENY |
7473eb3c573Smarks 	    ACL_READ_NAMED_READER_ERR_DENY);
7483eb3c573Smarks 
7493eb3c573Smarks 	if (mask_bit == ACE_SYNCHRONIZE) {
7503eb3c573Smarks 		set_deny = ACL_SYNCHRONIZE_SET_DENY;
7513eb3c573Smarks 		err_deny =  ACL_SYNCHRONIZE_ERR_DENY;
7523eb3c573Smarks 		set_allow = ACL_SYNCHRONIZE_SET_ALLOW;
7533eb3c573Smarks 		err_allow = ACL_SYNCHRONIZE_ERR_ALLOW;
7543eb3c573Smarks 	} else if (mask_bit == ACE_WRITE_OWNER) {
7553eb3c573Smarks 		set_deny = ACL_WRITE_OWNER_SET_DENY;
7563eb3c573Smarks 		err_deny =  ACL_WRITE_OWNER_ERR_DENY;
7573eb3c573Smarks 		set_allow = ACL_WRITE_OWNER_SET_ALLOW;
7583eb3c573Smarks 		err_allow = ACL_WRITE_OWNER_ERR_ALLOW;
7593eb3c573Smarks 	} else if (mask_bit == ACE_DELETE) {
7603eb3c573Smarks 		set_deny = ACL_DELETE_SET_DENY;
7613eb3c573Smarks 		err_deny =  ACL_DELETE_ERR_DENY;
7623eb3c573Smarks 		set_allow = ACL_DELETE_SET_ALLOW;
7633eb3c573Smarks 		err_allow = ACL_DELETE_ERR_ALLOW;
7643eb3c573Smarks 	} else if (mask_bit == ACE_WRITE_ATTRIBUTES) {
7653eb3c573Smarks 		if (isowner) {
7663eb3c573Smarks 			set_deny = ACL_WRITE_ATTRS_OWNER_SET_DENY;
7673eb3c573Smarks 			err_deny =  ACL_WRITE_ATTRS_OWNER_ERR_DENY;
7683eb3c573Smarks 			set_allow = ACL_WRITE_ATTRS_OWNER_SET_ALLOW;
7693eb3c573Smarks 			err_allow = ACL_WRITE_ATTRS_OWNER_ERR_ALLOW;
7703eb3c573Smarks 		} else if (haswriteperm) {
7713eb3c573Smarks 			set_deny = ACL_WRITE_ATTRS_WRITER_SET_DENY;
7723eb3c573Smarks 			err_deny =  ACL_WRITE_ATTRS_WRITER_ERR_DENY;
7733eb3c573Smarks 			set_allow = ACL_WRITE_ATTRS_WRITER_SET_ALLOW;
7743eb3c573Smarks 			err_allow = ACL_WRITE_ATTRS_WRITER_ERR_ALLOW;
7753eb3c573Smarks 		} else {
7763eb3c573Smarks 			if ((acep->a_access_mask & mask_bit) &&
7773eb3c573Smarks 			    (acep->a_type & ACE_ACCESS_ALLOWED_ACE_TYPE)) {
7783eb3c573Smarks 				return (ENOTSUP);
7793eb3c573Smarks 			}
7803eb3c573Smarks 			return (0);
7813eb3c573Smarks 		}
7823eb3c573Smarks 	} else if (mask_bit == ACE_READ_NAMED_ATTRS) {
7833eb3c573Smarks 		if (!hasreadperm)
7843eb3c573Smarks 			return (0);
7853eb3c573Smarks 
7863eb3c573Smarks 		set_deny = ACL_READ_NAMED_READER_SET_DENY;
7873eb3c573Smarks 		err_deny = ACL_READ_NAMED_READER_ERR_DENY;
7883eb3c573Smarks 		set_allow = ACL_READ_NAMED_READER_SET_ALLOW;
7893eb3c573Smarks 		err_allow = ACL_READ_NAMED_READER_ERR_ALLOW;
7903eb3c573Smarks 	} else if (mask_bit == ACE_WRITE_NAMED_ATTRS) {
7913eb3c573Smarks 		if (!haswriteperm)
7923eb3c573Smarks 			return (0);
7933eb3c573Smarks 
7943eb3c573Smarks 		set_deny = ACL_WRITE_NAMED_WRITER_SET_DENY;
7953eb3c573Smarks 		err_deny = ACL_WRITE_NAMED_WRITER_ERR_DENY;
7963eb3c573Smarks 		set_allow = ACL_WRITE_NAMED_WRITER_SET_ALLOW;
7973eb3c573Smarks 		err_allow = ACL_WRITE_NAMED_WRITER_ERR_ALLOW;
7983eb3c573Smarks 	} else {
7993eb3c573Smarks 		return (EINVAL);
8003eb3c573Smarks 	}
8013eb3c573Smarks 
8023eb3c573Smarks 	if (acep->a_type == ACE_ACCESS_DENIED_ACE_TYPE) {
8033eb3c573Smarks 		if (acl_consume & set_deny) {
8043eb3c573Smarks 			if (!(acep->a_access_mask & mask_bit)) {
8053eb3c573Smarks 				return (ENOTSUP);
8063eb3c573Smarks 			}
8073eb3c573Smarks 		} else if (acl_consume & err_deny) {
8083eb3c573Smarks 			if (acep->a_access_mask & mask_bit) {
8093eb3c573Smarks 				return (ENOTSUP);
8103eb3c573Smarks 			}
8113eb3c573Smarks 		}
8123eb3c573Smarks 	} else {
8133eb3c573Smarks 		/* ACE_ACCESS_ALLOWED_ACE_TYPE */
8143eb3c573Smarks 		if (acl_consume & set_allow) {
8153eb3c573Smarks 			if (!(acep->a_access_mask & mask_bit)) {
8163eb3c573Smarks 				return (ENOTSUP);
8173eb3c573Smarks 			}
8183eb3c573Smarks 		} else if (acl_consume & err_allow) {
8193eb3c573Smarks 			if (acep->a_access_mask & mask_bit) {
8203eb3c573Smarks 				return (ENOTSUP);
8213eb3c573Smarks 			}
8223eb3c573Smarks 		}
8233eb3c573Smarks 	}
8243eb3c573Smarks 	return (0);
8253eb3c573Smarks }
8263eb3c573Smarks 
8273eb3c573Smarks static int
8283eb3c573Smarks ace_to_aent_legal(ace_t *acep)
8293eb3c573Smarks {
8303eb3c573Smarks 	int error = 0;
8313eb3c573Smarks 	int isowner;
8323eb3c573Smarks 
8333eb3c573Smarks 	/* only ALLOW or DENY */
8343eb3c573Smarks 	if ((acep->a_type != ACE_ACCESS_ALLOWED_ACE_TYPE) &&
8353eb3c573Smarks 	    (acep->a_type != ACE_ACCESS_DENIED_ACE_TYPE)) {
8363eb3c573Smarks 		error = ENOTSUP;
8373eb3c573Smarks 		goto out;
8383eb3c573Smarks 	}
8393eb3c573Smarks 
8403eb3c573Smarks 	/* check for invalid flags */
8413eb3c573Smarks 	if (acep->a_flags & ~(ACE_VALID_FLAG_BITS)) {
8423eb3c573Smarks 		error = EINVAL;
8433eb3c573Smarks 		goto out;
8443eb3c573Smarks 	}
8453eb3c573Smarks 
8463eb3c573Smarks 	/* some flags are illegal */
8473eb3c573Smarks 	if (acep->a_flags & (ACE_SUCCESSFUL_ACCESS_ACE_FLAG |
8483eb3c573Smarks 	    ACE_FAILED_ACCESS_ACE_FLAG |
8493eb3c573Smarks 	    ACE_NO_PROPAGATE_INHERIT_ACE)) {
8503eb3c573Smarks 		error = ENOTSUP;
8513eb3c573Smarks 		goto out;
8523eb3c573Smarks 	}
8533eb3c573Smarks 
8543eb3c573Smarks 	/* check for invalid masks */
8553eb3c573Smarks 	if (acep->a_access_mask & ~(ACE_VALID_MASK_BITS)) {
8563eb3c573Smarks 		error = EINVAL;
8573eb3c573Smarks 		goto out;
8583eb3c573Smarks 	}
8593eb3c573Smarks 
8603eb3c573Smarks 	if ((acep->a_flags & ACE_OWNER)) {
8613eb3c573Smarks 		isowner = 1;
8623eb3c573Smarks 	} else {
8633eb3c573Smarks 		isowner = 0;
8643eb3c573Smarks 	}
8653eb3c573Smarks 
8663eb3c573Smarks 	error = access_mask_check(acep, ACE_SYNCHRONIZE, isowner);
8673eb3c573Smarks 	if (error)
8683eb3c573Smarks 		goto out;
8693eb3c573Smarks 
8703eb3c573Smarks 	error = access_mask_check(acep, ACE_WRITE_OWNER, isowner);
8713eb3c573Smarks 	if (error)
8723eb3c573Smarks 		goto out;
8733eb3c573Smarks 
8743eb3c573Smarks 	error = access_mask_check(acep, ACE_DELETE, isowner);
8753eb3c573Smarks 	if (error)
8763eb3c573Smarks 		goto out;
8773eb3c573Smarks 
8783eb3c573Smarks 	error = access_mask_check(acep, ACE_WRITE_ATTRIBUTES, isowner);
8793eb3c573Smarks 	if (error)
8803eb3c573Smarks 		goto out;
8813eb3c573Smarks 
8823eb3c573Smarks 	error = access_mask_check(acep, ACE_READ_NAMED_ATTRS, isowner);
8833eb3c573Smarks 	if (error)
8843eb3c573Smarks 		goto out;
8853eb3c573Smarks 
8863eb3c573Smarks 	error = access_mask_check(acep, ACE_WRITE_NAMED_ATTRS, isowner);
8873eb3c573Smarks 	if (error)
8883eb3c573Smarks 		goto out;
8893eb3c573Smarks 
8903eb3c573Smarks 	/* more detailed checking of masks */
8913eb3c573Smarks 	if (acep->a_type == ACE_ACCESS_ALLOWED_ACE_TYPE) {
8923eb3c573Smarks 		if (! (acep->a_access_mask & ACE_READ_ATTRIBUTES)) {
8933eb3c573Smarks 			error = ENOTSUP;
8943eb3c573Smarks 			goto out;
8953eb3c573Smarks 		}
8963eb3c573Smarks 		if ((acep->a_access_mask & ACE_WRITE_DATA) &&
8973eb3c573Smarks 		    (! (acep->a_access_mask & ACE_APPEND_DATA))) {
8983eb3c573Smarks 			error = ENOTSUP;
8993eb3c573Smarks 			goto out;
9003eb3c573Smarks 		}
9013eb3c573Smarks 		if ((! (acep->a_access_mask & ACE_WRITE_DATA)) &&
9023eb3c573Smarks 		    (acep->a_access_mask & ACE_APPEND_DATA)) {
9033eb3c573Smarks 			error = ENOTSUP;
9043eb3c573Smarks 			goto out;
9053eb3c573Smarks 		}
9063eb3c573Smarks 	}
9073eb3c573Smarks 
9083eb3c573Smarks 	/* ACL enforcement */
9093eb3c573Smarks 	if ((acep->a_access_mask & ACE_READ_ACL) &&
9103eb3c573Smarks 	    (acep->a_type != ACE_ACCESS_ALLOWED_ACE_TYPE)) {
9113eb3c573Smarks 		error = ENOTSUP;
9123eb3c573Smarks 		goto out;
9133eb3c573Smarks 	}
9143eb3c573Smarks 	if (acep->a_access_mask & ACE_WRITE_ACL) {
9153eb3c573Smarks 		if ((acep->a_type == ACE_ACCESS_DENIED_ACE_TYPE) &&
9163eb3c573Smarks 		    (isowner)) {
9173eb3c573Smarks 			error = ENOTSUP;
9183eb3c573Smarks 			goto out;
9193eb3c573Smarks 		}
9203eb3c573Smarks 		if ((acep->a_type == ACE_ACCESS_ALLOWED_ACE_TYPE) &&
9213eb3c573Smarks 		    (! isowner)) {
9223eb3c573Smarks 			error = ENOTSUP;
9233eb3c573Smarks 			goto out;
9243eb3c573Smarks 		}
9253eb3c573Smarks 	}
9263eb3c573Smarks 
9273eb3c573Smarks out:
9283eb3c573Smarks 	return (error);
9293eb3c573Smarks }
9303eb3c573Smarks 
9313eb3c573Smarks static int
9323eb3c573Smarks ace_mask_to_mode(uint32_t  mask, o_mode_t *modep, int isdir)
9333eb3c573Smarks {
9343eb3c573Smarks 	int error = 0;
9353eb3c573Smarks 	o_mode_t mode = 0;
9363eb3c573Smarks 	uint32_t bits, wantbits;
9373eb3c573Smarks 
9383eb3c573Smarks 	/* read */
9393eb3c573Smarks 	if (mask & ACE_READ_DATA)
9403eb3c573Smarks 		mode |= 04;
9413eb3c573Smarks 
9423eb3c573Smarks 	/* write */
9433eb3c573Smarks 	wantbits = (ACE_WRITE_DATA | ACE_APPEND_DATA);
9443eb3c573Smarks 	if (isdir)
9453eb3c573Smarks 		wantbits |= ACE_DELETE_CHILD;
9463eb3c573Smarks 	bits = mask & wantbits;
9473eb3c573Smarks 	if (bits != 0) {
9483eb3c573Smarks 		if (bits != wantbits) {
9493eb3c573Smarks 			error = ENOTSUP;
9503eb3c573Smarks 			goto out;
9513eb3c573Smarks 		}
9523eb3c573Smarks 		mode |= 02;
9533eb3c573Smarks 	}
9543eb3c573Smarks 
9553eb3c573Smarks 	/* exec */
9563eb3c573Smarks 	if (mask & ACE_EXECUTE) {
9573eb3c573Smarks 		mode |= 01;
9583eb3c573Smarks 	}
9593eb3c573Smarks 
9603eb3c573Smarks 	*modep = mode;
9613eb3c573Smarks 
9623eb3c573Smarks out:
9633eb3c573Smarks 	return (error);
9643eb3c573Smarks }
9653eb3c573Smarks 
9663eb3c573Smarks static int
9673eb3c573Smarks ace_allow_to_mode(uint32_t mask, o_mode_t *modep, int isdir)
9683eb3c573Smarks {
9693eb3c573Smarks 	/* ACE_READ_ACL and ACE_READ_ATTRIBUTES must both be set */
9703eb3c573Smarks 	if ((mask & (ACE_READ_ACL | ACE_READ_ATTRIBUTES)) !=
9713eb3c573Smarks 	    (ACE_READ_ACL | ACE_READ_ATTRIBUTES)) {
9723eb3c573Smarks 		return (ENOTSUP);
9733eb3c573Smarks 	}
9743eb3c573Smarks 
9753eb3c573Smarks 	return (ace_mask_to_mode(mask, modep, isdir));
9763eb3c573Smarks }
9773eb3c573Smarks 
9783eb3c573Smarks static int
9793eb3c573Smarks acevals_to_aent(acevals_t *vals, aclent_t *dest, ace_list_t *list,
9803eb3c573Smarks     uid_t owner, gid_t group, int isdir)
9813eb3c573Smarks {
9823eb3c573Smarks 	int error;
9833eb3c573Smarks 	uint32_t  flips = ACE_POSIX_SUPPORTED_BITS;
9843eb3c573Smarks 
9853eb3c573Smarks 	if (isdir)
9863eb3c573Smarks 		flips |= ACE_DELETE_CHILD;
9873eb3c573Smarks 	if (vals->allowed != (vals->denied ^ flips)) {
9883eb3c573Smarks 		error = ENOTSUP;
9893eb3c573Smarks 		goto out;
9903eb3c573Smarks 	}
9913eb3c573Smarks 	if ((list->hasmask) && (list->acl_mask != vals->mask) &&
9923eb3c573Smarks 	    (vals->aent_type & (USER | GROUP | GROUP_OBJ))) {
9933eb3c573Smarks 		error = ENOTSUP;
9943eb3c573Smarks 		goto out;
9953eb3c573Smarks 	}
9963eb3c573Smarks 	error = ace_allow_to_mode(vals->allowed, &dest->a_perm, isdir);
9973eb3c573Smarks 	if (error != 0)
9983eb3c573Smarks 		goto out;
9993eb3c573Smarks 	dest->a_type = vals->aent_type;
10003eb3c573Smarks 	if (dest->a_type & (USER | GROUP)) {
10013eb3c573Smarks 		dest->a_id = vals->key;
10023eb3c573Smarks 	} else if (dest->a_type & USER_OBJ) {
10033eb3c573Smarks 		dest->a_id = owner;
10043eb3c573Smarks 	} else if (dest->a_type & GROUP_OBJ) {
10053eb3c573Smarks 		dest->a_id = group;
10063eb3c573Smarks 	} else if (dest->a_type & OTHER_OBJ) {
10073eb3c573Smarks 		dest->a_id = 0;
10083eb3c573Smarks 	} else {
10093eb3c573Smarks 		error = EINVAL;
10103eb3c573Smarks 		goto out;
10113eb3c573Smarks 	}
10123eb3c573Smarks 
10133eb3c573Smarks out:
10143eb3c573Smarks 	return (error);
10153eb3c573Smarks }
10163eb3c573Smarks 
10173eb3c573Smarks static int
10183eb3c573Smarks ace_list_to_aent(ace_list_t *list, aclent_t **aclentp, int *aclcnt,
10193eb3c573Smarks     uid_t owner, gid_t group, int isdir)
10203eb3c573Smarks {
10213eb3c573Smarks 	int error = 0;
10223eb3c573Smarks 	aclent_t *aent, *result = NULL;
10233eb3c573Smarks 	acevals_t *vals;
10243eb3c573Smarks 	int resultcount;
10253eb3c573Smarks 
10263eb3c573Smarks 	if ((list->seen & (USER_OBJ | GROUP_OBJ | OTHER_OBJ)) !=
10273eb3c573Smarks 	    (USER_OBJ | GROUP_OBJ | OTHER_OBJ)) {
10283eb3c573Smarks 		error = ENOTSUP;
10293eb3c573Smarks 		goto out;
10303eb3c573Smarks 	}
10313eb3c573Smarks 	if ((! list->hasmask) && (list->numusers + list->numgroups > 0)) {
10323eb3c573Smarks 		error = ENOTSUP;
10333eb3c573Smarks 		goto out;
10343eb3c573Smarks 	}
10353eb3c573Smarks 
10363eb3c573Smarks 	resultcount = 3 + list->numusers + list->numgroups;
10373eb3c573Smarks 	/*
10383eb3c573Smarks 	 * This must be the same condition as below, when we add the CLASS_OBJ
10393eb3c573Smarks 	 * (aka ACL mask)
10403eb3c573Smarks 	 */
10413eb3c573Smarks 	if ((list->hasmask) || (! list->dfacl_flag))
10423eb3c573Smarks 		resultcount += 1;
10433eb3c573Smarks 
10443eb3c573Smarks 	result = aent = calloc(1, resultcount * sizeof (aclent_t));
10453eb3c573Smarks 
10463eb3c573Smarks 	if (result == NULL) {
10473eb3c573Smarks 		error = ENOMEM;
10483eb3c573Smarks 		goto out;
10493eb3c573Smarks 	}
10503eb3c573Smarks 
10513eb3c573Smarks 	/* USER_OBJ */
10523eb3c573Smarks 	if (!(list->user_obj.aent_type & USER_OBJ)) {
10533eb3c573Smarks 		error = EINVAL;
10543eb3c573Smarks 		goto out;
10553eb3c573Smarks 	}
10563eb3c573Smarks 
10573eb3c573Smarks 	error = acevals_to_aent(&list->user_obj, aent, list, owner, group,
10583eb3c573Smarks 	    isdir);
10593eb3c573Smarks 
10603eb3c573Smarks 	if (error != 0)
10613eb3c573Smarks 		goto out;
10623eb3c573Smarks 	++aent;
10633eb3c573Smarks 	/* USER */
10643eb3c573Smarks 	vals = NULL;
10653eb3c573Smarks 	for (vals = avl_first(&list->user); vals != NULL;
10663eb3c573Smarks 	    vals = AVL_NEXT(&list->user, vals)) {
10673eb3c573Smarks 		if (!(vals->aent_type & USER)) {
10683eb3c573Smarks 			error = EINVAL;
10693eb3c573Smarks 			goto out;
10703eb3c573Smarks 		}
10713eb3c573Smarks 		error = acevals_to_aent(vals, aent, list, owner, group,
10723eb3c573Smarks 		    isdir);
10733eb3c573Smarks 		if (error != 0)
10743eb3c573Smarks 			goto out;
10753eb3c573Smarks 		++aent;
10763eb3c573Smarks 	}
10773eb3c573Smarks 	/* GROUP_OBJ */
10783eb3c573Smarks 	if (!(list->group_obj.aent_type & GROUP_OBJ)) {
10793eb3c573Smarks 		error = EINVAL;
10803eb3c573Smarks 		goto out;
10813eb3c573Smarks 	}
10823eb3c573Smarks 	error = acevals_to_aent(&list->group_obj, aent, list, owner, group,
10833eb3c573Smarks 	    isdir);
10843eb3c573Smarks 	if (error != 0)
10853eb3c573Smarks 		goto out;
10863eb3c573Smarks 	++aent;
10873eb3c573Smarks 	/* GROUP */
10883eb3c573Smarks 	vals = NULL;
10893eb3c573Smarks 	for (vals = avl_first(&list->group); vals != NULL;
10903eb3c573Smarks 	    vals = AVL_NEXT(&list->group, vals)) {
10913eb3c573Smarks 		if (!(vals->aent_type & GROUP)) {
10923eb3c573Smarks 			error = EINVAL;
10933eb3c573Smarks 			goto out;
10943eb3c573Smarks 		}
10953eb3c573Smarks 		error = acevals_to_aent(vals, aent, list, owner, group,
10963eb3c573Smarks 		    isdir);
10973eb3c573Smarks 		if (error != 0)
10983eb3c573Smarks 			goto out;
10993eb3c573Smarks 		++aent;
11003eb3c573Smarks 	}
11013eb3c573Smarks 	/*
11023eb3c573Smarks 	 * CLASS_OBJ (aka ACL_MASK)
11033eb3c573Smarks 	 *
11043eb3c573Smarks 	 * An ACL_MASK is not fabricated if the ACL is a default ACL.
11053eb3c573Smarks 	 * This is to follow UFS's behavior.
11063eb3c573Smarks 	 */
11073eb3c573Smarks 	if ((list->hasmask) || (! list->dfacl_flag)) {
11083eb3c573Smarks 		if (list->hasmask) {
11093eb3c573Smarks 			uint32_t flips = ACE_POSIX_SUPPORTED_BITS;
11103eb3c573Smarks 			if (isdir)
11113eb3c573Smarks 				flips |= ACE_DELETE_CHILD;
11123eb3c573Smarks 			error = ace_mask_to_mode(list->acl_mask ^ flips,
11133eb3c573Smarks 			    &aent->a_perm, isdir);
11143eb3c573Smarks 			if (error != 0)
11153eb3c573Smarks 				goto out;
11163eb3c573Smarks 		} else {
11173eb3c573Smarks 			/* fabricate the ACL_MASK from the group permissions */
11183eb3c573Smarks 			error = ace_mask_to_mode(list->group_obj.allowed,
11193eb3c573Smarks 			    &aent->a_perm, isdir);
11203eb3c573Smarks 			if (error != 0)
11213eb3c573Smarks 				goto out;
11223eb3c573Smarks 		}
11233eb3c573Smarks 		aent->a_id = 0;
11243eb3c573Smarks 		aent->a_type = CLASS_OBJ | list->dfacl_flag;
11253eb3c573Smarks 		++aent;
11263eb3c573Smarks 	}
11273eb3c573Smarks 	/* OTHER_OBJ */
11283eb3c573Smarks 	if (!(list->other_obj.aent_type & OTHER_OBJ)) {
11293eb3c573Smarks 		error = EINVAL;
11303eb3c573Smarks 		goto out;
11313eb3c573Smarks 	}
11323eb3c573Smarks 	error = acevals_to_aent(&list->other_obj, aent, list, owner, group,
11333eb3c573Smarks 	    isdir);
11343eb3c573Smarks 	if (error != 0)
11353eb3c573Smarks 		goto out;
11363eb3c573Smarks 	++aent;
11373eb3c573Smarks 
11383eb3c573Smarks 	*aclentp = result;
11393eb3c573Smarks 	*aclcnt = resultcount;
11403eb3c573Smarks 
11413eb3c573Smarks out:
11423eb3c573Smarks 	if (error != 0) {
11433eb3c573Smarks 		if (result != NULL)
11443eb3c573Smarks 			free(result);
11453eb3c573Smarks 	}
11463eb3c573Smarks 
11473eb3c573Smarks 	return (error);
11483eb3c573Smarks }
11493eb3c573Smarks 
11503eb3c573Smarks /*
11513eb3c573Smarks  * free all data associated with an ace_list
11523eb3c573Smarks  */
11533eb3c573Smarks static void
11543eb3c573Smarks ace_list_free(ace_list_t *al)
11553eb3c573Smarks {
11563eb3c573Smarks 	acevals_t *node;
11573eb3c573Smarks 	void *cookie;
11583eb3c573Smarks 
11593eb3c573Smarks 	if (al == NULL)
11603eb3c573Smarks 		return;
11613eb3c573Smarks 
11623eb3c573Smarks 	cookie = NULL;
11633eb3c573Smarks 	while ((node = avl_destroy_nodes(&al->user, &cookie)) != NULL)
11643eb3c573Smarks 		free(node);
11653eb3c573Smarks 	cookie = NULL;
11663eb3c573Smarks 	while ((node = avl_destroy_nodes(&al->group, &cookie)) != NULL)
11673eb3c573Smarks 		free(node);
11683eb3c573Smarks 
11693eb3c573Smarks 	avl_destroy(&al->user);
11703eb3c573Smarks 	avl_destroy(&al->group);
11713eb3c573Smarks 
11723eb3c573Smarks 	/* free the container itself */
11733eb3c573Smarks 	free(al);
11743eb3c573Smarks }
11753eb3c573Smarks 
11763eb3c573Smarks static int
11773eb3c573Smarks acevals_compare(const void *va, const void *vb)
11783eb3c573Smarks {
11793eb3c573Smarks 	const acevals_t *a = va, *b = vb;
11803eb3c573Smarks 
11813eb3c573Smarks 	if (a->key == b->key)
11823eb3c573Smarks 		return (0);
11833eb3c573Smarks 
11843eb3c573Smarks 	if (a->key > b->key)
11853eb3c573Smarks 		return (1);
11863eb3c573Smarks 
11873eb3c573Smarks 	else
11883eb3c573Smarks 		return (-1);
11893eb3c573Smarks }
11903eb3c573Smarks 
11913eb3c573Smarks /*
11923eb3c573Smarks  * Convert a list of ace_t entries to equivalent regular and default
11933eb3c573Smarks  * aclent_t lists.  Return error (ENOTSUP) when conversion is not possible.
11943eb3c573Smarks  */
11953eb3c573Smarks static int
11963eb3c573Smarks ln_ace_to_aent(ace_t *ace, int n, uid_t owner, gid_t group,
11973eb3c573Smarks     aclent_t **aclentp, int *aclcnt, aclent_t **dfaclentp, int *dfaclcnt,
11983eb3c573Smarks     int isdir)
11993eb3c573Smarks {
12003eb3c573Smarks 	int error = 0;
12013eb3c573Smarks 	ace_t *acep;
12023eb3c573Smarks 	uint32_t bits;
12033eb3c573Smarks 	int i;
12043eb3c573Smarks 	ace_list_t *normacl = NULL, *dfacl = NULL, *acl;
12053eb3c573Smarks 	acevals_t *vals;
12063eb3c573Smarks 
12073eb3c573Smarks 	*aclentp = NULL;
12083eb3c573Smarks 	*aclcnt = 0;
12093eb3c573Smarks 	*dfaclentp = NULL;
12103eb3c573Smarks 	*dfaclcnt = 0;
12113eb3c573Smarks 
12123eb3c573Smarks 	/* we need at least user_obj, group_obj, and other_obj */
12133eb3c573Smarks 	if (n < 6) {
12143eb3c573Smarks 		error = ENOTSUP;
12153eb3c573Smarks 		goto out;
12163eb3c573Smarks 	}
12173eb3c573Smarks 	if (ace == NULL) {
12183eb3c573Smarks 		error = EINVAL;
12193eb3c573Smarks 		goto out;
12203eb3c573Smarks 	}
12213eb3c573Smarks 
12223eb3c573Smarks 	normacl = calloc(1, sizeof (ace_list_t));
12233eb3c573Smarks 
12243eb3c573Smarks 	if (normacl == NULL) {
12253eb3c573Smarks 		error = errno;
12263eb3c573Smarks 		goto out;
12273eb3c573Smarks 	}
12283eb3c573Smarks 
12293eb3c573Smarks 	avl_create(&normacl->user, acevals_compare, sizeof (acevals_t),
12303eb3c573Smarks 	    offsetof(acevals_t, avl));
12313eb3c573Smarks 	avl_create(&normacl->group, acevals_compare, sizeof (acevals_t),
12323eb3c573Smarks 	    offsetof(acevals_t, avl));
12333eb3c573Smarks 
12343eb3c573Smarks 	ace_list_init(normacl, 0);
12353eb3c573Smarks 
12363eb3c573Smarks 	dfacl = calloc(1, sizeof (ace_list_t));
12373eb3c573Smarks 	if (dfacl == NULL) {
12383eb3c573Smarks 		error = errno;
12393eb3c573Smarks 		goto out;
12403eb3c573Smarks 	}
12413eb3c573Smarks 	avl_create(&dfacl->user, acevals_compare, sizeof (acevals_t),
12423eb3c573Smarks 	    offsetof(acevals_t, avl));
12433eb3c573Smarks 	avl_create(&dfacl->group, acevals_compare, sizeof (acevals_t),
12443eb3c573Smarks 	    offsetof(acevals_t, avl));
12453eb3c573Smarks 	ace_list_init(dfacl, ACL_DEFAULT);
12463eb3c573Smarks 
12473eb3c573Smarks 	/* process every ace_t... */
12483eb3c573Smarks 	for (i = 0; i < n; i++) {
12493eb3c573Smarks 		acep = &ace[i];
12503eb3c573Smarks 
12513eb3c573Smarks 		/* rule out certain cases quickly */
12523eb3c573Smarks 		error = ace_to_aent_legal(acep);
12533eb3c573Smarks 		if (error != 0)
12543eb3c573Smarks 			goto out;
12553eb3c573Smarks 
12563eb3c573Smarks 		/*
12573eb3c573Smarks 		 * Turn off these bits in order to not have to worry about
12583eb3c573Smarks 		 * them when doing the checks for compliments.
12593eb3c573Smarks 		 */
12603eb3c573Smarks 		acep->a_access_mask &= ~(ACE_WRITE_OWNER | ACE_DELETE |
12613eb3c573Smarks 		    ACE_SYNCHRONIZE | ACE_WRITE_ATTRIBUTES |
12623eb3c573Smarks 		    ACE_READ_NAMED_ATTRS | ACE_WRITE_NAMED_ATTRS);
12633eb3c573Smarks 
12643eb3c573Smarks 		/* see if this should be a regular or default acl */
12653eb3c573Smarks 		bits = acep->a_flags &
12663eb3c573Smarks 		    (ACE_INHERIT_ONLY_ACE |
12673eb3c573Smarks 		    ACE_FILE_INHERIT_ACE |
12683eb3c573Smarks 		    ACE_DIRECTORY_INHERIT_ACE);
12693eb3c573Smarks 		if (bits != 0) {
12703eb3c573Smarks 			/* all or nothing on these inherit bits */
12713eb3c573Smarks 			if (bits != (ACE_INHERIT_ONLY_ACE |
12723eb3c573Smarks 			    ACE_FILE_INHERIT_ACE |
12733eb3c573Smarks 			    ACE_DIRECTORY_INHERIT_ACE)) {
12743eb3c573Smarks 				error = ENOTSUP;
12753eb3c573Smarks 				goto out;
12763eb3c573Smarks 			}
12773eb3c573Smarks 			acl = dfacl;
12783eb3c573Smarks 		} else {
12793eb3c573Smarks 			acl = normacl;
12803eb3c573Smarks 		}
12813eb3c573Smarks 
12823eb3c573Smarks 		if ((acep->a_flags & ACE_OWNER)) {
12833eb3c573Smarks 			if (acl->state > ace_user_obj) {
12843eb3c573Smarks 				error = ENOTSUP;
12853eb3c573Smarks 				goto out;
12863eb3c573Smarks 			}
12873eb3c573Smarks 			acl->state = ace_user_obj;
12883eb3c573Smarks 			acl->seen |= USER_OBJ;
12893eb3c573Smarks 			vals = &acl->user_obj;
12903eb3c573Smarks 			vals->aent_type = USER_OBJ | acl->dfacl_flag;
12913eb3c573Smarks 		} else if ((acep->a_flags & ACE_EVERYONE)) {
12923eb3c573Smarks 			acl->state = ace_other_obj;
12933eb3c573Smarks 			acl->seen |= OTHER_OBJ;
12943eb3c573Smarks 			vals = &acl->other_obj;
12953eb3c573Smarks 			vals->aent_type = OTHER_OBJ | acl->dfacl_flag;
12963eb3c573Smarks 		} else if (acep->a_flags & ACE_IDENTIFIER_GROUP) {
12973eb3c573Smarks 			if (acl->state > ace_group) {
12983eb3c573Smarks 				error = ENOTSUP;
12993eb3c573Smarks 				goto out;
13003eb3c573Smarks 			}
13013eb3c573Smarks 			if ((acep->a_flags & ACE_GROUP)) {
13023eb3c573Smarks 				acl->seen |= GROUP_OBJ;
13033eb3c573Smarks 				vals = &acl->group_obj;
13043eb3c573Smarks 				vals->aent_type = GROUP_OBJ | acl->dfacl_flag;
13053eb3c573Smarks 			} else {
13063eb3c573Smarks 				acl->seen |= GROUP;
13073eb3c573Smarks 				vals = acevals_find(acep, &acl->group,
13083eb3c573Smarks 				    &acl->numgroups);
13093eb3c573Smarks 				if (vals == NULL) {
13103eb3c573Smarks 					error = ENOMEM;
13113eb3c573Smarks 					goto out;
13123eb3c573Smarks 				}
13133eb3c573Smarks 				vals->aent_type = GROUP | acl->dfacl_flag;
13143eb3c573Smarks 			}
13153eb3c573Smarks 			acl->state = ace_group;
13163eb3c573Smarks 		} else {
13173eb3c573Smarks 			if (acl->state > ace_user) {
13183eb3c573Smarks 				error = ENOTSUP;
13193eb3c573Smarks 				goto out;
13203eb3c573Smarks 			}
13213eb3c573Smarks 			acl->state = ace_user;
13223eb3c573Smarks 			acl->seen |= USER;
13233eb3c573Smarks 			vals = acevals_find(acep, &acl->user,
13243eb3c573Smarks 			    &acl->numusers);
13253eb3c573Smarks 			if (vals == NULL) {
13263eb3c573Smarks 				error = ENOMEM;
13273eb3c573Smarks 				goto out;
13283eb3c573Smarks 			}
13293eb3c573Smarks 			vals->aent_type = USER | acl->dfacl_flag;
13303eb3c573Smarks 		}
13313eb3c573Smarks 
13323eb3c573Smarks 		if (!(acl->state > ace_unused)) {
13333eb3c573Smarks 			error = EINVAL;
13343eb3c573Smarks 			goto out;
13353eb3c573Smarks 		}
13363eb3c573Smarks 
13373eb3c573Smarks 		if (acep->a_type == ACE_ACCESS_ALLOWED_ACE_TYPE) {
13383eb3c573Smarks 			/* no more than one allowed per aclent_t */
13393eb3c573Smarks 			if (vals->allowed != ACE_MASK_UNDEFINED) {
13403eb3c573Smarks 				error = ENOTSUP;
13413eb3c573Smarks 				goto out;
13423eb3c573Smarks 			}
13433eb3c573Smarks 			vals->allowed = acep->a_access_mask;
13443eb3c573Smarks 		} else {
13453eb3c573Smarks 			/*
13463eb3c573Smarks 			 * it's a DENY; if there was a previous DENY, it
13473eb3c573Smarks 			 * must have been an ACL_MASK.
13483eb3c573Smarks 			 */
13493eb3c573Smarks 			if (vals->denied != ACE_MASK_UNDEFINED) {
13503eb3c573Smarks 				/* ACL_MASK is for USER and GROUP only */
13513eb3c573Smarks 				if ((acl->state != ace_user) &&
13523eb3c573Smarks 				    (acl->state != ace_group)) {
13533eb3c573Smarks 					error = ENOTSUP;
13543eb3c573Smarks 					goto out;
13553eb3c573Smarks 				}
13563eb3c573Smarks 
13573eb3c573Smarks 				if (! acl->hasmask) {
13583eb3c573Smarks 					acl->hasmask = 1;
13593eb3c573Smarks 					acl->acl_mask = vals->denied;
13603eb3c573Smarks 				/* check for mismatched ACL_MASK emulations */
13613eb3c573Smarks 				} else if (acl->acl_mask != vals->denied) {
13623eb3c573Smarks 					error = ENOTSUP;
13633eb3c573Smarks 					goto out;
13643eb3c573Smarks 				}
13653eb3c573Smarks 				vals->mask = vals->denied;
13663eb3c573Smarks 			}
13673eb3c573Smarks 			vals->denied = acep->a_access_mask;
13683eb3c573Smarks 		}
13693eb3c573Smarks 	}
13703eb3c573Smarks 
13713eb3c573Smarks 	/* done collating; produce the aclent_t lists */
13723eb3c573Smarks 	if (normacl->state != ace_unused) {
13733eb3c573Smarks 		error = ace_list_to_aent(normacl, aclentp, aclcnt,
13743eb3c573Smarks 		    owner, group, isdir);
13753eb3c573Smarks 		if (error != 0) {
13763eb3c573Smarks 			goto out;
13773eb3c573Smarks 		}
13783eb3c573Smarks 	}
13793eb3c573Smarks 	if (dfacl->state != ace_unused) {
13803eb3c573Smarks 		error = ace_list_to_aent(dfacl, dfaclentp, dfaclcnt,
13813eb3c573Smarks 		    owner, group, isdir);
13823eb3c573Smarks 		if (error != 0) {
13833eb3c573Smarks 			goto out;
13843eb3c573Smarks 		}
13853eb3c573Smarks 	}
13863eb3c573Smarks 
13873eb3c573Smarks out:
13883eb3c573Smarks 	if (normacl != NULL)
13893eb3c573Smarks 		ace_list_free(normacl);
13903eb3c573Smarks 	if (dfacl != NULL)
13913eb3c573Smarks 		ace_list_free(dfacl);
13923eb3c573Smarks 
13933eb3c573Smarks 	return (error);
13943eb3c573Smarks }
13953eb3c573Smarks 
13963eb3c573Smarks static int
13973eb3c573Smarks convert_ace_to_aent(ace_t *acebufp, int acecnt, int isdir,
13983eb3c573Smarks     uid_t owner, gid_t group, aclent_t **retaclentp, int *retaclcnt)
13993eb3c573Smarks {
14003eb3c573Smarks 	int error;
14013eb3c573Smarks 	aclent_t *aclentp, *dfaclentp;
14023eb3c573Smarks 	int aclcnt, dfaclcnt;
14033eb3c573Smarks 
14043eb3c573Smarks 	error = ln_ace_to_aent(acebufp, acecnt, owner, group,
14053eb3c573Smarks 	    &aclentp, &aclcnt, &dfaclentp, &dfaclcnt, isdir);
14063eb3c573Smarks 
14073eb3c573Smarks 	if (error)
14083eb3c573Smarks 		return (error);
14093eb3c573Smarks 
14103eb3c573Smarks 
14113eb3c573Smarks 	if (dfaclcnt != 0) {
14123eb3c573Smarks 		/*
14133eb3c573Smarks 		 * Slap aclentp and dfaclentp into a single array.
14143eb3c573Smarks 		 */
14153eb3c573Smarks 		aclentp = realloc(aclentp, (sizeof (aclent_t) * aclcnt) +
14163eb3c573Smarks 		    (sizeof (aclent_t) * dfaclcnt));
14173eb3c573Smarks 		if (aclentp != NULL) {
14183eb3c573Smarks 			(void) memcpy(aclentp + aclcnt,
14193eb3c573Smarks 			    dfaclentp, sizeof (aclent_t) * dfaclcnt);
14203eb3c573Smarks 		} else {
14213eb3c573Smarks 			error = -1;
14223eb3c573Smarks 		}
14233eb3c573Smarks 	}
14243eb3c573Smarks 
14253eb3c573Smarks 	if (aclentp) {
14263eb3c573Smarks 		*retaclentp = aclentp;
14273eb3c573Smarks 		*retaclcnt = aclcnt + dfaclcnt;
14283eb3c573Smarks 	}
14293eb3c573Smarks 
14303eb3c573Smarks 	if (dfaclentp)
14313eb3c573Smarks 		free(dfaclentp);
14323eb3c573Smarks 
14333eb3c573Smarks 	return (error);
14343eb3c573Smarks }
14353eb3c573Smarks 
1436*0157963dSmarks static int
1437fa9e4066Sahrens cacl_get(acl_inp inp, int get_flag, int type, acl_t **aclp)
1438fa9e4066Sahrens {
1439fa9e4066Sahrens 	const char *fname;
1440fa9e4066Sahrens 	int fd;
1441fa9e4066Sahrens 	int ace_acl = 0;
1442fa9e4066Sahrens 	int error;
1443fa9e4066Sahrens 	int getcmd, cntcmd;
1444fa9e4066Sahrens 	acl_t *acl_info;
1445fa9e4066Sahrens 	int	save_errno;
1446fa9e4066Sahrens 	int	stat_error;
1447fa9e4066Sahrens 	struct stat64 statbuf;
1448fa9e4066Sahrens 
1449fa9e4066Sahrens 	*aclp = NULL;
1450fa9e4066Sahrens 	if (type == ACL_PATH) {
1451fa9e4066Sahrens 		fname = inp.file;
1452fa9e4066Sahrens 		ace_acl = pathconf(fname, _PC_ACL_ENABLED);
1453fa9e4066Sahrens 	} else {
1454fa9e4066Sahrens 		fd = inp.fd;
1455fa9e4066Sahrens 		ace_acl = fpathconf(fd, _PC_ACL_ENABLED);
1456fa9e4066Sahrens 	}
1457fa9e4066Sahrens 
1458fa9e4066Sahrens 	if (ace_acl == -1)
1459fa9e4066Sahrens 		return (-1);
1460fa9e4066Sahrens 
1461fa9e4066Sahrens 	/*
1462fa9e4066Sahrens 	 * if acl's aren't supported then
1463fa9e4066Sahrens 	 * send it through the old GETACL interface
1464fa9e4066Sahrens 	 */
1465fa9e4066Sahrens 	if (ace_acl == 0) {
1466fa9e4066Sahrens 		ace_acl = _ACL_ACLENT_ENABLED;
1467fa9e4066Sahrens 	}
1468fa9e4066Sahrens 
1469fa9e4066Sahrens 	if (ace_acl & _ACL_ACE_ENABLED) {
1470fa9e4066Sahrens 		cntcmd = ACE_GETACLCNT;
1471fa9e4066Sahrens 		getcmd = ACE_GETACL;
1472fa9e4066Sahrens 		acl_info = acl_alloc(ACE_T);
1473fa9e4066Sahrens 	} else {
1474fa9e4066Sahrens 		cntcmd = GETACLCNT;
1475fa9e4066Sahrens 		getcmd = GETACL;
1476fa9e4066Sahrens 		acl_info = acl_alloc(ACLENT_T);
1477fa9e4066Sahrens 	}
1478fa9e4066Sahrens 
1479fa9e4066Sahrens 	if (acl_info == NULL)
1480fa9e4066Sahrens 		return (-1);
1481fa9e4066Sahrens 
1482fa9e4066Sahrens 	if (type == ACL_PATH) {
1483fa9e4066Sahrens 		acl_info->acl_cnt = acl(fname, cntcmd, 0, NULL);
1484fa9e4066Sahrens 	} else {
1485fa9e4066Sahrens 		acl_info->acl_cnt = facl(fd, cntcmd, 0, NULL);
1486fa9e4066Sahrens 	}
1487fa9e4066Sahrens 
1488fa9e4066Sahrens 	save_errno = errno;
1489fa9e4066Sahrens 	if (acl_info->acl_cnt < 0) {
1490fa9e4066Sahrens 		acl_free(acl_info);
1491fa9e4066Sahrens 		errno = save_errno;
1492fa9e4066Sahrens 		return (-1);
1493fa9e4066Sahrens 	}
1494fa9e4066Sahrens 
1495fa9e4066Sahrens 	if (acl_info->acl_cnt == 0) {
1496fa9e4066Sahrens 		acl_free(acl_info);
1497fa9e4066Sahrens 		errno = save_errno;
1498fa9e4066Sahrens 		return (0);
1499fa9e4066Sahrens 	}
1500fa9e4066Sahrens 
1501fa9e4066Sahrens 	acl_info->acl_aclp =
1502fa9e4066Sahrens 	    malloc(acl_info->acl_cnt * acl_info->acl_entry_size);
1503fa9e4066Sahrens 	save_errno = errno;
1504fa9e4066Sahrens 
1505fa9e4066Sahrens 	if (acl_info->acl_aclp == NULL) {
1506fa9e4066Sahrens 		acl_free(acl_info);
1507fa9e4066Sahrens 		errno = save_errno;
1508fa9e4066Sahrens 		return (-1);
1509fa9e4066Sahrens 	}
1510fa9e4066Sahrens 
1511fa9e4066Sahrens 	if (type == ACL_PATH) {
1512fa9e4066Sahrens 		stat_error = stat64(fname, &statbuf);
1513fa9e4066Sahrens 		error = acl(fname, getcmd, acl_info->acl_cnt,
1514fa9e4066Sahrens 		    acl_info->acl_aclp);
1515fa9e4066Sahrens 	} else {
1516fa9e4066Sahrens 		stat_error = fstat64(fd, &statbuf);
1517fa9e4066Sahrens 		error = facl(fd, getcmd, acl_info->acl_cnt,
1518fa9e4066Sahrens 		    acl_info->acl_aclp);
1519fa9e4066Sahrens 	}
1520fa9e4066Sahrens 
1521fa9e4066Sahrens 	save_errno = errno;
1522fa9e4066Sahrens 	if (error == -1) {
1523fa9e4066Sahrens 		acl_free(acl_info);
1524fa9e4066Sahrens 		errno = save_errno;
1525fa9e4066Sahrens 		return (-1);
1526fa9e4066Sahrens 	}
1527fa9e4066Sahrens 
1528fa9e4066Sahrens 
1529fa9e4066Sahrens 	if (stat_error == 0) {
1530fa9e4066Sahrens 		acl_info->acl_flags =
1531fa9e4066Sahrens 		    (S_ISDIR(statbuf.st_mode) ? ACL_IS_DIR : 0);
1532fa9e4066Sahrens 	} else
1533fa9e4066Sahrens 		acl_info->acl_flags = 0;
1534fa9e4066Sahrens 
1535fa9e4066Sahrens 	switch (acl_info->acl_type) {
1536fa9e4066Sahrens 	case ACLENT_T:
1537fa9e4066Sahrens 		if (acl_info->acl_cnt <= MIN_ACL_ENTRIES)
1538fa9e4066Sahrens 			acl_info->acl_flags |= ACL_IS_TRIVIAL;
1539fa9e4066Sahrens 		break;
1540fa9e4066Sahrens 	case ACE_T:
1541fa9e4066Sahrens 		if (ace_trivial(acl_info->acl_aclp, acl_info->acl_cnt) == 0)
1542fa9e4066Sahrens 			acl_info->acl_flags |= ACL_IS_TRIVIAL;
1543fa9e4066Sahrens 		break;
1544fa9e4066Sahrens 	default:
1545fa9e4066Sahrens 		errno = EINVAL;
1546fa9e4066Sahrens 		acl_free(acl_info);
1547fa9e4066Sahrens 		return (-1);
1548fa9e4066Sahrens 	}
1549fa9e4066Sahrens 
1550fa9e4066Sahrens 	if ((acl_info->acl_flags & ACL_IS_TRIVIAL) &&
1551fa9e4066Sahrens 	    (get_flag & ACL_NO_TRIVIAL)) {
1552fa9e4066Sahrens 		acl_free(acl_info);
1553fa9e4066Sahrens 		errno = 0;
1554fa9e4066Sahrens 		return (0);
1555fa9e4066Sahrens 	}
1556fa9e4066Sahrens 
1557fa9e4066Sahrens 	*aclp = acl_info;
1558fa9e4066Sahrens 	return (0);
1559fa9e4066Sahrens }
1560fa9e4066Sahrens 
1561fa9e4066Sahrens /*
1562fa9e4066Sahrens  * return -1 on failure, otherwise the number of acl
1563fa9e4066Sahrens  * entries is returned
1564fa9e4066Sahrens  */
1565fa9e4066Sahrens int
1566fa9e4066Sahrens acl_get(const char *path, int get_flag, acl_t **aclp)
1567fa9e4066Sahrens {
1568fa9e4066Sahrens 	acl_inp acl_inp;
1569fa9e4066Sahrens 	acl_inp.file = path;
1570fa9e4066Sahrens 
1571fa9e4066Sahrens 	return (cacl_get(acl_inp, get_flag, ACL_PATH, aclp));
1572fa9e4066Sahrens }
1573fa9e4066Sahrens 
1574fa9e4066Sahrens int
1575fa9e4066Sahrens facl_get(int fd, int get_flag, acl_t **aclp)
1576fa9e4066Sahrens {
1577fa9e4066Sahrens 
1578fa9e4066Sahrens 	acl_inp acl_inp;
1579fa9e4066Sahrens 	acl_inp.fd = fd;
1580fa9e4066Sahrens 
1581fa9e4066Sahrens 	return (cacl_get(acl_inp, get_flag, ACL_FD, aclp));
1582fa9e4066Sahrens }
1583fa9e4066Sahrens 
15843eb3c573Smarks static int
15853eb3c573Smarks acl_translate(acl_t *aclp, int target_flavor, int isdir, uid_t owner,
15863eb3c573Smarks     gid_t group)
15873eb3c573Smarks {
15883eb3c573Smarks 	int aclcnt;
15893eb3c573Smarks 	void *acldata;
15903eb3c573Smarks 	int error;
15913eb3c573Smarks 
15923eb3c573Smarks 	/*
15933eb3c573Smarks 	 * See if we need to translate
15943eb3c573Smarks 	 */
15953eb3c573Smarks 	if ((target_flavor == _ACL_ACE_ENABLED && aclp->acl_type == ACE_T) ||
15963eb3c573Smarks 	    (target_flavor == _ACL_ACLENT_ENABLED &&
15973eb3c573Smarks 	    aclp->acl_type == ACLENT_T))
15983eb3c573Smarks 		return (0);
15993eb3c573Smarks 
16003eb3c573Smarks 	if (target_flavor == -1)
16013eb3c573Smarks 		return (-1);
16023eb3c573Smarks 
16033eb3c573Smarks 	if (target_flavor ==  _ACL_ACE_ENABLED &&
16043eb3c573Smarks 	    aclp->acl_type == ACLENT_T) {
16053eb3c573Smarks 		error = convert_aent_to_ace(aclp->acl_aclp,
16063eb3c573Smarks 		    aclp->acl_cnt, isdir, (ace_t **)&acldata, &aclcnt);
16073eb3c573Smarks 		if (error) {
16083eb3c573Smarks 			errno = error;
16093eb3c573Smarks 			return (-1);
16103eb3c573Smarks 		}
16113eb3c573Smarks 	} else if (target_flavor == _ACL_ACLENT_ENABLED &&
16123eb3c573Smarks 	    aclp->acl_type == ACE_T) {
16133eb3c573Smarks 		error = convert_ace_to_aent(aclp->acl_aclp, aclp->acl_cnt,
16143eb3c573Smarks 		    isdir, owner, group, (aclent_t **)&acldata, &aclcnt);
16153eb3c573Smarks 		if (error) {
16163eb3c573Smarks 			errno = error;
16173eb3c573Smarks 			return (-1);
16183eb3c573Smarks 		}
16193eb3c573Smarks 	} else {
16203eb3c573Smarks 		errno = ENOTSUP;
16213eb3c573Smarks 		return (-1);
16223eb3c573Smarks 	}
16233eb3c573Smarks 
16243eb3c573Smarks 	/*
16253eb3c573Smarks 	 * replace old acl with newly translated acl
16263eb3c573Smarks 	 */
16273eb3c573Smarks 	free(aclp->acl_aclp);
16283eb3c573Smarks 	aclp->acl_aclp = acldata;
16293eb3c573Smarks 	aclp->acl_cnt = aclcnt;
16303eb3c573Smarks 	aclp->acl_type = (target_flavor == _ACL_ACE_ENABLED) ? ACE_T : ACLENT_T;
16313eb3c573Smarks 	return (0);
16323eb3c573Smarks }
16333eb3c573Smarks 
1634fa9e4066Sahrens /*
1635fa9e4066Sahrens  * Set an ACL, translates acl to ace_t when appropriate.
1636fa9e4066Sahrens  */
1637fa9e4066Sahrens static int
1638fa9e4066Sahrens cacl_set(acl_inp *acl_inp, acl_t *aclp, int type)
1639fa9e4066Sahrens {
1640fa9e4066Sahrens 	int error = 0;
1641fa9e4066Sahrens 	int acl_flavor_target;
1642fa9e4066Sahrens 	struct stat64 statbuf;
1643fa9e4066Sahrens 	int stat_error;
1644fa9e4066Sahrens 	int isdir;
1645fa9e4066Sahrens 
1646fa9e4066Sahrens 
1647fa9e4066Sahrens 	if (type == ACL_PATH) {
1648fa9e4066Sahrens 		stat_error = stat64(acl_inp->file, &statbuf);
1649fa9e4066Sahrens 		if (stat_error)
1650fa9e4066Sahrens 			return (-1);
1651fa9e4066Sahrens 		acl_flavor_target = pathconf(acl_inp->file, _PC_ACL_ENABLED);
1652fa9e4066Sahrens 	} else {
1653fa9e4066Sahrens 		stat_error = fstat64(acl_inp->fd, &statbuf);
1654fa9e4066Sahrens 		if (stat_error)
1655fa9e4066Sahrens 			return (-1);
1656fa9e4066Sahrens 		acl_flavor_target = fpathconf(acl_inp->fd, _PC_ACL_ENABLED);
1657fa9e4066Sahrens 	}
1658fa9e4066Sahrens 
1659fa9e4066Sahrens 	isdir = S_ISDIR(statbuf.st_mode);
1660fa9e4066Sahrens 
16613eb3c573Smarks 	if ((error = acl_translate(aclp, acl_flavor_target, isdir,
16623eb3c573Smarks 	    statbuf.st_uid, statbuf.st_gid)) != 0) {
16633eb3c573Smarks 		return (error);
1664fa9e4066Sahrens 	}
1665fa9e4066Sahrens 
1666fa9e4066Sahrens 	if (type == ACL_PATH) {
1667fa9e4066Sahrens 		error = acl(acl_inp->file,
1668fa9e4066Sahrens 		    (aclp->acl_type == ACE_T) ? ACE_SETACL : SETACL,
1669fa9e4066Sahrens 		    aclp->acl_cnt, aclp->acl_aclp);
1670fa9e4066Sahrens 	} else {
1671fa9e4066Sahrens 		error = facl(acl_inp->fd,
1672fa9e4066Sahrens 		    (aclp->acl_type == ACE_T) ? ACE_SETACL : SETACL,
1673fa9e4066Sahrens 		    aclp->acl_cnt, aclp->acl_aclp);
1674fa9e4066Sahrens 	}
1675fa9e4066Sahrens 
1676fa9e4066Sahrens 	return (error);
1677fa9e4066Sahrens }
1678fa9e4066Sahrens 
1679fa9e4066Sahrens int
1680fa9e4066Sahrens acl_set(const char *path, acl_t *aclp)
1681fa9e4066Sahrens {
1682fa9e4066Sahrens 	acl_inp acl_inp;
1683fa9e4066Sahrens 
1684fa9e4066Sahrens 	acl_inp.file = path;
1685fa9e4066Sahrens 
1686fa9e4066Sahrens 	return (cacl_set(&acl_inp, aclp, ACL_PATH));
1687fa9e4066Sahrens }
1688fa9e4066Sahrens 
1689fa9e4066Sahrens int
1690fa9e4066Sahrens facl_set(int fd, acl_t *aclp)
1691fa9e4066Sahrens {
1692fa9e4066Sahrens 	acl_inp acl_inp;
1693fa9e4066Sahrens 
1694fa9e4066Sahrens 	acl_inp.fd = fd;
1695fa9e4066Sahrens 
1696fa9e4066Sahrens 	return (cacl_set(&acl_inp, aclp, ACL_FD));
1697fa9e4066Sahrens }
1698fa9e4066Sahrens 
1699fa9e4066Sahrens int
1700fa9e4066Sahrens acl_cnt(acl_t *aclp)
1701fa9e4066Sahrens {
1702fa9e4066Sahrens 	return (aclp->acl_cnt);
1703fa9e4066Sahrens }
1704fa9e4066Sahrens 
1705fa9e4066Sahrens int
1706fa9e4066Sahrens acl_type(acl_t *aclp)
1707fa9e4066Sahrens {
1708fa9e4066Sahrens 	return (aclp->acl_type);
1709fa9e4066Sahrens }
1710fa9e4066Sahrens 
1711fa9e4066Sahrens acl_t *
1712fa9e4066Sahrens acl_dup(acl_t *aclp)
1713fa9e4066Sahrens {
1714fa9e4066Sahrens 	acl_t *newaclp;
1715fa9e4066Sahrens 
1716fa9e4066Sahrens 	newaclp = acl_alloc(aclp->acl_type);
1717fa9e4066Sahrens 	if (newaclp == NULL)
1718fa9e4066Sahrens 		return (NULL);
1719fa9e4066Sahrens 
1720fa9e4066Sahrens 	newaclp->acl_aclp = malloc(aclp->acl_entry_size * aclp->acl_cnt);
1721fa9e4066Sahrens 	if (newaclp->acl_aclp == NULL) {
1722fa9e4066Sahrens 		acl_free(newaclp);
1723fa9e4066Sahrens 		return (NULL);
1724fa9e4066Sahrens 	}
1725fa9e4066Sahrens 
1726fa9e4066Sahrens 	(void) memcpy(newaclp->acl_aclp,
1727fa9e4066Sahrens 	    aclp->acl_aclp, aclp->acl_entry_size * aclp->acl_cnt);
1728fa9e4066Sahrens 	newaclp->acl_cnt = aclp->acl_cnt;
1729fa9e4066Sahrens 
1730fa9e4066Sahrens 	return (newaclp);
1731fa9e4066Sahrens }
1732fa9e4066Sahrens 
1733fa9e4066Sahrens int
1734fa9e4066Sahrens acl_flags(acl_t *aclp)
1735fa9e4066Sahrens {
1736fa9e4066Sahrens 	return (aclp->acl_flags);
1737fa9e4066Sahrens }
1738fa9e4066Sahrens 
1739fa9e4066Sahrens void *
1740fa9e4066Sahrens acl_data(acl_t *aclp)
1741fa9e4066Sahrens {
1742fa9e4066Sahrens 	return (aclp->acl_aclp);
1743fa9e4066Sahrens }
1744fa9e4066Sahrens 
1745fa9e4066Sahrens /*
1746fa9e4066Sahrens  * Remove an ACL from a file and create a trivial ACL based
1747fa9e4066Sahrens  * off of the mode argument.  After acl has been set owner/group
1748fa9e4066Sahrens  * are updated to match owner,group arguments
1749fa9e4066Sahrens  */
1750fa9e4066Sahrens int
1751fa9e4066Sahrens acl_strip(const char *file, uid_t owner, gid_t group, mode_t mode)
1752fa9e4066Sahrens {
1753fa9e4066Sahrens 	int	error = 0;
1754fa9e4066Sahrens 	aclent_t min_acl[MIN_ACL_ENTRIES];
1755fa9e4066Sahrens 	ace_t	min_ace_acl[6];	/* owner, group, everyone + complement denies */
1756fa9e4066Sahrens 	int	acl_flavor;
1757fa9e4066Sahrens 	int	aclcnt;
1758fa9e4066Sahrens 
1759fa9e4066Sahrens 	acl_flavor = pathconf(file, _PC_ACL_ENABLED);
1760fa9e4066Sahrens 
1761fa9e4066Sahrens 	if (acl_flavor == -1)
1762fa9e4066Sahrens 		return (-1);
1763fa9e4066Sahrens 	/*
1764fa9e4066Sahrens 	 * force it through aclent flavor when file system doesn't
1765fa9e4066Sahrens 	 * understand question
1766fa9e4066Sahrens 	 */
1767fa9e4066Sahrens 	if (acl_flavor == 0)
1768fa9e4066Sahrens 		acl_flavor = _ACL_ACLENT_ENABLED;
1769fa9e4066Sahrens 
1770fa9e4066Sahrens 	if (acl_flavor & _ACL_ACLENT_ENABLED) {
1771fa9e4066Sahrens 		min_acl[0].a_type = USER_OBJ;
1772fa9e4066Sahrens 		min_acl[0].a_id   = owner;
1773fa9e4066Sahrens 		min_acl[0].a_perm = ((mode & 0700) >> 6);
1774fa9e4066Sahrens 		min_acl[1].a_type = GROUP_OBJ;
1775fa9e4066Sahrens 		min_acl[1].a_id   = group;
1776fa9e4066Sahrens 		min_acl[1].a_perm = ((mode & 0070) >> 3);
1777fa9e4066Sahrens 		min_acl[2].a_type = CLASS_OBJ;
1778fa9e4066Sahrens 		min_acl[2].a_id   = (uid_t)-1;
1779fa9e4066Sahrens 		min_acl[2].a_perm = ((mode & 0070) >> 3);
1780fa9e4066Sahrens 		min_acl[3].a_type = OTHER_OBJ;
1781fa9e4066Sahrens 		min_acl[3].a_id   = (uid_t)-1;
1782fa9e4066Sahrens 		min_acl[3].a_perm = (mode & 0007);
1783fa9e4066Sahrens 		aclcnt = 4;
1784fa9e4066Sahrens 		error = acl(file, SETACL, aclcnt, min_acl);
1785fa9e4066Sahrens 	} else if (acl_flavor & _ACL_ACE_ENABLED) {
1786fa9e4066Sahrens 		(void) memcpy(min_ace_acl, trivial_acl, sizeof (ace_t) * 6);
1787fa9e4066Sahrens 
1788fa9e4066Sahrens 		/*
1789fa9e4066Sahrens 		 * Make aces match request mode
1790fa9e4066Sahrens 		 */
1791fa9e4066Sahrens 		adjust_ace_pair(&min_ace_acl[0], (mode & 0700) >> 6);
1792fa9e4066Sahrens 		adjust_ace_pair(&min_ace_acl[2], (mode & 0070) >> 3);
1793fa9e4066Sahrens 		adjust_ace_pair(&min_ace_acl[4], mode & 0007);
1794fa9e4066Sahrens 
1795fa9e4066Sahrens 		error = acl(file, ACE_SETACL, 6, min_ace_acl);
1796fa9e4066Sahrens 	} else {
1797fa9e4066Sahrens 		errno = EINVAL;
1798fa9e4066Sahrens 		error = 1;
1799fa9e4066Sahrens 	}
1800fa9e4066Sahrens 
1801fa9e4066Sahrens 	if (error == 0)
1802fa9e4066Sahrens 		error = chown(file, owner, group);
1803fa9e4066Sahrens 	return (error);
1804fa9e4066Sahrens }
1805fa9e4066Sahrens 
1806fa9e4066Sahrens static int
1807fa9e4066Sahrens ace_match(void *entry1, void *entry2)
1808fa9e4066Sahrens {
1809fa9e4066Sahrens 	ace_t *p1 = (ace_t *)entry1;
1810fa9e4066Sahrens 	ace_t *p2 = (ace_t *)entry2;
1811fa9e4066Sahrens 	ace_t ace1, ace2;
1812fa9e4066Sahrens 
1813fa9e4066Sahrens 	ace1 = *p1;
1814fa9e4066Sahrens 	ace2 = *p2;
1815fa9e4066Sahrens 
1816fa9e4066Sahrens 	/*
1817fa9e4066Sahrens 	 * Need to fixup who field for abstrations for
1818fa9e4066Sahrens 	 * accurate comparison, since field is undefined.
1819fa9e4066Sahrens 	 */
1820fa9e4066Sahrens 	if (ace1.a_flags & (ACE_OWNER|ACE_GROUP|ACE_EVERYONE))
1821fa9e4066Sahrens 		ace1.a_who = -1;
1822fa9e4066Sahrens 	if (ace2.a_flags & (ACE_OWNER|ACE_GROUP|ACE_EVERYONE))
1823fa9e4066Sahrens 		ace2.a_who = -1;
1824fa9e4066Sahrens 	return (memcmp(&ace1, &ace2, sizeof (ace_t)));
1825fa9e4066Sahrens }
1826fa9e4066Sahrens 
1827fa9e4066Sahrens static int
1828fa9e4066Sahrens aclent_match(void *entry1, void *entry2)
1829fa9e4066Sahrens {
1830fa9e4066Sahrens 	aclent_t *aclent1 = (aclent_t *)entry1;
1831fa9e4066Sahrens 	aclent_t *aclent2 = (aclent_t *)entry2;
1832fa9e4066Sahrens 
1833fa9e4066Sahrens 	return (memcmp(aclent1, aclent2, sizeof (aclent_t)));
1834fa9e4066Sahrens }
1835fa9e4066Sahrens 
1836fa9e4066Sahrens /*
1837fa9e4066Sahrens  * Find acl entries in acl that correspond to removeacl.  Search
1838fa9e4066Sahrens  * is started from slot.  The flag argument indicates whether to
1839fa9e4066Sahrens  * remove all matches or just the first match.
1840fa9e4066Sahrens  */
1841fa9e4066Sahrens int
1842fa9e4066Sahrens acl_removeentries(acl_t *acl, acl_t *removeacl, int start_slot, int flag)
1843fa9e4066Sahrens {
1844fa9e4066Sahrens 	int i, j;
1845fa9e4066Sahrens 	int match;
1846fa9e4066Sahrens 	int (*acl_match)(void *acl1, void *acl2);
1847fa9e4066Sahrens 	void *acl_entry, *remove_entry;
1848fa9e4066Sahrens 	void *start;
1849fa9e4066Sahrens 	int found = 0;
1850fa9e4066Sahrens 
1851fa9e4066Sahrens 	if (flag != ACL_REMOVE_ALL && flag != ACL_REMOVE_FIRST)
1852fa9e4066Sahrens 		flag = ACL_REMOVE_FIRST;
1853fa9e4066Sahrens 
1854fa9e4066Sahrens 	if (acl == NULL || removeacl == NULL)
1855fa9e4066Sahrens 		return (EACL_NO_ACL_ENTRY);
1856fa9e4066Sahrens 
1857fa9e4066Sahrens 	if (acl->acl_type != removeacl->acl_type)
1858fa9e4066Sahrens 		return (EACL_DIFF_TYPE);
1859fa9e4066Sahrens 
1860fa9e4066Sahrens 	if (acl->acl_type == ACLENT_T)
1861fa9e4066Sahrens 		acl_match = aclent_match;
1862fa9e4066Sahrens 	else
1863fa9e4066Sahrens 		acl_match = ace_match;
1864fa9e4066Sahrens 
1865fa9e4066Sahrens 	for (i = 0, remove_entry = removeacl->acl_aclp;
1866fa9e4066Sahrens 	    i != removeacl->acl_cnt; i++) {
1867fa9e4066Sahrens 
1868fa9e4066Sahrens 		j = 0;
1869fa9e4066Sahrens 		acl_entry = (char *)acl->acl_aclp +
1870fa9e4066Sahrens 		    (acl->acl_entry_size * start_slot);
1871fa9e4066Sahrens 		for (;;) {
1872fa9e4066Sahrens 			match = acl_match(acl_entry, remove_entry);
1873fa9e4066Sahrens 			if (match == 0)  {
1874fa9e4066Sahrens 				found++;
1875fa9e4066Sahrens 				start = (char *)acl_entry +
1876fa9e4066Sahrens 				    acl->acl_entry_size;
1877fa9e4066Sahrens 				(void) memmove(acl_entry, start,
1878fa9e4066Sahrens 				    acl->acl_entry_size *
1879fa9e4066Sahrens 				    acl->acl_cnt-- - (j + 1));
1880fa9e4066Sahrens 
1881fa9e4066Sahrens 				if (flag == ACL_REMOVE_FIRST)
1882fa9e4066Sahrens 					break;
1883fa9e4066Sahrens 				/*
1884fa9e4066Sahrens 				 * List has changed, restart search from
1885fa9e4066Sahrens 				 * beginning.
1886fa9e4066Sahrens 				 */
1887fa9e4066Sahrens 				acl_entry = acl->acl_aclp;
1888fa9e4066Sahrens 				j = 0;
1889fa9e4066Sahrens 				continue;
1890fa9e4066Sahrens 			}
1891fa9e4066Sahrens 			acl_entry = ((char *)acl_entry + acl->acl_entry_size);
1892fa9e4066Sahrens 			if (++j >= acl->acl_cnt) {
1893fa9e4066Sahrens 				break;
1894fa9e4066Sahrens 			}
1895fa9e4066Sahrens 		}
1896fa9e4066Sahrens 	}
1897fa9e4066Sahrens 
1898fa9e4066Sahrens 	return ((found == 0) ? EACL_NO_ACL_ENTRY : 0);
1899fa9e4066Sahrens }
1900fa9e4066Sahrens 
1901fa9e4066Sahrens /*
1902fa9e4066Sahrens  * Replace entires entries in acl1 with the corresponding entries
1903fa9e4066Sahrens  * in newentries.  The where argument specifies where to begin
1904fa9e4066Sahrens  * the replacement.  If the where argument is 1 greater than the
1905fa9e4066Sahrens  * number of acl entries in acl1 then they are appended.  If the
1906fa9e4066Sahrens  * where argument is 2+ greater than the number of acl entries then
1907fa9e4066Sahrens  * EACL_INVALID_SLOT is returned.
1908fa9e4066Sahrens  */
1909fa9e4066Sahrens int
1910fa9e4066Sahrens acl_modifyentries(acl_t *acl1, acl_t *newentries, int where)
1911fa9e4066Sahrens {
1912fa9e4066Sahrens 
1913fa9e4066Sahrens 	int slot;
1914fa9e4066Sahrens 	int slots_needed;
1915fa9e4066Sahrens 	int slots_left;
1916fa9e4066Sahrens 	int newsize;
1917fa9e4066Sahrens 
1918fa9e4066Sahrens 	if (acl1 == NULL || newentries == NULL)
1919fa9e4066Sahrens 		return (EACL_NO_ACL_ENTRY);
1920fa9e4066Sahrens 
1921fa9e4066Sahrens 	if (where < 0 || where >= acl1->acl_cnt)
1922fa9e4066Sahrens 		return (EACL_INVALID_SLOT);
1923fa9e4066Sahrens 
1924fa9e4066Sahrens 	if (acl1->acl_type != newentries->acl_type)
1925fa9e4066Sahrens 		return (EACL_DIFF_TYPE);
1926fa9e4066Sahrens 
1927fa9e4066Sahrens 	slot = where;
1928fa9e4066Sahrens 
1929fa9e4066Sahrens 	slots_left = acl1->acl_cnt - slot + 1;
1930fa9e4066Sahrens 	if (slots_left < newentries->acl_cnt) {
1931fa9e4066Sahrens 		slots_needed = newentries->acl_cnt - slots_left;
1932fa9e4066Sahrens 		newsize = (acl1->acl_entry_size * acl1->acl_cnt) +
1933fa9e4066Sahrens 		    (acl1->acl_entry_size * slots_needed);
1934fa9e4066Sahrens 		acl1->acl_aclp = realloc(acl1->acl_aclp, newsize);
1935fa9e4066Sahrens 		if (acl1->acl_aclp == NULL)
1936fa9e4066Sahrens 			return (-1);
1937fa9e4066Sahrens 	}
1938fa9e4066Sahrens 	(void) memcpy((char *)acl1->acl_aclp + (acl1->acl_entry_size * slot),
1939fa9e4066Sahrens 	    newentries->acl_aclp,
1940fa9e4066Sahrens 	    newentries->acl_entry_size * newentries->acl_cnt);
1941fa9e4066Sahrens 
1942fa9e4066Sahrens 	/*
1943fa9e4066Sahrens 	 * Did ACL grow?
1944fa9e4066Sahrens 	 */
1945fa9e4066Sahrens 
1946fa9e4066Sahrens 	if ((slot + newentries->acl_cnt) > acl1->acl_cnt) {
1947fa9e4066Sahrens 		acl1->acl_cnt = slot + newentries->acl_cnt;
1948fa9e4066Sahrens 	}
1949fa9e4066Sahrens 
1950fa9e4066Sahrens 	return (0);
1951fa9e4066Sahrens }
1952fa9e4066Sahrens 
1953fa9e4066Sahrens /*
1954fa9e4066Sahrens  * Add acl2 entries into acl1.  The where argument specifies where
1955fa9e4066Sahrens  * to add the entries.
1956fa9e4066Sahrens  */
1957fa9e4066Sahrens int
1958fa9e4066Sahrens acl_addentries(acl_t *acl1, acl_t *acl2, int where)
1959fa9e4066Sahrens {
1960fa9e4066Sahrens 
1961fa9e4066Sahrens 	int newsize;
1962fa9e4066Sahrens 	int len;
1963fa9e4066Sahrens 	void *start;
1964fa9e4066Sahrens 	void *to;
1965fa9e4066Sahrens 
1966fa9e4066Sahrens 	if (acl1 == NULL || acl2 == NULL)
1967fa9e4066Sahrens 		return (EACL_NO_ACL_ENTRY);
1968fa9e4066Sahrens 
1969fa9e4066Sahrens 	if (acl1->acl_type != acl2->acl_type)
1970fa9e4066Sahrens 		return (EACL_DIFF_TYPE);
1971fa9e4066Sahrens 
1972fa9e4066Sahrens 	/*
1973fa9e4066Sahrens 	 * allow where to specify 1 past last slot for an append operation
1974fa9e4066Sahrens 	 * but anything greater is an error.
1975fa9e4066Sahrens 	 */
1976fa9e4066Sahrens 	if (where < 0 || where > acl1->acl_cnt)
1977fa9e4066Sahrens 		return (EACL_INVALID_SLOT);
1978fa9e4066Sahrens 
1979fa9e4066Sahrens 	newsize = (acl2->acl_entry_size * acl2->acl_cnt) +
1980fa9e4066Sahrens 	    (acl1->acl_entry_size * acl1->acl_cnt);
1981fa9e4066Sahrens 	acl1->acl_aclp = realloc(acl1->acl_aclp, newsize);
1982fa9e4066Sahrens 	if (acl1->acl_aclp == NULL)
1983fa9e4066Sahrens 		return (-1);
1984fa9e4066Sahrens 
1985fa9e4066Sahrens 	/*
1986fa9e4066Sahrens 	 * first push down entries where new ones will be inserted
1987fa9e4066Sahrens 	 */
1988fa9e4066Sahrens 
1989fa9e4066Sahrens 	to = (void *)((char *)acl1->acl_aclp +
1990fa9e4066Sahrens 	    ((where + acl2->acl_cnt) * acl1->acl_entry_size));
1991fa9e4066Sahrens 
1992fa9e4066Sahrens 	start = (void *)((char *)acl1->acl_aclp +
1993fa9e4066Sahrens 	    where * acl1->acl_entry_size);
1994fa9e4066Sahrens 
1995fa9e4066Sahrens 	if (where < acl1->acl_cnt) {
1996fa9e4066Sahrens 		len = (acl1->acl_cnt - where) * acl1->acl_entry_size;
1997fa9e4066Sahrens 		(void) memmove(to, start, len);
1998fa9e4066Sahrens 	}
1999fa9e4066Sahrens 
2000fa9e4066Sahrens 	/*
2001fa9e4066Sahrens 	 * now stick in new entries.
2002fa9e4066Sahrens 	 */
2003fa9e4066Sahrens 
2004fa9e4066Sahrens 	(void) memmove(start, acl2->acl_aclp,
2005fa9e4066Sahrens 	    acl2->acl_cnt * acl2->acl_entry_size);
2006fa9e4066Sahrens 
2007fa9e4066Sahrens 	acl1->acl_cnt += acl2->acl_cnt;
2008fa9e4066Sahrens 	return (0);
2009fa9e4066Sahrens }
2010fa9e4066Sahrens 
2011fa9e4066Sahrens /*
2012fa9e4066Sahrens  * return text for an ACL error.
2013fa9e4066Sahrens  */
2014fa9e4066Sahrens char *
2015fa9e4066Sahrens acl_strerror(int errnum)
2016fa9e4066Sahrens {
2017fa9e4066Sahrens 	switch (errnum) {
2018fa9e4066Sahrens 	case EACL_GRP_ERROR:
2019fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN,
20205a5eeccaSmarks 		    "There is more than one group or default group entry"));
2021fa9e4066Sahrens 	case EACL_USER_ERROR:
2022fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN,
20235a5eeccaSmarks 		    "There is more than one user or default user entry"));
2024fa9e4066Sahrens 	case EACL_OTHER_ERROR:
2025fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN,
2026fa9e4066Sahrens 		    "There is more than one other entry"));
2027fa9e4066Sahrens 	case EACL_CLASS_ERROR:
2028fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN,
2029fa9e4066Sahrens 		    "There is more than one mask entry"));
2030fa9e4066Sahrens 	case EACL_DUPLICATE_ERROR:
2031fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN,
2032fa9e4066Sahrens 		    "Duplicate user or group entries"));
2033fa9e4066Sahrens 	case EACL_MISS_ERROR:
2034fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN,
2035fa9e4066Sahrens 		    "Missing user/group owner, other, mask entry"));
2036fa9e4066Sahrens 	case EACL_MEM_ERROR:
2037fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN,
2038fa9e4066Sahrens 		    "Memory error"));
2039fa9e4066Sahrens 	case EACL_ENTRY_ERROR:
2040fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN,
2041fa9e4066Sahrens 		    "Unrecognized entry type"));
2042fa9e4066Sahrens 	case EACL_INHERIT_ERROR:
2043fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN,
2044fa9e4066Sahrens 		    "Invalid inheritance flags"));
2045fa9e4066Sahrens 	case EACL_FLAGS_ERROR:
2046fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN,
2047fa9e4066Sahrens 		    "Unrecognized entry flags"));
2048fa9e4066Sahrens 	case EACL_PERM_MASK_ERROR:
2049fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN,
2050fa9e4066Sahrens 		    "Invalid ACL permissions"));
2051fa9e4066Sahrens 	case EACL_COUNT_ERROR:
2052fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN,
2053fa9e4066Sahrens 		    "Invalid ACL count"));
2054fa9e4066Sahrens 	case EACL_INVALID_SLOT:
2055fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN,
2056fa9e4066Sahrens 		    "Invalid ACL entry number specified"));
2057fa9e4066Sahrens 	case EACL_NO_ACL_ENTRY:
2058fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN,
2059fa9e4066Sahrens 		    "ACL entry doesn't exist"));
2060fa9e4066Sahrens 	case EACL_DIFF_TYPE:
2061fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN,
2062fa9e4066Sahrens 		    "ACL type's are different"));
2063fa9e4066Sahrens 	case EACL_INVALID_USER_GROUP:
2064fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN, "Invalid user or group"));
2065fa9e4066Sahrens 	case EACL_INVALID_STR:
2066fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN, "ACL string is invalid"));
2067fa9e4066Sahrens 	case EACL_FIELD_NOT_BLANK:
2068fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN, "Field expected to be blank"));
2069fa9e4066Sahrens 	case EACL_INVALID_ACCESS_TYPE:
2070fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN, "Invalid access type"));
2071fa9e4066Sahrens 	case EACL_UNKNOWN_DATA:
2072fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN, "Unrecognized entry"));
2073fa9e4066Sahrens 	case EACL_MISSING_FIELDS:
2074fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN,
2075fa9e4066Sahrens 		    "ACL specification missing required fields"));
2076fa9e4066Sahrens 	case EACL_INHERIT_NOTDIR:
2077fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN,
2078fa9e4066Sahrens 		    "Inheritance flags are only allowed on directories"));
2079fa9e4066Sahrens 	case -1:
2080fa9e4066Sahrens 		return (strerror(errno));
2081fa9e4066Sahrens 	default:
2082fa9e4066Sahrens 		errno = EINVAL;
2083fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN, "Unknown error"));
2084fa9e4066Sahrens 	}
2085fa9e4066Sahrens }
20865a5eeccaSmarks 
20875a5eeccaSmarks extern int yyinteractive;
20885a5eeccaSmarks 
20895a5eeccaSmarks /* PRINTFLIKE1 */
20905a5eeccaSmarks void
20915a5eeccaSmarks acl_error(const char *fmt, ...)
20925a5eeccaSmarks {
20935a5eeccaSmarks 	va_list va;
20945a5eeccaSmarks 
20955a5eeccaSmarks 	if (yyinteractive == 0)
20965a5eeccaSmarks 		return;
20975a5eeccaSmarks 
20985a5eeccaSmarks 	va_start(va, fmt);
20995a5eeccaSmarks 	(void) vfprintf(stderr, fmt, va);
21005a5eeccaSmarks 	va_end(va);
21015a5eeccaSmarks }
2102