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.
This commit is contained in:
Richard W.M. Jones
2011-10-25 22:49:55 +01:00
parent 663b99950d
commit f223dfa29a
6 changed files with 326 additions and 12 deletions

View File

@@ -95,6 +95,7 @@ guestfsd_SOURCES = \
cmp.c \
command.c \
compress.c \
copy.c \
cpmv.c \
daemon.h \
dd.c \

242
daemon/copy.c Normal file
View File

@@ -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;
}

View 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

View 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

View File

@@ -1 +1 @@
293
297

View 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>