2 * Copyright (C) 2010-2011 Red Hat Inc.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
41 #include "ignore-value.h"
45 #include "guestfs-internal.h"
46 #include "guestfs-internal-actions.h"
47 #include "guestfs_protocol.h"
49 #if defined(HAVE_PCRE) && defined(HAVE_HIVEX)
51 /* Compile all the regular expressions once when the shared library is
52 * loaded. PCRE is thread safe so we're supposedly OK here if
53 * multiple threads call into the libguestfs API functions below
56 static pcre *re_windows_version;
58 static void compile_regexps (void) __attribute__((constructor));
59 static void free_regexps (void) __attribute__((destructor));
62 compile_regexps (void)
67 #define COMPILE(re,pattern,options) \
69 re = pcre_compile ((pattern), (options), &err, &offset, NULL); \
71 ignore_value (write (2, err, strlen (err))); \
76 COMPILE (re_windows_version, "^(\\d+)\\.(\\d+)", 0);
82 pcre_free (re_windows_version);
85 static int check_windows_arch (guestfs_h *g, struct inspect_fs *fs);
86 static int check_windows_software_registry (guestfs_h *g, struct inspect_fs *fs);
87 static int check_windows_system_registry (guestfs_h *g, struct inspect_fs *fs);
88 static char *map_registry_disk_blob (guestfs_h *g, const char *blob);
90 /* XXX Handling of boot.ini in the Perl version was pretty broken. It
91 * essentially didn't do anything for modern Windows guests.
92 * Therefore I've omitted all that code.
95 /* Try to find Windows systemroot using some common locations.
99 * (1) We check for some directories inside to see if it is a real
100 * systemroot, and not just a directory that happens to have the same
103 * (2) If a Windows guest has multiple disks and applications are
104 * installed on those other disks, then those other disks will contain
105 * "/Program Files" and "/System Volume Information". Those would
106 * *not* be Windows root disks. (RHBZ#674130)
108 static const char *systemroots[] =
109 { "/windows", "/winnt", "/win32", "/win", NULL };
112 guestfs___has_windows_systemroot (guestfs_h *g)
115 char *systemroot, *p;
118 for (i = 0; i < sizeof systemroots / sizeof systemroots[0]; ++i) {
119 systemroot = guestfs___case_sensitive_path_silently (g, systemroots[i]);
123 snprintf (path, sizeof path, "%s/system32", systemroot);
124 if (!guestfs___is_dir_nocase (g, path)) {
129 snprintf (path, sizeof path, "%s/system32/config", systemroot);
130 if (!guestfs___is_dir_nocase (g, path)) {
135 snprintf (path, sizeof path, "%s/system32/cmd.exe", systemroot);
136 if (!guestfs___is_file_nocase (g, path)) {
146 return -1; /* not found */
150 guestfs___check_windows_root (guestfs_h *g, struct inspect_fs *fs)
155 fs->type = OS_TYPE_WINDOWS;
156 fs->distro = OS_DISTRO_WINDOWS;
158 i = guestfs___has_windows_systemroot (g);
160 error (g, "check_windows_root: has_windows_systemroot unexpectedly returned -1");
164 systemroot = guestfs___case_sensitive_path_silently (g, systemroots[i]);
166 error (g, _("cannot resolve Windows %%SYSTEMROOT%%"));
170 debug (g, "windows %%SYSTEMROOT%% = %s", systemroot);
172 /* Freed by guestfs___free_inspect_info. */
173 fs->windows_systemroot = systemroot;
175 if (check_windows_arch (g, fs) == -1)
178 /* Product name and version. */
179 if (check_windows_software_registry (g, fs) == -1)
183 if (check_windows_system_registry (g, fs) == -1)
190 check_windows_arch (guestfs_h *g, struct inspect_fs *fs)
192 size_t len = strlen (fs->windows_systemroot) + 32;
194 snprintf (cmd_exe, len, "%s/system32/cmd.exe", fs->windows_systemroot);
196 char *cmd_exe_path = guestfs___case_sensitive_path_silently (g, cmd_exe);
200 char *arch = guestfs_file_architecture (g, cmd_exe_path);
204 fs->arch = arch; /* freed by guestfs___free_inspect_info */
209 /* At the moment, pull just the ProductName and version numbers from
210 * the registry. In future there is a case for making many more
211 * registry fields available to callers.
214 check_windows_software_registry (guestfs_h *g, struct inspect_fs *fs)
216 size_t len = strlen (fs->windows_systemroot) + 64;
218 snprintf (software, len, "%s/system32/config/software",
219 fs->windows_systemroot);
221 char *software_path = guestfs___case_sensitive_path_silently (g, software);
223 /* If the software hive doesn't exist, just accept that we cannot
224 * find product_name etc.
228 char *software_hive = NULL;
231 hive_value_h *values = NULL;
233 software_hive = guestfs___download_to_tmp (g, fs, software_path, "software",
235 if (software_hive == NULL)
238 h = hivex_open (software_hive, g->verbose ? HIVEX_OPEN_VERBOSE : 0);
240 perrorf (g, "hivex_open");
244 hive_node_h node = hivex_root (h);
245 const char *hivepath[] =
246 { "Microsoft", "Windows NT", "CurrentVersion" };
249 node != 0 && i < sizeof hivepath / sizeof hivepath[0];
251 node = hivex_node_get_child (h, node, hivepath[i]);
255 perrorf (g, "hivex: cannot locate HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion");
259 values = hivex_node_values (h, node);
261 for (i = 0; values[i] != 0; ++i) {
262 char *key = hivex_value_key (h, values[i]);
264 perrorf (g, "hivex_value_key");
268 if (STRCASEEQ (key, "ProductName")) {
269 fs->product_name = hivex_value_string (h, values[i]);
270 if (!fs->product_name) {
271 perrorf (g, "hivex_value_string");
276 else if (STRCASEEQ (key, "CurrentVersion")) {
277 char *version = hivex_value_string (h, values[i]);
279 perrorf (g, "hivex_value_string");
284 if (match2 (g, version, re_windows_version, &major, &minor)) {
285 fs->major_version = guestfs___parse_unsigned_int (g, major);
287 if (fs->major_version == -1) {
293 fs->minor_version = guestfs___parse_unsigned_int (g, minor);
295 if (fs->minor_version == -1) {
304 else if (STRCASEEQ (key, "InstallationType")) {
305 fs->product_variant = hivex_value_string (h, values[i]);
306 if (!fs->product_variant) {
307 perrorf (g, "hivex_value_string");
319 if (h) hivex_close (h);
321 free (software_path);
322 free (software_hive);
328 check_windows_system_registry (guestfs_h *g, struct inspect_fs *fs)
330 size_t len = strlen (fs->windows_systemroot) + 64;
332 snprintf (system, len, "%s/system32/config/system",
333 fs->windows_systemroot);
335 char *system_path = guestfs___case_sensitive_path_silently (g, system);
337 /* If the system hive doesn't exist, just accept that we cannot
342 char *system_hive = NULL;
345 hive_node_h root, node;
346 hive_value_h value, *values = NULL;
351 guestfs___download_to_tmp (g, fs, system_path, "system",
353 if (system_hive == NULL)
356 h = hivex_open (system_hive, g->verbose ? HIVEX_OPEN_VERBOSE : 0);
358 perrorf (g, "hivex_open");
362 root = hivex_root (h);
364 perrorf (g, "hivex_root");
368 /* Get the CurrentControlSet. */
370 node = hivex_node_get_child (h, root, "Select");
373 perrorf (g, "hivex_node_get_child");
375 error (g, "hivex: could not locate HKLM\\SYSTEM\\Select");
380 value = hivex_node_get_value (h, node, "Current");
383 perrorf (g, "hivex_node_get_value");
385 error (g, "hivex: HKLM\\System\\Select Default entry not found.");
389 /* XXX Should check the type. */
390 dword = hivex_value_dword (h, value);
391 fs->windows_current_control_set = safe_asprintf (g, "ControlSet%03d", dword);
393 /* Get the drive mappings.
394 * This page explains the contents of HKLM\System\MountedDevices:
395 * http://www.goodells.net/multiboot/partsigs.shtml
398 node = hivex_node_get_child (h, root, "MountedDevices");
401 perrorf (g, "hivex_node_get_child");
403 error (g, "hivex: could not locate HKLM\\SYSTEM\\MountedDevices");
407 values = hivex_node_values (h, node);
409 /* Count how many DOS drive letter mappings there are. This doesn't
410 * ignore removable devices, so it overestimates, but that doesn't
411 * matter because it just means we'll allocate a few bytes extra.
413 for (i = count = 0; values[i] != 0; ++i) {
414 char *key = hivex_value_key (h, values[i]);
416 perrorf (g, "hivex_value_key");
419 if (STRCASEEQLEN (key, "\\DosDevices\\", 12) &&
420 c_isalpha (key[12]) && key[13] == ':')
425 fs->drive_mappings = calloc (2*count + 1, sizeof (char *));
426 if (fs->drive_mappings == NULL) {
427 perrorf (g, "calloc");
431 for (i = count = 0; values[i] != 0; ++i) {
432 char *key = hivex_value_key (h, values[i]);
434 perrorf (g, "hivex_value_key");
437 if (STRCASEEQLEN (key, "\\DosDevices\\", 12) &&
438 c_isalpha (key[12]) && key[13] == ':') {
439 /* Get the binary value. Is it a fixed disk? */
444 blob = hivex_value_value (h, values[i], &type, &len);
445 if (blob != NULL && type == 3 && len == 12) {
446 /* Try to map the blob to a known disk and partition. */
447 device = map_registry_disk_blob (g, blob);
448 if (device != NULL) {
449 fs->drive_mappings[count++] = safe_strndup (g, &key[12], 1);
450 fs->drive_mappings[count++] = device;
458 /* Get the hostname. */
459 const char *hivepath[] =
460 { fs->windows_current_control_set, "Services", "Tcpip", "Parameters" };
461 for (node = root, i = 0;
462 node != 0 && i < sizeof hivepath / sizeof hivepath[0];
464 node = hivex_node_get_child (h, node, hivepath[i]);
468 perrorf (g, "hivex: cannot locate HKLM\\SYSTEM\\%s\\Services\\Tcpip\\Parameters",
469 fs->windows_current_control_set);
474 values = hivex_node_values (h, node);
476 for (i = 0; values[i] != 0; ++i) {
477 char *key = hivex_value_key (h, values[i]);
479 perrorf (g, "hivex_value_key");
483 if (STRCASEEQ (key, "Hostname")) {
484 fs->hostname = hivex_value_string (h, values[i]);
486 perrorf (g, "hivex_value_string");
491 /* many other interesting fields here ... */
499 if (h) hivex_close (h);
507 /* Windows Registry HKLM\SYSTEM\MountedDevices uses a blob of data
508 * to store partitions. This blob is described here:
509 * http://www.goodells.net/multiboot/partsigs.shtml
510 * The following function maps this blob to a libguestfs partition
514 map_registry_disk_blob (guestfs_h *g, const char *blob)
516 char **devices = NULL;
517 struct guestfs_partition_list *partitions = NULL;
521 uint64_t part_offset;
523 /* First 4 bytes are the disk ID. Search all devices to find the
524 * disk with this disk ID.
526 devices = guestfs_list_devices (g);
530 for (i = 0; devices[i] != NULL; ++i) {
531 /* Read the disk ID. */
532 diskid = guestfs_pread_device (g, devices[i], 4, 0x01b8, &len);
539 if (memcmp (diskid, blob, 4) == 0) { /* found it */
548 /* Next 8 bytes are the offset of the partition in bytes(!) given as
549 * a 64 bit little endian number. Luckily it's easy to get the
550 * partition byte offset from guestfs_part_list.
552 part_offset = le64toh (* (uint64_t *) &blob[4]);
554 partitions = guestfs_part_list (g, devices[i]);
555 if (partitions == NULL)
558 for (j = 0; j < partitions->len; ++j) {
559 if (partitions->val[j].part_start == part_offset) /* found it */
560 goto found_partition;
565 /* Construct the full device name. */
566 ret = safe_asprintf (g, "%s%d", devices[i], partitions->val[j].part_num);
570 guestfs___free_string_list (devices);
572 guestfs_free_partition_list (partitions);
577 guestfs___case_sensitive_path_silently (guestfs_h *g, const char *path)
579 guestfs_error_handler_cb old_error_cb = g->error_cb;
581 char *ret = guestfs_case_sensitive_path (g, path);
582 g->error_cb = old_error_cb;
586 #endif /* defined(HAVE_PCRE) && defined(HAVE_HIVEX) */