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;
|
struct sftp_pipeline_chunk *next;
|
||||||
size_t acked = 0;
|
size_t acked = 0;
|
||||||
size_t org_count = count;
|
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
|
switch(sftp->write_state) {
|
||||||
get passed in here again.
|
default:
|
||||||
|
case libssh2_NB_state_idle:
|
||||||
|
|
||||||
Also, add up the number of bytes that actually already have been acked
|
/* Number of bytes sent off that haven't been acked and therefor we
|
||||||
but we haven't been able to return as such yet, so we will get that
|
will get passed in here again.
|
||||||
data as well 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;
|
handle->u.file.acked;
|
||||||
|
|
||||||
if(count >= already) {
|
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 */
|
/* there is more data already fine than what we got in this call */
|
||||||
count = 0;
|
count = 0;
|
||||||
|
|
||||||
|
sftp->write_state = libssh2_NB_state_idle;
|
||||||
while(count) {
|
while(count) {
|
||||||
/* TODO: Possibly this should have some logic to prevent a very very
|
/* TODO: Possibly this should have some logic to prevent a very
|
||||||
small fraction to be left but lets ignore that for now */
|
very small fraction to be left but lets ignore that for now */
|
||||||
uint32_t size = MIN(MAX_SFTP_OUTGOING_SIZE, count);
|
uint32_t size = MIN(MAX_SFTP_OUTGOING_SIZE, count);
|
||||||
uint32_t request_id;
|
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,
|
rc = _libssh2_channel_write(channel, 0,
|
||||||
&chunk->packet[chunk->sent],
|
&chunk->packet[chunk->sent],
|
||||||
chunk->lefttosend);
|
chunk->lefttosend);
|
||||||
if(rc < 0) {
|
if(rc < 0)
|
||||||
if(rc != LIBSSH2_ERROR_EAGAIN)
|
/* remain in idle state */
|
||||||
/* error */
|
|
||||||
return rc;
|
return rc;
|
||||||
eagain++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* remember where to continue sending the next time */
|
/* remember where to continue sending the next time */
|
||||||
chunk->lefttosend -= rc;
|
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);
|
chunk = _libssh2_list_next(&chunk->node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* fall-through */
|
||||||
|
case libssh2_NB_state_sent:
|
||||||
|
|
||||||
|
sftp->write_state = libssh2_NB_state_idle;
|
||||||
/*
|
/*
|
||||||
* Count all ACKed packets
|
* Count all ACKed packets
|
||||||
*/
|
*/
|
||||||
@ -1741,20 +1746,24 @@ static ssize_t sftp_write(LIBSSH2_SFTP_HANDLE *handle, const char *buffer,
|
|||||||
|
|
||||||
while(chunk) {
|
while(chunk) {
|
||||||
if(chunk->lefttosend)
|
if(chunk->lefttosend)
|
||||||
/* if the chunk still has data left to send, we shouldn't wait for
|
/* if the chunk still has data left to send, we shouldn't wait
|
||||||
an ACK for it just yet */
|
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;
|
break;
|
||||||
|
|
||||||
/* we check the packets in order */
|
/* we check the packets in order */
|
||||||
rc = sftp_packet_require(sftp, SSH_FXP_STATUS,
|
rc = sftp_packet_require(sftp, SSH_FXP_STATUS,
|
||||||
chunk->request_id, &data, &data_len);
|
chunk->request_id, &data, &data_len);
|
||||||
if (rc == LIBSSH2_ERROR_EAGAIN) {
|
if (rc < 0) {
|
||||||
eagain++;
|
if (rc == LIBSSH2_ERROR_EAGAIN)
|
||||||
break;
|
sftp->write_state = libssh2_NB_state_sent;
|
||||||
}
|
return rc;
|
||||||
else if (rc) {
|
|
||||||
return _libssh2_error(session, rc, "Waiting for SFTP status");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
retcode = _libssh2_ntohu32(data + 5);
|
retcode = _libssh2_ntohu32(data + 5);
|
||||||
LIBSSH2_FREE(session, data);
|
LIBSSH2_FREE(session, data);
|
||||||
|
|
||||||
@ -1795,6 +1804,8 @@ static ssize_t sftp_write(LIBSSH2_SFTP_HANDLE *handle, const char *buffer,
|
|||||||
"FXP write failed");
|
"FXP write failed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
/* if there were acked data in a previous call that wasn't returned then,
|
/* 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
|
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;
|
return ret;
|
||||||
}
|
}
|
||||||
else if(eagain)
|
|
||||||
return _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
|
|
||||||
"Would block sftp_write");
|
|
||||||
else
|
else
|
||||||
return 0; /* nothing was acked, and no EAGAIN was received! */
|
return 0; /* nothing was acked, and no EAGAIN was received! */
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#ifndef _LIBSSH2_SFTP_H
|
#ifndef _LIBSSH2_SFTP_H
|
||||||
#define _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>
|
* Author: Daniel Stenberg <daniel@haxx.se>
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms,
|
* Redistribution and use in source and binary forms,
|
||||||
@ -158,9 +158,12 @@ struct _LIBSSH2_SFTP
|
|||||||
size_t open_packet_sent;
|
size_t open_packet_sent;
|
||||||
uint32_t open_request_id;
|
uint32_t open_request_id;
|
||||||
|
|
||||||
/* State variables used in libssh2_sftp_read() */
|
/* State variable used in sftp_read() */
|
||||||
libssh2_nonblocking_states read_state;
|
libssh2_nonblocking_states read_state;
|
||||||
|
|
||||||
|
/* State variable used in sftp_write() */
|
||||||
|
libssh2_nonblocking_states write_state;
|
||||||
|
|
||||||
/* State variables used in libssh2_sftp_readdir() */
|
/* State variables used in libssh2_sftp_readdir() */
|
||||||
libssh2_nonblocking_states readdir_state;
|
libssh2_nonblocking_states readdir_state;
|
||||||
unsigned char *readdir_packet;
|
unsigned char *readdir_packet;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user