~ubuntu-branches/ubuntu/trusty/vsftpd/trusty-proposed

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