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 uuid_t
*entryg
= NULL
;
104 size_t len
= strlen(tok
);
106 if ((entryg
= (uuid_t
*) calloc(1, sizeof(uuid_t
))) == NULL
) {
107 errx(1, "Unable to allocate a uuid");
110 if ((nametype
& NAME_USER
) && mbr_identifier_to_uuid(ID_TYPE_USERNAME
, tok
, len
, *entryg
) == 0) {
114 if ((nametype
& NAME_GROUP
) && mbr_identifier_to_uuid(ID_TYPE_GROUPNAME
, tok
, len
, *entryg
) == 0) {
118 errx(1, "Unable to translate '%s' to a UUID", tok
);
121 /* Convert an acl entry in string form to an acl_entry_t */
123 parse_entry(char *entrybuf
, acl_entry_t newent
) {
131 unsigned permcount
= 0;
133 char *delimiter
= " ";
134 int nametype
= NAME_EITHER
;
136 acl_get_permset(newent
, &perms
);
137 acl_get_flagset_np(newent
, &flags
);
141 if (0 == strncmp(entrybuf
, "user:", 5)) {
142 nametype
= NAME_USER
;
144 } else if (0 == strncmp(entrybuf
, "group:", 6)) {
145 nametype
= NAME_GROUP
;
149 if (strchr(pebuf
, ':')) /* User/Group names can have spaces */
151 tok
= strsep(&pebuf
, delimiter
);
153 if ((tok
== NULL
) || *tok
== '\0') {
154 errx(1, "Invalid entry format -- expected user or group name");
157 /* parse the name into a qualifier */
158 entryg
= name_to_uuid(tok
, nametype
);
160 tok
= strsep(&pebuf
, ": "); /* Stick with delimiter? */
161 if ((tok
== NULL
) || *tok
== '\0') {
162 errx(1, "Invalid entry format -- expected allow or deny");
165 /* is the verb 'allow' or 'deny'? */
166 if (!strcmp(tok
, "allow")) {
167 tag
= ACL_EXTENDED_ALLOW
;
168 } else if (!strcmp(tok
, "deny")) {
169 tag
= ACL_EXTENDED_DENY
;
171 errx(1, "Unknown tag type '%s'", tok
);
174 /* parse permissions */
175 for (; (tok
= strsep(&pebuf
, ",")) != NULL
;) {
177 /* is it a permission? */
178 for (pindex
= 0; acl_perms
[pindex
].name
!= NULL
; pindex
++) {
179 if (!strcmp(acl_perms
[pindex
].name
, tok
)) {
181 acl_add_perm(perms
, acl_perms
[pindex
].perm
);
187 for (pindex
= 0; acl_flags
[pindex
].name
!= NULL
; pindex
++) {
188 if (!strcmp(acl_flags
[pindex
].name
, tok
)) {
190 acl_add_flag_np(flags
, acl_flags
[pindex
].flag
);
195 errx(1,"Invalid permission type '%s'", tok
);
200 if (0 == permcount
) {
201 errx(1, "No permissions specified");
203 acl_set_tag_type(newent
, tag
);
204 acl_set_qualifier(newent
, entryg
);
205 acl_set_permset(newent
, perms
);
206 acl_set_flagset_np(newent
, flags
);
212 /* Convert one or more acl entries in string form to an acl_t */
214 parse_acl_entries(const char *input
) {
220 char **bufp
, *entryv
[ACL_MAX_ENTRIES
];
222 /* XXX acl_from_text(), when implemented, will presumably use the canonical
223 * text representation format, which is what chmod should be using
224 * We may need to add an entry number to the input
226 /* Translate the user supplied ACL entry */
227 /* acl_input = acl_from_text(input); */
229 inbuf
= malloc(MAX_ACL_TEXT_SIZE
);
232 err(1, "malloc() failed");
233 strncpy(inbuf
, input
, MAX_ACL_TEXT_SIZE
);
234 inbuf
[MAX_ACL_TEXT_SIZE
- 1] = '\0';
236 if ((acl_input
= acl_init(1)) == NULL
)
237 err(1, "acl_init() failed");
241 for (bufp
= entryv
; (*bufp
= strsep(&oinbuf
, "\n")) != NULL
;)
242 if (**bufp
!= '\0') {
243 if (0 != acl_create_entry(&acl_input
, &newent
))
244 err(1, "acl_create_entry() failed");
245 if (0 != parse_entry(*bufp
, newent
)) {
246 errx(1, "Failed parsing entry '%s'", *bufp
);
248 if (++bufp
>= &entryv
[ACL_MAX_ENTRIES
- 1]) {
249 errx(1, "Too many entries");
258 /* XXX No Libc support for inherited entries and generation determination yet */
260 get_inheritance_level(acl_entry_t entry
) {
261 /* XXX to be implemented */
265 /* Determine a "score" for an acl entry. The entry scores higher if it's
266 * tagged ACL_EXTENDED_DENY, and non-inherited entries are ranked higher
267 * than inherited entries.
271 score_acl_entry(acl_entry_t entry
) {
280 return (MINIMUM_TIER
);
282 if (acl_get_tag_type(entry
, &tag
) != 0) {
283 err(1, "Malformed ACL entry, no tag present");
285 if (acl_get_flagset_np(entry
, &flags
) != 0){
286 err(1, "Unable to obtain flagset");
288 if (acl_get_permset(entry
, &perms
) != 0)
289 err(1, "Malformed ACL entry, no permset present");
292 case ACL_EXTENDED_ALLOW
:
294 case ACL_EXTENDED_DENY
:
298 errx(1, "Unknown tag type %d present in ACL entry", tag
);
302 if (acl_get_flag_np(flags
, ACL_ENTRY_INHERITED
))
303 score
+= get_inheritance_level(entry
) * INHERITANCE_TIER
;
309 compare_acl_qualifiers(uuid_t
*qa
, uuid_t
*qb
) {
310 return bcmp(qa
, qb
, sizeof(uuid_t
));
313 /* Compare two ACL permsets.
315 * MATCH_SUBSET if bperms is a subset of aperms
316 * MATCH_SUPERSET if bperms is a superset of aperms
317 * MATCH_PARTIAL if the two permsets have a common subset
318 * MATCH_EXACT if the two permsets are identical
319 * MATCH_NONE if they are disjoint
323 compare_acl_permsets(acl_permset_t aperms
, acl_permset_t bperms
)
326 /* TBD Implement other match levels as needed */
327 for (i
= 0; acl_perms
[i
].name
!= NULL
; i
++) {
328 if (acl_get_perm_np(aperms
, acl_perms
[i
].perm
) !=
329 acl_get_perm_np(bperms
, acl_perms
[i
].perm
))
336 compare_acl_flagsets(acl_flagset_t aflags
, acl_flagset_t bflags
)
339 /* TBD Implement other match levels as needed */
340 for (i
= 0; acl_flags
[i
].name
!= NULL
; i
++) {
341 if (acl_get_flag_np(aflags
, acl_flags
[i
].flag
) !=
342 acl_get_flag_np(bflags
, acl_flags
[i
].flag
))
348 /* Compares two ACL entries for equality */
350 compare_acl_entries(acl_entry_t a
, acl_entry_t b
)
352 acl_tag_t atag
, btag
;
353 acl_permset_t aperms
, bperms
;
354 acl_flagset_t aflags
, bflags
;
355 int pcmp
= 0, fcmp
= 0;
358 aqual
= acl_get_qualifier(a
);
359 bqual
= acl_get_qualifier(b
);
361 int compare
= compare_acl_qualifiers(aqual
, bqual
);
368 if (0 != acl_get_tag_type(a
, &atag
))
369 err(1, "No tag type present in entry");
370 if (0!= acl_get_tag_type(b
, &btag
))
371 err(1, "No tag type present in entry");
376 if ((acl_get_permset(a
, &aperms
) != 0) ||
377 (acl_get_flagset_np(a
, &aflags
) != 0) ||
378 (acl_get_permset(b
, &bperms
) != 0) ||
379 (acl_get_flagset_np(b
, &bflags
) != 0))
380 err(1, "error fetching permissions");
382 pcmp
= compare_acl_permsets(aperms
, bperms
);
383 fcmp
= compare_acl_flagsets(aflags
, bflags
);
385 if ((pcmp
== MATCH_NONE
) || (fcmp
== MATCH_NONE
))
386 return(MATCH_PARTIAL
);
391 /* Verify that an ACL is in canonical order. Currently, the canonical
395 * inherited deny (parent)
396 * inherited allow (parent)
397 * inherited deny (grandparent)
398 * inherited allow (grandparent)
402 is_canonical(acl_t acl
) {
406 int score
= 0, next_score
= 0;
408 /* XXX - is a zero entry ACL in canonical form? */
409 if (0 != acl_get_entry(acl
, ACL_FIRST_ENTRY
, &entry
))
412 score
= score_acl_entry(entry
);
414 for (aindex
= 0; acl_get_entry(acl
, ACL_NEXT_ENTRY
, &entry
) == 0;
416 if (score
< (next_score
= score_acl_entry(entry
)))
424 /* Iterate through an ACL, and find the canonical position for the
428 find_canonical_position(acl_t acl
, acl_entry_t modifier
) {
434 /* Check if there's an entry with the same qualifier
435 * and tag type; if not, find the appropriate slot
439 if (0 != acl_get_entry(acl
, ACL_FIRST_ENTRY
, &entry
))
442 mscore
= score_acl_entry(modifier
);
444 while (mscore
< score_acl_entry(entry
)) {
448 if (0 != acl_get_entry(acl
, ACL_NEXT_ENTRY
, &entry
))
455 int canonicalize_acl_entries(acl_t acl
);
457 /* For a given acl_entry_t "modifier", find the first exact or
458 * partially matching entry from the specified acl_t acl
462 find_matching_entry (acl_t acl
, acl_entry_t modifier
, acl_entry_t
*rentryp
,
463 unsigned match_inherited
) {
465 acl_entry_t entry
= NULL
;
468 int cmp
, fcmp
= MATCH_NONE
;
471 acl_get_entry(acl
, entry
== NULL
? ACL_FIRST_ENTRY
:
472 ACL_NEXT_ENTRY
, &entry
) == 0;
474 cmp
= compare_acl_entries(entry
, modifier
);
475 if ((cmp
== MATCH_EXACT
) || (cmp
== MATCH_PARTIAL
)) {
476 if (match_inherited
) {
477 acl_flagset_t eflags
, mflags
;
479 if (0 != acl_get_flagset_np(modifier
, &mflags
))
480 err(1, "Unable to get flagset");
482 if (0 != acl_get_flagset_np(entry
, &eflags
))
483 err(1, "Unable to get flagset");
485 if (compare_acl_flagsets(mflags
, eflags
) == MATCH_EXACT
) {
495 if (fcmp
== MATCH_EXACT
)
501 /* Remove all perms specified in modifier from rentry*/
503 subtract_from_entry(acl_entry_t rentry
, acl_entry_t modifier
, int* valid_perms
)
505 acl_permset_t rperms
, mperms
;
506 acl_flagset_t rflags
, mflags
;
511 if ((acl_get_permset(rentry
, &rperms
) != 0) ||
512 (acl_get_flagset_np(rentry
, &rflags
) != 0) ||
513 (acl_get_permset(modifier
, &mperms
) != 0) ||
514 (acl_get_flagset_np(modifier
, &mflags
) != 0))
515 err(1, "error computing ACL modification");
517 for (i
= 0; acl_perms
[i
].name
!= NULL
; i
++) {
518 if (acl_get_perm_np(mperms
, acl_perms
[i
].perm
))
519 acl_delete_perm(rperms
, acl_perms
[i
].perm
);
520 else if (valid_perms
&& acl_get_perm_np(rperms
, acl_perms
[i
].perm
))
523 for (i
= 0; acl_flags
[i
].name
!= NULL
; i
++) {
524 if (acl_get_flag_np(mflags
, acl_flags
[i
].flag
))
525 acl_delete_flag_np(rflags
, acl_flags
[i
].flag
);
527 acl_set_permset(rentry
, rperms
);
528 acl_set_flagset_np(rentry
, rflags
);
531 /* Add the perms specified in modifier to rentry */
533 merge_entry_perms(acl_entry_t rentry
, acl_entry_t modifier
)
535 acl_permset_t rperms
, mperms
;
536 acl_flagset_t rflags
, mflags
;
539 if ((acl_get_permset(rentry
, &rperms
) != 0) ||
540 (acl_get_flagset_np(rentry
, &rflags
) != 0) ||
541 (acl_get_permset(modifier
, &mperms
) != 0) ||
542 (acl_get_flagset_np(modifier
, &mflags
) != 0))
543 err(1, "error computing ACL modification");
545 for (i
= 0; acl_perms
[i
].name
!= NULL
; i
++) {
546 if (acl_get_perm_np(mperms
, acl_perms
[i
].perm
))
547 acl_add_perm(rperms
, acl_perms
[i
].perm
);
549 for (i
= 0; acl_flags
[i
].name
!= NULL
; i
++) {
550 if (acl_get_flag_np(mflags
, acl_flags
[i
].flag
))
551 acl_add_flag_np(rflags
, acl_flags
[i
].flag
);
553 acl_set_permset(rentry
, rperms
);
554 acl_set_flagset_np(rentry
, rflags
);
559 modify_acl(acl_t
*oaclp
, acl_entry_t modifier
, unsigned int optflags
,
560 int position
, int inheritance_level
,
561 unsigned flag_new_acl
, const char* path
) {
564 acl_entry_t newent
= NULL
;
566 acl_entry_t rentry
= NULL
;
570 /* Add the inherited flag if requested by the user*/
571 if (modifier
&& (optflags
& ACL_INHERIT_FLAG
)) {
572 acl_flagset_t mflags
;
574 acl_get_flagset_np(modifier
, &mflags
);
575 acl_add_flag_np(mflags
, ACL_ENTRY_INHERITED
);
576 acl_set_flagset_np(modifier
, mflags
);
579 if (optflags
& ACL_SET_FLAG
) {
580 if (position
!= -1) {
581 if (0 != acl_create_entry_np(&oacl
, &newent
, position
))
582 err(1, "acl_create_entry() failed");
583 acl_copy_entry(newent
, modifier
);
585 /* If an entry exists, add the new permissions to it, else add an
586 * entry in the canonical position.
589 /* First, check for a matching entry - if one exists, merge flags */
590 dmatch
= find_matching_entry(oacl
, modifier
, &rentry
, 1);
592 if (dmatch
!= MATCH_NONE
) {
593 if (dmatch
== MATCH_EXACT
)
594 /* Nothing to be done */
597 if (dmatch
== MATCH_PARTIAL
) {
598 merge_entry_perms(rentry
, modifier
);
602 /* Insert the entry in canonical order */
603 cpos
= find_canonical_position(oacl
, modifier
);
604 if (0!= acl_create_entry_np(&oacl
, &newent
, cpos
))
605 err(1, "acl_create_entry() failed");
606 acl_copy_entry(newent
, modifier
);
608 } else if (optflags
& ACL_DELETE_FLAG
) {
610 warnx("No ACL present '%s'", path
);
612 } else if (position
!= -1 ) {
613 if (0 != acl_get_entry(oacl
, position
, &rentry
)) {
614 warnx("Invalid entry number '%s'", path
);
617 acl_delete_entry(oacl
, rentry
);
620 unsigned match_found
= 0, aindex
;
622 acl_get_entry(oacl
, rentry
== NULL
?
624 ACL_NEXT_ENTRY
, &rentry
) == 0;
627 cmp
= compare_acl_entries(rentry
, modifier
);
628 if ((cmp
== MATCH_EXACT
) ||
629 (cmp
== MATCH_PARTIAL
)) {
631 if (cmp
== MATCH_EXACT
)
632 acl_delete_entry(oacl
, rentry
);
635 /* In the event of a partial match, remove the specified perms from the
637 subtract_from_entry(rentry
, modifier
, &valid_perms
);
638 /* if no perms survived then delete the entry */
639 if (valid_perms
== 0)
640 acl_delete_entry(oacl
, rentry
);
644 if (0 == match_found
) {
645 warnx("Entry not found when attempting delete '%s'",path
);
649 } else if (optflags
& ACL_REWRITE_FLAG
) {
652 if (-1 == position
) {
655 if (0 == flag_new_acl
) {
656 if (0 != acl_get_entry(oacl
, position
,
658 err(1, "Invalid entry number '%s'", path
);
660 if (0 != acl_delete_entry(oacl
, rentry
))
661 err(1, "Unable to delete entry '%s'", path
);
663 if (0!= acl_create_entry_np(&oacl
, &newent
, position
))
664 err(1, "acl_create_entry() failed");
665 acl_copy_entry(newent
, modifier
);
673 modify_file_acl(unsigned int optflags
, const char *path
, acl_t modifier
, int position
, int inheritance_level
, int follow
) {
676 unsigned aindex
= 0, flag_new_acl
= 0;
677 acl_entry_t newent
= NULL
;
678 acl_entry_t entry
= NULL
;
683 /* XXX acl_get_file() returns a zero entry ACL if an ACL was previously
684 * associated with the file, and has had its entries removed.
685 * However, POSIX 1003.1e states that a zero entry ACL should be
686 * returned if the caller asks for ACL_TYPE_DEFAULT, and no ACL is
687 * associated with the path; it
688 * does not specifically state that a request for ACL_TYPE_EXTENDED
689 * should not return a zero entry ACL, however.
692 /* Determine if we've been given a zero entry ACL, or create an ACL if
693 * none exists. There are some issues to consider here: Should we create
694 * a zero-entry ACL for a delete or check canonicity operation?
700 if (optflags
& ACL_CLEAR_FLAG
) {
701 filesec_t fsec
= filesec_init();
703 err(1, "filesec_init() failed");
705 if (filesec_set_property(fsec
, FILESEC_ACL
, _FILESEC_REMOVE_ACL
) != 0) {
706 err(1, "filesec_set_property() failed");
709 if (chmodx_np(path
, fsec
) != 0) {
711 warn("Failed to clear ACL on file %s", path
);
716 int fd
= open(path
, O_SYMLINK
);
718 if (fchmodx_np(fd
, fsec
) != 0) {
720 warn("Failed to clear ACL on file %s", path
);
727 warn("Failed to open file %s", path
);
736 if (optflags
& ACL_FROM_STDIN
) {
737 oacl
= acl_dup(modifier
);
740 oacl
= acl_get_file(path
, ACL_TYPE_EXTENDED
);
742 int fd
= open(path
, O_SYMLINK
);
744 oacl
= acl_get_fd_np(fd
, ACL_TYPE_EXTENDED
);
748 if ((oacl
== NULL
) ||
749 (acl_get_entry(oacl
,ACL_FIRST_ENTRY
, &newent
) != 0)) {
750 if ((oacl
= acl_init(1)) == NULL
)
751 err(1, "acl_init() failed");
756 if ((0 == flag_new_acl
) && (optflags
& (ACL_REMOVE_INHERIT_FLAG
|
757 ACL_REMOVE_INHERITED_ENTRIES
))) {
759 if ((facl
= acl_init(1)) == NULL
)
760 err(1, "acl_init() failed");
763 (entry
== NULL
? ACL_FIRST_ENTRY
:
764 ACL_NEXT_ENTRY
), &entry
) == 0;
766 acl_flagset_t eflags
;
767 acl_entry_t fent
= NULL
;
768 if (acl_get_flagset_np(entry
, &eflags
) != 0) {
769 err(1, "Unable to obtain flagset");
772 if (acl_get_flag_np(eflags
, ACL_ENTRY_INHERITED
)) {
773 if (optflags
& ACL_REMOVE_INHERIT_FLAG
) {
774 acl_delete_flag_np(eflags
, ACL_ENTRY_INHERITED
);
775 acl_set_flagset_np(entry
, eflags
);
776 acl_create_entry(&facl
, &fent
);
777 acl_copy_entry(fent
, entry
);
781 acl_create_entry(&facl
, &fent
);
782 acl_copy_entry(fent
, entry
);
788 } else if (optflags
& ACL_TO_STDOUT
) {
789 ssize_t len
; /* need to get printacl() from ls(1) */
790 char *text
= acl_to_text(oacl
, &len
);
793 } else if (optflags
& ACL_CHECK_CANONICITY
) {
795 warnx("No ACL currently associated with file '%s'", path
);
797 retval
= is_canonical(oacl
);
798 } else if ((optflags
& ACL_SET_FLAG
) && (position
== -1) &&
799 (!is_canonical(oacl
))) {
800 warnx("The specified file '%s' does not have an ACL in canonical order, please specify a position with +a# ", path
);
802 } else if (((optflags
& ACL_DELETE_FLAG
) && (position
!= -1))
803 || (optflags
& ACL_CHECK_CANONICITY
)) {
804 retval
= modify_acl(&oacl
, NULL
, optflags
, position
,
805 inheritance_level
, flag_new_acl
, path
);
806 } else if ((optflags
& (ACL_REMOVE_INHERIT_FLAG
|ACL_REMOVE_INHERITED_ENTRIES
)) && flag_new_acl
) {
807 warnx("No ACL currently associated with file '%s'", path
);
810 if (!modifier
) { /* avoid bus error in acl_get_entry */
811 errx(1, "Internal error: modifier should not be NULL");
814 acl_get_entry(modifier
,
815 (entry
== NULL
? ACL_FIRST_ENTRY
:
816 ACL_NEXT_ENTRY
), &entry
) == 0;
819 retval
+= modify_acl(&oacl
, entry
, optflags
,
820 position
, inheritance_level
,
826 /* XXX Potential race here, since someone else could've modified or
827 * read the ACL on this file (with the intention of modifying it) in
828 * the interval from acl_get_file() to acl_set_file(); we can
829 * minimize one aspect of this window by comparing the original acl
830 * to a fresh one from acl_get_file() but we could consider a
831 * "changeset" mechanism, common locking strategy, or kernel
832 * supplied reservation mechanism to prevent this race.
834 if (!(optflags
& (ACL_TO_STDOUT
|ACL_CHECK_CANONICITY
))) {
837 status
= acl_set_file(path
, ACL_TYPE_EXTENDED
, oacl
);
839 int fd
= open(path
, O_SYMLINK
);
841 status
= acl_set_fd_np(fd
, oacl
,
848 warn("Failed to set ACL on file '%s'", path
);