1*789Sahrens /*
2*789Sahrens  * CDDL HEADER START
3*789Sahrens  *
4*789Sahrens  * The contents of this file are subject to the terms of the
5*789Sahrens  * Common Development and Distribution License, Version 1.0 only
6*789Sahrens  * (the "License").  You may not use this file except in compliance
7*789Sahrens  * with the License.
8*789Sahrens  *
9*789Sahrens  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*789Sahrens  * or http://www.opensolaris.org/os/licensing.
11*789Sahrens  * See the License for the specific language governing permissions
12*789Sahrens  * and limitations under the License.
13*789Sahrens  *
14*789Sahrens  * When distributing Covered Code, include this CDDL HEADER in each
15*789Sahrens  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*789Sahrens  * If applicable, add the following below this CDDL HEADER, with the
17*789Sahrens  * fields enclosed by brackets "[]" replaced with your own identifying
18*789Sahrens  * information: Portions Copyright [yyyy] [name of copyright owner]
19*789Sahrens  *
20*789Sahrens  * CDDL HEADER END
21*789Sahrens  */
22*789Sahrens /*
23*789Sahrens  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24*789Sahrens  * Use is subject to license terms.
25*789Sahrens  */
26*789Sahrens 
27*789Sahrens #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*789Sahrens 
29*789Sahrens #include <stdlib.h>
30*789Sahrens #include <string.h>
31*789Sahrens #include <unistd.h>
32*789Sahrens #include <limits.h>
33*789Sahrens #include <grp.h>
34*789Sahrens #include <pwd.h>
35*789Sahrens #include <sys/types.h>
36*789Sahrens #include <sys/acl.h>
37*789Sahrens #include <errno.h>
38*789Sahrens #include <sys/stat.h>
39*789Sahrens #include <locale.h>
40*789Sahrens #include <aclutils.h>
41*789Sahrens #include <acl_common.h>
42*789Sahrens 
43*789Sahrens #define	ACL_PATH	0
44*789Sahrens #define	ACL_FD		1
45*789Sahrens 
46*789Sahrens #define	ACE_POSIX_SUPPORTED_BITS (ACE_READ_DATA | \
47*789Sahrens     ACE_WRITE_DATA | ACE_APPEND_DATA | ACE_EXECUTE | \
48*789Sahrens     ACE_READ_ATTRIBUTES | ACE_READ_ACL | ACE_WRITE_ACL)
49*789Sahrens 
50*789Sahrens 
51*789Sahrens #define	ACL_SYNCHRONIZE_SET_ALLOW		0x0000002
52*789Sahrens #define	ACL_SYNCHRONIZE_SET_DENY		0x0000001
53*789Sahrens 
54*789Sahrens #define	ACL_WRITE_OWNER_SET_ALLOW		0x0000020
55*789Sahrens #define	ACL_WRITE_OWNER_SET_DENY		0x0000010
56*789Sahrens 
57*789Sahrens #define	ACL_WRITE_ATTRS_OWNER_SET_ALLOW		0x0002000
58*789Sahrens #define	ACL_WRITE_ATTRS_OWNER_SET_DENY		0x0001000
59*789Sahrens 
60*789Sahrens #define	ACL_WRITE_ATTRS_WRITER_SET_DENY		0x0010000
61*789Sahrens 
62*789Sahrens #define	ACL_DELETE_SET_ALLOW			0x0000200
63*789Sahrens #define	ACL_DELETE_SET_DENY			0x0000100
64*789Sahrens 
65*789Sahrens #define	ACL_READ_NAMED_READER_SET_ALLOW		0x2000000
66*789Sahrens 
67*789Sahrens #define	ACL_WRITE_NAMED_WRITER_SET_ALLOW	0x0200000
68*789Sahrens #define	ACL_WRITE_NAMED_WRITER_SET_DENY		0x0100000
69*789Sahrens 
70*789Sahrens #define	ACL_WRITE_ATTRS_OWNER_SET_ALLOW		0x0002000
71*789Sahrens #define	ACL_WRITE_ATTRS_WRITER_SET_ALLOW	0x0020000
72*789Sahrens 
73*789Sahrens #define	ACL_WRITE_OWNER_ERR_DENY		0x0000040
74*789Sahrens #define	ACL_READ_NAMED_READER_SET_DENY		0x1000000
75*789Sahrens #define	ACL_WRITE_NAMED_WRITER_SET_ALLO		W0x0200000
76*789Sahrens typedef union {
77*789Sahrens 	const char *file;
78*789Sahrens 	int  fd;
79*789Sahrens } acl_inp;
80*789Sahrens 
81*789Sahrens acl_t *
82*789Sahrens acl_alloc(enum acl_type type)
83*789Sahrens {
84*789Sahrens 	acl_t *aclp;
85*789Sahrens 
86*789Sahrens 	aclp = malloc(sizeof (acl_t));
87*789Sahrens 
88*789Sahrens 	if (aclp == NULL)
89*789Sahrens 		return (NULL);
90*789Sahrens 
91*789Sahrens 	aclp->acl_aclp = NULL;
92*789Sahrens 	aclp->acl_cnt = 0;
93*789Sahrens 
94*789Sahrens 	switch (type) {
95*789Sahrens 	case ACE_T:
96*789Sahrens 		aclp->acl_type = ACE_T;
97*789Sahrens 		aclp->acl_entry_size = sizeof (ace_t);
98*789Sahrens 		break;
99*789Sahrens 	case ACLENT_T:
100*789Sahrens 		aclp->acl_type = ACLENT_T;
101*789Sahrens 		aclp->acl_entry_size = sizeof (aclent_t);
102*789Sahrens 		break;
103*789Sahrens 	default:
104*789Sahrens 		acl_free(aclp);
105*789Sahrens 		aclp = NULL;
106*789Sahrens 	}
107*789Sahrens 	return (aclp);
108*789Sahrens }
109*789Sahrens 
110*789Sahrens /*
111*789Sahrens  * Free acl_t structure
112*789Sahrens  */
113*789Sahrens void
114*789Sahrens acl_free(acl_t *aclp)
115*789Sahrens {
116*789Sahrens 	if (aclp == NULL)
117*789Sahrens 		return;
118*789Sahrens 
119*789Sahrens 	if (aclp->acl_aclp)
120*789Sahrens 		free(aclp->acl_aclp);
121*789Sahrens 	free(aclp);
122*789Sahrens }
123*789Sahrens 
124*789Sahrens /*
125*789Sahrens  * Determine whether a file has a trivial ACL
126*789Sahrens  * returns: 	0 = trivial
127*789Sahrens  *		1 = nontrivial
128*789Sahrens  *		<0 some other system failure, such as ENOENT or EPERM
129*789Sahrens  */
130*789Sahrens int
131*789Sahrens acl_trivial(const char *filename)
132*789Sahrens {
133*789Sahrens 	int acl_flavor;
134*789Sahrens 	int aclcnt;
135*789Sahrens 	int cntcmd;
136*789Sahrens 	int val = 0;
137*789Sahrens 	ace_t *acep;
138*789Sahrens 
139*789Sahrens 	acl_flavor = pathconf(filename, _PC_ACL_ENABLED);
140*789Sahrens 	if (acl_flavor == -1)
141*789Sahrens 		return (-1);
142*789Sahrens 
143*789Sahrens 	if (acl_flavor == _ACL_ACE_ENABLED)
144*789Sahrens 		cntcmd = ACE_GETACLCNT;
145*789Sahrens 	else
146*789Sahrens 		cntcmd = GETACLCNT;
147*789Sahrens 
148*789Sahrens 	aclcnt = acl(filename, cntcmd, 0, NULL);
149*789Sahrens 	if (aclcnt > 0) {
150*789Sahrens 		if (acl_flavor == _ACL_ACE_ENABLED) {
151*789Sahrens 			if (aclcnt != 6)
152*789Sahrens 				val = 1;
153*789Sahrens 			else {
154*789Sahrens 				acep = malloc(sizeof (ace_t) * aclcnt);
155*789Sahrens 				if (acep == NULL)
156*789Sahrens 					return (-1);
157*789Sahrens 				if (acl(filename, ACE_GETACL,
158*789Sahrens 				    aclcnt, acep) < 0) {
159*789Sahrens 					free(acep);
160*789Sahrens 					return (-1);
161*789Sahrens 				}
162*789Sahrens 
163*789Sahrens 				val = ace_trivial(acep, aclcnt);
164*789Sahrens 				free(acep);
165*789Sahrens 			}
166*789Sahrens 		} else if (aclcnt > MIN_ACL_ENTRIES)
167*789Sahrens 			val = 1;
168*789Sahrens 	}
169*789Sahrens 	return (val);
170*789Sahrens }
171*789Sahrens 
172*789Sahrens static uint32_t
173*789Sahrens access_mask_set(int haswriteperm, int hasreadperm, int isowner, int isallow)
174*789Sahrens {
175*789Sahrens 	uint32_t access_mask = 0;
176*789Sahrens 	int acl_produce;
177*789Sahrens 	int synchronize_set = 0, write_owner_set = 0;
178*789Sahrens 	int delete_set = 0, write_attrs_set = 0;
179*789Sahrens 	int read_named_set = 0, write_named_set = 0;
180*789Sahrens 
181*789Sahrens 	acl_produce = (ACL_SYNCHRONIZE_SET_ALLOW |
182*789Sahrens 	    ACL_WRITE_ATTRS_OWNER_SET_ALLOW |
183*789Sahrens 	    ACL_WRITE_ATTRS_WRITER_SET_DENY);
184*789Sahrens 
185*789Sahrens 	if (isallow) {
186*789Sahrens 		synchronize_set = ACL_SYNCHRONIZE_SET_ALLOW;
187*789Sahrens 		write_owner_set = ACL_WRITE_OWNER_SET_ALLOW;
188*789Sahrens 		delete_set = ACL_DELETE_SET_ALLOW;
189*789Sahrens 		if (hasreadperm)
190*789Sahrens 			read_named_set = ACL_READ_NAMED_READER_SET_ALLOW;
191*789Sahrens 		if (haswriteperm)
192*789Sahrens 			write_named_set = ACL_WRITE_NAMED_WRITER_SET_ALLOW;
193*789Sahrens 		if (isowner)
194*789Sahrens 			write_attrs_set = ACL_WRITE_ATTRS_OWNER_SET_ALLOW;
195*789Sahrens 		else if (haswriteperm)
196*789Sahrens 			write_attrs_set = ACL_WRITE_ATTRS_WRITER_SET_ALLOW;
197*789Sahrens 	} else {
198*789Sahrens 
199*789Sahrens 		synchronize_set = ACL_SYNCHRONIZE_SET_DENY;
200*789Sahrens 		write_owner_set = ACL_WRITE_OWNER_SET_DENY;
201*789Sahrens 		delete_set = ACL_DELETE_SET_DENY;
202*789Sahrens 		if (hasreadperm)
203*789Sahrens 			read_named_set = ACL_READ_NAMED_READER_SET_DENY;
204*789Sahrens 		if (haswriteperm)
205*789Sahrens 			write_named_set = ACL_WRITE_NAMED_WRITER_SET_DENY;
206*789Sahrens 		if (isowner)
207*789Sahrens 			write_attrs_set = ACL_WRITE_ATTRS_OWNER_SET_DENY;
208*789Sahrens 		else if (haswriteperm)
209*789Sahrens 			write_attrs_set = ACL_WRITE_ATTRS_WRITER_SET_DENY;
210*789Sahrens 		else
211*789Sahrens 			/*
212*789Sahrens 			 * If the entity is not the owner and does not
213*789Sahrens 			 * have write permissions ACE_WRITE_ATTRIBUTES will
214*789Sahrens 			 * always go in the DENY ACE.
215*789Sahrens 			 */
216*789Sahrens 			access_mask |= ACE_WRITE_ATTRIBUTES;
217*789Sahrens 	}
218*789Sahrens 
219*789Sahrens 	if (acl_produce & synchronize_set)
220*789Sahrens 		access_mask |= ACE_SYNCHRONIZE;
221*789Sahrens 	if (acl_produce & write_owner_set)
222*789Sahrens 		access_mask |= ACE_WRITE_OWNER;
223*789Sahrens 	if (acl_produce & delete_set)
224*789Sahrens 		access_mask |= ACE_DELETE;
225*789Sahrens 	if (acl_produce & write_attrs_set)
226*789Sahrens 		access_mask |= ACE_WRITE_ATTRIBUTES;
227*789Sahrens 	if (acl_produce & read_named_set)
228*789Sahrens 		access_mask |= ACE_READ_NAMED_ATTRS;
229*789Sahrens 	if (acl_produce & write_named_set)
230*789Sahrens 		access_mask |= ACE_WRITE_NAMED_ATTRS;
231*789Sahrens 
232*789Sahrens 	return (access_mask);
233*789Sahrens }
234*789Sahrens 
235*789Sahrens /*
236*789Sahrens  * Given an mode_t, convert it into an access_mask as used
237*789Sahrens  * by nfsace, assuming aclent_t -> nfsace semantics.
238*789Sahrens  */
239*789Sahrens static uint32_t
240*789Sahrens mode_to_ace_access(mode_t mode, int isdir, int isowner, int isallow)
241*789Sahrens {
242*789Sahrens 	uint32_t access = 0;
243*789Sahrens 	int haswriteperm = 0;
244*789Sahrens 	int hasreadperm = 0;
245*789Sahrens 
246*789Sahrens 	if (isallow) {
247*789Sahrens 		haswriteperm = (mode & 02);
248*789Sahrens 		hasreadperm = (mode & 04);
249*789Sahrens 	} else {
250*789Sahrens 		haswriteperm = !(mode & 02);
251*789Sahrens 		hasreadperm = !(mode & 04);
252*789Sahrens 	}
253*789Sahrens 
254*789Sahrens 	/*
255*789Sahrens 	 * The following call takes care of correctly setting the following
256*789Sahrens 	 * mask bits in the access_mask:
257*789Sahrens 	 * ACE_SYNCHRONIZE, ACE_WRITE_OWNER, ACE_DELETE,
258*789Sahrens 	 * ACE_WRITE_ATTRIBUTES, ACE_WRITE_NAMED_ATTRS, ACE_READ_NAMED_ATTRS
259*789Sahrens 	 */
260*789Sahrens 	access = access_mask_set(haswriteperm, hasreadperm, isowner, isallow);
261*789Sahrens 
262*789Sahrens 	if (isallow) {
263*789Sahrens 		access |= ACE_READ_ACL | ACE_READ_ATTRIBUTES;
264*789Sahrens 		if (isowner)
265*789Sahrens 			access |= ACE_WRITE_ACL;
266*789Sahrens 	} else {
267*789Sahrens 		if (! isowner)
268*789Sahrens 			access |= ACE_WRITE_ACL;
269*789Sahrens 	}
270*789Sahrens 
271*789Sahrens 	/* read */
272*789Sahrens 	if (mode & 04) {
273*789Sahrens 		access |= ACE_READ_DATA;
274*789Sahrens 	}
275*789Sahrens 	/* write */
276*789Sahrens 	if (mode & 02) {
277*789Sahrens 		access |= ACE_WRITE_DATA |
278*789Sahrens 		    ACE_APPEND_DATA;
279*789Sahrens 		if (isdir)
280*789Sahrens 			access |= ACE_DELETE_CHILD;
281*789Sahrens 	}
282*789Sahrens 	/* exec */
283*789Sahrens 	if (mode & 01) {
284*789Sahrens 		access |= ACE_EXECUTE;
285*789Sahrens 	}
286*789Sahrens 
287*789Sahrens 	return (access);
288*789Sahrens }
289*789Sahrens 
290*789Sahrens /*
291*789Sahrens  * Given an nfsace (presumably an ALLOW entry), make a
292*789Sahrens  * corresponding DENY entry at the address given.
293*789Sahrens  */
294*789Sahrens static void
295*789Sahrens ace_make_deny(ace_t *allow, ace_t *deny, int isdir, int isowner)
296*789Sahrens {
297*789Sahrens 	(void) memcpy(deny, allow, sizeof (ace_t));
298*789Sahrens 
299*789Sahrens 	deny->a_who = allow->a_who;
300*789Sahrens 
301*789Sahrens 	deny->a_type = ACE_ACCESS_DENIED_ACE_TYPE;
302*789Sahrens 	deny->a_access_mask ^= ACE_POSIX_SUPPORTED_BITS;
303*789Sahrens 	if (isdir)
304*789Sahrens 		deny->a_access_mask ^= ACE_DELETE_CHILD;
305*789Sahrens 
306*789Sahrens 	deny->a_access_mask &= ~(ACE_SYNCHRONIZE | ACE_WRITE_OWNER |
307*789Sahrens 	    ACE_DELETE | ACE_WRITE_ATTRIBUTES | ACE_READ_NAMED_ATTRS |
308*789Sahrens 	    ACE_WRITE_NAMED_ATTRS);
309*789Sahrens 	deny->a_access_mask |= access_mask_set((allow->a_access_mask &
310*789Sahrens 	    ACE_WRITE_DATA), (allow->a_access_mask & ACE_READ_DATA), isowner,
311*789Sahrens 	    B_FALSE);
312*789Sahrens }
313*789Sahrens /*
314*789Sahrens  * Make an initial pass over an array of aclent_t's.  Gather
315*789Sahrens  * information such as an ACL_MASK (if any), number of users,
316*789Sahrens  * number of groups, and whether the array needs to be sorted.
317*789Sahrens  */
318*789Sahrens static int
319*789Sahrens ln_aent_preprocess(aclent_t *aclent, int n,
320*789Sahrens     int *hasmask, mode_t *mask,
321*789Sahrens     int *numuser, int *numgroup, int *needsort)
322*789Sahrens {
323*789Sahrens 	int error = 0;
324*789Sahrens 	int i;
325*789Sahrens 	int curtype = 0;
326*789Sahrens 
327*789Sahrens 	*hasmask = 0;
328*789Sahrens 	*mask = 07;
329*789Sahrens 	*needsort = 0;
330*789Sahrens 	*numuser = 0;
331*789Sahrens 	*numgroup = 0;
332*789Sahrens 
333*789Sahrens 	for (i = 0; i < n; i++) {
334*789Sahrens 		if (aclent[i].a_type < curtype)
335*789Sahrens 			*needsort = 1;
336*789Sahrens 		else if (aclent[i].a_type > curtype)
337*789Sahrens 			curtype = aclent[i].a_type;
338*789Sahrens 		if (aclent[i].a_type & USER)
339*789Sahrens 			(*numuser)++;
340*789Sahrens 		if (aclent[i].a_type & (GROUP | GROUP_OBJ))
341*789Sahrens 			(*numgroup)++;
342*789Sahrens 		if (aclent[i].a_type & CLASS_OBJ) {
343*789Sahrens 			if (*hasmask) {
344*789Sahrens 				error = EINVAL;
345*789Sahrens 				goto out;
346*789Sahrens 			} else {
347*789Sahrens 				*hasmask = 1;
348*789Sahrens 				*mask = aclent[i].a_perm;
349*789Sahrens 			}
350*789Sahrens 		}
351*789Sahrens 	}
352*789Sahrens 
353*789Sahrens 	if ((! *hasmask) && (*numuser + *numgroup > 1)) {
354*789Sahrens 		error = EINVAL;
355*789Sahrens 		goto out;
356*789Sahrens 	}
357*789Sahrens 
358*789Sahrens out:
359*789Sahrens 	return (error);
360*789Sahrens }
361*789Sahrens 
362*789Sahrens /*
363*789Sahrens  * Convert an array of aclent_t into an array of nfsace entries,
364*789Sahrens  * following POSIX draft -> nfsv4 conversion semantics as outlined in
365*789Sahrens  * the IETF draft.
366*789Sahrens  */
367*789Sahrens static int
368*789Sahrens ln_aent_to_ace(aclent_t *aclent, int n, ace_t **acepp, int *rescount, int isdir)
369*789Sahrens {
370*789Sahrens 	int error = 0;
371*789Sahrens 	mode_t mask;
372*789Sahrens 	int numuser, numgroup, needsort;
373*789Sahrens 	int resultsize = 0;
374*789Sahrens 	int i, groupi = 0, skip;
375*789Sahrens 	ace_t *acep, *result = NULL;
376*789Sahrens 	int hasmask;
377*789Sahrens 
378*789Sahrens 	error = ln_aent_preprocess(aclent, n, &hasmask, &mask,
379*789Sahrens 	    &numuser, &numgroup, &needsort);
380*789Sahrens 	if (error != 0)
381*789Sahrens 		goto out;
382*789Sahrens 
383*789Sahrens 	/* allow + deny for each aclent */
384*789Sahrens 	resultsize = n * 2;
385*789Sahrens 	if (hasmask) {
386*789Sahrens 		/*
387*789Sahrens 		 * stick extra deny on the group_obj and on each
388*789Sahrens 		 * user|group for the mask (the group_obj was added
389*789Sahrens 		 * into the count for numgroup)
390*789Sahrens 		 */
391*789Sahrens 		resultsize += numuser + numgroup;
392*789Sahrens 		/* ... and don't count the mask itself */
393*789Sahrens 		resultsize -= 2;
394*789Sahrens 	}
395*789Sahrens 
396*789Sahrens 	/* sort the source if necessary */
397*789Sahrens 	if (needsort)
398*789Sahrens 		ksort((caddr_t)aclent, n, sizeof (aclent_t), cmp2acls);
399*789Sahrens 
400*789Sahrens 	result = acep = calloc(1, resultsize * sizeof (ace_t));
401*789Sahrens 	if (result == NULL)
402*789Sahrens 		goto out;
403*789Sahrens 
404*789Sahrens 	for (i = 0; i < n; i++) {
405*789Sahrens 		/*
406*789Sahrens 		 * don't process CLASS_OBJ (mask); mask was grabbed in
407*789Sahrens 		 * ln_aent_preprocess()
408*789Sahrens 		 */
409*789Sahrens 		if (aclent[i].a_type & CLASS_OBJ)
410*789Sahrens 			continue;
411*789Sahrens 
412*789Sahrens 		/* If we need an ACL_MASK emulator, prepend it now */
413*789Sahrens 		if ((hasmask) &&
414*789Sahrens 		    (aclent[i].a_type & (USER | GROUP | GROUP_OBJ))) {
415*789Sahrens 			acep->a_type = ACE_ACCESS_DENIED_ACE_TYPE;
416*789Sahrens 			acep->a_flags = 0;
417*789Sahrens 			if (aclent[i].a_type & GROUP_OBJ) {
418*789Sahrens 				acep->a_who = -1;
419*789Sahrens 				acep->a_flags |=
420*789Sahrens 				    (ACE_IDENTIFIER_GROUP|ACE_GROUP);
421*789Sahrens 			} else if (aclent[i].a_type & USER) {
422*789Sahrens 				acep->a_who = aclent[i].a_id;
423*789Sahrens 			} else {
424*789Sahrens 				acep->a_who = aclent[i].a_id;
425*789Sahrens 				acep->a_flags |= ACE_IDENTIFIER_GROUP;
426*789Sahrens 			}
427*789Sahrens 			if (aclent[i].a_type & ACL_DEFAULT) {
428*789Sahrens 				acep->a_flags |= ACE_INHERIT_ONLY_ACE |
429*789Sahrens 				    ACE_FILE_INHERIT_ACE |
430*789Sahrens 				    ACE_DIRECTORY_INHERIT_ACE;
431*789Sahrens 			}
432*789Sahrens 			/*
433*789Sahrens 			 * Set the access mask for the prepended deny
434*789Sahrens 			 * ace.  To do this, we invert the mask (found
435*789Sahrens 			 * in ln_aent_preprocess()) then convert it to an
436*789Sahrens 			 * DENY ace access_mask.
437*789Sahrens 			 */
438*789Sahrens 			acep->a_access_mask = mode_to_ace_access((mask ^ 07),
439*789Sahrens 			    isdir, 0, 0);
440*789Sahrens 			acep += 1;
441*789Sahrens 		}
442*789Sahrens 
443*789Sahrens 		/* handle a_perm -> access_mask */
444*789Sahrens 		acep->a_access_mask = mode_to_ace_access(aclent[i].a_perm,
445*789Sahrens 		    isdir, aclent[i].a_type & USER_OBJ, 1);
446*789Sahrens 
447*789Sahrens 		/* emulate a default aclent */
448*789Sahrens 		if (aclent[i].a_type & ACL_DEFAULT) {
449*789Sahrens 			acep->a_flags |= ACE_INHERIT_ONLY_ACE |
450*789Sahrens 			    ACE_FILE_INHERIT_ACE |
451*789Sahrens 			    ACE_DIRECTORY_INHERIT_ACE;
452*789Sahrens 		}
453*789Sahrens 
454*789Sahrens 		/*
455*789Sahrens 		 * handle a_perm and a_id
456*789Sahrens 		 *
457*789Sahrens 		 * this must be done last, since it involves the
458*789Sahrens 		 * corresponding deny aces, which are handled
459*789Sahrens 		 * differently for each different a_type.
460*789Sahrens 		 */
461*789Sahrens 		if (aclent[i].a_type & USER_OBJ) {
462*789Sahrens 			acep->a_who = -1;
463*789Sahrens 			acep->a_flags |= ACE_OWNER;
464*789Sahrens 			ace_make_deny(acep, acep + 1, isdir, B_TRUE);
465*789Sahrens 			acep += 2;
466*789Sahrens 		} else if (aclent[i].a_type & USER) {
467*789Sahrens 			acep->a_who = aclent[i].a_id;
468*789Sahrens 			ace_make_deny(acep, acep + 1, isdir, B_FALSE);
469*789Sahrens 			acep += 2;
470*789Sahrens 		} else if (aclent[i].a_type & (GROUP_OBJ | GROUP)) {
471*789Sahrens 			if (aclent[i].a_type & GROUP_OBJ) {
472*789Sahrens 				acep->a_who = -1;
473*789Sahrens 				acep->a_flags |= ACE_GROUP;
474*789Sahrens 			} else {
475*789Sahrens 				acep->a_who = aclent[i].a_id;
476*789Sahrens 			}
477*789Sahrens 			acep->a_flags |= ACE_IDENTIFIER_GROUP;
478*789Sahrens 			/*
479*789Sahrens 			 * Set the corresponding deny for the group ace.
480*789Sahrens 			 *
481*789Sahrens 			 * The deny aces go after all of the groups, unlike
482*789Sahrens 			 * everything else, where they immediately follow
483*789Sahrens 			 * the allow ace.
484*789Sahrens 			 *
485*789Sahrens 			 * We calculate "skip", the number of slots to
486*789Sahrens 			 * skip ahead for the deny ace, here.
487*789Sahrens 			 *
488*789Sahrens 			 * The pattern is:
489*789Sahrens 			 * MD1 A1 MD2 A2 MD3 A3 D1 D2 D3
490*789Sahrens 			 * thus, skip is
491*789Sahrens 			 * (2 * numgroup) - 1 - groupi
492*789Sahrens 			 * (2 * numgroup) to account for MD + A
493*789Sahrens 			 * - 1 to account for the fact that we're on the
494*789Sahrens 			 * access (A), not the mask (MD)
495*789Sahrens 			 * - groupi to account for the fact that we have
496*789Sahrens 			 * passed up groupi number of MD's.
497*789Sahrens 			 */
498*789Sahrens 			skip = (2 * numgroup) - 1 - groupi;
499*789Sahrens 			ace_make_deny(acep, acep + skip, isdir, B_FALSE);
500*789Sahrens 			/*
501*789Sahrens 			 * If we just did the last group, skip acep past
502*789Sahrens 			 * all of the denies; else, just move ahead one.
503*789Sahrens 			 */
504*789Sahrens 			if (++groupi >= numgroup)
505*789Sahrens 				acep += numgroup + 1;
506*789Sahrens 			else
507*789Sahrens 				acep += 1;
508*789Sahrens 		} else if (aclent[i].a_type & OTHER_OBJ) {
509*789Sahrens 			acep->a_who = -1;
510*789Sahrens 			acep->a_flags |= ACE_EVERYONE;
511*789Sahrens 			ace_make_deny(acep, acep + 1, isdir, B_FALSE);
512*789Sahrens 			acep += 2;
513*789Sahrens 		} else {
514*789Sahrens 			error = EINVAL;
515*789Sahrens 			goto out;
516*789Sahrens 		}
517*789Sahrens 	}
518*789Sahrens 
519*789Sahrens 	*acepp = result;
520*789Sahrens 	*rescount = resultsize;
521*789Sahrens 
522*789Sahrens out:
523*789Sahrens 	if (error != 0) {
524*789Sahrens 		if ((result != NULL) && (resultsize > 0)) {
525*789Sahrens 			free(result);
526*789Sahrens 		}
527*789Sahrens 	}
528*789Sahrens 
529*789Sahrens 	return (error);
530*789Sahrens }
531*789Sahrens 
532*789Sahrens static int
533*789Sahrens convert_aent_to_ace(aclent_t *aclentp, int aclcnt, int isdir,
534*789Sahrens     ace_t **retacep, int *retacecnt)
535*789Sahrens {
536*789Sahrens 	ace_t *acep;
537*789Sahrens 	ace_t *dfacep;
538*789Sahrens 	ace_t *newacep;
539*789Sahrens 	int acecnt = 0;
540*789Sahrens 	int dfacecnt = 0;
541*789Sahrens 	int dfaclstart = 0;
542*789Sahrens 	int dfaclcnt = 0;
543*789Sahrens 	aclent_t *aclp;
544*789Sahrens 	int i;
545*789Sahrens 	int error;
546*789Sahrens 
547*789Sahrens 	ksort((caddr_t)aclentp, aclcnt, sizeof (aclent_t), cmp2acls);
548*789Sahrens 
549*789Sahrens 	for (i = 0, aclp = aclentp; i < aclcnt; aclp++, i++) {
550*789Sahrens 		if (aclp->a_type & ACL_DEFAULT)
551*789Sahrens 			break;
552*789Sahrens 	}
553*789Sahrens 
554*789Sahrens 	if (i < aclcnt) {
555*789Sahrens 		dfaclstart = aclcnt - i;
556*789Sahrens 		dfaclcnt = i;
557*789Sahrens 	}
558*789Sahrens 
559*789Sahrens 	if (dfaclcnt && isdir == 0) {
560*789Sahrens 		return (-1);
561*789Sahrens 	}
562*789Sahrens 
563*789Sahrens 	error = ln_aent_to_ace(aclentp, i,  &acep, &acecnt, isdir);
564*789Sahrens 	if (error)
565*789Sahrens 		return (-1);
566*789Sahrens 
567*789Sahrens 	if (dfaclcnt) {
568*789Sahrens 		error = ln_aent_to_ace(&aclentp[dfaclstart], dfaclcnt,
569*789Sahrens 		    &dfacep, &dfacecnt, isdir);
570*789Sahrens 		if (error) {
571*789Sahrens 			if (acep) {
572*789Sahrens 				free(acep);
573*789Sahrens 			}
574*789Sahrens 			return (-1);
575*789Sahrens 		}
576*789Sahrens 	}
577*789Sahrens 
578*789Sahrens 	newacep = malloc(sizeof (ace_t) * (acecnt + dfacecnt));
579*789Sahrens 	if (newacep == NULL)
580*789Sahrens 		return (-1);
581*789Sahrens 
582*789Sahrens 	(void) memcpy(newacep, acep, sizeof (ace_t) * acecnt);
583*789Sahrens 	if (dfaclcnt) {
584*789Sahrens 		(void) memcpy(newacep + acecnt, dfacep,
585*789Sahrens 		    sizeof (ace_t) * dfacecnt);
586*789Sahrens 	}
587*789Sahrens 	free(acep);
588*789Sahrens 	if (dfaclcnt)
589*789Sahrens 		free(dfacep);
590*789Sahrens 
591*789Sahrens 	*retacecnt = acecnt + dfacecnt;
592*789Sahrens 	*retacep = newacep;
593*789Sahrens 	return (0);
594*789Sahrens }
595*789Sahrens 
596*789Sahrens 
597*789Sahrens static int
598*789Sahrens cacl_get(acl_inp inp, int get_flag, int type, acl_t **aclp)
599*789Sahrens {
600*789Sahrens 	const char *fname;
601*789Sahrens 	int fd;
602*789Sahrens 	int ace_acl = 0;
603*789Sahrens 	int error;
604*789Sahrens 	int getcmd, cntcmd;
605*789Sahrens 	acl_t *acl_info;
606*789Sahrens 	int	save_errno;
607*789Sahrens 	int	stat_error;
608*789Sahrens 	struct stat64 statbuf;
609*789Sahrens 
610*789Sahrens 	*aclp = NULL;
611*789Sahrens 	if (type == ACL_PATH) {
612*789Sahrens 		fname = inp.file;
613*789Sahrens 		ace_acl = pathconf(fname, _PC_ACL_ENABLED);
614*789Sahrens 	} else {
615*789Sahrens 		fd = inp.fd;
616*789Sahrens 		ace_acl = fpathconf(fd, _PC_ACL_ENABLED);
617*789Sahrens 	}
618*789Sahrens 
619*789Sahrens 	if (ace_acl == -1)
620*789Sahrens 		return (-1);
621*789Sahrens 
622*789Sahrens 	/*
623*789Sahrens 	 * if acl's aren't supported then
624*789Sahrens 	 * send it through the old GETACL interface
625*789Sahrens 	 */
626*789Sahrens 	if (ace_acl == 0) {
627*789Sahrens 		ace_acl = _ACL_ACLENT_ENABLED;
628*789Sahrens 	}
629*789Sahrens 
630*789Sahrens 	if (ace_acl & _ACL_ACE_ENABLED) {
631*789Sahrens 		cntcmd = ACE_GETACLCNT;
632*789Sahrens 		getcmd = ACE_GETACL;
633*789Sahrens 		acl_info = acl_alloc(ACE_T);
634*789Sahrens 	} else {
635*789Sahrens 		cntcmd = GETACLCNT;
636*789Sahrens 		getcmd = GETACL;
637*789Sahrens 		acl_info = acl_alloc(ACLENT_T);
638*789Sahrens 	}
639*789Sahrens 
640*789Sahrens 	if (acl_info == NULL)
641*789Sahrens 		return (-1);
642*789Sahrens 
643*789Sahrens 	if (type == ACL_PATH) {
644*789Sahrens 		acl_info->acl_cnt = acl(fname, cntcmd, 0, NULL);
645*789Sahrens 	} else {
646*789Sahrens 		acl_info->acl_cnt = facl(fd, cntcmd, 0, NULL);
647*789Sahrens 	}
648*789Sahrens 
649*789Sahrens 	save_errno = errno;
650*789Sahrens 	if (acl_info->acl_cnt < 0) {
651*789Sahrens 		acl_free(acl_info);
652*789Sahrens 		errno = save_errno;
653*789Sahrens 		return (-1);
654*789Sahrens 	}
655*789Sahrens 
656*789Sahrens 	if (acl_info->acl_cnt == 0) {
657*789Sahrens 		acl_free(acl_info);
658*789Sahrens 		errno = save_errno;
659*789Sahrens 		return (0);
660*789Sahrens 	}
661*789Sahrens 
662*789Sahrens 	acl_info->acl_aclp =
663*789Sahrens 	    malloc(acl_info->acl_cnt * acl_info->acl_entry_size);
664*789Sahrens 	save_errno = errno;
665*789Sahrens 
666*789Sahrens 	if (acl_info->acl_aclp == NULL) {
667*789Sahrens 		acl_free(acl_info);
668*789Sahrens 		errno = save_errno;
669*789Sahrens 		return (-1);
670*789Sahrens 	}
671*789Sahrens 
672*789Sahrens 	if (type == ACL_PATH) {
673*789Sahrens 		stat_error = stat64(fname, &statbuf);
674*789Sahrens 		error = acl(fname, getcmd, acl_info->acl_cnt,
675*789Sahrens 		    acl_info->acl_aclp);
676*789Sahrens 	} else {
677*789Sahrens 		stat_error = fstat64(fd, &statbuf);
678*789Sahrens 		error = facl(fd, getcmd, acl_info->acl_cnt,
679*789Sahrens 		    acl_info->acl_aclp);
680*789Sahrens 	}
681*789Sahrens 
682*789Sahrens 	save_errno = errno;
683*789Sahrens 	if (error == -1) {
684*789Sahrens 		acl_free(acl_info);
685*789Sahrens 		errno = save_errno;
686*789Sahrens 		return (-1);
687*789Sahrens 	}
688*789Sahrens 
689*789Sahrens 
690*789Sahrens 	if (stat_error == 0) {
691*789Sahrens 		acl_info->acl_flags =
692*789Sahrens 		    (S_ISDIR(statbuf.st_mode) ? ACL_IS_DIR : 0);
693*789Sahrens 	} else
694*789Sahrens 		acl_info->acl_flags = 0;
695*789Sahrens 
696*789Sahrens 	switch (acl_info->acl_type) {
697*789Sahrens 	case ACLENT_T:
698*789Sahrens 		if (acl_info->acl_cnt <= MIN_ACL_ENTRIES)
699*789Sahrens 			acl_info->acl_flags |= ACL_IS_TRIVIAL;
700*789Sahrens 		break;
701*789Sahrens 	case ACE_T:
702*789Sahrens 		if (ace_trivial(acl_info->acl_aclp, acl_info->acl_cnt) == 0)
703*789Sahrens 			acl_info->acl_flags |= ACL_IS_TRIVIAL;
704*789Sahrens 		break;
705*789Sahrens 	default:
706*789Sahrens 		errno = EINVAL;
707*789Sahrens 		acl_free(acl_info);
708*789Sahrens 		return (-1);
709*789Sahrens 	}
710*789Sahrens 
711*789Sahrens 	if ((acl_info->acl_flags & ACL_IS_TRIVIAL) &&
712*789Sahrens 	    (get_flag & ACL_NO_TRIVIAL)) {
713*789Sahrens 		acl_free(acl_info);
714*789Sahrens 		errno = 0;
715*789Sahrens 		return (0);
716*789Sahrens 	}
717*789Sahrens 
718*789Sahrens 	*aclp = acl_info;
719*789Sahrens 	return (0);
720*789Sahrens }
721*789Sahrens 
722*789Sahrens /*
723*789Sahrens  * return -1 on failure, otherwise the number of acl
724*789Sahrens  * entries is returned
725*789Sahrens  */
726*789Sahrens int
727*789Sahrens acl_get(const char *path, int get_flag, acl_t **aclp)
728*789Sahrens {
729*789Sahrens 	acl_inp acl_inp;
730*789Sahrens 	acl_inp.file = path;
731*789Sahrens 
732*789Sahrens 	return (cacl_get(acl_inp, get_flag, ACL_PATH, aclp));
733*789Sahrens }
734*789Sahrens 
735*789Sahrens int
736*789Sahrens facl_get(int fd, int get_flag, acl_t **aclp)
737*789Sahrens {
738*789Sahrens 
739*789Sahrens 	acl_inp acl_inp;
740*789Sahrens 	acl_inp.fd = fd;
741*789Sahrens 
742*789Sahrens 	return (cacl_get(acl_inp, get_flag, ACL_FD, aclp));
743*789Sahrens }
744*789Sahrens 
745*789Sahrens /*
746*789Sahrens  * Set an ACL, translates acl to ace_t when appropriate.
747*789Sahrens  */
748*789Sahrens static int
749*789Sahrens cacl_set(acl_inp *acl_inp, acl_t *aclp, int type)
750*789Sahrens {
751*789Sahrens 	int error = 0;
752*789Sahrens 	int acl_flavor_target;
753*789Sahrens 	ace_t *acep = NULL;
754*789Sahrens 	int acecnt;
755*789Sahrens 	struct stat64 statbuf;
756*789Sahrens 	int stat_error;
757*789Sahrens 	int isdir;
758*789Sahrens 
759*789Sahrens 
760*789Sahrens 	if (type == ACL_PATH) {
761*789Sahrens 		stat_error = stat64(acl_inp->file, &statbuf);
762*789Sahrens 		if (stat_error)
763*789Sahrens 			return (-1);
764*789Sahrens 		acl_flavor_target = pathconf(acl_inp->file, _PC_ACL_ENABLED);
765*789Sahrens 	} else {
766*789Sahrens 		stat_error = fstat64(acl_inp->fd, &statbuf);
767*789Sahrens 		if (stat_error)
768*789Sahrens 			return (-1);
769*789Sahrens 		acl_flavor_target = fpathconf(acl_inp->fd, _PC_ACL_ENABLED);
770*789Sahrens 	}
771*789Sahrens 
772*789Sahrens 	isdir = S_ISDIR(statbuf.st_mode);
773*789Sahrens 
774*789Sahrens 	if (acl_flavor_target == -1)
775*789Sahrens 		return (-1);
776*789Sahrens 
777*789Sahrens 	/*
778*789Sahrens 	 * Translate aclent_t ACL's to ACE ACL's.
779*789Sahrens 	 */
780*789Sahrens 	if (acl_flavor_target ==  _ACL_ACE_ENABLED &&
781*789Sahrens 	    aclp->acl_type == ACLENT_T) {
782*789Sahrens 		error = convert_aent_to_ace(aclp->acl_aclp,
783*789Sahrens 		    aclp->acl_cnt, isdir, &acep, &acecnt);
784*789Sahrens 		if (error) {
785*789Sahrens 			errno = ENOTSUP;
786*789Sahrens 			return (-1);
787*789Sahrens 		}
788*789Sahrens 		/*
789*789Sahrens 		 * replace old acl with newly translated acl
790*789Sahrens 		 */
791*789Sahrens 		free(aclp->acl_aclp);
792*789Sahrens 		aclp->acl_aclp = acep;
793*789Sahrens 		aclp->acl_cnt = acecnt;
794*789Sahrens 		aclp->acl_type = ACE_T;
795*789Sahrens 	}
796*789Sahrens 
797*789Sahrens 	if (type == ACL_PATH) {
798*789Sahrens 		error = acl(acl_inp->file,
799*789Sahrens 		    (aclp->acl_type == ACE_T) ? ACE_SETACL : SETACL,
800*789Sahrens 		    aclp->acl_cnt, aclp->acl_aclp);
801*789Sahrens 	} else {
802*789Sahrens 		error = facl(acl_inp->fd,
803*789Sahrens 		    (aclp->acl_type == ACE_T) ? ACE_SETACL : SETACL,
804*789Sahrens 		    aclp->acl_cnt, aclp->acl_aclp);
805*789Sahrens 	}
806*789Sahrens 
807*789Sahrens 	return (error);
808*789Sahrens }
809*789Sahrens 
810*789Sahrens int
811*789Sahrens acl_set(const char *path, acl_t *aclp)
812*789Sahrens {
813*789Sahrens 	acl_inp acl_inp;
814*789Sahrens 
815*789Sahrens 	acl_inp.file = path;
816*789Sahrens 
817*789Sahrens 	return (cacl_set(&acl_inp, aclp, ACL_PATH));
818*789Sahrens }
819*789Sahrens 
820*789Sahrens int
821*789Sahrens facl_set(int fd, acl_t *aclp)
822*789Sahrens {
823*789Sahrens 	acl_inp acl_inp;
824*789Sahrens 
825*789Sahrens 	acl_inp.fd = fd;
826*789Sahrens 
827*789Sahrens 	return (cacl_set(&acl_inp, aclp, ACL_FD));
828*789Sahrens }
829*789Sahrens 
830*789Sahrens int
831*789Sahrens acl_cnt(acl_t *aclp)
832*789Sahrens {
833*789Sahrens 	return (aclp->acl_cnt);
834*789Sahrens }
835*789Sahrens 
836*789Sahrens int
837*789Sahrens acl_type(acl_t *aclp)
838*789Sahrens {
839*789Sahrens 	return (aclp->acl_type);
840*789Sahrens }
841*789Sahrens 
842*789Sahrens acl_t *
843*789Sahrens acl_dup(acl_t *aclp)
844*789Sahrens {
845*789Sahrens 	acl_t *newaclp;
846*789Sahrens 
847*789Sahrens 	newaclp = acl_alloc(aclp->acl_type);
848*789Sahrens 	if (newaclp == NULL)
849*789Sahrens 		return (NULL);
850*789Sahrens 
851*789Sahrens 	newaclp->acl_aclp = malloc(aclp->acl_entry_size * aclp->acl_cnt);
852*789Sahrens 	if (newaclp->acl_aclp == NULL) {
853*789Sahrens 		acl_free(newaclp);
854*789Sahrens 		return (NULL);
855*789Sahrens 	}
856*789Sahrens 
857*789Sahrens 	(void) memcpy(newaclp->acl_aclp,
858*789Sahrens 	    aclp->acl_aclp, aclp->acl_entry_size * aclp->acl_cnt);
859*789Sahrens 	newaclp->acl_cnt = aclp->acl_cnt;
860*789Sahrens 
861*789Sahrens 	return (newaclp);
862*789Sahrens }
863*789Sahrens 
864*789Sahrens int
865*789Sahrens acl_flags(acl_t *aclp)
866*789Sahrens {
867*789Sahrens 	return (aclp->acl_flags);
868*789Sahrens }
869*789Sahrens 
870*789Sahrens void *
871*789Sahrens acl_data(acl_t *aclp)
872*789Sahrens {
873*789Sahrens 	return (aclp->acl_aclp);
874*789Sahrens }
875*789Sahrens 
876*789Sahrens /*
877*789Sahrens  * Remove an ACL from a file and create a trivial ACL based
878*789Sahrens  * off of the mode argument.  After acl has been set owner/group
879*789Sahrens  * are updated to match owner,group arguments
880*789Sahrens  */
881*789Sahrens int
882*789Sahrens acl_strip(const char *file, uid_t owner, gid_t group, mode_t mode)
883*789Sahrens {
884*789Sahrens 	int	error = 0;
885*789Sahrens 	aclent_t min_acl[MIN_ACL_ENTRIES];
886*789Sahrens 	ace_t	min_ace_acl[6];	/* owner, group, everyone + complement denies */
887*789Sahrens 	int	acl_flavor;
888*789Sahrens 	int	aclcnt;
889*789Sahrens 
890*789Sahrens 	acl_flavor = pathconf(file, _PC_ACL_ENABLED);
891*789Sahrens 
892*789Sahrens 	if (acl_flavor == -1)
893*789Sahrens 		return (-1);
894*789Sahrens 	/*
895*789Sahrens 	 * force it through aclent flavor when file system doesn't
896*789Sahrens 	 * understand question
897*789Sahrens 	 */
898*789Sahrens 	if (acl_flavor == 0)
899*789Sahrens 		acl_flavor = _ACL_ACLENT_ENABLED;
900*789Sahrens 
901*789Sahrens 	if (acl_flavor & _ACL_ACLENT_ENABLED) {
902*789Sahrens 		min_acl[0].a_type = USER_OBJ;
903*789Sahrens 		min_acl[0].a_id   = owner;
904*789Sahrens 		min_acl[0].a_perm = ((mode & 0700) >> 6);
905*789Sahrens 		min_acl[1].a_type = GROUP_OBJ;
906*789Sahrens 		min_acl[1].a_id   = group;
907*789Sahrens 		min_acl[1].a_perm = ((mode & 0070) >> 3);
908*789Sahrens 		min_acl[2].a_type = CLASS_OBJ;
909*789Sahrens 		min_acl[2].a_id   = (uid_t)-1;
910*789Sahrens 		min_acl[2].a_perm = ((mode & 0070) >> 3);
911*789Sahrens 		min_acl[3].a_type = OTHER_OBJ;
912*789Sahrens 		min_acl[3].a_id   = (uid_t)-1;
913*789Sahrens 		min_acl[3].a_perm = (mode & 0007);
914*789Sahrens 		aclcnt = 4;
915*789Sahrens 		error = acl(file, SETACL, aclcnt, min_acl);
916*789Sahrens 	} else if (acl_flavor & _ACL_ACE_ENABLED) {
917*789Sahrens 		(void) memcpy(min_ace_acl, trivial_acl, sizeof (ace_t) * 6);
918*789Sahrens 
919*789Sahrens 		/*
920*789Sahrens 		 * Make aces match request mode
921*789Sahrens 		 */
922*789Sahrens 		adjust_ace_pair(&min_ace_acl[0], (mode & 0700) >> 6);
923*789Sahrens 		adjust_ace_pair(&min_ace_acl[2], (mode & 0070) >> 3);
924*789Sahrens 		adjust_ace_pair(&min_ace_acl[4], mode & 0007);
925*789Sahrens 
926*789Sahrens 		error = acl(file, ACE_SETACL, 6, min_ace_acl);
927*789Sahrens 	} else {
928*789Sahrens 		errno = EINVAL;
929*789Sahrens 		error = 1;
930*789Sahrens 	}
931*789Sahrens 
932*789Sahrens 	if (error == 0)
933*789Sahrens 		error = chown(file, owner, group);
934*789Sahrens 	return (error);
935*789Sahrens }
936*789Sahrens 
937*789Sahrens static int
938*789Sahrens ace_match(void *entry1, void *entry2)
939*789Sahrens {
940*789Sahrens 	ace_t *p1 = (ace_t *)entry1;
941*789Sahrens 	ace_t *p2 = (ace_t *)entry2;
942*789Sahrens 	ace_t ace1, ace2;
943*789Sahrens 
944*789Sahrens 	ace1 = *p1;
945*789Sahrens 	ace2 = *p2;
946*789Sahrens 
947*789Sahrens 	/*
948*789Sahrens 	 * Need to fixup who field for abstrations for
949*789Sahrens 	 * accurate comparison, since field is undefined.
950*789Sahrens 	 */
951*789Sahrens 	if (ace1.a_flags & (ACE_OWNER|ACE_GROUP|ACE_EVERYONE))
952*789Sahrens 		ace1.a_who = -1;
953*789Sahrens 	if (ace2.a_flags & (ACE_OWNER|ACE_GROUP|ACE_EVERYONE))
954*789Sahrens 		ace2.a_who = -1;
955*789Sahrens 	return (memcmp(&ace1, &ace2, sizeof (ace_t)));
956*789Sahrens }
957*789Sahrens 
958*789Sahrens static int
959*789Sahrens aclent_match(void *entry1, void *entry2)
960*789Sahrens {
961*789Sahrens 	aclent_t *aclent1 = (aclent_t *)entry1;
962*789Sahrens 	aclent_t *aclent2 = (aclent_t *)entry2;
963*789Sahrens 
964*789Sahrens 	return (memcmp(aclent1, aclent2, sizeof (aclent_t)));
965*789Sahrens }
966*789Sahrens 
967*789Sahrens /*
968*789Sahrens  * Find acl entries in acl that correspond to removeacl.  Search
969*789Sahrens  * is started from slot.  The flag argument indicates whether to
970*789Sahrens  * remove all matches or just the first match.
971*789Sahrens  */
972*789Sahrens int
973*789Sahrens acl_removeentries(acl_t *acl, acl_t *removeacl, int start_slot, int flag)
974*789Sahrens {
975*789Sahrens 	int i, j;
976*789Sahrens 	int match;
977*789Sahrens 	int (*acl_match)(void *acl1, void *acl2);
978*789Sahrens 	void *acl_entry, *remove_entry;
979*789Sahrens 	void *start;
980*789Sahrens 	int found = 0;
981*789Sahrens 
982*789Sahrens 	if (flag != ACL_REMOVE_ALL && flag != ACL_REMOVE_FIRST)
983*789Sahrens 		flag = ACL_REMOVE_FIRST;
984*789Sahrens 
985*789Sahrens 	if (acl == NULL || removeacl == NULL)
986*789Sahrens 		return (EACL_NO_ACL_ENTRY);
987*789Sahrens 
988*789Sahrens 	if (acl->acl_type != removeacl->acl_type)
989*789Sahrens 		return (EACL_DIFF_TYPE);
990*789Sahrens 
991*789Sahrens 	if (acl->acl_type == ACLENT_T)
992*789Sahrens 		acl_match = aclent_match;
993*789Sahrens 	else
994*789Sahrens 		acl_match = ace_match;
995*789Sahrens 
996*789Sahrens 	for (i = 0, remove_entry = removeacl->acl_aclp;
997*789Sahrens 	    i != removeacl->acl_cnt; i++) {
998*789Sahrens 
999*789Sahrens 		j = 0;
1000*789Sahrens 		acl_entry = (char *)acl->acl_aclp +
1001*789Sahrens 		    (acl->acl_entry_size * start_slot);
1002*789Sahrens 		for (;;) {
1003*789Sahrens 			match = acl_match(acl_entry, remove_entry);
1004*789Sahrens 			if (match == 0)  {
1005*789Sahrens 				found++;
1006*789Sahrens 				start = (char *)acl_entry +
1007*789Sahrens 				    acl->acl_entry_size;
1008*789Sahrens 				(void) memmove(acl_entry, start,
1009*789Sahrens 				    acl->acl_entry_size *
1010*789Sahrens 				    acl->acl_cnt-- - (j + 1));
1011*789Sahrens 
1012*789Sahrens 				if (flag == ACL_REMOVE_FIRST)
1013*789Sahrens 					break;
1014*789Sahrens 				/*
1015*789Sahrens 				 * List has changed, restart search from
1016*789Sahrens 				 * beginning.
1017*789Sahrens 				 */
1018*789Sahrens 				acl_entry = acl->acl_aclp;
1019*789Sahrens 				j = 0;
1020*789Sahrens 				continue;
1021*789Sahrens 			}
1022*789Sahrens 			acl_entry = ((char *)acl_entry + acl->acl_entry_size);
1023*789Sahrens 			if (++j >= acl->acl_cnt) {
1024*789Sahrens 				break;
1025*789Sahrens 			}
1026*789Sahrens 		}
1027*789Sahrens 	}
1028*789Sahrens 
1029*789Sahrens 	return ((found == 0) ? EACL_NO_ACL_ENTRY : 0);
1030*789Sahrens }
1031*789Sahrens 
1032*789Sahrens /*
1033*789Sahrens  * Replace entires entries in acl1 with the corresponding entries
1034*789Sahrens  * in newentries.  The where argument specifies where to begin
1035*789Sahrens  * the replacement.  If the where argument is 1 greater than the
1036*789Sahrens  * number of acl entries in acl1 then they are appended.  If the
1037*789Sahrens  * where argument is 2+ greater than the number of acl entries then
1038*789Sahrens  * EACL_INVALID_SLOT is returned.
1039*789Sahrens  */
1040*789Sahrens int
1041*789Sahrens acl_modifyentries(acl_t *acl1, acl_t *newentries, int where)
1042*789Sahrens {
1043*789Sahrens 
1044*789Sahrens 	int slot;
1045*789Sahrens 	int slots_needed;
1046*789Sahrens 	int slots_left;
1047*789Sahrens 	int newsize;
1048*789Sahrens 
1049*789Sahrens 	if (acl1 == NULL || newentries == NULL)
1050*789Sahrens 		return (EACL_NO_ACL_ENTRY);
1051*789Sahrens 
1052*789Sahrens 	if (where < 0 || where >= acl1->acl_cnt)
1053*789Sahrens 		return (EACL_INVALID_SLOT);
1054*789Sahrens 
1055*789Sahrens 	if (acl1->acl_type != newentries->acl_type)
1056*789Sahrens 		return (EACL_DIFF_TYPE);
1057*789Sahrens 
1058*789Sahrens 	slot = where;
1059*789Sahrens 
1060*789Sahrens 	slots_left = acl1->acl_cnt - slot + 1;
1061*789Sahrens 	if (slots_left < newentries->acl_cnt) {
1062*789Sahrens 		slots_needed = newentries->acl_cnt - slots_left;
1063*789Sahrens 		newsize = (acl1->acl_entry_size * acl1->acl_cnt) +
1064*789Sahrens 		    (acl1->acl_entry_size * slots_needed);
1065*789Sahrens 		acl1->acl_aclp = realloc(acl1->acl_aclp, newsize);
1066*789Sahrens 		if (acl1->acl_aclp == NULL)
1067*789Sahrens 			return (-1);
1068*789Sahrens 	}
1069*789Sahrens 	(void) memcpy((char *)acl1->acl_aclp + (acl1->acl_entry_size * slot),
1070*789Sahrens 	    newentries->acl_aclp,
1071*789Sahrens 	    newentries->acl_entry_size * newentries->acl_cnt);
1072*789Sahrens 
1073*789Sahrens 	/*
1074*789Sahrens 	 * Did ACL grow?
1075*789Sahrens 	 */
1076*789Sahrens 
1077*789Sahrens 	if ((slot + newentries->acl_cnt) > acl1->acl_cnt) {
1078*789Sahrens 		acl1->acl_cnt = slot + newentries->acl_cnt;
1079*789Sahrens 	}
1080*789Sahrens 
1081*789Sahrens 	return (0);
1082*789Sahrens }
1083*789Sahrens 
1084*789Sahrens /*
1085*789Sahrens  * Add acl2 entries into acl1.  The where argument specifies where
1086*789Sahrens  * to add the entries.
1087*789Sahrens  */
1088*789Sahrens int
1089*789Sahrens acl_addentries(acl_t *acl1, acl_t *acl2, int where)
1090*789Sahrens {
1091*789Sahrens 
1092*789Sahrens 	int newsize;
1093*789Sahrens 	int len;
1094*789Sahrens 	void *start;
1095*789Sahrens 	void *to;
1096*789Sahrens 
1097*789Sahrens 	if (acl1 == NULL || acl2 == NULL)
1098*789Sahrens 		return (EACL_NO_ACL_ENTRY);
1099*789Sahrens 
1100*789Sahrens 	if (acl1->acl_type != acl2->acl_type)
1101*789Sahrens 		return (EACL_DIFF_TYPE);
1102*789Sahrens 
1103*789Sahrens 	/*
1104*789Sahrens 	 * allow where to specify 1 past last slot for an append operation
1105*789Sahrens 	 * but anything greater is an error.
1106*789Sahrens 	 */
1107*789Sahrens 	if (where < 0 || where > acl1->acl_cnt)
1108*789Sahrens 		return (EACL_INVALID_SLOT);
1109*789Sahrens 
1110*789Sahrens 	newsize = (acl2->acl_entry_size * acl2->acl_cnt) +
1111*789Sahrens 	    (acl1->acl_entry_size * acl1->acl_cnt);
1112*789Sahrens 	acl1->acl_aclp = realloc(acl1->acl_aclp, newsize);
1113*789Sahrens 	if (acl1->acl_aclp == NULL)
1114*789Sahrens 		return (-1);
1115*789Sahrens 
1116*789Sahrens 	/*
1117*789Sahrens 	 * first push down entries where new ones will be inserted
1118*789Sahrens 	 */
1119*789Sahrens 
1120*789Sahrens 	to = (void *)((char *)acl1->acl_aclp +
1121*789Sahrens 	    ((where + acl2->acl_cnt) * acl1->acl_entry_size));
1122*789Sahrens 
1123*789Sahrens 	start = (void *)((char *)acl1->acl_aclp +
1124*789Sahrens 	    where * acl1->acl_entry_size);
1125*789Sahrens 
1126*789Sahrens 	if (where < acl1->acl_cnt) {
1127*789Sahrens 		len = (acl1->acl_cnt - where) * acl1->acl_entry_size;
1128*789Sahrens 		(void) memmove(to, start, len);
1129*789Sahrens 	}
1130*789Sahrens 
1131*789Sahrens 	/*
1132*789Sahrens 	 * now stick in new entries.
1133*789Sahrens 	 */
1134*789Sahrens 
1135*789Sahrens 	(void) memmove(start, acl2->acl_aclp,
1136*789Sahrens 	    acl2->acl_cnt * acl2->acl_entry_size);
1137*789Sahrens 
1138*789Sahrens 	acl1->acl_cnt += acl2->acl_cnt;
1139*789Sahrens 	return (0);
1140*789Sahrens }
1141*789Sahrens 
1142*789Sahrens static void
1143*789Sahrens aclent_perms(int perm, char *txt_perms)
1144*789Sahrens {
1145*789Sahrens 	if (perm & S_IROTH)
1146*789Sahrens 		txt_perms[0] = 'r';
1147*789Sahrens 	else
1148*789Sahrens 		txt_perms[0] = '-';
1149*789Sahrens 	if (perm & S_IWOTH)
1150*789Sahrens 		txt_perms[1] = 'w';
1151*789Sahrens 	else
1152*789Sahrens 		txt_perms[1] = '-';
1153*789Sahrens 	if (perm & S_IXOTH)
1154*789Sahrens 		txt_perms[2] = 'x';
1155*789Sahrens 	else
1156*789Sahrens 		txt_perms[2] = '-';
1157*789Sahrens 	txt_perms[3] = '\0';
1158*789Sahrens }
1159*789Sahrens 
1160*789Sahrens static char *
1161*789Sahrens pruname(uid_t uid)
1162*789Sahrens {
1163*789Sahrens 	struct passwd	*passwdp;
1164*789Sahrens 	static char	uidp[10];	/* big enough */
1165*789Sahrens 
1166*789Sahrens 	passwdp = getpwuid(uid);
1167*789Sahrens 	if (passwdp == (struct passwd *)NULL) {
1168*789Sahrens 		/* could not get passwd information: display uid instead */
1169*789Sahrens 		(void) sprintf(uidp, "%ld", (long)uid);
1170*789Sahrens 		return (uidp);
1171*789Sahrens 	} else
1172*789Sahrens 		return (passwdp->pw_name);
1173*789Sahrens }
1174*789Sahrens 
1175*789Sahrens static char *
1176*789Sahrens prgname(gid_t gid)
1177*789Sahrens {
1178*789Sahrens 	struct group	*groupp;
1179*789Sahrens 	static char	gidp[10];	/* big enough */
1180*789Sahrens 
1181*789Sahrens 	groupp = getgrgid(gid);
1182*789Sahrens 	if (groupp == (struct group *)NULL) {
1183*789Sahrens 		/* could not get group information: display gid instead */
1184*789Sahrens 		(void) sprintf(gidp, "%ld", (long)gid);
1185*789Sahrens 		return (gidp);
1186*789Sahrens 	} else
1187*789Sahrens 		return (groupp->gr_name);
1188*789Sahrens }
1189*789Sahrens static void
1190*789Sahrens aclent_printacl(acl_t *aclp)
1191*789Sahrens {
1192*789Sahrens 	aclent_t *tp;
1193*789Sahrens 	int aclcnt;
1194*789Sahrens 	int mask;
1195*789Sahrens 	int slot = 0;
1196*789Sahrens 	char perm[4];
1197*789Sahrens 
1198*789Sahrens 	/* display ACL: assume it is sorted. */
1199*789Sahrens 	aclcnt = aclp->acl_cnt;
1200*789Sahrens 	for (tp = aclp->acl_aclp; aclcnt--; tp++) {
1201*789Sahrens 		if (tp->a_type == CLASS_OBJ)
1202*789Sahrens 			mask = tp->a_perm;
1203*789Sahrens 	}
1204*789Sahrens 	aclcnt = aclp->acl_cnt;
1205*789Sahrens 	for (tp = aclp->acl_aclp; aclcnt--; tp++) {
1206*789Sahrens 		(void) printf("     %d:", slot++);
1207*789Sahrens 		switch (tp->a_type) {
1208*789Sahrens 		case USER:
1209*789Sahrens 			aclent_perms(tp->a_perm, perm);
1210*789Sahrens 			(void) printf("user:%s:%s\t\t",
1211*789Sahrens 			    pruname(tp->a_id), perm);
1212*789Sahrens 			aclent_perms((tp->a_perm & mask), perm);
1213*789Sahrens 			(void) printf("#effective:%s\n", perm);
1214*789Sahrens 			break;
1215*789Sahrens 		case USER_OBJ:
1216*789Sahrens 			/* no need to display uid */
1217*789Sahrens 			aclent_perms(tp->a_perm, perm);
1218*789Sahrens 			(void) printf("user::%s\n", perm);
1219*789Sahrens 			break;
1220*789Sahrens 		case GROUP:
1221*789Sahrens 			aclent_perms(tp->a_perm, perm);
1222*789Sahrens 			(void) printf("group:%s:%s\t\t",
1223*789Sahrens 			    prgname(tp->a_id), perm);
1224*789Sahrens 			aclent_perms(tp->a_perm & mask, perm);
1225*789Sahrens 			(void) printf("#effective:%s\n", perm);
1226*789Sahrens 			break;
1227*789Sahrens 		case GROUP_OBJ:
1228*789Sahrens 			aclent_perms(tp->a_perm, perm);
1229*789Sahrens 			(void) printf("group::%s\t\t", perm);
1230*789Sahrens 			aclent_perms(tp->a_perm & mask, perm);
1231*789Sahrens 			(void) printf("#effective:%s\n", perm);
1232*789Sahrens 			break;
1233*789Sahrens 		case CLASS_OBJ:
1234*789Sahrens 			aclent_perms(tp->a_perm, perm);
1235*789Sahrens 			(void) printf("mask:%s\n", perm);
1236*789Sahrens 			break;
1237*789Sahrens 		case OTHER_OBJ:
1238*789Sahrens 			aclent_perms(tp->a_perm, perm);
1239*789Sahrens 			(void) printf("other:%s\n", perm);
1240*789Sahrens 			break;
1241*789Sahrens 		case DEF_USER:
1242*789Sahrens 			aclent_perms(tp->a_perm, perm);
1243*789Sahrens 			(void) printf("default:user:%s:%s\n",
1244*789Sahrens 			    pruname(tp->a_id), perm);
1245*789Sahrens 			break;
1246*789Sahrens 		case DEF_USER_OBJ:
1247*789Sahrens 			aclent_perms(tp->a_perm, perm);
1248*789Sahrens 			(void) printf("default:user::%s\n", perm);
1249*789Sahrens 			break;
1250*789Sahrens 		case DEF_GROUP:
1251*789Sahrens 			aclent_perms(tp->a_perm, perm);
1252*789Sahrens 			(void) printf("default:group:%s:%s\n",
1253*789Sahrens 			    prgname(tp->a_id), perm);
1254*789Sahrens 			break;
1255*789Sahrens 		case DEF_GROUP_OBJ:
1256*789Sahrens 			aclent_perms(tp->a_perm, perm);
1257*789Sahrens 			(void) printf("default:group::%s\n", perm);
1258*789Sahrens 			break;
1259*789Sahrens 		case DEF_CLASS_OBJ:
1260*789Sahrens 			aclent_perms(tp->a_perm, perm);
1261*789Sahrens 			(void) printf("default:mask:%s\n", perm);
1262*789Sahrens 			break;
1263*789Sahrens 		case DEF_OTHER_OBJ:
1264*789Sahrens 			aclent_perms(tp->a_perm, perm);
1265*789Sahrens 			(void) printf("default:other:%s\n", perm);
1266*789Sahrens 			break;
1267*789Sahrens 		default:
1268*789Sahrens 			(void) fprintf(stderr,
1269*789Sahrens 			    gettext("unrecognized entry\n"));
1270*789Sahrens 			break;
1271*789Sahrens 		}
1272*789Sahrens 	}
1273*789Sahrens }
1274*789Sahrens 
1275*789Sahrens static void
1276*789Sahrens split_line(char *str, int cols)
1277*789Sahrens {
1278*789Sahrens 	char *ptr;
1279*789Sahrens 	int len;
1280*789Sahrens 	int i;
1281*789Sahrens 	int last_split;
1282*789Sahrens 	char pad[11];
1283*789Sahrens 	int pad_len;
1284*789Sahrens 
1285*789Sahrens 	len = strlen(str);
1286*789Sahrens 	ptr = str;
1287*789Sahrens 	(void) strcpy(pad, "");
1288*789Sahrens 	pad_len = 0;
1289*789Sahrens 
1290*789Sahrens 	ptr = str;
1291*789Sahrens 	last_split = 0;
1292*789Sahrens 	for (i = 0; i != len; i++) {
1293*789Sahrens 		if ((i + pad_len + 4) >= cols) {
1294*789Sahrens 			(void) printf("%s%.*s\n", pad, last_split, ptr);
1295*789Sahrens 			ptr = &ptr[last_split];
1296*789Sahrens 			len = strlen(ptr);
1297*789Sahrens 			i = 0;
1298*789Sahrens 			pad_len = 4;
1299*789Sahrens 			(void) strcpy(pad, "         ");
1300*789Sahrens 		} else {
1301*789Sahrens 			if (ptr[i] == '/' || ptr[i] == ':') {
1302*789Sahrens 				last_split = i;
1303*789Sahrens 			}
1304*789Sahrens 		}
1305*789Sahrens 	}
1306*789Sahrens 	if (i == len) {
1307*789Sahrens 		(void) printf("%s%s\n", pad, ptr);
1308*789Sahrens 	}
1309*789Sahrens }
1310*789Sahrens 
1311*789Sahrens static void
1312*789Sahrens ace_printacl(acl_t *aclp, int cols)
1313*789Sahrens {
1314*789Sahrens 	int  slot = 0;
1315*789Sahrens 	char *token;
1316*789Sahrens 	char *acltext;
1317*789Sahrens 
1318*789Sahrens 	acltext = acl_totext(aclp);
1319*789Sahrens 
1320*789Sahrens 	if (acltext == NULL)
1321*789Sahrens 		return;
1322*789Sahrens 
1323*789Sahrens 	token = strtok(acltext, ",");
1324*789Sahrens 	if (token == NULL) {
1325*789Sahrens 		free(acltext);
1326*789Sahrens 		return;
1327*789Sahrens 	}
1328*789Sahrens 
1329*789Sahrens 	do {
1330*789Sahrens 		(void) printf("     %d:", slot++);
1331*789Sahrens 		split_line(token, cols - 5);
1332*789Sahrens 	} while (token = strtok(NULL, ","));
1333*789Sahrens 	free(acltext);
1334*789Sahrens }
1335*789Sahrens 
1336*789Sahrens /*
1337*789Sahrens  * pretty print an ACL.
1338*789Sahrens  * For aclent_t ACL's the format is
1339*789Sahrens  * similar to the old format used by getfacl,
1340*789Sahrens  * with the addition of adding a "slot" number
1341*789Sahrens  * before each entry.
1342*789Sahrens  *
1343*789Sahrens  * for ace_t ACL's the cols variable will break up
1344*789Sahrens  * the long lines into multiple lines and will also
1345*789Sahrens  * print a "slot" number.
1346*789Sahrens  */
1347*789Sahrens void
1348*789Sahrens acl_printacl(acl_t *aclp, int cols)
1349*789Sahrens {
1350*789Sahrens 
1351*789Sahrens 	switch (aclp->acl_type) {
1352*789Sahrens 	case ACLENT_T:
1353*789Sahrens 		aclent_printacl(aclp);
1354*789Sahrens 		break;
1355*789Sahrens 	case ACE_T:
1356*789Sahrens 		ace_printacl(aclp, cols);
1357*789Sahrens 		break;
1358*789Sahrens 	}
1359*789Sahrens }
1360*789Sahrens 
1361*789Sahrens 
1362*789Sahrens /*
1363*789Sahrens  * return text for an ACL error.
1364*789Sahrens  */
1365*789Sahrens char *
1366*789Sahrens acl_strerror(int errnum)
1367*789Sahrens {
1368*789Sahrens 	switch (errnum) {
1369*789Sahrens 	case EACL_GRP_ERROR:
1370*789Sahrens 		return (dgettext(TEXT_DOMAIN,
1371*789Sahrens 		    "There is more than one user group owner entry"));
1372*789Sahrens 	case EACL_USER_ERROR:
1373*789Sahrens 		return (dgettext(TEXT_DOMAIN,
1374*789Sahrens 		    "There is more than one user owner entry"));
1375*789Sahrens 	case EACL_OTHER_ERROR:
1376*789Sahrens 		return (dgettext(TEXT_DOMAIN,
1377*789Sahrens 		    "There is more than one other entry"));
1378*789Sahrens 	case EACL_CLASS_ERROR:
1379*789Sahrens 		return (dgettext(TEXT_DOMAIN,
1380*789Sahrens 		    "There is more than one mask entry"));
1381*789Sahrens 	case EACL_DUPLICATE_ERROR:
1382*789Sahrens 		return (dgettext(TEXT_DOMAIN,
1383*789Sahrens 		    "Duplicate user or group entries"));
1384*789Sahrens 	case EACL_MISS_ERROR:
1385*789Sahrens 		return (dgettext(TEXT_DOMAIN,
1386*789Sahrens 		    "Missing user/group owner, other, mask entry"));
1387*789Sahrens 	case EACL_MEM_ERROR:
1388*789Sahrens 		return (dgettext(TEXT_DOMAIN,
1389*789Sahrens 		    "Memory error"));
1390*789Sahrens 	case EACL_ENTRY_ERROR:
1391*789Sahrens 		return (dgettext(TEXT_DOMAIN,
1392*789Sahrens 		    "Unrecognized entry type"));
1393*789Sahrens 	case EACL_INHERIT_ERROR:
1394*789Sahrens 		return (dgettext(TEXT_DOMAIN,
1395*789Sahrens 		    "Invalid inheritance flags"));
1396*789Sahrens 	case EACL_FLAGS_ERROR:
1397*789Sahrens 		return (dgettext(TEXT_DOMAIN,
1398*789Sahrens 		    "Unrecognized entry flags"));
1399*789Sahrens 	case EACL_PERM_MASK_ERROR:
1400*789Sahrens 		return (dgettext(TEXT_DOMAIN,
1401*789Sahrens 		    "Invalid ACL permissions"));
1402*789Sahrens 	case EACL_COUNT_ERROR:
1403*789Sahrens 		return (dgettext(TEXT_DOMAIN,
1404*789Sahrens 		    "Invalid ACL count"));
1405*789Sahrens 	case EACL_INVALID_SLOT:
1406*789Sahrens 		return (dgettext(TEXT_DOMAIN,
1407*789Sahrens 		    "Invalid ACL entry number specified"));
1408*789Sahrens 	case EACL_NO_ACL_ENTRY:
1409*789Sahrens 		return (dgettext(TEXT_DOMAIN,
1410*789Sahrens 		    "ACL entry doesn't exist"));
1411*789Sahrens 	case EACL_DIFF_TYPE:
1412*789Sahrens 		return (dgettext(TEXT_DOMAIN,
1413*789Sahrens 		    "ACL type's are different"));
1414*789Sahrens 	case EACL_INVALID_USER_GROUP:
1415*789Sahrens 		return (dgettext(TEXT_DOMAIN, "Invalid user or group"));
1416*789Sahrens 	case EACL_INVALID_STR:
1417*789Sahrens 		return (dgettext(TEXT_DOMAIN, "ACL string is invalid"));
1418*789Sahrens 	case EACL_FIELD_NOT_BLANK:
1419*789Sahrens 		return (dgettext(TEXT_DOMAIN, "Field expected to be blank"));
1420*789Sahrens 	case EACL_INVALID_ACCESS_TYPE:
1421*789Sahrens 		return (dgettext(TEXT_DOMAIN, "Invalid access type"));
1422*789Sahrens 	case EACL_UNKNOWN_DATA:
1423*789Sahrens 		return (dgettext(TEXT_DOMAIN, "Unrecognized entry"));
1424*789Sahrens 	case EACL_MISSING_FIELDS:
1425*789Sahrens 		return (dgettext(TEXT_DOMAIN,
1426*789Sahrens 		    "ACL specification missing required fields"));
1427*789Sahrens 	case EACL_INHERIT_NOTDIR:
1428*789Sahrens 		return (dgettext(TEXT_DOMAIN,
1429*789Sahrens 		    "Inheritance flags are only allowed on directories"));
1430*789Sahrens 	case -1:
1431*789Sahrens 		return (strerror(errno));
1432*789Sahrens 	default:
1433*789Sahrens 		errno = EINVAL;
1434*789Sahrens 		return (dgettext(TEXT_DOMAIN, "Unknown error"));
1435*789Sahrens 	}
1436*789Sahrens }
1437