]> gitweb.michael.orlitzky.com - apply-default-acl.git/blob - src/aclq.c
Implement the reapply_default_acl function.
[apply-default-acl.git] / src / aclq.c
1 #include <errno.h>
2 #include <libgen.h> /* dirname() */
3 #include <limits.h> /* PATH_MAX */
4 #include <stdbool.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <sys/stat.h>
9 #include <unistd.h>
10
11 /* ACLs */
12 #include <sys/types.h>
13 #include <sys/acl.h>
14
15
16 mode_t get_mode(const char* path) {
17 if (path == NULL) {
18 errno = ENOENT;
19 return -1;
20 }
21
22 struct stat s;
23 int result = stat(path, &s);
24
25 if (result == 0) {
26 return s.st_mode;
27 }
28 else {
29 /* errno will be set already by stat() */
30 return result;
31 }
32 }
33
34 bool mode_has_perm(mode_t mode, int perm) {
35 if (mode & perm) {
36 return true;
37 }
38 else {
39 return false;
40 }
41 }
42
43
44 bool is_regular_file(const char* path) {
45 if (path == NULL) {
46 return false;
47 }
48
49 struct stat s;
50 int result = stat(path, &s);
51 if (result == 0) {
52 return S_ISREG(s.st_mode);
53 }
54 else {
55 return false;
56 }
57 }
58
59 bool is_directory(const char* path) {
60 if (path == NULL) {
61 return false;
62 }
63
64 struct stat s;
65 int result = stat(path, &s);
66 if (result == 0) {
67 return S_ISDIR(s.st_mode);
68 }
69 else {
70 return false;
71 }
72 }
73
74
75 bool has_default_acl(const char* path) {
76 /* Return true if the given path has a default ACL, false
77 otherwise. */
78 acl_t defacl = acl_get_file(path, ACL_TYPE_DEFAULT);
79
80 if (defacl == (acl_t)NULL) {
81 return false;
82 }
83
84 /* Used to store the entry if it exists, even though we don't care
85 what it is. */
86 acl_entry_t dummy;
87
88 int result = acl_get_entry(defacl, ACL_FIRST_ENTRY, &dummy);
89
90 if (result == 1) {
91 /* There's a first entry in the default ACL. */
92 return true;
93 }
94 else if (result == 0) {
95 return false;
96 }
97 else {
98 perror("has_default_acl");
99 return false;
100 }
101 }
102
103
104
105 bool has_default_tag_acl(const char* path, acl_tag_t tag_type) {
106 /* Return true if the given path has a default ACL for the supplied
107 tag, false otherwise. */
108 acl_t defacl = acl_get_file(path, ACL_TYPE_DEFAULT);
109
110 if (defacl == (acl_t)NULL) {
111 return false;
112 }
113
114 acl_entry_t entry;
115 int result = acl_get_entry(defacl, ACL_FIRST_ENTRY, &entry);
116
117 while (result == 1) {
118 acl_tag_t tag = ACL_UNDEFINED_TAG;
119 int tag_result = acl_get_tag_type(entry, &tag);
120
121 if (tag_result == -1) {
122 perror("has_default_tag_acl - acl_get_tag_type");
123 return false;
124 }
125 else {
126 if (tag == tag_type) {
127 return true;
128 }
129 }
130
131 result = acl_get_entry(defacl, ACL_NEXT_ENTRY, &entry);
132 }
133
134 return false;
135 }
136
137
138 bool has_default_user_obj_acl(const char* path) {
139 return has_default_tag_acl(path, ACL_USER_OBJ);
140 }
141
142 bool has_default_group_obj_acl(const char* path) {
143 return has_default_tag_acl(path, ACL_GROUP_OBJ);
144 }
145
146 bool has_default_other_acl(const char* path) {
147 return has_default_tag_acl(path, ACL_OTHER);
148 }
149
150
151 bool get_default_tag_permset(const char* path,
152 acl_tag_t tag_type,
153 acl_permset_t* output_perms) {
154 /* Returns true if successful or false on error */
155 acl_t defacl = acl_get_file(path, ACL_TYPE_DEFAULT);
156
157 if (defacl == (acl_t)NULL) {
158 /* Follow the acl_foo convention of -1 == error. */
159 errno = EINVAL;
160 return false;
161 }
162
163 acl_entry_t entry;
164 int result = acl_get_entry(defacl, ACL_FIRST_ENTRY, &entry);
165
166 while (result == 1) {
167 acl_tag_t tag = ACL_UNDEFINED_TAG;
168 int tag_result = acl_get_tag_type(entry, &tag);
169
170 if (tag_result == -1) {
171 perror("get_default_tag_permset");
172 return false;
173 }
174 else {
175 if (tag == tag_type) {
176 /* We found the right tag, now get the permset. */
177 int ps_result = acl_get_permset(entry, output_perms);
178 if (ps_result == 0) {
179 return true;
180 }
181 else {
182 return false;
183 }
184 }
185 }
186
187 result = acl_get_entry(defacl, ACL_NEXT_ENTRY, &entry);
188 }
189
190 errno = EINVAL;
191 return false;
192 }
193
194 bool get_default_user_obj_permset(const char* path,
195 acl_permset_t* output_perms) {
196 return get_default_tag_permset(path, ACL_USER_OBJ, output_perms);
197 }
198
199 bool get_default_group_obj_permset(const char* path,
200 acl_permset_t* output_perms) {
201 return get_default_tag_permset(path, ACL_GROUP_OBJ, output_perms);
202 }
203
204 bool get_default_other_permset(const char* path,
205 acl_permset_t* output_perms) {
206 return get_default_tag_permset(path, ACL_OTHER, output_perms);
207 }
208
209
210
211 bool has_default_tag_perm(const char* path,
212 acl_tag_t tag,
213 acl_perm_t perm) {
214 /* Check path to see if tag has perm. */
215
216 if (!has_default_tag_acl(path, tag)) {
217 return false;
218 }
219
220 acl_permset_t permset;
221 bool ps_result = get_default_tag_permset(path, tag, &permset);
222
223 if (!ps_result) {
224 return false;
225 }
226
227 int p_result = acl_get_perm(permset, perm);
228 if (p_result == 1) {
229 return true;
230 }
231 else {
232 return false;
233 }
234 }
235
236 bool has_default_user_obj_read(const char* path) {
237 return has_default_tag_perm(path, ACL_USER_OBJ, ACL_READ);
238 }
239
240 bool has_default_user_obj_write(const char* path) {
241 return has_default_tag_perm(path, ACL_USER_OBJ, ACL_WRITE);
242 }
243
244 bool has_default_user_obj_execute(const char* path) {
245 return has_default_tag_perm(path, ACL_USER_OBJ, ACL_EXECUTE);
246 }
247
248 bool has_default_group_obj_read(const char* path) {
249 return has_default_tag_perm(path, ACL_GROUP_OBJ, ACL_READ);
250 }
251
252 bool has_default_group_obj_write(const char* path) {
253 return has_default_tag_perm(path, ACL_GROUP_OBJ, ACL_WRITE);
254 }
255
256 bool has_default_group_obj_execute(const char* path) {
257 return has_default_tag_perm(path, ACL_GROUP_OBJ, ACL_EXECUTE);
258 }
259
260 bool has_default_other_read(const char* path) {
261 return has_default_tag_perm(path, ACL_OTHER, ACL_READ);
262 }
263
264 bool has_default_other_write(const char* path) {
265 return has_default_tag_perm(path, ACL_OTHER, ACL_WRITE);
266 }
267
268 bool has_default_other_execute(const char* path) {
269 return has_default_tag_perm(path, ACL_OTHER, ACL_EXECUTE);
270 }
271
272 bool has_default_mask_read(const char* path) {
273 return has_default_tag_perm(path, ACL_MASK, ACL_READ);
274 }
275
276 bool has_default_mask_write(const char* path) {
277 return has_default_tag_perm(path, ACL_MASK, ACL_WRITE);
278 }
279
280 bool has_default_mask_execute(const char* path) {
281 return has_default_tag_perm(path, ACL_MASK, ACL_EXECUTE);
282 }
283
284
285 bool reapply_default_acl(const char* path) {
286 /* If this is a normal file or directory (i.e. that has just been
287 created), we proceed to find its parent directory which will have
288 a default ACL. */
289 if (path == NULL) {
290 return false;
291 }
292
293 if (!is_regular_file(path) && !is_directory(path)) {
294 return false;
295 }
296
297 /* dirname mangles its argument */
298 char path_copy[PATH_MAX];
299 strncpy(path_copy, path, PATH_MAX-1);
300 path_copy[PATH_MAX] = 0;
301
302 char* parent = dirname(path_copy);
303 if (!is_directory(parent)) {
304 /* Make sure dirname() did what we think it did. */
305 return false;
306 }
307
308 /* This is the original mode of path. We will simply add permissions
309 to it, and then later reapply the result via chmod. */
310 mode_t path_mode = get_mode(path);
311
312
313 /* If parent has a default user ACL, apply it to path via chmod. */
314 if (has_default_user_obj_read(parent)) {
315 path_mode |= S_IRUSR;
316 }
317
318 if (has_default_user_obj_write(parent)) {
319 path_mode |= S_IWUSR;
320 }
321
322 /* However, we don't want to set the execute bit on via the ACL
323 unless it was on originally. Furthermore, if the ACL denies
324 execute, we want to honor that. */
325 if (has_default_user_obj_acl(parent)) {
326 if (!has_default_user_obj_execute(parent)) {
327 /* It has a user ACL, but no user execute is granted. */
328 path_mode &= ~S_IXUSR;
329 }
330 }
331
332
333 /* Do the same thing with the other perms/ACL. */
334 if (has_default_other_read(parent)) {
335 path_mode |= S_IROTH;
336 }
337
338 if (has_default_other_write(parent)) {
339 path_mode |= S_IWOTH;
340 }
341
342 if (has_default_other_acl(parent)) {
343 if (!has_default_other_execute(parent)) {
344 /* It has an other ACL, but no other execute is granted. */
345 path_mode &= ~S_IXOTH;
346 }
347 }
348
349 /* For the group bits, we'll use the ACL's mask instead of the group
350 object bits. If the default ACL had a group entry, it should
351 already have propagated (but might be masked. */
352 if (has_default_mask_read(parent)) {
353 path_mode |= S_IRGRP;
354 }
355
356 if (has_default_mask_write(parent)) {
357 path_mode |= S_IWGRP;
358 }
359
360 /* Honor the mask execute unconditionally. */
361 if (has_default_mask_execute(parent)) {
362 if (mode_has_perm(path_mode, S_IXGRP)) {
363 path_mode |= S_IXGRP;
364 }
365 }
366
367 int result = chmod(path, path_mode);
368 if (result == 0) {
369 return true;
370 }
371 else {
372 return false;
373 }
374 }
375
376
377 int main(int argc, char* argv[]) {
378 const char* target = argv[1];
379
380 bool result = reapply_default_acl(target);
381
382 if (result) {
383 printf("Success.\n");
384 return EXIT_SUCCESS;
385 }
386 else {
387 return EXIT_FAILURE;
388 }
389 }