From bcdbe6405c10ecb7374ae47eee6385be17dbd49e Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Tue, 8 Jul 2014 16:33:45 +0100 Subject: [PATCH] v2v: Add support for converting Windows guests. This completes commit f296d34f3b30c82309b22b54bf5a2c133992bd93. --- run.in | 6 - v2v/Makefile.am | 16 ++- v2v/convert_windows.ml | 285 +++++++++++++++++++++++++++++------------ v2v/link.sh.in | 2 +- 4 files changed, 218 insertions(+), 91 deletions(-) diff --git a/run.in b/run.in index 1fcd7952d..f09b8fd99 100755 --- a/run.in +++ b/run.in @@ -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" diff --git a/v2v/Makefile.am b/v2v/Makefile.am index a62e191a6..69875fd06 100644 --- a/v2v/Makefile.am +++ b/v2v/Makefile.am @@ -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' | \ diff --git a/v2v/convert_windows.ml b/v2v/convert_windows.ml index d8df3c844..3b0f67327 100644 --- a/v2v/convert_windows.ml +++ b/v2v/convert_windows.ml @@ -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 diff --git a/v2v/link.sh.in b/v2v/link.sh.in index 9e5684b04..f90901551 100644 --- a/v2v/link.sh.in +++ b/v2v/link.sh.in @@ -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'