From b7fc90f3d4f0d61281312c5d05859ee2de8fc8be Mon Sep 17 00:00:00 2001
From: Olaf Hering <olaf@aepfle.de>
Date: Mon, 10 Feb 2014 12:47:26 +0100
Subject: libxc: pass errno to callers of xc_domain_save

Callers of xc_domain_save use errno to print diagnostics if the call
fails. But xc_domain_save does not preserve the actual errno in case of
a failure.

This change preserves errno in all cases where code jumps to the label
"out". In addition a new label "exit" is added to catch also code which
used to do just "return 1".

Now libxl_save_helper:complete can print the actual error string.

Note: some of the functions used in xc_domain_save do not use errno to
indicate a reason. In these cases the errno remains undefined as it used
to be without this change.

Signed-off-by: Olaf Hering <olaf@aepfle.de>
---
 tools/libxc/xc_domain_save.c | 88 +++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 82 insertions(+), 6 deletions(-)

--- a/tools/libxc/xc_domain_save.c
+++ b/tools/libxc/xc_domain_save.c
@@ -806,6 +806,7 @@ int xc_domain_save(xc_interface *xch, in
     xc_dominfo_t info;
     DECLARE_DOMCTL;
 
+    int errnoval = 0;
     int rc = 1, frc, i, j, last_iter = 0, iter = 0;
     int live  = (flags & XCFLAGS_LIVE);
     int debug = (flags & XCFLAGS_DEBUG);
@@ -898,8 +899,8 @@ int xc_domain_save(xc_interface *xch, in
     if ( hvm && !callbacks->switch_qemu_logdirty )
     {
         ERROR("No switch_qemu_logdirty callback provided.");
-        errno = EINVAL;
-        return 1;
+        errnoval = EINVAL;
+        goto exit;
     }
 
     outbuf_init(xch, &ob_pagebuf, OUTBUF_SIZE);
@@ -913,14 +914,16 @@ int xc_domain_save(xc_interface *xch, in
     if ( !get_platform_info(xch, dom,
                             &ctx->max_mfn, &ctx->hvirt_start, &ctx->pt_levels, &dinfo->guest_width) )
     {
+        errnoval = errno;
         ERROR("Unable to get platform info.");
-        return 1;
+        goto exit;
     }
 
     if ( xc_domain_getinfo(xch, dom, 1, &info) != 1 )
     {
+        errnoval = errno;
         PERROR("Could not get domain info");
-        return 1;
+        goto exit;
     }
 
     shared_info_frame = info.shared_info_frame;
@@ -932,6 +935,7 @@ int xc_domain_save(xc_interface *xch, in
                                            PROT_READ, shared_info_frame);
         if ( !live_shinfo )
         {
+            errnoval = errno;
             PERROR("Couldn't map live_shinfo");
             goto out;
         }
@@ -942,6 +946,7 @@ int xc_domain_save(xc_interface *xch, in
 
     if ( dinfo->p2m_size > ~XEN_DOMCTL_PFINFO_LTAB_MASK )
     {
+        errnoval = E2BIG;
         ERROR("Cannot save this big a guest");
         goto out;
     }
@@ -967,6 +972,7 @@ int xc_domain_save(xc_interface *xch, in
             
             if ( frc < 0 )
             {
+                errnoval = errno;
                 PERROR("Couldn't enable shadow mode (rc %d) (errno %d)", frc, errno );
                 goto out;
             }
@@ -975,6 +981,7 @@ int xc_domain_save(xc_interface *xch, in
         /* Enable qemu-dm logging dirty pages to xen */
         if ( hvm && callbacks->switch_qemu_logdirty(dom, 1, callbacks->data) )
         {
+            errnoval = errno;
             PERROR("Couldn't enable qemu log-dirty mode (errno %d)", errno);
             goto out;
         }
@@ -985,6 +992,7 @@ int xc_domain_save(xc_interface *xch, in
         if ( suspend_and_state(callbacks->suspend, callbacks->data, xch,
                                io_fd, dom, &info) )
         {
+            errnoval = errno;
             ERROR("Domain appears not to have suspended");
             goto out;
         }
@@ -994,6 +1002,7 @@ int xc_domain_save(xc_interface *xch, in
     {
         if (!(compress_ctx = xc_compression_create_context(xch, dinfo->p2m_size)))
         {
+            errnoval = errno;
             ERROR("Failed to create compression context");
             goto out;
         }
@@ -1012,6 +1021,7 @@ int xc_domain_save(xc_interface *xch, in
 
     if ( !to_send || !to_fix || !to_skip )
     {
+        errnoval = ENOMEM;
         ERROR("Couldn't allocate to_send array");
         goto out;
     }
@@ -1024,12 +1034,14 @@ int xc_domain_save(xc_interface *xch, in
         hvm_buf_size = xc_domain_hvm_getcontext(xch, dom, 0, 0);
         if ( hvm_buf_size == -1 )
         {
+            errnoval = errno;
             PERROR("Couldn't get HVM context size from Xen");
             goto out;
         }
         hvm_buf = malloc(hvm_buf_size);
         if ( !hvm_buf )
         {
+            errnoval = ENOMEM;
             ERROR("Couldn't allocate memory");
             goto out;
         }
@@ -1043,7 +1055,7 @@ int xc_domain_save(xc_interface *xch, in
     if ( (pfn_type == NULL) || (pfn_batch == NULL) || (pfn_err == NULL) )
     {
         ERROR("failed to alloc memory for pfn_type and/or pfn_batch arrays");
-        errno = ENOMEM;
+        errnoval = ENOMEM;
         goto out;
     }
     memset(pfn_type, 0,
@@ -1052,6 +1064,7 @@ int xc_domain_save(xc_interface *xch, in
     /* Setup the mfn_to_pfn table mapping */
     if ( !(ctx->live_m2p = xc_map_m2p(xch, ctx->max_mfn, PROT_READ, &ctx->m2p_mfn0)) )
     {
+        errnoval = errno;
         PERROR("Failed to map live M2P table");
         goto out;
     }
@@ -1059,6 +1072,7 @@ int xc_domain_save(xc_interface *xch, in
     /* Start writing out the saved-domain record. */
     if ( write_exact(io_fd, &dinfo->p2m_size, sizeof(unsigned long)) )
     {
+        errnoval = errno;
         PERROR("write: p2m_size");
         goto out;
     }
@@ -1071,6 +1085,7 @@ int xc_domain_save(xc_interface *xch, in
         ctx->live_p2m = map_and_save_p2m_table(xch, io_fd, dom, ctx, live_shinfo);
         if ( ctx->live_p2m == NULL )
         {
+            errnoval = errno;
             PERROR("Failed to map/save the p2m frame list");
             goto out;
         }
@@ -1097,12 +1112,14 @@ int xc_domain_save(xc_interface *xch, in
     tmem_saved = xc_tmem_save(xch, dom, io_fd, live, XC_SAVE_ID_TMEM);
     if ( tmem_saved == -1 )
     {
+        errnoval = errno;
         PERROR("Error when writing to state file (tmem)");
         goto out;
     }
 
     if ( !live && save_tsc_info(xch, dom, io_fd) < 0 )
     {
+        errnoval = errno;
         PERROR("Error when writing to state file (tsc)");
         goto out;
     }
@@ -1143,6 +1160,7 @@ int xc_domain_save(xc_interface *xch, in
                     dinfo->p2m_size, NULL, 0, NULL);
                 if ( frc != dinfo->p2m_size )
                 {
+                    errnoval = errno;
                     ERROR("Error peeking shadow bitmap");
                     goto out;
                 }
@@ -1257,6 +1275,7 @@ int xc_domain_save(xc_interface *xch, in
                 xch, dom, PROT_READ, pfn_type, pfn_err, batch);
             if ( region_base == NULL )
             {
+                errnoval = errno;
                 PERROR("map batch failed");
                 goto out;
             }
@@ -1264,6 +1283,7 @@ int xc_domain_save(xc_interface *xch, in
             /* Get page types */
             if ( xc_get_pfn_type_batch(xch, dom, batch, pfn_type) )
             {
+                errnoval = errno;
                 PERROR("get_pfn_type_batch failed");
                 goto out;
             }
@@ -1332,6 +1352,7 @@ int xc_domain_save(xc_interface *xch, in
 
             if ( wrexact(io_fd, &batch, sizeof(unsigned int)) )
             {
+                errnoval = errno;
                 PERROR("Error when writing to state file (2)");
                 goto out;
             }
@@ -1341,6 +1362,7 @@ int xc_domain_save(xc_interface *xch, in
                     ((unsigned long *)pfn_type)[j] = pfn_type[j];
             if ( wrexact(io_fd, pfn_type, sizeof(unsigned long)*batch) )
             {
+                errnoval = errno;
                 PERROR("Error when writing to state file (3)");
                 goto out;
             }
@@ -1368,6 +1390,7 @@ int xc_domain_save(xc_interface *xch, in
                                        (char*)region_base+(PAGE_SIZE*(j-run)), 
                                        PAGE_SIZE*run) != PAGE_SIZE*run )
                         {
+                            errnoval = errno;
                             PERROR("Error when writing to state file (4a)"
                                   " (errno %d)", errno);
                             goto out;
@@ -1396,6 +1419,7 @@ int xc_domain_save(xc_interface *xch, in
 
                     if ( race && !live )
                     {
+                        errnoval = errno;
                         ERROR("Fatal PT race (pfn %lx, type %08lx)", pfn,
                               pagetype);
                         goto out;
@@ -1409,6 +1433,7 @@ int xc_domain_save(xc_interface *xch, in
                                                         pfn, 1 /* raw page */);
                         if (c_err == -2) /* OOB PFN */
                         {
+                            errnoval = errno;
                             ERROR("Could not add pagetable page "
                                   "(pfn:%" PRIpfn "to page buffer\n", pfn);
                             goto out;
@@ -1428,6 +1453,7 @@ int xc_domain_save(xc_interface *xch, in
                              */
                             if (wrcompressed(io_fd) < 0)
                             {
+                                errnoval = errno;
                                 ERROR("Error when writing compressed"
                                       " data (4b)\n");
                                 goto out;
@@ -1437,6 +1463,7 @@ int xc_domain_save(xc_interface *xch, in
                     else if ( wruncached(io_fd, live, page,
                                          PAGE_SIZE) != PAGE_SIZE )
                     {
+                        errnoval = errno;
                         PERROR("Error when writing to state file (4b)"
                               " (errno %d)", errno);
                         goto out;
@@ -1456,6 +1483,7 @@ int xc_domain_save(xc_interface *xch, in
 
                         if (c_err == -2) /* OOB PFN */
                         {
+                            errnoval = errno;
                             ERROR("Could not add page "
                                   "(pfn:%" PRIpfn "to page buffer\n", pfn);
                             goto out;
@@ -1465,6 +1493,7 @@ int xc_domain_save(xc_interface *xch, in
                         {
                             if (wrcompressed(io_fd) < 0)
                             {
+                                errnoval = errno;
                                 ERROR("Error when writing compressed"
                                       " data (4c)\n");
                                 goto out;
@@ -1483,6 +1512,7 @@ int xc_domain_save(xc_interface *xch, in
                                (char*)region_base+(PAGE_SIZE*(j-run)), 
                                PAGE_SIZE*run) != PAGE_SIZE*run )
                 {
+                    errnoval = errno;
                     PERROR("Error when writing to state file (4c)"
                           " (errno %d)", errno);
                     goto out;
@@ -1520,6 +1550,7 @@ int xc_domain_save(xc_interface *xch, in
             /* send "-1" to put receiver into debug mode */
             if ( wrexact(io_fd, &id, sizeof(int)) )
             {
+                errnoval = errno;
                 PERROR("Error when writing to state file (6)");
                 goto out;
             }
@@ -1542,6 +1573,7 @@ int xc_domain_save(xc_interface *xch, in
                 if ( suspend_and_state(callbacks->suspend, callbacks->data,
                                        xch, io_fd, dom, &info) )
                 {
+                    errnoval = errno;
                     ERROR("Domain appears not to have suspended");
                     goto out;
                 }
@@ -1550,12 +1582,14 @@ int xc_domain_save(xc_interface *xch, in
                 if ( (tmem_saved > 0) &&
                      (xc_tmem_save_extra(xch,dom,io_fd,XC_SAVE_ID_TMEM_EXTRA) == -1) )
                 {
+                        errnoval = errno;
                         PERROR("Error when writing to state file (tmem)");
                         goto out;
                 }
 
                 if ( save_tsc_info(xch, dom, io_fd) < 0 )
                 {
+                    errnoval = errno;
                     PERROR("Error when writing to state file (tsc)");
                     goto out;
                 }
@@ -1567,6 +1601,7 @@ int xc_domain_save(xc_interface *xch, in
                                    XEN_DOMCTL_SHADOW_OP_CLEAN, HYPERCALL_BUFFER(to_send),
                                    dinfo->p2m_size, NULL, 0, &shadow_stats) != dinfo->p2m_size )
             {
+                errnoval = errno;
                 PERROR("Error flushing shadow PT");
                 goto out;
             }
@@ -1598,6 +1633,7 @@ int xc_domain_save(xc_interface *xch, in
 
         if ( info.max_vcpu_id >= XC_SR_MAX_VCPUS )
         {
+            errnoval = E2BIG;
             ERROR("Too many VCPUS in guest!");
             goto out;
         }
@@ -1614,6 +1650,7 @@ int xc_domain_save(xc_interface *xch, in
         if ( wrexact(io_fd, &chunk, offsetof(struct chunk, vcpumap)
                      + vcpumap_sz(info.max_vcpu_id)) )
         {
+            errnoval = errno;
             PERROR("Error when writing to state file");
             goto out;
         }
@@ -1633,6 +1670,7 @@ int xc_domain_save(xc_interface *xch, in
         if ( (chunk.data != 0) &&
              wrexact(io_fd, &chunk, sizeof(chunk)) )
         {
+            errnoval = errno;
             PERROR("Error when writing the generation id buffer location for guest");
             goto out;
         }
@@ -1645,6 +1683,7 @@ int xc_domain_save(xc_interface *xch, in
         if ( (chunk.data != 0) &&
              wrexact(io_fd, &chunk, sizeof(chunk)) )
         {
+            errnoval = errno;
             PERROR("Error when writing the ident_pt for EPT guest");
             goto out;
         }
@@ -1657,6 +1696,7 @@ int xc_domain_save(xc_interface *xch, in
         if ( (chunk.data != 0) &&
              wrexact(io_fd, &chunk, sizeof(chunk)) )
         {
+            errnoval = errno;
             PERROR("Error when writing the paging ring pfn for guest");
             goto out;
         }
@@ -1669,6 +1709,7 @@ int xc_domain_save(xc_interface *xch, in
         if ( (chunk.data != 0) &&
              wrexact(io_fd, &chunk, sizeof(chunk)) )
         {
+            errnoval = errno;
             PERROR("Error when writing the access ring pfn for guest");
             goto out;
         }
@@ -1681,6 +1722,7 @@ int xc_domain_save(xc_interface *xch, in
         if ( (chunk.data != 0) &&
              wrexact(io_fd, &chunk, sizeof(chunk)) )
         {
+            errnoval = errno;
             PERROR("Error when writing the sharing ring pfn for guest");
             goto out;
         }
@@ -1693,6 +1735,7 @@ int xc_domain_save(xc_interface *xch, in
         if ( (chunk.data != 0) &&
              wrexact(io_fd, &chunk, sizeof(chunk)) )
         {
+            errnoval = errno;
             PERROR("Error when writing the vm86 TSS for guest");
             goto out;
         }
@@ -1705,6 +1748,7 @@ int xc_domain_save(xc_interface *xch, in
         if ( (chunk.data != 0) &&
              wrexact(io_fd, &chunk, sizeof(chunk)) )
         {
+            errnoval = errno;
             PERROR("Error when writing the console pfn for guest");
             goto out;
         }
@@ -1716,6 +1760,7 @@ int xc_domain_save(xc_interface *xch, in
 
         if ((chunk.data != 0) && wrexact(io_fd, &chunk, sizeof(chunk)))
         {
+            errnoval = errno;
             PERROR("Error when writing the firmware ioport version");
             goto out;
         }
@@ -1728,6 +1773,7 @@ int xc_domain_save(xc_interface *xch, in
         if ( (chunk.data != 0) &&
              wrexact(io_fd, &chunk, sizeof(chunk)) )
         {
+            errnoval = errno;
             PERROR("Error when writing the viridian flag");
             goto out;
         }
@@ -1753,6 +1799,7 @@ int xc_domain_save(xc_interface *xch, in
 
         if ( callbacks->toolstack_save(dom, &buf, &len, callbacks->data) < 0 )
         {
+            errnoval = errno;
             PERROR("Error calling toolstack_save");
             goto out;
         }
@@ -1771,6 +1818,7 @@ int xc_domain_save(xc_interface *xch, in
         i = XC_SAVE_ID_LAST_CHECKPOINT;
         if ( wrexact(io_fd, &i, sizeof(int)) )
         {
+            errnoval = errno;
             PERROR("Error when writing last checkpoint chunk");
             goto out;
         }
@@ -1790,6 +1838,7 @@ int xc_domain_save(xc_interface *xch, in
         i = XC_SAVE_ID_ENABLE_COMPRESSION;
         if ( wrexact(io_fd, &i, sizeof(int)) )
         {
+            errnoval = errno;
             PERROR("Error when writing enable_compression marker");
             goto out;
         }
@@ -1799,6 +1848,7 @@ int xc_domain_save(xc_interface *xch, in
     i = 0;
     if ( wrexact(io_fd, &i, sizeof(int)) )
     {
+        errnoval = errno;
         PERROR("Error when writing to state file (6')");
         goto out;
     }
@@ -1817,6 +1867,7 @@ int xc_domain_save(xc_interface *xch, in
                          (unsigned long *)&magic_pfns[2]);
         if ( wrexact(io_fd, magic_pfns, sizeof(magic_pfns)) )
         {
+            errnoval = errno;
             PERROR("Error when writing to state file (7)");
             goto out;
         }
@@ -1825,18 +1876,21 @@ int xc_domain_save(xc_interface *xch, in
         if ( (rec_size = xc_domain_hvm_getcontext(xch, dom, hvm_buf, 
                                                   hvm_buf_size)) == -1 )
         {
+            errnoval = errno;
             PERROR("HVM:Could not get hvm buffer");
             goto out;
         }
         
         if ( wrexact(io_fd, &rec_size, sizeof(uint32_t)) )
         {
+            errnoval = errno;
             PERROR("error write hvm buffer size");
             goto out;
         }
         
         if ( wrexact(io_fd, hvm_buf, rec_size) )
         {
+            errnoval = errno;
             PERROR("write HVM info failed!");
             goto out;
         }
@@ -1861,6 +1915,7 @@ int xc_domain_save(xc_interface *xch, in
 
         if ( wrexact(io_fd, &j, sizeof(unsigned int)) )
         {
+            errnoval = errno;
             PERROR("Error when writing to state file (6a)");
             goto out;
         }
@@ -1875,6 +1930,7 @@ int xc_domain_save(xc_interface *xch, in
             {
                 if ( wrexact(io_fd, &pfntab, sizeof(unsigned long)*j) )
                 {
+                    errnoval = errno;
                     PERROR("Error when writing to state file (6b)");
                     goto out;
                 }
@@ -1885,6 +1941,7 @@ int xc_domain_save(xc_interface *xch, in
 
     if ( xc_vcpu_getcontext(xch, dom, 0, &ctxt) )
     {
+        errnoval = errno;
         PERROR("Could not get vcpu context");
         goto out;
     }
@@ -1900,6 +1957,7 @@ int xc_domain_save(xc_interface *xch, in
     mfn = GET_FIELD(&ctxt, user_regs.edx);
     if ( !MFN_IS_IN_PSEUDOPHYS_MAP(mfn) )
     {
+        errnoval = ERANGE;
         ERROR("Suspend record is not in range of pseudophys map");
         goto out;
     }
@@ -1912,6 +1970,7 @@ int xc_domain_save(xc_interface *xch, in
 
         if ( (i != 0) && xc_vcpu_getcontext(xch, dom, i, &ctxt) )
         {
+            errnoval = errno;
             PERROR("No context for VCPU%d", i);
             goto out;
         }
@@ -1922,6 +1981,7 @@ int xc_domain_save(xc_interface *xch, in
             mfn = GET_FIELD(&ctxt, gdt_frames[j]);
             if ( !MFN_IS_IN_PSEUDOPHYS_MAP(mfn) )
             {
+                errnoval = ERANGE;
                 ERROR("GDT frame is not in range of pseudophys map");
                 goto out;
             }
@@ -1932,6 +1992,7 @@ int xc_domain_save(xc_interface *xch, in
         if ( !MFN_IS_IN_PSEUDOPHYS_MAP(UNFOLD_CR3(
                                            GET_FIELD(&ctxt, ctrlreg[3]))) )
         {
+            errnoval = ERANGE;
             ERROR("PT base is not in range of pseudophys map");
             goto out;
         }
@@ -1943,6 +2004,7 @@ int xc_domain_save(xc_interface *xch, in
         {
             if ( !MFN_IS_IN_PSEUDOPHYS_MAP(UNFOLD_CR3(ctxt.x64.ctrlreg[1])) )
             {
+                errnoval = ERANGE;
                 ERROR("PT base is not in range of pseudophys map");
                 goto out;
             }
@@ -1955,6 +2017,7 @@ int xc_domain_save(xc_interface *xch, in
                                         ? sizeof(ctxt.x64) 
                                         : sizeof(ctxt.x32))) )
         {
+            errnoval = errno;
             PERROR("Error when writing to state file (1)");
             goto out;
         }
@@ -1965,11 +2028,13 @@ int xc_domain_save(xc_interface *xch, in
         domctl.u.ext_vcpucontext.vcpu = i;
         if ( xc_domctl(xch, &domctl) < 0 )
         {
+            errnoval = errno;
             PERROR("No extended context for VCPU%d", i);
             goto out;
         }
         if ( wrexact(io_fd, &domctl.u.ext_vcpucontext, 128) )
         {
+            errnoval = errno;
             PERROR("Error when writing to state file (2)");
             goto out;
         }
@@ -1983,6 +2048,7 @@ int xc_domain_save(xc_interface *xch, in
         domctl.u.vcpuextstate.size = 0;
         if ( xc_domctl(xch, &domctl) < 0 )
         {
+            errnoval = errno;
             PERROR("No eXtended states (XSAVE) for VCPU%d", i);
             goto out;
         }
@@ -1994,6 +2060,7 @@ int xc_domain_save(xc_interface *xch, in
         buffer = xc_hypercall_buffer_alloc(xch, buffer, domctl.u.vcpuextstate.size);
         if ( !buffer )
         {
+            errnoval = errno;
             PERROR("Insufficient memory for getting eXtended states for"
                    "VCPU%d", i);
             goto out;
@@ -2001,6 +2068,7 @@ int xc_domain_save(xc_interface *xch, in
         set_xen_guest_handle(domctl.u.vcpuextstate.buffer, buffer);
         if ( xc_domctl(xch, &domctl) < 0 )
         {
+            errnoval = errno;
             PERROR("No eXtended states (XSAVE) for VCPU%d", i);
             xc_hypercall_buffer_free(xch, buffer);
             goto out;
@@ -2012,6 +2080,7 @@ int xc_domain_save(xc_interface *xch, in
                      sizeof(domctl.u.vcpuextstate.size)) ||
              wrexact(io_fd, buffer, domctl.u.vcpuextstate.size) )
         {
+            errnoval = errno;
             PERROR("Error when writing to state file VCPU extended state");
             xc_hypercall_buffer_free(xch, buffer);
             goto out;
@@ -2027,6 +2096,7 @@ int xc_domain_save(xc_interface *xch, in
               arch.pfn_to_mfn_frame_list_list, 0);
     if ( wrexact(io_fd, page, PAGE_SIZE) )
     {
+        errnoval = errno;
         PERROR("Error when writing to state file (1)");
         goto out;
     }
@@ -2034,6 +2104,7 @@ int xc_domain_save(xc_interface *xch, in
     /* Flush last write and check for errors. */
     if ( fsync(io_fd) && errno != EINVAL )
     {
+        errnoval = errno;
         PERROR("Error when flushing state file");
         goto out;
     }
@@ -2055,6 +2126,7 @@ int xc_domain_save(xc_interface *xch, in
         ob = &ob_pagebuf;
         if (wrcompressed(io_fd) < 0)
         {
+            errnoval = errno;
             ERROR("Error when writing compressed data, after postcopy\n");
             rc = 1;
             goto out;
@@ -2063,6 +2135,7 @@ int xc_domain_save(xc_interface *xch, in
         if ( wrexact(io_fd, ob_tailbuf.buf, ob_tailbuf.pos) )
         {
             rc = 1;
+            errnoval = errno;
             PERROR("Error when copying tailbuf into outbuf");
             goto out;
         }
@@ -2091,6 +2164,7 @@ int xc_domain_save(xc_interface *xch, in
         if ( suspend_and_state(callbacks->suspend, callbacks->data, xch,
                                io_fd, dom, &info) )
         {
+            errnoval = errno;
             ERROR("Domain appears not to have suspended");
             goto out;
         }
@@ -2142,7 +2216,9 @@ int xc_domain_save(xc_interface *xch, in
     free(hvm_buf);
     outbuf_free(&ob_pagebuf);
 
-    DPRINTF("Save exit of domid %u with rc=%d\n", dom, rc);
+exit:
+    DPRINTF("Save exit of domid %u with rc=%d, errno=%d\n", dom, rc, errnoval);
+    errno = errnoval;
 
     return !!rc;
 }
