]>
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_default_tag_acl(const char* path
, acl_tag_t desired_tag
) {
77 /* Returns one if the given path has a default ACL for the supplied
78 tag, zero if it doesn't, and -1 on error. */
79 acl_t defacl
= acl_get_file(path
, ACL_TYPE_DEFAULT
);
81 if (defacl
== (acl_t
)NULL
) {
86 int result
= acl_get_entry(defacl
, ACL_FIRST_ENTRY
, &entry
);
89 acl_tag_t tag
= ACL_UNDEFINED_TAG
;
90 int tag_result
= acl_get_tag_type(entry
, &tag
);
92 if (tag_result
== -1) {
93 perror("has_default_tag_acl (acl_get_tag_type)");
97 if (tag
== desired_tag
) {
102 result
= acl_get_entry(defacl
, ACL_NEXT_ENTRY
, &entry
);
106 perror("has_default_tag_acl (acl_get_entry)");
114 int has_default_user_obj_acl(const char* path
) {
115 return has_default_tag_acl(path
, ACL_USER_OBJ
);
118 int has_default_group_obj_acl(const char* path
) {
119 return has_default_tag_acl(path
, ACL_GROUP_OBJ
);
122 int has_default_other_acl(const char* path
) {
123 return has_default_tag_acl(path
, ACL_OTHER
);
126 int has_default_mask_acl(const char* path
) {
127 return has_default_tag_acl(path
, ACL_MASK
);
131 int get_default_tag_entry(const char* path
,
132 acl_tag_t desired_tag
,
133 acl_entry_t
* entry
) {
134 /* Returns one if successful, zero when the ACL doesn't exist, and
135 -1 on unexpected errors. */
136 acl_t defacl
= acl_get_file(path
, ACL_TYPE_DEFAULT
);
138 if (defacl
== (acl_t
)NULL
) {
139 /* Follow the acl_foo convention of -1 == error. */
143 int result
= acl_get_entry(defacl
, ACL_FIRST_ENTRY
, entry
);
145 while (result
== 1) {
146 acl_tag_t tag
= ACL_UNDEFINED_TAG
;
147 int tag_result
= acl_get_tag_type(*entry
, &tag
);
149 if (tag_result
== -1) {
150 perror("get_default_tag_entry (acl_get_tag_type)");
154 if (tag
== desired_tag
) {
155 /* We found the right tag, so return successfully. */
159 result
= acl_get_entry(defacl
, ACL_NEXT_ENTRY
, entry
);
162 /* This catches both the initial acl_get_entry and the ones at the
165 perror("get_default_tag_entry (acl_get_entry)");
174 int get_default_tag_permset(const char* path
,
175 acl_tag_t desired_tag
,
176 acl_permset_t
* output_perms
) {
177 /* Returns one if successful, zero when the ACL doesn't exist, and
178 -1 on unexpected errors. */
179 acl_t defacl
= acl_get_file(path
, ACL_TYPE_DEFAULT
);
181 if (defacl
== (acl_t
)NULL
) {
182 /* Follow the acl_foo convention of -1 == error. */
187 int result
= get_default_tag_entry(path
, desired_tag
, &entry
);
190 /* We found the right tag, now get the permset. */
191 int ps_result
= acl_get_permset(entry
, output_perms
);
192 if (ps_result
== -1) {
193 perror("get_default_tag_permset (acl_get_permset)");
196 if (ps_result
== 0) {
209 int has_default_tag_perm(const char* path
,
212 /* Check path to see if tag has the given perm. Returns one if it
213 does, zero if it doesn't (or there's no ACL), and -1 on unexpected
216 if (!has_default_tag_acl(path
, tag
)) {
220 acl_permset_t permset
;
221 bool ps_result
= get_default_tag_permset(path
, tag
, &permset
);
223 if (ps_result
!= 1) {
224 /* Failure or error. */
228 int p_result
= acl_get_perm(permset
, perm
);
229 if (p_result
== -1) {
230 perror("has_default_tag_perm (acl_get_perm)");
236 int remove_default_tag_perm(const char* path
,
239 /* Attempt to remove perm from tag. Returns one if successful, zero
240 if there was nothing to do, and -1 on errors. */
241 int hdta
= has_default_tag_acl(path
, tag
);
243 /* Failure or error. */
247 acl_permset_t permset
;
248 bool ps_result
= get_default_tag_permset(path
, tag
, &permset
);
250 if (ps_result
!= 1) {
251 /* Failure or error. */
255 int d_result
= acl_delete_perm(permset
, perm
);
256 if (d_result
== -1) {
257 perror("remove_default_tag_perm (acl_delete_perm)");
261 /* We've only removed perm from the permset; now we have to replace
264 int entry_result
= get_default_tag_entry(path
, tag
, &entry
);
266 if (entry_result
== -1) {
267 perror("remove_default_tag_perm (get_default_tag_entry)");
271 if (entry_result
== 1) {
273 int s_result
= acl_set_permset(entry
, permset
);
274 if (s_result
== -1) {
275 perror("remove_default_tag_perm (acl_set_permset)");
286 int remove_default_group_obj_execute(const char* path
) {
287 return remove_default_tag_perm(path
, ACL_GROUP_OBJ
, ACL_EXECUTE
);
291 int has_default_user_obj_read(const char* path
) {
292 return has_default_tag_perm(path
, ACL_USER_OBJ
, ACL_READ
);
295 int has_default_user_obj_write(const char* path
) {
296 return has_default_tag_perm(path
, ACL_USER_OBJ
, ACL_WRITE
);
299 int has_default_user_obj_execute(const char* path
) {
300 return has_default_tag_perm(path
, ACL_USER_OBJ
, ACL_EXECUTE
);
303 int has_default_group_obj_read(const char* path
) {
304 return has_default_tag_perm(path
, ACL_GROUP_OBJ
, ACL_READ
);
307 int has_default_group_obj_write(const char* path
) {
308 return has_default_tag_perm(path
, ACL_GROUP_OBJ
, ACL_WRITE
);
311 int has_default_group_obj_execute(const char* path
) {
312 return has_default_tag_perm(path
, ACL_GROUP_OBJ
, ACL_EXECUTE
);
315 int has_default_other_read(const char* path
) {
316 return has_default_tag_perm(path
, ACL_OTHER
, ACL_READ
);
319 int has_default_other_write(const char* path
) {
320 return has_default_tag_perm(path
, ACL_OTHER
, ACL_WRITE
);
323 int has_default_other_execute(const char* path
) {
324 return has_default_tag_perm(path
, ACL_OTHER
, ACL_EXECUTE
);
327 int has_default_mask_read(const char* path
) {
328 return has_default_tag_perm(path
, ACL_MASK
, ACL_READ
);
331 int has_default_mask_write(const char* path
) {
332 return has_default_tag_perm(path
, ACL_MASK
, ACL_WRITE
);
335 int has_default_mask_execute(const char* path
) {
336 return has_default_tag_perm(path
, ACL_MASK
, ACL_EXECUTE
);
340 int reapply_default_acl(const char* path
) {
341 /* If this is a normal file or directory (i.e. that has just been
342 created), we proceed to find its parent directory which will have
345 Returns one for success, zero for failure (i.e. no ACL), and -1
346 on unexpected errors. */
351 if (!is_regular_file(path
) && !is_directory(path
)) {
355 /* dirname mangles its argument */
356 char path_copy
[PATH_MAX
];
357 strncpy(path_copy
, path
, PATH_MAX
-1);
358 path_copy
[PATH_MAX
-1] = 0;
360 char* parent
= dirname(path_copy
);
361 if (!is_directory(parent
)) {
362 /* Make sure dirname() did what we think it did. */
366 /* This is the original mode of path. We will simply add permissions
367 to it, and then later reapply the result via chmod. */
368 mode_t path_mode
= get_mode(path
);
370 if (has_default_mask_acl(parent
)) {
371 /* The parent has an extended ACL. Extended ACLs use the mask
374 /* For the group bits, we'll use the ACL's mask instead of the group
375 object bits. If the default ACL had a group entry, it should
376 already have propagated (but might be masked). */
377 if (has_default_mask_read(parent
)) {
378 path_mode
|= S_IRGRP
;
381 path_mode
&= ~S_IRGRP
;
384 if (has_default_mask_write(parent
)) {
385 path_mode
|= S_IWGRP
;
388 path_mode
&= ~S_IWGRP
;
391 if (!mode_has_perm(path_mode
, S_IXGRP
)) {
392 /* The group ACL entry should already have been inherited from the
393 default ACL. If the source was not group executable, we want to
394 modify the destination so that it is not group executable
395 either. In the presence of ACLs, the group permissions come not
396 from the mode bits, but from the group:: ACL entry. So, to do
397 this, we remove the group::x entry. */
398 remove_default_group_obj_execute(path
);
401 /* We need to determine whether or not to mask the execute
402 bit. This applies not only to the user/group/other entries, but
403 also to all other named entries. If the original file wasn't
404 executable, then the result probably should not be. To
405 determine whether or not "it was executable", we rely on the
406 user execute bits. Obviously this should be done before we
408 if (has_default_mask_execute(parent
)) {
409 if (mode_has_perm(path_mode
, S_IXUSR
)) {
410 /* This just adds the group execute bit, and doesn't actually
411 grant group execute permissions. */
412 path_mode
|= S_IXGRP
;
416 path_mode
&= ~S_IXGRP
;
421 /* It's a minimal ACL. We'll repeat for the group bits what we
422 already did for the owner/other bits. */
423 if (has_default_group_obj_acl(parent
)) {
424 if (has_default_group_obj_read(parent
)) {
425 path_mode
|= S_IRGRP
;
428 path_mode
&= ~S_IRGRP
;
432 if (has_default_group_obj_write(parent
)) {
433 path_mode
|= S_IWGRP
;
436 path_mode
&= ~S_IWGRP
;
439 /* We don't want to set the execute bit on via the ACL unless it
440 was on originally. */
441 if (!has_default_group_obj_execute(parent
)) {
442 path_mode
&= ~S_IXGRP
;
448 /* If parent has a default user ACL, apply it. */
449 if (has_default_user_obj_acl(parent
)) {
451 if (has_default_user_obj_read(parent
)) {
453 path_mode
|= S_IRUSR
;
456 /* Remove user read. */
457 path_mode
&= ~S_IRUSR
;
461 if (has_default_user_obj_write(parent
)) {
462 /* Add user write. */
463 path_mode
|= S_IWUSR
;
466 /* Remove user write. */
467 path_mode
&= ~S_IWUSR
;
471 /* We don't want to set the execute bit on via the ACL unless it
472 was on originally. */
473 if (!has_default_user_obj_execute(parent
)) {
474 /* Remove user execute. */
475 path_mode
&= ~S_IXUSR
;
480 /* Do the same thing with the other perms/ACL. */
481 if (has_default_other_acl(parent
)) {
483 if (has_default_other_read(parent
)) {
484 path_mode
|= S_IROTH
;
487 path_mode
&= ~S_IROTH
;
491 if (has_default_other_write(parent
)) {
492 path_mode
|= S_IWOTH
;
495 path_mode
&= ~S_IWOTH
;
499 /* We don't want to set the execute bit on via the ACL unless it
500 was on originally. */
501 if (!has_default_other_execute(parent
)) {
502 path_mode
&= ~S_IXOTH
;
506 int chmod_result
= chmod(path
, path_mode
);
507 if (chmod_result
== 0) {
516 int main(int argc
, char* argv
[]) {
517 const char* target
= argv
[1];
519 bool result
= reapply_default_acl(target
);