Index: src/sftp.c =================================================================== RCS file: /cvsroot/libssh2/libssh2/src/sftp.c,v retrieving revision 1.34 diff -u -r1.34 sftp.c --- src/sftp.c 18 Apr 2007 18:51:04 -0000 1.34 +++ src/sftp.c 19 Apr 2007 16:44:21 -0000 @@ -84,6 +84,8 @@ LIBSSH2_SFTP_HANDLE *handles; unsigned long last_errno; + + time_t requirev_start; }; #define LIBSSH2_SFTP_HANDLE_FILE 0 @@ -158,10 +160,15 @@ unsigned char buffer[4]; /* To store the packet length */ unsigned char *packet; unsigned long packet_len, packet_received; + int rc; _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Waiting for packet: %s block", should_block ? "will" : "willnot"); libssh2_channel_set_blocking(channel, should_block); - if (4 != _libssh2_channel_read(channel, (char *)buffer, 4)) { + rc = _libssh2_channel_read(channel, (char *)buffer, 4); + if (!should_block && (rc == PACKET_EAGAIN)) { + return PACKET_EAGAIN; + } + else if (4 != rc) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for FXP packet", 0); return -1; @@ -181,27 +188,34 @@ return -1; } - packet_received = 0; - while (packet_len > packet_received) { - long bytes_received = - _libssh2_channel_read(channel, - (char *)packet + packet_received, - packet_len - packet_received); - - if (bytes_received < 0) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Receive error waiting for SFTP packet", 0); - LIBSSH2_FREE(session, packet); - return -1; - } - packet_received += bytes_received; - } + packet_received = 0; + while (packet_len > packet_received) { + long bytes_received = _libssh2_channel_read(channel, (char *)packet + packet_received, packet_len - packet_received); + + if (!should_block && (bytes_received == PACKET_EAGAIN)) { +#warning "XXX - This is not right, but adding a partial packet causes crashes" + bytes_received = 0; +// if (libssh2_sftp_packet_add(sftp, packet, packet_received)) { +// LIBSSH2_FREE(session, packet); +// return -1; +// } +// _libssh2_debug(session, LIBSSH2_DBG_SFTP, "libssh2_sftp_packet_read(): read %ld bytes: EAGAIN", packet_received); +// return PACKET_EAGAIN; + } + else if (bytes_received < 0) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Receive error waiting for SFTP packet", 0); + LIBSSH2_FREE(session, packet); + return -1; + } + packet_received += bytes_received; + } - if (libssh2_sftp_packet_add(sftp, packet, packet_len)) { - LIBSSH2_FREE(session, packet); - return -1; - } + if (libssh2_sftp_packet_add(sftp, packet, packet_len)) { + LIBSSH2_FREE(session, packet); + return -1; + } - return packet[0]; + return packet[0]; } /* }}} */ @@ -263,28 +277,32 @@ */ static int libssh2_sftp_packet_require(LIBSSH2_SFTP *sftp, unsigned char packet_type, unsigned long request_id, unsigned char **data, unsigned long *data_len) { - LIBSSH2_SESSION *session = sftp->channel->session; - _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Requiring %d packet", (int)packet_type); - - if (libssh2_sftp_packet_ask(sftp, packet_type, request_id, data, data_len, 0) == 0) { - /* A packet was available in the packet brigade */ - return 0; - } - - while (session->socket_state == LIBSSH2_SOCKET_CONNECTED) { - int ret = libssh2_sftp_packet_read(sftp, 1); - if (ret <= 0) { - return -1; - } - - if (packet_type == ret) { - /* Be lazy, let packet_ask pull it out of the brigade */ - return libssh2_sftp_packet_ask(sftp, packet_type, request_id, data, data_len, 0); - } - } - - /* Only reached if the socket died */ - return -1; + LIBSSH2_SESSION *session = sftp->channel->session; + int bl = libssh2_channel_get_blocking(sftp->channel); + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Requiring %d packet", (int)packet_type); + + if (libssh2_sftp_packet_ask(sftp, packet_type, request_id, data, data_len, 0) == 0) { + /* The right packet was available in the packet brigade */ + return 0; + } + + while (session->socket_state == LIBSSH2_SOCKET_CONNECTED) { + int ret = libssh2_sftp_packet_read(sftp, bl); + if (!bl && (ret == PACKET_EAGAIN)) { + return PACKET_EAGAIN; + } + else if (ret <= 0) { + return -1; + } + + if (packet_type == ret) { + /* Be lazy, let packet_ask pull it out of the brigade */ + return libssh2_sftp_packet_ask(sftp, packet_type, request_id, data, data_len, 0); + } + } + + /* Only reached if the socket died */ + return -1; } /* }}} */ @@ -298,40 +316,69 @@ unsigned char **data, unsigned long *data_len) { - int i; - time_t start = time(NULL); - - /* Flush */ - while (libssh2_sftp_packet_read(sftp, 0) > 0); - - while (sftp->channel->session->socket_state == LIBSSH2_SOCKET_CONNECTED) { - int ret; - for(i = 0; i < num_valid_responses; i++) { - if (libssh2_sftp_packet_ask(sftp, valid_responses[i], - request_id, data, - data_len, 0) == 0) { - return 0; - } - } - - ret = libssh2_sftp_packet_read(sftp, 1); - if (ret < 0) { - return -1; - } - if (ret == 0) { - /* prevent busy-looping */ - long left = LIBSSH2_READ_TIMEOUT - - (time(NULL) - start); - - if((left <= 0) || - (libssh2_waitsocket(sftp->channel->session, - left)<=0)) { - return PACKET_TIMEOUT; - } - } - } - - return -1; + int i; + int bl = libssh2_channel_get_blocking(sftp->channel); + + /* + * If no timeout is active, start a new one and flush + * any pending packets + */ + if (sftp->requirev_start == 0) { + _libssh2_debug(sftp->channel->session, LIBSSH2_DBG_SFTP, "_requirev(): Initialize timeout"); + sftp->requirev_start = time(NULL); + + /* Flush */ + while (libssh2_sftp_packet_read(sftp, 0) > 0); + } + + while (sftp->channel->session->socket_state == LIBSSH2_SOCKET_CONNECTED) { + int ret; + + for(i = 0; i < num_valid_responses; i++) { + if (libssh2_sftp_packet_ask(sftp, valid_responses[i], request_id, + data, data_len, 0) == 0) { + /* + * Set to zero before all returns to say + * the timeout is not active + */ + _libssh2_debug(sftp->channel->session, LIBSSH2_DBG_SFTP, "_requirev(): found in ask"); + sftp->requirev_start = 0; + return 0; + } + } + + ret = libssh2_sftp_packet_read(sftp, bl); + if (!bl && (ret == PACKET_EAGAIN)) { + _libssh2_debug(sftp->channel->session, LIBSSH2_DBG_SFTP, "_requirev(): PACKET_EAGAIN"); + return PACKET_EAGAIN; + } + else if (ret < 0) { + sftp->requirev_start = 0; + _libssh2_debug(sftp->channel->session, LIBSSH2_DBG_SFTP, "_requirev(): -1"); + return -1; + } + else if (ret == 0) { + /* prevent busy-looping */ + if ((LIBSSH2_READ_TIMEOUT - (time(NULL) - sftp->requirev_start)) <= 0) { + _libssh2_debug(sftp->channel->session, LIBSSH2_DBG_SFTP, "_requirev(): PACKET_TIMEOUT"); + return PACKET_TIMEOUT; + } + ret = libssh2_waitsocket(sftp->channel->session, 1); + if (!bl && (ret == PACKET_EAGAIN)) { + _libssh2_debug(sftp->channel->session, LIBSSH2_DBG_SFTP, "_requirev(): PACKET_EAGAIN 2"); + return PACKET_EAGAIN; + } + else if (ret <= 0) { + sftp->requirev_start = 0; + _libssh2_debug(sftp->channel->session, LIBSSH2_DBG_SFTP, "_requirev(): PACKET_TIMEOUT 2"); + return PACKET_TIMEOUT; + } + } + } + + _libssh2_debug(sftp->channel->session, LIBSSH2_DBG_SFTP, "_requirev(): -1 2"); + sftp->requirev_start = 0; + return -1; } /* }}} */ @@ -459,6 +506,7 @@ LIBSSH2_CHANNEL *channel; unsigned char *data, *s, buffer[9]; /* sftp_header(5){excludes request_id} + version_id(4) */ unsigned long data_len; + int rc; _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Initializing SFTP subsystem"); channel = libssh2_channel_open_session(session); @@ -500,8 +548,11 @@ return NULL; } - if (libssh2_sftp_packet_require(sftp, SSH_FXP_VERSION, 0, - &data, &data_len)) { +#warning "XXX - Looping on PACKET_EAGAIN (blocking) until fix is migrated up farther" + while ((rc = libssh2_sftp_packet_require(sftp, SSH_FXP_VERSION, 0, &data, &data_len)) == PACKET_EAGAIN) { + ; + } + if (rc) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for response from SFTP subsystem", 0); libssh2_channel_free(channel); @@ -574,6 +625,7 @@ unsigned char *packet, *data, *s; static const unsigned char fopen_responses[2] = { SSH_FXP_HANDLE, SSH_FXP_STATUS }; unsigned long request_id; + int rc; s = packet = LIBSSH2_ALLOC(session, packet_len); if (!packet) { @@ -603,7 +655,11 @@ } LIBSSH2_FREE(session, packet); - if (libssh2_sftp_packet_requirev(sftp, 2, fopen_responses, request_id, &data, &data_len)) { +#warning "XXX - Looping on PACKET_EAGAIN (blocking) until fix is migrated up farther" + while ((rc = libssh2_sftp_packet_requirev(sftp, 2, fopen_responses, request_id, &data, &data_len)) == PACKET_EAGAIN) { + ; + } + if (rc) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); return NULL; } @@ -670,6 +726,7 @@ size_t bytes_read = 0; size_t bytes_requested = 0; size_t total_read = 0; + int rc; _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Reading %lu bytes from SFTP handle", (unsigned long)buffer_maxlen); packet = LIBSSH2_ALLOC(session, packet_len); @@ -717,8 +774,11 @@ return -1; } - if (libssh2_sftp_packet_requirev(sftp, 2, read_responses, request_id, - &data, &data_len)) { +#warning "XXX - Looping on PACKET_EAGAIN (blocking) until fix is migrated up farther" + while ((rc = libssh2_sftp_packet_requirev(sftp, 2, read_responses, request_id, &data, &data_len)) == PACKET_EAGAIN) { + ; + } + if (rc) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); return -1; @@ -810,131 +870,203 @@ } /* }}} */ -/* {{{ libssh2_sftp_readdir - * Read from an SFTP directory handle +/* {{{ _libssh2_sftp_readdir + * Read from an SFTP directory handle blocking/non-blocking depending on state */ -LIBSSH2_API int libssh2_sftp_readdir(LIBSSH2_SFTP_HANDLE *handle, char *buffer, size_t buffer_maxlen, LIBSSH2_SFTP_ATTRIBUTES *attrs) +static int _libssh2_sftp_readdir(LIBSSH2_SFTP_HANDLE *handle, char *buffer, size_t buffer_maxlen, LIBSSH2_SFTP_ATTRIBUTES *attrs) { - LIBSSH2_SFTP *sftp = handle->sftp; - LIBSSH2_CHANNEL *channel = sftp->channel; - LIBSSH2_SESSION *session = channel->session; - LIBSSH2_SFTP_ATTRIBUTES attrs_dummy; - unsigned long data_len, request_id, filename_len, num_names; - ssize_t packet_len = handle->handle_len + 13; /* packet_len(4) + packet_type(1) + request_id(4) + handle_len(4) */ - unsigned char *packet, *s, *data; - static const unsigned char read_responses[2] = { SSH_FXP_NAME, SSH_FXP_STATUS }; - - if (handle->u.dir.names_left) { - /* A prior request returned more than one directory entry, feed it back from the buffer */ - unsigned char *s = (unsigned char *)handle->u.dir.next_name; - unsigned long real_filename_len = libssh2_ntohu32(s); - - filename_len = real_filename_len; s += 4; - if (filename_len > buffer_maxlen) { - filename_len = buffer_maxlen; - } - memcpy(buffer, s, filename_len); s += real_filename_len; - - /* The filename is not null terminated, make it so if possible */ - if (filename_len < buffer_maxlen) { - buffer[filename_len] = '\0'; - } - - /* Skip longname */ - s += 4 + libssh2_ntohu32(s); + LIBSSH2_SFTP *sftp = handle->sftp; + LIBSSH2_CHANNEL *channel = sftp->channel; + LIBSSH2_SESSION *session = channel->session; + LIBSSH2_SFTP_ATTRIBUTES attrs_dummy; + unsigned long data_len, request_id, filename_len, num_names; + ssize_t packet_len = handle->handle_len + 13; /* packet_len(4) + packet_type(1) + request_id(4) + handle_len(4) */ + unsigned char *packet, *s, *data; + unsigned char read_responses[2] = { SSH_FXP_NAME, SSH_FXP_STATUS }; + int rc; + + if (handle->u.dir.names_left) { + /* A prior request returned more than one directory entry, feed it back from the buffer */ + unsigned char *s = (unsigned char *)handle->u.dir.next_name; + unsigned long real_filename_len = libssh2_ntohu32(s); + + filename_len = real_filename_len; + s += 4; + if (filename_len > buffer_maxlen) { + filename_len = buffer_maxlen; + } + memcpy(buffer, s, filename_len); + s += real_filename_len; + + /* The filename is not null terminated, make it so if possible */ + if (filename_len < buffer_maxlen) { + buffer[filename_len] = '\0'; + } + + /* Skip longname */ + s += 4 + libssh2_ntohu32(s); + + if (attrs) { + memset(attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES)); + } + s += libssh2_sftp_bin2attr(attrs ? attrs : &attrs_dummy, s); + + handle->u.dir.next_name = (char *)s; + if ((--handle->u.dir.names_left) == 0) { + LIBSSH2_FREE(session, handle->u.dir.names_packet); + } - if (attrs) { - memset(attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES)); - } - s += libssh2_sftp_bin2attr(attrs ? attrs : &attrs_dummy, s); - - handle->u.dir.next_name = (char *)s; - if ((--handle->u.dir.names_left) == 0) { - LIBSSH2_FREE(session, handle->u.dir.names_packet); - } - - return filename_len; - } - - /* Request another entry(entries?) */ + return filename_len; + } - s = packet = LIBSSH2_ALLOC(session, packet_len); - if (!packet) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_READDIR packet", 0); - return -1; - } + /* Request another entry(entries?) */ - libssh2_htonu32(s, packet_len - 4); s += 4; - *(s++) = SSH_FXP_READDIR; - request_id = sftp->request_id++; - libssh2_htonu32(s, request_id); s += 4; - libssh2_htonu32(s, handle->handle_len); s += 4; - memcpy(s, handle->handle, handle->handle_len); s += handle->handle_len; + s = packet = LIBSSH2_ALLOC(session, packet_len); + if (!packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_READDIR packet", 0); + return -1; + } - _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Reading entries from directory handle"); - if (packet_len != libssh2_channel_write(channel, (char *)packet, - packet_len)) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_READ command", 0); - LIBSSH2_FREE(session, packet); - return -1; - } - LIBSSH2_FREE(session, packet); + libssh2_htonu32(s, packet_len - 4); + s += 4; + *(s++) = SSH_FXP_READDIR; + request_id = sftp->request_id++; + libssh2_htonu32(s, request_id); + s += 4; + libssh2_htonu32(s, handle->handle_len); + s += 4; + memcpy(s, handle->handle, handle->handle_len); + s += handle->handle_len; + + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Reading entries from directory handle"); + if (libssh2_channel_get_blocking(channel)) { + if (packet_len != libssh2_channel_write(channel, (char *)packet, packet_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_READ command", 0); + LIBSSH2_FREE(session, packet); + return -1; + } + } else { + if ((rc = libssh2_channel_writenb(channel, (char *)packet, packet_len)) == PACKET_EAGAIN) { + LIBSSH2_FREE(session, packet); + return rc; + } + if (packet_len != rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_READ command", 0); + LIBSSH2_FREE(session, packet); + return -1; + } + } + LIBSSH2_FREE(session, packet); - if (libssh2_sftp_packet_requirev(sftp, 2, read_responses, request_id, &data, &data_len)) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); - return -1; - } +#warning "XXX - Looping on PACKET_EAGAIN (blocking) until fix is migrated up farther" + while ((rc = libssh2_sftp_packet_requirev(sftp, 2, read_responses, request_id, &data, &data_len)) == PACKET_EAGAIN) { + ; + } + if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); + return -1; + } - if (data[0] == SSH_FXP_STATUS) { - int retcode; + if (data[0] == SSH_FXP_STATUS) { + int retcode; - retcode = libssh2_ntohu32(data + 5); - LIBSSH2_FREE(session, data); - if (retcode == LIBSSH2_FX_EOF) { - return 0; - } else { - sftp->last_errno = retcode; - libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "SFTP Protocol Error", 0); - return -1; - } - } + retcode = libssh2_ntohu32(data + 5); + LIBSSH2_FREE(session, data); + if (retcode == LIBSSH2_FX_EOF) { + return 0; + } else { + sftp->last_errno = retcode; + libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "SFTP Protocol Error", 0); + return -1; + } + } - num_names = libssh2_ntohu32(data + 5); - _libssh2_debug(session, LIBSSH2_DBG_SFTP, "%lu entries returned", num_names); - if (num_names <= 0) { - LIBSSH2_FREE(session, data); - return (num_names == 0) ? 0 : -1; - } + num_names = libssh2_ntohu32(data + 5); + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "%lu entries returned", num_names); + if (num_names <= 0) { + LIBSSH2_FREE(session, data); + return (num_names == 0) ? 0 : -1; + } - if (num_names == 1) { - unsigned long real_filename_len = libssh2_ntohu32(data + 9); + if (num_names == 1) { + unsigned long real_filename_len = libssh2_ntohu32(data + 9); - filename_len = real_filename_len; - if (filename_len > buffer_maxlen) { - filename_len = buffer_maxlen; - } - memcpy(buffer, data + 13, filename_len); + filename_len = real_filename_len; + if (filename_len > buffer_maxlen) { + filename_len = buffer_maxlen; + } + memcpy(buffer, data + 13, filename_len); + + /* The filename is not null terminated, make it so if possible */ + if (filename_len < buffer_maxlen) { + buffer[filename_len] = '\0'; + } + + if (attrs) { + memset(attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES)); + libssh2_sftp_bin2attr(attrs, data + 13 + real_filename_len + (4 + libssh2_ntohu32(data + 13 + real_filename_len))); + } + LIBSSH2_FREE(session, data); - /* The filename is not null terminated, make it so if possible */ - if (filename_len < buffer_maxlen) { - buffer[filename_len] = '\0'; - } + return filename_len; + } - if (attrs) { - memset(attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES)); - libssh2_sftp_bin2attr(attrs, data + 13 + real_filename_len + (4 + libssh2_ntohu32(data + 13 + real_filename_len))); - } - LIBSSH2_FREE(session, data); + handle->u.dir.names_left = num_names; + handle->u.dir.names_packet = data; + handle->u.dir.next_name = (char *)data + 9; - return filename_len; - } + /* Be lazy, just use the name popping mechanism from the start of the function */ + return libssh2_sftp_readdir(handle, buffer, buffer_maxlen, attrs); +} +/* }}} */ - handle->u.dir.names_left = num_names; - handle->u.dir.names_packet = data; - handle->u.dir.next_name = (char *)data + 9; +/* {{{ libssh2_sftp_readdir + * Read from an SFTP directory handle blocking + */ +LIBSSH2_API int libssh2_sftp_readdir(LIBSSH2_SFTP_HANDLE *handle, char *buffer, + size_t buffer_maxlen, LIBSSH2_SFTP_ATTRIBUTES *attrs) +{ + int rc; + LIBSSH2_CHANNEL *ch = handle->sftp->channel; + int bl = libssh2_channel_get_blocking(ch); + + /* set blocking */ + libssh2_channel_set_blocking(ch, 1); + + rc = _libssh2_sftp_readdir(handle, buffer, buffer_maxlen, attrs); + + /* restore state */ + libssh2_channel_set_blocking(ch, bl); + + if(rc < 0) { + /* precent accidental returning of other return codes since + this API does not support/provide those */ + return -1; + } + + return rc; +} +/* }}} */ - /* Be lazy, just use the name popping mechanism from the start of the function */ - return libssh2_sftp_readdir(handle, buffer, buffer_maxlen, attrs); +/* {{{ libssh2_sftp_readdirnb + * Read from an SFTP directory handle non-blocking + */ +LIBSSH2_API int libssh2_sftp_readdirnb(LIBSSH2_SFTP_HANDLE *handle, char *buffer, + size_t buffer_maxlen, LIBSSH2_SFTP_ATTRIBUTES *attrs) +{ + int rc; + LIBSSH2_CHANNEL *ch = handle->sftp->channel; + int bl = libssh2_channel_get_blocking(ch); + + /* set non-blocking */ + libssh2_channel_set_blocking(ch, 0); + + rc = _libssh2_sftp_readdir(handle, buffer, buffer_maxlen, attrs); + + /* restore state */ + libssh2_channel_set_blocking(ch, bl); + + return rc; } /* }}} */ @@ -942,56 +1074,80 @@ * Write data to a file handle */ static ssize_t _libssh2_sftp_write(LIBSSH2_SFTP_HANDLE *handle, - const char *buffer, - size_t count) + const char *buffer, + size_t count) { - LIBSSH2_SFTP *sftp = handle->sftp; - LIBSSH2_CHANNEL *channel = sftp->channel; - LIBSSH2_SESSION *session = channel->session; - unsigned long data_len, request_id, retcode; - ssize_t packet_len = handle->handle_len + count + 25; /* packet_len(4) + packet_type(1) + request_id(4) + handle_len(4) + offset(8) + count(4) */ - unsigned char *packet, *s, *data; - - _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Writing %lu bytes", (unsigned long)count); - s = packet = LIBSSH2_ALLOC(session, packet_len); - if (!packet) { - libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_WRITE packet", 0); - return -1; - } - - libssh2_htonu32(s, packet_len - 4); s += 4; - *(s++) = SSH_FXP_WRITE; - request_id = sftp->request_id++; - libssh2_htonu32(s, request_id); s += 4; - libssh2_htonu32(s, handle->handle_len); s += 4; - memcpy(s, handle->handle, handle->handle_len); s += handle->handle_len; - libssh2_htonu64(s, handle->u.file.offset); s += 8; - libssh2_htonu32(s, count); s += 4; - memcpy(s, buffer, count); s += count; - - if (packet_len != libssh2_channel_write(channel, (char *)packet, packet_len)) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_WRITE command", 0); - LIBSSH2_FREE(session, packet); - return -1; - } - LIBSSH2_FREE(session, packet); - - if (libssh2_sftp_packet_require(sftp, SSH_FXP_STATUS, request_id, &data, &data_len)) { - libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); - return -1; - } - - retcode = libssh2_ntohu32(data + 5); - LIBSSH2_FREE(session, data); - - if (retcode == LIBSSH2_FX_OK) { - handle->u.file.offset += count; - return count; - } - libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "SFTP Protocol Error", 0); - sftp->last_errno = retcode; - - return -1; + LIBSSH2_SFTP *sftp = handle->sftp; + LIBSSH2_CHANNEL *channel = sftp->channel; + LIBSSH2_SESSION *session = channel->session; + unsigned long data_len, request_id, retcode; + ssize_t packet_len = handle->handle_len + count + 25; /* packet_len(4) + packet_type(1) + request_id(4) + handle_len(4) + offset(8) + count(4) */ + unsigned char *packet, *s, *data; + int rc; + + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Writing %lu bytes", (unsigned long)count); + s = packet = LIBSSH2_ALLOC(session, packet_len); + if (!packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_WRITE packet", 0); + return -1; + } + + libssh2_htonu32(s, packet_len - 4); s += 4; + *(s++) = SSH_FXP_WRITE; + request_id = sftp->request_id++; + libssh2_htonu32(s, request_id); s += 4; + libssh2_htonu32(s, handle->handle_len); s += 4; + memcpy(s, handle->handle, handle->handle_len); s += handle->handle_len; + libssh2_htonu64(s, handle->u.file.offset); s += 8; + libssh2_htonu32(s, count); s += 4; + memcpy(s, buffer, count); s += count; + + if (libssh2_channel_get_blocking(channel)) { + if (packet_len != libssh2_channel_write(channel, (char *)packet, packet_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_READ command", 0); + LIBSSH2_FREE(session, packet); + return -1; + } + } else { + if ((rc = libssh2_channel_writenb(channel, (char *)packet, packet_len)) == PACKET_EAGAIN) { + LIBSSH2_FREE(session, packet); + return rc; + } + if (packet_len != rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_READ command", 0); + LIBSSH2_FREE(session, packet); + return -1; + } + } + LIBSSH2_FREE(session, packet); + + if (libssh2_channel_get_blocking(channel)) { + if (libssh2_sftp_packet_require(sftp, SSH_FXP_STATUS, request_id, &data, &data_len)) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); + return -1; + } + } else { +#warning "XXX - Looping on PACKET_EAGAIN (blocking) until fix is migrated up farther" + while ((rc = libssh2_sftp_packet_require(sftp, SSH_FXP_STATUS, request_id, &data, &data_len)) == PACKET_EAGAIN) { + ; + } + if (packet_len != rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); + return -1; + } + } + + retcode = libssh2_ntohu32(data + 5); + LIBSSH2_FREE(session, data); + + if (retcode == LIBSSH2_FX_OK) { + handle->u.file.offset += count; + return count; + } + libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "SFTP Protocol Error", 0); + sftp->last_errno = retcode; + + return -1; } /* }}} */ @@ -1052,6 +1208,7 @@ /* packet_len(4) + packet_type(1) + request_id(4) + handle_len(4) */ unsigned char *packet, *s, *data; static const unsigned char fstat_responses[2] = { SSH_FXP_ATTRS, SSH_FXP_STATUS }; + int rc; _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Issuing %s command", setstat ? "set-stat" : "stat"); s = packet = LIBSSH2_ALLOC(session, packet_len); @@ -1077,7 +1234,11 @@ } LIBSSH2_FREE(session, packet); - if (libssh2_sftp_packet_requirev(sftp, 2, fstat_responses, request_id, &data, &data_len)) { +#warning "XXX - Looping on PACKET_EAGAIN (blocking) until fix is migrated up farther" + while ((rc = libssh2_sftp_packet_requirev(sftp, 2, fstat_responses, request_id, &data, &data_len)) == PACKET_EAGAIN) { + ; + } + if (rc) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); return -1; } @@ -1133,6 +1294,7 @@ unsigned long data_len, retcode, request_id; ssize_t packet_len = handle->handle_len + 13; /* packet_len(4) + packet_type(1) + request_id(4) + handle_len(4) */ unsigned char *packet, *s, *data; + int rc; _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Closing handle"); s = packet = LIBSSH2_ALLOC(session, packet_len); @@ -1155,7 +1317,11 @@ } LIBSSH2_FREE(session, packet); - if (libssh2_sftp_packet_require(sftp, SSH_FXP_STATUS, request_id, &data, &data_len)) { +#warning "XXX - Looping on PACKET_EAGAIN (blocking) until fix is migrated up farther" + while ((rc = libssh2_sftp_packet_require(sftp, SSH_FXP_STATUS, request_id, &data, &data_len)) == PACKET_EAGAIN) { + ; + } + if (rc) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); return -1; } @@ -1202,6 +1368,7 @@ unsigned long data_len, retcode, request_id; ssize_t packet_len = filename_len + 13; /* packet_len(4) + packet_type(1) + request_id(4) + filename_len(4) */ unsigned char *packet, *s, *data; + int rc; _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Unlinking %s", filename); s = packet = LIBSSH2_ALLOC(session, packet_len); @@ -1225,7 +1392,11 @@ } LIBSSH2_FREE(session, packet); - if (libssh2_sftp_packet_require(sftp, SSH_FXP_STATUS, request_id, &data, &data_len)) { +#warning "XXX - Looping on PACKET_EAGAIN (blocking) until fix is migrated up farther" + while ((rc = libssh2_sftp_packet_require(sftp, SSH_FXP_STATUS, request_id, &data, &data_len)) == PACKET_EAGAIN) { + ; + } + if (rc) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); return -1; } @@ -1257,6 +1428,7 @@ /* packet_len(4) + packet_type(1) + request_id(4) + source_filename_len(4) + dest_filename_len(4) + flags(4){SFTP5+) */ unsigned char *packet, *s, *data; + int rc; if (sftp->version < 2) { libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "Server does not support RENAME", 0); @@ -1291,7 +1463,11 @@ } LIBSSH2_FREE(session, packet); - if (libssh2_sftp_packet_require(sftp, SSH_FXP_STATUS, request_id, &data, &data_len)) { +#warning "XXX - Looping on PACKET_EAGAIN (blocking) until fix is migrated up farther" + while ((rc = libssh2_sftp_packet_require(sftp, SSH_FXP_STATUS, request_id, &data, &data_len)) == PACKET_EAGAIN) { + ; + } + if (rc) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); return -1; } @@ -1337,6 +1513,7 @@ ssize_t packet_len = path_len + 13 + libssh2_sftp_attrsize(&attrs); /* packet_len(4) + packet_type(1) + request_id(4) + path_len(4) */ unsigned char *packet, *s, *data; + int rc; _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Creating directory %s with mode 0%lo", path, mode); s = packet = LIBSSH2_ALLOC(session, packet_len); @@ -1363,7 +1540,11 @@ } LIBSSH2_FREE(session, packet); - if (libssh2_sftp_packet_require(sftp, SSH_FXP_STATUS, request_id, &data, &data_len)) { +#warning "XXX - Looping on PACKET_EAGAIN (blocking) until fix is migrated up farther" + while ((rc = libssh2_sftp_packet_require(sftp, SSH_FXP_STATUS, request_id, &data, &data_len)) == PACKET_EAGAIN) { + ; + } + if (rc) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); return -1; } @@ -1391,6 +1572,7 @@ unsigned long data_len, retcode, request_id; ssize_t packet_len = path_len + 13; /* packet_len(4) + packet_type(1) + request_id(4) + path_len(4) */ unsigned char *packet, *s, *data; + int rc; _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Removing directory: %s", path); s = packet = LIBSSH2_ALLOC(session, packet_len); @@ -1413,7 +1595,11 @@ } LIBSSH2_FREE(session, packet); - if (libssh2_sftp_packet_require(sftp, SSH_FXP_STATUS, request_id, &data, &data_len)) { +#warning "XXX - Looping on PACKET_EAGAIN (blocking) until fix is migrated up farther" + while ((rc = libssh2_sftp_packet_require(sftp, SSH_FXP_STATUS, request_id, &data, &data_len)) == PACKET_EAGAIN) { + ; + } + if (rc) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); return -1; } @@ -1443,6 +1629,7 @@ /* packet_len(4) + packet_type(1) + request_id(4) + path_len(4) */ unsigned char *packet, *s, *data; static const unsigned char stat_responses[2] = { SSH_FXP_ATTRS, SSH_FXP_STATUS }; + int rc; _libssh2_debug(session, LIBSSH2_DBG_SFTP, "%s %s", (stat_type == LIBSSH2_SFTP_SETSTAT) ? "Set-statting" : (stat_type == LIBSSH2_SFTP_LSTAT ? "LStatting" : "Statting"), path); s = packet = LIBSSH2_ALLOC(session, packet_len); @@ -1479,7 +1666,11 @@ } LIBSSH2_FREE(session, packet); - if (libssh2_sftp_packet_requirev(sftp, 2, stat_responses, request_id, &data, &data_len)) { +#warning "XXX - Looping on PACKET_EAGAIN (blocking) until fix is migrated up farther" + while ((rc = libssh2_sftp_packet_requirev(sftp, 2, stat_responses, request_id, &data, &data_len)) == PACKET_EAGAIN) { + ; + } + if (rc) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); return -1; } @@ -1518,6 +1709,7 @@ /* packet_len(4) + packet_type(1) + request_id(4) + path_len(4) */ unsigned char *packet, *s, *data; static const unsigned char link_responses[2] = { SSH_FXP_NAME, SSH_FXP_STATUS }; + int rc; if ((sftp->version < 3) && (link_type != LIBSSH2_SFTP_REALPATH)) { @@ -1562,7 +1754,11 @@ } LIBSSH2_FREE(session, packet); - if (libssh2_sftp_packet_requirev(sftp, 2, link_responses, request_id, &data, &data_len)) { +#warning "XXX - Looping on PACKET_EAGAIN (blocking) until fix is migrated up farther" + while ((rc = libssh2_sftp_packet_requirev(sftp, 2, link_responses, request_id, &data, &data_len)) == PACKET_EAGAIN) { + ; + } + if (rc) { libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); return -1; } @@ -1607,3 +1803,22 @@ return sftp->last_errno; } /* }}} */ + +/* {{{ libssh2_sftp_set_blocking + * Set a channel's blocking mode on or off, this is an accessor + * to the channel through the SFTP session handle + */ +LIBSSH2_API void libssh2_sftp_set_blocking(LIBSSH2_SFTP *session, int blocking) { + libssh2_channel_set_blocking(session->channel, blocking); +} +/* }}} */ + +/* {{{ libssh2_sftp_get_blocking + * Returns a channel's blocking mode on or off, this is an accessor + * to the channel through the SFTP session handle + */ +LIBSSH2_API int libssh2_sftp_get_blocking(LIBSSH2_SFTP *session) { + return libssh2_channel_get_blocking(session->channel); +} +/* }}} */ +