#include <sys/sendfile.h>
#include "util.h"
+#include "btrfs-util.h"
#include "copy.h"
-int copy_bytes(int fdf, int fdt, off_t max_bytes) {
+int copy_bytes(int fdf, int fdt, off_t max_bytes, bool try_reflink) {
bool try_sendfile = true;
+ int r;
assert(fdf >= 0);
assert(fdt >= 0);
+ /* Try btrfs reflinks first. */
+ if (try_reflink && max_bytes == (off_t) -1) {
+ r = btrfs_reflink(fdf, fdt);
+ if (r >= 0)
+ return r;
+ }
+
for (;;) {
size_t m = PIPE_BUF;
ssize_t n;
/* As a fallback just copy bits by hand */
{
char buf[m];
- ssize_t k;
n = read(fdf, buf, m);
if (n < 0)
if (n == 0) /* EOF */
break;
- errno = 0;
- k = loop_write(fdt, buf, n, false);
- if (k < 0)
- return k;
- if (k != n)
- return errno ? -errno : -EIO;
-
+ r = loop_write(fdt, buf, (size_t) n, false);
+ if (r < 0)
+ return r;
}
next:
if (fdt < 0)
return -errno;
- r = copy_bytes(fdf, fdt, (off_t) -1);
+ r = copy_bytes(fdf, fdt, (off_t) -1, true);
if (r < 0) {
unlinkat(dt, to, 0);
return r;
return r;
}
-static int fd_copy_directory(int df, const char *from, const struct stat *st, int dt, const char *to, dev_t original_device, bool merge) {
+static int fd_copy_directory(
+ int df,
+ const char *from,
+ const struct stat *st,
+ int dt,
+ const char *to,
+ dev_t original_device,
+ bool merge) {
+
_cleanup_close_ int fdf = -1, fdt = -1;
_cleanup_closedir_ DIR *d = NULL;
struct dirent *de;
bool created;
int r;
- assert(from);
assert(st);
assert(to);
- fdf = openat(df, from, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
- if (fdf < 0)
- return -errno;
+ if (from)
+ fdf = openat(df, from, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
+ else
+ fdf = fcntl(df, F_DUPFD_CLOEXEC, 3);
d = fdopendir(fdf);
if (!d)
return -ENOTSUP;
}
-int copy_file_fd(const char *from, int fdt) {
+int copy_tree_fd(int dirfd, const char *to, bool merge) {
+
+ struct stat st;
+
+ assert(dirfd >= 0);
+ assert(to);
+
+ if (fstat(dirfd, &st) < 0)
+ return -errno;
+
+ if (!S_ISDIR(st.st_mode))
+ return -ENOTDIR;
+
+ return fd_copy_directory(dirfd, NULL, &st, AT_FDCWD, to, st.st_dev, merge);
+}
+
+int copy_file_fd(const char *from, int fdt, bool try_reflink) {
_cleanup_close_ int fdf = -1;
assert(from);
if (fdf < 0)
return -errno;
- return copy_bytes(fdf, fdt, (off_t) -1);
+ return copy_bytes(fdf, fdt, (off_t) -1, try_reflink);
}
int copy_file(const char *from, const char *to, int flags, mode_t mode) {
if (fdt < 0)
return -errno;
- r = copy_file_fd(from, fdt);
+ r = copy_file_fd(from, fdt, true);
if (r < 0) {
close(fdt);
unlink(to);