diff --git a/0005-lib-qemu.c-Use-machine-type-none-when-inspecting-QMP.patch b/0005-lib-qemu.c-Use-machine-type-none-when-inspecting-QMP.patch new file mode 100644 index 0000000..a77013f --- /dev/null +++ b/0005-lib-qemu.c-Use-machine-type-none-when-inspecting-QMP.patch @@ -0,0 +1,47 @@ +From d448d69171b5c1ee14a4eca5dc4964840ba99507 Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Mon, 26 Jan 2026 14:30:46 +0000 +Subject: [PATCH] lib/qemu.c: Use machine type none when inspecting QMP + properties +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Dan mentioned that there is a special machine type ("none") we can use +when querying for KVM. It has no CPU, memory, etc and does not run, +but you can still enable KVM for it. + +Note we have to remove the -cpu parameter, otherwise qemu prints this +error: + + qemu-kvm: apic-id property was not initialized properly + +Updates: commit 5da8102f5f59b9781075440dc68c8d08f9c8691e +Suggested-by: Daniel P. Berrangé +(cherry picked from commit f7a24b2ea81f3c5c2754fcf43e0347c7c5378b4a) +--- + lib/qemu.c | 8 +------- + 1 file changed, 1 insertion(+), 7 deletions(-) + +diff --git a/lib/qemu.c b/lib/qemu.c +index 19b5aa336..9edbe8208 100644 +--- a/lib/qemu.c ++++ b/lib/qemu.c +@@ -81,13 +81,7 @@ generic_qmp_test (guestfs_h *g, const char *qmp_command, char **outp) + guestfs_int_cmd_add_string_unquoted (cmd, "QEMU_AUDIO_DRV=none "); + guestfs_int_cmd_add_string_quoted (cmd, g->hv); + guestfs_int_cmd_add_string_unquoted (cmd, " -display none"); +- guestfs_int_cmd_add_string_unquoted (cmd, " -cpu max"); +- guestfs_int_cmd_add_string_unquoted (cmd, " -machine "); +- guestfs_int_cmd_add_string_quoted (cmd, +-#ifdef MACHINE_TYPE +- MACHINE_TYPE "," +-#endif +- "accel=kvm:hvf:tcg"); ++ guestfs_int_cmd_add_string_unquoted (cmd, " -machine none,accel=kvm:hvf:tcg"); + guestfs_int_cmd_add_string_unquoted (cmd, " -qmp stdio"); + guestfs_int_cmd_add_string_unquoted (cmd, " -S"); + guestfs_int_cmd_clear_capture_errors (cmd); +-- +2.47.3 + diff --git a/0006-generator-Fix-description-of-xfs_growfs.patch b/0006-generator-Fix-description-of-xfs_growfs.patch new file mode 100644 index 0000000..3e5d6fd --- /dev/null +++ b/0006-generator-Fix-description-of-xfs_growfs.patch @@ -0,0 +1,33 @@ +From 43d8a4970bbd908ed4b583335f1f4f42a3f6f354 Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Fri, 23 Jan 2026 08:47:42 +0000 +Subject: [PATCH] generator: Fix description of xfs_growfs + +This function does not return "geometry information". Remove +the bogus description. + +(cherry picked from commit 6166304be685b2fafb8cc968a4558a906bb0609b) +--- + generator/actions_core.ml | 6 +----- + 1 file changed, 1 insertion(+), 5 deletions(-) + +diff --git a/generator/actions_core.ml b/generator/actions_core.ml +index 9c23da008..de95bdaf0 100644 +--- a/generator/actions_core.ml ++++ b/generator/actions_core.ml +@@ -7332,11 +7332,7 @@ in the returned structure is defined by the API." }; + ["xfs_info"; "/"]], "ret->xfs_blocksize == 4096"), []; + ]; + shortdesc = "expand an existing XFS filesystem"; +- longdesc = {|Grow the XFS filesystem mounted at C. +- +-The returned struct contains geometry information. Missing +-fields are returned as C<-1> (for numeric fields) or empty +-string.|} }; ++ longdesc = {|Grow the XFS filesystem mounted at C.|} }; + + { defaults with + name = "rsync"; added = (1, 19, 29); +-- +2.47.3 + diff --git a/0007-generator-Fix-description-of-xfs_info.patch b/0007-generator-Fix-description-of-xfs_info.patch new file mode 100644 index 0000000..36a8105 --- /dev/null +++ b/0007-generator-Fix-description-of-xfs_info.patch @@ -0,0 +1,36 @@ +From d5daad98cfe254c14f4f4f12348f1589838c36a8 Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Fri, 23 Jan 2026 08:49:14 +0000 +Subject: [PATCH] generator: Fix description of xfs_info + +The returned struct contains filesystem metadata, not "geometry +information" (whatever that means). + +(cherry picked from commit 1c9c03bcd40ede11e7f4ece44dab908dcd36a657) +--- + generator/actions_core.ml | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +diff --git a/generator/actions_core.ml b/generator/actions_core.ml +index de95bdaf0..b57cf56b0 100644 +--- a/generator/actions_core.ml ++++ b/generator/actions_core.ml +@@ -7236,11 +7236,12 @@ call C.|} }; + ["mount"; "/dev/sda1"; "/"]; + ["xfs_info"; "/"]], "ret->xfs_blocksize == 4096"), [] + ]; +- shortdesc = "get geometry of XFS filesystem"; ++ shortdesc = "get information about the XFS filesystem"; + longdesc = {|C is a mounted XFS filesystem or a device containing +-an XFS filesystem. This command returns the geometry of the filesystem. ++an XFS filesystem. This command returns miscellaneous ++metadata about the XFS filesystem. + +-The returned struct contains geometry information. Missing ++The returned struct contains miscellaneous metadata. Missing + fields are returned as C<-1> (for numeric fields) or empty + string.|} }; + +-- +2.47.3 + diff --git a/0008-New-API-xfs_info2.patch b/0008-New-API-xfs_info2.patch new file mode 100644 index 0000000..4bce713 --- /dev/null +++ b/0008-New-API-xfs_info2.patch @@ -0,0 +1,351 @@ +From aac2ec68202f2913ae6000aa117a8ed5dffbd6bb Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Fri, 23 Jan 2026 08:58:07 +0000 +Subject: [PATCH] New API: xfs_info2 + +Reimplement xfs_info by returning a hash table of values (rather than +a limited struct), and by writing it in OCaml with PCRE which makes +string parsing a lot simpler. This will now flexibly return all the +fields from the underlying xfs_info command, even (hopefully) future +fields. + +Note the field values are returned as strings, because the actual +fields in xfs_info output are fairly random and free-form. There is a +trade off here between returning as much information as we can, and +requiring the user to do a bit of (simple) field parsing. + +Fixes: https://issues.redhat.com/browse/RHEL-143673 +(cherry picked from commit dfd2700616dee92d75e91936fb56b5de0450f992) +--- + .gitignore | 1 + + common | 2 +- + daemon/Makefile.am | 7 ++- + daemon/xfs.ml | 128 ++++++++++++++++++++++++++++++++++++++ + generator/actions_core.ml | 35 +++++++++++ + generator/proc_nr.ml | 1 + + lib/MAX_PROC_NR | 2 +- + 7 files changed, 172 insertions(+), 4 deletions(-) + create mode 100644 daemon/xfs.ml + +diff --git a/.gitignore b/.gitignore +index 02160caff..c8a8312fa 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -119,6 +119,7 @@ Makefile.in + /daemon/stubs-?.c + /daemon/stubs.h + /daemon/types.ml ++/daemon/xfs.mli + /depcomp + /docs/guestfs-building.1 + /docs/guestfs-faq.1 +Submodule common b54ba2031..3ac5d1841: +diff --git a/common/mlpcre/PCRE.ml b/common/mlpcre/PCRE.ml +index 077290e..33074af 100644 +--- a/common/mlpcre/PCRE.ml ++++ b/common/mlpcre/PCRE.ml +@@ -22,7 +22,7 @@ exception Error of string * int + + type regexp + +-external compile : ?caseless:bool -> ?dotall:bool -> ?extended:bool -> ?multiline:bool -> string -> regexp = "guestfs_int_pcre_compile" ++external compile : ?anchored:bool -> ?caseless:bool -> ?dotall:bool -> ?extended:bool -> ?multiline:bool -> string -> regexp = "guestfs_int_pcre_compile_byte" "guestfs_int_pcre_compile" + external matches : ?offset:int -> regexp -> string -> bool = "guestfs_int_pcre_matches" + external sub : int -> string = "guestfs_int_pcre_sub" + external subi : int -> int * int = "guestfs_int_pcre_subi" +diff --git a/common/mlpcre/PCRE.mli b/common/mlpcre/PCRE.mli +index b69a56b..0fdc2bd 100644 +--- a/common/mlpcre/PCRE.mli ++++ b/common/mlpcre/PCRE.mli +@@ -52,11 +52,12 @@ exception Error of string * int + type regexp + (** The type of a compiled regular expression. *) + +-val compile : ?caseless:bool -> ?dotall:bool -> ?extended:bool -> ?multiline:bool -> string -> regexp ++val compile : ?anchored:bool -> ?caseless:bool -> ?dotall:bool -> ++ ?extended:bool -> ?multiline:bool -> string -> regexp + (** Compile a regular expression. This can raise {!Error}. + +- The flags [?caseless], [?dotall], [?extended], [?multiline] +- correspond to the [pcre_compile] flags [PCRE_CASELESS] etc. ++ The flags [?anchored], [?caseless], [?dotall], [?extended], [?multiline] ++ correspond to the [pcre_compile] flags [PCRE_ANCHORED] etc. + See pcre2api(3) for details of what they do. + All flags default to false. *) + +diff --git a/common/mlpcre/pcre-c.c b/common/mlpcre/pcre-c.c +index 3959fd5..11be157 100644 +--- a/common/mlpcre/pcre-c.c ++++ b/common/mlpcre/pcre-c.c +@@ -154,11 +154,12 @@ Optint_val (value intv, int defval) + } + + value +-guestfs_int_pcre_compile (value caselessv, value dotallv, +- value extendedv, value multilinev, ++guestfs_int_pcre_compile (value anchoredv, value caselessv, ++ value dotallv, value extendedv, ++ value multilinev, + value pattv) + { +- CAMLparam4 (caselessv, dotallv, extendedv, multilinev); ++ CAMLparam5 (anchoredv, caselessv, dotallv, extendedv, multilinev); + CAMLxparam1 (pattv); + const char *patt; + int options = 0; +@@ -167,6 +168,8 @@ guestfs_int_pcre_compile (value caselessv, value dotallv, + PCRE2_SIZE errnum; + + /* Flag parameters are all ‘bool option’, defaulting to false. */ ++ if (is_Some_true (anchoredv)) ++ options |= PCRE2_ANCHORED; + if (is_Some_true (caselessv)) + options |= PCRE2_CASELESS; + if (is_Some_true (dotallv)) +@@ -186,6 +189,14 @@ guestfs_int_pcre_compile (value caselessv, value dotallv, + CAMLreturn (Val_regexp (re)); + } + ++value ++guestfs_int_pcre_compile_byte (value *argv, int argn) ++{ ++ assert (argn == 6); ++ return guestfs_int_pcre_compile (argv[0], argv[1], argv[2], ++ argv[3], argv[4], argv[5]); ++} ++ + value + guestfs_int_pcre_matches (value offsetv, value rev, value strv) + { +diff --git a/daemon/Makefile.am b/daemon/Makefile.am +index c644d9881..d4a805046 100644 +--- a/daemon/Makefile.am ++++ b/daemon/Makefile.am +@@ -63,7 +63,8 @@ generator_built = \ + sfdisk.mli \ + statvfs.mli \ + structs.ml \ +- structs.mli ++ structs.mli \ ++ xfs.mli + + CONFIGURE_GENERATED_ML = \ + daemon_config.ml +@@ -312,7 +313,8 @@ SOURCES_MLI = \ + statvfs.mli \ + structs.mli \ + sysroot.mli \ +- utils.mli ++ utils.mli \ ++ xfs.mli + + SOURCES_ML = \ + $(CONFIGURE_GENERATED_ML) \ +@@ -347,6 +349,7 @@ SOURCES_ML = \ + realpath.ml \ + statvfs.ml \ + selinux.ml \ ++ xfs.ml \ + inspect_types.ml \ + inspect_utils.ml \ + inspect_fs_unix_fstab.ml \ +diff --git a/daemon/xfs.ml b/daemon/xfs.ml +new file mode 100644 +index 000000000..142b26775 +--- /dev/null ++++ b/daemon/xfs.ml +@@ -0,0 +1,128 @@ ++(* 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. ++ *) ++ ++open Printf ++ ++open Std_utils ++ ++open Utils ++ ++(* The output is horrific ... ++ ++meta-data=/dev/sda1 isize=512 agcount=4, agsize=122094659 blks ++ = sectsz=4096 attr=2, projid32bit=1 ++ = crc=1 finobt=1, sparse=1, rmapbt=0 ++ = reflink=1 bigtime=1 inobtcount=1 nrext64=0 ++ = exchange=0 metadir=0 ++data = bsize=4096 blocks=488378636, imaxpct=5 ++ = sunit=0 swidth=0 blks ++naming =version 2 bsize=4096 ascii-ci=0, ftype=1, parent=0 ++log =internal log bsize=4096 blocks=238466, version=2 ++ = sectsz=4096 sunit=1 blks, lazy-count=1 ++realtime =none extsz=4096 blocks=0, rtextents=0 ++ = rgcount=0 rgsize=0 extents ++ = zoned=0 start=0 reserved=0 ++ ++^heading ^"stuff" ^ data fields vaguely related to heading ++ ++Note also the inconsistent use of commas. ++*) ++ ++(* Split into groups using a positive lookahead assertion. *) ++let re1 = PCRE.compile ~extended:true {| \n (?=[a-z]) |} ++ ++(* Separate group heading and the rest. *) ++let re2 = PCRE.compile ~extended:true {| = |} ++ ++(* Match the first field in a group (if present). *) ++let re3 = PCRE.compile ~anchored:true ~extended:true {| ++ (version\s\d+|\S+\slog|\S+).* ++|} ++ ++(* Match next field=value in group. *) ++let re4 = PCRE.compile ~extended:true {| ++ ([-\w]+)=(\d+(\s(blks|extents))?) ++|} ++ ++let xfs_info2 dev = ++ (* Uncomment the first line to enable extra debugging. *) ++ (*let extra_debug = verbose () in*) ++ let extra_debug = false in ++ ++ let is_dev = is_device_parameter dev in ++ let arg = if is_dev then dev else Sysroot.sysroot_path dev in ++ let out = command "xfs_info" [arg] in ++ ++ (* Split the output by heading. *) ++ let groups = PCRE.nsplit re1 out in ++ let groups = List.map (PCRE.split re2) groups in ++ let groups = List.map (fun (name, rest) -> String.trim name, rest) groups in ++ ++ if extra_debug then ( ++ List.iteri ( ++ fun i (name, rest) -> ++ eprintf "xfs_info2: group %d: %S: %S\n%!" i name rest ++ ) groups ++ ); ++ ++ (* Parse each group into the final list of values. *) ++ let values = ref [] in ++ List.iter ( ++ fun (group_name, rest) -> ++ let len = String.length rest in ++ ++ (* If there is some string at the beginning of the ++ * group then we create a (group_name, string) value, ++ * eg. ("meta-data", "/dev/sda1") ++ *) ++ let start = ++ if PCRE.matches re3 rest then ( ++ let value = PCRE.sub 1 in ++ List.push_front (group_name, value) values; ++ (* Start parsing after this. *) ++ String.length value ++ ) ++ else 0 in ++ ++ let rec loop i = ++ if extra_debug then ++ eprintf "xfs_info2: parsing group %S from %d\n%!" group_name i; ++ if i <= len && PCRE.matches ~offset:i re4 rest then ( ++ let field_name = PCRE.sub 1 in ++ if extra_debug then eprintf "xfs_info2: sub1=%S\n%!" field_name; ++ let value = PCRE.sub 2 in ++ if extra_debug then eprintf "xfs_info2: sub2=%S\n%!" value; ++ let name = sprintf "%s.%s" group_name field_name in ++ List.push_front (name, value) values; ++ ++ (* Next time round the loop, start parsing after the ++ * current matched subexpression. ++ *) ++ loop (snd (PCRE.subi 2) + 1) ++ ) ++ in ++ (try ++ loop start ++ with ++ Not_found -> ++ failwithf "xfs_info2: internal error: unexpected Not_found exception. Enable debug and send the full output in a bug report." ++ ); ++ ++ ) groups; ++ ++ List.rev !values +diff --git a/generator/actions_core.ml b/generator/actions_core.ml +index b57cf56b0..b7e4dae45 100644 +--- a/generator/actions_core.ml ++++ b/generator/actions_core.ml +@@ -9575,4 +9575,39 @@ The optional C boolean controls whether the context + is reset for customizable files, and also whether the + user, role and range parts of the file context is changed.|} }; + ++ { defaults with ++ name = "xfs_info2"; added = (1, 59, 2); ++ style = RHashtable (RPlainString, RPlainString, "info"), [String (Dev_or_Path, "pathordevice")], []; ++ impl = OCaml "Xfs.xfs_info2"; ++ optional = Some "xfs"; ++ tests = [ ++ InitEmpty, Always, TestResult ( ++ [["part_disk"; "/dev/sda"; "mbr"]; ++ ["mkfs"; "xfs"; "/dev/sda1"; ""; "NOARG"; ""; ""; "NOARG"]; ++ ["mount"; "/dev/sda1"; "/"]; ++ ["xfs_info2"; "/"]], ++ "check_hash (ret, \"data.bsize\", \"4096\") == 0"), [] ++ ]; ++ shortdesc = "get information about the XFS filesystem"; ++ longdesc = {|C is a mounted XFS filesystem or ++a device containing an XFS filesystem. This command returns ++miscellaneous metadata about the XFS filesystem. ++ ++The output is a hash derived from the output of L, ++and generally looks like: ++ ++ meta-data: /dev/sda1 ++ meta-data.isize: 512 ++ meta-data.agcount: 4 ++ meta-data.agsize: 65528 blks ++ meta-data.sectsz: 512 ++ meta-data.attr: 2 ++ meta-data.projid32bit: 1 ++ meta-data.crc: 1 ++ [...] ++ data.bsize: 4096 ++ data.blocks: 262112 ++ [...] ++ ++More information can be found by reading L.|} }; + ] +diff --git a/generator/proc_nr.ml b/generator/proc_nr.ml +index 11e7b9d1b..b22d88b58 100644 +--- a/generator/proc_nr.ml ++++ b/generator/proc_nr.ml +@@ -524,6 +524,7 @@ let proc_nr = [ + 519, "setfiles"; + 520, "ntfs_chmod"; + 521, "inspect_get_windows_group_policy"; ++522, "xfs_info2"; + ] + + (* End of list. If adding a new entry, add it at the end of the list +diff --git a/lib/MAX_PROC_NR b/lib/MAX_PROC_NR +index 5a232f264..ec0e415d0 100644 +--- a/lib/MAX_PROC_NR ++++ b/lib/MAX_PROC_NR +@@ -1 +1 @@ +-521 ++522 +-- +2.47.3 + diff --git a/0009-daemon-Reimplement-xfs_info-using-xfs_info2.patch b/0009-daemon-Reimplement-xfs_info-using-xfs_info2.patch new file mode 100644 index 0000000..811f5b2 --- /dev/null +++ b/0009-daemon-Reimplement-xfs_info-using-xfs_info2.patch @@ -0,0 +1,411 @@ +From fb77334e1c40ac64535f2e36ee6edbdf54a5a6ac Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Fri, 23 Jan 2026 12:27:39 +0000 +Subject: [PATCH] daemon: Reimplement xfs_info using xfs_info2 + +Remove all the complicated old C parsing code and reimplement xfs_info +using xfs_info2. Note that this function will be deprecated. + +(cherry picked from commit e1deb358ce19685e4d078a64edfdbe1da7ddf63c) +--- + daemon/xfs.c | 310 -------------------------------------- + daemon/xfs.ml | 45 ++++++ + generator/actions_core.ml | 1 + + 3 files changed, 46 insertions(+), 310 deletions(-) + +diff --git a/daemon/xfs.c b/daemon/xfs.c +index aa056ddff..06e5fc072 100644 +--- a/daemon/xfs.c ++++ b/daemon/xfs.c +@@ -38,316 +38,6 @@ optgroup_xfs_available (void) + return prog_exists ("mkfs.xfs"); + } + +-/* Return everything up to the first comma, equals or space in the input +- * string, strdup'ing the return value. +- */ +-static char * +-split_strdup (char *string) +-{ +- size_t len; +- char *ret; +- +- len = strcspn (string, " ,="); +- ret = strndup (string, len); +- if (!ret) { +- reply_with_perror ("malloc"); +- return NULL; +- } +- return ret; +-} +- +-static int +-parse_uint32 (uint32_t *ret, const char *str) +-{ +- uint32_t r; +- +- if (sscanf (str, "%" SCNu32, &r) != 1) { +- reply_with_error ("cannot parse numeric field from xfs_info: %s", str); +- return -1; +- } +- +- *ret = r; +- return 0; +-} +- +-static int +-parse_uint64 (uint64_t *ret, const char *str) +-{ +- uint64_t r; +- +- if (sscanf (str, "%" SCNu64, &r) != 1) { +- reply_with_error ("cannot parse numeric field from xfs_info: %s", str); +- return -1; +- } +- +- *ret = r; +- return 0; +-} +- +-/* Typical crazy output from the xfs_info command: +- * +- * meta-data=/dev/sda1 isize=256 agcount=4, agsize=6392 blks +- * = sectsz=512 attr=2 +- *[ = crc=0 ] +- * data = bsize=4096 blocks=25568, imaxpct=25 +- * = sunit=0 swidth=0 blks +- * naming =version 2 bsize=4096 ascii-ci=0 +- * log =internal bsize=4096 blocks=1200, version=2 +- * = sectsz=512 sunit=0 blks, lazy-count=1 +- * realtime =none extsz=4096 blocks=0, rtextents=0 +- * +- * [...] line only appears in Fedora >= 21 +- * +- * We may need to revisit this parsing code if the output changes +- * in future. +- */ +-static guestfs_int_xfsinfo * +-parse_xfs_info (char **lines) +-{ +- guestfs_int_xfsinfo *ret; +- CLEANUP_FREE char *section = NULL; /* first column, eg "meta-data", "data" */ +- char *p; +- size_t i; +- +- ret = malloc (sizeof *ret); +- if (ret == NULL) { +- reply_with_error ("malloc"); +- return NULL; +- } +- +- /* Initialize fields to NULL or -1 so the caller can tell which fields +- * were updated in the code below. +- */ +- ret->xfs_mntpoint = NULL; +- ret->xfs_inodesize = -1; +- ret->xfs_agcount = -1; +- ret->xfs_agsize = -1; +- ret->xfs_sectsize = -1; +- ret->xfs_attr = -1; +- ret->xfs_blocksize = -1; +- ret->xfs_datablocks = -1; +- ret->xfs_imaxpct = -1; +- ret->xfs_sunit = -1; +- ret->xfs_swidth = -1; +- ret->xfs_dirversion = -1; +- ret->xfs_dirblocksize = -1; +- ret->xfs_cimode = -1; +- ret->xfs_logname = NULL; +- ret->xfs_logblocksize = -1; +- ret->xfs_logblocks = -1; +- ret->xfs_logversion = -1; +- ret->xfs_logsectsize = -1; +- ret->xfs_logsunit = -1; +- ret->xfs_lazycount = -1; +- ret->xfs_rtname = NULL; +- ret->xfs_rtextsize = -1; +- ret->xfs_rtblocks = -1; +- ret->xfs_rtextents = -1; +- +- for (i = 0; lines[i] != NULL; ++i) { +- if (verbose) +- fprintf (stderr, "xfs_info: lines[%zu] = \'%s\'\n", i, lines[i]); +- +- if (c_isalpha (lines[i][0])) { +- free (section); +- section = split_strdup (lines[i]); +- if (!section) goto error; +- +- if (verbose) +- fprintf (stderr, "xfs_info: new section %s\n", section); +- } +- +- if ((p = strstr (lines[i], "meta-data="))) { +- ret->xfs_mntpoint = split_strdup (p + 10); +- if (ret->xfs_mntpoint == NULL) goto error; +- } +- if ((p = strstr (lines[i], "isize="))) { +- CLEANUP_FREE char *buf = split_strdup (p + 6); +- if (buf == NULL) goto error; +- if (parse_uint32 (&ret->xfs_inodesize, buf) == -1) +- goto error; +- } +- if ((p = strstr (lines[i], "agcount="))) { +- CLEANUP_FREE char *buf = split_strdup (p + 8); +- if (buf == NULL) goto error; +- if (parse_uint32 (&ret->xfs_agcount, buf) == -1) +- goto error; +- } +- if ((p = strstr (lines[i], "agsize="))) { +- CLEANUP_FREE char *buf = split_strdup (p + 7); +- if (buf == NULL) goto error; +- if (parse_uint32 (&ret->xfs_agsize, buf) == -1) +- goto error; +- } +- if ((p = strstr (lines[i], "sectsz="))) { +- if (section) { +- CLEANUP_FREE char *buf = split_strdup (p + 7); +- if (buf == NULL) goto error; +- if (STREQ (section, "meta-data")) { +- if (parse_uint32 (&ret->xfs_sectsize, buf) == -1) +- goto error; +- } else if (STREQ (section, "log")) { +- if (parse_uint32 (&ret->xfs_logsectsize, buf) == -1) +- goto error; +- } +- } +- } +- if ((p = strstr (lines[i], "attr="))) { +- CLEANUP_FREE char *buf = split_strdup (p + 5); +- if (buf == NULL) goto error; +- if (parse_uint32 (&ret->xfs_attr, buf) == -1) +- goto error; +- } +- if ((p = strstr (lines[i], "bsize="))) { +- if (section) { +- CLEANUP_FREE char *buf = split_strdup (p + 6); +- if (buf == NULL) goto error; +- if (STREQ (section, "data")) { +- if (parse_uint32 (&ret->xfs_blocksize, buf) == -1) +- goto error; +- } else if (STREQ (section, "naming")) { +- if (parse_uint32 (&ret->xfs_dirblocksize, buf) == -1) +- goto error; +- } else if (STREQ (section, "log")) { +- if (parse_uint32 (&ret->xfs_logblocksize, buf) == -1) +- goto error; +- } +- } +- } +- if ((p = strstr (lines[i], "blocks="))) { +- if (section) { +- CLEANUP_FREE char *buf = split_strdup (p + 7); +- if (buf == NULL) goto error; +- if (STREQ (section, "data")) { +- if (parse_uint64 (&ret->xfs_datablocks, buf) == -1) +- goto error; +- } else if (STREQ (section, "log")) { +- if (parse_uint32 (&ret->xfs_logblocks, buf) == -1) +- goto error; +- } else if (STREQ (section, "realtime")) { +- if (parse_uint64 (&ret->xfs_rtblocks, buf) == -1) +- goto error; +- } +- } +- } +- if ((p = strstr (lines[i], "imaxpct="))) { +- CLEANUP_FREE char *buf = split_strdup (p + 8); +- if (buf == NULL) goto error; +- if (parse_uint32 (&ret->xfs_imaxpct, buf) == -1) +- goto error; +- } +- if ((p = strstr (lines[i], "sunit="))) { +- if (section) { +- CLEANUP_FREE char *buf = split_strdup (p + 6); +- if (buf == NULL) goto error; +- if (STREQ (section, "data")) { +- if (parse_uint32 (&ret->xfs_sunit, buf) == -1) +- goto error; +- } else if (STREQ (section, "log")) { +- if (parse_uint32 (&ret->xfs_logsunit, buf) == -1) +- goto error; +- } +- } +- } +- if ((p = strstr (lines[i], "swidth="))) { +- CLEANUP_FREE char *buf = split_strdup (p + 7); +- if (buf == NULL) goto error; +- if (parse_uint32 (&ret->xfs_swidth, buf) == -1) +- goto error; +- } +- if ((p = strstr (lines[i], "naming =version "))) { +- CLEANUP_FREE char *buf = split_strdup (p + 18); +- if (buf == NULL) goto error; +- if (parse_uint32 (&ret->xfs_dirversion, buf) == -1) +- goto error; +- } +- if ((p = strstr (lines[i], "ascii-ci="))) { +- CLEANUP_FREE char *buf = split_strdup (p + 9); +- if (buf == NULL) goto error; +- if (parse_uint32 (&ret->xfs_cimode, buf) == -1) +- goto error; +- } +- if ((p = strstr (lines[i], "log ="))) { +- ret->xfs_logname = split_strdup (p + 10); +- if (ret->xfs_logname == NULL) goto error; +- } +- if ((p = strstr (lines[i], "version="))) { +- CLEANUP_FREE char *buf = split_strdup (p + 8); +- if (buf == NULL) goto error; +- if (parse_uint32 (&ret->xfs_logversion, buf) == -1) +- goto error; +- } +- if ((p = strstr (lines[i], "lazy-count="))) { +- CLEANUP_FREE char *buf = split_strdup (p + 11); +- if (buf == NULL) goto error; +- if (parse_uint32 (&ret->xfs_lazycount, buf) == -1) +- goto error; +- } +- if ((p = strstr (lines[i], "realtime ="))) { +- ret->xfs_rtname = split_strdup (p + 10); +- if (ret->xfs_rtname == NULL) goto error; +- } +- if ((p = strstr (lines[i], "rtextents="))) { +- CLEANUP_FREE char *buf = split_strdup (p + 10); +- if (buf == NULL) goto error; +- if (parse_uint64 (&ret->xfs_rtextents, buf) == -1) +- goto error; +- } +- } +- +- if (ret->xfs_mntpoint == NULL) { +- ret->xfs_mntpoint = strdup (""); +- if (ret->xfs_mntpoint == NULL) goto error; +- } +- if (ret->xfs_logname == NULL) { +- ret->xfs_logname = strdup (""); +- if (ret->xfs_logname == NULL) goto error; +- } +- if (ret->xfs_rtname == NULL) { +- ret->xfs_rtname = strdup (""); +- if (ret->xfs_rtname == NULL) goto error; +- } +- +- return ret; +- +- error: +- free (ret->xfs_mntpoint); +- free (ret->xfs_logname); +- free (ret->xfs_rtname); +- free (ret); +- return NULL; +-} +- +-guestfs_int_xfsinfo * +-do_xfs_info (const char *pathordevice) +-{ +- int r; +- CLEANUP_FREE char *buf = NULL; +- CLEANUP_FREE char *out = NULL, *err = NULL; +- CLEANUP_FREE_STRING_LIST char **lines = NULL; +- int is_dev; +- +- is_dev = is_device_parameter (pathordevice); +- buf = is_dev ? strdup (pathordevice) +- : sysroot_path (pathordevice); +- if (buf == NULL) { +- reply_with_perror ("malloc"); +- return NULL; +- } +- +- r = command (&out, &err, "xfs_info", buf, NULL); +- if (r == -1) { +- reply_with_error ("%s", err); +- return NULL; +- } +- +- lines = split_lines (out); +- if (lines == NULL) +- return NULL; +- +- return parse_xfs_info (lines); +-} +- + int + do_xfs_growfs (const char *path, + int datasec, int logsec, int rtsec, +diff --git a/daemon/xfs.ml b/daemon/xfs.ml +index 142b26775..157a84058 100644 +--- a/daemon/xfs.ml ++++ b/daemon/xfs.ml +@@ -17,6 +17,7 @@ + *) + + open Printf ++open Scanf + + open Std_utils + +@@ -126,3 +127,47 @@ let xfs_info2 dev = + ) groups; + + List.rev !values ++ ++(* Deprecated xfs_info. *) ++let xfs_info dev = ++ let h = xfs_info2 dev in ++ ++ let find field parsefn = ++ try List.assoc field h |> parsefn ++ with ++ | Not_found -> ++ failwithf "xfs_info: unexpected missing field: %s" field ++ | exn -> ++ failwithf "xfs_info: failure finding field: %s: %s" ++ field (Printexc.to_string exn) ++ in ++ ++ let parse_blks s = sscanf s "%ld blks" Fun.id in ++ let parse_version s = sscanf s "version %ld" Fun.id in ++ ++ { Structs.xfs_mntpoint = find "meta-data" Fun.id; ++ xfs_inodesize = find "meta-data.isize" Int32.of_string; ++ xfs_agcount = find "meta-data.agcount" Int32.of_string; ++ xfs_agsize = find "meta-data.agsize" parse_blks; ++ xfs_sectsize = find "meta-data.sectsz" Int32.of_string; ++ xfs_attr = find "meta-data.attr" Int32.of_string; ++ xfs_blocksize = find "data.bsize" Int32.of_string; ++ xfs_datablocks = find "data.blocks" Int64.of_string; ++ xfs_imaxpct = find "data.imaxpct" Int32.of_string; ++ xfs_sunit = find "data.sunit" Int32.of_string; ++ xfs_swidth = find "data.swidth" parse_blks; ++ xfs_dirversion = find "naming" parse_version; ++ xfs_dirblocksize = find "naming.bsize" Int32.of_string; ++ xfs_cimode = find "naming.ascii-ci" Int32.of_string; ++ xfs_logname = find "log" Fun.id; ++ xfs_logblocksize = find "log.bsize" Int32.of_string; ++ xfs_logblocks = find "log.blocks" Int32.of_string; ++ xfs_logversion = find "log.version" Int32.of_string; ++ xfs_logsectsize = find "log.sectsz" Int32.of_string; ++ xfs_logsunit = find "log.sunit" parse_blks; ++ xfs_lazycount = find "log.lazy-count" Int32.of_string; ++ xfs_rtname = find "realtime" Fun.id; ++ xfs_rtextsize = find "realtime.extsz" Int32.of_string; ++ xfs_rtblocks = find "realtime.blocks" Int64.of_string; ++ xfs_rtextents = find "realtime.rtextents" Int64.of_string; ++ } +diff --git a/generator/actions_core.ml b/generator/actions_core.ml +index b7e4dae45..f2cf83440 100644 +--- a/generator/actions_core.ml ++++ b/generator/actions_core.ml +@@ -7228,6 +7228,7 @@ call C.|} }; + { defaults with + name = "xfs_info"; added = (1, 19, 21); + style = RStruct ("info", "xfsinfo"), [String (Dev_or_Path, "pathordevice")], []; ++ impl = OCaml "Xfs.xfs_info"; + optional = Some "xfs"; + tests = [ + InitEmpty, Always, TestResult ( +-- +2.47.3 + diff --git a/0010-generator-Deprecate-xfs_info-replaced-by-xfs_info2.patch b/0010-generator-Deprecate-xfs_info-replaced-by-xfs_info2.patch new file mode 100644 index 0000000..82e2b45 --- /dev/null +++ b/0010-generator-Deprecate-xfs_info-replaced-by-xfs_info2.patch @@ -0,0 +1,80 @@ +From 5b3de10b018e891ef7cc7fae0e8b1b32445eb392 Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Fri, 23 Jan 2026 08:51:11 +0000 +Subject: [PATCH] generator: Deprecate xfs_info (replaced by xfs_info2) + +Deprecate this function, and suggest using xfs_info2 as its +replacement. + +(cherry picked from commit 7833461af76d1feaf2dd1e779f7f04bcca91a6bc) +--- + generator/actions_core.ml | 21 --------------------- + generator/actions_core_deprecated.ml | 22 ++++++++++++++++++++++ + 2 files changed, 22 insertions(+), 21 deletions(-) + +diff --git a/generator/actions_core.ml b/generator/actions_core.ml +index f2cf83440..26d77c667 100644 +--- a/generator/actions_core.ml ++++ b/generator/actions_core.ml +@@ -7225,27 +7225,6 @@ be returned if you called C. + To find out the maximum number of devices that could be added, + call C.|} }; + +- { defaults with +- name = "xfs_info"; added = (1, 19, 21); +- style = RStruct ("info", "xfsinfo"), [String (Dev_or_Path, "pathordevice")], []; +- impl = OCaml "Xfs.xfs_info"; +- optional = Some "xfs"; +- tests = [ +- InitEmpty, Always, TestResult ( +- [["part_disk"; "/dev/sda"; "mbr"]; +- ["mkfs"; "xfs"; "/dev/sda1"; ""; "NOARG"; ""; ""; "NOARG"]; +- ["mount"; "/dev/sda1"; "/"]; +- ["xfs_info"; "/"]], "ret->xfs_blocksize == 4096"), [] +- ]; +- shortdesc = "get information about the XFS filesystem"; +- longdesc = {|C is a mounted XFS filesystem or a device containing +-an XFS filesystem. This command returns miscellaneous +-metadata about the XFS filesystem. +- +-The returned struct contains miscellaneous metadata. Missing +-fields are returned as C<-1> (for numeric fields) or empty +-string.|} }; +- + { defaults with + name = "pvchange_uuid"; added = (1, 19, 26); + style = RErr, [String (Device, "device")], []; +diff --git a/generator/actions_core_deprecated.ml b/generator/actions_core_deprecated.ml +index aa98bd5fe..308495c45 100644 +--- a/generator/actions_core_deprecated.ml ++++ b/generator/actions_core_deprecated.ml +@@ -945,4 +945,26 @@ The optional C boolean controls whether the context + is reset for customizable files, and also whether the + user, role and range parts of the file context is changed.|} }; + ++ { defaults with ++ name = "xfs_info"; added = (1, 19, 21); ++ style = RStruct ("info", "xfsinfo"), [String (Dev_or_Path, "pathordevice")], []; ++ impl = OCaml "Xfs.xfs_info"; ++ optional = Some "xfs"; ++ deprecated_by = Replaced_by "xfs_info2"; ++ tests = [ ++ InitEmpty, Always, TestResult ( ++ [["part_disk"; "/dev/sda"; "mbr"]; ++ ["mkfs"; "xfs"; "/dev/sda1"; ""; "NOARG"; ""; ""; "NOARG"]; ++ ["mount"; "/dev/sda1"; "/"]; ++ ["xfs_info"; "/"]], "ret->xfs_blocksize == 4096"), [] ++ ]; ++ shortdesc = "get information about the XFS filesystem"; ++ longdesc = {|C is a mounted XFS filesystem or a device containing ++an XFS filesystem. This command returns miscellaneous ++metadata about the XFS filesystem. ++ ++The returned struct contains miscellaneous metadata. Missing ++fields are returned as C<-1> (for numeric fields) or empty ++string.|} }; ++ + ] +-- +2.47.3 + diff --git a/0011-tests-disks-debug-qemu.sh-Fix-test-for-update-QMP-te.patch b/0011-tests-disks-debug-qemu.sh-Fix-test-for-update-QMP-te.patch new file mode 100644 index 0000000..670aa32 --- /dev/null +++ b/0011-tests-disks-debug-qemu.sh-Fix-test-for-update-QMP-te.patch @@ -0,0 +1,33 @@ +From 4a5ee2fbcbef5f10b8faf8f8659ba5f82145f42d Mon Sep 17 00:00:00 2001 +From: "Richard W.M. Jones" +Date: Mon, 26 Jan 2026 14:59:13 +0000 +Subject: [PATCH] tests/disks/debug-qemu.sh: Fix test for update QMP test + +Commit f7a24b2ea8 ("lib/qemu.c: Use machine type none when inspecting +QMP properties") changed the number of command line parameters used +before the '-qmp stdio' option when libguestfs queries qemu features. +This broke some tests which rely on the exact order of parameters. + +Fixes: commit f7a24b2ea81f3c5c2754fcf43e0347c7c5378b4a +Updates: commit 5da8102f5f59b9781075440dc68c8d08f9c8691e +(cherry picked from commit 45d0e66d02adf1a42c8eca979fa38d2f91d9f3c2) +--- + tests/disks/debug-qemu.sh | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tests/disks/debug-qemu.sh b/tests/disks/debug-qemu.sh +index 96c90553b..e6e80abe3 100755 +--- a/tests/disks/debug-qemu.sh ++++ b/tests/disks/debug-qemu.sh +@@ -25,7 +25,7 @@ fi + + # The direct backend runs qemu ... -qmp stdio to query for KVM. For + # the test to pass we have to provide an answer here. +-if [ "x$7" = "x-qmp" ]; then ++if [ "x$5" = "x-qmp" ]; then + # Consume stdin first. + cat >/dev/null + # Write some fake output. +-- +2.47.3 + diff --git a/libguestfs.spec b/libguestfs.spec index a32bc0e..992d67e 100644 --- a/libguestfs.spec +++ b/libguestfs.spec @@ -35,7 +35,7 @@ Summary: Access and modify virtual machine disk images Name: libguestfs Epoch: 1 Version: 1.58.1 -Release: 1%{?dist}.alma.1 +Release: 2%{?dist}.alma.1 License: LGPL-2.1-or-later # Build only for architectures that have a kernel @@ -79,6 +79,13 @@ Patch0001: 0001-generator-daemon.ml-Avoid-not-available-macro-for-OC.patch Patch0002: 0002-RHEL-Disable-unsupported-remote-drive-protocols-RHBZ.patch Patch0003: 0003-RHEL-Reject-use-of-libguestfs-winsupport-features-ex.patch Patch0004: 0004-RHEL-appliance-init-Run-depmod-a-to-rebuild-kernel-m.patch +Patch0005: 0005-lib-qemu.c-Use-machine-type-none-when-inspecting-QMP.patch +Patch0006: 0006-generator-Fix-description-of-xfs_growfs.patch +Patch0007: 0007-generator-Fix-description-of-xfs_info.patch +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 BuildRequires: autoconf, automake, libtool, gettext-devel @@ -1090,10 +1097,10 @@ rm ocaml/html/.gitignore %changelog -* Wed Jan 21 2026 Eduard Abdullin - 1:1.58.1-1.alma.1 +* Tue Jan 27 2026 Eduard Abdullin - 1:1.58.1-2.alma.1 - Enable building for ppc64le -* Tue Jan 20 2026 Richard W.M. Jones - 1:1.58.1-1 +* Mon Jan 26 2026 Richard W.M. Jones - 1:1.58.1-2 - Rebase to libguestfs 1.58.1 resolves: RHEL-111240 - Synchronize spec file with Fedora @@ -1107,6 +1114,8 @@ rm ocaml/html/.gitignore resolves: RHEL-122315 - Add AV and GPOs to inspection info resolves: RHEL-125846 +- Add new xfs_info2 API + resolves: RHEL-143673 * Thu Aug 14 2025 Richard W.M. Jones - 1:1.56.1-3 - Rebase to libguestfs 1.56.1