1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
1 |
/*
|
2 |
* Part of Very Secure FTPd
|
|
1.2.1
by Daniel Jacobowitz
Import upstream version 2.0.3 |
3 |
* Licence: GPL v2
|
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
4 |
* Author: Chris Evans
|
5 |
* ftpdataio.c
|
|
6 |
*
|
|
7 |
* Code to handle FTP data connections. This includes both PORT (server
|
|
8 |
* connects) and PASV (client connects) modes of data transfer. This
|
|
9 |
* includes sends and receives, files and directories.
|
|
10 |
*/
|
|
11 |
||
12 |
#include "ftpdataio.h" |
|
13 |
#include "session.h" |
|
14 |
#include "ftpcmdio.h" |
|
15 |
#include "ftpcodes.h" |
|
16 |
#include "utility.h" |
|
17 |
#include "tunables.h" |
|
18 |
#include "defs.h" |
|
19 |
#include "str.h" |
|
20 |
#include "strlist.h" |
|
21 |
#include "sysutil.h" |
|
22 |
#include "logging.h" |
|
23 |
#include "secbuf.h" |
|
24 |
#include "sysstr.h" |
|
25 |
#include "sysdeputil.h" |
|
26 |
#include "ascii.h" |
|
27 |
#include "oneprocess.h" |
|
28 |
#include "twoprocess.h" |
|
29 |
#include "ls.h" |
|
1.2.1
by Daniel Jacobowitz
Import upstream version 2.0.3 |
30 |
#include "ssl.h" |
31 |
#include "readwrite.h" |
|
1.2.6
by Daniel Baumann
Import upstream version 2.1.1~pre1 |
32 |
#include "privsock.h" |
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
33 |
|
34 |
static void init_data_sock_params(struct vsf_session* p_sess, int sock_fd); |
|
1.2.1
by Daniel Jacobowitz
Import upstream version 2.0.3 |
35 |
static filesize_t calc_num_send(int file_fd, filesize_t init_offset); |
36 |
static struct vsf_transfer_ret do_file_send_sendfile( |
|
37 |
struct vsf_session* p_sess, int net_fd, int file_fd, |
|
38 |
filesize_t curr_file_offset, filesize_t bytes_to_send); |
|
39 |
static struct vsf_transfer_ret do_file_send_rwloop( |
|
40 |
struct vsf_session* p_sess, int file_fd, int is_ascii); |
|
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
41 |
static struct vsf_transfer_ret do_file_recv( |
1.2.1
by Daniel Jacobowitz
Import upstream version 2.0.3 |
42 |
struct vsf_session* p_sess, int file_fd, int is_ascii); |
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
43 |
static void handle_sigalrm(void* p_private); |
44 |
static void start_data_alarm(struct vsf_session* p_sess); |
|
45 |
static void handle_io(int retval, int fd, void* p_private); |
|
46 |
static int transfer_dir_internal( |
|
1.2.1
by Daniel Jacobowitz
Import upstream version 2.0.3 |
47 |
struct vsf_session* p_sess, int is_control, struct vsf_sysutil_dir* p_dir, |
48 |
const struct mystr* p_base_dir_str, const struct mystr* p_option_str, |
|
49 |
const struct mystr* p_filter_str, int is_verbose); |
|
50 |
static int write_dir_list(struct vsf_session* p_sess, |
|
51 |
struct mystr_list* p_dir_list, |
|
52 |
enum EVSFRWTarget target); |
|
53 |
static unsigned int get_chunk_size(); |
|
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
54 |
|
1.2.5
by Adrien Cunin
Import upstream version 2.0.7 |
55 |
int
|
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
56 |
vsf_ftpdataio_dispose_transfer_fd(struct vsf_session* p_sess) |
57 |
{
|
|
1.2.5
by Adrien Cunin
Import upstream version 2.0.7 |
58 |
int dispose_ret = 1; |
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
59 |
int retval; |
60 |
if (p_sess->data_fd == -1) |
|
61 |
{
|
|
62 |
bug("no data descriptor in vsf_ftpdataio_dispose_transfer_fd"); |
|
63 |
}
|
|
1.2.1
by Daniel Jacobowitz
Import upstream version 2.0.3 |
64 |
/* Reset the data connection alarm so it runs anew with the blocking close */
|
65 |
start_data_alarm(p_sess); |
|
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
66 |
vsf_sysutil_uninstall_io_handler(); |
1.2.6
by Daniel Baumann
Import upstream version 2.1.1~pre1 |
67 |
if (p_sess->data_use_ssl && p_sess->ssl_slave_active) |
68 |
{
|
|
69 |
char result; |
|
70 |
priv_sock_send_cmd(p_sess->ssl_consumer_fd, PRIV_SOCK_DO_SSL_CLOSE); |
|
71 |
result = priv_sock_get_result(p_sess->ssl_consumer_fd); |
|
72 |
if (result != PRIV_SOCK_RESULT_OK) |
|
73 |
{
|
|
74 |
dispose_ret = 0; |
|
75 |
}
|
|
76 |
}
|
|
77 |
else if (p_sess->p_data_ssl) |
|
1.2.1
by Daniel Jacobowitz
Import upstream version 2.0.3 |
78 |
{
|
1.2.5
by Adrien Cunin
Import upstream version 2.0.7 |
79 |
dispose_ret = ssl_data_close(p_sess); |
1.2.1
by Daniel Jacobowitz
Import upstream version 2.0.3 |
80 |
}
|
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
81 |
/* This close() blocks because we set SO_LINGER */
|
82 |
retval = vsf_sysutil_close_failok(p_sess->data_fd); |
|
83 |
if (vsf_sysutil_retval_is_error(retval)) |
|
84 |
{
|
|
85 |
/* Do it again without blocking. */
|
|
1.2.1
by Daniel Jacobowitz
Import upstream version 2.0.3 |
86 |
vsf_sysutil_deactivate_linger_failok(p_sess->data_fd); |
87 |
(void) vsf_sysutil_close_failok(p_sess->data_fd); |
|
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
88 |
}
|
1.2.6
by Daniel Baumann
Import upstream version 2.1.1~pre1 |
89 |
if (tunable_data_connection_timeout > 0) |
90 |
{
|
|
91 |
vsf_sysutil_clear_alarm(); |
|
92 |
}
|
|
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
93 |
p_sess->data_fd = -1; |
1.2.5
by Adrien Cunin
Import upstream version 2.0.7 |
94 |
return dispose_ret; |
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
95 |
}
|
96 |
||
97 |
int
|
|
98 |
vsf_ftpdataio_get_pasv_fd(struct vsf_session* p_sess) |
|
99 |
{
|
|
1.2.1
by Daniel Jacobowitz
Import upstream version 2.0.3 |
100 |
int remote_fd; |
1.5.1
by Daniel Baumann
Import upstream version 2.2.0~pre1 |
101 |
if (tunable_one_process_model) |
102 |
{
|
|
103 |
remote_fd = vsf_one_process_get_pasv_fd(p_sess); |
|
104 |
}
|
|
105 |
else
|
|
106 |
{
|
|
107 |
remote_fd = vsf_two_process_get_pasv_fd(p_sess); |
|
108 |
}
|
|
109 |
/* Yes, yes, hardcoded bad I know. */
|
|
110 |
if (remote_fd == -1) |
|
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
111 |
{
|
112 |
vsf_cmdio_write(p_sess, FTP_BADSENDCONN, |
|
113 |
"Failed to establish connection."); |
|
114 |
return remote_fd; |
|
115 |
}
|
|
1.5.1
by Daniel Baumann
Import upstream version 2.2.0~pre1 |
116 |
else if (remote_fd == -2) |
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
117 |
{
|
1.5.1
by Daniel Baumann
Import upstream version 2.2.0~pre1 |
118 |
vsf_cmdio_write(p_sess, FTP_BADSENDCONN, "Security: Bad IP connecting."); |
119 |
vsf_sysutil_close(remote_fd); |
|
120 |
return -1; |
|
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
121 |
}
|
122 |
init_data_sock_params(p_sess, remote_fd); |
|
123 |
return remote_fd; |
|
124 |
}
|
|
125 |
||
126 |
int
|
|
127 |
vsf_ftpdataio_get_port_fd(struct vsf_session* p_sess) |
|
128 |
{
|
|
129 |
int remote_fd; |
|
1.5.1
by Daniel Baumann
Import upstream version 2.2.0~pre1 |
130 |
if (tunable_one_process_model || tunable_port_promiscuous) |
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
131 |
{
|
1.5.1
by Daniel Baumann
Import upstream version 2.2.0~pre1 |
132 |
remote_fd = vsf_one_process_get_priv_data_sock(p_sess); |
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
133 |
}
|
134 |
else
|
|
135 |
{
|
|
1.5.1
by Daniel Baumann
Import upstream version 2.2.0~pre1 |
136 |
remote_fd = vsf_two_process_get_priv_data_sock(p_sess); |
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
137 |
}
|
1.5.1
by Daniel Baumann
Import upstream version 2.2.0~pre1 |
138 |
if (vsf_sysutil_retval_is_error(remote_fd)) |
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
139 |
{
|
140 |
vsf_cmdio_write(p_sess, FTP_BADSENDCONN, |
|
141 |
"Failed to establish connection."); |
|
142 |
return -1; |
|
143 |
}
|
|
144 |
init_data_sock_params(p_sess, remote_fd); |
|
145 |
return remote_fd; |
|
146 |
}
|
|
147 |
||
1.2.1
by Daniel Jacobowitz
Import upstream version 2.0.3 |
148 |
int
|
149 |
vsf_ftpdataio_post_mark_connect(struct vsf_session* p_sess) |
|
150 |
{
|
|
1.2.6
by Daniel Baumann
Import upstream version 2.1.1~pre1 |
151 |
int ret = 0; |
152 |
if (!p_sess->data_use_ssl) |
|
153 |
{
|
|
154 |
return 1; |
|
155 |
}
|
|
156 |
if (!p_sess->ssl_slave_active) |
|
157 |
{
|
|
158 |
ret = ssl_accept(p_sess, p_sess->data_fd); |
|
159 |
}
|
|
160 |
else
|
|
161 |
{
|
|
162 |
int sock_ret; |
|
163 |
priv_sock_send_cmd(p_sess->ssl_consumer_fd, PRIV_SOCK_DO_SSL_HANDSHAKE); |
|
164 |
priv_sock_send_fd(p_sess->ssl_consumer_fd, p_sess->data_fd); |
|
165 |
sock_ret = priv_sock_get_result(p_sess->ssl_consumer_fd); |
|
166 |
if (sock_ret == PRIV_SOCK_RESULT_OK) |
|
167 |
{
|
|
168 |
ret = 1; |
|
169 |
}
|
|
170 |
}
|
|
171 |
if (ret != 1) |
|
172 |
{
|
|
173 |
static struct mystr s_err_msg; |
|
174 |
str_alloc_text(&s_err_msg, "SSL connection failed"); |
|
175 |
if (tunable_require_ssl_reuse) |
|
176 |
{
|
|
177 |
str_append_text(&s_err_msg, "; session reuse required"); |
|
1.2.7
by Daniel Baumann
Import upstream version 2.1.2 |
178 |
str_append_text( |
179 |
&s_err_msg, ": see require_ssl_reuse option in vsftpd.conf man page"); |
|
1.2.6
by Daniel Baumann
Import upstream version 2.1.1~pre1 |
180 |
}
|
181 |
vsf_cmdio_write_str(p_sess, FTP_DATATLSBAD, &s_err_msg); |
|
182 |
}
|
|
183 |
return ret; |
|
1.2.1
by Daniel Jacobowitz
Import upstream version 2.0.3 |
184 |
}
|
185 |
||
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
186 |
static void |
187 |
handle_sigalrm(void* p_private) |
|
188 |
{
|
|
189 |
struct vsf_session* p_sess = (struct vsf_session*) p_private; |
|
190 |
if (!p_sess->data_progress) |
|
191 |
{
|
|
1.2.1
by Daniel Jacobowitz
Import upstream version 2.0.3 |
192 |
vsf_cmdio_write_exit(p_sess, FTP_DATA_TIMEOUT, |
193 |
"Data timeout. Reconnect. Sorry."); |
|
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
194 |
}
|
195 |
p_sess->data_progress = 0; |
|
196 |
start_data_alarm(p_sess); |
|
197 |
}
|
|
198 |
||
199 |
void
|
|
200 |
start_data_alarm(struct vsf_session* p_sess) |
|
201 |
{
|
|
202 |
if (tunable_data_connection_timeout > 0) |
|
203 |
{
|
|
1.2.6
by Daniel Baumann
Import upstream version 2.1.1~pre1 |
204 |
vsf_sysutil_install_sighandler(kVSFSysUtilSigALRM, |
205 |
handle_sigalrm, |
|
206 |
p_sess, |
|
207 |
1); |
|
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
208 |
vsf_sysutil_set_alarm(tunable_data_connection_timeout); |
209 |
}
|
|
1.2.6
by Daniel Baumann
Import upstream version 2.1.1~pre1 |
210 |
else if (tunable_idle_session_timeout > 0) |
211 |
{
|
|
212 |
vsf_sysutil_clear_alarm(); |
|
213 |
}
|
|
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
214 |
}
|
215 |
||
216 |
static void |
|
217 |
init_data_sock_params(struct vsf_session* p_sess, int sock_fd) |
|
218 |
{
|
|
219 |
if (p_sess->data_fd != -1) |
|
220 |
{
|
|
221 |
bug("data descriptor still present in init_data_sock_params"); |
|
222 |
}
|
|
223 |
p_sess->data_fd = sock_fd; |
|
224 |
p_sess->data_progress = 0; |
|
225 |
vsf_sysutil_activate_keepalive(sock_fd); |
|
226 |
/* And in the vague hope it might help... */
|
|
227 |
vsf_sysutil_set_iptos_throughput(sock_fd); |
|
228 |
/* Set up lingering, so that we wait for all data to transfer, and report
|
|
229 |
* more accurate transfer rates.
|
|
230 |
*/
|
|
231 |
vsf_sysutil_activate_linger(sock_fd); |
|
232 |
/* Start the timeout monitor */
|
|
233 |
vsf_sysutil_install_io_handler(handle_io, p_sess); |
|
234 |
start_data_alarm(p_sess); |
|
235 |
}
|
|
236 |
||
237 |
static void |
|
238 |
handle_io(int retval, int fd, void* p_private) |
|
239 |
{
|
|
240 |
long curr_sec; |
|
241 |
long curr_usec; |
|
242 |
unsigned int bw_rate; |
|
243 |
double elapsed; |
|
244 |
double pause_time; |
|
245 |
double rate_ratio; |
|
246 |
struct vsf_session* p_sess = (struct vsf_session*) p_private; |
|
247 |
if (p_sess->data_fd != fd || vsf_sysutil_retval_is_error(retval) || |
|
248 |
retval == 0) |
|
249 |
{
|
|
250 |
return; |
|
251 |
}
|
|
252 |
/* Note that the session hasn't stalled, i.e. don't time it out */
|
|
253 |
p_sess->data_progress = 1; |
|
254 |
/* Apply bandwidth quotas via a little pause, if necessary */
|
|
255 |
if (p_sess->bw_rate_max == 0) |
|
256 |
{
|
|
257 |
return; |
|
258 |
}
|
|
259 |
/* Calculate bandwidth rate */
|
|
1.5.5
by Daniel Baumann
Import upstream version 2.2.1 |
260 |
curr_sec = vsf_sysutil_get_time_sec(); |
261 |
curr_usec = vsf_sysutil_get_time_usec(); |
|
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
262 |
elapsed = (double) (curr_sec - p_sess->bw_send_start_sec); |
263 |
elapsed += (double) (curr_usec - p_sess->bw_send_start_usec) / |
|
264 |
(double) 1000000; |
|
1.2.1
by Daniel Jacobowitz
Import upstream version 2.0.3 |
265 |
if (elapsed <= (double) 0) |
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
266 |
{
|
267 |
elapsed = (double) 0.01; |
|
268 |
}
|
|
269 |
bw_rate = (unsigned int) ((double) retval / elapsed); |
|
270 |
if (bw_rate <= p_sess->bw_rate_max) |
|
271 |
{
|
|
272 |
p_sess->bw_send_start_sec = curr_sec; |
|
273 |
p_sess->bw_send_start_usec = curr_usec; |
|
274 |
return; |
|
275 |
}
|
|
276 |
/* Tut! Rate exceeded, calculate a pause to bring things back into line */
|
|
277 |
rate_ratio = (double) bw_rate / (double) p_sess->bw_rate_max; |
|
278 |
pause_time = (rate_ratio - (double) 1) * elapsed; |
|
279 |
vsf_sysutil_sleep(pause_time); |
|
1.5.5
by Daniel Baumann
Import upstream version 2.2.1 |
280 |
p_sess->bw_send_start_sec = vsf_sysutil_get_time_sec(); |
281 |
p_sess->bw_send_start_usec = vsf_sysutil_get_time_usec(); |
|
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
282 |
}
|
283 |
||
284 |
int
|
|
1.2.1
by Daniel Jacobowitz
Import upstream version 2.0.3 |
285 |
vsf_ftpdataio_transfer_dir(struct vsf_session* p_sess, int is_control, |
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
286 |
struct vsf_sysutil_dir* p_dir, |
287 |
const struct mystr* p_base_dir_str, |
|
288 |
const struct mystr* p_option_str, |
|
289 |
const struct mystr* p_filter_str, |
|
290 |
int is_verbose) |
|
291 |
{
|
|
1.2.1
by Daniel Jacobowitz
Import upstream version 2.0.3 |
292 |
return transfer_dir_internal(p_sess, is_control, p_dir, p_base_dir_str, |
293 |
p_option_str, p_filter_str, is_verbose); |
|
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
294 |
}
|
295 |
||
296 |
static int |
|
1.2.1
by Daniel Jacobowitz
Import upstream version 2.0.3 |
297 |
transfer_dir_internal(struct vsf_session* p_sess, int is_control, |
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
298 |
struct vsf_sysutil_dir* p_dir, |
299 |
const struct mystr* p_base_dir_str, |
|
300 |
const struct mystr* p_option_str, |
|
301 |
const struct mystr* p_filter_str, |
|
1.2.1
by Daniel Jacobowitz
Import upstream version 2.0.3 |
302 |
int is_verbose) |
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
303 |
{
|
304 |
struct mystr_list dir_list = INIT_STRLIST; |
|
305 |
struct mystr_list subdir_list = INIT_STRLIST; |
|
306 |
struct mystr dir_prefix_str = INIT_MYSTR; |
|
307 |
struct mystr_list* p_subdir_list = 0; |
|
308 |
struct str_locate_result loc_result = str_locate_char(p_option_str, 'R'); |
|
309 |
int failed = 0; |
|
1.2.1
by Daniel Jacobowitz
Import upstream version 2.0.3 |
310 |
enum EVSFRWTarget target = kVSFRWData; |
311 |
if (is_control) |
|
312 |
{
|
|
313 |
target = kVSFRWControl; |
|
314 |
}
|
|
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
315 |
if (loc_result.found && tunable_ls_recurse_enable) |
316 |
{
|
|
317 |
p_subdir_list = &subdir_list; |
|
318 |
}
|
|
319 |
vsf_ls_populate_dir_list(&dir_list, p_subdir_list, p_dir, p_base_dir_str, |
|
320 |
p_option_str, p_filter_str, is_verbose); |
|
321 |
if (p_subdir_list) |
|
322 |
{
|
|
323 |
int retval; |
|
324 |
str_copy(&dir_prefix_str, p_base_dir_str); |
|
325 |
str_append_text(&dir_prefix_str, ":\r\n"); |
|
1.2.1
by Daniel Jacobowitz
Import upstream version 2.0.3 |
326 |
retval = ftp_write_str(p_sess, &dir_prefix_str, target); |
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
327 |
if (retval != 0) |
328 |
{
|
|
329 |
failed = 1; |
|
330 |
}
|
|
331 |
}
|
|
332 |
if (!failed) |
|
333 |
{
|
|
1.2.1
by Daniel Jacobowitz
Import upstream version 2.0.3 |
334 |
failed = write_dir_list(p_sess, &dir_list, target); |
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
335 |
}
|
336 |
/* Recurse into the subdirectories if required... */
|
|
337 |
if (!failed) |
|
338 |
{
|
|
339 |
struct mystr sub_str = INIT_MYSTR; |
|
340 |
unsigned int num_subdirs = str_list_get_length(&subdir_list); |
|
341 |
unsigned int subdir_index; |
|
342 |
for (subdir_index = 0; subdir_index < num_subdirs; subdir_index++) |
|
343 |
{
|
|
344 |
int retval; |
|
345 |
struct vsf_sysutil_dir* p_subdir; |
|
346 |
const struct mystr* p_subdir_str = |
|
347 |
str_list_get_pstr(&subdir_list, subdir_index); |
|
348 |
if (str_equal_text(p_subdir_str, ".") || |
|
349 |
str_equal_text(p_subdir_str, "..")) |
|
350 |
{
|
|
351 |
continue; |
|
352 |
}
|
|
353 |
str_copy(&sub_str, p_base_dir_str); |
|
354 |
str_append_char(&sub_str, '/'); |
|
355 |
str_append_str(&sub_str, p_subdir_str); |
|
356 |
p_subdir = str_opendir(&sub_str); |
|
357 |
if (p_subdir == 0) |
|
358 |
{
|
|
359 |
/* Unreadable, gone missing, etc. - no matter */
|
|
360 |
continue; |
|
361 |
}
|
|
362 |
str_alloc_text(&dir_prefix_str, "\r\n"); |
|
1.2.1
by Daniel Jacobowitz
Import upstream version 2.0.3 |
363 |
retval = ftp_write_str(p_sess, &dir_prefix_str, target); |
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
364 |
if (retval != 0) |
365 |
{
|
|
366 |
failed = 1; |
|
367 |
break; |
|
368 |
}
|
|
1.2.1
by Daniel Jacobowitz
Import upstream version 2.0.3 |
369 |
retval = transfer_dir_internal(p_sess, is_control, p_subdir, &sub_str, |
370 |
p_option_str, p_filter_str, is_verbose); |
|
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
371 |
vsf_sysutil_closedir(p_subdir); |
372 |
if (retval != 0) |
|
373 |
{
|
|
374 |
failed = 1; |
|
375 |
break; |
|
376 |
}
|
|
377 |
}
|
|
378 |
str_free(&sub_str); |
|
379 |
}
|
|
380 |
str_list_free(&dir_list); |
|
381 |
str_list_free(&subdir_list); |
|
382 |
str_free(&dir_prefix_str); |
|
383 |
if (!failed) |
|
384 |
{
|
|
385 |
return 0; |
|
386 |
}
|
|
387 |
else
|
|
388 |
{
|
|
389 |
return -1; |
|
390 |
}
|
|
391 |
}
|
|
392 |
||
393 |
/* XXX - really, this should be refactored into a "buffered writer" object */
|
|
394 |
static int |
|
1.2.1
by Daniel Jacobowitz
Import upstream version 2.0.3 |
395 |
write_dir_list(struct vsf_session* p_sess, struct mystr_list* p_dir_list, |
396 |
enum EVSFRWTarget target) |
|
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
397 |
{
|
398 |
/* This function writes out a list of strings to the client, over the
|
|
399 |
* data socket. We now coalesce the strings into fewer write() syscalls,
|
|
400 |
* which saved 33% CPU time writing a large directory.
|
|
401 |
*/
|
|
402 |
int retval = 0; |
|
403 |
unsigned int dir_index_max = str_list_get_length(p_dir_list); |
|
404 |
unsigned int dir_index; |
|
405 |
struct mystr buf_str = INIT_MYSTR; |
|
406 |
str_reserve(&buf_str, VSFTP_DIR_BUFSIZE); |
|
407 |
for (dir_index = 0; dir_index < dir_index_max; dir_index++) |
|
408 |
{
|
|
409 |
str_append_str(&buf_str, str_list_get_pstr(p_dir_list, dir_index)); |
|
410 |
if (dir_index == dir_index_max - 1 || |
|
411 |
str_getlen(&buf_str) + |
|
412 |
str_getlen(str_list_get_pstr(p_dir_list, dir_index + 1)) > |
|
413 |
VSFTP_DIR_BUFSIZE) |
|
414 |
{
|
|
415 |
/* Writeout needed - we're either at the end, or we filled the buffer */
|
|
1.2.1
by Daniel Jacobowitz
Import upstream version 2.0.3 |
416 |
int writeret = ftp_write_str(p_sess, &buf_str, target); |
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
417 |
if (writeret != 0) |
418 |
{
|
|
419 |
retval = 1; |
|
420 |
break; |
|
421 |
}
|
|
422 |
str_empty(&buf_str); |
|
423 |
}
|
|
424 |
}
|
|
425 |
str_free(&buf_str); |
|
426 |
return retval; |
|
427 |
}
|
|
428 |
||
429 |
struct vsf_transfer_ret |
|
430 |
vsf_ftpdataio_transfer_file(struct vsf_session* p_sess, int remote_fd, |
|
431 |
int file_fd, int is_recv, int is_ascii) |
|
432 |
{
|
|
433 |
if (!is_recv) |
|
434 |
{
|
|
1.2.1
by Daniel Jacobowitz
Import upstream version 2.0.3 |
435 |
if (is_ascii || p_sess->data_use_ssl) |
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
436 |
{
|
1.2.1
by Daniel Jacobowitz
Import upstream version 2.0.3 |
437 |
return do_file_send_rwloop(p_sess, file_fd, is_ascii); |
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
438 |
}
|
439 |
else
|
|
440 |
{
|
|
1.2.1
by Daniel Jacobowitz
Import upstream version 2.0.3 |
441 |
filesize_t curr_offset = vsf_sysutil_get_file_offset(file_fd); |
442 |
filesize_t num_send = calc_num_send(file_fd, curr_offset); |
|
443 |
return do_file_send_sendfile( |
|
444 |
p_sess, remote_fd, file_fd, curr_offset, num_send); |
|
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
445 |
}
|
446 |
}
|
|
447 |
else
|
|
448 |
{
|
|
1.2.1
by Daniel Jacobowitz
Import upstream version 2.0.3 |
449 |
return do_file_recv(p_sess, file_fd, is_ascii); |
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
450 |
}
|
451 |
}
|
|
452 |
||
453 |
static struct vsf_transfer_ret |
|
1.2.1
by Daniel Jacobowitz
Import upstream version 2.0.3 |
454 |
do_file_send_rwloop(struct vsf_session* p_sess, int file_fd, int is_ascii) |
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
455 |
{
|
456 |
static char* p_readbuf; |
|
457 |
static char* p_asciibuf; |
|
458 |
struct vsf_transfer_ret ret_struct = { 0, 0 }; |
|
1.2.1
by Daniel Jacobowitz
Import upstream version 2.0.3 |
459 |
unsigned int chunk_size = get_chunk_size(); |
460 |
char* p_writefrom_buf; |
|
1.2.6
by Daniel Baumann
Import upstream version 2.1.1~pre1 |
461 |
int prev_cr = 0; |
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
462 |
if (p_readbuf == 0) |
463 |
{
|
|
464 |
vsf_secbuf_alloc(&p_readbuf, VSFTP_DATA_BUFSIZE); |
|
465 |
}
|
|
1.2.1
by Daniel Jacobowitz
Import upstream version 2.0.3 |
466 |
if (is_ascii) |
467 |
{
|
|
1.2.6
by Daniel Baumann
Import upstream version 2.1.1~pre1 |
468 |
if (p_asciibuf == 0) |
469 |
{
|
|
470 |
/* NOTE!! * 2 factor because we can double the data by doing our ASCII
|
|
471 |
* linefeed mangling
|
|
472 |
*/
|
|
473 |
vsf_secbuf_alloc(&p_asciibuf, VSFTP_DATA_BUFSIZE * 2); |
|
474 |
}
|
|
1.2.1
by Daniel Jacobowitz
Import upstream version 2.0.3 |
475 |
p_writefrom_buf = p_asciibuf; |
476 |
}
|
|
477 |
else
|
|
478 |
{
|
|
479 |
p_writefrom_buf = p_readbuf; |
|
480 |
}
|
|
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
481 |
while (1) |
482 |
{
|
|
483 |
unsigned int num_to_write; |
|
1.2.1
by Daniel Jacobowitz
Import upstream version 2.0.3 |
484 |
int retval = vsf_sysutil_read(file_fd, p_readbuf, chunk_size); |
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
485 |
if (vsf_sysutil_retval_is_error(retval)) |
486 |
{
|
|
487 |
ret_struct.retval = -1; |
|
488 |
return ret_struct; |
|
489 |
}
|
|
490 |
else if (retval == 0) |
|
491 |
{
|
|
492 |
/* Success - cool */
|
|
493 |
return ret_struct; |
|
494 |
}
|
|
1.2.1
by Daniel Jacobowitz
Import upstream version 2.0.3 |
495 |
if (is_ascii) |
496 |
{
|
|
1.2.6
by Daniel Baumann
Import upstream version 2.1.1~pre1 |
497 |
struct bin_to_ascii_ret ret = |
498 |
vsf_ascii_bin_to_ascii(p_readbuf, |
|
499 |
p_asciibuf, |
|
500 |
(unsigned int) retval, |
|
501 |
prev_cr); |
|
502 |
num_to_write = ret.stored; |
|
503 |
prev_cr = ret.last_was_cr; |
|
1.2.1
by Daniel Jacobowitz
Import upstream version 2.0.3 |
504 |
}
|
505 |
else
|
|
506 |
{
|
|
507 |
num_to_write = (unsigned int) retval; |
|
508 |
}
|
|
509 |
retval = ftp_write_data(p_sess, p_writefrom_buf, num_to_write); |
|
1.2.4
by Matti Lindell
Import upstream version 2.0.6 |
510 |
if (!vsf_sysutil_retval_is_error(retval)) |
511 |
{
|
|
512 |
ret_struct.transferred += (unsigned int) retval; |
|
513 |
}
|
|
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
514 |
if (vsf_sysutil_retval_is_error(retval) || |
515 |
(unsigned int) retval != num_to_write) |
|
516 |
{
|
|
1.2.1
by Daniel Jacobowitz
Import upstream version 2.0.3 |
517 |
ret_struct.retval = -2; |
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
518 |
return ret_struct; |
519 |
}
|
|
520 |
}
|
|
521 |
}
|
|
522 |
||
523 |
static struct vsf_transfer_ret |
|
1.2.1
by Daniel Jacobowitz
Import upstream version 2.0.3 |
524 |
do_file_send_sendfile(struct vsf_session* p_sess, int net_fd, int file_fd, |
525 |
filesize_t curr_file_offset, filesize_t bytes_to_send) |
|
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
526 |
{
|
527 |
int retval; |
|
1.2.1
by Daniel Jacobowitz
Import upstream version 2.0.3 |
528 |
unsigned int chunk_size = 0; |
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
529 |
struct vsf_transfer_ret ret_struct = { 0, 0 }; |
1.2.1
by Daniel Jacobowitz
Import upstream version 2.0.3 |
530 |
filesize_t init_file_offset = curr_file_offset; |
531 |
filesize_t bytes_sent; |
|
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
532 |
if (p_sess->bw_rate_max) |
533 |
{
|
|
1.2.1
by Daniel Jacobowitz
Import upstream version 2.0.3 |
534 |
chunk_size = get_chunk_size(); |
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
535 |
}
|
536 |
/* Just because I can ;-) */
|
|
537 |
retval = vsf_sysutil_sendfile(net_fd, file_fd, &curr_file_offset, |
|
538 |
bytes_to_send, chunk_size); |
|
539 |
bytes_sent = curr_file_offset - init_file_offset; |
|
540 |
ret_struct.transferred = bytes_sent; |
|
541 |
if (vsf_sysutil_retval_is_error(retval)) |
|
542 |
{
|
|
1.2.1
by Daniel Jacobowitz
Import upstream version 2.0.3 |
543 |
ret_struct.retval = -2; |
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
544 |
return ret_struct; |
545 |
}
|
|
546 |
else if (bytes_sent != bytes_to_send) |
|
547 |
{
|
|
1.2.1
by Daniel Jacobowitz
Import upstream version 2.0.3 |
548 |
ret_struct.retval = -2; |
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
549 |
return ret_struct; |
550 |
}
|
|
551 |
return ret_struct; |
|
552 |
}
|
|
553 |
||
1.2.1
by Daniel Jacobowitz
Import upstream version 2.0.3 |
554 |
static filesize_t |
555 |
calc_num_send(int file_fd, filesize_t init_offset) |
|
556 |
{
|
|
557 |
static struct vsf_sysutil_statbuf* s_p_statbuf; |
|
558 |
filesize_t bytes_to_send; |
|
559 |
/* Work out how many bytes to send based on file size minus current offset */
|
|
560 |
vsf_sysutil_fstat(file_fd, &s_p_statbuf); |
|
561 |
bytes_to_send = vsf_sysutil_statbuf_get_size(s_p_statbuf); |
|
562 |
if (init_offset < 0 || bytes_to_send < 0) |
|
563 |
{
|
|
564 |
die("calc_num_send: negative file offset or send count"); |
|
565 |
}
|
|
566 |
/* Don't underflow if some bonehead sets a REST greater than the file size */
|
|
567 |
if (init_offset > bytes_to_send) |
|
568 |
{
|
|
569 |
bytes_to_send = 0; |
|
570 |
}
|
|
571 |
else
|
|
572 |
{
|
|
573 |
bytes_to_send -= init_offset; |
|
574 |
}
|
|
575 |
return bytes_to_send; |
|
576 |
}
|
|
577 |
||
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
578 |
static struct vsf_transfer_ret |
1.2.1
by Daniel Jacobowitz
Import upstream version 2.0.3 |
579 |
do_file_recv(struct vsf_session* p_sess, int file_fd, int is_ascii) |
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
580 |
{
|
581 |
static char* p_recvbuf; |
|
582 |
unsigned int num_to_write; |
|
583 |
struct vsf_transfer_ret ret_struct = { 0, 0 }; |
|
1.2.1
by Daniel Jacobowitz
Import upstream version 2.0.3 |
584 |
unsigned int chunk_size = get_chunk_size(); |
585 |
int prev_cr = 0; |
|
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
586 |
if (p_recvbuf == 0) |
587 |
{
|
|
1.2.1
by Daniel Jacobowitz
Import upstream version 2.0.3 |
588 |
/* Now that we do ASCII conversion properly, the plus one is to cater for
|
589 |
* the fact we may need to stick a '\r' at the front of the buffer if the
|
|
590 |
* last buffer fragment eneded in a '\r' and the current buffer fragment
|
|
591 |
* does not start with a '\n'.
|
|
592 |
*/
|
|
593 |
vsf_secbuf_alloc(&p_recvbuf, VSFTP_DATA_BUFSIZE + 1); |
|
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
594 |
}
|
595 |
while (1) |
|
596 |
{
|
|
1.2.1
by Daniel Jacobowitz
Import upstream version 2.0.3 |
597 |
const char* p_writebuf = p_recvbuf + 1; |
598 |
int retval = ftp_read_data(p_sess, p_recvbuf + 1, chunk_size); |
|
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
599 |
if (vsf_sysutil_retval_is_error(retval)) |
600 |
{
|
|
1.2.1
by Daniel Jacobowitz
Import upstream version 2.0.3 |
601 |
ret_struct.retval = -2; |
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
602 |
return ret_struct; |
603 |
}
|
|
1.2.1
by Daniel Jacobowitz
Import upstream version 2.0.3 |
604 |
else if (retval == 0 && !prev_cr) |
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
605 |
{
|
606 |
/* Transfer done, nifty */
|
|
607 |
return ret_struct; |
|
608 |
}
|
|
609 |
num_to_write = (unsigned int) retval; |
|
610 |
ret_struct.transferred += num_to_write; |
|
611 |
if (is_ascii) |
|
612 |
{
|
|
613 |
/* Handle ASCII conversion if we have to. Note that using the same
|
|
614 |
* buffer for source and destination is safe, because the ASCII ->
|
|
615 |
* binary transform only ever results in a smaller file.
|
|
616 |
*/
|
|
1.2.1
by Daniel Jacobowitz
Import upstream version 2.0.3 |
617 |
struct ascii_to_bin_ret ret = |
618 |
vsf_ascii_ascii_to_bin(p_recvbuf, num_to_write, prev_cr); |
|
619 |
num_to_write = ret.stored; |
|
620 |
prev_cr = ret.last_was_cr; |
|
621 |
p_writebuf = ret.p_buf; |
|
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
622 |
}
|
1.2.1
by Daniel Jacobowitz
Import upstream version 2.0.3 |
623 |
retval = vsf_sysutil_write_loop(file_fd, p_writebuf, num_to_write); |
1
by Daniel Jacobowitz
Import upstream version 1.0.0 |
624 |
if (vsf_sysutil_retval_is_error(retval) || |
625 |
(unsigned int) retval != num_to_write) |
|
626 |
{
|
|
627 |
ret_struct.retval = -1; |
|
628 |
return ret_struct; |
|
629 |
}
|
|
630 |
}
|
|
631 |
}
|
|
632 |
||
1.2.1
by Daniel Jacobowitz
Import upstream version 2.0.3 |
633 |
static unsigned int |
634 |
get_chunk_size() |
|
635 |
{
|
|
636 |
unsigned int ret = VSFTP_DATA_BUFSIZE; |
|
637 |
if (tunable_trans_chunk_size < VSFTP_DATA_BUFSIZE && |
|
638 |
tunable_trans_chunk_size > 0) |
|
639 |
{
|
|
640 |
ret = tunable_trans_chunk_size; |
|
641 |
if (ret < 4096) |
|
642 |
{
|
|
643 |
ret = 4096; |
|
644 |
}
|
|
645 |
}
|
|
646 |
return ret; |
|
647 |
}
|
|
648 |