]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
copy: Introduce reflink() and reflink_full()
authorDaan De Meyer <daan.j.demeyer@gmail.com>
Thu, 27 Apr 2023 18:37:50 +0000 (20:37 +0200)
committerLuca Boccassi <luca.boccassi@gmail.com>
Thu, 27 Apr 2023 23:57:03 +0000 (00:57 +0100)
The kernel has had filesystem independent reflink ioctls for a
while now, let's try to use them and fall back to the btrfs specific
ones if they're not supported.

src/basic/missing_fs.h
src/import/export-raw.c
src/import/import-raw.c
src/import/qcow2-util.c
src/shared/btrfs-util.c
src/shared/btrfs-util.h
src/shared/copy.c
src/shared/copy.h

index 6638d7696221d17ada03e392fad61c89c3bc50a4..9a0b12af4a724f27cae9177f4794f7fe42aeb0d3 100644 (file)
 #define BLKGETDISKSEQ _IOR(0x12,128,__u64)
 #endif
 
+#ifndef FICLONE
+#define FICLONE _IOW(0x94, 9, int)
+#endif
+
+#ifndef FICLONERANGE
+#define FICLONERANGE _IOW(0x94, 13, struct file_clone_range)
+#endif
+
 /* linux/fs.h or sys/mount.h */
 #ifndef MS_MOVE
 #define MS_MOVE 8192
index 44181fd9b2fa8fe4c4f746bfa21a5b97dbfd75d5..5c9c2bbcd947c295b6df831f992a524148fa82f3 100644 (file)
@@ -138,7 +138,7 @@ static int raw_export_process(RawExport *e) {
                  * reflink source to destination directly. Let's see
                  * if this works. */
 
-                r = btrfs_reflink(e->input_fd, e->output_fd);
+                r = reflink(e->input_fd, e->output_fd);
                 if (r >= 0) {
                         r = 0;
                         goto finish;
@@ -257,7 +257,7 @@ static int reflink_snapshot(int fd, const char *path) {
                 (void) unlink(t);
         }
 
-        r = btrfs_reflink(fd, new_fd);
+        r = reflink(fd, new_fd);
         if (r < 0) {
                 safe_close(new_fd);
                 return r;
index 3765b514bb373f9c4e176bc5133271b886559efc..4c9a30292b2521ab73196a30faefad434681a93f 100644 (file)
@@ -335,7 +335,7 @@ static int raw_import_try_reflink(RawImport *i) {
         if ((uint64_t) p != (uint64_t) i->buffer_size)
                 return 0;
 
-        r = btrfs_reflink(i->input_fd, i->output_fd);
+        r = reflink(i->input_fd, i->output_fd);
         if (r >= 0)
                 return 1;
 
index fe2b5350875ca10ec611ce6b9417db0fe678d529..9addb5c55563a7a92c9bee92f0ef829825f503f8 100644 (file)
@@ -69,7 +69,7 @@ static int copy_cluster(
         ssize_t l;
         int r;
 
-        r = btrfs_clone_range(sfd, soffset, dfd, doffset, cluster_size);
+        r = reflink_full(sfd, soffset, dfd, doffset, cluster_size);
         if (r >= 0)
                 return r;
 
index 2789364c98fe84bbcd1b3f01e49a0e94f88ebaef..7909184f2dc7eca25fe1d35e258e7ff062efe909 100644 (file)
@@ -213,40 +213,6 @@ int btrfs_subvol_get_read_only_fd(int fd) {
         return !!(flags & BTRFS_SUBVOL_RDONLY);
 }
 
-int btrfs_reflink(int infd, int outfd) {
-        int r;
-
-        assert(infd >= 0);
-        assert(outfd >= 0);
-
-        /* Make sure we invoke the ioctl on a regular file, so that no device driver accidentally gets it. */
-
-        r = fd_verify_regular(outfd);
-        if (r < 0)
-                return r;
-
-        return RET_NERRNO(ioctl(outfd, BTRFS_IOC_CLONE, infd));
-}
-
-int btrfs_clone_range(int infd, uint64_t in_offset, int outfd, uint64_t out_offset, uint64_t sz) {
-        struct btrfs_ioctl_clone_range_args args = {
-                .src_fd = infd,
-                .src_offset = in_offset,
-                .src_length = sz,
-                .dest_offset = out_offset,
-        };
-        int r;
-
-        assert(infd >= 0);
-        assert(outfd >= 0);
-
-        r = fd_verify_regular(outfd);
-        if (r < 0)
-                return r;
-
-        return RET_NERRNO(ioctl(outfd, BTRFS_IOC_CLONE_RANGE, &args));
-}
-
 int btrfs_get_block_device_at(int dir_fd, const char *path, dev_t *ret) {
         struct btrfs_ioctl_fs_info_args fsi = {};
         _cleanup_close_ int fd = -EBADF;
index 75a8ed85490ec0f37fcbd9e0f048873b7e2d8e55..0ce6d4b96ada717bb8ea1bd57b933c439300efc4 100644 (file)
@@ -46,9 +46,6 @@ typedef enum BtrfsRemoveFlags {
 int btrfs_is_subvol_fd(int fd);
 int btrfs_is_subvol(const char *path);
 
-int btrfs_reflink(int infd, int outfd);
-int btrfs_clone_range(int infd, uint64_t in_offset, int ofd, uint64_t out_offset, uint64_t sz);
-
 int btrfs_get_block_device_at(int dir_fd, const char *path, dev_t *ret);
 static inline int btrfs_get_block_device(const char *path, dev_t *ret) {
         return btrfs_get_block_device_at(AT_FDCWD, path, ret);
index f283394545b85dc5632a03f007dfbcc071b26ecc..dd1306abe97c124482173f98de8398bca8b55ba5 100644 (file)
@@ -2,9 +2,11 @@
 
 #include <errno.h>
 #include <fcntl.h>
+#include <linux/btrfs.h>
 #include <stddef.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <sys/ioctl.h>
 #include <sys/sendfile.h>
 #include <sys/xattr.h>
 #include <unistd.h>
@@ -19,6 +21,7 @@
 #include "fs-util.h"
 #include "io-util.h"
 #include "macro.h"
+#include "missing_fs.h"
 #include "missing_syscall.h"
 #include "mkdir-label.h"
 #include "mountpoint-util.h"
@@ -198,9 +201,9 @@ int copy_bytes_full(
                         if (toffset >= 0) {
 
                                 if (foffset == 0 && toffset == 0 && max_bytes == UINT64_MAX)
-                                        r = btrfs_reflink(fdf, fdt); /* full file reflink */
+                                        r = reflink(fdf, fdt); /* full file reflink */
                                 else
-                                        r = btrfs_clone_range(fdf, foffset, fdt, toffset, max_bytes == UINT64_MAX ? 0 : max_bytes); /* partial reflink */
+                                        r = reflink_full(fdf, foffset, fdt, toffset, max_bytes == UINT64_MAX ? 0 : max_bytes); /* partial reflink */
                                 if (r >= 0) {
                                         off_t t;
 
@@ -1594,3 +1597,49 @@ int copy_xattr(int df, const char *from, int dt, const char *to, CopyFlags copy_
 
         return ret;
 }
+
+int reflink(int infd, int outfd) {
+        int r;
+
+        assert(infd >= 0);
+        assert(outfd >= 0);
+
+        /* Make sure we invoke the ioctl on a regular file, so that no device driver accidentally gets it. */
+
+        r = fd_verify_regular(outfd);
+        if (r < 0)
+                return r;
+
+        /* FICLONE was introduced in Linux 4.5, so let's fall back to BTRFS_IOC_CLONE if it's not supported. */
+
+        r = ioctl(outfd, FICLONE, infd);
+        if (r < 0 && ERRNO_IS_NOT_SUPPORTED(errno))
+                r = ioctl(outfd, BTRFS_IOC_CLONE, infd);
+
+        return RET_NERRNO(r);
+}
+
+assert_cc(sizeof(struct file_clone_range) == sizeof(struct btrfs_ioctl_clone_range_args));
+
+int reflink_full(int infd, uint64_t in_offset, int outfd, uint64_t out_offset, uint64_t sz) {
+        struct file_clone_range args = {
+                .src_fd = infd,
+                .src_offset = in_offset,
+                .src_length = sz,
+                .dest_offset = out_offset,
+        };
+        int r;
+
+        assert(infd >= 0);
+        assert(outfd >= 0);
+
+        r = fd_verify_regular(outfd);
+        if (r < 0)
+                return r;
+
+        r = ioctl(outfd, FICLONERANGE, &args);
+        if (r < 0 && ERRNO_IS_NOT_SUPPORTED(errno))
+                r = ioctl(outfd, BTRFS_IOC_CLONE_RANGE, &args);
+
+        return RET_NERRNO(r);
+}
index 9e67838a99a4e8fbbefe75f38a43d0b3ffc93604..4f97e542f5f1c4656e33b85e0c2e06d2c168d387 100644 (file)
@@ -104,3 +104,6 @@ static inline int copy_rights(int fdf, int fdt) {
         return copy_rights_with_fallback(fdf, fdt, NULL); /* no fallback */
 }
 int copy_xattr(int df, const char *from, int dt, const char *to, CopyFlags copy_flags);
+
+int reflink(int infd, int outfd);
+int reflink_full(int infd, uint64_t in_offset, int outfd, uint64_t out_offset, uint64_t sz);