mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-21 16:29:59 +00:00
[PATCH] knfsd: nfsd4: acls: relax the nfsv4->posix mapping
Use a different nfsv4->(draft posix) acl mapping which is 1. completely backwards compatible, 2. accepts any nfsv4 acl, and 3. errs on the side of restricting permissions. In detail: 1. completely backwards compatible: The new mapping produces the same result on any acl produced by the existing (draft posix)->nfsv4 mapping; the one exception is that we no longer attempt to guess the value of the mask by assuming certain denies represent the mask. Since the server still keeps track of the mask locally, sequences of chmod's will still be handled fine; the only thing this will change is sequences of chmod's with intervening read-modify-writes of the acl. That last case just isn't worth the trouble and the possible misrepresentations of the user's intent (if we guess that a certain deny indicates masking is in effect when it really isn't). 2. accepts any nfsv4 acl: That's not quite true: we still reject acls that use combinations of inheritance flags that we don't support. We also reject acls that attempt to explicitly deny read_acl or read_attributes permissions, or that attempt to deny write_acl or write_attributes permissions to the owner of the file. 3. errs on the side of restricting permissions: one exception to this last rule: we totally ignore some bits (write_owner, synchronize, read_named_attributes, etc.) that are completely alien to our filesystem semantics, in some cases even if that would mean ignoring an explicit deny that we have no intention of enforcing. Excepting that, the posix acl produced should be the most permissive acl that is not more permissive than the given nfsv4 acl. And the new code's shorter, too. Neato. Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu> Signed-off-by: Neil Brown <neilb@suse.de> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
This commit is contained in:
parent
d0ebd9c0e7
commit
09229edb68
1 changed files with 288 additions and 369 deletions
|
@ -96,24 +96,26 @@ deny_mask(u32 allow_mask, unsigned int flags)
|
|||
/* XXX: modify functions to return NFS errors; they're only ever
|
||||
* used by nfs code, after all.... */
|
||||
|
||||
static int
|
||||
mode_from_nfs4(u32 perm, unsigned short *mode, unsigned int flags)
|
||||
{
|
||||
u32 ignore = 0;
|
||||
/* We only map from NFSv4 to POSIX ACLs when setting ACLs, when we err on the
|
||||
* side of being more restrictive, so the mode bit mapping below is
|
||||
* pessimistic. An optimistic version would be needed to handle DENY's,
|
||||
* but we espect to coalesce all ALLOWs and DENYs before mapping to mode
|
||||
* bits. */
|
||||
|
||||
if (!(flags & NFS4_ACL_DIR))
|
||||
ignore |= NFS4_ACE_DELETE_CHILD; /* ignore it */
|
||||
perm |= ignore;
|
||||
static void
|
||||
low_mode_from_nfs4(u32 perm, unsigned short *mode, unsigned int flags)
|
||||
{
|
||||
u32 write_mode = NFS4_WRITE_MODE;
|
||||
|
||||
if (flags & NFS4_ACL_DIR)
|
||||
write_mode |= NFS4_ACE_DELETE_CHILD;
|
||||
*mode = 0;
|
||||
if ((perm & NFS4_READ_MODE) == NFS4_READ_MODE)
|
||||
*mode |= ACL_READ;
|
||||
if ((perm & NFS4_WRITE_MODE) == NFS4_WRITE_MODE)
|
||||
if ((perm & write_mode) == write_mode)
|
||||
*mode |= ACL_WRITE;
|
||||
if ((perm & NFS4_EXECUTE_MODE) == NFS4_EXECUTE_MODE)
|
||||
*mode |= ACL_EXECUTE;
|
||||
if (!MASK_EQUAL(perm, ignore|mask_from_posix(*mode, flags)))
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct ace_container {
|
||||
|
@ -338,38 +340,6 @@ sort_pacl(struct posix_acl *pacl)
|
|||
return;
|
||||
}
|
||||
|
||||
static int
|
||||
write_pace(struct nfs4_ace *ace, struct posix_acl *pacl,
|
||||
struct posix_acl_entry **pace, short tag, unsigned int flags)
|
||||
{
|
||||
struct posix_acl_entry *this = *pace;
|
||||
|
||||
if (*pace == pacl->a_entries + pacl->a_count)
|
||||
return -EINVAL; /* fell off the end */
|
||||
(*pace)++;
|
||||
this->e_tag = tag;
|
||||
if (tag == ACL_USER_OBJ)
|
||||
flags |= NFS4_ACL_OWNER;
|
||||
if (mode_from_nfs4(ace->access_mask, &this->e_perm, flags))
|
||||
return -EINVAL;
|
||||
this->e_id = (tag == ACL_USER || tag == ACL_GROUP ?
|
||||
ace->who : ACL_UNDEFINED_ID);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct nfs4_ace *
|
||||
get_next_v4_ace(struct list_head **p, struct list_head *head)
|
||||
{
|
||||
struct nfs4_ace *ace;
|
||||
|
||||
*p = (*p)->next;
|
||||
if (*p == head)
|
||||
return NULL;
|
||||
ace = list_entry(*p, struct nfs4_ace, l_ace);
|
||||
|
||||
return ace;
|
||||
}
|
||||
|
||||
int
|
||||
nfs4_acl_nfsv4_to_posix(struct nfs4_acl *acl, struct posix_acl **pacl,
|
||||
struct posix_acl **dpacl, unsigned int flags)
|
||||
|
@ -429,349 +399,311 @@ nfs4_acl_nfsv4_to_posix(struct nfs4_acl *acl, struct posix_acl **pacl,
|
|||
return error;
|
||||
}
|
||||
|
||||
static int
|
||||
same_who(struct nfs4_ace *a, struct nfs4_ace *b)
|
||||
{
|
||||
return a->whotype == b->whotype &&
|
||||
(a->whotype != NFS4_ACL_WHO_NAMED || a->who == b->who);
|
||||
}
|
||||
/*
|
||||
* While processing the NFSv4 ACE, this maintains bitmasks representing
|
||||
* which permission bits have been allowed and which denied to a given
|
||||
* entity: */
|
||||
struct posix_ace_state {
|
||||
u32 allow;
|
||||
u32 deny;
|
||||
};
|
||||
|
||||
struct posix_user_ace_state {
|
||||
uid_t uid;
|
||||
struct posix_ace_state perms;
|
||||
};
|
||||
|
||||
struct posix_ace_state_array {
|
||||
int n;
|
||||
struct posix_user_ace_state aces[];
|
||||
};
|
||||
|
||||
/*
|
||||
* While processing the NFSv4 ACE, this maintains the partial permissions
|
||||
* calculated so far: */
|
||||
|
||||
struct posix_acl_state {
|
||||
struct posix_ace_state owner;
|
||||
struct posix_ace_state group;
|
||||
struct posix_ace_state other;
|
||||
struct posix_ace_state everyone;
|
||||
struct posix_ace_state mask; /* Deny unused in this case */
|
||||
struct posix_ace_state_array *users;
|
||||
struct posix_ace_state_array *groups;
|
||||
};
|
||||
|
||||
static int
|
||||
complementary_ace_pair(struct nfs4_ace *allow, struct nfs4_ace *deny,
|
||||
unsigned int flags)
|
||||
init_state(struct posix_acl_state *state, int cnt)
|
||||
{
|
||||
int ignore = 0;
|
||||
if (!(flags & NFS4_ACL_DIR))
|
||||
ignore |= NFS4_ACE_DELETE_CHILD;
|
||||
return MASK_EQUAL(ignore|deny_mask(allow->access_mask, flags),
|
||||
ignore|deny->access_mask) &&
|
||||
allow->type == NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE &&
|
||||
deny->type == NFS4_ACE_ACCESS_DENIED_ACE_TYPE &&
|
||||
allow->flag == deny->flag &&
|
||||
same_who(allow, deny);
|
||||
int alloc;
|
||||
|
||||
memset(state, 0, sizeof(struct posix_acl_state));
|
||||
/*
|
||||
* In the worst case, each individual acl could be for a distinct
|
||||
* named user or group, but we don't no which, so we allocate
|
||||
* enough space for either:
|
||||
*/
|
||||
alloc = sizeof(struct posix_ace_state_array)
|
||||
+ cnt*sizeof(struct posix_ace_state);
|
||||
state->users = kzalloc(alloc, GFP_KERNEL);
|
||||
if (!state->users)
|
||||
return -ENOMEM;
|
||||
state->groups = kzalloc(alloc, GFP_KERNEL);
|
||||
if (!state->groups) {
|
||||
kfree(state->users);
|
||||
return -ENOMEM;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
user_obj_from_v4(struct nfs4_acl *n4acl, struct list_head **p,
|
||||
struct posix_acl *pacl, struct posix_acl_entry **pace,
|
||||
unsigned int flags)
|
||||
{
|
||||
int error = -EINVAL;
|
||||
struct nfs4_ace *ace, *ace2;
|
||||
|
||||
ace = get_next_v4_ace(p, &n4acl->ace_head);
|
||||
if (ace == NULL)
|
||||
goto out;
|
||||
if (ace2type(ace) != ACL_USER_OBJ)
|
||||
goto out;
|
||||
error = write_pace(ace, pacl, pace, ACL_USER_OBJ, flags);
|
||||
if (error < 0)
|
||||
goto out;
|
||||
error = -EINVAL;
|
||||
ace2 = get_next_v4_ace(p, &n4acl->ace_head);
|
||||
if (ace2 == NULL)
|
||||
goto out;
|
||||
if (!complementary_ace_pair(ace, ace2, flags))
|
||||
goto out;
|
||||
error = 0;
|
||||
out:
|
||||
return error;
|
||||
static void
|
||||
free_state(struct posix_acl_state *state) {
|
||||
kfree(state->users);
|
||||
kfree(state->groups);
|
||||
}
|
||||
|
||||
static inline int
|
||||
users_from_v4(struct nfs4_acl *n4acl, struct list_head **p,
|
||||
struct nfs4_ace **mask_ace,
|
||||
struct posix_acl *pacl, struct posix_acl_entry **pace,
|
||||
unsigned int flags)
|
||||
static inline void add_to_mask(struct posix_acl_state *state, struct posix_ace_state *astate)
|
||||
{
|
||||
int error = -EINVAL;
|
||||
struct nfs4_ace *ace, *ace2;
|
||||
|
||||
ace = get_next_v4_ace(p, &n4acl->ace_head);
|
||||
if (ace == NULL)
|
||||
goto out;
|
||||
while (ace2type(ace) == ACL_USER) {
|
||||
if (ace->type != NFS4_ACE_ACCESS_DENIED_ACE_TYPE)
|
||||
goto out;
|
||||
if (*mask_ace &&
|
||||
!MASK_EQUAL(ace->access_mask, (*mask_ace)->access_mask))
|
||||
goto out;
|
||||
*mask_ace = ace;
|
||||
ace = get_next_v4_ace(p, &n4acl->ace_head);
|
||||
if (ace == NULL)
|
||||
goto out;
|
||||
if (ace->type != NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE)
|
||||
goto out;
|
||||
error = write_pace(ace, pacl, pace, ACL_USER, flags);
|
||||
if (error < 0)
|
||||
goto out;
|
||||
error = -EINVAL;
|
||||
ace2 = get_next_v4_ace(p, &n4acl->ace_head);
|
||||
if (ace2 == NULL)
|
||||
goto out;
|
||||
if (!complementary_ace_pair(ace, ace2, flags))
|
||||
goto out;
|
||||
if ((*mask_ace)->flag != ace2->flag ||
|
||||
!same_who(*mask_ace, ace2))
|
||||
goto out;
|
||||
ace = get_next_v4_ace(p, &n4acl->ace_head);
|
||||
if (ace == NULL)
|
||||
goto out;
|
||||
}
|
||||
error = 0;
|
||||
out:
|
||||
return error;
|
||||
state->mask.allow |= astate->allow;
|
||||
}
|
||||
|
||||
static inline int
|
||||
group_obj_and_groups_from_v4(struct nfs4_acl *n4acl, struct list_head **p,
|
||||
struct nfs4_ace **mask_ace,
|
||||
struct posix_acl *pacl, struct posix_acl_entry **pace,
|
||||
unsigned int flags)
|
||||
/*
|
||||
* Certain bits (SYNCHRONIZE, DELETE, WRITE_OWNER, READ/WRITE_NAMED_ATTRS,
|
||||
* READ_ATTRIBUTES, READ_ACL) are currently unenforceable and don't translate
|
||||
* to traditional read/write/execute permissions.
|
||||
*
|
||||
* It's problematic to reject acls that use certain mode bits, because it
|
||||
* places the burden on users to learn the rules about which bits one
|
||||
* particular server sets, without giving the user a lot of help--we return an
|
||||
* error that could mean any number of different things. To make matters
|
||||
* worse, the problematic bits might be introduced by some application that's
|
||||
* automatically mapping from some other acl model.
|
||||
*
|
||||
* So wherever possible we accept anything, possibly erring on the side of
|
||||
* denying more permissions than necessary.
|
||||
*
|
||||
* However we do reject *explicit* DENY's of a few bits representing
|
||||
* permissions we could never deny:
|
||||
*/
|
||||
|
||||
static inline int check_deny(u32 mask, int isowner)
|
||||
{
|
||||
int error = -EINVAL;
|
||||
struct nfs4_ace *ace, *ace2;
|
||||
struct ace_container *ac;
|
||||
struct list_head group_l;
|
||||
|
||||
INIT_LIST_HEAD(&group_l);
|
||||
ace = list_entry(*p, struct nfs4_ace, l_ace);
|
||||
|
||||
/* group owner (mask and allow aces) */
|
||||
|
||||
if (pacl->a_count != 3) {
|
||||
/* then the group owner should be preceded by mask */
|
||||
if (ace->type != NFS4_ACE_ACCESS_DENIED_ACE_TYPE)
|
||||
goto out;
|
||||
if (*mask_ace &&
|
||||
!MASK_EQUAL(ace->access_mask, (*mask_ace)->access_mask))
|
||||
goto out;
|
||||
*mask_ace = ace;
|
||||
ace = get_next_v4_ace(p, &n4acl->ace_head);
|
||||
if (ace == NULL)
|
||||
goto out;
|
||||
|
||||
if ((*mask_ace)->flag != ace->flag || !same_who(*mask_ace, ace))
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (ace2type(ace) != ACL_GROUP_OBJ)
|
||||
goto out;
|
||||
|
||||
ac = kmalloc(sizeof(*ac), GFP_KERNEL);
|
||||
error = -ENOMEM;
|
||||
if (ac == NULL)
|
||||
goto out;
|
||||
ac->ace = ace;
|
||||
list_add_tail(&ac->ace_l, &group_l);
|
||||
|
||||
error = -EINVAL;
|
||||
if (ace->type != NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE)
|
||||
goto out;
|
||||
|
||||
error = write_pace(ace, pacl, pace, ACL_GROUP_OBJ, flags);
|
||||
if (error < 0)
|
||||
goto out;
|
||||
|
||||
error = -EINVAL;
|
||||
ace = get_next_v4_ace(p, &n4acl->ace_head);
|
||||
if (ace == NULL)
|
||||
goto out;
|
||||
|
||||
/* groups (mask and allow aces) */
|
||||
|
||||
while (ace2type(ace) == ACL_GROUP) {
|
||||
if (*mask_ace == NULL)
|
||||
goto out;
|
||||
|
||||
if (ace->type != NFS4_ACE_ACCESS_DENIED_ACE_TYPE ||
|
||||
!MASK_EQUAL(ace->access_mask, (*mask_ace)->access_mask))
|
||||
goto out;
|
||||
*mask_ace = ace;
|
||||
|
||||
ace = get_next_v4_ace(p, &n4acl->ace_head);
|
||||
if (ace == NULL)
|
||||
goto out;
|
||||
ac = kmalloc(sizeof(*ac), GFP_KERNEL);
|
||||
error = -ENOMEM;
|
||||
if (ac == NULL)
|
||||
goto out;
|
||||
error = -EINVAL;
|
||||
if (ace->type != NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE ||
|
||||
!same_who(ace, *mask_ace))
|
||||
goto out;
|
||||
|
||||
ac->ace = ace;
|
||||
list_add_tail(&ac->ace_l, &group_l);
|
||||
|
||||
error = write_pace(ace, pacl, pace, ACL_GROUP, flags);
|
||||
if (error < 0)
|
||||
goto out;
|
||||
error = -EINVAL;
|
||||
ace = get_next_v4_ace(p, &n4acl->ace_head);
|
||||
if (ace == NULL)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* group owner (deny ace) */
|
||||
|
||||
if (ace2type(ace) != ACL_GROUP_OBJ)
|
||||
goto out;
|
||||
ac = list_entry(group_l.next, struct ace_container, ace_l);
|
||||
ace2 = ac->ace;
|
||||
if (!complementary_ace_pair(ace2, ace, flags))
|
||||
goto out;
|
||||
list_del(group_l.next);
|
||||
kfree(ac);
|
||||
|
||||
/* groups (deny aces) */
|
||||
|
||||
while (!list_empty(&group_l)) {
|
||||
ace = get_next_v4_ace(p, &n4acl->ace_head);
|
||||
if (ace == NULL)
|
||||
goto out;
|
||||
if (ace2type(ace) != ACL_GROUP)
|
||||
goto out;
|
||||
ac = list_entry(group_l.next, struct ace_container, ace_l);
|
||||
ace2 = ac->ace;
|
||||
if (!complementary_ace_pair(ace2, ace, flags))
|
||||
goto out;
|
||||
list_del(group_l.next);
|
||||
kfree(ac);
|
||||
}
|
||||
|
||||
ace = get_next_v4_ace(p, &n4acl->ace_head);
|
||||
if (ace == NULL)
|
||||
goto out;
|
||||
if (ace2type(ace) != ACL_OTHER)
|
||||
goto out;
|
||||
error = 0;
|
||||
out:
|
||||
while (!list_empty(&group_l)) {
|
||||
ac = list_entry(group_l.next, struct ace_container, ace_l);
|
||||
list_del(group_l.next);
|
||||
kfree(ac);
|
||||
}
|
||||
return error;
|
||||
if (mask & (NFS4_ACE_READ_ATTRIBUTES | NFS4_ACE_READ_ACL))
|
||||
return -EINVAL;
|
||||
if (!isowner)
|
||||
return 0;
|
||||
if (mask & (NFS4_ACE_WRITE_ATTRIBUTES | NFS4_ACE_WRITE_ACL))
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
mask_from_v4(struct nfs4_acl *n4acl, struct list_head **p,
|
||||
struct nfs4_ace **mask_ace,
|
||||
struct posix_acl *pacl, struct posix_acl_entry **pace,
|
||||
unsigned int flags)
|
||||
static struct posix_acl *
|
||||
posix_state_to_acl(struct posix_acl_state *state, unsigned int flags)
|
||||
{
|
||||
int error = -EINVAL;
|
||||
struct nfs4_ace *ace;
|
||||
struct posix_acl_entry *pace;
|
||||
struct posix_acl *pacl;
|
||||
int nace;
|
||||
int i, error = 0;
|
||||
|
||||
ace = list_entry(*p, struct nfs4_ace, l_ace);
|
||||
if (pacl->a_count != 3) {
|
||||
if (*mask_ace == NULL)
|
||||
goto out;
|
||||
(*mask_ace)->access_mask = deny_mask((*mask_ace)->access_mask, flags);
|
||||
write_pace(*mask_ace, pacl, pace, ACL_MASK, flags);
|
||||
nace = 4 + state->users->n + state->groups->n;
|
||||
pacl = posix_acl_alloc(nace, GFP_KERNEL);
|
||||
if (!pacl)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
pace = pacl->a_entries;
|
||||
pace->e_tag = ACL_USER_OBJ;
|
||||
error = check_deny(state->owner.deny, 1);
|
||||
if (error)
|
||||
goto out_err;
|
||||
low_mode_from_nfs4(state->owner.allow, &pace->e_perm, flags);
|
||||
pace->e_id = ACL_UNDEFINED_ID;
|
||||
|
||||
for (i=0; i < state->users->n; i++) {
|
||||
pace++;
|
||||
pace->e_tag = ACL_USER;
|
||||
error = check_deny(state->users->aces[i].perms.deny, 0);
|
||||
if (error)
|
||||
goto out_err;
|
||||
low_mode_from_nfs4(state->users->aces[i].perms.allow,
|
||||
&pace->e_perm, flags);
|
||||
pace->e_id = state->users->aces[i].uid;
|
||||
add_to_mask(state, &state->users->aces[i].perms);
|
||||
}
|
||||
error = 0;
|
||||
out:
|
||||
return error;
|
||||
|
||||
pace++;
|
||||
pace->e_tag = ACL_GROUP_OBJ;
|
||||
error = check_deny(state->group.deny, 0);
|
||||
if (error)
|
||||
goto out_err;
|
||||
low_mode_from_nfs4(state->group.allow, &pace->e_perm, flags);
|
||||
pace->e_id = ACL_UNDEFINED_ID;
|
||||
add_to_mask(state, &state->group);
|
||||
|
||||
for (i=0; i < state->groups->n; i++) {
|
||||
pace++;
|
||||
pace->e_tag = ACL_GROUP;
|
||||
error = check_deny(state->groups->aces[i].perms.deny, 0);
|
||||
if (error)
|
||||
goto out_err;
|
||||
low_mode_from_nfs4(state->groups->aces[i].perms.allow,
|
||||
&pace->e_perm, flags);
|
||||
pace->e_id = state->groups->aces[i].uid;
|
||||
add_to_mask(state, &state->groups->aces[i].perms);
|
||||
}
|
||||
|
||||
pace++;
|
||||
pace->e_tag = ACL_MASK;
|
||||
low_mode_from_nfs4(state->mask.allow, &pace->e_perm, flags);
|
||||
pace->e_id = ACL_UNDEFINED_ID;
|
||||
|
||||
pace++;
|
||||
pace->e_tag = ACL_OTHER;
|
||||
error = check_deny(state->other.deny, 0);
|
||||
if (error)
|
||||
goto out_err;
|
||||
low_mode_from_nfs4(state->other.allow, &pace->e_perm, flags);
|
||||
pace->e_id = ACL_UNDEFINED_ID;
|
||||
|
||||
return pacl;
|
||||
out_err:
|
||||
posix_acl_release(pacl);
|
||||
return ERR_PTR(error);
|
||||
}
|
||||
|
||||
static inline int
|
||||
other_from_v4(struct nfs4_acl *n4acl, struct list_head **p,
|
||||
struct posix_acl *pacl, struct posix_acl_entry **pace,
|
||||
unsigned int flags)
|
||||
static inline void allow_bits(struct posix_ace_state *astate, u32 mask)
|
||||
{
|
||||
int error = -EINVAL;
|
||||
struct nfs4_ace *ace, *ace2;
|
||||
|
||||
ace = list_entry(*p, struct nfs4_ace, l_ace);
|
||||
if (ace->type != NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE)
|
||||
goto out;
|
||||
error = write_pace(ace, pacl, pace, ACL_OTHER, flags);
|
||||
if (error < 0)
|
||||
goto out;
|
||||
error = -EINVAL;
|
||||
ace2 = get_next_v4_ace(p, &n4acl->ace_head);
|
||||
if (ace2 == NULL)
|
||||
goto out;
|
||||
if (!complementary_ace_pair(ace, ace2, flags))
|
||||
goto out;
|
||||
error = 0;
|
||||
out:
|
||||
return error;
|
||||
/* Allow all bits in the mask not already denied: */
|
||||
astate->allow |= mask & ~astate->deny;
|
||||
}
|
||||
|
||||
static int
|
||||
calculate_posix_ace_count(struct nfs4_acl *n4acl)
|
||||
static inline void deny_bits(struct posix_ace_state *astate, u32 mask)
|
||||
{
|
||||
if (n4acl->naces == 6) /* owner, owner group, and other only */
|
||||
return 3;
|
||||
else { /* Otherwise there must be a mask entry. */
|
||||
/* Also, the remaining entries are for named users and
|
||||
* groups, and come in threes (mask, allow, deny): */
|
||||
if (n4acl->naces < 7)
|
||||
return -EINVAL;
|
||||
if ((n4acl->naces - 7) % 3)
|
||||
return -EINVAL;
|
||||
return 4 + (n4acl->naces - 7)/3;
|
||||
/* Deny all bits in the mask not already allowed: */
|
||||
astate->deny |= mask & ~astate->allow;
|
||||
}
|
||||
|
||||
static int find_uid(struct posix_acl_state *state, struct posix_ace_state_array *a, uid_t uid)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < a->n; i++)
|
||||
if (a->aces[i].uid == uid)
|
||||
return i;
|
||||
/* Not found: */
|
||||
a->n++;
|
||||
a->aces[i].uid = uid;
|
||||
a->aces[i].perms.allow = state->everyone.allow;
|
||||
a->aces[i].perms.deny = state->everyone.deny;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static void deny_bits_array(struct posix_ace_state_array *a, u32 mask)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; i < a->n; i++)
|
||||
deny_bits(&a->aces[i].perms, mask);
|
||||
}
|
||||
|
||||
static void allow_bits_array(struct posix_ace_state_array *a, u32 mask)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; i < a->n; i++)
|
||||
allow_bits(&a->aces[i].perms, mask);
|
||||
}
|
||||
|
||||
static void process_one_v4_ace(struct posix_acl_state *state,
|
||||
struct nfs4_ace *ace)
|
||||
{
|
||||
u32 mask = ace->access_mask;
|
||||
int i;
|
||||
|
||||
switch (ace2type(ace)) {
|
||||
case ACL_USER_OBJ:
|
||||
if (ace->type == NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE) {
|
||||
allow_bits(&state->owner, mask);
|
||||
} else {
|
||||
deny_bits(&state->owner, mask);
|
||||
}
|
||||
break;
|
||||
case ACL_USER:
|
||||
i = find_uid(state, state->users, ace->who);
|
||||
if (ace->type == NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE) {
|
||||
allow_bits(&state->users->aces[i].perms, mask);
|
||||
} else {
|
||||
deny_bits(&state->users->aces[i].perms, mask);
|
||||
mask = state->users->aces[i].perms.deny;
|
||||
deny_bits(&state->owner, mask);
|
||||
}
|
||||
break;
|
||||
case ACL_GROUP_OBJ:
|
||||
if (ace->type == NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE) {
|
||||
allow_bits(&state->group, mask);
|
||||
} else {
|
||||
deny_bits(&state->group, mask);
|
||||
mask = state->group.deny;
|
||||
deny_bits(&state->owner, mask);
|
||||
deny_bits(&state->everyone, mask);
|
||||
deny_bits_array(state->users, mask);
|
||||
deny_bits_array(state->groups, mask);
|
||||
}
|
||||
break;
|
||||
case ACL_GROUP:
|
||||
i = find_uid(state, state->groups, ace->who);
|
||||
if (ace->type == NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE) {
|
||||
allow_bits(&state->groups->aces[i].perms, mask);
|
||||
} else {
|
||||
deny_bits(&state->groups->aces[i].perms, mask);
|
||||
mask = state->groups->aces[i].perms.deny;
|
||||
deny_bits(&state->owner, mask);
|
||||
deny_bits(&state->group, mask);
|
||||
deny_bits(&state->everyone, mask);
|
||||
deny_bits_array(state->users, mask);
|
||||
deny_bits_array(state->groups, mask);
|
||||
}
|
||||
break;
|
||||
case ACL_OTHER:
|
||||
if (ace->type == NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE) {
|
||||
allow_bits(&state->owner, mask);
|
||||
allow_bits(&state->group, mask);
|
||||
allow_bits(&state->other, mask);
|
||||
allow_bits(&state->everyone, mask);
|
||||
allow_bits_array(state->users, mask);
|
||||
allow_bits_array(state->groups, mask);
|
||||
} else {
|
||||
deny_bits(&state->owner, mask);
|
||||
deny_bits(&state->group, mask);
|
||||
deny_bits(&state->other, mask);
|
||||
deny_bits(&state->everyone, mask);
|
||||
deny_bits_array(state->users, mask);
|
||||
deny_bits_array(state->groups, mask);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static struct posix_acl *
|
||||
_nfsv4_to_posix_one(struct nfs4_acl *n4acl, unsigned int flags)
|
||||
{
|
||||
struct posix_acl_state state;
|
||||
struct posix_acl *pacl;
|
||||
int error = -EINVAL, nace = 0;
|
||||
struct list_head *p;
|
||||
struct nfs4_ace *mask_ace = NULL;
|
||||
struct posix_acl_entry *pace;
|
||||
struct nfs4_ace *ace;
|
||||
int ret;
|
||||
|
||||
nace = calculate_posix_ace_count(n4acl);
|
||||
if (nace < 0)
|
||||
goto out_err;
|
||||
ret = init_state(&state, n4acl->naces);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
pacl = posix_acl_alloc(nace, GFP_KERNEL);
|
||||
error = -ENOMEM;
|
||||
if (pacl == NULL)
|
||||
goto out_err;
|
||||
list_for_each_entry(ace, &n4acl->ace_head, l_ace)
|
||||
process_one_v4_ace(&state, ace);
|
||||
|
||||
pace = &pacl->a_entries[0];
|
||||
p = &n4acl->ace_head;
|
||||
pacl = posix_state_to_acl(&state, flags);
|
||||
|
||||
error = user_obj_from_v4(n4acl, &p, pacl, &pace, flags);
|
||||
if (error)
|
||||
goto out_acl;
|
||||
free_state(&state);
|
||||
|
||||
error = users_from_v4(n4acl, &p, &mask_ace, pacl, &pace, flags);
|
||||
if (error)
|
||||
goto out_acl;
|
||||
|
||||
error = group_obj_and_groups_from_v4(n4acl, &p, &mask_ace, pacl, &pace,
|
||||
flags);
|
||||
if (error)
|
||||
goto out_acl;
|
||||
|
||||
error = mask_from_v4(n4acl, &p, &mask_ace, pacl, &pace, flags);
|
||||
if (error)
|
||||
goto out_acl;
|
||||
error = other_from_v4(n4acl, &p, pacl, &pace, flags);
|
||||
if (error)
|
||||
goto out_acl;
|
||||
|
||||
error = -EINVAL;
|
||||
if (p->next != &n4acl->ace_head)
|
||||
goto out_acl;
|
||||
if (pace != pacl->a_entries + pacl->a_count)
|
||||
goto out_acl;
|
||||
|
||||
sort_pacl(pacl);
|
||||
|
||||
return pacl;
|
||||
out_acl:
|
||||
posix_acl_release(pacl);
|
||||
out_err:
|
||||
pacl = ERR_PTR(error);
|
||||
if (!IS_ERR(pacl))
|
||||
sort_pacl(pacl);
|
||||
return pacl;
|
||||
}
|
||||
|
||||
|
@ -785,6 +717,10 @@ nfs4_acl_split(struct nfs4_acl *acl, struct nfs4_acl *dacl)
|
|||
list_for_each_safe(h, n, &acl->ace_head) {
|
||||
ace = list_entry(h, struct nfs4_ace, l_ace);
|
||||
|
||||
if (ace->type != NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE &&
|
||||
ace->type != NFS4_ACE_ACCESS_DENIED_ACE_TYPE)
|
||||
return -EINVAL;
|
||||
|
||||
if ((ace->flag & NFS4_INHERITANCE_FLAGS)
|
||||
!= NFS4_INHERITANCE_FLAGS)
|
||||
continue;
|
||||
|
@ -930,23 +866,6 @@ nfs4_acl_write_who(int who, char *p)
|
|||
return -1;
|
||||
}
|
||||
|
||||
static inline int
|
||||
match_who(struct nfs4_ace *ace, uid_t owner, gid_t group, uid_t who)
|
||||
{
|
||||
switch (ace->whotype) {
|
||||
case NFS4_ACL_WHO_NAMED:
|
||||
return who == ace->who;
|
||||
case NFS4_ACL_WHO_OWNER:
|
||||
return who == owner;
|
||||
case NFS4_ACL_WHO_GROUP:
|
||||
return who == group;
|
||||
case NFS4_ACL_WHO_EVERYONE:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(nfs4_acl_new);
|
||||
EXPORT_SYMBOL(nfs4_acl_free);
|
||||
EXPORT_SYMBOL(nfs4_acl_add_ace);
|
||||
|
|
Loading…
Reference in a new issue