qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [RFC PATCH 13/16] BER Visitor: Create input visitor


From: Dr. David Alan Gilbert (git)
Subject: [Qemu-devel] [RFC PATCH 13/16] BER Visitor: Create input visitor
Date: Tue, 25 Mar 2014 20:17:24 +0000

From: "Dr. David Alan Gilbert" <address@hidden>

Signed-off-by: Dr. David Alan Gilbert <address@hidden>
---
 include/qapi/qemu-file-ber-input-visitor.h |   26 +
 qapi/Makefile.objs                         |    2 +-
 qapi/qemu-file-ber-input-visitor.c         | 1163 ++++++++++++++++++++++++++++
 3 files changed, 1190 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 <address@hidden>
+ *
+ * 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 f8fb347..c91dc46 100644
--- a/qapi/Makefile.objs
+++ b/qapi/Makefile.objs
@@ -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
diff --git a/qapi/qemu-file-ber-input-visitor.c 
b/qapi/qemu-file-ber-input-visitor.c
new file mode 100644
index 0000000..d730208
--- /dev/null
+++ b/qapi/qemu-file-ber-input-visitor.c
@@ -0,0 +1,1163 @@
+/*
+ * QEMUFile input visitor for BER format
+ *
+ * Copyright IBM, Corp. 2011, 2013
+ * Copyright Red Hat, Corp. 2011,2014
+ *
+ * Authors:
+ *  Anthony Liguori   <address@hidden>
+ *  David Gilbert  <address@hidden>
+ *  Michael Tsirkin   <address@hidden>
+ *  Stefan Berger     <address@hidden>
+ *
+ * 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;
+}
-- 
1.8.5.3




reply via email to

[Prev in Thread] Current Thread [Next in Thread]