Compare commits
3 Commits
4012f73ff9
...
6dea6849bc
| Author | SHA1 | Date |
|---|---|---|
|
|
6dea6849bc | 1 day ago |
|
|
ce5fd01ba7 | 1 day ago |
|
|
815e579244 | 2 days ago |
25 changed files with 1888 additions and 63 deletions
@ -0,0 +1,3 @@ |
|||
version https://git-lfs.github.com/spec/v1 |
|||
oid sha256:66154fee836235678b712676b2589c45f66e3d6a8721ee0697c9f20a66cad0d8 |
|||
size 10241776 |
|||
@ -0,0 +1,69 @@ |
|||
From 933524784d813b24aa0970992b820698f1e03180 Mon Sep 17 00:00:00 2001 |
|||
Message-ID: <933524784d813b24aa0970992b820698f1e03180.1766070439.git.jdenemar@redhat.com> |
|||
From: Nathan Chen via Devel <devel@lists.libvirt.org> |
|||
Date: Tue, 2 Dec 2025 11:59:47 -0800 |
|||
Subject: [PATCH] qemu: Use pci_bus to identify multi-smmuv3 model |
|||
MIME-Version: 1.0 |
|||
Content-Type: text/plain; charset=UTF-8 |
|||
Content-Transfer-Encoding: 8bit |
|||
|
|||
Use presence of non-negative pci_bus to identify multi-smmuv3 |
|||
IOMMU model, instead of the niommus attribute. This allows for |
|||
specifying a single arm-smmuv3 on the qemu command line, |
|||
instead of both the virt-machine smmuv3 and arm-smmuv3 |
|||
being specified at the same time. |
|||
|
|||
Signed-off-by: Nathan Chen <nathanc@nvidia.com> |
|||
Fixes: e70c4d54d365 conf: Support multiple device-pluggable smmuv3 IOMMUs |
|||
Reviewed-by: Ján Tomko <jtomko@redhat.com> |
|||
(cherry picked from commit da4305b7bc8d3bd52c60db1905db88e43ebd9868) |
|||
|
|||
https://issues.redhat.com/browse/RHEL-74200 |
|||
Signed-off-by: Ján Tomko <jtomko@redhat.com> |
|||
---
|
|||
src/qemu/qemu_command.c | 2 +- |
|||
src/qemu/qemu_postparse.c | 2 +- |
|||
.../iommu-smmuv3-pci-bus-single.aarch64-latest.args | 2 +- |
|||
3 files changed, 3 insertions(+), 3 deletions(-) |
|||
|
|||
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
|
|||
index b69fe23236..fb89dbec27 100644
|
|||
--- a/src/qemu/qemu_command.c
|
|||
+++ b/src/qemu/qemu_command.c
|
|||
@@ -7192,7 +7192,7 @@ qemuBuildMachineCommandLine(virCommand *cmd,
|
|||
if (qemuAppendDomainFeaturesMachineParam(&buf, def, qemuCaps) < 0) |
|||
return -1; |
|||
|
|||
- if (def->niommus == 1) {
|
|||
+ if (def->iommus && def->iommus[0]->pci_bus < 0) {
|
|||
switch (def->iommus[0]->model) { |
|||
case VIR_DOMAIN_IOMMU_MODEL_SMMUV3: |
|||
virBufferAddLit(&buf, ",iommu=smmuv3"); |
|||
diff --git a/src/qemu/qemu_postparse.c b/src/qemu/qemu_postparse.c
|
|||
index dc5ade829a..840d6a1174 100644
|
|||
--- a/src/qemu/qemu_postparse.c
|
|||
+++ b/src/qemu/qemu_postparse.c
|
|||
@@ -1559,7 +1559,7 @@ qemuDomainDefEnableDefaultFeatures(virDomainDef *def,
|
|||
* domain already has IOMMU without inremap. This will be fixed in |
|||
* qemuDomainIOMMUDefPostParse() but there domain definition can't be |
|||
* modified so change it now. */ |
|||
- if (def->iommus && def->niommus == 1 &&
|
|||
+ if (def->iommus && def->iommus[0]->pci_bus < 0 &&
|
|||
(def->iommus[0]->intremap == VIR_TRISTATE_SWITCH_ON || |
|||
qemuDomainNeedsIOMMUWithEIM(def)) && |
|||
def->features[VIR_DOMAIN_FEATURE_IOAPIC] == VIR_DOMAIN_IOAPIC_NONE) { |
|||
diff --git a/tests/qemuxmlconfdata/iommu-smmuv3-pci-bus-single.aarch64-latest.args b/tests/qemuxmlconfdata/iommu-smmuv3-pci-bus-single.aarch64-latest.args
|
|||
index 976467e641..34e7bda1c5 100644
|
|||
--- a/tests/qemuxmlconfdata/iommu-smmuv3-pci-bus-single.aarch64-latest.args
|
|||
+++ b/tests/qemuxmlconfdata/iommu-smmuv3-pci-bus-single.aarch64-latest.args
|
|||
@@ -10,7 +10,7 @@ XDG_CONFIG_HOME=/var/lib/libvirt/qemu/domain--1-guest/.config \
|
|||
-name guest=guest,debug-threads=on \ |
|||
-S \ |
|||
-object '{"qom-type":"secret","id":"masterKey0","format":"raw","file":"/var/lib/libvirt/qemu/domain--1-guest/master-key.aes"}' \ |
|||
--machine virt,usb=off,gic-version=2,iommu=smmuv3,dump-guest-core=off,memory-backend=mach-virt.ram,acpi=off \
|
|||
+-machine virt,usb=off,gic-version=2,dump-guest-core=off,memory-backend=mach-virt.ram,acpi=off \
|
|||
-accel tcg \ |
|||
-cpu cortex-a15 \ |
|||
-m size=1048576k \ |
|||
--
|
|||
2.52.0 |
|||
@ -0,0 +1,119 @@ |
|||
From 8e9dc8aed52c98c3683949dfe1127061bd9df47a Mon Sep 17 00:00:00 2001 |
|||
Message-ID: <8e9dc8aed52c98c3683949dfe1127061bd9df47a.1766070439.git.jdenemar@redhat.com> |
|||
From: Peter Krempa <pkrempa@redhat.com> |
|||
Date: Mon, 1 Dec 2025 11:35:32 +0100 |
|||
Subject: [PATCH] qemu: tpm: Account for possible migration without actually |
|||
sharing storage |
|||
|
|||
The current logic in 'qemuTPMEmulatorBuildCommand' skips all setup if |
|||
the *location* of the data is on what we'd consider shared storage. |
|||
|
|||
This means that if the location is not actually shared (e.g. it's shared |
|||
betweeh some other hosts than the two doing the migration) and the path |
|||
wasn't ever used (e.g. by migrating out) from the host where we're |
|||
migrating into the complete setup of the location would be skipped even |
|||
when it doesn't exist. |
|||
|
|||
Fix the logic by skipping only some of the setup steps so that |
|||
'qemuTPMEmulatorCreateStorage' can still create the storage if it |
|||
doesn't exist. |
|||
|
|||
The rest of the code then needs to take the 'created' flag returned from |
|||
'qemuTPMEmulatorCreateStorage' into account. |
|||
|
|||
Fixes: 68103e9daf633b789428fedef56f816c92f6ee75 |
|||
Signed-off-by: Peter Krempa <pkrempa@redhat.com> |
|||
Reviewed-by: Michal Privoznik <mprivozn@redhat.com> |
|||
(cherry picked from commit d56d0560946770d4364a4918cc289e6a7fe5d15c) |
|||
https://issues.redhat.com/browse/RHEL-132534 |
|||
---
|
|||
src/qemu/qemu_tpm.c | 29 ++++++++++++++++++++--------- |
|||
1 file changed, 20 insertions(+), 9 deletions(-) |
|||
|
|||
diff --git a/src/qemu/qemu_tpm.c b/src/qemu/qemu_tpm.c
|
|||
index 4c9445d72c..660410bcba 100644
|
|||
--- a/src/qemu/qemu_tpm.c
|
|||
+++ b/src/qemu/qemu_tpm.c
|
|||
@@ -158,6 +158,7 @@ qemuTPMEmulatorGetPid(const char *swtpmStateDir,
|
|||
/** |
|||
* qemuTPMEmulatorCreateStorage: |
|||
* @tpm: TPM definition for an emulator type |
|||
+ * @sharedStorageMigration: VM is being migrated with possibly shared storage
|
|||
* @created: a pointer to a bool that will be set to true if the |
|||
* storage was created because it did not exist yet |
|||
* @swtpm_user: The uid that needs to be able to access the directory |
|||
@@ -169,6 +170,7 @@ qemuTPMEmulatorGetPid(const char *swtpmStateDir,
|
|||
*/ |
|||
static int |
|||
qemuTPMEmulatorCreateStorage(virDomainTPMDef *tpm, |
|||
+ bool sharedStorageMigration,
|
|||
bool *created, |
|||
uid_t swtpm_user, |
|||
gid_t swtpm_group) |
|||
@@ -187,8 +189,17 @@ qemuTPMEmulatorCreateStorage(virDomainTPMDef *tpm,
|
|||
*created = false; |
|||
|
|||
if (!virFileExists(source_path) || |
|||
- virDirIsEmpty(source_path, true) > 0)
|
|||
+ virDirIsEmpty(source_path, true) > 0) {
|
|||
*created = true; |
|||
+ } else {
|
|||
+ /* If the location exists and is shared, we don't need to create it
|
|||
+ * during migration */
|
|||
+ if (sharedStorageMigration) {
|
|||
+ VIR_DEBUG("Skipping TPM storage creation. Path '%s' already exists and is on shared storage.",
|
|||
+ source_path);
|
|||
+ return 0;
|
|||
+ }
|
|||
+ }
|
|||
|
|||
if (virDirCreate(source_path, 0700, swtpm_user, swtpm_group, |
|||
VIR_DIR_CREATE_ALLOW_EXIST) < 0) { |
|||
@@ -809,16 +820,13 @@ qemuTPMEmulatorBuildCommand(virDomainTPMDef *tpm,
|
|||
run_setup = true; |
|||
} |
|||
|
|||
- /* Do not create storage and run swtpm_setup on incoming migration over
|
|||
- * shared storage
|
|||
- */
|
|||
on_shared_storage = virFileIsSharedFS(tpm->data.emulator.source_path, |
|||
cfg->sharedFilesystems) == 1; |
|||
- if (incomingMigration && on_shared_storage)
|
|||
- create_storage = false;
|
|||
|
|||
if (create_storage) { |
|||
- if (qemuTPMEmulatorCreateStorage(tpm, &created,
|
|||
+ if (qemuTPMEmulatorCreateStorage(tpm,
|
|||
+ incomingMigration && on_shared_storage,
|
|||
+ &created,
|
|||
cfg->swtpm_user, cfg->swtpm_group) < 0) |
|||
return NULL; |
|||
run_setup = created; |
|||
@@ -885,6 +893,9 @@ qemuTPMEmulatorBuildCommand(virDomainTPMDef *tpm,
|
|||
/* If swtpm supports it and the TPM state is stored on shared storage, |
|||
* start swtpm with --migration release-lock-outgoing so it can migrate |
|||
* across shared storage if needed. |
|||
+ *
|
|||
+ * Note that if 'created' is true, the location didn't exist so the storage
|
|||
+ * is not actually shared.
|
|||
*/ |
|||
QEMU_DOMAIN_TPM_PRIVATE(tpm)->swtpm.can_migrate_shared_storage = false; |
|||
if (on_shared_storage && |
|||
@@ -892,13 +903,13 @@ qemuTPMEmulatorBuildCommand(virDomainTPMDef *tpm,
|
|||
|
|||
virCommandAddArg(cmd, "--migration"); |
|||
virCommandAddArgFormat(cmd, "release-lock-outgoing%s", |
|||
- incomingMigration ? ",incoming": "");
|
|||
+ incomingMigration && !created ? ",incoming": "");
|
|||
QEMU_DOMAIN_TPM_PRIVATE(tpm)->swtpm.can_migrate_shared_storage = true; |
|||
} else { |
|||
/* Report an error if there's an incoming migration across shared |
|||
* storage and swtpm does not support the --migration option. |
|||
*/ |
|||
- if (incomingMigration && on_shared_storage) {
|
|||
+ if (incomingMigration && on_shared_storage && !created) {
|
|||
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, |
|||
_("%1$s (on destination side) does not support the --migration option needed for migration with shared storage"), |
|||
swtpm); |
|||
--
|
|||
2.52.0 |
|||
@ -0,0 +1,111 @@ |
|||
From 0bb3bb84670dbd45c6eca2d108b2d4d5a7754ef4 Mon Sep 17 00:00:00 2001 |
|||
Message-ID: <0bb3bb84670dbd45c6eca2d108b2d4d5a7754ef4.1766070439.git.jdenemar@redhat.com> |
|||
From: Jiri Denemark <jdenemar@redhat.com> |
|||
Date: Fri, 5 Dec 2025 15:09:15 +0100 |
|||
Subject: [PATCH] tests: Test virFileIsSharedFSOverride |
|||
|
|||
Technically virFileIsSharedFSOverride is available on any OS, but we |
|||
need a mocked realpath() to test it. Because the virfilemock library |
|||
also mocks statfs() which is only available on Linux, we don't even try |
|||
to load the library anywhere else. Thus we need to skip testing |
|||
virFileIsSharedFSOverride on non-Linux too. |
|||
|
|||
Signed-off-by: Jiri Denemark <jdenemar@redhat.com> |
|||
(cherry picked from commit 121d179e068b584f62ea2c029d89a44e67c909c0) |
|||
|
|||
https://issues.redhat.com/browse/RHEL-102925 |
|||
|
|||
Signed-off-by: Jiri Denemark <jdenemar@redhat.com> |
|||
---
|
|||
tests/virfiletest.c | 69 +++++++++++++++++++++++++++++++++++++++++++++ |
|||
1 file changed, 69 insertions(+) |
|||
|
|||
diff --git a/tests/virfiletest.c b/tests/virfiletest.c
|
|||
index e05925a321..ccd76a3fac 100644
|
|||
--- a/tests/virfiletest.c
|
|||
+++ b/tests/virfiletest.c
|
|||
@@ -329,6 +329,55 @@ testFileIsSharedFSType(const void *opaque G_GNUC_UNUSED)
|
|||
} |
|||
|
|||
|
|||
+static const char *shared_filesystems[] = {
|
|||
+ "/run/user/501/gvfs",
|
|||
+ "/nfs",
|
|||
+ "/gluster",
|
|||
+ "/ceph/multi",
|
|||
+ "/gpfs/data/blaf",
|
|||
+ "/quobyte",
|
|||
+ NULL,
|
|||
+};
|
|||
+
|
|||
+static int
|
|||
+testFileIsSharedFSOverride(const void *opaque G_GNUC_UNUSED)
|
|||
+{
|
|||
+#ifndef __linux__
|
|||
+ return EXIT_AM_SKIP;
|
|||
+#else
|
|||
+ const struct testFileIsSharedFSType *data = opaque;
|
|||
+ g_autofree char *mtabFile = NULL;
|
|||
+ bool actual;
|
|||
+ int ret = -1;
|
|||
+
|
|||
+ /* mtab is used by mocked realpath to decide whether a given path exists */
|
|||
+ mtabFile = g_strdup_printf(abs_srcdir "/virfiledata/%s", data->mtabFile);
|
|||
+
|
|||
+ if (!g_setenv("LIBVIRT_MTAB", mtabFile, true)) {
|
|||
+ fprintf(stderr, "Unable to set env variable\n");
|
|||
+ goto cleanup;
|
|||
+ }
|
|||
+
|
|||
+ actual = virFileIsSharedFSOverride(data->filename,
|
|||
+ (char * const *) shared_filesystems);
|
|||
+
|
|||
+ if (actual != data->expected) {
|
|||
+ fprintf(stderr, "FS of '%s' is %s. Expected: %s\n",
|
|||
+ data->filename,
|
|||
+ actual ? "shared" : "not shared",
|
|||
+ data->expected ? "shared" : "not shared");
|
|||
+ goto cleanup;
|
|||
+ }
|
|||
+
|
|||
+ ret = 0;
|
|||
+
|
|||
+ cleanup:
|
|||
+ g_unsetenv("LIBVIRT_MTAB");
|
|||
+ return ret;
|
|||
+#endif
|
|||
+}
|
|||
+
|
|||
+
|
|||
static int |
|||
mymain(void) |
|||
{ |
|||
@@ -439,6 +488,26 @@ mymain(void)
|
|||
DO_TEST_FILE_IS_SHARED_FS_TYPE("mounts3.txt", "/gpfs/data", true); |
|||
DO_TEST_FILE_IS_SHARED_FS_TYPE("mounts3.txt", "/quobyte", true); |
|||
|
|||
+#define DO_TEST_FILE_IS_SHARED_FS_OVERRIDE(mtab, file, exp) \
|
|||
+ do { \
|
|||
+ struct testFileIsSharedFSType data = { \
|
|||
+ .mtabFile = mtab, .filename = file, .expected = exp \
|
|||
+ }; \
|
|||
+ if (virTestRun(virTestCounterNext(), testFileIsSharedFSOverride, &data) < 0) \
|
|||
+ ret = -1; \
|
|||
+ } while (0)
|
|||
+
|
|||
+ virTestCounterReset("testFileIsSharedFSOverride ");
|
|||
+ DO_TEST_FILE_IS_SHARED_FS_OVERRIDE("mounts2.txt", "/boot/vmlinuz", false);
|
|||
+ DO_TEST_FILE_IS_SHARED_FS_OVERRIDE("mounts2.txt", "/run/user/501/gvfs/some/file", true);
|
|||
+ DO_TEST_FILE_IS_SHARED_FS_OVERRIDE("mounts3.txt", "/nfs/file", true);
|
|||
+ DO_TEST_FILE_IS_SHARED_FS_OVERRIDE("mounts3.txt", "/gluster/file", true);
|
|||
+ DO_TEST_FILE_IS_SHARED_FS_OVERRIDE("mounts3.txt", "/some/symlink/file", true);
|
|||
+ DO_TEST_FILE_IS_SHARED_FS_OVERRIDE("mounts3.txt", "/ceph/file", false);
|
|||
+ DO_TEST_FILE_IS_SHARED_FS_OVERRIDE("mounts3.txt", "/ceph/multi/file", true);
|
|||
+ DO_TEST_FILE_IS_SHARED_FS_OVERRIDE("mounts3.txt", "/gpfs/data", false);
|
|||
+ DO_TEST_FILE_IS_SHARED_FS_OVERRIDE("mounts3.txt", "/quobyte", true);
|
|||
+
|
|||
return ret != 0 ? EXIT_FAILURE : EXIT_SUCCESS; |
|||
} |
|||
|
|||
--
|
|||
2.52.0 |
|||
@ -0,0 +1,201 @@ |
|||
From d30c21439f3847ecc229db9355eb802f0256a3f0 Mon Sep 17 00:00:00 2001 |
|||
Message-ID: <d30c21439f3847ecc229db9355eb802f0256a3f0.1766070438.git.jdenemar@redhat.com> |
|||
From: =?UTF-8?q?J=C3=A1n=20Tomko?= <jtomko@redhat.com> |
|||
Date: Fri, 5 Dec 2025 08:50:51 +0100 |
|||
Subject: [PATCH] tests: add test for a single per-device smmuv3 |
|||
MIME-Version: 1.0 |
|||
Content-Type: text/plain; charset=UTF-8 |
|||
Content-Transfer-Encoding: 8bit |
|||
|
|||
Signed-off-by: Ján Tomko <jtomko@redhat.com> |
|||
(cherry picked from commit 45ff1c002629dadd9d94b91742ffb985b0fe027f) |
|||
|
|||
https://issues.redhat.com/browse/RHEL-74200 |
|||
Signed-off-by: Ján Tomko <jtomko@redhat.com> |
|||
---
|
|||
...-smmuv3-pci-bus-single.aarch64-latest.args | 40 +++++++++++++ |
|||
...u-smmuv3-pci-bus-single.aarch64-latest.xml | 59 +++++++++++++++++++ |
|||
.../iommu-smmuv3-pci-bus-single.xml | 46 +++++++++++++++ |
|||
tests/qemuxmlconftest.c | 1 + |
|||
4 files changed, 146 insertions(+) |
|||
create mode 100644 tests/qemuxmlconfdata/iommu-smmuv3-pci-bus-single.aarch64-latest.args |
|||
create mode 100644 tests/qemuxmlconfdata/iommu-smmuv3-pci-bus-single.aarch64-latest.xml |
|||
create mode 100644 tests/qemuxmlconfdata/iommu-smmuv3-pci-bus-single.xml |
|||
|
|||
diff --git a/tests/qemuxmlconfdata/iommu-smmuv3-pci-bus-single.aarch64-latest.args b/tests/qemuxmlconfdata/iommu-smmuv3-pci-bus-single.aarch64-latest.args
|
|||
new file mode 100644 |
|||
index 0000000000..976467e641
|
|||
--- /dev/null
|
|||
+++ b/tests/qemuxmlconfdata/iommu-smmuv3-pci-bus-single.aarch64-latest.args
|
|||
@@ -0,0 +1,40 @@
|
|||
+LC_ALL=C \
|
|||
+PATH=/bin \
|
|||
+HOME=/var/lib/libvirt/qemu/domain--1-guest \
|
|||
+USER=test \
|
|||
+LOGNAME=test \
|
|||
+XDG_DATA_HOME=/var/lib/libvirt/qemu/domain--1-guest/.local/share \
|
|||
+XDG_CACHE_HOME=/var/lib/libvirt/qemu/domain--1-guest/.cache \
|
|||
+XDG_CONFIG_HOME=/var/lib/libvirt/qemu/domain--1-guest/.config \
|
|||
+/usr/bin/qemu-system-aarch64 \
|
|||
+-name guest=guest,debug-threads=on \
|
|||
+-S \
|
|||
+-object '{"qom-type":"secret","id":"masterKey0","format":"raw","file":"/var/lib/libvirt/qemu/domain--1-guest/master-key.aes"}' \
|
|||
+-machine virt,usb=off,gic-version=2,iommu=smmuv3,dump-guest-core=off,memory-backend=mach-virt.ram,acpi=off \
|
|||
+-accel tcg \
|
|||
+-cpu cortex-a15 \
|
|||
+-m size=1048576k \
|
|||
+-object '{"qom-type":"memory-backend-ram","id":"mach-virt.ram","size":1073741824}' \
|
|||
+-overcommit mem-lock=off \
|
|||
+-smp 1,sockets=1,cores=1,threads=1 \
|
|||
+-uuid 1ccfd97d-5eb4-478a-bbe6-88d254c16db7 \
|
|||
+-display none \
|
|||
+-no-user-config \
|
|||
+-nodefaults \
|
|||
+-chardev socket,id=charmonitor,fd=1729,server=on,wait=off \
|
|||
+-mon chardev=charmonitor,id=monitor,mode=control \
|
|||
+-rtc base=utc \
|
|||
+-no-shutdown \
|
|||
+-boot strict=on \
|
|||
+-device '{"driver":"pxb-pcie","bus_nr":252,"id":"pci.1","bus":"pcie.0","addr":"0x1"}' \
|
|||
+-device '{"driver":"pxb-pcie","bus_nr":248,"id":"pci.2","bus":"pcie.0","addr":"0x2"}' \
|
|||
+-device '{"driver":"pcie-root-port","port":0,"chassis":21,"id":"pci.3","bus":"pci.1","addr":"0x0"}' \
|
|||
+-device '{"driver":"pcie-root-port","port":168,"chassis":22,"id":"pci.4","bus":"pci.2","addr":"0x0"}' \
|
|||
+-device '{"driver":"arm-smmuv3","primary-bus":"pci.1","id":"iommu0"}' \
|
|||
+-audiodev '{"id":"audio1","driver":"none"}' \
|
|||
+-object '{"qom-type":"rng-random","id":"objrng0","filename":"/dev/urandom"}' \
|
|||
+-device '{"driver":"virtio-rng-pci","rng":"objrng0","id":"rng0","bus":"pci.3","addr":"0x0"}' \
|
|||
+-object '{"qom-type":"rng-random","id":"objrng1","filename":"/dev/urandom"}' \
|
|||
+-device '{"driver":"virtio-rng-pci","rng":"objrng1","id":"rng1","bus":"pci.4","addr":"0x0"}' \
|
|||
+-sandbox on,obsolete=deny,elevateprivileges=deny,spawn=deny,resourcecontrol=deny \
|
|||
+-msg timestamp=on
|
|||
diff --git a/tests/qemuxmlconfdata/iommu-smmuv3-pci-bus-single.aarch64-latest.xml b/tests/qemuxmlconfdata/iommu-smmuv3-pci-bus-single.aarch64-latest.xml
|
|||
new file mode 100644 |
|||
index 0000000000..e6071fd71b
|
|||
--- /dev/null
|
|||
+++ b/tests/qemuxmlconfdata/iommu-smmuv3-pci-bus-single.aarch64-latest.xml
|
|||
@@ -0,0 +1,59 @@
|
|||
+<domain type='qemu'>
|
|||
+ <name>guest</name>
|
|||
+ <uuid>1ccfd97d-5eb4-478a-bbe6-88d254c16db7</uuid>
|
|||
+ <memory unit='KiB'>1048576</memory>
|
|||
+ <currentMemory unit='KiB'>1048576</currentMemory>
|
|||
+ <vcpu placement='static'>1</vcpu>
|
|||
+ <os>
|
|||
+ <type arch='aarch64' machine='virt'>hvm</type>
|
|||
+ <boot dev='hd'/>
|
|||
+ </os>
|
|||
+ <features>
|
|||
+ <gic version='2'/>
|
|||
+ </features>
|
|||
+ <cpu mode='custom' match='exact' check='none'>
|
|||
+ <model fallback='forbid'>cortex-a15</model>
|
|||
+ </cpu>
|
|||
+ <clock offset='utc'/>
|
|||
+ <on_poweroff>destroy</on_poweroff>
|
|||
+ <on_reboot>restart</on_reboot>
|
|||
+ <on_crash>destroy</on_crash>
|
|||
+ <devices>
|
|||
+ <emulator>/usr/bin/qemu-system-aarch64</emulator>
|
|||
+ <controller type='usb' index='0' model='none'/>
|
|||
+ <controller type='pci' index='0' model='pcie-root'/>
|
|||
+ <controller type='pci' index='1' model='pcie-expander-bus'>
|
|||
+ <model name='pxb-pcie'/>
|
|||
+ <target busNr='252'/>
|
|||
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x0'/>
|
|||
+ </controller>
|
|||
+ <controller type='pci' index='2' model='pcie-expander-bus'>
|
|||
+ <model name='pxb-pcie'/>
|
|||
+ <target busNr='248'/>
|
|||
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>
|
|||
+ </controller>
|
|||
+ <controller type='pci' index='3' model='pcie-root-port'>
|
|||
+ <model name='pcie-root-port'/>
|
|||
+ <target chassis='21' port='0x0'/>
|
|||
+ <address type='pci' domain='0x0000' bus='0x01' slot='0x00' function='0x0'/>
|
|||
+ </controller>
|
|||
+ <controller type='pci' index='4' model='pcie-root-port'>
|
|||
+ <model name='pcie-root-port'/>
|
|||
+ <target chassis='22' port='0xa8'/>
|
|||
+ <address type='pci' domain='0x0000' bus='0x02' slot='0x00' function='0x0'/>
|
|||
+ </controller>
|
|||
+ <audio id='1' type='none'/>
|
|||
+ <memballoon model='none'/>
|
|||
+ <rng model='virtio'>
|
|||
+ <backend model='random'>/dev/urandom</backend>
|
|||
+ <address type='pci' domain='0x0000' bus='0x03' slot='0x00' function='0x0'/>
|
|||
+ </rng>
|
|||
+ <rng model='virtio'>
|
|||
+ <backend model='random'>/dev/urandom</backend>
|
|||
+ <address type='pci' domain='0x0000' bus='0x04' slot='0x00' function='0x0'/>
|
|||
+ </rng>
|
|||
+ <iommu model='smmuv3'>
|
|||
+ <driver pciBus='1'/>
|
|||
+ </iommu>
|
|||
+ </devices>
|
|||
+</domain>
|
|||
diff --git a/tests/qemuxmlconfdata/iommu-smmuv3-pci-bus-single.xml b/tests/qemuxmlconfdata/iommu-smmuv3-pci-bus-single.xml
|
|||
new file mode 100644 |
|||
index 0000000000..6f40a19740
|
|||
--- /dev/null
|
|||
+++ b/tests/qemuxmlconfdata/iommu-smmuv3-pci-bus-single.xml
|
|||
@@ -0,0 +1,46 @@
|
|||
+<domain type='qemu'>
|
|||
+ <name>guest</name>
|
|||
+ <uuid>1ccfd97d-5eb4-478a-bbe6-88d254c16db7</uuid>
|
|||
+ <memory unit='KiB'>1048576</memory>
|
|||
+ <vcpu placement='static'>1</vcpu>
|
|||
+ <os>
|
|||
+ <type arch='aarch64' machine='virt'>hvm</type>
|
|||
+ </os>
|
|||
+ <devices>
|
|||
+ <emulator>/usr/bin/qemu-system-aarch64</emulator>
|
|||
+ <controller type='usb' model='none'/>
|
|||
+ <memballoon model='none'/>
|
|||
+ <controller type='pci' index='0' model='pcie-root'/>
|
|||
+ <controller type='pci' index='1' model='pcie-expander-bus'>
|
|||
+ <model name='pxb-pcie'/>
|
|||
+ <target busNr='252'/>
|
|||
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x0'/>
|
|||
+ </controller>
|
|||
+ <controller type='pci' index='2' model='pcie-expander-bus'>
|
|||
+ <model name='pxb-pcie'/>
|
|||
+ <target busNr='248'/>
|
|||
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>
|
|||
+ </controller>
|
|||
+ <controller type='pci' index='3' model='pcie-root-port'>
|
|||
+ <model name='pcie-root-port'/>
|
|||
+ <target chassis='21' port='0x0'/>
|
|||
+ <address type='pci' domain='0x0000' bus='0x01' slot='0x00' function='0x0'/>
|
|||
+ </controller>
|
|||
+ <controller type='pci' index='4' model='pcie-root-port'>
|
|||
+ <model name='pcie-root-port'/>
|
|||
+ <target chassis='22' port='0xa8'/>
|
|||
+ <address type='pci' domain='0x0000' bus='0x02' slot='0x00' function='0x0'/>
|
|||
+ </controller>
|
|||
+ <rng model='virtio'>
|
|||
+ <backend model='random'>/dev/urandom</backend>
|
|||
+ <address type='pci' domain='0x0000' bus='0x03' slot='0x00' function='0x0'/>
|
|||
+ </rng>
|
|||
+ <rng model='virtio'>
|
|||
+ <backend model='random'>/dev/urandom</backend>
|
|||
+ <address type='pci' domain='0x0000' bus='0x04' slot='0x00' function='0x0'/>
|
|||
+ </rng>
|
|||
+ <iommu model='smmuv3'>
|
|||
+ <driver pciBus='1'/>
|
|||
+ </iommu>
|
|||
+ </devices>
|
|||
+</domain>
|
|||
diff --git a/tests/qemuxmlconftest.c b/tests/qemuxmlconftest.c
|
|||
index de03c58c8a..9a3257ea55 100644
|
|||
--- a/tests/qemuxmlconftest.c
|
|||
+++ b/tests/qemuxmlconftest.c
|
|||
@@ -3039,6 +3039,7 @@ mymain(void)
|
|||
DO_TEST_CAPS_LATEST_ABI_UPDATE("intel-iommu-eim-autoadd-v2"); |
|||
DO_TEST_CAPS_ARCH_LATEST("iommu-smmuv3", "aarch64"); |
|||
DO_TEST_CAPS_ARCH_LATEST("iommu-smmuv3-pci-bus", "aarch64"); |
|||
+ DO_TEST_CAPS_ARCH_LATEST("iommu-smmuv3-pci-bus-single", "aarch64");
|
|||
DO_TEST_CAPS_LATEST("virtio-iommu-x86_64"); |
|||
DO_TEST_CAPS_ARCH_LATEST("virtio-iommu-aarch64", "aarch64"); |
|||
DO_TEST_CAPS_LATEST_PARSE_ERROR("virtio-iommu-wrong-machine"); |
|||
--
|
|||
2.52.0 |
|||
@ -0,0 +1,114 @@ |
|||
From bcf71678b891cb8862b85be2fd44d2dc61686f94 Mon Sep 17 00:00:00 2001 |
|||
Message-ID: <bcf71678b891cb8862b85be2fd44d2dc61686f94.1766070439.git.jdenemar@redhat.com> |
|||
From: Jiri Denemark <jdenemar@redhat.com> |
|||
Date: Fri, 5 Dec 2025 16:51:25 +0100 |
|||
Subject: [PATCH] util: Fix race condition in virFileIsSharedFSOverride |
|||
|
|||
Switch virFileIsSharedFSOverride to use virFileCheckParents to avoid a |
|||
race which could result in virFileCanonicalizePath to be called on a |
|||
path that does not exist anymore. |
|||
|
|||
Signed-off-by: Jiri Denemark <jdenemar@redhat.com> |
|||
(cherry picked from commit 3a44f0c23d75519a9a374f790f4b91ab7b65a138) |
|||
|
|||
https://issues.redhat.com/browse/RHEL-102925 |
|||
|
|||
Signed-off-by: Jiri Denemark <jdenemar@redhat.com> |
|||
---
|
|||
src/util/virfile.c | 59 +++++++++++++++++----------------------------- |
|||
1 file changed, 22 insertions(+), 37 deletions(-) |
|||
|
|||
diff --git a/src/util/virfile.c b/src/util/virfile.c
|
|||
index 95fc8ff0e6..52d711d2a9 100644
|
|||
--- a/src/util/virfile.c
|
|||
+++ b/src/util/virfile.c
|
|||
@@ -3502,33 +3502,6 @@ virFileCheckParents(const char *path,
|
|||
} |
|||
|
|||
|
|||
-static char *
|
|||
-virFileGetExistingParent(const char *path)
|
|||
-{
|
|||
- g_autofree char *dirpath = g_strdup(path);
|
|||
- char *p = NULL;
|
|||
-
|
|||
- /* Try less and less of the path until we get to a directory we can access.
|
|||
- * Even if we don't have 'x' permission on any directory in the path on the
|
|||
- * NFS server (assuming it's NFS), we will be able to stat the mount point.
|
|||
- */
|
|||
- while (!virFileExists(dirpath) && p != dirpath) {
|
|||
- if (!(p = strrchr(dirpath, '/'))) {
|
|||
- virReportSystemError(EINVAL,
|
|||
- _("Invalid relative path '%1$s'"), path);
|
|||
- return NULL;
|
|||
- }
|
|||
-
|
|||
- if (p == dirpath)
|
|||
- *(p + 1) = '\0';
|
|||
- else
|
|||
- *p = '\0';
|
|||
- }
|
|||
-
|
|||
- return g_steal_pointer(&dirpath);
|
|||
-}
|
|||
-
|
|||
-
|
|||
#ifdef __linux__ |
|||
|
|||
# ifndef NFS_SUPER_MAGIC |
|||
@@ -3875,6 +3848,17 @@ virFileGetDefaultHugepage(virHugeTLBFS *fs,
|
|||
} |
|||
|
|||
|
|||
+static bool
|
|||
+virFileCheckParentsCanonicalize(const char *path,
|
|||
+ void *opaque)
|
|||
+{
|
|||
+ char **canonical = opaque;
|
|||
+
|
|||
+ *canonical = virFileCanonicalizePath(path);
|
|||
+ return !!*canonical;
|
|||
+}
|
|||
+
|
|||
+
|
|||
/** |
|||
* virFileIsSharedFSOverride: |
|||
* @path: Path to check |
|||
@@ -3888,24 +3872,25 @@ virFileIsSharedFSOverride(const char *path,
|
|||
char *const *overrides) |
|||
{ |
|||
g_autofree char *dirpath = NULL; |
|||
- g_autofree char *existing = NULL;
|
|||
char *p = NULL; |
|||
+ int rc;
|
|||
|
|||
if (!path || path[0] != '/' || !overrides) |
|||
return false; |
|||
|
|||
/* We only care about the longest existing sub-path. Further components |
|||
- * may will later be created by libvirt will not magically become a shared
|
|||
- * filesystem. */
|
|||
- if (!(existing = virFileGetExistingParent(path)))
|
|||
+ * that may later be created by libvirt will not magically become a shared
|
|||
+ * filesystem. Overrides have been canonicalized ahead of time, so we need
|
|||
+ * to do the same for the provided path or we'll never be able to find a
|
|||
+ * match if symlinks are involved.
|
|||
+ */
|
|||
+ rc = virFileCheckParents(path, NULL,
|
|||
+ virFileCheckParentsCanonicalize, &dirpath);
|
|||
+ if (rc == -1)
|
|||
return false; |
|||
|
|||
- /* Overrides have been canonicalized ahead of time, so we need to
|
|||
- * do the same for the provided path or we'll never be able to
|
|||
- * find a match if symlinks are involved */
|
|||
- if (!(dirpath = virFileCanonicalizePath(existing))) {
|
|||
- VIR_DEBUG("Cannot canonicalize parent '%s' of path '%s'",
|
|||
- existing, path);
|
|||
+ if (rc != 0) {
|
|||
+ VIR_DEBUG("Cannot canonicalize path '%s'", path);
|
|||
return false; |
|||
} |
|||
|
|||
--
|
|||
2.52.0 |
|||
@ -0,0 +1,144 @@ |
|||
From 0ef9c539a38510d35daec578dae8491f8a1c651c Mon Sep 17 00:00:00 2001 |
|||
Message-ID: <0ef9c539a38510d35daec578dae8491f8a1c651c.1766070439.git.jdenemar@redhat.com> |
|||
From: Jiri Denemark <jdenemar@redhat.com> |
|||
Date: Fri, 5 Dec 2025 16:47:14 +0100 |
|||
Subject: [PATCH] util: Fix race condition in virFileIsSharedFSType |
|||
|
|||
virFileIsSharedFSType could end up calling statfs on a path that no |
|||
longer exists and return an error. If this happens for a path on a |
|||
shared filesystem, the caller may incorrectly consider the path as |
|||
non-shared. |
|||
|
|||
Specifically, when starting a domain with TPM enabled and deciding |
|||
whether its vTPM state is stored on a shared storage, the race could |
|||
cause qemuTPMEmulatorBuildCommand to consider the state to be |
|||
non-shared. This means swtpm would be started without --migration even |
|||
when the state is actually stored on a shared storage and any attempt to |
|||
migrate such domain would fail with |
|||
|
|||
Operation not supported: the running swtpm does not support |
|||
migration with shared storage |
|||
|
|||
In fact, any caller of virFileGetExistingParent contained an inherent |
|||
TOCTOU race condition as the existing parent of a given path return by |
|||
virFileGetExistingParent may no longer exist at the time the caller |
|||
wants to check it. |
|||
|
|||
This patch introduces a new virFileCheckParents API which is almost |
|||
identical to virFileGetExistingParent, but uses a supplied callback to |
|||
check each path. This new API is used in virFileIsSharedFSType to avoid |
|||
the race. The old function will later be completely removed once all |
|||
callers are switched to the new one. |
|||
|
|||
Fixes: 05526b50909ff50c16e13a0b5580d41de74e3d59 |
|||
Signed-off-by: Jiri Denemark <jdenemar@redhat.com> |
|||
(cherry picked from commit b6addd42bece693debbf2e95551a2b4d2e1b453f) |
|||
|
|||
https://issues.redhat.com/browse/RHEL-102925 |
|||
|
|||
Signed-off-by: Jiri Denemark <jdenemar@redhat.com> |
|||
---
|
|||
src/util/virfile.c | 71 ++++++++++++++++++++++++++++++++++++++++++++-- |
|||
1 file changed, 69 insertions(+), 2 deletions(-) |
|||
|
|||
diff --git a/src/util/virfile.c b/src/util/virfile.c
|
|||
index a5c9fbe0d9..95fc8ff0e6 100644
|
|||
--- a/src/util/virfile.c
|
|||
+++ b/src/util/virfile.c
|
|||
@@ -3445,6 +3445,63 @@ virFileRemoveLastComponent(char *path)
|
|||
} |
|||
|
|||
|
|||
+/* Check callback for virFileCheckParents */
|
|||
+typedef bool (*virFileCheckParentsCallback)(const char *dirpath,
|
|||
+ void *opaque);
|
|||
+
|
|||
+/**
|
|||
+ * virFileCheckParents:
|
|||
+ * @path: path to check
|
|||
+ * @parent: where to store the closest parent satisfying the check
|
|||
+ * @check: callback called on parent paths
|
|||
+ * @opaque: data for the @check callback
|
|||
+ *
|
|||
+ * Calls @check on the @path and its parent paths until it returns true or a
|
|||
+ * root directory is reached. When @check returns true, the @parent (if
|
|||
+ * non-NULL) will be set to a copy of the corresponding path. The caller is
|
|||
+ * responsible for freeing it.
|
|||
+ *
|
|||
+ * Returns 0 on success (@parent set),
|
|||
+ * -1 on invalid input,
|
|||
+ * -2 when no path (including "/") satisfies the @check.
|
|||
+ */
|
|||
+static int
|
|||
+virFileCheckParents(const char *path,
|
|||
+ char **parent,
|
|||
+ virFileCheckParentsCallback check,
|
|||
+ void *opaque)
|
|||
+{
|
|||
+ g_autofree char *dirpath = g_strdup(path);
|
|||
+ char *p = NULL;
|
|||
+ bool checkOK;
|
|||
+
|
|||
+ checkOK = check(dirpath, opaque);
|
|||
+
|
|||
+ while (!checkOK && p != dirpath) {
|
|||
+ if (!(p = strrchr(dirpath, G_DIR_SEPARATOR))) {
|
|||
+ virReportSystemError(EINVAL,
|
|||
+ _("Invalid absolute path '%1$s'"), path);
|
|||
+ return -1;
|
|||
+ }
|
|||
+
|
|||
+ if (p == dirpath)
|
|||
+ *(p + 1) = '\0';
|
|||
+ else
|
|||
+ *p = '\0';
|
|||
+
|
|||
+ checkOK = check(dirpath, opaque);
|
|||
+ }
|
|||
+
|
|||
+ if (!checkOK)
|
|||
+ return -2;
|
|||
+
|
|||
+ if (parent)
|
|||
+ *parent = g_steal_pointer(&dirpath);
|
|||
+
|
|||
+ return 0;
|
|||
+}
|
|||
+
|
|||
+
|
|||
static char * |
|||
virFileGetExistingParent(const char *path) |
|||
{ |
|||
@@ -3599,6 +3656,14 @@ static const struct virFileSharedFsData virFileSharedFs[] = {
|
|||
}; |
|||
|
|||
|
|||
+static bool
|
|||
+virFileCheckParentsStatFS(const char *path,
|
|||
+ void *opaque)
|
|||
+{
|
|||
+ return statfs(path, (struct statfs *) opaque) == 0;
|
|||
+}
|
|||
+
|
|||
+
|
|||
int |
|||
virFileIsSharedFSType(const char *path, |
|||
unsigned int fstypes) |
|||
@@ -3607,11 +3672,13 @@ virFileIsSharedFSType(const char *path,
|
|||
struct statfs sb; |
|||
long long f_type = 0; |
|||
size_t i; |
|||
+ int rc;
|
|||
|
|||
- if (!(dirpath = virFileGetExistingParent(path)))
|
|||
+ if ((rc = virFileCheckParents(path, &dirpath,
|
|||
+ virFileCheckParentsStatFS, &sb)) == -1)
|
|||
return -1; |
|||
|
|||
- if (statfs(dirpath, &sb) < 0) {
|
|||
+ if (rc != 0) {
|
|||
virReportSystemError(errno, |
|||
_("cannot determine filesystem for '%1$s'"), |
|||
path); |
|||
--
|
|||
2.52.0 |
|||
@ -0,0 +1,84 @@ |
|||
From 9a8eedf3c0eca3075cf27ebae2d872df6627f3dd Mon Sep 17 00:00:00 2001 |
|||
Message-ID: <9a8eedf3c0eca3075cf27ebae2d872df6627f3dd.1766070439.git.jdenemar@redhat.com> |
|||
From: Jiri Denemark <jdenemar@redhat.com> |
|||
Date: Fri, 5 Dec 2025 16:52:32 +0100 |
|||
Subject: [PATCH] util: Rework virFileIsSharedFSOverride using |
|||
virFileCheckParents |
|||
|
|||
The newly introduced virFileCheckParents is generic enough to be used |
|||
for checking whether a specific path or any of its parents is included |
|||
in the overrides array. |
|||
|
|||
Signed-off-by: Jiri Denemark <jdenemar@redhat.com> |
|||
(cherry picked from commit eedf9ed68b45585569865604bf2a403670feaf3e) |
|||
|
|||
https://issues.redhat.com/browse/RHEL-102925 |
|||
|
|||
Signed-off-by: Jiri Denemark <jdenemar@redhat.com> |
|||
---
|
|||
src/util/virfile.c | 35 ++++++++++++----------------------- |
|||
1 file changed, 12 insertions(+), 23 deletions(-) |
|||
|
|||
diff --git a/src/util/virfile.c b/src/util/virfile.c
|
|||
index 52d711d2a9..05b2fa8168 100644
|
|||
--- a/src/util/virfile.c
|
|||
+++ b/src/util/virfile.c
|
|||
@@ -3859,6 +3859,14 @@ virFileCheckParentsCanonicalize(const char *path,
|
|||
} |
|||
|
|||
|
|||
+static bool
|
|||
+virFileCheckParentsInOverrides(const char *path,
|
|||
+ void *opaque)
|
|||
+{
|
|||
+ return g_strv_contains((const char *const *) opaque, path);
|
|||
+}
|
|||
+
|
|||
+
|
|||
/** |
|||
* virFileIsSharedFSOverride: |
|||
* @path: Path to check |
|||
@@ -3872,7 +3880,6 @@ virFileIsSharedFSOverride(const char *path,
|
|||
char *const *overrides) |
|||
{ |
|||
g_autofree char *dirpath = NULL; |
|||
- char *p = NULL;
|
|||
int rc; |
|||
|
|||
if (!path || path[0] != '/' || !overrides) |
|||
@@ -3894,29 +3901,11 @@ virFileIsSharedFSOverride(const char *path,
|
|||
return false; |
|||
} |
|||
|
|||
- if (g_strv_contains((const char *const *) overrides, dirpath))
|
|||
- return true;
|
|||
+ if (virFileCheckParents(dirpath, NULL, virFileCheckParentsInOverrides,
|
|||
+ (void *) overrides) < 0)
|
|||
+ return false;
|
|||
|
|||
- /* Continue until we've scanned the entire path */
|
|||
- while (p != dirpath) {
|
|||
-
|
|||
- /* Find the last slash */
|
|||
- if ((p = strrchr(dirpath, '/')) == NULL)
|
|||
- break;
|
|||
-
|
|||
- /* Truncate the path by overwriting the slash that we've just
|
|||
- * found with a null byte. If it is the very first slash in
|
|||
- * the path, we need to handle things slightly differently */
|
|||
- if (p == dirpath)
|
|||
- *(p+1) = '\0';
|
|||
- else
|
|||
- *p = '\0';
|
|||
-
|
|||
- if (g_strv_contains((const char *const *) overrides, dirpath))
|
|||
- return true;
|
|||
- }
|
|||
-
|
|||
- return false;
|
|||
+ return true;
|
|||
} |
|||
|
|||
|
|||
--
|
|||
2.52.0 |
|||
@ -0,0 +1,3 @@ |
|||
version https://git-lfs.github.com/spec/v1 |
|||
oid sha256:66154fee836235678b712676b2589c45f66e3d6a8721ee0697c9f20a66cad0d8 |
|||
size 10241776 |
|||
@ -0,0 +1,59 @@ |
|||
From 0431af4412aab6aadcde893a7eacf1ae24771590 Mon Sep 17 00:00:00 2001 |
|||
Message-ID: <0431af4412aab6aadcde893a7eacf1ae24771590.1768317034.git.jdenemar@redhat.com> |
|||
From: Michal Privoznik <mprivozn@redhat.com> |
|||
Date: Tue, 6 Jan 2026 17:18:03 +0100 |
|||
Subject: [PATCH] esx: URI encode inventory objects twice |
|||
|
|||
While discouraged by a KB article to use special characters in |
|||
inventory object names [1], ESX won't stop you. And thus users |
|||
can end up with a datastore named "datastore2+", for instance. |
|||
The datastore name (and datacenter path) are important when |
|||
fetching/uploading a .vmx file (used in APIs like |
|||
virDomainGetXMLDesc() or virDomainDefineXML()). And while we do |
|||
URI encode both (dcPath and dsName), encoding them once is not |
|||
enough. Cole Robinson discovered [2] that they need to be |
|||
URI-encoded twice. Use newly introduced |
|||
esxUtil_EscapeInventoryObject() helper to encode them twice. |
|||
|
|||
1: https://knowledge.broadcom.com/external/article/386368/vcenter-inventory-object-name-with-speci.html |
|||
2: https://issues.redhat.com/browse/RHEL-133729#comment-28604072 |
|||
Resolves: https://issues.redhat.com/browse/RHEL-134127 |
|||
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> |
|||
Reviewed-by: Jiri Denemark <jdenemar@redhat.com> |
|||
Reviewed-by: Richard W.M. Jones <rjones@redhat.com> |
|||
(cherry picked from commit 6c9d2591c668732eb05cf17d27c9102ef3d40b39) |
|||
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> |
|||
---
|
|||
src/esx/esx_driver.c | 8 ++++---- |
|||
1 file changed, 4 insertions(+), 4 deletions(-) |
|||
|
|||
diff --git a/src/esx/esx_driver.c b/src/esx/esx_driver.c
|
|||
index 9f965811b1..02f30c2b19 100644
|
|||
--- a/src/esx/esx_driver.c
|
|||
+++ b/src/esx/esx_driver.c
|
|||
@@ -2567,9 +2567,9 @@ esxDomainGetXMLDesc(virDomainPtr domain, unsigned int flags)
|
|||
domain->conn->uri->server, domain->conn->uri->port); |
|||
virBufferURIEncodeString(&buffer, directoryAndFileName); |
|||
virBufferAddLit(&buffer, "?dcPath="); |
|||
- virBufferURIEncodeString(&buffer, priv->primary->datacenterPath);
|
|||
+ esxUtil_EscapeInventoryObject(&buffer, priv->primary->datacenterPath);
|
|||
virBufferAddLit(&buffer, "&dsName="); |
|||
- virBufferURIEncodeString(&buffer, datastoreName);
|
|||
+ esxUtil_EscapeInventoryObject(&buffer, datastoreName);
|
|||
|
|||
url = virBufferContentAndReset(&buffer); |
|||
|
|||
@@ -3002,9 +3002,9 @@ esxDomainDefineXMLFlags(virConnectPtr conn, const char *xml, unsigned int flags)
|
|||
|
|||
virBufferURIEncodeString(&buffer, escapedName); |
|||
virBufferAddLit(&buffer, ".vmx?dcPath="); |
|||
- virBufferURIEncodeString(&buffer, priv->primary->datacenterPath);
|
|||
+ esxUtil_EscapeInventoryObject(&buffer, priv->primary->datacenterPath);
|
|||
virBufferAddLit(&buffer, "&dsName="); |
|||
- virBufferURIEncodeString(&buffer, datastoreName);
|
|||
+ esxUtil_EscapeInventoryObject(&buffer, datastoreName);
|
|||
|
|||
url = virBufferContentAndReset(&buffer); |
|||
|
|||
--
|
|||
2.52.0 |
|||
@ -0,0 +1,76 @@ |
|||
From 3a1f8bb838db0f412205e2918fc2eb4213f323ad Mon Sep 17 00:00:00 2001 |
|||
Message-ID: <3a1f8bb838db0f412205e2918fc2eb4213f323ad.1768317034.git.jdenemar@redhat.com> |
|||
From: Michal Privoznik <mprivozn@redhat.com> |
|||
Date: Wed, 7 Jan 2026 10:34:25 +0100 |
|||
Subject: [PATCH] esx_util: Introduce esxUtil_EscapeInventoryObject() |
|||
|
|||
The aim of this helper function is to URI-encode given string |
|||
twice. There's a bug (fixed in next commit) in which we're unable |
|||
to fetch .vmx file for a domain if corresponding datastore |
|||
contains some special characters (like +). Cole Robinson |
|||
discovered that encoding datastore twice enables libvirt to work |
|||
around the issue [2]. Well, this function does exactly that. |
|||
It was tested with the following inputs and all worked |
|||
flawlessly: "datastore", "datastore2", "datastore2+", |
|||
"datastore3+-@", "data store2+". |
|||
|
|||
1: https://issues.redhat.com/browse/RHEL-134127 |
|||
2: https://issues.redhat.com/browse/RHEL-133729#comment-28604072 |
|||
|
|||
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> |
|||
Reviewed-by: Jiri Denemark <jdenemar@redhat.com> |
|||
Reviewed-by: Richard W.M. Jones <rjones@redhat.com> |
|||
(cherry picked from commit ffe74c7c551bd641cbcaa2512ed0ad4a25d3980b) |
|||
Resolves: https://issues.redhat.com/browse/RHEL-134127 |
|||
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> |
|||
---
|
|||
src/esx/esx_util.c | 18 ++++++++++++++++++ |
|||
src/esx/esx_util.h | 3 +++ |
|||
2 files changed, 21 insertions(+) |
|||
|
|||
diff --git a/src/esx/esx_util.c b/src/esx/esx_util.c
|
|||
index 7ee0e5f7c0..9b714d90ba 100644
|
|||
--- a/src/esx/esx_util.c
|
|||
+++ b/src/esx/esx_util.c
|
|||
@@ -448,3 +448,21 @@ esxUtil_EscapeForXml(const char *string)
|
|||
|
|||
return virBufferContentAndReset(&buffer); |
|||
} |
|||
+
|
|||
+
|
|||
+/* esxUtil_EscapeInventoryObject:
|
|||
+ * @buf: the buffer to append to
|
|||
+ * @string: the string argument which will be URI-encoded
|
|||
+ *
|
|||
+ * URI-encode given @string TWICE and append the result to the @buf. This is
|
|||
+ * to be used with inventory objects (like 'dcPath' and 'dsName') to work
|
|||
+ * around a VMware bug in which once round of URI-encoding is not enough.
|
|||
+ */
|
|||
+void
|
|||
+esxUtil_EscapeInventoryObject(virBuffer *buf, const char *string)
|
|||
+{
|
|||
+ g_autoptr(GString) escaped = g_string_new(NULL);
|
|||
+
|
|||
+ g_string_append_uri_escaped(escaped, string, NULL, false);
|
|||
+ virBufferURIEncodeString(buf, escaped->str);
|
|||
+}
|
|||
diff --git a/src/esx/esx_util.h b/src/esx/esx_util.h
|
|||
index 58bc44e744..29f01e0c15 100644
|
|||
--- a/src/esx/esx_util.h
|
|||
+++ b/src/esx/esx_util.h
|
|||
@@ -22,6 +22,7 @@
|
|||
#pragma once |
|||
|
|||
#include "internal.h" |
|||
+#include "virbuffer.h"
|
|||
#include "viruri.h" |
|||
|
|||
#define ESX_VI_CHECK_ARG_LIST(val) \ |
|||
@@ -67,3 +68,5 @@ void esxUtil_ReplaceSpecialWindowsPathChars(char *string);
|
|||
char *esxUtil_EscapeDatastoreItem(const char *string); |
|||
|
|||
char *esxUtil_EscapeForXml(const char *string); |
|||
+
|
|||
+void esxUtil_EscapeInventoryObject(virBuffer *buf, const char *string);
|
|||
--
|
|||
2.52.0 |
|||
@ -0,0 +1,119 @@ |
|||
From 582ac1d5b308d1b9816c57ebca762a8796c67df4 Mon Sep 17 00:00:00 2001 |
|||
Message-ID: <582ac1d5b308d1b9816c57ebca762a8796c67df4.1766070256.git.jdenemar@redhat.com> |
|||
From: Peter Krempa <pkrempa@redhat.com> |
|||
Date: Mon, 1 Dec 2025 11:35:32 +0100 |
|||
Subject: [PATCH] qemu: tpm: Account for possible migration without actually |
|||
sharing storage |
|||
|
|||
The current logic in 'qemuTPMEmulatorBuildCommand' skips all setup if |
|||
the *location* of the data is on what we'd consider shared storage. |
|||
|
|||
This means that if the location is not actually shared (e.g. it's shared |
|||
betweeh some other hosts than the two doing the migration) and the path |
|||
wasn't ever used (e.g. by migrating out) from the host where we're |
|||
migrating into the complete setup of the location would be skipped even |
|||
when it doesn't exist. |
|||
|
|||
Fix the logic by skipping only some of the setup steps so that |
|||
'qemuTPMEmulatorCreateStorage' can still create the storage if it |
|||
doesn't exist. |
|||
|
|||
The rest of the code then needs to take the 'created' flag returned from |
|||
'qemuTPMEmulatorCreateStorage' into account. |
|||
|
|||
Fixes: 68103e9daf633b789428fedef56f816c92f6ee75 |
|||
Signed-off-by: Peter Krempa <pkrempa@redhat.com> |
|||
Reviewed-by: Michal Privoznik <mprivozn@redhat.com> |
|||
(cherry picked from commit d56d0560946770d4364a4918cc289e6a7fe5d15c) |
|||
https://issues.redhat.com/browse/RHEL-108915 |
|||
---
|
|||
src/qemu/qemu_tpm.c | 29 ++++++++++++++++++++--------- |
|||
1 file changed, 20 insertions(+), 9 deletions(-) |
|||
|
|||
diff --git a/src/qemu/qemu_tpm.c b/src/qemu/qemu_tpm.c
|
|||
index 4c9445d72c..660410bcba 100644
|
|||
--- a/src/qemu/qemu_tpm.c
|
|||
+++ b/src/qemu/qemu_tpm.c
|
|||
@@ -158,6 +158,7 @@ qemuTPMEmulatorGetPid(const char *swtpmStateDir,
|
|||
/** |
|||
* qemuTPMEmulatorCreateStorage: |
|||
* @tpm: TPM definition for an emulator type |
|||
+ * @sharedStorageMigration: VM is being migrated with possibly shared storage
|
|||
* @created: a pointer to a bool that will be set to true if the |
|||
* storage was created because it did not exist yet |
|||
* @swtpm_user: The uid that needs to be able to access the directory |
|||
@@ -169,6 +170,7 @@ qemuTPMEmulatorGetPid(const char *swtpmStateDir,
|
|||
*/ |
|||
static int |
|||
qemuTPMEmulatorCreateStorage(virDomainTPMDef *tpm, |
|||
+ bool sharedStorageMigration,
|
|||
bool *created, |
|||
uid_t swtpm_user, |
|||
gid_t swtpm_group) |
|||
@@ -187,8 +189,17 @@ qemuTPMEmulatorCreateStorage(virDomainTPMDef *tpm,
|
|||
*created = false; |
|||
|
|||
if (!virFileExists(source_path) || |
|||
- virDirIsEmpty(source_path, true) > 0)
|
|||
+ virDirIsEmpty(source_path, true) > 0) {
|
|||
*created = true; |
|||
+ } else {
|
|||
+ /* If the location exists and is shared, we don't need to create it
|
|||
+ * during migration */
|
|||
+ if (sharedStorageMigration) {
|
|||
+ VIR_DEBUG("Skipping TPM storage creation. Path '%s' already exists and is on shared storage.",
|
|||
+ source_path);
|
|||
+ return 0;
|
|||
+ }
|
|||
+ }
|
|||
|
|||
if (virDirCreate(source_path, 0700, swtpm_user, swtpm_group, |
|||
VIR_DIR_CREATE_ALLOW_EXIST) < 0) { |
|||
@@ -809,16 +820,13 @@ qemuTPMEmulatorBuildCommand(virDomainTPMDef *tpm,
|
|||
run_setup = true; |
|||
} |
|||
|
|||
- /* Do not create storage and run swtpm_setup on incoming migration over
|
|||
- * shared storage
|
|||
- */
|
|||
on_shared_storage = virFileIsSharedFS(tpm->data.emulator.source_path, |
|||
cfg->sharedFilesystems) == 1; |
|||
- if (incomingMigration && on_shared_storage)
|
|||
- create_storage = false;
|
|||
|
|||
if (create_storage) { |
|||
- if (qemuTPMEmulatorCreateStorage(tpm, &created,
|
|||
+ if (qemuTPMEmulatorCreateStorage(tpm,
|
|||
+ incomingMigration && on_shared_storage,
|
|||
+ &created,
|
|||
cfg->swtpm_user, cfg->swtpm_group) < 0) |
|||
return NULL; |
|||
run_setup = created; |
|||
@@ -885,6 +893,9 @@ qemuTPMEmulatorBuildCommand(virDomainTPMDef *tpm,
|
|||
/* If swtpm supports it and the TPM state is stored on shared storage, |
|||
* start swtpm with --migration release-lock-outgoing so it can migrate |
|||
* across shared storage if needed. |
|||
+ *
|
|||
+ * Note that if 'created' is true, the location didn't exist so the storage
|
|||
+ * is not actually shared.
|
|||
*/ |
|||
QEMU_DOMAIN_TPM_PRIVATE(tpm)->swtpm.can_migrate_shared_storage = false; |
|||
if (on_shared_storage && |
|||
@@ -892,13 +903,13 @@ qemuTPMEmulatorBuildCommand(virDomainTPMDef *tpm,
|
|||
|
|||
virCommandAddArg(cmd, "--migration"); |
|||
virCommandAddArgFormat(cmd, "release-lock-outgoing%s", |
|||
- incomingMigration ? ",incoming": "");
|
|||
+ incomingMigration && !created ? ",incoming": "");
|
|||
QEMU_DOMAIN_TPM_PRIVATE(tpm)->swtpm.can_migrate_shared_storage = true; |
|||
} else { |
|||
/* Report an error if there's an incoming migration across shared |
|||
* storage and swtpm does not support the --migration option. |
|||
*/ |
|||
- if (incomingMigration && on_shared_storage) {
|
|||
+ if (incomingMigration && on_shared_storage && !created) {
|
|||
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, |
|||
_("%1$s (on destination side) does not support the --migration option needed for migration with shared storage"), |
|||
swtpm); |
|||
--
|
|||
2.52.0 |
|||
@ -0,0 +1,69 @@ |
|||
From ba255cecade6c9f690997b53bfe517eef341695a Mon Sep 17 00:00:00 2001 |
|||
Message-ID: <ba255cecade6c9f690997b53bfe517eef341695a.1768317034.git.jdenemar@redhat.com> |
|||
From: Michal Privoznik <mprivozn@redhat.com> |
|||
Date: Tue, 6 Jan 2026 14:37:23 +0100 |
|||
Subject: [PATCH] qemu_validate: Drop VIR_DOMAIN_HYPERV_STIMER dependency on |
|||
VIR_DOMAIN_HYPERV_VPINDEX |
|||
MIME-Version: 1.0 |
|||
Content-Type: text/plain; charset=UTF-8 |
|||
Content-Transfer-Encoding: 8bit |
|||
|
|||
The original commit (v11.9.0-rc1~84) added a dependency checking |
|||
of VIR_DOMAIN_HYPERV_STIMER on VIR_DOMAIN_HYPERV_VPINDEX |
|||
(meaning, if stimer is on then vpindex must also be on). It |
|||
justified this by citing QEMU documentation: |
|||
|
|||
Per QEMU documentation (docs/system/i386/hyperv.rst): |
|||
|
|||
``hv-stimer`` |
|||
Enables Hyper-V synthetic timers. <snip/> |
|||
|
|||
Requires: ``hv-vpindex``, ``hv-synic``, ``hv-time`` |
|||
|
|||
While the documentation is almost correct (see previous commit |
|||
when it's incorrect), the code express no dependency on vpindex |
|||
(kvm_hyperv_properties[] array from target/i386/kvm/kvm.c): |
|||
|
|||
[HYPERV_FEAT_STIMER] = { |
|||
.desc = "synthetic timers (hv-stimer)", |
|||
.flags = { |
|||
{.func = HV_CPUID_FEATURES, .reg = R_EAX, |
|||
.bits = HV_SYNTIMERS_AVAILABLE} |
|||
}, |
|||
.dependencies = BIT(HYPERV_FEAT_SYNIC) | BIT(HYPERV_FEAT_TIME) |
|||
}, |
|||
|
|||
If transitivity is taken into account then the documentation is |
|||
of course correct (minus that one aforementioned special case). |
|||
Well, there's no need for us to implement transitional checks. |
|||
VIR_DOMAIN_HYPERV_STIMER requires VIR_DOMAIN_HYPERV_SYNIC and |
|||
whether that requires VIR_DOMAIN_HYPERV_VPINDEX is another |
|||
question. |
|||
|
|||
Just drop the transitive check. |
|||
|
|||
Resolves: https://gitlab.com/libvirt/libvirt/-/issues/837 |
|||
Resolves: https://issues.redhat.com/browse/RHEL-138689 |
|||
Fixes: da261327ea94300d1aa2d3b76ba9dcd4de6160f6 |
|||
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> |
|||
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> |
|||
(cherry picked from commit 6df374fefc94f5e729870dfe1ff65b62ee986fce) |
|||
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> |
|||
---
|
|||
src/qemu/qemu_validate.c | 1 - |
|||
1 file changed, 1 deletion(-) |
|||
|
|||
diff --git a/src/qemu/qemu_validate.c b/src/qemu/qemu_validate.c
|
|||
index 3e27c11da3..66c59c3696 100644
|
|||
--- a/src/qemu/qemu_validate.c
|
|||
+++ b/src/qemu/qemu_validate.c
|
|||
@@ -122,7 +122,6 @@ qemuValidateDomainDefHypervFeatures(const virDomainDef *def)
|
|||
} |
|||
} |
|||
|
|||
- CHECK_HV_FEAT(VIR_DOMAIN_HYPERV_STIMER, VIR_DOMAIN_HYPERV_VPINDEX);
|
|||
CHECK_HV_FEAT(VIR_DOMAIN_HYPERV_STIMER, VIR_DOMAIN_HYPERV_SYNIC); |
|||
|
|||
CHECK_HV_FEAT(VIR_DOMAIN_HYPERV_TLBFLUSH, VIR_DOMAIN_HYPERV_VPINDEX); |
|||
--
|
|||
2.52.0 |
|||
@ -0,0 +1,47 @@ |
|||
From e9a4c12db5da26084d8ec03b9249a53f980b3ad3 Mon Sep 17 00:00:00 2001 |
|||
Message-ID: <e9a4c12db5da26084d8ec03b9249a53f980b3ad3.1768317034.git.jdenemar@redhat.com> |
|||
From: Michal Privoznik <mprivozn@redhat.com> |
|||
Date: Tue, 6 Jan 2026 12:03:56 +0100 |
|||
Subject: [PATCH] qemu_validate: Drop VIR_DOMAIN_HYPERV_SYNIC dependency on |
|||
VIR_DOMAIN_HYPERV_VPINDEX |
|||
MIME-Version: 1.0 |
|||
Content-Type: text/plain; charset=UTF-8 |
|||
Content-Transfer-Encoding: 8bit |
|||
|
|||
Turns out, that synic hyperv enlightenment not always requires |
|||
vpindex. Some (older) machine types (e.g. pc-i440fx-3.0, |
|||
pc-q35-3.0, pc-i440fx-rhel7.6.0) can run with synic enabled and vpindex |
|||
disabled. This is because they did enable 'x-hv-synic-kvm-only' |
|||
CPU property, but starting from QEMU commit v3.1.0-rc0~44^2~9 the |
|||
property is disabled by default. |
|||
|
|||
To avoid parsing machine type version, let's just drop this |
|||
dependency validation and rely on QEMU to report sensible error |
|||
message. |
|||
|
|||
Resolves: https://gitlab.com/libvirt/libvirt/-/issues/837 |
|||
Resolves: https://issues.redhat.com/browse/RHEL-138689 |
|||
Fixes: 1822d030c32d9857020ee8385b0a8808a29a472f |
|||
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> |
|||
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> |
|||
(cherry picked from commit 8e9a9f86b046c5bc917875f4b3854f13c4b33b55) |
|||
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> |
|||
---
|
|||
src/qemu/qemu_validate.c | 2 -- |
|||
1 file changed, 2 deletions(-) |
|||
|
|||
diff --git a/src/qemu/qemu_validate.c b/src/qemu/qemu_validate.c
|
|||
index da08fd17cd..3e27c11da3 100644
|
|||
--- a/src/qemu/qemu_validate.c
|
|||
+++ b/src/qemu/qemu_validate.c
|
|||
@@ -112,8 +112,6 @@ qemuValidateDomainDefHypervFeatures(const virDomainDef *def)
|
|||
return -1; |
|||
} |
|||
|
|||
- CHECK_HV_FEAT(VIR_DOMAIN_HYPERV_SYNIC, VIR_DOMAIN_HYPERV_VPINDEX);
|
|||
-
|
|||
if (def->hyperv.features[VIR_DOMAIN_HYPERV_STIMER] == VIR_TRISTATE_SWITCH_ON) { |
|||
if (!virDomainDefHasTimer(def, VIR_DOMAIN_TIMER_NAME_HYPERVCLOCK)) { |
|||
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, |
|||
--
|
|||
2.52.0 |
|||
@ -0,0 +1,111 @@ |
|||
From b630429647207ba0d406cf855e590ddaa98fdb9b Mon Sep 17 00:00:00 2001 |
|||
Message-ID: <b630429647207ba0d406cf855e590ddaa98fdb9b.1766070256.git.jdenemar@redhat.com> |
|||
From: Jiri Denemark <jdenemar@redhat.com> |
|||
Date: Fri, 5 Dec 2025 15:09:15 +0100 |
|||
Subject: [PATCH] tests: Test virFileIsSharedFSOverride |
|||
|
|||
Technically virFileIsSharedFSOverride is available on any OS, but we |
|||
need a mocked realpath() to test it. Because the virfilemock library |
|||
also mocks statfs() which is only available on Linux, we don't even try |
|||
to load the library anywhere else. Thus we need to skip testing |
|||
virFileIsSharedFSOverride on non-Linux too. |
|||
|
|||
Signed-off-by: Jiri Denemark <jdenemar@redhat.com> |
|||
(cherry picked from commit 121d179e068b584f62ea2c029d89a44e67c909c0) |
|||
|
|||
https://issues.redhat.com/browse/RHEL-135287 |
|||
|
|||
Signed-off-by: Jiri Denemark <jdenemar@redhat.com> |
|||
---
|
|||
tests/virfiletest.c | 69 +++++++++++++++++++++++++++++++++++++++++++++ |
|||
1 file changed, 69 insertions(+) |
|||
|
|||
diff --git a/tests/virfiletest.c b/tests/virfiletest.c
|
|||
index e05925a321..ccd76a3fac 100644
|
|||
--- a/tests/virfiletest.c
|
|||
+++ b/tests/virfiletest.c
|
|||
@@ -329,6 +329,55 @@ testFileIsSharedFSType(const void *opaque G_GNUC_UNUSED)
|
|||
} |
|||
|
|||
|
|||
+static const char *shared_filesystems[] = {
|
|||
+ "/run/user/501/gvfs",
|
|||
+ "/nfs",
|
|||
+ "/gluster",
|
|||
+ "/ceph/multi",
|
|||
+ "/gpfs/data/blaf",
|
|||
+ "/quobyte",
|
|||
+ NULL,
|
|||
+};
|
|||
+
|
|||
+static int
|
|||
+testFileIsSharedFSOverride(const void *opaque G_GNUC_UNUSED)
|
|||
+{
|
|||
+#ifndef __linux__
|
|||
+ return EXIT_AM_SKIP;
|
|||
+#else
|
|||
+ const struct testFileIsSharedFSType *data = opaque;
|
|||
+ g_autofree char *mtabFile = NULL;
|
|||
+ bool actual;
|
|||
+ int ret = -1;
|
|||
+
|
|||
+ /* mtab is used by mocked realpath to decide whether a given path exists */
|
|||
+ mtabFile = g_strdup_printf(abs_srcdir "/virfiledata/%s", data->mtabFile);
|
|||
+
|
|||
+ if (!g_setenv("LIBVIRT_MTAB", mtabFile, true)) {
|
|||
+ fprintf(stderr, "Unable to set env variable\n");
|
|||
+ goto cleanup;
|
|||
+ }
|
|||
+
|
|||
+ actual = virFileIsSharedFSOverride(data->filename,
|
|||
+ (char * const *) shared_filesystems);
|
|||
+
|
|||
+ if (actual != data->expected) {
|
|||
+ fprintf(stderr, "FS of '%s' is %s. Expected: %s\n",
|
|||
+ data->filename,
|
|||
+ actual ? "shared" : "not shared",
|
|||
+ data->expected ? "shared" : "not shared");
|
|||
+ goto cleanup;
|
|||
+ }
|
|||
+
|
|||
+ ret = 0;
|
|||
+
|
|||
+ cleanup:
|
|||
+ g_unsetenv("LIBVIRT_MTAB");
|
|||
+ return ret;
|
|||
+#endif
|
|||
+}
|
|||
+
|
|||
+
|
|||
static int |
|||
mymain(void) |
|||
{ |
|||
@@ -439,6 +488,26 @@ mymain(void)
|
|||
DO_TEST_FILE_IS_SHARED_FS_TYPE("mounts3.txt", "/gpfs/data", true); |
|||
DO_TEST_FILE_IS_SHARED_FS_TYPE("mounts3.txt", "/quobyte", true); |
|||
|
|||
+#define DO_TEST_FILE_IS_SHARED_FS_OVERRIDE(mtab, file, exp) \
|
|||
+ do { \
|
|||
+ struct testFileIsSharedFSType data = { \
|
|||
+ .mtabFile = mtab, .filename = file, .expected = exp \
|
|||
+ }; \
|
|||
+ if (virTestRun(virTestCounterNext(), testFileIsSharedFSOverride, &data) < 0) \
|
|||
+ ret = -1; \
|
|||
+ } while (0)
|
|||
+
|
|||
+ virTestCounterReset("testFileIsSharedFSOverride ");
|
|||
+ DO_TEST_FILE_IS_SHARED_FS_OVERRIDE("mounts2.txt", "/boot/vmlinuz", false);
|
|||
+ DO_TEST_FILE_IS_SHARED_FS_OVERRIDE("mounts2.txt", "/run/user/501/gvfs/some/file", true);
|
|||
+ DO_TEST_FILE_IS_SHARED_FS_OVERRIDE("mounts3.txt", "/nfs/file", true);
|
|||
+ DO_TEST_FILE_IS_SHARED_FS_OVERRIDE("mounts3.txt", "/gluster/file", true);
|
|||
+ DO_TEST_FILE_IS_SHARED_FS_OVERRIDE("mounts3.txt", "/some/symlink/file", true);
|
|||
+ DO_TEST_FILE_IS_SHARED_FS_OVERRIDE("mounts3.txt", "/ceph/file", false);
|
|||
+ DO_TEST_FILE_IS_SHARED_FS_OVERRIDE("mounts3.txt", "/ceph/multi/file", true);
|
|||
+ DO_TEST_FILE_IS_SHARED_FS_OVERRIDE("mounts3.txt", "/gpfs/data", false);
|
|||
+ DO_TEST_FILE_IS_SHARED_FS_OVERRIDE("mounts3.txt", "/quobyte", true);
|
|||
+
|
|||
return ret != 0 ? EXIT_FAILURE : EXIT_SUCCESS; |
|||
} |
|||
|
|||
--
|
|||
2.52.0 |
|||
@ -0,0 +1,114 @@ |
|||
From 7f7d60e42f39deaec69318b93cf922f1dda54a26 Mon Sep 17 00:00:00 2001 |
|||
Message-ID: <7f7d60e42f39deaec69318b93cf922f1dda54a26.1766070256.git.jdenemar@redhat.com> |
|||
From: Jiri Denemark <jdenemar@redhat.com> |
|||
Date: Fri, 5 Dec 2025 16:51:25 +0100 |
|||
Subject: [PATCH] util: Fix race condition in virFileIsSharedFSOverride |
|||
|
|||
Switch virFileIsSharedFSOverride to use virFileCheckParents to avoid a |
|||
race which could result in virFileCanonicalizePath to be called on a |
|||
path that does not exist anymore. |
|||
|
|||
Signed-off-by: Jiri Denemark <jdenemar@redhat.com> |
|||
(cherry picked from commit 3a44f0c23d75519a9a374f790f4b91ab7b65a138) |
|||
|
|||
https://issues.redhat.com/browse/RHEL-135287 |
|||
|
|||
Signed-off-by: Jiri Denemark <jdenemar@redhat.com> |
|||
---
|
|||
src/util/virfile.c | 59 +++++++++++++++++----------------------------- |
|||
1 file changed, 22 insertions(+), 37 deletions(-) |
|||
|
|||
diff --git a/src/util/virfile.c b/src/util/virfile.c
|
|||
index 95fc8ff0e6..52d711d2a9 100644
|
|||
--- a/src/util/virfile.c
|
|||
+++ b/src/util/virfile.c
|
|||
@@ -3502,33 +3502,6 @@ virFileCheckParents(const char *path,
|
|||
} |
|||
|
|||
|
|||
-static char *
|
|||
-virFileGetExistingParent(const char *path)
|
|||
-{
|
|||
- g_autofree char *dirpath = g_strdup(path);
|
|||
- char *p = NULL;
|
|||
-
|
|||
- /* Try less and less of the path until we get to a directory we can access.
|
|||
- * Even if we don't have 'x' permission on any directory in the path on the
|
|||
- * NFS server (assuming it's NFS), we will be able to stat the mount point.
|
|||
- */
|
|||
- while (!virFileExists(dirpath) && p != dirpath) {
|
|||
- if (!(p = strrchr(dirpath, '/'))) {
|
|||
- virReportSystemError(EINVAL,
|
|||
- _("Invalid relative path '%1$s'"), path);
|
|||
- return NULL;
|
|||
- }
|
|||
-
|
|||
- if (p == dirpath)
|
|||
- *(p + 1) = '\0';
|
|||
- else
|
|||
- *p = '\0';
|
|||
- }
|
|||
-
|
|||
- return g_steal_pointer(&dirpath);
|
|||
-}
|
|||
-
|
|||
-
|
|||
#ifdef __linux__ |
|||
|
|||
# ifndef NFS_SUPER_MAGIC |
|||
@@ -3875,6 +3848,17 @@ virFileGetDefaultHugepage(virHugeTLBFS *fs,
|
|||
} |
|||
|
|||
|
|||
+static bool
|
|||
+virFileCheckParentsCanonicalize(const char *path,
|
|||
+ void *opaque)
|
|||
+{
|
|||
+ char **canonical = opaque;
|
|||
+
|
|||
+ *canonical = virFileCanonicalizePath(path);
|
|||
+ return !!*canonical;
|
|||
+}
|
|||
+
|
|||
+
|
|||
/** |
|||
* virFileIsSharedFSOverride: |
|||
* @path: Path to check |
|||
@@ -3888,24 +3872,25 @@ virFileIsSharedFSOverride(const char *path,
|
|||
char *const *overrides) |
|||
{ |
|||
g_autofree char *dirpath = NULL; |
|||
- g_autofree char *existing = NULL;
|
|||
char *p = NULL; |
|||
+ int rc;
|
|||
|
|||
if (!path || path[0] != '/' || !overrides) |
|||
return false; |
|||
|
|||
/* We only care about the longest existing sub-path. Further components |
|||
- * may will later be created by libvirt will not magically become a shared
|
|||
- * filesystem. */
|
|||
- if (!(existing = virFileGetExistingParent(path)))
|
|||
+ * that may later be created by libvirt will not magically become a shared
|
|||
+ * filesystem. Overrides have been canonicalized ahead of time, so we need
|
|||
+ * to do the same for the provided path or we'll never be able to find a
|
|||
+ * match if symlinks are involved.
|
|||
+ */
|
|||
+ rc = virFileCheckParents(path, NULL,
|
|||
+ virFileCheckParentsCanonicalize, &dirpath);
|
|||
+ if (rc == -1)
|
|||
return false; |
|||
|
|||
- /* Overrides have been canonicalized ahead of time, so we need to
|
|||
- * do the same for the provided path or we'll never be able to
|
|||
- * find a match if symlinks are involved */
|
|||
- if (!(dirpath = virFileCanonicalizePath(existing))) {
|
|||
- VIR_DEBUG("Cannot canonicalize parent '%s' of path '%s'",
|
|||
- existing, path);
|
|||
+ if (rc != 0) {
|
|||
+ VIR_DEBUG("Cannot canonicalize path '%s'", path);
|
|||
return false; |
|||
} |
|||
|
|||
--
|
|||
2.52.0 |
|||
@ -0,0 +1,144 @@ |
|||
From 4596ee4c2fe33d3b44a44b120d61052ac943bae4 Mon Sep 17 00:00:00 2001 |
|||
Message-ID: <4596ee4c2fe33d3b44a44b120d61052ac943bae4.1766070256.git.jdenemar@redhat.com> |
|||
From: Jiri Denemark <jdenemar@redhat.com> |
|||
Date: Fri, 5 Dec 2025 16:47:14 +0100 |
|||
Subject: [PATCH] util: Fix race condition in virFileIsSharedFSType |
|||
|
|||
virFileIsSharedFSType could end up calling statfs on a path that no |
|||
longer exists and return an error. If this happens for a path on a |
|||
shared filesystem, the caller may incorrectly consider the path as |
|||
non-shared. |
|||
|
|||
Specifically, when starting a domain with TPM enabled and deciding |
|||
whether its vTPM state is stored on a shared storage, the race could |
|||
cause qemuTPMEmulatorBuildCommand to consider the state to be |
|||
non-shared. This means swtpm would be started without --migration even |
|||
when the state is actually stored on a shared storage and any attempt to |
|||
migrate such domain would fail with |
|||
|
|||
Operation not supported: the running swtpm does not support |
|||
migration with shared storage |
|||
|
|||
In fact, any caller of virFileGetExistingParent contained an inherent |
|||
TOCTOU race condition as the existing parent of a given path return by |
|||
virFileGetExistingParent may no longer exist at the time the caller |
|||
wants to check it. |
|||
|
|||
This patch introduces a new virFileCheckParents API which is almost |
|||
identical to virFileGetExistingParent, but uses a supplied callback to |
|||
check each path. This new API is used in virFileIsSharedFSType to avoid |
|||
the race. The old function will later be completely removed once all |
|||
callers are switched to the new one. |
|||
|
|||
Fixes: 05526b50909ff50c16e13a0b5580d41de74e3d59 |
|||
Signed-off-by: Jiri Denemark <jdenemar@redhat.com> |
|||
(cherry picked from commit b6addd42bece693debbf2e95551a2b4d2e1b453f) |
|||
|
|||
https://issues.redhat.com/browse/RHEL-135287 |
|||
|
|||
Signed-off-by: Jiri Denemark <jdenemar@redhat.com> |
|||
---
|
|||
src/util/virfile.c | 71 ++++++++++++++++++++++++++++++++++++++++++++-- |
|||
1 file changed, 69 insertions(+), 2 deletions(-) |
|||
|
|||
diff --git a/src/util/virfile.c b/src/util/virfile.c
|
|||
index a5c9fbe0d9..95fc8ff0e6 100644
|
|||
--- a/src/util/virfile.c
|
|||
+++ b/src/util/virfile.c
|
|||
@@ -3445,6 +3445,63 @@ virFileRemoveLastComponent(char *path)
|
|||
} |
|||
|
|||
|
|||
+/* Check callback for virFileCheckParents */
|
|||
+typedef bool (*virFileCheckParentsCallback)(const char *dirpath,
|
|||
+ void *opaque);
|
|||
+
|
|||
+/**
|
|||
+ * virFileCheckParents:
|
|||
+ * @path: path to check
|
|||
+ * @parent: where to store the closest parent satisfying the check
|
|||
+ * @check: callback called on parent paths
|
|||
+ * @opaque: data for the @check callback
|
|||
+ *
|
|||
+ * Calls @check on the @path and its parent paths until it returns true or a
|
|||
+ * root directory is reached. When @check returns true, the @parent (if
|
|||
+ * non-NULL) will be set to a copy of the corresponding path. The caller is
|
|||
+ * responsible for freeing it.
|
|||
+ *
|
|||
+ * Returns 0 on success (@parent set),
|
|||
+ * -1 on invalid input,
|
|||
+ * -2 when no path (including "/") satisfies the @check.
|
|||
+ */
|
|||
+static int
|
|||
+virFileCheckParents(const char *path,
|
|||
+ char **parent,
|
|||
+ virFileCheckParentsCallback check,
|
|||
+ void *opaque)
|
|||
+{
|
|||
+ g_autofree char *dirpath = g_strdup(path);
|
|||
+ char *p = NULL;
|
|||
+ bool checkOK;
|
|||
+
|
|||
+ checkOK = check(dirpath, opaque);
|
|||
+
|
|||
+ while (!checkOK && p != dirpath) {
|
|||
+ if (!(p = strrchr(dirpath, G_DIR_SEPARATOR))) {
|
|||
+ virReportSystemError(EINVAL,
|
|||
+ _("Invalid absolute path '%1$s'"), path);
|
|||
+ return -1;
|
|||
+ }
|
|||
+
|
|||
+ if (p == dirpath)
|
|||
+ *(p + 1) = '\0';
|
|||
+ else
|
|||
+ *p = '\0';
|
|||
+
|
|||
+ checkOK = check(dirpath, opaque);
|
|||
+ }
|
|||
+
|
|||
+ if (!checkOK)
|
|||
+ return -2;
|
|||
+
|
|||
+ if (parent)
|
|||
+ *parent = g_steal_pointer(&dirpath);
|
|||
+
|
|||
+ return 0;
|
|||
+}
|
|||
+
|
|||
+
|
|||
static char * |
|||
virFileGetExistingParent(const char *path) |
|||
{ |
|||
@@ -3599,6 +3656,14 @@ static const struct virFileSharedFsData virFileSharedFs[] = {
|
|||
}; |
|||
|
|||
|
|||
+static bool
|
|||
+virFileCheckParentsStatFS(const char *path,
|
|||
+ void *opaque)
|
|||
+{
|
|||
+ return statfs(path, (struct statfs *) opaque) == 0;
|
|||
+}
|
|||
+
|
|||
+
|
|||
int |
|||
virFileIsSharedFSType(const char *path, |
|||
unsigned int fstypes) |
|||
@@ -3607,11 +3672,13 @@ virFileIsSharedFSType(const char *path,
|
|||
struct statfs sb; |
|||
long long f_type = 0; |
|||
size_t i; |
|||
+ int rc;
|
|||
|
|||
- if (!(dirpath = virFileGetExistingParent(path)))
|
|||
+ if ((rc = virFileCheckParents(path, &dirpath,
|
|||
+ virFileCheckParentsStatFS, &sb)) == -1)
|
|||
return -1; |
|||
|
|||
- if (statfs(dirpath, &sb) < 0) {
|
|||
+ if (rc != 0) {
|
|||
virReportSystemError(errno, |
|||
_("cannot determine filesystem for '%1$s'"), |
|||
path); |
|||
--
|
|||
2.52.0 |
|||
@ -0,0 +1,84 @@ |
|||
From d85627338e531618aa72b6039483b0d0a3e3d474 Mon Sep 17 00:00:00 2001 |
|||
Message-ID: <d85627338e531618aa72b6039483b0d0a3e3d474.1766070256.git.jdenemar@redhat.com> |
|||
From: Jiri Denemark <jdenemar@redhat.com> |
|||
Date: Fri, 5 Dec 2025 16:52:32 +0100 |
|||
Subject: [PATCH] util: Rework virFileIsSharedFSOverride using |
|||
virFileCheckParents |
|||
|
|||
The newly introduced virFileCheckParents is generic enough to be used |
|||
for checking whether a specific path or any of its parents is included |
|||
in the overrides array. |
|||
|
|||
Signed-off-by: Jiri Denemark <jdenemar@redhat.com> |
|||
(cherry picked from commit eedf9ed68b45585569865604bf2a403670feaf3e) |
|||
|
|||
https://issues.redhat.com/browse/RHEL-135287 |
|||
|
|||
Signed-off-by: Jiri Denemark <jdenemar@redhat.com> |
|||
---
|
|||
src/util/virfile.c | 35 ++++++++++++----------------------- |
|||
1 file changed, 12 insertions(+), 23 deletions(-) |
|||
|
|||
diff --git a/src/util/virfile.c b/src/util/virfile.c
|
|||
index 52d711d2a9..05b2fa8168 100644
|
|||
--- a/src/util/virfile.c
|
|||
+++ b/src/util/virfile.c
|
|||
@@ -3859,6 +3859,14 @@ virFileCheckParentsCanonicalize(const char *path,
|
|||
} |
|||
|
|||
|
|||
+static bool
|
|||
+virFileCheckParentsInOverrides(const char *path,
|
|||
+ void *opaque)
|
|||
+{
|
|||
+ return g_strv_contains((const char *const *) opaque, path);
|
|||
+}
|
|||
+
|
|||
+
|
|||
/** |
|||
* virFileIsSharedFSOverride: |
|||
* @path: Path to check |
|||
@@ -3872,7 +3880,6 @@ virFileIsSharedFSOverride(const char *path,
|
|||
char *const *overrides) |
|||
{ |
|||
g_autofree char *dirpath = NULL; |
|||
- char *p = NULL;
|
|||
int rc; |
|||
|
|||
if (!path || path[0] != '/' || !overrides) |
|||
@@ -3894,29 +3901,11 @@ virFileIsSharedFSOverride(const char *path,
|
|||
return false; |
|||
} |
|||
|
|||
- if (g_strv_contains((const char *const *) overrides, dirpath))
|
|||
- return true;
|
|||
+ if (virFileCheckParents(dirpath, NULL, virFileCheckParentsInOverrides,
|
|||
+ (void *) overrides) < 0)
|
|||
+ return false;
|
|||
|
|||
- /* Continue until we've scanned the entire path */
|
|||
- while (p != dirpath) {
|
|||
-
|
|||
- /* Find the last slash */
|
|||
- if ((p = strrchr(dirpath, '/')) == NULL)
|
|||
- break;
|
|||
-
|
|||
- /* Truncate the path by overwriting the slash that we've just
|
|||
- * found with a null byte. If it is the very first slash in
|
|||
- * the path, we need to handle things slightly differently */
|
|||
- if (p == dirpath)
|
|||
- *(p+1) = '\0';
|
|||
- else
|
|||
- *p = '\0';
|
|||
-
|
|||
- if (g_strv_contains((const char *const *) overrides, dirpath))
|
|||
- return true;
|
|||
- }
|
|||
-
|
|||
- return false;
|
|||
+ return true;
|
|||
} |
|||
|
|||
|
|||
--
|
|||
2.52.0 |
|||
@ -0,0 +1,50 @@ |
|||
From 4540f9271990c01649029ab2c9fd6414109e6583 Mon Sep 17 00:00:00 2001 |
|||
Message-ID: <4540f9271990c01649029ab2c9fd6414109e6583.1768317034.git.jdenemar@redhat.com> |
|||
From: Peter Krempa <pkrempa@redhat.com> |
|||
Date: Thu, 11 Dec 2025 09:39:03 +0100 |
|||
Subject: [PATCH] util: json: Increase JSON nesting limit when parsing to 300 |
|||
MIME-Version: 1.0 |
|||
Content-Type: text/plain; charset=UTF-8 |
|||
Content-Transfer-Encoding: 8bit |
|||
|
|||
The default in json-c is 32 which is too low to accomodate the 200 |
|||
snapshot layers we supported historically in the qemu driver (200 is |
|||
picked based on the 256 layer limit in libxml). |
|||
|
|||
The response to 'query-block' is otherwise too low and we fail to start |
|||
the VM when there's around 26 images in a backing chain. |
|||
|
|||
'json_tokener_new_ex' is supported since json-c 0.11 and we require at |
|||
least 0.14. |
|||
|
|||
Signed-off-by: Peter Krempa <pkrempa@redhat.com> |
|||
Reviewed-by: Ján Tomko <jtomko@redhat.com> |
|||
(cherry picked from commit b49d41b7e9eb983fdfbf70c91c2a27a995af3987) |
|||
https://issues.redhat.com/browse/RHEL-135128 |
|||
---
|
|||
src/util/virjson.c | 10 +++++++++- |
|||
1 file changed, 9 insertions(+), 1 deletion(-) |
|||
|
|||
diff --git a/src/util/virjson.c b/src/util/virjson.c
|
|||
index a799707c16..454bd657be 100644
|
|||
--- a/src/util/virjson.c
|
|||
+++ b/src/util/virjson.c
|
|||
@@ -1466,7 +1466,15 @@ virJSONValueFromString(const char *jsonstring)
|
|||
|
|||
VIR_DEBUG("string=%s", jsonstring); |
|||
|
|||
- tok = json_tokener_new();
|
|||
+ /* When creating the tokener we need to specify the limit of the nesting
|
|||
+ * depth of JSON objects. The default in json-c is 32. Since we need to
|
|||
+ * support at least 200 layers of snapshots (the limit is based on a
|
|||
+ * conservative take on the 256 layer nesting limit for XML in libxml), for
|
|||
+ * which we have internal checks, we also need to set the JSON limit to
|
|||
+ * be able to parse qemu responses for such a deeply nested snapshot list.
|
|||
+ * '300' is picked a sa conservative buffer on top of the 200 layers plus
|
|||
+ * some of the extra wrappers that qemu adds*/
|
|||
+ tok = json_tokener_new_ex(300);
|
|||
if (!tok) { |
|||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", |
|||
_("failed to create JSON tokener")); |
|||
--
|
|||
2.52.0 |
|||
@ -0,0 +1,45 @@ |
|||
From ee409902d270d19ae1c7a1fc3e0af67320bdb26b Mon Sep 17 00:00:00 2001 |
|||
Message-ID: <ee409902d270d19ae1c7a1fc3e0af67320bdb26b.1768317034.git.jdenemar@redhat.com> |
|||
From: Peter Krempa <pkrempa@redhat.com> |
|||
Date: Mon, 5 Jan 2026 15:00:18 +0100 |
|||
Subject: [PATCH] virjsontest: Add test for nesting depth |
|||
MIME-Version: 1.0 |
|||
Content-Type: text/plain; charset=UTF-8 |
|||
Content-Transfer-Encoding: 8bit |
|||
|
|||
Add an example of 250 layer deep nested JSON to make sure the parser |
|||
supports it. This is in order to maintain compatibility with external |
|||
snapshots in qemu, where such a deeply nested document is returned with |
|||
a 'query-block' QMP call. |
|||
|
|||
I've used a fake JSON as a real reply from qemu is around 1.4MiB for a |
|||
200 deep image chain. |
|||
|
|||
Signed-off-by: Peter Krempa <pkrempa@redhat.com> |
|||
Reviewed-by: Ján Tomko <jtomko@redhat.com> |
|||
(cherry picked from commit 16804acf14616d7357ad6a336f2ffd6d255a8d63) |
|||
https://issues.redhat.com/browse/RHEL-135128 |
|||
---
|
|||
tests/virjsondata/parse-nesting-in.json | 1 + |
|||
tests/virjsondata/parse-nesting-out.json | 1 + |
|||
2 files changed, 2 insertions(+) |
|||
create mode 100644 tests/virjsondata/parse-nesting-in.json |
|||
create mode 120000 tests/virjsondata/parse-nesting-out.json |
|||
|
|||
diff --git a/tests/virjsondata/parse-nesting-in.json b/tests/virjsondata/parse-nesting-in.json
|
|||
new file mode 100644 |
|||
index 0000000000..8bbe1a3439
|
|||
--- /dev/null
|
|||
+++ b/tests/virjsondata/parse-nesting-in.json
|
|||
@@ -0,0 +1 @@
|
|||
+{"n249": {"n248": {"n247": {"n246": {"n245": {"n244": {"n243": {"n242": {"n241": {"n240": {"n239": {"n238": {"n237": {"n236": {"n235": {"n234": {"n233": {"n232": {"n231": {"n230": {"n229": {"n228": {"n227": {"n226": {"n225": {"n224": {"n223": {"n222": {"n221": {"n220": {"n219": {"n218": {"n217": {"n216": {"n215": {"n214": {"n213": {"n212": {"n211": {"n210": {"n209": {"n208": {"n207": {"n206": {"n205": {"n204": {"n203": {"n202": {"n201": {"n200": {"n199": {"n198": {"n197": {"n196": {"n195": {"n194": {"n193": {"n192": {"n191": {"n190": {"n189": {"n188": {"n187": {"n186": {"n185": {"n184": {"n183": {"n182": {"n181": {"n180": {"n179": {"n178": {"n177": {"n176": {"n175": {"n174": {"n173": {"n172": {"n171": {"n170": {"n169": {"n168": {"n167": {"n166": {"n165": {"n164": {"n163": {"n162": {"n161": {"n160": {"n159": {"n158": {"n157": {"n156": {"n155": {"n154": {"n153": {"n152": {"n151": {"n150": {"n149": {"n148": {"n147": {"n146": {"n145": {"n144": {"n143": {"n142": {"n141": {"n140": {"n139": {"n138": {"n137": {"n136": {"n135": {"n134": {"n133": {"n132": {"n131": {"n130": {"n129": {"n128": {"n127": {"n126": {"n125": {"n124": {"n123": {"n122": {"n121": {"n120": {"n119": {"n118": {"n117": {"n116": {"n115": {"n114": {"n113": {"n112": {"n111": {"n110": {"n109": {"n108": {"n107": {"n106": {"n105": {"n104": {"n103": {"n102": {"n101": {"n100": {"n99": {"n98": {"n97": {"n96": {"n95": {"n94": {"n93": {"n92": {"n91": {"n90": {"n89": {"n88": {"n87": {"n86": {"n85": {"n84": {"n83": {"n82": {"n81": {"n80": {"n79": {"n78": {"n77": {"n76": {"n75": {"n74": {"n73": {"n72": {"n71": {"n70": {"n69": {"n68": {"n67": {"n66": {"n65": {"n64": {"n63": {"n62": {"n61": {"n60": {"n59": {"n58": {"n57": {"n56": {"n55": {"n54": {"n53": {"n52": {"n51": {"n50": {"n49": {"n48": {"n47": {"n46": {"n45": {"n44": {"n43": {"n42": {"n41": {"n40": {"n39": {"n38": {"n37": {"n36": {"n35": {"n34": {"n33": {"n32": {"n31": {"n30": {"n29": {"n28": {"n27": {"n26": {"n25": {"n24": {"n23": {"n22": {"n21": {"n20": {"n19": {"n18": {"n17": {"n16": {"n15": {"n14": {"n13": {"n12": {"n11": {"n10": {"n9": {"n8": {"n7": {"n6": {"n5": {"n4": {"n3": {"n2": {"n1": {"n0": "end"}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
|
|||
diff --git a/tests/virjsondata/parse-nesting-out.json b/tests/virjsondata/parse-nesting-out.json
|
|||
new file mode 120000 |
|||
index 0000000000..d269172843
|
|||
--- /dev/null
|
|||
+++ b/tests/virjsondata/parse-nesting-out.json
|
|||
@@ -0,0 +1 @@
|
|||
+parse-nesting-in.json
|
|||
\ No newline at end of file |
|||
--
|
|||
2.52.0 |
|||
Loading…
Reference in new issue