mirror of
https://github.com/libguestfs/libguestfs.git
synced 2026-03-21 22:53:37 +00:00
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.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
33
v2v/kvmuid-c.c
Normal file
33
v2v/kvmuid-c.c
Normal file
@@ -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));
|
||||
}
|
||||
86
v2v/kvmuid.ml
Normal file
86
v2v/kvmuid.ml
Normal file
@@ -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"
|
||||
)
|
||||
69
v2v/kvmuid.mli
Normal file
69
v2v/kvmuid.mli
Normal file
@@ -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. *)
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user