[libvirt] [PATCHv1 0/5] qcow3 support

This adds support for qcow3 to storage and qemu drivers (except for snapshots, I still need to do that). Qcow3 adds feature bits for compatible, incompatible and autoclear features. I'm not sure if it makes sense to differentiate between them in the XML. If yes, perhaps unknown incompatible features might result in an error, while we could just warn about unknown compatible ones. If not, one bitmap should be enough to track the ones that interest us. There are two feature bits so far: lazy_refcounts (delayed refcount updates) and a dirty bit (refcounts haven't been updated and older QEMU can't read this). If we knew what features are supported by QEMU, we could refuse to use them, however I don't know about other way to find out than running: qemu-img create -f qcow2 -o ? /dev/null If we don't know, I don't think it's any good to find out the dirty bit value. Ján Tomko (5): storage: refactor qemu-img command line generation storage: use virBuffer for generating qemu options string util: add qcow3 format probing conf: add format features to target XML qemu: add support for creating and using qcow3 images src/conf/storage_conf.c | 90 +++++++++++++++++++++++++++++ src/conf/storage_conf.h | 3 + src/libvirt_private.syms | 3 + src/qemu/qemu_command.c | 2 +- src/qemu/qemu_hotplug.c | 4 +- src/storage/storage_backend.c | 106 ++++++++++++++++++----------------- src/storage/storage_backend_fs.c | 7 ++ src/util/virstoragefile.c | 116 ++++++++++++++++++++++++++++++++++++-- src/util/virstoragefile.h | 27 +++++++++ 9 files changed, 298 insertions(+), 60 deletions(-) -- 1.7.8.6

This makes adding new options easier. The only change in command line is movement of -o or -F options to the end for creating images with backing stores. --- src/storage/storage_backend.c | 77 ++++++++++++++++------------------------ 1 files changed, 31 insertions(+), 46 deletions(-) diff --git a/src/storage/storage_backend.c b/src/storage/storage_backend.c index df79b28..9f598fc 100644 --- a/src/storage/storage_backend.c +++ b/src/storage/storage_backend.c @@ -810,61 +810,46 @@ virStorageBackendCreateQemuImg(virConnectPtr conn, if (inputvol) { virCommandAddArgList(cmd, "convert", "-f", inputType, "-O", type, inputPath, vol->target.path, NULL); - - if (imgformat == QEMU_IMG_BACKING_FORMAT_OPTIONS && - (do_encryption || preallocate)) { - virCommandAddArg(cmd, "-o"); - virCommandAddArgFormat(cmd, "%s%s%s", do_encryption ? "encryption=on" : "", - (do_encryption && preallocate) ? "," : "", - preallocate ? "preallocation=metadata" : ""); - } else if (do_encryption) { - virCommandAddArg(cmd, "-e"); - } } else if (vol->backingStore.path) { + virCommandAddArgList(cmd, "create", "-f", type, "-b", + vol->backingStore.path, vol->target.path, NULL); + virCommandAddArgFormat(cmd, "%lluK", size_arg); + } else { virCommandAddArgList(cmd, "create", "-f", type, - "-b", vol->backingStore.path, NULL); - - switch (imgformat) { - case QEMU_IMG_BACKING_FORMAT_FLAG: - virCommandAddArgList(cmd, "-F", backingType, vol->target.path, - NULL); - virCommandAddArgFormat(cmd, "%lluK", size_arg); - - if (do_encryption) - virCommandAddArg(cmd, "-e"); - break; + vol->target.path, NULL); + virCommandAddArgFormat(cmd, "%lluK", size_arg); + } - case QEMU_IMG_BACKING_FORMAT_OPTIONS: + if (imgformat == QEMU_IMG_BACKING_FORMAT_OPTIONS) { + if (inputvol) { + if (do_encryption || preallocate) { + virCommandAddArg(cmd, "-o"); + virCommandAddArgFormat(cmd, "%s%s%s", do_encryption ? "encryption=on" : "", + (do_encryption && preallocate) ? "," : "", + preallocate ? "preallocation=metadata" : ""); + } + } else if (vol->backingStore.path) { virCommandAddArg(cmd, "-o"); virCommandAddArgFormat(cmd, "backing_fmt=%s%s", backingType, do_encryption ? ",encryption=on" : ""); - virCommandAddArg(cmd, vol->target.path); - virCommandAddArgFormat(cmd, "%lluK", size_arg); - break; - - default: - VIR_INFO("Unable to set backing store format for %s with %s", - vol->target.path, create_tool); - - virCommandAddArg(cmd, vol->target.path); - virCommandAddArgFormat(cmd, "%lluK", size_arg); - if (do_encryption) - virCommandAddArg(cmd, "-e"); + } else { + if (do_encryption || preallocate) { + virCommandAddArg(cmd, "-o"); + virCommandAddArgFormat(cmd, "%s%s%s", do_encryption ? "encryption=on" : "", + (do_encryption && preallocate) ? "," : "", + preallocate ? "preallocation=metadata" : ""); + } } } else { - virCommandAddArgList(cmd, "create", "-f", type, - vol->target.path, NULL); - virCommandAddArgFormat(cmd, "%lluK", size_arg); - - if (imgformat == QEMU_IMG_BACKING_FORMAT_OPTIONS && - (do_encryption || preallocate)) { - virCommandAddArg(cmd, "-o"); - virCommandAddArgFormat(cmd, "%s%s%s", do_encryption ? "encryption=on" : "", - (do_encryption && preallocate) ? "," : "", - preallocate ? "preallocation=metadata" : ""); - } else if (do_encryption) { - virCommandAddArg(cmd, "-e"); + if (!inputvol && vol->backingStore.path) { + if (imgformat == QEMU_IMG_BACKING_FORMAT_FLAG) + virCommandAddArgList(cmd, "-F", backingType, NULL); + else + VIR_INFO("Unable to set backing store format for %s with %s", + vol->target.path, create_tool); } + if (do_encryption) + virCommandAddArg(cmd, "-e"); } ret = virStorageBackendCreateExecCommand(pool, vol, cmd); -- 1.7.8.6

--- src/storage/storage_backend.c | 36 ++++++++++++++++++------------------ 1 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/storage/storage_backend.c b/src/storage/storage_backend.c index 9f598fc..a7e9493 100644 --- a/src/storage/storage_backend.c +++ b/src/storage/storage_backend.c @@ -669,6 +669,8 @@ virStorageBackendCreateQemuImg(virConnectPtr conn, bool do_encryption = (vol->target.encryption != NULL); unsigned long long int size_arg; bool preallocate = false; + char *options; + virBuffer buf = VIR_BUFFER_INITIALIZER; virCheckFlags(VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA, -1); @@ -821,25 +823,23 @@ virStorageBackendCreateQemuImg(virConnectPtr conn, } if (imgformat == QEMU_IMG_BACKING_FORMAT_OPTIONS) { - if (inputvol) { - if (do_encryption || preallocate) { - virCommandAddArg(cmd, "-o"); - virCommandAddArgFormat(cmd, "%s%s%s", do_encryption ? "encryption=on" : "", - (do_encryption && preallocate) ? "," : "", - preallocate ? "preallocation=metadata" : ""); - } - } else if (vol->backingStore.path) { - virCommandAddArg(cmd, "-o"); - virCommandAddArgFormat(cmd, "backing_fmt=%s%s", backingType, - do_encryption ? ",encryption=on" : ""); - } else { - if (do_encryption || preallocate) { - virCommandAddArg(cmd, "-o"); - virCommandAddArgFormat(cmd, "%s%s%s", do_encryption ? "encryption=on" : "", - (do_encryption && preallocate) ? "," : "", - preallocate ? "preallocation=metadata" : ""); - } + if (do_encryption) + virBufferAddLit(&buf, ",encryption=on"); + + if (!inputvol && vol->backingStore.path) + virBufferAsprintf(&buf, ",backing_fmt=%s", backingType); + else if (preallocate) + virBufferAddLit(&buf, ",preallocation=metadata"); + + if (virBufferError(&buf) > 0) { + virReportOOMError(); + goto cleanup; } + + if ((options = virBufferContentAndReset(&buf))) + virCommandAddArgList(cmd, "-o", &(options[1]), NULL); + + VIR_FREE(options); } else { if (!inputvol && vol->backingStore.path) { if (imgformat == QEMU_IMG_BACKING_FORMAT_FLAG) -- 1.7.8.6

QCOWv3 introduced feature bits in the header, which get stored in virStorageFileFeatures. --- src/util/virstoragefile.c | 108 ++++++++++++++++++++++++++++++++++++++++++-- src/util/virstoragefile.h | 26 +++++++++++ 2 files changed, 129 insertions(+), 5 deletions(-) diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c index c7941c3..e9ecff1 100644 --- a/src/util/virstoragefile.c +++ b/src/util/virstoragefile.c @@ -36,6 +36,7 @@ #endif #include "dirname.h" #include "viralloc.h" +#include "virbitmap.h" #include "virerror.h" #include "virlog.h" #include "virfile.h" @@ -50,9 +51,17 @@ VIR_ENUM_IMPL(virStorageFileFormat, "none", "raw", "dir", "bochs", "cloop", "cow", "dmg", "iso", - "qcow", "qcow2", "qed", "vmdk", "vpc", + "qcow", "qcow2", "qcow3", "qed", "vmdk", "vpc", "fat", "vhd") +VIR_ENUM_IMPL(virStorageFileQcow3Incomp, + VIR_STORAGE_FILE_QCOW3_INCOMP_LAST, + "dirty_refcounts") + +VIR_ENUM_IMPL(virStorageFileQcow3Comp, + VIR_STORAGE_FILE_QCOW3_COMP_LAST, + "lazy_refcounts") + enum lv_endian { LV_LITTLE_ENDIAN = 1, /* 1234 */ LV_BIG_ENDIAN /* 4321 */ @@ -89,12 +98,15 @@ struct FileTypeInfo { const unsigned char *buf, size_t buf_size); }; +static unsigned long qcow3GetHeaderSize(const unsigned char *, size_t); static int cowGetBackingStore(char **, int *, const unsigned char *, size_t); static int qcow1GetBackingStore(char **, int *, const unsigned char *, size_t); static int qcow2GetBackingStore(char **, int *, const unsigned char *, size_t); +static int qcow3GetBackingStore(char **, int *, + const unsigned char *, size_t); static int vmdk4GetBackingStore(char **, int *, const unsigned char *, size_t); static int @@ -110,6 +122,8 @@ qedGetBackingStore(char **, int *, const unsigned char *, size_t); #define QCOW1_HDR_TOTAL_SIZE (QCOW1_HDR_CRYPT+4+8) #define QCOW2_HDR_TOTAL_SIZE (QCOW2_HDR_CRYPT+4+4+8+8+4+4+8) +#define QCOW3_HDR_FEATURES (QCOW2_HDR_TOTAL_SIZE) +#define QCOW3_HDR_SIZE (QCOW2_HDR_CRYPT+4+4+8+8+4+4+8+8+8+8+4) #define QCOW2_HDR_EXTENSION_END 0 #define QCOW2_HDR_EXTENSION_BACKING_FORMAT 0xE2792ACA @@ -172,6 +186,11 @@ static struct FileTypeInfo const fileTypeInfo[] = { LV_BIG_ENDIAN, 4, 2, QCOWX_HDR_IMAGE_SIZE, 8, 1, QCOW2_HDR_CRYPT, qcow2GetBackingStore, }, + [VIR_STORAGE_FILE_QCOW3] = { + "QFI", NULL, + LV_BIG_ENDIAN, 4, 3, + QCOWX_HDR_IMAGE_SIZE, 8, 1, QCOW2_HDR_CRYPT, qcow3GetBackingStore, + }, [VIR_STORAGE_FILE_QED] = { /* http://wiki.qemu.org/Features/QED */ "QED", NULL, @@ -284,15 +303,53 @@ done: } +static unsigned long +qcow3GetHeaderSize(const unsigned char *buf, + size_t buf_size) +{ + unsigned long ret; + if (buf_size < QCOW3_HDR_SIZE+4) + return 0; + ret = (((unsigned long)buf[QCOW3_HDR_SIZE] << 24) + | ((unsigned long)buf[QCOW3_HDR_SIZE+1] << 16) + | ((unsigned long)buf[QCOW3_HDR_SIZE+2] << 8) + | ((unsigned long)buf[QCOW3_HDR_SIZE+3])); + return ret; +} + +static int +qcow3GetFeatures(virStorageFileFeaturesPtr features, + const unsigned char *buf, + size_t buf_size) +{ + if (buf_size < QCOW3_HDR_SIZE) + return -1; + /* FIXME */ + /* incompatible features */ + if (buf[QCOW3_HDR_FEATURES+7] & (1 << 0)) { + if (virBitmapSetBit(features->incompatible, + VIR_STORAGE_FILE_QCOW3_INCOMP_DIRTY) < 0) + return -1; + } + /* compatible features */ + if (buf[QCOW3_HDR_FEATURES+8+7] & (1 << 0)) { + if (virBitmapSetBit(features->compatible, + VIR_STORAGE_FILE_QCOW3_COMP_LAZY_REFCOUNT) < 0) + return -1; + } + return 0; +} + static int qcowXGetBackingStore(char **res, int *format, const unsigned char *buf, size_t buf_size, - bool isQCow2) + unsigned version) { unsigned long long offset; unsigned int size; + unsigned long header_size; *res = NULL; if (format) @@ -354,10 +411,21 @@ qcowXGetBackingStore(char **res, * between the end of the header (QCOW2_HDR_TOTAL_SIZE) * and the start of the backingStoreName (offset) */ - if (isQCow2 && format && + if (version == 2 && format && qcow2GetBackingStoreFormat(format, buf, buf_size, QCOW2_HDR_TOTAL_SIZE, offset) < 0) return BACKING_STORE_INVALID; + /* + * QCow3 files can have a variable header length, it's stored at + * QCOW3_HDR_SIZE. + */ + if (version == 3 && format) { + header_size = qcow3GetHeaderSize(buf, buf_size); + if (!header_size || + qcow2GetBackingStoreFormat(format, buf, buf_size, header_size, + offset) < 0) + return BACKING_STORE_INVALID; + } return BACKING_STORE_OK; } @@ -374,7 +442,7 @@ qcow1GetBackingStore(char **res, /* QCow1 doesn't have the extensions capability * used to store backing format */ *format = VIR_STORAGE_FILE_AUTO; - ret = qcowXGetBackingStore(res, NULL, buf, buf_size, false); + ret = qcowXGetBackingStore(res, NULL, buf, buf_size, 1); if (ret == 0 && *buf == '\0') *format = VIR_STORAGE_FILE_NONE; return ret; @@ -386,7 +454,16 @@ qcow2GetBackingStore(char **res, const unsigned char *buf, size_t buf_size) { - return qcowXGetBackingStore(res, format, buf, buf_size, true); + return qcowXGetBackingStore(res, format, buf, buf_size, 2); +} + +static int +qcow3GetBackingStore(char **res, + int *format, + const unsigned char *buf, + size_t buf_size) +{ + return qcowXGetBackingStore(res, format, buf, buf_size, 3); } @@ -754,7 +831,26 @@ virStorageFileGetMetadataFromBuf(int format, } } + if (format == VIR_STORAGE_FILE_QCOW3) { + meta->features.compatible = + virBitmapNew(VIR_STORAGE_FILE_QCOW3_COMP_LAST); + meta->features.incompatible = + virBitmapNew(VIR_STORAGE_FILE_QCOW3_INCOMP_LAST); + if (!meta->features.compatible || !meta->features.incompatible) { + virReportOOMError(); + goto error; + } + if (qcow3GetFeatures(&(meta->features), buf, buflen) < 0) + goto error; + } + return 0; + +error: + VIR_FREE(meta->backingStore); + virBitmapFree(meta->features.compatible); + virBitmapFree(meta->features.incompatible); + return -1; } @@ -1069,6 +1165,8 @@ virStorageFileFreeMetadata(virStorageFileMetadata *meta) return; virStorageFileFreeMetadata(meta->backingMeta); + virBitmapFree(meta->features.compatible); + virBitmapFree(meta->features.incompatible); VIR_FREE(meta->backingStore); VIR_FREE(meta->backingStoreRaw); VIR_FREE(meta); diff --git a/src/util/virstoragefile.h b/src/util/virstoragefile.h index 618b028..3249e8f 100644 --- a/src/util/virstoragefile.h +++ b/src/util/virstoragefile.h @@ -24,6 +24,7 @@ #ifndef __VIR_STORAGE_FILE_H__ # define __VIR_STORAGE_FILE_H__ +# include "virbitmap.h" # include "virutil.h" enum virStorageFileFormat { @@ -39,6 +40,7 @@ enum virStorageFileFormat { VIR_STORAGE_FILE_ISO, VIR_STORAGE_FILE_QCOW, VIR_STORAGE_FILE_QCOW2, + VIR_STORAGE_FILE_QCOW3, VIR_STORAGE_FILE_QED, VIR_STORAGE_FILE_VMDK, VIR_STORAGE_FILE_VPC, @@ -50,6 +52,29 @@ enum virStorageFileFormat { VIR_ENUM_DECL(virStorageFileFormat); +enum virStorageFileQcow3Incomp { + VIR_STORAGE_FILE_QCOW3_INCOMP_NONE = -1, + VIR_STORAGE_FILE_QCOW3_INCOMP_DIRTY = 0, + + VIR_STORAGE_FILE_QCOW3_INCOMP_LAST, +}; +VIR_ENUM_DECL(virStorageFileQcow3Incomp); + +enum virStorageFileQcow3Comp { + VIR_STORAGE_FILE_QCOW3_COMP_NONE = -1, + VIR_STORAGE_FILE_QCOW3_COMP_LAZY_REFCOUNT = 0, + + VIR_STORAGE_FILE_QCOW3_COMP_LAST, +}; +VIR_ENUM_DECL(virStorageFileQcow3Comp); + +typedef struct _virStorageFileFeatures virStorageFileFeatures; +typedef virStorageFileFeatures *virStorageFileFeaturesPtr; +struct _virStorageFileFeatures { + virBitmapPtr compatible; + virBitmapPtr incompatible; +}; + typedef struct _virStorageFileMetadata virStorageFileMetadata; typedef virStorageFileMetadata *virStorageFileMetadataPtr; struct _virStorageFileMetadata { @@ -60,6 +85,7 @@ struct _virStorageFileMetadata { virStorageFileMetadataPtr backingMeta; unsigned long long capacity; bool encrypted; + virStorageFileFeatures features; }; # ifndef DEV_BSIZE -- 1.7.8.6

The format is: <volume> <name>q4</name> <source> </source> <capacity unit='MiB'>128</capacity> <target> <path>/var/lib/libvirt/images/q4</path> <format type='qcow3'/> <features> <compatible> <lazy_refcounts/> </compatible> </features> </target> </volume> --- src/conf/storage_conf.c | 90 ++++++++++++++++++++++++++++++++++++++ src/conf/storage_conf.h | 3 + src/libvirt_private.syms | 2 + src/storage/storage_backend_fs.c | 7 +++ 4 files changed, 102 insertions(+), 0 deletions(-) diff --git a/src/conf/storage_conf.c b/src/conf/storage_conf.c index 7a39998..f13daa1 100644 --- a/src/conf/storage_conf.c +++ b/src/conf/storage_conf.c @@ -293,10 +293,15 @@ virStorageVolDefFree(virStorageVolDefPtr def) { } VIR_FREE(def->source.extents); + virBitmapFree(def->target.features.compatible); + virBitmapFree(def->target.features.incompatible); VIR_FREE(def->target.path); VIR_FREE(def->target.perms.label); VIR_FREE(def->target.timestamps); virStorageEncryptionFree(def->target.encryption); + + virBitmapFree(def->backingStore.features.compatible); + virBitmapFree(def->backingStore.features.incompatible); VIR_FREE(def->backingStore.path); VIR_FREE(def->backingStore.perms.label); VIR_FREE(def->backingStore.timestamps); @@ -1105,6 +1110,60 @@ virStorageSize(const char *unit, return 0; } +static int +virStorageVolDefParseTargetFeatures(virStorageVolTargetPtr target, + xmlNodePtr start_node) +{ + int value, ret = -1; + xmlNodePtr cur, node; + + target->features.compatible = virBitmapNew(VIR_STORAGE_FILE_QCOW3_COMP_LAST); + target->features.incompatible = virBitmapNew(VIR_STORAGE_FILE_QCOW3_INCOMP_LAST); + + if (!target->features.compatible || !target->features.incompatible) { + virReportOOMError(); + goto error; + } + + for (cur = start_node->children; cur; cur = cur->next) { + if (cur->type != XML_ELEMENT_NODE) + continue; + + if (xmlStrEqual(cur->name, BAD_CAST "compatible")) { + for (node = cur->children->next; node; node = node->next) { + if (node->type != XML_ELEMENT_NODE) + continue; + value = virStorageFileQcow3CompTypeFromString( + (const char*) node->name); + + if (value < 0) { + virReportError(VIR_ERR_XML_ERROR, + _("unexpected feature %s"), + node->name); + goto error; + } + if (virBitmapSetBit(target->features.compatible, value) < 0) + goto error; + } + } else { + value = virStorageFileQcow3IncompTypeFromString( + (const char*) cur->name); + if (value < 0) { + virReportError(VIR_ERR_XML_ERROR, + _("unexpected feature %s"), + cur->name); + goto error; + } + if (virBitmapSetBit(target->features.incompatible, value) < 0) + goto error; + } + } + + ret = 0; +error: + return ret; +} + static virStorageVolDefPtr virStorageVolDefParseXML(virStoragePoolDefPtr pool, xmlXPathContextPtr ctxt) { @@ -1211,6 +1270,13 @@ virStorageVolDefParseXML(virStoragePoolDefPtr pool, DEFAULT_VOL_PERM_MODE) < 0) goto cleanup; + if (pool->type == VIR_STORAGE_POOL_DIR && + ret->target.format == VIR_STORAGE_FILE_QCOW3 && + (node = virXPathNode("./target/features", ctxt)) != NULL) { + if (virStorageVolDefParseTargetFeatures(&(ret->target), node) < 0) + goto cleanup; + } + return ret; cleanup: @@ -1294,6 +1360,9 @@ virStorageVolTargetDefFormat(virStorageVolOptionsPtr options, virBufferPtr buf, virStorageVolTargetPtr def, const char *type) { + bool tmp; + int i; + virBufferAsprintf(buf, " <%s>\n", type); if (def->path) @@ -1341,6 +1410,27 @@ virStorageVolTargetDefFormat(virStorageVolOptionsPtr options, virBufferAdjustIndent(buf, -4); } + if (def->features.compatible || def->features.incompatible) { + virBufferAddLit(buf, " <features>\n"); + for (i = 0; i < VIR_STORAGE_FILE_QCOW3_INCOMP_LAST; i++) { + if (virBitmapGetBit(def->features.incompatible, i, &tmp) == 0 + && tmp) { + virBufferAsprintf(buf, " <%s/>\n", + virStorageFileQcow3IncompTypeToString(i)); + } + } + virBufferAddLit(buf, " <compatible>\n"); + for (i = 0; i < VIR_STORAGE_FILE_QCOW3_COMP_LAST; i++) { + if (virBitmapGetBit(def->features.compatible, i, &tmp) == 0 + && tmp) { + virBufferAsprintf(buf, " <%s/>\n", + virStorageFileQcow3CompTypeToString(i)); + } + } + virBufferAddLit(buf, " </compatible>\n"); + virBufferAddLit(buf, " </features>\n"); + } + virBufferAsprintf(buf, " </%s>\n", type); return 0; diff --git a/src/conf/storage_conf.h b/src/conf/storage_conf.h index ad16eca..fa60252 100644 --- a/src/conf/storage_conf.h +++ b/src/conf/storage_conf.h @@ -28,6 +28,7 @@ # include "virutil.h" # include "storage_encryption_conf.h" # include "virthread.h" +# include "virstoragefile.h" # include <libxml/tree.h> @@ -93,6 +94,8 @@ struct _virStorageVolTarget { int type; /* only used by disk backend for partition type */ /* Currently used only in virStorageVolDef.target, not in .backingstore. */ virStorageEncryptionPtr encryption; + /* Currently used only by Qcow3 */ + virStorageFileFeatures features; }; diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index d079cc9..699cec6 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1177,6 +1177,8 @@ virStorageFileIsSharedFS; virStorageFileIsSharedFSType; virStorageFileProbeFormat; virStorageFileProbeFormatFromFD; +virStorageFileQcow3CompTypeToString; +virStorageFileQcow3IncompTypeToString; virStorageFileResize; # sysinfo.h diff --git a/src/storage/storage_backend_fs.c b/src/storage/storage_backend_fs.c index 2205371..33ef129 100644 --- a/src/storage/storage_backend_fs.c +++ b/src/storage/storage_backend_fs.c @@ -136,6 +136,7 @@ virStorageBackendProbeTarget(virStorageVolTargetPtr target, switch (target->format) { case VIR_STORAGE_FILE_QCOW: case VIR_STORAGE_FILE_QCOW2: + case VIR_STORAGE_FILE_QCOW3: (*encryption)->format = VIR_STORAGE_ENCRYPTION_FORMAT_QCOW; break; default: @@ -149,6 +150,12 @@ virStorageBackendProbeTarget(virStorageVolTargetPtr target, */ } + if (target->format == VIR_STORAGE_FILE_QCOW3) { + target->features = meta->features; + meta->features.compatible = NULL; + meta->features.incompatible = NULL; + } + virStorageFileFreeMetadata(meta); return ret; -- 1.7.8.6

QEMU uses the qcow2 driver for qcow3, which is why virStorageFileFormatToStringQemu is added. TODO: make snapshots work. --- src/libvirt_private.syms | 1 + src/qemu/qemu_command.c | 2 +- src/qemu/qemu_hotplug.c | 4 ++-- src/storage/storage_backend.c | 29 +++++++++++++++++++++++------ src/util/virstoragefile.c | 8 ++++++++ src/util/virstoragefile.h | 1 + 6 files changed, 36 insertions(+), 9 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 699cec6..9193342 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1165,6 +1165,7 @@ virStorageGenerateQcowPassphrase; # storage_file.h virStorageFileChainLookup; +virStorageFileFormatToStringQemu; virStorageFileFormatTypeFromString; virStorageFileFormatTypeToString; virStorageFileFreeMetadata; diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 3e3b588..4c7451b 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -2417,7 +2417,7 @@ qemuBuildDriveStr(virConnectPtr conn ATTRIBUTE_UNUSED, disk->type != VIR_DOMAIN_DISK_TYPE_DIR && qemuCapsGet(caps, QEMU_CAPS_DRIVE_FORMAT)) virBufferAsprintf(&opt, ",format=%s", - virStorageFileFormatTypeToString(disk->format)); + virStorageFileFormatToStringQemu(disk->format)); /* generate geometry command string */ if (disk->geometry.cylinders > 0 && diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 19172e1..305271b 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -109,9 +109,9 @@ int qemuDomainChangeEjectableMedia(virQEMUDriverPtr driver, const char *format = NULL; if (disk->type != VIR_DOMAIN_DISK_TYPE_DIR) { if (disk->format > 0) - format = virStorageFileFormatTypeToString(disk->format); + format = virStorageFileFormatToStringQemu(disk->format); else if (origdisk->format > 0) - format = virStorageFileFormatTypeToString(origdisk->format); + format = virStorageFileFormatToStringQemu(origdisk->format); } ret = qemuMonitorChangeMedia(priv->mon, driveAlias, diff --git a/src/storage/storage_backend.c b/src/storage/storage_backend.c index a7e9493..192c085 100644 --- a/src/storage/storage_backend.c +++ b/src/storage/storage_backend.c @@ -670,22 +670,23 @@ virStorageBackendCreateQemuImg(virConnectPtr conn, unsigned long long int size_arg; bool preallocate = false; char *options; + bool tmp; virBuffer buf = VIR_BUFFER_INITIALIZER; virCheckFlags(VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA, -1); preallocate = !!(flags & VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA); - const char *type = virStorageFileFormatTypeToString(vol->target.format); + const char *type = virStorageFileFormatToStringQemu(vol->target.format); const char *backingType = vol->backingStore.path ? - virStorageFileFormatTypeToString(vol->backingStore.format) : NULL; + virStorageFileFormatToStringQemu(vol->backingStore.format) : NULL; const char *inputBackingPath = (inputvol ? inputvol->backingStore.path : NULL); const char *inputPath = inputvol ? inputvol->target.path : NULL; /* Treat input block devices as 'raw' format */ const char *inputType = inputPath ? - virStorageFileFormatTypeToString(inputvol->type == VIR_STORAGE_VOL_BLOCK ? + virStorageFileFormatToStringQemu(inputvol->type == VIR_STORAGE_VOL_BLOCK ? VIR_STORAGE_FILE_RAW : inputvol->target.format) : NULL; @@ -702,9 +703,11 @@ virStorageBackendCreateQemuImg(virConnectPtr conn, inputvol->target.format); return -1; } - if (preallocate && vol->target.format != VIR_STORAGE_FILE_QCOW2) { + if (preallocate && vol->target.format != VIR_STORAGE_FILE_QCOW2 + && vol->target.format != VIR_STORAGE_FILE_QCOW3) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("metadata preallocation only available with qcow2")); + _("metadata preallocation only available with qcow2" + " or qcow3")); return -1; } @@ -763,7 +766,8 @@ virStorageBackendCreateQemuImg(virConnectPtr conn, virStorageEncryptionPtr enc; if (vol->target.format != VIR_STORAGE_FILE_QCOW && - vol->target.format != VIR_STORAGE_FILE_QCOW2) { + vol->target.format != VIR_STORAGE_FILE_QCOW2 && + vol->target.format != VIR_STORAGE_FILE_QCOW3) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("qcow volume encryption unsupported with " "volume format %s"), type); @@ -831,6 +835,19 @@ virStorageBackendCreateQemuImg(virConnectPtr conn, else if (preallocate) virBufferAddLit(&buf, ",preallocation=metadata"); + if (vol->target.format == VIR_STORAGE_FILE_QCOW3) { + virBufferAddLit(&buf, ",compat=1.1"); + if (virBitmapGetBit(vol->target.features.compatible, + VIR_STORAGE_FILE_QCOW3_COMP_LAZY_REFCOUNT, + &tmp) < 0) { + virReportOOMError(); + virBufferFreeAndReset(&buf); + goto cleanup; + } + if (tmp) + virBufferAddLit(&buf, ",lazy_refcounts=on"); + } + if (virBufferError(&buf) > 0) { virReportOOMError(); goto cleanup; diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c index e9ecff1..75518ab 100644 --- a/src/util/virstoragefile.c +++ b/src/util/virstoragefile.c @@ -1493,3 +1493,11 @@ error: *meta = NULL; return NULL; } + +const char *virStorageFileFormatToStringQemu(enum virStorageFileFormat format) +{ + if (format == VIR_STORAGE_FILE_QCOW3) + return virStorageFileFormatTypeToString(VIR_STORAGE_FILE_QCOW2); + else + return virStorageFileFormatTypeToString(format); +} diff --git a/src/util/virstoragefile.h b/src/util/virstoragefile.h index 3249e8f..8a010fa 100644 --- a/src/util/virstoragefile.h +++ b/src/util/virstoragefile.h @@ -131,5 +131,6 @@ int virStorageFileGetLVMKey(const char *path, char **key); int virStorageFileGetSCSIKey(const char *path, char **key); +const char *virStorageFileFormatToStringQemu(enum virStorageFileFormat format); #endif /* __VIR_STORAGE_FILE_H__ */ -- 1.7.8.6
participants (1)
-
Ján Tomko