chiark / gitweb /
systemctl: refuse to edit runtime dropins when they already exist in /etc
[elogind.git] / src / shared / copy.c
index a863246b2bf47c5ed821f2603f8ddb9f0ea3c38a..b4a85c7bff0a12f76835a7cce3f867be37951b30 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) {
+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;
@@ -37,7 +46,7 @@ int copy_bytes(int fdf, int fdt, off_t max_bytes) {
                 if (max_bytes != (off_t) -1) {
 
                         if (max_bytes <= 0)
-                                return -E2BIG;
+                                return -EFBIG;
 
                         if ((off_t) m > max_bytes)
                                 m = (size_t) max_bytes;
@@ -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,13 +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;
                 }
 
         next:
@@ -127,7 +131,7 @@ static int fd_copy_regular(int df, const char *from, const struct stat *st, int
         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;
@@ -190,20 +194,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,7 +302,23 @@ int copy_tree(const char *from, const char *to, bool merge) {
                 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);
@@ -300,7 +328,7 @@ int copy_file_fd(const char *from, int fdt) {
         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) {
@@ -313,7 +341,7 @@ 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);