summaryrefslogtreecommitdiff
path: root/fs/ceph/mds_client.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2011-07-18 09:03:15 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2011-07-18 09:03:15 -0700
commitd36c30181c4cf6ead34ae30fa2c777b871225c87 (patch)
tree8a2476c0eb6bb83ed5a8b493d79458d0e114a146 /fs/ceph/mds_client.c
parenta406721dff91a9a5297d140dbb90327966cf9bc0 (diff)
parent0916a5e45fbd2604a303c8cc18e6b2b7c815e4c9 (diff)
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs-2.6
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs-2.6: hppfs_lookup(): don't open-code lookup_one_len() hppfs: fix dentry leak cramfs: get_cramfs_inode() returns ERR_PTR() on failure ufs should use d_splice_alias() fix exofs ->get_parent() ceph analog of cifs build_path_from_dentry() race fix cifs: build_path_from_dentry() race fix
Diffstat (limited to 'fs/ceph/mds_client.c')
-rw-r--r--fs/ceph/mds_client.c19
1 files changed, 16 insertions, 3 deletions
diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c
index 79743d146be6..0c1d91756528 100644
--- a/fs/ceph/mds_client.c
+++ b/fs/ceph/mds_client.c
@@ -1438,12 +1438,15 @@ char *ceph_mdsc_build_path(struct dentry *dentry, int *plen, u64 *base,
struct dentry *temp;
char *path;
int len, pos;
+ unsigned seq;
if (dentry == NULL)
return ERR_PTR(-EINVAL);
retry:
len = 0;
+ seq = read_seqbegin(&rename_lock);
+ rcu_read_lock();
for (temp = dentry; !IS_ROOT(temp);) {
struct inode *inode = temp->d_inode;
if (inode && ceph_snap(inode) == CEPH_SNAPDIR)
@@ -1455,10 +1458,12 @@ retry:
len += 1 + temp->d_name.len;
temp = temp->d_parent;
if (temp == NULL) {
+ rcu_read_unlock();
pr_err("build_path corrupt dentry %p\n", dentry);
return ERR_PTR(-EINVAL);
}
}
+ rcu_read_unlock();
if (len)
len--; /* no leading '/' */
@@ -1467,9 +1472,12 @@ retry:
return ERR_PTR(-ENOMEM);
pos = len;
path[pos] = 0; /* trailing null */
+ rcu_read_lock();
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) {
dout("build_path path+%d: %p SNAPDIR\n",
pos, temp);
@@ -1478,21 +1486,26 @@ retry:
break;
} else {
pos -= temp->d_name.len;
- if (pos < 0)
+ if (pos < 0) {
+ spin_unlock(&temp->d_lock);
break;
+ }
strncpy(path + pos, temp->d_name.name,
temp->d_name.len);
}
+ spin_unlock(&temp->d_lock);
if (pos)
path[--pos] = '/';
temp = temp->d_parent;
if (temp == NULL) {
+ rcu_read_unlock();
pr_err("build_path corrupt dentry\n");
kfree(path);
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 "
"expected, namelen is %d, pos is %d\n", len, pos);
/* presumably this is only possible if racing with a