diff options
author | Jeff Layton <jlayton@kernel.org> | 2019-04-15 12:00:42 -0400 |
---|---|---|
committer | Ilya Dryomov <idryomov@gmail.com> | 2019-04-23 15:37:54 -0400 |
commit | 4b8222870032715f9d995f3eb7c7acd8379a275d (patch) | |
tree | 922907eb1251073439bc094e76800b7f1188c098 /fs/ceph | |
parent | 76a495d666e5043ffc315695f8241f5e94a98849 (diff) |
ceph: handle the case where a dentry has been renamed on outstanding req
It's possible for us to issue a lookup to revalidate a dentry
concurrently with a rename. If done in the right order, then we could
end up processing dentry info in the reply that no longer reflects the
state of the dentry.
If req->r_dentry->d_name differs from the one in the trace, then just
ignore the trace in the reply. We only need to do this however if the
parent's i_rwsem is not held.
Signed-off-by: Jeff Layton <jlayton@kernel.org>
Reviewed-by: "Yan, Zheng" <zyan@redhat.com>
Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
Diffstat (limited to 'fs/ceph')
-rw-r--r-- | fs/ceph/inode.c | 16 |
1 files changed, 15 insertions, 1 deletions
diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c index 2d61ddda9bf5..c2feb310ac1e 100644 --- a/fs/ceph/inode.c +++ b/fs/ceph/inode.c | |||
@@ -1163,6 +1163,19 @@ static int splice_dentry(struct dentry **pdn, struct inode *in) | |||
1163 | return 0; | 1163 | return 0; |
1164 | } | 1164 | } |
1165 | 1165 | ||
1166 | static int d_name_cmp(struct dentry *dentry, const char *name, size_t len) | ||
1167 | { | ||
1168 | int ret; | ||
1169 | |||
1170 | /* take d_lock to ensure dentry->d_name stability */ | ||
1171 | spin_lock(&dentry->d_lock); | ||
1172 | ret = dentry->d_name.len - len; | ||
1173 | if (!ret) | ||
1174 | ret = memcmp(dentry->d_name.name, name, len); | ||
1175 | spin_unlock(&dentry->d_lock); | ||
1176 | return ret; | ||
1177 | } | ||
1178 | |||
1166 | /* | 1179 | /* |
1167 | * Incorporate results into the local cache. This is either just | 1180 | * Incorporate results into the local cache. This is either just |
1168 | * one inode, or a directory, dentry, and possibly linked-to inode (e.g., | 1181 | * one inode, or a directory, dentry, and possibly linked-to inode (e.g., |
@@ -1412,7 +1425,8 @@ retry_lookup: | |||
1412 | err = splice_dentry(&req->r_dentry, in); | 1425 | err = splice_dentry(&req->r_dentry, in); |
1413 | if (err < 0) | 1426 | if (err < 0) |
1414 | goto done; | 1427 | goto done; |
1415 | } else if (rinfo->head->is_dentry) { | 1428 | } else if (rinfo->head->is_dentry && |
1429 | !d_name_cmp(req->r_dentry, rinfo->dname, rinfo->dname_len)) { | ||
1416 | struct ceph_vino *ptvino = NULL; | 1430 | struct ceph_vino *ptvino = NULL; |
1417 | 1431 | ||
1418 | if ((le32_to_cpu(rinfo->diri.in->cap.caps) & CEPH_CAP_FILE_SHARED) || | 1432 | if ((le32_to_cpu(rinfo->diri.in->cap.caps) & CEPH_CAP_FILE_SHARED) || |