From f223dfa29aafeb7b9a07bbedf03d15acddd62e92 Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Tue, 25 Oct 2011 22:49:55 +0100 Subject: [PATCH] New APIs: copy-{file,device}-to-{file,device}. 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 | 1 + daemon/copy.c | 242 +++++++++++++++++++++++++++++++++++++++++ generator/generator_actions.ml | 61 ++++++++++- po/POTFILES.in | 1 + src/MAX_PROC_NR | 2 +- src/guestfs.pod | 31 ++++-- 6 files changed, 326 insertions(+), 12 deletions(-) create mode 100644 daemon/copy.c diff --git a/daemon/Makefile.am b/daemon/Makefile.am index e23ce86..f8e0ecb 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -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 index 0000000..1d515f8 --- /dev/null +++ b/daemon/copy.c @@ -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 + +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/generator/generator_actions.ml b/generator/generator_actions.ml index ce3382f..f10a040 100644 --- a/generator/generator_actions.ml +++ b/generator/generator_actions.ml @@ -5168,7 +5168,7 @@ See also C. =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)."); +This command cannot do partial copies +(see C)."); ("filesize", (RInt64 "size", [Pathname "file"], []), 218, [], [InitScratchFS, Always, TestOutputInt ( @@ -5276,7 +5277,7 @@ calls to associate logical volumes and volume groups. See also C."); - ("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. See also C."); + + ("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, +C, +C, and +C +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 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 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 for a general overview +of this call. + +This is B the function you want for copying files. This +is for copying blocks within existing files. See C, +C and C for general file copying and +moving functions."); + ] let all_functions = non_daemon_functions @ daemon_functions diff --git a/po/POTFILES.in b/po/POTFILES.in index 8a061d7..0ed9e45 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -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 diff --git a/src/MAX_PROC_NR b/src/MAX_PROC_NR index 4438e30..95de1ee 100644 --- a/src/MAX_PROC_NR +++ b/src/MAX_PROC_NR @@ -1 +1 @@ -293 +297 diff --git a/src/guestfs.pod b/src/guestfs.pod index ada9f1e..e6a16a6 100644 --- a/src/guestfs.pod +++ b/src/guestfs.pod @@ -313,21 +313,36 @@ in the table below. =item B to B -Use L to copy a single file, or -L to copy directories recursively. +Use L to copy a single file, or L to copy +directories recursively. -=item B to B +To copy part of a file (offset and size) use +L. -Use L which efficiently uses L -to copy between files and devices in the guest. +=item B to B + +=item B to B + +=item B to B + +Use L, L, +or L. 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) must be at least as large as the -source (C). To copy less than the whole -source device, use L. +source (C). To copy less than the whole source +device, use the optional C parameter: + + guestfs_copy_device_to_device (g, + "/dev/VG/Original", "/dev/VG/Copy", + GUESTFS_COPY_DEVICE_TO_DEVICE_SIZE, 10000, + -1); =item B to B -- 1.8.3.1