2 #include <libgen.h> /* dirname() */
3 #include <limits.h> /* PATH_MAX */
12 #include <acl/libacl.h> /* acl_get_perm, not portable */
13 #include <sys/types.h>
17 mode_t
get_mode(const char* path
) {
19 * Get the mode bits from path.
27 int result
= stat(path
, &s
);
33 /* errno will be set already by stat() */
39 bool is_regular_file(const char* path
) {
41 * Returns true if path is a regular file, false otherwise.
48 int result
= stat(path
, &s
);
50 return S_ISREG(s
.st_mode
);
57 bool is_directory(const char* path
) {
59 * Returns true if path is a directory, false otherwise.
66 int result
= stat(path
, &s
);
68 return S_ISDIR(s
.st_mode
);
77 int acl_set_entry(acl_t
* aclp
,
80 * Update or create the given entry.
84 int gt_result
= acl_get_tag_type(entry
, &entry_tag
);
85 if (gt_result
== -1) {
86 perror("acl_set_entry (acl_get_tag_type)");
90 acl_permset_t entry_permset
;
91 int ps_result
= acl_get_permset(entry
, &entry_permset
);
92 if (ps_result
== -1) {
93 perror("acl_set_entry (acl_get_permset)");
97 acl_entry_t existing_entry
;
98 /* Loop through the given ACL looking for matching entries. */
99 int result
= acl_get_entry(*aclp
, ACL_FIRST_ENTRY
, &existing_entry
);
101 while (result
== 1) {
102 acl_tag_t existing_tag
= ACL_UNDEFINED_TAG
;
103 int tag_result
= acl_get_tag_type(existing_entry
, &existing_tag
);
105 if (tag_result
== -1) {
106 perror("set_acl_tag_permset (acl_get_tag_type)");
110 if (existing_tag
== entry_tag
) {
111 if (entry_tag
== ACL_USER_OBJ
||
112 entry_tag
== ACL_GROUP_OBJ
||
113 entry_tag
== ACL_OTHER
) {
114 /* Only update for these three since all other tags will have
116 acl_permset_t existing_permset
;
117 int gep_result
= acl_get_permset(existing_entry
, &existing_permset
);
118 if (gep_result
== -1) {
119 perror("acl_set_entry (acl_get_permset)");
123 int s_result
= acl_set_permset(existing_entry
, entry_permset
);
124 if (s_result
== -1) {
125 perror("acl_set_entry (acl_set_permset)");
134 result
= acl_get_entry(*aclp
, ACL_NEXT_ENTRY
, &existing_entry
);
137 /* This catches both the initial acl_get_entry and the ones at the
140 perror("acl_set_entry (acl_get_entry)");
144 /* If we've made it this far, we need to add a new entry to the
146 acl_entry_t new_entry
;
148 /* We allocate memory here that we should release! */
149 int c_result
= acl_create_entry(aclp
, &new_entry
);
150 if (c_result
== -1) {
151 perror("acl_set_entry (acl_create_entry)");
155 int st_result
= acl_set_tag_type(new_entry
, entry_tag
);
156 if (st_result
== -1) {
157 perror("acl_set_entry (acl_set_tag_type)");
161 int s_result
= acl_set_permset(new_entry
, entry_permset
);
162 if (s_result
== -1) {
163 perror("acl_set_entry (acl_set_permset)");
167 if (entry_tag
== ACL_USER
|| entry_tag
== ACL_GROUP
) {
168 /* We need to set the qualifier too. */
169 void* entry_qual
= acl_get_qualifier(entry
);
170 if (entry_qual
== (void*)NULL
) {
171 perror("acl_set_entry (acl_get_qualifier)");
175 int sq_result
= acl_set_qualifier(new_entry
, entry_qual
);
176 if (sq_result
== -1) {
177 perror("acl_set_entry (acl_set_qualifier)");
187 int acl_entry_count(acl_t
* acl
) {
189 * Return the number of entries in acl, or -1 on error.
193 int result
= acl_get_entry(*acl
, ACL_FIRST_ENTRY
, &entry
);
195 while (result
== 1) {
197 result
= acl_get_entry(*acl
, ACL_NEXT_ENTRY
, &entry
);
201 perror("acl_is_minimal (acl_get_entry)");
209 int acl_is_minimal(acl_t
* acl
) {
210 /* An ACL is minimal if it has fewer than four entries. Return 0 for
211 * false, 1 for true, and -1 on error.
214 int ec
= acl_entry_count(acl
);
216 perror("acl_is_minimal (acl_entry_count)");
229 int any_can_execute(const char* path
) {
230 /* Returns 1 if any ACL entry has execute access, 0 if none do, and
233 acl_t acl
= acl_get_file(path
, ACL_TYPE_ACCESS
);
235 if (acl
== (acl_t
)NULL
) {
239 /* Our return value. */
242 if (acl_is_minimal(&acl
)) {
243 mode_t mode
= get_mode(path
);
244 if (mode
& (S_IXUSR
| S_IXOTH
| S_IXGRP
)) {
255 int ge_result
= acl_get_entry(acl
, ACL_FIRST_ENTRY
, &entry
);
257 while (ge_result
== 1) {
258 acl_permset_t permset
;
260 int ps_result
= acl_get_permset(entry
, &permset
);
261 if (ps_result
== -1) {
262 perror("any_can_execute (acl_get_permset)");
267 int gp_result
= acl_get_perm(permset
, ACL_EXECUTE
);
268 if (gp_result
== -1) {
269 perror("any_can_execute (acl_get_perm)");
274 if (gp_result
== 1) {
279 ge_result
= acl_get_entry(acl
, ACL_NEXT_ENTRY
, &entry
);
282 if (ge_result
== -1) {
283 perror("any_can_execute (acl_get_entry)");
294 int inherit_default_acl(const char* path
, const char* parent
) {
295 /* Inherit the default ACL from parent to path. This overwrites any
296 * existing default ACL. Returns 1 for success, 0 for failure, and
300 /* Our return value. */
308 if (!is_directory(path
) || !is_directory(parent
)) {
312 acl_t parent_acl
= acl_get_file(parent
, ACL_TYPE_DEFAULT
);
313 if (parent_acl
== (acl_t
)NULL
) {
317 acl_t path_acl
= acl_dup(parent_acl
);
319 if (path_acl
== (acl_t
)NULL
) {
320 perror("inherit_default_acl (acl_dup)");
321 acl_free(parent_acl
);
325 int sf_result
= acl_set_file(path
, ACL_TYPE_DEFAULT
, path_acl
);
326 if (sf_result
== -1) {
327 perror("inherit_default_acl (acl_set_file)");
338 int wipe_acls(const char* path
) {
339 /* Remove ACL_USER, ACL_GROUP, and ACL_MASK entries from
340 path. Returns 1 for success, 0 for failure, and -1 on error. */
347 /* Finally, remove individual named/mask entries. */
348 acl_t acl
= acl_get_file(path
, ACL_TYPE_ACCESS
);
349 if (acl
== (acl_t
)NULL
) {
350 perror("wipe_acls (acl_get_file)");
354 /* Our return value. */
358 int ge_result
= acl_get_entry(acl
, ACL_FIRST_ENTRY
, &entry
);
360 while (ge_result
== 1) {
361 int d_result
= acl_delete_entry(acl
, entry
);
362 if (d_result
== -1) {
363 perror("wipe_acls (acl_delete_entry)");
368 ge_result
= acl_get_entry(acl
, ACL_NEXT_ENTRY
, &entry
);
371 /* Catches the first acl_get_entry as well as the ones at the end of
373 if (ge_result
== -1) {
374 perror("wipe_acls (acl_get_entry)");
379 int sf_result
= acl_set_file(path
, ACL_TYPE_ACCESS
, acl
);
380 if (sf_result
== -1) {
381 perror("wipe_acls (acl_set_file)");
392 int reapply_default_acl(const char* path
) {
393 /* Really reapply the default ACL by looping through it. Returns one
394 * for success, zero for failure (i.e. no ACL), and -1 on unexpected
401 if (!is_regular_file(path
) && !is_directory(path
)) {
405 /* dirname mangles its argument */
406 char path_copy
[PATH_MAX
];
407 strncpy(path_copy
, path
, PATH_MAX
-1);
408 path_copy
[PATH_MAX
-1] = 0;
410 char* parent
= dirname(path_copy
);
411 if (!is_directory(parent
)) {
412 /* Make sure dirname() did what we think it did. */
416 int ace_result
= any_can_execute(path
);
417 if (ace_result
== -1) {
418 perror("reapply_default_acl_ng (any_can_execute)");
422 bool allow_exec
= (bool)ace_result
;
424 acl_t defacl
= acl_get_file(parent
, ACL_TYPE_DEFAULT
);
426 if (defacl
== (acl_t
)NULL
) {
427 perror("reapply_default_acl_ng (acl_get_file)");
431 /* Our return value. */
434 int wipe_result
= wipe_acls(path
);
435 if (wipe_result
== -1) {
436 perror("reapply_default_acl_ng (wipe_acls)");
441 /* Do this after wipe_acls(), otherwise we'll overwrite the wiped
442 ACL with this one. */
443 acl_t acl
= acl_get_file(path
, ACL_TYPE_ACCESS
);
444 if (acl
== (acl_t
)NULL
) {
445 perror("reapply_default_acl_ng (acl_get_file)");
449 /* If it's a directory, inherit the parent's default. */
450 int inherit_result
= inherit_default_acl(path
, parent
);
451 if (inherit_result
== -1) {
452 perror("reapply_default_acl_ng (inherit_acls)");
458 int ge_result
= acl_get_entry(defacl
, ACL_FIRST_ENTRY
, &entry
);
460 while (ge_result
== 1) {
461 acl_tag_t tag
= ACL_UNDEFINED_TAG
;
462 int tag_result
= acl_get_tag_type(entry
, &tag
);
464 if (tag_result
== -1) {
465 perror("has_default_tag_acl (acl_get_tag_type)");
471 /* We've got an entry/tag from the default ACL. Get its permset. */
472 acl_permset_t permset
;
473 int ps_result
= acl_get_permset(entry
, &permset
);
474 if (ps_result
== -1) {
475 perror("reapply_default_acl_ng (acl_get_permset)");
480 /* If this is a default mask, fix it up. */
481 if (tag
== ACL_MASK
||
482 tag
== ACL_USER_OBJ
||
483 tag
== ACL_GROUP_OBJ
||
486 /* The mask doesn't affect acl_user_obj, acl_group_obj (in
487 minimal ACLs) or acl_other entries, so if execute should be
488 masked, we have to do it manually. */
489 int d_result
= acl_delete_perm(permset
, ACL_EXECUTE
);
490 if (d_result
== -1) {
491 perror("reapply_default_acl_ng (acl_delete_perm)");
496 int sp_result
= acl_set_permset(entry
, permset
);
497 if (sp_result
== -1) {
498 perror("reapply_default_acl_ng (acl_set_permset)");
505 /* Finally, add the permset to the access ACL. */
506 int set_result
= acl_set_entry(&acl
, entry
);
507 if (set_result
== -1) {
508 perror("reapply_default_acl_ng (acl_set_entry)");
513 ge_result
= acl_get_entry(defacl
, ACL_NEXT_ENTRY
, &entry
);
516 /* Catches the first acl_get_entry as well as the ones at the end of
518 if (ge_result
== -1) {
519 perror("reapply_default_acl_ng (acl_get_entry)");
524 int sf_result
= acl_set_file(path
, ACL_TYPE_ACCESS
, acl
);
525 if (sf_result
== -1) {
526 perror("reapply_default_acl_ng (acl_set_file)");
537 void usage(char* program_name
) {
539 * Print usage information.
541 printf("Reapply any applicable default ACLs to the given files or "
543 printf("Usage: %s <target1> [<target2> [ <target3>...]]\n", program_name
);
547 bool asked_for_help(int argc
, char* argv
[]) {
549 * Check argv for either form of the "help" flag, -h or --help.
552 for (arg_index
= 1; arg_index
< argc
; arg_index
++) {
553 if (!strcmp(argv
[arg_index
], "-h")) {
556 if (!strcmp(argv
[arg_index
], "--help")) {
565 int main(int argc
, char* argv
[]) {
567 * Call reapply_default_acl on each command-line argument.
574 if (asked_for_help(argc
, argv
)) {
579 int result
= EXIT_SUCCESS
;
582 for (arg_index
= 1; arg_index
< argc
; arg_index
++) {
583 const char* target
= argv
[arg_index
];
584 bool reapp_result
= reapply_default_acl(target
);
587 result
= EXIT_FAILURE
;