From nobody Sun Feb 8 14:30:52 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of redhat.com designates 216.205.24.124 as permitted sender) client-ip=216.205.24.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 216.205.24.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=1611257703; cv=none; d=zohomail.com; s=zohoarc; b=NkfoEcorDeo3mngfXGXah95wO2aDrE+mFLArxPJbKFVh5rRG44z+MEwEi7Fcyetz2NOFS12Pq4zTVyGlBC8oeR1CDmvEMaO7qwjvL9uiJ3CLeoEFLySa7fytq31yBcSf2JgOP+09AEnlNo11Eyo0w+VQxPOsUPIXNcLaxvJ/kmI= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1611257703; h=Content-Type:Content-Transfer-Encoding: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=1g4IjR4VPzry3RkkaoNZWRotUGa5S1VvwnFbhV4nAHQ=; b=f+jUaYMstXQquODdq/UDwgPlkp33Bm9fMurU0V+mToCmLUaM96V+d35BCUoYazJP1dlVdCU2AlGczWtD6t7+24JKW0u2FXldOX9Pc6FmfPjC9LL/ZkLQapQ6I/B/GwrkWpBsS9xb1QSPgb8qYOTn+68mmZ/xL5k/ofwvcqkohaI= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of redhat.com designates 216.205.24.124 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; dmarc=pass header.from= (p=none dis=none) header.from= Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [216.205.24.124]) by mx.zohomail.com with SMTPS id 1611257703613791.7213988033004; Thu, 21 Jan 2021 11:35:03 -0800 (PST) Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-203-dsm0zmWRMVCZ5yqyFoA4kQ-1; Thu, 21 Jan 2021 14:34:58 -0500 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 49B7F1842143; Thu, 21 Jan 2021 19:34:52 +0000 (UTC) Received: from colo-mx.corp.redhat.com (colo-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.20]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 20F4A5D9E3; Thu, 21 Jan 2021 19:34:52 +0000 (UTC) Received: from lists01.pubmisc.prod.ext.phx2.redhat.com (lists01.pubmisc.prod.ext.phx2.redhat.com [10.5.19.33]) by colo-mx.corp.redhat.com (Postfix) with ESMTP id D77721809CAC; Thu, 21 Jan 2021 19:34:51 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.phx2.redhat.com [10.5.11.22]) by lists01.pubmisc.prod.ext.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id 10LJYgTt023209 for ; Thu, 21 Jan 2021 14:34:42 -0500 Received: by smtp.corp.redhat.com (Postfix) id 561C810021AA; Thu, 21 Jan 2021 19:34:42 +0000 (UTC) Received: from antique-laptop.redhat.com (unknown [10.40.208.8]) by smtp.corp.redhat.com (Postfix) with ESMTP id D9FE710023AE for ; Thu, 21 Jan 2021 19:34:40 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1611257702; h=from:from:sender:sender:reply-to:subject:subject:date:date: message-id:message-id:to:to: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=1g4IjR4VPzry3RkkaoNZWRotUGa5S1VvwnFbhV4nAHQ=; b=hAEBlUMfLsmUt+oSBO6+SLId0rLmJQIB58DkdEr6bwm6GHW3B5JC7AN2rKoBVb9NiHiuwR SzoAVKNmN3AobeF9qnyDCW1tK1dY+34TsJtNLfhHtOkW9we2kz0KOJqlXMLImIrMMfUY8J /LLdzrhDsyBD0j+eWGFD+LHBtFqCfOk= X-MC-Unique: dsm0zmWRMVCZ5yqyFoA4kQ-1 From: Pavel Hrdina To: libvir-list@redhat.com Subject: [libvirt PATCH v2 07/13] util: extract virStorageFile code into storage_source Date: Thu, 21 Jan 2021 20:34:21 +0100 Message-Id: In-Reply-To: References: MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.22 X-loop: libvir-list@redhat.com X-BeenThere: libvir-list@redhat.com X-Mailman-Version: 2.1.12 Precedence: junk List-Id: Development discussions about the libvirt library & tools List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: libvir-list-bounces@redhat.com Errors-To: libvir-list-bounces@redhat.com X-Scanned-By: MIMEDefang 2.79 on 10.5.11.14 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=libvir-list-bounces@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: quoted-printable X-ZohoMail-DKIM: pass (identity @redhat.com) Content-Type: text/plain; charset="utf-8" Up until now we had a runtime code and XML related code in the same source file inside util directory. This patch takes the runtime part and extracts it into the new storage_file directory. Signed-off-by: Pavel Hrdina Reviewed-by: Peter Krempa --- po/POTFILES.in | 1 + src/libvirt_private.syms | 57 +- src/libxl/meson.build | 1 + src/libxl/xen_xl.c | 2 +- src/qemu/meson.build | 1 + src/qemu/qemu_backup.c | 1 + src/qemu/qemu_block.c | 1 + src/qemu/qemu_domain.c | 1 + src/qemu/qemu_driver.c | 1 + src/qemu/qemu_hotplug.c | 1 + src/qemu/qemu_process.c | 1 + src/qemu/qemu_snapshot.c | 1 + src/security/meson.build | 1 + src/security/virt-aa-helper.c | 2 +- src/storage/meson.build | 4 + src/storage/storage_backend_gluster.c | 1 + src/storage/storage_util.c | 1 + src/storage_file/meson.build | 18 + src/storage_file/storage_source.c | 2614 +++++++++++++++++++++++++ src/storage_file/storage_source.h | 148 ++ src/util/virstoragefile.c | 2573 ------------------------ src/util/virstoragefile.h | 75 - tests/meson.build | 4 +- tests/qemublocktest.c | 1 + tests/virstoragetest.c | 1 + 25 files changed, 2833 insertions(+), 2679 deletions(-) create mode 100644 src/storage_file/storage_source.c create mode 100644 src/storage_file/storage_source.h diff --git a/po/POTFILES.in b/po/POTFILES.in index 19eb15ada0..1e47af987d 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -225,6 +225,7 @@ @SRCDIR@src/storage/storage_util.c @SRCDIR@src/storage_file/storage_file_fs.c @SRCDIR@src/storage_file/storage_file_gluster.c +@SRCDIR@src/storage_file/storage_source.c @SRCDIR@src/test/test_driver.c @SRCDIR@src/util/iohelper.c @SRCDIR@src/util/viralloc.c diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 2dfc7e32d5..67a863467d 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1618,6 +1618,36 @@ virSecurityManagerVerify; virSecurityXATTRNamespaceDefined; =20 =20 +# storage_file/storage_source.h +virStorageFileAccess; +virStorageFileChainLookup; +virStorageFileChown; +virStorageFileCreate; +virStorageFileDeinit; +virStorageFileGetBackingStoreStr; +virStorageFileGetMetadata; +virStorageFileGetMetadataFromBuf; +virStorageFileGetMetadataFromFD; +virStorageFileGetRelativeBackingPath; +virStorageFileGetUniqueIdentifier; +virStorageFileInit; +virStorageFileInitAs; +virStorageFileRead; +virStorageFileReportBrokenChain; +virStorageFileStat; +virStorageFileSupportsAccess; +virStorageFileSupportsBackingChainTraversal; +virStorageFileSupportsCreate; +virStorageFileSupportsSecurityDriver; +virStorageFileUnlink; +virStorageSourceNewFromBacking; +virStorageSourceNewFromBackingAbsolute; +virStorageSourceParseRBDColonString; +virStorageSourceUpdateBackingSizes; +virStorageSourceUpdateCapacity; +virStorageSourceUpdatePhysicalSize; + + # util/glibcompat.h vir_g_canonicalize_filename; vir_g_fsync; @@ -3126,36 +3156,15 @@ virStorageAuthDefCopy; virStorageAuthDefFormat; virStorageAuthDefFree; virStorageAuthDefParse; -virStorageFileAccess; virStorageFileCanonicalizePath; -virStorageFileChainLookup; -virStorageFileChown; -virStorageFileCreate; -virStorageFileDeinit; virStorageFileFeatureTypeFromString; virStorageFileFeatureTypeToString; virStorageFileFormatTypeFromString; virStorageFileFormatTypeToString; -virStorageFileGetBackingStoreStr; -virStorageFileGetMetadata; -virStorageFileGetMetadataFromBuf; -virStorageFileGetMetadataFromFD; virStorageFileGetNPIVKey; -virStorageFileGetRelativeBackingPath; virStorageFileGetSCSIKey; -virStorageFileGetUniqueIdentifier; -virStorageFileInit; -virStorageFileInitAs; virStorageFileParseBackingStoreStr; virStorageFileParseChainIndex; -virStorageFileRead; -virStorageFileReportBrokenChain; -virStorageFileStat; -virStorageFileSupportsAccess; -virStorageFileSupportsBackingChainTraversal; -virStorageFileSupportsCreate; -virStorageFileSupportsSecurityDriver; -virStorageFileUnlink; virStorageIsFile; virStorageIsRelative; virStorageNetHostDefClear; @@ -3191,18 +3200,12 @@ virStorageSourceIsSameLocation; virStorageSourceNetCookiesValidate; virStorageSourceNetworkAssignDefaultPorts; virStorageSourceNew; -virStorageSourceNewFromBacking; -virStorageSourceNewFromBackingAbsolute; virStorageSourceNVMeDefFree; -virStorageSourceParseRBDColonString; virStorageSourcePoolDefFree; virStorageSourcePoolModeTypeFromString; virStorageSourcePoolModeTypeToString; virStorageSourcePrivateDataFormatRelPath; virStorageSourcePrivateDataParseRelPath; -virStorageSourceUpdateBackingSizes; -virStorageSourceUpdateCapacity; -virStorageSourceUpdatePhysicalSize; virStorageTypeFromString; virStorageTypeToString; =20 diff --git a/src/libxl/meson.build b/src/libxl/meson.build index 3bb6cc5f2e..783af6c667 100644 --- a/src/libxl/meson.build +++ b/src/libxl/meson.build @@ -27,6 +27,7 @@ if conf.has('WITH_LIBXL') include_directories: [ conf_inc_dir, hypervisor_inc_dir, + storage_file_inc_dir, ], ) =20 diff --git a/src/libxl/xen_xl.c b/src/libxl/xen_xl.c index 17b93d0f5c..621ee63a99 100644 --- a/src/libxl/xen_xl.c +++ b/src/libxl/xen_xl.c @@ -29,7 +29,7 @@ #include "domain_conf.h" #include "viralloc.h" #include "virstring.h" -#include "virstoragefile.h" +#include "storage_source.h" #include "xen_xl.h" #include "libxl_capabilities.h" #include "libxl_conf.h" diff --git a/src/qemu/meson.build b/src/qemu/meson.build index 90640b03c6..7ab591d040 100644 --- a/src/qemu/meson.build +++ b/src/qemu/meson.build @@ -104,6 +104,7 @@ if conf.has('WITH_QEMU') include_directories: [ conf_inc_dir, hypervisor_inc_dir, + storage_file_inc_dir, ], ) =20 diff --git a/src/qemu/qemu_backup.c b/src/qemu/qemu_backup.c index c444f8aaba..72ca781d8b 100644 --- a/src/qemu/qemu_backup.c +++ b/src/qemu/qemu_backup.c @@ -29,6 +29,7 @@ #include "qemu_checkpoint.h" #include "qemu_command.h" =20 +#include "storage_source.h" #include "virerror.h" #include "virlog.h" #include "virbuffer.h" diff --git a/src/qemu/qemu_block.c b/src/qemu/qemu_block.c index c98094e746..ca23466b22 100644 --- a/src/qemu/qemu_block.c +++ b/src/qemu/qemu_block.c @@ -24,6 +24,7 @@ #include "qemu_alias.h" #include "qemu_security.h" =20 +#include "storage_source.h" #include "viralloc.h" #include "virstoragefile.h" #include "virstring.h" diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 966608882f..165321858e 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -52,6 +52,7 @@ #include "virtime.h" #include "virnetdevopenvswitch.h" #include "virstoragefile.h" +#include "storage_source.h" #include "virstring.h" #include "virthreadjob.h" #include "virprocess.h" diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 34a8fbe233..a5c2488f2c 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -84,6 +84,7 @@ #include "virhook.h" #include "virstoragefile.h" #include "virstoragefileprobe.h" +#include "storage_source.h" #include "virfile.h" #include "virfdstream.h" #include "configmake.h" diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index d016fe09a4..f1fa5986e1 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -53,6 +53,7 @@ #include "virnetdevmidonet.h" #include "device_conf.h" #include "virstoragefile.h" +#include "storage_source.h" #include "virstring.h" #include "virtime.h" #include "virqemu.h" diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 202d867289..14e1f5d962 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -95,6 +95,7 @@ #include "viridentity.h" #include "virthreadjob.h" #include "virutil.h" +#include "storage_source.h" =20 #define VIR_FROM_THIS VIR_FROM_QEMU =20 diff --git a/src/qemu/qemu_snapshot.c b/src/qemu/qemu_snapshot.c index c2fa54f5ab..994c7dc60b 100644 --- a/src/qemu/qemu_snapshot.c +++ b/src/qemu/qemu_snapshot.c @@ -42,6 +42,7 @@ #include "virstring.h" #include "virdomainsnapshotobjlist.h" #include "virqemu.h" +#include "storage_source.h" =20 #define VIR_FROM_THIS VIR_FROM_QEMU =20 diff --git a/src/security/meson.build b/src/security/meson.build index 4f876c03c2..416fec7900 100644 --- a/src/security/meson.build +++ b/src/security/meson.build @@ -48,6 +48,7 @@ if conf.has('WITH_LIBVIRTD') and conf.has('WITH_APPARMOR') conf_inc_dir, hypervisor_inc_dir, include_directories('.'), + include_directories('../storage_file'), ], } endif diff --git a/src/security/virt-aa-helper.c b/src/security/virt-aa-helper.c index 18af175655..6525baf193 100644 --- a/src/security/virt-aa-helper.c +++ b/src/security/virt-aa-helper.c @@ -37,6 +37,7 @@ =20 #include "security_driver.h" #include "security_apparmor.h" +#include "storage_source.h" #include "domain_conf.h" #include "virxml.h" #include "viruuid.h" @@ -46,7 +47,6 @@ #include "virfile.h" #include "configmake.h" #include "virrandom.h" -#include "virstoragefile.h" #include "virstring.h" #include "virgettext.h" #include "virhostdev.h" diff --git a/src/storage/meson.build b/src/storage/meson.build index 8537359e93..153ff6f846 100644 --- a/src/storage/meson.build +++ b/src/storage/meson.build @@ -79,6 +79,7 @@ if conf.has('WITH_STORAGE') ], include_directories: [ conf_inc_dir, + include_directories('../storage_file'), ], ) =20 @@ -162,6 +163,9 @@ if conf.has('WITH_STORAGE_GLUSTER') 'deps': [ glusterfs_dep, ], + 'include': [ + include_directories('../storage_file'), + ], 'install_dir': storage_backend_install_dir, } endif diff --git a/src/storage/storage_backend_gluster.c b/src/storage/storage_ba= ckend_gluster.c index 205a707a17..782f8eb611 100644 --- a/src/storage/storage_backend_gluster.c +++ b/src/storage/storage_backend_gluster.c @@ -32,6 +32,7 @@ #include "virstring.h" #include "viruri.h" #include "storage_util.h" +#include "storage_source.h" =20 #define VIR_FROM_THIS VIR_FROM_STORAGE =20 diff --git a/src/storage/storage_util.c b/src/storage/storage_util.c index 4117127d65..d51fa2b4c0 100644 --- a/src/storage/storage_util.c +++ b/src/storage/storage_util.c @@ -64,6 +64,7 @@ #include "virstoragefile.h" #include "virstoragefileprobe.h" #include "storage_util.h" +#include "storage_source.h" #include "virlog.h" #include "virfile.h" #include "virjson.h" diff --git a/src/storage_file/meson.build b/src/storage_file/meson.build index 20eb0176fc..bae018feac 100644 --- a/src/storage_file/meson.build +++ b/src/storage_file/meson.build @@ -1,3 +1,7 @@ +storage_file_sources =3D [ + 'storage_source.c', +] + stoarge_file_fs_sources =3D [ 'storage_file_fs.c', ] @@ -8,6 +12,18 @@ storage_file_gluster_sources =3D [ =20 storage_file_install_dir =3D libdir / 'libvirt' / 'storage-file' =20 +virt_storage_file_lib =3D static_library( + 'virt_storage_file', + [ + storage_file_sources, + ], + dependencies: [ + src_dep, + ], +) + +libvirt_libs +=3D virt_storage_file_lib + if conf.has('WITH_STORAGE') virt_modules +=3D { 'name': 'virt_storage_file_fs', @@ -36,3 +52,5 @@ if conf.has('WITH_STORAGE_GLUSTER') 'install_dir': storage_file_install_dir, } endif + +storage_file_inc_dir =3D include_directories('.') diff --git a/src/storage_file/storage_source.c b/src/storage_file/storage_s= ource.c new file mode 100644 index 0000000000..b5e0bc5040 --- /dev/null +++ b/src/storage_file/storage_source.c @@ -0,0 +1,2614 @@ +/* + * storage_source.c: file utility functions for FS storage backend + * + * Copyright (C) 2007-2017 Red Hat, Inc. + * Copyright (C) 2007-2008 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + */ + +#include + +#include +#include + +#include "internal.h" +#include "storage_source.h" +#include "viralloc.h" +#include "virerror.h" +#include "virfile.h" +#include "virhash.h" +#include "virjson.h" +#include "virlog.h" +#include "virobject.h" +#include "virstoragefile.h" +#include "virstoragefilebackend.h" +#include "virstoragefileprobe.h" +#include "virstring.h" +#include "viruri.h" +#include "virutil.h" + +#define VIR_FROM_THIS VIR_FROM_STORAGE + +VIR_LOG_INIT("storage_source"); + + +static virStorageSourcePtr +virStorageFileMetadataNew(const char *path, + int format) +{ + g_autoptr(virStorageSource) def =3D virStorageSourceNew(); + + def->format =3D format; + def->type =3D VIR_STORAGE_TYPE_FILE; + + def->path =3D g_strdup(path); + + return g_steal_pointer(&def); +} + + +/** + * virStorageFileGetMetadataFromBuf: + * @path: name of file, for error messages + * @buf: header bytes from @path + * @len: length of @buf + * @format: format of the storage file + * + * Extract metadata about the storage volume with the specified image form= at. + * If image format is VIR_STORAGE_FILE_AUTO, it will probe to automatically + * identify the format. Does not recurse. + * + * Callers are advised never to use VIR_STORAGE_FILE_AUTO as a format on a= file + * that might be raw if that file will then be passed to a guest, since a + * malicious guest can turn a raw file into any other non-raw format at wi= ll. + * + * If the 'backingStoreRawFormat' field of the returned structure is + * VIR_STORAGE_FILE_AUTO it indicates the image didn't specify an explicit + * format for its backing store. Callers are advised against probing for t= he + * backing store format in this case. + * + * Caller MUST free the result after use via virObjectUnref. + */ +virStorageSourcePtr +virStorageFileGetMetadataFromBuf(const char *path, + char *buf, + size_t len, + int format) +{ + virStorageSourcePtr ret =3D NULL; + + if (!(ret =3D virStorageFileMetadataNew(path, format))) + return NULL; + + if (virStorageFileProbeGetMetadata(ret, buf, len) < 0) { + virObjectUnref(ret); + return NULL; + } + + return ret; +} + + +/** + * virStorageFileGetMetadataFromFD: + * + * Extract metadata about the storage volume with the specified + * image format. If image format is VIR_STORAGE_FILE_AUTO, it + * will probe to automatically identify the format. Does not recurse. + * + * Callers are advised never to use VIR_STORAGE_FILE_AUTO as a + * format, since a malicious guest can turn a raw file into any + * other non-raw format at will. + * + * Caller MUST free the result after use via virObjectUnref. + */ +virStorageSourcePtr +virStorageFileGetMetadataFromFD(const char *path, + int fd, + int format) + +{ + ssize_t len =3D VIR_STORAGE_MAX_HEADER; + struct stat sb; + g_autofree char *buf =3D NULL; + g_autoptr(virStorageSource) meta =3D NULL; + + if (fstat(fd, &sb) < 0) { + virReportSystemError(errno, + _("cannot stat file '%s'"), path); + return NULL; + } + + if (!(meta =3D virStorageFileMetadataNew(path, format))) + return NULL; + + if (S_ISDIR(sb.st_mode)) { + /* No header to probe for directories, but also no backing file. J= ust + * update the metadata.*/ + meta->type =3D VIR_STORAGE_TYPE_DIR; + meta->format =3D VIR_STORAGE_FILE_DIR; + return g_steal_pointer(&meta); + } + + if (lseek(fd, 0, SEEK_SET) =3D=3D (off_t)-1) { + virReportSystemError(errno, _("cannot seek to start of '%s'"), met= a->path); + return NULL; + } + + if ((len =3D virFileReadHeaderFD(fd, len, &buf)) < 0) { + virReportSystemError(errno, _("cannot read header '%s'"), meta->pa= th); + return NULL; + } + + if (virStorageFileProbeGetMetadata(meta, buf, len) < 0) + return NULL; + + if (S_ISREG(sb.st_mode)) + meta->type =3D VIR_STORAGE_TYPE_FILE; + else if (S_ISBLK(sb.st_mode)) + meta->type =3D VIR_STORAGE_TYPE_BLOCK; + + return g_steal_pointer(&meta); +} + + +/* Given a @chain, look for the backing store @name that is a backing file + * of @startFrom (or any member of @chain if @startFrom is NULL) and return + * that location within the chain. @chain must always point to the top of + * the chain. Pass NULL for @name and 0 for @idx to find the base of the + * chain. Pass nonzero @idx to find the backing source according to its + * position in the backing chain. If @parent is not NULL, set *@parent to + * the preferred name of the parent (or to NULL if @name matches the start + * of the chain). Since the results point within @chain, they must not be + * independently freed. Reports an error and returns NULL if @name is not + * found. + */ +virStorageSourcePtr +virStorageFileChainLookup(virStorageSourcePtr chain, + virStorageSourcePtr startFrom, + const char *name, + unsigned int idx, + virStorageSourcePtr *parent) +{ + virStorageSourcePtr prev; + const char *start =3D chain->path; + bool nameIsFile =3D virStorageIsFile(name); + + if (!parent) + parent =3D &prev; + *parent =3D NULL; + + if (startFrom) { + while (virStorageSourceIsBacking(chain) && + chain !=3D startFrom->backingStore) + chain =3D chain->backingStore; + + *parent =3D startFrom; + } + + while (virStorageSourceIsBacking(chain)) { + if (!name && !idx) { + if (!virStorageSourceHasBacking(chain)) + break; + } else if (idx) { + VIR_DEBUG("%u: %s", chain->id, chain->path); + if (idx =3D=3D chain->id) + break; + } else { + if (STREQ_NULLABLE(name, chain->relPath) || + STREQ_NULLABLE(name, chain->path)) + break; + + if (nameIsFile && virStorageSourceIsLocalStorage(chain)) { + g_autofree char *parentDir =3D NULL; + int result; + + if (*parent && virStorageSourceIsLocalStorage(*parent)) + parentDir =3D g_path_get_dirname((*parent)->path); + else + parentDir =3D g_strdup("."); + + result =3D virFileRelLinkPointsTo(parentDir, name, + chain->path); + + if (result < 0) + goto error; + + if (result > 0) + break; + } + } + *parent =3D chain; + chain =3D chain->backingStore; + } + + if (!virStorageSourceIsBacking(chain)) + goto error; + + return chain; + + error: + if (idx) { + virReportError(VIR_ERR_INVALID_ARG, + _("could not find backing store index %u in chain " + "for '%s'"), + idx, NULLSTR(start)); + } else if (name) { + if (startFrom) + virReportError(VIR_ERR_INVALID_ARG, + _("could not find image '%s' beneath '%s' in " + "chain for '%s'"), name, NULLSTR(startFrom->p= ath), + NULLSTR(start)); + else + virReportError(VIR_ERR_INVALID_ARG, + _("could not find image '%s' in chain for '%s'"= ), + name, NULLSTR(start)); + } else { + virReportError(VIR_ERR_INVALID_ARG, + _("could not find base image in chain for '%s'"), + NULLSTR(start)); + } + *parent =3D NULL; + return NULL; +} + + +static virStorageSourcePtr +virStorageSourceNewFromBackingRelative(virStorageSourcePtr parent, + const char *rel) +{ + g_autofree char *dirname =3D NULL; + g_autoptr(virStorageSource) def =3D virStorageSourceNew(); + + /* store relative name */ + def->relPath =3D g_strdup(rel); + + dirname =3D g_path_get_dirname(parent->path); + + if (STRNEQ(dirname, "/")) { + def->path =3D g_strdup_printf("%s/%s", dirname, rel); + } else { + def->path =3D g_strdup_printf("/%s", rel); + } + + if (virStorageSourceGetActualType(parent) =3D=3D VIR_STORAGE_TYPE_NETW= ORK) { + def->type =3D VIR_STORAGE_TYPE_NETWORK; + + /* copy the host network part */ + def->protocol =3D parent->protocol; + if (parent->nhosts) { + if (!(def->hosts =3D virStorageNetHostDefCopy(parent->nhosts, + parent->hosts))) + return NULL; + + def->nhosts =3D parent->nhosts; + } + + def->volume =3D g_strdup(parent->volume); + } else { + /* set the type to _FILE, the caller shall update it to the actual= type */ + def->type =3D VIR_STORAGE_TYPE_FILE; + } + + return g_steal_pointer(&def); +} + + +static int +virStorageSourceParseBackingURI(virStorageSourcePtr src, + const char *uristr) +{ + g_autoptr(virURI) uri =3D NULL; + const char *path =3D NULL; + g_auto(GStrv) scheme =3D NULL; + + if (!(uri =3D virURIParse(uristr))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("failed to parse backing file location '%s'"), + uristr); + return -1; + } + + src->hosts =3D g_new0(virStorageNetHostDef, 1); + src->nhosts =3D 1; + + if (!(scheme =3D virStringSplit(uri->scheme, "+", 2))) + return -1; + + if (!scheme[0] || + (src->protocol =3D virStorageNetProtocolTypeFromString(scheme[0]))= < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("invalid backing protocol '%s'"), + NULLSTR(scheme[0])); + return -1; + } + + if (scheme[1] && + (src->hosts->transport =3D virStorageNetHostTransportTypeFromStrin= g(scheme[1])) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("invalid protocol transport type '%s'"), + scheme[1]); + return -1; + } + + if (uri->query) { + if (src->protocol =3D=3D VIR_STORAGE_NET_PROTOCOL_HTTP || + src->protocol =3D=3D VIR_STORAGE_NET_PROTOCOL_HTTPS) { + src->query =3D g_strdup(uri->query); + } else { + /* handle socket stored as a query */ + if (STRPREFIX(uri->query, "socket=3D")) + src->hosts->socket =3D g_strdup(STRSKIP(uri->query, "socke= t=3D")); + } + } + + /* uri->path is NULL if the URI does not contain slash after host: + * transport://host:port */ + if (uri->path) + path =3D uri->path; + else + path =3D ""; + + /* possibly skip the leading slash */ + if (path[0] =3D=3D '/') + path++; + + /* NBD allows empty export name (path) */ + if (src->protocol =3D=3D VIR_STORAGE_NET_PROTOCOL_NBD && + path[0] =3D=3D '\0') + path =3D NULL; + + src->path =3D g_strdup(path); + + if (src->protocol =3D=3D VIR_STORAGE_NET_PROTOCOL_GLUSTER) { + char *tmp; + + if (!src->path) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("missing volume name and path for gluster vol= ume")); + return -1; + } + + if (!(tmp =3D strchr(src->path, '/')) || + tmp =3D=3D src->path) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("missing volume name or file name in " + "gluster source path '%s'"), src->path); + return -1; + } + + src->volume =3D src->path; + + src->path =3D g_strdup(tmp + 1); + + tmp[0] =3D '\0'; + } + + src->hosts->port =3D uri->port; + + src->hosts->name =3D g_strdup(uri->server); + + /* Libvirt doesn't handle inline authentication. Make the caller aware= . */ + if (uri->user) + return 1; + + return 0; +} + + +static int +virStorageSourceRBDAddHost(virStorageSourcePtr src, + char *hostport) +{ + char *port; + size_t skip; + g_auto(GStrv) parts =3D NULL; + + if (VIR_EXPAND_N(src->hosts, src->nhosts, 1) < 0) + return -1; + + if ((port =3D strchr(hostport, ']'))) { + /* ipv6, strip brackets */ + hostport +=3D 1; + skip =3D 3; + } else { + port =3D strstr(hostport, "\\:"); + skip =3D 2; + } + + if (port) { + *port =3D '\0'; + port +=3D skip; + if (virStringParsePort(port, &src->hosts[src->nhosts - 1].port) < = 0) + goto error; + } + + parts =3D virStringSplit(hostport, "\\:", 0); + if (!parts) + goto error; + src->hosts[src->nhosts-1].name =3D virStringListJoin((const char **)pa= rts, ":"); + if (!src->hosts[src->nhosts-1].name) + goto error; + + src->hosts[src->nhosts-1].transport =3D VIR_STORAGE_NET_HOST_TRANS_TCP; + src->hosts[src->nhosts-1].socket =3D NULL; + + return 0; + + error: + VIR_FREE(src->hosts[src->nhosts-1].name); + return -1; +} + + +int +virStorageSourceParseRBDColonString(const char *rbdstr, + virStorageSourcePtr src) +{ + char *p, *e, *next; + g_autofree char *options =3D NULL; + g_autoptr(virStorageAuthDef) authdef =3D NULL; + + /* optionally skip the "rbd:" prefix if provided */ + if (STRPREFIX(rbdstr, "rbd:")) + rbdstr +=3D strlen("rbd:"); + + src->path =3D g_strdup(rbdstr); + + p =3D strchr(src->path, ':'); + if (p) { + options =3D g_strdup(p + 1); + *p =3D '\0'; + } + + /* snapshot name */ + if ((p =3D strchr(src->path, '@'))) { + src->snapshot =3D g_strdup(p + 1); + *p =3D '\0'; + } + + /* pool vs. image name */ + if ((p =3D strchr(src->path, '/'))) { + src->volume =3D g_steal_pointer(&src->path); + src->path =3D g_strdup(p + 1); + *p =3D '\0'; + } + + /* options */ + if (!options) + return 0; /* all done */ + + p =3D options; + while (*p) { + /* find : delimiter or end of string */ + for (e =3D p; *e && *e !=3D ':'; ++e) { + if (*e =3D=3D '\\') { + e++; + if (*e =3D=3D '\0') + break; + } + } + if (*e =3D=3D '\0') { + next =3D e; /* last kv pair */ + } else { + next =3D e + 1; + *e =3D '\0'; + } + + if (STRPREFIX(p, "id=3D")) { + /* formulate authdef for src->auth */ + if (src->auth) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("duplicate 'id' found in '%s'"), src->pat= h); + return -1; + } + + authdef =3D g_new0(virStorageAuthDef, 1); + + authdef->username =3D g_strdup(p + strlen("id=3D")); + + authdef->secrettype =3D g_strdup(virSecretUsageTypeToString(VI= R_SECRET_USAGE_TYPE_CEPH)); + src->auth =3D g_steal_pointer(&authdef); + + /* Cannot formulate a secretType (eg, usage or uuid) given + * what is provided. + */ + } + if (STRPREFIX(p, "mon_host=3D")) { + char *h, *sep; + + h =3D p + strlen("mon_host=3D"); + while (h < e) { + for (sep =3D h; sep < e; ++sep) { + if (*sep =3D=3D '\\' && (sep[1] =3D=3D ',' || + sep[1] =3D=3D ';' || + sep[1] =3D=3D ' ')) { + *sep =3D '\0'; + sep +=3D 2; + break; + } + } + + if (virStorageSourceRBDAddHost(src, h) < 0) + return -1; + + h =3D sep; + } + } + + if (STRPREFIX(p, "conf=3D")) + src->configFile =3D g_strdup(p + strlen("conf=3D")); + + p =3D next; + } + return 0; +} + + +static int +virStorageSourceParseNBDColonString(const char *nbdstr, + virStorageSourcePtr src) +{ + g_autofree char *nbd =3D g_strdup(nbdstr); + char *export_name; + char *host_spec; + char *unixpath; + char *port; + + src->hosts =3D g_new0(virStorageNetHostDef, 1); + src->nhosts =3D 1; + + /* We extract the parameters in a similar way qemu does it */ + + /* format: [] denotes optional sections, uppercase are variable strings + * nbd:unix:/PATH/TO/SOCKET[:exportname=3DEXPORTNAME] + * nbd:HOSTNAME:PORT[:exportname=3DEXPORTNAME] + */ + + /* first look for ':exportname=3D' and cut it off */ + if ((export_name =3D strstr(nbd, ":exportname=3D"))) { + src->path =3D g_strdup(export_name + strlen(":exportname=3D")); + export_name[0] =3D '\0'; + } + + /* Verify the prefix and contents. Note that we require a + * "host_spec" part to be present. */ + if (!(host_spec =3D STRSKIP(nbd, "nbd:")) || host_spec[0] =3D=3D '\0') + goto malformed; + + if ((unixpath =3D STRSKIP(host_spec, "unix:"))) { + src->hosts->transport =3D VIR_STORAGE_NET_HOST_TRANS_UNIX; + + if (unixpath[0] =3D=3D '\0') + goto malformed; + + src->hosts->socket =3D g_strdup(unixpath); + } else { + src->hosts->transport =3D VIR_STORAGE_NET_HOST_TRANS_TCP; + + if (host_spec[0] =3D=3D ':') { + /* no host given */ + goto malformed; + } else if (host_spec[0] =3D=3D '[') { + host_spec++; + /* IPv6 addr */ + if (!(port =3D strstr(host_spec, "]:"))) + goto malformed; + + port[0] =3D '\0'; + port +=3D 2; + + if (host_spec[0] =3D=3D '\0') + goto malformed; + } else { + if (!(port =3D strchr(host_spec, ':'))) + goto malformed; + + port[0] =3D '\0'; + port++; + } + + if (virStringParsePort(port, &src->hosts->port) < 0) + return -1; + + src->hosts->name =3D g_strdup(host_spec); + } + + return 0; + + malformed: + virReportError(VIR_ERR_INTERNAL_ERROR, + _("malformed nbd string '%s'"), nbdstr); + return -1; +} + + +static int +virStorageSourceParseBackingColon(virStorageSourcePtr src, + const char *path) +{ + const char *p; + g_autofree char *protocol =3D NULL; + + if (!(p =3D strchr(path, ':'))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("invalid backing protocol string '%s'"), + path); + return -1; + } + + protocol =3D g_strndup(path, p - path); + + if ((src->protocol =3D virStorageNetProtocolTypeFromString(protocol)) = < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("invalid backing protocol '%s'"), + protocol); + return -1; + } + + switch ((virStorageNetProtocol) src->protocol) { + case VIR_STORAGE_NET_PROTOCOL_NBD: + if (virStorageSourceParseNBDColonString(path, src) < 0) + return -1; + break; + + case VIR_STORAGE_NET_PROTOCOL_RBD: + if (virStorageSourceParseRBDColonString(path, src) < 0) + return -1; + break; + + case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG: + case VIR_STORAGE_NET_PROTOCOL_LAST: + case VIR_STORAGE_NET_PROTOCOL_NONE: + virReportError(VIR_ERR_INTERNAL_ERROR, + _("backing store parser is not implemented for prot= ocol %s"), + protocol); + return -1; + + case VIR_STORAGE_NET_PROTOCOL_HTTP: + case VIR_STORAGE_NET_PROTOCOL_HTTPS: + case VIR_STORAGE_NET_PROTOCOL_FTP: + case VIR_STORAGE_NET_PROTOCOL_FTPS: + case VIR_STORAGE_NET_PROTOCOL_TFTP: + case VIR_STORAGE_NET_PROTOCOL_ISCSI: + case VIR_STORAGE_NET_PROTOCOL_GLUSTER: + case VIR_STORAGE_NET_PROTOCOL_SSH: + case VIR_STORAGE_NET_PROTOCOL_VXHS: + case VIR_STORAGE_NET_PROTOCOL_NFS: + virReportError(VIR_ERR_INTERNAL_ERROR, + _("malformed backing store path for protocol %s"), + protocol); + return -1; + } + + return 0; +} + + +static int +virStorageSourceParseBackingJSONInternal(virStorageSourcePtr src, + virJSONValuePtr json, + const char *jsonstr, + bool allowformat); + + +static int +virStorageSourceParseBackingJSONPath(virStorageSourcePtr src, + virJSONValuePtr json, + const char *jsonstr G_GNUC_UNUSED, + int type) +{ + const char *path; + + if (!(path =3D virJSONValueObjectGetString(json, "filename"))) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("missing 'filename' field in JSON backing volume " + "definition")); + return -1; + } + + src->path =3D g_strdup(path); + + src->type =3D type; + return 0; +} + + +static int +virStorageSourceParseBackingJSONUriStr(virStorageSourcePtr src, + const char *uri, + int protocol) +{ + int rc; + + if ((rc =3D virStorageSourceParseBackingURI(src, uri)) < 0) + return -1; + + if (src->protocol !=3D protocol) { + virReportError(VIR_ERR_INVALID_ARG, + _("expected protocol '%s' but got '%s' in URI JSON = volume " + "definition"), + virStorageNetProtocolTypeToString(protocol), + virStorageNetProtocolTypeToString(src->protocol)); + return -1; + } + + return rc; +} + + +static int +virStorageSourceParseBackingJSONUriCookies(virStorageSourcePtr src, + virJSONValuePtr json, + const char *jsonstr) +{ + const char *cookiestr; + g_auto(GStrv) cookies =3D NULL; + size_t ncookies =3D 0; + size_t i; + + if (!virJSONValueObjectHasKey(json, "cookie")) + return 0; + + if (!(cookiestr =3D virJSONValueObjectGetString(json, "cookie"))) { + virReportError(VIR_ERR_INVALID_ARG, + _("wrong format of 'cookie' field in backing store = definition '%s'"), + jsonstr); + return -1; + } + + if (!(cookies =3D virStringSplitCount(cookiestr, ";", 0, &ncookies))) + return -1; + + src->cookies =3D g_new0(virStorageNetCookieDefPtr, ncookies); + src->ncookies =3D ncookies; + + for (i =3D 0; i < ncookies; i++) { + char *cookiename =3D cookies[i]; + char *cookievalue; + + virSkipSpaces((const char **) &cookiename); + + if (!(cookievalue =3D strchr(cookiename, '=3D'))) { + virReportError(VIR_ERR_INVALID_ARG, + _("malformed http cookie '%s' in backing store = definition '%s'"), + cookies[i], jsonstr); + return -1; + } + + *cookievalue =3D '\0'; + cookievalue++; + + src->cookies[i] =3D g_new0(virStorageNetCookieDef, 1); + src->cookies[i]->name =3D g_strdup(cookiename); + src->cookies[i]->value =3D g_strdup(cookievalue); + } + + return 0; +} + + +static int +virStorageSourceParseBackingJSONUri(virStorageSourcePtr src, + virJSONValuePtr json, + const char *jsonstr, + int protocol) +{ + const char *uri; + + if (!(uri =3D virJSONValueObjectGetString(json, "url"))) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("missing 'url' in JSON backing volume definition"= )); + return -1; + } + + if (protocol =3D=3D VIR_STORAGE_NET_PROTOCOL_HTTPS || + protocol =3D=3D VIR_STORAGE_NET_PROTOCOL_FTPS) { + if (virJSONValueObjectHasKey(json, "sslverify")) { + const char *tmpstr; + bool tmp; + + /* libguestfs still uses undocumented legacy value of 'off' */ + if ((tmpstr =3D virJSONValueObjectGetString(json, "sslverify")= ) && + STREQ(tmpstr, "off")) { + src->sslverify =3D VIR_TRISTATE_BOOL_NO; + } else { + if (virJSONValueObjectGetBoolean(json, "sslverify", &tmp) = < 0) { + virReportError(VIR_ERR_INVALID_ARG, + _("malformed 'sslverify' field in backi= ng store definition '%s'"), + jsonstr); + return -1; + } + + src->sslverify =3D virTristateBoolFromBool(tmp); + } + } + } + + if (protocol =3D=3D VIR_STORAGE_NET_PROTOCOL_HTTPS || + protocol =3D=3D VIR_STORAGE_NET_PROTOCOL_HTTP) { + if (virStorageSourceParseBackingJSONUriCookies(src, json, jsonstr)= < 0) + return -1; + } + + if (virJSONValueObjectHasKey(json, "readahead") && + virJSONValueObjectGetNumberUlong(json, "readahead", &src->readahea= d) < 0) { + virReportError(VIR_ERR_INVALID_ARG, + _("malformed 'readahead' field in backing store def= inition '%s'"), + jsonstr); + return -1; + } + + if (virJSONValueObjectHasKey(json, "timeout") && + virJSONValueObjectGetNumberUlong(json, "timeout", &src->timeout) <= 0) { + virReportError(VIR_ERR_INVALID_ARG, + _("malformed 'timeout' field in backing store defin= ition '%s'"), + jsonstr); + return -1; + } + + return virStorageSourceParseBackingJSONUriStr(src, uri, protocol); +} + + +static int +virStorageSourceParseBackingJSONInetSocketAddress(virStorageNetHostDefPtr = host, + virJSONValuePtr json) +{ + const char *hostname; + const char *port; + + if (!json) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("missing remote server specification in JSON " + "backing volume definition")); + return -1; + } + + hostname =3D virJSONValueObjectGetString(json, "host"); + port =3D virJSONValueObjectGetString(json, "port"); + + if (!hostname) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("missing hostname for tcp backing server in " + "JSON backing volume definition")); + return -1; + } + + host->transport =3D VIR_STORAGE_NET_HOST_TRANS_TCP; + host->name =3D g_strdup(hostname); + + if (virStringParsePort(port, &host->port) < 0) + return -1; + + return 0; +} + + +static int +virStorageSourceParseBackingJSONSocketAddress(virStorageNetHostDefPtr host, + virJSONValuePtr json) +{ + const char *type; + const char *socket; + + if (!json) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("missing remote server specification in JSON " + "backing volume definition")); + return -1; + } + + if (!(type =3D virJSONValueObjectGetString(json, "type"))) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("missing socket address type in " + "JSON backing volume definition")); + return -1; + } + + if (STREQ(type, "tcp") || STREQ(type, "inet")) { + return virStorageSourceParseBackingJSONInetSocketAddress(host, jso= n); + + } else if (STREQ(type, "unix")) { + host->transport =3D VIR_STORAGE_NET_HOST_TRANS_UNIX; + + socket =3D virJSONValueObjectGetString(json, "path"); + + /* check for old spelling for gluster protocol */ + if (!socket) + socket =3D virJSONValueObjectGetString(json, "socket"); + + if (!socket) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("missing socket path for udp backing server i= n " + "JSON backing volume definition")); + return -1; + } + + host->socket =3D g_strdup(socket); + } else { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("backing store protocol '%s' is not yet supported= "), + type); + return -1; + } + + return 0; +} + + +static int +virStorageSourceParseBackingJSONGluster(virStorageSourcePtr src, + virJSONValuePtr json, + const char *jsonstr G_GNUC_UNUSED, + int opaque G_GNUC_UNUSED) +{ + const char *uri =3D virJSONValueObjectGetString(json, "filename"); + const char *volume =3D virJSONValueObjectGetString(json, "volume"); + const char *path =3D virJSONValueObjectGetString(json, "path"); + virJSONValuePtr server =3D virJSONValueObjectGetArray(json, "server"); + size_t nservers; + size_t i; + + /* legacy URI based syntax passed via 'filename' option */ + if (uri) + return virStorageSourceParseBackingJSONUriStr(src, uri, + VIR_STORAGE_NET_PROT= OCOL_GLUSTER); + + if (!volume || !path || !server) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("missing 'volume', 'path' or 'server' attribute i= n " + "JSON backing definition for gluster volume")); + return -1; + } + + src->type =3D VIR_STORAGE_TYPE_NETWORK; + src->protocol =3D VIR_STORAGE_NET_PROTOCOL_GLUSTER; + + src->volume =3D g_strdup(volume); + src->path =3D g_strdup(path); + + nservers =3D virJSONValueArraySize(server); + if (nservers =3D=3D 0) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("at least 1 server is necessary in " + "JSON backing definition for gluster volume")); + + return -1; + } + + src->hosts =3D g_new0(virStorageNetHostDef, nservers); + src->nhosts =3D nservers; + + for (i =3D 0; i < nservers; i++) { + if (virStorageSourceParseBackingJSONSocketAddress(src->hosts + i, + virJSONValueArra= yGet(server, i)) < 0) + return -1; + } + + return 0; +} + + +static int +virStorageSourceParseBackingJSONiSCSI(virStorageSourcePtr src, + virJSONValuePtr json, + const char *jsonstr G_GNUC_UNUSED, + int opaque G_GNUC_UNUSED) +{ + const char *transport =3D virJSONValueObjectGetString(json, "transport= "); + const char *portal =3D virJSONValueObjectGetString(json, "portal"); + const char *target =3D virJSONValueObjectGetString(json, "target"); + const char *lun =3D virJSONValueObjectGetStringOrNumber(json, "lun"); + const char *uri; + char *port; + + /* legacy URI based syntax passed via 'filename' option */ + if ((uri =3D virJSONValueObjectGetString(json, "filename"))) + return virStorageSourceParseBackingJSONUriStr(src, uri, + VIR_STORAGE_NET_PROT= OCOL_ISCSI); + + src->type =3D VIR_STORAGE_TYPE_NETWORK; + src->protocol =3D VIR_STORAGE_NET_PROTOCOL_ISCSI; + + if (!lun) + lun =3D "0"; + + src->hosts =3D g_new0(virStorageNetHostDef, 1); + src->nhosts =3D 1; + + if (STRNEQ_NULLABLE(transport, "tcp")) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("only TCP transport is supported for iSCSI volume= s")); + return -1; + } + + src->hosts->transport =3D VIR_STORAGE_NET_HOST_TRANS_TCP; + + if (!portal) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("missing 'portal' address in iSCSI backing defini= tion")); + return -1; + } + + if (!target) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("missing 'target' in iSCSI backing definition")); + return -1; + } + + src->hosts->name =3D g_strdup(portal); + + if ((port =3D strrchr(src->hosts->name, ':')) && + !strchr(port, ']')) { + if (virStringParsePort(port + 1, &src->hosts->port) < 0) + return -1; + + *port =3D '\0'; + } + + src->path =3D g_strdup_printf("%s/%s", target, lun); + + /* Libvirt doesn't handle inline authentication. Make the caller aware= . */ + if (virJSONValueObjectGetString(json, "user") || + virJSONValueObjectGetString(json, "password")) + return 1; + + return 0; +} + + +static int +virStorageSourceParseBackingJSONNbd(virStorageSourcePtr src, + virJSONValuePtr json, + const char *jsonstr G_GNUC_UNUSED, + int opaque G_GNUC_UNUSED) +{ + const char *path =3D virJSONValueObjectGetString(json, "path"); + const char *host =3D virJSONValueObjectGetString(json, "host"); + const char *port =3D virJSONValueObjectGetString(json, "port"); + const char *export =3D virJSONValueObjectGetString(json, "export"); + virJSONValuePtr server =3D virJSONValueObjectGetObject(json, "server"); + + if (!path && !host && !server) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("missing host specification of NBD server in JSON= " + "backing volume definition")); + return -1; + } + + src->type =3D VIR_STORAGE_TYPE_NETWORK; + src->protocol =3D VIR_STORAGE_NET_PROTOCOL_NBD; + + src->path =3D g_strdup(export); + + src->hosts =3D g_new0(virStorageNetHostDef, 1); + src->nhosts =3D 1; + + if (server) { + if (virStorageSourceParseBackingJSONSocketAddress(src->hosts, serv= er) < 0) + return -1; + } else { + if (path) { + src->hosts[0].transport =3D VIR_STORAGE_NET_HOST_TRANS_UNIX; + src->hosts[0].socket =3D g_strdup(path); + } else { + src->hosts[0].transport =3D VIR_STORAGE_NET_HOST_TRANS_TCP; + src->hosts[0].name =3D g_strdup(host); + + if (virStringParsePort(port, &src->hosts[0].port) < 0) + return -1; + } + } + + return 0; +} + + +static int +virStorageSourceParseBackingJSONSheepdog(virStorageSourcePtr src, + virJSONValuePtr json, + const char *jsonstr G_GNUC_UNUSED, + int opaque G_GNUC_UNUSED) +{ + const char *filename; + const char *vdi =3D virJSONValueObjectGetString(json, "vdi"); + virJSONValuePtr server =3D virJSONValueObjectGetObject(json, "server"); + + /* legacy URI based syntax passed via 'filename' option */ + if ((filename =3D virJSONValueObjectGetString(json, "filename"))) { + if (strstr(filename, "://")) + return virStorageSourceParseBackingJSONUriStr(src, filename, + VIR_STORAGE_NET_= PROTOCOL_SHEEPDOG); + + /* libvirt doesn't implement a parser for the legacy non-URI synta= x */ + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("missing sheepdog URI in JSON backing volume defi= nition")); + return -1; + } + + src->type =3D VIR_STORAGE_TYPE_NETWORK; + src->protocol =3D VIR_STORAGE_NET_PROTOCOL_SHEEPDOG; + + if (!vdi) { + virReportError(VIR_ERR_INVALID_ARG, "%s", _("missing sheepdog vdi = name")); + return -1; + } + + src->path =3D g_strdup(vdi); + + src->hosts =3D g_new0(virStorageNetHostDef, 1); + src->nhosts =3D 1; + + if (virStorageSourceParseBackingJSONSocketAddress(src->hosts, server) = < 0) + return -1; + + return 0; +} + + +static int +virStorageSourceParseBackingJSONSSH(virStorageSourcePtr src, + virJSONValuePtr json, + const char *jsonstr G_GNUC_UNUSED, + int opaque G_GNUC_UNUSED) +{ + const char *path =3D virJSONValueObjectGetString(json, "path"); + const char *host =3D virJSONValueObjectGetString(json, "host"); + const char *port =3D virJSONValueObjectGetString(json, "port"); + const char *user =3D virJSONValueObjectGetString(json, "user"); + const char *host_key_check =3D virJSONValueObjectGetString(json, "host= _key_check"); + virJSONValuePtr server =3D virJSONValueObjectGetObject(json, "server"); + + if (!(host || server) || !path) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("missing host/server or path of SSH JSON backing " + "volume definition")); + return -1; + } + + src->type =3D VIR_STORAGE_TYPE_NETWORK; + src->protocol =3D VIR_STORAGE_NET_PROTOCOL_SSH; + + src->path =3D g_strdup(path); + + src->hosts =3D g_new0(virStorageNetHostDef, 1); + src->nhosts =3D 1; + + if (server) { + if (virStorageSourceParseBackingJSONInetSocketAddress(src->hosts, + server) < 0) + return -1; + } else { + src->hosts[0].transport =3D VIR_STORAGE_NET_HOST_TRANS_TCP; + src->hosts[0].name =3D g_strdup(host); + + if (virStringParsePort(port, &src->hosts[0].port) < 0) + return -1; + } + + /* these two are parsed just to be passed back as we don't model them = yet */ + src->ssh_user =3D g_strdup(user); + if (STREQ_NULLABLE(host_key_check, "no")) + src->ssh_host_key_check_disabled =3D true; + + return 0; +} + + +static int +virStorageSourceParseBackingJSONRBD(virStorageSourcePtr src, + virJSONValuePtr json, + const char *jsonstr G_GNUC_UNUSED, + int opaque G_GNUC_UNUSED) +{ + const char *filename; + const char *pool =3D virJSONValueObjectGetString(json, "pool"); + const char *image =3D virJSONValueObjectGetString(json, "image"); + const char *conf =3D virJSONValueObjectGetString(json, "conf"); + const char *snapshot =3D virJSONValueObjectGetString(json, "snapshot"); + virJSONValuePtr servers =3D virJSONValueObjectGetArray(json, "server"); + size_t nservers; + size_t i; + + src->type =3D VIR_STORAGE_TYPE_NETWORK; + src->protocol =3D VIR_STORAGE_NET_PROTOCOL_RBD; + + /* legacy syntax passed via 'filename' option */ + if ((filename =3D virJSONValueObjectGetString(json, "filename"))) + return virStorageSourceParseRBDColonString(filename, src); + + if (!pool || !image) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("missing pool or image name in ceph backing volum= e " + "JSON specification")); + return -1; + } + + src->volume =3D g_strdup(pool); + src->path =3D g_strdup(image); + src->snapshot =3D g_strdup(snapshot); + src->configFile =3D g_strdup(conf); + + if (servers) { + nservers =3D virJSONValueArraySize(servers); + + src->hosts =3D g_new0(virStorageNetHostDef, nservers); + src->nhosts =3D nservers; + + for (i =3D 0; i < nservers; i++) { + if (virStorageSourceParseBackingJSONInetSocketAddress(src->hos= ts + i, + virJSONV= alueArrayGet(servers, i)) < 0) + return -1; + } + } + + return 0; +} + +static int +virStorageSourceParseBackingJSONRaw(virStorageSourcePtr src, + virJSONValuePtr json, + const char *jsonstr, + int opaque G_GNUC_UNUSED) +{ + bool has_offset =3D virJSONValueObjectHasKey(json, "offset"); + bool has_size =3D virJSONValueObjectHasKey(json, "size"); + virJSONValuePtr file; + + if (has_offset || has_size) { + src->sliceStorage =3D g_new0(virStorageSourceSlice, 1); + + if (has_offset && + virJSONValueObjectGetNumberUlong(json, "offset", &src->sliceSt= orage->offset) < 0) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("malformed 'offset' property of 'raw' driver"= )); + return -1; + } + + if (has_size && + virJSONValueObjectGetNumberUlong(json, "size", &src->sliceStor= age->size) < 0) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("malformed 'size' property of 'raw' driver")); + return -1; + } + } + + /* 'raw' is a format driver so it can have protocol driver children */ + if (!(file =3D virJSONValueObjectGetObject(json, "file"))) { + virReportError(VIR_ERR_INVALID_ARG, + _("JSON backing volume definition '%s' lacks 'file'= object"), + jsonstr); + return -1; + } + + return virStorageSourceParseBackingJSONInternal(src, file, jsonstr, fa= lse); +} + + +static int +virStorageSourceParseBackingJSONVxHS(virStorageSourcePtr src, + virJSONValuePtr json, + const char *jsonstr G_GNUC_UNUSED, + int opaque G_GNUC_UNUSED) +{ + const char *vdisk_id =3D virJSONValueObjectGetString(json, "vdisk-id"); + virJSONValuePtr server =3D virJSONValueObjectGetObject(json, "server"); + + if (!vdisk_id || !server) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("missing 'vdisk-id' or 'server' attribute in " + "JSON backing definition for VxHS volume")); + return -1; + } + + src->type =3D VIR_STORAGE_TYPE_NETWORK; + src->protocol =3D VIR_STORAGE_NET_PROTOCOL_VXHS; + + src->path =3D g_strdup(vdisk_id); + + src->hosts =3D g_new0(virStorageNetHostDef, 1); + src->nhosts =3D 1; + + if (virStorageSourceParseBackingJSONInetSocketAddress(src->hosts, + server) < 0) + return -1; + + return 0; +} + + +static int +virStorageSourceParseBackingJSONNFS(virStorageSourcePtr src, + virJSONValuePtr json, + const char *jsonstr G_GNUC_UNUSED, + int opaque G_GNUC_UNUSED) +{ + virJSONValuePtr server =3D virJSONValueObjectGetObject(json, "server"); + int uidStore =3D -1; + int gidStore =3D -1; + int gotUID =3D virJSONValueObjectGetNumberInt(json, "user", &uidStore); + int gotGID =3D virJSONValueObjectGetNumberInt(json, "group", &gidStore= ); + + if (!server) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("missing 'server' attribute in JSON backing defin= ition for NFS volume")); + return -1; + } + + if (gotUID < 0 || gotGID < 0) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("missing 'user' or 'group' attribute in JSON back= ing definition for NFS volume")); + return -1; + } + + src->path =3D g_strdup(virJSONValueObjectGetString(json, "path")); + if (!src->path) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("missing 'path' attribute in JSON backing definit= ion for NFS volume")); + return -1; + } + + src->nfs_user =3D g_strdup_printf("+%d", uidStore); + src->nfs_group =3D g_strdup_printf("+%d", gidStore); + + src->type =3D VIR_STORAGE_TYPE_NETWORK; + src->protocol =3D VIR_STORAGE_NET_PROTOCOL_NFS; + + src->hosts =3D g_new0(virStorageNetHostDef, 1); + src->nhosts =3D 1; + + if (virStorageSourceParseBackingJSONInetSocketAddress(src->hosts, + server) < 0) + return -1; + + return 0; +} + + +static int +virStorageSourceParseBackingJSONNVMe(virStorageSourcePtr src, + virJSONValuePtr json, + const char *jsonstr G_GNUC_UNUSED, + int opaque G_GNUC_UNUSED) +{ + g_autoptr(virStorageSourceNVMeDef) nvme =3D g_new0(virStorageSourceNVM= eDef, 1); + const char *device =3D virJSONValueObjectGetString(json, "device"); + + if (!device || virPCIDeviceAddressParse((char *) device, &nvme->pciAdd= r) < 0) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("missing or malformed 'device' field of 'nvme' st= orage")); + return -1; + } + + if (virJSONValueObjectGetNumberUlong(json, "namespace", &nvme->namespc= ) < 0 || + nvme->namespc =3D=3D 0) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("missing or malformed 'namespace' field of 'nvme'= storage")); + return -1; + } + + src->type =3D VIR_STORAGE_TYPE_NVME; + src->nvme =3D g_steal_pointer(&nvme); + + return 0; +} + + +struct virStorageSourceJSONDriverParser { + const char *drvname; + bool formatdriver; + /** + * The callback gets a pre-allocated storage source @src and the JSON + * object to parse. The callback shall return -1 on error and report e= rror + * 0 on success and 1 in cases when the configuration itself is valid,= but + * can't be converted to libvirt's configuration (e.g. inline authenti= cation + * credentials are present). + */ + int (*func)(virStorageSourcePtr src, virJSONValuePtr json, const char = *jsonstr, int opaque); + int opaque; +}; + +static const struct virStorageSourceJSONDriverParser jsonParsers[] =3D { + {"file", false, virStorageSourceParseBackingJSONPath, VIR_STORAGE_TYPE= _FILE}, + {"host_device", false, virStorageSourceParseBackingJSONPath, VIR_STORA= GE_TYPE_BLOCK}, + {"host_cdrom", false, virStorageSourceParseBackingJSONPath, VIR_STORAG= E_TYPE_BLOCK}, + {"http", false, virStorageSourceParseBackingJSONUri, VIR_STORAGE_NET_P= ROTOCOL_HTTP}, + {"https", false, virStorageSourceParseBackingJSONUri, VIR_STORAGE_NET_= PROTOCOL_HTTPS}, + {"ftp", false, virStorageSourceParseBackingJSONUri, VIR_STORAGE_NET_PR= OTOCOL_FTP}, + {"ftps", false, virStorageSourceParseBackingJSONUri, VIR_STORAGE_NET_P= ROTOCOL_FTPS}, + {"tftp", false, virStorageSourceParseBackingJSONUri, VIR_STORAGE_NET_P= ROTOCOL_TFTP}, + {"gluster", false, virStorageSourceParseBackingJSONGluster, 0}, + {"iscsi", false, virStorageSourceParseBackingJSONiSCSI, 0}, + {"nbd", false, virStorageSourceParseBackingJSONNbd, 0}, + {"sheepdog", false, virStorageSourceParseBackingJSONSheepdog, 0}, + {"ssh", false, virStorageSourceParseBackingJSONSSH, 0}, + {"rbd", false, virStorageSourceParseBackingJSONRBD, 0}, + {"raw", true, virStorageSourceParseBackingJSONRaw, 0}, + {"nfs", false, virStorageSourceParseBackingJSONNFS, 0}, + {"vxhs", false, virStorageSourceParseBackingJSONVxHS, 0}, + {"nvme", false, virStorageSourceParseBackingJSONNVMe, 0}, +}; + + + +static int +virStorageSourceParseBackingJSONInternal(virStorageSourcePtr src, + virJSONValuePtr json, + const char *jsonstr, + bool allowformat) +{ + const char *drvname; + size_t i; + + if (!(drvname =3D virJSONValueObjectGetString(json, "driver"))) { + virReportError(VIR_ERR_INVALID_ARG, + _("JSON backing volume definition '%s' lacks driver= name"), + jsonstr); + return -1; + } + + for (i =3D 0; i < G_N_ELEMENTS(jsonParsers); i++) { + if (STRNEQ(drvname, jsonParsers[i].drvname)) + continue; + + if (jsonParsers[i].formatdriver && !allowformat) { + virReportError(VIR_ERR_INVALID_ARG, + _("JSON backing volume definition '%s' must not= have nested format drivers"), + jsonstr); + return -1; + } + + return jsonParsers[i].func(src, json, jsonstr, jsonParsers[i].opaq= ue); + } + + virReportError(VIR_ERR_INTERNAL_ERROR, + _("missing parser implementation for JSON backing volum= e " + "driver '%s'"), drvname); + return -1; +} + + +static int +virStorageSourceParseBackingJSON(virStorageSourcePtr src, + const char *json) +{ + g_autoptr(virJSONValue) root =3D NULL; + g_autoptr(virJSONValue) deflattened =3D NULL; + virJSONValuePtr file =3D NULL; + + if (!(root =3D virJSONValueFromString(json))) + return -1; + + if (!(deflattened =3D virJSONValueObjectDeflatten(root))) + return -1; + + /* There are 2 possible syntaxes: + * 1) json:{"file":{"driver":...}} + * 2) json:{"driver":...} + * Remove the 'file' wrapper object in case 1. + */ + if (!virJSONValueObjectHasKey(deflattened, "driver")) + file =3D virJSONValueObjectGetObject(deflattened, "file"); + + if (!file) + file =3D deflattened; + + return virStorageSourceParseBackingJSONInternal(src, file, json, true); +} + + +/** + * virStorageSourceNewFromBackingAbsolute + * @path: string representing absolute location of a storage source + * @src: filled with virStorageSource object representing @path + * + * Returns 0 on success, 1 if we could parse all location data but @path + * specified other data unrepresentable by libvirt (e.g. inline authentica= tion). + * In both cases @src is filled. On error -1 is returned @src is NULL and = an + * error is reported. + */ +int +virStorageSourceNewFromBackingAbsolute(const char *path, + virStorageSourcePtr *src) +{ + const char *json; + const char *dirpath; + int rc =3D 0; + g_autoptr(virStorageSource) def =3D virStorageSourceNew(); + + *src =3D NULL; + + if (virStorageIsFile(path)) { + def->type =3D VIR_STORAGE_TYPE_FILE; + + def->path =3D g_strdup(path); + } else { + if ((dirpath =3D STRSKIP(path, "fat:"))) { + def->type =3D VIR_STORAGE_TYPE_DIR; + def->format =3D VIR_STORAGE_FILE_FAT; + def->path =3D g_strdup(dirpath); + *src =3D g_steal_pointer(&def); + return 0; + } + + def->type =3D VIR_STORAGE_TYPE_NETWORK; + + VIR_DEBUG("parsing backing store string: '%s'", path); + + /* handle URI formatted backing stores */ + if ((json =3D STRSKIP(path, "json:"))) + rc =3D virStorageSourceParseBackingJSON(def, json); + else if (strstr(path, "://")) + rc =3D virStorageSourceParseBackingURI(def, path); + else + rc =3D virStorageSourceParseBackingColon(def, path); + + if (rc < 0) + return -1; + + virStorageSourceNetworkAssignDefaultPorts(def); + + /* Some of the legacy parsers parse authentication data since they= are + * also used in other places. For backing store detection the + * authentication data would be invalid anyways, so we clear it */ + if (def->auth) { + virStorageAuthDefFree(def->auth); + def->auth =3D NULL; + } + } + + *src =3D g_steal_pointer(&def); + return rc; +} + + +/** + * virStorageSourceNewFromChild: + * @parent: storage source parent + * @child: returned child/backing store definition + * @parentRaw: raw child string (backingStoreRaw) + * + * Creates a storage source which describes the backing image of @parent a= nd + * fills it into @backing depending on the passed parentRaw (backingStoreR= aw) + * and other data. Note that for local storage this function accesses the = file + * to update the actual type of the child store. + * + * Returns 0 on success, 1 if we could parse all location data but the chi= ld + * store specification contained other data unrepresentable by libvirt (e.= g. + * inline authentication). + * In both cases @src is filled. On error -1 is returned @src is NULL and = an + * error is reported. + */ +static int +virStorageSourceNewFromChild(virStorageSourcePtr parent, + const char *parentRaw, + virStorageSourcePtr *child) +{ + struct stat st; + g_autoptr(virStorageSource) def =3D NULL; + int rc =3D 0; + + *child =3D NULL; + + if (virStorageIsRelative(parentRaw)) { + if (!(def =3D virStorageSourceNewFromBackingRelative(parent, paren= tRaw))) + return -1; + } else { + if ((rc =3D virStorageSourceNewFromBackingAbsolute(parentRaw, &def= )) < 0) + return -1; + } + + /* possibly update local type */ + if (def->type =3D=3D VIR_STORAGE_TYPE_FILE) { + if (stat(def->path, &st) =3D=3D 0) { + if (S_ISDIR(st.st_mode)) { + def->type =3D VIR_STORAGE_TYPE_DIR; + def->format =3D VIR_STORAGE_FILE_DIR; + } else if (S_ISBLK(st.st_mode)) { + def->type =3D VIR_STORAGE_TYPE_BLOCK; + } + } + } + + /* copy parent's labelling and other top level stuff */ + if (virStorageSourceInitChainElement(def, parent, true) < 0) + return -1; + + def->detected =3D true; + + *child =3D g_steal_pointer(&def); + return rc; +} + + +int +virStorageSourceNewFromBacking(virStorageSourcePtr parent, + virStorageSourcePtr *backing) +{ + int rc; + + if ((rc =3D virStorageSourceNewFromChild(parent, + parent->backingStoreRaw, + backing)) < 0) + return rc; + + (*backing)->format =3D parent->backingStoreRawFormat; + (*backing)->readonly =3D true; + return rc; +} + + +/** + * @src: disk source definition structure + * @fd: file descriptor + * @sb: stat buffer + * + * Updates src->physical depending on the actual type of storage being use= d. + * To be called for domain storage source reporting as the volume code does + * not set/use the 'type' field for the voldef->source.target + * + * Returns 0 on success, -1 on error. No libvirt errors are reported. + */ +int +virStorageSourceUpdatePhysicalSize(virStorageSourcePtr src, + int fd, + struct stat const *sb) +{ + off_t end; + virStorageType actual_type =3D virStorageSourceGetActualType(src); + + switch (actual_type) { + case VIR_STORAGE_TYPE_FILE: + case VIR_STORAGE_TYPE_NETWORK: + src->physical =3D sb->st_size; + break; + + case VIR_STORAGE_TYPE_BLOCK: + if ((end =3D lseek(fd, 0, SEEK_END)) =3D=3D (off_t) -1) + return -1; + + src->physical =3D end; + break; + + case VIR_STORAGE_TYPE_DIR: + src->physical =3D 0; + break; + + /* We shouldn't get VOLUME, but the switch requires all cases */ + case VIR_STORAGE_TYPE_VOLUME: + case VIR_STORAGE_TYPE_NVME: + case VIR_STORAGE_TYPE_NONE: + case VIR_STORAGE_TYPE_LAST: + return -1; + } + + return 0; +} + + +/** + * @src: disk source definition structure + * @fd: file descriptor + * @sb: stat buffer + * + * Update the capacity, allocation, physical values for the storage @src + * Shared between the domain storage source for an inactive domain and the + * voldef source target as the result is not affected by the 'type' field. + * + * Returns 0 on success, -1 on error. + */ +int +virStorageSourceUpdateBackingSizes(virStorageSourcePtr src, + int fd, + struct stat const *sb) +{ + /* Get info for normal formats */ + if (S_ISREG(sb->st_mode) || fd =3D=3D -1) { +#ifndef WIN32 + src->allocation =3D (unsigned long long)sb->st_blocks * + (unsigned long long)DEV_BSIZE; +#else + src->allocation =3D sb->st_size; +#endif + /* Regular files may be sparse, so logical size (capacity) is not = same + * as actual allocation above + */ + src->capacity =3D sb->st_size; + + /* Allocation tracks when the file is sparse, physical is the + * last offset of the file. */ + src->physical =3D sb->st_size; + } else if (S_ISDIR(sb->st_mode)) { + src->allocation =3D 0; + src->capacity =3D 0; + src->physical =3D 0; + } else if (fd >=3D 0) { + off_t end; + + /* XXX this is POSIX compliant, but doesn't work for CHAR files, + * only BLOCK. There is a Linux specific ioctl() for getting + * size of both CHAR / BLOCK devices we should check for in + * configure + * + * NB. Because we configure with AC_SYS_LARGEFILE, off_t + * should be 64 bits on all platforms. For block devices, we + * have to seek (safe even if someone else is writing) to + * determine physical size, and assume that allocation is the + * same as physical (but can refine that assumption later if + * qemu is still running). + */ + if ((end =3D lseek(fd, 0, SEEK_END)) =3D=3D (off_t)-1) { + virReportSystemError(errno, + _("failed to seek to end of %s"), src->pa= th); + return -1; + } + src->physical =3D end; + src->allocation =3D end; + src->capacity =3D end; + } + + return 0; +} + + +/** + * @src: disk source definition structure + * @buf: buffer to the storage file header + * @len: length of the storage file header + * + * Update the storage @src capacity. + * + * Returns 0 on success, -1 on error. + */ +int +virStorageSourceUpdateCapacity(virStorageSourcePtr src, + char *buf, + ssize_t len) +{ + int format =3D src->format; + g_autoptr(virStorageSource) meta =3D NULL; + + /* Raw files: capacity is physical size. For all other files: if + * the metadata has a capacity, use that, otherwise fall back to + * physical size. */ + if (format =3D=3D VIR_STORAGE_FILE_NONE) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("no disk format for %s was specified"), + src->path); + return -1; + } + + if (format =3D=3D VIR_STORAGE_FILE_RAW && !src->encryption) { + src->capacity =3D src->physical; + } else if ((meta =3D virStorageFileGetMetadataFromBuf(src->path, buf, + len, format))) { + src->capacity =3D meta->capacity ? meta->capacity : src->physical; + if (src->encryption && meta->encryption) + src->encryption->payload_offset =3D meta->encryption->payload_= offset; + } else { + return -1; + } + + if (src->encryption && src->encryption->payload_offset !=3D -1) + src->capacity -=3D src->encryption->payload_offset * 512; + + return 0; +} + + +/** + * virStorageFileRemoveLastPathComponent: + * + * @path: Path string to remove the last component from + * + * Removes the last path component of a path. This function is designed to= be + * called on file paths only (no trailing slashes in @path). Caller is + * responsible to free the returned string. + */ +static char * +virStorageFileRemoveLastPathComponent(const char *path) +{ + char *ret; + + ret =3D g_strdup(NULLSTR_EMPTY(path)); + + virFileRemoveLastComponent(ret); + + return ret; +} + + +/* + * virStorageFileGetRelativeBackingPath: + * + * Resolve relative path to be written to the overlay of @top image when + * collapsing the backing chain between @top and @base. + * + * Returns 0 on success; 1 if backing chain isn't relative and -1 on error. + */ +int +virStorageFileGetRelativeBackingPath(virStorageSourcePtr top, + virStorageSourcePtr base, + char **relpath) +{ + virStorageSourcePtr next; + g_autofree char *tmp =3D NULL; + g_autofree char *path =3D NULL; + + *relpath =3D NULL; + + for (next =3D top; virStorageSourceIsBacking(next); next =3D next->bac= kingStore) { + if (!next->relPath) + return 1; + + if (!(tmp =3D virStorageFileRemoveLastPathComponent(path))) + return -1; + + VIR_FREE(path); + + path =3D g_strdup_printf("%s%s", tmp, next->relPath); + + VIR_FREE(tmp); + + if (next =3D=3D base) + break; + } + + if (next !=3D base) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("failed to resolve relative backing name: " + "base image is not in backing chain")); + return -1; + } + + *relpath =3D g_steal_pointer(&path); + return 0; +} + + +static bool +virStorageFileIsInitialized(const virStorageSource *src) +{ + return src && src->drv; +} + + +/** + * virStorageFileGetBackendForSupportCheck: + * @src: storage source to check support for + * @backend: pointer to the storage backend for @src if it's supported + * + * Returns 0 if @src is not supported by any storage backend currently lin= ked + * 1 if it is supported and -1 on error with an error reported. + */ +static int +virStorageFileGetBackendForSupportCheck(const virStorageSource *src, + virStorageFileBackendPtr *backend) +{ + int actualType; + + + if (!src) { + *backend =3D NULL; + return 0; + } + + if (src->drv) { + virStorageDriverDataPtr drv =3D src->drv; + *backend =3D drv->backend; + return 1; + } + + actualType =3D virStorageSourceGetActualType(src); + + if (virStorageFileBackendForType(actualType, src->protocol, false, bac= kend) < 0) + return -1; + + if (!*backend) + return 0; + + return 1; +} + + +int +virStorageFileSupportsBackingChainTraversal(const virStorageSource *src) +{ + virStorageFileBackendPtr backend; + int rv; + + if ((rv =3D virStorageFileGetBackendForSupportCheck(src, &backend)) < = 1) + return rv; + + return backend->storageFileGetUniqueIdentifier && + backend->storageFileRead && + backend->storageFileAccess ? 1 : 0; +} + + +/** + * virStorageFileSupportsSecurityDriver: + * + * @src: a storage file structure + * + * Check if a storage file supports operations needed by the security + * driver to perform labelling + */ +int +virStorageFileSupportsSecurityDriver(const virStorageSource *src) +{ + virStorageFileBackendPtr backend; + int rv; + + if ((rv =3D virStorageFileGetBackendForSupportCheck(src, &backend)) < = 1) + return rv; + + return backend->storageFileChown ? 1 : 0; +} + + +/** + * virStorageFileSupportsAccess: + * + * @src: a storage file structure + * + * Check if a storage file supports checking if the storage source is acce= ssible + * for the given vm. + */ +int +virStorageFileSupportsAccess(const virStorageSource *src) +{ + virStorageFileBackendPtr backend; + int rv; + + if ((rv =3D virStorageFileGetBackendForSupportCheck(src, &backend)) < = 1) + return rv; + + return backend->storageFileAccess ? 1 : 0; +} + + +/** + * virStorageFileSupportsCreate: + * @src: a storage file structure + * + * Check if the storage driver supports creating storage described by @src + * via virStorageFileCreate. + */ +int +virStorageFileSupportsCreate(const virStorageSource *src) +{ + virStorageFileBackendPtr backend; + int rv; + + if ((rv =3D virStorageFileGetBackendForSupportCheck(src, &backend)) < = 1) + return rv; + + return backend->storageFileCreate ? 1 : 0; +} + + +void +virStorageFileDeinit(virStorageSourcePtr src) +{ + virStorageDriverDataPtr drv =3D NULL; + + if (!virStorageFileIsInitialized(src)) + return; + + drv =3D src->drv; + + if (drv->backend && + drv->backend->backendDeinit) + drv->backend->backendDeinit(src); + + VIR_FREE(src->drv); +} + + +/** + * virStorageFileInitAs: + * + * @src: storage source definition + * @uid: uid used to access the file, or -1 for current uid + * @gid: gid used to access the file, or -1 for current gid + * + * Initialize a storage source to be used with storage driver. Use the pro= vided + * uid and gid if possible for the operations. + * + * Returns 0 if the storage file was successfully initialized, -1 if the + * initialization failed. Libvirt error is reported. + */ +int +virStorageFileInitAs(virStorageSourcePtr src, + uid_t uid, gid_t gid) +{ + int actualType =3D virStorageSourceGetActualType(src); + virStorageDriverDataPtr drv =3D g_new0(virStorageDriverData, 1); + + src->drv =3D drv; + + if (uid =3D=3D (uid_t) -1) + drv->uid =3D geteuid(); + else + drv->uid =3D uid; + + if (gid =3D=3D (gid_t) -1) + drv->gid =3D getegid(); + else + drv->gid =3D gid; + + if (virStorageFileBackendForType(actualType, + src->protocol, + true, + &drv->backend) < 0) + goto error; + + if (drv->backend->backendInit && + drv->backend->backendInit(src) < 0) + goto error; + + return 0; + + error: + VIR_FREE(src->drv); + return -1; +} + + +/** + * virStorageFileInit: + * + * See virStorageFileInitAs. The file is initialized to be accessed by the + * current user. + */ +int +virStorageFileInit(virStorageSourcePtr src) +{ + return virStorageFileInitAs(src, -1, -1); +} + + +/** + * virStorageFileCreate: Creates an empty storage file via storage driver + * + * @src: file structure pointing to the file + * + * Returns 0 on success, -2 if the function isn't supported by the backend, + * -1 on other failure. Errno is set in case of failure. + */ +int +virStorageFileCreate(virStorageSourcePtr src) +{ + virStorageDriverDataPtr drv =3D NULL; + int ret; + + if (!virStorageFileIsInitialized(src)) { + errno =3D ENOSYS; + return -2; + } + + drv =3D src->drv; + + if (!drv->backend->storageFileCreate) { + errno =3D ENOSYS; + return -2; + } + + ret =3D drv->backend->storageFileCreate(src); + + VIR_DEBUG("created storage file %p: ret=3D%d, errno=3D%d", + src, ret, errno); + + return ret; +} + + +/** + * virStorageFileUnlink: Unlink storage file via storage driver + * + * @src: file structure pointing to the file + * + * Unlinks the file described by the @file structure. + * + * Returns 0 on success, -2 if the function isn't supported by the backend, + * -1 on other failure. Errno is set in case of failure. + */ +int +virStorageFileUnlink(virStorageSourcePtr src) +{ + virStorageDriverDataPtr drv =3D NULL; + int ret; + + if (!virStorageFileIsInitialized(src)) { + errno =3D ENOSYS; + return -2; + } + + drv =3D src->drv; + + if (!drv->backend->storageFileUnlink) { + errno =3D ENOSYS; + return -2; + } + + ret =3D drv->backend->storageFileUnlink(src); + + VIR_DEBUG("unlinked storage file %p: ret=3D%d, errno=3D%d", + src, ret, errno); + + return ret; +} + + +/** + * virStorageFileStat: returns stat struct of a file via storage driver + * + * @src: file structure pointing to the file + * @stat: stat structure to return data + * + * Returns 0 on success, -2 if the function isn't supported by the backend, + * -1 on other failure. Errno is set in case of failure. +*/ +int +virStorageFileStat(virStorageSourcePtr src, + struct stat *st) +{ + virStorageDriverDataPtr drv =3D NULL; + int ret; + + if (!virStorageFileIsInitialized(src)) { + errno =3D ENOSYS; + return -2; + } + + drv =3D src->drv; + + if (!drv->backend->storageFileStat) { + errno =3D ENOSYS; + return -2; + } + + ret =3D drv->backend->storageFileStat(src, st); + + VIR_DEBUG("stat of storage file %p: ret=3D%d, errno=3D%d", + src, ret, errno); + + return ret; +} + + +/** + * virStorageFileRead: read bytes from a file into a buffer + * + * @src: file structure pointing to the file + * @offset: number of bytes to skip in the storage file + * @len: maximum number of bytes read from the storage file + * @buf: buffer to read the data into. (buffer shall be freed by caller) + * + * Returns the count of bytes read on success and -1 on failure, -2 if the + * function isn't supported by the backend. + * Libvirt error is reported on failure. + */ +ssize_t +virStorageFileRead(virStorageSourcePtr src, + size_t offset, + size_t len, + char **buf) +{ + virStorageDriverDataPtr drv =3D NULL; + ssize_t ret; + + if (!virStorageFileIsInitialized(src)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("storage file backend not initialized")); + return -1; + } + + drv =3D src->drv; + + if (!drv->backend->storageFileRead) + return -2; + + ret =3D drv->backend->storageFileRead(src, offset, len, buf); + + VIR_DEBUG("read '%zd' bytes from storage '%p' starting at offset '%zu'= ", + ret, src, offset); + + return ret; +} + + +/* + * virStorageFileGetUniqueIdentifier: Get a unique string describing the v= olume + * + * @src: file structure pointing to the file + * + * Returns a string uniquely describing a single volume (canonical path). + * The string shall not be freed and is valid until the storage file is + * deinitialized. Returns NULL on error and sets a libvirt error code */ +const char * +virStorageFileGetUniqueIdentifier(virStorageSourcePtr src) +{ + virStorageDriverDataPtr drv =3D NULL; + + if (!virStorageFileIsInitialized(src)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("storage file backend not initialized")); + return NULL; + } + + drv =3D src->drv; + + if (!drv->backend->storageFileGetUniqueIdentifier) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unique storage file identifier not implemented f= or " + "storage type %s (protocol: %s)'"), + virStorageTypeToString(src->type), + virStorageNetProtocolTypeToString(src->protocol)); + return NULL; + } + + return drv->backend->storageFileGetUniqueIdentifier(src); +} + + +/** + * virStorageFileAccess: Check accessibility of a storage file + * + * @src: storage file to check access permissions + * @mode: accessibility check options (see man 2 access) + * + * Returns 0 on success, -1 on error and sets errno. No libvirt + * error is reported. Returns -2 if the operation isn't supported + * by libvirt storage backend. + */ +int +virStorageFileAccess(virStorageSourcePtr src, + int mode) +{ + virStorageDriverDataPtr drv =3D NULL; + + if (!virStorageFileIsInitialized(src)) { + errno =3D ENOSYS; + return -2; + } + + drv =3D src->drv; + + if (!drv->backend->storageFileAccess) { + errno =3D ENOSYS; + return -2; + } + + return drv->backend->storageFileAccess(src, mode); +} + + +/** + * virStorageFileChown: Change owner of a storage file + * + * @src: storage file to change owner of + * @uid: new owner id + * @gid: new group id + * + * Returns 0 on success, -1 on error and sets errno. No libvirt + * error is reported. Returns -2 if the operation isn't supported + * by libvirt storage backend. + */ +int +virStorageFileChown(const virStorageSource *src, + uid_t uid, + gid_t gid) +{ + virStorageDriverDataPtr drv =3D NULL; + + if (!virStorageFileIsInitialized(src)) { + errno =3D ENOSYS; + return -2; + } + + drv =3D src->drv; + + if (!drv->backend->storageFileChown) { + errno =3D ENOSYS; + return -2; + } + + VIR_DEBUG("chown of storage file %p to %u:%u", + src, (unsigned int)uid, (unsigned int)gid); + + return drv->backend->storageFileChown(src, uid, gid); +} + + +/** + * virStorageFileReportBrokenChain: + * + * @errcode: errno when accessing @src + * @src: inaccessible file in the backing chain of @parent + * @parent: root virStorageSource being checked + * + * Reports the correct error message if @src is missing in the backing cha= in + * for @parent. + */ +void +virStorageFileReportBrokenChain(int errcode, + virStorageSourcePtr src, + virStorageSourcePtr parent) +{ + if (src->drv) { + virStorageDriverDataPtr drv =3D src->drv; + unsigned int access_user =3D drv->uid; + unsigned int access_group =3D drv->gid; + + if (src =3D=3D parent) { + virReportSystemError(errcode, + _("Cannot access storage file '%s' " + "(as uid:%u, gid:%u)"), + src->path, access_user, access_group); + } else { + virReportSystemError(errcode, + _("Cannot access backing file '%s' " + "of storage file '%s' (as uid:%u, gid:%= u)"), + src->path, parent->path, access_user, acc= ess_group); + } + } else { + if (src =3D=3D parent) { + virReportSystemError(errcode, + _("Cannot access storage file '%s'"), + src->path); + } else { + virReportSystemError(errcode, + _("Cannot access backing file '%s' " + "of storage file '%s'"), + src->path, parent->path); + } + } +} + + +static int +virStorageFileGetMetadataRecurseReadHeader(virStorageSourcePtr src, + virStorageSourcePtr parent, + uid_t uid, + gid_t gid, + char **buf, + size_t *headerLen, + GHashTable *cycle) +{ + int ret =3D -1; + const char *uniqueName; + ssize_t len; + + if (virStorageFileInitAs(src, uid, gid) < 0) + return -1; + + if (virStorageFileAccess(src, F_OK) < 0) { + virStorageFileReportBrokenChain(errno, src, parent); + goto cleanup; + } + + if (!(uniqueName =3D virStorageFileGetUniqueIdentifier(src))) + goto cleanup; + + if (virHashHasEntry(cycle, uniqueName)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("backing store for %s (%s) is self-referential"), + NULLSTR(src->path), uniqueName); + goto cleanup; + } + + if (virHashAddEntry(cycle, uniqueName, NULL) < 0) + goto cleanup; + + if ((len =3D virStorageFileRead(src, 0, VIR_STORAGE_MAX_HEADER, buf)) = < 0) + goto cleanup; + + *headerLen =3D len; + ret =3D 0; + + cleanup: + virStorageFileDeinit(src); + return ret; +} + + +/* Recursive workhorse for virStorageFileGetMetadata. */ +static int +virStorageFileGetMetadataRecurse(virStorageSourcePtr src, + virStorageSourcePtr parent, + uid_t uid, gid_t gid, + bool report_broken, + GHashTable *cycle, + unsigned int depth) +{ + virStorageFileFormat orig_format =3D src->format; + size_t headerLen; + int rv; + g_autofree char *buf =3D NULL; + g_autoptr(virStorageSource) backingStore =3D NULL; + + VIR_DEBUG("path=3D%s format=3D%d uid=3D%u gid=3D%u", + NULLSTR(src->path), src->format, + (unsigned int)uid, (unsigned int)gid); + + if (src->format =3D=3D VIR_STORAGE_FILE_AUTO_SAFE) + src->format =3D VIR_STORAGE_FILE_AUTO; + + /* exit if we can't load information about the current image */ + rv =3D virStorageFileSupportsBackingChainTraversal(src); + if (rv <=3D 0) { + if (orig_format =3D=3D VIR_STORAGE_FILE_AUTO) + return -2; + + return rv; + } + + if (virStorageFileGetMetadataRecurseReadHeader(src, parent, uid, gid, + &buf, &headerLen, cycle= ) < 0) + return -1; + + if (virStorageFileProbeGetMetadata(src, buf, headerLen) < 0) + return -1; + + /* If we probed the format we MUST ensure that nothing else than the c= urrent + * image is considered for security labelling and/or recursion. */ + if (orig_format =3D=3D VIR_STORAGE_FILE_AUTO) { + if (src->backingStoreRaw) { + src->format =3D VIR_STORAGE_FILE_RAW; + VIR_FREE(src->backingStoreRaw); + return -2; + } + } + + if (src->backingStoreRaw) { + if ((rv =3D virStorageSourceNewFromBacking(src, &backingStore)) < = 0) + return -1; + + /* the backing file would not be usable for VM usage */ + if (rv =3D=3D 1) + return 0; + + if ((rv =3D virStorageFileGetMetadataRecurse(backingStore, parent, + uid, gid, + report_broken, + cycle, depth + 1)) < 0)= { + if (!report_broken) + return 0; + + if (rv =3D=3D -2) { + virReportError(VIR_ERR_OPERATION_INVALID, + _("format of backing image '%s' of image '%= s' was not specified in the image metadata " + "(See https://libvirt.org/kbase/backing_c= hains.html for troubleshooting)"), + src->backingStoreRaw, NULLSTR(src->path)); + } + + return -1; + } + + backingStore->id =3D depth; + src->backingStore =3D g_steal_pointer(&backingStore); + } else { + /* add terminator */ + src->backingStore =3D virStorageSourceNew(); + } + + return 0; +} + + +/** + * virStorageFileGetMetadata: + * + * Extract metadata about the storage volume with the specified + * image format. If image format is VIR_STORAGE_FILE_AUTO, it + * will probe to automatically identify the format. Recurses through + * the entire chain. + * + * Open files using UID and GID (or pass -1 for the current user/group). + * Treat any backing files without explicit type as raw, unless ALLOW_PROB= E. + * + * Callers are advised never to use VIR_STORAGE_FILE_AUTO as a + * format, since a malicious guest can turn a raw file into any + * other non-raw format at will. + * + * If @report_broken is true, the whole function fails with a possibly sane + * error instead of just returning a broken chain. Note that the inability= for + * libvirt to traverse a given source is not considered an error. + * + * Caller MUST free result after use via virObjectUnref. + */ +int +virStorageFileGetMetadata(virStorageSourcePtr src, + uid_t uid, gid_t gid, + bool report_broken) +{ + GHashTable *cycle =3D NULL; + virStorageType actualType =3D virStorageSourceGetActualType(src); + int ret =3D -1; + + VIR_DEBUG("path=3D%s format=3D%d uid=3D%u gid=3D%u report_broken=3D%d", + src->path, src->format, (unsigned int)uid, (unsigned int)gid, + report_broken); + + if (!(cycle =3D virHashNew(NULL))) + return -1; + + if (src->format <=3D VIR_STORAGE_FILE_NONE) { + if (actualType =3D=3D VIR_STORAGE_TYPE_DIR) + src->format =3D VIR_STORAGE_FILE_DIR; + else + src->format =3D VIR_STORAGE_FILE_RAW; + } + + ret =3D virStorageFileGetMetadataRecurse(src, src, uid, gid, + report_broken, cycle, 1); + + virHashFree(cycle); + return ret; +} + + +/** + * virStorageFileGetBackingStoreStr: + * @src: storage object + * + * Extracts the backing store string as stored in the storage volume descr= ibed + * by @src and returns it to the user. Caller is responsible for freeing i= t. + * In case when the string can't be retrieved or does not exist NULL is + * returned. + */ +int +virStorageFileGetBackingStoreStr(virStorageSourcePtr src, + char **backing) +{ + ssize_t headerLen; + int rv; + g_autofree char *buf =3D NULL; + g_autoptr(virStorageSource) tmp =3D NULL; + + *backing =3D NULL; + + /* exit if we can't load information about the current image */ + if (!virStorageFileSupportsBackingChainTraversal(src)) + return 0; + + rv =3D virStorageFileAccess(src, F_OK); + if (rv =3D=3D -2) + return 0; + if (rv < 0) { + virStorageFileReportBrokenChain(errno, src, src); + return -1; + } + + if ((headerLen =3D virStorageFileRead(src, 0, VIR_STORAGE_MAX_HEADER, + &buf)) < 0) { + if (headerLen =3D=3D -2) + return 0; + return -1; + } + + if (!(tmp =3D virStorageSourceCopy(src, false))) + return -1; + + if (virStorageFileProbeGetMetadata(tmp, buf, headerLen) < 0) + return -1; + + *backing =3D g_steal_pointer(&tmp->backingStoreRaw); + return 0; +} diff --git a/src/storage_file/storage_source.h b/src/storage_file/storage_s= ource.h new file mode 100644 index 0000000000..23f56417a8 --- /dev/null +++ b/src/storage_file/storage_source.h @@ -0,0 +1,148 @@ +/* + * storage_source.h: file utility functions for FS storage backend + * + * Copyright (C) 2007-2009, 2012-2016 Red Hat, Inc. + * Copyright (C) 2007-2008 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + */ + +#pragma once + +#include "virstoragefile.h" + +#ifndef DEV_BSIZE +# define DEV_BSIZE 512 +#endif + +virStorageSourcePtr +virStorageFileGetMetadataFromFD(const char *path, + int fd, + int format); + +virStorageSourcePtr +virStorageFileGetMetadataFromBuf(const char *path, + char *buf, + size_t len, + int format) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); + +virStorageSourcePtr +virStorageFileChainLookup(virStorageSourcePtr chain, + virStorageSourcePtr startFrom, + const char *name, + unsigned int idx, + virStorageSourcePtr *parent) + ATTRIBUTE_NONNULL(1); + +int +virStorageSourceUpdatePhysicalSize(virStorageSourcePtr src, + int fd, + struct stat const *sb); + +int +virStorageSourceUpdateBackingSizes(virStorageSourcePtr src, + int fd, + struct stat const *sb); + +int +virStorageSourceUpdateCapacity(virStorageSourcePtr src, + char *buf, + ssize_t len); + +int +virStorageSourceNewFromBacking(virStorageSourcePtr parent, + virStorageSourcePtr *backing); + +int +virStorageSourceParseRBDColonString(const char *rbdstr, + virStorageSourcePtr src) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); + +int +virStorageFileGetRelativeBackingPath(virStorageSourcePtr top, + virStorageSourcePtr base, + char **relpath) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3); + +int +virStorageSourceNewFromBackingAbsolute(const char *path, + virStorageSourcePtr *src); + +int +virStorageFileInit(virStorageSourcePtr src); + +int +virStorageFileInitAs(virStorageSourcePtr src, + uid_t uid, gid_t gid); + +void +virStorageFileDeinit(virStorageSourcePtr src); + +int +virStorageFileCreate(virStorageSourcePtr src); + +int +virStorageFileUnlink(virStorageSourcePtr src); + +int +virStorageFileStat(virStorageSourcePtr src, + struct stat *st); + +ssize_t +virStorageFileRead(virStorageSourcePtr src, + size_t offset, + size_t len, + char **buf); + +const char * +virStorageFileGetUniqueIdentifier(virStorageSourcePtr src); + +int +virStorageFileAccess(virStorageSourcePtr src, + int mode); + +int +virStorageFileChown(const virStorageSource *src, + uid_t uid, + gid_t gid); + +int +virStorageFileSupportsSecurityDriver(const virStorageSource *src); + +int +virStorageFileSupportsAccess(const virStorageSource *src); + +int +virStorageFileSupportsCreate(const virStorageSource *src); + +int +virStorageFileSupportsBackingChainTraversal(const virStorageSource *src); + +int +virStorageFileGetMetadata(virStorageSourcePtr src, + uid_t uid, gid_t gid, + bool report_broken) + ATTRIBUTE_NONNULL(1); + +int +virStorageFileGetBackingStoreStr(virStorageSourcePtr src, + char **backing) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); + +void +virStorageFileReportBrokenChain(int errcode, + virStorageSourcePtr src, + virStorageSourcePtr parent); diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c index 98a3222d09..c70d5713de 100644 --- a/src/util/virstoragefile.c +++ b/src/util/virstoragefile.c @@ -31,14 +31,9 @@ #include "vircommand.h" #include "virhash.h" #include "virstring.h" -#include "viruri.h" #include "virbuffer.h" -#include "virjson.h" #include "virstorageencryption.h" -#include "virstoragefilebackend.h" -#include "virstoragefileprobe.h" #include "virsecret.h" -#include "virutil.h" =20 #define VIR_FROM_THIS VIR_FROM_STORAGE =20 @@ -146,126 +141,6 @@ virStorageIsRelative(const char *backing) } =20 =20 -static virStorageSourcePtr -virStorageFileMetadataNew(const char *path, - int format) -{ - g_autoptr(virStorageSource) def =3D virStorageSourceNew(); - - def->format =3D format; - def->type =3D VIR_STORAGE_TYPE_FILE; - - def->path =3D g_strdup(path); - - return g_steal_pointer(&def); -} - - -/** - * virStorageFileGetMetadataFromBuf: - * @path: name of file, for error messages - * @buf: header bytes from @path - * @len: length of @buf - * @format: format of the storage file - * - * Extract metadata about the storage volume with the specified image form= at. - * If image format is VIR_STORAGE_FILE_AUTO, it will probe to automatically - * identify the format. Does not recurse. - * - * Callers are advised never to use VIR_STORAGE_FILE_AUTO as a format on a= file - * that might be raw if that file will then be passed to a guest, since a - * malicious guest can turn a raw file into any other non-raw format at wi= ll. - * - * If the 'backingStoreRawFormat' field of the returned structure is - * VIR_STORAGE_FILE_AUTO it indicates the image didn't specify an explicit - * format for its backing store. Callers are advised against probing for t= he - * backing store format in this case. - * - * Caller MUST free the result after use via virObjectUnref. - */ -virStorageSourcePtr -virStorageFileGetMetadataFromBuf(const char *path, - char *buf, - size_t len, - int format) -{ - virStorageSourcePtr ret =3D NULL; - - if (!(ret =3D virStorageFileMetadataNew(path, format))) - return NULL; - - if (virStorageFileProbeGetMetadata(ret, buf, len) < 0) { - virObjectUnref(ret); - return NULL; - } - - return ret; -} - - -/** - * virStorageFileGetMetadataFromFD: - * - * Extract metadata about the storage volume with the specified - * image format. If image format is VIR_STORAGE_FILE_AUTO, it - * will probe to automatically identify the format. Does not recurse. - * - * Callers are advised never to use VIR_STORAGE_FILE_AUTO as a - * format, since a malicious guest can turn a raw file into any - * other non-raw format at will. - * - * Caller MUST free the result after use via virObjectUnref. - */ -virStorageSourcePtr -virStorageFileGetMetadataFromFD(const char *path, - int fd, - int format) - -{ - ssize_t len =3D VIR_STORAGE_MAX_HEADER; - struct stat sb; - g_autofree char *buf =3D NULL; - g_autoptr(virStorageSource) meta =3D NULL; - - if (fstat(fd, &sb) < 0) { - virReportSystemError(errno, - _("cannot stat file '%s'"), path); - return NULL; - } - - if (!(meta =3D virStorageFileMetadataNew(path, format))) - return NULL; - - if (S_ISDIR(sb.st_mode)) { - /* No header to probe for directories, but also no backing file. J= ust - * update the metadata.*/ - meta->type =3D VIR_STORAGE_TYPE_DIR; - meta->format =3D VIR_STORAGE_FILE_DIR; - return g_steal_pointer(&meta); - } - - if (lseek(fd, 0, SEEK_SET) =3D=3D (off_t)-1) { - virReportSystemError(errno, _("cannot seek to start of '%s'"), met= a->path); - return NULL; - } - - if ((len =3D virFileReadHeaderFD(fd, len, &buf)) < 0) { - virReportSystemError(errno, _("cannot read header '%s'"), meta->pa= th); - return NULL; - } - - if (virStorageFileProbeGetMetadata(meta, buf, len) < 0) - return NULL; - - if (S_ISREG(sb.st_mode)) - meta->type =3D VIR_STORAGE_TYPE_FILE; - else if (S_ISBLK(sb.st_mode)) - meta->type =3D VIR_STORAGE_TYPE_BLOCK; - - return g_steal_pointer(&meta); -} - - #ifdef WITH_UDEV /* virStorageFileGetSCSIKey * @path: Path to the SCSI device @@ -503,107 +378,6 @@ virStorageSourceHasBacking(const virStorageSource *sr= c) } =20 =20 -/* Given a @chain, look for the backing store @name that is a backing file - * of @startFrom (or any member of @chain if @startFrom is NULL) and return - * that location within the chain. @chain must always point to the top of - * the chain. Pass NULL for @name and 0 for @idx to find the base of the - * chain. Pass nonzero @idx to find the backing source according to its - * position in the backing chain. If @parent is not NULL, set *@parent to - * the preferred name of the parent (or to NULL if @name matches the start - * of the chain). Since the results point within @chain, they must not be - * independently freed. Reports an error and returns NULL if @name is not - * found. - */ -virStorageSourcePtr -virStorageFileChainLookup(virStorageSourcePtr chain, - virStorageSourcePtr startFrom, - const char *name, - unsigned int idx, - virStorageSourcePtr *parent) -{ - virStorageSourcePtr prev; - const char *start =3D chain->path; - bool nameIsFile =3D virStorageIsFile(name); - - if (!parent) - parent =3D &prev; - *parent =3D NULL; - - if (startFrom) { - while (virStorageSourceIsBacking(chain) && - chain !=3D startFrom->backingStore) - chain =3D chain->backingStore; - - *parent =3D startFrom; - } - - while (virStorageSourceIsBacking(chain)) { - if (!name && !idx) { - if (!virStorageSourceHasBacking(chain)) - break; - } else if (idx) { - VIR_DEBUG("%u: %s", chain->id, chain->path); - if (idx =3D=3D chain->id) - break; - } else { - if (STREQ_NULLABLE(name, chain->relPath) || - STREQ_NULLABLE(name, chain->path)) - break; - - if (nameIsFile && virStorageSourceIsLocalStorage(chain)) { - g_autofree char *parentDir =3D NULL; - int result; - - if (*parent && virStorageSourceIsLocalStorage(*parent)) - parentDir =3D g_path_get_dirname((*parent)->path); - else - parentDir =3D g_strdup("."); - - result =3D virFileRelLinkPointsTo(parentDir, name, - chain->path); - - if (result < 0) - goto error; - - if (result > 0) - break; - } - } - *parent =3D chain; - chain =3D chain->backingStore; - } - - if (!virStorageSourceIsBacking(chain)) - goto error; - - return chain; - - error: - if (idx) { - virReportError(VIR_ERR_INVALID_ARG, - _("could not find backing store index %u in chain " - "for '%s'"), - idx, NULLSTR(start)); - } else if (name) { - if (startFrom) - virReportError(VIR_ERR_INVALID_ARG, - _("could not find image '%s' beneath '%s' in " - "chain for '%s'"), name, NULLSTR(startFrom->p= ath), - NULLSTR(start)); - else - virReportError(VIR_ERR_INVALID_ARG, - _("could not find image '%s' in chain for '%s'"= ), - name, NULLSTR(start)); - } else { - virReportError(VIR_ERR_INVALID_ARG, - _("could not find base image in chain for '%s'"), - NULLSTR(start)); - } - *parent =3D NULL; - return NULL; -} - - void virStorageNetHostDefClear(virStorageNetHostDefPtr def) { @@ -1660,1550 +1434,6 @@ virStorageSourceNew(void) } =20 =20 -static virStorageSourcePtr -virStorageSourceNewFromBackingRelative(virStorageSourcePtr parent, - const char *rel) -{ - g_autofree char *dirname =3D NULL; - g_autoptr(virStorageSource) def =3D virStorageSourceNew(); - - /* store relative name */ - def->relPath =3D g_strdup(rel); - - dirname =3D g_path_get_dirname(parent->path); - - if (STRNEQ(dirname, "/")) { - def->path =3D g_strdup_printf("%s/%s", dirname, rel); - } else { - def->path =3D g_strdup_printf("/%s", rel); - } - - if (virStorageSourceGetActualType(parent) =3D=3D VIR_STORAGE_TYPE_NETW= ORK) { - def->type =3D VIR_STORAGE_TYPE_NETWORK; - - /* copy the host network part */ - def->protocol =3D parent->protocol; - if (parent->nhosts) { - if (!(def->hosts =3D virStorageNetHostDefCopy(parent->nhosts, - parent->hosts))) - return NULL; - - def->nhosts =3D parent->nhosts; - } - - def->volume =3D g_strdup(parent->volume); - } else { - /* set the type to _FILE, the caller shall update it to the actual= type */ - def->type =3D VIR_STORAGE_TYPE_FILE; - } - - return g_steal_pointer(&def); -} - - -static int -virStorageSourceParseBackingURI(virStorageSourcePtr src, - const char *uristr) -{ - g_autoptr(virURI) uri =3D NULL; - const char *path =3D NULL; - g_auto(GStrv) scheme =3D NULL; - - if (!(uri =3D virURIParse(uristr))) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("failed to parse backing file location '%s'"), - uristr); - return -1; - } - - src->hosts =3D g_new0(virStorageNetHostDef, 1); - src->nhosts =3D 1; - - if (!(scheme =3D virStringSplit(uri->scheme, "+", 2))) - return -1; - - if (!scheme[0] || - (src->protocol =3D virStorageNetProtocolTypeFromString(scheme[0]))= < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("invalid backing protocol '%s'"), - NULLSTR(scheme[0])); - return -1; - } - - if (scheme[1] && - (src->hosts->transport =3D virStorageNetHostTransportTypeFromStrin= g(scheme[1])) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("invalid protocol transport type '%s'"), - scheme[1]); - return -1; - } - - if (uri->query) { - if (src->protocol =3D=3D VIR_STORAGE_NET_PROTOCOL_HTTP || - src->protocol =3D=3D VIR_STORAGE_NET_PROTOCOL_HTTPS) { - src->query =3D g_strdup(uri->query); - } else { - /* handle socket stored as a query */ - if (STRPREFIX(uri->query, "socket=3D")) - src->hosts->socket =3D g_strdup(STRSKIP(uri->query, "socke= t=3D")); - } - } - - /* uri->path is NULL if the URI does not contain slash after host: - * transport://host:port */ - if (uri->path) - path =3D uri->path; - else - path =3D ""; - - /* possibly skip the leading slash */ - if (path[0] =3D=3D '/') - path++; - - /* NBD allows empty export name (path) */ - if (src->protocol =3D=3D VIR_STORAGE_NET_PROTOCOL_NBD && - path[0] =3D=3D '\0') - path =3D NULL; - - src->path =3D g_strdup(path); - - if (src->protocol =3D=3D VIR_STORAGE_NET_PROTOCOL_GLUSTER) { - char *tmp; - - if (!src->path) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("missing volume name and path for gluster vol= ume")); - return -1; - } - - if (!(tmp =3D strchr(src->path, '/')) || - tmp =3D=3D src->path) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("missing volume name or file name in " - "gluster source path '%s'"), src->path); - return -1; - } - - src->volume =3D src->path; - - src->path =3D g_strdup(tmp + 1); - - tmp[0] =3D '\0'; - } - - src->hosts->port =3D uri->port; - - src->hosts->name =3D g_strdup(uri->server); - - /* Libvirt doesn't handle inline authentication. Make the caller aware= . */ - if (uri->user) - return 1; - - return 0; -} - - -static int -virStorageSourceRBDAddHost(virStorageSourcePtr src, - char *hostport) -{ - char *port; - size_t skip; - g_auto(GStrv) parts =3D NULL; - - if (VIR_EXPAND_N(src->hosts, src->nhosts, 1) < 0) - return -1; - - if ((port =3D strchr(hostport, ']'))) { - /* ipv6, strip brackets */ - hostport +=3D 1; - skip =3D 3; - } else { - port =3D strstr(hostport, "\\:"); - skip =3D 2; - } - - if (port) { - *port =3D '\0'; - port +=3D skip; - if (virStringParsePort(port, &src->hosts[src->nhosts - 1].port) < = 0) - goto error; - } - - parts =3D virStringSplit(hostport, "\\:", 0); - if (!parts) - goto error; - src->hosts[src->nhosts-1].name =3D virStringListJoin((const char **)pa= rts, ":"); - if (!src->hosts[src->nhosts-1].name) - goto error; - - src->hosts[src->nhosts-1].transport =3D VIR_STORAGE_NET_HOST_TRANS_TCP; - src->hosts[src->nhosts-1].socket =3D NULL; - - return 0; - - error: - VIR_FREE(src->hosts[src->nhosts-1].name); - return -1; -} - - -int -virStorageSourceParseRBDColonString(const char *rbdstr, - virStorageSourcePtr src) -{ - char *p, *e, *next; - g_autofree char *options =3D NULL; - g_autoptr(virStorageAuthDef) authdef =3D NULL; - - /* optionally skip the "rbd:" prefix if provided */ - if (STRPREFIX(rbdstr, "rbd:")) - rbdstr +=3D strlen("rbd:"); - - src->path =3D g_strdup(rbdstr); - - p =3D strchr(src->path, ':'); - if (p) { - options =3D g_strdup(p + 1); - *p =3D '\0'; - } - - /* snapshot name */ - if ((p =3D strchr(src->path, '@'))) { - src->snapshot =3D g_strdup(p + 1); - *p =3D '\0'; - } - - /* pool vs. image name */ - if ((p =3D strchr(src->path, '/'))) { - src->volume =3D g_steal_pointer(&src->path); - src->path =3D g_strdup(p + 1); - *p =3D '\0'; - } - - /* options */ - if (!options) - return 0; /* all done */ - - p =3D options; - while (*p) { - /* find : delimiter or end of string */ - for (e =3D p; *e && *e !=3D ':'; ++e) { - if (*e =3D=3D '\\') { - e++; - if (*e =3D=3D '\0') - break; - } - } - if (*e =3D=3D '\0') { - next =3D e; /* last kv pair */ - } else { - next =3D e + 1; - *e =3D '\0'; - } - - if (STRPREFIX(p, "id=3D")) { - /* formulate authdef for src->auth */ - if (src->auth) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("duplicate 'id' found in '%s'"), src->pat= h); - return -1; - } - - authdef =3D g_new0(virStorageAuthDef, 1); - - authdef->username =3D g_strdup(p + strlen("id=3D")); - - authdef->secrettype =3D g_strdup(virSecretUsageTypeToString(VI= R_SECRET_USAGE_TYPE_CEPH)); - src->auth =3D g_steal_pointer(&authdef); - - /* Cannot formulate a secretType (eg, usage or uuid) given - * what is provided. - */ - } - if (STRPREFIX(p, "mon_host=3D")) { - char *h, *sep; - - h =3D p + strlen("mon_host=3D"); - while (h < e) { - for (sep =3D h; sep < e; ++sep) { - if (*sep =3D=3D '\\' && (sep[1] =3D=3D ',' || - sep[1] =3D=3D ';' || - sep[1] =3D=3D ' ')) { - *sep =3D '\0'; - sep +=3D 2; - break; - } - } - - if (virStorageSourceRBDAddHost(src, h) < 0) - return -1; - - h =3D sep; - } - } - - if (STRPREFIX(p, "conf=3D")) - src->configFile =3D g_strdup(p + strlen("conf=3D")); - - p =3D next; - } - return 0; -} - - -static int -virStorageSourceParseNBDColonString(const char *nbdstr, - virStorageSourcePtr src) -{ - g_autofree char *nbd =3D g_strdup(nbdstr); - char *export_name; - char *host_spec; - char *unixpath; - char *port; - - src->hosts =3D g_new0(virStorageNetHostDef, 1); - src->nhosts =3D 1; - - /* We extract the parameters in a similar way qemu does it */ - - /* format: [] denotes optional sections, uppercase are variable strings - * nbd:unix:/PATH/TO/SOCKET[:exportname=3DEXPORTNAME] - * nbd:HOSTNAME:PORT[:exportname=3DEXPORTNAME] - */ - - /* first look for ':exportname=3D' and cut it off */ - if ((export_name =3D strstr(nbd, ":exportname=3D"))) { - src->path =3D g_strdup(export_name + strlen(":exportname=3D")); - export_name[0] =3D '\0'; - } - - /* Verify the prefix and contents. Note that we require a - * "host_spec" part to be present. */ - if (!(host_spec =3D STRSKIP(nbd, "nbd:")) || host_spec[0] =3D=3D '\0') - goto malformed; - - if ((unixpath =3D STRSKIP(host_spec, "unix:"))) { - src->hosts->transport =3D VIR_STORAGE_NET_HOST_TRANS_UNIX; - - if (unixpath[0] =3D=3D '\0') - goto malformed; - - src->hosts->socket =3D g_strdup(unixpath); - } else { - src->hosts->transport =3D VIR_STORAGE_NET_HOST_TRANS_TCP; - - if (host_spec[0] =3D=3D ':') { - /* no host given */ - goto malformed; - } else if (host_spec[0] =3D=3D '[') { - host_spec++; - /* IPv6 addr */ - if (!(port =3D strstr(host_spec, "]:"))) - goto malformed; - - port[0] =3D '\0'; - port +=3D 2; - - if (host_spec[0] =3D=3D '\0') - goto malformed; - } else { - if (!(port =3D strchr(host_spec, ':'))) - goto malformed; - - port[0] =3D '\0'; - port++; - } - - if (virStringParsePort(port, &src->hosts->port) < 0) - return -1; - - src->hosts->name =3D g_strdup(host_spec); - } - - return 0; - - malformed: - virReportError(VIR_ERR_INTERNAL_ERROR, - _("malformed nbd string '%s'"), nbdstr); - return -1; -} - - -static int -virStorageSourceParseBackingColon(virStorageSourcePtr src, - const char *path) -{ - const char *p; - g_autofree char *protocol =3D NULL; - - if (!(p =3D strchr(path, ':'))) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("invalid backing protocol string '%s'"), - path); - return -1; - } - - protocol =3D g_strndup(path, p - path); - - if ((src->protocol =3D virStorageNetProtocolTypeFromString(protocol)) = < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("invalid backing protocol '%s'"), - protocol); - return -1; - } - - switch ((virStorageNetProtocol) src->protocol) { - case VIR_STORAGE_NET_PROTOCOL_NBD: - if (virStorageSourceParseNBDColonString(path, src) < 0) - return -1; - break; - - case VIR_STORAGE_NET_PROTOCOL_RBD: - if (virStorageSourceParseRBDColonString(path, src) < 0) - return -1; - break; - - case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG: - case VIR_STORAGE_NET_PROTOCOL_LAST: - case VIR_STORAGE_NET_PROTOCOL_NONE: - virReportError(VIR_ERR_INTERNAL_ERROR, - _("backing store parser is not implemented for prot= ocol %s"), - protocol); - return -1; - - case VIR_STORAGE_NET_PROTOCOL_HTTP: - case VIR_STORAGE_NET_PROTOCOL_HTTPS: - case VIR_STORAGE_NET_PROTOCOL_FTP: - case VIR_STORAGE_NET_PROTOCOL_FTPS: - case VIR_STORAGE_NET_PROTOCOL_TFTP: - case VIR_STORAGE_NET_PROTOCOL_ISCSI: - case VIR_STORAGE_NET_PROTOCOL_GLUSTER: - case VIR_STORAGE_NET_PROTOCOL_SSH: - case VIR_STORAGE_NET_PROTOCOL_VXHS: - case VIR_STORAGE_NET_PROTOCOL_NFS: - virReportError(VIR_ERR_INTERNAL_ERROR, - _("malformed backing store path for protocol %s"), - protocol); - return -1; - } - - return 0; -} - - -static int -virStorageSourceParseBackingJSONInternal(virStorageSourcePtr src, - virJSONValuePtr json, - const char *jsonstr, - bool allowformat); - - -static int -virStorageSourceParseBackingJSONPath(virStorageSourcePtr src, - virJSONValuePtr json, - const char *jsonstr G_GNUC_UNUSED, - int type) -{ - const char *path; - - if (!(path =3D virJSONValueObjectGetString(json, "filename"))) { - virReportError(VIR_ERR_INVALID_ARG, "%s", - _("missing 'filename' field in JSON backing volume " - "definition")); - return -1; - } - - src->path =3D g_strdup(path); - - src->type =3D type; - return 0; -} - - -static int -virStorageSourceParseBackingJSONUriStr(virStorageSourcePtr src, - const char *uri, - int protocol) -{ - int rc; - - if ((rc =3D virStorageSourceParseBackingURI(src, uri)) < 0) - return -1; - - if (src->protocol !=3D protocol) { - virReportError(VIR_ERR_INVALID_ARG, - _("expected protocol '%s' but got '%s' in URI JSON = volume " - "definition"), - virStorageNetProtocolTypeToString(protocol), - virStorageNetProtocolTypeToString(src->protocol)); - return -1; - } - - return rc; -} - - -static int -virStorageSourceParseBackingJSONUriCookies(virStorageSourcePtr src, - virJSONValuePtr json, - const char *jsonstr) -{ - const char *cookiestr; - g_auto(GStrv) cookies =3D NULL; - size_t ncookies =3D 0; - size_t i; - - if (!virJSONValueObjectHasKey(json, "cookie")) - return 0; - - if (!(cookiestr =3D virJSONValueObjectGetString(json, "cookie"))) { - virReportError(VIR_ERR_INVALID_ARG, - _("wrong format of 'cookie' field in backing store = definition '%s'"), - jsonstr); - return -1; - } - - if (!(cookies =3D virStringSplitCount(cookiestr, ";", 0, &ncookies))) - return -1; - - src->cookies =3D g_new0(virStorageNetCookieDefPtr, ncookies); - src->ncookies =3D ncookies; - - for (i =3D 0; i < ncookies; i++) { - char *cookiename =3D cookies[i]; - char *cookievalue; - - virSkipSpaces((const char **) &cookiename); - - if (!(cookievalue =3D strchr(cookiename, '=3D'))) { - virReportError(VIR_ERR_INVALID_ARG, - _("malformed http cookie '%s' in backing store = definition '%s'"), - cookies[i], jsonstr); - return -1; - } - - *cookievalue =3D '\0'; - cookievalue++; - - src->cookies[i] =3D g_new0(virStorageNetCookieDef, 1); - src->cookies[i]->name =3D g_strdup(cookiename); - src->cookies[i]->value =3D g_strdup(cookievalue); - } - - return 0; -} - - -static int -virStorageSourceParseBackingJSONUri(virStorageSourcePtr src, - virJSONValuePtr json, - const char *jsonstr, - int protocol) -{ - const char *uri; - - if (!(uri =3D virJSONValueObjectGetString(json, "url"))) { - virReportError(VIR_ERR_INVALID_ARG, "%s", - _("missing 'url' in JSON backing volume definition"= )); - return -1; - } - - if (protocol =3D=3D VIR_STORAGE_NET_PROTOCOL_HTTPS || - protocol =3D=3D VIR_STORAGE_NET_PROTOCOL_FTPS) { - if (virJSONValueObjectHasKey(json, "sslverify")) { - const char *tmpstr; - bool tmp; - - /* libguestfs still uses undocumented legacy value of 'off' */ - if ((tmpstr =3D virJSONValueObjectGetString(json, "sslverify")= ) && - STREQ(tmpstr, "off")) { - src->sslverify =3D VIR_TRISTATE_BOOL_NO; - } else { - if (virJSONValueObjectGetBoolean(json, "sslverify", &tmp) = < 0) { - virReportError(VIR_ERR_INVALID_ARG, - _("malformed 'sslverify' field in backi= ng store definition '%s'"), - jsonstr); - return -1; - } - - src->sslverify =3D virTristateBoolFromBool(tmp); - } - } - } - - if (protocol =3D=3D VIR_STORAGE_NET_PROTOCOL_HTTPS || - protocol =3D=3D VIR_STORAGE_NET_PROTOCOL_HTTP) { - if (virStorageSourceParseBackingJSONUriCookies(src, json, jsonstr)= < 0) - return -1; - } - - if (virJSONValueObjectHasKey(json, "readahead") && - virJSONValueObjectGetNumberUlong(json, "readahead", &src->readahea= d) < 0) { - virReportError(VIR_ERR_INVALID_ARG, - _("malformed 'readahead' field in backing store def= inition '%s'"), - jsonstr); - return -1; - } - - if (virJSONValueObjectHasKey(json, "timeout") && - virJSONValueObjectGetNumberUlong(json, "timeout", &src->timeout) <= 0) { - virReportError(VIR_ERR_INVALID_ARG, - _("malformed 'timeout' field in backing store defin= ition '%s'"), - jsonstr); - return -1; - } - - return virStorageSourceParseBackingJSONUriStr(src, uri, protocol); -} - - -static int -virStorageSourceParseBackingJSONInetSocketAddress(virStorageNetHostDefPtr = host, - virJSONValuePtr json) -{ - const char *hostname; - const char *port; - - if (!json) { - virReportError(VIR_ERR_INVALID_ARG, "%s", - _("missing remote server specification in JSON " - "backing volume definition")); - return -1; - } - - hostname =3D virJSONValueObjectGetString(json, "host"); - port =3D virJSONValueObjectGetString(json, "port"); - - if (!hostname) { - virReportError(VIR_ERR_INVALID_ARG, "%s", - _("missing hostname for tcp backing server in " - "JSON backing volume definition")); - return -1; - } - - host->transport =3D VIR_STORAGE_NET_HOST_TRANS_TCP; - host->name =3D g_strdup(hostname); - - if (virStringParsePort(port, &host->port) < 0) - return -1; - - return 0; -} - - -static int -virStorageSourceParseBackingJSONSocketAddress(virStorageNetHostDefPtr host, - virJSONValuePtr json) -{ - const char *type; - const char *socket; - - if (!json) { - virReportError(VIR_ERR_INVALID_ARG, "%s", - _("missing remote server specification in JSON " - "backing volume definition")); - return -1; - } - - if (!(type =3D virJSONValueObjectGetString(json, "type"))) { - virReportError(VIR_ERR_INVALID_ARG, "%s", - _("missing socket address type in " - "JSON backing volume definition")); - return -1; - } - - if (STREQ(type, "tcp") || STREQ(type, "inet")) { - return virStorageSourceParseBackingJSONInetSocketAddress(host, jso= n); - - } else if (STREQ(type, "unix")) { - host->transport =3D VIR_STORAGE_NET_HOST_TRANS_UNIX; - - socket =3D virJSONValueObjectGetString(json, "path"); - - /* check for old spelling for gluster protocol */ - if (!socket) - socket =3D virJSONValueObjectGetString(json, "socket"); - - if (!socket) { - virReportError(VIR_ERR_INVALID_ARG, "%s", - _("missing socket path for udp backing server i= n " - "JSON backing volume definition")); - return -1; - } - - host->socket =3D g_strdup(socket); - } else { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("backing store protocol '%s' is not yet supported= "), - type); - return -1; - } - - return 0; -} - - -static int -virStorageSourceParseBackingJSONGluster(virStorageSourcePtr src, - virJSONValuePtr json, - const char *jsonstr G_GNUC_UNUSED, - int opaque G_GNUC_UNUSED) -{ - const char *uri =3D virJSONValueObjectGetString(json, "filename"); - const char *volume =3D virJSONValueObjectGetString(json, "volume"); - const char *path =3D virJSONValueObjectGetString(json, "path"); - virJSONValuePtr server =3D virJSONValueObjectGetArray(json, "server"); - size_t nservers; - size_t i; - - /* legacy URI based syntax passed via 'filename' option */ - if (uri) - return virStorageSourceParseBackingJSONUriStr(src, uri, - VIR_STORAGE_NET_PROT= OCOL_GLUSTER); - - if (!volume || !path || !server) { - virReportError(VIR_ERR_INVALID_ARG, "%s", - _("missing 'volume', 'path' or 'server' attribute i= n " - "JSON backing definition for gluster volume")); - return -1; - } - - src->type =3D VIR_STORAGE_TYPE_NETWORK; - src->protocol =3D VIR_STORAGE_NET_PROTOCOL_GLUSTER; - - src->volume =3D g_strdup(volume); - src->path =3D g_strdup(path); - - nservers =3D virJSONValueArraySize(server); - if (nservers =3D=3D 0) { - virReportError(VIR_ERR_INVALID_ARG, "%s", - _("at least 1 server is necessary in " - "JSON backing definition for gluster volume")); - - return -1; - } - - src->hosts =3D g_new0(virStorageNetHostDef, nservers); - src->nhosts =3D nservers; - - for (i =3D 0; i < nservers; i++) { - if (virStorageSourceParseBackingJSONSocketAddress(src->hosts + i, - virJSONValueArra= yGet(server, i)) < 0) - return -1; - } - - return 0; -} - - -static int -virStorageSourceParseBackingJSONiSCSI(virStorageSourcePtr src, - virJSONValuePtr json, - const char *jsonstr G_GNUC_UNUSED, - int opaque G_GNUC_UNUSED) -{ - const char *transport =3D virJSONValueObjectGetString(json, "transport= "); - const char *portal =3D virJSONValueObjectGetString(json, "portal"); - const char *target =3D virJSONValueObjectGetString(json, "target"); - const char *lun =3D virJSONValueObjectGetStringOrNumber(json, "lun"); - const char *uri; - char *port; - - /* legacy URI based syntax passed via 'filename' option */ - if ((uri =3D virJSONValueObjectGetString(json, "filename"))) - return virStorageSourceParseBackingJSONUriStr(src, uri, - VIR_STORAGE_NET_PROT= OCOL_ISCSI); - - src->type =3D VIR_STORAGE_TYPE_NETWORK; - src->protocol =3D VIR_STORAGE_NET_PROTOCOL_ISCSI; - - if (!lun) - lun =3D "0"; - - src->hosts =3D g_new0(virStorageNetHostDef, 1); - src->nhosts =3D 1; - - if (STRNEQ_NULLABLE(transport, "tcp")) { - virReportError(VIR_ERR_INVALID_ARG, "%s", - _("only TCP transport is supported for iSCSI volume= s")); - return -1; - } - - src->hosts->transport =3D VIR_STORAGE_NET_HOST_TRANS_TCP; - - if (!portal) { - virReportError(VIR_ERR_INVALID_ARG, "%s", - _("missing 'portal' address in iSCSI backing defini= tion")); - return -1; - } - - if (!target) { - virReportError(VIR_ERR_INVALID_ARG, "%s", - _("missing 'target' in iSCSI backing definition")); - return -1; - } - - src->hosts->name =3D g_strdup(portal); - - if ((port =3D strrchr(src->hosts->name, ':')) && - !strchr(port, ']')) { - if (virStringParsePort(port + 1, &src->hosts->port) < 0) - return -1; - - *port =3D '\0'; - } - - src->path =3D g_strdup_printf("%s/%s", target, lun); - - /* Libvirt doesn't handle inline authentication. Make the caller aware= . */ - if (virJSONValueObjectGetString(json, "user") || - virJSONValueObjectGetString(json, "password")) - return 1; - - return 0; -} - - -static int -virStorageSourceParseBackingJSONNbd(virStorageSourcePtr src, - virJSONValuePtr json, - const char *jsonstr G_GNUC_UNUSED, - int opaque G_GNUC_UNUSED) -{ - const char *path =3D virJSONValueObjectGetString(json, "path"); - const char *host =3D virJSONValueObjectGetString(json, "host"); - const char *port =3D virJSONValueObjectGetString(json, "port"); - const char *export =3D virJSONValueObjectGetString(json, "export"); - virJSONValuePtr server =3D virJSONValueObjectGetObject(json, "server"); - - if (!path && !host && !server) { - virReportError(VIR_ERR_INVALID_ARG, "%s", - _("missing host specification of NBD server in JSON= " - "backing volume definition")); - return -1; - } - - src->type =3D VIR_STORAGE_TYPE_NETWORK; - src->protocol =3D VIR_STORAGE_NET_PROTOCOL_NBD; - - src->path =3D g_strdup(export); - - src->hosts =3D g_new0(virStorageNetHostDef, 1); - src->nhosts =3D 1; - - if (server) { - if (virStorageSourceParseBackingJSONSocketAddress(src->hosts, serv= er) < 0) - return -1; - } else { - if (path) { - src->hosts[0].transport =3D VIR_STORAGE_NET_HOST_TRANS_UNIX; - src->hosts[0].socket =3D g_strdup(path); - } else { - src->hosts[0].transport =3D VIR_STORAGE_NET_HOST_TRANS_TCP; - src->hosts[0].name =3D g_strdup(host); - - if (virStringParsePort(port, &src->hosts[0].port) < 0) - return -1; - } - } - - return 0; -} - - -static int -virStorageSourceParseBackingJSONSheepdog(virStorageSourcePtr src, - virJSONValuePtr json, - const char *jsonstr G_GNUC_UNUSED, - int opaque G_GNUC_UNUSED) -{ - const char *filename; - const char *vdi =3D virJSONValueObjectGetString(json, "vdi"); - virJSONValuePtr server =3D virJSONValueObjectGetObject(json, "server"); - - /* legacy URI based syntax passed via 'filename' option */ - if ((filename =3D virJSONValueObjectGetString(json, "filename"))) { - if (strstr(filename, "://")) - return virStorageSourceParseBackingJSONUriStr(src, filename, - VIR_STORAGE_NET_= PROTOCOL_SHEEPDOG); - - /* libvirt doesn't implement a parser for the legacy non-URI synta= x */ - virReportError(VIR_ERR_INVALID_ARG, "%s", - _("missing sheepdog URI in JSON backing volume defi= nition")); - return -1; - } - - src->type =3D VIR_STORAGE_TYPE_NETWORK; - src->protocol =3D VIR_STORAGE_NET_PROTOCOL_SHEEPDOG; - - if (!vdi) { - virReportError(VIR_ERR_INVALID_ARG, "%s", _("missing sheepdog vdi = name")); - return -1; - } - - src->path =3D g_strdup(vdi); - - src->hosts =3D g_new0(virStorageNetHostDef, 1); - src->nhosts =3D 1; - - if (virStorageSourceParseBackingJSONSocketAddress(src->hosts, server) = < 0) - return -1; - - return 0; -} - - -static int -virStorageSourceParseBackingJSONSSH(virStorageSourcePtr src, - virJSONValuePtr json, - const char *jsonstr G_GNUC_UNUSED, - int opaque G_GNUC_UNUSED) -{ - const char *path =3D virJSONValueObjectGetString(json, "path"); - const char *host =3D virJSONValueObjectGetString(json, "host"); - const char *port =3D virJSONValueObjectGetString(json, "port"); - const char *user =3D virJSONValueObjectGetString(json, "user"); - const char *host_key_check =3D virJSONValueObjectGetString(json, "host= _key_check"); - virJSONValuePtr server =3D virJSONValueObjectGetObject(json, "server"); - - if (!(host || server) || !path) { - virReportError(VIR_ERR_INVALID_ARG, "%s", - _("missing host/server or path of SSH JSON backing " - "volume definition")); - return -1; - } - - src->type =3D VIR_STORAGE_TYPE_NETWORK; - src->protocol =3D VIR_STORAGE_NET_PROTOCOL_SSH; - - src->path =3D g_strdup(path); - - src->hosts =3D g_new0(virStorageNetHostDef, 1); - src->nhosts =3D 1; - - if (server) { - if (virStorageSourceParseBackingJSONInetSocketAddress(src->hosts, - server) < 0) - return -1; - } else { - src->hosts[0].transport =3D VIR_STORAGE_NET_HOST_TRANS_TCP; - src->hosts[0].name =3D g_strdup(host); - - if (virStringParsePort(port, &src->hosts[0].port) < 0) - return -1; - } - - /* these two are parsed just to be passed back as we don't model them = yet */ - src->ssh_user =3D g_strdup(user); - if (STREQ_NULLABLE(host_key_check, "no")) - src->ssh_host_key_check_disabled =3D true; - - return 0; -} - - -static int -virStorageSourceParseBackingJSONRBD(virStorageSourcePtr src, - virJSONValuePtr json, - const char *jsonstr G_GNUC_UNUSED, - int opaque G_GNUC_UNUSED) -{ - const char *filename; - const char *pool =3D virJSONValueObjectGetString(json, "pool"); - const char *image =3D virJSONValueObjectGetString(json, "image"); - const char *conf =3D virJSONValueObjectGetString(json, "conf"); - const char *snapshot =3D virJSONValueObjectGetString(json, "snapshot"); - virJSONValuePtr servers =3D virJSONValueObjectGetArray(json, "server"); - size_t nservers; - size_t i; - - src->type =3D VIR_STORAGE_TYPE_NETWORK; - src->protocol =3D VIR_STORAGE_NET_PROTOCOL_RBD; - - /* legacy syntax passed via 'filename' option */ - if ((filename =3D virJSONValueObjectGetString(json, "filename"))) - return virStorageSourceParseRBDColonString(filename, src); - - if (!pool || !image) { - virReportError(VIR_ERR_INVALID_ARG, "%s", - _("missing pool or image name in ceph backing volum= e " - "JSON specification")); - return -1; - } - - src->volume =3D g_strdup(pool); - src->path =3D g_strdup(image); - src->snapshot =3D g_strdup(snapshot); - src->configFile =3D g_strdup(conf); - - if (servers) { - nservers =3D virJSONValueArraySize(servers); - - src->hosts =3D g_new0(virStorageNetHostDef, nservers); - src->nhosts =3D nservers; - - for (i =3D 0; i < nservers; i++) { - if (virStorageSourceParseBackingJSONInetSocketAddress(src->hos= ts + i, - virJSONV= alueArrayGet(servers, i)) < 0) - return -1; - } - } - - return 0; -} - -static int -virStorageSourceParseBackingJSONRaw(virStorageSourcePtr src, - virJSONValuePtr json, - const char *jsonstr, - int opaque G_GNUC_UNUSED) -{ - bool has_offset =3D virJSONValueObjectHasKey(json, "offset"); - bool has_size =3D virJSONValueObjectHasKey(json, "size"); - virJSONValuePtr file; - - if (has_offset || has_size) { - src->sliceStorage =3D g_new0(virStorageSourceSlice, 1); - - if (has_offset && - virJSONValueObjectGetNumberUlong(json, "offset", &src->sliceSt= orage->offset) < 0) { - virReportError(VIR_ERR_INVALID_ARG, "%s", - _("malformed 'offset' property of 'raw' driver"= )); - return -1; - } - - if (has_size && - virJSONValueObjectGetNumberUlong(json, "size", &src->sliceStor= age->size) < 0) { - virReportError(VIR_ERR_INVALID_ARG, "%s", - _("malformed 'size' property of 'raw' driver")); - return -1; - } - } - - /* 'raw' is a format driver so it can have protocol driver children */ - if (!(file =3D virJSONValueObjectGetObject(json, "file"))) { - virReportError(VIR_ERR_INVALID_ARG, - _("JSON backing volume definition '%s' lacks 'file'= object"), - jsonstr); - return -1; - } - - return virStorageSourceParseBackingJSONInternal(src, file, jsonstr, fa= lse); -} - - -static int -virStorageSourceParseBackingJSONVxHS(virStorageSourcePtr src, - virJSONValuePtr json, - const char *jsonstr G_GNUC_UNUSED, - int opaque G_GNUC_UNUSED) -{ - const char *vdisk_id =3D virJSONValueObjectGetString(json, "vdisk-id"); - virJSONValuePtr server =3D virJSONValueObjectGetObject(json, "server"); - - if (!vdisk_id || !server) { - virReportError(VIR_ERR_INVALID_ARG, "%s", - _("missing 'vdisk-id' or 'server' attribute in " - "JSON backing definition for VxHS volume")); - return -1; - } - - src->type =3D VIR_STORAGE_TYPE_NETWORK; - src->protocol =3D VIR_STORAGE_NET_PROTOCOL_VXHS; - - src->path =3D g_strdup(vdisk_id); - - src->hosts =3D g_new0(virStorageNetHostDef, 1); - src->nhosts =3D 1; - - if (virStorageSourceParseBackingJSONInetSocketAddress(src->hosts, - server) < 0) - return -1; - - return 0; -} - - -static int -virStorageSourceParseBackingJSONNFS(virStorageSourcePtr src, - virJSONValuePtr json, - const char *jsonstr G_GNUC_UNUSED, - int opaque G_GNUC_UNUSED) -{ - virJSONValuePtr server =3D virJSONValueObjectGetObject(json, "server"); - int uidStore =3D -1; - int gidStore =3D -1; - int gotUID =3D virJSONValueObjectGetNumberInt(json, "user", &uidStore); - int gotGID =3D virJSONValueObjectGetNumberInt(json, "group", &gidStore= ); - - if (!server) { - virReportError(VIR_ERR_INVALID_ARG, "%s", - _("missing 'server' attribute in JSON backing defin= ition for NFS volume")); - return -1; - } - - if (gotUID < 0 || gotGID < 0) { - virReportError(VIR_ERR_INVALID_ARG, "%s", - _("missing 'user' or 'group' attribute in JSON back= ing definition for NFS volume")); - return -1; - } - - src->path =3D g_strdup(virJSONValueObjectGetString(json, "path")); - if (!src->path) { - virReportError(VIR_ERR_INVALID_ARG, "%s", - _("missing 'path' attribute in JSON backing definit= ion for NFS volume")); - return -1; - } - - src->nfs_user =3D g_strdup_printf("+%d", uidStore); - src->nfs_group =3D g_strdup_printf("+%d", gidStore); - - src->type =3D VIR_STORAGE_TYPE_NETWORK; - src->protocol =3D VIR_STORAGE_NET_PROTOCOL_NFS; - - src->hosts =3D g_new0(virStorageNetHostDef, 1); - src->nhosts =3D 1; - - if (virStorageSourceParseBackingJSONInetSocketAddress(src->hosts, - server) < 0) - return -1; - - return 0; -} - - -static int -virStorageSourceParseBackingJSONNVMe(virStorageSourcePtr src, - virJSONValuePtr json, - const char *jsonstr G_GNUC_UNUSED, - int opaque G_GNUC_UNUSED) -{ - g_autoptr(virStorageSourceNVMeDef) nvme =3D g_new0(virStorageSourceNVM= eDef, 1); - const char *device =3D virJSONValueObjectGetString(json, "device"); - - if (!device || virPCIDeviceAddressParse((char *) device, &nvme->pciAdd= r) < 0) { - virReportError(VIR_ERR_INVALID_ARG, "%s", - _("missing or malformed 'device' field of 'nvme' st= orage")); - return -1; - } - - if (virJSONValueObjectGetNumberUlong(json, "namespace", &nvme->namespc= ) < 0 || - nvme->namespc =3D=3D 0) { - virReportError(VIR_ERR_INVALID_ARG, "%s", - _("missing or malformed 'namespace' field of 'nvme'= storage")); - return -1; - } - - src->type =3D VIR_STORAGE_TYPE_NVME; - src->nvme =3D g_steal_pointer(&nvme); - - return 0; -} - - -struct virStorageSourceJSONDriverParser { - const char *drvname; - bool formatdriver; - /** - * The callback gets a pre-allocated storage source @src and the JSON - * object to parse. The callback shall return -1 on error and report e= rror - * 0 on success and 1 in cases when the configuration itself is valid,= but - * can't be converted to libvirt's configuration (e.g. inline authenti= cation - * credentials are present). - */ - int (*func)(virStorageSourcePtr src, virJSONValuePtr json, const char = *jsonstr, int opaque); - int opaque; -}; - -static const struct virStorageSourceJSONDriverParser jsonParsers[] =3D { - {"file", false, virStorageSourceParseBackingJSONPath, VIR_STORAGE_TYPE= _FILE}, - {"host_device", false, virStorageSourceParseBackingJSONPath, VIR_STORA= GE_TYPE_BLOCK}, - {"host_cdrom", false, virStorageSourceParseBackingJSONPath, VIR_STORAG= E_TYPE_BLOCK}, - {"http", false, virStorageSourceParseBackingJSONUri, VIR_STORAGE_NET_P= ROTOCOL_HTTP}, - {"https", false, virStorageSourceParseBackingJSONUri, VIR_STORAGE_NET_= PROTOCOL_HTTPS}, - {"ftp", false, virStorageSourceParseBackingJSONUri, VIR_STORAGE_NET_PR= OTOCOL_FTP}, - {"ftps", false, virStorageSourceParseBackingJSONUri, VIR_STORAGE_NET_P= ROTOCOL_FTPS}, - {"tftp", false, virStorageSourceParseBackingJSONUri, VIR_STORAGE_NET_P= ROTOCOL_TFTP}, - {"gluster", false, virStorageSourceParseBackingJSONGluster, 0}, - {"iscsi", false, virStorageSourceParseBackingJSONiSCSI, 0}, - {"nbd", false, virStorageSourceParseBackingJSONNbd, 0}, - {"sheepdog", false, virStorageSourceParseBackingJSONSheepdog, 0}, - {"ssh", false, virStorageSourceParseBackingJSONSSH, 0}, - {"rbd", false, virStorageSourceParseBackingJSONRBD, 0}, - {"raw", true, virStorageSourceParseBackingJSONRaw, 0}, - {"nfs", false, virStorageSourceParseBackingJSONNFS, 0}, - {"vxhs", false, virStorageSourceParseBackingJSONVxHS, 0}, - {"nvme", false, virStorageSourceParseBackingJSONNVMe, 0}, -}; - - - -static int -virStorageSourceParseBackingJSONInternal(virStorageSourcePtr src, - virJSONValuePtr json, - const char *jsonstr, - bool allowformat) -{ - const char *drvname; - size_t i; - - if (!(drvname =3D virJSONValueObjectGetString(json, "driver"))) { - virReportError(VIR_ERR_INVALID_ARG, - _("JSON backing volume definition '%s' lacks driver= name"), - jsonstr); - return -1; - } - - for (i =3D 0; i < G_N_ELEMENTS(jsonParsers); i++) { - if (STRNEQ(drvname, jsonParsers[i].drvname)) - continue; - - if (jsonParsers[i].formatdriver && !allowformat) { - virReportError(VIR_ERR_INVALID_ARG, - _("JSON backing volume definition '%s' must not= have nested format drivers"), - jsonstr); - return -1; - } - - return jsonParsers[i].func(src, json, jsonstr, jsonParsers[i].opaq= ue); - } - - virReportError(VIR_ERR_INTERNAL_ERROR, - _("missing parser implementation for JSON backing volum= e " - "driver '%s'"), drvname); - return -1; -} - - -static int -virStorageSourceParseBackingJSON(virStorageSourcePtr src, - const char *json) -{ - g_autoptr(virJSONValue) root =3D NULL; - g_autoptr(virJSONValue) deflattened =3D NULL; - virJSONValuePtr file =3D NULL; - - if (!(root =3D virJSONValueFromString(json))) - return -1; - - if (!(deflattened =3D virJSONValueObjectDeflatten(root))) - return -1; - - /* There are 2 possible syntaxes: - * 1) json:{"file":{"driver":...}} - * 2) json:{"driver":...} - * Remove the 'file' wrapper object in case 1. - */ - if (!virJSONValueObjectHasKey(deflattened, "driver")) - file =3D virJSONValueObjectGetObject(deflattened, "file"); - - if (!file) - file =3D deflattened; - - return virStorageSourceParseBackingJSONInternal(src, file, json, true); -} - - -/** - * virStorageSourceNewFromBackingAbsolute - * @path: string representing absolute location of a storage source - * @src: filled with virStorageSource object representing @path - * - * Returns 0 on success, 1 if we could parse all location data but @path - * specified other data unrepresentable by libvirt (e.g. inline authentica= tion). - * In both cases @src is filled. On error -1 is returned @src is NULL and = an - * error is reported. - */ -int -virStorageSourceNewFromBackingAbsolute(const char *path, - virStorageSourcePtr *src) -{ - const char *json; - const char *dirpath; - int rc =3D 0; - g_autoptr(virStorageSource) def =3D virStorageSourceNew(); - - *src =3D NULL; - - if (virStorageIsFile(path)) { - def->type =3D VIR_STORAGE_TYPE_FILE; - - def->path =3D g_strdup(path); - } else { - if ((dirpath =3D STRSKIP(path, "fat:"))) { - def->type =3D VIR_STORAGE_TYPE_DIR; - def->format =3D VIR_STORAGE_FILE_FAT; - def->path =3D g_strdup(dirpath); - *src =3D g_steal_pointer(&def); - return 0; - } - - def->type =3D VIR_STORAGE_TYPE_NETWORK; - - VIR_DEBUG("parsing backing store string: '%s'", path); - - /* handle URI formatted backing stores */ - if ((json =3D STRSKIP(path, "json:"))) - rc =3D virStorageSourceParseBackingJSON(def, json); - else if (strstr(path, "://")) - rc =3D virStorageSourceParseBackingURI(def, path); - else - rc =3D virStorageSourceParseBackingColon(def, path); - - if (rc < 0) - return -1; - - virStorageSourceNetworkAssignDefaultPorts(def); - - /* Some of the legacy parsers parse authentication data since they= are - * also used in other places. For backing store detection the - * authentication data would be invalid anyways, so we clear it */ - if (def->auth) { - virStorageAuthDefFree(def->auth); - def->auth =3D NULL; - } - } - - *src =3D g_steal_pointer(&def); - return rc; -} - - -/** - * virStorageSourceNewFromChild: - * @parent: storage source parent - * @child: returned child/backing store definition - * @parentRaw: raw child string (backingStoreRaw) - * - * Creates a storage source which describes the backing image of @parent a= nd - * fills it into @backing depending on the passed parentRaw (backingStoreR= aw) - * and other data. Note that for local storage this function accesses the = file - * to update the actual type of the child store. - * - * Returns 0 on success, 1 if we could parse all location data but the chi= ld - * store specification contained other data unrepresentable by libvirt (e.= g. - * inline authentication). - * In both cases @src is filled. On error -1 is returned @src is NULL and = an - * error is reported. - */ -static int -virStorageSourceNewFromChild(virStorageSourcePtr parent, - const char *parentRaw, - virStorageSourcePtr *child) -{ - struct stat st; - g_autoptr(virStorageSource) def =3D NULL; - int rc =3D 0; - - *child =3D NULL; - - if (virStorageIsRelative(parentRaw)) { - if (!(def =3D virStorageSourceNewFromBackingRelative(parent, paren= tRaw))) - return -1; - } else { - if ((rc =3D virStorageSourceNewFromBackingAbsolute(parentRaw, &def= )) < 0) - return -1; - } - - /* possibly update local type */ - if (def->type =3D=3D VIR_STORAGE_TYPE_FILE) { - if (stat(def->path, &st) =3D=3D 0) { - if (S_ISDIR(st.st_mode)) { - def->type =3D VIR_STORAGE_TYPE_DIR; - def->format =3D VIR_STORAGE_FILE_DIR; - } else if (S_ISBLK(st.st_mode)) { - def->type =3D VIR_STORAGE_TYPE_BLOCK; - } - } - } - - /* copy parent's labelling and other top level stuff */ - if (virStorageSourceInitChainElement(def, parent, true) < 0) - return -1; - - def->detected =3D true; - - *child =3D g_steal_pointer(&def); - return rc; -} - - -int -virStorageSourceNewFromBacking(virStorageSourcePtr parent, - virStorageSourcePtr *backing) -{ - int rc; - - if ((rc =3D virStorageSourceNewFromChild(parent, - parent->backingStoreRaw, - backing)) < 0) - return rc; - - (*backing)->format =3D parent->backingStoreRawFormat; - (*backing)->readonly =3D true; - return rc; -} - - -/** - * @src: disk source definition structure - * @fd: file descriptor - * @sb: stat buffer - * - * Updates src->physical depending on the actual type of storage being use= d. - * To be called for domain storage source reporting as the volume code does - * not set/use the 'type' field for the voldef->source.target - * - * Returns 0 on success, -1 on error. No libvirt errors are reported. - */ -int -virStorageSourceUpdatePhysicalSize(virStorageSourcePtr src, - int fd, - struct stat const *sb) -{ - off_t end; - virStorageType actual_type =3D virStorageSourceGetActualType(src); - - switch (actual_type) { - case VIR_STORAGE_TYPE_FILE: - case VIR_STORAGE_TYPE_NETWORK: - src->physical =3D sb->st_size; - break; - - case VIR_STORAGE_TYPE_BLOCK: - if ((end =3D lseek(fd, 0, SEEK_END)) =3D=3D (off_t) -1) - return -1; - - src->physical =3D end; - break; - - case VIR_STORAGE_TYPE_DIR: - src->physical =3D 0; - break; - - /* We shouldn't get VOLUME, but the switch requires all cases */ - case VIR_STORAGE_TYPE_VOLUME: - case VIR_STORAGE_TYPE_NVME: - case VIR_STORAGE_TYPE_NONE: - case VIR_STORAGE_TYPE_LAST: - return -1; - } - - return 0; -} - - -/** - * @src: disk source definition structure - * @fd: file descriptor - * @sb: stat buffer - * - * Update the capacity, allocation, physical values for the storage @src - * Shared between the domain storage source for an inactive domain and the - * voldef source target as the result is not affected by the 'type' field. - * - * Returns 0 on success, -1 on error. - */ -int -virStorageSourceUpdateBackingSizes(virStorageSourcePtr src, - int fd, - struct stat const *sb) -{ - /* Get info for normal formats */ - if (S_ISREG(sb->st_mode) || fd =3D=3D -1) { -#ifndef WIN32 - src->allocation =3D (unsigned long long)sb->st_blocks * - (unsigned long long)DEV_BSIZE; -#else - src->allocation =3D sb->st_size; -#endif - /* Regular files may be sparse, so logical size (capacity) is not = same - * as actual allocation above - */ - src->capacity =3D sb->st_size; - - /* Allocation tracks when the file is sparse, physical is the - * last offset of the file. */ - src->physical =3D sb->st_size; - } else if (S_ISDIR(sb->st_mode)) { - src->allocation =3D 0; - src->capacity =3D 0; - src->physical =3D 0; - } else if (fd >=3D 0) { - off_t end; - - /* XXX this is POSIX compliant, but doesn't work for CHAR files, - * only BLOCK. There is a Linux specific ioctl() for getting - * size of both CHAR / BLOCK devices we should check for in - * configure - * - * NB. Because we configure with AC_SYS_LARGEFILE, off_t - * should be 64 bits on all platforms. For block devices, we - * have to seek (safe even if someone else is writing) to - * determine physical size, and assume that allocation is the - * same as physical (but can refine that assumption later if - * qemu is still running). - */ - if ((end =3D lseek(fd, 0, SEEK_END)) =3D=3D (off_t)-1) { - virReportSystemError(errno, - _("failed to seek to end of %s"), src->pa= th); - return -1; - } - src->physical =3D end; - src->allocation =3D end; - src->capacity =3D end; - } - - return 0; -} - - -/** - * @src: disk source definition structure - * @buf: buffer to the storage file header - * @len: length of the storage file header - * - * Update the storage @src capacity. - * - * Returns 0 on success, -1 on error. - */ -int -virStorageSourceUpdateCapacity(virStorageSourcePtr src, - char *buf, - ssize_t len) -{ - int format =3D src->format; - g_autoptr(virStorageSource) meta =3D NULL; - - /* Raw files: capacity is physical size. For all other files: if - * the metadata has a capacity, use that, otherwise fall back to - * physical size. */ - if (format =3D=3D VIR_STORAGE_FILE_NONE) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("no disk format for %s was specified"), - src->path); - return -1; - } - - if (format =3D=3D VIR_STORAGE_FILE_RAW && !src->encryption) { - src->capacity =3D src->physical; - } else if ((meta =3D virStorageFileGetMetadataFromBuf(src->path, buf, - len, format))) { - src->capacity =3D meta->capacity ? meta->capacity : src->physical; - if (src->encryption && meta->encryption) - src->encryption->payload_offset =3D meta->encryption->payload_= offset; - } else { - return -1; - } - - if (src->encryption && src->encryption->payload_offset !=3D -1) - src->capacity -=3D src->encryption->payload_offset * 512; - - return 0; -} - - static char * virStorageFileCanonicalizeFormatPath(char **components, size_t ncomponents, @@ -3414,76 +1644,6 @@ virStorageFileCanonicalizePath(const char *path, } =20 =20 -/** - * virStorageFileRemoveLastPathComponent: - * - * @path: Path string to remove the last component from - * - * Removes the last path component of a path. This function is designed to= be - * called on file paths only (no trailing slashes in @path). Caller is - * responsible to free the returned string. - */ -static char * -virStorageFileRemoveLastPathComponent(const char *path) -{ - char *ret; - - ret =3D g_strdup(NULLSTR_EMPTY(path)); - - virFileRemoveLastComponent(ret); - - return ret; -} - - -/* - * virStorageFileGetRelativeBackingPath: - * - * Resolve relative path to be written to the overlay of @top image when - * collapsing the backing chain between @top and @base. - * - * Returns 0 on success; 1 if backing chain isn't relative and -1 on error. - */ -int -virStorageFileGetRelativeBackingPath(virStorageSourcePtr top, - virStorageSourcePtr base, - char **relpath) -{ - virStorageSourcePtr next; - g_autofree char *tmp =3D NULL; - g_autofree char *path =3D NULL; - - *relpath =3D NULL; - - for (next =3D top; virStorageSourceIsBacking(next); next =3D next->bac= kingStore) { - if (!next->relPath) - return 1; - - if (!(tmp =3D virStorageFileRemoveLastPathComponent(path))) - return -1; - - VIR_FREE(path); - - path =3D g_strdup_printf("%s%s", tmp, next->relPath); - - VIR_FREE(tmp); - - if (next =3D=3D base) - break; - } - - if (next !=3D base) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("failed to resolve relative backing name: " - "base image is not in backing chain")); - return -1; - } - - *relpath =3D g_steal_pointer(&path); - return 0; -} - - /** * virStorageSourceIsRelative: * @src: storage source to check @@ -3636,736 +1796,3 @@ virStorageSourceInitiatorClear(virStorageSourceInit= iatorDefPtr initiator) { VIR_FREE(initiator->iqn); } - -static bool -virStorageFileIsInitialized(const virStorageSource *src) -{ - return src && src->drv; -} - - -/** - * virStorageFileGetBackendForSupportCheck: - * @src: storage source to check support for - * @backend: pointer to the storage backend for @src if it's supported - * - * Returns 0 if @src is not supported by any storage backend currently lin= ked - * 1 if it is supported and -1 on error with an error reported. - */ -static int -virStorageFileGetBackendForSupportCheck(const virStorageSource *src, - virStorageFileBackendPtr *backend) -{ - int actualType; - - - if (!src) { - *backend =3D NULL; - return 0; - } - - if (src->drv) { - virStorageDriverDataPtr drv =3D src->drv; - *backend =3D drv->backend; - return 1; - } - - actualType =3D virStorageSourceGetActualType(src); - - if (virStorageFileBackendForType(actualType, src->protocol, false, bac= kend) < 0) - return -1; - - if (!*backend) - return 0; - - return 1; -} - - -int -virStorageFileSupportsBackingChainTraversal(const virStorageSource *src) -{ - virStorageFileBackendPtr backend; - int rv; - - if ((rv =3D virStorageFileGetBackendForSupportCheck(src, &backend)) < = 1) - return rv; - - return backend->storageFileGetUniqueIdentifier && - backend->storageFileRead && - backend->storageFileAccess ? 1 : 0; -} - - -/** - * virStorageFileSupportsSecurityDriver: - * - * @src: a storage file structure - * - * Check if a storage file supports operations needed by the security - * driver to perform labelling - */ -int -virStorageFileSupportsSecurityDriver(const virStorageSource *src) -{ - virStorageFileBackendPtr backend; - int rv; - - if ((rv =3D virStorageFileGetBackendForSupportCheck(src, &backend)) < = 1) - return rv; - - return backend->storageFileChown ? 1 : 0; -} - - -/** - * virStorageFileSupportsAccess: - * - * @src: a storage file structure - * - * Check if a storage file supports checking if the storage source is acce= ssible - * for the given vm. - */ -int -virStorageFileSupportsAccess(const virStorageSource *src) -{ - virStorageFileBackendPtr backend; - int rv; - - if ((rv =3D virStorageFileGetBackendForSupportCheck(src, &backend)) < = 1) - return rv; - - return backend->storageFileAccess ? 1 : 0; -} - - -/** - * virStorageFileSupportsCreate: - * @src: a storage file structure - * - * Check if the storage driver supports creating storage described by @src - * via virStorageFileCreate. - */ -int -virStorageFileSupportsCreate(const virStorageSource *src) -{ - virStorageFileBackendPtr backend; - int rv; - - if ((rv =3D virStorageFileGetBackendForSupportCheck(src, &backend)) < = 1) - return rv; - - return backend->storageFileCreate ? 1 : 0; -} - - -void -virStorageFileDeinit(virStorageSourcePtr src) -{ - virStorageDriverDataPtr drv =3D NULL; - - if (!virStorageFileIsInitialized(src)) - return; - - drv =3D src->drv; - - if (drv->backend && - drv->backend->backendDeinit) - drv->backend->backendDeinit(src); - - VIR_FREE(src->drv); -} - - -/** - * virStorageFileInitAs: - * - * @src: storage source definition - * @uid: uid used to access the file, or -1 for current uid - * @gid: gid used to access the file, or -1 for current gid - * - * Initialize a storage source to be used with storage driver. Use the pro= vided - * uid and gid if possible for the operations. - * - * Returns 0 if the storage file was successfully initialized, -1 if the - * initialization failed. Libvirt error is reported. - */ -int -virStorageFileInitAs(virStorageSourcePtr src, - uid_t uid, gid_t gid) -{ - int actualType =3D virStorageSourceGetActualType(src); - virStorageDriverDataPtr drv =3D g_new0(virStorageDriverData, 1); - - src->drv =3D drv; - - if (uid =3D=3D (uid_t) -1) - drv->uid =3D geteuid(); - else - drv->uid =3D uid; - - if (gid =3D=3D (gid_t) -1) - drv->gid =3D getegid(); - else - drv->gid =3D gid; - - if (virStorageFileBackendForType(actualType, - src->protocol, - true, - &drv->backend) < 0) - goto error; - - if (drv->backend->backendInit && - drv->backend->backendInit(src) < 0) - goto error; - - return 0; - - error: - VIR_FREE(src->drv); - return -1; -} - - -/** - * virStorageFileInit: - * - * See virStorageFileInitAs. The file is initialized to be accessed by the - * current user. - */ -int -virStorageFileInit(virStorageSourcePtr src) -{ - return virStorageFileInitAs(src, -1, -1); -} - - -/** - * virStorageFileCreate: Creates an empty storage file via storage driver - * - * @src: file structure pointing to the file - * - * Returns 0 on success, -2 if the function isn't supported by the backend, - * -1 on other failure. Errno is set in case of failure. - */ -int -virStorageFileCreate(virStorageSourcePtr src) -{ - virStorageDriverDataPtr drv =3D NULL; - int ret; - - if (!virStorageFileIsInitialized(src)) { - errno =3D ENOSYS; - return -2; - } - - drv =3D src->drv; - - if (!drv->backend->storageFileCreate) { - errno =3D ENOSYS; - return -2; - } - - ret =3D drv->backend->storageFileCreate(src); - - VIR_DEBUG("created storage file %p: ret=3D%d, errno=3D%d", - src, ret, errno); - - return ret; -} - - -/** - * virStorageFileUnlink: Unlink storage file via storage driver - * - * @src: file structure pointing to the file - * - * Unlinks the file described by the @file structure. - * - * Returns 0 on success, -2 if the function isn't supported by the backend, - * -1 on other failure. Errno is set in case of failure. - */ -int -virStorageFileUnlink(virStorageSourcePtr src) -{ - virStorageDriverDataPtr drv =3D NULL; - int ret; - - if (!virStorageFileIsInitialized(src)) { - errno =3D ENOSYS; - return -2; - } - - drv =3D src->drv; - - if (!drv->backend->storageFileUnlink) { - errno =3D ENOSYS; - return -2; - } - - ret =3D drv->backend->storageFileUnlink(src); - - VIR_DEBUG("unlinked storage file %p: ret=3D%d, errno=3D%d", - src, ret, errno); - - return ret; -} - - -/** - * virStorageFileStat: returns stat struct of a file via storage driver - * - * @src: file structure pointing to the file - * @stat: stat structure to return data - * - * Returns 0 on success, -2 if the function isn't supported by the backend, - * -1 on other failure. Errno is set in case of failure. -*/ -int -virStorageFileStat(virStorageSourcePtr src, - struct stat *st) -{ - virStorageDriverDataPtr drv =3D NULL; - int ret; - - if (!virStorageFileIsInitialized(src)) { - errno =3D ENOSYS; - return -2; - } - - drv =3D src->drv; - - if (!drv->backend->storageFileStat) { - errno =3D ENOSYS; - return -2; - } - - ret =3D drv->backend->storageFileStat(src, st); - - VIR_DEBUG("stat of storage file %p: ret=3D%d, errno=3D%d", - src, ret, errno); - - return ret; -} - - -/** - * virStorageFileRead: read bytes from a file into a buffer - * - * @src: file structure pointing to the file - * @offset: number of bytes to skip in the storage file - * @len: maximum number of bytes read from the storage file - * @buf: buffer to read the data into. (buffer shall be freed by caller) - * - * Returns the count of bytes read on success and -1 on failure, -2 if the - * function isn't supported by the backend. - * Libvirt error is reported on failure. - */ -ssize_t -virStorageFileRead(virStorageSourcePtr src, - size_t offset, - size_t len, - char **buf) -{ - virStorageDriverDataPtr drv =3D NULL; - ssize_t ret; - - if (!virStorageFileIsInitialized(src)) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("storage file backend not initialized")); - return -1; - } - - drv =3D src->drv; - - if (!drv->backend->storageFileRead) - return -2; - - ret =3D drv->backend->storageFileRead(src, offset, len, buf); - - VIR_DEBUG("read '%zd' bytes from storage '%p' starting at offset '%zu'= ", - ret, src, offset); - - return ret; -} - - -/* - * virStorageFileGetUniqueIdentifier: Get a unique string describing the v= olume - * - * @src: file structure pointing to the file - * - * Returns a string uniquely describing a single volume (canonical path). - * The string shall not be freed and is valid until the storage file is - * deinitialized. Returns NULL on error and sets a libvirt error code */ -const char * -virStorageFileGetUniqueIdentifier(virStorageSourcePtr src) -{ - virStorageDriverDataPtr drv =3D NULL; - - if (!virStorageFileIsInitialized(src)) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("storage file backend not initialized")); - return NULL; - } - - drv =3D src->drv; - - if (!drv->backend->storageFileGetUniqueIdentifier) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unique storage file identifier not implemented f= or " - "storage type %s (protocol: %s)'"), - virStorageTypeToString(src->type), - virStorageNetProtocolTypeToString(src->protocol)); - return NULL; - } - - return drv->backend->storageFileGetUniqueIdentifier(src); -} - - -/** - * virStorageFileAccess: Check accessibility of a storage file - * - * @src: storage file to check access permissions - * @mode: accessibility check options (see man 2 access) - * - * Returns 0 on success, -1 on error and sets errno. No libvirt - * error is reported. Returns -2 if the operation isn't supported - * by libvirt storage backend. - */ -int -virStorageFileAccess(virStorageSourcePtr src, - int mode) -{ - virStorageDriverDataPtr drv =3D NULL; - - if (!virStorageFileIsInitialized(src)) { - errno =3D ENOSYS; - return -2; - } - - drv =3D src->drv; - - if (!drv->backend->storageFileAccess) { - errno =3D ENOSYS; - return -2; - } - - return drv->backend->storageFileAccess(src, mode); -} - - -/** - * virStorageFileChown: Change owner of a storage file - * - * @src: storage file to change owner of - * @uid: new owner id - * @gid: new group id - * - * Returns 0 on success, -1 on error and sets errno. No libvirt - * error is reported. Returns -2 if the operation isn't supported - * by libvirt storage backend. - */ -int -virStorageFileChown(const virStorageSource *src, - uid_t uid, - gid_t gid) -{ - virStorageDriverDataPtr drv =3D NULL; - - if (!virStorageFileIsInitialized(src)) { - errno =3D ENOSYS; - return -2; - } - - drv =3D src->drv; - - if (!drv->backend->storageFileChown) { - errno =3D ENOSYS; - return -2; - } - - VIR_DEBUG("chown of storage file %p to %u:%u", - src, (unsigned int)uid, (unsigned int)gid); - - return drv->backend->storageFileChown(src, uid, gid); -} - - -/** - * virStorageFileReportBrokenChain: - * - * @errcode: errno when accessing @src - * @src: inaccessible file in the backing chain of @parent - * @parent: root virStorageSource being checked - * - * Reports the correct error message if @src is missing in the backing cha= in - * for @parent. - */ -void -virStorageFileReportBrokenChain(int errcode, - virStorageSourcePtr src, - virStorageSourcePtr parent) -{ - if (src->drv) { - virStorageDriverDataPtr drv =3D src->drv; - unsigned int access_user =3D drv->uid; - unsigned int access_group =3D drv->gid; - - if (src =3D=3D parent) { - virReportSystemError(errcode, - _("Cannot access storage file '%s' " - "(as uid:%u, gid:%u)"), - src->path, access_user, access_group); - } else { - virReportSystemError(errcode, - _("Cannot access backing file '%s' " - "of storage file '%s' (as uid:%u, gid:%= u)"), - src->path, parent->path, access_user, acc= ess_group); - } - } else { - if (src =3D=3D parent) { - virReportSystemError(errcode, - _("Cannot access storage file '%s'"), - src->path); - } else { - virReportSystemError(errcode, - _("Cannot access backing file '%s' " - "of storage file '%s'"), - src->path, parent->path); - } - } -} - - -static int -virStorageFileGetMetadataRecurseReadHeader(virStorageSourcePtr src, - virStorageSourcePtr parent, - uid_t uid, - gid_t gid, - char **buf, - size_t *headerLen, - GHashTable *cycle) -{ - int ret =3D -1; - const char *uniqueName; - ssize_t len; - - if (virStorageFileInitAs(src, uid, gid) < 0) - return -1; - - if (virStorageFileAccess(src, F_OK) < 0) { - virStorageFileReportBrokenChain(errno, src, parent); - goto cleanup; - } - - if (!(uniqueName =3D virStorageFileGetUniqueIdentifier(src))) - goto cleanup; - - if (virHashHasEntry(cycle, uniqueName)) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("backing store for %s (%s) is self-referential"), - NULLSTR(src->path), uniqueName); - goto cleanup; - } - - if (virHashAddEntry(cycle, uniqueName, NULL) < 0) - goto cleanup; - - if ((len =3D virStorageFileRead(src, 0, VIR_STORAGE_MAX_HEADER, buf)) = < 0) - goto cleanup; - - *headerLen =3D len; - ret =3D 0; - - cleanup: - virStorageFileDeinit(src); - return ret; -} - - -/* Recursive workhorse for virStorageFileGetMetadata. */ -static int -virStorageFileGetMetadataRecurse(virStorageSourcePtr src, - virStorageSourcePtr parent, - uid_t uid, gid_t gid, - bool report_broken, - GHashTable *cycle, - unsigned int depth) -{ - virStorageFileFormat orig_format =3D src->format; - size_t headerLen; - int rv; - g_autofree char *buf =3D NULL; - g_autoptr(virStorageSource) backingStore =3D NULL; - - VIR_DEBUG("path=3D%s format=3D%d uid=3D%u gid=3D%u", - NULLSTR(src->path), src->format, - (unsigned int)uid, (unsigned int)gid); - - if (src->format =3D=3D VIR_STORAGE_FILE_AUTO_SAFE) - src->format =3D VIR_STORAGE_FILE_AUTO; - - /* exit if we can't load information about the current image */ - rv =3D virStorageFileSupportsBackingChainTraversal(src); - if (rv <=3D 0) { - if (orig_format =3D=3D VIR_STORAGE_FILE_AUTO) - return -2; - - return rv; - } - - if (virStorageFileGetMetadataRecurseReadHeader(src, parent, uid, gid, - &buf, &headerLen, cycle= ) < 0) - return -1; - - if (virStorageFileProbeGetMetadata(src, buf, headerLen) < 0) - return -1; - - /* If we probed the format we MUST ensure that nothing else than the c= urrent - * image is considered for security labelling and/or recursion. */ - if (orig_format =3D=3D VIR_STORAGE_FILE_AUTO) { - if (src->backingStoreRaw) { - src->format =3D VIR_STORAGE_FILE_RAW; - VIR_FREE(src->backingStoreRaw); - return -2; - } - } - - if (src->backingStoreRaw) { - if ((rv =3D virStorageSourceNewFromBacking(src, &backingStore)) < = 0) - return -1; - - /* the backing file would not be usable for VM usage */ - if (rv =3D=3D 1) - return 0; - - if ((rv =3D virStorageFileGetMetadataRecurse(backingStore, parent, - uid, gid, - report_broken, - cycle, depth + 1)) < 0)= { - if (!report_broken) - return 0; - - if (rv =3D=3D -2) { - virReportError(VIR_ERR_OPERATION_INVALID, - _("format of backing image '%s' of image '%= s' was not specified in the image metadata " - "(See https://libvirt.org/kbase/backing_c= hains.html for troubleshooting)"), - src->backingStoreRaw, NULLSTR(src->path)); - } - - return -1; - } - - backingStore->id =3D depth; - src->backingStore =3D g_steal_pointer(&backingStore); - } else { - /* add terminator */ - src->backingStore =3D virStorageSourceNew(); - } - - return 0; -} - - -/** - * virStorageFileGetMetadata: - * - * Extract metadata about the storage volume with the specified - * image format. If image format is VIR_STORAGE_FILE_AUTO, it - * will probe to automatically identify the format. Recurses through - * the entire chain. - * - * Open files using UID and GID (or pass -1 for the current user/group). - * Treat any backing files without explicit type as raw, unless ALLOW_PROB= E. - * - * Callers are advised never to use VIR_STORAGE_FILE_AUTO as a - * format, since a malicious guest can turn a raw file into any - * other non-raw format at will. - * - * If @report_broken is true, the whole function fails with a possibly sane - * error instead of just returning a broken chain. Note that the inability= for - * libvirt to traverse a given source is not considered an error. - * - * Caller MUST free result after use via virObjectUnref. - */ -int -virStorageFileGetMetadata(virStorageSourcePtr src, - uid_t uid, gid_t gid, - bool report_broken) -{ - GHashTable *cycle =3D NULL; - virStorageType actualType =3D virStorageSourceGetActualType(src); - int ret =3D -1; - - VIR_DEBUG("path=3D%s format=3D%d uid=3D%u gid=3D%u report_broken=3D%d", - src->path, src->format, (unsigned int)uid, (unsigned int)gid, - report_broken); - - if (!(cycle =3D virHashNew(NULL))) - return -1; - - if (src->format <=3D VIR_STORAGE_FILE_NONE) { - if (actualType =3D=3D VIR_STORAGE_TYPE_DIR) - src->format =3D VIR_STORAGE_FILE_DIR; - else - src->format =3D VIR_STORAGE_FILE_RAW; - } - - ret =3D virStorageFileGetMetadataRecurse(src, src, uid, gid, - report_broken, cycle, 1); - - virHashFree(cycle); - return ret; -} - - -/** - * virStorageFileGetBackingStoreStr: - * @src: storage object - * - * Extracts the backing store string as stored in the storage volume descr= ibed - * by @src and returns it to the user. Caller is responsible for freeing i= t. - * In case when the string can't be retrieved or does not exist NULL is - * returned. - */ -int -virStorageFileGetBackingStoreStr(virStorageSourcePtr src, - char **backing) -{ - ssize_t headerLen; - int rv; - g_autofree char *buf =3D NULL; - g_autoptr(virStorageSource) tmp =3D NULL; - - *backing =3D NULL; - - /* exit if we can't load information about the current image */ - if (!virStorageFileSupportsBackingChainTraversal(src)) - return 0; - - rv =3D virStorageFileAccess(src, F_OK); - if (rv =3D=3D -2) - return 0; - if (rv < 0) { - virStorageFileReportBrokenChain(errno, src, src); - return -1; - } - - if ((headerLen =3D virStorageFileRead(src, 0, VIR_STORAGE_MAX_HEADER, - &buf)) < 0) { - if (headerLen =3D=3D -2) - return 0; - return -1; - } - - if (!(tmp =3D virStorageSourceCopy(src, false))) - return -1; - - if (virStorageFileProbeGetMetadata(tmp, buf, headerLen) < 0) - return -1; - - *backing =3D g_steal_pointer(&tmp->backingStoreRaw); - return 0; -} diff --git a/src/util/virstoragefile.h b/src/util/virstoragefile.h index 27ac6a493f..d6f194a7dd 100644 --- a/src/util/virstoragefile.h +++ b/src/util/virstoragefile.h @@ -388,18 +388,6 @@ struct _virStorageSource { G_DEFINE_AUTOPTR_CLEANUP_FUNC(virStorageSource, virObjectUnref); =20 =20 -#ifndef DEV_BSIZE -# define DEV_BSIZE 512 -#endif - -virStorageSourcePtr virStorageFileGetMetadataFromFD(const char *path, - int fd, - int format); -virStorageSourcePtr virStorageFileGetMetadataFromBuf(const char *path, - char *buf, - size_t len, - int format) - ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); int virStorageFileParseChainIndex(const char *diskTarget, const char *name, unsigned int *chainIndex) @@ -410,13 +398,6 @@ int virStorageFileParseBackingStoreStr(const char *str, unsigned int *chainIndex) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3); =20 -virStorageSourcePtr virStorageFileChainLookup(virStorageSourcePtr chain, - virStorageSourcePtr startFro= m, - const char *name, - unsigned int idx, - virStorageSourcePtr *parent) - ATTRIBUTE_NONNULL(1); - bool virStorageIsFile(const char *path); bool virStorageIsRelative(const char *backing); =20 @@ -469,15 +450,6 @@ bool virStorageSourceIsEmpty(virStorageSourcePtr src); bool virStorageSourceIsBlockLocal(const virStorageSource *src); virStorageSourcePtr virStorageSourceNew(void); void virStorageSourceBackingStoreClear(virStorageSourcePtr def); -int virStorageSourceUpdatePhysicalSize(virStorageSourcePtr src, - int fd, struct stat const *sb); -int virStorageSourceUpdateBackingSizes(virStorageSourcePtr src, - int fd, struct stat const *sb); -int virStorageSourceUpdateCapacity(virStorageSourcePtr src, - char *buf, ssize_t len); - -int virStorageSourceNewFromBacking(virStorageSourcePtr parent, - virStorageSourcePtr *backing); =20 int virStorageSourceNetCookiesValidate(virStorageSourcePtr src); =20 @@ -488,10 +460,6 @@ bool virStorageSourceIsSameLocation(virStorageSourcePt= r a, virStorageSourcePtr b) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); =20 -int virStorageSourceParseRBDColonString(const char *rbdstr, - virStorageSourcePtr src) - ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); - typedef int (*virStorageFileSimplifyPathReadlinkCallback)(const char *path, char **link, @@ -500,14 +468,6 @@ char *virStorageFileCanonicalizePath(const char *path, virStorageFileSimplifyPathReadlinkCal= lback cb, void *cbdata); =20 -int virStorageFileGetRelativeBackingPath(virStorageSourcePtr from, - virStorageSourcePtr to, - char **relpath) - ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3); - -int virStorageSourceNewFromBackingAbsolute(const char *path, - virStorageSourcePtr *src); - bool virStorageSourceIsRelative(virStorageSourcePtr src); =20 void @@ -542,39 +502,4 @@ virStorageSourceInitiatorCopy(virStorageSourceInitiato= rDefPtr dest, void virStorageSourceInitiatorClear(virStorageSourceInitiatorDefPtr initiator); =20 -int virStorageFileInit(virStorageSourcePtr src); -int virStorageFileInitAs(virStorageSourcePtr src, - uid_t uid, gid_t gid); -void virStorageFileDeinit(virStorageSourcePtr src); - -int virStorageFileCreate(virStorageSourcePtr src); -int virStorageFileUnlink(virStorageSourcePtr src); -int virStorageFileStat(virStorageSourcePtr src, - struct stat *stat); -ssize_t virStorageFileRead(virStorageSourcePtr src, - size_t offset, - size_t len, - char **buf); -const char *virStorageFileGetUniqueIdentifier(virStorageSourcePtr src); -int virStorageFileAccess(virStorageSourcePtr src, int mode); -int virStorageFileChown(const virStorageSource *src, uid_t uid, gid_t gid); - -int virStorageFileSupportsSecurityDriver(const virStorageSource *src); -int virStorageFileSupportsAccess(const virStorageSource *src); -int virStorageFileSupportsCreate(const virStorageSource *src); -int virStorageFileSupportsBackingChainTraversal(const virStorageSource *sr= c); - -int virStorageFileGetMetadata(virStorageSourcePtr src, - uid_t uid, gid_t gid, - bool report_broken) - ATTRIBUTE_NONNULL(1); - -int virStorageFileGetBackingStoreStr(virStorageSourcePtr src, - char **backing) - ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); - -void virStorageFileReportBrokenChain(int errcode, - virStorageSourcePtr src, - virStorageSourcePtr parent); - G_DEFINE_AUTOPTR_CLEANUP_FUNC(virStorageAuthDef, virStorageAuthDefFree); diff --git a/tests/meson.build b/tests/meson.build index 23255dd62a..0de0783839 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -442,7 +442,7 @@ endif if conf.has('WITH_QEMU') tests +=3D [ { 'name': 'qemuagenttest', 'link_with': [ test_qemu_driver_lib, test_u= tils_qemu_monitor_lib ], 'link_whole': [ test_utils_qemu_lib ] }, - { 'name': 'qemublocktest', 'link_with': [ test_qemu_driver_lib, test_u= tils_qemu_monitor_lib ], 'link_whole': [ test_utils_qemu_lib ] }, + { 'name': 'qemublocktest', 'include': [ storage_file_inc_dir ], 'link_= with': [ test_qemu_driver_lib, test_utils_qemu_monitor_lib ], 'link_whole':= [ test_utils_qemu_lib ] }, { 'name': 'qemucapabilitiestest', 'link_with': [ test_qemu_driver_lib,= test_utils_qemu_monitor_lib ], 'link_whole': [ test_utils_qemu_lib ] }, { 'name': 'qemucaps2xmltest', 'link_with': [ test_qemu_driver_lib ], '= link_whole': [ test_utils_qemu_lib ] }, { 'name': 'qemucommandutiltest', 'link_with': [ test_qemu_driver_lib, = test_utils_qemu_monitor_lib ], 'link_whole': [ test_utils_qemu_lib ] }, @@ -509,7 +509,7 @@ endif =20 if conf.has('WITH_STORAGE_FS') tests +=3D [ - { 'name': 'virstoragetest', 'link_with': [ storage_driver_impl_lib ] }, + { 'name': 'virstoragetest', 'include': [ storage_file_inc_dir ],'link_= with': [ storage_driver_impl_lib ] }, ] endif =20 diff --git a/tests/qemublocktest.c b/tests/qemublocktest.c index f86e9fd552..ddaf73359d 100644 --- a/tests/qemublocktest.c +++ b/tests/qemublocktest.c @@ -17,6 +17,7 @@ #include =20 =20 +#include "storage_source.h" #include "testutils.h" #include "testutilsqemu.h" #include "testutilsqemuschema.h" diff --git a/tests/virstoragetest.c b/tests/virstoragetest.c index 86c7cd910c..5c2f211557 100644 --- a/tests/virstoragetest.c +++ b/tests/virstoragetest.c @@ -20,6 +20,7 @@ =20 #include =20 +#include "storage_source.h" #include "testutils.h" #include "vircommand.h" #include "virerror.h" --=20 2.29.2