This adds the <features> element to volume XML and allows
virStorageBackendCreateQemuImg to create qcow3 volumes.
For qcow3 images, it runs qemu-img with -f qcow2 -o compat=1.1,...
For qcow2 images, it specifies -o compat=0.10 if it's supported,
just in case the default of qemu-img changed to qcow3 in the future.
---
docs/formatstorage.html.in | 14 ++++-
docs/schemas/Makefile.am | 1 +
docs/schemas/storagefeatures.rng | 17 ++++++
docs/schemas/storagevol.rng | 5 +-
libvirt.spec.in | 1 +
mingw-libvirt.spec.in | 2 +
src/conf/storage_conf.c | 69 ++++++++++++++++++++++++
src/conf/storage_conf.h | 4 ++
src/libvirt_private.syms | 3 ++
src/storage/storage_backend.c | 114 ++++++++++++++++++++++++++++++++++++---
src/storage/storage_backend_fs.c | 5 ++
src/util/virstoragefile.c | 27 ++++++++++
src/util/virstoragefile.h | 1 +
13 files changed, 254 insertions(+), 9 deletions(-)
create mode 100644 docs/schemas/storagefeatures.rng
diff --git a/docs/formatstorage.html.in b/docs/formatstorage.html.in
index 9f93db8..37bf8ed 100644
--- a/docs/formatstorage.html.in
+++ b/docs/formatstorage.html.in
@@ -298,13 +298,16 @@
...
<target>
<path>/var/lib/virt/images/sparse.img</path>
- <format type='qcow2'/>
+ <format type='qcow3'/>
<permissions>
<owner>107</owner>
<group>107</group>
<mode>0744</mode>
<label>virt_image_t</label>
</permissions>
+ <features>
+ <lazy_refcounts/>
+ </features>
</target></pre>
<dl>
@@ -333,6 +336,15 @@
contains the MAC (eg SELinux) label string.
<span class="since">Since 0.4.1</span>
</dd>
+ <dt><code>features</code></dt>
+ <dd>Provides a format-specific list of features enabled in the volume.
+ So far, this is only supported for <code>qcow3</code>. Valid values
+ are:
+ <ul>
+ <li><code><lazy_refcounts></code> for delayed
refcount updates
+ (This makes snapshot creation faster).<span
class="since">Since
+ 1.0.3</span></li>
+ </dd>
</dl>
<h3><a name="StorageVolBacking">Backing store
elements</a></h3>
diff --git a/docs/schemas/Makefile.am b/docs/schemas/Makefile.am
index 4413d9e..6e54048 100644
--- a/docs/schemas/Makefile.am
+++ b/docs/schemas/Makefile.am
@@ -15,6 +15,7 @@ schema_DATA = \
nwfilter.rng \
secret.rng \
storageencryption.rng \
+ storagefeatures.rng \
storagepool.rng \
storagevol.rng
diff --git a/docs/schemas/storagefeatures.rng b/docs/schemas/storagefeatures.rng
new file mode 100644
index 0000000..d8ee857
--- /dev/null
+++ b/docs/schemas/storagefeatures.rng
@@ -0,0 +1,17 @@
+<?xml version="1.0"?>
+<!-- A Relax NG schema for the libvirt volume features XML format -->
+<grammar
xmlns="http://relaxng.org/ns/structure/1.0"
+
datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+
+ <define name='diskFormatFeatures'>
+ <element name='features'>
+ <interleave>
+ <optional>
+ <element name="lazy_refcounts">
+ <empty/>
+ </element>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+</grammar>
diff --git a/docs/schemas/storagevol.rng b/docs/schemas/storagevol.rng
index 653491d..b426eca 100644
--- a/docs/schemas/storagevol.rng
+++ b/docs/schemas/storagevol.rng
@@ -8,7 +8,7 @@
</start>
<include href='storageencryption.rng'/>
-
+ <include href='storagefeatures.rng'/>
<define name='vol'>
<element name='volume'>
@@ -111,6 +111,9 @@
<optional>
<ref name='encryption'/>
</optional>
+ <optional>
+ <ref name='diskFormatFeatures'/>
+ </optional>
</element>
</define>
diff --git a/libvirt.spec.in b/libvirt.spec.in
index c2da6ec..93591d1 100644
--- a/libvirt.spec.in
+++ b/libvirt.spec.in
@@ -1937,6 +1937,7 @@ fi
%{_datadir}/libvirt/schemas/nwfilter.rng
%{_datadir}/libvirt/schemas/secret.rng
%{_datadir}/libvirt/schemas/storageencryption.rng
+%{_datadir}/libvirt/schemas/storagefeatures.rng
%{_datadir}/libvirt/schemas/storagepool.rng
%{_datadir}/libvirt/schemas/storagevol.rng
diff --git a/mingw-libvirt.spec.in b/mingw-libvirt.spec.in
index b27b0ee..9c7ba1e 100644
--- a/mingw-libvirt.spec.in
+++ b/mingw-libvirt.spec.in
@@ -213,6 +213,7 @@ rm -rf $RPM_BUILD_ROOT%{mingw64_libexecdir}/libvirt-guests.sh
%{mingw32_datadir}/libvirt/schemas/nwfilter.rng
%{mingw32_datadir}/libvirt/schemas/secret.rng
%{mingw32_datadir}/libvirt/schemas/storageencryption.rng
+%{mingw32_datadir}/libvirt/schemas/storagefeatures.rng
%{mingw32_datadir}/libvirt/schemas/storagepool.rng
%{mingw32_datadir}/libvirt/schemas/storagevol.rng
%dir %{mingw32_datadir}/libvirt/api/
@@ -272,6 +273,7 @@ rm -rf $RPM_BUILD_ROOT%{mingw64_libexecdir}/libvirt-guests.sh
%{mingw64_datadir}/libvirt/schemas/nwfilter.rng
%{mingw64_datadir}/libvirt/schemas/secret.rng
%{mingw64_datadir}/libvirt/schemas/storageencryption.rng
+%{mingw64_datadir}/libvirt/schemas/storagefeatures.rng
%{mingw64_datadir}/libvirt/schemas/storagepool.rng
%{mingw64_datadir}/libvirt/schemas/storagevol.rng
%dir %{mingw64_datadir}/libvirt/api/
diff --git a/src/conf/storage_conf.c b/src/conf/storage_conf.c
index 7a39998..6fa536a 100644
--- a/src/conf/storage_conf.c
+++ b/src/conf/storage_conf.c
@@ -293,6 +293,7 @@ virStorageVolDefFree(virStorageVolDefPtr def) {
}
VIR_FREE(def->source.extents);
+ virBitmapFree(def->target.features);
VIR_FREE(def->target.path);
VIR_FREE(def->target.perms.label);
VIR_FREE(def->target.timestamps);
@@ -1105,6 +1106,36 @@ virStorageSize(const char *unit,
return 0;
}
+static int
+virStorageVolDefParseTargetFeatures(virStorageVolTargetPtr target,
+ xmlNodePtr start_node)
+{
+ int value;
+ xmlNodePtr cur;
+
+ target->features = virBitmapNew(VIR_STORAGE_FILE_FEAT_QCOW3_LAST);
+
+ if (!target->features)
+ goto no_memory;
+
+ for (cur = start_node->children; cur; cur = cur->next) {
+ if (cur->type != XML_ELEMENT_NODE)
+ continue;
+
+ value = virStorageFileFeaturesQcow3TypeFromString((const char*)
+ cur->name);
+ if (value >= 0 && virBitmapSetBit(target->features, value) < 0)
+ goto error;
+ }
+
+ return 0;
+no_memory:
+ virReportOOMError();
+error:
+ virBitmapFree(target->features);
+ return -1;
+}
+
static virStorageVolDefPtr
virStorageVolDefParseXML(virStoragePoolDefPtr pool,
xmlXPathContextPtr ctxt) {
@@ -1211,6 +1242,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))) {
+ if (virStorageVolDefParseTargetFeatures(&(ret->target), node) < 0)
+ goto cleanup;
+ }
+
return ret;
cleanup:
@@ -1290,10 +1328,35 @@ virStorageVolTimestampFormat(virBufferPtr buf, const char *name,
}
static int
+virStorageVolDefFeaturesFormat(virBufferPtr buf,
+ int format,
+ virBitmapPtr features)
+{
+ int i;
+ bool tmp;
+ if (format != VIR_STORAGE_FILE_QCOW3)
+ return 0;
+
+ virBufferAddLit(buf, "<features>\n");
+ if (features) {
+ for (i = 0; i < VIR_STORAGE_FILE_FEAT_QCOW3_LAST; i++) {
+ if (virBitmapGetBit(features, i, &tmp) == 0 && tmp) {
+ virBufferEscapeString(buf, " <%s/>\n",
+ virStorageFileFeaturesQcow3TypeToString(i));
+ }
+ }
+ }
+ virBufferAddLit(buf, "</features>\n");
+
+ return 0;
+}
+
+static int
virStorageVolTargetDefFormat(virStorageVolOptionsPtr options,
virBufferPtr buf,
virStorageVolTargetPtr def,
const char *type) {
+
virBufferAsprintf(buf, " <%s>\n", type);
if (def->path)
@@ -1341,6 +1404,12 @@ virStorageVolTargetDefFormat(virStorageVolOptionsPtr options,
virBufferAdjustIndent(buf, -4);
}
+ if (def->format == VIR_STORAGE_FILE_QCOW3) {
+ virBufferAdjustIndent(buf, 4);
+ virStorageVolDefFeaturesFormat(buf, def->format, def->features);
+ virBufferAdjustIndent(buf, -4);
+ }
+
virBufferAsprintf(buf, " </%s>\n", type);
return 0;
diff --git a/src/conf/storage_conf.h b/src/conf/storage_conf.h
index ad16eca..e7f0744 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,9 @@ struct _virStorageVolTarget {
int type; /* only used by disk backend for partition type */
/* Currently used only in virStorageVolDef.target, not in .backingstore. */
virStorageEncryptionPtr encryption;
+ /* Format-specific features. Currently only used for qcow3.
+ * Only in virStorageVolDef.target, not in .backingstore. */
+ virBitmapPtr features;
};
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 4504ccd..60d224c 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -1168,6 +1168,8 @@ virStorageGenerateQcowPassphrase;
# storage_file.h
virStorageFileChainLookup;
+virStorageFileFeaturesQcow3TypeFromString;
+virStorageFileFeaturesQcow3TypeToString;
virStorageFileFormatToStringQemu;
virStorageFileFormatTypeFromString;
virStorageFileFormatTypeToString;
@@ -1181,6 +1183,7 @@ virStorageFileIsSharedFS;
virStorageFileIsSharedFSType;
virStorageFileProbeFormat;
virStorageFileProbeFormatFromFD;
+virStorageFileQemuImgQcow3Options;
virStorageFileResize;
# sysinfo.h
diff --git a/src/storage/storage_backend.c b/src/storage/storage_backend.c
index f9e604a..e4c586d 100644
--- a/src/storage/storage_backend.c
+++ b/src/storage/storage_backend.c
@@ -654,6 +654,75 @@ cleanup:
return ret;
}
+static int virStorageBackendQEMUImgOpts(const char *qemuimg, virBitmapPtr opts)
+{
+ char *buf = NULL;
+ char *p, *q = NULL, *r;
+ int ret = -1;
+ int val;
+ virCommandPtr cmd = virCommandNewArgList(qemuimg, "create", "-o",
"?",
+ "-f", "qcow2",
"/dev/null", NULL);
+
+ virCommandAddEnvString(cmd, "LC_ALL=C");
+ virCommandSetOutputBuffer(cmd, &buf);
+
+ if (virCommandRun(cmd, NULL) < 0)
+ goto cleanup;
+
+
+ q = strchr(buf, '\n');
+ while (q) {
+ p = q + 1;
+ q = strchr(p, '\n');
+ if (STRPREFIX(p, "compat ")) {
+ ret = 1;
+ if (opts)
+ continue;
+ else
+ goto cleanup;
+ }
+ r = strchr(p, ' ');
+ if (!r)
+ goto cleanup;
+ *r = '\0';
+ val = virStorageFileFeaturesQcow3TypeFromString(p);
+ if (val >= 0)
+ ignore_value(virBitmapSetBit(opts, val));
+ }
+
+ if (ret < 0)
+ ret = 0;
+
+cleanup:
+ virCommandFree(cmd);
+ VIR_FREE(buf);
+ return ret;
+}
+
+static int virStorageBackendQemuCheckFeatures(virBitmapPtr opts, virBitmapPtr feats)
+{
+ int i;
+ bool val;
+ int ret = -1;
+
+ for (i = 0; i < VIR_STORAGE_FILE_FEAT_QCOW3_LAST; i++) {
+ if (virBitmapGetBit(feats, i, &val) < 0)
+ goto cleanup;
+ if (val) {
+ if (virBitmapGetBit(opts, i, &val) < 0)
+ goto cleanup;
+ if (!val) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("feature %s not supported by qemu-img"),
+ virStorageFileFeaturesQcow3TypeToString(i));
+ goto cleanup;
+ }
+ }
+ }
+ ret = 0;
+cleanup:
+ return ret;
+}
static int
virStorageBackendCreateQemuImg(virConnectPtr conn,
@@ -665,27 +734,29 @@ virStorageBackendCreateQemuImg(virConnectPtr conn,
int ret = -1;
char *create_tool;
int imgformat = -1;
+ int compat = 0;
virCommandPtr cmd = NULL;
bool do_encryption = (vol->target.encryption != NULL);
unsigned long long int size_arg;
bool preallocate = false;
- char *options = NULL;
+ const char *options = NULL;
virBuffer buf = VIR_BUFFER_INITIALIZER;
+ virBitmapPtr opts = NULL;
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 +773,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 +836,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);
@@ -807,6 +881,18 @@ virStorageBackendCreateQemuImg(virConnectPtr conn,
if (imgformat < 0)
goto cleanup;
+ if (vol->target.format == VIR_STORAGE_FILE_QCOW3 &&
+ (opts = virBitmapNew(VIR_STORAGE_FILE_FEAT_QCOW3_LAST)) == NULL) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ if (imgformat == QEMU_IMG_BACKING_FORMAT_OPTIONS) {
+ compat = virStorageBackendQEMUImgOpts(create_tool, opts);
+ if (compat < 0)
+ goto cleanup;
+ }
+
cmd = virCommandNew(create_tool);
if (inputvol) {
@@ -831,6 +917,18 @@ virStorageBackendCreateQemuImg(virConnectPtr conn,
else if (preallocate)
virBufferAddLit(&buf, ",preallocation=metadata");
+ if (vol->target.format == VIR_STORAGE_FILE_QCOW3) {
+ if (virStorageBackendQemuCheckFeatures(opts, vol->target.features) <
0)
+ goto cleanup;
+ options = virStorageFileQemuImgQcow3Options(vol->target.features);
+ if (!options)
+ goto cleanup;
+ virBufferAdd(&buf, options, strlen(options));
+ VIR_FREE(options);
+ } else if (compat && vol->target.format == VIR_STORAGE_FILE_QCOW2) {
+ virBufferAddLit(&buf, ",compat=0.10");
+ }
+
if (virBufferError(&buf) > 0) {
virReportOOMError();
goto cleanup;
@@ -854,6 +952,8 @@ virStorageBackendCreateQemuImg(virConnectPtr conn,
ret = virStorageBackendCreateExecCommand(pool, vol, cmd);
cleanup:
+ virBitmapFree(opts);
+ virBufferFreeAndReset(&buf);
VIR_FREE(create_tool);
virCommandFree(cmd);
diff --git a/src/storage/storage_backend_fs.c b/src/storage/storage_backend_fs.c
index f356173..538f540 100644
--- a/src/storage/storage_backend_fs.c
+++ b/src/storage/storage_backend_fs.c
@@ -150,6 +150,11 @@ virStorageBackendProbeTarget(virStorageVolTargetPtr target,
*/
}
+ if (target->format == VIR_STORAGE_FILE_QCOW3) {
+ target->features = meta->features;
+ meta->features = NULL;
+ }
+
virStorageFileFreeMetadata(meta);
return ret;
diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c
index 1d6a13e..7e49ba3 100644
--- a/src/util/virstoragefile.c
+++ b/src/util/virstoragefile.c
@@ -1482,3 +1482,30 @@ const char *virStorageFileFormatToStringQemu(enum
virStorageFileFormat format)
else
return virStorageFileFormatTypeToString(format);
}
+
+const char *virStorageFileQemuImgQcow3Options(virBitmapPtr features)
+{
+ virBuffer buf = VIR_BUFFER_INITIALIZER;
+ bool value;
+
+ virBufferAddLit(&buf, ",compat=1.1");
+
+ if (features) {
+ if (virBitmapGetBit(features,
+ VIR_STORAGE_FILE_FEAT_QCOW3_LAZY_REFCOUNTS,
+ &value) < 0)
+ goto no_memory;
+ if (value)
+ virBufferAddLit(&buf, ",lazy_refcounts=on");
+ }
+
+ if (virBufferError(&buf) > 0)
+ goto no_memory;
+
+ return virBufferContentAndReset(&buf);
+
+no_memory:
+ virBufferFreeAndReset(&buf);
+ virReportOOMError();
+ return NULL;
+}
diff --git a/src/util/virstoragefile.h b/src/util/virstoragefile.h
index f765e71..1a8cb53 100644
--- a/src/util/virstoragefile.h
+++ b/src/util/virstoragefile.h
@@ -118,5 +118,6 @@ int virStorageFileGetLVMKey(const char *path,
int virStorageFileGetSCSIKey(const char *path,
char **key);
const char *virStorageFileFormatToStringQemu(enum virStorageFileFormat format);
+const char *virStorageFileQemuImgQcow3Options(virBitmapPtr features);
#endif /* __VIR_STORAGE_FILE_H__ */
--
1.7.12.4