1789Sahrens /*
2789Sahrens  * CDDL HEADER START
3789Sahrens  *
4789Sahrens  * The contents of this file are subject to the terms of the
5789Sahrens  * Common Development and Distribution License, Version 1.0 only
6789Sahrens  * (the "License").  You may not use this file except in compliance
7789Sahrens  * with the License.
8789Sahrens  *
9789Sahrens  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10789Sahrens  * or http://www.opensolaris.org/os/licensing.
11789Sahrens  * See the License for the specific language governing permissions
12789Sahrens  * and limitations under the License.
13789Sahrens  *
14789Sahrens  * When distributing Covered Code, include this CDDL HEADER in each
15789Sahrens  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16789Sahrens  * If applicable, add the following below this CDDL HEADER, with the
17789Sahrens  * fields enclosed by brackets "[]" replaced with your own identifying
18789Sahrens  * information: Portions Copyright [yyyy] [name of copyright owner]
19789Sahrens  *
20789Sahrens  * CDDL HEADER END
21789Sahrens  */
22789Sahrens /*
231231Smarks  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24789Sahrens  * Use is subject to license terms.
25789Sahrens  */
26789Sahrens 
27789Sahrens #pragma ident	"%Z%%M%	%I%	%E% SMI"
28789Sahrens 
29789Sahrens #include <stdlib.h>
30789Sahrens #include <string.h>
31789Sahrens #include <unistd.h>
32789Sahrens #include <limits.h>
33789Sahrens #include <grp.h>
34789Sahrens #include <pwd.h>
35789Sahrens #include <sys/types.h>
36789Sahrens #include <sys/acl.h>
37789Sahrens #include <errno.h>
38789Sahrens #include <sys/stat.h>
39*1420Smarks #include <sys/varargs.h>
40789Sahrens #include <locale.h>
41789Sahrens #include <aclutils.h>
42789Sahrens #include <acl_common.h>
43789Sahrens 
44789Sahrens #define	ACL_PATH	0
45789Sahrens #define	ACL_FD		1
46789Sahrens 
47789Sahrens #define	ACE_POSIX_SUPPORTED_BITS (ACE_READ_DATA | \
48789Sahrens     ACE_WRITE_DATA | ACE_APPEND_DATA | ACE_EXECUTE | \
49789Sahrens     ACE_READ_ATTRIBUTES | ACE_READ_ACL | ACE_WRITE_ACL)
50789Sahrens 
51789Sahrens 
52789Sahrens #define	ACL_SYNCHRONIZE_SET_ALLOW		0x0000002
53789Sahrens #define	ACL_SYNCHRONIZE_SET_DENY		0x0000001
54789Sahrens 
55789Sahrens #define	ACL_WRITE_OWNER_SET_ALLOW		0x0000020
56789Sahrens #define	ACL_WRITE_OWNER_SET_DENY		0x0000010
57789Sahrens 
58789Sahrens #define	ACL_WRITE_ATTRS_OWNER_SET_ALLOW		0x0002000
59789Sahrens #define	ACL_WRITE_ATTRS_OWNER_SET_DENY		0x0001000
60789Sahrens 
61789Sahrens #define	ACL_WRITE_ATTRS_WRITER_SET_DENY		0x0010000
62789Sahrens 
63789Sahrens #define	ACL_DELETE_SET_ALLOW			0x0000200
64789Sahrens #define	ACL_DELETE_SET_DENY			0x0000100
65789Sahrens 
66789Sahrens #define	ACL_READ_NAMED_READER_SET_ALLOW		0x2000000
67789Sahrens 
68789Sahrens #define	ACL_WRITE_NAMED_WRITER_SET_ALLOW	0x0200000
69789Sahrens #define	ACL_WRITE_NAMED_WRITER_SET_DENY		0x0100000
70789Sahrens 
71789Sahrens #define	ACL_WRITE_ATTRS_OWNER_SET_ALLOW		0x0002000
72789Sahrens #define	ACL_WRITE_ATTRS_WRITER_SET_ALLOW	0x0020000
73789Sahrens 
74789Sahrens #define	ACL_WRITE_OWNER_ERR_DENY		0x0000040
75789Sahrens #define	ACL_READ_NAMED_READER_SET_DENY		0x1000000
761231Smarks 
77789Sahrens typedef union {
78789Sahrens 	const char *file;
79789Sahrens 	int  fd;
80789Sahrens } acl_inp;
81789Sahrens 
82789Sahrens acl_t *
83789Sahrens acl_alloc(enum acl_type type)
84789Sahrens {
85789Sahrens 	acl_t *aclp;
86789Sahrens 
87789Sahrens 	aclp = malloc(sizeof (acl_t));
88789Sahrens 
89789Sahrens 	if (aclp == NULL)
90789Sahrens 		return (NULL);
91789Sahrens 
92789Sahrens 	aclp->acl_aclp = NULL;
93789Sahrens 	aclp->acl_cnt = 0;
94789Sahrens 
95789Sahrens 	switch (type) {
96789Sahrens 	case ACE_T:
97789Sahrens 		aclp->acl_type = ACE_T;
98789Sahrens 		aclp->acl_entry_size = sizeof (ace_t);
99789Sahrens 		break;
100789Sahrens 	case ACLENT_T:
101789Sahrens 		aclp->acl_type = ACLENT_T;
102789Sahrens 		aclp->acl_entry_size = sizeof (aclent_t);
103789Sahrens 		break;
104789Sahrens 	default:
105789Sahrens 		acl_free(aclp);
106789Sahrens 		aclp = NULL;
107789Sahrens 	}
108789Sahrens 	return (aclp);
109789Sahrens }
110789Sahrens 
111789Sahrens /*
112789Sahrens  * Free acl_t structure
113789Sahrens  */
114789Sahrens void
115789Sahrens acl_free(acl_t *aclp)
116789Sahrens {
117789Sahrens 	if (aclp == NULL)
118789Sahrens 		return;
119789Sahrens 
120789Sahrens 	if (aclp->acl_aclp)
121789Sahrens 		free(aclp->acl_aclp);
122789Sahrens 	free(aclp);
123789Sahrens }
124789Sahrens 
125789Sahrens /*
126789Sahrens  * Determine whether a file has a trivial ACL
127789Sahrens  * returns: 	0 = trivial
128789Sahrens  *		1 = nontrivial
129789Sahrens  *		<0 some other system failure, such as ENOENT or EPERM
130789Sahrens  */
131789Sahrens int
132789Sahrens acl_trivial(const char *filename)
133789Sahrens {
134789Sahrens 	int acl_flavor;
135789Sahrens 	int aclcnt;
136789Sahrens 	int cntcmd;
137789Sahrens 	int val = 0;
138789Sahrens 	ace_t *acep;
139789Sahrens 
140789Sahrens 	acl_flavor = pathconf(filename, _PC_ACL_ENABLED);
141789Sahrens 	if (acl_flavor == -1)
142789Sahrens 		return (-1);
143789Sahrens 
144789Sahrens 	if (acl_flavor == _ACL_ACE_ENABLED)
145789Sahrens 		cntcmd = ACE_GETACLCNT;
146789Sahrens 	else
147789Sahrens 		cntcmd = GETACLCNT;
148789Sahrens 
149789Sahrens 	aclcnt = acl(filename, cntcmd, 0, NULL);
150789Sahrens 	if (aclcnt > 0) {
151789Sahrens 		if (acl_flavor == _ACL_ACE_ENABLED) {
1521231Smarks 			acep = malloc(sizeof (ace_t) * aclcnt);
1531231Smarks 			if (acep == NULL)
1541231Smarks 				return (-1);
1551231Smarks 			if (acl(filename, ACE_GETACL,
1561231Smarks 			    aclcnt, acep) < 0) {
1571231Smarks 				free(acep);
1581231Smarks 				return (-1);
1591231Smarks 			}
160789Sahrens 
1611231Smarks 			val = ace_trivial(acep, aclcnt);
1621231Smarks 			free(acep);
1631231Smarks 
164789Sahrens 		} else if (aclcnt > MIN_ACL_ENTRIES)
165789Sahrens 			val = 1;
166789Sahrens 	}
167789Sahrens 	return (val);
168789Sahrens }
169789Sahrens 
170789Sahrens static uint32_t
171789Sahrens access_mask_set(int haswriteperm, int hasreadperm, int isowner, int isallow)
172789Sahrens {
173789Sahrens 	uint32_t access_mask = 0;
174789Sahrens 	int acl_produce;
175789Sahrens 	int synchronize_set = 0, write_owner_set = 0;
176789Sahrens 	int delete_set = 0, write_attrs_set = 0;
177789Sahrens 	int read_named_set = 0, write_named_set = 0;
178789Sahrens 
179789Sahrens 	acl_produce = (ACL_SYNCHRONIZE_SET_ALLOW |
180789Sahrens 	    ACL_WRITE_ATTRS_OWNER_SET_ALLOW |
181789Sahrens 	    ACL_WRITE_ATTRS_WRITER_SET_DENY);
182789Sahrens 
183789Sahrens 	if (isallow) {
184789Sahrens 		synchronize_set = ACL_SYNCHRONIZE_SET_ALLOW;
185789Sahrens 		write_owner_set = ACL_WRITE_OWNER_SET_ALLOW;
186789Sahrens 		delete_set = ACL_DELETE_SET_ALLOW;
187789Sahrens 		if (hasreadperm)
188789Sahrens 			read_named_set = ACL_READ_NAMED_READER_SET_ALLOW;
189789Sahrens 		if (haswriteperm)
190789Sahrens 			write_named_set = ACL_WRITE_NAMED_WRITER_SET_ALLOW;
191789Sahrens 		if (isowner)
192789Sahrens 			write_attrs_set = ACL_WRITE_ATTRS_OWNER_SET_ALLOW;
193789Sahrens 		else if (haswriteperm)
194789Sahrens 			write_attrs_set = ACL_WRITE_ATTRS_WRITER_SET_ALLOW;
195789Sahrens 	} else {
196789Sahrens 
197789Sahrens 		synchronize_set = ACL_SYNCHRONIZE_SET_DENY;
198789Sahrens 		write_owner_set = ACL_WRITE_OWNER_SET_DENY;
199789Sahrens 		delete_set = ACL_DELETE_SET_DENY;
200789Sahrens 		if (hasreadperm)
201789Sahrens 			read_named_set = ACL_READ_NAMED_READER_SET_DENY;
202789Sahrens 		if (haswriteperm)
203789Sahrens 			write_named_set = ACL_WRITE_NAMED_WRITER_SET_DENY;
204789Sahrens 		if (isowner)
205789Sahrens 			write_attrs_set = ACL_WRITE_ATTRS_OWNER_SET_DENY;
206789Sahrens 		else if (haswriteperm)
207789Sahrens 			write_attrs_set = ACL_WRITE_ATTRS_WRITER_SET_DENY;
208789Sahrens 		else
209789Sahrens 			/*
210789Sahrens 			 * If the entity is not the owner and does not
211789Sahrens 			 * have write permissions ACE_WRITE_ATTRIBUTES will
212789Sahrens 			 * always go in the DENY ACE.
213789Sahrens 			 */
214789Sahrens 			access_mask |= ACE_WRITE_ATTRIBUTES;
215789Sahrens 	}
216789Sahrens 
217789Sahrens 	if (acl_produce & synchronize_set)
218789Sahrens 		access_mask |= ACE_SYNCHRONIZE;
219789Sahrens 	if (acl_produce & write_owner_set)
220789Sahrens 		access_mask |= ACE_WRITE_OWNER;
221789Sahrens 	if (acl_produce & delete_set)
222789Sahrens 		access_mask |= ACE_DELETE;
223789Sahrens 	if (acl_produce & write_attrs_set)
224789Sahrens 		access_mask |= ACE_WRITE_ATTRIBUTES;
225789Sahrens 	if (acl_produce & read_named_set)
226789Sahrens 		access_mask |= ACE_READ_NAMED_ATTRS;
227789Sahrens 	if (acl_produce & write_named_set)
228789Sahrens 		access_mask |= ACE_WRITE_NAMED_ATTRS;
229789Sahrens 
230789Sahrens 	return (access_mask);
231789Sahrens }
232789Sahrens 
233789Sahrens /*
234789Sahrens  * Given an mode_t, convert it into an access_mask as used
235789Sahrens  * by nfsace, assuming aclent_t -> nfsace semantics.
236789Sahrens  */
237789Sahrens static uint32_t
238789Sahrens mode_to_ace_access(mode_t mode, int isdir, int isowner, int isallow)
239789Sahrens {
240789Sahrens 	uint32_t access = 0;
241789Sahrens 	int haswriteperm = 0;
242789Sahrens 	int hasreadperm = 0;
243789Sahrens 
244789Sahrens 	if (isallow) {
245789Sahrens 		haswriteperm = (mode & 02);
246789Sahrens 		hasreadperm = (mode & 04);
247789Sahrens 	} else {
248789Sahrens 		haswriteperm = !(mode & 02);
249789Sahrens 		hasreadperm = !(mode & 04);
250789Sahrens 	}
251789Sahrens 
252789Sahrens 	/*
253789Sahrens 	 * The following call takes care of correctly setting the following
254789Sahrens 	 * mask bits in the access_mask:
255789Sahrens 	 * ACE_SYNCHRONIZE, ACE_WRITE_OWNER, ACE_DELETE,
256789Sahrens 	 * ACE_WRITE_ATTRIBUTES, ACE_WRITE_NAMED_ATTRS, ACE_READ_NAMED_ATTRS
257789Sahrens 	 */
258789Sahrens 	access = access_mask_set(haswriteperm, hasreadperm, isowner, isallow);
259789Sahrens 
260789Sahrens 	if (isallow) {
261789Sahrens 		access |= ACE_READ_ACL | ACE_READ_ATTRIBUTES;
262789Sahrens 		if (isowner)
263789Sahrens 			access |= ACE_WRITE_ACL;
264789Sahrens 	} else {
265789Sahrens 		if (! isowner)
266789Sahrens 			access |= ACE_WRITE_ACL;
267789Sahrens 	}
268789Sahrens 
269789Sahrens 	/* read */
270789Sahrens 	if (mode & 04) {
271789Sahrens 		access |= ACE_READ_DATA;
272789Sahrens 	}
273789Sahrens 	/* write */
274789Sahrens 	if (mode & 02) {
275789Sahrens 		access |= ACE_WRITE_DATA |
276789Sahrens 		    ACE_APPEND_DATA;
277789Sahrens 		if (isdir)
278789Sahrens 			access |= ACE_DELETE_CHILD;
279789Sahrens 	}
280789Sahrens 	/* exec */
281789Sahrens 	if (mode & 01) {
282789Sahrens 		access |= ACE_EXECUTE;
283789Sahrens 	}
284789Sahrens 
285789Sahrens 	return (access);
286789Sahrens }
287789Sahrens 
288789Sahrens /*
289789Sahrens  * Given an nfsace (presumably an ALLOW entry), make a
290789Sahrens  * corresponding DENY entry at the address given.
291789Sahrens  */
292789Sahrens static void
293789Sahrens ace_make_deny(ace_t *allow, ace_t *deny, int isdir, int isowner)
294789Sahrens {
295789Sahrens 	(void) memcpy(deny, allow, sizeof (ace_t));
296789Sahrens 
297789Sahrens 	deny->a_who = allow->a_who;
298789Sahrens 
299789Sahrens 	deny->a_type = ACE_ACCESS_DENIED_ACE_TYPE;
300789Sahrens 	deny->a_access_mask ^= ACE_POSIX_SUPPORTED_BITS;
301789Sahrens 	if (isdir)
302789Sahrens 		deny->a_access_mask ^= ACE_DELETE_CHILD;
303789Sahrens 
304789Sahrens 	deny->a_access_mask &= ~(ACE_SYNCHRONIZE | ACE_WRITE_OWNER |
305789Sahrens 	    ACE_DELETE | ACE_WRITE_ATTRIBUTES | ACE_READ_NAMED_ATTRS |
306789Sahrens 	    ACE_WRITE_NAMED_ATTRS);
307789Sahrens 	deny->a_access_mask |= access_mask_set((allow->a_access_mask &
308789Sahrens 	    ACE_WRITE_DATA), (allow->a_access_mask & ACE_READ_DATA), isowner,
309789Sahrens 	    B_FALSE);
310789Sahrens }
311789Sahrens /*
312789Sahrens  * Make an initial pass over an array of aclent_t's.  Gather
313789Sahrens  * information such as an ACL_MASK (if any), number of users,
314789Sahrens  * number of groups, and whether the array needs to be sorted.
315789Sahrens  */
316789Sahrens static int
317789Sahrens ln_aent_preprocess(aclent_t *aclent, int n,
318789Sahrens     int *hasmask, mode_t *mask,
319789Sahrens     int *numuser, int *numgroup, int *needsort)
320789Sahrens {
321789Sahrens 	int error = 0;
322789Sahrens 	int i;
323789Sahrens 	int curtype = 0;
324789Sahrens 
325789Sahrens 	*hasmask = 0;
326789Sahrens 	*mask = 07;
327789Sahrens 	*needsort = 0;
328789Sahrens 	*numuser = 0;
329789Sahrens 	*numgroup = 0;
330789Sahrens 
331789Sahrens 	for (i = 0; i < n; i++) {
332789Sahrens 		if (aclent[i].a_type < curtype)
333789Sahrens 			*needsort = 1;
334789Sahrens 		else if (aclent[i].a_type > curtype)
335789Sahrens 			curtype = aclent[i].a_type;
336789Sahrens 		if (aclent[i].a_type & USER)
337789Sahrens 			(*numuser)++;
338789Sahrens 		if (aclent[i].a_type & (GROUP | GROUP_OBJ))
339789Sahrens 			(*numgroup)++;
340789Sahrens 		if (aclent[i].a_type & CLASS_OBJ) {
341789Sahrens 			if (*hasmask) {
342789Sahrens 				error = EINVAL;
343789Sahrens 				goto out;
344789Sahrens 			} else {
345789Sahrens 				*hasmask = 1;
346789Sahrens 				*mask = aclent[i].a_perm;
347789Sahrens 			}
348789Sahrens 		}
349789Sahrens 	}
350789Sahrens 
351789Sahrens 	if ((! *hasmask) && (*numuser + *numgroup > 1)) {
352789Sahrens 		error = EINVAL;
353789Sahrens 		goto out;
354789Sahrens 	}
355789Sahrens 
356789Sahrens out:
357789Sahrens 	return (error);
358789Sahrens }
359789Sahrens 
360789Sahrens /*
361789Sahrens  * Convert an array of aclent_t into an array of nfsace entries,
362789Sahrens  * following POSIX draft -> nfsv4 conversion semantics as outlined in
363789Sahrens  * the IETF draft.
364789Sahrens  */
365789Sahrens static int
366789Sahrens ln_aent_to_ace(aclent_t *aclent, int n, ace_t **acepp, int *rescount, int isdir)
367789Sahrens {
368789Sahrens 	int error = 0;
369789Sahrens 	mode_t mask;
370789Sahrens 	int numuser, numgroup, needsort;
371789Sahrens 	int resultsize = 0;
372789Sahrens 	int i, groupi = 0, skip;
373789Sahrens 	ace_t *acep, *result = NULL;
374789Sahrens 	int hasmask;
375789Sahrens 
376789Sahrens 	error = ln_aent_preprocess(aclent, n, &hasmask, &mask,
377789Sahrens 	    &numuser, &numgroup, &needsort);
378789Sahrens 	if (error != 0)
379789Sahrens 		goto out;
380789Sahrens 
381789Sahrens 	/* allow + deny for each aclent */
382789Sahrens 	resultsize = n * 2;
383789Sahrens 	if (hasmask) {
384789Sahrens 		/*
385789Sahrens 		 * stick extra deny on the group_obj and on each
386789Sahrens 		 * user|group for the mask (the group_obj was added
387789Sahrens 		 * into the count for numgroup)
388789Sahrens 		 */
389789Sahrens 		resultsize += numuser + numgroup;
390789Sahrens 		/* ... and don't count the mask itself */
391789Sahrens 		resultsize -= 2;
392789Sahrens 	}
393789Sahrens 
394789Sahrens 	/* sort the source if necessary */
395789Sahrens 	if (needsort)
396789Sahrens 		ksort((caddr_t)aclent, n, sizeof (aclent_t), cmp2acls);
397789Sahrens 
398789Sahrens 	result = acep = calloc(1, resultsize * sizeof (ace_t));
399789Sahrens 	if (result == NULL)
400789Sahrens 		goto out;
401789Sahrens 
402789Sahrens 	for (i = 0; i < n; i++) {
403789Sahrens 		/*
404789Sahrens 		 * don't process CLASS_OBJ (mask); mask was grabbed in
405789Sahrens 		 * ln_aent_preprocess()
406789Sahrens 		 */
407789Sahrens 		if (aclent[i].a_type & CLASS_OBJ)
408789Sahrens 			continue;
409789Sahrens 
410789Sahrens 		/* If we need an ACL_MASK emulator, prepend it now */
411789Sahrens 		if ((hasmask) &&
412789Sahrens 		    (aclent[i].a_type & (USER | GROUP | GROUP_OBJ))) {
413789Sahrens 			acep->a_type = ACE_ACCESS_DENIED_ACE_TYPE;
414789Sahrens 			acep->a_flags = 0;
415789Sahrens 			if (aclent[i].a_type & GROUP_OBJ) {
416789Sahrens 				acep->a_who = -1;
417789Sahrens 				acep->a_flags |=
418789Sahrens 				    (ACE_IDENTIFIER_GROUP|ACE_GROUP);
419789Sahrens 			} else if (aclent[i].a_type & USER) {
420789Sahrens 				acep->a_who = aclent[i].a_id;
421789Sahrens 			} else {
422789Sahrens 				acep->a_who = aclent[i].a_id;
423789Sahrens 				acep->a_flags |= ACE_IDENTIFIER_GROUP;
424789Sahrens 			}
425789Sahrens 			if (aclent[i].a_type & ACL_DEFAULT) {
426789Sahrens 				acep->a_flags |= ACE_INHERIT_ONLY_ACE |
427789Sahrens 				    ACE_FILE_INHERIT_ACE |
428789Sahrens 				    ACE_DIRECTORY_INHERIT_ACE;
429789Sahrens 			}
430789Sahrens 			/*
431789Sahrens 			 * Set the access mask for the prepended deny
432789Sahrens 			 * ace.  To do this, we invert the mask (found
433789Sahrens 			 * in ln_aent_preprocess()) then convert it to an
434789Sahrens 			 * DENY ace access_mask.
435789Sahrens 			 */
436789Sahrens 			acep->a_access_mask = mode_to_ace_access((mask ^ 07),
437789Sahrens 			    isdir, 0, 0);
438789Sahrens 			acep += 1;
439789Sahrens 		}
440789Sahrens 
441789Sahrens 		/* handle a_perm -> access_mask */
442789Sahrens 		acep->a_access_mask = mode_to_ace_access(aclent[i].a_perm,
443789Sahrens 		    isdir, aclent[i].a_type & USER_OBJ, 1);
444789Sahrens 
445789Sahrens 		/* emulate a default aclent */
446789Sahrens 		if (aclent[i].a_type & ACL_DEFAULT) {
447789Sahrens 			acep->a_flags |= ACE_INHERIT_ONLY_ACE |
448789Sahrens 			    ACE_FILE_INHERIT_ACE |
449789Sahrens 			    ACE_DIRECTORY_INHERIT_ACE;
450789Sahrens 		}
451789Sahrens 
452789Sahrens 		/*
453789Sahrens 		 * handle a_perm and a_id
454789Sahrens 		 *
455789Sahrens 		 * this must be done last, since it involves the
456789Sahrens 		 * corresponding deny aces, which are handled
457789Sahrens 		 * differently for each different a_type.
458789Sahrens 		 */
459789Sahrens 		if (aclent[i].a_type & USER_OBJ) {
460789Sahrens 			acep->a_who = -1;
461789Sahrens 			acep->a_flags |= ACE_OWNER;
462789Sahrens 			ace_make_deny(acep, acep + 1, isdir, B_TRUE);
463789Sahrens 			acep += 2;
464789Sahrens 		} else if (aclent[i].a_type & USER) {
465789Sahrens 			acep->a_who = aclent[i].a_id;
466789Sahrens 			ace_make_deny(acep, acep + 1, isdir, B_FALSE);
467789Sahrens 			acep += 2;
468789Sahrens 		} else if (aclent[i].a_type & (GROUP_OBJ | GROUP)) {
469789Sahrens 			if (aclent[i].a_type & GROUP_OBJ) {
470789Sahrens 				acep->a_who = -1;
471789Sahrens 				acep->a_flags |= ACE_GROUP;
472789Sahrens 			} else {
473789Sahrens 				acep->a_who = aclent[i].a_id;
474789Sahrens 			}
475789Sahrens 			acep->a_flags |= ACE_IDENTIFIER_GROUP;
476789Sahrens 			/*
477789Sahrens 			 * Set the corresponding deny for the group ace.
478789Sahrens 			 *
479789Sahrens 			 * The deny aces go after all of the groups, unlike
480789Sahrens 			 * everything else, where they immediately follow
481789Sahrens 			 * the allow ace.
482789Sahrens 			 *
483789Sahrens 			 * We calculate "skip", the number of slots to
484789Sahrens 			 * skip ahead for the deny ace, here.
485789Sahrens 			 *
486789Sahrens 			 * The pattern is:
487789Sahrens 			 * MD1 A1 MD2 A2 MD3 A3 D1 D2 D3
488789Sahrens 			 * thus, skip is
489789Sahrens 			 * (2 * numgroup) - 1 - groupi
490789Sahrens 			 * (2 * numgroup) to account for MD + A
491789Sahrens 			 * - 1 to account for the fact that we're on the
492789Sahrens 			 * access (A), not the mask (MD)
493789Sahrens 			 * - groupi to account for the fact that we have
494789Sahrens 			 * passed up groupi number of MD's.
495789Sahrens 			 */
496789Sahrens 			skip = (2 * numgroup) - 1 - groupi;
497789Sahrens 			ace_make_deny(acep, acep + skip, isdir, B_FALSE);
498789Sahrens 			/*
499789Sahrens 			 * If we just did the last group, skip acep past
500789Sahrens 			 * all of the denies; else, just move ahead one.
501789Sahrens 			 */
502789Sahrens 			if (++groupi >= numgroup)
503789Sahrens 				acep += numgroup + 1;
504789Sahrens 			else
505789Sahrens 				acep += 1;
506789Sahrens 		} else if (aclent[i].a_type & OTHER_OBJ) {
507789Sahrens 			acep->a_who = -1;
508789Sahrens 			acep->a_flags |= ACE_EVERYONE;
509789Sahrens 			ace_make_deny(acep, acep + 1, isdir, B_FALSE);
510789Sahrens 			acep += 2;
511789Sahrens 		} else {
512789Sahrens 			error = EINVAL;
513789Sahrens 			goto out;
514789Sahrens 		}
515789Sahrens 	}
516789Sahrens 
517789Sahrens 	*acepp = result;
518789Sahrens 	*rescount = resultsize;
519789Sahrens 
520789Sahrens out:
521789Sahrens 	if (error != 0) {
522789Sahrens 		if ((result != NULL) && (resultsize > 0)) {
523789Sahrens 			free(result);
524789Sahrens 		}
525789Sahrens 	}
526789Sahrens 
527789Sahrens 	return (error);
528789Sahrens }
529789Sahrens 
530789Sahrens static int
531789Sahrens convert_aent_to_ace(aclent_t *aclentp, int aclcnt, int isdir,
532789Sahrens     ace_t **retacep, int *retacecnt)
533789Sahrens {
534789Sahrens 	ace_t *acep;
535789Sahrens 	ace_t *dfacep;
536789Sahrens 	ace_t *newacep;
537789Sahrens 	int acecnt = 0;
538789Sahrens 	int dfacecnt = 0;
539789Sahrens 	int dfaclstart = 0;
540789Sahrens 	int dfaclcnt = 0;
541789Sahrens 	aclent_t *aclp;
542789Sahrens 	int i;
543789Sahrens 	int error;
544789Sahrens 
545789Sahrens 	ksort((caddr_t)aclentp, aclcnt, sizeof (aclent_t), cmp2acls);
546789Sahrens 
547789Sahrens 	for (i = 0, aclp = aclentp; i < aclcnt; aclp++, i++) {
548789Sahrens 		if (aclp->a_type & ACL_DEFAULT)
549789Sahrens 			break;
550789Sahrens 	}
551789Sahrens 
552789Sahrens 	if (i < aclcnt) {
553789Sahrens 		dfaclstart = aclcnt - i;
554789Sahrens 		dfaclcnt = i;
555789Sahrens 	}
556789Sahrens 
557789Sahrens 	if (dfaclcnt && isdir == 0) {
558789Sahrens 		return (-1);
559789Sahrens 	}
560789Sahrens 
561789Sahrens 	error = ln_aent_to_ace(aclentp, i,  &acep, &acecnt, isdir);
562789Sahrens 	if (error)
563789Sahrens 		return (-1);
564789Sahrens 
565789Sahrens 	if (dfaclcnt) {
566789Sahrens 		error = ln_aent_to_ace(&aclentp[dfaclstart], dfaclcnt,
567789Sahrens 		    &dfacep, &dfacecnt, isdir);
568789Sahrens 		if (error) {
569789Sahrens 			if (acep) {
570789Sahrens 				free(acep);
571789Sahrens 			}
572789Sahrens 			return (-1);
573789Sahrens 		}
574789Sahrens 	}
575789Sahrens 
576789Sahrens 	newacep = malloc(sizeof (ace_t) * (acecnt + dfacecnt));
577789Sahrens 	if (newacep == NULL)
578789Sahrens 		return (-1);
579789Sahrens 
580789Sahrens 	(void) memcpy(newacep, acep, sizeof (ace_t) * acecnt);
581789Sahrens 	if (dfaclcnt) {
582789Sahrens 		(void) memcpy(newacep + acecnt, dfacep,
583789Sahrens 		    sizeof (ace_t) * dfacecnt);
584789Sahrens 	}
585789Sahrens 	free(acep);
586789Sahrens 	if (dfaclcnt)
587789Sahrens 		free(dfacep);
588789Sahrens 
589789Sahrens 	*retacecnt = acecnt + dfacecnt;
590789Sahrens 	*retacep = newacep;
591789Sahrens 	return (0);
592789Sahrens }
593789Sahrens 
594789Sahrens 
595789Sahrens static int
596789Sahrens cacl_get(acl_inp inp, int get_flag, int type, acl_t **aclp)
597789Sahrens {
598789Sahrens 	const char *fname;
599789Sahrens 	int fd;
600789Sahrens 	int ace_acl = 0;
601789Sahrens 	int error;
602789Sahrens 	int getcmd, cntcmd;
603789Sahrens 	acl_t *acl_info;
604789Sahrens 	int	save_errno;
605789Sahrens 	int	stat_error;
606789Sahrens 	struct stat64 statbuf;
607789Sahrens 
608789Sahrens 	*aclp = NULL;
609789Sahrens 	if (type == ACL_PATH) {
610789Sahrens 		fname = inp.file;
611789Sahrens 		ace_acl = pathconf(fname, _PC_ACL_ENABLED);
612789Sahrens 	} else {
613789Sahrens 		fd = inp.fd;
614789Sahrens 		ace_acl = fpathconf(fd, _PC_ACL_ENABLED);
615789Sahrens 	}
616789Sahrens 
617789Sahrens 	if (ace_acl == -1)
618789Sahrens 		return (-1);
619789Sahrens 
620789Sahrens 	/*
621789Sahrens 	 * if acl's aren't supported then
622789Sahrens 	 * send it through the old GETACL interface
623789Sahrens 	 */
624789Sahrens 	if (ace_acl == 0) {
625789Sahrens 		ace_acl = _ACL_ACLENT_ENABLED;
626789Sahrens 	}
627789Sahrens 
628789Sahrens 	if (ace_acl & _ACL_ACE_ENABLED) {
629789Sahrens 		cntcmd = ACE_GETACLCNT;
630789Sahrens 		getcmd = ACE_GETACL;
631789Sahrens 		acl_info = acl_alloc(ACE_T);
632789Sahrens 	} else {
633789Sahrens 		cntcmd = GETACLCNT;
634789Sahrens 		getcmd = GETACL;
635789Sahrens 		acl_info = acl_alloc(ACLENT_T);
636789Sahrens 	}
637789Sahrens 
638789Sahrens 	if (acl_info == NULL)
639789Sahrens 		return (-1);
640789Sahrens 
641789Sahrens 	if (type == ACL_PATH) {
642789Sahrens 		acl_info->acl_cnt = acl(fname, cntcmd, 0, NULL);
643789Sahrens 	} else {
644789Sahrens 		acl_info->acl_cnt = facl(fd, cntcmd, 0, NULL);
645789Sahrens 	}
646789Sahrens 
647789Sahrens 	save_errno = errno;
648789Sahrens 	if (acl_info->acl_cnt < 0) {
649789Sahrens 		acl_free(acl_info);
650789Sahrens 		errno = save_errno;
651789Sahrens 		return (-1);
652789Sahrens 	}
653789Sahrens 
654789Sahrens 	if (acl_info->acl_cnt == 0) {
655789Sahrens 		acl_free(acl_info);
656789Sahrens 		errno = save_errno;
657789Sahrens 		return (0);
658789Sahrens 	}
659789Sahrens 
660789Sahrens 	acl_info->acl_aclp =
661789Sahrens 	    malloc(acl_info->acl_cnt * acl_info->acl_entry_size);
662789Sahrens 	save_errno = errno;
663789Sahrens 
664789Sahrens 	if (acl_info->acl_aclp == NULL) {
665789Sahrens 		acl_free(acl_info);
666789Sahrens 		errno = save_errno;
667789Sahrens 		return (-1);
668789Sahrens 	}
669789Sahrens 
670789Sahrens 	if (type == ACL_PATH) {
671789Sahrens 		stat_error = stat64(fname, &statbuf);
672789Sahrens 		error = acl(fname, getcmd, acl_info->acl_cnt,
673789Sahrens 		    acl_info->acl_aclp);
674789Sahrens 	} else {
675789Sahrens 		stat_error = fstat64(fd, &statbuf);
676789Sahrens 		error = facl(fd, getcmd, acl_info->acl_cnt,
677789Sahrens 		    acl_info->acl_aclp);
678789Sahrens 	}
679789Sahrens 
680789Sahrens 	save_errno = errno;
681789Sahrens 	if (error == -1) {
682789Sahrens 		acl_free(acl_info);
683789Sahrens 		errno = save_errno;
684789Sahrens 		return (-1);
685789Sahrens 	}
686789Sahrens 
687789Sahrens 
688789Sahrens 	if (stat_error == 0) {
689789Sahrens 		acl_info->acl_flags =
690789Sahrens 		    (S_ISDIR(statbuf.st_mode) ? ACL_IS_DIR : 0);
691789Sahrens 	} else
692789Sahrens 		acl_info->acl_flags = 0;
693789Sahrens 
694789Sahrens 	switch (acl_info->acl_type) {
695789Sahrens 	case ACLENT_T:
696789Sahrens 		if (acl_info->acl_cnt <= MIN_ACL_ENTRIES)
697789Sahrens 			acl_info->acl_flags |= ACL_IS_TRIVIAL;
698789Sahrens 		break;
699789Sahrens 	case ACE_T:
700789Sahrens 		if (ace_trivial(acl_info->acl_aclp, acl_info->acl_cnt) == 0)
701789Sahrens 			acl_info->acl_flags |= ACL_IS_TRIVIAL;
702789Sahrens 		break;
703789Sahrens 	default:
704789Sahrens 		errno = EINVAL;
705789Sahrens 		acl_free(acl_info);
706789Sahrens 		return (-1);
707789Sahrens 	}
708789Sahrens 
709789Sahrens 	if ((acl_info->acl_flags & ACL_IS_TRIVIAL) &&
710789Sahrens 	    (get_flag & ACL_NO_TRIVIAL)) {
711789Sahrens 		acl_free(acl_info);
712789Sahrens 		errno = 0;
713789Sahrens 		return (0);
714789Sahrens 	}
715789Sahrens 
716789Sahrens 	*aclp = acl_info;
717789Sahrens 	return (0);
718789Sahrens }
719789Sahrens 
720789Sahrens /*
721789Sahrens  * return -1 on failure, otherwise the number of acl
722789Sahrens  * entries is returned
723789Sahrens  */
724789Sahrens int
725789Sahrens acl_get(const char *path, int get_flag, acl_t **aclp)
726789Sahrens {
727789Sahrens 	acl_inp acl_inp;
728789Sahrens 	acl_inp.file = path;
729789Sahrens 
730789Sahrens 	return (cacl_get(acl_inp, get_flag, ACL_PATH, aclp));
731789Sahrens }
732789Sahrens 
733789Sahrens int
734789Sahrens facl_get(int fd, int get_flag, acl_t **aclp)
735789Sahrens {
736789Sahrens 
737789Sahrens 	acl_inp acl_inp;
738789Sahrens 	acl_inp.fd = fd;
739789Sahrens 
740789Sahrens 	return (cacl_get(acl_inp, get_flag, ACL_FD, aclp));
741789Sahrens }
742789Sahrens 
743789Sahrens /*
744789Sahrens  * Set an ACL, translates acl to ace_t when appropriate.
745789Sahrens  */
746789Sahrens static int
747789Sahrens cacl_set(acl_inp *acl_inp, acl_t *aclp, int type)
748789Sahrens {
749789Sahrens 	int error = 0;
750789Sahrens 	int acl_flavor_target;
751789Sahrens 	ace_t *acep = NULL;
752789Sahrens 	int acecnt;
753789Sahrens 	struct stat64 statbuf;
754789Sahrens 	int stat_error;
755789Sahrens 	int isdir;
756789Sahrens 
757789Sahrens 
758789Sahrens 	if (type == ACL_PATH) {
759789Sahrens 		stat_error = stat64(acl_inp->file, &statbuf);
760789Sahrens 		if (stat_error)
761789Sahrens 			return (-1);
762789Sahrens 		acl_flavor_target = pathconf(acl_inp->file, _PC_ACL_ENABLED);
763789Sahrens 	} else {
764789Sahrens 		stat_error = fstat64(acl_inp->fd, &statbuf);
765789Sahrens 		if (stat_error)
766789Sahrens 			return (-1);
767789Sahrens 		acl_flavor_target = fpathconf(acl_inp->fd, _PC_ACL_ENABLED);
768789Sahrens 	}
769789Sahrens 
770789Sahrens 	isdir = S_ISDIR(statbuf.st_mode);
771789Sahrens 
772789Sahrens 	if (acl_flavor_target == -1)
773789Sahrens 		return (-1);
774789Sahrens 
775789Sahrens 	/*
776789Sahrens 	 * Translate aclent_t ACL's to ACE ACL's.
777789Sahrens 	 */
778789Sahrens 	if (acl_flavor_target ==  _ACL_ACE_ENABLED &&
779789Sahrens 	    aclp->acl_type == ACLENT_T) {
780789Sahrens 		error = convert_aent_to_ace(aclp->acl_aclp,
781789Sahrens 		    aclp->acl_cnt, isdir, &acep, &acecnt);
782789Sahrens 		if (error) {
783789Sahrens 			errno = ENOTSUP;
784789Sahrens 			return (-1);
785789Sahrens 		}
786789Sahrens 		/*
787789Sahrens 		 * replace old acl with newly translated acl
788789Sahrens 		 */
789789Sahrens 		free(aclp->acl_aclp);
790789Sahrens 		aclp->acl_aclp = acep;
791789Sahrens 		aclp->acl_cnt = acecnt;
792789Sahrens 		aclp->acl_type = ACE_T;
793789Sahrens 	}
794789Sahrens 
795789Sahrens 	if (type == ACL_PATH) {
796789Sahrens 		error = acl(acl_inp->file,
797789Sahrens 		    (aclp->acl_type == ACE_T) ? ACE_SETACL : SETACL,
798789Sahrens 		    aclp->acl_cnt, aclp->acl_aclp);
799789Sahrens 	} else {
800789Sahrens 		error = facl(acl_inp->fd,
801789Sahrens 		    (aclp->acl_type == ACE_T) ? ACE_SETACL : SETACL,
802789Sahrens 		    aclp->acl_cnt, aclp->acl_aclp);
803789Sahrens 	}
804789Sahrens 
805789Sahrens 	return (error);
806789Sahrens }
807789Sahrens 
808789Sahrens int
809789Sahrens acl_set(const char *path, acl_t *aclp)
810789Sahrens {
811789Sahrens 	acl_inp acl_inp;
812789Sahrens 
813789Sahrens 	acl_inp.file = path;
814789Sahrens 
815789Sahrens 	return (cacl_set(&acl_inp, aclp, ACL_PATH));
816789Sahrens }
817789Sahrens 
818789Sahrens int
819789Sahrens facl_set(int fd, acl_t *aclp)
820789Sahrens {
821789Sahrens 	acl_inp acl_inp;
822789Sahrens 
823789Sahrens 	acl_inp.fd = fd;
824789Sahrens 
825789Sahrens 	return (cacl_set(&acl_inp, aclp, ACL_FD));
826789Sahrens }
827789Sahrens 
828789Sahrens int
829789Sahrens acl_cnt(acl_t *aclp)
830789Sahrens {
831789Sahrens 	return (aclp->acl_cnt);
832789Sahrens }
833789Sahrens 
834789Sahrens int
835789Sahrens acl_type(acl_t *aclp)
836789Sahrens {
837789Sahrens 	return (aclp->acl_type);
838789Sahrens }
839789Sahrens 
840789Sahrens acl_t *
841789Sahrens acl_dup(acl_t *aclp)
842789Sahrens {
843789Sahrens 	acl_t *newaclp;
844789Sahrens 
845789Sahrens 	newaclp = acl_alloc(aclp->acl_type);
846789Sahrens 	if (newaclp == NULL)
847789Sahrens 		return (NULL);
848789Sahrens 
849789Sahrens 	newaclp->acl_aclp = malloc(aclp->acl_entry_size * aclp->acl_cnt);
850789Sahrens 	if (newaclp->acl_aclp == NULL) {
851789Sahrens 		acl_free(newaclp);
852789Sahrens 		return (NULL);
853789Sahrens 	}
854789Sahrens 
855789Sahrens 	(void) memcpy(newaclp->acl_aclp,
856789Sahrens 	    aclp->acl_aclp, aclp->acl_entry_size * aclp->acl_cnt);
857789Sahrens 	newaclp->acl_cnt = aclp->acl_cnt;
858789Sahrens 
859789Sahrens 	return (newaclp);
860789Sahrens }
861789Sahrens 
862789Sahrens int
863789Sahrens acl_flags(acl_t *aclp)
864789Sahrens {
865789Sahrens 	return (aclp->acl_flags);
866789Sahrens }
867789Sahrens 
868789Sahrens void *
869789Sahrens acl_data(acl_t *aclp)
870789Sahrens {
871789Sahrens 	return (aclp->acl_aclp);
872789Sahrens }
873789Sahrens 
874789Sahrens /*
875789Sahrens  * Remove an ACL from a file and create a trivial ACL based
876789Sahrens  * off of the mode argument.  After acl has been set owner/group
877789Sahrens  * are updated to match owner,group arguments
878789Sahrens  */
879789Sahrens int
880789Sahrens acl_strip(const char *file, uid_t owner, gid_t group, mode_t mode)
881789Sahrens {
882789Sahrens 	int	error = 0;
883789Sahrens 	aclent_t min_acl[MIN_ACL_ENTRIES];
884789Sahrens 	ace_t	min_ace_acl[6];	/* owner, group, everyone + complement denies */
885789Sahrens 	int	acl_flavor;
886789Sahrens 	int	aclcnt;
887789Sahrens 
888789Sahrens 	acl_flavor = pathconf(file, _PC_ACL_ENABLED);
889789Sahrens 
890789Sahrens 	if (acl_flavor == -1)
891789Sahrens 		return (-1);
892789Sahrens 	/*
893789Sahrens 	 * force it through aclent flavor when file system doesn't
894789Sahrens 	 * understand question
895789Sahrens 	 */
896789Sahrens 	if (acl_flavor == 0)
897789Sahrens 		acl_flavor = _ACL_ACLENT_ENABLED;
898789Sahrens 
899789Sahrens 	if (acl_flavor & _ACL_ACLENT_ENABLED) {
900789Sahrens 		min_acl[0].a_type = USER_OBJ;
901789Sahrens 		min_acl[0].a_id   = owner;
902789Sahrens 		min_acl[0].a_perm = ((mode & 0700) >> 6);
903789Sahrens 		min_acl[1].a_type = GROUP_OBJ;
904789Sahrens 		min_acl[1].a_id   = group;
905789Sahrens 		min_acl[1].a_perm = ((mode & 0070) >> 3);
906789Sahrens 		min_acl[2].a_type = CLASS_OBJ;
907789Sahrens 		min_acl[2].a_id   = (uid_t)-1;
908789Sahrens 		min_acl[2].a_perm = ((mode & 0070) >> 3);
909789Sahrens 		min_acl[3].a_type = OTHER_OBJ;
910789Sahrens 		min_acl[3].a_id   = (uid_t)-1;
911789Sahrens 		min_acl[3].a_perm = (mode & 0007);
912789Sahrens 		aclcnt = 4;
913789Sahrens 		error = acl(file, SETACL, aclcnt, min_acl);
914789Sahrens 	} else if (acl_flavor & _ACL_ACE_ENABLED) {
915789Sahrens 		(void) memcpy(min_ace_acl, trivial_acl, sizeof (ace_t) * 6);
916789Sahrens 
917789Sahrens 		/*
918789Sahrens 		 * Make aces match request mode
919789Sahrens 		 */
920789Sahrens 		adjust_ace_pair(&min_ace_acl[0], (mode & 0700) >> 6);
921789Sahrens 		adjust_ace_pair(&min_ace_acl[2], (mode & 0070) >> 3);
922789Sahrens 		adjust_ace_pair(&min_ace_acl[4], mode & 0007);
923789Sahrens 
924789Sahrens 		error = acl(file, ACE_SETACL, 6, min_ace_acl);
925789Sahrens 	} else {
926789Sahrens 		errno = EINVAL;
927789Sahrens 		error = 1;
928789Sahrens 	}
929789Sahrens 
930789Sahrens 	if (error == 0)
931789Sahrens 		error = chown(file, owner, group);
932789Sahrens 	return (error);
933789Sahrens }
934789Sahrens 
935789Sahrens static int
936789Sahrens ace_match(void *entry1, void *entry2)
937789Sahrens {
938789Sahrens 	ace_t *p1 = (ace_t *)entry1;
939789Sahrens 	ace_t *p2 = (ace_t *)entry2;
940789Sahrens 	ace_t ace1, ace2;
941789Sahrens 
942789Sahrens 	ace1 = *p1;
943789Sahrens 	ace2 = *p2;
944789Sahrens 
945789Sahrens 	/*
946789Sahrens 	 * Need to fixup who field for abstrations for
947789Sahrens 	 * accurate comparison, since field is undefined.
948789Sahrens 	 */
949789Sahrens 	if (ace1.a_flags & (ACE_OWNER|ACE_GROUP|ACE_EVERYONE))
950789Sahrens 		ace1.a_who = -1;
951789Sahrens 	if (ace2.a_flags & (ACE_OWNER|ACE_GROUP|ACE_EVERYONE))
952789Sahrens 		ace2.a_who = -1;
953789Sahrens 	return (memcmp(&ace1, &ace2, sizeof (ace_t)));
954789Sahrens }
955789Sahrens 
956789Sahrens static int
957789Sahrens aclent_match(void *entry1, void *entry2)
958789Sahrens {
959789Sahrens 	aclent_t *aclent1 = (aclent_t *)entry1;
960789Sahrens 	aclent_t *aclent2 = (aclent_t *)entry2;
961789Sahrens 
962789Sahrens 	return (memcmp(aclent1, aclent2, sizeof (aclent_t)));
963789Sahrens }
964789Sahrens 
965789Sahrens /*
966789Sahrens  * Find acl entries in acl that correspond to removeacl.  Search
967789Sahrens  * is started from slot.  The flag argument indicates whether to
968789Sahrens  * remove all matches or just the first match.
969789Sahrens  */
970789Sahrens int
971789Sahrens acl_removeentries(acl_t *acl, acl_t *removeacl, int start_slot, int flag)
972789Sahrens {
973789Sahrens 	int i, j;
974789Sahrens 	int match;
975789Sahrens 	int (*acl_match)(void *acl1, void *acl2);
976789Sahrens 	void *acl_entry, *remove_entry;
977789Sahrens 	void *start;
978789Sahrens 	int found = 0;
979789Sahrens 
980789Sahrens 	if (flag != ACL_REMOVE_ALL && flag != ACL_REMOVE_FIRST)
981789Sahrens 		flag = ACL_REMOVE_FIRST;
982789Sahrens 
983789Sahrens 	if (acl == NULL || removeacl == NULL)
984789Sahrens 		return (EACL_NO_ACL_ENTRY);
985789Sahrens 
986789Sahrens 	if (acl->acl_type != removeacl->acl_type)
987789Sahrens 		return (EACL_DIFF_TYPE);
988789Sahrens 
989789Sahrens 	if (acl->acl_type == ACLENT_T)
990789Sahrens 		acl_match = aclent_match;
991789Sahrens 	else
992789Sahrens 		acl_match = ace_match;
993789Sahrens 
994789Sahrens 	for (i = 0, remove_entry = removeacl->acl_aclp;
995789Sahrens 	    i != removeacl->acl_cnt; i++) {
996789Sahrens 
997789Sahrens 		j = 0;
998789Sahrens 		acl_entry = (char *)acl->acl_aclp +
999789Sahrens 		    (acl->acl_entry_size * start_slot);
1000789Sahrens 		for (;;) {
1001789Sahrens 			match = acl_match(acl_entry, remove_entry);
1002789Sahrens 			if (match == 0)  {
1003789Sahrens 				found++;
1004789Sahrens 				start = (char *)acl_entry +
1005789Sahrens 				    acl->acl_entry_size;
1006789Sahrens 				(void) memmove(acl_entry, start,
1007789Sahrens 				    acl->acl_entry_size *
1008789Sahrens 				    acl->acl_cnt-- - (j + 1));
1009789Sahrens 
1010789Sahrens 				if (flag == ACL_REMOVE_FIRST)
1011789Sahrens 					break;
1012789Sahrens 				/*
1013789Sahrens 				 * List has changed, restart search from
1014789Sahrens 				 * beginning.
1015789Sahrens 				 */
1016789Sahrens 				acl_entry = acl->acl_aclp;
1017789Sahrens 				j = 0;
1018789Sahrens 				continue;
1019789Sahrens 			}
1020789Sahrens 			acl_entry = ((char *)acl_entry + acl->acl_entry_size);
1021789Sahrens 			if (++j >= acl->acl_cnt) {
1022789Sahrens 				break;
1023789Sahrens 			}
1024789Sahrens 		}
1025789Sahrens 	}
1026789Sahrens 
1027789Sahrens 	return ((found == 0) ? EACL_NO_ACL_ENTRY : 0);
1028789Sahrens }
1029789Sahrens 
1030789Sahrens /*
1031789Sahrens  * Replace entires entries in acl1 with the corresponding entries
1032789Sahrens  * in newentries.  The where argument specifies where to begin
1033789Sahrens  * the replacement.  If the where argument is 1 greater than the
1034789Sahrens  * number of acl entries in acl1 then they are appended.  If the
1035789Sahrens  * where argument is 2+ greater than the number of acl entries then
1036789Sahrens  * EACL_INVALID_SLOT is returned.
1037789Sahrens  */
1038789Sahrens int
1039789Sahrens acl_modifyentries(acl_t *acl1, acl_t *newentries, int where)
1040789Sahrens {
1041789Sahrens 
1042789Sahrens 	int slot;
1043789Sahrens 	int slots_needed;
1044789Sahrens 	int slots_left;
1045789Sahrens 	int newsize;
1046789Sahrens 
1047789Sahrens 	if (acl1 == NULL || newentries == NULL)
1048789Sahrens 		return (EACL_NO_ACL_ENTRY);
1049789Sahrens 
1050789Sahrens 	if (where < 0 || where >= acl1->acl_cnt)
1051789Sahrens 		return (EACL_INVALID_SLOT);
1052789Sahrens 
1053789Sahrens 	if (acl1->acl_type != newentries->acl_type)
1054789Sahrens 		return (EACL_DIFF_TYPE);
1055789Sahrens 
1056789Sahrens 	slot = where;
1057789Sahrens 
1058789Sahrens 	slots_left = acl1->acl_cnt - slot + 1;
1059789Sahrens 	if (slots_left < newentries->acl_cnt) {
1060789Sahrens 		slots_needed = newentries->acl_cnt - slots_left;
1061789Sahrens 		newsize = (acl1->acl_entry_size * acl1->acl_cnt) +
1062789Sahrens 		    (acl1->acl_entry_size * slots_needed);
1063789Sahrens 		acl1->acl_aclp = realloc(acl1->acl_aclp, newsize);
1064789Sahrens 		if (acl1->acl_aclp == NULL)
1065789Sahrens 			return (-1);
1066789Sahrens 	}
1067789Sahrens 	(void) memcpy((char *)acl1->acl_aclp + (acl1->acl_entry_size * slot),
1068789Sahrens 	    newentries->acl_aclp,
1069789Sahrens 	    newentries->acl_entry_size * newentries->acl_cnt);
1070789Sahrens 
1071789Sahrens 	/*
1072789Sahrens 	 * Did ACL grow?
1073789Sahrens 	 */
1074789Sahrens 
1075789Sahrens 	if ((slot + newentries->acl_cnt) > acl1->acl_cnt) {
1076789Sahrens 		acl1->acl_cnt = slot + newentries->acl_cnt;
1077789Sahrens 	}
1078789Sahrens 
1079789Sahrens 	return (0);
1080789Sahrens }
1081789Sahrens 
1082789Sahrens /*
1083789Sahrens  * Add acl2 entries into acl1.  The where argument specifies where
1084789Sahrens  * to add the entries.
1085789Sahrens  */
1086789Sahrens int
1087789Sahrens acl_addentries(acl_t *acl1, acl_t *acl2, int where)
1088789Sahrens {
1089789Sahrens 
1090789Sahrens 	int newsize;
1091789Sahrens 	int len;
1092789Sahrens 	void *start;
1093789Sahrens 	void *to;
1094789Sahrens 
1095789Sahrens 	if (acl1 == NULL || acl2 == NULL)
1096789Sahrens 		return (EACL_NO_ACL_ENTRY);
1097789Sahrens 
1098789Sahrens 	if (acl1->acl_type != acl2->acl_type)
1099789Sahrens 		return (EACL_DIFF_TYPE);
1100789Sahrens 
1101789Sahrens 	/*
1102789Sahrens 	 * allow where to specify 1 past last slot for an append operation
1103789Sahrens 	 * but anything greater is an error.
1104789Sahrens 	 */
1105789Sahrens 	if (where < 0 || where > acl1->acl_cnt)
1106789Sahrens 		return (EACL_INVALID_SLOT);
1107789Sahrens 
1108789Sahrens 	newsize = (acl2->acl_entry_size * acl2->acl_cnt) +
1109789Sahrens 	    (acl1->acl_entry_size * acl1->acl_cnt);
1110789Sahrens 	acl1->acl_aclp = realloc(acl1->acl_aclp, newsize);
1111789Sahrens 	if (acl1->acl_aclp == NULL)
1112789Sahrens 		return (-1);
1113789Sahrens 
1114789Sahrens 	/*
1115789Sahrens 	 * first push down entries where new ones will be inserted
1116789Sahrens 	 */
1117789Sahrens 
1118789Sahrens 	to = (void *)((char *)acl1->acl_aclp +
1119789Sahrens 	    ((where + acl2->acl_cnt) * acl1->acl_entry_size));
1120789Sahrens 
1121789Sahrens 	start = (void *)((char *)acl1->acl_aclp +
1122789Sahrens 	    where * acl1->acl_entry_size);
1123789Sahrens 
1124789Sahrens 	if (where < acl1->acl_cnt) {
1125789Sahrens 		len = (acl1->acl_cnt - where) * acl1->acl_entry_size;
1126789Sahrens 		(void) memmove(to, start, len);
1127789Sahrens 	}
1128789Sahrens 
1129789Sahrens 	/*
1130789Sahrens 	 * now stick in new entries.
1131789Sahrens 	 */
1132789Sahrens 
1133789Sahrens 	(void) memmove(start, acl2->acl_aclp,
1134789Sahrens 	    acl2->acl_cnt * acl2->acl_entry_size);
1135789Sahrens 
1136789Sahrens 	acl1->acl_cnt += acl2->acl_cnt;
1137789Sahrens 	return (0);
1138789Sahrens }
1139789Sahrens 
1140789Sahrens /*
1141789Sahrens  * return text for an ACL error.
1142789Sahrens  */
1143789Sahrens char *
1144789Sahrens acl_strerror(int errnum)
1145789Sahrens {
1146789Sahrens 	switch (errnum) {
1147789Sahrens 	case EACL_GRP_ERROR:
1148789Sahrens 		return (dgettext(TEXT_DOMAIN,
1149*1420Smarks 		    "There is more than one group or default group entry"));
1150789Sahrens 	case EACL_USER_ERROR:
1151789Sahrens 		return (dgettext(TEXT_DOMAIN,
1152*1420Smarks 		    "There is more than one user or default user entry"));
1153789Sahrens 	case EACL_OTHER_ERROR:
1154789Sahrens 		return (dgettext(TEXT_DOMAIN,
1155789Sahrens 		    "There is more than one other entry"));
1156789Sahrens 	case EACL_CLASS_ERROR:
1157789Sahrens 		return (dgettext(TEXT_DOMAIN,
1158789Sahrens 		    "There is more than one mask entry"));
1159789Sahrens 	case EACL_DUPLICATE_ERROR:
1160789Sahrens 		return (dgettext(TEXT_DOMAIN,
1161789Sahrens 		    "Duplicate user or group entries"));
1162789Sahrens 	case EACL_MISS_ERROR:
1163789Sahrens 		return (dgettext(TEXT_DOMAIN,
1164789Sahrens 		    "Missing user/group owner, other, mask entry"));
1165789Sahrens 	case EACL_MEM_ERROR:
1166789Sahrens 		return (dgettext(TEXT_DOMAIN,
1167789Sahrens 		    "Memory error"));
1168789Sahrens 	case EACL_ENTRY_ERROR:
1169789Sahrens 		return (dgettext(TEXT_DOMAIN,
1170789Sahrens 		    "Unrecognized entry type"));
1171789Sahrens 	case EACL_INHERIT_ERROR:
1172789Sahrens 		return (dgettext(TEXT_DOMAIN,
1173789Sahrens 		    "Invalid inheritance flags"));
1174789Sahrens 	case EACL_FLAGS_ERROR:
1175789Sahrens 		return (dgettext(TEXT_DOMAIN,
1176789Sahrens 		    "Unrecognized entry flags"));
1177789Sahrens 	case EACL_PERM_MASK_ERROR:
1178789Sahrens 		return (dgettext(TEXT_DOMAIN,
1179789Sahrens 		    "Invalid ACL permissions"));
1180789Sahrens 	case EACL_COUNT_ERROR:
1181789Sahrens 		return (dgettext(TEXT_DOMAIN,
1182789Sahrens 		    "Invalid ACL count"));
1183789Sahrens 	case EACL_INVALID_SLOT:
1184789Sahrens 		return (dgettext(TEXT_DOMAIN,
1185789Sahrens 		    "Invalid ACL entry number specified"));
1186789Sahrens 	case EACL_NO_ACL_ENTRY:
1187789Sahrens 		return (dgettext(TEXT_DOMAIN,
1188789Sahrens 		    "ACL entry doesn't exist"));
1189789Sahrens 	case EACL_DIFF_TYPE:
1190789Sahrens 		return (dgettext(TEXT_DOMAIN,
1191789Sahrens 		    "ACL type's are different"));
1192789Sahrens 	case EACL_INVALID_USER_GROUP:
1193789Sahrens 		return (dgettext(TEXT_DOMAIN, "Invalid user or group"));
1194789Sahrens 	case EACL_INVALID_STR:
1195789Sahrens 		return (dgettext(TEXT_DOMAIN, "ACL string is invalid"));
1196789Sahrens 	case EACL_FIELD_NOT_BLANK:
1197789Sahrens 		return (dgettext(TEXT_DOMAIN, "Field expected to be blank"));
1198789Sahrens 	case EACL_INVALID_ACCESS_TYPE:
1199789Sahrens 		return (dgettext(TEXT_DOMAIN, "Invalid access type"));
1200789Sahrens 	case EACL_UNKNOWN_DATA:
1201789Sahrens 		return (dgettext(TEXT_DOMAIN, "Unrecognized entry"));
1202789Sahrens 	case EACL_MISSING_FIELDS:
1203789Sahrens 		return (dgettext(TEXT_DOMAIN,
1204789Sahrens 		    "ACL specification missing required fields"));
1205789Sahrens 	case EACL_INHERIT_NOTDIR:
1206789Sahrens 		return (dgettext(TEXT_DOMAIN,
1207789Sahrens 		    "Inheritance flags are only allowed on directories"));
1208789Sahrens 	case -1:
1209789Sahrens 		return (strerror(errno));
1210789Sahrens 	default:
1211789Sahrens 		errno = EINVAL;
1212789Sahrens 		return (dgettext(TEXT_DOMAIN, "Unknown error"));
1213789Sahrens 	}
1214789Sahrens }
1215*1420Smarks 
1216*1420Smarks extern int yyinteractive;
1217*1420Smarks 
1218*1420Smarks /* PRINTFLIKE1 */
1219*1420Smarks void
1220*1420Smarks acl_error(const char *fmt, ...)
1221*1420Smarks {
1222*1420Smarks 	va_list va;
1223*1420Smarks 
1224*1420Smarks 	if (yyinteractive == 0)
1225*1420Smarks 		return;
1226*1420Smarks 
1227*1420Smarks 	va_start(va, fmt);
1228*1420Smarks 	(void) vfprintf(stderr, fmt, va);
1229*1420Smarks 	va_end(va);
1230*1420Smarks }
1231