From 9292a4637e8f4d534f4dde70e8e5451f61ad0162 Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Tue, 19 Jan 2021 14:22:33 +0000 Subject: [PATCH] Update common/ submodule to latest upstream. Only for RHEL AV 8.4.0, allowing this branch to be compiled from git with libguestfs 1.44. --- common | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) Submodule common 9338df5e...be09523d: diff --git a/common/mlcustomize/SELinux_relabel.ml b/common/mlcustomize/SELinux_relabel.ml index 44995df6..5ecf7bd7 100644 --- a/common/mlcustomize/SELinux_relabel.ml +++ b/common/mlcustomize/SELinux_relabel.ml @@ -28,65 +28,80 @@ module G = Guestfs let array_find a l = List.mem a (Array.to_list l) -let relabel (g : G.guestfs) = - (* Is the guest using SELinux? *) - if g#is_file ~followsymlinks:true "/usr/sbin/load_policy" && - g#is_file ~followsymlinks:true "/etc/selinux/config" then ( - (* Is setfiles / SELinux relabelling functionality available? *) - if g#feature_available [| "selinuxrelabel" |] then ( - (* Use Augeas to parse /etc/selinux/config. *) - g#aug_init "/" (16+32) (* AUG_SAVE_NOOP | AUG_NO_LOAD *); - (* See: https://bugzilla.redhat.com/show_bug.cgi?id=975412#c0 *) - ignore (g#aug_rm "/augeas/load/*[\"/etc/selinux/config/\" !~ regexp('^') + glob(incl) + regexp('/.*')]"); - g#aug_load (); - debug_augeas_errors g; - - (* Get the SELinux policy name, eg. "targeted", "minimum". - * Use "targeted" if not specified, just like libselinux does. - *) - let policy = - let config_path = "/files/etc/selinux/config" in - let selinuxtype_path = config_path ^ "/SELINUXTYPE" in - let keys = g#aug_ls config_path in - if array_find selinuxtype_path keys then - g#aug_get selinuxtype_path - else - "targeted" in - - g#aug_close (); - - (* Get the spec file name. *) - let specfile = - sprintf "/etc/selinux/%s/contexts/files/file_contexts" policy in - - (* RHEL 6.2 - 6.5 had a malformed specfile that contained the - * invalid regular expression "/var/run/spice-vdagentd.\pid" - * (instead of "\.p"). This stops setfiles from working on - * the guest. - * - * Because an SELinux relabel writes all over the filesystem, - * it seems reasonable to fix this problem in the specfile - * at the same time. (RHBZ#1374232) - *) - if g#grep ~fixed:true "vdagentd.\\pid" specfile <> [||] then ( - debug "fixing invalid regular expression in %s" specfile; - let old_specfile = specfile ^ "~" in - g#mv specfile old_specfile; - let content = g#read_file old_specfile in - let content = - String.replace content "vdagentd.\\pid" "vdagentd\\.pid" in - g#write specfile content; - g#copy_attributes ~all:true old_specfile specfile - ); - - (* Relabel everything. *) - g#selinux_relabel ~force:true specfile "/"; - - (* If that worked, we don't need to autorelabel. *) +let rec relabel (g : G.guestfs) = + (* Is the guest using SELinux? (Otherwise this is a no-op). *) + if is_selinux_guest g then ( + try + use_setfiles g; + (* That worked, so we don't need to autorelabel. *) g#rm_f "/.autorelabel" - ) - else ( - (* SELinux guest, but not SELinux host. Fallback to this. *) + with Failure _ -> + (* This is the fallback in case something in the setfiles + * method didn't work. That includes the case where a non-SELinux + * host is processing an SELinux guest, and other things. + *) g#touch "/.autorelabel" - ) ) + +and is_selinux_guest g = + g#is_file ~followsymlinks:true "/usr/sbin/load_policy" && + g#is_file ~followsymlinks:true "/etc/selinux/config" + +and use_setfiles g = + (* Is setfiles / SELinux relabelling functionality available? *) + if not (g#feature_available [| "selinuxrelabel" |]) then + failwith "no selinux relabel feature"; + + (* Use Augeas to parse /etc/selinux/config. *) + g#aug_init "/" (16+32) (* AUG_SAVE_NOOP | AUG_NO_LOAD *); + (* See: https://bugzilla.redhat.com/show_bug.cgi?id=975412#c0 *) + ignore (g#aug_rm "/augeas/load/*[\"/etc/selinux/config/\" !~ regexp('^') + glob(incl) + regexp('/.*')]"); + g#aug_load (); + debug_augeas_errors g; + + (* Get the SELinux policy name, eg. "targeted", "minimum". + * Use "targeted" if not specified, just like libselinux does. + *) + let policy = + let config_path = "/files/etc/selinux/config" in + let selinuxtype_path = config_path ^ "/SELINUXTYPE" in + let keys = g#aug_ls config_path in + if array_find selinuxtype_path keys then + g#aug_get selinuxtype_path + else + "targeted" in + + g#aug_close (); + + (* Get the spec file name. *) + let specfile = + sprintf "/etc/selinux/%s/contexts/files/file_contexts" policy in + + (* If the spec file doesn't exist then fall back to using + * autorelabel (RHBZ#1828952). + *) + if not (g#is_file ~followsymlinks:true specfile) then + failwith "no spec file"; + + (* RHEL 6.2 - 6.5 had a malformed specfile that contained the + * invalid regular expression "/var/run/spice-vdagentd.\pid" + * (instead of "\.p"). This stops setfiles from working on + * the guest. + * + * Because an SELinux relabel writes all over the filesystem, + * it seems reasonable to fix this problem in the specfile + * at the same time. (RHBZ#1374232) + *) + if g#grep ~fixed:true "vdagentd.\\pid" specfile <> [||] then ( + debug "fixing invalid regular expression in %s" specfile; + let old_specfile = specfile ^ "~" in + g#mv specfile old_specfile; + let content = g#read_file old_specfile in + let content = + String.replace content "vdagentd.\\pid" "vdagentd\\.pid" in + g#write specfile content; + g#copy_attributes ~all:true old_specfile specfile + ); + + (* Relabel everything. *) + g#selinux_relabel ~force:true specfile "/" diff --git a/common/mltools/Makefile.am b/common/mltools/Makefile.am index 3b4172db..aea2dce9 100644 --- a/common/mltools/Makefile.am +++ b/common/mltools/Makefile.am @@ -95,6 +95,7 @@ libmltools_a_CPPFLAGS = \ -I$(shell $(OCAMLC) -where) \ -I$(top_srcdir)/common/utils \ -I$(top_srcdir)/lib \ + $(INCLUDE_DIRECTORY) \ -I$(top_srcdir)/common/options \ -I$(top_srcdir)/common/mlgettext \ -I$(top_srcdir)/common/mlpcre \ diff --git a/common/mltools/tools_utils.ml b/common/mltools/tools_utils.ml index 12718022..d54ec581 100644 --- a/common/mltools/tools_utils.ml +++ b/common/mltools/tools_utils.ml @@ -679,3 +679,53 @@ let with_timeout op timeout ?(sleep = 2) fn = loop () in loop () + +let run_in_guest_command g root ?logfile ?incompatible_fn cmd = + (* Is the host_cpu compatible with the guest arch? ie. Can we + * run commands in this guest? + *) + let guest_arch = g#inspect_get_arch root in + let guest_arch_compatible = guest_arch_compatible guest_arch in + if not guest_arch_compatible then ( + match incompatible_fn with + | None -> () + | Some fn -> fn () + ) + else ( + (* Add a prologue to the scripts: + * - Pass environment variables through from the host. + * - Optionally send stdout and stderr to a log file so we capture + * all output in error messages. + * - Use setarch when running x86_64 host + i686 guest. + *) + let env_vars = + List.filter_map ( + fun name -> + try Some (sprintf "export %s=%s" name (quote (Sys.getenv name))) + with Not_found -> None + ) [ "http_proxy"; "https_proxy"; "ftp_proxy"; "no_proxy" ] in + let env_vars = String.concat "\n" env_vars ^ "\n" in + + let cmd = + match Guestfs_config.host_cpu, guest_arch with + | "x86_64", ("i386"|"i486"|"i586"|"i686") -> + sprintf "setarch i686 <<\"__EOCMD\" +%s +__EOCMD +" cmd + | _ -> cmd in + + let logfile_redirect = + match logfile with + | None -> "" + | Some logfile -> sprintf "exec >>%s 2>&1" (quote logfile) in + + let cmd = sprintf "\ +%s +%s +%s +" (logfile_redirect) env_vars cmd in + + debug "running command:\n%s" cmd; + ignore (g#sh cmd) + ) diff --git a/common/mltools/tools_utils.mli b/common/mltools/tools_utils.mli index ab70f583..1d1ac8a8 100644 --- a/common/mltools/tools_utils.mli +++ b/common/mltools/tools_utils.mli @@ -195,9 +195,8 @@ val is_btrfs_subvolume : Guestfs.guestfs -> string -> bool (** Checks if a filesystem is a btrfs subvolume. *) val inspect_decrypt : Guestfs.guestfs -> key_store -> unit -(** Simple implementation of decryption: look for any [crypto_LUKS] - partitions and decrypt them, then rescan for VGs. This only works - for Fedora whole-disk encryption. *) +(** Simple implementation of decryption: look for any encrypted + partitions and decrypt them, then rescan for VGs. *) val with_timeout : string -> int -> ?sleep:int -> (unit -> 'a option) -> 'a (** [with_timeout op timeout ?sleep fn] implements a timeout loop. @@ -212,3 +211,13 @@ val with_timeout : string -> int -> ?sleep:int -> (unit -> 'a option) -> 'a calls {!error} and the program exits. The error message will contain the diagnostic string [op] to identify the operation which timed out. *) + +val run_in_guest_command : Guestfs.guestfs -> string -> ?logfile:string -> ?incompatible_fn:(unit -> unit) -> string -> unit +(** [run_in_guest_command g root ?incompatible_archs_fn cmd] + runs a command in the guest, which is already mounted for the + specified [root]. The command is run directly in case the + architecture of the host and the guest are compatible, optionally + calling [?incompatible_fn] in case they are not. + + [?logfile] is an optional file in the guest to where redirect + stdout and stderr of the command. *) diff --git a/common/mlutils/unix_utils-c.c b/common/mlutils/unix_utils-c.c index 33099611..8acf0395 100644 --- a/common/mlutils/unix_utils-c.c +++ b/common/mlutils/unix_utils-c.c @@ -77,6 +77,7 @@ extern value guestfs_int_mllib_mkdtemp (value val_pattern); extern value guestfs_int_mllib_realpath (value pathv); extern value guestfs_int_mllib_statvfs_statvfs (value pathv); extern value guestfs_int_mllib_statvfs_is_network_filesystem (value pathv); +extern value guestfs_int_mllib_sysconf_nr_processors_online (value unitv); /* NB: This is a "noalloc" call. */ value @@ -368,3 +369,17 @@ guestfs_int_mllib_statvfs_is_network_filesystem (value pathv) return Val_bool (0); #endif } + +/* NB: This is a "noalloc" call. */ +value +guestfs_int_mllib_sysconf_nr_processors_online (value unitv) +{ +#ifdef _SC_NPROCESSORS_ONLN + long n; + + n = sysconf (_SC_NPROCESSORS_ONLN); + if (n > 0) return Val_int (n); +#endif + /* Return a safe value so that callers don't need to deal with errors. */ + return Val_int (1); +} diff --git a/common/mlutils/unix_utils.ml b/common/mlutils/unix_utils.ml index 52eb824d..2bdda12a 100644 --- a/common/mlutils/unix_utils.ml +++ b/common/mlutils/unix_utils.ml @@ -84,3 +84,8 @@ module StatVFS = struct external is_network_filesystem : string -> bool = "guestfs_int_mllib_statvfs_is_network_filesystem" "noalloc" end + +module Sysconf = struct + external nr_processors_online : unit -> int = + "guestfs_int_mllib_sysconf_nr_processors_online" "noalloc" +end diff --git a/common/mlutils/unix_utils.mli b/common/mlutils/unix_utils.mli index 4fcea4a3..aead4df2 100644 --- a/common/mlutils/unix_utils.mli +++ b/common/mlutils/unix_utils.mli @@ -121,3 +121,12 @@ module StatVFS : sig (** [is_network_filesystem path] returns true if [path] is located on a network filesystem such as NFS or CIFS. *) end + +module Sysconf : sig + val nr_processors_online : unit -> int + (** [nr_processors_online ()] returns the number of processors + currently online, from [sysconf (_SC_NPROCESSORS_ONLN)]. + + Note this never fails. In case we cannot get the number of + cores it returns 1. *) +end diff --git a/common/options/Makefile.am b/common/options/Makefile.am index f7ea7493..162d143b 100644 --- a/common/options/Makefile.am +++ b/common/options/Makefile.am @@ -41,8 +41,9 @@ liboptions_la_SOURCES = \ liboptions_la_CPPFLAGS = \ -DGUESTFS_NO_DEPRECATED=1 \ -I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \ + -I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib \ -I$(top_srcdir)/lib -I$(top_builddir)/lib \ - -I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib + $(INCLUDE_DIRECTORY) liboptions_la_CFLAGS = \ $(WARN_CFLAGS) $(WERROR_CFLAGS) \ $(LIBCONFIG_CFLAGS) \ diff --git a/common/options/decrypt.c b/common/options/decrypt.c index 683cf5ed..434b7d58 100644 --- a/common/options/decrypt.c +++ b/common/options/decrypt.c @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -38,18 +39,18 @@ /** * Make a LUKS map name from the partition name, - * eg. C<"/dev/vda2" =E "luksvda2"> + * eg. C<"/dev/vda2" =E "cryptvda2"> */ static void make_mapname (const char *device, char *mapname, size_t len) { size_t i = 0; - if (len < 5) + if (len < 6) abort (); - strcpy (mapname, "luks"); - mapname += 4; - len -= 4; + strcpy (mapname, "crypt"); + mapname += 5; + len -= 5; if (STRPREFIX (device, "/dev/")) i = 5; @@ -65,10 +66,8 @@ make_mapname (const char *device, char *mapname, size_t len) } /** - * Simple implementation of decryption: look for any C - * partitions and decrypt them, then rescan for VGs. This only works - * for Fedora whole-disk encryption. WIP to make this work for other - * encryption schemes. + * Simple implementation of decryption: look for any encrypted + * partitions and decrypt them, then rescan for VGs. */ void inspect_do_decrypt (guestfs_h *g, struct key_store *ks) @@ -82,12 +81,21 @@ inspect_do_decrypt (guestfs_h *g, struct key_store *ks) for (i = 0; partitions[i] != NULL; ++i) { CLEANUP_FREE char *type = guestfs_vfs_type (g, partitions[i]); - if (type && STREQ (type, "crypto_LUKS")) { + if (type && + (STREQ (type, "crypto_LUKS") || STREQ (type, "BitLocker"))) { + bool is_bitlocker = STREQ (type, "BitLocker"); char mapname[32]; make_mapname (partitions[i], mapname, sizeof mapname); #ifdef GUESTFS_HAVE_LUKS_UUID - CLEANUP_FREE char *uuid = guestfs_luks_uuid (g, partitions[i]); + CLEANUP_FREE char *uuid = NULL; + + /* This fails for Windows BitLocker disks because cryptsetup + * luksUUID cannot read a UUID (unclear if this is a limitation + * of the format or cryptsetup). + */ + if (!is_bitlocker) + uuid = guestfs_luks_uuid (g, partitions[i]); #else const char *uuid = NULL; #endif @@ -97,11 +105,15 @@ inspect_do_decrypt (guestfs_h *g, struct key_store *ks) /* Try each key in turn. */ for (j = 0; keys[j] != NULL; ++j) { - /* XXX Should we call guestfs_luks_open_ro if readonly flag + /* XXX Should we set GUESTFS_CRYPTSETUP_OPEN_READONLY if readonly * is set? This might break 'mount_ro'. */ guestfs_push_error_handler (g, NULL, NULL); +#ifdef GUESTFS_HAVE_CRYPTSETUP_OPEN + r = guestfs_cryptsetup_open (g, partitions[i], keys[j], mapname, -1); +#else r = guestfs_luks_open (g, partitions[i], keys[j], mapname); +#endif guestfs_pop_error_handler (g); if (r == 0) goto opened; diff --git a/common/options/uri.c b/common/options/uri.c index ac36bccb..6b696fc2 100644 --- a/common/options/uri.c +++ b/common/options/uri.c @@ -194,6 +194,7 @@ parse (const char *arg, char **path_ret, char **protocol_ret, if (path && path[0] == '/' && (STREQ (uri->scheme, "gluster") || STREQ (uri->scheme, "iscsi") || + STREQ (uri->scheme, "nbd") || STREQ (uri->scheme, "rbd") || STREQ (uri->scheme, "sheepdog"))) path++; diff --git a/common/utils/guestfs-stringlists-utils.h b/common/utils/guestfs-stringlists-utils.h index 0bac1587..ade3b6f3 100644 --- a/common/utils/guestfs-stringlists-utils.h +++ b/common/utils/guestfs-stringlists-utils.h @@ -21,7 +21,8 @@ /* stringlists-utils.c */ extern void guestfs_int_free_string_list (char **); -extern size_t guestfs_int_count_strings (char *const *); +extern size_t guestfs_int_count_strings (char *const *) + __attribute__((__nonnull__ (1))); extern char *guestfs_int_concat_strings (char *const *); extern char **guestfs_int_copy_string_list (char *const *); extern char *guestfs_int_join_strings (const char *sep, char *const *);