ceph analog of cifs build_path_from_dentry() race fix
... unfortunately, cifs bug got copied. Fix is essentially the same. Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
Родитель
dc137bf553
Коммит
1b71fe2efa
|
@ -1438,12 +1438,15 @@ char *ceph_mdsc_build_path(struct dentry *dentry, int *plen, u64 *base,
|
||||||
struct dentry *temp;
|
struct dentry *temp;
|
||||||
char *path;
|
char *path;
|
||||||
int len, pos;
|
int len, pos;
|
||||||
|
unsigned seq;
|
||||||
|
|
||||||
if (dentry == NULL)
|
if (dentry == NULL)
|
||||||
return ERR_PTR(-EINVAL);
|
return ERR_PTR(-EINVAL);
|
||||||
|
|
||||||
retry:
|
retry:
|
||||||
len = 0;
|
len = 0;
|
||||||
|
seq = read_seqbegin(&rename_lock);
|
||||||
|
rcu_read_lock();
|
||||||
for (temp = dentry; !IS_ROOT(temp);) {
|
for (temp = dentry; !IS_ROOT(temp);) {
|
||||||
struct inode *inode = temp->d_inode;
|
struct inode *inode = temp->d_inode;
|
||||||
if (inode && ceph_snap(inode) == CEPH_SNAPDIR)
|
if (inode && ceph_snap(inode) == CEPH_SNAPDIR)
|
||||||
|
@ -1455,10 +1458,12 @@ retry:
|
||||||
len += 1 + temp->d_name.len;
|
len += 1 + temp->d_name.len;
|
||||||
temp = temp->d_parent;
|
temp = temp->d_parent;
|
||||||
if (temp == NULL) {
|
if (temp == NULL) {
|
||||||
|
rcu_read_unlock();
|
||||||
pr_err("build_path corrupt dentry %p\n", dentry);
|
pr_err("build_path corrupt dentry %p\n", dentry);
|
||||||
return ERR_PTR(-EINVAL);
|
return ERR_PTR(-EINVAL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
rcu_read_unlock();
|
||||||
if (len)
|
if (len)
|
||||||
len--; /* no leading '/' */
|
len--; /* no leading '/' */
|
||||||
|
|
||||||
|
@ -1467,9 +1472,12 @@ retry:
|
||||||
return ERR_PTR(-ENOMEM);
|
return ERR_PTR(-ENOMEM);
|
||||||
pos = len;
|
pos = len;
|
||||||
path[pos] = 0; /* trailing null */
|
path[pos] = 0; /* trailing null */
|
||||||
|
rcu_read_lock();
|
||||||
for (temp = dentry; !IS_ROOT(temp) && pos != 0; ) {
|
for (temp = dentry; !IS_ROOT(temp) && pos != 0; ) {
|
||||||
struct inode *inode = temp->d_inode;
|
struct inode *inode;
|
||||||
|
|
||||||
|
spin_lock(&temp->d_lock);
|
||||||
|
inode = temp->d_inode;
|
||||||
if (inode && ceph_snap(inode) == CEPH_SNAPDIR) {
|
if (inode && ceph_snap(inode) == CEPH_SNAPDIR) {
|
||||||
dout("build_path path+%d: %p SNAPDIR\n",
|
dout("build_path path+%d: %p SNAPDIR\n",
|
||||||
pos, temp);
|
pos, temp);
|
||||||
|
@ -1478,21 +1486,26 @@ retry:
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
pos -= temp->d_name.len;
|
pos -= temp->d_name.len;
|
||||||
if (pos < 0)
|
if (pos < 0) {
|
||||||
|
spin_unlock(&temp->d_lock);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
strncpy(path + pos, temp->d_name.name,
|
strncpy(path + pos, temp->d_name.name,
|
||||||
temp->d_name.len);
|
temp->d_name.len);
|
||||||
}
|
}
|
||||||
|
spin_unlock(&temp->d_lock);
|
||||||
if (pos)
|
if (pos)
|
||||||
path[--pos] = '/';
|
path[--pos] = '/';
|
||||||
temp = temp->d_parent;
|
temp = temp->d_parent;
|
||||||
if (temp == NULL) {
|
if (temp == NULL) {
|
||||||
|
rcu_read_unlock();
|
||||||
pr_err("build_path corrupt dentry\n");
|
pr_err("build_path corrupt dentry\n");
|
||||||
kfree(path);
|
kfree(path);
|
||||||
return ERR_PTR(-EINVAL);
|
return ERR_PTR(-EINVAL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (pos != 0) {
|
rcu_read_unlock();
|
||||||
|
if (pos != 0 || read_seqretry(&rename_lock, seq)) {
|
||||||
pr_err("build_path did not end path lookup where "
|
pr_err("build_path did not end path lookup where "
|
||||||
"expected, namelen is %d, pos is %d\n", len, pos);
|
"expected, namelen is %d, pos is %d\n", len, pos);
|
||||||
/* presumably this is only possible if racing with a
|
/* presumably this is only possible if racing with a
|
||||||
|
|
Загрузка…
Ссылка в новой задаче