]>
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 defacl
= acl_get_file(path
, type
);
148 if (defacl
== (acl_t
)NULL
) {
149 /* Follow the acl_foo convention of -1 == error. */
153 int result
= acl_get_entry(defacl
, 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_default_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(defacl
, ACL_NEXT_ENTRY
, entry
);
172 /* This catches both the initial acl_get_entry and the ones at the
175 perror("get_default_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_default_tag_entry(path
, 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_default_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
, ACL_TYPE_DEFAULT
, desired_tag
, output_perms
);
237 int get_access_tag_permset(const char* path
,
238 acl_tag_t desired_tag
,
239 acl_permset_t
* output_perms
) {
240 return get_type_tag_permset(path
, ACL_TYPE_ACCESS
, desired_tag
, output_perms
);
243 int has_default_tag_perm(const char* path
,
246 /* Check path to see if tag has the given perm. Returns one if it
247 does, zero if it doesn't (or there's no ACL), and -1 on unexpected
250 if (!has_default_tag_acl(path
, tag
)) {
254 acl_permset_t permset
;
255 bool ps_result
= get_default_tag_permset(path
, tag
, &permset
);
257 if (ps_result
!= 1) {
258 /* Failure or error. */
262 int p_result
= acl_get_perm(permset
, perm
);
263 if (p_result
== -1) {
264 perror("has_default_tag_perm (acl_get_perm)");
270 int remove_access_tag_perm(const char* path
,
273 /* Attempt to remove perm from tag. Returns one if successful, zero
274 if there was nothing to do, and -1 on errors. */
275 int hata
= has_access_tag_acl(path
, tag
);
277 /* Failure or error. */
281 acl_permset_t permset
;
282 bool ps_result
= get_access_tag_permset(path
, tag
, &permset
);
284 if (ps_result
!= 1) {
285 /* Failure or error. */
289 int d_result
= acl_delete_perm(permset
, perm
);
290 if (d_result
== -1) {
291 perror("remove_access_tag_perm (acl_delete_perm)");
295 /* We've only removed perm from the permset; now we have to replace
298 int entry_result
= get_access_tag_entry(path
, tag
, &entry
);
300 if (entry_result
== -1) {
301 perror("remove_access_tag_perm (get_access_tag_entry)");
305 if (entry_result
== 1) {
307 int s_result
= acl_set_permset(entry
, permset
);
308 if (s_result
== -1) {
309 perror("remove_access_tag_perm (acl_set_permset)");
320 int remove_access_group_obj_execute(const char* path
) {
321 return remove_access_tag_perm(path
, ACL_GROUP_OBJ
, ACL_EXECUTE
);
325 int has_default_user_obj_read(const char* path
) {
326 return has_default_tag_perm(path
, ACL_USER_OBJ
, ACL_READ
);
329 int has_default_user_obj_write(const char* path
) {
330 return has_default_tag_perm(path
, ACL_USER_OBJ
, ACL_WRITE
);
333 int has_default_user_obj_execute(const char* path
) {
334 return has_default_tag_perm(path
, ACL_USER_OBJ
, ACL_EXECUTE
);
337 int has_default_group_obj_read(const char* path
) {
338 return has_default_tag_perm(path
, ACL_GROUP_OBJ
, ACL_READ
);
341 int has_default_group_obj_write(const char* path
) {
342 return has_default_tag_perm(path
, ACL_GROUP_OBJ
, ACL_WRITE
);
345 int has_default_group_obj_execute(const char* path
) {
346 return has_default_tag_perm(path
, ACL_GROUP_OBJ
, ACL_EXECUTE
);
349 int has_default_other_read(const char* path
) {
350 return has_default_tag_perm(path
, ACL_OTHER
, ACL_READ
);
353 int has_default_other_write(const char* path
) {
354 return has_default_tag_perm(path
, ACL_OTHER
, ACL_WRITE
);
357 int has_default_other_execute(const char* path
) {
358 return has_default_tag_perm(path
, ACL_OTHER
, ACL_EXECUTE
);
361 int has_default_mask_read(const char* path
) {
362 return has_default_tag_perm(path
, ACL_MASK
, ACL_READ
);
365 int has_default_mask_write(const char* path
) {
366 return has_default_tag_perm(path
, ACL_MASK
, ACL_WRITE
);
369 int has_default_mask_execute(const char* path
) {
370 return has_default_tag_perm(path
, ACL_MASK
, ACL_EXECUTE
);
374 int reapply_default_acl(const char* path
) {
375 /* If this is a normal file or directory (i.e. that has just been
376 created), we proceed to find its parent directory which will have
379 Returns one for success, zero for failure (i.e. no ACL), and -1
380 on unexpected errors. */
385 if (!is_regular_file(path
) && !is_directory(path
)) {
389 /* dirname mangles its argument */
390 char path_copy
[PATH_MAX
];
391 strncpy(path_copy
, path
, PATH_MAX
-1);
392 path_copy
[PATH_MAX
-1] = 0;
394 char* parent
= dirname(path_copy
);
395 if (!is_directory(parent
)) {
396 /* Make sure dirname() did what we think it did. */
400 /* This is the original mode of path. We will simply add permissions
401 to it, and then later reapply the result via chmod. */
402 mode_t path_mode
= get_mode(path
);
404 if (has_default_mask_acl(parent
)) {
405 /* The parent has an extended ACL. Extended ACLs use the mask
408 /* For the group bits, we'll use the ACL's mask instead of the group
409 object bits. If the default ACL had a group entry, it should
410 already have propagated (but might be masked). */
411 if (has_default_mask_read(parent
)) {
412 path_mode
|= S_IRGRP
;
415 path_mode
&= ~S_IRGRP
;
418 if (has_default_mask_write(parent
)) {
419 path_mode
|= S_IWGRP
;
422 path_mode
&= ~S_IWGRP
;
425 if (!mode_has_perm(path_mode
, S_IXGRP
)) {
426 /* The group ACL entry should already have been inherited from the
427 default ACL. If the source was not group executable, we want to
428 modify the destination so that it is not group executable
429 either. In the presence of ACLs, the group permissions come not
430 from the mode bits, but from the group:: ACL entry. So, to do
431 this, we remove the group::x entry. */
432 remove_access_group_obj_execute(path
);
435 /* We need to determine whether or not to mask the execute
436 bit. This applies not only to the user/group/other entries, but
437 also to all other named entries. If the original file wasn't
438 executable, then the result probably should not be. To
439 determine whether or not "it was executable", we rely on the
440 user execute bits. Obviously this should be done before we
442 if (has_default_mask_execute(parent
)) {
443 if (mode_has_perm(path_mode
, S_IXUSR
)) {
444 /* This just adds the group execute bit, and doesn't actually
445 grant group execute permissions. */
446 path_mode
|= S_IXGRP
;
450 path_mode
&= ~S_IXGRP
;
455 /* It's a minimal ACL. We'll repeat for the group bits what we
456 already did for the owner/other bits. */
457 if (has_default_group_obj_acl(parent
)) {
458 if (has_default_group_obj_read(parent
)) {
459 path_mode
|= S_IRGRP
;
462 path_mode
&= ~S_IRGRP
;
466 if (has_default_group_obj_write(parent
)) {
467 path_mode
|= S_IWGRP
;
470 path_mode
&= ~S_IWGRP
;
473 /* We don't want to set the execute bit on via the ACL unless it
474 was on originally. */
475 if (!has_default_group_obj_execute(parent
)) {
476 path_mode
&= ~S_IXGRP
;
482 /* If parent has a default user ACL, apply it. */
483 if (has_default_user_obj_acl(parent
)) {
485 if (has_default_user_obj_read(parent
)) {
487 path_mode
|= S_IRUSR
;
490 /* Remove user read. */
491 path_mode
&= ~S_IRUSR
;
495 if (has_default_user_obj_write(parent
)) {
496 /* Add user write. */
497 path_mode
|= S_IWUSR
;
500 /* Remove user write. */
501 path_mode
&= ~S_IWUSR
;
505 /* We don't want to set the execute bit on via the ACL unless it
506 was on originally. */
507 if (!has_default_user_obj_execute(parent
)) {
508 /* Remove user execute. */
509 path_mode
&= ~S_IXUSR
;
514 /* Do the same thing with the other perms/ACL. */
515 if (has_default_other_acl(parent
)) {
517 if (has_default_other_read(parent
)) {
518 path_mode
|= S_IROTH
;
521 path_mode
&= ~S_IROTH
;
525 if (has_default_other_write(parent
)) {
526 path_mode
|= S_IWOTH
;
529 path_mode
&= ~S_IWOTH
;
533 /* We don't want to set the execute bit on via the ACL unless it
534 was on originally. */
535 if (!has_default_other_execute(parent
)) {
536 path_mode
&= ~S_IXOTH
;
540 int chmod_result
= chmod(path
, path_mode
);
541 if (chmod_result
== 0) {
550 int main(int argc
, char* argv
[]) {
551 const char* target
= argv
[1];
553 bool result
= reapply_default_acl(target
);