diff options
Diffstat (limited to 'libssh')
-rw-r--r-- | libssh/channels.c | 349 | ||||
-rw-r--r-- | libssh/libssh.map | 4 | ||||
-rw-r--r-- | libssh/sftp.c | 6 |
3 files changed, 239 insertions, 120 deletions
diff --git a/libssh/channels.c b/libssh/channels.c index 5949f78..4fbe3f8 100644 --- a/libssh/channels.c +++ b/libssh/channels.c @@ -1335,90 +1335,248 @@ error: /* TODO : fix the delayed close thing */ /* TODO : fix the blocking behaviours */ -/* reads into a channel and put result into buffer */ -/* returns number of bytes read, 0 if eof or such and -1 in case of error */ -/* if bytes != 0, the exact number of bytes are going to be read */ -/** \brief reads data from a channel - * \param channel channel - * \param buffer buffer which will get the data - * \param bytes number of bytes to be read. If it is bigger - * than 0, the exact size will be read, else (bytes=0) it will return - * once anything is available - * \param is_stderr boolean value to mark reading from the stderr flow. - * \return number of bytes read\n - * 0 on end of file\n - * SSH_ERROR on error + +/** + * @brief Read data from a channel into a buffer. + * + * @param channel The channel to read from. + * + * @param buffer The buffer which will get the data. + * + * @param count The count of bytes to be read. If it is biggerthan 0, + * the exact size will be read, else (bytes=0) it will + * return once anything is available. + * + * @param is_stderr A boolean value to mark reading from the stderr stream. + * + * @return The number of bytes read, 0 on end of file or SSH_ERROR on error. */ -int channel_read(CHANNEL *channel, BUFFER *buffer, u32 bytes, int is_stderr) { - BUFFER *stdbuf=NULL; - SSH_SESSION *session=channel->session; - u32 maxread=bytes; - u32 len; +int channel_read_buffer(CHANNEL *channel, BUFFER *buffer, u32 count, + int is_stderr) { + SSH_SESSION *session=channel->session; + BUFFER *stdbuf = channel->stdout_buffer; + u32 maxread = count; + u32 len; - buffer_reinit(buffer); - enter_function(); - if(bytes==0) - maxread=MAX_PACKET_LEN; - /* maybe i should always set a buffer to avoid races between channel_default_bufferize and channel_read */ - if(is_stderr) - stdbuf=channel->stderr_buffer; - else - stdbuf=channel->stdout_buffer; + buffer_reinit(buffer); - /* We may have problem if the window is too small to accept as much data as asked */ - ssh_log(session, SSH_LOG_PROTOCOL, - "Read (%d) buffered : %d bytes. Window: %d", - bytes, - buffer_get_rest_len(stdbuf), - channel->local_window); - if(bytes > buffer_get_rest_len(stdbuf) + channel->local_window) { - if (grow_window(session, channel, bytes - buffer_get_rest_len(stdbuf)) < 0) { - leave_function(); - return -1; - } + enter_function(); + + if (count == 0) { + maxread = MAX_PACKET_LEN; + } + + if (is_stderr) { + stdbuf = channel->stderr_buffer; + } + + /* + * We may have problem if the window is too small to accept as much data + * as asked + */ + ssh_log(session, SSH_LOG_PROTOCOL, + "Read (%d) buffered: %d bytes. Window: %d", + count, + buffer_get_rest_len(stdbuf), + channel->local_window); + + if (count > buffer_get_rest_len(stdbuf) + channel->local_window) { + if (grow_window(session, channel, + count - buffer_get_rest_len(stdbuf)) < 0) { + leave_function(); + return -1; } - /* block reading if asked bytes=0 */ - while((buffer_get_rest_len(stdbuf)==0) || (buffer_get_rest_len(stdbuf) < bytes)){ - if(channel->remote_eof && buffer_get_rest_len(stdbuf)==0){ - leave_function(); - return 0; - } - if(channel->remote_eof) - break; /* return the resting bytes in buffer */ - if(buffer_get_rest_len(stdbuf)>=maxread) // stop reading when buffer is full enough - break; - if ((packet_read(session)) != SSH_OK || - (packet_translate(session) != SSH_OK)) { - leave_function(); - return -1; - } - packet_parse(session); + } + /* block reading if asked bytes=0 */ + while (buffer_get_rest_len(stdbuf) == 0 || + buffer_get_rest_len(stdbuf) < count) { + if (channel->remote_eof && buffer_get_rest_len(stdbuf) == 0) { + leave_function(); + return 0; } - if(channel->local_window < WINDOWLIMIT) { - if (grow_window(session, channel, 0) < 0) { - leave_function(); - return -1; - } + if (channel->remote_eof) { + /* Return the resting bytes in buffer */ + break; } - if(bytes==0){ - /* write the ful buffer informations */ - if (buffer_add_data(buffer, buffer_get_rest(stdbuf), - buffer_get_rest_len(stdbuf)) < 0) { - leave_function(); - return -1; - } - buffer_reinit(stdbuf); - } else { - len=buffer_get_rest_len(stdbuf); - len= (len>bytes?bytes:len); /* read bytes bytes if len is greater, everything otherwise */ - if (buffer_add_data(buffer, buffer_get_rest(stdbuf), len) < 0) { - leave_function(); - return -1; - } - buffer_pass_bytes(stdbuf,len); + if (buffer_get_rest_len(stdbuf) >= maxread) { + /* Stop reading when buffer is full enough */ + break; + } + + if ((packet_read(session)) != SSH_OK || + (packet_translate(session) != SSH_OK)) { + leave_function(); + return -1; + } + packet_parse(session); + } + + if(channel->local_window < WINDOWLIMIT) { + if (grow_window(session, channel, 0) < 0) { + leave_function(); + return -1; + } + } + + if (count == 0) { + /* write the ful buffer informations */ + if (buffer_add_data(buffer, buffer_get_rest(stdbuf), + buffer_get_rest_len(stdbuf)) < 0) { + leave_function(); + return -1; + } + buffer_reinit(stdbuf); + } else { + /* Read bytes bytes if len is greater, everything otherwise */ + len = buffer_get_rest_len(stdbuf); + len = (len > count ? count : len); + if (buffer_add_data(buffer, buffer_get_rest(stdbuf), len) < 0) { + leave_function(); + return -1; + } + buffer_pass_bytes(stdbuf,len); + } + + leave_function(); + return buffer_get_len(buffer); +} + +/* TODO FIXME Fix the delayed close thing */ +/* TODO FIXME Fix the blocking behaviours */ + +/** + * @brief Reads data from a channel. + * + * @param channel The channel to read from. + * + * @param dest The destination buffer which will get the data. + * + * @param count The count of bytes to be read. + * + * @param is_stderr A boolean value to mark reading from the stderr flow. + * + * @return The number of bytes read, 0 on end of file or SSH_ERROR on error. + */ +int channel_read(CHANNEL *channel, void *dest, u32 count, int is_stderr) { + SSH_SESSION *session = channel->session; + BUFFER *stdbuf = channel->stdout_buffer; + u32 len; + + enter_function(); + + if (count == 0) { + leave_function(); + return 0; + } + + if (is_stderr) { + stdbuf=channel->stderr_buffer; + } + + /* + * We may have problem if the window is too small to accept as much data + * as asked + */ + ssh_log(session, SSH_LOG_PROTOCOL, + "Read (%d) buffered : %d bytes. Window: %d", + count, + buffer_get_rest_len(stdbuf), + channel->local_window); + + if (count > buffer_get_rest_len(stdbuf) + channel->local_window) { + if (grow_window(session, channel, + count - buffer_get_rest_len(stdbuf)) < 0) { + leave_function(); + return -1; + } + } + + /* block reading if asked bytes=0 */ + while (buffer_get_rest_len(stdbuf) == 0 || + buffer_get_rest_len(stdbuf) < count) { + if (channel->remote_eof && buffer_get_rest_len(stdbuf) == 0) { + leave_function(); + return 0; + } + + if (channel->remote_eof) { + /* Return the resting bytes in buffer */ + break; + } + + if (buffer_get_rest_len(stdbuf) >= count) { + /* Stop reading when buffer is full enough */ + break; + } + + if ((packet_read(session)) != SSH_OK || + (packet_translate(session) != SSH_OK)) { + leave_function(); + return -1; + } + packet_parse(session); + } + + if (channel->local_window < WINDOWLIMIT) { + if (grow_window(session, channel, 0) < 0) { + leave_function(); + return -1; } + } + + len = buffer_get_rest_len(stdbuf); + /* Read count bytes if len is greater, everything otherwise */ + len = (len > count ? count : len); + memcpy(dest, buffer_get_rest(stdbuf), len); + buffer_pass_bytes(stdbuf,len); + + leave_function(); + return len; +} + +/** + * @brief Do a nonblocking read on the channel. + * + * A nonblocking read on the specified channel. it will return <= count bytes of + * data read atomicly. + * + * @param channel The channel to read from. + * + * @param dest A pointer to a destination buffer. + * + * @param count The count of bytes of data to be read. + * + * @param is_stderr A boolean to select the stderr stream. + * + * @return The number of bytes read, 0 if nothing is available or + * SSH_ERROR on error. + * + * @warning Don't forget to check for EOF as it would return 0 here. + * + * @see channel_is_eof() + */ +int channel_read_nonblocking(CHANNEL *channel, void *dest, u32 count, + int is_stderr) { + SSH_SESSION *session = channel->session; + u32 to_read; + int rc; + + enter_function(); + + to_read = channel_poll(channel, is_stderr); + + if (to_read <= 0) { leave_function(); - return buffer_get_len(buffer); + return to_read; /* may be an error code */ + } + + if (to_read > count) { + to_read = count; + } + rc = channel_read(channel, dest, to_read, is_stderr); + + leave_function(); + return rc; } /** \brief polls the channel for data to read @@ -1453,45 +1611,6 @@ int channel_poll(CHANNEL *channel, int is_stderr){ return buffer_get_rest_len(buffer); } -/* nonblocking read on the specified channel. it will return <=len bytes of data read - atomicly. */ -/** This read will make a nonblocking read (unlike channel_read()) and won't force you - * to deal with BUFFER's - * \brief nonblocking read - * \param channel channel - * \param dest pointer to destination for data - * \param len maximum length of data to be read - * \param is_stderr boolean to select the stderr stream - * \return number of bytes read\n - * 0 if nothing is available\n - * SSH_ERROR on error - * \warning don't forget to check for EOF as it would - * return 0 here - * \see channel_is_eof() - */ -int channel_read_nonblocking(CHANNEL *channel, char *dest, u32 len, int is_stderr){ - SSH_SESSION *session = channel->session; - BUFFER *buffer; - int lu; - u32 to_read; - - enter_function(); - to_read=channel_poll(channel,is_stderr); - buffer=buffer_new(); - if(to_read<=0){ - buffer_free(buffer); - leave_function(); - return to_read; /* may be an error code */ - } - if(to_read>len) - to_read=len; - lu=channel_read(channel,buffer,to_read,is_stderr); - memcpy(dest,buffer_get(buffer),lu>=0?lu:0); - buffer_free(buffer); - leave_function(); - return lu; -} - /** \brief recover the session in which belong a channel * \param channel channel * \return the session pointer diff --git a/libssh/libssh.map b/libssh/libssh.map index c28d2e1..c865212 100644 --- a/libssh/libssh.map +++ b/libssh/libssh.map @@ -18,8 +18,8 @@ SSH_0.3 { channel_request_pty; channel_request_pty_size; channel_change_pty_size; channel_request_shell; channel_request_subsystem; channel_request_env; channel_request_exec; channel_request_sftp; channel_write; - channel_send_eof; channel_read; channel_poll; channel_close; - channel_read_nonblocking; channel_is_open; + channel_send_eof; channel_read_buffer; channel_read; channel_read_nonblocking; + channel_poll; channel_close; channel_is_open; channel_is_closed; channel_is_eof; channel_select; ssh_options_new; ssh_options_copy; ssh_options_free; ssh_options_set_wanted_algos; ssh_options_set_username; ssh_options_set_port; ssh_options_getopt; diff --git a/libssh/sftp.c b/libssh/sftp.c index 55dd93f..24666c8 100644 --- a/libssh/sftp.c +++ b/libssh/sftp.c @@ -237,7 +237,7 @@ SFTP_PACKET *sftp_packet_read(SFTP_SESSION *sftp) { return NULL; } - if (channel_read(sftp->channel, packet->payload, 4, 0) <= 0) { + if (channel_read_buffer(sftp->channel, packet->payload, 4, 0) <= 0) { buffer_free(packet->payload); SAFE_FREE(packet); sftp_leave_function(); @@ -252,7 +252,7 @@ SFTP_PACKET *sftp_packet_read(SFTP_SESSION *sftp) { } size = ntohl(size); - if (channel_read(sftp->channel, packet->payload, 1, 0) <= 0) { + if (channel_read_buffer(sftp->channel, packet->payload, 1, 0) <= 0) { buffer_free(packet->payload); SAFE_FREE(packet); sftp_leave_function(); @@ -261,7 +261,7 @@ SFTP_PACKET *sftp_packet_read(SFTP_SESSION *sftp) { buffer_get_u8(packet->payload, &packet->type); if (size > 1) { - if (channel_read(sftp->channel, packet->payload, size - 1, 0) <= 0) { + if (channel_read_buffer(sftp->channel, packet->payload, size - 1, 0) <= 0) { buffer_free(packet->payload); SAFE_FREE(packet); sftp_leave_function(); |