[Libguestfs] [PATCH 2/2] New API: inspect-get-drive-mappings

Richard W.M. Jones rjones at redhat.com
Tue Apr 5 13:40:22 UTC 2011


-- 
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 2001
From: 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



More information about the Libguestfs mailing list