summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2018-03-23 16:36:56 +0100
committerSven Eden <yamakuzure@gmx.net>2018-08-24 16:47:08 +0200
commit0c28c8723ac7d39dbba1b9679fb0d4bc16d83ab0 (patch)
tree30420028f3be5440060fd0ba1077d5b1b273c878 /src
parentaed713497cfa93e0ab3397a82af013381b4a370b (diff)
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.
Diffstat (limited to 'src')
-rw-r--r--src/basic/copy.c62
1 files changed, 53 insertions, 9 deletions
diff --git a/src/basic/copy.c b/src/basic/copy.c
index 673272d65..478911285 100644
--- a/src/basic/copy.c
+++ b/src/basic/copy.c
@@ -70,15 +70,59 @@ int copy_bytes(int fdf, int fdt, uint64_t max_bytes, CopyFlags copy_flags) {
* error, zero if EOF is hit before the bytes limit is hit and positive otherwise. */
#if 0 /// UNNEEDED by elogind
- /* Try btrfs reflinks first. */
- if ((copy_flags & COPY_REFLINK) &&
- max_bytes == (uint64_t) -1 &&
- lseek(fdf, 0, SEEK_CUR) == 0 &&
- lseek(fdt, 0, SEEK_CUR) == 0) {
-
- r = btrfs_reflink(fdf, fdt);
- if (r >= 0)
- return 0; /* we copied the whole thing, hence hit EOF, return 0 */
+ /* Try btrfs reflinks first. This only works on regular, seekable files, hence let's check the file offsets of
+ * source and destination first. */
+ if ((copy_flags & COPY_REFLINK)) {
+ off_t foffset;
+
+ foffset = lseek(fdf, 0, SEEK_CUR);
+ if (foffset >= 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");
+ }
+ }
}
#endif // 0