2 * @file apply-default-acl.c
4 * @brief The entire implementation.
8 /* On Linux, ftw.h needs this special voodoo to work. */
9 #define _XOPEN_SOURCE 500
13 #include <fcntl.h> /* AT_FOO constants */
14 #include <ftw.h> /* nftw() et al. */
16 #include <libgen.h> /* basename(), dirname() */
17 #include <limits.h> /* PATH_MAX */
26 #include <acl/libacl.h> /* acl_get_perm, not portable */
27 #include <sys/types.h>
30 /* Most of the libacl functions return 1 for success, 0 for failure,
36 /* Even though most other library functions reliably return -1 for
37 * error, it feels a little wrong to re-use the ACL_ERROR constant.
39 #define CLOSE_ERROR -1
42 #define SNPRINTF_ERROR -1
45 int safe_open_ex(int at_fd
, char* pathname
, int flags
) {
46 if (pathname
!= NULL
&& strlen(pathname
) == 0) {
47 /* Oops, went one level to deep with nothing to do. */
51 char* firstslash
= strchr(pathname
, '/');
52 if (firstslash
== NULL
) {
53 /* No more slashes, this is the base case. */
54 int r
= openat(at_fd
, pathname
, flags
);
58 /* Temporarily disable the slash, so that the subsequent call to
59 openat() opens only the next directory (and doesn't recurse). */
61 int fd
= safe_open_ex(at_fd
, pathname
, flags
);
62 if (fd
== OPEN_ERROR
) {
64 /* Don't output anything if we ignore a symlink */
65 perror("safe_open_ex (safe_open_ex)");
70 /* The ++ is safe because there needs to be at least a null byte
71 after the first slash, even if it's the last real character in
73 int result
= safe_open_ex(fd
, firstslash
+1, flags
);
74 if (close(fd
) == CLOSE_ERROR
) {
75 perror("safe_open_ex (close)");
82 int safe_open(const char* pathname
, int flags
) {
83 if (pathname
== NULL
|| strlen(pathname
) == 0 || pathname
[0] == '\0') {
88 char abspath
[PATH_MAX
];
89 int snprintf_result
= 0;
90 if (strchr(pathname
, '/') == pathname
) {
91 /* pathname is already absolute; just copy it. */
92 snprintf_result
= snprintf(abspath
, PATH_MAX
, "%s", pathname
);
95 /* Concatenate the current working directory and pathname into an
96 * absolute path. We use realpath() ONLY on the cwd part, and not
97 * on the pathname part, because realpath() resolves symlinks. And
98 * the whole point of all this crap is to avoid following symlinks
101 * Using realpath() on the cwd lets us operate on relative paths
102 * while we're sitting in a directory that happens to have a
103 * symlink in it; for example: cd /var/run && apply-default-acl foo.
105 char* cwd
= get_current_dir_name();
107 perror("safe_open (get_current_dir_name)");
111 char abs_cwd
[PATH_MAX
];
112 if (realpath(cwd
, abs_cwd
) == NULL
) {
113 perror("safe_open (realpath)");
117 snprintf_result
= snprintf(abspath
, PATH_MAX
, "%s/%s", abs_cwd
, pathname
);
120 if (snprintf_result
== SNPRINTF_ERROR
|| snprintf_result
> PATH_MAX
) {
121 perror("safe_open (snprintf)");
125 int fd
= open("/", flags
);
126 if (strcmp(abspath
, "/") == 0) {
130 int result
= safe_open_ex(fd
, abspath
+1, flags
);
131 if (close(fd
) == CLOSE_ERROR
) {
132 perror("safe_open (close)");
141 * @brief Determine whether or not the given path is accessible.
146 * @return true if @c path is accessible to the current effective
147 * user/group, false otherwise.
149 bool path_accessible(const char* path
) {
154 /* Test for access using the effective user and group rather than
156 int flags
= AT_EACCESS
;
158 /* Don't follow symlinks when checking for a path's existence,
159 since we won't follow them to set its ACLs either. */
160 flags
|= AT_SYMLINK_NOFOLLOW
;
162 /* If the path is relative, interpret it relative to the current
163 working directory (just like the access() system call). */
164 if (faccessat(AT_FDCWD
, path
, F_OK
, flags
) == 0) {
175 * @brief Update (or create) an entry in an @b minimal ACL.
177 * This function will not work if @c aclp contains extended
178 * entries. This is fine for our purposes, since we call @c wipe_acls
179 * on each path before applying the default to it.
181 * The assumption that there are no extended entries makes things much
182 * simpler. For example, we only have to update the @c ACL_USER_OBJ,
183 * @c ACL_GROUP_OBJ, and @c ACL_OTHER entries -- all others can simply
184 * be created anew. This means we don't have to fool around comparing
185 * named-user/group entries.
188 * A pointer to the acl_t structure whose entry we want to modify.
191 * The new entry. If @c entry contains a user/group/other entry, we
192 * update the existing one. Otherwise we create a new entry.
194 * @return If there is an unexpected library error, @c ACL_ERROR is
195 * returned. Otherwise, @c ACL_SUCCESS.
198 int acl_set_entry(acl_t
* aclp
, acl_entry_t entry
) {
201 if (acl_get_tag_type(entry
, &entry_tag
) == ACL_ERROR
) {
202 perror("acl_set_entry (acl_get_tag_type)");
206 acl_permset_t entry_permset
;
207 if (acl_get_permset(entry
, &entry_permset
) == ACL_ERROR
) {
208 perror("acl_set_entry (acl_get_permset)");
212 acl_entry_t existing_entry
;
213 /* Loop through the given ACL looking for matching entries. */
214 int result
= acl_get_entry(*aclp
, ACL_FIRST_ENTRY
, &existing_entry
);
216 while (result
== ACL_SUCCESS
) {
217 acl_tag_t existing_tag
= ACL_UNDEFINED_TAG
;
219 if (acl_get_tag_type(existing_entry
, &existing_tag
) == ACL_ERROR
) {
220 perror("set_acl_tag_permset (acl_get_tag_type)");
224 if (existing_tag
== entry_tag
) {
225 if (entry_tag
== ACL_USER_OBJ
||
226 entry_tag
== ACL_GROUP_OBJ
||
227 entry_tag
== ACL_OTHER
) {
228 /* Only update for these three since all other tags will have
229 been wiped. These three are guaranteed to exist, so if we
230 match one of them, we're allowed to return ACL_SUCCESS
231 below and bypass the rest of the function. */
232 acl_permset_t existing_permset
;
233 if (acl_get_permset(existing_entry
, &existing_permset
) == ACL_ERROR
) {
234 perror("acl_set_entry (acl_get_permset)");
238 if (acl_set_permset(existing_entry
, entry_permset
) == ACL_ERROR
) {
239 perror("acl_set_entry (acl_set_permset)");
248 result
= acl_get_entry(*aclp
, ACL_NEXT_ENTRY
, &existing_entry
);
251 /* This catches both the initial acl_get_entry and the ones at the
253 if (result
== ACL_ERROR
) {
254 perror("acl_set_entry (acl_get_entry)");
258 /* If we've made it this far, we need to add a new entry to the
260 acl_entry_t new_entry
;
262 /* The acl_create_entry() function can allocate new memory and/or
263 * change the location of the ACL structure entirely. When that
264 * happens, the value pointed to by aclp is updated, which means
265 * that a new acl_t gets "passed out" to our caller, eventually to
266 * be fed to acl_free(). In other words, we should still be freeing
267 * the right thing, even if the value pointed to by aclp changes.
269 if (acl_create_entry(aclp
, &new_entry
) == ACL_ERROR
) {
270 perror("acl_set_entry (acl_create_entry)");
274 if (acl_set_tag_type(new_entry
, entry_tag
) == ACL_ERROR
) {
275 perror("acl_set_entry (acl_set_tag_type)");
279 if (acl_set_permset(new_entry
, entry_permset
) == ACL_ERROR
) {
280 perror("acl_set_entry (acl_set_permset)");
284 if (entry_tag
== ACL_USER
|| entry_tag
== ACL_GROUP
) {
285 /* We need to set the qualifier too. */
286 void* entry_qual
= acl_get_qualifier(entry
);
287 if (entry_qual
== (void*)NULL
) {
288 perror("acl_set_entry (acl_get_qualifier)");
292 if (acl_set_qualifier(new_entry
, entry_qual
) == ACL_ERROR
) {
293 perror("acl_set_entry (acl_set_qualifier)");
304 * @brief Determine the number of entries in the given ACL.
307 * The ACL to inspect.
309 * @return Either the non-negative number of entries in @c acl, or
310 * @c ACL_ERROR on error.
312 int acl_entry_count(acl_t acl
) {
316 int result
= acl_get_entry(acl
, ACL_FIRST_ENTRY
, &entry
);
318 while (result
== ACL_SUCCESS
) {
320 result
= acl_get_entry(acl
, ACL_NEXT_ENTRY
, &entry
);
323 if (result
== ACL_ERROR
) {
324 perror("acl_entry_count (acl_get_entry)");
334 * @brief Determine whether or not the given ACL is minimal.
336 * An ACL is minimal if it has fewer than four entries.
339 * The ACL whose minimality is in question.
342 * - @c ACL_SUCCESS - @c acl is minimal
343 * - @c ACL_FAILURE - @c acl is not minimal
344 * - @c ACL_ERROR - Unexpected library error
346 int acl_is_minimal(acl_t acl
) {
348 int ec
= acl_entry_count(acl
);
350 if (ec
== ACL_ERROR
) {
351 perror("acl_is_minimal (acl_entry_count)");
366 * @brief Determine whether the given ACL's mask denies execute.
369 * The ACL whose mask we want to check.
372 * - @c ACL_SUCCESS - The @c acl has a mask which denies execute.
373 * - @c ACL_FAILURE - The @c acl has a mask which does not deny execute.
374 * - @c ACL_ERROR - Unexpected library error.
376 int acl_execute_masked(acl_t acl
) {
379 int ge_result
= acl_get_entry(acl
, ACL_FIRST_ENTRY
, &entry
);
381 while (ge_result
== ACL_SUCCESS
) {
382 acl_tag_t tag
= ACL_UNDEFINED_TAG
;
384 if (acl_get_tag_type(entry
, &tag
) == ACL_ERROR
) {
385 perror("acl_execute_masked (acl_get_tag_type)");
389 if (tag
== ACL_MASK
) {
390 /* This is the mask entry, get its permissions, and see if
391 execute is specified. */
392 acl_permset_t permset
;
394 if (acl_get_permset(entry
, &permset
) == ACL_ERROR
) {
395 perror("acl_execute_masked (acl_get_permset)");
399 int gp_result
= acl_get_perm(permset
, ACL_EXECUTE
);
400 if (gp_result
== ACL_ERROR
) {
401 perror("acl_execute_masked (acl_get_perm)");
405 if (gp_result
== ACL_FAILURE
) {
406 /* No execute bit set in the mask; execute not allowed. */
411 ge_result
= acl_get_entry(acl
, ACL_NEXT_ENTRY
, &entry
);
420 * @brief Determine whether @c fd is executable by anyone.
423 * This is used as part of the heuristic to determine whether or not
424 * we should mask the execute bit when inheriting an ACL. If @c fd
425 * describes a file, we check the @a effective permissions, contrary
426 * to what setfacl does.
429 * The file descriptor to check.
432 * A pointer to a stat structure for @c fd.
435 * - @c ACL_SUCCESS - Someone has effective execute permissions on @c fd.
436 * - @c ACL_FAILURE - Nobody can execute @c fd.
437 * - @c ACL_ERROR - Unexpected library error.
439 int any_can_execute(int fd
, const struct stat
* sp
) {
440 acl_t acl
= acl_get_fd(fd
);
442 if (acl
== (acl_t
)NULL
) {
443 perror("any_can_execute (acl_get_file)");
447 /* Our return value. */
448 int result
= ACL_FAILURE
;
450 if (acl_is_minimal(acl
)) {
451 if (sp
->st_mode
& (S_IXUSR
| S_IXOTH
| S_IXGRP
)) {
452 result
= ACL_SUCCESS
;
456 result
= ACL_FAILURE
;
462 int ge_result
= acl_get_entry(acl
, ACL_FIRST_ENTRY
, &entry
);
464 while (ge_result
== ACL_SUCCESS
) {
465 /* The first thing we do is check to see if this is a mask
466 entry. If it is, we skip it entirely. */
467 acl_tag_t tag
= ACL_UNDEFINED_TAG
;
469 if (acl_get_tag_type(entry
, &tag
) == ACL_ERROR
) {
470 perror("any_can_execute_or (acl_get_tag_type)");
475 if (tag
== ACL_MASK
) {
476 ge_result
= acl_get_entry(acl
, ACL_NEXT_ENTRY
, &entry
);
480 /* Ok, so it's not a mask entry. Check the execute perms. */
481 acl_permset_t permset
;
483 if (acl_get_permset(entry
, &permset
) == ACL_ERROR
) {
484 perror("any_can_execute_or (acl_get_permset)");
489 int gp_result
= acl_get_perm(permset
, ACL_EXECUTE
);
490 if (gp_result
== ACL_ERROR
) {
491 perror("any_can_execute (acl_get_perm)");
496 if (gp_result
== ACL_SUCCESS
) {
497 /* Only return ACL_SUCCESS if this execute bit is not masked. */
498 if (acl_execute_masked(acl
) != ACL_SUCCESS
) {
499 result
= ACL_SUCCESS
;
504 ge_result
= acl_get_entry(acl
, ACL_NEXT_ENTRY
, &entry
);
507 if (ge_result
== ACL_ERROR
) {
508 perror("any_can_execute (acl_get_entry)");
521 * @brief Set @c acl as the default ACL on @c path.
523 * This overwrites any existing default ACL on @c path. If @c path is
524 * not a directory, we return ACL_ERROR and @c errno is set.
527 * The target directory whose ACL we wish to replace or create.
530 * The ACL to set as default on @c path.
533 * - @c ACL_SUCCESS - The default ACL was assigned successfully.
534 * - @c ACL_ERROR - Unexpected library error.
536 int assign_default_acl(const char* path
, acl_t acl
) {
540 perror("assign_default_acl (args)");
544 /* Our return value; success unless something bad happens. */
545 int result
= ACL_SUCCESS
;
546 acl_t path_acl
= acl_dup(acl
);
548 if (path_acl
== (acl_t
)NULL
) {
549 perror("assign_default_acl (acl_dup)");
550 return ACL_ERROR
; /* Nothing to clean up in this case. */
553 if (acl_set_file(path
, ACL_TYPE_DEFAULT
, path_acl
) == ACL_ERROR
) {
554 perror("assign_default_acl (acl_set_file)");
565 * @brief Remove all @c ACL_TYPE_ACCESS entries from the given file
566 * descriptor, leaving the UNIX permission bits.
569 * The file descriptor whose ACLs we want to wipe.
572 * - @c ACL_SUCCESS - The ACLs were wiped successfully, or none
573 * existed in the first place.
574 * - @c ACL_ERROR - Unexpected library error.
576 int wipe_acls(int fd
) {
577 /* Initialize an empty ACL, and then overwrite the one on "fd" with it. */
578 acl_t empty_acl
= acl_init(0);
580 if (empty_acl
== (acl_t
)NULL
) {
581 perror("wipe_acls (acl_init)");
585 if (acl_set_fd(fd
, empty_acl
) == ACL_ERROR
) {
586 perror("wipe_acls (acl_set_fd)");
598 * @brief Apply parent default ACL to a path.
600 * This overwrites any existing ACLs on @c path.
603 * The path whose ACL we would like to reset to its default.
606 * A pointer to a stat structure for @c path, or @c NULL if you don't
609 * @param no_exec_mask
610 * The value (either true or false) of the --no-exec-mask flag.
613 * - @c ACL_SUCCESS - The parent default ACL was inherited successfully.
614 * - @c ACL_FAILURE - The target path is not a regular file/directory,
615 * or the parent of @c path is not a directory.
616 * - @c ACL_ERROR - Unexpected library error.
618 int apply_default_acl(const char* path
,
619 const struct stat
* sp
,
624 perror("apply_default_acl (args)");
628 /* Define these next three variables here because we may have to
629 * jump to the cleanup routine which expects them to exist.
632 /* Our return value. */
633 int result
= ACL_SUCCESS
;
635 /* The default ACL on path's parent directory */
636 acl_t defacl
= (acl_t
)NULL
;
638 /* The file descriptor corresponding to "path" */
641 /* Get the parent directory of "path" with dirname(), which happens
642 * to murder its argument and necessitates a path_copy.
644 char* path_copy
= strdup(path
);
645 if (path_copy
== NULL
) {
646 perror("apply_default_acl (strdup)");
649 char* parent
= dirname(path_copy
);
651 fd
= safe_open(path
, O_NOFOLLOW
);
652 if (fd
== OPEN_ERROR
) {
653 if (errno
== ELOOP
) {
654 result
= ACL_FAILURE
; /* hit a symlink */
658 perror("apply_default_acl (open fd)");
665 /* Refuse to operate on hard links, which can be abused by an
666 * attacker to trick us into changing the ACL on a file we didn't
667 * intend to; namely the "target" of the hard link. There is TOCTOU
668 * race condition here, but the window is as small as possible
669 * between when we open the file descriptor (look above) and when we
672 * Note: we only need to call fstat ourselves if we weren't passed a
673 * valid pointer to a stat structure (nftw does that).
677 if (fstat(fd
, &s
) == STAT_ERROR
) {
678 perror("apply_default_acl (fstat)");
685 if (!S_ISDIR(sp
->st_mode
)) {
686 /* If it's not a directory, make sure it's a regular,
687 non-hard-linked file. */
688 if (!S_ISREG(sp
->st_mode
) || sp
->st_nlink
!= 1) {
689 result
= ACL_FAILURE
;
695 /* Default to not masking the exec bit; i.e. applying the default
696 ACL literally. If --no-exec-mask was not specified, then we try
697 to "guess" whether or not to mask the exec bit. This behavior
698 is modeled after the capital 'X' perms of setfacl. */
699 bool allow_exec
= true;
702 /* Never mask the execute bit on directories. */
703 int ace_result
= any_can_execute(fd
,sp
) || S_ISDIR(sp
->st_mode
);
705 if (ace_result
== ACL_ERROR
) {
706 perror("apply_default_acl (any_can_execute)");
711 allow_exec
= (bool)ace_result
;
714 defacl
= acl_get_file(parent
, ACL_TYPE_DEFAULT
);
716 if (defacl
== (acl_t
)NULL
) {
717 perror("apply_default_acl (acl_get_file)");
722 if (wipe_acls(fd
) == ACL_ERROR
) {
723 perror("apply_default_acl (wipe_acls)");
728 /* Do this after wipe_acls(), otherwise we'll overwrite the wiped
729 ACL with this one. */
730 acl_t acl
= acl_get_fd(fd
);
731 if (acl
== (acl_t
)NULL
) {
732 perror("apply_default_acl (acl_get_fd)");
737 /* If it's a directory, inherit the parent's default. We sure hope
738 * that "path" still points to the same thing that "fd" and this
739 * "sp" describe. If not, we may wind up trying to set a default ACL
740 * on a file, and this will throw an error. I guess that's what we
743 if (S_ISDIR(sp
->st_mode
) && assign_default_acl(path
, defacl
) == ACL_ERROR
) {
744 perror("apply_default_acl (assign_default_acl)");
750 int ge_result
= acl_get_entry(defacl
, ACL_FIRST_ENTRY
, &entry
);
752 while (ge_result
== ACL_SUCCESS
) {
753 acl_tag_t tag
= ACL_UNDEFINED_TAG
;
755 if (acl_get_tag_type(entry
, &tag
) == ACL_ERROR
) {
756 perror("apply_default_acl (acl_get_tag_type)");
762 /* We've got an entry/tag from the default ACL. Get its permset. */
763 acl_permset_t permset
;
764 if (acl_get_permset(entry
, &permset
) == ACL_ERROR
) {
765 perror("apply_default_acl (acl_get_permset)");
770 /* If this is a default mask, fix it up. */
771 if (tag
== ACL_MASK
||
772 tag
== ACL_USER_OBJ
||
773 tag
== ACL_GROUP_OBJ
||
777 /* The mask doesn't affect acl_user_obj, acl_group_obj (in
778 minimal ACLs) or acl_other entries, so if execute should be
779 masked, we have to do it manually. */
780 if (acl_delete_perm(permset
, ACL_EXECUTE
) == ACL_ERROR
) {
781 perror("apply_default_acl (acl_delete_perm)");
786 if (acl_set_permset(entry
, permset
) == ACL_ERROR
) {
787 perror("apply_default_acl (acl_set_permset)");
794 /* Finally, add the permset to the access ACL. It's actually
795 * important that we pass in the address of "acl" here, and not
796 * "acl" itself. Why? The call to acl_create_entry() within
797 * acl_set_entry() can allocate new memory for the entry.
798 * Sometimes that can be done in-place, in which case everything
799 * is cool and the new memory gets released when we call
802 * But occasionally, the whole ACL structure will have to be moved
803 * in order to allocate the extra space. When that happens,
804 * acl_create_entry() modifies the pointer it was passed (in this
805 * case, &acl) to point to the new location. We want to call
806 * acl_free() on the new location, and since acl_free() gets
807 * called right here, we need acl_create_entry() to update the
808 * value of "acl". To do that, it needs the address of "acl".
810 if (acl_set_entry(&acl
, entry
) == ACL_ERROR
) {
811 perror("apply_default_acl (acl_set_entry)");
816 ge_result
= acl_get_entry(defacl
, ACL_NEXT_ENTRY
, &entry
);
819 /* Catches the first acl_get_entry as well as the ones at the end of
821 if (ge_result
== ACL_ERROR
) {
822 perror("apply_default_acl (acl_get_entry)");
827 if (acl_set_fd(fd
, acl
) == ACL_ERROR
) {
828 perror("apply_default_acl (acl_set_fd)");
835 if (defacl
!= (acl_t
)NULL
) {
838 if (fd
>= 0 && close(fd
) == CLOSE_ERROR
) {
839 perror("apply_default_acl (close)");
848 * @brief Display program usage information.
850 * @param program_name
851 * The program name to use in the output.
854 void usage(const char* program_name
) {
855 printf("Apply any applicable default ACLs to the given files or "
857 printf("Usage: %s [flags] <target1> [<target2> [ <target3>...]]\n\n",
860 printf(" -h, --help Print this help message\n");
861 printf(" -r, --recursive Act on any given directories recursively\n");
862 printf(" -x, --no-exec-mask Apply execute permissions unconditionally\n");
869 * @brief Wrapper around @c apply_default_acl() for use with @c nftw().
871 * For parameter information, see the @c nftw man page.
873 * @return If the ACL was applied to @c target successfully, we return
874 * @c FTW_CONTINUE to signal to @ nftw() that we should proceed onto
875 * the next file or directory. Otherwise, we return @c FTW_STOP to
879 int apply_default_acl_nftw(const char *target
,
880 const struct stat
*sp
,
884 if (apply_default_acl(target
, sp
, false)) {
895 * @brief Wrapper around @c apply_default_acl() for use with @c nftw().
897 * This is identical to @c apply_default_acl_nftw(), except it passes
898 * @c true to @c apply_default_acl() as its no_exec_mask argument.
901 int apply_default_acl_nftw_x(const char *target
,
902 const struct stat
*sp
,
906 if (apply_default_acl(target
, sp
, true)) {
917 * @brief Recursive version of @c apply_default_acl().
919 * If @c target is a directory, we use @c nftw() to call @c
920 * apply_default_acl() recursively on all of its children. Otherwise,
921 * we just delegate to @c apply_default_acl().
923 * We ignore symlinks for consistency with chmod -r.
926 * The root (path) of the recursive application.
928 * @param no_exec_mask
929 * The value (either true or false) of the --no-exec-mask flag.
932 * If @c target is not a directory, we return the result of
933 * calling @c apply_default_acl() on @c target. Otherwise, we convert
934 * the return value of @c nftw(). If @c nftw() succeeds (returns 0),
935 * then we return @c true. Otherwise, we return @c false.
937 * If there is an error, it will be reported via @c perror, but
938 * we still return @c false.
940 bool apply_default_acl_recursive(const char *target
, bool no_exec_mask
) {
941 int max_levels
= 256;
942 int flags
= FTW_PHYS
; /* Don't follow links. */
944 /* There are two separate functions that could be passed to
945 nftw(). One passes no_exec_mask = true to apply_default_acl(),
946 and the other passes no_exec_mask = false. Since the function we
947 pass to nftw() cannot have parameters, we have to create separate
948 options and make the decision here. */
949 int (*fn
)(const char *, const struct stat
*, int, struct FTW
*) = NULL
;
950 fn
= no_exec_mask
? apply_default_acl_nftw_x
: apply_default_acl_nftw
;
952 int nftw_result
= nftw(target
, fn
, max_levels
, flags
);
954 if (nftw_result
== 0) {
959 /* nftw will return NFTW_ERROR on error, or if the supplied function
960 * (apply_default_acl_nftw) returns a non-zero result, nftw will
963 if (nftw_result
== NFTW_ERROR
) {
964 perror("apply_default_acl_recursive (nftw)");
973 * @brief Call apply_default_acl (possibly recursively) on each
974 * command-line argument.
976 * @return Either @c EXIT_FAILURE or @c EXIT_SUCCESS. If everything
977 * goes as expected, we return @c EXIT_SUCCESS. Otherwise, we return
980 int main(int argc
, char* argv
[]) {
987 bool recursive
= false;
988 bool no_exec_mask
= false;
990 struct option long_options
[] = {
991 /* These options set a flag. */
992 {"help", no_argument
, NULL
, 'h'},
993 {"recursive", no_argument
, NULL
, 'r'},
994 {"no-exec-mask", no_argument
, NULL
, 'x'},
1000 while ((opt
= getopt_long(argc
, argv
, "hrx", long_options
, NULL
)) != -1) {
1004 return EXIT_SUCCESS
;
1009 no_exec_mask
= true;
1013 return EXIT_FAILURE
;
1017 int result
= EXIT_SUCCESS
;
1020 for (arg_index
= optind
; arg_index
< argc
; arg_index
++) {
1021 const char* target
= argv
[arg_index
];
1022 bool reapp_result
= false;
1024 /* Make sure we can access the given path before we go out of our
1025 * way to please it. Doing this check outside of
1026 * apply_default_acl() lets us spit out a better error message for
1029 if (!path_accessible(target
)) {
1030 fprintf(stderr
, "%s: %s: No such file or directory\n", argv
[0], target
);
1031 result
= EXIT_FAILURE
;
1036 reapp_result
= apply_default_acl_recursive(target
, no_exec_mask
);
1039 /* It's either a normal file, or we're not operating recursively. */
1040 reapp_result
= apply_default_acl(target
, NULL
, no_exec_mask
);
1043 if (!reapp_result
) {
1044 result
= EXIT_FAILURE
;