sftp_write: cannot return acked data *and* EAGAIN

Whenever we have acked data and is about to call a function that *MAY*
return EAGAIN we must return the number now and wait to get called
again. Our API only allows data *or* EAGAIN and we must never try to get
both.
This commit is contained in:
Daniel Stenberg 2012-02-07 00:35:51 +01:00
parent 4774d500e7
commit e07342443f
2 changed files with 148 additions and 136 deletions

View File

@ -1643,16 +1643,20 @@ static ssize_t sftp_write(LIBSSH2_SFTP_HANDLE *handle, const char *buffer,
struct sftp_pipeline_chunk *next;
size_t acked = 0;
size_t org_count = count;
size_t eagain = 0;
size_t already;
/* Number of bytes sent off that haven't been acked and therefor we will
get passed in here again.
switch(sftp->write_state) {
default:
case libssh2_NB_state_idle:
Also, add up the number of bytes that actually already have been acked
but we haven't been able to return as such yet, so we will get that
data as well passed in here again.
/* Number of bytes sent off that haven't been acked and therefor we
will get passed in here again.
Also, add up the number of bytes that actually already have been
acked but we haven't been able to return as such yet, so we will
get that data as well passed in here again.
*/
size_t already = (handle->u.file.offset_sent - handle->u.file.offset)+
already = (handle->u.file.offset_sent - handle->u.file.offset)+
handle->u.file.acked;
if(count >= already) {
@ -1664,9 +1668,10 @@ static ssize_t sftp_write(LIBSSH2_SFTP_HANDLE *handle, const char *buffer,
/* there is more data already fine than what we got in this call */
count = 0;
sftp->write_state = libssh2_NB_state_idle;
while(count) {
/* TODO: Possibly this should have some logic to prevent a very very
small fraction to be left but lets ignore that for now */
/* TODO: Possibly this should have some logic to prevent a very
very small fraction to be left but lets ignore that for now */
uint32_t size = MIN(MAX_SFTP_OUTGOING_SIZE, count);
uint32_t request_id;
@ -1713,13 +1718,9 @@ static ssize_t sftp_write(LIBSSH2_SFTP_HANDLE *handle, const char *buffer,
rc = _libssh2_channel_write(channel, 0,
&chunk->packet[chunk->sent],
chunk->lefttosend);
if(rc < 0) {
if(rc != LIBSSH2_ERROR_EAGAIN)
/* error */
if(rc < 0)
/* remain in idle state */
return rc;
eagain++;
break;
}
/* remember where to continue sending the next time */
chunk->lefttosend -= rc;
@ -1734,6 +1735,10 @@ static ssize_t sftp_write(LIBSSH2_SFTP_HANDLE *handle, const char *buffer,
chunk = _libssh2_list_next(&chunk->node);
}
/* fall-through */
case libssh2_NB_state_sent:
sftp->write_state = libssh2_NB_state_idle;
/*
* Count all ACKed packets
*/
@ -1741,20 +1746,24 @@ static ssize_t sftp_write(LIBSSH2_SFTP_HANDLE *handle, const char *buffer,
while(chunk) {
if(chunk->lefttosend)
/* if the chunk still has data left to send, we shouldn't wait for
an ACK for it just yet */
/* if the chunk still has data left to send, we shouldn't wait
for an ACK for it just yet */
break;
else if(acked)
/* if we have sent data that is acked, we must return that
info before we call a function that might return EAGAIN */
break;
/* we check the packets in order */
rc = sftp_packet_require(sftp, SSH_FXP_STATUS,
chunk->request_id, &data, &data_len);
if (rc == LIBSSH2_ERROR_EAGAIN) {
eagain++;
break;
}
else if (rc) {
return _libssh2_error(session, rc, "Waiting for SFTP status");
if (rc < 0) {
if (rc == LIBSSH2_ERROR_EAGAIN)
sftp->write_state = libssh2_NB_state_sent;
return rc;
}
retcode = _libssh2_ntohu32(data + 5);
LIBSSH2_FREE(session, data);
@ -1795,6 +1804,8 @@ static ssize_t sftp_write(LIBSSH2_SFTP_HANDLE *handle, const char *buffer,
"FXP write failed");
}
}
break;
}
/* if there were acked data in a previous call that wasn't returned then,
add that up and try to return it all now. This can happen if the app
@ -1813,9 +1824,7 @@ static ssize_t sftp_write(LIBSSH2_SFTP_HANDLE *handle, const char *buffer,
return ret;
}
else if(eagain)
return _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
"Would block sftp_write");
else
return 0; /* nothing was acked, and no EAGAIN was received! */
}

View File

@ -1,7 +1,7 @@
#ifndef _LIBSSH2_SFTP_H
#define _LIBSSH2_SFTP_H
/*
* Copyright (C) 2010, 2011 by Daniel Stenberg
* Copyright (C) 2010 - 2012 by Daniel Stenberg
* Author: Daniel Stenberg <daniel@haxx.se>
*
* Redistribution and use in source and binary forms,
@ -158,9 +158,12 @@ struct _LIBSSH2_SFTP
size_t open_packet_sent;
uint32_t open_request_id;
/* State variables used in libssh2_sftp_read() */
/* State variable used in sftp_read() */
libssh2_nonblocking_states read_state;
/* State variable used in sftp_write() */
libssh2_nonblocking_states write_state;
/* State variables used in libssh2_sftp_readdir() */
libssh2_nonblocking_states readdir_state;
unsigned char *readdir_packet;