Message ID | 20190417183959.11296-5-jlayton@kernel.org (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | ceph: dentry->d_name handing fixes | expand |
On Wed, Apr 17, 2019 at 2:40 PM Jeff Layton <jlayton@kernel.org> wrote: > > 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. > > Reported-by: "Yan, Zheng" <zyan@redhat.com> > Signed-off-by: Jeff Layton <jlayton@kernel.org> > --- > fs/ceph/inode.c | 17 ++++++++++++++++- > 1 file changed, 16 insertions(+), 1 deletion(-) > > diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c > index b33ba16f7ae8..e1ac10f960dd 100644 > --- a/fs/ceph/inode.c > +++ b/fs/ceph/inode.c > @@ -1163,6 +1163,20 @@ static int splice_dentry(struct dentry **pdn, struct inode *in) > return 0; > } > > +static bool Probably should make this return int instead. I'll change that in tree, but it shouldn't change the code materially. > +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., > @@ -1412,7 +1426,8 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req) > 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) || > -- > 2.20.1 >
On Thu, Apr 18, 2019 at 2:42 AM Jeff Layton <jlayton@kernel.org> wrote: > > 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. > > Reported-by: "Yan, Zheng" <zyan@redhat.com> > Signed-off-by: Jeff Layton <jlayton@kernel.org> > --- > fs/ceph/inode.c | 17 ++++++++++++++++- > 1 file changed, 16 insertions(+), 1 deletion(-) > > diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c > index b33ba16f7ae8..e1ac10f960dd 100644 > --- a/fs/ceph/inode.c > +++ b/fs/ceph/inode.c > @@ -1163,6 +1163,20 @@ static int splice_dentry(struct dentry **pdn, struct inode *in) > return 0; > } > > +static bool > +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., > @@ -1412,7 +1426,8 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req) > 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) || > -- > 2.20.1 > Reviewed-by: "Yan, Zheng" <zyan@redhat.com>
diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c index b33ba16f7ae8..e1ac10f960dd 100644 --- a/fs/ceph/inode.c +++ b/fs/ceph/inode.c @@ -1163,6 +1163,20 @@ static int splice_dentry(struct dentry **pdn, struct inode *in) return 0; } +static bool +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., @@ -1412,7 +1426,8 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req) 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) ||
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. Reported-by: "Yan, Zheng" <zyan@redhat.com> Signed-off-by: Jeff Layton <jlayton@kernel.org> --- fs/ceph/inode.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-)