NFS: Ensure that fstat() always returns the correct mtime Even if the file is open for writes. Signed-off-by: Trond Myklebust --- fs/nfs/file.c | 28 ++++++++++++++++++++++------ fs/nfs/inode.c | 24 ++++++++++++++++-------- include/linux/nfs_fs.h | 1 + 3 files changed, 39 insertions(+), 14 deletions(-) Index: linux-2.6.12-rc1/fs/nfs/inode.c =================================================================== --- linux-2.6.12-rc1.orig/fs/nfs/inode.c +++ linux-2.6.12-rc1/fs/nfs/inode.c @@ -620,9 +620,9 @@ nfs_zap_caches(struct inode *inode) memset(NFS_COOKIEVERF(inode), 0, sizeof(NFS_COOKIEVERF(inode))); if (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)) - nfsi->flags |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL; + nfsi->flags |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL|NFS_INO_REVAL_PAGECACHE; else - nfsi->flags |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL; + nfsi->flags |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL|NFS_INO_REVAL_PAGECACHE; } static void nfs_zap_acl_cache(struct inode *inode) @@ -1055,6 +1055,7 @@ __nfs_revalidate_inode(struct nfs_server goto out; } flags = nfsi->flags; + nfsi->flags &= ~NFS_INO_REVAL_PAGECACHE; /* * We may need to keep the attributes marked as invalid if * we raced with nfs_end_attr_update(). @@ -1188,8 +1189,11 @@ int nfs_refresh_inode(struct inode *inod if ((fattr->valid & NFS_ATTR_PRE_CHANGE) != 0 && nfsi->change_attr == fattr->pre_change_attr) nfsi->change_attr = fattr->change_attr; - if (!data_unstable && nfsi->change_attr != fattr->change_attr) + if (nfsi->change_attr != fattr->change_attr) { nfsi->flags |= NFS_INO_INVALID_ATTR; + if (!data_unstable) + nfsi->flags |= NFS_INO_REVAL_PAGECACHE; + } } if ((fattr->valid & NFS_ATTR_FATTR) == 0) @@ -1212,12 +1216,16 @@ int nfs_refresh_inode(struct inode *inod } /* Verify a few of the more important attributes */ - if (!data_unstable) { - if (!timespec_equal(&inode->i_mtime, &fattr->mtime) - || cur_size != new_isize) - nfsi->flags |= NFS_INO_INVALID_ATTR; - } else if (new_isize != cur_size && nfsi->npages == 0) + if (!timespec_equal(&inode->i_mtime, &fattr->mtime)) { nfsi->flags |= NFS_INO_INVALID_ATTR; + if (!data_unstable) + nfsi->flags |= NFS_INO_REVAL_PAGECACHE; + } + if (cur_size != new_isize) { + nfsi->flags |= NFS_INO_INVALID_ATTR; + if (nfsi->npages == 0) + nfsi->flags |= NFS_INO_REVAL_PAGECACHE; + } /* Have any file permissions changed? */ if ((inode->i_mode & S_IALLUGO) != (fattr->mode & S_IALLUGO) Index: linux-2.6.12-rc1/fs/nfs/file.c =================================================================== --- linux-2.6.12-rc1.orig/fs/nfs/file.c +++ linux-2.6.12-rc1/fs/nfs/file.c @@ -128,6 +128,21 @@ nfs_file_release(struct inode *inode, st } /** + * nfs_revalidate_file - Revalidate the page cache & related metadata + * @inode - pointer to inode struct + * @file - pointer to file + */ +static int nfs_revalidate_file(struct inode *inode, struct file *filp) +{ + int retval = 0; + + if ((NFS_FLAGS(inode) & NFS_INO_REVAL_PAGECACHE) || nfs_attribute_timeout(inode)) + retval = __nfs_revalidate_inode(NFS_SERVER(inode), inode); + nfs_revalidate_mapping(inode, filp->f_mapping); + return 0; +} + +/** * nfs_revalidate_size - Revalidate the file size * @inode - pointer to inode struct * @file - pointer to struct file @@ -149,7 +164,8 @@ static int nfs_revalidate_file_size(stru goto force_reval; if (nfsi->npages != 0) return 0; - return nfs_revalidate_inode(server, inode); + if (!(NFS_FLAGS(inode) & NFS_INO_REVAL_PAGECACHE) && !nfs_attribute_timeout(inode)) + return 0; force_reval: return __nfs_revalidate_inode(server, inode); } @@ -210,7 +226,7 @@ nfs_file_read(struct kiocb *iocb, char _ dentry->d_parent->d_name.name, dentry->d_name.name, (unsigned long) count, (unsigned long) pos); - result = nfs_revalidate_inode(NFS_SERVER(inode), inode); + result = nfs_revalidate_file(inode, iocb->ki_filp); if (!result) result = generic_file_aio_read(iocb, buf, count, pos); return result; @@ -228,7 +244,7 @@ nfs_file_sendfile(struct file *filp, lof dentry->d_parent->d_name.name, dentry->d_name.name, (unsigned long) count, (unsigned long long) *ppos); - res = nfs_revalidate_inode(NFS_SERVER(inode), inode); + res = nfs_revalidate_file(inode, filp); if (!res) res = generic_file_sendfile(filp, ppos, count, actor, target); return res; @@ -244,7 +260,7 @@ nfs_file_mmap(struct file * file, struct dfprintk(VFS, "nfs: mmap(%s/%s)\n", dentry->d_parent->d_name.name, dentry->d_name.name); - status = nfs_revalidate_inode(NFS_SERVER(inode), inode); + status = nfs_revalidate_file(inode, file); if (!status) status = generic_file_mmap(file, vma); return status; @@ -340,8 +356,8 @@ nfs_file_write(struct kiocb *iocb, const result = nfs_revalidate_file_size(inode, iocb->ki_filp); if (result) goto out; - } else - nfs_revalidate_mapping(inode, iocb->ki_filp->f_mapping); + } + nfs_revalidate_mapping(inode, iocb->ki_filp->f_mapping); result = count; if (!count) Index: linux-2.6.12-rc1/include/linux/nfs_fs.h =================================================================== --- linux-2.6.12-rc1.orig/include/linux/nfs_fs.h +++ linux-2.6.12-rc1/include/linux/nfs_fs.h @@ -198,6 +198,7 @@ struct nfs_inode { #define NFS_INO_INVALID_ATIME 0x0020 /* cached atime is invalid */ #define NFS_INO_INVALID_ACCESS 0x0040 /* cached access cred invalid */ #define NFS_INO_INVALID_ACL 0x0080 /* cached acls are invalid */ +#define NFS_INO_REVAL_PAGECACHE 0x1000 /* must revalidate pagecache */ static inline struct nfs_inode *NFS_I(struct inode *inode) {