]> gitweb.michael.orlitzky.com - apply-default-acl.git/blob - src/apply-default-acl.c
9c48b977d77adc90eb59306d12ed2549c24e6348
[apply-default-acl.git] / src / apply-default-acl.c
1 /**
2 * @file apply-default-acl.c
3 *
4 * @brief The entire implementation.
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 <ftw.h> /* nftw() et al. */
14 #include <getopt.h>
15 #include <libgen.h> /* dirname() */
16 #include <limits.h> /* PATH_MAX */
17 #include <stdbool.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <sys/stat.h>
22 #include <unistd.h>
23
24 /* ACLs */
25 #include <acl/libacl.h> /* acl_get_perm, not portable */
26 #include <sys/types.h>
27 #include <sys/acl.h>
28
29 /* Most of the libacl functions return 1 for success, 0 for failure,
30 and -1 on error */
31 #define ACL_ERROR -1
32 #define ACL_FAILURE 0
33 #define ACL_SUCCESS 1
34
35
36 /* Command-line options */
37 static bool no_exec_mask = false;
38
39
40
41 /**
42 * @brief Get the mode bits from the given path.
43 *
44 * @param path
45 * The path (file or directory) whose mode we want.
46 *
47 * @return A mode_t (st_mode) structure containing the mode bits.
48 * See sys/stat.h for details.
49 */
50 mode_t get_mode(const char* path) {
51 if (path == NULL) {
52 errno = ENOENT;
53 return -1;
54 }
55
56 struct stat s;
57 int result = stat(path, &s);
58
59 if (result == 0) {
60 return s.st_mode;
61 }
62 else {
63 /* errno will be set already by stat() */
64 return result;
65 }
66 }
67
68
69
70 /**
71 * @brief Determine whether or not the given path is a regular file.
72 *
73 * @param path
74 * The path to test.
75 *
76 * @return true if @c path is a regular file, false otherwise.
77 */
78 bool is_regular_file(const char* path) {
79 if (path == NULL) {
80 return false;
81 }
82
83 struct stat s;
84 int result = stat(path, &s);
85 if (result == 0) {
86 return S_ISREG(s.st_mode);
87 }
88 else {
89 return false;
90 }
91 }
92
93
94
95 /**
96 * @brief Determine whether or not the given path is a directory.
97 *
98 * @param path
99 * The path to test.
100 *
101 * @return true if @c path is a directory, false otherwise.
102 */
103 bool is_directory(const char* path) {
104 if (path == NULL) {
105 return false;
106 }
107
108 struct stat s;
109 int result = stat(path, &s);
110 if (result == 0) {
111 return S_ISDIR(s.st_mode);
112 }
113 else {
114 return false;
115 }
116 }
117
118
119
120 /**
121 * @brief Update (or create) an entry in an @b minimal ACL.
122 *
123 * This function will not work if @c aclp contains extended
124 * entries. This is fine for our purposes, since we call @c wipe_acls
125 * on each path before applying the default to it.
126 *
127 * The assumption that there are no extended entries makes things much
128 * simpler. For example, we only have to update the ACL_USER_OBJ,
129 * ACL_GROUP_OBJ, and ACL_OTHER entries -- all others can simply be
130 * created anew. This means we don't have to fool around comparing
131 * named-user/group entries.
132 *
133 * @param aclp
134 * A pointer to the acl_t structure whose entry we want to modify.
135 *
136 * @param entry
137 * The new entry. If @c entry contains a user/group/other entry, we
138 * update the existing one. Otherwise we create a new entry.
139 *
140 * @return If there is an unexpected library error, ACL_ERROR is
141 * returned. Otherwise, ACL_SUCCESS.
142 *
143 */
144 int acl_set_entry(acl_t* aclp,
145 acl_entry_t entry) {
146
147 acl_tag_t entry_tag;
148 int gt_result = acl_get_tag_type(entry, &entry_tag);
149 if (gt_result == ACL_ERROR) {
150 perror("acl_set_entry (acl_get_tag_type)");
151 return ACL_ERROR;
152 }
153
154 acl_permset_t entry_permset;
155 int ps_result = acl_get_permset(entry, &entry_permset);
156 if (ps_result == ACL_ERROR) {
157 perror("acl_set_entry (acl_get_permset)");
158 return ACL_ERROR;
159 }
160
161 acl_entry_t existing_entry;
162 /* Loop through the given ACL looking for matching entries. */
163 int result = acl_get_entry(*aclp, ACL_FIRST_ENTRY, &existing_entry);
164
165 while (result == ACL_SUCCESS) {
166 acl_tag_t existing_tag = ACL_UNDEFINED_TAG;
167 int tag_result = acl_get_tag_type(existing_entry, &existing_tag);
168
169 if (tag_result == ACL_ERROR) {
170 perror("set_acl_tag_permset (acl_get_tag_type)");
171 return ACL_ERROR;
172 }
173
174 if (existing_tag == entry_tag) {
175 if (entry_tag == ACL_USER_OBJ ||
176 entry_tag == ACL_GROUP_OBJ ||
177 entry_tag == ACL_OTHER) {
178 /* Only update for these three since all other tags will have
179 been wiped. These three are guaranteed to exist, so if we
180 match one of them, we're allowed to return ACL_SUCCESS
181 below and bypass the rest of the function. */
182 acl_permset_t existing_permset;
183 int gep_result = acl_get_permset(existing_entry, &existing_permset);
184 if (gep_result == ACL_ERROR) {
185 perror("acl_set_entry (acl_get_permset)");
186 return ACL_ERROR;
187 }
188
189 int s_result = acl_set_permset(existing_entry, entry_permset);
190 if (s_result == ACL_ERROR) {
191 perror("acl_set_entry (acl_set_permset)");
192 return ACL_ERROR;
193 }
194
195 return ACL_SUCCESS;
196 }
197
198 }
199
200 result = acl_get_entry(*aclp, ACL_NEXT_ENTRY, &existing_entry);
201 }
202
203 /* This catches both the initial acl_get_entry and the ones at the
204 end of the loop. */
205 if (result == ACL_ERROR) {
206 perror("acl_set_entry (acl_get_entry)");
207 return ACL_ERROR;
208 }
209
210 /* If we've made it this far, we need to add a new entry to the
211 ACL. */
212 acl_entry_t new_entry;
213
214 /* We allocate memory here that we should release! */
215 int c_result = acl_create_entry(aclp, &new_entry);
216 if (c_result == ACL_ERROR) {
217 perror("acl_set_entry (acl_create_entry)");
218 return ACL_ERROR;
219 }
220
221 int st_result = acl_set_tag_type(new_entry, entry_tag);
222 if (st_result == ACL_ERROR) {
223 perror("acl_set_entry (acl_set_tag_type)");
224 return ACL_ERROR;
225 }
226
227 int s_result = acl_set_permset(new_entry, entry_permset);
228 if (s_result == ACL_ERROR) {
229 perror("acl_set_entry (acl_set_permset)");
230 return ACL_ERROR;
231 }
232
233 if (entry_tag == ACL_USER || entry_tag == ACL_GROUP) {
234 /* We need to set the qualifier too. */
235 void* entry_qual = acl_get_qualifier(entry);
236 if (entry_qual == (void*)NULL) {
237 perror("acl_set_entry (acl_get_qualifier)");
238 return ACL_ERROR;
239 }
240
241 int sq_result = acl_set_qualifier(new_entry, entry_qual);
242 if (sq_result == ACL_ERROR) {
243 perror("acl_set_entry (acl_set_qualifier)");
244 return ACL_ERROR;
245 }
246 }
247
248 return ACL_SUCCESS;
249 }
250
251
252
253 /**
254 * @brief Determine the number of entries in the given ACL.
255 *
256 * @param acl
257 * A pointer to an acl_t structure.
258 *
259 * @return Either the non-negative number of entries in @c acl, or
260 * ACL_ERROR on error.
261 */
262 int acl_entry_count(acl_t* acl) {
263
264 acl_entry_t entry;
265 int entry_count = 0;
266 int result = acl_get_entry(*acl, ACL_FIRST_ENTRY, &entry);
267
268 while (result == ACL_SUCCESS) {
269 entry_count++;
270 result = acl_get_entry(*acl, ACL_NEXT_ENTRY, &entry);
271 }
272
273 if (result == ACL_ERROR) {
274 perror("acl_entry_count (acl_get_entry)");
275 return ACL_ERROR;
276 }
277
278 return entry_count;
279 }
280
281
282
283 /**
284 * @brief Determine whether or not the given ACL is minimal.
285 *
286 * An ACL is minimal if it has fewer than four entries.
287 *
288 * @param acl
289 * A pointer to an acl_t structure.
290 *
291 * @return
292 * - ACL_SUCCESS - @c acl is minimal
293 * - ACL_FAILURE - @c acl is not minimal
294 * - ACL_ERROR - Unexpected library error
295 */
296 int acl_is_minimal(acl_t* acl) {
297
298 int ec = acl_entry_count(acl);
299
300 if (ec == ACL_ERROR) {
301 perror("acl_is_minimal (acl_entry_count)");
302 return ACL_ERROR;
303 }
304
305 if (ec < 4) {
306 return ACL_SUCCESS;
307 }
308 else {
309 return ACL_FAILURE;
310 }
311 }
312
313
314
315 /**
316 * @brief Determine whether the given path has an ACL whose mask
317 * denies execute.
318 *
319 * @param path
320 * The path to check.
321 *
322 * @return
323 * - ACL_SUCCESS - @c path has a mask which denies execute.
324 * - ACL_FAILURE - The ACL for @c path does not deny execute,
325 * or @c path has no extended ACL at all.
326 * - ACL_ERROR - Unexpected library error.
327 */
328 int acl_execute_masked(const char* path) {
329
330 acl_t acl = acl_get_file(path, ACL_TYPE_ACCESS);
331
332 if (acl == (acl_t)NULL) {
333 perror("acl_execute_masked (acl_get_file)");
334 return ACL_ERROR;
335 }
336
337 /* Our return value. */
338 int result = ACL_FAILURE;
339
340 acl_entry_t entry;
341 int ge_result = acl_get_entry(acl, ACL_FIRST_ENTRY, &entry);
342
343 while (ge_result == ACL_SUCCESS) {
344 acl_tag_t tag = ACL_UNDEFINED_TAG;
345 int tag_result = acl_get_tag_type(entry, &tag);
346
347 if (tag_result == ACL_ERROR) {
348 perror("acl_execute_masked (acl_get_tag_type)");
349 result = ACL_ERROR;
350 goto cleanup;
351 }
352
353 if (tag == ACL_MASK) {
354 /* This is the mask entry, get its permissions, and see if
355 execute is specified. */
356 acl_permset_t permset;
357
358 int ps_result = acl_get_permset(entry, &permset);
359 if (ps_result == ACL_ERROR) {
360 perror("acl_execute_masked (acl_get_permset)");
361 result = ACL_ERROR;
362 goto cleanup;
363 }
364
365 int gp_result = acl_get_perm(permset, ACL_EXECUTE);
366 if (gp_result == ACL_ERROR) {
367 perror("acl_execute_masked (acl_get_perm)");
368 result = ACL_ERROR;
369 goto cleanup;
370 }
371
372 if (gp_result == ACL_FAILURE) {
373 /* No execute bit set in the mask; execute not allowed. */
374 return ACL_SUCCESS;
375 }
376 }
377
378 ge_result = acl_get_entry(acl, ACL_NEXT_ENTRY, &entry);
379 }
380
381 cleanup:
382 acl_free(acl);
383 return result;
384 }
385
386
387 /**
388 * @brief Determine whether @c path is executable (by anyone) or a
389 * directory.
390 *
391 * This is used as part of the heuristic to determine whether or not
392 * we should mask the execute bit when inheriting an ACL. If @c path
393 * is a directory, the answer is a clear-cut yes. This behavior is
394 * modeled after the capital 'X' perms of setfacl.
395 *
396 * If @c path is a file, we check the @a effective permissions,
397 * contrary to what setfacl does.
398 *
399 * @param path
400 * The path to check.
401 *
402 * @return
403 * - ACL_SUCCESS - @c path is a directory, or someone has effective
404 execute permissions.
405 * - ACL_FAILURE - @c path is a regular file and nobody can execute it.
406 * - ACL_ERROR - Unexpected library error.
407 */
408 int any_can_execute_or_dir(const char* path) {
409
410 if (is_directory(path)) {
411 /* That was easy... */
412 return ACL_SUCCESS;
413 }
414
415 acl_t acl = acl_get_file(path, ACL_TYPE_ACCESS);
416
417 if (acl == (acl_t)NULL) {
418 perror("any_can_execute_or_dir (acl_get_file)");
419 return ACL_ERROR;
420 }
421
422 /* Our return value. */
423 int result = ACL_FAILURE;
424
425 if (acl_is_minimal(&acl)) {
426 mode_t mode = get_mode(path);
427 if (mode & (S_IXUSR | S_IXOTH | S_IXGRP)) {
428 result = ACL_SUCCESS;
429 goto cleanup;
430 }
431 else {
432 result = ACL_FAILURE;
433 goto cleanup;
434 }
435 }
436
437 acl_entry_t entry;
438 int ge_result = acl_get_entry(acl, ACL_FIRST_ENTRY, &entry);
439
440 while (ge_result == ACL_SUCCESS) {
441 acl_permset_t permset;
442
443 int ps_result = acl_get_permset(entry, &permset);
444 if (ps_result == ACL_ERROR) {
445 perror("any_can_execute_or_dir (acl_get_permset)");
446 result = ACL_ERROR;
447 goto cleanup;
448 }
449
450 int gp_result = acl_get_perm(permset, ACL_EXECUTE);
451 if (gp_result == ACL_ERROR) {
452 perror("any_can_execute_or_dir (acl_get_perm)");
453 result = ACL_ERROR;
454 goto cleanup;
455 }
456
457 if (gp_result == ACL_SUCCESS) {
458 /* Only return one if this execute bit is not masked. */
459 if (acl_execute_masked(path) != ACL_SUCCESS) {
460 result = ACL_SUCCESS;
461 goto cleanup;
462 }
463 }
464
465 ge_result = acl_get_entry(acl, ACL_NEXT_ENTRY, &entry);
466 }
467
468 if (ge_result == ACL_ERROR) {
469 perror("any_can_execute_or_dir (acl_get_entry)");
470 result = ACL_ERROR;
471 goto cleanup;
472 }
473
474 cleanup:
475 acl_free(acl);
476 return result;
477 }
478
479
480
481 /**
482 * @brief Inherit the default ACL from @c parent to @c path.
483 *
484 * The @c parent parameter does not necessarily need to be the parent
485 * of @c path, although that will usually be the case. This overwrites
486 * any existing default ACL on @c path.
487 *
488 * @param parent
489 * The parent directory whose ACL we want to inherit.
490 *
491 * @param path
492 * The target directory whose ACL we wish to overwrite (or create).
493 *
494 * @return
495 * - ACL_SUCCESS - The default ACL was inherited successfully.
496 * - ACL_FAILURE - Either @c parent or @c path is not a directory.
497 * - ACL_ERROR - Unexpected library error.
498 */
499 int inherit_default_acl(const char* path, const char* parent) {
500 /* Inherit the default ACL from parent to path. This overwrites any
501 * existing default ACL. Returns 1 for success, 0 for failure, and
502 * -1 on error.
503 */
504
505 /* Our return value. */
506 int result = ACL_SUCCESS;
507
508 if (path == NULL) {
509 errno = ENOENT;
510 return ACL_ERROR;
511 }
512
513 if (!is_directory(path) || !is_directory(parent)) {
514 return ACL_FAILURE;
515 }
516
517 acl_t parent_acl = acl_get_file(parent, ACL_TYPE_DEFAULT);
518 if (parent_acl == (acl_t)NULL) {
519 perror("inherit_default_acl (acl_get_file)");
520 return ACL_ERROR;
521 }
522
523 acl_t path_acl = acl_dup(parent_acl);
524
525 if (path_acl == (acl_t)NULL) {
526 perror("inherit_default_acl (acl_dup)");
527 acl_free(parent_acl);
528 return ACL_ERROR;
529 }
530
531 int sf_result = acl_set_file(path, ACL_TYPE_DEFAULT, path_acl);
532 if (sf_result == -1) {
533 perror("inherit_default_acl (acl_set_file)");
534 result = ACL_ERROR;
535 goto cleanup;
536 }
537
538 cleanup:
539 acl_free(path_acl);
540 return result;
541 }
542
543
544 int wipe_acls(const char* path) {
545 /* Remove ACL_USER, ACL_GROUP, and ACL_MASK entries from
546 path. Returns 1 for success, 0 for failure, and -1 on error. */
547
548 if (path == NULL) {
549 errno = ENOENT;
550 return ACL_ERROR;
551 }
552
553 /* Finally, remove individual named/mask entries. */
554 acl_t acl = acl_get_file(path, ACL_TYPE_ACCESS);
555 if (acl == (acl_t)NULL) {
556 perror("wipe_acls (acl_get_file)");
557 return ACL_ERROR;
558 }
559
560 /* Our return value. */
561 int result = ACL_SUCCESS;
562
563 acl_entry_t entry;
564 int ge_result = acl_get_entry(acl, ACL_FIRST_ENTRY, &entry);
565
566 while (ge_result == ACL_SUCCESS) {
567 int d_result = acl_delete_entry(acl, entry);
568 if (d_result == ACL_ERROR) {
569 perror("wipe_acls (acl_delete_entry)");
570 result = ACL_ERROR;
571 goto cleanup;
572 }
573
574 ge_result = acl_get_entry(acl, ACL_NEXT_ENTRY, &entry);
575 }
576
577 /* Catches the first acl_get_entry as well as the ones at the end of
578 the loop. */
579 if (ge_result == ACL_ERROR) {
580 perror("wipe_acls (acl_get_entry)");
581 result = ACL_ERROR;
582 goto cleanup;
583 }
584
585 int sf_result = acl_set_file(path, ACL_TYPE_ACCESS, acl);
586 if (sf_result == ACL_ERROR) {
587 perror("wipe_acls (acl_set_file)");
588 result = ACL_ERROR;
589 goto cleanup;
590 }
591
592 cleanup:
593 acl_free(acl);
594 return result;
595 }
596
597
598 int apply_default_acl(const char* path) {
599 /* Really apply the default ACL by looping through it. Returns one
600 * for success, zero for failure (i.e. no ACL), and -1 on unexpected
601 * errors.
602
603 */
604 if (path == NULL) {
605 return ACL_FAILURE;
606 }
607
608 if (!is_regular_file(path) && !is_directory(path)) {
609 return ACL_FAILURE;
610 }
611
612 /* dirname mangles its argument */
613 char path_copy[PATH_MAX];
614 strncpy(path_copy, path, PATH_MAX-1);
615 path_copy[PATH_MAX-1] = 0;
616
617 char* parent = dirname(path_copy);
618 if (!is_directory(parent)) {
619 /* Make sure dirname() did what we think it did. */
620 return ACL_FAILURE;
621 }
622
623 /* Default to not masking the exec bit; i.e. applying the default
624 ACL literally. If --no-exec-mask was not specified, then we try
625 to "guess" whether or not to mask the exec bit. */
626 bool allow_exec = true;
627
628 if (!no_exec_mask) {
629 int ace_result = any_can_execute_or_dir(path);
630
631 if (ace_result == ACL_ERROR) {
632 perror("apply_default_acl (any_can_execute_or_dir)");
633 return ACL_ERROR;
634 }
635 allow_exec = (bool)ace_result;
636 }
637
638 acl_t defacl = acl_get_file(parent, ACL_TYPE_DEFAULT);
639
640 if (defacl == (acl_t)NULL) {
641 perror("apply_default_acl (acl_get_file)");
642 return ACL_ERROR;
643 }
644
645 /* Our return value. */
646 int result = ACL_SUCCESS;
647
648 int wipe_result = wipe_acls(path);
649 if (wipe_result == ACL_ERROR) {
650 perror("apply_default_acl (wipe_acls)");
651 result = ACL_ERROR;
652 goto cleanup;
653 }
654
655 /* Do this after wipe_acls(), otherwise we'll overwrite the wiped
656 ACL with this one. */
657 acl_t acl = acl_get_file(path, ACL_TYPE_ACCESS);
658 if (acl == (acl_t)NULL) {
659 perror("apply_default_acl (acl_get_file)");
660 return ACL_ERROR;
661 }
662
663 /* If it's a directory, inherit the parent's default. */
664 int inherit_result = inherit_default_acl(path, parent);
665 if (inherit_result == ACL_ERROR) {
666 perror("apply_default_acl (inherit_acls)");
667 result = ACL_ERROR;
668 goto cleanup;
669 }
670
671 acl_entry_t entry;
672 int ge_result = acl_get_entry(defacl, ACL_FIRST_ENTRY, &entry);
673
674 while (ge_result == ACL_SUCCESS) {
675 acl_tag_t tag = ACL_UNDEFINED_TAG;
676 int tag_result = acl_get_tag_type(entry, &tag);
677
678 if (tag_result == ACL_ERROR) {
679 perror("apply_default_acl (acl_get_tag_type)");
680 result = ACL_ERROR;
681 goto cleanup;
682 }
683
684
685 /* We've got an entry/tag from the default ACL. Get its permset. */
686 acl_permset_t permset;
687 int ps_result = acl_get_permset(entry, &permset);
688 if (ps_result == ACL_ERROR) {
689 perror("apply_default_acl (acl_get_permset)");
690 result = ACL_ERROR;
691 goto cleanup;
692 }
693
694 /* If this is a default mask, fix it up. */
695 if (tag == ACL_MASK ||
696 tag == ACL_USER_OBJ ||
697 tag == ACL_GROUP_OBJ ||
698 tag == ACL_OTHER) {
699
700 if (!allow_exec) {
701 /* The mask doesn't affect acl_user_obj, acl_group_obj (in
702 minimal ACLs) or acl_other entries, so if execute should be
703 masked, we have to do it manually. */
704 int d_result = acl_delete_perm(permset, ACL_EXECUTE);
705 if (d_result == ACL_ERROR) {
706 perror("apply_default_acl (acl_delete_perm)");
707 result = ACL_ERROR;
708 goto cleanup;
709 }
710
711 int sp_result = acl_set_permset(entry, permset);
712 if (sp_result == ACL_ERROR) {
713 perror("apply_default_acl (acl_set_permset)");
714 result = ACL_ERROR;
715 goto cleanup;
716 }
717 }
718 }
719
720 /* Finally, add the permset to the access ACL. */
721 int set_result = acl_set_entry(&acl, entry);
722 if (set_result == ACL_ERROR) {
723 perror("apply_default_acl (acl_set_entry)");
724 result = ACL_ERROR;
725 goto cleanup;
726 }
727
728 ge_result = acl_get_entry(defacl, ACL_NEXT_ENTRY, &entry);
729 }
730
731 /* Catches the first acl_get_entry as well as the ones at the end of
732 the loop. */
733 if (ge_result == ACL_ERROR) {
734 perror("apply_default_acl (acl_get_entry)");
735 result = ACL_ERROR;
736 goto cleanup;
737 }
738
739 int sf_result = acl_set_file(path, ACL_TYPE_ACCESS, acl);
740 if (sf_result == ACL_ERROR) {
741 perror("apply_default_acl (acl_set_file)");
742 result = ACL_ERROR;
743 goto cleanup;
744 }
745
746 cleanup:
747 acl_free(defacl);
748 return result;
749 }
750
751
752 void usage(char* program_name) {
753 /*
754 * Print usage information.
755 */
756 printf("Apply any applicable default ACLs to the given files or "
757 "directories.\n\n");
758 printf("Usage: %s [flags] <target1> [<target2> [ <target3>...]]\n\n",
759 program_name);
760 printf("Flags:\n");
761 printf(" -h, --help Print this help message\n");
762 printf(" -r, --recursive Act on any given directories recursively\n");
763 printf(" -x, --no-exec-mask Apply execute permissions unconditionally\n");
764
765 return;
766 }
767
768
769 int apply_default_acl_nftw(const char *target,
770 const struct stat *s,
771 int info,
772 struct FTW *ftw) {
773 /* A wrapper around the apply_default_acl() function for use with
774 * nftw(). We need to adjust the return value so that nftw() doesn't
775 * think we've failed.
776 */
777 bool reapp_result = apply_default_acl(target);
778 if (reapp_result) {
779 return FTW_CONTINUE;
780 }
781 else {
782 return FTW_STOP;
783 }
784 }
785
786
787 bool apply_default_acl_recursive(const char *target) {
788 /* Attempt to apply default ACLs recursively. If target is a
789 * directory, we recurse through its entries. If not, we just
790 * apply the default ACL to target.
791 *
792 * We ignore symlinks for consistency with chmod -r.
793 *
794 */
795 if (!is_directory(target)) {
796 return apply_default_acl(target);
797 }
798
799 int max_levels = 256;
800 int flags = FTW_PHYS; /* Don't follow links. */
801
802 int nftw_result = nftw(target,
803 apply_default_acl_nftw,
804 max_levels,
805 flags);
806
807 if (nftw_result == 0) {
808 /* Success */
809 return true;
810 }
811
812 /* nftw will return -1 on error, or if the supplied function
813 * (apply_default_acl_nftw) returns a non-zero result, nftw will
814 * return that.
815 */
816 if (nftw_result == -1) {
817 perror("apply_default_acl_recursive (nftw)");
818 }
819
820 return false;
821 }
822
823
824 int main(int argc, char* argv[]) {
825 /*
826 * Call apply_default_acl on each command-line argument.
827 */
828 if (argc < 2) {
829 usage(argv[0]);
830 return EXIT_FAILURE;
831 }
832
833 bool recursive = false;
834 /* bool no_exec_mask is declared static/global */
835
836 struct option long_options[] = {
837 /* These options set a flag. */
838 {"help", no_argument, NULL, 'h'},
839 {"recursive", no_argument, NULL, 'r'},
840 {"no-exec-mask", no_argument, NULL, 'x'},
841 {NULL, 0, NULL, 0}
842 };
843
844 int opt = 0;
845
846 while ((opt = getopt_long(argc, argv, "hrx", long_options, NULL)) != -1) {
847 switch (opt) {
848 case 'h':
849 usage(argv[0]);
850 return EXIT_SUCCESS;
851 case 'r':
852 recursive = true;
853 break;
854 case 'x':
855 no_exec_mask = true;
856 break;
857 default:
858 usage(argv[0]);
859 return EXIT_FAILURE;
860 }
861 }
862
863 int result = EXIT_SUCCESS;
864
865 int arg_index = 1;
866 for (arg_index = optind; arg_index < argc; arg_index++) {
867 const char* target = argv[arg_index];
868 bool reapp_result = false;
869
870 if (recursive) {
871 reapp_result = apply_default_acl_recursive(target);
872 }
873 else {
874 /* It's either normal file, or we're not operating recursively. */
875 reapp_result = apply_default_acl(target);
876 }
877
878 if (!reapp_result) {
879 result = EXIT_FAILURE;
880 }
881 }
882
883 return result;
884 }