Fix failure if the kernel-source package is installed in the source VM

resolves: RHEL-80080
This commit is contained in:
Richard W.M. Jones 2025-02-25 11:08:26 +00:00
parent d9760b8da3
commit 23a230a8f8
3 changed files with 491 additions and 4 deletions

View File

@ -0,0 +1,141 @@
From 453e38ce02118fc81fe9ec9ca8f81ae341c93fe7 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Wed, 19 Feb 2025 15:15:30 +0000
Subject: [PATCH] test-data: phony fedora: Add simple static /bin/sh
This is able to do enough to run commands via g#sh and related calls
inside the phony Fedora image.
(cherry picked from
guestfs-tools commit 4c5854f54e1da0d96807acb1b047bbf34694a0bb)
(cherry picked from commit de073524ec9fbfa179f2721c36013b2bb312e73f)
---
test-data/phony-guests/fedora.c | 76 +++++++++++++++++++++--
test-data/phony-guests/make-fedora-img.pl | 6 +-
2 files changed, 75 insertions(+), 7 deletions(-)
diff --git a/test-data/phony-guests/fedora.c b/test-data/phony-guests/fedora.c
index c74976d6..0e324d21 100644
--- a/test-data/phony-guests/fedora.c
+++ b/test-data/phony-guests/fedora.c
@@ -16,13 +16,14 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-/* This is "just enough" of a binary to look like RPM and dracut, as
- * far as virt-v2v is concerned.
+/* This is "just enough" of a binary to look like /bin/sh, RPM and
+ * dracut, as far as virt-v2v is concerned.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <unistd.h>
/* NB: This is also defined in make-fedora-img.pl */
#define KVER "5.19.0-0.rc1.14.fc37.x86_64"
@@ -34,6 +35,27 @@ get_basename (const char *str)
return ret == NULL ? str : ret + 1;
}
+static void
+add_str (char ***argv, size_t *argc, char *str)
+{
+ (*argc)++;
+ (*argv) = realloc (*argv, *argc * sizeof (char *));
+ (*argv)[*argc-1] = str;
+}
+
+static void
+add_null (char ***argv, size_t *argc)
+{
+ add_str (argv, argc, NULL);
+}
+
+static void
+add (char ***argv, size_t *argc, const char *s, size_t len)
+{
+ char *copy = strndup (s, len);
+ add_str (argv, argc, copy);
+}
+
int
main (int argc, char *argv[])
{
@@ -57,10 +79,54 @@ main (int argc, char *argv[])
strcmp (get_basename (argv[0]), "dracut") == 0) {
// do nothing, pretend to rebuild the initramfs
}
+ else if (argc == 3 &&
+ strcmp (get_basename (argv[0]), "sh") == 0 &&
+ strcmp (argv[1], "-c") == 0) {
+ /* Split the command and execute it. Only handles trivial cases. */
+ char *cmd = argv[2];
+ char **cmdv = NULL;
+ size_t i, cmdvlen = 0, n;
+ const size_t len = strlen (cmd);
+
+ for (i = 0; i < len;) {
+ switch (cmd[i]) {
+ case ' ': case '\t':
+ i++;
+ continue;
+
+ case '"':
+ n = strcspn (&cmd[i+1], "\"");
+ add (&cmdv, &cmdvlen, &cmd[i+1], n);
+ i += n+2;
+ break;
+
+ case '\'':
+ n = strcspn (&cmd[i+1], "'");
+ add (&cmdv, &cmdvlen, &cmd[i+1], n);
+ i += n+2;
+ break;
+
+ default:
+ n = strcspn (&cmd[i], " \t");
+ add (&cmdv, &cmdvlen, &cmd[i], n);
+ i += n;
+ }
+ }
+ add_null (&cmdv, &cmdvlen);
+
+ execvp (cmdv[0], cmdv);
+ perror (cmdv[0]);
+ exit (EXIT_FAILURE);
+ }
else {
- fprintf (stderr, "phony Fedora: unknown command\n");
- exit (1);
+ int i;
+
+ fprintf (stderr, "ERROR: test-data/phony-guests/fedora.c: "
+ "unexpected command:\n");
+ for (i = 0; i < argc; ++i)
+ fprintf (stderr, "argv[%d] = %s\n", i, argv[i]);
+ exit (EXIT_FAILURE);
}
- exit (0);
+ exit (EXIT_SUCCESS);
}
diff --git a/test-data/phony-guests/make-fedora-img.pl b/test-data/phony-guests/make-fedora-img.pl
index 188cdd75..f661e8ec 100755
--- a/test-data/phony-guests/make-fedora-img.pl
+++ b/test-data/phony-guests/make-fedora-img.pl
@@ -369,8 +369,10 @@ $g->ln_s ('/bin/test1', '/bin/test5');
$g->mkfifo (0777, '/bin/test6');
$g->mknod (0777, 10, 10, '/bin/test7');
-# Virt-v2v needs an RPM command, or at least something which acts
-# similarly, and also a dracut command.
+# Virt-v2v needs a /bin/sh, an RPM command and a dracut command, or at
+# least something which acts similarly to those.
+$g->upload ('fedora-static-bin', '/bin/sh');
+$g->chmod (0777, '/bin/sh');
$g->upload ('fedora-static-bin', '/bin/rpm');
$g->chmod (0777, '/bin/rpm');
$g->upload ('fedora-static-bin', '/sbin/dracut');

View File

@ -0,0 +1,342 @@
From 0a5fb62a2816d16cbdd7452c8bd95a8fcd2d4a02 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Wed, 19 Feb 2025 14:16:13 +0000
Subject: [PATCH] convert: Handle large output from 'rpm -ql' command
This requires the new guestfs_sh_out API from libguestfs 1.55.6.
Update common submodule to include:
Richard W.M. Jones (3):
mlstdutils: Reimplement String.find, add String.find_from
mlstdutils: Reimplement String.nsplit tail recursively
mldrivers: Handle large output from 'rpm -ql' command
Fixes: https://issues.redhat.com/browse/RHEL-80080
Reported-by: Nijin Ashok
(cherry picked from commit 5dfe62885bc7d81c7d3c9260ba6def5cb477b5fd)
---
common | 2 +-
m4/guestfs-libraries.m4 | 4 +++-
2 files changed, 4 insertions(+), 2 deletions(-)
Submodule common 2bb8c83c..18310179:
diff --git a/common/mldrivers/linux.ml b/common/mldrivers/linux.ml
index 4e30a8e1..0dec1549 100644
--- a/common/mldrivers/linux.ml
+++ b/common/mldrivers/linux.ml
@@ -58,76 +58,84 @@ and do_remove g root packages =
let file_list_of_package (g : Guestfs.guestfs) root app =
let package_format = g#inspect_get_package_format root in
- match package_format with
- | "deb" ->
- let cmd = [| "dpkg"; "-L"; app.G.app2_name |] in
- debug "%s" (String.concat " " (Array.to_list cmd));
- let files = g#command_lines cmd in
- let files = Array.to_list files in
+
+ let cmd =
+ match package_format with
+ | "deb" -> sprintf "dpkg -L %s" (quote app.G.app2_name)
+
+ | "rpm" ->
+ (* Since RPM allows multiple packages installed with the same
+ * name, always check the full NEVR here (RHBZ#1161250).
+ *
+ * In RPM < 4.11 query commands that use the epoch number in the
+ * package name did not work.
+ *
+ * For example:
+ * RHEL 6 (rpm 4.8.0):
+ * $ rpm -q tar-2:1.23-11.el6.x86_64
+ * package tar-2:1.23-11.el6.x86_64 is not installed
+ * Fedora 20 (rpm 4.11.2):
+ * $ rpm -q tar-2:1.26-30.fc20.x86_64
+ * tar-1.26-30.fc20.x86_64
+ *)
+ let is_rpm_lt_4_11 () =
+ let ver =
+ try
+ (* Since we're going to run 'rpm' below anyway, seems safe
+ * to run it here and assume the binary works.
+ *)
+ let cmd = [| "rpm"; "--version" |] in
+ debug "%s" (String.concat " " (Array.to_list cmd));
+ let ver = g#command_lines cmd in
+ let ver =
+ if Array.length ver > 0 then ver.(0) else raise Not_found in
+ debug "%s" ver;
+ let ver = String.nsplit " " ver in
+ let ver =
+ match ver with
+ | [ "RPM"; "version"; ver ] -> ver
+ | _ -> raise Not_found in
+ if not (PCRE.matches re_version ver) then raise Not_found;
+ (int_of_string (PCRE.sub 1), int_of_string (PCRE.sub 2))
+ with Not_found ->
+ (* 'rpm' not installed? Hmm... *)
+ (0, 0) in
+ ver < (4, 11)
+ in
+ let pkg_name =
+ if app.G.app2_epoch = Int32.zero || is_rpm_lt_4_11 () then
+ sprintf "%s-%s-%s" app.G.app2_name app.G.app2_version
+ app.G.app2_release
+ else
+ sprintf "%s-%ld:%s-%s" app.G.app2_name app.G.app2_epoch
+ app.G.app2_version app.G.app2_release in
+ sprintf "rpm -ql %s" (quote pkg_name)
+
+ | format ->
+ error (f_"dont know how to get list of files from package using %s")
+ format in
+
+ debug "file_list_of_package: running: %s" cmd;
+
+ (* Some packages have a lot of files, too many to list without
+ * breaking the maximum message size assumption in libguestfs.
+ * To cope with this, use guestfs_sh_out, added in 1.55.6.
+ * https://issues.redhat.com/browse/RHEL-80080
+ *)
+ let tmpfile = Filename.temp_file "v2vcmd" ".out" in
+ On_exit.unlink tmpfile;
+ g#sh_out cmd tmpfile;
+ let files = read_whole_file tmpfile in
+
+ (* RPM prints "(contains no files)" on stdout when a package
+ * has no files in it:
+ * https://github.com/rpm-software-management/rpm/issues/962
+ *)
+ if String.is_prefix files "(contains no files)" then []
+ else (
+ let files = String.nsplit "\n" files in
List.sort compare files
-
- | "rpm" ->
- (* Since RPM allows multiple packages installed with the same
- * name, always check the full NEVR here (RHBZ#1161250).
- *
- * In RPM < 4.11 query commands that use the epoch number in the
- * package name did not work.
- *
- * For example:
- * RHEL 6 (rpm 4.8.0):
- * $ rpm -q tar-2:1.23-11.el6.x86_64
- * package tar-2:1.23-11.el6.x86_64 is not installed
- * Fedora 20 (rpm 4.11.2):
- * $ rpm -q tar-2:1.26-30.fc20.x86_64
- * tar-1.26-30.fc20.x86_64
- *)
- let is_rpm_lt_4_11 () =
- let ver =
- try
- (* Since we're going to run 'rpm' below anyway, seems safe
- * to run it here and assume the binary works.
- *)
- let cmd = [| "rpm"; "--version" |] in
- debug "%s" (String.concat " " (Array.to_list cmd));
- let ver = g#command_lines cmd in
- let ver = if Array.length ver > 0 then ver.(0) else raise Not_found in
- debug "%s" ver;
- let ver = String.nsplit " " ver in
- let ver =
- match ver with
- | [ "RPM"; "version"; ver ] -> ver
- | _ -> raise Not_found in
- if not (PCRE.matches re_version ver) then raise Not_found;
- (int_of_string (PCRE.sub 1), int_of_string (PCRE.sub 2))
- with Not_found ->
- (* 'rpm' not installed? Hmm... *)
- (0, 0) in
- ver < (4, 11)
- in
- let pkg_name =
- if app.G.app2_epoch = Int32.zero || is_rpm_lt_4_11 () then
- sprintf "%s-%s-%s" app.G.app2_name app.G.app2_version
- app.G.app2_release
- else
- sprintf "%s-%ld:%s-%s" app.G.app2_name app.G.app2_epoch
- app.G.app2_version app.G.app2_release in
- let cmd = [| "rpm"; "-ql"; pkg_name |] in
- debug "%s" (String.concat " " (Array.to_list cmd));
- let files = g#command_lines cmd in
- (* RPM prints "(contains no files)" on stdout when a package
- * has no files in it:
- * https://github.com/rpm-software-management/rpm/issues/962
- *)
- if files = [| "(contains no files)" |] then
- []
- else (
- let files = Array.to_list files in
- List.sort compare files
- )
-
- | format ->
- error (f_"dont know how to get list of files from package using %s")
- format
+ )
let is_file_owned (g : G.guestfs) root path =
let package_format = g#inspect_get_package_format root in
diff --git a/common/mlstdutils/std_utils.ml b/common/mlstdutils/std_utils.ml
index 5be358de..212a1513 100644
--- a/common/mlstdutils/std_utils.ml
+++ b/common/mlstdutils/std_utils.ml
@@ -98,24 +98,27 @@ module String = struct
and len = length str in
len >= sufflen && sub str (len - sufflen) sufflen = suffix
- let rec find s sub =
- let len = length s in
+ let find_from str pos sub =
let sublen = length sub in
- let rec loop i =
- if i <= len-sublen then (
- let rec loop2 j =
- if j < sublen then (
- if s.[i+j] = sub.[j] then loop2 (j+1)
- else -1
- ) else
- i (* found *)
- in
- let r = loop2 0 in
- if r = -1 then loop (i+1) else r
- ) else
- -1 (* not found *)
- in
- loop 0
+ if sublen = 0 then
+ 0
+ else (
+ let found = ref 0 in
+ let len = length str in
+ try
+ for i = pos to len - sublen do
+ let j = ref 0 in
+ while unsafe_get str (i + !j) = unsafe_get sub !j do
+ incr j;
+ if !j = sublen then begin found := i; raise Exit; end;
+ done;
+ done;
+ -1
+ with
+ Exit -> !found
+ )
+
+ let find str sub = find_from str 0 sub
let rec replace s s1 s2 =
let len = length s in
@@ -145,7 +148,7 @@ module String = struct
else if n >= len then str, ""
else sub str 0 n, sub str n (len-n)
- let rec split sep str =
+ let split sep str =
let seplen = length sep in
let strlen = length str in
let i = find str sep in
@@ -154,20 +157,36 @@ module String = struct
sub str 0 i, sub str (i + seplen) (strlen - i - seplen)
)
- and nsplit ?(max = 0) sep str =
+ let nsplit ?(max = 0) sep str =
if max < 0 then
invalid_arg "String.nsplit: max parameter should not be negative";
- (* If we reached the limit, OR if the pattern does not match the string
- * at all, return the rest of the string as a single element list.
- *)
- if max = 1 || find str sep = -1 then
- [str]
- else (
- let s1, s2 = split sep str in
- let max = if max = 0 then 0 else max - 1 in
- s1 :: nsplit ~max sep s2
- )
+ let len = String.length str in
+ let seplen = String.length sep in
+
+ let rec loop iters posn acc =
+ (* If we reached the limit, OR if the pattern does not match
+ * the string at all, return the rest of the string.
+ *)
+ if max > 0 && iters = max then (
+ let rest =
+ if posn = 0 then str else String.sub str posn (len-posn) in
+ List.rev (rest :: acc)
+ )
+ else (
+ let end_ = find_from str posn sep in
+ if end_ = -1 then (
+ let rest =
+ if posn = 0 then str else String.sub str posn (len-posn) in
+ List.rev (rest :: acc)
+ )
+ else (
+ let acc = String.sub str posn (end_-posn) :: acc in
+ loop (iters+1) (end_+seplen) acc
+ )
+ )
+ in
+ loop 1 0 []
let rec lines_split str =
let buf = Buffer.create 16 in
diff --git a/common/mlstdutils/std_utils.mli b/common/mlstdutils/std_utils.mli
index c320b877..72a2d44c 100644
--- a/common/mlstdutils/std_utils.mli
+++ b/common/mlstdutils/std_utils.mli
@@ -82,6 +82,10 @@ module String : sig
val find : string -> string -> int
(** [find str sub] searches for [sub] as a substring of [str]. If
found it returns the index. If not found, it returns [-1]. *)
+ val find_from : string -> int -> string -> int
+ (** [find_from str start sub] searches for [sub] as a substring of [str],
+ starting at index [start]. If found it returns the index.
+ If not found, it returns [-1]. *)
val replace : string -> string -> string -> string
(** [replace str s1 s2] replaces all instances of [s1] appearing in
[str] with [s2]. *)
diff --git a/common/mlstdutils/std_utils_tests.ml b/common/mlstdutils/std_utils_tests.ml
index 3f5bb1a8..4e368152 100644
--- a/common/mlstdutils/std_utils_tests.ml
+++ b/common/mlstdutils/std_utils_tests.ml
@@ -113,13 +113,19 @@ let test_string_nsplit ctx =
assert_equal_stringlist [""] (String.nsplit " " "");
assert_equal_stringlist ["abc"] (String.nsplit " " "abc");
assert_equal_stringlist ["a"; "b"; "c"] (String.nsplit " " "a b c");
+ assert_equal_stringlist ["abc"; "d"; "e"] (String.nsplit " " "abc d e");
assert_equal_stringlist ["a"; "b"; "c"; ""] (String.nsplit " " "a b c ");
assert_equal_stringlist [""; "a"; "b"; "c"] (String.nsplit " " " a b c");
assert_equal_stringlist [""; "a"; "b"; "c"; ""] (String.nsplit " " " a b c ");
assert_equal_stringlist ["a b c d"] (String.nsplit ~max:1 " " "a b c d");
assert_equal_stringlist ["a"; "b c d"] (String.nsplit ~max:2 " " "a b c d");
assert_equal_stringlist ["a"; "b"; "c d"] (String.nsplit ~max:3 " " "a b c d");
- assert_equal_stringlist ["a"; "b"; "c"; "d"] (String.nsplit ~max:10 " " "a b c d")
+ assert_equal_stringlist ["a"; "b"; "c"; "d"] (String.nsplit ~max:10 " " "a b c d");
+
+ (* Test that nsplit can handle large strings. *)
+ let xs = Array.to_list (Array.make 10_000_000 "xyz") in
+ let xs_concat = String.concat " " xs in
+ assert_equal_stringlist xs (String.nsplit " " xs_concat)
(* Test Std_utils.String.lines_split. *)
let test_string_lines_split ctx =
diff --git a/m4/guestfs-libraries.m4 b/m4/guestfs-libraries.m4
index f0cad0f5..3cb3cabd 100644
--- a/m4/guestfs-libraries.m4
+++ b/m4/guestfs-libraries.m4
@@ -18,7 +18,9 @@
dnl Any C libraries required by virt-v2v.
dnl Of course we need libguestfs.
-PKG_CHECK_MODULES([LIBGUESTFS], [libguestfs >= 1.44])
+dnl
+dnl We need libguestfs >= 1:1.54.0-4.el9_6 for guestfs_sh_out.
+PKG_CHECK_MODULES([LIBGUESTFS], [libguestfs >= 1.54.0])
dnl And libnbd.
PKG_CHECK_MODULES([LIBNBD], [libnbd >= 1.10])

View File

@ -8,7 +8,7 @@
Name: virt-v2v
Epoch: 1
Version: 2.7.1
Release: 4%{?dist}
Release: 5%{?dist}
Summary: Convert a virtual machine to run on KVM
License: GPL-2.0-or-later AND LGPL-2.0-or-later
@ -59,6 +59,8 @@ Patch0028: 0028-in-place-Add-new-O-option-to-write-inspector-XML.patch
Patch0029: 0029-convert-Mention-operating-system-in-error-message.patch
Patch0030: 0030-Update-common-submodule.patch
Patch0031: 0031-convert-Use-yum-apt-.-for-package-removals-not-rpm-d.patch
Patch0032: 0032-test-data-phony-fedora-Add-simple-static-bin-sh.patch
Patch0033: 0033-convert-Handle-large-output-from-rpm-ql-command.patch
%if !0%{?rhel}
# libguestfs hasn't been built on i686 for a while since there is no
@ -83,7 +85,7 @@ BuildRequires: /usr/bin/pod2man
BuildRequires: gcc
BuildRequires: ocaml >= 4.04
BuildRequires: libguestfs-devel >= 1:1.49.8-1
BuildRequires: libguestfs-devel >= 1:1.54.0-4.el9
BuildRequires: augeas-devel
BuildRequires: bash-completion
BuildRequires: file-devel
@ -127,7 +129,7 @@ BuildRequires: sqlite
BuildRequires: gnupg2
%endif
Requires: libguestfs%{?_isa} >= 1:1.49.8-1
Requires: libguestfs%{?_isa} >= 1:1.54.0-4.el9
Requires: guestfs-tools >= 1.49.7-1
# XFS is the default filesystem in Fedora and RHEL.
@ -360,7 +362,7 @@ make -C tests TESTS=test-fedora-luks-on-lvm-conversion.sh check
%changelog
* Tue Feb 11 2025 Richard W.M. Jones <rjones@redhat.com> - 1:2.7.1-4
* Tue Feb 25 2025 Richard W.M. Jones <rjones@redhat.com> - 1:2.7.1-5
- Rebase to upstream development version 2.7.1
resolves: RHEL-56813
- Replace Jansson with json-c
@ -373,6 +375,8 @@ make -C tests TESTS=test-fedora-luks-on-lvm-conversion.sh check
resolves: RHEL-77989
- convert: Use yum/apt/... for package removals
resolves: RHEL-71522
- Fix failure if the kernel-source package is installed in the source VM
resolves: RHEL-80080
* Thu Sep 05 2024 Richard W.M. Jones <rjones@redhat.com> - 1:2.5.9-1
- Rebase to upstream development version 2.5.9