+ /* 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;
+};