ftpdataio.c
上传用户:ig0539
上传日期:2022-05-21
资源大小:181k
文件大小:18k
源码类别:

Ftp客户端

开发平台:

C/C++

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