Commit dc137bf5 authored by Al Viro's avatar Al Viro

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: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 3110df80
...@@ -55,6 +55,7 @@ build_path_from_dentry(struct dentry *direntry) ...@@ -55,6 +55,7 @@ build_path_from_dentry(struct dentry *direntry)
char dirsep; char dirsep;
struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb); struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb);
struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
unsigned seq;
if (direntry == NULL) if (direntry == NULL)
return NULL; /* not much we can do if dentry is freed and return NULL; /* not much we can do if dentry is freed and
...@@ -68,22 +69,29 @@ build_path_from_dentry(struct dentry *direntry) ...@@ -68,22 +69,29 @@ build_path_from_dentry(struct dentry *direntry)
dfsplen = 0; dfsplen = 0;
cifs_bp_rename_retry: cifs_bp_rename_retry:
namelen = dfsplen; namelen = dfsplen;
seq = read_seqbegin(&rename_lock);
rcu_read_lock();
for (temp = direntry; !IS_ROOT(temp);) { for (temp = direntry; !IS_ROOT(temp);) {
namelen += (1 + temp->d_name.len); namelen += (1 + temp->d_name.len);
temp = temp->d_parent; temp = temp->d_parent;
if (temp == NULL) { if (temp == NULL) {
cERROR(1, "corrupt dentry"); cERROR(1, "corrupt dentry");
rcu_read_unlock();
return NULL; return NULL;
} }
} }
rcu_read_unlock();
full_path = kmalloc(namelen+1, GFP_KERNEL); full_path = kmalloc(namelen+1, GFP_KERNEL);
if (full_path == NULL) if (full_path == NULL)
return full_path; return full_path;
full_path[namelen] = 0; /* trailing null */ full_path[namelen] = 0; /* trailing null */
rcu_read_lock();
for (temp = direntry; !IS_ROOT(temp);) { for (temp = direntry; !IS_ROOT(temp);) {
spin_lock(&temp->d_lock);
namelen -= 1 + temp->d_name.len; namelen -= 1 + temp->d_name.len;
if (namelen < 0) { if (namelen < 0) {
spin_unlock(&temp->d_lock);
break; break;
} else { } else {
full_path[namelen] = dirsep; full_path[namelen] = dirsep;
...@@ -91,14 +99,17 @@ build_path_from_dentry(struct dentry *direntry) ...@@ -91,14 +99,17 @@ build_path_from_dentry(struct dentry *direntry)
temp->d_name.len); temp->d_name.len);
cFYI(0, "name: %s", full_path + namelen); cFYI(0, "name: %s", full_path + namelen);
} }
spin_unlock(&temp->d_lock);
temp = temp->d_parent; temp = temp->d_parent;
if (temp == NULL) { if (temp == NULL) {
cERROR(1, "corrupt dentry"); cERROR(1, "corrupt dentry");
rcu_read_unlock();
kfree(full_path); kfree(full_path);
return NULL; return NULL;
} }
} }
if (namelen != dfsplen) { rcu_read_unlock();
if (namelen != dfsplen || read_seqretry(&rename_lock, seq)) {
cERROR(1, "did not end path lookup where expected namelen is %d", cERROR(1, "did not end path lookup where expected namelen is %d",
namelen); namelen);
/* presumably this is only possible if racing with a rename /* presumably this is only possible if racing with a rename
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment