aboutsummaryrefslogtreecommitdiffstats
path: root/fs/cifs/dir.c
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2011-07-16 23:37:20 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2011-07-16 23:37:20 -0400
commitdc137bf553dbb6855bd7efc34fedcd03102455f7 (patch)
treeab1fefc7da26bb86655a28651c4eafb56d741ea1 /fs/cifs/dir.c
parent3110df800c4de2724624d46e6bed27efc5e9a707 (diff)
cifs: build_path_from_dentry() race fix
deal with d_move() races properly; rename_lock read-retry loop, rcu_read_lock() held while walking to root, d_lock held over subtraction from namelen and copying the component to stabilize ->d_name. Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs/cifs/dir.c')
-rw-r--r--fs/cifs/dir.c13
1 files changed, 12 insertions, 1 deletions
diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c
index 81914df47ef1..fa8c21d913bc 100644
--- a/fs/cifs/dir.c
+++ b/fs/cifs/dir.c
@@ -55,6 +55,7 @@ build_path_from_dentry(struct dentry *direntry)
55 char dirsep; 55 char dirsep;
56 struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb); 56 struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb);
57 struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); 57 struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
58 unsigned seq;
58 59
59 if (direntry == NULL) 60 if (direntry == NULL)
60 return NULL; /* not much we can do if dentry is freed and 61 return NULL; /* not much we can do if dentry is freed and
@@ -68,22 +69,29 @@ build_path_from_dentry(struct dentry *direntry)
68 dfsplen = 0; 69 dfsplen = 0;
69cifs_bp_rename_retry: 70cifs_bp_rename_retry:
70 namelen = dfsplen; 71 namelen = dfsplen;
72 seq = read_seqbegin(&rename_lock);
73 rcu_read_lock();
71 for (temp = direntry; !IS_ROOT(temp);) { 74 for (temp = direntry; !IS_ROOT(temp);) {
72 namelen += (1 + temp->d_name.len); 75 namelen += (1 + temp->d_name.len);
73 temp = temp->d_parent; 76 temp = temp->d_parent;
74 if (temp == NULL) { 77 if (temp == NULL) {
75 cERROR(1, "corrupt dentry"); 78 cERROR(1, "corrupt dentry");
79 rcu_read_unlock();
76 return NULL; 80 return NULL;
77 } 81 }
78 } 82 }
83 rcu_read_unlock();
79 84
80 full_path = kmalloc(namelen+1, GFP_KERNEL); 85 full_path = kmalloc(namelen+1, GFP_KERNEL);
81 if (full_path == NULL) 86 if (full_path == NULL)
82 return full_path; 87 return full_path;
83 full_path[namelen] = 0; /* trailing null */ 88 full_path[namelen] = 0; /* trailing null */
89 rcu_read_lock();
84 for (temp = direntry; !IS_ROOT(temp);) { 90 for (temp = direntry; !IS_ROOT(temp);) {
91 spin_lock(&temp->d_lock);
85 namelen -= 1 + temp->d_name.len; 92 namelen -= 1 + temp->d_name.len;
86 if (namelen < 0) { 93 if (namelen < 0) {
94 spin_unlock(&temp->d_lock);
87 break; 95 break;
88 } else { 96 } else {
89 full_path[namelen] = dirsep; 97 full_path[namelen] = dirsep;
@@ -91,14 +99,17 @@ cifs_bp_rename_retry:
91 temp->d_name.len); 99 temp->d_name.len);
92 cFYI(0, "name: %s", full_path + namelen); 100 cFYI(0, "name: %s", full_path + namelen);
93 } 101 }
102 spin_unlock(&temp->d_lock);
94 temp = temp->d_parent; 103 temp = temp->d_parent;
95 if (temp == NULL) { 104 if (temp == NULL) {
96 cERROR(1, "corrupt dentry"); 105 cERROR(1, "corrupt dentry");
106 rcu_read_unlock();
97 kfree(full_path); 107 kfree(full_path);
98 return NULL; 108 return NULL;
99 } 109 }
100 } 110 }
101 if (namelen != dfsplen) { 111 rcu_read_unlock();
112 if (namelen != dfsplen || read_seqretry(&rename_lock, seq)) {
102 cERROR(1, "did not end path lookup where expected namelen is %d", 113 cERROR(1, "did not end path lookup where expected namelen is %d",
103 namelen); 114 namelen);
104 /* presumably this is only possible if racing with a rename 115 /* presumably this is only possible if racing with a rename