diff --git a/src/basic/missing_fs.h b/src/basic/missing_fs.h index 6638d769622..9a0b12af4a7 100644 --- a/src/basic/missing_fs.h +++ b/src/basic/missing_fs.h @@ -10,6 +10,14 @@ #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 diff --git a/src/import/export-raw.c b/src/import/export-raw.c index 44181fd9b2f..5c9c2bbcd94 100644 --- a/src/import/export-raw.c +++ b/src/import/export-raw.c @@ -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; diff --git a/src/import/import-raw.c b/src/import/import-raw.c index 3765b514bb3..4c9a30292b2 100644 --- a/src/import/import-raw.c +++ b/src/import/import-raw.c @@ -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; diff --git a/src/import/qcow2-util.c b/src/import/qcow2-util.c index fe2b5350875..9addb5c5556 100644 --- a/src/import/qcow2-util.c +++ b/src/import/qcow2-util.c @@ -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; diff --git a/src/shared/btrfs-util.c b/src/shared/btrfs-util.c index 2789364c98f..7909184f2dc 100644 --- a/src/shared/btrfs-util.c +++ b/src/shared/btrfs-util.c @@ -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; diff --git a/src/shared/btrfs-util.h b/src/shared/btrfs-util.h index 75a8ed85490..0ce6d4b96ad 100644 --- a/src/shared/btrfs-util.h +++ b/src/shared/btrfs-util.h @@ -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); diff --git a/src/shared/copy.c b/src/shared/copy.c index f283394545b..dd1306abe97 100644 --- a/src/shared/copy.c +++ b/src/shared/copy.c @@ -2,9 +2,11 @@ #include #include +#include #include #include #include +#include #include #include #include @@ -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); +} diff --git a/src/shared/copy.h b/src/shared/copy.h index 9e67838a99a..4f97e542f5f 100644 --- a/src/shared/copy.h +++ b/src/shared/copy.h @@ -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);