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>
This commit is contained in:
parent
3110df800c
commit
dc137bf553
@ -55,6 +55,7 @@ build_path_from_dentry(struct dentry *direntry)
|
||||
char dirsep;
|
||||
struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb);
|
||||
struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
|
||||
unsigned seq;
|
||||
|
||||
if (direntry == NULL)
|
||||
return NULL; /* not much we can do if dentry is freed and
|
||||
@ -68,22 +69,29 @@ build_path_from_dentry(struct dentry *direntry)
|
||||
dfsplen = 0;
|
||||
cifs_bp_rename_retry:
|
||||
namelen = dfsplen;
|
||||
seq = read_seqbegin(&rename_lock);
|
||||
rcu_read_lock();
|
||||
for (temp = direntry; !IS_ROOT(temp);) {
|
||||
namelen += (1 + temp->d_name.len);
|
||||
temp = temp->d_parent;
|
||||
if (temp == NULL) {
|
||||
cERROR(1, "corrupt dentry");
|
||||
rcu_read_unlock();
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
full_path = kmalloc(namelen+1, GFP_KERNEL);
|
||||
if (full_path == NULL)
|
||||
return full_path;
|
||||
full_path[namelen] = 0; /* trailing null */
|
||||
rcu_read_lock();
|
||||
for (temp = direntry; !IS_ROOT(temp);) {
|
||||
spin_lock(&temp->d_lock);
|
||||
namelen -= 1 + temp->d_name.len;
|
||||
if (namelen < 0) {
|
||||
spin_unlock(&temp->d_lock);
|
||||
break;
|
||||
} else {
|
||||
full_path[namelen] = dirsep;
|
||||
@ -91,14 +99,17 @@ cifs_bp_rename_retry:
|
||||
temp->d_name.len);
|
||||
cFYI(0, "name: %s", full_path + namelen);
|
||||
}
|
||||
spin_unlock(&temp->d_lock);
|
||||
temp = temp->d_parent;
|
||||
if (temp == NULL) {
|
||||
cERROR(1, "corrupt dentry");
|
||||
rcu_read_unlock();
|
||||
kfree(full_path);
|
||||
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",
|
||||
namelen);
|
||||
/* presumably this is only possible if racing with a rename
|
||||
|
Loading…
Reference in New Issue
Block a user