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