1789Sahrens /*
2789Sahrens  * CDDL HEADER START
3789Sahrens  *
4789Sahrens  * The contents of this file are subject to the terms of the
51462Smarks  * Common Development and Distribution License (the "License").
61462Smarks  * 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>
341462Smarks #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>
431462Smarks #include <sys/avl.h>
441462Smarks 
451462Smarks #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 
551462Smarks #define	ACL_SYNCHRONIZE_SET_DENY		0x0000001
56789Sahrens #define	ACL_SYNCHRONIZE_SET_ALLOW		0x0000002
571462Smarks #define	ACL_SYNCHRONIZE_ERR_DENY		0x0000004
581462Smarks #define	ACL_SYNCHRONIZE_ERR_ALLOW		0x0000008
59789Sahrens 
601462Smarks #define	ACL_WRITE_OWNER_SET_DENY		0x0000010
61789Sahrens #define	ACL_WRITE_OWNER_SET_ALLOW		0x0000020
621462Smarks #define	ACL_WRITE_OWNER_ERR_DENY		0x0000040
631462Smarks #define	ACL_WRITE_OWNER_ERR_ALLOW		0x0000080
64789Sahrens 
651462Smarks #define	ACL_DELETE_SET_DENY			0x0000100
661462Smarks #define	ACL_DELETE_SET_ALLOW			0x0000200
671462Smarks #define	ACL_DELETE_ERR_DENY			0x0000400
681462Smarks #define	ACL_DELETE_ERR_ALLOW			0x0000800
691462Smarks 
701462Smarks #define	ACL_WRITE_ATTRS_OWNER_SET_DENY		0x0001000
71789Sahrens #define	ACL_WRITE_ATTRS_OWNER_SET_ALLOW		0x0002000
721462Smarks #define	ACL_WRITE_ATTRS_OWNER_ERR_DENY		0x0004000
731462Smarks #define	ACL_WRITE_ATTRS_OWNER_ERR_ALLOW		0x0008000
74789Sahrens 
75789Sahrens #define	ACL_WRITE_ATTRS_WRITER_SET_DENY		0x0010000
761462Smarks #define	ACL_WRITE_ATTRS_WRITER_SET_ALLOW	0x0020000
771462Smarks #define	ACL_WRITE_ATTRS_WRITER_ERR_DENY		0x0040000
781462Smarks #define	ACL_WRITE_ATTRS_WRITER_ERR_ALLOW	0x0080000
79789Sahrens 
801462Smarks #define	ACL_WRITE_NAMED_WRITER_SET_DENY		0x0100000
81789Sahrens #define	ACL_WRITE_NAMED_WRITER_SET_ALLOW	0x0200000
821462Smarks #define	ACL_WRITE_NAMED_WRITER_ERR_DENY		0x0400000
831462Smarks #define	ACL_WRITE_NAMED_WRITER_ERR_ALLOW	0x0800000
841462Smarks 
851462Smarks #define	ACL_READ_NAMED_READER_SET_DENY		0x1000000
861462Smarks #define	ACL_READ_NAMED_READER_SET_ALLOW		0x2000000
871462Smarks #define	ACL_READ_NAMED_READER_ERR_DENY		0x4000000
881462Smarks #define	ACL_READ_NAMED_READER_ERR_ALLOW		0x8000000
891462Smarks 
901462Smarks 
911462Smarks #define	ACE_VALID_MASK_BITS (\
921462Smarks     ACE_READ_DATA | \
931462Smarks     ACE_LIST_DIRECTORY | \
941462Smarks     ACE_WRITE_DATA | \
951462Smarks     ACE_ADD_FILE | \
961462Smarks     ACE_APPEND_DATA | \
971462Smarks     ACE_ADD_SUBDIRECTORY | \
981462Smarks     ACE_READ_NAMED_ATTRS | \
991462Smarks     ACE_WRITE_NAMED_ATTRS | \
1001462Smarks     ACE_EXECUTE | \
1011462Smarks     ACE_DELETE_CHILD | \
1021462Smarks     ACE_READ_ATTRIBUTES | \
1031462Smarks     ACE_WRITE_ATTRIBUTES | \
1041462Smarks     ACE_DELETE | \
1051462Smarks     ACE_READ_ACL | \
1061462Smarks     ACE_WRITE_ACL | \
1071462Smarks     ACE_WRITE_OWNER | \
1081462Smarks     ACE_SYNCHRONIZE)
1091462Smarks 
1101462Smarks #define	ACE_MASK_UNDEFINED			0x80000000
1111462Smarks 
1121462Smarks #define	ACE_VALID_FLAG_BITS (ACE_FILE_INHERIT_ACE | \
1131462Smarks     ACE_DIRECTORY_INHERIT_ACE | \
1141462Smarks     ACE_NO_PROPAGATE_INHERIT_ACE | ACE_INHERIT_ONLY_ACE | \
1151462Smarks     ACE_SUCCESSFUL_ACCESS_ACE_FLAG | ACE_FAILED_ACCESS_ACE_FLAG | \
1161462Smarks     ACE_IDENTIFIER_GROUP | ACE_OWNER | ACE_GROUP | ACE_EVERYONE)
117789Sahrens 
1181462Smarks /*
1191462Smarks  * ACL conversion helpers
1201462Smarks  */
1211462Smarks 
1221462Smarks typedef enum {
1231462Smarks 	ace_unused,
1241462Smarks 	ace_user_obj,
1251462Smarks 	ace_user,
1261462Smarks 	ace_group, /* includes GROUP and GROUP_OBJ */
1271462Smarks 	ace_other_obj
1281462Smarks } ace_to_aent_state_t;
129789Sahrens 
1301462Smarks typedef struct acevals {
1311462Smarks 	uid_t key;
1321462Smarks 	avl_node_t avl;
1331462Smarks 	uint32_t mask;
1341462Smarks 	uint32_t allowed;
1351462Smarks 	uint32_t denied;
1361462Smarks 	int aent_type;
1371462Smarks } acevals_t;
1381462Smarks 
1391462Smarks typedef struct ace_list {
1401462Smarks 	acevals_t user_obj;
1411462Smarks 	avl_tree_t user;
1421462Smarks 	int numusers;
1431462Smarks 	acevals_t group_obj;
1441462Smarks 	avl_tree_t group;
1451462Smarks 	int numgroups;
1461462Smarks 	acevals_t other_obj;
1471462Smarks 	uint32_t acl_mask;
1481462Smarks 	int hasmask;
1491462Smarks 	int dfacl_flag;
1501462Smarks 	ace_to_aent_state_t state;
1511462Smarks 	int seen; /* bitmask of all aclent_t a_type values seen */
1521462Smarks } 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 
219789Sahrens 	if (acl_flavor == _ACL_ACE_ENABLED)
220789Sahrens 		cntcmd = ACE_GETACLCNT;
221789Sahrens 	else
222789Sahrens 		cntcmd = GETACLCNT;
223789Sahrens 
224789Sahrens 	aclcnt = acl(filename, cntcmd, 0, NULL);
225789Sahrens 	if (aclcnt > 0) {
226789Sahrens 		if (acl_flavor == _ACL_ACE_ENABLED) {
2271231Smarks 			acep = malloc(sizeof (ace_t) * aclcnt);
2281231Smarks 			if (acep == NULL)
2291231Smarks 				return (-1);
2301231Smarks 			if (acl(filename, ACE_GETACL,
2311231Smarks 			    aclcnt, acep) < 0) {
2321231Smarks 				free(acep);
2331231Smarks 				return (-1);
2341231Smarks 			}
235789Sahrens 
2361231Smarks 			val = ace_trivial(acep, aclcnt);
2371231Smarks 			free(acep);
2381231Smarks 
239789Sahrens 		} else if (aclcnt > MIN_ACL_ENTRIES)
240789Sahrens 			val = 1;
241789Sahrens 	}
242789Sahrens 	return (val);
243789Sahrens }
244789Sahrens 
245789Sahrens static uint32_t
246789Sahrens access_mask_set(int haswriteperm, int hasreadperm, int isowner, int isallow)
247789Sahrens {
248789Sahrens 	uint32_t access_mask = 0;
249789Sahrens 	int acl_produce;
250789Sahrens 	int synchronize_set = 0, write_owner_set = 0;
251789Sahrens 	int delete_set = 0, write_attrs_set = 0;
252789Sahrens 	int read_named_set = 0, write_named_set = 0;
253789Sahrens 
254789Sahrens 	acl_produce = (ACL_SYNCHRONIZE_SET_ALLOW |
255789Sahrens 	    ACL_WRITE_ATTRS_OWNER_SET_ALLOW |
256789Sahrens 	    ACL_WRITE_ATTRS_WRITER_SET_DENY);
257789Sahrens 
258789Sahrens 	if (isallow) {
259789Sahrens 		synchronize_set = ACL_SYNCHRONIZE_SET_ALLOW;
260789Sahrens 		write_owner_set = ACL_WRITE_OWNER_SET_ALLOW;
261789Sahrens 		delete_set = ACL_DELETE_SET_ALLOW;
262789Sahrens 		if (hasreadperm)
263789Sahrens 			read_named_set = ACL_READ_NAMED_READER_SET_ALLOW;
264789Sahrens 		if (haswriteperm)
265789Sahrens 			write_named_set = ACL_WRITE_NAMED_WRITER_SET_ALLOW;
266789Sahrens 		if (isowner)
267789Sahrens 			write_attrs_set = ACL_WRITE_ATTRS_OWNER_SET_ALLOW;
268789Sahrens 		else if (haswriteperm)
269789Sahrens 			write_attrs_set = ACL_WRITE_ATTRS_WRITER_SET_ALLOW;
270789Sahrens 	} else {
271789Sahrens 
272789Sahrens 		synchronize_set = ACL_SYNCHRONIZE_SET_DENY;
273789Sahrens 		write_owner_set = ACL_WRITE_OWNER_SET_DENY;
274789Sahrens 		delete_set = ACL_DELETE_SET_DENY;
275789Sahrens 		if (hasreadperm)
276789Sahrens 			read_named_set = ACL_READ_NAMED_READER_SET_DENY;
277789Sahrens 		if (haswriteperm)
278789Sahrens 			write_named_set = ACL_WRITE_NAMED_WRITER_SET_DENY;
279789Sahrens 		if (isowner)
280789Sahrens 			write_attrs_set = ACL_WRITE_ATTRS_OWNER_SET_DENY;
281789Sahrens 		else if (haswriteperm)
282789Sahrens 			write_attrs_set = ACL_WRITE_ATTRS_WRITER_SET_DENY;
283789Sahrens 		else
284789Sahrens 			/*
285789Sahrens 			 * If the entity is not the owner and does not
286789Sahrens 			 * have write permissions ACE_WRITE_ATTRIBUTES will
287789Sahrens 			 * always go in the DENY ACE.
288789Sahrens 			 */
289789Sahrens 			access_mask |= ACE_WRITE_ATTRIBUTES;
290789Sahrens 	}
291789Sahrens 
292789Sahrens 	if (acl_produce & synchronize_set)
293789Sahrens 		access_mask |= ACE_SYNCHRONIZE;
294789Sahrens 	if (acl_produce & write_owner_set)
295789Sahrens 		access_mask |= ACE_WRITE_OWNER;
296789Sahrens 	if (acl_produce & delete_set)
297789Sahrens 		access_mask |= ACE_DELETE;
298789Sahrens 	if (acl_produce & write_attrs_set)
299789Sahrens 		access_mask |= ACE_WRITE_ATTRIBUTES;
300789Sahrens 	if (acl_produce & read_named_set)
301789Sahrens 		access_mask |= ACE_READ_NAMED_ATTRS;
302789Sahrens 	if (acl_produce & write_named_set)
303789Sahrens 		access_mask |= ACE_WRITE_NAMED_ATTRS;
304789Sahrens 
305789Sahrens 	return (access_mask);
306789Sahrens }
307789Sahrens 
308789Sahrens /*
309789Sahrens  * Given an mode_t, convert it into an access_mask as used
310789Sahrens  * by nfsace, assuming aclent_t -> nfsace semantics.
311789Sahrens  */
312789Sahrens static uint32_t
313789Sahrens mode_to_ace_access(mode_t mode, int isdir, int isowner, int isallow)
314789Sahrens {
315789Sahrens 	uint32_t access = 0;
316789Sahrens 	int haswriteperm = 0;
317789Sahrens 	int hasreadperm = 0;
318789Sahrens 
319789Sahrens 	if (isallow) {
320789Sahrens 		haswriteperm = (mode & 02);
321789Sahrens 		hasreadperm = (mode & 04);
322789Sahrens 	} else {
323789Sahrens 		haswriteperm = !(mode & 02);
324789Sahrens 		hasreadperm = !(mode & 04);
325789Sahrens 	}
326789Sahrens 
327789Sahrens 	/*
328789Sahrens 	 * The following call takes care of correctly setting the following
329789Sahrens 	 * mask bits in the access_mask:
330789Sahrens 	 * ACE_SYNCHRONIZE, ACE_WRITE_OWNER, ACE_DELETE,
331789Sahrens 	 * ACE_WRITE_ATTRIBUTES, ACE_WRITE_NAMED_ATTRS, ACE_READ_NAMED_ATTRS
332789Sahrens 	 */
333789Sahrens 	access = access_mask_set(haswriteperm, hasreadperm, isowner, isallow);
334789Sahrens 
335789Sahrens 	if (isallow) {
336789Sahrens 		access |= ACE_READ_ACL | ACE_READ_ATTRIBUTES;
337789Sahrens 		if (isowner)
338789Sahrens 			access |= ACE_WRITE_ACL;
339789Sahrens 	} else {
340789Sahrens 		if (! isowner)
341789Sahrens 			access |= ACE_WRITE_ACL;
342789Sahrens 	}
343789Sahrens 
344789Sahrens 	/* read */
345789Sahrens 	if (mode & 04) {
346789Sahrens 		access |= ACE_READ_DATA;
347789Sahrens 	}
348789Sahrens 	/* write */
349789Sahrens 	if (mode & 02) {
350789Sahrens 		access |= ACE_WRITE_DATA |
351789Sahrens 		    ACE_APPEND_DATA;
352789Sahrens 		if (isdir)
353789Sahrens 			access |= ACE_DELETE_CHILD;
354789Sahrens 	}
355789Sahrens 	/* exec */
356789Sahrens 	if (mode & 01) {
357789Sahrens 		access |= ACE_EXECUTE;
358789Sahrens 	}
359789Sahrens 
360789Sahrens 	return (access);
361789Sahrens }
362789Sahrens 
363789Sahrens /*
364789Sahrens  * Given an nfsace (presumably an ALLOW entry), make a
365789Sahrens  * corresponding DENY entry at the address given.
366789Sahrens  */
367789Sahrens static void
368789Sahrens ace_make_deny(ace_t *allow, ace_t *deny, int isdir, int isowner)
369789Sahrens {
370789Sahrens 	(void) memcpy(deny, allow, sizeof (ace_t));
371789Sahrens 
372789Sahrens 	deny->a_who = allow->a_who;
373789Sahrens 
374789Sahrens 	deny->a_type = ACE_ACCESS_DENIED_ACE_TYPE;
375789Sahrens 	deny->a_access_mask ^= ACE_POSIX_SUPPORTED_BITS;
376789Sahrens 	if (isdir)
377789Sahrens 		deny->a_access_mask ^= ACE_DELETE_CHILD;
378789Sahrens 
379789Sahrens 	deny->a_access_mask &= ~(ACE_SYNCHRONIZE | ACE_WRITE_OWNER |
380789Sahrens 	    ACE_DELETE | ACE_WRITE_ATTRIBUTES | ACE_READ_NAMED_ATTRS |
381789Sahrens 	    ACE_WRITE_NAMED_ATTRS);
382789Sahrens 	deny->a_access_mask |= access_mask_set((allow->a_access_mask &
383789Sahrens 	    ACE_WRITE_DATA), (allow->a_access_mask & ACE_READ_DATA), isowner,
384789Sahrens 	    B_FALSE);
385789Sahrens }
386789Sahrens /*
387789Sahrens  * Make an initial pass over an array of aclent_t's.  Gather
388789Sahrens  * information such as an ACL_MASK (if any), number of users,
389789Sahrens  * number of groups, and whether the array needs to be sorted.
390789Sahrens  */
391789Sahrens static int
392789Sahrens ln_aent_preprocess(aclent_t *aclent, int n,
393789Sahrens     int *hasmask, mode_t *mask,
394789Sahrens     int *numuser, int *numgroup, int *needsort)
395789Sahrens {
396789Sahrens 	int error = 0;
397789Sahrens 	int i;
398789Sahrens 	int curtype = 0;
399789Sahrens 
400789Sahrens 	*hasmask = 0;
401789Sahrens 	*mask = 07;
402789Sahrens 	*needsort = 0;
403789Sahrens 	*numuser = 0;
404789Sahrens 	*numgroup = 0;
405789Sahrens 
406789Sahrens 	for (i = 0; i < n; i++) {
407789Sahrens 		if (aclent[i].a_type < curtype)
408789Sahrens 			*needsort = 1;
409789Sahrens 		else if (aclent[i].a_type > curtype)
410789Sahrens 			curtype = aclent[i].a_type;
411789Sahrens 		if (aclent[i].a_type & USER)
412789Sahrens 			(*numuser)++;
413789Sahrens 		if (aclent[i].a_type & (GROUP | GROUP_OBJ))
414789Sahrens 			(*numgroup)++;
415789Sahrens 		if (aclent[i].a_type & CLASS_OBJ) {
416789Sahrens 			if (*hasmask) {
417789Sahrens 				error = EINVAL;
418789Sahrens 				goto out;
419789Sahrens 			} else {
420789Sahrens 				*hasmask = 1;
421789Sahrens 				*mask = aclent[i].a_perm;
422789Sahrens 			}
423789Sahrens 		}
424789Sahrens 	}
425789Sahrens 
426789Sahrens 	if ((! *hasmask) && (*numuser + *numgroup > 1)) {
427789Sahrens 		error = EINVAL;
428789Sahrens 		goto out;
429789Sahrens 	}
430789Sahrens 
431789Sahrens out:
432789Sahrens 	return (error);
433789Sahrens }
434789Sahrens 
435789Sahrens /*
436789Sahrens  * Convert an array of aclent_t into an array of nfsace entries,
437789Sahrens  * following POSIX draft -> nfsv4 conversion semantics as outlined in
438789Sahrens  * the IETF draft.
439789Sahrens  */
440789Sahrens static int
441789Sahrens ln_aent_to_ace(aclent_t *aclent, int n, ace_t **acepp, int *rescount, int isdir)
442789Sahrens {
443789Sahrens 	int error = 0;
444789Sahrens 	mode_t mask;
445789Sahrens 	int numuser, numgroup, needsort;
446789Sahrens 	int resultsize = 0;
447789Sahrens 	int i, groupi = 0, skip;
448789Sahrens 	ace_t *acep, *result = NULL;
449789Sahrens 	int hasmask;
450789Sahrens 
451789Sahrens 	error = ln_aent_preprocess(aclent, n, &hasmask, &mask,
452789Sahrens 	    &numuser, &numgroup, &needsort);
453789Sahrens 	if (error != 0)
454789Sahrens 		goto out;
455789Sahrens 
456789Sahrens 	/* allow + deny for each aclent */
457789Sahrens 	resultsize = n * 2;
458789Sahrens 	if (hasmask) {
459789Sahrens 		/*
460789Sahrens 		 * stick extra deny on the group_obj and on each
461789Sahrens 		 * user|group for the mask (the group_obj was added
462789Sahrens 		 * into the count for numgroup)
463789Sahrens 		 */
464789Sahrens 		resultsize += numuser + numgroup;
465789Sahrens 		/* ... and don't count the mask itself */
466789Sahrens 		resultsize -= 2;
467789Sahrens 	}
468789Sahrens 
469789Sahrens 	/* sort the source if necessary */
470789Sahrens 	if (needsort)
471789Sahrens 		ksort((caddr_t)aclent, n, sizeof (aclent_t), cmp2acls);
472789Sahrens 
473789Sahrens 	result = acep = calloc(1, resultsize * sizeof (ace_t));
474789Sahrens 	if (result == NULL)
475789Sahrens 		goto out;
476789Sahrens 
477789Sahrens 	for (i = 0; i < n; i++) {
478789Sahrens 		/*
479789Sahrens 		 * don't process CLASS_OBJ (mask); mask was grabbed in
480789Sahrens 		 * ln_aent_preprocess()
481789Sahrens 		 */
482789Sahrens 		if (aclent[i].a_type & CLASS_OBJ)
483789Sahrens 			continue;
484789Sahrens 
485789Sahrens 		/* If we need an ACL_MASK emulator, prepend it now */
486789Sahrens 		if ((hasmask) &&
487789Sahrens 		    (aclent[i].a_type & (USER | GROUP | GROUP_OBJ))) {
488789Sahrens 			acep->a_type = ACE_ACCESS_DENIED_ACE_TYPE;
489789Sahrens 			acep->a_flags = 0;
490789Sahrens 			if (aclent[i].a_type & GROUP_OBJ) {
491789Sahrens 				acep->a_who = -1;
492789Sahrens 				acep->a_flags |=
493789Sahrens 				    (ACE_IDENTIFIER_GROUP|ACE_GROUP);
494789Sahrens 			} else if (aclent[i].a_type & USER) {
495789Sahrens 				acep->a_who = aclent[i].a_id;
496789Sahrens 			} else {
497789Sahrens 				acep->a_who = aclent[i].a_id;
498789Sahrens 				acep->a_flags |= ACE_IDENTIFIER_GROUP;
499789Sahrens 			}
500789Sahrens 			if (aclent[i].a_type & ACL_DEFAULT) {
501789Sahrens 				acep->a_flags |= ACE_INHERIT_ONLY_ACE |
502789Sahrens 				    ACE_FILE_INHERIT_ACE |
503789Sahrens 				    ACE_DIRECTORY_INHERIT_ACE;
504789Sahrens 			}
505789Sahrens 			/*
506789Sahrens 			 * Set the access mask for the prepended deny
507789Sahrens 			 * ace.  To do this, we invert the mask (found
508789Sahrens 			 * in ln_aent_preprocess()) then convert it to an
509789Sahrens 			 * DENY ace access_mask.
510789Sahrens 			 */
511789Sahrens 			acep->a_access_mask = mode_to_ace_access((mask ^ 07),
512789Sahrens 			    isdir, 0, 0);
513789Sahrens 			acep += 1;
514789Sahrens 		}
515789Sahrens 
516789Sahrens 		/* handle a_perm -> access_mask */
517789Sahrens 		acep->a_access_mask = mode_to_ace_access(aclent[i].a_perm,
518789Sahrens 		    isdir, aclent[i].a_type & USER_OBJ, 1);
519789Sahrens 
520789Sahrens 		/* emulate a default aclent */
521789Sahrens 		if (aclent[i].a_type & ACL_DEFAULT) {
522789Sahrens 			acep->a_flags |= ACE_INHERIT_ONLY_ACE |
523789Sahrens 			    ACE_FILE_INHERIT_ACE |
524789Sahrens 			    ACE_DIRECTORY_INHERIT_ACE;
525789Sahrens 		}
526789Sahrens 
527789Sahrens 		/*
528789Sahrens 		 * handle a_perm and a_id
529789Sahrens 		 *
530789Sahrens 		 * this must be done last, since it involves the
531789Sahrens 		 * corresponding deny aces, which are handled
532789Sahrens 		 * differently for each different a_type.
533789Sahrens 		 */
534789Sahrens 		if (aclent[i].a_type & USER_OBJ) {
535789Sahrens 			acep->a_who = -1;
536789Sahrens 			acep->a_flags |= ACE_OWNER;
537789Sahrens 			ace_make_deny(acep, acep + 1, isdir, B_TRUE);
538789Sahrens 			acep += 2;
539789Sahrens 		} else if (aclent[i].a_type & USER) {
540789Sahrens 			acep->a_who = aclent[i].a_id;
541789Sahrens 			ace_make_deny(acep, acep + 1, isdir, B_FALSE);
542789Sahrens 			acep += 2;
543789Sahrens 		} else if (aclent[i].a_type & (GROUP_OBJ | GROUP)) {
544789Sahrens 			if (aclent[i].a_type & GROUP_OBJ) {
545789Sahrens 				acep->a_who = -1;
546789Sahrens 				acep->a_flags |= ACE_GROUP;
547789Sahrens 			} else {
548789Sahrens 				acep->a_who = aclent[i].a_id;
549789Sahrens 			}
550789Sahrens 			acep->a_flags |= ACE_IDENTIFIER_GROUP;
551789Sahrens 			/*
552789Sahrens 			 * Set the corresponding deny for the group ace.
553789Sahrens 			 *
554789Sahrens 			 * The deny aces go after all of the groups, unlike
555789Sahrens 			 * everything else, where they immediately follow
556789Sahrens 			 * the allow ace.
557789Sahrens 			 *
558789Sahrens 			 * We calculate "skip", the number of slots to
559789Sahrens 			 * skip ahead for the deny ace, here.
560789Sahrens 			 *
561789Sahrens 			 * The pattern is:
562789Sahrens 			 * MD1 A1 MD2 A2 MD3 A3 D1 D2 D3
563789Sahrens 			 * thus, skip is
564789Sahrens 			 * (2 * numgroup) - 1 - groupi
565789Sahrens 			 * (2 * numgroup) to account for MD + A
566789Sahrens 			 * - 1 to account for the fact that we're on the
567789Sahrens 			 * access (A), not the mask (MD)
568789Sahrens 			 * - groupi to account for the fact that we have
569789Sahrens 			 * passed up groupi number of MD's.
570789Sahrens 			 */
571789Sahrens 			skip = (2 * numgroup) - 1 - groupi;
572789Sahrens 			ace_make_deny(acep, acep + skip, isdir, B_FALSE);
573789Sahrens 			/*
574789Sahrens 			 * If we just did the last group, skip acep past
575789Sahrens 			 * all of the denies; else, just move ahead one.
576789Sahrens 			 */
577789Sahrens 			if (++groupi >= numgroup)
578789Sahrens 				acep += numgroup + 1;
579789Sahrens 			else
580789Sahrens 				acep += 1;
581789Sahrens 		} else if (aclent[i].a_type & OTHER_OBJ) {
582789Sahrens 			acep->a_who = -1;
583789Sahrens 			acep->a_flags |= ACE_EVERYONE;
584789Sahrens 			ace_make_deny(acep, acep + 1, isdir, B_FALSE);
585789Sahrens 			acep += 2;
586789Sahrens 		} else {
587789Sahrens 			error = EINVAL;
588789Sahrens 			goto out;
589789Sahrens 		}
590789Sahrens 	}
591789Sahrens 
592789Sahrens 	*acepp = result;
593789Sahrens 	*rescount = resultsize;
594789Sahrens 
595789Sahrens out:
596789Sahrens 	if (error != 0) {
597789Sahrens 		if ((result != NULL) && (resultsize > 0)) {
598789Sahrens 			free(result);
599789Sahrens 		}
600789Sahrens 	}
601789Sahrens 
602789Sahrens 	return (error);
603789Sahrens }
604789Sahrens 
605789Sahrens static int
606789Sahrens convert_aent_to_ace(aclent_t *aclentp, int aclcnt, int isdir,
607789Sahrens     ace_t **retacep, int *retacecnt)
608789Sahrens {
609789Sahrens 	ace_t *acep;
610789Sahrens 	ace_t *dfacep;
611789Sahrens 	int acecnt = 0;
612789Sahrens 	int dfacecnt = 0;
613789Sahrens 	int dfaclstart = 0;
614789Sahrens 	int dfaclcnt = 0;
615789Sahrens 	aclent_t *aclp;
616789Sahrens 	int i;
617789Sahrens 	int error;
618789Sahrens 
619789Sahrens 	ksort((caddr_t)aclentp, aclcnt, sizeof (aclent_t), cmp2acls);
620789Sahrens 
621789Sahrens 	for (i = 0, aclp = aclentp; i < aclcnt; aclp++, i++) {
622789Sahrens 		if (aclp->a_type & ACL_DEFAULT)
623789Sahrens 			break;
624789Sahrens 	}
625789Sahrens 
626789Sahrens 	if (i < aclcnt) {
6271462Smarks 		dfaclstart = i;
6281462Smarks 		dfaclcnt = aclcnt - i;
629789Sahrens 	}
630789Sahrens 
631789Sahrens 	if (dfaclcnt && isdir == 0) {
632789Sahrens 		return (-1);
633789Sahrens 	}
634789Sahrens 
635789Sahrens 	error = ln_aent_to_ace(aclentp, i,  &acep, &acecnt, isdir);
636789Sahrens 	if (error)
637789Sahrens 		return (-1);
638789Sahrens 
639789Sahrens 	if (dfaclcnt) {
640789Sahrens 		error = ln_aent_to_ace(&aclentp[dfaclstart], dfaclcnt,
641789Sahrens 		    &dfacep, &dfacecnt, isdir);
642789Sahrens 		if (error) {
643789Sahrens 			if (acep) {
644789Sahrens 				free(acep);
645789Sahrens 			}
646789Sahrens 			return (-1);
647789Sahrens 		}
648789Sahrens 	}
649789Sahrens 
6501462Smarks 	if (dfacecnt != 0) {
6511462Smarks 		acep = realloc(acep, sizeof (ace_t) * (acecnt + dfacecnt));
6521462Smarks 		if (acep == NULL)
6531462Smarks 			return (-1);
6541462Smarks 		if (dfaclcnt) {
6551462Smarks 			(void) memcpy(acep + acecnt, dfacep,
6561462Smarks 			    sizeof (ace_t) * dfacecnt);
6571462Smarks 		}
658789Sahrens 	}
659789Sahrens 	if (dfaclcnt)
660789Sahrens 		free(dfacep);
661789Sahrens 
662789Sahrens 	*retacecnt = acecnt + dfacecnt;
6631462Smarks 	*retacep = acep;
6641462Smarks 	return (0);
6651462Smarks }
6661462Smarks 
6671462Smarks static void
6681462Smarks acevals_init(acevals_t *vals, uid_t key)
6691462Smarks {
6701462Smarks 	bzero(vals, sizeof (*vals));
6711462Smarks 	vals->allowed = ACE_MASK_UNDEFINED;
6721462Smarks 	vals->denied = ACE_MASK_UNDEFINED;
6731462Smarks 	vals->mask = ACE_MASK_UNDEFINED;
6741462Smarks 	vals->key = key;
6751462Smarks }
6761462Smarks 
6771462Smarks static void
6781462Smarks ace_list_init(ace_list_t *al, int dfacl_flag)
6791462Smarks {
6801462Smarks 	acevals_init(&al->user_obj, NULL);
6811462Smarks 	acevals_init(&al->group_obj, NULL);
6821462Smarks 	acevals_init(&al->other_obj, NULL);
6831462Smarks 	al->numusers = 0;
6841462Smarks 	al->numgroups = 0;
6851462Smarks 	al->acl_mask = 0;
6861462Smarks 	al->hasmask = 0;
6871462Smarks 	al->state = ace_unused;
6881462Smarks 	al->seen = 0;
6891462Smarks 	al->dfacl_flag = dfacl_flag;
6901462Smarks }
6911462Smarks 
6921462Smarks /*
6931462Smarks  * Find or create an acevals holder for a given id and avl tree.
6941462Smarks  *
6951462Smarks  * Note that only one thread will ever touch these avl trees, so
6961462Smarks  * there is no need for locking.
6971462Smarks  */
6981462Smarks static acevals_t *
6991462Smarks acevals_find(ace_t *ace, avl_tree_t *avl, int *num)
7001462Smarks {
7011462Smarks 	acevals_t key, *rc;
7021462Smarks 	avl_index_t where;
7031462Smarks 
7041462Smarks 	key.key = ace->a_who;
7051462Smarks 	rc = avl_find(avl, &key, &where);
7061462Smarks 	if (rc != NULL)
7071462Smarks 		return (rc);
7081462Smarks 
7091462Smarks 	/* this memory is freed by ln_ace_to_aent()->ace_list_free() */
7101462Smarks 	rc = calloc(1, sizeof (acevals_t));
7111462Smarks 	if (rc == NULL)
7121462Smarks 		return (rc);
7131462Smarks 	acevals_init(rc, ace->a_who);
7141462Smarks 	avl_insert(avl, rc, where);
7151462Smarks 	(*num)++;
7161462Smarks 
7171462Smarks 	return (rc);
7181462Smarks }
7191462Smarks 
7201462Smarks static int
7211462Smarks access_mask_check(ace_t *acep, int mask_bit, int isowner)
7221462Smarks {
7231462Smarks 	int set_deny, err_deny;
7241462Smarks 	int set_allow, err_allow;
7251462Smarks 	int acl_consume;
7261462Smarks 	int haswriteperm, hasreadperm;
7271462Smarks 
7281462Smarks 	if (acep->a_type == ACE_ACCESS_DENIED_ACE_TYPE) {
7291462Smarks 		haswriteperm = (acep->a_access_mask & ACE_WRITE_DATA) ? 0 : 1;
7301462Smarks 		hasreadperm = (acep->a_access_mask & ACE_READ_DATA) ? 0 : 1;
7311462Smarks 	} else {
7321462Smarks 		haswriteperm = (acep->a_access_mask & ACE_WRITE_DATA) ? 1 : 0;
7331462Smarks 		hasreadperm = (acep->a_access_mask & ACE_READ_DATA) ? 1 : 0;
7341462Smarks 	}
7351462Smarks 
7361462Smarks 	acl_consume = (ACL_SYNCHRONIZE_ERR_DENY |
7371462Smarks 	    ACL_DELETE_ERR_DENY |
7381462Smarks 	    ACL_WRITE_OWNER_ERR_DENY |
7391462Smarks 	    ACL_WRITE_OWNER_ERR_ALLOW |
7401462Smarks 	    ACL_WRITE_ATTRS_OWNER_SET_ALLOW |
7411462Smarks 	    ACL_WRITE_ATTRS_OWNER_ERR_DENY |
7421462Smarks 	    ACL_WRITE_ATTRS_WRITER_SET_DENY |
7431462Smarks 	    ACL_WRITE_ATTRS_WRITER_ERR_ALLOW |
7441462Smarks 	    ACL_WRITE_NAMED_WRITER_ERR_DENY |
7451462Smarks 	    ACL_READ_NAMED_READER_ERR_DENY);
7461462Smarks 
7471462Smarks 	if (mask_bit == ACE_SYNCHRONIZE) {
7481462Smarks 		set_deny = ACL_SYNCHRONIZE_SET_DENY;
7491462Smarks 		err_deny =  ACL_SYNCHRONIZE_ERR_DENY;
7501462Smarks 		set_allow = ACL_SYNCHRONIZE_SET_ALLOW;
7511462Smarks 		err_allow = ACL_SYNCHRONIZE_ERR_ALLOW;
7521462Smarks 	} else if (mask_bit == ACE_WRITE_OWNER) {
7531462Smarks 		set_deny = ACL_WRITE_OWNER_SET_DENY;
7541462Smarks 		err_deny =  ACL_WRITE_OWNER_ERR_DENY;
7551462Smarks 		set_allow = ACL_WRITE_OWNER_SET_ALLOW;
7561462Smarks 		err_allow = ACL_WRITE_OWNER_ERR_ALLOW;
7571462Smarks 	} else if (mask_bit == ACE_DELETE) {
7581462Smarks 		set_deny = ACL_DELETE_SET_DENY;
7591462Smarks 		err_deny =  ACL_DELETE_ERR_DENY;
7601462Smarks 		set_allow = ACL_DELETE_SET_ALLOW;
7611462Smarks 		err_allow = ACL_DELETE_ERR_ALLOW;
7621462Smarks 	} else if (mask_bit == ACE_WRITE_ATTRIBUTES) {
7631462Smarks 		if (isowner) {
7641462Smarks 			set_deny = ACL_WRITE_ATTRS_OWNER_SET_DENY;
7651462Smarks 			err_deny =  ACL_WRITE_ATTRS_OWNER_ERR_DENY;
7661462Smarks 			set_allow = ACL_WRITE_ATTRS_OWNER_SET_ALLOW;
7671462Smarks 			err_allow = ACL_WRITE_ATTRS_OWNER_ERR_ALLOW;
7681462Smarks 		} else if (haswriteperm) {
7691462Smarks 			set_deny = ACL_WRITE_ATTRS_WRITER_SET_DENY;
7701462Smarks 			err_deny =  ACL_WRITE_ATTRS_WRITER_ERR_DENY;
7711462Smarks 			set_allow = ACL_WRITE_ATTRS_WRITER_SET_ALLOW;
7721462Smarks 			err_allow = ACL_WRITE_ATTRS_WRITER_ERR_ALLOW;
7731462Smarks 		} else {
7741462Smarks 			if ((acep->a_access_mask & mask_bit) &&
7751462Smarks 			    (acep->a_type & ACE_ACCESS_ALLOWED_ACE_TYPE)) {
7761462Smarks 				return (ENOTSUP);
7771462Smarks 			}
7781462Smarks 			return (0);
7791462Smarks 		}
7801462Smarks 	} else if (mask_bit == ACE_READ_NAMED_ATTRS) {
7811462Smarks 		if (!hasreadperm)
7821462Smarks 			return (0);
7831462Smarks 
7841462Smarks 		set_deny = ACL_READ_NAMED_READER_SET_DENY;
7851462Smarks 		err_deny = ACL_READ_NAMED_READER_ERR_DENY;
7861462Smarks 		set_allow = ACL_READ_NAMED_READER_SET_ALLOW;
7871462Smarks 		err_allow = ACL_READ_NAMED_READER_ERR_ALLOW;
7881462Smarks 	} else if (mask_bit == ACE_WRITE_NAMED_ATTRS) {
7891462Smarks 		if (!haswriteperm)
7901462Smarks 			return (0);
7911462Smarks 
7921462Smarks 		set_deny = ACL_WRITE_NAMED_WRITER_SET_DENY;
7931462Smarks 		err_deny = ACL_WRITE_NAMED_WRITER_ERR_DENY;
7941462Smarks 		set_allow = ACL_WRITE_NAMED_WRITER_SET_ALLOW;
7951462Smarks 		err_allow = ACL_WRITE_NAMED_WRITER_ERR_ALLOW;
7961462Smarks 	} else {
7971462Smarks 		return (EINVAL);
7981462Smarks 	}
7991462Smarks 
8001462Smarks 	if (acep->a_type == ACE_ACCESS_DENIED_ACE_TYPE) {
8011462Smarks 		if (acl_consume & set_deny) {
8021462Smarks 			if (!(acep->a_access_mask & mask_bit)) {
8031462Smarks 				return (ENOTSUP);
8041462Smarks 			}
8051462Smarks 		} else if (acl_consume & err_deny) {
8061462Smarks 			if (acep->a_access_mask & mask_bit) {
8071462Smarks 				return (ENOTSUP);
8081462Smarks 			}
8091462Smarks 		}
8101462Smarks 	} else {
8111462Smarks 		/* ACE_ACCESS_ALLOWED_ACE_TYPE */
8121462Smarks 		if (acl_consume & set_allow) {
8131462Smarks 			if (!(acep->a_access_mask & mask_bit)) {
8141462Smarks 				return (ENOTSUP);
8151462Smarks 			}
8161462Smarks 		} else if (acl_consume & err_allow) {
8171462Smarks 			if (acep->a_access_mask & mask_bit) {
8181462Smarks 				return (ENOTSUP);
8191462Smarks 			}
8201462Smarks 		}
8211462Smarks 	}
822789Sahrens 	return (0);
823789Sahrens }
824789Sahrens 
8251462Smarks static int
8261462Smarks ace_to_aent_legal(ace_t *acep)
8271462Smarks {
8281462Smarks 	int error = 0;
8291462Smarks 	int isowner;
8301462Smarks 
8311462Smarks 	/* only ALLOW or DENY */
8321462Smarks 	if ((acep->a_type != ACE_ACCESS_ALLOWED_ACE_TYPE) &&
8331462Smarks 	    (acep->a_type != ACE_ACCESS_DENIED_ACE_TYPE)) {
8341462Smarks 		error = ENOTSUP;
8351462Smarks 		goto out;
8361462Smarks 	}
8371462Smarks 
8381462Smarks 	/* check for invalid flags */
8391462Smarks 	if (acep->a_flags & ~(ACE_VALID_FLAG_BITS)) {
8401462Smarks 		error = EINVAL;
8411462Smarks 		goto out;
8421462Smarks 	}
8431462Smarks 
8441462Smarks 	/* some flags are illegal */
8451462Smarks 	if (acep->a_flags & (ACE_SUCCESSFUL_ACCESS_ACE_FLAG |
8461462Smarks 	    ACE_FAILED_ACCESS_ACE_FLAG |
8471462Smarks 	    ACE_NO_PROPAGATE_INHERIT_ACE)) {
8481462Smarks 		error = ENOTSUP;
8491462Smarks 		goto out;
8501462Smarks 	}
8511462Smarks 
8521462Smarks 	/* check for invalid masks */
8531462Smarks 	if (acep->a_access_mask & ~(ACE_VALID_MASK_BITS)) {
8541462Smarks 		error = EINVAL;
8551462Smarks 		goto out;
8561462Smarks 	}
8571462Smarks 
8581462Smarks 	if ((acep->a_flags & ACE_OWNER)) {
8591462Smarks 		isowner = 1;
8601462Smarks 	} else {
8611462Smarks 		isowner = 0;
8621462Smarks 	}
8631462Smarks 
8641462Smarks 	error = access_mask_check(acep, ACE_SYNCHRONIZE, isowner);
8651462Smarks 	if (error)
8661462Smarks 		goto out;
8671462Smarks 
8681462Smarks 	error = access_mask_check(acep, ACE_WRITE_OWNER, isowner);
8691462Smarks 	if (error)
8701462Smarks 		goto out;
8711462Smarks 
8721462Smarks 	error = access_mask_check(acep, ACE_DELETE, isowner);
8731462Smarks 	if (error)
8741462Smarks 		goto out;
8751462Smarks 
8761462Smarks 	error = access_mask_check(acep, ACE_WRITE_ATTRIBUTES, isowner);
8771462Smarks 	if (error)
8781462Smarks 		goto out;
8791462Smarks 
8801462Smarks 	error = access_mask_check(acep, ACE_READ_NAMED_ATTRS, isowner);
8811462Smarks 	if (error)
8821462Smarks 		goto out;
8831462Smarks 
8841462Smarks 	error = access_mask_check(acep, ACE_WRITE_NAMED_ATTRS, isowner);
8851462Smarks 	if (error)
8861462Smarks 		goto out;
8871462Smarks 
8881462Smarks 	/* more detailed checking of masks */
8891462Smarks 	if (acep->a_type == ACE_ACCESS_ALLOWED_ACE_TYPE) {
8901462Smarks 		if (! (acep->a_access_mask & ACE_READ_ATTRIBUTES)) {
8911462Smarks 			error = ENOTSUP;
8921462Smarks 			goto out;
8931462Smarks 		}
8941462Smarks 		if ((acep->a_access_mask & ACE_WRITE_DATA) &&
8951462Smarks 		    (! (acep->a_access_mask & ACE_APPEND_DATA))) {
8961462Smarks 			error = ENOTSUP;
8971462Smarks 			goto out;
8981462Smarks 		}
8991462Smarks 		if ((! (acep->a_access_mask & ACE_WRITE_DATA)) &&
9001462Smarks 		    (acep->a_access_mask & ACE_APPEND_DATA)) {
9011462Smarks 			error = ENOTSUP;
9021462Smarks 			goto out;
9031462Smarks 		}
9041462Smarks 	}
9051462Smarks 
9061462Smarks 	/* ACL enforcement */
9071462Smarks 	if ((acep->a_access_mask & ACE_READ_ACL) &&
9081462Smarks 	    (acep->a_type != ACE_ACCESS_ALLOWED_ACE_TYPE)) {
9091462Smarks 		error = ENOTSUP;
9101462Smarks 		goto out;
9111462Smarks 	}
9121462Smarks 	if (acep->a_access_mask & ACE_WRITE_ACL) {
9131462Smarks 		if ((acep->a_type == ACE_ACCESS_DENIED_ACE_TYPE) &&
9141462Smarks 		    (isowner)) {
9151462Smarks 			error = ENOTSUP;
9161462Smarks 			goto out;
9171462Smarks 		}
9181462Smarks 		if ((acep->a_type == ACE_ACCESS_ALLOWED_ACE_TYPE) &&
9191462Smarks 		    (! isowner)) {
9201462Smarks 			error = ENOTSUP;
9211462Smarks 			goto out;
9221462Smarks 		}
9231462Smarks 	}
9241462Smarks 
9251462Smarks out:
9261462Smarks 	return (error);
9271462Smarks }
9281462Smarks 
9291462Smarks static int
9301462Smarks ace_mask_to_mode(uint32_t  mask, o_mode_t *modep, int isdir)
9311462Smarks {
9321462Smarks 	int error = 0;
9331462Smarks 	o_mode_t mode = 0;
9341462Smarks 	uint32_t bits, wantbits;
9351462Smarks 
9361462Smarks 	/* read */
9371462Smarks 	if (mask & ACE_READ_DATA)
9381462Smarks 		mode |= 04;
9391462Smarks 
9401462Smarks 	/* write */
9411462Smarks 	wantbits = (ACE_WRITE_DATA | ACE_APPEND_DATA);
9421462Smarks 	if (isdir)
9431462Smarks 		wantbits |= ACE_DELETE_CHILD;
9441462Smarks 	bits = mask & wantbits;
9451462Smarks 	if (bits != 0) {
9461462Smarks 		if (bits != wantbits) {
9471462Smarks 			error = ENOTSUP;
9481462Smarks 			goto out;
9491462Smarks 		}
9501462Smarks 		mode |= 02;
9511462Smarks 	}
9521462Smarks 
9531462Smarks 	/* exec */
9541462Smarks 	if (mask & ACE_EXECUTE) {
9551462Smarks 		mode |= 01;
9561462Smarks 	}
9571462Smarks 
9581462Smarks 	*modep = mode;
9591462Smarks 
9601462Smarks out:
9611462Smarks 	return (error);
9621462Smarks }
9631462Smarks 
9641462Smarks static int
9651462Smarks ace_allow_to_mode(uint32_t mask, o_mode_t *modep, int isdir)
9661462Smarks {
9671462Smarks 	/* ACE_READ_ACL and ACE_READ_ATTRIBUTES must both be set */
9681462Smarks 	if ((mask & (ACE_READ_ACL | ACE_READ_ATTRIBUTES)) !=
9691462Smarks 	    (ACE_READ_ACL | ACE_READ_ATTRIBUTES)) {
9701462Smarks 		return (ENOTSUP);
9711462Smarks 	}
9721462Smarks 
9731462Smarks 	return (ace_mask_to_mode(mask, modep, isdir));
9741462Smarks }
9751462Smarks 
9761462Smarks static int
9771462Smarks acevals_to_aent(acevals_t *vals, aclent_t *dest, ace_list_t *list,
9781462Smarks     uid_t owner, gid_t group, int isdir)
9791462Smarks {
9801462Smarks 	int error;
9811462Smarks 	uint32_t  flips = ACE_POSIX_SUPPORTED_BITS;
9821462Smarks 
9831462Smarks 	if (isdir)
9841462Smarks 		flips |= ACE_DELETE_CHILD;
9851462Smarks 	if (vals->allowed != (vals->denied ^ flips)) {
9861462Smarks 		error = ENOTSUP;
9871462Smarks 		goto out;
9881462Smarks 	}
9891462Smarks 	if ((list->hasmask) && (list->acl_mask != vals->mask) &&
9901462Smarks 	    (vals->aent_type & (USER | GROUP | GROUP_OBJ))) {
9911462Smarks 		error = ENOTSUP;
9921462Smarks 		goto out;
9931462Smarks 	}
9941462Smarks 	error = ace_allow_to_mode(vals->allowed, &dest->a_perm, isdir);
9951462Smarks 	if (error != 0)
9961462Smarks 		goto out;
9971462Smarks 	dest->a_type = vals->aent_type;
9981462Smarks 	if (dest->a_type & (USER | GROUP)) {
9991462Smarks 		dest->a_id = vals->key;
10001462Smarks 	} else if (dest->a_type & USER_OBJ) {
10011462Smarks 		dest->a_id = owner;
10021462Smarks 	} else if (dest->a_type & GROUP_OBJ) {
10031462Smarks 		dest->a_id = group;
10041462Smarks 	} else if (dest->a_type & OTHER_OBJ) {
10051462Smarks 		dest->a_id = 0;
10061462Smarks 	} else {
10071462Smarks 		error = EINVAL;
10081462Smarks 		goto out;
10091462Smarks 	}
10101462Smarks 
10111462Smarks out:
10121462Smarks 	return (error);
10131462Smarks }
1014789Sahrens 
1015789Sahrens static int
10161462Smarks ace_list_to_aent(ace_list_t *list, aclent_t **aclentp, int *aclcnt,
10171462Smarks     uid_t owner, gid_t group, int isdir)
10181462Smarks {
10191462Smarks 	int error = 0;
10201462Smarks 	aclent_t *aent, *result = NULL;
10211462Smarks 	acevals_t *vals;
10221462Smarks 	int resultcount;
10231462Smarks 
10241462Smarks 	if ((list->seen & (USER_OBJ | GROUP_OBJ | OTHER_OBJ)) !=
10251462Smarks 	    (USER_OBJ | GROUP_OBJ | OTHER_OBJ)) {
10261462Smarks 		error = ENOTSUP;
10271462Smarks 		goto out;
10281462Smarks 	}
10291462Smarks 	if ((! list->hasmask) && (list->numusers + list->numgroups > 0)) {
10301462Smarks 		error = ENOTSUP;
10311462Smarks 		goto out;
10321462Smarks 	}
10331462Smarks 
10341462Smarks 	resultcount = 3 + list->numusers + list->numgroups;
10351462Smarks 	/*
10361462Smarks 	 * This must be the same condition as below, when we add the CLASS_OBJ
10371462Smarks 	 * (aka ACL mask)
10381462Smarks 	 */
10391462Smarks 	if ((list->hasmask) || (! list->dfacl_flag))
10401462Smarks 		resultcount += 1;
10411462Smarks 
10421462Smarks 	result = aent = calloc(1, resultcount * sizeof (aclent_t));
10431462Smarks 
10441462Smarks 	if (result == NULL) {
10451462Smarks 		error = ENOMEM;
10461462Smarks 		goto out;
10471462Smarks 	}
10481462Smarks 
10491462Smarks 	/* USER_OBJ */
10501462Smarks 	if (!(list->user_obj.aent_type & USER_OBJ)) {
10511462Smarks 		error = EINVAL;
10521462Smarks 		goto out;
10531462Smarks 	}
10541462Smarks 
10551462Smarks 	error = acevals_to_aent(&list->user_obj, aent, list, owner, group,
10561462Smarks 	    isdir);
10571462Smarks 
10581462Smarks 	if (error != 0)
10591462Smarks 		goto out;
10601462Smarks 	++aent;
10611462Smarks 	/* USER */
10621462Smarks 	vals = NULL;
10631462Smarks 	for (vals = avl_first(&list->user); vals != NULL;
10641462Smarks 	    vals = AVL_NEXT(&list->user, vals)) {
10651462Smarks 		if (!(vals->aent_type & USER)) {
10661462Smarks 			error = EINVAL;
10671462Smarks 			goto out;
10681462Smarks 		}
10691462Smarks 		error = acevals_to_aent(vals, aent, list, owner, group,
10701462Smarks 		    isdir);
10711462Smarks 		if (error != 0)
10721462Smarks 			goto out;
10731462Smarks 		++aent;
10741462Smarks 	}
10751462Smarks 	/* GROUP_OBJ */
10761462Smarks 	if (!(list->group_obj.aent_type & GROUP_OBJ)) {
10771462Smarks 		error = EINVAL;
10781462Smarks 		goto out;
10791462Smarks 	}
10801462Smarks 	error = acevals_to_aent(&list->group_obj, aent, list, owner, group,
10811462Smarks 	    isdir);
10821462Smarks 	if (error != 0)
10831462Smarks 		goto out;
10841462Smarks 	++aent;
10851462Smarks 	/* GROUP */
10861462Smarks 	vals = NULL;
10871462Smarks 	for (vals = avl_first(&list->group); vals != NULL;
10881462Smarks 	    vals = AVL_NEXT(&list->group, vals)) {
10891462Smarks 		if (!(vals->aent_type & GROUP)) {
10901462Smarks 			error = EINVAL;
10911462Smarks 			goto out;
10921462Smarks 		}
10931462Smarks 		error = acevals_to_aent(vals, aent, list, owner, group,
10941462Smarks 		    isdir);
10951462Smarks 		if (error != 0)
10961462Smarks 			goto out;
10971462Smarks 		++aent;
10981462Smarks 	}
10991462Smarks 	/*
11001462Smarks 	 * CLASS_OBJ (aka ACL_MASK)
11011462Smarks 	 *
11021462Smarks 	 * An ACL_MASK is not fabricated if the ACL is a default ACL.
11031462Smarks 	 * This is to follow UFS's behavior.
11041462Smarks 	 */
11051462Smarks 	if ((list->hasmask) || (! list->dfacl_flag)) {
11061462Smarks 		if (list->hasmask) {
11071462Smarks 			uint32_t flips = ACE_POSIX_SUPPORTED_BITS;
11081462Smarks 			if (isdir)
11091462Smarks 				flips |= ACE_DELETE_CHILD;
11101462Smarks 			error = ace_mask_to_mode(list->acl_mask ^ flips,
11111462Smarks 			    &aent->a_perm, isdir);
11121462Smarks 			if (error != 0)
11131462Smarks 				goto out;
11141462Smarks 		} else {
11151462Smarks 			/* fabricate the ACL_MASK from the group permissions */
11161462Smarks 			error = ace_mask_to_mode(list->group_obj.allowed,
11171462Smarks 			    &aent->a_perm, isdir);
11181462Smarks 			if (error != 0)
11191462Smarks 				goto out;
11201462Smarks 		}
11211462Smarks 		aent->a_id = 0;
11221462Smarks 		aent->a_type = CLASS_OBJ | list->dfacl_flag;
11231462Smarks 		++aent;
11241462Smarks 	}
11251462Smarks 	/* OTHER_OBJ */
11261462Smarks 	if (!(list->other_obj.aent_type & OTHER_OBJ)) {
11271462Smarks 		error = EINVAL;
11281462Smarks 		goto out;
11291462Smarks 	}
11301462Smarks 	error = acevals_to_aent(&list->other_obj, aent, list, owner, group,
11311462Smarks 	    isdir);
11321462Smarks 	if (error != 0)
11331462Smarks 		goto out;
11341462Smarks 	++aent;
11351462Smarks 
11361462Smarks 	*aclentp = result;
11371462Smarks 	*aclcnt = resultcount;
11381462Smarks 
11391462Smarks out:
11401462Smarks 	if (error != 0) {
11411462Smarks 		if (result != NULL)
11421462Smarks 			free(result);
11431462Smarks 	}
11441462Smarks 
11451462Smarks 	return (error);
11461462Smarks }
11471462Smarks 
11481462Smarks /*
11491462Smarks  * free all data associated with an ace_list
11501462Smarks  */
11511462Smarks static void
11521462Smarks ace_list_free(ace_list_t *al)
11531462Smarks {
11541462Smarks 	acevals_t *node;
11551462Smarks 	void *cookie;
11561462Smarks 
11571462Smarks 	if (al == NULL)
11581462Smarks 		return;
11591462Smarks 
11601462Smarks 	cookie = NULL;
11611462Smarks 	while ((node = avl_destroy_nodes(&al->user, &cookie)) != NULL)
11621462Smarks 		free(node);
11631462Smarks 	cookie = NULL;
11641462Smarks 	while ((node = avl_destroy_nodes(&al->group, &cookie)) != NULL)
11651462Smarks 		free(node);
11661462Smarks 
11671462Smarks 	avl_destroy(&al->user);
11681462Smarks 	avl_destroy(&al->group);
11691462Smarks 
11701462Smarks 	/* free the container itself */
11711462Smarks 	free(al);
11721462Smarks }
11731462Smarks 
11741462Smarks static int
11751462Smarks acevals_compare(const void *va, const void *vb)
11761462Smarks {
11771462Smarks 	const acevals_t *a = va, *b = vb;
11781462Smarks 
11791462Smarks 	if (a->key == b->key)
11801462Smarks 		return (0);
11811462Smarks 
11821462Smarks 	if (a->key > b->key)
11831462Smarks 		return (1);
11841462Smarks 
11851462Smarks 	else
11861462Smarks 		return (-1);
11871462Smarks }
11881462Smarks 
11891462Smarks /*
11901462Smarks  * Convert a list of ace_t entries to equivalent regular and default
11911462Smarks  * aclent_t lists.  Return error (ENOTSUP) when conversion is not possible.
11921462Smarks  */
11931462Smarks static int
11941462Smarks ln_ace_to_aent(ace_t *ace, int n, uid_t owner, gid_t group,
11951462Smarks     aclent_t **aclentp, int *aclcnt, aclent_t **dfaclentp, int *dfaclcnt,
11961462Smarks     int isdir)
11971462Smarks {
11981462Smarks 	int error = 0;
11991462Smarks 	ace_t *acep;
12001462Smarks 	uint32_t bits;
12011462Smarks 	int i;
12021462Smarks 	ace_list_t *normacl = NULL, *dfacl = NULL, *acl;
12031462Smarks 	acevals_t *vals;
12041462Smarks 
12051462Smarks 	*aclentp = NULL;
12061462Smarks 	*aclcnt = 0;
12071462Smarks 	*dfaclentp = NULL;
12081462Smarks 	*dfaclcnt = 0;
12091462Smarks 
12101462Smarks 	/* we need at least user_obj, group_obj, and other_obj */
12111462Smarks 	if (n < 6) {
12121462Smarks 		error = ENOTSUP;
12131462Smarks 		goto out;
12141462Smarks 	}
12151462Smarks 	if (ace == NULL) {
12161462Smarks 		error = EINVAL;
12171462Smarks 		goto out;
12181462Smarks 	}
12191462Smarks 
12201462Smarks 	normacl = calloc(1, sizeof (ace_list_t));
12211462Smarks 
12221462Smarks 	if (normacl == NULL) {
12231462Smarks 		error = errno;
12241462Smarks 		goto out;
12251462Smarks 	}
12261462Smarks 
12271462Smarks 	avl_create(&normacl->user, acevals_compare, sizeof (acevals_t),
12281462Smarks 	    offsetof(acevals_t, avl));
12291462Smarks 	avl_create(&normacl->group, acevals_compare, sizeof (acevals_t),
12301462Smarks 	    offsetof(acevals_t, avl));
12311462Smarks 
12321462Smarks 	ace_list_init(normacl, 0);
12331462Smarks 
12341462Smarks 	dfacl = calloc(1, sizeof (ace_list_t));
12351462Smarks 	if (dfacl == NULL) {
12361462Smarks 		error = errno;
12371462Smarks 		goto out;
12381462Smarks 	}
12391462Smarks 	avl_create(&dfacl->user, acevals_compare, sizeof (acevals_t),
12401462Smarks 	    offsetof(acevals_t, avl));
12411462Smarks 	avl_create(&dfacl->group, acevals_compare, sizeof (acevals_t),
12421462Smarks 	    offsetof(acevals_t, avl));
12431462Smarks 	ace_list_init(dfacl, ACL_DEFAULT);
12441462Smarks 
12451462Smarks 	/* process every ace_t... */
12461462Smarks 	for (i = 0; i < n; i++) {
12471462Smarks 		acep = &ace[i];
12481462Smarks 
12491462Smarks 		/* rule out certain cases quickly */
12501462Smarks 		error = ace_to_aent_legal(acep);
12511462Smarks 		if (error != 0)
12521462Smarks 			goto out;
12531462Smarks 
12541462Smarks 		/*
12551462Smarks 		 * Turn off these bits in order to not have to worry about
12561462Smarks 		 * them when doing the checks for compliments.
12571462Smarks 		 */
12581462Smarks 		acep->a_access_mask &= ~(ACE_WRITE_OWNER | ACE_DELETE |
12591462Smarks 		    ACE_SYNCHRONIZE | ACE_WRITE_ATTRIBUTES |
12601462Smarks 		    ACE_READ_NAMED_ATTRS | ACE_WRITE_NAMED_ATTRS);
12611462Smarks 
12621462Smarks 		/* see if this should be a regular or default acl */
12631462Smarks 		bits = acep->a_flags &
12641462Smarks 		    (ACE_INHERIT_ONLY_ACE |
12651462Smarks 		    ACE_FILE_INHERIT_ACE |
12661462Smarks 		    ACE_DIRECTORY_INHERIT_ACE);
12671462Smarks 		if (bits != 0) {
12681462Smarks 			/* all or nothing on these inherit bits */
12691462Smarks 			if (bits != (ACE_INHERIT_ONLY_ACE |
12701462Smarks 			    ACE_FILE_INHERIT_ACE |
12711462Smarks 			    ACE_DIRECTORY_INHERIT_ACE)) {
12721462Smarks 				error = ENOTSUP;
12731462Smarks 				goto out;
12741462Smarks 			}
12751462Smarks 			acl = dfacl;
12761462Smarks 		} else {
12771462Smarks 			acl = normacl;
12781462Smarks 		}
12791462Smarks 
12801462Smarks 		if ((acep->a_flags & ACE_OWNER)) {
12811462Smarks 			if (acl->state > ace_user_obj) {
12821462Smarks 				error = ENOTSUP;
12831462Smarks 				goto out;
12841462Smarks 			}
12851462Smarks 			acl->state = ace_user_obj;
12861462Smarks 			acl->seen |= USER_OBJ;
12871462Smarks 			vals = &acl->user_obj;
12881462Smarks 			vals->aent_type = USER_OBJ | acl->dfacl_flag;
12891462Smarks 		} else if ((acep->a_flags & ACE_EVERYONE)) {
12901462Smarks 			acl->state = ace_other_obj;
12911462Smarks 			acl->seen |= OTHER_OBJ;
12921462Smarks 			vals = &acl->other_obj;
12931462Smarks 			vals->aent_type = OTHER_OBJ | acl->dfacl_flag;
12941462Smarks 		} else if (acep->a_flags & ACE_IDENTIFIER_GROUP) {
12951462Smarks 			if (acl->state > ace_group) {
12961462Smarks 				error = ENOTSUP;
12971462Smarks 				goto out;
12981462Smarks 			}
12991462Smarks 			if ((acep->a_flags & ACE_GROUP)) {
13001462Smarks 				acl->seen |= GROUP_OBJ;
13011462Smarks 				vals = &acl->group_obj;
13021462Smarks 				vals->aent_type = GROUP_OBJ | acl->dfacl_flag;
13031462Smarks 			} else {
13041462Smarks 				acl->seen |= GROUP;
13051462Smarks 				vals = acevals_find(acep, &acl->group,
13061462Smarks 				    &acl->numgroups);
13071462Smarks 				if (vals == NULL) {
13081462Smarks 					error = ENOMEM;
13091462Smarks 					goto out;
13101462Smarks 				}
13111462Smarks 				vals->aent_type = GROUP | acl->dfacl_flag;
13121462Smarks 			}
13131462Smarks 			acl->state = ace_group;
13141462Smarks 		} else {
13151462Smarks 			if (acl->state > ace_user) {
13161462Smarks 				error = ENOTSUP;
13171462Smarks 				goto out;
13181462Smarks 			}
13191462Smarks 			acl->state = ace_user;
13201462Smarks 			acl->seen |= USER;
13211462Smarks 			vals = acevals_find(acep, &acl->user,
13221462Smarks 			    &acl->numusers);
13231462Smarks 			if (vals == NULL) {
13241462Smarks 				error = ENOMEM;
13251462Smarks 				goto out;
13261462Smarks 			}
13271462Smarks 			vals->aent_type = USER | acl->dfacl_flag;
13281462Smarks 		}
13291462Smarks 
13301462Smarks 		if (!(acl->state > ace_unused)) {
13311462Smarks 			error = EINVAL;
13321462Smarks 			goto out;
13331462Smarks 		}
13341462Smarks 
13351462Smarks 		if (acep->a_type == ACE_ACCESS_ALLOWED_ACE_TYPE) {
13361462Smarks 			/* no more than one allowed per aclent_t */
13371462Smarks 			if (vals->allowed != ACE_MASK_UNDEFINED) {
13381462Smarks 				error = ENOTSUP;
13391462Smarks 				goto out;
13401462Smarks 			}
13411462Smarks 			vals->allowed = acep->a_access_mask;
13421462Smarks 		} else {
13431462Smarks 			/*
13441462Smarks 			 * it's a DENY; if there was a previous DENY, it
13451462Smarks 			 * must have been an ACL_MASK.
13461462Smarks 			 */
13471462Smarks 			if (vals->denied != ACE_MASK_UNDEFINED) {
13481462Smarks 				/* ACL_MASK is for USER and GROUP only */
13491462Smarks 				if ((acl->state != ace_user) &&
13501462Smarks 				    (acl->state != ace_group)) {
13511462Smarks 					error = ENOTSUP;
13521462Smarks 					goto out;
13531462Smarks 				}
13541462Smarks 
13551462Smarks 				if (! acl->hasmask) {
13561462Smarks 					acl->hasmask = 1;
13571462Smarks 					acl->acl_mask = vals->denied;
13581462Smarks 				/* check for mismatched ACL_MASK emulations */
13591462Smarks 				} else if (acl->acl_mask != vals->denied) {
13601462Smarks 					error = ENOTSUP;
13611462Smarks 					goto out;
13621462Smarks 				}
13631462Smarks 				vals->mask = vals->denied;
13641462Smarks 			}
13651462Smarks 			vals->denied = acep->a_access_mask;
13661462Smarks 		}
13671462Smarks 	}
13681462Smarks 
13691462Smarks 	/* done collating; produce the aclent_t lists */
13701462Smarks 	if (normacl->state != ace_unused) {
13711462Smarks 		error = ace_list_to_aent(normacl, aclentp, aclcnt,
13721462Smarks 		    owner, group, isdir);
13731462Smarks 		if (error != 0) {
13741462Smarks 			goto out;
13751462Smarks 		}
13761462Smarks 	}
13771462Smarks 	if (dfacl->state != ace_unused) {
13781462Smarks 		error = ace_list_to_aent(dfacl, dfaclentp, dfaclcnt,
13791462Smarks 		    owner, group, isdir);
13801462Smarks 		if (error != 0) {
13811462Smarks 			goto out;
13821462Smarks 		}
13831462Smarks 	}
13841462Smarks 
13851462Smarks out:
13861462Smarks 	if (normacl != NULL)
13871462Smarks 		ace_list_free(normacl);
13881462Smarks 	if (dfacl != NULL)
13891462Smarks 		ace_list_free(dfacl);
13901462Smarks 
13911462Smarks 	return (error);
13921462Smarks }
13931462Smarks 
13941462Smarks static int
13951462Smarks convert_ace_to_aent(ace_t *acebufp, int acecnt, int isdir,
13961462Smarks     uid_t owner, gid_t group, aclent_t **retaclentp, int *retaclcnt)
13971462Smarks {
13981462Smarks 	int error;
13991462Smarks 	aclent_t *aclentp, *dfaclentp;
14001462Smarks 	int aclcnt, dfaclcnt;
14011462Smarks 
14021462Smarks 	error = ln_ace_to_aent(acebufp, acecnt, owner, group,
14031462Smarks 	    &aclentp, &aclcnt, &dfaclentp, &dfaclcnt, isdir);
14041462Smarks 
14051462Smarks 	if (error)
14061462Smarks 		return (error);
14071462Smarks 
14081462Smarks 
14091462Smarks 	if (dfaclcnt != 0) {
14101462Smarks 		/*
14111462Smarks 		 * Slap aclentp and dfaclentp into a single array.
14121462Smarks 		 */
14131462Smarks 		aclentp = realloc(aclentp, (sizeof (aclent_t) * aclcnt) +
14141462Smarks 		    (sizeof (aclent_t) * dfaclcnt));
14151462Smarks 		if (aclentp != NULL) {
14161462Smarks 			(void) memcpy(aclentp + aclcnt,
14171462Smarks 			    dfaclentp, sizeof (aclent_t) * dfaclcnt);
14181462Smarks 		} else {
14191462Smarks 			error = -1;
14201462Smarks 		}
14211462Smarks 	}
14221462Smarks 
14231462Smarks 	if (aclentp) {
14241462Smarks 		*retaclentp = aclentp;
14251462Smarks 		*retaclcnt = aclcnt + dfaclcnt;
14261462Smarks 	}
14271462Smarks 
14281462Smarks 	if (dfaclentp)
14291462Smarks 		free(dfaclentp);
14301462Smarks 
14311462Smarks 	return (error);
14321462Smarks }
14331462Smarks 
14341477Smarks static int
1435789Sahrens cacl_get(acl_inp inp, int get_flag, int type, acl_t **aclp)
1436789Sahrens {
1437789Sahrens 	const char *fname;
1438789Sahrens 	int fd;
1439789Sahrens 	int ace_acl = 0;
1440789Sahrens 	int error;
1441789Sahrens 	int getcmd, cntcmd;
1442789Sahrens 	acl_t *acl_info;
1443789Sahrens 	int	save_errno;
1444789Sahrens 	int	stat_error;
1445789Sahrens 	struct stat64 statbuf;
1446789Sahrens 
1447789Sahrens 	*aclp = NULL;
1448789Sahrens 	if (type == ACL_PATH) {
1449789Sahrens 		fname = inp.file;
1450789Sahrens 		ace_acl = pathconf(fname, _PC_ACL_ENABLED);
1451789Sahrens 	} else {
1452789Sahrens 		fd = inp.fd;
1453789Sahrens 		ace_acl = fpathconf(fd, _PC_ACL_ENABLED);
1454789Sahrens 	}
1455789Sahrens 
1456789Sahrens 	/*
1457789Sahrens 	 * if acl's aren't supported then
1458789Sahrens 	 * send it through the old GETACL interface
1459789Sahrens 	 */
14601666Smarks 	if (ace_acl == 0 || ace_acl == -1) {
1461789Sahrens 		ace_acl = _ACL_ACLENT_ENABLED;
1462789Sahrens 	}
1463789Sahrens 
1464789Sahrens 	if (ace_acl & _ACL_ACE_ENABLED) {
1465789Sahrens 		cntcmd = ACE_GETACLCNT;
1466789Sahrens 		getcmd = ACE_GETACL;
1467789Sahrens 		acl_info = acl_alloc(ACE_T);
1468789Sahrens 	} else {
1469789Sahrens 		cntcmd = GETACLCNT;
1470789Sahrens 		getcmd = GETACL;
1471789Sahrens 		acl_info = acl_alloc(ACLENT_T);
1472789Sahrens 	}
1473789Sahrens 
1474789Sahrens 	if (acl_info == NULL)
1475789Sahrens 		return (-1);
1476789Sahrens 
1477789Sahrens 	if (type == ACL_PATH) {
1478789Sahrens 		acl_info->acl_cnt = acl(fname, cntcmd, 0, NULL);
1479789Sahrens 	} else {
1480789Sahrens 		acl_info->acl_cnt = facl(fd, cntcmd, 0, NULL);
1481789Sahrens 	}
1482789Sahrens 
1483789Sahrens 	save_errno = errno;
1484789Sahrens 	if (acl_info->acl_cnt < 0) {
1485789Sahrens 		acl_free(acl_info);
1486789Sahrens 		errno = save_errno;
1487789Sahrens 		return (-1);
1488789Sahrens 	}
1489789Sahrens 
1490789Sahrens 	if (acl_info->acl_cnt == 0) {
1491789Sahrens 		acl_free(acl_info);
1492789Sahrens 		errno = save_errno;
1493789Sahrens 		return (0);
1494789Sahrens 	}
1495789Sahrens 
1496789Sahrens 	acl_info->acl_aclp =
1497789Sahrens 	    malloc(acl_info->acl_cnt * acl_info->acl_entry_size);
1498789Sahrens 	save_errno = errno;
1499789Sahrens 
1500789Sahrens 	if (acl_info->acl_aclp == NULL) {
1501789Sahrens 		acl_free(acl_info);
1502789Sahrens 		errno = save_errno;
1503789Sahrens 		return (-1);
1504789Sahrens 	}
1505789Sahrens 
1506789Sahrens 	if (type == ACL_PATH) {
1507789Sahrens 		stat_error = stat64(fname, &statbuf);
1508789Sahrens 		error = acl(fname, getcmd, acl_info->acl_cnt,
1509789Sahrens 		    acl_info->acl_aclp);
1510789Sahrens 	} else {
1511789Sahrens 		stat_error = fstat64(fd, &statbuf);
1512789Sahrens 		error = facl(fd, getcmd, acl_info->acl_cnt,
1513789Sahrens 		    acl_info->acl_aclp);
1514789Sahrens 	}
1515789Sahrens 
1516789Sahrens 	save_errno = errno;
1517789Sahrens 	if (error == -1) {
1518789Sahrens 		acl_free(acl_info);
1519789Sahrens 		errno = save_errno;
1520789Sahrens 		return (-1);
1521789Sahrens 	}
1522789Sahrens 
1523789Sahrens 
1524789Sahrens 	if (stat_error == 0) {
1525789Sahrens 		acl_info->acl_flags =
1526789Sahrens 		    (S_ISDIR(statbuf.st_mode) ? ACL_IS_DIR : 0);
1527789Sahrens 	} else
1528789Sahrens 		acl_info->acl_flags = 0;
1529789Sahrens 
1530789Sahrens 	switch (acl_info->acl_type) {
1531789Sahrens 	case ACLENT_T:
1532789Sahrens 		if (acl_info->acl_cnt <= MIN_ACL_ENTRIES)
1533789Sahrens 			acl_info->acl_flags |= ACL_IS_TRIVIAL;
1534789Sahrens 		break;
1535789Sahrens 	case ACE_T:
1536789Sahrens 		if (ace_trivial(acl_info->acl_aclp, acl_info->acl_cnt) == 0)
1537789Sahrens 			acl_info->acl_flags |= ACL_IS_TRIVIAL;
1538789Sahrens 		break;
1539789Sahrens 	default:
1540789Sahrens 		errno = EINVAL;
1541789Sahrens 		acl_free(acl_info);
1542789Sahrens 		return (-1);
1543789Sahrens 	}
1544789Sahrens 
1545789Sahrens 	if ((acl_info->acl_flags & ACL_IS_TRIVIAL) &&
1546789Sahrens 	    (get_flag & ACL_NO_TRIVIAL)) {
1547789Sahrens 		acl_free(acl_info);
1548789Sahrens 		errno = 0;
1549789Sahrens 		return (0);
1550789Sahrens 	}
1551789Sahrens 
1552789Sahrens 	*aclp = acl_info;
1553789Sahrens 	return (0);
1554789Sahrens }
1555789Sahrens 
1556789Sahrens /*
1557789Sahrens  * return -1 on failure, otherwise the number of acl
1558789Sahrens  * entries is returned
1559789Sahrens  */
1560789Sahrens int
1561789Sahrens acl_get(const char *path, int get_flag, acl_t **aclp)
1562789Sahrens {
1563789Sahrens 	acl_inp acl_inp;
1564789Sahrens 	acl_inp.file = path;
1565789Sahrens 
1566789Sahrens 	return (cacl_get(acl_inp, get_flag, ACL_PATH, aclp));
1567789Sahrens }
1568789Sahrens 
1569789Sahrens int
1570789Sahrens facl_get(int fd, int get_flag, acl_t **aclp)
1571789Sahrens {
1572789Sahrens 
1573789Sahrens 	acl_inp acl_inp;
1574789Sahrens 	acl_inp.fd = fd;
1575789Sahrens 
1576789Sahrens 	return (cacl_get(acl_inp, get_flag, ACL_FD, aclp));
1577789Sahrens }
1578789Sahrens 
15791462Smarks static int
15801462Smarks acl_translate(acl_t *aclp, int target_flavor, int isdir, uid_t owner,
15811462Smarks     gid_t group)
15821462Smarks {
15831462Smarks 	int aclcnt;
15841462Smarks 	void *acldata;
15851462Smarks 	int error;
15861462Smarks 
15871462Smarks 	/*
15881462Smarks 	 * See if we need to translate
15891462Smarks 	 */
15901462Smarks 	if ((target_flavor == _ACL_ACE_ENABLED && aclp->acl_type == ACE_T) ||
15911462Smarks 	    (target_flavor == _ACL_ACLENT_ENABLED &&
15921462Smarks 	    aclp->acl_type == ACLENT_T))
15931462Smarks 		return (0);
15941462Smarks 
15951462Smarks 	if (target_flavor == -1)
15961462Smarks 		return (-1);
15971462Smarks 
15981462Smarks 	if (target_flavor ==  _ACL_ACE_ENABLED &&
15991462Smarks 	    aclp->acl_type == ACLENT_T) {
16001462Smarks 		error = convert_aent_to_ace(aclp->acl_aclp,
16011462Smarks 		    aclp->acl_cnt, isdir, (ace_t **)&acldata, &aclcnt);
16021462Smarks 		if (error) {
16031462Smarks 			errno = error;
16041462Smarks 			return (-1);
16051462Smarks 		}
16061462Smarks 	} else if (target_flavor == _ACL_ACLENT_ENABLED &&
16071462Smarks 	    aclp->acl_type == ACE_T) {
16081462Smarks 		error = convert_ace_to_aent(aclp->acl_aclp, aclp->acl_cnt,
16091462Smarks 		    isdir, owner, group, (aclent_t **)&acldata, &aclcnt);
16101462Smarks 		if (error) {
16111462Smarks 			errno = error;
16121462Smarks 			return (-1);
16131462Smarks 		}
16141462Smarks 	} else {
16151462Smarks 		errno = ENOTSUP;
16161462Smarks 		return (-1);
16171462Smarks 	}
16181462Smarks 
16191462Smarks 	/*
16201462Smarks 	 * replace old acl with newly translated acl
16211462Smarks 	 */
16221462Smarks 	free(aclp->acl_aclp);
16231462Smarks 	aclp->acl_aclp = acldata;
16241462Smarks 	aclp->acl_cnt = aclcnt;
16251462Smarks 	aclp->acl_type = (target_flavor == _ACL_ACE_ENABLED) ? ACE_T : ACLENT_T;
16261462Smarks 	return (0);
16271462Smarks }
16281462Smarks 
1629789Sahrens /*
1630789Sahrens  * Set an ACL, translates acl to ace_t when appropriate.
1631789Sahrens  */
1632789Sahrens static int
1633789Sahrens cacl_set(acl_inp *acl_inp, acl_t *aclp, int type)
1634789Sahrens {
1635789Sahrens 	int error = 0;
1636789Sahrens 	int acl_flavor_target;
1637789Sahrens 	struct stat64 statbuf;
1638789Sahrens 	int stat_error;
1639789Sahrens 	int isdir;
1640789Sahrens 
1641789Sahrens 
1642789Sahrens 	if (type == ACL_PATH) {
1643789Sahrens 		stat_error = stat64(acl_inp->file, &statbuf);
1644789Sahrens 		if (stat_error)
1645789Sahrens 			return (-1);
1646789Sahrens 		acl_flavor_target = pathconf(acl_inp->file, _PC_ACL_ENABLED);
1647789Sahrens 	} else {
1648789Sahrens 		stat_error = fstat64(acl_inp->fd, &statbuf);
1649789Sahrens 		if (stat_error)
1650789Sahrens 			return (-1);
1651789Sahrens 		acl_flavor_target = fpathconf(acl_inp->fd, _PC_ACL_ENABLED);
1652789Sahrens 	}
1653789Sahrens 
16541666Smarks 	/*
16551666Smarks 	 * If target returns an error or 0 from pathconf call then
16561666Smarks 	 * fall back to UFS/POSIX Draft interface.
16571666Smarks 	 * In the case of 0 we will then fail in either acl(2) or
16581666Smarks 	 * acl_translate().  We could erroneously get 0 back from
16591666Smarks 	 * a file system that is using fs_pathconf() and not answering
16601666Smarks 	 * the _PC_ACL_ENABLED question itself.
16611666Smarks 	 */
16621666Smarks 	if (acl_flavor_target == 0 || acl_flavor_target == -1)
16631666Smarks 		acl_flavor_target = _ACL_ACLENT_ENABLED;
16641666Smarks 
1665789Sahrens 	isdir = S_ISDIR(statbuf.st_mode);
1666789Sahrens 
16671462Smarks 	if ((error = acl_translate(aclp, acl_flavor_target, isdir,
16681462Smarks 	    statbuf.st_uid, statbuf.st_gid)) != 0) {
16691462Smarks 		return (error);
1670789Sahrens 	}
1671789Sahrens 
1672789Sahrens 	if (type == ACL_PATH) {
1673789Sahrens 		error = acl(acl_inp->file,
1674789Sahrens 		    (aclp->acl_type == ACE_T) ? ACE_SETACL : SETACL,
1675789Sahrens 		    aclp->acl_cnt, aclp->acl_aclp);
1676789Sahrens 	} else {
1677789Sahrens 		error = facl(acl_inp->fd,
1678789Sahrens 		    (aclp->acl_type == ACE_T) ? ACE_SETACL : SETACL,
1679789Sahrens 		    aclp->acl_cnt, aclp->acl_aclp);
1680789Sahrens 	}
1681789Sahrens 
1682789Sahrens 	return (error);
1683789Sahrens }
1684789Sahrens 
1685789Sahrens int
1686789Sahrens acl_set(const char *path, acl_t *aclp)
1687789Sahrens {
1688789Sahrens 	acl_inp acl_inp;
1689789Sahrens 
1690789Sahrens 	acl_inp.file = path;
1691789Sahrens 
1692789Sahrens 	return (cacl_set(&acl_inp, aclp, ACL_PATH));
1693789Sahrens }
1694789Sahrens 
1695789Sahrens int
1696789Sahrens facl_set(int fd, acl_t *aclp)
1697789Sahrens {
1698789Sahrens 	acl_inp acl_inp;
1699789Sahrens 
1700789Sahrens 	acl_inp.fd = fd;
1701789Sahrens 
1702789Sahrens 	return (cacl_set(&acl_inp, aclp, ACL_FD));
1703789Sahrens }
1704789Sahrens 
1705789Sahrens int
1706789Sahrens acl_cnt(acl_t *aclp)
1707789Sahrens {
1708789Sahrens 	return (aclp->acl_cnt);
1709789Sahrens }
1710789Sahrens 
1711789Sahrens int
1712789Sahrens acl_type(acl_t *aclp)
1713789Sahrens {
1714789Sahrens 	return (aclp->acl_type);
1715789Sahrens }
1716789Sahrens 
1717789Sahrens acl_t *
1718789Sahrens acl_dup(acl_t *aclp)
1719789Sahrens {
1720789Sahrens 	acl_t *newaclp;
1721789Sahrens 
1722789Sahrens 	newaclp = acl_alloc(aclp->acl_type);
1723789Sahrens 	if (newaclp == NULL)
1724789Sahrens 		return (NULL);
1725789Sahrens 
1726789Sahrens 	newaclp->acl_aclp = malloc(aclp->acl_entry_size * aclp->acl_cnt);
1727789Sahrens 	if (newaclp->acl_aclp == NULL) {
1728789Sahrens 		acl_free(newaclp);
1729789Sahrens 		return (NULL);
1730789Sahrens 	}
1731789Sahrens 
1732789Sahrens 	(void) memcpy(newaclp->acl_aclp,
1733789Sahrens 	    aclp->acl_aclp, aclp->acl_entry_size * aclp->acl_cnt);
1734789Sahrens 	newaclp->acl_cnt = aclp->acl_cnt;
1735789Sahrens 
1736789Sahrens 	return (newaclp);
1737789Sahrens }
1738789Sahrens 
1739789Sahrens int
1740789Sahrens acl_flags(acl_t *aclp)
1741789Sahrens {
1742789Sahrens 	return (aclp->acl_flags);
1743789Sahrens }
1744789Sahrens 
1745789Sahrens void *
1746789Sahrens acl_data(acl_t *aclp)
1747789Sahrens {
1748789Sahrens 	return (aclp->acl_aclp);
1749789Sahrens }
1750789Sahrens 
1751789Sahrens /*
1752*1953Smarks  * Take an acl array and build an acl_t.
1753*1953Smarks  */
1754*1953Smarks acl_t *
1755*1953Smarks acl_to_aclp(enum acl_type type, void *acl, int count)
1756*1953Smarks {
1757*1953Smarks 	acl_t *aclp;
1758*1953Smarks 
1759*1953Smarks 
1760*1953Smarks 	aclp = acl_alloc(type);
1761*1953Smarks 	if (aclp == NULL)
1762*1953Smarks 		return (aclp);
1763*1953Smarks 
1764*1953Smarks 	aclp->acl_aclp = acl;
1765*1953Smarks 	aclp->acl_cnt = count;
1766*1953Smarks 
1767*1953Smarks 	return (aclp);
1768*1953Smarks }
1769*1953Smarks 
1770*1953Smarks /*
1771789Sahrens  * Remove an ACL from a file and create a trivial ACL based
1772789Sahrens  * off of the mode argument.  After acl has been set owner/group
1773789Sahrens  * are updated to match owner,group arguments
1774789Sahrens  */
1775789Sahrens int
1776789Sahrens acl_strip(const char *file, uid_t owner, gid_t group, mode_t mode)
1777789Sahrens {
1778789Sahrens 	int	error = 0;
1779789Sahrens 	aclent_t min_acl[MIN_ACL_ENTRIES];
1780789Sahrens 	ace_t	min_ace_acl[6];	/* owner, group, everyone + complement denies */
1781789Sahrens 	int	acl_flavor;
1782789Sahrens 	int	aclcnt;
1783789Sahrens 
1784789Sahrens 	acl_flavor = pathconf(file, _PC_ACL_ENABLED);
1785789Sahrens 
1786789Sahrens 	/*
1787789Sahrens 	 * force it through aclent flavor when file system doesn't
1788789Sahrens 	 * understand question
1789789Sahrens 	 */
17901666Smarks 	if (acl_flavor == 0 || acl_flavor == -1)
1791789Sahrens 		acl_flavor = _ACL_ACLENT_ENABLED;
1792789Sahrens 
1793789Sahrens 	if (acl_flavor & _ACL_ACLENT_ENABLED) {
1794789Sahrens 		min_acl[0].a_type = USER_OBJ;
1795789Sahrens 		min_acl[0].a_id   = owner;
1796789Sahrens 		min_acl[0].a_perm = ((mode & 0700) >> 6);
1797789Sahrens 		min_acl[1].a_type = GROUP_OBJ;
1798789Sahrens 		min_acl[1].a_id   = group;
1799789Sahrens 		min_acl[1].a_perm = ((mode & 0070) >> 3);
1800789Sahrens 		min_acl[2].a_type = CLASS_OBJ;
1801789Sahrens 		min_acl[2].a_id   = (uid_t)-1;
1802789Sahrens 		min_acl[2].a_perm = ((mode & 0070) >> 3);
1803789Sahrens 		min_acl[3].a_type = OTHER_OBJ;
1804789Sahrens 		min_acl[3].a_id   = (uid_t)-1;
1805789Sahrens 		min_acl[3].a_perm = (mode & 0007);
1806789Sahrens 		aclcnt = 4;
1807789Sahrens 		error = acl(file, SETACL, aclcnt, min_acl);
1808789Sahrens 	} else if (acl_flavor & _ACL_ACE_ENABLED) {
1809789Sahrens 		(void) memcpy(min_ace_acl, trivial_acl, sizeof (ace_t) * 6);
1810789Sahrens 
1811789Sahrens 		/*
1812789Sahrens 		 * Make aces match request mode
1813789Sahrens 		 */
1814789Sahrens 		adjust_ace_pair(&min_ace_acl[0], (mode & 0700) >> 6);
1815789Sahrens 		adjust_ace_pair(&min_ace_acl[2], (mode & 0070) >> 3);
1816789Sahrens 		adjust_ace_pair(&min_ace_acl[4], mode & 0007);
1817789Sahrens 
1818789Sahrens 		error = acl(file, ACE_SETACL, 6, min_ace_acl);
1819789Sahrens 	} else {
1820789Sahrens 		errno = EINVAL;
1821789Sahrens 		error = 1;
1822789Sahrens 	}
1823789Sahrens 
1824789Sahrens 	if (error == 0)
1825789Sahrens 		error = chown(file, owner, group);
1826789Sahrens 	return (error);
1827789Sahrens }
1828789Sahrens 
1829789Sahrens static int
1830789Sahrens ace_match(void *entry1, void *entry2)
1831789Sahrens {
1832789Sahrens 	ace_t *p1 = (ace_t *)entry1;
1833789Sahrens 	ace_t *p2 = (ace_t *)entry2;
1834789Sahrens 	ace_t ace1, ace2;
1835789Sahrens 
1836789Sahrens 	ace1 = *p1;
1837789Sahrens 	ace2 = *p2;
1838789Sahrens 
1839789Sahrens 	/*
1840789Sahrens 	 * Need to fixup who field for abstrations for
1841789Sahrens 	 * accurate comparison, since field is undefined.
1842789Sahrens 	 */
1843789Sahrens 	if (ace1.a_flags & (ACE_OWNER|ACE_GROUP|ACE_EVERYONE))
1844789Sahrens 		ace1.a_who = -1;
1845789Sahrens 	if (ace2.a_flags & (ACE_OWNER|ACE_GROUP|ACE_EVERYONE))
1846789Sahrens 		ace2.a_who = -1;
1847789Sahrens 	return (memcmp(&ace1, &ace2, sizeof (ace_t)));
1848789Sahrens }
1849789Sahrens 
1850789Sahrens static int
1851789Sahrens aclent_match(void *entry1, void *entry2)
1852789Sahrens {
1853789Sahrens 	aclent_t *aclent1 = (aclent_t *)entry1;
1854789Sahrens 	aclent_t *aclent2 = (aclent_t *)entry2;
1855789Sahrens 
1856789Sahrens 	return (memcmp(aclent1, aclent2, sizeof (aclent_t)));
1857789Sahrens }
1858789Sahrens 
1859789Sahrens /*
1860789Sahrens  * Find acl entries in acl that correspond to removeacl.  Search
1861789Sahrens  * is started from slot.  The flag argument indicates whether to
1862789Sahrens  * remove all matches or just the first match.
1863789Sahrens  */
1864789Sahrens int
1865789Sahrens acl_removeentries(acl_t *acl, acl_t *removeacl, int start_slot, int flag)
1866789Sahrens {
1867789Sahrens 	int i, j;
1868789Sahrens 	int match;
1869789Sahrens 	int (*acl_match)(void *acl1, void *acl2);
1870789Sahrens 	void *acl_entry, *remove_entry;
1871789Sahrens 	void *start;
1872789Sahrens 	int found = 0;
1873789Sahrens 
1874789Sahrens 	if (flag != ACL_REMOVE_ALL && flag != ACL_REMOVE_FIRST)
1875789Sahrens 		flag = ACL_REMOVE_FIRST;
1876789Sahrens 
1877789Sahrens 	if (acl == NULL || removeacl == NULL)
1878789Sahrens 		return (EACL_NO_ACL_ENTRY);
1879789Sahrens 
1880789Sahrens 	if (acl->acl_type != removeacl->acl_type)
1881789Sahrens 		return (EACL_DIFF_TYPE);
1882789Sahrens 
1883789Sahrens 	if (acl->acl_type == ACLENT_T)
1884789Sahrens 		acl_match = aclent_match;
1885789Sahrens 	else
1886789Sahrens 		acl_match = ace_match;
1887789Sahrens 
1888789Sahrens 	for (i = 0, remove_entry = removeacl->acl_aclp;
1889789Sahrens 	    i != removeacl->acl_cnt; i++) {
1890789Sahrens 
1891789Sahrens 		j = 0;
1892789Sahrens 		acl_entry = (char *)acl->acl_aclp +
1893789Sahrens 		    (acl->acl_entry_size * start_slot);
1894789Sahrens 		for (;;) {
1895789Sahrens 			match = acl_match(acl_entry, remove_entry);
1896789Sahrens 			if (match == 0)  {
1897789Sahrens 				found++;
1898789Sahrens 				start = (char *)acl_entry +
1899789Sahrens 				    acl->acl_entry_size;
1900789Sahrens 				(void) memmove(acl_entry, start,
1901789Sahrens 				    acl->acl_entry_size *
1902789Sahrens 				    acl->acl_cnt-- - (j + 1));
1903789Sahrens 
1904789Sahrens 				if (flag == ACL_REMOVE_FIRST)
1905789Sahrens 					break;
1906789Sahrens 				/*
1907789Sahrens 				 * List has changed, restart search from
1908789Sahrens 				 * beginning.
1909789Sahrens 				 */
1910789Sahrens 				acl_entry = acl->acl_aclp;
1911789Sahrens 				j = 0;
1912789Sahrens 				continue;
1913789Sahrens 			}
1914789Sahrens 			acl_entry = ((char *)acl_entry + acl->acl_entry_size);
1915789Sahrens 			if (++j >= acl->acl_cnt) {
1916789Sahrens 				break;
1917789Sahrens 			}
1918789Sahrens 		}
1919789Sahrens 	}
1920789Sahrens 
1921789Sahrens 	return ((found == 0) ? EACL_NO_ACL_ENTRY : 0);
1922789Sahrens }
1923789Sahrens 
1924789Sahrens /*
1925789Sahrens  * Replace entires entries in acl1 with the corresponding entries
1926789Sahrens  * in newentries.  The where argument specifies where to begin
1927789Sahrens  * the replacement.  If the where argument is 1 greater than the
1928789Sahrens  * number of acl entries in acl1 then they are appended.  If the
1929789Sahrens  * where argument is 2+ greater than the number of acl entries then
1930789Sahrens  * EACL_INVALID_SLOT is returned.
1931789Sahrens  */
1932789Sahrens int
1933789Sahrens acl_modifyentries(acl_t *acl1, acl_t *newentries, int where)
1934789Sahrens {
1935789Sahrens 
1936789Sahrens 	int slot;
1937789Sahrens 	int slots_needed;
1938789Sahrens 	int slots_left;
1939789Sahrens 	int newsize;
1940789Sahrens 
1941789Sahrens 	if (acl1 == NULL || newentries == NULL)
1942789Sahrens 		return (EACL_NO_ACL_ENTRY);
1943789Sahrens 
1944789Sahrens 	if (where < 0 || where >= acl1->acl_cnt)
1945789Sahrens 		return (EACL_INVALID_SLOT);
1946789Sahrens 
1947789Sahrens 	if (acl1->acl_type != newentries->acl_type)
1948789Sahrens 		return (EACL_DIFF_TYPE);
1949789Sahrens 
1950789Sahrens 	slot = where;
1951789Sahrens 
1952789Sahrens 	slots_left = acl1->acl_cnt - slot + 1;
1953789Sahrens 	if (slots_left < newentries->acl_cnt) {
1954789Sahrens 		slots_needed = newentries->acl_cnt - slots_left;
1955789Sahrens 		newsize = (acl1->acl_entry_size * acl1->acl_cnt) +
1956789Sahrens 		    (acl1->acl_entry_size * slots_needed);
1957789Sahrens 		acl1->acl_aclp = realloc(acl1->acl_aclp, newsize);
1958789Sahrens 		if (acl1->acl_aclp == NULL)
1959789Sahrens 			return (-1);
1960789Sahrens 	}
1961789Sahrens 	(void) memcpy((char *)acl1->acl_aclp + (acl1->acl_entry_size * slot),
1962789Sahrens 	    newentries->acl_aclp,
1963789Sahrens 	    newentries->acl_entry_size * newentries->acl_cnt);
1964789Sahrens 
1965789Sahrens 	/*
1966789Sahrens 	 * Did ACL grow?
1967789Sahrens 	 */
1968789Sahrens 
1969789Sahrens 	if ((slot + newentries->acl_cnt) > acl1->acl_cnt) {
1970789Sahrens 		acl1->acl_cnt = slot + newentries->acl_cnt;
1971789Sahrens 	}
1972789Sahrens 
1973789Sahrens 	return (0);
1974789Sahrens }
1975789Sahrens 
1976789Sahrens /*
1977789Sahrens  * Add acl2 entries into acl1.  The where argument specifies where
1978789Sahrens  * to add the entries.
1979789Sahrens  */
1980789Sahrens int
1981789Sahrens acl_addentries(acl_t *acl1, acl_t *acl2, int where)
1982789Sahrens {
1983789Sahrens 
1984789Sahrens 	int newsize;
1985789Sahrens 	int len;
1986789Sahrens 	void *start;
1987789Sahrens 	void *to;
1988789Sahrens 
1989789Sahrens 	if (acl1 == NULL || acl2 == NULL)
1990789Sahrens 		return (EACL_NO_ACL_ENTRY);
1991789Sahrens 
1992789Sahrens 	if (acl1->acl_type != acl2->acl_type)
1993789Sahrens 		return (EACL_DIFF_TYPE);
1994789Sahrens 
1995789Sahrens 	/*
1996789Sahrens 	 * allow where to specify 1 past last slot for an append operation
1997789Sahrens 	 * but anything greater is an error.
1998789Sahrens 	 */
1999789Sahrens 	if (where < 0 || where > acl1->acl_cnt)
2000789Sahrens 		return (EACL_INVALID_SLOT);
2001789Sahrens 
2002789Sahrens 	newsize = (acl2->acl_entry_size * acl2->acl_cnt) +
2003789Sahrens 	    (acl1->acl_entry_size * acl1->acl_cnt);
2004789Sahrens 	acl1->acl_aclp = realloc(acl1->acl_aclp, newsize);
2005789Sahrens 	if (acl1->acl_aclp == NULL)
2006789Sahrens 		return (-1);
2007789Sahrens 
2008789Sahrens 	/*
2009789Sahrens 	 * first push down entries where new ones will be inserted
2010789Sahrens 	 */
2011789Sahrens 
2012789Sahrens 	to = (void *)((char *)acl1->acl_aclp +
2013789Sahrens 	    ((where + acl2->acl_cnt) * acl1->acl_entry_size));
2014789Sahrens 
2015789Sahrens 	start = (void *)((char *)acl1->acl_aclp +
2016789Sahrens 	    where * acl1->acl_entry_size);
2017789Sahrens 
2018789Sahrens 	if (where < acl1->acl_cnt) {
2019789Sahrens 		len = (acl1->acl_cnt - where) * acl1->acl_entry_size;
2020789Sahrens 		(void) memmove(to, start, len);
2021789Sahrens 	}
2022789Sahrens 
2023789Sahrens 	/*
2024789Sahrens 	 * now stick in new entries.
2025789Sahrens 	 */
2026789Sahrens 
2027789Sahrens 	(void) memmove(start, acl2->acl_aclp,
2028789Sahrens 	    acl2->acl_cnt * acl2->acl_entry_size);
2029789Sahrens 
2030789Sahrens 	acl1->acl_cnt += acl2->acl_cnt;
2031789Sahrens 	return (0);
2032789Sahrens }
2033789Sahrens 
2034789Sahrens /*
2035789Sahrens  * return text for an ACL error.
2036789Sahrens  */
2037789Sahrens char *
2038789Sahrens acl_strerror(int errnum)
2039789Sahrens {
2040789Sahrens 	switch (errnum) {
2041789Sahrens 	case EACL_GRP_ERROR:
2042789Sahrens 		return (dgettext(TEXT_DOMAIN,
20431420Smarks 		    "There is more than one group or default group entry"));
2044789Sahrens 	case EACL_USER_ERROR:
2045789Sahrens 		return (dgettext(TEXT_DOMAIN,
20461420Smarks 		    "There is more than one user or default user entry"));
2047789Sahrens 	case EACL_OTHER_ERROR:
2048789Sahrens 		return (dgettext(TEXT_DOMAIN,
2049789Sahrens 		    "There is more than one other entry"));
2050789Sahrens 	case EACL_CLASS_ERROR:
2051789Sahrens 		return (dgettext(TEXT_DOMAIN,
2052789Sahrens 		    "There is more than one mask entry"));
2053789Sahrens 	case EACL_DUPLICATE_ERROR:
2054789Sahrens 		return (dgettext(TEXT_DOMAIN,
2055789Sahrens 		    "Duplicate user or group entries"));
2056789Sahrens 	case EACL_MISS_ERROR:
2057789Sahrens 		return (dgettext(TEXT_DOMAIN,
2058789Sahrens 		    "Missing user/group owner, other, mask entry"));
2059789Sahrens 	case EACL_MEM_ERROR:
2060789Sahrens 		return (dgettext(TEXT_DOMAIN,
2061789Sahrens 		    "Memory error"));
2062789Sahrens 	case EACL_ENTRY_ERROR:
2063789Sahrens 		return (dgettext(TEXT_DOMAIN,
2064789Sahrens 		    "Unrecognized entry type"));
2065789Sahrens 	case EACL_INHERIT_ERROR:
2066789Sahrens 		return (dgettext(TEXT_DOMAIN,
2067789Sahrens 		    "Invalid inheritance flags"));
2068789Sahrens 	case EACL_FLAGS_ERROR:
2069789Sahrens 		return (dgettext(TEXT_DOMAIN,
2070789Sahrens 		    "Unrecognized entry flags"));
2071789Sahrens 	case EACL_PERM_MASK_ERROR:
2072789Sahrens 		return (dgettext(TEXT_DOMAIN,
2073789Sahrens 		    "Invalid ACL permissions"));
2074789Sahrens 	case EACL_COUNT_ERROR:
2075789Sahrens 		return (dgettext(TEXT_DOMAIN,
2076789Sahrens 		    "Invalid ACL count"));
2077789Sahrens 	case EACL_INVALID_SLOT:
2078789Sahrens 		return (dgettext(TEXT_DOMAIN,
2079789Sahrens 		    "Invalid ACL entry number specified"));
2080789Sahrens 	case EACL_NO_ACL_ENTRY:
2081789Sahrens 		return (dgettext(TEXT_DOMAIN,
2082789Sahrens 		    "ACL entry doesn't exist"));
2083789Sahrens 	case EACL_DIFF_TYPE:
2084789Sahrens 		return (dgettext(TEXT_DOMAIN,
2085789Sahrens 		    "ACL type's are different"));
2086789Sahrens 	case EACL_INVALID_USER_GROUP:
2087789Sahrens 		return (dgettext(TEXT_DOMAIN, "Invalid user or group"));
2088789Sahrens 	case EACL_INVALID_STR:
2089789Sahrens 		return (dgettext(TEXT_DOMAIN, "ACL string is invalid"));
2090789Sahrens 	case EACL_FIELD_NOT_BLANK:
2091789Sahrens 		return (dgettext(TEXT_DOMAIN, "Field expected to be blank"));
2092789Sahrens 	case EACL_INVALID_ACCESS_TYPE:
2093789Sahrens 		return (dgettext(TEXT_DOMAIN, "Invalid access type"));
2094789Sahrens 	case EACL_UNKNOWN_DATA:
2095789Sahrens 		return (dgettext(TEXT_DOMAIN, "Unrecognized entry"));
2096789Sahrens 	case EACL_MISSING_FIELDS:
2097789Sahrens 		return (dgettext(TEXT_DOMAIN,
2098789Sahrens 		    "ACL specification missing required fields"));
2099789Sahrens 	case EACL_INHERIT_NOTDIR:
2100789Sahrens 		return (dgettext(TEXT_DOMAIN,
2101789Sahrens 		    "Inheritance flags are only allowed on directories"));
2102789Sahrens 	case -1:
2103789Sahrens 		return (strerror(errno));
2104789Sahrens 	default:
2105789Sahrens 		errno = EINVAL;
2106789Sahrens 		return (dgettext(TEXT_DOMAIN, "Unknown error"));
2107789Sahrens 	}
2108789Sahrens }
21091420Smarks 
21101420Smarks extern int yyinteractive;
21111420Smarks 
21121420Smarks /* PRINTFLIKE1 */
21131420Smarks void
21141420Smarks acl_error(const char *fmt, ...)
21151420Smarks {
21161420Smarks 	va_list va;
21171420Smarks 
21181420Smarks 	if (yyinteractive == 0)
21191420Smarks 		return;
21201420Smarks 
21211420Smarks 	va_start(va, fmt);
21221420Smarks 	(void) vfprintf(stderr, fmt, va);
21231420Smarks 	va_end(va);
21241420Smarks }
2125