From: "Dr. David Alan Gilbert" <dgilb...@redhat.com> based on Michael Roth's https://lists.gnu.org/archive/html/qemu-devel/2011-09/msg02470.html
Signed-off-by: Dr. David Alan Gilbert <dgilb...@redhat.com> --- include/qapi/qemu-file-binary-input-visitor.h | 27 + qapi/Makefile.objs | 2 +- qapi/qemu-file-binary-input-visitor.c | 708 ++++++++++++++++++++++++++ 3 files changed, 736 insertions(+), 1 deletion(-) create mode 100644 include/qapi/qemu-file-binary-input-visitor.h create mode 100644 qapi/qemu-file-binary-input-visitor.c diff --git a/include/qapi/qemu-file-binary-input-visitor.h b/include/qapi/qemu-file-binary-input-visitor.h new file mode 100644 index 0000000..dd7e40e --- /dev/null +++ b/include/qapi/qemu-file-binary-input-visitor.h @@ -0,0 +1,27 @@ +/* + * QEMUFile Visitor + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Michael Roth <mdr...@linux.vnet.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. + * + */ + +#ifndef QEMU_FILE_BINARY_INPUT_VISITOR_H +#define QEMU_FILE_BINARY_INPUT_VISITOR_H + +#include "visitor.h" + +typedef struct QemuFileBinInputVisitor QemuFileBinInputVisitor; + +QemuFileBinInputVisitor *qemu_file_bin_input_visitor_new(QEMUFile *f); +void qemu_file_bin_input_visitor_cleanup(QemuFileBinInputVisitor *d); + +Visitor *qemu_file_bin_input_get_visitor(QemuFileBinInputVisitor *v); + +#endif + diff --git a/qapi/Makefile.objs b/qapi/Makefile.objs index a054d52..ae5309a 100644 --- a/qapi/Makefile.objs +++ b/qapi/Makefile.objs @@ -1,5 +1,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 +util-obj-y += qemu-file-binary-output-visitor.o qemu-file-binary-input-visitor.o util-obj-y += opts-visitor.o diff --git a/qapi/qemu-file-binary-input-visitor.c b/qapi/qemu-file-binary-input-visitor.c new file mode 100644 index 0000000..6400d0a --- /dev/null +++ b/qapi/qemu-file-binary-input-visitor.c @@ -0,0 +1,708 @@ +/* + * QEMUFile Output Visitor + * + * Copyright IBM, Corp. 2011 + * Copyright Red Hat, Corp. 2014 + * + * Authors: + * Michael Roth <mdr...@linux.vnet.ibm.com> + * David Gilbert <dgilb...@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. + * + */ + +#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" + +/* Note that this can generate so much debug virt-test times out */ +#if 0 +#define DPRINTF(v, fmt, ...) \ + do { \ + fprintf(stderr, "%*s qfbiv/%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; +} 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 QemuFileBinInputVisitor { + Visitor visitor; + QTAILQ_HEAD(, StackEntry) stack; + QEMUFile *file; + unsigned int depth; +}; + +static QemuFileBinInputVisitor *to_iv(Visitor *v) +{ + return container_of(v, QemuFileBinInputVisitor, visitor); +} + +static void qfbi_push(QemuFileBinInputVisitor *iv, StackEntry *e) +{ + QTAILQ_INSERT_HEAD(&iv->stack, e, node); + iv->depth++; +} + +static void qfbi_push_array(QemuFileBinInputVisitor *iv, + ArrayInfo ai) +{ + StackEntry *e = g_malloc0(sizeof(*e)); + e->type = QFIV_ARRAY; + e->array_info = ai; + qfbi_push(iv, e); +} + +static void qfbi_push_list(QemuFileBinInputVisitor *iv) +{ + StackEntry *e = g_malloc0(sizeof(*e)); + e->type = QFIV_LIST; + qfbi_push(iv, e); +} + +static void qfbi_push_seqcompat(QemuFileBinInputVisitor *iv, + SeqCompatInfo sci) +{ + StackEntry *e = g_malloc0(sizeof(*e)); + e->type = QFIV_SEQCOMPAT; + e->seqcompat_info = sci; + qfbi_push(iv, e); +} + +static void qfbi_push_struct(QemuFileBinInputVisitor *iv) +{ + StackEntry *e = g_malloc0(sizeof(*e)); + e->type = QFIV_STRUCT; + qfbi_push(iv, e); +} + +static void *qfbi_pop(QemuFileBinInputVisitor *iv) +{ + StackEntry *e = QTAILQ_FIRST(&iv->stack); + QTAILQ_REMOVE(&iv->stack, e, node); + iv->depth--; + return e; +} + +static bool qfbi_is_array(QemuFileBinInputVisitor *iv) +{ + StackEntry *e = QTAILQ_FIRST(&iv->stack); + return e->type == QFIV_ARRAY; +} + +static bool qfbi_is_list(QemuFileBinInputVisitor *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 qfbi_is_seqcompat(QemuFileBinInputVisitor *iv, + SeqCompatInfo **sci) +{ + StackEntry *e = QTAILQ_FIRST(&iv->stack); + if (e && e->type == QFIV_SEQCOMPAT) { + *sci = &e->seqcompat_info; + return true; + } + return false; +} + +static void qfbi_start_struct(Visitor *v, void **obj, + const char *kind, + const char *name, size_t size, + Error **errp) +{ + QemuFileBinInputVisitor *iv = to_iv(v); + DPRINTF(iv, "for '%s' of '%s'", name, kind); + + qfbi_push_struct(iv); +} + +static void qfbi_end_struct(Visitor *v, Error **errp) +{ + QemuFileBinInputVisitor *iv = to_iv(v); + StackEntry *e = qfbi_pop(iv); + + DPRINTF(iv, "<"); + if (!e || e->type != QFIV_STRUCT) { + error_set(errp, QERR_UNDEFINED_ERROR); + return; + } + g_free(e); +} + +static void qfbi_start_list(Visitor *v, const char *name, Error **errp) +{ + QemuFileBinInputVisitor *iv = to_iv(v); + qfbi_push_list(iv); +} + +static GenericList *qfbi_next_list(Visitor *v, GenericList **list, Error **errp) +{ + QemuFileBinInputVisitor *iv = to_iv(v); + GenericList *entry; + + if (!qfbi_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 qfbi_end_list(Visitor *v, Error **errp) +{ + QemuFileBinInputVisitor *iv = to_iv(v); + StackEntry *e = qfbi_pop(iv); + if (!e || e->type != QFIV_LIST) { + error_set(errp, QERR_UNDEFINED_ERROR); + return; + } + g_free(e); +} + +static void qfbi_start_array(Visitor *v, void **obj, const char *name, + size_t elem_count, size_t elem_size, Error **errp) +{ + QemuFileBinInputVisitor *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); + } + qfbi_push_array(iv, ai); +} + +static void qfbi_next_array(Visitor *v, Error **errp) +{ + QemuFileBinInputVisitor *iv = to_iv(v); + StackEntry *e = QTAILQ_FIRST(&iv->stack); + + if (!qfbi_is_array(iv) || + e->array_info.pos >= e->array_info.elem_count) { + error_set(errp, QERR_UNDEFINED_ERROR); + } + + e->array_info.pos++; +} + +static void qfbi_end_array(Visitor *v, Error **errp) +{ + QemuFileBinInputVisitor *iv = to_iv(v); + StackEntry *e = qfbi_pop(iv); + if (!e || e->type != QFIV_ARRAY) { + error_set(errp, QERR_UNDEFINED_ERROR); + return; + } + g_free(e); +} + +static void qfbi_type_str(Visitor *v, char **obj, const char *name, + Error **errp) +{ + if (obj) { + g_free(*obj); + } +} + +/* Read in a byte+buffer -> giving a string. obj must be a buffer of + * at least 256 chars in length + */ +static void qfbi_type_str256(Visitor *v, char *obj, const char *name, + Error **errp) +{ + QemuFileBinInputVisitor *iv = to_iv(v); + unsigned int len = qemu_get_byte(iv->file); + + qemu_get_buffer(iv->file, (uint8_t *)obj, len); + obj[len] = 0; + DPRINTF(iv, "for '%s' len=%d str=%s", name, len, obj); +} + +static void qfbi_type_buffer(Visitor *v, void *data, size_t len, bool async, + const char *name, Error **errp) +{ + QemuFileBinInputVisitor *iv = to_iv(v); + qemu_get_buffer(iv->file, data, len); +} + +static void qfbi_type_uint8(Visitor *v, uint8_t *obj, const char *name, + Error **errp) +{ + QemuFileBinInputVisitor *iv = to_iv(v); + *obj = qemu_get_byte(iv->file); + DPRINTF(iv, "for '%s' / %u", name, *obj); +} + +static void qfbi_type_uint16(Visitor *v, uint16_t *obj, const char *name, + Error **errp) +{ + QemuFileBinInputVisitor *iv = to_iv(v); + qemu_get_be16s(iv->file, obj); + DPRINTF(iv, "for '%s' / %u", name, *obj); +} + +static void qfbi_type_uint32(Visitor *v, uint32_t *obj, const char *name, + Error **errp) +{ + QemuFileBinInputVisitor *iv = to_iv(v); + qemu_get_be32s(iv->file, obj); + DPRINTF(iv, "for '%s' / %u", name, *obj); +} + +static void qfbi_type_uint64(Visitor *v, uint64_t *obj, const char *name, + Error **errp) +{ + QemuFileBinInputVisitor *iv = to_iv(v); + qemu_get_be64s(iv->file, obj); + DPRINTF(iv, "for '%s' / %lu", name, *obj); +} + +static void qfbi_type_int8(Visitor *v, int8_t *obj, const char *name, + Error **errp) +{ + QemuFileBinInputVisitor *iv = to_iv(v); + *obj = qemu_get_sbyte(iv->file); + DPRINTF(iv, "for '%s' / %d", name, *obj); +} + +static void qfbi_type_int16(Visitor *v, int16_t *obj, const char *name, + Error **errp) +{ + QemuFileBinInputVisitor *iv = to_iv(v); + qemu_get_sbe16s(iv->file, obj); + DPRINTF(iv, "for '%s' / %d", name, *obj); +} + +static void qfbi_type_int32(Visitor *v, int32_t *obj, const char *name, + Error **errp) +{ + QemuFileBinInputVisitor *iv = to_iv(v); + qemu_get_sbe32s(iv->file, obj); + DPRINTF(iv, "for '%s' / %d", name, *obj); +} + +static void qfbi_type_int64(Visitor *v, int64_t *obj, const char *name, + Error **errp) +{ + QemuFileBinInputVisitor *iv = to_iv(v); + qemu_get_sbe64s(iv->file, obj); + DPRINTF(iv, "for '%s' / %ld", name, *obj); +} + +static void qfbi_type_bool(Visitor *v, bool *obj, const char *name, + Error **errp) +{ + uint8_t val; + qfbi_type_uint8(v, &val, name, errp); + *obj = val; +} + +static QEMUFile *qfbi_get_qemufile(Visitor *v) +{ + QemuFileBinInputVisitor *iv = to_iv(v); + + return iv->file; +} + +static void qfbi_get_next_type(Visitor *v, int *kind, const int *qobjects, + const char *name, Error **errp) +{ + QemuFileBinInputVisitor *iv = to_iv(v); + SeqCompatInfo *sci; + uint64_t tmp64; + uint8_t tmpbyte; + + DPRINTF(iv, "for '%s'", name); + if (qfbi_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; + } + + DPRINTF(iv, "/seqcompat for '%s' mode=%d tmpbyte=%d", name, sci->mode, + tmpbyte); + switch (sci->mode) { + case VISIT_SEQ_COMPAT_BYTE0TERM: + /* + * End of the list is marked by a 0 byte, + * we'll consume the byte read + */ + tmpbyte = qemu_get_byte(iv->file); + if (tmpbyte == 0) { + sci->hit_end = true; + } + *kind = tmpbyte; + break; + + case VISIT_SEQ_COMPAT_RAMSECLIST: + /* + * the RAM sections are lists of entries each that start with a + * 64 bit word that has an address or length, OR'd with a set of + * the RAM_SAVE_FLAG_ bits; RAM_SAVE_FLAG_EOS indicates the end + * of the list. + * We don't consume the word since it'll be consumed by + * the SEQ_COMPAT_RAMSECENTRY; except for the EOS + */ + + if (qemu_peek_buffer(iv->file, (uint8_t *)&tmp64, 8, 0) != 8) { + error_setg(errp, "Failed to read flag word for '%s'", name); + *kind = -1; + return; + } + + tmp64 = be64_to_cpu(tmp64); + + *kind = tmp64 & RAM_SAVE_FLAG_MASK; + DPRINTF(iv, "tmp64=0x%lx *kind=%x\n", tmp64, *kind); + if (tmp64 & RAM_SAVE_FLAG_EOS) { + tmp64 = qemu_get_be64(iv->file); /* Consume terminator */ + sci->hit_end = true; + } + break; + + case VISIT_SEQ_COMPAT_SUBSECLIST: + /* In theory subsections are terminated by a + * byte != QEMU_VM_SUBSECTION + * and that byte is part of whatever comes next, so we mustn't + * consume the terminator if it's not valid. + * However, it's worse than that, historical layouts weren't + * really that careful about what came next, so you can get + * false sections, so we check everything in the header and if + * it's not valid we declare it end-of-section. + * For this case the 'opaque' passed in is the parent string + * name. + */ + if (qemu_peek_buffer(iv->file, &tmpbyte, 1, 0) != 1) { + error_setg(errp, "Failed to read type byte for '%s'", name); + *kind = -1; + return; + } + + if (tmpbyte == QEMU_VM_SUBSECTION) { + uint8_t len, size; + char idstr[256]; + char *parent_name = (char *)sci->data; + size_t pn_len = strlen(parent_name); + DPRINTF(iv, "/subsection for '%s'/%s", name, parent_name); + + len = qemu_peek_byte(iv->file, 1); + if (len < (pn_len + 1)) { + DPRINTF(iv, "/subsection for '%s'/%s - len too short", + name, parent_name); + sci->hit_end = true; + *kind = 0xff; + break; + } + size = qemu_peek_buffer(iv->file, (uint8_t *)idstr, + len, 2); + if (size != len) { + DPRINTF(iv, "/subsection for '%s'/%s - size!=len", + name, parent_name); + sci->hit_end = true; + *kind = 0xff; + break; + } + idstr[size] = 0; + + if (strncmp(parent_name, idstr, pn_len) != 0) { + DPRINTF(iv, "/subsection for n='%s' / pn='%s' " + "idstr='%s' - invalid subsection name", + name, parent_name, idstr); + /* it don't have a valid subsection name */ + sci->hit_end = true; + *kind = 0xff; + break; + } + qemu_file_skip(iv->file, 1); /* subsection byte */ + + /* We're not going to consume the name or idstr here + * since they're logically part of the item not the + * list, so they're going to get re-read. + */ + DPRINTF(iv, "/subsection for '%s' got %s from %s " + "tmpbyte=%d", name, idstr, parent_name, + tmpbyte); + } else { + sci->hit_end = true; + } + *kind = tmpbyte; + 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 qfbi_start_sequence_compat(Visitor *v, const char *name, + Visit_seq_compat_mode compat_mode, + void *opaque, Error **errp) +{ + QemuFileBinInputVisitor *iv = to_iv(v); + SeqCompatInfo sci = { + .mode = compat_mode, + .data = opaque + }; + SectionHeader *sh; + ramsecentry_header *rse_hdr; + unsigned int len; + uint32_t tmp; + uint64_t tmp64; + + switch (compat_mode) { + case VISIT_SEQ_COMPAT_FILE: + tmp = qemu_get_be32(iv->file); + if (tmp != QEMU_VM_FILE_MAGIC) { + error_setg(errp, "Incorrect SaveVM file header (read)"); + return; + } + + tmp = qemu_get_be32(iv->file); + if (tmp == QEMU_VM_FILE_VERSION_COMPAT) { + error_setg(errp, "SaveVM v2 format is obsolete and will not load"); + return; + } + if (tmp != QEMU_VM_FILE_VERSION) { + error_setg(errp, "Unsupported SaveVM format (%d)", tmp); + return; + } + sci.hit_end = true; /* It doesn't iterate over this as a list */ + break; + + case VISIT_SEQ_COMPAT_SECTION_HEADER: + /* + * for VM_SECTION_FULL and VM_SECTION_START + * 'opaque' points to a struct Sectionheader + */ + sh = opaque; + sh->section_id = qemu_get_be32(iv->file); + len = qemu_get_byte(iv->file); + qemu_get_buffer(iv->file, (uint8_t *)sh->idstr, len); + sh->idstr[len] = 0; + sh->instance_id = qemu_get_be32(iv->file); + sh->version_id = qemu_get_be32(iv->file); + DPRINTF(iv, "for '%s'/%s v %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_SECTION_MIN: + /* for VM_SECTION_PART?END where the section ID is already known */ + sh = opaque; + sh->section_id = qemu_get_be32(iv->file); + sci.hit_end = true; /* It doesn't iterate over this as a list */ + break; + + + case VISIT_SEQ_COMPAT_VMSTATE: + /* These don't need anything in the header on the compatibility side */ + sci.hit_end = true; /* It doesn't iterate over this as a list */ + break; + + case VISIT_SEQ_COMPAT_BYTE0TERM: + case VISIT_SEQ_COMPAT_SUBSECLIST: + case VISIT_SEQ_COMPAT_RAMSECLIST: + /* These don't need anything in the header on the compatibility side */ + break; + + case VISIT_SEQ_COMPAT_RAMSECENTRY: + rse_hdr = opaque; + tmp64 = qemu_get_be64(iv->file); + rse_hdr->flags = tmp64 & RAM_SAVE_FLAG_MASK; + rse_hdr->addr = tmp64 - rse_hdr->flags; + if ((rse_hdr->flags & + (RAM_SAVE_FLAG_CONTINUE | RAM_SAVE_FLAG_MEM_SIZE | + RAM_SAVE_FLAG_HOOK)) == 0) { + len = qemu_get_byte(iv->file); + qemu_get_buffer(iv->file, (uint8_t *)rse_hdr->idstr, len); + rse_hdr->idstr[len] = 0; + } + + if (rse_hdr->flags & RAM_SAVE_FLAG_COMPRESS) { + /* Page filled with a byte (normally with 0's) */ + rse_hdr->ch = qemu_get_byte(iv->file); + } else if (rse_hdr->flags & RAM_SAVE_FLAG_XBZRLE) { + /* + * XBZRLE page header: + * 1 byte 'flags' byte (which is only ever a constant 1) + * a 16bit length. + */ + tmp = qemu_get_byte(iv->file); + if (tmp != 1) { + error_setg(errp, "XBZRLE flag byte incorrect (%d)", tmp); + return; + } + len = qemu_get_be16(iv->file); + rse_hdr->len = len; + /* load_xbzrle code fetches it's data itself */ + } + + sci.hit_end = true; /* It doesn't iterate over this as a list */ + break; + + case VISIT_SEQ_COMPAT_SUBSECTION: /* An element in a subsection list */ + sh = opaque; + len = qemu_get_byte(iv->file); + qemu_get_buffer(iv->file, (uint8_t *)sh->idstr, len); + sh->idstr[len] = 0; + sh->version_id = qemu_get_be32(iv->file); + DPRINTF(iv, "for '%s'/%s v %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_BLOB: + /* Nothing in the header, but opaque gets a copy of our QEMUFile - + * other implementations might give a different QEMUFile + */ + *(QEMUFile **)opaque = iv->file; + sci.hit_end = true; /* It doesn't iterate over this as a list */ + break; + + } + + DPRINTF(iv, "for '%s'", name); + qfbi_push_seqcompat(iv, sci); + + + /* We don't need to read anything at this point */ +} + +static void qfbi_end_sequence_compat(Visitor *v, const char* name, + Visit_seq_compat_mode compat_mode, + Error **errp) +{ + QemuFileBinInputVisitor *iv = to_iv(v); + StackEntry *e = qfbi_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 for '%s'", name); + } + g_free(e); +} + +static void qfbi_destroy(Visitor *v, Error **errp) +{ + QemuFileBinInputVisitor *iv = to_iv(v); + + qemu_file_bin_input_visitor_cleanup(iv); +} + +Visitor *qemu_file_bin_input_get_visitor(QemuFileBinInputVisitor *iv) +{ + return &iv->visitor; +} + +void qemu_file_bin_input_visitor_cleanup(QemuFileBinInputVisitor *iv) +{ + g_free(iv); +} + +QemuFileBinInputVisitor *qemu_file_bin_input_visitor_new(QEMUFile *f) +{ + QemuFileBinInputVisitor *v; + + v = g_malloc0(sizeof(*v)); + + v->file = f; + + v->visitor.start_struct = qfbi_start_struct; + v->visitor.end_struct = qfbi_end_struct; + v->visitor.start_list = qfbi_start_list; + v->visitor.next_list = qfbi_next_list; + v->visitor.end_list = qfbi_end_list; + v->visitor.start_array = qfbi_start_array; + v->visitor.next_array = qfbi_next_array; + v->visitor.end_array = qfbi_end_array; + v->visitor.type_int = qfbi_type_int64; + v->visitor.type_buffer = qfbi_type_buffer; + v->visitor.type_uint8 = qfbi_type_uint8; + v->visitor.type_uint16 = qfbi_type_uint16; + v->visitor.type_uint32 = qfbi_type_uint32; + v->visitor.type_uint64 = qfbi_type_uint64; + v->visitor.type_int8 = qfbi_type_int8; + v->visitor.type_int16 = qfbi_type_int16; + v->visitor.type_int32 = qfbi_type_int32; + v->visitor.type_int64 = qfbi_type_int64; + v->visitor.type_bool = qfbi_type_bool; + v->visitor.type_str = qfbi_type_str; + v->visitor.type_str256 = qfbi_type_str256; + v->visitor.destroy = qfbi_destroy; + v->visitor.start_sequence_compat = qfbi_start_sequence_compat; + v->visitor.get_next_type = qfbi_get_next_type; + v->visitor.end_sequence_compat = qfbi_end_sequence_compat; + v->visitor.get_qemufile = qfbi_get_qemufile; + + v->visitor.flags = VISITOR_LOADING; + + QTAILQ_INIT(&v->stack); + v->depth = 0; + + return v; +} -- 1.9.0