chiark / gitweb /
seccomp-util.h: make sure seccomp-util.h can be included alone
[elogind.git] / src / shared / copy.c
index 867e49bf89ee5a7d92d869ef31e270bd5a6f0f58..b8b1ba18664da6d615a5e98e831dbd505211adb4 100644 (file)
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
+#include <sys/sendfile.h>
+
 #include "util.h"
 #include "copy.h"
 
-int copy_bytes(int fdf, int fdt) {
+int copy_bytes(int fdf, int fdt, off_t max_bytes) {
+        bool try_sendfile = true;
+
         assert(fdf >= 0);
         assert(fdt >= 0);
 
         for (;;) {
-                char buf[PIPE_BUF];
-                ssize_t n, k;
-
-                n = read(fdf, buf, sizeof(buf));
-                if (n < 0)
-                        return -errno;
-                if (n == 0)
-                        break;
-
-                errno = 0;
-                k = loop_write(fdt, buf, n, false);
-                if (k < 0)
-                        return k;
-                if (k != n)
-                        return errno ? -errno : -EIO;
+                size_t m = PIPE_BUF;
+                ssize_t n;
+
+                if (max_bytes != (off_t) -1) {
+
+                        if (max_bytes <= 0)
+                                return -EFBIG;
+
+                        if ((off_t) m > max_bytes)
+                                m = (size_t) max_bytes;
+                }
+
+                /* First try sendfile(), unless we already tried */
+                if (try_sendfile) {
+
+                        n = sendfile(fdt, fdf, NULL, m);
+                        if (n < 0) {
+                                if (errno != EINVAL && errno != ENOSYS)
+                                        return -errno;
+
+                                try_sendfile = false;
+                                /* use fallback below */
+                        } else if (n == 0) /* EOF */
+                                break;
+                        else if (n > 0)
+                                /* Succcess! */
+                                goto next;
+                }
+
+                /* As a fallback just copy bits by hand */
+                {
+                        char buf[m];
+                        int r;
+
+                        n = read(fdf, buf, m);
+                        if (n < 0)
+                                return -errno;
+                        if (n == 0) /* EOF */
+                                break;
+
+                        r = loop_write(fdt, buf, n, false);
+                        if (r < 0)
+                                return r;
+
+                }
+
+        next:
+                if (max_bytes != (off_t) -1) {
+                        assert(max_bytes >= n);
+                        max_bytes -= n;
+                }
         }
 
         return 0;
@@ -84,7 +124,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);
+        r = copy_bytes(fdf, fdt, (off_t) -1);
         if (r < 0) {
                 unlinkat(dt, to, 0);
                 return r;
@@ -179,6 +219,8 @@ static int fd_copy_directory(int df, const char *from, const struct stat *st, in
         if (fdt < 0)
                 return -errno;
 
+        r = 0;
+
         if (created) {
                 if (fchown(fdt, st->st_uid, st->st_gid) < 0)
                         r = -errno;
@@ -187,7 +229,6 @@ static int fd_copy_directory(int df, const char *from, const struct stat *st, in
                         r = -errno;
         }
 
-        r = 0;
         FOREACH_DIRENT(de, d, return -errno) {
                 struct stat buf;
                 int q;
@@ -246,34 +287,39 @@ int copy_tree(const char *from, const char *to, bool merge) {
                 return -ENOTSUP;
 }
 
-int copy_file(const char *from, const char *to, int flags, mode_t mode) {
-        _cleanup_close_ int fdf = -1, fdt = -1;
-        int r;
+int copy_file_fd(const char *from, int fdt) {
+        _cleanup_close_ int fdf = -1;
 
         assert(from);
-        assert(to);
+        assert(fdt >= 0);
 
         fdf = open(from, O_RDONLY|O_CLOEXEC|O_NOCTTY);
         if (fdf < 0)
                 return -errno;
 
+        return copy_bytes(fdf, fdt, (off_t) -1);
+}
+
+int copy_file(const char *from, const char *to, int flags, mode_t mode) {
+        int fdt, r;
+
+        assert(from);
+        assert(to);
+
         fdt = open(to, flags|O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, mode);
         if (fdt < 0)
                 return -errno;
 
-        r = copy_bytes(fdf, fdt);
+        r = copy_file_fd(from, fdt);
         if (r < 0) {
+                close(fdt);
                 unlink(to);
                 return r;
         }
 
-        r = close(fdt);
-        fdt = -1;
-
-        if (r < 0) {
-                r = -errno;
-                unlink(to);
-                return r;
+        if (close(fdt) < 0) {
+                unlink_noerrno(to);
+                return -errno;
         }
 
         return 0;