summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRichard W.M. Jones <rjones@redhat.com>2011-04-05 14:03:08 +0100
committerRichard W.M. Jones <rjones@redhat.com>2011-04-05 20:01:57 +0100
commit13d8963d8c0203e1f72c519e5acf79ebf7cccb4c (patch)
tree0699b86ada86e692a24294e7ca719d18c702e13e
parent9e7c6a548353bde925c16434711bcc6208038d83 (diff)
downloadlibguestfs-13d8963d8c0203e1f72c519e5acf79ebf7cccb4c.tar.gz
libguestfs-13d8963d8c0203e1f72c519e5acf79ebf7cccb4c.tar.xz
libguestfs-13d8963d8c0203e1f72c519e5acf79ebf7cccb4c.zip
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>
-rw-r--r--generator/generator_actions.ml42
-rwxr-xr-ximages/guest-aux/make-windows-img.sh3
-rwxr-xr-ximages/guest-aux/windows-systembin12288 -> 12288 bytes
-rw-r--r--images/guest-aux/windows-system.reg3
-rw-r--r--inspector/example-windows.xml3
-rw-r--r--inspector/virt-inspector.c54
-rw-r--r--inspector/virt-inspector.rng13
-rw-r--r--src/guestfs-internal.h1
-rw-r--r--src/inspect.c180
9 files changed, 298 insertions, 1 deletions
diff --git a/generator/generator_actions.ml b/generator/generator_actions.ml
index c59964ab..26dc64e4 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 15cb7c53..f659f4b4 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 7d935b02..3a232142 100755
--- a/images/guest-aux/windows-system
+++ b/images/guest-aux/windows-system
Binary files differ
diff --git a/images/guest-aux/windows-system.reg b/images/guest-aux/windows-system.reg
index 6cb5d324..5478d2de 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 8e53159a..8b3b8a70 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 d016b2dd..7724be65 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 7a822e68..669e8bc6 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 b29fa577..46576bb2 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 154207b9..be98b0dd 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 *case_sensitive_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 *
case_sensitive_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)
{
@@ -3040,6 +3216,8 @@ guestfs___free_inspect_info (guestfs_h *g)
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;