Skip to content

Commit a12ed06

Browse files
committed
Merge tag 'ceph-for-4.19-rc3' of https://github.com/ceph/ceph-client
Pull ceph fixes from Ilya Dryomov: "Two rbd patches to complete support for images within namespaces that went into -rc1 and a use-after-free fix. The rbd changes have been sitting in a branch for quite a while but couldn't be included into the -rc1 pull request because of a pending wire protocol backwards compatibility fixup that only got committed early this week" * tag 'ceph-for-4.19-rc3' of https://github.com/ceph/ceph-client: rbd: support cloning across namespaces rbd: factor out get_parent_info() ceph: avoid a use-after-free in ceph_destroy_options()
2 parents d042a24 + e92c0ea commit a12ed06

File tree

2 files changed

+189
-62
lines changed

2 files changed

+189
-62
lines changed

drivers/block/rbd.c

Lines changed: 178 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -4207,11 +4207,13 @@ static ssize_t rbd_parent_show(struct device *dev,
42074207

42084208
count += sprintf(&buf[count], "%s"
42094209
"pool_id %llu\npool_name %s\n"
4210+
"pool_ns %s\n"
42104211
"image_id %s\nimage_name %s\n"
42114212
"snap_id %llu\nsnap_name %s\n"
42124213
"overlap %llu\n",
42134214
!count ? "" : "\n", /* first? */
42144215
spec->pool_id, spec->pool_name,
4216+
spec->pool_ns ?: "",
42154217
spec->image_id, spec->image_name ?: "(unknown)",
42164218
spec->snap_id, spec->snap_name,
42174219
rbd_dev->parent_overlap);
@@ -4584,47 +4586,177 @@ static int rbd_dev_v2_features(struct rbd_device *rbd_dev)
45844586
&rbd_dev->header.features);
45854587
}
45864588

4589+
struct parent_image_info {
4590+
u64 pool_id;
4591+
const char *pool_ns;
4592+
const char *image_id;
4593+
u64 snap_id;
4594+
4595+
bool has_overlap;
4596+
u64 overlap;
4597+
};
4598+
4599+
/*
4600+
* The caller is responsible for @pii.
4601+
*/
4602+
static int decode_parent_image_spec(void **p, void *end,
4603+
struct parent_image_info *pii)
4604+
{
4605+
u8 struct_v;
4606+
u32 struct_len;
4607+
int ret;
4608+
4609+
ret = ceph_start_decoding(p, end, 1, "ParentImageSpec",
4610+
&struct_v, &struct_len);
4611+
if (ret)
4612+
return ret;
4613+
4614+
ceph_decode_64_safe(p, end, pii->pool_id, e_inval);
4615+
pii->pool_ns = ceph_extract_encoded_string(p, end, NULL, GFP_KERNEL);
4616+
if (IS_ERR(pii->pool_ns)) {
4617+
ret = PTR_ERR(pii->pool_ns);
4618+
pii->pool_ns = NULL;
4619+
return ret;
4620+
}
4621+
pii->image_id = ceph_extract_encoded_string(p, end, NULL, GFP_KERNEL);
4622+
if (IS_ERR(pii->image_id)) {
4623+
ret = PTR_ERR(pii->image_id);
4624+
pii->image_id = NULL;
4625+
return ret;
4626+
}
4627+
ceph_decode_64_safe(p, end, pii->snap_id, e_inval);
4628+
return 0;
4629+
4630+
e_inval:
4631+
return -EINVAL;
4632+
}
4633+
4634+
static int __get_parent_info(struct rbd_device *rbd_dev,
4635+
struct page *req_page,
4636+
struct page *reply_page,
4637+
struct parent_image_info *pii)
4638+
{
4639+
struct ceph_osd_client *osdc = &rbd_dev->rbd_client->client->osdc;
4640+
size_t reply_len = PAGE_SIZE;
4641+
void *p, *end;
4642+
int ret;
4643+
4644+
ret = ceph_osdc_call(osdc, &rbd_dev->header_oid, &rbd_dev->header_oloc,
4645+
"rbd", "parent_get", CEPH_OSD_FLAG_READ,
4646+
req_page, sizeof(u64), reply_page, &reply_len);
4647+
if (ret)
4648+
return ret == -EOPNOTSUPP ? 1 : ret;
4649+
4650+
p = page_address(reply_page);
4651+
end = p + reply_len;
4652+
ret = decode_parent_image_spec(&p, end, pii);
4653+
if (ret)
4654+
return ret;
4655+
4656+
ret = ceph_osdc_call(osdc, &rbd_dev->header_oid, &rbd_dev->header_oloc,
4657+
"rbd", "parent_overlap_get", CEPH_OSD_FLAG_READ,
4658+
req_page, sizeof(u64), reply_page, &reply_len);
4659+
if (ret)
4660+
return ret;
4661+
4662+
p = page_address(reply_page);
4663+
end = p + reply_len;
4664+
ceph_decode_8_safe(&p, end, pii->has_overlap, e_inval);
4665+
if (pii->has_overlap)
4666+
ceph_decode_64_safe(&p, end, pii->overlap, e_inval);
4667+
4668+
return 0;
4669+
4670+
e_inval:
4671+
return -EINVAL;
4672+
}
4673+
4674+
/*
4675+
* The caller is responsible for @pii.
4676+
*/
4677+
static int __get_parent_info_legacy(struct rbd_device *rbd_dev,
4678+
struct page *req_page,
4679+
struct page *reply_page,
4680+
struct parent_image_info *pii)
4681+
{
4682+
struct ceph_osd_client *osdc = &rbd_dev->rbd_client->client->osdc;
4683+
size_t reply_len = PAGE_SIZE;
4684+
void *p, *end;
4685+
int ret;
4686+
4687+
ret = ceph_osdc_call(osdc, &rbd_dev->header_oid, &rbd_dev->header_oloc,
4688+
"rbd", "get_parent", CEPH_OSD_FLAG_READ,
4689+
req_page, sizeof(u64), reply_page, &reply_len);
4690+
if (ret)
4691+
return ret;
4692+
4693+
p = page_address(reply_page);
4694+
end = p + reply_len;
4695+
ceph_decode_64_safe(&p, end, pii->pool_id, e_inval);
4696+
pii->image_id = ceph_extract_encoded_string(&p, end, NULL, GFP_KERNEL);
4697+
if (IS_ERR(pii->image_id)) {
4698+
ret = PTR_ERR(pii->image_id);
4699+
pii->image_id = NULL;
4700+
return ret;
4701+
}
4702+
ceph_decode_64_safe(&p, end, pii->snap_id, e_inval);
4703+
pii->has_overlap = true;
4704+
ceph_decode_64_safe(&p, end, pii->overlap, e_inval);
4705+
4706+
return 0;
4707+
4708+
e_inval:
4709+
return -EINVAL;
4710+
}
4711+
4712+
static int get_parent_info(struct rbd_device *rbd_dev,
4713+
struct parent_image_info *pii)
4714+
{
4715+
struct page *req_page, *reply_page;
4716+
void *p;
4717+
int ret;
4718+
4719+
req_page = alloc_page(GFP_KERNEL);
4720+
if (!req_page)
4721+
return -ENOMEM;
4722+
4723+
reply_page = alloc_page(GFP_KERNEL);
4724+
if (!reply_page) {
4725+
__free_page(req_page);
4726+
return -ENOMEM;
4727+
}
4728+
4729+
p = page_address(req_page);
4730+
ceph_encode_64(&p, rbd_dev->spec->snap_id);
4731+
ret = __get_parent_info(rbd_dev, req_page, reply_page, pii);
4732+
if (ret > 0)
4733+
ret = __get_parent_info_legacy(rbd_dev, req_page, reply_page,
4734+
pii);
4735+
4736+
__free_page(req_page);
4737+
__free_page(reply_page);
4738+
return ret;
4739+
}
4740+
45874741
static int rbd_dev_v2_parent_info(struct rbd_device *rbd_dev)
45884742
{
45894743
struct rbd_spec *parent_spec;
4590-
size_t size;
4591-
void *reply_buf = NULL;
4592-
__le64 snapid;
4593-
void *p;
4594-
void *end;
4595-
u64 pool_id;
4596-
char *image_id;
4597-
u64 snap_id;
4598-
u64 overlap;
4744+
struct parent_image_info pii = { 0 };
45994745
int ret;
46004746

46014747
parent_spec = rbd_spec_alloc();
46024748
if (!parent_spec)
46034749
return -ENOMEM;
46044750

4605-
size = sizeof (__le64) + /* pool_id */
4606-
sizeof (__le32) + RBD_IMAGE_ID_LEN_MAX + /* image_id */
4607-
sizeof (__le64) + /* snap_id */
4608-
sizeof (__le64); /* overlap */
4609-
reply_buf = kmalloc(size, GFP_KERNEL);
4610-
if (!reply_buf) {
4611-
ret = -ENOMEM;
4751+
ret = get_parent_info(rbd_dev, &pii);
4752+
if (ret)
46124753
goto out_err;
4613-
}
46144754

4615-
snapid = cpu_to_le64(rbd_dev->spec->snap_id);
4616-
ret = rbd_obj_method_sync(rbd_dev, &rbd_dev->header_oid,
4617-
&rbd_dev->header_oloc, "get_parent",
4618-
&snapid, sizeof(snapid), reply_buf, size);
4619-
dout("%s: rbd_obj_method_sync returned %d\n", __func__, ret);
4620-
if (ret < 0)
4621-
goto out_err;
4755+
dout("%s pool_id %llu pool_ns %s image_id %s snap_id %llu has_overlap %d overlap %llu\n",
4756+
__func__, pii.pool_id, pii.pool_ns, pii.image_id, pii.snap_id,
4757+
pii.has_overlap, pii.overlap);
46224758

4623-
p = reply_buf;
4624-
end = reply_buf + ret;
4625-
ret = -ERANGE;
4626-
ceph_decode_64_safe(&p, end, pool_id, out_err);
4627-
if (pool_id == CEPH_NOPOOL) {
4759+
if (pii.pool_id == CEPH_NOPOOL || !pii.has_overlap) {
46284760
/*
46294761
* Either the parent never existed, or we have
46304762
* record of it but the image got flattened so it no
@@ -4633,6 +4765,10 @@ static int rbd_dev_v2_parent_info(struct rbd_device *rbd_dev)
46334765
* overlap to 0. The effect of this is that all new
46344766
* requests will be treated as if the image had no
46354767
* parent.
4768+
*
4769+
* If !pii.has_overlap, the parent image spec is not
4770+
* applicable. It's there to avoid duplication in each
4771+
* snapshot record.
46364772
*/
46374773
if (rbd_dev->parent_overlap) {
46384774
rbd_dev->parent_overlap = 0;
@@ -4647,51 +4783,36 @@ static int rbd_dev_v2_parent_info(struct rbd_device *rbd_dev)
46474783
/* The ceph file layout needs to fit pool id in 32 bits */
46484784

46494785
ret = -EIO;
4650-
if (pool_id > (u64)U32_MAX) {
4786+
if (pii.pool_id > (u64)U32_MAX) {
46514787
rbd_warn(NULL, "parent pool id too large (%llu > %u)",
4652-
(unsigned long long)pool_id, U32_MAX);
4788+
(unsigned long long)pii.pool_id, U32_MAX);
46534789
goto out_err;
46544790
}
46554791

4656-
image_id = ceph_extract_encoded_string(&p, end, NULL, GFP_KERNEL);
4657-
if (IS_ERR(image_id)) {
4658-
ret = PTR_ERR(image_id);
4659-
goto out_err;
4660-
}
4661-
ceph_decode_64_safe(&p, end, snap_id, out_err);
4662-
ceph_decode_64_safe(&p, end, overlap, out_err);
4663-
46644792
/*
46654793
* The parent won't change (except when the clone is
46664794
* flattened, already handled that). So we only need to
46674795
* record the parent spec we have not already done so.
46684796
*/
46694797
if (!rbd_dev->parent_spec) {
4670-
parent_spec->pool_id = pool_id;
4671-
parent_spec->image_id = image_id;
4672-
parent_spec->snap_id = snap_id;
4673-
4674-
/* TODO: support cloning across namespaces */
4675-
if (rbd_dev->spec->pool_ns) {
4676-
parent_spec->pool_ns = kstrdup(rbd_dev->spec->pool_ns,
4677-
GFP_KERNEL);
4678-
if (!parent_spec->pool_ns) {
4679-
ret = -ENOMEM;
4680-
goto out_err;
4681-
}
4798+
parent_spec->pool_id = pii.pool_id;
4799+
if (pii.pool_ns && *pii.pool_ns) {
4800+
parent_spec->pool_ns = pii.pool_ns;
4801+
pii.pool_ns = NULL;
46824802
}
4803+
parent_spec->image_id = pii.image_id;
4804+
pii.image_id = NULL;
4805+
parent_spec->snap_id = pii.snap_id;
46834806

46844807
rbd_dev->parent_spec = parent_spec;
46854808
parent_spec = NULL; /* rbd_dev now owns this */
4686-
} else {
4687-
kfree(image_id);
46884809
}
46894810

46904811
/*
46914812
* We always update the parent overlap. If it's zero we issue
46924813
* a warning, as we will proceed as if there was no parent.
46934814
*/
4694-
if (!overlap) {
4815+
if (!pii.overlap) {
46954816
if (parent_spec) {
46964817
/* refresh, careful to warn just once */
46974818
if (rbd_dev->parent_overlap)
@@ -4702,14 +4823,14 @@ static int rbd_dev_v2_parent_info(struct rbd_device *rbd_dev)
47024823
rbd_warn(rbd_dev, "clone is standalone (overlap 0)");
47034824
}
47044825
}
4705-
rbd_dev->parent_overlap = overlap;
4826+
rbd_dev->parent_overlap = pii.overlap;
47064827

47074828
out:
47084829
ret = 0;
47094830
out_err:
4710-
kfree(reply_buf);
4831+
kfree(pii.pool_ns);
4832+
kfree(pii.image_id);
47114833
rbd_spec_put(parent_spec);
4712-
47134834
return ret;
47144835
}
47154836

fs/ceph/super.c

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -602,24 +602,29 @@ static int extra_mon_dispatch(struct ceph_client *client, struct ceph_msg *msg)
602602

603603
/*
604604
* create a new fs client
605+
*
606+
* Success or not, this function consumes @fsopt and @opt.
605607
*/
606608
static struct ceph_fs_client *create_fs_client(struct ceph_mount_options *fsopt,
607609
struct ceph_options *opt)
608610
{
609611
struct ceph_fs_client *fsc;
610612
int page_count;
611613
size_t size;
612-
int err = -ENOMEM;
614+
int err;
613615

614616
fsc = kzalloc(sizeof(*fsc), GFP_KERNEL);
615-
if (!fsc)
616-
return ERR_PTR(-ENOMEM);
617+
if (!fsc) {
618+
err = -ENOMEM;
619+
goto fail;
620+
}
617621

618622
fsc->client = ceph_create_client(opt, fsc);
619623
if (IS_ERR(fsc->client)) {
620624
err = PTR_ERR(fsc->client);
621625
goto fail;
622626
}
627+
opt = NULL; /* fsc->client now owns this */
623628

624629
fsc->client->extra_mon_dispatch = extra_mon_dispatch;
625630
fsc->client->osdc.abort_on_full = true;
@@ -677,6 +682,9 @@ static struct ceph_fs_client *create_fs_client(struct ceph_mount_options *fsopt,
677682
ceph_destroy_client(fsc->client);
678683
fail:
679684
kfree(fsc);
685+
if (opt)
686+
ceph_destroy_options(opt);
687+
destroy_mount_options(fsopt);
680688
return ERR_PTR(err);
681689
}
682690

@@ -1042,8 +1050,6 @@ static struct dentry *ceph_mount(struct file_system_type *fs_type,
10421050
fsc = create_fs_client(fsopt, opt);
10431051
if (IS_ERR(fsc)) {
10441052
res = ERR_CAST(fsc);
1045-
destroy_mount_options(fsopt);
1046-
ceph_destroy_options(opt);
10471053
goto out_final;
10481054
}
10491055

0 commit comments

Comments
 (0)