| @@ -8,6 +8,20 @@ |
| */ |
| |
| #include "libbb.h" |
| + | #if ENABLE_FEATURE_USE_SENDFILE |
| + | # include <sys/sendfile.h> |
| + | #else |
| + | # define sendfile(a,b,c,d) (-1) |
| + | #endif |
| + | |
| + | /* |
| + | * We were using 0x7fff0000 as sendfile chunk size, but it |
| + | * was seen to cause largish delays when user tries to ^C a file copy. |
| + | * Let's use a saner size. |
| + | * Note: needs to be >= max(CONFIG_FEATURE_COPYBUF_KB), |
| + | * or else "copy to eof" code will use neddlesly short reads. |
| + | */ |
| + | #define SENDFILE_BIGBUF (16*1024*1024) |
| |
| /* Used by NOFORK applets (e.g. cat) - must not use xmalloc. |
| * 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) |
| int status = -1; |
| off_t total = 0; |
| bool continue_on_write_error = 0; |
| - | #if CONFIG_FEATURE_COPYBUF_KB <= 4 |
| + | ssize_t sendfile_sz; |
| + | #if CONFIG_FEATURE_COPYBUF_KB > 4 |
| + | char *buffer = buffer; /* for compiler */ |
| + | int buffer_size = 0; |
| + | #else |
| char buffer[CONFIG_FEATURE_COPYBUF_KB * 1024]; |
| enum { buffer_size = sizeof(buffer) }; |
| - | #else |
| - | char *buffer; |
| - | int buffer_size; |
| #endif |
| |
| if (size < 0) { |
| @@ -31,46 +46,58 @@ static off_t bb_full_fd_action(int src_fd, int dst_fd, off_t size) |
| continue_on_write_error = 1; |
| } |
| |
| - | #if CONFIG_FEATURE_COPYBUF_KB > 4 |
| - | if (size > 0 && size <= 4 * 1024) |
| - | goto use_small_buf; |
| - | /* We want page-aligned buffer, just in case kernel is clever |
| - | * and can do page-aligned io more efficiently */ |
| - | buffer = mmap(NULL, CONFIG_FEATURE_COPYBUF_KB * 1024, |
| - | PROT_READ | PROT_WRITE, |
| - | MAP_PRIVATE | MAP_ANON, |
| - | /* ignored: */ -1, 0); |
| - | buffer_size = CONFIG_FEATURE_COPYBUF_KB * 1024; |
| - | if (buffer == MAP_FAILED) { |
| - | use_small_buf: |
| - | buffer = alloca(4 * 1024); |
| - | buffer_size = 4 * 1024; |
| - | } |
| - | #endif |
| - | |
| if (src_fd < 0) |
| goto out; |
| |
| + | sendfile_sz = !ENABLE_FEATURE_USE_SENDFILE |
| + | ? 0 |
| + | : SENDFILE_BIGBUF; |
| if (!size) { |
| - | size = buffer_size; |
| + | size = SENDFILE_BIGBUF; |
| status = 1; /* copy until eof */ |
| } |
| |
| while (1) { |
| ssize_t rd; |
| |
| - | rd = safe_read(src_fd, buffer, size > buffer_size ? buffer_size : size); |
| - | |
| - | if (!rd) { /* eof - all done */ |
| - | status = 0; |
| - | break; |
| + | if (sendfile_sz) { |
| + | rd = sendfile(dst_fd, src_fd, NULL, |
| + | size > sendfile_sz ? sendfile_sz : size); |
| + | if (rd >= 0) |
| + | goto read_ok; |
| + | sendfile_sz = 0; /* do not try sendfile anymore */ |
| + | } |
| + | #if CONFIG_FEATURE_COPYBUF_KB > 4 |
| + | if (buffer_size == 0) { |
| + | if (size > 0 && size <= 4 * 1024) |
| + | goto use_small_buf; |
| + | /* We want page-aligned buffer, just in case kernel is clever |
| + | * and can do page-aligned io more efficiently */ |
| + | buffer = mmap(NULL, CONFIG_FEATURE_COPYBUF_KB * 1024, |
| + | PROT_READ | PROT_WRITE, |
| + | MAP_PRIVATE | MAP_ANON, |
| + | /* ignored: */ -1, 0); |
| + | buffer_size = CONFIG_FEATURE_COPYBUF_KB * 1024; |
| + | if (buffer == MAP_FAILED) { |
| + | use_small_buf: |
| + | buffer = alloca(4 * 1024); |
| + | buffer_size = 4 * 1024; |
| + | } |
| } |
| + | #endif |
| + | rd = safe_read(src_fd, buffer, |
| + | size > buffer_size ? buffer_size : size); |
| if (rd < 0) { |
| bb_perror_msg(bb_msg_read_error); |
| break; |
| } |
| + | read_ok: |
| + | if (!rd) { /* eof - all done */ |
| + | status = 0; |
| + | break; |
| + | } |
| /* dst_fd == -1 is a fake, else... */ |
| - | if (dst_fd >= 0) { |
| + | if (dst_fd >= 0 && !sendfile_sz) { |
| ssize_t wr = full_write(dst_fd, buffer, rd); |
| if (wr < rd) { |
| 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) |
| } |
| out: |
| |
| - | #if CONFIG_FEATURE_COPYBUF_KB > 4 |
| - | if (buffer_size != 4 * 1024) |
| + | if (buffer_size > 4 * 1024) |
| munmap(buffer, buffer_size); |
| - | #endif |
| return status ? -1 : total; |
| } |
| |