Patchwork [RFC,07/16] Visitor: Binary compatible input visitor

login
register
mail settings
Submitter Dave Gilbert
Date March 25, 2014, 8:17 p.m.
Message ID <1395778647-30925-8-git-send-email-dgilbert@redhat.com>
Download mbox | patch
Permalink /patch/333699/
State New
Headers show

Comments

Dave Gilbert - March 25, 2014, 8:17 p.m.
From: "Dr. David Alan Gilbert" <dgilbert@redhat.com>

based on Michael Roth's
https://lists.gnu.org/archive/html/qemu-devel/2011-09/msg02470.html

Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
---
 include/qapi/qemu-file-binary-input-visitor.h |  27 +
 qapi/Makefile.objs                            |   2 +-
 qapi/qemu-file-binary-input-visitor.c         | 688 ++++++++++++++++++++++++++
 3 files changed, 716 insertions(+), 1 deletion(-)
 create mode 100644 include/qapi/qemu-file-binary-input-visitor.h
 create mode 100644 qapi/qemu-file-binary-input-visitor.c

Patch

diff --git a/include/qapi/qemu-file-binary-input-visitor.h b/include/qapi/qemu-file-binary-input-visitor.h
new file mode 100644
index 0000000..dd7e40e
--- /dev/null
+++ b/include/qapi/qemu-file-binary-input-visitor.h
@@ -0,0 +1,27 @@ 
+/*
+ * QEMUFile Visitor
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ *  Michael Roth   <mdroth@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#ifndef QEMU_FILE_BINARY_INPUT_VISITOR_H
+#define QEMU_FILE_BINARY_INPUT_VISITOR_H
+
+#include "visitor.h"
+
+typedef struct QemuFileBinInputVisitor QemuFileBinInputVisitor;
+
+QemuFileBinInputVisitor *qemu_file_bin_input_visitor_new(QEMUFile *f);
+void qemu_file_bin_input_visitor_cleanup(QemuFileBinInputVisitor *d);
+
+Visitor *qemu_file_bin_input_get_visitor(QemuFileBinInputVisitor *v);
+
+#endif
+
diff --git a/qapi/Makefile.objs b/qapi/Makefile.objs
index 06c69e6..3d9d47a 100644
--- a/qapi/Makefile.objs
+++ b/qapi/Makefile.objs
@@ -1,6 +1,6 @@ 
 util-obj-y = qapi-visit-core.o qapi-dealloc-visitor.o qmp-input-visitor.o
 util-obj-y += qmp-output-visitor.o qmp-registry.o qmp-dispatch.o
 util-obj-y += string-input-visitor.o string-output-visitor.o
-util-obj-y += qemu-file-binary-output-visitor.o
+util-obj-y += qemu-file-binary-output-visitor.o qemu-file-binary-input-visitor.o
 util-obj-y += qemu-file-debug-output-visitor.o
 util-obj-y += opts-visitor.o
diff --git a/qapi/qemu-file-binary-input-visitor.c b/qapi/qemu-file-binary-input-visitor.c
new file mode 100644
index 0000000..162765e
--- /dev/null
+++ b/qapi/qemu-file-binary-input-visitor.c
@@ -0,0 +1,688 @@ 
+/*
+ * QEMUFile Output Visitor
+ *
+ * Copyright IBM, Corp. 2011
+ * Copyright Red Hat, Corp. 2014
+ *
+ * Authors:
+ *  Michael Roth   <mdroth@linux.vnet.ibm.com>
+ *  David Gilbert  <dgilbert@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include "qapi/qemu-file-binary-input-visitor.h"
+#include "qapi/visitor-impl.h"
+#include "qapi/qmp/qerror.h"
+#include "qemu/queue.h"
+#include "qemu-common.h"
+#include "hw/hw.h"
+#include "migration/migration.h"
+
+/* Note that this can generate so much debug virt-test times out */
+#if 0
+#define DPRINTF(v, fmt, ...) \
+    do { \
+        fprintf(stderr, "%*s qfbiv/%s/%d: " fmt "\n", v->depth, "", __func__, \
+                       __LINE__, ## __VA_ARGS__); \
+    } while (0)
+#else
+#define DPRINTF(v, fmt, ...) \
+    do { } while (0)
+#endif
+
+typedef struct {
+    size_t elem_count;
+    size_t elem_size;
+    size_t pos;
+} ArrayInfo;
+
+typedef struct {
+    Visit_seq_compat_mode mode;
+    const void           *data;
+    bool                  hit_end;
+} SeqCompatInfo;
+
+typedef struct StackEntry {
+    enum {
+        QFIV_ARRAY,
+        QFIV_LIST,
+        QFIV_STRUCT,
+        QFIV_SEQCOMPAT,
+    } type;
+    ArrayInfo array_info;
+    SeqCompatInfo seqcompat_info;
+    QTAILQ_ENTRY(StackEntry) node;
+} StackEntry;
+
+struct QemuFileBinInputVisitor {
+    Visitor visitor;
+    QTAILQ_HEAD(, StackEntry) stack;
+    QEMUFile *file;
+    unsigned int depth;
+};
+
+static QemuFileBinInputVisitor *to_iv(Visitor *v)
+{
+    return container_of(v, QemuFileBinInputVisitor, visitor);
+}
+
+static void qfbi_push(QemuFileBinInputVisitor *iv, StackEntry *e)
+{
+    QTAILQ_INSERT_HEAD(&iv->stack, e, node);
+    iv->depth++;
+}
+
+static void qfbi_push_array(QemuFileBinInputVisitor *iv,
+                                           ArrayInfo ai)
+{
+    StackEntry *e = g_malloc0(sizeof(*e));
+    e->type = QFIV_ARRAY;
+    e->array_info = ai;
+    qfbi_push(iv, e);
+}
+
+static void qfbi_push_list(QemuFileBinInputVisitor *iv)
+{
+    StackEntry *e = g_malloc0(sizeof(*e));
+    e->type = QFIV_LIST;
+    qfbi_push(iv, e);
+}
+
+static void qfbi_push_seqcompat(QemuFileBinInputVisitor *iv,
+                                               SeqCompatInfo sci)
+{
+    StackEntry *e = g_malloc0(sizeof(*e));
+    e->type = QFIV_SEQCOMPAT;
+    e->seqcompat_info = sci;
+    qfbi_push(iv, e);
+}
+
+static void qfbi_push_struct(QemuFileBinInputVisitor *iv)
+{
+    StackEntry *e = g_malloc0(sizeof(*e));
+    e->type = QFIV_STRUCT;
+    qfbi_push(iv, e);
+}
+
+static void *qfbi_pop(QemuFileBinInputVisitor *iv)
+{
+    StackEntry *e = QTAILQ_FIRST(&iv->stack);
+    QTAILQ_REMOVE(&iv->stack, e, node);
+    iv->depth--;
+    return e;
+}
+
+static bool qfbi_is_array(QemuFileBinInputVisitor *iv)
+{
+    StackEntry *e = QTAILQ_FIRST(&iv->stack);
+    return e->type == QFIV_ARRAY;
+}
+
+static bool qfbi_is_list(QemuFileBinInputVisitor *iv)
+{
+    StackEntry *e = QTAILQ_FIRST(&iv->stack);
+    return e && e->type == QFIV_LIST;
+}
+
+/* If we are in a seqcompat list return true and fill in
+ * sci with the compat mode
+ */
+static bool qfbi_is_seqcompat(QemuFileBinInputVisitor *iv,
+                              SeqCompatInfo **sci)
+{
+    StackEntry *e = QTAILQ_FIRST(&iv->stack);
+    if (e && e->type == QFIV_SEQCOMPAT) {
+        *sci = &e->seqcompat_info;
+        return true;
+    }
+    return false;
+}
+
+static void qfbi_start_struct(Visitor *v, void **obj,
+                              const char *kind,
+                              const char *name, size_t size,
+                              Error **errp)
+{
+    QemuFileBinInputVisitor *iv = to_iv(v);
+    DPRINTF(iv, "for '%s' of '%s'", name, kind);
+
+    qfbi_push_struct(iv);
+}
+
+static void qfbi_end_struct(Visitor *v, Error **errp)
+{
+    QemuFileBinInputVisitor *iv = to_iv(v);
+    StackEntry *e = qfbi_pop(iv);
+
+    DPRINTF(iv, "<");
+    if (!e || e->type != QFIV_STRUCT) {
+        error_set(errp, QERR_UNDEFINED_ERROR);
+        return;
+    }
+    g_free(e);
+}
+
+static void qfbi_start_list(Visitor *v, const char *name, Error **errp)
+{
+    QemuFileBinInputVisitor *iv = to_iv(v);
+    qfbi_push_list(iv);
+}
+
+static GenericList *qfbi_next_list(Visitor *v, GenericList **list, Error **errp)
+{
+    QemuFileBinInputVisitor *iv = to_iv(v);
+    GenericList *entry;
+
+    if (!qfbi_is_list(iv)) {
+        error_set(errp, QERR_UNDEFINED_ERROR);
+    }
+
+    /* Some users maintain their own list structure */
+    if (!list) {
+        return NULL;
+    }
+
+    entry = g_malloc0(sizeof(*entry));
+    if (*list) {
+        (*list)->next = entry;
+    }
+
+    *list = entry;
+    return entry;
+}
+
+static void qfbi_end_list(Visitor *v, Error **errp)
+{
+    QemuFileBinInputVisitor *iv = to_iv(v);
+    StackEntry *e = qfbi_pop(iv);
+    if (!e || e->type != QFIV_LIST) {
+        error_set(errp, QERR_UNDEFINED_ERROR);
+        return;
+    }
+    g_free(e);
+}
+
+static void qfbi_start_array(Visitor *v, void **obj, const char *name,
+                             size_t elem_count, size_t elem_size, Error **errp)
+{
+    QemuFileBinInputVisitor *iv = to_iv(v);
+    ArrayInfo ai = {
+        .elem_count = elem_count,
+        .elem_size = elem_size,
+        .pos = 0
+    };
+    if (obj && (*obj == NULL) && elem_size) {
+        *obj = g_malloc0(elem_count * elem_size);
+    }
+    qfbi_push_array(iv, ai);
+}
+
+static void qfbi_next_array(Visitor *v, Error **errp)
+{
+    QemuFileBinInputVisitor *iv = to_iv(v);
+    StackEntry *e = QTAILQ_FIRST(&iv->stack);
+
+    if (!qfbi_is_array(iv) ||
+        e->array_info.pos >= e->array_info.elem_count) {
+        error_set(errp, QERR_UNDEFINED_ERROR);
+    }
+
+    e->array_info.pos++;
+}
+
+static void qfbi_end_array(Visitor *v, Error **errp)
+{
+    QemuFileBinInputVisitor *iv = to_iv(v);
+    StackEntry *e = qfbi_pop(iv);
+    if (!e || e->type != QFIV_ARRAY) {
+        error_set(errp, QERR_UNDEFINED_ERROR);
+        return;
+    }
+    g_free(e);
+}
+
+static void qfbi_type_str(Visitor *v, char **obj, const char *name,
+                                  Error **errp)
+{
+    if (obj) {
+        g_free(*obj);
+    }
+}
+
+/* Read in a byte+buffer -> giving a string.  obj must be a buffer of
+ * at least 256 chars in length
+ */
+static void qfbi_type_str256(Visitor *v, char *obj, const char *name,
+                             Error **errp)
+{
+    QemuFileBinInputVisitor *iv = to_iv(v);
+    unsigned int len = qemu_get_byte(iv->file);
+
+    qemu_get_buffer(iv->file, (uint8_t *)obj, len);
+    obj[len] = 0;
+    DPRINTF(iv, "for '%s' len=%d str=%s", name, len, obj);
+}
+
+static void qfbi_type_buffer(Visitor *v, void *data, size_t len, bool async,
+                             const char *name, Error **errp)
+{
+    QemuFileBinInputVisitor *iv = to_iv(v);
+    qemu_get_buffer(iv->file, data, len);
+}
+
+static void qfbi_type_uint8(Visitor *v, uint8_t *obj, const char *name,
+                            Error **errp)
+{
+    QemuFileBinInputVisitor *iv = to_iv(v);
+    *obj = qemu_get_byte(iv->file);
+    DPRINTF(iv, "for '%s' / %u", name, *obj);
+}
+
+static void qfbi_type_uint16(Visitor *v, uint16_t *obj, const char *name,
+                             Error **errp)
+{
+    QemuFileBinInputVisitor *iv = to_iv(v);
+    qemu_get_be16s(iv->file, obj);
+    DPRINTF(iv, "for '%s' / %u", name, *obj);
+}
+
+static void qfbi_type_uint32(Visitor *v, uint32_t *obj, const char *name,
+                             Error **errp)
+{
+    QemuFileBinInputVisitor *iv = to_iv(v);
+    qemu_get_be32s(iv->file, obj);
+    DPRINTF(iv, "for '%s' / %u", name, *obj);
+}
+
+static void qfbi_type_uint64(Visitor *v, uint64_t *obj, const char *name,
+                             Error **errp)
+{
+    QemuFileBinInputVisitor *iv = to_iv(v);
+    qemu_get_be64s(iv->file, obj);
+    DPRINTF(iv, "for '%s' / %lu", name, *obj);
+}
+
+static void qfbi_type_int8(Visitor *v, int8_t *obj, const char *name,
+                           Error **errp)
+{
+    QemuFileBinInputVisitor *iv = to_iv(v);
+    *obj = qemu_get_sbyte(iv->file);
+    DPRINTF(iv, "for '%s' / %d", name, *obj);
+}
+
+static void qfbi_type_int16(Visitor *v, int16_t *obj, const char *name,
+                            Error **errp)
+{
+    QemuFileBinInputVisitor *iv = to_iv(v);
+    qemu_get_sbe16s(iv->file, obj);
+    DPRINTF(iv, "for '%s' / %d", name, *obj);
+}
+
+static void qfbi_type_int32(Visitor *v, int32_t *obj, const char *name,
+                            Error **errp)
+{
+    QemuFileBinInputVisitor *iv = to_iv(v);
+    qemu_get_sbe32s(iv->file, obj);
+    DPRINTF(iv, "for '%s' / %d", name, *obj);
+}
+
+static void qfbi_type_int64(Visitor *v, int64_t *obj, const char *name,
+                            Error **errp)
+{
+    QemuFileBinInputVisitor *iv = to_iv(v);
+    qemu_get_sbe64s(iv->file, obj);
+    DPRINTF(iv, "for '%s' / %ld", name, *obj);
+}
+
+static void qfbi_type_bool(Visitor *v, bool *obj, const char *name,
+                           Error **errp)
+{
+    uint8_t val;
+    qfbi_type_uint8(v, &val, name, errp);
+    *obj = val;
+}
+
+static QEMUFile *qfbi_get_qemufile(Visitor *v)
+{
+    QemuFileBinInputVisitor *iv = to_iv(v);
+
+    return iv->file;
+}
+
+static void qfbi_get_next_type(Visitor *v, int *kind, const int *qobjects,
+                               const char *name, Error **errp)
+{
+    QemuFileBinInputVisitor *iv = to_iv(v);
+    SeqCompatInfo *sci;
+    uint64_t tmp64;
+    uint8_t tmpbyte;
+
+    DPRINTF(iv, "for '%s'", name);
+    if (qfbi_is_seqcompat(iv, &sci)) {
+        DPRINTF(iv, "/seqcompat for '%s'", name);
+        if (sci->hit_end) {
+            error_setg(errp, "Attempted to read beyond the end of list '%s'",
+                       name);
+            *kind = -1;
+            return;
+        }
+
+        DPRINTF(iv, "/seqcompat for '%s' mode=%d tmpbyte=%d", name, sci->mode,
+                tmpbyte);
+        switch (sci->mode) {
+        case VISIT_SEQ_COMPAT_BYTE0TERM:
+            /*
+             * End of the list is marked by a 0 byte,
+             * we'll consume the byte read
+             */
+            tmpbyte = qemu_get_byte(iv->file);
+            if (tmpbyte == 0) {
+                sci->hit_end = true;
+            }
+            *kind = tmpbyte;
+            break;
+
+        case VISIT_SEQ_COMPAT_RAMSECLIST:
+            /*
+             * the RAM sections are lists of entries each that start with a
+             * 64 bit word that has an address or length, OR'd with a set of
+             * the RAM_SAVE_FLAG_ bits; RAM_SAVE_FLAG_EOS indicates the end
+             * of the list.
+             * We don't consume the word since it'll be consumed by
+             * the SEQ_COMPAT_RAMSECENTRY; except for the EOS
+             */
+
+            if (qemu_peek_buffer(iv->file, (uint8_t *)&tmp64, 8, 0) != 8) {
+                error_setg(errp, "Failed to read flag word for '%s'", name);
+                *kind = -1;
+                return;
+            }
+
+            tmp64 = be64_to_cpu(tmp64);
+
+            *kind = tmp64 & RAM_SAVE_FLAG_MASK;
+            DPRINTF(iv, "tmp64=0x%lx *kind=%x\n", tmp64, *kind);
+            if (tmp64 & RAM_SAVE_FLAG_EOS) {
+                tmp64 = qemu_get_be64(iv->file); /* Consume terminator */
+                sci->hit_end = true;
+            }
+            break;
+
+        case VISIT_SEQ_COMPAT_SUBSECLIST:
+            /* In theory subsections are terminated by a
+             * byte != QEMU_VM_SUBSECTION
+             * and that byte is part of whatever comes next, so we mustn't
+             * consume the terminator if it's not valid.
+             * However, it's worse than that, historical layouts weren't
+             * really that careful about what came next, so you can get
+             * false sections, so we check everything in the header and if
+             * it's not valid we declare it end-of-section.
+             * For this case the 'opaque' passed in is the parent string
+             * name.
+             */
+            if (qemu_peek_buffer(iv->file, &tmpbyte, 1, 0) != 1) {
+                error_setg(errp, "Failed to read type byte for '%s'", name);
+                *kind = -1;
+                return;
+            }
+
+            if (tmpbyte == QEMU_VM_SUBSECTION) {
+                uint8_t len, size;
+                char idstr[256];
+                char *parent_name = (char *)sci->data;
+                size_t pn_len = strlen(parent_name);
+                DPRINTF(iv, "/subsection for '%s'/%s", name, parent_name);
+
+                len = qemu_peek_byte(iv->file, 1);
+                if (len < (pn_len + 1)) {
+                    DPRINTF(iv, "/subsection for '%s'/%s - len too short",
+                            name, parent_name);
+                    sci->hit_end = true;
+                    *kind = 0xff;
+                    break;
+                }
+                size = qemu_peek_buffer(iv->file, (uint8_t *)idstr,
+                                        len, 2);
+                if (size != len) {
+                    DPRINTF(iv, "/subsection for '%s'/%s - size!=len",
+                            name, parent_name);
+                    sci->hit_end = true;
+                    *kind = 0xff;
+                    break;
+                }
+                idstr[size] = 0;
+
+                if (strncmp(parent_name, idstr, pn_len) != 0) {
+                    DPRINTF(iv, "/subsection for n='%s' / pn='%s' "
+                               "idstr='%s' - invalid subsection name",
+                               name, parent_name, idstr);
+                    /* it don't have a valid subsection name */
+                    sci->hit_end = true;
+                    *kind = 0xff;
+                    break;
+                }
+                qemu_file_skip(iv->file, 1); /* subsection byte */
+
+                /* We're not going to consume the name or idstr here
+                 * since they're logically part of the item not the
+                 * list, so they're going to get re-read.
+                 */
+                DPRINTF(iv, "/subsection for '%s' got %s from %s "
+                           "tmpbyte=%d", name, idstr, parent_name,
+                           tmpbyte);
+            } else {
+                sci->hit_end = true;
+            }
+            *kind = tmpbyte;
+            break;
+
+        default:
+            *kind = -1;
+            error_set(errp, QERR_UNDEFINED_ERROR);
+            return;
+        }
+        return;
+    }
+
+    /* Only dealing with SeqCompat's for the moment */
+    error_set(errp, QERR_UNDEFINED_ERROR);
+}
+
+static void qfbi_start_sequence_compat(Visitor *v, const char *name,
+                                       Visit_seq_compat_mode compat_mode,
+                                       void *opaque, Error **errp)
+{
+    QemuFileBinInputVisitor *iv = to_iv(v);
+    SeqCompatInfo sci = {
+        .mode = compat_mode,
+        .data = opaque
+    };
+    SectionHeader *sh;
+    ramsecentry_header *rse_hdr;
+    unsigned int len;
+    uint32_t tmp;
+    uint64_t tmp64;
+
+    switch (compat_mode) {
+    case VISIT_SEQ_COMPAT_FILE:
+        tmp = qemu_get_be32(iv->file);
+        if (tmp != QEMU_VM_FILE_MAGIC) {
+            error_setg(errp, "Incorrect SaveVM file header (read)");
+            return;
+        }
+
+        tmp = qemu_get_be32(iv->file);
+        if (tmp == QEMU_VM_FILE_VERSION_COMPAT) {
+            error_setg(errp, "SaveVM v2 format is obsolete and will not load");
+            return;
+        }
+        if (tmp != QEMU_VM_FILE_VERSION) {
+            error_setg(errp, "Unsupported SaveVM format (%d)", tmp);
+            return;
+        }
+        sci.hit_end = true; /* It doesn't iterate over this as a list */
+        break;
+
+    case VISIT_SEQ_COMPAT_SECTION_HEADER:
+        /*
+         *  for VM_SECTION_FULL and VM_SECTION_START
+         * 'opaque' points to a struct Sectionheader
+         */
+        sh = opaque;
+        sh->section_id = qemu_get_be32(iv->file);
+        len = qemu_get_byte(iv->file);
+        qemu_get_buffer(iv->file, (uint8_t *)sh->idstr, len);
+        sh->idstr[len] = 0;
+        sh->instance_id = qemu_get_be32(iv->file);
+        sh->version_id = qemu_get_be32(iv->file);
+        DPRINTF(iv, "for '%s'/%s v %d", name, sh->idstr, sh->version_id);
+        sci.hit_end = true; /* It doesn't iterate over this as a list */
+        break;
+
+    case VISIT_SEQ_COMPAT_SECTION_MIN:
+        /* for VM_SECTION_PART?END where the section ID is already known */
+        sh = opaque;
+        sh->section_id = qemu_get_be32(iv->file);
+        sci.hit_end = true; /* It doesn't iterate over this as a list */
+        break;
+
+
+    case VISIT_SEQ_COMPAT_VMSTATE:
+        /* These don't need anything in the header on the compatibility side */
+        sci.hit_end = true; /* It doesn't iterate over this as a list */
+        break;
+
+    case VISIT_SEQ_COMPAT_BYTE0TERM:
+    case VISIT_SEQ_COMPAT_SUBSECLIST:
+    case VISIT_SEQ_COMPAT_RAMSECLIST:
+        /* These don't need anything in the header on the compatibility side */
+        break;
+
+    case VISIT_SEQ_COMPAT_RAMSECENTRY:
+        rse_hdr = opaque;
+        tmp64 = qemu_get_be64(iv->file);
+        rse_hdr->flags = tmp64 & RAM_SAVE_FLAG_MASK;
+        rse_hdr->addr = tmp64 - rse_hdr->flags;
+        if ((rse_hdr->flags &
+             (RAM_SAVE_FLAG_CONTINUE | RAM_SAVE_FLAG_MEM_SIZE |
+              RAM_SAVE_FLAG_HOOK)) == 0) {
+            len = qemu_get_byte(iv->file);
+            qemu_get_buffer(iv->file, (uint8_t *)rse_hdr->idstr, len);
+            rse_hdr->idstr[len] = 0;
+        }
+        sci.hit_end = true; /* It doesn't iterate over this as a list */
+        break;
+
+    case VISIT_SEQ_COMPAT_SUBSECTION: /* An element in a subsection list */
+        sh = opaque;
+        len = qemu_get_byte(iv->file);
+        qemu_get_buffer(iv->file, (uint8_t *)sh->idstr, len);
+        sh->idstr[len] = 0;
+        sh->version_id = qemu_get_be32(iv->file);
+        DPRINTF(iv, "for '%s'/%s v %d", name, sh->idstr, sh->version_id);
+        sci.hit_end = true; /* It doesn't iterate over this as a list */
+        break;
+
+    case VISIT_SEQ_COMPAT_BLOB:
+        /* Nothing in the header, but opaque gets a copy of our QEMUFile -
+         * other implementations might give a different QEMUFile
+         */
+        *(QEMUFile **)opaque = iv->file;
+        sci.hit_end = true; /* It doesn't iterate over this as a list */
+        break;
+
+    }
+
+    DPRINTF(iv, "for '%s'", name);
+    qfbi_push_seqcompat(iv, sci);
+
+
+    /* We don't need to read anything at this point */
+}
+
+static void qfbi_end_sequence_compat(Visitor *v, const char* name,
+                                     Visit_seq_compat_mode compat_mode,
+                                     Error **errp)
+{
+    QemuFileBinInputVisitor *iv = to_iv(v);
+    StackEntry *e = qfbi_pop(iv);
+    DPRINTF(iv, "> for '%s'", name);
+    if (!e || e->type != QFIV_SEQCOMPAT) {
+        error_setg(errp, "bad struct stack %d", e ? e->type : -1);
+        if (e) {
+            g_free(e);
+        }
+        return;
+    }
+    if (e->seqcompat_info.mode != compat_mode) {
+        error_setg(errp, "mismatched seqcompat mode %d/%d", compat_mode,
+                   e->seqcompat_info.mode);
+    }
+    if (!*errp && !e->seqcompat_info.hit_end) {
+        error_setg(errp, "Didn't read the whole of list for '%s'", name);
+    }
+    g_free(e);
+}
+
+static void qfbi_destroy(Visitor *v, Error **errp)
+{
+    QemuFileBinInputVisitor *iv = to_iv(v);
+
+    qemu_file_bin_input_visitor_cleanup(iv);
+}
+
+Visitor *qemu_file_bin_input_get_visitor(QemuFileBinInputVisitor *iv)
+{
+    return &iv->visitor;
+}
+
+void qemu_file_bin_input_visitor_cleanup(QemuFileBinInputVisitor *iv)
+{
+    g_free(iv);
+}
+
+QemuFileBinInputVisitor *qemu_file_bin_input_visitor_new(QEMUFile *f)
+{
+    QemuFileBinInputVisitor *v;
+
+    v = g_malloc0(sizeof(*v));
+
+    v->file = f;
+
+    v->visitor.start_struct = qfbi_start_struct;
+    v->visitor.end_struct = qfbi_end_struct;
+    v->visitor.start_list = qfbi_start_list;
+    v->visitor.next_list = qfbi_next_list;
+    v->visitor.end_list = qfbi_end_list;
+    v->visitor.start_array = qfbi_start_array;
+    v->visitor.next_array = qfbi_next_array;
+    v->visitor.end_array = qfbi_end_array;
+    v->visitor.type_int = qfbi_type_int64;
+    v->visitor.type_buffer = qfbi_type_buffer;
+    v->visitor.type_uint8 = qfbi_type_uint8;
+    v->visitor.type_uint16 = qfbi_type_uint16;
+    v->visitor.type_uint32 = qfbi_type_uint32;
+    v->visitor.type_uint64 = qfbi_type_uint64;
+    v->visitor.type_int8 = qfbi_type_int8;
+    v->visitor.type_int16 = qfbi_type_int16;
+    v->visitor.type_int32 = qfbi_type_int32;
+    v->visitor.type_int64 = qfbi_type_int64;
+    v->visitor.type_bool = qfbi_type_bool;
+    v->visitor.type_str = qfbi_type_str;
+    v->visitor.type_str256 = qfbi_type_str256;
+    v->visitor.destroy = qfbi_destroy;
+    v->visitor.start_sequence_compat = qfbi_start_sequence_compat;
+    v->visitor.get_next_type = qfbi_get_next_type;
+    v->visitor.end_sequence_compat = qfbi_end_sequence_compat;
+    v->visitor.get_qemufile = qfbi_get_qemufile;
+
+    v->visitor.flags = VISITOR_LOADING;
+
+    QTAILQ_INIT(&v->stack);
+    v->depth = 0;
+
+    return v;
+}