]> gitweb.michael.orlitzky.com - apply-default-acl.git/blob - src/aclq.c
Add -Wall and fix warnings.
[apply-default-acl.git] / src / aclq.c
1 #include <errno.h>
2 #include <libgen.h> /* dirname() */
3 #include <limits.h> /* PATH_MAX */
4 #include <stdbool.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <sys/stat.h>
9 #include <unistd.h>
10
11 /* ACLs */
12 #include <acl/libacl.h> /* acl_get_perm, not portable */
13 #include <sys/types.h>
14 #include <sys/acl.h>
15
16
17 mode_t get_mode(const char* path) {
18 if (path == NULL) {
19 errno = ENOENT;
20 return -1;
21 }
22
23 struct stat s;
24 int result = stat(path, &s);
25
26 if (result == 0) {
27 return s.st_mode;
28 }
29 else {
30 /* errno will be set already by stat() */
31 return result;
32 }
33 }
34
35 bool mode_has_perm(mode_t mode, int perm) {
36 if (mode & perm) {
37 return true;
38 }
39 else {
40 return false;
41 }
42 }
43
44
45 bool is_regular_file(const char* path) {
46 if (path == NULL) {
47 return false;
48 }
49
50 struct stat s;
51 int result = stat(path, &s);
52 if (result == 0) {
53 return S_ISREG(s.st_mode);
54 }
55 else {
56 return false;
57 }
58 }
59
60 bool is_directory(const char* path) {
61 if (path == NULL) {
62 return false;
63 }
64
65 struct stat s;
66 int result = stat(path, &s);
67 if (result == 0) {
68 return S_ISDIR(s.st_mode);
69 }
70 else {
71 return false;
72 }
73 }
74
75
76 bool has_default_acl(const char* path) {
77 /* Return true if the given path has a default ACL, false
78 otherwise. */
79 acl_t defacl = acl_get_file(path, ACL_TYPE_DEFAULT);
80
81 if (defacl == (acl_t)NULL) {
82 return false;
83 }
84
85 /* Used to store the entry if it exists, even though we don't care
86 what it is. */
87 acl_entry_t dummy;
88
89 int result = acl_get_entry(defacl, ACL_FIRST_ENTRY, &dummy);
90
91 if (result == 1) {
92 /* There's a first entry in the default ACL. */
93 return true;
94 }
95 else if (result == 0) {
96 return false;
97 }
98 else {
99 perror("has_default_acl");
100 return false;
101 }
102 }
103
104
105
106 bool has_default_tag_acl(const char* path, acl_tag_t tag_type) {
107 /* Return true if the given path has a default ACL for the supplied
108 tag, false otherwise. */
109 acl_t defacl = acl_get_file(path, ACL_TYPE_DEFAULT);
110
111 if (defacl == (acl_t)NULL) {
112 return false;
113 }
114
115 acl_entry_t entry;
116 int result = acl_get_entry(defacl, ACL_FIRST_ENTRY, &entry);
117
118 while (result == 1) {
119 acl_tag_t tag = ACL_UNDEFINED_TAG;
120 int tag_result = acl_get_tag_type(entry, &tag);
121
122 if (tag_result == -1) {
123 perror("has_default_tag_acl - acl_get_tag_type");
124 return false;
125 }
126 else {
127 if (tag == tag_type) {
128 return true;
129 }
130 }
131
132 result = acl_get_entry(defacl, ACL_NEXT_ENTRY, &entry);
133 }
134
135 return false;
136 }
137
138
139 bool has_default_user_obj_acl(const char* path) {
140 return has_default_tag_acl(path, ACL_USER_OBJ);
141 }
142
143 bool has_default_group_obj_acl(const char* path) {
144 return has_default_tag_acl(path, ACL_GROUP_OBJ);
145 }
146
147 bool has_default_other_acl(const char* path) {
148 return has_default_tag_acl(path, ACL_OTHER);
149 }
150
151
152 bool get_default_tag_permset(const char* path,
153 acl_tag_t tag_type,
154 acl_permset_t* output_perms) {
155 /* Returns true if successful or false on error */
156 acl_t defacl = acl_get_file(path, ACL_TYPE_DEFAULT);
157
158 if (defacl == (acl_t)NULL) {
159 /* Follow the acl_foo convention of -1 == error. */
160 errno = EINVAL;
161 return false;
162 }
163
164 acl_entry_t entry;
165 int result = acl_get_entry(defacl, ACL_FIRST_ENTRY, &entry);
166
167 while (result == 1) {
168 acl_tag_t tag = ACL_UNDEFINED_TAG;
169 int tag_result = acl_get_tag_type(entry, &tag);
170
171 if (tag_result == -1) {
172 perror("get_default_tag_permset");
173 return false;
174 }
175 else {
176 if (tag == tag_type) {
177 /* We found the right tag, now get the permset. */
178 int ps_result = acl_get_permset(entry, output_perms);
179 if (ps_result == 0) {
180 return true;
181 }
182 else {
183 return false;
184 }
185 }
186 }
187
188 result = acl_get_entry(defacl, ACL_NEXT_ENTRY, &entry);
189 }
190
191 errno = EINVAL;
192 return false;
193 }
194
195 bool get_default_user_obj_permset(const char* path,
196 acl_permset_t* output_perms) {
197 return get_default_tag_permset(path, ACL_USER_OBJ, output_perms);
198 }
199
200 bool get_default_group_obj_permset(const char* path,
201 acl_permset_t* output_perms) {
202 return get_default_tag_permset(path, ACL_GROUP_OBJ, output_perms);
203 }
204
205 bool get_default_other_permset(const char* path,
206 acl_permset_t* output_perms) {
207 return get_default_tag_permset(path, ACL_OTHER, output_perms);
208 }
209
210
211
212 bool has_default_tag_perm(const char* path,
213 acl_tag_t tag,
214 acl_perm_t perm) {
215 /* Check path to see if tag has perm. */
216
217 if (!has_default_tag_acl(path, tag)) {
218 return false;
219 }
220
221 acl_permset_t permset;
222 bool ps_result = get_default_tag_permset(path, tag, &permset);
223
224 if (!ps_result) {
225 return false;
226 }
227
228 int p_result = acl_get_perm(permset, perm);
229 if (p_result == 1) {
230 return true;
231 }
232 else {
233 return false;
234 }
235 }
236
237 bool has_default_user_obj_read(const char* path) {
238 return has_default_tag_perm(path, ACL_USER_OBJ, ACL_READ);
239 }
240
241 bool has_default_user_obj_write(const char* path) {
242 return has_default_tag_perm(path, ACL_USER_OBJ, ACL_WRITE);
243 }
244
245 bool has_default_user_obj_execute(const char* path) {
246 return has_default_tag_perm(path, ACL_USER_OBJ, ACL_EXECUTE);
247 }
248
249 bool has_default_group_obj_read(const char* path) {
250 return has_default_tag_perm(path, ACL_GROUP_OBJ, ACL_READ);
251 }
252
253 bool has_default_group_obj_write(const char* path) {
254 return has_default_tag_perm(path, ACL_GROUP_OBJ, ACL_WRITE);
255 }
256
257 bool has_default_group_obj_execute(const char* path) {
258 return has_default_tag_perm(path, ACL_GROUP_OBJ, ACL_EXECUTE);
259 }
260
261 bool has_default_other_read(const char* path) {
262 return has_default_tag_perm(path, ACL_OTHER, ACL_READ);
263 }
264
265 bool has_default_other_write(const char* path) {
266 return has_default_tag_perm(path, ACL_OTHER, ACL_WRITE);
267 }
268
269 bool has_default_other_execute(const char* path) {
270 return has_default_tag_perm(path, ACL_OTHER, ACL_EXECUTE);
271 }
272
273 bool has_default_mask_read(const char* path) {
274 return has_default_tag_perm(path, ACL_MASK, ACL_READ);
275 }
276
277 bool has_default_mask_write(const char* path) {
278 return has_default_tag_perm(path, ACL_MASK, ACL_WRITE);
279 }
280
281 bool has_default_mask_execute(const char* path) {
282 return has_default_tag_perm(path, ACL_MASK, ACL_EXECUTE);
283 }
284
285
286 bool reapply_default_acl(const char* path) {
287 /* If this is a normal file or directory (i.e. that has just been
288 created), we proceed to find its parent directory which will have
289 a default ACL. */
290 if (path == NULL) {
291 return false;
292 }
293
294 if (!is_regular_file(path) && !is_directory(path)) {
295 return false;
296 }
297
298 /* dirname mangles its argument */
299 char path_copy[PATH_MAX];
300 strncpy(path_copy, path, PATH_MAX-1);
301 path_copy[PATH_MAX-1] = 0;
302
303 char* parent = dirname(path_copy);
304 if (!is_directory(parent)) {
305 /* Make sure dirname() did what we think it did. */
306 return false;
307 }
308
309 /* This is the original mode of path. We will simply add permissions
310 to it, and then later reapply the result via chmod. */
311 mode_t path_mode = get_mode(path);
312
313
314 /* If parent has a default user ACL, apply it to path via chmod. */
315 if (has_default_user_obj_read(parent)) {
316 path_mode |= S_IRUSR;
317 }
318
319 if (has_default_user_obj_write(parent)) {
320 path_mode |= S_IWUSR;
321 }
322
323 /* However, we don't want to set the execute bit on via the ACL
324 unless it was on originally. Furthermore, if the ACL denies
325 execute, we want to honor that. */
326 if (has_default_user_obj_acl(parent)) {
327 if (!has_default_user_obj_execute(parent)) {
328 /* It has a user ACL, but no user execute is granted. */
329 path_mode &= ~S_IXUSR;
330 }
331 }
332
333
334 /* Do the same thing with the other perms/ACL. */
335 if (has_default_other_read(parent)) {
336 path_mode |= S_IROTH;
337 }
338
339 if (has_default_other_write(parent)) {
340 path_mode |= S_IWOTH;
341 }
342
343 if (has_default_other_acl(parent)) {
344 if (!has_default_other_execute(parent)) {
345 /* It has an other ACL, but no other execute is granted. */
346 path_mode &= ~S_IXOTH;
347 }
348 }
349
350 /* For the group bits, we'll use the ACL's mask instead of the group
351 object bits. If the default ACL had a group entry, it should
352 already have propagated (but might be masked). */
353 if (has_default_mask_read(parent)) {
354 path_mode |= S_IRGRP;
355 }
356
357 if (has_default_mask_write(parent)) {
358 path_mode |= S_IWGRP;
359 }
360
361 /* Honor the mask execute unconditionally. */
362 if (has_default_mask_execute(parent)) {
363 if (mode_has_perm(path_mode, S_IXGRP)) {
364 path_mode |= S_IXGRP;
365 }
366 }
367
368 int result = chmod(path, path_mode);
369 if (result == 0) {
370 return true;
371 }
372 else {
373 return false;
374 }
375 }
376
377
378 int main(int argc, char* argv[]) {
379 const char* target = argv[1];
380
381 bool result = reapply_default_acl(target);
382
383 if (result) {
384 printf("Success.\n");
385 return EXIT_SUCCESS;
386 }
387 else {
388 return EXIT_FAILURE;
389 }
390 }