diff --git a/.gitignore b/.gitignore index 50d4b8f..529d8a1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ -SOURCES/guestfs-tools-1.52.2.tar.gz -SOURCES/libguestfs.keyring +guestfs-tools-1.54.0.tar.gz +libguestfs.keyring diff --git a/.guestfs-tools.metadata b/.guestfs-tools.metadata deleted file mode 100644 index f247829..0000000 --- a/.guestfs-tools.metadata +++ /dev/null @@ -1,2 +0,0 @@ -6eb02f4f70cb224548567ce7e4b67712c8ebb643 SOURCES/guestfs-tools-1.52.2.tar.gz -1bbc40f501a7fef9eef2a39b701a71aee2fea7c4 SOURCES/libguestfs.keyring diff --git a/0001-docs-Move-release-note-about-GNU-gettext-to-build-se.patch b/0001-docs-Move-release-note-about-GNU-gettext-to-build-se.patch new file mode 100644 index 0000000..7cf17b4 --- /dev/null +++ b/0001-docs-Move-release-note-about-GNU-gettext-to-build-se.patch @@ -0,0 +1,34 @@ +From 3a99e87bdee6c20a6286aa3e10bb2c2d896fb2ed Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Wed, 21 May 2025 15:02:44 +0100 +Subject: [PATCH] docs: Move release note about GNU gettext to build section + +Fixes: commit 9b7410c220f1111e6acef88efbbaee3fea4019b8 +Updates: commit 80ce443e5eb7323e9b7b5cef30b8f7673ca51d67 +(cherry picked from commit bf4fef4f2b45a2016c42094f8e4fe18abc8d90e2) +--- + docs/guestfs-tools-release-notes-1.54.pod | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/docs/guestfs-tools-release-notes-1.54.pod b/docs/guestfs-tools-release-notes-1.54.pod +index b44eb19a2..ab9062303 100644 +--- a/docs/guestfs-tools-release-notes-1.54.pod ++++ b/docs/guestfs-tools-release-notes-1.54.pod +@@ -78,6 +78,8 @@ Some deprecated autoconf macros are no longer used. + + Fix some issues when building on macOS (Mohamed Akram). + ++Fix compatibility with GNU gettext 0.25. ++ + =head2 Internals + + The tests were modified to use a set of common functions and remove +@@ -87,8 +89,6 @@ nbdkit. + Some internal OCaml List and String functions that we used have been + replaced by ones from the OCaml stdlib, reducing code maintenance. + +-Fix compatibility with GNU gettext 0.25. +- + =head2 Bugs fixed + + =begin comment diff --git a/SOURCES/0010-builder-Update-link-to-templates-to-use-https.patch b/0002-builder-Update-link-to-templates-to-use-https.patch similarity index 92% rename from SOURCES/0010-builder-Update-link-to-templates-to-use-https.patch rename to 0002-builder-Update-link-to-templates-to-use-https.patch index 5a06966..842b604 100644 --- a/SOURCES/0010-builder-Update-link-to-templates-to-use-https.patch +++ b/0002-builder-Update-link-to-templates-to-use-https.patch @@ -1,4 +1,4 @@ -From a0fbbc98f5fb069bcbf15a5a8ec51e4c778a2bd5 Mon Sep 17 00:00:00 2001 +From 2eae3778a32b18944a6a561e10a35eae74fa11f0 Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Tue, 3 Jun 2025 08:29:57 +0100 Subject: [PATCH] builder: Update link to templates to use https diff --git a/0003-builder-Replace-cpu-host-with-cpu-max-in-example.patch b/0003-builder-Replace-cpu-host-with-cpu-max-in-example.patch new file mode 100644 index 0000000..01c4e2e --- /dev/null +++ b/0003-builder-Replace-cpu-host-with-cpu-max-in-example.patch @@ -0,0 +1,32 @@ +From b6401279c65497506eac2eaf61470bf6f49e89e7 Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Mon, 16 Jun 2025 21:47:41 +0100 +Subject: [PATCH] builder: Replace -cpu host with -cpu max in example + +When KVM isn't present, some versions of qemu may print: + + qemu-system-x86_64: Could not access KVM kernel module: No such file or directory + qemu-system-x86_64: failed to initialize kvm: No such file or directory + qemu-system-x86_64: falling back to tcg + qemu-system-x86_64: CPU model 'host' requires KVM or HVF + +Use -cpu max instead which should work in both cases. + +(cherry picked from commit 2e93abca5acaa69cd6fd08b70079e8f432539076) +--- + builder/virt-builder.pod | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/builder/virt-builder.pod b/builder/virt-builder.pod +index 05bef1e05..ff0ec250c 100644 +--- a/builder/virt-builder.pod ++++ b/builder/virt-builder.pod +@@ -1043,7 +1043,7 @@ following could be used to boot the virtual machine: + + qemu-system-x86_64 \ + -machine accel=kvm:tcg \ +- -cpu host \ ++ -cpu max \ + -m 2048 \ + -drive file=disk.img,format=raw,if=virtio + diff --git a/0004-customize-Fixes-for-selinux-relabelling-and-Windows-.patch b/0004-customize-Fixes-for-selinux-relabelling-and-Windows-.patch new file mode 100644 index 0000000..88bb660 --- /dev/null +++ b/0004-customize-Fixes-for-selinux-relabelling-and-Windows-.patch @@ -0,0 +1,143 @@ +From 6a1e751d4b10ae1833ee18c0796306711e842e76 Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Wed, 13 Aug 2025 16:51:39 +0100 +Subject: [PATCH] customize: Fixes for selinux relabelling and Windows + firstboot + +This updates the common submodule to add the fixes below. These +changes allow SELinux relabelling to work correctly on Linux split- +/usr configurations, and allow Windows firstboot scripts to be +deferred until after a reboot. + +The SELinux relabelling change requires libguestfs >= 1.57.1 (for the +new guestfs_setfiles API). + + Richard W.M. Jones (4): + mlstdutils: Add List.combine4 function + mlcustomize/SELinux_relabel.ml: Add comment + mlcustomize/SELinux_relabel.ml: Use new guestfs_setfiles API + mlcustomize/SELinux_relabel.ml: Relabel every mountpoint + + Vadim Rozenfeld (1): + Modify the firstboot script to check the scripts execution return status + +Fixes: https://issues.redhat.com/browse/RHEL-108174 +Related: https://issues.redhat.com/browse/RHEL-100682 +(cherry picked from commit ea0f9cf0743c3e50a996a9d7ec488d58a9312b11) +--- + common | 2 +- + m4/guestfs-libraries.m4 | 4 ++-- + 2 files changed, 3 insertions(+), 3 deletions(-) + +Submodule common d4a81e9dd..89f1eb2d3: +diff --git a/common/mlcustomize/SELinux_relabel.ml b/common/mlcustomize/SELinux_relabel.ml +index 2f3a09bf7..f1729e3f4 100644 +--- a/common/mlcustomize/SELinux_relabel.ml ++++ b/common/mlcustomize/SELinux_relabel.ml +@@ -1,5 +1,5 @@ + (* virt-customize +- * Copyright (C) 2016 Red Hat Inc. ++ * Copyright (C) 2016-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 +@@ -24,6 +24,10 @@ open Printf + + module G = Guestfs + ++(* XXX A lot of this code could usefully be moved into ++ * [libguestfs.git/daemon/selinux.ml]. ++ *) ++ + let rec relabel (g : G.guestfs) = + (* Is the guest using SELinux? (Otherwise this is a no-op). *) + if is_selinux_guest g then ( +@@ -109,5 +113,13 @@ and use_setfiles g = + g#copy_attributes ~all:true old_specfile specfile + ); + ++ (* Get the list of mountpoints, since setfiles does not cross ++ * filesystems (RHEL-108174). ++ *) ++ let mps = g#mountpoints () |> ++ List.map snd |> (* the list of directories *) ++ List.sort compare |> (* sort them for consistency *) ++ Array.of_list in ++ + (* Relabel everything. *) +- g#selinux_relabel ~force:true specfile "/" ++ g#setfiles ~force:true specfile mps +diff --git a/common/mlcustomize/firstboot.ml b/common/mlcustomize/firstboot.ml +index 6aca4c34a..5f2642b06 100644 +--- a/common/mlcustomize/firstboot.ml ++++ b/common/mlcustomize/firstboot.ml +@@ -305,13 +305,19 @@ if not exist \"%%scripts_done%%\" ( + :: Pick the next script to run. + for %%%%f in (\"%%scripts%%\"\\*.bat) do ( + echo running \"%%%%f\" +- move \"%%%%f\" \"%%scripts_done%%\" +- pushd \"%%scripts_done%%\" ++ pushd \"%%scripts%%\" + call \"%%%%~nf\" + set elvl=!errorlevel! + echo .... exit code !elvl! + popd + ++ if !elvl! NEQ 249 ( ++ echo Script succeeded, moving to scripts-done ++ move \"%%%%f\" \"%%scripts_done%%\" ++ ) else ( ++ echo Script failed, will retry on next boot ++ ) ++ + :: Reboot the computer. This is necessary to free any locked + :: files which may prevent later scripts from running. + shutdown /r /t 0 /y +diff --git a/common/mlstdutils/std_utils.ml b/common/mlstdutils/std_utils.ml +index 4850a5598..16032d992 100644 +--- a/common/mlstdutils/std_utils.ml ++++ b/common/mlstdutils/std_utils.ml +@@ -80,6 +80,12 @@ module List = struct + | x::xs, y::ys, z::zs -> (x, y, z) :: combine3 xs ys zs + | _ -> invalid_arg "combine3" + ++ let rec combine4 ws xs ys zs = ++ match ws, xs, ys, zs with ++ | [], [], [], [] -> [] ++ | w::ws, x::xs, y::ys, z::zs -> (w, x, y, z) :: combine4 ws xs ys zs ++ | _ -> invalid_arg "combine4" ++ + let rec assoc_lbl ?(cmp = Stdlib.compare) ~default x = function + | [] -> default + | (y, y') :: _ when cmp x y = 0 -> y' +diff --git a/common/mlstdutils/std_utils.mli b/common/mlstdutils/std_utils.mli +index fe6bf1a7c..a20e720c2 100644 +--- a/common/mlstdutils/std_utils.mli ++++ b/common/mlstdutils/std_utils.mli +@@ -106,6 +106,11 @@ module List : sig + (** Like {!List.combine} but for triples. + All lists must be the same length. *) + ++ val combine4 : 'a list -> 'b list -> 'c list -> 'd list -> ++ ('a * 'b * 'c * 'd) list ++ (** Like {!List.combine} but for 4-tuples. ++ All lists must be the same length. *) ++ + val assoc_lbl : ?cmp:('a -> 'a -> int) -> default:'b -> 'a -> ('a * 'b) list -> 'b + (** Like {!assoc} but with a user-defined comparison function, and + instead of raising [Not_found], it returns the [~default] value. *) +diff --git a/m4/guestfs-libraries.m4 b/m4/guestfs-libraries.m4 +index c9fbf58b2..82e62d54f 100644 +--- a/m4/guestfs-libraries.m4 ++++ b/m4/guestfs-libraries.m4 +@@ -19,8 +19,8 @@ dnl Any C libraries required by the libguestfs C library (not the daemon). + + dnl Of course we need libguestfs. + dnl +-dnl We need libguestfs 1.55.6 for guestfs_sh_out. +-PKG_CHECK_MODULES([LIBGUESTFS], [libguestfs >= 1.55.6]) ++dnl We need libguestfs 1.57.1 for guestfs_setfiles. ++PKG_CHECK_MODULES([LIBGUESTFS], [libguestfs >= 1.57.1]) + printf "libguestfs version is "; $PKG_CONFIG --modversion libguestfs + + dnl Test if it's GNU or XSI strerror_r. diff --git a/0005-Update-common-submodule.patch b/0005-Update-common-submodule.patch new file mode 100644 index 0000000..a4f49aa --- /dev/null +++ b/0005-Update-common-submodule.patch @@ -0,0 +1,122 @@ +From a01095308d22a5ab4d5858dfe0ee46f0b1bec28c Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Wed, 20 Aug 2025 10:34:24 +0100 +Subject: [PATCH] Update common submodule + +Pulls in this fix which should have no effect: + + Richard W.M. Jones (1): + mlcustomize/firstboot.ml: Use quoted string literals for firstboot + +(cherry picked from virt-v2v commit b7aadeac02ef326d542cd83e441b5a9d99ced793) + +(cherry picked from commit 038b30fb08107487cdba4bc0dbabb33dbf75f4c3) +--- + common | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +Submodule common 89f1eb2d3..7ecf3992b: +diff --git a/common/mlcustomize/firstboot.ml b/common/mlcustomize/firstboot.ml +index 5f2642b06..360c33d67 100644 +--- a/common/mlcustomize/firstboot.ml ++++ b/common/mlcustomize/firstboot.ml +@@ -35,8 +35,7 @@ let sanitize_name = + module Linux = struct + let firstboot_dir = "/usr/lib/virt-sysprep" + +- let firstboot_sh = sprintf "\ +-#!/bin/sh - ++ let firstboot_sh = sprintf {|#!/bin/sh - + + ### BEGIN INIT INFO + # Provides: virt-sysprep +@@ -57,14 +56,14 @@ d=%s/scripts + d_done=%s/scripts-done + logfile=~root/virt-sysprep-firstboot.log + +-echo \"$0\" \"$@\" 2>&1 | tee -a $logfile +-echo \"Scripts dir: $d\" 2>&1 | tee -a $logfile ++echo "$0" "$@" 2>&1 | tee -a $logfile ++echo "Scripts dir: $d" 2>&1 | tee -a $logfile + +-if test \"$1\" = \"start\" ++if test "$1" = "start" + then + mkdir -p $d_done + for f in $d/* ; do +- if test -x \"$f\" ++ if test -x "$f" + then + # move the script to the 'scripts-done' directory, so it is not + # executed again at the next boot +@@ -75,7 +74,7 @@ then + done + rm -f $d_done/* + fi +-" firstboot_dir firstboot_dir ++|} firstboot_dir firstboot_dir + + let systemd_target = "multi-user.target" + +@@ -282,38 +281,37 @@ module Windows = struct + * XXX It would be better to use powershell here. For some ideas see + * https://github.com/HCK-CI/HLK-Setup-Scripts/ + *) +- let firstboot_script = sprintf "\ +-@echo off ++ let firstboot_script = sprintf {|@echo off + + setlocal EnableDelayedExpansion + set firstboot=%s +-set log=%%firstboot%%\\log.txt ++set log=%%firstboot%%\log.txt + +-set scripts=%%firstboot%%\\scripts +-set scripts_done=%%firstboot%%\\scripts-done ++set scripts=%%firstboot%%\scripts ++set scripts_done=%%firstboot%%\scripts-done + +-call :main >> \"%%log%%\" 2>&1 ++call :main >> "%%log%%" 2>&1 + exit /b + + :main + echo starting firstboot service + +-if not exist \"%%scripts_done%%\" ( +- mkdir \"%%scripts_done%%\" ++if not exist "%%scripts_done%%" ( ++ mkdir "%%scripts_done%%" + ) + + :: Pick the next script to run. +-for %%%%f in (\"%%scripts%%\"\\*.bat) do ( +- echo running \"%%%%f\" +- pushd \"%%scripts%%\" +- call \"%%%%~nf\" ++for %%%%f in ("%%scripts%%"\*.bat) do ( ++ echo running "%%%%f" ++ pushd "%%scripts%%" ++ call "%%%%~nf" + set elvl=!errorlevel! + echo .... exit code !elvl! + popd + + if !elvl! NEQ 249 ( + echo Script succeeded, moving to scripts-done +- move \"%%%%f\" \"%%scripts_done%%\" ++ move "%%%%f" "%%scripts_done%%" + ) else ( + echo Script failed, will retry on next boot + ) +@@ -329,8 +327,8 @@ for %%%%f in (\"%%scripts%%\"\\*.bat) do ( + + :: Fallthrough here if there are no scripts. + echo uninstalling firstboot service +-\"%%firstboot%%\\%s\" -s firstboot uninstall +-" firstboot_dir_win srvany in ++"%%firstboot%%\%s" -s firstboot uninstall ++|} firstboot_dir_win srvany in + + g#write (firstboot_dir // "firstboot.bat") + (String.unix2dos firstboot_script); diff --git a/0006-.gitignore-Ignore-.bak-files.patch b/0006-.gitignore-Ignore-.bak-files.patch new file mode 100644 index 0000000..60acd93 --- /dev/null +++ b/0006-.gitignore-Ignore-.bak-files.patch @@ -0,0 +1,22 @@ +From 9ee5fdf8146231087a461f4cddc0280db762c2f4 Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Mon, 1 Sep 2025 17:33:07 +0100 +Subject: [PATCH] .gitignore: Ignore *.bak files + +(cherry picked from commit cd5ea5cd63a11bc85077e6296021bd25b3c84662) +--- + .gitignore | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/.gitignore b/.gitignore +index 9c2e200e6..94f77ac74 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -2,6 +2,7 @@ + *.1 + *.a + *.annot ++*.bak + *.cmi + *.cmo + *.cmx diff --git a/0007-builder-sysprep-Use-quoted-string-literals-in-a-few-.patch b/0007-builder-sysprep-Use-quoted-string-literals-in-a-few-.patch new file mode 100644 index 0000000..1aac5de --- /dev/null +++ b/0007-builder-sysprep-Use-quoted-string-literals-in-a-few-.patch @@ -0,0 +1,206 @@ +From 0074de5f6eccb75dad814527813eba3524bb22e0 Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Mon, 1 Sep 2025 18:41:24 +0100 +Subject: [PATCH] builder, sysprep: Use quoted string literals in a few places + +This change was automated using the script in: +https://github.com/libguestfs/libguestfs/commit/02b64d5cec1ac0a6f7627d5489996de4474a67f6 + +However I manually only picked a few of the changes suggested +by the script. + +(cherry picked from commit 624df48b3d14d2e5420d758a425bcae509cfe318) +--- + builder/templates/make-template.ml | 62 ++++++++++++++--------------- + sysprep/sysprep_operation_script.ml | 5 ++- + 2 files changed, 33 insertions(+), 34 deletions(-) + +diff --git a/builder/templates/make-template.ml b/builder/templates/make-template.ml +index 73a55bd45..515c3da8b 100755 +--- a/builder/templates/make-template.ml ++++ b/builder/templates/make-template.ml +@@ -535,8 +535,7 @@ and make_kickstart_common ks_filename os arch = + | _ -> bpf "install\n"; + ); + +- bpf "\ +-text ++ bpf {|text + reboot + lang en_US.UTF-8 + keyboard us +@@ -544,7 +543,7 @@ network --bootproto dhcp + rootpw builder + firewall --enabled --ssh + timezone --utc America/New_York +-"; ++|}; + + (match os with + | RHEL (ver, _) when ver <= 4 -> +@@ -586,19 +585,19 @@ mouse generic + | CentOS ((3|4|5|6) as major, _) | RHEL ((3|4|5|6) as major, _) -> + let bootfs = if major <= 5 then "ext2" else "ext4" in + let rootfs = if major <= 4 then "ext3" else "ext4" in +- bpf "\ ++ bpf {| + zerombr + clearpart --all --initlabel + part /boot --fstype=%s --size=512 --asprimary + part swap --size=1024 --asprimary + part / --fstype=%s --size=1024 --grow --asprimary +-" bootfs rootfs; ++|} bootfs rootfs; + | Alma _ | CentOS _ | CentOSStream _ | RHEL _ | Fedora _ -> +- bpf "\ ++ bpf {| + zerombr + clearpart --all --initlabel --disklabel=gpt + autopart --type=plain +-"; ++|}; + | _ -> assert false (* cannot happen, see caller *) + ); + bpf "\n"; +@@ -753,16 +752,16 @@ and make_unattend_iso os arch = + * file called \Windows\Panther\Setupact.log (NB: + * not \Windows\Setupact.log) + *) +- fprintf chan " +- +- +- ++ fprintf chan {| ++ ++ ++ + + true + +@@ -772,18 +771,18 @@ and make_unattend_iso os arch = + + + +- ++ + 0 + true + + +- ++ + 1 + Primary + 300 + + +- ++ + 2 + Primary + true +@@ -791,7 +790,7 @@ and make_unattend_iso os arch = + + + +- ++ + 1 + 1 + +@@ -799,7 +798,7 @@ and make_unattend_iso os arch = + true + + +- ++ + 2 + 2 + +@@ -828,11 +827,11 @@ and make_unattend_iso os arch = + + + +- ++ + + en-US + +@@ -841,7 +840,7 @@ and make_unattend_iso os arch = + en-US + + +-" ++|} + arch product_key arch; + close_out chan; + +@@ -1411,14 +1410,13 @@ baseurl=%s + enabled=0 + gpgcheck=0 + keepcache=0 +-" major major baseurl major major srpms; ++|} major major baseurl major major srpms; + + (match optional with + | None -> () + | Some (name, optionalbaseurl, optionalsrpms) -> + let lc_name = String.lowercase_ascii name in +- bpf "\ +- ++ bpf {| + [rhel%d-%s] + name=RHEL %d Server %s + baseurl=%s +@@ -1432,7 +1430,7 @@ baseurl=%s + enabled=0 + gpgcheck=0 + keepcache=0 +-" major lc_name major lc_name optionalbaseurl ++|} major lc_name major lc_name optionalbaseurl + major lc_name major lc_name optionalsrpms + ) + ) else ( +diff --git a/sysprep/sysprep_operation_script.ml b/sysprep/sysprep_operation_script.ml +index fff5e4d51..a91cea26b 100644 +--- a/sysprep/sysprep_operation_script.ml ++++ b/sysprep/sysprep_operation_script.ml +@@ -81,7 +81,7 @@ let rec script_perform (g : Guestfs.guestfs) root side_effects = + and run_scripts mp scripts = + let sh = "/bin/bash" in + let cmd = +- sprintf "\ ++ sprintf {| + set -e + #set -x + cleanup () +@@ -91,7 +91,8 @@ cleanup () + guestunmount %s ||: + exit $status + } +-trap cleanup INT TERM QUIT EXIT ERR\n" ++trap cleanup INT TERM QUIT EXIT ERR ++|} + (Filename.quote mp) ^ + String.concat "\n" scripts in + let args = [| sh; "-c"; cmd |] in diff --git a/0008-builder-templates-make-template.ml-Fix-quoting.patch b/0008-builder-templates-make-template.ml-Fix-quoting.patch new file mode 100644 index 0000000..88ef43e --- /dev/null +++ b/0008-builder-templates-make-template.ml-Fix-quoting.patch @@ -0,0 +1,26 @@ +From cde27ebd4775cfa9a23a53a48b47303557bcb537 Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Tue, 2 Sep 2025 09:05:27 +0100 +Subject: [PATCH] builder/templates/make-template.ml: Fix quoting + +Fix mistake introduced by manual editing of the previous commit. + +Fixes: commit 624df48b3d14d2e5420d758a425bcae509cfe318 +(cherry picked from commit 7e63b2142682582ec87bdd8c0d98bb22fcc0dabf) +--- + builder/templates/make-template.ml | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/builder/templates/make-template.ml b/builder/templates/make-template.ml +index 515c3da8b..a69d3ad3f 100755 +--- a/builder/templates/make-template.ml ++++ b/builder/templates/make-template.ml +@@ -1394,7 +1394,7 @@ and make_rhel_yum_conf major minor arch = + sprintf "%s/AppStream/source/tree" topurl) + | _ -> assert false in + +- bpf "\ ++ bpf {| + # Yum configuration pointing to Red Hat servers. + + [rhel%d] diff --git a/0009-builder-templates-make-template.ml-Use-quoted-string.patch b/0009-builder-templates-make-template.ml-Use-quoted-string.patch new file mode 100644 index 0000000..eab0843 --- /dev/null +++ b/0009-builder-templates-make-template.ml-Use-quoted-string.patch @@ -0,0 +1,33 @@ +From 1fca99c9bed60cd9a9c62826d4e6995052d0c5dd Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Tue, 2 Sep 2025 09:06:33 +0100 +Subject: [PATCH] builder/templates/make-template.ml: Use quoted string + literals for regexps + +(cherry picked from commit aafb271e8ffd961c529f90ef65df75a70102f0fc) +--- + builder/templates/make-template.ml | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/builder/templates/make-template.ml b/builder/templates/make-template.ml +index a69d3ad3f..f8b41209e 100755 +--- a/builder/templates/make-template.ml ++++ b/builder/templates/make-template.ml +@@ -374,7 +374,7 @@ and os_of_string os ver = + eprintf "%s: unknown or unsupported OS (%s, %s)\n" prog os ver; exit 1 + + and parse_major_minor ver = +- let rex = Str.regexp "^\\([0-9]+\\)\\.\\([0-9]+\\)$" in ++ let rex = Str.regexp {|^\([0-9]+\)\.\([0-9]+\)$|} in + if Str.string_match rex ver 0 then ( + int_of_string (Str.matched_group 1 ver), + int_of_string (Str.matched_group 2 ver) +@@ -1631,7 +1631,7 @@ and read_revision filename = + | None -> `No_file + | Some chan -> + let r = ref `No_revision in +- let rex = Str.regexp "^revision=\\([0-9]+\\)$" in ++ let rex = Str.regexp {|^revision=\([0-9]+\)$|} in + (try + let rec loop () = + let line = input_line chan in diff --git a/0010-builder-templates-make-template.ml-Add-str-and-unix-.patch b/0010-builder-templates-make-template.ml-Add-str-and-unix-.patch new file mode 100644 index 0000000..5e109e2 --- /dev/null +++ b/0010-builder-templates-make-template.ml-Add-str-and-unix-.patch @@ -0,0 +1,43 @@ +From 7b2125b42535e6768d8f162d19463af4c4e8df1b Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Tue, 2 Sep 2025 09:07:34 +0100 +Subject: [PATCH] builder/templates/make-template.ml: Add +str and +unix for + OCaml 5 + +Removes these warnings: + +File "_none_", line 1: +Alert ocaml_deprecated_auto_include: +OCaml's lib directory layout changed in 5.0. The str subdirectory has been +automatically added to the search path, but you should add -I +str to the +command-line to silence this alert (e.g. by adding str to the list of +libraries in your dune file, or adding use_str to your _tags file for +ocamlbuild, or using -package str for ocamlfind). + +File "_none_", line 1: +Alert ocaml_deprecated_auto_include: +OCaml's lib directory layout changed in 5.0. The unix subdirectory has been +automatically added to the search path, but you should add -I +unix to the +command-line to silence this alert (e.g. by adding unix to the list of +libraries in your dune file, or adding use_unix to your _tags file for +ocamlbuild, or using -package unix for ocamlfind). + +(cherry picked from commit 046309b31c22da5ac64feac12bcf4501377a8b3f) +--- + builder/templates/make-template.ml | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/builder/templates/make-template.ml b/builder/templates/make-template.ml +index f8b41209e..2ce803e2b 100755 +--- a/builder/templates/make-template.ml ++++ b/builder/templates/make-template.ml +@@ -24,7 +24,9 @@ + * shell scripts located in libguestfs.git/builder/website. + *) + ++#directory "+str";; + #load "str.cma";; ++#directory "+unix";; + #load "unix.cma";; + #directory "+guestfs";; (* use globally installed guestfs *) + #load "mlguestfs.cma";; diff --git a/0011-daemon-generator-Use-power-of-2-for-initial-size-of-.patch b/0011-daemon-generator-Use-power-of-2-for-initial-size-of-.patch new file mode 100644 index 0000000..e4bcfa7 --- /dev/null +++ b/0011-daemon-generator-Use-power-of-2-for-initial-size-of-.patch @@ -0,0 +1,76 @@ +From 68a3719888f36719cef8bcd7817ed634c4524012 Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Fri, 12 Sep 2025 08:37:58 +0100 +Subject: [PATCH] daemon, generator: Use power of 2 for initial size of + Hashtbl.create + +Before 2011 it was recommended to use a prime number for the initial +size. In 2011 the OCaml hash table was reimplemented using a hash +function based on Murmur 3. Hashtbl.create now adjusts the initial +size to the next power of 2 (minimum 16). So replace obsolete +'Hashtbl.create 13' with 'Hashtbl.create 16'. + +(cherry picked from commit 51f7dfa8612e8e273e3ef15eddd7a5714122cc57) +--- + builder/builder.ml | 2 +- + builder/index_parser.ml | 4 ++-- + resize/resize.ml | 4 ++-- + 3 files changed, 5 insertions(+), 5 deletions(-) + +diff --git a/builder/builder.ml b/builder/builder.ml +index bbe2808f5..2df2fa5c7 100644 +--- a/builder/builder.ml ++++ b/builder/builder.ml +@@ -46,7 +46,7 @@ let remove_duplicates index = + * (name, arch) tuples, so it possible to ignore duplicates, + * and versions with a lower revision. + *) +- let nseen = Hashtbl.create 13 in ++ let nseen = Hashtbl.create 16 in + List.iter ( + fun (name, { Index.arch; revision }) -> + let id = name, arch in +diff --git a/builder/index_parser.ml b/builder/index_parser.ml +index 3e19e489e..2cb89488f 100644 +--- a/builder/index_parser.ml ++++ b/builder/index_parser.ml +@@ -55,7 +55,7 @@ let get_index ~downloader ~sigchecker ?(template = false) + in + n, (find_arch fields) + ) sections in +- let nseen = Hashtbl.create 13 in ++ let nseen = Hashtbl.create 16 in + List.iter ( + fun (n, arch) -> + let id = n, arch in +@@ -71,7 +71,7 @@ let get_index ~downloader ~sigchecker ?(template = false) + (* Check for repeated fields. *) + List.iter ( + fun (n, fields) -> +- let fseen = Hashtbl.create 13 in ++ let fseen = Hashtbl.create 16 in + List.iter ( + fun (field, subkey, _) -> + let hashkey = (field, subkey) in +diff --git a/resize/resize.ml b/resize/resize.ml +index fffe2de77..707def8e9 100644 +--- a/resize/resize.ml ++++ b/resize/resize.ml +@@ -630,7 +630,7 @@ read the man page virt-resize(1). + * that the user has asked to be ignored or deleted. + *) + let find_partition = +- let hash = Hashtbl.create 13 in ++ let hash = Hashtbl.create 16 in + List.iter (fun ({ p_name = name } as p) -> Hashtbl.add hash name p) + partitions; + fun ~option name -> +@@ -881,7 +881,7 @@ read the man page virt-resize(1). + surplus in + + (* Mark the --lv-expand LVs. *) +- let hash = Hashtbl.create 13 in ++ let hash = Hashtbl.create 16 in + List.iter (fun ({ lv_name = name } as lv) -> Hashtbl.add hash name lv) lvs; + + List.iter ( diff --git a/0012-builder-Build-fedora-42-template.patch b/0012-builder-Build-fedora-42-template.patch new file mode 100644 index 0000000..dfc7ed4 --- /dev/null +++ b/0012-builder-Build-fedora-42-template.patch @@ -0,0 +1,123 @@ +From abb56223e0a35c3e5d1158f32789c44c28bacf40 Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Sat, 24 May 2025 13:27:00 +0100 +Subject: [PATCH] builder: Build fedora-42 template + +Thanks: Jeff Layton +--- + builder/templates/fedora-42.index-fragment | 22 +++++++++ + builder/templates/fedora-42.ks | 48 ++++++++++++++++++++ + builder/templates/fedora-42.virt-install-cmd | 20 ++++++++ + 3 files changed, 90 insertions(+) + create mode 100644 builder/templates/fedora-42.index-fragment + create mode 100644 builder/templates/fedora-42.ks + create mode 100644 builder/templates/fedora-42.virt-install-cmd + +diff --git a/builder/templates/fedora-42.index-fragment b/builder/templates/fedora-42.index-fragment +new file mode 100644 +index 000000000..6d21b742c +--- /dev/null ++++ b/builder/templates/fedora-42.index-fragment +@@ -0,0 +1,22 @@ ++[fedora-42] ++name=Fedora® 42 Server ++osinfo=fedora42 ++arch=x86_64 ++file=fedora-42.xz ++checksum[sha512]=dda921f3c522da8d8a67e31948260f52a9aa5bdeca4382a5c557635d77ad91d11b9aded70a225f59503b268ec73815ceac2eac5b70c1d47f22056febdd9da6bb ++format=raw ++size=6442450944 ++compressed_size=927728896 ++expand=/dev/sda3 ++notes=Fedora® 42 Server ++ ++ This Fedora image contains only unmodified @Core group packages. ++ ++ Fedora and the Infinity design logo are trademarks of Red Hat, Inc. ++ Source and further information is available from http://fedoraproject.org/ ++ ++ This template was generated by a script in the libguestfs source tree: ++ builder/templates/make-template.ml ++ Associated files used to prepare this template can be found in the ++ same directory. ++ +diff --git a/builder/templates/fedora-42.ks b/builder/templates/fedora-42.ks +new file mode 100644 +index 000000000..17e2e3f15 +--- /dev/null ++++ b/builder/templates/fedora-42.ks +@@ -0,0 +1,48 @@ ++# Kickstart file for fedora-42 ++# Generated by libguestfs.git/builder/templates/make-template.ml ++ ++text ++reboot ++lang en_US.UTF-8 ++keyboard us ++network --bootproto dhcp ++rootpw builder ++firewall --enabled --ssh ++timezone --utc America/New_York ++selinux --enforcing ++ ++bootloader --location=mbr --append="console=tty0 console=ttyS0,115200 rd_NO_PLYMOUTH" ++ ++ ++zerombr ++clearpart --all --initlabel --disklabel=gpt ++autopart --type=plain ++ ++# Halt the system once configuration has finished. ++poweroff ++ ++%packages ++@core ++%end ++ ++%post ++# Ensure the installation is up-to-date. ++dnf -y --best upgrade ++# This required otherwise the kernel will not be bootable, see ++# https://bugzilla.redhat.com/show_bug.cgi?id=1911177 ++# https://bugzilla.redhat.com/show_bug.cgi?id=1945835#c24 ++grub2-mkconfig -o '/etc/grub2.cfg' ++# Enable Xen domU support. ++pushd /etc/dracut.conf.d ++echo 'add_drivers+=" xen:vbd xen:vif "' > virt-builder-xen-drivers.conf ++popd ++# To make dracut config changes permanent, we need to rerun dracut. ++# Rerun dracut for the installed kernel (not the running kernel). ++# See commit 0fa52e4e45d80874bc5ea5f112f74be1d3f3472f and ++# https://www.redhat.com/archives/libguestfs/2014-June/thread.html#00045 ++KERNEL_VERSION="$(rpm -q kernel --qf '%{version}-%{release}.%{arch}\n' | ++ sort -V | tail -1)" ++dracut -f /boot/initramfs-$KERNEL_VERSION.img $KERNEL_VERSION ++%end ++ ++# EOF +diff --git a/builder/templates/fedora-42.virt-install-cmd b/builder/templates/fedora-42.virt-install-cmd +new file mode 100644 +index 000000000..2a3256bbd +--- /dev/null ++++ b/builder/templates/fedora-42.virt-install-cmd +@@ -0,0 +1,20 @@ ++# This is the virt-install command which was used to create ++# the virt-builder template 'fedora-42' ++# NB: This file is generated for documentation purposes ONLY! ++# This script was never run, and is not intended to be run. ++ ++'virt-install' \ ++ '--transient' \ ++ '--name=tmp-oeg15xnl' \ ++ '--ram=4096' \ ++ '--arch=x86_64' \ ++ '--cpu=host' \ ++ '--vcpus=4' \ ++ '--os-variant=fedora34' \ ++ '--initrd-inject=fedora-42.ks' \ ++ '--extra-args=inst.ks=file:/fedora-42.ks console=tty0 console=ttyS0,115200 rd_NO_PLYMOUTH' \ ++ '--disk=/home/rjones/d/guestfs-tools/builder/templates/tmp-oeg15xnl.img,size=6,format=raw' \ ++ '--location=https://lon.mirror.rackspace.com/fedora/releases/42/Server/x86_64/os/' \ ++ '--serial=pty' \ ++ '--nographics' ++ diff --git a/0013-Update-common-submodule.patch b/0013-Update-common-submodule.patch new file mode 100644 index 0000000..6840853 --- /dev/null +++ b/0013-Update-common-submodule.patch @@ -0,0 +1,61 @@ +From 2d808c89eee6369df62b3afe16681a41ca42e0b9 Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Sat, 20 Sep 2025 17:20:25 +0100 +Subject: [PATCH] Update common submodule + +Richard W.M. Jones (3): + mlstdutils: Export List.find_opt + daemon, generator: Use power of 2 for initial size of Hashtbl.create + mlcustomize/inject_virtio_win.ml: Use viostor.inf instead of guestor + +See-also: https://github.com/libguestfs/libguestfs-common/pull/17 +(cherry picked from commit 13bcff3c2ca3e7ad355527a13b3aa2941ec1ff5b) +--- + common | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +Submodule common 7ecf3992b..da55dc4d7: +diff --git a/common/mlcustomize/customize_run.ml b/common/mlcustomize/customize_run.ml +index ff719f4ec..64afd3ab5 100644 +--- a/common/mlcustomize/customize_run.ml ++++ b/common/mlcustomize/customize_run.ml +@@ -97,7 +97,7 @@ let run (g : G.guestfs) root (ops : ops) = + ) in + + (* Store the passwords and set them all at the end. *) +- let passwords = Hashtbl.create 13 in ++ let passwords = Hashtbl.create 16 in + let set_password user pw = + if Hashtbl.mem passwords user then + error (f_"multiple --root-password/--password options set the \ +diff --git a/common/mlcustomize/inject_virtio_win.ml b/common/mlcustomize/inject_virtio_win.ml +index b26b14d34..114df0641 100644 +--- a/common/mlcustomize/inject_virtio_win.ml ++++ b/common/mlcustomize/inject_virtio_win.ml +@@ -310,8 +310,13 @@ and ddb_regedits inspect drv_name drv_pciid = + * one must add keys into the DriverDatabase. + *) + ++ let winarch = ++ match inspect.i_arch with ++ | "i386" -> "x86" | "x86_64" -> "amd64" ++ | _ -> assert false in ++ + let drv_inf = "guestor.inf" in +- let drv_inf_label = drv_inf ^ "_tmp" in ++ let drv_inf_label = sprintf "%s_%s_0000000000000000" drv_inf winarch in + let drv_config = "guestor_conf" in + + [ +diff --git a/common/mlstdutils/std_utils.mli b/common/mlstdutils/std_utils.mli +index a20e720c2..6c1911da8 100644 +--- a/common/mlstdutils/std_utils.mli ++++ b/common/mlstdutils/std_utils.mli +@@ -46,6 +46,7 @@ module List : sig + val mem : 'a -> 'a list -> bool + val memq : 'a -> 'a list -> bool + val find : ('a -> bool) -> 'a list -> 'a ++ val find_opt : ('a -> bool) -> 'a list -> 'a option + val filter : ('a -> bool) -> 'a list -> 'a list + val find_all : ('a -> bool) -> 'a list -> 'a list + val partition : ('a -> bool) -> 'a list -> 'a list * 'a list diff --git a/0014-common-update-submodule.patch b/0014-common-update-submodule.patch new file mode 100644 index 0000000..ea8e00e --- /dev/null +++ b/0014-common-update-submodule.patch @@ -0,0 +1,427 @@ +From 5ed99959b689fc6fa287d9c6100e92a18bc1c9be Mon Sep 17 00:00:00 2001 +From: Cole Robinson +Date: Tue, 14 Oct 2025 14:07:40 -0400 +Subject: [PATCH] common: update submodule + +Cole Robinson (1): + mlcustomize: deprecate and remove --sm-* options + +Signed-off-by: Cole Robinson +(cherry picked from commit 57b225d8d324d15d79fab2ccc6944868c1168a42) +--- + common | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +Submodule common da55dc4d7..90399dd18: +diff --git a/common/mlcustomize/Makefile.am b/common/mlcustomize/Makefile.am +index 6e03bfea3..351524c80 100644 +--- a/common/mlcustomize/Makefile.am ++++ b/common/mlcustomize/Makefile.am +@@ -47,7 +47,6 @@ SOURCES_MLI = \ + random_seed.mli \ + SELinux_relabel.mli \ + ssh_key.mli \ +- subscription_manager.mli \ + timezone.mli + + # This list must be in dependency order. +@@ -58,7 +57,6 @@ SOURCES_ML = \ + perl_edit.ml \ + random_seed.ml \ + ssh_key.ml \ +- subscription_manager.ml \ + timezone.ml \ + crypt.ml \ + password.ml \ +diff --git a/common/mlcustomize/customize-options.pod b/common/mlcustomize/customize-options.pod +index 157145ebc..c8de3953c 100644 +--- a/common/mlcustomize/customize-options.pod ++++ b/common/mlcustomize/customize-options.pod +@@ -392,35 +392,6 @@ It cannot delete directories, only regular files. + + This is a compatibility option that does nothing. + +-=item B<--sm-attach> SELECTOR +- +-Attach to a pool using C. +- +-See L for the format of +-the C field. +- +-=item B<--sm-credentials> SELECTOR +- +-Set the credentials for C. +- +-See L for the format of +-the C field. +- +-=item B<--sm-register> +- +-Register the guest using C. +- +-This requires credentials being set using I<--sm-credentials>. +- +-=item B<--sm-remove> +- +-Remove all the subscriptions from the guest using +-C. +- +-=item B<--sm-unregister> +- +-Unregister the guest using C. +- + =item B<--ssh-inject> USER[:SELECTOR] + + Inject an ssh key so the given C will be able to log in over +diff --git a/common/mlcustomize/customize-synopsis.pod b/common/mlcustomize/customize-synopsis.pod +index 957de8cf2..f5eeb69dd 100644 +--- a/common/mlcustomize/customize-synopsis.pod ++++ b/common/mlcustomize/customize-synopsis.pod +@@ -8,11 +8,10 @@ + [--install PKG,PKG..] [--link TARGET:LINK[:LINK..]] [--mkdir DIR] + [--move SOURCE:DEST] [--password USER:SELECTOR] + [--root-password SELECTOR] [--run SCRIPT] +- [--run-command 'CMD+ARGS'] [--scrub FILE] [--sm-attach SELECTOR] +- [--sm-register] [--sm-remove] [--sm-unregister] ++ [--run-command 'CMD+ARGS'] [--scrub FILE] + [--ssh-inject USER[:SELECTOR]] [--tar-in TARFILE:REMOTEDIR] + [--timezone TIMEZONE] [--touch FILE] [--truncate FILE] + [--truncate-recursive PATH] [--uninstall PKG,PKG..] [--update] + [--upload FILE:DEST] [--write FILE:CONTENT] [--no-logfile] + [--password-crypto md5|sha256|sha512] [--no-selinux-relabel] +- [--selinux-relabel] [--sm-credentials SELECTOR] ++ [--selinux-relabel] +diff --git a/common/mlcustomize/customize_cmdline.ml b/common/mlcustomize/customize_cmdline.ml +index 7451f1325..786f4ee49 100644 +--- a/common/mlcustomize/customize_cmdline.ml ++++ b/common/mlcustomize/customize_cmdline.ml +@@ -85,14 +85,6 @@ and op = [ + (* --run-command 'CMD+ARGS' *) + | `Scrub of string + (* --scrub FILE *) +- | `SMAttach of Subscription_manager.sm_pool +- (* --sm-attach SELECTOR *) +- | `SMRegister +- (* --sm-register *) +- | `SMRemove +- (* --sm-remove *) +- | `SMUnregister +- (* --sm-unregister *) + | `SSHInject of string * Ssh_key.ssh_key_selector + (* --ssh-inject USER[:SELECTOR] *) + | `TarIn of string * string +@@ -123,8 +115,6 @@ and flags = { + (* --no-selinux-relabel *) + selinux_relabel_ignored : bool; + (* --selinux-relabel *) +- sm_credentials : Subscription_manager.sm_credentials option; +- (* --sm-credentials SELECTOR *) + } + + type argspec = Getopt.keys * Getopt.spec * Getopt.doc +@@ -135,7 +125,6 @@ let rec argspec ?(v2v = false) () = + let password_crypto = ref None in + let no_selinux_relabel = ref false in + let selinux_relabel_ignored = ref false in +- let sm_credentials = ref None in + + let rec get_ops () = { + ops = List.rev !ops; +@@ -146,7 +135,6 @@ let rec argspec ?(v2v = false) () = + password_crypto = !password_crypto; + no_selinux_relabel = !no_selinux_relabel; + selinux_relabel_ignored = !selinux_relabel_ignored; +- sm_credentials = !sm_credentials; + } + in + +@@ -386,35 +374,6 @@ let rec argspec ?(v2v = false) () = + s_"Scrub a file" + ), + Some "FILE", "Scrub a file from the guest. This is like I<--delete> except that:\n\n=over 4\n\n=item *\n\nIt scrubs the data so a guest could not recover it.\n\n=item *\n\nIt cannot delete directories, only regular files.\n\n=back", false; +- ( +- [ L"sm-attach" ], +- Getopt.String ( +- s_"SELECTOR", +- fun s -> +- let sel = Subscription_manager.parse_pool_selector s in +- List.push_front (`SMAttach sel) ops +- ), +- s_"Attach to a subscription-manager pool" +- ), +- Some "SELECTOR", "Attach to a pool using C.\n\nSee L for the format of\nthe C field.", false; +- ( +- [ L"sm-register" ], +- Getopt.Unit (fun () -> List.push_front `SMRegister ops), +- s_"Register using subscription-manager" +- ), +- None, "Register the guest using C.\n\nThis requires credentials being set using I<--sm-credentials>.", false; +- ( +- [ L"sm-remove" ], +- Getopt.Unit (fun () -> List.push_front `SMRemove ops), +- s_"Remove all the subscriptions" +- ), +- None, "Remove all the subscriptions from the guest using\nC.", false; +- ( +- [ L"sm-unregister" ], +- Getopt.Unit (fun () -> List.push_front `SMUnregister ops), +- s_"Unregister using subscription-manager" +- ), +- None, "Unregister the guest using C.", false; + ( + [ L"ssh-inject" ], + Getopt.String ( +@@ -529,16 +488,6 @@ let rec argspec ?(v2v = false) () = + s_"Compatibility option doing nothing" + ), + None, "This is a compatibility option that does nothing.", false; +- ( +- [ L"sm-credentials" ], +- Getopt.String ( +- s_"SELECTOR", +- fun s -> +- sm_credentials := Some (Subscription_manager.parse_credentials_selector s) +- ), +- s_"Credentials for subscription-manager" +- ), +- Some "SELECTOR", "Set the credentials for C.\n\nSee L for the format of\nthe C field.", false; + ] + and customize_read_from_file filename = + let forbidden_commands = [ +diff --git a/common/mlcustomize/customize_cmdline.mli b/common/mlcustomize/customize_cmdline.mli +index cc32f3af4..baacda9d3 100644 +--- a/common/mlcustomize/customize_cmdline.mli ++++ b/common/mlcustomize/customize_cmdline.mli +@@ -77,14 +77,6 @@ and op = [ + (* --run-command 'CMD+ARGS' *) + | `Scrub of string + (* --scrub FILE *) +- | `SMAttach of Subscription_manager.sm_pool +- (* --sm-attach SELECTOR *) +- | `SMRegister +- (* --sm-register *) +- | `SMRemove +- (* --sm-remove *) +- | `SMUnregister +- (* --sm-unregister *) + | `SSHInject of string * Ssh_key.ssh_key_selector + (* --ssh-inject USER[:SELECTOR] *) + | `TarIn of string * string +@@ -115,8 +107,6 @@ and flags = { + (* --no-selinux-relabel *) + selinux_relabel_ignored : bool; + (* --selinux-relabel *) +- sm_credentials : Subscription_manager.sm_credentials option; +- (* --sm-credentials SELECTOR *) + } + + type argspec = Getopt.keys * Getopt.spec * Getopt.doc +diff --git a/common/mlcustomize/customize_run.ml b/common/mlcustomize/customize_run.ml +index 64afd3ab5..1baf0f3cf 100644 +--- a/common/mlcustomize/customize_run.ml ++++ b/common/mlcustomize/customize_run.ml +@@ -279,43 +279,6 @@ let run (g : G.guestfs) root (ops : ops) = + message (f_"Scrubbing: %s") path; + g#scrub_file path + +- | `SMAttach pool -> +- (match pool with +- | Subscription_manager.PoolAuto -> +- message (f_"Attaching to compatible subscriptions"); +- let cmd = "subscription-manager attach --auto" in +- do_run ~display:cmd ~warn_failed_no_network:true cmd +- | Subscription_manager.PoolId id -> +- message (f_"Attaching to the pool %s") id; +- let cmd = sprintf "subscription-manager attach --pool=%s" (quote id) in +- do_run ~display:cmd ~warn_failed_no_network:true cmd +- ) +- +- | `SMRegister -> +- message (f_"Registering with subscription-manager"); +- let creds = +- match ops.flags.sm_credentials with +- | None -> +- error (f_"subscription-manager credentials required for \ +- --sm-register") +- | Some c -> c in +- let cmd = sprintf "subscription-manager register \ +- --username=%s --password=%s" +- (quote creds.Subscription_manager.sm_username) +- (quote creds.Subscription_manager.sm_password) in +- do_run ~display:"subscription-manager register" +- ~warn_failed_no_network:true cmd +- +- | `SMRemove -> +- message (f_"Removing all the subscriptions"); +- let cmd = "subscription-manager remove --all" in +- do_run ~display:cmd ~warn_failed_no_network:true cmd +- +- | `SMUnregister -> +- message (f_"Unregistering with subscription-manager"); +- let cmd = "subscription-manager unregister" in +- do_run ~display:cmd ~warn_failed_no_network:true cmd +- + | `SSHInject (user, selector) -> + if unix_like (g#inspect_get_type root) then ( + message (f_"SSH key inject: %s") user; +diff --git a/common/mlcustomize/subscription_manager.ml b/common/mlcustomize/subscription_manager.ml +deleted file mode 100644 +index 104602462..000000000 +--- a/common/mlcustomize/subscription_manager.ml ++++ /dev/null +@@ -1,54 +0,0 @@ +-(* virt-customize +- * Copyright (C) 2015 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 Std_utils +-open Tools_utils +-open Common_gettext.Gettext +- +-type sm_credentials = { +- sm_username : string; +- sm_password : string; +-} +- +-type sm_pool = +-| PoolAuto +-| PoolId of string +- +-let rec parse_credentials_selector arg = +- parse_credentials_selector_list arg (String.nsplit ":" arg) +- +-and parse_credentials_selector_list orig_arg = function +- | [ username; "password"; password ] -> +- { sm_username = username; sm_password = password } +- | [ username; "file"; filename ] -> +- { sm_username = username; sm_password = read_first_line_from_file filename } +- | _ -> +- error (f_"invalid sm-credentials selector ‘%s’; see the man page") orig_arg +- +-let rec parse_pool_selector arg = +- parse_pool_selector_list arg (String.nsplit ":" arg) +- +-and parse_pool_selector_list orig_arg = function +- | [ "auto" ] -> +- PoolAuto +- | [ "pool"; pool ] -> +- PoolId pool +- | [ "file"; filename ] -> +- PoolId (read_first_line_from_file filename) +- | _ -> +- error (f_"invalid sm-attach selector ‘%s’; see the man page") orig_arg +diff --git a/common/mlcustomize/subscription_manager.mli b/common/mlcustomize/subscription_manager.mli +deleted file mode 100644 +index bb6b92014..000000000 +--- a/common/mlcustomize/subscription_manager.mli ++++ /dev/null +@@ -1,34 +0,0 @@ +-(* virt-customize +- * Copyright (C) 2015 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. +- *) +- +-type sm_credentials = { +- sm_username : string; +- sm_password : string; +-} +- +-type sm_pool = +-| PoolAuto (** Automatic entitlements. *) +-| PoolId of string (** Specific pool. *) +- +-val parse_credentials_selector : string -> sm_credentials +-(** Parse the selector field in --sm-credentials. Exits if the format +- is not valid. *) +- +-val parse_pool_selector : string -> sm_pool +-(** Parse the selector field in --sm-attach. Exits if the format +- is not valid. *) +diff --git a/common/mlcustomize/v2v-customize-options.pod b/common/mlcustomize/v2v-customize-options.pod +index 0974f4592..f02f05dac 100644 +--- a/common/mlcustomize/v2v-customize-options.pod ++++ b/common/mlcustomize/v2v-customize-options.pod +@@ -338,35 +338,6 @@ It cannot delete directories, only regular files. + + This is a compatibility option that does nothing. + +-=item B<--sm-attach> SELECTOR +- +-Attach to a pool using C. +- +-See L for the format of +-the C field. +- +-=item B<--sm-credentials> SELECTOR +- +-Set the credentials for C. +- +-See L for the format of +-the C field. +- +-=item B<--sm-register> +- +-Register the guest using C. +- +-This requires credentials being set using I<--sm-credentials>. +- +-=item B<--sm-remove> +- +-Remove all the subscriptions from the guest using +-C. +- +-=item B<--sm-unregister> +- +-Unregister the guest using C. +- + =item B<--ssh-inject> USER[:SELECTOR] + + Inject an ssh key so the given C will be able to log in over +diff --git a/common/mlcustomize/v2v-customize-synopsis.pod b/common/mlcustomize/v2v-customize-synopsis.pod +index 7eb0a941c..4c1a1547b 100644 +--- a/common/mlcustomize/v2v-customize-synopsis.pod ++++ b/common/mlcustomize/v2v-customize-synopsis.pod +@@ -7,11 +7,9 @@ + [--link TARGET:LINK[:LINK..]] [--mkdir DIR] [--move SOURCE:DEST] + [--password USER:SELECTOR] [--root-password SELECTOR] + [--run SCRIPT] [--run-command 'CMD+ARGS'] [--scrub FILE] +- [--sm-attach SELECTOR] [--sm-register] [--sm-remove] +- [--sm-unregister] [--ssh-inject USER[:SELECTOR]] +- [--tar-in TARFILE:REMOTEDIR] [--timezone TIMEZONE] [--touch FILE] +- [--truncate FILE] [--truncate-recursive PATH] +- [--uninstall PKG,PKG..] [--update] [--upload FILE:DEST] +- [--write FILE:CONTENT] [--no-logfile] ++ [--ssh-inject USER[:SELECTOR]] [--tar-in TARFILE:REMOTEDIR] ++ [--timezone TIMEZONE] [--touch FILE] [--truncate FILE] ++ [--truncate-recursive PATH] [--uninstall PKG,PKG..] [--update] ++ [--upload FILE:DEST] [--write FILE:CONTENT] [--no-logfile] + [--password-crypto md5|sha256|sha512] [--no-selinux-relabel] +- [--selinux-relabel] [--sm-credentials SELECTOR] ++ [--selinux-relabel] diff --git a/0015-pod-Document-removal-of-sm-options.patch b/0015-pod-Document-removal-of-sm-options.patch new file mode 100644 index 0000000..c2448a6 --- /dev/null +++ b/0015-pod-Document-removal-of-sm-options.patch @@ -0,0 +1,150 @@ +From de70086dbe2eb8a6ec023ebbd0c84489a8719ba1 Mon Sep 17 00:00:00 2001 +From: Cole Robinson +Date: Tue, 14 Oct 2025 14:16:19 -0400 +Subject: [PATCH] pod: Document removal of --sm-* options + +Update podcheck.pl to allow for still documenting these +in virt-builder.pod + +Signed-off-by: Cole Robinson +(cherry picked from commit 7bad703d724cd976cc3b7760b09af353aab46ff0) +--- + builder/virt-builder.pod | 73 +++++++++++++----------------------- + customize/virt-customize.pod | 8 ++++ + podcheck.pl | 13 ++++++- + 3 files changed, 46 insertions(+), 48 deletions(-) + +diff --git a/builder/virt-builder.pod b/builder/virt-builder.pod +index ff0ec250c..f043939ca 100644 +--- a/builder/virt-builder.pod ++++ b/builder/virt-builder.pod +@@ -878,53 +878,6 @@ F. + + =back + +-=head2 SUBSCRIPTION-MANAGER +- +-It is possible to automate the registration and attaching of the +-system using C. This is typical on +-Red Hat Enterprise Linux guests. There are few options which ease +-this process, avoid executing commands manually and exposing +-passwords on command line. +- +-I<--sm-register> starts the registration process, and requires +-I<--sm-credentials> to be specified; the format of the C +-of I<--sm-credentials> is one of the following formats: +- +-=over 4 +- +-=item B<--sm-credentials> USER:file:FILENAME +- +-Read the password for the specified C from F. +- +-=item B<--sm-credentials> USER:password:PASSWORD +- +-Use the literal string C for the specified C. +- +-=back +- +-I<--sm-attach> attaches the system to subscriptions; the format +-of its C is one of the following: +- +-=over 4 +- +-=item B<--sm-attach> auto +- +-C attaches to the best-fitting subscriptions +-for the system. +- +-=item B<--sm-attach> file:FILENAME +- +-Read the pool ID from F. +- +-=item B<--sm-attach> pool:POOL +- +-Use the literal string C as pool ID. +- +-=back +- +-I<--sm-remove> removes all the subscriptions from the guest, while +-I<--sm-unregister> completely unregister the system. +- + =head2 INSTALLATION PROCESS + + When you invoke virt-builder, installation proceeds as follows: +@@ -1764,6 +1717,32 @@ them, which is normal and harmless. + + =back + ++=head2 DEPRECATED OPTIONS ++ ++In version 1.56 the subscription-manager options were removed. ++ ++=over 4 ++ ++=item B<--sm-credentials> ++ ++=item B<--sm-register> ++ ++Use --run='subscription-manager register --username=USER --password=PASS' ++ ++=item B<--sm-unregister> ++ ++Use --run='subscription-manager unregister' ++ ++=item B<--sm-attach> ++ ++Use --run='subscription-manager attach ...' ++ ++=item B<--sm-remove> ++ ++Use --run='subscription-manager remove --all' ++ ++=back ++ + =head1 MACHINE READABLE OUTPUT + + The I<--machine-readable> option can be used to make the output more +diff --git a/customize/virt-customize.pod b/customize/virt-customize.pod +index fb3d65665..9fee64ebc 100644 +--- a/customize/virt-customize.pod ++++ b/customize/virt-customize.pod +@@ -248,6 +248,14 @@ existing ones. + + For further details, see L. + ++=head1 DEPRECATED OPTIONS ++ ++In version 1.56 the subscription-manager options were removed: ++B<--sm-credentials>, B<--sm-register>, B<--sm-unregister>, ++B<--sm-attach>, B<--sm-remove> ++ ++For further details, see L. ++ + =head1 EXIT STATUS + + This program returns 0 on success, or 1 if there was an error. +diff --git a/podcheck.pl b/podcheck.pl +index 795fe0e9b..047bbf8da 100755 +--- a/podcheck.pl ++++ b/podcheck.pl +@@ -203,7 +203,18 @@ my $pod_options_checked = 0; + + my %pod_options = (); + $pod_options{$_} = 1 foreach ( $content =~ /^=item.*B<(-[-\w]+)(?:=.*)?>/gm ); +-foreach (sort keys %pod_options) { ++ ++my %pod_option_exists = (); ++$pod_option_exists{$_} = 1 foreach keys %pod_options; ++ ++# Removed from the tool but we still document them ++delete $pod_option_exists{"--sm-credentials"}; ++delete $pod_option_exists{"--sm-register"}; ++delete $pod_option_exists{"--sm-unregister"}; ++delete $pod_option_exists{"--sm-remove"}; ++delete $pod_option_exists{"--sm-attach"}; ++ ++foreach (sort keys %pod_option_exists) { + unless ($ignore{$_}) { + $pod_options_checked++; + unless (exists $tool_option_exists{$_}) { diff --git a/0016-test-data-phony-guests-Increase-size-of-Windows-imag.patch b/0016-test-data-phony-guests-Increase-size-of-Windows-imag.patch new file mode 100644 index 0000000..438b686 --- /dev/null +++ b/0016-test-data-phony-guests-Increase-size-of-Windows-imag.patch @@ -0,0 +1,31 @@ +From 269302e1a500d3cba94194faa258f6b47fef88fb Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Tue, 4 Nov 2025 13:04:12 +0000 +Subject: [PATCH] test-data/phony-guests: Increase size of Windows image + +For testing I tried to upload a very large Windows registry to the +phony Windows image. However because of the 256M limit on the size of +the second partition (and NTFS overhead?) this was not possible. + +There's no real penalty to increasing the size of this image since +it's just virtual space, so increase the virtual size to 2G (256M/1.7G +split). + +(cherry picked from commit a39ae0b898825970549876738bd465f6a110de7c) +--- + test-data/phony-guests/make-windows-img.sh | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/test-data/phony-guests/make-windows-img.sh b/test-data/phony-guests/make-windows-img.sh +index 0ec6b4851..e72013c3d 100755 +--- a/test-data/phony-guests/make-windows-img.sh ++++ b/test-data/phony-guests/make-windows-img.sh +@@ -37,7 +37,7 @@ fi + + # Create a disk image. + guestfish < +Date: Mon, 3 Nov 2025 15:26:19 +0000 +Subject: [PATCH] inspector: Add new field to output of virt-inspector + +Rebase common submodule to pick up: + + Richard W.M. Jones (1): + structs/structs-print.c: Update this generated file + +Fixes: https://issues.redhat.com/browse/RHEL-125955 +(cherry picked from commit 53e59d0189dc71d78559478c4924f1a1a994c455) +--- + common | 2 +- + inspector/inspector.c | 2 ++ + inspector/virt-inspector.rng | 1 + + m4/guestfs-libraries.m4 | 3 ++- + 4 files changed, 6 insertions(+), 2 deletions(-) + +Submodule common 90399dd18..b54ba2031: +diff --git a/common/structs/structs-print.c b/common/structs/structs-print.c +index bfe007cde..8c376b0ee 100644 +--- a/common/structs/structs-print.c ++++ b/common/structs/structs-print.c +@@ -63,7 +63,7 @@ guestfs_int_print_application2_indent (struct guestfs_application2 *application2 + fprintf (dest, "%sapp2_source_package: %s%s", indent, application2->app2_source_package, linesep); + fprintf (dest, "%sapp2_summary: %s%s", indent, application2->app2_summary, linesep); + fprintf (dest, "%sapp2_description: %s%s", indent, application2->app2_description, linesep); +- fprintf (dest, "%sapp2_spare1: %s%s", indent, application2->app2_spare1, linesep); ++ fprintf (dest, "%sapp2_class: %s%s", indent, application2->app2_class, linesep); + fprintf (dest, "%sapp2_spare2: %s%s", indent, application2->app2_spare2, linesep); + fprintf (dest, "%sapp2_spare3: %s%s", indent, application2->app2_spare3, linesep); + fprintf (dest, "%sapp2_spare4: %s%s", indent, application2->app2_spare4, linesep); +diff --git a/inspector/inspector.c b/inspector/inspector.c +index 1177f44a4..cccc209d0 100644 +--- a/inspector/inspector.c ++++ b/inspector/inspector.c +@@ -697,6 +697,8 @@ output_applications (xmlTextWriterPtr xo, char *root) + single_element ("summary", apps->val[i].app2_summary); + if (apps->val[i].app2_description && apps->val[i].app2_description[0]) + single_element ("description", apps->val[i].app2_description); ++ if (apps->val[i].app2_class && apps->val[i].app2_class[0]) ++ single_element ("class", apps->val[i].app2_class); + } end_element (); + } + } end_element (); +diff --git a/inspector/virt-inspector.rng b/inspector/virt-inspector.rng +index 29c5798e1..e30686d4f 100644 +--- a/inspector/virt-inspector.rng ++++ b/inspector/virt-inspector.rng +@@ -212,6 +212,7 @@ + + + ++ + + + +diff --git a/m4/guestfs-libraries.m4 b/m4/guestfs-libraries.m4 +index 82e62d54f..4ca86161c 100644 +--- a/m4/guestfs-libraries.m4 ++++ b/m4/guestfs-libraries.m4 +@@ -20,7 +20,8 @@ dnl Any C libraries required by the libguestfs C library (not the daemon). + dnl Of course we need libguestfs. + dnl + dnl We need libguestfs 1.57.1 for guestfs_setfiles. +-PKG_CHECK_MODULES([LIBGUESTFS], [libguestfs >= 1.57.1]) ++dnl We need libguestfs 1.57.6 for guestfs_inspect_get_applications2 app2_class. ++PKG_CHECK_MODULES([LIBGUESTFS], [libguestfs >= 1.57.6]) + printf "libguestfs version is "; $PKG_CONFIG --modversion libguestfs + + dnl Test if it's GNU or XSI strerror_r. diff --git a/0018-inspector-Add-windows_group_policy-is-Windows-GPOs-d.patch b/0018-inspector-Add-windows_group_policy-is-Windows-GPOs-d.patch new file mode 100644 index 0000000..0a162c0 --- /dev/null +++ b/0018-inspector-Add-windows_group_policy-is-Windows-GPOs-d.patch @@ -0,0 +1,52 @@ +From c715ed2a3b82ce6cfcd44fbb8a29476621ebe049 Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Mon, 3 Nov 2025 15:56:56 +0000 +Subject: [PATCH] inspector: Add is Windows GPOs + detected in guest + +Fixes: https://issues.redhat.com/browse/RHEL-125955 +(cherry picked from commit d56129cfccedd26739ee5b813060e7547d977c53) +--- + inspector/inspector.c | 3 +++ + inspector/virt-inspector.rng | 1 + + m4/guestfs-libraries.m4 | 1 + + 3 files changed, 5 insertions(+) + +diff --git a/inspector/inspector.c b/inspector/inspector.c +index cccc209d0..6b5d50157 100644 +--- a/inspector/inspector.c ++++ b/inspector/inspector.c +@@ -439,6 +439,9 @@ output_root (xmlTextWriterPtr xo, char *root) + if (str) + single_element ("windows_current_control_set", str); + free (str); ++ i = guestfs_inspect_get_windows_group_policy (g, root); ++ if (i > 0) ++ empty_element ("windows_group_policy"); + guestfs_pop_error_handler (g); + + str = guestfs_inspect_get_hostname (g, root); +diff --git a/inspector/virt-inspector.rng b/inspector/virt-inspector.rng +index e30686d4f..90f74cf78 100644 +--- a/inspector/virt-inspector.rng ++++ b/inspector/virt-inspector.rng +@@ -35,6 +35,7 @@ + + + ++ + + + +diff --git a/m4/guestfs-libraries.m4 b/m4/guestfs-libraries.m4 +index 4ca86161c..02f2281f8 100644 +--- a/m4/guestfs-libraries.m4 ++++ b/m4/guestfs-libraries.m4 +@@ -21,6 +21,7 @@ dnl Of course we need libguestfs. + dnl + dnl We need libguestfs 1.57.1 for guestfs_setfiles. + dnl We need libguestfs 1.57.6 for guestfs_inspect_get_applications2 app2_class. ++dnl We need libguestfs 1.57.6 for guestfs_inspect_get_windows_group_policy + PKG_CHECK_MODULES([LIBGUESTFS], [libguestfs >= 1.57.6]) + printf "libguestfs version is "; $PKG_CONFIG --modversion libguestfs + diff --git a/0019-inspector-For-xfs-try-to-find-and-print-the-filesyst.patch b/0019-inspector-For-xfs-try-to-find-and-print-the-filesyst.patch new file mode 100644 index 0000000..5332fa6 --- /dev/null +++ b/0019-inspector-For-xfs-try-to-find-and-print-the-filesyst.patch @@ -0,0 +1,139 @@ +From d1ca20bde34a4085f3ec7e3e418d01bc4deb2698 Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Mon, 26 Jan 2026 15:26:23 +0000 +Subject: [PATCH] inspector: For xfs, try to find and print the filesystem + version + +RHEL 7.0, 7.1 and (possibly*) 7.2 used XFS version 4. New versions of +RHEL use XFS v5. + +Support even for opening version 4 filesystems was removed in RHEL 10 +(and will be removed altogether from the Linux kernel in 2030). This +prevents virt-v2v conversions or libguestfs in general from accessing +those filesystems. + +Therefore it's a good idea to be able to tell the XFS filesystem +version and print that in virt-inspector output. + +The new output will look like: + + + + xfs + 35904e42-4e3d-40c7-a4ef-213786c18339 + + +To work this requires libguestfs >= 1.59.2 (with guestfs_xfs_info2). +Older versions of libguestfs, or if we cannot tell the version, will +not have the version attribute. + +* = The virt-builder rhel-7.2 image definitely uses XFS v4, but it may +have been built from an early (pre-)release of 7.2. Later RHEL 7.2 +seems to use XFS v5. + +Fixes: https://issues.redhat.com/browse/RHEL-144074 +(cherry picked from commit bb210ca4330bc307d972201faf713d4c3c49fc6a) +--- + inspector/inspector.c | 54 ++++++++++++++++++++++++++++++++---- + inspector/virt-inspector.rng | 9 +++++- + 2 files changed, 56 insertions(+), 7 deletions(-) + +diff --git a/inspector/inspector.c b/inspector/inspector.c +index 6b5d50157..fc6b9f0d0 100644 +--- a/inspector/inspector.c ++++ b/inspector/inspector.c +@@ -569,6 +569,42 @@ output_mountpoints (xmlTextWriterPtr xo, char *root) + } end_element (); + } + ++static const char * ++get_filesystem_version (const char *dev, const char *fs_type) ++{ ++ const char *version = NULL; ++ ++#ifdef GUESTFS_HAVE_XFS_INFO2 ++ /* For type=xfs, try to guess the filesystem version. */ ++ if (STREQ (fs_type, "xfs")) { ++ CLEANUP_FREE_STRING_LIST char **hash = NULL; ++ size_t i; ++ ++ guestfs_push_error_handler (g, NULL, NULL); ++ ++ hash = guestfs_xfs_info2 (g, dev); ++ if (hash) { ++ for (i = 0; hash[i] != NULL; i += 2) { ++ if (STREQ (hash[i], "meta-data.crc")) { ++ if (STREQ (hash[i+1], "0")) ++ version = "4"; ++ else if (STREQ (hash[i+1], "1")) ++ version = "5"; ++ break; ++ } ++ /* If new XFS versions are added in future then we can test ++ * for new fields here ... ++ */ ++ } ++ } ++ ++ guestfs_pop_error_handler (g); ++ } ++#endif /* GUESTFS_HAVE_XFS_INFO2 */ ++ ++ return version; ++} ++ + static void + output_filesystems (xmlTextWriterPtr xo, char *root) + { +@@ -586,19 +622,25 @@ output_filesystems (xmlTextWriterPtr xo, char *root) + + start_element ("filesystems") { + for (i = 0; filesystems[i] != NULL; ++i) { +- str = guestfs_canonical_device_name (g, filesystems[i]); +- if (!str) ++ CLEANUP_FREE char *dev = ++ guestfs_canonical_device_name (g, filesystems[i]); ++ if (!dev) + exit (EXIT_FAILURE); + + start_element ("filesystem") { +- attribute ("dev", str); +- free (str); ++ attribute ("dev", dev); + + guestfs_push_error_handler (g, NULL, NULL); + + str = guestfs_vfs_type (g, filesystems[i]); +- if (str && str[0]) +- single_element ("type", str); ++ if (str && str[0]) { ++ const char *version = get_filesystem_version (dev, str); ++ start_element ("type") { ++ if (version) ++ attribute ("version", version); ++ string (str); ++ } end_element (); ++ } + free (str); + + str = guestfs_vfs_label (g, filesystems[i]); +diff --git a/inspector/virt-inspector.rng b/inspector/virt-inspector.rng +index 90f74cf78..aeec082ff 100644 +--- a/inspector/virt-inspector.rng ++++ b/inspector/virt-inspector.rng +@@ -175,7 +175,14 @@ + + + +- ++ ++ ++ ++ ++ ++ ++ ++ + + + diff --git a/0020-Sort-some-entries-in-.gitignore-into-order.patch b/0020-Sort-some-entries-in-.gitignore-into-order.patch new file mode 100644 index 0000000..c28e522 --- /dev/null +++ b/0020-Sort-some-entries-in-.gitignore-into-order.patch @@ -0,0 +1,35 @@ +From 83d338a7691d6c1ef169fb62011854bb9b450917 Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Thu, 5 Feb 2026 11:57:33 +0000 +Subject: [PATCH] Sort some entries in .gitignore into order + +(cherry picked from commit 9d7161da76160b2e5d1f3488b3f0a652554d2077) +--- + .gitignore | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/.gitignore b/.gitignore +index 94f77ac74..023686435 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -81,9 +81,9 @@ Makefile.in + /cat/virt-ls + /cat/virt-tail + /config.cache +-/config.log + /config.h + /config.h.in ++/config.log + /config.sh + /config.status + /configure +@@ -112,8 +112,8 @@ Makefile.in + /ocaml-dep.sh + /ocaml-link.sh + /po-docs/*/*.pod +-/podwrapper.pl + /po/*.gmo ++/podwrapper.pl + /resize/.depend + /resize/virt-resize + /run diff --git a/0021-build-Add-NULL-as-a-convenient-list-terminator.patch b/0021-build-Add-NULL-as-a-convenient-list-terminator.patch new file mode 100644 index 0000000..42448af --- /dev/null +++ b/0021-build-Add-NULL-as-a-convenient-list-terminator.patch @@ -0,0 +1,35 @@ +From 590c1aae4d7baadd21ff862c96d4516f52ae9c32 Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Mon, 22 Sep 2025 10:58:29 +0100 +Subject: [PATCH] build: Add $(NULL) as a convenient list terminator + +When building lists of things in Makefiles it's convenient to have a +list terminator to avoid hanging backslash problems. eg: + + EXTRA_DIST = \ + thing1 \ + thing2 \ + $(NULL) + +Cherry picked from virt-v2v commit 09b86c07bf19beba9ccb8fcca0ebfae34dd56406 + +(cherry picked from commit dad8c0d3803dcbc91e22ca35c630d9e8a01df81b) +--- + common-rules.mk | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/common-rules.mk b/common-rules.mk +index 4df1e33f1..7a116c827 100644 +--- a/common-rules.mk ++++ b/common-rules.mk +@@ -20,6 +20,10 @@ + + -include $(top_builddir)/localenv + ++# Convenient way to terminate lists in Makefiles, so that we avoid ++# problems with dangling backslashes. ++NULL = ++ + # Files that should universally be removed by 'make clean'. Note if + # there is any case in any subdirectory where a file should not be + # removed by 'make clean', it should not be listed here! diff --git a/0022-Move-virt-filesystems-virt-log-virt-ls-virt-tail-to-.patch b/0022-Move-virt-filesystems-virt-log-virt-ls-virt-tail-to-.patch new file mode 100644 index 0000000..f34a947 --- /dev/null +++ b/0022-Move-virt-filesystems-virt-log-virt-ls-virt-tail-to-.patch @@ -0,0 +1,1032 @@ +From 4a1f7d22d35438b73974e293a1f481977c8c8a67 Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Thu, 5 Feb 2026 11:56:40 +0000 +Subject: [PATCH] Move virt-filesystems, virt-log, virt-ls, virt-tail to new + directories + +For historical reasons these unrelated tools were all placed into the +cat/ subdirectory (with virt-cat). As this makes no sense, move them +to their own directories. + +(cherry picked from commit 291cc520ffbe2b5986067b2ce754c3a532066d85) +--- + .gitignore | 10 +- + Makefile.am | 3 +- + cat/Makefile.am | 205 +----------------- + cat/test-docs.sh | 9 - + configure.ac | 4 + + filesystems/Makefile.am | 88 ++++++++ + {cat => filesystems}/filesystems.c | 0 + filesystems/test-docs.sh | 26 +++ + {cat => filesystems}/test-virt-filesystems.sh | 0 + {cat => filesystems}/virt-filesystems.pod | 0 + log/Makefile.am | 83 +++++++ + {cat => log}/log.c | 0 + log/test-docs.sh | 26 +++ + {cat => log}/test-virt-log.sh | 0 + {cat => log}/virt-log.pod | 0 + ls/Makefile.am | 85 ++++++++ + {cat => ls}/ls.c | 0 + ls/test-docs.sh | 27 +++ + {cat => ls}/test-virt-ls.sh | 0 + {cat => ls}/virt-ls.pod | 0 + po-docs/podfiles | 8 +- + po/POTFILES | 8 +- + run.in | 4 + + tail/Makefile.am | 84 +++++++ + {cat => tail}/tail.c | 0 + tail/test-docs.sh | 26 +++ + {cat => tail}/test-virt-tail.sh | 0 + {cat => tail}/virt-tail.pod | 0 + 28 files changed, 476 insertions(+), 220 deletions(-) + create mode 100644 filesystems/Makefile.am + rename {cat => filesystems}/filesystems.c (100%) + create mode 100755 filesystems/test-docs.sh + rename {cat => filesystems}/test-virt-filesystems.sh (100%) + rename {cat => filesystems}/virt-filesystems.pod (100%) + create mode 100644 log/Makefile.am + rename {cat => log}/log.c (100%) + create mode 100755 log/test-docs.sh + rename {cat => log}/test-virt-log.sh (100%) + rename {cat => log}/virt-log.pod (100%) + create mode 100644 ls/Makefile.am + rename {cat => ls}/ls.c (100%) + create mode 100755 ls/test-docs.sh + rename {cat => ls}/test-virt-ls.sh (100%) + rename {cat => ls}/virt-ls.pod (100%) + create mode 100644 tail/Makefile.am + rename {cat => tail}/tail.c (100%) + create mode 100755 tail/test-docs.sh + rename {cat => tail}/test-virt-tail.sh (100%) + rename {cat => tail}/virt-tail.pod (100%) + +diff --git a/.gitignore b/.gitignore +index 023686435..ed850a20f 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -76,10 +76,6 @@ Makefile.in + /builder/virt-index-validate + /builder/*.xz + /cat/virt-cat +-/cat/virt-filesystems +-/cat/virt-log +-/cat/virt-ls +-/cat/virt-tail + /config.cache + /config.h + /config.h.in +@@ -92,17 +88,20 @@ Makefile.in + /customize/test-settings-*.sh + /customize/virt-customize + /df/virt-df ++/diff/virt-diff + /drivers/.depend + /drivers/hwdata_config.ml + /drivers/virt-drivers +-/diff/virt-diff + /edit/virt-edit ++/filesystems/virt-filesystems + /format/virt-format + /get-kernel/.depend + /get-kernel/virt-get-kernel + /inspector/actual-*.xml + /inspector/virt-inspector + /libtool ++/log/virt-log ++/ls/virt-ls + /m4/libtool.m4 + /m4/ltoptions.m4 + /m4/ltsugar.m4 +@@ -128,6 +127,7 @@ Makefile.in + /sysprep/sysprep-operations.pod + /sysprep/sysprep_operation_*.mli + /sysprep/virt-sysprep ++/tail/virt-tail + /test-data/blank-disks/blank-disk-* + /test-data/fake-virtio-win/fake-virtio-win.iso + /test-data/phony-guests/archlinux.img +diff --git a/Makefile.am b/Makefile.am +index b831ce7fe..7be3c1496 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -51,7 +51,8 @@ SUBDIRS += test-data + SUBDIRS += tests + + # virt-tools in C. +-SUBDIRS += align cat diff df edit format inspector make-fs ++SUBDIRS += align cat diff df edit filesystems format inspector ++SUBDIRS += log ls make-fs tail + + if HAVE_OCAML + # OCaml tools. Note 'common/ml*' and 'customize' contain shared code +diff --git a/cat/Makefile.am b/cat/Makefile.am +index 5569da185..483bf0913 100644 +--- a/cat/Makefile.am ++++ b/cat/Makefile.am +@@ -1,5 +1,5 @@ +-# libguestfs virt-cat, virt-filesystems, virt-log, virt-ls and virt-tail. +-# Copyright (C) 2010-2025 Red Hat Inc. ++# libguestfs virt-cat ++# Copyright (C) 2010-2026 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 +@@ -21,19 +21,11 @@ EXTRA_DIST = \ + test-docs.sh \ + test-virt-cat.sh \ + virt-cat.pod \ +- test-virt-filesystems.sh \ +- virt-filesystems.pod \ +- test-virt-log.sh \ +- virt-log.pod \ +- test-virt-ls.sh \ +- virt-ls.pod \ +- test-virt-tail.sh \ +- virt-tail.pod ++ $(NULL) + +-bin_PROGRAMS = virt-cat virt-filesystems virt-log virt-ls virt-tail ++bin_PROGRAMS = virt-cat + +-virt_cat_SOURCES = \ +- cat.c ++virt_cat_SOURCES = cat.c + + virt_cat_CPPFLAGS = \ + -DGUESTFS_NO_DEPRECATED=1 \ +@@ -62,135 +54,10 @@ virt_cat_LDADD = \ + $(LTLIBINTL) \ + ../gnulib/lib/libgnu.la + +-virt_filesystems_SOURCES = \ +- filesystems.c +- +-virt_filesystems_CPPFLAGS = \ +- -DGUESTFS_NO_DEPRECATED=1 \ +- -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ +- -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ +- -I$(top_srcdir)/common/structs -I$(top_builddir)/common/structs \ +- -I$(top_srcdir)/lib -I$(top_builddir)/lib \ +- -I$(top_srcdir)/include \ +- -I$(top_srcdir)/common/options -I$(top_builddir)/common/options \ +- -I$(top_srcdir)/common/windows -I$(top_builddir)/common/windows \ +- -I$(srcdir)/../gnulib/lib -I../gnulib/lib +- +-virt_filesystems_CFLAGS = \ +- $(WARN_CFLAGS) $(WERROR_CFLAGS) \ +- $(LIBXML2_CFLAGS) \ +- $(LIBGUESTFS_CFLAGS) +- +-virt_filesystems_LDADD = \ +- $(top_builddir)/common/options/liboptions.la \ +- $(top_builddir)/common/windows/libwindows.la \ +- $(top_builddir)/common/structs/libstructs.la \ +- $(top_builddir)/common/utils/libutils.la \ +- $(LIBGUESTFS_LIBS) \ +- $(LIBXML2_LIBS) \ +- $(LIBVIRT_LIBS) \ +- $(LTLIBINTL) \ +- ../gnulib/lib/libgnu.la +- +-virt_log_SOURCES = \ +- log.c +- +-virt_log_CPPFLAGS = \ +- -DGUESTFS_NO_DEPRECATED=1 \ +- -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ +- -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ +- -I$(top_srcdir)/common/structs -I$(top_builddir)/common/structs \ +- -I$(top_srcdir)/lib -I$(top_builddir)/lib \ +- -I$(top_srcdir)/include \ +- -I$(top_srcdir)/common/options -I$(top_builddir)/common/options \ +- -I$(top_srcdir)/common/windows -I$(top_builddir)/common/windows \ +- -I$(srcdir)/../gnulib/lib -I../gnulib/lib +- +-virt_log_CFLAGS = \ +- $(WARN_CFLAGS) $(WERROR_CFLAGS) \ +- $(LIBXML2_CFLAGS) \ +- $(LIBGUESTFS_CFLAGS) +- +-virt_log_LDADD = \ +- $(top_builddir)/common/options/liboptions.la \ +- $(top_builddir)/common/structs/libstructs.la \ +- $(top_builddir)/common/utils/libutils.la \ +- $(LIBGUESTFS_LIBS) \ +- $(LIBXML2_LIBS) \ +- $(LIBVIRT_LIBS) \ +- $(LTLIBINTL) \ +- ../gnulib/lib/libgnu.la +- +-virt_ls_SOURCES = \ +- ls.c +- +-virt_ls_CPPFLAGS = \ +- -DGUESTFS_NO_DEPRECATED=1 \ +- -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ +- -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ +- -I$(top_srcdir)/common/structs -I$(top_builddir)/common/structs \ +- -I$(top_srcdir)/lib -I$(top_builddir)/lib \ +- -I$(top_srcdir)/include \ +- -I$(top_srcdir)/common/visit \ +- -I$(top_srcdir)/common/options -I$(top_builddir)/common/options \ +- -I$(top_srcdir)/common/windows -I$(top_builddir)/common/windows \ +- -I$(srcdir)/../gnulib/lib -I../gnulib/lib +- +-virt_ls_CFLAGS = \ +- $(WARN_CFLAGS) $(WERROR_CFLAGS) \ +- $(LIBXML2_CFLAGS) \ +- $(LIBGUESTFS_CFLAGS) +- +-virt_ls_LDADD = \ +- $(top_builddir)/common/options/liboptions.la \ +- $(top_builddir)/common/visit/libvisit.la \ +- $(top_builddir)/common/structs/libstructs.la \ +- $(top_builddir)/common/utils/libutils.la \ +- $(LIBGUESTFS_LIBS) \ +- $(LIBXML2_LIBS) \ +- $(LIBVIRT_LIBS) \ +- $(LTLIBINTL) \ +- ../gnulib/lib/libgnu.la +- +-virt_tail_SOURCES = \ +- tail.c +- +-virt_tail_CPPFLAGS = \ +- -DGUESTFS_NO_DEPRECATED=1 \ +- -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ +- -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ +- -I$(top_srcdir)/common/structs -I$(top_builddir)/common/structs \ +- -I$(top_srcdir)/lib -I$(top_builddir)/lib \ +- -I$(top_srcdir)/include \ +- -I$(top_srcdir)/common/options -I$(top_builddir)/common/options \ +- -I$(top_srcdir)/common/windows -I$(top_builddir)/common/windows \ +- -I$(srcdir)/../gnulib/lib -I../gnulib/lib +- +-virt_tail_CFLAGS = \ +- $(WARN_CFLAGS) $(WERROR_CFLAGS) \ +- $(LIBXML2_CFLAGS) \ +- $(LIBGUESTFS_CFLAGS) +- +-virt_tail_LDADD = \ +- $(top_builddir)/common/options/liboptions.la \ +- $(top_builddir)/common/windows/libwindows.la \ +- $(top_builddir)/common/structs/libstructs.la \ +- $(top_builddir)/common/utils/libutils.la \ +- $(LIBGUESTFS_LIBS) \ +- $(LIBXML2_LIBS) \ +- $(LIBVIRT_LIBS) \ +- $(LTLIBINTL) \ +- ../gnulib/lib/libgnu.la +- + # Manual pages and HTML files for the website. +-man_MANS = virt-cat.1 virt-filesystems.1 virt-log.1 virt-ls.1 virt-tail.1 ++man_MANS = virt-cat.1 + +-noinst_DATA = \ +- $(top_builddir)/website/virt-cat.1.html \ +- $(top_builddir)/website/virt-filesystems.1.html \ +- $(top_builddir)/website/virt-log.1.html \ +- $(top_builddir)/website/virt-ls.1.html \ +- $(top_builddir)/website/virt-tail.1.html ++noinst_DATA = $(top_builddir)/website/virt-cat.1.html + + virt-cat.1 $(top_builddir)/website/virt-cat.1.html: stamp-virt-cat.pod + +@@ -204,54 +71,6 @@ stamp-virt-cat.pod: virt-cat.pod + $< + touch $@ + +-virt-filesystems.1 $(top_builddir)/website/virt-filesystems.1.html: stamp-virt-filesystems.pod +- +-stamp-virt-filesystems.pod: virt-filesystems.pod +- $(PODWRAPPER) \ +- --man virt-filesystems.1 \ +- --html $(top_builddir)/website/virt-filesystems.1.html \ +- --path $(top_srcdir)/common/options \ +- --license GPLv2+ \ +- --warning safe \ +- $< +- touch $@ +- +-virt-log.1 $(top_builddir)/website/virt-log.1.html: stamp-virt-log.pod +- +-stamp-virt-log.pod: virt-log.pod +- $(PODWRAPPER) \ +- --man virt-log.1 \ +- --html $(top_builddir)/website/virt-log.1.html \ +- --path $(top_srcdir)/common/options \ +- --license GPLv2+ \ +- --warning safe \ +- $< +- touch $@ +- +-virt-ls.1 $(top_builddir)/website/virt-ls.1.html: stamp-virt-ls.pod +- +-stamp-virt-ls.pod: virt-ls.pod +- $(PODWRAPPER) \ +- --man virt-ls.1 \ +- --html $(top_builddir)/website/virt-ls.1.html \ +- --path $(top_srcdir)/common/options \ +- --license GPLv2+ \ +- --warning safe \ +- $< +- touch $@ +- +-virt-tail.1 $(top_builddir)/website/virt-tail.1.html: stamp-virt-tail.pod +- +-stamp-virt-tail.pod: virt-tail.pod +- $(PODWRAPPER) \ +- --man virt-tail.1 \ +- --html $(top_builddir)/website/virt-tail.1.html \ +- --path $(top_srcdir)/common/options \ +- --license GPLv2+ \ +- --warning safe \ +- $< +- touch $@ +- + # Tests. + + TESTS_ENVIRONMENT = $(top_builddir)/run --test +@@ -259,15 +78,7 @@ TESTS_ENVIRONMENT = $(top_builddir)/run --test + TESTS = \ + test-docs.sh \ + test-virt-cat.sh \ +- test-virt-filesystems.sh \ +- test-virt-log.sh \ +- test-virt-ls.sh \ +- test-virt-tail.sh ++ $(NULL) + + check-valgrind: + $(MAKE) VG="@VG@" check +- +-check-valgrind-local-guests: +- for g in $(GUESTS); do \ +- $(top_builddir)/run --test @VG@ virt-filesystems -c "$(libvirt_ro_uri)" -d "$$g" --all --long -h --uuid || exit $$?; \ +- done +diff --git a/cat/test-docs.sh b/cat/test-docs.sh +index bca349b52..31a17f31a 100755 +--- a/cat/test-docs.sh ++++ b/cat/test-docs.sh +@@ -24,12 +24,3 @@ skip_if_skipped + + $top_srcdir/podcheck.pl "$srcdir/virt-cat.pod" virt-cat \ + --path $top_srcdir/common/options +-$top_srcdir/podcheck.pl "$srcdir/virt-filesystems.pod" virt-filesystems \ +- --path $top_srcdir/common/options +-$top_srcdir/podcheck.pl "$srcdir/virt-log.pod" virt-log \ +- --path $top_srcdir/common/options +-$top_srcdir/podcheck.pl "$srcdir/virt-ls.pod" virt-ls \ +- --path $top_srcdir/common/options \ +- --ignore=--checksums,--extra-stat,--time,--uid +-$top_srcdir/podcheck.pl "$srcdir/virt-tail.pod" virt-tail \ +- --path $top_srcdir/common/options +diff --git a/configure.ac b/configure.ac +index 015c16823..998be7b9f 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -157,10 +157,13 @@ AC_CONFIG_FILES([Makefile + drivers/Makefile + drivers/hwdata_config.ml + edit/Makefile ++ filesystems/Makefile + format/Makefile + get-kernel/Makefile + gnulib/lib/Makefile + inspector/Makefile ++ log/Makefile ++ ls/Makefile + make-fs/Makefile + po-docs/Makefile + po-docs/ja/Makefile +@@ -169,6 +172,7 @@ AC_CONFIG_FILES([Makefile + resize/Makefile + sparsify/Makefile + sysprep/Makefile ++ tail/Makefile + test-data/Makefile + test-data/binaries/Makefile + test-data/blank-disks/Makefile +diff --git a/filesystems/Makefile.am b/filesystems/Makefile.am +new file mode 100644 +index 000000000..17e3b9c6f +--- /dev/null ++++ b/filesystems/Makefile.am +@@ -0,0 +1,88 @@ ++# libguestfs virt-filesystems ++# Copyright (C) 2010-2026 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 $(top_srcdir)/subdir-rules.mk ++ ++EXTRA_DIST = \ ++ test-docs.sh \ ++ test-virt-filesystems.sh \ ++ virt-filesystems.pod \ ++ $(NULL) ++ ++bin_PROGRAMS = virt-filesystems ++ ++virt_filesystems_SOURCES = filesystems.c ++ ++virt_filesystems_CPPFLAGS = \ ++ -DGUESTFS_NO_DEPRECATED=1 \ ++ -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ ++ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ ++ -I$(top_srcdir)/common/structs -I$(top_builddir)/common/structs \ ++ -I$(top_srcdir)/lib -I$(top_builddir)/lib \ ++ -I$(top_srcdir)/include \ ++ -I$(top_srcdir)/common/options -I$(top_builddir)/common/options \ ++ -I$(top_srcdir)/common/windows -I$(top_builddir)/common/windows \ ++ -I$(srcdir)/../gnulib/lib -I../gnulib/lib ++ ++virt_filesystems_CFLAGS = \ ++ $(WARN_CFLAGS) $(WERROR_CFLAGS) \ ++ $(LIBXML2_CFLAGS) \ ++ $(LIBGUESTFS_CFLAGS) ++ ++virt_filesystems_LDADD = \ ++ $(top_builddir)/common/options/liboptions.la \ ++ $(top_builddir)/common/windows/libwindows.la \ ++ $(top_builddir)/common/structs/libstructs.la \ ++ $(top_builddir)/common/utils/libutils.la \ ++ $(LIBGUESTFS_LIBS) \ ++ $(LIBXML2_LIBS) \ ++ $(LIBVIRT_LIBS) \ ++ $(LTLIBINTL) \ ++ ../gnulib/lib/libgnu.la ++ ++# Manual pages and HTML files for the website. ++man_MANS = virt-filesystems.1 ++ ++noinst_DATA = $(top_builddir)/website/virt-filesystems.1.html ++ ++virt-filesystems.1 $(top_builddir)/website/virt-filesystems.1.html: stamp-virt-filesystems.pod ++ ++stamp-virt-filesystems.pod: virt-filesystems.pod ++ $(PODWRAPPER) \ ++ --man virt-filesystems.1 \ ++ --html $(top_builddir)/website/virt-filesystems.1.html \ ++ --path $(top_srcdir)/common/options \ ++ --license GPLv2+ \ ++ --warning safe \ ++ $< ++ touch $@ ++# Tests. ++ ++TESTS_ENVIRONMENT = $(top_builddir)/run --test ++ ++TESTS = \ ++ test-docs.sh \ ++ test-virt-filesystems.sh \ ++ $(NULL) ++ ++check-valgrind: ++ $(MAKE) VG="@VG@" check ++ ++check-valgrind-local-guests: ++ for g in $(GUESTS); do \ ++ $(top_builddir)/run --test @VG@ virt-filesystems -c "$(libvirt_ro_uri)" -d "$$g" --all --long -h --uuid || exit $$?; \ ++ done +diff --git a/cat/filesystems.c b/filesystems/filesystems.c +similarity index 100% +rename from cat/filesystems.c +rename to filesystems/filesystems.c +diff --git a/filesystems/test-docs.sh b/filesystems/test-docs.sh +new file mode 100755 +index 000000000..03c37dbb8 +--- /dev/null ++++ b/filesystems/test-docs.sh +@@ -0,0 +1,26 @@ ++#!/bin/bash - ++# libguestfs ++# 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. ++ ++source ../tests/functions.sh ++set -e ++set -x ++ ++skip_if_skipped ++ ++$top_srcdir/podcheck.pl "$srcdir/virt-filesystems.pod" virt-filesystems \ ++ --path $top_srcdir/common/options +diff --git a/cat/test-virt-filesystems.sh b/filesystems/test-virt-filesystems.sh +similarity index 100% +rename from cat/test-virt-filesystems.sh +rename to filesystems/test-virt-filesystems.sh +diff --git a/cat/virt-filesystems.pod b/filesystems/virt-filesystems.pod +similarity index 100% +rename from cat/virt-filesystems.pod +rename to filesystems/virt-filesystems.pod +diff --git a/log/Makefile.am b/log/Makefile.am +new file mode 100644 +index 000000000..790dcbf98 +--- /dev/null ++++ b/log/Makefile.am +@@ -0,0 +1,83 @@ ++# libguestfs virt-log ++# Copyright (C) 2010-2026 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 $(top_srcdir)/subdir-rules.mk ++ ++EXTRA_DIST = \ ++ test-docs.sh \ ++ test-virt-log.sh \ ++ virt-log.pod \ ++ $(NULL) ++ ++bin_PROGRAMS = virt-log ++ ++virt_log_SOURCES = log.c ++ ++virt_log_CPPFLAGS = \ ++ -DGUESTFS_NO_DEPRECATED=1 \ ++ -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ ++ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ ++ -I$(top_srcdir)/common/structs -I$(top_builddir)/common/structs \ ++ -I$(top_srcdir)/lib -I$(top_builddir)/lib \ ++ -I$(top_srcdir)/include \ ++ -I$(top_srcdir)/common/options -I$(top_builddir)/common/options \ ++ -I$(top_srcdir)/common/windows -I$(top_builddir)/common/windows \ ++ -I$(srcdir)/../gnulib/lib -I../gnulib/lib ++ ++virt_log_CFLAGS = \ ++ $(WARN_CFLAGS) $(WERROR_CFLAGS) \ ++ $(LIBXML2_CFLAGS) \ ++ $(LIBGUESTFS_CFLAGS) ++ ++virt_log_LDADD = \ ++ $(top_builddir)/common/options/liboptions.la \ ++ $(top_builddir)/common/structs/libstructs.la \ ++ $(top_builddir)/common/utils/libutils.la \ ++ $(LIBGUESTFS_LIBS) \ ++ $(LIBXML2_LIBS) \ ++ $(LIBVIRT_LIBS) \ ++ $(LTLIBINTL) \ ++ ../gnulib/lib/libgnu.la ++ ++# Manual pages and HTML files for the website. ++man_MANS = virt-log.1 ++ ++noinst_DATA = $(top_builddir)/website/virt-log.1.html ++ ++virt-log.1 $(top_builddir)/website/virt-log.1.html: stamp-virt-log.pod ++ ++stamp-virt-log.pod: virt-log.pod ++ $(PODWRAPPER) \ ++ --man virt-log.1 \ ++ --html $(top_builddir)/website/virt-log.1.html \ ++ --path $(top_srcdir)/common/options \ ++ --license GPLv2+ \ ++ --warning safe \ ++ $< ++ touch $@ ++ ++# Tests. ++ ++TESTS_ENVIRONMENT = $(top_builddir)/run --test ++ ++TESTS = \ ++ test-docs.sh \ ++ test-virt-log.sh \ ++ $(NULL) ++ ++check-valgrind: ++ $(MAKE) VG="@VG@" check +diff --git a/cat/log.c b/log/log.c +similarity index 100% +rename from cat/log.c +rename to log/log.c +diff --git a/log/test-docs.sh b/log/test-docs.sh +new file mode 100755 +index 000000000..fff8ccf30 +--- /dev/null ++++ b/log/test-docs.sh +@@ -0,0 +1,26 @@ ++#!/bin/bash - ++# libguestfs ++# 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. ++ ++source ../tests/functions.sh ++set -e ++set -x ++ ++skip_if_skipped ++ ++$top_srcdir/podcheck.pl "$srcdir/virt-log.pod" virt-log \ ++ --path $top_srcdir/common/options +diff --git a/cat/test-virt-log.sh b/log/test-virt-log.sh +similarity index 100% +rename from cat/test-virt-log.sh +rename to log/test-virt-log.sh +diff --git a/cat/virt-log.pod b/log/virt-log.pod +similarity index 100% +rename from cat/virt-log.pod +rename to log/virt-log.pod +diff --git a/ls/Makefile.am b/ls/Makefile.am +new file mode 100644 +index 000000000..c4b64b912 +--- /dev/null ++++ b/ls/Makefile.am +@@ -0,0 +1,85 @@ ++# libguestfs virt-log ++# Copyright (C) 2010-2026 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 $(top_srcdir)/subdir-rules.mk ++ ++EXTRA_DIST = \ ++ test-docs.sh \ ++ test-virt-ls.sh \ ++ virt-ls.pod \ ++ $(NULL) ++ ++bin_PROGRAMS = virt-ls ++ ++virt_ls_SOURCES = ls.c ++ ++virt_ls_CPPFLAGS = \ ++ -DGUESTFS_NO_DEPRECATED=1 \ ++ -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ ++ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ ++ -I$(top_srcdir)/common/structs -I$(top_builddir)/common/structs \ ++ -I$(top_srcdir)/lib -I$(top_builddir)/lib \ ++ -I$(top_srcdir)/include \ ++ -I$(top_srcdir)/common/visit \ ++ -I$(top_srcdir)/common/options -I$(top_builddir)/common/options \ ++ -I$(top_srcdir)/common/windows -I$(top_builddir)/common/windows \ ++ -I$(srcdir)/../gnulib/lib -I../gnulib/lib ++ ++virt_ls_CFLAGS = \ ++ $(WARN_CFLAGS) $(WERROR_CFLAGS) \ ++ $(LIBXML2_CFLAGS) \ ++ $(LIBGUESTFS_CFLAGS) ++ ++virt_ls_LDADD = \ ++ $(top_builddir)/common/options/liboptions.la \ ++ $(top_builddir)/common/visit/libvisit.la \ ++ $(top_builddir)/common/structs/libstructs.la \ ++ $(top_builddir)/common/utils/libutils.la \ ++ $(LIBGUESTFS_LIBS) \ ++ $(LIBXML2_LIBS) \ ++ $(LIBVIRT_LIBS) \ ++ $(LTLIBINTL) \ ++ ../gnulib/lib/libgnu.la ++ ++# Manual pages and HTML files for the website. ++man_MANS = virt-ls.1 ++ ++noinst_DATA = $(top_builddir)/website/virt-ls.1.html ++ ++virt-ls.1 $(top_builddir)/website/virt-ls.1.html: stamp-virt-ls.pod ++ ++stamp-virt-ls.pod: virt-ls.pod ++ $(PODWRAPPER) \ ++ --man virt-ls.1 \ ++ --html $(top_builddir)/website/virt-ls.1.html \ ++ --path $(top_srcdir)/common/options \ ++ --license GPLv2+ \ ++ --warning safe \ ++ $< ++ touch $@ ++ ++# Tests. ++ ++TESTS_ENVIRONMENT = $(top_builddir)/run --test ++ ++TESTS = \ ++ test-docs.sh \ ++ test-virt-ls.sh \ ++ $(NULL) ++ ++check-valgrind: ++ $(MAKE) VG="@VG@" check +diff --git a/cat/ls.c b/ls/ls.c +similarity index 100% +rename from cat/ls.c +rename to ls/ls.c +diff --git a/ls/test-docs.sh b/ls/test-docs.sh +new file mode 100755 +index 000000000..e5e398368 +--- /dev/null ++++ b/ls/test-docs.sh +@@ -0,0 +1,27 @@ ++#!/bin/bash - ++# libguestfs ++# 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. ++ ++source ../tests/functions.sh ++set -e ++set -x ++ ++skip_if_skipped ++ ++$top_srcdir/podcheck.pl "$srcdir/virt-ls.pod" virt-ls \ ++ --path $top_srcdir/common/options \ ++ --ignore=--checksums,--extra-stat,--time,--uid +diff --git a/cat/test-virt-ls.sh b/ls/test-virt-ls.sh +similarity index 100% +rename from cat/test-virt-ls.sh +rename to ls/test-virt-ls.sh +diff --git a/cat/virt-ls.pod b/ls/virt-ls.pod +similarity index 100% +rename from cat/virt-ls.pod +rename to ls/virt-ls.pod +diff --git a/po-docs/podfiles b/po-docs/podfiles +index 69ad52768..7fb4162dc 100644 +--- a/po-docs/podfiles ++++ b/po-docs/podfiles +@@ -3,10 +3,6 @@ builder/virt-builder-repository.pod + builder/virt-builder.pod + builder/virt-index-validate.pod + cat/virt-cat.pod +-cat/virt-filesystems.pod +-cat/virt-log.pod +-cat/virt-ls.pod +-cat/virt-tail.pod + common/mlcustomize/customize-options.pod + common/mlcustomize/customize-synopsis.pod + common/mlcustomize/v2v-customize-options.pod +@@ -23,13 +19,17 @@ docs/guestfs-tools-release-notes-1.52.pod + docs/guestfs-tools-release-notes-1.54.pod + drivers/virt-drivers.pod + edit/virt-edit.pod ++filesystems/virt-filesystems.pod + format/virt-format.pod + get-kernel/virt-get-kernel.pod + inspector/virt-inspector.pod ++log/virt-log.pod ++ls/virt-ls.pod + make-fs/virt-make-fs.pod + resize/virt-resize.pod + sparsify/virt-sparsify.pod + sysprep/sysprep-extra-options.pod + sysprep/sysprep-operations.pod + sysprep/virt-sysprep.pod ++tail/virt-tail.pod + win-reg/virt-win-reg.in +diff --git a/po/POTFILES b/po/POTFILES +index 396729c30..c540c4e29 100644 +--- a/po/POTFILES ++++ b/po/POTFILES +@@ -7,10 +7,6 @@ builder/index-validate.c + builder/pxzcat-c.c + builder/setlocale-c.c + cat/cat.c +-cat/filesystems.c +-cat/log.c +-cat/ls.c +-cat/tail.c + common/edit/file-edit.c + common/mlcustomize/crypt-c.c + common/mlcustomize/perl_edit-c.c +@@ -53,6 +49,10 @@ df/main.c + df/output.c + diff/diff.c + edit/edit.c ++filesystems/filesystems.c + format/format.c + inspector/inspector.c ++log/log.c ++ls/ls.c + make-fs/make-fs.c ++tail/tail.c +diff --git a/run.in b/run.in +index 741434a5a..e52d66c97 100755 +--- a/run.in ++++ b/run.in +@@ -78,14 +78,18 @@ prepend PATH "$b/diff" + prepend PATH "$b/drivers" + prepend PATH "$b/edit" + prepend PATH "$b/erlang" ++prepend PATH "$b/filesystems" + prepend PATH "$b/format" + prepend PATH "$b/fuse" + prepend PATH "$b/get-kernel" + prepend PATH "$b/inspector" ++prepend PATH "$b/log" ++prepend PATH "$b/ls" + prepend PATH "$b/make-fs" + prepend PATH "$b/resize" + prepend PATH "$b/sparsify" + prepend PATH "$b/sysprep" ++prepend PATH "$b/tail" + prepend PATH "$b/win-reg" + export PATH + +diff --git a/tail/Makefile.am b/tail/Makefile.am +new file mode 100644 +index 000000000..ac822b841 +--- /dev/null ++++ b/tail/Makefile.am +@@ -0,0 +1,84 @@ ++# libguestfs virt-tail. ++# Copyright (C) 2010-2026 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 $(top_srcdir)/subdir-rules.mk ++ ++EXTRA_DIST = \ ++ test-docs.sh \ ++ test-virt-tail.sh \ ++ virt-tail.pod \ ++ $(NULL) ++ ++bin_PROGRAMS = virt-tail ++ ++virt_tail_SOURCES = tail.c ++ ++virt_tail_CPPFLAGS = \ ++ -DGUESTFS_NO_DEPRECATED=1 \ ++ -DLOCALEBASEDIR=\""$(datadir)/locale"\" \ ++ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ ++ -I$(top_srcdir)/common/structs -I$(top_builddir)/common/structs \ ++ -I$(top_srcdir)/lib -I$(top_builddir)/lib \ ++ -I$(top_srcdir)/include \ ++ -I$(top_srcdir)/common/options -I$(top_builddir)/common/options \ ++ -I$(top_srcdir)/common/windows -I$(top_builddir)/common/windows \ ++ -I$(srcdir)/../gnulib/lib -I../gnulib/lib ++ ++virt_tail_CFLAGS = \ ++ $(WARN_CFLAGS) $(WERROR_CFLAGS) \ ++ $(LIBXML2_CFLAGS) \ ++ $(LIBGUESTFS_CFLAGS) ++ ++virt_tail_LDADD = \ ++ $(top_builddir)/common/options/liboptions.la \ ++ $(top_builddir)/common/windows/libwindows.la \ ++ $(top_builddir)/common/structs/libstructs.la \ ++ $(top_builddir)/common/utils/libutils.la \ ++ $(LIBGUESTFS_LIBS) \ ++ $(LIBXML2_LIBS) \ ++ $(LIBVIRT_LIBS) \ ++ $(LTLIBINTL) \ ++ ../gnulib/lib/libgnu.la ++ ++# Manual pages and HTML files for the website. ++man_MANS = virt-tail.1 ++ ++noinst_DATA = $(top_builddir)/website/virt-tail.1.html ++ ++virt-tail.1 $(top_builddir)/website/virt-tail.1.html: stamp-virt-tail.pod ++ ++stamp-virt-tail.pod: virt-tail.pod ++ $(PODWRAPPER) \ ++ --man virt-tail.1 \ ++ --html $(top_builddir)/website/virt-tail.1.html \ ++ --path $(top_srcdir)/common/options \ ++ --license GPLv2+ \ ++ --warning safe \ ++ $< ++ touch $@ ++ ++# Tests. ++ ++TESTS_ENVIRONMENT = $(top_builddir)/run --test ++ ++TESTS = \ ++ test-docs.sh \ ++ test-virt-tail.sh \ ++ $(NULL) ++ ++check-valgrind: ++ $(MAKE) VG="@VG@" check +diff --git a/cat/tail.c b/tail/tail.c +similarity index 100% +rename from cat/tail.c +rename to tail/tail.c +diff --git a/tail/test-docs.sh b/tail/test-docs.sh +new file mode 100755 +index 000000000..ffa114be6 +--- /dev/null ++++ b/tail/test-docs.sh +@@ -0,0 +1,26 @@ ++#!/bin/bash - ++# libguestfs ++# 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. ++ ++source ../tests/functions.sh ++set -e ++set -x ++ ++skip_if_skipped ++ ++$top_srcdir/podcheck.pl "$srcdir/virt-tail.pod" virt-tail \ ++ --path $top_srcdir/common/options +diff --git a/cat/test-virt-tail.sh b/tail/test-virt-tail.sh +similarity index 100% +rename from cat/test-virt-tail.sh +rename to tail/test-virt-tail.sh +diff --git a/cat/virt-tail.pod b/tail/virt-tail.pod +similarity index 100% +rename from cat/virt-tail.pod +rename to tail/virt-tail.pod diff --git a/0023-filesystems-Optionally-display-filesystem-version.patch b/0023-filesystems-Optionally-display-filesystem-version.patch new file mode 100644 index 0000000..c303720 --- /dev/null +++ b/0023-filesystems-Optionally-display-filesystem-version.patch @@ -0,0 +1,473 @@ +From 52a921e1276748f9de7115078fd2999e750299c0 Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Thu, 5 Feb 2026 13:17:38 +0000 +Subject: [PATCH] filesystems: Optionally display filesystem version + +Unlike the recent change to virt-inspector (commit bb210ca433 +("inspector: For xfs, try to find and print the filesystem version")) +this does not require the ability to mount the XFS v4 filesystem so it +will work on RHEL 10. + + $ virt-filesystems -a rhel-7.0.img --long --fs-version + Name Type VFS Label Size Parent FSVersion + /dev/sda1 filesystem ext4 - 510873600 - - + /dev/sda3 filesystem xfs - 4820303872 - 4 + + $ virt-filesystems -a rhel-7.3.img --long --fs-version + Name Type VFS Label Size Parent FSVersion + /dev/sda1 filesystem ext4 - 510873600 - - + /dev/sda3 filesystem xfs - 4820303872 - 5 + +Fixes: https://issues.redhat.com/browse/RHEL-144074 +(cherry picked from commit fc61c9439d84fdaab71e1628eb5b95f18ff40ce1) +--- + .gitignore | 1 + + filesystems/Makefile.am | 6 +++- + filesystems/filesystems.c | 46 ++++++++++++++++++------ + filesystems/utils.c | 60 ++++++++++++++++++++++++++++++++ + filesystems/utils.h | 30 ++++++++++++++++ + filesystems/virt-filesystems.pod | 12 ++++++- + inspector/Makefile.am | 6 +++- + inspector/inspector.c | 39 ++------------------- + 8 files changed, 150 insertions(+), 50 deletions(-) + create mode 100644 filesystems/utils.c + create mode 100644 filesystems/utils.h + +diff --git a/.gitignore b/.gitignore +index ed850a20f..0b26da37b 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -18,6 +18,7 @@ + stamp-*.pod + + .deps ++.dirstamp + .libs + Makefile + Makefile.in +diff --git a/filesystems/Makefile.am b/filesystems/Makefile.am +index 17e3b9c6f..625592ff3 100644 +--- a/filesystems/Makefile.am ++++ b/filesystems/Makefile.am +@@ -25,7 +25,11 @@ EXTRA_DIST = \ + + bin_PROGRAMS = virt-filesystems + +-virt_filesystems_SOURCES = filesystems.c ++virt_filesystems_SOURCES = \ ++ filesystems.c \ ++ utils.c \ ++ utils.h \ ++ $(NULL) + + virt_filesystems_CPPFLAGS = \ + -DGUESTFS_NO_DEPRECATED=1 \ +diff --git a/filesystems/filesystems.c b/filesystems/filesystems.c +index ecaeb0bb2..e86d92be0 100644 +--- a/filesystems/filesystems.c ++++ b/filesystems/filesystems.c +@@ -39,6 +39,7 @@ + #include "structs-cleanups.h" + #include "options.h" + #include "display-options.h" ++#include "utils.h" + + /* These globals are shared with options.c. */ + guestfs_h *g; +@@ -75,7 +76,8 @@ static int output = 0; + #define COLUMN_SIZE 32 /* bytes, or human-readable if -h */ + #define COLUMN_PARENTS 64 + #define COLUMN_UUID 128 /* if --uuid */ +-#define NR_COLUMNS 8 ++#define COLUMN_FS_VERSION 256 /* if --fs-version */ ++#define NR_COLUMNS 9 + static int columns; + + static void do_output_title (void); +@@ -111,6 +113,8 @@ usage (int status) + " --extra Display swap and data filesystems\n" + " --filesystems Display mountable filesystems\n" + " --format[=raw|..] Force disk format for -a option\n" ++ " --fs-version|--fs-versions\n" ++ " Add filesystem version to --long output\n" + " -h|--human-readable Human-readable sizes in --long output\n" + " --help Display brief help\n" + " --keys-from-stdin Read passphrases from stdin\n" +@@ -157,6 +161,8 @@ main (int argc, char *argv[]) + { "extra", 0, 0, 0 }, + { "filesystems", 0, 0, 0 }, + { "format", 2, 0, 0 }, ++ { "fs-version", 0, 0, 0 }, ++ { "fs-versions", 0, 0, 0 }, + { "help", 0, 0, HELP_OPTION }, + { "human-readable", 0, 0, 'h' }, + { "keys-from-stdin", 0, 0, 0 }, +@@ -191,6 +197,7 @@ main (int argc, char *argv[]) + int no_title = 0; /* --no-title */ + int long_mode = 0; /* --long|-l */ + int uuid = 0; /* --uuid */ ++ int fs_version = 0; /* --fs-version */ + int title; + + g = guestfs_create (); +@@ -227,6 +234,9 @@ main (int argc, char *argv[]) + output |= OUTPUT_FILESYSTEMS_EXTRA; + } else if (STREQ (long_options[option_index].name, "filesystems")) { + output |= OUTPUT_FILESYSTEMS; ++ } else if (STREQ (long_options[option_index].name, "fs-version") || ++ STREQ (long_options[option_index].name, "fs-versions")) { ++ fs_version = 1; + } else if (STREQ (long_options[option_index].name, "logical-volumes") || + STREQ (long_options[option_index].name, "logvols") || + STREQ (long_options[option_index].name, "lvs")) { +@@ -337,6 +347,8 @@ main (int argc, char *argv[]) + columns |= COLUMN_MBR; + if (uuid) + columns |= COLUMN_UUID; ++ if (fs_version) ++ columns |= COLUMN_FS_VERSION; + } + + /* Display title by default only in long mode. */ +@@ -379,7 +391,7 @@ static void do_output_pvs (void); + static void do_output_partitions (void); + static void do_output_blockdevs (void); + +-static void write_row (const char *name, const char *type, const char *vfs_type, const char *vfs_label, int mbr_id, int64_t size, char **parents, const char *uuid); ++static void write_row (const char *name, const char *type, const char *vfs_type, const char *vfs_label, int mbr_id, int64_t size, char **parents, const char *uuid, const char *fs_version); + static void write_row_strings (char **strings, size_t len); + + static char **no_parents (void); +@@ -410,6 +422,8 @@ do_output_title (void) + headings[len++] = "Parent"; + if ((columns & COLUMN_UUID)) + headings[len++] = "UUID"; ++ if ((columns & COLUMN_FS_VERSION)) ++ headings[len++] = "FSVersion"; + assert (len <= NR_COLUMNS); + + write_row_strings ((char **) headings, len); +@@ -450,13 +464,15 @@ do_output_filesystems (void) + exit (EXIT_FAILURE); + + for (i = 0; fses[i] != NULL; i += 2) { ++ const char *fs_type = fses[i+1]; + CLEANUP_FREE char *dev = NULL, *vfs_label = NULL, *vfs_uuid = NULL; ++ CLEANUP_FREE char *fs_version = NULL; + CLEANUP_FREE_STRING_LIST char **parents = NULL; + int64_t size = -1; + + /* Skip swap and unknown, unless --extra flag was given. */ + if (!(output & OUTPUT_FILESYSTEMS_EXTRA) && +- (STREQ (fses[i+1], "swap") || STREQ (fses[i+1], "unknown"))) ++ (STREQ (fs_type, "swap") || STREQ (fs_type, "unknown"))) + continue; + + dev = guestfs_canonical_device_name (g, fses[i]); +@@ -486,6 +502,12 @@ do_output_filesystems (void) + error (EXIT_FAILURE, errno, "strdup"); + } + } ++ if ((columns & COLUMN_FS_VERSION)) { ++ const char *version = get_filesystem_version (g, fses[i], fs_type); ++ fs_version = strdup (version ? version : ""); ++ if (fs_version == NULL) ++ error (EXIT_FAILURE, errno, "strdup"); ++ } + if ((columns & COLUMN_SIZE)) { + CLEANUP_FREE char *device = guestfs_mountable_device (g, fses[i]); + CLEANUP_FREE char *subvolume = NULL; +@@ -533,7 +555,7 @@ do_output_filesystems (void) + parents = no_parents (); + + write_row (dev, "filesystem", +- fses[i+1], vfs_label, -1, size, parents, vfs_uuid); ++ fs_type, vfs_label, -1, size, parents, vfs_uuid, fs_version); + } + } + +@@ -573,7 +595,7 @@ do_output_lvs (void) + } + + write_row (lvs[i], "lv", +- NULL, NULL, -1, size, (char **) parents, uuid); ++ NULL, NULL, -1, size, (char **) parents, uuid, NULL); + } + } + +@@ -601,7 +623,8 @@ do_output_vgs (void) + parents = parents_of_vg (vgs->val[i].vg_name); + + write_row (name, "vg", +- NULL, NULL, -1, (int64_t) vgs->val[i].vg_size, parents, uuid); ++ NULL, NULL, -1, (int64_t) vgs->val[i].vg_size, parents, uuid, ++ NULL); + } + } + +@@ -649,7 +672,7 @@ do_output_pvs (void) + uuid[32] = '\0'; + write_row (dev, "pv", + NULL, NULL, -1, (int64_t) pvs->val[i].pv_size, +- (char **) parents, uuid); ++ (char **) parents, uuid, NULL); + } + } + +@@ -715,7 +738,7 @@ do_output_partitions (void) + } + + write_row (dev, "partition", +- NULL, NULL, mbr_id, size, (char **) parents, NULL); ++ NULL, NULL, mbr_id, size, (char **) parents, NULL, NULL); + } + } + +@@ -749,7 +772,7 @@ do_output_blockdevs (void) + parents = no_parents (); + + write_row (dev, "device", +- NULL, NULL, -1, size, parents, NULL); ++ NULL, NULL, -1, size, parents, NULL, NULL); + } + } + +@@ -882,7 +905,8 @@ parents_of_vg (char *vg) + static void + write_row (const char *name, const char *type, + const char *vfs_type, const char *vfs_label, int mbr_id, +- int64_t size, char **parents, const char *uuid) ++ int64_t size, char **parents, const char *uuid, ++ const char *fs_version) + { + const char *strings[NR_COLUMNS]; + CLEANUP_FREE char *parents_str = NULL; +@@ -930,6 +954,8 @@ write_row (const char *name, const char *type, + } + if ((columns & COLUMN_UUID)) + strings[len++] = uuid; ++ if ((columns & COLUMN_FS_VERSION)) ++ strings[len++] = fs_version; + assert (len <= NR_COLUMNS); + + write_row_strings ((char **) strings, len); +diff --git a/filesystems/utils.c b/filesystems/utils.c +new file mode 100644 +index 000000000..9de32f1bd +--- /dev/null ++++ b/filesystems/utils.c +@@ -0,0 +1,60 @@ ++/* Utility function used by virt-filesystems and virrt-inspector ++ * Copyright (C) 2026 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 ++ ++#include "guestfs.h" ++ ++#include "guestfs-utils.h" ++#include "utils.h" ++ ++const char * ++get_filesystem_version (guestfs_h *g, const char *dev, const char *fs_type) ++{ ++ const char *version = NULL; ++ ++#ifdef GUESTFS_HAVE_XFS_INFO2 ++ /* For type=xfs, try to guess the filesystem version. */ ++ if (STREQ (fs_type, "xfs")) { ++ CLEANUP_FREE_STRING_LIST char **hash = NULL; ++ size_t i; ++ ++ guestfs_push_error_handler (g, NULL, NULL); ++ ++ hash = guestfs_xfs_info2 (g, dev); ++ if (hash) { ++ for (i = 0; hash[i] != NULL; i += 2) { ++ if (STREQ (hash[i], "meta-data.crc")) { ++ if (STREQ (hash[i+1], "0")) ++ version = "4"; ++ else if (STREQ (hash[i+1], "1")) ++ version = "5"; ++ break; ++ } ++ /* If new XFS versions are added in future then we can test ++ * for new fields here ... ++ */ ++ } ++ } ++ ++ guestfs_pop_error_handler (g); ++ } ++#endif /* GUESTFS_HAVE_XFS_INFO2 */ ++ ++ return version; ++} +diff --git a/filesystems/utils.h b/filesystems/utils.h +new file mode 100644 +index 000000000..284f8e1ac +--- /dev/null ++++ b/filesystems/utils.h +@@ -0,0 +1,30 @@ ++/* Utility function used by virt-filesystems and virrt-inspector ++ * Copyright (C) 2026 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. ++ */ ++ ++#ifndef GUESTFS_FILESYSTEMS_UTILS_H ++#define GUESTFS_FILESYSTEMS_UTILS_H ++ ++#include "guestfs.h" ++ ++/* For XFS, return the filesystem version (eg. "4" or "5"). This may ++ * return NULL if no filesystem version is known. ++ */ ++const char *get_filesystem_version (guestfs_h *g, ++ const char *dev, const char *fs_type); ++ ++#endif /* GUESTFS_FILESYSTEMS_UTILS_H */ +diff --git a/filesystems/virt-filesystems.pod b/filesystems/virt-filesystems.pod +index 7955d6a84..c72ae3cf7 100644 +--- a/filesystems/virt-filesystems.pod ++++ b/filesystems/virt-filesystems.pod +@@ -197,6 +197,14 @@ If you have untrusted raw-format guest disk images, you should use + this option to specify the disk format. This avoids a possible + security problem with malicious guests (CVE-2010-3851). + ++=item B<--fs-version> ++ ++=item B<--fs-versions> ++ ++In I<--long> mode, display the filesystem version. Currently this ++only has any effect for XFS, displaying either C<4> (for XFS v4) or ++C<5> (for XFS v5). XFS v4 support will be removed from Linux in 2030. ++ + =item B<-h> + + =item B<--human-readable> +@@ -221,7 +229,9 @@ external programs. + Use I<-h> if you want sizes to be displayed in human-readable format. + The default is to show raw numbers of I. + +-Use I<--uuid> to display UUIDs too. ++Use I<--fs-version> to display filesystem versions. ++ ++Use I<--uuid> to display UUIDs. + + =item B<--lvs> + +diff --git a/inspector/Makefile.am b/inspector/Makefile.am +index 04c608f44..637a10415 100644 +--- a/inspector/Makefile.am ++++ b/inspector/Makefile.am +@@ -48,7 +48,10 @@ dist_doc_DATA = \ + bin_PROGRAMS = virt-inspector + + virt_inspector_SOURCES = \ +- inspector.c ++ ../filesystems/utils.c \ ++ ../filesystems/utils.h \ ++ inspector.c \ ++ $(NULL) + + virt_inspector_CPPFLAGS = \ + -DGUESTFS_NO_DEPRECATED=1 \ +@@ -56,6 +59,7 @@ virt_inspector_CPPFLAGS = \ + -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ + -I$(top_srcdir)/common/structs -I$(top_builddir)/common/structs \ + -I$(top_srcdir)/lib -I$(top_builddir)/lib \ ++ -I$(top_srcdir)/filesystems -I$(top_builddir)/filesystems \ + -I$(top_srcdir)/include \ + -I$(top_srcdir)/common/options -I$(top_builddir)/common/options \ + -I$(top_srcdir)/fish \ +diff --git a/inspector/inspector.c b/inspector/inspector.c +index fc6b9f0d0..be34e8794 100644 +--- a/inspector/inspector.c ++++ b/inspector/inspector.c +@@ -44,6 +44,7 @@ + #include "options.h" + #include "display-options.h" + #include "libxml2-writer-macros.h" ++#include "utils.h" + + /* Currently open libguestfs handle. */ + guestfs_h *g; +@@ -569,42 +570,6 @@ output_mountpoints (xmlTextWriterPtr xo, char *root) + } end_element (); + } + +-static const char * +-get_filesystem_version (const char *dev, const char *fs_type) +-{ +- const char *version = NULL; +- +-#ifdef GUESTFS_HAVE_XFS_INFO2 +- /* For type=xfs, try to guess the filesystem version. */ +- if (STREQ (fs_type, "xfs")) { +- CLEANUP_FREE_STRING_LIST char **hash = NULL; +- size_t i; +- +- guestfs_push_error_handler (g, NULL, NULL); +- +- hash = guestfs_xfs_info2 (g, dev); +- if (hash) { +- for (i = 0; hash[i] != NULL; i += 2) { +- if (STREQ (hash[i], "meta-data.crc")) { +- if (STREQ (hash[i+1], "0")) +- version = "4"; +- else if (STREQ (hash[i+1], "1")) +- version = "5"; +- break; +- } +- /* If new XFS versions are added in future then we can test +- * for new fields here ... +- */ +- } +- } +- +- guestfs_pop_error_handler (g); +- } +-#endif /* GUESTFS_HAVE_XFS_INFO2 */ +- +- return version; +-} +- + static void + output_filesystems (xmlTextWriterPtr xo, char *root) + { +@@ -634,7 +599,7 @@ output_filesystems (xmlTextWriterPtr xo, char *root) + + str = guestfs_vfs_type (g, filesystems[i]); + if (str && str[0]) { +- const char *version = get_filesystem_version (dev, str); ++ const char *version = get_filesystem_version (g, dev, str); + start_element ("type") { + if (version) + attribute ("version", version); diff --git a/0024-Update-common-submodule.patch b/0024-Update-common-submodule.patch new file mode 100644 index 0000000..d18975f --- /dev/null +++ b/0024-Update-common-submodule.patch @@ -0,0 +1,28 @@ +From 3051cfbed63f8f8865b4a3374a474031dc916faa Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Wed, 10 Dec 2025 13:47:00 +0000 +Subject: [PATCH] Update common submodule + +Pull in this change: + + Richard W.M. Jones (1): + mlcustomize/firstboot.ml: Print %USERNAME% and %USERDOMAIN% + +(cherry picked from commit 7ee462f9f9c2235b2d3dacbf3cd1006060108635) +--- + common | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +Submodule common b54ba2031..d03dd8547: +diff --git a/common/mlcustomize/firstboot.ml b/common/mlcustomize/firstboot.ml +index 360c33d67..c1738e749 100644 +--- a/common/mlcustomize/firstboot.ml ++++ b/common/mlcustomize/firstboot.ml +@@ -295,6 +295,7 @@ exit /b + + :main + echo starting firstboot service ++echo effective user: %USERNAME% domain: %USERDOMAIN% + + if not exist "%%scripts_done%%" ( + mkdir "%%scripts_done%%" diff --git a/0025-Update-common-submodule.patch b/0025-Update-common-submodule.patch new file mode 100644 index 0000000..fe13945 --- /dev/null +++ b/0025-Update-common-submodule.patch @@ -0,0 +1,25 @@ +From f0e429ec0eadf74594fd3202f77b7191963df771 Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Wed, 10 Dec 2025 14:06:10 +0000 +Subject: [PATCH] Update common submodule + +Fixes: commit 7ee462f9f9c2235b2d3dacbf3cd1006060108635 +(cherry picked from commit 6436529d116dd415b26841d7b4bcee7564a97b0f) +--- + common | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +Submodule common d03dd8547..cf663a835: +diff --git a/common/mlcustomize/firstboot.ml b/common/mlcustomize/firstboot.ml +index c1738e749..f29884c88 100644 +--- a/common/mlcustomize/firstboot.ml ++++ b/common/mlcustomize/firstboot.ml +@@ -295,7 +295,7 @@ exit /b + + :main + echo starting firstboot service +-echo effective user: %USERNAME% domain: %USERDOMAIN% ++echo effective user: %%USERNAME%% domain: %%USERDOMAIN%% + + if not exist "%%scripts_done%%" ( + mkdir "%%scripts_done%%" diff --git a/0026-Update-common-submodule.patch b/0026-Update-common-submodule.patch new file mode 100644 index 0000000..9ce33f8 --- /dev/null +++ b/0026-Update-common-submodule.patch @@ -0,0 +1,638 @@ +From 001cb3dddd4ddc218d02558a46070545bbca3c98 Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Mon, 16 Mar 2026 09:38:37 +0000 +Subject: [PATCH] Update common submodule + + Cole Robinson (2): + virtio-win: Install blnsvr.exe to C:\Windows\Drivers\VirtIO + build: mark some ocaml commands with $(AM_V_GEN) + + Richard W.M. Jones (7): + mlpcre: Add optional PCRE_ANCHORED flag when compiling expressions + mlstdutils: Export List.assoc_opt + mlcustomize/firstboot.ml: Be more careful about quoting + mlcustomize: Create a sentinel file when firstboot completes + mlutils/c_utils-c.c: Throw Unix_error on failure + Use caml_unix_error instead of unix_error + mlutils/unix_utils.ml: statvfs_is_network_filesystem should not be noalloc + + grass-lu (1): + Add support for Kylin, NeoKylin and Anolis Linux + + shivanayak (5): + common: fix FILE stream leak in worker_thread on guestfs_create failure (#27) + options: fix memory leak in read_key on getline failure (#30) + edit: check WIFEXITED before WEXITSTATUS in edit_file_editor and edit_file_perl (#31) + mlutils: fix pattern leak in mkdtemp wrapper on failure + qemuopts: check for write errors in qemuopts_to_channel and qemuopts_to_config_channel (#33) + +(cherry picked from commit a735b6b45de2afaddd4d4287cd666a5afc9ec964) +--- + common | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +Submodule common cf663a835..c8e64b3ff: +diff --git a/common/edit/file-edit.c b/common/edit/file-edit.c +index ef56ed13d..a10d61c99 100644 +--- a/common/edit/file-edit.c ++++ b/common/edit/file-edit.c +@@ -119,7 +119,7 @@ edit_file_editor (guestfs_h *g, const char *filename, const char *editor, + fprintf (stderr, "%s\n", cmd); + + r = system (cmd); +- if (r == -1 || WEXITSTATUS (r) != 0) { ++ if (r == -1 || !WIFEXITED (r) || WEXITSTATUS (r) != 0) { + perror (cmd); + return -1; + } +@@ -194,7 +194,7 @@ edit_file_perl (guestfs_h *g, const char *filename, const char *perl_expr, + fprintf (stderr, "%s\n", cmd); + + r = system (cmd); +- if (r == -1 || WEXITSTATUS (r) != 0) ++ if (r == -1 || !WIFEXITED (r) || WEXITSTATUS (r) != 0) + return -1; + + if (rename (outfile, tmpfilename) == -1) { +diff --git a/common/mlcustomize/Makefile.am b/common/mlcustomize/Makefile.am +index 351524c80..3bdff2a63 100644 +--- a/common/mlcustomize/Makefile.am ++++ b/common/mlcustomize/Makefile.am +@@ -142,7 +142,7 @@ endif + libmlcustomize_a_DEPENDENCIES = $(OBJECTS) + + $(MLCUSTOMIZE_CMA): $(OBJECTS) libmlcustomize.a +- $(OCAMLFIND) mklib $(OCAMLPACKAGES) \ ++ $(AM_V_GEN) $(OCAMLFIND) mklib $(OCAMLPACKAGES) \ + $(OBJECTS) $(libmlcustomize_a_OBJECTS) -o mlcustomize + + # Tests. +diff --git a/common/mlcustomize/crypt-c.c b/common/mlcustomize/crypt-c.c +index e358018cd..e1bc740d1 100644 +--- a/common/mlcustomize/crypt-c.c ++++ b/common/mlcustomize/crypt-c.c +@@ -47,7 +47,7 @@ virt_customize_crypt (value keyv, value saltv) + */ + r = crypt (String_val (keyv), String_val (saltv)); + if (r == NULL) +- unix_error (errno, (char *) "crypt", Nothing); ++ caml_unix_error (errno, (char *) "crypt", Nothing); + rv = caml_copy_string (r); + + CAMLreturn (rv); +diff --git a/common/mlcustomize/firstboot.ml b/common/mlcustomize/firstboot.ml +index f29884c88..543e98fb1 100644 +--- a/common/mlcustomize/firstboot.ml ++++ b/common/mlcustomize/firstboot.ml +@@ -151,7 +151,7 @@ WantedBy=%s + and install_sysvinit_service g root distro major = + match distro with + | "fedora"|"rhel"|"centos"|"scientificlinux"|"oraclelinux"|"rocky"| +- "redhat-based"|"openeuler" -> ++ "redhat-based"|"openeuler"|"kylin"|"neokylin"|"anolis" -> + install_sysvinit_redhat g + | "opensuse"|"sles"|"suse-based" -> + install_sysvinit_suse g +@@ -271,6 +271,9 @@ module Windows = struct + (* Create a directory for firstboot scripts in the guest. *) + g#mkdir_p (firstboot_dir // "scripts"); + ++ (* Remove the sentinel file if it exists from a previous run. *) ++ g#rm_f (firstboot_dir // "complete"); ++ + (* Copy pvvxsvc or rhsrvany to the guest. *) + g#upload (virt_tools_data_dir () // srvany) (firstboot_dir // srvany); + +@@ -284,11 +287,11 @@ module Windows = struct + let firstboot_script = sprintf {|@echo off + + setlocal EnableDelayedExpansion +-set firstboot=%s +-set log=%%firstboot%%\log.txt ++set "firstboot=%s" ++set "log=%%firstboot%%\log.txt" + +-set scripts=%%firstboot%%\scripts +-set scripts_done=%%firstboot%%\scripts-done ++set "scripts=%%firstboot%%\scripts" ++set "scripts_done=%%firstboot%%\scripts-done" + + call :main >> "%%log%%" 2>&1 + exit /b +@@ -327,6 +330,10 @@ for %%%%f in ("%%scripts%%"\*.bat) do ( + ) + + :: Fallthrough here if there are no scripts. ++ ++:: Touch a sentinel file to say we have finished. ++type nul > "%%firstboot%%\complete" ++ + echo uninstalling firstboot service + "%%firstboot%%\%s" -s firstboot uninstall + |} firstboot_dir_win srvany in +diff --git a/common/mlcustomize/hostname.ml b/common/mlcustomize/hostname.ml +index d4a13d4d4..66f59a322 100644 +--- a/common/mlcustomize/hostname.ml ++++ b/common/mlcustomize/hostname.ml +@@ -37,7 +37,7 @@ let rec set_hostname (g : Guestfs.guestfs) root hostname = + true + + | "linux", ("rhel"|"centos"|"scientificlinux"|"oraclelinux"|"rocky"| +- "redhat-based"|"openeuler"), v ++ "redhat-based"|"openeuler"|"kylin"|"neokylin"|"anolis"), v + when v >= 7 -> + update_etc_hostname g hostname; + update_etc_machine_info g hostname; +@@ -50,7 +50,7 @@ let rec set_hostname (g : Guestfs.guestfs) root hostname = + true + + | "linux", ("fedora"|"rhel"|"centos"|"scientificlinux"|"oraclelinux"| +- "redhat-based"|"openeuler"), _ -> ++ "redhat-based"|"openeuler"|"kylin"|"neokylin"|"anolis"), _ -> + replace_line_in_file g "/etc/sysconfig/network" "HOSTNAME" hostname; + true + +diff --git a/common/mlcustomize/inject_virtio_win.ml b/common/mlcustomize/inject_virtio_win.ml +index 114df0641..a29380806 100644 +--- a/common/mlcustomize/inject_virtio_win.ml ++++ b/common/mlcustomize/inject_virtio_win.ml +@@ -254,13 +254,10 @@ and inject_qemu_ga ({ g; root } as t) = + + and inject_blnsvr ({ g; root } as t) = + (* Copy the files to the guest. *) +- let dir, dir_win = Firstboot.firstboot_dir g root in +- let dir_win = Option.value dir_win ~default:dir in +- let tempdir = sprintf "%s/Temp" dir in +- let tempdir_win = sprintf "%s\\Temp" dir_win in +- g#mkdir_p tempdir; ++ let driverdir = sprintf "%s/Drivers/VirtIO" t.i_windows_systemroot in ++ g#mkdir_p driverdir; + +- let files = copy_blnsvr t tempdir in ++ let files = copy_blnsvr t driverdir in + match files with + | [] -> false (* Didn't find or install anything. *) + +@@ -268,7 +265,7 @@ and inject_blnsvr ({ g; root } as t) = + * drivers/by-driver). Pick the first. + *) + | blnsvr :: _ -> +- configure_blnsvr t tempdir_win blnsvr; ++ configure_blnsvr t driverdir blnsvr; + true + + and add_guestor_to_registry t ((g, root) as reg) drv_name drv_pciid = +@@ -570,9 +567,9 @@ and configure_qemu_ga t tempdir_win files = + + Firstboot.add_firstboot_powershell t.g t.root "install-qemu-ga" !script + +-and configure_blnsvr t tempdir_win blnsvr = ++and configure_blnsvr t driverdir blnsvr = + let cmd = sprintf "\ + @echo off\n\ + echo Installing %s\n\ +- \"%s\\%s\" -i\n" blnsvr tempdir_win blnsvr in ++ \"%s\\%s\" -i\n" blnsvr driverdir blnsvr in + Firstboot.add_firstboot_script t.g t.root "install-blnsvr" cmd +diff --git a/common/mlcustomize/password.ml b/common/mlcustomize/password.ml +index 62f80b05f..8dba0358a 100644 +--- a/common/mlcustomize/password.ml ++++ b/common/mlcustomize/password.ml +@@ -190,6 +190,7 @@ and default_crypto g root = + (* Rolling distributions, which hopefully should be updated enough. *) + | ("archlinux"|"kalilinux"), _ -> `YESCRYPT + | ("voidlinux"|"openeuler"), _ -> `SHA512 ++ | ("kylin"|"neokylin"|"anolis"), _ -> `SHA512 + + | _, _ -> + let minor = g#inspect_get_minor_version root in +diff --git a/common/mldrivers/Makefile.am b/common/mldrivers/Makefile.am +index 714fc9f74..aac3da4f1 100644 +--- a/common/mldrivers/Makefile.am ++++ b/common/mldrivers/Makefile.am +@@ -94,7 +94,7 @@ endif + libmldrivers_a_DEPENDENCIES = $(OBJECTS) + + $(MLDRIVERS_CMA): $(OBJECTS) libmldrivers.a +- $(OCAMLFIND) mklib $(OCAMLPACKAGES) \ ++ $(AM_V_GEN) $(OCAMLFIND) mklib $(OCAMLPACKAGES) \ + $(OBJECTS) $(libmldrivers_a_OBJECTS) -o mldrivers + + # OCaml dependencies. +diff --git a/common/mlgettext/Makefile.am b/common/mlgettext/Makefile.am +index 712feb11b..275b15fa2 100644 +--- a/common/mlgettext/Makefile.am ++++ b/common/mlgettext/Makefile.am +@@ -71,11 +71,11 @@ endif + libmlgettext_a_DEPENDENCIES = $(OBJECTS) + + mlgettext.cma: $(BOBJECTS) +- $(OCAMLFIND) ocamlc $(OCAMLPACKAGES) -a $^ -o $@ ++ $(AM_V_GEN) $(OCAMLFIND) ocamlc $(OCAMLPACKAGES) -a $^ -o $@ + + if HAVE_OCAMLOPT + mlgettext.cmxa: $(XOBJECTS) +- $(OCAMLFIND) ocamlopt $(OCAMLPACKAGES) -a $^ -o $@ ++ $(AM_V_GEN) $(OCAMLFIND) ocamlopt $(OCAMLPACKAGES) -a $^ -o $@ + endif + + # Dependencies. +diff --git a/common/mlpcre/Makefile.am b/common/mlpcre/Makefile.am +index a1d8b0293..30a60f645 100644 +--- a/common/mlpcre/Makefile.am ++++ b/common/mlpcre/Makefile.am +@@ -83,7 +83,7 @@ endif + libmlpcre_a_DEPENDENCIES = $(OBJECTS) + + $(MLPCRE_CMA): $(OBJECTS) libmlpcre.a +- $(OCAMLFIND) mklib $(OCAMLPACKAGES) \ ++ $(AM_V_GEN) $(OCAMLFIND) mklib $(OCAMLPACKAGES) \ + $(OBJECTS) $(libmlpcre_a_OBJECTS) -cclib -lpcre2-8 -o mlpcre + + # Tests. +diff --git a/common/mlpcre/PCRE.ml b/common/mlpcre/PCRE.ml +index 077290ef1..33074af1c 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 b69a56ba9..0fdc2bd5f 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 3959fd566..11be15775 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/common/mlprogress/Makefile.am b/common/mlprogress/Makefile.am +index 3f3424e04..134c4ab4c 100644 +--- a/common/mlprogress/Makefile.am ++++ b/common/mlprogress/Makefile.am +@@ -83,7 +83,7 @@ endif + libmlprogress_a_DEPENDENCIES = $(OBJECTS) + + $(MLPROGRESS_CMA): $(OBJECTS) libmlprogress.a +- $(OCAMLFIND) mklib $(OCAMLPACKAGES) \ ++ $(AM_V_GEN) $(OCAMLFIND) mklib $(OCAMLPACKAGES) \ + $(OBJECTS) $(libmlprogress_a_OBJECTS) \ + -cclib -lprogress \ + -o mlprogress +diff --git a/common/mlstdutils/Makefile.am b/common/mlstdutils/Makefile.am +index b9632b078..0f881907a 100644 +--- a/common/mlstdutils/Makefile.am ++++ b/common/mlstdutils/Makefile.am +@@ -84,11 +84,11 @@ endif + libmlstdutils_a_DEPENDENCIES = $(OBJECTS) + + mlstdutils.cma: $(BOBJECTS) +- $(OCAMLFIND) ocamlc $(OCAMLPACKAGES) -a $^ -o $@ ++ $(AM_V_GEN) $(OCAMLFIND) ocamlc $(OCAMLPACKAGES) -a $^ -o $@ + + if HAVE_OCAMLOPT + mlstdutils.cmxa: $(XOBJECTS) +- $(OCAMLFIND) ocamlopt $(OCAMLPACKAGES) -a $^ -o $@ ++ $(AM_V_GEN) $(OCAMLFIND) ocamlopt $(OCAMLPACKAGES) -a $^ -o $@ + endif + + # Tests. +diff --git a/common/mlstdutils/std_utils.mli b/common/mlstdutils/std_utils.mli +index 6c1911da8..77cf107ed 100644 +--- a/common/mlstdutils/std_utils.mli ++++ b/common/mlstdutils/std_utils.mli +@@ -51,6 +51,7 @@ module List : sig + val find_all : ('a -> bool) -> 'a list -> 'a list + val partition : ('a -> bool) -> 'a list -> 'a list * 'a list + val assoc : 'a -> ('a * 'b) list -> 'b ++ val assoc_opt : 'a -> ('a * 'b) list -> 'b option + val assq : 'a -> ('a * 'b) list -> 'b + val mem_assoc : 'a -> ('a * 'b) list -> bool + val mem_assq : 'a -> ('a * 'b) list -> bool +diff --git a/common/mltools/Makefile.am b/common/mltools/Makefile.am +index e9e0398c7..44cfbb1b1 100644 +--- a/common/mltools/Makefile.am ++++ b/common/mltools/Makefile.am +@@ -161,7 +161,7 @@ endif + libmltools_a_DEPENDENCIES = $(OBJECTS) + + $(MLTOOLS_CMA): $(OBJECTS) libmltools.a +- $(OCAMLFIND) mklib $(OCAMLPACKAGES) \ ++ $(AM_V_GEN) $(OCAMLFIND) mklib $(OCAMLPACKAGES) \ + $(OBJECTS) $(libmltools_a_OBJECTS) -o mltools + + # Tests. +diff --git a/common/mltools/tools_utils-c.c b/common/mltools/tools_utils-c.c +index 4ff42e5d5..21f7469aa 100644 +--- a/common/mltools/tools_utils-c.c ++++ b/common/mltools/tools_utils-c.c +@@ -126,10 +126,10 @@ guestfs_int_mllib_rfc3339_date_time_string (value unitv) + size_t total = 0; + + if (clock_gettime (CLOCK_REALTIME, &ts) == -1) +- unix_error (errno, (char *) "clock_gettime", Val_unit); ++ caml_unix_error (errno, (char *) "clock_gettime", Val_unit); + + if (localtime_r (&ts.tv_sec, &tm) == NULL) +- unix_error (errno, (char *) "localtime_r", caml_copy_int64 (ts.tv_sec)); ++ caml_unix_error (errno, (char *) "localtime_r", caml_copy_int64 (ts.tv_sec)); + + /* Sadly strftime does not support nanoseconds, so what we do is: + * - stringify everything before the nanoseconds +@@ -141,17 +141,17 @@ guestfs_int_mllib_rfc3339_date_time_string (value unitv) + + ret = strftime (buf, sizeof (buf), "%Y-%m-%dT%H:%M:%S.", &tm); + if (ret == 0) +- unix_error (errno, (char *) "strftime", Val_unit); ++ caml_unix_error (errno, (char *) "strftime", Val_unit); + total += ret; + + ret = snprintf (buf + total, sizeof (buf) - total, "%09ld", ts.tv_nsec); + if (ret == 0) +- unix_error (errno, (char *) "sprintf", caml_copy_int64 (ts.tv_nsec)); ++ caml_unix_error (errno, (char *) "sprintf", caml_copy_int64 (ts.tv_nsec)); + total += ret; + + ret = strftime (buf + total, sizeof (buf) - total, "%z", &tm); + if (ret == 0) +- unix_error (errno, (char *) "strftime", Val_unit); ++ caml_unix_error (errno, (char *) "strftime", Val_unit); + total += ret; + + /* Move the timezone minutes one character to the right, moving the +diff --git a/common/mlutils/Makefile.am b/common/mlutils/Makefile.am +index d52cb9c86..084ce6304 100644 +--- a/common/mlutils/Makefile.am ++++ b/common/mlutils/Makefile.am +@@ -86,7 +86,7 @@ endif + libmlcutils_a_DEPENDENCIES = $(OBJECTS) + + $(MLCUTILS_CMA): $(OBJECTS) libmlcutils.a +- $(OCAMLFIND) mklib $(OCAMLPACKAGES) \ ++ $(AM_V_GEN) $(OCAMLFIND) mklib $(OCAMLPACKAGES) \ + $(OBJECTS) $(libmlcutils_a_OBJECTS) \ + -cclib -lutils \ + -o mlcutils +diff --git a/common/mlutils/c_utils-c.c b/common/mlutils/c_utils-c.c +index d9c1a4803..f0e2798f4 100644 +--- a/common/mlutils/c_utils-c.c ++++ b/common/mlutils/c_utils-c.c +@@ -68,7 +68,7 @@ guestfs_int_mlutils_shell_unquote (value strv) + + ret = guestfs_int_shell_unquote (String_val (strv)); + if (ret == NULL) +- unix_error (errno, (char *) "guestfs_int_shell_unquote", Nothing); ++ caml_unix_error (errno, (char *) "guestfs_int_shell_unquote", Nothing); + + retv = caml_copy_string (ret); + free (ret); +@@ -101,6 +101,8 @@ guestfs_int_mlutils_full_path (value dirv, value namev) + name = String_val (Field (namev, 0)); + + ret = guestfs_int_full_path (String_val (dirv), name); ++ if (ret == NULL) ++ caml_unix_error (errno, (char *) "guestfs_int_full_path", dirv); + rv = caml_copy_string (ret); + free (ret); + +diff --git a/common/mlutils/unix_utils-c.c b/common/mlutils/unix_utils-c.c +index ee5a37964..28df2c310 100644 +--- a/common/mlutils/unix_utils-c.c ++++ b/common/mlutils/unix_utils-c.c +@@ -147,7 +147,7 @@ guestfs_int_mllib_fnmatch (value patternv, value strv, value flagsv) + /* XXX The fnmatch specification doesn't mention what errors can + * be returned by fnmatch. Assume they are errnos for now. + */ +- unix_error (errno, (char *) "fnmatch", patternv); ++ caml_unix_error (errno, (char *) "fnmatch", patternv); + } + } + +@@ -180,16 +180,16 @@ guestfs_int_mllib_fsync_file (value filenamev) + /* Note to do fsync you have to open for write. */ + fd = open (filename, O_RDWR); + if (fd == -1) +- unix_error (errno, (char *) "open", filenamev); ++ caml_unix_error (errno, (char *) "open", filenamev); + + if (fsync (fd) == -1) { + err = errno; + close (fd); +- unix_error (err, (char *) "fsync", filenamev); ++ caml_unix_error (err, (char *) "fsync", filenamev); + } + + if (close (fd) == -1) +- unix_error (errno, (char *) "close", filenamev); ++ caml_unix_error (errno, (char *) "close", filenamev); + + CAMLreturn (Val_unit); + } +@@ -203,11 +203,14 @@ guestfs_int_mllib_mkdtemp (value val_pattern) + + pattern = strdup (String_val (val_pattern)); + if (pattern == NULL) +- unix_error (errno, (char *) "strdup", val_pattern); ++ caml_unix_error (errno, (char *) "strdup", val_pattern); + + ret = mkdtemp (pattern); +- if (ret == NULL) +- unix_error (errno, (char *) "mkdtemp", val_pattern); ++ if (ret == NULL) { ++ int err = errno; ++ free (pattern); ++ caml_unix_error (err, (char *) "mkdtemp", val_pattern); ++ } + + rv = caml_copy_string (ret); + free (pattern); +@@ -224,7 +227,7 @@ guestfs_int_mllib_realpath (value pathv) + + r = realpath (String_val (pathv), NULL); + if (r == NULL) +- unix_error (errno, (char *) "realpath", pathv); ++ caml_unix_error (errno, (char *) "realpath", pathv); + + rv = caml_copy_string (r); + free (r); +@@ -252,7 +255,7 @@ guestfs_int_mllib_statvfs_statvfs (value pathv) + struct statvfs buf; + + if (statvfs (String_val (pathv), &buf) == -1) +- unix_error (errno, (char *) "statvfs", pathv); ++ caml_unix_error (errno, (char *) "statvfs", pathv); + + f_bsize = buf.f_bsize; + f_frsize = buf.f_frsize; +@@ -276,7 +279,7 @@ guestfs_int_mllib_statvfs_statvfs (value pathv) + (PULARGE_INTEGER) &free_bytes_available, + (PULARGE_INTEGER) &total_number_of_bytes, + (PULARGE_INTEGER) &total_number_of_free_bytes)) +- unix_error (EIO, (char *) "statvfs: GetDiskFreeSpaceEx", pathv); ++ caml_unix_error (EIO, (char *) "statvfs: GetDiskFreeSpaceEx", pathv); + + /* XXX I couldn't determine how to get block size. MSDN has a + * unhelpful hard-coded list here: +@@ -341,15 +344,17 @@ guestfs_int_mllib_statvfs_statvfs (value pathv) + CAMLreturn (rv); + } + +-/* NB: This is a [@@noalloc] call. */ + value + guestfs_int_mllib_statvfs_is_network_filesystem (value pathv) + { ++ CAMLparam1 (pathv); ++ CAMLlocal1 (rv); ++ + #ifdef HAVE_STATFS + struct statfs buf; + + if (statfs (String_val (pathv), &buf) == -1) +- unix_error (errno, (char *) "statvfs", pathv); ++ caml_unix_error (errno, (char *) "statvfs", pathv); + + /* Some but not all of these are defined in . */ + #ifndef CIFS_MAGIC_NUMBER +@@ -362,12 +367,14 @@ guestfs_int_mllib_statvfs_is_network_filesystem (value pathv) + #define SMB_SUPER_MAGIC 0x517b + #endif + +- return Val_bool ((unsigned int) buf.f_type == CIFS_MAGIC_NUMBER || +- (unsigned int) buf.f_type == NFS_SUPER_MAGIC || +- (unsigned int) buf.f_type == SMB_SUPER_MAGIC); ++ rv = Val_bool ((unsigned int) buf.f_type == CIFS_MAGIC_NUMBER || ++ (unsigned int) buf.f_type == NFS_SUPER_MAGIC || ++ (unsigned int) buf.f_type == SMB_SUPER_MAGIC); + #else +- return Val_bool (0); ++ rv = Val_bool (0); + #endif ++ ++ CAMLreturn (rv); + } + + /* NB: This is a [@@noalloc] call. */ +diff --git a/common/mlutils/unix_utils.ml b/common/mlutils/unix_utils.ml +index b79144a98..5898cbec7 100644 +--- a/common/mlutils/unix_utils.ml ++++ b/common/mlutils/unix_utils.ml +@@ -82,7 +82,7 @@ module StatVFS = struct + let free_space { f_bsize = bsize; f_bavail = bavail } = bsize *^ bavail + + external is_network_filesystem : string -> bool = +- "guestfs_int_mllib_statvfs_is_network_filesystem" [@@noalloc] ++ "guestfs_int_mllib_statvfs_is_network_filesystem" + end + + module Sysconf = struct +diff --git a/common/mlvisit/Makefile.am b/common/mlvisit/Makefile.am +index beff0bc64..40ecb0f76 100644 +--- a/common/mlvisit/Makefile.am ++++ b/common/mlvisit/Makefile.am +@@ -88,7 +88,7 @@ endif + libmlvisit_a_DEPENDENCIES = $(OBJECTS) + + $(MLVISIT_CMA): $(OBJECTS) libmlvisit.a +- $(OCAMLFIND) mklib $(OCAMLPACKAGES) \ ++ $(AM_V_GEN) $(OCAMLFIND) mklib $(OCAMLPACKAGES) \ + $(OBJECTS) $(libmlvisit_a_OBJECTS) -cclib -lvisit -o mlvisit + + # Tests. +diff --git a/common/mlxml/Makefile.am b/common/mlxml/Makefile.am +index e56b6fd54..db131ab30 100644 +--- a/common/mlxml/Makefile.am ++++ b/common/mlxml/Makefile.am +@@ -84,7 +84,7 @@ endif + libmlxml_a_DEPENDENCIES = $(OBJECTS) + + $(MLXML_CMA): $(OBJECTS) libmlxml.a +- $(OCAMLFIND) mklib $(OCAMLPACKAGES) \ ++ $(AM_V_GEN) $(OCAMLFIND) mklib $(OCAMLPACKAGES) \ + $(OBJECTS) $(libmlxml_a_OBJECTS) \ + -o mlxml + +diff --git a/common/options/keys.c b/common/options/keys.c +index 7027104a9..b8f19cebf 100644 +--- a/common/options/keys.c ++++ b/common/options/keys.c +@@ -78,6 +78,7 @@ read_key (const char *param) + len = getline (&ret, &allocsize, infp); + if (len == -1) { + perror ("getline"); ++ free (ret); + ret = NULL; + goto error; + } +diff --git a/common/parallel/parallel.c b/common/parallel/parallel.c +index 18b060740..88ad819f0 100644 +--- a/common/parallel/parallel.c ++++ b/common/parallel/parallel.c +@@ -221,6 +221,7 @@ worker_thread (void *thread_data_vp) + g = guestfs_create (); + if (g == NULL) { + perror ("guestfs_create"); ++ fclose (fp); + thread_data->r = -1; + return &thread_data->r; + } diff --git a/0027-common-update-submodule.patch b/0027-common-update-submodule.patch new file mode 100644 index 0000000..a73b185 --- /dev/null +++ b/0027-common-update-submodule.patch @@ -0,0 +1,119 @@ +From b603dd04190ff1c7c71b04c135474859ccacd136 Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Thu, 26 Mar 2026 13:02:45 +0000 +Subject: [PATCH] common: update submodule + + Cole Robinson (1): + Add update-submodule.sh + + Richard W.M. Jones (3): + mlutils/unix_utils-c.c: Include "guestfs-utils.h" + mlcustomize: Remove fictitious "vista" virtio-win directory + mlcustomize: Use 2k8 drivers for Windows Vista and 2008 Server R1 + +(cherry picked from commit 6b8eb0742dab07ce0c90e0aa855f9ba85df89fb0) +--- + Makefile.am | 1 + + common | 2 +- + 2 files changed, 2 insertions(+), 1 deletion(-) + +diff --git a/Makefile.am b/Makefile.am +index 7be3c1496..87cf2cdc7 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -92,6 +92,7 @@ EXTRA_DIST = \ + check-mli.sh \ + common/.gitignore \ + common/README \ ++ common/update-submodule.sh \ + lib/guestfs-internal-all.h \ + m4/.gitignore \ + podcheck.pl \ +Submodule common c8e64b3ff..0948fc94a: +diff --git a/common/mlcustomize/inject_virtio_win.ml b/common/mlcustomize/inject_virtio_win.ml +index a29380806..ce24332ff 100644 +--- a/common/mlcustomize/inject_virtio_win.ml ++++ b/common/mlcustomize/inject_virtio_win.ml +@@ -468,10 +468,8 @@ and virtio_iso_path_matches_guest_os t path = + ((=) "winxp") + else if pathelem "2k3" then + (function "win2k3" | "win2k3r2" -> true | _ -> false) +- else if pathelem "vista" then +- ((=) "winvista") + else if pathelem "2k8" then +- ((=) "win2k8") ++ (function "win2k8" | "winvista" -> true | _ -> false) + else if pathelem "w7" then + ((=) "win7") + else if pathelem "2k8r2" then +diff --git a/common/mlutils/unix_utils-c.c b/common/mlutils/unix_utils-c.c +index 28df2c310..919f52602 100644 +--- a/common/mlutils/unix_utils-c.c ++++ b/common/mlutils/unix_utils-c.c +@@ -65,6 +65,8 @@ + #include + #include + ++#include "guestfs-utils.h" ++ + extern value guestfs_int_mllib_dev_t_makedev (value majv, value minv); + extern value guestfs_int_mllib_dev_t_major (value devv); + extern value guestfs_int_mllib_dev_t_minor (value devv); +diff --git a/common/update-submodule.sh b/common/update-submodule.sh +new file mode 100755 +index 000000000..ecf22f166 +--- /dev/null ++++ b/common/update-submodule.sh +@@ -0,0 +1,52 @@ ++#!/bin/bash ++# (C) Copyright 2026 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., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ++# Generate a submodule update commit, with formatted shortlog output. ++# Run this from the parent repo like: ./common/update-submodule.sh ++# Then `git commit --amend ...` as desired. ++ ++set -euo pipefail ++ ++if ! git submodule status common &>/dev/null; then ++ echo "Error: this script must be run from the top directory of a repo with a 'common/' submodule" >&2 ++ exit 1 ++fi ++ ++if ! git diff --quiet --exit-code; then ++ echo "Error: working tree has uncommitted changes" >&2 ++ exit 1 ++fi ++ ++echo "Running: git submodule update --remote common" ++git submodule update --remote common ++ ++if git diff --quiet --exit-code; then ++ echo "'common' submodule already up to date" ++ exit 0 ++fi ++ ++OLD_COMMIT=$(git ls-tree HEAD common | awk '{print $3}') ++NEW_COMMIT=$(git -C common rev-parse HEAD) ++echo "Old 'common' commit: $OLD_COMMIT" ++echo "New 'common' commit: $NEW_COMMIT" ++ ++SHORTLOG=$(git -C common shortlog --no-merges "${OLD_COMMIT}..${NEW_COMMIT}" | sed '/^$/!s/^/ /') ++git commit -F - common < +Date: Tue, 28 Apr 2026 16:57:48 +0100 +Subject: [PATCH] common: update submodule + + Richard W.M. Jones (5): + mldrivers/firmware.ml: Ignore CHS geometry error from parted + utils: Add read_whole_file function + options/keys.c: When reading key from user, prefix with "text:" + options/keys.c: When using --key :key:, prefix with "text:" + options/keys.c: When reading the key from a file, encode it with base64 + + Srihari Parimi (2): + mltools: add prefix to debug () output + mltools: for external commands include function name in debug() output + + Susant Sahani (1): + smp: respect cgroup v2 CPU limits for appliance SMP + +Also this now requires libguestfs >= 1.59.7 because we rely on text: +and base64: prefixes in LUKS functions. + +Fixes: https://redhat.atlassian.net/browse/RHEL-171896 + +Cherry picked from commit 08afa63ea1541f5e9711d1c8be3b5806c226d539. +For RHEL 10.2, adjust minimum libguestfs version to match. +--- + common | 2 +- + m4/guestfs-libraries.m4 | 3 ++- + 2 files changed, 3 insertions(+), 2 deletions(-) + +Submodule common 0948fc94a..cf2e12078: +diff --git a/common/mldrivers/firmware.ml b/common/mldrivers/firmware.ml +index ee0a7cafe..6698b0736 100644 +--- a/common/mldrivers/firmware.ml ++++ b/common/mldrivers/firmware.ml +@@ -18,6 +18,7 @@ + + open Printf + ++open Std_utils + open Tools_utils + + module G = Guestfs +@@ -28,12 +29,22 @@ type i_firmware = + + let detect_firmware g = + let parttype_is_gpt dev = +- try g#part_get_parttype dev = "gpt" +- with G.Error msg as exn -> +- (* If it's _not_ "unrecognised disk label" then re-raise it. *) +- if g#last_errno () <> G.Errno.errno_EINVAL then raise exn; +- debug "%s (ignored)" msg; +- false ++ try ++ g#part_get_parttype dev = "gpt" ++ with ++ | G.Error 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). ++ *) ++ debug "%s (ignored)" msg; ++ false ++ | G.Error msg as exn -> ++ (* If it's _not_ "unrecognised disk label" then re-raise it. *) ++ if g#last_errno () <> G.Errno.errno_EINVAL then raise exn; ++ debug "%s (ignored)" msg; ++ false + in + let accumulate_partition (esp_parts, bboot) part = + let dev = g#part_to_dev part in +diff --git a/common/mltools/tools_utils.ml b/common/mltools/tools_utils.ml +index 330ca3d00..b1938b5db 100644 +--- a/common/mltools/tools_utils.ml ++++ b/common/mltools/tools_utils.ml +@@ -207,7 +207,10 @@ let info fs = + + (* Print a debug message. *) + let debug fs = +- let display str = if verbose () then prerr_endline str in ++ let display str = ++ if verbose () then ++ prerr_endline (sprintf "%s: debug: %s" !prog str) ++ in + ksprintf display fs + + (* Common function to create a new Guestfs handle, with common options +@@ -442,7 +445,7 @@ let external_command_failed help cmd reason = + (* Run an external command, slurp up the output as a list of lines. *) + let external_command ?(echo_cmd = true) ?help cmd = + if echo_cmd then +- debug "%s" cmd; ++ debug "external_command: %s" cmd; + let chan = Unix.open_process_in cmd in + let lines = ref [] in + (try while true do lines := input_line chan :: !lines done +@@ -517,7 +520,7 @@ and do_run ?(echo_cmd = true) ?help ?stdout_fd ?stderr_fd args = + let outfd = get_fd Unix.stdout stdout_fd in + let errfd = get_fd Unix.stderr stderr_fd in + if echo_cmd then +- debug "%s" (stringify_args args); ++ debug "run_command: %s" (stringify_args args); + let pid = Unix.create_process app (Array.of_list args) Unix.stdin + outfd errfd in + Either (pid, app, stdout_fd, stderr_fd) +@@ -544,7 +547,7 @@ and do_teardown help app outfd errfd exitstat = + + let shell_command ?(echo_cmd = true) cmd = + if echo_cmd then +- debug "%s" cmd; ++ debug "shell_command: %s" cmd; + Sys.command cmd + + (* Run uuidgen to return a random UUID. *) +diff --git a/common/mlutils/unix_utils.ml b/common/mlutils/unix_utils.ml +index 5898cbec7..9d7e11a65 100644 +--- a/common/mlutils/unix_utils.ml ++++ b/common/mlutils/unix_utils.ml +@@ -89,3 +89,28 @@ module Sysconf = struct + external nr_processors_online : unit -> int = + "guestfs_int_mllib_sysconf_nr_processors_online" [@@noalloc] + end ++ ++module Cgroup = struct ++ let v2_cpus () = ++ let file = "/sys/fs/cgroup/cpu.max" in ++ if Sys.file_exists file then ++ try ++ let line = read_first_line_from_file file in ++ if String.starts_with ~prefix:"max" line then ++ None ++ else ++ let quota, period = ++ Scanf.sscanf line "%Ld %Ld" (fun q p -> (q, p)) in ++ if period > 0L then ++ Some (max 1 (Int64.to_int (Int64.div quota period))) ++ else None ++ with ++ | Scanf.Scan_failure _ | Failure _ | End_of_file -> None ++ else ++ None ++ ++ let nr_cpus_available () = ++ match v2_cpus () with ++ | Some cpus -> cpus ++ | None -> Sysconf.nr_processors_online () ++end +diff --git a/common/mlutils/unix_utils.mli b/common/mlutils/unix_utils.mli +index aead4df21..cb4f0426e 100644 +--- a/common/mlutils/unix_utils.mli ++++ b/common/mlutils/unix_utils.mli +@@ -130,3 +130,21 @@ module Sysconf : sig + Note this never fails. In case we cannot get the number of + cores it returns 1. *) + end ++ ++module Cgroup : sig ++ (** Functions to read CPU limits from cgroup filesystems. *) ++ ++ val v2_cpus : unit -> int option ++ (** [v2_cpus ()] reads the cgroup v2 CPU quota from ++ [/sys/fs/cgroup/cpu.max] and returns the number of CPUs ++ allocated (quota / period), or [None] if the file does not ++ exist, the quota is "max" (unlimited), or the file cannot ++ be parsed. The kernel stores quota and period as [long] ++ values in microseconds, so we parse them as [Int64]. *) ++ ++ val nr_cpus_available : unit -> int ++ (** [nr_cpus_available ()] returns the number of CPUs available, ++ taking into account cgroup v2 CPU limits. ++ Falls back to {!Sysconf.nr_processors_online} if no cgroup ++ limits are set. *) ++end +diff --git a/common/options/keys.c b/common/options/keys.c +index b8f19cebf..432e26dc8 100644 +--- a/common/options/keys.c ++++ b/common/options/keys.c +@@ -37,17 +37,23 @@ + * Read a passphrase ('Key') from F with echo off. + * + * The caller (F) will call free on the string +- * afterwards. Based on the code in cryptsetup file F. ++ * afterwards. ++ * ++ * The entered string is prefixed with "text:..." to avoid ambiguity ++ * (with libguestfs >= 1.60). Base64 encoding cannot be used here. ++ * ++ * Based on the code in cryptsetup file F. + */ + char * + read_key (const char *param) + { + FILE *infp, *outfp; + struct termios orig, temp; ++ CLEANUP_FREE char *key = NULL; ++ size_t keysize = 0; + char *ret = NULL; + int tty; + int tcset = 0; +- size_t allocsize = 0; + ssize_t len; + + /* Read and write to /dev/tty if available. */ +@@ -75,17 +81,21 @@ read_key (const char *param) + } + } + +- len = getline (&ret, &allocsize, infp); ++ len = getline (&key, &keysize, infp); + if (len == -1) { + perror ("getline"); +- free (ret); +- ret = NULL; + goto error; + } + + /* Remove the terminating \n if there is one. */ +- if (len > 0 && ret[len-1] == '\n') +- ret[len-1] = '\0'; ++ if (len > 0 && key[len-1] == '\n') ++ key[len-1] = '\0'; ++ ++ /* Prefix with "text:". */ ++ if (asprintf (&ret, "text:%s", key) == -1) { ++ perror ("asprintf"); ++ goto error; ++ } + + error: + /* Restore echo, close file descriptor. */ +@@ -100,27 +110,60 @@ read_key (const char *param) + return ret; + } + ++/* Read a key from a file and base64 encode it, returning "base64:..." */ + static char * +-read_first_line_from_file (const char *filename) ++read_key_and_base64_encode (const char *filename) + { +- CLEANUP_FCLOSE FILE *fp = NULL; +- char *ret = NULL; +- size_t allocsize = 0; +- ssize_t len; ++ CLEANUP_FREE char *inp = NULL; ++ char *out; ++ size_t inplen, outlen, i, j; + +- fp = fopen (filename, "r"); +- if (!fp) +- error (EXIT_FAILURE, errno, "fopen: %s", filename); ++ if (read_whole_file (filename, &inp, &inplen) == -1) ++ error (EXIT_FAILURE, 0, "read_key_and_base64_encode: read_whole_file: %s", ++ filename); + +- len = getline (&ret, &allocsize, fp); +- if (len == -1) +- error (EXIT_FAILURE, errno, "getline: %s", filename); ++ /* From https://stackoverflow.com/a/6782480 */ ++ static char encoding_table[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', ++ 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', ++ 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', ++ 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', ++ 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', ++ 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', ++ 'w', 'x', 'y', 'z', '0', '1', '2', '3', ++ '4', '5', '6', '7', '8', '9', '+', '/'}; ++ static int mod_table[] = {0, 2, 1}; + +- /* Remove the terminating \n if there is one. */ +- if (len > 0 && ret[len-1] == '\n') +- ret[len-1] = '\0'; ++ outlen = 4 * ((inplen + 2) / 3); ++ out = malloc (outlen + 7 + 1); ++ if (!out) ++ error (EXIT_FAILURE, errno, "read_key_and_base64_encode: %s: malloc", ++ filename); + +- return ret; ++ /* Add prefix and NUL-termination, then adjust 'out' to make the ++ * rest of the code simpler. ++ */ ++ memcpy (out, "base64:", 7); ++ out[7 + outlen] = '\0'; ++ out += 7; ++ ++ for (i = 0, j = 0; i < inplen;) { ++ uint32_t octet_a = i < inplen ? (unsigned char) inp[i++] : 0; ++ uint32_t octet_b = i < inplen ? (unsigned char) inp[i++] : 0; ++ uint32_t octet_c = i < inplen ? (unsigned char) inp[i++] : 0; ++ ++ uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c; ++ ++ assert (j <= outlen-4); ++ out[j++] = encoding_table[(triple >> 3 * 6) & 0x3F]; ++ out[j++] = encoding_table[(triple >> 2 * 6) & 0x3F]; ++ out[j++] = encoding_table[(triple >> 1 * 6) & 0x3F]; ++ out[j++] = encoding_table[(triple >> 0 * 6) & 0x3F]; ++ } ++ ++ for (i = 0; i < mod_table[inplen % 3]; i++) ++ out[outlen - 1 - i] = '='; ++ ++ return out - 7 /* see above */; + } + + /* Return the key(s) matching this particular device from the +@@ -164,15 +207,14 @@ get_keys (struct key_store *ks, const char *device, const char *uuid, + + switch (key->type) { + case key_string: +- s = strdup (key->string.s); +- if (!s) +- error (EXIT_FAILURE, errno, "strdup"); ++ if (asprintf (&s, "text:%s", key->string.s) == -1) ++ error (EXIT_FAILURE, errno, "asprintf"); + match->clevis = false; + match->passphrase = s; + ++match; + break; + case key_file: +- s = read_first_line_from_file (key->file.name); ++ s = read_key_and_base64_encode (key->file.name); + match->clevis = false; + match->passphrase = s; + ++match; +diff --git a/common/utils/Makefile.am b/common/utils/Makefile.am +index 25c6100b2..f328dcadf 100644 +--- a/common/utils/Makefile.am ++++ b/common/utils/Makefile.am +@@ -30,6 +30,7 @@ libutils_la_SOURCES = \ + libxml2-writer-macros.h \ + pcre2-cleanups.c \ + stringlists-utils.c \ ++ whole-file.c \ + utils.c + libutils_la_CPPFLAGS = \ + -DGUESTFS_NO_DEPRECATED=1 \ +diff --git a/common/utils/guestfs-utils.h b/common/utils/guestfs-utils.h +index e861e7db5..a8bd9ac2c 100644 +--- a/common/utils/guestfs-utils.h ++++ b/common/utils/guestfs-utils.h +@@ -113,4 +113,8 @@ extern const char *guestfs_int_strerror (int errnum, char *buf, size_t buflen); + /* environ.c */ + extern char **guestfs_int_copy_environ (char **env, ...); + ++/* whole-file.c */ ++extern int read_whole_file (const char *filename, ++ char **data_r, size_t *size_r); ++ + #endif /* GUESTFS_UTILS_H_ */ +diff --git a/common/utils/whole-file.c b/common/utils/whole-file.c +new file mode 100644 +index 000000000..a896e0241 +--- /dev/null ++++ b/common/utils/whole-file.c +@@ -0,0 +1,111 @@ ++/* libguestfs ++ * Copyright (C) 2011-2026 Red Hat Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "guestfs-utils.h" ++ ++/** ++ * Read the whole file C into a memory buffer. ++ * ++ * The memory buffer is initialized and returned in C. The ++ * size of the file in bytes is returned in C. The return ++ * buffer must be freed by the caller. ++ * ++ * On error this prints an error on C and returns -1. Unlike ++ * the similar C this does not use the ++ * libguestfs handle or call C. ++ * ++ * For the convenience of callers, the returned buffer is ++ * NUL-terminated (the NUL is not included in the size). ++ * ++ * The file must be a B, B, B file. In ++ * particular, do not use this function to read files that might be ++ * under control of an untrusted user since that will lead to a ++ * denial-of-service attack. ++ */ ++int ++read_whole_file (const char *filename, char **data_r, size_t *size_r) ++{ ++ int fd; ++ char *data; ++ off_t size; ++ off_t n; ++ ssize_t r; ++ struct stat statbuf; ++ ++ fd = open (filename, O_RDONLY|O_CLOEXEC); ++ if (fd == -1) { ++ perror (filename); ++ return -1; ++ } ++ ++ if (fstat (fd, &statbuf) == -1) { ++ perror (filename); ++ close (fd); ++ return -1; ++ } ++ ++ size = statbuf.st_size; ++ data = malloc (size + 1); ++ if (data == NULL) { ++ perror ("malloc"); ++ close (fd); ++ return -1; ++ } ++ ++ n = 0; ++ while (n < size) { ++ r = read (fd, &data[n], size - n); ++ if (r == -1) { ++ perror (filename); ++ free (data); ++ close (fd); ++ return -1; ++ } ++ if (r == 0) { ++ fprintf (stderr, "%s: unexpected end of input", filename); ++ free (data); ++ close (fd); ++ return -1; ++ } ++ n += r; ++ } ++ ++ if (close (fd) == -1) { ++ perror (filename); ++ free (data); ++ return -1; ++ } ++ ++ /* For convenience of callers, \0-terminate the data. */ ++ data[size] = '\0'; ++ ++ *data_r = data; ++ if (size_r != NULL) ++ *size_r = size; ++ ++ return 0; ++} +diff --git a/m4/guestfs-libraries.m4 b/m4/guestfs-libraries.m4 +index 02f2281f8..9b643650d 100644 +--- a/m4/guestfs-libraries.m4 ++++ b/m4/guestfs-libraries.m4 +@@ -22,7 +22,8 @@ dnl + dnl We need libguestfs 1.57.1 for guestfs_setfiles. + dnl We need libguestfs 1.57.6 for guestfs_inspect_get_applications2 app2_class. + dnl We need libguestfs 1.57.6 for guestfs_inspect_get_windows_group_policy +-PKG_CHECK_MODULES([LIBGUESTFS], [libguestfs >= 1.57.6]) ++dnl We need libguestfs 1.58.1-6.el10 for text: and base64: prefix in LUKS funcs. ++PKG_CHECK_MODULES([LIBGUESTFS], [libguestfs >= 1.58.1]) + printf "libguestfs version is "; $PKG_CONFIG --modversion libguestfs + + dnl Test if it's GNU or XSI strerror_r. diff --git a/0029-virt-builder-document-Windows-firstboot-exit-code-be.patch b/0029-virt-builder-document-Windows-firstboot-exit-code-be.patch new file mode 100644 index 0000000..83f86d3 --- /dev/null +++ b/0029-virt-builder-document-Windows-firstboot-exit-code-be.patch @@ -0,0 +1,39 @@ +From 0f1e772fcf18fc87fd3f4c98566358963893c715 Mon Sep 17 00:00:00 2001 +From: Srihari Parimi +Date: Fri, 1 May 2026 15:04:41 +0530 +Subject: [PATCH] virt-builder: document Windows firstboot exit code behavior + +In the FIRST BOOT SCRIPTS section of the man page, update the +Windows-specific documentation to clarify how exit codes are handled. + +Specifically, explain that for Windows guests, the firstboot.bat +script considers any exit code other than 249 as a success. An exit +code of 249 is treated as a failure, which triggers a retry of the +script on the next boot. + +Fixes: https://redhat.atlassian.net/browse/RHEL-170708 +(cherry picked from commit da6500b696d2c8d7614adb68a307651fe84663d6) +--- + builder/virt-builder.pod | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/builder/virt-builder.pod b/builder/virt-builder.pod +index f043939ca..59dd197f0 100644 +--- a/builder/virt-builder.pod ++++ b/builder/virt-builder.pod +@@ -873,6 +873,15 @@ F or F is copied from the location pointed to by the + C environment variable; if not set, a compiled-in + default will be used (something like F). + ++On Windows guests, firstboot scripts are executed by a dedicated ++C script which manages the execution flow. ++ ++This script monitors the exit code of each firstboot task. Any exit ++code other than B<249> is considered a success, and the script moves ++on to the next task. If a script returns B<249>, it is treated as a ++failure, and the system will attempt to run that script again on the ++next boot. ++ + The output of the first boot scripts is available in the guest as + F. + diff --git a/0030-virt-builder-document-exit-code-250-for-Windows-firs.patch b/0030-virt-builder-document-exit-code-250-for-Windows-firs.patch new file mode 100644 index 0000000..5eaba3a --- /dev/null +++ b/0030-virt-builder-document-exit-code-250-for-Windows-firs.patch @@ -0,0 +1,61 @@ +From bdd1c1465eab264e50767c2a2d67d6a346033fcf Mon Sep 17 00:00:00 2001 +From: Srihari Parimi +Date: Fri, 1 May 2026 20:20:47 +0530 +Subject: [PATCH] virt-builder: document exit code 250 for Windows firstboot + +Update the FIRST BOOT SCRIPTS section in virt-builder.pod to +document the behavior of exit code 250 for Windows guests. + +Code 250 explicitly indicates that a reboot is not required, +allowing firstboot.bat to proceed to the next script without +shutting down. The section is now organized into a bulleted +list for better readability, covering codes 250, 249 +(failure/retry), and general success codes. + +Fixes: https://redhat.atlassian.net/browse/RHEL-170706 +(cherry picked from commit 0fa9030e97e419fc03f61842e9e6df689623d345) +--- + builder/virt-builder.pod | 29 +++++++++++++++++++++++------ + 1 file changed, 23 insertions(+), 6 deletions(-) + +diff --git a/builder/virt-builder.pod b/builder/virt-builder.pod +index 59dd197f0..fcffecbee 100644 +--- a/builder/virt-builder.pod ++++ b/builder/virt-builder.pod +@@ -874,13 +874,30 @@ C environment variable; if not set, a compiled-in + default will be used (something like F). + + On Windows guests, firstboot scripts are executed by a dedicated +-C script which manages the execution flow. ++C script which manages the execution flow. It monitors ++the exit code of each firstboot script: + +-This script monitors the exit code of each firstboot task. Any exit +-code other than B<249> is considered a success, and the script moves +-on to the next task. If a script returns B<249>, it is treated as a +-failure, and the system will attempt to run that script again on the +-next boot. ++=over 4 ++ ++=item * B<250> ++ ++The script indicates that a system reboot is not required at ++this stage. The C script will continue ++immediately to the next script in the sequence without ++initiating a shutdown. ++ ++=item * B<249> ++ ++The script is treated as a failure. The C script ++stops execution, and the system will attempt to run this script ++again on the next boot. ++ ++=item * B ++ ++The script is considered a success. The C script ++proceeds to the next script in the sequence. ++ ++=back + + The output of the first boot scripts is available in the guest as + F. diff --git a/0031-common-update-submodule.patch b/0031-common-update-submodule.patch new file mode 100644 index 0000000..86911ff --- /dev/null +++ b/0031-common-update-submodule.patch @@ -0,0 +1,43 @@ +From 0a3e2281c04fa4bffeaac1a2fd4c5a8234c2e1ab Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Fri, 1 May 2026 16:35:25 +0100 +Subject: [PATCH] common: update submodule + + Srihari Parimi (1): + mlcustomize: firstboot.bat must not reboot VM on error code 250 + +(cherry picked from commit a4bc0acf3707b14bcaee237a3c9a7f8f5dd13ff9) +--- + common | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +Submodule common cf2e12078..228449739: +diff --git a/common/mlcustomize/firstboot.ml b/common/mlcustomize/firstboot.ml +index 543e98fb1..366beca9e 100644 +--- a/common/mlcustomize/firstboot.ml ++++ b/common/mlcustomize/firstboot.ml +@@ -320,13 +320,18 @@ for %%%%f in ("%%scripts%%"\*.bat) do ( + echo Script failed, will retry on next boot + ) + +- :: Reboot the computer. This is necessary to free any locked +- :: files which may prevent later scripts from running. +- shutdown /r /t 0 /y ++ :: Reboot the computer only if exit code indicates. ++ :: Exit code 250 means do not reboot, otherwise reboot ++ :: Reboot is necessary to free any locked files which ++ :: may prevent later scripts from running. ++ if !elvl! NEQ 250 ( ++ shutdown /r /t 0 /y + +- :: Exit the script (in case shutdown returns before rebooting). +- :: On next boot, the whole firstboot service will be called again. +- exit /b ++ :: Exit the script (in case shutdown returns before rebooting). ++ :: On next boot, the whole firstboot service will be called again. ++ ++ exit /b ++ ) + ) + + :: Fallthrough here if there are no scripts. diff --git a/0032-sysprep-Remove-documentation-about-copying-and-cloni.patch b/0032-sysprep-Remove-documentation-about-copying-and-cloni.patch new file mode 100644 index 0000000..a6da8a5 --- /dev/null +++ b/0032-sysprep-Remove-documentation-about-copying-and-cloni.patch @@ -0,0 +1,223 @@ +From 9c9877bf262d69b1ab6b265bb17dc3f009dcfffa Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Tue, 5 May 2026 12:02:11 +0100 +Subject: [PATCH] sysprep: Remove documentation about copying and cloning + +This documentation is mostly obsolete and doesn't really cover any +useful use cases these days. It's best to remove it rather than +trying to keep it up to date. + +(cherry picked from commit 82edee0cdcb8b0b8cb93d3067b311e8a13bd7a66) +--- + sysprep/virt-sysprep.pod | 191 +-------------------------------------- + 1 file changed, 1 insertion(+), 190 deletions(-) + +diff --git a/sysprep/virt-sysprep.pod b/sysprep/virt-sysprep.pod +index f044998cc..6b913ad70 100644 +--- a/sysprep/virt-sysprep.pod ++++ b/sysprep/virt-sysprep.pod +@@ -19,8 +19,7 @@ can be enabled or disabled as required. + + Virt-sysprep modifies the guest or disk image I. The guest + must be shut down. If you want to preserve the existing contents of +-the guest, I. See +-L below. ++the guest, I. + + You do I need to run virt-sysprep as root. In fact we'd + generally recommend that you don't. The time you might want to run it +@@ -301,194 +300,6 @@ is given. + + __OPERATIONS__ + +-=head1 COPYING AND CLONING +- +-Virt-sysprep can be used as part of a process of cloning guests, or to +-prepare a template from which guests can be cloned. There are many +-different ways to achieve this using the virt tools, and this section +-is just an introduction. +- +-A virtual machine (when switched off) consists of two parts: +- +-=over 4 +- +-=item I +- +-The configuration or description of the guest. eg. The libvirt +-XML (see C), the running configuration of the guest, +-or another external format like OVF. +- +-Some configuration items that might need to be changed: +- +-=over 4 +- +-=item * +- +-name +- +-=item * +- +-UUID +- +-=item * +- +-path to block device(s) +- +-=item * +- +-network card MAC address +- +-=back +- +-=item I +- +-One or more hard disk images, themselves containing files, +-directories, applications, kernels, configuration, etc. +- +-Some things inside the block devices that might need to be changed: +- +-=over 4 +- +-=item * +- +-hostname and other net configuration +- +-=item * +- +-UUID +- +-=item * +- +-SSH host keys +- +-=item * +- +-Windows unique security ID (SID) +- +-=item * +- +-Puppet registration +- +-=back +- +-=back +- +-=head2 COPYING THE BLOCK DEVICE +- +-Starting with an original guest, you probably wish to copy the guest +-block device and its configuration to make a template. Then once you +-are happy with the template, you will want to make many clones from +-it. +- +- virt-sysprep +- | +- v +- original guest --------> template ----------> +- \------> cloned +- \-----> guests +- \----> +- +-You can, of course, just copy the block device on the host using +-L or L. +- +- dd dd +- original guest --------> template ----------> +- \------> cloned +- \-----> guests +- \----> +- +-There are some smarter (and faster) ways too: +- +- snapshot +- template ----------> +- \------> cloned +- \-----> guests +- \----> +- +-You may want to run virt-sysprep twice, once to reset the guest (to +-make a template) and a second time to customize the guest for a +-specific user: +- +- virt-sysprep virt-sysprep +- (reset) (add user, keys, logos) +- | | +- dd v dd v +- original guest ----> template ---------> copied ------> custom +- template guest +- +-=over 4 +- +-=item * +- +-Create a snapshot using qemu-img: +- +- qemu-img create -f qcow2 -o backing_file=original snapshot.qcow +- +-The advantage is that you don’t need to copy the original (very fast) +-and only changes are stored (less storage required). +- +-Note that writing to the backing file once you have created guests on +-top of it is not possible: you will corrupt the guests. +- +-=item * +- +-Create a snapshot using C. +- +-=item * +- +-Other ways to create snapshots include using filesystems-level tools +-(for filesystems such as btrfs). +- +-Most Network Attached Storage (NAS) devices can also create cheap +-snapshots from files or LUNs. +- +-=item * +- +-Get your NAS to duplicate the LUN. Most NAS devices can also +-duplicate LUNs very cheaply (they copy them on-demand in the +-background). +- +-=item * +- +-Prepare your template using L. See below. +- +-=back +- +-=head2 VIRT-CLONE +- +-A separate tool, L, can be used to duplicate the block +-device and/or modify the external libvirt configuration of a guest. +-It will reset the name, UUID and MAC address of the guest in the +-libvirt XML. +- +-L does not use libguestfs and cannot look inside the +-disk image. This was the original motivation to write virt-sysprep. +- +-=head2 SPARSIFY +- +- virt-sparsify +- original guest --------> template +- +-L can be used to make the cloning template smaller, +-making it easier to compress and/or faster to copy. +- +-Notice that since virt-sparsify also copies the image, you can use it +-to make the initial copy (instead of C
). +- +-=head2 RESIZE +- +- virt-resize +- template ----------> +- \------> cloned +- \-----> guests +- \----> +- +-If you want to give people cloned guests, but let them pick the size +-of the guest themselves (eg. depending on how much they are prepared +-to pay for disk space), then instead of copying the template, you can +-run L. Virt-resize performs a copy and resize, and +-thus is ideal for cloning guests from a template. +- + =head1 FIRSTBOOT VS SCRIPT + + The two options I<--firstboot> and I<--script> both supply shell diff --git a/0033-sysprep-Improve-synopsis-section.patch b/0033-sysprep-Improve-synopsis-section.patch new file mode 100644 index 0000000..8cbed46 --- /dev/null +++ b/0033-sysprep-Improve-synopsis-section.patch @@ -0,0 +1,39 @@ +From c7e355b2abfaa0a29ce9bfaeab8883e2553cbee1 Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Tue, 5 May 2026 12:11:31 +0100 +Subject: [PATCH] sysprep: Improve synopsis section + +Expand the synopsis with more usage information. We cannot list all +the options here (eg. the customize options) because we don't +auto-generate that. + +(cherry picked from commit e2b43bad4dc0176d2d43694b28b3d78e8fdcebc7) +--- + sysprep/virt-sysprep.pod | 13 +++++++++++-- + 1 file changed, 11 insertions(+), 2 deletions(-) + +diff --git a/sysprep/virt-sysprep.pod b/sysprep/virt-sysprep.pod +index 6b913ad70..799764c8e 100644 +--- a/sysprep/virt-sysprep.pod ++++ b/sysprep/virt-sysprep.pod +@@ -4,9 +4,18 @@ virt-sysprep - Reset, unconfigure or customize a virtual machine so clones can b + + =head1 SYNOPSIS + +- virt-sysprep [--options] -d domname ++ virt-sysprep [--colors] [--colours] [-c URI] [--connect URI] ++ [-n] [--dry-run] [--enable OPERATIONS] [--operations OPERATIONS] ++ [--echo-keys] [--format raw|qcow2] [--key SELECTOR] ++ [--keys-from-stdin] [--mount-options] [-q] [--quiet] ++ [--network] [--no-network] [-v] [--verbose] [--wrap] [-x] ++ -d domname | -a disk.img [-a disk.img ...] + +- virt-sysprep [--options] -a disk.img [-a disk.img ...] ++ virt-sysprep --list-operations ++ ++ virt-sysprep --version ++ ++ virt-sysprep -V + + =head1 DESCRIPTION + diff --git a/0034-sysprep-Rework-documentation.patch b/0034-sysprep-Rework-documentation.patch new file mode 100644 index 0000000..681e32e --- /dev/null +++ b/0034-sysprep-Rework-documentation.patch @@ -0,0 +1,69 @@ +From a22e17e45044cf1e3c116cc721b91ce1c9f4635d Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Tue, 5 May 2026 12:12:12 +0100 +Subject: [PATCH] sysprep: Rework documentation + +Minor reworking: + + - expand description + + - combine multiple notes into one section + +(cherry picked from commit b20cbdc6d003026403bedbe23e6c7841e22e324e) +--- + sysprep/virt-sysprep.pod | 21 ++++++++++++--------- + 1 file changed, 12 insertions(+), 9 deletions(-) + +diff --git a/sysprep/virt-sysprep.pod b/sysprep/virt-sysprep.pod +index 799764c8e..4f552b385 100644 +--- a/sysprep/virt-sysprep.pod ++++ b/sysprep/virt-sysprep.pod +@@ -19,12 +19,13 @@ virt-sysprep - Reset, unconfigure or customize a virtual machine so clones can b + + =head1 DESCRIPTION + +-Virt-sysprep can reset or unconfigure a virtual machine so that +-clones can be made from it. Steps in this process include removing +-SSH host keys, removing persistent network MAC configuration, and +-removing user accounts. Virt-sysprep can also customize a virtual +-machine, for instance by adding SSH keys, users or logos. Each step +-can be enabled or disabled as required. ++Virt-sysprep can reset or unconfigure a virtual machine so that clones ++can be made from it, or to prepare a template from which clones can be ++made later on. Steps in this process include removing SSH host keys, ++removing persistent network MAC configuration, and removing user ++accounts. Virt-sysprep can also customize a virtual machine, for ++instance by adding SSH keys, users or logos. Each step can be enabled ++or disabled as required. + + Virt-sysprep modifies the guest or disk image I. The guest + must be shut down. If you want to preserve the existing contents of +@@ -355,7 +356,7 @@ just deleting them. L is another way to remove this + content. See also the L command to get rid of deleted + content in directory entries and inodes. + +-=head2 RANDOM SEED ++=head2 Random seed + + I<(This section applies to Linux guests only)> + +@@ -373,7 +374,9 @@ module: + cp template.img newguest.img + virt-sysprep --enable customize -a newguest.img + +-=head1 SELINUX ++=head1 NOTES ++ ++=head2 SELinux + + For guests which make use of SELinux, special handling for them might + be needed when using operations which create new files or alter +@@ -381,7 +384,7 @@ existing ones. + + For further details, see L. + +-=head1 WINDOWS 8 ++=head2 Windows E 8 + + Windows 8 "fast startup" can prevent virt-sysprep from working. + See L. diff --git a/0035-customize-sysprep-Move-firstboot-docs-from-sysprep-c.patch b/0035-customize-sysprep-Move-firstboot-docs-from-sysprep-c.patch new file mode 100644 index 0000000..4dab3c6 --- /dev/null +++ b/0035-customize-sysprep-Move-firstboot-docs-from-sysprep-c.patch @@ -0,0 +1,91 @@ +From 4c926d7a4b2dc5ac5eaee443f1a901f9e3ad99d4 Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Tue, 5 May 2026 12:18:51 +0100 +Subject: [PATCH] customize, sysprep: Move firstboot docs from sysprep -> + customize + +As a first step towards consolidating all firstboot documentation in +one place, move the "FIRSTBOOT VS SCRIPTS" documentation from +virt-sysprep(1) to virt-customize(1). This section will later be +reworked and expanded, but in this commit it's just a straight move. + +(cherry picked from commit 0a148a87541ed5f5b934103e2a1bc7f701d89833) +--- + customize/virt-customize.pod | 26 ++++++++++++++++++++++++++ + sysprep/virt-sysprep.pod | 26 -------------------------- + 2 files changed, 26 insertions(+), 26 deletions(-) + +diff --git a/customize/virt-customize.pod b/customize/virt-customize.pod +index 9fee64ebc..e72e35fac 100644 +--- a/customize/virt-customize.pod ++++ b/customize/virt-customize.pod +@@ -240,6 +240,32 @@ Enable tracing of libguestfs API calls. + + __CUSTOMIZE_OPTIONS__ + ++=head1 FIRSTBOOT ++ ++The two options I<--firstboot> and I<--script> both supply shell ++scripts that are run against the guest. However these two options are ++significantly different. ++ ++I<--firstboot script> uploads the file C