Subject: Problem with large write buffer

Problem with large write buffer

From: Daniele Pianu <muogoro_at_gmail.com>
Date: Tue, 12 May 2009 13:01:22 +0200

Hi guys,
I'm working on a strem library which implements various kind of
streams. All SSH based streams are implemented using your great
libssh2 library ;) Now I'm trying to implement the SFTP protocol, but
I've some problem when uploading large files. This example partially
reproduces the problem:

/*
 * test.c
 *
 * Test some sort of buffering mode built upon libssh2 SFtp implementation
 *
 */
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>

#include <libssh2.h>
#include <libssh2_sftp.h>

#define DEFAULT_BUFFLEN (1024)

int main( int argc, char *argv[] )
{
  struct sockaddr_in sin;
  in_addr_t host_addr;
  LIBSSH2_SESSION *session;
  LIBSSH2_SFTP *sftp_session;
  LIBSSH2_SFTP_HANDLE *sftp_handle;
  int fd, err_no, err_len;
  int sock, ret_val=0, rd_bytes=0, wr_bytes=0;

  void *buff, *tmp;
  int buff_pos=0, buff_size=DEFAULT_BUFFLEN, buff_read_step=DEFAULT_BUFFLEN, \
    buff_eof_pos=0;

  if ( argc != 5 )
  {
    fprintf( stderr, "Usage: %s USER PASSWORD SERVER FILEPATH\n", argv[0] );
    return 2;
  }

  /* Open file */
  fd = open( argv[4], O_RDONLY );
  if ( fd == -1 )
  {
    fprintf( stderr, "Unable to open file [%s]\n", argv[4] );
    return 1;
  }

  /* Init and connect socket */
  if ( host_addr = inet_addr( argv[3] ) == INADDR_NONE )
  {
    fprintf( stderr, "Invalid server address specified [%s]\n", argv[3] );
    return 1;
  }
  sin.sin_family = AF_INET;
  sin.sin_port = htons(22);
  sin.sin_addr.s_addr = host_addr;

  sock = socket( AF_INET, SOCK_STREAM, 0 );
  if ( connect( sock, (struct sockaddr*)(&sin), sizeof(struct sockaddr_in) ) )
  {
    fprintf( stderr, "Unable to connect to [%s:22]\n", argv[3] );
    return 1;
  }

  /* Create the session instance */
  session = libssh2_session_init();
  if ( !session )
  {
    fprintf( stderr, "Error initializing SSH session\n" );
    return 1;
  }

  //libssh2_session_set_blocking(session, 1);

  if ( libssh2_session_startup( session, sock ) )
  {
    fprintf( stderr, "Error starting SSH session\n" );
    return 1;
  }

  /* Authenticate via password */
  if ( libssh2_userauth_password( session, argv[1], argv[2] ) )
  {
    fprintf( stderr, "Unable to authenticate user [%s]"
             "(wrong password specified?)\n", argv[1] );
    ret_val = 1;
    goto clear_ssh;
  }

  /* Open an SFTP channel */
  sftp_session = libssh2_sftp_init( session );
  if ( !sftp_session )
  {
    fprintf( stderr, "Unable to open an SFTP channel\n" );
    ret_val = 1;
    goto clear_ssh;
  }

  sftp_handle = libssh2_sftp_open( sftp_session, "Test.out",
                                   LIBSSH2_FXF_WRITE | LIBSSH2_FXF_CREAT |
                                   LIBSSH2_FXF_TRUNC, LIBSSH2_SFTP_S_IRUSR |
                                   LIBSSH2_SFTP_S_IWUSR| LIBSSH2_SFTP_S_IRGRP|
                                   LIBSSH2_SFTP_S_IROTH );
  if ( !sftp_handle )
  {
    fprintf( stderr, "Unable to open remote file Test.out\n" );
    ret_val = 1;
    goto clear_sftp;
  }

  /* Init buffer */
  buff = malloc( DEFAULT_BUFFLEN );

  /* Fill the buffer:
   * buff_read_step bytes at time are read from buffer. If buffer capacity limit
   * is reached, a new buffer with double size is allocated and used
   */
  while ( ( rd_bytes = read( fd, buff+buff_pos, buff_read_step ) ) > 0 )
  {
    if ( rd_bytes == -1 )
    {
      fprintf( stderr, "Error reading local file\n" );
      ret_val = 1;
      libssh2_sftp_close( sftp_handle );
      goto clear_sftp;
    }

    /* Resize the buffer if needed */
    if ( (buff_pos + rd_bytes) >= buff_size )
    {
      fprintf( stdout, "Resize buffer from size [%d] to size [%d]\n",
               buff_size, buff_size*2 );
      tmp = malloc( buff_size );
      memcpy( tmp, buff, buff_size );
      free( buff );
      buff = malloc( buff_size * 2 );
      memcpy( buff, tmp, buff_size );
      buff_size *= 2;
      free( tmp );
    }

    buff_pos += rd_bytes;
  }
  close( fd );

  /* Flush the buffer to remote opened file */
  buff_eof_pos = buff_pos;
  buff_pos = 0;
  while ( buff_pos < buff_eof_pos )
  {
    /* Try always to write as much bytes as possible */
    wr_bytes = libssh2_sftp_write( sftp_handle, buff+buff_pos,
buff_eof_pos-buff_pos );

    if ( wr_bytes == -1 )
    {
      err_no = libssh2_session_last_error(session, (char**)(&tmp), &err_len, 1);
      fprintf( stderr, "Error flushing buffer: error [%d], [%s]\n",
               err_no, tmp );
      ret_val = 1;
      libssh2_sftp_close( sftp_handle );
      goto clear_sftp;
    }

    fprintf( stdout, "[%d] bytes flushed\n", wr_bytes );
    buff_pos += wr_bytes;
  }

  libssh2_sftp_close( sftp_handle );

 clear_sftp:
  libssh2_sftp_shutdown( sftp_session );
  free( buff );

 clear_ssh:
  libssh2_session_disconnect( session, "Bye bye" );
  libssh2_session_free( session );

  close( sock );

  return ret_val;
}

What the program does is fill (and, if needed, resize) a buffer with
the content of a local file. Then, the buffer is flushed in the remote
file "Test.out" in the specified SSH server. With small size files the
program works without problems, but with large files the
libssh2_sftp_write function always returns -1. The output of the
execution is something like this:

[daniele_at_foo libssh2_sftp_test]$ ./test daniele my_pass 192.168.0.66
/home/daniele/a_larg_file.large
Start filling buffer
Resize buffer from size [1024] to size [2048]
Resize buffer from size [2048] to size [4096]
Resize buffer from size [4096] to size [8192]
Resize buffer from size [8192] to size [16384]
Resize buffer from size [16384] to size [32768]
Resize buffer from size [32768] to size [65536]
Resize buffer from size [65536] to size [131072]
Resize buffer from size [131072] to size [262144]
Resize buffer from size [262144] to size [524288]
Resize buffer from size [524288] to size [1048576]
Resize buffer from size [1048576] to size [2097152]
Start flushing buffer: [1519424] bytes to flush
Error flushing buffer: error [-30], [Timeout waiting for status message]
[daniele_at_foo libssh2_sftp_test]$

In my stream library the implementation of the buffering write
strategy is similar to the one used in the above example, but the
error returned is
libssh2 error code: [-7] description: [Unable to send FXP_READ command]

Is there some know problem with large write buffer and sftp protocol
implementation?

Thanks for any advice,
Daniele

------------------------------------------------------------------------------
The NEW KODAK i700 Series Scanners deliver under ANY circumstances! Your
production scanning environment may not be a perfect world - but thanks to
Kodak, there's a perfect scanner to get the job done! With the NEW KODAK i700
Series Scanner you'll get full speed at 300 dpi even with all image
processing features enabled. http://p.sf.net/sfu/kodak-com
_______________________________________________
libssh2-devel mailing list
libssh2-devel_at_lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/libssh2-devel
Received on 2009-05-12