Commit f5d55f03 authored by Jeff Layton's avatar Jeff Layton Committed by Ilya Dryomov

ceph: vet the target and parent inodes before updating dentry lease

In a later patch, we're going to need to allow ceph_fill_trace to
update the dentry's lease when the parent is not locked. This is
potentially racy though -- by the time we get around to processing the
trace, the parent may have already changed.

Change update_dentry_lease to take a ceph_vino pointer and use that to
ensure that the dentry's parent still matches it before updating the
lease.
Signed-off-by: default avatarJeff Layton <jlayton@redhat.com>
Reviewed-by: default avatarYan, Zheng <zyan@redhat.com>
Signed-off-by: default avatarIlya Dryomov <idryomov@gmail.com>
parent 80d025ff
...@@ -1016,7 +1016,9 @@ static int fill_inode(struct inode *inode, struct page *locked_page, ...@@ -1016,7 +1016,9 @@ static int fill_inode(struct inode *inode, struct page *locked_page,
static void update_dentry_lease(struct dentry *dentry, static void update_dentry_lease(struct dentry *dentry,
struct ceph_mds_reply_lease *lease, struct ceph_mds_reply_lease *lease,
struct ceph_mds_session *session, struct ceph_mds_session *session,
unsigned long from_time) unsigned long from_time,
struct ceph_vino *tgt_vino,
struct ceph_vino *dir_vino)
{ {
struct ceph_dentry_info *di = ceph_dentry(dentry); struct ceph_dentry_info *di = ceph_dentry(dentry);
long unsigned duration = le32_to_cpu(lease->duration_ms); long unsigned duration = le32_to_cpu(lease->duration_ms);
...@@ -1024,13 +1026,27 @@ static void update_dentry_lease(struct dentry *dentry, ...@@ -1024,13 +1026,27 @@ static void update_dentry_lease(struct dentry *dentry,
long unsigned half_ttl = from_time + (duration * HZ / 2) / 1000; long unsigned half_ttl = from_time + (duration * HZ / 2) / 1000;
struct inode *dir; struct inode *dir;
/*
* Make sure dentry's inode matches tgt_vino. NULL tgt_vino means that
* we expect a negative dentry.
*/
if (!tgt_vino && d_really_is_positive(dentry))
return;
if (tgt_vino && (d_really_is_negative(dentry) ||
!ceph_ino_compare(d_inode(dentry), tgt_vino)))
return;
spin_lock(&dentry->d_lock); spin_lock(&dentry->d_lock);
dout("update_dentry_lease %p duration %lu ms ttl %lu\n", dout("update_dentry_lease %p duration %lu ms ttl %lu\n",
dentry, duration, ttl); dentry, duration, ttl);
/* make lease_rdcache_gen match directory */
dir = d_inode(dentry->d_parent); dir = d_inode(dentry->d_parent);
/* make sure parent matches dir_vino */
if (!ceph_ino_compare(dir, dir_vino))
goto out_unlock;
/* only track leases on regular dentries */ /* only track leases on regular dentries */
if (ceph_snap(dir) != CEPH_NOSNAP) if (ceph_snap(dir) != CEPH_NOSNAP)
goto out_unlock; goto out_unlock;
...@@ -1113,7 +1129,7 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req) ...@@ -1113,7 +1129,7 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req)
struct ceph_mds_session *session = req->r_session; struct ceph_mds_session *session = req->r_session;
struct ceph_mds_reply_info_parsed *rinfo = &req->r_reply_info; struct ceph_mds_reply_info_parsed *rinfo = &req->r_reply_info;
struct inode *in = NULL; struct inode *in = NULL;
struct ceph_vino vino; struct ceph_vino tvino, dvino;
struct ceph_fs_client *fsc = ceph_sb_to_client(sb); struct ceph_fs_client *fsc = ceph_sb_to_client(sb);
int err = 0; int err = 0;
...@@ -1154,8 +1170,8 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req) ...@@ -1154,8 +1170,8 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req)
dname.name = rinfo->dname; dname.name = rinfo->dname;
dname.len = rinfo->dname_len; dname.len = rinfo->dname_len;
dname.hash = full_name_hash(parent, dname.name, dname.len); dname.hash = full_name_hash(parent, dname.name, dname.len);
vino.ino = le64_to_cpu(rinfo->targeti.in->ino); tvino.ino = le64_to_cpu(rinfo->targeti.in->ino);
vino.snap = le64_to_cpu(rinfo->targeti.in->snapid); tvino.snap = le64_to_cpu(rinfo->targeti.in->snapid);
retry_lookup: retry_lookup:
dn = d_lookup(parent, &dname); dn = d_lookup(parent, &dname);
dout("d_lookup on parent=%p name=%.*s got %p\n", dout("d_lookup on parent=%p name=%.*s got %p\n",
...@@ -1172,8 +1188,8 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req) ...@@ -1172,8 +1188,8 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req)
} }
err = 0; err = 0;
} else if (d_really_is_positive(dn) && } else if (d_really_is_positive(dn) &&
(ceph_ino(d_inode(dn)) != vino.ino || (ceph_ino(d_inode(dn)) != tvino.ino ||
ceph_snap(d_inode(dn)) != vino.snap)) { ceph_snap(d_inode(dn)) != tvino.snap)) {
dout(" dn %p points to wrong inode %p\n", dout(" dn %p points to wrong inode %p\n",
dn, d_inode(dn)); dn, d_inode(dn));
d_delete(dn); d_delete(dn);
...@@ -1187,10 +1203,10 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req) ...@@ -1187,10 +1203,10 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req)
} }
if (rinfo->head->is_target) { if (rinfo->head->is_target) {
vino.ino = le64_to_cpu(rinfo->targeti.in->ino); tvino.ino = le64_to_cpu(rinfo->targeti.in->ino);
vino.snap = le64_to_cpu(rinfo->targeti.in->snapid); tvino.snap = le64_to_cpu(rinfo->targeti.in->snapid);
in = ceph_get_inode(sb, vino); in = ceph_get_inode(sb, tvino);
if (IS_ERR(in)) { if (IS_ERR(in)) {
err = PTR_ERR(in); err = PTR_ERR(in);
goto done; goto done;
...@@ -1231,10 +1247,12 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req) ...@@ -1231,10 +1247,12 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req)
BUG_ON(!dn); BUG_ON(!dn);
BUG_ON(!dir); BUG_ON(!dir);
BUG_ON(d_inode(dn->d_parent) != dir); BUG_ON(d_inode(dn->d_parent) != dir);
BUG_ON(ceph_ino(dir) !=
le64_to_cpu(rinfo->diri.in->ino)); dvino.ino = le64_to_cpu(rinfo->diri.in->ino);
BUG_ON(ceph_snap(dir) != dvino.snap = le64_to_cpu(rinfo->diri.in->snapid);
le64_to_cpu(rinfo->diri.in->snapid));
BUG_ON(ceph_ino(dir) != dvino.ino);
BUG_ON(ceph_snap(dir) != dvino.snap);
/* do we have a lease on the whole dir? */ /* do we have a lease on the whole dir? */
have_dir_cap = have_dir_cap =
...@@ -1291,7 +1309,8 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req) ...@@ -1291,7 +1309,8 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req)
d_add(dn, NULL); d_add(dn, NULL);
update_dentry_lease(dn, rinfo->dlease, update_dentry_lease(dn, rinfo->dlease,
session, session,
req->r_request_started); req->r_request_started,
NULL, &dvino);
} }
goto done; goto done;
} }
...@@ -1314,9 +1333,13 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req) ...@@ -1314,9 +1333,13 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req)
have_lease = false; have_lease = false;
} }
if (have_lease) if (have_lease) {
tvino.ino = le64_to_cpu(rinfo->targeti.in->ino);
tvino.snap = le64_to_cpu(rinfo->targeti.in->snapid);
update_dentry_lease(dn, rinfo->dlease, session, update_dentry_lease(dn, rinfo->dlease, session,
req->r_request_started); req->r_request_started,
&tvino, &dvino);
}
dout(" final dn %p\n", dn); dout(" final dn %p\n", dn);
} else if ((req->r_op == CEPH_MDS_OP_LOOKUPSNAP || } else if ((req->r_op == CEPH_MDS_OP_LOOKUPSNAP ||
req->r_op == CEPH_MDS_OP_MKSNAP) && req->r_op == CEPH_MDS_OP_MKSNAP) &&
...@@ -1490,14 +1513,14 @@ int ceph_readdir_prepopulate(struct ceph_mds_request *req, ...@@ -1490,14 +1513,14 @@ int ceph_readdir_prepopulate(struct ceph_mds_request *req,
/* FIXME: release caps/leases if error occurs */ /* FIXME: release caps/leases if error occurs */
for (i = 0; i < rinfo->dir_nr; i++) { for (i = 0; i < rinfo->dir_nr; i++) {
struct ceph_mds_reply_dir_entry *rde = rinfo->dir_entries + i; struct ceph_mds_reply_dir_entry *rde = rinfo->dir_entries + i;
struct ceph_vino vino; struct ceph_vino tvino, dvino;
dname.name = rde->name; dname.name = rde->name;
dname.len = rde->name_len; dname.len = rde->name_len;
dname.hash = full_name_hash(parent, dname.name, dname.len); dname.hash = full_name_hash(parent, dname.name, dname.len);
vino.ino = le64_to_cpu(rde->inode.in->ino); tvino.ino = le64_to_cpu(rde->inode.in->ino);
vino.snap = le64_to_cpu(rde->inode.in->snapid); tvino.snap = le64_to_cpu(rde->inode.in->snapid);
if (rinfo->hash_order) { if (rinfo->hash_order) {
u32 hash = ceph_str_hash(ci->i_dir_layout.dl_dir_hash, u32 hash = ceph_str_hash(ci->i_dir_layout.dl_dir_hash,
...@@ -1526,8 +1549,8 @@ int ceph_readdir_prepopulate(struct ceph_mds_request *req, ...@@ -1526,8 +1549,8 @@ int ceph_readdir_prepopulate(struct ceph_mds_request *req,
goto out; goto out;
} }
} else if (d_really_is_positive(dn) && } else if (d_really_is_positive(dn) &&
(ceph_ino(d_inode(dn)) != vino.ino || (ceph_ino(d_inode(dn)) != tvino.ino ||
ceph_snap(d_inode(dn)) != vino.snap)) { ceph_snap(d_inode(dn)) != tvino.snap)) {
dout(" dn %p points to wrong inode %p\n", dout(" dn %p points to wrong inode %p\n",
dn, d_inode(dn)); dn, d_inode(dn));
d_delete(dn); d_delete(dn);
...@@ -1539,7 +1562,7 @@ int ceph_readdir_prepopulate(struct ceph_mds_request *req, ...@@ -1539,7 +1562,7 @@ int ceph_readdir_prepopulate(struct ceph_mds_request *req,
if (d_really_is_positive(dn)) { if (d_really_is_positive(dn)) {
in = d_inode(dn); in = d_inode(dn);
} else { } else {
in = ceph_get_inode(parent->d_sb, vino); in = ceph_get_inode(parent->d_sb, tvino);
if (IS_ERR(in)) { if (IS_ERR(in)) {
dout("new_inode badness\n"); dout("new_inode badness\n");
d_drop(dn); d_drop(dn);
...@@ -1584,8 +1607,9 @@ int ceph_readdir_prepopulate(struct ceph_mds_request *req, ...@@ -1584,8 +1607,9 @@ int ceph_readdir_prepopulate(struct ceph_mds_request *req,
ceph_dentry(dn)->offset = rde->offset; ceph_dentry(dn)->offset = rde->offset;
dvino = ceph_vino(d_inode(parent));
update_dentry_lease(dn, rde->lease, req->r_session, update_dentry_lease(dn, rde->lease, req->r_session,
req->r_request_started); req->r_request_started, &tvino, &dvino);
if (err == 0 && skipped == 0 && cache_ctl.index >= 0) { if (err == 0 && skipped == 0 && cache_ctl.index >= 0) {
ret = fill_readdir_cache(d_inode(parent), dn, ret = fill_readdir_cache(d_inode(parent), dn,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment