libguestfs/0008-New-API-xfs_info2.patch
Richard W.M. Jones 78ba1493b5 Add new xfs_info2 API
resolves: RHEL-143673
2026-01-26 18:13:56 +00:00

352 lines
12 KiB
Diff
Raw Permalink Blame History

This file contains ambiguous Unicode characters

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

From aac2ec68202f2913ae6000aa117a8ed5dffbd6bb Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
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<force> 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<pathordevice> 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<xfs_info(8)>,
+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<xfs_info(8)>.|} };
]
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