diff options
Diffstat (limited to 'fs/nfs_common/nfsacl.c')
| -rw-r--r-- | fs/nfs_common/nfsacl.c | 96 | 
1 files changed, 67 insertions, 29 deletions
diff --git a/fs/nfs_common/nfsacl.c b/fs/nfs_common/nfsacl.c index fc1c52571c0..ed628f71274 100644 --- a/fs/nfs_common/nfsacl.c +++ b/fs/nfs_common/nfsacl.c @@ -38,8 +38,13 @@ struct nfsacl_encode_desc {  	unsigned int count;  	struct posix_acl *acl;  	int typeflag; -	uid_t uid; -	gid_t gid; +	kuid_t uid; +	kgid_t gid; +}; + +struct nfsacl_simple_acl { +	struct posix_acl acl; +	struct posix_acl_entry ace[4];  };  static int @@ -55,14 +60,16 @@ xdr_nfsace_encode(struct xdr_array2_desc *desc, void *elem)  	*p++ = htonl(entry->e_tag | nfsacl_desc->typeflag);  	switch(entry->e_tag) {  		case ACL_USER_OBJ: -			*p++ = htonl(nfsacl_desc->uid); +			*p++ = htonl(from_kuid(&init_user_ns, nfsacl_desc->uid));  			break;  		case ACL_GROUP_OBJ: -			*p++ = htonl(nfsacl_desc->gid); +			*p++ = htonl(from_kgid(&init_user_ns, nfsacl_desc->gid));  			break;  		case ACL_USER: +			*p++ = htonl(from_kuid(&init_user_ns, entry->e_uid)); +			break;  		case ACL_GROUP: -			*p++ = htonl(entry->e_id); +			*p++ = htonl(from_kgid(&init_user_ns, entry->e_gid));  			break;  		default:  /* Solaris depends on that! */  			*p++ = 0; @@ -72,9 +79,20 @@ xdr_nfsace_encode(struct xdr_array2_desc *desc, void *elem)  	return 0;  } -unsigned int -nfsacl_encode(struct xdr_buf *buf, unsigned int base, struct inode *inode, -	      struct posix_acl *acl, int encode_entries, int typeflag) +/** + * nfsacl_encode - Encode an NFSv3 ACL + * + * @buf: destination xdr_buf to contain XDR encoded ACL + * @base: byte offset in xdr_buf where XDR'd ACL begins + * @inode: inode of file whose ACL this is + * @acl: posix_acl to encode + * @encode_entries: whether to encode ACEs as well + * @typeflag: ACL type: NFS_ACL_DEFAULT or zero + * + * Returns size of encoded ACL in bytes or a negative errno value. + */ +int nfsacl_encode(struct xdr_buf *buf, unsigned int base, struct inode *inode, +		  struct posix_acl *acl, int encode_entries, int typeflag)  {  	int entries = (acl && acl->a_count) ? max_t(int, acl->a_count, 4) : 0;  	struct nfsacl_encode_desc nfsacl_desc = { @@ -88,17 +106,21 @@ nfsacl_encode(struct xdr_buf *buf, unsigned int base, struct inode *inode,  		.uid = inode->i_uid,  		.gid = inode->i_gid,  	}; +	struct nfsacl_simple_acl aclbuf;  	int err; -	struct posix_acl *acl2 = NULL;  	if (entries > NFS_ACL_MAX_ENTRIES ||  	    xdr_encode_word(buf, base, entries))  		return -EINVAL;  	if (encode_entries && acl && acl->a_count == 3) { -		/* Fake up an ACL_MASK entry. */ -		acl2 = posix_acl_alloc(4, GFP_KERNEL); -		if (!acl2) -			return -ENOMEM; +		struct posix_acl *acl2 = &aclbuf.acl; + +		/* Avoid the use of posix_acl_alloc().  nfsacl_encode() is +		 * invoked in contexts where a memory allocation failure is +		 * fatal.  Fortunately this fake ACL is small enough to +		 * construct on the stack. */ +		posix_acl_init(acl2, 4); +  		/* Insert entries in canonical order: other orders seem  		 to confuse Solaris VxFS. */  		acl2->a_entries[0] = acl->a_entries[0];  /* ACL_USER_OBJ */ @@ -109,8 +131,6 @@ nfsacl_encode(struct xdr_buf *buf, unsigned int base, struct inode *inode,  		nfsacl_desc.acl = acl2;  	}  	err = xdr_encode_array2(buf, base + 4, &nfsacl_desc.desc); -	if (acl2) -		posix_acl_release(acl2);  	if (!err)  		err = 8 + nfsacl_desc.desc.elem_size *  			  nfsacl_desc.desc.array_len; @@ -130,6 +150,7 @@ xdr_nfsace_decode(struct xdr_array2_desc *desc, void *elem)  		(struct nfsacl_decode_desc *) desc;  	__be32 *p = elem;  	struct posix_acl_entry *entry; +	unsigned int id;  	if (!nfsacl_desc->acl) {  		if (desc->array_len > NFS_ACL_MAX_ENTRIES) @@ -142,20 +163,28 @@ xdr_nfsace_decode(struct xdr_array2_desc *desc, void *elem)  	entry = &nfsacl_desc->acl->a_entries[nfsacl_desc->count++];  	entry->e_tag = ntohl(*p++) & ~NFS_ACL_DEFAULT; -	entry->e_id = ntohl(*p++); +	id = ntohl(*p++);  	entry->e_perm = ntohl(*p++);  	switch(entry->e_tag) { -		case ACL_USER_OBJ:  		case ACL_USER: -		case ACL_GROUP_OBJ: +			entry->e_uid = make_kuid(&init_user_ns, id); +			if (!uid_valid(entry->e_uid)) +				return -EINVAL; +			break;  		case ACL_GROUP: +			entry->e_gid = make_kgid(&init_user_ns, id); +			if (!gid_valid(entry->e_gid)) +				return -EINVAL; +			break; +		case ACL_USER_OBJ: +		case ACL_GROUP_OBJ:  		case ACL_OTHER:  			if (entry->e_perm & ~S_IRWXO)  				return -EINVAL;  			break;  		case ACL_MASK: -			/* Solaris sometimes sets additonal bits in the mask */ +			/* Solaris sometimes sets additional bits in the mask */  			entry->e_perm &= S_IRWXO;  			break;  		default: @@ -172,9 +201,13 @@ cmp_acl_entry(const void *x, const void *y)  	if (a->e_tag != b->e_tag)  		return a->e_tag - b->e_tag; -	else if (a->e_id > b->e_id) +	else if ((a->e_tag == ACL_USER) && uid_gt(a->e_uid, b->e_uid))  		return 1; -	else if (a->e_id < b->e_id) +	else if ((a->e_tag == ACL_USER) && uid_lt(a->e_uid, b->e_uid)) +		return -1; +	else if ((a->e_tag == ACL_GROUP) && gid_gt(a->e_gid, b->e_gid)) +		return 1; +	else if ((a->e_tag == ACL_GROUP) && gid_lt(a->e_gid, b->e_gid))  		return -1;  	else  		return 0; @@ -195,22 +228,18 @@ posix_acl_from_nfsacl(struct posix_acl *acl)  	sort(acl->a_entries, acl->a_count, sizeof(struct posix_acl_entry),  	     cmp_acl_entry, NULL); -	/* Clear undefined identifier fields and find the ACL_GROUP_OBJ -	   and ACL_MASK entries. */ +	/* Find the ACL_GROUP_OBJ and ACL_MASK entries. */  	FOREACH_ACL_ENTRY(pa, acl, pe) {  		switch(pa->e_tag) {  			case ACL_USER_OBJ: -				pa->e_id = ACL_UNDEFINED_ID;  				break;  			case ACL_GROUP_OBJ: -				pa->e_id = ACL_UNDEFINED_ID;  				group_obj = pa;  				break;  			case ACL_MASK:  				mask = pa;  				/* fall through */  			case ACL_OTHER: -				pa->e_id = ACL_UNDEFINED_ID;  				break;  		}  	} @@ -224,9 +253,18 @@ posix_acl_from_nfsacl(struct posix_acl *acl)  	return 0;  } -unsigned int -nfsacl_decode(struct xdr_buf *buf, unsigned int base, unsigned int *aclcnt, -	      struct posix_acl **pacl) +/** + * nfsacl_decode - Decode an NFSv3 ACL + * + * @buf: xdr_buf containing XDR'd ACL data to decode + * @base: byte offset in xdr_buf where XDR'd ACL begins + * @aclcnt: count of ACEs in decoded posix_acl + * @pacl: buffer in which to place decoded posix_acl + * + * Returns the length of the decoded ACL in bytes, or a negative errno value. + */ +int nfsacl_decode(struct xdr_buf *buf, unsigned int base, unsigned int *aclcnt, +		  struct posix_acl **pacl)  {  	struct nfsacl_decode_desc nfsacl_desc = {  		.desc = {  | 
