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:
parent
4774d500e7
commit
e07342443f
61
src/sftp.c
61
src/sftp.c
@ -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! */
|
||||
}
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user