diff options
Diffstat (limited to 'fs/nfs/dir.c')
-rw-r--r-- | fs/nfs/dir.c | 37 |
1 files changed, 31 insertions, 6 deletions
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index f03a770bacb..92d8ec859e2 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -637,7 +637,7 @@ int nfs_fsync_dir(struct file *filp, struct dentry *dentry, int datasync) * In the case it has, we assume that the dentries are untrustworthy * and may need to be looked up again. */ -static inline int nfs_check_verifier(struct inode *dir, struct dentry *dentry) +static int nfs_check_verifier(struct inode *dir, struct dentry *dentry) { if (IS_ROOT(dentry)) return 1; @@ -652,6 +652,12 @@ static inline void nfs_set_verifier(struct dentry * dentry, unsigned long verf) dentry->d_fsdata = (void *)verf; } +static void nfs_refresh_verifier(struct dentry * dentry, unsigned long verf) +{ + if (time_after(verf, (unsigned long)dentry->d_fsdata)) + nfs_set_verifier(dentry, verf); +} + /* * Whenever an NFS operation succeeds, we know that the dentry * is valid, so we update the revalidation timestamp. @@ -785,7 +791,7 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd) goto out_bad; nfs_renew_times(dentry); - nfs_set_verifier(dentry, verifier); + nfs_refresh_verifier(dentry, verifier); out_valid: unlock_kernel(); dput(parent); @@ -1085,7 +1091,7 @@ static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd) verifier = nfs_save_change_attribute(dir); ret = nfs4_open_revalidate(dir, dentry, openflags, nd); if (!ret) - nfs_set_verifier(dentry, verifier); + nfs_refresh_verifier(dentry, verifier); unlock_kernel(); out: dput(parent); @@ -1123,8 +1129,21 @@ static struct dentry *nfs_readdir_lookup(nfs_readdir_descriptor_t *desc) } name.hash = full_name_hash(name.name, name.len); dentry = d_lookup(parent, &name); - if (dentry != NULL) - return dentry; + if (dentry != NULL) { + /* Is this a positive dentry that matches the readdir info? */ + if (dentry->d_inode != NULL && + (NFS_FILEID(dentry->d_inode) == entry->ino || + d_mountpoint(dentry))) { + if (!desc->plus || entry->fh->size == 0) + return dentry; + if (nfs_compare_fh(NFS_FH(dentry->d_inode), + entry->fh) == 0) + goto out_renew; + } + /* No, so d_drop to allow one to be created */ + d_drop(dentry); + dput(dentry); + } if (!desc->plus || !(entry->fattr->valid & NFS_ATTR_FATTR)) return NULL; /* Note: caller is already holding the dir->i_mutex! */ @@ -1149,6 +1168,10 @@ static struct dentry *nfs_readdir_lookup(nfs_readdir_descriptor_t *desc) nfs_renew_times(dentry); nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); return dentry; +out_renew: + nfs_renew_times(dentry); + nfs_refresh_verifier(dentry, nfs_save_change_attribute(dir)); + return dentry; } /* @@ -1443,6 +1466,8 @@ static int nfs_unlink(struct inode *dir, struct dentry *dentry) if (atomic_read(&dentry->d_count) > 1) { spin_unlock(&dentry->d_lock); spin_unlock(&dcache_lock); + /* Start asynchronous writeout of the inode */ + write_inode_now(dentry->d_inode, 0); error = nfs_sillyrename(dir, dentry); unlock_kernel(); return error; @@ -1684,7 +1709,7 @@ out: if (!error) { d_move(old_dentry, new_dentry); nfs_renew_times(new_dentry); - nfs_set_verifier(new_dentry, nfs_save_change_attribute(new_dir)); + nfs_refresh_verifier(new_dentry, nfs_save_change_attribute(new_dir)); } /* new dentry created? */ |