#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")))
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;
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,
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;
/* 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;
int Image<I>::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<I>* result_format;
auto r = util::create_encryption_format(
ictx->cct, format, opts, opts_size, c_api, &result_format);
}
template <typename I>
-int Image<I>::encryption_load(I* ictx, encryption_format_t format,
- encryption_options_t opts, size_t opts_size,
- bool c_api) {
- crypto::EncryptionFormat<I>* 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<I>::encryption_load(I* ictx, encryption_spec_t *specs,
+ size_t spec_count, bool c_api) {
std::vector<std::unique_ptr<crypto::EncryptionFormat<I>>> formats;
- formats.emplace_back(result_format);
+
+ for (size_t i = 0; i < spec_count; ++i) {
+ crypto::EncryptionFormat<I>* 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<I>::create(
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);
};
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()
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)
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)
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)
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):
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
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
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):
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 = <rbd_encryption_spec_t *>malloc(len(specs) *
+ sizeof(rbd_encryption_spec_t))
+ if _specs == NULL:
+ raise MemoryError("malloc failed")
+
+ memset(<void *>_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 = <rbd_encryption_luks1_format_options_t *>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 = <rbd_encryption_options_t> _luks1_opts
+ _specs[i].opts_size = sizeof(
+ rbd_encryption_luks1_format_options_t)
+ elif (format == RBD_ENCRYPTION_FORMAT_LUKS2):
+ _luks2_opts = <rbd_encryption_luks2_format_options_t *>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 = <rbd_encryption_options_t> _luks2_opts
+ _specs[i].opts_size = sizeof(
+ rbd_encryption_luks2_format_options_t)
+ elif (format == RBD_ENCRYPTION_FORMAT_LUKS):
+ _luks_opts = <rbd_encryption_luks_format_options_t *>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 = <rbd_encryption_options_t> _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):
"""
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));
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;
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])