[PATCH 2/3] NFS: stamp after the server's ctime when truncating under delegated times

From: Jeff Layton

Date: Fri Nov 06 2026 - 15:52:11 EST


A size-changing SETATTR always has the server stamp c/mtime
(__nfsd_setattr(), per RFC 5661 section 18.30.4). With delegated
timestamps the client discards the reply's ctime and stamps its own in
nfs_vmtruncate(), clamped only against its cached ctime. A coarse client
stamp can still land before the server's, which then won't move ctime
back when the holder returns its times, so ctime changes after
DELEGRETURN.

Raise the cached ctime to the reply's before nfs_vmtruncate() stamps, so
the delegated stamp lands after the server's.

Fixes: e12912d94137 ("NFSv4: Add support for delegated atime and mtime attributes")
Signed-off-by: Jeff Layton <jlayton@xxxxxxxxxx>
---
fs/nfs/inode.c | 17 +++++++++++++++++
1 file changed, 17 insertions(+)

diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index e990f353b7d0..8b86d93f8101 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -648,6 +648,21 @@ nfs_delegated_ctime_after(struct inode *inode, const struct timespec64 *old)
return now;
}

+/*
+ * The server stamped c/mtime itself (e.g. for a size change). Raise the
+ * cached ctime to the server's so that the next delegated stamp lands
+ * after it.
+ */
+static void nfs_delegated_ctime_raise(struct inode *inode,
+ const struct nfs_fattr *fattr)
+{
+ struct timespec64 ctime = inode_get_ctime(inode);
+
+ if ((fattr->valid & NFS_ATTR_FATTR_CTIME) &&
+ timespec64_compare(&fattr->ctime, &ctime) > 0)
+ inode_set_ctime_to_ts(inode, fattr->ctime);
+}
+
static void nfs_set_timestamps_to_ts(struct inode *inode, struct iattr *attr)
{
unsigned int cache_flags = 0;
@@ -860,6 +875,8 @@ void nfs_setattr_update_inode(struct inode *inode, struct iattr *attr,
if ((attr->ia_valid & ATTR_SIZE) != 0) {
if (!nfs_have_delegated_mtime(inode))
nfs_set_cache_invalid(inode, NFS_INO_INVALID_MTIME);
+ else
+ nfs_delegated_ctime_raise(inode, fattr);
nfs_set_cache_invalid(inode, NFS_INO_INVALID_BLOCKS);
nfs_inc_stats(inode, NFSIOS_SETATTRTRUNC);
nfs_vmtruncate(inode, attr->ia_size);

--
2.56.0