mirror of
https://github.com/systemd/systemd-stable.git
synced 2025-01-25 06:03:40 +03:00
copy: teach copy_bytes() sendfile() support, and then replace sendfile_full() by it
This commit is contained in:
parent
0c2576ef74
commit
cda134ab1e
@ -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) {
|
||||
|
||||
errno = 0;
|
||||
k = loop_write(fdt, buf, n, false);
|
||||
if (k < 0)
|
||||
return k;
|
||||
if (k != n)
|
||||
return errno ? -errno : -EIO;
|
||||
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;
|
||||
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user