diff --git a/docs/api-status.json b/docs/api-status.json index 6e0803296..8b0c1ae9a 100644 --- a/docs/api-status.json +++ b/docs/api-status.json @@ -1953,6 +1953,12 @@ "comment": "GroupSnapGetInfo returns a slice of RBD image snapshots that are part of a\ngroup snapshot.\n\nImplements:\n\n\tint rbd_group_snap_get_info(rados_ioctx_t group_p,\n\t const char *group_name,\n\t const char *snap_name,\n\t rbd_group_snap_info2_t *snaps);\n", "added_in_version": "v0.30.0", "expected_stable_version": "v0.32.0" + }, + { + "name": "Image.EncryptionLoad2", + "comment": "EncryptionLoad2 enables IO on an open encrypted image. Multiple encryption\noption values can be passed to this call in a slice. For more information\nabout how items in the slice are applied to images, and possibly ancestor\nimages refer to the documentation in the C api for rbd_encryption_load2.\n\nImplements:\n\n\tint rbd_encryption_load2(rbd_image_t image,\n\t const rbd_encryption_spec_t *specs,\n\t size_t spec_count);\n", + "added_in_version": "$NEXT_RELEASE", + "expected_stable_version": "$NEXT_RELEASE_STABLE" } ] }, diff --git a/docs/api-status.md b/docs/api-status.md index e2865fadb..4f301e43b 100644 --- a/docs/api-status.md +++ b/docs/api-status.md @@ -25,6 +25,7 @@ Conn.GetAddrs | v0.31.0 | v0.33.0 | Name | Added in Version | Expected Stable Version | ---- | ---------------- | ----------------------- | GroupSnapGetInfo | v0.30.0 | v0.32.0 | +Image.EncryptionLoad2 | $NEXT_RELEASE | $NEXT_RELEASE_STABLE | ### Deprecated APIs diff --git a/entrypoint.sh b/entrypoint.sh index fdb891231..348359c87 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -264,7 +264,7 @@ test_pkg() { ulimit -c unlimited testbin="./${pkg}/${pkg##*/}.test" - show go test -v "${testargs[@]}" -o "${testbin}" "./${pkg}" + show go test -timeout 15m -v "${testargs[@]}" -o "${testbin}" "./${pkg}" ret=$(($?+ret)) if ls "${pkg}"/core.* >/dev/null 2>&1; then echo "Found coredump" diff --git a/rbd/encryption.go b/rbd/encryption.go index a0ddfea9d..1fede749d 100644 --- a/rbd/encryption.go +++ b/rbd/encryption.go @@ -64,7 +64,7 @@ func (opts EncryptionOptionsLUKS1) allocateEncryptionOptions() cEncryptionData { var cOpts C.rbd_encryption_luks1_format_options_t var retData cEncryptionData cOpts.alg = C.rbd_encryption_algorithm_t(opts.Alg) - //CBytes allocates memory which we'll free by calling cOptsFree() + // CBytes allocates memory. it will be freed when cEncryptionData.free is called cOpts.passphrase = (*C.char)(C.CBytes(opts.Passphrase)) cOpts.passphrase_size = C.size_t(len(opts.Passphrase)) retData.opts = C.rbd_encryption_options_t(&cOpts) @@ -78,7 +78,7 @@ func (opts EncryptionOptionsLUKS2) allocateEncryptionOptions() cEncryptionData { var cOpts C.rbd_encryption_luks2_format_options_t var retData cEncryptionData cOpts.alg = C.rbd_encryption_algorithm_t(opts.Alg) - //CBytes allocates memory which we'll free by calling cOptsFree() + // CBytes allocates memory. it will be freed when cEncryptionData.free is called cOpts.passphrase = (*C.char)(C.CBytes(opts.Passphrase)) cOpts.passphrase_size = C.size_t(len(opts.Passphrase)) retData.opts = C.rbd_encryption_options_t(&cOpts) diff --git a/rbd/encryption_load2.go b/rbd/encryption_load2.go new file mode 100644 index 000000000..81f6f3e4f --- /dev/null +++ b/rbd/encryption_load2.go @@ -0,0 +1,105 @@ +//go:build !octopus && !pacific && !quincy && ceph_preview + +package rbd + +// #cgo LDFLAGS: -lrbd +// /* force XSI-complaint strerror_r() */ +// #define _POSIX_C_SOURCE 200112L +// #undef _GNU_SOURCE +// #include +// #include +// #include +import "C" + +import ( + "unsafe" +) + +type encryptionOptions2 interface { + EncryptionOptions + writeEncryptionSpec(spec *C.rbd_encryption_spec_t) func() +} + +func (opts EncryptionOptionsLUKS1) writeEncryptionSpec(spec *C.rbd_encryption_spec_t) func() { + /* only C memory should be attached to spec */ + cPassphrase := (*C.char)(C.CBytes(opts.Passphrase)) + cOptsSize := C.size_t(C.sizeof_rbd_encryption_luks1_format_options_t) + cOpts := (*C.rbd_encryption_luks1_format_options_t)(C.malloc(cOptsSize)) + cOpts.alg = C.rbd_encryption_algorithm_t(opts.Alg) + cOpts.passphrase = cPassphrase + cOpts.passphrase_size = C.size_t(len(opts.Passphrase)) + + spec.format = C.RBD_ENCRYPTION_FORMAT_LUKS1 + spec.opts = C.rbd_encryption_options_t(cOpts) + spec.opts_size = cOptsSize + return func() { + C.free(unsafe.Pointer(cOpts.passphrase)) + C.free(unsafe.Pointer(cOpts)) + } +} + +func (opts EncryptionOptionsLUKS2) writeEncryptionSpec(spec *C.rbd_encryption_spec_t) func() { + /* only C memory should be attached to spec */ + cPassphrase := (*C.char)(C.CBytes(opts.Passphrase)) + cOptsSize := C.size_t(C.sizeof_rbd_encryption_luks2_format_options_t) + cOpts := (*C.rbd_encryption_luks2_format_options_t)(C.malloc(cOptsSize)) + cOpts.alg = C.rbd_encryption_algorithm_t(opts.Alg) + cOpts.passphrase = cPassphrase + cOpts.passphrase_size = C.size_t(len(opts.Passphrase)) + + spec.format = C.RBD_ENCRYPTION_FORMAT_LUKS2 + spec.opts = C.rbd_encryption_options_t(cOpts) + spec.opts_size = cOptsSize + return func() { + C.free(unsafe.Pointer(cOpts.passphrase)) + C.free(unsafe.Pointer(cOpts)) + } +} + +// EncryptionLoad2 enables IO on an open encrypted image. Multiple encryption +// option values can be passed to this call in a slice. For more information +// about how items in the slice are applied to images, and possibly ancestor +// images refer to the documentation in the C api for rbd_encryption_load2. +// +// Implements: +// +// int rbd_encryption_load2(rbd_image_t image, +// const rbd_encryption_spec_t *specs, +// size_t spec_count); +func (image *Image) EncryptionLoad2(opts []EncryptionOptions) error { + if image.image == nil { + return ErrImageNotOpen + } + for _, o := range opts { + if _, ok := o.(encryptionOptions2); !ok { + // this should not happen unless someone adds a new type + // implementing EncryptionOptions but fails to add a + // writeEncryptionSpec such that the type is not also implementing + // encryptionOptions2. + return getError(C.EINVAL) + } + } + + length := len(opts) + cspecs := (*C.rbd_encryption_spec_t)(C.malloc( + C.size_t(C.sizeof_rbd_encryption_spec_t * length))) + specs := unsafe.Slice(cspecs, length) + freeFuncs := make([]func(), length) + + for idx, option := range opts { + f := option.(encryptionOptions2).writeEncryptionSpec(&specs[idx]) + freeFuncs[idx] = f + } + defer func() { + for _, f := range freeFuncs { + f() + } + C.free(unsafe.Pointer(cspecs)) + }() + + ret := C.rbd_encryption_load2( + image.image, + cspecs, + C.size_t(length)) + return getError(ret) +} diff --git a/rbd/encryption_load2_test.go b/rbd/encryption_load2_test.go new file mode 100644 index 000000000..7a7b07578 --- /dev/null +++ b/rbd/encryption_load2_test.go @@ -0,0 +1,261 @@ +//go:build !octopus && !pacific && !quincy && ceph_preview + +package rbd + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestEncryptionLoad2(t *testing.T) { + conn := radosConnect(t) + defer conn.Shutdown() + + poolname := GetUUID() + err := conn.MakePool(poolname) + assert.NoError(t, err) + defer conn.DeletePool(poolname) + + ioctx, err := conn.OpenIOContext(poolname) + require.NoError(t, err) + defer ioctx.Destroy() + + name := GetUUID() + testImageSize := uint64(50) * 1024 * 1024 + options := NewRbdImageOptions() + assert.NoError(t, + options.SetUint64(ImageOptionOrder, uint64(testImageOrder))) + err = CreateImage(ioctx, name, testImageSize, options) + assert.NoError(t, err) + + img, err := OpenImage(ioctx, name, NoSnapshot) + assert.NoError(t, err) + + encOpts := EncryptionOptionsLUKS2{ + Alg: EncryptionAlgorithmAES256, + Passphrase: []byte("test-password"), + } + err = img.EncryptionFormat(encOpts) + assert.NoError(t, err) + + // close the image so we can reopen it and load the encryption info + // then write some encrypted data at the end of the image + err = img.Close() + assert.NoError(t, err) + defer func() { + assert.NoError(t, img.Remove()) + }() + + testData := []byte("Jinxed wizards pluck ivy from the big quilt") + var offset int64 + + t.Run("prepare", func(t *testing.T) { + img, err = OpenImage(ioctx, name, NoSnapshot) + assert.NoError(t, err) + defer img.Close() + err = img.EncryptionLoad2([]EncryptionOptions{encOpts}) + assert.NoError(t, err) + + stats, err := img.Stat() + require.NoError(t, err) + offset = int64(stats.Size) - int64(len(testData)) + + nOut, err := img.WriteAt(testData, offset) + assert.Equal(t, len(testData), nOut) + assert.NoError(t, err) + }) + + t.Run("readEnc", func(t *testing.T) { + require.NotEqual(t, offset, 0) + // Re-open the image, load the encryption format, and read the encrypted data + img, err = OpenImage(ioctx, name, NoSnapshot) + assert.NoError(t, err) + defer img.Close() + err = img.EncryptionLoad2([]EncryptionOptions{encOpts}) + assert.NoError(t, err) + + inData := make([]byte, len(testData)) + nIn, err := img.ReadAt(inData, offset) + assert.Equal(t, nIn, len(testData)) + assert.Equal(t, inData, testData) + assert.NoError(t, err) + }) + + t.Run("noEnc", func(t *testing.T) { + require.NotEqual(t, offset, 0) + // Re-open the image and attempt to read the encrypted data without loading the encryption + img, err = OpenImage(ioctx, name, NoSnapshot) + assert.NoError(t, err) + defer img.Close() + + inData := make([]byte, len(testData)) + nIn, err := img.ReadAt(inData, offset) + assert.Equal(t, nIn, len(testData)) + assert.NotEqual(t, inData, testData) + assert.NoError(t, err) + }) +} + +func TestEncryptionLoad2WithParents(t *testing.T) { + dlength := int64(32) + testData1 := []byte("Very nice object ahead of change") + testData2 := []byte("A nice object encryption applied") + testData3 := []byte("A good object encryption abounds") + testData4 := []byte("Another portion is here and well") + written := [][]byte{} + assert.EqualValues(t, len(testData1), dlength) + assert.EqualValues(t, len(testData2), dlength) + assert.EqualValues(t, len(testData3), dlength) + assert.EqualValues(t, len(testData4), dlength) + + encOpts1 := EncryptionOptionsLUKS1{ + Alg: EncryptionAlgorithmAES128, + Passphrase: []byte("test-password"), + } + encOpts2 := EncryptionOptionsLUKS2{ + Alg: EncryptionAlgorithmAES128, + Passphrase: []byte("test-password"), + } + encOpts3 := EncryptionOptionsLUKS2{ + Alg: EncryptionAlgorithmAES256, + Passphrase: []byte("something-stronger"), + } + + conn := radosConnect(t) + defer conn.Shutdown() + + poolname := GetUUID() + err := conn.MakePool(poolname) + assert.NoError(t, err) + defer conn.DeletePool(poolname) + + ioctx, err := conn.OpenIOContext(poolname) + require.NoError(t, err) + defer ioctx.Destroy() + + name := GetUUID() + testImageSize := uint64(256) * 1024 * 1024 + options := NewRbdImageOptions() + assert.NoError(t, + options.SetUint64(ImageOptionOrder, uint64(testImageOrder))) + err = CreateImage(ioctx, name, testImageSize, options) + assert.NoError(t, err) + + t.Run("prepare", func(t *testing.T) { + img, err := OpenImage(ioctx, name, NoSnapshot) + assert.NoError(t, err) + defer img.Close() + + _, err = img.WriteAt(testData1, 0) + assert.NoError(t, err) + written = append(written, testData1) + }) + + t.Run("createClone1", func(t *testing.T) { + require.Len(t, written, 1) + parent, err := OpenImage(ioctx, name, NoSnapshot) + assert.NoError(t, err) + defer parent.Close() + snap, err := parent.CreateSnapshot("sn1") + assert.NoError(t, err) + err = snap.Protect() + assert.NoError(t, err) + + err = CloneImage(ioctx, name, "sn1", ioctx, name+"clone1", options) + assert.NoError(t, err) + + img, err := OpenImage(ioctx, name+"clone1", NoSnapshot) + assert.NoError(t, err) + defer img.Close() + err = img.EncryptionFormat(encOpts1) + assert.NoError(t, err) + + err = img.EncryptionLoad2([]EncryptionOptions{encOpts1}) + assert.NoError(t, err) + _, err = img.WriteAt(testData2, dlength) + assert.NoError(t, err) + written = append(written, testData2) + }) + + t.Run("createClone2", func(t *testing.T) { + require.Len(t, written, 2) + parentName := name + "clone1" + cloneName := name + "clone2" + + parent, err := OpenImage(ioctx, parentName, NoSnapshot) + assert.NoError(t, err) + defer parent.Close() + snap, err := parent.CreateSnapshot("sn2") + assert.NoError(t, err) + err = snap.Protect() + assert.NoError(t, err) + + err = CloneImage(ioctx, parentName, "sn2", ioctx, cloneName, options) + assert.NoError(t, err) + + img, err := OpenImage(ioctx, cloneName, NoSnapshot) + assert.NoError(t, err) + defer img.Close() + err = img.EncryptionFormat(encOpts2) + assert.NoError(t, err) + + err = img.EncryptionLoad2([]EncryptionOptions{encOpts2, encOpts1}) + assert.NoError(t, err) + _, err = img.WriteAt(testData3, dlength*2) + assert.NoError(t, err) + written = append(written, testData3) + }) + + t.Run("createClone3", func(t *testing.T) { + require.Len(t, written, 3) + parentName := name + "clone2" + cloneName := name + "clone3" + + parent, err := OpenImage(ioctx, parentName, NoSnapshot) + assert.NoError(t, err) + defer parent.Close() + snap, err := parent.CreateSnapshot("sn3") + assert.NoError(t, err) + err = snap.Protect() + assert.NoError(t, err) + + err = CloneImage(ioctx, parentName, "sn3", ioctx, cloneName, options) + assert.NoError(t, err) + + img, err := OpenImage(ioctx, cloneName, NoSnapshot) + assert.NoError(t, err) + defer img.Close() + err = img.EncryptionFormat(encOpts3) + assert.NoError(t, err) + + err = img.EncryptionLoad2([]EncryptionOptions{ + encOpts3, encOpts2, encOpts1, + }) + assert.NoError(t, err) + _, err = img.WriteAt(testData4, dlength*3) + assert.NoError(t, err) + written = append(written, testData4) + }) + + t.Run("readAll", func(t *testing.T) { + require.Len(t, written, 4) + img, err := OpenImage(ioctx, name+"clone3", NoSnapshot) + assert.NoError(t, err) + defer img.Close() + + err = img.EncryptionLoad2([]EncryptionOptions{ + encOpts3, encOpts2, encOpts1, + }) + assert.NoError(t, err) + + inData := make([]byte, int(dlength)) + for idx, td := range written { + n, err := img.ReadAt(inData, int64(idx)*dlength) + assert.NoError(t, err) + assert.EqualValues(t, dlength, n) + assert.Equal(t, inData, td) + } + }) +} diff --git a/rbd/encryption_opt_luks.go b/rbd/encryption_opt_luks.go new file mode 100644 index 000000000..bfacdde54 --- /dev/null +++ b/rbd/encryption_opt_luks.go @@ -0,0 +1,51 @@ +//go:build !octopus && !pacific && !quincy && ceph_preview + +package rbd + +// #cgo LDFLAGS: -lrbd +// /* force XSI-complaint strerror_r() */ +// #define _POSIX_C_SOURCE 200112L +// #undef _GNU_SOURCE +// #include +// #include +import "C" + +import ( + "unsafe" +) + +// EncryptionOptionsLUKS generic options for either LUKS v1 or v2. May only be +// used for opening existing images - not valid for the EncryptionFormat call. +type EncryptionOptionsLUKS struct { + Passphrase []byte +} + +func (opts EncryptionOptionsLUKS) allocateEncryptionOptions() cEncryptionData { + var cOpts C.rbd_encryption_luks_format_options_t + var retData cEncryptionData + // CBytes allocates memory. it will be freed when cEncryptionData.free is called + cOpts.passphrase = (*C.char)(C.CBytes(opts.Passphrase)) + cOpts.passphrase_size = C.size_t(len(opts.Passphrase)) + retData.opts = C.rbd_encryption_options_t(&cOpts) + retData.optsSize = C.size_t(C.sizeof_rbd_encryption_luks_format_options_t) + retData.free = func() { C.free(unsafe.Pointer(cOpts.passphrase)) } + retData.format = C.RBD_ENCRYPTION_FORMAT_LUKS + return retData +} + +func (opts EncryptionOptionsLUKS) writeEncryptionSpec(spec *C.rbd_encryption_spec_t) func() { + /* only C memory should be attached to spec */ + cPassphrase := (*C.char)(C.CBytes(opts.Passphrase)) + cOptsSize := C.size_t(C.sizeof_rbd_encryption_luks_format_options_t) + cOpts := (*C.rbd_encryption_luks_format_options_t)(C.malloc(cOptsSize)) + cOpts.passphrase = cPassphrase + cOpts.passphrase_size = C.size_t(len(opts.Passphrase)) + + spec.format = C.RBD_ENCRYPTION_FORMAT_LUKS + spec.opts = C.rbd_encryption_options_t(cOpts) + spec.opts_size = cOptsSize + return func() { + C.free(unsafe.Pointer(cOpts.passphrase)) + C.free(unsafe.Pointer(cOpts)) + } +} diff --git a/rbd/encryption_opt_luks_test.go b/rbd/encryption_opt_luks_test.go new file mode 100644 index 000000000..10b6d9f10 --- /dev/null +++ b/rbd/encryption_opt_luks_test.go @@ -0,0 +1,103 @@ +//go:build !octopus && !pacific && !quincy && ceph_preview + +package rbd + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestEncryptionOptionsLUKS(t *testing.T) { + conn := radosConnect(t) + defer conn.Shutdown() + + poolname := GetUUID() + err := conn.MakePool(poolname) + assert.NoError(t, err) + defer conn.DeletePool(poolname) + + ioctx, err := conn.OpenIOContext(poolname) + require.NoError(t, err) + defer ioctx.Destroy() + + name := GetUUID() + testImageSize := uint64(50) * 1024 * 1024 + options := NewRbdImageOptions() + assert.NoError(t, + options.SetUint64(ImageOptionOrder, uint64(testImageOrder))) + err = CreateImage(ioctx, name, testImageSize, options) + assert.NoError(t, err) + + img, err := OpenImage(ioctx, name, NoSnapshot) + assert.NoError(t, err) + + encOpts := EncryptionOptionsLUKS2{ + Alg: EncryptionAlgorithmAES256, + Passphrase: []byte("sesame-123-luks-ury"), + } + err = img.EncryptionFormat(encOpts) + assert.NoError(t, err) + + // close the image so we can reopen it and load the encryption info + // then write some encrypted data at the end of the image + err = img.Close() + assert.NoError(t, err) + defer func() { + assert.NoError(t, img.Remove()) + }() + + testData := []byte("Another day another image to unlock") + var offset int64 + + t.Run("prepare", func(t *testing.T) { + img, err = OpenImage(ioctx, name, NoSnapshot) + assert.NoError(t, err) + defer img.Close() + err = img.EncryptionLoad2([]EncryptionOptions{encOpts}) + assert.NoError(t, err) + + stats, err := img.Stat() + require.NoError(t, err) + offset = int64(stats.Size) - int64(len(testData)) + + nOut, err := img.WriteAt(testData, offset) + assert.Equal(t, len(testData), nOut) + assert.NoError(t, err) + }) + + unlock := []EncryptionOptions{ + EncryptionOptionsLUKS{Passphrase: []byte("sesame-123-luks-ury")}, + } + + t.Run("readEnc", func(t *testing.T) { + require.NotEqual(t, offset, 0) + // Re-open the image, using the generic luks encryption options + img, err = OpenImage(ioctx, name, NoSnapshot) + assert.NoError(t, err) + defer img.Close() + err = img.EncryptionLoad2(unlock) + assert.NoError(t, err) + + inData := make([]byte, len(testData)) + nIn, err := img.ReadAt(inData, offset) + assert.Equal(t, nIn, len(testData)) + assert.Equal(t, inData, testData) + assert.NoError(t, err) + }) + + t.Run("noEnc", func(t *testing.T) { + require.NotEqual(t, offset, 0) + // Re-open the image and attempt to read the encrypted data without loading the encryption + img, err = OpenImage(ioctx, name, NoSnapshot) + assert.NoError(t, err) + defer img.Close() + + inData := make([]byte, len(testData)) + nIn, err := img.ReadAt(inData, offset) + assert.Equal(t, nIn, len(testData)) + assert.NotEqual(t, inData, testData) + assert.NoError(t, err) + }) +} diff --git a/rbd/encryption_test.go b/rbd/encryption_test.go index 3bc066158..b5d9851c9 100644 --- a/rbd/encryption_test.go +++ b/rbd/encryption_test.go @@ -49,13 +49,16 @@ func TestEncryptionFormat(t *testing.T) { func TestEncryptionLoad(t *testing.T) { conn := radosConnect(t) + defer conn.Shutdown() poolname := GetUUID() err := conn.MakePool(poolname) assert.NoError(t, err) + defer conn.DeletePool(poolname) ioctx, err := conn.OpenIOContext(poolname) require.NoError(t, err) + defer ioctx.Destroy() name := GetUUID() testImageSize := uint64(1 << 23) // format requires more than 4194304 bytes @@ -78,55 +81,58 @@ func TestEncryptionLoad(t *testing.T) { // then write some encrypted data at the end of the image err = img.Close() assert.NoError(t, err) - img, err = OpenImage(ioctx, name, NoSnapshot) - err = img.EncryptionLoad(opts) - assert.NoError(t, err) + defer func() { + assert.NoError(t, img.Remove()) + }() - outData := []byte("Hi rbd! Nice to talk through go-ceph :)") - - stats, err := img.Stat() - require.NoError(t, err) - offset := int64(stats.Size) - int64(len(outData)) - - nOut, err := img.WriteAt(outData, offset) - assert.Equal(t, len(outData), nOut) - assert.NoError(t, err) - - err = img.Close() - assert.NoError(t, err) + testData := []byte("Hi rbd! Nice to talk through go-ceph :)") + var offset int64 - // Re-open the image, load the encryption format, and read the encrypted data - img, err = OpenImage(ioctx, name, NoSnapshot) - assert.NoError(t, err) - err = img.EncryptionLoad(opts) - assert.NoError(t, err) + t.Run("prepare", func(t *testing.T) { + img, err = OpenImage(ioctx, name, NoSnapshot) + assert.NoError(t, err) + defer img.Close() + err = img.EncryptionLoad(opts) + assert.NoError(t, err) - inData := make([]byte, len(outData)) - nIn, err := img.ReadAt(inData, offset) - assert.Equal(t, nIn, len(inData)) - assert.Equal(t, inData, outData) - assert.NoError(t, err) + stats, err := img.Stat() + require.NoError(t, err) + offset = int64(stats.Size) - int64(len(testData)) - err = img.Close() - assert.NoError(t, err) + nOut, err := img.WriteAt(testData, offset) + assert.Equal(t, len(testData), nOut) + assert.NoError(t, err) + }) - // Re-open the image and attempt to read the encrypted data without loading the encryption - img, err = OpenImage(ioctx, name, NoSnapshot) - assert.NoError(t, err) + t.Run("readEnc", func(t *testing.T) { + require.NotEqual(t, offset, 0) + // Re-open the image, load the encryption format, and read the encrypted data + img, err = OpenImage(ioctx, name, NoSnapshot) + assert.NoError(t, err) + defer img.Close() + err = img.EncryptionLoad(opts) + assert.NoError(t, err) - nIn, err = img.ReadAt(inData, offset) - assert.Equal(t, nIn, len(inData)) - assert.NotEqual(t, inData, outData) - assert.NoError(t, err) + inData := make([]byte, len(testData)) + nIn, err := img.ReadAt(inData, offset) + assert.Equal(t, nIn, len(inData)) + assert.Equal(t, inData, testData) + assert.NoError(t, err) + }) - err = img.Close() - assert.NoError(t, err) - err = img.Remove() - assert.NoError(t, err) + t.Run("noEnc", func(t *testing.T) { + require.NotEqual(t, offset, 0) + // Re-open the image and attempt to read the encrypted data without loading the encryption + img, err = OpenImage(ioctx, name, NoSnapshot) + assert.NoError(t, err) + defer img.Close() - ioctx.Destroy() - conn.DeletePool(poolname) - conn.Shutdown() + inData := make([]byte, len(testData)) + nIn, err := img.ReadAt(inData, offset) + assert.Equal(t, nIn, len(inData)) + assert.NotEqual(t, inData, testData) + assert.NoError(t, err) + }) } func TestEncryptedResize(t *testing.T) {