New APIs: copy-{file,device}-to-{file,device}.
authorRichard W.M. Jones <rjones@redhat.com>
Tue, 25 Oct 2011 21:49:55 +0000 (22:49 +0100)
committerRichard W.M. Jones <rjones@redhat.com>
Wed, 26 Oct 2011 09:07:21 +0000 (10:07 +0100)
The four new APIs:
guestfs_copy_device_to_device,
guestfs_copy_device_to_file,
guestfs_copy_file_to_device, and
guestfs_copy_file_to_file
let you copy from a source to a destination, between files and
devices, optionally allowing source and destination offsets and size
to be specified.

daemon/Makefile.am
daemon/copy.c [new file with mode: 0644]
generator/generator_actions.ml
po/POTFILES.in
src/MAX_PROC_NR
src/guestfs.pod

index e23ce86..f8e0ecb 100644 (file)
@@ -95,6 +95,7 @@ guestfsd_SOURCES = \
        cmp.c \
        command.c \
        compress.c \
+       copy.c \
        cpmv.c \
        daemon.h \
        dd.c \
diff --git a/daemon/copy.c b/daemon/copy.c
new file mode 100644 (file)
index 0000000..1d515f8
--- /dev/null
@@ -0,0 +1,242 @@
+/* libguestfs - the guestfsd daemon
+ * Copyright (C) 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * 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.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+
+#include "guestfs_protocol.h"
+#include "daemon.h"
+#include "actions.h"
+
+#define DEST_FILE_FLAGS O_WRONLY|O_CREAT|O_TRUNC|O_NOCTTY, 0666
+#define DEST_DEVICE_FLAGS O_WRONLY, 0
+
+/* NB: We cheat slightly by assuming that optargs_bitmask is
+ * compatible for all four of the calls.  This is true provided they
+ * all take the same set of optional arguments.
+ */
+
+/* Takes optional arguments, consult optargs_bitmask. */
+static int
+copy (const char *src, const char *src_display,
+      const char *dest, const char *dest_display,
+      int wrflags, int wrmode,
+      int64_t srcoffset, int64_t destoffset, int64_t size)
+{
+  int64_t saved_size = size;
+  int src_fd, dest_fd;
+  char buf[BUFSIZ];
+  size_t n;
+  ssize_t r;
+
+  if ((optargs_bitmask & GUESTFS_COPY_DEVICE_TO_DEVICE_SRCOFFSET_BITMASK)) {
+    if (srcoffset < 0) {
+      reply_with_error ("srcoffset is negative");
+      return -1;
+    }
+  }
+  else
+    srcoffset = 0;
+
+  if ((optargs_bitmask & GUESTFS_COPY_DEVICE_TO_DEVICE_DESTOFFSET_BITMASK)) {
+    if (destoffset < 0) {
+      reply_with_error ("destoffset is negative");
+      return -1;
+    }
+  }
+  else
+    destoffset = 0;
+
+  if ((optargs_bitmask & GUESTFS_COPY_DEVICE_TO_DEVICE_SIZE_BITMASK)) {
+    if (size < 0) {
+      reply_with_error ("size is negative");
+      return -1;
+    }
+  }
+  else
+    size = -1;
+
+  /* Open source and destination. */
+  src_fd = open (src, O_RDONLY);
+  if (src_fd == -1) {
+    reply_with_perror ("%s", src_display);
+    return -1;
+  }
+
+  if (srcoffset > 0 && lseek (src_fd, srcoffset, SEEK_SET) == (off_t) -1) {
+    reply_with_perror ("lseek: %s", src_display);
+    close (src_fd);
+    return -1;
+  }
+
+  dest_fd = open (dest, wrflags, wrmode);
+  if (dest_fd == -1) {
+    reply_with_perror ("%s", dest_display);
+    close (src_fd);
+    return -1;
+  }
+
+  if (destoffset > 0 && lseek (dest_fd, destoffset, SEEK_SET) == (off_t) -1) {
+    reply_with_perror ("lseek: %s", dest_display);
+    close (src_fd);
+    close (dest_fd);
+    return -1;
+  }
+
+  if (size == -1)
+    pulse_mode_start ();
+
+  while (size != 0) {
+    /* Calculate bytes to copy. */
+    if (size == -1 || size > (int64_t) sizeof buf)
+      n = sizeof buf;
+    else
+      n = size;
+
+    r = read (src_fd, buf, n);
+    if (r == -1) {
+      if (size == -1)
+        pulse_mode_cancel ();
+      reply_with_perror ("read: %s", src_display);
+      close (src_fd);
+      close (dest_fd);
+      return -1;
+    }
+
+    if (r == 0) {
+      if (size == -1) /* if size == -1, this is normal end of loop */
+        break;
+      reply_with_error ("%s: input too short", src_display);
+      close (src_fd);
+      close (dest_fd);
+      return -1;
+    }
+
+    if (xwrite (dest_fd, buf, r) == -1) {
+      if (size == -1)
+        pulse_mode_cancel ();
+      reply_with_perror ("%s: write", dest_display);
+      close (src_fd);
+      close (dest_fd);
+      return -1;
+    }
+
+    if (size != -1) {
+      size -= r;
+      notify_progress ((uint64_t) (saved_size - size), (uint64_t) saved_size);
+    }
+  }
+
+  if (size == -1)
+    pulse_mode_end ();
+
+  if (close (src_fd) == -1) {
+    reply_with_perror ("close: %s", src_display);
+    close (dest_fd);
+    return -1;
+  }
+
+  if (close (dest_fd) == -1) {
+    reply_with_perror ("close: %s", dest_display);
+    return -1;
+  }
+
+  return 0;
+}
+
+int
+do_copy_device_to_device (const char *src, const char *dest,
+                          int64_t srcoffset, int64_t destoffset, int64_t size)
+{
+  return copy (src, src, dest, dest, DEST_DEVICE_FLAGS,
+               srcoffset, destoffset, size);
+}
+
+int
+do_copy_device_to_file (const char *src, const char *dest,
+                        int64_t srcoffset, int64_t destoffset, int64_t size)
+{
+  char *dest_buf;
+  int r;
+
+  dest_buf = sysroot_path (dest);
+  if (!dest_buf) {
+    reply_with_perror ("malloc");
+    return -1;
+  }
+
+  r = copy (src, src, dest_buf, dest, DEST_FILE_FLAGS,
+            srcoffset, destoffset, size);
+  free (dest_buf);
+
+  return r;
+}
+
+int
+do_copy_file_to_device (const char *src, const char *dest,
+                        int64_t srcoffset, int64_t destoffset, int64_t size)
+{
+  char *src_buf;
+  int r;
+
+  src_buf = sysroot_path (src);
+  if (!src_buf) {
+    reply_with_perror ("malloc");
+    return -1;
+  }
+
+  r = copy (src_buf, src, dest, dest, DEST_DEVICE_FLAGS,
+            srcoffset, destoffset, size);
+  free (src_buf);
+
+  return r;
+}
+
+int
+do_copy_file_to_file (const char *src, const char *dest,
+                      int64_t srcoffset, int64_t destoffset, int64_t size)
+{
+  char *src_buf, *dest_buf;
+  int r;
+
+  src_buf = sysroot_path (src);
+  if (!src_buf) {
+    reply_with_perror ("malloc");
+    return -1;
+  }
+
+  dest_buf = sysroot_path (dest);
+  if (!dest_buf) {
+    reply_with_perror ("malloc");
+    free (src_buf);
+    return -1;
+  }
+
+  r = copy (src_buf, src, dest_buf, dest, DEST_FILE_FLAGS,
+            srcoffset, destoffset, size);
+  free (src_buf);
+  free (dest_buf);
+
+  return r;
+}
index ce3382f..f10a040 100644 (file)
@@ -5168,7 +5168,7 @@ See also C<guestfs_version>.
 
 =back");
 
-  ("dd", (RErr, [Dev_or_Path "src"; Dev_or_Path "dest"], []), 217, [],
+  ("dd", (RErr, [Dev_or_Path "src"; Dev_or_Path "dest"], []), 217, [DeprecatedBy "copy_device_to_device"],
    [InitScratchFS, Always, TestOutputBuffer (
       [["mkdir"; "/dd"];
        ["write"; "/dd/src"; "hello, world"];
@@ -5183,7 +5183,8 @@ example to duplicate a filesystem.
 
 If the destination is a device, it must be as large or larger
 than the source file or device, otherwise the copy will fail.
-This command cannot do partial copies (see C<guestfs_copy_size>).");
+This command cannot do partial copies
+(see C<guestfs_copy_device_to_device>).");
 
   ("filesize", (RInt64 "size", [Pathname "file"], []), 218, [],
    [InitScratchFS, Always, TestOutputInt (
@@ -5276,7 +5277,7 @@ calls to associate logical volumes and volume groups.
 
 See also C<guestfs_vgpvuuids>.");
 
-  ("copy_size", (RErr, [Dev_or_Path "src"; Dev_or_Path "dest"; Int64 "size"], []), 227, [Progress],
+  ("copy_size", (RErr, [Dev_or_Path "src"; Dev_or_Path "dest"; Int64 "size"], []), 227, [Progress; DeprecatedBy "copy_device_to_device"],
    [InitScratchFS, Always, TestOutputBuffer (
       [["mkdir"; "/copy_size"];
        ["write"; "/copy_size/src"; "hello, world"];
@@ -6227,6 +6228,60 @@ The named partition must exist, for example as a string returned
 from C<guestfs_list_partitions>.
 
 See also C<guestfs_part_to_dev>.");
+
+  ("copy_device_to_device", (RErr, [Device "src"; Device "dest"], [Int64 "srcoffset"; Int64 "destoffset"; Int64 "size"]), 294, [Progress],
+   [],
+   "copy from source device to destination device",
+   "\
+The four calls C<guestfs_copy_device_to_device>,
+C<guestfs_copy_device_to_file>,
+C<guestfs_copy_file_to_device>, and
+C<guestfs_copy_file_to_file>
+let you copy from a source (device|file) to a destination
+(device|file).
+
+Partial copies can be made since you can specify optionally
+the source offset, destination offset and size to copy.  These
+values are all specified in bytes.  If not given, the offsets
+both default to zero, and the size defaults to copying as much
+as possible until we hit the end of the source.
+
+The source and destination may be the same object.  However
+overlapping regions may not be copied correctly.
+
+If the destination is a file, it is created if required.  If
+the destination file is not large enough, it is extended.");
+
+  ("copy_device_to_file", (RErr, [Device "src"; Pathname "dest"], [Int64 "srcoffset"; Int64 "destoffset"; Int64 "size"]), 295, [Progress],
+   [],
+   "copy from source device to destination file",
+   "\
+See C<guestfs_copy_device_to_device> for a general overview
+of this call.");
+
+  ("copy_file_to_device", (RErr, [Pathname "src"; Device "dest"], [Int64 "srcoffset"; Int64 "destoffset"; Int64 "size"]), 296, [Progress],
+   [],
+   "copy from source file to destination device",
+   "\
+See C<guestfs_copy_device_to_device> for a general overview
+of this call.");
+
+  ("copy_file_to_file", (RErr, [Pathname "src"; Pathname "dest"], [Int64 "srcoffset"; Int64 "destoffset"; Int64 "size"]), 297, [Progress],
+   [InitScratchFS, Always, TestOutputBuffer (
+      [["mkdir"; "/copyff"];
+       ["write"; "/copyff/src"; "hello, world"];
+       ["copy_file_to_file"; "/copyff/src"; "/copyff/dest"; ""; ""; ""];
+       ["read_file"; "/copyff/dest"]], "hello, world")],
+   "copy from source file to destination file",
+   "\
+See C<guestfs_copy_device_to_device> for a general overview
+of this call.
+
+This is B<not> the function you want for copying files.  This
+is for copying blocks within existing files.  See C<guestfs_cp>,
+C<guestfs_cp_a> and C<guestfs_mv> for general file copying and
+moving functions.");
+
 ]
 
 let all_functions = non_daemon_functions @ daemon_functions
index 8a061d7..0ed9e45 100644 (file)
@@ -13,6 +13,7 @@ daemon/checksum.c
 daemon/cmp.c
 daemon/command.c
 daemon/compress.c
+daemon/copy.c
 daemon/cpmv.c
 daemon/dd.c
 daemon/debug.c
index 4438e30..95de1ee 100644 (file)
@@ -1 +1 @@
-293
+297
index ada9f1e..e6a16a6 100644 (file)
@@ -313,21 +313,36 @@ in the table below.
 
 =item B<file> to B<file>
 
-Use L</guestfs_cp> to copy a single file, or
-L</guestfs_cp_a> to copy directories recursively.
+Use L</guestfs_cp> to copy a single file, or L</guestfs_cp_a> to copy
+directories recursively.
 
-=item B<file or device> to B<file or device>
+To copy part of a file (offset and size) use
+L</guestfs_copy_file_to_file>.
 
-Use L</guestfs_dd> which efficiently uses L<dd(1)>
-to copy between files and devices in the guest.
+=item B<file> to B<device>
+
+=item B<device> to B<file>
+
+=item B<device> to B<device>
+
+Use L</guestfs_copy_file_to_device>, L</guestfs_copy_device_to_file>,
+or L</guestfs_copy_device_to_device>.
 
 Example: duplicate the contents of an LV:
 
- guestfs_dd (g, "/dev/VG/Original", "/dev/VG/Copy");
+ guestfs_copy_device_to_device (g,
+         "/dev/VG/Original", "/dev/VG/Copy",
+         /* -1 marks the end of the list of optional parameters */
+         -1);
 
 The destination (C</dev/VG/Copy>) must be at least as large as the
-source (C</dev/VG/Original>).  To copy less than the whole
-source device, use L</guestfs_copy_size>.
+source (C</dev/VG/Original>).  To copy less than the whole source
+device, use the optional C<size> parameter:
+
+ guestfs_copy_device_to_device (g,
+         "/dev/VG/Original", "/dev/VG/Copy",
+         GUESTFS_COPY_DEVICE_TO_DEVICE_SIZE, 10000,
+         -1);
 
 =item B<file on the host> to B<file or device>