chiark / gitweb /
copy: teach copy_bytes() btrfs reflink magic
[elogind.git] / src / shared / copy.c
index abb7fbc52b7bdbda65bd3defeb1331770ef7ba0c..4c41f2fdd4c24bb7dcf57ac300d7bfc88f23a84f 100644 (file)
 #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) {
         bool try_sendfile = true;
+        int r;
 
         assert(fdf >= 0);
         assert(fdt >= 0);
 
+        /* Try btrfs reflinks first. */
+        if (max_bytes == (off_t) -1) {
+                r = btrfs_reflink(fdf, fdt);
+                if (r >= 0)
+                        return 0;
+        }
+
         for (;;) {
                 size_t m = PIPE_BUF;
                 ssize_t n;
@@ -63,7 +72,6 @@ int copy_bytes(int fdf, int fdt, off_t max_bytes) {
                 /* As a fallback just copy bits by hand */
                 {
                         char buf[m];
-                        ssize_t k;
 
                         n = read(fdf, buf, m);
                         if (n < 0)
@@ -71,12 +79,9 @@ int copy_bytes(int fdf, int fdt, off_t max_bytes) {
                         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;
 
                 }
 
@@ -190,20 +195,28 @@ static int fd_copy_node(int df, const char *from, const struct stat *st, int dt,
         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)
@@ -290,6 +303,22 @@ int copy_tree(const char *from, const char *to, bool merge) {
                 return -ENOTSUP;
 }
 
+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) {
         _cleanup_close_ int fdf = -1;