mirror of
https://github.com/libguestfs/libguestfs.git
synced 2026-03-21 22:53:37 +00:00
Add 'sparse' option to copy-{device,file}-to-{device,file} calls.
Setting the 'sparse' optional boolean causes writes to be omitted if the block to be written contains all zero bytes. This should help with sparse backing files (eg. raw, qcow2, dm-thin, etc). Also, modify virt-resize to use this option by default when copying devices. The savings in virt-resize can be quite startling, eg 'du -sh' (ie. true size) of a resized disk image: 8.1G /tmp/f15x32-resized.img # before this change 3.2G /tmp/f15x32-resized.img # after this change
This commit is contained in:
@@ -42,7 +42,7 @@ 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 srcoffset, int64_t destoffset, int64_t size, int sparse)
|
||||
{
|
||||
int64_t saved_size = size;
|
||||
int src_fd, dest_fd;
|
||||
@@ -77,6 +77,9 @@ copy (const char *src, const char *src_display,
|
||||
else
|
||||
size = -1;
|
||||
|
||||
if (! (optargs_bitmask & GUESTFS_COPY_DEVICE_TO_DEVICE_SPARSE_BITMASK))
|
||||
sparse = 0;
|
||||
|
||||
/* Open source and destination. */
|
||||
src_fd = open (src, O_RDONLY|O_CLOEXEC);
|
||||
if (src_fd == -1) {
|
||||
@@ -133,6 +136,18 @@ copy (const char *src, const char *src_display,
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (sparse && is_zero (buf, r)) {
|
||||
if (lseek (dest_fd, r, SEEK_CUR) == -1) {
|
||||
if (size == -1)
|
||||
pulse_mode_cancel ();
|
||||
reply_with_perror ("%s: seek (because of sparse flag)", dest_display);
|
||||
close (src_fd);
|
||||
close (dest_fd);
|
||||
return -1;
|
||||
}
|
||||
goto sparse_skip;
|
||||
}
|
||||
|
||||
if (xwrite (dest_fd, buf, r) == -1) {
|
||||
if (size == -1)
|
||||
pulse_mode_cancel ();
|
||||
@@ -141,6 +156,7 @@ copy (const char *src, const char *src_display,
|
||||
close (dest_fd);
|
||||
return -1;
|
||||
}
|
||||
sparse_skip:
|
||||
|
||||
if (size != -1) {
|
||||
size -= r;
|
||||
@@ -167,15 +183,17 @@ copy (const char *src, const char *src_display,
|
||||
|
||||
int
|
||||
do_copy_device_to_device (const char *src, const char *dest,
|
||||
int64_t srcoffset, int64_t destoffset, int64_t size)
|
||||
int64_t srcoffset, int64_t destoffset, int64_t size,
|
||||
int sparse)
|
||||
{
|
||||
return copy (src, src, dest, dest, DEST_DEVICE_FLAGS,
|
||||
srcoffset, destoffset, size);
|
||||
srcoffset, destoffset, size, sparse);
|
||||
}
|
||||
|
||||
int
|
||||
do_copy_device_to_file (const char *src, const char *dest,
|
||||
int64_t srcoffset, int64_t destoffset, int64_t size)
|
||||
int64_t srcoffset, int64_t destoffset, int64_t size,
|
||||
int sparse)
|
||||
{
|
||||
CLEANUP_FREE char *dest_buf = sysroot_path (dest);
|
||||
|
||||
@@ -185,12 +203,13 @@ do_copy_device_to_file (const char *src, const char *dest,
|
||||
}
|
||||
|
||||
return copy (src, src, dest_buf, dest, DEST_FILE_FLAGS,
|
||||
srcoffset, destoffset, size);
|
||||
srcoffset, destoffset, size, sparse);
|
||||
}
|
||||
|
||||
int
|
||||
do_copy_file_to_device (const char *src, const char *dest,
|
||||
int64_t srcoffset, int64_t destoffset, int64_t size)
|
||||
int64_t srcoffset, int64_t destoffset, int64_t size,
|
||||
int sparse)
|
||||
{
|
||||
CLEANUP_FREE char *src_buf = sysroot_path (src);
|
||||
|
||||
@@ -200,12 +219,13 @@ do_copy_file_to_device (const char *src, const char *dest,
|
||||
}
|
||||
|
||||
return copy (src_buf, src, dest, dest, DEST_DEVICE_FLAGS,
|
||||
srcoffset, destoffset, size);
|
||||
srcoffset, destoffset, size, sparse);
|
||||
}
|
||||
|
||||
int
|
||||
do_copy_file_to_file (const char *src, const char *dest,
|
||||
int64_t srcoffset, int64_t destoffset, int64_t size)
|
||||
int64_t srcoffset, int64_t destoffset, int64_t size,
|
||||
int sparse)
|
||||
{
|
||||
CLEANUP_FREE char *src_buf = NULL, *dest_buf = NULL;
|
||||
|
||||
@@ -222,5 +242,5 @@ do_copy_file_to_file (const char *src, const char *dest,
|
||||
}
|
||||
|
||||
return copy (src_buf, src, dest_buf, dest, DEST_FILE_FLAGS,
|
||||
srcoffset, destoffset, size);
|
||||
srcoffset, destoffset, size, sparse);
|
||||
}
|
||||
|
||||
@@ -8784,7 +8784,7 @@ See also C<guestfs_part_to_dev>." };
|
||||
|
||||
{ defaults with
|
||||
name = "copy_device_to_device";
|
||||
style = RErr, [Device "src"; Device "dest"], [OInt64 "srcoffset"; OInt64 "destoffset"; OInt64 "size"];
|
||||
style = RErr, [Device "src"; Device "dest"], [OInt64 "srcoffset"; OInt64 "destoffset"; OInt64 "size"; OBool "sparse"];
|
||||
proc_nr = Some 294;
|
||||
progress = true;
|
||||
shortdesc = "copy from source device to destination device";
|
||||
@@ -8806,11 +8806,17 @@ 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." };
|
||||
the destination file is not large enough, it is extended.
|
||||
|
||||
If the C<sparse> flag is true then the call avoids writing
|
||||
blocks that contain only zeroes, which can help in some situations
|
||||
where the backing disk is thin-provisioned. Note that unless
|
||||
the target is already zeroed, using this option will result
|
||||
in incorrect copying." };
|
||||
|
||||
{ defaults with
|
||||
name = "copy_device_to_file";
|
||||
style = RErr, [Device "src"; Pathname "dest"], [OInt64 "srcoffset"; OInt64 "destoffset"; OInt64 "size"];
|
||||
style = RErr, [Device "src"; Pathname "dest"], [OInt64 "srcoffset"; OInt64 "destoffset"; OInt64 "size"; OBool "sparse"];
|
||||
proc_nr = Some 295;
|
||||
progress = true;
|
||||
shortdesc = "copy from source device to destination file";
|
||||
@@ -8820,7 +8826,7 @@ of this call." };
|
||||
|
||||
{ defaults with
|
||||
name = "copy_file_to_device";
|
||||
style = RErr, [Pathname "src"; Device "dest"], [OInt64 "srcoffset"; OInt64 "destoffset"; OInt64 "size"];
|
||||
style = RErr, [Pathname "src"; Device "dest"], [OInt64 "srcoffset"; OInt64 "destoffset"; OInt64 "size"; OBool "sparse"];
|
||||
proc_nr = Some 296;
|
||||
progress = true;
|
||||
shortdesc = "copy from source file to destination device";
|
||||
@@ -8830,15 +8836,23 @@ of this call." };
|
||||
|
||||
{ defaults with
|
||||
name = "copy_file_to_file";
|
||||
style = RErr, [Pathname "src"; Pathname "dest"], [OInt64 "srcoffset"; OInt64 "destoffset"; OInt64 "size"];
|
||||
style = RErr, [Pathname "src"; Pathname "dest"], [OInt64 "srcoffset"; OInt64 "destoffset"; OInt64 "size"; OBool "sparse"];
|
||||
proc_nr = Some 297;
|
||||
progress = true;
|
||||
tests = [
|
||||
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_file_to_file"; "/copyff/src"; "/copyff/dest"; ""; ""; ""; ""];
|
||||
["read_file"; "/copyff/dest"]], "hello, world");
|
||||
let size = 1024 * 1024 in
|
||||
InitScratchFS, Always, TestOutputTrue (
|
||||
[["mkdir"; "/copyff2"];
|
||||
["fill"; "0"; string_of_int size; "/copyff2/src"];
|
||||
["touch"; "/copyff2/dest"];
|
||||
["truncate_size"; "/copyff2/dest"; string_of_int size];
|
||||
["copy_file_to_file"; "/copyff2/src"; "/copyff2/dest"; ""; ""; ""; "true"];
|
||||
["is_zero"; "/copyff2/dest"]])
|
||||
];
|
||||
shortdesc = "copy from source file to destination file";
|
||||
longdesc = "\
|
||||
|
||||
@@ -1035,7 +1035,7 @@ let () =
|
||||
|
||||
(match p.p_type with
|
||||
| ContentUnknown | ContentPV _ | ContentFS _ ->
|
||||
g#copy_device_to_device ~size:copysize source target
|
||||
g#copy_device_to_device ~size:copysize ~sparse:true source target
|
||||
|
||||
| ContentExtendedPartition ->
|
||||
(* You can't just copy an extended partition by name, eg.
|
||||
|
||||
@@ -671,6 +671,24 @@ C<UNMOUNTABLE_BOOT_VOLUME> BSOD. This error is caused by having
|
||||
C<ExtendOemPartition=1> in the sysprep.inf file. Removing this line
|
||||
before sysprepping should fix the problem.
|
||||
|
||||
=head2 SPARSE COPYING
|
||||
|
||||
You must create a fresh, zeroed target disk image for virt-resize to
|
||||
use. Do not reuse a target, especially one which contains data
|
||||
already.
|
||||
|
||||
Virt-resize performs sparse copying. This means that it does not copy
|
||||
blocks from the source disk which are all zeroes. This improves speed
|
||||
and efficiency, but will produce incorrect results if the target disk
|
||||
image contains unzeroed data.
|
||||
|
||||
The main time this can be a problem is if the target is a host
|
||||
partition (eg. S<C<virt-resize source.img /dev/sda4>>) because the
|
||||
usual partitioning tools tend to leave whatever data happened to be on
|
||||
the disk when making partitions. In rare cases you may need to
|
||||
S<C<dd if=/dev/zero of=/dev/sdXN>> first to ensure the target
|
||||
partition is zeroed.
|
||||
|
||||
=head1 ALTERNATIVE TOOLS
|
||||
|
||||
There are several proprietary tools for resizing partitions. We
|
||||
|
||||
Reference in New Issue
Block a user