From b89dcf4d92d988fe9d8484f0eeb6c1fe3bfd5a2c Mon Sep 17 00:00:00 2001
From: Daniel Stenberg <daniel@haxx.se>
Date: Tue, 7 Feb 2012 00:35:51 +0100
Subject: [PATCH 2/2] 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.
---
 src/sftp.c |   37 ++++++++++++++++++++-----------------
 1 files changed, 20 insertions(+), 17 deletions(-)

diff --git a/src/sftp.c b/src/sftp.c
index 0a1cf61..4300d2e 100644
--- a/src/sftp.c
+++ b/src/sftp.c
@@ -1617,7 +1617,6 @@ 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;
 
     /* Number of bytes sent off that haven't been acked and therefor we will
        get passed in here again.
@@ -1638,6 +1637,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;
 
+    if(sftp->write_state == libssh2_NB_state_sent)
+        goto sftp_write_fxp_status;
+
+    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 */
@@ -1687,13 +1690,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 */
-                    return rc;
-                eagain++;
-                break;
-            }
+            if(rc < 0)
+                /* remain in idle state */
+                return rc;
 
             /* remember where to continue sending the next time */
             chunk->lefttosend -= rc;
@@ -1708,6 +1707,9 @@ static ssize_t sftp_write(LIBSSH2_SFTP_HANDLE *handle, const char *buffer,
         chunk = _libssh2_list_next(&chunk->node);
     }
 
+  sftp_write_fxp_status:
+
+    sftp->write_state = libssh2_NB_state_idle;
     /*
      * Count all ACKed packets
      */
@@ -1719,16 +1721,19 @@ static ssize_t sftp_write(LIBSSH2_SFTP_HANDLE *handle, const char *buffer,
                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) {
+            sftp->write_state = libssh2_NB_state_sent;
+            return rc;
         }
+
         retcode = _libssh2_ntohu32(data + 5);
         LIBSSH2_FREE(session, data);
 
@@ -1787,9 +1792,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.9

