]>
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 tag_type
) {
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
== tag_type
) {
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_permset(const char* path
,
147 acl_permset_t
* output_perms
) {
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. */
159 int result
= acl_get_entry(defacl
, ACL_FIRST_ENTRY
, &entry
);
161 while (result
== 1) {
162 acl_tag_t tag
= ACL_UNDEFINED_TAG
;
163 int tag_result
= acl_get_tag_type(entry
, &tag
);
165 if (tag_result
== -1) {
166 perror("get_default_tag_permset (acl_get_tag_type)");
170 if (tag
== tag_type
) {
171 /* We found the right tag, now get the permset. */
172 int ps_result
= acl_get_permset(entry
, output_perms
);
173 if (ps_result
== -1) {
174 perror("get_default_tag_permset (acl_get_permset)");
177 if (ps_result
== 0) {
186 result
= acl_get_entry(defacl
, ACL_NEXT_ENTRY
, &entry
);
189 /* This catches both the initial acl_get_entry and the ones at the
192 perror("get_default_tag_permset (acl_get_entry)");
200 int has_default_tag_perm(const char* path
,
203 /* Check path to see if tag has the given perm. Returns one if it
204 does, zero if it doesn't (or there's no ACL), and -1 on unexpected
207 if (!has_default_tag_acl(path
, tag
)) {
211 acl_permset_t permset
;
212 bool ps_result
= get_default_tag_permset(path
, tag
, &permset
);
214 if (ps_result
!= 1) {
215 /* Failure or error. */
219 int p_result
= acl_get_perm(permset
, perm
);
220 if (p_result
== -1) {
221 perror("has_default_tag_perm (acl_get_perm)");
227 int has_default_user_obj_read(const char* path
) {
228 return has_default_tag_perm(path
, ACL_USER_OBJ
, ACL_READ
);
231 int has_default_user_obj_write(const char* path
) {
232 return has_default_tag_perm(path
, ACL_USER_OBJ
, ACL_WRITE
);
235 int has_default_user_obj_execute(const char* path
) {
236 return has_default_tag_perm(path
, ACL_USER_OBJ
, ACL_EXECUTE
);
239 int has_default_group_obj_read(const char* path
) {
240 return has_default_tag_perm(path
, ACL_GROUP_OBJ
, ACL_READ
);
243 int has_default_group_obj_write(const char* path
) {
244 return has_default_tag_perm(path
, ACL_GROUP_OBJ
, ACL_WRITE
);
247 int has_default_group_obj_execute(const char* path
) {
248 return has_default_tag_perm(path
, ACL_GROUP_OBJ
, ACL_EXECUTE
);
251 int has_default_other_read(const char* path
) {
252 return has_default_tag_perm(path
, ACL_OTHER
, ACL_READ
);
255 int has_default_other_write(const char* path
) {
256 return has_default_tag_perm(path
, ACL_OTHER
, ACL_WRITE
);
259 int has_default_other_execute(const char* path
) {
260 return has_default_tag_perm(path
, ACL_OTHER
, ACL_EXECUTE
);
263 int has_default_mask_read(const char* path
) {
264 return has_default_tag_perm(path
, ACL_MASK
, ACL_READ
);
267 int has_default_mask_write(const char* path
) {
268 return has_default_tag_perm(path
, ACL_MASK
, ACL_WRITE
);
271 int has_default_mask_execute(const char* path
) {
272 return has_default_tag_perm(path
, ACL_MASK
, ACL_EXECUTE
);
276 int reapply_default_acl(const char* path
) {
277 /* If this is a normal file or directory (i.e. that has just been
278 created), we proceed to find its parent directory which will have
281 Returns one for success, zero for failure (i.e. no ACL), and -1
282 on unexpected errors. */
287 if (!is_regular_file(path
) && !is_directory(path
)) {
291 /* dirname mangles its argument */
292 char path_copy
[PATH_MAX
];
293 strncpy(path_copy
, path
, PATH_MAX
-1);
294 path_copy
[PATH_MAX
-1] = 0;
296 char* parent
= dirname(path_copy
);
297 if (!is_directory(parent
)) {
298 /* Make sure dirname() did what we think it did. */
302 /* This is the original mode of path. We will simply add permissions
303 to it, and then later reapply the result via chmod. */
304 mode_t path_mode
= get_mode(path
);
307 /* If parent has a default user ACL, apply it to path via chmod. */
308 if (has_default_user_obj_read(parent
)) {
309 path_mode
|= S_IRUSR
;
312 if (has_default_user_obj_write(parent
)) {
313 path_mode
|= S_IWUSR
;
316 /* However, we don't want to set the execute bit on via the ACL
317 unless it was on originally. Furthermore, if the ACL denies
318 execute, we want to honor that. */
319 if (has_default_user_obj_acl(parent
)) {
320 if (!has_default_user_obj_execute(parent
)) {
321 /* It has a user ACL, but no user execute is granted. */
322 path_mode
&= ~S_IXUSR
;
327 /* Do the same thing with the other perms/ACL. */
328 if (has_default_other_read(parent
)) {
329 path_mode
|= S_IROTH
;
332 if (has_default_other_write(parent
)) {
333 path_mode
|= S_IWOTH
;
336 if (has_default_other_acl(parent
)) {
337 if (!has_default_other_execute(parent
)) {
338 /* It has an other ACL, but no other execute is granted. */
339 path_mode
&= ~S_IXOTH
;
343 if (has_minimal_default_acl(parent
)) {
344 /* With a minimal ACL, we'll repeat for the group bits what we
345 already did for the owner/other bits. */
346 if (has_default_group_obj_read(parent
)) {
347 path_mode
|= S_IRGRP
;
350 if (has_default_group_obj_write(parent
)) {
351 path_mode
|= S_IWGRP
;
354 if (has_default_group_obj_acl(parent
)) {
355 if (!has_default_group_obj_execute(parent
)) {
356 /* It has a group ACL, but no group execute is granted. */
357 path_mode
&= ~S_IXGRP
;
362 /* The parent has an extended ACL. Extended ACLs use the mask
365 /* For the group bits, we'll use the ACL's mask instead of the group
366 object bits. If the default ACL had a group entry, it should
367 already have propagated (but might be masked). */
368 if (has_default_mask_read(parent
)) {
369 path_mode
|= S_IRGRP
;
372 if (has_default_mask_write(parent
)) {
373 path_mode
|= S_IWGRP
;
376 /* Honor the mask execute unconditionally. */
377 if (has_default_mask_execute(parent
)) {
378 /* This just adds the group execute bit, and doesn't actually
379 grant group execute permissions. */
380 path_mode
|= S_IXGRP
;
383 if (!mode_has_perm(path_mode
, S_IXGRP
)) {
384 /* The group ACL entry should already have been inherited from the
385 default ACL. If the source was not group executable, we want to
386 modify the destination so that it is not group executable
387 either. In the presence of ACLs, the group permissions come not
388 from the mode bits, but from the group:: ACL entry. So, to do
389 this, we remove the group::x entry. */
390 if (has_default_group_obj_execute(path
)) {
391 /* remove_default_group_obj_execute(path);*/
396 int chmod_result
= chmod(path
, path_mode
);
397 if (chmod_result
== 0) {
406 int main(int argc
, char* argv
[]) {
407 const char* target
= argv
[1];
409 bool result
= reapply_default_acl(target
);
412 printf("Success.\n");