summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaan De Meyer <daan.j.demeyer@gmail.com>2023-04-27 20:37:50 +0200
committerLuca Boccassi <luca.boccassi@gmail.com>2023-04-28 00:57:03 +0100
commitb640e274a7c363a2b6394c9dce5671d9404d2e2a (patch)
treec19180439582ff18879d13ef8964228e9cccd042
parentfafded0ce0902e948cdeeaa6a12609a28d9c3ded (diff)
downloadsystemd-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.h8
-rw-r--r--src/import/export-raw.c4
-rw-r--r--src/import/import-raw.c2
-rw-r--r--src/import/qcow2-util.c2
-rw-r--r--src/shared/btrfs-util.c34
-rw-r--r--src/shared/btrfs-util.h3
-rw-r--r--src/shared/copy.c53
-rw-r--r--src/shared/copy.h3
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);