From nobody Mon Feb 9 19:53:42 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of redhat.com designates 170.10.133.124 as permitted sender) client-ip=170.10.133.124; envelope-from=libvir-list-bounces@redhat.com; helo=us-smtp-delivery-124.mimecast.com; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of redhat.com designates 170.10.133.124 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; dmarc=pass(p=none dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1661975035; cv=none; d=zohomail.com; s=zohoarc; b=GJbONpeiC24EH9ArAN7Vai8RwHJmkm+smZt+oJP5G4d3APHXva0pXlOs1g3t2kJpSyFm7/7UU1zUAHKHboArUzy96ZQVrMe0iWIZKrLBh/bsuFTgEt/95RkRi+wEVVPNQw2RKAN7o7ZL/EYD0/MjOB9vvSlOOWNN9vYFTMrT+90= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1661975035; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=/kWIybfAI/q+Ah5zP3tV7HZsHixGUeyCxv9YaWixRn4=; b=nlc7SMGqY+5zoJuKpOlusyaZBXRPlZeLuJy7CBmBnocxkxz2fvytbuvlM/IBUR9O6VLoK6joSTsKI/307FA29alUxk2TgF93MdvtwrrDaGu3PHJX8ctAfwUnwL/sugUUVA91jSdNJ9N20ETjg8UL6WXZYOxPX+pYQ6OPg5Cvwys= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of redhat.com designates 170.10.133.124 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by mx.zohomail.com with SMTPS id 1661975035657662.5390867944027; Wed, 31 Aug 2022 12:43:55 -0700 (PDT) Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-296-EKerT9PZMG-XYqBpEpUXzg-1; Wed, 31 Aug 2022 15:43:51 -0400 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.rdu2.redhat.com [10.11.54.8]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 1E62310119EE; Wed, 31 Aug 2022 19:43:49 +0000 (UTC) Received: from mm-prod-listman-01.mail-001.prod.us-east-1.aws.redhat.com (unknown [10.30.29.100]) by smtp.corp.redhat.com (Postfix) with ESMTP id 01683C15BB3; Wed, 31 Aug 2022 19:43:49 +0000 (UTC) Received: from mm-prod-listman-01.mail-001.prod.us-east-1.aws.redhat.com (localhost [IPv6:::1]) by mm-prod-listman-01.mail-001.prod.us-east-1.aws.redhat.com (Postfix) with ESMTP id 8D84B1946A45; Wed, 31 Aug 2022 19:43:48 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.rdu2.redhat.com [10.11.54.6]) by mm-prod-listman-01.mail-001.prod.us-east-1.aws.redhat.com (Postfix) with ESMTP id 8D4641946A40 for ; Wed, 31 Aug 2022 18:41:04 +0000 (UTC) Received: by smtp.corp.redhat.com (Postfix) id 6FB412166B2A; Wed, 31 Aug 2022 18:41:04 +0000 (UTC) Received: from himantopus.redhat.com (unknown [10.22.10.226]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 44AF92166B26; Wed, 31 Aug 2022 18:41:04 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1661975034; h=from:from:sender:sender:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references:list-id:list-help: list-unsubscribe:list-subscribe:list-post; bh=/kWIybfAI/q+Ah5zP3tV7HZsHixGUeyCxv9YaWixRn4=; b=NaE1VisDAHhmkCOExYaFIzFCOla5Ijf6Tt7ye16xGJuV+fndF5D/VB9S3u8l2+OU/IEmGY e150r+Ja5fur08mIUPWXs2ZG+jKtCpJYvod7PJqPJDjD3vKqMv1Qcs0bpSZdDV8wYRyfRG 4Y4stQC/1UmRUCiMH/mmeG5WnV4oAro= X-MC-Unique: EKerT9PZMG-XYqBpEpUXzg-1 X-Original-To: libvir-list@listman.corp.redhat.com From: Jonathon Jongsma To: libvir-list@redhat.com Subject: [libvirt PATCH v2 06/16] qemu: implement persistent file cache for nbdkit caps Date: Wed, 31 Aug 2022 13:40:51 -0500 Message-Id: <20220831184101.716362-7-jjongsma@redhat.com> In-Reply-To: <20220831184101.716362-1-jjongsma@redhat.com> References: <20220831184101.716362-1-jjongsma@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.78 on 10.11.54.6 X-BeenThere: libvir-list@redhat.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Development discussions about the libvirt library & tools List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: pkrempa@redhat.com Errors-To: libvir-list-bounces@redhat.com Sender: "libvir-list" X-Scanned-By: MIMEDefang 2.85 on 10.11.54.8 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: quoted-printable X-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1661975036374100003 Content-Type: text/plain; charset="utf-8"; x-default="true" 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 --- 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, } =20 =20 +static int +qemuNbdkitCapsValidateBinary(qemuNbdkitCaps *nbdkitCaps, + xmlXPathContextPtr ctxt) +{ + g_autofree char *str =3D NULL; + + if (!(str =3D 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 =3D NULL; + size_t i; + int n; + + if ((n =3D 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 =3D 0; i < n; i++) { + g_autofree char *str =3D NULL; + int flag; + + if (!(str =3D virXMLPropString(nodes[i], "name"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("missing flag name in QEMU capabilities cache= ")); + return -1; + } + + flag =3D 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 + * + * + * /some/path + * 234235253 + * 234235253 + * 234235253 + * 234235253 + * 1002016 + * + * + * ... + * + * + * Returns 0 on success, 1 if outdated, -1 on error + */ +static int +qemuNbdkitCapsLoadCache(qemuNbdkitCaps *nbdkitCaps, + const char *filename) +{ + g_autoptr(xmlDoc) doc =3D NULL; + g_autoptr(xmlXPathContext) ctxt =3D NULL; + long long int l; + unsigned long lu; + + if (!(doc =3D virXMLParseFile(filename))) + return -1; + + if (!(ctxt =3D virXMLXPathContextNew(doc))) + return -1; + + ctxt->node =3D xmlDocGetRootElement(doc); + + if (STRNEQ((const char*)ctxt->node->name, "nbdkitCaps")) { + virReportError(VIR_ERR_XML_ERROR, + _("unexpected root element <%s>, " + "expecting "), + 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 =3D (time_t)l; + + nbdkitCaps->libvirtVersion =3D 0; + if (virXPathULong("string(./selfvers)", ctxt, &lu) =3D=3D 0) + nbdkitCaps->libvirtVersion =3D lu; + + if (nbdkitCaps->libvirtCtime !=3D virGetSelfLastChanged() || + nbdkitCaps->libvirtVersion !=3D 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 =3D (time_t)l; + + if (virXPathLongLong("string(./plugindirmtime)", ctxt, &l) =3D=3D 0) + nbdkitCaps->pluginDirMtime =3D (time_t)l; + + if (virXPathLongLong("string(./filterdirmtime)", ctxt, &l) =3D=3D 0) + nbdkitCaps->filterDirMtime =3D (time_t)l; + + if (qemuNbdkitCapsParseFlags(nbdkitCaps, ctxt) < 0) + return -1; + + if ((nbdkitCaps->version =3D virXPathString("string(./version)", ctxt)= ) =3D=3D 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 =3D qemuNbdkitCapsNew(binary); + int ret; + + if (!nbdkitCaps) + return NULL; + + ret =3D qemuNbdkitCapsLoadCache(nbdkitCaps, filename); + if (ret < 0) + return NULL; + if (ret =3D=3D 1) { + *outdated =3D true; + return NULL; + } + + return g_steal_pointer(&nbdkitCaps); +} + + +static char* +qemuNbdkitCapsFormatCache(qemuNbdkitCaps *nbdkitCaps) +{ + g_auto(virBuffer) buf =3D VIR_BUFFER_INITIALIZER; + size_t i; + + virBufferAddLit(&buf, "\n"); + virBufferAdjustIndent(&buf, 2); + + virBufferEscapeString(&buf, "%s\n", + nbdkitCaps->path); + virBufferAsprintf(&buf, "%llu\n", + (long long)nbdkitCaps->ctime); + if (nbdkitCaps->pluginDirMtime > 0) { + virBufferAsprintf(&buf, "%llu\n", + (long long)nbdkitCaps->pluginDirMtime); + } + if (nbdkitCaps->filterDirMtime > 0) { + virBufferAsprintf(&buf, "%llu\n", + (long long)nbdkitCaps->filterDirMtime); + } + virBufferAsprintf(&buf, "%llu\n", + (long long)nbdkitCaps->libvirtCtime); + virBufferAsprintf(&buf, "%lu\n", + (unsigned long)nbdkitCaps->libvirtVersion); + + for (i =3D 0; i < QEMU_NBDKIT_CAPS_LAST; i++) { + if (qemuNbdkitCapsGet(nbdkitCaps, i)) { + virBufferAsprintf(&buf, "\n", + qemuNbdkitCapsTypeToString(i)); + } + } + + virBufferAsprintf(&buf, "%s\n", + nbdkitCaps->version); + + virBufferAdjustIndent(&buf, -2); + virBufferAddLit(&buf, "\n"); + + return virBufferContentAndReset(&buf); +} + + +static int +virNbdkitCapsSaveFile(void *data, + const char *filename, + void *privData G_GNUC_UNUSED) +{ + qemuNbdkitCaps *nbdkitCaps =3D data; + g_autofree char *xml =3D NULL; + + xml =3D 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 =3D { .isValid =3D virNbdkitCapsIsValid, .newData =3D virNbdkitCapsNewData, - .loadFile =3D NULL, - .saveFile =3D NULL, + .loadFile =3D virNbdkitCapsLoadFile, + .saveFile =3D virNbdkitCapsSaveFile, .privFree =3D NULL, }; =20 --=20 2.37.1