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:
Al Viro 2011-07-16 23:43:58 -04:00
Родитель dc137bf553
Коммит 1b71fe2efa
1 изменённых файлов: 16 добавлений и 3 удалений

Просмотреть файл

@ -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