mirror of
https://github.com/libguestfs/libguestfs.git
synced 2026-03-21 22:53:37 +00:00
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:
@@ -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
242
daemon/copy.c
Normal 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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1 +1 @@
|
||||
293
|
||||
297
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user