X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Fshared%2Fcopy.c;h=abb7fbc52b7bdbda65bd3defeb1331770ef7ba0c;hb=4a02e68602be9f6f3caac0f6195672490f715fe1;hp=867e49bf89ee5a7d92d869ef31e270bd5a6f0f58;hpb=e156347e0480d3eee4877463297149ae96e34f8c;p=elogind.git
diff --git a/src/shared/copy.c b/src/shared/copy.c
index 867e49bf8..abb7fbc52 100644
--- a/src/shared/copy.c
+++ b/src/shared/copy.c
@@ -19,29 +19,72 @@
along with systemd; If not, see .
***/
+#include
+
#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];
+ ssize_t k;
+
+ n = read(fdf, buf, m);
+ if (n < 0)
+ return -errno;
+ 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;
+
+ }
+
+ next:
+ if (max_bytes != (off_t) -1) {
+ assert(max_bytes >= n);
+ max_bytes -= n;
+ }
}
return 0;
@@ -84,7 +127,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 +222,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 +232,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 +290,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;