import CS libguestfs-1.54.0-11.el9

This commit is contained in:
eabdullin 2025-09-15 12:14:38 +00:00
parent 59d72cefdc
commit b64cb2c7bf
21 changed files with 2115 additions and 3 deletions

View File

@ -0,0 +1,45 @@
From 19c4d1c8b9f278e054594660b5392d6c08a59d8f Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Mon, 10 Mar 2025 18:52:08 +0000
Subject: [PATCH] lib: Print kernel utsname in debug output
Useful for debugging problems caused by the host kernel. In
particular we were looking at a problem with passt creating a user
namespace but didn't know what exact kernel was being used.
(cherry picked from commit 31fa712aa07190f2c5ed789712b92b4be2d51488)
---
lib/launch.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/lib/launch.c b/lib/launch.c
index b9b76e50..9c44612d 100644
--- a/lib/launch.c
+++ b/lib/launch.c
@@ -36,6 +36,7 @@
#include <signal.h>
#include <sys/stat.h>
#include <sys/types.h>
+#include <sys/utsname.h>
#include <sys/wait.h>
#include <errno.h>
#include <assert.h>
@@ -93,6 +94,7 @@ guestfs_impl_launch (guestfs_h *g)
struct backend *b;
CLEANUP_FREE char *backend = guestfs_get_backend (g);
int mask;
+ struct utsname utsname;
debug (g, "launch: program=%s", g->program);
if (STRNEQ (g->identifier, ""))
@@ -109,6 +111,10 @@ guestfs_impl_launch (guestfs_h *g)
if (mask >= 0)
debug (g, "launch: umask=0%03o", (unsigned) mask);
debug (g, "launch: euid=%ju", (uintmax_t) geteuid ());
+ if (uname (&utsname) == 0)
+ debug (g, "launch: host: %s %s %s %s %s",
+ utsname.sysname, utsname.nodename, utsname.release,
+ utsname.version, utsname.machine);
}
/* Launch the appliance. */

View File

@ -0,0 +1,29 @@
From fa1c16528267c89de8a2ecebd44405cbd04fa0ee Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Tue, 11 Mar 2025 13:36:12 +0000
Subject: [PATCH] daemon: Fix loongarch64 detection on RHEL 9
$ rpm -q file
file-5.39-16.el9.x86_64
$ file ./test-data/binaries/bin-loongarch64-dynamic
./test-data/binaries/bin-loongarch64-dynamic: ELF 64-bit LSB pie executable, *unknown arch 0x102* version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-loongarch-lp64d.so.1, BuildID[sha1]=7622a1a70bf6e697851ac3790557e1ca686459b5, for GNU/Linux 5.19.0, stripped
Updates: commit 729d6d55ea84494f0398d02450bd29c39c55f0bd
(cherry picked from commit 4176b2043f6cf65f8f5f4f7d6fa39beb9c0a22c6)
---
daemon/filearch.ml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/daemon/filearch.ml b/daemon/filearch.ml
index 7dfc1cb7..e2bd5a2c 100644
--- a/daemon/filearch.ml
+++ b/daemon/filearch.ml
@@ -100,6 +100,8 @@ and canonical_elf_arch bits endianness elf_arch =
)
else if substr "LoongArch" then
sprintf "loongarch%s" bits
+ else if substr "*unknown arch 0x102*" then (* file command on RHEL 9 *)
+ sprintf "loongarch%s" bits
else
elf_arch

View File

@ -0,0 +1,69 @@
From 68cecb64758b8ff6d3af842721cb7cf706cb5d0c Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Wed, 16 Apr 2025 10:01:33 +0100
Subject: [PATCH] daemon: inspect: Add some debugging of /usr merging
(cherry picked from commit 2d1e8941301373d04a436333219358a72f9660f1)
---
daemon/inspect.ml | 15 ++++++++++++++-
1 file changed, 14 insertions(+), 1 deletion(-)
diff --git a/daemon/inspect.ml b/daemon/inspect.ml
index f6e0ce1f..bec9d567 100644
--- a/daemon/inspect.ml
+++ b/daemon/inspect.ml
@@ -47,18 +47,21 @@ let rec inspect_os () =
* multiple filesystems. Gather all the inspected information in the
* inspect_fs struct of the root filesystem.
*)
+ eprintf "inspect_os: collect_coreos_inspection_info\n%!";
let fses = collect_coreos_inspection_info fses in
(* Check if the same filesystem was listed twice as root in fses.
* This may happen for the *BSD root partition where an MBR partition
* is a shadow of the real root partition probably /dev/sda5
*)
+ eprintf "inspect_os: check_for_duplicated_bsd_root\n%!";
let fses = check_for_duplicated_bsd_root fses in
(* For Linux guests with a separate /usr filesystem, merge some of the
* inspected information in that partition to the inspect_fs struct
* of the root filesystem.
*)
+ eprintf "inspect_os: collect_linux_inspection_info\n%!";
let fses = collect_linux_inspection_info fses in
(* Save what we found in a global variable. *)
@@ -194,6 +197,9 @@ and collect_linux_inspection_info fses =
* or other ways to identify the OS).
*)
and collect_linux_inspection_info_for fses root =
+ eprintf "inspect_os: collect_linux_inspection_info_for %s\n"
+ (string_of_location root.fs_location);
+
let root_fstab =
match root with
| { role = RoleRoot { fstab = f } } -> f
@@ -207,14 +213,21 @@ and collect_linux_inspection_info_for fses root =
(* This checks that this usr is found in the fstab of
* the root filesystem.
*)
+ eprintf "inspect_os: checking if %s found in fstab of this root\n"
+ (string_of_location usr_mp);
List.exists (
fun (mountable, _) ->
+ eprintf "inspect_os: collect_linux_inspection_info_for: \
+ compare %s = %s\n"
+ (Mountable.to_string usr_mp.mountable)
+ (Mountable.to_string mountable);
usr_mp.mountable = mountable
) root_fstab
| _ -> false
) fses in
- eprintf "collect_linux_inspection_info_for: merging:\n%sinto:\n%s"
+ eprintf "inspect_os: collect_linux_inspection_info_for: merging:\n\
+ %sinto:\n%s"
(string_of_fs usr) (string_of_fs root);
merge usr root;
root

View File

@ -0,0 +1,490 @@
From 4e27b259c166d87a57d133a79b8eed3b556288b8 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Wed, 16 Apr 2025 11:35:13 +0100
Subject: [PATCH] generator: Implement struct FDevice type
This acts just like FString except that we do reverse device name
translation on it. The only use is in the 'pvs-full' API where we
will use it (in a subsequent commit) to reverse translate the pv_name
field (a device name) before returning it from the daemon.
Compare this to the 'pvs' API which also returns a list of device
names, but using the generator's 'RStructList (RDevice,...)' return
type, where RDevice is similarly reverse translated.
Note in the library-side bindings, because the name has already been
translated in the daemon, we just treat it exactly the same as
FString. The vast majority of this patch is this mechanical change.
(cherry picked from commit 0ff73a42c7f4f309fbab11ea2e89ee6f0501367d)
---
generator/GObject.ml | 6 +++---
generator/OCaml.ml | 6 ++----
generator/XDR.ml | 2 +-
generator/c.ml | 20 +++++++++-----------
generator/csharp.ml | 2 +-
generator/daemon.ml | 30 ++++++++++++++++++++++++++----
generator/erlang.ml | 2 +-
generator/golang.ml | 4 ++--
generator/java.ml | 7 ++++---
generator/lua.ml | 2 +-
generator/perl.ml | 4 ++--
generator/php.ml | 4 ++--
generator/python.ml | 2 +-
generator/ruby.ml | 4 ++--
generator/rust.ml | 6 +++---
generator/types.ml | 1 +
generator/types.mli | 1 +
17 files changed, 62 insertions(+), 41 deletions(-)
diff --git a/generator/GObject.ml b/generator/GObject.ml
index 6c74b31b..e7462c0e 100644
--- a/generator/GObject.ml
+++ b/generator/GObject.ml
@@ -206,7 +206,7 @@ let generate_gobject_struct_header filename typ cols () =
pr " * @%s: An unsigned 64-bit integer\n" n
| n, FInt64 ->
pr " * @%s: A signed 64-bit integer\n" n
- | n, FString ->
+ | n, (FString|FDevice) ->
pr " * @%s: A NULL-terminated string\n" n
| n, FBuffer ->
pr " * @%s: A GByteArray\n" n
@@ -231,7 +231,7 @@ let generate_gobject_struct_header filename typ cols () =
pr " guint64 %s;\n" n
| n, FInt64 ->
pr " gint64 %s;\n" n
- | n, FString ->
+ | n, (FString|FDevice) ->
pr " gchar *%s;\n" n
| n, FBuffer ->
pr " GByteArray *%s;\n" n
@@ -1228,7 +1228,7 @@ guestfs_session_close (GuestfsSession *session, GError **err)
| n, FUUID ->
pr "%smemcpy (%s%s, %s%s, sizeof (%s%s));\n"
indent dst n src n dst n
- | n, FString ->
+ | n, (FString|FDevice) ->
pr "%sif (%s%s) %s%s = g_strdup (%s%s);\n"
indent src n dst n src n
| n, FBuffer ->
diff --git a/generator/OCaml.ml b/generator/OCaml.ml
index 1e6f603a..4ef07e27 100644
--- a/generator/OCaml.ml
+++ b/generator/OCaml.ml
@@ -512,7 +512,7 @@ copy_table (char * const * argv)
List.iteri (
fun i col ->
(match col with
- | name, FString ->
+ | name, (FString|FDevice) ->
pr " v = caml_copy_string (%s->%s);\n" typ name
| name, FBuffer ->
pr " v = caml_alloc_initialized_string (%s->%s_len, %s->%s);\n"
@@ -839,9 +839,7 @@ and generate_ocaml_structure_decls () =
pr "type %s = {\n" typ;
List.iter (
function
- | name, FString -> pr " %s : string;\n" name
- | name, FBuffer -> pr " %s : string;\n" name
- | name, FUUID -> pr " %s : string;\n" name
+ | name, (FString|FDevice|FBuffer|FUUID) -> pr " %s : string;\n" name
| name, (FBytes|FInt64|FUInt64) -> pr " %s : int64;\n" name
| name, (FInt32|FUInt32) -> pr " %s : int32;\n" name
| name, FChar -> pr " %s : char;\n" name
diff --git a/generator/XDR.ml b/generator/XDR.ml
index 566ba69e..e71d97f5 100644
--- a/generator/XDR.ml
+++ b/generator/XDR.ml
@@ -66,7 +66,7 @@ let generate_xdr () =
pr "struct guestfs_int_%s {\n" typ;
List.iter (function
| name, FChar -> pr " char %s;\n" name
- | name, FString -> pr " string %s<>;\n" name
+ | name, (FString|FDevice) -> pr " string %s<>;\n" name
| name, FBuffer -> pr " opaque %s<>;\n" name
| name, FUUID -> pr " opaque %s[32];\n" name
| name, FInt32 -> pr " int %s;\n" name
diff --git a/generator/c.ml b/generator/c.ml
index 09181028..124aeb6d 100644
--- a/generator/c.ml
+++ b/generator/c.ml
@@ -352,7 +352,7 @@ and generate_structs_pod () =
| name, FInt32 -> pr " int32_t %s;\n" name
| name, (FUInt64|FBytes) -> pr " uint64_t %s;\n" name
| name, FInt64 -> pr " int64_t %s;\n" name
- | name, FString -> pr " char *%s;\n" name
+ | name, (FString|FDevice) -> pr " char *%s;\n" name
| name, FBuffer ->
pr " /* The next two fields describe a byte array. */\n";
pr " uint32_t %s_len;\n" name;
@@ -609,7 +609,7 @@ extern GUESTFS_DLL_PUBLIC void *guestfs_next_private (guestfs_h *g, const char *
List.iter (
function
| name, FChar -> pr " char %s;\n" name
- | name, FString -> pr " char *%s;\n" name
+ | name, (FString|FDevice) -> pr " char *%s;\n" name
| name, FBuffer ->
pr " uint32_t %s_len;\n" name;
pr " char *%s;\n" name
@@ -916,7 +916,7 @@ and generate_client_structs_compare () =
fun { s_name = typ; s_cols = cols } ->
let has_nonnumeric_cols =
let nonnumeric = function
- | _,(FString|FUUID|FBuffer) -> true
+ | _,(FString|FDevice|FUUID|FBuffer) -> true
| _,(FChar|FUInt32|FInt32|FUInt64|FBytes|FInt64|FOptPercent) -> false
in
List.exists nonnumeric cols in
@@ -932,7 +932,7 @@ and generate_client_structs_compare () =
);
List.iter (
function
- | name, FString ->
+ | name, (FString|FDevice) ->
pr " r = strcmp (s1->%s, s2->%s);\n" name name;
pr " if (r != 0) return r;\n"
| name, FBuffer ->
@@ -1001,7 +1001,7 @@ and generate_client_structs_copy () =
fun { s_name = typ; s_cols = cols } ->
let has_boxed_cols =
let boxed = function
- | _,(FString|FBuffer) -> true
+ | _,(FString|FDevice|FBuffer) -> true
| _,(FChar|FUUID|FUInt32|FInt32|FUInt64|FBytes|FInt64|FOptPercent) ->
false
in
@@ -1014,8 +1014,7 @@ and generate_client_structs_copy () =
pr "{\n";
List.iter (
function
- | name, FString
- | name, FBuffer -> pr " free (s->%s);\n" name
+ | name, (FString|FDevice|FBuffer) -> pr " free (s->%s);\n" name
| _, FChar
| _, FUUID
| _, FUInt32
@@ -1038,8 +1037,7 @@ and generate_client_structs_copy () =
pr "\n";
List.iter (
function
- | name, FString
- | name, FBuffer -> pr " out->%s = NULL;\n" name
+ | name, (FString|FDevice|FBuffer) -> pr " out->%s = NULL;\n" name
| _, FChar
| _, FUUID
| _, FUInt32
@@ -1051,7 +1049,7 @@ and generate_client_structs_copy () =
) cols;
List.iter (
function
- | name, FString ->
+ | name, (FString|FDevice) ->
pr " out->%s = strdup (inp->%s);\n" name name;
pr " if (out->%s == NULL) goto error;\n" name
| name, FBuffer ->
@@ -1234,7 +1232,7 @@ and generate_client_structs_print_c () =
);
List.iter (
function
- | name, FString ->
+ | name, (FString|FDevice) ->
pr " fprintf (dest, \"%%s%s: %%s%%s\", indent, %s->%s, linesep);\n"
name typ name
| name, FUUID ->
diff --git a/generator/csharp.ml b/generator/csharp.ml
index 43579df5..6ab6c3c9 100644
--- a/generator/csharp.ml
+++ b/generator/csharp.ml
@@ -119,7 +119,7 @@ namespace Guestfs
List.iter (
function
| name, FChar -> pr " char %s;\n" name
- | name, FString -> pr " string %s;\n" name
+ | name, (FString | FDevice) -> pr " string %s;\n" name
| name, FBuffer ->
pr " uint %s_len;\n" name;
pr " string %s;\n" name
diff --git a/generator/daemon.ml b/generator/daemon.ml
index 9ab9e12d..0baa7708 100644
--- a/generator/daemon.ml
+++ b/generator/daemon.ml
@@ -442,15 +442,37 @@ let generate_daemon_stubs actions () =
pr " ret.%s.%s_val = r;\n" n n;
pr " reply ((xdrproc_t) &xdr_guestfs_%s_ret, (char *) &ret);\n"
name
- | RStruct (n, _) ->
+ | RStruct (n, typ) ->
+ (* XXX RStruct containing an FDevice field would require
+ * reverse device name translation. That is not implemented.
+ * See also RStructList immediately below this.
+ *)
+ let cols = (Structs.lookup_struct typ).s_cols in
+ assert (not (List.exists
+ (function (_, FDevice) -> true | _ -> false) cols));
pr " struct guestfs_%s_ret ret;\n" name;
pr " ret.%s = *r;\n" n;
pr " reply ((xdrproc_t) xdr_guestfs_%s_ret, (char *) &ret);\n"
name;
pr " xdr_free ((xdrproc_t) xdr_guestfs_%s_ret, (char *) &ret);\n"
name
- | RStructList (n, _) ->
+ | RStructList (n, typ) ->
pr " struct guestfs_%s_ret ret;\n" name;
+ let cols = (Structs.lookup_struct typ).s_cols in
+ List.iter (
+ function
+ | (fname, FDevice) ->
+ pr " for (size_t i = 0; i < r->guestfs_int_%s_list_len; ++i) {\n"
+ typ;
+ pr " char *field = r->guestfs_int_%s_list_val[i].%s;\n"
+ typ fname;
+ pr " char *rr = reverse_device_name_translation (field);\n";
+ pr " if (!rr) abort ();\n";
+ pr " free (field);\n";
+ pr " r->guestfs_int_%s_list_val[i].%s = rr;\n" typ fname;
+ pr " }\n";
+ | _ -> ()
+ ) cols;
pr " ret.%s = *r;\n" n;
pr " reply ((xdrproc_t) xdr_guestfs_%s_ret, (char *) &ret);\n"
name;
@@ -619,7 +641,7 @@ let generate_daemon_caml_stubs () =
fun i ->
pr " v = Field (retv, %d);\n" i;
function
- | n, (FString|FUUID) ->
+ | n, (FString|FDevice|FUUID) ->
pr " ret->%s = strdup (String_val (v));\n" n;
pr " if (ret->%s == NULL) return NULL;\n" n
| n, FBuffer ->
@@ -986,7 +1008,7 @@ let generate_daemon_lvm_tokenization () =
pr " if (*p) next = p+1; else next = NULL;\n";
pr " *p = '\\0';\n";
(match coltype with
- | FString ->
+ | FString | FDevice ->
pr " r->%s = strdup (tok);\n" name;
pr " if (r->%s == NULL) {\n" name;
pr " perror (\"strdup\");\n";
diff --git a/generator/erlang.ml b/generator/erlang.ml
index 65af75aa..864b3ff0 100644
--- a/generator/erlang.ml
+++ b/generator/erlang.ml
@@ -286,7 +286,7 @@ and generate_erlang_structs () =
List.iteri (
fun i col ->
(match col with
- | name, FString ->
+ | name, (FString|FDevice) ->
pr " if (ei_x_encode_string (buff, %s->%s) != 0) return -1;\n" typ name
| name, FBuffer ->
pr " if (ei_x_encode_string_len (buff, %s->%s, %s->%s_len) != 0) return -1;\n"
diff --git a/generator/golang.ml b/generator/golang.ml
index 0d6a9236..a5b39f5d 100644
--- a/generator/golang.ml
+++ b/generator/golang.ml
@@ -248,7 +248,7 @@ func return_hashtable (argv **C.char) map[string]string {
let n = String.capitalize_ascii n in
match field with
| FChar -> pr " %s byte\n" n
- | FString -> pr " %s string\n" n
+ | FString | FDevice -> pr " %s string\n" n
| FBuffer -> pr " %s []byte\n" n
| FUInt32 -> pr " %s uint32\n" n
| FInt32 -> pr " %s int32\n" n
@@ -267,7 +267,7 @@ func return_hashtable (argv **C.char) map[string]string {
let gon = String.capitalize_ascii n in
match field with
| FChar -> pr " r.%s = byte (c.%s)\n" gon n
- | FString -> pr " r.%s = C.GoString (c.%s)\n" gon n
+ | FString | FDevice -> pr " r.%s = C.GoString (c.%s)\n" gon n
| FBuffer ->
pr " r.%s = C.GoBytes (unsafe.Pointer (c.%s), C.int (c.%s_len))\n"
gon n n
diff --git a/generator/java.ml b/generator/java.ml
index afcddf90..4b74203a 100644
--- a/generator/java.ml
+++ b/generator/java.ml
@@ -560,6 +560,7 @@ public class %s {
List.iter (
function
| name, FString
+ | name, FDevice
| name, FUUID
| name, FBuffer -> pr " public String %s;\n" name
| name, (FBytes|FUInt64|FInt64) -> pr " public long %s;\n" name
@@ -947,7 +948,7 @@ and generate_java_struct_return typ jtyp cols =
pr " jr = (*env)->AllocObject (env, cl);\n";
List.iter (
function
- | name, FString ->
+ | name, (FString|FDevice) ->
pr " fl = (*env)->GetFieldID (env, cl, \"%s\", \"Ljava/lang/String;\");\n" name;
pr " (*env)->SetObjectField (env, jr, fl, (*env)->NewStringUTF (env, r->%s));\n" name;
| name, FUUID ->
@@ -997,7 +998,7 @@ and generate_java_struct_list_return typ jtyp cols =
fun (name, ftyp) ->
(* Get the field ID in 'fl'. *)
let java_field_type = match ftyp with
- | FString | FUUID | FBuffer -> "Ljava/lang/String;"
+ | FString | FDevice | FUUID | FBuffer -> "Ljava/lang/String;"
| FBytes | FUInt64 | FInt64 -> "J"
| FUInt32 | FInt32 -> "I"
| FOptPercent -> "F"
@@ -1007,7 +1008,7 @@ and generate_java_struct_list_return typ jtyp cols =
(* Assign the value to this field. *)
match ftyp with
- | FString ->
+ | FString | FDevice ->
pr " (*env)->SetObjectField (env, jfl, fl,\n";
pr " (*env)->NewStringUTF (env, r->val[i].%s));\n" name;
| FUUID ->
diff --git a/generator/lua.ml b/generator/lua.ml
index 0d7e63be..685645ab 100644
--- a/generator/lua.ml
+++ b/generator/lua.ml
@@ -824,7 +824,7 @@ push_event (lua_State *L, uint64_t event)
(match field with
| FChar ->
pr " lua_pushlstring (L, &v->%s, 1);\n" n
- | FString ->
+ | FString | FDevice ->
pr " lua_pushstring (L, v->%s);\n" n
| FBuffer ->
pr " lua_pushlstring (L, v->%s, v->%s_len);\n" n n
diff --git a/generator/perl.ml b/generator/perl.ml
index 8b9834ef..e0edc249 100644
--- a/generator/perl.ml
+++ b/generator/perl.ml
@@ -607,7 +607,7 @@ and generate_perl_struct_list_code typ cols name style =
pr " hv = newHV ();\n";
List.iter (
function
- | name, FString ->
+ | name, (FString|FDevice) ->
pr " (void) hv_store (hv, \"%s\", %d, newSVpv (r->val[i].%s, 0), 0);\n"
name (String.length name) name
| name, FUUID ->
@@ -645,7 +645,7 @@ and generate_perl_struct_code typ cols name style =
pr " PUSHs (sv_2mortal (newSVpv (\"%s\", 0)));\n" name;
match col with
- | name, FString ->
+ | name, (FString|FDevice) ->
pr " PUSHs (sv_2mortal (newSVpv (r->%s, 0)));\n"
name
| name, FBuffer ->
diff --git a/generator/php.ml b/generator/php.ml
index 99ec66c7..5023e0c5 100644
--- a/generator/php.ml
+++ b/generator/php.ml
@@ -616,7 +616,7 @@ and generate_php_struct_code typ cols =
pr " array_init (return_value);\n";
List.iter (
function
- | name, FString ->
+ | name, (FString|FDevice) ->
pr " guestfs_add_assoc_string (return_value, \"%s\", r->%s, 1);\n" name name
| name, FBuffer ->
pr " guestfs_add_assoc_stringl (return_value, \"%s\", r->%s, r->%s_len, 1);\n"
@@ -650,7 +650,7 @@ and generate_php_struct_list_code typ cols =
pr " array_init (z_elem);\n";
List.iter (
function
- | name, FString ->
+ | name, (FString|FDevice) ->
pr " guestfs_add_assoc_string (z_elem, \"%s\", r->val[c].%s, 1);\n"
name name
| name, FBuffer ->
diff --git a/generator/python.ml b/generator/python.ml
index ad02ea93..5534e74a 100644
--- a/generator/python.ml
+++ b/generator/python.ml
@@ -168,7 +168,7 @@ and generate_python_structs () =
pr " return NULL;\n";
List.iter (
function
- | name, FString ->
+ | name, (FString|FDevice) ->
pr " value = guestfs_int_py_fromstring (%s->%s);\n" typ name;
pr " if (value == NULL)\n";
pr " goto err;\n";
diff --git a/generator/ruby.ml b/generator/ruby.ml
index be3238f6..ea5a3194 100644
--- a/generator/ruby.ml
+++ b/generator/ruby.ml
@@ -526,7 +526,7 @@ and generate_ruby_struct_code typ cols =
pr " volatile VALUE rv = rb_hash_new ();\n";
List.iter (
function
- | name, FString ->
+ | name, (FString|FDevice) ->
pr " rb_hash_aset (rv, rb_str_new2 (\"%s\"), rb_str_new2 (r->%s));\n" name name
| name, FBuffer ->
pr " rb_hash_aset (rv, rb_str_new2 (\"%s\"), rb_str_new (r->%s, r->%s_len));\n" name name name
@@ -556,7 +556,7 @@ and generate_ruby_struct_list_code typ cols =
pr " volatile VALUE hv = rb_hash_new ();\n";
List.iter (
function
- | name, FString ->
+ | name, (FString|FDevice) ->
pr " rb_hash_aset (hv, rb_str_new2 (\"%s\"), rb_str_new2 (r->val[i].%s));\n" name name
| name, FBuffer ->
pr " rb_hash_aset (hv, rb_str_new2 (\"%s\"), rb_str_new (r->val[i].%s, r->val[i].%s_len));\n" name name name
diff --git a/generator/rust.ml b/generator/rust.ml
index 1f5cefa6..f4dcfd72 100644
--- a/generator/rust.ml
+++ b/generator/rust.ml
@@ -115,7 +115,7 @@ extern \"C\" {
List.iter (
function
| n, FChar -> pr " pub %s: i8,\n" n
- | n, FString -> pr " pub %s: String,\n" n
+ | n, (FString|FDevice) -> pr " pub %s: String,\n" n
| n, FBuffer -> pr " pub %s: Vec<u8>,\n" n
| n, FUInt32 -> pr " pub %s: u32,\n" n
| n, FInt32 -> pr " pub %s: i32,\n" n
@@ -130,7 +130,7 @@ extern \"C\" {
List.iter (
function
| n, FChar -> pr " %s: c_char,\n" n
- | n, FString -> pr " %s: *const c_char,\n" n
+ | n, (FString|FDevice) -> pr " %s: *const c_char,\n" n
| n, FBuffer ->
pr " %s_len: usize,\n" n;
pr " %s: *const c_char,\n" n;
@@ -154,7 +154,7 @@ extern \"C\" {
match x with
| n, FChar ->
pr "%s: (*raw).%s as i8,\n" n n;
- | n, FString ->
+ | n, (FString|FDevice) ->
pr "%s: char_ptr_to_string((*raw).%s)?,\n" n n;
| n, FBuffer ->
pr "%s: slice::from_raw_parts((*raw).%s as *const u8, (*raw).%s_len).to_vec(),\n" n n n
diff --git a/generator/types.ml b/generator/types.ml
index d9b00885..c901aa73 100644
--- a/generator/types.ml
+++ b/generator/types.ml
@@ -215,6 +215,7 @@ let defaults = { name = "";
type field =
| FChar (* C 'char' (really, a 7 bit byte). *)
| FString (* nul-terminated ASCII string, NOT NULL. *)
+ | FDevice (* device name, needs reverse transl. *)
| FBuffer (* opaque buffer of bytes, (char *, int) pair *)
| FUInt32
| FInt32
diff --git a/generator/types.mli b/generator/types.mli
index 0ce0d33b..0a4752d5 100644
--- a/generator/types.mli
+++ b/generator/types.mli
@@ -413,6 +413,7 @@ val defaults : action
type field =
| FChar (** C 'char' (really, a 7 bit byte). *)
| FString (** nul-terminated ASCII string, NOT NULL. *)
+ | FDevice (** device name, needs reverse transl. *)
| FBuffer (** opaque buffer of bytes, (char *, int) pair*)
| FUInt32
| FInt32

View File

@ -0,0 +1,68 @@
From b306532e7a4a3f235b24e5dab22ab36b849ac886 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Wed, 16 Apr 2025 11:50:37 +0100
Subject: [PATCH] generator: Use new FDevice type for the pvs-full pv_name
field
Remove the code which did explicit reverse device name translation,
and use the generator's code instead.
(cherry picked from commit 5a16d1120fb7b046974abde43b1c40250bfd1a95)
---
daemon/lvm.c | 29 +----------------------------
generator/structs.ml | 2 +-
2 files changed, 2 insertions(+), 29 deletions(-)
diff --git a/daemon/lvm.c b/daemon/lvm.c
index b8c01f71..7e76e17c 100644
--- a/daemon/lvm.c
+++ b/daemon/lvm.c
@@ -146,34 +146,7 @@ do_vgs (void)
guestfs_int_lvm_pv_list *
do_pvs_full (void)
{
- guestfs_int_lvm_pv_list *r;
- size_t i;
- char *din, *dout;
-
- r = parse_command_line_pvs ();
- if (r == NULL)
- /* parse_command_line_pvs has already called reply_with_error */
- return NULL;
-
- /* The pv_name fields contain device names which must be reverse
- * translated. The problem here is that the generator does not have
- * a "FMountable" field type in types.mli.
- */
- for (i = 0; i < r->guestfs_int_lvm_pv_list_len; ++i) {
- din = r->guestfs_int_lvm_pv_list_val[i].pv_name;
- if (din) {
- dout = reverse_device_name_translation (din);
- if (!dout) {
- /* reverse_device_name_translation has already called reply_with_error*/
- /* XXX memory leak here */
- return NULL;
- }
- r->guestfs_int_lvm_pv_list_val[i].pv_name = dout;
- free (din);
- }
- }
-
- return r;
+ return parse_command_line_pvs ();
}
guestfs_int_lvm_vg_list *
diff --git a/generator/structs.ml b/generator/structs.ml
index c1408804..93a3ed03 100644
--- a/generator/structs.ml
+++ b/generator/structs.ml
@@ -35,7 +35,7 @@ type struc = {
* we have to pull out the LVM columns separately here.
*)
let lvm_pv_cols = [
- "pv_name", FString;
+ "pv_name", FDevice;
"pv_uuid", FUUID;
"pv_fmt", FString;
"pv_size", FBytes;

View File

@ -0,0 +1,46 @@
From 0d466cb25e86ab6f7f6838d35afb37b99f83e0e4 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Wed, 16 Apr 2025 10:13:52 +0100
Subject: [PATCH] daemon: inspect: Resolve Ubuntu 22+ /dev/disk/by-uuid/ in
fstab
Ubuntu 22= uses /dev/disk/by-uuid/ followed by a filesystem UUID in
fstab entries. Resolve these to mountables.
A typical fstab entry looks like this:
# /boot was on /dev/vda2 during curtin installation
/dev/disk/by-uuid/b4e56462-5a64-4272-b76d-f5e58bd8f128 /boot ext4 defaults 0 1
The comment is generated by the installer and appears in the fstab.
This entry would be translated to /dev/sda2.
(cherry picked from commit 7a1ffd744b12c4c79fa1b78341ea714d831f4205)
---
daemon/inspect_fs_unix_fstab.ml | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/daemon/inspect_fs_unix_fstab.ml b/daemon/inspect_fs_unix_fstab.ml
index f5817a31..45c62175 100644
--- a/daemon/inspect_fs_unix_fstab.ml
+++ b/daemon/inspect_fs_unix_fstab.ml
@@ -394,6 +394,19 @@ and resolve_fstab_device spec md_map os_type =
resolve_diskbyid part default
)
+ (* Ubuntu 22+ uses /dev/disk/by-uuid/ followed by a UUID. *)
+ else if String.is_prefix spec "/dev/disk/by-uuid/" then (
+ debug_matching "diskbyuuid";
+ let uuid = String.sub spec 18 (String.length spec - 18) in
+ try
+ (* Try a filesystem UUID. Unclear if this could be a partition UUID
+ * as well, but in the Ubuntu guest I tried it was an fs UUID XXX.
+ *)
+ Mountable.of_device (Findfs.findfs_uuid uuid)
+ with
+ Failure _ -> default
+ )
+
else if PCRE.matches re_freebsd_gpt spec then (
debug_matching "FreeBSD GPT";
(* group 1 (type) is not used *)

View File

@ -0,0 +1,43 @@
From 1b64c54b8a7f3a0ada0cc52fc74d800a55318112 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Wed, 16 Apr 2025 13:19:17 +0100
Subject: [PATCH] generator: Fix implementation of FUUID for OCaml functions
This was implemented wrongly. In the XDR protocol, UUIDs are fixed
buffers of length 32. We can just use memcpy to copy from the OCaml
string to the UUID, but we have to ensure the string length returned
by OCaml is correct (if not we just assert, it's an internal error).
(It didn't even compile before, so we know it was never used).
(cherry picked from commit bcd6b3ec3a1038d840e832732b3910f939566436)
---
generator/daemon.ml | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/generator/daemon.ml b/generator/daemon.ml
index 0baa7708..91decf40 100644
--- a/generator/daemon.ml
+++ b/generator/daemon.ml
@@ -606,6 +606,7 @@ let generate_daemon_caml_stubs () =
#include <string.h>
#include <inttypes.h>
#include <errno.h>
+#include <assert.h>
#include <caml/alloc.h>
#include <caml/callback.h>
@@ -641,9 +642,12 @@ let generate_daemon_caml_stubs () =
fun i ->
pr " v = Field (retv, %d);\n" i;
function
- | n, (FString|FDevice|FUUID) ->
+ | n, (FString|FDevice) ->
pr " ret->%s = strdup (String_val (v));\n" n;
pr " if (ret->%s == NULL) return NULL;\n" n
+ | n, FUUID ->
+ pr " assert (caml_string_length (v) == sizeof ret->%s);\n" n;
+ pr " memcpy (ret->%s, String_val (v), sizeof ret->%s);\n" n n
| n, FBuffer ->
pr " ret->%s_len = caml_string_length (v);\n" n;
pr " ret->%s = strdup (String_val (v));\n" n;

View File

@ -0,0 +1,42 @@
From c7930f21405720f51d74efa9f6f7b9da0132a929 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Wed, 16 Apr 2025 21:31:49 +0100
Subject: [PATCH] Update common submodule
Richard W.M. Jones (1):
mlstdutils: Implement String.implode
---
common | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
Submodule common e9eea65a..8aed7b6a:
diff --git a/common/mlstdutils/std_utils.ml b/common/mlstdutils/std_utils.ml
index 86b21a7c..1177ff69 100644
--- a/common/mlstdutils/std_utils.ml
+++ b/common/mlstdutils/std_utils.ml
@@ -256,6 +256,12 @@ module String = struct
let map_chars f str =
List.map f (explode str)
+ let implode cs =
+ let n = List.length cs in
+ let b = Bytes.create n in
+ List.iteri (Bytes.unsafe_set b) cs;
+ Bytes.to_string b
+
let spaces n = String.make n ' '
let span str accept =
diff --git a/common/mlstdutils/std_utils.mli b/common/mlstdutils/std_utils.mli
index a39ac5f3..6811b4bc 100644
--- a/common/mlstdutils/std_utils.mli
+++ b/common/mlstdutils/std_utils.mli
@@ -123,6 +123,8 @@ module String : sig
(** Explode a string into a list of characters. *)
val map_chars : (char -> 'a) -> string -> 'a list
(** Explode string, then map function over the characters. *)
+ val implode : char list -> string
+ (** Join list of characters into a single string. *)
val spaces : int -> string
(** [spaces n] creates a string of n spaces. *)
val span : string -> string -> int

View File

@ -0,0 +1,659 @@
From 80b2fcb243613bc16217b5908941ed2607f398fd Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Wed, 16 Apr 2025 10:41:59 +0100
Subject: [PATCH] daemon: Rewrite {pvs,vgs,lvs}-full APIs in OCaml
These were previously written in very convoluted C which had to deal
with parsing the crazy output of the "lvm" command. In fact the
parsing was so complex that it was generated by the generator. It's
easier to do this in OCaml.
These are basically legacy APIs. They cannot be expanded and LVM
already supports many more fields. We should replace these with APIs
for getting single named fields from LVM.
(cherry picked from commit a73f248369d35249a9324a0e0df9f7ccd1420d3f)
---
.gitignore | 2 +-
daemon/Makefile.am | 5 +-
daemon/lvm.c | 22 ----
daemon/lvm_full.ml | 221 ++++++++++++++++++++++++++++++++++++++
docs/C_SOURCE_FILES | 1 -
generator/actions_core.ml | 3 +
generator/daemon.ml | 190 --------------------------------
generator/daemon.mli | 1 -
generator/main.ml | 2 -
generator/structs.ml | 4 +-
generator/structs.mli | 7 --
po/POTFILES | 1 -
12 files changed, 229 insertions(+), 230 deletions(-)
create mode 100644 daemon/lvm_full.ml
diff --git a/.gitignore b/.gitignore
index 68b27c79..c5e8f802 100644
--- a/.gitignore
+++ b/.gitignore
@@ -97,7 +97,7 @@ Makefile.in
/daemon/listfs.mli
/daemon/lvm.mli
/daemon/lvm_dm.mli
-/daemon/lvm-tokenization.c
+/daemon/lvm_full.mli
/daemon/md.mli
/daemon/mount.mli
/daemon/names.c
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index bb72c024..90ece846 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -22,7 +22,6 @@ BUILT_SOURCES = \
caml-stubs.c \
dispatch.c \
names.c \
- lvm-tokenization.c \
structs-cleanups.c \
structs-cleanups.h \
stubs-0.c \
@@ -52,6 +51,7 @@ generator_built = \
listfs.mli \
lvm.mli \
lvm_dm.mli \
+ lvm_full.mli \
md.mli \
mount.mli \
optgroups.ml \
@@ -152,7 +152,6 @@ guestfsd_SOURCES = \
luks.c \
lvm.c \
lvm-filter.c \
- lvm-tokenization.c \
md.c \
mkfs.c \
mknod.c \
@@ -298,6 +297,7 @@ SOURCES_MLI = \
listfs.mli \
lvm.mli \
lvm_dm.mli \
+ lvm_full.mli \
lvm_utils.mli \
md.mli \
mount.mli \
@@ -333,6 +333,7 @@ SOURCES_ML = \
ldm.ml \
link.ml \
lvm.ml \
+ lvm_full.ml \
lvm_utils.ml \
lvm_dm.ml \
findfs.ml \
diff --git a/daemon/lvm.c b/daemon/lvm.c
index 7e76e17c..001aacb2 100644
--- a/daemon/lvm.c
+++ b/daemon/lvm.c
@@ -139,28 +139,6 @@ do_vgs (void)
return convert_lvm_output (out, NULL);
}
-/* These were so complex to implement that I ended up auto-generating
- * the code. That code is in stubs.c, and it is generated as usual
- * by generator.ml.
- */
-guestfs_int_lvm_pv_list *
-do_pvs_full (void)
-{
- return parse_command_line_pvs ();
-}
-
-guestfs_int_lvm_vg_list *
-do_vgs_full (void)
-{
- return parse_command_line_vgs ();
-}
-
-guestfs_int_lvm_lv_list *
-do_lvs_full (void)
-{
- return parse_command_line_lvs ();
-}
-
int
do_pvcreate (const char *device)
{
diff --git a/daemon/lvm_full.ml b/daemon/lvm_full.ml
new file mode 100644
index 00000000..d5653d2f
--- /dev/null
+++ b/daemon/lvm_full.ml
@@ -0,0 +1,221 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2025 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *)
+
+(* This file implements the complicated lvs-full, vgs-full and pvs-full APIs
+ *
+ * XXX Deprecate these APIs are replace with APIs for getting single
+ * named fields from LVM. That will be slower but far more flexible
+ * and extensible.
+ *)
+
+open Unix
+open Printf
+
+open Std_utils
+
+open Utils
+
+(* LVM UUIDs are basically 32 byte strings with '-' inserted.
+ * Remove the '-' characters and check it's the right length.
+ *)
+let parse_uuid uuid =
+ let uuid' =
+ uuid |> String.explode |> List.filter ((<>) '-') |> String.implode in
+ if String.length uuid' <> 32 then
+ failwithf "lvm-full: parse_uuid: unexpected UUID format: %S" uuid;
+ uuid'
+
+(* Parse the percent fields. These can be empty. *)
+let parse_percent pc = if pc = "" then None else Some (float_of_string pc)
+
+(* XXX These must match generator/structs.ml *)
+let lvm_pv_cols = [
+ "pv_name"; (* FString *)
+ "pv_uuid"; (* FUUID *)
+ "pv_fmt"; (* FString *)
+ "pv_size"; (* FBytes *)
+ "dev_size"; (* FBytes *)
+ "pv_free"; (* FBytes *)
+ "pv_used"; (* FBytes *)
+ "pv_attr"; (* FString (* XXX *) *)
+ "pv_pe_count"; (* FInt64 *)
+ "pv_pe_alloc_count"; (* FInt64 *)
+ "pv_tags"; (* FString *)
+ "pe_start"; (* FBytes *)
+ "pv_mda_count"; (* FInt64 *)
+ "pv_mda_free"; (* FBytes *)
+]
+
+let tokenize_pvs = function
+ | [ pv_name; pv_uuid; pv_fmt; pv_size; dev_size; pv_free;
+ pv_used; pv_attr; pv_pe_count; pv_pe_alloc_count; pv_tags;
+ pe_start; pv_mda_count; pv_mda_free ] ->
+ { Structs.pv_name = pv_name;
+ pv_uuid = parse_uuid pv_uuid;
+ pv_fmt = pv_fmt;
+ pv_size = Int64.of_string pv_size;
+ dev_size = Int64.of_string dev_size;
+ pv_free = Int64.of_string pv_free;
+ pv_used = Int64.of_string pv_used;
+ pv_attr = pv_attr;
+ pv_pe_count = Int64.of_string pv_pe_count;
+ pv_pe_alloc_count = Int64.of_string pv_pe_alloc_count;
+ pv_tags = pv_tags;
+ pe_start = Int64.of_string pe_start;
+ pv_mda_count = Int64.of_string pv_mda_count;
+ pv_mda_free = Int64.of_string pv_mda_free }
+
+ | fields ->
+ failwithf "pvs-full: tokenize_pvs: unexpected number of fields: %d"
+ (List.length fields)
+
+(* XXX These must match generator/structs.ml *)
+let lvm_vg_cols = [
+ "vg_name"; (* FString *)
+ "vg_uuid"; (* FUUID *)
+ "vg_fmt"; (* FString *)
+ "vg_attr"; (* FString (* XXX *) *)
+ "vg_size"; (* FBytes *)
+ "vg_free"; (* FBytes *)
+ "vg_sysid"; (* FString *)
+ "vg_extent_size"; (* FBytes *)
+ "vg_extent_count"; (* FInt64 *)
+ "vg_free_count"; (* FInt64 *)
+ "max_lv"; (* FInt64 *)
+ "max_pv"; (* FInt64 *)
+ "pv_count"; (* FInt64 *)
+ "lv_count"; (* FInt64 *)
+ "snap_count"; (* FInt64 *)
+ "vg_seqno"; (* FInt64 *)
+ "vg_tags"; (* FString *)
+ "vg_mda_count"; (* FInt64 *)
+ "vg_mda_free"; (* FBytes *)
+]
+
+let tokenize_vgs = function
+ | [ vg_name; vg_uuid; vg_fmt; vg_attr; vg_size; vg_free; vg_sysid;
+ vg_extent_size; vg_extent_count; vg_free_count; max_lv;
+ max_pv; pv_count; lv_count; snap_count; vg_seqno; vg_tags;
+ vg_mda_count; vg_mda_free ] ->
+ { Structs.vg_name = vg_name;
+ vg_uuid = parse_uuid vg_uuid;
+ vg_fmt = vg_fmt;
+ vg_attr = vg_attr;
+ vg_size = Int64.of_string vg_size;
+ vg_free = Int64.of_string vg_free;
+ vg_sysid = vg_sysid;
+ vg_extent_size = Int64.of_string vg_extent_size;
+ vg_extent_count = Int64.of_string vg_extent_count;
+ vg_free_count = Int64.of_string vg_free_count;
+ max_lv = Int64.of_string max_lv;
+ max_pv = Int64.of_string max_pv;
+ pv_count = Int64.of_string pv_count;
+ lv_count = Int64.of_string lv_count;
+ snap_count = Int64.of_string snap_count;
+ vg_seqno = Int64.of_string vg_seqno;
+ vg_tags = vg_tags;
+ vg_mda_count = Int64.of_string vg_mda_count;
+ vg_mda_free = Int64.of_string vg_mda_free }
+
+ | fields ->
+ failwithf "pvs-full: tokenize_vgs: unexpected number of fields: %d"
+ (List.length fields)
+
+(* XXX These must match generator/structs.ml *)
+let lvm_lv_cols = [
+ "lv_name"; (* FString *)
+ "lv_uuid"; (* FUUID *)
+ "lv_attr"; (* FString (* XXX *) *)
+ "lv_major"; (* FInt64 *)
+ "lv_minor"; (* FInt64 *)
+ "lv_kernel_major"; (* FInt64 *)
+ "lv_kernel_minor"; (* FInt64 *)
+ "lv_size"; (* FBytes *)
+ "seg_count"; (* FInt64 *)
+ "origin"; (* FString *)
+ "snap_percent"; (* FOptPercent *)
+ "copy_percent"; (* FOptPercent *)
+ "move_pv"; (* FString *)
+ "lv_tags"; (* FString *)
+ "mirror_log"; (* FString *)
+ "modules"; (* FString *)
+]
+
+let tokenize_lvs = function
+ | [ lv_name; lv_uuid; lv_attr; lv_major; lv_minor; lv_kernel_major;
+ lv_kernel_minor; lv_size; seg_count; origin; snap_percent;
+ copy_percent; move_pv; lv_tags; mirror_log; modules ] ->
+ { Structs.lv_name = lv_name;
+ lv_uuid = parse_uuid lv_uuid;
+ lv_attr = lv_attr;
+ lv_major = Int64.of_string lv_major;
+ lv_minor = Int64.of_string lv_minor;
+ lv_kernel_major = Int64.of_string lv_kernel_major;
+ lv_kernel_minor = Int64.of_string lv_kernel_minor;
+ lv_size = Int64.of_string lv_size;
+ seg_count = Int64.of_string seg_count;
+ origin = origin;
+ snap_percent = parse_percent snap_percent;
+ copy_percent = parse_percent copy_percent;
+ move_pv = move_pv;
+ lv_tags = lv_tags;
+ mirror_log = mirror_log;
+ modules = modules }
+
+ | fields ->
+ failwithf "pvs-full: tokenize_vgs: unexpected number of fields: %d"
+ (List.length fields)
+
+let rec pvs_full () =
+ let out = run_lvm_command "pvs" lvm_pv_cols in
+ let lines = trim_and_split out in
+ let pvs = List.map tokenize_pvs lines in
+ pvs
+
+and vgs_full () =
+ let out = run_lvm_command "vgs" lvm_vg_cols in
+ let lines = trim_and_split out in
+ let vgs = List.map tokenize_vgs lines in
+ vgs
+
+and lvs_full () =
+ let out = run_lvm_command "lvs" lvm_lv_cols in
+ let lines = trim_and_split out in
+ let lvs = List.map tokenize_lvs lines in
+ lvs
+
+and run_lvm_command typ cols =
+ let cols = String.concat "," cols in
+ let cmd = [ typ; "-o"; cols;
+ "--unbuffered"; "--noheadings"; "--nosuffix";
+ "--separator"; "\r"; "--units"; "b" ] in
+ command "lvm" cmd
+
+and trim_and_split out =
+ (* Split the output into lines. *)
+ let lines = String.nsplit "\n" out in
+
+ (* LVM puts leading whitespace on each line so remove that. *)
+ let lines = List.map String.triml lines in
+
+ (* Ignore any blank lines. *)
+ let lines = List.filter ((<>) "") lines in
+
+ (* Split each line into fields. *)
+ let lines = List.map (String.nsplit "\r") lines in
+ lines
diff --git a/docs/C_SOURCE_FILES b/docs/C_SOURCE_FILES
index 0038f95e..cdfb1d61 100644
--- a/docs/C_SOURCE_FILES
+++ b/docs/C_SOURCE_FILES
@@ -111,7 +111,6 @@ daemon/link.c
daemon/ls.c
daemon/luks.c
daemon/lvm-filter.c
-daemon/lvm-tokenization.c
daemon/lvm.c
daemon/md.c
daemon/mkfs.c
diff --git a/generator/actions_core.ml b/generator/actions_core.ml
index eb047b6b..01991dd0 100644
--- a/generator/actions_core.ml
+++ b/generator/actions_core.ml
@@ -1767,6 +1767,7 @@ See also C<guestfs_lvs_full>, C<guestfs_list_filesystems>." };
{ defaults with
name = "pvs_full"; added = (0, 0, 4);
style = RStructList ("physvols", "lvm_pv"), [], [];
+ impl = OCaml "Lvm_full.pvs_full";
optional = Some "lvm2";
shortdesc = "list the LVM physical volumes (PVs)";
longdesc = "\
@@ -1776,6 +1777,7 @@ of the L<pvs(8)> command. The \"full\" version includes all fields." };
{ defaults with
name = "vgs_full"; added = (0, 0, 4);
style = RStructList ("volgroups", "lvm_vg"), [], [];
+ impl = OCaml "Lvm_full.vgs_full";
optional = Some "lvm2";
shortdesc = "list the LVM volume groups (VGs)";
longdesc = "\
@@ -1785,6 +1787,7 @@ of the L<vgs(8)> command. The \"full\" version includes all fields." };
{ defaults with
name = "lvs_full"; added = (0, 0, 4);
style = RStructList ("logvols", "lvm_lv"), [], [];
+ impl = OCaml "Lvm_full.lvs_full";
optional = Some "lvm2";
shortdesc = "list the LVM logical volumes (LVs)";
longdesc = "\
diff --git a/generator/daemon.ml b/generator/daemon.ml
index 91decf40..f9b77a18 100644
--- a/generator/daemon.ml
+++ b/generator/daemon.ml
@@ -951,196 +951,6 @@ let generate_daemon_dispatch () =
pr "}\n";
pr "\n"
-let generate_daemon_lvm_tokenization () =
- generate_header CStyle GPLv2plus;
-
- pr "\
-#include <config.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <inttypes.h>
-#include <errno.h>
-#include <rpc/types.h>
-#include <rpc/xdr.h>
-
-#include \"daemon.h\"
-#include \"c-ctype.h\"
-#include \"guestfs_protocol.h\"
-#include \"actions.h\"
-#include \"optgroups.h\"
-
-";
-
- (* LVM columns and tokenization functions. *)
- (* XXX This generates crap code. We should rethink how we
- * do this parsing.
- *)
- List.iter (
- function
- | typ, cols ->
- pr "static const char lvm_%s_cols[] = \"%s\";\n"
- typ (String.concat "," (List.map fst cols));
- pr "\n";
-
- pr "static int lvm_tokenize_%s (char *str, guestfs_int_lvm_%s *r)\n" typ typ;
- pr "{\n";
- pr " char *tok, *p, *next;\n";
- pr " size_t i, j;\n";
- pr "\n";
- (*
- pr " fprintf (stderr, \"%%s: <<%%s>>\\n\", __func__, str);\n";
- pr "\n";
- *)
- pr " if (!str) {\n";
- pr " fprintf (stderr, \"%%s: failed: passed a NULL string\\n\", __func__);\n";
- pr " return -1;\n";
- pr " }\n";
- pr " if (!*str || c_isspace (*str)) {\n";
- pr " fprintf (stderr, \"%%s: failed: passed a empty string or one beginning with whitespace\\n\", __func__);\n";
- pr " return -1;\n";
- pr " }\n";
- pr " tok = str;\n";
- List.iter (
- fun (name, coltype) ->
- pr " if (!tok) {\n";
- pr " fprintf (stderr, \"%%s: failed: string finished early, around token %%s\\n\", __func__, \"%s\");\n" name;
- pr " return -1;\n";
- pr " }\n";
- pr " p = strchrnul (tok, '\\r');\n";
- pr " if (*p) next = p+1; else next = NULL;\n";
- pr " *p = '\\0';\n";
- (match coltype with
- | FString | FDevice ->
- pr " r->%s = strdup (tok);\n" name;
- pr " if (r->%s == NULL) {\n" name;
- pr " perror (\"strdup\");\n";
- pr " return -1;\n";
- pr " }\n"
- | FUUID ->
- pr " for (i = j = 0; i < 32; ++j) {\n";
- pr " if (tok[j] == '\\0') {\n";
- pr " fprintf (stderr, \"%%s: failed to parse UUID from '%%s'\\n\", __func__, tok);\n";
- pr " return -1;\n";
- pr " } else if (tok[j] != '-')\n";
- pr " r->%s[i++] = tok[j];\n" name;
- pr " }\n";
- | FBytes ->
- pr " if (sscanf (tok, \"%%\" SCNi64, &r->%s) != 1) {\n" name;
- pr " fprintf (stderr, \"%%s: failed to parse size '%%s' from token %%s\\n\", __func__, tok, \"%s\");\n" name;
- pr " return -1;\n";
- pr " }\n";
- | FInt64 ->
- pr " if (sscanf (tok, \"%%\" SCNi64, &r->%s) != 1) {\n" name;
- pr " fprintf (stderr, \"%%s: failed to parse int '%%s' from token %%s\\n\", __func__, tok, \"%s\");\n" name;
- pr " return -1;\n";
- pr " }\n";
- | FOptPercent ->
- pr " if (tok[0] == '\\0')\n";
- pr " r->%s = -1;\n" name;
- pr " else if (sscanf (tok, \"%%f\", &r->%s) != 1) {\n" name;
- pr " fprintf (stderr, \"%%s: failed to parse float '%%s' from token %%s\\n\", __func__, tok, \"%s\");\n" name;
- pr " return -1;\n";
- pr " }\n";
- | FBuffer | FInt32 | FUInt32 | FUInt64 | FChar ->
- assert false (* can never be an LVM column *)
- );
- pr " tok = next;\n";
- ) cols;
-
- pr " if (tok != NULL) {\n";
- pr " fprintf (stderr, \"%%s: failed: extra tokens at end of string\\n\", __func__);\n";
- pr " return -1;\n";
- pr " }\n";
- pr " return 0;\n";
- pr "}\n";
- pr "\n";
-
- pr "guestfs_int_lvm_%s_list *\n" typ;
- pr "parse_command_line_%ss (void)\n" typ;
- pr "{\n";
- pr " char *out, *err;\n";
- pr " char *p, *pend;\n";
- pr " int r, i;\n";
- pr " guestfs_int_lvm_%s_list *ret;\n" typ;
- pr " void *newp;\n";
- pr "\n";
- pr " ret = malloc (sizeof *ret);\n";
- pr " if (!ret) {\n";
- pr " reply_with_perror (\"malloc\");\n";
- pr " return NULL;\n";
- pr " }\n";
- pr "\n";
- pr " ret->guestfs_int_lvm_%s_list_len = 0;\n" typ;
- pr " ret->guestfs_int_lvm_%s_list_val = NULL;\n" typ;
- pr "\n";
- pr " r = command (&out, &err,\n";
- pr " \"lvm\", \"%ss\",\n" typ;
- pr " \"-o\", lvm_%s_cols, \"--unbuffered\", \"--noheadings\",\n" typ;
- pr " \"--nosuffix\", \"--separator\", \"\\r\", \"--units\", \"b\", NULL);\n";
- pr " if (r == -1) {\n";
- pr " reply_with_error (\"%%s\", err);\n";
- pr " free (out);\n";
- pr " free (err);\n";
- pr " free (ret);\n";
- pr " return NULL;\n";
- pr " }\n";
- pr "\n";
- pr " free (err);\n";
- pr "\n";
- pr " /* Tokenize each line of the output. */\n";
- pr " p = out;\n";
- pr " i = 0;\n";
- pr " while (p) {\n";
- pr " pend = strchr (p, '\\n'); /* Get the next line of output. */\n";
- pr " if (pend) {\n";
- pr " *pend = '\\0';\n";
- pr " pend++;\n";
- pr " }\n";
- pr "\n";
- pr " while (*p && c_isspace (*p)) /* Skip any leading whitespace. */\n";
- pr " p++;\n";
- pr "\n";
- pr " if (!*p) { /* Empty line? Skip it. */\n";
- pr " p = pend;\n";
- pr " continue;\n";
- pr " }\n";
- pr "\n";
- pr " /* Allocate some space to store this next entry. */\n";
- pr " newp = realloc (ret->guestfs_int_lvm_%s_list_val,\n" typ;
- pr " sizeof (guestfs_int_lvm_%s) * (i+1));\n" typ;
- pr " if (newp == NULL) {\n";
- pr " reply_with_perror (\"realloc\");\n";
- pr " free (ret->guestfs_int_lvm_%s_list_val);\n" typ;
- pr " free (ret);\n";
- pr " free (out);\n";
- pr " return NULL;\n";
- pr " }\n";
- pr " ret->guestfs_int_lvm_%s_list_val = newp;\n" typ;
- pr "\n";
- pr " /* Tokenize the next entry. */\n";
- pr " r = lvm_tokenize_%s (p, &ret->guestfs_int_lvm_%s_list_val[i]);\n" typ typ;
- pr " if (r == -1) {\n";
- pr " reply_with_error (\"failed to parse output of '%ss' command\");\n" typ;
- pr " free (ret->guestfs_int_lvm_%s_list_val);\n" typ;
- pr " free (ret);\n";
- pr " free (out);\n";
- pr " return NULL;\n";
- pr " }\n";
- pr "\n";
- pr " ++i;\n";
- pr " p = pend;\n";
- pr " }\n";
- pr "\n";
- pr " ret->guestfs_int_lvm_%s_list_len = i;\n" typ;
- pr "\n";
- pr " free (out);\n";
- pr " return ret;\n";
- pr "}\n"
-
- ) ["pv", lvm_pv_cols; "vg", lvm_vg_cols; "lv", lvm_lv_cols]
-
(* Generate a list of function names, for debugging in the daemon.. *)
let generate_daemon_names () =
generate_header CStyle GPLv2plus;
diff --git a/generator/daemon.mli b/generator/daemon.mli
index edf331a9..77c6e632 100644
--- a/generator/daemon.mli
+++ b/generator/daemon.mli
@@ -23,7 +23,6 @@ val generate_daemon_caml_stubs : unit -> unit
val generate_daemon_caml_callbacks_ml : unit -> unit
val generate_daemon_caml_interface : string -> unit -> unit
val generate_daemon_dispatch : unit -> unit
-val generate_daemon_lvm_tokenization : unit -> unit
val generate_daemon_names : unit -> unit
val generate_daemon_optgroups_c : unit -> unit
val generate_daemon_optgroups_h : unit -> unit
diff --git a/generator/main.ml b/generator/main.ml
index ff5e964a..6e36d9c7 100644
--- a/generator/main.ml
+++ b/generator/main.ml
@@ -147,8 +147,6 @@ Run it from the top source directory using the command
Daemon.generate_daemon_optgroups_ml;
output_to "daemon/optgroups.mli"
Daemon.generate_daemon_optgroups_mli;
- output_to "daemon/lvm-tokenization.c"
- Daemon.generate_daemon_lvm_tokenization;
output_to "daemon/structs-cleanups.c"
Daemon.generate_daemon_structs_cleanups_c;
output_to "daemon/structs-cleanups.h"
diff --git a/generator/structs.ml b/generator/structs.ml
index 93a3ed03..239a5d5e 100644
--- a/generator/structs.ml
+++ b/generator/structs.ml
@@ -31,9 +31,7 @@ type struc = {
s_unused : unit; (* Silences warning 23 when using 'defaults with ...' *)
}
-(* Because we generate extra parsing code for LVM command line tools,
- * we have to pull out the LVM columns separately here.
- *)
+(* XXX These must match daemon/lvm_full.ml *)
let lvm_pv_cols = [
"pv_name", FDevice;
"pv_uuid", FUUID;
diff --git a/generator/structs.mli b/generator/structs.mli
index e7a29b01..b3444623 100644
--- a/generator/structs.mli
+++ b/generator/structs.mli
@@ -34,13 +34,6 @@ type struc = {
val structs : struc list
(** List of structures. *)
-val lvm_pv_cols : cols
-val lvm_vg_cols : cols
-val lvm_lv_cols : cols
-(** These are exported to the daemon code generator where they are
- used to generate code for parsing the output of commands like
- [lvs]. One day replace this with liblvm API calls. *)
-
val lookup_struct : string -> struc
(** Lookup a struct by name. *)
diff --git a/po/POTFILES b/po/POTFILES
index 75b040c0..d10f9710 100644
--- a/po/POTFILES
+++ b/po/POTFILES
@@ -90,7 +90,6 @@ daemon/link.c
daemon/ls.c
daemon/luks.c
daemon/lvm-filter.c
-daemon/lvm-tokenization.c
daemon/lvm.c
daemon/md.c
daemon/mkfs.c

View File

@ -0,0 +1,70 @@
From 180293338e0d127d0545fe03b6141ed04e22e441 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Wed, 16 Apr 2025 10:27:25 +0100
Subject: [PATCH] daemon: inspect: Resolve Ubuntu 22+
/dev/disk/by-id/dm-uuid-LVM-... in fstab
Linux + LVM supports device names like /dev/disk/by-id/dm-uuid-LVM-
followed by two concatenated UUIDs, firstly for the volume group and
secondly for the logical volume. We can reverse those to get the
device name (/dev/VG/LV).
fstab entries look like:
# / was on /dev/vg0/lv-0 during curtin installation
/dev/disk/by-id/dm-uuid-LVM-OzFWT6NHkstr1hcmrWRRMDGPn9xdZj1YOOycQ533186x288FdU6UubU3OlnWJz6D / ext4 defaults 0 1
# /usr was on /dev/vg0/lv-1 during curtin installation
/dev/disk/by-id/dm-uuid-LVM-OzFWT6NHkstr1hcmrWRRMDGPn9xdZj1YZu53m4ZssZ8Jeb3I14RAJwIj5YlHIb9P /usr ext4 defaults 0 1
The upshot of this fix is that we are now able to correctly inspect
and run virt-v2v on Ubuntu 22+ guests with split /usr. In particular,
we correctly map /etc/fstab entries like the above to LV device names,
which means that /usr merging now works correctly.
Reported-by: Jaroslav Spanko
Thanks: Daniel Berrange
Fixes: https://issues.redhat.com/browse/RHEL-87493
(cherry picked from commit e43ca1912973b3ddfa73b09a4690aa8bb26e08af)
---
daemon/inspect_fs_unix_fstab.ml | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/daemon/inspect_fs_unix_fstab.ml b/daemon/inspect_fs_unix_fstab.ml
index 45c62175..dcbdab3c 100644
--- a/daemon/inspect_fs_unix_fstab.ml
+++ b/daemon/inspect_fs_unix_fstab.ml
@@ -27,6 +27,7 @@ open Inspect_utils
let re_cciss = PCRE.compile "^/dev/(cciss/c\\d+d\\d+)(?:p(\\d+))?$"
let re_diskbyid = PCRE.compile "^/dev/disk/by-id/.*-part(\\d+)$"
+let re_dmuuid = PCRE.compile "^/dev/disk/by-id/dm-uuid-LVM-([0-9a-zA-Z]{32})([0-9a-zA-Z]{32})$"
let re_freebsd_gpt = PCRE.compile "^/dev/(ada{0,1}|vtbd)(\\d+)p(\\d+)$"
let re_freebsd_mbr = PCRE.compile "^/dev/(ada{0,1}|vtbd)(\\d+)s(\\d+)([a-z])$"
let re_hurd_dev = PCRE.compile "^/dev/(h)d(\\d+)s(\\d+)$"
@@ -407,6 +408,26 @@ and resolve_fstab_device spec md_map os_type =
Failure _ -> default
)
+ (* Ubuntu 22+ uses /dev/disk/by-id/dm-uuid-LVM-... followed by a
+ * double UUID which identifies an LV. The first part of the UUID
+ * is the VG UUID. The second part is the LV UUID.
+ *)
+ else if PCRE.matches re_dmuuid spec then (
+ debug_matching "dmuuid";
+ let vg_uuid_spec = PCRE.sub 1 and lv_uuid_spec = PCRE.sub 2 in
+ try
+ (* Get the list of all VGs and LVs. *)
+ let vgs = Lvm_full.vgs_full () and lvs = Lvm_full.lvs_full () in
+ (* Find one VG & LV (hopefully) that matches the UUIDs. *)
+ let vg =
+ List.find (fun { Structs.vg_uuid } -> vg_uuid = vg_uuid_spec) vgs
+ and lv =
+ List.find (fun { Structs.lv_uuid } -> lv_uuid = lv_uuid_spec) lvs in
+ Mountable.of_device (sprintf "/dev/%s/%s" vg.vg_name lv.lv_name)
+ with
+ Failure _ | Not_found -> default
+ )
+
else if PCRE.matches re_freebsd_gpt spec then (
debug_matching "FreeBSD GPT";
(* group 1 (type) is not used *)

View File

@ -0,0 +1,38 @@
From 14b2ac2d5f087b0d8fc8c1e0451cae22af753734 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Fri, 25 Apr 2025 15:14:24 +0100
Subject: [PATCH] RHEL 9: daemon/fstrim.c: Run the fstrim command twice
Workaround for https://issues.redhat.com/browse/RHEL-88450
Fixes: https://issues.redhat.com/browse/RHEL-88508
Related: https://issues.redhat.com/browse/RHEL-88450
Thanks: Eric Sandeen
---
daemon/fstrim.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/daemon/fstrim.c b/daemon/fstrim.c
index f6869042..06707a7f 100644
--- a/daemon/fstrim.c
+++ b/daemon/fstrim.c
@@ -101,8 +101,19 @@ do_fstrim (const char *path,
ADD_ARG (argv, i, buf);
ADD_ARG (argv, i, NULL);
+ /* Run the command twice to workaround
+ * https://issues.redhat.com/browse/RHEL-88450
+ */
+ r = commandv (&out, &err, argv);
+ if (r == -1) goto error;
+ if (verbose)
+ fprintf (stderr, "%s\n", out);
+ free (out); out = NULL;
+ free (err); err = NULL;
+
r = commandv (&out, &err, argv);
if (r == -1) {
+ error:
/* If the error is about the kernel operation not being supported
* for this filesystem type, then return errno ENOTSUP here.
*/

View File

@ -0,0 +1,29 @@
From e9a7378d730477d6a548c18244ba0bde9c7dc93d Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Fri, 25 Apr 2025 15:24:43 +0100
Subject: [PATCH] daemon/fstrim.c: Issue sync_disks after fstrim
Thanks: Eric Sandeen
(cherry picked from commit e127edcafc95c75bf484bf2199eb746a392c58c0)
---
daemon/fstrim.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/daemon/fstrim.c b/daemon/fstrim.c
index 06707a7f..474ef72d 100644
--- a/daemon/fstrim.c
+++ b/daemon/fstrim.c
@@ -127,5 +127,13 @@ do_fstrim (const char *path,
if (verbose)
fprintf (stderr, "%s\n", out);
+ /* Sync the disks again. In practice we always call fstrim
+ * expecting that afterwards the results are visible in the qemu
+ * devices backing the guest. Depending on the Linux filesystem,
+ * fstrim may issue asynch discard requests, so it's not necessarily
+ * true that everything has been written out before this point.
+ */
+ sync_disks ();
+
return 0;
}

View File

@ -0,0 +1,127 @@
From 682765a63ec7c3140e6332f15d9833072eea05e1 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Thu, 8 May 2025 09:10:38 +0100
Subject: [PATCH] daemon: inspect: Remove duplicate root mountpoints in
/etc/fstab
A customer case was found where /etc/fstab contained multiple root
mountpoints, something like:
LABEL=System / xfs ...
LABEL=Boot /boot ext2 ...
LABEL=System / xfs ...
This causes libguestfs and virt-v2v to fail. Either (on RHEL 9) we
try to mount the second instance of / which gives an error. Or (on
upstream kernels) we are able to mount the second instance but then
libguestfs gets confused when trying to unmount them.
In this case as the mounted devices are the same we can just delete
the duplicate. It's also possible that there could be multiple
non-identical root mountpoints, in which case we have to pick one, and
this code arbitrarily picks the first[*] (but emits a warning).
We don't do anything for non-root mountpoints.
Update common submodule to add 'List.same' function from mlstdutils.
[*] Which one is "the first" depends on what version of ocaml-augeas
we are using. ocaml-augeas version 0.6 Augeas.matches function
returns entries in reverse order (compared to augeas itself). This is
fixed in version 0.7:
http://git.annexia.org/?p=ocaml-augeas.git;a=commitdiff;h=b703b92e3d26690aa6f7b822132049ce5435983e
Fixes: https://issues.redhat.com/browse/RHEL-90168
(cherry picked from commit 5441d3dd0c8843897f65c8d40c82e0d204748b4e)
---
common | 2 +-
daemon/inspect_fs_unix_fstab.ml | 31 +++++++++++++++++++++++++++++--
2 files changed, 30 insertions(+), 3 deletions(-)
Submodule common 8aed7b6a..9053edae:
diff --git a/common/mlstdutils/std_utils.ml b/common/mlstdutils/std_utils.ml
index 1177ff69..a0745d38 100644
--- a/common/mlstdutils/std_utils.ml
+++ b/common/mlstdutils/std_utils.ml
@@ -396,6 +396,18 @@ module List = struct
let push_back_list xsp xs = xsp := !xsp @ xs
let push_front_list xs xsp = xsp := xs @ !xsp
+
+ let make n x =
+ let rec loop acc = function
+ | 0 -> acc
+ | i when i > 0 -> loop (x :: acc) (i-1)
+ | _ -> invalid_arg "make"
+ in
+ loop [] n
+
+ let same = function
+ | [] -> true
+ | x :: xs -> List.for_all ((=) x) xs
end
module Option = struct
diff --git a/common/mlstdutils/std_utils.mli b/common/mlstdutils/std_utils.mli
index 6811b4bc..f5ff3470 100644
--- a/common/mlstdutils/std_utils.mli
+++ b/common/mlstdutils/std_utils.mli
@@ -289,6 +289,12 @@ module List : sig
[push_front_list] is like {!push_front} above, except it prepends
a list to the list reference. *)
+
+ val make : int -> 'a -> 'a list
+ (** [make n x] returns a list with [x] repeated [n] times. *)
+ val same : 'a list -> bool
+ (** [same xs] returns true iff the list contains only identical elements,
+ or is the empty list. *)
end
(** Override the List module from stdlib. *)
diff --git a/daemon/inspect_fs_unix_fstab.ml b/daemon/inspect_fs_unix_fstab.ml
index dcbdab3c..b0de55e4 100644
--- a/daemon/inspect_fs_unix_fstab.ml
+++ b/daemon/inspect_fs_unix_fstab.ml
@@ -53,8 +53,10 @@ and check_fstab_aug mdadm_conf root_mountable os_type aug =
let md_map = if mdadm_conf then map_md_devices aug else StringMap.empty in
let path = "/files/etc/fstab/*[label() != '#comment']" in
- let entries = aug_matches_noerrors aug path in
- List.filter_map (check_fstab_entry md_map root_mountable os_type aug) entries
+ path |>
+ aug_matches_noerrors aug |>
+ List.filter_map (check_fstab_entry md_map root_mountable os_type aug) |>
+ remove_duplicate_root_mountpoints
and check_fstab_entry md_map root_mountable os_type aug entry =
with_return (fun {return} ->
@@ -604,3 +606,28 @@ and resolve_diskbyid part default =
if is_partition dev then Mountable.of_device dev
else default
)
+
+(* Remove duplicate root mountpoints if they are identical. If
+ * there are multiple non-identical roots we pick the first and
+ * emit a warning (RHEL-90168).
+ *)
+and remove_duplicate_root_mountpoints (entries : fstab_entry list) =
+ let root_entries, non_root_entries =
+ List.partition (function (_, "/") -> true | _ -> false) entries in
+ (* If there is one root entry (the normal case) return the list unmodified. *)
+ if List.length root_entries <= 1 then entries
+ else (
+ (* If they are not the same, issue a warning. *)
+ if not (List.same root_entries) then
+ eprintf "check_fstab: multiple, non-identical root mountpoints found \
+ in the /etc/fstab of this guest, picking the first. The \
+ root entries were: [%s]\n"
+ (String.concat "; "
+ (List.map (fun (mountable, mp) ->
+ sprintf "%s -> %s" (Mountable.to_string mountable) mp)
+ root_entries)
+ );
+
+ (* Choose the first root entry and return it. *)
+ List.hd root_entries :: non_root_entries
+ )

View File

@ -0,0 +1,41 @@
From e73ac4912d4864d5d378cd40bf1979e02bf0fcb1 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Wed, 11 Jun 2025 11:38:23 +0100
Subject: [PATCH] lib/inspect-osinfo.c: Generate new osinfo shortname for SLES
>= 15
libosinfo changes the naming scheme it uses for SUSE starting with
major version 15. Previously it used names like "sles12" (or
"sles12sp1"), "sled12" for Server and Desktop variants. In 15+ it
uses "sle15" as there are no variants any longer (instead the
installer asks you what variant you want to install). We're only
interested in the Server variant. Change the name that we return to
"sle15" or "sle15sp1".
See: https://gitlab.com/libosinfo/osinfo-db/-/commit/b0fa386699ce634abb6dd4b56aa0e83d979353fc
Fixes: https://issues.redhat.com/browse/RHEL-95791
Thanks: Ming Xie, Victor Toso
Related: https://issues.redhat.com/browse/RHEL-95540
(cherry picked from commit db46bcb5356e1c654a4b5b8ecca1ae657d46b9cf)
---
lib/inspect-osinfo.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/lib/inspect-osinfo.c b/lib/inspect-osinfo.c
index f792d771..1fc2f59a 100644
--- a/lib/inspect-osinfo.c
+++ b/lib/inspect-osinfo.c
@@ -62,10 +62,11 @@ guestfs_impl_inspect_get_osinfo (guestfs_h *g, const char *root)
else if (STREQ (distro, "fedora") || STREQ (distro, "mageia"))
return safe_asprintf (g, "%s%d", distro, major);
else if (STREQ (distro, "sles")) {
+ const char *base = major >= 15 ? "sle" : "sles";
if (minor == 0)
- return safe_asprintf (g, "%s%d", distro, major);
+ return safe_asprintf (g, "%s%d", base, major);
else
- return safe_asprintf (g, "%s%dsp%d", distro, major, minor);
+ return safe_asprintf (g, "%s%dsp%d", base, major, minor);
}
else if (STREQ (distro, "ubuntu"))
return safe_asprintf (g, "%s%d.%02d", distro, major, minor);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -7,7 +7,7 @@ set -e
# ./copy-patches.sh
project=libguestfs
rhel_version=9.6
rhel_version=9.7
# Check we're in the right directory.
if [ ! -f $project.spec ]; then

View File

@ -37,7 +37,7 @@ Summary: Access and modify virtual machine disk images
Name: libguestfs
Epoch: 1
Version: 1.54.0
Release: 4%{?dist}
Release: 11%{?dist}
License: LGPL-2.1-or-later
# Build only for architectures that have a kernel
@ -72,7 +72,7 @@ Source7: libguestfs.keyring
Source8: copy-patches.sh
# Patches are maintained in the following repository:
# https://github.com/libguestfs/libguestfs/commits/rhel-9.6
# https://github.com/libguestfs/libguestfs/commits/rhel-9.7
# Patches.
Patch0001: 0001-website-Add-links-for-1.54-and-1.55-download-locatio.patch
@ -92,6 +92,25 @@ Patch0014: 0014-website-Fix-link-to-latest-development-version.patch
Patch0015: 0015-RHEL-Disable-unsupported-remote-drive-protocols-RHBZ.patch
Patch0016: 0016-RHEL-Reject-use-of-libguestfs-winsupport-features-ex.patch
Patch0017: 0017-daemon-New-command_out-and-sh_out-APIs.patch
Patch0018: 0018-lib-Print-kernel-utsname-in-debug-output.patch
Patch0019: 0019-daemon-Fix-loongarch64-detection-on-RHEL-9.patch
Patch0020: 0020-daemon-inspect-Add-some-debugging-of-usr-merging.patch
Patch0021: 0021-generator-Implement-struct-FDevice-type.patch
Patch0022: 0022-generator-Use-new-FDevice-type-for-the-pvs-full-pv_n.patch
Patch0023: 0023-daemon-inspect-Resolve-Ubuntu-22-dev-disk-by-uuid-in.patch
Patch0024: 0024-generator-Fix-implementation-of-FUUID-for-OCaml-func.patch
Patch0025: 0025-Update-common-submodule.patch
Patch0026: 0026-daemon-Rewrite-pvs-vgs-lvs-full-APIs-in-OCaml.patch
Patch0027: 0027-daemon-inspect-Resolve-Ubuntu-22-dev-disk-by-id-dm-u.patch
Patch0028: 0028-RHEL-9-daemon-fstrim.c-Run-the-fstrim-command-twice.patch
Patch0029: 0029-daemon-fstrim.c-Issue-sync_disks-after-fstrim.patch
Patch0030: 0030-daemon-inspect-Remove-duplicate-root-mountpoints-in-.patch
Patch0031: 0031-lib-inspect-osinfo.c-Generate-new-osinfo-shortname-f.patch
Patch0032: 0032-daemon-Add-contents-of-etc-fstab-to-verbose-log.patch
Patch0033: 0033-appliance-init-Add-lsblk-and-blkid-output-to-verbose.patch
Patch0034: 0034-daemon-inspect_fs_windows.ml-Add-debugging-for-MBR-d.patch
Patch0035: 0035-daemon-inspect_fs_windows.ml-Add-debugging-when-we-s.patch
Patch0036: 0036-daemon-inspect_fs_windows.ml-Ignore-blank-disks-in-d.patch
BuildRequires: autoconf, automake, libtool, gettext-devel
@ -1101,6 +1120,30 @@ rm ocaml/html/.gitignore
%changelog
* Thu Aug 14 2025 Richard W.M. Jones <rjones@redhat.com> - 1:1.54.0-11
- Ignore blank disks in Windows drive mapping
resolves: RHEL-108803
* Tue Jun 17 2025 Richard W.M. Jones <rjones@redhat.com> - 1:1.54.0-9
- Generate correct osinfo for SLES >= 15
resolves: RHEL-95791
* Thu May 08 2025 Richard W.M. Jones <rjones@redhat.com> - 1:1.54.0-8
- Remove duplicate root mountpoints in /etc/fstab
resolves: RHEL-90168
* Fri Apr 25 2025 Richard W.M. Jones <rjones@redhat.com> - 1:1.54.0-7
- Run the fstrim command twice
resolves: RHEL-88508
* Wed Apr 16 2025 Richard W.M. Jones <rjones@redhat.com> - 1:1.54.0-6
- Fix virt-v2v conversion of split /usr Ubuntu 22+
resolves: RHEL-87493
* Tue Mar 11 2025 Richard W.M. Jones <rjones@redhat.com> - 1:1.54.0-5
- Include host kernel information in libguestfs debugging output
resolves: RHEL-83025
* Mon Feb 24 2025 Richard W.M. Jones <rjones@redhat.com> - 1:1.54.0-4
- Add new APIs to allow command output > 4MB
resolves: RHEL-80159