mirror of
https://github.com/systemd/systemd-stable.git
synced 2025-01-06 13:17:44 +03:00
file-io: Fix copying sparse files
This change makes sure a data copy using copy_bytes() does not exceed the
max_bytes value when using COPY_HOLES and max_bytes stops before the next
data section.
(cherry picked from commit c2dfcbd48e
)
This commit is contained in:
parent
5c3fa0cfee
commit
7bfe3c6215
@ -275,9 +275,22 @@ int copy_bytes_full(
|
||||
/* If we're in a hole (current offset is not a data offset), create a hole of the
|
||||
* same size in the target file. */
|
||||
if (e > c) {
|
||||
r = create_hole(fdt, e - c);
|
||||
/* Make sure our new hole doesn't go over the maximum size we're allowed to copy. */
|
||||
n = MIN(max_bytes, (uint64_t) e - c);
|
||||
r = create_hole(fdt, n);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Make sure holes are taken into account in the maximum size we're supposed to copy. */
|
||||
if (max_bytes != UINT64_MAX) {
|
||||
max_bytes -= n;
|
||||
if (max_bytes <= 0)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Update the size we're supposed to copy in this iteration if needed. */
|
||||
if (m > max_bytes)
|
||||
m = max_bytes;
|
||||
}
|
||||
|
||||
c = e; /* Set c to the start of the data segment. */
|
||||
|
@ -11,10 +11,12 @@
|
||||
#include "fileio.h"
|
||||
#include "fs-util.h"
|
||||
#include "hexdecoct.h"
|
||||
#include "io-util.h"
|
||||
#include "log.h"
|
||||
#include "macro.h"
|
||||
#include "mkdir.h"
|
||||
#include "path-util.h"
|
||||
#include "random-util.h"
|
||||
#include "rm-rf.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
@ -436,6 +438,77 @@ TEST_RET(copy_holes) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
TEST_RET(copy_holes_with_gaps) {
|
||||
_cleanup_(rm_rf_physical_and_freep) char *t = NULL;
|
||||
_cleanup_close_ int tfd = -EBADF, fd = -EBADF, fd_copy = -EBADF;
|
||||
struct stat st;
|
||||
off_t blksz;
|
||||
char *buf;
|
||||
int r;
|
||||
|
||||
assert_se((tfd = mkdtemp_open(NULL, 0, &t)) >= 0);
|
||||
assert_se((fd = openat(tfd, "src", O_CREAT | O_RDWR, 0600)) >= 0);
|
||||
assert_se((fd_copy = openat(tfd, "dst", O_CREAT | O_WRONLY, 0600)) >= 0);
|
||||
|
||||
assert_se(fstat(fd, &st) >= 0);
|
||||
blksz = st.st_blksize;
|
||||
buf = alloca_safe(blksz);
|
||||
memset(buf, 1, blksz);
|
||||
|
||||
/* Create a file with:
|
||||
* - hole of 1 block
|
||||
* - data of 2 block
|
||||
* - hole of 2 blocks
|
||||
* - data of 1 block
|
||||
*
|
||||
* Since sparse files are based on blocks and not bytes, we need to make
|
||||
* sure that the holes are aligned to the block size.
|
||||
*/
|
||||
|
||||
r = RET_NERRNO(fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 0, blksz));
|
||||
if (ERRNO_IS_NOT_SUPPORTED(r))
|
||||
return log_tests_skipped("Filesystem doesn't support hole punching");
|
||||
|
||||
assert_se(lseek(fd, blksz, SEEK_CUR) >= 0);
|
||||
assert_se(loop_write(fd, buf, blksz, 0) >= 0);
|
||||
assert_se(loop_write(fd, buf, blksz, 0) >= 0);
|
||||
assert_se(lseek(fd, 2 * blksz, SEEK_CUR) >= 0);
|
||||
assert_se(loop_write(fd, buf, blksz, 0) >= 0);
|
||||
assert_se(lseek(fd, 0, SEEK_SET) >= 0);
|
||||
assert_se(fsync(fd) >= 0);
|
||||
|
||||
/* Copy to the start of the second hole */
|
||||
assert_se(copy_bytes(fd, fd_copy, 3 * blksz, COPY_HOLES) >= 0);
|
||||
assert_se(fstat(fd_copy, &st) >= 0);
|
||||
assert_se(st.st_size == 3 * blksz);
|
||||
|
||||
/* Copy to the middle of the second hole */
|
||||
assert_se(lseek(fd, 0, SEEK_SET) >= 0);
|
||||
assert_se(lseek(fd_copy, 0, SEEK_SET) >= 0);
|
||||
assert_se(ftruncate(fd_copy, 0) >= 0);
|
||||
assert_se(copy_bytes(fd, fd_copy, 4 * blksz, COPY_HOLES) >= 0);
|
||||
assert_se(fstat(fd_copy, &st) >= 0);
|
||||
assert_se(st.st_size == 4 * blksz);
|
||||
|
||||
/* Copy to the end of the second hole */
|
||||
assert_se(lseek(fd, 0, SEEK_SET) >= 0);
|
||||
assert_se(lseek(fd_copy, 0, SEEK_SET) >= 0);
|
||||
assert_se(ftruncate(fd_copy, 0) >= 0);
|
||||
assert_se(copy_bytes(fd, fd_copy, 5 * blksz, COPY_HOLES) >= 0);
|
||||
assert_se(fstat(fd_copy, &st) >= 0);
|
||||
assert_se(st.st_size == 5 * blksz);
|
||||
|
||||
/* Copy everything */
|
||||
assert_se(lseek(fd, 0, SEEK_SET) >= 0);
|
||||
assert_se(lseek(fd_copy, 0, SEEK_SET) >= 0);
|
||||
assert_se(ftruncate(fd_copy, 0) >= 0);
|
||||
assert_se(copy_bytes(fd, fd_copy, UINT64_MAX, COPY_HOLES) >= 0);
|
||||
assert_se(fstat(fd_copy, &st) >= 0);
|
||||
assert_se(st.st_size == 6 * blksz);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
TEST(copy_lock) {
|
||||
_cleanup_(rm_rf_physical_and_freep) char *t = NULL;
|
||||
_cleanup_close_ int tfd = -EBADF, fd = -EBADF;
|
||||
|
Loading…
Reference in New Issue
Block a user