]> gitweb.michael.orlitzky.com - apply-default-acl.git/blob - src/apply-default-acl.c
Rename apply_default_acl() to apply_default_acl_ex() and add a wrapper.
[apply-default-acl.git] / src / apply-default-acl.c
1 /**
2 * @file apply-default-acl.c
3 *
4 * @brief The command-line interface.
5 *
6 */
7
8 /* On Linux, ftw.h needs this special voodoo to work. */
9 #define _XOPEN_SOURCE 500
10 #define _GNU_SOURCE
11
12 #include <fcntl.h> /* AT_FOO constants */
13 #include <ftw.h> /* nftw() et al. */
14 #include <getopt.h> /* getopt_long() */
15 #include <stdbool.h> /* the "bool" type */
16 #include <stdio.h> /* perror() */
17 #include <stdlib.h> /* EXIT_FAILURE, EXIT_SUCCESS */
18 #include <unistd.h> /* faccessat() */
19
20 #include "libadacl.h"
21
22
23
24 /**
25 * @brief Determine whether or not the given path is accessible.
26 *
27 * @param path
28 * The path to test.
29 *
30 * @return true if @c path is accessible to the current effective
31 * user/group, false otherwise.
32 */
33 bool path_accessible(const char* path) {
34 if (path == NULL) {
35 return false;
36 }
37
38 /* Test for access using the effective user and group rather than
39 the real one. */
40 int flags = AT_EACCESS;
41
42 /* Don't follow symlinks when checking for a path's existence,
43 since we won't follow them to set its ACLs either. */
44 flags |= AT_SYMLINK_NOFOLLOW;
45
46 /* If the path is relative, interpret it relative to the current
47 working directory (just like the access() system call). */
48 if (faccessat(AT_FDCWD, path, F_OK, flags) == 0) {
49 return true;
50 }
51 else {
52 return false;
53 }
54 }
55
56
57 /**
58 * @brief Display program usage information.
59 *
60 * @param program_name
61 * The program name to use in the output.
62 *
63 */
64 void usage(const char* program_name) {
65 printf("Apply any applicable default ACLs to the given files or "
66 "directories.\n\n");
67 printf("Usage: %s [flags] <target1> [<target2> [ <target3>...]]\n\n",
68 program_name);
69 printf("Flags:\n");
70 printf(" -h, --help Print this help message\n");
71 printf(" -r, --recursive Act on any given directories recursively\n");
72 printf(" -x, --no-exec-mask Apply execute permissions unconditionally\n");
73
74 return;
75 }
76
77
78 /**
79 * @brief Wrapper around @c apply_default_acl() for use with @c nftw().
80 *
81 * For parameter information, see the @c nftw man page.
82 *
83 * @return If the ACL was applied to @c target successfully, we return
84 * @c FTW_CONTINUE to signal to @ nftw() that we should proceed onto
85 * the next file or directory. Otherwise, we return @c FTW_STOP to
86 * signal failure.
87 *
88 */
89 int apply_default_acl_nftw(const char *target,
90 const struct stat *sp,
91 int info,
92 struct FTW *ftw) {
93
94 if (apply_default_acl_ex(target, sp, false)) {
95 return FTW_CONTINUE;
96 }
97 else {
98 return FTW_STOP;
99 }
100 }
101
102
103
104 /**
105 * @brief Wrapper around @c apply_default_acl() for use with @c nftw().
106 *
107 * This is identical to @c apply_default_acl_nftw(), except it passes
108 * @c true to @c apply_default_acl() as its no_exec_mask argument.
109 *
110 */
111 int apply_default_acl_nftw_x(const char *target,
112 const struct stat *sp,
113 int info,
114 struct FTW *ftw) {
115
116 if (apply_default_acl_ex(target, sp, true)) {
117 return FTW_CONTINUE;
118 }
119 else {
120 return FTW_STOP;
121 }
122 }
123
124
125
126 /**
127 * @brief Recursive version of @c apply_default_acl().
128 *
129 * If @c target is a directory, we use @c nftw() to call @c
130 * apply_default_acl() recursively on all of its children. Otherwise,
131 * we just delegate to @c apply_default_acl().
132 *
133 * We ignore symlinks for consistency with chmod -r.
134 *
135 * @param target
136 * The root (path) of the recursive application.
137 *
138 * @param no_exec_mask
139 * The value (either true or false) of the --no-exec-mask flag.
140 *
141 * @return
142 * If @c target is not a directory, we return the result of
143 * calling @c apply_default_acl() on @c target. Otherwise, we convert
144 * the return value of @c nftw(). If @c nftw() succeeds (returns 0),
145 * then we return @c true. Otherwise, we return @c false.
146 * \n\n
147 * If there is an error, it will be reported via @c perror, but
148 * we still return @c false.
149 */
150 bool apply_default_acl_recursive(const char *target, bool no_exec_mask) {
151 int max_levels = 256;
152 int flags = FTW_PHYS; /* Don't follow links. */
153
154 /* There are two separate functions that could be passed to
155 nftw(). One passes no_exec_mask = true to apply_default_acl(),
156 and the other passes no_exec_mask = false. Since the function we
157 pass to nftw() cannot have parameters, we have to create separate
158 options and make the decision here. */
159 int (*fn)(const char *, const struct stat *, int, struct FTW *) = NULL;
160 fn = no_exec_mask ? apply_default_acl_nftw_x : apply_default_acl_nftw;
161
162 int nftw_result = nftw(target, fn, max_levels, flags);
163
164 if (nftw_result == 0) {
165 /* Success */
166 return true;
167 }
168
169 /* nftw will return NFTW_ERROR on error, or if the supplied function
170 * (apply_default_acl_nftw) returns a non-zero result, nftw will
171 * return that.
172 */
173 if (nftw_result == NFTW_ERROR) {
174 perror("apply_default_acl_recursive (nftw)");
175 }
176
177 return false;
178 }
179
180
181
182 /**
183 * @brief Call apply_default_acl (possibly recursively) on each
184 * command-line argument.
185 *
186 * @return Either @c EXIT_FAILURE or @c EXIT_SUCCESS. If everything
187 * goes as expected, we return @c EXIT_SUCCESS. Otherwise, we return
188 * @c EXIT_FAILURE.
189 */
190 int main(int argc, char* argv[]) {
191
192 if (argc < 2) {
193 usage(argv[0]);
194 return EXIT_FAILURE;
195 }
196
197 bool recursive = false;
198 bool no_exec_mask = false;
199
200 struct option long_options[] = {
201 /* These options set a flag. */
202 {"help", no_argument, NULL, 'h'},
203 {"recursive", no_argument, NULL, 'r'},
204 {"no-exec-mask", no_argument, NULL, 'x'},
205 {NULL, 0, NULL, 0}
206 };
207
208 int opt = 0;
209
210 while ((opt = getopt_long(argc, argv, "hrx", long_options, NULL)) != -1) {
211 switch (opt) {
212 case 'h':
213 usage(argv[0]);
214 return EXIT_SUCCESS;
215 case 'r':
216 recursive = true;
217 break;
218 case 'x':
219 no_exec_mask = true;
220 break;
221 default:
222 usage(argv[0]);
223 return EXIT_FAILURE;
224 }
225 }
226
227 int result = EXIT_SUCCESS;
228
229 int arg_index = 1;
230 for (arg_index = optind; arg_index < argc; arg_index++) {
231 const char* target = argv[arg_index];
232 bool reapp_result = false;
233
234 /* Make sure we can access the given path before we go out of our
235 * way to please it. Doing this check outside of
236 * apply_default_acl() lets us spit out a better error message for
237 * typos, too.
238 */
239 if (!path_accessible(target)) {
240 fprintf(stderr, "%s: %s: No such file or directory\n", argv[0], target);
241 result = EXIT_FAILURE;
242 continue;
243 }
244
245 if (recursive) {
246 reapp_result = apply_default_acl_recursive(target, no_exec_mask);
247 }
248 else {
249 /* It's either a normal file, or we're not operating recursively. */
250 reapp_result = apply_default_acl(target, no_exec_mask);
251 }
252
253 if (!reapp_result) {
254 result = EXIT_FAILURE;
255 }
256 }
257
258 return result;
259 }