Files
libguestfs/daemon/inspect_utils.ml
Richard W.M. Jones 3cea2cfe04 lib: Move guestfs_device_index impl from daemon to library.
This function doesn't work reliably with the proposed change to device
name translation.  The reason is that strings returned by
Devsparts.list_devices contained translated names, so their indexes
did not correspond to the untranslated names used outside the
appliance..

We can avoid this and make the function much simpler and faster by
implementing it on the library side instead.
2020-03-05 13:18:27 +00:00

183 lines
5.8 KiB
OCaml

(* guestfs-inspection
* Copyright (C) 2009-2019 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 Devsparts.part_to_dev partition <> partition 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 then (
let major = int_of_string (PCRE.sub 1) in
let minor = int_of_string (PCRE.sub 2) in
data.version <- Some (major, minor)
)
else if PCRE.matches re_major_no_minor str then (
let major = int_of_string (PCRE.sub 1) in
data.version <- Some (major, 0)
)
else (
eprintf "parse_version_from_major_minor: cannot parse version from '%s'\n"
str
)
let with_hive hive_filename f =
let flags = [] in
let flags =
match Daemon_config.hivex_flag_unsafe with
| None -> flags
| Some f -> f :: flags 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)