]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
f4b47811 | 2 | |
ca78ad1d | 3 | #include <sys/stat.h> |
f4b47811 | 4 | |
b1d4f8e1 | 5 | #include "acl-util.h" |
cf0fbc49 | 6 | #include "alloc-util.h" |
f76ce81b | 7 | #include "errno-util.h" |
6553db60 | 8 | #include "extract-word.h" |
07630cea | 9 | #include "string-util.h" |
478c8269 | 10 | #include "strv.h" |
b1d4f8e1 | 11 | #include "user-util.h" |
f4b47811 | 12 | |
f76ce81b LP |
13 | #if HAVE_ACL |
14 | ||
003c6faf | 15 | static int acl_find_uid(acl_t acl, uid_t uid, acl_entry_t *ret_entry) { |
f4b47811 | 16 | acl_entry_t i; |
dd4105b0 | 17 | int r; |
f4b47811 LP |
18 | |
19 | assert(acl); | |
71281a76 LP |
20 | assert(uid_is_valid(uid)); |
21 | assert(ret_entry); | |
f4b47811 | 22 | |
dd4105b0 ZJS |
23 | for (r = acl_get_entry(acl, ACL_FIRST_ENTRY, &i); |
24 | r > 0; | |
25 | r = acl_get_entry(acl, ACL_NEXT_ENTRY, &i)) { | |
f4b47811 LP |
26 | |
27 | acl_tag_t tag; | |
28 | uid_t *u; | |
29 | bool b; | |
30 | ||
31 | if (acl_get_tag_type(i, &tag) < 0) | |
32 | return -errno; | |
33 | ||
34 | if (tag != ACL_USER) | |
35 | continue; | |
36 | ||
37 | u = acl_get_qualifier(i); | |
38 | if (!u) | |
39 | return -errno; | |
40 | ||
41 | b = *u == uid; | |
42 | acl_free(u); | |
43 | ||
44 | if (b) { | |
71281a76 | 45 | *ret_entry = i; |
f4b47811 LP |
46 | return 1; |
47 | } | |
48 | } | |
dd4105b0 | 49 | if (r < 0) |
f4b47811 LP |
50 | return -errno; |
51 | ||
71281a76 | 52 | *ret_entry = NULL; |
f4b47811 LP |
53 | return 0; |
54 | } | |
478c8269 | 55 | |
23ad4dd8 JAS |
56 | int calc_acl_mask_if_needed(acl_t *acl_p) { |
57 | acl_entry_t i; | |
dd4105b0 | 58 | int r; |
6debb398 | 59 | bool need = false; |
23ad4dd8 JAS |
60 | |
61 | assert(acl_p); | |
62 | ||
dd4105b0 ZJS |
63 | for (r = acl_get_entry(*acl_p, ACL_FIRST_ENTRY, &i); |
64 | r > 0; | |
65 | r = acl_get_entry(*acl_p, ACL_NEXT_ENTRY, &i)) { | |
23ad4dd8 JAS |
66 | acl_tag_t tag; |
67 | ||
68 | if (acl_get_tag_type(i, &tag) < 0) | |
69 | return -errno; | |
70 | ||
71 | if (tag == ACL_MASK) | |
72 | return 0; | |
e346512c | 73 | |
6debb398 ZJS |
74 | if (IN_SET(tag, ACL_USER, ACL_GROUP)) |
75 | need = true; | |
23ad4dd8 | 76 | } |
dd4105b0 | 77 | if (r < 0) |
23ad4dd8 JAS |
78 | return -errno; |
79 | ||
6debb398 ZJS |
80 | if (need && acl_calc_mask(acl_p) < 0) |
81 | return -errno; | |
82 | ||
83 | return need; | |
dd4105b0 ZJS |
84 | } |
85 | ||
86 | int add_base_acls_if_needed(acl_t *acl_p, const char *path) { | |
87 | acl_entry_t i; | |
88 | int r; | |
89 | bool have_user_obj = false, have_group_obj = false, have_other = false; | |
90 | struct stat st; | |
91 | _cleanup_(acl_freep) acl_t basic = NULL; | |
92 | ||
93 | assert(acl_p); | |
cfef0734 | 94 | assert(path); |
dd4105b0 ZJS |
95 | |
96 | for (r = acl_get_entry(*acl_p, ACL_FIRST_ENTRY, &i); | |
97 | r > 0; | |
98 | r = acl_get_entry(*acl_p, ACL_NEXT_ENTRY, &i)) { | |
99 | acl_tag_t tag; | |
100 | ||
101 | if (acl_get_tag_type(i, &tag) < 0) | |
102 | return -errno; | |
103 | ||
104 | if (tag == ACL_USER_OBJ) | |
105 | have_user_obj = true; | |
106 | else if (tag == ACL_GROUP_OBJ) | |
107 | have_group_obj = true; | |
108 | else if (tag == ACL_OTHER) | |
109 | have_other = true; | |
110 | if (have_user_obj && have_group_obj && have_other) | |
111 | return 0; | |
112 | } | |
113 | if (r < 0) | |
114 | return -errno; | |
23ad4dd8 | 115 | |
dd4105b0 ZJS |
116 | r = stat(path, &st); |
117 | if (r < 0) | |
118 | return -errno; | |
119 | ||
120 | basic = acl_from_mode(st.st_mode); | |
121 | if (!basic) | |
122 | return -errno; | |
123 | ||
124 | for (r = acl_get_entry(basic, ACL_FIRST_ENTRY, &i); | |
125 | r > 0; | |
126 | r = acl_get_entry(basic, ACL_NEXT_ENTRY, &i)) { | |
127 | acl_tag_t tag; | |
128 | acl_entry_t dst; | |
129 | ||
130 | if (acl_get_tag_type(i, &tag) < 0) | |
131 | return -errno; | |
132 | ||
133 | if ((tag == ACL_USER_OBJ && have_user_obj) || | |
134 | (tag == ACL_GROUP_OBJ && have_group_obj) || | |
135 | (tag == ACL_OTHER && have_other)) | |
136 | continue; | |
137 | ||
138 | r = acl_create_entry(acl_p, &dst); | |
139 | if (r < 0) | |
140 | return -errno; | |
141 | ||
142 | r = acl_copy_entry(dst, i); | |
143 | if (r < 0) | |
144 | return -errno; | |
145 | } | |
146 | if (r < 0) | |
147 | return -errno; | |
23ad4dd8 JAS |
148 | return 0; |
149 | } | |
150 | ||
e346512c LP |
151 | int acl_search_groups(const char *path, char ***ret_groups) { |
152 | _cleanup_strv_free_ char **g = NULL; | |
29d87223 | 153 | _cleanup_(acl_freep) acl_t acl = NULL; |
e346512c LP |
154 | bool ret = false; |
155 | acl_entry_t entry; | |
156 | int r; | |
478c8269 ZJS |
157 | |
158 | assert(path); | |
478c8269 ZJS |
159 | |
160 | acl = acl_get_file(path, ACL_TYPE_DEFAULT); | |
e346512c LP |
161 | if (!acl) |
162 | return -errno; | |
478c8269 | 163 | |
e346512c LP |
164 | r = acl_get_entry(acl, ACL_FIRST_ENTRY, &entry); |
165 | for (;;) { | |
166 | _cleanup_(acl_free_gid_tpp) gid_t *gid = NULL; | |
167 | acl_tag_t tag; | |
478c8269 | 168 | |
e346512c LP |
169 | if (r < 0) |
170 | return -errno; | |
171 | if (r == 0) | |
172 | break; | |
478c8269 | 173 | |
e346512c LP |
174 | if (acl_get_tag_type(entry, &tag) < 0) |
175 | return -errno; | |
176 | ||
177 | if (tag != ACL_GROUP) | |
178 | goto next; | |
179 | ||
180 | gid = acl_get_qualifier(entry); | |
181 | if (!gid) | |
182 | return -errno; | |
478c8269 | 183 | |
e346512c LP |
184 | if (in_gid(*gid) > 0) { |
185 | if (!ret_groups) | |
186 | return true; | |
187 | ||
188 | ret = true; | |
189 | } | |
190 | ||
191 | if (ret_groups) { | |
192 | char *name; | |
478c8269 ZJS |
193 | |
194 | name = gid_to_name(*gid); | |
e346512c LP |
195 | if (!name) |
196 | return -ENOMEM; | |
197 | ||
198 | r = strv_consume(&g, name); | |
199 | if (r < 0) | |
200 | return r; | |
478c8269 ZJS |
201 | } |
202 | ||
e346512c LP |
203 | next: |
204 | r = acl_get_entry(acl, ACL_NEXT_ENTRY, &entry); | |
478c8269 ZJS |
205 | } |
206 | ||
1cc6c93a YW |
207 | if (ret_groups) |
208 | *ret_groups = TAKE_PTR(g); | |
e346512c LP |
209 | |
210 | return ret; | |
478c8269 | 211 | } |
f8eeeaf9 | 212 | |
26d98cdd MY |
213 | int parse_acl( |
214 | const char *text, | |
215 | acl_t *ret_acl_access, | |
216 | acl_t *ret_acl_access_exec, /* extra rules to apply to inodes subject to uppercase X handling */ | |
217 | acl_t *ret_acl_default, | |
218 | bool want_mask) { | |
219 | ||
220 | _cleanup_strv_free_ char **a = NULL, **e = NULL, **d = NULL, **split = NULL; | |
221 | _cleanup_(acl_freep) acl_t a_acl = NULL, e_acl = NULL, d_acl = NULL; | |
222 | int r; | |
f8eeeaf9 | 223 | |
cfef0734 YW |
224 | assert(text); |
225 | assert(ret_acl_access); | |
26d98cdd | 226 | assert(ret_acl_access_exec); |
cfef0734 YW |
227 | assert(ret_acl_default); |
228 | ||
f8eeeaf9 ZJS |
229 | split = strv_split(text, ","); |
230 | if (!split) | |
e738c945 | 231 | return -ENOMEM; |
f8eeeaf9 ZJS |
232 | |
233 | STRV_FOREACH(entry, split) { | |
26d98cdd MY |
234 | _cleanup_strv_free_ char **entry_split = NULL; |
235 | _cleanup_free_ char *entry_join = NULL; | |
236 | int n; | |
237 | ||
238 | n = strv_split_full(&entry_split, *entry, ":", EXTRACT_DONT_COALESCE_SEPARATORS|EXTRACT_RETAIN_ESCAPE); | |
239 | if (n < 0) | |
240 | return n; | |
241 | ||
242 | if (n < 3 || n > 4) | |
243 | return -EINVAL; | |
244 | ||
245 | string_replace_char(entry_split[n-1], 'X', 'x'); | |
246 | ||
247 | if (n == 4) { | |
248 | if (!STR_IN_SET(entry_split[0], "default", "d")) | |
249 | return -EINVAL; | |
f8eeeaf9 | 250 | |
26d98cdd MY |
251 | entry_join = strv_join(entry_split + 1, ":"); |
252 | if (!entry_join) | |
253 | return -ENOMEM; | |
254 | ||
255 | r = strv_consume(&d, TAKE_PTR(entry_join)); | |
256 | } else { /* n == 3 */ | |
257 | entry_join = strv_join(entry_split, ":"); | |
258 | if (!entry_join) | |
259 | return -ENOMEM; | |
260 | ||
261 | if (!streq(*entry, entry_join)) | |
262 | r = strv_consume(&e, TAKE_PTR(entry_join)); | |
263 | else | |
264 | r = strv_consume(&a, TAKE_PTR(entry_join)); | |
265 | } | |
e738c945 LP |
266 | if (r < 0) |
267 | return r; | |
f8eeeaf9 | 268 | } |
f8eeeaf9 ZJS |
269 | |
270 | if (!strv_isempty(a)) { | |
c2b2df60 | 271 | _cleanup_free_ char *join = NULL; |
f8eeeaf9 ZJS |
272 | |
273 | join = strv_join(a, ","); | |
274 | if (!join) | |
275 | return -ENOMEM; | |
276 | ||
277 | a_acl = acl_from_text(join); | |
278 | if (!a_acl) | |
e738c945 | 279 | return -errno; |
f8eeeaf9 | 280 | |
50d9e46d ZJS |
281 | if (want_mask) { |
282 | r = calc_acl_mask_if_needed(&a_acl); | |
283 | if (r < 0) | |
284 | return r; | |
285 | } | |
f8eeeaf9 ZJS |
286 | } |
287 | ||
26d98cdd MY |
288 | if (!strv_isempty(e)) { |
289 | _cleanup_free_ char *join = NULL; | |
290 | ||
291 | join = strv_join(e, ","); | |
292 | if (!join) | |
293 | return -ENOMEM; | |
294 | ||
295 | e_acl = acl_from_text(join); | |
296 | if (!e_acl) | |
297 | return -errno; | |
298 | ||
299 | /* The mask must be calculated after deciding whether the execute bit should be set. */ | |
300 | } | |
301 | ||
f8eeeaf9 | 302 | if (!strv_isempty(d)) { |
c2b2df60 | 303 | _cleanup_free_ char *join = NULL; |
f8eeeaf9 ZJS |
304 | |
305 | join = strv_join(d, ","); | |
306 | if (!join) | |
307 | return -ENOMEM; | |
308 | ||
309 | d_acl = acl_from_text(join); | |
310 | if (!d_acl) | |
e738c945 | 311 | return -errno; |
f8eeeaf9 | 312 | |
50d9e46d ZJS |
313 | if (want_mask) { |
314 | r = calc_acl_mask_if_needed(&d_acl); | |
315 | if (r < 0) | |
316 | return r; | |
317 | } | |
f8eeeaf9 ZJS |
318 | } |
319 | ||
cfef0734 | 320 | *ret_acl_access = TAKE_PTR(a_acl); |
26d98cdd | 321 | *ret_acl_access_exec = TAKE_PTR(e_acl); |
cfef0734 | 322 | *ret_acl_default = TAKE_PTR(d_acl); |
e738c945 | 323 | |
f8eeeaf9 ZJS |
324 | return 0; |
325 | } | |
50d9e46d | 326 | |
1c73f3bc ZJS |
327 | static int acl_entry_equal(acl_entry_t a, acl_entry_t b) { |
328 | acl_tag_t tag_a, tag_b; | |
329 | ||
330 | if (acl_get_tag_type(a, &tag_a) < 0) | |
331 | return -errno; | |
332 | ||
333 | if (acl_get_tag_type(b, &tag_b) < 0) | |
334 | return -errno; | |
335 | ||
336 | if (tag_a != tag_b) | |
337 | return false; | |
338 | ||
339 | switch (tag_a) { | |
340 | case ACL_USER_OBJ: | |
341 | case ACL_GROUP_OBJ: | |
342 | case ACL_MASK: | |
343 | case ACL_OTHER: | |
344 | /* can have only one of those */ | |
345 | return true; | |
346 | case ACL_USER: { | |
76dcbc49 | 347 | _cleanup_(acl_free_uid_tpp) uid_t *uid_a = NULL, *uid_b = NULL; |
1c73f3bc ZJS |
348 | |
349 | uid_a = acl_get_qualifier(a); | |
350 | if (!uid_a) | |
351 | return -errno; | |
352 | ||
353 | uid_b = acl_get_qualifier(b); | |
354 | if (!uid_b) | |
355 | return -errno; | |
356 | ||
357 | return *uid_a == *uid_b; | |
358 | } | |
359 | case ACL_GROUP: { | |
76dcbc49 | 360 | _cleanup_(acl_free_gid_tpp) gid_t *gid_a = NULL, *gid_b = NULL; |
1c73f3bc ZJS |
361 | |
362 | gid_a = acl_get_qualifier(a); | |
363 | if (!gid_a) | |
364 | return -errno; | |
365 | ||
366 | gid_b = acl_get_qualifier(b); | |
367 | if (!gid_b) | |
368 | return -errno; | |
369 | ||
370 | return *gid_a == *gid_b; | |
371 | } | |
372 | default: | |
04499a70 | 373 | assert_not_reached(); |
1c73f3bc ZJS |
374 | } |
375 | } | |
376 | ||
cfef0734 | 377 | static int find_acl_entry(acl_t acl, acl_entry_t entry, acl_entry_t *ret) { |
1c73f3bc ZJS |
378 | acl_entry_t i; |
379 | int r; | |
380 | ||
381 | for (r = acl_get_entry(acl, ACL_FIRST_ENTRY, &i); | |
382 | r > 0; | |
383 | r = acl_get_entry(acl, ACL_NEXT_ENTRY, &i)) { | |
384 | ||
385 | r = acl_entry_equal(i, entry); | |
386 | if (r < 0) | |
387 | return r; | |
388 | if (r > 0) { | |
cfef0734 YW |
389 | if (ret) |
390 | *ret = i; | |
391 | return 0; | |
1c73f3bc ZJS |
392 | } |
393 | } | |
394 | if (r < 0) | |
395 | return -errno; | |
cfef0734 YW |
396 | |
397 | return -ENOENT; | |
1c73f3bc ZJS |
398 | } |
399 | ||
cfef0734 YW |
400 | int acls_for_file(const char *path, acl_type_t type, acl_t acl, acl_t *ret) { |
401 | _cleanup_(acl_freep) acl_t applied = NULL; | |
50d9e46d | 402 | acl_entry_t i; |
dd4105b0 | 403 | int r; |
50d9e46d | 404 | |
cfef0734 YW |
405 | assert(path); |
406 | ||
407 | applied = acl_get_file(path, type); | |
408 | if (!applied) | |
50d9e46d ZJS |
409 | return -errno; |
410 | ||
cfef0734 | 411 | for (r = acl_get_entry(acl, ACL_FIRST_ENTRY, &i); |
dd4105b0 | 412 | r > 0; |
cfef0734 | 413 | r = acl_get_entry(acl, ACL_NEXT_ENTRY, &i)) { |
50d9e46d ZJS |
414 | |
415 | acl_entry_t j; | |
416 | ||
cfef0734 YW |
417 | r = find_acl_entry(applied, i, &j); |
418 | if (r == -ENOENT) { | |
419 | if (acl_create_entry(&applied, &j) < 0) | |
1c73f3bc | 420 | return -errno; |
cfef0734 YW |
421 | } else if (r < 0) |
422 | return r; | |
50d9e46d ZJS |
423 | |
424 | if (acl_copy_entry(j, i) < 0) | |
425 | return -errno; | |
426 | } | |
50d9e46d | 427 | if (r < 0) |
dd4105b0 | 428 | return -errno; |
50d9e46d | 429 | |
cfef0734 YW |
430 | if (ret) |
431 | *ret = TAKE_PTR(applied); | |
1cc6c93a | 432 | |
50d9e46d ZJS |
433 | return 0; |
434 | } | |
5c3bde3f | 435 | |
567aeb58 ZJS |
436 | /* POSIX says that ACL_{READ,WRITE,EXECUTE} don't have to be bitmasks. But that is a natural thing to do and |
437 | * all extant implementations do it. Let's make sure that we fail verbosely in the (imho unlikely) scenario | |
438 | * that we get a new implementation that does not satisfy this. */ | |
439 | assert_cc(!(ACL_READ & ACL_WRITE)); | |
440 | assert_cc(!(ACL_WRITE & ACL_EXECUTE)); | |
441 | assert_cc(!(ACL_EXECUTE & ACL_READ)); | |
442 | assert_cc((unsigned) ACL_READ == ACL_READ); | |
443 | assert_cc((unsigned) ACL_WRITE == ACL_WRITE); | |
444 | assert_cc((unsigned) ACL_EXECUTE == ACL_EXECUTE); | |
445 | ||
9db59d92 LP |
446 | int fd_add_uid_acl_permission( |
447 | int fd, | |
448 | uid_t uid, | |
567aeb58 | 449 | unsigned mask) { |
9db59d92 | 450 | |
5c3bde3f | 451 | _cleanup_(acl_freep) acl_t acl = NULL; |
5c3bde3f | 452 | acl_permset_t permset; |
2ea6247e | 453 | acl_entry_t entry; |
5c3bde3f ZJS |
454 | int r; |
455 | ||
9db59d92 LP |
456 | /* Adds an ACL entry for the specified file to allow the indicated access to the specified |
457 | * user. Operates purely incrementally. */ | |
458 | ||
2ea6247e LP |
459 | assert(fd >= 0); |
460 | assert(uid_is_valid(uid)); | |
461 | ||
5c3bde3f ZJS |
462 | acl = acl_get_fd(fd); |
463 | if (!acl) | |
464 | return -errno; | |
465 | ||
466 | r = acl_find_uid(acl, uid, &entry); | |
467 | if (r <= 0) { | |
468 | if (acl_create_entry(&acl, &entry) < 0 || | |
469 | acl_set_tag_type(entry, ACL_USER) < 0 || | |
470 | acl_set_qualifier(entry, &uid) < 0) | |
471 | return -errno; | |
472 | } | |
473 | ||
9db59d92 LP |
474 | if (acl_get_permset(entry, &permset) < 0) |
475 | return -errno; | |
476 | ||
567aeb58 | 477 | if ((mask & ACL_READ) && acl_add_perm(permset, ACL_READ) < 0) |
9db59d92 | 478 | return -errno; |
567aeb58 | 479 | if ((mask & ACL_WRITE) && acl_add_perm(permset, ACL_WRITE) < 0) |
9db59d92 | 480 | return -errno; |
567aeb58 | 481 | if ((mask & ACL_EXECUTE) && acl_add_perm(permset, ACL_EXECUTE) < 0) |
5c3bde3f ZJS |
482 | return -errno; |
483 | ||
484 | r = calc_acl_mask_if_needed(&acl); | |
485 | if (r < 0) | |
486 | return r; | |
487 | ||
2ea6247e LP |
488 | if (acl_set_fd(fd, acl) < 0) |
489 | return -errno; | |
490 | ||
491 | return 0; | |
5c3bde3f | 492 | } |
f76ce81b LP |
493 | |
494 | int fd_acl_make_read_only(int fd) { | |
495 | _cleanup_(acl_freep) acl_t acl = NULL; | |
496 | bool changed = false; | |
497 | acl_entry_t i; | |
498 | int r; | |
499 | ||
500 | assert(fd >= 0); | |
501 | ||
502 | /* Safely drops all W bits from all relevant ACL entries of the file, without changing entries which | |
503 | * are masked by the ACL mask */ | |
504 | ||
505 | acl = acl_get_fd(fd); | |
506 | if (!acl) { | |
507 | ||
508 | if (!ERRNO_IS_NOT_SUPPORTED(errno)) | |
509 | return -errno; | |
510 | ||
511 | /* No ACLs? Then just update the regular mode_t */ | |
512 | return fd_acl_make_read_only_fallback(fd); | |
513 | } | |
514 | ||
515 | for (r = acl_get_entry(acl, ACL_FIRST_ENTRY, &i); | |
516 | r > 0; | |
517 | r = acl_get_entry(acl, ACL_NEXT_ENTRY, &i)) { | |
518 | acl_permset_t permset; | |
519 | acl_tag_t tag; | |
520 | int b; | |
521 | ||
522 | if (acl_get_tag_type(i, &tag) < 0) | |
523 | return -errno; | |
524 | ||
525 | /* These three control the x bits overall (as ACL_MASK affects all remaining tags) */ | |
526 | if (!IN_SET(tag, ACL_USER_OBJ, ACL_MASK, ACL_OTHER)) | |
527 | continue; | |
528 | ||
529 | if (acl_get_permset(i, &permset) < 0) | |
530 | return -errno; | |
531 | ||
532 | b = acl_get_perm(permset, ACL_WRITE); | |
533 | if (b < 0) | |
534 | return -errno; | |
535 | ||
536 | if (b) { | |
537 | if (acl_delete_perm(permset, ACL_WRITE) < 0) | |
538 | return -errno; | |
539 | ||
540 | changed = true; | |
541 | } | |
542 | } | |
543 | if (r < 0) | |
544 | return -errno; | |
545 | ||
546 | if (!changed) | |
547 | return 0; | |
548 | ||
549 | if (acl_set_fd(fd, acl) < 0) { | |
550 | if (!ERRNO_IS_NOT_SUPPORTED(errno)) | |
551 | return -errno; | |
552 | ||
553 | return fd_acl_make_read_only_fallback(fd); | |
554 | } | |
555 | ||
556 | return 1; | |
557 | } | |
558 | ||
559 | int fd_acl_make_writable(int fd) { | |
560 | _cleanup_(acl_freep) acl_t acl = NULL; | |
561 | acl_entry_t i; | |
562 | int r; | |
563 | ||
564 | /* Safely adds the writable bit to the owner's ACL entry of this inode. (And only the owner's! – This | |
565 | * not the obvious inverse of fd_acl_make_read_only() hence!) */ | |
566 | ||
567 | acl = acl_get_fd(fd); | |
568 | if (!acl) { | |
569 | if (!ERRNO_IS_NOT_SUPPORTED(errno)) | |
570 | return -errno; | |
571 | ||
572 | /* No ACLs? Then just update the regular mode_t */ | |
573 | return fd_acl_make_writable_fallback(fd); | |
574 | } | |
575 | ||
576 | for (r = acl_get_entry(acl, ACL_FIRST_ENTRY, &i); | |
577 | r > 0; | |
578 | r = acl_get_entry(acl, ACL_NEXT_ENTRY, &i)) { | |
579 | acl_permset_t permset; | |
580 | acl_tag_t tag; | |
581 | int b; | |
582 | ||
583 | if (acl_get_tag_type(i, &tag) < 0) | |
584 | return -errno; | |
585 | ||
586 | if (tag != ACL_USER_OBJ) | |
587 | continue; | |
588 | ||
589 | if (acl_get_permset(i, &permset) < 0) | |
590 | return -errno; | |
591 | ||
592 | b = acl_get_perm(permset, ACL_WRITE); | |
593 | if (b < 0) | |
594 | return -errno; | |
595 | ||
596 | if (b) | |
597 | return 0; /* Already set? Then there's nothing to do. */ | |
598 | ||
599 | if (acl_add_perm(permset, ACL_WRITE) < 0) | |
600 | return -errno; | |
601 | ||
602 | break; | |
603 | } | |
604 | if (r < 0) | |
605 | return -errno; | |
606 | ||
607 | if (acl_set_fd(fd, acl) < 0) { | |
608 | if (!ERRNO_IS_NOT_SUPPORTED(errno)) | |
609 | return -errno; | |
610 | ||
611 | return fd_acl_make_writable_fallback(fd); | |
612 | } | |
613 | ||
614 | return 1; | |
615 | } | |
616 | #endif | |
617 | ||
618 | int fd_acl_make_read_only_fallback(int fd) { | |
619 | struct stat st; | |
620 | ||
621 | assert(fd >= 0); | |
622 | ||
623 | if (fstat(fd, &st) < 0) | |
624 | return -errno; | |
625 | ||
626 | if ((st.st_mode & 0222) == 0) | |
627 | return 0; | |
628 | ||
629 | if (fchmod(fd, st.st_mode & 0555) < 0) | |
630 | return -errno; | |
631 | ||
632 | return 1; | |
633 | } | |
634 | ||
635 | int fd_acl_make_writable_fallback(int fd) { | |
636 | struct stat st; | |
637 | ||
638 | assert(fd >= 0); | |
639 | ||
640 | if (fstat(fd, &st) < 0) | |
641 | return -errno; | |
642 | ||
643 | if ((st.st_mode & 0200) != 0) /* already set */ | |
644 | return 0; | |
645 | ||
646 | if (fchmod(fd, (st.st_mode & 07777) | 0200) < 0) | |
647 | return -errno; | |
648 | ||
649 | return 1; | |
650 | } |