From: Or Ozeri Date: Thu, 4 Aug 2022 10:05:02 +0000 (+0300) Subject: librbd: add cloned images encryption API X-Git-Tag: v18.1.0~960^2~2 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=4a5a0a5dd82be9266064631dfee352a96e4061ca;p=ceph.git librbd: add cloned images encryption API This commit extends encryption_format and adds encryption_load2 to librbd API, which enables crypto formatting and loading of cloned images. i.e. the child image is encrypted with a key different from its parent, while keeping the child thinly-provisioned. Signed-off-by: Or Ozeri --- diff --git a/src/include/rbd/librbd.h b/src/include/rbd/librbd.h index e0da63dd8221..0810794d54b4 100644 --- a/src/include/rbd/librbd.h +++ b/src/include/rbd/librbd.h @@ -49,6 +49,7 @@ extern "C" { #define LIBRBD_SUPPORTS_WRITESAME 1 #define LIBRBD_SUPPORTS_WRITE_ZEROES 1 #define LIBRBD_SUPPORTS_ENCRYPTION 1 +#define LIBRBD_SUPPORTS_ENCRYPTION_LOAD2 1 #if __GNUC__ >= 4 #define CEPH_RBD_API __attribute__ ((visibility ("default"))) @@ -386,6 +387,12 @@ typedef enum { typedef void *rbd_encryption_options_t; +typedef struct { + rbd_encryption_format_t format; + rbd_encryption_options_t opts; + size_t opts_size; +} rbd_encryption_spec_t; + typedef struct { rbd_encryption_algorithm_t alg; const char* passphrase; @@ -823,16 +830,49 @@ CEPH_RBD_API int rbd_deep_copy_with_progress(rbd_image_t image, void *cbdata); /* encryption */ + +/* + * Format the image using the encryption spec specified by + * (format, opts, opts_size) tuple. + * + * For a flat (i.e. non-cloned) image, the new encryption is loaded + * implicitly, calling rbd_encryption_load() afterwards is not needed. + * If existing encryption is already loaded, it is automatically + * replaced with the new encryption. + * + * For a cloned image, the new encryption must be loaded explicitly. + * Existing encryption (if any) must not be loaded. + */ CEPH_RBD_API int rbd_encryption_format(rbd_image_t image, rbd_encryption_format_t format, rbd_encryption_options_t opts, size_t opts_size); -/* encryption will be loaded on all ancestor images, - * until reaching an ancestor image which does not match any known format */ +/* + * Load the encryption spec specified by (format, opts, opts_size) + * tuple for the image and all ancestor images. If an ancestor image + * which does not match any encryption format known to librbd is + * encountered, it - along with remaining ancestor images - is + * interpreted as plaintext. + */ CEPH_RBD_API int rbd_encryption_load(rbd_image_t image, rbd_encryption_format_t format, rbd_encryption_options_t opts, size_t opts_size); +/* + * Load encryption specs. The first spec in the passed array is + * applied to the image itself, the second spec is applied to its + * ancestor image, the third spec is applied to the ancestor of + * that ancestor image and so on. + * + * If not enough specs are passed, the last spec is reused exactly as + * in rbd_encryption_load(). If an ancestor image for which the last + * spec is being reused turns out to not match any encryption format + * known to librbd, it - along with remaining ancestor images - is + * interpreted as plaintext. + */ +CEPH_RBD_API int rbd_encryption_load2(rbd_image_t image, + rbd_encryption_spec_t *specs, + size_t spec_count); /* snapshots */ CEPH_RBD_API int rbd_snap_list(rbd_image_t image, rbd_snap_info_t *snaps, diff --git a/src/include/rbd/librbd.hpp b/src/include/rbd/librbd.hpp index 6e84a0a0f5c9..14f48305c44b 100644 --- a/src/include/rbd/librbd.hpp +++ b/src/include/rbd/librbd.hpp @@ -217,6 +217,7 @@ namespace librbd { typedef rbd_encryption_format_t encryption_format_t; typedef rbd_encryption_algorithm_t encryption_algorithm_t; typedef rbd_encryption_options_t encryption_options_t; + typedef rbd_encryption_spec_t encryption_spec_t; typedef struct { encryption_algorithm_t alg; @@ -597,10 +598,9 @@ public: /* encryption */ int encryption_format(encryption_format_t format, encryption_options_t opts, size_t opts_size); - /* encryption will be loaded on all ancestor images, - * until reaching an ancestor image which does not match any known format */ int encryption_load(encryption_format_t format, encryption_options_t opts, size_t opts_size); + int encryption_load2(encryption_spec_t *specs, size_t spec_count); /* striping */ uint64_t get_stripe_unit() const; diff --git a/src/librbd/api/Image.cc b/src/librbd/api/Image.cc index ab62bf50c301..7f18ee583140 100644 --- a/src/librbd/api/Image.cc +++ b/src/librbd/api/Image.cc @@ -959,11 +959,6 @@ template int Image::encryption_format(I* ictx, encryption_format_t format, encryption_options_t opts, size_t opts_size, bool c_api) { - if (ictx->parent != nullptr) { - lderr(ictx->cct) << "cannot format a cloned image" << dendl; - return -ENOTSUP; - } - crypto::EncryptionFormat* result_format; auto r = util::create_encryption_format( ictx->cct, format, opts, opts_size, c_api, &result_format); @@ -980,18 +975,21 @@ int Image::encryption_format(I* ictx, encryption_format_t format, } template -int Image::encryption_load(I* ictx, encryption_format_t format, - encryption_options_t opts, size_t opts_size, - bool c_api) { - crypto::EncryptionFormat* result_format; - auto r = util::create_encryption_format( - ictx->cct, format, opts, opts_size, c_api, &result_format); - if (r != 0) { - return r; - } - +int Image::encryption_load(I* ictx, encryption_spec_t *specs, + size_t spec_count, bool c_api) { std::vector>> formats; - formats.emplace_back(result_format); + + for (size_t i = 0; i < spec_count; ++i) { + crypto::EncryptionFormat* result_format; + auto r = util::create_encryption_format( + ictx->cct, specs[i].format, specs[i].opts, specs[i].opts_size, + c_api, &result_format); + if (r != 0) { + return r; + } + + formats.emplace_back(result_format); + } C_SaferCond cond; auto req = librbd::crypto::LoadRequest::create( diff --git a/src/librbd/api/Image.h b/src/librbd/api/Image.h index 192f9b7a7998..e843f5b51e21 100644 --- a/src/librbd/api/Image.h +++ b/src/librbd/api/Image.h @@ -73,9 +73,8 @@ struct Image { static int encryption_format(ImageCtxT *ictx, encryption_format_t format, encryption_options_t opts, size_t opts_size, bool c_api); - static int encryption_load(ImageCtxT *ictx, encryption_format_t format, - encryption_options_t opts, size_t opts_size, - bool c_api); + static int encryption_load(ImageCtxT *ictx, encryption_spec_t *specs, + size_t spec_count, bool c_api); }; diff --git a/src/librbd/librbd.cc b/src/librbd/librbd.cc index 28bb2449d752..3c0a411ea259 100644 --- a/src/librbd/librbd.cc +++ b/src/librbd/librbd.cc @@ -2095,10 +2095,17 @@ namespace librbd { int Image::encryption_load(encryption_format_t format, encryption_options_t opts, size_t opts_size) + { + ImageCtx *ictx = (ImageCtx *)ctx; + encryption_spec_t spec = {format, opts, opts_size}; + return librbd::api::Image<>::encryption_load(ictx, &spec, 1, false); + } + + int Image::encryption_load2(encryption_spec_t *specs, size_t spec_count) { ImageCtx *ictx = (ImageCtx *)ctx; return librbd::api::Image<>::encryption_load( - ictx, format, opts, opts_size, false); + ictx, specs, spec_count, false); } int Image::flatten() @@ -4382,8 +4389,16 @@ extern "C" int rbd_encryption_load(rbd_image_t image, size_t opts_size) { librbd::ImageCtx *ictx = (librbd::ImageCtx *)image; - return librbd::api::Image<>::encryption_load( - ictx, format, opts, opts_size, true); + librbd::encryption_spec_t spec = {format, opts, opts_size}; + return librbd::api::Image<>::encryption_load(ictx, &spec, 1, true); +} + +extern "C" int rbd_encryption_load2(rbd_image_t image, + rbd_encryption_spec_t *specs, + size_t spec_count) +{ + librbd::ImageCtx *ictx = (librbd::ImageCtx *)image; + return librbd::api::Image<>::encryption_load(ictx, specs, spec_count, true); } extern "C" int rbd_flatten(rbd_image_t image) diff --git a/src/pybind/rbd/c_rbd.pxd b/src/pybind/rbd/c_rbd.pxd index cca2c0327e5f..2415290b175b 100644 --- a/src/pybind/rbd/c_rbd.pxd +++ b/src/pybind/rbd/c_rbd.pxd @@ -287,6 +287,11 @@ cdef extern from "rbd/librbd.h" nogil: ctypedef void* rbd_encryption_options_t + ctypedef struct rbd_encryption_spec_t: + rbd_encryption_format_t format + rbd_encryption_options_t opts + size_t opts_size + ctypedef void (*rbd_callback_t)(rbd_completion_t cb, void *arg) void rbd_version(int *major, int *minor, int *extra) @@ -720,5 +725,8 @@ cdef extern from "rbd/librbd.h" nogil: rbd_encryption_format_t format, rbd_encryption_options_t opts, size_t opts_size) int rbd_encryption_load(rbd_image_t image, - rbd_encryption_format_t format, - rbd_encryption_options_t opts, size_t opts_size) + rbd_encryption_format_t format, + rbd_encryption_options_t opts, size_t opts_size) + int rbd_encryption_load2(rbd_image_t image, + rbd_encryption_spec_t *specs, + size_t spec_count) diff --git a/src/pybind/rbd/mock_rbd.pxi b/src/pybind/rbd/mock_rbd.pxi index 311d2b77f6ba..9d8df1328037 100644 --- a/src/pybind/rbd/mock_rbd.pxi +++ b/src/pybind/rbd/mock_rbd.pxi @@ -289,6 +289,11 @@ cdef nogil: ctypedef void* rbd_encryption_options_t + ctypedef struct rbd_encryption_spec_t: + rbd_encryption_format_t format + rbd_encryption_options_t opts + size_t opts_size + void rbd_version(int *major, int *minor, int *extra): pass void rbd_image_spec_list_cleanup(rbd_image_spec_t *image, size_t num_images): @@ -909,6 +914,10 @@ cdef nogil: rbd_encryption_options_t opts, size_t opts_size): pass int rbd_encryption_load(rbd_image_t image, - rbd_encryption_format_t format, - rbd_encryption_options_t opts, size_t opts_size): + rbd_encryption_format_t format, + rbd_encryption_options_t opts, size_t opts_size): + pass + int rbd_encryption_load2(rbd_image_t image, + rbd_encryption_spec_t *specs, + size_t spec_count): pass diff --git a/src/pybind/rbd/rbd.pyx b/src/pybind/rbd/rbd.pyx index ab14d35fb111..f2d22b4a16cb 100644 --- a/src/pybind/rbd/rbd.pyx +++ b/src/pybind/rbd/rbd.pyx @@ -21,8 +21,8 @@ import sys from cpython cimport PyObject, ref, exc from libc cimport errno from libc.stdint cimport * -from libc.stdlib cimport realloc, free -from libc.string cimport strdup +from libc.stdlib cimport malloc, realloc, free +from libc.string cimport strdup, memset try: from collections.abc import Iterable @@ -5173,7 +5173,7 @@ written." % (self.name, ret, length)) passphrase = cstr(passphrase, "passphrase") cdef rbd_encryption_format_t _format = format cdef rbd_encryption_luks1_format_options_t _luks1_opts - cdef rbd_encryption_luks1_format_options_t _luks2_opts + cdef rbd_encryption_luks2_format_options_t _luks2_opts cdef char* _passphrase = passphrase if (format == RBD_ENCRYPTION_FORMAT_LUKS1): @@ -5246,6 +5246,71 @@ written." % (self.name, ret, length)) else: raise make_ex(-errno.ENOTSUP, 'Unsupported encryption format') + @requires_not_closed + def encryption_load2(self, specs): + cdef rbd_encryption_spec_t *_specs + cdef rbd_encryption_luks1_format_options_t* _luks1_opts + cdef rbd_encryption_luks2_format_options_t* _luks2_opts + cdef rbd_encryption_luks_format_options_t* _luks_opts + cdef size_t spec_count = len(specs) + + _specs = malloc(len(specs) * + sizeof(rbd_encryption_spec_t)) + if _specs == NULL: + raise MemoryError("malloc failed") + + memset(_specs, 0, len(specs) * sizeof(rbd_encryption_spec_t)) + try: + for i in range(len(specs)): + format, passphrase = specs[i] + passphrase = cstr(passphrase, "specs[%d][1]" % i) + _specs[i].format = format + if (format == RBD_ENCRYPTION_FORMAT_LUKS1): + _luks1_opts = malloc( + sizeof(rbd_encryption_luks1_format_options_t)) + if _luks1_opts == NULL: + raise MemoryError("malloc failed") + _luks1_opts.passphrase = passphrase + _luks1_opts.passphrase_size = len(passphrase) + _specs[i].opts = _luks1_opts + _specs[i].opts_size = sizeof( + rbd_encryption_luks1_format_options_t) + elif (format == RBD_ENCRYPTION_FORMAT_LUKS2): + _luks2_opts = malloc( + sizeof(rbd_encryption_luks2_format_options_t)) + if _luks2_opts == NULL: + raise MemoryError("malloc failed") + _luks2_opts.passphrase = passphrase + _luks2_opts.passphrase_size = len(passphrase) + _specs[i].opts = _luks2_opts + _specs[i].opts_size = sizeof( + rbd_encryption_luks2_format_options_t) + elif (format == RBD_ENCRYPTION_FORMAT_LUKS): + _luks_opts = malloc( + sizeof(rbd_encryption_luks_format_options_t)) + if _luks_opts == NULL: + raise MemoryError("malloc failed") + _luks_opts.passphrase = passphrase + _luks_opts.passphrase_size = len(passphrase) + _specs[i].opts = _luks_opts + _specs[i].opts_size = sizeof( + rbd_encryption_luks_format_options_t) + else: + raise make_ex( + -errno.ENOTSUP, + 'specs[%d][1]: Unsupported encryption format' % i) + with nogil: + ret = rbd_encryption_load2(self.image, _specs, spec_count) + if ret != 0: + raise make_ex( + ret, + 'error loading encryption on image %s' % self.name) + finally: + for i in range(len(specs)): + if _specs[i].opts != NULL: + free(_specs[i].opts) + free(_specs) + cdef class ImageIterator(object): """ diff --git a/src/test/librbd/test_librbd.cc b/src/test/librbd/test_librbd.cc index b0c49220244e..5d4ba4343152 100644 --- a/src/test/librbd/test_librbd.cc +++ b/src/test/librbd/test_librbd.cc @@ -2268,16 +2268,13 @@ TEST_F(TestLibRBD, TestEncryptionLUKS1) test_io(image); - bool passed; - write_test_data(image, "test", 0, 4, 0, &passed); - ASSERT_TRUE(passed); + ASSERT_PASSED(write_test_data, image, "test", 0, 4, 0); ASSERT_EQ(0, rbd_close(image)); ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL)); ASSERT_EQ(0, rbd_encryption_load( image, RBD_ENCRYPTION_FORMAT_LUKS, &luks_opts, sizeof(luks_opts))); - read_test_data(image, "test", 0, 4, 0, &passed); - ASSERT_TRUE(passed); + ASSERT_PASSED(read_test_data, image, "test", 0, 4, 0); #endif ASSERT_EQ(0, rbd_close(image)); @@ -2330,22 +2327,209 @@ TEST_F(TestLibRBD, TestEncryptionLUKS2) test_io(image); - bool passed; - write_test_data(image, "test", 0, 4, 0, &passed); - ASSERT_TRUE(passed); + ASSERT_PASSED(write_test_data, image, "test", 0, 4, 0); ASSERT_EQ(0, rbd_close(image)); ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL)); ASSERT_EQ(0, rbd_encryption_load( image, RBD_ENCRYPTION_FORMAT_LUKS, &luks_opts, sizeof(luks_opts))); - read_test_data(image, "test", 0, 4, 0, &passed); - ASSERT_TRUE(passed); + ASSERT_PASSED(read_test_data, image, "test", 0, 4, 0); #endif ASSERT_EQ(0, rbd_close(image)); rados_ioctx_destroy(ioctx); } +#ifdef HAVE_LIBCRYPTSETUP + +TEST_F(TestLibRBD, TestCloneEncryption) +{ + REQUIRE(!is_feature_enabled(RBD_FEATURE_JOURNALING)); + REQUIRE(!is_feature_enabled(RBD_FEATURE_STRIPINGV2)); + REQUIRE_FEATURE(RBD_FEATURE_LAYERING); + + rados_ioctx_t ioctx; + rados_ioctx_create(_cluster, m_pool_name.c_str(), &ioctx); + ASSERT_EQ(0, rados_conf_set( + _cluster, "rbd_read_from_replica_policy", "balance")); + + // create base image, write 'a's + int order = 0; + std::string name = get_temp_image_name(); + uint64_t size = 256 << 20; + ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order)); + + rbd_image_t image; + ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL)); + ASSERT_PASSED(write_test_data, image, "aaaa", 0, 4, 0); + ASSERT_EQ(0, rbd_flush(image)); + + // clone, encrypt with LUKS1, write 'b's + ASSERT_EQ(0, rbd_snap_create(image, "snap")); + ASSERT_EQ(0, rbd_snap_protect(image, "snap")); + + rbd_image_options_t image_opts; + rbd_image_options_create(&image_opts); + BOOST_SCOPE_EXIT_ALL( (&image_opts) ) { + rbd_image_options_destroy(image_opts); + }; + std::string child1_name = get_temp_image_name(); + ASSERT_EQ(0, rbd_clone3(ioctx, name.c_str(), "snap", ioctx, + child1_name.c_str(), image_opts)); + + rbd_image_t child1; + ASSERT_EQ(0, rbd_open(ioctx, child1_name.c_str(), &child1, NULL)); + + rbd_encryption_luks1_format_options_t child1_opts = { + .alg = RBD_ENCRYPTION_ALGORITHM_AES256, + .passphrase = "password", + .passphrase_size = 8, + }; + ASSERT_EQ(-EINVAL, rbd_encryption_load( + child1, RBD_ENCRYPTION_FORMAT_LUKS1, &child1_opts, + sizeof(child1_opts))); + ASSERT_EQ(0, rbd_encryption_format( + child1, RBD_ENCRYPTION_FORMAT_LUKS1, &child1_opts, + sizeof(child1_opts))); + ASSERT_EQ(0, rbd_encryption_load( + child1, RBD_ENCRYPTION_FORMAT_LUKS1, &child1_opts, + sizeof(child1_opts))); + ASSERT_PASSED(write_test_data, child1, "bbbb", 64 << 20, 4, 0); + ASSERT_EQ(0, rbd_flush(child1)); + + // clone, encrypt with LUKS2 (same passphrase), write 'c's + ASSERT_EQ(0, rbd_snap_create(child1, "snap")); + ASSERT_EQ(0, rbd_snap_protect(child1, "snap")); + + std::string child2_name = get_temp_image_name(); + ASSERT_EQ(0, rbd_clone3(ioctx, child1_name.c_str(), "snap", ioctx, + child2_name.c_str(), image_opts)); + + rbd_image_t child2; + ASSERT_EQ(0, rbd_open(ioctx, child2_name.c_str(), &child2, NULL)); + + rbd_encryption_luks2_format_options_t child2_opts = { + .alg = RBD_ENCRYPTION_ALGORITHM_AES256, + .passphrase = "password", + .passphrase_size = 8, + }; + ASSERT_EQ(0, rbd_encryption_format( + child2, RBD_ENCRYPTION_FORMAT_LUKS2, &child2_opts, + sizeof(child2_opts))); + ASSERT_EQ(0, rbd_encryption_load( + child2, RBD_ENCRYPTION_FORMAT_LUKS2, &child2_opts, + sizeof(child2_opts))); + ASSERT_PASSED(write_test_data, child2, "cccc", 128 << 20, 4, 0); + ASSERT_EQ(0, rbd_flush(child2)); + + // clone, encrypt with LUKS2 (different passphrase) + ASSERT_EQ(0, rbd_snap_create(child2, "snap")); + ASSERT_EQ(0, rbd_snap_protect(child2, "snap")); + + std::string child3_name = get_temp_image_name(); + ASSERT_EQ(0, rbd_clone3(ioctx, child2_name.c_str(), "snap", ioctx, + child3_name.c_str(), image_opts)); + + rbd_image_t child3; + ASSERT_EQ(0, rbd_open(ioctx, child3_name.c_str(), &child3, NULL)); + + rbd_encryption_luks2_format_options_t child3_opts = { + .alg = RBD_ENCRYPTION_ALGORITHM_AES256, + .passphrase = "12345678", + .passphrase_size = 8, + }; + ASSERT_EQ(0, rbd_encryption_format( + child3, RBD_ENCRYPTION_FORMAT_LUKS2, &child3_opts, + sizeof(child3_opts))); + ASSERT_EQ(-EPERM, rbd_encryption_load( + child3, RBD_ENCRYPTION_FORMAT_LUKS2, &child3_opts, + sizeof(child3_opts))); + + // verify child3 data + rbd_encryption_spec_t specs[] = { + { .format = RBD_ENCRYPTION_FORMAT_LUKS2, + .opts = &child3_opts, + .opts_size = sizeof(child3_opts)}, + { .format = RBD_ENCRYPTION_FORMAT_LUKS2, + .opts = &child2_opts, + .opts_size = sizeof(child2_opts)}, + { .format = RBD_ENCRYPTION_FORMAT_LUKS1, + .opts = &child1_opts, + .opts_size = sizeof(child1_opts)} + }; + + ASSERT_EQ(0, rbd_encryption_load2(child3, specs, 3)); + + ASSERT_PASSED(read_test_data, child3, "aaaa", 0, 4, 0); + ASSERT_PASSED(read_test_data, child3, "bbbb", 64 << 20, 4, 0); + ASSERT_PASSED(read_test_data, child3, "cccc", 128 << 20, 4, 0); + + // clone without formatting + ASSERT_EQ(0, rbd_snap_create(child3, "snap")); + ASSERT_EQ(0, rbd_snap_protect(child3, "snap")); + + std::string child4_name = get_temp_image_name(); + ASSERT_EQ(0, rbd_clone3(ioctx, child3_name.c_str(), "snap", ioctx, + child4_name.c_str(), image_opts)); + + rbd_image_t child4; + ASSERT_EQ(0, rbd_open(ioctx, child4_name.c_str(), &child4, NULL)); + + rbd_encryption_spec_t child4_specs[] = { + { .format = RBD_ENCRYPTION_FORMAT_LUKS2, + .opts = &child3_opts, + .opts_size = sizeof(child3_opts)}, + { .format = RBD_ENCRYPTION_FORMAT_LUKS2, + .opts = &child3_opts, + .opts_size = sizeof(child3_opts)}, + { .format = RBD_ENCRYPTION_FORMAT_LUKS2, + .opts = &child2_opts, + .opts_size = sizeof(child2_opts)}, + { .format = RBD_ENCRYPTION_FORMAT_LUKS1, + .opts = &child1_opts, + .opts_size = sizeof(child1_opts)} + }; + + ASSERT_EQ(0, rbd_encryption_load2(child4, child4_specs, 4)); + + // flatten child4 + ASSERT_EQ(0, rbd_flatten(child4)); + + // reopen child4 and load encryption + ASSERT_EQ(0, rbd_close(child4)); + ASSERT_EQ(0, rbd_open(ioctx, child4_name.c_str(), &child4, NULL)); + ASSERT_EQ(0, rbd_encryption_load( + child4, RBD_ENCRYPTION_FORMAT_LUKS2, &child3_opts, + sizeof(child3_opts))); + + // verify flattend image + ASSERT_PASSED(read_test_data, child4, "aaaa", 0, 4, 0); + ASSERT_PASSED(read_test_data, child4, "bbbb", 64 << 20, 4, 0); + ASSERT_PASSED(read_test_data, child4, "cccc", 128 << 20, 4, 0); + + ASSERT_EQ(0, rbd_close(child4)); + ASSERT_EQ(0, rbd_remove(ioctx, child4_name.c_str())); + ASSERT_EQ(0, rbd_snap_unprotect(child3, "snap")); + ASSERT_EQ(0, rbd_snap_remove(child3, "snap")); + ASSERT_EQ(0, rbd_close(child3)); + ASSERT_EQ(0, rbd_remove(ioctx, child3_name.c_str())); + ASSERT_EQ(0, rbd_snap_unprotect(child2, "snap")); + ASSERT_EQ(0, rbd_snap_remove(child2, "snap")); + ASSERT_EQ(0, rbd_close(child2)); + ASSERT_EQ(0, rbd_remove(ioctx, child2_name.c_str())); + ASSERT_EQ(0, rbd_snap_unprotect(child1, "snap")); + ASSERT_EQ(0, rbd_snap_remove(child1, "snap")); + ASSERT_EQ(0, rbd_close(child1)); + ASSERT_EQ(0, rbd_remove(ioctx, child1_name.c_str())); + ASSERT_EQ(0, rbd_snap_unprotect(image, "snap")); + ASSERT_EQ(0, rbd_snap_remove(image, "snap")); + ASSERT_EQ(0, rbd_close(image)); + ASSERT_EQ(0, rbd_remove(ioctx, name.c_str())); + rados_ioctx_destroy(ioctx); +} + +#endif + TEST_F(TestLibRBD, TestIOWithIOHint) { rados_ioctx_t ioctx; diff --git a/src/test/pybind/test_rbd.py b/src/test/pybind/test_rbd.py index 11ca9a3350a0..a7b74acbb86d 100644 --- a/src/test/pybind/test_rbd.py +++ b/src/test/pybind/test_rbd.py @@ -1876,6 +1876,34 @@ class TestClone(object): self.rbd.remove(ioctx, clone_name) eq([], [s for s in self.image.list_snaps() if s['name'] != 'snap1']) + @require_linux() + @blocklist_features([RBD_FEATURE_JOURNALING]) + def test_encryption_luks1(self): + data = b'hello world' + offset = 16<<20 + image_size = 32<<20 + + self.clone.resize(image_size) + self.clone.encryption_format(RBD_ENCRYPTION_FORMAT_LUKS1, "password") + self.clone.encryption_load2( + ((RBD_ENCRYPTION_FORMAT_LUKS1, "password"),)) + self.clone.write(data, offset) + eq(self.clone.read(0, 16), self.image.read(0, 16)) + + @require_linux() + @blocklist_features([RBD_FEATURE_JOURNALING]) + def test_encryption_luks2(self): + data = b'hello world' + offset = 16<<20 + image_size = 64<<20 + + self.clone.resize(image_size) + self.clone.encryption_format(RBD_ENCRYPTION_FORMAT_LUKS2, "password") + self.clone.encryption_load2( + ((RBD_ENCRYPTION_FORMAT_LUKS2, "password"),)) + self.clone.write(data, offset) + eq(self.clone.read(0, 16), self.image.read(0, 16)) + class TestExclusiveLock(object): @require_features([RBD_FEATURE_EXCLUSIVE_LOCK])