+/* Send a file complete chunk. */
+static int
+send_file_complete_sync (guestfs_h *g)
+{
+ char buf[1];
+ return send_file_chunk_sync (g, 0, buf, 0);
+}
+
+/* Send a chunk, cancellation or end of file, synchronously (ie. wait
+ * for it to go).
+ */
+static int check_for_daemon_cancellation (guestfs_h *g);
+
+static int
+send_file_chunk_sync (guestfs_h *g, int cancel, const char *buf, size_t len)
+{
+ char data[GUESTFS_MAX_CHUNK_SIZE + 48];
+ unsigned datalen;
+ int sent;
+ guestfs_chunk chunk;
+ XDR xdr;
+ guestfs_main_loop *ml = guestfs_get_main_loop (g);
+
+ if (g->state != BUSY) {
+ error (g, "send_file_chunk_sync: state %d != READY", g->state);
+ return -1;
+ }
+
+ /* Did the daemon send a cancellation message? */
+ if (check_for_daemon_cancellation (g)) {
+ if (g->verbose)
+ fprintf (stderr, "got daemon cancellation\n");
+ return -2;
+ }
+
+ /* Serialize the chunk. */
+ chunk.cancel = cancel;
+ chunk.data.data_len = len;
+ chunk.data.data_val = (char *) buf;
+
+ if (g->verbose)
+ fprintf (stderr,
+ "library sending chunk cancel = %d, len = %zu, buf = %p\n",
+ cancel, len, buf);
+
+ xdrmem_create (&xdr, data, sizeof data, XDR_ENCODE);
+ if (!xdr_guestfs_chunk (&xdr, &chunk)) {
+ error (g, "xdr_guestfs_chunk failed (buf = %p, len = %zu)", buf, len);
+ xdr_destroy (&xdr);
+ return -1;
+ }
+
+ datalen = xdr_getpos (&xdr);
+ xdr_destroy (&xdr);
+
+ /* Allocate outgoing message buffer. */
+ g->msg_out = safe_malloc (g, datalen + 4);
+ g->msg_out_size = datalen + 4;
+ g->msg_out_pos = 0;
+
+ xdrmem_create (&xdr, g->msg_out, 4, XDR_ENCODE);
+ xdr_uint32_t (&xdr, &datalen);
+
+ memcpy (g->msg_out + 4, data, datalen);
+
+ if (guestfs__switch_to_sending (g) == -1)
+ goto cleanup1;
+
+ sent = 0;
+ guestfs_set_send_callback (g, send_cb, &sent);
+ if (ml->main_loop_run (ml, g) == -1)
+ goto cleanup1;
+ if (sent != 1) {
+ error (g, "send file chunk failed, see earlier error messages");
+ goto cleanup1;
+ }
+
+ return 0;
+
+ cleanup1:
+ free (g->msg_out);
+ g->msg_out = NULL;
+ g->msg_out_size = 0;
+ return -1;
+}
+
+/* At this point we are sending FileIn file(s) to the guest, and not
+ * expecting to read anything, so if we do read anything, it must be
+ * a cancellation message. This checks for this case without blocking.
+ */
+static int
+check_for_daemon_cancellation (guestfs_h *g)
+{
+ fd_set rset;
+ struct timeval tv;
+ int r;
+ char buf[4];
+ uint32_t flag;
+ XDR xdr;
+
+ FD_ZERO (&rset);
+ FD_SET (g->sock, &rset);
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ r = select (g->sock+1, &rset, NULL, NULL, &tv);
+ if (r == -1) {
+ perrorf (g, "select");
+ return 0;
+ }
+ if (r == 0)
+ return 0;
+
+ /* Read the message from the daemon. */
+ r = xread (g->sock, buf, sizeof buf);
+ if (r == -1) {
+ perrorf (g, "read");
+ return 0;
+ }
+
+ xdrmem_create (&xdr, buf, sizeof buf, XDR_DECODE);
+ xdr_uint32_t (&xdr, &flag);
+ xdr_destroy (&xdr);
+
+ if (flag != GUESTFS_CANCEL_FLAG) {
+ error (g, "check_for_daemon_cancellation: read 0x%x from daemon, expected 0x%x\n",
+ flag, GUESTFS_CANCEL_FLAG);
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Synchronously receive a file. */
+
+static int receive_file_data_sync (guestfs_h *g, void **buf);
+
+int
+guestfs__receive_file_sync (guestfs_h *g, const char *filename)
+{
+ void *buf;
+ int fd, r;
+
+ fd = open (filename, O_WRONLY|O_CREAT|O_TRUNC|O_NOCTTY, 0666);
+ if (fd == -1) {
+ perrorf (g, "open: %s", filename);
+ goto cancel;
+ }
+
+ /* Receive the file in chunked encoding. */
+ while ((r = receive_file_data_sync (g, &buf)) > 0) {
+ if (xwrite (fd, buf, r) == -1) {
+ perrorf (g, "%s: write", filename);
+ free (buf);
+ goto cancel;
+ }
+ free (buf);
+ }
+
+ if (r == -1) {
+ error (g, "%s: error in chunked encoding", filename);
+ return -1;
+ }
+
+ if (close (fd) == -1) {
+ perrorf (g, "close: %s", filename);
+ return -1;
+ }
+
+ return 0;
+
+ cancel: ;
+ /* Send cancellation message to daemon, then wait until it
+ * cancels (just throwing away data).
+ */
+ XDR xdr;
+ char fbuf[4];
+ uint32_t flag = GUESTFS_CANCEL_FLAG;
+
+ xdrmem_create (&xdr, fbuf, sizeof fbuf, XDR_ENCODE);
+ xdr_uint32_t (&xdr, &flag);
+ xdr_destroy (&xdr);
+
+ if (xwrite (g->sock, fbuf, sizeof fbuf) == -1) {
+ perrorf (g, "write to daemon socket");
+ return -1;
+ }
+
+ while ((r = receive_file_data_sync (g, &buf)) > 0)
+ free (buf); /* just discard it */
+
+ return -1;
+}
+
+struct receive_file_ctx {
+ int code;
+ void **buf;
+};