#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
* 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;
(void) unlink(t);
}
- r = btrfs_reflink(fd, new_fd);
+ r = reflink(fd, new_fd);
if (r < 0) {
safe_close(new_fd);
return r;
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;
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;
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;
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);
#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>
#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"
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;
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);
+}
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);