tests: Split images -> tests/data + tests/guests
[libguestfs.git] / fish / copy.c
index f5edad1..820e092 100644 (file)
@@ -1,5 +1,5 @@
 /* guestfish - the filesystem interactive shell
- * 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
@@ -13,7 +13,7 @@
  *
  * 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 <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include <limits.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/wait.h>
 
 #include "fish.h"
 
-static int make_tar_from_local (const char *local);
-static int make_tar_output (const char *local, const char *basename);
+struct fd_pid {
+  int fd;                       /* -1 == error */
+  pid_t pid;
+};
+
+static struct fd_pid make_tar_from_local (const char *local);
+static struct fd_pid make_tar_output (const char *local, const char *basename);
 static int split_path (char *buf, size_t buf_size, const char *path, const char **dirname, const char **basename);
 
 int
@@ -42,47 +48,65 @@ run_copy_in (const char *cmd, size_t argc, char *argv[])
   }
 
   /* Remote directory is always the last arg. */
-  const char *remote = argv[argc-1];
+  char *remote = argv[argc-1];
+
+  /* Allow win: prefix on remote. */
+  remote = win_prefix (remote);
+  if (remote == NULL)
+    return -1;
+
   int nr_locals = argc-1;
 
   int remote_is_dir = guestfs_is_dir (g, remote);
-  if (remote_is_dir == -1)
+  if (remote_is_dir == -1) {
+    free (remote);
     return -1;
+  }
 
   if (!remote_is_dir) {
     fprintf (stderr, _("copy-in: target '%s' is not a directory\n"), remote);
+    free (remote);
     return -1;
   }
 
   /* Upload each local one at a time using tar-in. */
   int i;
   for (i = 0; i < nr_locals; ++i) {
-    int fd = make_tar_from_local (argv[i]);
-    if (fd == -1)
+    struct fd_pid fdpid = make_tar_from_local (argv[i]);
+    if (fdpid.fd == -1) {
+      free (remote);
       return -1;
+    }
 
     char fdbuf[64];
-    snprintf (fdbuf, sizeof fdbuf, "/dev/fd/%d", fd);
+    snprintf (fdbuf, sizeof fdbuf, "/dev/fd/%d", fdpid.fd);
 
     int r = guestfs_tar_in (g, fdbuf, remote);
 
-    if (close (fd) == -1) {
+    if (close (fdpid.fd) == -1) {
       perror ("close (tar-from-local subprocess)");
       r = -1;
     }
 
     int status;
-    if (wait (&status) == -1) {
+    if (waitpid (fdpid.pid, &status, 0) == -1) {
       perror ("wait (tar-from-local subprocess)");
+      free (remote);
       return -1;
     }
-    if (!(WIFEXITED (status) && WEXITSTATUS (status) == 0))
+    if (!(WIFEXITED (status) && WEXITSTATUS (status) == 0)) {
+      free (remote);
       return -1;
+    }
 
-    if (r == -1)
+    if (r == -1) {
+      free (remote);
       return -1;
+    }
   }
 
+  free (remote);
+
   return 0;
 }
 
@@ -91,25 +115,27 @@ static void tar_create (const char *dir, const char *path) __attribute__((noretu
 /* This creates a subprocess which feeds a tar file back to the
  * main guestfish process.
  */
-static int
+static struct fd_pid
 make_tar_from_local (const char *local)
 {
   int fd[2];
+  struct fd_pid r = { .fd = -1 };
 
   if (pipe (fd) == -1) {
     perror ("pipe");
-    return -1;
+    return r;
   }
 
-  pid_t pid = fork ();
-  if (pid == -1) {
+  r.pid = fork ();
+  if (r.pid == -1) {
     perror ("fork");
-    return -1;
+    return r;
   }
 
-  if (pid > 0) {                /* Parent */
+  if (r.pid > 0) {              /* Parent */
     close (fd[1]);
-    return fd[0];
+    r.fd = fd[0];
+    return r;
   }
 
   /* Child. */
@@ -191,71 +217,99 @@ run_copy_out (const char *cmd, size_t argc, char *argv[])
   struct stat statbuf;
   if (stat (local, &statbuf) == -1 ||
       ! (S_ISDIR (statbuf.st_mode))) {
-    fprintf (stderr, _("copy-in: target '%s' is not a directory\n"), local);
+    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, argv[i]);
-    if (r == -1)
+    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, argv[i], NULL, &basename) == -1)
+      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, argv[i], filename) == -1)
+      if (guestfs_download (g, remote, filename) == -1) {
+        free (remote);
         return -1;
+      }
     }
     else {                      /* not a regular file */
-      r = guestfs_is_dir (g, argv[i]);
-      if (r == -1)
+      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"),
-                 argv[i]);
+                 remote);
+        free (remote);
         return -1;
       }
 
       char buf[PATH_MAX];
       const char *basename;
-      if (split_path (buf, sizeof buf, argv[i], NULL, &basename) == -1)
+      if (split_path (buf, sizeof buf, remote, NULL, &basename) == -1) {
+        free (remote);
         return -1;
+      }
 
-      int fd = make_tar_output (local, basename);
-      if (fd == -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", fd);
+      snprintf (fdbuf, sizeof fdbuf, "/dev/fd/%d", fdpid.fd);
 
-      int r = guestfs_tar_out (g, argv[i], fdbuf);
+      int r = guestfs_tar_out (g, remote, fdbuf);
 
-      if (close (fd) == -1) {
+      if (close (fdpid.fd) == -1) {
         perror ("close (tar-output subprocess)");
+        free (remote);
         r = -1;
       }
 
       int status;
-      if (wait (&status) == -1) {
+      if (waitpid (fdpid.pid, &status, 0) == -1) {
         perror ("wait (tar-output subprocess)");
+        free (remote);
         return -1;
       }
-      if (!(WIFEXITED (status) && WEXITSTATUS (status) == 0))
+      if (!(WIFEXITED (status) && WEXITSTATUS (status) == 0)) {
+        free (remote);
         return -1;
+      }
 
-      if (r == -1)
+      if (r == -1) {
+        free (remote);
         return -1;
+      }
     }
+
+    free (remote);
   }
 
   return 0;
@@ -265,25 +319,27 @@ run_copy_out (const char *cmd, size_t argc, char *argv[])
  * guestfish process and unpacks it into the 'local/basename'
  * directory.
  */
-static int
+static struct fd_pid
 make_tar_output (const char *local, const char *basename)
 {
   int fd[2];
+  struct fd_pid r = { .fd = -1 };
 
   if (pipe (fd) == -1) {
     perror ("pipe");
-    return -1;
+    return r;
   }
 
-  pid_t pid = fork ();
-  if (pid == -1) {
+  r.pid = fork ();
+  if (r.pid == -1) {
     perror ("fork");
-    return -1;
+    return r;
   }
 
-  if (pid > 0) {                /* Parent */
+  if (r.pid > 0) {              /* Parent */
     close (fd[0]);
-    return fd[1];
+    r.fd = fd[1];
+    return r;
   }
 
   /* Child. */