summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/shared/copy.c85
-rw-r--r--src/shared/copy.h1
-rw-r--r--src/shared/fileio.c75
-rw-r--r--src/shared/fileio.h1
-rw-r--r--src/systemctl/systemctl.c5
-rw-r--r--src/test/test-copy.c26
-rw-r--r--src/test/test-fileio.c25
7 files changed, 91 insertions, 127 deletions
diff --git a/src/shared/copy.c b/src/shared/copy.c
index 3744797b95..a863246b2b 100644
--- a/src/shared/copy.c
+++ b/src/shared/copy.c
@@ -19,17 +19,20 @@
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, off_t max_bytes) {
+ bool try_sendfile = true;
+
assert(fdf >= 0);
assert(fdt >= 0);
for (;;) {
- char buf[PIPE_BUF];
- ssize_t n, k;
- size_t m = sizeof(buf);
+ size_t m = PIPE_BUF;
+ ssize_t n;
if (max_bytes != (off_t) -1) {
@@ -40,19 +43,44 @@ int copy_bytes(int fdf, int fdt, off_t max_bytes) {
m = (size_t) max_bytes;
}
- n = read(fdf, buf, m);
- if (n < 0)
- return -errno;
- if (n == 0)
- break;
+ /* 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;
- errno = 0;
- k = loop_write(fdt, buf, n, false);
- if (k < 0)
- return k;
- if (k != n)
- return errno ? -errno : -EIO;
+ 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;
@@ -262,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, (off_t) -1);
+ 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;
diff --git a/src/shared/copy.h b/src/shared/copy.h
index 6b93107fab..62932112af 100644
--- a/src/shared/copy.h
+++ b/src/shared/copy.h
@@ -24,6 +24,7 @@
#include <stdbool.h>
#include <sys/types.h>
+int copy_file_fd(const char *from, int to);
int copy_file(const char *from, const char *to, int flags, mode_t mode);
int copy_tree(const char *from, const char *to, bool merge);
int copy_bytes(int fdf, int fdt, off_t max_bytes);
diff --git a/src/shared/fileio.c b/src/shared/fileio.c
index 38028b972e..f4efc4c9f1 100644
--- a/src/shared/fileio.c
+++ b/src/shared/fileio.c
@@ -20,12 +20,12 @@
***/
#include <unistd.h>
-#include <sys/sendfile.h>
-#include "fileio.h"
+
#include "util.h"
#include "strv.h"
#include "utf8.h"
#include "ctype.h"
+#include "fileio.h"
int write_string_stream(FILE *f, const char *line) {
assert(f);
@@ -144,77 +144,6 @@ int read_one_line_file(const char *fn, char **line) {
return 0;
}
-ssize_t sendfile_full(int out_fd, const char *fn) {
- _cleanup_fclose_ FILE *f;
- struct stat st;
- int r;
- ssize_t s;
-
- size_t n, l;
- _cleanup_free_ char *buf = NULL;
-
- assert(out_fd > 0);
- assert(fn);
-
- f = fopen(fn, "re");
- if (!f)
- return -errno;
-
- r = fstat(fileno(f), &st);
- if (r < 0)
- return -errno;
-
- s = sendfile(out_fd, fileno(f), NULL, st.st_size);
- if (s < 0)
- if (errno == EINVAL || errno == ENOSYS) {
- /* continue below */
- } else
- return -errno;
- else
- return s;
-
- /* sendfile() failed, fall back to read/write */
-
- /* Safety check */
- if (st.st_size > 4*1024*1024)
- return -E2BIG;
-
- n = st.st_size > 0 ? st.st_size : LINE_MAX;
- l = 0;
-
- while (true) {
- char *t;
- size_t k;
-
- t = realloc(buf, n);
- if (!t)
- return -ENOMEM;
-
- buf = t;
- k = fread(buf + l, 1, n - l, f);
-
- if (k <= 0) {
- if (ferror(f))
- return -errno;
-
- break;
- }
-
- l += k;
- n *= 2;
-
- /* Safety check */
- if (n > 4*1024*1024)
- return -E2BIG;
- }
-
- r = write(out_fd, buf, l);
- if (r < 0)
- return -errno;
-
- return (ssize_t) l;
-}
-
int read_full_stream(FILE *f, char **contents, size_t *size) {
size_t n, l;
_cleanup_free_ char *buf = NULL;
diff --git a/src/shared/fileio.h b/src/shared/fileio.h
index c256915799..5ae51c1e28 100644
--- a/src/shared/fileio.h
+++ b/src/shared/fileio.h
@@ -33,7 +33,6 @@ int write_string_file_atomic(const char *fn, const char *line);
int read_one_line_file(const char *fn, char **line);
int read_full_file(const char *fn, char **contents, size_t *size);
int read_full_stream(FILE *f, char **contents, size_t *size);
-ssize_t sendfile_full(int out_fd, const char *fn);
int parse_env_file(const char *fname, const char *separator, ...) _sentinel_;
int load_env_file(FILE *f, const char *fname, const char *separator, char ***l);
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index d9e9c2a6c3..c903c54e9b 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -67,6 +67,7 @@
#include "logs-show.h"
#include "socket-util.h"
#include "fileio.h"
+#include "copy.h"
#include "env-util.h"
#include "bus-util.h"
#include "bus-message.h"
@@ -4647,7 +4648,7 @@ static int cat(sd_bus *bus, char **args) {
ansi_highlight_off());
fflush(stdout);
- r = sendfile_full(STDOUT_FILENO, fragment_path);
+ r = copy_file_fd(fragment_path, STDOUT_FILENO);
if (r < 0) {
log_warning("Failed to cat %s: %s", fragment_path, strerror(-r));
continue;
@@ -4662,7 +4663,7 @@ static int cat(sd_bus *bus, char **args) {
ansi_highlight_off());
fflush(stdout);
- r = sendfile_full(STDOUT_FILENO, *path);
+ r = copy_file_fd(*path, STDOUT_FILENO);
if (r < 0) {
log_warning("Failed to cat %s: %s", *path, strerror(-r));
continue;
diff --git a/src/test/test-copy.c b/src/test/test-copy.c
index 6aa86a03be..d2cad08cb6 100644
--- a/src/test/test-copy.c
+++ b/src/test/test-copy.c
@@ -48,11 +48,36 @@ static void test_copy_file(void) {
assert_se(read_full_file(fn_copy, &buf, &sz) == 0);
assert_se(streq(buf, "foo bar bar bar foo\n"));
+ assert_se(sz == 20);
unlink(fn);
unlink(fn_copy);
}
+static void test_copy_file_fd(void) {
+ char in_fn[] = "/tmp/test-copy-file-fd-XXXXXX";
+ char out_fn[] = "/tmp/test-copy-file-fd-XXXXXX";
+ _cleanup_close_ int in_fd = -1, out_fd = -1;
+ char text[] = "boohoo\nfoo\n\tbar\n";
+ char buf[64] = {0};
+
+ in_fd = mkostemp_safe(in_fn, O_RDWR);
+ assert_se(in_fd >= 0);
+ out_fd = mkostemp_safe(out_fn, O_RDWR);
+ assert_se(out_fd >= 0);
+
+ assert_se(write_string_file(in_fn, text) == 0);
+ assert_se(copy_file_fd("/a/file/which/does/not/exist/i/guess", out_fd) < 0);
+ assert_se(copy_file_fd(in_fn, out_fd) >= 0);
+ assert_se(lseek(out_fd, SEEK_SET, 0) == 0);
+
+ assert_se(read(out_fd, buf, sizeof(buf)) == sizeof(text) - 1);
+ assert_se(streq(buf, text));
+
+ unlink(in_fn);
+ unlink(out_fn);
+}
+
static void test_copy_tree(void) {
char original_dir[] = "/tmp/test-copy_tree/";
char copy_dir[] = "/tmp/test-copy_tree-copy/";
@@ -109,6 +134,7 @@ static void test_copy_tree(void) {
int main(int argc, char *argv[]) {
test_copy_file();
+ test_copy_file_fd();
test_copy_tree();
return 0;
diff --git a/src/test/test-fileio.c b/src/test/test-fileio.c
index c26a6facb4..cdf1973ea5 100644
--- a/src/test/test-fileio.c
+++ b/src/test/test-fileio.c
@@ -348,30 +348,6 @@ static void test_write_string_file_no_create(void) {
unlink(fn);
}
-static void test_sendfile_full(void) {
- char in_fn[] = "/tmp/test-sendfile_full-XXXXXX";
- char out_fn[] = "/tmp/test-sendfile_full-XXXXXX";
- _cleanup_close_ int in_fd, out_fd;
- char text[] = "boohoo\nfoo\n\tbar\n";
- char buf[64] = {0};
-
- in_fd = mkostemp_safe(in_fn, O_RDWR);
- assert_se(in_fd >= 0);
- out_fd = mkostemp_safe(out_fn, O_RDWR);
- assert_se(out_fd >= 0);
-
- assert_se(write_string_file(in_fn, text) == 0);
- assert_se(sendfile_full(out_fd, "/a/file/which/does/not/exist/i/guess") < 0);
- assert_se(sendfile_full(out_fd, in_fn) == sizeof(text) - 1);
- assert_se(lseek(out_fd, SEEK_SET, 0) == 0);
-
- assert_se(read(out_fd, buf, sizeof(buf)) == sizeof(text) - 1);
- assert_se(streq(buf, text));
-
- unlink(in_fn);
- unlink(out_fn);
-}
-
static void test_load_env_file_pairs(void) {
char fn[] = "/tmp/test-load_env_file_pairs-XXXXXX";
int fd;
@@ -428,7 +404,6 @@ int main(int argc, char *argv[]) {
test_write_string_stream();
test_write_string_file();
test_write_string_file_no_create();
- test_sendfile_full();
test_load_env_file_pairs();
return 0;