Update to libguestfs-1.58.1-6

Patch series (24) regenerated from public sources:
- https://github.com/libguestfs/libguestfs (branch rhel-10.2)
- https://github.com/libguestfs/libguestfs-common (submodule)

Apply patches with %autosetup -S git (BuildRequires: git): a patch adds
binary test data that /usr/bin/patch cannot apply.

Retains AlmaLinux ppc64le build enablement (.alma.1).
This commit is contained in:
Andrew Lukoshko 2026-06-08 13:36:15 +00:00
parent b9f12a4084
commit e7e9a5e032
14 changed files with 2056 additions and 3 deletions

View File

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

View File

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

View File

@ -0,0 +1,684 @@
From 1308ee6c911cdaaa9ed15e9fb45a0c59d45c2490 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Tue, 28 Apr 2026 11:51:30 +0100
Subject: [PATCH] daemon: Move read_whole_file to common/utils
Remove our read_whole_file function. A similar function will be added
to common/utils which we will use instead.
Update common submodule, pulling in:
Richard W.M. Jones (2):
mldrivers/firmware.ml: Ignore CHS geometry error from parted
utils: Add read_whole_file function
Srihari Parimi (2):
mltools: add prefix to debug () output
mltools: for external commands include function name in debug() output
Susant Sahani (1):
smp: respect cgroup v2 CPU limits for appliance SMP
(cherry picked from commit 76f590de0626ccc10d8aaecc1a7d9761189b6117)
---
common | 2 +-
daemon/daemon.h | 1 -
daemon/ntfsclone.c | 3 +--
daemon/tar.c | 3 +--
daemon/utils.c | 58 ----------------------------------------------
5 files changed, 3 insertions(+), 64 deletions(-)
Submodule common 3ac5d1841..800510306:
diff --git a/common/edit/file-edit.c b/common/edit/file-edit.c
index ef56ed1..a10d61c 100644
--- a/common/edit/file-edit.c
+++ b/common/edit/file-edit.c
@@ -119,7 +119,7 @@ edit_file_editor (guestfs_h *g, const char *filename, const char *editor,
fprintf (stderr, "%s\n", cmd);
r = system (cmd);
- if (r == -1 || WEXITSTATUS (r) != 0) {
+ if (r == -1 || !WIFEXITED (r) || WEXITSTATUS (r) != 0) {
perror (cmd);
return -1;
}
@@ -194,7 +194,7 @@ edit_file_perl (guestfs_h *g, const char *filename, const char *perl_expr,
fprintf (stderr, "%s\n", cmd);
r = system (cmd);
- if (r == -1 || WEXITSTATUS (r) != 0)
+ if (r == -1 || !WIFEXITED (r) || WEXITSTATUS (r) != 0)
return -1;
if (rename (outfile, tmpfilename) == -1) {
diff --git a/common/mlpcre/Makefile.am b/common/mlpcre/Makefile.am
index a1d8b02..30a60f6 100644
--- a/common/mlpcre/Makefile.am
+++ b/common/mlpcre/Makefile.am
@@ -83,7 +83,7 @@ endif
libmlpcre_a_DEPENDENCIES = $(OBJECTS)
$(MLPCRE_CMA): $(OBJECTS) libmlpcre.a
- $(OCAMLFIND) mklib $(OCAMLPACKAGES) \
+ $(AM_V_GEN) $(OCAMLFIND) mklib $(OCAMLPACKAGES) \
$(OBJECTS) $(libmlpcre_a_OBJECTS) -cclib -lpcre2-8 -o mlpcre
# Tests.
diff --git a/common/mlstdutils/Makefile.am b/common/mlstdutils/Makefile.am
index b9632b0..0f88190 100644
--- a/common/mlstdutils/Makefile.am
+++ b/common/mlstdutils/Makefile.am
@@ -84,11 +84,11 @@ endif
libmlstdutils_a_DEPENDENCIES = $(OBJECTS)
mlstdutils.cma: $(BOBJECTS)
- $(OCAMLFIND) ocamlc $(OCAMLPACKAGES) -a $^ -o $@
+ $(AM_V_GEN) $(OCAMLFIND) ocamlc $(OCAMLPACKAGES) -a $^ -o $@
if HAVE_OCAMLOPT
mlstdutils.cmxa: $(XOBJECTS)
- $(OCAMLFIND) ocamlopt $(OCAMLPACKAGES) -a $^ -o $@
+ $(AM_V_GEN) $(OCAMLFIND) ocamlopt $(OCAMLPACKAGES) -a $^ -o $@
endif
# Tests.
diff --git a/common/mlstdutils/std_utils.mli b/common/mlstdutils/std_utils.mli
index 6c1911d..77cf107 100644
--- a/common/mlstdutils/std_utils.mli
+++ b/common/mlstdutils/std_utils.mli
@@ -51,6 +51,7 @@ module List : sig
val find_all : ('a -> bool) -> 'a list -> 'a list
val partition : ('a -> bool) -> 'a list -> 'a list * 'a list
val assoc : 'a -> ('a * 'b) list -> 'b
+ val assoc_opt : 'a -> ('a * 'b) list -> 'b option
val assq : 'a -> ('a * 'b) list -> 'b
val mem_assoc : 'a -> ('a * 'b) list -> bool
val mem_assq : 'a -> ('a * 'b) list -> bool
diff --git a/common/mlutils/Makefile.am b/common/mlutils/Makefile.am
index d52cb9c..084ce63 100644
--- a/common/mlutils/Makefile.am
+++ b/common/mlutils/Makefile.am
@@ -86,7 +86,7 @@ endif
libmlcutils_a_DEPENDENCIES = $(OBJECTS)
$(MLCUTILS_CMA): $(OBJECTS) libmlcutils.a
- $(OCAMLFIND) mklib $(OCAMLPACKAGES) \
+ $(AM_V_GEN) $(OCAMLFIND) mklib $(OCAMLPACKAGES) \
$(OBJECTS) $(libmlcutils_a_OBJECTS) \
-cclib -lutils \
-o mlcutils
diff --git a/common/mlutils/c_utils-c.c b/common/mlutils/c_utils-c.c
index d9c1a48..f0e2798 100644
--- a/common/mlutils/c_utils-c.c
+++ b/common/mlutils/c_utils-c.c
@@ -68,7 +68,7 @@ guestfs_int_mlutils_shell_unquote (value strv)
ret = guestfs_int_shell_unquote (String_val (strv));
if (ret == NULL)
- unix_error (errno, (char *) "guestfs_int_shell_unquote", Nothing);
+ caml_unix_error (errno, (char *) "guestfs_int_shell_unquote", Nothing);
retv = caml_copy_string (ret);
free (ret);
@@ -101,6 +101,8 @@ guestfs_int_mlutils_full_path (value dirv, value namev)
name = String_val (Field (namev, 0));
ret = guestfs_int_full_path (String_val (dirv), name);
+ if (ret == NULL)
+ caml_unix_error (errno, (char *) "guestfs_int_full_path", dirv);
rv = caml_copy_string (ret);
free (ret);
diff --git a/common/mlutils/unix_utils-c.c b/common/mlutils/unix_utils-c.c
index ee5a379..919f526 100644
--- a/common/mlutils/unix_utils-c.c
+++ b/common/mlutils/unix_utils-c.c
@@ -65,6 +65,8 @@
#include <caml/mlvalues.h>
#include <caml/unixsupport.h>
+#include "guestfs-utils.h"
+
extern value guestfs_int_mllib_dev_t_makedev (value majv, value minv);
extern value guestfs_int_mllib_dev_t_major (value devv);
extern value guestfs_int_mllib_dev_t_minor (value devv);
@@ -147,7 +149,7 @@ guestfs_int_mllib_fnmatch (value patternv, value strv, value flagsv)
/* XXX The fnmatch specification doesn't mention what errors can
* be returned by fnmatch. Assume they are errnos for now.
*/
- unix_error (errno, (char *) "fnmatch", patternv);
+ caml_unix_error (errno, (char *) "fnmatch", patternv);
}
}
@@ -180,16 +182,16 @@ guestfs_int_mllib_fsync_file (value filenamev)
/* Note to do fsync you have to open for write. */
fd = open (filename, O_RDWR);
if (fd == -1)
- unix_error (errno, (char *) "open", filenamev);
+ caml_unix_error (errno, (char *) "open", filenamev);
if (fsync (fd) == -1) {
err = errno;
close (fd);
- unix_error (err, (char *) "fsync", filenamev);
+ caml_unix_error (err, (char *) "fsync", filenamev);
}
if (close (fd) == -1)
- unix_error (errno, (char *) "close", filenamev);
+ caml_unix_error (errno, (char *) "close", filenamev);
CAMLreturn (Val_unit);
}
@@ -203,11 +205,14 @@ guestfs_int_mllib_mkdtemp (value val_pattern)
pattern = strdup (String_val (val_pattern));
if (pattern == NULL)
- unix_error (errno, (char *) "strdup", val_pattern);
+ caml_unix_error (errno, (char *) "strdup", val_pattern);
ret = mkdtemp (pattern);
- if (ret == NULL)
- unix_error (errno, (char *) "mkdtemp", val_pattern);
+ if (ret == NULL) {
+ int err = errno;
+ free (pattern);
+ caml_unix_error (err, (char *) "mkdtemp", val_pattern);
+ }
rv = caml_copy_string (ret);
free (pattern);
@@ -224,7 +229,7 @@ guestfs_int_mllib_realpath (value pathv)
r = realpath (String_val (pathv), NULL);
if (r == NULL)
- unix_error (errno, (char *) "realpath", pathv);
+ caml_unix_error (errno, (char *) "realpath", pathv);
rv = caml_copy_string (r);
free (r);
@@ -252,7 +257,7 @@ guestfs_int_mllib_statvfs_statvfs (value pathv)
struct statvfs buf;
if (statvfs (String_val (pathv), &buf) == -1)
- unix_error (errno, (char *) "statvfs", pathv);
+ caml_unix_error (errno, (char *) "statvfs", pathv);
f_bsize = buf.f_bsize;
f_frsize = buf.f_frsize;
@@ -276,7 +281,7 @@ guestfs_int_mllib_statvfs_statvfs (value pathv)
(PULARGE_INTEGER) &free_bytes_available,
(PULARGE_INTEGER) &total_number_of_bytes,
(PULARGE_INTEGER) &total_number_of_free_bytes))
- unix_error (EIO, (char *) "statvfs: GetDiskFreeSpaceEx", pathv);
+ caml_unix_error (EIO, (char *) "statvfs: GetDiskFreeSpaceEx", pathv);
/* XXX I couldn't determine how to get block size. MSDN has a
* unhelpful hard-coded list here:
@@ -341,15 +346,17 @@ guestfs_int_mllib_statvfs_statvfs (value pathv)
CAMLreturn (rv);
}
-/* NB: This is a [@@noalloc] call. */
value
guestfs_int_mllib_statvfs_is_network_filesystem (value pathv)
{
+ CAMLparam1 (pathv);
+ CAMLlocal1 (rv);
+
#ifdef HAVE_STATFS
struct statfs buf;
if (statfs (String_val (pathv), &buf) == -1)
- unix_error (errno, (char *) "statvfs", pathv);
+ caml_unix_error (errno, (char *) "statvfs", pathv);
/* Some but not all of these are defined in <linux/magic.h>. */
#ifndef CIFS_MAGIC_NUMBER
@@ -362,12 +369,14 @@ guestfs_int_mllib_statvfs_is_network_filesystem (value pathv)
#define SMB_SUPER_MAGIC 0x517b
#endif
- return Val_bool ((unsigned int) buf.f_type == CIFS_MAGIC_NUMBER ||
- (unsigned int) buf.f_type == NFS_SUPER_MAGIC ||
- (unsigned int) buf.f_type == SMB_SUPER_MAGIC);
+ rv = Val_bool ((unsigned int) buf.f_type == CIFS_MAGIC_NUMBER ||
+ (unsigned int) buf.f_type == NFS_SUPER_MAGIC ||
+ (unsigned int) buf.f_type == SMB_SUPER_MAGIC);
#else
- return Val_bool (0);
+ rv = Val_bool (0);
#endif
+
+ CAMLreturn (rv);
}
/* NB: This is a [@@noalloc] call. */
diff --git a/common/mlutils/unix_utils.ml b/common/mlutils/unix_utils.ml
index b79144a..9d7e11a 100644
--- a/common/mlutils/unix_utils.ml
+++ b/common/mlutils/unix_utils.ml
@@ -82,10 +82,35 @@ module StatVFS = struct
let free_space { f_bsize = bsize; f_bavail = bavail } = bsize *^ bavail
external is_network_filesystem : string -> bool =
- "guestfs_int_mllib_statvfs_is_network_filesystem" [@@noalloc]
+ "guestfs_int_mllib_statvfs_is_network_filesystem"
end
module Sysconf = struct
external nr_processors_online : unit -> int =
"guestfs_int_mllib_sysconf_nr_processors_online" [@@noalloc]
end
+
+module Cgroup = struct
+ let v2_cpus () =
+ let file = "/sys/fs/cgroup/cpu.max" in
+ if Sys.file_exists file then
+ try
+ let line = read_first_line_from_file file in
+ if String.starts_with ~prefix:"max" line then
+ None
+ else
+ let quota, period =
+ Scanf.sscanf line "%Ld %Ld" (fun q p -> (q, p)) in
+ if period > 0L then
+ Some (max 1 (Int64.to_int (Int64.div quota period)))
+ else None
+ with
+ | Scanf.Scan_failure _ | Failure _ | End_of_file -> None
+ else
+ None
+
+ let nr_cpus_available () =
+ match v2_cpus () with
+ | Some cpus -> cpus
+ | None -> Sysconf.nr_processors_online ()
+end
diff --git a/common/mlutils/unix_utils.mli b/common/mlutils/unix_utils.mli
index aead4df..cb4f042 100644
--- a/common/mlutils/unix_utils.mli
+++ b/common/mlutils/unix_utils.mli
@@ -130,3 +130,21 @@ module Sysconf : sig
Note this never fails. In case we cannot get the number of
cores it returns 1. *)
end
+
+module Cgroup : sig
+ (** Functions to read CPU limits from cgroup filesystems. *)
+
+ val v2_cpus : unit -> int option
+ (** [v2_cpus ()] reads the cgroup v2 CPU quota from
+ [/sys/fs/cgroup/cpu.max] and returns the number of CPUs
+ allocated (quota / period), or [None] if the file does not
+ exist, the quota is "max" (unlimited), or the file cannot
+ be parsed. The kernel stores quota and period as [long]
+ values in microseconds, so we parse them as [Int64]. *)
+
+ val nr_cpus_available : unit -> int
+ (** [nr_cpus_available ()] returns the number of CPUs available,
+ taking into account cgroup v2 CPU limits.
+ Falls back to {!Sysconf.nr_processors_online} if no cgroup
+ limits are set. *)
+end
diff --git a/common/options/keys.c b/common/options/keys.c
index 7027104..b8f19ce 100644
--- a/common/options/keys.c
+++ b/common/options/keys.c
@@ -78,6 +78,7 @@ read_key (const char *param)
len = getline (&ret, &allocsize, infp);
if (len == -1) {
perror ("getline");
+ free (ret);
ret = NULL;
goto error;
}
diff --git a/common/parallel/parallel.c b/common/parallel/parallel.c
index 18b0607..88ad819 100644
--- a/common/parallel/parallel.c
+++ b/common/parallel/parallel.c
@@ -221,6 +221,7 @@ worker_thread (void *thread_data_vp)
g = guestfs_create ();
if (g == NULL) {
perror ("guestfs_create");
+ fclose (fp);
thread_data->r = -1;
return &thread_data->r;
}
diff --git a/common/qemuopts/qemuopts.c b/common/qemuopts/qemuopts.c
index 7dd9136..e351465 100644
--- a/common/qemuopts/qemuopts.c
+++ b/common/qemuopts/qemuopts.c
@@ -756,6 +756,11 @@ qemuopts_to_channel (struct qemuopts *qopts, FILE *fp)
}
fputc ('\n', fp);
+ if (ferror (fp)) {
+ errno = EIO;
+ return -1;
+ }
+
return 0;
}
@@ -1035,5 +1040,10 @@ qemuopts_to_config_channel (struct qemuopts *qopts, FILE *fp)
fprintf (fp, "\n");
}
+ if (ferror (fp)) {
+ errno = EIO;
+ return -1;
+ }
+
return 0;
}
diff --git a/common/update-submodule.sh b/common/update-submodule.sh
new file mode 100755
index 0000000..ecf22f1
--- /dev/null
+++ b/common/update-submodule.sh
@@ -0,0 +1,52 @@
+#!/bin/bash
+# (C) Copyright 2026 Red Hat Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+# Generate a submodule update commit, with formatted shortlog output.
+# Run this from the parent repo like: ./common/update-submodule.sh
+# Then `git commit --amend ...` as desired.
+
+set -euo pipefail
+
+if ! git submodule status common &>/dev/null; then
+ echo "Error: this script must be run from the top directory of a repo with a 'common/' submodule" >&2
+ exit 1
+fi
+
+if ! git diff --quiet --exit-code; then
+ echo "Error: working tree has uncommitted changes" >&2
+ exit 1
+fi
+
+echo "Running: git submodule update --remote common"
+git submodule update --remote common
+
+if git diff --quiet --exit-code; then
+ echo "'common' submodule already up to date"
+ exit 0
+fi
+
+OLD_COMMIT=$(git ls-tree HEAD common | awk '{print $3}')
+NEW_COMMIT=$(git -C common rev-parse HEAD)
+echo "Old 'common' commit: $OLD_COMMIT"
+echo "New 'common' commit: $NEW_COMMIT"
+
+SHORTLOG=$(git -C common shortlog --no-merges "${OLD_COMMIT}..${NEW_COMMIT}" | sed '/^$/!s/^/ /')
+git commit -F - common <<EOF
+common: update submodule
+
+$SHORTLOG
+EOF
diff --git a/common/utils/Makefile.am b/common/utils/Makefile.am
index 25c6100..f328dca 100644
--- a/common/utils/Makefile.am
+++ b/common/utils/Makefile.am
@@ -30,6 +30,7 @@ libutils_la_SOURCES = \
libxml2-writer-macros.h \
pcre2-cleanups.c \
stringlists-utils.c \
+ whole-file.c \
utils.c
libutils_la_CPPFLAGS = \
-DGUESTFS_NO_DEPRECATED=1 \
diff --git a/common/utils/guestfs-utils.h b/common/utils/guestfs-utils.h
index e861e7d..a8bd9ac 100644
--- a/common/utils/guestfs-utils.h
+++ b/common/utils/guestfs-utils.h
@@ -113,4 +113,8 @@ extern const char *guestfs_int_strerror (int errnum, char *buf, size_t buflen);
/* environ.c */
extern char **guestfs_int_copy_environ (char **env, ...);
+/* whole-file.c */
+extern int read_whole_file (const char *filename,
+ char **data_r, size_t *size_r);
+
#endif /* GUESTFS_UTILS_H_ */
diff --git a/common/utils/whole-file.c b/common/utils/whole-file.c
new file mode 100644
index 0000000..a896e02
--- /dev/null
+++ b/common/utils/whole-file.c
@@ -0,0 +1,111 @@
+/* libguestfs
+ * Copyright (C) 2011-2026 Red Hat Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <libintl.h>
+
+#include "guestfs-utils.h"
+
+/**
+ * Read the whole file C<filename> into a memory buffer.
+ *
+ * The memory buffer is initialized and returned in C<data_r>. The
+ * size of the file in bytes is returned in C<size_r>. The return
+ * buffer must be freed by the caller.
+ *
+ * On error this prints an error on C<stderr> and returns -1. Unlike
+ * the similar C<guestfs_int_read_whole_file> this does not use the
+ * libguestfs handle or call C<error()>.
+ *
+ * For the convenience of callers, the returned buffer is
+ * NUL-terminated (the NUL is not included in the size).
+ *
+ * The file must be a B<regular>, B<local>, B<trusted> file. In
+ * particular, do not use this function to read files that might be
+ * under control of an untrusted user since that will lead to a
+ * denial-of-service attack.
+ */
+int
+read_whole_file (const char *filename, char **data_r, size_t *size_r)
+{
+ int fd;
+ char *data;
+ off_t size;
+ off_t n;
+ ssize_t r;
+ struct stat statbuf;
+
+ fd = open (filename, O_RDONLY|O_CLOEXEC);
+ if (fd == -1) {
+ perror (filename);
+ return -1;
+ }
+
+ if (fstat (fd, &statbuf) == -1) {
+ perror (filename);
+ close (fd);
+ return -1;
+ }
+
+ size = statbuf.st_size;
+ data = malloc (size + 1);
+ if (data == NULL) {
+ perror ("malloc");
+ close (fd);
+ return -1;
+ }
+
+ n = 0;
+ while (n < size) {
+ r = read (fd, &data[n], size - n);
+ if (r == -1) {
+ perror (filename);
+ free (data);
+ close (fd);
+ return -1;
+ }
+ if (r == 0) {
+ fprintf (stderr, "%s: unexpected end of input", filename);
+ free (data);
+ close (fd);
+ return -1;
+ }
+ n += r;
+ }
+
+ if (close (fd) == -1) {
+ perror (filename);
+ free (data);
+ return -1;
+ }
+
+ /* For convenience of callers, \0-terminate the data. */
+ data[size] = '\0';
+
+ *data_r = data;
+ if (size_r != NULL)
+ *size_r = size;
+
+ return 0;
+}
diff --git a/daemon/daemon.h b/daemon/daemon.h
index 72b9e97c0..d3ed90924 100644
--- a/daemon/daemon.h
+++ b/daemon/daemon.h
@@ -87,7 +87,6 @@ extern void udev_settle (void);
extern int random_name (char *template);
extern char *get_random_uuid (void);
extern char *make_exclude_from_file (const char *function, char *const *excludes);
-extern char *read_whole_file (const char *filename, size_t *size_r);
/* mountable functions (in utils.c) */
extern char *mountable_to_string (const mountable_t *mountable);
diff --git a/daemon/ntfsclone.c b/daemon/ntfsclone.c
index e9a4c3ea9..3a3e9a7a7 100644
--- a/daemon/ntfsclone.c
+++ b/daemon/ntfsclone.c
@@ -39,8 +39,7 @@ read_error_file (char *error_file)
size_t len;
char *str;
- str = read_whole_file (error_file, &len);
- if (str == NULL) {
+ if (read_whole_file (error_file, &str, &len) == -1) {
str = strdup ("(no error)");
if (str == NULL)
error (EXIT_FAILURE, errno, "strdup"); /* XXX */
diff --git a/daemon/tar.c b/daemon/tar.c
index 1d6a4b7eb..faf46051b 100644
--- a/daemon/tar.c
+++ b/daemon/tar.c
@@ -108,8 +108,7 @@ read_error_file (char *error_file)
size_t len;
char *str;
- str = read_whole_file (error_file, &len);
- if (str == NULL) {
+ if (read_whole_file (error_file, &str, &len) == -1) {
str = strdup ("(no error)");
if (str == NULL)
error (EXIT_FAILURE, errno, "strdup"); /* XXX */
diff --git a/daemon/utils.c b/daemon/utils.c
index 2da33b9aa..d359d3c3b 100644
--- a/daemon/utils.c
+++ b/daemon/utils.c
@@ -845,61 +845,3 @@ cleanup_free_mountable (mountable_t *mountable)
free (mountable->volume);
}
}
-
-/**
- * Read whole file into dynamically allocated array. If there is an
- * error, DON'T call reply_with_perror, just return NULL. Returns a
- * C<\0>-terminated string. C<size_r> can be specified to get the
- * size of the returned data.
- */
-char *
-read_whole_file (const char *filename, size_t *size_r)
-{
- char *r = NULL;
- size_t alloc = 0, size = 0;
- int fd;
-
- fd = open (filename, O_RDONLY|O_CLOEXEC);
- if (fd == -1) {
- perror (filename);
- return NULL;
- }
-
- while (1) {
- alloc += 256;
- char *r2 = realloc (r, alloc);
- if (r2 == NULL) {
- perror ("realloc");
- free (r);
- close (fd);
- return NULL;
- }
- r = r2;
-
- /* The '- 1' in the size calculation ensures there is space below
- * to add \0 to the end of the input.
- */
- ssize_t n = read (fd, r + size, alloc - size - 1);
- if (n == -1) {
- fprintf (stderr, "read: %s: %m\n", filename);
- free (r);
- close (fd);
- return NULL;
- }
- if (n == 0)
- break;
- size += n;
- }
-
- if (close (fd) == -1) {
- fprintf (stderr, "close: %s: %m\n", filename);
- free (r);
- return NULL;
- }
-
- r[size] = '\0';
- if (size_r != NULL)
- *size_r = size;
-
- return r;
-}
--
2.47.3

View File

@ -0,0 +1,40 @@
From 84bc5dc2c9c9c82a27270e8068e224af51b7ac89 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Tue, 28 Apr 2026 09:18:27 +0100
Subject: [PATCH] generator: Adjust comment for String(Key)
Just tidy up the comment.
(cherry picked from commit f2b1d9de9b2adaf9e372438f27da710d243e628d)
---
generator/types.mli | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/generator/types.mli b/generator/types.mli
index 0e1b89846..01f00044e 100644
--- a/generator/types.mli
+++ b/generator/types.mli
@@ -185,12 +185,14 @@ and stringt =
stdin or write to stdout. *)
| Key
- (** Key material / passphrase. Eventually we should treat this
- as sensitive and mlock it into physical RAM. However this
- is highly complex because of all the places that XDR-encoded
- strings can end up. So currently the only difference from
- 'PlainString' is the way that guestfish requests these
- parameters from the user. *)
+ (** Key material / passphrase.
+
+ Currently the only difference from 'PlainString' is the way
+ that guestfish requests these parameters from the user.
+
+ Eventually we should treat this as sensitive and mlock it
+ into physical RAM. This is highly complex because of all
+ the places that XDR-encoded strings can end up. *)
| GUID
(** A GUID string.
--
2.47.3

View File

@ -0,0 +1,151 @@
From 9ad80afc0c79311c7d67b4dd67aa80a41e039263 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Tue, 28 Apr 2026 09:36:18 +0100
Subject: [PATCH] daemon: Move some deprecated functions to new Luks module
No change, just code movement.
(cherry picked from commit 5e7aef40c2e9a81f5ad3f8c2e1ff1389e7825efb)
---
.gitignore | 1 +
daemon/Makefile.am | 3 +++
daemon/cryptsetup.ml | 9 +-------
daemon/luks.ml | 32 ++++++++++++++++++++++++++++
generator/actions_core_deprecated.ml | 6 +++---
5 files changed, 40 insertions(+), 11 deletions(-)
create mode 100644 daemon/luks.ml
diff --git a/.gitignore b/.gitignore
index c8a8312fa..7715d2cfc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -95,6 +95,7 @@ Makefile.in
/daemon/ldm.mli
/daemon/link.mli
/daemon/listfs.mli
+/daemon/luks.mli
/daemon/lvm.mli
/daemon/lvm_dm.mli
/daemon/lvm_full.mli
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index d4a805046..6f3b9e7d5 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -49,6 +49,7 @@ generator_built = \
ldm.mli \
link.mli \
listfs.mli \
+ luks.mli \
lvm.mli \
lvm_dm.mli \
lvm_full.mli \
@@ -296,6 +297,7 @@ SOURCES_MLI = \
ldm.mli \
link.mli \
listfs.mli \
+ luks.mli \
lvm.mli \
lvm_dm.mli \
lvm_full.mli \
@@ -327,6 +329,7 @@ SOURCES_ML = \
blkid.ml \
btrfs.ml \
cryptsetup.ml \
+ luks.ml \
devsparts.ml \
file_helper.ml \
file.ml \
diff --git a/daemon/cryptsetup.ml b/daemon/cryptsetup.ml
index 6f677aef0..002e0993a 100644
--- a/daemon/cryptsetup.ml
+++ b/daemon/cryptsetup.ml
@@ -1,5 +1,5 @@
(* guestfs-inspection
- * Copyright (C) 2009-2025 Red Hat Inc.
+ * Copyright (C) 2009-2026 Red Hat Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -70,10 +70,3 @@ let cryptsetup_close device =
ignore (command "cryptsetup" ["close"; mapname]);
udev_settle ()
-
-(* Deprecated APIs for backwards compatibility. *)
-let luks_open device key mapname =
- cryptsetup_open ~crypttype:"luks" device key mapname
-let luks_open_ro device key mapname =
- cryptsetup_open ~crypttype:"luks" ~readonly:true device key mapname
-let luks_close = cryptsetup_close
diff --git a/daemon/luks.ml b/daemon/luks.ml
new file mode 100644
index 000000000..e7e6deefe
--- /dev/null
+++ b/daemon/luks.ml
@@ -0,0 +1,32 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2026 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *)
+
+open Printf
+open Unix
+
+open Std_utils
+
+open Utils
+
+(* Deprecated APIs for backwards compatibility. *)
+let luks_open device key mapname =
+ Cryptsetup.cryptsetup_open ~crypttype:"luks" device key mapname
+let luks_open_ro device key mapname =
+ Cryptsetup.cryptsetup_open ~crypttype:"luks" ~readonly:true device key mapname
+let luks_close =
+ Cryptsetup.cryptsetup_close
diff --git a/generator/actions_core_deprecated.ml b/generator/actions_core_deprecated.ml
index 308495c45..d930b8b88 100644
--- a/generator/actions_core_deprecated.ml
+++ b/generator/actions_core_deprecated.ml
@@ -843,7 +843,7 @@ physical volumes, volume groups and logical volumes." };
{ defaults with
name = "luks_open"; added = (1, 5, 1);
style = RErr, [String (Device, "device"); String (Key, "key"); String (PlainString, "mapname")], [];
- impl = OCaml "Cryptsetup.luks_open";
+ impl = OCaml "Luks.luks_open";
optional = Some "luks";
deprecated_by = Replaced_by "cryptsetup_open";
shortdesc = "open a LUKS-encrypted block device";
@@ -869,7 +869,7 @@ devices.|} };
{ defaults with
name = "luks_open_ro"; added = (1, 5, 1);
style = RErr, [String (Device, "device"); String (Key, "key"); String (PlainString, "mapname")], [];
- impl = OCaml "Cryptsetup.luks_open_ro";
+ impl = OCaml "Luks.luks_open_ro";
optional = Some "luks";
deprecated_by = Replaced_by "cryptsetup_open";
shortdesc = "open a LUKS-encrypted block device read-only";
@@ -880,7 +880,7 @@ mapping is created." };
{ defaults with
name = "luks_close"; added = (1, 5, 1);
style = RErr, [String (Device, "device")], [];
- impl = OCaml "Cryptsetup.luks_close";
+ impl = OCaml "Luks.luks_close";
optional = Some "luks";
deprecated_by = Replaced_by "cryptsetup_close";
shortdesc = "close a LUKS device";
--
2.47.3

View File

@ -0,0 +1,323 @@
From fcc511920866437fbe71739f0c170e377e18f73e Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Tue, 28 Apr 2026 09:57:27 +0100
Subject: [PATCH] daemon: Rewrite some luks_* APIs in OCaml
No change. Just rewriting these to so that all functions that handle
cryptsetup Key parameters are written in OCaml, so we can use a common
function in future for handling base64.
Note that the previous versions of these functions ignored the case of
cryptsetup exiting with a non-zero error code, as does existing
Cryptsetup.cryptsetup_open. Unclear if this is right or not, but this
change keeps the same behaviour.
(cherry picked from commit ec3548af1e6c75e021e49887fd27efa9262be98b)
---
daemon/luks.c | 182 --------------------------------------
daemon/luks.ml | 59 ++++++++++++
generator/actions_core.ml | 4 +
3 files changed, 63 insertions(+), 182 deletions(-)
diff --git a/daemon/luks.c b/daemon/luks.c
index 9d120b0eb..db061fe66 100644
--- a/daemon/luks.c
+++ b/daemon/luks.c
@@ -34,188 +34,6 @@ optgroup_luks_available (void)
return prog_exists ("cryptsetup");
}
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wanalyzer-possible-null-argument"
-/* Callers must also call remove_temp (tempfile). */
-static char *
-write_key_to_temp (const char *key)
-{
- char *tempfile;
- int fd;
- size_t len;
-
- tempfile = strdup ("/tmp/luksXXXXXX");
- if (!tempfile) {
- reply_with_perror ("strdup");
- return NULL;
- }
-
- fd = mkstemp (tempfile);
- if (fd == -1) {
- reply_with_perror ("mkstemp");
- goto error;
- }
-
- len = strlen (key);
- if (xwrite (fd, key, len) == -1) {
- reply_with_perror ("write");
- close (fd);
- goto error;
- }
-
- if (close (fd) == -1) {
- reply_with_perror ("close");
- goto error;
- }
-
- return tempfile;
-
- error:
- unlink (tempfile);
- free (tempfile);
- return NULL;
-}
-#pragma GCC diagnostic pop
-
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wanalyzer-double-free"
-static void
-remove_temp (char *tempfile)
-{
- unlink (tempfile);
- free (tempfile);
-}
-#pragma GCC diagnostic pop
-
-static int
-luks_format (const char *device, const char *key, int keyslot,
- const char *cipher)
-{
- char *tempfile = write_key_to_temp (key);
- if (!tempfile)
- return -1;
-
- const char *argv[MAX_ARGS];
- char keyslot_s[16];
- size_t i = 0;
-
- ADD_ARG (argv, i, "cryptsetup");
- ADD_ARG (argv, i, "-q");
- if (cipher) {
- ADD_ARG (argv, i, "--cipher");
- ADD_ARG (argv, i, cipher);
- }
- ADD_ARG (argv, i, "--key-slot");
- snprintf (keyslot_s, sizeof keyslot_s, "%d", keyslot);
- ADD_ARG (argv, i, keyslot_s);
- ADD_ARG (argv, i, "luksFormat");
- ADD_ARG (argv, i, device);
- ADD_ARG (argv, i, tempfile);
- ADD_ARG (argv, i, NULL);
-
- CLEANUP_FREE char *err = NULL;
- int r = commandv (NULL, &err, (const char * const *) argv);
- remove_temp (tempfile);
-
- if (r == -1) {
- reply_with_error ("%s", err);
- return -1;
- }
-
- udev_settle ();
-
- return 0;
-}
-
-int
-do_luks_format (const char *device, const char *key, int keyslot)
-{
- return luks_format (device, key, keyslot, NULL);
-}
-
-int
-do_luks_format_cipher (const char *device, const char *key, int keyslot,
- const char *cipher)
-{
- return luks_format (device, key, keyslot, cipher);
-}
-
-int
-do_luks_add_key (const char *device, const char *key, const char *newkey,
- int keyslot)
-{
- char *keyfile = write_key_to_temp (key);
- if (!keyfile)
- return -1;
-
- char *newkeyfile = write_key_to_temp (newkey);
- if (!newkeyfile) {
- remove_temp (keyfile);
- return -1;
- }
-
- const char *argv[MAX_ARGS];
- char keyslot_s[16];
- size_t i = 0;
-
- ADD_ARG (argv, i, "cryptsetup");
- ADD_ARG (argv, i, "-q");
- ADD_ARG (argv, i, "-d");
- ADD_ARG (argv, i, keyfile);
- ADD_ARG (argv, i, "--key-slot");
- snprintf (keyslot_s, sizeof keyslot_s, "%d", keyslot);
- ADD_ARG (argv, i, keyslot_s);
- ADD_ARG (argv, i, "luksAddKey");
- ADD_ARG (argv, i, device);
- ADD_ARG (argv, i, newkeyfile);
- ADD_ARG (argv, i, NULL);
-
- CLEANUP_FREE char *err = NULL;
- int r = commandv (NULL, &err, (const char * const *) argv);
- remove_temp (keyfile);
- remove_temp (newkeyfile);
-
- if (r == -1) {
- reply_with_error ("%s", err);
- return -1;
- }
-
- return 0;
-}
-
-int
-do_luks_kill_slot (const char *device, const char *key, int keyslot)
-{
- char *tempfile = write_key_to_temp (key);
- if (!tempfile)
- return -1;
-
- const char *argv[MAX_ARGS];
- char keyslot_s[16];
- size_t i = 0;
-
- ADD_ARG (argv, i, "cryptsetup");
- ADD_ARG (argv, i, "-q");
- ADD_ARG (argv, i, "-d");
- ADD_ARG (argv, i, tempfile);
- ADD_ARG (argv, i, "luksKillSlot");
- ADD_ARG (argv, i, device);
- snprintf (keyslot_s, sizeof keyslot_s, "%d", keyslot);
- ADD_ARG (argv, i, keyslot_s);
- ADD_ARG (argv, i, NULL);
-
- CLEANUP_FREE char *err = NULL;
- int r = commandv (NULL, &err, (const char * const *) argv);
- remove_temp (tempfile);
-
- if (r == -1) {
- reply_with_error ("%s", err);
- return -1;
- }
-
- return 0;
-}
-
char *
do_luks_uuid (const char *device)
{
diff --git a/daemon/luks.ml b/daemon/luks.ml
index e7e6deefe..4e6531755 100644
--- a/daemon/luks.ml
+++ b/daemon/luks.ml
@@ -23,6 +23,65 @@ open Std_utils
open Utils
+let write_key_to_tmp_file key =
+ let filename, chan = Filename.open_temp_file "luks" ".out" in
+ output_string chan key;
+ close_out chan;
+ filename
+
+let rec luks_format device key keyslot =
+ _luks_format device key keyslot
+
+and luks_format_cipher device key keyslot cipher =
+ _luks_format ~cipher device key keyslot
+
+and _luks_format ?cipher device key keyslot =
+ let tmp = write_key_to_tmp_file key in
+ Fun.protect ~finally:(fun () -> unlink tmp) (
+ fun () ->
+ let args = ref [] in
+ List.push_back args "-q";
+ Option.iter (fun s -> List.push_back_list args ["--cipher"; s]) cipher;
+ List.push_back args "--key-slot";
+ List.push_back args (string_of_int keyslot);
+ List.push_back args "luksFormat";
+ List.push_back args device;
+ List.push_back args tmp;
+ ignore (command "cryptsetup" !args)
+ );
+ udev_settle ()
+
+let luks_add_key device key newkey keyslot =
+ let keyfile = write_key_to_tmp_file key
+ and newkeyfile = write_key_to_tmp_file newkey in
+ Fun.protect ~finally:(fun () -> unlink keyfile; unlink newkeyfile) (
+ fun () ->
+ let args = ref [] in
+ List.push_back args "-q";
+ List.push_back args "-d";
+ List.push_back args keyfile;
+ List.push_back args "--key-slot";
+ List.push_back args (string_of_int keyslot);
+ List.push_back args "luksAddKey";
+ List.push_back args device;
+ List.push_back args newkeyfile;
+ ignore (command "cryptsetup" !args)
+ )
+
+let luks_kill_slot device key keyslot =
+ let tmp = write_key_to_tmp_file key in
+ Fun.protect ~finally:(fun () -> unlink tmp) (
+ fun () ->
+ let args = ref [] in
+ List.push_back args "-q";
+ List.push_back args "-d";
+ List.push_back args tmp;
+ List.push_back args "luksKillSlot";
+ List.push_back args device;
+ List.push_back args (string_of_int keyslot);
+ ignore (command "cryptsetup" !args)
+ )
+
(* Deprecated APIs for backwards compatibility. *)
let luks_open device key mapname =
Cryptsetup.cryptsetup_open ~crypttype:"luks" device key mapname
diff --git a/generator/actions_core.ml b/generator/actions_core.ml
index 26d77c667..5876188e9 100644
--- a/generator/actions_core.ml
+++ b/generator/actions_core.ml
@@ -5503,6 +5503,7 @@ group scan.|} };
{ defaults with
name = "luks_format"; added = (1, 5, 2);
style = RErr, [String (Device, "device"); String (Key, "key"); Int "keyslot"], [];
+ impl = OCaml "Luks.luks_format";
optional = Some "luks";
shortdesc = "format a block device as a LUKS encrypted device";
longdesc = "\
@@ -5514,6 +5515,7 @@ supports 8 key slots, numbered 0-7)." };
{ defaults with
name = "luks_format_cipher"; added = (1, 5, 2);
style = RErr, [String (Device, "device"); String (Key, "key"); Int "keyslot"; String (PlainString, "cipher")], [];
+ impl = OCaml "Luks.luks_format_cipher";
optional = Some "luks";
shortdesc = "format a block device as a LUKS encrypted device";
longdesc = "\
@@ -5523,6 +5525,7 @@ it also allows you to set the C<cipher> used." };
{ defaults with
name = "luks_add_key"; added = (1, 5, 2);
style = RErr, [String (Device, "device"); String (Key, "key"); String (Key, "newkey"); Int "keyslot"], [];
+ impl = OCaml "Luks.luks_add_key";
optional = Some "luks";
shortdesc = "add a key on a LUKS encrypted device";
longdesc = {|This command adds a new key on LUKS device C<device>.
@@ -5537,6 +5540,7 @@ first to remove that key.|} };
{ defaults with
name = "luks_kill_slot"; added = (1, 5, 2);
style = RErr, [String (Device, "device"); String (Key, "key"); Int "keyslot"], [];
+ impl = OCaml "Luks.luks_kill_slot";
optional = Some "luks";
shortdesc = "remove a key from a LUKS encrypted device";
longdesc = "\
--
2.47.3

View File

@ -0,0 +1,50 @@
From ac9b4454413097667158a54cbd2d4d646d25e600 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Tue, 28 Apr 2026 14:22:51 +0100
Subject: [PATCH] daemon/cryptsetup.ml: Reformat this code for consistency
No functional change, simply reformat the code for consistency with
the Luks module.
(cherry picked from commit e3a79166d9a3ce07e3434e6d2504495248160061)
---
daemon/cryptsetup.ml | 23 ++++++++++++++---------
1 file changed, 14 insertions(+), 9 deletions(-)
diff --git a/daemon/cryptsetup.ml b/daemon/cryptsetup.ml
index 002e0993a..47b17856c 100644
--- a/daemon/cryptsetup.ml
+++ b/daemon/cryptsetup.ml
@@ -49,15 +49,20 @@ let cryptsetup_open ?(readonly = false) ?crypttype ?cipher device key mapname =
output_string chan key;
close_out chan;
- let args = ref [] in
- List.push_back_list args ["-d"; keyfile];
- if readonly then List.push_back args "--readonly";
- List.push_back_list args ["open"; device; mapname; "--type"; crypttype];
- Option.iter (fun s -> List.push_back_list args ["--cipher"; s]) cipher;
-
- (* Make sure we always remove the temporary file. *)
- Fun.protect (fun () -> ignore (command "cryptsetup" !args))
- ~finally:(fun () -> unlink keyfile);
+ Fun.protect ~finally:(fun () -> unlink keyfile) (
+ fun () ->
+ let args = ref [] in
+ List.push_back args "-d";
+ List.push_back args keyfile;
+ if readonly then List.push_back args "--readonly";
+ List.push_back args "open";
+ List.push_back args device;
+ List.push_back args mapname;
+ List.push_back args "--type";
+ List.push_back args crypttype;
+ Option.iter (fun s -> List.push_back_list args ["--cipher"; s]) cipher;
+ ignore (command "cryptsetup" !args)
+ );
udev_settle ()
--
2.47.3

View File

@ -0,0 +1,81 @@
From fabe35d7b170eccac917de451fb955be4af0808d Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Tue, 28 Apr 2026 10:09:09 +0100
Subject: [PATCH] daemon: Use common Utils.write_key_to_tmp_file
Move this function to Utils module, and ensure it is called from all
places where we use Key parameters.
(cherry picked from commit 8a36eb4afa10186bf9c62e121ca4db6ef0ec5a8a)
---
daemon/cryptsetup.ml | 4 +---
daemon/luks.ml | 6 ------
daemon/utils.ml | 6 ++++++
daemon/utils.mli | 6 ++++++
4 files changed, 13 insertions(+), 9 deletions(-)
diff --git a/daemon/cryptsetup.ml b/daemon/cryptsetup.ml
index 47b17856c..29f3acc4d 100644
--- a/daemon/cryptsetup.ml
+++ b/daemon/cryptsetup.ml
@@ -45,9 +45,7 @@ let cryptsetup_open ?(readonly = false) ?crypttype ?cipher device key mapname =
failwithf "%s: unknown encrypted device type" t in
(* Write the key to a temporary file. *)
- let keyfile, chan = Filename.open_temp_file "crypt" ".key" in
- output_string chan key;
- close_out chan;
+ let keyfile = write_key_to_tmp_file key in
Fun.protect ~finally:(fun () -> unlink keyfile) (
fun () ->
diff --git a/daemon/luks.ml b/daemon/luks.ml
index 4e6531755..371c656ef 100644
--- a/daemon/luks.ml
+++ b/daemon/luks.ml
@@ -23,12 +23,6 @@ open Std_utils
open Utils
-let write_key_to_tmp_file key =
- let filename, chan = Filename.open_temp_file "luks" ".out" in
- output_string chan key;
- close_out chan;
- filename
-
let rec luks_format device key keyslot =
_luks_format device key keyslot
diff --git a/daemon/utils.ml b/daemon/utils.ml
index 3aa1d7ed2..2a03c7190 100644
--- a/daemon/utils.ml
+++ b/daemon/utils.ml
@@ -295,3 +295,9 @@ let parse_key_value_strings ?unquote lines =
let hex_of_string s =
let bytes = String.map_chars (fun c -> sprintf "%02x" (Char.code c)) s in
String.concat " " bytes
+
+let write_key_to_tmp_file key =
+ let filename, chan = Filename.open_temp_file "key" ".out" in
+ output_string chan key;
+ close_out chan;
+ filename
diff --git a/daemon/utils.mli b/daemon/utils.mli
index e14735038..730e4af38 100644
--- a/daemon/utils.mli
+++ b/daemon/utils.mli
@@ -125,5 +125,11 @@ val hex_of_string : string -> string
(** Return a string as a list of hex bytes.
Use this for debugging msgs only. *)
+val write_key_to_tmp_file : string -> string
+(** Write a Key parameter to a temporary file. Returns the name of
+ the temporary file.
+
+ The caller must call {!Unix.unlink} on the file. *)
+
(**/**)
val get_verbose_flag : unit -> bool
--
2.47.3

View File

@ -0,0 +1,126 @@
From 20b5a3f5d4a4ebd51354b0ab92141dd4f80e0ef9 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Tue, 28 Apr 2026 09:33:06 +0100
Subject: [PATCH] daemon: Allow base64:/text: as a prefix on Key parameters
To support 8 bit keys, allow Key parameters to be passed with the
prefix "base64:..." where the key that follows is base64-encoded.
"text:..." can be used to prefix plaintext passphrases. Or, for
backwards compat, they can be passed without prefix, but this is now
slightly ambiguous.
Base64 keys are passed through all the APIs as opaque strings, and
then decoded in the daemon just before calling the cryptsetup command.
(cherry picked from commit 9324859875bd8845e63edd918885ef70bb47211b)
---
daemon/utils.ml | 23 ++++++++++++++++++++++-
daemon/utils.mli | 3 +++
generator/types.mli | 6 ++++++
lib/guestfs.pod | 23 +++++++++++++++++++++++
4 files changed, 54 insertions(+), 1 deletion(-)
diff --git a/daemon/utils.ml b/daemon/utils.ml
index 2a03c7190..2ee303a41 100644
--- a/daemon/utils.ml
+++ b/daemon/utils.ml
@@ -296,8 +296,29 @@ let hex_of_string s =
let bytes = String.map_chars (fun c -> sprintf "%02x" (Char.code c)) s in
String.concat " " bytes
-let write_key_to_tmp_file key =
+let rec write_key_to_tmp_file key =
+ (* If the key starts with a prefix then it may need decoding. *)
+ let len = String.length key in
+ if String.starts_with "base64:" key then
+ _decode_base64_key (String.sub key 7 (len-7))
+ else if String.starts_with "text:" key then
+ _write_key (String.sub key 5 (len-5))
+ else
+ _write_key key
+
+and _write_key key =
let filename, chan = Filename.open_temp_file "key" ".out" in
output_string chan key;
close_out chan;
filename
+
+and _decode_base64_key b64 =
+ let b64file, chan = Filename.open_temp_file "key" ".b64" in
+ output_string chan b64;
+ close_out chan;
+ let keyfile = Filename.temp_file "key" ".out" in
+ let cmd = sprintf "base64 -d < %s > %s" (quote b64file) (quote keyfile) in
+ if Sys.command cmd <> 0 then
+ failwithf "could not decode base64-encoded key";
+ unlink b64file;
+ keyfile
diff --git a/daemon/utils.mli b/daemon/utils.mli
index 730e4af38..57f33dea8 100644
--- a/daemon/utils.mli
+++ b/daemon/utils.mli
@@ -129,6 +129,9 @@ val write_key_to_tmp_file : string -> string
(** Write a Key parameter to a temporary file. Returns the name of
the temporary file.
+ This handles the special "base64:..." or "text:..." prefixes
+ introduced in libguestfs 1.60.
+
The caller must call {!Unix.unlink} on the file. *)
(**/**)
diff --git a/generator/types.mli b/generator/types.mli
index 01f00044e..9416974db 100644
--- a/generator/types.mli
+++ b/generator/types.mli
@@ -190,6 +190,12 @@ and stringt =
Currently the only difference from 'PlainString' is the way
that guestfish requests these parameters from the user.
+ In libguestfs >= 1.60, these strings can be prefixed with
+ 'base64:...' to pass in a base64 encoded string (supporting
+ arbitrary 8 bit binary). 'text:...' can be used for plain
+ passphrases, or (for backwards compat) the passphrase can be
+ unprefixed.
+
Eventually we should treat this as sensitive and mlock it
into physical RAM. This is highly complex because of all
the places that XDR-encoded strings can end up. *)
diff --git a/lib/guestfs.pod b/lib/guestfs.pod
index 07737c839..fe74f5542 100644
--- a/lib/guestfs.pod
+++ b/lib/guestfs.pod
@@ -1218,6 +1218,29 @@ representation. Also consider how it might work in guestfish.
Certain libguestfs calls take a parameter that contains sensitive key
material, passed in as a C string.
+In libguestfs E<ge> 1.60 you can pass in 8 bit binary key data
+(allowing C<'\n'> or C<'\0'>) by prefixing the string with:
+
+=over 4
+
+=item C<base64:...>
+
+Pass in base64-encoded key data. This supports 8 bit binary key
+data.
+
+=item C<text:...>
+
+Pass in regular text passphrase. This does not support 8 bit binary
+key data.
+
+=item Unprefixed
+
+For backwards compatibility with libguestfs E<le> 1.58, a text
+passphrase can be passed in unprefixed. However this does not support
+8 bit binary key data.
+
+=back
+
In the future we would hope to change the libguestfs implementation so
that keys are L<mlock(2)>-ed into physical RAM, and thus can never end
up in swap. However this is I<not> done at the moment, because of the
--
2.47.3

View File

@ -0,0 +1,174 @@
From 7508991c1356155c8fa9dd65d0d5c2c90eea75aa Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Tue, 28 Apr 2026 12:34:08 +0100
Subject: [PATCH] Update common submodule
Richard W.M. Jones (3):
options/keys.c: When reading key from user, prefix with "text:"
options/keys.c: When using --key <dev>:key:<string>, prefix with "text:"
options/keys.c: When reading the key from a file, encode it with base64
Fixes: https://redhat.atlassian.net/browse/RHEL-170864
Fixes: https://redhat.atlassian.net/browse/RHEL-171895
Fixes: https://redhat.atlassian.net/browse/RHEL-171896
(cherry picked from commit 6a181ecc7abe8cd67ce0ac15a1a75fd58837091e)
---
common | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
Submodule common 800510306..cf2e12078:
diff --git a/common/options/keys.c b/common/options/keys.c
index b8f19ce..432e26d 100644
--- a/common/options/keys.c
+++ b/common/options/keys.c
@@ -37,17 +37,23 @@
* Read a passphrase ('Key') from F</dev/tty> with echo off.
*
* The caller (F<fish/cmds.c>) will call free on the string
- * afterwards. Based on the code in cryptsetup file F<lib/utils.c>.
+ * afterwards.
+ *
+ * The entered string is prefixed with "text:..." to avoid ambiguity
+ * (with libguestfs >= 1.60). Base64 encoding cannot be used here.
+ *
+ * Based on the code in cryptsetup file F<lib/utils.c>.
*/
char *
read_key (const char *param)
{
FILE *infp, *outfp;
struct termios orig, temp;
+ CLEANUP_FREE char *key = NULL;
+ size_t keysize = 0;
char *ret = NULL;
int tty;
int tcset = 0;
- size_t allocsize = 0;
ssize_t len;
/* Read and write to /dev/tty if available. */
@@ -75,17 +81,21 @@ read_key (const char *param)
}
}
- len = getline (&ret, &allocsize, infp);
+ len = getline (&key, &keysize, infp);
if (len == -1) {
perror ("getline");
- free (ret);
- ret = NULL;
goto error;
}
/* Remove the terminating \n if there is one. */
- if (len > 0 && ret[len-1] == '\n')
- ret[len-1] = '\0';
+ if (len > 0 && key[len-1] == '\n')
+ key[len-1] = '\0';
+
+ /* Prefix with "text:". */
+ if (asprintf (&ret, "text:%s", key) == -1) {
+ perror ("asprintf");
+ goto error;
+ }
error:
/* Restore echo, close file descriptor. */
@@ -100,27 +110,60 @@ read_key (const char *param)
return ret;
}
+/* Read a key from a file and base64 encode it, returning "base64:..." */
static char *
-read_first_line_from_file (const char *filename)
+read_key_and_base64_encode (const char *filename)
{
- CLEANUP_FCLOSE FILE *fp = NULL;
- char *ret = NULL;
- size_t allocsize = 0;
- ssize_t len;
+ CLEANUP_FREE char *inp = NULL;
+ char *out;
+ size_t inplen, outlen, i, j;
- fp = fopen (filename, "r");
- if (!fp)
- error (EXIT_FAILURE, errno, "fopen: %s", filename);
+ if (read_whole_file (filename, &inp, &inplen) == -1)
+ error (EXIT_FAILURE, 0, "read_key_and_base64_encode: read_whole_file: %s",
+ filename);
- len = getline (&ret, &allocsize, fp);
- if (len == -1)
- error (EXIT_FAILURE, errno, "getline: %s", filename);
+ /* From https://stackoverflow.com/a/6782480 */
+ static char encoding_table[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
+ 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+ 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+ 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
+ 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
+ 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
+ 'w', 'x', 'y', 'z', '0', '1', '2', '3',
+ '4', '5', '6', '7', '8', '9', '+', '/'};
+ static int mod_table[] = {0, 2, 1};
- /* Remove the terminating \n if there is one. */
- if (len > 0 && ret[len-1] == '\n')
- ret[len-1] = '\0';
+ outlen = 4 * ((inplen + 2) / 3);
+ out = malloc (outlen + 7 + 1);
+ if (!out)
+ error (EXIT_FAILURE, errno, "read_key_and_base64_encode: %s: malloc",
+ filename);
- return ret;
+ /* Add prefix and NUL-termination, then adjust 'out' to make the
+ * rest of the code simpler.
+ */
+ memcpy (out, "base64:", 7);
+ out[7 + outlen] = '\0';
+ out += 7;
+
+ for (i = 0, j = 0; i < inplen;) {
+ uint32_t octet_a = i < inplen ? (unsigned char) inp[i++] : 0;
+ uint32_t octet_b = i < inplen ? (unsigned char) inp[i++] : 0;
+ uint32_t octet_c = i < inplen ? (unsigned char) inp[i++] : 0;
+
+ uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c;
+
+ assert (j <= outlen-4);
+ out[j++] = encoding_table[(triple >> 3 * 6) & 0x3F];
+ out[j++] = encoding_table[(triple >> 2 * 6) & 0x3F];
+ out[j++] = encoding_table[(triple >> 1 * 6) & 0x3F];
+ out[j++] = encoding_table[(triple >> 0 * 6) & 0x3F];
+ }
+
+ for (i = 0; i < mod_table[inplen % 3]; i++)
+ out[outlen - 1 - i] = '=';
+
+ return out - 7 /* see above */;
}
/* Return the key(s) matching this particular device from the
@@ -164,15 +207,14 @@ get_keys (struct key_store *ks, const char *device, const char *uuid,
switch (key->type) {
case key_string:
- s = strdup (key->string.s);
- if (!s)
- error (EXIT_FAILURE, errno, "strdup");
+ if (asprintf (&s, "text:%s", key->string.s) == -1)
+ error (EXIT_FAILURE, errno, "asprintf");
match->clevis = false;
match->passphrase = s;
++match;
break;
case key_file:
- s = read_first_line_from_file (key->file.name);
+ s = read_key_and_base64_encode (key->file.name);
match->clevis = false;
match->passphrase = s;
++match;
--
2.47.3

View File

@ -0,0 +1,114 @@
From b105fe3e7c470ca8b2f51dce10666b6d320e5ba0 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Tue, 28 Apr 2026 12:47:12 +0100
Subject: [PATCH] tests/luks: Test handling of a binary --key <dev>:file:<file>
(cherry picked from commit 2ddbff5c2939fe987388428c8d8aed2575bdec33)
---
test-data/phony-guests/make-fedora-img.pl | 7 +++++--
tests/Makefile.am | 3 ++-
tests/luks/fedora-lv3.key | Bin 0 -> 10 bytes
tests/luks/test-key-option-inspect-luks-on-lvm.sh | 10 +++++-----
4 files changed, 12 insertions(+), 8 deletions(-)
create mode 100644 tests/luks/fedora-lv3.key
diff --git a/test-data/phony-guests/make-fedora-img.pl b/test-data/phony-guests/make-fedora-img.pl
index e4ea07841..6025bc838 100755
--- a/test-data/phony-guests/make-fedora-img.pl
+++ b/test-data/phony-guests/make-fedora-img.pl
@@ -234,7 +234,10 @@ EOF
$g->luks_format ('/dev/Volume-Group/Root', 'FEDORA-Root', 0);
$g->luks_format ('/dev/Volume-Group/Logical-Volume-1', 'FEDORA-LV1', 0);
$g->luks_format ('/dev/Volume-Group/Logical-Volume-2', 'FEDORA-LV2', 0);
- $g->luks_format ('/dev/Volume-Group/Logical-Volume-3', 'FEDORA-LV3', 0);
+ # This is a little different, because we use a binary passphrase.
+ # The base64 string is "FEDORA\0LV3" (containing ASCII NUL).
+ my $base64_key3 = 'base64:RkVET1JBAExWMw==';
+ $g->luks_format ('/dev/Volume-Group/Logical-Volume-3', $base64_key3, 0);
# Open the LUKS devices. This creates nodes like /dev/mapper/*-luks.
$g->cryptsetup_open ('/dev/Volume-Group/Root',
@@ -244,7 +247,7 @@ EOF
$g->cryptsetup_open ('/dev/Volume-Group/Logical-Volume-2',
'FEDORA-LV2', 'LV2-luks');
$g->cryptsetup_open ('/dev/Volume-Group/Logical-Volume-3',
- 'FEDORA-LV3', 'LV3-luks');
+ $base64_key3, 'LV3-luks');
# Phony root filesystem.
$g->mkfs ('ext2', '/dev/mapper/Root-luks', blocksize => 4096, label => 'ROOT');
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 91e917b50..f4d92eaa7 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -441,7 +441,8 @@ EXTRA_DIST += \
luks/test-luks-list.sh \
luks/test-key-option.sh \
luks/test-key-option-inspect-luks-on-lvm.sh \
- luks/test-key-option-inspect-lvm-on-luks.sh
+ luks/test-key-option-inspect-lvm-on-luks.sh \
+ luks/fedora-lv3.key
TESTS += \
lvm/test-lvm-filtering.sh \
diff --git a/tests/luks/fedora-lv3.key b/tests/luks/fedora-lv3.key
new file mode 100644
index 0000000000000000000000000000000000000000..89c372fd5df2216b6d2ca9ccb7879dc8ade8ec5c
GIT binary patch
literal 10
RcmZ>Bb@2~!Wbg?y1^^Cl0*3$q
literal 0
HcmV?d00001
diff --git a/tests/luks/test-key-option-inspect-luks-on-lvm.sh b/tests/luks/test-key-option-inspect-luks-on-lvm.sh
index 6109c4eec..2ee83332c 100755
--- a/tests/luks/test-key-option-inspect-luks-on-lvm.sh
+++ b/tests/luks/test-key-option-inspect-luks-on-lvm.sh
@@ -1,6 +1,6 @@
#!/bin/bash -
# libguestfs
-# Copyright (C) 2019-2025 Red Hat Inc.
+# Copyright (C) 2019-2026 Red Hat Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -34,7 +34,7 @@ guestfish=(guestfish --listen --ro --inspector
keys_by_lvname=(--key /dev/Volume-Group/Root:key:FEDORA-Root
--key /dev/Volume-Group/Logical-Volume-1:key:FEDORA-LV1
--key /dev/Volume-Group/Logical-Volume-2:key:FEDORA-LV2
- --key /dev/Volume-Group/Logical-Volume-3:key:FEDORA-LV3)
+ --key /dev/Volume-Group/Logical-Volume-3:file:luks/fedora-lv3.key)
# The variable assignment below will fail, and abort the script, if guestfish
# refuses to start up.
@@ -96,7 +96,7 @@ GUESTFISH_PID=
keys_by_uuid=(--key "$uuid_root":key:FEDORA-Root
--key "$uuid_lv1":key:FEDORA-LV1
--key "$uuid_lv2":key:FEDORA-LV2
- --key "$uuid_lv3":key:FEDORA-LV3)
+ --key "$uuid_lv3":file:luks/fedora-lv3.key)
fish_ref=$("${guestfish[@]}" "${keys_by_uuid[@]}")
eval "$fish_ref"
@@ -113,7 +113,7 @@ keys_by_mapper_lvname=(
--key /dev/mapper/Volume--Group-Root:key:FEDORA-Root
--key /dev/mapper/Volume--Group-Logical--Volume--1:key:FEDORA-LV1
--key /dev/mapper/Volume--Group-Logical--Volume--2:key:FEDORA-LV2
- --key /dev/mapper/Volume--Group-Logical--Volume--3:key:FEDORA-LV3
+ --key /dev/mapper/Volume--Group-Logical--Volume--3:file:luks/fedora-lv3.key
)
fish_ref=$("${guestfish[@]}" "${keys_by_mapper_lvname[@]}")
eval "$fish_ref"
@@ -130,7 +130,7 @@ keys_by_mapper_lvname=(
--key all:key:FEDORA-Root
--key all:key:FEDORA-LV1
--key all:key:FEDORA-LV2
- --key all:key:FEDORA-LV3
+ --key all:file:luks/fedora-lv3.key
)
fish_ref=$("${guestfish[@]}" "${keys_by_mapper_lvname[@]}")
eval "$fish_ref"
--
2.47.3

View File

@ -0,0 +1,31 @@
From ae3a26d4a26fd446054ba2b53082b0dd1406d29c Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Tue, 5 May 2026 12:03:11 +0100
Subject: [PATCH] docs/guestfs-recipes.pod: Remove reference to deleted
documentation
guestfs-tools commit 82edee0cdc ("sysprep: Remove documentation about
copying and cloning") removed this documentation from the
virt-sysprep(1) man page, so remove the reference here.
(cherry picked from commit c08fbe10db0b54916b6a52b2340106a0193a394d)
---
docs/guestfs-recipes.pod | 2 --
1 file changed, 2 deletions(-)
diff --git a/docs/guestfs-recipes.pod b/docs/guestfs-recipes.pod
index 305bd92f6..4a54696c6 100644
--- a/docs/guestfs-recipes.pod
+++ b/docs/guestfs-recipes.pod
@@ -68,8 +68,6 @@ Use a combination of tools like L<cp(1)>, L<dd(1)>, and
virt tools like L<virt-sysprep(1)>, L<virt-sparsify(1)>
and L<virt-resize(1)>.
-For more details, see: L<virt-sysprep(1)/COPYING AND CLONING>.
-
=head1 Convert a CD-ROM / DVD / ISO to a tarball
This converts input F<cd.iso> to output F<cd.tar.gz>:
--
2.47.3

View File

@ -0,0 +1,55 @@
From 596d6c6dc194e914064b53fa68d6834d2e8e78a1 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Tue, 5 May 2026 12:22:18 +0100
Subject: [PATCH] generator: Refer to new virt-customize(1)/FIRSTBOOT section
Documentation about firstboot scripts was consolidated and reworked
into a new section of the virt-customize(1) manual. Change the
existing references.
Update common submodule:
Richard W.M. Jones (1):
mlcustomize: Update generated files
Srihari Parimi (1):
mlcustomize: firstboot.bat must not reboot VM on error code 250
(cherry picked from commit 1f00e533610dc9291337c81669997ebebae23719)
---
common | 2 +-
generator/customize.ml | 10 ++--------
2 files changed, 3 insertions(+), 9 deletions(-)
Submodule common cf2e12078..dd34aebb4:
diff --git a/generator/customize.ml b/generator/customize.ml
index 19511dcdc..8df20ac1d 100644
--- a/generator/customize.ml
+++ b/generator/customize.ml
@@ -224,10 +224,7 @@ conveniently wraps the command up in a single line script for you.
You can have multiple I<--firstboot> options. They run in the same
order that they appear on the command line.
-Please take a look at L<virt-builder(1)/FIRST BOOT SCRIPTS> for more
-information and caveats about the first boot scripts.
-
-See also I<--run>.|};
+See also: L<virt-customize(1)/FIRSTBOOT>, I<--run>.|};
};
{ op_name = "firstboot-command";
@@ -241,10 +238,7 @@ boots up (as root, late in the boot process).
You can have multiple I<--firstboot> options. They run in the same
order that they appear on the command line.
-Please take a look at L<virt-builder(1)/FIRST BOOT SCRIPTS> for more
-information and caveats about the first boot scripts.
-
-See also I<--run>.|};
+See also: L<virt-customize(1)/FIRSTBOOT>, I<--run>.|};
};
{ op_name = "firstboot-install";
--
2.47.3

View File

@ -35,7 +35,7 @@ Summary: Access and modify virtual machine disk images
Name: libguestfs
Epoch: 1
Version: 1.58.1
Release: 2%{?dist}.alma.1
Release: 6%{?dist}.alma.1
License: LGPL-2.1-or-later
# Build only for architectures that have a kernel
@ -86,6 +86,22 @@ Patch0008: 0008-New-API-xfs_info2.patch
Patch0009: 0009-daemon-Reimplement-xfs_info-using-xfs_info2.patch
Patch0010: 0010-generator-Deprecate-xfs_info-replaced-by-xfs_info2.patch
Patch0011: 0011-tests-disks-debug-qemu.sh-Fix-test-for-update-QMP-te.patch
Patch0012: 0012-daemon-listfs.ml-Refactor-is_partition_can_hold_file.patch
Patch0013: 0013-daemon-listfs.ml-Ignore-CHS-geometry-error-from-part.patch
Patch0014: 0014-daemon-Move-read_whole_file-to-common-utils.patch
Patch0015: 0015-generator-Adjust-comment-for-String-Key.patch
Patch0016: 0016-daemon-Move-some-deprecated-functions-to-new-Luks-mo.patch
Patch0017: 0017-daemon-Rewrite-some-luks_-APIs-in-OCaml.patch
Patch0018: 0018-daemon-cryptsetup.ml-Reformat-this-code-for-consiste.patch
Patch0019: 0019-daemon-Use-common-Utils.write_key_to_tmp_file.patch
Patch0020: 0020-daemon-Allow-base64-text-as-a-prefix-on-Key-paramete.patch
Patch0021: 0021-Update-common-submodule.patch
Patch0022: 0022-tests-luks-Test-handling-of-a-binary-key-dev-file-fi.patch
Patch0023: 0023-docs-guestfs-recipes.pod-Remove-reference-to-deleted.patch
Patch0024: 0024-generator-Refer-to-new-virt-customize-1-FIRSTBOOT-se.patch
# For applying patches:
BuildRequires: git
BuildRequires: autoconf, automake, libtool, gettext-devel
@ -676,8 +692,7 @@ for %{name}.
%if 0%{verify_tarball_signature}
%{gpgverify} --keyring='%{SOURCE7}' --signature='%{SOURCE1}' --data='%{SOURCE0}'
%endif
%setup -q
%autopatch -p1
%autosetup -S git -p1
autoreconf -fiv
@ -1097,6 +1112,14 @@ rm ocaml/html/.gitignore
%changelog
* Mon Jun 08 2026 Andrew Lukoshko <alukoshko@almalinux.org> - 1:1.58.1-6.alma.1
- Update to libguestfs-1.58.1-6
- daemon/listfs.ml: ignore CHS geometry error from parted (Veritas/Sun)
- daemon: allow base64:/text: prefixes on Key parameters (binary LUKS keys)
- daemon: move read_whole_file to common/utils and rewrite luks_* APIs in OCaml
- Update common submodule
- Apply patches with %%autosetup -S git (a patch now adds binary test data)
* Tue Jan 27 2026 Eduard Abdullin <eabdullin@almalinux.org> - 1:1.58.1-2.alma.1
- Enable building for ppc64le