diff mbox

[RFC,v2,12/16] BER Visitor: Create input visitor

Message ID 1398271069-22057-13-git-send-email-dgilbert@redhat.com
State New
Headers show

Commit Message

Dr. David Alan Gilbert April 23, 2014, 4:37 p.m. UTC
From: "Dr. David Alan Gilbert" <dgilbert@redhat.com>

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

Patch

diff --git a/include/qapi/qemu-file-ber-input-visitor.h b/include/qapi/qemu-file-ber-input-visitor.h
new file mode 100644
index 0000000..18045f7
--- /dev/null
+++ b/include/qapi/qemu-file-ber-input-visitor.h
@@ -0,0 +1,26 @@ 
+/*
+ * QEMUFile input visitor for BER format files
+ *
+ * Copyright 2014 Red Hat, Inc. and/or its affiliates
+ *
+ * Authors:
+ *  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.
+ *
+ */
+
+#ifndef QEMU_FILE_BER_INPUT_VISITOR_H
+#define QEMU_FILE_BER_INPUT_VISITOR_H
+
+#include "visitor.h"
+
+typedef struct QemuFileBERInputVisitor QemuFileBERInputVisitor;
+
+QemuFileBERInputVisitor *qemu_file_ber_input_visitor_new(QEMUFile *f);
+void qemu_file_ber_input_visitor_cleanup(QemuFileBERInputVisitor *d);
+
+Visitor *qemu_file_ber_input_get_visitor(QemuFileBERInputVisitor *v);
+
+#endif
diff --git a/qapi/Makefile.objs b/qapi/Makefile.objs
index 3de3d2b..0639c09 100644
--- a/qapi/Makefile.objs
+++ b/qapi/Makefile.objs
@@ -2,5 +2,5 @@  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 qemu-file-binary-input-visitor.o
-util-obj-y += qemu-file-ber-output-visitor.o
+util-obj-y += qemu-file-ber-input-visitor.o qemu-file-ber-output-visitor.o
 util-obj-y += opts-visitor.o
diff --git a/qapi/qemu-file-ber-input-visitor.c b/qapi/qemu-file-ber-input-visitor.c
new file mode 100644
index 0000000..7da3cf7
--- /dev/null
+++ b/qapi/qemu-file-ber-input-visitor.c
@@ -0,0 +1,1575 @@ 
+/*
+ * QEMUFile input visitor for BER format
+ *
+ * Copyright IBM, Corp. 2011, 2013
+ * Copyright Red Hat, Corp. 2011,2014
+ *
+ * Authors:
+ *  Anthony Liguori   <aliguori@us.ibm.com>
+ *  David Gilbert  <dgilbert@redhat.com>
+ *  Michael Tsirkin   <mst@redhat.com>
+ *  Stefan Berger     <stefanb@us.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.
+ *
+ */
+
+#include "qapi/ber.h"
+#include "qapi/qemu-file-ber-input-visitor.h"
+#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"
+
+#if 0
+#define DPRINTF(v, fmt, ...) \
+    do { \
+        fprintf(stderr, "%*s qfberiv/%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;
+    QEMUFile             *binfile;
+    bool                  in_block_list;
+    bool                  in_ramsecblock;
+    bool                  pending_hook; /* For RAMSecList to pass to Entry */
+} SeqCompatInfo;
+
+typedef struct StackEntry {
+    enum {
+        QFIV_ARRAY = 0,
+        QFIV_LIST,
+        QFIV_STRUCT,
+        QFIV_SEQCOMPAT,
+    } type;
+    ArrayInfo array_info;
+    SeqCompatInfo seqcompat_info;
+    QTAILQ_ENTRY(StackEntry) node;
+} StackEntry;
+
+struct QemuFileBERInputVisitor {
+    Visitor visitor;
+    QTAILQ_HEAD(, StackEntry) stack;
+    QEMUFile *file;
+    unsigned int depth;
+};
+
+static void dump_visitor_stack(QemuFileBERInputVisitor *iv)
+{
+    StackEntry *e;
+    error_report("Visitor stack (depth=%u)", iv->depth);
+    QTAILQ_FOREACH(e, &iv->stack, node) {
+        switch (e->type) {
+        case QFIV_ARRAY:
+           error_report("  Array: %zd/%zd/%zd",
+                        e->array_info.elem_count,
+                        e->array_info.elem_size,
+                        e->array_info.pos);
+           break;
+
+        case QFIV_LIST:
+           error_report("  List");
+           break;
+
+        case QFIV_STRUCT:
+           error_report("  Struct");
+           break;
+
+        case QFIV_SEQCOMPAT:
+           error_report("  SeqCompat: %d", e->seqcompat_info.mode);
+           break;
+        }
+    }
+}
+
+static QemuFileBERInputVisitor *to_iv(Visitor *v)
+{
+    return container_of(v, QemuFileBERInputVisitor, visitor);
+}
+
+static void qfberi_push(QemuFileBERInputVisitor *iv, StackEntry *e)
+{
+    QTAILQ_INSERT_HEAD(&iv->stack, e, node);
+    iv->depth++;
+}
+
+static void qfberi_push_array(QemuFileBERInputVisitor *iv,
+                                           ArrayInfo ai)
+{
+    StackEntry *e = g_malloc0(sizeof(*e));
+    e->type = QFIV_ARRAY;
+    e->array_info = ai;
+    qfberi_push(iv, e);
+}
+
+static void qfberi_push_list(QemuFileBERInputVisitor *iv)
+{
+    StackEntry *e = g_malloc0(sizeof(*e));
+    e->type = QFIV_LIST;
+    qfberi_push(iv, e);
+}
+
+static void qfberi_push_seqcompat(QemuFileBERInputVisitor *iv,
+                                               SeqCompatInfo sci)
+{
+    StackEntry *e = g_malloc0(sizeof(*e));
+    e->type = QFIV_SEQCOMPAT;
+    e->seqcompat_info = sci;
+    qfberi_push(iv, e);
+}
+
+static void qfberi_push_struct(QemuFileBERInputVisitor *iv)
+{
+    StackEntry *e = g_malloc0(sizeof(*e));
+    e->type = QFIV_STRUCT;
+    qfberi_push(iv, e);
+}
+
+static void *qfberi_pop(QemuFileBERInputVisitor *iv)
+{
+    StackEntry *e = QTAILQ_FIRST(&iv->stack);
+    if (e) {
+        QTAILQ_REMOVE(&iv->stack, e, node);
+        iv->depth--;
+    }
+    return e;
+}
+
+static bool qfberi_is_array(QemuFileBERInputVisitor *iv)
+{
+    StackEntry *e = QTAILQ_FIRST(&iv->stack);
+    return e->type == QFIV_ARRAY;
+}
+
+static bool qfberi_is_list(QemuFileBERInputVisitor *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 qfberi_is_seqcompat(QemuFileBERInputVisitor *iv,
+                              SeqCompatInfo **sci)
+{
+    StackEntry *e = QTAILQ_FIRST(&iv->stack);
+    if (e && e->type == QFIV_SEQCOMPAT) {
+        *sci = &e->seqcompat_info;
+        return true;
+    }
+    return false;
+}
+
+/*
+ * Read a type tag from the stream. Up-to 32 bit type tags are supported
+ * for reading and otherwise an error is returned. Anything larger than that
+ * would not be reasonable and could only be abused.
+ *
+ *    peek: Don't consume the type from the stream
+ *
+ * Returns: the length of the type tag (or 0 on error)
+ */
+static size_t ber_read_type(QemuFileBERInputVisitor *iv, uint32_t *ber_type,
+                            uint8_t *ber_type_flags, bool peek, Error **errp)
+{
+    uint32_t type;
+    uint8_t byte;
+    uint8_t ctr = 0;
+    size_t byte_counter = 0;
+
+    if (*errp) { /* Skip if already in error */
+        return 0;
+    }
+
+    if (qemu_peek_buffer(iv->file, &byte, 1, byte_counter++) != 1) {
+        error_setg(errp, "QEMUFile has an error, error was '%s'",
+                  "Error while reading type");
+        return 0;
+    }
+    type = byte;
+
+    *ber_type_flags = type & (BER_TYPE_P_C_MASK | BER_TYPE_CLASS_MASK);
+
+    if ((type & BER_TYPE_TAG_MASK) == BER_TYPE_LONG_FORM) {
+        type = 0;
+        while (true) {
+            type <<= 7;
+            if (qemu_peek_buffer(iv->file, &byte, 1, byte_counter++) != 1) {
+                error_setg(errp, "QEMUFile has an error, error was '%s'",
+                          "Error while reading long type");
+                return 0;
+            }
+
+            type |= (byte & 0x7f);
+            if ((byte & 0x80) == 0) {
+                break;
+            }
+            ctr += 7; /* read 7 bits */
+            if (ctr >= (sizeof(type) * 8)) {
+                /* only support 32 bit type tags */
+                error_setg(errp, "Data stream is invalid; type tag is larger "
+                                 "than 32 bit");
+                return 0;
+            }
+        }
+    } else {
+        type &= BER_TYPE_TAG_MASK;
+    }
+
+    if (!peek) {
+        qemu_file_skip(iv->file, byte_counter);
+    }
+    *ber_type = type;
+
+    return byte_counter;
+}
+
+/*
+ * Read the length for a BER type, (returning a flag if it's indefinite)
+ * If 'peek' is true the data is not consumed
+ * 'offset' should generally be 0 (and must be if !peek), but allows
+ *    the length to be peeked further into the file - as long as the previous
+ *    bytes have been read or peeked.  e.g. a type and it's length can be
+ *    peeked.
+ */
+static uint64_t ber_read_length(QemuFileBERInputVisitor *iv,
+                                bool *is_indefinite, bool peek,
+                                unsigned int offset, Error **errp)
+{
+    uint8_t byte, c, int_len;
+    uint64_t len = 0;
+    unsigned char int_array[sizeof(len)];
+    char buf[128];
+    unsigned int byte_counter = offset;
+
+    assert((offset == 0) || peek);
+
+    *is_indefinite = false;
+
+    if (qemu_peek_buffer(iv->file, &byte, 1, byte_counter++) != 1) {
+        error_setg(errp, "QEMUFile has an error, error was '%s'",
+                  "Error while reading length indicator");
+        return ~0x0ULL;
+    }
+
+    if (byte == BER_LENGTH_INDEFINITE) {
+        *is_indefinite = true;
+
+        if (!peek) {
+            qemu_file_skip(iv->file, byte_counter);
+        }
+        return ~0x0ULL;
+    }
+
+    if (!(byte & BER_LENGTH_LONG)) {
+        len = byte;
+    } else {
+        int_len = byte & BER_LENGTH_MASK;
+        if (int_len > sizeof(len)) {
+            snprintf(buf, sizeof(buf),
+                     "ASN.1 integer length field %d > %" PRIu64,
+                     int_len, sizeof(len));
+            /* Length can be up to 127 byte, but it seems
+             * safe to assume any input will be < 1TB in length. */
+            error_set(errp, QERR_INVALID_PARAMETER, buf);
+            return ~0x0ULL;
+        }
+        if (qemu_peek_buffer(iv->file, int_array, int_len, byte_counter)
+            != int_len) {
+            error_setg(errp, "QEMUFile error: Error while reading length");
+            return ~0x0ULL;
+        }
+        byte_counter += int_len;
+        for (c = 0; c < int_len; c++) {
+            len <<= 8;
+            len |= int_array[c];
+        }
+    }
+    if (!peek) {
+        qemu_file_skip(iv->file, byte_counter);
+    }
+
+    return len;
+}
+
+/* Read a type tag from the stream, and compare it to the expected type and
+ * flags.
+ *   get_length_indef - if true also fetch the length, which is expected to be
+ *                      indefinite
+ * Return:
+ *   True if it matched
+ *   False if it failed to read, or failed to match
+ *     setting errp to explain
+ */
+static bool ber_expect_type(QemuFileBERInputVisitor *iv,
+                            enum ber_type_tag expected_type,
+                            uint8_t expected_flags, bool get_length_indef,
+                            Error **errp)
+{
+    uint32_t read_type;
+    uint8_t read_flags = 0; /* Silence compiler warning of possible uninit */
+
+    ber_read_type(iv, &read_type, &read_flags, false, errp);
+    if (*errp) {
+        return false;
+    }
+
+    if ((expected_flags != read_flags) || (expected_type != read_type)) {
+        dump_visitor_stack(iv);
+        error_setg(errp, "Read incorrect BER types/flags; expecting %d/%d "
+                         "read %d/%d",
+                         expected_type, expected_flags,
+                         read_type, read_flags);
+        return false;
+    }
+
+    DPRINTF(iv, "got match %d/%d", expected_type, expected_flags);
+
+    if (get_length_indef) {
+        bool is_indef;
+        ber_read_length(iv, &is_indef, false, 0, errp);
+        if (*errp) {
+            return false;
+        }
+        if (!is_indef) {
+            dump_visitor_stack(iv);
+            error_setg(errp, "Got defined length, expecting indef (for %d/%d)",
+                       expected_type, expected_flags);
+            return false;
+        }
+    }
+    return true;
+}
+
+/* The next entry is expected to be an EOC (0 0)
+ * read and error if not found.
+ */
+static void ber_consume_eoc(QemuFileBERInputVisitor *iv, Error **errp)
+{
+    uint16_t tmp;
+
+    qemu_get_be16s(iv->file, &tmp);
+
+    if (tmp != 0) {
+        error_setg(errp, "Expecting EOC but found %x", tmp);
+    }
+}
+
+/*
+ * There are a few places where Application class Null's are used as marks
+ * This function checks that we have one of the right type.
+ * Returns true on success.
+ */
+static bool ber_consume_special_null(QemuFileBERInputVisitor *iv,
+                                    const char *descr,
+                                    enum ber_type_tag expected_type,
+                                    Error **errp)
+{
+    bool is_indef;
+    /* It's effectively a NULL, should be 0 length */
+    if (!ber_expect_type(iv,
+                         expected_type, BER_TYPE_CLASS_APPLICATION |
+                             BER_TYPE_PRIMITIVE,
+                         false, errp)) {
+        error_setg(errp, "Unable to read %s", descr);
+        return false;
+    }
+    if (ber_read_length(iv, &is_indef, false, 0, errp) ||
+        is_indef) {
+        error_setg(errp, "None 0 NULL length for %s", descr);
+        return false;
+    }
+
+    return true;
+}
+
+
+/* Read an integer from the stream, with the given type/flags
+ * (typically integer/(primitive|universal) - but can be app specific
+ *   obj - pointer to uint64_t to hold result
+ *   intsize - Integer byte size the result must fit into (i.e. 1 for
+ *             uint8_t, 2 for uint16_t etc)
+ *   is_signed - True if the integer can be signed (in which case
+ *               obj is treated as an int64_t
+ */
+static void ber_input_integer(QemuFileBERInputVisitor *iv, uint64_t *obj,
+                              uint8_t intsize, bool is_signed,
+                              enum ber_type_tag expected_type,
+                              uint8_t expected_flags,
+                              Error **errp)
+{
+    bool is_indefinite;
+    uint64_t len;
+    uint64_t val = 0;
+    unsigned int maxbytes;
+    unsigned char int_array[sizeof(val)+1];
+    int c;
+
+    assert(intsize <= 8);
+
+    if (!ber_expect_type(iv, expected_type, expected_flags, false, errp)) {
+        return;
+    }
+
+    len = ber_read_length(iv, &is_indefinite, false, 0, errp);
+    if (error_is_set(errp)) {
+        return;
+    }
+
+    if (is_indefinite) {
+        error_set(errp, QERR_INVALID_PARAMETER_VALUE,
+                  "BER int indicator is indefinite",
+                  "[1..8]");
+        return;
+    }
+
+    /*
+     * Since BER ints are always signed, an n-byte unsigned int might need
+     * a 00 prefix byte so that it isn't signed.
+     */
+    maxbytes = intsize + (is_signed ? 0 : 1);
+    DPRINTF(iv, "len=%" PRIu64 " intsize=%d maxbytes=%d", len, intsize,
+            maxbytes);
+
+    if (len > maxbytes) {
+        char buf[128];
+        snprintf(buf, sizeof(buf), "BER integer length indicator %" PRIi64
+                 " is larger than expected (%u bytes)",
+                 len, maxbytes);
+        error_set(errp, QERR_INVALID_PARAMETER_VALUE,
+                  buf, "[1..8]");
+        return;
+    }
+
+    if (qemu_get_buffer(iv->file, int_array, len) != len) {
+        error_setg(errp, "QEMUFile error: Error while reading integer");
+        return;
+    }
+
+    if (!is_signed && len == (intsize+1)) {
+        /*
+         *  In the unsigned case, the extra byte can only be a 00 to indicate
+         * the lack of sign, anything else and it's too big to fit in the int
+         */
+        if (int_array[0]) {
+            error_setg(errp, "BER Integer; invalid 1st byte (%x) on unsigned",
+                       int_array[0]);
+            return;
+        }
+    }
+
+    for (c = 0; c < len ; c++) {
+        val <<= 8;
+        val |= int_array[c];
+        if (c == 0 && (val & 0x80) == 0x80) {
+            if (!is_signed) {
+                error_setg(errp, "Unsigned integer received with sign set");
+                return;
+            }
+            /* sign extend */
+            val |= 0xffffffffffffff00ULL;
+        }
+    }
+
+    *obj = val;
+    DPRINTF(iv, "Val=%" PRIx64 " len=%" PRIu64, val, len);
+}
+
+static void qfberi_start_struct(Visitor *v, void **obj,
+                                         const char *kind,
+                                         const char *name, size_t size,
+                                         Error **errp)
+{
+    QemuFileBERInputVisitor *iv = to_iv(v);
+    DPRINTF(iv, "for '%s' of '%s'", name, kind);
+
+    if (!ber_expect_type(iv, BER_TYPE_SEQUENCE,
+                    BER_TYPE_CLASS_UNIVERSAL | BER_TYPE_CONSTRUCTED,
+                    true, errp)) {
+        return;
+    }
+    qfberi_push_struct(iv);
+}
+
+static void qfberi_end_struct(Visitor *v, Error **errp)
+{
+    QemuFileBERInputVisitor *iv = to_iv(v);
+    StackEntry *e = qfberi_pop(iv);
+
+    DPRINTF(iv, "<");
+    if (!e || e->type != QFIV_STRUCT) {
+        error_set(errp, QERR_UNDEFINED_ERROR);
+        return;
+    }
+    ber_consume_eoc(iv, errp);
+    g_free(e);
+}
+
+static void qfberi_start_list(Visitor *v, const char *name,
+                                       Error **errp)
+{
+    QemuFileBERInputVisitor *iv = to_iv(v);
+    if (!ber_expect_type(iv, BER_TYPE_SEQUENCE,
+                    BER_TYPE_CLASS_UNIVERSAL | BER_TYPE_CONSTRUCTED,
+                    true, errp)) {
+        return;
+    }
+    qfberi_push_list(iv);
+}
+
+static GenericList *qfberi_next_list(Visitor *v, GenericList **list,
+                                           Error **errp)
+{
+    QemuFileBERInputVisitor *iv = to_iv(v);
+    GenericList *entry;
+
+    if (!qfberi_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 qfberi_end_list(Visitor *v, Error **errp)
+{
+    QemuFileBERInputVisitor *iv = to_iv(v);
+    StackEntry *e = qfberi_pop(iv);
+    if (!e || e->type != QFIV_LIST) {
+        error_set(errp, QERR_UNDEFINED_ERROR);
+        return;
+    }
+    ber_consume_eoc(iv, errp);
+    g_free(e);
+}
+
+static void qfberi_start_array(Visitor *v, void **obj,
+                                        const char *name,
+                                        size_t elem_count,
+                                        size_t elem_size,
+                                        Error **errp)
+{
+    QemuFileBERInputVisitor *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);
+    }
+    if (!ber_expect_type(iv, BER_TYPE_SEQUENCE,
+                    BER_TYPE_CLASS_UNIVERSAL | BER_TYPE_CONSTRUCTED,
+                    true, errp)) {
+        return;
+    }
+    qfberi_push_array(iv, ai);
+}
+
+static void qfberi_next_array(Visitor *v, Error **errp)
+{
+    QemuFileBERInputVisitor *iv = to_iv(v);
+    StackEntry *e = QTAILQ_FIRST(&iv->stack);
+
+    if (!qfberi_is_array(iv) ||
+        e->array_info.pos >= e->array_info.elem_count) {
+        error_set(errp, QERR_UNDEFINED_ERROR);
+    }
+
+    e->array_info.pos++;
+}
+
+static void qfberi_end_array(Visitor *v, Error **errp)
+{
+    QemuFileBERInputVisitor *iv = to_iv(v);
+    StackEntry *e = qfberi_pop(iv);
+    if (!e || e->type != QFIV_ARRAY) {
+        error_set(errp, QERR_UNDEFINED_ERROR);
+        return;
+    }
+    ber_consume_eoc(iv, errp);
+    g_free(e);
+}
+
+static void qfberi_type_str(Visitor *v, char **obj, const char *name,
+                                  Error **errp)
+{
+    if (obj) {
+        g_free(*obj);
+    }
+    assert(0); /* Not implemented yet for BER */
+}
+
+/* Read in a byte+buffer -> giving a string.  obj must be a buffer of
+ * at least 256 chars in length
+ */
+static void qfberi_type_str256(Visitor *v, char *obj, const char *name,
+                                  Error **errp)
+{
+    QemuFileBERInputVisitor *iv = to_iv(v);
+    uint64_t len;
+    bool read_indef;
+
+    obj[0] = '\0'; /* in case we errror out */
+    DPRINTF(iv, "for %s", name);
+
+    if (!ber_expect_type(iv, BER_TYPE_UTF8_STRING,
+                         BER_TYPE_PRIMITIVE | BER_TYPE_CLASS_UNIVERSAL,
+                         false, errp)) {
+        return;
+    }
+    len = ber_read_length(iv, &read_indef, false, 0, errp);
+    if (*errp) {
+        return;
+    }
+    DPRINTF(iv, "for %s len=%" PRIu64, name, len);
+    if (read_indef || (len > 255)) {
+        error_setg(errp, "Invalid length reading '%s' (len=%" PRIu64
+                   ") indef=%d", name, len, read_indef);
+        return;
+    }
+
+    if (qemu_get_buffer(iv->file, (uint8_t *)obj, len) != len) {
+        error_setg(errp, "QEMUFile error: Error while reading string '%s'",
+                   name);
+        return;
+    }
+
+    obj[len] = '\0';
+    DPRINTF(iv, "'%s' got %s", name, obj);
+}
+
+static void qfberi_type_buffer(Visitor *v, void *data, size_t len, bool async,
+                                          const char *name, Error **errp)
+{
+    QemuFileBERInputVisitor *iv = to_iv(v);
+    uint64_t read_len;
+    bool read_indef;
+
+    DPRINTF(iv, "for %s", name);
+    if (!ber_expect_type(iv, BER_TYPE_OCTET_STRING,
+                         BER_TYPE_PRIMITIVE | BER_TYPE_CLASS_UNIVERSAL,
+                         false, errp)) {
+        return;
+    }
+    read_len = ber_read_length(iv, &read_indef, false, 0, errp);
+    if (*errp) {
+        return;
+    }
+    DPRINTF(iv, "for %s len=%zu read_len=%" PRIu64, name, len, read_len);
+    if (read_indef || (len != read_len)) {
+        error_setg(errp, "Invalid length reading '%s' (len=%zd read_len=%"
+                   PRIu64 ") indef=%d", name, len, read_len, read_indef);
+        return;
+    }
+
+    if (qemu_get_buffer(iv->file, data, len) != len) {
+        error_setg(errp, "QEMUFile error: Error reading buffer for '%s'", name);
+    }
+    DPRINTF(iv, "'%s' read %zd bytes:", name, len);
+    /*qemu_hexdump(data, stderr, __FUNCTION__, len); */
+    /*DPRINTF(iv, "------------------");*/
+}
+
+static void qfberi_type_uint8(Visitor *v, uint8_t *obj,
+                                         const char *name, Error **errp)
+{
+    QemuFileBERInputVisitor *iv = to_iv(v);
+    uint64_t tmp64;
+    ber_input_integer(iv, &tmp64, 1, false, BER_TYPE_INTEGER,
+                      BER_TYPE_PRIMITIVE | BER_TYPE_CLASS_UNIVERSAL, errp);
+    *obj = (uint8_t)tmp64;
+}
+
+static void qfberi_type_uint16(Visitor *v, uint16_t *obj,
+                                          const char *name, Error **errp)
+{
+    QemuFileBERInputVisitor *iv = to_iv(v);
+    uint64_t tmp64;
+    ber_input_integer(iv, &tmp64, 2, false, BER_TYPE_INTEGER,
+                      BER_TYPE_PRIMITIVE | BER_TYPE_CLASS_UNIVERSAL, errp);
+    *obj = (uint16_t)tmp64;
+}
+
+static void qfberi_type_uint32(Visitor *v, uint32_t *obj,
+                                          const char *name, Error **errp)
+{
+    QemuFileBERInputVisitor *iv = to_iv(v);
+    uint64_t tmp64;
+    ber_input_integer(iv, &tmp64, 4, false, BER_TYPE_INTEGER,
+                      BER_TYPE_PRIMITIVE | BER_TYPE_CLASS_UNIVERSAL, errp);
+    *obj = (uint32_t)tmp64;
+}
+
+static void qfberi_type_uint64(Visitor *v, uint64_t *obj,
+                                          const char *name, Error **errp)
+{
+    QemuFileBERInputVisitor *iv = to_iv(v);
+    uint64_t tmp64;
+    ber_input_integer(iv, &tmp64, 8, false, BER_TYPE_INTEGER,
+                      BER_TYPE_PRIMITIVE | BER_TYPE_CLASS_UNIVERSAL, errp);
+    *obj = tmp64;
+}
+
+static void qfberi_type_int8(Visitor *v, int8_t *obj,
+                                        const char *name, Error **errp)
+{
+    QemuFileBERInputVisitor *iv = to_iv(v);
+    int64_t tmp64;
+    ber_input_integer(iv, (uint64_t *)&tmp64, 1, true, BER_TYPE_INTEGER,
+                      BER_TYPE_PRIMITIVE | BER_TYPE_CLASS_UNIVERSAL, errp);
+    *obj = (int8_t)tmp64;
+}
+
+static void qfberi_type_int16(Visitor *v, int16_t *obj,
+                                         const char *name, Error **errp)
+{
+    QemuFileBERInputVisitor *iv = to_iv(v);
+    int64_t tmp64;
+    ber_input_integer(iv, (uint64_t *)&tmp64, 2, true, BER_TYPE_INTEGER,
+                      BER_TYPE_PRIMITIVE | BER_TYPE_CLASS_UNIVERSAL, errp);
+    *obj = (int16_t)tmp64;
+}
+
+static void qfberi_type_int32(Visitor *v, int32_t *obj,
+                                         const char *name, Error **errp)
+{
+    QemuFileBERInputVisitor *iv = to_iv(v);
+    int64_t tmp64;
+    ber_input_integer(iv, (uint64_t *)&tmp64, 4, true, BER_TYPE_INTEGER,
+                      BER_TYPE_PRIMITIVE | BER_TYPE_CLASS_UNIVERSAL, errp);
+    *obj = (int32_t)tmp64;
+    DPRINTF(iv, "for '%s' / %d", name, *obj);
+}
+
+static void qfberi_type_int64(Visitor *v, int64_t *obj,
+                                         const char *name, Error **errp)
+{
+    QemuFileBERInputVisitor *iv = to_iv(v);
+    int64_t tmp64;
+    ber_input_integer(iv, (uint64_t *)&tmp64, 8, true, BER_TYPE_INTEGER,
+                      BER_TYPE_PRIMITIVE | BER_TYPE_CLASS_UNIVERSAL, errp);
+    *obj = tmp64;
+    DPRINTF(iv, "for '%s' / %ld", name, *obj);
+}
+
+static void qfberi_type_bool(Visitor *v, bool *obj, const char *name,
+                                     Error **errp)
+{
+    QemuFileBERInputVisitor *iv = to_iv(v);
+    uint8_t tmp;
+    if (!ber_expect_type(iv, BER_TYPE_BOOLEAN,
+                         BER_TYPE_PRIMITIVE | BER_TYPE_CLASS_UNIVERSAL,
+                         false, errp)) {
+        return;
+    }
+
+    tmp = qemu_get_byte(iv->file);
+    if (tmp != 1) {
+        error_setg(errp, "Invalid length (%d) for boolean '%s'", tmp, name);
+        return;
+    }
+
+    tmp = qemu_get_byte(iv->file);
+    *obj = (tmp != 0);
+}
+
+static QEMUFile *qfberi_get_qemufile(Visitor *v)
+{
+    QemuFileBERInputVisitor *iv = to_iv(v);
+
+    return iv->file;
+}
+
+/*
+ * Helper for get_next_type_ramseclist in the case in which we've just peeked
+ * the EOC on a ramsecblock.
+ */
+static void get_next_type_ramsecblock_eoc(QemuFileBERInputVisitor *iv,
+                                     int *kind, SeqCompatInfo *sci,
+                                     Error **errp)
+{
+    uint32_t read_type;
+    uint8_t read_flags;
+
+    /*
+     * The EOC we just peeked was the EOC for a 'pagelist'
+     * member of a RAMSecBlock; consume both of these and
+     * then see if there is another RAMSecBlock to read.
+     */
+    ber_consume_eoc(iv, errp); /* pagelist */
+    ber_consume_eoc(iv, errp); /* RAMSecBlock */
+    sci->in_ramsecblock = false;
+
+    /* Now, is there another RAMSecBlock or is it EOC? */
+    if (!ber_read_type(iv, &read_type, &read_flags, true /* peek */, errp)) {
+        *kind = -1;
+        return;
+    }
+
+    switch (read_type) {
+    case BER_TYPE_EOC:
+        /* Real end of the ramseclist */
+        *kind = RAM_SAVE_FLAG_EOS;
+        sci->hit_end = true;
+        break;
+
+    case BER_TYPE_SEQUENCE: /* Should be the RAMSecBlock */
+        /*
+         * This will be read for RAMSECENTRY - for now we just know there are
+         * some more entries to be read.
+         */
+        *kind = 0;
+        break;
+
+    default:
+        error_setg(errp, "Unexpected type 0x%x in next RAMSecBlock test",
+                   read_type);
+        *kind = -1;
+        break;
+    }
+}
+
+/*
+ * Helper for get_next_type for the seq-compat ramseclist case which might
+ * actually be a RAMBlocklist
+ */
+static void get_next_type_ramseclist(QemuFileBERInputVisitor *iv, int *kind,
+                                     uint8_t read_flags, uint32_t read_type,
+                                     SeqCompatInfo *sci, Error **errp)
+{
+    bool repeat;
+
+    do {
+        repeat = false;
+
+        /* The only flag we're providing to the host here is the EOS, it
+         * will get the rest from a ramsecentry
+         * (Might change if we have a type for 0 page)
+         */
+        if (sci->in_block_list) {
+            /*
+             *  We should only be called twice within block_list, once
+             * at the start, and once at the end.
+             */
+            switch (read_type) {
+            case BER_TYPE_INTEGER:
+                /* The 'totalram' at the start of the list */
+                DPRINTF(iv, "got 'totalram'");
+                *kind = RAM_SAVE_FLAG_MEM_SIZE;
+                break;
+
+            case BER_TYPE_EOC:
+                /* end of list, EoC of 'blocks' member of RAMBlockList */
+                DPRINTF(iv, "got RAMBlockList eoc");
+                *kind = RAM_SAVE_FLAG_EOS;
+                sci->hit_end = true;
+                break;
+
+            default:
+                error_setg(errp, "Unexpected type 0x%x for RAMSecList",
+                           read_type);
+                *kind = -1;
+                break;
+            }
+        } else {
+            /*
+             * In general in the cases where we 'peek' we don't check the flags
+             * they get checked when we actually read data - however, here we
+             * use small APPLICATION tags for size and those overlap the
+             * universal tags so we need to check flags to know what we've got.
+             * PAGE_ALLZERO is an application tag of '0' which
+             * we need to distinguish from universal EOC;
+             */
+            DPRINTF(iv, "RAMSecList non-blocklist case type/flags=%d/%d",
+                    read_type, read_flags);
+            if ((read_flags & BER_TYPE_CLASS_MASK) ==
+                BER_TYPE_CLASS_APPLICATION) {
+                switch (read_type) {
+                case BER_TYPE_QEMU_PAGE_ALLZERO:
+                case BER_TYPE_QEMU_PAGE_FULL:
+                case BER_TYPE_QEMU_PAGE_XBZRLE:
+                    /*
+                     *  Don't bother filling kind in here, it'll get it on
+                     * start_compat for RAMSEC_ENTRY
+                     */
+                    if (!sci->in_ramsecblock) {
+                        error_setg(errp,
+                                   "Unexpected page outside of RAMSecBlock");
+                        *kind = -1;
+                        break;
+                    }
+                    *kind = 0;
+                    break;
+
+                case BER_TYPE_QEMU_PAGE_HOOK:
+                    if (!ber_consume_special_null(iv, "hook",
+                                                  BER_TYPE_QEMU_PAGE_HOOK,
+                                                  errp)) {
+                        *kind = -1;
+                        break;
+                    }
+                    if (!*errp) {
+                        sci->pending_hook = true;
+                        /* Go around again to get the real page */
+                        repeat = true;
+                    }
+                    break;
+
+                default:
+                    error_setg(errp, "Unexpected (app) type 0x%x in RAMSecList",
+                               read_type);
+                    *kind = -1;
+                    break;
+                }
+            } else {
+                switch (read_type) {
+                case BER_TYPE_SEQUENCE:
+                    /* A RAMSecBlock - we know it will have at least one page */
+                    if (sci->in_ramsecblock) {
+                        error_setg(errp, "Unexpected seq start in RAMSecBlock");
+                        *kind = -1;
+                        break;
+                    }
+                    *kind = 0;
+                    break;
+
+                case BER_TYPE_EOC:
+                    if (sci->in_ramsecblock) {
+                        get_next_type_ramsecblock_eoc(iv, kind, sci, errp);
+                    } else {
+                        /* Odd case, not in a ramsecblock and hit the end */
+                        *kind = RAM_SAVE_FLAG_EOS;
+                        sci->hit_end = true;
+                    }
+                    break;
+
+                default:
+                    error_setg(errp,
+                               "Unexpected (!app) type 0x%x for RAMSecList",
+                               read_type);
+                    *kind = -1;
+                    break;
+                }
+            }
+        }
+    } while (repeat);
+}
+
+static void qfberi_get_next_type(Visitor *v, int *kind, const int *qobjects,
+                               const char *name, Error **errp)
+{
+    QemuFileBERInputVisitor *iv = to_iv(v);
+    SeqCompatInfo *sci;
+    uint32_t read_type;
+    uint8_t read_flags;
+
+    if (!ber_read_type(iv, &read_type, &read_flags, true /* peek */, errp)) {
+        *kind = -1;
+        return;
+    }
+
+    DPRINTF(iv, "for '%s' peeked %d/%d", name, read_type, read_flags);
+    if (qfberi_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;
+        }
+
+        switch (sci->mode) {
+        case VISIT_SEQ_COMPAT_BYTE0TERM:
+            switch (read_type) {
+            case BER_TYPE_QEMU_SEC_MIN:
+                *kind = QEMU_VM_SECTION_PART; /* Caller expects part or end
+                                                 we only represent one */
+                break;
+
+            case BER_TYPE_QEMU_SEC_FULL:
+                *kind = QEMU_VM_SECTION_FULL; /* Caller expects full or start
+                                                 we only represent full */
+                break;
+
+            case BER_TYPE_EOC:
+                *kind = QEMU_VM_EOF;
+                sci->hit_end = true;
+                break;
+
+            default:
+                error_setg(errp, "Unexpected type 0x%x for %s", read_type,
+                           name);
+                *kind = -1;
+            }
+            break;
+
+        case VISIT_SEQ_COMPAT_RAMSECLIST:
+            get_next_type_ramseclist(iv, kind, read_flags, read_type, sci,
+                                     errp);
+            break;
+
+        case VISIT_SEQ_COMPAT_SUBSECLIST:
+            switch (read_type) {
+            case BER_TYPE_EOC:
+                *kind = QEMU_VM_EOF;
+                sci->hit_end = true;
+                break;
+
+            case BER_TYPE_QEMU_SUBSEC:
+                *kind = QEMU_VM_SUBSECTION;
+                break;
+
+            default:
+                error_setg(errp, "Unexpected type 0x%x for %s", read_type,
+                           name);
+                break;
+            }
+            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 qfberi_start_sequence_compat(Visitor *v, const char *name,
+                                       Visit_seq_compat_mode compat_mode,
+                                       void *opaque, Error **errp)
+{
+    QemuFileBERInputVisitor *iv = to_iv(v);
+    SeqCompatInfo sci = {
+        .mode = compat_mode,
+        .data = opaque
+    };
+    SectionHeader *sh;
+    ramsecentry_header *rse_hdr;
+    uint64_t tmp64, len;
+    uint32_t tmp32;
+    QemuFileBinInputVisitor *qfbiv;
+    Visitor *bv;
+    uint32_t read_type;
+    uint8_t read_flags;
+    bool read_indef;
+
+    switch (compat_mode) {
+    case VISIT_SEQ_COMPAT_FILE:
+        DPRINTF(iv, "for '%s'", name);
+        if (!ber_expect_type(iv, BER_TYPE_QEMU_FILE,
+                        BER_TYPE_CLASS_APPLICATION | BER_TYPE_CONSTRUCTED,
+                        true, errp)) {
+            return;
+        }
+        DPRINTF(iv, "for '%s'", name);
+        ber_input_integer(iv, &tmp64, sizeof(tmp64), false, BER_TYPE_INTEGER,
+                          BER_TYPE_PRIMITIVE | BER_TYPE_CLASS_UNIVERSAL, errp);
+        if (*errp) {
+            return;
+        }
+        DPRINTF(iv, "for '%s'", name);
+
+        if (tmp64 != 3) {
+            error_setg(errp, "Unsupported file version %" PRIu64 " expecting 3",
+                       tmp64);
+            return;
+        }
+        DPRINTF(iv, "for '%s'", name);
+        sci.hit_end = true; /* It doesn't iterate over this as a list */
+        break;
+
+    case VISIT_SEQ_COMPAT_BYTE0TERM:
+        if (!ber_expect_type(iv, BER_TYPE_SEQUENCE,
+                             BER_TYPE_CONSTRUCTED | BER_TYPE_CLASS_UNIVERSAL,
+                             true, errp)) {
+            return;
+        }
+        break;
+
+    case VISIT_SEQ_COMPAT_RAMSECLIST: {
+        bool is_indef;
+        DPRINTF(iv, "for '%s'", name);
+        /*
+         * At this point we either have a RAMSEC_LIST or a RAMBLOCK_LIST
+         * figure out which and set a flag to know what to expect next.
+         */
+        ber_read_type(iv, &read_type, &read_flags, false, errp);
+        if (*errp) {
+            return;
+        }
+        if (read_flags != (BER_TYPE_CLASS_APPLICATION | BER_TYPE_CONSTRUCTED)) {
+            error_setg(errp, "Unexpected type flags %d on RAMSecList",
+                        read_flags);
+            return;
+        }
+        switch (read_type) {
+        case BER_TYPE_QEMU_RAMSEC_LIST:
+            sci.in_block_list = false;
+            DPRINTF(iv, "RAMSECLIST really RAMSEC");
+            break;
+
+        case BER_TYPE_QEMU_RAMBLOCK_LIST:
+            sci.in_block_list = true;
+            DPRINTF(iv, "RAMSECLIST really RAMBLOCK");
+            break;
+
+        default:
+            error_setg(errp, "Unexpected type %d in RAMSecList", read_type);
+            return;
+        }
+        if (*errp) {
+            return;
+        }
+        ber_read_length(iv, &is_indef, false, 0, errp);
+        if (!is_indef) {
+            error_setg(errp, "RAMSECList expecting indef length but got def");
+            return;
+        }
+        sci.in_ramsecblock = false;
+        sci.pending_hook = false;
+        break;
+    }
+
+    case VISIT_SEQ_COMPAT_RAMSECENTRY: {
+        rse_hdr = opaque;
+        SeqCompatInfo *parent_sci;
+
+        DPRINTF(iv, "for '%s'", name);
+
+        if (!qfberi_is_seqcompat(iv, &parent_sci)) {
+            error_setg(errp, "RAMSECENTRY not in seqcompat");
+            break;
+        }
+        if (parent_sci->mode != VISIT_SEQ_COMPAT_RAMSECLIST) {
+            error_setg(errp, "RAMSECENTRY not in RAMSECLIST");
+            break;
+        }
+
+        if (parent_sci->in_block_list) {
+            DPRINTF(iv, "blocklist case");
+            rse_hdr->flags = RAM_SAVE_FLAG_MEM_SIZE;
+
+            /* 'totalram' - full size of RAM address space */
+            ber_input_integer(iv, &rse_hdr->addr, sizeof(rse_hdr->addr), false,
+                              BER_TYPE_INTEGER,
+                              BER_TYPE_PRIMITIVE | BER_TYPE_CLASS_UNIVERSAL,
+                              errp);
+            /* 'blocks' sequence */
+            if (!ber_expect_type(iv, BER_TYPE_SEQUENCE,
+                        BER_TYPE_CLASS_UNIVERSAL | BER_TYPE_CONSTRUCTED,
+                        true, errp)) {
+                return;
+            }
+            sci.hit_end = true; /* It doesn't iterate over this as a list */
+            break;
+        }
+
+        rse_hdr->flags = 0;
+        if (!parent_sci->in_ramsecblock) {
+            DPRINTF(iv, "!in_ramsecblock case");
+            /* The RAMSecBlock is just a normal sequence type */
+            if (!ber_expect_type(iv, BER_TYPE_SEQUENCE,
+                        BER_TYPE_CLASS_UNIVERSAL | BER_TYPE_CONSTRUCTED,
+                        true, errp)) {
+                return;
+            }
+            /* The name of the block that all the subsequent pages are for */
+            qfberi_type_str256(v, rse_hdr->idstr, name, errp);
+            /* The 'pagelist' is also just a sequence type */
+            if (!ber_expect_type(iv, BER_TYPE_SEQUENCE,
+                        BER_TYPE_CLASS_UNIVERSAL | BER_TYPE_CONSTRUCTED,
+                        true, errp)) {
+                return;
+            }
+
+            parent_sci->in_ramsecblock = true;
+        } else {
+            DPRINTF(iv, "in_ramsecblock case");
+            /* Same block as last time */
+            rse_hdr->flags |= RAM_SAVE_FLAG_CONTINUE;
+            rse_hdr->idstr[0] = '\0';
+        }
+
+        if (parent_sci->pending_hook) {
+            /*
+             *  get_next_type found a BER_TYPE_QEMU_PAGE_HOOK, we just need
+             * to tell the caller this happened.
+             */
+            parent_sci->pending_hook = false;
+            rse_hdr->flags |= RAM_SAVE_FLAG_HOOK;
+        }
+
+        /* We're expecting one of the subtypes of RAMPage */
+        if (!ber_read_type(iv, &read_type, &read_flags, true /* peek */,
+                           errp)) {
+            return;
+        }
+
+        switch (read_type) {
+        case BER_TYPE_QEMU_PAGE_ALLZERO:
+            rse_hdr->flags |= RAM_SAVE_FLAG_COMPRESS;
+            rse_hdr->ch = 0;
+            ber_input_integer(iv, &rse_hdr->addr, sizeof(rse_hdr->addr), false,
+                              BER_TYPE_QEMU_PAGE_ALLZERO,
+                              BER_TYPE_PRIMITIVE | BER_TYPE_CLASS_APPLICATION,
+                              errp);
+            break;
+
+        case BER_TYPE_QEMU_PAGE_FULL:
+            rse_hdr->flags |= RAM_SAVE_FLAG_PAGE;
+            if (!ber_expect_type(iv, BER_TYPE_QEMU_PAGE_FULL,
+                            BER_TYPE_CLASS_APPLICATION | BER_TYPE_CONSTRUCTED,
+                            true, errp)) {
+                return;
+            }
+            ber_input_integer(iv, &rse_hdr->addr, sizeof(rse_hdr->addr), false,
+                          BER_TYPE_INTEGER,
+                          BER_TYPE_PRIMITIVE | BER_TYPE_CLASS_UNIVERSAL, errp);
+            /* Caller reads the page data */
+            break;
+
+        case BER_TYPE_QEMU_PAGE_XBZRLE:
+            rse_hdr->flags |= RAM_SAVE_FLAG_XBZRLE;
+            if (!ber_expect_type(iv, BER_TYPE_QEMU_PAGE_XBZRLE,
+                            BER_TYPE_CLASS_APPLICATION | BER_TYPE_CONSTRUCTED,
+                            true, errp)) {
+                return;
+            }
+            ber_input_integer(iv, &rse_hdr->addr, sizeof(rse_hdr->addr), false,
+                          BER_TYPE_INTEGER,
+                          BER_TYPE_PRIMITIVE | BER_TYPE_CLASS_UNIVERSAL, errp);
+            /*
+             * Next is an odd case; we need to peek the length of the octet
+             * string that should follow
+             */
+            len = ber_read_type(iv, &read_type, &read_flags, true /* peek */,
+                                errp);
+            if (!len) {
+                return;
+            }
+            if (read_type != BER_TYPE_OCTET_STRING ||
+                read_flags !=
+                    (BER_TYPE_PRIMITIVE | BER_TYPE_CLASS_UNIVERSAL)) {
+                error_setg(errp, "Read incorrect BER types/flags for XBZRLE "
+                         "read %d/%d", read_type, read_flags);
+                return;
+            }
+            rse_hdr->len = ber_read_length(iv, &read_indef, true /* peek */,
+                                           len /* offset */, errp);
+            /* Caller reads the XBZRLE page data */
+            break;
+
+        /* BER_TYPE_QEMU_PAGE_HOOK will have been consumed by get_next_type */
+        default:
+            error_setg(errp, "Unexpected type %d in RAMPage", read_type);
+            break;
+        }
+
+        sci.hit_end = true; /* It doesn't iterate over this as a list */
+        break;
+    }
+
+    case VISIT_SEQ_COMPAT_SECTION_HEADER:
+        DPRINTF(iv, "for '%s'", name);
+        if (!ber_expect_type(iv, BER_TYPE_QEMU_SEC_FULL,
+                        BER_TYPE_CLASS_APPLICATION | BER_TYPE_CONSTRUCTED,
+                        true, errp)) {
+            return;
+        }
+        /*
+         * for VM_SECTION_FULL and VM_SECTION_START
+         * 'opaque' points to a struct Sectionheader
+         */
+        sh = opaque;
+        qfberi_type_str256(v, sh->idstr, name, errp);
+        ber_input_integer(iv, &tmp64, sizeof(sh->section_id), false,
+                          BER_TYPE_INTEGER,
+                          BER_TYPE_PRIMITIVE | BER_TYPE_CLASS_UNIVERSAL, errp);
+        sh->section_id = tmp64;
+        ber_input_integer(iv, &tmp64, sizeof(sh->instance_id), false,
+                          BER_TYPE_INTEGER,
+                          BER_TYPE_PRIMITIVE | BER_TYPE_CLASS_UNIVERSAL, errp);
+        sh->instance_id = tmp64;
+        ber_input_integer(iv, &tmp64, sizeof(sh->version_id), false,
+                          BER_TYPE_INTEGER,
+                          BER_TYPE_PRIMITIVE | BER_TYPE_CLASS_UNIVERSAL, errp);
+        sh->version_id = tmp64;
+        DPRINTF(iv, "'%s' got %s %d/%d/%d", name, sh->idstr, sh->section_id,
+                   sh->instance_id, 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
+         * 'opaque' points to a struct Sectionheader
+         */
+        sh = opaque;
+        if (!ber_expect_type(iv, BER_TYPE_QEMU_SEC_MIN,
+                        BER_TYPE_CLASS_APPLICATION | BER_TYPE_CONSTRUCTED,
+                        true, errp)) {
+            return;
+        }
+        ber_input_integer(iv, &tmp64, sizeof(sh->section_id), false,
+                          BER_TYPE_INTEGER,
+                          BER_TYPE_PRIMITIVE | BER_TYPE_CLASS_UNIVERSAL, errp);
+        sh->section_id = tmp64;
+        DPRINTF(iv, "'%s' got %d", name, sh->section_id);
+        sci.hit_end = true; /* It doesn't iterate over this as a list */
+        break;
+
+    case VISIT_SEQ_COMPAT_SUBSECLIST:
+        if (!ber_expect_type(iv, BER_TYPE_QEMU_SUBSEC_LIST,
+                        BER_TYPE_CLASS_APPLICATION | BER_TYPE_CONSTRUCTED,
+                        true, errp)) {
+            return;
+        }
+        break;
+
+    case VISIT_SEQ_COMPAT_SUBSECTION:
+        DPRINTF(iv, "for '%s'", name);
+        if (!ber_expect_type(iv, BER_TYPE_QEMU_SUBSEC,
+                        BER_TYPE_CLASS_APPLICATION | BER_TYPE_CONSTRUCTED,
+                        true, errp)) {
+            return;
+        }
+        /* 'opaque' points to a struct Sectionheader */
+        sh = opaque;
+        qfberi_type_str256(v, sh->idstr, name, errp);
+        ber_input_integer(iv, &tmp64, sizeof(sh->version_id), false,
+                          BER_TYPE_INTEGER,
+                          BER_TYPE_PRIMITIVE | BER_TYPE_CLASS_UNIVERSAL, errp);
+        sh->version_id = tmp64;
+        DPRINTF(iv, "'%s' got %s %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_VMSTATE:
+        /*
+         * VMState's are just sequences, but the type tag is determined by
+         * the opaque, as follows
+         *   0 - a flag meaning use a universal sequence tag
+         * else - the tag to use with an BER_TYPE_CLASS_APPLICATION class
+         */
+        tmp32 = *(uint32_t *)opaque;
+        if (tmp32 == 0) {
+            if (!ber_expect_type(iv, BER_TYPE_SEQUENCE,
+                        BER_TYPE_CLASS_UNIVERSAL | BER_TYPE_CONSTRUCTED,
+                        true, errp)) {
+                return;
+            }
+        } else {
+            if (!ber_expect_type(iv, (enum ber_type_tag)tmp32,
+                        BER_TYPE_CLASS_APPLICATION | BER_TYPE_CONSTRUCTED,
+                        true, errp)) {
+                return;
+            }
+        }
+        sci.hit_end = true; /* It doesn't iterate over this as a list */
+        break;
+
+    case VISIT_SEQ_COMPAT_BLOB:
+        if (!ber_expect_type(iv, BER_TYPE_OCTET_STRING,
+                        BER_TYPE_PRIMITIVE | BER_TYPE_CLASS_UNIVERSAL,
+                        false, errp)) {
+            return;
+        }
+
+        len = ber_read_length(iv, &read_indef, false, 0, errp);
+
+        if (*errp) {
+            return;
+        }
+
+        if (read_indef) {
+            error_setg(errp, "Unexpected indefinite length on '%s'",
+                   name);
+            return;
+        }
+
+        /*
+         * Hand back a QEMUFile to the caller that they can read the binary
+         * data off - restrict it so that it can only read the blob data.
+         */
+        sci.binfile = qemu_partopen("r", iv->file, len);
+
+        /* That file gets a binary visitor for the duration of the blob */
+        qfbiv = qemu_file_bin_input_visitor_new(sci.binfile);
+        bv = qemu_file_bin_input_get_visitor(qfbiv);
+        qemu_file_set_tmp_visitor(sci.binfile, bv);
+        *(QEMUFile **)opaque = sci.binfile;
+
+        sci.hit_end = true; /* It doesn't iterate over this as a list */
+        break;
+
+    }
+
+    DPRINTF(iv, "for '%s'", name);
+    qfberi_push_seqcompat(iv, sci);
+
+
+    /* We don't need to read anything at this point */
+}
+
+static void qfberi_end_sequence_compat(Visitor *v, const char* name,
+                                     Visit_seq_compat_mode compat_mode,
+                                     Error **errp)
+{
+    QemuFileBERInputVisitor *iv = to_iv(v);
+    StackEntry *e = qfberi_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");
+    }
+    if (!*errp) {
+        switch (e->seqcompat_info.mode) {
+        case VISIT_SEQ_COMPAT_RAMSECENTRY: {
+            const ramsecentry_header *rse_hdr;
+            rse_hdr = e->seqcompat_info.data;
+            if (rse_hdr->flags & (RAM_SAVE_FLAG_PAGE | RAM_SAVE_FLAG_XBZRLE)) {
+                /* End of a 'FullPage' or 'XBZRLEPage' sequence */
+                ber_consume_eoc(iv, errp);
+            }
+            break;
+        }
+
+        case VISIT_SEQ_COMPAT_RAMSECLIST: {
+            if (e->seqcompat_info.in_block_list) {
+                ber_consume_eoc(iv, errp); /* 'blocks' list in RAMBlockList */
+            }
+            if (e->seqcompat_info.in_ramsecblock) {
+                /* A pagelist sequence in a RAMSecBlock in a RAMSecList */
+                ber_consume_eoc(iv, errp); /* Closing the pagelist */
+                ber_consume_eoc(iv, errp); /* Closing the RAMSecBlock */
+            }
+            /* Closing the RAMSecList or RAMBlockList */
+            ber_consume_eoc(iv, errp);
+            break;
+        }
+
+        case VISIT_SEQ_COMPAT_SECTION_HEADER:
+        case VISIT_SEQ_COMPAT_SECTION_MIN:
+            ber_consume_special_null(iv, "SecEnd", BER_TYPE_QEMU_SEC_END, errp);
+            ber_consume_eoc(iv, errp);
+            break;
+
+        case VISIT_SEQ_COMPAT_BYTE0TERM:
+        case VISIT_SEQ_COMPAT_FILE:
+        case VISIT_SEQ_COMPAT_SUBSECLIST:
+        case VISIT_SEQ_COMPAT_SUBSECTION:
+        case VISIT_SEQ_COMPAT_VMSTATE:
+            ber_consume_eoc(iv, errp);
+            break;
+
+        case VISIT_SEQ_COMPAT_BLOB:
+            qemu_fclose(e->seqcompat_info.binfile);
+            break;
+        }
+    }
+    g_free(e);
+}
+
+static void qfberi_destroy(Visitor *v, Error **errp)
+{
+    QemuFileBERInputVisitor *iv = to_iv(v);
+
+    qemu_file_ber_input_visitor_cleanup(iv);
+}
+
+Visitor *qemu_file_ber_input_get_visitor(QemuFileBERInputVisitor *iv)
+{
+    return &iv->visitor;
+}
+
+void qemu_file_ber_input_visitor_cleanup(QemuFileBERInputVisitor *iv)
+{
+    g_free(iv);
+}
+
+QemuFileBERInputVisitor *qemu_file_ber_input_visitor_new(QEMUFile *f)
+{
+    QemuFileBERInputVisitor *v;
+
+    v = g_malloc0(sizeof(*v));
+
+    v->file = f;
+
+    v->visitor.start_struct = qfberi_start_struct;
+    v->visitor.end_struct = qfberi_end_struct;
+    v->visitor.start_list = qfberi_start_list;
+    v->visitor.next_list = qfberi_next_list;
+    v->visitor.end_list = qfberi_end_list;
+    v->visitor.start_array = qfberi_start_array;
+    v->visitor.next_array = qfberi_next_array;
+    v->visitor.end_array = qfberi_end_array;
+    v->visitor.type_int = qfberi_type_int64;
+    v->visitor.type_buffer = qfberi_type_buffer;
+    v->visitor.type_uint8 = qfberi_type_uint8;
+    v->visitor.type_uint16 = qfberi_type_uint16;
+    v->visitor.type_uint32 = qfberi_type_uint32;
+    v->visitor.type_uint64 = qfberi_type_uint64;
+    v->visitor.type_int8 = qfberi_type_int8;
+    v->visitor.type_int16 = qfberi_type_int16;
+    v->visitor.type_int32 = qfberi_type_int32;
+    v->visitor.type_int64 = qfberi_type_int64;
+    v->visitor.type_bool = qfberi_type_bool;
+    v->visitor.type_str = qfberi_type_str;
+    v->visitor.type_str256 = qfberi_type_str256;
+    v->visitor.destroy = qfberi_destroy;
+    v->visitor.start_sequence_compat = qfberi_start_sequence_compat;
+    v->visitor.get_next_type = qfberi_get_next_type;
+    v->visitor.end_sequence_compat = qfberi_end_sequence_compat;
+    v->visitor.get_qemufile = qfberi_get_qemufile;
+
+    v->visitor.flags = VISITOR_LOADING;
+
+    QTAILQ_INIT(&v->stack);
+    v->depth = 0;
+
+    return v;
+}