2 * Copyright (c) 1989, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 #include <sys/types.h>
43 #include <membership.h>
44 #include "chmod_acl.h"
46 extern void usage(void);
53 #define ACL_PERM_DIR (1<<0)
54 #define ACL_PERM_FILE (1<<1)
56 {ACL_READ_DATA
, "read", ACL_PERM_FILE
},
57 {ACL_LIST_DIRECTORY
, "list", ACL_PERM_DIR
},
58 {ACL_WRITE_DATA
, "write", ACL_PERM_FILE
},
59 {ACL_ADD_FILE
, "add_file", ACL_PERM_DIR
},
60 {ACL_EXECUTE
, "execute", ACL_PERM_FILE
},
61 {ACL_SEARCH
, "search", ACL_PERM_DIR
},
62 {ACL_DELETE
, "delete", ACL_PERM_FILE
| ACL_PERM_DIR
},
63 {ACL_APPEND_DATA
, "append", ACL_PERM_FILE
},
64 {ACL_ADD_SUBDIRECTORY
, "add_subdirectory", ACL_PERM_DIR
},
65 {ACL_DELETE_CHILD
, "delete_child", ACL_PERM_DIR
},
66 {ACL_READ_ATTRIBUTES
, "readattr", ACL_PERM_FILE
| ACL_PERM_DIR
},
67 {ACL_WRITE_ATTRIBUTES
, "writeattr", ACL_PERM_FILE
| ACL_PERM_DIR
},
68 {ACL_READ_EXTATTRIBUTES
, "readextattr", ACL_PERM_FILE
| ACL_PERM_DIR
},
69 {ACL_WRITE_EXTATTRIBUTES
, "writeextattr", ACL_PERM_FILE
| ACL_PERM_DIR
},
70 {ACL_READ_SECURITY
, "readsecurity", ACL_PERM_FILE
| ACL_PERM_DIR
},
71 {ACL_WRITE_SECURITY
, "writesecurity", ACL_PERM_FILE
| ACL_PERM_DIR
},
72 {ACL_CHANGE_OWNER
, "chown", ACL_PERM_FILE
| ACL_PERM_DIR
},
81 {ACL_ENTRY_INHERITED
, "inherited", ACL_PERM_FILE
| ACL_PERM_DIR
},
82 {ACL_ENTRY_FILE_INHERIT
, "file_inherit", ACL_PERM_DIR
},
83 {ACL_ENTRY_DIRECTORY_INHERIT
, "directory_inherit", ACL_PERM_DIR
},
84 {ACL_ENTRY_LIMIT_INHERIT
, "limit_inherit", ACL_PERM_FILE
| ACL_PERM_DIR
},
85 {ACL_ENTRY_ONLY_INHERIT
, "only_inherit", ACL_PERM_DIR
},
89 /* TBD - Many of these routines could potentially be considered for
90 * inclusion in a library. If that is done, either avoid use of "err"
91 * and implement a better fall-through strategy in case of errors,
92 * or use err_set_exit() and make various structures globals.
96 #define NAME_GROUP (2)
97 #define NAME_EITHER (NAME_USER | NAME_GROUP)
99 /* Perform a name to uuid mapping - calls through to memberd */
102 name_to_uuid(char *tok
, int nametype
) {
103 struct passwd
*tpass
= NULL
;
104 struct group
*tgrp
= NULL
;
105 uuid_t
*entryg
= NULL
;
107 if ((entryg
= (uuid_t
*) calloc(1,sizeof(uuid_t
))) == NULL
)
108 err(1, "Unable to allocate a uuid");
110 if (nametype
& NAME_USER
)
111 tpass
= getpwnam(tok
);
113 if (NULL
== tpass
&& (nametype
& NAME_GROUP
))
114 tgrp
= getgrnam(tok
);
117 if (0 != mbr_uid_to_uuid(tpass
->pw_uid
, *entryg
)) {
118 errx(1, "mbr_uid_to_uuid(): Unable to translate uid %d", tpass
->pw_uid
);
121 if (0 != mbr_gid_to_uuid(tgrp
->gr_gid
, *entryg
)) {
122 errx(1, "mbr_gid_to_uuid(): Unable to translate gid %d", tgrp
->gr_gid
);
125 errx(1, "Unable to translate '%s' to a UID/GID", tok
);
130 /* Convert an acl entry in string form to an acl_entry_t */
132 parse_entry(char *entrybuf
, acl_entry_t newent
) {
140 unsigned permcount
= 0;
142 char *delimiter
= " ";
143 int nametype
= NAME_EITHER
;
145 acl_get_permset(newent
, &perms
);
146 acl_get_flagset_np(newent
, &flags
);
150 if (0 == strncmp(entrybuf
, "user:", 5)) {
151 nametype
= NAME_USER
;
153 } else if (0 == strncmp(entrybuf
, "group:", 6)) {
154 nametype
= NAME_GROUP
;
158 if (strchr(pebuf
, ':')) /* User/Group names can have spaces */
160 tok
= strsep(&pebuf
, delimiter
);
162 if ((tok
== NULL
) || *tok
== '\0') {
163 errx(1, "Invalid entry format -- expected user or group name");
166 /* parse the name into a qualifier */
167 entryg
= name_to_uuid(tok
, nametype
);
169 tok
= strsep(&pebuf
, ": "); /* Stick with delimiter? */
170 if ((tok
== NULL
) || *tok
== '\0') {
171 errx(1, "Invalid entry format -- expected allow or deny");
174 /* is the verb 'allow' or 'deny'? */
175 if (!strcmp(tok
, "allow")) {
176 tag
= ACL_EXTENDED_ALLOW
;
177 } else if (!strcmp(tok
, "deny")) {
178 tag
= ACL_EXTENDED_DENY
;
180 errx(1, "Unknown tag type '%s'", tok
);
183 /* parse permissions */
184 for (; (tok
= strsep(&pebuf
, ",")) != NULL
;) {
186 /* is it a permission? */
187 for (pindex
= 0; acl_perms
[pindex
].name
!= NULL
; pindex
++) {
188 if (!strcmp(acl_perms
[pindex
].name
, tok
)) {
190 acl_add_perm(perms
, acl_perms
[pindex
].perm
);
196 for (pindex
= 0; acl_flags
[pindex
].name
!= NULL
; pindex
++) {
197 if (!strcmp(acl_flags
[pindex
].name
, tok
)) {
199 acl_add_flag_np(flags
, acl_flags
[pindex
].flag
);
204 errx(1,"Invalid permission type '%s'", tok
);
209 if (0 == permcount
) {
210 errx(1, "No permissions specified");
212 acl_set_tag_type(newent
, tag
);
213 acl_set_qualifier(newent
, entryg
);
214 acl_set_permset(newent
, perms
);
215 acl_set_flagset_np(newent
, flags
);
221 /* Convert one or more acl entries in string form to an acl_t */
223 parse_acl_entries(const char *input
) {
229 char **bufp
, *entryv
[ACL_MAX_ENTRIES
];
231 /* XXX acl_from_text(), when implemented, will presumably use the canonical
232 * text representation format, which is what chmod should be using
233 * We may need to add an entry number to the input
235 /* Translate the user supplied ACL entry */
236 /* acl_input = acl_from_text(input); */
238 inbuf
= malloc(MAX_ACL_TEXT_SIZE
);
241 err(1, "malloc() failed");
242 strncpy(inbuf
, input
, MAX_ACL_TEXT_SIZE
);
243 inbuf
[MAX_ACL_TEXT_SIZE
- 1] = '\0';
245 if ((acl_input
= acl_init(1)) == NULL
)
246 err(1, "acl_init() failed");
250 for (bufp
= entryv
; (*bufp
= strsep(&oinbuf
, "\n")) != NULL
;)
251 if (**bufp
!= '\0') {
252 if (0 != acl_create_entry(&acl_input
, &newent
))
253 err(1, "acl_create_entry() failed");
254 if (0 != parse_entry(*bufp
, newent
)) {
255 errx(1, "Failed parsing entry '%s'", *bufp
);
257 if (++bufp
>= &entryv
[ACL_MAX_ENTRIES
- 1]) {
258 errx(1, "Too many entries");
267 /* XXX No Libc support for inherited entries and generation determination yet */
269 get_inheritance_level(acl_entry_t entry
) {
270 /* XXX to be implemented */
274 /* Determine a "score" for an acl entry. The entry scores higher if it's
275 * tagged ACL_EXTENDED_DENY, and non-inherited entries are ranked higher
276 * than inherited entries.
280 score_acl_entry(acl_entry_t entry
) {
289 return (MINIMUM_TIER
);
291 if (acl_get_tag_type(entry
, &tag
) != 0) {
292 err(1, "Malformed ACL entry, no tag present");
294 if (acl_get_flagset_np(entry
, &flags
) != 0){
295 err(1, "Unable to obtain flagset");
297 if (acl_get_permset(entry
, &perms
) != 0)
298 err(1, "Malformed ACL entry, no permset present");
301 case ACL_EXTENDED_ALLOW
:
303 case ACL_EXTENDED_DENY
:
307 errx(1, "Unknown tag type %d present in ACL entry", tag
);
311 if (acl_get_flag_np(flags
, ACL_ENTRY_INHERITED
))
312 score
+= get_inheritance_level(entry
) * INHERITANCE_TIER
;
318 compare_acl_qualifiers(uuid_t
*qa
, uuid_t
*qb
) {
319 return bcmp(qa
, qb
, sizeof(uuid_t
));
322 /* Compare two ACL permsets.
324 * MATCH_SUBSET if bperms is a subset of aperms
325 * MATCH_SUPERSET if bperms is a superset of aperms
326 * MATCH_PARTIAL if the two permsets have a common subset
327 * MATCH_EXACT if the two permsets are identical
328 * MATCH_NONE if they are disjoint
332 compare_acl_permsets(acl_permset_t aperms
, acl_permset_t bperms
)
335 /* TBD Implement other match levels as needed */
336 for (i
= 0; acl_perms
[i
].name
!= NULL
; i
++) {
337 if (acl_get_perm_np(aperms
, acl_perms
[i
].perm
) !=
338 acl_get_perm_np(bperms
, acl_perms
[i
].perm
))
345 compare_acl_flagsets(acl_flagset_t aflags
, acl_flagset_t bflags
)
348 /* TBD Implement other match levels as needed */
349 for (i
= 0; acl_flags
[i
].name
!= NULL
; i
++) {
350 if (acl_get_flag_np(aflags
, acl_flags
[i
].flag
) !=
351 acl_get_flag_np(bflags
, acl_flags
[i
].flag
))
357 /* Compares two ACL entries for equality */
359 compare_acl_entries(acl_entry_t a
, acl_entry_t b
)
361 acl_tag_t atag
, btag
;
362 acl_permset_t aperms
, bperms
;
363 acl_flagset_t aflags
, bflags
;
364 int pcmp
= 0, fcmp
= 0;
367 aqual
= acl_get_qualifier(a
);
368 bqual
= acl_get_qualifier(b
);
370 int compare
= compare_acl_qualifiers(aqual
, bqual
);
377 if (0 != acl_get_tag_type(a
, &atag
))
378 err(1, "No tag type present in entry");
379 if (0!= acl_get_tag_type(b
, &btag
))
380 err(1, "No tag type present in entry");
385 if ((acl_get_permset(a
, &aperms
) != 0) ||
386 (acl_get_flagset_np(a
, &aflags
) != 0) ||
387 (acl_get_permset(b
, &bperms
) != 0) ||
388 (acl_get_flagset_np(b
, &bflags
) != 0))
389 err(1, "error fetching permissions");
391 pcmp
= compare_acl_permsets(aperms
, bperms
);
392 fcmp
= compare_acl_flagsets(aflags
, bflags
);
394 if ((pcmp
== MATCH_NONE
) || (fcmp
== MATCH_NONE
))
395 return(MATCH_PARTIAL
);
400 /* Verify that an ACL is in canonical order. Currently, the canonical
404 * inherited deny (parent)
405 * inherited allow (parent)
406 * inherited deny (grandparent)
407 * inherited allow (grandparent)
411 is_canonical(acl_t acl
) {
415 int score
= 0, next_score
= 0;
417 /* XXX - is a zero entry ACL in canonical form? */
418 if (0 != acl_get_entry(acl
, ACL_FIRST_ENTRY
, &entry
))
421 score
= score_acl_entry(entry
);
423 for (aindex
= 0; acl_get_entry(acl
, ACL_NEXT_ENTRY
, &entry
) == 0;
425 if (score
< (next_score
= score_acl_entry(entry
)))
433 /* Iterate through an ACL, and find the canonical position for the
437 find_canonical_position(acl_t acl
, acl_entry_t modifier
) {
443 /* Check if there's an entry with the same qualifier
444 * and tag type; if not, find the appropriate slot
448 if (0 != acl_get_entry(acl
, ACL_FIRST_ENTRY
, &entry
))
451 mscore
= score_acl_entry(modifier
);
453 while (mscore
< score_acl_entry(entry
)) {
457 if (0 != acl_get_entry(acl
, ACL_NEXT_ENTRY
, &entry
))
464 int canonicalize_acl_entries(acl_t acl
);
466 /* For a given acl_entry_t "modifier", find the first exact or
467 * partially matching entry from the specified acl_t acl
471 find_matching_entry (acl_t acl
, acl_entry_t modifier
, acl_entry_t
*rentryp
,
472 unsigned match_inherited
) {
474 acl_entry_t entry
= NULL
;
477 int cmp
, fcmp
= MATCH_NONE
;
480 acl_get_entry(acl
, entry
== NULL
? ACL_FIRST_ENTRY
:
481 ACL_NEXT_ENTRY
, &entry
) == 0;
483 cmp
= compare_acl_entries(entry
, modifier
);
484 if ((cmp
== MATCH_EXACT
) || (cmp
== MATCH_PARTIAL
)) {
485 if (match_inherited
) {
486 acl_flagset_t eflags
, mflags
;
488 if (0 != acl_get_flagset_np(modifier
, &mflags
))
489 err(1, "Unable to get flagset");
491 if (0 != acl_get_flagset_np(entry
, &eflags
))
492 err(1, "Unable to get flagset");
494 if (compare_acl_flagsets(mflags
, eflags
) == MATCH_EXACT
) {
504 if (fcmp
== MATCH_EXACT
)
510 /* Remove all perms specified in modifier from rentry*/
512 subtract_from_entry(acl_entry_t rentry
, acl_entry_t modifier
, int* valid_perms
)
514 acl_permset_t rperms
, mperms
;
515 acl_flagset_t rflags
, mflags
;
520 if ((acl_get_permset(rentry
, &rperms
) != 0) ||
521 (acl_get_flagset_np(rentry
, &rflags
) != 0) ||
522 (acl_get_permset(modifier
, &mperms
) != 0) ||
523 (acl_get_flagset_np(modifier
, &mflags
) != 0))
524 err(1, "error computing ACL modification");
526 for (i
= 0; acl_perms
[i
].name
!= NULL
; i
++) {
527 if (acl_get_perm_np(mperms
, acl_perms
[i
].perm
))
528 acl_delete_perm(rperms
, acl_perms
[i
].perm
);
529 else if (valid_perms
&& acl_get_perm_np(rperms
, acl_perms
[i
].perm
))
532 for (i
= 0; acl_flags
[i
].name
!= NULL
; i
++) {
533 if (acl_get_flag_np(mflags
, acl_flags
[i
].flag
))
534 acl_delete_flag_np(rflags
, acl_flags
[i
].flag
);
536 acl_set_permset(rentry
, rperms
);
537 acl_set_flagset_np(rentry
, rflags
);
540 /* Add the perms specified in modifier to rentry */
542 merge_entry_perms(acl_entry_t rentry
, acl_entry_t modifier
)
544 acl_permset_t rperms
, mperms
;
545 acl_flagset_t rflags
, mflags
;
548 if ((acl_get_permset(rentry
, &rperms
) != 0) ||
549 (acl_get_flagset_np(rentry
, &rflags
) != 0) ||
550 (acl_get_permset(modifier
, &mperms
) != 0) ||
551 (acl_get_flagset_np(modifier
, &mflags
) != 0))
552 err(1, "error computing ACL modification");
554 for (i
= 0; acl_perms
[i
].name
!= NULL
; i
++) {
555 if (acl_get_perm_np(mperms
, acl_perms
[i
].perm
))
556 acl_add_perm(rperms
, acl_perms
[i
].perm
);
558 for (i
= 0; acl_flags
[i
].name
!= NULL
; i
++) {
559 if (acl_get_flag_np(mflags
, acl_flags
[i
].flag
))
560 acl_add_flag_np(rflags
, acl_flags
[i
].flag
);
562 acl_set_permset(rentry
, rperms
);
563 acl_set_flagset_np(rentry
, rflags
);
568 modify_acl(acl_t
*oaclp
, acl_entry_t modifier
, unsigned int optflags
,
569 int position
, int inheritance_level
,
570 unsigned flag_new_acl
, const char* path
) {
573 acl_entry_t newent
= NULL
;
575 acl_entry_t rentry
= NULL
;
579 /* Add the inherited flag if requested by the user*/
580 if (modifier
&& (optflags
& ACL_INHERIT_FLAG
)) {
581 acl_flagset_t mflags
;
583 acl_get_flagset_np(modifier
, &mflags
);
584 acl_add_flag_np(mflags
, ACL_ENTRY_INHERITED
);
585 acl_set_flagset_np(modifier
, mflags
);
588 if (optflags
& ACL_SET_FLAG
) {
589 if (position
!= -1) {
590 if (0 != acl_create_entry_np(&oacl
, &newent
, position
))
591 err(1, "acl_create_entry() failed");
592 acl_copy_entry(newent
, modifier
);
594 /* If an entry exists, add the new permissions to it, else add an
595 * entry in the canonical position.
598 /* First, check for a matching entry - if one exists, merge flags */
599 dmatch
= find_matching_entry(oacl
, modifier
, &rentry
, 1);
601 if (dmatch
!= MATCH_NONE
) {
602 if (dmatch
== MATCH_EXACT
)
603 /* Nothing to be done */
606 if (dmatch
== MATCH_PARTIAL
) {
607 merge_entry_perms(rentry
, modifier
);
611 /* Insert the entry in canonical order */
612 cpos
= find_canonical_position(oacl
, modifier
);
613 if (0!= acl_create_entry_np(&oacl
, &newent
, cpos
))
614 err(1, "acl_create_entry() failed");
615 acl_copy_entry(newent
, modifier
);
617 } else if (optflags
& ACL_DELETE_FLAG
) {
619 warnx("No ACL present '%s'", path
);
621 } else if (position
!= -1 ) {
622 if (0 != acl_get_entry(oacl
, position
, &rentry
)) {
623 warnx("Invalid entry number '%s'", path
);
626 acl_delete_entry(oacl
, rentry
);
629 unsigned match_found
= 0, aindex
;
631 acl_get_entry(oacl
, rentry
== NULL
?
633 ACL_NEXT_ENTRY
, &rentry
) == 0;
636 cmp
= compare_acl_entries(rentry
, modifier
);
637 if ((cmp
== MATCH_EXACT
) ||
638 (cmp
== MATCH_PARTIAL
)) {
640 if (cmp
== MATCH_EXACT
)
641 acl_delete_entry(oacl
, rentry
);
644 /* In the event of a partial match, remove the specified perms from the
646 subtract_from_entry(rentry
, modifier
, &valid_perms
);
647 /* if no perms survived then delete the entry */
648 if (valid_perms
== 0)
649 acl_delete_entry(oacl
, rentry
);
653 if (0 == match_found
) {
654 warnx("Entry not found when attempting delete '%s'",path
);
658 } else if (optflags
& ACL_REWRITE_FLAG
) {
661 if (-1 == position
) {
664 if (0 == flag_new_acl
) {
665 if (0 != acl_get_entry(oacl
, position
,
667 err(1, "Invalid entry number '%s'", path
);
669 if (0 != acl_delete_entry(oacl
, rentry
))
670 err(1, "Unable to delete entry '%s'", path
);
672 if (0!= acl_create_entry_np(&oacl
, &newent
, position
))
673 err(1, "acl_create_entry() failed");
674 acl_copy_entry(newent
, modifier
);
682 modify_file_acl(unsigned int optflags
, const char *path
, acl_t modifier
, int position
, int inheritance_level
) {
685 unsigned aindex
= 0, flag_new_acl
= 0;
686 acl_entry_t newent
= NULL
;
687 acl_entry_t entry
= NULL
;
688 unsigned retval
= 0 ;
692 /* XXX acl_get_file() returns a zero entry ACL if an ACL was previously
693 * associated with the file, and has had its entries removed.
694 * However, POSIX 1003.1e states that a zero entry ACL should be
695 * returned if the caller asks for ACL_TYPE_DEFAULT, and no ACL is
696 * associated with the path; it
697 * does not specifically state that a request for ACL_TYPE_EXTENDED
698 * should not return a zero entry ACL, however.
701 /* Determine if we've been given a zero entry ACL, or create an ACL if
702 * none exists. There are some issues to consider here: Should we create
703 * a zero-entry ACL for a delete or check canonicity operation?
709 if (optflags
& ACL_CLEAR_FLAG
) {
710 filesec_t fsec
= filesec_init();
712 err(1, "filesec_init() failed");
713 if (filesec_set_property(fsec
, FILESEC_ACL
,
714 _FILESEC_REMOVE_ACL
) != 0)
715 err(1, "filesec_set_property() failed");
716 if (chmodx_np(path
, fsec
) != 0) {
718 warn("Failed to clear ACL on file %s", path
);
726 if (optflags
& ACL_FROM_STDIN
)
727 oacl
= acl_dup(modifier
);
729 oacl
= acl_get_file(path
, ACL_TYPE_EXTENDED
);
731 if ((oacl
== NULL
) ||
732 (acl_get_entry(oacl
,ACL_FIRST_ENTRY
, &newent
) != 0)) {
733 if ((oacl
= acl_init(1)) == NULL
)
734 err(1, "acl_init() failed");
739 if ((0 == flag_new_acl
) && (optflags
& (ACL_REMOVE_INHERIT_FLAG
|
740 ACL_REMOVE_INHERITED_ENTRIES
))) {
742 if ((facl
= acl_init(1)) == NULL
)
743 err(1, "acl_init() failed");
746 (entry
== NULL
? ACL_FIRST_ENTRY
:
747 ACL_NEXT_ENTRY
), &entry
) == 0;
749 acl_flagset_t eflags
;
750 acl_entry_t fent
= NULL
;
751 if (acl_get_flagset_np(entry
, &eflags
) != 0) {
752 err(1, "Unable to obtain flagset");
755 if (acl_get_flag_np(eflags
, ACL_ENTRY_INHERITED
)) {
756 if (optflags
& ACL_REMOVE_INHERIT_FLAG
) {
757 acl_delete_flag_np(eflags
, ACL_ENTRY_INHERITED
);
758 acl_set_flagset_np(entry
, eflags
);
759 acl_create_entry(&facl
, &fent
);
760 acl_copy_entry(fent
, entry
);
764 acl_create_entry(&facl
, &fent
);
765 acl_copy_entry(fent
, entry
);
771 } else if (optflags
& ACL_TO_STDOUT
) {
772 ssize_t len
; /* need to get printacl() from ls(1) */
773 char *text
= acl_to_text(oacl
, &len
);
776 } else if (optflags
& ACL_CHECK_CANONICITY
) {
778 warnx("No ACL currently associated with file '%s'", path
);
780 retval
= is_canonical(oacl
);
781 } else if ((optflags
& ACL_SET_FLAG
) && (position
== -1) &&
782 (!is_canonical(oacl
))) {
783 warnx("The specified file '%s' does not have an ACL in canonical order, please specify a position with +a# ", path
);
785 } else if (((optflags
& ACL_DELETE_FLAG
) && (position
!= -1))
786 || (optflags
& ACL_CHECK_CANONICITY
)) {
787 retval
= modify_acl(&oacl
, NULL
, optflags
, position
,
788 inheritance_level
, flag_new_acl
, path
);
789 } else if ((optflags
& (ACL_REMOVE_INHERIT_FLAG
|ACL_REMOVE_INHERITED_ENTRIES
)) && flag_new_acl
) {
790 warnx("No ACL currently associated with file '%s'", path
);
793 if (!modifier
) { /* avoid bus error in acl_get_entry */
794 errx(1, "Internal error: modifier should not be NULL");
797 acl_get_entry(modifier
,
798 (entry
== NULL
? ACL_FIRST_ENTRY
:
799 ACL_NEXT_ENTRY
), &entry
) == 0;
802 retval
+= modify_acl(&oacl
, entry
, optflags
,
803 position
, inheritance_level
,
809 /* XXX Potential race here, since someone else could've modified or
810 * read the ACL on this file (with the intention of modifying it) in
811 * the interval from acl_get_file() to acl_set_file(); we can
812 * minimize one aspect of this window by comparing the original acl
813 * to a fresh one from acl_get_file() but we could consider a
814 * "changeset" mechanism, common locking strategy, or kernel
815 * supplied reservation mechanism to prevent this race.
817 if (!(optflags
& (ACL_TO_STDOUT
|ACL_CHECK_CANONICITY
)) &&
818 (0 != acl_set_file(path
, ACL_TYPE_EXTENDED
, oacl
))){
820 warn("Failed to set ACL on file '%s'", path
);