diff options
author | Daan De Meyer <daan.j.demeyer@gmail.com> | 2023-04-27 20:37:50 +0200 |
---|---|---|
committer | Luca Boccassi <luca.boccassi@gmail.com> | 2023-04-28 00:57:03 +0100 |
commit | b640e274a7c363a2b6394c9dce5671d9404d2e2a (patch) | |
tree | c19180439582ff18879d13ef8964228e9cccd042 | |
parent | fafded0ce0902e948cdeeaa6a12609a28d9c3ded (diff) | |
download | systemd-b640e274a7c363a2b6394c9dce5671d9404d2e2a.tar.gz |
copy: Introduce reflink() and reflink_full()
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.
-rw-r--r-- | src/basic/missing_fs.h | 8 | ||||
-rw-r--r-- | src/import/export-raw.c | 4 | ||||
-rw-r--r-- | src/import/import-raw.c | 2 | ||||
-rw-r--r-- | src/import/qcow2-util.c | 2 | ||||
-rw-r--r-- | src/shared/btrfs-util.c | 34 | ||||
-rw-r--r-- | src/shared/btrfs-util.h | 3 | ||||
-rw-r--r-- | src/shared/copy.c | 53 | ||||
-rw-r--r-- | src/shared/copy.h | 3 |
8 files changed, 66 insertions, 43 deletions
diff --git a/src/basic/missing_fs.h b/src/basic/missing_fs.h index 6638d76962..9a0b12af4a 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 44181fd9b2..5c9c2bbcd9 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 3765b514bb..4c9a30292b 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 fe2b535087..9addb5c555 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 2789364c98..7909184f2d 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 75a8ed8549..0ce6d4b96a 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 f283394545..dd1306abe9 100644 --- a/src/shared/copy.c +++ b/src/shared/copy.c @@ -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); +} diff --git a/src/shared/copy.h b/src/shared/copy.h index 9e67838a99..4f97e542f5 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); |