Implement the loadFile and saveFile virFileCacheHandlers callbacks so
that nbdkit capabilities are cached perstistently across daemon
restarts. The format and implementation is modeled on the qemu
capabilities, but simplified slightly.
Signed-off-by: Jonathon Jongsma <jjongsma@redhat.com>
---
src/qemu/qemu_nbdkit.c | 253 ++++++++++++++++++++++++++++++++++++++++-
1 file changed, 251 insertions(+), 2 deletions(-)
diff --git a/src/qemu/qemu_nbdkit.c b/src/qemu/qemu_nbdkit.c
index 3faa16674a..ac498b948c 100644
--- a/src/qemu/qemu_nbdkit.c
+++ b/src/qemu/qemu_nbdkit.c
@@ -339,11 +339,260 @@ virNbdkitCapsNewData(const char *binary,
}
+static int
+qemuNbdkitCapsValidateBinary(qemuNbdkitCaps *nbdkitCaps,
+ xmlXPathContextPtr ctxt)
+{
+ g_autofree char *str = NULL;
+
+ if (!(str = virXPathString("string(./path)", ctxt))) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("missing path in nbdkit capabilities cache"));
+ return -1;
+ }
+
+ if (STRNEQ(str, nbdkitCaps->path)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Expected caps for '%s' but saw '%s'"),
+ nbdkitCaps->path, str);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int
+qemuNbdkitCapsParseFlags(qemuNbdkitCaps *nbdkitCaps,
+ xmlXPathContextPtr ctxt)
+{
+ g_autofree xmlNodePtr *nodes = NULL;
+ size_t i;
+ int n;
+
+ if ((n = virXPathNodeSet("./flag", ctxt, &nodes)) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("failed to parse qemu capabilities flags"));
+ return -1;
+ }
+
+ VIR_DEBUG("Got flags %d", n);
+ for (i = 0; i < n; i++) {
+ g_autofree char *str = NULL;
+ int flag;
+
+ if (!(str = virXMLPropString(nodes[i], "name"))) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("missing flag name in QEMU capabilities cache"));
+ return -1;
+ }
+
+ flag = qemuNbdkitCapsTypeFromString(str);
+ if (flag < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Unknown nbdkit capabilities flag %s"), str);
+ return -1;
+ }
+
+ qemuNbdkitCapsSet(nbdkitCaps, flag);
+ }
+
+ return 0;
+}
+
+
+/*
+ * Parsing a doc that looks like
+ *
+ * <nbdkitCaps>
+ * <path>/some/path</path>
+ * <nbdkitctime>234235253</nbdkitctime>
+ * <plugindirmtime>234235253</plugindirmtime>
+ * <filterdirmtime>234235253</filterdirmtime>
+ * <selfctime>234235253</selfctime>
+ * <selfvers>1002016</selfvers>
+ * <flag name='foo'/>
+ * <flag name='bar'/>
+ * ...
+ * </nbdkitCaps>
+ *
+ * Returns 0 on success, 1 if outdated, -1 on error
+ */
+static int
+qemuNbdkitCapsLoadCache(qemuNbdkitCaps *nbdkitCaps,
+ const char *filename)
+{
+ g_autoptr(xmlDoc) doc = NULL;
+ g_autoptr(xmlXPathContext) ctxt = NULL;
+ long long int l;
+ unsigned long lu;
+
+ if (!(doc = virXMLParseFile(filename)))
+ return -1;
+
+ if (!(ctxt = virXMLXPathContextNew(doc)))
+ return -1;
+
+ ctxt->node = xmlDocGetRootElement(doc);
+
+ if (STRNEQ((const char*)ctxt->node->name, "nbdkitCaps")) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("unexpected root element <%s>, "
+ "expecting <nbdkitCaps>"),
+ ctxt->node->name);
+ return -1;
+ }
+
+ if (virXPathLongLong("string(./selfctime)", ctxt, &l) < 0) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("missing selfctime in nbdkit capabilities XML"));
+ return -1;
+ }
+ nbdkitCaps->libvirtCtime = (time_t)l;
+
+ nbdkitCaps->libvirtVersion = 0;
+ if (virXPathULong("string(./selfvers)", ctxt, &lu) == 0)
+ nbdkitCaps->libvirtVersion = lu;
+
+ if (nbdkitCaps->libvirtCtime != virGetSelfLastChanged() ||
+ nbdkitCaps->libvirtVersion != LIBVIR_VERSION_NUMBER) {
+ VIR_DEBUG("Outdated capabilities in %s: libvirt changed "
+ "(%lld vs %lld, %lu vs %lu), stopping load",
+ nbdkitCaps->path,
+ (long long)nbdkitCaps->libvirtCtime,
+ (long long)virGetSelfLastChanged(),
+ (unsigned long)nbdkitCaps->libvirtVersion,
+ (unsigned long)LIBVIR_VERSION_NUMBER);
+ return 1;
+ }
+
+ if (qemuNbdkitCapsValidateBinary(nbdkitCaps, ctxt) < 0)
+ return -1;
+
+ if (virXPathLongLong("string(./nbdkitctime)", ctxt, &l) < 0) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("missing nbdkitctime in nbdkit capabilities XML"));
+ return -1;
+ }
+ nbdkitCaps->ctime = (time_t)l;
+
+ if (virXPathLongLong("string(./plugindirmtime)", ctxt, &l) == 0)
+ nbdkitCaps->pluginDirMtime = (time_t)l;
+
+ if (virXPathLongLong("string(./filterdirmtime)", ctxt, &l) == 0)
+ nbdkitCaps->filterDirMtime = (time_t)l;
+
+ if (qemuNbdkitCapsParseFlags(nbdkitCaps, ctxt) < 0)
+ return -1;
+
+ if ((nbdkitCaps->version = virXPathString("string(./version)", ctxt)) == NULL) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("missing version in nbdkit capabilities cache"));
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static void*
+virNbdkitCapsLoadFile(const char *filename,
+ const char *binary,
+ void *privData G_GNUC_UNUSED,
+ bool *outdated)
+{
+ g_autoptr(qemuNbdkitCaps) nbdkitCaps = qemuNbdkitCapsNew(binary);
+ int ret;
+
+ if (!nbdkitCaps)
+ return NULL;
+
+ ret = qemuNbdkitCapsLoadCache(nbdkitCaps, filename);
+ if (ret < 0)
+ return NULL;
+ if (ret == 1) {
+ *outdated = true;
+ return NULL;
+ }
+
+ return g_steal_pointer(&nbdkitCaps);
+}
+
+
+static char*
+qemuNbdkitCapsFormatCache(qemuNbdkitCaps *nbdkitCaps)
+{
+ g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
+ size_t i;
+
+ virBufferAddLit(&buf, "<nbdkitCaps>\n");
+ virBufferAdjustIndent(&buf, 2);
+
+ virBufferEscapeString(&buf, "<path>%s</path>\n",
+ nbdkitCaps->path);
+ virBufferAsprintf(&buf, "<nbdkitctime>%llu</nbdkitctime>\n",
+ (long long)nbdkitCaps->ctime);
+ if (nbdkitCaps->pluginDirMtime > 0) {
+ virBufferAsprintf(&buf, "<plugindirmtime>%llu</plugindirmtime>\n",
+ (long long)nbdkitCaps->pluginDirMtime);
+ }
+ if (nbdkitCaps->filterDirMtime > 0) {
+ virBufferAsprintf(&buf, "<filterdirmtime>%llu</filterdirmtime>\n",
+ (long long)nbdkitCaps->filterDirMtime);
+ }
+ virBufferAsprintf(&buf, "<selfctime>%llu</selfctime>\n",
+ (long long)nbdkitCaps->libvirtCtime);
+ virBufferAsprintf(&buf, "<selfvers>%lu</selfvers>\n",
+ (unsigned long)nbdkitCaps->libvirtVersion);
+
+ for (i = 0; i < QEMU_NBDKIT_CAPS_LAST; i++) {
+ if (qemuNbdkitCapsGet(nbdkitCaps, i)) {
+ virBufferAsprintf(&buf, "<flag name='%s'/>\n",
+ qemuNbdkitCapsTypeToString(i));
+ }
+ }
+
+ virBufferAsprintf(&buf, "<version>%s</version>\n",
+ nbdkitCaps->version);
+
+ virBufferAdjustIndent(&buf, -2);
+ virBufferAddLit(&buf, "</nbdkitCaps>\n");
+
+ return virBufferContentAndReset(&buf);
+}
+
+
+static int
+virNbdkitCapsSaveFile(void *data,
+ const char *filename,
+ void *privData G_GNUC_UNUSED)
+{
+ qemuNbdkitCaps *nbdkitCaps = data;
+ g_autofree char *xml = NULL;
+
+ xml = qemuNbdkitCapsFormatCache(nbdkitCaps);
+
+ if (virFileWriteStr(filename, xml, 0600) < 0) {
+ virReportSystemError(errno,
+ _("Failed to save '%s' for '%s'"),
+ filename, nbdkitCaps->path);
+ return -1;
+ }
+
+ VIR_DEBUG("Saved caps '%s' for '%s' with (%lld, %lld)",
+ filename, nbdkitCaps->path,
+ (long long)nbdkitCaps->ctime,
+ (long long)nbdkitCaps->libvirtCtime);
+
+ return 0;
+}
+
+
virFileCacheHandlers nbdkitCapsCacheHandlers = {
.isValid = virNbdkitCapsIsValid,
.newData = virNbdkitCapsNewData,
- .loadFile = NULL,
- .saveFile = NULL,
+ .loadFile = virNbdkitCapsLoadFile,
+ .saveFile = virNbdkitCapsSaveFile,
.privFree = NULL,
};
--
2.37.1
On Wed, Aug 31, 2022 at 13:40:51 -0500, Jonathon Jongsma wrote:
> Implement the loadFile and saveFile virFileCacheHandlers callbacks so
> that nbdkit capabilities are cached perstistently across daemon
> restarts. The format and implementation is modeled on the qemu
> capabilities, but simplified slightly.
>
> Signed-off-by: Jonathon Jongsma <jjongsma@redhat.com>
> ---
> src/qemu/qemu_nbdkit.c | 253 ++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 251 insertions(+), 2 deletions(-)
>
> diff --git a/src/qemu/qemu_nbdkit.c b/src/qemu/qemu_nbdkit.c
> index 3faa16674a..ac498b948c 100644
> --- a/src/qemu/qemu_nbdkit.c
> +++ b/src/qemu/qemu_nbdkit.c
> @@ -339,11 +339,260 @@ virNbdkitCapsNewData(const char *binary,
> }
>
>
> +static int
> +qemuNbdkitCapsValidateBinary(qemuNbdkitCaps *nbdkitCaps,
> + xmlXPathContextPtr ctxt)
> +{
> + g_autofree char *str = NULL;
> +
> + if (!(str = virXPathString("string(./path)", ctxt))) {
> + virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
> + _("missing path in nbdkit capabilities cache"));
> + return -1;
> + }
> +
> + if (STRNEQ(str, nbdkitCaps->path)) {
> + virReportError(VIR_ERR_INTERNAL_ERROR,
> + _("Expected caps for '%s' but saw '%s'"),
> + nbdkitCaps->path, str);
> + return -1;
> + }
> +
> + return 0;
> +}
> +
> +
> +static int
> +qemuNbdkitCapsParseFlags(qemuNbdkitCaps *nbdkitCaps,
> + xmlXPathContextPtr ctxt)
> +{
> + g_autofree xmlNodePtr *nodes = NULL;
> + size_t i;
> + int n;
> +
> + if ((n = virXPathNodeSet("./flag", ctxt, &nodes)) < 0) {
> + virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
> + _("failed to parse qemu capabilities flags"));
> + return -1;
> + }
> +
> + VIR_DEBUG("Got flags %d", n);
> + for (i = 0; i < n; i++) {
> + g_autofree char *str = NULL;
> + int flag;
> +
> + if (!(str = virXMLPropString(nodes[i], "name"))) {
> + virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
> + _("missing flag name in QEMU capabilities cache"));
> + return -1;
> + }
> +
> + flag = qemuNbdkitCapsTypeFromString(str);
> + if (flag < 0) {
> + virReportError(VIR_ERR_INTERNAL_ERROR,
> + _("Unknown nbdkit capabilities flag %s"), str);
> + return -1;
> + }
You can use virXMLPropEnum instead of having two clauses.
> +
> + qemuNbdkitCapsSet(nbdkitCaps, flag);
> + }
> +
> + return 0;
> +}
> +
> +
> +/*
> + * Parsing a doc that looks like
> + *
> + * <nbdkitCaps>
> + * <path>/some/path</path>
> + * <nbdkitctime>234235253</nbdkitctime>
> + * <plugindirmtime>234235253</plugindirmtime>
> + * <filterdirmtime>234235253</filterdirmtime>
> + * <selfctime>234235253</selfctime>
> + * <selfvers>1002016</selfvers>
> + * <flag name='foo'/>
> + * <flag name='bar'/>
> + * ...
> + * </nbdkitCaps>
> + *
> + * Returns 0 on success, 1 if outdated, -1 on error
> + */
> +static int
> +qemuNbdkitCapsLoadCache(qemuNbdkitCaps *nbdkitCaps,
> + const char *filename)
> +{
> + g_autoptr(xmlDoc) doc = NULL;
> + g_autoptr(xmlXPathContext) ctxt = NULL;
> + long long int l;
> + unsigned long lu;
> +
> + if (!(doc = virXMLParseFile(filename)))
I'll be posting soon patches that make 'virXMLParse' usable here without
the need to ...
> + return -1;
> +
> + if (!(ctxt = virXMLXPathContextNew(doc)))
> + return -1;
... fetch the context ...
> +
> + ctxt->node = xmlDocGetRootElement(doc);
... get the root node...
> +
> + if (STRNEQ((const char*)ctxt->node->name, "nbdkitCaps")) {
> + virReportError(VIR_ERR_XML_ERROR,
> + _("unexpected root element <%s>, "
> + "expecting <nbdkitCaps>"),
> + ctxt->node->name);
> + return -1;
> + }
... and also do this validation by doing all of that in the virXMLParse
function.
> +
> + if (virXPathLongLong("string(./selfctime)", ctxt, &l) < 0) {
> + virReportError(VIR_ERR_XML_ERROR, "%s",
> + _("missing selfctime in nbdkit capabilities XML"));
> + return -1;
> + }
> + nbdkitCaps->libvirtCtime = (time_t)l;
> +
> + nbdkitCaps->libvirtVersion = 0;
> + if (virXPathULong("string(./selfvers)", ctxt, &lu) == 0)
> + nbdkitCaps->libvirtVersion = lu;
> +
> + if (nbdkitCaps->libvirtCtime != virGetSelfLastChanged() ||
> + nbdkitCaps->libvirtVersion != LIBVIR_VERSION_NUMBER) {
> + VIR_DEBUG("Outdated capabilities in %s: libvirt changed "
> + "(%lld vs %lld, %lu vs %lu), stopping load",
> + nbdkitCaps->path,
> + (long long)nbdkitCaps->libvirtCtime,
> + (long long)virGetSelfLastChanged(),
> + (unsigned long)nbdkitCaps->libvirtVersion,
> + (unsigned long)LIBVIR_VERSION_NUMBER);
> + return 1;
> + }
> +
> + if (qemuNbdkitCapsValidateBinary(nbdkitCaps, ctxt) < 0)
> + return -1;
> +
> + if (virXPathLongLong("string(./nbdkitctime)", ctxt, &l) < 0) {
> + virReportError(VIR_ERR_XML_ERROR, "%s",
> + _("missing nbdkitctime in nbdkit capabilities XML"));
> + return -1;
> + }
> + nbdkitCaps->ctime = (time_t)l;
> +
> + if (virXPathLongLong("string(./plugindirmtime)", ctxt, &l) == 0)
> + nbdkitCaps->pluginDirMtime = (time_t)l;
> +
> + if (virXPathLongLong("string(./filterdirmtime)", ctxt, &l) == 0)
> + nbdkitCaps->filterDirMtime = (time_t)l;
Any reason why these are optional but the ctime of nbdkit is not?
> +
> + if (qemuNbdkitCapsParseFlags(nbdkitCaps, ctxt) < 0)
> + return -1;
> +
> + if ((nbdkitCaps->version = virXPathString("string(./version)", ctxt)) == NULL) {
> + virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
> + _("missing version in nbdkit capabilities cache"));
> + return -1;
> + }
> +
> + return 0;
> +}
> +
> +
> +static void*
> +virNbdkitCapsLoadFile(const char *filename,
> + const char *binary,
> + void *privData G_GNUC_UNUSED,
> + bool *outdated)
> +{
> + g_autoptr(qemuNbdkitCaps) nbdkitCaps = qemuNbdkitCapsNew(binary);
> + int ret;
> +
> + if (!nbdkitCaps)
> + return NULL;
> +
> + ret = qemuNbdkitCapsLoadCache(nbdkitCaps, filename);
> + if (ret < 0)
> + return NULL;
> + if (ret == 1) {
> + *outdated = true;
> + return NULL;
> + }
> +
> + return g_steal_pointer(&nbdkitCaps);
> +}
> +
> +
> +static char*
> +qemuNbdkitCapsFormatCache(qemuNbdkitCaps *nbdkitCaps)
> +{
> + g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
> + size_t i;
> +
> + virBufferAddLit(&buf, "<nbdkitCaps>\n");
> + virBufferAdjustIndent(&buf, 2);
> +
> + virBufferEscapeString(&buf, "<path>%s</path>\n",
> + nbdkitCaps->path);
> + virBufferAsprintf(&buf, "<nbdkitctime>%llu</nbdkitctime>\n",
> + (long long)nbdkitCaps->ctime);
> + if (nbdkitCaps->pluginDirMtime > 0) {
> + virBufferAsprintf(&buf, "<plugindirmtime>%llu</plugindirmtime>\n",
The format substitution is specifying an unsigned long long ...
> + (long long)nbdkitCaps->pluginDirMtime);
while you typecast to long long. Similarly above in the parser you parse
as long long instead of unsigned long long.
> + }
> + if (nbdkitCaps->filterDirMtime > 0) {
> + virBufferAsprintf(&buf, "<filterdirmtime>%llu</filterdirmtime>\n",
> + (long long)nbdkitCaps->filterDirMtime);
> + }
> + virBufferAsprintf(&buf, "<selfctime>%llu</selfctime>\n",
> + (long long)nbdkitCaps->libvirtCtime);
> + virBufferAsprintf(&buf, "<selfvers>%lu</selfvers>\n",
> + (unsigned long)nbdkitCaps->libvirtVersion);
> +
> + for (i = 0; i < QEMU_NBDKIT_CAPS_LAST; i++) {
> + if (qemuNbdkitCapsGet(nbdkitCaps, i)) {
> + virBufferAsprintf(&buf, "<flag name='%s'/>\n",
> + qemuNbdkitCapsTypeToString(i));
> + }
> + }
> +
> + virBufferAsprintf(&buf, "<version>%s</version>\n",
> + nbdkitCaps->version);
> +
> + virBufferAdjustIndent(&buf, -2);
> + virBufferAddLit(&buf, "</nbdkitCaps>\n");
> +
> + return virBufferContentAndReset(&buf);
> +}
> +
> +
> +static int
> +virNbdkitCapsSaveFile(void *data,
> + const char *filename,
> + void *privData G_GNUC_UNUSED)
> +{
> + qemuNbdkitCaps *nbdkitCaps = data;
> + g_autofree char *xml = NULL;
> +
> + xml = qemuNbdkitCapsFormatCache(nbdkitCaps);
> +
> + if (virFileWriteStr(filename, xml, 0600) < 0) {
> + virReportSystemError(errno,
> + _("Failed to save '%s' for '%s'"),
> + filename, nbdkitCaps->path);
> + return -1;
> + }
> +
> + VIR_DEBUG("Saved caps '%s' for '%s' with (%lld, %lld)",
> + filename, nbdkitCaps->path,
> + (long long)nbdkitCaps->ctime,
> + (long long)nbdkitCaps->libvirtCtime);
> +
> + return 0;
> +}
> +
> +
> virFileCacheHandlers nbdkitCapsCacheHandlers = {
> .isValid = virNbdkitCapsIsValid,
> .newData = virNbdkitCapsNewData,
> - .loadFile = NULL,
> - .saveFile = NULL,
> + .loadFile = virNbdkitCapsLoadFile,
> + .saveFile = virNbdkitCapsSaveFile,
> .privFree = NULL,
> };
>
> --
> 2.37.1
>
On 9/27/22 6:49 AM, Peter Krempa wrote:
>> +
>> + if (virXPathLongLong("string(./selfctime)", ctxt, &l) < 0) {
>> + virReportError(VIR_ERR_XML_ERROR, "%s",
>> + _("missing selfctime in nbdkit capabilities XML"));
>> + return -1;
>> + }
>> + nbdkitCaps->libvirtCtime = (time_t)l;
>> +
>> + nbdkitCaps->libvirtVersion = 0;
>> + if (virXPathULong("string(./selfvers)", ctxt, &lu) == 0)
>> + nbdkitCaps->libvirtVersion = lu;
>> +
>> + if (nbdkitCaps->libvirtCtime != virGetSelfLastChanged() ||
>> + nbdkitCaps->libvirtVersion != LIBVIR_VERSION_NUMBER) {
>> + VIR_DEBUG("Outdated capabilities in %s: libvirt changed "
>> + "(%lld vs %lld, %lu vs %lu), stopping load",
>> + nbdkitCaps->path,
>> + (long long)nbdkitCaps->libvirtCtime,
>> + (long long)virGetSelfLastChanged(),
>> + (unsigned long)nbdkitCaps->libvirtVersion,
>> + (unsigned long)LIBVIR_VERSION_NUMBER);
>> + return 1;
>> + }
>> +
>> + if (qemuNbdkitCapsValidateBinary(nbdkitCaps, ctxt) < 0)
>> + return -1;
>> +
>> + if (virXPathLongLong("string(./nbdkitctime)", ctxt, &l) < 0) {
>> + virReportError(VIR_ERR_XML_ERROR, "%s",
>> + _("missing nbdkitctime in nbdkit capabilities XML"));
>> + return -1;
>> + }
>> + nbdkitCaps->ctime = (time_t)l;
>> +
>> + if (virXPathLongLong("string(./plugindirmtime)", ctxt, &l) == 0)
>> + nbdkitCaps->pluginDirMtime = (time_t)l;
>> +
>> + if (virXPathLongLong("string(./filterdirmtime)", ctxt, &l) == 0)
>> + nbdkitCaps->filterDirMtime = (time_t)l;
>
> Any reason why these are optional but the ctime of nbdkit is not?
Because the format function below does not serialize these values to the
xml in the case where the directories don't exist. But I will change it
so that we format these fields to the xml even in the case where the
directories are not found. The time value will simply be 0 in this case.
>
>> +
>> + if (qemuNbdkitCapsParseFlags(nbdkitCaps, ctxt) < 0)
>> + return -1;
>> +
>> + if ((nbdkitCaps->version = virXPathString("string(./version)", ctxt)) == NULL) {
>> + virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
>> + _("missing version in nbdkit capabilities cache"));
>> + return -1;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +
>> +static void*
>> +virNbdkitCapsLoadFile(const char *filename,
>> + const char *binary,
>> + void *privData G_GNUC_UNUSED,
>> + bool *outdated)
>> +{
>> + g_autoptr(qemuNbdkitCaps) nbdkitCaps = qemuNbdkitCapsNew(binary);
>> + int ret;
>> +
>> + if (!nbdkitCaps)
>> + return NULL;
>> +
>> + ret = qemuNbdkitCapsLoadCache(nbdkitCaps, filename);
>> + if (ret < 0)
>> + return NULL;
>> + if (ret == 1) {
>> + *outdated = true;
>> + return NULL;
>> + }
>> +
>> + return g_steal_pointer(&nbdkitCaps);
>> +}
>> +
>> +
>> +static char*
>> +qemuNbdkitCapsFormatCache(qemuNbdkitCaps *nbdkitCaps)
>> +{
>> + g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
>> + size_t i;
>> +
>> + virBufferAddLit(&buf, "<nbdkitCaps>\n");
>> + virBufferAdjustIndent(&buf, 2);
>> +
>> + virBufferEscapeString(&buf, "<path>%s</path>\n",
>> + nbdkitCaps->path);
>> + virBufferAsprintf(&buf, "<nbdkitctime>%llu</nbdkitctime>\n",
>> + (long long)nbdkitCaps->ctime);
>> + if (nbdkitCaps->pluginDirMtime > 0) {
>> + virBufferAsprintf(&buf, "<plugindirmtime>%llu</plugindirmtime>\n",
>
> The format substitution is specifying an unsigned long long ...
>
>> + (long long)nbdkitCaps->pluginDirMtime);
>
> while you typecast to long long. Similarly above in the parser you parse
> as long long instead of unsigned long long.
I have to admit, this code was blatantly copied and adapted from
qemu_capabilities.c. So that code also has the same issue. But I can
change this to format as a signed integer since the underlying time_t
type is signed.
Jonathon
© 2016 - 2026 Red Hat, Inc.