]>
gitweb.michael.orlitzky.com - apply-default-acl.git/blob - src/aclq.c
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
) {
24 int result
= stat(path
, &s
);
30 /* errno will be set already by stat() */
35 bool mode_has_perm(mode_t mode
, int perm
) {
45 bool is_regular_file(const char* path
) {
51 int result
= stat(path
, &s
);
53 return S_ISREG(s
.st_mode
);
60 bool is_directory(const char* path
) {
66 int result
= stat(path
, &s
);
68 return S_ISDIR(s
.st_mode
);
76 int has_type_tag_acl(const char* path
,
78 acl_tag_t desired_tag
) {
79 /* Returns one if the given path has a default ACL for the supplied
80 tag, zero if it doesn't, and -1 on error. */
81 acl_t defacl
= acl_get_file(path
, type
);
83 if (defacl
== (acl_t
)NULL
) {
88 int result
= acl_get_entry(defacl
, ACL_FIRST_ENTRY
, &entry
);
91 acl_tag_t tag
= ACL_UNDEFINED_TAG
;
92 int tag_result
= acl_get_tag_type(entry
, &tag
);
94 if (tag_result
== -1) {
95 perror("has_default_tag_acl (acl_get_tag_type)");
99 if (tag
== desired_tag
) {
104 result
= acl_get_entry(defacl
, ACL_NEXT_ENTRY
, &entry
);
108 perror("has_default_tag_acl (acl_get_entry)");
115 int has_default_tag_acl(const char* path
, acl_tag_t desired_tag
) {
116 return has_type_tag_acl(path
, ACL_TYPE_DEFAULT
, desired_tag
);
119 int has_access_tag_acl(const char* path
, acl_tag_t desired_tag
) {
120 return has_type_tag_acl(path
, ACL_TYPE_ACCESS
, desired_tag
);
123 int has_default_user_obj_acl(const char* path
) {
124 return has_default_tag_acl(path
, ACL_USER_OBJ
);
127 int has_default_group_obj_acl(const char* path
) {
128 return has_default_tag_acl(path
, ACL_GROUP_OBJ
);
131 int has_default_other_acl(const char* path
) {
132 return has_default_tag_acl(path
, ACL_OTHER
);
135 int has_default_mask_acl(const char* path
) {
136 return has_default_tag_acl(path
, ACL_MASK
);
140 int get_type_tag_entry(const char* path
,
142 acl_tag_t desired_tag
,
143 acl_entry_t
* entry
) {
144 /* Returns one if successful, zero when the ACL doesn't exist, and
145 -1 on unexpected errors. */
146 acl_t acl
= acl_get_file(path
, type
);
148 if (acl
== (acl_t
)NULL
) {
149 /* Follow the acl_foo convention of -1 == error. */
153 int result
= acl_get_entry(acl
, ACL_FIRST_ENTRY
, entry
);
155 while (result
== 1) {
156 acl_tag_t tag
= ACL_UNDEFINED_TAG
;
157 int tag_result
= acl_get_tag_type(*entry
, &tag
);
159 if (tag_result
== -1) {
160 perror("get_type_tag_entry (acl_get_tag_type)");
164 if (tag
== desired_tag
) {
165 /* We found the right tag, so return successfully. */
169 result
= acl_get_entry(acl
, ACL_NEXT_ENTRY
, entry
);
172 /* This catches both the initial acl_get_entry and the ones at the
175 perror("get_type_tag_entry (acl_get_entry)");
182 int get_default_tag_entry(const char* path
,
183 acl_tag_t desired_tag
,
184 acl_entry_t
* entry
) {
185 return get_type_tag_entry(path
, ACL_TYPE_DEFAULT
, desired_tag
, entry
);
188 int get_access_tag_entry(const char* path
,
189 acl_tag_t desired_tag
,
190 acl_entry_t
* entry
) {
191 return get_type_tag_entry(path
, ACL_TYPE_ACCESS
, desired_tag
, entry
);
196 int get_type_tag_permset(const char* path
,
198 acl_tag_t desired_tag
,
199 acl_permset_t
* output_perms
) {
200 /* Returns one if successful, zero when the ACL doesn't exist, and
201 -1 on unexpected errors. */
202 acl_t defacl
= acl_get_file(path
, type
);
204 if (defacl
== (acl_t
)NULL
) {
205 /* Follow the acl_foo convention of -1 == error. */
210 int result
= get_type_tag_entry(path
, type
, desired_tag
, &entry
);
213 /* We found the right tag, now get the permset. */
214 int ps_result
= acl_get_permset(entry
, output_perms
);
215 if (ps_result
== -1) {
216 perror("get_type_tag_permset (acl_get_permset)");
219 if (ps_result
== 0) {
231 int get_default_tag_permset(const char* path
,
232 acl_tag_t desired_tag
,
233 acl_permset_t
* output_perms
) {
234 return get_type_tag_permset(path
,
240 int get_access_tag_permset(const char* path
,
241 acl_tag_t desired_tag
,
242 acl_permset_t
* output_perms
) {
243 return get_type_tag_permset(path
, ACL_TYPE_ACCESS
, desired_tag
, output_perms
);
246 int has_default_tag_perm(const char* path
,
249 /* Check path to see if tag has the given perm. Returns one if it
250 does, zero if it doesn't (or there's no ACL), and -1 on unexpected
253 if (!has_default_tag_acl(path
, tag
)) {
257 acl_permset_t permset
;
258 bool ps_result
= get_default_tag_permset(path
, tag
, &permset
);
260 if (ps_result
!= 1) {
261 /* Failure or error. */
265 int p_result
= acl_get_perm(permset
, perm
);
266 if (p_result
== -1) {
267 perror("has_default_tag_perm (acl_get_perm)");
273 int remove_access_tag_perm(const char* path
,
274 acl_tag_t desired_tag
,
276 /* Attempt to remove perm from tag. Returns one if successful, zero
277 if there was nothing to do, and -1 on errors. */
278 acl_t acl
= acl_get_file(path
, ACL_TYPE_ACCESS
);
279 if (acl
== (acl_t
)NULL
) {
284 acl_permset_t permset
;
285 bool ps_result
= get_access_tag_permset(path
, desired_tag
, &permset
);
287 if (ps_result
!= 1) {
288 /* Failure or error. */
292 int d_result
= acl_delete_perm(permset
, perm
);
293 if (d_result
== -1) {
294 perror("remove_access_tag_perm (acl_delete_perm)");
298 /* We've only removed perm from the permset; now we have to replace
301 int result
= acl_get_entry(acl
, ACL_FIRST_ENTRY
, &entry
);
303 while (result
== 1) {
304 acl_tag_t tag
= ACL_UNDEFINED_TAG
;
305 int tag_result
= acl_get_tag_type(entry
, &tag
);
307 if (tag_result
== -1) {
308 perror("remove_access_tag_perm (acl_get_tag_type)");
312 if (tag
== desired_tag
) {
313 /* We found the right tag. Update the permset. */
314 int s_result
= acl_set_permset(entry
, permset
);
315 if (s_result
== -1) {
316 perror("remove_access_tag_perm (acl_set_permset)");
320 int sf_result
= acl_set_file(path
, ACL_TYPE_ACCESS
, acl
);
321 if (sf_result
== -1) {
322 perror("remove_access_tag_perm (acl_set_file)");
329 result
= acl_get_entry(acl
, ACL_NEXT_ENTRY
, &entry
);
332 /* This catches both the initial acl_get_entry and the ones at the
335 perror("remove_access_tag_perm (acl_get_entry)");
342 int remove_access_group_obj_execute(const char* path
) {
343 return remove_access_tag_perm(path
, ACL_GROUP_OBJ
, ACL_EXECUTE
);
347 int has_default_user_obj_read(const char* path
) {
348 return has_default_tag_perm(path
, ACL_USER_OBJ
, ACL_READ
);
351 int has_default_user_obj_write(const char* path
) {
352 return has_default_tag_perm(path
, ACL_USER_OBJ
, ACL_WRITE
);
355 int has_default_user_obj_execute(const char* path
) {
356 return has_default_tag_perm(path
, ACL_USER_OBJ
, ACL_EXECUTE
);
359 int has_default_group_obj_read(const char* path
) {
360 return has_default_tag_perm(path
, ACL_GROUP_OBJ
, ACL_READ
);
363 int has_default_group_obj_write(const char* path
) {
364 return has_default_tag_perm(path
, ACL_GROUP_OBJ
, ACL_WRITE
);
367 int has_default_group_obj_execute(const char* path
) {
368 return has_default_tag_perm(path
, ACL_GROUP_OBJ
, ACL_EXECUTE
);
371 int has_default_other_read(const char* path
) {
372 return has_default_tag_perm(path
, ACL_OTHER
, ACL_READ
);
375 int has_default_other_write(const char* path
) {
376 return has_default_tag_perm(path
, ACL_OTHER
, ACL_WRITE
);
379 int has_default_other_execute(const char* path
) {
380 return has_default_tag_perm(path
, ACL_OTHER
, ACL_EXECUTE
);
383 int has_default_mask_read(const char* path
) {
384 return has_default_tag_perm(path
, ACL_MASK
, ACL_READ
);
387 int has_default_mask_write(const char* path
) {
388 return has_default_tag_perm(path
, ACL_MASK
, ACL_WRITE
);
391 int has_default_mask_execute(const char* path
) {
392 return has_default_tag_perm(path
, ACL_MASK
, ACL_EXECUTE
);
396 int reapply_default_acl(const char* path
) {
397 /* If this is a normal file or directory (i.e. that has just been
398 created), we proceed to find its parent directory which will have
401 Returns one for success, zero for failure (i.e. no ACL), and -1
402 on unexpected errors. */
407 if (!is_regular_file(path
) && !is_directory(path
)) {
411 /* dirname mangles its argument */
412 char path_copy
[PATH_MAX
];
413 strncpy(path_copy
, path
, PATH_MAX
-1);
414 path_copy
[PATH_MAX
-1] = 0;
416 char* parent
= dirname(path_copy
);
417 if (!is_directory(parent
)) {
418 /* Make sure dirname() did what we think it did. */
422 /* This is the original mode of path. We will simply add permissions
423 to it, and then later reapply the result via chmod. */
424 mode_t path_mode
= get_mode(path
);
426 if (has_default_mask_acl(parent
)) {
427 /* The parent has an extended ACL. Extended ACLs use the mask
430 /* For the group bits, we'll use the ACL's mask instead of the group
431 object bits. If the default ACL had a group entry, it should
432 already have propagated (but might be masked). */
433 if (has_default_mask_read(parent
)) {
434 path_mode
|= S_IRGRP
;
437 path_mode
&= ~S_IRGRP
;
440 if (has_default_mask_write(parent
)) {
441 path_mode
|= S_IWGRP
;
444 path_mode
&= ~S_IWGRP
;
447 if (!mode_has_perm(path_mode
, S_IXGRP
)) {
448 /* The group ACL entry should already have been inherited from the
449 default ACL. If the source was not group executable, we want to
450 modify the destination so that it is not group executable
451 either. In the presence of ACLs, the group permissions come not
452 from the mode bits, but from the group:: ACL entry. So, to do
453 this, we remove the group::x entry. */
454 remove_access_group_obj_execute(path
);
457 /* We need to determine whether or not to mask the execute
458 bit. This applies not only to the user/group/other entries, but
459 also to all other named entries. If the original file wasn't
460 executable, then the result probably should not be. To
461 determine whether or not "it was executable", we rely on the
462 user execute bits. Obviously this should be done before we
464 if (has_default_mask_execute(parent
)) {
465 if (mode_has_perm(path_mode
, S_IXUSR
)) {
466 /* This just adds the group execute bit, and doesn't actually
467 grant group execute permissions. */
468 path_mode
|= S_IXGRP
;
472 path_mode
&= ~S_IXGRP
;
477 /* It's a minimal ACL. We'll repeat for the group bits what we
478 already did for the owner/other bits. */
479 if (has_default_group_obj_acl(parent
)) {
480 if (has_default_group_obj_read(parent
)) {
481 path_mode
|= S_IRGRP
;
484 path_mode
&= ~S_IRGRP
;
488 if (has_default_group_obj_write(parent
)) {
489 path_mode
|= S_IWGRP
;
492 path_mode
&= ~S_IWGRP
;
495 /* We don't want to set the execute bit on via the ACL unless it
496 was on originally. */
497 if (!has_default_group_obj_execute(parent
)) {
498 path_mode
&= ~S_IXGRP
;
504 /* If parent has a default user ACL, apply it. */
505 if (has_default_user_obj_acl(parent
)) {
507 if (has_default_user_obj_read(parent
)) {
509 path_mode
|= S_IRUSR
;
512 /* Remove user read. */
513 path_mode
&= ~S_IRUSR
;
517 if (has_default_user_obj_write(parent
)) {
518 /* Add user write. */
519 path_mode
|= S_IWUSR
;
522 /* Remove user write. */
523 path_mode
&= ~S_IWUSR
;
527 /* We don't want to set the execute bit on via the ACL unless it
528 was on originally. */
529 if (!has_default_user_obj_execute(parent
)) {
530 /* Remove user execute. */
531 path_mode
&= ~S_IXUSR
;
536 /* Do the same thing with the other perms/ACL. */
537 if (has_default_other_acl(parent
)) {
539 if (has_default_other_read(parent
)) {
540 path_mode
|= S_IROTH
;
543 path_mode
&= ~S_IROTH
;
547 if (has_default_other_write(parent
)) {
548 path_mode
|= S_IWOTH
;
551 path_mode
&= ~S_IWOTH
;
555 /* We don't want to set the execute bit on via the ACL unless it
556 was on originally. */
557 if (!has_default_other_execute(parent
)) {
558 path_mode
&= ~S_IXOTH
;
562 int chmod_result
= chmod(path
, path_mode
);
563 if (chmod_result
== 0) {
572 int main(int argc
, char* argv
[]) {
573 const char* target
= argv
[1];
575 bool result
= reapply_default_acl(target
);