/* Try several methods to determine the hostname from a Linux or * FreeBSD guest. Note that type and distro have been set, so we can * use that information to direct the search. */ static int check_hostname_unix (guestfs_h *g, struct inspect_fs *fs) { switch (fs->type) { case OS_TYPE_LINUX: case OS_TYPE_HURD: /* Red Hat-derived would be in /etc/sysconfig/network or * /etc/hostname (RHEL 7+, F18+). Debian-derived in the file * /etc/hostname. Very old Debian and SUSE use /etc/HOSTNAME. * It's best to just look for each of these files in turn, rather * than try anything clever based on distro. */ if (guestfs_is_file (g, "/etc/HOSTNAME")) { fs->hostname = guestfs___first_line_of_file (g, "/etc/HOSTNAME"); if (fs->hostname == NULL) return -1; if (STREQ (fs->hostname, "")) { free (fs->hostname); fs->hostname = NULL; } } if (!fs->hostname && guestfs_is_file (g, "/etc/hostname")) { fs->hostname = guestfs___first_line_of_file (g, "/etc/hostname"); if (fs->hostname == NULL) return -1; if (STREQ (fs->hostname, "")) { free (fs->hostname); fs->hostname = NULL; } } if (!fs->hostname && guestfs_is_file (g, "/etc/sysconfig/network")) { const char *configfiles[] = { "/etc/sysconfig/network", NULL }; if (inspect_with_augeas (g, fs, configfiles, check_hostname_redhat) == -1) return -1; } break; case OS_TYPE_FREEBSD: case OS_TYPE_NETBSD: /* /etc/rc.conf contains the hostname, but there is no Augeas lens * for this file. */ if (guestfs_is_file (g, "/etc/rc.conf")) { if (check_hostname_freebsd (g, fs) == -1) return -1; } break; case OS_TYPE_WINDOWS: /* not here, see check_windows_system_registry */ case OS_TYPE_DOS: case OS_TYPE_OPENBSD: case OS_TYPE_UNKNOWN: /* nothing */; } return 0; }
/* There are several sources we might use: * - /ProgramData/Microsoft/Windows Live/WLive48x48.png * - w-brand.png (in a very long directory name) * - /Windows/System32/slui.exe --type=14 group icon #2 */ static char * icon_windows_8 (guestfs_h *g, struct inspect_fs *fs, size_t *size_r) { CLEANUP_FREE char *filename_case = NULL; CLEANUP_FREE char *filename_downloaded = NULL; int r; char *ret; filename_case = guestfs___case_sensitive_path_silently (g, "/ProgramData/Microsoft/Windows Live/WLive48x48.png"); if (filename_case == NULL) return NOT_FOUND; /* Not an error since a parent dir might not exist. */ guestfs_push_error_handler (g, NULL, NULL); r = guestfs_is_file (g, filename_case); guestfs_pop_error_handler (g); if (r == -1) return NULL; if (r == 0) return NOT_FOUND; filename_downloaded = guestfs___download_to_tmp (g, fs, filename_case, "wlive48x48.png", 8192); if (filename_downloaded == NULL) return NOT_FOUND; if (read_whole_file (g, filename_downloaded, &ret, size_r) == -1) return NULL; return ret; }
static void check_architecture (guestfs_h *g, struct inspect_fs *fs) { const char *binaries[] = { "/bin/bash", "/bin/ls", "/bin/echo", "/bin/rm", "/bin/sh" }; size_t i; char *arch; for (i = 0; i < sizeof binaries / sizeof binaries[0]; ++i) { if (guestfs_is_file (g, binaries[i]) > 0) { /* Ignore errors from file_architecture call. */ guestfs_push_error_handler (g, NULL, NULL); arch = guestfs_file_architecture (g, binaries[i]); guestfs_pop_error_handler (g); if (arch) { /* String will be owned by handle, freed by * guestfs___free_inspect_info. */ fs->arch = arch; break; } } } }
/* The currently mounted device is very likely to be an installer. */ int guestfs___check_installer_root (guestfs_h *g, struct inspect_fs *fs) { /* The presence of certain files indicates a live CD. * * XXX Fedora netinst contains a ~120MB squashfs called * /images/install.img. However this is not a live CD (unlike the * Fedora live CDs which contain the same, but larger file). We * need to unpack this and look inside to tell the difference. */ if (guestfs_is_file (g, "/casper/filesystem.squashfs") > 0) fs->is_live_disk = 1; /* Debian/Ubuntu. */ if (guestfs_is_file (g, "/.disk/info") > 0) { if (check_debian_installer_root (g, fs) == -1) return -1; } /* Fedora CDs and DVD (not netinst). */ else if (guestfs_is_file (g, "/.treeinfo") > 0) { if (check_fedora_installer_root (g, fs) == -1) return -1; } /* FreeDOS install CD. */ else if (guestfs_is_file (g, "/freedos/freedos.ico") > 0 && guestfs_is_file (g, "/setup.bat") > 0) { fs->type = OS_TYPE_DOS; fs->distro = OS_DISTRO_FREEDOS; fs->arch = safe_strdup (g, "i386"); } /* Linux with /isolinux/isolinux.cfg (note that non-Linux can use * ISOLINUX too, eg. FreeDOS). */ else if (guestfs_is_file (g, "/isolinux/isolinux.cfg") > 0) { if (check_isolinux_installer_root (g, fs) == -1) return -1; } /* Windows 2003 64 bit */ else if (guestfs_is_file (g, "/amd64/txtsetup.sif") > 0) { fs->arch = safe_strdup (g, "x86_64"); if (check_w2k3_installer_root (g, fs, "/amd64/txtsetup.sif") == -1) return -1; } /* Windows 2003 32 bit */ else if (guestfs_is_file (g, "/i386/txtsetup.sif") > 0) { fs->arch = safe_strdup (g, "i386"); if (check_w2k3_installer_root (g, fs, "/i386/txtsetup.sif") == -1) return -1; } return 0; }
int guestfs_int_is_file_nocase (guestfs_h *g, const char *path) { CLEANUP_FREE char *p = NULL; int r; p = guestfs_int_case_sensitive_path_silently (g, path); if (!p) return 0; r = guestfs_is_file (g, p); return r > 0; }
static char * icon_windows_7 (guestfs_h *g, struct inspect_fs *fs, size_t *size_r) { CLEANUP_FREE char *filename = NULL; CLEANUP_FREE char *filename_case = NULL; CLEANUP_FREE char *filename_downloaded = NULL; CLEANUP_FREE char *pngfile = NULL; CLEANUP_CMD_CLOSE struct command *cmd = guestfs___new_command (g); int r; char *ret; /* Download %systemroot%\explorer.exe */ filename = safe_asprintf (g, "%s/explorer.exe", fs->windows_systemroot); filename_case = guestfs_case_sensitive_path (g, filename); if (filename_case == NULL) return NULL; guestfs_push_error_handler (g, NULL, NULL); r = guestfs_is_file (g, filename_case); guestfs_pop_error_handler (g); if (r == -1) return NULL; if (r == 0) return NOT_FOUND; filename_downloaded = guestfs___download_to_tmp (g, fs, filename_case, "explorer.exe", MAX_WINDOWS_EXPLORER_SIZE); if (filename_downloaded == NULL) return NOT_FOUND; pngfile = safe_asprintf (g, "%s/windows-7-icon.png", g->tmpdir); guestfs___cmd_add_string_unquoted (cmd, WRESTOOL " -x --type=2 --name=6801 "); guestfs___cmd_add_string_quoted (cmd, filename_downloaded); guestfs___cmd_add_string_unquoted (cmd, " | " BMPTOPNM " | " PAMCUT " -bottom 54 | " PNMTOPNG " > "); guestfs___cmd_add_string_quoted (cmd, pngfile); r = guestfs___cmd_run (cmd); if (r == -1) return NULL; if (!WIFEXITED (r) || WEXITSTATUS (r) != 0) return NOT_FOUND; if (read_whole_file (g, pngfile, &ret, size_r) == -1) return NULL; return ret; }
/* Debian/Ubuntu install disks are easy ... * * These files are added by the debian-cd program, and it is worth * looking at the source code to determine exact values, in * particular '/usr/share/debian-cd/tools/start_new_disc' * * XXX Architecture? We could parse it out of the product name * string, but that seems quite hairy. We could look for the names * of packages. Also note that some Debian install disks are * multiarch. */ static int check_debian_installer_root (guestfs_h *g, struct inspect_fs *fs) { fs->product_name = guestfs___first_line_of_file (g, "/.disk/info"); if (!fs->product_name) return -1; fs->type = OS_TYPE_LINUX; if (STRPREFIX (fs->product_name, "Ubuntu")) fs->distro = OS_DISTRO_UBUNTU; else if (STRPREFIX (fs->product_name, "Debian")) fs->distro = OS_DISTRO_DEBIAN; (void) guestfs___parse_major_minor (g, fs); if (guestfs_is_file (g, "/.disk/cd_type") > 0) { char *cd_type = guestfs___first_line_of_file (g, "/.disk/cd_type"); if (!cd_type) return -1; if (STRPREFIX (cd_type, "dvd/single") || STRPREFIX (cd_type, "full_cd/single")) { fs->is_multipart_disk = 0; fs->is_netinst_disk = 0; } else if (STRPREFIX (cd_type, "dvd") || STRPREFIX (cd_type, "full_cd")) { fs->is_multipart_disk = 1; fs->is_netinst_disk = 0; } else if (STRPREFIX (cd_type, "not_complete")) { fs->is_multipart_disk = 0; fs->is_netinst_disk = 1; } free (cd_type); } return 0; }
static int check_filesystem (guestfs_h *g, const char *mountable, const struct guestfs_internal_mountable *m, int whole_device) { int partnum = -1, nr_partitions = -1; /* Not CLEANUP_FREE, as it will be cleaned up with inspection info */ char *windows_systemroot = NULL; extend_fses (g); if (!whole_device && m->im_type == MOUNTABLE_DEVICE && guestfs_int_is_partition (g, m->im_device)) { if (get_partition_context (g, m->im_device, &partnum, &nr_partitions) == -1) return -1; } struct inspect_fs *fs = &g->fses[g->nr_fses-1]; fs->mountable = safe_strdup (g, mountable); /* Optimize some of the tests by avoiding multiple tests of the same thing. */ const int is_dir_etc = guestfs_is_dir (g, "/etc") > 0; const int is_dir_bin = guestfs_is_dir (g, "/bin") > 0; const int is_dir_share = guestfs_is_dir (g, "/share") > 0; /* Grub /boot? */ if (guestfs_is_file (g, "/grub/menu.lst") > 0 || guestfs_is_file (g, "/grub/grub.conf") > 0 || guestfs_is_file (g, "/grub2/grub.cfg") > 0) ; /* FreeBSD root? */ else if (is_dir_etc && is_dir_bin && guestfs_is_file (g, "/etc/freebsd-update.conf") > 0 && guestfs_is_file (g, "/etc/fstab") > 0) { fs->role = OS_ROLE_ROOT; fs->format = OS_FORMAT_INSTALLED; if (guestfs_int_check_freebsd_root (g, fs) == -1) return -1; } /* NetBSD root? */ else if (is_dir_etc && is_dir_bin && guestfs_is_file (g, "/netbsd") > 0 && guestfs_is_file (g, "/etc/fstab") > 0 && guestfs_is_file (g, "/etc/release") > 0) { fs->role = OS_ROLE_ROOT; fs->format = OS_FORMAT_INSTALLED; if (guestfs_int_check_netbsd_root (g, fs) == -1) return -1; } /* OpenBSD root? */ else if (is_dir_etc && is_dir_bin && guestfs_is_file (g, "/bsd") > 0 && guestfs_is_file (g, "/etc/fstab") > 0 && guestfs_is_file (g, "/etc/motd") > 0) { fs->role = OS_ROLE_ROOT; fs->format = OS_FORMAT_INSTALLED; if (guestfs_int_check_openbsd_root (g, fs) == -1) return -1; } /* Hurd root? */ else if (guestfs_is_file (g, "/hurd/console") > 0 && guestfs_is_file (g, "/hurd/hello") > 0 && guestfs_is_file (g, "/hurd/null") > 0) { fs->role = OS_ROLE_ROOT; fs->format = OS_FORMAT_INSTALLED; /* XXX could be more specific */ if (guestfs_int_check_hurd_root (g, fs) == -1) return -1; } /* Minix root? */ else if (is_dir_etc && is_dir_bin && guestfs_is_file (g, "/service/vm") > 0 && guestfs_is_file (g, "/etc/fstab") > 0 && guestfs_is_file (g, "/etc/version") > 0) { fs->role = OS_ROLE_ROOT; fs->format = OS_FORMAT_INSTALLED; if (guestfs_int_check_minix_root (g, fs) == -1) return -1; } /* Linux root? */ else if (is_dir_etc && (is_dir_bin || is_symlink_to (g, "/bin", "usr/bin") > 0) && (guestfs_is_file (g, "/etc/fstab") > 0 || guestfs_is_file (g, "/etc/hosts") > 0)) { fs->role = OS_ROLE_ROOT; fs->format = OS_FORMAT_INSTALLED; if (guestfs_int_check_linux_root (g, fs) == -1) return -1; } /* CoreOS root? */ else if (is_dir_etc && guestfs_is_dir (g, "/root") > 0 && guestfs_is_dir (g, "/home") > 0 && guestfs_is_dir (g, "/usr") > 0 && guestfs_is_file (g, "/etc/coreos/update.conf") > 0) { fs->role = OS_ROLE_ROOT; fs->format = OS_FORMAT_INSTALLED; if (guestfs_int_check_coreos_root (g, fs) == -1) return -1; } /* Linux /usr/local? */ else if (is_dir_etc && is_dir_bin && is_dir_share && guestfs_is_dir (g, "/local") == 0 && guestfs_is_file (g, "/etc/fstab") == 0) ; /* Linux /usr? */ else if (is_dir_etc && is_dir_bin && is_dir_share && guestfs_is_dir (g, "/local") > 0 && guestfs_is_file (g, "/etc/fstab") == 0) { if (guestfs_int_check_linux_usr (g, fs) == -1) return -1; } /* CoreOS /usr? */ else if (is_dir_bin && is_dir_share && guestfs_is_dir (g, "/local") > 0 && guestfs_is_dir (g, "/share/coreos") > 0) { if (guestfs_int_check_coreos_usr (g, fs) == -1) return -1; } /* Linux /var? */ else if (guestfs_is_dir (g, "/log") > 0 && guestfs_is_dir (g, "/run") > 0 && guestfs_is_dir (g, "/spool") > 0) ; /* Windows root? */ else if ((windows_systemroot = guestfs_int_get_windows_systemroot (g)) != NULL) { fs->role = OS_ROLE_ROOT; fs->format = OS_FORMAT_INSTALLED; if (guestfs_int_check_windows_root (g, fs, windows_systemroot) == -1) return -1; } /* Windows volume with installed applications (but not root)? */ else if (guestfs_int_is_dir_nocase (g, "/System Volume Information") > 0 && guestfs_int_is_dir_nocase (g, "/Program Files") > 0) ; /* Windows volume (but not root)? */ else if (guestfs_int_is_dir_nocase (g, "/System Volume Information") > 0) ; /* FreeDOS? */ else if (guestfs_int_is_dir_nocase (g, "/FDOS") > 0 && guestfs_int_is_file_nocase (g, "/FDOS/FREEDOS.BSS") > 0) { fs->role = OS_ROLE_ROOT; fs->format = OS_FORMAT_INSTALLED; fs->type = OS_TYPE_DOS; fs->distro = OS_DISTRO_FREEDOS; /* FreeDOS is a mix of 16 and 32 bit, but assume it requires a * 32 bit i386 processor. */ fs->arch = safe_strdup (g, "i386"); } /* Install CD/disk? * * Note that we checked (above) for an install ISO, but there are * other types of install image (eg. USB keys) which that check * wouldn't have picked up. * * Skip these checks if it's not a whole device (eg. CD) or the * first partition (eg. bootable USB key). */ else if ((whole_device || (partnum == 1 && nr_partitions == 1)) && (guestfs_is_file (g, "/isolinux/isolinux.cfg") > 0 || guestfs_is_dir (g, "/EFI/BOOT") > 0 || guestfs_is_file (g, "/images/install.img") > 0 || guestfs_is_dir (g, "/.disk") > 0 || guestfs_is_file (g, "/.discinfo") > 0 || guestfs_is_file (g, "/i386/txtsetup.sif") > 0 || guestfs_is_file (g, "/amd64/txtsetup.sif") > 0 || guestfs_is_file (g, "/freedos/freedos.ico") > 0 || guestfs_is_file (g, "/boot/loader.rc") > 0)) { fs->role = OS_ROLE_ROOT; fs->format = OS_FORMAT_INSTALLER; if (guestfs_int_check_installer_root (g, fs) == -1) return -1; } /* The above code should have set fs->type and fs->distro fields, so * we can now guess the package management system. */ guestfs_int_check_package_format (g, fs); guestfs_int_check_package_management (g, fs); return 0; }
int run_copy_out (const char *cmd, size_t argc, char *argv[]) { if (argc < 2) { fprintf (stderr, _("use 'copy-out <remote> [<remote>...] <localdir>' to copy files out of the image\n")); return -1; } /* Local directory is always the last arg. */ const char *local = argv[argc-1]; int nr_remotes = argc-1; struct stat statbuf; if (stat (local, &statbuf) == -1 || ! (S_ISDIR (statbuf.st_mode))) { fprintf (stderr, _("copy-out: target '%s' is not a directory\n"), local); return -1; } /* Download each remote one at a time using tar-out. */ int i, r; for (i = 0; i < nr_remotes; ++i) { char *remote = argv[i]; /* Allow win:... prefix on remotes. */ remote = win_prefix (remote); if (remote == NULL) return -1; /* If the remote is a file, download it. If it's a directory, * create the directory in local first before using tar-out. */ r = guestfs_is_file (g, remote); if (r == -1) { free (remote); return -1; } if (r == 1) { /* is file */ char buf[PATH_MAX]; const char *basename; if (split_path (buf, sizeof buf, remote, NULL, &basename) == -1) { free (remote); return -1; } char filename[PATH_MAX]; snprintf (filename, sizeof filename, "%s/%s", local, basename); if (guestfs_download (g, remote, filename) == -1) { free (remote); return -1; } } else { /* not a regular file */ r = guestfs_is_dir (g, remote); if (r == -1) { free (remote); return -1; } if (r == 0) { fprintf (stderr, _("copy-out: '%s' is not a file or directory\n"), remote); free (remote); return -1; } char buf[PATH_MAX]; const char *basename; if (split_path (buf, sizeof buf, remote, NULL, &basename) == -1) { free (remote); return -1; } struct fd_pid fdpid = make_tar_output (local, basename); if (fdpid.fd == -1) { free (remote); return -1; } char fdbuf[64]; snprintf (fdbuf, sizeof fdbuf, "/dev/fd/%d", fdpid.fd); int r = guestfs_tar_out (g, remote, fdbuf); if (close (fdpid.fd) == -1) { perror ("close (tar-output subprocess)"); free (remote); r = -1; } int status; if (waitpid (fdpid.pid, &status, 0) == -1) { perror ("wait (tar-output subprocess)"); free (remote); return -1; } if (!(WIFEXITED (status) && WEXITSTATUS (status) == 0)) { free (remote); return -1; } if (r == -1) { free (remote); return -1; } } free (remote); } return 0; }
static int check_windows_system_registry (guestfs_h *g, struct inspect_fs *fs) { int r; size_t len = strlen (fs->windows_systemroot) + 64; char system[len]; snprintf (system, len, "%s/system32/config/system", fs->windows_systemroot); CLEANUP_FREE char *system_path = guestfs_case_sensitive_path (g, system); if (!system_path) return -1; r = guestfs_is_file (g, system_path); if (r == -1) return -1; /* If the system hive doesn't exist, just accept that we cannot * find hostname etc. */ if (r == 0) return 0; int ret = -1; int64_t root, node, value; CLEANUP_FREE_HIVEX_VALUE_LIST struct guestfs_hivex_value_list *values = NULL; CLEANUP_FREE_HIVEX_VALUE_LIST struct guestfs_hivex_value_list *values2 = NULL; int32_t dword; size_t i, count; CLEANUP_FREE void *buf = NULL; size_t buflen; const char *hivepath[] = { NULL /* current control set */, "Services", "Tcpip", "Parameters" }; if (guestfs_hivex_open (g, system_path, GUESTFS_HIVEX_OPEN_VERBOSE, g->verbose, -1) == -1) goto out; root = guestfs_hivex_root (g); if (root == 0) goto out; /* Get the CurrentControlSet. */ node = guestfs_hivex_node_get_child (g, root, "Select"); if (node == -1) goto out; if (node == 0) { error (g, "hivex: could not locate HKLM\\SYSTEM\\Select"); goto out; } value = guestfs_hivex_node_get_value (g, node, "Current"); if (value == -1) goto out; if (value == 0) { error (g, "hivex: HKLM\\System\\Select Default entry not found"); goto out; } /* XXX Should check the type. */ buf = guestfs_hivex_value_value (g, value, &buflen); if (buflen != 4) { error (g, "hivex: HKLM\\System\\Select\\Current expected to be DWORD"); goto out; } dword = le32toh (*(int32_t *)buf); 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 */ node = guestfs_hivex_node_get_child (g, root, "MountedDevices"); if (node == -1) goto out; if (node == 0) /* Not found: skip getting drive letter mappings (RHBZ#803664). */ goto skip_drive_letter_mappings; values = guestfs_hivex_node_values (g, 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; i < values->len; ++i) { CLEANUP_FREE char *key = guestfs_hivex_value_key (g, values->val[i].hivex_value_h); if (key == NULL) goto out; if (STRCASEEQLEN (key, "\\DosDevices\\", 12) && c_isalpha (key[12]) && key[13] == ':') count++; } fs->drive_mappings = safe_calloc (g, 2*count + 1, sizeof (char *)); for (i = count = 0; i < values->len; ++i) { int64_t v = values->val[i].hivex_value_h; CLEANUP_FREE char *key = guestfs_hivex_value_key (g, v); if (key == NULL) goto out; if (STRCASEEQLEN (key, "\\DosDevices\\", 12) && c_isalpha (key[12]) && key[13] == ':') { /* Get the binary value. Is it a fixed disk? */ CLEANUP_FREE char *blob = NULL; char *device; size_t len; int64_t type; type = guestfs_hivex_value_type (g, v); blob = guestfs_hivex_value_value (g, v, &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; } } } } skip_drive_letter_mappings:; /* Get the hostname. */ hivepath[0] = fs->windows_current_control_set; for (node = root, i = 0; node > 0 && i < sizeof hivepath / sizeof hivepath[0]; ++i) { node = guestfs_hivex_node_get_child (g, node, hivepath[i]); } if (node == -1) goto out; if (node == 0) { perrorf (g, "hivex: cannot locate HKLM\\SYSTEM\\%s\\Services\\Tcpip\\Parameters", fs->windows_current_control_set); goto out; } values2 = guestfs_hivex_node_values (g, node); if (values2 == NULL) goto out; for (i = 0; i < values2->len; ++i) { int64_t v = values2->val[i].hivex_value_h; CLEANUP_FREE char *key = guestfs_hivex_value_key (g, v); if (key == NULL) goto out; if (STRCASEEQ (key, "Hostname")) { fs->hostname = guestfs_hivex_value_utf8 (g, v); if (!fs->hostname) goto out; } /* many other interesting fields here ... */ } ret = 0; out: guestfs_hivex_close (g); return ret; }
/* At the moment, pull just the ProductName and version numbers from * the registry. In future there is a case for making many more * registry fields available to callers. */ static int check_windows_software_registry (guestfs_h *g, struct inspect_fs *fs) { int ret = -1; int r; size_t len = strlen (fs->windows_systemroot) + 64; char software[len]; snprintf (software, len, "%s/system32/config/software", fs->windows_systemroot); CLEANUP_FREE char *software_path = guestfs_case_sensitive_path (g, software); if (!software_path) return -1; r = guestfs_is_file (g, software_path); if (r == -1) return -1; /* If the software hive doesn't exist, just accept that we cannot * find product_name etc. */ if (r == 0) return 0; int64_t node; const char *hivepath[] = { "Microsoft", "Windows NT", "CurrentVersion" }; size_t i; CLEANUP_FREE_HIVEX_VALUE_LIST struct guestfs_hivex_value_list *values = NULL; if (guestfs_hivex_open (g, software_path, GUESTFS_HIVEX_OPEN_VERBOSE, g->verbose, -1) == -1) return -1; node = guestfs_hivex_root (g); for (i = 0; node > 0 && i < sizeof hivepath / sizeof hivepath[0]; ++i) node = guestfs_hivex_node_get_child (g, node, hivepath[i]); if (node == -1) goto out; if (node == 0) { perrorf (g, "hivex: cannot locate HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"); goto out; } values = guestfs_hivex_node_values (g, node); for (i = 0; i < values->len; ++i) { int64_t value = values->val[i].hivex_value_h; CLEANUP_FREE char *key = guestfs_hivex_value_key (g, value); if (key == NULL) goto out; if (STRCASEEQ (key, "ProductName")) { fs->product_name = guestfs_hivex_value_utf8 (g, value); if (!fs->product_name) goto out; } else if (STRCASEEQ (key, "CurrentVersion")) { CLEANUP_FREE char *version = guestfs_hivex_value_utf8 (g, value); if (!version) goto out; char *major, *minor; if (match2 (g, version, re_windows_version, &major, &minor)) { fs->major_version = guestfs___parse_unsigned_int (g, major); free (major); if (fs->major_version == -1) { free (minor); goto out; } fs->minor_version = guestfs___parse_unsigned_int (g, minor); free (minor); if (fs->minor_version == -1) goto out; } } else if (STRCASEEQ (key, "InstallationType")) { fs->product_variant = guestfs_hivex_value_utf8 (g, value); if (!fs->product_variant) goto out; } } ret = 0; out: guestfs_hivex_close (g); return ret; }
char * guestfs___get_windows_systemroot (guestfs_h *g) { /* Check a predefined list of common windows system root locations */ static const char *systemroots[] = { "/windows", "/winnt", "/win32", "/win", NULL }; for (size_t i = 0; i < sizeof systemroots / sizeof systemroots[0]; ++i) { char *systemroot = guestfs___case_sensitive_path_silently (g, systemroots[i]); if (!systemroot) continue; if (is_systemroot (g, systemroot)) { debug (g, "windows %%SYSTEMROOT%% = %s", systemroot); return systemroot; } else { free (systemroot); } } /* If the fs contains boot.ini, check it for non-standard * systemroot locations */ CLEANUP_FREE char *boot_ini_path = guestfs___case_sensitive_path_silently (g, "/boot.ini"); if (boot_ini_path && guestfs_is_file (g, boot_ini_path) > 0) { CLEANUP_FREE_STRING_LIST char **boot_ini = guestfs_read_lines (g, boot_ini_path); if (!boot_ini) { debug (g, "error reading %s", boot_ini_path); return NULL; } int found_os = 0; for (char **i = boot_ini; *i != NULL; i++) { CLEANUP_FREE char *controller_type = NULL; CLEANUP_FREE char *controller = NULL; CLEANUP_FREE char *disk = NULL; CLEANUP_FREE char *rdisk = NULL; CLEANUP_FREE char *partition = NULL; CLEANUP_FREE char *path = NULL; char *line = *i; if (!found_os) { if (match (g, line, re_boot_ini_os_header)) { found_os = 1; continue; } } /* See http://support.microsoft.com/kb/102873 for a discussion * of what this line means */ if (match6 (g, line, re_boot_ini_os, &controller_type, &controller, &disk, &rdisk, &partition, &path)) { /* The Windows system root may be on any disk. However, there * are currently (at least) 2 practical problems preventing us * from locating it on another disk: * * 1. We don't have enough metadata about the disks we were * given to know if what controller they were on and what * index they had. * * 2. The way inspection of filesystems currently works, we * can't mark another filesystem, which we may have already * inspected, to be inspected for a specific Windows system * root. * * Solving 1 properly would require a new API at a minimum. We * might be able to fudge something practical without this, * though, e.g. by looking at the <partition>th partition of * every disk for the specific windows root. * * Solving 2 would probably require a significant refactoring * of the way filesystems are inspected. We should probably do * this some time. * * For the moment, we ignore all partition information and * assume the system root is on the current partition. In * practice, this will normally be correct. */ /* Swap backslashes for forward slashes in the system root * path */ for (char *j = path; *j != '\0'; j++) { if (*j == '\\') *j = '/'; } char *systemroot = guestfs___case_sensitive_path_silently (g, path); if (systemroot && is_systemroot (g, systemroot)) { debug (g, "windows %%SYSTEMROOT%% = %s", systemroot); return systemroot; } else { free (systemroot); } } } } return NULL; /* not found */ }
int main (int argc, char *argv[]) { guestfs_h *g; const char *disk; char **roots, *root, *str, **mountpoints, **lines; size_t i, j; if (argc != 2) { fprintf (stderr, "usage: inspect_vm disk.img\n"); exit (EXIT_FAILURE); } disk = argv[1]; g = guestfs_create (); if (g == NULL) { perror ("failed to create libguestfs handle"); exit (EXIT_FAILURE); } /* Attach the disk image read-only to libguestfs. */ if (guestfs_add_drive_opts (g, disk, /* GUESTFS_ADD_DRIVE_OPTS_FORMAT, "raw", */ GUESTFS_ADD_DRIVE_OPTS_READONLY, 1, -1) /* this marks end of optional arguments */ == -1) exit (EXIT_FAILURE); /* Run the libguestfs back-end. */ if (guestfs_launch (g) == -1) exit (EXIT_FAILURE); /* Ask libguestfs to inspect for operating systems. */ roots = guestfs_inspect_os (g); if (roots == NULL) exit (EXIT_FAILURE); if (roots[0] == NULL) { fprintf (stderr, "inspect_vm: no operating systems found\n"); exit (EXIT_FAILURE); } for (j = 0; roots[j] != NULL; ++j) { root = roots[j]; printf ("Root device: %s\n", root); /* Print basic information about the operating system. */ str = guestfs_inspect_get_product_name (g, root); if (str) printf (" Product name: %s\n", str); free (str); printf (" Version: %d.%d\n", guestfs_inspect_get_major_version (g, root), guestfs_inspect_get_minor_version (g, root)); str = guestfs_inspect_get_type (g, root); if (str) printf (" Type: %s\n", str); free (str); str = guestfs_inspect_get_distro (g, root); if (str) printf (" Distro: %s\n", str); free (str); /* Mount up the disks, like guestfish -i. * * Sort keys by length, shortest first, so that we end up * mounting the filesystems in the correct order. */ mountpoints = guestfs_inspect_get_mountpoints (g, root); if (mountpoints == NULL) exit (EXIT_FAILURE); qsort (mountpoints, count_strings (mountpoints) / 2, 2 * sizeof (char *), compare_keys_len); for (i = 0; mountpoints[i] != NULL; i += 2) { /* Ignore failures from this call, since bogus entries can * appear in the guest's /etc/fstab. */ guestfs_mount_ro (g, mountpoints[i+1], mountpoints[i]); free (mountpoints[i]); free (mountpoints[i+1]); } free (mountpoints); /* If /etc/issue.net file exists, print up to 3 lines. */ if (guestfs_is_file (g, "/etc/issue.net") > 0) { printf ("--- /etc/issue.net ---\n"); lines = guestfs_head_n (g, 3, "/etc/issue.net"); if (lines == NULL) exit (EXIT_FAILURE); for (i = 0; lines[i] != NULL; ++i) { printf ("%s\n", lines[i]); free (lines[i]); } free (lines); } /* Unmount everything. */ if (guestfs_umount_all (g) == -1) exit (EXIT_FAILURE); free (root); } free (roots); guestfs_close (g); exit (EXIT_SUCCESS); }
/* At the moment, pull just the ProductName and version numbers from * the registry. In future there is a case for making many more * registry fields available to callers. */ static int check_windows_software_registry (guestfs_h *g, struct inspect_fs *fs) { int ret = -1; int r; CLEANUP_FREE char *software = safe_asprintf (g, "%s/system32/config/software", fs->windows_systemroot); CLEANUP_FREE char *software_path = guestfs_case_sensitive_path (g, software); if (!software_path) return -1; r = guestfs_is_file (g, software_path); if (r == -1) return -1; /* If the software hive doesn't exist, just accept that we cannot * find product_name etc. */ if (r == 0) return 0; int64_t node; const char *hivepath[] = { "Microsoft", "Windows NT", "CurrentVersion" }; size_t i; CLEANUP_FREE_HIVEX_VALUE_LIST struct guestfs_hivex_value_list *values = NULL; bool ignore_currentversion = false; if (guestfs_hivex_open (g, software_path, GUESTFS_HIVEX_OPEN_VERBOSE, g->verbose, -1) == -1) return -1; node = guestfs_hivex_root (g); for (i = 0; node > 0 && i < sizeof hivepath / sizeof hivepath[0]; ++i) node = guestfs_hivex_node_get_child (g, node, hivepath[i]); if (node == -1) goto out; if (node == 0) { perrorf (g, "hivex: cannot locate HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"); goto out; } values = guestfs_hivex_node_values (g, node); for (i = 0; i < values->len; ++i) { int64_t value = values->val[i].hivex_value_h; CLEANUP_FREE char *key = guestfs_hivex_value_key (g, value); if (key == NULL) goto out; if (STRCASEEQ (key, "ProductName")) { fs->product_name = guestfs_hivex_value_utf8 (g, value); if (!fs->product_name) goto out; } else if (STRCASEEQ (key, "CurrentMajorVersionNumber")) { size_t vsize; int64_t vtype = guestfs_hivex_value_type (g, value); CLEANUP_FREE char *vbuf = guestfs_hivex_value_value (g, value, &vsize); if (vbuf == NULL) goto out; if (vtype != 4 || vsize != 4) { error (g, "hivex: expected CurrentVersion\\%s to be a DWORD field", "CurrentMajorVersionNumber"); goto out; } fs->version.v_major = le32toh (*(int32_t *)vbuf); /* Ignore CurrentVersion if we see it after this key. */ ignore_currentversion = true; } else if (STRCASEEQ (key, "CurrentMinorVersionNumber")) { size_t vsize; int64_t vtype = guestfs_hivex_value_type (g, value); CLEANUP_FREE char *vbuf = guestfs_hivex_value_value (g, value, &vsize); if (vbuf == NULL) goto out; if (vtype != 4 || vsize != 4) { error (g, "hivex: expected CurrentVersion\\%s to be a DWORD field", "CurrentMinorVersionNumber"); goto out; } fs->version.v_minor = le32toh (*(int32_t *)vbuf); /* Ignore CurrentVersion if we see it after this key. */ ignore_currentversion = true; } else if (!ignore_currentversion && STRCASEEQ (key, "CurrentVersion")) { CLEANUP_FREE char *version = guestfs_hivex_value_utf8 (g, value); if (!version) goto out; if (guestfs_int_version_from_x_y_re (g, &fs->version, version, re_windows_version) == -1) goto out; } else if (STRCASEEQ (key, "InstallationType")) { fs->product_variant = guestfs_hivex_value_utf8 (g, value); if (!fs->product_variant) goto out; } } ret = 0; out: guestfs_hivex_close (g); return ret; }
int guestfs_impl_copy_out (guestfs_h *g, const char *remotepath, const char *localdir) { struct stat statbuf; int r; if (stat (localdir, &statbuf) == -1 || ! (S_ISDIR (statbuf.st_mode))) { error (g, _("target '%s' is not a directory"), localdir); return -1; } /* If the remote is a file, download it. If it's a directory, * create the directory in localdir first before using tar-out. */ r = guestfs_is_file (g, remotepath); if (r == -1) return -1; if (r == 1) { /* is file */ CLEANUP_FREE char *filename = NULL; size_t buf_len = strlen (remotepath) + 1; CLEANUP_FREE char *buf = safe_malloc (g, buf_len); const char *basename; if (split_path (g, buf, buf_len, remotepath, NULL, &basename) == -1) return -1; if (asprintf (&filename, "%s/%s", localdir, basename) == -1) { perrorf (g, "asprintf"); return -1; } if (guestfs_download (g, remotepath, filename) == -1) return -1; } else { /* not a regular file */ CLEANUP_CMD_CLOSE struct command *cmd = guestfs_int_new_command (g); struct copy_out_child_data data; char fdbuf[64]; int fd; r = guestfs_is_dir (g, remotepath); if (r == -1) return -1; if (r == 0) { error (g, _("'%s' is not a file or directory"), remotepath); return -1; } size_t buf_len = strlen (remotepath) + 1; CLEANUP_FREE char *buf = safe_malloc (g, buf_len); const char *basename; if (split_path (g, buf, buf_len, remotepath, NULL, &basename) == -1) return -1; /* RHBZ#845522: If remotepath == "/" then basename would be an empty * string. Replace it with "." so that make_tar_output writes * to "localdir/." */ if (STREQ (basename, "")) basename = "."; data.localdir = localdir; data.basename = basename; guestfs_int_cmd_set_child_callback (cmd, &child_setup, &data); guestfs_int_cmd_add_arg (cmd, "tar"); guestfs_int_cmd_add_arg (cmd, "-xf"); guestfs_int_cmd_add_arg (cmd, "-"); guestfs_int_cmd_clear_capture_errors (cmd); fd = guestfs_int_cmd_pipe_run (cmd, "w"); if (fd == -1) return -1; snprintf (fdbuf, sizeof fdbuf, "/dev/fd/%d", fd); r = guestfs_tar_out (g, remotepath, fdbuf); if (close (fd) == -1) { perrorf (g, "close (tar-output subprocess)"); return -1; } r = guestfs_int_cmd_pipe_wait (cmd); if (r == -1) return -1; if (!WIFEXITED (r) || WEXITSTATUS (r) != 0) { CLEANUP_FREE char *errors = guestfs_int_cmd_get_pipe_errors (cmd); if (errors == NULL) return -1; error (g, "tar subprocess failed: %s", errors); return -1; } } return 0; }
static int check_filesystem (guestfs_h *g, const char *mountable, const struct guestfs_internal_mountable *m, int whole_device) { /* Not CLEANUP_FREE, as it will be cleaned up with inspection info */ char *windows_systemroot = NULL; if (extend_fses (g) == -1) return -1; int partnum = -1; if (!whole_device && m->im_type == MOUNTABLE_DEVICE) { guestfs_push_error_handler (g, NULL, NULL); partnum = guestfs_part_to_partnum (g, m->im_device); guestfs_pop_error_handler (g); } struct inspect_fs *fs = &g->fses[g->nr_fses-1]; fs->mountable = safe_strdup (g, mountable); /* Optimize some of the tests by avoiding multiple tests of the same thing. */ int is_dir_etc = guestfs_is_dir (g, "/etc") > 0; int is_dir_bin = guestfs_is_dir (g, "/bin") > 0; int is_dir_share = guestfs_is_dir (g, "/share") > 0; /* Grub /boot? */ if (guestfs_is_file (g, "/grub/menu.lst") > 0 || guestfs_is_file (g, "/grub/grub.conf") > 0 || guestfs_is_file (g, "/grub2/grub.cfg") > 0) ; /* FreeBSD root? */ else if (is_dir_etc && is_dir_bin && guestfs_is_file (g, "/etc/freebsd-update.conf") > 0 && guestfs_is_file (g, "/etc/fstab") > 0) { /* Ignore /dev/sda1 which is a shadow of the real root filesystem * that is probably /dev/sda5 (see: * http://www.freebsd.org/doc/handbook/disk-organization.html) */ if (m->im_type == MOUNTABLE_DEVICE && match (g, m->im_device, re_first_partition)) return 0; fs->is_root = 1; fs->format = OS_FORMAT_INSTALLED; if (guestfs___check_freebsd_root (g, fs) == -1) return -1; } else if (is_dir_etc && is_dir_bin && guestfs_is_file (g, "/etc/fstab") > 0 && guestfs_is_file (g, "/etc/release") > 0) { /* Ignore /dev/sda1 which is a shadow of the real root filesystem * that is probably /dev/sda5 (see: * http://www.freebsd.org/doc/handbook/disk-organization.html) */ if (m->im_type == MOUNTABLE_DEVICE && match (g, m->im_device, re_first_partition)) return 0; fs->is_root = 1; fs->format = OS_FORMAT_INSTALLED; if (guestfs___check_netbsd_root (g, fs) == -1) return -1; } /* Hurd root? */ else if (guestfs_is_file (g, "/hurd/console") > 0 && guestfs_is_file (g, "/hurd/hello") > 0 && guestfs_is_file (g, "/hurd/null") > 0) { fs->is_root = 1; fs->format = OS_FORMAT_INSTALLED; /* XXX could be more specific */ if (guestfs___check_hurd_root (g, fs) == -1) return -1; } /* Linux root? */ else if (is_dir_etc && (is_dir_bin || (guestfs_is_symlink (g, "/bin") > 0 && guestfs_is_dir (g, "/usr/bin") > 0)) && guestfs_is_file (g, "/etc/fstab") > 0) { fs->is_root = 1; fs->format = OS_FORMAT_INSTALLED; if (guestfs___check_linux_root (g, fs) == -1) return -1; } /* Linux /usr/local? */ else if (is_dir_etc && is_dir_bin && is_dir_share && guestfs_is_dir (g, "/local") == 0 && guestfs_is_file (g, "/etc/fstab") == 0) ; /* Linux /usr? */ else if (is_dir_etc && is_dir_bin && is_dir_share && guestfs_is_dir (g, "/local") > 0 && guestfs_is_file (g, "/etc/fstab") == 0) ; /* Linux /var? */ else if (guestfs_is_dir (g, "/log") > 0 && guestfs_is_dir (g, "/run") > 0 && guestfs_is_dir (g, "/spool") > 0) ; /* Windows root? */ else if ((windows_systemroot = guestfs___get_windows_systemroot (g)) != NULL) { fs->is_root = 1; fs->format = OS_FORMAT_INSTALLED; if (guestfs___check_windows_root (g, fs, windows_systemroot) == -1) return -1; } /* Windows volume with installed applications (but not root)? */ else if (guestfs___is_dir_nocase (g, "/System Volume Information") > 0 && guestfs___is_dir_nocase (g, "/Program Files") > 0) ; /* Windows volume (but not root)? */ else if (guestfs___is_dir_nocase (g, "/System Volume Information") > 0) ; /* FreeDOS? */ else if (guestfs___is_dir_nocase (g, "/FDOS") > 0 && guestfs___is_file_nocase (g, "/FDOS/FREEDOS.BSS") > 0) { fs->is_root = 1; fs->format = OS_FORMAT_INSTALLED; fs->type = OS_TYPE_DOS; fs->distro = OS_DISTRO_FREEDOS; /* FreeDOS is a mix of 16 and 32 bit, but assume it requires a * 32 bit i386 processor. */ fs->arch = safe_strdup (g, "i386"); } /* Install CD/disk? * * Note that we checked (above) for an install ISO, but there are * other types of install image (eg. USB keys) which that check * wouldn't have picked up. * * Skip these checks if it's not a whole device (eg. CD) or the * first partition (eg. bootable USB key). */ else if ((whole_device || partnum == 1) && (guestfs_is_file (g, "/isolinux/isolinux.cfg") > 0 || guestfs_is_dir (g, "/EFI/BOOT") > 0 || guestfs_is_file (g, "/images/install.img") > 0 || guestfs_is_dir (g, "/.disk") > 0 || guestfs_is_file (g, "/.discinfo") > 0 || guestfs_is_file (g, "/i386/txtsetup.sif") > 0 || guestfs_is_file (g, "/amd64/txtsetup.sif") > 0 || guestfs_is_file (g, "/freedos/freedos.ico") > 0 || guestfs_is_file (g, "/boot/loader.rc") > 0)) { fs->is_root = 1; fs->format = OS_FORMAT_INSTALLER; if (guestfs___check_installer_root (g, fs) == -1) return -1; } /* The above code should have set fs->type and fs->distro fields, so * we can now guess the package management system. */ guestfs___check_package_format (g, fs); guestfs___check_package_management (g, fs); return 0; }