]> gitweb.michael.orlitzky.com - apply-default-acl.git/blob - src/apply-default-acl.c
Split most functions off into a separate shared library.
[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 <errno.h>
13 #include <fcntl.h> /* AT_FOO constants */
14 #include <ftw.h> /* nftw() et al. */
15 #include <getopt.h>
16 #include <stdbool.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <unistd.h>
20
21 #include "libadacl.h"
22
23
24
25 /**
26 * @brief Determine whether or not the given path is accessible.
27 *
28 * @param path
29 * The path to test.
30 *
31 * @return true if @c path is accessible to the current effective
32 * user/group, false otherwise.
33 */
34 bool path_accessible(const char* path) {
35 if (path == NULL) {
36 return false;
37 }
38
39 /* Test for access using the effective user and group rather than
40 the real one. */
41 int flags = AT_EACCESS;
42
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;
46
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) {
50 return true;
51 }
52 else {
53 return false;
54 }
55 }
56
57
58 /**
59 * @brief Display program usage information.
60 *
61 * @param program_name
62 * The program name to use in the output.
63 *
64 */
65 void usage(const char* program_name) {
66 printf("Apply any applicable default ACLs to the given files or "
67 "directories.\n\n");
68 printf("Usage: %s [flags] <target1> [<target2> [ <target3>...]]\n\n",
69 program_name);
70 printf("Flags:\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");
74
75 return;
76 }
77
78
79 /**
80 * @brief Wrapper around @c apply_default_acl() for use with @c nftw().
81 *
82 * For parameter information, see the @c nftw man page.
83 *
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
87 * signal failure.
88 *
89 */
90 int apply_default_acl_nftw(const char *target,
91 const struct stat *sp,
92 int info,
93 struct FTW *ftw) {
94
95 if (apply_default_acl(target, sp, false)) {
96 return FTW_CONTINUE;
97 }
98 else {
99 return FTW_STOP;
100 }
101 }
102
103
104
105 /**
106 * @brief Wrapper around @c apply_default_acl() for use with @c nftw().
107 *
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.
110 *
111 */
112 int apply_default_acl_nftw_x(const char *target,
113 const struct stat *sp,
114 int info,
115 struct FTW *ftw) {
116
117 if (apply_default_acl(target, sp, true)) {
118 return FTW_CONTINUE;
119 }
120 else {
121 return FTW_STOP;
122 }
123 }
124
125
126
127 /**
128 * @brief Recursive version of @c apply_default_acl().
129 *
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().
133 *
134 * We ignore symlinks for consistency with chmod -r.
135 *
136 * @param target
137 * The root (path) of the recursive application.
138 *
139 * @param no_exec_mask
140 * The value (either true or false) of the --no-exec-mask flag.
141 *
142 * @return
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.
147 * \n\n
148 * If there is an error, it will be reported via @c perror, but
149 * we still return @c false.
150 */
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. */
154
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;
162
163 int nftw_result = nftw(target, fn, max_levels, flags);
164
165 if (nftw_result == 0) {
166 /* Success */
167 return true;
168 }
169
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
172 * return that.
173 */
174 if (nftw_result == NFTW_ERROR) {
175 perror("apply_default_acl_recursive (nftw)");
176 }
177
178 return false;
179 }
180
181
182
183 /**
184 * @brief Call apply_default_acl (possibly recursively) on each
185 * command-line argument.
186 *
187 * @return Either @c EXIT_FAILURE or @c EXIT_SUCCESS. If everything
188 * goes as expected, we return @c EXIT_SUCCESS. Otherwise, we return
189 * @c EXIT_FAILURE.
190 */
191 int main(int argc, char* argv[]) {
192
193 if (argc < 2) {
194 usage(argv[0]);
195 return EXIT_FAILURE;
196 }
197
198 bool recursive = false;
199 bool no_exec_mask = false;
200
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'},
206 {NULL, 0, NULL, 0}
207 };
208
209 int opt = 0;
210
211 while ((opt = getopt_long(argc, argv, "hrx", long_options, NULL)) != -1) {
212 switch (opt) {
213 case 'h':
214 usage(argv[0]);
215 return EXIT_SUCCESS;
216 case 'r':
217 recursive = true;
218 break;
219 case 'x':
220 no_exec_mask = true;
221 break;
222 default:
223 usage(argv[0]);
224 return EXIT_FAILURE;
225 }
226 }
227
228 int result = EXIT_SUCCESS;
229
230 int arg_index = 1;
231 for (arg_index = optind; arg_index < argc; arg_index++) {
232 const char* target = argv[arg_index];
233 bool reapp_result = false;
234
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
238 * typos, too.
239 */
240 if (!path_accessible(target)) {
241 fprintf(stderr, "%s: %s: No such file or directory\n", argv[0], target);
242 result = EXIT_FAILURE;
243 continue;
244 }
245
246 if (recursive) {
247 reapp_result = apply_default_acl_recursive(target, no_exec_mask);
248 }
249 else {
250 /* It's either a normal file, or we're not operating recursively. */
251 reapp_result = apply_default_acl(target, NULL, no_exec_mask);
252 }
253
254 if (!reapp_result) {
255 result = EXIT_FAILURE;
256 }
257 }
258
259 return result;
260 }