From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.remote-ip=100.103.45.18 ARC-Seal: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=none; t=1793050584; b=MBpLMtmEo6QobNIpBf0aTlyKjvPMnPumfJrtHUIQCqmDkCDunoz3W2/0c2yWHJRe4xBs lfLesbvj3IB5BxsVw8GntOYA79HKvsL4bLtFQJqqneKNTUFy8SLImSs/tOJmnUUZS6If3 ABGzoAV/UCQf9Fk6zPJysmFRwxbjXamNudCgGsM4AtYZSSjIxL/fqIJoaoy/45ier23lI KC/Cq1v7lJOILW99QmPzgpns2+GdUZBDQC/Fac4YBQIqVL/svRWosufWzjtMg4WjsYwtU 4Kuw1/kQEVSjtlo9dYsisz1GsCVSNZ2Ml7H8xjEbzgToZtEMYGK3CsFWLST7eyNdgxw== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1793050584; h=Received:DKIM-Signature:From:To:Cc:Subject:Date:Message-ID:X-Mailer: In-Reply-To:References:MIME-Version:Content-Transfer-Encoding; bh=bygsCI9KKaUTnIdSB4/0s5423Ws1S5p/QO3PCKXOHXo=; b=XFsjwSOK76T3duA6qOtAP3B+PUMjT802BmGr8Yiic+PoX1CC2d/XX+EnBmZhqKuzZicw V907LvBseLRmIXoeChiFLflu2FinwdUERrcifxrJmKK1+dhhex6fy07DQVXCaHqS4Iox0 LPBnPnsWPYMZZ41++lZ3QmF8V5VNfA2iHheL6gVqXS0xAmBMgbmDZZfEPNORo+Rg8ftNk 1KHWq6INOtOHPkdMQl+1zHz23tFlPNEPEk/Pdar9f9hHELG3giH4lCVJ86LJfWq7NqB3X WZD+5IQQgfs8xoAMcD6pQk79MLdpZQa1TTxzCsCZo4PecTGnYfxUkuuGEMiA6LDC6Hw== ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass header.d=kernel.org header.i=@kernel.org header.a=rsa-sha256 header.s=k20260515 header.b=Fo0DSol3; arc=none smtp.remote-ip=100.103.45.18 Received: from smtp.kernel.org (quasi.space.kernel.org [100.103.45.18]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (prime256v1)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B4353606A320 for ; Mon, 26 Oct 2026 21:36:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key, unprotected) header.d=kernel.org header.i=@kernel.org header.a=rsa-sha256 header.s=k20260515 header.b=Fo0DSol3 Received: by smtp.kernel.org (Postfix) with ESMTPSA id 8E6461F031E1; Mon, 26 Oct 2026 21:36:22 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1793050583; bh=bygsCI9KKaUTnIdSB4/0s5423Ws1S5p/QO3PCKXOHXo=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=Fo0DSol35TA64InB37WzUsDAZervHN3NBaD+4NQXsEQwNkpsQzNk7BGlx1OV1fv5Y ApbyGRR0aRZYDK/kTC1XVxGqC2mPeYrJhWpclSn9evvvFRQTsEkF23Qwb6yWLWmmFX ZX+BOplFKPm6PKMbI90YDMIqIE9Ab/cL0ZhdoLfZieoGj0VTsHSLP3iKRzGuAVx9Lc qw2kgTnVCRUtwYv4KrCulNbTFs8bbn8lpErFL9fHTweSxZNtgP7zIv2JPSSRsqL2nh vj0Amc7/cpvwD5CfBCpvniU6V+9z9mwyozdM7gHlZmdcPpWsNcYc4YSX2SeHv6+/LO Fb2jk/4Edu5Jg== From: Chuck Lever To: NeilBrown , Jeff Layton , Olga Kornievskaia , Dai Ngo , Tom Talpey Cc: 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 Message-ID: <20261025151622.141025-7-cel@kernel.org> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261025151622.141025-1-cel@kernel.org> References: <20261025151622.141025-1-cel@kernel.org> X-Mailing-List: linux-nfs@vger.kernel.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Transfer-Encoding: 8bit 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 --- 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; }; +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 @@ -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 #include +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