new file mode 100644
@@ -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
@@ -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
new file mode 100644
@@ -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;
+}