diff options
Diffstat (limited to 'fs/nfsd/nfs4acl.c')
| -rw-r--r-- | fs/nfsd/nfs4acl.c | 169 | 
1 files changed, 120 insertions, 49 deletions
diff --git a/fs/nfsd/nfs4acl.c b/fs/nfsd/nfs4acl.c index 8a50b3c1809..d714156a19f 100644 --- a/fs/nfsd/nfs4acl.c +++ b/fs/nfsd/nfs4acl.c @@ -36,9 +36,14 @@  #include <linux/slab.h>  #include <linux/nfs_fs.h> -#include <linux/export.h> +#include "nfsfh.h" +#include "nfsd.h"  #include "acl.h" +#include "vfs.h" +#define NFS4_ACL_TYPE_DEFAULT	0x01 +#define NFS4_ACL_DIR		0x02 +#define NFS4_ACL_OWNER		0x04  /* mode bit translations: */  #define NFS4_READ_MODE (NFS4_ACE_READ_DATA) @@ -130,36 +135,47 @@ static short ace2type(struct nfs4_ace *);  static void _posix_to_nfsv4_one(struct posix_acl *, struct nfs4_acl *,  				unsigned int); -struct nfs4_acl * -nfs4_acl_posix_to_nfsv4(struct posix_acl *pacl, struct posix_acl *dpacl, -			unsigned int flags) +int +nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry, +		struct nfs4_acl **acl)  { -	struct nfs4_acl *acl; +	struct inode *inode = dentry->d_inode; +	int error = 0; +	struct posix_acl *pacl = NULL, *dpacl = NULL; +	unsigned int flags = 0;  	int size = 0; -	if (pacl) { -		if (posix_acl_valid(pacl) < 0) -			return ERR_PTR(-EINVAL); -		size += 2*pacl->a_count; +	pacl = get_acl(inode, ACL_TYPE_ACCESS); +	if (!pacl) { +		pacl = posix_acl_from_mode(inode->i_mode, GFP_KERNEL); +		if (IS_ERR(pacl)) +			return PTR_ERR(pacl);  	} -	if (dpacl) { -		if (posix_acl_valid(dpacl) < 0) -			return ERR_PTR(-EINVAL); -		size += 2*dpacl->a_count; +	/* allocate for worst case: one (deny, allow) pair each: */ +	size += 2 * pacl->a_count; + +	if (S_ISDIR(inode->i_mode)) { +		flags = NFS4_ACL_DIR; +		dpacl = get_acl(inode, ACL_TYPE_DEFAULT); +		if (dpacl) +			size += 2 * dpacl->a_count;  	} -	/* Allocate for worst case: one (deny, allow) pair each: */ -	acl = nfs4_acl_new(size); -	if (acl == NULL) -		return ERR_PTR(-ENOMEM); +	*acl = nfs4_acl_new(size); +	if (*acl == NULL) { +		error = -ENOMEM; +		goto out; +	} -	if (pacl) -		_posix_to_nfsv4_one(pacl, acl, flags & ~NFS4_ACL_TYPE_DEFAULT); +	_posix_to_nfsv4_one(pacl, *acl, flags & ~NFS4_ACL_TYPE_DEFAULT);  	if (dpacl) -		_posix_to_nfsv4_one(dpacl, acl, flags | NFS4_ACL_TYPE_DEFAULT); +		_posix_to_nfsv4_one(dpacl, *acl, flags | NFS4_ACL_TYPE_DEFAULT); -	return acl; + out: +	posix_acl_release(pacl); +	posix_acl_release(dpacl); +	return error;  }  struct posix_acl_summary { @@ -385,8 +401,10 @@ sort_pacl(struct posix_acl *pacl)  	 * by uid/gid. */  	int i, j; -	if (pacl->a_count <= 4) -		return; /* no users or groups */ +	/* no users or groups */ +	if (!pacl || pacl->a_count <= 4) +		return; +  	i = 1;  	while (pacl->a_entries[i].e_tag == ACL_USER)  		i++; @@ -513,19 +531,21 @@ posix_state_to_acl(struct posix_acl_state *state, unsigned int flags)  	/*  	 * ACLs with no ACEs are treated differently in the inheritable -	 * and effective cases: when there are no inheritable ACEs, we -	 * set a zero-length default posix acl: +	 * and effective cases: when there are no inheritable ACEs, +	 * calls ->set_acl with a NULL ACL structure.  	 */ -	if (state->empty && (flags & NFS4_ACL_TYPE_DEFAULT)) { -		pacl = posix_acl_alloc(0, GFP_KERNEL); -		return pacl ? pacl : ERR_PTR(-ENOMEM); -	} +	if (state->empty && (flags & NFS4_ACL_TYPE_DEFAULT)) +		return NULL; +  	/*  	 * When there are no effective ACEs, the following will end  	 * up setting a 3-element effective posix ACL with all  	 * permissions zero.  	 */ -	nace = 4 + state->users->n + state->groups->n; +	if (!state->users->n && !state->groups->n) +		nace = 3; +	else /* Note we also include a MASK ACE in this case: */ +		nace = 4 + state->users->n + state->groups->n;  	pacl = posix_acl_alloc(nace, GFP_KERNEL);  	if (!pacl)  		return ERR_PTR(-ENOMEM); @@ -569,9 +589,11 @@ posix_state_to_acl(struct posix_acl_state *state, unsigned int flags)  		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); +	if (state->users->n || state->groups->n) { +		pace++; +		pace->e_tag = ACL_MASK; +		low_mode_from_nfs4(state->mask.allow, &pace->e_perm, flags); +	}  	pace++;  	pace->e_tag = ACL_OTHER; @@ -719,8 +741,9 @@ static void process_one_v4_ace(struct posix_acl_state *state,  	}  } -int nfs4_acl_nfsv4_to_posix(struct nfs4_acl *acl, struct posix_acl **pacl, -			    struct posix_acl **dpacl, unsigned int flags) +static int nfs4_acl_nfsv4_to_posix(struct nfs4_acl *acl, +		struct posix_acl **pacl, struct posix_acl **dpacl, +		unsigned int flags)  {  	struct posix_acl_state effective_acl_state, default_acl_state;  	struct nfs4_ace *ace; @@ -780,6 +803,57 @@ out_estate:  	return ret;  } +__be32 +nfsd4_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp, +		struct nfs4_acl *acl) +{ +	__be32 error; +	int host_error; +	struct dentry *dentry; +	struct inode *inode; +	struct posix_acl *pacl = NULL, *dpacl = NULL; +	unsigned int flags = 0; + +	/* Get inode */ +	error = fh_verify(rqstp, fhp, 0, NFSD_MAY_SATTR); +	if (error) +		return error; + +	dentry = fhp->fh_dentry; +	inode = dentry->d_inode; + +	if (!inode->i_op->set_acl || !IS_POSIXACL(inode)) +		return nfserr_attrnotsupp; + +	if (S_ISDIR(inode->i_mode)) +		flags = NFS4_ACL_DIR; + +	host_error = nfs4_acl_nfsv4_to_posix(acl, &pacl, &dpacl, flags); +	if (host_error == -EINVAL) +		return nfserr_attrnotsupp; +	if (host_error < 0) +		goto out_nfserr; + +	host_error = inode->i_op->set_acl(inode, pacl, ACL_TYPE_ACCESS); +	if (host_error < 0) +		goto out_release; + +	if (S_ISDIR(inode->i_mode)) { +		host_error = inode->i_op->set_acl(inode, dpacl, +						  ACL_TYPE_DEFAULT); +	} + +out_release: +	posix_acl_release(pacl); +	posix_acl_release(dpacl); +out_nfserr: +	if (host_error == -EOPNOTSUPP) +		return nfserr_attrnotsupp; +	else +		return nfserrno(host_error); +} + +  static short  ace2type(struct nfs4_ace *ace)  { @@ -798,9 +872,6 @@ ace2type(struct nfs4_ace *ace)  	return -1;  } -EXPORT_SYMBOL(nfs4_acl_posix_to_nfsv4); -EXPORT_SYMBOL(nfs4_acl_nfsv4_to_posix); -  struct nfs4_acl *  nfs4_acl_new(int n)  { @@ -848,21 +919,21 @@ nfs4_acl_get_whotype(char *p, u32 len)  	return NFS4_ACL_WHO_NAMED;  } -int -nfs4_acl_write_who(int who, char *p) +__be32 nfs4_acl_write_who(struct xdr_stream *xdr, int who)  { +	__be32 *p;  	int i;  	for (i = 0; i < ARRAY_SIZE(s2t_map); i++) { -		if (s2t_map[i].type == who) { -			memcpy(p, s2t_map[i].string, s2t_map[i].stringlen); -			return s2t_map[i].stringlen; -		} +		if (s2t_map[i].type != who) +			continue; +		p = xdr_reserve_space(xdr, s2t_map[i].stringlen + 4); +		if (!p) +			return nfserr_resource; +		p = xdr_encode_opaque(p, s2t_map[i].string, +					s2t_map[i].stringlen); +		return 0;  	} -	BUG(); +	WARN_ON_ONCE(1);  	return -1;  } - -EXPORT_SYMBOL(nfs4_acl_new); -EXPORT_SYMBOL(nfs4_acl_get_whotype); -EXPORT_SYMBOL(nfs4_acl_write_who);  | 
