diff options
Diffstat (limited to 'libbb')
-rw-r--r-- | libbb/copyfd.c | 87 |
1 files changed, 56 insertions, 31 deletions
diff --git a/libbb/copyfd.c b/libbb/copyfd.c index eda2747f9..7e3531903 100644 --- a/libbb/copyfd.c +++ b/libbb/copyfd.c | |||
@@ -8,6 +8,20 @@ | |||
8 | */ | 8 | */ |
9 | 9 | ||
10 | #include "libbb.h" | 10 | #include "libbb.h" |
11 | #if ENABLE_FEATURE_USE_SENDFILE | ||
12 | # include <sys/sendfile.h> | ||
13 | #else | ||
14 | # define sendfile(a,b,c,d) (-1) | ||
15 | #endif | ||
16 | |||
17 | /* | ||
18 | * We were using 0x7fff0000 as sendfile chunk size, but it | ||
19 | * was seen to cause largish delays when user tries to ^C a file copy. | ||
20 | * Let's use a saner size. | ||
21 | * Note: needs to be >= max(CONFIG_FEATURE_COPYBUF_KB), | ||
22 | * or else "copy to eof" code will use neddlesly short reads. | ||
23 | */ | ||
24 | #define SENDFILE_BIGBUF (16*1024*1024) | ||
11 | 25 | ||
12 | /* Used by NOFORK applets (e.g. cat) - must not use xmalloc. | 26 | /* Used by NOFORK applets (e.g. cat) - must not use xmalloc. |
13 | * size < 0 means "ignore write errors", used by tar --to-command | 27 | * size < 0 means "ignore write errors", used by tar --to-command |
@@ -18,12 +32,13 @@ static off_t bb_full_fd_action(int src_fd, int dst_fd, off_t size) | |||
18 | int status = -1; | 32 | int status = -1; |
19 | off_t total = 0; | 33 | off_t total = 0; |
20 | bool continue_on_write_error = 0; | 34 | bool continue_on_write_error = 0; |
21 | #if CONFIG_FEATURE_COPYBUF_KB <= 4 | 35 | ssize_t sendfile_sz; |
36 | #if CONFIG_FEATURE_COPYBUF_KB > 4 | ||
37 | char *buffer = buffer; /* for compiler */ | ||
38 | int buffer_size = 0; | ||
39 | #else | ||
22 | char buffer[CONFIG_FEATURE_COPYBUF_KB * 1024]; | 40 | char buffer[CONFIG_FEATURE_COPYBUF_KB * 1024]; |
23 | enum { buffer_size = sizeof(buffer) }; | 41 | enum { buffer_size = sizeof(buffer) }; |
24 | #else | ||
25 | char *buffer; | ||
26 | int buffer_size; | ||
27 | #endif | 42 | #endif |
28 | 43 | ||
29 | if (size < 0) { | 44 | if (size < 0) { |
@@ -31,46 +46,58 @@ static off_t bb_full_fd_action(int src_fd, int dst_fd, off_t size) | |||
31 | continue_on_write_error = 1; | 46 | continue_on_write_error = 1; |
32 | } | 47 | } |
33 | 48 | ||
34 | #if CONFIG_FEATURE_COPYBUF_KB > 4 | ||
35 | if (size > 0 && size <= 4 * 1024) | ||
36 | goto use_small_buf; | ||
37 | /* We want page-aligned buffer, just in case kernel is clever | ||
38 | * and can do page-aligned io more efficiently */ | ||
39 | buffer = mmap(NULL, CONFIG_FEATURE_COPYBUF_KB * 1024, | ||
40 | PROT_READ | PROT_WRITE, | ||
41 | MAP_PRIVATE | MAP_ANON, | ||
42 | /* ignored: */ -1, 0); | ||
43 | buffer_size = CONFIG_FEATURE_COPYBUF_KB * 1024; | ||
44 | if (buffer == MAP_FAILED) { | ||
45 | use_small_buf: | ||
46 | buffer = alloca(4 * 1024); | ||
47 | buffer_size = 4 * 1024; | ||
48 | } | ||
49 | #endif | ||
50 | |||
51 | if (src_fd < 0) | 49 | if (src_fd < 0) |
52 | goto out; | 50 | goto out; |
53 | 51 | ||
52 | sendfile_sz = !ENABLE_FEATURE_USE_SENDFILE | ||
53 | ? 0 | ||
54 | : SENDFILE_BIGBUF; | ||
54 | if (!size) { | 55 | if (!size) { |
55 | size = buffer_size; | 56 | size = SENDFILE_BIGBUF; |
56 | status = 1; /* copy until eof */ | 57 | status = 1; /* copy until eof */ |
57 | } | 58 | } |
58 | 59 | ||
59 | while (1) { | 60 | while (1) { |
60 | ssize_t rd; | 61 | ssize_t rd; |
61 | 62 | ||
62 | rd = safe_read(src_fd, buffer, size > buffer_size ? buffer_size : size); | 63 | if (sendfile_sz) { |
63 | 64 | rd = sendfile(dst_fd, src_fd, NULL, | |
64 | if (!rd) { /* eof - all done */ | 65 | size > sendfile_sz ? sendfile_sz : size); |
65 | status = 0; | 66 | if (rd >= 0) |
66 | break; | 67 | goto read_ok; |
68 | sendfile_sz = 0; /* do not try sendfile anymore */ | ||
69 | } | ||
70 | #if CONFIG_FEATURE_COPYBUF_KB > 4 | ||
71 | if (buffer_size == 0) { | ||
72 | if (size > 0 && size <= 4 * 1024) | ||
73 | goto use_small_buf; | ||
74 | /* We want page-aligned buffer, just in case kernel is clever | ||
75 | * and can do page-aligned io more efficiently */ | ||
76 | buffer = mmap(NULL, CONFIG_FEATURE_COPYBUF_KB * 1024, | ||
77 | PROT_READ | PROT_WRITE, | ||
78 | MAP_PRIVATE | MAP_ANON, | ||
79 | /* ignored: */ -1, 0); | ||
80 | buffer_size = CONFIG_FEATURE_COPYBUF_KB * 1024; | ||
81 | if (buffer == MAP_FAILED) { | ||
82 | use_small_buf: | ||
83 | buffer = alloca(4 * 1024); | ||
84 | buffer_size = 4 * 1024; | ||
85 | } | ||
67 | } | 86 | } |
87 | #endif | ||
88 | rd = safe_read(src_fd, buffer, | ||
89 | size > buffer_size ? buffer_size : size); | ||
68 | if (rd < 0) { | 90 | if (rd < 0) { |
69 | bb_perror_msg(bb_msg_read_error); | 91 | bb_perror_msg(bb_msg_read_error); |
70 | break; | 92 | break; |
71 | } | 93 | } |
94 | read_ok: | ||
95 | if (!rd) { /* eof - all done */ | ||
96 | status = 0; | ||
97 | break; | ||
98 | } | ||
72 | /* dst_fd == -1 is a fake, else... */ | 99 | /* dst_fd == -1 is a fake, else... */ |
73 | if (dst_fd >= 0) { | 100 | if (dst_fd >= 0 && !sendfile_sz) { |
74 | ssize_t wr = full_write(dst_fd, buffer, rd); | 101 | ssize_t wr = full_write(dst_fd, buffer, rd); |
75 | if (wr < rd) { | 102 | if (wr < rd) { |
76 | if (!continue_on_write_error) { | 103 | if (!continue_on_write_error) { |
@@ -92,10 +119,8 @@ static off_t bb_full_fd_action(int src_fd, int dst_fd, off_t size) | |||
92 | } | 119 | } |
93 | out: | 120 | out: |
94 | 121 | ||
95 | #if CONFIG_FEATURE_COPYBUF_KB > 4 | 122 | if (buffer_size > 4 * 1024) |
96 | if (buffer_size != 4 * 1024) | ||
97 | munmap(buffer, buffer_size); | 123 | munmap(buffer, buffer_size); |
98 | #endif | ||
99 | return status ? -1 : total; | 124 | return status ? -1 : total; |
100 | } | 125 | } |
101 | 126 | ||