aboutsummaryrefslogtreecommitdiff
path: root/libbb
diff options
context:
space:
mode:
Diffstat (limited to 'libbb')
-rw-r--r--libbb/copyfd.c87
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