#include "sysemu/block-backend.h"
#include "crypto/block.h"
#include "qapi/opts-visitor.h"
+#include "qapi/qobject-input-visitor.h"
#include "qapi-visit.h"
#include "qapi/error.h"
-
-#define BLOCK_CRYPTO_OPT_LUKS_KEY_SECRET "key-secret"
-#define BLOCK_CRYPTO_OPT_LUKS_CIPHER_ALG "cipher-alg"
-#define BLOCK_CRYPTO_OPT_LUKS_CIPHER_MODE "cipher-mode"
-#define BLOCK_CRYPTO_OPT_LUKS_IVGEN_ALG "ivgen-alg"
-#define BLOCK_CRYPTO_OPT_LUKS_IVGEN_HASH_ALG "ivgen-hash-alg"
-#define BLOCK_CRYPTO_OPT_LUKS_HASH_ALG "hash-alg"
+#include "block/crypto.h"
typedef struct BlockCrypto BlockCrypto;
size_t offset,
uint8_t *buf,
size_t buflen,
- Error **errp,
- void *opaque)
+ void *opaque,
+ Error **errp)
{
BlockDriverState *bs = opaque;
ssize_t ret;
- ret = bdrv_pread(bs->file->bs, offset, buf, buflen);
+ ret = bdrv_pread(bs->file, offset, buf, buflen);
if (ret < 0) {
error_setg_errno(errp, -ret, "Could not read encryption header");
return ret;
size_t offset,
const uint8_t *buf,
size_t buflen,
- Error **errp,
- void *opaque)
+ void *opaque,
+ Error **errp)
{
struct BlockCryptoCreateData *data = opaque;
ssize_t ret;
static ssize_t block_crypto_init_func(QCryptoBlock *block,
size_t headerlen,
- Error **errp,
- void *opaque)
+ void *opaque,
+ Error **errp)
{
struct BlockCryptoCreateData *data = opaque;
int ret;
.name = "crypto",
.head = QTAILQ_HEAD_INITIALIZER(block_crypto_runtime_opts_luks.head),
.desc = {
- {
- .name = BLOCK_CRYPTO_OPT_LUKS_KEY_SECRET,
- .type = QEMU_OPT_STRING,
- .help = "ID of the secret that provides the encryption key",
- },
+ BLOCK_CRYPTO_OPT_DEF_LUKS_KEY_SECRET(""),
{ /* end of list */ }
},
};
.type = QEMU_OPT_SIZE,
.help = "Virtual disk size"
},
- {
- .name = BLOCK_CRYPTO_OPT_LUKS_KEY_SECRET,
- .type = QEMU_OPT_STRING,
- .help = "ID of the secret that provides the encryption key",
- },
- {
- .name = BLOCK_CRYPTO_OPT_LUKS_CIPHER_ALG,
- .type = QEMU_OPT_STRING,
- .help = "Name of encryption cipher algorithm",
- },
- {
- .name = BLOCK_CRYPTO_OPT_LUKS_CIPHER_MODE,
- .type = QEMU_OPT_STRING,
- .help = "Name of encryption cipher mode",
- },
- {
- .name = BLOCK_CRYPTO_OPT_LUKS_IVGEN_ALG,
- .type = QEMU_OPT_STRING,
- .help = "Name of IV generator algorithm",
- },
- {
- .name = BLOCK_CRYPTO_OPT_LUKS_IVGEN_HASH_ALG,
- .type = QEMU_OPT_STRING,
- .help = "Name of IV generator hash algorithm",
- },
- {
- .name = BLOCK_CRYPTO_OPT_LUKS_HASH_ALG,
- .type = QEMU_OPT_STRING,
- .help = "Name of encryption hash algorithm",
- },
+ BLOCK_CRYPTO_OPT_DEF_LUKS_KEY_SECRET(""),
+ BLOCK_CRYPTO_OPT_DEF_LUKS_CIPHER_ALG(""),
+ BLOCK_CRYPTO_OPT_DEF_LUKS_CIPHER_MODE(""),
+ BLOCK_CRYPTO_OPT_DEF_LUKS_IVGEN_ALG(""),
+ BLOCK_CRYPTO_OPT_DEF_LUKS_IVGEN_HASH_ALG(""),
+ BLOCK_CRYPTO_OPT_DEF_LUKS_HASH_ALG(""),
+ BLOCK_CRYPTO_OPT_DEF_LUKS_ITER_TIME(""),
{ /* end of list */ }
},
};
-static QCryptoBlockOpenOptions *
+QCryptoBlockOpenOptions *
block_crypto_open_opts_init(QCryptoBlockFormat format,
- QemuOpts *opts,
+ QDict *opts,
Error **errp)
{
- OptsVisitor *ov;
+ Visitor *v;
QCryptoBlockOpenOptions *ret = NULL;
Error *local_err = NULL;
ret = g_new0(QCryptoBlockOpenOptions, 1);
ret->format = format;
- ov = opts_visitor_new(opts);
+ v = qobject_input_visitor_new_keyval(QOBJECT(opts));
- visit_start_struct(opts_get_visitor(ov),
- NULL, NULL, 0, &local_err);
+ visit_start_struct(v, NULL, NULL, 0, &local_err);
if (local_err) {
goto out;
}
switch (format) {
case Q_CRYPTO_BLOCK_FORMAT_LUKS:
visit_type_QCryptoBlockOptionsLUKS_members(
- opts_get_visitor(ov), &ret->u.luks, &local_err);
+ v, &ret->u.luks, &local_err);
+ break;
+
+ case Q_CRYPTO_BLOCK_FORMAT_QCOW:
+ visit_type_QCryptoBlockOptionsQCow_members(
+ v, &ret->u.qcow, &local_err);
break;
default:
break;
}
if (!local_err) {
- visit_check_struct(opts_get_visitor(ov), &local_err);
+ visit_check_struct(v, &local_err);
}
- visit_end_struct(opts_get_visitor(ov));
+ visit_end_struct(v, NULL);
out:
if (local_err) {
qapi_free_QCryptoBlockOpenOptions(ret);
ret = NULL;
}
- opts_visitor_cleanup(ov);
+ visit_free(v);
return ret;
}
-static QCryptoBlockCreateOptions *
+QCryptoBlockCreateOptions *
block_crypto_create_opts_init(QCryptoBlockFormat format,
- QemuOpts *opts,
+ QDict *opts,
Error **errp)
{
- OptsVisitor *ov;
+ Visitor *v;
QCryptoBlockCreateOptions *ret = NULL;
Error *local_err = NULL;
ret = g_new0(QCryptoBlockCreateOptions, 1);
ret->format = format;
- ov = opts_visitor_new(opts);
+ v = qobject_input_visitor_new_keyval(QOBJECT(opts));
- visit_start_struct(opts_get_visitor(ov),
- NULL, NULL, 0, &local_err);
+ visit_start_struct(v, NULL, NULL, 0, &local_err);
if (local_err) {
goto out;
}
switch (format) {
case Q_CRYPTO_BLOCK_FORMAT_LUKS:
visit_type_QCryptoBlockCreateOptionsLUKS_members(
- opts_get_visitor(ov), &ret->u.luks, &local_err);
+ v, &ret->u.luks, &local_err);
+ break;
+
+ case Q_CRYPTO_BLOCK_FORMAT_QCOW:
+ visit_type_QCryptoBlockOptionsQCow_members(
+ v, &ret->u.qcow, &local_err);
break;
default:
break;
}
if (!local_err) {
- visit_check_struct(opts_get_visitor(ov), &local_err);
+ visit_check_struct(v, &local_err);
}
- visit_end_struct(opts_get_visitor(ov));
+ visit_end_struct(v, NULL);
out:
if (local_err) {
qapi_free_QCryptoBlockCreateOptions(ret);
ret = NULL;
}
- opts_visitor_cleanup(ov);
+ visit_free(v);
return ret;
}
int ret = -EINVAL;
QCryptoBlockOpenOptions *open_opts = NULL;
unsigned int cflags = 0;
+ QDict *cryptoopts = NULL;
+
+ bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
+ false, errp);
+ if (!bs->file) {
+ return -EINVAL;
+ }
opts = qemu_opts_create(opts_spec, NULL, 0, &error_abort);
qemu_opts_absorb_qdict(opts, options, &local_err);
goto cleanup;
}
- open_opts = block_crypto_open_opts_init(format, opts, errp);
+ cryptoopts = qemu_opts_to_qdict(opts, NULL);
+
+ open_opts = block_crypto_open_opts_init(format, cryptoopts, errp);
if (!open_opts) {
goto cleanup;
}
if (flags & BDRV_O_NO_IO) {
cflags |= QCRYPTO_BLOCK_OPEN_NO_IO;
}
- crypto->block = qcrypto_block_open(open_opts,
+ crypto->block = qcrypto_block_open(open_opts, NULL,
block_crypto_read_func,
bs,
cflags,
}
bs->encrypted = true;
- bs->valid_key = true;
ret = 0;
cleanup:
+ QDECREF(cryptoopts);
qapi_free_QCryptoBlockOpenOptions(open_opts);
return ret;
}
.opts = opts,
.filename = filename,
};
+ QDict *cryptoopts;
- create_opts = block_crypto_create_opts_init(format, opts, errp);
+ cryptoopts = qemu_opts_to_qdict(opts, NULL);
+
+ create_opts = block_crypto_create_opts_init(format, cryptoopts, errp);
if (!create_opts) {
return -1;
}
- crypto = qcrypto_block_create(create_opts,
+ crypto = qcrypto_block_create(create_opts, NULL,
block_crypto_init_func,
block_crypto_write_func,
&data,
ret = 0;
cleanup:
+ QDECREF(cryptoopts);
qcrypto_block_free(crypto);
blk_unref(data.blk);
qapi_free_QCryptoBlockCreateOptions(create_opts);
return ret;
}
-static int block_crypto_truncate(BlockDriverState *bs, int64_t offset)
+static int block_crypto_truncate(BlockDriverState *bs, int64_t offset,
+ PreallocMode prealloc, Error **errp)
{
BlockCrypto *crypto = bs->opaque;
size_t payload_offset =
offset += payload_offset;
- return bdrv_truncate(bs->file->bs, offset);
+ return bdrv_truncate(bs->file, offset, prealloc, errp);
}
static void block_crypto_close(BlockDriverState *bs)
qemu_iovec_reset(&hd_qiov);
qemu_iovec_add(&hd_qiov, cipher_data, cur_nr_sectors * 512);
- ret = bdrv_co_readv(bs->file->bs,
+ ret = bdrv_co_readv(bs->file,
payload_offset + sector_num,
cur_nr_sectors, &hd_qiov);
if (ret < 0) {
qemu_iovec_reset(&hd_qiov);
qemu_iovec_add(&hd_qiov, cipher_data, cur_nr_sectors * 512);
- ret = bdrv_co_writev(bs->file->bs,
+ ret = bdrv_co_writev(bs->file,
payload_offset + sector_num,
cur_nr_sectors, &hd_qiov);
if (ret < 0) {
filename, opts, errp);
}
+static int block_crypto_get_info_luks(BlockDriverState *bs,
+ BlockDriverInfo *bdi)
+{
+ BlockDriverInfo subbdi;
+ int ret;
+
+ ret = bdrv_get_info(bs->file->bs, &subbdi);
+ if (ret != 0) {
+ return ret;
+ }
+
+ bdi->unallocated_blocks_are_zero = false;
+ bdi->can_write_zeroes_with_unmap = false;
+ bdi->cluster_size = subbdi.cluster_size;
+
+ return 0;
+}
+
+static ImageInfoSpecific *
+block_crypto_get_specific_info_luks(BlockDriverState *bs)
+{
+ BlockCrypto *crypto = bs->opaque;
+ ImageInfoSpecific *spec_info;
+ QCryptoBlockInfo *info;
+
+ info = qcrypto_block_get_info(crypto->block, NULL);
+ if (!info) {
+ return NULL;
+ }
+ if (info->format != Q_CRYPTO_BLOCK_FORMAT_LUKS) {
+ qapi_free_QCryptoBlockInfo(info);
+ return NULL;
+ }
+
+ spec_info = g_new(ImageInfoSpecific, 1);
+ spec_info->type = IMAGE_INFO_SPECIFIC_KIND_LUKS;
+ spec_info->u.luks.data = g_new(QCryptoBlockInfoLUKS, 1);
+ *spec_info->u.luks.data = info->u.luks;
+
+ /* Blank out pointers we've just stolen to avoid double free */
+ memset(&info->u.luks, 0, sizeof(info->u.luks));
+
+ qapi_free_QCryptoBlockInfo(info);
+
+ return spec_info;
+}
+
BlockDriver bdrv_crypto_luks = {
.format_name = "luks",
.instance_size = sizeof(BlockCrypto),
.bdrv_probe = block_crypto_probe_luks,
.bdrv_open = block_crypto_open_luks,
.bdrv_close = block_crypto_close,
+ .bdrv_child_perm = bdrv_format_default_perms,
.bdrv_create = block_crypto_create_luks,
.bdrv_truncate = block_crypto_truncate,
.create_opts = &block_crypto_create_opts_luks,
.bdrv_co_readv = block_crypto_co_readv,
.bdrv_co_writev = block_crypto_co_writev,
.bdrv_getlength = block_crypto_getlength,
+ .bdrv_get_info = block_crypto_get_info_luks,
+ .bdrv_get_specific_info = block_crypto_get_specific_info_luks,
};
static void block_crypto_init(void)