Linux NFS development
 help / color / mirror / Atom feed
From: Chuck Lever <cel@kernel.org>
To: NeilBrown <neil@brown.name>, Jeff Layton <jlayton@kernel.org>,
	Olga Kornievskaia <okorniev@redhat.com>,
	Dai Ngo <dai.ngo@oracle.com>, Tom Talpey <tom@talpey.com>
Cc: <linux-nfs@vger.kernel.org>
Subject: [PATCH v1 06/13] NFSD: Implement NFS-ACL v3 ACL encoding and decoding with xdrgen
Date: Sun, 25 Oct 2026 11:16:15 -0400	[thread overview]
Message-ID: <20261025151622.141025-7-cel@kernel.org> (raw)
In-Reply-To: <20261025151622.141025-1-cel@kernel.org>

The NFS-ACL v3 GETACL result encoder and SETACL argument decoder
cannot be generated the way the other procedures' codecs are: the
wire secattr is a Solaris projection of the kernel's struct
posix_acl, and the per-entry transformation between them -- tag and
id mapping, the canonical four-entry expansion that Solaris VxFS
interoperability requires, and counts gated by the request mask --
is application logic that xdrgen cannot synthesize. Keeping those two
codecs hand-rolled would force the generated NFSv3 types they call,
nfs_fh3, fattr3, and post_op_attr, to keep external linkage, and
those symbols would collide at link time with the public
definitions that nfs3xdr_gen.c already emits for NFSv3 proper.

Supply the nfsd side of xdrgen's hook-driven aggregate codec
mechanism. "pragma aggregate" markers on secattr's two aclent<>
members select the streaming codec, whose generated framing owns
the wire representation and drives per-element application hooks. The
hooks hold the posix_acl logic factored from
nfs_stream_encode_acl() and nfs_stream_decode_acl(); both arrays
share one hook set, with the cursor's member_id distinguishing the
access list from the default list. Wrapper structures pair each
generated argument or result type with the legacy application state
(svc_fh, posix_acl pointers, kstat), placing the generated type at
offset zero so rq_argp or rq_resp addresses both the generated type
the codec fills and the wrapper the hooks read. The hooks reach
that wrapper by way of the svc_rqst, which svcxdr_init_decode() and
svcxdr_init_encode() leave in the xdr_stream's application-private
pointer.

On decode, the begin hook validates the declared count and the
request mask, allocates the posix_acl at its known size, and
publishes it into the argument wrapper before any fallible
per-entry decode runs, so a mid-list failure leaves the ACL
reachable by pc_release rather than leaked. A zero-length published
default list stays NULL, which set_posix_acl() treats as ACL
removal.

A well-formed request that is nonetheless invalid -- an undefined
request mask bit, an empty access ACL, a perm bit outside rwx, an
undefined entry type, a user or group id with no kernel mapping, a
list whose declared count differs from its length, or more entries
than NFS_ACL_MAX_ENTRIES -- instead marks the argument wrapper and
lets the decode succeed, so nfsd3_proc_setacl() can report
ACL3ERR_INVAL rather than GARBAGE_ARGS (draft-ietf-nfsv4-nfs-acl
Section 3.4.4.2). The pre-conversion decoder rejected most of these
cases with GARBAGE_ARGS. It accepted an empty access ACL as a request
to remove the object's access ACL, an operation the protocol does not
define, and it cleared perm bits outside rwx in an NA_CLASS_OBJ entry
without complaint because Solaris clients were seen to set them. The
draft makes both invalid, so the NFS_ACLv3 server now reports
ACL3ERR_INVAL for them as well. These are deliberate, minor changes to
the SETACL wire behavior.

Because the containing codecs are fully generated, the NFSv3 types
the ACL specification re-declares stay static in nfs_acl3xdr_gen.c,
and no public pragma is needed.

Subsequent patches convert the GETACL and SETACL procedures to the
generated codecs.

Signed-off-by: Chuck Lever <cel@kernel.org>
---
 Documentation/sunrpc/xdr/nfs_acl3.x    |   2 +
 fs/nfsd/nfs3acl.c                      | 271 +++++++++++++++++++++++++
 fs/nfsd/nfs_acl3xdr_gen.c              | 138 ++++++++++---
 fs/nfsd/nfs_acl3xdr_gen.h              |  14 +-
 include/linux/sunrpc/xdrgen/nfs_acl3.h |   2 +-
 5 files changed, 400 insertions(+), 27 deletions(-)

diff --git a/Documentation/sunrpc/xdr/nfs_acl3.x b/Documentation/sunrpc/xdr/nfs_acl3.x
index b355705bf970..931a7d629603 100644
--- a/Documentation/sunrpc/xdr/nfs_acl3.x
+++ b/Documentation/sunrpc/xdr/nfs_acl3.x
@@ -91,6 +91,8 @@ struct secattr {
     int dfaclcnt;
     aclent dfaclent<NFS_ACL_MAX_ENTRIES>;
 };
+pragma aggregate secattr aclent;
+pragma aggregate secattr dfaclent;
 
 /*
  * The values for the mask element of the secattr struct as well
diff --git a/fs/nfsd/nfs3acl.c b/fs/nfsd/nfs3acl.c
index 048046af15c0..1fb9d265bc03 100644
--- a/fs/nfsd/nfs3acl.c
+++ b/fs/nfsd/nfs3acl.c
@@ -18,6 +18,52 @@
 
 #define NFS_ACL_MASK		(NA_ACL | NA_ACLCNT | NA_DFACL | NA_DFACLCNT)
 
+/*
+ * Wrapper structures combine xdrgen types with legacy structures.
+ * The xdrgen field must be first so the structure can be cast
+ * to its XDR type for the RPC dispatch layer.
+ */
+struct GETACL3args_wrapper {
+	struct GETACL3args	xdrgen;
+	struct svc_fh		fh;
+};
+
+static_assert(offsetof(struct GETACL3args_wrapper, xdrgen) == 0);
+
+/*
+ * The ACL is encoded from the posix_acl fields below, so the
+ * generated secattr (xdrgen.u.resok.acl) is left unused.
+ */
+struct GETACL3res_wrapper {
+	struct GETACL3res	xdrgen;
+	int			mask;
+	struct posix_acl	*acl_access;
+	struct posix_acl	*acl_default;
+	struct kstat		stat;
+};
+
+static_assert(offsetof(struct GETACL3res_wrapper, xdrgen) == 0);
+
+/*
+ * The ACLs are decoded by hand into the posix_acl fields below, so
+ * the generated aclent arrays (xdrgen.acl) are left unused.
+ */
+struct SETACL3args_wrapper {
+	struct SETACL3args	xdrgen;
+	struct svc_fh		fh;
+	struct posix_acl	*acl_access;
+	struct posix_acl	*acl_default;
+	/*
+	 * Set when the wire ACL is well-formed XDR but semantically
+	 * invalid. The decoder accepts the request so that
+	 * nfsd3_proc_setacl can return ACL3ERR_INVAL rather than RPC
+	 * GARBAGE_ARGS. See draft-ietf-nfsv4-nfs-acl Section 3.4.4.2.
+	 */
+	bool			invalid;
+};
+
+static_assert(offsetof(struct SETACL3args_wrapper, xdrgen) == 0);
+
 struct nfsd3_getaclargs {
 	struct svc_fh		fh;
 	__u32			mask;
@@ -110,6 +156,19 @@ static __be32 nfsd3_map_status(__be32 status)
 	return status;
 }
 
+/*
+ * A minimal (three-entry) ACL travels as four wire entries: the
+ * POSIX entries plus a synthesized NA_CLASS_OBJ mask. The wire
+ * element count is thus max(a_count, 4) when the ACL has at least
+ * three entries, and zero otherwise. An ACL with fewer than three
+ * entries has no minimal set to expand, so it travels as empty.
+ * See draft-ietf-nfsv4-nfs-acl Section 3.4.4.2.
+ */
+static u32 nfs3acl_acl_entries(const struct posix_acl *acl)
+{
+	return (acl && acl->a_count >= 3) ? max_t(int, acl->a_count, 4) : 0;
+}
+
 /**
  * nfsd3_proc_null - NULL: Do nothing
  * @rqstp: RPC transaction context
@@ -318,6 +377,218 @@ nfs3svc_encode_setaclres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
 		svcxdr_encode_post_op_attr(rqstp, xdr, &resp->fh);
 }
 
+/*
+ * Aggregate codec hooks for the generated secattr codec.
+ *
+ * The generated codec owns the wire framing and drives these hooks
+ * once per element; the hooks hold all posix_acl application logic. The
+ * application context (@c->ctx) is the svc_rqst, bound to the stream by
+ * svcxdr_init_{decode,encode}(); each hook reaches the proc's arg or
+ * result wrapper through rq_{argp,resp}.
+ * The cursor's member_id selects the member: it is
+ * NFS_ACL3_SECATTR_ACLENT or NFS_ACL3_SECATTR_DFACLENT.
+ */
+
+/* GETACL reply: stream the wire entries out of the source posix_acl. */
+
+bool nfs_acl3_secattr_encode_begin(struct xdrgen_aggregate_cursor *c)
+{
+	struct svc_rqst *rqstp = c->ctx;
+	struct GETACL3res_wrapper *resp = rqstp->rq_resp;
+	bool dfacl = c->member_id == NFS_ACL3_SECATTR_DFACLENT;
+	struct posix_acl *acl = dfacl ? resp->acl_default : resp->acl_access;
+	int gate = dfacl ? (resp->mask & NA_DFACL) : (resp->mask & NA_ACL);
+
+	c->count = gate ? nfs3acl_acl_entries(acl) : 0;
+	return true;
+}
+
+bool nfs_acl3_secattr_encode(struct xdrgen_aggregate_cursor *c,
+			     struct aclent *out)
+{
+	struct svc_rqst *rqstp = c->ctx;
+	struct GETACL3res_wrapper *resp = rqstp->rq_resp;
+	bool dfacl = c->member_id == NFS_ACL3_SECATTR_DFACLENT;
+	struct posix_acl *acl = dfacl ? resp->acl_default : resp->acl_access;
+	int typeflag = dfacl ? NA_ACL_DEFAULT : 0;
+	const struct posix_acl_entry *entry;
+	int tag;
+
+	/*
+	 * A three-entry ACL expands to four wire entries in ascending
+	 * type order (USER_OBJ, GROUP_OBJ, a NA_CLASS_OBJ mask cloned
+	 * from GROUP_OBJ, OTHER). Some receivers depend on this ordering.
+	 * See draft-ietf-nfsv4-nfs-acl Section 3.4.4.2.
+	 */
+	if (acl->a_count == 3) {
+		static const u8 map[4] = { 0, 1, 1, 2 };
+
+		entry = &acl->a_entries[map[c->index]];
+		tag = (c->index == 2) ? ACL_MASK : entry->e_tag;
+	} else {
+		entry = &acl->a_entries[c->index];
+		tag = entry->e_tag;
+	}
+
+	out->type = tag | typeflag;
+	switch (tag) {
+	case ACL_USER_OBJ:
+		out->id = from_kuid(&init_user_ns, resp->stat.uid);
+		break;
+	case ACL_GROUP_OBJ:
+		out->id = from_kgid(&init_user_ns, resp->stat.gid);
+		break;
+	case ACL_USER:
+		out->id = from_kuid(&init_user_ns, entry->e_uid);
+		break;
+	case ACL_GROUP:
+		out->id = from_kgid(&init_user_ns, entry->e_gid);
+		break;
+	default:
+		/*
+		 * The id field is undefined for these entry types;
+		 * emit zero per draft-ietf-nfsv4-nfs-acl Section 3.4.4.2.
+		 */
+		out->id = 0;
+		break;
+	}
+	out->perm = entry->e_perm & (ACL_READ | ACL_WRITE | ACL_EXECUTE);
+	return true;
+}
+
+bool nfs_acl3_secattr_encode_end(struct xdrgen_aggregate_cursor *c, bool ok)
+{
+	return true;
+}
+
+/* SETACL args: consume the decoded wire entries into the target posix_acl. */
+
+bool nfs_acl3_secattr_decode_begin(struct xdrgen_aggregate_cursor *c)
+{
+	struct svc_rqst *rqstp = c->ctx;
+	struct SETACL3args_wrapper *argp = rqstp->rq_argp;
+	bool dfacl = c->member_id == NFS_ACL3_SECATTR_DFACLENT;
+	struct posix_acl **pacl = dfacl ? &argp->acl_default : &argp->acl_access;
+	u32 mask = argp->xdrgen.acl.mask;
+	u32 declared = dfacl ? (u32)argp->xdrgen.acl.dfaclcnt :
+			       (u32)argp->xdrgen.acl.aclcnt;
+	int gate = dfacl ? (mask & NA_DFACL) : (mask & NA_ACL);
+
+	/* Already rejected: consume the list. */
+	if (argp->invalid)
+		return true;
+	/* The access list is decoded first, so the mask is checked once. */
+	if (!dfacl && (mask & ~NFS_ACL_MASK)) {
+		argp->invalid = true;
+		return true;
+	}
+	/* Too many ACEs is an invalid ACL, not a malformed request. */
+	if (declared > NFS_ACL_MAX_ENTRIES) {
+		argp->invalid = true;
+		return true;
+	}
+
+	if (gate) {
+		/*
+		 * A published list whose declared count differs from its
+		 * length is an invalid ACL, not a malformed request.
+		 */
+		if (c->count != declared) {
+			argp->invalid = true;
+			return true;
+		}
+		if (!dfacl && !c->count) {
+			argp->invalid = true;
+			return true;
+		}
+		/* An empty default list publishes NULL: default ACL removal. */
+		if (c->count) {
+			*pacl = posix_acl_alloc(c->count, GFP_KERNEL);
+			if (!*pacl)
+				return false;
+		}
+	} else if (c->count > declared) {
+		argp->invalid = true;
+		return true;
+	}
+	return true;
+}
+
+bool nfs_acl3_secattr_decode(struct xdrgen_aggregate_cursor *c,
+			     const struct aclent *in)
+{
+	struct svc_rqst *rqstp = c->ctx;
+	struct SETACL3args_wrapper *argp = rqstp->rq_argp;
+	bool dfacl = c->member_id == NFS_ACL3_SECATTR_DFACLENT;
+	struct posix_acl *acl = dfacl ? argp->acl_default : argp->acl_access;
+	struct posix_acl_entry *entry;
+
+	/* Already rejected, or list gated out by mask: consume it. */
+	if (argp->invalid || !acl)
+		return true;
+
+	/* A perm bit outside rwx makes the ACL invalid. */
+	if (in->perm & ~(ACL_READ | ACL_WRITE | ACL_EXECUTE)) {
+		argp->invalid = true;
+		return true;
+	}
+
+	/* A type bit the protocol does not define makes the ACL invalid. */
+	if (in->type & ~(NA_USER_OBJ | NA_USER | NA_GROUP_OBJ | NA_GROUP |
+			 NA_CLASS_OBJ | NA_OTHER_OBJ | NA_ACL_DEFAULT)) {
+		argp->invalid = true;
+		return true;
+	}
+
+	entry = &acl->a_entries[c->index];
+	entry->e_tag = in->type & ~NA_ACL_DEFAULT;
+	entry->e_perm = in->perm;
+	switch (entry->e_tag) {
+	case ACL_USER:
+		/* An id with no kernel mapping makes the ACL invalid. */
+		entry->e_uid = make_kuid(&init_user_ns, in->id);
+		if (!uid_valid(entry->e_uid)) {
+			argp->invalid = true;
+			return true;
+		}
+		break;
+	case ACL_GROUP:
+		entry->e_gid = make_kgid(&init_user_ns, in->id);
+		if (!gid_valid(entry->e_gid)) {
+			argp->invalid = true;
+			return true;
+		}
+		break;
+	case ACL_USER_OBJ:
+	case ACL_GROUP_OBJ:
+	case ACL_OTHER:
+	case ACL_MASK:
+		break;
+	default:
+		/* An undefined entry type makes the ACL invalid. */
+		argp->invalid = true;
+		return true;
+	}
+
+	return true;
+}
+
+bool nfs_acl3_secattr_decode_end(struct xdrgen_aggregate_cursor *c, bool ok)
+{
+	struct svc_rqst *rqstp = c->ctx;
+	struct SETACL3args_wrapper *argp = rqstp->rq_argp;
+	bool dfacl = c->member_id == NFS_ACL3_SECATTR_DFACLENT;
+	struct posix_acl *acl = dfacl ? argp->acl_default : argp->acl_access;
+
+	/*
+	 * On failure the published ACL stays reachable through the arg
+	 * wrapper and is freed by pc_release; nothing to do here.
+	 */
+	if (!ok || argp->invalid)
+		return true;
+	return posix_acl_from_nfsacl(acl) == 0;
+}
+
 /*
  * XDR release functions
  */
diff --git a/fs/nfsd/nfs_acl3xdr_gen.c b/fs/nfsd/nfs_acl3xdr_gen.c
index 05e35653a0fc..bbd8bb7d79d3 100644
--- a/fs/nfsd/nfs_acl3xdr_gen.c
+++ b/fs/nfsd/nfs_acl3xdr_gen.c
@@ -1,7 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0
 // Generated by xdrgen. Manual edits will be lost.
 // XDR specification file: ../../Documentation/sunrpc/xdr/nfs_acl3.x
-// XDR specification modification time: Fri Jul 10 20:12:08 2026
+// XDR specification modification time: Fri Jul 10 20:12:44 2026
 
 #include <linux/sunrpc/svc.h>
 
@@ -38,22 +38,66 @@ xdrgen_decode_secattr(struct xdr_stream *xdr, struct secattr *ptr)
 		return false;
 	if (!xdrgen_decode_int(xdr, &ptr->aclcnt))
 		return false;
-	if (xdr_stream_decode_u32(xdr, &ptr->aclent.count) < 0)
-		return false;
-	if (ptr->aclent.count > NFS_ACL_MAX_ENTRIES)
-		return false;
-	for (u32 i = 0; i < ptr->aclent.count; i++)
-		if (!xdrgen_decode_aclent(xdr, &ptr->aclent.element[i]))
+	{
+		struct xdrgen_aggregate_cursor cursor = {
+			.xdr = xdr,
+			.member_id = NFS_ACL3_SECATTR_ACLENT,
+			.ctx = xdr->xdrgen_ctx,
+		};
+		bool ok = true;
+
+		if (xdr_stream_decode_u32(xdr, &cursor.count) < 0)
 			return false;
+		if (cursor.count > NFS_ACL_MAX_ENTRIES)
+			return false;
+		if (!nfs_acl3_secattr_decode_begin(&cursor))
+			return false;
+		for (cursor.index = 0; cursor.index < cursor.count; cursor.index++) {
+			struct aclent element = {};
+
+			if (!xdrgen_decode_aclent(xdr, &element)) {
+				ok = false;
+				break;
+			}
+			if (!nfs_acl3_secattr_decode(&cursor, &element)) {
+				ok = false;
+				break;
+			}
+		}
+		if (!nfs_acl3_secattr_decode_end(&cursor, ok) || !ok)
+			return false;
+	}
 	if (!xdrgen_decode_int(xdr, &ptr->dfaclcnt))
 		return false;
-	if (xdr_stream_decode_u32(xdr, &ptr->dfaclent.count) < 0)
-		return false;
-	if (ptr->dfaclent.count > NFS_ACL_MAX_ENTRIES)
-		return false;
-	for (u32 i = 0; i < ptr->dfaclent.count; i++)
-		if (!xdrgen_decode_aclent(xdr, &ptr->dfaclent.element[i]))
+	{
+		struct xdrgen_aggregate_cursor cursor = {
+			.xdr = xdr,
+			.member_id = NFS_ACL3_SECATTR_DFACLENT,
+			.ctx = xdr->xdrgen_ctx,
+		};
+		bool ok = true;
+
+		if (xdr_stream_decode_u32(xdr, &cursor.count) < 0)
 			return false;
+		if (cursor.count > NFS_ACL_MAX_ENTRIES)
+			return false;
+		if (!nfs_acl3_secattr_decode_begin(&cursor))
+			return false;
+		for (cursor.index = 0; cursor.index < cursor.count; cursor.index++) {
+			struct aclent element = {};
+
+			if (!xdrgen_decode_aclent(xdr, &element)) {
+				ok = false;
+				break;
+			}
+			if (!nfs_acl3_secattr_decode(&cursor, &element)) {
+				ok = false;
+				break;
+			}
+		}
+		if (!nfs_acl3_secattr_decode_end(&cursor, ok) || !ok)
+			return false;
+	}
 	return true;
 }
 
@@ -431,22 +475,66 @@ xdrgen_encode_secattr(struct xdr_stream *xdr, const struct secattr *value)
 		return false;
 	if (!xdrgen_encode_int(xdr, value->aclcnt))
 		return false;
-	if (value->aclent.count > NFS_ACL_MAX_ENTRIES)
-		return false;
-	if (xdr_stream_encode_u32(xdr, value->aclent.count) != XDR_UNIT)
-		return false;
-	for (u32 i = 0; i < value->aclent.count; i++)
-		if (!xdrgen_encode_aclent(xdr, &value->aclent.element[i]))
+	{
+		struct xdrgen_aggregate_cursor cursor = {
+			.xdr = xdr,
+			.member_id = NFS_ACL3_SECATTR_ACLENT,
+			.ctx = xdr->xdrgen_ctx,
+		};
+		bool ok = true;
+
+		if (!nfs_acl3_secattr_encode_begin(&cursor))
 			return false;
+		if (cursor.count > NFS_ACL_MAX_ENTRIES)
+			ok = false;
+		if (ok && xdr_stream_encode_u32(xdr, cursor.count) != XDR_UNIT)
+			ok = false;
+		for (cursor.index = 0; ok && cursor.index < cursor.count; cursor.index++) {
+			struct aclent element = {};
+
+			if (!nfs_acl3_secattr_encode(&cursor, &element)) {
+				ok = false;
+				break;
+			}
+			if (!xdrgen_encode_aclent(xdr, &element)) {
+				ok = false;
+				break;
+			}
+		}
+		if (!nfs_acl3_secattr_encode_end(&cursor, ok) || !ok)
+			return false;
+	}
 	if (!xdrgen_encode_int(xdr, value->dfaclcnt))
 		return false;
-	if (value->dfaclent.count > NFS_ACL_MAX_ENTRIES)
-		return false;
-	if (xdr_stream_encode_u32(xdr, value->dfaclent.count) != XDR_UNIT)
-		return false;
-	for (u32 i = 0; i < value->dfaclent.count; i++)
-		if (!xdrgen_encode_aclent(xdr, &value->dfaclent.element[i]))
+	{
+		struct xdrgen_aggregate_cursor cursor = {
+			.xdr = xdr,
+			.member_id = NFS_ACL3_SECATTR_DFACLENT,
+			.ctx = xdr->xdrgen_ctx,
+		};
+		bool ok = true;
+
+		if (!nfs_acl3_secattr_encode_begin(&cursor))
 			return false;
+		if (cursor.count > NFS_ACL_MAX_ENTRIES)
+			ok = false;
+		if (ok && xdr_stream_encode_u32(xdr, cursor.count) != XDR_UNIT)
+			ok = false;
+		for (cursor.index = 0; ok && cursor.index < cursor.count; cursor.index++) {
+			struct aclent element = {};
+
+			if (!nfs_acl3_secattr_encode(&cursor, &element)) {
+				ok = false;
+				break;
+			}
+			if (!xdrgen_encode_aclent(xdr, &element)) {
+				ok = false;
+				break;
+			}
+		}
+		if (!nfs_acl3_secattr_encode_end(&cursor, ok) || !ok)
+			return false;
+	}
 	return true;
 }
 
diff --git a/fs/nfsd/nfs_acl3xdr_gen.h b/fs/nfsd/nfs_acl3xdr_gen.h
index 8d960c0506db..629a06566ca7 100644
--- a/fs/nfsd/nfs_acl3xdr_gen.h
+++ b/fs/nfsd/nfs_acl3xdr_gen.h
@@ -1,7 +1,7 @@
 /* SPDX-License-Identifier: GPL-2.0 */
 /* Generated by xdrgen. Manual edits will be lost. */
 /* XDR specification file: ../../Documentation/sunrpc/xdr/nfs_acl3.x */
-/* XDR specification modification time: Fri Jul 10 20:12:08 2026 */
+/* XDR specification modification time: Fri Jul 10 20:12:44 2026 */
 
 #ifndef _LINUX_XDRGEN_NFS_ACL3_DECL_H
 #define _LINUX_XDRGEN_NFS_ACL3_DECL_H
@@ -13,6 +13,18 @@
 #include <linux/sunrpc/xdrgen/_builtins.h>
 #include <linux/sunrpc/xdrgen/nfs_acl3.h>
 
+enum {
+	NFS_ACL3_SECATTR_ACLENT,
+	NFS_ACL3_SECATTR_DFACLENT,
+};
+
+bool nfs_acl3_secattr_encode_begin(struct xdrgen_aggregate_cursor *c);
+bool nfs_acl3_secattr_encode(struct xdrgen_aggregate_cursor *c, struct aclent *out);
+bool nfs_acl3_secattr_encode_end(struct xdrgen_aggregate_cursor *c, bool ok);
+bool nfs_acl3_secattr_decode_begin(struct xdrgen_aggregate_cursor *c);
+bool nfs_acl3_secattr_decode(struct xdrgen_aggregate_cursor *c, const struct aclent *in);
+bool nfs_acl3_secattr_decode_end(struct xdrgen_aggregate_cursor *c, bool ok);
+
 bool nfs_acl_svc_decode_GETACL3args(struct svc_rqst *rqstp, struct xdr_stream *xdr);
 bool nfs_acl_svc_decode_SETACL3args(struct svc_rqst *rqstp, struct xdr_stream *xdr);
 bool nfs_acl_svc_decode_GETXATTRDIR3args(struct svc_rqst *rqstp, struct xdr_stream *xdr);
diff --git a/include/linux/sunrpc/xdrgen/nfs_acl3.h b/include/linux/sunrpc/xdrgen/nfs_acl3.h
index 4d71e4907261..92c0d1e0b0a8 100644
--- a/include/linux/sunrpc/xdrgen/nfs_acl3.h
+++ b/include/linux/sunrpc/xdrgen/nfs_acl3.h
@@ -1,7 +1,7 @@
 /* SPDX-License-Identifier: GPL-2.0 */
 /* Generated by xdrgen. Manual edits will be lost. */
 /* XDR specification file: ../../Documentation/sunrpc/xdr/nfs_acl3.x */
-/* XDR specification modification time: Fri Jul 10 20:12:08 2026 */
+/* XDR specification modification time: Fri Jul 10 20:12:44 2026 */
 
 #ifndef _LINUX_XDRGEN_NFS_ACL3_DEF_H
 #define _LINUX_XDRGEN_NFS_ACL3_DEF_H
-- 
2.56.0


  parent reply	other threads:[~2026-10-26 21:36 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-25 15:16 [PATCH v1 00/13] Convert server-side NFS_ACLv3 XDR to use xdrgen Chuck Lever
2026-10-25 15:16 ` [PATCH v1 01/13] Documentation: Add the RPC language description of NFS_ACLv3 Chuck Lever
2026-10-25 15:16 ` [PATCH v1 02/13] NFSD: Add infrastructure for generating NFS_ACLv3 XDR encoders and decoders Chuck Lever
2026-10-25 15:16 ` [PATCH v1 03/13] NFSD: Remove '#include "xdr3.h"' from nfs3acl.c Chuck Lever
2026-10-25 15:16 ` [PATCH v1 04/13] NFSD: New NFS_ACLv3 status codes Chuck Lever
2026-10-25 15:16 ` [PATCH v1 05/13] NFSD: Use xdrgen XDR functions for the NFS-ACL v3 NULL procedure Chuck Lever
2026-10-25 15:16 ` Chuck Lever [this message]
2026-10-25 15:16 ` [PATCH v1 07/13] NFSD: Use xdrgen XDR functions for the NFS-ACL v3 GETACL procedure Chuck Lever
2026-10-25 15:16 ` [PATCH v1 08/13] NFSD: Use xdrgen XDR functions for the NFS-ACL v3 SETACL procedure Chuck Lever
2026-10-25 15:16 ` [PATCH v1 09/13] NFSD: Map every NFS_ACLv3 reply status into aclstat3 Chuck Lever
2026-10-25 15:16 ` [PATCH v1 10/13] NFSD: Remove the hand-coded XDR helpers for the NFS_ACLv3 procedures Chuck Lever
2026-10-25 15:16 ` [PATCH v1 11/13] NFS: Move NFSACL_MAXPAGES into fs/nfs/nfs3_fs.h Chuck Lever
2026-10-26 22:50   ` Anna Schumaker
2026-10-25 15:16 ` [PATCH v1 12/13] NFSD: Share the attribute conversion helpers with the NFS_ACL procs Chuck Lever
2026-10-25 15:16 ` [PATCH v1 13/13] nfs_common: Remove the unused NFS ACL stream codecs Chuck Lever
2026-10-27 10:56 ` [PATCH v1 00/13] Convert server-side NFS_ACLv3 XDR to use xdrgen Jeff Layton

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261025151622.141025-7-cel@kernel.org \
    --to=cel@kernel.org \
    --cc=dai.ngo@oracle.com \
    --cc=jlayton@kernel.org \
    --cc=linux-nfs@vger.kernel.org \
    --cc=neil@brown.name \
    --cc=okorniev@redhat.com \
    --cc=tom@talpey.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox