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,6 +2,6 @@ util-obj-y = qapi-visit-core.o qapi-dealloc-visitor.o qmp-input-visitor.o
util-obj-y += qmp-output-visitor.o qmp-registry.o qmp-dispatch.o
util-obj-y += string-input-visitor.o string-output-visitor.o
util-obj-y += qemu-file-binary-output-visitor.o 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 += qemu-file-debug-output-visitor.o
util-obj-y += opts-visitor.o
new file mode 100644
@@ -0,0 +1,1163 @@
+/*
+ * 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;
+ QEMUSizedBuffer *qsb;
+} SeqCompatInfo;
+
+typedef struct StackEntry {
+ enum {
+ QFIV_ARRAY,
+ QFIV_LIST,
+ QFIV_STRUCT,
+ QFIV_SEQCOMPAT,
+ } type;
+ ArrayInfo array_info;
+ SeqCompatInfo seqcompat_info;
+ QTAILQ_ENTRY(StackEntry) node;
+} StackEntry;
+
+struct QemuFileBERInputVisitor {
+ Visitor visitor;
+ QTAILQ_HEAD(, StackEntry) stack;
+ QEMUFile *file;
+ unsigned int depth;
+};
+
+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
+ */
+static uint32_t ber_read_type(QemuFileBERInputVisitor *iv,
+ 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);
+ }
+ return type;
+}
+
+static uint64_t ber_read_length(QemuFileBERInputVisitor *aiv,
+ bool *is_indefinite, Error **errp)
+{
+ uint8_t byte, c, int_len;
+ uint64_t len = 0;
+ QEMUFile *qfile = aiv->file;
+ unsigned char int_array[sizeof(len)];
+ char buf[128];
+
+ *is_indefinite = false;
+
+ if (qemu_get_buffer(qfile, &byte, 1) != 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;
+ 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_get_buffer(qfile, int_array, int_len) != int_len) {
+ error_setg(errp, "QEMUFile error: Error while reading length");
+ return ~0x0ULL;
+ }
+ for (c = 0; c < int_len; c++) {
+ len <<= 8;
+ len |= int_array[c];
+ }
+ }
+
+ 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 */
+
+ read_type = ber_read_type(iv, &read_flags, false, errp);
+ if (*errp) {
+ return false;
+ }
+
+ if ((expected_flags != read_flags) || (expected_type != read_type)) {
+ error_setg(errp, "Read incorrect BER types/flags; expecting %x/%x "
+ "read %x/%x",
+ expected_type, expected_flags,
+ read_type, read_flags);
+ return false;
+ }
+
+ DPRINTF(iv, "got match %x/%x", expected_type, expected_flags);
+
+ if (get_length_indef) {
+ bool is_indef;
+ ber_read_length(iv, &is_indef, errp);
+ if (*errp) {
+ return false;
+ }
+ if (!is_indef) {
+ 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);
+ }
+}
+
+/* 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, 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, 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, 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;
+}
+
+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;
+
+ read_type = ber_read_type(iv, &read_flags, true /* peek */, errp);
+
+ DPRINTF(iv, "for '%s' peeked %x/%x", 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:
+ /* 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)
+ */
+ switch (read_type) {
+ case BER_TYPE_EOC:
+ *kind = RAM_SAVE_FLAG_EOS;
+ sci->hit_end = true;
+ break;
+
+ case BER_TYPE_QEMU_RAMSEC_ENTRY:
+ *kind = 0; /* Don't have visibility of type here */
+ break;
+
+ default:
+ error_setg(errp, "Unexpected type 0x%x for %s", read_type,
+ name);
+ break;
+ }
+ 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;
+ uint8_t *buffer;
+ QemuFileBinInputVisitor *qfbiv;
+ Visitor *bv;
+ 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:
+ if (!ber_expect_type(iv, BER_TYPE_QEMU_RAMSEC_LIST,
+ BER_TYPE_CLASS_APPLICATION | BER_TYPE_CONSTRUCTED,
+ true, errp)) {
+ return;
+ }
+ 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_RAMSECENTRY:
+ rse_hdr = opaque;
+ if (!ber_expect_type(iv, BER_TYPE_QEMU_RAMSEC_ENTRY,
+ 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);
+ ber_input_integer(iv, &tmp64, sizeof(rse_hdr->flags), false,
+ BER_TYPE_INTEGER,
+ BER_TYPE_PRIMITIVE | BER_TYPE_CLASS_UNIVERSAL, errp);
+ rse_hdr->flags = (uint16_t)tmp64;
+
+ if ((rse_hdr->flags &
+ (RAM_SAVE_FLAG_CONTINUE | RAM_SAVE_FLAG_MEM_SIZE |
+ RAM_SAVE_FLAG_HOOK)) == 0) {
+ qfberi_type_str256(v, rse_hdr->idstr, name, errp);
+ }
+ 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:
+ /* TODO! We don't need to, and shouldn't load this into memory
+ * we can do this as a shim on QEMUFile that only allows so many
+ * bytes to be loaded.
+ */
+ 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, errp);
+ if (*errp) {
+ return;
+ }
+ if (read_indef) {
+ error_setg(errp, "Unexpected indefinite length on '%s'",
+ name);
+ return;
+ }
+ DPRINTF(iv, "for %s read_len=%" PRIu64, name, len);
+ if (len > (10*1024*1024)) {
+ error_setg(errp, "Suspicious size (%" PRIu64 ") for '%s' ",
+ len, name);
+ return;
+ }
+ /* Load this blob into memory and hand back a QEMUFile that the
+ * caller can read.
+ */
+ buffer = g_malloc(len);
+
+ if (qemu_get_buffer(iv->file, buffer, len) != len) {
+ error_setg(errp, "QEMUFile error: Error reading buffer for '%s'",
+ name);
+ g_free(buffer);
+ }
+ sci.qsb = qsb_create(buffer, len);
+ g_free(buffer); /* Because qsb_create copies */
+ sci.binfile = qemu_bufopen("r", sci.qsb);
+
+ 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_BYTE0TERM:
+ case VISIT_SEQ_COMPAT_FILE:
+ case VISIT_SEQ_COMPAT_RAMSECENTRY:
+ case VISIT_SEQ_COMPAT_RAMSECLIST:
+ case VISIT_SEQ_COMPAT_SECTION_HEADER:
+ case VISIT_SEQ_COMPAT_SECTION_MIN:
+ 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); /* Also frees the qsb */
+ 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;
+}