From 1e891d900714b84a84f9c9e79c6e033aa56c0fcc Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Tue, 25 Oct 2011 10:55:20 +0100 Subject: [PATCH] virt-cat: Handle Windows paths and drive letters (RHBZ#693359). --- cat/virt-cat.c | 143 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- cat/virt-cat.pod | 40 ++++++++++++++++ 2 files changed, 180 insertions(+), 3 deletions(-) diff --git a/cat/virt-cat.c b/cat/virt-cat.c index 06bd8b1..268ca56 100644 --- a/cat/virt-cat.c +++ b/cat/virt-cat.c @@ -29,6 +29,7 @@ #include #include "progname.h" +#include "c-ctype.h" #include "guestfs.h" #include "options.h" @@ -44,6 +45,9 @@ int echo_keys = 0; const char *libvirt_uri = NULL; int inspector = 1; +static int is_windows (guestfs_h *g, const char *root); +static char *windows_path (guestfs_h *g, const char *root, const char *filename); + static inline char * bad_cast (char const *s) { @@ -241,14 +245,147 @@ main (int argc, char *argv[]) free_drives (drvs); unsigned errors = 0; + int windows; + char *root, **roots; + + /* Get root mountpoint. See: fish/inspect.c:inspect_mount */ + roots = guestfs_inspect_get_roots (g); + assert (roots); + assert (roots[0] != NULL); + assert (roots[1] == NULL); + root = roots[0]; + free (roots); + + /* Windows? Special handling is required. */ + windows = is_windows (g, root); + + for (; optind < argc; optind++) { + char *filename_to_free = NULL; + const char *filename = argv[optind]; + + if (windows) { + filename = filename_to_free = windows_path (g, root, filename); + if (filename == NULL) { + errors++; + continue; + } + } - while (optind < argc) { - if (guestfs_download (g, argv[optind], "/dev/stdout") == -1) + if (guestfs_download (g, filename, "/dev/stdout") == -1) errors++; - optind++; + + free (filename_to_free); } + free (root); + guestfs_close (g); exit (errors == 0 ? EXIT_SUCCESS : EXIT_FAILURE); } + +static int +is_windows (guestfs_h *g, const char *root) +{ + char *type; + int w; + + type = guestfs_inspect_get_type (g, root); + if (!type) + return 0; + + w = STREQ (type, "windows"); + free (type); + return w; +} + +static void mount_drive_letter_ro (char drive_letter, const char *root); + +static char * +windows_path (guestfs_h *g, const char *root, const char *path) +{ + char *ret; + size_t i; + + /* If there is a drive letter, rewrite the path. */ + if (c_isalpha (path[0]) && path[1] == ':') { + char drive_letter = c_tolower (path[0]); + /* This returns the newly allocated string. */ + mount_drive_letter_ro (drive_letter, root); + ret = strdup (path + 2); + if (ret == NULL) { + perror ("strdup"); + exit (EXIT_FAILURE); + } + } + else if (!*path) { + ret = strdup ("/"); + if (ret == NULL) { + perror ("strdup"); + exit (EXIT_FAILURE); + } + } + else { + ret = strdup (path); + if (ret == NULL) { + perror ("strdup"); + exit (EXIT_FAILURE); + } + } + + /* Blindly convert any backslashes into forward slashes. Is this good? */ + for (i = 0; i < strlen (ret); ++i) + if (ret[i] == '\\') + ret[i] = '/'; + + /* If this fails, we want to return NULL. */ + char *t = guestfs_case_sensitive_path (g, ret); + free (ret); + ret = t; + + return ret; +} + +static void +mount_drive_letter_ro (char drive_letter, const char *root) +{ + char **drives; + char *device; + size_t i; + + /* Resolve the drive letter using the drive mappings table. */ + drives = guestfs_inspect_get_drive_mappings (g, root); + if (drives == NULL || drives[0] == NULL) { + fprintf (stderr, _("%s: to use Windows drive letters, this must be a Windows guest\n"), + program_name); + exit (EXIT_FAILURE); + } + + device = NULL; + for (i = 0; drives[i] != NULL; i += 2) { + if (c_tolower (drives[i][0]) == drive_letter && drives[i][1] == '\0') { + device = drives[i+1]; + break; + } + } + + if (device == NULL) { + fprintf (stderr, _("%s: drive '%c:' not found.\n"), + program_name, drive_letter); + exit (EXIT_FAILURE); + } + + /* Unmount current disk and remount device. */ + if (guestfs_umount_all (g) == -1) + exit (EXIT_FAILURE); + + if (guestfs_mount_ro (g, device, "/") == -1) + exit (EXIT_FAILURE); + + for (i = 0; drives[i] != NULL; ++i) + free (drives[i]); + free (drives); + /* Don't need to free (device) because that string was in the + * drives array. + */ +} diff --git a/cat/virt-cat.pod b/cat/virt-cat.pod index c5d5c6e..a4b54b8 100755 --- a/cat/virt-cat.pod +++ b/cat/virt-cat.pod @@ -165,6 +165,46 @@ name as a guest. For compatibility the old style is still supported. +=head1 WINDOWS PATHS + +C has a limited ability to understand Windows drive letters +and paths (eg. C). + +If and only if the guest is running Windows then: + +=over 4 + +=item * + +Drive letter prefixes like C are resolved against the +Windows Registry to the correct filesystem. + +=item * + +Any backslash (C<\>) characters in the path are replaced +with forward slashes so that libguestfs can process it. + +=item * + +The path is resolved case insensitively to locate the file +that should be displayed. + +=back + +There are some known shortcomings: + +=over 4 + +=item * + +Some NTFS symbolic links may not be followed correctly. + +=item * + +NTFS junction points that cross filesystems are not followed. + +=back + =head1 USING GUESTFISH L is a more powerful, lower level tool which you can use -- 1.8.3.1