mirror of
https://github.com/libguestfs/libguestfs.git
synced 2026-03-22 07:03:38 +00:00
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.
183 lines
5.8 KiB
OCaml
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)
|