From: Or Ozeri Date: Thu, 2 Jul 2020 14:50:16 +0000 (+0300) Subject: librbd: add a crypto object dispatch layer X-Git-Tag: v16.1.0~1503^2 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=b6d377281ad38955165cdeebb70ade943b803b79;p=ceph.git librbd: add a crypto object dispatch layer This commit adds a new object dispatch layer to enable encryption of RBD images. Signed-off-by: Or Ozeri --- diff --git a/src/librbd/CMakeLists.txt b/src/librbd/CMakeLists.txt index 65e98ad1f369..2f8bb82fa1b1 100644 --- a/src/librbd/CMakeLists.txt +++ b/src/librbd/CMakeLists.txt @@ -46,6 +46,7 @@ set(librbd_internal_srcs cache/rwl/InitRequest.cc cache/rwl/ShutdownRequest.cc cache/WriteAroundObjectDispatch.cc + crypto/CryptoObjectDispatch.cc deep_copy/ImageCopyRequest.cc deep_copy/MetadataCopyRequest.cc deep_copy/ObjectCopyRequest.cc diff --git a/src/librbd/crypto/CryptoInterface.h b/src/librbd/crypto/CryptoInterface.h new file mode 100644 index 000000000000..28f2baacc389 --- /dev/null +++ b/src/librbd/crypto/CryptoInterface.h @@ -0,0 +1,26 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef CEPH_LIBRBD_CRYPTO_CRYPTO_INTERFACE_H +#define CEPH_LIBRBD_CRYPTO_CRYPTO_INTERFACE_H + +#include "common/RefCountedObj.h" +#include "include/buffer.h" + +namespace librbd { +namespace crypto { + +class CryptoInterface : public RefCountedObject { + +public: + virtual void encrypt(ceph::bufferlist&& data, + uint64_t image_offset) const = 0; + virtual void decrypt(ceph::bufferlist&& data, + uint64_t image_offset) const = 0; + +}; + +} // namespace crypto +} // namespace librbd + +#endif // CEPH_LIBRBD_CRYPTO_CRYPTO_INTERFACE_H diff --git a/src/librbd/crypto/CryptoObjectDispatch.cc b/src/librbd/crypto/CryptoObjectDispatch.cc new file mode 100644 index 000000000000..99ccc49c1a13 --- /dev/null +++ b/src/librbd/crypto/CryptoObjectDispatch.cc @@ -0,0 +1,254 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "librbd/crypto/CryptoObjectDispatch.h" +#include "include/ceph_assert.h" +#include "common/dout.h" +#include "librbd/ImageCtx.h" +#include "librbd/Utils.h" +#include "librbd/crypto/CryptoInterface.h" +#include "librbd/io/AioCompletion.h" +#include "librbd/io/ObjectDispatcherInterface.h" +#include "librbd/io/ObjectDispatchSpec.h" +#include "librbd/io/ReadResult.h" +#include "librbd/io/Utils.h" + +#define dout_subsys ceph_subsys_rbd +#undef dout_prefix +#define dout_prefix *_dout << "librbd::crypto::CryptoObjectDispatch: " \ + << this << " " << __func__ << ": " + +namespace librbd { +namespace crypto { + +using librbd::util::data_object_name; + +template +struct C_EncryptedObjectReadRequest : public Context { + + I* image_ctx; + CryptoInterface* crypto; + uint64_t object_no; + uint64_t object_off; + ceph::bufferlist* read_data; + Context* onfinish; + io::ReadResult::C_ObjectReadRequest* req_comp; + io::ObjectDispatchSpec* req; + + C_EncryptedObjectReadRequest( + I* image_ctx, CryptoInterface* crypto, uint64_t object_no, + uint64_t object_off, uint64_t object_len, librados::snap_t snap_id, + int op_flags, const ZTracer::Trace &parent_trace, + ceph::bufferlist* read_data, int* object_dispatch_flags, + Context** on_finish, + Context* on_dispatched) : image_ctx(image_ctx), + crypto(crypto), + object_no(object_no), + object_off(object_off), + read_data(read_data), + onfinish(on_dispatched) { + *on_finish = util::create_context_callback< + Context, &Context::complete>(*on_finish, crypto); + + auto aio_comp = io::AioCompletion::create_and_start( + (Context*)this, util::get_image_ctx(image_ctx), + io::AIO_TYPE_READ); + aio_comp->read_result = io::ReadResult{read_data}; + aio_comp->set_request_count(1); + + auto req_comp = new io::ReadResult::C_ObjectReadRequest( + aio_comp, object_off, object_len, {{0, object_len}}); + + req = io::ObjectDispatchSpec::create_read( + image_ctx, io::OBJECT_DISPATCH_LAYER_CRYPTO, object_no, + {{object_off, object_len}}, snap_id, op_flags, parent_trace, + &req_comp->bl, &req_comp->extent_map, nullptr, req_comp); + } + + void send() { + req->send(); + } + + void finish(int r) override { + ldout(image_ctx->cct, 20) << "r=" << r << dendl; + if (r > 0) { + crypto->decrypt( + std::move(*read_data), + Striper::get_file_offset( + image_ctx->cct, &image_ctx->layout, object_no, + object_off)); + } + onfinish->complete(r); + } +}; + +template +CryptoObjectDispatch::CryptoObjectDispatch( + I* image_ctx, CryptoInterface *crypto) + : m_image_ctx(image_ctx), m_crypto(crypto) { +} + +template +void CryptoObjectDispatch::init(Context* on_finish) { + auto cct = m_image_ctx->cct; + ldout(cct, 5) << dendl; + + // need to initialize m_crypto here using image header object + + m_image_ctx->io_object_dispatcher->register_dispatch(this); + + on_finish->complete(0); +} + +template +void CryptoObjectDispatch::shut_down(Context* on_finish) { + if (m_crypto != nullptr) { + m_crypto->put(); + m_crypto = nullptr; + } + on_finish->complete(0); +} + +template +bool CryptoObjectDispatch::read( + uint64_t object_no, const io::Extents &extents, + librados::snap_t snap_id, int op_flags, const ZTracer::Trace &parent_trace, + ceph::bufferlist* read_data, io::Extents* extent_map, uint64_t* version, + int* object_dispatch_flags, io::DispatchResult* dispatch_result, + Context** on_finish, Context* on_dispatched) { + auto cct = m_image_ctx->cct; + ldout(cct, 20) << data_object_name(m_image_ctx, object_no) << " " + << extents << dendl; + ceph_assert(m_crypto != nullptr); + + if (version != nullptr || extents.size() != 1) { + // there's currently no need to support multiple extents + // as well as returning object version + return false; + } + + auto [object_off, object_len] = extents.front(); + + *dispatch_result = io::DISPATCH_RESULT_COMPLETE; + auto req = new C_EncryptedObjectReadRequest( + m_image_ctx, m_crypto, object_no, object_off, object_len, snap_id, + op_flags, parent_trace, read_data, object_dispatch_flags, on_finish, + on_dispatched); + req->send(); + return true; +} + +template +bool CryptoObjectDispatch::write( + uint64_t object_no, uint64_t object_off, ceph::bufferlist&& data, + const ::SnapContext &snapc, int op_flags, + const ZTracer::Trace &parent_trace, int* object_dispatch_flags, + uint64_t* journal_tid, io::DispatchResult* dispatch_result, + Context** on_finish, Context* on_dispatched) { + auto cct = m_image_ctx->cct; + ldout(cct, 20) << data_object_name(m_image_ctx, object_no) << " " + << object_off << "~" << data.length() << dendl; + ceph_assert(m_crypto != nullptr); + + m_crypto->encrypt( + std::move(data), + Striper::get_file_offset( + m_image_ctx->cct, &m_image_ctx->layout, object_no, + object_off)); + *dispatch_result = io::DISPATCH_RESULT_CONTINUE; + on_dispatched->complete(0); + return true; +} + +template +bool CryptoObjectDispatch::write_same( + uint64_t object_no, uint64_t object_off, uint64_t object_len, + io::LightweightBufferExtents&& buffer_extents, ceph::bufferlist&& data, + const ::SnapContext &snapc, int op_flags, + const ZTracer::Trace &parent_trace, int* object_dispatch_flags, + uint64_t* journal_tid, io::DispatchResult* dispatch_result, + Context** on_finish, Context* on_dispatched) { + auto cct = m_image_ctx->cct; + ldout(cct, 20) << data_object_name(m_image_ctx, object_no) << " " + << object_off << "~" << object_len << dendl; + ceph_assert(m_crypto != nullptr); + + // convert to regular write + io::LightweightObjectExtent extent(object_no, object_off, object_len, 0); + extent.buffer_extents = std::move(buffer_extents); + + bufferlist ws_data; + io::util::assemble_write_same_extent(extent, data, &ws_data, true); + + auto ctx = new LambdaContext( + [on_finish_ctx=on_dispatched](int r) { + on_finish_ctx->complete(r); + }); + + *dispatch_result = io::DISPATCH_RESULT_COMPLETE; + auto req = io::ObjectDispatchSpec::create_write( + m_image_ctx, io::OBJECT_DISPATCH_LAYER_NONE, object_no, + object_off, std::move(ws_data), snapc, op_flags, 0, + parent_trace, ctx); + req->send(); + return true; +} + +template +bool CryptoObjectDispatch::compare_and_write( + uint64_t object_no, uint64_t object_off, ceph::bufferlist&& cmp_data, + ceph::bufferlist&& write_data, const ::SnapContext &snapc, int op_flags, + const ZTracer::Trace &parent_trace, uint64_t* mismatch_offset, + int* object_dispatch_flags, uint64_t* journal_tid, + io::DispatchResult* dispatch_result, Context** on_finish, + Context* on_dispatched) { + auto cct = m_image_ctx->cct; + ldout(cct, 20) << data_object_name(m_image_ctx, object_no) << " " + << object_off << "~" << write_data.length() + << dendl; + ceph_assert(m_crypto != nullptr); + + uint64_t image_offset = Striper::get_file_offset( + m_image_ctx->cct, &m_image_ctx->layout, object_no, object_off); + m_crypto->encrypt(std::move(cmp_data), image_offset); + m_crypto->encrypt(std::move(write_data), image_offset); + *dispatch_result = io::DISPATCH_RESULT_CONTINUE; + on_dispatched->complete(0); + return true; +} + +template +bool CryptoObjectDispatch::discard( + uint64_t object_no, uint64_t object_off, uint64_t object_len, + const ::SnapContext &snapc, int discard_flags, + const ZTracer::Trace &parent_trace, int* object_dispatch_flags, + uint64_t* journal_tid, io::DispatchResult* dispatch_result, + Context** on_finish, Context* on_dispatched) { + auto cct = m_image_ctx->cct; + ldout(cct, 20) << data_object_name(m_image_ctx, object_no) << " " + << object_off << "~" << object_len << dendl; + ceph_assert(m_crypto != nullptr); + + // convert to write-same + auto ctx = new LambdaContext( + [on_finish_ctx=on_dispatched](int r) { + on_finish_ctx->complete(r); + }); + + bufferlist bl; + const int buffer_size = 4096; + bl.append_zero(buffer_size); + + *dispatch_result = io::DISPATCH_RESULT_COMPLETE; + auto req = io::ObjectDispatchSpec::create_write_same( + m_image_ctx, io::OBJECT_DISPATCH_LAYER_NONE, object_no, object_off, + object_len, {{0, buffer_size}}, std::move(bl), snapc, + *object_dispatch_flags, 0, parent_trace, ctx); + req->send(); + return true; +} + +} // namespace crypto +} // namespace librbd + +template class librbd::crypto::CryptoObjectDispatch; diff --git a/src/librbd/crypto/CryptoObjectDispatch.h b/src/librbd/crypto/CryptoObjectDispatch.h new file mode 100644 index 000000000000..1d9edda909be --- /dev/null +++ b/src/librbd/crypto/CryptoObjectDispatch.h @@ -0,0 +1,103 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef CEPH_LIBRBD_CRYPTO_CRYPTO_OBJECT_DISPATCH_H +#define CEPH_LIBRBD_CRYPTO_CRYPTO_OBJECT_DISPATCH_H + +#include "librbd/crypto/CryptoInterface.h" +#include "librbd/io/Types.h" +#include "librbd/io/ObjectDispatchInterface.h" + +namespace librbd { + +struct ImageCtx; + +namespace crypto { + +template +class CryptoObjectDispatch : public io::ObjectDispatchInterface { +public: + static CryptoObjectDispatch* create(ImageCtxT* image_ctx) { + return new CryptoObjectDispatch(image_ctx, nullptr); + } + + CryptoObjectDispatch(ImageCtxT* image_ctx, CryptoInterface *crypto); + + io::ObjectDispatchLayer get_dispatch_layer() const override { + return io::OBJECT_DISPATCH_LAYER_CRYPTO; + } + + void init(Context* on_finish); + + void shut_down(Context* on_finish) override; + + bool read( + uint64_t object_no, const io::Extents &extents, + librados::snap_t snap_id, int op_flags, + const ZTracer::Trace &parent_trace, ceph::bufferlist* read_data, + io::Extents* extent_map, uint64_t* version, int* object_dispatch_flags, + io::DispatchResult* dispatch_result, Context** on_finish, + Context* on_dispatched) override; + + bool discard( + uint64_t object_no, uint64_t object_off, uint64_t object_len, + const ::SnapContext &snapc, int discard_flags, + const ZTracer::Trace &parent_trace, int* object_dispatch_flags, + uint64_t* journal_tid, io::DispatchResult* dispatch_result, + Context** on_finish, Context* on_dispatched) override; + + bool write( + uint64_t object_no, uint64_t object_off, ceph::bufferlist&& data, + const ::SnapContext &snapc, int op_flags, + const ZTracer::Trace &parent_trace, int* object_dispatch_flags, + uint64_t* journal_tid, io::DispatchResult* dispatch_result, + Context** on_finish, Context* on_dispatched) override; + + bool write_same( + uint64_t object_no, uint64_t object_off, uint64_t object_len, + io::LightweightBufferExtents&& buffer_extents, ceph::bufferlist&& data, + const ::SnapContext &snapc, int op_flags, + const ZTracer::Trace &parent_trace, int* object_dispatch_flags, + uint64_t* journal_tid, io::DispatchResult* dispatch_result, + Context** on_finish, Context* on_dispatched) override; + + bool compare_and_write( + uint64_t object_no, uint64_t object_off, ceph::bufferlist&& cmp_data, + ceph::bufferlist&& write_data, const ::SnapContext &snapc, int op_flags, + const ZTracer::Trace &parent_trace, uint64_t* mismatch_offset, + int* object_dispatch_flags, uint64_t* journal_tid, + io::DispatchResult* dispatch_result, Context** on_finish, + Context* on_dispatched) override; + + bool flush( + io::FlushSource flush_source, const ZTracer::Trace &parent_trace, + uint64_t* journal_tid, io::DispatchResult* dispatch_result, + Context** on_finish, Context* on_dispatched) override { + return false; + } + + bool invalidate_cache(Context* on_finish) override { + return false; + } + bool reset_existence_cache(Context* on_finish) override { + return false; + } + + void extent_overwritten( + uint64_t object_no, uint64_t object_off, uint64_t object_len, + uint64_t journal_tid, uint64_t new_journal_tid) override { + } + +private: + + ImageCtxT* m_image_ctx; + CryptoInterface *m_crypto; + +}; + +} // namespace crypto +} // namespace librbd + +extern template class librbd::crypto::CryptoObjectDispatch; + +#endif // CEPH_LIBRBD_CRYPTO_CRYPTO_OBJECT_DISPATCH_H diff --git a/src/librbd/io/Types.h b/src/librbd/io/Types.h index 3d43bcc16ae1..6a46788d2ed9 100644 --- a/src/librbd/io/Types.h +++ b/src/librbd/io/Types.h @@ -91,6 +91,7 @@ enum { enum ObjectDispatchLayer { OBJECT_DISPATCH_LAYER_NONE = 0, OBJECT_DISPATCH_LAYER_CACHE, + OBJECT_DISPATCH_LAYER_CRYPTO, OBJECT_DISPATCH_LAYER_JOURNAL, OBJECT_DISPATCH_LAYER_PARENT_CACHE, OBJECT_DISPATCH_LAYER_SCHEDULER, diff --git a/src/test/librbd/CMakeLists.txt b/src/test/librbd/CMakeLists.txt index 403c3c12ccdf..ae6966d5daf9 100644 --- a/src/test/librbd/CMakeLists.txt +++ b/src/test/librbd/CMakeLists.txt @@ -53,6 +53,7 @@ set(unittest_librbd_srcs test_mock_Watcher.cc cache/test_mock_WriteAroundObjectDispatch.cc cache/test_mock_ParentCacheObjectDispatch.cc + crypto/test_mock_CryptoObjectDispatch.cc deep_copy/test_mock_ImageCopyRequest.cc deep_copy/test_mock_MetadataCopyRequest.cc deep_copy/test_mock_ObjectCopyRequest.cc diff --git a/src/test/librbd/crypto/test_mock_CryptoObjectDispatch.cc b/src/test/librbd/crypto/test_mock_CryptoObjectDispatch.cc new file mode 100644 index 000000000000..d38d10dae07e --- /dev/null +++ b/src/test/librbd/crypto/test_mock_CryptoObjectDispatch.cc @@ -0,0 +1,204 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "test/librbd/test_mock_fixture.h" +#include "test/librbd/test_support.h" +#include "test/librbd/mock/MockImageCtx.h" +#include "test/librbd/mock/crypto/MockCryptoInterface.h" +#include "librbd/crypto/CryptoObjectDispatch.h" +#include "librbd/io/ObjectDispatchSpec.h" + +namespace librbd { + +namespace util { + +inline ImageCtx *get_image_ctx(MockImageCtx *image_ctx) { + return image_ctx->image_ctx; +} + +} // namespace util +} // namespace librbd + +#include "librbd/crypto/CryptoObjectDispatch.cc" + +namespace librbd { +namespace crypto { + +using ::testing::_; +using ::testing::Invoke; + +struct TestMockCryptoCryptoObjectDispatch : public TestMockFixture { + typedef CryptoObjectDispatch MockCryptoObjectDispatch; + + MockCryptoInterface* crypto; + MockImageCtx* mock_image_ctx; + MockCryptoObjectDispatch* mock_crypto_object_dispatch; + + C_SaferCond finished_cond; + Context *on_finish = &finished_cond; + C_SaferCond dispatched_cond; + Context *on_dispatched = &dispatched_cond; + Context *dispatcher_ctx; + ceph::bufferlist data; + io::DispatchResult dispatch_result; + io::Extents extent_map; + int object_dispatch_flags = 0; + + void SetUp() override { + TestMockFixture::SetUp(); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + crypto = new MockCryptoInterface(); + mock_image_ctx = new MockImageCtx(*ictx); + mock_crypto_object_dispatch = new MockCryptoObjectDispatch(mock_image_ctx, crypto); + data.append("X"); + } + + void TearDown() override { + C_SaferCond cond; + Context *on_finish = &cond; + mock_crypto_object_dispatch->shut_down(on_finish); + ASSERT_EQ(0, cond.wait()); + + delete mock_image_ctx; + + TestMockFixture::TearDown(); + } + + void expect_object_read() { + EXPECT_CALL(*mock_image_ctx->io_object_dispatcher, send(_)) + .WillOnce(Invoke([this](io::ObjectDispatchSpec* spec) { + auto* read = boost::get( + &spec->request); + ASSERT_TRUE(read != nullptr); + + spec->dispatch_result = io::DISPATCH_RESULT_COMPLETE; + dispatcher_ctx = &spec->dispatcher_ctx; + })); + } + + void expect_object_write() { + EXPECT_CALL(*mock_image_ctx->io_object_dispatcher, send(_)) + .WillOnce(Invoke([this](io::ObjectDispatchSpec* spec) { + auto* write = boost::get( + &spec->request); + ASSERT_TRUE(write != nullptr); + + spec->dispatch_result = io::DISPATCH_RESULT_COMPLETE; + dispatcher_ctx = &spec->dispatcher_ctx; + })); + } + + void expect_object_write_same() { + EXPECT_CALL(*mock_image_ctx->io_object_dispatcher, send(_)) + .WillOnce(Invoke([this](io::ObjectDispatchSpec* spec) { + auto* write_same = boost::get< + io::ObjectDispatchSpec::WriteSameRequest>( + &spec->request); + ASSERT_TRUE(write_same != nullptr); + + spec->dispatch_result = io::DISPATCH_RESULT_COMPLETE; + dispatcher_ctx = &spec->dispatcher_ctx; + })); + } + + void expect_encrypt(int count = 1) { + EXPECT_CALL(*crypto, encrypt(_, _)).Times(count); + } + + void expect_decrypt() { + EXPECT_CALL(*crypto, decrypt(_, _)); + } +}; + +TEST_F(TestMockCryptoCryptoObjectDispatch, Flush) { + ASSERT_FALSE(mock_crypto_object_dispatch->flush( + io::FLUSH_SOURCE_USER, {}, nullptr, nullptr, &on_finish, nullptr)); + ASSERT_EQ(on_finish, &finished_cond); // not modified + on_finish->complete(0); + ASSERT_EQ(0, finished_cond.wait()); +} + +TEST_F(TestMockCryptoCryptoObjectDispatch, Discard) { + expect_object_write_same(); + ASSERT_TRUE(mock_crypto_object_dispatch->discard( + 0, 0, 4096, mock_image_ctx->snapc, 0, {}, &object_dispatch_flags, + nullptr, &dispatch_result, &on_finish, on_dispatched)); + ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE); + + ASSERT_EQ(ETIMEDOUT, dispatched_cond.wait_for(0)); + dispatcher_ctx->complete(0); + ASSERT_EQ(0, dispatched_cond.wait()); +} + +TEST_F(TestMockCryptoCryptoObjectDispatch, ReadFail) { + expect_object_read(); + ASSERT_TRUE(mock_crypto_object_dispatch->read( + 0, {{0, 4096}}, CEPH_NOSNAP, 0, {}, &data, &extent_map, nullptr, + &object_dispatch_flags, &dispatch_result, &on_finish, on_dispatched)); + ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE); + ASSERT_NE(on_finish, &finished_cond); + + ASSERT_EQ(ETIMEDOUT, dispatched_cond.wait_for(0)); + dispatcher_ctx->complete(-EIO); + ASSERT_EQ(-EIO, dispatched_cond.wait()); +} + +TEST_F(TestMockCryptoCryptoObjectDispatch, Read) { + expect_object_read(); + ASSERT_TRUE(mock_crypto_object_dispatch->read( + 0, {{0, 4096}}, CEPH_NOSNAP, 0, {}, &data, &extent_map, nullptr, + &object_dispatch_flags, &dispatch_result, &on_finish, + on_dispatched)); + ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE); + ASSERT_NE(on_finish, &finished_cond); + ASSERT_EQ(ETIMEDOUT, dispatched_cond.wait_for(0)); + + expect_decrypt(); + dispatcher_ctx->complete(0); + ASSERT_EQ(4096, dispatched_cond.wait()); +} + +TEST_F(TestMockCryptoCryptoObjectDispatch, Write) { + expect_encrypt(); + ASSERT_TRUE(mock_crypto_object_dispatch->write( + 0, 0, std::move(data), mock_image_ctx->snapc, 0, {}, nullptr, nullptr, + &dispatch_result, &on_finish, on_dispatched)); + ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_CONTINUE); + ASSERT_EQ(on_finish, &finished_cond); // not modified + on_finish->complete(0); + ASSERT_EQ(0, finished_cond.wait()); +} + +TEST_F(TestMockCryptoCryptoObjectDispatch, CompareAndWrite) { + ceph::bufferlist cmp_data(data); + + expect_encrypt(2); + ASSERT_TRUE(mock_crypto_object_dispatch->compare_and_write( + 0, 0, std::move(cmp_data), std::move(data), + mock_image_ctx->snapc, 0, {}, nullptr, nullptr, nullptr, + &dispatch_result, &on_finish, on_dispatched)); + ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_CONTINUE); + ASSERT_EQ(on_finish, &finished_cond); // not modified + on_finish->complete(0); + ASSERT_EQ(0, finished_cond.wait()); +} + +TEST_F(TestMockCryptoCryptoObjectDispatch, WriteSame) { + io::LightweightBufferExtents buffer_extents; + + expect_object_write(); + ASSERT_TRUE(mock_crypto_object_dispatch->write_same( + 0, 0, 4096, std::move(buffer_extents), std::move(data), + mock_image_ctx->snapc, 0, {}, nullptr, nullptr, &dispatch_result, + &on_finish, on_dispatched)); + ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE); + + ASSERT_EQ(ETIMEDOUT, dispatched_cond.wait_for(0)); + dispatcher_ctx->complete(0); + ASSERT_EQ(0, dispatched_cond.wait()); +} + +} // namespace io +} // namespace librbd diff --git a/src/test/librbd/mock/crypto/MockCryptoInterface.h b/src/test/librbd/mock/crypto/MockCryptoInterface.h new file mode 100644 index 000000000000..5bb1c7e31c8b --- /dev/null +++ b/src/test/librbd/mock/crypto/MockCryptoInterface.h @@ -0,0 +1,23 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef CEPH_TEST_LIBRBD_MOCK_CRYPTO_MOCK_CRYPTO_INTERFACE_H +#define CEPH_TEST_LIBRBD_MOCK_CRYPTO_MOCK_CRYPTO_INTERFACE_H + +#include "include/buffer.h" +#include "gmock/gmock.h" +#include "librbd/crypto/CryptoInterface.h" + +namespace librbd { +namespace crypto { + +struct MockCryptoInterface : CryptoInterface { + + MOCK_CONST_METHOD2(encrypt, void(ceph::bufferlist&&, uint64_t)); + MOCK_CONST_METHOD2(decrypt, void(ceph::bufferlist&&, uint64_t)); +}; + +} // namespace crypto +} // namespace librbd + +#endif // CEPH_TEST_LIBRBD_MOCK_CRYPTO_MOCK_CRYPTO_INTERFACE_H