* [PATCH v3 10/34] NFSD: Use xdrgen XDR functions for the NFSv3 LOOKUP procedure
@ 2026-10-13 12:49 Chuck Lever
2026-10-13 12:49 ` [PATCH v3 11/34] NFSD: Use xdrgen XDR functions for NFSv3 ACCESS procedure Chuck Lever
` (8 more replies)
0 siblings, 9 replies; 10+ messages in thread
From: Chuck Lever @ 2026-10-13 12:49 UTC (permalink / raw)
To: NeilBrown, Jeff Layton, Olga Kornievskaia, Dai Ngo, Tom Talpey; +Cc: linux-nfs
Replace the NFSPROC3_LOOKUP entry in the nfsd_procedures3 array
with an entry that dispatches the xdrgen-generated
nfs_svc_decode_LOOKUP3args and nfs_svc_encode_LOOKUP3res. Wrapper
structures bridge the generated xdrgen types and the legacy svc_fh
representation the NFSD VFS layer still uses.
The pc_argzero field is now set to zero for the NFSv3 LOOKUP
procedure. The xdrgen decoders are trusted to initialize all
arguments in the argp->xdrgen field, making the early defensive
memset unnecessary. The remaining argp fields are cleared as
needed.
nfs3svc_decode_diropargs() rejects an empty name, a name longer
than NFS3_MAXNAMLEN, and a name containing '/' or NUL. The
generated filename3 decoder checks none of these. Introduce
nfsd3_check_filename() so that nfsd3_proc_lookup() applies the same
checks before it calls nfsd_lookup(). After the change, a name that
fails them draws NFS3ERR_INVAL or NFS3ERR_NAMETOOLONG rather than
GARBAGE_ARGS. Later procedures that take a filename share the helper.
Because nfsd3_proc_lookup() now fills in the post-op attributes,
the fh_getattr() calls are made in the proc function rather than in
the XDR result encoder, and the references to the file handles can
be released directly by nfsd3_proc_lookup(). A separate ->pc_release
callback is thus no longer needed. This makes it straightforward to
confirm that the file handles are properly released during every
error flow.
Following the precedent of commit 40ee5dc6af35 ("knfsd: 64 bit ino
support for NFS server"), lease_get_mtime() determines the file's
mtime when filling in post-op attributes, but is not used for WCC
data. This seems odd to me; NFSv4 does not use lease_get_mtime() at
all, and not using it for WCC data seems like an NFSv3 file's
reported mtime values might vary depending on how the file metadata
is queried.
nfs3svc_encode_lookupres() no longer has any callers, so it is
removed.
Signed-off-by: Chuck Lever <cel@kernel.org>
---
fs/nfsd/nfs3proc.c | 116 +++++++++++++++++++++++++++++++++++----------
fs/nfsd/nfs3xdr.c | 25 ----------
fs/nfsd/xdr3.h | 1 -
3 files changed, 90 insertions(+), 52 deletions(-)
diff --git a/fs/nfsd/nfs3proc.c b/fs/nfsd/nfs3proc.c
index c86b225ff245..0eedfcf2454f 100644
--- a/fs/nfsd/nfs3proc.c
+++ b/fs/nfsd/nfs3proc.c
@@ -56,6 +56,21 @@ struct SETATTR3args_wrapper {
static_assert(offsetof(struct SETATTR3args_wrapper, xdrgen) == 0);
+struct LOOKUP3args_wrapper {
+ struct LOOKUP3args xdrgen;
+ struct svc_fh fh;
+};
+
+static_assert(offsetof(struct LOOKUP3args_wrapper, xdrgen) == 0);
+
+struct LOOKUP3res_wrapper {
+ struct LOOKUP3res xdrgen;
+ struct svc_fh fh;
+ u8 fh_data[NFS3_FHSIZE];
+};
+
+static_assert(offsetof(struct LOOKUP3res_wrapper, xdrgen) == 0);
+
static int nfs3_ftypes[] = {
0, /* NF3NON */
S_IFREG, /* NF3REG */
@@ -170,6 +185,14 @@ nfsd3_fh3_to_svc_fh(struct svc_fh *fhp, const struct nfs_fh3 *fh3)
memcpy(&fhp->fh_handle.fh_raw, fh3->data.data, fh3->data.len);
}
+static __always_inline void
+nfsd3_svc_fh_to_fh3(struct nfs_fh3 *fh3, const struct svc_fh *fhp, u8 *scratch)
+{
+ memcpy(scratch, fhp->fh_handle.fh_raw, fhp->fh_handle.fh_size);
+ fh3->data.data = scratch;
+ fh3->data.len = fhp->fh_handle.fh_size;
+}
+
static __always_inline void
nfsd3_timespec64_to_nfstime3(struct nfstime3 *dst,
const struct timespec64 *src)
@@ -178,6 +201,22 @@ nfsd3_timespec64_to_nfstime3(struct nfstime3 *dst,
dst->nseconds = src->tv_nsec;
}
+static __be32
+nfsd3_check_filename(const unsigned char *name, u32 len)
+{
+ u32 i;
+
+ if (len == 0)
+ return nfserr_inval;
+ if (len > NFS3_MAXNAMLEN)
+ return nfserr_nametoolong;
+ for (i = 0; i < len; i++) {
+ if (name[i] == '\0' || name[i] == '/')
+ return nfserr_inval;
+ }
+ return nfs_ok;
+}
+
static u32
nfsd3_mode_to_ftype3(umode_t mode)
{
@@ -456,27 +495,51 @@ static __be32 nfsd3_proc_setattr(struct svc_rqst *rqstp)
return rpc_success;
}
-/*
- * Look up a path name component
+/**
+ * nfsd3_proc_lookup - LOOKUP: Look up file name
+ * @rqstp: RPC transaction context
+ *
+ * Return:
+ * %rpc_success: RPC executed successfully
+ *
+ * RPC synopsis:
+ * LOOKUP3res NFSPROC3_LOOKUP(LOOKUP3args) = 3;
*/
-static __be32
-nfsd3_proc_lookup(struct svc_rqst *rqstp)
+static __be32 nfsd3_proc_lookup(struct svc_rqst *rqstp)
{
- struct nfsd3_diropargs *argp = rqstp->rq_argp;
- struct nfsd3_diropres *resp = rqstp->rq_resp;
+ struct LOOKUP3args_wrapper *argp = rqstp->rq_argp;
+ struct LOOKUP3res_wrapper *resp = rqstp->rq_resp;
+ struct diropargs3 *what = &argp->xdrgen.what;
+ struct svc_fh *dirfhp = &argp->fh;
+ struct svc_fh *fhp = &resp->fh;
- dprintk("nfsd: LOOKUP(3) %s %.*s\n",
- SVCFH_fmt(&argp->fh),
- argp->len,
- argp->name);
+ nfsd3_fh3_to_svc_fh(dirfhp, &what->dir);
- fh_copy(&resp->dirfh, &argp->fh);
- fh_init(&resp->fh, NFS3_FHSIZE);
+ fh_init(fhp, NFS3_FHSIZE);
+ resp->xdrgen.status = nfsd3_check_filename(what->name.data,
+ what->name.len);
+ if (resp->xdrgen.status != nfs_ok)
+ goto out;
+ resp->xdrgen.status = nfsd_lookup(rqstp, dirfhp,
+ (char *)what->name.data,
+ what->name.len, fhp);
- resp->status = nfsd_lookup(rqstp, &resp->dirfh,
- argp->name, argp->len,
- &resp->fh);
- resp->status = nfsd3_map_status(resp->status);
+out:
+ if (resp->xdrgen.status == nfs_ok) {
+ struct LOOKUP3resok *resok = &resp->xdrgen.u.resok;
+
+ nfsd3_svc_fh_to_fh3(&resok->object, fhp, resp->fh_data);
+ nfsd3_fill_post_op_attr(rqstp, &resok->obj_attributes, fhp);
+ nfsd3_fill_post_op_attr(rqstp, &resok->dir_attributes, dirfhp);
+ } else {
+ struct LOOKUP3resfail *resfail = &resp->xdrgen.u.resfail;
+
+ resp->xdrgen.status = nfsd3_map_status(resp->xdrgen.status);
+ nfsd3_fill_post_op_attr(rqstp, &resfail->dir_attributes, dirfhp);
+ }
+
+ fh_put(fhp);
+ fh_put(dirfhp);
return rpc_success;
}
@@ -1222,16 +1285,15 @@ static const struct svc_procedure nfsd_procedures3[22] = {
.pc_name = "SETATTR",
},
[NFSPROC3_LOOKUP] = {
- .pc_func = nfsd3_proc_lookup,
- .pc_decode = nfs3svc_decode_diropargs,
- .pc_encode = nfs3svc_encode_lookupres,
- .pc_release = nfs3svc_release_fhandle2,
- .pc_argsize = sizeof(struct nfsd3_diropargs),
- .pc_argzero = sizeof(struct nfsd3_diropargs),
- .pc_ressize = sizeof(struct nfsd3_diropres),
- .pc_cachetype = RC_NOCACHE,
- .pc_xdrressize = ST+FH+pAT+pAT,
- .pc_name = "LOOKUP",
+ .pc_func = nfsd3_proc_lookup,
+ .pc_decode = nfs_svc_decode_LOOKUP3args,
+ .pc_encode = nfs_svc_encode_LOOKUP3res,
+ .pc_argsize = sizeof(struct LOOKUP3args_wrapper),
+ .pc_argzero = 0,
+ .pc_ressize = sizeof(struct LOOKUP3res_wrapper),
+ .pc_cachetype = RC_NOCACHE,
+ .pc_xdrressize = NFS3_LOOKUP3res_sz,
+ .pc_name = "LOOKUP",
},
[NFSPROC3_ACCESS] = {
.pc_func = nfsd3_proc_access,
@@ -1454,6 +1516,8 @@ union nfsd3_xdrstore {
struct GETATTR3res_wrapper getattrres;
struct SETATTR3args_wrapper setattrargs;
struct SETATTR3res setattrres;
+ struct LOOKUP3args_wrapper lookupargs;
+ struct LOOKUP3res_wrapper lookupres;
struct nfsd3_diropargs diropargs;
struct nfsd3_readargs readargs;
struct nfsd3_writeargs writeargs;
diff --git a/fs/nfsd/nfs3xdr.c b/fs/nfsd/nfs3xdr.c
index eb5f3148a0ab..36c22611370d 100644
--- a/fs/nfsd/nfs3xdr.c
+++ b/fs/nfsd/nfs3xdr.c
@@ -721,31 +721,6 @@ nfs3svc_encode_wccstat(struct svc_rqst *rqstp, struct xdr_stream *xdr)
svcxdr_encode_wcc_data(rqstp, xdr, &resp->fh);
}
-/* LOOKUP */
-bool
-nfs3svc_encode_lookupres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
-{
- struct nfsd3_diropres *resp = rqstp->rq_resp;
-
- if (!svcxdr_encode_nfsstat3(xdr, resp->status))
- return false;
- switch (resp->status) {
- case nfs_ok:
- if (!svcxdr_encode_nfs_fh3(xdr, &resp->fh))
- return false;
- if (!svcxdr_encode_post_op_attr(rqstp, xdr, &resp->fh))
- return false;
- if (!svcxdr_encode_post_op_attr(rqstp, xdr, &resp->dirfh))
- return false;
- break;
- default:
- if (!svcxdr_encode_post_op_attr(rqstp, xdr, &resp->dirfh))
- return false;
- }
-
- return true;
-}
-
/* ACCESS */
bool
nfs3svc_encode_accessres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
diff --git a/fs/nfsd/xdr3.h b/fs/nfsd/xdr3.h
index 354cff178b75..d17192f3c983 100644
--- a/fs/nfsd/xdr3.h
+++ b/fs/nfsd/xdr3.h
@@ -252,7 +252,6 @@ bool nfs3svc_decode_readdirplusargs(struct svc_rqst *rqstp, struct xdr_stream *x
bool nfs3svc_decode_commitargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs3svc_encode_wccstat(struct svc_rqst *rqstp, struct xdr_stream *xdr);
-bool nfs3svc_encode_lookupres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs3svc_encode_accessres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs3svc_encode_readlinkres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs3svc_encode_readres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
--
2.55.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v3 11/34] NFSD: Use xdrgen XDR functions for NFSv3 ACCESS procedure
2026-10-13 12:49 [PATCH v3 10/34] NFSD: Use xdrgen XDR functions for the NFSv3 LOOKUP procedure Chuck Lever
@ 2026-10-13 12:49 ` Chuck Lever
2026-10-13 12:49 ` [PATCH v3 12/34] NFSD: Use xdrgen XDR functions for NFSv3 READLINK procedure Chuck Lever
` (7 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Chuck Lever @ 2026-10-13 12:49 UTC (permalink / raw)
To: NeilBrown, Jeff Layton, Olga Kornievskaia, Dai Ngo, Tom Talpey; +Cc: linux-nfs
Replace the NFSPROC3_ACCESS entry in the nfsd_procedures3 array
with an entry that dispatches the xdrgen-generated
nfs_svc_decode_ACCESS3args and nfs_svc_encode_ACCESS3res. Wrapper
structures bridge the generated xdrgen types and the legacy svc_fh
representation the NFSD VFS layer still uses.
The pc_argzero field is now set to zero for the NFSv3 ACCESS
procedure. The xdrgen decoders are trusted to initialize all
arguments in the argp->xdrgen field, making the early defensive
memset unnecessary. The remaining argp fields are cleared as
needed.
Because nfsd3_proc_access() now fills in the post-op attributes,
the fh_getattr() calls are made in the proc function rather than in
the XDR result encoder, and the reference to the file handle can be
released directly by nfsd3_proc_access(). A separate ->pc_release
callback is thus no longer needed. This makes it straightforward to
confirm that the file handle is properly released during every
error flow.
nfs3svc_decode_accessargs() and nfs3svc_encode_accessres() no
longer have any callers, so they are removed.
Signed-off-by: Chuck Lever <cel@kernel.org>
---
fs/nfsd/nfs3proc.c | 73 ++++++++++++++++++++++++++++++----------------
fs/nfsd/nfs3xdr.c | 36 -----------------------
fs/nfsd/xdr3.h | 1 -
3 files changed, 48 insertions(+), 62 deletions(-)
diff --git a/fs/nfsd/nfs3proc.c b/fs/nfsd/nfs3proc.c
index 0eedfcf2454f..8f02dcd6404b 100644
--- a/fs/nfsd/nfs3proc.c
+++ b/fs/nfsd/nfs3proc.c
@@ -71,6 +71,13 @@ struct LOOKUP3res_wrapper {
static_assert(offsetof(struct LOOKUP3res_wrapper, xdrgen) == 0);
+struct ACCESS3args_wrapper {
+ struct ACCESS3args xdrgen;
+ struct svc_fh fh;
+};
+
+static_assert(offsetof(struct ACCESS3args_wrapper, xdrgen) == 0);
+
static int nfs3_ftypes[] = {
0, /* NF3NON */
S_IFREG, /* NF3REG */
@@ -543,24 +550,40 @@ static __be32 nfsd3_proc_lookup(struct svc_rqst *rqstp)
return rpc_success;
}
-/*
- * Check file access
+/**
+ * nfsd3_proc_access - ACCESS: Check access permission
+ * @rqstp: RPC transaction context
+ *
+ * Return:
+ * %rpc_success: RPC executed successfully
+ *
+ * RPC synopsis:
+ * ACCESS3res NFSPROC3_ACCESS(ACCESS3args) = 4;
*/
-static __be32
-nfsd3_proc_access(struct svc_rqst *rqstp)
+static __be32 nfsd3_proc_access(struct svc_rqst *rqstp)
{
- struct nfsd3_accessargs *argp = rqstp->rq_argp;
- struct nfsd3_accessres *resp = rqstp->rq_resp;
+ struct ACCESS3args_wrapper *argp = rqstp->rq_argp;
+ struct ACCESS3res *resp = rqstp->rq_resp;
+ struct svc_fh *fhp = &argp->fh;
- dprintk("nfsd: ACCESS(3) %s 0x%x\n",
- SVCFH_fmt(&argp->fh),
- argp->access);
+ nfsd3_fh3_to_svc_fh(fhp, &argp->xdrgen.object);
- fh_copy(&resp->fh, &argp->fh);
- resp->access = argp->access;
- resp->status = nfsd_access(rqstp, &resp->fh, &nfsd3_access_maps,
- &resp->access, NULL);
- resp->status = nfsd3_map_status(resp->status);
+ resp->status = nfsd_access(rqstp, fhp, &nfsd3_access_maps,
+ &argp->xdrgen.access, NULL);
+
+ if (resp->status == nfs_ok) {
+ struct ACCESS3resok *resok = &resp->u.resok;
+
+ resok->access = argp->xdrgen.access;
+ nfsd3_fill_post_op_attr(rqstp, &resok->obj_attributes, fhp);
+ } else {
+ struct ACCESS3resfail *resfail = &resp->u.resfail;
+
+ resp->status = nfsd3_map_status(resp->status);
+ nfsd3_fill_post_op_attr(rqstp, &resfail->obj_attributes, fhp);
+ }
+
+ fh_put(fhp);
return rpc_success;
}
@@ -1296,16 +1319,15 @@ static const struct svc_procedure nfsd_procedures3[22] = {
.pc_name = "LOOKUP",
},
[NFSPROC3_ACCESS] = {
- .pc_func = nfsd3_proc_access,
- .pc_decode = nfs3svc_decode_accessargs,
- .pc_encode = nfs3svc_encode_accessres,
- .pc_release = nfs3svc_release_fhandle,
- .pc_argsize = sizeof(struct nfsd3_accessargs),
- .pc_argzero = sizeof(struct nfsd3_accessargs),
- .pc_ressize = sizeof(struct nfsd3_accessres),
- .pc_cachetype = RC_NOCACHE,
- .pc_xdrressize = ST+pAT+1,
- .pc_name = "ACCESS",
+ .pc_func = nfsd3_proc_access,
+ .pc_decode = nfs_svc_decode_ACCESS3args,
+ .pc_encode = nfs_svc_encode_ACCESS3res,
+ .pc_argsize = sizeof(struct ACCESS3args_wrapper),
+ .pc_argzero = 0,
+ .pc_ressize = sizeof(struct ACCESS3res),
+ .pc_cachetype = RC_NOCACHE,
+ .pc_xdrressize = NFS3_ACCESS3res_sz,
+ .pc_name = "ACCESS",
},
[NFSPROC3_READLINK] = {
.pc_func = nfsd3_proc_readlink,
@@ -1518,6 +1540,8 @@ union nfsd3_xdrstore {
struct SETATTR3res setattrres;
struct LOOKUP3args_wrapper lookupargs;
struct LOOKUP3res_wrapper lookupres;
+ struct ACCESS3args_wrapper accessargs;
+ struct ACCESS3res accessres;
struct nfsd3_diropargs diropargs;
struct nfsd3_readargs readargs;
struct nfsd3_writeargs writeargs;
@@ -1527,7 +1551,6 @@ union nfsd3_xdrstore {
struct nfsd3_symlinkargs symlinkargs;
struct nfsd3_readdirargs readdirargs;
struct nfsd3_diropres diropres;
- struct nfsd3_accessres accessres;
struct nfsd3_readlinkres readlinkres;
struct nfsd3_readres readres;
struct nfsd3_writeres writeres;
diff --git a/fs/nfsd/nfs3xdr.c b/fs/nfsd/nfs3xdr.c
index 36c22611370d..f3f723215114 100644
--- a/fs/nfsd/nfs3xdr.c
+++ b/fs/nfsd/nfs3xdr.c
@@ -490,19 +490,6 @@ nfs3svc_decode_diropargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
return svcxdr_decode_diropargs3(xdr, &args->fh, &args->name, &args->len);
}
-bool
-nfs3svc_decode_accessargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
-{
- struct nfsd3_accessargs *args = rqstp->rq_argp;
-
- if (!svcxdr_decode_nfs_fh3(xdr, &args->fh))
- return false;
- if (xdr_stream_decode_u32(xdr, &args->access) < 0)
- return false;
-
- return true;
-}
-
bool
nfs3svc_decode_readargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
@@ -721,29 +708,6 @@ nfs3svc_encode_wccstat(struct svc_rqst *rqstp, struct xdr_stream *xdr)
svcxdr_encode_wcc_data(rqstp, xdr, &resp->fh);
}
-/* ACCESS */
-bool
-nfs3svc_encode_accessres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
-{
- struct nfsd3_accessres *resp = rqstp->rq_resp;
-
- if (!svcxdr_encode_nfsstat3(xdr, resp->status))
- return false;
- switch (resp->status) {
- case nfs_ok:
- if (!svcxdr_encode_post_op_attr(rqstp, xdr, &resp->fh))
- return false;
- if (xdr_stream_encode_u32(xdr, resp->access) < 0)
- return false;
- break;
- default:
- if (!svcxdr_encode_post_op_attr(rqstp, xdr, &resp->fh))
- return false;
- }
-
- return true;
-}
-
/* READLINK */
bool
nfs3svc_encode_readlinkres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
diff --git a/fs/nfsd/xdr3.h b/fs/nfsd/xdr3.h
index d17192f3c983..bb23c444c23d 100644
--- a/fs/nfsd/xdr3.h
+++ b/fs/nfsd/xdr3.h
@@ -238,7 +238,6 @@ struct nfsd3_fhandle_pair {
bool nfs3svc_decode_fhandleargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs3svc_decode_diropargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
-bool nfs3svc_decode_accessargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs3svc_decode_readargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs3svc_decode_writeargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs3svc_decode_createargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
--
2.55.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v3 12/34] NFSD: Use xdrgen XDR functions for NFSv3 READLINK procedure
2026-10-13 12:49 [PATCH v3 10/34] NFSD: Use xdrgen XDR functions for the NFSv3 LOOKUP procedure Chuck Lever
2026-10-13 12:49 ` [PATCH v3 11/34] NFSD: Use xdrgen XDR functions for NFSv3 ACCESS procedure Chuck Lever
@ 2026-10-13 12:49 ` Chuck Lever
2026-10-13 12:49 ` [PATCH v3 13/34] NFSD: Use xdrgen XDR functions for NFSv3 READ procedure Chuck Lever
` (6 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Chuck Lever @ 2026-10-13 12:49 UTC (permalink / raw)
To: NeilBrown, Jeff Layton, Olga Kornievskaia, Dai Ngo, Tom Talpey; +Cc: linux-nfs
Replace the hand-written NFSv3 READLINK codec pair with the
xdrgen-generated functions. The NFSPROC3_READLINK entry in the
nfsd_procedures3 array now dispatches nfs_svc_decode_READLINK3args
and nfs_svc_encode_READLINK3res. A wrapper structure bridges the
generated xdrgen fhandle type and the legacy svc_fh representation
the NFSD VFS layer still uses.
The link target does not reside at the data pointer of the
READLINK3resok data member: nfsd_readlink() deposits it directly
in a page of the Reply buffer, so the generic encoder that copies
an opaque from the member's data pointer cannot encode it. Mark
the data member with the "pragma pages" directive so the generated
encoder calls svcxdr_encode_opaque_payload(), which encodes the
length prefix and inserts the payload page into the stream by
reference. The page anchor that the result structure carried
duplicated rq_res.pages -- svc_process() points rq_res.pages at
the first Reply page before dispatch, and the READLINK3args
argument decoder consumes no pages -- so the helper locates the
payload through the Reply buffer itself, and the wrapper collapses
to the generated READLINK3res type.
The pc_argzero field is now set to zero for the NFSv3 READLINK
procedure. The xdrgen decoders are trusted to initialize all
arguments in the argp->xdrgen field, making the early defensive
memset unnecessary. The remaining argp fields are cleared as
needed.
Because nfsd3_proc_readlink() now fills in the post-op attributes,
the fh_getattr() calls are made in the proc function rather than
in the XDR result encoder, and the reference to the file handle
can be released directly by nfsd3_proc_readlink(). A separate
->pc_release callback is thus no longer needed. This makes it
straightforward to confirm that the file handle is properly
released during every error flow.
Signed-off-by: Chuck Lever <cel@kernel.org>
---
Documentation/sunrpc/xdr/nfs3.x | 1 +
fs/nfsd/nfs3proc.c | 79 ++++++++++++++++++++----------
fs/nfsd/nfs3xdr.c | 28 -----------
fs/nfsd/nfs3xdr_gen.c | 4 +-
fs/nfsd/nfs3xdr_gen.h | 2 +-
fs/nfsd/xdr3.h | 9 +---
include/linux/sunrpc/xdrgen/nfs3.h | 2 +-
7 files changed, 60 insertions(+), 65 deletions(-)
diff --git a/Documentation/sunrpc/xdr/nfs3.x b/Documentation/sunrpc/xdr/nfs3.x
index 1ef99ec54103..b50ffb77af54 100644
--- a/Documentation/sunrpc/xdr/nfs3.x
+++ b/Documentation/sunrpc/xdr/nfs3.x
@@ -331,6 +331,7 @@ struct READLINK3resok {
post_op_attr symlink_attributes;
nfspath3 data;
};
+pragma pages READLINK3resok data;
struct READLINK3resfail {
post_op_attr symlink_attributes;
diff --git a/fs/nfsd/nfs3proc.c b/fs/nfsd/nfs3proc.c
index 8f02dcd6404b..3c557124196f 100644
--- a/fs/nfsd/nfs3proc.c
+++ b/fs/nfsd/nfs3proc.c
@@ -78,6 +78,13 @@ struct ACCESS3args_wrapper {
static_assert(offsetof(struct ACCESS3args_wrapper, xdrgen) == 0);
+struct READLINK3args_wrapper {
+ struct READLINK3args xdrgen;
+ struct svc_fh fh;
+};
+
+static_assert(offsetof(struct READLINK3args_wrapper, xdrgen) == 0);
+
static int nfs3_ftypes[] = {
0, /* NF3NON */
S_IFREG, /* NF3REG */
@@ -587,24 +594,44 @@ static __be32 nfsd3_proc_access(struct svc_rqst *rqstp)
return rpc_success;
}
-/*
- * Read a symlink.
+/**
+ * nfsd3_proc_readlink - READLINK: Read from symbolic link
+ * @rqstp: RPC transaction context
+ *
+ * Return:
+ * %rpc_success: RPC executed successfully
+ *
+ * RPC synopsis:
+ * READLINK3res NFSPROC3_READLINK(READLINK3args) = 5;
*/
-static __be32
-nfsd3_proc_readlink(struct svc_rqst *rqstp)
+static __be32 nfsd3_proc_readlink(struct svc_rqst *rqstp)
{
- struct nfsd_fhandle *argp = rqstp->rq_argp;
- struct nfsd3_readlinkres *resp = rqstp->rq_resp;
+ struct READLINK3args_wrapper *argp = rqstp->rq_argp;
+ struct READLINK3res *resp = rqstp->rq_resp;
+ struct svc_fh *fhp = &argp->fh;
+ struct page *page;
+ u32 len;
- dprintk("nfsd: READLINK(3) %s\n", SVCFH_fmt(&argp->fh));
+ nfsd3_fh3_to_svc_fh(fhp, &argp->xdrgen.symlink);
- /* Read the symlink. */
- fh_copy(&resp->fh, &argp->fh);
- resp->len = NFS3_MAXPATHLEN;
- resp->pages = rqstp->rq_next_page++;
- resp->status = nfsd_readlink(rqstp, &resp->fh,
- page_address(*resp->pages), &resp->len);
- resp->status = nfsd3_map_status(resp->status);
+ len = NFS3_MAXPATHLEN;
+ page = *(rqstp->rq_next_page++);
+ resp->status = nfsd_readlink(rqstp, fhp, page_address(page), &len);
+
+ if (resp->status == nfs_ok) {
+ struct READLINK3resok *resok = &resp->u.resok;
+
+ resok->data.len = len;
+ nfsd3_fill_post_op_attr(rqstp, &resok->symlink_attributes, fhp);
+ } else {
+ struct READLINK3resfail *resfail = &resp->u.resfail;
+
+ resp->status = nfsd3_map_status(resp->status);
+ nfsd3_fill_post_op_attr(rqstp, &resfail->symlink_attributes,
+ fhp);
+ }
+
+ fh_put(fhp);
return rpc_success;
}
@@ -1330,16 +1357,17 @@ static const struct svc_procedure nfsd_procedures3[22] = {
.pc_name = "ACCESS",
},
[NFSPROC3_READLINK] = {
- .pc_func = nfsd3_proc_readlink,
- .pc_decode = nfs3svc_decode_fhandleargs,
- .pc_encode = nfs3svc_encode_readlinkres,
- .pc_release = nfs3svc_release_fhandle,
- .pc_argsize = sizeof(struct nfsd_fhandle),
- .pc_argzero = sizeof(struct nfsd_fhandle),
- .pc_ressize = sizeof(struct nfsd3_readlinkres),
- .pc_cachetype = RC_NOCACHE,
- .pc_xdrressize = ST+pAT+1+NFS3_MAXPATHLEN/4,
- .pc_name = "READLINK",
+ .pc_func = nfsd3_proc_readlink,
+ .pc_decode = nfs_svc_decode_READLINK3args,
+ .pc_encode = nfs_svc_encode_READLINK3res,
+ .pc_argsize = sizeof(struct READLINK3args_wrapper),
+ .pc_argzero = 0,
+ .pc_ressize = sizeof(struct READLINK3res),
+ .pc_cachetype = RC_NOCACHE,
+ /* _sz omits the payload, which nfs3.x leaves unbounded */
+ .pc_xdrressize = NFS3_READLINK3res_sz +
+ XDR_QUADLEN(NFS3_MAXPATHLEN),
+ .pc_name = "READLINK",
},
[NFSPROC3_READ] = {
.pc_func = nfsd3_proc_read,
@@ -1542,6 +1570,7 @@ union nfsd3_xdrstore {
struct LOOKUP3res_wrapper lookupres;
struct ACCESS3args_wrapper accessargs;
struct ACCESS3res accessres;
+ struct READLINK3args_wrapper readlinkargs;
struct nfsd3_diropargs diropargs;
struct nfsd3_readargs readargs;
struct nfsd3_writeargs writeargs;
@@ -1551,7 +1580,7 @@ union nfsd3_xdrstore {
struct nfsd3_symlinkargs symlinkargs;
struct nfsd3_readdirargs readdirargs;
struct nfsd3_diropres diropres;
- struct nfsd3_readlinkres readlinkres;
+ struct READLINK3res readlinkres;
struct nfsd3_readres readres;
struct nfsd3_writeres writeres;
struct nfsd3_renameres renameres;
diff --git a/fs/nfsd/nfs3xdr.c b/fs/nfsd/nfs3xdr.c
index f3f723215114..a77b5f7a7e27 100644
--- a/fs/nfsd/nfs3xdr.c
+++ b/fs/nfsd/nfs3xdr.c
@@ -708,34 +708,6 @@ nfs3svc_encode_wccstat(struct svc_rqst *rqstp, struct xdr_stream *xdr)
svcxdr_encode_wcc_data(rqstp, xdr, &resp->fh);
}
-/* READLINK */
-bool
-nfs3svc_encode_readlinkres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
-{
- struct nfsd3_readlinkres *resp = rqstp->rq_resp;
- struct kvec *head = rqstp->rq_res.head;
-
- if (!svcxdr_encode_nfsstat3(xdr, resp->status))
- return false;
- switch (resp->status) {
- case nfs_ok:
- if (!svcxdr_encode_post_op_attr(rqstp, xdr, &resp->fh))
- return false;
- if (xdr_stream_encode_u32(xdr, resp->len) < 0)
- return false;
- svcxdr_encode_opaque_pages(rqstp, xdr, resp->pages, 0,
- resp->len);
- if (svc_encode_result_payload(rqstp, head->iov_len, resp->len) < 0)
- return false;
- break;
- default:
- if (!svcxdr_encode_post_op_attr(rqstp, xdr, &resp->fh))
- return false;
- }
-
- return true;
-}
-
/* READ */
bool
nfs3svc_encode_readres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
diff --git a/fs/nfsd/nfs3xdr_gen.c b/fs/nfsd/nfs3xdr_gen.c
index e21ee0053129..bc6fb9829397 100644
--- a/fs/nfsd/nfs3xdr_gen.c
+++ b/fs/nfsd/nfs3xdr_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/nfs3.x
-// XDR specification modification time: Fri Jul 10 23:21:45 2026
+// XDR specification modification time: Tue Jul 14 09:53:56 2026
#include <linux/sunrpc/svc.h>
@@ -2480,7 +2480,7 @@ xdrgen_encode_READLINK3resok(struct xdr_stream *xdr, const struct READLINK3resok
{
if (!xdrgen_encode_post_op_attr(xdr, &value->symlink_attributes))
return false;
- if (!xdrgen_encode_nfspath3(xdr, value->data))
+ if (!svcxdr_encode_opaque_payload(xdr, value->data.len))
return false;
return true;
}
diff --git a/fs/nfsd/nfs3xdr_gen.h b/fs/nfsd/nfs3xdr_gen.h
index 58499ca28c43..3fefbb797b69 100644
--- a/fs/nfsd/nfs3xdr_gen.h
+++ b/fs/nfsd/nfs3xdr_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/nfs3.x */
-/* XDR specification modification time: Fri Jul 10 23:21:45 2026 */
+/* XDR specification modification time: Tue Jul 14 09:53:56 2026 */
#ifndef _LINUX_XDRGEN_NFS3_DECL_H
#define _LINUX_XDRGEN_NFS3_DECL_H
diff --git a/fs/nfsd/xdr3.h b/fs/nfsd/xdr3.h
index bb23c444c23d..39a2109a2c66 100644
--- a/fs/nfsd/xdr3.h
+++ b/fs/nfsd/xdr3.h
@@ -9,6 +9,7 @@
#define _LINUX_NFSD_XDR3_H
#include <linux/vfs.h>
+#include <linux/sunrpc/xdrgen/nfs3.h>
#include "vfs.h"
@@ -134,13 +135,6 @@ struct nfsd3_accessres {
struct kstat stat;
};
-struct nfsd3_readlinkres {
- __be32 status;
- struct svc_fh fh;
- __u32 len;
- struct page **pages;
-};
-
struct nfsd3_readres {
__be32 status;
struct svc_fh fh;
@@ -252,7 +246,6 @@ bool nfs3svc_decode_commitargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs3svc_encode_wccstat(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs3svc_encode_accessres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
-bool nfs3svc_encode_readlinkres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs3svc_encode_readres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs3svc_encode_writeres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs3svc_encode_createres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
diff --git a/include/linux/sunrpc/xdrgen/nfs3.h b/include/linux/sunrpc/xdrgen/nfs3.h
index 785ebff351ce..3f8d1ecda7f2 100644
--- a/include/linux/sunrpc/xdrgen/nfs3.h
+++ b/include/linux/sunrpc/xdrgen/nfs3.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/nfs3.x */
-/* XDR specification modification time: Fri Jul 10 23:21:45 2026 */
+/* XDR specification modification time: Tue Jul 14 09:53:56 2026 */
#ifndef _LINUX_XDRGEN_NFS3_DEF_H
#define _LINUX_XDRGEN_NFS3_DEF_H
--
2.55.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v3 13/34] NFSD: Use xdrgen XDR functions for NFSv3 READ procedure
2026-10-13 12:49 [PATCH v3 10/34] NFSD: Use xdrgen XDR functions for the NFSv3 LOOKUP procedure Chuck Lever
2026-10-13 12:49 ` [PATCH v3 11/34] NFSD: Use xdrgen XDR functions for NFSv3 ACCESS procedure Chuck Lever
2026-10-13 12:49 ` [PATCH v3 12/34] NFSD: Use xdrgen XDR functions for NFSv3 READLINK procedure Chuck Lever
@ 2026-10-13 12:49 ` Chuck Lever
2026-10-13 12:49 ` [PATCH v3 14/34] NFSD: Use xdrgen XDR functions for NFSv3 WRITE procedure Chuck Lever
` (5 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Chuck Lever @ 2026-10-13 12:49 UTC (permalink / raw)
To: NeilBrown, Jeff Layton, Olga Kornievskaia, Dai Ngo, Tom Talpey; +Cc: linux-nfs
Replace the NFSPROC3_READ entry in the nfsd_procedures3 array
with an entry that dispatches the xdrgen-generated
nfs_svc_decode_READ3args and nfs_svc_encode_READ3res. A wrapper
structure bridges the generated xdrgen READ3args type and the
legacy svc_fh representation the NFSD VFS layer still uses.
The file's data does not reside at the data pointer of the
READ3resok data member: nfsd_read() deposits it directly in the
pages of the Reply buffer, so the generic encoder that copies an
opaque from the member's data pointer cannot encode it. Mark the
member with the "pragma pages" directive so the generated encoder
calls svcxdr_encode_opaque_payload(), which encodes the length
prefix and inserts the payload pages into the stream by reference.
The page anchor that the result structure carried duplicated
rq_res.pages -- svc_process() points rq_res.pages at the first
Reply page before dispatch, and the READ3args decoder consumes no
pages -- so the helper locates the payload through the Reply
buffer itself, and the wrapper collapses to the generated READ3res
type.
The pc_argzero field is now set to zero for the NFSv3 READ
procedure. The xdrgen decoders are trusted to initialize all
arguments in the argp->xdrgen field, making the early defensive
memset unnecessary. The remaining argp fields are cleared as
needed.
Because nfsd3_proc_read() now fills in the post-op attributes,
the fh_getattr() calls are made in the proc function rather than
in the XDR result encoder, and the reference to the file handle
can be released directly by nfsd3_proc_read(). A separate
->pc_release callback is thus no longer needed. This makes it
straightforward to confirm that the file handle is properly
released during every error flow.
nfs3svc_decode_readargs() no longer has any callers, so it is
removed.
Signed-off-by: Chuck Lever <cel@kernel.org>
---
Documentation/sunrpc/xdr/nfs3.x | 1 +
fs/nfsd/nfs3proc.c | 99 ++++++++++++++++++------------
fs/nfsd/nfs3xdr.c | 48 ---------------
fs/nfsd/nfs3xdr_gen.c | 4 +-
fs/nfsd/nfs3xdr_gen.h | 2 +-
fs/nfsd/xdr3.h | 16 -----
include/linux/sunrpc/xdrgen/nfs3.h | 2 +-
7 files changed, 66 insertions(+), 106 deletions(-)
diff --git a/Documentation/sunrpc/xdr/nfs3.x b/Documentation/sunrpc/xdr/nfs3.x
index b50ffb77af54..b7571bc47f47 100644
--- a/Documentation/sunrpc/xdr/nfs3.x
+++ b/Documentation/sunrpc/xdr/nfs3.x
@@ -356,6 +356,7 @@ struct READ3resok {
bool eof;
opaque data<>;
};
+pragma pages READ3resok data;
struct READ3resfail {
post_op_attr file_attributes;
diff --git a/fs/nfsd/nfs3proc.c b/fs/nfsd/nfs3proc.c
index 3c557124196f..3d5d9bedc89c 100644
--- a/fs/nfsd/nfs3proc.c
+++ b/fs/nfsd/nfs3proc.c
@@ -85,6 +85,13 @@ struct READLINK3args_wrapper {
static_assert(offsetof(struct READLINK3args_wrapper, xdrgen) == 0);
+struct READ3args_wrapper {
+ struct READ3args xdrgen;
+ struct svc_fh fh;
+};
+
+static_assert(offsetof(struct READ3args_wrapper, xdrgen) == 0);
+
static int nfs3_ftypes[] = {
0, /* NF3NON */
S_IFREG, /* NF3REG */
@@ -635,41 +642,57 @@ static __be32 nfsd3_proc_readlink(struct svc_rqst *rqstp)
return rpc_success;
}
-/*
- * Read a portion of a file.
+/**
+ * nfsd3_proc_read - READ: Read from file
+ * @rqstp: RPC transaction context
+ *
+ * Return:
+ * %rpc_success: RPC executed successfully
+ *
+ * RPC synopsis:
+ * READ3res NFSPROC3_READ(READ3args) = 6;
*/
-static __be32
-nfsd3_proc_read(struct svc_rqst *rqstp)
+static __be32 nfsd3_proc_read(struct svc_rqst *rqstp)
{
- struct nfsd3_readargs *argp = rqstp->rq_argp;
- struct nfsd3_readres *resp = rqstp->rq_resp;
+ struct READ3args_wrapper *argp = rqstp->rq_argp;
+ struct READ3res *resp = rqstp->rq_resp;
+ unsigned long count = argp->xdrgen.count;
+ u64 offset = argp->xdrgen.offset;
+ struct svc_fh *fhp = &argp->fh;
+ u32 eof;
- dprintk("nfsd: READ(3) %s %lu bytes at %Lu\n",
- SVCFH_fmt(&argp->fh),
- (unsigned long) argp->count,
- (unsigned long long) argp->offset);
+ nfsd3_fh3_to_svc_fh(fhp, &argp->xdrgen.file);
+ count = min_t(u32, count, svc_max_payload(rqstp));
+ count = min_t(u32, count, rqstp->rq_res.buflen);
+ if (offset > (u64)OFFSET_MAX)
+ offset = (u64)OFFSET_MAX;
+ if (offset + count > (u64)OFFSET_MAX)
+ count = (u64)OFFSET_MAX - offset;
- argp->count = min_t(u32, argp->count, svc_max_payload(rqstp));
- argp->count = min_t(u32, argp->count, rqstp->rq_res.buflen);
- if (argp->offset > (u64)OFFSET_MAX)
- argp->offset = (u64)OFFSET_MAX;
- if (argp->offset + argp->count > (u64)OFFSET_MAX)
- argp->count = (u64)OFFSET_MAX - argp->offset;
-
- resp->pages = rqstp->rq_next_page;
-
- /* Obtain buffer pointer for payload.
+ /*
+ * Obtain buffer pointer for payload.
* 1 (status) + 22 (post_op_attr) + 1 (count) + 1 (eof)
* + 1 (xdr opaque byte count) = 26
*/
- resp->count = argp->count;
svc_reserve_auth(rqstp, ((1 + NFS3_post_op_attr_sz + 3) << 2) +
- resp->count + 4);
+ count + 4);
+ resp->status = nfsd_read(rqstp, fhp, offset, &count, &eof);
- fh_copy(&resp->fh, &argp->fh);
- resp->status = nfsd_read(rqstp, &resp->fh, argp->offset,
- &resp->count, &resp->eof);
- resp->status = nfsd3_map_status(resp->status);
+ if (resp->status == nfs_ok) {
+ struct READ3resok *resok = &resp->u.resok;
+
+ resok->count = count;
+ resok->eof = !!eof;
+ resok->data.len = count;
+ nfsd3_fill_post_op_attr(rqstp, &resok->file_attributes, fhp);
+ } else {
+ struct READ3resfail *resfail = &resp->u.resfail;
+
+ resp->status = nfsd3_map_status(resp->status);
+ nfsd3_fill_post_op_attr(rqstp, &resfail->file_attributes, fhp);
+ }
+
+ fh_put(fhp);
return rpc_success;
}
@@ -1370,16 +1393,16 @@ static const struct svc_procedure nfsd_procedures3[22] = {
.pc_name = "READLINK",
},
[NFSPROC3_READ] = {
- .pc_func = nfsd3_proc_read,
- .pc_decode = nfs3svc_decode_readargs,
- .pc_encode = nfs3svc_encode_readres,
- .pc_release = nfs3svc_release_fhandle,
- .pc_argsize = sizeof(struct nfsd3_readargs),
- .pc_argzero = sizeof(struct nfsd3_readargs),
- .pc_ressize = sizeof(struct nfsd3_readres),
- .pc_cachetype = RC_NOCACHE,
- .pc_xdrressize = ST+pAT+4+NFSSVC_MAXBLKSIZE/4,
- .pc_name = "READ",
+ .pc_func = nfsd3_proc_read,
+ .pc_decode = nfs_svc_decode_READ3args,
+ .pc_encode = nfs_svc_encode_READ3res,
+ .pc_argsize = sizeof(struct READ3args_wrapper),
+ .pc_argzero = 0,
+ .pc_ressize = sizeof(struct READ3res),
+ .pc_cachetype = RC_NOCACHE,
+ .pc_xdrressize = NFS3_READ3res_sz +
+ XDR_QUADLEN(NFSSVC_MAXBLKSIZE),
+ .pc_name = "READ",
},
[NFSPROC3_WRITE] = {
.pc_func = nfsd3_proc_write,
@@ -1571,8 +1594,8 @@ union nfsd3_xdrstore {
struct ACCESS3args_wrapper accessargs;
struct ACCESS3res accessres;
struct READLINK3args_wrapper readlinkargs;
+ struct READ3args_wrapper readargs;
struct nfsd3_diropargs diropargs;
- struct nfsd3_readargs readargs;
struct nfsd3_writeargs writeargs;
struct nfsd3_createargs createargs;
struct nfsd3_renameargs renameargs;
@@ -1581,7 +1604,7 @@ union nfsd3_xdrstore {
struct nfsd3_readdirargs readdirargs;
struct nfsd3_diropres diropres;
struct READLINK3res readlinkres;
- struct nfsd3_readres readres;
+ struct READ3res readres;
struct nfsd3_writeres writeres;
struct nfsd3_renameres renameres;
struct nfsd3_linkres linkres;
diff --git a/fs/nfsd/nfs3xdr.c b/fs/nfsd/nfs3xdr.c
index a77b5f7a7e27..9cee503407f7 100644
--- a/fs/nfsd/nfs3xdr.c
+++ b/fs/nfsd/nfs3xdr.c
@@ -490,21 +490,6 @@ nfs3svc_decode_diropargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
return svcxdr_decode_diropargs3(xdr, &args->fh, &args->name, &args->len);
}
-bool
-nfs3svc_decode_readargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
-{
- struct nfsd3_readargs *args = rqstp->rq_argp;
-
- if (!svcxdr_decode_nfs_fh3(xdr, &args->fh))
- return false;
- if (xdr_stream_decode_u64(xdr, &args->offset) < 0)
- return false;
- if (xdr_stream_decode_u32(xdr, &args->count) < 0)
- return false;
-
- return true;
-}
-
bool
nfs3svc_decode_writeargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
@@ -708,39 +693,6 @@ nfs3svc_encode_wccstat(struct svc_rqst *rqstp, struct xdr_stream *xdr)
svcxdr_encode_wcc_data(rqstp, xdr, &resp->fh);
}
-/* READ */
-bool
-nfs3svc_encode_readres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
-{
- struct nfsd3_readres *resp = rqstp->rq_resp;
- struct kvec *head = rqstp->rq_res.head;
-
- if (!svcxdr_encode_nfsstat3(xdr, resp->status))
- return false;
- switch (resp->status) {
- case nfs_ok:
- if (!svcxdr_encode_post_op_attr(rqstp, xdr, &resp->fh))
- return false;
- if (xdr_stream_encode_u32(xdr, resp->count) < 0)
- return false;
- if (xdr_stream_encode_bool(xdr, resp->eof) < 0)
- return false;
- if (xdr_stream_encode_u32(xdr, resp->count) < 0)
- return false;
- svcxdr_encode_opaque_pages(rqstp, xdr, resp->pages,
- rqstp->rq_res.page_base,
- resp->count);
- if (svc_encode_result_payload(rqstp, head->iov_len, resp->count) < 0)
- return false;
- break;
- default:
- if (!svcxdr_encode_post_op_attr(rqstp, xdr, &resp->fh))
- return false;
- }
-
- return true;
-}
-
/* WRITE */
bool
nfs3svc_encode_writeres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
diff --git a/fs/nfsd/nfs3xdr_gen.c b/fs/nfsd/nfs3xdr_gen.c
index bc6fb9829397..0961f6c92ee6 100644
--- a/fs/nfsd/nfs3xdr_gen.c
+++ b/fs/nfsd/nfs3xdr_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/nfs3.x
-// XDR specification modification time: Tue Jul 14 09:53:56 2026
+// XDR specification modification time: Tue Jul 14 11:24:16 2026
#include <linux/sunrpc/svc.h>
@@ -2531,7 +2531,7 @@ xdrgen_encode_READ3resok(struct xdr_stream *xdr, const struct READ3resok *value)
return false;
if (!xdrgen_encode_bool(xdr, value->eof))
return false;
- if (xdr_stream_encode_opaque(xdr, value->data.data, value->data.len) < 0)
+ if (!svcxdr_encode_opaque_payload(xdr, value->data.len))
return false;
return true;
}
diff --git a/fs/nfsd/nfs3xdr_gen.h b/fs/nfsd/nfs3xdr_gen.h
index 3fefbb797b69..d54b087b7748 100644
--- a/fs/nfsd/nfs3xdr_gen.h
+++ b/fs/nfsd/nfs3xdr_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/nfs3.x */
-/* XDR specification modification time: Tue Jul 14 09:53:56 2026 */
+/* XDR specification modification time: Tue Jul 14 11:24:16 2026 */
#ifndef _LINUX_XDRGEN_NFS3_DECL_H
#define _LINUX_XDRGEN_NFS3_DECL_H
diff --git a/fs/nfsd/xdr3.h b/fs/nfsd/xdr3.h
index 39a2109a2c66..6ca1a2c84b42 100644
--- a/fs/nfsd/xdr3.h
+++ b/fs/nfsd/xdr3.h
@@ -30,12 +30,6 @@ struct nfsd3_accessargs {
__u32 access;
};
-struct nfsd3_readargs {
- struct svc_fh fh;
- __u64 offset;
- __u32 count;
-};
-
struct nfsd3_writeargs {
svc_fh fh;
__u64 offset;
@@ -135,14 +129,6 @@ struct nfsd3_accessres {
struct kstat stat;
};
-struct nfsd3_readres {
- __be32 status;
- struct svc_fh fh;
- unsigned long count;
- __u32 eof;
- struct page **pages;
-};
-
struct nfsd3_writeres {
__be32 status;
struct svc_fh fh;
@@ -232,7 +218,6 @@ struct nfsd3_fhandle_pair {
bool nfs3svc_decode_fhandleargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs3svc_decode_diropargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
-bool nfs3svc_decode_readargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs3svc_decode_writeargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs3svc_decode_createargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs3svc_decode_mkdirargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
@@ -246,7 +231,6 @@ bool nfs3svc_decode_commitargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs3svc_encode_wccstat(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs3svc_encode_accessres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
-bool nfs3svc_encode_readres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs3svc_encode_writeres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs3svc_encode_createres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs3svc_encode_renameres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
diff --git a/include/linux/sunrpc/xdrgen/nfs3.h b/include/linux/sunrpc/xdrgen/nfs3.h
index 3f8d1ecda7f2..75ff4a924cf4 100644
--- a/include/linux/sunrpc/xdrgen/nfs3.h
+++ b/include/linux/sunrpc/xdrgen/nfs3.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/nfs3.x */
-/* XDR specification modification time: Tue Jul 14 09:53:56 2026 */
+/* XDR specification modification time: Tue Jul 14 11:24:16 2026 */
#ifndef _LINUX_XDRGEN_NFS3_DEF_H
#define _LINUX_XDRGEN_NFS3_DEF_H
--
2.55.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v3 14/34] NFSD: Use xdrgen XDR functions for NFSv3 WRITE procedure
2026-10-13 12:49 [PATCH v3 10/34] NFSD: Use xdrgen XDR functions for the NFSv3 LOOKUP procedure Chuck Lever
` (2 preceding siblings ...)
2026-10-13 12:49 ` [PATCH v3 13/34] NFSD: Use xdrgen XDR functions for NFSv3 READ procedure Chuck Lever
@ 2026-10-13 12:49 ` Chuck Lever
2026-10-13 12:49 ` [PATCH v3 15/34] NFSD: Use xdrgen XDR functions for NFSv3 CREATE procedure Chuck Lever
` (4 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Chuck Lever @ 2026-10-13 12:49 UTC (permalink / raw)
To: NeilBrown, Jeff Layton, Olga Kornievskaia, Dai Ngo, Tom Talpey; +Cc: linux-nfs
Replace the NFSPROC3_WRITE entry in the nfsd_procedures3 array with
an entry that dispatches the xdrgen-generated nfs_svc_decode_WRITE3args
and nfs_svc_encode_WRITE3res. Wrapper structures bridge the generated
xdrgen types and the legacy svc_fh representation the NFSD VFS layer
still uses.
The NFSv3 WRITE argument decoder could not be generated before because
the write payload must be directed into the server's page cache and
decoded in place; a materializing decoder would copy every write. The
"pages" directive now expresses this: it represents the payload as a
struct xdr_buf and decodes it with svcxdr_decode_opaque_payload(), which
subsegments the source stream in place rather than copying it. Annotate
the data member of WRITE3args with "pragma pages".
Clamping the payload size when decoding serves no functional purpose and
is removed entirely. The transport layer has already guaranteed that the
incoming message, including the WRITE payload, does not exceed
svc_max_payload.
The WRITE count versus data length sanity check moves to the
proc function: if those values are ever not equal, that does not
count as GARBAGE_ARGS, since the server is able to decode these
arguments. Rather, the arguments are invalid.
The pc_argzero field is now set to zero for the NFSv3 WRITE
procedure. The xdrgen decoders are trusted to initialize all
arguments in the argp->xdrgen field, making the early defensive memset
unnecessary.
Because nfsd3_proc_write() now fills in the WCC data, including
both the pre-op and post-op attributes, the fh_getattr() calls are
made in the proc function rather than in the XDR result encoder,
and the reference to the file handle can be released directly by
nfsd3_proc_write(). A separate ->pc_release callback is thus no longer
needed. This makes it straightforward to confirm that the file handle
is properly released during every error flow.
nfs3svc_decode_writeargs() and nfs3svc_encode_writeres() no longer have
any callers, so they are removed.
Signed-off-by: Chuck Lever <cel@kernel.org>
---
Documentation/sunrpc/xdr/nfs3.x | 1 +
fs/nfsd/nfs3proc.c | 94 +++++++++++++++++++-----------
fs/nfsd/nfs3xdr.c | 59 -------------------
fs/nfsd/nfs3xdr_gen.c | 8 +--
fs/nfsd/nfs3xdr_gen.h | 4 +-
fs/nfsd/xdr3.h | 19 +-----
include/linux/sunrpc/xdrgen/nfs3.h | 6 +-
7 files changed, 73 insertions(+), 118 deletions(-)
diff --git a/Documentation/sunrpc/xdr/nfs3.x b/Documentation/sunrpc/xdr/nfs3.x
index b7571bc47f47..61b87245be1e 100644
--- a/Documentation/sunrpc/xdr/nfs3.x
+++ b/Documentation/sunrpc/xdr/nfs3.x
@@ -382,6 +382,7 @@ struct WRITE3args {
stable_how stable;
opaque data<>;
};
+pragma pages WRITE3args data;
struct WRITE3resok {
wcc_data file_wcc;
diff --git a/fs/nfsd/nfs3proc.c b/fs/nfsd/nfs3proc.c
index 3d5d9bedc89c..73f58914c3d2 100644
--- a/fs/nfsd/nfs3proc.c
+++ b/fs/nfsd/nfs3proc.c
@@ -92,6 +92,13 @@ struct READ3args_wrapper {
static_assert(offsetof(struct READ3args_wrapper, xdrgen) == 0);
+struct WRITE3args_wrapper {
+ struct WRITE3args xdrgen;
+ struct svc_fh fh;
+};
+
+static_assert(offsetof(struct WRITE3args_wrapper, xdrgen) == 0);
+
static int nfs3_ftypes[] = {
0, /* NF3NON */
S_IFREG, /* NF3REG */
@@ -696,35 +703,55 @@ static __be32 nfsd3_proc_read(struct svc_rqst *rqstp)
return rpc_success;
}
-/*
- * Write data to a file
+/**
+ * nfsd3_proc_write - WRITE: Write to file
+ * @rqstp: RPC transaction context
+ *
+ * Return:
+ * %rpc_success: RPC executed successfully
+ *
+ * RPC synopsis:
+ * WRITE3res NFSPROC3_WRITE(WRITE3args) = 7;
*/
-static __be32
-nfsd3_proc_write(struct svc_rqst *rqstp)
+static __be32 nfsd3_proc_write(struct svc_rqst *rqstp)
{
- struct nfsd3_writeargs *argp = rqstp->rq_argp;
+ struct WRITE3args_wrapper *argp = rqstp->rq_argp;
struct nfsd3_writeres *resp = rqstp->rq_resp;
- unsigned long cnt = argp->len;
+ struct svc_fh *fhp = &argp->fh;
+ unsigned long count;
- dprintk("nfsd: WRITE(3) %s %d bytes at %Lu%s\n",
- SVCFH_fmt(&argp->fh),
- argp->len,
- (unsigned long long) argp->offset,
- argp->stable ? " stable" : "");
+ nfsd3_fh3_to_svc_fh(fhp, &argp->xdrgen.file);
+ if (argp->xdrgen.count != argp->xdrgen.data.len) {
+ resp->xdrgen.status = nfserr_inval;
+ goto out;
+ }
+ count = min_t(u32, argp->xdrgen.count, svc_max_payload(rqstp));
+ if (argp->xdrgen.offset > (u64)OFFSET_MAX ||
+ argp->xdrgen.offset + count > (u64)OFFSET_MAX) {
+ resp->xdrgen.status = nfserr_fbig;
+ goto out;
+ }
- resp->status = nfserr_fbig;
- if (argp->offset > (u64)OFFSET_MAX ||
- argp->offset + argp->len > (u64)OFFSET_MAX)
- return rpc_success;
+ resp->xdrgen.status = nfsd_write(rqstp, fhp, argp->xdrgen.offset,
+ &argp->xdrgen.data, &count,
+ nfsd3_iocb_flags(argp->xdrgen.stable),
+ (__be32 *)resp->xdrgen.u.resok.verf);
- fh_copy(&resp->fh, &argp->fh);
- resp->committed = argp->stable;
- resp->status = nfsd_write(rqstp, &resp->fh, argp->offset,
- &argp->payload, &cnt,
- nfsd3_iocb_flags(resp->committed),
- resp->verf);
- resp->count = cnt;
- resp->status = nfsd3_map_status(resp->status);
+out:
+ if (resp->xdrgen.status == nfs_ok) {
+ struct WRITE3resok *resok = &resp->xdrgen.u.resok;
+
+ resok->count = count;
+ resok->committed = argp->xdrgen.stable;
+ nfsd3_fill_wcc_data(rqstp, &resok->file_wcc, fhp);
+ } else {
+ struct WRITE3resfail *resfail = &resp->xdrgen.u.resfail;
+
+ resp->xdrgen.status = nfsd3_map_status(resp->xdrgen.status);
+ nfsd3_fill_wcc_data(rqstp, &resfail->file_wcc, fhp);
+ }
+
+ fh_put(fhp);
return rpc_success;
}
@@ -1405,16 +1432,15 @@ static const struct svc_procedure nfsd_procedures3[22] = {
.pc_name = "READ",
},
[NFSPROC3_WRITE] = {
- .pc_func = nfsd3_proc_write,
- .pc_decode = nfs3svc_decode_writeargs,
- .pc_encode = nfs3svc_encode_writeres,
- .pc_release = nfs3svc_release_fhandle,
- .pc_argsize = sizeof(struct nfsd3_writeargs),
- .pc_argzero = sizeof(struct nfsd3_writeargs),
- .pc_ressize = sizeof(struct nfsd3_writeres),
- .pc_cachetype = RC_REPLBUFF,
- .pc_xdrressize = ST+WC+4,
- .pc_name = "WRITE",
+ .pc_func = nfsd3_proc_write,
+ .pc_decode = nfs_svc_decode_WRITE3args,
+ .pc_encode = nfs_svc_encode_WRITE3res,
+ .pc_argsize = sizeof(struct WRITE3args_wrapper),
+ .pc_argzero = 0,
+ .pc_ressize = sizeof(struct nfsd3_writeres),
+ .pc_cachetype = RC_REPLBUFF,
+ .pc_xdrressize = NFS3_WRITE3res_sz,
+ .pc_name = "WRITE",
},
[NFSPROC3_CREATE] = {
.pc_func = nfsd3_proc_create,
@@ -1596,7 +1622,7 @@ union nfsd3_xdrstore {
struct READLINK3args_wrapper readlinkargs;
struct READ3args_wrapper readargs;
struct nfsd3_diropargs diropargs;
- struct nfsd3_writeargs writeargs;
+ struct WRITE3args_wrapper writeargs;
struct nfsd3_createargs createargs;
struct nfsd3_renameargs renameargs;
struct nfsd3_linkargs linkargs;
diff --git a/fs/nfsd/nfs3xdr.c b/fs/nfsd/nfs3xdr.c
index 9cee503407f7..cc3aebd97959 100644
--- a/fs/nfsd/nfs3xdr.c
+++ b/fs/nfsd/nfs3xdr.c
@@ -490,38 +490,6 @@ nfs3svc_decode_diropargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
return svcxdr_decode_diropargs3(xdr, &args->fh, &args->name, &args->len);
}
-bool
-nfs3svc_decode_writeargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
-{
- struct nfsd3_writeargs *args = rqstp->rq_argp;
- u32 max_blocksize = svc_max_payload(rqstp);
-
- if (!svcxdr_decode_nfs_fh3(xdr, &args->fh))
- return false;
- if (xdr_stream_decode_u64(xdr, &args->offset) < 0)
- return false;
- if (xdr_stream_decode_u32(xdr, &args->count) < 0)
- return false;
- if (xdr_stream_decode_u32(xdr, &args->stable) < 0)
- return false;
- if (args->stable > FILE_SYNC)
- return false;
-
- /* opaque data */
- if (xdr_stream_decode_u32(xdr, &args->len) < 0)
- return false;
-
- /* request sanity */
- if (args->count != args->len)
- return false;
- if (args->count > max_blocksize) {
- args->count = max_blocksize;
- args->len = max_blocksize;
- }
-
- return xdr_stream_subsegment(xdr, &args->payload, args->count);
-}
-
bool
nfs3svc_decode_createargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
@@ -693,33 +661,6 @@ nfs3svc_encode_wccstat(struct svc_rqst *rqstp, struct xdr_stream *xdr)
svcxdr_encode_wcc_data(rqstp, xdr, &resp->fh);
}
-/* WRITE */
-bool
-nfs3svc_encode_writeres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
-{
- struct nfsd3_writeres *resp = rqstp->rq_resp;
-
- if (!svcxdr_encode_nfsstat3(xdr, resp->status))
- return false;
- switch (resp->status) {
- case nfs_ok:
- if (!svcxdr_encode_wcc_data(rqstp, xdr, &resp->fh))
- return false;
- if (xdr_stream_encode_u32(xdr, resp->count) < 0)
- return false;
- if (xdr_stream_encode_u32(xdr, resp->committed) < 0)
- return false;
- if (!svcxdr_encode_writeverf3(xdr, resp->verf))
- return false;
- break;
- default:
- if (!svcxdr_encode_wcc_data(rqstp, xdr, &resp->fh))
- return false;
- }
-
- return true;
-}
-
/* CREATE, MKDIR, SYMLINK, MKNOD */
bool
nfs3svc_encode_createres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
diff --git a/fs/nfsd/nfs3xdr_gen.c b/fs/nfsd/nfs3xdr_gen.c
index 0961f6c92ee6..159aff1a8567 100644
--- a/fs/nfsd/nfs3xdr_gen.c
+++ b/fs/nfsd/nfs3xdr_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/nfs3.x
-// XDR specification modification time: Tue Jul 14 11:24:16 2026
+// XDR specification file: Documentation/sunrpc/xdr/nfs3.x
+// XDR specification modification time: Tue Jul 14 11:43:26 2026
#include <linux/sunrpc/svc.h>
@@ -731,7 +731,7 @@ xdrgen_decode_WRITE3args(struct xdr_stream *xdr, struct WRITE3args *ptr)
return false;
if (!xdrgen_decode_stable_how(xdr, &ptr->stable))
return false;
- if (!xdrgen_decode_opaque(xdr, &ptr->data, 0))
+ if (!svcxdr_decode_opaque_payload(xdr, &ptr->data, 0))
return false;
return true;
}
@@ -2578,7 +2578,7 @@ xdrgen_encode_WRITE3args(struct xdr_stream *xdr, const struct WRITE3args *value)
return false;
if (!xdrgen_encode_stable_how(xdr, value->stable))
return false;
- if (xdr_stream_encode_opaque(xdr, value->data.data, value->data.len) < 0)
+ if (!svcxdr_encode_opaque_payload(xdr, value->data.len))
return false;
return true;
}
diff --git a/fs/nfsd/nfs3xdr_gen.h b/fs/nfsd/nfs3xdr_gen.h
index d54b087b7748..98e29c8efc5e 100644
--- a/fs/nfsd/nfs3xdr_gen.h
+++ b/fs/nfsd/nfs3xdr_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/nfs3.x */
-/* XDR specification modification time: Tue Jul 14 11:24:16 2026 */
+/* XDR specification file: Documentation/sunrpc/xdr/nfs3.x */
+/* XDR specification modification time: Tue Jul 14 11:43:26 2026 */
#ifndef _LINUX_XDRGEN_NFS3_DECL_H
#define _LINUX_XDRGEN_NFS3_DECL_H
diff --git a/fs/nfsd/xdr3.h b/fs/nfsd/xdr3.h
index 6ca1a2c84b42..9c41244d27bb 100644
--- a/fs/nfsd/xdr3.h
+++ b/fs/nfsd/xdr3.h
@@ -30,15 +30,6 @@ struct nfsd3_accessargs {
__u32 access;
};
-struct nfsd3_writeargs {
- svc_fh fh;
- __u64 offset;
- __u32 count;
- __u32 stable;
- __u32 len;
- struct xdr_buf payload;
-};
-
struct nfsd3_createargs {
struct svc_fh fh;
char * name;
@@ -130,13 +121,11 @@ struct nfsd3_accessres {
};
struct nfsd3_writeres {
- __be32 status;
- struct svc_fh fh;
- unsigned long count;
- int committed;
- __be32 verf[2];
+ struct WRITE3res xdrgen;
};
+static_assert(offsetof(struct nfsd3_writeres, xdrgen) == 0);
+
struct nfsd3_renameres {
__be32 status;
struct svc_fh ffh;
@@ -218,7 +207,6 @@ struct nfsd3_fhandle_pair {
bool nfs3svc_decode_fhandleargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs3svc_decode_diropargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
-bool nfs3svc_decode_writeargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs3svc_decode_createargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs3svc_decode_mkdirargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs3svc_decode_mknodargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
@@ -231,7 +219,6 @@ bool nfs3svc_decode_commitargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs3svc_encode_wccstat(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs3svc_encode_accessres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
-bool nfs3svc_encode_writeres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs3svc_encode_createres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs3svc_encode_renameres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs3svc_encode_linkres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
diff --git a/include/linux/sunrpc/xdrgen/nfs3.h b/include/linux/sunrpc/xdrgen/nfs3.h
index 75ff4a924cf4..3b6738c0074b 100644
--- a/include/linux/sunrpc/xdrgen/nfs3.h
+++ b/include/linux/sunrpc/xdrgen/nfs3.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/nfs3.x */
-/* XDR specification modification time: Tue Jul 14 11:24:16 2026 */
+/* XDR specification file: Documentation/sunrpc/xdr/nfs3.x */
+/* XDR specification modification time: Tue Jul 14 11:43:26 2026 */
#ifndef _LINUX_XDRGEN_NFS3_DEF_H
#define _LINUX_XDRGEN_NFS3_DEF_H
@@ -382,7 +382,7 @@ struct WRITE3args {
offset3 offset;
count3 count;
stable_how stable;
- opaque data;
+ struct xdr_buf data;
};
struct WRITE3resok {
--
2.55.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v3 15/34] NFSD: Use xdrgen XDR functions for NFSv3 CREATE procedure
2026-10-13 12:49 [PATCH v3 10/34] NFSD: Use xdrgen XDR functions for the NFSv3 LOOKUP procedure Chuck Lever
` (3 preceding siblings ...)
2026-10-13 12:49 ` [PATCH v3 14/34] NFSD: Use xdrgen XDR functions for NFSv3 WRITE procedure Chuck Lever
@ 2026-10-13 12:49 ` Chuck Lever
2026-10-13 12:49 ` [PATCH v3 16/34] NFSD: Use xdrgen XDR functions for NFSv3 MKDIR procedure Chuck Lever
` (3 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Chuck Lever @ 2026-10-13 12:49 UTC (permalink / raw)
To: NeilBrown, Jeff Layton, Olga Kornievskaia, Dai Ngo, Tom Talpey; +Cc: linux-nfs
Replace the NFSPROC3_CREATE entry in the nfsd_procedures3 array
with an entry that dispatches the xdrgen-generated
nfs_svc_decode_CREATE3args and nfs_svc_encode_CREATE3res. Wrapper
structures bridge the generated xdrgen types and the legacy
svc_fh and iattr representations the NFSD VFS layer still uses.
Introduce nfsd3_fill_post_op_fh3() to fill a post_op_fh3. It is
useful here and in subsequent patches.
Filename validity is now checked by nfsd3_create_file() so that a
proper NFS3ERR status code can be returned to the client rather
than GARBAGE_ARGS.
The pc_argzero field is now set to zero for the NFSv3 CREATE
procedure. The xdrgen decoders are trusted to initialize all
arguments in the argp->xdrgen field, making the early defensive
memset unnecessary. The remaining argp fields are cleared as
needed.
Because nfsd3_proc_create() now fills in the post-op file handle,
the post-op attributes, and the WCC data, the fh_getattr() calls
are made in the proc function rather than in the XDR result
encoder, and the references to the file handles can be released
directly by nfsd3_proc_create(). A separate ->pc_release callback
is thus no longer needed. This makes it straightforward to
confirm that the file handles are properly released during every
error flow.
nfs3svc_decode_createargs() no longer has any callers, so it is
removed.
Signed-off-by: Chuck Lever <cel@kernel.org>
---
fs/nfsd/nfs3proc.c | 194 ++++++++++++++++++++++++++++++---------------
fs/nfsd/nfs3xdr.c | 24 ------
fs/nfsd/xdr3.h | 1 -
3 files changed, 130 insertions(+), 89 deletions(-)
diff --git a/fs/nfsd/nfs3proc.c b/fs/nfsd/nfs3proc.c
index 73f58914c3d2..6f7b5b1d7f20 100644
--- a/fs/nfsd/nfs3proc.c
+++ b/fs/nfsd/nfs3proc.c
@@ -99,6 +99,22 @@ struct WRITE3args_wrapper {
static_assert(offsetof(struct WRITE3args_wrapper, xdrgen) == 0);
+struct CREATE3args_wrapper {
+ struct CREATE3args xdrgen;
+ struct svc_fh fh;
+ struct iattr iattrs;
+};
+
+static_assert(offsetof(struct CREATE3args_wrapper, xdrgen) == 0);
+
+struct CREATE3res_wrapper {
+ struct CREATE3res xdrgen;
+ struct svc_fh fh;
+ u8 fh_data[NFS3_FHSIZE];
+};
+
+static_assert(offsetof(struct CREATE3res_wrapper, xdrgen) == 0);
+
static int nfs3_ftypes[] = {
0, /* NF3NON */
S_IFREG, /* NF3REG */
@@ -420,6 +436,18 @@ nfsd3_fill_post_op_attr(struct svc_rqst *rqstp, struct post_op_attr *attr,
attr->attributes_follow = false;
}
+static void
+nfsd3_fill_post_op_fh3(struct post_op_fh3 *post_op_fh,
+ const struct svc_fh *fhp, u8 *data)
+{
+ if (fhp->fh_handle.fh_size != 0) {
+ post_op_fh->handle_follows = true;
+ nfsd3_svc_fh_to_fh3(&post_op_fh->u.handle, fhp, data);
+ } else {
+ post_op_fh->handle_follows = false;
+ }
+}
+
/*
* A full specification of each of the following NFSv3 procedures is
* available in RFC 1813 Section 3.3.
@@ -759,59 +787,72 @@ static __be32 nfsd3_proc_write(struct svc_rqst *rqstp)
* Implement NFSv3's unchecked, guarded, and exclusive CREATE
* semantics for regular files. Except for the created file,
* this operation is stateless on the server.
- *
- * Upon return, caller must release @fhp and @resfhp.
*/
static __be32
-nfsd3_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
- struct svc_fh *resfhp, struct nfsd3_createargs *argp)
+nfsd3_create_file(struct svc_rqst *rqstp, struct svc_fh *dirfhp,
+ struct svc_fh *resfhp, struct CREATE3args_wrapper *argp)
{
- struct iattr *iap = &argp->attrs;
+ struct diropargs3 *where = &argp->xdrgen.where;
+ struct createhow3 *how = &argp->xdrgen.how;
+ struct iattr *iattrs = &argp->iattrs;
struct dentry *parent, *child;
- struct nfsd_attrs attrs = {
- .na_iattr = iap,
- };
struct svc_export *exp;
__u32 v_mtime, v_atime;
struct inode *inode;
+ struct nfsd_attrs nattrs = {
+ .na_iattr = iattrs,
+ };
__be32 status;
int host_err;
- trace_nfsd_vfs_create(rqstp, fhp, S_IFREG, argp->name, argp->len);
-
- if (!nfsd3_time_in_range(iap))
- return nfserr_inval;
- if (name_is_dot_dotdot(argp->name, argp->len))
- return nfserr_exist;
- if (!(iap->ia_valid & ATTR_MODE))
- iap->ia_mode = 0;
-
- status = fh_verify(rqstp, fhp, S_IFDIR, NFSD_MAY_EXEC);
+ status = nfsd3_check_filename(where->name.data, where->name.len);
if (status != nfs_ok)
return status;
- parent = fhp->fh_dentry;
+ trace_nfsd_vfs_create(rqstp, dirfhp, S_IFREG, (char *)where->name.data,
+ where->name.len);
+
+ if (name_is_dot_dotdot(where->name.data, where->name.len))
+ return nfserr_exist;
+
+ if (how->mode != EXCLUSIVE) {
+ nfsd3_sattr3_to_iattr(rqstp, iattrs,
+ &how->u.obj_attributes);
+ if (!(iattrs->ia_valid & ATTR_MODE))
+ iattrs->ia_mode = 0;
+ } else {
+ memset(iattrs, 0, sizeof(*iattrs));
+ }
+
+ if (!nfsd3_time_in_range(iattrs))
+ return nfserr_inval;
+
+ status = fh_verify(rqstp, dirfhp, S_IFDIR, NFSD_MAY_EXEC);
+ if (status != nfs_ok)
+ return status;
+
+ parent = dirfhp->fh_dentry;
inode = d_inode(parent);
- host_err = fh_want_write(fhp);
+ host_err = fh_want_write(dirfhp);
if (host_err)
return nfserrno(host_err);
child = start_creating(&nop_mnt_idmap, parent,
- &QSTR_LEN(argp->name, argp->len));
+ &QSTR_LEN(where->name.data, where->name.len));
if (IS_ERR(child)) {
status = nfserrno(PTR_ERR(child));
goto out_write;
}
if (d_really_is_negative(child)) {
- status = fh_verify(rqstp, fhp, S_IFDIR, NFSD_MAY_CREATE);
+ status = fh_verify(rqstp, dirfhp, S_IFDIR, NFSD_MAY_CREATE);
if (status != nfs_ok)
goto out;
}
- exp = exp_get(fhp->fh_export);
- if (argp->createmode == UNCHECKED) {
+ exp = exp_get(dirfhp->fh_export);
+ if (how->mode == UNCHECKED) {
/*
* If name is already in dcache we need to check for mountpoints
*/
@@ -825,15 +866,15 @@ nfsd3_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
}
}
- status = fh_compose(resfhp, exp, child, fhp);
+ status = fh_compose(resfhp, exp, child, dirfhp);
exp_put(exp);
if (status != nfs_ok)
goto out;
v_mtime = 0;
v_atime = 0;
- if (argp->createmode == EXCLUSIVE) {
- u32 *verifier = (u32 *)argp->verf;
+ if (how->mode == EXCLUSIVE) {
+ u32 *verifier = (u32 *)how->u.verf;
/*
* Solaris 7 gets confused (bugid 4218508) if these have
@@ -847,11 +888,11 @@ nfsd3_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
if (d_really_is_positive(child)) {
status = nfs_ok;
- switch (argp->createmode) {
+ switch (how->mode) {
case UNCHECKED:
if (!d_is_reg(child))
break;
- iap->ia_valid &= ATTR_SIZE;
+ iattrs->ia_valid &= ATTR_SIZE;
goto set_attr;
case GUARDED:
status = nfserr_exist;
@@ -868,52 +909,77 @@ nfsd3_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
}
if (!IS_POSIXACL(inode))
- iap->ia_mode &= ~current_umask();
+ iattrs->ia_mode &= ~current_umask();
- status = fh_fill_pre_attrs(fhp);
+ status = fh_fill_pre_attrs(dirfhp);
if (status != nfs_ok)
goto out;
- host_err = vfs_create(&nop_mnt_idmap, child, iap->ia_mode, NULL);
+ host_err = vfs_create(&nop_mnt_idmap, child, iattrs->ia_mode, NULL);
if (host_err < 0) {
status = nfserrno(host_err);
goto out;
}
- fh_fill_post_attrs(fhp);
+ fh_fill_post_attrs(dirfhp);
/* A newly created file already has a file size of zero. */
- if ((iap->ia_valid & ATTR_SIZE) && (iap->ia_size == 0))
- iap->ia_valid &= ~ATTR_SIZE;
- if (argp->createmode == EXCLUSIVE) {
- iap->ia_valid = ATTR_MTIME | ATTR_ATIME |
- ATTR_MTIME_SET | ATTR_ATIME_SET;
- iap->ia_mtime.tv_sec = v_mtime;
- iap->ia_atime.tv_sec = v_atime;
- iap->ia_mtime.tv_nsec = 0;
- iap->ia_atime.tv_nsec = 0;
+ if ((iattrs->ia_valid & ATTR_SIZE) && iattrs->ia_size == 0)
+ iattrs->ia_valid &= ~ATTR_SIZE;
+ if (how->mode == EXCLUSIVE) {
+ iattrs->ia_valid = ATTR_MTIME | ATTR_ATIME |
+ ATTR_MTIME_SET | ATTR_ATIME_SET;
+ iattrs->ia_mtime.tv_sec = v_mtime;
+ iattrs->ia_atime.tv_sec = v_atime;
+ iattrs->ia_mtime.tv_nsec = 0;
+ iattrs->ia_atime.tv_nsec = 0;
}
set_attr:
- status = nfsd_create_setattr(rqstp, fhp, resfhp, &attrs);
+ status = nfsd_create_setattr(rqstp, dirfhp, resfhp, &nattrs);
out:
end_creating(child);
out_write:
- fh_drop_write(fhp);
+ fh_drop_write(dirfhp);
return status;
}
-static __be32
-nfsd3_proc_create(struct svc_rqst *rqstp)
+/**
+ * nfsd3_proc_create - CREATE: Create a file
+ * @rqstp: RPC transaction context
+ *
+ * Return:
+ * %rpc_success: RPC executed successfully
+ *
+ * RPC synopsis:
+ * CREATE3res NFSPROC3_CREATE(CREATE3args) = 8;
+ */
+static __be32 nfsd3_proc_create(struct svc_rqst *rqstp)
{
- struct nfsd3_createargs *argp = rqstp->rq_argp;
- struct nfsd3_diropres *resp = rqstp->rq_resp;
- svc_fh *dirfhp, *newfhp;
+ struct CREATE3args_wrapper *argp = rqstp->rq_argp;
+ struct CREATE3res_wrapper *resp = rqstp->rq_resp;
+ struct svc_fh *dirfhp = &argp->fh;
+ struct svc_fh *fhp = &resp->fh;
- dirfhp = fh_copy(&resp->dirfh, &argp->fh);
- newfhp = fh_init(&resp->fh, NFS3_FHSIZE);
+ nfsd3_fh3_to_svc_fh(dirfhp, &argp->xdrgen.where.dir);
- resp->status = nfsd3_create_file(rqstp, dirfhp, newfhp, argp);
- resp->status = nfsd3_map_status(resp->status);
+ fh_init(fhp, NFS3_FHSIZE);
+ resp->xdrgen.status = nfsd3_create_file(rqstp, dirfhp, fhp, argp);
+
+ if (resp->xdrgen.status == nfs_ok) {
+ struct CREATE3resok *resok = &resp->xdrgen.u.resok;
+
+ nfsd3_fill_post_op_fh3(&resok->obj, fhp, resp->fh_data);
+ nfsd3_fill_post_op_attr(rqstp, &resok->obj_attributes, fhp);
+ nfsd3_fill_wcc_data(rqstp, &resok->dir_wcc, dirfhp);
+ } else {
+ struct CREATE3resfail *resfail = &resp->xdrgen.u.resfail;
+
+ resp->xdrgen.status = nfsd3_map_status(resp->xdrgen.status);
+ nfsd3_fill_wcc_data(rqstp, &resfail->dir_wcc, dirfhp);
+ }
+
+ fh_put(fhp);
+ fh_put(dirfhp);
return rpc_success;
}
@@ -1443,16 +1509,15 @@ static const struct svc_procedure nfsd_procedures3[22] = {
.pc_name = "WRITE",
},
[NFSPROC3_CREATE] = {
- .pc_func = nfsd3_proc_create,
- .pc_decode = nfs3svc_decode_createargs,
- .pc_encode = nfs3svc_encode_createres,
- .pc_release = nfs3svc_release_fhandle2,
- .pc_argsize = sizeof(struct nfsd3_createargs),
- .pc_argzero = sizeof(struct nfsd3_createargs),
- .pc_ressize = sizeof(struct nfsd3_diropres),
- .pc_cachetype = RC_REPLBUFF,
- .pc_xdrressize = ST+(1+FH+pAT)+WC,
- .pc_name = "CREATE",
+ .pc_func = nfsd3_proc_create,
+ .pc_decode = nfs_svc_decode_CREATE3args,
+ .pc_encode = nfs_svc_encode_CREATE3res,
+ .pc_argsize = sizeof(struct CREATE3args_wrapper),
+ .pc_argzero = 0,
+ .pc_ressize = sizeof(struct CREATE3res_wrapper),
+ .pc_cachetype = RC_REPLBUFF,
+ .pc_xdrressize = NFS3_CREATE3res_sz,
+ .pc_name = "CREATE",
},
[NFSPROC3_MKDIR] = {
.pc_func = nfsd3_proc_mkdir,
@@ -1623,7 +1688,8 @@ union nfsd3_xdrstore {
struct READ3args_wrapper readargs;
struct nfsd3_diropargs diropargs;
struct WRITE3args_wrapper writeargs;
- struct nfsd3_createargs createargs;
+ struct CREATE3args_wrapper createargs;
+ struct CREATE3res_wrapper createres;
struct nfsd3_renameargs renameargs;
struct nfsd3_linkargs linkargs;
struct nfsd3_symlinkargs symlinkargs;
diff --git a/fs/nfsd/nfs3xdr.c b/fs/nfsd/nfs3xdr.c
index cc3aebd97959..1bdfe5398031 100644
--- a/fs/nfsd/nfs3xdr.c
+++ b/fs/nfsd/nfs3xdr.c
@@ -490,30 +490,6 @@ nfs3svc_decode_diropargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
return svcxdr_decode_diropargs3(xdr, &args->fh, &args->name, &args->len);
}
-bool
-nfs3svc_decode_createargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
-{
- struct nfsd3_createargs *args = rqstp->rq_argp;
-
- if (!svcxdr_decode_diropargs3(xdr, &args->fh, &args->name, &args->len))
- return false;
- if (xdr_stream_decode_u32(xdr, &args->createmode) < 0)
- return false;
- switch (args->createmode) {
- case UNCHECKED:
- case GUARDED:
- return svcxdr_decode_sattr3(rqstp, xdr, &args->attrs);
- case EXCLUSIVE:
- args->verf = xdr_inline_decode(xdr, NFS3_CREATEVERFSIZE);
- if (!args->verf)
- return false;
- break;
- default:
- return false;
- }
- return true;
-}
-
bool
nfs3svc_decode_mkdirargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
diff --git a/fs/nfsd/xdr3.h b/fs/nfsd/xdr3.h
index 9c41244d27bb..30347cce5517 100644
--- a/fs/nfsd/xdr3.h
+++ b/fs/nfsd/xdr3.h
@@ -207,7 +207,6 @@ struct nfsd3_fhandle_pair {
bool nfs3svc_decode_fhandleargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs3svc_decode_diropargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
-bool nfs3svc_decode_createargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs3svc_decode_mkdirargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs3svc_decode_mknodargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs3svc_decode_renameargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
--
2.55.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v3 16/34] NFSD: Use xdrgen XDR functions for NFSv3 MKDIR procedure
2026-10-13 12:49 [PATCH v3 10/34] NFSD: Use xdrgen XDR functions for the NFSv3 LOOKUP procedure Chuck Lever
` (4 preceding siblings ...)
2026-10-13 12:49 ` [PATCH v3 15/34] NFSD: Use xdrgen XDR functions for NFSv3 CREATE procedure Chuck Lever
@ 2026-10-13 12:49 ` Chuck Lever
2026-10-13 12:49 ` [PATCH v3 17/34] NFSD: Use xdrgen XDR functions for NFSv3 SYMLINK procedure Chuck Lever
` (2 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Chuck Lever @ 2026-10-13 12:49 UTC (permalink / raw)
To: NeilBrown, Jeff Layton, Olga Kornievskaia, Dai Ngo, Tom Talpey; +Cc: linux-nfs
Replace the NFSPROC3_MKDIR entry in the nfsd_procedures3 array
with an entry that dispatches the xdrgen-generated
nfs_svc_decode_MKDIR3args and nfs_svc_encode_MKDIR3res. Wrapper
structures bridge the generated xdrgen types and the legacy
svc_fh and iattr representations the NFSD VFS layer still uses.
Filename validity is now checked by nfsd3_proc_mkdir() so that a
proper NFS3ERR status code can be returned to the client rather
than GARBAGE_ARGS.
The pc_argzero field is now set to zero for the NFSv3 MKDIR
procedure. The xdrgen decoders are trusted to initialize all
arguments in the argp->xdrgen field, making the early defensive
memset unnecessary. The remaining argp fields are cleared as
needed.
Because nfsd3_proc_mkdir() now fills in the post-op file handle,
the post-op attributes, and the WCC data, the fh_getattr() calls
are made in the proc function rather than in the XDR result
encoder, and the references to the file handles can be released
directly by nfsd3_proc_mkdir(). A separate ->pc_release callback
is thus no longer needed. This makes it straightforward to
confirm that the file handles are properly released during every
error flow.
nfs3svc_decode_mkdirargs() no longer has any callers, so it is
removed.
Signed-off-by: Chuck Lever <cel@kernel.org>
---
fs/nfsd/nfs3proc.c | 102 +++++++++++++++++++++++++++++++++------------
fs/nfsd/nfs3xdr.c | 10 -----
fs/nfsd/xdr3.h | 10 -----
3 files changed, 75 insertions(+), 47 deletions(-)
diff --git a/fs/nfsd/nfs3proc.c b/fs/nfsd/nfs3proc.c
index 6f7b5b1d7f20..94751f50248a 100644
--- a/fs/nfsd/nfs3proc.c
+++ b/fs/nfsd/nfs3proc.c
@@ -115,6 +115,22 @@ struct CREATE3res_wrapper {
static_assert(offsetof(struct CREATE3res_wrapper, xdrgen) == 0);
+struct MKDIR3args_wrapper {
+ struct MKDIR3args xdrgen;
+ struct svc_fh fh;
+ struct iattr iattrs;
+};
+
+static_assert(offsetof(struct MKDIR3args_wrapper, xdrgen) == 0);
+
+struct MKDIR3res_wrapper {
+ struct MKDIR3res xdrgen;
+ struct svc_fh fh;
+ u8 fh_data[NFS3_FHSIZE];
+};
+
+static_assert(offsetof(struct MKDIR3res_wrapper, xdrgen) == 0);
+
static int nfs3_ftypes[] = {
0, /* NF3NON */
S_IFREG, /* NF3REG */
@@ -983,29 +999,61 @@ static __be32 nfsd3_proc_create(struct svc_rqst *rqstp)
return rpc_success;
}
-/*
- * Make directory. This operation is not idempotent.
+/**
+ * nfsd3_proc_mkdir - MKDIR: Create a directory
+ * @rqstp: RPC transaction context
+ *
+ * Return:
+ * %rpc_success: RPC executed successfully
+ *
+ * RPC synopsis:
+ * MKDIR3res NFSPROC3_MKDIR(MKDIR3args) = 9;
*/
-static __be32
-nfsd3_proc_mkdir(struct svc_rqst *rqstp)
+static __be32 nfsd3_proc_mkdir(struct svc_rqst *rqstp)
{
- struct nfsd3_createargs *argp = rqstp->rq_argp;
- struct nfsd3_diropres *resp = rqstp->rq_resp;
- struct nfsd_attrs attrs = {
- .na_iattr = &argp->attrs,
+ struct MKDIR3args_wrapper *argp = rqstp->rq_argp;
+ struct diropargs3 *where = &argp->xdrgen.where;
+ struct MKDIR3res_wrapper *resp = rqstp->rq_resp;
+ struct iattr *iattrs = &argp->iattrs;
+ struct svc_fh *dirfhp = &argp->fh;
+ struct svc_fh *fhp = &resp->fh;
+ struct nfsd_attrs nattrs = {
+ .na_iattr = iattrs,
};
- argp->attrs.ia_valid &= ~ATTR_SIZE;
- fh_copy(&resp->dirfh, &argp->fh);
- fh_init(&resp->fh, NFS3_FHSIZE);
- if (!nfsd3_time_in_range(&argp->attrs)) {
- resp->status = nfserr_inval;
+ nfsd3_fh3_to_svc_fh(dirfhp, &where->dir);
+ resp->xdrgen.status = nfsd3_check_filename(where->name.data,
+ where->name.len);
+ if (resp->xdrgen.status != nfs_ok)
+ goto out;
+ nfsd3_sattr3_to_iattr(rqstp, iattrs, &argp->xdrgen.attributes);
+ if (!nfsd3_time_in_range(iattrs)) {
+ resp->xdrgen.status = nfserr_inval;
goto out;
}
- resp->status = nfsd_create(rqstp, &resp->dirfh, argp->name, argp->len,
- &attrs, S_IFDIR, 0, &resp->fh);
+
+ fh_init(fhp, NFS3_FHSIZE);
+ iattrs->ia_valid &= ~ATTR_SIZE;
+ resp->xdrgen.status = nfsd_create(rqstp, dirfhp, (char *)where->name.data,
+ where->name.len, &nattrs, S_IFDIR,
+ 0, fhp);
+
out:
- resp->status = nfsd3_map_status(resp->status);
+ if (resp->xdrgen.status == nfs_ok) {
+ struct MKDIR3resok *resok = &resp->xdrgen.u.resok;
+
+ nfsd3_fill_post_op_fh3(&resok->obj, fhp, resp->fh_data);
+ nfsd3_fill_post_op_attr(rqstp, &resok->obj_attributes, fhp);
+ nfsd3_fill_wcc_data(rqstp, &resok->dir_wcc, dirfhp);
+ } else {
+ struct MKDIR3resfail *resfail = &resp->xdrgen.u.resfail;
+
+ resp->xdrgen.status = nfsd3_map_status(resp->xdrgen.status);
+ nfsd3_fill_wcc_data(rqstp, &resfail->dir_wcc, dirfhp);
+ }
+
+ fh_put(fhp);
+ fh_put(dirfhp);
return rpc_success;
}
@@ -1405,7 +1453,6 @@ nfsd3_proc_commit(struct svc_rqst *rqstp)
* Only the results of non-idempotent operations are cached.
*/
#define nfs3svc_encode_wccstatres nfs3svc_encode_wccstat
-#define nfsd3_mkdirargs nfsd3_createargs
#define nfsd3_readdirplusargs nfsd3_readdirargs
#define nfsd3_fhandleargs nfsd_fhandle
#define nfsd3_wccstatres nfsd3_attrstat
@@ -1520,16 +1567,15 @@ static const struct svc_procedure nfsd_procedures3[22] = {
.pc_name = "CREATE",
},
[NFSPROC3_MKDIR] = {
- .pc_func = nfsd3_proc_mkdir,
- .pc_decode = nfs3svc_decode_mkdirargs,
- .pc_encode = nfs3svc_encode_createres,
- .pc_release = nfs3svc_release_fhandle2,
- .pc_argsize = sizeof(struct nfsd3_mkdirargs),
- .pc_argzero = sizeof(struct nfsd3_mkdirargs),
- .pc_ressize = sizeof(struct nfsd3_diropres),
- .pc_cachetype = RC_REPLBUFF,
- .pc_xdrressize = ST+(1+FH+pAT)+WC,
- .pc_name = "MKDIR",
+ .pc_func = nfsd3_proc_mkdir,
+ .pc_decode = nfs_svc_decode_MKDIR3args,
+ .pc_encode = nfs_svc_encode_MKDIR3res,
+ .pc_argsize = sizeof(struct MKDIR3args_wrapper),
+ .pc_argzero = 0,
+ .pc_ressize = sizeof(struct MKDIR3res_wrapper),
+ .pc_cachetype = RC_REPLBUFF,
+ .pc_xdrressize = NFS3_MKDIR3res_sz,
+ .pc_name = "MKDIR",
},
[NFSPROC3_SYMLINK] = {
.pc_func = nfsd3_proc_symlink,
@@ -1690,6 +1736,8 @@ union nfsd3_xdrstore {
struct WRITE3args_wrapper writeargs;
struct CREATE3args_wrapper createargs;
struct CREATE3res_wrapper createres;
+ struct MKDIR3args_wrapper mkdirargs;
+ struct MKDIR3res_wrapper mkdirres;
struct nfsd3_renameargs renameargs;
struct nfsd3_linkargs linkargs;
struct nfsd3_symlinkargs symlinkargs;
diff --git a/fs/nfsd/nfs3xdr.c b/fs/nfsd/nfs3xdr.c
index 1bdfe5398031..67cf76c13636 100644
--- a/fs/nfsd/nfs3xdr.c
+++ b/fs/nfsd/nfs3xdr.c
@@ -490,16 +490,6 @@ nfs3svc_decode_diropargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
return svcxdr_decode_diropargs3(xdr, &args->fh, &args->name, &args->len);
}
-bool
-nfs3svc_decode_mkdirargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
-{
- struct nfsd3_createargs *args = rqstp->rq_argp;
-
- return svcxdr_decode_diropargs3(xdr, &args->fh,
- &args->name, &args->len) &&
- svcxdr_decode_sattr3(rqstp, xdr, &args->attrs);
-}
-
bool
nfs3svc_decode_symlinkargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
diff --git a/fs/nfsd/xdr3.h b/fs/nfsd/xdr3.h
index 30347cce5517..c8d22a1be9d6 100644
--- a/fs/nfsd/xdr3.h
+++ b/fs/nfsd/xdr3.h
@@ -30,15 +30,6 @@ struct nfsd3_accessargs {
__u32 access;
};
-struct nfsd3_createargs {
- struct svc_fh fh;
- char * name;
- unsigned int len;
- int createmode;
- struct iattr attrs;
- __be32 * verf;
-};
-
struct nfsd3_mknodargs {
struct svc_fh fh;
char * name;
@@ -207,7 +198,6 @@ struct nfsd3_fhandle_pair {
bool nfs3svc_decode_fhandleargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs3svc_decode_diropargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
-bool nfs3svc_decode_mkdirargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs3svc_decode_mknodargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs3svc_decode_renameargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs3svc_decode_linkargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
--
2.55.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v3 17/34] NFSD: Use xdrgen XDR functions for NFSv3 SYMLINK procedure
2026-10-13 12:49 [PATCH v3 10/34] NFSD: Use xdrgen XDR functions for the NFSv3 LOOKUP procedure Chuck Lever
` (5 preceding siblings ...)
2026-10-13 12:49 ` [PATCH v3 16/34] NFSD: Use xdrgen XDR functions for NFSv3 MKDIR procedure Chuck Lever
@ 2026-10-13 12:49 ` Chuck Lever
2026-10-13 12:49 ` [PATCH v3 18/34] NFSD: Use xdrgen XDR functions for NFSv3 MKNOD procedure Chuck Lever
2026-10-13 12:49 ` [PATCH v3 19/34] NFSD: Use xdrgen XDR functions for the NFSv3 REMOVE procedure Chuck Lever
8 siblings, 0 replies; 10+ messages in thread
From: Chuck Lever @ 2026-10-13 12:49 UTC (permalink / raw)
To: NeilBrown, Jeff Layton, Olga Kornievskaia, Dai Ngo, Tom Talpey; +Cc: linux-nfs
Replace the NFSPROC3_SYMLINK entry in the nfsd_procedures3 array
with an entry that dispatches the xdrgen-generated
nfs_svc_decode_SYMLINK3args and nfs_svc_encode_SYMLINK3res. Wrapper
structures bridge the generated xdrgen types and the legacy
svc_fh and iattr representations the NFSD VFS layer still uses.
A SYMLINK request's target pathname can extend from the head of
the receive buffer into its page data. nfsd3_proc_symlink()
reassembles it from a head segment and a single page via
svc_fill_symlink_pathname(), which copies from one page and does
not account for a page offset. Annotate the symlink_data member of
symlinkdata3 with "pragma pages" so the generated decoder
represents the pathname as a struct xdr_buf and decodes it with
svcxdr_decode_opaque_payload(), which subsegments the receive
stream instead of pulling the string inline through the bounded
xdr scratch buffer. Copy the pathname out of that xdr_buf with
nfsd_symlink_target(), which walks the head, pages, and tail of
the subsegment. The NFSv2 SYMLINK procedure already uses that
helper, so move it into vfs.c where both procedures can reach it.
Filename validity is now checked by nfsd3_proc_symlink() so that a
proper NFS3ERR status code can be returned to the client rather
than GARBAGE_ARGS.
The pc_argzero field is now set to zero for the NFSv3 SYMLINK
procedure. The xdrgen decoders are trusted to initialize all
arguments in the argp->xdrgen field, making the early defensive
memset unnecessary. The remaining argp fields are cleared as
needed.
Because nfsd3_proc_symlink() now fills in the post-op file handle,
the post-op attributes, and the WCC data, the fh_getattr() calls
are made in the proc function rather than in the XDR result
encoder, and the references to the file handles can be released
directly by nfsd3_proc_symlink(). A separate ->pc_release callback
is thus no longer needed. This makes it straightforward to
confirm that the file handles are properly released during every
error flow.
nfs3svc_decode_symlinkargs() no longer has any callers, so it is
removed.
Signed-off-by: Chuck Lever <cel@kernel.org>
---
Documentation/sunrpc/xdr/nfs3.x | 1 +
fs/nfsd/nfs3proc.c | 120 +++++++++++++++++++++--------
fs/nfsd/nfs3xdr.c | 19 -----
fs/nfsd/nfs3xdr_gen.c | 6 +-
fs/nfsd/nfs3xdr_gen.h | 2 +-
fs/nfsd/nfsproc.c | 22 ------
fs/nfsd/vfs.c | 29 +++++++
fs/nfsd/vfs.h | 1 +
fs/nfsd/xdr3.h | 11 ---
include/linux/sunrpc/xdrgen/nfs3.h | 4 +-
10 files changed, 123 insertions(+), 92 deletions(-)
diff --git a/Documentation/sunrpc/xdr/nfs3.x b/Documentation/sunrpc/xdr/nfs3.x
index 61b87245be1e..c987788bf34c 100644
--- a/Documentation/sunrpc/xdr/nfs3.x
+++ b/Documentation/sunrpc/xdr/nfs3.x
@@ -464,6 +464,7 @@ struct symlinkdata3 {
sattr3 symlink_attributes;
nfspath3 symlink_data;
};
+pragma pages symlinkdata3 symlink_data;
struct SYMLINK3args {
diropargs3 where;
diff --git a/fs/nfsd/nfs3proc.c b/fs/nfsd/nfs3proc.c
index 94751f50248a..a068b9f157d7 100644
--- a/fs/nfsd/nfs3proc.c
+++ b/fs/nfsd/nfs3proc.c
@@ -131,6 +131,22 @@ struct MKDIR3res_wrapper {
static_assert(offsetof(struct MKDIR3res_wrapper, xdrgen) == 0);
+struct SYMLINK3args_wrapper {
+ struct SYMLINK3args xdrgen;
+ struct svc_fh fh;
+ struct iattr iattrs;
+};
+
+static_assert(offsetof(struct SYMLINK3args_wrapper, xdrgen) == 0);
+
+struct SYMLINK3res_wrapper {
+ struct SYMLINK3res xdrgen;
+ struct svc_fh fh;
+ u8 fh_data[NFS3_FHSIZE];
+};
+
+static_assert(offsetof(struct SYMLINK3res_wrapper, xdrgen) == 0);
+
static int nfs3_ftypes[] = {
0, /* NF3NON */
S_IFREG, /* NF3REG */
@@ -1057,43 +1073,79 @@ static __be32 nfsd3_proc_mkdir(struct svc_rqst *rqstp)
return rpc_success;
}
-static __be32
-nfsd3_proc_symlink(struct svc_rqst *rqstp)
+/**
+ * nfsd3_proc_symlink - SYMLINK: Create a symbolic link
+ * @rqstp: RPC transaction context
+ *
+ * Return:
+ * %rpc_success: RPC executed successfully
+ *
+ * RPC synopsis:
+ * SYMLINK3res NFSPROC3_SYMLINK(SYMLINK3args) = 10;
+ */
+static __be32 nfsd3_proc_symlink(struct svc_rqst *rqstp)
{
- struct nfsd3_symlinkargs *argp = rqstp->rq_argp;
- struct nfsd3_diropres *resp = rqstp->rq_resp;
- struct nfsd_attrs attrs = {
- .na_iattr = &argp->attrs,
+ struct SYMLINK3args_wrapper *argp = rqstp->rq_argp;
+ struct symlinkdata3 *symlink = &argp->xdrgen.symlink;
+ struct xdr_buf *to = &symlink->symlink_data;
+ struct diropargs3 *where = &argp->xdrgen.where;
+ struct SYMLINK3res_wrapper *resp = rqstp->rq_resp;
+ struct iattr *iattrs = &argp->iattrs;
+ struct svc_fh *dirfhp = &argp->fh;
+ struct svc_fh *fhp = &resp->fh;
+ struct nfsd_attrs nattrs = {
+ .na_iattr = iattrs,
};
+ char *tname;
- if (!nfsd3_time_in_range(&argp->attrs)) {
- resp->status = nfserr_inval;
+ nfsd3_fh3_to_svc_fh(dirfhp, &where->dir);
+ resp->xdrgen.status = nfsd3_check_filename(where->name.data,
+ where->name.len);
+ if (resp->xdrgen.status != nfs_ok)
+ goto out;
+ nfsd3_sattr3_to_iattr(rqstp, iattrs, &symlink->symlink_attributes);
+ if (!nfsd3_time_in_range(iattrs)) {
+ resp->xdrgen.status = nfserr_inval;
goto out;
}
- if (argp->tlen == 0) {
- resp->status = nfserr_inval;
+ if (to->len == 0) {
+ resp->xdrgen.status = nfserr_inval;
goto out;
}
- if (argp->tlen > NFS3_MAXPATHLEN) {
- resp->status = nfserr_nametoolong;
+ if (to->len > NFS3_MAXPATHLEN) {
+ resp->xdrgen.status = nfserr_nametoolong;
goto out;
}
- argp->tname = svc_fill_symlink_pathname(rqstp, &argp->first,
- page_address(rqstp->rq_arg.pages[0]),
- argp->tlen);
- if (IS_ERR(argp->tname)) {
- resp->status = nfserrno(PTR_ERR(argp->tname));
+ tname = nfsd_symlink_target(to);
+ if (IS_ERR(tname)) {
+ resp->xdrgen.status = nfserrno(PTR_ERR(tname));
goto out;
}
- fh_copy(&resp->dirfh, &argp->ffh);
- fh_init(&resp->fh, NFS3_FHSIZE);
- resp->status = nfsd_symlink(rqstp, &resp->dirfh, argp->fname,
- argp->flen, argp->tname, &attrs, &resp->fh);
- kfree(argp->tname);
+ fh_init(fhp, NFS3_FHSIZE);
+ resp->xdrgen.status = nfsd_symlink(rqstp, dirfhp,
+ (char *)where->name.data,
+ where->name.len, tname,
+ &nattrs, fhp);
+ kfree(tname);
+
out:
- resp->status = nfsd3_map_status(resp->status);
+ if (resp->xdrgen.status == nfs_ok) {
+ struct SYMLINK3resok *resok = &resp->xdrgen.u.resok;
+
+ nfsd3_fill_post_op_fh3(&resok->obj, fhp, resp->fh_data);
+ nfsd3_fill_post_op_attr(rqstp, &resok->obj_attributes, fhp);
+ nfsd3_fill_wcc_data(rqstp, &resok->dir_wcc, dirfhp);
+ } else {
+ struct SYMLINK3resfail *resfail = &resp->xdrgen.u.resfail;
+
+ resp->xdrgen.status = nfsd3_map_status(resp->xdrgen.status);
+ nfsd3_fill_wcc_data(rqstp, &resfail->dir_wcc, dirfhp);
+ }
+
+ fh_put(fhp);
+ fh_put(dirfhp);
return rpc_success;
}
@@ -1578,16 +1630,15 @@ static const struct svc_procedure nfsd_procedures3[22] = {
.pc_name = "MKDIR",
},
[NFSPROC3_SYMLINK] = {
- .pc_func = nfsd3_proc_symlink,
- .pc_decode = nfs3svc_decode_symlinkargs,
- .pc_encode = nfs3svc_encode_createres,
- .pc_release = nfs3svc_release_fhandle2,
- .pc_argsize = sizeof(struct nfsd3_symlinkargs),
- .pc_argzero = sizeof(struct nfsd3_symlinkargs),
- .pc_ressize = sizeof(struct nfsd3_diropres),
- .pc_cachetype = RC_REPLBUFF,
- .pc_xdrressize = ST+(1+FH+pAT)+WC,
- .pc_name = "SYMLINK",
+ .pc_func = nfsd3_proc_symlink,
+ .pc_decode = nfs_svc_decode_SYMLINK3args,
+ .pc_encode = nfs_svc_encode_SYMLINK3res,
+ .pc_argsize = sizeof(struct SYMLINK3args_wrapper),
+ .pc_argzero = 0,
+ .pc_ressize = sizeof(struct SYMLINK3res_wrapper),
+ .pc_cachetype = RC_REPLBUFF,
+ .pc_xdrressize = NFS3_SYMLINK3res_sz,
+ .pc_name = "SYMLINK",
},
[NFSPROC3_MKNOD] = {
.pc_func = nfsd3_proc_mknod,
@@ -1738,9 +1789,10 @@ union nfsd3_xdrstore {
struct CREATE3res_wrapper createres;
struct MKDIR3args_wrapper mkdirargs;
struct MKDIR3res_wrapper mkdirres;
+ struct SYMLINK3args_wrapper symlinkargs;
+ struct SYMLINK3res_wrapper symlinkres;
struct nfsd3_renameargs renameargs;
struct nfsd3_linkargs linkargs;
- struct nfsd3_symlinkargs symlinkargs;
struct nfsd3_readdirargs readdirargs;
struct nfsd3_diropres diropres;
struct READLINK3res readlinkres;
diff --git a/fs/nfsd/nfs3xdr.c b/fs/nfsd/nfs3xdr.c
index 67cf76c13636..68f00ff604be 100644
--- a/fs/nfsd/nfs3xdr.c
+++ b/fs/nfsd/nfs3xdr.c
@@ -490,25 +490,6 @@ nfs3svc_decode_diropargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
return svcxdr_decode_diropargs3(xdr, &args->fh, &args->name, &args->len);
}
-bool
-nfs3svc_decode_symlinkargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
-{
- struct nfsd3_symlinkargs *args = rqstp->rq_argp;
- struct kvec *head = rqstp->rq_arg.head;
-
- if (!svcxdr_decode_diropargs3(xdr, &args->ffh, &args->fname, &args->flen))
- return false;
- if (!svcxdr_decode_sattr3(rqstp, xdr, &args->attrs))
- return false;
- if (xdr_stream_decode_u32(xdr, &args->tlen) < 0)
- return false;
-
- /* symlink_data */
- args->first.iov_len = head->iov_len - xdr_stream_pos(xdr);
- args->first.iov_base = xdr_inline_decode(xdr, args->tlen);
- return args->first.iov_base != NULL;
-}
-
bool
nfs3svc_decode_mknodargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
diff --git a/fs/nfsd/nfs3xdr_gen.c b/fs/nfsd/nfs3xdr_gen.c
index 159aff1a8567..3667c160e789 100644
--- a/fs/nfsd/nfs3xdr_gen.c
+++ b/fs/nfsd/nfs3xdr_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/nfs3.x
-// XDR specification modification time: Tue Jul 14 11:43:26 2026
+// XDR specification modification time: Tue Jul 14 12:13:57 2026
#include <linux/sunrpc/svc.h>
@@ -915,7 +915,7 @@ xdrgen_decode_symlinkdata3(struct xdr_stream *xdr, struct symlinkdata3 *ptr)
{
if (!xdrgen_decode_sattr3(xdr, &ptr->symlink_attributes))
return false;
- if (!xdrgen_decode_nfspath3(xdr, &ptr->symlink_data))
+ if (!svcxdr_decode_opaque_payload(xdr, &ptr->symlink_data, 0))
return false;
return true;
}
@@ -2748,7 +2748,7 @@ xdrgen_encode_symlinkdata3(struct xdr_stream *xdr, const struct symlinkdata3 *va
{
if (!xdrgen_encode_sattr3(xdr, &value->symlink_attributes))
return false;
- if (!xdrgen_encode_nfspath3(xdr, value->symlink_data))
+ if (!svcxdr_encode_opaque_payload(xdr, value->symlink_data.len))
return false;
return true;
}
diff --git a/fs/nfsd/nfs3xdr_gen.h b/fs/nfsd/nfs3xdr_gen.h
index 98e29c8efc5e..f336ffa92a7a 100644
--- a/fs/nfsd/nfs3xdr_gen.h
+++ b/fs/nfsd/nfs3xdr_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/nfs3.x */
-/* XDR specification modification time: Tue Jul 14 11:43:26 2026 */
+/* XDR specification modification time: Tue Jul 14 12:13:57 2026 */
#ifndef _LINUX_XDRGEN_NFS3_DECL_H
#define _LINUX_XDRGEN_NFS3_DECL_H
diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c
index 1f74008bc06a..39e5caa8e286 100644
--- a/fs/nfsd/nfsproc.c
+++ b/fs/nfsd/nfsproc.c
@@ -968,28 +968,6 @@ static __be32 nfsd_proc_link(struct svc_rqst *rqstp)
return rpc_success;
}
-static char *nfsd_symlink_target(const struct xdr_buf *to)
-{
- char *result;
-
- result = kmalloc(to->len + 1, GFP_KERNEL);
- if (!result)
- return ERR_PTR(-ESERVERFAULT);
- /* The target can span the receive buffer's head, pages, and tail */
- if (read_bytes_from_xdr_buf(to, 0, result, to->len)) {
- kfree(result);
- return ERR_PTR(-ESERVERFAULT);
- }
- result[to->len] = '\0';
-
- /* The VFS rejects a pathname that contains a NUL byte */
- if (strlen(result) != to->len) {
- kfree(result);
- return ERR_PTR(-EINVAL);
- }
- return result;
-}
-
/**
* nfsd_proc_symlink - SYMLINK: Create a symbolic link
* @rqstp: RPC transaction context
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index c753d3257f1e..f4069da85372 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -1953,6 +1953,35 @@ nfsd_readlink(struct svc_rqst *rqstp, struct svc_fh *fhp, char *buf, int *lenp)
return 0;
}
+/**
+ * nfsd_symlink_target - Copy a SYMLINK target out of the receive buffer
+ * @to: subsegment of the receive buffer that holds the target pathname
+ *
+ * Return: a NUL-terminated pathname the caller frees with kfree(),
+ * or an ERR_PTR holding a negative errno.
+ */
+char *nfsd_symlink_target(const struct xdr_buf *to)
+{
+ char *result;
+
+ result = kmalloc(to->len + 1, GFP_KERNEL);
+ if (!result)
+ return ERR_PTR(-ESERVERFAULT);
+ /* The target can span the receive buffer's head, pages, and tail */
+ if (read_bytes_from_xdr_buf(to, 0, result, to->len)) {
+ kfree(result);
+ return ERR_PTR(-ESERVERFAULT);
+ }
+ result[to->len] = '\0';
+
+ /* The VFS rejects a pathname that contains a NUL byte */
+ if (strlen(result) != to->len) {
+ kfree(result);
+ return ERR_PTR(-EINVAL);
+ }
+ return result;
+}
+
/**
* nfsd_symlink - Create a symlink and look up its inode
* @rqstp: RPC transaction being executed
diff --git a/fs/nfsd/vfs.h b/fs/nfsd/vfs.h
index 8c130473aa79..eb75e4c82318 100644
--- a/fs/nfsd/vfs.h
+++ b/fs/nfsd/vfs.h
@@ -197,6 +197,7 @@ __be32 nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp,
__be32 *verf);
__be32 nfsd_readlink(struct svc_rqst *, struct svc_fh *,
char *, int *);
+char *nfsd_symlink_target(const struct xdr_buf *to);
__be32 nfsd_symlink(struct svc_rqst *, struct svc_fh *,
char *name, int len, char *path,
struct nfsd_attrs *attrs,
diff --git a/fs/nfsd/xdr3.h b/fs/nfsd/xdr3.h
index c8d22a1be9d6..43ded5cff892 100644
--- a/fs/nfsd/xdr3.h
+++ b/fs/nfsd/xdr3.h
@@ -55,16 +55,6 @@ struct nfsd3_linkargs {
unsigned int tlen;
};
-struct nfsd3_symlinkargs {
- struct svc_fh ffh;
- char * fname;
- unsigned int flen;
- char * tname;
- unsigned int tlen;
- struct iattr attrs;
- struct kvec first;
-};
-
struct nfsd3_readdirargs {
struct svc_fh fh;
__u64 cookie;
@@ -201,7 +191,6 @@ bool nfs3svc_decode_diropargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs3svc_decode_mknodargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs3svc_decode_renameargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs3svc_decode_linkargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
-bool nfs3svc_decode_symlinkargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs3svc_decode_readdirargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs3svc_decode_readdirplusargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs3svc_decode_commitargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
diff --git a/include/linux/sunrpc/xdrgen/nfs3.h b/include/linux/sunrpc/xdrgen/nfs3.h
index 3b6738c0074b..3e24abb1d2e5 100644
--- a/include/linux/sunrpc/xdrgen/nfs3.h
+++ b/include/linux/sunrpc/xdrgen/nfs3.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/nfs3.x */
-/* XDR specification modification time: Tue Jul 14 11:43:26 2026 */
+/* XDR specification modification time: Tue Jul 14 12:13:57 2026 */
#ifndef _LINUX_XDRGEN_NFS3_DEF_H
#define _LINUX_XDRGEN_NFS3_DEF_H
@@ -468,7 +468,7 @@ struct MKDIR3res {
struct symlinkdata3 {
struct sattr3 symlink_attributes;
- nfspath3 symlink_data;
+ struct xdr_buf symlink_data;
};
struct SYMLINK3args {
--
2.55.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v3 18/34] NFSD: Use xdrgen XDR functions for NFSv3 MKNOD procedure
2026-10-13 12:49 [PATCH v3 10/34] NFSD: Use xdrgen XDR functions for the NFSv3 LOOKUP procedure Chuck Lever
` (6 preceding siblings ...)
2026-10-13 12:49 ` [PATCH v3 17/34] NFSD: Use xdrgen XDR functions for NFSv3 SYMLINK procedure Chuck Lever
@ 2026-10-13 12:49 ` Chuck Lever
2026-10-13 12:49 ` [PATCH v3 19/34] NFSD: Use xdrgen XDR functions for the NFSv3 REMOVE procedure Chuck Lever
8 siblings, 0 replies; 10+ messages in thread
From: Chuck Lever @ 2026-10-13 12:49 UTC (permalink / raw)
To: NeilBrown, Jeff Layton, Olga Kornievskaia, Dai Ngo, Tom Talpey; +Cc: linux-nfs
Replace the NFSPROC3_MKNOD entry in the nfsd_procedures3 array
with an entry that dispatches the xdrgen-generated
nfs_svc_decode_MKNOD3args and nfs_svc_encode_MKNOD3res. Wrapper
structures bridge the generated xdrgen types and the legacy
svc_fh and iattr representations the NFSD VFS layer still uses.
Filename validity is now checked by nfsd3_proc_mknod() so that a
proper NFS3ERR status code can be returned to the client rather
than GARBAGE_ARGS.
The pc_argzero field is now set to zero for the NFSv3 MKNOD
procedure. The xdrgen decoders are trusted to initialize all
arguments in the argp->xdrgen field, making the early defensive
memset unnecessary. The remaining argp fields are cleared as
needed.
Because nfsd3_proc_mknod() now fills in the post-op file handle,
the post-op attributes, and the WCC data, the fh_getattr() calls
are made in the proc function rather than in the XDR result
encoder, and the references to the file handles can be released
directly by nfsd3_proc_mknod(). A separate ->pc_release callback
is thus no longer needed. This makes it straightforward to
confirm that the file handles are properly released during every
error flow.
nfs3svc_decode_mknodargs() and nfs3svc_encode_createres() no
longer have any callers, so they are removed. MKNOD was the last
consumer of the hand-coded sattr3 decoder, so svcxdr_decode_sattr3()
and its helpers svcxdr_decode_nfstime3(), svcxdr_decode_specdata3(),
and svcxdr_decode_devicedata3() are removed as well.
Signed-off-by: Chuck Lever <cel@kernel.org>
---
fs/nfsd/nfs3proc.c | 154 +++++++++++++++++++++++++++------------
fs/nfsd/nfs3xdr.c | 175 ---------------------------------------------
fs/nfsd/xdr3.h | 18 -----
3 files changed, 107 insertions(+), 240 deletions(-)
diff --git a/fs/nfsd/nfs3proc.c b/fs/nfsd/nfs3proc.c
index a068b9f157d7..d878391e63f8 100644
--- a/fs/nfsd/nfs3proc.c
+++ b/fs/nfsd/nfs3proc.c
@@ -147,17 +147,37 @@ struct SYMLINK3res_wrapper {
static_assert(offsetof(struct SYMLINK3res_wrapper, xdrgen) == 0);
-static int nfs3_ftypes[] = {
- 0, /* NF3NON */
- S_IFREG, /* NF3REG */
- S_IFDIR, /* NF3DIR */
- S_IFBLK, /* NF3BLK */
- S_IFCHR, /* NF3CHR */
- S_IFLNK, /* NF3LNK */
- S_IFSOCK, /* NF3SOCK */
- S_IFIFO, /* NF3FIFO */
+struct MKNOD3args_wrapper {
+ struct MKNOD3args xdrgen;
+ struct svc_fh fh;
+ struct iattr iattrs;
};
+static_assert(offsetof(struct MKNOD3args_wrapper, xdrgen) == 0);
+
+struct MKNOD3res_wrapper {
+ struct MKNOD3res xdrgen;
+ struct svc_fh fh;
+ u8 fh_data[NFS3_FHSIZE];
+};
+
+static_assert(offsetof(struct MKNOD3res_wrapper, xdrgen) == 0);
+
+static int nfsd3_ftype3_to_mode(ftype3 ftype)
+{
+ switch (ftype) {
+ case NF3REG: return S_IFREG;
+ case NF3DIR: return S_IFDIR;
+ case NF3BLK: return S_IFBLK;
+ case NF3CHR: return S_IFCHR;
+ case NF3LNK: return S_IFLNK;
+ case NF3SOCK: return S_IFSOCK;
+ case NF3FIFO: return S_IFIFO;
+ default:
+ return 0;
+ }
+}
+
/*
* Reject a client-supplied atime or mtime whose nanoseconds field is out
* of range. Such a value is well-formed on the wire but is not a valid
@@ -1149,45 +1169,86 @@ static __be32 nfsd3_proc_symlink(struct svc_rqst *rqstp)
return rpc_success;
}
-/*
- * Make socket/fifo/device.
+/**
+ * nfsd3_proc_mknod - MKNOD: Create a special file
+ * @rqstp: RPC transaction context
+ *
+ * Return:
+ * %rpc_success: RPC executed successfully
+ *
+ * RPC synopsis:
+ * MKNOD3res NFSPROC3_MKNOD(MKNOD3args) = 11;
*/
-static __be32
-nfsd3_proc_mknod(struct svc_rqst *rqstp)
+static __be32 nfsd3_proc_mknod(struct svc_rqst *rqstp)
{
- struct nfsd3_mknodargs *argp = rqstp->rq_argp;
- struct nfsd3_diropres *resp = rqstp->rq_resp;
- struct nfsd_attrs attrs = {
- .na_iattr = &argp->attrs,
+ struct MKNOD3args_wrapper *argp = rqstp->rq_argp;
+ struct diropargs3 *where = &argp->xdrgen.where;
+ struct mknoddata3 *what = &argp->xdrgen.what;
+ struct MKNOD3res_wrapper *resp = rqstp->rq_resp;
+ struct iattr *iattrs = &argp->iattrs;
+ struct svc_fh *dirfhp = &argp->fh;
+ struct svc_fh *fhp = &resp->fh;
+ struct nfsd_attrs nattrs = {
+ .na_iattr = iattrs,
};
- int type;
- dev_t rdev = 0;
+ dev_t rdev = 0;
- fh_copy(&resp->dirfh, &argp->fh);
- fh_init(&resp->fh, NFS3_FHSIZE);
-
- if (argp->ftype == NF3CHR || argp->ftype == NF3BLK) {
- rdev = MKDEV(argp->major, argp->minor);
- if (MAJOR(rdev) != argp->major ||
- MINOR(rdev) != argp->minor) {
- resp->status = nfserr_inval;
+ nfsd3_fh3_to_svc_fh(dirfhp, &where->dir);
+ resp->xdrgen.status = nfsd3_check_filename(where->name.data,
+ where->name.len);
+ if (resp->xdrgen.status != nfs_ok)
+ goto out;
+ switch (what->type) {
+ case NF3CHR:
+ case NF3BLK:
+ rdev = MKDEV(what->u.device.spec.specdata1,
+ what->u.device.spec.specdata2);
+ if (MAJOR(rdev) != what->u.device.spec.specdata1 ||
+ MINOR(rdev) != what->u.device.spec.specdata2) {
+ resp->xdrgen.status = nfserr_inval;
goto out;
}
- } else if (argp->ftype != NF3SOCK && argp->ftype != NF3FIFO) {
- resp->status = nfserr_badtype;
+ nfsd3_sattr3_to_iattr(rqstp, iattrs,
+ &what->u.device.dev_attributes);
+ break;
+ case NF3SOCK:
+ case NF3FIFO:
+ nfsd3_sattr3_to_iattr(rqstp, iattrs,
+ &what->u.pipe_attributes);
+ break;
+ default:
+ resp->xdrgen.status = nfserr_badtype;
goto out;
}
- if (!nfsd3_time_in_range(&argp->attrs)) {
- resp->status = nfserr_inval;
+ if (!nfsd3_time_in_range(iattrs)) {
+ resp->xdrgen.status = nfserr_inval;
goto out;
}
- type = nfs3_ftypes[argp->ftype];
- resp->status = nfsd_create(rqstp, &resp->dirfh, argp->name, argp->len,
- &attrs, type, rdev, &resp->fh);
+ fh_init(fhp, NFS3_FHSIZE);
+ resp->xdrgen.status = nfsd_create(rqstp, dirfhp,
+ (char *)where->name.data,
+ where->name.len, &nattrs,
+ nfsd3_ftype3_to_mode(what->type),
+ rdev, fhp);
+
out:
- resp->status = nfsd3_map_status(resp->status);
+ if (resp->xdrgen.status == nfs_ok) {
+ struct MKNOD3resok *resok = &resp->xdrgen.u.resok;
+
+ nfsd3_fill_post_op_fh3(&resok->obj, fhp, resp->fh_data);
+ nfsd3_fill_post_op_attr(rqstp, &resok->obj_attributes, fhp);
+ nfsd3_fill_wcc_data(rqstp, &resok->dir_wcc, dirfhp);
+ } else {
+ struct MKNOD3resfail *resfail = &resp->xdrgen.u.resfail;
+
+ resp->xdrgen.status = nfsd3_map_status(resp->xdrgen.status);
+ nfsd3_fill_wcc_data(rqstp, &resfail->dir_wcc, dirfhp);
+ }
+
+ fh_put(fhp);
+ fh_put(dirfhp);
return rpc_success;
}
@@ -1510,7 +1571,6 @@ nfsd3_proc_commit(struct svc_rqst *rqstp)
#define nfsd3_wccstatres nfsd3_attrstat
#define ST 1 /* status*/
-#define FH 17 /* filehandle with length */
#define AT 21 /* attributes */
#define pAT (1+AT) /* post attributes - conditional */
#define WC (7+pAT) /* WCC attributes */
@@ -1641,16 +1701,15 @@ static const struct svc_procedure nfsd_procedures3[22] = {
.pc_name = "SYMLINK",
},
[NFSPROC3_MKNOD] = {
- .pc_func = nfsd3_proc_mknod,
- .pc_decode = nfs3svc_decode_mknodargs,
- .pc_encode = nfs3svc_encode_createres,
- .pc_release = nfs3svc_release_fhandle2,
- .pc_argsize = sizeof(struct nfsd3_mknodargs),
- .pc_argzero = sizeof(struct nfsd3_mknodargs),
- .pc_ressize = sizeof(struct nfsd3_diropres),
- .pc_cachetype = RC_REPLBUFF,
- .pc_xdrressize = ST+(1+FH+pAT)+WC,
- .pc_name = "MKNOD",
+ .pc_func = nfsd3_proc_mknod,
+ .pc_decode = nfs_svc_decode_MKNOD3args,
+ .pc_encode = nfs_svc_encode_MKNOD3res,
+ .pc_argsize = sizeof(struct MKNOD3args_wrapper),
+ .pc_argzero = 0,
+ .pc_ressize = sizeof(struct MKNOD3res_wrapper),
+ .pc_cachetype = RC_REPLBUFF,
+ .pc_xdrressize = NFS3_MKNOD3res_sz,
+ .pc_name = "MKNOD",
},
[NFSPROC3_REMOVE] = {
.pc_func = nfsd3_proc_remove,
@@ -1791,10 +1850,11 @@ union nfsd3_xdrstore {
struct MKDIR3res_wrapper mkdirres;
struct SYMLINK3args_wrapper symlinkargs;
struct SYMLINK3res_wrapper symlinkres;
+ struct MKNOD3args_wrapper mknodargs;
+ struct MKNOD3res_wrapper mknodres;
struct nfsd3_renameargs renameargs;
struct nfsd3_linkargs linkargs;
struct nfsd3_readdirargs readdirargs;
- struct nfsd3_diropres diropres;
struct READLINK3res readlinkres;
struct READ3res readres;
struct nfsd3_writeres writeres;
diff --git a/fs/nfsd/nfs3xdr.c b/fs/nfsd/nfs3xdr.c
index 68f00ff604be..53a82af80ad5 100644
--- a/fs/nfsd/nfs3xdr.c
+++ b/fs/nfsd/nfs3xdr.c
@@ -57,20 +57,6 @@ encode_nfstime3(__be32 *p, const struct timespec64 *time)
return p;
}
-static bool
-svcxdr_decode_nfstime3(struct xdr_stream *xdr, struct timespec64 *timep)
-{
- __be32 *p;
-
- p = xdr_inline_decode(xdr, XDR_UNIT * 2);
- if (!p)
- return false;
- timep->tv_sec = be32_to_cpup(p++);
- timep->tv_nsec = be32_to_cpup(p);
-
- return true;
-}
-
/**
* svcxdr_decode_nfs_fh3 - Decode an NFSv3 file handle
* @xdr: XDR stream positioned at an undecoded NFSv3 FH
@@ -209,114 +195,6 @@ svcxdr_decode_diropargs3(struct xdr_stream *xdr, struct svc_fh *fhp,
svcxdr_decode_filename3(xdr, name, len);
}
-static bool
-svcxdr_decode_sattr3(struct svc_rqst *rqstp, struct xdr_stream *xdr,
- struct iattr *iap)
-{
- u32 set_it;
-
- iap->ia_valid = 0;
-
- if (xdr_stream_decode_bool(xdr, &set_it) < 0)
- return false;
- if (set_it) {
- u32 mode;
-
- if (xdr_stream_decode_u32(xdr, &mode) < 0)
- return false;
- iap->ia_valid |= ATTR_MODE;
- iap->ia_mode = mode;
- }
- if (xdr_stream_decode_bool(xdr, &set_it) < 0)
- return false;
- if (set_it) {
- u32 uid;
-
- if (xdr_stream_decode_u32(xdr, &uid) < 0)
- return false;
- iap->ia_uid = make_kuid(nfsd_user_namespace(rqstp), uid);
- if (uid_valid(iap->ia_uid))
- iap->ia_valid |= ATTR_UID;
- }
- if (xdr_stream_decode_bool(xdr, &set_it) < 0)
- return false;
- if (set_it) {
- u32 gid;
-
- if (xdr_stream_decode_u32(xdr, &gid) < 0)
- return false;
- iap->ia_gid = make_kgid(nfsd_user_namespace(rqstp), gid);
- if (gid_valid(iap->ia_gid))
- iap->ia_valid |= ATTR_GID;
- }
- if (xdr_stream_decode_bool(xdr, &set_it) < 0)
- return false;
- if (set_it) {
- u64 newsize;
-
- if (xdr_stream_decode_u64(xdr, &newsize) < 0)
- return false;
- iap->ia_valid |= ATTR_SIZE;
- iap->ia_size = newsize;
- }
- if (xdr_stream_decode_u32(xdr, &set_it) < 0)
- return false;
- switch (set_it) {
- case DONT_CHANGE:
- break;
- case SET_TO_SERVER_TIME:
- iap->ia_valid |= ATTR_ATIME;
- break;
- case SET_TO_CLIENT_TIME:
- if (!svcxdr_decode_nfstime3(xdr, &iap->ia_atime))
- return false;
- iap->ia_valid |= ATTR_ATIME | ATTR_ATIME_SET;
- break;
- default:
- return false;
- }
- if (xdr_stream_decode_u32(xdr, &set_it) < 0)
- return false;
- switch (set_it) {
- case DONT_CHANGE:
- break;
- case SET_TO_SERVER_TIME:
- iap->ia_valid |= ATTR_MTIME;
- break;
- case SET_TO_CLIENT_TIME:
- if (!svcxdr_decode_nfstime3(xdr, &iap->ia_mtime))
- return false;
- iap->ia_valid |= ATTR_MTIME | ATTR_MTIME_SET;
- break;
- default:
- return false;
- }
-
- return true;
-}
-
-static bool
-svcxdr_decode_specdata3(struct xdr_stream *xdr, struct nfsd3_mknodargs *args)
-{
- __be32 *p;
-
- p = xdr_inline_decode(xdr, XDR_UNIT * 2);
- if (!p)
- return false;
- args->major = be32_to_cpup(p++);
- args->minor = be32_to_cpup(p);
-
- return true;
-}
-
-static bool
-svcxdr_decode_devicedata3(struct svc_rqst *rqstp, struct xdr_stream *xdr,
- struct nfsd3_mknodargs *args)
-{
- return svcxdr_decode_sattr3(rqstp, xdr, &args->attrs) &&
- svcxdr_decode_specdata3(xdr, args);
-}
-
static bool
svcxdr_encode_fattr3(struct svc_rqst *rqstp, struct xdr_stream *xdr,
const struct svc_fh *fhp, const struct kstat *stat)
@@ -490,34 +368,6 @@ nfs3svc_decode_diropargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
return svcxdr_decode_diropargs3(xdr, &args->fh, &args->name, &args->len);
}
-bool
-nfs3svc_decode_mknodargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
-{
- struct nfsd3_mknodargs *args = rqstp->rq_argp;
-
- if (!svcxdr_decode_diropargs3(xdr, &args->fh, &args->name, &args->len))
- return false;
- if (xdr_stream_decode_u32(xdr, &args->ftype) < 0)
- return false;
- switch (args->ftype) {
- case NF3CHR:
- case NF3BLK:
- return svcxdr_decode_devicedata3(rqstp, xdr, args);
- case NF3SOCK:
- case NF3FIFO:
- return svcxdr_decode_sattr3(rqstp, xdr, &args->attrs);
- case NF3REG:
- case NF3DIR:
- case NF3LNK:
- /* Valid XDR but illegal file types */
- break;
- default:
- return false;
- }
-
- return true;
-}
-
bool
nfs3svc_decode_renameargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
{
@@ -608,31 +458,6 @@ nfs3svc_encode_wccstat(struct svc_rqst *rqstp, struct xdr_stream *xdr)
svcxdr_encode_wcc_data(rqstp, xdr, &resp->fh);
}
-/* CREATE, MKDIR, SYMLINK, MKNOD */
-bool
-nfs3svc_encode_createres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
-{
- struct nfsd3_diropres *resp = rqstp->rq_resp;
-
- if (!svcxdr_encode_nfsstat3(xdr, resp->status))
- return false;
- switch (resp->status) {
- case nfs_ok:
- if (!svcxdr_encode_post_op_fh3(xdr, &resp->fh))
- return false;
- if (!svcxdr_encode_post_op_attr(rqstp, xdr, &resp->fh))
- return false;
- if (!svcxdr_encode_wcc_data(rqstp, xdr, &resp->dirfh))
- return false;
- break;
- default:
- if (!svcxdr_encode_wcc_data(rqstp, xdr, &resp->dirfh))
- return false;
- }
-
- return true;
-}
-
/* RENAME */
bool
nfs3svc_encode_renameres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
diff --git a/fs/nfsd/xdr3.h b/fs/nfsd/xdr3.h
index 43ded5cff892..6d5cea6baf3a 100644
--- a/fs/nfsd/xdr3.h
+++ b/fs/nfsd/xdr3.h
@@ -30,15 +30,6 @@ struct nfsd3_accessargs {
__u32 access;
};
-struct nfsd3_mknodargs {
- struct svc_fh fh;
- char * name;
- unsigned int len;
- __u32 ftype;
- __u32 major, minor;
- struct iattr attrs;
-};
-
struct nfsd3_renameargs {
struct svc_fh ffh;
char * fname;
@@ -87,13 +78,6 @@ struct nfsd3_attrstat {
struct kstat stat;
};
-/* LOOKUP, CREATE, MKDIR, SYMLINK, MKNOD */
-struct nfsd3_diropres {
- __be32 status;
- struct svc_fh dirfh;
- struct svc_fh fh;
-};
-
struct nfsd3_accessres {
__be32 status;
struct svc_fh fh;
@@ -188,7 +172,6 @@ struct nfsd3_fhandle_pair {
bool nfs3svc_decode_fhandleargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs3svc_decode_diropargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
-bool nfs3svc_decode_mknodargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs3svc_decode_renameargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs3svc_decode_linkargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs3svc_decode_readdirargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
@@ -197,7 +180,6 @@ bool nfs3svc_decode_commitargs(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs3svc_encode_wccstat(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs3svc_encode_accessres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
-bool nfs3svc_encode_createres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs3svc_encode_renameres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs3svc_encode_linkres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
bool nfs3svc_encode_readdirres(struct svc_rqst *rqstp, struct xdr_stream *xdr);
--
2.55.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v3 19/34] NFSD: Use xdrgen XDR functions for the NFSv3 REMOVE procedure
2026-10-13 12:49 [PATCH v3 10/34] NFSD: Use xdrgen XDR functions for the NFSv3 LOOKUP procedure Chuck Lever
` (7 preceding siblings ...)
2026-10-13 12:49 ` [PATCH v3 18/34] NFSD: Use xdrgen XDR functions for NFSv3 MKNOD procedure Chuck Lever
@ 2026-10-13 12:49 ` Chuck Lever
8 siblings, 0 replies; 10+ messages in thread
From: Chuck Lever @ 2026-10-13 12:49 UTC (permalink / raw)
To: NeilBrown, Jeff Layton, Olga Kornievskaia, Dai Ngo, Tom Talpey; +Cc: linux-nfs
Replace the NFSPROC3_REMOVE entry in the nfsd_procedures3 array
with an entry that dispatches the xdrgen-generated
nfs_svc_decode_REMOVE3args and nfs_svc_encode_REMOVE3res. A
wrapper structure bridges the generated xdrgen argument type and
the legacy svc_fh representation the NFSD VFS layer still uses;
the result uses the generated REMOVE3res type directly.
Filename validity is now checked by nfsd3_proc_remove() so that a
proper NFS3ERR status code can be returned to the client rather
than GARBAGE_ARGS.
The pc_argzero field is now set to zero for the NFSv3 REMOVE
procedure. The xdrgen decoders are trusted to initialize all
arguments in the argp->xdrgen field, making the early defensive
memset unnecessary. The remaining argp fields are cleared as
needed.
Because nfsd3_proc_remove() now assembles the WCC data, that work
moves out of the XDR result encoder, and the reference to the
file handle can be released directly by nfsd3_proc_remove(). A
separate ->pc_release callback is thus no longer needed. This
makes it straightforward to confirm that the file handle is
properly released during every error flow.
Signed-off-by: Chuck Lever <cel@kernel.org>
---
fs/nfsd/nfs3proc.c | 72 ++++++++++++++++++++++++++++++++--------------
1 file changed, 51 insertions(+), 21 deletions(-)
diff --git a/fs/nfsd/nfs3proc.c b/fs/nfsd/nfs3proc.c
index d878391e63f8..540e4a6a42e0 100644
--- a/fs/nfsd/nfs3proc.c
+++ b/fs/nfsd/nfs3proc.c
@@ -163,6 +163,13 @@ struct MKNOD3res_wrapper {
static_assert(offsetof(struct MKNOD3res_wrapper, xdrgen) == 0);
+struct REMOVE3args_wrapper {
+ struct REMOVE3args xdrgen;
+ struct svc_fh fh;
+};
+
+static_assert(offsetof(struct REMOVE3args_wrapper, xdrgen) == 0);
+
static int nfsd3_ftype3_to_mode(ftype3 ftype)
{
switch (ftype) {
@@ -1252,20 +1259,42 @@ static __be32 nfsd3_proc_mknod(struct svc_rqst *rqstp)
return rpc_success;
}
-/*
- * Remove file/fifo/socket etc.
+/**
+ * nfsd3_proc_remove - REMOVE: Remove a file
+ * @rqstp: RPC transaction context
+ *
+ * Return:
+ * %rpc_success: RPC executed successfully
+ *
+ * RPC synopsis:
+ * REMOVE3res NFSPROC3_REMOVE(REMOVE3args) = 12;
*/
-static __be32
-nfsd3_proc_remove(struct svc_rqst *rqstp)
+static __be32 nfsd3_proc_remove(struct svc_rqst *rqstp)
{
- struct nfsd3_diropargs *argp = rqstp->rq_argp;
- struct nfsd3_attrstat *resp = rqstp->rq_resp;
+ struct REMOVE3args_wrapper *argp = rqstp->rq_argp;
+ struct diropargs3 *object = &argp->xdrgen.object;
+ struct REMOVE3res *resp = rqstp->rq_resp;
+ struct svc_fh *fhp = &argp->fh;
- /* Unlink. -S_IFDIR means file must not be a directory */
- fh_copy(&resp->fh, &argp->fh);
- resp->status = nfsd_unlink(rqstp, &resp->fh, -S_IFDIR,
- argp->name, argp->len);
- resp->status = nfsd3_map_status(resp->status);
+ nfsd3_fh3_to_svc_fh(fhp, &object->dir);
+ resp->status = nfsd3_check_filename(object->name.data,
+ object->name.len);
+ if (resp->status != nfs_ok)
+ goto out;
+
+ resp->status = nfsd_unlink(rqstp, fhp, -S_IFDIR,
+ (char *)object->name.data,
+ object->name.len);
+
+out:
+ if (resp->status == nfs_ok) {
+ nfsd3_fill_wcc_data(rqstp, &resp->u.resok.dir_wcc, fhp);
+ } else {
+ resp->status = nfsd3_map_status(resp->status);
+ nfsd3_fill_wcc_data(rqstp, &resp->u.resfail.dir_wcc, fhp);
+ }
+
+ fh_put(fhp);
return rpc_success;
}
@@ -1712,16 +1741,15 @@ static const struct svc_procedure nfsd_procedures3[22] = {
.pc_name = "MKNOD",
},
[NFSPROC3_REMOVE] = {
- .pc_func = nfsd3_proc_remove,
- .pc_decode = nfs3svc_decode_diropargs,
- .pc_encode = nfs3svc_encode_wccstatres,
- .pc_release = nfs3svc_release_fhandle,
- .pc_argsize = sizeof(struct nfsd3_diropargs),
- .pc_argzero = sizeof(struct nfsd3_diropargs),
- .pc_ressize = sizeof(struct nfsd3_wccstatres),
- .pc_cachetype = RC_REPLBUFF,
- .pc_xdrressize = ST+WC,
- .pc_name = "REMOVE",
+ .pc_func = nfsd3_proc_remove,
+ .pc_decode = nfs_svc_decode_REMOVE3args,
+ .pc_encode = nfs_svc_encode_REMOVE3res,
+ .pc_argsize = sizeof(struct REMOVE3args_wrapper),
+ .pc_argzero = 0,
+ .pc_ressize = sizeof(struct REMOVE3res),
+ .pc_cachetype = RC_REPLBUFF,
+ .pc_xdrressize = NFS3_REMOVE3res_sz,
+ .pc_name = "REMOVE",
},
[NFSPROC3_RMDIR] = {
.pc_func = nfsd3_proc_rmdir,
@@ -1852,6 +1880,8 @@ union nfsd3_xdrstore {
struct SYMLINK3res_wrapper symlinkres;
struct MKNOD3args_wrapper mknodargs;
struct MKNOD3res_wrapper mknodres;
+ struct REMOVE3args_wrapper removeargs;
+ struct REMOVE3res removeres;
struct nfsd3_renameargs renameargs;
struct nfsd3_linkargs linkargs;
struct nfsd3_readdirargs readdirargs;
--
2.55.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
end of thread, other threads:[~2026-10-13 12:49 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-13 12:49 [PATCH v3 10/34] NFSD: Use xdrgen XDR functions for the NFSv3 LOOKUP procedure Chuck Lever
2026-10-13 12:49 ` [PATCH v3 11/34] NFSD: Use xdrgen XDR functions for NFSv3 ACCESS procedure Chuck Lever
2026-10-13 12:49 ` [PATCH v3 12/34] NFSD: Use xdrgen XDR functions for NFSv3 READLINK procedure Chuck Lever
2026-10-13 12:49 ` [PATCH v3 13/34] NFSD: Use xdrgen XDR functions for NFSv3 READ procedure Chuck Lever
2026-10-13 12:49 ` [PATCH v3 14/34] NFSD: Use xdrgen XDR functions for NFSv3 WRITE procedure Chuck Lever
2026-10-13 12:49 ` [PATCH v3 15/34] NFSD: Use xdrgen XDR functions for NFSv3 CREATE procedure Chuck Lever
2026-10-13 12:49 ` [PATCH v3 16/34] NFSD: Use xdrgen XDR functions for NFSv3 MKDIR procedure Chuck Lever
2026-10-13 12:49 ` [PATCH v3 17/34] NFSD: Use xdrgen XDR functions for NFSv3 SYMLINK procedure Chuck Lever
2026-10-13 12:49 ` [PATCH v3 18/34] NFSD: Use xdrgen XDR functions for NFSv3 MKNOD procedure Chuck Lever
2026-10-13 12:49 ` [PATCH v3 19/34] NFSD: Use xdrgen XDR functions for the NFSv3 REMOVE procedure Chuck Lever
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.