]>
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 bool has_default_acl(const char* path
) {
77 /* Return true if the given path has a default ACL, false
79 acl_t defacl
= acl_get_file(path
, ACL_TYPE_DEFAULT
);
81 if (defacl
== (acl_t
)NULL
) {
85 /* Used to store the entry if it exists, even though we don't care
89 int result
= acl_get_entry(defacl
, ACL_FIRST_ENTRY
, &dummy
);
92 /* There's a first entry in the default ACL. */
95 else if (result
== 0) {
99 perror("has_default_acl");
106 bool has_default_tag_acl(const char* path
, acl_tag_t tag_type
) {
107 /* Return true if the given path has a default ACL for the supplied
108 tag, false otherwise. */
109 acl_t defacl
= acl_get_file(path
, ACL_TYPE_DEFAULT
);
111 if (defacl
== (acl_t
)NULL
) {
116 int result
= acl_get_entry(defacl
, ACL_FIRST_ENTRY
, &entry
);
118 while (result
== 1) {
119 acl_tag_t tag
= ACL_UNDEFINED_TAG
;
120 int tag_result
= acl_get_tag_type(entry
, &tag
);
122 if (tag_result
== -1) {
123 perror("has_default_tag_acl - acl_get_tag_type");
127 if (tag
== tag_type
) {
132 result
= acl_get_entry(defacl
, ACL_NEXT_ENTRY
, &entry
);
139 bool has_default_user_obj_acl(const char* path
) {
140 return has_default_tag_acl(path
, ACL_USER_OBJ
);
143 bool has_default_group_obj_acl(const char* path
) {
144 return has_default_tag_acl(path
, ACL_GROUP_OBJ
);
147 bool has_default_other_acl(const char* path
) {
148 return has_default_tag_acl(path
, ACL_OTHER
);
152 bool get_default_tag_permset(const char* path
,
154 acl_permset_t
* output_perms
) {
155 /* Returns true if successful or false on error */
156 acl_t defacl
= acl_get_file(path
, ACL_TYPE_DEFAULT
);
158 if (defacl
== (acl_t
)NULL
) {
159 /* Follow the acl_foo convention of -1 == error. */
165 int result
= acl_get_entry(defacl
, ACL_FIRST_ENTRY
, &entry
);
167 while (result
== 1) {
168 acl_tag_t tag
= ACL_UNDEFINED_TAG
;
169 int tag_result
= acl_get_tag_type(entry
, &tag
);
171 if (tag_result
== -1) {
172 perror("get_default_tag_permset");
176 if (tag
== tag_type
) {
177 /* We found the right tag, now get the permset. */
178 int ps_result
= acl_get_permset(entry
, output_perms
);
179 if (ps_result
== 0) {
188 result
= acl_get_entry(defacl
, ACL_NEXT_ENTRY
, &entry
);
195 bool get_default_user_obj_permset(const char* path
,
196 acl_permset_t
* output_perms
) {
197 return get_default_tag_permset(path
, ACL_USER_OBJ
, output_perms
);
200 bool get_default_group_obj_permset(const char* path
,
201 acl_permset_t
* output_perms
) {
202 return get_default_tag_permset(path
, ACL_GROUP_OBJ
, output_perms
);
205 bool get_default_other_permset(const char* path
,
206 acl_permset_t
* output_perms
) {
207 return get_default_tag_permset(path
, ACL_OTHER
, output_perms
);
212 bool has_default_tag_perm(const char* path
,
215 /* Check path to see if tag has perm. */
217 if (!has_default_tag_acl(path
, tag
)) {
221 acl_permset_t permset
;
222 bool ps_result
= get_default_tag_permset(path
, tag
, &permset
);
228 int p_result
= acl_get_perm(permset
, perm
);
237 bool has_default_user_obj_read(const char* path
) {
238 return has_default_tag_perm(path
, ACL_USER_OBJ
, ACL_READ
);
241 bool has_default_user_obj_write(const char* path
) {
242 return has_default_tag_perm(path
, ACL_USER_OBJ
, ACL_WRITE
);
245 bool has_default_user_obj_execute(const char* path
) {
246 return has_default_tag_perm(path
, ACL_USER_OBJ
, ACL_EXECUTE
);
249 bool has_default_group_obj_read(const char* path
) {
250 return has_default_tag_perm(path
, ACL_GROUP_OBJ
, ACL_READ
);
253 bool has_default_group_obj_write(const char* path
) {
254 return has_default_tag_perm(path
, ACL_GROUP_OBJ
, ACL_WRITE
);
257 bool has_default_group_obj_execute(const char* path
) {
258 return has_default_tag_perm(path
, ACL_GROUP_OBJ
, ACL_EXECUTE
);
261 bool has_default_other_read(const char* path
) {
262 return has_default_tag_perm(path
, ACL_OTHER
, ACL_READ
);
265 bool has_default_other_write(const char* path
) {
266 return has_default_tag_perm(path
, ACL_OTHER
, ACL_WRITE
);
269 bool has_default_other_execute(const char* path
) {
270 return has_default_tag_perm(path
, ACL_OTHER
, ACL_EXECUTE
);
273 bool has_default_mask_read(const char* path
) {
274 return has_default_tag_perm(path
, ACL_MASK
, ACL_READ
);
277 bool has_default_mask_write(const char* path
) {
278 return has_default_tag_perm(path
, ACL_MASK
, ACL_WRITE
);
281 bool has_default_mask_execute(const char* path
) {
282 return has_default_tag_perm(path
, ACL_MASK
, ACL_EXECUTE
);
286 bool reapply_default_acl(const char* path
) {
287 /* If this is a normal file or directory (i.e. that has just been
288 created), we proceed to find its parent directory which will have
294 if (!is_regular_file(path
) && !is_directory(path
)) {
298 /* dirname mangles its argument */
299 char path_copy
[PATH_MAX
];
300 strncpy(path_copy
, path
, PATH_MAX
-1);
301 path_copy
[PATH_MAX
-1] = 0;
303 char* parent
= dirname(path_copy
);
304 if (!is_directory(parent
)) {
305 /* Make sure dirname() did what we think it did. */
309 /* This is the original mode of path. We will simply add permissions
310 to it, and then later reapply the result via chmod. */
311 mode_t path_mode
= get_mode(path
);
314 /* If parent has a default user ACL, apply it to path via chmod. */
315 if (has_default_user_obj_read(parent
)) {
316 path_mode
|= S_IRUSR
;
319 if (has_default_user_obj_write(parent
)) {
320 path_mode
|= S_IWUSR
;
323 /* However, we don't want to set the execute bit on via the ACL
324 unless it was on originally. Furthermore, if the ACL denies
325 execute, we want to honor that. */
326 if (has_default_user_obj_acl(parent
)) {
327 if (!has_default_user_obj_execute(parent
)) {
328 /* It has a user ACL, but no user execute is granted. */
329 path_mode
&= ~S_IXUSR
;
334 /* Do the same thing with the other perms/ACL. */
335 if (has_default_other_read(parent
)) {
336 path_mode
|= S_IROTH
;
339 if (has_default_other_write(parent
)) {
340 path_mode
|= S_IWOTH
;
343 if (has_default_other_acl(parent
)) {
344 if (!has_default_other_execute(parent
)) {
345 /* It has an other ACL, but no other execute is granted. */
346 path_mode
&= ~S_IXOTH
;
350 /* For the group bits, we'll use the ACL's mask instead of the group
351 object bits. If the default ACL had a group entry, it should
352 already have propagated (but might be masked). */
353 if (has_default_mask_read(parent
)) {
354 path_mode
|= S_IRGRP
;
357 if (has_default_mask_write(parent
)) {
358 path_mode
|= S_IWGRP
;
361 /* Honor the mask execute unconditionally. */
362 if (has_default_mask_execute(parent
)) {
363 if (mode_has_perm(path_mode
, S_IXGRP
)) {
364 path_mode
|= S_IXGRP
;
368 int result
= chmod(path
, path_mode
);
378 int main(int argc
, char* argv
[]) {
379 const char* target
= argv
[1];
381 bool result
= reapply_default_acl(target
);
384 printf("Success.\n");