]> gitweb.michael.orlitzky.com - apply-default-acl.git/blob - src/apply-default-acl.c
Replace nftw() with manual recursion in apply_default_acl().
[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 #include <errno.h> /* EINVAL */
9 #include <fcntl.h> /* AT_FOO constants */
10 #include <getopt.h> /* getopt_long() */
11 #include <stdbool.h> /* the "bool" type */
12 #include <stdio.h> /* perror() */
13 #include <stdlib.h> /* EXIT_FAILURE, EXIT_SUCCESS */
14 #include <unistd.h> /* faccessat() */
15
16 #include "libadacl.h"
17
18 /* We exit with EXIT_FAILURE for small errors, but we need something
19 * else for big ones. */
20 #define EXIT_ERROR 2
21
22
23 /**
24 * @brief Determine whether or not the given path is accessible.
25 *
26 * @param path
27 * The path to test.
28 *
29 * @return true if @c path is accessible to the current effective
30 * user/group, false otherwise.
31 */
32 bool path_accessible(const char* path) {
33 if (path == NULL) {
34 return false;
35 }
36
37 /* Test for access using the effective user and group rather than
38 the real one. */
39 int flags = AT_EACCESS;
40
41 /* Don't follow symlinks when checking for a path's existence,
42 since we won't follow them to set its ACLs either. */
43 flags |= AT_SYMLINK_NOFOLLOW;
44
45 /* If the path is relative, interpret it relative to the current
46 working directory (just like the access() system call). */
47 if (faccessat(AT_FDCWD, path, F_OK, flags) == 0) {
48 return true;
49 }
50 else {
51 return false;
52 }
53 }
54
55
56 /**
57 * @brief Display program usage information.
58 *
59 * @param program_name
60 * The program name to use in the output.
61 *
62 */
63 void usage(const char* program_name) {
64 if (program_name == NULL) {
65 /* ??? */
66 return;
67 }
68
69 printf("Apply any applicable default ACLs to the given files or "
70 "directories.\n\n");
71 printf("Usage: %s [flags] <target1> [<target2> [ <target3>...]]\n\n",
72 program_name);
73 printf("Flags:\n");
74 printf(" -h, --help Print this help message\n");
75 printf(" -r, --recursive Act on any given directories recursively\n");
76 printf(" -x, --no-exec-mask Apply execute permissions unconditionally\n");
77
78 return;
79 }
80
81
82
83
84 /**
85 * @brief Call apply_default_acl (possibly recursively) on each
86 * command-line argument.
87 *
88 * @return Either @c EXIT_FAILURE or @c EXIT_SUCCESS. If everything
89 * goes as expected, we return @c EXIT_SUCCESS. Otherwise, we return
90 * @c EXIT_FAILURE.
91 */
92 int main(int argc, char* argv[]) {
93
94 if (argc < 2) {
95 usage(argv[0]);
96 return EXIT_FAILURE;
97 }
98
99 bool recursive = false;
100 bool no_exec_mask = false;
101
102 struct option long_options[] = {
103 /* These options set a flag. */
104 {"help", no_argument, NULL, 'h'},
105 {"recursive", no_argument, NULL, 'r'},
106 {"no-exec-mask", no_argument, NULL, 'x'},
107 {NULL, 0, NULL, 0}
108 };
109
110 int opt = 0;
111
112 while ((opt = getopt_long(argc, argv, "hrx", long_options, NULL)) != -1) {
113 switch (opt) {
114 case 'h':
115 usage(argv[0]);
116 return EXIT_SUCCESS;
117 case 'r':
118 recursive = true;
119 break;
120 case 'x':
121 no_exec_mask = true;
122 break;
123 default:
124 usage(argv[0]);
125 return EXIT_FAILURE;
126 }
127 }
128
129 int result = EXIT_SUCCESS;
130
131 int arg_index = 1;
132 int reapp_result = ACL_SUCCESS;
133 for (arg_index = optind; arg_index < argc; arg_index++) {
134 const char* target = argv[arg_index];
135
136 /* Make sure we can access the given path before we go out of our
137 * way to please it. Doing this check outside of
138 * apply_default_acl() lets us spit out a better error message for
139 * typos, too.
140 */
141 if (!path_accessible(target)) {
142 perror(target);
143 result = EXIT_FAILURE;
144 continue;
145 }
146
147 reapp_result = apply_default_acl(target, no_exec_mask, recursive);
148
149 if (result == EXIT_SUCCESS && reapp_result == ACL_FAILURE) {
150 /* We don't want to turn an error into a (less-severe) failure. */
151 result = EXIT_FAILURE;
152 }
153 if (reapp_result == ACL_ERROR) {
154 /* Turn both success and failure into an error, if we encounter one. */
155 result = EXIT_ERROR;
156 }
157 }
158
159 return result;
160 }