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