2 * @file apply-default-acl.c
4 * @brief The command-line interface.
8 /* On Linux, ftw.h needs this special voodoo to work. */
9 #define _XOPEN_SOURCE 500
12 #include <errno.h> /* EINVAL */
13 #include <fcntl.h> /* AT_FOO constants */
14 #include <ftw.h> /* nftw() et al. */
15 #include <getopt.h> /* getopt_long() */
16 #include <stdbool.h> /* the "bool" type */
17 #include <stdio.h> /* perror() */
18 #include <stdlib.h> /* EXIT_FAILURE, EXIT_SUCCESS */
19 #include <unistd.h> /* faccessat() */
23 /* We exit with EXIT_FAILURE for small errors, but we need something
24 * else for big ones. */
31 * @brief Determine whether or not the given path is accessible.
36 * @return true if @c path is accessible to the current effective
37 * user/group, false otherwise.
39 bool path_accessible(const char* path
) {
44 /* Test for access using the effective user and group rather than
46 int flags
= AT_EACCESS
;
48 /* Don't follow symlinks when checking for a path's existence,
49 since we won't follow them to set its ACLs either. */
50 flags
|= AT_SYMLINK_NOFOLLOW
;
52 /* If the path is relative, interpret it relative to the current
53 working directory (just like the access() system call). */
54 if (faccessat(AT_FDCWD
, path
, F_OK
, flags
) == 0) {
64 * @brief Display program usage information.
67 * The program name to use in the output.
70 void usage(const char* program_name
) {
71 if (program_name
== NULL
) {
76 printf("Apply any applicable default ACLs to the given files or "
78 printf("Usage: %s [flags] <target1> [<target2> [ <target3>...]]\n\n",
81 printf(" -h, --help Print this help message\n");
82 printf(" -r, --recursive Act on any given directories recursively\n");
83 printf(" -x, --no-exec-mask Apply execute permissions unconditionally\n");
90 * @brief Wrapper around @c apply_default_acl() for use with @c nftw().
92 * For parameter information, see the @c nftw man page.
94 * @return If the ACL was applied to @c target successfully, we return
95 * @c FTW_CONTINUE to signal to @ nftw() that we should proceed onto
96 * the next file or directory. Otherwise, we return @c FTW_STOP to
100 int apply_default_acl_nftw(const char *target
,
101 const struct stat
*sp
,
105 if (target
== NULL
) {
107 perror("apply_default_acl_nftw (args)");
112 if (apply_default_acl_ex(target
, sp
, false) == ACL_ERROR
) {
113 /* I guess we do want to bail out for serious/unexpected errors? */
117 /* We don't want to kill the tree walk because we it a symlink. */
124 * @brief Wrapper around @c apply_default_acl() for use with @c nftw().
126 * This is identical to @c apply_default_acl_nftw(), except it passes
127 * @c true to @c apply_default_acl() as its no_exec_mask argument.
130 int apply_default_acl_nftw_x(const char *target
,
131 const struct stat
*sp
,
135 if (target
== NULL
) {
137 perror("apply_default_acl_nftw_x (args)");
141 if (apply_default_acl_ex(target
, sp
, true) == ACL_ERROR
) {
142 /* I guess we do want to bail out for serious/unexpected errors? */
146 /* We don't want to kill the tree walk because we it a symlink. */
153 * @brief Recursive version of @c apply_default_acl().
155 * If @c target is a directory, we use @c nftw() to call @c
156 * apply_default_acl() recursively on all of its children. Otherwise,
157 * we just delegate to @c apply_default_acl().
160 * The root (path) of the recursive application.
162 * @param no_exec_mask
163 * The value (either true or false) of the --no-exec-mask flag.
166 * If @c nftw() fails with a serious error (returns NFTW_ERROR),
167 * then we return @c ACL_ERROR. Otherwise, we return @c ACL_SUCCESS.
169 int apply_default_acl_recursive(const char *target
, bool no_exec_mask
) {
170 if (target
== NULL
) {
172 perror("apply_default_acl_recursive (args)");
176 int max_levels
= 256;
177 int flags
= FTW_MOUNT
| FTW_PHYS
;
179 /* There are two separate functions that could be passed to
180 nftw(). One passes no_exec_mask = true to apply_default_acl(),
181 and the other passes no_exec_mask = false. Since the function we
182 pass to nftw() cannot have parameters, we have to create separate
183 options and make the decision here. */
184 int (*fn
)(const char *, const struct stat
*, int, struct FTW
*) = NULL
;
185 fn
= no_exec_mask
? apply_default_acl_nftw_x
: apply_default_acl_nftw
;
187 int nftw_result
= nftw(target
, fn
, max_levels
, flags
);
189 /* nftw will itself return NFTW_ERROR on errors like malloc failure,
190 and since the only non-success value that "fn" can return us
191 ACL_ERROR == NFTW_ERROR, this covers all error cases. */
192 if (nftw_result
== NFTW_ERROR
) {
193 perror("apply_default_acl_recursive (nftw)");
197 /* Beware: nftw indicates success with 0, but ACL_SUCCESS != 0. */
204 * @brief Call apply_default_acl (possibly recursively) on each
205 * command-line argument.
207 * @return Either @c EXIT_FAILURE or @c EXIT_SUCCESS. If everything
208 * goes as expected, we return @c EXIT_SUCCESS. Otherwise, we return
211 int main(int argc
, char* argv
[]) {
218 bool recursive
= false;
219 bool no_exec_mask
= false;
221 struct option long_options
[] = {
222 /* These options set a flag. */
223 {"help", no_argument
, NULL
, 'h'},
224 {"recursive", no_argument
, NULL
, 'r'},
225 {"no-exec-mask", no_argument
, NULL
, 'x'},
231 while ((opt
= getopt_long(argc
, argv
, "hrx", long_options
, NULL
)) != -1) {
248 int result
= EXIT_SUCCESS
;
251 for (arg_index
= optind
; arg_index
< argc
; arg_index
++) {
252 const char* target
= argv
[arg_index
];
254 /* Make sure we can access the given path before we go out of our
255 * way to please it. Doing this check outside of
256 * apply_default_acl() lets us spit out a better error message for
259 if (!path_accessible(target
)) {
261 result
= EXIT_FAILURE
;
265 int (*f
)(const char *, bool) = recursive
? apply_default_acl_recursive
267 int reapp_result
= f(target
, no_exec_mask
);
269 if (result
== EXIT_SUCCESS
&& reapp_result
== ACL_FAILURE
) {
270 /* We don't want to turn an error into a (less-severe) failure. */
271 result
= EXIT_FAILURE
;
273 if (reapp_result
== ACL_ERROR
) {
274 /* Turn both success and failure into an error, if we encounter one. */