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.
95 /* Perform a name to uuid mapping - calls through to memberd */
98 name_to_uuid(char *tok
) {
99 struct passwd
*tpass
= NULL
;
100 struct group
*tgrp
= NULL
;
101 uuid_t
*entryg
= NULL
;
104 if ((entryg
= (uuid_t
*) calloc(1,sizeof(uuid_t
))) == NULL
)
105 err(1, "Unable to allocate a uuid");
107 tpass
= getpwnam(tok
);
110 tgrp
= getgrnam(tok
);
113 err(1, "Unable to translate %s to a UID/GID", tok
);
119 if (0 != mbr_uid_to_uuid(tpass
->pw_uid
, entryg
)) {
121 err(1, "mbr_uid_to_uuid(): Unable to translate");
125 if (0 != mbr_gid_to_uuid(tgrp
->gr_gid
, entryg
)) {
127 err(1, "mbr_gid_to_uuid(): Unable to translate");
133 /* Convert an acl entry in string form to an acl_entry_t */
135 parse_entry(char *entrybuf
, acl_entry_t newent
) {
143 unsigned permcount
= 0;
146 acl_get_permset(newent
, &perms
);
147 acl_get_flagset_np(newent
, &flags
);
151 tok
= strsep(&pebuf
, " ");
153 if ((tok
== NULL
) || *tok
== '\0') {
155 err(1, "Invalid entry format");
158 /* parse the name into a qualifier */
159 entryg
= name_to_uuid(tok
);
161 tok
= strsep(&pebuf
, " ");
162 if ((tok
== NULL
) || *tok
== '\0') {
164 err(1, "Invalid entry format");
167 /* is the verb 'allow' or 'deny'? */
168 if (!strcmp(tok
, "allow")) {
169 tag
= ACL_EXTENDED_ALLOW
;
170 } else if (!strcmp(tok
, "deny")) {
171 tag
= ACL_EXTENDED_DENY
;
174 err(1, "Unknown tag type '%s'", tok
);
177 /* parse permissions */
178 for (; (tok
= strsep(&pebuf
, ",")) != NULL
;) {
180 /* is it a permission? */
181 for (pindex
= 0; acl_perms
[pindex
].name
!= NULL
; pindex
++) {
182 if (!strcmp(acl_perms
[pindex
].name
, tok
)) {
184 acl_add_perm(perms
, acl_perms
[pindex
].perm
);
190 for (pindex
= 0; acl_flags
[pindex
].name
!= NULL
; pindex
++) {
191 if (!strcmp(acl_flags
[pindex
].name
, tok
)) {
193 acl_add_flag_np(flags
, acl_flags
[pindex
].flag
);
199 err(1,"Invalid permission type %s", tok
);
204 if (0 == permcount
) {
206 err(1, "No permissions specified");
208 acl_set_tag_type(newent
, tag
);
209 acl_set_qualifier(newent
, entryg
);
210 acl_set_permset(newent
, perms
);
211 acl_set_flagset_np(newent
, flags
);
217 /* Convert one or more acl entries in string form to an acl_t */
219 parse_acl_entries(const char *input
) {
225 char **bufp
, *entryv
[ACL_MAX_ENTRIES
];
227 /* XXX acl_from_text(), when implemented, will presumably use the canonical
228 * text representation format, which is what chmod should be using
229 * We may need to add an entry number to the input
231 /* Translate the user supplied ACL entry */
232 /* acl_input = acl_from_text(input); */
234 inbuf
= malloc(MAX_ACL_TEXT_SIZE
);
237 err(1, "malloc() failed");
238 strncpy(inbuf
, input
, MAX_ACL_TEXT_SIZE
);
239 inbuf
[MAX_ACL_TEXT_SIZE
- 1] = '\0';
241 if ((acl_input
= acl_init(1)) == NULL
)
242 err(1, "acl_init() failed");
246 for (bufp
= entryv
; (*bufp
= strsep(&oinbuf
, "\n")) != NULL
;)
247 if (**bufp
!= '\0') {
248 if (0 != acl_create_entry(&acl_input
, &newent
))
249 err(1, "acl_create_entry() failed");
250 if (0 != parse_entry(*bufp
, newent
)) {
252 err(1, "Failed parsing entry %s", *bufp
);
254 if (++bufp
>= &entryv
[ACL_MAX_ENTRIES
- 1]) {
256 err(1, "Too many entries");
265 /* XXX No Libc support for inherited entries and generation determination yet */
267 get_inheritance_level(acl_entry_t entry
) {
268 /* XXX to be implemented */
272 /* Determine a "score" for an acl entry. The entry scores higher if it's
273 * tagged ACL_EXTENDED_DENY, and non-inherited entries are ranked higher
274 * than inherited entries.
278 score_acl_entry(acl_entry_t entry
) {
287 return (MINIMUM_TIER
);
289 if (acl_get_tag_type(entry
, &tag
) != 0) {
290 err(1, "Malformed ACL entry, no tag present");
292 if (acl_get_flagset_np(entry
, &flags
) != 0){
293 err(1, "Unable to obtain flagset");
295 if (acl_get_permset(entry
, &perms
) != 0)
296 err(1, "Malformed ACL entry, no permset present");
299 case ACL_EXTENDED_ALLOW
:
301 case ACL_EXTENDED_DENY
:
306 err(1, "Unknown tag type present in ACL entry");
310 if (acl_get_flag_np(flags
, ACL_ENTRY_INHERITED
))
311 score
+= get_inheritance_level(entry
) * INHERITANCE_TIER
;
317 compare_acl_qualifiers(uuid_t
*qa
, uuid_t
*qb
) {
318 return bcmp(qa
, qb
, sizeof(uuid_t
));
321 /* Compare two ACL permsets.
323 * MATCH_SUBSET if bperms is a subset of aperms
324 * MATCH_SUPERSET if bperms is a superset of aperms
325 * MATCH_PARTIAL if the two permsets have a common subset
326 * MATCH_EXACT if the two permsets are identical
327 * MATCH_NONE if they are disjoint
331 compare_acl_permsets(acl_permset_t aperms
, acl_permset_t bperms
)
334 /* TBD Implement other match levels as needed */
335 for (i
= 0; acl_perms
[i
].name
!= NULL
; i
++) {
336 if (acl_get_perm_np(aperms
, acl_perms
[i
].perm
) !=
337 acl_get_perm_np(bperms
, acl_perms
[i
].perm
))
344 compare_acl_flagsets(acl_flagset_t aflags
, acl_flagset_t bflags
)
347 /* TBD Implement other match levels as needed */
348 for (i
= 0; acl_flags
[i
].name
!= NULL
; i
++) {
349 if (acl_get_flag_np(aflags
, acl_flags
[i
].flag
) !=
350 acl_get_flag_np(bflags
, acl_flags
[i
].flag
))
356 /* Compares two ACL entries for equality */
358 compare_acl_entries(acl_entry_t a
, acl_entry_t b
)
360 acl_tag_t atag
, btag
;
361 acl_permset_t aperms
, bperms
;
362 acl_flagset_t aflags
, bflags
;
363 int pcmp
= 0, fcmp
= 0;
365 if (0 != compare_acl_qualifiers(acl_get_qualifier(a
),
366 acl_get_qualifier(b
)))
369 if (0 != acl_get_tag_type(a
, &atag
))
370 err(1, "No tag type present in entry");
371 if (0!= acl_get_tag_type(b
, &btag
))
372 err(1, "No tag type present in entry");
377 if ((acl_get_permset(a
, &aperms
) != 0) ||
378 (acl_get_flagset_np(a
, &aflags
) != 0) ||
379 (acl_get_permset(b
, &bperms
) != 0) ||
380 (acl_get_flagset_np(b
, &bflags
) != 0))
381 err(1, "error fetching permissions");
383 pcmp
= compare_acl_permsets(aperms
, bperms
);
384 fcmp
= compare_acl_flagsets(aflags
, bflags
);
386 if ((pcmp
== MATCH_NONE
) || (fcmp
== MATCH_NONE
))
387 return(MATCH_PARTIAL
);
392 /* Verify that an ACL is in canonical order. Currently, the canonical
396 * inherited deny (parent)
397 * inherited allow (parent)
398 * inherited deny (grandparent)
399 * inherited allow (grandparent)
403 is_canonical(acl_t acl
) {
407 int score
= 0, next_score
= 0;
409 /* XXX - is a zero entry ACL in canonical form? */
410 if (0 != acl_get_entry(acl
, ACL_FIRST_ENTRY
, &entry
))
413 score
= score_acl_entry(entry
);
415 for (aindex
= 0; acl_get_entry(acl
, ACL_NEXT_ENTRY
, &entry
) == 0;
417 if (score
< (next_score
= score_acl_entry(entry
)))
425 /* Iterate through an ACL, and find the canonical position for the
429 find_canonical_position(acl_t acl
, acl_entry_t modifier
) {
435 /* Check if there's an entry with the same qualifier
436 * and tag type; if not, find the appropriate slot
440 if (0 != acl_get_entry(acl
, ACL_FIRST_ENTRY
, &entry
))
443 mscore
= score_acl_entry(modifier
);
445 while (mscore
< score_acl_entry(entry
)) {
449 if (0 != acl_get_entry(acl
, ACL_NEXT_ENTRY
, &entry
))
456 int canonicalize_acl_entries(acl_t acl
);
458 /* For a given acl_entry_t "modifier", find the first exact or
459 * partially matching entry from the specified acl_t acl
463 find_matching_entry (acl_t acl
, acl_entry_t modifier
, acl_entry_t
*rentryp
,
464 unsigned match_inherited
) {
466 acl_entry_t entry
= NULL
;
469 int cmp
, fcmp
= MATCH_NONE
;
472 acl_get_entry(acl
, entry
== NULL
? ACL_FIRST_ENTRY
:
473 ACL_NEXT_ENTRY
, &entry
) == 0;
475 cmp
= compare_acl_entries(entry
, modifier
);
476 if ((cmp
== MATCH_EXACT
) || (cmp
== MATCH_PARTIAL
)) {
477 if (match_inherited
) {
478 acl_flagset_t eflags
, mflags
;
480 if (0 != acl_get_flagset_np(modifier
, &mflags
))
481 err(1, "Unable to get flagset");
483 if (0 != acl_get_flagset_np(entry
, &eflags
))
484 err(1, "Unable to get flagset");
486 if (acl_get_flag_np(mflags
, ACL_ENTRY_INHERITED
) ==
487 acl_get_flag_np(eflags
, ACL_ENTRY_INHERITED
)) {
497 if (fcmp
== MATCH_EXACT
)
503 /* Remove all perms specified in modifier from rentry*/
505 subtract_from_entry(acl_entry_t rentry
, acl_entry_t modifier
)
507 acl_permset_t rperms
, mperms
;
508 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
);
521 for (i
= 0; acl_flags
[i
].name
!= NULL
; i
++) {
522 if (acl_get_flag_np(mflags
, acl_flags
[i
].flag
))
523 acl_delete_flag_np(rflags
, acl_flags
[i
].flag
);
525 acl_set_permset(rentry
, rperms
);
526 acl_set_flagset_np(rentry
, rflags
);
529 /* Add the perms specified in modifier to rentry */
531 merge_entry_perms(acl_entry_t rentry
, acl_entry_t modifier
)
533 acl_permset_t rperms
, mperms
;
534 acl_flagset_t rflags
, mflags
;
537 if ((acl_get_permset(rentry
, &rperms
) != 0) ||
538 (acl_get_flagset_np(rentry
, &rflags
) != 0) ||
539 (acl_get_permset(modifier
, &mperms
) != 0) ||
540 (acl_get_flagset_np(modifier
, &mflags
) != 0))
541 err(1, "error computing ACL modification");
543 for (i
= 0; acl_perms
[i
].name
!= NULL
; i
++) {
544 if (acl_get_perm_np(mperms
, acl_perms
[i
].perm
))
545 acl_add_perm(rperms
, acl_perms
[i
].perm
);
547 for (i
= 0; acl_flags
[i
].name
!= NULL
; i
++) {
548 if (acl_get_flag_np(mflags
, acl_flags
[i
].flag
))
549 acl_add_flag_np(rflags
, acl_flags
[i
].flag
);
551 acl_set_permset(rentry
, rperms
);
552 acl_set_flagset_np(rentry
, rflags
);
557 modify_acl(acl_t
*oaclp
, acl_entry_t modifier
, unsigned int optflags
,
558 int position
, int inheritance_level
,
559 unsigned flag_new_acl
) {
562 acl_entry_t newent
= NULL
;
564 acl_entry_t rentry
= NULL
;
568 /* Add the inherited flag if requested by the user*/
569 if (modifier
&& (optflags
& ACL_INHERIT_FLAG
)) {
570 acl_flagset_t mflags
;
572 acl_get_flagset_np(modifier
, &mflags
);
573 acl_add_flag_np(mflags
, ACL_ENTRY_INHERITED
);
574 acl_set_flagset_np(modifier
, mflags
);
577 if (optflags
& ACL_SET_FLAG
) {
578 if (position
!= -1) {
579 if (0 != acl_create_entry_np(&oacl
, &newent
, position
))
580 err(1, "acl_create_entry() failed");
581 acl_copy_entry(newent
, modifier
);
584 /* If an entry exists, add the new permissions to it, else add an
585 * entry in the canonical position.
588 /* First, check for a matching entry - if one exists, merge flags */
589 dmatch
= find_matching_entry(oacl
, modifier
, &rentry
, 1);
591 if (dmatch
!= MATCH_NONE
) {
592 if (dmatch
== MATCH_EXACT
)
593 /* Nothing to be done */
596 if (dmatch
== MATCH_PARTIAL
) {
597 merge_entry_perms(rentry
, modifier
);
601 /* Insert the entry in canonical order */
602 cpos
= find_canonical_position(oacl
, modifier
);
603 if (0!= acl_create_entry_np(&oacl
, &newent
, cpos
))
604 err(1, "acl_create_entry() failed");
605 acl_copy_entry(newent
, modifier
);
609 if (optflags
& ACL_DELETE_FLAG
) {
613 err(1, "No ACL present");
615 if (position
!= -1 ) {
616 if (0 != acl_get_entry(oacl
, position
, &rentry
))
617 err(1, "Invalid entry number");
618 acl_delete_entry(oacl
, rentry
);
621 unsigned match_found
= 0, aindex
;
623 acl_get_entry(oacl
, rentry
== NULL
?
625 ACL_NEXT_ENTRY
, &rentry
)
628 cmp
= compare_acl_entries(rentry
,
630 if ((cmp
== MATCH_EXACT
) ||
631 (cmp
== MATCH_PARTIAL
)) {
633 if (cmp
== MATCH_EXACT
)
634 acl_delete_entry(oacl
, rentry
);
636 /* In the event of a partial match, remove the specified perms from the
638 subtract_from_entry(rentry
, modifier
);
641 if (0 == match_found
) {
643 warn("Entry not found when attempting delete");
649 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");
660 if (0 != acl_delete_entry(oacl
, rentry
))
661 err(1, "Unable to delete entry");
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
) {
676 unsigned aindex
= 0, flag_new_acl
= 0;
677 acl_entry_t newent
= NULL
;
678 acl_entry_t entry
= NULL
;
679 unsigned retval
= 0 ;
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");
704 if (filesec_set_property(fsec
, FILESEC_ACL
,
705 _FILESEC_REMOVE_ACL
) != 0)
706 err(1, "filesec_set_property() failed");
707 if (chmodx_np(path
, fsec
) != 0) {
709 warn("Failed to clear ACL on file %s", path
);
717 if (optflags
& ACL_FROM_STDIN
)
718 oacl
= acl_dup(modifier
);
720 oacl
= acl_get_file(path
, ACL_TYPE_EXTENDED
);
722 if ((oacl
== NULL
) ||
723 (acl_get_entry(oacl
,ACL_FIRST_ENTRY
, &newent
) != 0)) {
724 if ((oacl
= acl_init(1)) == NULL
)
725 err(1, "acl_init() failed");
730 if ((0 == flag_new_acl
) && (optflags
& (ACL_REMOVE_INHERIT_FLAG
|
731 ACL_REMOVE_INHERITED_ENTRIES
))) {
733 if ((facl
= acl_init(1)) == NULL
)
734 err(1, "acl_init() failed");
737 (entry
== NULL
? ACL_FIRST_ENTRY
:
738 ACL_NEXT_ENTRY
), &entry
) == 0;
740 acl_flagset_t eflags
;
741 acl_entry_t fent
= NULL
;
742 if (acl_get_flagset_np(entry
, &eflags
) != 0) {
743 err(1, "Unable to obtain flagset");
746 if (acl_get_flag_np(eflags
, ACL_ENTRY_INHERITED
)) {
747 if (optflags
& ACL_REMOVE_INHERIT_FLAG
) {
748 acl_delete_flag_np(eflags
, ACL_ENTRY_INHERITED
);
749 acl_set_flagset_np(entry
, eflags
);
750 acl_create_entry(&facl
, &fent
);
751 acl_copy_entry(fent
, entry
);
755 acl_create_entry(&facl
, &fent
);
756 acl_copy_entry(fent
, entry
);
764 if (optflags
& ACL_CHECK_CANONICITY
) {
767 warn("No ACL currently associated with file %s", path
);
769 return(is_canonical(oacl
));
772 if ((optflags
& ACL_SET_FLAG
) && (position
== -1) &&
773 (!is_canonical(oacl
))) {
775 warn("The specified file %s does not have an ACL in canonical order, please specify a position with +a# ", path
);
779 if (((optflags
& ACL_DELETE_FLAG
) && (position
!= -1))
780 || (optflags
& ACL_CHECK_CANONICITY
)) {
781 retval
= modify_acl(&oacl
, NULL
, optflags
, position
,
782 inheritance_level
, flag_new_acl
);
786 acl_get_entry(modifier
,
787 (entry
== NULL
? ACL_FIRST_ENTRY
:
788 ACL_NEXT_ENTRY
), &entry
) == 0;
791 retval
+= modify_acl(&oacl
, entry
, optflags
,
792 position
, inheritance_level
,
797 /* XXX Potential race here, since someone else could've modified or
798 * read the ACL on this file (with the intention of modifying it) in
799 * the interval from acl_get_file() to acl_set_file(); we can
800 * minimize one aspect of this window by comparing the original acl
801 * to a fresh one from acl_get_file() but we could consider a
802 * "changeset" mechanism, common locking strategy, or kernel
803 * supplied reservation mechanism to prevent this race.
805 if (!(optflags
& ACL_CHECK_CANONICITY
) &&
806 (0 != acl_set_file(path
, ACL_TYPE_EXTENDED
, oacl
))){
808 warn("Failed to set ACL on file %s", path
);