diff options
author | Jeff Layton <jlayton@kernel.org> | 2019-04-15 12:00:42 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2019-05-16 19:40:25 +0200 |
commit | 2455f6cbd19b99642f8022696fbe2094e32a813e (patch) | |
tree | a2be07b79c422dedc5105ccc5771e4330be96d33 | |
parent | d3a9275364d9c4257a76fdc2290cc19586b5acf6 (diff) |
ceph: handle the case where a dentry has been renamed on outstanding req
[ Upstream commit 4b8222870032715f9d995f3eb7c7acd8379a275d ]
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>
Signed-off-by: Sasha Levin <sashal@kernel.org>
-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 f7f9e305aaf8..fd3db2e112d6 100644 --- a/fs/ceph/inode.c +++ b/fs/ceph/inode.c @@ -1152,6 +1152,19 @@ static int splice_dentry(struct dentry **pdn, struct inode *in) return 0; } +static int d_name_cmp(struct dentry *dentry, const char *name, size_t len) +{ + int ret; + + /* take d_lock to ensure dentry->d_name stability */ + spin_lock(&dentry->d_lock); + ret = dentry->d_name.len - len; + if (!ret) + ret = memcmp(dentry->d_name.name, name, len); + spin_unlock(&dentry->d_lock); + return ret; +} + /* * Incorporate results into the local cache. This is either just * one inode, or a directory, dentry, and possibly linked-to inode (e.g., @@ -1401,7 +1414,8 @@ retry_lookup: err = splice_dentry(&req->r_dentry, in); if (err < 0) goto done; - } else if (rinfo->head->is_dentry) { + } else if (rinfo->head->is_dentry && + !d_name_cmp(req->r_dentry, rinfo->dname, rinfo->dname_len)) { struct ceph_vino *ptvino = NULL; if ((le32_to_cpu(rinfo->diri.in->cap.caps) & CEPH_CAP_FILE_SHARED) || |