return 0;
}
- auto [data_size, data_area] = io::util::raw_to_area_offset(*this, raw_size);
- ceph_assert(data_size <= raw_size && data_area == io::ImageArea::DATA);
+ auto size = io::util::raw_to_area_offset(*this, raw_size);
+ ceph_assert(size.first <= raw_size && size.second == io::ImageArea::DATA);
switch (area) {
case io::ImageArea::DATA:
- return data_size;
+ return size.first;
case io::ImageArea::CRYPTO_HEADER:
// CRYPTO_HEADER area ends where DATA area begins
- return raw_size - data_size;
+ return raw_size - size.first;
default:
ceph_abort();
}
if (migration_write) {
// don't reduce migration write overlap -- it may be larger as
// it's the largest overlap across snapshots by construction
- return io::util::remap_offset(*this, raw_overlap);
+ return io::util::raw_to_area_offset(*this, raw_overlap);
}
if (raw_overlap == 0 || parent == nullptr) {
// image opened with OPEN_FLAG_SKIP_OPEN_PARENT -> no overlap
- return io::util::remap_offset(*this, 0);
+ return io::util::raw_to_area_offset(*this, 0);
}
// DATA area in the parent may be smaller than the part of DATA
// area in the clone that is still within the overlap (e.g. for
// LUKS2-encrypted parent + LUKS1-encrypted clone, due to LUKS2
// header usually being bigger than LUKS1 header)
- auto overlap = io::util::remap_offset(*this, raw_overlap);
+ auto overlap = io::util::raw_to_area_offset(*this, raw_overlap);
std::shared_lock parent_image_locker(parent->image_lock);
overlap.first = std::min(overlap.first,
parent->get_area_size(overlap.second));
return overlap;
}
- void ImageCtx::register_watch(Context *on_finish) {
- ceph_assert(image_watcher != NULL);
- image_watcher->register_watch(on_finish);
- }
+ uint64_t ImageCtx::prune_parent_extents(io::Extents& image_extents,
+ io::ImageArea area,
+ uint64_t raw_overlap,
+ bool migration_write) const {
+ ceph_assert(ceph_mutex_is_locked(image_lock));
+ ldout(cct, 10) << __func__ << ": image_extents=" << image_extents
+ << " area=" << area << " raw_overlap=" << raw_overlap
+ << " migration_write=" << migration_write << dendl;
+ if (raw_overlap == 0) {
+ image_extents.clear();
+ return 0;
+ }
- uint64_t ImageCtx::prune_parent_extents(vector<pair<uint64_t,uint64_t> >& objectx,
- uint64_t overlap)
- {
- // drop extents completely beyond the overlap
- while (!objectx.empty() && objectx.back().first >= overlap)
- objectx.pop_back();
+ auto overlap = reduce_parent_overlap(raw_overlap, migration_write);
+ if (area == overlap.second) {
+ // drop extents completely beyond the overlap
+ while (!image_extents.empty() &&
+ image_extents.back().first >= overlap.first) {
+ image_extents.pop_back();
+ }
+ if (!image_extents.empty()) {
+ // trim final overlapping extent
+ auto& last_extent = image_extents.back();
+ if (last_extent.first + last_extent.second > overlap.first) {
+ last_extent.second = overlap.first - last_extent.first;
+ }
+ }
+ } else if (area == io::ImageArea::DATA &&
+ overlap.second == io::ImageArea::CRYPTO_HEADER) {
+ // all extents completely beyond the overlap
+ image_extents.clear();
+ } else {
+ // all extents completely within the overlap
+ ceph_assert(area == io::ImageArea::CRYPTO_HEADER &&
+ overlap.second == io::ImageArea::DATA);
+ }
- // trim final overlapping extent
- if (!objectx.empty() && objectx.back().first + objectx.back().second > overlap)
- objectx.back().second = overlap - objectx.back().first;
+ uint64_t overlap_bytes = 0;
+ for (auto [_, len] : image_extents) {
+ overlap_bytes += len;
+ }
+ ldout(cct, 10) << __func__ << ": overlap=" << overlap.first
+ << "/" << overlap.second
+ << " got overlap_bytes=" << overlap_bytes
+ << " at " << image_extents << dendl;
+ return overlap_bytes;
+ }
- uint64_t len = 0;
- for (vector<pair<uint64_t,uint64_t> >::iterator p = objectx.begin();
- p != objectx.end();
- ++p)
- len += p->second;
- ldout(cct, 10) << "prune_parent_extents image overlap " << overlap
- << ", object overlap " << len
- << " from image extents " << objectx << dendl;
- return len;
+ void ImageCtx::register_watch(Context *on_finish) {
+ ceph_assert(image_watcher != NULL);
+ image_watcher->register_watch(on_finish);
}
void ImageCtx::cancel_async_requests() {
uint64_t* raw_overlap) const;
std::pair<uint64_t, io::ImageArea> reduce_parent_overlap(
uint64_t raw_overlap, bool migration_write) const;
+ uint64_t prune_parent_extents(
+ std::vector<std::pair<uint64_t, uint64_t>>& image_extents,
+ io::ImageArea area, uint64_t raw_overlap, bool migration_write) const;
+
void register_watch(Context *on_finish);
- uint64_t prune_parent_extents(std::vector<std::pair<uint64_t,uint64_t> >& objectx,
- uint64_t overlap);
void cancel_async_requests();
void cancel_async_requests(Context *on_finish);
uint64_t read_len,
snapid_t snapid)
{
- m_ictx->image_lock.lock_shared();
- librados::snap_t snap_id = m_ictx->snap_id;
- uint64_t overlap = 0;
- m_ictx->get_parent_overlap(snap_id, &overlap);
- m_ictx->image_lock.unlock_shared();
-
- uint64_t object_no = oid_to_object_no(oid.name, m_ictx->object_prefix);
-
- // reverse map this object extent onto the parent
- auto [parent_extents, _] = io::util::object_to_area_extents(
- m_ictx, object_no, {{0, m_ictx->layout.object_size}});
- uint64_t object_overlap = m_ictx->prune_parent_extents(parent_extents,
- overlap);
+ std::shared_lock image_locker(m_ictx->image_lock);
+ uint64_t raw_overlap = 0;
+ uint64_t object_overlap = 0;
+ m_ictx->get_parent_overlap(m_ictx->snap_id, &raw_overlap);
+ if (raw_overlap > 0) {
+ uint64_t object_no = oid_to_object_no(oid.name, m_ictx->object_prefix);
+ auto [parent_extents, area] = io::util::object_to_area_extents(
+ m_ictx, object_no, {{0, m_ictx->layout.object_size}});
+ object_overlap = m_ictx->prune_parent_extents(parent_extents, area,
+ raw_overlap, false);
+ }
bool may = object_overlap > 0;
ldout(m_ictx->cct, 10) << "may_copy_on_write " << oid << " " << read_off
<< "~" << read_len << " = " << may << dendl;
WriteReadSnapIds write_read_snap_ids{src_snap_seq, src_snap_seq};
// prepare to prune the extents to the maximum parent overlap
- m_src_image_ctx->image_lock.lock_shared();
- uint64_t src_parent_overlap = 0;
- int r = m_src_image_ctx->get_parent_overlap(src_snap_seq,
- &src_parent_overlap);
- m_src_image_ctx->image_lock.unlock_shared();
-
+ std::shared_lock image_locker(m_src_image_ctx->image_lock);
+ uint64_t raw_overlap = 0;
+ int r = m_src_image_ctx->get_parent_overlap(src_snap_seq, &raw_overlap);
if (r < 0) {
ldout(m_cct, 5) << "failed getting parent overlap for snap_id: "
<< src_snap_seq << ": " << cpp_strerror(r) << dendl;
- } else {
- ldout(m_cct, 20) << "parent overlap=" << src_parent_overlap << dendl;
- for (auto& [image_offset, image_length] : dne_image_interval) {
- auto end_image_offset = std::min(
- image_offset + image_length, src_parent_overlap);
- if (image_offset >= end_image_offset) {
- // starting offset is beyond the end of the parent overlap
- continue;
- }
-
- image_length = end_image_offset - image_offset;
+ } else if (raw_overlap > 0) {
+ ldout(m_cct, 20) << "raw_overlap=" << raw_overlap << dendl;
+ io::Extents parent_extents;
+ for (auto [image_offset, image_length] : dne_image_interval) {
+ parent_extents.emplace_back(image_offset, image_length);
+ }
+ m_src_image_ctx->prune_parent_extents(parent_extents, m_image_area,
+ raw_overlap, false);
+ for (auto [image_offset, image_length] : parent_extents) {
ldout(m_cct, 20) << "parent read op: "
<< "snap_ids=" << write_read_snap_ids << " "
<< image_offset << "~" << image_length << dendl;
if (hide_parent) {
std::shared_lock image_locker{m_dst_image_ctx->image_lock};
- uint64_t parent_overlap = 0;
- int r = m_dst_image_ctx->get_parent_overlap(dst_snap_seq,
- &parent_overlap);
+ uint64_t raw_overlap = 0;
+ uint64_t object_overlap = 0;
+ int r = m_dst_image_ctx->get_parent_overlap(dst_snap_seq, &raw_overlap);
if (r < 0) {
ldout(m_cct, 5) << "failed getting parent overlap for snap_id: "
<< dst_snap_seq << ": " << cpp_strerror(r) << dendl;
+ } else if (raw_overlap > 0) {
+ auto parent_extents = m_image_extents;
+ object_overlap = m_dst_image_ctx->prune_parent_extents(
+ parent_extents, m_image_area, raw_overlap, false);
}
- if (parent_overlap == 0) {
+ if (object_overlap == 0) {
ldout(m_cct, 20) << "no parent overlap" << dendl;
hide_parent = false;
- } else {
- auto image_extents = m_image_extents;
- uint64_t overlap = m_dst_image_ctx->prune_parent_extents(
- image_extents, parent_overlap);
- if (overlap == 0) {
- ldout(m_cct, 20) << "no parent overlap" << dendl;
- hide_parent = false;
- }
}
}
return false;
}
- uint64_t parent_overlap = 0;
- int r = m_image_ctx->get_parent_overlap(snap_id, &parent_overlap);
+ uint64_t raw_overlap = 0;
+ uint64_t object_overlap = 0;
+ int r = m_image_ctx->get_parent_overlap(snap_id, &raw_overlap);
if (r < 0) {
ldout(cct, 5) << "failed getting parent overlap for snap_id: "
<< snap_id << ": " << cpp_strerror(r) << dendl;
+ } else if (raw_overlap > 0) {
+ auto [parent_extents, area] = util::object_to_area_extents(
+ m_image_ctx, m_object_no, {{0, m_image_ctx->layout.object_size}});
+ object_overlap = m_image_ctx->prune_parent_extents(parent_extents, area,
+ raw_overlap, false);
}
- if (parent_overlap == 0) {
- return false;
- }
- auto [parent_extents, _] = util::object_to_area_extents(
- m_image_ctx, m_object_no, {{0, m_image_ctx->layout.object_size}});
- auto overlap = m_image_ctx->prune_parent_extents(parent_extents,
- parent_overlap);
- return overlap > 0;
+ return object_overlap > 0;
});
}
parent_extents->clear();
*area = ImageArea::DATA;
- uint64_t parent_overlap;
+ uint64_t raw_overlap;
int r = m_ictx->get_parent_overlap(
- m_io_context->read_snap().value_or(CEPH_NOSNAP), &parent_overlap);
+ m_io_context->read_snap().value_or(CEPH_NOSNAP), &raw_overlap);
if (r < 0) {
// NOTE: it's possible for a snapshot to be deleted while we are
// still reading from it
<< cpp_strerror(r) << dendl;
return false;
}
-
- if (!read_request && !m_ictx->migration_info.empty()) {
- parent_overlap = m_ictx->migration_info.overlap;
+ bool migration_write = !read_request && !m_ictx->migration_info.empty();
+ if (migration_write) {
+ raw_overlap = m_ictx->migration_info.overlap;
}
-
- if (parent_overlap == 0) {
+ if (raw_overlap == 0) {
return false;
}
std::tie(*parent_extents, *area) = io::util::object_to_area_extents(
m_ictx, m_object_no, {{0, m_ictx->layout.object_size}});
- uint64_t object_overlap = m_ictx->prune_parent_extents(*parent_extents,
- parent_overlap);
+ uint64_t object_overlap = m_ictx->prune_parent_extents(
+ *parent_extents, *area, raw_overlap, migration_write);
if (object_overlap > 0) {
- ldout(m_ictx->cct, 20) << "overlap=" << parent_overlap
- << " extents=" << *parent_extents
- << " area=" << *area << dendl;
- m_has_parent = !parent_extents->empty();
+ m_has_parent = true;
return true;
}
return false;
return;
}
- // calculate reverse mapping onto the parent image
Extents parent_extents;
- std::tie(parent_extents, m_image_area) = io::util::object_to_area_extents(
- image_ctx, this->m_object_no, m_object_extents);
-
- uint64_t parent_overlap = 0;
+ uint64_t raw_overlap = 0;
uint64_t object_overlap = 0;
- int r = image_ctx->get_parent_overlap(snap_id_end, &parent_overlap);
- if (r == 0) {
- object_overlap = image_ctx->prune_parent_extents(parent_extents,
- parent_overlap);
+ image_ctx->get_parent_overlap(snap_id_end, &raw_overlap);
+ if (raw_overlap > 0) {
+ // calculate reverse mapping onto the parent image
+ std::tie(parent_extents, m_image_area) = io::util::object_to_area_extents(
+ image_ctx, this->m_object_no, m_object_extents);
+ object_overlap = image_ctx->prune_parent_extents(
+ parent_extents, m_image_area, raw_overlap, false);
}
-
if (object_overlap == 0) {
image_locker.unlock();
std::shared_lock image_locker{image_ctx->image_lock};
- // calculate reverse mapping onto the image
- Extents extents;
- for (const auto& extent : *read_extents) {
- extents.emplace_back(extent.offset, extent.length);
- }
- auto [parent_extents, area] = object_to_area_extents(image_ctx, object_no,
- extents);
-
- uint64_t parent_overlap = 0;
+ Extents parent_extents;
+ ImageArea area;
+ uint64_t raw_overlap = 0;
uint64_t object_overlap = 0;
- int r = image_ctx->get_parent_overlap(snap_id, &parent_overlap);
- if (r == 0) {
- object_overlap = image_ctx->prune_parent_extents(parent_extents,
- parent_overlap);
+ image_ctx->get_parent_overlap(snap_id, &raw_overlap);
+ if (raw_overlap > 0) {
+ // calculate reverse mapping onto the parent image
+ Extents extents;
+ for (const auto& extent : *read_extents) {
+ extents.emplace_back(extent.offset, extent.length);
+ }
+ std::tie(parent_extents, area) = object_to_area_extents(image_ctx,
+ object_no, extents);
+ object_overlap = image_ctx->prune_parent_extents(parent_extents, area,
+ raw_overlap, false);
}
-
if (object_overlap == 0) {
image_locker.unlock();
}
void expect_prune_parent_extents(uint64_t object_overlap) {
- EXPECT_CALL(*mock_image_ctx, prune_parent_extents(_, _))
+ EXPECT_CALL(*mock_image_ctx, prune_parent_extents(_, _, _, _))
.WillOnce(Return(object_overlap));
}
void expect_prune_parent_extents(MockTestImageCtx& mock_image_ctx,
uint64_t overlap, uint64_t object_overlap) {
- EXPECT_CALL(mock_image_ctx, prune_parent_extents(_, overlap))
+ EXPECT_CALL(mock_image_ctx, prune_parent_extents(_, _, overlap, _))
.WillOnce(WithoutArgs(Invoke([object_overlap]() {
return object_overlap;
})));
void expect_prune_parent_extents(MockTestImageCtx &mock_image_ctx,
const Extents& extents,
uint64_t overlap, uint64_t object_overlap) {
- EXPECT_CALL(mock_image_ctx, prune_parent_extents(_, overlap))
+ EXPECT_CALL(mock_image_ctx, prune_parent_extents(_, _, overlap, _))
.WillOnce(WithArg<0>(Invoke([extents, object_overlap](Extents& e) {
e = extents;
return object_overlap;
uint64_t *raw_overlap));
MOCK_CONST_METHOD2(reduce_parent_overlap,
std::pair<uint64_t, io::ImageArea>(uint64_t, bool));
- MOCK_CONST_METHOD2(prune_parent_extents, uint64_t(std::vector<std::pair<uint64_t,uint64_t> >& ,
- uint64_t));
+ MOCK_CONST_METHOD4(prune_parent_extents,
+ uint64_t(std::vector<std::pair<uint64_t, uint64_t>>&,
+ io::ImageArea, uint64_t, bool));
MOCK_CONST_METHOD2(is_snap_protected, int(librados::snap_t in_snap_id,
bool *is_protected));
rados_ioctx_destroy(ioctx);
}
+TEST_F(TestLibRBD, LUKS1UnderLUKS2WithoutResize)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+ REQUIRE(!is_feature_enabled(RBD_FEATURE_STRIPINGV2));
+ REQUIRE(!is_feature_enabled(RBD_FEATURE_JOURNALING));
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ std::string parent_name = get_temp_image_name();
+ std::string clone_name = get_temp_image_name();
+ uint64_t data_size = 25 << 20;
+ uint64_t luks1_meta_size = 4 << 20;
+ uint64_t luks2_meta_size = 16 << 20;
+ std::string parent_passphrase = "parent passphrase";
+ std::string clone_passphrase = "clone passphrase";
+
+ {
+ int order = 22;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, parent_name.c_str(),
+ luks1_meta_size + data_size, &order));
+ librbd::Image parent;
+ ASSERT_EQ(0, rbd.open(ioctx, parent, parent_name.c_str(), nullptr));
+
+ librbd::encryption_luks1_format_options_t fopts = {
+ RBD_ENCRYPTION_ALGORITHM_AES256, parent_passphrase};
+ ASSERT_EQ(0, parent.encryption_format(RBD_ENCRYPTION_FORMAT_LUKS1, &fopts,
+ sizeof(fopts)));
+
+ ceph::bufferlist bl;
+ bl.append(std::string(data_size, 'a'));
+ ASSERT_EQ(data_size, parent.write(0, data_size, bl));
+
+ ASSERT_EQ(0, parent.snap_create("snap"));
+ ASSERT_EQ(0, parent.snap_protect("snap"));
+ uint64_t features;
+ ASSERT_EQ(0, parent.features(&features));
+ ASSERT_EQ(0, rbd.clone(ioctx, parent_name.c_str(), "snap", ioctx,
+ clone_name.c_str(), features, &order));
+ }
+
+ {
+ librbd::Image clone;
+ ASSERT_EQ(0, rbd.open(ioctx, clone, clone_name.c_str(), nullptr));
+
+ librbd::encryption_luks2_format_options_t fopts =
+ {RBD_ENCRYPTION_ALGORITHM_AES256, clone_passphrase};
+ ASSERT_EQ(0, clone.encryption_format(RBD_ENCRYPTION_FORMAT_LUKS2, &fopts,
+ sizeof(fopts)));
+
+ librbd::encryption_luks_format_options_t opts1 = {parent_passphrase};
+ librbd::encryption_luks_format_options_t opts2 = {clone_passphrase};
+ librbd::encryption_spec_t specs[] = {
+ {RBD_ENCRYPTION_FORMAT_LUKS, &opts2, sizeof(opts2)},
+ {RBD_ENCRYPTION_FORMAT_LUKS, &opts1, sizeof(opts1)}};
+ ASSERT_EQ(0, clone.encryption_load2(specs, std::size(specs)));
+
+ uint64_t size;
+ ASSERT_EQ(0, clone.size(&size));
+ EXPECT_EQ(data_size + luks1_meta_size - luks2_meta_size, size);
+ uint64_t overlap;
+ ASSERT_EQ(0, clone.overlap(&overlap));
+ EXPECT_EQ(data_size + luks1_meta_size - luks2_meta_size, overlap);
+
+ ceph::bufferlist expected_bl;
+ expected_bl.append(std::string(
+ data_size + luks1_meta_size - luks2_meta_size, 'a'));
+
+ ceph::bufferlist read_bl;
+ ASSERT_EQ(expected_bl.length(),
+ clone.read(0, expected_bl.length(), read_bl));
+ EXPECT_TRUE(expected_bl.contents_equal(read_bl));
+ }
+}
+
+TEST_F(TestLibRBD, LUKS2UnderLUKS1WithoutResize)
+{
+ REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+ REQUIRE(!is_feature_enabled(RBD_FEATURE_STRIPINGV2));
+ REQUIRE(!is_feature_enabled(RBD_FEATURE_JOURNALING));
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ std::string parent_name = get_temp_image_name();
+ std::string clone_name = get_temp_image_name();
+ uint64_t data_size = 25 << 20;
+ uint64_t luks1_meta_size = 4 << 20;
+ uint64_t luks2_meta_size = 16 << 20;
+ std::string parent_passphrase = "parent passphrase";
+ std::string clone_passphrase = "clone passphrase";
+
+ {
+ int order = 22;
+ ASSERT_EQ(0, create_image_pp(rbd, ioctx, parent_name.c_str(),
+ luks2_meta_size + data_size, &order));
+ librbd::Image parent;
+ ASSERT_EQ(0, rbd.open(ioctx, parent, parent_name.c_str(), nullptr));
+
+ librbd::encryption_luks2_format_options_t fopts = {
+ RBD_ENCRYPTION_ALGORITHM_AES256, parent_passphrase};
+ ASSERT_EQ(0, parent.encryption_format(RBD_ENCRYPTION_FORMAT_LUKS2, &fopts,
+ sizeof(fopts)));
+
+ ceph::bufferlist bl;
+ bl.append(std::string(data_size, 'a'));
+ ASSERT_EQ(data_size, parent.write(0, data_size, bl));
+
+ ASSERT_EQ(0, parent.snap_create("snap"));
+ ASSERT_EQ(0, parent.snap_protect("snap"));
+ uint64_t features;
+ ASSERT_EQ(0, parent.features(&features));
+ ASSERT_EQ(0, rbd.clone(ioctx, parent_name.c_str(), "snap", ioctx,
+ clone_name.c_str(), features, &order));
+ }
+
+ {
+ librbd::Image clone;
+ ASSERT_EQ(0, rbd.open(ioctx, clone, clone_name.c_str(), nullptr));
+
+ librbd::encryption_luks1_format_options_t fopts =
+ {RBD_ENCRYPTION_ALGORITHM_AES256, clone_passphrase};
+ ASSERT_EQ(0, clone.encryption_format(RBD_ENCRYPTION_FORMAT_LUKS1, &fopts,
+ sizeof(fopts)));
+
+ librbd::encryption_luks_format_options_t opts1 = {parent_passphrase};
+ librbd::encryption_luks_format_options_t opts2 = {clone_passphrase};
+ librbd::encryption_spec_t specs[] = {
+ {RBD_ENCRYPTION_FORMAT_LUKS, &opts2, sizeof(opts2)},
+ {RBD_ENCRYPTION_FORMAT_LUKS, &opts1, sizeof(opts1)}};
+ ASSERT_EQ(0, clone.encryption_load2(specs, std::size(specs)));
+
+ uint64_t size;
+ ASSERT_EQ(0, clone.size(&size));
+ EXPECT_EQ(data_size + luks2_meta_size - luks1_meta_size, size);
+ uint64_t overlap;
+ ASSERT_EQ(0, clone.overlap(&overlap));
+ EXPECT_EQ(data_size, overlap);
+
+ ceph::bufferlist expected_bl;
+ expected_bl.append(std::string(data_size, 'a'));
+ expected_bl.append_zero(luks2_meta_size - luks1_meta_size);
+
+ ceph::bufferlist read_bl;
+ ASSERT_EQ(expected_bl.length(),
+ clone.read(0, expected_bl.length(), read_bl));
+ EXPECT_TRUE(expected_bl.contents_equal(read_bl));
+ }
+}
+
TEST_F(TestLibRBD, EncryptionFormatNoData)
{
REQUIRE(!is_feature_enabled(RBD_FEATURE_JOURNALING));