| /* |
| * Copyright (C) 2006 Andreas Gruenbacher <a.gruenbacher@computer.org> |
| * |
| * This program is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License as published by the |
| * Free Software Foundation; either version 2, or (at your option) any |
| * later version. |
| * |
| * This program is distributed in the hope that it will be useful, but |
| * WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * General Public License for more details. |
| */ |
| |
| #include <linux/module.h> |
| #include <linux/fs.h> |
| #include <linux/nfs4acl.h> |
| |
| /** |
| * struct nfs4acl_alloc - remember how many entries are actually allocated |
| * @acl: acl with a_count <= @count |
| * @count: the actual number of entries allocated in @acl |
| * |
| * We pass around this structure while modifying an acl, so that we do |
| * not have to reallocate when we remove existing entries followed by |
| * adding new entries. |
| */ |
| struct nfs4acl_alloc { |
| struct nfs4acl *acl; |
| unsigned int count; |
| }; |
| |
| /** |
| * nfs4acl_delete_entry - delete an entry in an acl |
| * @x: acl and number of allocated entries |
| * @ace: an entry in @x->acl |
| * |
| * Updates @ace so that it points to the entry before the deleted entry |
| * on return. (When deleting the first entry, @ace will point to the |
| * (non-existant) entry before the first entry). This behavior is the |
| * expected behavior when deleting entries while forward iterating over |
| * an acl. |
| */ |
| static void |
| nfs4acl_delete_entry(struct nfs4acl_alloc *x, struct nfs4ace **ace) |
| { |
| void *end = x->acl->a_entries + x->acl->a_count; |
| |
| memmove(*ace, *ace + 1, end - (void *)(*ace + 1)); |
| (*ace)--; |
| x->acl->a_count--; |
| } |
| |
| /** |
| * nfs4acl_insert_entry - insert an entry in an acl |
| * @x: acl and number of allocated entries |
| * @ace: entry before which the new entry shall be inserted |
| * |
| * Insert a new entry in @x->acl at position @ace, and zero-initialize |
| * it. This may require reallocating @x->acl. |
| */ |
| static int |
| nfs4acl_insert_entry(struct nfs4acl_alloc *x, struct nfs4ace **ace) |
| { |
| if (x->count == x->acl->a_count) { |
| int n = *ace - x->acl->a_entries; |
| struct nfs4acl *acl2; |
| |
| acl2 = nfs4acl_alloc(x->acl->a_count + 1); |
| if (!acl2) |
| return -1; |
| acl2->a_flags = x->acl->a_flags; |
| acl2->a_owner_mask = x->acl->a_owner_mask; |
| acl2->a_group_mask = x->acl->a_group_mask; |
| acl2->a_other_mask = x->acl->a_other_mask; |
| memcpy(acl2->a_entries, x->acl->a_entries, |
| n * sizeof(struct nfs4ace)); |
| memcpy(acl2->a_entries + n + 1, *ace, |
| (x->acl->a_count - n) * sizeof(struct nfs4ace)); |
| kfree(x->acl); |
| x->acl = acl2; |
| x->count = acl2->a_count; |
| *ace = acl2->a_entries + n; |
| } else { |
| void *end = x->acl->a_entries + x->acl->a_count; |
| |
| memmove(*ace + 1, *ace, end - (void *)*ace); |
| x->acl->a_count++; |
| } |
| memset(*ace, 0, sizeof(struct nfs4ace)); |
| return 0; |
| } |
| |
| /** |
| * nfs4ace_change_mask - change the mask in @ace to @mask |
| * @x: acl and number of allocated entries |
| * @ace: entry to modify |
| * @mask: new mask for @ace |
| * |
| * Set the effective mask of @ace to @mask. This will require splitting |
| * off a separate acl entry if @ace is inheritable. In that case, the |
| * effective- only acl entry is inserted after the inheritable acl |
| * entry, end the inheritable acl entry is set to inheritable-only. If |
| * @mode is 0, either set the original acl entry to inheritable-only if |
| * it was inheritable, or remove it otherwise. The returned @ace points |
| * to the modified or inserted effective-only acl entry if that entry |
| * exists, to the entry that has become inheritable-only, or else to the |
| * previous entry in the acl. This is the expected behavior when |
| * modifying masks while forward iterating over an acl. |
| */ |
| static int |
| nfs4ace_change_mask(struct nfs4acl_alloc *x, struct nfs4ace **ace, |
| unsigned int mask) |
| { |
| if (mask && (*ace)->e_mask == mask) |
| return 0; |
| if (mask & ~ACE4_POSIX_ALWAYS_ALLOWED) { |
| if (nfs4ace_is_inheritable(*ace)) { |
| if (nfs4acl_insert_entry(x, ace)) |
| return -1; |
| memcpy(*ace, *ace + 1, sizeof(struct nfs4ace)); |
| (*ace)->e_flags |= ACE4_INHERIT_ONLY_ACE; |
| (*ace)++; |
| nfs4ace_clear_inheritance_flags(*ace); |
| } |
| (*ace)->e_mask = mask; |
| } else { |
| if (nfs4ace_is_inheritable(*ace)) |
| (*ace)->e_flags |= ACE4_INHERIT_ONLY_ACE; |
| else |
| nfs4acl_delete_entry(x, ace); |
| } |
| return 0; |
| } |
| |
| /** |
| * nfs4acl_move_everyone_aces_down - move everyone@ acl entries to the end |
| * @x: acl and number of allocated entries |
| * |
| * Move all everyone acl entries to the bottom of the acl so that only a |
| * single everyone@ allow acl entry remains at the end, and update the |
| * mask fields of all acl entries on the way. If everyone@ is not |
| * granted any permissions, no empty everyone@ acl entry is inserted. |
| * |
| * This transformation does not modify the permissions that the acl |
| * grants, but we need it to simplify successive transformations. |
| */ |
| static int |
| nfs4acl_move_everyone_aces_down(struct nfs4acl_alloc *x) |
| { |
| struct nfs4ace *ace; |
| unsigned int allowed = 0, denied = 0; |
| |
| nfs4acl_for_each_entry(ace, x->acl) { |
| if (nfs4ace_is_inherit_only(ace)) |
| continue; |
| if (nfs4ace_is_everyone(ace)) { |
| if (nfs4ace_is_allow(ace)) |
| allowed |= (ace->e_mask & ~denied); |
| else if (nfs4ace_is_deny(ace)) |
| denied |= (ace->e_mask & ~allowed); |
| else |
| continue; |
| if (nfs4ace_change_mask(x, &ace, 0)) |
| return -1; |
| } else { |
| if (nfs4ace_is_allow(ace)) { |
| if (nfs4ace_change_mask(x, &ace, allowed | |
| (ace->e_mask & ~denied))) |
| return -1; |
| } else if (nfs4ace_is_deny(ace)) { |
| if (nfs4ace_change_mask(x, &ace, denied | |
| (ace->e_mask & ~allowed))) |
| return -1; |
| } |
| } |
| } |
| if (allowed & ~ACE4_POSIX_ALWAYS_ALLOWED) { |
| struct nfs4ace *last_ace = ace - 1; |
| |
| if (nfs4ace_is_everyone(last_ace) && |
| nfs4ace_is_allow(last_ace) && |
| nfs4ace_is_inherit_only(last_ace) && |
| last_ace->e_mask == allowed) |
| last_ace->e_flags &= ~ACE4_INHERIT_ONLY_ACE; |
| else { |
| if (nfs4acl_insert_entry(x, &ace)) |
| return -1; |
| ace->e_type = ACE4_ACCESS_ALLOWED_ACE_TYPE; |
| ace->e_flags = ACE4_SPECIAL_WHO; |
| ace->e_mask = allowed; |
| ace->u.e_who = nfs4ace_everyone_who; |
| } |
| } |
| return 0; |
| } |
| |
| /** |
| * __nfs4acl_propagate_everyone - propagate everyone@ mask flags up for @who |
| * @x: acl and number of allocated entries |
| * @who: identifier to propagate mask flags for |
| * @allow: mask flags to propagate up |
| * |
| * Propagate mask flags from the trailing everyone@ allow acl entry up |
| * for the specified @who. |
| * |
| * The idea here is to precede the trailing EVERYONE@ ALLOW entry by an |
| * additional @who ALLOW entry, but with the following optimizations: |
| * (1) we don't bother setting any flags in the new @who ALLOW entry |
| * that has already been allowed or denied by a previous @who entry, (2) |
| * we merge the new @who entry with a previous @who entry if there is |
| * such a previous @who entry and there are no intervening DENY entries |
| * with mask flags that overlap the flags we care about. |
| */ |
| static int |
| __nfs4acl_propagate_everyone(struct nfs4acl_alloc *x, struct nfs4ace *who, |
| unsigned int allow) |
| { |
| struct nfs4ace *allow_last = NULL, *ace; |
| |
| /* Remove the mask flags from allow that are already determined for |
| this who value, and figure out if there is an ALLOW entry for |
| this who value that is "reachable" from the trailing EVERYONE@ |
| ALLOW ACE. */ |
| nfs4acl_for_each_entry(ace, x->acl) { |
| if (nfs4ace_is_inherit_only(ace)) |
| continue; |
| if (nfs4ace_is_allow(ace)) { |
| if (nfs4ace_is_same_who(ace, who)) { |
| allow &= ~ace->e_mask; |
| allow_last = ace; |
| } |
| } else if (nfs4ace_is_deny(ace)) { |
| if (nfs4ace_is_same_who(ace, who)) |
| allow &= ~ace->e_mask; |
| if (allow & ace->e_mask) |
| allow_last = NULL; |
| } |
| } |
| |
| if (allow) { |
| if (allow_last) |
| return nfs4ace_change_mask(x, &allow_last, |
| allow_last->e_mask | allow); |
| else { |
| struct nfs4ace who_copy; |
| |
| ace = x->acl->a_entries + x->acl->a_count - 1; |
| memcpy(&who_copy, who, sizeof(struct nfs4ace)); |
| if (nfs4acl_insert_entry(x, &ace)) |
| return -1; |
| memcpy(ace, &who_copy, sizeof(struct nfs4ace)); |
| ace->e_type = ACE4_ACCESS_ALLOWED_ACE_TYPE; |
| nfs4ace_clear_inheritance_flags(ace); |
| ace->e_mask = allow; |
| } |
| } |
| return 0; |
| } |
| |
| /** |
| * nfs4acl_propagate_everyone - propagate everyone@ mask flags up the acl |
| * @x: acl and number of allocated entries |
| * |
| * Make sure for owner@, group@, and all other users, groups, and |
| * special identifiers that they are allowed or denied all permissions |
| * that are granted be the trailing everyone@ acl entry. If they are |
| * not, try to add the missing permissions to existing allow acl entries |
| * for those users, or introduce additional acl entries if that is not |
| * possible. |
| * |
| * We do this so that no mask flags will get lost when finally applying |
| * the file masks to the acl entries: otherwise, with an other file mask |
| * that is more restrictive than the owner and/or group file mask, mask |
| * flags that were allowed to processes in the owner and group classes |
| * and that the other mask denies would be lost. For example, the |
| * following two acls show the problem when mode 0664 is applied to |
| * them: |
| * |
| * masking without propagation (wrong) |
| * =========================================================== |
| * joe:r::allow => joe:r::allow |
| * everyone@:rwx::allow => everyone@:r::allow |
| * ----------------------------------------------------------- |
| * joe:w::deny => joe:w::deny |
| * everyone@:rwx::allow everyone@:r::allow |
| * |
| * Note that the permissions of joe end up being more restrictive than |
| * what the acl would allow when first computing the allowed flags and |
| * then applying the respective mask. With propagation of permissions, |
| * we get: |
| * |
| * masking after propagation (correct) |
| * =========================================================== |
| * joe:r::allow => joe:rw::allow |
| * owner@:rw::allow |
| * group@:rw::allow |
| * everyone@:rwx::allow everyone@:r::allow |
| * ----------------------------------------------------------- |
| * joe:w::deny => owner@:x::deny |
| * joe:w::deny |
| * owner@:rw::allow |
| * owner@:rw::allow |
| * joe:r::allow |
| * everyone@:rwx::allow everyone@:r::allow |
| * |
| * The examples show the acls that would result from propagation with no |
| * masking performed. In fact, we do apply the respective mask to the |
| * acl entries before computing the propagation because this will save |
| * us from adding acl entries that would end up with empty mask fields |
| * after applying the masks. |
| * |
| * It is ensured that no more than one entry will be inserted for each |
| * who value, no matter how many entries each who value has already. |
| */ |
| static int |
| nfs4acl_propagate_everyone(struct nfs4acl_alloc *x) |
| { |
| int write_through = (x->acl->a_flags & ACL4_WRITE_THROUGH); |
| struct nfs4ace who = { .e_flags = ACE4_SPECIAL_WHO }; |
| struct nfs4ace *ace; |
| unsigned int owner_allow, group_allow; |
| int retval; |
| |
| if (!((x->acl->a_owner_mask | x->acl->a_group_mask) & |
| ~x->acl->a_other_mask)) |
| return 0; |
| if (!x->acl->a_count) |
| return 0; |
| ace = x->acl->a_entries + x->acl->a_count - 1; |
| if (nfs4ace_is_inherit_only(ace) || !nfs4ace_is_everyone(ace)) |
| return 0; |
| if (!(ace->e_mask & ~x->acl->a_other_mask)) { |
| /* None of the allowed permissions will get masked. */ |
| return 0; |
| } |
| owner_allow = ace->e_mask & x->acl->a_owner_mask; |
| group_allow = ace->e_mask & x->acl->a_group_mask; |
| |
| /* Propagate everyone@ permissions through to owner@. */ |
| if (owner_allow && !write_through && |
| (x->acl->a_owner_mask & ~x->acl->a_other_mask)) { |
| who.u.e_who = nfs4ace_owner_who; |
| retval = __nfs4acl_propagate_everyone(x, &who, owner_allow); |
| if (retval) |
| return -1; |
| } |
| |
| if (group_allow && (x->acl->a_group_mask & ~x->acl->a_other_mask)) { |
| int n; |
| |
| if (!write_through) { |
| /* Propagate everyone@ permissions through to group@. */ |
| who.u.e_who = nfs4ace_group_who; |
| retval = __nfs4acl_propagate_everyone(x, &who, |
| group_allow); |
| if (retval) |
| return -1; |
| } |
| |
| /* Start from the entry before the trailing EVERYONE@ ALLOW |
| entry. We will not hit EVERYONE@ entries in the loop. */ |
| for (n = x->acl->a_count - 2; n != -1; n--) { |
| ace = x->acl->a_entries + n; |
| |
| if (nfs4ace_is_inherit_only(ace) || |
| nfs4ace_is_owner(ace) || |
| nfs4ace_is_group(ace)) |
| continue; |
| if (nfs4ace_is_allow(ace) || nfs4ace_is_deny(ace)) { |
| /* Any inserted entry will end up below the |
| current entry. */ |
| retval = __nfs4acl_propagate_everyone(x, ace, |
| group_allow); |
| if (retval) |
| return -1; |
| } |
| } |
| } |
| return 0; |
| } |
| |
| /** |
| * __nfs4acl_apply_masks - apply the masks to the acl entries |
| * @x: acl and number of allocated entries |
| * |
| * Apply the owner file mask to owner@ entries, the intersection of the |
| * group and other file masks to everyone@ entries, and the group file |
| * mask to all other entries. |
| */ |
| static int |
| __nfs4acl_apply_masks(struct nfs4acl_alloc *x) |
| { |
| struct nfs4ace *ace; |
| |
| nfs4acl_for_each_entry(ace, x->acl) { |
| unsigned int mask; |
| |
| if (nfs4ace_is_inherit_only(ace) || !nfs4ace_is_allow(ace)) |
| continue; |
| if (nfs4ace_is_owner(ace)) |
| mask = x->acl->a_owner_mask; |
| else if (nfs4ace_is_everyone(ace)) |
| mask = x->acl->a_other_mask; |
| else |
| mask = x->acl->a_group_mask; |
| if (nfs4ace_change_mask(x, &ace, ace->e_mask & mask)) |
| return -1; |
| } |
| return 0; |
| } |
| |
| /** |
| * nfs4acl_max_allowed - maximum mask flags that anybody is allowed |
| */ |
| static unsigned int |
| nfs4acl_max_allowed(struct nfs4acl *acl) |
| { |
| struct nfs4ace *ace; |
| unsigned int allowed = 0; |
| |
| nfs4acl_for_each_entry_reverse(ace, acl) { |
| if (nfs4ace_is_inherit_only(ace)) |
| continue; |
| if (nfs4ace_is_allow(ace)) |
| allowed |= ace->e_mask; |
| else if (nfs4ace_is_deny(ace)) { |
| if (nfs4ace_is_everyone(ace)) |
| allowed &= ~ace->e_mask; |
| } |
| } |
| return allowed; |
| } |
| |
| /** |
| * nfs4acl_isolate_owner_class - limit the owner class to the owner file mask |
| * @x: acl and number of allocated entries |
| * |
| * Make sure the owner class (owner@) is granted no more than the owner |
| * mask by first checking which permissions anyone is granted, and then |
| * denying owner@ all permissions beyond that. |
| */ |
| static int |
| nfs4acl_isolate_owner_class(struct nfs4acl_alloc *x) |
| { |
| struct nfs4ace *ace; |
| unsigned int allowed = 0; |
| |
| allowed = nfs4acl_max_allowed(x->acl); |
| if (allowed & ~x->acl->a_owner_mask) { |
| /* Figure out if we can update an existig OWNER@ DENY entry. */ |
| nfs4acl_for_each_entry(ace, x->acl) { |
| if (nfs4ace_is_inherit_only(ace)) |
| continue; |
| if (nfs4ace_is_deny(ace)) { |
| if (nfs4ace_is_owner(ace)) |
| break; |
| } else if (nfs4ace_is_allow(ace)) { |
| ace = x->acl->a_entries + x->acl->a_count; |
| break; |
| } |
| } |
| if (ace != x->acl->a_entries + x->acl->a_count) { |
| if (nfs4ace_change_mask(x, &ace, ace->e_mask | |
| (allowed & ~x->acl->a_owner_mask))) |
| return -1; |
| } else { |
| /* Insert an owner@ deny entry at the front. */ |
| ace = x->acl->a_entries; |
| if (nfs4acl_insert_entry(x, &ace)) |
| return -1; |
| ace->e_type = ACE4_ACCESS_DENIED_ACE_TYPE; |
| ace->e_flags = ACE4_SPECIAL_WHO; |
| ace->e_mask = allowed & ~x->acl->a_owner_mask; |
| ace->u.e_who = nfs4ace_owner_who; |
| } |
| } |
| return 0; |
| } |
| |
| /** |
| * __nfs4acl_isolate_who - isolate entry from EVERYONE@ ALLOW entry |
| * @x: acl and number of allocated entries |
| * @who: identifier to isolate |
| * @deny: mask flags this identifier should not be allowed |
| * |
| * Make sure that @who is not allowed any mask flags in @deny by checking |
| * which mask flags this identifier is allowed, and adding excess allowed |
| * mask flags to an existing DENY entry before the trailing EVERYONE@ ALLOW |
| * entry, or inserting such an entry. |
| */ |
| static int |
| __nfs4acl_isolate_who(struct nfs4acl_alloc *x, struct nfs4ace *who, |
| unsigned int deny) |
| { |
| struct nfs4ace *ace; |
| unsigned int allowed = 0, n; |
| |
| /* Compute the mask flags granted to this who value. */ |
| nfs4acl_for_each_entry_reverse(ace, x->acl) { |
| if (nfs4ace_is_inherit_only(ace)) |
| continue; |
| if (nfs4ace_is_same_who(ace, who)) { |
| if (nfs4ace_is_allow(ace)) |
| allowed |= ace->e_mask; |
| else if (nfs4ace_is_deny(ace)) |
| allowed &= ~ace->e_mask; |
| deny &= ~ace->e_mask; |
| } |
| } |
| if (!deny) |
| return 0; |
| |
| /* Figure out if we can update an existig DENY entry. Start |
| from the entry before the trailing EVERYONE@ ALLOW entry. We |
| will not hit EVERYONE@ entries in the loop. */ |
| for (n = x->acl->a_count - 2; n != -1; n--) { |
| ace = x->acl->a_entries + n; |
| if (nfs4ace_is_inherit_only(ace)) |
| continue; |
| if (nfs4ace_is_deny(ace)) { |
| if (nfs4ace_is_same_who(ace, who)) |
| break; |
| } else if (nfs4ace_is_allow(ace) && |
| (ace->e_mask & deny)) { |
| n = -1; |
| break; |
| } |
| } |
| if (n != -1) { |
| if (nfs4ace_change_mask(x, &ace, ace->e_mask | deny)) |
| return -1; |
| } else { |
| /* Insert a eny entry before the trailing EVERYONE@ DENY |
| entry. */ |
| struct nfs4ace who_copy; |
| |
| ace = x->acl->a_entries + x->acl->a_count - 1; |
| memcpy(&who_copy, who, sizeof(struct nfs4ace)); |
| if (nfs4acl_insert_entry(x, &ace)) |
| return -1; |
| memcpy(ace, &who_copy, sizeof(struct nfs4ace)); |
| ace->e_type = ACE4_ACCESS_DENIED_ACE_TYPE; |
| nfs4ace_clear_inheritance_flags(ace); |
| ace->e_mask = deny; |
| } |
| return 0; |
| } |
| |
| /** |
| * nfs4acl_isolate_group_class - limit the group class to the group file mask |
| * @x: acl and number of allocated entries |
| * |
| * Make sure the group class (all entries except owner@ and everyone@) is |
| * granted no more than the group mask by inserting DENY entries for group |
| * class entries where necessary. |
| */ |
| static int |
| nfs4acl_isolate_group_class(struct nfs4acl_alloc *x) |
| { |
| struct nfs4ace who = { |
| .e_flags = ACE4_SPECIAL_WHO, |
| .u.e_who = nfs4ace_group_who, |
| }; |
| struct nfs4ace *ace; |
| unsigned int deny; |
| |
| if (!x->acl->a_count) |
| return 0; |
| ace = x->acl->a_entries + x->acl->a_count - 1; |
| if (nfs4ace_is_inherit_only(ace) || !nfs4ace_is_everyone(ace)) |
| return 0; |
| deny = ace->e_mask & ~x->acl->a_group_mask; |
| |
| if (deny) { |
| unsigned int n; |
| |
| if (__nfs4acl_isolate_who(x, &who, deny)) |
| return -1; |
| |
| /* Start from the entry before the trailing EVERYONE@ ALLOW |
| entry. We will not hit EVERYONE@ entries in the loop. */ |
| for (n = x->acl->a_count - 2; n != -1; n--) { |
| ace = x->acl->a_entries + n; |
| |
| if (nfs4ace_is_inherit_only(ace) || |
| nfs4ace_is_owner(ace) || |
| nfs4ace_is_group(ace)) |
| continue; |
| if (__nfs4acl_isolate_who(x, ace, deny)) |
| return -1; |
| } |
| } |
| return 0; |
| } |
| |
| /** |
| * __nfs4acl_write_through - grant the full masks to owner@, group@, everyone@ |
| * |
| * Make sure that owner, group@, and everyone@ are allowed the full mask |
| * permissions, and not only the permissions granted both by the acl and |
| * the masks. |
| */ |
| static int |
| __nfs4acl_write_through(struct nfs4acl_alloc *x) |
| { |
| struct nfs4ace *ace; |
| unsigned int allowed; |
| |
| /* Remove all owner@ and group@ ACEs: we re-insert them at the |
| top. */ |
| nfs4acl_for_each_entry(ace, x->acl) { |
| if (nfs4ace_is_inherit_only(ace)) |
| continue; |
| if ((nfs4ace_is_owner(ace) || nfs4ace_is_group(ace)) && |
| nfs4ace_change_mask(x, &ace, 0)) |
| return -1; |
| } |
| |
| /* Insert the everyone@ allow entry at the end, or update the |
| existing entry. */ |
| allowed = x->acl->a_other_mask; |
| if (allowed & ~ACE4_POSIX_ALWAYS_ALLOWED) { |
| ace = x->acl->a_entries + x->acl->a_count - 1; |
| if (x->acl->a_count && nfs4ace_is_everyone(ace) && |
| !nfs4ace_is_inherit_only(ace)) { |
| if (nfs4ace_change_mask(x, &ace, allowed)) |
| return -1; |
| } else { |
| ace = x->acl->a_entries + x->acl->a_count; |
| if (nfs4acl_insert_entry(x, &ace)) |
| return -1; |
| ace->e_type = ACE4_ACCESS_ALLOWED_ACE_TYPE; |
| ace->e_flags = ACE4_SPECIAL_WHO; |
| ace->e_mask = allowed; |
| ace->u.e_who = nfs4ace_everyone_who; |
| } |
| } |
| |
| /* Compute the permissions that owner@ and group@ are already granted |
| though the everyone@ allow entry at the end. Note that the acl |
| contains no owner@ or group@ entries at this point. */ |
| allowed = 0; |
| nfs4acl_for_each_entry_reverse(ace, x->acl) { |
| if (nfs4ace_is_inherit_only(ace)) |
| continue; |
| if (nfs4ace_is_allow(ace)) { |
| if (nfs4ace_is_everyone(ace)) |
| allowed |= ace->e_mask; |
| } else if (nfs4ace_is_deny(ace)) |
| allowed &= ~ace->e_mask; |
| } |
| |
| /* Insert the appropriate group@ allow entry at the front. */ |
| if (x->acl->a_group_mask & ~allowed) { |
| ace = x->acl->a_entries; |
| if (nfs4acl_insert_entry(x, &ace)) |
| return -1; |
| ace->e_type = ACE4_ACCESS_ALLOWED_ACE_TYPE; |
| ace->e_flags = ACE4_SPECIAL_WHO; |
| ace->e_mask = x->acl->a_group_mask /*& ~allowed*/; |
| ace->u.e_who = nfs4ace_group_who; |
| } |
| |
| /* Insert the appropriate owner@ allow entry at the front. */ |
| if (x->acl->a_owner_mask & ~allowed) { |
| ace = x->acl->a_entries; |
| if (nfs4acl_insert_entry(x, &ace)) |
| return -1; |
| ace->e_type = ACE4_ACCESS_ALLOWED_ACE_TYPE; |
| ace->e_flags = ACE4_SPECIAL_WHO; |
| ace->e_mask = x->acl->a_owner_mask /*& ~allowed*/; |
| ace->u.e_who = nfs4ace_owner_who; |
| } |
| |
| /* Insert the appropriate owner@ deny entry at the front. */ |
| allowed = nfs4acl_max_allowed(x->acl); |
| if (allowed & ~x->acl->a_owner_mask) { |
| nfs4acl_for_each_entry(ace, x->acl) { |
| if (nfs4ace_is_inherit_only(ace)) |
| continue; |
| if (nfs4ace_is_allow(ace)) { |
| ace = x->acl->a_entries + x->acl->a_count; |
| break; |
| } |
| if (nfs4ace_is_deny(ace) && nfs4ace_is_owner(ace)) |
| break; |
| } |
| if (ace != x->acl->a_entries + x->acl->a_count) { |
| if (nfs4ace_change_mask(x, &ace, ace->e_mask | |
| (allowed & ~x->acl->a_owner_mask))) |
| return -1; |
| } else { |
| ace = x->acl->a_entries; |
| if (nfs4acl_insert_entry(x, &ace)) |
| return -1; |
| ace->e_type = ACE4_ACCESS_DENIED_ACE_TYPE; |
| ace->e_flags = ACE4_SPECIAL_WHO; |
| ace->e_mask = allowed & ~x->acl->a_owner_mask; |
| ace->u.e_who = nfs4ace_owner_who; |
| } |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * nfs4acl_apply_masks - apply the masks to the acl |
| * |
| * Apply the masks so that the acl allows no more flags than the |
| * intersection between the flags that the original acl allows and the |
| * mask matching the process. |
| * |
| * Note: this algorithm may push the number of entries in the acl above |
| * ACL4_XATTR_MAX_COUNT, so a read-modify-write cycle would fail. |
| */ |
| int |
| nfs4acl_apply_masks(struct nfs4acl **acl) |
| { |
| struct nfs4acl_alloc x = { |
| .acl = *acl, |
| .count = (*acl)->a_count, |
| }; |
| int retval = 0; |
| |
| if (nfs4acl_move_everyone_aces_down(&x) || |
| nfs4acl_propagate_everyone(&x) || |
| __nfs4acl_apply_masks(&x) || |
| nfs4acl_isolate_owner_class(&x) || |
| nfs4acl_isolate_group_class(&x)) |
| retval = -ENOMEM; |
| |
| *acl = x.acl; |
| return retval; |
| } |
| EXPORT_SYMBOL(nfs4acl_apply_masks); |
| |
| int nfs4acl_write_through(struct nfs4acl **acl) |
| { |
| struct nfs4acl_alloc x = { |
| .acl = *acl, |
| .count = (*acl)->a_count, |
| }; |
| int retval = 0; |
| |
| if (!((*acl)->a_flags & ACL4_WRITE_THROUGH)) |
| goto out; |
| |
| if (nfs4acl_move_everyone_aces_down(&x) || |
| nfs4acl_propagate_everyone(&x) || |
| __nfs4acl_write_through(&x)) |
| retval = -ENOMEM; |
| |
| *acl = x.acl; |
| out: |
| return retval; |
| } |