162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * fs/nfs_common/nfsacl.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Copyright (C) 2002-2003 Andreas Gruenbacher <agruen@suse.de>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci/*
962306a36Sopenharmony_ci * The Solaris nfsacl protocol represents some ACLs slightly differently
1062306a36Sopenharmony_ci * than POSIX 1003.1e draft 17 does (and we do):
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci *  - Minimal ACLs always have an ACL_MASK entry, so they have
1362306a36Sopenharmony_ci *    four instead of three entries.
1462306a36Sopenharmony_ci *  - The ACL_MASK entry in such minimal ACLs always has the same
1562306a36Sopenharmony_ci *    permissions as the ACL_GROUP_OBJ entry. (In extended ACLs
1662306a36Sopenharmony_ci *    the ACL_MASK and ACL_GROUP_OBJ entries may differ.)
1762306a36Sopenharmony_ci *  - The identifier fields of the ACL_USER_OBJ and ACL_GROUP_OBJ
1862306a36Sopenharmony_ci *    entries contain the identifiers of the owner and owning group.
1962306a36Sopenharmony_ci *    (In POSIX ACLs we always set them to ACL_UNDEFINED_ID).
2062306a36Sopenharmony_ci *  - ACL entries in the kernel are kept sorted in ascending order
2162306a36Sopenharmony_ci *    of (e_tag, e_id). Solaris ACLs are unsorted.
2262306a36Sopenharmony_ci */
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include <linux/module.h>
2562306a36Sopenharmony_ci#include <linux/fs.h>
2662306a36Sopenharmony_ci#include <linux/gfp.h>
2762306a36Sopenharmony_ci#include <linux/sunrpc/xdr.h>
2862306a36Sopenharmony_ci#include <linux/nfsacl.h>
2962306a36Sopenharmony_ci#include <linux/nfs3.h>
3062306a36Sopenharmony_ci#include <linux/sort.h>
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ciMODULE_LICENSE("GPL");
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistruct nfsacl_encode_desc {
3562306a36Sopenharmony_ci	struct xdr_array2_desc desc;
3662306a36Sopenharmony_ci	unsigned int count;
3762306a36Sopenharmony_ci	struct posix_acl *acl;
3862306a36Sopenharmony_ci	int typeflag;
3962306a36Sopenharmony_ci	kuid_t uid;
4062306a36Sopenharmony_ci	kgid_t gid;
4162306a36Sopenharmony_ci};
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistruct nfsacl_simple_acl {
4462306a36Sopenharmony_ci	struct posix_acl acl;
4562306a36Sopenharmony_ci	struct posix_acl_entry ace[4];
4662306a36Sopenharmony_ci};
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic int
4962306a36Sopenharmony_cixdr_nfsace_encode(struct xdr_array2_desc *desc, void *elem)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	struct nfsacl_encode_desc *nfsacl_desc =
5262306a36Sopenharmony_ci		(struct nfsacl_encode_desc *) desc;
5362306a36Sopenharmony_ci	__be32 *p = elem;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	struct posix_acl_entry *entry =
5662306a36Sopenharmony_ci		&nfsacl_desc->acl->a_entries[nfsacl_desc->count++];
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	*p++ = htonl(entry->e_tag | nfsacl_desc->typeflag);
5962306a36Sopenharmony_ci	switch(entry->e_tag) {
6062306a36Sopenharmony_ci		case ACL_USER_OBJ:
6162306a36Sopenharmony_ci			*p++ = htonl(from_kuid(&init_user_ns, nfsacl_desc->uid));
6262306a36Sopenharmony_ci			break;
6362306a36Sopenharmony_ci		case ACL_GROUP_OBJ:
6462306a36Sopenharmony_ci			*p++ = htonl(from_kgid(&init_user_ns, nfsacl_desc->gid));
6562306a36Sopenharmony_ci			break;
6662306a36Sopenharmony_ci		case ACL_USER:
6762306a36Sopenharmony_ci			*p++ = htonl(from_kuid(&init_user_ns, entry->e_uid));
6862306a36Sopenharmony_ci			break;
6962306a36Sopenharmony_ci		case ACL_GROUP:
7062306a36Sopenharmony_ci			*p++ = htonl(from_kgid(&init_user_ns, entry->e_gid));
7162306a36Sopenharmony_ci			break;
7262306a36Sopenharmony_ci		default:  /* Solaris depends on that! */
7362306a36Sopenharmony_ci			*p++ = 0;
7462306a36Sopenharmony_ci			break;
7562306a36Sopenharmony_ci	}
7662306a36Sopenharmony_ci	*p++ = htonl(entry->e_perm & S_IRWXO);
7762306a36Sopenharmony_ci	return 0;
7862306a36Sopenharmony_ci}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci/**
8162306a36Sopenharmony_ci * nfsacl_encode - Encode an NFSv3 ACL
8262306a36Sopenharmony_ci *
8362306a36Sopenharmony_ci * @buf: destination xdr_buf to contain XDR encoded ACL
8462306a36Sopenharmony_ci * @base: byte offset in xdr_buf where XDR'd ACL begins
8562306a36Sopenharmony_ci * @inode: inode of file whose ACL this is
8662306a36Sopenharmony_ci * @acl: posix_acl to encode
8762306a36Sopenharmony_ci * @encode_entries: whether to encode ACEs as well
8862306a36Sopenharmony_ci * @typeflag: ACL type: NFS_ACL_DEFAULT or zero
8962306a36Sopenharmony_ci *
9062306a36Sopenharmony_ci * Returns size of encoded ACL in bytes or a negative errno value.
9162306a36Sopenharmony_ci */
9262306a36Sopenharmony_ciint nfsacl_encode(struct xdr_buf *buf, unsigned int base, struct inode *inode,
9362306a36Sopenharmony_ci		  struct posix_acl *acl, int encode_entries, int typeflag)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	int entries = (acl && acl->a_count) ? max_t(int, acl->a_count, 4) : 0;
9662306a36Sopenharmony_ci	struct nfsacl_encode_desc nfsacl_desc = {
9762306a36Sopenharmony_ci		.desc = {
9862306a36Sopenharmony_ci			.elem_size = 12,
9962306a36Sopenharmony_ci			.array_len = encode_entries ? entries : 0,
10062306a36Sopenharmony_ci			.xcode = xdr_nfsace_encode,
10162306a36Sopenharmony_ci		},
10262306a36Sopenharmony_ci		.acl = acl,
10362306a36Sopenharmony_ci		.typeflag = typeflag,
10462306a36Sopenharmony_ci		.uid = inode->i_uid,
10562306a36Sopenharmony_ci		.gid = inode->i_gid,
10662306a36Sopenharmony_ci	};
10762306a36Sopenharmony_ci	struct nfsacl_simple_acl aclbuf;
10862306a36Sopenharmony_ci	int err;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	if (entries > NFS_ACL_MAX_ENTRIES ||
11162306a36Sopenharmony_ci	    xdr_encode_word(buf, base, entries))
11262306a36Sopenharmony_ci		return -EINVAL;
11362306a36Sopenharmony_ci	if (encode_entries && acl && acl->a_count == 3) {
11462306a36Sopenharmony_ci		struct posix_acl *acl2 = &aclbuf.acl;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci		/* Avoid the use of posix_acl_alloc().  nfsacl_encode() is
11762306a36Sopenharmony_ci		 * invoked in contexts where a memory allocation failure is
11862306a36Sopenharmony_ci		 * fatal.  Fortunately this fake ACL is small enough to
11962306a36Sopenharmony_ci		 * construct on the stack. */
12062306a36Sopenharmony_ci		posix_acl_init(acl2, 4);
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci		/* Insert entries in canonical order: other orders seem
12362306a36Sopenharmony_ci		 to confuse Solaris VxFS. */
12462306a36Sopenharmony_ci		acl2->a_entries[0] = acl->a_entries[0];  /* ACL_USER_OBJ */
12562306a36Sopenharmony_ci		acl2->a_entries[1] = acl->a_entries[1];  /* ACL_GROUP_OBJ */
12662306a36Sopenharmony_ci		acl2->a_entries[2] = acl->a_entries[1];  /* ACL_MASK */
12762306a36Sopenharmony_ci		acl2->a_entries[2].e_tag = ACL_MASK;
12862306a36Sopenharmony_ci		acl2->a_entries[3] = acl->a_entries[2];  /* ACL_OTHER */
12962306a36Sopenharmony_ci		nfsacl_desc.acl = acl2;
13062306a36Sopenharmony_ci	}
13162306a36Sopenharmony_ci	err = xdr_encode_array2(buf, base + 4, &nfsacl_desc.desc);
13262306a36Sopenharmony_ci	if (!err)
13362306a36Sopenharmony_ci		err = 8 + nfsacl_desc.desc.elem_size *
13462306a36Sopenharmony_ci			  nfsacl_desc.desc.array_len;
13562306a36Sopenharmony_ci	return err;
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfsacl_encode);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci/**
14062306a36Sopenharmony_ci * nfs_stream_encode_acl - Encode an NFSv3 ACL
14162306a36Sopenharmony_ci *
14262306a36Sopenharmony_ci * @xdr: an xdr_stream positioned to receive an encoded ACL
14362306a36Sopenharmony_ci * @inode: inode of file whose ACL this is
14462306a36Sopenharmony_ci * @acl: posix_acl to encode
14562306a36Sopenharmony_ci * @encode_entries: whether to encode ACEs as well
14662306a36Sopenharmony_ci * @typeflag: ACL type: NFS_ACL_DEFAULT or zero
14762306a36Sopenharmony_ci *
14862306a36Sopenharmony_ci * Return values:
14962306a36Sopenharmony_ci *   %false: The ACL could not be encoded
15062306a36Sopenharmony_ci *   %true: @xdr is advanced to the next available position
15162306a36Sopenharmony_ci */
15262306a36Sopenharmony_cibool nfs_stream_encode_acl(struct xdr_stream *xdr, struct inode *inode,
15362306a36Sopenharmony_ci			   struct posix_acl *acl, int encode_entries,
15462306a36Sopenharmony_ci			   int typeflag)
15562306a36Sopenharmony_ci{
15662306a36Sopenharmony_ci	const size_t elem_size = XDR_UNIT * 3;
15762306a36Sopenharmony_ci	u32 entries = (acl && acl->a_count) ? max_t(int, acl->a_count, 4) : 0;
15862306a36Sopenharmony_ci	struct nfsacl_encode_desc nfsacl_desc = {
15962306a36Sopenharmony_ci		.desc = {
16062306a36Sopenharmony_ci			.elem_size = elem_size,
16162306a36Sopenharmony_ci			.array_len = encode_entries ? entries : 0,
16262306a36Sopenharmony_ci			.xcode = xdr_nfsace_encode,
16362306a36Sopenharmony_ci		},
16462306a36Sopenharmony_ci		.acl = acl,
16562306a36Sopenharmony_ci		.typeflag = typeflag,
16662306a36Sopenharmony_ci		.uid = inode->i_uid,
16762306a36Sopenharmony_ci		.gid = inode->i_gid,
16862306a36Sopenharmony_ci	};
16962306a36Sopenharmony_ci	struct nfsacl_simple_acl aclbuf;
17062306a36Sopenharmony_ci	unsigned int base;
17162306a36Sopenharmony_ci	int err;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	if (entries > NFS_ACL_MAX_ENTRIES)
17462306a36Sopenharmony_ci		return false;
17562306a36Sopenharmony_ci	if (xdr_stream_encode_u32(xdr, entries) < 0)
17662306a36Sopenharmony_ci		return false;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	if (encode_entries && acl && acl->a_count == 3) {
17962306a36Sopenharmony_ci		struct posix_acl *acl2 = &aclbuf.acl;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci		/* Avoid the use of posix_acl_alloc().  nfsacl_encode() is
18262306a36Sopenharmony_ci		 * invoked in contexts where a memory allocation failure is
18362306a36Sopenharmony_ci		 * fatal.  Fortunately this fake ACL is small enough to
18462306a36Sopenharmony_ci		 * construct on the stack. */
18562306a36Sopenharmony_ci		posix_acl_init(acl2, 4);
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci		/* Insert entries in canonical order: other orders seem
18862306a36Sopenharmony_ci		 to confuse Solaris VxFS. */
18962306a36Sopenharmony_ci		acl2->a_entries[0] = acl->a_entries[0];  /* ACL_USER_OBJ */
19062306a36Sopenharmony_ci		acl2->a_entries[1] = acl->a_entries[1];  /* ACL_GROUP_OBJ */
19162306a36Sopenharmony_ci		acl2->a_entries[2] = acl->a_entries[1];  /* ACL_MASK */
19262306a36Sopenharmony_ci		acl2->a_entries[2].e_tag = ACL_MASK;
19362306a36Sopenharmony_ci		acl2->a_entries[3] = acl->a_entries[2];  /* ACL_OTHER */
19462306a36Sopenharmony_ci		nfsacl_desc.acl = acl2;
19562306a36Sopenharmony_ci	}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	base = xdr_stream_pos(xdr);
19862306a36Sopenharmony_ci	if (!xdr_reserve_space(xdr, XDR_UNIT +
19962306a36Sopenharmony_ci			       elem_size * nfsacl_desc.desc.array_len))
20062306a36Sopenharmony_ci		return false;
20162306a36Sopenharmony_ci	err = xdr_encode_array2(xdr->buf, base, &nfsacl_desc.desc);
20262306a36Sopenharmony_ci	if (err)
20362306a36Sopenharmony_ci		return false;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	return true;
20662306a36Sopenharmony_ci}
20762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_stream_encode_acl);
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_cistruct nfsacl_decode_desc {
21162306a36Sopenharmony_ci	struct xdr_array2_desc desc;
21262306a36Sopenharmony_ci	unsigned int count;
21362306a36Sopenharmony_ci	struct posix_acl *acl;
21462306a36Sopenharmony_ci};
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_cistatic int
21762306a36Sopenharmony_cixdr_nfsace_decode(struct xdr_array2_desc *desc, void *elem)
21862306a36Sopenharmony_ci{
21962306a36Sopenharmony_ci	struct nfsacl_decode_desc *nfsacl_desc =
22062306a36Sopenharmony_ci		(struct nfsacl_decode_desc *) desc;
22162306a36Sopenharmony_ci	__be32 *p = elem;
22262306a36Sopenharmony_ci	struct posix_acl_entry *entry;
22362306a36Sopenharmony_ci	unsigned int id;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	if (!nfsacl_desc->acl) {
22662306a36Sopenharmony_ci		if (desc->array_len > NFS_ACL_MAX_ENTRIES)
22762306a36Sopenharmony_ci			return -EINVAL;
22862306a36Sopenharmony_ci		nfsacl_desc->acl = posix_acl_alloc(desc->array_len, GFP_KERNEL);
22962306a36Sopenharmony_ci		if (!nfsacl_desc->acl)
23062306a36Sopenharmony_ci			return -ENOMEM;
23162306a36Sopenharmony_ci		nfsacl_desc->count = 0;
23262306a36Sopenharmony_ci	}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	entry = &nfsacl_desc->acl->a_entries[nfsacl_desc->count++];
23562306a36Sopenharmony_ci	entry->e_tag = ntohl(*p++) & ~NFS_ACL_DEFAULT;
23662306a36Sopenharmony_ci	id = ntohl(*p++);
23762306a36Sopenharmony_ci	entry->e_perm = ntohl(*p++);
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	switch(entry->e_tag) {
24062306a36Sopenharmony_ci		case ACL_USER:
24162306a36Sopenharmony_ci			entry->e_uid = make_kuid(&init_user_ns, id);
24262306a36Sopenharmony_ci			if (!uid_valid(entry->e_uid))
24362306a36Sopenharmony_ci				return -EINVAL;
24462306a36Sopenharmony_ci			break;
24562306a36Sopenharmony_ci		case ACL_GROUP:
24662306a36Sopenharmony_ci			entry->e_gid = make_kgid(&init_user_ns, id);
24762306a36Sopenharmony_ci			if (!gid_valid(entry->e_gid))
24862306a36Sopenharmony_ci				return -EINVAL;
24962306a36Sopenharmony_ci			break;
25062306a36Sopenharmony_ci		case ACL_USER_OBJ:
25162306a36Sopenharmony_ci		case ACL_GROUP_OBJ:
25262306a36Sopenharmony_ci		case ACL_OTHER:
25362306a36Sopenharmony_ci			if (entry->e_perm & ~S_IRWXO)
25462306a36Sopenharmony_ci				return -EINVAL;
25562306a36Sopenharmony_ci			break;
25662306a36Sopenharmony_ci		case ACL_MASK:
25762306a36Sopenharmony_ci			/* Solaris sometimes sets additional bits in the mask */
25862306a36Sopenharmony_ci			entry->e_perm &= S_IRWXO;
25962306a36Sopenharmony_ci			break;
26062306a36Sopenharmony_ci		default:
26162306a36Sopenharmony_ci			return -EINVAL;
26262306a36Sopenharmony_ci	}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	return 0;
26562306a36Sopenharmony_ci}
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_cistatic int
26862306a36Sopenharmony_cicmp_acl_entry(const void *x, const void *y)
26962306a36Sopenharmony_ci{
27062306a36Sopenharmony_ci	const struct posix_acl_entry *a = x, *b = y;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	if (a->e_tag != b->e_tag)
27362306a36Sopenharmony_ci		return a->e_tag - b->e_tag;
27462306a36Sopenharmony_ci	else if ((a->e_tag == ACL_USER) && uid_gt(a->e_uid, b->e_uid))
27562306a36Sopenharmony_ci		return 1;
27662306a36Sopenharmony_ci	else if ((a->e_tag == ACL_USER) && uid_lt(a->e_uid, b->e_uid))
27762306a36Sopenharmony_ci		return -1;
27862306a36Sopenharmony_ci	else if ((a->e_tag == ACL_GROUP) && gid_gt(a->e_gid, b->e_gid))
27962306a36Sopenharmony_ci		return 1;
28062306a36Sopenharmony_ci	else if ((a->e_tag == ACL_GROUP) && gid_lt(a->e_gid, b->e_gid))
28162306a36Sopenharmony_ci		return -1;
28262306a36Sopenharmony_ci	else
28362306a36Sopenharmony_ci		return 0;
28462306a36Sopenharmony_ci}
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci/*
28762306a36Sopenharmony_ci * Convert from a Solaris ACL to a POSIX 1003.1e draft 17 ACL.
28862306a36Sopenharmony_ci */
28962306a36Sopenharmony_cistatic int
29062306a36Sopenharmony_ciposix_acl_from_nfsacl(struct posix_acl *acl)
29162306a36Sopenharmony_ci{
29262306a36Sopenharmony_ci	struct posix_acl_entry *pa, *pe,
29362306a36Sopenharmony_ci	       *group_obj = NULL, *mask = NULL;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	if (!acl)
29662306a36Sopenharmony_ci		return 0;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	sort(acl->a_entries, acl->a_count, sizeof(struct posix_acl_entry),
29962306a36Sopenharmony_ci	     cmp_acl_entry, NULL);
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	/* Find the ACL_GROUP_OBJ and ACL_MASK entries. */
30262306a36Sopenharmony_ci	FOREACH_ACL_ENTRY(pa, acl, pe) {
30362306a36Sopenharmony_ci		switch(pa->e_tag) {
30462306a36Sopenharmony_ci			case ACL_USER_OBJ:
30562306a36Sopenharmony_ci				break;
30662306a36Sopenharmony_ci			case ACL_GROUP_OBJ:
30762306a36Sopenharmony_ci				group_obj = pa;
30862306a36Sopenharmony_ci				break;
30962306a36Sopenharmony_ci			case ACL_MASK:
31062306a36Sopenharmony_ci				mask = pa;
31162306a36Sopenharmony_ci				fallthrough;
31262306a36Sopenharmony_ci			case ACL_OTHER:
31362306a36Sopenharmony_ci				break;
31462306a36Sopenharmony_ci		}
31562306a36Sopenharmony_ci	}
31662306a36Sopenharmony_ci	if (acl->a_count == 4 && group_obj && mask &&
31762306a36Sopenharmony_ci	    mask->e_perm == group_obj->e_perm) {
31862306a36Sopenharmony_ci		/* remove bogus ACL_MASK entry */
31962306a36Sopenharmony_ci		memmove(mask, mask+1, (3 - (mask - acl->a_entries)) *
32062306a36Sopenharmony_ci				      sizeof(struct posix_acl_entry));
32162306a36Sopenharmony_ci		acl->a_count = 3;
32262306a36Sopenharmony_ci	}
32362306a36Sopenharmony_ci	return 0;
32462306a36Sopenharmony_ci}
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci/**
32762306a36Sopenharmony_ci * nfsacl_decode - Decode an NFSv3 ACL
32862306a36Sopenharmony_ci *
32962306a36Sopenharmony_ci * @buf: xdr_buf containing XDR'd ACL data to decode
33062306a36Sopenharmony_ci * @base: byte offset in xdr_buf where XDR'd ACL begins
33162306a36Sopenharmony_ci * @aclcnt: count of ACEs in decoded posix_acl
33262306a36Sopenharmony_ci * @pacl: buffer in which to place decoded posix_acl
33362306a36Sopenharmony_ci *
33462306a36Sopenharmony_ci * Returns the length of the decoded ACL in bytes, or a negative errno value.
33562306a36Sopenharmony_ci */
33662306a36Sopenharmony_ciint nfsacl_decode(struct xdr_buf *buf, unsigned int base, unsigned int *aclcnt,
33762306a36Sopenharmony_ci		  struct posix_acl **pacl)
33862306a36Sopenharmony_ci{
33962306a36Sopenharmony_ci	struct nfsacl_decode_desc nfsacl_desc = {
34062306a36Sopenharmony_ci		.desc = {
34162306a36Sopenharmony_ci			.elem_size = 12,
34262306a36Sopenharmony_ci			.xcode = pacl ? xdr_nfsace_decode : NULL,
34362306a36Sopenharmony_ci		},
34462306a36Sopenharmony_ci	};
34562306a36Sopenharmony_ci	u32 entries;
34662306a36Sopenharmony_ci	int err;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	if (xdr_decode_word(buf, base, &entries) ||
34962306a36Sopenharmony_ci	    entries > NFS_ACL_MAX_ENTRIES)
35062306a36Sopenharmony_ci		return -EINVAL;
35162306a36Sopenharmony_ci	nfsacl_desc.desc.array_maxlen = entries;
35262306a36Sopenharmony_ci	err = xdr_decode_array2(buf, base + 4, &nfsacl_desc.desc);
35362306a36Sopenharmony_ci	if (err)
35462306a36Sopenharmony_ci		return err;
35562306a36Sopenharmony_ci	if (pacl) {
35662306a36Sopenharmony_ci		if (entries != nfsacl_desc.desc.array_len ||
35762306a36Sopenharmony_ci		    posix_acl_from_nfsacl(nfsacl_desc.acl) != 0) {
35862306a36Sopenharmony_ci			posix_acl_release(nfsacl_desc.acl);
35962306a36Sopenharmony_ci			return -EINVAL;
36062306a36Sopenharmony_ci		}
36162306a36Sopenharmony_ci		*pacl = nfsacl_desc.acl;
36262306a36Sopenharmony_ci	}
36362306a36Sopenharmony_ci	if (aclcnt)
36462306a36Sopenharmony_ci		*aclcnt = entries;
36562306a36Sopenharmony_ci	return 8 + nfsacl_desc.desc.elem_size *
36662306a36Sopenharmony_ci		   nfsacl_desc.desc.array_len;
36762306a36Sopenharmony_ci}
36862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfsacl_decode);
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci/**
37162306a36Sopenharmony_ci * nfs_stream_decode_acl - Decode an NFSv3 ACL
37262306a36Sopenharmony_ci *
37362306a36Sopenharmony_ci * @xdr: an xdr_stream positioned at an encoded ACL
37462306a36Sopenharmony_ci * @aclcnt: OUT: count of ACEs in decoded posix_acl
37562306a36Sopenharmony_ci * @pacl: OUT: a dynamically-allocated buffer containing the decoded posix_acl
37662306a36Sopenharmony_ci *
37762306a36Sopenharmony_ci * Return values:
37862306a36Sopenharmony_ci *   %false: The encoded ACL is not valid
37962306a36Sopenharmony_ci *   %true: @pacl contains a decoded ACL, and @xdr is advanced
38062306a36Sopenharmony_ci *
38162306a36Sopenharmony_ci * On a successful return, caller must release *pacl using posix_acl_release().
38262306a36Sopenharmony_ci */
38362306a36Sopenharmony_cibool nfs_stream_decode_acl(struct xdr_stream *xdr, unsigned int *aclcnt,
38462306a36Sopenharmony_ci			   struct posix_acl **pacl)
38562306a36Sopenharmony_ci{
38662306a36Sopenharmony_ci	const size_t elem_size = XDR_UNIT * 3;
38762306a36Sopenharmony_ci	struct nfsacl_decode_desc nfsacl_desc = {
38862306a36Sopenharmony_ci		.desc = {
38962306a36Sopenharmony_ci			.elem_size = elem_size,
39062306a36Sopenharmony_ci			.xcode = pacl ? xdr_nfsace_decode : NULL,
39162306a36Sopenharmony_ci		},
39262306a36Sopenharmony_ci	};
39362306a36Sopenharmony_ci	unsigned int base;
39462306a36Sopenharmony_ci	u32 entries;
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	if (xdr_stream_decode_u32(xdr, &entries) < 0)
39762306a36Sopenharmony_ci		return false;
39862306a36Sopenharmony_ci	if (entries > NFS_ACL_MAX_ENTRIES)
39962306a36Sopenharmony_ci		return false;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	base = xdr_stream_pos(xdr);
40262306a36Sopenharmony_ci	if (!xdr_inline_decode(xdr, XDR_UNIT + elem_size * entries))
40362306a36Sopenharmony_ci		return false;
40462306a36Sopenharmony_ci	nfsacl_desc.desc.array_maxlen = entries;
40562306a36Sopenharmony_ci	if (xdr_decode_array2(xdr->buf, base, &nfsacl_desc.desc))
40662306a36Sopenharmony_ci		return false;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	if (pacl) {
40962306a36Sopenharmony_ci		if (entries != nfsacl_desc.desc.array_len ||
41062306a36Sopenharmony_ci		    posix_acl_from_nfsacl(nfsacl_desc.acl) != 0) {
41162306a36Sopenharmony_ci			posix_acl_release(nfsacl_desc.acl);
41262306a36Sopenharmony_ci			return false;
41362306a36Sopenharmony_ci		}
41462306a36Sopenharmony_ci		*pacl = nfsacl_desc.acl;
41562306a36Sopenharmony_ci	}
41662306a36Sopenharmony_ci	if (aclcnt)
41762306a36Sopenharmony_ci		*aclcnt = entries;
41862306a36Sopenharmony_ci	return true;
41962306a36Sopenharmony_ci}
42062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_stream_decode_acl);
421