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 12/17] NFSD: Implement NFS-ACL v2 ACL encoding and decoding with xdrgen
Date: Thu, 22 Oct 2026 09:53:36 -0400	[thread overview]
Message-ID: <20261022135341.14265-13-cel@kernel.org> (raw)
In-Reply-To: <20261022135341.14265-1-cel@kernel.org>

The NFS-ACL v2 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 described in
draft-ietf-nfsv4-nfs-acl Section 3.4.4.2, and counts gated by the
request mask -- is application logic that xdrgen cannot synthesize.
Keeping those two codecs hand-rolled would force the generated
NFSv2 types they call, fattr and fhandle, to keep external linkage,
and those symbols would collide at link time with the public
definitions that nfs2xdr_gen.c already emits for NFSv2 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, or more entries than NFS_ACL_MAX_ENTRIES --
instead marks the argument wrapper and lets the decode succeed, so
nfsacld_proc_setacl() can report ACL2ERR_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. It also cleared undefined perm bits from the mask entry, an
accommodation for Solaris clients that the draft does not carry,
and it did not check the perm field of a user or group entry at
all. Reporting ACL2ERR_INVAL is a deliberate, minor change to the
SETACL wire behavior.

Because the containing codecs are fully generated, the NFSv2 types
the ACL specification re-declares stay static in nfs_acl2xdr_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_acl2.x |   2 +
 fs/nfsd/nfs2acl.c                   | 246 ++++++++++++++++++++++++++++
 fs/nfsd/nfs_acl2xdr_gen.c           | 136 ++++++++++++---
 fs/nfsd/nfs_acl2xdr_gen.h           |  12 ++
 4 files changed, 372 insertions(+), 24 deletions(-)

diff --git a/Documentation/sunrpc/xdr/nfs_acl2.x b/Documentation/sunrpc/xdr/nfs_acl2.x
index 8ec8bffbe77b..25c0b54ece1a 100644
--- a/Documentation/sunrpc/xdr/nfs_acl2.x
+++ b/Documentation/sunrpc/xdr/nfs_acl2.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/nfs2acl.c b/fs/nfsd/nfs2acl.c
index 0698c9dc85dd..13e7415aa97b 100644
--- a/fs/nfsd/nfs2acl.c
+++ b/fs/nfsd/nfs2acl.c
@@ -31,6 +31,45 @@ struct nfsd_attrstat {
 	struct kstat		stat;
 };
 
+/*
+ * 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 GETACL2args_wrapper {
+	struct GETACL2args	xdrgen;
+	struct svc_fh		fh;
+};
+
+static_assert(offsetof(struct GETACL2args_wrapper, xdrgen) == 0);
+
+struct GETACL2res_wrapper {
+	struct GETACL2res	xdrgen;
+	int			mask;
+	struct posix_acl	*acl_access;
+	struct posix_acl	*acl_default;
+	struct kstat		stat;
+};
+
+static_assert(offsetof(struct GETACL2res_wrapper, xdrgen) == 0);
+
+struct SETACL2args_wrapper {
+	struct SETACL2args	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
+	 * nfsacld_proc_setacl can return ACL2ERR_INVAL rather than RPC
+	 * GARBAGE_ARGS. See draft-ietf-nfsv4-nfs-acl Section 3.4.4.2.
+	 */
+	bool			invalid;
+};
+
+static_assert(offsetof(struct SETACL2args_wrapper, xdrgen) == 0);
+
 struct nfsd3_getaclargs {
 	struct svc_fh		fh;
 	__u32			mask;
@@ -163,6 +202,19 @@ static __be32 nfsacld_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 nfsacld_acl_entries(const struct posix_acl *acl)
+{
+	return (acl && acl->a_count >= 3) ? max_t(int, acl->a_count, 4) : 0;
+}
+
 /**
  * nfsacld_proc_null - NULL: Do nothing
  * @rqstp: RPC transaction context
@@ -348,6 +400,200 @@ static __be32 nfsacld_proc_access(struct svc_rqst *rqstp)
 	return rpc_success;
 }
 
+/*
+ * 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_ACL2_SECATTR_ACLENT or NFS_ACL2_SECATTR_DFACLENT.
+ */
+
+/* GETACL reply: stream the wire entries out of the source posix_acl. */
+
+bool nfs_acl2_secattr_encode_begin(struct xdrgen_aggregate_cursor *c)
+{
+	struct svc_rqst *rqstp = c->ctx;
+	struct GETACL2res_wrapper *resp = rqstp->rq_resp;
+	bool dfacl = c->member_id == NFS_ACL2_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 ? nfsacld_acl_entries(acl) : 0;
+	return true;
+}
+
+bool nfs_acl2_secattr_encode(struct xdrgen_aggregate_cursor *c,
+			     struct aclent *out)
+{
+	struct svc_rqst *rqstp = c->ctx;
+	struct GETACL2res_wrapper *resp = rqstp->rq_resp;
+	bool dfacl = c->member_id == NFS_ACL2_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_acl2_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_acl2_secattr_decode_begin(struct xdrgen_aggregate_cursor *c)
+{
+	struct svc_rqst *rqstp = c->ctx;
+	struct SETACL2args_wrapper *argp = rqstp->rq_argp;
+	bool dfacl = c->member_id == NFS_ACL2_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's declared count must equal its length. */
+		if (c->count != declared)
+			return false;
+		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) {
+		return false;
+	}
+	return true;
+}
+
+bool nfs_acl2_secattr_decode(struct xdrgen_aggregate_cursor *c,
+			     const struct aclent *in)
+{
+	struct svc_rqst *rqstp = c->ctx;
+	struct SETACL2args_wrapper *argp = rqstp->rq_argp;
+	bool dfacl = c->member_id == NFS_ACL2_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;
+	}
+
+	entry = &acl->a_entries[c->index];
+	switch (in->type & ~NA_ACL_DEFAULT) {
+	case ACL_USER:
+		entry->e_uid = make_kuid(&init_user_ns, in->id);
+		if (!uid_valid(entry->e_uid))
+			return false;
+		break;
+	case ACL_GROUP:
+		entry->e_gid = make_kgid(&init_user_ns, in->id);
+		if (!gid_valid(entry->e_gid))
+			return false;
+		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;
+	}
+	entry->e_tag = in->type & ~NA_ACL_DEFAULT;
+	entry->e_perm = in->perm;
+
+	return true;
+}
+
+bool nfs_acl2_secattr_decode_end(struct xdrgen_aggregate_cursor *c, bool ok)
+{
+	struct svc_rqst *rqstp = c->ctx;
+	struct SETACL2args_wrapper *argp = rqstp->rq_argp;
+	bool dfacl = c->member_id == NFS_ACL2_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 decode functions
  */
diff --git a/fs/nfsd/nfs_acl2xdr_gen.c b/fs/nfsd/nfs_acl2xdr_gen.c
index 59765733bd49..f6e506970d73 100644
--- a/fs/nfsd/nfs_acl2xdr_gen.c
+++ b/fs/nfsd/nfs_acl2xdr_gen.c
@@ -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_ACL2_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_acl2_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_acl2_secattr_decode(&cursor, &element)) {
+				ok = false;
+				break;
+			}
+		}
+		if (!nfs_acl2_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_ACL2_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_acl2_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_acl2_secattr_decode(&cursor, &element)) {
+				ok = false;
+				break;
+			}
+		}
+		if (!nfs_acl2_secattr_decode_end(&cursor, ok) || !ok)
+			return false;
+	}
 	return true;
 }
 
@@ -445,22 +489,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_ACL2_SECATTR_ACLENT,
+			.ctx = xdr->xdrgen_ctx,
+		};
+		bool ok = true;
+
+		if (!nfs_acl2_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_acl2_secattr_encode(&cursor, &element)) {
+				ok = false;
+				break;
+			}
+			if (!xdrgen_encode_aclent(xdr, &element)) {
+				ok = false;
+				break;
+			}
+		}
+		if (!nfs_acl2_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_ACL2_SECATTR_DFACLENT,
+			.ctx = xdr->xdrgen_ctx,
+		};
+		bool ok = true;
+
+		if (!nfs_acl2_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_acl2_secattr_encode(&cursor, &element)) {
+				ok = false;
+				break;
+			}
+			if (!xdrgen_encode_aclent(xdr, &element)) {
+				ok = false;
+				break;
+			}
+		}
+		if (!nfs_acl2_secattr_encode_end(&cursor, ok) || !ok)
+			return false;
+	}
 	return true;
 }
 
diff --git a/fs/nfsd/nfs_acl2xdr_gen.h b/fs/nfsd/nfs_acl2xdr_gen.h
index a650fb7a3c83..b7fc4f23ffc5 100644
--- a/fs/nfsd/nfs_acl2xdr_gen.h
+++ b/fs/nfsd/nfs_acl2xdr_gen.h
@@ -13,6 +13,18 @@
 #include <linux/sunrpc/xdrgen/_builtins.h>
 #include <linux/sunrpc/xdrgen/nfs_acl2.h>
 
+enum {
+	NFS_ACL2_SECATTR_ACLENT,
+	NFS_ACL2_SECATTR_DFACLENT,
+};
+
+bool nfs_acl2_secattr_encode_begin(struct xdrgen_aggregate_cursor *c);
+bool nfs_acl2_secattr_encode(struct xdrgen_aggregate_cursor *c, struct aclent *out);
+bool nfs_acl2_secattr_encode_end(struct xdrgen_aggregate_cursor *c, bool ok);
+bool nfs_acl2_secattr_decode_begin(struct xdrgen_aggregate_cursor *c);
+bool nfs_acl2_secattr_decode(struct xdrgen_aggregate_cursor *c, const struct aclent *in);
+bool nfs_acl2_secattr_decode_end(struct xdrgen_aggregate_cursor *c, bool ok);
+
 bool nfs_acl_svc_decode_GETACL2args(struct svc_rqst *rqstp, struct xdr_stream *xdr);
 bool nfs_acl_svc_decode_SETACL2args(struct svc_rqst *rqstp, struct xdr_stream *xdr);
 bool nfs_acl_svc_decode_GETATTR2args(struct svc_rqst *rqstp, struct xdr_stream *xdr);
-- 
2.56.0


  parent reply	other threads:[~2026-10-22 13:53 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-22 13:53 [PATCH v1 00/17] Convert server-side NFS_ACLv2 XDR to use xdrgen Chuck Lever
2026-10-22 13:53 ` [PATCH v1 01/17] xdrgen: Reject out-of-range unsigned short values on decode Chuck Lever
2026-10-22 13:53 ` [PATCH v1 02/17] xdrgen: Reject out-of-range " Chuck Lever
2026-10-22 13:53 ` [PATCH v1 03/17] nfs_common: Export posix_acl_from_nfsacl for server ACL codecs Chuck Lever
2026-10-22 13:53 ` [PATCH v1 04/17] NFSD: Remove the unused NFSACL_MAXWORDS macro Chuck Lever
2026-10-22 13:53 ` [PATCH v1 05/17] NFSD: Move the definition of NFS_ACL_MAX_ENTRIES Chuck Lever
2026-10-22 13:53 ` [PATCH v1 06/17] Documentation: Add the RPC language description of NFS_ACLv2 Chuck Lever
2026-10-22 13:53 ` [PATCH v1 07/17] NFSD: Add infrastructure for generating NFS_ACLv2 XDR encoders and decoders Chuck Lever
2026-10-22 13:53 ` [PATCH v1 08/17] NFSD: Remove '#include "xdr3.h"' from nfs2acl.c Chuck Lever
2026-10-22 13:53 ` [PATCH v1 09/17] NFSD: Remove '#include "xdr.h"' " Chuck Lever
2026-10-22 13:53 ` [PATCH v1 10/17] NFSD: Switch to using the NFS_ACLv2 status codes Chuck Lever
2026-10-22 13:53 ` [PATCH v1 11/17] NFSD: Use xdrgen XDR functions for the NFS-ACL v2 NULL procedure Chuck Lever
2026-10-22 13:53 ` Chuck Lever [this message]
2026-10-22 13:53 ` [PATCH v1 13/17] NFSD: Use xdrgen XDR functions for the NFS-ACL v2 GETACL procedure Chuck Lever
2026-10-22 13:53 ` [PATCH v1 14/17] NFSD: Use xdrgen XDR functions for the NFS-ACL v2 SETACL procedure Chuck Lever
2026-10-22 13:53 ` [PATCH v1 15/17] NFSD: Use xdrgen XDR functions for the NFS-ACL v2 GETATTR procedure Chuck Lever
2026-10-22 13:53 ` [PATCH v1 16/17] NFSD: Use xdrgen XDR functions for the NFS-ACL v2 ACCESS procedure Chuck Lever
2026-10-22 13:53 ` [PATCH v1 17/17] NFSD: Map every NFS_ACLv2 reply status into aclstat2 Chuck Lever

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=20261022135341.14265-13-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