1
0
mirror of https://github.com/systemd/systemd.git synced 2024-11-06 16:59:03 +03:00

copy: tweak reflink logic in copy_bytes() a bit

Let's use btrfs_clone_range() if partial copies are desired. And use
btrfs_reflink() only for full-file reflinks.
This commit is contained in:
Lennart Poettering 2018-03-23 16:36:56 +01:00
parent 78ba8cf770
commit 5de6e11673

View File

@ -69,15 +69,59 @@ int copy_bytes(int fdf, int fdt, uint64_t max_bytes, CopyFlags copy_flags) {
* of 'max_bytes', which may be specified as UINT64_MAX, in which no maximum is applied. Returns negative on * of 'max_bytes', which may be specified as UINT64_MAX, in which no maximum is applied. Returns negative on
* error, zero if EOF is hit before the bytes limit is hit and positive otherwise. */ * error, zero if EOF is hit before the bytes limit is hit and positive otherwise. */
/* Try btrfs reflinks first. */ /* Try btrfs reflinks first. This only works on regular, seekable files, hence let's check the file offsets of
if ((copy_flags & COPY_REFLINK) && * source and destination first. */
max_bytes == (uint64_t) -1 && if ((copy_flags & COPY_REFLINK)) {
lseek(fdf, 0, SEEK_CUR) == 0 && off_t foffset;
lseek(fdt, 0, SEEK_CUR) == 0) {
r = btrfs_reflink(fdf, fdt); foffset = lseek(fdf, 0, SEEK_CUR);
if (r >= 0) if (foffset >= 0) {
return 0; /* we copied the whole thing, hence hit EOF, return 0 */ off_t toffset;
toffset = lseek(fdt, 0, SEEK_CUR);
if (toffset >= 0) {
if (foffset == 0 && toffset == 0 && max_bytes == UINT64_MAX)
r = btrfs_reflink(fdf, fdt); /* full file reflink */
else
r = btrfs_clone_range(fdf, foffset, fdt, toffset, max_bytes == UINT64_MAX ? 0 : max_bytes); /* partial reflink */
if (r >= 0) {
off_t t;
/* This worked, yay! Now — to be fully correct — let's adjust the file pointers */
if (max_bytes == UINT64_MAX) {
/* We cloned to the end of the source file, let's position the read
* pointer there, and query it at the same time. */
t = lseek(fdf, 0, SEEK_END);
if (t < 0)
return -errno;
if (t < foffset)
return -ESPIPE;
/* Let's adjust the destination file write pointer by the same number
* of bytes. */
t = lseek(fdt, toffset + (t - foffset), SEEK_SET);
if (t < 0)
return -errno;
return 0; /* we copied the whole thing, hence hit EOF, return 0 */
} else {
t = lseek(fdf, foffset + max_bytes, SEEK_SET);
if (t < 0)
return -errno;
t = lseek(fdt, toffset + max_bytes, SEEK_SET);
if (t < 0)
return -errno;
return 1; /* we copied only some number of bytes, which worked, but this means we didn't hit EOF, return 1 */
}
}
log_debug_errno(r, "Reflinking didn't work, falling back to non-reflink copying: %m");
}
}
} }
for (;;) { for (;;) {