1789Sahrens /*
2789Sahrens  * CDDL HEADER START
3789Sahrens  *
4789Sahrens  * The contents of this file are subject to the terms of the
5*1462Smarks  * Common Development and Distribution License (the "License").
6*1462Smarks  * 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 /*
221231Smarks  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23789Sahrens  * Use is subject to license terms.
24789Sahrens  */
25789Sahrens 
26789Sahrens #pragma ident	"%Z%%M%	%I%	%E% SMI"
27789Sahrens 
28789Sahrens #include <stdlib.h>
29789Sahrens #include <string.h>
30789Sahrens #include <unistd.h>
31789Sahrens #include <limits.h>
32789Sahrens #include <grp.h>
33789Sahrens #include <pwd.h>
34*1462Smarks #include <strings.h>
35789Sahrens #include <sys/types.h>
36789Sahrens #include <sys/acl.h>
37789Sahrens #include <errno.h>
38789Sahrens #include <sys/stat.h>
391420Smarks #include <sys/varargs.h>
40789Sahrens #include <locale.h>
41789Sahrens #include <aclutils.h>
42789Sahrens #include <acl_common.h>
43*1462Smarks #include <sys/avl.h>
44*1462Smarks 
45*1462Smarks #define	offsetof(s, m)	((size_t)(&(((s *)0)->m)))
46789Sahrens 
47789Sahrens #define	ACL_PATH	0
48789Sahrens #define	ACL_FD		1
49789Sahrens 
50789Sahrens #define	ACE_POSIX_SUPPORTED_BITS (ACE_READ_DATA | \
51789Sahrens     ACE_WRITE_DATA | ACE_APPEND_DATA | ACE_EXECUTE | \
52789Sahrens     ACE_READ_ATTRIBUTES | ACE_READ_ACL | ACE_WRITE_ACL)
53789Sahrens 
54789Sahrens 
55*1462Smarks #define	ACL_SYNCHRONIZE_SET_DENY		0x0000001
56789Sahrens #define	ACL_SYNCHRONIZE_SET_ALLOW		0x0000002
57*1462Smarks #define	ACL_SYNCHRONIZE_ERR_DENY		0x0000004
58*1462Smarks #define	ACL_SYNCHRONIZE_ERR_ALLOW		0x0000008
59789Sahrens 
60*1462Smarks #define	ACL_WRITE_OWNER_SET_DENY		0x0000010
61789Sahrens #define	ACL_WRITE_OWNER_SET_ALLOW		0x0000020
62*1462Smarks #define	ACL_WRITE_OWNER_ERR_DENY		0x0000040
63*1462Smarks #define	ACL_WRITE_OWNER_ERR_ALLOW		0x0000080
64789Sahrens 
65*1462Smarks #define	ACL_DELETE_SET_DENY			0x0000100
66*1462Smarks #define	ACL_DELETE_SET_ALLOW			0x0000200
67*1462Smarks #define	ACL_DELETE_ERR_DENY			0x0000400
68*1462Smarks #define	ACL_DELETE_ERR_ALLOW			0x0000800
69*1462Smarks 
70*1462Smarks #define	ACL_WRITE_ATTRS_OWNER_SET_DENY		0x0001000
71789Sahrens #define	ACL_WRITE_ATTRS_OWNER_SET_ALLOW		0x0002000
72*1462Smarks #define	ACL_WRITE_ATTRS_OWNER_ERR_DENY		0x0004000
73*1462Smarks #define	ACL_WRITE_ATTRS_OWNER_ERR_ALLOW		0x0008000
74789Sahrens 
75789Sahrens #define	ACL_WRITE_ATTRS_WRITER_SET_DENY		0x0010000
76*1462Smarks #define	ACL_WRITE_ATTRS_WRITER_SET_ALLOW	0x0020000
77*1462Smarks #define	ACL_WRITE_ATTRS_WRITER_ERR_DENY		0x0040000
78*1462Smarks #define	ACL_WRITE_ATTRS_WRITER_ERR_ALLOW	0x0080000
79789Sahrens 
80*1462Smarks #define	ACL_WRITE_NAMED_WRITER_SET_DENY		0x0100000
81789Sahrens #define	ACL_WRITE_NAMED_WRITER_SET_ALLOW	0x0200000
82*1462Smarks #define	ACL_WRITE_NAMED_WRITER_ERR_DENY		0x0400000
83*1462Smarks #define	ACL_WRITE_NAMED_WRITER_ERR_ALLOW	0x0800000
84*1462Smarks 
85*1462Smarks #define	ACL_READ_NAMED_READER_SET_DENY		0x1000000
86*1462Smarks #define	ACL_READ_NAMED_READER_SET_ALLOW		0x2000000
87*1462Smarks #define	ACL_READ_NAMED_READER_ERR_DENY		0x4000000
88*1462Smarks #define	ACL_READ_NAMED_READER_ERR_ALLOW		0x8000000
89*1462Smarks 
90*1462Smarks 
91*1462Smarks #define	ACE_VALID_MASK_BITS (\
92*1462Smarks     ACE_READ_DATA | \
93*1462Smarks     ACE_LIST_DIRECTORY | \
94*1462Smarks     ACE_WRITE_DATA | \
95*1462Smarks     ACE_ADD_FILE | \
96*1462Smarks     ACE_APPEND_DATA | \
97*1462Smarks     ACE_ADD_SUBDIRECTORY | \
98*1462Smarks     ACE_READ_NAMED_ATTRS | \
99*1462Smarks     ACE_WRITE_NAMED_ATTRS | \
100*1462Smarks     ACE_EXECUTE | \
101*1462Smarks     ACE_DELETE_CHILD | \
102*1462Smarks     ACE_READ_ATTRIBUTES | \
103*1462Smarks     ACE_WRITE_ATTRIBUTES | \
104*1462Smarks     ACE_DELETE | \
105*1462Smarks     ACE_READ_ACL | \
106*1462Smarks     ACE_WRITE_ACL | \
107*1462Smarks     ACE_WRITE_OWNER | \
108*1462Smarks     ACE_SYNCHRONIZE)
109*1462Smarks 
110*1462Smarks #define	ACE_MASK_UNDEFINED			0x80000000
111*1462Smarks 
112*1462Smarks #define	ACE_VALID_FLAG_BITS (ACE_FILE_INHERIT_ACE | \
113*1462Smarks     ACE_DIRECTORY_INHERIT_ACE | \
114*1462Smarks     ACE_NO_PROPAGATE_INHERIT_ACE | ACE_INHERIT_ONLY_ACE | \
115*1462Smarks     ACE_SUCCESSFUL_ACCESS_ACE_FLAG | ACE_FAILED_ACCESS_ACE_FLAG | \
116*1462Smarks     ACE_IDENTIFIER_GROUP | ACE_OWNER | ACE_GROUP | ACE_EVERYONE)
117789Sahrens 
118*1462Smarks /*
119*1462Smarks  * ACL conversion helpers
120*1462Smarks  */
121*1462Smarks 
122*1462Smarks typedef enum {
123*1462Smarks 	ace_unused,
124*1462Smarks 	ace_user_obj,
125*1462Smarks 	ace_user,
126*1462Smarks 	ace_group, /* includes GROUP and GROUP_OBJ */
127*1462Smarks 	ace_other_obj
128*1462Smarks } ace_to_aent_state_t;
129789Sahrens 
130*1462Smarks typedef struct acevals {
131*1462Smarks 	uid_t key;
132*1462Smarks 	avl_node_t avl;
133*1462Smarks 	uint32_t mask;
134*1462Smarks 	uint32_t allowed;
135*1462Smarks 	uint32_t denied;
136*1462Smarks 	int aent_type;
137*1462Smarks } acevals_t;
138*1462Smarks 
139*1462Smarks typedef struct ace_list {
140*1462Smarks 	acevals_t user_obj;
141*1462Smarks 	avl_tree_t user;
142*1462Smarks 	int numusers;
143*1462Smarks 	acevals_t group_obj;
144*1462Smarks 	avl_tree_t group;
145*1462Smarks 	int numgroups;
146*1462Smarks 	acevals_t other_obj;
147*1462Smarks 	uint32_t acl_mask;
148*1462Smarks 	int hasmask;
149*1462Smarks 	int dfacl_flag;
150*1462Smarks 	ace_to_aent_state_t state;
151*1462Smarks 	int seen; /* bitmask of all aclent_t a_type values seen */
152*1462Smarks } ace_list_t;
1531231Smarks 
154789Sahrens typedef union {
155789Sahrens 	const char *file;
156789Sahrens 	int  fd;
157789Sahrens } acl_inp;
158789Sahrens 
159789Sahrens acl_t *
160789Sahrens acl_alloc(enum acl_type type)
161789Sahrens {
162789Sahrens 	acl_t *aclp;
163789Sahrens 
164789Sahrens 	aclp = malloc(sizeof (acl_t));
165789Sahrens 
166789Sahrens 	if (aclp == NULL)
167789Sahrens 		return (NULL);
168789Sahrens 
169789Sahrens 	aclp->acl_aclp = NULL;
170789Sahrens 	aclp->acl_cnt = 0;
171789Sahrens 
172789Sahrens 	switch (type) {
173789Sahrens 	case ACE_T:
174789Sahrens 		aclp->acl_type = ACE_T;
175789Sahrens 		aclp->acl_entry_size = sizeof (ace_t);
176789Sahrens 		break;
177789Sahrens 	case ACLENT_T:
178789Sahrens 		aclp->acl_type = ACLENT_T;
179789Sahrens 		aclp->acl_entry_size = sizeof (aclent_t);
180789Sahrens 		break;
181789Sahrens 	default:
182789Sahrens 		acl_free(aclp);
183789Sahrens 		aclp = NULL;
184789Sahrens 	}
185789Sahrens 	return (aclp);
186789Sahrens }
187789Sahrens 
188789Sahrens /*
189789Sahrens  * Free acl_t structure
190789Sahrens  */
191789Sahrens void
192789Sahrens acl_free(acl_t *aclp)
193789Sahrens {
194789Sahrens 	if (aclp == NULL)
195789Sahrens 		return;
196789Sahrens 
197789Sahrens 	if (aclp->acl_aclp)
198789Sahrens 		free(aclp->acl_aclp);
199789Sahrens 	free(aclp);
200789Sahrens }
201789Sahrens 
202789Sahrens /*
203789Sahrens  * Determine whether a file has a trivial ACL
204789Sahrens  * returns: 	0 = trivial
205789Sahrens  *		1 = nontrivial
206789Sahrens  *		<0 some other system failure, such as ENOENT or EPERM
207789Sahrens  */
208789Sahrens int
209789Sahrens acl_trivial(const char *filename)
210789Sahrens {
211789Sahrens 	int acl_flavor;
212789Sahrens 	int aclcnt;
213789Sahrens 	int cntcmd;
214789Sahrens 	int val = 0;
215789Sahrens 	ace_t *acep;
216789Sahrens 
217789Sahrens 	acl_flavor = pathconf(filename, _PC_ACL_ENABLED);
218789Sahrens 	if (acl_flavor == -1)
219789Sahrens 		return (-1);
220789Sahrens 
221789Sahrens 	if (acl_flavor == _ACL_ACE_ENABLED)
222789Sahrens 		cntcmd = ACE_GETACLCNT;
223789Sahrens 	else
224789Sahrens 		cntcmd = GETACLCNT;
225789Sahrens 
226789Sahrens 	aclcnt = acl(filename, cntcmd, 0, NULL);
227789Sahrens 	if (aclcnt > 0) {
228789Sahrens 		if (acl_flavor == _ACL_ACE_ENABLED) {
2291231Smarks 			acep = malloc(sizeof (ace_t) * aclcnt);
2301231Smarks 			if (acep == NULL)
2311231Smarks 				return (-1);
2321231Smarks 			if (acl(filename, ACE_GETACL,
2331231Smarks 			    aclcnt, acep) < 0) {
2341231Smarks 				free(acep);
2351231Smarks 				return (-1);
2361231Smarks 			}
237789Sahrens 
2381231Smarks 			val = ace_trivial(acep, aclcnt);
2391231Smarks 			free(acep);
2401231Smarks 
241789Sahrens 		} else if (aclcnt > MIN_ACL_ENTRIES)
242789Sahrens 			val = 1;
243789Sahrens 	}
244789Sahrens 	return (val);
245789Sahrens }
246789Sahrens 
247789Sahrens static uint32_t
248789Sahrens access_mask_set(int haswriteperm, int hasreadperm, int isowner, int isallow)
249789Sahrens {
250789Sahrens 	uint32_t access_mask = 0;
251789Sahrens 	int acl_produce;
252789Sahrens 	int synchronize_set = 0, write_owner_set = 0;
253789Sahrens 	int delete_set = 0, write_attrs_set = 0;
254789Sahrens 	int read_named_set = 0, write_named_set = 0;
255789Sahrens 
256789Sahrens 	acl_produce = (ACL_SYNCHRONIZE_SET_ALLOW |
257789Sahrens 	    ACL_WRITE_ATTRS_OWNER_SET_ALLOW |
258789Sahrens 	    ACL_WRITE_ATTRS_WRITER_SET_DENY);
259789Sahrens 
260789Sahrens 	if (isallow) {
261789Sahrens 		synchronize_set = ACL_SYNCHRONIZE_SET_ALLOW;
262789Sahrens 		write_owner_set = ACL_WRITE_OWNER_SET_ALLOW;
263789Sahrens 		delete_set = ACL_DELETE_SET_ALLOW;
264789Sahrens 		if (hasreadperm)
265789Sahrens 			read_named_set = ACL_READ_NAMED_READER_SET_ALLOW;
266789Sahrens 		if (haswriteperm)
267789Sahrens 			write_named_set = ACL_WRITE_NAMED_WRITER_SET_ALLOW;
268789Sahrens 		if (isowner)
269789Sahrens 			write_attrs_set = ACL_WRITE_ATTRS_OWNER_SET_ALLOW;
270789Sahrens 		else if (haswriteperm)
271789Sahrens 			write_attrs_set = ACL_WRITE_ATTRS_WRITER_SET_ALLOW;
272789Sahrens 	} else {
273789Sahrens 
274789Sahrens 		synchronize_set = ACL_SYNCHRONIZE_SET_DENY;
275789Sahrens 		write_owner_set = ACL_WRITE_OWNER_SET_DENY;
276789Sahrens 		delete_set = ACL_DELETE_SET_DENY;
277789Sahrens 		if (hasreadperm)
278789Sahrens 			read_named_set = ACL_READ_NAMED_READER_SET_DENY;
279789Sahrens 		if (haswriteperm)
280789Sahrens 			write_named_set = ACL_WRITE_NAMED_WRITER_SET_DENY;
281789Sahrens 		if (isowner)
282789Sahrens 			write_attrs_set = ACL_WRITE_ATTRS_OWNER_SET_DENY;
283789Sahrens 		else if (haswriteperm)
284789Sahrens 			write_attrs_set = ACL_WRITE_ATTRS_WRITER_SET_DENY;
285789Sahrens 		else
286789Sahrens 			/*
287789Sahrens 			 * If the entity is not the owner and does not
288789Sahrens 			 * have write permissions ACE_WRITE_ATTRIBUTES will
289789Sahrens 			 * always go in the DENY ACE.
290789Sahrens 			 */
291789Sahrens 			access_mask |= ACE_WRITE_ATTRIBUTES;
292789Sahrens 	}
293789Sahrens 
294789Sahrens 	if (acl_produce & synchronize_set)
295789Sahrens 		access_mask |= ACE_SYNCHRONIZE;
296789Sahrens 	if (acl_produce & write_owner_set)
297789Sahrens 		access_mask |= ACE_WRITE_OWNER;
298789Sahrens 	if (acl_produce & delete_set)
299789Sahrens 		access_mask |= ACE_DELETE;
300789Sahrens 	if (acl_produce & write_attrs_set)
301789Sahrens 		access_mask |= ACE_WRITE_ATTRIBUTES;
302789Sahrens 	if (acl_produce & read_named_set)
303789Sahrens 		access_mask |= ACE_READ_NAMED_ATTRS;
304789Sahrens 	if (acl_produce & write_named_set)
305789Sahrens 		access_mask |= ACE_WRITE_NAMED_ATTRS;
306789Sahrens 
307789Sahrens 	return (access_mask);
308789Sahrens }
309789Sahrens 
310789Sahrens /*
311789Sahrens  * Given an mode_t, convert it into an access_mask as used
312789Sahrens  * by nfsace, assuming aclent_t -> nfsace semantics.
313789Sahrens  */
314789Sahrens static uint32_t
315789Sahrens mode_to_ace_access(mode_t mode, int isdir, int isowner, int isallow)
316789Sahrens {
317789Sahrens 	uint32_t access = 0;
318789Sahrens 	int haswriteperm = 0;
319789Sahrens 	int hasreadperm = 0;
320789Sahrens 
321789Sahrens 	if (isallow) {
322789Sahrens 		haswriteperm = (mode & 02);
323789Sahrens 		hasreadperm = (mode & 04);
324789Sahrens 	} else {
325789Sahrens 		haswriteperm = !(mode & 02);
326789Sahrens 		hasreadperm = !(mode & 04);
327789Sahrens 	}
328789Sahrens 
329789Sahrens 	/*
330789Sahrens 	 * The following call takes care of correctly setting the following
331789Sahrens 	 * mask bits in the access_mask:
332789Sahrens 	 * ACE_SYNCHRONIZE, ACE_WRITE_OWNER, ACE_DELETE,
333789Sahrens 	 * ACE_WRITE_ATTRIBUTES, ACE_WRITE_NAMED_ATTRS, ACE_READ_NAMED_ATTRS
334789Sahrens 	 */
335789Sahrens 	access = access_mask_set(haswriteperm, hasreadperm, isowner, isallow);
336789Sahrens 
337789Sahrens 	if (isallow) {
338789Sahrens 		access |= ACE_READ_ACL | ACE_READ_ATTRIBUTES;
339789Sahrens 		if (isowner)
340789Sahrens 			access |= ACE_WRITE_ACL;
341789Sahrens 	} else {
342789Sahrens 		if (! isowner)
343789Sahrens 			access |= ACE_WRITE_ACL;
344789Sahrens 	}
345789Sahrens 
346789Sahrens 	/* read */
347789Sahrens 	if (mode & 04) {
348789Sahrens 		access |= ACE_READ_DATA;
349789Sahrens 	}
350789Sahrens 	/* write */
351789Sahrens 	if (mode & 02) {
352789Sahrens 		access |= ACE_WRITE_DATA |
353789Sahrens 		    ACE_APPEND_DATA;
354789Sahrens 		if (isdir)
355789Sahrens 			access |= ACE_DELETE_CHILD;
356789Sahrens 	}
357789Sahrens 	/* exec */
358789Sahrens 	if (mode & 01) {
359789Sahrens 		access |= ACE_EXECUTE;
360789Sahrens 	}
361789Sahrens 
362789Sahrens 	return (access);
363789Sahrens }
364789Sahrens 
365789Sahrens /*
366789Sahrens  * Given an nfsace (presumably an ALLOW entry), make a
367789Sahrens  * corresponding DENY entry at the address given.
368789Sahrens  */
369789Sahrens static void
370789Sahrens ace_make_deny(ace_t *allow, ace_t *deny, int isdir, int isowner)
371789Sahrens {
372789Sahrens 	(void) memcpy(deny, allow, sizeof (ace_t));
373789Sahrens 
374789Sahrens 	deny->a_who = allow->a_who;
375789Sahrens 
376789Sahrens 	deny->a_type = ACE_ACCESS_DENIED_ACE_TYPE;
377789Sahrens 	deny->a_access_mask ^= ACE_POSIX_SUPPORTED_BITS;
378789Sahrens 	if (isdir)
379789Sahrens 		deny->a_access_mask ^= ACE_DELETE_CHILD;
380789Sahrens 
381789Sahrens 	deny->a_access_mask &= ~(ACE_SYNCHRONIZE | ACE_WRITE_OWNER |
382789Sahrens 	    ACE_DELETE | ACE_WRITE_ATTRIBUTES | ACE_READ_NAMED_ATTRS |
383789Sahrens 	    ACE_WRITE_NAMED_ATTRS);
384789Sahrens 	deny->a_access_mask |= access_mask_set((allow->a_access_mask &
385789Sahrens 	    ACE_WRITE_DATA), (allow->a_access_mask & ACE_READ_DATA), isowner,
386789Sahrens 	    B_FALSE);
387789Sahrens }
388789Sahrens /*
389789Sahrens  * Make an initial pass over an array of aclent_t's.  Gather
390789Sahrens  * information such as an ACL_MASK (if any), number of users,
391789Sahrens  * number of groups, and whether the array needs to be sorted.
392789Sahrens  */
393789Sahrens static int
394789Sahrens ln_aent_preprocess(aclent_t *aclent, int n,
395789Sahrens     int *hasmask, mode_t *mask,
396789Sahrens     int *numuser, int *numgroup, int *needsort)
397789Sahrens {
398789Sahrens 	int error = 0;
399789Sahrens 	int i;
400789Sahrens 	int curtype = 0;
401789Sahrens 
402789Sahrens 	*hasmask = 0;
403789Sahrens 	*mask = 07;
404789Sahrens 	*needsort = 0;
405789Sahrens 	*numuser = 0;
406789Sahrens 	*numgroup = 0;
407789Sahrens 
408789Sahrens 	for (i = 0; i < n; i++) {
409789Sahrens 		if (aclent[i].a_type < curtype)
410789Sahrens 			*needsort = 1;
411789Sahrens 		else if (aclent[i].a_type > curtype)
412789Sahrens 			curtype = aclent[i].a_type;
413789Sahrens 		if (aclent[i].a_type & USER)
414789Sahrens 			(*numuser)++;
415789Sahrens 		if (aclent[i].a_type & (GROUP | GROUP_OBJ))
416789Sahrens 			(*numgroup)++;
417789Sahrens 		if (aclent[i].a_type & CLASS_OBJ) {
418789Sahrens 			if (*hasmask) {
419789Sahrens 				error = EINVAL;
420789Sahrens 				goto out;
421789Sahrens 			} else {
422789Sahrens 				*hasmask = 1;
423789Sahrens 				*mask = aclent[i].a_perm;
424789Sahrens 			}
425789Sahrens 		}
426789Sahrens 	}
427789Sahrens 
428789Sahrens 	if ((! *hasmask) && (*numuser + *numgroup > 1)) {
429789Sahrens 		error = EINVAL;
430789Sahrens 		goto out;
431789Sahrens 	}
432789Sahrens 
433789Sahrens out:
434789Sahrens 	return (error);
435789Sahrens }
436789Sahrens 
437789Sahrens /*
438789Sahrens  * Convert an array of aclent_t into an array of nfsace entries,
439789Sahrens  * following POSIX draft -> nfsv4 conversion semantics as outlined in
440789Sahrens  * the IETF draft.
441789Sahrens  */
442789Sahrens static int
443789Sahrens ln_aent_to_ace(aclent_t *aclent, int n, ace_t **acepp, int *rescount, int isdir)
444789Sahrens {
445789Sahrens 	int error = 0;
446789Sahrens 	mode_t mask;
447789Sahrens 	int numuser, numgroup, needsort;
448789Sahrens 	int resultsize = 0;
449789Sahrens 	int i, groupi = 0, skip;
450789Sahrens 	ace_t *acep, *result = NULL;
451789Sahrens 	int hasmask;
452789Sahrens 
453789Sahrens 	error = ln_aent_preprocess(aclent, n, &hasmask, &mask,
454789Sahrens 	    &numuser, &numgroup, &needsort);
455789Sahrens 	if (error != 0)
456789Sahrens 		goto out;
457789Sahrens 
458789Sahrens 	/* allow + deny for each aclent */
459789Sahrens 	resultsize = n * 2;
460789Sahrens 	if (hasmask) {
461789Sahrens 		/*
462789Sahrens 		 * stick extra deny on the group_obj and on each
463789Sahrens 		 * user|group for the mask (the group_obj was added
464789Sahrens 		 * into the count for numgroup)
465789Sahrens 		 */
466789Sahrens 		resultsize += numuser + numgroup;
467789Sahrens 		/* ... and don't count the mask itself */
468789Sahrens 		resultsize -= 2;
469789Sahrens 	}
470789Sahrens 
471789Sahrens 	/* sort the source if necessary */
472789Sahrens 	if (needsort)
473789Sahrens 		ksort((caddr_t)aclent, n, sizeof (aclent_t), cmp2acls);
474789Sahrens 
475789Sahrens 	result = acep = calloc(1, resultsize * sizeof (ace_t));
476789Sahrens 	if (result == NULL)
477789Sahrens 		goto out;
478789Sahrens 
479789Sahrens 	for (i = 0; i < n; i++) {
480789Sahrens 		/*
481789Sahrens 		 * don't process CLASS_OBJ (mask); mask was grabbed in
482789Sahrens 		 * ln_aent_preprocess()
483789Sahrens 		 */
484789Sahrens 		if (aclent[i].a_type & CLASS_OBJ)
485789Sahrens 			continue;
486789Sahrens 
487789Sahrens 		/* If we need an ACL_MASK emulator, prepend it now */
488789Sahrens 		if ((hasmask) &&
489789Sahrens 		    (aclent[i].a_type & (USER | GROUP | GROUP_OBJ))) {
490789Sahrens 			acep->a_type = ACE_ACCESS_DENIED_ACE_TYPE;
491789Sahrens 			acep->a_flags = 0;
492789Sahrens 			if (aclent[i].a_type & GROUP_OBJ) {
493789Sahrens 				acep->a_who = -1;
494789Sahrens 				acep->a_flags |=
495789Sahrens 				    (ACE_IDENTIFIER_GROUP|ACE_GROUP);
496789Sahrens 			} else if (aclent[i].a_type & USER) {
497789Sahrens 				acep->a_who = aclent[i].a_id;
498789Sahrens 			} else {
499789Sahrens 				acep->a_who = aclent[i].a_id;
500789Sahrens 				acep->a_flags |= ACE_IDENTIFIER_GROUP;
501789Sahrens 			}
502789Sahrens 			if (aclent[i].a_type & ACL_DEFAULT) {
503789Sahrens 				acep->a_flags |= ACE_INHERIT_ONLY_ACE |
504789Sahrens 				    ACE_FILE_INHERIT_ACE |
505789Sahrens 				    ACE_DIRECTORY_INHERIT_ACE;
506789Sahrens 			}
507789Sahrens 			/*
508789Sahrens 			 * Set the access mask for the prepended deny
509789Sahrens 			 * ace.  To do this, we invert the mask (found
510789Sahrens 			 * in ln_aent_preprocess()) then convert it to an
511789Sahrens 			 * DENY ace access_mask.
512789Sahrens 			 */
513789Sahrens 			acep->a_access_mask = mode_to_ace_access((mask ^ 07),
514789Sahrens 			    isdir, 0, 0);
515789Sahrens 			acep += 1;
516789Sahrens 		}
517789Sahrens 
518789Sahrens 		/* handle a_perm -> access_mask */
519789Sahrens 		acep->a_access_mask = mode_to_ace_access(aclent[i].a_perm,
520789Sahrens 		    isdir, aclent[i].a_type & USER_OBJ, 1);
521789Sahrens 
522789Sahrens 		/* emulate a default aclent */
523789Sahrens 		if (aclent[i].a_type & ACL_DEFAULT) {
524789Sahrens 			acep->a_flags |= ACE_INHERIT_ONLY_ACE |
525789Sahrens 			    ACE_FILE_INHERIT_ACE |
526789Sahrens 			    ACE_DIRECTORY_INHERIT_ACE;
527789Sahrens 		}
528789Sahrens 
529789Sahrens 		/*
530789Sahrens 		 * handle a_perm and a_id
531789Sahrens 		 *
532789Sahrens 		 * this must be done last, since it involves the
533789Sahrens 		 * corresponding deny aces, which are handled
534789Sahrens 		 * differently for each different a_type.
535789Sahrens 		 */
536789Sahrens 		if (aclent[i].a_type & USER_OBJ) {
537789Sahrens 			acep->a_who = -1;
538789Sahrens 			acep->a_flags |= ACE_OWNER;
539789Sahrens 			ace_make_deny(acep, acep + 1, isdir, B_TRUE);
540789Sahrens 			acep += 2;
541789Sahrens 		} else if (aclent[i].a_type & USER) {
542789Sahrens 			acep->a_who = aclent[i].a_id;
543789Sahrens 			ace_make_deny(acep, acep + 1, isdir, B_FALSE);
544789Sahrens 			acep += 2;
545789Sahrens 		} else if (aclent[i].a_type & (GROUP_OBJ | GROUP)) {
546789Sahrens 			if (aclent[i].a_type & GROUP_OBJ) {
547789Sahrens 				acep->a_who = -1;
548789Sahrens 				acep->a_flags |= ACE_GROUP;
549789Sahrens 			} else {
550789Sahrens 				acep->a_who = aclent[i].a_id;
551789Sahrens 			}
552789Sahrens 			acep->a_flags |= ACE_IDENTIFIER_GROUP;
553789Sahrens 			/*
554789Sahrens 			 * Set the corresponding deny for the group ace.
555789Sahrens 			 *
556789Sahrens 			 * The deny aces go after all of the groups, unlike
557789Sahrens 			 * everything else, where they immediately follow
558789Sahrens 			 * the allow ace.
559789Sahrens 			 *
560789Sahrens 			 * We calculate "skip", the number of slots to
561789Sahrens 			 * skip ahead for the deny ace, here.
562789Sahrens 			 *
563789Sahrens 			 * The pattern is:
564789Sahrens 			 * MD1 A1 MD2 A2 MD3 A3 D1 D2 D3
565789Sahrens 			 * thus, skip is
566789Sahrens 			 * (2 * numgroup) - 1 - groupi
567789Sahrens 			 * (2 * numgroup) to account for MD + A
568789Sahrens 			 * - 1 to account for the fact that we're on the
569789Sahrens 			 * access (A), not the mask (MD)
570789Sahrens 			 * - groupi to account for the fact that we have
571789Sahrens 			 * passed up groupi number of MD's.
572789Sahrens 			 */
573789Sahrens 			skip = (2 * numgroup) - 1 - groupi;
574789Sahrens 			ace_make_deny(acep, acep + skip, isdir, B_FALSE);
575789Sahrens 			/*
576789Sahrens 			 * If we just did the last group, skip acep past
577789Sahrens 			 * all of the denies; else, just move ahead one.
578789Sahrens 			 */
579789Sahrens 			if (++groupi >= numgroup)
580789Sahrens 				acep += numgroup + 1;
581789Sahrens 			else
582789Sahrens 				acep += 1;
583789Sahrens 		} else if (aclent[i].a_type & OTHER_OBJ) {
584789Sahrens 			acep->a_who = -1;
585789Sahrens 			acep->a_flags |= ACE_EVERYONE;
586789Sahrens 			ace_make_deny(acep, acep + 1, isdir, B_FALSE);
587789Sahrens 			acep += 2;
588789Sahrens 		} else {
589789Sahrens 			error = EINVAL;
590789Sahrens 			goto out;
591789Sahrens 		}
592789Sahrens 	}
593789Sahrens 
594789Sahrens 	*acepp = result;
595789Sahrens 	*rescount = resultsize;
596789Sahrens 
597789Sahrens out:
598789Sahrens 	if (error != 0) {
599789Sahrens 		if ((result != NULL) && (resultsize > 0)) {
600789Sahrens 			free(result);
601789Sahrens 		}
602789Sahrens 	}
603789Sahrens 
604789Sahrens 	return (error);
605789Sahrens }
606789Sahrens 
607789Sahrens static int
608789Sahrens convert_aent_to_ace(aclent_t *aclentp, int aclcnt, int isdir,
609789Sahrens     ace_t **retacep, int *retacecnt)
610789Sahrens {
611789Sahrens 	ace_t *acep;
612789Sahrens 	ace_t *dfacep;
613789Sahrens 	int acecnt = 0;
614789Sahrens 	int dfacecnt = 0;
615789Sahrens 	int dfaclstart = 0;
616789Sahrens 	int dfaclcnt = 0;
617789Sahrens 	aclent_t *aclp;
618789Sahrens 	int i;
619789Sahrens 	int error;
620789Sahrens 
621789Sahrens 	ksort((caddr_t)aclentp, aclcnt, sizeof (aclent_t), cmp2acls);
622789Sahrens 
623789Sahrens 	for (i = 0, aclp = aclentp; i < aclcnt; aclp++, i++) {
624789Sahrens 		if (aclp->a_type & ACL_DEFAULT)
625789Sahrens 			break;
626789Sahrens 	}
627789Sahrens 
628789Sahrens 	if (i < aclcnt) {
629*1462Smarks 		dfaclstart = i;
630*1462Smarks 		dfaclcnt = aclcnt - i;
631789Sahrens 	}
632789Sahrens 
633789Sahrens 	if (dfaclcnt && isdir == 0) {
634789Sahrens 		return (-1);
635789Sahrens 	}
636789Sahrens 
637789Sahrens 	error = ln_aent_to_ace(aclentp, i,  &acep, &acecnt, isdir);
638789Sahrens 	if (error)
639789Sahrens 		return (-1);
640789Sahrens 
641789Sahrens 	if (dfaclcnt) {
642789Sahrens 		error = ln_aent_to_ace(&aclentp[dfaclstart], dfaclcnt,
643789Sahrens 		    &dfacep, &dfacecnt, isdir);
644789Sahrens 		if (error) {
645789Sahrens 			if (acep) {
646789Sahrens 				free(acep);
647789Sahrens 			}
648789Sahrens 			return (-1);
649789Sahrens 		}
650789Sahrens 	}
651789Sahrens 
652*1462Smarks 	if (dfacecnt != 0) {
653*1462Smarks 		acep = realloc(acep, sizeof (ace_t) * (acecnt + dfacecnt));
654*1462Smarks 		if (acep == NULL)
655*1462Smarks 			return (-1);
656*1462Smarks 		if (dfaclcnt) {
657*1462Smarks 			(void) memcpy(acep + acecnt, dfacep,
658*1462Smarks 			    sizeof (ace_t) * dfacecnt);
659*1462Smarks 		}
660789Sahrens 	}
661789Sahrens 	if (dfaclcnt)
662789Sahrens 		free(dfacep);
663789Sahrens 
664789Sahrens 	*retacecnt = acecnt + dfacecnt;
665*1462Smarks 	*retacep = acep;
666*1462Smarks 	return (0);
667*1462Smarks }
668*1462Smarks 
669*1462Smarks static void
670*1462Smarks acevals_init(acevals_t *vals, uid_t key)
671*1462Smarks {
672*1462Smarks 	bzero(vals, sizeof (*vals));
673*1462Smarks 	vals->allowed = ACE_MASK_UNDEFINED;
674*1462Smarks 	vals->denied = ACE_MASK_UNDEFINED;
675*1462Smarks 	vals->mask = ACE_MASK_UNDEFINED;
676*1462Smarks 	vals->key = key;
677*1462Smarks }
678*1462Smarks 
679*1462Smarks static void
680*1462Smarks ace_list_init(ace_list_t *al, int dfacl_flag)
681*1462Smarks {
682*1462Smarks 	acevals_init(&al->user_obj, NULL);
683*1462Smarks 	acevals_init(&al->group_obj, NULL);
684*1462Smarks 	acevals_init(&al->other_obj, NULL);
685*1462Smarks 	al->numusers = 0;
686*1462Smarks 	al->numgroups = 0;
687*1462Smarks 	al->acl_mask = 0;
688*1462Smarks 	al->hasmask = 0;
689*1462Smarks 	al->state = ace_unused;
690*1462Smarks 	al->seen = 0;
691*1462Smarks 	al->dfacl_flag = dfacl_flag;
692*1462Smarks }
693*1462Smarks 
694*1462Smarks /*
695*1462Smarks  * Find or create an acevals holder for a given id and avl tree.
696*1462Smarks  *
697*1462Smarks  * Note that only one thread will ever touch these avl trees, so
698*1462Smarks  * there is no need for locking.
699*1462Smarks  */
700*1462Smarks static acevals_t *
701*1462Smarks acevals_find(ace_t *ace, avl_tree_t *avl, int *num)
702*1462Smarks {
703*1462Smarks 	acevals_t key, *rc;
704*1462Smarks 	avl_index_t where;
705*1462Smarks 
706*1462Smarks 	key.key = ace->a_who;
707*1462Smarks 	rc = avl_find(avl, &key, &where);
708*1462Smarks 	if (rc != NULL)
709*1462Smarks 		return (rc);
710*1462Smarks 
711*1462Smarks 	/* this memory is freed by ln_ace_to_aent()->ace_list_free() */
712*1462Smarks 	rc = calloc(1, sizeof (acevals_t));
713*1462Smarks 	if (rc == NULL)
714*1462Smarks 		return (rc);
715*1462Smarks 	acevals_init(rc, ace->a_who);
716*1462Smarks 	avl_insert(avl, rc, where);
717*1462Smarks 	(*num)++;
718*1462Smarks 
719*1462Smarks 	return (rc);
720*1462Smarks }
721*1462Smarks 
722*1462Smarks static int
723*1462Smarks access_mask_check(ace_t *acep, int mask_bit, int isowner)
724*1462Smarks {
725*1462Smarks 	int set_deny, err_deny;
726*1462Smarks 	int set_allow, err_allow;
727*1462Smarks 	int acl_consume;
728*1462Smarks 	int haswriteperm, hasreadperm;
729*1462Smarks 
730*1462Smarks 	if (acep->a_type == ACE_ACCESS_DENIED_ACE_TYPE) {
731*1462Smarks 		haswriteperm = (acep->a_access_mask & ACE_WRITE_DATA) ? 0 : 1;
732*1462Smarks 		hasreadperm = (acep->a_access_mask & ACE_READ_DATA) ? 0 : 1;
733*1462Smarks 	} else {
734*1462Smarks 		haswriteperm = (acep->a_access_mask & ACE_WRITE_DATA) ? 1 : 0;
735*1462Smarks 		hasreadperm = (acep->a_access_mask & ACE_READ_DATA) ? 1 : 0;
736*1462Smarks 	}
737*1462Smarks 
738*1462Smarks 	acl_consume = (ACL_SYNCHRONIZE_ERR_DENY |
739*1462Smarks 	    ACL_DELETE_ERR_DENY |
740*1462Smarks 	    ACL_WRITE_OWNER_ERR_DENY |
741*1462Smarks 	    ACL_WRITE_OWNER_ERR_ALLOW |
742*1462Smarks 	    ACL_WRITE_ATTRS_OWNER_SET_ALLOW |
743*1462Smarks 	    ACL_WRITE_ATTRS_OWNER_ERR_DENY |
744*1462Smarks 	    ACL_WRITE_ATTRS_WRITER_SET_DENY |
745*1462Smarks 	    ACL_WRITE_ATTRS_WRITER_ERR_ALLOW |
746*1462Smarks 	    ACL_WRITE_NAMED_WRITER_ERR_DENY |
747*1462Smarks 	    ACL_READ_NAMED_READER_ERR_DENY);
748*1462Smarks 
749*1462Smarks 	if (mask_bit == ACE_SYNCHRONIZE) {
750*1462Smarks 		set_deny = ACL_SYNCHRONIZE_SET_DENY;
751*1462Smarks 		err_deny =  ACL_SYNCHRONIZE_ERR_DENY;
752*1462Smarks 		set_allow = ACL_SYNCHRONIZE_SET_ALLOW;
753*1462Smarks 		err_allow = ACL_SYNCHRONIZE_ERR_ALLOW;
754*1462Smarks 	} else if (mask_bit == ACE_WRITE_OWNER) {
755*1462Smarks 		set_deny = ACL_WRITE_OWNER_SET_DENY;
756*1462Smarks 		err_deny =  ACL_WRITE_OWNER_ERR_DENY;
757*1462Smarks 		set_allow = ACL_WRITE_OWNER_SET_ALLOW;
758*1462Smarks 		err_allow = ACL_WRITE_OWNER_ERR_ALLOW;
759*1462Smarks 	} else if (mask_bit == ACE_DELETE) {
760*1462Smarks 		set_deny = ACL_DELETE_SET_DENY;
761*1462Smarks 		err_deny =  ACL_DELETE_ERR_DENY;
762*1462Smarks 		set_allow = ACL_DELETE_SET_ALLOW;
763*1462Smarks 		err_allow = ACL_DELETE_ERR_ALLOW;
764*1462Smarks 	} else if (mask_bit == ACE_WRITE_ATTRIBUTES) {
765*1462Smarks 		if (isowner) {
766*1462Smarks 			set_deny = ACL_WRITE_ATTRS_OWNER_SET_DENY;
767*1462Smarks 			err_deny =  ACL_WRITE_ATTRS_OWNER_ERR_DENY;
768*1462Smarks 			set_allow = ACL_WRITE_ATTRS_OWNER_SET_ALLOW;
769*1462Smarks 			err_allow = ACL_WRITE_ATTRS_OWNER_ERR_ALLOW;
770*1462Smarks 		} else if (haswriteperm) {
771*1462Smarks 			set_deny = ACL_WRITE_ATTRS_WRITER_SET_DENY;
772*1462Smarks 			err_deny =  ACL_WRITE_ATTRS_WRITER_ERR_DENY;
773*1462Smarks 			set_allow = ACL_WRITE_ATTRS_WRITER_SET_ALLOW;
774*1462Smarks 			err_allow = ACL_WRITE_ATTRS_WRITER_ERR_ALLOW;
775*1462Smarks 		} else {
776*1462Smarks 			if ((acep->a_access_mask & mask_bit) &&
777*1462Smarks 			    (acep->a_type & ACE_ACCESS_ALLOWED_ACE_TYPE)) {
778*1462Smarks 				return (ENOTSUP);
779*1462Smarks 			}
780*1462Smarks 			return (0);
781*1462Smarks 		}
782*1462Smarks 	} else if (mask_bit == ACE_READ_NAMED_ATTRS) {
783*1462Smarks 		if (!hasreadperm)
784*1462Smarks 			return (0);
785*1462Smarks 
786*1462Smarks 		set_deny = ACL_READ_NAMED_READER_SET_DENY;
787*1462Smarks 		err_deny = ACL_READ_NAMED_READER_ERR_DENY;
788*1462Smarks 		set_allow = ACL_READ_NAMED_READER_SET_ALLOW;
789*1462Smarks 		err_allow = ACL_READ_NAMED_READER_ERR_ALLOW;
790*1462Smarks 	} else if (mask_bit == ACE_WRITE_NAMED_ATTRS) {
791*1462Smarks 		if (!haswriteperm)
792*1462Smarks 			return (0);
793*1462Smarks 
794*1462Smarks 		set_deny = ACL_WRITE_NAMED_WRITER_SET_DENY;
795*1462Smarks 		err_deny = ACL_WRITE_NAMED_WRITER_ERR_DENY;
796*1462Smarks 		set_allow = ACL_WRITE_NAMED_WRITER_SET_ALLOW;
797*1462Smarks 		err_allow = ACL_WRITE_NAMED_WRITER_ERR_ALLOW;
798*1462Smarks 	} else {
799*1462Smarks 		return (EINVAL);
800*1462Smarks 	}
801*1462Smarks 
802*1462Smarks 	if (acep->a_type == ACE_ACCESS_DENIED_ACE_TYPE) {
803*1462Smarks 		if (acl_consume & set_deny) {
804*1462Smarks 			if (!(acep->a_access_mask & mask_bit)) {
805*1462Smarks 				return (ENOTSUP);
806*1462Smarks 			}
807*1462Smarks 		} else if (acl_consume & err_deny) {
808*1462Smarks 			if (acep->a_access_mask & mask_bit) {
809*1462Smarks 				return (ENOTSUP);
810*1462Smarks 			}
811*1462Smarks 		}
812*1462Smarks 	} else {
813*1462Smarks 		/* ACE_ACCESS_ALLOWED_ACE_TYPE */
814*1462Smarks 		if (acl_consume & set_allow) {
815*1462Smarks 			if (!(acep->a_access_mask & mask_bit)) {
816*1462Smarks 				return (ENOTSUP);
817*1462Smarks 			}
818*1462Smarks 		} else if (acl_consume & err_allow) {
819*1462Smarks 			if (acep->a_access_mask & mask_bit) {
820*1462Smarks 				return (ENOTSUP);
821*1462Smarks 			}
822*1462Smarks 		}
823*1462Smarks 	}
824789Sahrens 	return (0);
825789Sahrens }
826789Sahrens 
827*1462Smarks static int
828*1462Smarks ace_to_aent_legal(ace_t *acep)
829*1462Smarks {
830*1462Smarks 	int error = 0;
831*1462Smarks 	int isowner;
832*1462Smarks 
833*1462Smarks 	/* only ALLOW or DENY */
834*1462Smarks 	if ((acep->a_type != ACE_ACCESS_ALLOWED_ACE_TYPE) &&
835*1462Smarks 	    (acep->a_type != ACE_ACCESS_DENIED_ACE_TYPE)) {
836*1462Smarks 		error = ENOTSUP;
837*1462Smarks 		goto out;
838*1462Smarks 	}
839*1462Smarks 
840*1462Smarks 	/* check for invalid flags */
841*1462Smarks 	if (acep->a_flags & ~(ACE_VALID_FLAG_BITS)) {
842*1462Smarks 		error = EINVAL;
843*1462Smarks 		goto out;
844*1462Smarks 	}
845*1462Smarks 
846*1462Smarks 	/* some flags are illegal */
847*1462Smarks 	if (acep->a_flags & (ACE_SUCCESSFUL_ACCESS_ACE_FLAG |
848*1462Smarks 	    ACE_FAILED_ACCESS_ACE_FLAG |
849*1462Smarks 	    ACE_NO_PROPAGATE_INHERIT_ACE)) {
850*1462Smarks 		error = ENOTSUP;
851*1462Smarks 		goto out;
852*1462Smarks 	}
853*1462Smarks 
854*1462Smarks 	/* check for invalid masks */
855*1462Smarks 	if (acep->a_access_mask & ~(ACE_VALID_MASK_BITS)) {
856*1462Smarks 		error = EINVAL;
857*1462Smarks 		goto out;
858*1462Smarks 	}
859*1462Smarks 
860*1462Smarks 	if ((acep->a_flags & ACE_OWNER)) {
861*1462Smarks 		isowner = 1;
862*1462Smarks 	} else {
863*1462Smarks 		isowner = 0;
864*1462Smarks 	}
865*1462Smarks 
866*1462Smarks 	error = access_mask_check(acep, ACE_SYNCHRONIZE, isowner);
867*1462Smarks 	if (error)
868*1462Smarks 		goto out;
869*1462Smarks 
870*1462Smarks 	error = access_mask_check(acep, ACE_WRITE_OWNER, isowner);
871*1462Smarks 	if (error)
872*1462Smarks 		goto out;
873*1462Smarks 
874*1462Smarks 	error = access_mask_check(acep, ACE_DELETE, isowner);
875*1462Smarks 	if (error)
876*1462Smarks 		goto out;
877*1462Smarks 
878*1462Smarks 	error = access_mask_check(acep, ACE_WRITE_ATTRIBUTES, isowner);
879*1462Smarks 	if (error)
880*1462Smarks 		goto out;
881*1462Smarks 
882*1462Smarks 	error = access_mask_check(acep, ACE_READ_NAMED_ATTRS, isowner);
883*1462Smarks 	if (error)
884*1462Smarks 		goto out;
885*1462Smarks 
886*1462Smarks 	error = access_mask_check(acep, ACE_WRITE_NAMED_ATTRS, isowner);
887*1462Smarks 	if (error)
888*1462Smarks 		goto out;
889*1462Smarks 
890*1462Smarks 	/* more detailed checking of masks */
891*1462Smarks 	if (acep->a_type == ACE_ACCESS_ALLOWED_ACE_TYPE) {
892*1462Smarks 		if (! (acep->a_access_mask & ACE_READ_ATTRIBUTES)) {
893*1462Smarks 			error = ENOTSUP;
894*1462Smarks 			goto out;
895*1462Smarks 		}
896*1462Smarks 		if ((acep->a_access_mask & ACE_WRITE_DATA) &&
897*1462Smarks 		    (! (acep->a_access_mask & ACE_APPEND_DATA))) {
898*1462Smarks 			error = ENOTSUP;
899*1462Smarks 			goto out;
900*1462Smarks 		}
901*1462Smarks 		if ((! (acep->a_access_mask & ACE_WRITE_DATA)) &&
902*1462Smarks 		    (acep->a_access_mask & ACE_APPEND_DATA)) {
903*1462Smarks 			error = ENOTSUP;
904*1462Smarks 			goto out;
905*1462Smarks 		}
906*1462Smarks 	}
907*1462Smarks 
908*1462Smarks 	/* ACL enforcement */
909*1462Smarks 	if ((acep->a_access_mask & ACE_READ_ACL) &&
910*1462Smarks 	    (acep->a_type != ACE_ACCESS_ALLOWED_ACE_TYPE)) {
911*1462Smarks 		error = ENOTSUP;
912*1462Smarks 		goto out;
913*1462Smarks 	}
914*1462Smarks 	if (acep->a_access_mask & ACE_WRITE_ACL) {
915*1462Smarks 		if ((acep->a_type == ACE_ACCESS_DENIED_ACE_TYPE) &&
916*1462Smarks 		    (isowner)) {
917*1462Smarks 			error = ENOTSUP;
918*1462Smarks 			goto out;
919*1462Smarks 		}
920*1462Smarks 		if ((acep->a_type == ACE_ACCESS_ALLOWED_ACE_TYPE) &&
921*1462Smarks 		    (! isowner)) {
922*1462Smarks 			error = ENOTSUP;
923*1462Smarks 			goto out;
924*1462Smarks 		}
925*1462Smarks 	}
926*1462Smarks 
927*1462Smarks out:
928*1462Smarks 	return (error);
929*1462Smarks }
930*1462Smarks 
931*1462Smarks static int
932*1462Smarks ace_mask_to_mode(uint32_t  mask, o_mode_t *modep, int isdir)
933*1462Smarks {
934*1462Smarks 	int error = 0;
935*1462Smarks 	o_mode_t mode = 0;
936*1462Smarks 	uint32_t bits, wantbits;
937*1462Smarks 
938*1462Smarks 	/* read */
939*1462Smarks 	if (mask & ACE_READ_DATA)
940*1462Smarks 		mode |= 04;
941*1462Smarks 
942*1462Smarks 	/* write */
943*1462Smarks 	wantbits = (ACE_WRITE_DATA | ACE_APPEND_DATA);
944*1462Smarks 	if (isdir)
945*1462Smarks 		wantbits |= ACE_DELETE_CHILD;
946*1462Smarks 	bits = mask & wantbits;
947*1462Smarks 	if (bits != 0) {
948*1462Smarks 		if (bits != wantbits) {
949*1462Smarks 			error = ENOTSUP;
950*1462Smarks 			goto out;
951*1462Smarks 		}
952*1462Smarks 		mode |= 02;
953*1462Smarks 	}
954*1462Smarks 
955*1462Smarks 	/* exec */
956*1462Smarks 	if (mask & ACE_EXECUTE) {
957*1462Smarks 		mode |= 01;
958*1462Smarks 	}
959*1462Smarks 
960*1462Smarks 	*modep = mode;
961*1462Smarks 
962*1462Smarks out:
963*1462Smarks 	return (error);
964*1462Smarks }
965*1462Smarks 
966*1462Smarks static int
967*1462Smarks ace_allow_to_mode(uint32_t mask, o_mode_t *modep, int isdir)
968*1462Smarks {
969*1462Smarks 	/* ACE_READ_ACL and ACE_READ_ATTRIBUTES must both be set */
970*1462Smarks 	if ((mask & (ACE_READ_ACL | ACE_READ_ATTRIBUTES)) !=
971*1462Smarks 	    (ACE_READ_ACL | ACE_READ_ATTRIBUTES)) {
972*1462Smarks 		return (ENOTSUP);
973*1462Smarks 	}
974*1462Smarks 
975*1462Smarks 	return (ace_mask_to_mode(mask, modep, isdir));
976*1462Smarks }
977*1462Smarks 
978*1462Smarks static int
979*1462Smarks acevals_to_aent(acevals_t *vals, aclent_t *dest, ace_list_t *list,
980*1462Smarks     uid_t owner, gid_t group, int isdir)
981*1462Smarks {
982*1462Smarks 	int error;
983*1462Smarks 	uint32_t  flips = ACE_POSIX_SUPPORTED_BITS;
984*1462Smarks 
985*1462Smarks 	if (isdir)
986*1462Smarks 		flips |= ACE_DELETE_CHILD;
987*1462Smarks 	if (vals->allowed != (vals->denied ^ flips)) {
988*1462Smarks 		error = ENOTSUP;
989*1462Smarks 		goto out;
990*1462Smarks 	}
991*1462Smarks 	if ((list->hasmask) && (list->acl_mask != vals->mask) &&
992*1462Smarks 	    (vals->aent_type & (USER | GROUP | GROUP_OBJ))) {
993*1462Smarks 		error = ENOTSUP;
994*1462Smarks 		goto out;
995*1462Smarks 	}
996*1462Smarks 	error = ace_allow_to_mode(vals->allowed, &dest->a_perm, isdir);
997*1462Smarks 	if (error != 0)
998*1462Smarks 		goto out;
999*1462Smarks 	dest->a_type = vals->aent_type;
1000*1462Smarks 	if (dest->a_type & (USER | GROUP)) {
1001*1462Smarks 		dest->a_id = vals->key;
1002*1462Smarks 	} else if (dest->a_type & USER_OBJ) {
1003*1462Smarks 		dest->a_id = owner;
1004*1462Smarks 	} else if (dest->a_type & GROUP_OBJ) {
1005*1462Smarks 		dest->a_id = group;
1006*1462Smarks 	} else if (dest->a_type & OTHER_OBJ) {
1007*1462Smarks 		dest->a_id = 0;
1008*1462Smarks 	} else {
1009*1462Smarks 		error = EINVAL;
1010*1462Smarks 		goto out;
1011*1462Smarks 	}
1012*1462Smarks 
1013*1462Smarks out:
1014*1462Smarks 	return (error);
1015*1462Smarks }
1016789Sahrens 
1017789Sahrens static int
1018*1462Smarks ace_list_to_aent(ace_list_t *list, aclent_t **aclentp, int *aclcnt,
1019*1462Smarks     uid_t owner, gid_t group, int isdir)
1020*1462Smarks {
1021*1462Smarks 	int error = 0;
1022*1462Smarks 	aclent_t *aent, *result = NULL;
1023*1462Smarks 	acevals_t *vals;
1024*1462Smarks 	int resultcount;
1025*1462Smarks 
1026*1462Smarks 	if ((list->seen & (USER_OBJ | GROUP_OBJ | OTHER_OBJ)) !=
1027*1462Smarks 	    (USER_OBJ | GROUP_OBJ | OTHER_OBJ)) {
1028*1462Smarks 		error = ENOTSUP;
1029*1462Smarks 		goto out;
1030*1462Smarks 	}
1031*1462Smarks 	if ((! list->hasmask) && (list->numusers + list->numgroups > 0)) {
1032*1462Smarks 		error = ENOTSUP;
1033*1462Smarks 		goto out;
1034*1462Smarks 	}
1035*1462Smarks 
1036*1462Smarks 	resultcount = 3 + list->numusers + list->numgroups;
1037*1462Smarks 	/*
1038*1462Smarks 	 * This must be the same condition as below, when we add the CLASS_OBJ
1039*1462Smarks 	 * (aka ACL mask)
1040*1462Smarks 	 */
1041*1462Smarks 	if ((list->hasmask) || (! list->dfacl_flag))
1042*1462Smarks 		resultcount += 1;
1043*1462Smarks 
1044*1462Smarks 	result = aent = calloc(1, resultcount * sizeof (aclent_t));
1045*1462Smarks 
1046*1462Smarks 	if (result == NULL) {
1047*1462Smarks 		error = ENOMEM;
1048*1462Smarks 		goto out;
1049*1462Smarks 	}
1050*1462Smarks 
1051*1462Smarks 	/* USER_OBJ */
1052*1462Smarks 	if (!(list->user_obj.aent_type & USER_OBJ)) {
1053*1462Smarks 		error = EINVAL;
1054*1462Smarks 		goto out;
1055*1462Smarks 	}
1056*1462Smarks 
1057*1462Smarks 	error = acevals_to_aent(&list->user_obj, aent, list, owner, group,
1058*1462Smarks 	    isdir);
1059*1462Smarks 
1060*1462Smarks 	if (error != 0)
1061*1462Smarks 		goto out;
1062*1462Smarks 	++aent;
1063*1462Smarks 	/* USER */
1064*1462Smarks 	vals = NULL;
1065*1462Smarks 	for (vals = avl_first(&list->user); vals != NULL;
1066*1462Smarks 	    vals = AVL_NEXT(&list->user, vals)) {
1067*1462Smarks 		if (!(vals->aent_type & USER)) {
1068*1462Smarks 			error = EINVAL;
1069*1462Smarks 			goto out;
1070*1462Smarks 		}
1071*1462Smarks 		error = acevals_to_aent(vals, aent, list, owner, group,
1072*1462Smarks 		    isdir);
1073*1462Smarks 		if (error != 0)
1074*1462Smarks 			goto out;
1075*1462Smarks 		++aent;
1076*1462Smarks 	}
1077*1462Smarks 	/* GROUP_OBJ */
1078*1462Smarks 	if (!(list->group_obj.aent_type & GROUP_OBJ)) {
1079*1462Smarks 		error = EINVAL;
1080*1462Smarks 		goto out;
1081*1462Smarks 	}
1082*1462Smarks 	error = acevals_to_aent(&list->group_obj, aent, list, owner, group,
1083*1462Smarks 	    isdir);
1084*1462Smarks 	if (error != 0)
1085*1462Smarks 		goto out;
1086*1462Smarks 	++aent;
1087*1462Smarks 	/* GROUP */
1088*1462Smarks 	vals = NULL;
1089*1462Smarks 	for (vals = avl_first(&list->group); vals != NULL;
1090*1462Smarks 	    vals = AVL_NEXT(&list->group, vals)) {
1091*1462Smarks 		if (!(vals->aent_type & GROUP)) {
1092*1462Smarks 			error = EINVAL;
1093*1462Smarks 			goto out;
1094*1462Smarks 		}
1095*1462Smarks 		error = acevals_to_aent(vals, aent, list, owner, group,
1096*1462Smarks 		    isdir);
1097*1462Smarks 		if (error != 0)
1098*1462Smarks 			goto out;
1099*1462Smarks 		++aent;
1100*1462Smarks 	}
1101*1462Smarks 	/*
1102*1462Smarks 	 * CLASS_OBJ (aka ACL_MASK)
1103*1462Smarks 	 *
1104*1462Smarks 	 * An ACL_MASK is not fabricated if the ACL is a default ACL.
1105*1462Smarks 	 * This is to follow UFS's behavior.
1106*1462Smarks 	 */
1107*1462Smarks 	if ((list->hasmask) || (! list->dfacl_flag)) {
1108*1462Smarks 		if (list->hasmask) {
1109*1462Smarks 			uint32_t flips = ACE_POSIX_SUPPORTED_BITS;
1110*1462Smarks 			if (isdir)
1111*1462Smarks 				flips |= ACE_DELETE_CHILD;
1112*1462Smarks 			error = ace_mask_to_mode(list->acl_mask ^ flips,
1113*1462Smarks 			    &aent->a_perm, isdir);
1114*1462Smarks 			if (error != 0)
1115*1462Smarks 				goto out;
1116*1462Smarks 		} else {
1117*1462Smarks 			/* fabricate the ACL_MASK from the group permissions */
1118*1462Smarks 			error = ace_mask_to_mode(list->group_obj.allowed,
1119*1462Smarks 			    &aent->a_perm, isdir);
1120*1462Smarks 			if (error != 0)
1121*1462Smarks 				goto out;
1122*1462Smarks 		}
1123*1462Smarks 		aent->a_id = 0;
1124*1462Smarks 		aent->a_type = CLASS_OBJ | list->dfacl_flag;
1125*1462Smarks 		++aent;
1126*1462Smarks 	}
1127*1462Smarks 	/* OTHER_OBJ */
1128*1462Smarks 	if (!(list->other_obj.aent_type & OTHER_OBJ)) {
1129*1462Smarks 		error = EINVAL;
1130*1462Smarks 		goto out;
1131*1462Smarks 	}
1132*1462Smarks 	error = acevals_to_aent(&list->other_obj, aent, list, owner, group,
1133*1462Smarks 	    isdir);
1134*1462Smarks 	if (error != 0)
1135*1462Smarks 		goto out;
1136*1462Smarks 	++aent;
1137*1462Smarks 
1138*1462Smarks 	*aclentp = result;
1139*1462Smarks 	*aclcnt = resultcount;
1140*1462Smarks 
1141*1462Smarks out:
1142*1462Smarks 	if (error != 0) {
1143*1462Smarks 		if (result != NULL)
1144*1462Smarks 			free(result);
1145*1462Smarks 	}
1146*1462Smarks 
1147*1462Smarks 	return (error);
1148*1462Smarks }
1149*1462Smarks 
1150*1462Smarks /*
1151*1462Smarks  * free all data associated with an ace_list
1152*1462Smarks  */
1153*1462Smarks static void
1154*1462Smarks ace_list_free(ace_list_t *al)
1155*1462Smarks {
1156*1462Smarks 	acevals_t *node;
1157*1462Smarks 	void *cookie;
1158*1462Smarks 
1159*1462Smarks 	if (al == NULL)
1160*1462Smarks 		return;
1161*1462Smarks 
1162*1462Smarks 	cookie = NULL;
1163*1462Smarks 	while ((node = avl_destroy_nodes(&al->user, &cookie)) != NULL)
1164*1462Smarks 		free(node);
1165*1462Smarks 	cookie = NULL;
1166*1462Smarks 	while ((node = avl_destroy_nodes(&al->group, &cookie)) != NULL)
1167*1462Smarks 		free(node);
1168*1462Smarks 
1169*1462Smarks 	avl_destroy(&al->user);
1170*1462Smarks 	avl_destroy(&al->group);
1171*1462Smarks 
1172*1462Smarks 	/* free the container itself */
1173*1462Smarks 	free(al);
1174*1462Smarks }
1175*1462Smarks 
1176*1462Smarks static int
1177*1462Smarks acevals_compare(const void *va, const void *vb)
1178*1462Smarks {
1179*1462Smarks 	const acevals_t *a = va, *b = vb;
1180*1462Smarks 
1181*1462Smarks 	if (a->key == b->key)
1182*1462Smarks 		return (0);
1183*1462Smarks 
1184*1462Smarks 	if (a->key > b->key)
1185*1462Smarks 		return (1);
1186*1462Smarks 
1187*1462Smarks 	else
1188*1462Smarks 		return (-1);
1189*1462Smarks }
1190*1462Smarks 
1191*1462Smarks /*
1192*1462Smarks  * Convert a list of ace_t entries to equivalent regular and default
1193*1462Smarks  * aclent_t lists.  Return error (ENOTSUP) when conversion is not possible.
1194*1462Smarks  */
1195*1462Smarks static int
1196*1462Smarks ln_ace_to_aent(ace_t *ace, int n, uid_t owner, gid_t group,
1197*1462Smarks     aclent_t **aclentp, int *aclcnt, aclent_t **dfaclentp, int *dfaclcnt,
1198*1462Smarks     int isdir)
1199*1462Smarks {
1200*1462Smarks 	int error = 0;
1201*1462Smarks 	ace_t *acep;
1202*1462Smarks 	uint32_t bits;
1203*1462Smarks 	int i;
1204*1462Smarks 	ace_list_t *normacl = NULL, *dfacl = NULL, *acl;
1205*1462Smarks 	acevals_t *vals;
1206*1462Smarks 
1207*1462Smarks 	*aclentp = NULL;
1208*1462Smarks 	*aclcnt = 0;
1209*1462Smarks 	*dfaclentp = NULL;
1210*1462Smarks 	*dfaclcnt = 0;
1211*1462Smarks 
1212*1462Smarks 	/* we need at least user_obj, group_obj, and other_obj */
1213*1462Smarks 	if (n < 6) {
1214*1462Smarks 		error = ENOTSUP;
1215*1462Smarks 		goto out;
1216*1462Smarks 	}
1217*1462Smarks 	if (ace == NULL) {
1218*1462Smarks 		error = EINVAL;
1219*1462Smarks 		goto out;
1220*1462Smarks 	}
1221*1462Smarks 
1222*1462Smarks 	normacl = calloc(1, sizeof (ace_list_t));
1223*1462Smarks 
1224*1462Smarks 	if (normacl == NULL) {
1225*1462Smarks 		error = errno;
1226*1462Smarks 		goto out;
1227*1462Smarks 	}
1228*1462Smarks 
1229*1462Smarks 	avl_create(&normacl->user, acevals_compare, sizeof (acevals_t),
1230*1462Smarks 	    offsetof(acevals_t, avl));
1231*1462Smarks 	avl_create(&normacl->group, acevals_compare, sizeof (acevals_t),
1232*1462Smarks 	    offsetof(acevals_t, avl));
1233*1462Smarks 
1234*1462Smarks 	ace_list_init(normacl, 0);
1235*1462Smarks 
1236*1462Smarks 	dfacl = calloc(1, sizeof (ace_list_t));
1237*1462Smarks 	if (dfacl == NULL) {
1238*1462Smarks 		error = errno;
1239*1462Smarks 		goto out;
1240*1462Smarks 	}
1241*1462Smarks 	avl_create(&dfacl->user, acevals_compare, sizeof (acevals_t),
1242*1462Smarks 	    offsetof(acevals_t, avl));
1243*1462Smarks 	avl_create(&dfacl->group, acevals_compare, sizeof (acevals_t),
1244*1462Smarks 	    offsetof(acevals_t, avl));
1245*1462Smarks 	ace_list_init(dfacl, ACL_DEFAULT);
1246*1462Smarks 
1247*1462Smarks 	/* process every ace_t... */
1248*1462Smarks 	for (i = 0; i < n; i++) {
1249*1462Smarks 		acep = &ace[i];
1250*1462Smarks 
1251*1462Smarks 		/* rule out certain cases quickly */
1252*1462Smarks 		error = ace_to_aent_legal(acep);
1253*1462Smarks 		if (error != 0)
1254*1462Smarks 			goto out;
1255*1462Smarks 
1256*1462Smarks 		/*
1257*1462Smarks 		 * Turn off these bits in order to not have to worry about
1258*1462Smarks 		 * them when doing the checks for compliments.
1259*1462Smarks 		 */
1260*1462Smarks 		acep->a_access_mask &= ~(ACE_WRITE_OWNER | ACE_DELETE |
1261*1462Smarks 		    ACE_SYNCHRONIZE | ACE_WRITE_ATTRIBUTES |
1262*1462Smarks 		    ACE_READ_NAMED_ATTRS | ACE_WRITE_NAMED_ATTRS);
1263*1462Smarks 
1264*1462Smarks 		/* see if this should be a regular or default acl */
1265*1462Smarks 		bits = acep->a_flags &
1266*1462Smarks 		    (ACE_INHERIT_ONLY_ACE |
1267*1462Smarks 		    ACE_FILE_INHERIT_ACE |
1268*1462Smarks 		    ACE_DIRECTORY_INHERIT_ACE);
1269*1462Smarks 		if (bits != 0) {
1270*1462Smarks 			/* all or nothing on these inherit bits */
1271*1462Smarks 			if (bits != (ACE_INHERIT_ONLY_ACE |
1272*1462Smarks 			    ACE_FILE_INHERIT_ACE |
1273*1462Smarks 			    ACE_DIRECTORY_INHERIT_ACE)) {
1274*1462Smarks 				error = ENOTSUP;
1275*1462Smarks 				goto out;
1276*1462Smarks 			}
1277*1462Smarks 			acl = dfacl;
1278*1462Smarks 		} else {
1279*1462Smarks 			acl = normacl;
1280*1462Smarks 		}
1281*1462Smarks 
1282*1462Smarks 		if ((acep->a_flags & ACE_OWNER)) {
1283*1462Smarks 			if (acl->state > ace_user_obj) {
1284*1462Smarks 				error = ENOTSUP;
1285*1462Smarks 				goto out;
1286*1462Smarks 			}
1287*1462Smarks 			acl->state = ace_user_obj;
1288*1462Smarks 			acl->seen |= USER_OBJ;
1289*1462Smarks 			vals = &acl->user_obj;
1290*1462Smarks 			vals->aent_type = USER_OBJ | acl->dfacl_flag;
1291*1462Smarks 		} else if ((acep->a_flags & ACE_EVERYONE)) {
1292*1462Smarks 			acl->state = ace_other_obj;
1293*1462Smarks 			acl->seen |= OTHER_OBJ;
1294*1462Smarks 			vals = &acl->other_obj;
1295*1462Smarks 			vals->aent_type = OTHER_OBJ | acl->dfacl_flag;
1296*1462Smarks 		} else if (acep->a_flags & ACE_IDENTIFIER_GROUP) {
1297*1462Smarks 			if (acl->state > ace_group) {
1298*1462Smarks 				error = ENOTSUP;
1299*1462Smarks 				goto out;
1300*1462Smarks 			}
1301*1462Smarks 			if ((acep->a_flags & ACE_GROUP)) {
1302*1462Smarks 				acl->seen |= GROUP_OBJ;
1303*1462Smarks 				vals = &acl->group_obj;
1304*1462Smarks 				vals->aent_type = GROUP_OBJ | acl->dfacl_flag;
1305*1462Smarks 			} else {
1306*1462Smarks 				acl->seen |= GROUP;
1307*1462Smarks 				vals = acevals_find(acep, &acl->group,
1308*1462Smarks 				    &acl->numgroups);
1309*1462Smarks 				if (vals == NULL) {
1310*1462Smarks 					error = ENOMEM;
1311*1462Smarks 					goto out;
1312*1462Smarks 				}
1313*1462Smarks 				vals->aent_type = GROUP | acl->dfacl_flag;
1314*1462Smarks 			}
1315*1462Smarks 			acl->state = ace_group;
1316*1462Smarks 		} else {
1317*1462Smarks 			if (acl->state > ace_user) {
1318*1462Smarks 				error = ENOTSUP;
1319*1462Smarks 				goto out;
1320*1462Smarks 			}
1321*1462Smarks 			acl->state = ace_user;
1322*1462Smarks 			acl->seen |= USER;
1323*1462Smarks 			vals = acevals_find(acep, &acl->user,
1324*1462Smarks 			    &acl->numusers);
1325*1462Smarks 			if (vals == NULL) {
1326*1462Smarks 				error = ENOMEM;
1327*1462Smarks 				goto out;
1328*1462Smarks 			}
1329*1462Smarks 			vals->aent_type = USER | acl->dfacl_flag;
1330*1462Smarks 		}
1331*1462Smarks 
1332*1462Smarks 		if (!(acl->state > ace_unused)) {
1333*1462Smarks 			error = EINVAL;
1334*1462Smarks 			goto out;
1335*1462Smarks 		}
1336*1462Smarks 
1337*1462Smarks 		if (acep->a_type == ACE_ACCESS_ALLOWED_ACE_TYPE) {
1338*1462Smarks 			/* no more than one allowed per aclent_t */
1339*1462Smarks 			if (vals->allowed != ACE_MASK_UNDEFINED) {
1340*1462Smarks 				error = ENOTSUP;
1341*1462Smarks 				goto out;
1342*1462Smarks 			}
1343*1462Smarks 			vals->allowed = acep->a_access_mask;
1344*1462Smarks 		} else {
1345*1462Smarks 			/*
1346*1462Smarks 			 * it's a DENY; if there was a previous DENY, it
1347*1462Smarks 			 * must have been an ACL_MASK.
1348*1462Smarks 			 */
1349*1462Smarks 			if (vals->denied != ACE_MASK_UNDEFINED) {
1350*1462Smarks 				/* ACL_MASK is for USER and GROUP only */
1351*1462Smarks 				if ((acl->state != ace_user) &&
1352*1462Smarks 				    (acl->state != ace_group)) {
1353*1462Smarks 					error = ENOTSUP;
1354*1462Smarks 					goto out;
1355*1462Smarks 				}
1356*1462Smarks 
1357*1462Smarks 				if (! acl->hasmask) {
1358*1462Smarks 					acl->hasmask = 1;
1359*1462Smarks 					acl->acl_mask = vals->denied;
1360*1462Smarks 				/* check for mismatched ACL_MASK emulations */
1361*1462Smarks 				} else if (acl->acl_mask != vals->denied) {
1362*1462Smarks 					error = ENOTSUP;
1363*1462Smarks 					goto out;
1364*1462Smarks 				}
1365*1462Smarks 				vals->mask = vals->denied;
1366*1462Smarks 			}
1367*1462Smarks 			vals->denied = acep->a_access_mask;
1368*1462Smarks 		}
1369*1462Smarks 	}
1370*1462Smarks 
1371*1462Smarks 	/* done collating; produce the aclent_t lists */
1372*1462Smarks 	if (normacl->state != ace_unused) {
1373*1462Smarks 		error = ace_list_to_aent(normacl, aclentp, aclcnt,
1374*1462Smarks 		    owner, group, isdir);
1375*1462Smarks 		if (error != 0) {
1376*1462Smarks 			goto out;
1377*1462Smarks 		}
1378*1462Smarks 	}
1379*1462Smarks 	if (dfacl->state != ace_unused) {
1380*1462Smarks 		error = ace_list_to_aent(dfacl, dfaclentp, dfaclcnt,
1381*1462Smarks 		    owner, group, isdir);
1382*1462Smarks 		if (error != 0) {
1383*1462Smarks 			goto out;
1384*1462Smarks 		}
1385*1462Smarks 	}
1386*1462Smarks 
1387*1462Smarks out:
1388*1462Smarks 	if (normacl != NULL)
1389*1462Smarks 		ace_list_free(normacl);
1390*1462Smarks 	if (dfacl != NULL)
1391*1462Smarks 		ace_list_free(dfacl);
1392*1462Smarks 
1393*1462Smarks 	return (error);
1394*1462Smarks }
1395*1462Smarks 
1396*1462Smarks static int
1397*1462Smarks convert_ace_to_aent(ace_t *acebufp, int acecnt, int isdir,
1398*1462Smarks     uid_t owner, gid_t group, aclent_t **retaclentp, int *retaclcnt)
1399*1462Smarks {
1400*1462Smarks 	int error;
1401*1462Smarks 	aclent_t *aclentp, *dfaclentp;
1402*1462Smarks 	int aclcnt, dfaclcnt;
1403*1462Smarks 
1404*1462Smarks 	error = ln_ace_to_aent(acebufp, acecnt, owner, group,
1405*1462Smarks 	    &aclentp, &aclcnt, &dfaclentp, &dfaclcnt, isdir);
1406*1462Smarks 
1407*1462Smarks 	if (error)
1408*1462Smarks 		return (error);
1409*1462Smarks 
1410*1462Smarks 
1411*1462Smarks 	if (dfaclcnt != 0) {
1412*1462Smarks 		/*
1413*1462Smarks 		 * Slap aclentp and dfaclentp into a single array.
1414*1462Smarks 		 */
1415*1462Smarks 		aclentp = realloc(aclentp, (sizeof (aclent_t) * aclcnt) +
1416*1462Smarks 		    (sizeof (aclent_t) * dfaclcnt));
1417*1462Smarks 		if (aclentp != NULL) {
1418*1462Smarks 			(void) memcpy(aclentp + aclcnt,
1419*1462Smarks 			    dfaclentp, sizeof (aclent_t) * dfaclcnt);
1420*1462Smarks 		} else {
1421*1462Smarks 			error = -1;
1422*1462Smarks 		}
1423*1462Smarks 	}
1424*1462Smarks 
1425*1462Smarks 	if (aclentp) {
1426*1462Smarks 		*retaclentp = aclentp;
1427*1462Smarks 		*retaclcnt = aclcnt + dfaclcnt;
1428*1462Smarks 	}
1429*1462Smarks 
1430*1462Smarks 	if (dfaclentp)
1431*1462Smarks 		free(dfaclentp);
1432*1462Smarks 
1433*1462Smarks 	return (error);
1434*1462Smarks }
1435*1462Smarks 
1436789Sahrens cacl_get(acl_inp inp, int get_flag, int type, acl_t **aclp)
1437789Sahrens {
1438789Sahrens 	const char *fname;
1439789Sahrens 	int fd;
1440789Sahrens 	int ace_acl = 0;
1441789Sahrens 	int error;
1442789Sahrens 	int getcmd, cntcmd;
1443789Sahrens 	acl_t *acl_info;
1444789Sahrens 	int	save_errno;
1445789Sahrens 	int	stat_error;
1446789Sahrens 	struct stat64 statbuf;
1447789Sahrens 
1448789Sahrens 	*aclp = NULL;
1449789Sahrens 	if (type == ACL_PATH) {
1450789Sahrens 		fname = inp.file;
1451789Sahrens 		ace_acl = pathconf(fname, _PC_ACL_ENABLED);
1452789Sahrens 	} else {
1453789Sahrens 		fd = inp.fd;
1454789Sahrens 		ace_acl = fpathconf(fd, _PC_ACL_ENABLED);
1455789Sahrens 	}
1456789Sahrens 
1457789Sahrens 	if (ace_acl == -1)
1458789Sahrens 		return (-1);
1459789Sahrens 
1460789Sahrens 	/*
1461789Sahrens 	 * if acl's aren't supported then
1462789Sahrens 	 * send it through the old GETACL interface
1463789Sahrens 	 */
1464789Sahrens 	if (ace_acl == 0) {
1465789Sahrens 		ace_acl = _ACL_ACLENT_ENABLED;
1466789Sahrens 	}
1467789Sahrens 
1468789Sahrens 	if (ace_acl & _ACL_ACE_ENABLED) {
1469789Sahrens 		cntcmd = ACE_GETACLCNT;
1470789Sahrens 		getcmd = ACE_GETACL;
1471789Sahrens 		acl_info = acl_alloc(ACE_T);
1472789Sahrens 	} else {
1473789Sahrens 		cntcmd = GETACLCNT;
1474789Sahrens 		getcmd = GETACL;
1475789Sahrens 		acl_info = acl_alloc(ACLENT_T);
1476789Sahrens 	}
1477789Sahrens 
1478789Sahrens 	if (acl_info == NULL)
1479789Sahrens 		return (-1);
1480789Sahrens 
1481789Sahrens 	if (type == ACL_PATH) {
1482789Sahrens 		acl_info->acl_cnt = acl(fname, cntcmd, 0, NULL);
1483789Sahrens 	} else {
1484789Sahrens 		acl_info->acl_cnt = facl(fd, cntcmd, 0, NULL);
1485789Sahrens 	}
1486789Sahrens 
1487789Sahrens 	save_errno = errno;
1488789Sahrens 	if (acl_info->acl_cnt < 0) {
1489789Sahrens 		acl_free(acl_info);
1490789Sahrens 		errno = save_errno;
1491789Sahrens 		return (-1);
1492789Sahrens 	}
1493789Sahrens 
1494789Sahrens 	if (acl_info->acl_cnt == 0) {
1495789Sahrens 		acl_free(acl_info);
1496789Sahrens 		errno = save_errno;
1497789Sahrens 		return (0);
1498789Sahrens 	}
1499789Sahrens 
1500789Sahrens 	acl_info->acl_aclp =
1501789Sahrens 	    malloc(acl_info->acl_cnt * acl_info->acl_entry_size);
1502789Sahrens 	save_errno = errno;
1503789Sahrens 
1504789Sahrens 	if (acl_info->acl_aclp == NULL) {
1505789Sahrens 		acl_free(acl_info);
1506789Sahrens 		errno = save_errno;
1507789Sahrens 		return (-1);
1508789Sahrens 	}
1509789Sahrens 
1510789Sahrens 	if (type == ACL_PATH) {
1511789Sahrens 		stat_error = stat64(fname, &statbuf);
1512789Sahrens 		error = acl(fname, getcmd, acl_info->acl_cnt,
1513789Sahrens 		    acl_info->acl_aclp);
1514789Sahrens 	} else {
1515789Sahrens 		stat_error = fstat64(fd, &statbuf);
1516789Sahrens 		error = facl(fd, getcmd, acl_info->acl_cnt,
1517789Sahrens 		    acl_info->acl_aclp);
1518789Sahrens 	}
1519789Sahrens 
1520789Sahrens 	save_errno = errno;
1521789Sahrens 	if (error == -1) {
1522789Sahrens 		acl_free(acl_info);
1523789Sahrens 		errno = save_errno;
1524789Sahrens 		return (-1);
1525789Sahrens 	}
1526789Sahrens 
1527789Sahrens 
1528789Sahrens 	if (stat_error == 0) {
1529789Sahrens 		acl_info->acl_flags =
1530789Sahrens 		    (S_ISDIR(statbuf.st_mode) ? ACL_IS_DIR : 0);
1531789Sahrens 	} else
1532789Sahrens 		acl_info->acl_flags = 0;
1533789Sahrens 
1534789Sahrens 	switch (acl_info->acl_type) {
1535789Sahrens 	case ACLENT_T:
1536789Sahrens 		if (acl_info->acl_cnt <= MIN_ACL_ENTRIES)
1537789Sahrens 			acl_info->acl_flags |= ACL_IS_TRIVIAL;
1538789Sahrens 		break;
1539789Sahrens 	case ACE_T:
1540789Sahrens 		if (ace_trivial(acl_info->acl_aclp, acl_info->acl_cnt) == 0)
1541789Sahrens 			acl_info->acl_flags |= ACL_IS_TRIVIAL;
1542789Sahrens 		break;
1543789Sahrens 	default:
1544789Sahrens 		errno = EINVAL;
1545789Sahrens 		acl_free(acl_info);
1546789Sahrens 		return (-1);
1547789Sahrens 	}
1548789Sahrens 
1549789Sahrens 	if ((acl_info->acl_flags & ACL_IS_TRIVIAL) &&
1550789Sahrens 	    (get_flag & ACL_NO_TRIVIAL)) {
1551789Sahrens 		acl_free(acl_info);
1552789Sahrens 		errno = 0;
1553789Sahrens 		return (0);
1554789Sahrens 	}
1555789Sahrens 
1556789Sahrens 	*aclp = acl_info;
1557789Sahrens 	return (0);
1558789Sahrens }
1559789Sahrens 
1560789Sahrens /*
1561789Sahrens  * return -1 on failure, otherwise the number of acl
1562789Sahrens  * entries is returned
1563789Sahrens  */
1564789Sahrens int
1565789Sahrens acl_get(const char *path, int get_flag, acl_t **aclp)
1566789Sahrens {
1567789Sahrens 	acl_inp acl_inp;
1568789Sahrens 	acl_inp.file = path;
1569789Sahrens 
1570789Sahrens 	return (cacl_get(acl_inp, get_flag, ACL_PATH, aclp));
1571789Sahrens }
1572789Sahrens 
1573789Sahrens int
1574789Sahrens facl_get(int fd, int get_flag, acl_t **aclp)
1575789Sahrens {
1576789Sahrens 
1577789Sahrens 	acl_inp acl_inp;
1578789Sahrens 	acl_inp.fd = fd;
1579789Sahrens 
1580789Sahrens 	return (cacl_get(acl_inp, get_flag, ACL_FD, aclp));
1581789Sahrens }
1582789Sahrens 
1583*1462Smarks static int
1584*1462Smarks acl_translate(acl_t *aclp, int target_flavor, int isdir, uid_t owner,
1585*1462Smarks     gid_t group)
1586*1462Smarks {
1587*1462Smarks 	int aclcnt;
1588*1462Smarks 	void *acldata;
1589*1462Smarks 	int error;
1590*1462Smarks 
1591*1462Smarks 	/*
1592*1462Smarks 	 * See if we need to translate
1593*1462Smarks 	 */
1594*1462Smarks 	if ((target_flavor == _ACL_ACE_ENABLED && aclp->acl_type == ACE_T) ||
1595*1462Smarks 	    (target_flavor == _ACL_ACLENT_ENABLED &&
1596*1462Smarks 	    aclp->acl_type == ACLENT_T))
1597*1462Smarks 		return (0);
1598*1462Smarks 
1599*1462Smarks 	if (target_flavor == -1)
1600*1462Smarks 		return (-1);
1601*1462Smarks 
1602*1462Smarks 	if (target_flavor ==  _ACL_ACE_ENABLED &&
1603*1462Smarks 	    aclp->acl_type == ACLENT_T) {
1604*1462Smarks 		error = convert_aent_to_ace(aclp->acl_aclp,
1605*1462Smarks 		    aclp->acl_cnt, isdir, (ace_t **)&acldata, &aclcnt);
1606*1462Smarks 		if (error) {
1607*1462Smarks 			errno = error;
1608*1462Smarks 			return (-1);
1609*1462Smarks 		}
1610*1462Smarks 	} else if (target_flavor == _ACL_ACLENT_ENABLED &&
1611*1462Smarks 	    aclp->acl_type == ACE_T) {
1612*1462Smarks 		error = convert_ace_to_aent(aclp->acl_aclp, aclp->acl_cnt,
1613*1462Smarks 		    isdir, owner, group, (aclent_t **)&acldata, &aclcnt);
1614*1462Smarks 		if (error) {
1615*1462Smarks 			errno = error;
1616*1462Smarks 			return (-1);
1617*1462Smarks 		}
1618*1462Smarks 	} else {
1619*1462Smarks 		errno = ENOTSUP;
1620*1462Smarks 		return (-1);
1621*1462Smarks 	}
1622*1462Smarks 
1623*1462Smarks 	/*
1624*1462Smarks 	 * replace old acl with newly translated acl
1625*1462Smarks 	 */
1626*1462Smarks 	free(aclp->acl_aclp);
1627*1462Smarks 	aclp->acl_aclp = acldata;
1628*1462Smarks 	aclp->acl_cnt = aclcnt;
1629*1462Smarks 	aclp->acl_type = (target_flavor == _ACL_ACE_ENABLED) ? ACE_T : ACLENT_T;
1630*1462Smarks 	return (0);
1631*1462Smarks }
1632*1462Smarks 
1633789Sahrens /*
1634789Sahrens  * Set an ACL, translates acl to ace_t when appropriate.
1635789Sahrens  */
1636789Sahrens static int
1637789Sahrens cacl_set(acl_inp *acl_inp, acl_t *aclp, int type)
1638789Sahrens {
1639789Sahrens 	int error = 0;
1640789Sahrens 	int acl_flavor_target;
1641789Sahrens 	struct stat64 statbuf;
1642789Sahrens 	int stat_error;
1643789Sahrens 	int isdir;
1644789Sahrens 
1645789Sahrens 
1646789Sahrens 	if (type == ACL_PATH) {
1647789Sahrens 		stat_error = stat64(acl_inp->file, &statbuf);
1648789Sahrens 		if (stat_error)
1649789Sahrens 			return (-1);
1650789Sahrens 		acl_flavor_target = pathconf(acl_inp->file, _PC_ACL_ENABLED);
1651789Sahrens 	} else {
1652789Sahrens 		stat_error = fstat64(acl_inp->fd, &statbuf);
1653789Sahrens 		if (stat_error)
1654789Sahrens 			return (-1);
1655789Sahrens 		acl_flavor_target = fpathconf(acl_inp->fd, _PC_ACL_ENABLED);
1656789Sahrens 	}
1657789Sahrens 
1658789Sahrens 	isdir = S_ISDIR(statbuf.st_mode);
1659789Sahrens 
1660*1462Smarks 	if ((error = acl_translate(aclp, acl_flavor_target, isdir,
1661*1462Smarks 	    statbuf.st_uid, statbuf.st_gid)) != 0) {
1662*1462Smarks 		return (error);
1663789Sahrens 	}
1664789Sahrens 
1665789Sahrens 	if (type == ACL_PATH) {
1666789Sahrens 		error = acl(acl_inp->file,
1667789Sahrens 		    (aclp->acl_type == ACE_T) ? ACE_SETACL : SETACL,
1668789Sahrens 		    aclp->acl_cnt, aclp->acl_aclp);
1669789Sahrens 	} else {
1670789Sahrens 		error = facl(acl_inp->fd,
1671789Sahrens 		    (aclp->acl_type == ACE_T) ? ACE_SETACL : SETACL,
1672789Sahrens 		    aclp->acl_cnt, aclp->acl_aclp);
1673789Sahrens 	}
1674789Sahrens 
1675789Sahrens 	return (error);
1676789Sahrens }
1677789Sahrens 
1678789Sahrens int
1679789Sahrens acl_set(const char *path, acl_t *aclp)
1680789Sahrens {
1681789Sahrens 	acl_inp acl_inp;
1682789Sahrens 
1683789Sahrens 	acl_inp.file = path;
1684789Sahrens 
1685789Sahrens 	return (cacl_set(&acl_inp, aclp, ACL_PATH));
1686789Sahrens }
1687789Sahrens 
1688789Sahrens int
1689789Sahrens facl_set(int fd, acl_t *aclp)
1690789Sahrens {
1691789Sahrens 	acl_inp acl_inp;
1692789Sahrens 
1693789Sahrens 	acl_inp.fd = fd;
1694789Sahrens 
1695789Sahrens 	return (cacl_set(&acl_inp, aclp, ACL_FD));
1696789Sahrens }
1697789Sahrens 
1698789Sahrens int
1699789Sahrens acl_cnt(acl_t *aclp)
1700789Sahrens {
1701789Sahrens 	return (aclp->acl_cnt);
1702789Sahrens }
1703789Sahrens 
1704789Sahrens int
1705789Sahrens acl_type(acl_t *aclp)
1706789Sahrens {
1707789Sahrens 	return (aclp->acl_type);
1708789Sahrens }
1709789Sahrens 
1710789Sahrens acl_t *
1711789Sahrens acl_dup(acl_t *aclp)
1712789Sahrens {
1713789Sahrens 	acl_t *newaclp;
1714789Sahrens 
1715789Sahrens 	newaclp = acl_alloc(aclp->acl_type);
1716789Sahrens 	if (newaclp == NULL)
1717789Sahrens 		return (NULL);
1718789Sahrens 
1719789Sahrens 	newaclp->acl_aclp = malloc(aclp->acl_entry_size * aclp->acl_cnt);
1720789Sahrens 	if (newaclp->acl_aclp == NULL) {
1721789Sahrens 		acl_free(newaclp);
1722789Sahrens 		return (NULL);
1723789Sahrens 	}
1724789Sahrens 
1725789Sahrens 	(void) memcpy(newaclp->acl_aclp,
1726789Sahrens 	    aclp->acl_aclp, aclp->acl_entry_size * aclp->acl_cnt);
1727789Sahrens 	newaclp->acl_cnt = aclp->acl_cnt;
1728789Sahrens 
1729789Sahrens 	return (newaclp);
1730789Sahrens }
1731789Sahrens 
1732789Sahrens int
1733789Sahrens acl_flags(acl_t *aclp)
1734789Sahrens {
1735789Sahrens 	return (aclp->acl_flags);
1736789Sahrens }
1737789Sahrens 
1738789Sahrens void *
1739789Sahrens acl_data(acl_t *aclp)
1740789Sahrens {
1741789Sahrens 	return (aclp->acl_aclp);
1742789Sahrens }
1743789Sahrens 
1744789Sahrens /*
1745789Sahrens  * Remove an ACL from a file and create a trivial ACL based
1746789Sahrens  * off of the mode argument.  After acl has been set owner/group
1747789Sahrens  * are updated to match owner,group arguments
1748789Sahrens  */
1749789Sahrens int
1750789Sahrens acl_strip(const char *file, uid_t owner, gid_t group, mode_t mode)
1751789Sahrens {
1752789Sahrens 	int	error = 0;
1753789Sahrens 	aclent_t min_acl[MIN_ACL_ENTRIES];
1754789Sahrens 	ace_t	min_ace_acl[6];	/* owner, group, everyone + complement denies */
1755789Sahrens 	int	acl_flavor;
1756789Sahrens 	int	aclcnt;
1757789Sahrens 
1758789Sahrens 	acl_flavor = pathconf(file, _PC_ACL_ENABLED);
1759789Sahrens 
1760789Sahrens 	if (acl_flavor == -1)
1761789Sahrens 		return (-1);
1762789Sahrens 	/*
1763789Sahrens 	 * force it through aclent flavor when file system doesn't
1764789Sahrens 	 * understand question
1765789Sahrens 	 */
1766789Sahrens 	if (acl_flavor == 0)
1767789Sahrens 		acl_flavor = _ACL_ACLENT_ENABLED;
1768789Sahrens 
1769789Sahrens 	if (acl_flavor & _ACL_ACLENT_ENABLED) {
1770789Sahrens 		min_acl[0].a_type = USER_OBJ;
1771789Sahrens 		min_acl[0].a_id   = owner;
1772789Sahrens 		min_acl[0].a_perm = ((mode & 0700) >> 6);
1773789Sahrens 		min_acl[1].a_type = GROUP_OBJ;
1774789Sahrens 		min_acl[1].a_id   = group;
1775789Sahrens 		min_acl[1].a_perm = ((mode & 0070) >> 3);
1776789Sahrens 		min_acl[2].a_type = CLASS_OBJ;
1777789Sahrens 		min_acl[2].a_id   = (uid_t)-1;
1778789Sahrens 		min_acl[2].a_perm = ((mode & 0070) >> 3);
1779789Sahrens 		min_acl[3].a_type = OTHER_OBJ;
1780789Sahrens 		min_acl[3].a_id   = (uid_t)-1;
1781789Sahrens 		min_acl[3].a_perm = (mode & 0007);
1782789Sahrens 		aclcnt = 4;
1783789Sahrens 		error = acl(file, SETACL, aclcnt, min_acl);
1784789Sahrens 	} else if (acl_flavor & _ACL_ACE_ENABLED) {
1785789Sahrens 		(void) memcpy(min_ace_acl, trivial_acl, sizeof (ace_t) * 6);
1786789Sahrens 
1787789Sahrens 		/*
1788789Sahrens 		 * Make aces match request mode
1789789Sahrens 		 */
1790789Sahrens 		adjust_ace_pair(&min_ace_acl[0], (mode & 0700) >> 6);
1791789Sahrens 		adjust_ace_pair(&min_ace_acl[2], (mode & 0070) >> 3);
1792789Sahrens 		adjust_ace_pair(&min_ace_acl[4], mode & 0007);
1793789Sahrens 
1794789Sahrens 		error = acl(file, ACE_SETACL, 6, min_ace_acl);
1795789Sahrens 	} else {
1796789Sahrens 		errno = EINVAL;
1797789Sahrens 		error = 1;
1798789Sahrens 	}
1799789Sahrens 
1800789Sahrens 	if (error == 0)
1801789Sahrens 		error = chown(file, owner, group);
1802789Sahrens 	return (error);
1803789Sahrens }
1804789Sahrens 
1805789Sahrens static int
1806789Sahrens ace_match(void *entry1, void *entry2)
1807789Sahrens {
1808789Sahrens 	ace_t *p1 = (ace_t *)entry1;
1809789Sahrens 	ace_t *p2 = (ace_t *)entry2;
1810789Sahrens 	ace_t ace1, ace2;
1811789Sahrens 
1812789Sahrens 	ace1 = *p1;
1813789Sahrens 	ace2 = *p2;
1814789Sahrens 
1815789Sahrens 	/*
1816789Sahrens 	 * Need to fixup who field for abstrations for
1817789Sahrens 	 * accurate comparison, since field is undefined.
1818789Sahrens 	 */
1819789Sahrens 	if (ace1.a_flags & (ACE_OWNER|ACE_GROUP|ACE_EVERYONE))
1820789Sahrens 		ace1.a_who = -1;
1821789Sahrens 	if (ace2.a_flags & (ACE_OWNER|ACE_GROUP|ACE_EVERYONE))
1822789Sahrens 		ace2.a_who = -1;
1823789Sahrens 	return (memcmp(&ace1, &ace2, sizeof (ace_t)));
1824789Sahrens }
1825789Sahrens 
1826789Sahrens static int
1827789Sahrens aclent_match(void *entry1, void *entry2)
1828789Sahrens {
1829789Sahrens 	aclent_t *aclent1 = (aclent_t *)entry1;
1830789Sahrens 	aclent_t *aclent2 = (aclent_t *)entry2;
1831789Sahrens 
1832789Sahrens 	return (memcmp(aclent1, aclent2, sizeof (aclent_t)));
1833789Sahrens }
1834789Sahrens 
1835789Sahrens /*
1836789Sahrens  * Find acl entries in acl that correspond to removeacl.  Search
1837789Sahrens  * is started from slot.  The flag argument indicates whether to
1838789Sahrens  * remove all matches or just the first match.
1839789Sahrens  */
1840789Sahrens int
1841789Sahrens acl_removeentries(acl_t *acl, acl_t *removeacl, int start_slot, int flag)
1842789Sahrens {
1843789Sahrens 	int i, j;
1844789Sahrens 	int match;
1845789Sahrens 	int (*acl_match)(void *acl1, void *acl2);
1846789Sahrens 	void *acl_entry, *remove_entry;
1847789Sahrens 	void *start;
1848789Sahrens 	int found = 0;
1849789Sahrens 
1850789Sahrens 	if (flag != ACL_REMOVE_ALL && flag != ACL_REMOVE_FIRST)
1851789Sahrens 		flag = ACL_REMOVE_FIRST;
1852789Sahrens 
1853789Sahrens 	if (acl == NULL || removeacl == NULL)
1854789Sahrens 		return (EACL_NO_ACL_ENTRY);
1855789Sahrens 
1856789Sahrens 	if (acl->acl_type != removeacl->acl_type)
1857789Sahrens 		return (EACL_DIFF_TYPE);
1858789Sahrens 
1859789Sahrens 	if (acl->acl_type == ACLENT_T)
1860789Sahrens 		acl_match = aclent_match;
1861789Sahrens 	else
1862789Sahrens 		acl_match = ace_match;
1863789Sahrens 
1864789Sahrens 	for (i = 0, remove_entry = removeacl->acl_aclp;
1865789Sahrens 	    i != removeacl->acl_cnt; i++) {
1866789Sahrens 
1867789Sahrens 		j = 0;
1868789Sahrens 		acl_entry = (char *)acl->acl_aclp +
1869789Sahrens 		    (acl->acl_entry_size * start_slot);
1870789Sahrens 		for (;;) {
1871789Sahrens 			match = acl_match(acl_entry, remove_entry);
1872789Sahrens 			if (match == 0)  {
1873789Sahrens 				found++;
1874789Sahrens 				start = (char *)acl_entry +
1875789Sahrens 				    acl->acl_entry_size;
1876789Sahrens 				(void) memmove(acl_entry, start,
1877789Sahrens 				    acl->acl_entry_size *
1878789Sahrens 				    acl->acl_cnt-- - (j + 1));
1879789Sahrens 
1880789Sahrens 				if (flag == ACL_REMOVE_FIRST)
1881789Sahrens 					break;
1882789Sahrens 				/*
1883789Sahrens 				 * List has changed, restart search from
1884789Sahrens 				 * beginning.
1885789Sahrens 				 */
1886789Sahrens 				acl_entry = acl->acl_aclp;
1887789Sahrens 				j = 0;
1888789Sahrens 				continue;
1889789Sahrens 			}
1890789Sahrens 			acl_entry = ((char *)acl_entry + acl->acl_entry_size);
1891789Sahrens 			if (++j >= acl->acl_cnt) {
1892789Sahrens 				break;
1893789Sahrens 			}
1894789Sahrens 		}
1895789Sahrens 	}
1896789Sahrens 
1897789Sahrens 	return ((found == 0) ? EACL_NO_ACL_ENTRY : 0);
1898789Sahrens }
1899789Sahrens 
1900789Sahrens /*
1901789Sahrens  * Replace entires entries in acl1 with the corresponding entries
1902789Sahrens  * in newentries.  The where argument specifies where to begin
1903789Sahrens  * the replacement.  If the where argument is 1 greater than the
1904789Sahrens  * number of acl entries in acl1 then they are appended.  If the
1905789Sahrens  * where argument is 2+ greater than the number of acl entries then
1906789Sahrens  * EACL_INVALID_SLOT is returned.
1907789Sahrens  */
1908789Sahrens int
1909789Sahrens acl_modifyentries(acl_t *acl1, acl_t *newentries, int where)
1910789Sahrens {
1911789Sahrens 
1912789Sahrens 	int slot;
1913789Sahrens 	int slots_needed;
1914789Sahrens 	int slots_left;
1915789Sahrens 	int newsize;
1916789Sahrens 
1917789Sahrens 	if (acl1 == NULL || newentries == NULL)
1918789Sahrens 		return (EACL_NO_ACL_ENTRY);
1919789Sahrens 
1920789Sahrens 	if (where < 0 || where >= acl1->acl_cnt)
1921789Sahrens 		return (EACL_INVALID_SLOT);
1922789Sahrens 
1923789Sahrens 	if (acl1->acl_type != newentries->acl_type)
1924789Sahrens 		return (EACL_DIFF_TYPE);
1925789Sahrens 
1926789Sahrens 	slot = where;
1927789Sahrens 
1928789Sahrens 	slots_left = acl1->acl_cnt - slot + 1;
1929789Sahrens 	if (slots_left < newentries->acl_cnt) {
1930789Sahrens 		slots_needed = newentries->acl_cnt - slots_left;
1931789Sahrens 		newsize = (acl1->acl_entry_size * acl1->acl_cnt) +
1932789Sahrens 		    (acl1->acl_entry_size * slots_needed);
1933789Sahrens 		acl1->acl_aclp = realloc(acl1->acl_aclp, newsize);
1934789Sahrens 		if (acl1->acl_aclp == NULL)
1935789Sahrens 			return (-1);
1936789Sahrens 	}
1937789Sahrens 	(void) memcpy((char *)acl1->acl_aclp + (acl1->acl_entry_size * slot),
1938789Sahrens 	    newentries->acl_aclp,
1939789Sahrens 	    newentries->acl_entry_size * newentries->acl_cnt);
1940789Sahrens 
1941789Sahrens 	/*
1942789Sahrens 	 * Did ACL grow?
1943789Sahrens 	 */
1944789Sahrens 
1945789Sahrens 	if ((slot + newentries->acl_cnt) > acl1->acl_cnt) {
1946789Sahrens 		acl1->acl_cnt = slot + newentries->acl_cnt;
1947789Sahrens 	}
1948789Sahrens 
1949789Sahrens 	return (0);
1950789Sahrens }
1951789Sahrens 
1952789Sahrens /*
1953789Sahrens  * Add acl2 entries into acl1.  The where argument specifies where
1954789Sahrens  * to add the entries.
1955789Sahrens  */
1956789Sahrens int
1957789Sahrens acl_addentries(acl_t *acl1, acl_t *acl2, int where)
1958789Sahrens {
1959789Sahrens 
1960789Sahrens 	int newsize;
1961789Sahrens 	int len;
1962789Sahrens 	void *start;
1963789Sahrens 	void *to;
1964789Sahrens 
1965789Sahrens 	if (acl1 == NULL || acl2 == NULL)
1966789Sahrens 		return (EACL_NO_ACL_ENTRY);
1967789Sahrens 
1968789Sahrens 	if (acl1->acl_type != acl2->acl_type)
1969789Sahrens 		return (EACL_DIFF_TYPE);
1970789Sahrens 
1971789Sahrens 	/*
1972789Sahrens 	 * allow where to specify 1 past last slot for an append operation
1973789Sahrens 	 * but anything greater is an error.
1974789Sahrens 	 */
1975789Sahrens 	if (where < 0 || where > acl1->acl_cnt)
1976789Sahrens 		return (EACL_INVALID_SLOT);
1977789Sahrens 
1978789Sahrens 	newsize = (acl2->acl_entry_size * acl2->acl_cnt) +
1979789Sahrens 	    (acl1->acl_entry_size * acl1->acl_cnt);
1980789Sahrens 	acl1->acl_aclp = realloc(acl1->acl_aclp, newsize);
1981789Sahrens 	if (acl1->acl_aclp == NULL)
1982789Sahrens 		return (-1);
1983789Sahrens 
1984789Sahrens 	/*
1985789Sahrens 	 * first push down entries where new ones will be inserted
1986789Sahrens 	 */
1987789Sahrens 
1988789Sahrens 	to = (void *)((char *)acl1->acl_aclp +
1989789Sahrens 	    ((where + acl2->acl_cnt) * acl1->acl_entry_size));
1990789Sahrens 
1991789Sahrens 	start = (void *)((char *)acl1->acl_aclp +
1992789Sahrens 	    where * acl1->acl_entry_size);
1993789Sahrens 
1994789Sahrens 	if (where < acl1->acl_cnt) {
1995789Sahrens 		len = (acl1->acl_cnt - where) * acl1->acl_entry_size;
1996789Sahrens 		(void) memmove(to, start, len);
1997789Sahrens 	}
1998789Sahrens 
1999789Sahrens 	/*
2000789Sahrens 	 * now stick in new entries.
2001789Sahrens 	 */
2002789Sahrens 
2003789Sahrens 	(void) memmove(start, acl2->acl_aclp,
2004789Sahrens 	    acl2->acl_cnt * acl2->acl_entry_size);
2005789Sahrens 
2006789Sahrens 	acl1->acl_cnt += acl2->acl_cnt;
2007789Sahrens 	return (0);
2008789Sahrens }
2009789Sahrens 
2010789Sahrens /*
2011789Sahrens  * return text for an ACL error.
2012789Sahrens  */
2013789Sahrens char *
2014789Sahrens acl_strerror(int errnum)
2015789Sahrens {
2016789Sahrens 	switch (errnum) {
2017789Sahrens 	case EACL_GRP_ERROR:
2018789Sahrens 		return (dgettext(TEXT_DOMAIN,
20191420Smarks 		    "There is more than one group or default group entry"));
2020789Sahrens 	case EACL_USER_ERROR:
2021789Sahrens 		return (dgettext(TEXT_DOMAIN,
20221420Smarks 		    "There is more than one user or default user entry"));
2023789Sahrens 	case EACL_OTHER_ERROR:
2024789Sahrens 		return (dgettext(TEXT_DOMAIN,
2025789Sahrens 		    "There is more than one other entry"));
2026789Sahrens 	case EACL_CLASS_ERROR:
2027789Sahrens 		return (dgettext(TEXT_DOMAIN,
2028789Sahrens 		    "There is more than one mask entry"));
2029789Sahrens 	case EACL_DUPLICATE_ERROR:
2030789Sahrens 		return (dgettext(TEXT_DOMAIN,
2031789Sahrens 		    "Duplicate user or group entries"));
2032789Sahrens 	case EACL_MISS_ERROR:
2033789Sahrens 		return (dgettext(TEXT_DOMAIN,
2034789Sahrens 		    "Missing user/group owner, other, mask entry"));
2035789Sahrens 	case EACL_MEM_ERROR:
2036789Sahrens 		return (dgettext(TEXT_DOMAIN,
2037789Sahrens 		    "Memory error"));
2038789Sahrens 	case EACL_ENTRY_ERROR:
2039789Sahrens 		return (dgettext(TEXT_DOMAIN,
2040789Sahrens 		    "Unrecognized entry type"));
2041789Sahrens 	case EACL_INHERIT_ERROR:
2042789Sahrens 		return (dgettext(TEXT_DOMAIN,
2043789Sahrens 		    "Invalid inheritance flags"));
2044789Sahrens 	case EACL_FLAGS_ERROR:
2045789Sahrens 		return (dgettext(TEXT_DOMAIN,
2046789Sahrens 		    "Unrecognized entry flags"));
2047789Sahrens 	case EACL_PERM_MASK_ERROR:
2048789Sahrens 		return (dgettext(TEXT_DOMAIN,
2049789Sahrens 		    "Invalid ACL permissions"));
2050789Sahrens 	case EACL_COUNT_ERROR:
2051789Sahrens 		return (dgettext(TEXT_DOMAIN,
2052789Sahrens 		    "Invalid ACL count"));
2053789Sahrens 	case EACL_INVALID_SLOT:
2054789Sahrens 		return (dgettext(TEXT_DOMAIN,
2055789Sahrens 		    "Invalid ACL entry number specified"));
2056789Sahrens 	case EACL_NO_ACL_ENTRY:
2057789Sahrens 		return (dgettext(TEXT_DOMAIN,
2058789Sahrens 		    "ACL entry doesn't exist"));
2059789Sahrens 	case EACL_DIFF_TYPE:
2060789Sahrens 		return (dgettext(TEXT_DOMAIN,
2061789Sahrens 		    "ACL type's are different"));
2062789Sahrens 	case EACL_INVALID_USER_GROUP:
2063789Sahrens 		return (dgettext(TEXT_DOMAIN, "Invalid user or group"));
2064789Sahrens 	case EACL_INVALID_STR:
2065789Sahrens 		return (dgettext(TEXT_DOMAIN, "ACL string is invalid"));
2066789Sahrens 	case EACL_FIELD_NOT_BLANK:
2067789Sahrens 		return (dgettext(TEXT_DOMAIN, "Field expected to be blank"));
2068789Sahrens 	case EACL_INVALID_ACCESS_TYPE:
2069789Sahrens 		return (dgettext(TEXT_DOMAIN, "Invalid access type"));
2070789Sahrens 	case EACL_UNKNOWN_DATA:
2071789Sahrens 		return (dgettext(TEXT_DOMAIN, "Unrecognized entry"));
2072789Sahrens 	case EACL_MISSING_FIELDS:
2073789Sahrens 		return (dgettext(TEXT_DOMAIN,
2074789Sahrens 		    "ACL specification missing required fields"));
2075789Sahrens 	case EACL_INHERIT_NOTDIR:
2076789Sahrens 		return (dgettext(TEXT_DOMAIN,
2077789Sahrens 		    "Inheritance flags are only allowed on directories"));
2078789Sahrens 	case -1:
2079789Sahrens 		return (strerror(errno));
2080789Sahrens 	default:
2081789Sahrens 		errno = EINVAL;
2082789Sahrens 		return (dgettext(TEXT_DOMAIN, "Unknown error"));
2083789Sahrens 	}
2084789Sahrens }
20851420Smarks 
20861420Smarks extern int yyinteractive;
20871420Smarks 
20881420Smarks /* PRINTFLIKE1 */
20891420Smarks void
20901420Smarks acl_error(const char *fmt, ...)
20911420Smarks {
20921420Smarks 	va_list va;
20931420Smarks 
20941420Smarks 	if (yyinteractive == 0)
20951420Smarks 		return;
20961420Smarks 
20971420Smarks 	va_start(va, fmt);
20981420Smarks 	(void) vfprintf(stderr, fmt, va);
20991420Smarks 	va_end(va);
21001420Smarks }
2101