Sync with a10-beta

This commit is contained in:
Andrew Lukoshko 2026-06-08 13:23:54 +00:00
parent dbe3130b9c
commit b9f12a4084
39 changed files with 1158 additions and 1957 deletions

11
.gitignore vendored
View File

@ -1,2 +1,9 @@
libguestfs-1.56.1.tar.gz
libguestfs.keyring
*~
.build*.log
*.rpm
clog
/x86_64/
/i386/
/noarch/
/libguestfs-*.tar.gz
/libguestfs-*.tar.gz.sig

View File

@ -1,29 +0,0 @@
From dc218b25f0bc2704918748e4e8120ec436783e58 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Tue, 24 Jun 2025 14:04:10 +0100
Subject: [PATCH] appliance: Ignore sit0 network device in the guest
Reported-by: Srikanth Aithal <sraithal@amd.com>
Fixed-by: Stefano Brivio <sbrivio@redhat.com>
Tested-by: Srikanth Aithal <sraithal@amd.com>
See-also: https://lists.libguestfs.org/archives/list/guestfs@lists.libguestfs.org/thread/566LAY7RNM7T7EMQQQYIQA2VK5TXETK5/
---
appliance/init | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/appliance/init b/appliance/init
index 5d35a47dd..47eb97dfc 100755
--- a/appliance/init
+++ b/appliance/init
@@ -127,7 +127,7 @@ ip addr add 127.0.0.1/8 brd + dev lo scope host
ip link set dev lo up
if test "$guestfs_network" = 1; then
- iface=$(ls -I all -I default -I lo /proc/sys/net/ipv4/conf)
+ iface=$(ls -I all -I default -I lo -I sit0 /proc/sys/net/ipv4/conf)
# Two workarounds for Ubuntu:
touch /etc/fstab
rm -f /etc/dhcp/dhclient-enter-hooks.d/resolved
--
2.47.3

View File

@ -0,0 +1,42 @@
From 07ea5915766d829b62255fb2a94239a28f433159 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Mon, 19 Jan 2026 21:36:08 +0000
Subject: [PATCH] generator/daemon.ml: Avoid not available macro for OCaml
functions
Reported-by: Toolybird
Fixes: https://github.com/libguestfs/libguestfs/issues/290
(cherry picked from commit 1b79e22be443b48dc2bdb518cfea14342b3d8136)
---
generator/daemon.ml | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/generator/daemon.ml b/generator/daemon.ml
index 65844309e..f1f954087 100644
--- a/generator/daemon.ml
+++ b/generator/daemon.ml
@@ -1054,7 +1054,8 @@ let generate_daemon_optgroups_h () =
pr "#define OPTGROUP_%s_NOT_AVAILABLE \\\n"
(String.uppercase_ascii group);
List.iter (
- fun { name; style = ret, args, optargs } ->
+ function
+ | { name; style = ret, args, optargs; impl = C } ->
let style = ret, args @ args_of_optargs optargs, [] in
pr " ";
generate_prototype
@@ -1065,6 +1066,11 @@ let generate_daemon_optgroups_h () =
~semicolon:false
name style;
pr " { abort (); } \\\n"
+ | { impl = OCaml _ } ->
+ (* Don't need to generate anything for OCaml functions since
+ * the caml-stubs do_* function will still exist.
+ *)
+ ()
) fns;
pr " int optgroup_%s_available (void) { return 0; }\n" group;
pr "\n"
--
2.47.3

View File

@ -1,4 +1,4 @@
From d1808ea5eb7ad9c38f5f8c5e90d086886300acd8 Mon Sep 17 00:00:00 2001
From d9e9cc39c92288b713320b4d7f6f66d1b1e4ff42 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Mon, 29 Jul 2013 14:47:56 +0100
Subject: [PATCH] RHEL: Disable unsupported remote drive protocols
@ -180,21 +180,21 @@ index e4e1021db..8419ce78a 100755
rm test-add-uri.out
rm test-add-uri.img
diff --git a/generator/actions_core.ml b/generator/actions_core.ml
index 60d3140ed..d374ffbf8 100644
index b4ec6db87..9c23da008 100644
--- a/generator/actions_core.ml
+++ b/generator/actions_core.ml
@@ -350,22 +350,6 @@ F<filename> is interpreted as a local file or device.
@@ -343,22 +343,6 @@ F<filename> is interpreted as a local file or device.
This is the default if the optional protocol parameter
is omitted.
-=item C<protocol = \"ftp\"|\"ftps\"|\"http\"|\"https\">
-=item C<protocol = "ftp"|"ftps"|"http"|"https">
-
-Connect to a remote FTP or HTTP server.
-The C<server> parameter must also be supplied - see below.
-
-See also: L<guestfs(3)/FTP AND HTTP>
-
-=item C<protocol = \"iscsi\">
-=item C<protocol = "iscsi">
-
-Connect to the iSCSI server.
-The C<server> parameter must also be supplied - see below.
@ -203,14 +203,14 @@ index 60d3140ed..d374ffbf8 100644
-
-See also: L<guestfs(3)/ISCSI>.
-
=item C<protocol = \"nbd\">
=item C<protocol = "nbd">
Connect to the Network Block Device server.
@@ -382,15 +366,6 @@ The C<secret> parameter may be supplied. See below.
@@ -375,15 +359,6 @@ The C<secret> parameter may be supplied. See below.
See also: L<guestfs(3)/CEPH>.
-=item C<protocol = \"ssh\">
-=item C<protocol = "ssh">
-
-Connect to the Secure Shell (ssh) server.
-
@ -222,7 +222,7 @@ index 60d3140ed..d374ffbf8 100644
=back
=item C<server>
@@ -401,11 +376,8 @@ is a list of server(s).
@@ -394,11 +369,8 @@ is a list of server(s).
Protocol Number of servers required
-------- --------------------------
file List must be empty or param not used at all
@ -234,7 +234,7 @@ index 60d3140ed..d374ffbf8 100644
Each list element is a string specifying a server. The string must be
in one of the following formats:
@@ -421,10 +393,10 @@ for the protocol is used (see F</etc/services>).
@@ -414,10 +386,10 @@ for the protocol is used (see F</etc/services>).
=item C<username>
@ -249,7 +249,7 @@ index 60d3140ed..d374ffbf8 100644
example if using the libvirt backend and if the libvirt backend is configured to
start the qemu appliance as a special user such as C<qemu.qemu>. If in doubt,
diff --git a/lib/drives.c b/lib/drives.c
index c068b8ecb..6e4453ce5 100644
index 6a62623d5..8c334c6c7 100644
--- a/lib/drives.c
+++ b/lib/drives.c
@@ -166,34 +166,6 @@ create_drive_non_file (guestfs_h *g,
@ -355,7 +355,7 @@ index c068b8ecb..6e4453ce5 100644
/**
* Create the special F</dev/null> drive.
*
@@ -768,26 +679,6 @@ guestfs_impl_add_drive_opts (guestfs_h *g, const char *filename,
@@ -769,26 +680,6 @@ guestfs_impl_add_drive_opts (guestfs_h *g,
drv = create_drive_file (g, &data);
}
}
@ -382,7 +382,7 @@ index c068b8ecb..6e4453ce5 100644
else if (STREQ (protocol, "nbd")) {
data.protocol = drive_protocol_nbd;
drv = create_drive_nbd (g, &data);
@@ -796,10 +687,6 @@ guestfs_impl_add_drive_opts (guestfs_h *g, const char *filename,
@@ -797,10 +688,6 @@ guestfs_impl_add_drive_opts (guestfs_h *g,
data.protocol = drive_protocol_rbd;
drv = create_drive_rbd (g, &data);
}
@ -497,10 +497,10 @@ index 485d75718..e917cd1a6 100755
$guestfish -d nbd run ||:
diff --git a/tests/disks/test-qemu-drive.sh b/tests/disks/test-qemu-drive.sh
index d6ce0f07f..153b9c8cc 100755
index f5ecccc3c..d699d37ca 100755
--- a/tests/disks/test-qemu-drive.sh
+++ b/tests/disks/test-qemu-drive.sh
@@ -63,35 +63,6 @@ check_output
@@ -64,35 +64,6 @@ check_output
grep -sq -- '-drive file=rbd:abc-def/ghi-jkl:auth_supported=none,' "$DEBUG_QEMU_FILE" || fail
rm "$DEBUG_QEMU_FILE"
@ -536,7 +536,7 @@ index d6ce0f07f..153b9c8cc 100755
# NBD.
guestfish <<EOF ||:
@@ -109,14 +80,3 @@ EOF
@@ -110,14 +81,3 @@ EOF
check_output
grep -sq -- '-drive file=nbd:unix:/socket,' "$DEBUG_QEMU_FILE" || fail
rm "$DEBUG_QEMU_FILE"

View File

@ -1,29 +0,0 @@
From 0a91731356a5bb0ab8eee620fc1fed1656b117f9 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Fri, 25 Jul 2025 09:36:35 +0100
Subject: [PATCH] lib: libvirt: Debug error from virDomainDestroyFlags
It's useful to see the error returned from virDomainDestroyFlags, so
make sure this gets written to debug output.
---
lib/launch-libvirt.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/lib/launch-libvirt.c b/lib/launch-libvirt.c
index 55a4ad41c..8dbde5341 100644
--- a/lib/launch-libvirt.c
+++ b/lib/launch-libvirt.c
@@ -2173,6 +2173,10 @@ destroy_domain (guestfs_h *g, virDomainPtr dom, int check_for_errors)
/* Error returned by virDomainDestroyFlags ... */
err = virGetLastError ();
+ if (err && err->code != 0) {
+ debug (g, "virDomainDestroy: %s [code=%d int1=%d]",
+ err->message, err->code, err->int1);
+ }
/* Retry (indefinitely) if we're just waiting for qemu to shut down. See:
* https://www.redhat.com/archives/libvir-list/2016-January/msg00767.html
--
2.47.3

View File

@ -1,4 +1,4 @@
From f8e4c310bb580e576d4962c395a99278e039fdf4 Mon Sep 17 00:00:00 2001
From 3319a68b51de8ded8a76dcd72f8fce804dd581a4 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Tue, 7 Jul 2015 09:28:03 -0400
Subject: [PATCH] RHEL: Reject use of libguestfs-winsupport features except for
@ -13,7 +13,7 @@ edits.
3 files changed, 19 insertions(+)
diff --git a/generator/c.ml b/generator/c.ml
index c6e5dd994..b6cc0da20 100644
index f1217c659..923c2be38 100644
--- a/generator/c.ml
+++ b/generator/c.ml
@@ -1834,6 +1834,22 @@ and generate_client_actions actions () =

View File

@ -1,32 +0,0 @@
From c7aaa89fba21499fa6ba11e41fdc8de610819a87 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Fri, 25 Jul 2025 09:39:51 +0100
Subject: [PATCH] lib: libvirt: Sleep before retrying virDomainDestroyFlags
This saves us going into a loop if virDomainDestroyFlags keeps
returning -EBUSY quickly, which apparenrly can happen in containers.
The equivalent 'direct' backend code sleeps for 2 seconds in this case.
---
lib/launch-libvirt.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/lib/launch-libvirt.c b/lib/launch-libvirt.c
index 8dbde5341..c690a444a 100644
--- a/lib/launch-libvirt.c
+++ b/lib/launch-libvirt.c
@@ -2181,8 +2181,10 @@ destroy_domain (guestfs_h *g, virDomainPtr dom, int check_for_errors)
/* Retry (indefinitely) if we're just waiting for qemu to shut down. See:
* https://www.redhat.com/archives/libvir-list/2016-January/msg00767.html
*/
- if (err && err->code == VIR_ERR_SYSTEM_ERROR && err->int1 == EBUSY)
+ if (err && err->code == VIR_ERR_SYSTEM_ERROR && err->int1 == EBUSY) {
+ sleep (1);
goto again;
+ }
/* "Domain not found" is not treated as an error. */
if (err && err->code == VIR_ERR_NO_DOMAIN)
--
2.47.3

View File

@ -1,4 +1,4 @@
From 7a16a0b3580b081abc4880644ed0e34b30670cae Mon Sep 17 00:00:00 2001
From 4641910dc6aff98c3451041919dcca62b96e4cb7 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Tue, 13 May 2025 17:28:25 +0100
Subject: [PATCH] RHEL: appliance/init: Run depmod -a to rebuild kernel module

View File

@ -1,49 +0,0 @@
From f4f84a882468cb7b2dc4c265bdc18a5df79c3d4d Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Wed, 30 Jul 2025 10:53:20 +0100
Subject: [PATCH] daemon: Add contents of /etc/fstab to verbose log
Also some mdadm configuration files. This is useful for debugging.
The output looks like this:
info: /etc/fstab in /dev/VG/Root
LABEL=BOOT /boot ext2 default 0 0$
LABEL=ROOT / ext2 default 0 0$
Fixes: https://issues.redhat.com/browse/RHEL-106490
---
daemon/inspect_fs_unix_fstab.ml | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/daemon/inspect_fs_unix_fstab.ml b/daemon/inspect_fs_unix_fstab.ml
index 8e765454a..b4652a39d 100644
--- a/daemon/inspect_fs_unix_fstab.ml
+++ b/daemon/inspect_fs_unix_fstab.ml
@@ -43,6 +43,23 @@ let rec check_fstab ?(mdadm_conf = false) (root_mountable : Mountable.t)
if mdadm_conf then ["/etc/mdadm.conf"; "/etc/mdadm/mdadm.conf"] else [] in
let configfiles = "/etc/fstab" :: mdadmfiles in
+ (* If verbose, dump the contents of each config file as that can be
+ * useful for debugging.
+ *)
+ if verbose () then (
+ List.iter (
+ fun filename ->
+ let sysroot_filename = Sysroot.sysroot_path filename in
+ if Sys.file_exists sysroot_filename then (
+ eprintf "info: %s in %s\n%!"
+ filename (Mountable.to_string root_mountable);
+ let cmd = sprintf "cat -A %s >&2" (quote sysroot_filename) in
+ ignore (Sys.command cmd);
+ eprintf "\n%!"
+ )
+ ) configfiles
+ );
+
with_augeas ~name:"check_fstab_aug"
configfiles (check_fstab_aug mdadm_conf root_mountable os_type)
--
2.47.3

View File

@ -1,47 +0,0 @@
From 217823da95aad095a1c86a90aa4b1db8d46319e4 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Wed, 30 Jul 2025 11:05:17 +0100
Subject: [PATCH] appliance/init: Add lsblk and blkid output to verbose log
This is useful for debugging. The output looks like:
+ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sda 8:0 0 1G 0 disk
|-sda1 8:1 0 512M 0 part
`-sda2 8:2 0 512M 0 part
|-VG-Root 252:0 0 32M 0 lvm
|-VG-LV1 252:1 0 32M 0 lvm
|-VG-LV2 252:2 0 32M 0 lvm
`-VG-LV3 252:3 0 64M 0 lvm
sdb 8:16 0 4G 0 disk /
+ blkid
/dev/mapper/VG-LV1: UUID="cc8a3437-4169-4b1c-b432-ee8adc563f6d" BLOCK_SIZE="4096" TYPE="ext2"
/dev/sdb: UUID="30c70ddc-d00b-4620-a408-025890e59aa6" BLOCK_SIZE="4096" TYPE="ext2"
/dev/mapper/VG-LV2: UUID="747009aa-e183-46ba-a034-0c437b15cebc" BLOCK_SIZE="1024" TYPE="ext2"
/dev/mapper/VG-Root: LABEL="ROOT" UUID="01234567-0123-0123-0123-012345678902" BLOCK_SIZE="4096" TYPE="ext2"
/dev/sda2: UUID="DfEjc1-wRU6-vh8U-we7U-ivEl-FRwo-rG0ZuL" TYPE="LVM2_member" PARTUUID="184cbb43-02"
/dev/sda1: LABEL="BOOT" UUID="01234567-0123-0123-0123-012345678901" BLOCK_SIZE="4096" TYPE="ext2" PARTUUID="184cbb43-01"
/dev/mapper/VG-LV3: UUID="f9e5dc21-9a2a-45a0-85b0-e2889607139a" BLOCK_SIZE="2048" TYPE="ext2"
Fixes: https://issues.redhat.com/browse/RHEL-106490
---
appliance/init | 2 ++
1 file changed, 2 insertions(+)
diff --git a/appliance/init b/appliance/init
index 47eb97dfc..62526ac77 100755
--- a/appliance/init
+++ b/appliance/init
@@ -184,6 +184,8 @@ if test "$guestfs_verbose" = 1 && test "$guestfs_boot_analysis" != 1; then
ls -lR /dev
cat /proc/mounts
cat /proc/mdstat
+ lsblk
+ blkid
lvm config
lvm pvs
lvm vgs
--
2.47.3

View File

@ -0,0 +1,47 @@
From d448d69171b5c1ee14a4eca5dc4964840ba99507 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Mon, 26 Jan 2026 14:30:46 +0000
Subject: [PATCH] lib/qemu.c: Use machine type none when inspecting QMP
properties
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Dan mentioned that there is a special machine type ("none") we can use
when querying for KVM. It has no CPU, memory, etc and does not run,
but you can still enable KVM for it.
Note we have to remove the -cpu parameter, otherwise qemu prints this
error:
qemu-kvm: apic-id property was not initialized properly
Updates: commit 5da8102f5f59b9781075440dc68c8d08f9c8691e
Suggested-by: Daniel P. Berrangé <berrange@redhat.com>
(cherry picked from commit f7a24b2ea81f3c5c2754fcf43e0347c7c5378b4a)
---
lib/qemu.c | 8 +-------
1 file changed, 1 insertion(+), 7 deletions(-)
diff --git a/lib/qemu.c b/lib/qemu.c
index 19b5aa336..9edbe8208 100644
--- a/lib/qemu.c
+++ b/lib/qemu.c
@@ -81,13 +81,7 @@ generic_qmp_test (guestfs_h *g, const char *qmp_command, char **outp)
guestfs_int_cmd_add_string_unquoted (cmd, "QEMU_AUDIO_DRV=none ");
guestfs_int_cmd_add_string_quoted (cmd, g->hv);
guestfs_int_cmd_add_string_unquoted (cmd, " -display none");
- guestfs_int_cmd_add_string_unquoted (cmd, " -cpu max");
- guestfs_int_cmd_add_string_unquoted (cmd, " -machine ");
- guestfs_int_cmd_add_string_quoted (cmd,
-#ifdef MACHINE_TYPE
- MACHINE_TYPE ","
-#endif
- "accel=kvm:hvf:tcg");
+ guestfs_int_cmd_add_string_unquoted (cmd, " -machine none,accel=kvm:hvf:tcg");
guestfs_int_cmd_add_string_unquoted (cmd, " -qmp stdio");
guestfs_int_cmd_add_string_unquoted (cmd, " -S");
guestfs_int_cmd_clear_capture_errors (cmd);
--
2.47.3

View File

@ -1,50 +0,0 @@
From 701667b6f581a824059c4da50eb4df176decbb82 Mon Sep 17 00:00:00 2001
From: Cole Robinson <crobinso@redhat.com>
Date: Thu, 31 Jul 2025 15:27:38 -0400
Subject: [PATCH] docs: Fix dead ntfs-3g doc links
---
generator/actions_core.ml | 4 ++--
lib/guestfs.pod | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/generator/actions_core.ml b/generator/actions_core.ml
index 0f39fd509..108494ece 100644
--- a/generator/actions_core.ml
+++ b/generator/actions_core.ml
@@ -4661,8 +4661,8 @@ as F<C:\\windows> may appear as F</WINDOWS> or F</windows>
they were created. In Windows itself this would not be
a problem.
-Bug or feature? You decide:
-L<https://www.tuxera.com/community/ntfs-3g-faq/#posixfilenames1>
+Bug or feature? You decide. See the relevant entry in the ntfs-3g FAQ:
+L<https://github.com/tuxera/ntfs-3g/wiki/NTFS-3G-FAQ>
C<guestfs_case_sensitive_path> attempts to resolve the true case of
each element in the path. It will return a resolved path if either the
diff --git a/lib/guestfs.pod b/lib/guestfs.pod
index f69d5a070..505978aa1 100644
--- a/lib/guestfs.pod
+++ b/lib/guestfs.pod
@@ -984,7 +984,7 @@ Ntfs-3g tries to rewrite "Junction Points" and NTFS "symbolic links"
to provide something which looks like a Linux symlink. The way it
tries to do the rewriting is described here:
-L<http://www.tuxera.com/community/ntfs-3g-advanced/junction-points-and-symbolic-links/>
+L<https://github.com/tuxera/ntfs-3g/wiki/Junctions-Points,-Symbolic-Links-and-Reparse-Points>
The essential problem is that ntfs-3g simply does not have enough
information to do a correct job. NTFS links can contain drive letters
@@ -1003,7 +1003,7 @@ format documented in various places around the web).
There are other useful extended attributes that can be read from
ntfs-3g filesystems (using L</guestfs_getxattr>). See:
-L<http://www.tuxera.com/community/ntfs-3g-advanced/extended-attributes/>
+L<https://github.com/tuxera/ntfs-3g/wiki/Using-Extended-Attributes>
=head3 WINDOWS HIBERNATION AND WINDOWS 8 FAST STARTUP
--
2.47.3

View File

@ -0,0 +1,33 @@
From 43d8a4970bbd908ed4b583335f1f4f42a3f6f354 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Fri, 23 Jan 2026 08:47:42 +0000
Subject: [PATCH] generator: Fix description of xfs_growfs
This function does not return "geometry information". Remove
the bogus description.
(cherry picked from commit 6166304be685b2fafb8cc968a4558a906bb0609b)
---
generator/actions_core.ml | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/generator/actions_core.ml b/generator/actions_core.ml
index 9c23da008..de95bdaf0 100644
--- a/generator/actions_core.ml
+++ b/generator/actions_core.ml
@@ -7332,11 +7332,7 @@ in the returned structure is defined by the API." };
["xfs_info"; "/"]], "ret->xfs_blocksize == 4096"), [];
];
shortdesc = "expand an existing XFS filesystem";
- longdesc = {|Grow the XFS filesystem mounted at C<path>.
-
-The returned struct contains geometry information. Missing
-fields are returned as C<-1> (for numeric fields) or empty
-string.|} };
+ longdesc = {|Grow the XFS filesystem mounted at C<path>.|} };
{ defaults with
name = "rsync"; added = (1, 19, 29);
--
2.47.3

View File

@ -1,178 +0,0 @@
From 06db19c56c0a4e81596b24a7ab74ed545b422e4c Mon Sep 17 00:00:00 2001
From: Cole Robinson <crobinso@redhat.com>
Date: Thu, 12 Jun 2025 14:42:33 -0400
Subject: [PATCH] daemon: inspect: check /etc/crypttab for /dev/mapper/*
Encrypted root fs on SUSE distros will present itself like so:
```
/dev/mapper/cr_root / btrfs defaults 0 0
UUID=588905f9-bfa4-47b5-9fe8-893cb8ad4a0b /var btrfs subvol=/@/var 0 0
... more subvols here ...
UUID=8a278363-3042-4dea-a878-592f5e1b7381 swap btrfs defaults 0 0
/dev/mapper/cr_root /.snapshots btrfs subvol=/@/.snapshots 0 0
cr_root UUID=5289379a-a707-41b5-994c-c383f7ed54cc none x-initrd.attach
```
This breaks `-i` inspection, since libguestfs doesn't know what
/dev/mapper/cr_root is supposed to be, and nothing in the appliance
will autopopulate that path. This isn't a problem on Fedora, where
it uses UUID= instead of a /dev/mapper path.
Currently when we see /dev/mapper as a mount prefix, we only attempt
to do some LVM name mapping. This extends libguestfs to check
/etc/crypttab first. If we find an entry for the mapper path, and it
points to the encrypted luks UUID, we use that UUID to build the
associated /dev/disk/by-id/dm-uuid-CRYPT-* path, which is a symlink
to the unencrypted /dev/dm-X path
Resolves: https://issues.redhat.com/browse/RHEL-93584
Signed-off-by: Cole Robinson <crobinso@redhat.com>
---
daemon/inspect_fs_unix_fstab.ml | 93 +++++++++++++++++++++++++--------
1 file changed, 70 insertions(+), 23 deletions(-)
diff --git a/daemon/inspect_fs_unix_fstab.ml b/daemon/inspect_fs_unix_fstab.ml
index b4652a39d..bd1b8e540 100644
--- a/daemon/inspect_fs_unix_fstab.ml
+++ b/daemon/inspect_fs_unix_fstab.ml
@@ -41,7 +41,7 @@ let rec check_fstab ?(mdadm_conf = false) (root_mountable : Mountable.t)
os_type =
let mdadmfiles =
if mdadm_conf then ["/etc/mdadm.conf"; "/etc/mdadm/mdadm.conf"] else [] in
- let configfiles = "/etc/fstab" :: mdadmfiles in
+ let configfiles = "/etc/fstab" :: "/etc/crypttab" :: mdadmfiles in
(* If verbose, dump the contents of each config file as that can be
* useful for debugging.
@@ -179,7 +179,7 @@ and check_fstab_entry md_map root_mountable os_type aug entry =
root_mountable
(* Resolve guest block device names. *)
else if String.starts_with "/dev/" spec then
- resolve_fstab_device spec md_map os_type
+ resolve_fstab_device spec md_map os_type aug
(* In OpenBSD's fstab you can specify partitions
* on a disk by appending a period and a partition
* letter to a Disklable Unique Identifier. The
@@ -194,7 +194,7 @@ and check_fstab_entry md_map root_mountable os_type aug entry =
* assume that this is the first disk.
*)
let device = sprintf "/dev/sd0%c" part in
- resolve_fstab_device device md_map os_type
+ resolve_fstab_device device md_map os_type aug
)
(* Ignore "/.swap" (Pardus) and pseudo-devices
* like "tmpfs". If we haven't resolved the device
@@ -353,7 +353,7 @@ and parse_md_uuid uuid =
* the real VM, which is a reasonable assumption to make. Return
* anything we don't recognize unchanged.
*)
-and resolve_fstab_device spec md_map os_type =
+and resolve_fstab_device spec md_map os_type aug =
(* In any case where we didn't match a device pattern or there was
* another problem, return this default mountable derived from [spec].
*)
@@ -366,7 +366,7 @@ and resolve_fstab_device spec md_map os_type =
if String.starts_with "/dev/mapper" spec then (
debug_matching "/dev/mapper";
- resolve_dev_mapper spec default
+ resolve_dev_mapper spec default aug
)
else if PCRE.matches re_xdev spec then (
@@ -540,24 +540,71 @@ and resolve_fstab_device spec md_map os_type =
default
)
-and resolve_dev_mapper spec default =
- (* LVM2 does some strange munging on /dev/mapper paths for VGs and
- * LVs which contain '-' character:
- *
- * ><fs> lvcreate LV--test VG--test 32
- * ><fs> debug ls /dev/mapper
- * VG----test-LV----test
- *
- * This makes it impossible to reverse those paths directly, so
- * we have implemented lvm_canonical_lv_name in the daemon.
- *)
- try
- match Lvm_utils.lv_canonical spec with
- | None -> default
- | Some device -> Mountable.of_device device
- with
- (* Ignore devices that don't exist. (RHBZ#811872) *)
- | Unix.Unix_error (Unix.ENOENT, _, _) -> default
+and resolve_dev_mapper spec default aug =
+ let augpath =
+ sprintf "/files/etc/crypttab/*[target='%s']/device"
+ (Filename.basename spec) in
+ match aug_get_noerrors aug augpath with
+ | Some device ->
+ (* /dev/mapper name is present in /etc/crypttab *)
+ if verbose() then eprintf "mapped to crypttab device=%s\n%!" device;
+ (* device string is one of:
+ * + UUID=... without any shell quoting
+ * + An absolute path
+ *)
+ if String.starts_with "UUID=" device then (
+ (* We found the UUID for the encrypted LUKS partition, now we use
+ * that to get the unencrypted /dev/dm-X via
+ * /dev/disk/by-id/dm-uuid-CRYPT-* automagic paths. The format is
+ *
+ * /dev/disk/by-id/dm-uuid-CRYPT-$TYPE-$LUKSUUID-$DMNAME
+ *
+ * The fields are
+ * + $TYPE: `LUKS1` or `LUKS2`
+ * + $LUKSUUID: The UUID we got from crypttab, but with `-` removed
+ * + $DMNAME: this would be `cr_root` for `/dev/mapper/cr_root`, but
+ * we just ignore that.
+ *)
+ let byid_dir = "/dev/disk/by-id" in
+ let uuid = String.sub device 5 (String.length device - 5) in
+ let short_uuid = String.replace uuid "-" "" in
+ let regstr = sprintf "^dm-uuid-CRYPT-LUKS.-%s-.*$" short_uuid in
+ let re_dmcrypt = PCRE.compile regstr in
+ let entries = Sys.readdir byid_dir |> Array.to_list in
+ try
+ let filename = List.find (fun f -> PCRE.matches re_dmcrypt f) entries in
+ let fullpath = Filename.concat byid_dir filename in
+ let resolved_path = Unix_utils.Realpath.realpath fullpath in
+ eprintf("Found crypttab mapping %s -> %s\n%!") fullpath resolved_path;
+ Mountable.of_device (resolved_path)
+ with
+ Failure _ | Not_found ->
+ eprintf("Failed to find matching regex %s/%s\n%!") byid_dir regstr;
+ Mountable.of_device spec
+ ) else (
+ Mountable.of_device spec
+ )
+ | None ->
+ (* Assume /dev/mapper device is LVM *)
+
+ (* LVM2 does some strange munging on /dev/mapper paths for VGs and
+ * LVs which contain '-' character:
+ *
+ * ><fs> lvcreate LV--test VG--test 32
+ * ><fs> debug ls /dev/mapper
+ * VG----test-LV----test
+ *
+ * This makes it impossible to reverse those paths directly, so
+ * we have implemented lvm_canonical_lv_name in the daemon.
+ *)
+ try
+ match Lvm_utils.lv_canonical spec with
+ | None -> default
+ | Some device -> Mountable.of_device device
+ with
+ (* Ignore devices that don't exist. (RHBZ#811872) *)
+ | Unix.Unix_error (Unix.ENOENT, _, _) -> default
+
(* type: (h|s|v|xv)
* disk: [a-z]+
--
2.47.3

View File

@ -0,0 +1,36 @@
From d5daad98cfe254c14f4f4f12348f1589838c36a8 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Fri, 23 Jan 2026 08:49:14 +0000
Subject: [PATCH] generator: Fix description of xfs_info
The returned struct contains filesystem metadata, not "geometry
information" (whatever that means).
(cherry picked from commit 1c9c03bcd40ede11e7f4ece44dab908dcd36a657)
---
generator/actions_core.ml | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/generator/actions_core.ml b/generator/actions_core.ml
index de95bdaf0..b57cf56b0 100644
--- a/generator/actions_core.ml
+++ b/generator/actions_core.ml
@@ -7236,11 +7236,12 @@ call C<guestfs_max_disks>.|} };
["mount"; "/dev/sda1"; "/"];
["xfs_info"; "/"]], "ret->xfs_blocksize == 4096"), []
];
- shortdesc = "get geometry of XFS filesystem";
+ shortdesc = "get information about the XFS filesystem";
longdesc = {|C<pathordevice> is a mounted XFS filesystem or a device containing
-an XFS filesystem. This command returns the geometry of the filesystem.
+an XFS filesystem. This command returns miscellaneous
+metadata about the XFS filesystem.
-The returned struct contains geometry information. Missing
+The returned struct contains miscellaneous metadata. Missing
fields are returned as C<-1> (for numeric fields) or empty
string.|} };
--
2.47.3

View File

@ -0,0 +1,351 @@
From aac2ec68202f2913ae6000aa117a8ed5dffbd6bb Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Fri, 23 Jan 2026 08:58:07 +0000
Subject: [PATCH] New API: xfs_info2
Reimplement xfs_info by returning a hash table of values (rather than
a limited struct), and by writing it in OCaml with PCRE which makes
string parsing a lot simpler. This will now flexibly return all the
fields from the underlying xfs_info command, even (hopefully) future
fields.
Note the field values are returned as strings, because the actual
fields in xfs_info output are fairly random and free-form. There is a
trade off here between returning as much information as we can, and
requiring the user to do a bit of (simple) field parsing.
Fixes: https://issues.redhat.com/browse/RHEL-143673
(cherry picked from commit dfd2700616dee92d75e91936fb56b5de0450f992)
---
.gitignore | 1 +
common | 2 +-
daemon/Makefile.am | 7 ++-
daemon/xfs.ml | 128 ++++++++++++++++++++++++++++++++++++++
generator/actions_core.ml | 35 +++++++++++
generator/proc_nr.ml | 1 +
lib/MAX_PROC_NR | 2 +-
7 files changed, 172 insertions(+), 4 deletions(-)
create mode 100644 daemon/xfs.ml
diff --git a/.gitignore b/.gitignore
index 02160caff..c8a8312fa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -119,6 +119,7 @@ Makefile.in
/daemon/stubs-?.c
/daemon/stubs.h
/daemon/types.ml
+/daemon/xfs.mli
/depcomp
/docs/guestfs-building.1
/docs/guestfs-faq.1
Submodule common b54ba2031..3ac5d1841:
diff --git a/common/mlpcre/PCRE.ml b/common/mlpcre/PCRE.ml
index 077290e..33074af 100644
--- a/common/mlpcre/PCRE.ml
+++ b/common/mlpcre/PCRE.ml
@@ -22,7 +22,7 @@ exception Error of string * int
type regexp
-external compile : ?caseless:bool -> ?dotall:bool -> ?extended:bool -> ?multiline:bool -> string -> regexp = "guestfs_int_pcre_compile"
+external compile : ?anchored:bool -> ?caseless:bool -> ?dotall:bool -> ?extended:bool -> ?multiline:bool -> string -> regexp = "guestfs_int_pcre_compile_byte" "guestfs_int_pcre_compile"
external matches : ?offset:int -> regexp -> string -> bool = "guestfs_int_pcre_matches"
external sub : int -> string = "guestfs_int_pcre_sub"
external subi : int -> int * int = "guestfs_int_pcre_subi"
diff --git a/common/mlpcre/PCRE.mli b/common/mlpcre/PCRE.mli
index b69a56b..0fdc2bd 100644
--- a/common/mlpcre/PCRE.mli
+++ b/common/mlpcre/PCRE.mli
@@ -52,11 +52,12 @@ exception Error of string * int
type regexp
(** The type of a compiled regular expression. *)
-val compile : ?caseless:bool -> ?dotall:bool -> ?extended:bool -> ?multiline:bool -> string -> regexp
+val compile : ?anchored:bool -> ?caseless:bool -> ?dotall:bool ->
+ ?extended:bool -> ?multiline:bool -> string -> regexp
(** Compile a regular expression. This can raise {!Error}.
- The flags [?caseless], [?dotall], [?extended], [?multiline]
- correspond to the [pcre_compile] flags [PCRE_CASELESS] etc.
+ The flags [?anchored], [?caseless], [?dotall], [?extended], [?multiline]
+ correspond to the [pcre_compile] flags [PCRE_ANCHORED] etc.
See pcre2api(3) for details of what they do.
All flags default to false. *)
diff --git a/common/mlpcre/pcre-c.c b/common/mlpcre/pcre-c.c
index 3959fd5..11be157 100644
--- a/common/mlpcre/pcre-c.c
+++ b/common/mlpcre/pcre-c.c
@@ -154,11 +154,12 @@ Optint_val (value intv, int defval)
}
value
-guestfs_int_pcre_compile (value caselessv, value dotallv,
- value extendedv, value multilinev,
+guestfs_int_pcre_compile (value anchoredv, value caselessv,
+ value dotallv, value extendedv,
+ value multilinev,
value pattv)
{
- CAMLparam4 (caselessv, dotallv, extendedv, multilinev);
+ CAMLparam5 (anchoredv, caselessv, dotallv, extendedv, multilinev);
CAMLxparam1 (pattv);
const char *patt;
int options = 0;
@@ -167,6 +168,8 @@ guestfs_int_pcre_compile (value caselessv, value dotallv,
PCRE2_SIZE errnum;
/* Flag parameters are all bool option, defaulting to false. */
+ if (is_Some_true (anchoredv))
+ options |= PCRE2_ANCHORED;
if (is_Some_true (caselessv))
options |= PCRE2_CASELESS;
if (is_Some_true (dotallv))
@@ -186,6 +189,14 @@ guestfs_int_pcre_compile (value caselessv, value dotallv,
CAMLreturn (Val_regexp (re));
}
+value
+guestfs_int_pcre_compile_byte (value *argv, int argn)
+{
+ assert (argn == 6);
+ return guestfs_int_pcre_compile (argv[0], argv[1], argv[2],
+ argv[3], argv[4], argv[5]);
+}
+
value
guestfs_int_pcre_matches (value offsetv, value rev, value strv)
{
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index c644d9881..d4a805046 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -63,7 +63,8 @@ generator_built = \
sfdisk.mli \
statvfs.mli \
structs.ml \
- structs.mli
+ structs.mli \
+ xfs.mli
CONFIGURE_GENERATED_ML = \
daemon_config.ml
@@ -312,7 +313,8 @@ SOURCES_MLI = \
statvfs.mli \
structs.mli \
sysroot.mli \
- utils.mli
+ utils.mli \
+ xfs.mli
SOURCES_ML = \
$(CONFIGURE_GENERATED_ML) \
@@ -347,6 +349,7 @@ SOURCES_ML = \
realpath.ml \
statvfs.ml \
selinux.ml \
+ xfs.ml \
inspect_types.ml \
inspect_utils.ml \
inspect_fs_unix_fstab.ml \
diff --git a/daemon/xfs.ml b/daemon/xfs.ml
new file mode 100644
index 000000000..142b26775
--- /dev/null
+++ b/daemon/xfs.ml
@@ -0,0 +1,128 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2025 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *)
+
+open Printf
+
+open Std_utils
+
+open Utils
+
+(* The output is horrific ...
+
+meta-data=/dev/sda1 isize=512 agcount=4, agsize=122094659 blks
+ = sectsz=4096 attr=2, projid32bit=1
+ = crc=1 finobt=1, sparse=1, rmapbt=0
+ = reflink=1 bigtime=1 inobtcount=1 nrext64=0
+ = exchange=0 metadir=0
+data = bsize=4096 blocks=488378636, imaxpct=5
+ = sunit=0 swidth=0 blks
+naming =version 2 bsize=4096 ascii-ci=0, ftype=1, parent=0
+log =internal log bsize=4096 blocks=238466, version=2
+ = sectsz=4096 sunit=1 blks, lazy-count=1
+realtime =none extsz=4096 blocks=0, rtextents=0
+ = rgcount=0 rgsize=0 extents
+ = zoned=0 start=0 reserved=0
+
+^heading ^"stuff" ^ data fields vaguely related to heading
+
+Note also the inconsistent use of commas.
+*)
+
+(* Split into groups using a positive lookahead assertion. *)
+let re1 = PCRE.compile ~extended:true {| \n (?=[a-z]) |}
+
+(* Separate group heading and the rest. *)
+let re2 = PCRE.compile ~extended:true {| = |}
+
+(* Match the first field in a group (if present). *)
+let re3 = PCRE.compile ~anchored:true ~extended:true {|
+ (version\s\d+|\S+\slog|\S+).*
+|}
+
+(* Match next field=value in group. *)
+let re4 = PCRE.compile ~extended:true {|
+ ([-\w]+)=(\d+(\s(blks|extents))?)
+|}
+
+let xfs_info2 dev =
+ (* Uncomment the first line to enable extra debugging. *)
+ (*let extra_debug = verbose () in*)
+ let extra_debug = false in
+
+ let is_dev = is_device_parameter dev in
+ let arg = if is_dev then dev else Sysroot.sysroot_path dev in
+ let out = command "xfs_info" [arg] in
+
+ (* Split the output by heading. *)
+ let groups = PCRE.nsplit re1 out in
+ let groups = List.map (PCRE.split re2) groups in
+ let groups = List.map (fun (name, rest) -> String.trim name, rest) groups in
+
+ if extra_debug then (
+ List.iteri (
+ fun i (name, rest) ->
+ eprintf "xfs_info2: group %d: %S: %S\n%!" i name rest
+ ) groups
+ );
+
+ (* Parse each group into the final list of values. *)
+ let values = ref [] in
+ List.iter (
+ fun (group_name, rest) ->
+ let len = String.length rest in
+
+ (* If there is some string at the beginning of the
+ * group then we create a (group_name, string) value,
+ * eg. ("meta-data", "/dev/sda1")
+ *)
+ let start =
+ if PCRE.matches re3 rest then (
+ let value = PCRE.sub 1 in
+ List.push_front (group_name, value) values;
+ (* Start parsing after this. *)
+ String.length value
+ )
+ else 0 in
+
+ let rec loop i =
+ if extra_debug then
+ eprintf "xfs_info2: parsing group %S from %d\n%!" group_name i;
+ if i <= len && PCRE.matches ~offset:i re4 rest then (
+ let field_name = PCRE.sub 1 in
+ if extra_debug then eprintf "xfs_info2: sub1=%S\n%!" field_name;
+ let value = PCRE.sub 2 in
+ if extra_debug then eprintf "xfs_info2: sub2=%S\n%!" value;
+ let name = sprintf "%s.%s" group_name field_name in
+ List.push_front (name, value) values;
+
+ (* Next time round the loop, start parsing after the
+ * current matched subexpression.
+ *)
+ loop (snd (PCRE.subi 2) + 1)
+ )
+ in
+ (try
+ loop start
+ with
+ Not_found ->
+ failwithf "xfs_info2: internal error: unexpected Not_found exception. Enable debug and send the full output in a bug report."
+ );
+
+ ) groups;
+
+ List.rev !values
diff --git a/generator/actions_core.ml b/generator/actions_core.ml
index b57cf56b0..b7e4dae45 100644
--- a/generator/actions_core.ml
+++ b/generator/actions_core.ml
@@ -9575,4 +9575,39 @@ The optional C<force> boolean controls whether the context
is reset for customizable files, and also whether the
user, role and range parts of the file context is changed.|} };
+ { defaults with
+ name = "xfs_info2"; added = (1, 59, 2);
+ style = RHashtable (RPlainString, RPlainString, "info"), [String (Dev_or_Path, "pathordevice")], [];
+ impl = OCaml "Xfs.xfs_info2";
+ optional = Some "xfs";
+ tests = [
+ InitEmpty, Always, TestResult (
+ [["part_disk"; "/dev/sda"; "mbr"];
+ ["mkfs"; "xfs"; "/dev/sda1"; ""; "NOARG"; ""; ""; "NOARG"];
+ ["mount"; "/dev/sda1"; "/"];
+ ["xfs_info2"; "/"]],
+ "check_hash (ret, \"data.bsize\", \"4096\") == 0"), []
+ ];
+ shortdesc = "get information about the XFS filesystem";
+ longdesc = {|C<pathordevice> is a mounted XFS filesystem or
+a device containing an XFS filesystem. This command returns
+miscellaneous metadata about the XFS filesystem.
+
+The output is a hash derived from the output of L<xfs_info(8)>,
+and generally looks like:
+
+ meta-data: /dev/sda1
+ meta-data.isize: 512
+ meta-data.agcount: 4
+ meta-data.agsize: 65528 blks
+ meta-data.sectsz: 512
+ meta-data.attr: 2
+ meta-data.projid32bit: 1
+ meta-data.crc: 1
+ [...]
+ data.bsize: 4096
+ data.blocks: 262112
+ [...]
+
+More information can be found by reading L<xfs_info(8)>.|} };
]
diff --git a/generator/proc_nr.ml b/generator/proc_nr.ml
index 11e7b9d1b..b22d88b58 100644
--- a/generator/proc_nr.ml
+++ b/generator/proc_nr.ml
@@ -524,6 +524,7 @@ let proc_nr = [
519, "setfiles";
520, "ntfs_chmod";
521, "inspect_get_windows_group_policy";
+522, "xfs_info2";
]
(* End of list. If adding a new entry, add it at the end of the list
diff --git a/lib/MAX_PROC_NR b/lib/MAX_PROC_NR
index 5a232f264..ec0e415d0 100644
--- a/lib/MAX_PROC_NR
+++ b/lib/MAX_PROC_NR
@@ -1 +1 @@
-521
+522
--
2.47.3

View File

@ -1,42 +0,0 @@
From 1e0099671a2cd75e3407fc02cd16584fce3ba4ee Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Tue, 12 Aug 2025 13:04:45 +0100
Subject: [PATCH] daemon: sysroot: Avoid double-/ when creating sysroot paths
in OCaml
Previously calling 'sysroot_path "/dev"' for example would return the
string "/sysroot//dev". While this is not wrong, it confuses some
external programs (hello, setfiles), and it's not very "clean". Be a
bit more careful to avoid doubling the '/' character in the common case.
---
daemon/sysroot.ml | 6 +++++-
daemon/sysroot.mli | 2 +-
2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/daemon/sysroot.ml b/daemon/sysroot.ml
index 286d125b9..57e727066 100644
--- a/daemon/sysroot.ml
+++ b/daemon/sysroot.ml
@@ -20,4 +20,8 @@ open Std_utils
external sysroot : unit -> string = "guestfs_int_daemon_sysroot"
-let sysroot_path path = sysroot () // path
+let sysroot_path path =
+ let sysroot = sysroot () in
+ if path = "" then sysroot
+ else if path.[0] = '/' then sysroot ^ path
+ else sysroot // path
diff --git a/daemon/sysroot.mli b/daemon/sysroot.mli
index 7f8970cd8..1e6e75902 100644
--- a/daemon/sysroot.mli
+++ b/daemon/sysroot.mli
@@ -22,4 +22,4 @@ val sysroot : unit -> string
in default. *)
val sysroot_path : string -> string
-(** Equivalent to calling [sysroot () // path] *)
+(** Prepend [path] parameter with the sysroot. *)
--
2.47.3

View File

@ -0,0 +1,411 @@
From fb77334e1c40ac64535f2e36ee6edbdf54a5a6ac Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Fri, 23 Jan 2026 12:27:39 +0000
Subject: [PATCH] daemon: Reimplement xfs_info using xfs_info2
Remove all the complicated old C parsing code and reimplement xfs_info
using xfs_info2. Note that this function will be deprecated.
(cherry picked from commit e1deb358ce19685e4d078a64edfdbe1da7ddf63c)
---
daemon/xfs.c | 310 --------------------------------------
daemon/xfs.ml | 45 ++++++
generator/actions_core.ml | 1 +
3 files changed, 46 insertions(+), 310 deletions(-)
diff --git a/daemon/xfs.c b/daemon/xfs.c
index aa056ddff..06e5fc072 100644
--- a/daemon/xfs.c
+++ b/daemon/xfs.c
@@ -38,316 +38,6 @@ optgroup_xfs_available (void)
return prog_exists ("mkfs.xfs");
}
-/* Return everything up to the first comma, equals or space in the input
- * string, strdup'ing the return value.
- */
-static char *
-split_strdup (char *string)
-{
- size_t len;
- char *ret;
-
- len = strcspn (string, " ,=");
- ret = strndup (string, len);
- if (!ret) {
- reply_with_perror ("malloc");
- return NULL;
- }
- return ret;
-}
-
-static int
-parse_uint32 (uint32_t *ret, const char *str)
-{
- uint32_t r;
-
- if (sscanf (str, "%" SCNu32, &r) != 1) {
- reply_with_error ("cannot parse numeric field from xfs_info: %s", str);
- return -1;
- }
-
- *ret = r;
- return 0;
-}
-
-static int
-parse_uint64 (uint64_t *ret, const char *str)
-{
- uint64_t r;
-
- if (sscanf (str, "%" SCNu64, &r) != 1) {
- reply_with_error ("cannot parse numeric field from xfs_info: %s", str);
- return -1;
- }
-
- *ret = r;
- return 0;
-}
-
-/* Typical crazy output from the xfs_info command:
- *
- * meta-data=/dev/sda1 isize=256 agcount=4, agsize=6392 blks
- * = sectsz=512 attr=2
- *[ = crc=0 ]
- * data = bsize=4096 blocks=25568, imaxpct=25
- * = sunit=0 swidth=0 blks
- * naming =version 2 bsize=4096 ascii-ci=0
- * log =internal bsize=4096 blocks=1200, version=2
- * = sectsz=512 sunit=0 blks, lazy-count=1
- * realtime =none extsz=4096 blocks=0, rtextents=0
- *
- * [...] line only appears in Fedora >= 21
- *
- * We may need to revisit this parsing code if the output changes
- * in future.
- */
-static guestfs_int_xfsinfo *
-parse_xfs_info (char **lines)
-{
- guestfs_int_xfsinfo *ret;
- CLEANUP_FREE char *section = NULL; /* first column, eg "meta-data", "data" */
- char *p;
- size_t i;
-
- ret = malloc (sizeof *ret);
- if (ret == NULL) {
- reply_with_error ("malloc");
- return NULL;
- }
-
- /* Initialize fields to NULL or -1 so the caller can tell which fields
- * were updated in the code below.
- */
- ret->xfs_mntpoint = NULL;
- ret->xfs_inodesize = -1;
- ret->xfs_agcount = -1;
- ret->xfs_agsize = -1;
- ret->xfs_sectsize = -1;
- ret->xfs_attr = -1;
- ret->xfs_blocksize = -1;
- ret->xfs_datablocks = -1;
- ret->xfs_imaxpct = -1;
- ret->xfs_sunit = -1;
- ret->xfs_swidth = -1;
- ret->xfs_dirversion = -1;
- ret->xfs_dirblocksize = -1;
- ret->xfs_cimode = -1;
- ret->xfs_logname = NULL;
- ret->xfs_logblocksize = -1;
- ret->xfs_logblocks = -1;
- ret->xfs_logversion = -1;
- ret->xfs_logsectsize = -1;
- ret->xfs_logsunit = -1;
- ret->xfs_lazycount = -1;
- ret->xfs_rtname = NULL;
- ret->xfs_rtextsize = -1;
- ret->xfs_rtblocks = -1;
- ret->xfs_rtextents = -1;
-
- for (i = 0; lines[i] != NULL; ++i) {
- if (verbose)
- fprintf (stderr, "xfs_info: lines[%zu] = \'%s\'\n", i, lines[i]);
-
- if (c_isalpha (lines[i][0])) {
- free (section);
- section = split_strdup (lines[i]);
- if (!section) goto error;
-
- if (verbose)
- fprintf (stderr, "xfs_info: new section %s\n", section);
- }
-
- if ((p = strstr (lines[i], "meta-data="))) {
- ret->xfs_mntpoint = split_strdup (p + 10);
- if (ret->xfs_mntpoint == NULL) goto error;
- }
- if ((p = strstr (lines[i], "isize="))) {
- CLEANUP_FREE char *buf = split_strdup (p + 6);
- if (buf == NULL) goto error;
- if (parse_uint32 (&ret->xfs_inodesize, buf) == -1)
- goto error;
- }
- if ((p = strstr (lines[i], "agcount="))) {
- CLEANUP_FREE char *buf = split_strdup (p + 8);
- if (buf == NULL) goto error;
- if (parse_uint32 (&ret->xfs_agcount, buf) == -1)
- goto error;
- }
- if ((p = strstr (lines[i], "agsize="))) {
- CLEANUP_FREE char *buf = split_strdup (p + 7);
- if (buf == NULL) goto error;
- if (parse_uint32 (&ret->xfs_agsize, buf) == -1)
- goto error;
- }
- if ((p = strstr (lines[i], "sectsz="))) {
- if (section) {
- CLEANUP_FREE char *buf = split_strdup (p + 7);
- if (buf == NULL) goto error;
- if (STREQ (section, "meta-data")) {
- if (parse_uint32 (&ret->xfs_sectsize, buf) == -1)
- goto error;
- } else if (STREQ (section, "log")) {
- if (parse_uint32 (&ret->xfs_logsectsize, buf) == -1)
- goto error;
- }
- }
- }
- if ((p = strstr (lines[i], "attr="))) {
- CLEANUP_FREE char *buf = split_strdup (p + 5);
- if (buf == NULL) goto error;
- if (parse_uint32 (&ret->xfs_attr, buf) == -1)
- goto error;
- }
- if ((p = strstr (lines[i], "bsize="))) {
- if (section) {
- CLEANUP_FREE char *buf = split_strdup (p + 6);
- if (buf == NULL) goto error;
- if (STREQ (section, "data")) {
- if (parse_uint32 (&ret->xfs_blocksize, buf) == -1)
- goto error;
- } else if (STREQ (section, "naming")) {
- if (parse_uint32 (&ret->xfs_dirblocksize, buf) == -1)
- goto error;
- } else if (STREQ (section, "log")) {
- if (parse_uint32 (&ret->xfs_logblocksize, buf) == -1)
- goto error;
- }
- }
- }
- if ((p = strstr (lines[i], "blocks="))) {
- if (section) {
- CLEANUP_FREE char *buf = split_strdup (p + 7);
- if (buf == NULL) goto error;
- if (STREQ (section, "data")) {
- if (parse_uint64 (&ret->xfs_datablocks, buf) == -1)
- goto error;
- } else if (STREQ (section, "log")) {
- if (parse_uint32 (&ret->xfs_logblocks, buf) == -1)
- goto error;
- } else if (STREQ (section, "realtime")) {
- if (parse_uint64 (&ret->xfs_rtblocks, buf) == -1)
- goto error;
- }
- }
- }
- if ((p = strstr (lines[i], "imaxpct="))) {
- CLEANUP_FREE char *buf = split_strdup (p + 8);
- if (buf == NULL) goto error;
- if (parse_uint32 (&ret->xfs_imaxpct, buf) == -1)
- goto error;
- }
- if ((p = strstr (lines[i], "sunit="))) {
- if (section) {
- CLEANUP_FREE char *buf = split_strdup (p + 6);
- if (buf == NULL) goto error;
- if (STREQ (section, "data")) {
- if (parse_uint32 (&ret->xfs_sunit, buf) == -1)
- goto error;
- } else if (STREQ (section, "log")) {
- if (parse_uint32 (&ret->xfs_logsunit, buf) == -1)
- goto error;
- }
- }
- }
- if ((p = strstr (lines[i], "swidth="))) {
- CLEANUP_FREE char *buf = split_strdup (p + 7);
- if (buf == NULL) goto error;
- if (parse_uint32 (&ret->xfs_swidth, buf) == -1)
- goto error;
- }
- if ((p = strstr (lines[i], "naming =version "))) {
- CLEANUP_FREE char *buf = split_strdup (p + 18);
- if (buf == NULL) goto error;
- if (parse_uint32 (&ret->xfs_dirversion, buf) == -1)
- goto error;
- }
- if ((p = strstr (lines[i], "ascii-ci="))) {
- CLEANUP_FREE char *buf = split_strdup (p + 9);
- if (buf == NULL) goto error;
- if (parse_uint32 (&ret->xfs_cimode, buf) == -1)
- goto error;
- }
- if ((p = strstr (lines[i], "log ="))) {
- ret->xfs_logname = split_strdup (p + 10);
- if (ret->xfs_logname == NULL) goto error;
- }
- if ((p = strstr (lines[i], "version="))) {
- CLEANUP_FREE char *buf = split_strdup (p + 8);
- if (buf == NULL) goto error;
- if (parse_uint32 (&ret->xfs_logversion, buf) == -1)
- goto error;
- }
- if ((p = strstr (lines[i], "lazy-count="))) {
- CLEANUP_FREE char *buf = split_strdup (p + 11);
- if (buf == NULL) goto error;
- if (parse_uint32 (&ret->xfs_lazycount, buf) == -1)
- goto error;
- }
- if ((p = strstr (lines[i], "realtime ="))) {
- ret->xfs_rtname = split_strdup (p + 10);
- if (ret->xfs_rtname == NULL) goto error;
- }
- if ((p = strstr (lines[i], "rtextents="))) {
- CLEANUP_FREE char *buf = split_strdup (p + 10);
- if (buf == NULL) goto error;
- if (parse_uint64 (&ret->xfs_rtextents, buf) == -1)
- goto error;
- }
- }
-
- if (ret->xfs_mntpoint == NULL) {
- ret->xfs_mntpoint = strdup ("");
- if (ret->xfs_mntpoint == NULL) goto error;
- }
- if (ret->xfs_logname == NULL) {
- ret->xfs_logname = strdup ("");
- if (ret->xfs_logname == NULL) goto error;
- }
- if (ret->xfs_rtname == NULL) {
- ret->xfs_rtname = strdup ("");
- if (ret->xfs_rtname == NULL) goto error;
- }
-
- return ret;
-
- error:
- free (ret->xfs_mntpoint);
- free (ret->xfs_logname);
- free (ret->xfs_rtname);
- free (ret);
- return NULL;
-}
-
-guestfs_int_xfsinfo *
-do_xfs_info (const char *pathordevice)
-{
- int r;
- CLEANUP_FREE char *buf = NULL;
- CLEANUP_FREE char *out = NULL, *err = NULL;
- CLEANUP_FREE_STRING_LIST char **lines = NULL;
- int is_dev;
-
- is_dev = is_device_parameter (pathordevice);
- buf = is_dev ? strdup (pathordevice)
- : sysroot_path (pathordevice);
- if (buf == NULL) {
- reply_with_perror ("malloc");
- return NULL;
- }
-
- r = command (&out, &err, "xfs_info", buf, NULL);
- if (r == -1) {
- reply_with_error ("%s", err);
- return NULL;
- }
-
- lines = split_lines (out);
- if (lines == NULL)
- return NULL;
-
- return parse_xfs_info (lines);
-}
-
int
do_xfs_growfs (const char *path,
int datasec, int logsec, int rtsec,
diff --git a/daemon/xfs.ml b/daemon/xfs.ml
index 142b26775..157a84058 100644
--- a/daemon/xfs.ml
+++ b/daemon/xfs.ml
@@ -17,6 +17,7 @@
*)
open Printf
+open Scanf
open Std_utils
@@ -126,3 +127,47 @@ let xfs_info2 dev =
) groups;
List.rev !values
+
+(* Deprecated xfs_info. *)
+let xfs_info dev =
+ let h = xfs_info2 dev in
+
+ let find field parsefn =
+ try List.assoc field h |> parsefn
+ with
+ | Not_found ->
+ failwithf "xfs_info: unexpected missing field: %s" field
+ | exn ->
+ failwithf "xfs_info: failure finding field: %s: %s"
+ field (Printexc.to_string exn)
+ in
+
+ let parse_blks s = sscanf s "%ld blks" Fun.id in
+ let parse_version s = sscanf s "version %ld" Fun.id in
+
+ { Structs.xfs_mntpoint = find "meta-data" Fun.id;
+ xfs_inodesize = find "meta-data.isize" Int32.of_string;
+ xfs_agcount = find "meta-data.agcount" Int32.of_string;
+ xfs_agsize = find "meta-data.agsize" parse_blks;
+ xfs_sectsize = find "meta-data.sectsz" Int32.of_string;
+ xfs_attr = find "meta-data.attr" Int32.of_string;
+ xfs_blocksize = find "data.bsize" Int32.of_string;
+ xfs_datablocks = find "data.blocks" Int64.of_string;
+ xfs_imaxpct = find "data.imaxpct" Int32.of_string;
+ xfs_sunit = find "data.sunit" Int32.of_string;
+ xfs_swidth = find "data.swidth" parse_blks;
+ xfs_dirversion = find "naming" parse_version;
+ xfs_dirblocksize = find "naming.bsize" Int32.of_string;
+ xfs_cimode = find "naming.ascii-ci" Int32.of_string;
+ xfs_logname = find "log" Fun.id;
+ xfs_logblocksize = find "log.bsize" Int32.of_string;
+ xfs_logblocks = find "log.blocks" Int32.of_string;
+ xfs_logversion = find "log.version" Int32.of_string;
+ xfs_logsectsize = find "log.sectsz" Int32.of_string;
+ xfs_logsunit = find "log.sunit" parse_blks;
+ xfs_lazycount = find "log.lazy-count" Int32.of_string;
+ xfs_rtname = find "realtime" Fun.id;
+ xfs_rtextsize = find "realtime.extsz" Int32.of_string;
+ xfs_rtblocks = find "realtime.blocks" Int64.of_string;
+ xfs_rtextents = find "realtime.rtextents" Int64.of_string;
+ }
diff --git a/generator/actions_core.ml b/generator/actions_core.ml
index b7e4dae45..f2cf83440 100644
--- a/generator/actions_core.ml
+++ b/generator/actions_core.ml
@@ -7228,6 +7228,7 @@ call C<guestfs_max_disks>.|} };
{ defaults with
name = "xfs_info"; added = (1, 19, 21);
style = RStruct ("info", "xfsinfo"), [String (Dev_or_Path, "pathordevice")], [];
+ impl = OCaml "Xfs.xfs_info";
optional = Some "xfs";
tests = [
InitEmpty, Always, TestResult (
--
2.47.3

View File

@ -1,49 +0,0 @@
From c931ab3bc807cff785b1271c575855f0906e27b3 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Tue, 12 Aug 2025 13:09:16 +0100
Subject: [PATCH] daemon: sysroot: Avoid copying the path every time we call
sysroot ()
This path never changes once the daemon has started up, so we don't
need to call into C code and copy the string every time.
---
daemon/sysroot-c.c | 4 ++--
daemon/sysroot.ml | 5 ++++-
2 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/daemon/sysroot-c.c b/daemon/sysroot-c.c
index ad31d36ee..e664232b0 100644
--- a/daemon/sysroot-c.c
+++ b/daemon/sysroot-c.c
@@ -28,10 +28,10 @@
#include "daemon.h"
-extern value guestfs_int_daemon_sysroot (value unitv);
+extern value guestfs_int_daemon_get_sysroot (value unitv);
value
-guestfs_int_daemon_sysroot (value unitv)
+guestfs_int_daemon_get_sysroot (value unitv)
{
return caml_copy_string (sysroot);
}
diff --git a/daemon/sysroot.ml b/daemon/sysroot.ml
index 57e727066..35ae11f3f 100644
--- a/daemon/sysroot.ml
+++ b/daemon/sysroot.ml
@@ -18,7 +18,10 @@
open Std_utils
-external sysroot : unit -> string = "guestfs_int_daemon_sysroot"
+external get_sysroot : unit -> string = "guestfs_int_daemon_get_sysroot"
+
+let sysroot = lazy (get_sysroot ())
+let sysroot () = Lazy.force sysroot
let sysroot_path path =
let sysroot = sysroot () in
--
2.47.3

View File

@ -1,406 +0,0 @@
From ed40333a23ae8f20ac0360df444d10db369fa6d9 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Tue, 12 Aug 2025 12:22:42 +0100
Subject: [PATCH] daemon: Reimplement guestfs_selinux_relabel in OCaml
No change, just reimplement the existing C implementation in OCaml.
---
.gitignore | 1 +
daemon/Makefile.am | 4 +-
daemon/selinux-relabel.c | 169 --------------------------------------
daemon/selinux.c | 7 ++
daemon/selinux.ml | 101 +++++++++++++++++++++++
docs/C_SOURCE_FILES | 1 -
generator/actions_core.ml | 1 +
po/POTFILES | 1 -
8 files changed, 113 insertions(+), 172 deletions(-)
delete mode 100644 daemon/selinux-relabel.c
create mode 100644 daemon/selinux.ml
diff --git a/.gitignore b/.gitignore
index 81cd278cc..02160caff 100644
--- a/.gitignore
+++ b/.gitignore
@@ -108,6 +108,7 @@ Makefile.in
/daemon/parted.mli
/daemon/realpath.mli
/daemon/rpm.mli
+/daemon/selinux.mli
/daemon/sfdisk.mli
/daemon/stamp-guestfsd.pod
/daemon/statvfs.mli
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index 6d7492013..c644d9881 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -59,6 +59,7 @@ generator_built = \
parted.mli \
realpath.mli \
rpm.mli \
+ selinux.mli \
sfdisk.mli \
statvfs.mli \
structs.ml \
@@ -173,7 +174,6 @@ guestfsd_SOURCES = \
rsync.c \
scrub.c \
selinux.c \
- selinux-relabel.c \
sfdisk.c \
sh.c \
sleep.c \
@@ -307,6 +307,7 @@ SOURCES_MLI = \
parted.mli \
realpath.mli \
rpm.mli \
+ selinux.mli \
sfdisk.mli \
statvfs.mli \
structs.mli \
@@ -345,6 +346,7 @@ SOURCES_ML = \
listfs.ml \
realpath.ml \
statvfs.ml \
+ selinux.ml \
inspect_types.ml \
inspect_utils.ml \
inspect_fs_unix_fstab.ml \
diff --git a/daemon/selinux-relabel.c b/daemon/selinux-relabel.c
deleted file mode 100644
index cfc5a31d9..000000000
--- a/daemon/selinux-relabel.c
+++ /dev/null
@@ -1,169 +0,0 @@
-/* libguestfs - the guestfsd daemon
- * Copyright (C) 2016 Red Hat Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include <config.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-
-#include "guestfs_protocol.h"
-#include "daemon.h"
-#include "actions.h"
-#include "optgroups.h"
-
-#include "ignore-value.h"
-
-#define MAX_ARGS 64
-
-int
-optgroup_selinuxrelabel_available (void)
-{
- return prog_exists ("setfiles");
-}
-
-static int
-dir_exists (const char *dir)
-{
- struct stat statbuf;
-
- if (stat (dir, &statbuf) == 0 && S_ISDIR (statbuf.st_mode))
- return 1;
- else
- return 0;
-}
-
-static int
-setfiles_has_option (int *flag, char opt_char)
-{
- CLEANUP_FREE char *err = NULL;
-
- if (*flag == -1) {
- char option[] = { '-', opt_char, '\0' }; /* "-X" */
- char err_opt[32]; /* "invalid option -- 'X'" */
-
- snprintf(err_opt, sizeof(err_opt), "invalid option -- '%c'", opt_char);
- ignore_value (command (NULL, &err, "setfiles", option, NULL));
- *flag = err && strstr (err, /* "invalid option -- " */ err_opt) == NULL;
- }
-
- return *flag;
-}
-
-/* Takes optional arguments, consult optargs_bitmask. */
-int
-do_selinux_relabel (const char *specfile, const char *path,
- int force)
-{
- static int flag_m = -1;
- static int flag_C = -1;
- static int flag_T = -1;
- const char *argv[MAX_ARGS];
- CLEANUP_FREE char *s_dev = NULL, *s_proc = NULL, *s_selinux = NULL,
- *s_sys = NULL, *s_specfile = NULL, *s_path = NULL;
- CLEANUP_FREE char *err = NULL;
- size_t i = 0;
- int setfiles_status;
-
- s_dev = sysroot_path ("/dev");
- if (!s_dev) {
- malloc_error:
- reply_with_perror ("malloc");
- return -1;
- }
- s_proc = sysroot_path ("/proc"); if (!s_proc) goto malloc_error;
- s_selinux = sysroot_path ("/selinux"); if (!s_selinux) goto malloc_error;
- s_sys = sysroot_path ("/sys"); if (!s_sys) goto malloc_error;
- s_specfile = sysroot_path (specfile); if (!s_specfile) goto malloc_error;
- s_path = sysroot_path (path); if (!s_path) goto malloc_error;
-
- /* Default settings if not selected. */
- if (!(optargs_bitmask & GUESTFS_SELINUX_RELABEL_FORCE_BITMASK))
- force = 0;
-
- /* If setfiles takes an excessively long time to run (but still
- * completes) then removing .../contexts/files/file_contexts.bin
- * appears to help. If you find any such cases, please add
- * observations to the bug report:
- * https://bugzilla.redhat.com/show_bug.cgi?id=1396297
- */
- ADD_ARG (argv, i, "setfiles");
- if (force)
- ADD_ARG (argv, i, "-F");
-
- /* Exclude some directories that should never be relabelled in
- * ordinary Linux guests. These won't be mounted anyway. We have
- * to prefix all these with the sysroot path.
- */
- ADD_ARG (argv, i, "-e"); ADD_ARG (argv, i, s_dev);
- ADD_ARG (argv, i, "-e"); ADD_ARG (argv, i, s_proc);
- ADD_ARG (argv, i, "-e"); ADD_ARG (argv, i, s_sys);
- if (dir_exists (s_selinux)) {
- ADD_ARG (argv, i, "-e"); ADD_ARG (argv, i, s_selinux);
- }
-
- /* You have to use the -m option (where available) otherwise
- * setfiles puts all the mountpoints on the excludes list for no
- * useful reason (RHBZ#1433577).
- */
- if (setfiles_has_option (&flag_m, 'm'))
- ADD_ARG (argv, i, "-m");
-
- /* Not only do we want setfiles to trudge through individual relabeling
- * errors, we also want the setfiles exit status to differentiate a fatal
- * error from "relabeling errors only". See RHBZ#1794518.
- */
- if (setfiles_has_option (&flag_C, 'C'))
- ADD_ARG (argv, i, "-C");
-
- /* If the appliance is being run with multiple vCPUs, running setfiles
- * in multithreading mode might speeds up the process. Option "-T" was
- * introduced in SELinux userspace v3.4, and we need to check whether it's
- * supported. Passing "-T 0" creates as many threads as there're available
- * vCPU cores.
- * https://github.com/SELinuxProject/selinux/releases/tag/3.4
- */
- if (setfiles_has_option (&flag_T, 'T')) {
- ADD_ARG (argv, i, "-T"); ADD_ARG (argv, i, "0");
- }
-
- /* Relabelling in a chroot. */
- if (STRNEQ (sysroot, "/")) {
- ADD_ARG (argv, i, "-r");
- ADD_ARG (argv, i, sysroot);
- }
-
- if (verbose)
- ADD_ARG (argv, i, "-v");
- else
- /* Suppress non-error output. */
- ADD_ARG (argv, i, "-q");
-
- /* Add parameters. */
- ADD_ARG (argv, i, s_specfile);
- ADD_ARG (argv, i, s_path);
- ADD_ARG (argv, i, NULL);
-
- setfiles_status = commandrv (NULL, &err, argv);
- if ((setfiles_status == 0) || (setfiles_status == 1 && flag_C))
- return 0;
-
- reply_with_error ("%s", err);
- return -1;
-}
diff --git a/daemon/selinux.c b/daemon/selinux.c
index f4d839c19..4500d0096 100644
--- a/daemon/selinux.c
+++ b/daemon/selinux.c
@@ -39,6 +39,13 @@ optgroup_selinux_available (void)
return 1;
}
+/* For historical reasons, this is really "is setfiles available" */
+int
+optgroup_selinuxrelabel_available (void)
+{
+ return prog_exists ("setfiles");
+}
+
/* setcon is only valid under the following circumstances:
* - single threaded
* - enforcing=0
diff --git a/daemon/selinux.ml b/daemon/selinux.ml
new file mode 100644
index 000000000..d954fdead
--- /dev/null
+++ b/daemon/selinux.ml
@@ -0,0 +1,101 @@
+(* SELinux functions.
+ * Copyright (C) 2009-2025 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *)
+
+open Printf
+
+open Std_utils
+
+open Sysroot
+open Utils
+
+(* Test if setfiles has various options.
+ *
+ * The only way to do this is to run setfiles with the option alone, and
+ * test for the stderr message [invalid option -- 'X'].
+ *)
+let setfiles_has_option_m,
+ setfiles_has_option_C,
+ setfiles_has_option_T =
+ let setfiles_has_option flag =
+ let err_msg = sprintf "invalid option -- '%c'" flag in
+ let opt = sprintf "-%c" flag in
+ let _, _, err = commandr "setfiles" [opt] in
+ String.find err err_msg = -1
+ in
+ let setfiles_has_option_m = lazy (setfiles_has_option 'm')
+ and setfiles_has_option_C = lazy (setfiles_has_option 'C')
+ and setfiles_has_option_T = lazy (setfiles_has_option 'T') in
+ (fun () -> Lazy.force setfiles_has_option_m),
+ (fun () -> Lazy.force setfiles_has_option_C),
+ (fun () -> Lazy.force setfiles_has_option_T)
+
+let selinux_relabel ?(force = false) specfile path =
+ (* Prefix /sysroot on all paths. *)
+ let ignored_paths =
+ [ "/dev"; "/proc"; "/selinux"; "/sys" ] |>
+ List.map sysroot_path in
+ let specfile = sysroot_path specfile in
+ let path = sysroot_path path in
+
+ let args = ref [] in
+ if force then List.push_back args "-F";
+ List.iter (
+ fun ignored_path ->
+ List.push_back_list args [ "-e"; ignored_path ]
+ ) ignored_paths;
+
+ (* You have to use the -m option (where available) otherwise
+ * setfiles puts all the mountpoints on the excludes list for no
+ * useful reason (RHBZ#1433577).
+ *)
+ if setfiles_has_option_m () then List.push_back args "-m";
+
+ (* Not only do we want setfiles to trudge through individual relabeling
+ * errors, we also want the setfiles exit status to differentiate a fatal
+ * error from "relabeling errors only". See RHBZ#1794518.
+ *)
+ if setfiles_has_option_C () then List.push_back args "-C";
+
+ (* If the appliance is being run with multiple vCPUs, running setfiles
+ * in multithreading mode might speeds up the process. Option "-T" was
+ * introduced in SELinux userspace v3.4, and we need to check whether it's
+ * supported. Passing "-T 0" creates as many threads as there're available
+ * vCPU cores.
+ * https://github.com/SELinuxProject/selinux/releases/tag/3.4
+ *)
+ if setfiles_has_option_T () then
+ List.push_back_list args [ "-T"; "0" ];
+
+ (* Relabelling in a chroot. *)
+ if sysroot () <> "/" then
+ List.push_back_list args [ "-r"; sysroot () ];
+
+ if verbose () then
+ List.push_back args "-v"
+ else
+ (* Suppress non-error output. *)
+ List.push_back args "-q";
+
+ (* Add parameters. *)
+ List.push_back_list args [ specfile; path ];
+
+ let args = !args in
+ let r, _, err = commandr "setfiles" args in
+
+ let ok = r = 0 || r = 1 && setfiles_has_option_C () in
+ if not ok then failwithf "setfiles: %s" err
diff --git a/docs/C_SOURCE_FILES b/docs/C_SOURCE_FILES
index cdfb1d615..5270667bf 100644
--- a/docs/C_SOURCE_FILES
+++ b/docs/C_SOURCE_FILES
@@ -132,7 +132,6 @@ daemon/rename.c
daemon/rpm-c.c
daemon/rsync.c
daemon/scrub.c
-daemon/selinux-relabel.c
daemon/selinux.c
daemon/sfdisk.c
daemon/sh.c
diff --git a/generator/actions_core.ml b/generator/actions_core.ml
index 108494ece..128cbe0e9 100644
--- a/generator/actions_core.ml
+++ b/generator/actions_core.ml
@@ -9359,6 +9359,7 @@ fails and the C<errno> is set to C<ENODEV>." };
{ defaults with
name = "selinux_relabel"; added = (1, 33, 43);
style = RErr, [String (PlainString, "specfile"); String (Pathname, "path")], [OBool "force"];
+ impl = OCaml "Selinux.selinux_relabel";
optional = Some "selinuxrelabel";
test_excuse = "tests are in the tests/relabel directory";
shortdesc = "relabel parts of the filesystem";
diff --git a/po/POTFILES b/po/POTFILES
index acf3a68d7..fbe0a7fe2 100644
--- a/po/POTFILES
+++ b/po/POTFILES
@@ -110,7 +110,6 @@ daemon/rename.c
daemon/rpm-c.c
daemon/rsync.c
daemon/scrub.c
-daemon/selinux-relabel.c
daemon/selinux.c
daemon/sfdisk.c
daemon/sh.c
--
2.47.3

View File

@ -0,0 +1,80 @@
From 5b3de10b018e891ef7cc7fae0e8b1b32445eb392 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Fri, 23 Jan 2026 08:51:11 +0000
Subject: [PATCH] generator: Deprecate xfs_info (replaced by xfs_info2)
Deprecate this function, and suggest using xfs_info2 as its
replacement.
(cherry picked from commit 7833461af76d1feaf2dd1e779f7f04bcca91a6bc)
---
generator/actions_core.ml | 21 ---------------------
generator/actions_core_deprecated.ml | 22 ++++++++++++++++++++++
2 files changed, 22 insertions(+), 21 deletions(-)
diff --git a/generator/actions_core.ml b/generator/actions_core.ml
index f2cf83440..26d77c667 100644
--- a/generator/actions_core.ml
+++ b/generator/actions_core.ml
@@ -7225,27 +7225,6 @@ be returned if you called C<guestfs_list_devices>.
To find out the maximum number of devices that could be added,
call C<guestfs_max_disks>.|} };
- { defaults with
- name = "xfs_info"; added = (1, 19, 21);
- style = RStruct ("info", "xfsinfo"), [String (Dev_or_Path, "pathordevice")], [];
- impl = OCaml "Xfs.xfs_info";
- optional = Some "xfs";
- tests = [
- InitEmpty, Always, TestResult (
- [["part_disk"; "/dev/sda"; "mbr"];
- ["mkfs"; "xfs"; "/dev/sda1"; ""; "NOARG"; ""; ""; "NOARG"];
- ["mount"; "/dev/sda1"; "/"];
- ["xfs_info"; "/"]], "ret->xfs_blocksize == 4096"), []
- ];
- shortdesc = "get information about the XFS filesystem";
- longdesc = {|C<pathordevice> is a mounted XFS filesystem or a device containing
-an XFS filesystem. This command returns miscellaneous
-metadata about the XFS filesystem.
-
-The returned struct contains miscellaneous metadata. Missing
-fields are returned as C<-1> (for numeric fields) or empty
-string.|} };
-
{ defaults with
name = "pvchange_uuid"; added = (1, 19, 26);
style = RErr, [String (Device, "device")], [];
diff --git a/generator/actions_core_deprecated.ml b/generator/actions_core_deprecated.ml
index aa98bd5fe..308495c45 100644
--- a/generator/actions_core_deprecated.ml
+++ b/generator/actions_core_deprecated.ml
@@ -945,4 +945,26 @@ The optional C<force> boolean controls whether the context
is reset for customizable files, and also whether the
user, role and range parts of the file context is changed.|} };
+ { defaults with
+ name = "xfs_info"; added = (1, 19, 21);
+ style = RStruct ("info", "xfsinfo"), [String (Dev_or_Path, "pathordevice")], [];
+ impl = OCaml "Xfs.xfs_info";
+ optional = Some "xfs";
+ deprecated_by = Replaced_by "xfs_info2";
+ tests = [
+ InitEmpty, Always, TestResult (
+ [["part_disk"; "/dev/sda"; "mbr"];
+ ["mkfs"; "xfs"; "/dev/sda1"; ""; "NOARG"; ""; ""; "NOARG"];
+ ["mount"; "/dev/sda1"; "/"];
+ ["xfs_info"; "/"]], "ret->xfs_blocksize == 4096"), []
+ ];
+ shortdesc = "get information about the XFS filesystem";
+ longdesc = {|C<pathordevice> is a mounted XFS filesystem or a device containing
+an XFS filesystem. This command returns miscellaneous
+metadata about the XFS filesystem.
+
+The returned struct contains miscellaneous metadata. Missing
+fields are returned as C<-1> (for numeric fields) or empty
+string.|} };
+
]
--
2.47.3

View File

@ -1,99 +0,0 @@
From fd4db60cffd9d0ece25a436932aca5411e13b94e Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Tue, 12 Aug 2025 14:05:44 +0100
Subject: [PATCH] generator: Implement StringList for OCaml functions
No existing OCaml functions have a StringList parameter, but we would
like to add one.
The original plan seems to have been to map these to 'string array'
types, but 'string list' is more natural, albeit marginally less
efficient. The implementation here just has to convert the 'char **'
into the OCaml linked list of values.
---
daemon/daemon-c.c | 24 ++++++++++++++++++++++++
daemon/daemon-c.h | 1 +
generator/daemon.ml | 6 ++++--
3 files changed, 29 insertions(+), 2 deletions(-)
diff --git a/daemon/daemon-c.c b/daemon/daemon-c.c
index 1754cf0d2..371c2a9e4 100644
--- a/daemon/daemon-c.c
+++ b/daemon/daemon-c.c
@@ -114,6 +114,30 @@ guestfs_int_daemon_copy_mountable (const mountable_t *mountable)
CAMLreturn (r);
}
+/* Implement StringList(...) parameter. */
+value
+guestfs_int_daemon_copy_string_list (char * const *strs)
+{
+ CAMLparam0 ();
+ CAMLlocal3 (v, tlv, rv);
+ size_t i;
+
+ /* We need to build the list backwards so start at the end. */
+ for (i = 0; strs[i] != NULL; ++i)
+ ;
+
+ while (i > 0) {
+ --i;
+ v = caml_copy_string (strs[i]);
+ rv = caml_alloc (2, 0);
+ Store_field (rv, 0, v);
+ Store_field (rv, 1, tlv);
+ tlv = rv;
+ }
+
+ CAMLreturn (rv);
+}
+
/* Implement RStringList. */
char **
guestfs_int_daemon_return_string_list (value retv)
diff --git a/daemon/daemon-c.h b/daemon/daemon-c.h
index 9b7085bce..b06efc0cf 100644
--- a/daemon/daemon-c.h
+++ b/daemon/daemon-c.h
@@ -29,6 +29,7 @@
extern void guestfs_int_daemon_exn_to_reply_with_error (const char *func, value exn);
extern value guestfs_int_daemon_copy_mountable (const mountable_t *mountable);
+extern value guestfs_int_daemon_copy_string_list (char * const *strs);
extern char **guestfs_int_daemon_return_string_list (value retv);
extern char *guestfs_int_daemon_return_string_mountable (value retv);
extern char **guestfs_int_daemon_return_string_mountable_list (value retv);
diff --git a/generator/daemon.ml b/generator/daemon.ml
index 6221531d2..2b74f3059 100644
--- a/generator/daemon.ml
+++ b/generator/daemon.ml
@@ -558,7 +558,7 @@ and generate_ocaml_daemon_prototype name (ret, args, optargs) =
| OInt n -> pr "?%s:int -> " n
| OInt64 n -> pr "?%s:int64 -> " n
| OString n -> pr "?%s:string -> " n
- | OStringList n -> pr "?%s:string array -> " n
+ | OStringList n -> pr "?%s:string list -> " n
) optargs;
if args <> [] then
List.iter (
@@ -566,7 +566,7 @@ and generate_ocaml_daemon_prototype name (ret, args, optargs) =
| String (typ, _) -> pr "%s -> " (type_for_stringt typ)
| BufferIn _ -> pr "string -> "
| OptString _ -> pr "string option -> "
- | StringList (typ, _) -> pr "%s array -> " (type_for_stringt typ)
+ | StringList (typ, _) -> pr "%s list -> " (type_for_stringt typ)
| Bool _ -> pr "bool -> "
| Int _ -> pr "int -> "
| Int64 _ | Pointer _ -> pr "int64 -> "
@@ -820,6 +820,8 @@ let generate_daemon_caml_stubs () =
pr "guestfs_int_daemon_copy_mountable (%s)" n
| String _ -> assert false
| OptString _ -> assert false
+ | StringList ((PlainString|Filename|Pathname), n) ->
+ pr "guestfs_int_daemon_copy_string_list (%s)" n
| StringList _ -> assert false
| BufferIn _ -> assert false
| Pointer _ -> assert false
--
2.47.3

View File

@ -0,0 +1,33 @@
From 4a5ee2fbcbef5f10b8faf8f8659ba5f82145f42d Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Mon, 26 Jan 2026 14:59:13 +0000
Subject: [PATCH] tests/disks/debug-qemu.sh: Fix test for update QMP test
Commit f7a24b2ea8 ("lib/qemu.c: Use machine type none when inspecting
QMP properties") changed the number of command line parameters used
before the '-qmp stdio' option when libguestfs queries qemu features.
This broke some tests which rely on the exact order of parameters.
Fixes: commit f7a24b2ea81f3c5c2754fcf43e0347c7c5378b4a
Updates: commit 5da8102f5f59b9781075440dc68c8d08f9c8691e
(cherry picked from commit 45d0e66d02adf1a42c8eca979fa38d2f91d9f3c2)
---
tests/disks/debug-qemu.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/disks/debug-qemu.sh b/tests/disks/debug-qemu.sh
index 96c90553b..e6e80abe3 100755
--- a/tests/disks/debug-qemu.sh
+++ b/tests/disks/debug-qemu.sh
@@ -25,7 +25,7 @@ fi
# The direct backend runs qemu ... -qmp stdio to query for KVM. For
# the test to pass we have to provide an answer here.
-if [ "x$7" = "x-qmp" ]; then
+if [ "x$5" = "x-qmp" ]; then
# Consume stdin first.
cat >/dev/null
# Write some fake output.
--
2.47.3

View File

@ -1,83 +0,0 @@
From e4d9ee3fbc58c5993db0c75c647fdf904c520918 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Tue, 12 Aug 2025 14:04:42 +0100
Subject: [PATCH] generator: Allow StringList(Pathname) parameters
This was previously not implemented. It just requires us to call
ABS_PATH on each parameter. ABS_PATH checks the parameter is an
absolute path.
---
generator/checks.ml | 1 -
generator/daemon.ml | 16 ++++++++++++----
2 files changed, 12 insertions(+), 5 deletions(-)
diff --git a/generator/checks.ml b/generator/checks.ml
index d64d49d66..4207c0677 100644
--- a/generator/checks.ml
+++ b/generator/checks.ml
@@ -166,7 +166,6 @@ let () =
| StringList (FileIn, _)
| StringList (FileOut, _)
| StringList (Mountable, _)
- | StringList (Pathname, _)
| StringList (Dev_or_Path, _)
| StringList (Mountable_or_Path, _)
| StringList (Key, _)
diff --git a/generator/daemon.ml b/generator/daemon.ml
index 2b74f3059..6197288df 100644
--- a/generator/daemon.ml
+++ b/generator/daemon.ml
@@ -173,7 +173,7 @@ let generate_daemon_stubs actions () =
| String ((Mountable|Mountable_or_Path), n) ->
pr " CLEANUP_FREE_MOUNTABLE mountable_t %s\n" n;
pr " = { .device = NULL, .volume = NULL };\n"
- | StringList ((PlainString|Filename), n) ->
+ | StringList ((PlainString|Filename|Pathname), n) ->
pr " char **%s;\n" n
| StringList (Device, n) ->
pr " CLEANUP_FREE_STRING_LIST char **%s = NULL;\n" n
@@ -184,7 +184,7 @@ let generate_daemon_stubs actions () =
pr " const char *%s;\n" n;
pr " size_t %s_size;\n" n
| String ((FileIn|FileOut|Filename), _)
- | StringList ((Mountable|Pathname|FileIn|FileOut|Key|GUID
+ | StringList ((Mountable|FileIn|FileOut|Key|GUID
|Dev_or_Path|Mountable_or_Path), _)
| Pointer _ -> assert false
) args_passed_to_daemon
@@ -260,7 +260,7 @@ let generate_daemon_stubs actions () =
n n is_filein;
| String ((PlainString|Key|GUID), n) -> pr_args n
| OptString n -> pr " %s = args.%s ? *args.%s : NULL;\n" n n n
- | StringList ((PlainString|Filename) as arg, n) ->
+ | StringList ((PlainString|Filename|Pathname) as arg, n) ->
(match arg with
| Filename ->
pr " {\n";
@@ -275,6 +275,14 @@ let generate_daemon_stubs actions () =
pr " }\n";
pr " }\n";
pr " }\n"
+ | Pathname ->
+ pr " {\n";
+ pr " size_t i;\n";
+ pr " for (i = 0; i < args.%s.%s_len; ++i) {\n" n n;
+ pr " ABS_PATH (args.%s.%s_val[i], %b, return);\n"
+ n n is_filein;
+ pr " }\n";
+ pr " }\n"
| _ -> ()
);
pr " /* Ugly, but safe and avoids copying the strings. */\n";
@@ -307,7 +315,7 @@ let generate_daemon_stubs actions () =
pr " %s = args.%s.%s_val;\n" n n n;
pr " %s_size = args.%s.%s_len;\n" n n n
| String ((FileIn|FileOut|Filename), _)
- | StringList ((Mountable|Pathname|FileIn|FileOut|Key|GUID
+ | StringList ((Mountable|FileIn|FileOut|Key|GUID
|Dev_or_Path|Mountable_or_Path), _)
| Pointer _ -> assert false
) args_passed_to_daemon;
--
2.47.3

View File

@ -1,311 +0,0 @@
From 1c0b56158aa63359d1e53f7a31b483194f235a34 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Tue, 12 Aug 2025 13:27:32 +0100
Subject: [PATCH] daemon: Deprecate guestfs_selinux_relabel, replace with
guestfs_setfiles
The guestfs_selinux_relabel function was very hard to use. In
particular it didn't just do an SELinux relabel as you might expect.
Instead you have to write a whole bunch of code around it (example[1])
to make it useful.
Another problem is that it doesn't let you pass multiple paths to the
setfiles command, but the command itself does permit that (and, as it
turns out, will require it). There is no backwards compatible way to
extend the existing definition to allow a list parameter without
breaking API.
So deprecate guestfs_selinux_relabel. Reimplement it as
guestfs_setfiles. The new function is basically the same as the old
one, but allows you to pass a list of paths. The old function calls
the new function with a single path parameter.
[1] https://github.com/libguestfs/libguestfs-common/blob/master/mlcustomize/SELinux_relabel.ml
---
daemon/selinux.ml | 117 ++++++++++++++-------------
generator/actions_core.ml | 49 +++++------
generator/actions_core_deprecated.ml | 24 ++++++
generator/proc_nr.ml | 1 +
gobject/Makefile.inc | 2 +
lib/MAX_PROC_NR | 2 +-
tests/relabel/test-relabel.pl | 2 +-
7 files changed, 117 insertions(+), 80 deletions(-)
diff --git a/daemon/selinux.ml b/daemon/selinux.ml
index d954fdead..db0d71455 100644
--- a/daemon/selinux.ml
+++ b/daemon/selinux.ml
@@ -44,58 +44,65 @@ let setfiles_has_option_m,
(fun () -> Lazy.force setfiles_has_option_C),
(fun () -> Lazy.force setfiles_has_option_T)
-let selinux_relabel ?(force = false) specfile path =
- (* Prefix /sysroot on all paths. *)
- let ignored_paths =
- [ "/dev"; "/proc"; "/selinux"; "/sys" ] |>
- List.map sysroot_path in
- let specfile = sysroot_path specfile in
- let path = sysroot_path path in
-
- let args = ref [] in
- if force then List.push_back args "-F";
- List.iter (
- fun ignored_path ->
- List.push_back_list args [ "-e"; ignored_path ]
- ) ignored_paths;
-
- (* You have to use the -m option (where available) otherwise
- * setfiles puts all the mountpoints on the excludes list for no
- * useful reason (RHBZ#1433577).
- *)
- if setfiles_has_option_m () then List.push_back args "-m";
-
- (* Not only do we want setfiles to trudge through individual relabeling
- * errors, we also want the setfiles exit status to differentiate a fatal
- * error from "relabeling errors only". See RHBZ#1794518.
- *)
- if setfiles_has_option_C () then List.push_back args "-C";
-
- (* If the appliance is being run with multiple vCPUs, running setfiles
- * in multithreading mode might speeds up the process. Option "-T" was
- * introduced in SELinux userspace v3.4, and we need to check whether it's
- * supported. Passing "-T 0" creates as many threads as there're available
- * vCPU cores.
- * https://github.com/SELinuxProject/selinux/releases/tag/3.4
- *)
- if setfiles_has_option_T () then
- List.push_back_list args [ "-T"; "0" ];
-
- (* Relabelling in a chroot. *)
- if sysroot () <> "/" then
- List.push_back_list args [ "-r"; sysroot () ];
-
- if verbose () then
- List.push_back args "-v"
- else
- (* Suppress non-error output. *)
- List.push_back args "-q";
-
- (* Add parameters. *)
- List.push_back_list args [ specfile; path ];
-
- let args = !args in
- let r, _, err = commandr "setfiles" args in
-
- let ok = r = 0 || r = 1 && setfiles_has_option_C () in
- if not ok then failwithf "setfiles: %s" err
+let setfiles ?(force = false) specfile paths =
+ if paths = [] then ()
+ else (
+ (* Prefix /sysroot on all paths. *)
+ let ignored_paths =
+ [ "/dev"; "/proc"; "/selinux"; "/sys" ] |>
+ List.map sysroot_path in
+ let specfile = sysroot_path specfile in
+ let paths = List.map sysroot_path paths in
+
+ let args = ref [] in
+ if force then List.push_back args "-F";
+ List.iter (
+ fun ignored_path ->
+ List.push_back_list args [ "-e"; ignored_path ]
+ ) ignored_paths;
+
+ (* You have to use the -m option (where available) otherwise
+ * setfiles puts all the mountpoints on the excludes list for no
+ * useful reason (RHBZ#1433577).
+ *)
+ if setfiles_has_option_m () then List.push_back args "-m";
+
+ (* Not only do we want setfiles to trudge through individual relabeling
+ * errors, we also want the setfiles exit status to differentiate a fatal
+ * error from "relabeling errors only". See RHBZ#1794518.
+ *)
+ if setfiles_has_option_C () then List.push_back args "-C";
+
+ (* If the appliance is being run with multiple vCPUs, running setfiles
+ * in multithreading mode might speeds up the process. Option "-T" was
+ * introduced in SELinux userspace v3.4, and we need to check whether it's
+ * supported. Passing "-T 0" creates as many threads as there're available
+ * vCPU cores.
+ * https://github.com/SELinuxProject/selinux/releases/tag/3.4
+ *)
+ if setfiles_has_option_T () then
+ List.push_back_list args [ "-T"; "0" ];
+
+ (* Relabelling in a chroot. *)
+ if sysroot () <> "/" then
+ List.push_back_list args [ "-r"; sysroot () ];
+
+ if verbose () then
+ List.push_back args "-v"
+ else
+ (* Suppress non-error output. *)
+ List.push_back args "-q";
+
+ (* Add parameters. *)
+ List.push_back args specfile;
+ List.push_back_list args paths;
+
+ let args = !args in
+ let r, _, err = commandr "setfiles" args in
+
+ let ok = r = 0 || r = 1 && setfiles_has_option_C () in
+ if not ok then failwithf "setfiles: %s" err
+ )
+
+(* This is the deprecated selinux_relabel function from libguestfs <= 1.56. *)
+let selinux_relabel ?force specfile path = setfiles ?force specfile [path]
diff --git a/generator/actions_core.ml b/generator/actions_core.ml
index 128cbe0e9..60d3140ed 100644
--- a/generator/actions_core.ml
+++ b/generator/actions_core.ml
@@ -9356,29 +9356,6 @@ Show all the devices where the filesystems in C<device> is spanned over.
If not all the devices for the filesystems are present, then this function
fails and the C<errno> is set to C<ENODEV>." };
- { defaults with
- name = "selinux_relabel"; added = (1, 33, 43);
- style = RErr, [String (PlainString, "specfile"); String (Pathname, "path")], [OBool "force"];
- impl = OCaml "Selinux.selinux_relabel";
- optional = Some "selinuxrelabel";
- test_excuse = "tests are in the tests/relabel directory";
- shortdesc = "relabel parts of the filesystem";
- longdesc = "\
-SELinux relabel parts of the filesystem.
-
-The C<specfile> parameter controls the policy spec file used.
-You have to parse C</etc/selinux/config> to find the correct
-SELinux policy and then pass the spec file, usually:
-C</etc/selinux/> + I<selinuxtype> + C</contexts/files/file_contexts>.
-
-The required C<path> parameter is the top level directory where
-relabelling starts. Normally you should pass C<path> as C</>
-to relabel the whole guest filesystem.
-
-The optional C<force> boolean controls whether the context
-is reset for customizable files, and also whether the
-user, role and range parts of the file context is changed." };
-
{ defaults with
name = "mksquashfs"; added = (1, 35, 25);
style = RErr, [String (Pathname, "path"); String (FileOut, "filename")], [OString "compress"; OStringList "excludes"];
@@ -9820,4 +9797,30 @@ them visible.
Use C<guestfs_list_dm_devices> to list all device mapper devices." };
+ { defaults with
+ name = "setfiles"; added = (1, 57, 1);
+ style = RErr, [String (PlainString, "specfile"); StringList (Pathname, "paths")], [OBool "force"];
+ impl = OCaml "Selinux.setfiles";
+ optional = Some "selinuxrelabel";
+ test_excuse = "tests are in the tests/relabel directory";
+ shortdesc = "low level relabel parts of the filesystem";
+ longdesc = "\
+This invokes the SELinux C<setfiles> command which is a low
+level tool used to relabel parts of the filesystem.
+
+The C<specfile> parameter controls the policy spec file used.
+You have to parse C</etc/selinux/config> to find the correct
+SELinux policy and then pass the spec file, usually:
+C</etc/selinux/> + I<selinuxtype> + C</contexts/files/file_contexts>.
+
+The required C<paths> parameter is the list of top level directories
+where relabelling starts. C<setfiles> will only relabel up to
+filesystem boundaries so, for example, passing just C<\"/\"> will
+relabel the whole root filesystem, but no other mounted filesystems.
+If the list is empty, setfiles is not called.
+
+The optional C<force> boolean controls whether the context
+is reset for customizable files, and also whether the
+user, role and range parts of the file context is changed." };
+
]
diff --git a/generator/actions_core_deprecated.ml b/generator/actions_core_deprecated.ml
index 9d4b29f9d..2b1f5cdb4 100644
--- a/generator/actions_core_deprecated.ml
+++ b/generator/actions_core_deprecated.ml
@@ -942,4 +942,28 @@ This call does nothing and returns an error." };
Used to check a btrfs filesystem, C<device> is the device file where the
filesystem is stored." };
+ { defaults with
+ name = "selinux_relabel"; added = (1, 33, 43);
+ style = RErr, [String (PlainString, "specfile"); String (Pathname, "path")], [OBool "force"];
+ impl = OCaml "Selinux.selinux_relabel";
+ optional = Some "selinuxrelabel";
+ deprecated_by = Replaced_by "setfiles";
+ test_excuse = "tests are in the tests/relabel directory";
+ shortdesc = "relabel parts of the filesystem";
+ longdesc = "\
+SELinux relabel parts of the filesystem.
+
+The C<specfile> parameter controls the policy spec file used.
+You have to parse C</etc/selinux/config> to find the correct
+SELinux policy and then pass the spec file, usually:
+C</etc/selinux/> + I<selinuxtype> + C</contexts/files/file_contexts>.
+
+The required C<path> parameter is the top level directory where
+relabelling starts. Normally you should pass C<path> as C</>
+to relabel the whole guest filesystem.
+
+The optional C<force> boolean controls whether the context
+is reset for customizable files, and also whether the
+user, role and range parts of the file context is changed." };
+
]
diff --git a/generator/proc_nr.ml b/generator/proc_nr.ml
index 63cd72a3c..42624afef 100644
--- a/generator/proc_nr.ml
+++ b/generator/proc_nr.ml
@@ -521,6 +521,7 @@ let proc_nr = [
516, "command_out";
517, "sh_out";
518, "btrfs_scrub_full";
+519, "setfiles";
]
(* End of list. If adding a new entry, add it at the end of the list
diff --git a/gobject/Makefile.inc b/gobject/Makefile.inc
index b54245977..b828113c6 100644
--- a/gobject/Makefile.inc
+++ b/gobject/Makefile.inc
@@ -106,6 +106,7 @@ guestfs_gobject_headers= \
include/guestfs-gobject/optargs-rsync_out.h \
include/guestfs-gobject/optargs-selinux_relabel.h \
include/guestfs-gobject/optargs-set_e2attrs.h \
+ include/guestfs-gobject/optargs-setfiles.h \
include/guestfs-gobject/optargs-syslinux.h \
include/guestfs-gobject/optargs-tar_in.h \
include/guestfs-gobject/optargs-tar_out.h \
@@ -201,6 +202,7 @@ guestfs_gobject_sources= \
src/optargs-rsync_out.c \
src/optargs-selinux_relabel.c \
src/optargs-set_e2attrs.c \
+ src/optargs-setfiles.c \
src/optargs-syslinux.c \
src/optargs-tar_in.c \
src/optargs-tar_out.c \
diff --git a/lib/MAX_PROC_NR b/lib/MAX_PROC_NR
index 9a26b94d0..08f851b6e 100644
--- a/lib/MAX_PROC_NR
+++ b/lib/MAX_PROC_NR
@@ -1 +1 @@
-518
+519
diff --git a/tests/relabel/test-relabel.pl b/tests/relabel/test-relabel.pl
index 06fb0840b..4d4f6c7ba 100755
--- a/tests/relabel/test-relabel.pl
+++ b/tests/relabel/test-relabel.pl
@@ -87,7 +87,7 @@ $g->write ("/etc/file_contexts", <<'EOF');
EOF
# Do the relabel.
-$g->selinux_relabel ("/etc/file_contexts", "/", force => 1);
+$g->setfiles ("/etc/file_contexts", ["/"], force => 1);
# Check the labels were set correctly.
my $errors = 0;
--
2.47.3

View File

@ -1,75 +0,0 @@
From b43ca06ea69cebbdd774ed03bc0da63eb3955d66 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Thu, 14 Aug 2025 14:56:47 +0100
Subject: [PATCH] daemon/inspect_fs_windows.ml: Add debugging for MBR drive
mappings
The function 'map_registry_disk_blob_gpt' immediately below this one
has a debugging statement. Add the equivalent to the function
'map_registry_disk_blob_mbr'.
The output looks like:
map_registry_disk_blob_mbr: searching for MBR disk ID 31 32 33 34
map_registry_disk_blob_mbr: searching for MBR partition offset 00 00 00 10 00 00 00 00
---
daemon/inspect_fs_windows.ml | 8 ++++++++
daemon/utils.ml | 4 ++++
daemon/utils.mli | 4 ++++
3 files changed, 16 insertions(+)
diff --git a/daemon/inspect_fs_windows.ml b/daemon/inspect_fs_windows.ml
index dbaf4c362..5991cdba3 100644
--- a/daemon/inspect_fs_windows.ml
+++ b/daemon/inspect_fs_windows.ml
@@ -376,6 +376,10 @@ and map_registry_disk_blob_mbr devices blob =
* disk with this disk ID.
*)
let diskid = String.sub blob 0 4 in
+ if verbose () then
+ eprintf "map_registry_disk_blob_mbr: searching for MBR disk ID %s\n%!"
+ (hex_of_string diskid);
+
let device =
List.find (
fun dev ->
@@ -388,6 +392,10 @@ and map_registry_disk_blob_mbr devices blob =
* partition byte offset from Parted.part_list.
*)
let offset = String.sub blob 4 8 in
+ if verbose () then
+ eprintf "map_registry_disk_blob_mbr: searching for MBR partition offset \
+ %s\n%!"
+ (hex_of_string offset);
let offset = int_of_le64 offset in
let partitions = Parted.part_list device in
let partition =
diff --git a/daemon/utils.ml b/daemon/utils.ml
index 40584c9f1..3aa1d7ed2 100644
--- a/daemon/utils.ml
+++ b/daemon/utils.ml
@@ -291,3 +291,7 @@ let parse_key_value_strings ?unquote lines =
match unquote with
| None -> lines
| Some f -> List.map (fun (k, v) -> (k, f v)) lines
+
+let hex_of_string s =
+ let bytes = String.map_chars (fun c -> sprintf "%02x" (Char.code c)) s in
+ String.concat " " bytes
diff --git a/daemon/utils.mli b/daemon/utils.mli
index 0f2ae471f..e14735038 100644
--- a/daemon/utils.mli
+++ b/daemon/utils.mli
@@ -121,5 +121,9 @@ val parse_key_value_strings : ?unquote:(string -> string) -> string list -> (str
it is applied on the values as unquote function. Empty lines,
or that start with a comment character [#], are ignored. *)
+val hex_of_string : string -> string
+(** Return a string as a list of hex bytes.
+ Use this for debugging msgs only. *)
+
(**/**)
val get_verbose_flag : unit -> bool
--
2.47.3

View File

@ -1,32 +0,0 @@
From 7bbadaec5ab9c60bd5ad8e1feee39af9f170b552 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Thu, 14 Aug 2025 14:57:45 +0100
Subject: [PATCH] daemon/inspect_fs_windows.ml: Add debugging when we start
registry analysis
Add some debugging when we begin the process of analyzing the Windows
registry of a guest.
---
daemon/inspect_fs_windows.ml | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/daemon/inspect_fs_windows.ml b/daemon/inspect_fs_windows.ml
index 5991cdba3..00acf5196 100644
--- a/daemon/inspect_fs_windows.ml
+++ b/daemon/inspect_fs_windows.ml
@@ -207,6 +207,12 @@ and check_windows_registry systemroot data =
if Is.is_file system_hive then Some system_hive else None in
data.windows_system_hive <- system_hive;
+ if verbose () then
+ eprintf "check_windows_registry: software hive: %s\n\
+ check_windows_registry: system hive: %s\n%!"
+ (Option.value ~default:"None" software_hive)
+ (Option.value ~default:"None" system_hive);
+
match software_hive, system_hive with
| None, _ | Some _, None -> ()
| Some software_hive, Some system_hive ->
--
2.47.3

View File

@ -1,78 +0,0 @@
From 42afed95dc6611dc9585ab23134bdcc39a5b75ec Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Thu, 14 Aug 2025 15:17:59 +0100
Subject: [PATCH] daemon/inspect_fs_windows.ml: Ignore blank disks in drive
mapping
If HKLM\System\MountedDevices references a blank disk, then when we
try to search for the actual backing device we will get an error from
parted:
parted: /dev/sdb: parted exited with status 1: Error: /dev/sdb: unrecognised disk label: Invalid argument
Just ignore these errors instead of failing inspection.
Fixes: https://issues.redhat.com/browse/RHEL-108803
Reported-by: Ameen Barakat
Thanks: Ming Xie
---
daemon/inspect_fs_windows.ml | 35 ++++++++++++++++++++++++++---------
1 file changed, 26 insertions(+), 9 deletions(-)
diff --git a/daemon/inspect_fs_windows.ml b/daemon/inspect_fs_windows.ml
index 00acf5196..ba8ef4ee3 100644
--- a/daemon/inspect_fs_windows.ml
+++ b/daemon/inspect_fs_windows.ml
@@ -389,8 +389,18 @@ and map_registry_disk_blob_mbr devices blob =
let device =
List.find (
fun dev ->
- Parted.part_get_parttype dev = "msdos" &&
+ try
+ Parted.part_get_parttype dev = "msdos" &&
pread dev 4 0x01b8 = diskid
+ with Unix.Unix_error (EINVAL, "parted", msg) ->
+ (* Errors can happen here if the disk is empty. Just ignore
+ * them. It means the drive mapping might have missing
+ * entries but that's not important. (RHEL-108803)
+ *)
+ if verbose () then
+ eprintf "map_registry_disk_blob_mbr: parted returned: \
+ %s (ignored)\n" msg;
+ false
) devices in
(* Next 8 bytes are the offset of the partition in bytes(!) given as
@@ -428,14 +438,21 @@ and map_registry_disk_blob_gpt partitions blob =
let partition =
List.find (
fun part ->
- let partnum = Devsparts.part_to_partnum part in
- let device = Devsparts.part_to_dev part in
- let typ = Parted.part_get_parttype device in
- if typ <> "gpt" then false
- else (
- let guid = Sfdisk.part_get_gpt_guid device partnum in
- String.lowercase_ascii guid = blob_guid
- )
+ try
+ let partnum = Devsparts.part_to_partnum part in
+ let device = Devsparts.part_to_dev part in
+ let typ = Parted.part_get_parttype device in
+ if typ <> "gpt" then false
+ else (
+ let guid = Sfdisk.part_get_gpt_guid device partnum in
+ String.lowercase_ascii guid = blob_guid
+ )
+ with Unix.Unix_error (EINVAL, "parted", msg) ->
+ (* See comment in MBR code above (RHEL-108803) *)
+ if verbose () then
+ eprintf "map_registry_disk_blob_gpt: parted returned: \
+ %s (ignored)\n" msg;
+ false
) partitions in
Some partition
with
--
2.47.3

View File

@ -1,78 +0,0 @@
From 2613e5301b9e484b5e241afeaff96413597eaf7c Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Wed, 3 Dec 2025 11:15:39 +0000
Subject: [PATCH] daemon/device-name-translation.c: Fix btrfs volume reverse
translation
Devices associated with btrfs volumes are not reverse-translated
(e.g., btrfsvol:/dev/sdX to sdY).
Forward translation occurs, creating a path mismatch. This causes
errors in subsequent btrfs commands.
Thanks: Arye Yurkovsky
(cherry picked from commit c7b204bce3c860c10663fcb9250dd934eaf3390a)
---
daemon/device-name-translation.c | 36 ++++++++++++++++++++++++++++++++
1 file changed, 36 insertions(+)
diff --git a/daemon/device-name-translation.c b/daemon/device-name-translation.c
index cfebc6495..74b8b686e 100644
--- a/daemon/device-name-translation.c
+++ b/daemon/device-name-translation.c
@@ -248,12 +248,17 @@ device_name_translation (const char *device)
return NULL;
}
+static char *reverse_btrfsvol (const char *device);
+
char *
reverse_device_name_translation (const char *device)
{
char *ret = NULL;
size_t i;
+ if (STRPREFIX (device, "btrfsvol:"))
+ return reverse_btrfsvol (device);
+
/* Look it up in the cache, and if found return the canonical name.
* If not found return a copy of the original string.
*/
@@ -287,3 +292,34 @@ reverse_device_name_translation (const char *device)
return ret;
}
+
+/* btrfsvol:/dev/sdX also needs reversing. */
+static char *
+reverse_btrfsvol (const char *device)
+{
+ const char prefix[] = "btrfsvol:";
+ const char *device_start, *device_end;
+ CLEANUP_FREE char *device_name = NULL;
+ CLEANUP_FREE char *reversed_device = NULL;
+ char *ret;
+
+ device_start = device + strlen (prefix);
+ device_end = strchr (device_start + strlen ("/dev/"), '/');
+ device_name = strndup (device_start, device_end - device_start);
+ if (device_name == NULL) {
+ reply_with_perror ("strndup");
+ return NULL;
+ }
+
+ reversed_device = reverse_device_name_translation (device_name);
+ if (reversed_device == NULL)
+ return NULL;
+
+ /* Construct the final btrfsvol: and return it, caller frees. */
+ if (asprintf (&ret, "%s%s%s", prefix, reversed_device, device_end) == -1) {
+ reply_with_perror ("asprintf");
+ return NULL;
+ }
+
+ return ret;
+}
--
2.47.3

View File

@ -1,87 +0,0 @@
From 8613ec938e6670bf2043eed7634a502e59e261a7 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Thu, 16 Apr 2026 08:06:50 +0100
Subject: [PATCH] daemon/listfs.ml: Refactor is_partition_can_hold_filesystem
Refactor and simplify the function.
This is just code motion, there is no functional change.
(cherry picked from commit 88bd07f350407619d4f5fe406c5594b50cf541dd)
---
daemon/listfs.ml | 53 ++++++++++++++++++++----------------------------
1 file changed, 22 insertions(+), 31 deletions(-)
diff --git a/daemon/listfs.ml b/daemon/listfs.ml
index 4c90796ef..067314c47 100644
--- a/daemon/listfs.ml
+++ b/daemon/listfs.ml
@@ -115,43 +115,34 @@ and is_not_partitioned_device device =
* Windows Snapshot Partition as well as MBR extended partitions.
*)
and is_partition_can_hold_filesystem partition =
- let device = Devsparts.part_to_dev partition in
- let partnum = Devsparts.part_to_partnum partition in
- let parttype = Parted.part_get_parttype device in
+ let device = Devsparts.part_to_dev partition
+ and partnum = Devsparts.part_to_partnum partition in
- let is_gpt = parttype = "gpt" in
- let is_mbr = parttype = "msdos" in
- let is_gpt_or_mbr = is_gpt || is_mbr in
+ match Parted.part_get_parttype device with
+ | "msdos" ->
+ if Parted.part_get_mbr_part_type device partnum = "extended" then
+ false
+ else if partnum = 1 && Utils.has_bogus_mbr device then
+ true
+ else
+ true
- if is_gpt_or_mbr then (
- if is_mbr_extended parttype device partnum then
- false
- else if is_mbr_bogus parttype device partnum then
- true
- else if is_mbr then
- true
- else (
- let gpt_type = Sfdisk.part_get_gpt_type device partnum in
- match gpt_type with
+ | "gpt" ->
+ let gpt_type = Sfdisk.part_get_gpt_type device partnum in
+ (match gpt_type with
(* Windows Logical Disk Manager metadata partition. *)
| "5808C8AA-7E8F-42E0-85D2-E1E90434CFB3"
- (* Windows Logical Disk Manager data partition. *)
- | "AF9B60A0-1431-4F62-BC68-3311714A69AD"
- (* Microsoft Reserved Partition. *)
- | "E3C9E316-0B5C-4DB8-817D-F92DF00215AE"
- (* Windows Snapshot Partition. *)
- | "CADDEBF1-4400-4DE8-B103-12117DCF3CCF" -> false
+ (* Windows Logical Disk Manager data partition. *)
+ | "AF9B60A0-1431-4F62-BC68-3311714A69AD"
+ (* Microsoft Reserved Partition. *)
+ | "E3C9E316-0B5C-4DB8-817D-F92DF00215AE"
+ (* Windows Snapshot Partition. *)
+ | "CADDEBF1-4400-4DE8-B103-12117DCF3CCF" -> false
| _ -> true
- )
- )
- else true
+ )
-and is_mbr_extended parttype device partnum =
- parttype = "msdos" &&
- Parted.part_get_mbr_part_type device partnum = "extended"
-
-and is_mbr_bogus parttype device partnum =
- parttype = "msdos" && partnum = 1 && Utils.has_bogus_mbr device
+ | _ -> (* unknown or other *)
+ true
(* Use vfs-type to check for a filesystem of some sort of [device].
* Appends (device, vfs_type) to the ret parameter (there may be
--
2.47.3

View File

@ -1,114 +0,0 @@
From 7349b7c5aa58b937394f5a44844238ff38e2f80d Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Thu, 16 Apr 2026 08:34:38 +0100
Subject: [PATCH] daemon/listfs.ml: Ignore CHS geometry error from parted
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
parted has an annoying bug where it fails to parse Sun partition
tables (still used by Veritas). It tries to check the CHS geometry
stored in the header matches the physical CHS geometry, which is a
meaningless test.
In function is_partition_can_hold_filesystem we are only interested in
MBR and GPT partition types, so catch and ignore this specific parted
error.
I tested this by adding a Sun disk as a second disk to a Fedora
guest and doing inspection. Previously the operation would
fail:
$ guestfish --ro -a /var/tmp/fedora-41.img -a /var/tmp/dump.img -i
libguestfs: error: inspect_os: parted exited with status 1: Warning:
The disk CHS geometry (205603,255,2) reported by the operating
system does not match the geometry stored on the disk label
(64887,16,101).
After this change it succeeds:
$ guestfish --ro -a /var/tmp/fedora-41.img -a /var/tmp/dump.img -i
Welcome to guestfish, the guest filesystem shell for
editing virtual machine filesystems and disk images.
Type: help for help on commands
man to read the manual
quit to quit the shell
Operating system: Fedora Linux 41 (Forty One)
/dev/sda3 mounted on /
/dev/sda2 mounted on /boot
Fixes: https://redhat.atlassian.net/browse/RHEL-165220
(cherry picked from commit 8d83bf2bcf31c5206b6deaa5e993009a85ff174f)
---
daemon/listfs.ml | 44 ++++++++++++++++++++++++++++----------------
1 file changed, 28 insertions(+), 16 deletions(-)
diff --git a/daemon/listfs.ml b/daemon/listfs.ml
index 067314c47..8ca5ca8a8 100644
--- a/daemon/listfs.ml
+++ b/daemon/listfs.ml
@@ -118,30 +118,42 @@ and is_partition_can_hold_filesystem partition =
let device = Devsparts.part_to_dev partition
and partnum = Devsparts.part_to_partnum partition in
- match Parted.part_get_parttype device with
- | "msdos" ->
- if Parted.part_get_mbr_part_type device partnum = "extended" then
- false
- else if partnum = 1 && Utils.has_bogus_mbr device then
- true
- else
- true
+ try
+ match Parted.part_get_parttype device with
+ | "msdos" ->
+ if Parted.part_get_mbr_part_type device partnum = "extended" then
+ false
+ else if partnum = 1 && Utils.has_bogus_mbr device then
+ true
+ else
+ true
- | "gpt" ->
- let gpt_type = Sfdisk.part_get_gpt_type device partnum in
- (match gpt_type with
- (* Windows Logical Disk Manager metadata partition. *)
- | "5808C8AA-7E8F-42E0-85D2-E1E90434CFB3"
+ | "gpt" ->
+ let gpt_type = Sfdisk.part_get_gpt_type device partnum in
+ (match gpt_type with
+ (* Windows Logical Disk Manager metadata partition. *)
+ | "5808C8AA-7E8F-42E0-85D2-E1E90434CFB3"
(* Windows Logical Disk Manager data partition. *)
| "AF9B60A0-1431-4F62-BC68-3311714A69AD"
(* Microsoft Reserved Partition. *)
| "E3C9E316-0B5C-4DB8-817D-F92DF00215AE"
(* Windows Snapshot Partition. *)
| "CADDEBF1-4400-4DE8-B103-12117DCF3CCF" -> false
- | _ -> true
- )
+ | _ -> true
+ )
- | _ -> (* unknown or other *)
+ | _ -> (* unknown or other *)
+ true
+
+ with
+ | Failure msg when String.find msg "CHS geometry" >= 0 ->
+ (* Parted has poor handling of "sun" partition types, always
+ * issuing a warning because the CHS doesn't match the physical
+ * geometry. Ignore this as we don't care about it in this
+ * function (RHEL-165220).
+ *)
+ eprintf "is_partition_can_hold_filesystem: \
+ ignoring warning from parted: %s\n%!" msg;
true
(* Use vfs-type to check for a filesystem of some sort of [device].
--
2.47.3

View File

@ -7,7 +7,7 @@ set -e
# it like this:
# ./copy-patches.sh
rhel_version=10.1
rhel_version=10.2
# Check we're in the right directory.
if [ ! -f libguestfs.spec ]; then

6
gating.yaml Executable file
View File

@ -0,0 +1,6 @@
--- !Policy
product_versions:
- rhel-*
decision_context: osci_compose_gate
rules:
- !PassingTestCaseRule {test_case_name: xen-ci.brew-build.tier1.functional}

View File

@ -1,17 +0,0 @@
-----BEGIN PGP SIGNATURE-----
iQJFBAABCgAvFiEE93dPsa0HSn6Mh2fqkXOPc+G3aKAFAmhQPkcRHHJpY2hAYW5u
ZXhpYS5vcmcACgkQkXOPc+G3aKBzGBAAnhwMRwtiYaw94nMMhZA+896zxzBDi6Wx
YhWiJ7wrlgx7qchP21Y+As4mz5ldDeiFFsFMXXuIBz2m+G73yM1EGbcwxYlpWV+h
bSBca5Vd14WFAVX497fWTzcz3UXAvkYmqLJqCliSJ4BdnCuTcCoKK4+sl/F0kwXe
F2x0YNaUxCMiuscFhmLIkz1r+RDUyuQfCeb+ilkkdsd+Gugq42CkW5kLxCfy/dn7
gSyj/oaYIaE2bsYW39EKXuJX1qb4DlmwZa8JUsYsi9uZGQbwH+gka9XkKVvbSd6Q
s7q68xl3DIna7rfIrnLdf/OGcwmPQ0U02Yhfam7tM/4FY2t5hBOSCahBSrZVfuQt
Q/QXpXiYzoD/nmgUNHgFqgN1kgm22E/qaqwwbKf6k412NDfF+Ez84sZHCcgJH+LR
/eJrp7lde74QxGervdQ5dYmwNBuv5IPrRJA2kWv97wVDqaOlMgpjpmYNTFyxppZm
9H2NyI8x+jSZ9KQT0vEjIWpe3Yr5l0zUkLLzLoD7t0DcxTZGVIeHV7n8ITLlK0dH
Ki/BmkWqs2p39Izv7IWWBlRV/URVxW5zK4zEvRtTQtFidlaRqAy5oWUrARnmO4Te
sT7Y7ZQss9ZJkcz3JF9lh27JCvD4aRW62y4n2zreq7FXPA67YjbsMJ43BLQ7Gek2
I0nROw3mmHw=
=OxmC
-----END PGP SIGNATURE-----

BIN
libguestfs.keyring Normal file

Binary file not shown.

View File

@ -13,14 +13,7 @@ ExcludeArch: %{ix86}
# we only do a sanity check that kernel/qemu/libvirt/appliance is not
# broken. To perform the full test suite, see instructions here:
# https://www.redhat.com/archives/libguestfs/2015-September/msg00078.html
%if !0%{?rhel}
%global test_arches aarch64 %{power64} s390x x86_64
%else
# RHEL 9 only:
# x86-64: "/lib64/libc.so.6: CPU ISA level is lower than required"
# (RHBZ#1919389)
%global test_arches NONE
%endif
# Trim older changelog entries.
# https://lists.fedoraproject.org/pipermail/devel/2013-April/thread.html#181627
@ -30,7 +23,7 @@ ExcludeArch: %{ix86}
%global verify_tarball_signature 1
# The source directory.
%global source_directory 1.56-stable
%global source_directory 1.58-stable
# Filter perl provides.
%{?perl_default_filter}
@ -41,8 +34,8 @@ ExcludeArch: %{ix86}
Summary: Access and modify virtual machine disk images
Name: libguestfs
Epoch: 1
Version: 1.56.1
Release: 6%{?dist}.alma.1
Version: 1.58.1
Release: 2%{?dist}.alma.1
License: LGPL-2.1-or-later
# Build only for architectures that have a kernel
@ -79,31 +72,20 @@ Source7: libguestfs.keyring
Source8: copy-patches.sh
# Patches are maintained in the following repository:
# https://github.com/libguestfs/libguestfs/commits/rhel-10.1
# https://github.com/libguestfs/libguestfs/commits/rhel-10.2
# Patches.
Patch0001: 0001-appliance-Ignore-sit0-network-device-in-the-guest.patch
Patch0002: 0002-lib-libvirt-Debug-error-from-virDomainDestroyFlags.patch
Patch0003: 0003-lib-libvirt-Sleep-before-retrying-virDomainDestroyFl.patch
Patch0004: 0004-daemon-Add-contents-of-etc-fstab-to-verbose-log.patch
Patch0005: 0005-appliance-init-Add-lsblk-and-blkid-output-to-verbose.patch
Patch0006: 0006-docs-Fix-dead-ntfs-3g-doc-links.patch
Patch0007: 0007-daemon-inspect-check-etc-crypttab-for-dev-mapper.patch
Patch0008: 0008-daemon-sysroot-Avoid-double-when-creating-sysroot-pa.patch
Patch0009: 0009-daemon-sysroot-Avoid-copying-the-path-every-time-we-.patch
Patch0010: 0010-daemon-Reimplement-guestfs_selinux_relabel-in-OCaml.patch
Patch0011: 0011-generator-Implement-StringList-for-OCaml-functions.patch
Patch0012: 0012-generator-Allow-StringList-Pathname-parameters.patch
Patch0013: 0013-daemon-Deprecate-guestfs_selinux_relabel-replace-wit.patch
Patch0014: 0014-daemon-inspect_fs_windows.ml-Add-debugging-for-MBR-d.patch
Patch0015: 0015-daemon-inspect_fs_windows.ml-Add-debugging-when-we-s.patch
Patch0016: 0016-daemon-inspect_fs_windows.ml-Ignore-blank-disks-in-d.patch
Patch0017: 0017-RHEL-Disable-unsupported-remote-drive-protocols-RHBZ.patch
Patch0018: 0018-RHEL-Reject-use-of-libguestfs-winsupport-features-ex.patch
Patch0019: 0019-RHEL-appliance-init-Run-depmod-a-to-rebuild-kernel-m.patch
Patch0020: 0020-daemon-device-name-translation.c-Fix-btrfs-volume-re.patch
Patch0021: 0021-daemon-listfs.ml-Refactor-is_partition_can_hold_file.patch
Patch0022: 0022-daemon-listfs.ml-Ignore-CHS-geometry-error-from-part.patch
Patch0001: 0001-generator-daemon.ml-Avoid-not-available-macro-for-OC.patch
Patch0002: 0002-RHEL-Disable-unsupported-remote-drive-protocols-RHBZ.patch
Patch0003: 0003-RHEL-Reject-use-of-libguestfs-winsupport-features-ex.patch
Patch0004: 0004-RHEL-appliance-init-Run-depmod-a-to-rebuild-kernel-m.patch
Patch0005: 0005-lib-qemu.c-Use-machine-type-none-when-inspecting-QMP.patch
Patch0006: 0006-generator-Fix-description-of-xfs_growfs.patch
Patch0007: 0007-generator-Fix-description-of-xfs_info.patch
Patch0008: 0008-New-API-xfs_info2.patch
Patch0009: 0009-daemon-Reimplement-xfs_info-using-xfs_info2.patch
Patch0010: 0010-generator-Deprecate-xfs_info-replaced-by-xfs_info2.patch
Patch0011: 0011-tests-disks-debug-qemu.sh-Fix-test-for-update-QMP-te.patch
BuildRequires: autoconf, automake, libtool, gettext-devel
@ -130,7 +112,7 @@ BuildRequires: libselinux-utils
BuildRequires: libselinux-devel
BuildRequires: fuse, fuse-devel
BuildRequires: pcre2-devel
BuildRequires: libvirt-devel
BuildRequires: libvirt-devel >= 11.10.0
BuildRequires: gperf
BuildRequires: rpm-devel
BuildRequires: cpio
@ -142,7 +124,7 @@ BuildRequires: unzip
BuildRequires: systemd-units
BuildRequires: netpbm-progs
BuildRequires: icoutils
BuildRequires: libvirt-daemon-kvm >= 7.1.0
BuildRequires: libvirt-daemon-kvm
%if !0%{?rhel}
BuildRequires: perl(Expect)
%endif
@ -154,12 +136,15 @@ BuildRequires: libldm-devel
BuildRequires: json-c-devel
BuildRequires: systemd-devel
BuildRequires: bash-completion
%if 0%{?fedora} || 0%{?rhel} >= 11
BuildRequires: bash-completion-devel
%endif
BuildRequires: /usr/bin/ping
BuildRequires: curl
BuildRequires: xz
BuildRequires: zstd
BuildRequires: libzstd-devel
BuildRequires: /usr/bin/qemu-img
BuildRequires: qemu-img >= 7.2.0
%if 0%{verify_tarball_signature}
BuildRequires: gnupg2
@ -316,7 +301,7 @@ Requires: systemd-libs%{?_isa}
Requires: fuse
# For core APIs:
Requires: /usr/bin/qemu-img
Requires: qemu-img
Requires: coreutils
Requires: grep
Requires: tar
@ -325,7 +310,7 @@ Requires: tar
Requires: xz
# For qemu direct and libvirt backends.
Requires: qemu-kvm-core
Requires: qemu-kvm-core >= 7.2.0
%if !0%{?rhel}
Suggests: qemu-block-curl
Suggests: qemu-block-iscsi
@ -335,10 +320,10 @@ Suggests: qemu-block-rbd
Suggests: qemu-block-ssh
%endif
Recommends: libvirt-daemon-config-network
Requires: libvirt-daemon-driver-qemu >= 7.1.0
Requires: libvirt-daemon-driver-qemu >= 11.10.0
Requires: libvirt-daemon-driver-secret
Requires: libvirt-daemon-driver-storage-core
Recommends: passt
Requires: passt
Requires: (selinux-policy >= 3.11.1-63 if selinux-policy)
%ifarch aarch64
@ -783,6 +768,15 @@ if ! make quickcheck QUICKCHECK_TEST_TOOL_ARGS="-t 1200"; then
cat $HOME/.cache/libvirt/qemu/log/*
exit 1
fi
# As libvirt is the default backend, test that the direct backend
# works too. It's a good place to get test coverage across all the
# architectures.
if ! LIBGUESTFS_BACKEND=direct \
make quickcheck QUICKCHECK_TEST_TOOL_ARGS="-t 1200"; then
cat $HOME/.cache/libvirt/qemu/log/*
exit 1
fi
%endif
@ -974,6 +968,18 @@ rm ocaml/html/.gitignore
%files bash-completion
%if 0%{?fedora} || 0%{?rhel} >= 11
%dir %{bash_completions_dir}
%{bash_completions_dir}/guestfish
%{bash_completions_dir}/guestmount
%{bash_completions_dir}/guestunmount
%{bash_completions_dir}/libguestfs-test-tool
%{bash_completions_dir}/virt-copy-in
%{bash_completions_dir}/virt-copy-out
%{bash_completions_dir}/virt-rescue
%{bash_completions_dir}/virt-tar-in
%{bash_completions_dir}/virt-tar-out
%else
%dir %{_datadir}/bash-completion/completions
%{_datadir}/bash-completion/completions/guestfish
%{_datadir}/bash-completion/completions/guestmount
@ -984,6 +990,7 @@ rm ocaml/html/.gitignore
%{_datadir}/bash-completion/completions/virt-rescue
%{_datadir}/bash-completion/completions/virt-tar-in
%{_datadir}/bash-completion/completions/virt-tar-out
%endif
%files -n ocaml-%{name}
@ -1090,16 +1097,25 @@ rm ocaml/html/.gitignore
%changelog
* Wed May 06 2026 Eduard Abdullin <eabdullin@almalinux.org> - 1:1.56.1-6.alma.1
* Tue Jan 27 2026 Eduard Abdullin <eabdullin@almalinux.org> - 1:1.58.1-2.alma.1
- Enable building for ppc64le
* Fri Apr 17 2026 Richard W.M. Jones <rjones@redhat.com> - 1:1.56.1-6
- Fix CHS geometry error for Veritas/Sun partitions
resolves: RHEL-169224
* Mon Mar 09 2026 Richard W.M. Jones <rjones@redhat.com> - 1:1.56.1-4
- Fix btrfs volume reverse translation
resolves: RHEL-149119
* Mon Jan 26 2026 Richard W.M. Jones <rjones@redhat.com> - 1:1.58.1-2
- Rebase to libguestfs 1.58.1
resolves: RHEL-111240
- Synchronize spec file with Fedora
- Add new libguestfs ntfs_chmod API
resolves: RHEL-113833
- Use setfiles -A option if available to reduce memory usage
resolves: RHEL-114292
- Add -cpu max when testing for KVM via QMP
resolves: RHEL-121076
- Require passt
resolves: RHEL-122315
- Add AV and GPOs to inspection info
resolves: RHEL-125846
- Add new xfs_info2 API
resolves: RHEL-143673
* Thu Aug 14 2025 Richard W.M. Jones <rjones@redhat.com> - 1:1.56.1-3
- Rebase to libguestfs 1.56.1

View File

@ -1,2 +1,2 @@
SHA512 (libguestfs-1.56.1.tar.gz) = 8ec8db8b3de7471c7ab77161fa98349d7b6f88a803ab563f1859606a2ef55737f323b1cf3ef2ebb3055770f4140aabb056f97099ef76fa7ad0f7bd792cc699fc
SHA512 (libguestfs.keyring) = 297a15edc7c220222b9f650e0a9361ae132d3f0fed04aeb2237a1d9c3f6dac6f336846434f66480faed72635a33f659e849b052e74b88d1508aeff03f8c9a2ac
SHA512 (libguestfs-1.58.1.tar.gz) = 56e8f21592b4c63a14cfaa3255180c3d83bea7d3cc1a404befb121513b7287dd09ae96477b80b3f8794b2cde49febb1909b9716c3b896a313e4445f7f2a5072a
SHA512 (libguestfs-1.58.1.tar.gz.sig) = 9d8bca8928bfa360d0d307373676ea587af9e5154ddfd954c70283f3125b397df9294c1504e188bea5a5cfbb4bae9f27cdbacf92bc9a3e8cb05b7c900c0bfac5

View File

@ -0,0 +1,10 @@
#!/bin/bash -
set -e
set -x
# This only makes sure that libguestfs isn't totally broken.
# Fix libvirt.
systemctl restart virtqemud virtsecretd virtstoraged virtnetworkd
libguestfs-test-tool

14
tests/tests.yml Normal file
View File

@ -0,0 +1,14 @@
# https://fedoraproject.org/wiki/CI/Tests
# https://fedoraproject.org/wiki/CI/Standard_Test_Roles
- hosts: localhost
roles:
- role: standard-test-basic
tags:
- classic
required_packages:
- libguestfs
- libvirt-daemon-kvm
tests:
- libguestfs-test-tool:
dir: .
run: ./run-libguestfs-test-tool.sh