aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfs/dir.c
diff options
context:
space:
mode:
authorTrond Myklebust <Trond.Myklebust@netapp.com>2007-10-15 18:17:53 -0400
committerTrond Myklebust <Trond.Myklebust@netapp.com>2007-10-19 17:19:16 -0400
commit565277f63c616e11c37309a1e98c052d18ebbb55 (patch)
tree60fdddc5a1c97df696392e47ead71d33d39e487f /fs/nfs/dir.c
parent61e930a904966cc37e0a3404276f0b73037e57ca (diff)
NFS: Fix a race in sillyrename
lookup() and sillyrename() can race one another because the sillyrename() completion cannot take the parent directory's inode->i_mutex since the latter may be held by whoever is calling dput(). We therefore have little option but to add extra locking to ensure that nfs_lookup() and nfs_atomic_open() do not race with the sillyrename completion. If somebody has looked up the sillyrenamed file in the meantime, we just transfer the sillydelete information to the new dentry. Please refer to the bug-report at http://bugzilla.linux-nfs.org/show_bug.cgi?id=150 Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs/nfs/dir.c')
-rw-r--r--fs/nfs/dir.c14
1 files changed, 11 insertions, 3 deletions
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index 8ec7fbd8240c..35334539d947 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -562,6 +562,7 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
562 nfs_fattr_init(&fattr); 562 nfs_fattr_init(&fattr);
563 desc->entry = &my_entry; 563 desc->entry = &my_entry;
564 564
565 nfs_block_sillyrename(dentry);
565 while(!desc->entry->eof) { 566 while(!desc->entry->eof) {
566 res = readdir_search_pagecache(desc); 567 res = readdir_search_pagecache(desc);
567 568
@@ -592,6 +593,7 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
592 break; 593 break;
593 } 594 }
594 } 595 }
596 nfs_unblock_sillyrename(dentry);
595 unlock_kernel(); 597 unlock_kernel();
596 if (res > 0) 598 if (res > 0)
597 res = 0; 599 res = 0;
@@ -866,6 +868,7 @@ struct dentry_operations nfs_dentry_operations = {
866static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, struct nameidata *nd) 868static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, struct nameidata *nd)
867{ 869{
868 struct dentry *res; 870 struct dentry *res;
871 struct dentry *parent;
869 struct inode *inode = NULL; 872 struct inode *inode = NULL;
870 int error; 873 int error;
871 struct nfs_fh fhandle; 874 struct nfs_fh fhandle;
@@ -894,26 +897,31 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, stru
894 goto out_unlock; 897 goto out_unlock;
895 } 898 }
896 899
900 parent = dentry->d_parent;
901 /* Protect against concurrent sillydeletes */
902 nfs_block_sillyrename(parent);
897 error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, &fhandle, &fattr); 903 error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, &fhandle, &fattr);
898 if (error == -ENOENT) 904 if (error == -ENOENT)
899 goto no_entry; 905 goto no_entry;
900 if (error < 0) { 906 if (error < 0) {
901 res = ERR_PTR(error); 907 res = ERR_PTR(error);
902 goto out_unlock; 908 goto out_unblock_sillyrename;
903 } 909 }
904 inode = nfs_fhget(dentry->d_sb, &fhandle, &fattr); 910 inode = nfs_fhget(dentry->d_sb, &fhandle, &fattr);
905 res = (struct dentry *)inode; 911 res = (struct dentry *)inode;
906 if (IS_ERR(res)) 912 if (IS_ERR(res))
907 goto out_unlock; 913 goto out_unblock_sillyrename;
908 914
909no_entry: 915no_entry:
910 res = d_materialise_unique(dentry, inode); 916 res = d_materialise_unique(dentry, inode);
911 if (res != NULL) { 917 if (res != NULL) {
912 if (IS_ERR(res)) 918 if (IS_ERR(res))
913 goto out_unlock; 919 goto out_unblock_sillyrename;
914 dentry = res; 920 dentry = res;
915 } 921 }
916 nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); 922 nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
923out_unblock_sillyrename:
924 nfs_unblock_sillyrename(parent);
917out_unlock: 925out_unlock:
918 unlock_kernel(); 926 unlock_kernel();
919out: 927out: