]>
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_minimal_default_acl(const char* path
) {
115 /* An ACL is minimal if and only if it has no mask. Return 1 if it's
116 minimal, zero if it isn't, and -1 on errors. */
117 int has_dm
= has_default_tag_acl(path
, ACL_MASK
);
122 else if (has_dm
== 1) {
126 perror("has_minimal_default_acl");
132 int has_default_user_obj_acl(const char* path
) {
133 return has_default_tag_acl(path
, ACL_USER_OBJ
);
136 int has_default_group_obj_acl(const char* path
) {
137 return has_default_tag_acl(path
, ACL_GROUP_OBJ
);
140 int has_default_other_acl(const char* path
) {
141 return has_default_tag_acl(path
, ACL_OTHER
);
145 int get_default_tag_entry(const char* path
,
146 acl_tag_t desired_tag
,
147 acl_entry_t
* entry
) {
148 /* Returns one if successful, zero when the ACL doesn't exist, and
149 -1 on unexpected errors. */
150 acl_t defacl
= acl_get_file(path
, ACL_TYPE_DEFAULT
);
152 if (defacl
== (acl_t
)NULL
) {
153 /* Follow the acl_foo convention of -1 == error. */
157 int result
= acl_get_entry(defacl
, ACL_FIRST_ENTRY
, entry
);
159 while (result
== 1) {
160 acl_tag_t tag
= ACL_UNDEFINED_TAG
;
161 int tag_result
= acl_get_tag_type(*entry
, &tag
);
163 if (tag_result
== -1) {
164 perror("get_default_tag_entry (acl_get_tag_type)");
168 if (tag
== desired_tag
) {
169 /* We found the right tag, so return successfully. */
173 result
= acl_get_entry(defacl
, ACL_NEXT_ENTRY
, entry
);
176 /* This catches both the initial acl_get_entry and the ones at the
179 perror("get_default_tag_entry (acl_get_entry)");
188 int get_default_tag_permset(const char* path
,
189 acl_tag_t desired_tag
,
190 acl_permset_t
* output_perms
) {
191 /* Returns one if successful, zero when the ACL doesn't exist, and
192 -1 on unexpected errors. */
193 acl_t defacl
= acl_get_file(path
, ACL_TYPE_DEFAULT
);
195 if (defacl
== (acl_t
)NULL
) {
196 /* Follow the acl_foo convention of -1 == error. */
201 int result
= get_default_tag_entry(path
, desired_tag
, &entry
);
204 /* We found the right tag, now get the permset. */
205 int ps_result
= acl_get_permset(entry
, output_perms
);
206 if (ps_result
== -1) {
207 perror("get_default_tag_permset (acl_get_permset)");
210 if (ps_result
== 0) {
223 int has_default_tag_perm(const char* path
,
226 /* Check path to see if tag has the given perm. Returns one if it
227 does, zero if it doesn't (or there's no ACL), and -1 on unexpected
230 if (!has_default_tag_acl(path
, tag
)) {
234 acl_permset_t permset
;
235 bool ps_result
= get_default_tag_permset(path
, tag
, &permset
);
237 if (ps_result
!= 1) {
238 /* Failure or error. */
242 int p_result
= acl_get_perm(permset
, perm
);
243 if (p_result
== -1) {
244 perror("has_default_tag_perm (acl_get_perm)");
250 int remove_default_tag_perm(const char* path
,
253 /* Attempt to remove perm from tag. Returns one if successful, zero
254 if there was nothing to do, and -1 on errors. */
255 int hdta
= has_default_tag_acl(path
, tag
);
257 /* Failure or error. */
261 acl_permset_t permset
;
262 bool ps_result
= get_default_tag_permset(path
, tag
, &permset
);
264 if (ps_result
!= 1) {
265 /* Failure or error. */
269 int d_result
= acl_delete_perm(permset
, perm
);
270 if (d_result
== -1) {
271 perror("remove_default_tag_perm (acl_delete_perm)");
275 /* We've only removed perm from the permset; now we have to replace
278 int entry_result
= get_default_tag_entry(path
, tag
, &entry
);
280 if (entry_result
== -1) {
281 perror("remove_default_tag_perm (get_default_tag_entry)");
285 if (entry_result
== 1) {
287 int s_result
= acl_set_permset(entry
, permset
);
288 if (s_result
== -1) {
289 perror("remove_default_tag_perm (acl_set_permset)");
300 int remove_default_group_obj_execute(const char* path
) {
301 return remove_default_tag_perm(path
, ACL_GROUP_OBJ
, ACL_EXECUTE
);
305 int has_default_user_obj_read(const char* path
) {
306 return has_default_tag_perm(path
, ACL_USER_OBJ
, ACL_READ
);
309 int has_default_user_obj_write(const char* path
) {
310 return has_default_tag_perm(path
, ACL_USER_OBJ
, ACL_WRITE
);
313 int has_default_user_obj_execute(const char* path
) {
314 return has_default_tag_perm(path
, ACL_USER_OBJ
, ACL_EXECUTE
);
317 int has_default_group_obj_read(const char* path
) {
318 return has_default_tag_perm(path
, ACL_GROUP_OBJ
, ACL_READ
);
321 int has_default_group_obj_write(const char* path
) {
322 return has_default_tag_perm(path
, ACL_GROUP_OBJ
, ACL_WRITE
);
325 int has_default_group_obj_execute(const char* path
) {
326 return has_default_tag_perm(path
, ACL_GROUP_OBJ
, ACL_EXECUTE
);
329 int has_default_other_read(const char* path
) {
330 return has_default_tag_perm(path
, ACL_OTHER
, ACL_READ
);
333 int has_default_other_write(const char* path
) {
334 return has_default_tag_perm(path
, ACL_OTHER
, ACL_WRITE
);
337 int has_default_other_execute(const char* path
) {
338 return has_default_tag_perm(path
, ACL_OTHER
, ACL_EXECUTE
);
341 int has_default_mask_read(const char* path
) {
342 return has_default_tag_perm(path
, ACL_MASK
, ACL_READ
);
345 int has_default_mask_write(const char* path
) {
346 return has_default_tag_perm(path
, ACL_MASK
, ACL_WRITE
);
349 int has_default_mask_execute(const char* path
) {
350 return has_default_tag_perm(path
, ACL_MASK
, ACL_EXECUTE
);
354 int reapply_default_acl(const char* path
) {
355 /* If this is a normal file or directory (i.e. that has just been
356 created), we proceed to find its parent directory which will have
359 Returns one for success, zero for failure (i.e. no ACL), and -1
360 on unexpected errors. */
365 if (!is_regular_file(path
) && !is_directory(path
)) {
369 /* dirname mangles its argument */
370 char path_copy
[PATH_MAX
];
371 strncpy(path_copy
, path
, PATH_MAX
-1);
372 path_copy
[PATH_MAX
-1] = 0;
374 char* parent
= dirname(path_copy
);
375 if (!is_directory(parent
)) {
376 /* Make sure dirname() did what we think it did. */
380 /* This is the original mode of path. We will simply add permissions
381 to it, and then later reapply the result via chmod. */
382 mode_t path_mode
= get_mode(path
);
385 /* If parent has a default user ACL, apply it. */
386 if (has_default_user_obj_acl(parent
)) {
388 if (has_default_user_obj_read(parent
)) {
390 path_mode
|= S_IRUSR
;
393 /* Remove user read. */
394 path_mode
&= ~S_IRUSR
;
398 if (has_default_user_obj_write(parent
)) {
399 /* Add user write. */
400 path_mode
|= S_IWUSR
;
403 /* Remove user write. */
404 path_mode
&= ~S_IWUSR
;
408 /* We don't want to set the execute bit on via the ACL unless it
409 was on originally. */
410 if (!has_default_user_obj_execute(parent
)) {
411 /* Remove user execute. */
412 path_mode
&= ~S_IXUSR
;
417 /* Do the same thing with the other perms/ACL. */
418 if (has_default_other_acl(parent
)) {
420 if (has_default_other_read(parent
)) {
421 path_mode
|= S_IROTH
;
424 path_mode
&= ~S_IROTH
;
428 if (has_default_other_write(parent
)) {
429 path_mode
|= S_IWOTH
;
432 path_mode
&= ~S_IWOTH
;
436 /* We don't want to set the execute bit on via the ACL unless it
437 was on originally. */
438 if (!has_default_other_execute(parent
)) {
439 path_mode
&= ~S_IXOTH
;
444 if (has_minimal_default_acl(parent
)) {
445 /* With a minimal ACL, we'll repeat for the group bits what we
446 already did for the owner/other bits. */
447 if (has_default_group_obj_acl(parent
)) {
448 if (has_default_group_obj_read(parent
)) {
449 path_mode
|= S_IRGRP
;
452 path_mode
&= ~S_IRGRP
;
456 if (has_default_group_obj_write(parent
)) {
457 path_mode
|= S_IWGRP
;
460 path_mode
&= ~S_IWGRP
;
463 /* We don't want to set the execute bit on via the ACL unless it
464 was on originally. */
465 if (!has_default_group_obj_execute(parent
)) {
466 path_mode
&= ~S_IXGRP
;
471 /* The parent has an extended ACL. Extended ACLs use the mask
474 /* For the group bits, we'll use the ACL's mask instead of the group
475 object bits. If the default ACL had a group entry, it should
476 already have propagated (but might be masked). */
477 if (has_default_mask_read(parent
)) {
478 path_mode
|= S_IRGRP
;
481 if (has_default_mask_write(parent
)) {
482 path_mode
|= S_IWGRP
;
485 /* Honor the mask execute unconditionally. */
486 if (has_default_mask_execute(parent
)) {
487 /* This just adds the group execute bit, and doesn't actually
488 grant group execute permissions. */
489 path_mode
|= S_IXGRP
;
492 if (!mode_has_perm(path_mode
, S_IXGRP
)) {
493 /* The group ACL entry should already have been inherited from the
494 default ACL. If the source was not group executable, we want to
495 modify the destination so that it is not group executable
496 either. In the presence of ACLs, the group permissions come not
497 from the mode bits, but from the group:: ACL entry. So, to do
498 this, we remove the group::x entry. */
499 if (has_default_group_obj_execute(path
)) {
500 remove_default_group_obj_execute(path
);
505 int chmod_result
= chmod(path
, path_mode
);
506 if (chmod_result
== 0) {
515 int main(int argc
, char* argv
[]) {
516 const char* target
= argv
[1];
518 bool result
= reapply_default_acl(target
);