Get QEMUFile implementation to upstream level.

Change-Id: I0c5003876c7df0246118cb903cf6b834fac82687
diff --git a/Makefile.common b/Makefile.common
index 46e4230..b4488f1 100644
--- a/Makefile.common
+++ b/Makefile.common
@@ -484,6 +484,7 @@
     util/qemu-option.c \
     util/qemu-sockets-android.c \
     util/unicode.c \
+    util/yield-android.c \
 
 ifeq ($(HOST_ARCH),x86)
     CORE_MISC_SOURCES += disas/i386.c
diff --git a/android/boot-properties.c b/android/boot-properties.c
index acdeed9..c2b30b8 100644
--- a/android/boot-properties.c
+++ b/android/boot-properties.c
@@ -175,7 +175,7 @@
     if (split == NULL) {
         D("%s: save failed: illegal key/value pair \"%s\" (missing '=')\n",
           __FUNCTION__, p->property);
-        qemu_file_set_error(f);
+        qemu_file_set_error(f, -EINVAL);
         return -1;
     }
 
diff --git a/arch_init.c b/arch_init.c
index fddf4e3..426f7bc 100644
--- a/arch_init.c
+++ b/arch_init.c
@@ -263,7 +263,7 @@
     }
 
     if (cpu_physical_sync_dirty_bitmap(0, TARGET_PHYS_ADDR_MAX) != 0) {
-        qemu_file_set_error(f);
+        qemu_file_set_error(f, -errno);
         return 0;
     }
 
@@ -449,7 +449,7 @@
 
             qemu_get_buffer(f, host, TARGET_PAGE_SIZE);
         }
-        if (qemu_file_has_error(f)) {
+        if (qemu_file_get_error(f)) {
             return -EIO;
         }
     } while (!(flags & RAM_SAVE_FLAG_EOS));
diff --git a/hw/android/goldfish/nand.c b/hw/android/goldfish/nand.c
index 1c568e9..072b504 100644
--- a/hw/android/goldfish/nand.c
+++ b/hw/android/goldfish/nand.c
@@ -219,8 +219,8 @@
 
     lseek_ret = do_lseek(dev->fd, 0, SEEK_END);
     if (lseek_ret == -1) {
+      qemu_file_set_error(f, -errno);
       XLOG("%s EOF seek failed: %s\n", __FUNCTION__, strerror(errno));
-      qemu_file_set_error(f);
       return;
     }
     const uint64_t total_size = lseek_ret;
@@ -229,15 +229,15 @@
     /* copy all data from the stream to the stored image */
     lseek_ret = do_lseek(dev->fd, 0, SEEK_SET);
     if (lseek_ret == -1) {
+        qemu_file_set_error(f, -errno);
         XLOG("%s seek failed: %s\n", __FUNCTION__, strerror(errno));
-        qemu_file_set_error(f);
         return;
     }
     do {
         ret = do_read(dev->fd, buffer, buf_size);
         if (ret < 0) {
+            qemu_file_set_error(f, -errno);
             XLOG("%s read failed: %s\n", __FUNCTION__, strerror(errno));
-            qemu_file_set_error(f);
             return;
         }
         qemu_put_buffer(f, buffer, ret);
diff --git a/include/migration/qemu-file.h b/include/migration/qemu-file.h
index d15f8df..56738c7 100644
--- a/include/migration/qemu-file.h
+++ b/include/migration/qemu-file.h
@@ -132,7 +132,7 @@
 void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size);
 void qemu_put_byte(QEMUFile *f, int v);
 
-void qemu_file_set_error(QEMUFile* f);
+void qemu_file_set_error(QEMUFile* f, int error);
 int qemu_file_has_error(QEMUFile* f);
 void qemu_file_put_notify(QEMUFile* f);
 
@@ -176,7 +176,7 @@
 #endif
 int qemu_file_rate_limit(QEMUFile *f);
 void qemu_file_reset_rate_limit(QEMUFile *f);
-int64_t qemu_file_set_rate_limit(QEMUFile *f, int64_t new_rate);
+void qemu_file_set_rate_limit(QEMUFile *f, int64_t new_rate);
 int64_t qemu_file_get_rate_limit(QEMUFile *f);
 int qemu_file_get_error(QEMUFile *f);
 void qemu_fflush(QEMUFile *f);
diff --git a/include/qemu/sockets.h b/include/qemu/sockets.h
index 27a6f64..0639464 100644
--- a/include/qemu/sockets.h
+++ b/include/qemu/sockets.h
@@ -27,4 +27,6 @@
 int parse_host_src_port(SockAddress*  haddr, SockAddress*  saddr,
                         const char *str);
 
+void qemu_set_block(int fd);
+
 #endif /* QEMU__SOCKET_H */
diff --git a/migration-exec.c b/migration-exec.c
index fbbad71..2878e2b 100644
--- a/migration-exec.c
+++ b/migration-exec.c
@@ -68,29 +68,15 @@
                                              int detach)
 {
     FdMigrationState *s;
-    FILE *f;
 
     s = g_malloc0(sizeof(*s));
 
-    f = popen(command, "w");
-    if (f == NULL) {
-        DPRINTF("Unable to popen exec target\n");
-        goto err_after_alloc;
+    s->opaque = qemu_popen_cmd(command, "w");
+    if (!s->opaque) {
+        g_free(s);
+        return NULL;
     }
 
-    s->fd = fileno(f);
-    if (s->fd == -1) {
-        DPRINTF("Unable to retrieve file descriptor for popen'd handle\n");
-        goto err_after_open;
-    }
-
-    if (fcntl(s->fd, F_SETFD, O_NONBLOCK) == -1) {
-        DPRINTF("Unable to set nonblocking mode on file descriptor\n");
-        goto err_after_open;
-    }
-
-    s->opaque = qemu_popen(f, "w");
-
     s->close = exec_close;
     s->get_error = file_errno;
     s->write = file_write;
@@ -107,12 +93,6 @@
 
     migrate_fd_connect(s);
     return &s->mig_state;
-
-err_after_open:
-    pclose(f);
-err_after_alloc:
-    g_free(s);
-    return NULL;
 }
 
 static void exec_accept_incoming_migration(void *opaque)
diff --git a/migration.c b/migration.c
index cdcdd12..bd09e0a 100644
--- a/migration.c
+++ b/migration.c
@@ -206,7 +206,7 @@
     FdMigrationState *s = opaque;
 
     qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL);
-    qemu_file_put_notify(s->file);
+    //qemu_file_put_notify(s->file);
 }
 
 ssize_t migrate_fd_put_buffer(void *opaque, const void *data, size_t size)
diff --git a/savevm.c b/savevm.c
index e344fe8..005b0c3 100644
--- a/savevm.c
+++ b/savevm.c
@@ -75,6 +75,7 @@
 #include "net/net.h"
 #include "monitor/monitor.h"
 #include "sysemu/sysemu.h"
+#include "qemu/iov.h"
 #include "qemu/timer.h"
 #include "sysemu/char.h"
 #include "sysemu/blockdev.h"
@@ -160,19 +161,25 @@
 /* savevm/loadvm support */
 
 #define IO_BUF_SIZE 32768
+#define MAX_IOV_SIZE MIN(IOV_MAX, 64)
 
 struct QEMUFile {
-    const QEMUFileOps* ops;
+    const QEMUFileOps *ops;
     void *opaque;
-    int is_write;
 
-    int64_t buf_offset; /* start of buffer when writing, end of buffer
-                           when reading */
+    int64_t bytes_xfer;
+    int64_t xfer_limit;
+
+    int64_t pos; /* start of buffer when writing, end of buffer
+                    when reading */
     int buf_index;
     int buf_size; /* 0 when writing */
     uint8_t buf[IO_BUF_SIZE];
 
-    int has_error;
+    struct iovec iov[MAX_IOV_SIZE];
+    unsigned int iovcnt;
+
+    int last_error;
 };
 
 typedef struct QEMUFileStdio
@@ -187,6 +194,20 @@
     QEMUFile *file;
 } QEMUFileSocket;
 
+static ssize_t socket_writev_buffer(void *opaque, struct iovec *iov, int iovcnt,
+                                    int64_t pos)
+{
+    QEMUFileSocket *s = opaque;
+    ssize_t len;
+    ssize_t size = iov_size(iov, iovcnt);
+
+    len = iov_send(s->fd, iov, iovcnt, 0, size);
+    if (len < size) {
+        len = -socket_error();
+    }
+    return len;
+}
+
 static int socket_get_fd(void *opaque)
 {
     QEMUFileSocket *s = opaque;
@@ -199,13 +220,26 @@
     QEMUFileSocket *s = opaque;
     ssize_t len;
 
-    do {
-        len = recv(s->fd, (void *)buf, size, 0);
-    } while (len == -1 && socket_error() == EINTR);
+    for (;;) {
+        len = qemu_recv(s->fd, buf, size, 0);
+        if (len != -1) {
+            break;
+        }
+#ifndef CONFIG_ANDROID
+        if (socket_error() == EAGAIN) {
+            yield_until_fd_readable(s->fd);
+        } else if (socket_error() != EINTR) {
+            break;
+        }
+#else
+       if (socket_error() != EINTR)
+           break;
+#endif
+    }
 
-    if (len == -1)
+    if (len == -1) {
         len = -socket_error();
-
+    }
     return len;
 }
 
@@ -218,16 +252,16 @@
     return 0;
 }
 
-static int stdio_get_fd(void* opaque)
+static int stdio_get_fd(void *opaque)
 {
     QEMUFileStdio *s = opaque;
+
     return fileno(s->stdio_file);
 }
 
 static int stdio_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, int size)
 {
     QEMUFileStdio *s = opaque;
-    fseek(s->stdio_file, pos, SEEK_SET);
     return fwrite(buf, 1, size, s->stdio_file);
 }
 
@@ -237,11 +271,23 @@
     FILE *fp = s->stdio_file;
     int bytes;
 
-    fseek(s->stdio_file, pos, SEEK_SET);
-    do {
+    for (;;) {
         clearerr(fp);
         bytes = fread(buf, 1, size, fp);
-    } while ((bytes == 0) && ferror(fp) && (errno == EINTR));
+        if (bytes != 0 || !ferror(fp)) {
+            break;
+        }
+#ifndef CONFIG_ANDROID
+        if (errno == EAGAIN) {
+            yield_until_fd_readable(fileno(fp));
+        } else if (errno != EINTR) {
+            break;
+        }
+#else
+        if (errno != EINTR)
+            break;
+#endif
+    }
     return bytes;
 }
 
@@ -250,6 +296,12 @@
     QEMUFileStdio *s = opaque;
     int ret;
     ret = pclose(s->stdio_file);
+    if (ret == -1) {
+        ret = -errno;
+    } else if (!WIFEXITED(ret) || WEXITSTATUS(ret) != 0) {
+        /* close succeeded, but non-zero exit code: */
+        ret = -EIO; /* fake errno value */
+    }
     g_free(s);
     return ret;
 }
@@ -257,9 +309,30 @@
 static int stdio_fclose(void *opaque)
 {
     QEMUFileStdio *s = opaque;
-    fclose(s->stdio_file);
+    int ret = 0;
+
+    if (s->file->ops->put_buffer || s->file->ops->writev_buffer) {
+        int fd = fileno(s->stdio_file);
+        struct stat st;
+
+        ret = fstat(fd, &st);
+        if (ret == 0 && S_ISREG(st.st_mode)) {
+            /*
+             * If the file handle is a regular file make sure the
+             * data is flushed to disk before signaling success.
+             */
+            ret = fsync(fd);
+            if (ret != 0) {
+                ret = -errno;
+                return ret;
+            }
+        }
+    }
+    if (fclose(s->stdio_file) == EOF) {
+        ret = -errno;
+    }
     g_free(s);
-    return 0;
+    return ret;
 }
 
 static const QEMUFileOps stdio_pipe_read_ops = {
@@ -274,15 +347,21 @@
     .close =      stdio_pclose
 };
 
-QEMUFile *qemu_popen(FILE *stdio_file, const char *mode)
+QEMUFile *qemu_popen_cmd(const char *command, const char *mode)
 {
+    FILE *stdio_file;
     QEMUFileStdio *s;
 
-    if (stdio_file == NULL || mode == NULL || (mode[0] != 'r' && mode[0] != 'w') || mode[1] != 0) {
+    if (mode == NULL || (mode[0] != 'r' && mode[0] != 'w') || mode[1] != 0) {
         fprintf(stderr, "qemu_popen: Argument validity check failed\n");
         return NULL;
     }
 
+    stdio_file = popen(command, mode);
+    if (stdio_file == NULL) {
+        return NULL;
+    }
+
     s = g_malloc0(sizeof(QEMUFileStdio));
 
     s->stdio_file = stdio_file;
@@ -295,26 +374,6 @@
     return s->file;
 }
 
-QEMUFile *qemu_popen_cmd(const char *command, const char *mode)
-{
-    FILE *popen_file;
-
-    popen_file = popen(command, mode);
-    if(popen_file == NULL) {
-        return NULL;
-    }
-
-    return qemu_popen(popen_file, mode);
-}
-
-int qemu_get_fd(QEMUFile *f)
-{
-    if (f->ops->get_fd)
-        return f->ops->get_fd(f->opaque);
-
-    return -1;
-}
-
 static const QEMUFileOps stdio_file_read_ops = {
     .get_fd =     stdio_get_fd,
     .get_buffer = stdio_get_buffer,
@@ -327,32 +386,111 @@
     .close =      stdio_fclose
 };
 
+static ssize_t unix_writev_buffer(void *opaque, struct iovec *iov, int iovcnt,
+                                  int64_t pos)
+{
+    QEMUFileSocket *s = opaque;
+    ssize_t len, offset;
+    ssize_t size = iov_size(iov, iovcnt);
+    ssize_t total = 0;
+
+    assert(iovcnt > 0);
+    offset = 0;
+    while (size > 0) {
+        /* Find the next start position; skip all full-sized vector elements  */
+        while (offset >= iov[0].iov_len) {
+            offset -= iov[0].iov_len;
+            iov++, iovcnt--;
+        }
+
+        /* skip `offset' bytes from the (now) first element, undo it on exit */
+        assert(iovcnt > 0);
+        iov[0].iov_base += offset;
+        iov[0].iov_len -= offset;
+
+        do {
+            len = writev(s->fd, iov, iovcnt);
+        } while (len == -1 && errno == EINTR);
+        if (len == -1) {
+            return -errno;
+        }
+
+        /* Undo the changes above */
+        iov[0].iov_base -= offset;
+        iov[0].iov_len += offset;
+
+        /* Prepare for the next iteration */
+        offset += len;
+        total += len;
+        size -= len;
+    }
+
+    return total;
+}
+
+static int unix_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
+{
+    QEMUFileSocket *s = opaque;
+    ssize_t len;
+
+    for (;;) {
+        len = read(s->fd, buf, size);
+        if (len != -1) {
+            break;
+        }
+        if (errno == EAGAIN) {
+            yield_until_fd_readable(s->fd);
+        } else if (errno != EINTR) {
+            break;
+        }
+    }
+
+    if (len == -1) {
+        len = -errno;
+    }
+    return len;
+}
+
+static int unix_close(void *opaque)
+{
+    QEMUFileSocket *s = opaque;
+    close(s->fd);
+    g_free(s);
+    return 0;
+}
+
+static const QEMUFileOps unix_read_ops = {
+    .get_fd =     socket_get_fd,
+    .get_buffer = unix_get_buffer,
+    .close =      unix_close
+};
+
+static const QEMUFileOps unix_write_ops = {
+    .get_fd =     socket_get_fd,
+    .writev_buffer = unix_writev_buffer,
+    .close =      unix_close
+};
+
 QEMUFile *qemu_fdopen(int fd, const char *mode)
 {
-    QEMUFileStdio *s;
+    QEMUFileSocket *s;
 
     if (mode == NULL ||
-        (mode[0] != 'r' && mode[0] != 'w') ||
-        mode[1] != 'b' || mode[2] != 0) {
+	(mode[0] != 'r' && mode[0] != 'w') ||
+	mode[1] != 'b' || mode[2] != 0) {
         fprintf(stderr, "qemu_fdopen: Argument validity check failed\n");
         return NULL;
     }
 
-    s = g_malloc0(sizeof(QEMUFileStdio));
-    s->stdio_file = fdopen(fd, mode);
-    if (!s->stdio_file)
-        goto fail;
+    s = g_malloc0(sizeof(QEMUFileSocket));
+    s->fd = fd;
 
     if(mode[0] == 'r') {
-        s->file = qemu_fopen_ops(s, &stdio_file_read_ops);
+        s->file = qemu_fopen_ops(s, &unix_read_ops);
     } else {
-        s->file = qemu_fopen_ops(s, &stdio_file_write_ops);
+        s->file = qemu_fopen_ops(s, &unix_write_ops);
     }
     return s->file;
-
-fail:
-    g_free(s);
-    return NULL;
 }
 
 static const QEMUFileOps socket_read_ops = {
@@ -363,22 +501,10 @@
 
 static const QEMUFileOps socket_write_ops = {
     .get_fd =     socket_get_fd,
+    .writev_buffer = socket_writev_buffer,
     .close = file_socket_close
 };
 
-QEMUFile *qemu_fopen_socket(int fd, const char* mode)
-{
-    QEMUFileSocket *s = g_malloc0(sizeof(QEMUFileSocket));
-
-    s->fd = fd;
-    if (mode[0] == 'r')
-        s->file = qemu_fopen_ops(s, &socket_read_ops);
-    else
-        s->file = qemu_fopen_ops(s, &socket_write_ops);
-
-    return s->file;
-}
-
 bool qemu_file_mode_is_not_valid(const char *mode)
 {
     if (mode == NULL ||
@@ -391,6 +517,25 @@
     return false;
 }
 
+QEMUFile *qemu_fopen_socket(int fd, const char *mode)
+{
+    QEMUFileSocket *s;
+
+    if (qemu_file_mode_is_not_valid(mode)) {
+        return NULL;
+    }
+
+    s = g_malloc0(sizeof(QEMUFileSocket));
+    s->fd = fd;
+    if (mode[0] == 'w') {
+        qemu_set_block(s->fd);
+        s->file = qemu_fopen_ops(s, &socket_write_ops);
+    } else {
+        s->file = qemu_fopen_ops(s, &socket_read_ops);
+    }
+    return s->file;
+}
+
 QEMUFile *qemu_fopen(const char *filename, const char *mode)
 {
     QEMUFileStdio *s;
@@ -404,7 +549,7 @@
     s->stdio_file = fopen(filename, mode);
     if (!s->stdio_file)
         goto fail;
-
+    
     if(mode[0] == 'w') {
         s->file = qemu_fopen_ops(s, &stdio_file_write_ops);
     } else {
@@ -416,6 +561,24 @@
     return NULL;
 }
 
+#ifndef CONFIG_ANDROID
+// TODO(digit): Once bdrv_writev_vmstate() is implemented.
+static ssize_t block_writev_buffer(void *opaque, struct iovec *iov, int iovcnt,
+                                   int64_t pos)
+{
+    int ret;
+    QEMUIOVector qiov;
+
+    qemu_iovec_init_external(&qiov, iov, iovcnt);
+    ret = bdrv_writev_vmstate(opaque, &qiov, pos);
+    if (ret < 0) {
+        return ret;
+    }
+
+    return qiov.size;
+}
+#endif
+
 static int block_put_buffer(void *opaque, const uint8_t *buf,
                            int64_t pos, int size)
 {
@@ -430,176 +593,380 @@
 
 static int bdrv_fclose(void *opaque)
 {
+    // TODO(digit): bdrv_flush() should return error code.
+    bdrv_flush(opaque);
     return 0;
 }
 
-static const QEMUFileOps bdrv_file_read_ops = {
+static const QEMUFileOps bdrv_read_ops = {
     .get_buffer = block_get_buffer,
-    .close = bdrv_fclose,
+    .close =      bdrv_fclose
 };
 
-static const QEMUFileOps bdrv_file_write_opts = {
-    .put_buffer = block_put_buffer,
-    .close = bdrv_fclose,
+static const QEMUFileOps bdrv_write_ops = {
+    .put_buffer     = block_put_buffer,
+    //.writev_buffer  = block_writev_buffer,
+    .close          = bdrv_fclose
 };
 
 static QEMUFile *qemu_fopen_bdrv(BlockDriverState *bs, int is_writable)
 {
     if (is_writable)
-        return qemu_fopen_ops(bs, &bdrv_file_write_opts);
-    else
-        return qemu_fopen_ops(bs, &bdrv_file_read_ops);
+        return qemu_fopen_ops(bs, &bdrv_write_ops);
+    return qemu_fopen_ops(bs, &bdrv_read_ops);
 }
 
-QEMUFile *qemu_fopen_ops(void *opaque, const QEMUFileOps* file_ops)
+QEMUFile *qemu_fopen_ops(void *opaque, const QEMUFileOps *ops)
 {
     QEMUFile *f;
 
     f = g_malloc0(sizeof(QEMUFile));
 
     f->opaque = opaque;
-    f->ops = file_ops;
-    f->is_write = (file_ops->put_buffer != NULL);
-
+    f->ops = ops;
     return f;
 }
 
-int qemu_file_has_error(QEMUFile *f)
+/*
+ * Get last error for stream f
+ *
+ * Return negative error value if there has been an error on previous
+ * operations, return 0 if no error happened.
+ *
+ */
+int qemu_file_get_error(QEMUFile *f)
 {
-    return f->has_error;
+    return f->last_error;
 }
 
-void qemu_file_set_error(QEMUFile *f)
+void qemu_file_set_error(QEMUFile *f, int ret)
 {
-    f->has_error = 1;
-}
-
-void qemu_fflush(QEMUFile *f)
-{
-    if (!f->ops->put_buffer)
-        return;
-
-    if (f->is_write && f->buf_index > 0) {
-        int len;
-
-        len = f->ops->put_buffer(f->opaque, f->buf, f->buf_offset, f->buf_index);
-        if (len > 0)
-            f->buf_offset += f->buf_index;
-        else
-            f->has_error = 1;
-        f->buf_index = 0;
+    if (f->last_error == 0) {
+        f->last_error = ret;
     }
 }
 
+static inline bool qemu_file_is_writable(QEMUFile *f)
+{
+    return f->ops->writev_buffer || f->ops->put_buffer;
+}
+
+/**
+ * Flushes QEMUFile buffer
+ *
+ * If there is writev_buffer QEMUFileOps it uses it otherwise uses
+ * put_buffer ops.
+ */
+void qemu_fflush(QEMUFile *f)
+{
+    ssize_t ret = 0;
+
+    if (!qemu_file_is_writable(f)) {
+        return;
+    }
+
+    if (f->ops->writev_buffer) {
+        if (f->iovcnt > 0) {
+            ret = f->ops->writev_buffer(f->opaque, f->iov, f->iovcnt, f->pos);
+        }
+    } else {
+        if (f->buf_index > 0) {
+            ret = f->ops->put_buffer(f->opaque, f->buf, f->pos, f->buf_index);
+        }
+    }
+    if (ret >= 0) {
+        f->pos += ret;
+    }
+    f->buf_index = 0;
+    f->iovcnt = 0;
+    if (ret < 0) {
+        qemu_file_set_error(f, ret);
+    }
+}
+
+#ifndef CONFIG_ANDROID
+// TODO(digit).
+void ram_control_before_iterate(QEMUFile *f, uint64_t flags)
+{
+    int ret = 0;
+
+    if (f->ops->before_ram_iterate) {
+        ret = f->ops->before_ram_iterate(f, f->opaque, flags);
+        if (ret < 0) {
+            qemu_file_set_error(f, ret);
+        }
+    }
+}
+
+void ram_control_after_iterate(QEMUFile *f, uint64_t flags)
+{
+    int ret = 0;
+
+    if (f->ops->after_ram_iterate) {
+        ret = f->ops->after_ram_iterate(f, f->opaque, flags);
+        if (ret < 0) {
+            qemu_file_set_error(f, ret);
+        }
+    }
+}
+
+void ram_control_load_hook(QEMUFile *f, uint64_t flags)
+{
+    int ret = -EINVAL;
+
+    if (f->ops->hook_ram_load) {
+        ret = f->ops->hook_ram_load(f, f->opaque, flags);
+        if (ret < 0) {
+            qemu_file_set_error(f, ret);
+        }
+    } else {
+        qemu_file_set_error(f, ret);
+    }
+}
+
+size_t ram_control_save_page(QEMUFile *f, ram_addr_t block_offset,
+                         ram_addr_t offset, size_t size, int *bytes_sent)
+{
+    if (f->ops->save_page) {
+        int ret = f->ops->save_page(f, f->opaque, block_offset,
+                                    offset, size, bytes_sent);
+
+        if (ret != RAM_SAVE_CONTROL_DELAYED) {
+            if (bytes_sent && *bytes_sent > 0) {
+                qemu_update_position(f, *bytes_sent);
+            } else if (ret < 0) {
+                qemu_file_set_error(f, ret);
+            }
+        }
+
+        return ret;
+    }
+
+    return RAM_SAVE_CONTROL_NOT_SUPP;
+}
+#endif  // !CONFIG_ANDROID
+
 static void qemu_fill_buffer(QEMUFile *f)
 {
     int len;
+    int pending;
 
-    if (!f->ops->get_buffer)
-        return;
+    assert(!qemu_file_is_writable(f));
 
-    if (f->is_write)
-        abort();
+    pending = f->buf_size - f->buf_index;
+    if (pending > 0) {
+        memmove(f->buf, f->buf + f->buf_index, pending);
+    }
+    f->buf_index = 0;
+    f->buf_size = pending;
 
-    len = f->ops->get_buffer(f->opaque, f->buf, f->buf_offset, IO_BUF_SIZE);
+    len = f->ops->get_buffer(f->opaque, f->buf + pending, f->pos,
+                        IO_BUF_SIZE - pending);
     if (len > 0) {
-        f->buf_index = 0;
-        f->buf_size = len;
-        f->buf_offset += len;
+        f->buf_size += len;
+        f->pos += len;
+    } else if (len == 0) {
+        qemu_file_set_error(f, -EIO);
     } else if (len != -EAGAIN)
-        f->has_error = 1;
+        qemu_file_set_error(f, len);
 }
 
+int qemu_get_fd(QEMUFile *f)
+{
+    if (f->ops->get_fd) {
+        return f->ops->get_fd(f->opaque);
+    }
+    return -1;
+}
+
+void qemu_update_position(QEMUFile *f, size_t size)
+{
+    f->pos += size;
+}
+
+/** Closes the file
+ *
+ * Returns negative error value if any error happened on previous operations or
+ * while closing the file. Returns 0 or positive number on success.
+ *
+ * The meaning of return value on success depends on the specific backend
+ * being used.
+ */
 int qemu_fclose(QEMUFile *f)
 {
-    int ret = 0;
+    int ret;
     qemu_fflush(f);
-    if (f->ops->close)
-        ret = f->ops->close(f->opaque);
+    ret = qemu_file_get_error(f);
+
+    if (f->ops->close) {
+        int ret2 = f->ops->close(f->opaque);
+        if (ret >= 0) {
+            ret = ret2;
+        }
+    }
+    /* If any error was spotted before closing, we should report it
+     * instead of the close() return value.
+     */
+    if (f->last_error) {
+        ret = f->last_error;
+    }
     g_free(f);
     return ret;
 }
 
-void qemu_file_put_notify(QEMUFile *f)
+static void add_to_iovec(QEMUFile *f, const uint8_t *buf, int size)
 {
-    f->ops->put_buffer(f->opaque, NULL, 0, 0);
+    /* check for adjacent buffer and coalesce them */
+    if (f->iovcnt > 0 && buf == f->iov[f->iovcnt - 1].iov_base +
+        f->iov[f->iovcnt - 1].iov_len) {
+        f->iov[f->iovcnt - 1].iov_len += size;
+    } else {
+        f->iov[f->iovcnt].iov_base = (uint8_t *)buf;
+        f->iov[f->iovcnt++].iov_len = size;
+    }
+
+    if (f->iovcnt >= MAX_IOV_SIZE) {
+        qemu_fflush(f);
+    }
+}
+
+void qemu_put_buffer_async(QEMUFile *f, const uint8_t *buf, int size)
+{
+    if (!f->ops->writev_buffer) {
+        qemu_put_buffer(f, buf, size);
+        return;
+    }
+
+    if (f->last_error) {
+        return;
+    }
+
+    f->bytes_xfer += size;
+    add_to_iovec(f, buf, size);
 }
 
 void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size)
 {
     int l;
 
-    if (!f->has_error && f->is_write == 0 && f->buf_index > 0) {
-        fprintf(stderr,
-                "Attempted to write to buffer while read buffer is not empty\n");
-        abort();
+    if (f->last_error) {
+        return;
     }
 
-    while (!f->has_error && size > 0) {
+    while (size > 0) {
         l = IO_BUF_SIZE - f->buf_index;
         if (l > size)
             l = size;
         memcpy(f->buf + f->buf_index, buf, l);
-        f->is_write = 1;
+        f->bytes_xfer += l;
+        if (f->ops->writev_buffer) {
+            add_to_iovec(f, f->buf + f->buf_index, l);
+        }
         f->buf_index += l;
+        if (f->buf_index == IO_BUF_SIZE) {
+            qemu_fflush(f);
+        }
+        if (qemu_file_get_error(f)) {
+            break;
+        }
         buf += l;
         size -= l;
-        if (f->buf_index >= IO_BUF_SIZE)
-            qemu_fflush(f);
     }
 }
 
 void qemu_put_byte(QEMUFile *f, int v)
 {
-    if (!f->has_error && f->is_write == 0 && f->buf_index > 0) {
-        fprintf(stderr,
-                "Attempted to write to buffer while read buffer is not empty\n");
-        abort();
+    if (f->last_error) {
+        return;
     }
 
-    f->buf[f->buf_index++] = v;
-    f->is_write = 1;
-    if (f->buf_index >= IO_BUF_SIZE)
+    f->buf[f->buf_index] = v;
+    f->bytes_xfer++;
+    if (f->ops->writev_buffer) {
+        add_to_iovec(f, f->buf + f->buf_index, 1);
+    }
+    f->buf_index++;
+    if (f->buf_index == IO_BUF_SIZE) {
         qemu_fflush(f);
+    }
 }
 
-int qemu_get_buffer(QEMUFile *f, uint8_t *buf, int size1)
+static void qemu_file_skip(QEMUFile *f, int size)
 {
-    int size, l;
-
-    if (f->is_write)
-        abort();
-
-    size = size1;
-    while (size > 0) {
-        l = f->buf_size - f->buf_index;
-        if (l == 0) {
-            qemu_fill_buffer(f);
-            l = f->buf_size - f->buf_index;
-            if (l == 0)
-                break;
-        }
-        if (l > size)
-            l = size;
-        memcpy(buf, f->buf + f->buf_index, l);
-        f->buf_index += l;
-        buf += l;
-        size -= l;
+    if (f->buf_index + size <= f->buf_size) {
+        f->buf_index += size;
     }
-    return size1 - size;
+}
+
+static int qemu_peek_buffer(QEMUFile *f, uint8_t *buf, int size, size_t offset)
+{
+    int pending;
+    int index;
+
+    assert(!qemu_file_is_writable(f));
+
+    index = f->buf_index + offset;
+    pending = f->buf_size - index;
+    if (pending < size) {
+        qemu_fill_buffer(f);
+        index = f->buf_index + offset;
+        pending = f->buf_size - index;
+    }
+
+    if (pending <= 0) {
+        return 0;
+    }
+    if (size > pending) {
+        size = pending;
+    }
+
+    memcpy(buf, f->buf + index, size);
+    return size;
+}
+
+int qemu_get_buffer(QEMUFile *f, uint8_t *buf, int size)
+{
+    int pending = size;
+    int done = 0;
+
+    while (pending > 0) {
+        int res;
+
+        res = qemu_peek_buffer(f, buf, pending, 0);
+        if (res == 0) {
+            return done;
+        }
+        qemu_file_skip(f, res);
+        buf += res;
+        pending -= res;
+        done += res;
+    }
+    return done;
+}
+
+static int qemu_peek_byte(QEMUFile *f, int offset)
+{
+    int index = f->buf_index + offset;
+
+    assert(!qemu_file_is_writable(f));
+
+    if (index >= f->buf_size) {
+        qemu_fill_buffer(f);
+        index = f->buf_index + offset;
+        if (index >= f->buf_size) {
+            return 0;
+        }
+    }
+    return f->buf[index];
 }
 
 int qemu_get_byte(QEMUFile *f)
 {
-    if (f->is_write)
-        abort();
+    int result;
 
-    if (f->buf_index >= f->buf_size) {
-        qemu_fill_buffer(f);
-        if (f->buf_index >= f->buf_size)
-            return 0;
-    }
-    return f->buf[f->buf_index++];
+    result = qemu_peek_byte(f, 0);
+    qemu_file_skip(f, 1);
+    return result;
 }
 
 #ifdef CONFIG_ANDROID
@@ -635,54 +1002,34 @@
 
 int64_t qemu_ftell(QEMUFile *f)
 {
-    return f->buf_offset - f->buf_size + f->buf_index;
-}
-
-int64_t qemu_fseek(QEMUFile *f, int64_t pos, int whence)
-{
-    if (whence == SEEK_SET) {
-        /* nothing to do */
-    } else if (whence == SEEK_CUR) {
-        pos += qemu_ftell(f);
-    } else {
-        /* SEEK_END not supported */
-        return -1;
-    }
-    if (f->ops->put_buffer) {
-        qemu_fflush(f);
-        f->buf_offset = pos;
-    } else {
-        f->buf_offset = pos;
-        f->buf_index = 0;
-        f->buf_size = 0;
-    }
-    return pos;
+    qemu_fflush(f);
+    return f->pos;
 }
 
 int qemu_file_rate_limit(QEMUFile *f)
 {
-    if (f->ops->rate_limit)
-        return f->ops->rate_limit(f->opaque);
-
+    if (qemu_file_get_error(f)) {
+        return 1;
+    }
+    if (f->xfer_limit > 0 && f->bytes_xfer > f->xfer_limit) {
+        return 1;
+    }
     return 0;
 }
 
 int64_t qemu_file_get_rate_limit(QEMUFile *f)
 {
-    if (f->ops->get_rate_limit)
-        return f->ops->get_rate_limit(f->opaque);
-
-    return 0;
+    return f->xfer_limit;
 }
 
-int64_t qemu_file_set_rate_limit(QEMUFile *f, int64_t new_rate)
+void qemu_file_set_rate_limit(QEMUFile *f, int64_t limit)
 {
-    /* any failed or completed migration keeps its state to allow probing of
-     * migration data, but has no associated file anymore */
-    if (f && f->ops->set_rate_limit)
-        return f->ops->set_rate_limit(f->opaque, new_rate);
+    f->xfer_limit = limit;
+}
 
-    return 0;
+void qemu_file_reset_rate_limit(QEMUFile *f)
+{
+    f->bytes_xfer = 0;
 }
 
 void qemu_put_be16(QEMUFile *f, unsigned int v)
@@ -960,10 +1307,7 @@
         se->save_live_state(f, QEMU_VM_SECTION_START, se->opaque);
     }
 
-    if (qemu_file_has_error(f))
-        return -EIO;
-
-    return 0;
+    return qemu_file_get_error(f);
 }
 
 int qemu_savevm_state_iterate(QEMUFile *f)
@@ -985,10 +1329,7 @@
     if (ret)
         return 1;
 
-    if (qemu_file_has_error(f))
-        return -EIO;
-
-    return 0;
+    return qemu_file_get_error(f);
 }
 
 int qemu_savevm_state_complete(QEMUFile *f)
@@ -1029,10 +1370,7 @@
 
     qemu_put_byte(f, QEMU_VM_EOF);
 
-    if (qemu_file_has_error(f))
-        return -EIO;
-
-    return 0;
+    return qemu_file_get_error(f);
 }
 
 int qemu_savevm_state(QEMUFile *f)
@@ -1058,8 +1396,7 @@
     ret = qemu_savevm_state_complete(f);
 
 out:
-    if (qemu_file_has_error(f))
-        ret = -EIO;
+    ret = qemu_file_get_error(f);
 
     if (!ret && saved_vm_running)
         vm_start();
@@ -1118,13 +1455,10 @@
             }
         }
         /* always seek to exact end of record */
-        qemu_fseek(f, cur_pos + record_len, SEEK_SET);
+        qemu_file_skip(f, cur_pos + record_len - qemu_ftell(f));
     }
 
-    if (qemu_file_has_error(f))
-        return -EIO;
-
-    return 0;
+    return qemu_file_get_error(f);
 }
 
 int qemu_loadvm_state(QEMUFile *f)
@@ -1227,8 +1561,8 @@
         g_free(le);
     }
 
-    if (qemu_file_has_error(f))
-        ret = -EIO;
+    if (qemu_file_get_error(f))
+      ret = qemu_file_get_error(f);
 
     return ret;
 }
diff --git a/util/yield-android.c b/util/yield-android.c
new file mode 100644
index 0000000..b553ab9
--- /dev/null
+++ b/util/yield-android.c
@@ -0,0 +1,20 @@
+#include <errno.h>
+#include <stddef.h>
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <sys/select.h>
+#endif
+
+// Wait until file descriptor |fd| becomes readable.
+void yield_until_fd_readable(int fd) {
+    for (;;) {
+       fd_set read_fds;
+       FD_ZERO(&read_fds);
+       FD_SET(fd, &read_fds);
+       int ret = select(fd + 1, &read_fds, NULL, NULL, NULL);
+       if (ret == 1 || (ret < 0 && errno != -EINTR))
+           return;
+    }
+}
+