guestfs-tools/SOURCES/0008-drivers-Handle-large-output-from-rpm-ql-command.patch

344 lines
13 KiB
Diff
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

From 777f3ac82c20469c9e438b9fd88a57007fd2c2bb 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] drivers: 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-80214
Reported-by: Nijin Ashok
(cherry picked from commit 5520f1cfae55377c2fe1db3f2974f6006822e0ea)
---
common | 2 +-
m4/guestfs-libraries.m4 | 5 ++---
2 files changed, 3 insertions(+), 4 deletions(-)
Submodule common 766384a45..ee88791e1:
diff --git a/common/mldrivers/linux.ml b/common/mldrivers/linux.ml
index 4e30a8e1f..0dec15495 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 86b21a7c5..1a36ab772 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 a39ac5f3b..7bd55fbae 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 3f5bb1a86..4e368152f 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 80f9425f0..7c66853dd 100644
--- a/m4/guestfs-libraries.m4
+++ b/m4/guestfs-libraries.m4
@@ -19,9 +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.49.8 for guestfs_inspect_get_build_id in
-dnl virt-drivers.
-PKG_CHECK_MODULES([LIBGUESTFS], [libguestfs >= 1.49.8])
+dnl We need libguestfs >= 1:1.54.0-4.el9_6 for guestfs_sh_out.
+PKG_CHECK_MODULES([LIBGUESTFS], [libguestfs >= 1.54.0])
dnl Test if it's GNU or XSI strerror_r.
AC_FUNC_STRERROR_R