Richard W.M. Jones
2011-Apr-05 13:37 UTC
[Libguestfs] [PATCH 0/2] New inspection APIs for Windows
While we have always been able to correctly map Linux partitions to their mount points, the situation for Windows guests has not been so good. C: on Windows was mapped to '/' and any other drive was ignored unless you knew it was there. With these two commits we can get the mappings of drive letters to partitions. From there we'll be able to extend virt-edit, guestfish etc to do the right thing with Windows drive:path names. Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones libguestfs lets you edit virtual machines. Supports shell scripting, bindings from many languages. http://libguestfs.org
Richard W.M. Jones
2011-Apr-05 13:39 UTC
[Libguestfs] [PATCH 1/2] New API: inspect-get-windows-current-control-set
-- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones New in Fedora 11: Fedora Windows cross-compiler. Compile Windows programs, test, and build Windows installers. Over 70 libraries supprt'd http://fedoraproject.org/wiki/MinGW http://www.annexia.org/fedora_mingw -------------- next part -------------->From e306082952e36d9b50da84b160af6c0d0f277353 Mon Sep 17 00:00:00 2001From: Richard W.M. Jones <rjones at redhat.com> Date: Tue, 5 Apr 2011 12:44:34 +0100 Subject: [PATCH 1/2] New API: inspect-get-windows-current-control-set This returns the actual registry key corresponding to CurrentControlSet (eg. it might be "ControlSet001"). Previously the inspection code was hard-coding ControlSet001. Now we use the correct control set, and also make it available to callers through the API. This commit also updates the virt-dhcp-address example so it uses this new API. virt-inspector displays the current control set when available. --- examples/virt-dhcp-address.c | 29 +++----------- generator/generator_actions.ml | 16 +++++++ images/guest-aux/windows-system.reg | 6 +++ inspector/example-windows.xml | 1 + inspector/virt-inspector.c | 8 ++++ inspector/virt-inspector.rng | 2 +- src/guestfs-internal.h | 1 + src/inspect.c | 74 ++++++++++++++++++++++++++++++---- 8 files changed, 104 insertions(+), 33 deletions(-) diff --git a/examples/virt-dhcp-address.c b/examples/virt-dhcp-address.c index c075a47..dd2580f 100644 --- a/examples/virt-dhcp-address.c +++ b/examples/virt-dhcp-address.c @@ -189,7 +189,7 @@ print_dhcp_address_linux (guestfs_h *g, char *root, const char *logfile) /* Download the Windows SYSTEM hive and find DHCP configuration in there. */ static void -print_dhcp_address_windows (guestfs_h *g, char *root_unused) +print_dhcp_address_windows (guestfs_h *g, char *root_fs) { char *system_path; char tmpfile[] = "/tmp/systemXXXXXX"; @@ -197,8 +197,7 @@ print_dhcp_address_windows (guestfs_h *g, char *root_unused) hive_h *h; hive_node_h root, node, *nodes; hive_value_h value; - int32_t dword; - char controlset[] = "ControlSetXXX"; + char *controlset; size_t i; char *p; @@ -222,6 +221,10 @@ print_dhcp_address_windows (guestfs_h *g, char *root_unused) free (system_path); + controlset = guestfs_inspect_get_windows_current_control_set (g, root_fs); + if (controlset == NULL) + exit (EXIT_FAILURE); + /* Open the hive to parse it. */ h = hivex_open (tmpfile, 0); err = errno; @@ -234,31 +237,11 @@ print_dhcp_address_windows (guestfs_h *g, char *root_unused) exit (EXIT_FAILURE); } - /* Navigate to the Select key so we know which ControlSet is in use. */ root = hivex_root (h); if (root == 0) { perror ("hivex_root"); exit (EXIT_FAILURE); } - node = hivex_node_get_child (h, root, "Select"); - if (node == 0) { - if (errno != 0) - perror ("hivex_node_get_child"); - else - fprintf (stderr, "virt-dhcp-address: HKLM\\System\\Select key not found."); - exit (EXIT_FAILURE); - } - value = hivex_node_get_value (h, node, "Current"); - if (value == 0) { - if (errno != 0) - perror ("hivex_node_get_value"); - else - fprintf (stderr, "virt-dhcp-address: HKLM\\System\\Select Default entry not found."); - exit (EXIT_FAILURE); - } - /* XXX Should check the type. */ - dword = hivex_value_dword (h, value); - snprintf (controlset, sizeof controlset, "ControlSet%03d", dword); /* Get ControlSetXXX\Services\Tcpip\Parameters\Interfaces. */ const char *path[] = { controlset, "Services", "Tcpip", "Parameters", diff --git a/generator/generator_actions.ml b/generator/generator_actions.ml index a10b7f7..c59964a 100644 --- a/generator/generator_actions.ml +++ b/generator/generator_actions.ml @@ -1451,6 +1451,22 @@ Please read L<guestfs(3)/INSPECTION> for more details. See also C<guestfs_inspect_get_product_name>, C<guestfs_inspect_get_major_version>."); + ("inspect_get_windows_current_control_set", (RString "controlset", [Device "root"], []), -1, [], + [], + "get Windows CurrentControlSet of inspected operating system", + "\ +This function should only be called with a root device string +as returned by C<guestfs_inspect_os>. + +This returns the Windows CurrentControlSet of the inspected guest. +The CurrentControlSet is a registry key name such as C<ControlSet001>. + +This call assumes that the guest is Windows and that the +Registry could be examined by inspection. If this is not +the case then an error is returned. + +Please read L<guestfs(3)/INSPECTION> for more details."); + ] (* daemon_functions are any functions which cause some action diff --git a/images/guest-aux/windows-system.reg b/images/guest-aux/windows-system.reg index 87ae779..6cb5d32 100644 --- a/images/guest-aux/windows-system.reg +++ b/images/guest-aux/windows-system.reg @@ -1,3 +1,9 @@ +[HKEY_LOCAL_MACHINE\SYSTEM\Select] +"Current"=dword:00000001 +"Default"=dword:00000001 +"Failed"=dword:00000000 +"LastKnownGood"=dword:00000002 + [HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001] [HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services] diff --git a/inspector/example-windows.xml b/inspector/example-windows.xml index 7b3ae74..8e53159 100644 --- a/inspector/example-windows.xml +++ b/inspector/example-windows.xml @@ -10,6 +10,7 @@ <major_version>6</major_version> <minor_version>1</minor_version> <windows_systemroot>/Windows</windows_systemroot> + <windows_current_control_set>ControlSet001</windows_current_control_set> <format>installed</format> <mountpoints> <mountpoint dev="/dev/sda2">/</mountpoint> diff --git a/inspector/virt-inspector.c b/inspector/virt-inspector.c index 4d8e3ad..d016b2d 100644 --- a/inspector/virt-inspector.c +++ b/inspector/virt-inspector.c @@ -416,6 +416,14 @@ output_root (xmlTextWriterPtr xo, char *root) BAD_CAST str)); free (str); ); + DISABLE_GUESTFS_ERRORS_FOR ( + str = guestfs_inspect_get_windows_current_control_set (g, root); + if (str) + XMLERROR (-1, + xmlTextWriterWriteElement (xo, BAD_CAST "windows_current_control_set", + BAD_CAST str)); + free (str); + ); str = guestfs_inspect_get_format (g, root); if (!str) exit (EXIT_FAILURE); diff --git a/inspector/virt-inspector.rng b/inspector/virt-inspector.rng index 8d54fac..7a822e6 100644 --- a/inspector/virt-inspector.rng +++ b/inspector/virt-inspector.rng @@ -37,7 +37,7 @@ <element name="major_version"><text/></element> <element name="minor_version"><text/></element> <optional><element name="windows_systemroot"><text/></element></optional> - + <optional><element name="windows_current_control_set"><text/></element></optional> <optional><element name="package_format"><text/></element></optional> <optional><element name="package_management"><text/></element></optional> <optional><element name="format"><text/></element></optional> diff --git a/src/guestfs-internal.h b/src/guestfs-internal.h index 3a63f2e..b29fa57 100644 --- a/src/guestfs-internal.h +++ b/src/guestfs-internal.h @@ -251,6 +251,7 @@ struct inspect_fs { char *arch; char *hostname; char *windows_systemroot; + char *windows_current_control_set; enum inspect_os_format format; int is_live_disk; int is_netinst_disk; diff --git a/src/inspect.c b/src/inspect.c index 238e6e5..140e2e9 100644 --- a/src/inspect.c +++ b/src/inspect.c @@ -25,6 +25,7 @@ #include <unistd.h> #include <string.h> #include <sys/stat.h> +#include <errno.h> #ifdef HAVE_PCRE #include <pcre.h> @@ -1612,7 +1613,10 @@ check_windows_system_registry (guestfs_h *g, struct inspect_fs *fs) int ret = -1; hive_h *h = NULL; - hive_value_h *values = NULL; + hive_node_h root, node; + hive_value_h value, *values = NULL; + int32_t dword; + size_t i; if (download_to_tmp (g, system_path, system_local, MAX_REGISTRY_SIZE) == -1) goto out; @@ -1623,21 +1627,49 @@ check_windows_system_registry (guestfs_h *g, struct inspect_fs *fs) goto out; } - hive_node_h node = hivex_root (h); - /* XXX Don't hard-code ControlSet001. The current control set would - * be another good thing to expose up through the inspection API. - */ + root = hivex_root (h); + if (root == 0) { + perrorf (g, "hivex_root"); + goto out; + } + + /* Get the CurrentControlSet. */ + errno = 0; + node = hivex_node_get_child (h, root, "Select"); + if (node == 0) { + if (errno != 0) + perrorf (g, "hivex_node_get_child"); + else + error (g, "hivex: could not locate HKLM\\SYSTEM\\Select"); + goto out; + } + + errno = 0; + value = hivex_node_get_value (h, node, "Current"); + if (value == 0) { + if (errno != 0) + perrorf (g, "hivex_node_get_value"); + else + error (g, "hivex: HKLM\\System\\Select Default entry not found."); + goto out; + } + + /* XXX Should check the type. */ + dword = hivex_value_dword (h, value); + fs->windows_current_control_set = safe_asprintf (g, "ControlSet%03d", dword); + + /* Get the hostname. */ const char *hivepath[] - { "ControlSet001", "Services", "Tcpip", "Parameters" }; - size_t i; - for (i = 0; + { fs->windows_current_control_set, "Services", "Tcpip", "Parameters" }; + for (node = root, i = 0; node != 0 && i < sizeof hivepath / sizeof hivepath[0]; ++i) { node = hivex_node_get_child (h, node, hivepath[i]); } if (node == 0) { - perrorf (g, "hivex: cannot locate HKLM\\SYSTEM\\ControlSet001\\Services\\Tcpip\\Parameters"); + perrorf (g, "hivex: cannot locate HKLM\\SYSTEM\\%s\\Services\\Tcpip\\Parameters", + fs->windows_current_control_set); goto out; } @@ -2011,6 +2043,22 @@ guestfs__inspect_get_windows_systemroot (guestfs_h *g, const char *root) } char * +guestfs__inspect_get_windows_current_control_set (guestfs_h *g, + const char *root) +{ + struct inspect_fs *fs = search_for_root (g, root); + if (!fs) + return NULL; + + if (!fs->windows_current_control_set) { + error (g, _("not a Windows guest, or CurrentControlSet could not be determined")); + return NULL; + } + + return safe_strdup (g, fs->windows_current_control_set); +} + +char * guestfs__inspect_get_format (guestfs_h *g, const char *root) { struct inspect_fs *fs = search_for_root (g, root); @@ -2905,6 +2953,13 @@ guestfs__inspect_get_windows_systemroot (guestfs_h *g, const char *root) NOT_IMPL(NULL); } +char * +guestfs__inspect_get_windows_current_control_set (guestfs_h *g, + const char *root) +{ + NOT_IMPL(NULL); +} + char ** guestfs__inspect_get_mountpoints (guestfs_h *g, const char *root) { @@ -2978,6 +3033,7 @@ guestfs___free_inspect_info (guestfs_h *g) free (g->fses[i].arch); free (g->fses[i].hostname); free (g->fses[i].windows_systemroot); + free (g->fses[i].windows_current_control_set); size_t j; for (j = 0; j < g->fses[i].nr_fstab; ++j) { free (g->fses[i].fstab[j].device); -- 1.7.4.1
Richard W.M. Jones
2011-Apr-05 13:40 UTC
[Libguestfs] [PATCH 2/2] New API: inspect-get-drive-mappings
-- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones virt-p2v converts physical machines to virtual machines. Boot with a live CD or over the network (PXE) and turn machines into Xen guests. http://et.redhat.com/~rjones/virt-p2v -------------- next part -------------->From b2a397073b990e168b0099ea6a1f4f578f78b5d0 Mon Sep 17 00:00:00 2001From: Richard W.M. Jones <rjones at redhat.com> Date: Tue, 5 Apr 2011 14:03:08 +0100 Subject: [PATCH 2/2] New API: inspect-get-drive-mappings This returns the drive mappings from the Windows Registry. virt-inspector displays the drive mappings, giving output similar to this: <drive_mappings> <drive_mapping name="C">/dev/sda2</drive_mapping> <drive_mapping name="E">/dev/sdb1</drive_mapping> </drive_mappings> --- generator/generator_actions.ml | 42 ++++++++ images/guest-aux/make-windows-img.sh | 3 + images/guest-aux/windows-system | Bin 12288 -> 12288 bytes images/guest-aux/windows-system.reg | 3 + inspector/example-windows.xml | 3 + inspector/virt-inspector.c | 54 ++++++++++ inspector/virt-inspector.rng | 13 +++ src/guestfs-internal.h | 1 + src/inspect.c | 183 +++++++++++++++++++++++++++++++++- 9 files changed, 300 insertions(+), 2 deletions(-) diff --git a/generator/generator_actions.ml b/generator/generator_actions.ml index c59964a..26dc64e 100644 --- a/generator/generator_actions.ml +++ b/generator/generator_actions.ml @@ -928,6 +928,12 @@ which is the filesystem that would be mounted there Non-mounted devices such as swap devices are I<not> returned in this list. +For operating systems like Windows which still use drive +letters, this call will only return an entry for the first +drive \"mounted on\" C</>. For information about the +mapping of drive letters to partitions, see +C<guestfs_inspect_get_drive_mappings>. + Please read L<guestfs(3)/INSPECTION> for more details. See also C<guestfs_inspect_get_filesystems>."); @@ -1467,6 +1473,42 @@ the case then an error is returned. Please read L<guestfs(3)/INSPECTION> for more details."); + ("inspect_get_drive_mappings", (RHashtable "drives", [Device "root"], []), -1, [], + [], + "get drive letter mappings", + "\ +This function should only be called with a root device string +as returned by C<guestfs_inspect_os>. + +This call is useful for Windows which uses a primitive system +of assigning drive letters (like \"C:\") to partitions. +This inspection API examines the Windows Registry to find out +how disks/partitions are mapped to drive letters, and returns +a hash table as in the example below: + + C => /dev/vda2 + E => /dev/vdb1 + F => /dev/vdc1 + +Note that keys are drive letters. For Windows, the key is +case insensitive and just contains the drive letter, without +the customary colon separator character. + +In future we may support other operating systems that also used drive +letters, but the keys for those might not be case insensitive +and might be longer than 1 character. For example in OS-9, +hard drives were named C<h0>, C<h1> etc. + +For Windows guests, currently only hard drive mappings are +returned. Removable disks (eg. DVD-ROMs) are ignored. + +For guests that do not use drive mappings, or if the drive mappings +could not be determined, this returns an empty hash table. + +Please read L<guestfs(3)/INSPECTION> for more details. +See also C<guestfs_inspect_get_mountpoints>, +C<guestfs_inspect_get_filesystems>."); + ] (* daemon_functions are any functions which cause some action diff --git a/images/guest-aux/make-windows-img.sh b/images/guest-aux/make-windows-img.sh index 15cb7c5..f659f4b 100755 --- a/images/guest-aux/make-windows-img.sh +++ b/images/guest-aux/make-windows-img.sh @@ -45,6 +45,9 @@ part-init /dev/sda mbr part-add /dev/sda p 64 524287 part-add /dev/sda p 524288 -64 +# Disk ID. +pwrite-device /dev/sda "1234" 0x01b8 + # Phony boot loader filesystem. mkfs ntfs /dev/sda1 diff --git a/images/guest-aux/windows-system b/images/guest-aux/windows-system index 7d935b02d07e975ae956c05384b593f439ee0481..3a23214201cddb238238b801c6b5460fc81d03e3 100755 GIT binary patch delta 633 zcmZ`%O-lk%6umQrW~oh1RD{Hv5DJ3mYas;@Cbg(mZG{w_FdAkcwV*}LvR%l7K6Vl< zY!gv`pr7EfrORj$?P?L(xuZe^9k_Ep&VA>-``)-|H+>)1j4o<nB|_8$IAia&eYBF% zrKQCb|6 at fv>sM;h5W{8eVsIy;8}X?|<hE;Y9!BwHj^TrCo0gd^5}}o>yg~v&HAXux z(5NDQf;E^NfB_BE5BL47;VQ^k3%*RNrGo?0F8<d*Npm+-vO2ua6JWu%0Ig;AEHg*+ z>}?S8c=Z>*nJE-EZ0E>ccO0})6 at B);ATg1+3f;(Q(JNhJuw2DCUC>~?n?p!ZSdarC z4BK)_cG1iw&BMK{Ss)rg&C07ZiM3Py<FC#gd_tp!TI)z*Pos{UUx;T<C7nWBB(=Io z^xkX+nzM8B3*a;v6*2WGA`@a+ at TFP#E^1)$_3hHx-;JaYgJC9Fl`*Ny2eB4N;}@9u zI3DLG*2A228ZLD?ZY=lIQM$al2(W-!kH`~aNUiDe+*llo>!4oCHV<)+xHUu%3CKU| O^36!76ns?F at 4g>{?XIN& delta 209 zcmZojXh_&#A)v^}009jG3=IE)0K@~)4NzKYVxsWm903(Z*3A<I`uQh6P*h<0ATXIp z$xiSCP+3j}BZC4^>!Ov`{R<{5Dv0m`IWRLL1SdBsxp7PY3K at K_QktxwAi@XYz~oN| zPX451$H^ebz%XOobWV_dVI~H_$##ljOg984rzu;>Wq{;f)#v>H+Vud)z5%4mvN;$O VfX?F(n!HchiRl9GW=5uSyZ|MKK_mbG diff --git a/images/guest-aux/windows-system.reg b/images/guest-aux/windows-system.reg index 6cb5d32..5478d2d 100644 --- a/images/guest-aux/windows-system.reg +++ b/images/guest-aux/windows-system.reg @@ -4,6 +4,9 @@ "Failed"=dword:00000000 "LastKnownGood"=dword:00000002 +[HKEY_LOCAL_MACHINE\SYSTEM\MountedDevices] +"\\DosDevices\\C:"=hex(3):31,32,33,34,00,00,00,10,00,00,00,00 + [HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001] [HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services] diff --git a/inspector/example-windows.xml b/inspector/example-windows.xml index 8e53159..8b3b8a7 100644 --- a/inspector/example-windows.xml +++ b/inspector/example-windows.xml @@ -20,6 +20,9 @@ <type>ntfs</type> </filesystem> </filesystems> + <drive_mappings> + <drive_mapping name="C">/dev/sda2</drive_mapping> + </drive_mappings> <applications> <application> <name>test1</name> diff --git a/inspector/virt-inspector.c b/inspector/virt-inspector.c index d016b2d..7724be6 100644 --- a/inspector/virt-inspector.c +++ b/inspector/virt-inspector.c @@ -51,6 +51,7 @@ static void output_roots (xmlTextWriterPtr xo, char **roots); static void output_root (xmlTextWriterPtr xo, char *root); static void output_mountpoints (xmlTextWriterPtr xo, char *root); static void output_filesystems (xmlTextWriterPtr xo, char *root); +static void output_drive_mappings (xmlTextWriterPtr xo, char *root); static void output_applications (xmlTextWriterPtr xo, char *root); static void canonicalize (char *dev); static void free_strings (char **argv); @@ -458,6 +459,8 @@ output_root (xmlTextWriterPtr xo, char *root) output_filesystems (xo, root); + output_drive_mappings (xo, root); + output_applications (xo, root); XMLERROR (-1, xmlTextWriterEndElement (xo)); @@ -473,6 +476,15 @@ compare_keys (const void *p1, const void *p2) } static int +compare_keys_nocase (const void *p1, const void *p2) +{ + const char *key1 = * (char * const *) p1; + const char *key2 = * (char * const *) p2; + + return strcasecmp (key1, key2); +} + +static int compare_keys_len (const void *p1, const void *p2) { const char *key1 = * (char * const *) p1; @@ -583,6 +595,48 @@ output_filesystems (xmlTextWriterPtr xo, char *root) } static void +output_drive_mappings (xmlTextWriterPtr xo, char *root) +{ + char **drive_mappings = NULL; + size_t i; + + DISABLE_GUESTFS_ERRORS_FOR ( + drive_mappings = guestfs_inspect_get_drive_mappings (g, root); + ); + if (drive_mappings == NULL) + return; + + if (drive_mappings[0] == NULL) { + free_strings (drive_mappings); + return; + } + + /* Sort by key. */ + qsort (drive_mappings, + count_strings (drive_mappings) / 2, 2 * sizeof (char *), + compare_keys_nocase); + + XMLERROR (-1, xmlTextWriterStartElement (xo, BAD_CAST "drive_mappings")); + + for (i = 0; drive_mappings[i] != NULL; i += 2) { + canonicalize (drive_mappings[i+1]); + + XMLERROR (-1, + xmlTextWriterStartElement (xo, BAD_CAST "drive_mapping")); + XMLERROR (-1, + xmlTextWriterWriteAttribute (xo, BAD_CAST "name", + BAD_CAST drive_mappings[i])); + XMLERROR (-1, + xmlTextWriterWriteString (xo, BAD_CAST drive_mappings[i+1])); + XMLERROR (-1, xmlTextWriterEndElement (xo)); + } + + XMLERROR (-1, xmlTextWriterEndElement (xo)); + + free_strings (drive_mappings); +} + +static void output_applications (xmlTextWriterPtr xo, char *root) { struct guestfs_application_list *apps; diff --git a/inspector/virt-inspector.rng b/inspector/virt-inspector.rng index 7a822e6..669e8bc 100644 --- a/inspector/virt-inspector.rng +++ b/inspector/virt-inspector.rng @@ -47,6 +47,7 @@ <ref name="mountpoints"/> <ref name="filesystems"/> + <optional><ref name="drive_mappings"/></optional> <optional><ref name="applications"/></optional> </interleave> @@ -83,6 +84,18 @@ </element> </define> + <!-- drive mappings (for Windows) --> + <define name="drive_mappings"> + <element name="drive_mappings"> + <oneOrMore> + <element name="drive_mapping"> + <attribute name="name"><text/></attribute> + <text/> + </element> + </oneOrMore> + </element> + </define> + <!-- applications installed --> <define name="applications"> <element name="applications"> diff --git a/src/guestfs-internal.h b/src/guestfs-internal.h index b29fa57..46576bb 100644 --- a/src/guestfs-internal.h +++ b/src/guestfs-internal.h @@ -252,6 +252,7 @@ struct inspect_fs { char *hostname; char *windows_systemroot; char *windows_current_control_set; + char **drive_mappings; enum inspect_os_format format; int is_live_disk; int is_netinst_disk; diff --git a/src/inspect.c b/src/inspect.c index 140e2e9..da35fd9 100644 --- a/src/inspect.c +++ b/src/inspect.c @@ -26,6 +26,7 @@ #include <string.h> #include <sys/stat.h> #include <errno.h> +#include <endian.h> #ifdef HAVE_PCRE #include <pcre.h> @@ -230,6 +231,7 @@ static int check_windows_root (guestfs_h *g, struct inspect_fs *fs); static int check_windows_arch (guestfs_h *g, struct inspect_fs *fs); static int check_windows_software_registry (guestfs_h *g, struct inspect_fs *fs); static int check_windows_system_registry (guestfs_h *g, struct inspect_fs *fs); +static char *map_registry_disk_blob (guestfs_h *g, const char *blob); static char *resolve_windows_path_silently (guestfs_h *g, const char *); static int is_file_nocase (guestfs_h *g, const char *); static int is_dir_nocase (guestfs_h *g, const char *); @@ -1616,7 +1618,7 @@ check_windows_system_registry (guestfs_h *g, struct inspect_fs *fs) hive_node_h root, node; hive_value_h value, *values = NULL; int32_t dword; - size_t i; + size_t i, count; if (download_to_tmp (g, system_path, system_local, MAX_REGISTRY_SIZE) == -1) goto out; @@ -1658,6 +1660,69 @@ check_windows_system_registry (guestfs_h *g, struct inspect_fs *fs) dword = hivex_value_dword (h, value); fs->windows_current_control_set = safe_asprintf (g, "ControlSet%03d", dword); + /* Get the drive mappings. + * This page explains the contents of HKLM\System\MountedDevices: + * http://www.goodells.net/multiboot/partsigs.shtml + */ + errno = 0; + node = hivex_node_get_child (h, root, "MountedDevices"); + if (node == 0) { + if (errno != 0) + perrorf (g, "hivex_node_get_child"); + else + error (g, "hivex: could not locate HKLM\\SYSTEM\\MountedDevices"); + goto out; + } + + values = hivex_node_values (h, node); + + /* Count how many DOS drive letter mappings there are. This doesn't + * ignore removable devices, so it overestimates, but that doesn't + * matter because it just means we'll allocate a few bytes extra. + */ + for (i = count = 0; values[i] != 0; ++i) { + char *key = hivex_value_key (h, values[i]); + if (key == NULL) { + perrorf (g, "hivex_value_key"); + goto out; + } + if (STRCASEEQLEN (key, "\\DosDevices\\", 12) && + c_isalpha (key[12]) && key[13] == ':') + count++; + } + + fs->drive_mappings = calloc (2*count + 1, sizeof (char *)); + if (fs->drive_mappings == NULL) { + perrorf (g, "calloc"); + goto out; + } + + for (i = count = 0; values[i] != 0; ++i) { + char *key = hivex_value_key (h, values[i]); + if (key == NULL) { + perrorf (g, "hivex_value_key"); + goto out; + } + if (STRCASEEQLEN (key, "\\DosDevices\\", 12) && + c_isalpha (key[12]) && key[13] == ':') { + /* Get the binary value. Is it a fixed disk? */ + char *blob, *device; + size_t len; + hive_type type; + + blob = hivex_value_value (h, values[i], &type, &len); + if (blob != NULL && type == 3 && len == 12) { + /* Try to map the blob to a known disk and partition. */ + device = map_registry_disk_blob (g, blob); + if (device != NULL) { + fs->drive_mappings[count++] = safe_strndup (g, &key[12], 1); + fs->drive_mappings[count++] = device; + } + } + free (blob); + } + } + /* Get the hostname. */ const char *hivepath[] { fs->windows_current_control_set, "Services", "Tcpip", "Parameters" }; @@ -1709,6 +1774,75 @@ check_windows_system_registry (guestfs_h *g, struct inspect_fs *fs) return ret; } +/* Windows Registry HKLM\SYSTEM\MountedDevices uses a blob of data + * to store partitions. This blob is described here: + * http://www.goodells.net/multiboot/partsigs.shtml + * The following function maps this blob to a libguestfs partition + * name, if possible. + */ +static char * +map_registry_disk_blob (guestfs_h *g, const char *blob) +{ + char **devices = NULL; + struct guestfs_partition_list *partitions = NULL; + char *diskid; + size_t i, j, len; + char *ret = NULL; + uint64_t part_offset; + + /* First 4 bytes are the disk ID. Search all devices to find the + * disk with this disk ID. + */ + devices = guestfs_list_devices (g); + if (devices == NULL) + goto out; + + for (i = 0; devices[i] != NULL; ++i) { + /* Read the disk ID. */ + diskid = guestfs_pread_device (g, devices[i], 4, 0x01b8, &len); + if (diskid == NULL) + continue; + if (len < 4) { + free (diskid); + continue; + } + if (memcmp (diskid, blob, 4) == 0) { /* found it */ + free (diskid); + goto found_disk; + } + free (diskid); + } + goto out; + + found_disk: + /* Next 8 bytes are the offset of the partition in bytes(!) given as + * a 64 bit little endian number. Luckily it's easy to get the + * partition byte offset from guestfs_part_list. + */ + part_offset = le64toh (* (uint64_t *) &blob[4]); + + partitions = guestfs_part_list (g, devices[i]); + if (partitions == NULL) + goto out; + + for (j = 0; j < partitions->len; ++j) { + if (partitions->val[j].part_start == part_offset) /* found it */ + goto found_partition; + } + goto out; + + found_partition: + /* Construct the full device name. */ + ret = safe_asprintf (g, "%s%d", devices[i], partitions->val[j].part_num); + + out: + if (devices) + guestfs___free_string_list (devices); + if (partitions) + guestfs_free_partition_list (partitions); + return ret; +} + static char * resolve_windows_path_silently (guestfs_h *g, const char *path) { @@ -2178,6 +2312,42 @@ guestfs__inspect_get_filesystems (guestfs_h *g, const char *root) return ret; } +char ** +guestfs__inspect_get_drive_mappings (guestfs_h *g, const char *root) +{ + char **ret; + size_t i, count; + struct inspect_fs *fs; + + fs = search_for_root (g, root); + if (!fs) + return NULL; + + /* If no drive mappings, return an empty hashtable. */ + if (!fs->drive_mappings) + count = 0; + else { + for (count = 0; fs->drive_mappings[count] != NULL; count++) + ; + } + + ret = calloc (count+1, sizeof (char *)); + if (ret == NULL) { + perrorf (g, "calloc"); + return NULL; + } + + /* We need to make a deep copy of the hashtable since the caller + * will free it. + */ + for (i = 0; i < count; ++i) + ret[i] = safe_strdup (g, fs->drive_mappings[i]); + + ret[count] = NULL; + + return ret; +} + char * guestfs__inspect_get_package_format (guestfs_h *g, const char *root) { @@ -2972,6 +3142,12 @@ guestfs__inspect_get_filesystems (guestfs_h *g, const char *root) NOT_IMPL(NULL); } +char ** +guestfs__inspect_get_drive_mappings (guestfs_h *g, const char *root) +{ + NOT_IMPL(NULL); +} + char * guestfs__inspect_get_package_format (guestfs_h *g, const char *root) { @@ -3026,6 +3202,8 @@ void guestfs___free_inspect_info (guestfs_h *g) { size_t i; + size_t j; + for (i = 0; i < g->nr_fses; ++i) { free (g->fses[i].device); free (g->fses[i].product_name); @@ -3034,12 +3212,13 @@ guestfs___free_inspect_info (guestfs_h *g) free (g->fses[i].hostname); free (g->fses[i].windows_systemroot); free (g->fses[i].windows_current_control_set); - size_t j; for (j = 0; j < g->fses[i].nr_fstab; ++j) { free (g->fses[i].fstab[j].device); free (g->fses[i].fstab[j].mountpoint); } free (g->fses[i].fstab); + if (g->fses[i].drive_mappings) + guestfs___free_string_list (g->fses[i].drive_mappings); } free (g->fses); g->nr_fses = 0; -- 1.7.4.1
Seemingly Similar Threads
- [PATCH 0/7] Add libvirt domain to core API
- [PATCH 0/8 v2 DISCUSSION ONLY] Connecting to live virtual machines
- [PATCH febootstrap 0/8] Add support for building an ext2-based appliance
- [PATCH 0/7] Prepare for adding write support to hivex (windows registry) library
- [PATCH 0/13 v2] Prepare for adding write support to hivex (Windows registry) library