[Pkg-libvirt-commits] [libguestfs] 35/66: v2v: -o rhev: Write files and directories as user:group 36:36 (RHBZ#1143887).

Hilko Bengen bengen at moszumanska.debian.org
Fri Oct 3 14:47:44 UTC 2014


This is an automated email from the git hooks/post-receive script.

bengen pushed a commit to annotated tag debian/1%1.27.54-1
in repository libguestfs.

commit 0dfa96c043cee4ce82c0a45c3ad09b0a61798b79
Author: Richard W.M. Jones <rjones at redhat.com>
Date:   Mon Sep 22 21:57:57 2014 +0100

    v2v: -o rhev: Write files and directories as user:group 36:36 (RHBZ#1143887).
    
    We need to write files and directories as user:group 36:36, else
    RHEV-M cannot import the VM.  Doing this in the presence of NFS is
    difficult.  See v2v/kvmuid.mli for how it is done in this commit.
---
 po/POTFILES        |  1 +
 po/POTFILES-ml     |  1 +
 v2v/Makefile.am    |  7 +++--
 v2v/kvmuid-c.c     | 33 +++++++++++++++++++++
 v2v/kvmuid.ml      | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 v2v/kvmuid.mli     | 69 +++++++++++++++++++++++++++++++++++++++++++
 v2v/output_rhev.ml | 57 ++++++++++++++++++++++++++++++------
 7 files changed, 243 insertions(+), 11 deletions(-)

diff --git a/po/POTFILES b/po/POTFILES
index aec8c62..be2de28 100644
--- a/po/POTFILES
+++ b/po/POTFILES
@@ -337,5 +337,6 @@ src/tmpdirs.c
 src/utils.c
 test-tool/test-tool.c
 v2v/domainxml-c.c
+v2v/kvmuid-c.c
 v2v/utils-c.c
 v2v/xml-c.c
diff --git a/po/POTFILES-ml b/po/POTFILES-ml
index 76fe681..de37bf7 100644
--- a/po/POTFILES-ml
+++ b/po/POTFILES-ml
@@ -92,6 +92,7 @@ v2v/input_disk.ml
 v2v/input_libvirt.ml
 v2v/input_libvirtxml.ml
 v2v/input_ova.ml
+v2v/kvmuid.ml
 v2v/lib_esx.ml
 v2v/lib_linux.ml
 v2v/lib_ovf.ml
diff --git a/v2v/Makefile.am b/v2v/Makefile.am
index 03d7442..4b57aca 100644
--- a/v2v/Makefile.am
+++ b/v2v/Makefile.am
@@ -37,6 +37,7 @@ SOURCES_MLI = \
 	input_libvirtxml.mli \
 	input_ova.mli \
 	JSON.mli \
+	kvmuid.mli \
 	lib_esx.mli \
 	lib_linux.mli \
 	lib_ovf.mli \
@@ -60,6 +61,7 @@ SOURCES_ML = \
 	domainxml.ml \
 	DOM.ml \
 	JSON.ml \
+	kvmuid.ml \
 	lib_esx.ml \
 	lib_xen.ml \
 	lib_ovf.ml \
@@ -88,9 +90,10 @@ SOURCES_C = \
 	$(top_builddir)/mllib/mkdtemp-c.c \
 	$(top_builddir)/customize/crypt-c.c \
 	$(top_builddir)/customize/perl_edit-c.c \
+	domainxml-c.c \
+	kvmuid-c.c \
 	utils-c.c \
-	xml-c.c \
-	domainxml-c.c
+	xml-c.c
 
 if HAVE_OCAML
 
diff --git a/v2v/kvmuid-c.c b/v2v/kvmuid-c.c
new file mode 100644
index 0000000..fb41711
--- /dev/null
+++ b/v2v/kvmuid-c.c
@@ -0,0 +1,33 @@
+/* virt-v2v
+ * Copyright (C) 2009-2014 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.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <caml/mlvalues.h>
+
+extern int v2v_exit (value rv) __attribute__((noreturn));
+
+int
+v2v_exit (value rv)
+{
+  _exit (Int_val (rv));
+}
diff --git a/v2v/kvmuid.ml b/v2v/kvmuid.ml
new file mode 100644
index 0000000..93908d9
--- /dev/null
+++ b/v2v/kvmuid.ml
@@ -0,0 +1,86 @@
+(* virt-v2v
+ * Copyright (C) 2009-2014 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.
+ *)
+
+(* Functions for making files and directories as another user. *)
+
+open Unix
+open Printf
+
+open Common_gettext.Gettext
+
+open Utils
+
+type t = {
+  uid : int option;
+  gid : int option;
+}
+
+let create ?uid ?gid () = { uid = uid; gid = gid }
+
+(* Call _exit directly, ie. do not run OCaml atexit handlers. *)
+external _exit : int -> unit = "v2v_exit" "noalloc"
+
+let with_fork { uid = uid; gid = gid } f =
+  let pid = fork () in
+  if pid = 0 then ( (* child *)
+    (match gid with None -> () | Some gid -> setgid gid);
+    (match uid with None -> () | Some uid -> setuid uid);
+    (try f ()
+     with exn ->
+       eprintf "%s: KVM uid wrapper: %s\n%!" prog (Printexc.to_string exn);
+       _exit 1
+    );
+    _exit 0
+  );
+  (* parent *)
+  let _, status = waitpid [] pid in
+  match status with
+  | WEXITED 0 -> ()
+  | WEXITED i ->
+    error (f_"subprocess exited with non-zero error code %d") i
+  | WSIGNALED i | WSTOPPED i ->
+    error (f_"subprocess signalled or stopped by signal %d") i
+
+let mkdir t path perm =
+  with_fork t (fun () -> mkdir path perm)
+
+let rmdir t path =
+  with_fork t (fun () -> rmdir path)
+
+let output t path f =
+  with_fork t (
+    fun () ->
+      let chan = open_out path in
+      f chan;
+      close_out chan
+  )
+
+let make_file t path content =
+  output t path (fun chan -> output_string chan content)
+
+let unlink t path =
+  with_fork t (fun () -> unlink path)
+
+let func t f = with_fork t f
+
+let command t cmd =
+  with_fork t (
+    fun () ->
+      let r = Sys.command cmd in
+      if r <> 0 then failwith "external command failed"
+  )
diff --git a/v2v/kvmuid.mli b/v2v/kvmuid.mli
new file mode 100644
index 0000000..dfd31c2
--- /dev/null
+++ b/v2v/kvmuid.mli
@@ -0,0 +1,69 @@
+(* virt-v2v
+ * Copyright (C) 2009-2014 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.
+ *)
+
+(** Functions for making files and directories as another user.
+
+    [-o rhev] output mode has to write files as UID:GID 36:36,
+    otherwise RHEV cannot read them.  Because the files are located on
+    NFS (and hence might be root-squashed) we also cannot chown the
+    files.  We cannot setuid the whole process to 36:36 because it
+    needs to do other root things like mounting and unmounting the NFS
+    volume.
+
+    The solution to this craziness is to fork a subprocess every time
+    we need to create a file, setuid in the subprocess, and write the
+    file.  The subprocess then exits, leaving the main process still
+    running as root.
+
+    This mini-library encapsulates this tomfoolery into something that
+    is slightly more sane to use.
+
+    NB. We are {b not} dropping permissions for security reasons.
+    This file has nothing to do with security. *)
+
+type t
+(** Abstract handle. *)
+
+val create : ?uid:int -> ?gid:int -> unit -> t
+(** Create handle.  The optional [?uid] and [?gid] parameters are the
+    user/group to run as.  If omitted, then we don't change user
+    and/or group (but we still do the forking anyway). *)
+
+val mkdir : t -> string -> int -> unit
+(** [mkdir t path perm] creates the directory [path] with mode [perm]. *)
+
+val rmdir : t -> string -> unit
+(** [rmdir t path] removes the directory [path]. *)
+
+val make_file : t -> string -> string -> unit
+(** [make_file t path content] creates the file [path] with content
+    [content].  The current umask controls file permissions. *)
+
+val output : t -> string -> (out_channel -> unit) -> unit
+(** [output t path f] creates the file [path] with content from
+    function [f].  The current umask controls file permissions. *)
+
+val unlink : t -> string -> unit
+(** [unlink t path] deletes the file [path]. *)
+
+val func : t -> (unit -> unit) -> unit
+(** [func t f] runs the arbitrary function [f]. *)
+
+val command : t -> string -> unit
+(** [command t cmd] runs [cmd] as the alternate user/group after
+    forking. *)
diff --git a/v2v/output_rhev.ml b/v2v/output_rhev.ml
index 59991ba..bb77787 100644
--- a/v2v/output_rhev.ml
+++ b/v2v/output_rhev.ml
@@ -38,8 +38,13 @@ let rec mount_and_check_storage_domain verbose domain_class os =
   | server, export ->
     let export = "/" ^ export in
 
-    (* Try mounting it. *)
+    (* Create a mountpoint.  Default mode is too restrictive for us
+     * when we need to write into the directory as 36:36.
+     *)
     let mp = Mkdtemp.temp_dir "v2v." "" in
+    chmod mp 0o755;
+
+    (* Try mounting it. *)
     let cmd =
       sprintf "mount %s:%s %s" (quote server) (quote export) (quote mp) in
     if verbose then printf "%s\n%!" cmd;
@@ -111,7 +116,19 @@ and check_storage_domain verbose domain_class os mp =
   (* Looks good, so return the SD mountpoint and UUID. *)
   (mp, uuid)
 
+(* UID:GID required for files and directories when writing to ESD. *)
+let uid = 36 and gid = 36
+
 class output_rhev verbose os vmtype output_alloc =
+  (* Create a UID-switching handle.  If we're not root, create a dummy
+   * one because we cannot switch UIDs.
+   *)
+  let running_as_root = geteuid () = 0 in
+  let kvmuid_t =
+    if running_as_root then
+      Kvmuid.create ~uid ~gid ()
+    else
+      Kvmuid.create () in
 object
   inherit output verbose
 
@@ -171,6 +188,23 @@ object
       eprintf "RHEV: ESD mountpoint: %s\nRHEV: ESD UUID: %s\n%!"
         esd_mp esd_uuid;
 
+    (* See if we can write files as UID:GID 36:36. *)
+    let () =
+      let testfile = esd_mp // esd_uuid // "v2v-uid-test" in
+      Kvmuid.make_file kvmuid_t testfile "";
+      let stat = stat testfile in
+      Kvmuid.unlink kvmuid_t testfile;
+      let actual_uid = stat.st_uid and actual_gid = stat.st_gid in
+      if verbose then
+        eprintf "RHEV: actual UID:GID of new files is %d:%d\n"
+          actual_uid actual_gid;
+      if uid <> actual_uid || gid <> actual_gid then (
+        if running_as_root then
+          warning ~prog (f_"cannot write files to the NFS server as %d:%d, even though we appear to be running as root. This probably means the NFS client or idmapd is not configured properly.\n\nYou will have to chown the files that virt-v2v creates after the run, otherwise RHEV-M will not be able to import the VM.") uid gid
+        else
+          warning ~prog (f_"cannot write files to the NFS server as %d:%d. You might want to stop virt-v2v (^C) and rerun it as root.") uid gid
+      ) in
+
     (* Create unique UUIDs for everything *)
     image_uuid <- uuidgen ~prog ();
     vm_uuid <- uuidgen ~prog ();
@@ -185,7 +219,7 @@ object
      * conversion fails for any reason then we delete this directory.
      *)
     image_dir <- esd_mp // esd_uuid // "images" // image_uuid;
-    mkdir image_dir 0o755;
+    Kvmuid.mkdir kvmuid_t image_dir 0o755;
     at_exit (fun () ->
       if delete_target_directory then (
         let cmd = sprintf "rm -rf %s" (quote image_dir) in
@@ -227,14 +261,21 @@ object
     List.iter (
       fun ({ target_file = target_file }, meta) ->
         let meta_filename = target_file ^ ".meta" in
-        let chan = open_out meta_filename in
-        output_string chan meta;
-        close_out chan
+        Kvmuid.make_file kvmuid_t meta_filename meta
     ) (List.combine targets metas);
 
     (* Return the list of targets. *)
     targets
 
+  method disk_create ?backingfile ?backingformat ?preallocation ?compat
+    ?clustersize path format size =
+    Kvmuid.func kvmuid_t (
+      fun () ->
+        let g = new Guestfs.guestfs () in
+        g#disk_create ?backingfile ?backingformat ?preallocation ?compat
+          ?clustersize path format size
+    )
+
   (* This is called after conversion to write the OVF metadata. *)
   method create_metadata source targets guestcaps inspect =
     (* Create the metadata. *)
@@ -243,11 +284,9 @@ object
 
     (* Write it to the metadata file. *)
     let dir = esd_mp // esd_uuid // "master" // "vms" // vm_uuid in
-    mkdir dir 0o755;
+    Kvmuid.mkdir kvmuid_t dir 0o755;
     let file = dir // vm_uuid ^ ".ovf" in
-    let chan = open_out file in
-    doc_to_chan chan ovf;
-    close_out chan;
+    Kvmuid.output kvmuid_t file (fun chan -> doc_to_chan chan ovf);
 
     (* Finished, so don't delete the target directory on exit. *)
     delete_target_directory <- false

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-libvirt/libguestfs.git



More information about the Pkg-libvirt-commits mailing list