diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-09-07 13:57:59 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-09-07 13:57:59 -0400 |
commit | a12ed06ba2d3fa60e08e7449fe0c1715de401395 (patch) | |
tree | 340345c6d793f22f6e686402e01c0a131db9994b /drivers | |
parent | d042a240a823d4c048c4b0a5c19c65ac75b7addb (diff) | |
parent | e92c0eaf754310f9f31e9229a3f7274a67478f82 (diff) |
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()
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/block/rbd.c | 235 |
1 files changed, 178 insertions, 57 deletions
diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 7915f3b03736..73ed5f3a862d 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c | |||
@@ -4207,11 +4207,13 @@ static ssize_t rbd_parent_show(struct device *dev, | |||
4207 | 4207 | ||
4208 | count += sprintf(&buf[count], "%s" | 4208 | count += sprintf(&buf[count], "%s" |
4209 | "pool_id %llu\npool_name %s\n" | 4209 | "pool_id %llu\npool_name %s\n" |
4210 | "pool_ns %s\n" | ||
4210 | "image_id %s\nimage_name %s\n" | 4211 | "image_id %s\nimage_name %s\n" |
4211 | "snap_id %llu\nsnap_name %s\n" | 4212 | "snap_id %llu\nsnap_name %s\n" |
4212 | "overlap %llu\n", | 4213 | "overlap %llu\n", |
4213 | !count ? "" : "\n", /* first? */ | 4214 | !count ? "" : "\n", /* first? */ |
4214 | spec->pool_id, spec->pool_name, | 4215 | spec->pool_id, spec->pool_name, |
4216 | spec->pool_ns ?: "", | ||
4215 | spec->image_id, spec->image_name ?: "(unknown)", | 4217 | spec->image_id, spec->image_name ?: "(unknown)", |
4216 | spec->snap_id, spec->snap_name, | 4218 | spec->snap_id, spec->snap_name, |
4217 | rbd_dev->parent_overlap); | 4219 | rbd_dev->parent_overlap); |
@@ -4584,47 +4586,177 @@ static int rbd_dev_v2_features(struct rbd_device *rbd_dev) | |||
4584 | &rbd_dev->header.features); | 4586 | &rbd_dev->header.features); |
4585 | } | 4587 | } |
4586 | 4588 | ||
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 | |||
4587 | static int rbd_dev_v2_parent_info(struct rbd_device *rbd_dev) | 4741 | static int rbd_dev_v2_parent_info(struct rbd_device *rbd_dev) |
4588 | { | 4742 | { |
4589 | struct rbd_spec *parent_spec; | 4743 | struct rbd_spec *parent_spec; |
4590 | size_t size; | 4744 | struct parent_image_info pii = { 0 }; |
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; | ||
4599 | int ret; | 4745 | int ret; |
4600 | 4746 | ||
4601 | parent_spec = rbd_spec_alloc(); | 4747 | parent_spec = rbd_spec_alloc(); |
4602 | if (!parent_spec) | 4748 | if (!parent_spec) |
4603 | return -ENOMEM; | 4749 | return -ENOMEM; |
4604 | 4750 | ||
4605 | size = sizeof (__le64) + /* pool_id */ | 4751 | ret = get_parent_info(rbd_dev, &pii); |
4606 | sizeof (__le32) + RBD_IMAGE_ID_LEN_MAX + /* image_id */ | 4752 | if (ret) |
4607 | sizeof (__le64) + /* snap_id */ | ||
4608 | sizeof (__le64); /* overlap */ | ||
4609 | reply_buf = kmalloc(size, GFP_KERNEL); | ||
4610 | if (!reply_buf) { | ||
4611 | ret = -ENOMEM; | ||
4612 | goto out_err; | 4753 | goto out_err; |
4613 | } | ||
4614 | 4754 | ||
4615 | snapid = cpu_to_le64(rbd_dev->spec->snap_id); | 4755 | dout("%s pool_id %llu pool_ns %s image_id %s snap_id %llu has_overlap %d overlap %llu\n", |
4616 | ret = rbd_obj_method_sync(rbd_dev, &rbd_dev->header_oid, | 4756 | __func__, pii.pool_id, pii.pool_ns, pii.image_id, pii.snap_id, |
4617 | &rbd_dev->header_oloc, "get_parent", | 4757 | pii.has_overlap, pii.overlap); |
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; | ||
4622 | 4758 | ||
4623 | p = reply_buf; | 4759 | if (pii.pool_id == CEPH_NOPOOL || !pii.has_overlap) { |
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) { | ||
4628 | /* | 4760 | /* |
4629 | * Either the parent never existed, or we have | 4761 | * Either the parent never existed, or we have |
4630 | * record of it but the image got flattened so it no | 4762 | * 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) | |||
4633 | * overlap to 0. The effect of this is that all new | 4765 | * overlap to 0. The effect of this is that all new |
4634 | * requests will be treated as if the image had no | 4766 | * requests will be treated as if the image had no |
4635 | * parent. | 4767 | * 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. | ||
4636 | */ | 4772 | */ |
4637 | if (rbd_dev->parent_overlap) { | 4773 | if (rbd_dev->parent_overlap) { |
4638 | rbd_dev->parent_overlap = 0; | 4774 | rbd_dev->parent_overlap = 0; |
@@ -4647,51 +4783,36 @@ static int rbd_dev_v2_parent_info(struct rbd_device *rbd_dev) | |||
4647 | /* The ceph file layout needs to fit pool id in 32 bits */ | 4783 | /* The ceph file layout needs to fit pool id in 32 bits */ |
4648 | 4784 | ||
4649 | ret = -EIO; | 4785 | ret = -EIO; |
4650 | if (pool_id > (u64)U32_MAX) { | 4786 | if (pii.pool_id > (u64)U32_MAX) { |
4651 | rbd_warn(NULL, "parent pool id too large (%llu > %u)", | 4787 | 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); |
4653 | goto out_err; | 4789 | goto out_err; |
4654 | } | 4790 | } |
4655 | 4791 | ||
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 | |||
4664 | /* | 4792 | /* |
4665 | * The parent won't change (except when the clone is | 4793 | * The parent won't change (except when the clone is |
4666 | * flattened, already handled that). So we only need to | 4794 | * flattened, already handled that). So we only need to |
4667 | * record the parent spec we have not already done so. | 4795 | * record the parent spec we have not already done so. |
4668 | */ | 4796 | */ |
4669 | if (!rbd_dev->parent_spec) { | 4797 | if (!rbd_dev->parent_spec) { |
4670 | parent_spec->pool_id = pool_id; | 4798 | parent_spec->pool_id = pii.pool_id; |
4671 | parent_spec->image_id = image_id; | 4799 | if (pii.pool_ns && *pii.pool_ns) { |
4672 | parent_spec->snap_id = snap_id; | 4800 | parent_spec->pool_ns = pii.pool_ns; |
4673 | 4801 | pii.pool_ns = NULL; | |
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 | } | ||
4682 | } | 4802 | } |
4803 | parent_spec->image_id = pii.image_id; | ||
4804 | pii.image_id = NULL; | ||
4805 | parent_spec->snap_id = pii.snap_id; | ||
4683 | 4806 | ||
4684 | rbd_dev->parent_spec = parent_spec; | 4807 | rbd_dev->parent_spec = parent_spec; |
4685 | parent_spec = NULL; /* rbd_dev now owns this */ | 4808 | parent_spec = NULL; /* rbd_dev now owns this */ |
4686 | } else { | ||
4687 | kfree(image_id); | ||
4688 | } | 4809 | } |
4689 | 4810 | ||
4690 | /* | 4811 | /* |
4691 | * We always update the parent overlap. If it's zero we issue | 4812 | * We always update the parent overlap. If it's zero we issue |
4692 | * a warning, as we will proceed as if there was no parent. | 4813 | * a warning, as we will proceed as if there was no parent. |
4693 | */ | 4814 | */ |
4694 | if (!overlap) { | 4815 | if (!pii.overlap) { |
4695 | if (parent_spec) { | 4816 | if (parent_spec) { |
4696 | /* refresh, careful to warn just once */ | 4817 | /* refresh, careful to warn just once */ |
4697 | if (rbd_dev->parent_overlap) | 4818 | if (rbd_dev->parent_overlap) |
@@ -4702,14 +4823,14 @@ static int rbd_dev_v2_parent_info(struct rbd_device *rbd_dev) | |||
4702 | rbd_warn(rbd_dev, "clone is standalone (overlap 0)"); | 4823 | rbd_warn(rbd_dev, "clone is standalone (overlap 0)"); |
4703 | } | 4824 | } |
4704 | } | 4825 | } |
4705 | rbd_dev->parent_overlap = overlap; | 4826 | rbd_dev->parent_overlap = pii.overlap; |
4706 | 4827 | ||
4707 | out: | 4828 | out: |
4708 | ret = 0; | 4829 | ret = 0; |
4709 | out_err: | 4830 | out_err: |
4710 | kfree(reply_buf); | 4831 | kfree(pii.pool_ns); |
4832 | kfree(pii.image_id); | ||
4711 | rbd_spec_put(parent_spec); | 4833 | rbd_spec_put(parent_spec); |
4712 | |||
4713 | return ret; | 4834 | return ret; |
4714 | } | 4835 | } |
4715 | 4836 | ||