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>
41 #include <membership.h>
42 #include "chmod_acl.h"
44 extern void usage(void);
51 #define ACL_PERM_DIR (1<<0)
52 #define ACL_PERM_FILE (1<<1)
54 {ACL_READ_DATA
, "read", ACL_PERM_FILE
},
55 {ACL_LIST_DIRECTORY
, "list", ACL_PERM_DIR
},
56 {ACL_WRITE_DATA
, "write", ACL_PERM_FILE
},
57 {ACL_ADD_FILE
, "add_file", ACL_PERM_DIR
},
58 {ACL_EXECUTE
, "execute", ACL_PERM_FILE
},
59 {ACL_SEARCH
, "search", ACL_PERM_DIR
},
60 {ACL_DELETE
, "delete", ACL_PERM_FILE
| ACL_PERM_DIR
},
61 {ACL_APPEND_DATA
, "append", ACL_PERM_FILE
},
62 {ACL_ADD_SUBDIRECTORY
, "add_subdirectory", ACL_PERM_DIR
},
63 {ACL_DELETE_CHILD
, "delete_child", ACL_PERM_DIR
},
64 {ACL_READ_ATTRIBUTES
, "readattr", ACL_PERM_FILE
| ACL_PERM_DIR
},
65 {ACL_WRITE_ATTRIBUTES
, "writeattr", ACL_PERM_FILE
| ACL_PERM_DIR
},
66 {ACL_READ_EXTATTRIBUTES
, "readextattr", ACL_PERM_FILE
| ACL_PERM_DIR
},
67 {ACL_WRITE_EXTATTRIBUTES
, "writeextattr", ACL_PERM_FILE
| ACL_PERM_DIR
},
68 {ACL_READ_SECURITY
, "readsecurity", ACL_PERM_FILE
| ACL_PERM_DIR
},
69 {ACL_WRITE_SECURITY
, "writesecurity", ACL_PERM_FILE
| ACL_PERM_DIR
},
70 {ACL_CHANGE_OWNER
, "chown", ACL_PERM_FILE
| ACL_PERM_DIR
},
79 {ACL_ENTRY_INHERITED
, "inherited", ACL_PERM_FILE
| ACL_PERM_DIR
},
80 {ACL_ENTRY_FILE_INHERIT
, "file_inherit", ACL_PERM_DIR
},
81 {ACL_ENTRY_DIRECTORY_INHERIT
, "directory_inherit", ACL_PERM_DIR
},
82 {ACL_ENTRY_LIMIT_INHERIT
, "limit_inherit", ACL_PERM_FILE
| ACL_PERM_DIR
},
83 {ACL_ENTRY_ONLY_INHERIT
, "only_inherit", ACL_PERM_DIR
},
87 /* TBD - Many of these routines could potentially be considered for
88 * inclusion in a library. If that is done, either avoid use of "err"
89 * and implement a better fall-through strategy in case of errors,
90 * or use err_set_exit() and make various structures globals.
93 /* Perform a name to uuid mapping - calls through to memberd */
96 name_to_uuid(char *tok
) {
97 struct passwd
*tpass
= NULL
;
98 struct group
*tgrp
= NULL
;
99 uuid_t
*entryg
= NULL
;
102 if ((entryg
= (uuid_t
*) calloc(1,sizeof(uuid_t
))) == NULL
)
103 err(1, "Unable to allocate a uuid");
105 tpass
= getpwnam(tok
);
108 tgrp
= getgrnam(tok
);
111 err(1, "Unable to translate %s to a UID/GID", tok
);
117 if (0 != mbr_uid_to_uuid(tpass
->pw_uid
, entryg
)) {
119 err(1, "mbr_uid_to_uuid(): Unable to translate");
123 if (0 != mbr_gid_to_uuid(tgrp
->gr_gid
, entryg
)) {
125 err(1, "mbr_gid_to_uuid(): Unable to translate");
131 /* Convert an acl entry in string form to an acl_entry_t */
133 parse_entry(char *entrybuf
, acl_entry_t newent
) {
141 unsigned permcount
= 0;
144 acl_get_permset(newent
, &perms
);
145 acl_get_flagset_np(newent
, &flags
);
149 tok
= strsep(&pebuf
, " ");
151 if ((tok
== NULL
) || *tok
== '\0') {
153 err(1, "Invalid entry format");
156 /* parse the name into a qualifier */
157 entryg
= name_to_uuid(tok
);
159 tok
= strsep(&pebuf
, " ");
160 if ((tok
== NULL
) || *tok
== '\0') {
162 err(1, "Invalid entry format");
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
;
172 err(1, "Unknown tag type '%s'", tok
);
175 /* parse permissions */
176 for (; (tok
= strsep(&pebuf
, ",")) != NULL
;) {
178 /* is it a permission? */
179 for (pindex
= 0; acl_perms
[pindex
].name
!= NULL
; pindex
++) {
180 if (!strcmp(acl_perms
[pindex
].name
, tok
)) {
182 acl_add_perm(perms
, acl_perms
[pindex
].perm
);
188 for (pindex
= 0; acl_flags
[pindex
].name
!= NULL
; pindex
++) {
189 if (!strcmp(acl_flags
[pindex
].name
, tok
)) {
191 acl_add_flag_np(flags
, acl_flags
[pindex
].flag
);
197 err(1,"Invalid permission type %s", tok
);
202 if (0 == permcount
) {
204 err(1, "No permissions specified");
206 acl_set_tag_type(newent
, tag
);
207 acl_set_qualifier(newent
, entryg
);
208 acl_set_permset(newent
, perms
);
209 acl_set_flagset_np(newent
, flags
);
215 /* Convert one or more acl entries in string form to an acl_t */
217 parse_acl_entries(const char *input
) {
223 char **bufp
, *entryv
[ACL_MAX_ENTRIES
];
225 /* XXX acl_from_text(), when implemented, will presumably use the canonical
226 * text representation format, which is what chmod should be using
227 * We may need to add an entry number to the input
229 /* Translate the user supplied ACL entry */
230 /* acl_input = acl_from_text(input); */
232 inbuf
= malloc(MAX_ACL_TEXT_SIZE
);
235 err(1, "malloc() failed");
236 strncpy(inbuf
, input
, MAX_ACL_TEXT_SIZE
);
237 inbuf
[MAX_ACL_TEXT_SIZE
- 1] = '\0';
239 if ((acl_input
= acl_init(1)) == NULL
)
240 err(1, "acl_init() failed");
244 for (bufp
= entryv
; (*bufp
= strsep(&oinbuf
, "\n")) != NULL
;)
245 if (**bufp
!= '\0') {
246 if (0 != acl_create_entry(&acl_input
, &newent
))
247 err(1, "acl_create_entry() failed");
248 if (0 != parse_entry(*bufp
, newent
)) {
250 err(1, "Failed parsing entry %s", *bufp
);
252 if (++bufp
>= &entryv
[ACL_MAX_ENTRIES
- 1]) {
254 err(1, "Too many entries");
263 /* XXX No Libc support for inherited entries and generation determination yet */
265 get_inheritance_level(acl_entry_t entry
) {
266 /* XXX to be implemented */
270 /* Determine a "score" for an acl entry. The entry scores higher if it's
271 * tagged ACL_EXTENDED_DENY, and non-inherited entries are ranked higher
272 * than inherited entries.
276 score_acl_entry(acl_entry_t entry
) {
285 return (MINIMUM_TIER
);
287 if (acl_get_tag_type(entry
, &tag
) != 0) {
288 err(1, "Malformed ACL entry, no tag present");
290 if (acl_get_flagset_np(entry
, &flags
) != 0){
291 err(1, "Unable to obtain flagset");
293 if (acl_get_permset(entry
, &perms
) != 0)
294 err(1, "Malformed ACL entry, no permset present");
297 case ACL_EXTENDED_ALLOW
:
299 case ACL_EXTENDED_DENY
:
304 err(1, "Unknown tag type present in ACL entry");
308 if (acl_get_flag_np(flags
, ACL_ENTRY_INHERITED
))
309 score
+= get_inheritance_level(entry
) * INHERITANCE_TIER
;
315 compare_acl_qualifiers(uuid_t
*qa
, uuid_t
*qb
) {
316 return bcmp(qa
, qb
, sizeof(uuid_t
));
319 /* Compare two ACL permsets.
321 * MATCH_SUBSET if bperms is a subset of aperms
322 * MATCH_SUPERSET if bperms is a superset of aperms
323 * MATCH_PARTIAL if the two permsets have a common subset
324 * MATCH_EXACT if the two permsets are identical
325 * MATCH_NONE if they are disjoint
329 compare_acl_permsets(acl_permset_t aperms
, acl_permset_t bperms
)
332 /* TBD Implement other match levels as needed */
333 for (i
= 0; acl_perms
[i
].name
!= NULL
; i
++) {
334 if (acl_get_perm_np(aperms
, acl_perms
[i
].perm
) !=
335 acl_get_perm_np(bperms
, acl_perms
[i
].perm
))
342 compare_acl_flagsets(acl_flagset_t aflags
, acl_flagset_t bflags
)
345 /* TBD Implement other match levels as needed */
346 for (i
= 0; acl_flags
[i
].name
!= NULL
; i
++) {
347 if (acl_get_flag_np(aflags
, acl_flags
[i
].flag
) !=
348 acl_get_flag_np(bflags
, acl_flags
[i
].flag
))
354 /* Compares two ACL entries for equality */
356 compare_acl_entries(acl_entry_t a
, acl_entry_t b
)
358 acl_tag_t atag
, btag
;
359 acl_permset_t aperms
, bperms
;
360 acl_flagset_t aflags
, bflags
;
361 int pcmp
= 0, fcmp
= 0;
363 if (0 != compare_acl_qualifiers(acl_get_qualifier(a
),
364 acl_get_qualifier(b
)))
367 if (0 != acl_get_tag_type(a
, &atag
))
368 err(1, "No tag type present in entry");
369 if (0!= acl_get_tag_type(b
, &btag
))
370 err(1, "No tag type present in entry");
375 if ((acl_get_permset(a
, &aperms
) != 0) ||
376 (acl_get_flagset_np(a
, &aflags
) != 0) ||
377 (acl_get_permset(b
, &bperms
) != 0) ||
378 (acl_get_flagset_np(b
, &bflags
) != 0))
379 err(1, "error fetching permissions");
381 pcmp
= compare_acl_permsets(aperms
, bperms
);
382 fcmp
= compare_acl_flagsets(aflags
, bflags
);
384 if ((pcmp
== MATCH_NONE
) || (fcmp
== MATCH_NONE
))
385 return(MATCH_PARTIAL
);
390 /* Verify that an ACL is in canonical order. Currently, the canonical
394 * inherited deny (parent)
395 * inherited allow (parent)
396 * inherited deny (grandparent)
397 * inherited allow (grandparent)
401 is_canonical(acl_t acl
) {
405 int score
= 0, next_score
= 0;
407 /* XXX - is a zero entry ACL in canonical form? */
408 if (0 != acl_get_entry(acl
, ACL_FIRST_ENTRY
, &entry
))
411 score
= score_acl_entry(entry
);
413 for (aindex
= 0; acl_get_entry(acl
, ACL_NEXT_ENTRY
, &entry
) == 0;
415 if (score
< (next_score
= score_acl_entry(entry
)))
423 /* Iterate through an ACL, and find the canonical position for the
427 find_canonical_position(acl_t acl
, acl_entry_t modifier
) {
433 /* Check if there's an entry with the same qualifier
434 * and tag type; if not, find the appropriate slot
438 if (0 != acl_get_entry(acl
, ACL_FIRST_ENTRY
, &entry
))
441 mscore
= score_acl_entry(modifier
);
443 while (mscore
< score_acl_entry(entry
)) {
447 if (0 != acl_get_entry(acl
, ACL_NEXT_ENTRY
, &entry
))
454 int canonicalize_acl_entries(acl_t acl
);
456 /* For a given acl_entry_t "modifier", find the first exact or
457 * partially matching entry from the specified acl_t acl
461 find_matching_entry (acl_t acl
, acl_entry_t modifier
, acl_entry_t
*rentryp
,
462 unsigned match_inherited
) {
464 acl_entry_t entry
= NULL
;
467 int cmp
, fcmp
= MATCH_NONE
;
470 acl_get_entry(acl
, entry
== NULL
? ACL_FIRST_ENTRY
:
471 ACL_NEXT_ENTRY
, &entry
) == 0;
473 cmp
= compare_acl_entries(entry
, modifier
);
474 if ((cmp
== MATCH_EXACT
) || (cmp
== MATCH_PARTIAL
)) {
475 if (match_inherited
) {
476 acl_flagset_t eflags
, mflags
;
478 if (0 != acl_get_flagset_np(modifier
, &mflags
))
479 err(1, "Unable to get flagset");
481 if (0 != acl_get_flagset_np(entry
, &eflags
))
482 err(1, "Unable to get flagset");
484 if (acl_get_flag_np(mflags
, ACL_ENTRY_INHERITED
) ==
485 acl_get_flag_np(eflags
, ACL_ENTRY_INHERITED
)) {
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
)
505 acl_permset_t rperms
, mperms
;
506 acl_flagset_t rflags
, mflags
;
509 if ((acl_get_permset(rentry
, &rperms
) != 0) ||
510 (acl_get_flagset_np(rentry
, &rflags
) != 0) ||
511 (acl_get_permset(modifier
, &mperms
) != 0) ||
512 (acl_get_flagset_np(modifier
, &mflags
) != 0))
513 err(1, "error computing ACL modification");
515 for (i
= 0; acl_perms
[i
].name
!= NULL
; i
++) {
516 if (acl_get_perm_np(mperms
, acl_perms
[i
].perm
))
517 acl_delete_perm(rperms
, acl_perms
[i
].perm
);
519 for (i
= 0; acl_flags
[i
].name
!= NULL
; i
++) {
520 if (acl_get_flag_np(mflags
, acl_flags
[i
].flag
))
521 acl_delete_flag_np(rflags
, acl_flags
[i
].flag
);
523 acl_set_permset(rentry
, rperms
);
524 acl_set_flagset_np(rentry
, rflags
);
527 /* Add the perms specified in modifier to rentry */
529 merge_entry_perms(acl_entry_t rentry
, acl_entry_t modifier
)
531 acl_permset_t rperms
, mperms
;
532 acl_flagset_t rflags
, mflags
;
535 if ((acl_get_permset(rentry
, &rperms
) != 0) ||
536 (acl_get_flagset_np(rentry
, &rflags
) != 0) ||
537 (acl_get_permset(modifier
, &mperms
) != 0) ||
538 (acl_get_flagset_np(modifier
, &mflags
) != 0))
539 err(1, "error computing ACL modification");
541 for (i
= 0; acl_perms
[i
].name
!= NULL
; i
++) {
542 if (acl_get_perm_np(mperms
, acl_perms
[i
].perm
))
543 acl_add_perm(rperms
, acl_perms
[i
].perm
);
545 for (i
= 0; acl_flags
[i
].name
!= NULL
; i
++) {
546 if (acl_get_flag_np(mflags
, acl_flags
[i
].flag
))
547 acl_add_flag_np(rflags
, acl_flags
[i
].flag
);
549 acl_set_permset(rentry
, rperms
);
550 acl_set_flagset_np(rentry
, rflags
);
555 modify_acl(acl_t
*oaclp
, acl_entry_t modifier
, unsigned int optflags
,
556 int position
, int inheritance_level
,
557 unsigned flag_new_acl
) {
560 acl_entry_t newent
= NULL
;
562 acl_entry_t rentry
= NULL
;
566 /* Add the inherited flag if requested by the user*/
567 if (modifier
&& (optflags
& ACL_INHERIT_FLAG
)) {
568 acl_flagset_t mflags
;
570 acl_get_flagset_np(modifier
, &mflags
);
571 acl_add_flag_np(mflags
, ACL_ENTRY_INHERITED
);
572 acl_set_flagset_np(modifier
, mflags
);
575 if (optflags
& ACL_SET_FLAG
) {
576 if (position
!= -1) {
577 if (0 != acl_create_entry_np(&oacl
, &newent
, position
))
578 err(1, "acl_create_entry() failed");
579 acl_copy_entry(newent
, modifier
);
582 /* If an entry exists, add the new permissions to it, else add an
583 * entry in the canonical position.
586 /* First, check for a matching entry - if one exists, merge flags */
587 dmatch
= find_matching_entry(oacl
, modifier
, &rentry
, 1);
589 if (dmatch
!= MATCH_NONE
) {
590 if (dmatch
== MATCH_EXACT
)
591 /* Nothing to be done */
594 if (dmatch
== MATCH_PARTIAL
) {
595 merge_entry_perms(rentry
, modifier
);
599 /* Insert the entry in canonical order */
600 cpos
= find_canonical_position(oacl
, modifier
);
601 if (0!= acl_create_entry_np(&oacl
, &newent
, cpos
))
602 err(1, "acl_create_entry() failed");
603 acl_copy_entry(newent
, modifier
);
607 if (optflags
& ACL_DELETE_FLAG
) {
611 err(1, "No ACL present");
613 if (position
!= -1 ) {
614 if (0 != acl_get_entry(oacl
, position
, &rentry
))
615 err(1, "Invalid entry number");
616 acl_delete_entry(oacl
, rentry
);
619 unsigned match_found
= 0, aindex
;
621 acl_get_entry(oacl
, rentry
== NULL
?
623 ACL_NEXT_ENTRY
, &rentry
)
626 cmp
= compare_acl_entries(rentry
,
628 if ((cmp
== MATCH_EXACT
) ||
629 (cmp
== MATCH_PARTIAL
)) {
631 if (cmp
== MATCH_EXACT
)
632 acl_delete_entry(oacl
, rentry
);
634 /* In the event of a partial match, remove the specified perms from the
636 subtract_from_entry(rentry
, modifier
);
639 if (0 == match_found
) {
641 warn("Entry not found when attempting delete");
647 if (optflags
& ACL_REWRITE_FLAG
) {
650 if (-1 == position
) {
653 if (0 == flag_new_acl
) {
654 if (0 != acl_get_entry(oacl
, position
,
656 err(1, "Invalid entry number");
658 if (0 != acl_delete_entry(oacl
, rentry
))
659 err(1, "Unable to delete entry");
661 if (0!= acl_create_entry_np(&oacl
, &newent
, position
))
662 err(1, "acl_create_entry() failed");
663 acl_copy_entry(newent
, modifier
);
671 modify_file_acl(unsigned int optflags
, const char *path
, acl_t modifier
, int position
, int inheritance_level
) {
674 unsigned aindex
= 0, flag_new_acl
= 0;
675 acl_entry_t newent
= NULL
;
676 acl_entry_t entry
= NULL
;
677 unsigned retval
= 0 ;
681 /* XXX acl_get_file() returns a zero entry ACL if an ACL was previously
682 * associated with the file, and has had its entries removed.
683 * However, POSIX 1003.1e states that a zero entry ACL should be
684 * returned if the caller asks for ACL_TYPE_DEFAULT, and no ACL is
685 * associated with the path; it
686 * does not specifically state that a request for ACL_TYPE_EXTENDED
687 * should not return a zero entry ACL, however.
690 /* Determine if we've been given a zero entry ACL, or create an ACL if
691 * none exists. There are some issues to consider here: Should we create
692 * a zero-entry ACL for a delete or check canonicity operation?
698 if (optflags
& ACL_FROM_STDIN
)
699 oacl
= acl_dup(modifier
);
701 oacl
= acl_get_file(path
, ACL_TYPE_EXTENDED
);
703 if ((oacl
== NULL
) ||
704 (acl_get_entry(oacl
,ACL_FIRST_ENTRY
, &newent
) != 0)) {
705 if ((oacl
= acl_init(1)) == NULL
)
706 err(1, "acl_init() failed");
711 if ((0 == flag_new_acl
) && (optflags
& (ACL_REMOVE_INHERIT_FLAG
|
712 ACL_REMOVE_INHERITED_ENTRIES
))) {
714 if ((facl
= acl_init(1)) == NULL
)
715 err(1, "acl_init() failed");
718 (entry
== NULL
? ACL_FIRST_ENTRY
:
719 ACL_NEXT_ENTRY
), &entry
) == 0;
721 acl_flagset_t eflags
;
722 acl_entry_t fent
= NULL
;
723 if (acl_get_flagset_np(entry
, &eflags
) != 0) {
724 err(1, "Unable to obtain flagset");
727 if (acl_get_flag_np(eflags
, ACL_ENTRY_INHERITED
)) {
728 if (optflags
& ACL_REMOVE_INHERIT_FLAG
) {
729 acl_delete_flag_np(eflags
, ACL_ENTRY_INHERITED
);
730 acl_set_flagset_np(entry
, eflags
);
731 acl_create_entry(&facl
, &fent
);
732 acl_copy_entry(fent
, entry
);
736 acl_create_entry(&facl
, &fent
);
737 acl_copy_entry(fent
, entry
);
745 if (optflags
& ACL_CHECK_CANONICITY
) {
748 warn("No ACL currently associated with file %s", path
);
750 return(is_canonical(oacl
));
753 if ((optflags
& ACL_SET_FLAG
) && (position
== -1) &&
754 (!is_canonical(oacl
))) {
756 warn("The specified file %s does not have an ACL in canonical order, please specify a position with +a# ", path
);
760 if (((optflags
& ACL_DELETE_FLAG
) && (position
!= -1))
761 || (optflags
& ACL_CHECK_CANONICITY
)) {
762 retval
= modify_acl(&oacl
, NULL
, optflags
, position
,
763 inheritance_level
, flag_new_acl
);
767 acl_get_entry(modifier
,
768 (entry
== NULL
? ACL_FIRST_ENTRY
:
769 ACL_NEXT_ENTRY
), &entry
) == 0;
772 retval
+= modify_acl(&oacl
, entry
, optflags
,
773 position
, inheritance_level
,
778 /* XXX Potential race here, since someone else could've modified or
779 * read the ACL on this file (with the intention of modifying it) in
780 * the interval from acl_get_file() to acl_set_file(); we can
781 * minimize one aspect of this window by comparing the original acl
782 * to a fresh one from acl_get_file() but we could consider a
783 * "changeset" mechanism, common locking strategy, or kernel
784 * supplied reservation mechanism to prevent this race.
786 if (!(optflags
& ACL_CHECK_CANONICITY
) &&
787 (0 != acl_set_file(path
, ACL_TYPE_EXTENDED
, oacl
))){
789 warn("Failed to set ACL on file %s", path
);