Define the types which will be used to communicate between the
different parts of the inspection code. The main types are:
fs corresponds to ‘struct inspect_fs’ in C code
root no direct correspondence with the C code, but in the C
code, ‘inspect_fs’ was overloaded to store roots
inspection_data
the inspection data which is incrementally collected about
each filesystem as we perform inspection steps
Other types have simple and obvious correspondences with the
equivalent C code.
Add some utility function which will be used by inspection.
Note that this commit has no effect on its own, it just links extra
dead code into the daemon.
---
daemon/Makefile.am | 4 +
daemon/inspect_types.ml | 314 +++++++++++++++++++++++++++++++++++++++++++++++
daemon/inspect_types.mli | 182 +++++++++++++++++++++++++++
daemon/inspect_utils.ml | 187 ++++++++++++++++++++++++++++
daemon/inspect_utils.mli | 53 ++++++++
5 files changed, 740 insertions(+)
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index 3e8bdb44f..51737e511 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -252,6 +252,8 @@ SOURCES_MLI = \
file.mli \
filearch.mli \
findfs.mli \
+ inspect_types.mli \
+ inspect_utils.mli \
is.mli \
ldm.mli \
link.mli \
@@ -287,6 +289,8 @@ SOURCES_ML = \
parted.ml \
listfs.ml \
realpath.ml \
+ inspect_types.ml \
+ inspect_utils.ml \
callbacks.ml \
daemon.ml
diff --git a/daemon/inspect_types.ml b/daemon/inspect_types.ml
new file mode 100644
index 000000000..4570349ba
--- /dev/null
+++ b/daemon/inspect_types.ml
@@ -0,0 +1,314 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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
+
+type fs = {
+ fs_location : location;
+ role : role; (** Special cases: root filesystem or /usr *)
+}
+and root = {
+ root_location : location;
+ inspection_data : inspection_data;
+}
+and location = {
+ mountable : Mountable.t; (** The device name or other mountable object.*)
+ vfs_type : string; (** Returned from [vfs_type] API. *)
+}
+
+and role =
+ | RoleRoot of inspection_data
+ | RoleUsr of inspection_data
+ | RoleSwap
+ | RoleOther
+and inspection_data = {
+ mutable os_type : os_type option;
+ mutable distro : distro option;
+ mutable package_format : package_format option;
+ mutable package_management : package_management option;
+ mutable product_name : string option;
+ mutable product_variant : string option;
+ mutable version : version option;
+ mutable arch : string option;
+ mutable hostname : string option;
+ mutable fstab : fstab_entry list;
+ mutable windows_systemroot : string option;
+ mutable windows_software_hive : string option;
+ mutable windows_system_hive : string option;
+ mutable windows_current_control_set : string option;
+ mutable drive_mappings : drive_mapping list;
+}
+and os_type =
+ | OS_TYPE_DOS
+ | OS_TYPE_FREEBSD
+ | OS_TYPE_HURD
+ | OS_TYPE_LINUX
+ | OS_TYPE_MINIX
+ | OS_TYPE_NETBSD
+ | OS_TYPE_OPENBSD
+ | OS_TYPE_WINDOWS
+and distro =
+ | DISTRO_ALPINE_LINUX
+ | DISTRO_ALTLINUX
+ | DISTRO_ARCHLINUX
+ | DISTRO_BUILDROOT
+ | DISTRO_CENTOS
+ | DISTRO_CIRROS
+ | DISTRO_COREOS
+ | DISTRO_DEBIAN
+ | DISTRO_FEDORA
+ | DISTRO_FREEBSD
+ | DISTRO_FREEDOS
+ | DISTRO_FRUGALWARE
+ | DISTRO_GENTOO
+ | DISTRO_LINUX_MINT
+ | DISTRO_MAGEIA
+ | DISTRO_MANDRIVA
+ | DISTRO_MEEGO
+ | DISTRO_NETBSD
+ | DISTRO_OPENBSD
+ | DISTRO_OPENSUSE
+ | DISTRO_ORACLE_LINUX
+ | DISTRO_PARDUS
+ | DISTRO_PLD_LINUX
+ | DISTRO_REDHAT_BASED
+ | DISTRO_RHEL
+ | DISTRO_SCIENTIFIC_LINUX
+ | DISTRO_SLACKWARE
+ | DISTRO_SLES
+ | DISTRO_SUSE_BASED
+ | DISTRO_TTYLINUX
+ | DISTRO_UBUNTU
+ | DISTRO_VOID_LINUX
+ | DISTRO_WINDOWS
+and package_format =
+ | PACKAGE_FORMAT_APK
+ | PACKAGE_FORMAT_DEB
+ | PACKAGE_FORMAT_EBUILD
+ | PACKAGE_FORMAT_PACMAN
+ | PACKAGE_FORMAT_PISI
+ | PACKAGE_FORMAT_PKGSRC
+ | PACKAGE_FORMAT_RPM
+ | PACKAGE_FORMAT_XBPS
+and package_management =
+ | PACKAGE_MANAGEMENT_APK
+ | PACKAGE_MANAGEMENT_APT
+ | PACKAGE_MANAGEMENT_DNF
+ | PACKAGE_MANAGEMENT_PACMAN
+ | PACKAGE_MANAGEMENT_PISI
+ | PACKAGE_MANAGEMENT_PORTAGE
+ | PACKAGE_MANAGEMENT_UP2DATE
+ | PACKAGE_MANAGEMENT_URPMI
+ | PACKAGE_MANAGEMENT_XBPS
+ | PACKAGE_MANAGEMENT_YUM
+ | PACKAGE_MANAGEMENT_ZYPPER
+and version = int * int
+and fstab_entry = Mountable.t * string (* mountable, mountpoint *)
+and drive_mapping = string * string (* drive name, device *)
+
+let rec string_of_fs { fs_location = location; role = role } =
+ sprintf "fs: %s role: %s\n"
+ (string_of_location location)
+ (match role with
+ | RoleRoot data -> "root\n" ^ string_of_inspection_data data
+ | RoleUsr data -> "usr\n" ^ string_of_inspection_data data
+ | RoleSwap -> "swap"
+ | RoleOther -> "other")
+
+and string_of_location { mountable = mountable; vfs_type = vfs_type } =
+ sprintf "%s (%s)" (Mountable.to_string mountable) vfs_type
+
+and string_of_root { root_location = location;
+ inspection_data = inspection_data } =
+ sprintf "%s:\n%s"
+ (string_of_location location)
+ (string_of_inspection_data inspection_data)
+
+and string_of_inspection_data data =
+ let b = Buffer.create 1024 in
+ let bpf fs = bprintf b fs in
+ may (fun v -> bpf " type: %s\n" (string_of_os_type v))
+ data.os_type;
+ may (fun v -> bpf " distro: %s\n" (string_of_distro v))
+ data.distro;
+ may (fun v -> bpf " package_format: %s\n" (string_of_package_format
v))
+ data.package_format;
+ may (fun v -> bpf " package_management: %s\n"
(string_of_package_management v))
+ data.package_management;
+ may (fun v -> bpf " product_name: %s\n" v)
+ data.product_name;
+ may (fun v -> bpf " product_variant: %s\n" v)
+ data.product_variant;
+ may (fun (major, minor) -> bpf " version: %d.%d\n" major minor)
+ data.version;
+ may (fun v -> bpf " arch: %s\n" v)
+ data.arch;
+ may (fun v -> bpf " hostname: %s\n" v)
+ data.hostname;
+ if data.fstab <> [] then (
+ let v = List.map (
+ fun (a, b) -> sprintf "(%s, %s)" (Mountable.to_string a) b
+ ) data.fstab in
+ bpf " fstab: [%s]\n" (String.concat ", " v)
+ );
+ may (fun v -> bpf " windows_systemroot: %s\n" v)
+ data.windows_systemroot;
+ may (fun v -> bpf " windows_software_hive: %s\n" v)
+ data.windows_software_hive;
+ may (fun v -> bpf " windows_system_hive: %s\n" v)
+ data.windows_system_hive;
+ may (fun v -> bpf " windows_current_control_set: %s\n" v)
+ data.windows_current_control_set;
+ if data.drive_mappings <> [] then (
+ let v =
+ List.map (fun (a, b) -> sprintf "(%s, %s)" a b) data.drive_mappings
in
+ bpf " drive_mappings: [%s]\n" (String.concat ", " v)
+ );
+ Buffer.contents b
+
+and string_of_os_type = function
+ | OS_TYPE_DOS -> "dos"
+ | OS_TYPE_FREEBSD -> "freebsd"
+ | OS_TYPE_HURD -> "hurd"
+ | OS_TYPE_LINUX -> "linux"
+ | OS_TYPE_MINIX -> "minix"
+ | OS_TYPE_NETBSD -> "netbsd"
+ | OS_TYPE_OPENBSD -> "openbsd"
+ | OS_TYPE_WINDOWS -> "windows"
+
+and string_of_distro = function
+ | DISTRO_ALPINE_LINUX -> "alpinelinux"
+ | DISTRO_ALTLINUX -> "altlinux"
+ | DISTRO_ARCHLINUX -> "archlinux"
+ | DISTRO_BUILDROOT -> "buildroot"
+ | DISTRO_CENTOS -> "centos"
+ | DISTRO_CIRROS -> "cirros"
+ | DISTRO_COREOS -> "coreos"
+ | DISTRO_DEBIAN -> "debian"
+ | DISTRO_FEDORA -> "fedora"
+ | DISTRO_FREEBSD -> "freebsd"
+ | DISTRO_FREEDOS -> "freedos"
+ | DISTRO_FRUGALWARE -> "frugalware"
+ | DISTRO_GENTOO -> "gentoo"
+ | DISTRO_LINUX_MINT -> "linuxmint"
+ | DISTRO_MAGEIA -> "mageia"
+ | DISTRO_MANDRIVA -> "mandriva"
+ | DISTRO_MEEGO -> "meego"
+ | DISTRO_NETBSD -> "netbsd"
+ | DISTRO_OPENBSD -> "openbsd"
+ | DISTRO_OPENSUSE -> "opensuse"
+ | DISTRO_ORACLE_LINUX -> "oraclelinux"
+ | DISTRO_PARDUS -> "pardus"
+ | DISTRO_PLD_LINUX -> "pldlinux"
+ | DISTRO_REDHAT_BASED -> "redhat-based"
+ | DISTRO_RHEL -> "rhel"
+ | DISTRO_SCIENTIFIC_LINUX -> "scientificlinux"
+ | DISTRO_SLACKWARE -> "slackware"
+ | DISTRO_SLES -> "sles"
+ | DISTRO_SUSE_BASED -> "suse-based"
+ | DISTRO_TTYLINUX -> "ttylinux"
+ | DISTRO_UBUNTU -> "ubuntu"
+ | DISTRO_VOID_LINUX -> "voidlinux"
+ | DISTRO_WINDOWS -> "windows"
+
+and string_of_package_format = function
+ | PACKAGE_FORMAT_APK -> "apk"
+ | PACKAGE_FORMAT_DEB -> "deb"
+ | PACKAGE_FORMAT_EBUILD -> "ebuild"
+ | PACKAGE_FORMAT_PACMAN -> "pacman"
+ | PACKAGE_FORMAT_PISI -> "pisi"
+ | PACKAGE_FORMAT_PKGSRC -> "pkgsrc"
+ | PACKAGE_FORMAT_RPM -> "rpm"
+ | PACKAGE_FORMAT_XBPS -> "xbps"
+
+and string_of_package_management = function
+ | PACKAGE_MANAGEMENT_APK -> "apk"
+ | PACKAGE_MANAGEMENT_APT -> "apt"
+ | PACKAGE_MANAGEMENT_DNF -> "dnf"
+ | PACKAGE_MANAGEMENT_PACMAN -> "pacman"
+ | PACKAGE_MANAGEMENT_PISI -> "pisi"
+ | PACKAGE_MANAGEMENT_PORTAGE -> "portage"
+ | PACKAGE_MANAGEMENT_UP2DATE -> "up2date"
+ | PACKAGE_MANAGEMENT_URPMI -> "urpmi"
+ | PACKAGE_MANAGEMENT_XBPS -> "xbps"
+ | PACKAGE_MANAGEMENT_YUM -> "yum"
+ | PACKAGE_MANAGEMENT_ZYPPER -> "zypper"
+
+let null_inspection_data = {
+ os_type = None;
+ distro = None;
+ package_format = None;
+ package_management = None;
+ product_name = None;
+ product_variant = None;
+ version = None;
+ arch = None;
+ hostname = None;
+ fstab = [];
+ windows_systemroot = None;
+ windows_software_hive = None;
+ windows_system_hive = None;
+ windows_current_control_set = None;
+ drive_mappings = [];
+}
+let null_inspection_data () = { null_inspection_data with os_type = None }
+
+let merge_inspection_data child parent =
+ let merge child parent = if parent = None then child else parent in
+
+ parent.os_type <- merge child.os_type parent.os_type;
+ parent.distro <- merge child.distro parent.distro;
+ parent.package_format <- merge child.package_format parent.package_format;
+ parent.package_management <-
+ merge child.package_management parent.package_management;
+ parent.product_name <- merge child.product_name parent.product_name;
+ parent.product_variant <- merge child.product_variant parent.product_variant;
+ parent.version <- merge child.version parent.version;
+ parent.arch <- merge child.arch parent.arch;
+ parent.hostname <- merge child.hostname parent.hostname;
+ parent.fstab <- child.fstab @ parent.fstab;
+ parent.windows_systemroot <-
+ merge child.windows_systemroot parent.windows_systemroot;
+ parent.windows_software_hive <-
+ merge child.windows_software_hive parent.windows_software_hive;
+ parent.windows_system_hive <-
+ merge child.windows_system_hive parent.windows_system_hive;
+ parent.windows_current_control_set <-
+ merge child.windows_current_control_set parent.windows_current_control_set;
+
+ (* This is what the old C code did, but I doubt that it's correct. *)
+ parent.drive_mappings <- child.drive_mappings @ parent.drive_mappings
+
+let merge child_fs parent_fs =
+ let inspection_data_of_fs = function
+ | { role = RoleRoot data }
+ | { role = RoleUsr data } -> data
+ | { role = (RoleSwap|RoleOther) } -> null_inspection_data ()
+ in
+
+ match parent_fs with
+ | { role = RoleRoot parent_data } ->
+ merge_inspection_data (inspection_data_of_fs child_fs) parent_data
+ | { role = RoleUsr parent_data } ->
+ merge_inspection_data (inspection_data_of_fs child_fs) parent_data
+ | { role = (RoleSwap|RoleOther) } ->
+ ()
+
+let inspect_fses = ref []
diff --git a/daemon/inspect_types.mli b/daemon/inspect_types.mli
new file mode 100644
index 000000000..5c2151e14
--- /dev/null
+++ b/daemon/inspect_types.mli
@@ -0,0 +1,182 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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.
+ *)
+
+type fs = {
+ fs_location : location;
+ role : role; (** Special cases: root filesystem or /usr *)
+}
+(** A single filesystem. *)
+
+and root = {
+ root_location : location;
+ inspection_data : inspection_data;
+}
+(** A root (as in "inspect_get_roots"). *)
+
+and location = {
+ mountable : Mountable.t; (** The device name or other mountable object.*)
+ vfs_type : string; (** Returned from [vfs_type] API. *)
+}
+
+and role =
+ | RoleRoot of inspection_data
+ | RoleUsr of inspection_data
+ | RoleSwap
+ | RoleOther
+(** During inspection, single filesystems are assigned a role which is
+ one of root, /usr, swap or other. *)
+
+and inspection_data = {
+ mutable os_type : os_type option;
+ mutable distro : distro option;
+ mutable package_format : package_format option;
+ mutable package_management : package_management option;
+ mutable product_name : string option;
+ mutable product_variant : string option;
+ mutable version : version option;
+ mutable arch : string option;
+ mutable hostname : string option;
+ mutable fstab : fstab_entry list;
+ mutable windows_systemroot : string option;
+ mutable windows_software_hive : string option;
+ mutable windows_system_hive : string option;
+ mutable windows_current_control_set : string option;
+ mutable drive_mappings : drive_mapping list;
+}
+(** During inspection, this data is collected incrementally for each
+ filesystem. At the end of inspection, inspection data is merged
+ into the root. *)
+
+and os_type =
+ | OS_TYPE_DOS
+ | OS_TYPE_FREEBSD
+ | OS_TYPE_HURD
+ | OS_TYPE_LINUX
+ | OS_TYPE_MINIX
+ | OS_TYPE_NETBSD
+ | OS_TYPE_OPENBSD
+ | OS_TYPE_WINDOWS
+and distro =
+ | DISTRO_ALPINE_LINUX
+ | DISTRO_ALTLINUX
+ | DISTRO_ARCHLINUX
+ | DISTRO_BUILDROOT
+ | DISTRO_CENTOS
+ | DISTRO_CIRROS
+ | DISTRO_COREOS
+ | DISTRO_DEBIAN
+ | DISTRO_FEDORA
+ | DISTRO_FREEBSD
+ | DISTRO_FREEDOS
+ | DISTRO_FRUGALWARE
+ | DISTRO_GENTOO
+ | DISTRO_LINUX_MINT
+ | DISTRO_MAGEIA
+ | DISTRO_MANDRIVA
+ | DISTRO_MEEGO
+ | DISTRO_NETBSD
+ | DISTRO_OPENBSD
+ | DISTRO_OPENSUSE
+ | DISTRO_ORACLE_LINUX
+ | DISTRO_PARDUS
+ | DISTRO_PLD_LINUX
+ | DISTRO_REDHAT_BASED
+ | DISTRO_RHEL
+ | DISTRO_SCIENTIFIC_LINUX
+ | DISTRO_SLACKWARE
+ | DISTRO_SLES
+ | DISTRO_SUSE_BASED
+ | DISTRO_TTYLINUX
+ | DISTRO_UBUNTU
+ | DISTRO_VOID_LINUX
+ | DISTRO_WINDOWS
+and package_format =
+ | PACKAGE_FORMAT_APK
+ | PACKAGE_FORMAT_DEB
+ | PACKAGE_FORMAT_EBUILD
+ | PACKAGE_FORMAT_PACMAN
+ | PACKAGE_FORMAT_PISI
+ | PACKAGE_FORMAT_PKGSRC
+ | PACKAGE_FORMAT_RPM
+ | PACKAGE_FORMAT_XBPS
+and package_management =
+ | PACKAGE_MANAGEMENT_APK
+ | PACKAGE_MANAGEMENT_APT
+ | PACKAGE_MANAGEMENT_DNF
+ | PACKAGE_MANAGEMENT_PACMAN
+ | PACKAGE_MANAGEMENT_PISI
+ | PACKAGE_MANAGEMENT_PORTAGE
+ | PACKAGE_MANAGEMENT_UP2DATE
+ | PACKAGE_MANAGEMENT_URPMI
+ | PACKAGE_MANAGEMENT_XBPS
+ | PACKAGE_MANAGEMENT_YUM
+ | PACKAGE_MANAGEMENT_ZYPPER
+and version = int * int
+and fstab_entry = Mountable.t * string (* mountable, mountpoint *)
+and drive_mapping = string * string (* drive name, device *)
+
+val merge_inspection_data : inspection_data -> inspection_data -> unit
+(** [merge_inspection_data child parent] merges two sets of inspection
+ data into the parent. The parent inspection data fields, if
+ present, take precedence over the child inspection data fields.
+
+ It's intended that you merge upwards, ie.
+ [merge_inspection_data usr root] *)
+
+val merge : fs -> fs -> unit
+(** [merge child_fs parent_fs] merges two filesystems,
+ using [merge_inspection_data] to merge the inspection data of
+ the child into the parent. (Nothing else is merged, only
+ the inspection data). *)
+
+val string_of_fs : fs -> string
+(** Convert [fs] into a multi-line string, for debugging only. *)
+
+val string_of_root : root -> string
+(** Convert [root] into a multi-line string, for debugging only. *)
+
+val string_of_location : location -> string
+(** Convert [location] into a string, for debugging only. *)
+
+val string_of_inspection_data : inspection_data -> string
+(** Convert [inspection_data] into a multi-line string, for debugging only. *)
+
+val string_of_os_type : os_type -> string
+(** Convert [os_type] to a string.
+ The string is part of the public API. *)
+
+val string_of_distro : distro -> string
+(** Convert [distro] to a string.
+ The string is part of the public API. *)
+
+val string_of_package_format : package_format -> string
+(** Convert [package_format] to a string.
+ The string is part of the public API. *)
+
+val string_of_package_management : package_management -> string
+(** Convert [package_management] to a string.
+ The string is part of the public API. *)
+
+val null_inspection_data : unit -> inspection_data
+(** {!inspection_data} structure with all fields set to [None].
+ This is a function: since we mutate this structure, we want
+ a fresh structure each time (so we're not mutating a common copy). *)
+
+val inspect_fses : fs list ref
+(** The global list of filesystems found by the previous call to
+ inspect_os. *)
diff --git a/daemon/inspect_utils.ml b/daemon/inspect_utils.ml
new file mode 100644
index 000000000..ce9c7b441
--- /dev/null
+++ b/daemon/inspect_utils.ml
@@ -0,0 +1,187 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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 Unix
+open Printf
+
+open Std_utils
+
+open Utils
+open Inspect_types
+
+let max_augeas_file_size = 100 * 1000
+
+let rec with_augeas ?name configfiles f =
+ let name =
+ match name with
+ | None -> sprintf "with_augeas: %s" (String.concat " "
configfiles)
+ | Some name -> name in
+ let chroot = Chroot.create ~name () in
+
+ (* Security:
+ *
+ * The old C code had a few problems: It ignored non-regular-file
+ * objects (eg. devices), passing them to Augeas, so relying on
+ * Augeas to do the right thing. Also too-large regular files
+ * caused the whole inspection operation to fail.
+ *
+ * I have tried to improve this so that non-regular files and
+ * too large files are ignored (dropped from the configfiles list),
+ * so that Augeas won't touch them, but they also won't stop
+ * inspection.
+ *)
+ let safe_file file =
+ Is.is_file ~followsymlinks:true file && (
+ let size = (Chroot.f chroot Unix.stat file).Unix.st_size in
+ size <= max_augeas_file_size
+ )
+ in
+ let configfiles = List.filter safe_file configfiles in
+
+ let aug =
+ Augeas.create (Sysroot.sysroot ()) None
+ [Augeas.AugSaveNoop; Augeas.AugNoLoad] in
+
+ protect
+ ~f:(fun () ->
+ (* Tell Augeas to only load configfiles and no other files. This
+ * prevents a rogue guest from performing a denial of service attack
+ * by having large, over-complicated configuration files which are
+ * unrelated to the task at hand. (Thanks Dominic Cleal).
+ * Note this requires Augeas >= 1.0.0 because of RHBZ#975412.
+ *)
+ let pathexpr = make_augeas_path_expression configfiles in
+ ignore (aug_rm_noerrors aug pathexpr);
+ Augeas.load aug;
+
+ (* Check that augeas did not get a parse error for any of the
+ * configfiles, otherwise we are silently missing information.
+ *)
+ let matches = aug_matches_noerrors aug "/augeas/files//error" in
+ List.iter (
+ fun match_ ->
+ List.iter (
+ fun file ->
+ let errorpath = sprintf "/augeas/files%s/error" file in
+ if match_ = errorpath then (
+ (* There's been an error - get the error details. *)
+ let get path =
+ match aug_get_noerrors aug (errorpath ^ path) with
+ | None -> "<missing>"
+ | Some v -> v
+ in
+ let message = get "message" in
+ let line = get "line" in
+ let charp = get "char" in
+ failwithf "%s:%s:%s: augeas parse failure: %s"
+ file line charp message
+ )
+ ) configfiles
+ ) matches;
+
+ f aug
+ )
+ ~finally:(
+ fun () -> Augeas.close aug
+ )
+
+(* Explained here:
https://bugzilla.redhat.com/show_bug.cgi?id=975412#c0 *)
+and make_augeas_path_expression files =
+ let subexprs =
+ List.map (
+ fun file ->
+ (* v NB trailing '/' after filename *)
+ sprintf "\"%s/\" !~ regexp('^') + glob(incl) +
regexp('/.*')" file
+ ) files in
+ let subexprs = String.concat " and " subexprs in
+
+ let ret = sprintf "/augeas/load/*[ %s ]" subexprs in
+ if verbose () then
+ eprintf "augeas pathexpr = %s\n%!" ret;
+
+ ret
+
+and aug_get_noerrors aug path =
+ try Augeas.get aug path
+ with Augeas.Error _ -> None
+
+and aug_matches_noerrors aug path =
+ try Augeas.matches aug path
+ with Augeas.Error _ -> []
+
+and aug_rm_noerrors aug path =
+ try Augeas.rm aug path
+ with Augeas.Error _ -> 0
+
+let is_file_nocase path =
+ let path =
+ try Some (Realpath.case_sensitive_path path)
+ with _ -> None in
+ match path with
+ | None -> false
+ | Some path -> Is.is_file path
+
+and is_dir_nocase path =
+ let path =
+ try Some (Realpath.case_sensitive_path path)
+ with _ -> None in
+ match path with
+ | None -> false
+ | Some path -> Is.is_dir path
+
+(* Rather hairy test for "is a partition", taken directly from
+ * the old C inspection code. XXX fix function and callers
+ *)
+let is_partition partition =
+ try
+ let device = Devsparts.part_to_dev partition in
+ ignore (Devsparts.device_index device);
+ true
+ with _ -> false
+
+let re_major_minor = PCRE.compile "(\\d+)\\.(\\d+)"
+let re_major_no_minor = PCRE.compile "(\\d+)"
+
+let parse_version_from_major_minor str data =
+ if verbose () then
+ eprintf "parse_version_from_major_minor: parsing '%s'\n%!" str;
+
+ if PCRE.matches re_major_minor str ||
+ PCRE.matches re_major_no_minor str then (
+ let major =
+ try Some (int_of_string (PCRE.sub 1))
+ with Not_found | Invalid_argument _ | Failure _ -> None in
+ let minor =
+ try Some (int_of_string (PCRE.sub 2))
+ with Not_found | Invalid_argument _ | Failure _ -> None in
+ match major, minor with
+ | None, None
+ | None, Some _ -> ()
+ | Some major, None -> data.version <- Some (major, 0)
+ | Some major, Some minor -> data.version <- Some (major, minor)
+ )
+ else (
+ eprintf "parse_version_from_major_minor: cannot parse version from
'%s'\n"
+ str
+ )
+
+let with_hive hive_filename f =
+ let flags = [ Hivex.OPEN_UNSAFE ] in
+ let flags = if verbose () then Hivex.OPEN_VERBOSE :: flags else flags in
+ let h = Hivex.open_file hive_filename flags in
+ protect ~f:(fun () -> f h (Hivex.root h)) ~finally:(fun () -> Hivex.close h)
diff --git a/daemon/inspect_utils.mli b/daemon/inspect_utils.mli
new file mode 100644
index 000000000..93e4c4b33
--- /dev/null
+++ b/daemon/inspect_utils.mli
@@ -0,0 +1,53 @@
+(* guestfs-inspection
+ * Copyright (C) 2009-2017 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.
+ *)
+
+val with_augeas : ?name:string -> string list -> (Augeas.t -> 'a) ->
'a
+(** Open an Augeas handle, parse only 'configfiles' (these
+ files must exist), and then call 'f' with the Augeas handle.
+
+ As a security measure, this bails if any file is too large for
+ a reasonable configuration file. After the call to 'f' the
+ Augeas handle is closed. *)
+
+val aug_get_noerrors : Augeas.t -> string -> string option
+val aug_matches_noerrors : Augeas.t -> string -> string list
+val aug_rm_noerrors : Augeas.t -> string -> int
+(** When inspecting a guest, we don't particularly care if Augeas
+ calls fail. These functions wrap {!Augeas.get}, {!Augeas.matches}
+ and {!Augeas.rm} returning null content if there is an error. *)
+
+val is_file_nocase : string -> bool
+val is_dir_nocase : string -> bool
+(** With a filesystem mounted under sysroot, check if [path] is
+ a file or directory under that sysroot. The [path] is
+ checked case-insensitively. *)
+
+val is_partition : string -> bool
+(** Return true if the device is a partition. *)
+
+val parse_version_from_major_minor : string -> Inspect_types.inspection_data ->
unit
+(** Make a best effort attempt to parse either X or X.Y from a string,
+ usually the product_name string. *)
+
+val with_hive : string -> (Hivex.t -> Hivex.node -> 'a) -> 'a
+(** Open a Windows registry "hive", and call the function on the
+ handle and root node.
+
+ After the call to the function, the hive is always closed.
+
+ The hive is opened readonly. *)
--
2.13.2