]>
gitweb.michael.orlitzky.com - apply-default-acl.git/blob - src/apply-default-acl.c
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
13 #include <fcntl.h> /* AT_FOO constants */
14 #include <ftw.h> /* nftw() et al. */
26 * @brief Determine whether or not the given path is accessible.
31 * @return true if @c path is accessible to the current effective
32 * user/group, false otherwise.
34 bool path_accessible(const char* path
) {
39 /* Test for access using the effective user and group rather than
41 int flags
= AT_EACCESS
;
43 /* Don't follow symlinks when checking for a path's existence,
44 since we won't follow them to set its ACLs either. */
45 flags
|= AT_SYMLINK_NOFOLLOW
;
47 /* If the path is relative, interpret it relative to the current
48 working directory (just like the access() system call). */
49 if (faccessat(AT_FDCWD
, path
, F_OK
, flags
) == 0) {
59 * @brief Display program usage information.
62 * The program name to use in the output.
65 void usage(const char* program_name
) {
66 printf("Apply any applicable default ACLs to the given files or "
68 printf("Usage: %s [flags] <target1> [<target2> [ <target3>...]]\n\n",
71 printf(" -h, --help Print this help message\n");
72 printf(" -r, --recursive Act on any given directories recursively\n");
73 printf(" -x, --no-exec-mask Apply execute permissions unconditionally\n");
80 * @brief Wrapper around @c apply_default_acl() for use with @c nftw().
82 * For parameter information, see the @c nftw man page.
84 * @return If the ACL was applied to @c target successfully, we return
85 * @c FTW_CONTINUE to signal to @ nftw() that we should proceed onto
86 * the next file or directory. Otherwise, we return @c FTW_STOP to
90 int apply_default_acl_nftw(const char *target
,
91 const struct stat
*sp
,
95 if (apply_default_acl(target
, sp
, false)) {
106 * @brief Wrapper around @c apply_default_acl() for use with @c nftw().
108 * This is identical to @c apply_default_acl_nftw(), except it passes
109 * @c true to @c apply_default_acl() as its no_exec_mask argument.
112 int apply_default_acl_nftw_x(const char *target
,
113 const struct stat
*sp
,
117 if (apply_default_acl(target
, sp
, true)) {
128 * @brief Recursive version of @c apply_default_acl().
130 * If @c target is a directory, we use @c nftw() to call @c
131 * apply_default_acl() recursively on all of its children. Otherwise,
132 * we just delegate to @c apply_default_acl().
134 * We ignore symlinks for consistency with chmod -r.
137 * The root (path) of the recursive application.
139 * @param no_exec_mask
140 * The value (either true or false) of the --no-exec-mask flag.
143 * If @c target is not a directory, we return the result of
144 * calling @c apply_default_acl() on @c target. Otherwise, we convert
145 * the return value of @c nftw(). If @c nftw() succeeds (returns 0),
146 * then we return @c true. Otherwise, we return @c false.
148 * If there is an error, it will be reported via @c perror, but
149 * we still return @c false.
151 bool apply_default_acl_recursive(const char *target
, bool no_exec_mask
) {
152 int max_levels
= 256;
153 int flags
= FTW_PHYS
; /* Don't follow links. */
155 /* There are two separate functions that could be passed to
156 nftw(). One passes no_exec_mask = true to apply_default_acl(),
157 and the other passes no_exec_mask = false. Since the function we
158 pass to nftw() cannot have parameters, we have to create separate
159 options and make the decision here. */
160 int (*fn
)(const char *, const struct stat
*, int, struct FTW
*) = NULL
;
161 fn
= no_exec_mask
? apply_default_acl_nftw_x
: apply_default_acl_nftw
;
163 int nftw_result
= nftw(target
, fn
, max_levels
, flags
);
165 if (nftw_result
== 0) {
170 /* nftw will return NFTW_ERROR on error, or if the supplied function
171 * (apply_default_acl_nftw) returns a non-zero result, nftw will
174 if (nftw_result
== NFTW_ERROR
) {
175 perror("apply_default_acl_recursive (nftw)");
184 * @brief Call apply_default_acl (possibly recursively) on each
185 * command-line argument.
187 * @return Either @c EXIT_FAILURE or @c EXIT_SUCCESS. If everything
188 * goes as expected, we return @c EXIT_SUCCESS. Otherwise, we return
191 int main(int argc
, char* argv
[]) {
198 bool recursive
= false;
199 bool no_exec_mask
= false;
201 struct option long_options
[] = {
202 /* These options set a flag. */
203 {"help", no_argument
, NULL
, 'h'},
204 {"recursive", no_argument
, NULL
, 'r'},
205 {"no-exec-mask", no_argument
, NULL
, 'x'},
211 while ((opt
= getopt_long(argc
, argv
, "hrx", long_options
, NULL
)) != -1) {
228 int result
= EXIT_SUCCESS
;
231 for (arg_index
= optind
; arg_index
< argc
; arg_index
++) {
232 const char* target
= argv
[arg_index
];
233 bool reapp_result
= false;
235 /* Make sure we can access the given path before we go out of our
236 * way to please it. Doing this check outside of
237 * apply_default_acl() lets us spit out a better error message for
240 if (!path_accessible(target
)) {
241 fprintf(stderr
, "%s: %s: No such file or directory\n", argv
[0], target
);
242 result
= EXIT_FAILURE
;
247 reapp_result
= apply_default_acl_recursive(target
, no_exec_mask
);
250 /* It's either a normal file, or we're not operating recursively. */
251 reapp_result
= apply_default_acl(target
, NULL
, no_exec_mask
);
255 result
= EXIT_FAILURE
;