|
@@ -39,6 +39,7 @@ typedef struct BlockCrypto BlockCrypto;
|
|
struct BlockCrypto {
|
|
struct BlockCrypto {
|
|
QCryptoBlock *block;
|
|
QCryptoBlock *block;
|
|
bool updating_keys;
|
|
bool updating_keys;
|
|
|
|
+ BdrvChild *header; /* Reference to the detached LUKS header */
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
@@ -63,12 +64,14 @@ static int block_crypto_read_func(QCryptoBlock *block,
|
|
Error **errp)
|
|
Error **errp)
|
|
{
|
|
{
|
|
BlockDriverState *bs = opaque;
|
|
BlockDriverState *bs = opaque;
|
|
|
|
+ BlockCrypto *crypto = bs->opaque;
|
|
ssize_t ret;
|
|
ssize_t ret;
|
|
|
|
|
|
GLOBAL_STATE_CODE();
|
|
GLOBAL_STATE_CODE();
|
|
GRAPH_RDLOCK_GUARD_MAINLOOP();
|
|
GRAPH_RDLOCK_GUARD_MAINLOOP();
|
|
|
|
|
|
- ret = bdrv_pread(bs->file, offset, buflen, buf, 0);
|
|
|
|
|
|
+ ret = bdrv_pread(crypto->header ? crypto->header : bs->file,
|
|
|
|
+ offset, buflen, buf, 0);
|
|
if (ret < 0) {
|
|
if (ret < 0) {
|
|
error_setg_errno(errp, -ret, "Could not read encryption header");
|
|
error_setg_errno(errp, -ret, "Could not read encryption header");
|
|
return ret;
|
|
return ret;
|
|
@@ -84,12 +87,14 @@ static int block_crypto_write_func(QCryptoBlock *block,
|
|
Error **errp)
|
|
Error **errp)
|
|
{
|
|
{
|
|
BlockDriverState *bs = opaque;
|
|
BlockDriverState *bs = opaque;
|
|
|
|
+ BlockCrypto *crypto = bs->opaque;
|
|
ssize_t ret;
|
|
ssize_t ret;
|
|
|
|
|
|
GLOBAL_STATE_CODE();
|
|
GLOBAL_STATE_CODE();
|
|
GRAPH_RDLOCK_GUARD_MAINLOOP();
|
|
GRAPH_RDLOCK_GUARD_MAINLOOP();
|
|
|
|
|
|
- ret = bdrv_pwrite(bs->file, offset, buflen, buf, 0);
|
|
|
|
|
|
+ ret = bdrv_pwrite(crypto->header ? crypto->header : bs->file,
|
|
|
|
+ offset, buflen, buf, 0);
|
|
if (ret < 0) {
|
|
if (ret < 0) {
|
|
error_setg_errno(errp, -ret, "Could not write encryption header");
|
|
error_setg_errno(errp, -ret, "Could not write encryption header");
|
|
return ret;
|
|
return ret;
|
|
@@ -157,6 +162,48 @@ error:
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int coroutine_fn GRAPH_UNLOCKED
|
|
|
|
+block_crypto_co_format_luks_payload(BlockdevCreateOptionsLUKS *luks_opts,
|
|
|
|
+ Error **errp)
|
|
|
|
+{
|
|
|
|
+ BlockDriverState *bs = NULL;
|
|
|
|
+ BlockBackend *blk = NULL;
|
|
|
|
+ Error *local_error = NULL;
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ if (luks_opts->size > INT64_MAX) {
|
|
|
|
+ return -EFBIG;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ bs = bdrv_co_open_blockdev_ref(luks_opts->file, errp);
|
|
|
|
+ if (bs == NULL) {
|
|
|
|
+ return -EIO;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ blk = blk_co_new_with_bs(bs, BLK_PERM_WRITE | BLK_PERM_RESIZE,
|
|
|
|
+ BLK_PERM_ALL, errp);
|
|
|
|
+ if (!blk) {
|
|
|
|
+ ret = -EPERM;
|
|
|
|
+ goto fail;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ret = blk_truncate(blk, luks_opts->size, true,
|
|
|
|
+ luks_opts->preallocation, 0, &local_error);
|
|
|
|
+ if (ret < 0) {
|
|
|
|
+ if (ret == -EFBIG) {
|
|
|
|
+ /* Replace the error message with a better one */
|
|
|
|
+ error_free(local_error);
|
|
|
|
+ error_setg(errp, "The requested file size is too large");
|
|
|
|
+ }
|
|
|
|
+ goto fail;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ret = 0;
|
|
|
|
+
|
|
|
|
+fail:
|
|
|
|
+ bdrv_co_unref(bs);
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
|
|
static QemuOptsList block_crypto_runtime_opts_luks = {
|
|
static QemuOptsList block_crypto_runtime_opts_luks = {
|
|
.name = "crypto",
|
|
.name = "crypto",
|
|
@@ -184,6 +231,7 @@ static QemuOptsList block_crypto_create_opts_luks = {
|
|
BLOCK_CRYPTO_OPT_DEF_LUKS_IVGEN_HASH_ALG(""),
|
|
BLOCK_CRYPTO_OPT_DEF_LUKS_IVGEN_HASH_ALG(""),
|
|
BLOCK_CRYPTO_OPT_DEF_LUKS_HASH_ALG(""),
|
|
BLOCK_CRYPTO_OPT_DEF_LUKS_HASH_ALG(""),
|
|
BLOCK_CRYPTO_OPT_DEF_LUKS_ITER_TIME(""),
|
|
BLOCK_CRYPTO_OPT_DEF_LUKS_ITER_TIME(""),
|
|
|
|
+ BLOCK_CRYPTO_OPT_DEF_LUKS_DETACHED_HEADER(""),
|
|
{ /* end of list */ }
|
|
{ /* end of list */ }
|
|
},
|
|
},
|
|
};
|
|
};
|
|
@@ -262,6 +310,8 @@ static int block_crypto_open_generic(QCryptoBlockFormat format,
|
|
int flags,
|
|
int flags,
|
|
Error **errp)
|
|
Error **errp)
|
|
{
|
|
{
|
|
|
|
+ ERRP_GUARD();
|
|
|
|
+
|
|
BlockCrypto *crypto = bs->opaque;
|
|
BlockCrypto *crypto = bs->opaque;
|
|
QemuOpts *opts = NULL;
|
|
QemuOpts *opts = NULL;
|
|
int ret;
|
|
int ret;
|
|
@@ -276,6 +326,13 @@ static int block_crypto_open_generic(QCryptoBlockFormat format,
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ crypto->header = bdrv_open_child(NULL, options, "header", bs,
|
|
|
|
+ &child_of_bds, BDRV_CHILD_METADATA,
|
|
|
|
+ true, errp);
|
|
|
|
+ if (*errp != NULL) {
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ }
|
|
|
|
+
|
|
GRAPH_RDLOCK_GUARD_MAINLOOP();
|
|
GRAPH_RDLOCK_GUARD_MAINLOOP();
|
|
|
|
|
|
bs->supported_write_flags = BDRV_REQ_FUA &
|
|
bs->supported_write_flags = BDRV_REQ_FUA &
|
|
@@ -299,6 +356,9 @@ static int block_crypto_open_generic(QCryptoBlockFormat format,
|
|
if (flags & BDRV_O_NO_IO) {
|
|
if (flags & BDRV_O_NO_IO) {
|
|
cflags |= QCRYPTO_BLOCK_OPEN_NO_IO;
|
|
cflags |= QCRYPTO_BLOCK_OPEN_NO_IO;
|
|
}
|
|
}
|
|
|
|
+ if (crypto->header != NULL) {
|
|
|
|
+ cflags |= QCRYPTO_BLOCK_OPEN_DETACHED;
|
|
|
|
+ }
|
|
crypto->block = qcrypto_block_open(open_opts, NULL,
|
|
crypto->block = qcrypto_block_open(open_opts, NULL,
|
|
block_crypto_read_func,
|
|
block_crypto_read_func,
|
|
bs,
|
|
bs,
|
|
@@ -324,7 +384,9 @@ static int block_crypto_open_generic(QCryptoBlockFormat format,
|
|
static int coroutine_fn GRAPH_UNLOCKED
|
|
static int coroutine_fn GRAPH_UNLOCKED
|
|
block_crypto_co_create_generic(BlockDriverState *bs, int64_t size,
|
|
block_crypto_co_create_generic(BlockDriverState *bs, int64_t size,
|
|
QCryptoBlockCreateOptions *opts,
|
|
QCryptoBlockCreateOptions *opts,
|
|
- PreallocMode prealloc, Error **errp)
|
|
|
|
|
|
+ PreallocMode prealloc,
|
|
|
|
+ unsigned int flags,
|
|
|
|
+ Error **errp)
|
|
{
|
|
{
|
|
int ret;
|
|
int ret;
|
|
BlockBackend *blk;
|
|
BlockBackend *blk;
|
|
@@ -344,7 +406,7 @@ block_crypto_co_create_generic(BlockDriverState *bs, int64_t size,
|
|
|
|
|
|
data = (struct BlockCryptoCreateData) {
|
|
data = (struct BlockCryptoCreateData) {
|
|
.blk = blk,
|
|
.blk = blk,
|
|
- .size = size,
|
|
|
|
|
|
+ .size = flags & QCRYPTO_BLOCK_CREATE_DETACHED ? 0 : size,
|
|
.prealloc = prealloc,
|
|
.prealloc = prealloc,
|
|
};
|
|
};
|
|
|
|
|
|
@@ -352,6 +414,7 @@ block_crypto_co_create_generic(BlockDriverState *bs, int64_t size,
|
|
block_crypto_create_init_func,
|
|
block_crypto_create_init_func,
|
|
block_crypto_create_write_func,
|
|
block_crypto_create_write_func,
|
|
&data,
|
|
&data,
|
|
|
|
+ flags,
|
|
errp);
|
|
errp);
|
|
|
|
|
|
if (!crypto) {
|
|
if (!crypto) {
|
|
@@ -638,17 +701,27 @@ static int coroutine_fn GRAPH_UNLOCKED
|
|
block_crypto_co_create_luks(BlockdevCreateOptions *create_options, Error **errp)
|
|
block_crypto_co_create_luks(BlockdevCreateOptions *create_options, Error **errp)
|
|
{
|
|
{
|
|
BlockdevCreateOptionsLUKS *luks_opts;
|
|
BlockdevCreateOptionsLUKS *luks_opts;
|
|
|
|
+ BlockDriverState *hdr_bs = NULL;
|
|
BlockDriverState *bs = NULL;
|
|
BlockDriverState *bs = NULL;
|
|
QCryptoBlockCreateOptions create_opts;
|
|
QCryptoBlockCreateOptions create_opts;
|
|
PreallocMode preallocation = PREALLOC_MODE_OFF;
|
|
PreallocMode preallocation = PREALLOC_MODE_OFF;
|
|
|
|
+ unsigned int cflags = 0;
|
|
int ret;
|
|
int ret;
|
|
|
|
|
|
assert(create_options->driver == BLOCKDEV_DRIVER_LUKS);
|
|
assert(create_options->driver == BLOCKDEV_DRIVER_LUKS);
|
|
luks_opts = &create_options->u.luks;
|
|
luks_opts = &create_options->u.luks;
|
|
|
|
|
|
- bs = bdrv_co_open_blockdev_ref(luks_opts->file, errp);
|
|
|
|
- if (bs == NULL) {
|
|
|
|
- return -EIO;
|
|
|
|
|
|
+ if (luks_opts->header == NULL && luks_opts->file == NULL) {
|
|
|
|
+ error_setg(errp, "Either the parameter 'header' or 'file' must "
|
|
|
|
+ "be specified");
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if ((luks_opts->preallocation != PREALLOC_MODE_OFF) &&
|
|
|
|
+ (luks_opts->file == NULL)) {
|
|
|
|
+ error_setg(errp, "Parameter 'preallocation' requires 'file' to be "
|
|
|
|
+ "specified for formatting LUKS disk");
|
|
|
|
+ return -EINVAL;
|
|
}
|
|
}
|
|
|
|
|
|
create_opts = (QCryptoBlockCreateOptions) {
|
|
create_opts = (QCryptoBlockCreateOptions) {
|
|
@@ -660,15 +733,52 @@ block_crypto_co_create_luks(BlockdevCreateOptions *create_options, Error **errp)
|
|
preallocation = luks_opts->preallocation;
|
|
preallocation = luks_opts->preallocation;
|
|
}
|
|
}
|
|
|
|
|
|
- ret = block_crypto_co_create_generic(bs, luks_opts->size, &create_opts,
|
|
|
|
- preallocation, errp);
|
|
|
|
- if (ret < 0) {
|
|
|
|
- goto fail;
|
|
|
|
|
|
+ if (luks_opts->header) {
|
|
|
|
+ /* LUKS volume with detached header */
|
|
|
|
+ hdr_bs = bdrv_co_open_blockdev_ref(luks_opts->header, errp);
|
|
|
|
+ if (hdr_bs == NULL) {
|
|
|
|
+ return -EIO;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ cflags |= QCRYPTO_BLOCK_CREATE_DETACHED;
|
|
|
|
+
|
|
|
|
+ /* Format the LUKS header node */
|
|
|
|
+ ret = block_crypto_co_create_generic(hdr_bs, 0, &create_opts,
|
|
|
|
+ PREALLOC_MODE_OFF, cflags, errp);
|
|
|
|
+ if (ret < 0) {
|
|
|
|
+ goto fail;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Format the LUKS payload node */
|
|
|
|
+ if (luks_opts->file) {
|
|
|
|
+ ret = block_crypto_co_format_luks_payload(luks_opts, errp);
|
|
|
|
+ if (ret < 0) {
|
|
|
|
+ goto fail;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ } else if (luks_opts->file) {
|
|
|
|
+ /* LUKS volume with none-detached header */
|
|
|
|
+ bs = bdrv_co_open_blockdev_ref(luks_opts->file, errp);
|
|
|
|
+ if (bs == NULL) {
|
|
|
|
+ return -EIO;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ret = block_crypto_co_create_generic(bs, luks_opts->size, &create_opts,
|
|
|
|
+ preallocation, cflags, errp);
|
|
|
|
+ if (ret < 0) {
|
|
|
|
+ goto fail;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
ret = 0;
|
|
ret = 0;
|
|
fail:
|
|
fail:
|
|
- bdrv_co_unref(bs);
|
|
|
|
|
|
+ if (hdr_bs != NULL) {
|
|
|
|
+ bdrv_co_unref(hdr_bs);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (bs != NULL) {
|
|
|
|
+ bdrv_co_unref(bs);
|
|
|
|
+ }
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -682,6 +792,9 @@ block_crypto_co_create_opts_luks(BlockDriver *drv, const char *filename,
|
|
PreallocMode prealloc;
|
|
PreallocMode prealloc;
|
|
char *buf = NULL;
|
|
char *buf = NULL;
|
|
int64_t size;
|
|
int64_t size;
|
|
|
|
+ bool detached_hdr =
|
|
|
|
+ qemu_opt_get_bool(opts, "detached-header", false);
|
|
|
|
+ unsigned int cflags = 0;
|
|
int ret;
|
|
int ret;
|
|
Error *local_err = NULL;
|
|
Error *local_err = NULL;
|
|
|
|
|
|
@@ -721,8 +834,13 @@ block_crypto_co_create_opts_luks(BlockDriver *drv, const char *filename,
|
|
goto fail;
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ if (detached_hdr) {
|
|
|
|
+ cflags |= QCRYPTO_BLOCK_CREATE_DETACHED;
|
|
|
|
+ }
|
|
|
|
+
|
|
/* Create format layer */
|
|
/* Create format layer */
|
|
- ret = block_crypto_co_create_generic(bs, size, create_opts, prealloc, errp);
|
|
|
|
|
|
+ ret = block_crypto_co_create_generic(bs, size, create_opts,
|
|
|
|
+ prealloc, cflags, errp);
|
|
if (ret < 0) {
|
|
if (ret < 0) {
|
|
goto fail;
|
|
goto fail;
|
|
}
|
|
}
|