blob: 7e353190372260f32627816b2efcd9cd4442a6e2
1 | /* vi: set sw=4 ts=4: */ |
2 | /* |
3 | * Utility routines. |
4 | * |
5 | * Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org> |
6 | * |
7 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. |
8 | */ |
9 | |
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) |
25 | |
26 | /* Used by NOFORK applets (e.g. cat) - must not use xmalloc. |
27 | * size < 0 means "ignore write errors", used by tar --to-command |
28 | * size = 0 means "copy till EOF" |
29 | */ |
30 | static off_t bb_full_fd_action(int src_fd, int dst_fd, off_t size) |
31 | { |
32 | int status = -1; |
33 | off_t total = 0; |
34 | bool continue_on_write_error = 0; |
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 |
40 | char buffer[CONFIG_FEATURE_COPYBUF_KB * 1024]; |
41 | enum { buffer_size = sizeof(buffer) }; |
42 | #endif |
43 | |
44 | if (size < 0) { |
45 | size = -size; |
46 | continue_on_write_error = 1; |
47 | } |
48 | |
49 | if (src_fd < 0) |
50 | goto out; |
51 | |
52 | sendfile_sz = !ENABLE_FEATURE_USE_SENDFILE |
53 | ? 0 |
54 | : SENDFILE_BIGBUF; |
55 | if (!size) { |
56 | size = SENDFILE_BIGBUF; |
57 | status = 1; /* copy until eof */ |
58 | } |
59 | |
60 | while (1) { |
61 | ssize_t rd; |
62 | |
63 | if (sendfile_sz) { |
64 | rd = sendfile(dst_fd, src_fd, NULL, |
65 | size > sendfile_sz ? sendfile_sz : size); |
66 | if (rd >= 0) |
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 | } |
86 | } |
87 | #endif |
88 | rd = safe_read(src_fd, buffer, |
89 | size > buffer_size ? buffer_size : size); |
90 | if (rd < 0) { |
91 | bb_perror_msg(bb_msg_read_error); |
92 | break; |
93 | } |
94 | read_ok: |
95 | if (!rd) { /* eof - all done */ |
96 | status = 0; |
97 | break; |
98 | } |
99 | /* dst_fd == -1 is a fake, else... */ |
100 | if (dst_fd >= 0 && !sendfile_sz) { |
101 | ssize_t wr = full_write(dst_fd, buffer, rd); |
102 | if (wr < rd) { |
103 | if (!continue_on_write_error) { |
104 | bb_perror_msg(bb_msg_write_error); |
105 | break; |
106 | } |
107 | dst_fd = -1; |
108 | } |
109 | } |
110 | total += rd; |
111 | if (status < 0) { /* if we aren't copying till EOF... */ |
112 | size -= rd; |
113 | if (!size) { |
114 | /* 'size' bytes copied - all done */ |
115 | status = 0; |
116 | break; |
117 | } |
118 | } |
119 | } |
120 | out: |
121 | |
122 | if (buffer_size > 4 * 1024) |
123 | munmap(buffer, buffer_size); |
124 | return status ? -1 : total; |
125 | } |
126 | |
127 | |
128 | #if 0 |
129 | void FAST_FUNC complain_copyfd_and_die(off_t sz) |
130 | { |
131 | if (sz != -1) |
132 | bb_error_msg_and_die("short read"); |
133 | /* if sz == -1, bb_copyfd_XX already complained */ |
134 | xfunc_die(); |
135 | } |
136 | #endif |
137 | |
138 | off_t FAST_FUNC bb_copyfd_size(int fd1, int fd2, off_t size) |
139 | { |
140 | if (size) { |
141 | return bb_full_fd_action(fd1, fd2, size); |
142 | } |
143 | return 0; |
144 | } |
145 | |
146 | void FAST_FUNC bb_copyfd_exact_size(int fd1, int fd2, off_t size) |
147 | { |
148 | off_t sz = bb_copyfd_size(fd1, fd2, size); |
149 | if (sz == (size >= 0 ? size : -size)) |
150 | return; |
151 | if (sz != -1) |
152 | bb_error_msg_and_die("short read"); |
153 | /* if sz == -1, bb_copyfd_XX already complained */ |
154 | xfunc_die(); |
155 | } |
156 | |
157 | off_t FAST_FUNC bb_copyfd_eof(int fd1, int fd2) |
158 | { |
159 | return bb_full_fd_action(fd1, fd2, 0); |
160 | } |
161 |