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.
This commit is contained in:
Daan De Meyer 2023-04-27 20:37:50 +02:00 committed by Luca Boccassi
parent fafded0ce0
commit b640e274a7
8 changed files with 66 additions and 43 deletions

View file

@ -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

View 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;

View 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;

View 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;

View 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;

View 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);

View 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);
}

View 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);