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 diff --git a/common b/common index b54ba2031..3ac5d1841 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit b54ba2031a37026401a827ee85988be76d8637e0 +Subproject commit 3ac5d18419637e02c2309f58d722343f9e1884fb 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 aa2b296c6..66ec831bc 100644 --- a/generator/actions_core.ml +++ b/generator/actions_core.ml @@ -9609,4 +9609,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