Make _libssh2_sftp_write() fully non-blocking safe. This makes

libssh2_sftp_writenb() non-blocking safe and libssh2_sftp_write() blocking
safe
This commit is contained in:
James Housley 2007-04-22 15:56:06 +00:00
parent d79939fc3a
commit 7eccfc7fbc

View File

@ -87,6 +87,12 @@ typedef enum {
sftp_readdir_packet_sent sftp_readdir_packet_sent
} libssh2_sftp_readdir_state; } libssh2_sftp_readdir_state;
typedef enum {
sftp_write_idle = 0,
sftp_write_packet_created,
sftp_write_packet_sent
} libssh2_sftp_write_state;
struct _LIBSSH2_SFTP { struct _LIBSSH2_SFTP {
LIBSSH2_CHANNEL *channel; LIBSSH2_CHANNEL *channel;
@ -116,6 +122,12 @@ struct _LIBSSH2_SFTP {
libssh2_sftp_readdir_state readdir_state; libssh2_sftp_readdir_state readdir_state;
unsigned char *readdir_packet; unsigned char *readdir_packet;
unsigned long readdir_request_id; unsigned long readdir_request_id;
/* State variables used in _libssh2_sftp_write() */
libssh2_sftp_write_state write_state;
unsigned char *write_packet;
unsigned long write_request_id;
}; };
#define LIBSSH2_SFTP_HANDLE_FILE 0 #define LIBSSH2_SFTP_HANDLE_FILE 0
@ -392,7 +404,7 @@ static int libssh2_sftp_packet_requirev(LIBSSH2_SFTP *sftp, int num_valid_respon
sftp->requirev_start = time(NULL); sftp->requirev_start = time(NULL);
/* Flush */ /* Flush */
int bl = libssh2_channel_get_blocking(sftp->channel); bl = libssh2_channel_get_blocking(sftp->channel);
while (libssh2_sftp_packet_read(sftp, 0, 1) > 0); while (libssh2_sftp_packet_read(sftp, 0, 1) > 0);
libssh2_channel_set_blocking(sftp->channel, bl); libssh2_channel_set_blocking(sftp->channel, bl);
} }
@ -1236,7 +1248,7 @@ LIBSSH2_API int libssh2_sftp_readdirnb(LIBSSH2_SFTP_HANDLE *handle, char *buffer
/* {{{ _libssh2_sftp_write /* {{{ _libssh2_sftp_write
* Write data to a file handle * Write data to a file handle
*/ */
/* _libssh2_sftp_write - NB-UNSAFE?? */ /* _libssh2_sftp_write - NB-SAFE */
static ssize_t _libssh2_sftp_write(LIBSSH2_SFTP_HANDLE *handle, const char *buffer, size_t count) static ssize_t _libssh2_sftp_write(LIBSSH2_SFTP_HANDLE *handle, const char *buffer, size_t count)
{ {
LIBSSH2_SFTP *sftp = handle->sftp; LIBSSH2_SFTP *sftp = handle->sftp;
@ -1248,6 +1260,7 @@ static ssize_t _libssh2_sftp_write(LIBSSH2_SFTP_HANDLE *handle, const char *buff
unsigned char *packet, *s, *data; unsigned char *packet, *s, *data;
int rc; int rc;
if (sftp->write_state == sftp_write_idle) {
_libssh2_debug(session, LIBSSH2_DBG_SFTP, "Writing %lu bytes", (unsigned long)count); _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Writing %lu bytes", (unsigned long)count);
s = packet = LIBSSH2_ALLOC(session, packet_len); s = packet = LIBSSH2_ALLOC(session, packet_len);
if (!packet) { if (!packet) {
@ -1265,40 +1278,49 @@ static ssize_t _libssh2_sftp_write(LIBSSH2_SFTP_HANDLE *handle, const char *buff
libssh2_htonu32(s, count); s += 4; libssh2_htonu32(s, count); s += 4;
memcpy(s, buffer, count); s += count; memcpy(s, buffer, count); s += count;
sftp->write_state = sftp_write_packet_created;
} else {
packet = sftp->write_packet;
request_id = sftp->write_request_id;
}
if (sftp->write_state != sftp_write_packet_sent) {
if (libssh2_channel_get_blocking(channel)) { if (libssh2_channel_get_blocking(channel)) {
if (packet_len != libssh2_channel_write(channel, (char *)packet, packet_len)) { 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_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_READ command", 0);
LIBSSH2_FREE(session, packet); LIBSSH2_FREE(session, packet);
sftp->write_state = sftp_write_idle;
return -1; return -1;
} }
} else { } else {
if ((rc = libssh2_channel_writenb(channel, (char *)packet, packet_len)) == PACKET_EAGAIN) { if ((rc = libssh2_channel_writenb(channel, (char *)packet, packet_len)) == PACKET_EAGAIN) {
LIBSSH2_FREE(session, packet); sftp->write_packet = packet;
sftp->write_request_id = request_id;
return PACKET_EAGAIN; return PACKET_EAGAIN;
} }
if (packet_len != rc) { if (packet_len != rc) {
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_READ command", 0); libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_READ command", 0);
LIBSSH2_FREE(session, packet); LIBSSH2_FREE(session, packet);
sftp->write_state = sftp_write_idle;
return -1; return -1;
} }
} }
LIBSSH2_FREE(session, packet); LIBSSH2_FREE(session, packet);
sftp->write_state = sftp_write_packet_sent;
sftp->write_packet = NULL;
}
if (libssh2_channel_get_blocking(channel)) { rc = libssh2_sftp_packet_require(sftp, SSH_FXP_STATUS, request_id, &data, &data_len);
if (libssh2_sftp_packet_require(sftp, SSH_FXP_STATUS, request_id, &data, &data_len)) { if (rc == PACKET_EAGAIN) {
return PACKET_EAGAIN;
}
else if (rc) {
libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0); libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for status message", 0);
sftp->write_state = sftp_write_idle;
return -1; return -1;
} }
} else {
/* #warning "XXX - Looping on PACKET_EAGAIN (blocking) until fix is migrated up farther" */ sftp->write_state = sftp_write_idle;
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); retcode = libssh2_ntohu32(data + 5);
LIBSSH2_FREE(session, data); LIBSSH2_FREE(session, data);
@ -1340,7 +1362,7 @@ LIBSSH2_API ssize_t libssh2_sftp_write(LIBSSH2_SFTP_HANDLE *handle,
/* {{{ libssh2_sftp_write /* {{{ libssh2_sftp_write
* Write data to a SFTP handle non-blocking * Write data to a SFTP handle non-blocking
*/ */
/* libssh2_sftp_writenb - NB-UNSAFE?? */ /* libssh2_sftp_writenb - NB-SAFE */
LIBSSH2_API ssize_t libssh2_sftp_writenb(LIBSSH2_SFTP_HANDLE *handle, LIBSSH2_API ssize_t libssh2_sftp_writenb(LIBSSH2_SFTP_HANDLE *handle,
const char *buffer, size_t count) const char *buffer, size_t count)
{ {