3 (Linux Access Control List Management)
5 Copyright (C) 1999, 2000
6 Andreas Gruenbacher, <a.gruenbacher@bestbits.at>
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU Lesser General Public
10 License as published by the Free Software Foundation; either
11 version 2.1 of the License, or (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public
19 License along with this library; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26 #include <acl/libacl.h>
31 #include <sys/types.h>
40 #include "walk_tree.h"
43 extern const char *progname;
44 extern int opt_recalculate;
46 extern int print_options;
58 if (acl_get_entry(acl, ACL_FIRST_ENTRY, &ent) != 1)
62 acl_get_tag_type(ent, &e_type);
64 if (id != ACL_UNDEFINED_ID) {
65 e_id_p = acl_get_qualifier(ent);
77 if (acl_get_entry(acl, ACL_NEXT_ENTRY, &ent) != 1)
88 if (acl_get_entry(acl, ACL_FIRST_ENTRY, &ent) != 1)
92 acl_permset_t permset;
94 acl_get_permset(ent, &permset);
95 if (acl_get_perm(permset, ACL_EXECUTE) != 0)
98 if (acl_get_entry(acl, ACL_NEXT_ENTRY, &ent) != 1)
110 acl_entry_t from_entry, to_entry;
111 from_entry = find_entry(from_acl, from_type, ACL_UNDEFINED_ID);
113 if (acl_create_entry(to_acl, &to_entry) != 0)
115 acl_copy_entry(to_entry, from_entry);
116 acl_set_tag_type(to_entry, to_type);
128 const struct stat *st,
130 const acl_t default_acl)
132 char *acl_text, *default_acl_text;
134 acl_text = acl_to_any_text(acl, NULL, ',', TEXT_ABBREVIATE);
136 acl_to_any_text(default_acl, "d:", ',', TEXT_ABBREVIATE);
137 fprintf(file, "%s: %s,%s\n", path_p,
138 acl_text ? acl_text : "*",
139 default_acl_text ? default_acl_text : "*");
141 acl_free(default_acl_text);
152 acl_get_permset(ent, &set);
153 if (perm & CMD_PERM_READ)
154 acl_add_perm(set, ACL_READ);
156 acl_delete_perm(set, ACL_READ);
157 if (perm & CMD_PERM_WRITE)
158 acl_add_perm(set, ACL_WRITE);
160 acl_delete_perm(set, ACL_WRITE);
161 if (perm & CMD_PERM_EXECUTE)
162 acl_add_perm(set, ACL_EXECUTE);
164 acl_delete_perm(set, ACL_EXECUTE);
172 const struct stat *st,
179 if (type == ACL_TYPE_ACCESS || S_ISDIR(st->st_mode)) {
180 *old_acl = acl_get_file(path_p, type);
181 if (*old_acl == NULL && (errno == ENOSYS || errno == ENOTSUP)) {
182 if (type == ACL_TYPE_DEFAULT)
183 *old_acl = acl_init(0);
185 *old_acl = acl_from_mode(st->st_mode);
188 *old_acl = acl_init(0);
189 if (*old_acl == NULL)
191 *acl = acl_dup(*old_acl);
199 remove_extended_entries(
202 acl_entry_t ent, group_obj;
203 acl_permset_t mask_permset, group_obj_permset;
208 * Removing the ACL_MASK entry from the ACL results in
209 * increased permissions for the owning group if the
210 * ACL_GROUP_OBJ entry contains permissions not contained
211 * in the ACL_MASK entry. We remove these permissions from
212 * the ACL_GROUP_OBJ entry to avoid that.
214 * After removing the ACL, the file owner and the owning group
215 * therefore have the same permissions as before.
218 ent = find_entry(acl, ACL_MASK, ACL_UNDEFINED_ID);
219 group_obj = find_entry(acl, ACL_GROUP_OBJ, ACL_UNDEFINED_ID);
220 if (ent && group_obj) {
221 if (!acl_get_permset(ent, &mask_permset) &&
222 !acl_get_permset(group_obj, &group_obj_permset)) {
223 if (!acl_get_perm(mask_permset, ACL_READ))
224 acl_delete_perm(group_obj_permset, ACL_READ);
225 if (!acl_get_perm(mask_permset, ACL_WRITE))
226 acl_delete_perm(group_obj_permset, ACL_WRITE);
227 if (!acl_get_perm(mask_permset, ACL_EXECUTE))
228 acl_delete_perm(group_obj_permset, ACL_EXECUTE);
232 error = acl_get_entry(acl, ACL_FIRST_ENTRY, &ent);
234 acl_get_tag_type(ent, &tag);
239 acl_delete_entry(acl, ent);
245 error = acl_get_entry(acl, ACL_NEXT_ENTRY, &ent);
253 #define RETRIEVE_ACL(type) do { \
254 error = retrieve_acl(path_p, type, st, old_xacl, xacl); \
262 const struct stat *st,
266 struct do_set_args *args = arg;
267 acl_t old_acl = NULL, old_default_acl = NULL;
268 acl_t acl = NULL, default_acl = NULL;
269 acl_t *xacl, *old_xacl;
273 int errors = 0, error;
275 int acl_modified = 0, default_acl_modified = 0;
276 int acl_mask_provided = 0, default_acl_mask_provided = 0;
278 if (walk_flags & WALK_TREE_FAILED) {
279 fprintf(stderr, "%s: %s: %s\n", progname, path_p, strerror(errno));
284 * Symlinks can never have ACLs, so when doing a physical walk, we
285 * skip symlinks altogether, and when doing a half-logical walk, we
286 * skip all non-toplevel symlinks.
288 if ((walk_flags & WALK_TREE_SYMLINK) &&
289 ((walk_flags & WALK_TREE_PHYSICAL) ||
290 !(walk_flags & (WALK_TREE_TOPLEVEL | WALK_TREE_LOGICAL))))
293 /* Execute the commands in seq (read ACLs on demand) */
294 error = seq_get_cmd(args->seq, SEQ_FIRST_CMD, &cmd);
298 mode_t perm = cmd->c_perm;
300 if (cmd->c_type == ACL_TYPE_ACCESS) {
304 if (cmd->c_tag == ACL_MASK)
305 acl_mask_provided = 1;
308 old_xacl = &old_default_acl;
309 default_acl_modified = 1;
310 if (cmd->c_tag == ACL_MASK)
311 default_acl_mask_provided = 1;
314 RETRIEVE_ACL(cmd->c_type);
316 /* Check for `X', and replace with `x' as appropriate. */
317 if (perm & CMD_PERM_COND_EXECUTE) {
318 perm &= ~CMD_PERM_COND_EXECUTE;
319 if (S_ISDIR(st->st_mode) || has_execute_perms(*xacl))
320 perm |= CMD_PERM_EXECUTE;
324 case CMD_ENTRY_REPLACE:
325 ent = find_entry(*xacl, cmd->c_tag, cmd->c_id);
327 if (acl_create_entry(xacl, &ent) != 0)
329 acl_set_tag_type(ent, cmd->c_tag);
330 if (cmd->c_id != ACL_UNDEFINED_ID)
331 acl_set_qualifier(ent,
337 case CMD_REMOVE_ENTRY:
338 ent = find_entry(*xacl, cmd->c_tag, cmd->c_id);
340 acl_delete_entry(*xacl, ent);
345 case CMD_REMOVE_EXTENDED_ACL:
346 remove_extended_entries(acl);
361 error = seq_get_cmd(args->seq, SEQ_NEXT_CMD, &cmd);
367 /* Try to fill in missing entries */
368 if (default_acl && acl_entries(default_acl) != 0) {
372 if (!find_entry(default_acl, ACL_USER_OBJ, ACL_UNDEFINED_ID)) {
374 RETRIEVE_ACL(ACL_TYPE_ACCESS);
375 clone_entry(acl, ACL_USER_OBJ,
376 &default_acl, ACL_USER_OBJ);
378 if (!find_entry(default_acl, ACL_GROUP_OBJ, ACL_UNDEFINED_ID)) {
380 RETRIEVE_ACL(ACL_TYPE_ACCESS);
381 clone_entry(acl, ACL_GROUP_OBJ,
382 &default_acl, ACL_GROUP_OBJ);
384 if (!find_entry(default_acl, ACL_OTHER, ACL_UNDEFINED_ID)) {
386 RETRIEVE_ACL(ACL_TYPE_ACCESS);
387 clone_entry(acl, ACL_OTHER,
388 &default_acl, ACL_OTHER);
392 /* update mask entries and check if ACLs are valid */
393 if (acl && acl_modified) {
394 if (acl_equiv_mode(acl, NULL) != 0) {
395 if (!acl_mask_provided &&
396 !find_entry(acl, ACL_MASK, ACL_UNDEFINED_ID))
397 clone_entry(acl, ACL_GROUP_OBJ,
399 if (opt_recalculate != -1 &&
400 (!acl_mask_provided || opt_recalculate == 1))
404 error = acl_check(acl, &which_entry);
408 acl_text = acl_to_any_text(acl, NULL, ',', 0);
409 fprintf(stderr, _("%s: %s: Malformed access ACL "
410 "`%s': %s at entry %d\n"), progname, path_p,
411 acl_text, acl_error(error), which_entry+1);
418 if (default_acl && acl_entries(default_acl) != 0 &&
419 default_acl_modified) {
420 if (acl_equiv_mode(default_acl, NULL) != 0) {
421 if (!default_acl_mask_provided &&
422 !find_entry(default_acl,ACL_MASK,ACL_UNDEFINED_ID))
423 clone_entry(default_acl, ACL_GROUP_OBJ,
424 &default_acl, ACL_MASK);
425 if (opt_recalculate != -1 &&
426 (!default_acl_mask_provided ||
427 opt_recalculate == 1))
428 acl_calc_mask(&default_acl);
431 error = acl_check(default_acl, &which_entry);
435 acl_text = acl_to_any_text(default_acl, NULL, ',', 0);
436 fprintf(stderr, _("%s: %s: Malformed default ACL "
437 "`%s': %s at entry %d\n"),
438 progname, path_p, acl_text,
439 acl_error(error), which_entry+1);
446 /* Only directores can have default ACLs */
447 if (default_acl && !S_ISDIR(st->st_mode) && (walk_flags & WALK_TREE_RECURSIVE)) {
448 /* In recursive mode, ignore default ACLs for files */
449 acl_free(default_acl);
453 /* check which ACLs have changed */
454 if (acl && old_acl && acl_cmp(old_acl, acl) == 0) {
458 if ((default_acl && old_default_acl &&
459 acl_cmp(old_default_acl, default_acl) == 0)) {
460 acl_free(default_acl);
464 /* update the file system */
466 print_test(stdout, path_p, st,
474 equiv_mode = acl_equiv_mode(acl, &mode);
476 if (acl_set_file(path_p, ACL_TYPE_ACCESS, acl) != 0) {
477 if (errno == ENOSYS || errno == ENOTSUP) {
480 else if (chmod(path_p, mode) != 0)
488 if (S_ISDIR(st->st_mode)) {
489 if (acl_entries(default_acl) == 0) {
490 if (acl_delete_def_file(path_p) != 0 &&
491 errno != ENOSYS && errno != ENOTSUP)
494 if (acl_set_file(path_p, ACL_TYPE_DEFAULT,
499 if (acl_entries(default_acl) != 0) {
500 fprintf(stderr, _("%s: %s: Only directories "
501 "can have default ACLs\n"),
517 acl_free(default_acl);
519 acl_free(old_default_acl);
523 fprintf(stderr, "%s: %s: %s\n", progname, path_p, strerror(errno));