From patchwork Wed Apr 23 16:37:45 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Dr. David Alan Gilbert" X-Patchwork-Id: 341927 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id BF9FF14009F for ; Thu, 24 Apr 2014 02:43:09 +1000 (EST) Received: from localhost ([::1]:33827 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Wd0GJ-00005X-1w for incoming@patchwork.ozlabs.org; Wed, 23 Apr 2014 12:43:07 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:51380) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Wd0Cp-00059q-2L for qemu-devel@nongnu.org; Wed, 23 Apr 2014 12:39:37 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1Wd0Ci-0008SF-L9 for qemu-devel@nongnu.org; Wed, 23 Apr 2014 12:39:30 -0400 Received: from mx1.redhat.com ([209.132.183.28]:31298) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Wd0Ci-0008S9-6a for qemu-devel@nongnu.org; Wed, 23 Apr 2014 12:39:24 -0400 Received: from int-mx14.intmail.prod.int.phx2.redhat.com (int-mx14.intmail.prod.int.phx2.redhat.com [10.5.11.27]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id s3NGcJOu020455 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Wed, 23 Apr 2014 12:38:19 -0400 Received: from dgilbert-t530.home.treblig.org (vpn1-7-213.ams2.redhat.com [10.36.7.213]) by int-mx14.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id s3NGboL2024273; Wed, 23 Apr 2014 12:38:17 -0400 From: "Dr. David Alan Gilbert (git)" To: qemu-devel@nongnu.org Date: Wed, 23 Apr 2014 17:37:45 +0100 Message-Id: <1398271069-22057-13-git-send-email-dgilbert@redhat.com> In-Reply-To: <1398271069-22057-1-git-send-email-dgilbert@redhat.com> References: <1398271069-22057-1-git-send-email-dgilbert@redhat.com> X-Scanned-By: MIMEDefang 2.68 on 10.5.11.27 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x X-Received-From: 209.132.183.28 Cc: stefanb@linux.vnet.ibm.com, quintela@redhat.com, mdroth@linux.vnet.ibm.com, agraf@suse.de, mst@redhat.com, aliguori@amazon.com, afaerber@suse.de Subject: [Qemu-devel] [RFC PATCH v2 12/16] BER Visitor: Create input visitor X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org From: "Dr. David Alan Gilbert" Signed-off-by: Dr. David Alan Gilbert --- 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 --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 + * + * 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 + * David Gilbert + * Michael Tsirkin + * Stefan Berger + * + * 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; +}