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