mirror of
https://github.com/libguestfs/libguestfs.git
synced 2026-03-21 22:53:37 +00:00
v2v: Add support for converting Windows guests.
This completes commit f296d34f3b.
This commit is contained in:
6
run.in
6
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"
|
||||
|
||||
@@ -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' | \
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'
|
||||
|
||||
Reference in New Issue
Block a user