v2v: Add support for converting Windows guests.

This completes commit f296d34f3b.
This commit is contained in:
Richard W.M. Jones
2014-07-08 16:33:45 +01:00
parent f4698575cc
commit bcdbe6405c
4 changed files with 218 additions and 91 deletions

6
run.in
View File

@@ -92,12 +92,6 @@ if [ -z "$XDG_CONFIG_DIRS" ]; then
export XDG_CONFIG_DIRS
fi
# Data directory used by virt-v2v.
if [ -z "$VIRT_TOOLS_DATA_DIR" ]; then
VIRT_TOOLS_DATA_DIR="$s/v2v"
export VIRT_TOOLS_DATA_DIR
fi
# For Perl.
if [ -z "$PERL5LIB" ]; then
PERL5LIB="$b/perl/blib/lib:$b/perl/blib/arch"

View File

@@ -68,9 +68,20 @@ virt_v2v_CFLAGS = \
BOBJECTS = \
$(top_builddir)/mllib/common_gettext.cmo \
$(top_builddir)/mllib/common_utils.cmo \
$(top_builddir)/mllib/regedit.cmo \
$(top_builddir)/mllib/tTY.cmo \
$(top_builddir)/mllib/progress.cmo \
$(top_builddir)/mllib/config.cmo \
$(top_builddir)/customize/urandom.cmx \
$(top_builddir)/customize/random_seed.cmx \
$(top_builddir)/customize/hostname.cmx \
$(top_builddir)/customize/timezone.cmx \
$(top_builddir)/customize/firstboot.cmx \
$(top_builddir)/customize/perl_edit.cmx \
$(top_builddir)/customize/crypt-c.o \
$(top_builddir)/customize/crypt.cmx \
$(top_builddir)/customize/password.cmx \
$(top_builddir)/customize/customize_run.cmo \
$(SOURCES_ML:.ml=.cmo)
XOBJECTS = $(BOBJECTS:.cmo=.cmx)
@@ -82,7 +93,8 @@ OCAMLPACKAGES = \
-I $(top_builddir)/src/.libs \
-I ../gnulib/lib/.libs \
-I $(top_builddir)/ocaml \
-I $(top_builddir)/mllib
-I $(top_builddir)/mllib \
-I $(top_builddir)/customize
if HAVE_OCAML_PKG_GETTEXT
OCAMLPACKAGES += -package gettext-stub
endif
@@ -150,7 +162,7 @@ depend: .depend
.depend: $(wildcard $(abs_srcdir)/*.mli) $(wildcard $(abs_srcdir)/*.ml)
rm -f $@ $@-t
$(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/mllib $^ | \
$(OCAMLFIND) ocamldep -I ../ocaml -I $(abs_srcdir) -I $(abs_top_builddir)/mllib -I $(abs_top_builddir)/customize $^ | \
$(SED) 's/ *$$//' | \
$(SED) -e :a -e '/ *\\$$/N; s/ *\\\n */ /; ta' | \
$(SED) -e 's,$(abs_srcdir)/,$(builddir)/,g' | \

View File

@@ -21,6 +21,8 @@ open Printf
open Common_gettext.Gettext
open Common_utils
open Regedit
open Utils
open Types
@@ -44,22 +46,26 @@ let convert verbose (g : G.guestfs) inspect source =
let virt_tools_data_dir =
try Sys.getenv "VIRT_TOOLS_DATA_DIR"
with Not_found -> Config.datadir // "virt-tools" in
let virtio_win_dir = "/usr/share/virtio-win" in
(* Since this is a Windows guest, RHSrvAny must exist. (Check also
* that it's not a dangling symlink but a real file).
*)
let rhsrvany_exe = virt_tools_data_dir // "rhsrvany.exe" in
(try
let chan = open_in rhsrvany_exe in
close_in chan
with
Sys_error msg ->
error (f_"'%s' is missing. This file is required in order to do Windows conversions. You can get it by building rhsrvany (https://github.com/rwmjones/rhsrvany). Original error: %s")
rhsrvany_exe msg
);
(* Check if RHEV-APT exists. This is optional. *)
let rhev_apt_exe = virt_tools_data_dir // "rhev-apt.exe" in
let rhev_apt_exe =
try
let chan = open_in rhev_apt_exe in
close_in chan;
Some rhev_apt_exe
with
Sys_error msg ->
warning ~prog (f_"'%s' is missing. Unable to install RHEV-APT (RHEV guest agent). Original error: %s")
rhev_apt_exe msg;
None in
let systemroot = g#inspect_get_windows_systemroot inspect.i_root in
let systemroot = g#inspect_get_windows_systemroot inspect.i_root
and major_version = g#inspect_get_major_version inspect.i_root
and minor_version = g#inspect_get_minor_version inspect.i_root
and arch = g#inspect_get_arch inspect.i_root in
(* This is a wrapper that handles opening and closing the hive
* properly around a function [f]. If [~write] is [true] then the
@@ -90,32 +96,6 @@ let convert verbose (g : G.guestfs) inspect source =
let node = g#hivex_node_get_child node x in
if node = 0L then raise Not_found;
get_node node xs
(* Take a 7 bit ASCII string and encode it as UTF16LE. *)
and encode_utf16le str =
let len = String.length str in
let copy = String.make (len*2) '\000' in
for i = 0 to len-1 do
String.unsafe_set copy (i*2) (String.unsafe_get str i)
done;
copy
(* Take a UTF16LE string and decode it to UTF-8. Actually this
* fails if the string is not 7 bit ASCII. XXX Use iconv here.
*)
and decode_utf16le str =
let len = String.length str in
if len mod 2 <> 0 then
error (f_"decode_utf16le: Windows string does not appear to be in UTF16-LE encoding. This could be a bug in virt-v2v.");
let copy = String.create (len/2) in
for i = 0 to (len/2)-1 do
let cl = String.unsafe_get str (i*2) in
let ch = String.unsafe_get str ((i*2)+1) in
if ch != '\000' || Char.code cl >= 127 then
error (f_"decode_utf16le: Windows UTF16-LE string contains non-7-bit characters. This is a bug in virt-v2v, please report it.");
String.unsafe_set copy i cl
done;
copy
in
(*----------------------------------------------------------------------*)
@@ -135,7 +115,7 @@ let convert verbose (g : G.guestfs) inspect source =
raise Not_found
);
let data = g#hivex_value_value valueh in
let data = decode_utf16le data in
let data = decode_utf16le ~prog data in
(* The uninstall program will be uninst.exe. This is a wrapper
* around _uninst.exe which prompts the user. As we don't want
@@ -162,6 +142,44 @@ let convert verbose (g : G.guestfs) inspect source =
(*----------------------------------------------------------------------*)
(* Perform the conversion of the Windows guest. *)
let rec configure_firstboot () =
let fb = Buffer.create 1024 in
bprintf fb "@echo off\n";
configure_rhev_apt fb;
unconfigure_xenpv fb;
(* Write the completed script to the guest. *)
let firstboot_script = Buffer.contents fb in
Firstboot.add_firstboot_script ~prog g inspect.i_root 1 firstboot_script
and configure_rhev_apt fb =
(* Configure RHEV-APT (the RHEV guest agent). However if it doesn't
* exist just warn about it and continue.
*)
match rhev_apt_exe with
| None -> ()
| Some rhev_apt_exe ->
g#upload rhev_apt_exe "/rhev-apt.exe"; (* XXX *)
bprintf fb "\
echo installing rhev-apt
\"\\rhev-apt.exe\" /S /v /qn
echo starting rhev-apt
net start rhev-apt
"
and unconfigure_xenpv fb =
match xenpv_uninst with
| None -> () (* nothing to be uninstalled *)
| Some uninst ->
bprintf fb "\
echo uninstalling Xen PV driver
\"%s\"
" uninst
in
let rec update_system_hive root =
(* Update the SYSTEM hive. When this function is called the hive has
* already been opened as a hivex handle inside guestfs.
@@ -175,58 +193,158 @@ let convert verbose (g : G.guestfs) inspect source =
if verbose then printf "current ControlSet is %s\n%!" current_cs;
let firstboot = configure_firstboot root current_cs in
configure_rhev_apt root firstboot;
(match xenpv_uninst with
| None -> () (* nothing to be uninstalled *)
| Some uninst -> unconfigure_xenpv root firstboot uninst
);
close_firstboot root firstboot;
disable_services root current_cs;
let block_net_drivers = install_virtio_drivers root current_cs in
block_net_drivers
and configure_firstboot root current_cs =
ignore virt_tools_data_dir;
()
and configure_rhev_apt root firstboot =
()
and unconfigure_xenpv root firstboot uninst_exe =
()
and close_firstboot root firstboot =
()
install_virtio_drivers root current_cs
and disable_services root current_cs =
()
(* Disable miscellaneous services. *)
let services = get_node root [current_cs; "Services"] in
(* Disable the Processor and Intelppm services
* http://blogs.msdn.com/b/virtual_pc_guy/archive/2005/10/24/484461.aspx
*
* Disable the rhelscsi service (RHBZ#809273).
*)
let disable = [ "Processor"; "Intelppm"; "rhelscsi" ] in
List.iter (
fun name ->
let node = g#hivex_node_get_child services name in
if node <> 0L then (
(* Delete the node instead of trying to disable it. RHBZ#737600. *)
g#hivex_node_delete_child node
)
) disable
and install_virtio_drivers root current_cs =
ignore virtio_win_dir;
("XXX", "XXX")
(* Copy the virtio drivers to the guest. *)
let driverdir = sprintf "%s/Drivers/VirtIO" systemroot in
let driverdir = g#case_sensitive_path driverdir in
g#mkdir_p driverdir;
(* See if the drivers for this guest are available in virtio_win_dir. *)
let path =
match major_version, minor_version, arch with
| 5, 1, "i386" ->
Some (virtio_win_dir // "drivers/i386/WinXP")
| 5, 2, "i386" ->
Some (virtio_win_dir // "drivers/i386/Win2003")
| 5, 2, "x86_64" ->
Some (virtio_win_dir // "drivers/amd64/Win2003")
| 6, 0, "i386" ->
Some (virtio_win_dir // "drivers/i386/Win2008")
| 6, 0, "x86_64" ->
Some (virtio_win_dir // "drivers/amd64/Win2008")
| 6, 1, "i386" ->
Some (virtio_win_dir // "drivers/i386/Win2008R2")
| 6, 1, "x86_64" ->
Some (virtio_win_dir // "drivers/amd64/Win2008R2")
| 6, 3, "i386" ->
Some (virtio_win_dir // "drivers/i386/Win2012R2")
| 6, 3, "x86_64" ->
Some (virtio_win_dir // "drivers/amd64/Win2012R2")
| _, _, _ ->
None in
let path =
match path with
| None -> None
| Some path ->
if is_directory path then Some path else None in
match path with
| None ->
warning ~prog (f_"there are no virtio drivers available for this version of Windows (%d.%d %s). virt-v2v looks for drivers in %s\n\nThe guest will be configured to use slower emulated devices.")
major_version minor_version arch virtio_win_dir;
( "ide", "rtl8139" )
| Some path ->
(* Can we install the block driver? *)
let block =
let block_path = path // "viostor.sys" in
if not (Sys.file_exists block_path) then (
warning ~prog (f_"there is no viostor (virtio block device) driver for this version of Windows (%d.%d %s). virt-v2v looks for this driver here: %s\n\nThe guest will be configured to use a slower emulated device.")
major_version minor_version arch block_path;
"ide"
)
else (
let target = sprintf "%s/system32/drivers/viostor.sys" systemroot in
let target = g#case_sensitive_path target in
g#cp block_path target;
add_viostor_to_critical_device_database root current_cs;
"virtio"
) in
(* Can we install the virtio-net driver? *)
let net =
let net_path = path // "netkvm.inf" in
if not (Sys.file_exists net_path) then (
warning ~prog (f_"there is no virtio network driver for this version of Windows (%d.%d %s). virt-v2v looks for this driver here: %s\n\nThe guest will be configured to use a slower emulated device.")
major_version minor_version arch net_path;
"rtl8139"
)
else
(* It will be installed at firstboot. *)
"virtio" in
(* Copy the drivers to the driverdir. They will be installed at
* firstboot.
*)
let files = Sys.readdir path in
let files = Array.to_list files in
let files = List.map ((//) path) files in
List.iter (fun file -> g#upload file driverdir) files;
(block, net)
and add_viostor_to_critical_device_database root current_cs =
(* See http://rwmj.wordpress.com/2010/04/30/tip-install-a-device-driver-in-a-windows-vm/
* NB: All these edits are in the HKLM\SYSTEM hive. No other
* hive may be modified here.
*)
let regedits = [
[ current_cs; "Control"; "CriticalDeviceDatabase"; "pci#ven_1af4&dev_1001&subsys_00000000" ],
[ "Service", REG_SZ "viostor";
"ClassGUID", REG_SZ "{4D36E97B-E325-11CE-BFC1-08002BE10318}" ];
[ current_cs; "Control"; "CriticalDeviceDatabase"; "pci#ven_1af4&dev_1001&subsys_00020000" ],
[ "Service", REG_SZ "viostor";
"ClassGUID", REG_SZ "{4D36E97B-E325-11CE-BFC1-08002BE10318}" ];
[ current_cs; "Control"; "CriticalDeviceDatabase"; "pci#ven_1af4&dev_1001&subsys_00021af4" ],
[ "Service", REG_SZ "viostor";
"ClassGUID", REG_SZ "{4D36E97B-E325-11CE-BFC1-08002BE10318}" ];
[ current_cs; "Services"; "viostor" ],
[ "Type", REG_DWORD 0x1_l;
"Start", REG_DWORD 0x0_l;
"Group", REG_SZ "SCSI miniport";
"ErrorControl", REG_DWORD 0x1_l;
"ImagePath", REG_SZ "system32\\\\drivers\\\\viostor.sys";
"Tag", REG_DWORD 0x21_l ];
[ current_cs; "Services"; "viostor"; "Parameters" ],
[ "BusType", REG_DWORD 0x1_l ];
[ current_cs; "Services"; "viostor"; "Parameters"; "MaxTransferSize" ],
[ "ParamDesc", REG_SZ "Maximum Transfer Size";
"type", REG_SZ "enum";
"default", REG_SZ "0" ];
[ current_cs; "Services"; "viostor"; "Parameters"; "MaxTransferSize"; "enum" ],
[ "0", REG_SZ "64 KB";
"1", REG_SZ "128 KB";
"2", REG_SZ "256 KB" ];
[ current_cs; "Services"; "viostor"; "Parameters"; "PnpInterface" ],
[ "5", REG_DWORD 0x1_l ];
[ current_cs; "Services"; "viostor"; "Enum" ],
[ "0", REG_SZ "PCI\\VEN_1AF4&DEV_1001&SUBSYS_00021AF4&REV_00\\3&13c0b0c5&0&20";
"Count", REG_DWORD 0x1_l;
"NextInstance", REG_DWORD 0x1_l ];
] in
reg_import g root regedits
and update_software_hive root =
(* Update the SOFTWARE hive. When this function is called the
@@ -336,6 +454,9 @@ let convert verbose (g : G.guestfs) inspect source =
)
in
(* Firstboot configuration. *)
configure_firstboot ();
(* Open the system hive and update it. *)
let block_driver, net_driver =
with_hive "system" ~write:true update_system_hive in

View File

@@ -19,4 +19,4 @@
# Hack automake to link binary properly. There is no other way to add
# the -cclib parameter to the end of the command line.
exec "$@" -linkpkg -cclib '-lutils -lncurses @LIBXML2_LIBS@ -lgnu'
exec "$@" -linkpkg -cclib '-lutils -lncurses -lcrypt @LIBXML2_LIBS@ -lgnu'