@@ -2872,9 +2872,22 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,
goto out;
}
+ /*
+ * The inode lock is needed here to ensure that there is not a
+ * write to the inode in progress that might change the size,
+ * or an in-progress directory morphing operation for directory
+ * inodes.
+ *
+ * READ and GETATTR are not guaranteed to be atomic, even when in
+ * the same compound, but we do try to present attributes in the
+ * GETATTR reply as representing a single point in time.
+ */
+ inode_lock(d_inode(dentry));
err = vfs_getattr(&path, &stat,
STATX_BASIC_STATS | STATX_BTIME | STATX_INO_VERSION,
AT_STATX_SYNC_AS_STAT);
+ inode_unlock(d_inode(dentry));
+
if (err)
goto out_nfserr;
if (!(stat.result_mask & STATX_BTIME))
The i_version counter for regular files is updated in update_time, and that's usually done before copying the data to the pagecache. It's possible that a reader and writer could race like this: reader writer ------ ------ i_version++ read getattr update page cache If that happens then the reader may associate the i_version value with the wrong inode state. All of the existing filesystems that implement i_version take the i_rwsem in their write_iter operations before incrementing it. Take the inode_lock when issuing a getattr for NFSv4 attributes to prevent the above race. Signed-off-by: Jeff Layton <jlayton@kernel.org> --- fs/nfsd/nfs4xdr.c | 13 +++++++++++++ 1 file changed, 13 insertions(+)