Merge tag 'for-v5.12' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux...
[linux-2.6-microblaze.git] / fs / nfs_common / nfsacl.c
index d056ad2..79c563c 100644 (file)
@@ -295,3 +295,55 @@ int nfsacl_decode(struct xdr_buf *buf, unsigned int base, unsigned int *aclcnt,
                   nfsacl_desc.desc.array_len;
 }
 EXPORT_SYMBOL_GPL(nfsacl_decode);
+
+/**
+ * nfs_stream_decode_acl - Decode an NFSv3 ACL
+ *
+ * @xdr: an xdr_stream positioned at an encoded ACL
+ * @aclcnt: OUT: count of ACEs in decoded posix_acl
+ * @pacl: OUT: a dynamically-allocated buffer containing the decoded posix_acl
+ *
+ * Return values:
+ *   %false: The encoded ACL is not valid
+ *   %true: @pacl contains a decoded ACL, and @xdr is advanced
+ *
+ * On a successful return, caller must release *pacl using posix_acl_release().
+ */
+bool nfs_stream_decode_acl(struct xdr_stream *xdr, unsigned int *aclcnt,
+                          struct posix_acl **pacl)
+{
+       const size_t elem_size = XDR_UNIT * 3;
+       struct nfsacl_decode_desc nfsacl_desc = {
+               .desc = {
+                       .elem_size = elem_size,
+                       .xcode = pacl ? xdr_nfsace_decode : NULL,
+               },
+       };
+       unsigned int base;
+       u32 entries;
+
+       if (xdr_stream_decode_u32(xdr, &entries) < 0)
+               return false;
+       if (entries > NFS_ACL_MAX_ENTRIES)
+               return false;
+
+       base = xdr_stream_pos(xdr);
+       if (!xdr_inline_decode(xdr, XDR_UNIT + elem_size * entries))
+               return false;
+       nfsacl_desc.desc.array_maxlen = entries;
+       if (xdr_decode_array2(xdr->buf, base, &nfsacl_desc.desc))
+               return false;
+
+       if (pacl) {
+               if (entries != nfsacl_desc.desc.array_len ||
+                   posix_acl_from_nfsacl(nfsacl_desc.acl) != 0) {
+                       posix_acl_release(nfsacl_desc.acl);
+                       return false;
+               }
+               *pacl = nfsacl_desc.acl;
+       }
+       if (aclcnt)
+               *aclcnt = entries;
+       return true;
+}
+EXPORT_SYMBOL_GPL(nfs_stream_decode_acl);