+guestfs__send_sync (guestfs_h *g, int proc_nr,
+ xdrproc_t xdrp, char *args)
+{
+ struct guestfs_message_header hdr;
+ XDR xdr;
+ u_int32_t len;
+ int serial = g->msg_next_serial++;
+ int sent;
+ guestfs_main_loop *ml = guestfs_get_main_loop (g);
+
+ if (g->state != BUSY) {
+ error (g, _("guestfs__send_sync: state %d != BUSY"), g->state);
+ return -1;
+ }
+
+ /* This is probably an internal error. Or perhaps we should just
+ * free the buffer anyway?
+ */
+ if (g->msg_out != NULL) {
+ error (g, _("guestfs__send_sync: msg_out should be NULL"));
+ return -1;
+ }
+
+ /* We have to allocate this message buffer on the heap because
+ * it is quite large (although will be mostly unused). We
+ * can't allocate it on the stack because in some environments
+ * we have quite limited stack space available, notably when
+ * running in the JVM.
+ */
+ g->msg_out = safe_malloc (g, GUESTFS_MESSAGE_MAX + 4);
+ xdrmem_create (&xdr, g->msg_out + 4, GUESTFS_MESSAGE_MAX, XDR_ENCODE);
+
+ /* Serialize the header. */
+ hdr.prog = GUESTFS_PROGRAM;
+ hdr.vers = GUESTFS_PROTOCOL_VERSION;
+ hdr.proc = proc_nr;
+ hdr.direction = GUESTFS_DIRECTION_CALL;
+ hdr.serial = serial;
+ hdr.status = GUESTFS_STATUS_OK;
+
+ if (!xdr_guestfs_message_header (&xdr, &hdr)) {
+ error (g, _("xdr_guestfs_message_header failed"));
+ goto cleanup1;
+ }
+
+ /* Serialize the args. If any, because some message types
+ * have no parameters.
+ */
+ if (xdrp) {
+ if (!(*xdrp) (&xdr, args)) {
+ error (g, _("dispatch failed to marshal args"));
+ goto cleanup1;
+ }
+ }
+
+ /* Get the actual length of the message, resize the buffer to match
+ * the actual length, and write the length word at the beginning.
+ */
+ len = xdr_getpos (&xdr);
+ xdr_destroy (&xdr);
+
+ g->msg_out = safe_realloc (g, g->msg_out, len + 4);
+ g->msg_out_size = len + 4;
+ g->msg_out_pos = 0;
+
+ xdrmem_create (&xdr, g->msg_out, 4, XDR_ENCODE);
+ xdr_uint32_t (&xdr, &len);
+
+ 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 failed, see earlier error messages"));
+ goto cleanup1;
+ }
+
+ return serial;
+
+ cleanup1:
+ free (g->msg_out);
+ g->msg_out = NULL;
+ g->msg_out_size = 0;
+ return -1;
+}
+
+static int cancel = 0; /* XXX Implement file cancellation. */
+static int send_file_chunk_sync (guestfs_h *g, int cancel, const char *buf, size_t len);
+static int send_file_data_sync (guestfs_h *g, const char *buf, size_t len);
+static int send_file_cancellation_sync (guestfs_h *g);
+static int send_file_complete_sync (guestfs_h *g);
+
+/* Synchronously send a file.
+ * Returns:
+ * 0 OK
+ * -1 error
+ * -2 daemon cancelled (we must read the error message)
+ */
+int
+guestfs__send_file_sync (guestfs_h *g, const char *filename)
+{
+ char buf[GUESTFS_MAX_CHUNK_SIZE];
+ int fd, r, err;
+
+ fd = open (filename, O_RDONLY);
+ if (fd == -1) {
+ perrorf (g, "open: %s", filename);
+ send_file_cancellation_sync (g);
+ /* Daemon sees cancellation and won't reply, so caller can
+ * just return here.
+ */
+ return -1;
+ }
+
+ /* Send file in chunked encoding. */
+ while (!cancel) {
+ r = read (fd, buf, sizeof buf);
+ if (r == -1 && (errno == EINTR || errno == EAGAIN))
+ continue;
+ if (r <= 0) break;
+ err = send_file_data_sync (g, buf, r);
+ if (err < 0) {
+ if (err == -2) /* daemon sent cancellation */
+ send_file_cancellation_sync (g);
+ return err;
+ }
+ }
+
+ if (cancel) { /* cancel from either end */
+ send_file_cancellation_sync (g);
+ return -1;
+ }
+
+ if (r == -1) {
+ perrorf (g, "read: %s", filename);
+ send_file_cancellation_sync (g);
+ return -1;
+ }
+
+ /* End of file, but before we send that, we need to close
+ * the file and check for errors.
+ */
+ if (close (fd) == -1) {
+ perrorf (g, "close: %s", filename);
+ send_file_cancellation_sync (g);
+ return -1;
+ }
+
+ return send_file_complete_sync (g);
+}
+
+/* Send a chunk of file data. */
+static int
+send_file_data_sync (guestfs_h *g, const char *buf, size_t len)
+{
+ return send_file_chunk_sync (g, 0, buf, len);
+}
+
+/* Send a cancellation message. */
+static int
+send_file_cancellation_sync (guestfs_h *g)
+{
+ return send_file_chunk_sync (g, 1, NULL, 0);
+}
+
+/* Send a file complete chunk. */
+static int
+send_file_complete_sync (guestfs_h *g)