Commit 6c3944dc authored by Kevin Wolf's avatar Kevin Wolf
Browse files

qcow2: Implement data-file-raw create option



Provide an option to force QEMU to always keep the external data file
consistent as a standalone read-only raw image.

At the moment, this means making sure that write_zeroes requests are
forwarded to the data file instead of just updating the metadata, and
checking that no backing file is used.

Signed-off-by: default avatarKevin Wolf <kwolf@redhat.com>
parent 9b890bdc
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -1783,6 +1783,16 @@ int qcow2_cluster_zeroize(BlockDriverState *bs, uint64_t offset,
    int64_t cleared;
    int ret;

    /* If we have to stay in sync with an external data file, zero out
     * s->data_file first. */
    if (data_file_is_raw(bs)) {
        assert(has_data_file(bs));
        ret = bdrv_co_pwrite_zeroes(s->data_file, offset, bytes, flags);
        if (ret < 0) {
            return ret;
        }
    }

    /* Caller must pass aligned values, except at image end */
    assert(QEMU_IS_ALIGNED(offset, s->cluster_size));
    assert(QEMU_IS_ALIGNED(end_offset, s->cluster_size) ||
+54 −2
Original line number Diff line number Diff line
@@ -1498,8 +1498,14 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
                             "external data file");
            ret = -EINVAL;
            goto fail;
        } else {
        }

        s->data_file = bs->file;

        if (data_file_is_raw(bs)) {
            error_setg(errp, "data-file-raw requires a data file");
            ret = -EINVAL;
            goto fail;
        }
    }

@@ -2606,6 +2612,12 @@ static int qcow2_change_backing_file(BlockDriverState *bs,
{
    BDRVQcow2State *s = bs->opaque;

    /* Adding a backing file means that the external data file alone won't be
     * enough to make sense of the content */
    if (backing_file && data_file_is_raw(bs)) {
        return -EINVAL;
    }

    if (backing_file && strlen(backing_file) > 1023) {
        return -EINVAL;
    }
@@ -3016,6 +3028,18 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp)
    }
    refcount_order = ctz32(qcow2_opts->refcount_bits);

    if (qcow2_opts->data_file_raw && !qcow2_opts->data_file) {
        error_setg(errp, "data-file-raw requires data-file");
        ret = -EINVAL;
        goto out;
    }
    if (qcow2_opts->data_file_raw && qcow2_opts->has_backing_file) {
        error_setg(errp, "Backing file and data-file-raw cannot be used at "
                   "the same time");
        ret = -EINVAL;
        goto out;
    }

    if (qcow2_opts->data_file) {
        if (version < 3) {
            error_setg(errp, "External data files are only supported with "
@@ -3072,6 +3096,10 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp)
        header->incompatible_features |=
            cpu_to_be64(QCOW2_INCOMPAT_DATA_FILE);
    }
    if (qcow2_opts->data_file_raw) {
        header->autoclear_features |=
            cpu_to_be64(QCOW2_AUTOCLEAR_DATA_FILE_RAW);
    }

    ret = blk_pwrite(blk, 0, header, cluster_size, 0);
    g_free(header);
@@ -3253,6 +3281,7 @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt
        { BLOCK_OPT_REFCOUNT_BITS,      "refcount-bits" },
        { BLOCK_OPT_ENCRYPT,            BLOCK_OPT_ENCRYPT_FORMAT },
        { BLOCK_OPT_COMPAT_LEVEL,       "version" },
        { BLOCK_OPT_DATA_FILE_RAW,      "data-file-raw" },
        { NULL, NULL },
    };

@@ -4621,6 +4650,8 @@ static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs,
            .bitmaps            = bitmaps,
            .has_data_file      = !!s->image_data_file,
            .data_file          = g_strdup(s->image_data_file),
            .has_data_file_raw  = has_data_file(bs),
            .data_file_raw      = data_file_is_raw(bs),
        };
    } else {
        /* if this assertion fails, this probably means a new version was
@@ -4825,6 +4856,7 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
    uint64_t new_size = 0;
    const char *backing_file = NULL, *backing_format = NULL, *data_file = NULL;
    bool lazy_refcounts = s->use_lazy_refcounts;
    bool data_file_raw = data_file_is_raw(bs);
    const char *compat = NULL;
    uint64_t cluster_size = s->cluster_size;
    bool encrypt;
@@ -4912,6 +4944,14 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
                                 "use an external data file");
                return -EINVAL;
            }
        } else if (!strcmp(desc->name, BLOCK_OPT_DATA_FILE_RAW)) {
            data_file_raw = qemu_opt_get_bool(opts, BLOCK_OPT_DATA_FILE_RAW,
                                              data_file_raw);
            if (data_file_raw && !data_file_is_raw(bs)) {
                error_setg(errp, "data-file-raw cannot be set on existing "
                                 "images");
                return -EINVAL;
            }
        } else {
            /* if this point is reached, this probably means a new option was
             * added without having it covered here */
@@ -4958,6 +4998,13 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
        }
    }

    /* data-file-raw blocks backing files, so clear it first if requested */
    if (data_file_raw) {
        s->autoclear_features |= QCOW2_AUTOCLEAR_DATA_FILE_RAW;
    } else {
        s->autoclear_features &= ~QCOW2_AUTOCLEAR_DATA_FILE_RAW;
    }

    if (data_file) {
        g_free(s->image_data_file);
        s->image_data_file = *data_file ? g_strdup(data_file) : NULL;
@@ -5121,6 +5168,11 @@ static QemuOptsList qcow2_create_opts = {
            .type = QEMU_OPT_STRING,
            .help = "File name of an external data file"
        },
        {
            .name = BLOCK_OPT_DATA_FILE_RAW,
            .type = QEMU_OPT_BOOL,
            .help = "The external data file must stay valid as a raw image"
        },
        {
            .name = BLOCK_OPT_ENCRYPT,
            .type = QEMU_OPT_BOOL,
+8 −1
Original line number Diff line number Diff line
@@ -225,7 +225,8 @@ enum {
    QCOW2_AUTOCLEAR_BITMAPS             = 1 << QCOW2_AUTOCLEAR_BITMAPS_BITNR,
    QCOW2_AUTOCLEAR_DATA_FILE_RAW       = 1 << QCOW2_AUTOCLEAR_DATA_FILE_RAW_BITNR,

    QCOW2_AUTOCLEAR_MASK                = QCOW2_AUTOCLEAR_BITMAPS,
    QCOW2_AUTOCLEAR_MASK                = QCOW2_AUTOCLEAR_BITMAPS
                                        | QCOW2_AUTOCLEAR_DATA_FILE_RAW,
};

enum qcow2_discard_type {
@@ -474,6 +475,12 @@ static inline bool has_data_file(BlockDriverState *bs)
    return (s->data_file != bs->file);
}

static inline bool data_file_is_raw(BlockDriverState *bs)
{
    BDRVQcow2State *s = bs->opaque;
    return !!(s->autoclear_features & QCOW2_AUTOCLEAR_DATA_FILE_RAW);
}

static inline int64_t start_of_cluster(BDRVQcow2State *s, int64_t offset)
{
    return offset & ~(s->cluster_size - 1);
+1 −0
Original line number Diff line number Diff line
@@ -57,6 +57,7 @@
#define BLOCK_OPT_OBJECT_SIZE       "object_size"
#define BLOCK_OPT_REFCOUNT_BITS     "refcount_bits"
#define BLOCK_OPT_DATA_FILE         "data_file"
#define BLOCK_OPT_DATA_FILE_RAW     "data_file_raw"

#define BLOCK_PROBE_BUF_SIZE        512

+9 −0
Original line number Diff line number Diff line
@@ -62,6 +62,10 @@
# @data-file: the filename of the external data file that is stored in the
#             image and used as a default for opening the image (since: 4.0)
#
# @data-file-raw: True if the external data file must stay valid as a
#                 standalone (read-only) raw image without looking at qcow2
#                 metadata (since: 4.0)
#
# @lazy-refcounts: on or off; only valid for compat >= 1.1
#
# @corrupt: true if the image has been marked corrupt; only valid for
@@ -80,6 +84,7 @@
  'data': {
      'compat': 'str',
      '*data-file': 'str',
      '*data-file-raw': 'bool',
      '*lazy-refcounts': 'bool',
      '*corrupt': 'bool',
      'refcount-bits': 'int',
@@ -4144,6 +4149,9 @@
# @data-file        Node to use as an external data file in which all guest
#                   data is stored so that only metadata remains in the qcow2
#                   file (since: 4.0)
# @data-file-raw    True if the external data file must stay valid as a
#                   standalone (read-only) raw image without looking at qcow2
#                   metadata (default: false; since: 4.0)
# @size             Size of the virtual disk in bytes
# @version          Compatibility level (default: v3)
# @backing-file     File name of the backing file if a backing file
@@ -4160,6 +4168,7 @@
{ 'struct': 'BlockdevCreateOptionsQcow2',
  'data': { 'file':             'BlockdevRef',
            '*data-file':       'BlockdevRef',
            '*data-file-raw':   'bool',
            'size':             'size',
            '*version':         'BlockdevQcow2Version',
            '*backing-file':    'str',
Loading