out of tree build: erlang
[libguestfs.git] / cat / virt-cat.c
index ebae895..4d37dda 100644 (file)
@@ -1,5 +1,5 @@
 /* virt-cat
- * Copyright (C) 2010 Red Hat Inc.
+ * Copyright (C) 2010-2011 Red Hat Inc.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
 #include <config.h>
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 #include <inttypes.h>
 #include <unistd.h>
 #include <getopt.h>
+#include <locale.h>
 #include <assert.h>
+#include <libintl.h>
 
 #include "progname.h"
+#include "c-ctype.h"
 
 #include "guestfs.h"
 #include "options.h"
 guestfs_h *g;
 
 int read_only = 1;
+int live = 0;
 int verbose = 0;
 int keys_from_stdin = 0;
 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)
 {
@@ -104,11 +112,9 @@ main (int argc, char *argv[])
   };
   struct drv *drvs = NULL;
   struct drv *drv;
-  char *p, *file = NULL;
   const char *format = NULL;
   int c;
   int option_index;
-  int next_prepared_drive = 1;
 
   g = guestfs_create ();
   if (g == NULL) {
@@ -215,6 +221,7 @@ main (int argc, char *argv[])
    */
   assert (read_only == 1);
   assert (inspector == 1);
+  assert (live == 0);
 
   /* User must specify at least one filename on the command line. */
   if (optind >= argc || argc - optind < 1)
@@ -238,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.
+   */
+}