[PATCH v2 4/5] nfsd: don't vet delegated times against an exclusive-create verifier
From: Jeff Layton
Date: Fri Nov 06 2026 - 15:46:27 EST
An exclusive create stores the client's verifier in atime and mtime
until the client sets real times. A write delegation granted on that
OPEN records those as dl_atime and dl_mtime (e.g. 4777.0 and
681509.0), so vetting the holder's times against them always passes,
including times older than the file itself.
When the OPEN did an exclusive create, use the ctime at grant, less 1ns,
as the floor for atime and mtime too. Vetting rejects a time equal to
its floor, and a holder time equal to the ctime must still replace the
verifier. Move nfsd4_create_is_exclusive() to xdr4.h so nfs4state.c can
use it.
Fixes: 7663e963a511 ("nfsd: track original timestamps in nfs4_delegation")
Signed-off-by: Jeff Layton <jlayton@xxxxxxxxxx>
---
fs/nfsd/nfs4proc.c | 6 ------
fs/nfsd/nfs4state.c | 14 ++++++++++++++
fs/nfsd/xdr4.h | 6 ++++++
3 files changed, 20 insertions(+), 6 deletions(-)
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 4e5d5852bf57..25613c1ffaaf 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -245,12 +245,6 @@ static void nfsd4_set_open_owner_reply_cache(struct nfsd4_compound_state *cstate
&resfh->fh_handle);
}
-static inline bool nfsd4_create_is_exclusive(int createmode)
-{
- return createmode == NFS4_CREATE_EXCLUSIVE ||
- createmode == NFS4_CREATE_EXCLUSIVE4_1;
-}
-
static struct file *do_lookup_open(struct path *parent,
struct qstr *name,
unsigned int oflags,
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index f276c16b3ea4..360b8886dbc0 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -7543,6 +7543,20 @@ nfs4_open_delegation(struct svc_rqst *rqstp, struct nfsd4_open *open,
dp->dl_atime = stat.atime;
dp->dl_ctime = stat.ctime;
dp->dl_mtime = stat.mtime;
+ /*
+ * An exclusive create stores the verifier in atime and mtime,
+ * so the only real floor for the holder's times is the ctime.
+ * Vetting rejects a time equal to its floor, but one equal to
+ * the ctime must still replace the verifier.
+ */
+ if (open->op_created &&
+ nfsd4_create_is_exclusive(open->op_createmode)) {
+ struct timespec64 floor;
+
+ floor = timespec64_sub(stat.ctime, ns_to_timespec64(1));
+ dp->dl_atime = floor;
+ dp->dl_mtime = floor;
+ }
spin_lock(&f->f_lock);
if (deleg_ts)
f->f_mode |= FMODE_NOCMTIME;
diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
index ecab2249e9bd..feda5464f260 100644
--- a/fs/nfsd/xdr4.h
+++ b/fs/nfsd/xdr4.h
@@ -280,6 +280,12 @@ struct nfsd4_open {
struct svc_rqst *op_rqstp;
};
+static inline bool nfsd4_create_is_exclusive(int createmode)
+{
+ return createmode == NFS4_CREATE_EXCLUSIVE ||
+ createmode == NFS4_CREATE_EXCLUSIVE4_1;
+}
+
struct nfsd4_open_confirm {
stateid_t oc_req_stateid /* request */;
u32 oc_seqid /* request */;
--
2.56.0