From patchwork Thu Mar 21 18:29:27 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stefan Berger X-Patchwork-Id: 229808 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)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 90CDF2C0089 for ; Fri, 22 Mar 2013 05:47:20 +1100 (EST) Received: from localhost ([::1]:35077 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1UIkWE-00010X-RU for incoming@patchwork.ozlabs.org; Thu, 21 Mar 2013 14:47:18 -0400 Received: from eggs.gnu.org ([208.118.235.92]:44500) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1UIkUm-0007yr-3Q for qemu-devel@nongnu.org; Thu, 21 Mar 2013 14:45:57 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1UIkFa-00027n-Ht for qemu-devel@nongnu.org; Thu, 21 Mar 2013 14:30:24 -0400 Received: from e8.ny.us.ibm.com ([32.97.182.138]:37765) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1UIkFa-00026h-63 for qemu-devel@nongnu.org; Thu, 21 Mar 2013 14:30:06 -0400 Received: from /spool/local by e8.ny.us.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Thu, 21 Mar 2013 14:30:05 -0400 Received: from d01dlp03.pok.ibm.com (9.56.250.168) by e8.ny.us.ibm.com (192.168.1.108) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; Thu, 21 Mar 2013 14:30:04 -0400 Received: from d01relay04.pok.ibm.com (d01relay04.pok.ibm.com [9.56.227.236]) by d01dlp03.pok.ibm.com (Postfix) with ESMTP id 60392C90050 for ; Thu, 21 Mar 2013 14:30:03 -0400 (EDT) Received: from d03av03.boulder.ibm.com (d03av03.boulder.ibm.com [9.17.195.169]) by d01relay04.pok.ibm.com (8.13.8/8.13.8/NCO v10.0) with ESMTP id r2LITxGw290792 for ; Thu, 21 Mar 2013 14:30:00 -0400 Received: from d03av03.boulder.ibm.com (loopback [127.0.0.1]) by d03av03.boulder.ibm.com (8.14.4/8.13.1/NCO v10.0 AVout) with ESMTP id r2LITtYS029896 for ; Thu, 21 Mar 2013 12:29:55 -0600 Received: from k-d941f-5.watson.ibm.com (k-d941f-5.watson.ibm.com [9.2.141.165]) by d03av03.boulder.ibm.com (8.14.4/8.13.1/NCO v10.0 AVin) with ESMTP id r2LITnb3029347 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO); Thu, 21 Mar 2013 12:29:50 -0600 Received: from k-d941f-5.watson.ibm.com (localhost.localdomain [127.0.0.1]) by k-d941f-5.watson.ibm.com (8.14.5/8.14.3) with ESMTP id r2LITmcr015217; Thu, 21 Mar 2013 14:29:48 -0400 Received: (from root@localhost) by k-d941f-5.watson.ibm.com (8.14.5/8.14.5/Submit) id r2LITlNa015216; Thu, 21 Mar 2013 14:29:47 -0400 From: Stefan Berger To: stefanb@linux.vnet.ibm.com, qemu-devel@nongnu.org, anthony@codemonkey.ws Date: Thu, 21 Mar 2013 14:29:27 -0400 Message-Id: <1363890571-15146-6-git-send-email-stefanb@linux.vnet.ibm.com> X-Mailer: git-send-email 1.7.11.7 In-Reply-To: <1363890571-15146-1-git-send-email-stefanb@linux.vnet.ibm.com> References: <1363890571-15146-1-git-send-email-stefanb@linux.vnet.ibm.com> X-TM-AS-MML: No X-Content-Scanned: Fidelis XPS MAILER x-cbid: 13032118-9360-0000-0000-0000116C9F96 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.4.x-2.6.x [generic] X-Received-From: 32.97.182.138 Cc: jschopp@linux.vnet.ibm.com, coreyb@linux.vnet.ibm.com, mdroth@linux.vnet.ibm.com, mst@redhat.com Subject: [Qemu-devel] [PATCH v4 5/9] ASN.1 output 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 Implement an output visitor for ASN.1 BER and CER encoding. Cc: Michael Tsirkin Signed-off-by: Stefan Berger Signed-off-by: Joel Schopp --- configure | 2 +- include/qapi/ber-output-visitor.h | 28 ++ include/qapi/ber.h | 107 ++++++ include/qemu-common.h | 2 + qapi/Makefile.objs | 1 + qapi/ber-common.c | 86 +++++ qapi/ber-common.h | 29 ++ qapi/ber-output-visitor.c | 673 ++++++++++++++++++++++++++++++++++++++ util/qemu-file.c | 58 ++++ 9 files changed, 985 insertions(+), 1 deletion(-) create mode 100644 include/qapi/ber-output-visitor.h create mode 100644 include/qapi/ber.h create mode 100644 qapi/ber-common.c create mode 100644 qapi/ber-common.h create mode 100644 qapi/ber-output-visitor.c diff --git a/configure b/configure index 46a7594..5e1d69f 100755 --- a/configure +++ b/configure @@ -2844,7 +2844,7 @@ fi # Do we need libm cat > $TMPC << EOF #include -int main(void) { return isnan(sin(0.0)); } +int main(void) { return isnan(nan("NAN")); } EOF if compile_prog "" "" ; then : diff --git a/include/qapi/ber-output-visitor.h b/include/qapi/ber-output-visitor.h new file mode 100644 index 0000000..bd7e198 --- /dev/null +++ b/include/qapi/ber-output-visitor.h @@ -0,0 +1,28 @@ +/* + * BER Output Visitor header + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Anthony Liguori + * 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. + * + */ + +#ifndef BER_OUTPUT_VISITOR_H +#define BER_OUTPUT_VISITOR_H + +#include "qapi/visitor.h" +#include "qapi/ber.h" + +typedef struct BEROutputVisitor BEROutputVisitor; + +BEROutputVisitor *ber_output_visitor_new(QEMUFile *, BERTypePC mode); +void ber_output_visitor_cleanup(BEROutputVisitor *v); + +Visitor *ber_output_get_visitor(BEROutputVisitor *v); + +#endif diff --git a/include/qapi/ber.h b/include/qapi/ber.h new file mode 100644 index 0000000..ebf7bf1 --- /dev/null +++ b/include/qapi/ber.h @@ -0,0 +1,107 @@ +/* + * ASN.1 Basic Encoding Rules Common functions + * + * Copyright IBM, Corp. 2011, 2013 + * Copyright Red Hat, Inc. 2011 + * + * Authors: + * Stefan Berger + * Michael Tsirkin + * + * 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 QAPI_BER_H +#define QAPI_BER_H + +/* + * This is a subset of BER for QEMU use. + * QEMU will use the DER encoding always with one extension from + * CER: SET and SEQUENCE types can have indefinite-length encoding + * if the encoding is not all immediately available. + * + * We assume that SET encodings can be available or not available, + * and that SEQUENCE encodings are available unless a SEQUENCE includes + * a non-available SET. + * + * The last is an extension to allow an arbitrarily large SET + * to be produced online without knowing the length in advance. + * + * All types used shall be universal, with explicit tagging, to simplify + * use by external tools. + */ + + +#define BER_TYPE_CLASS_SHIFT 6 +#define BER_TYPE_PC_SHIFT 5 + +typedef enum ber_type_class { + BER_TYPE_CLASS_UNIVERSAL = 0x0 << BER_TYPE_CLASS_SHIFT, + BER_TYPE_CLASS_APPLICATION = 0x1 << BER_TYPE_CLASS_SHIFT, + BER_TYPE_CLASS_CONTENT_SPECIFIC = 0x2 << BER_TYPE_CLASS_SHIFT, + BER_TYPE_CLASS_PRIVATE = 0x3 << BER_TYPE_CLASS_SHIFT, + BER_TYPE_CLASS_MASK = 0x3 << BER_TYPE_CLASS_SHIFT /* Mask to get class */ +} BERTypeClass; + +/* P/C bit */ +typedef enum ber_type_p_c { + BER_TYPE_PRIMITIVE = 0x0 << BER_TYPE_PC_SHIFT, + BER_TYPE_CONSTRUCTED = 0x1 << BER_TYPE_PC_SHIFT, + BER_TYPE_P_C_MASK = 0x1 << BER_TYPE_PC_SHIFT /* Mask to get P/C bit */ +} BERTypePC; + +typedef enum ber_type_tag { + BER_TYPE_EOC /* P 0 0*/, + BER_TYPE_BOOLEAN /* P 1 1*/, + BER_TYPE_INTEGER /* P 2 2*/, + BER_TYPE_BIT_STRING /* P/C 3 3*/, + BER_TYPE_OCTET_STRING /* P/C 4 4*/, + BER_TYPE_NULL /* P 5 5*/, + BER_TYPE_OBJECT_ID /* P 6 6*/, + BER_TYPE_OBJECT_DESC /* P 7 7*/, + BER_TYPE_EXTERNAL /* C 8 8*/, + BER_TYPE_REAL /* P 9 9*/, + BER_TYPE_ENUMERATED /* P 10 A*/, + BER_TYPE_EMBEDDED /* C 11 B*/, + BER_TYPE_UTF8_STRING /* P/C 12 C*/, + BER_TYPE_RELATIVE_OID /* P 13 D*/, + BER_TYPE_UNUSED_0xE /* */, + BER_TYPE_UNUSED_0xF /* */, + BER_TYPE_SEQUENCE /* C 16 10*/, + BER_TYPE_SET /* C 17 11*/, + BER_TYPE_NUMERIC_STRING /* P/C 18 12*/, + BER_TYPE_PRINTABLE_STRING /* P/C 19 13*/, + BER_TYPE_T61STRING /* P/C 20 14*/, + BER_TYPE_VIDEOTEX_STRING /* P/C 21 15*/, + BER_TYPE_IA5_STRING /* P/C 22 16*/, + BER_TYPE_UTCTIME /* P/C 23 17*/, + BER_TYPE_GENERALIZED_TIME /* P/C 24 18*/, + BER_TYPE_GRAPHIC_STRING /* P/C 25 19*/, + BER_TYPE_VISIBLE_STRING /* P/C 26 1A*/, + BER_TYPE_GENERAL_STRING /* P/C 27 1B*/, + BER_TYPE_UNIVERSAL_STRING /* P/C 28 1C*/, + BER_TYPE_CHARACTER_STRING /* P/C 29 1D*/, + BER_TYPE_BMP_STRING /* P/C 30 1E*/, + BER_TYPE_LONG_FORM /* - 31 1F*/, + BER_TYPE_TAG_MASK = 0x1f /* Mask to get tag */, + BER_TYPE_CUSTOM_LIST = 0x20, +} BERTypeTag; + +typedef enum ber_length { + /* Special length values */ + BER_LENGTH_INDEFINITE = 0x1 << 7, + BER_LENGTH_RESERVED = 0xFF, + /* Anything else is either short or long */ + BER_LENGTH_SHORT = 0x0 << 7, + BER_LENGTH_LONG = 0x1 << 7, + BER_LENGTH_SHORT_LONG_MASK = 0x1 << 7, + BER_LENGTH_MASK = 0x7F, +} BERLength; + +const char *ber_type_to_str(uint8_t ber_type); +const char *ber_type_pc_to_str(enum ber_type_class ber_type_flags); +const char *ber_type_class_to_str(enum ber_type_class ber_type_flags); + +#endif /* QAPI_BER_H */ + diff --git a/include/qemu-common.h b/include/qemu-common.h index 7794fa7..31ce759 100644 --- a/include/qemu-common.h +++ b/include/qemu-common.h @@ -459,5 +459,7 @@ ssize_t qsb_get_buffer(const QEMUSizedBuffer *, off_t start, size_t count, uint8_t **buf); ssize_t qsb_write_at(QEMUSizedBuffer *qsb, const uint8_t *buf, off_t pos, size_t count); +ssize_t qsb_qfile_write(const QEMUSizedBuffer *qsb, QEMUFile *file, + off_t start, size_t count); #endif diff --git a/qapi/Makefile.objs b/qapi/Makefile.objs index 1f9c973..519e3ee 100644 --- a/qapi/Makefile.objs +++ b/qapi/Makefile.objs @@ -3,3 +3,4 @@ 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 += opts-visitor.o +util-obj-y += ber-common.o ber-output-visitor.o diff --git a/qapi/ber-common.c b/qapi/ber-common.c new file mode 100644 index 0000000..1053c41 --- /dev/null +++ b/qapi/ber-common.c @@ -0,0 +1,86 @@ +/* + * ASN.1 Basic Encoding Rules Common functions + * + * Copyright IBM, Corp. 2011, 2013 + * Copyright Red Hat, Inc. 2011 + * + * Authors: + * Stefan Berger + * Michael Tsirkin + * + * 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 + +#include "qapi/ber.h" + +static const char *ber_type_names[] = { + "BER_TYPE_EOC", + "BER_TYPE_BOOLEAN", + "BER_TYPE_INTEGER", + "BER_TYPE_BIT_STRING", + "BER_TYPE_OCTET_STRING", + "BER_TYPE_NULL", + "BER_TYPE_OBJECT_ID", + "BER_TYPE_OBJECT_DESC", + "BER_TYPE_EXTERNAL", + "BER_TYPE_REAL", + "BER_TYPE_ENUMERATED", + "BER_TYPE_EMBEDDED", + "BER_TYPE_UTF8_STRING", + "BER_TYPE_RELATIVE_OID", + "BER_TYPE_UNUSED_0xE", + "BER_TYPE_UNUSED_0xF", + "BER_TYPE_SEQUENCE", + "BER_TYPE_SET", + "BER_TYPE_NUMERIC_STRING", + "BER_TYPE_PRINTABLE_STRING", + "BER_TYPE_T61STRING", + "BER_TYPE_VIDEOTEX_STRING", + "BER_TYPE_IA5_STRING", + "BER_TYPE_UTCTIME", + "BER_TYPE_GENERALIZED_TIME", + "BER_TYPE_GRAPHIC_STRING", + "BER_TYPE_VISIBLE_STRING", + "BER_TYPE_GENERAL_STRING", + "BER_TYPE_UNIVERSAL_STRING", + "BER_TYPE_CHARACTER_STRING" + "BER_TYPE_BMP_STRING", + "BER_TYPE_LONG_FORM", +}; + +const char *ber_type_to_str(uint8_t ber_type) +{ + return ber_type_names[ber_type & BER_TYPE_TAG_MASK]; +} + +static const char *ber_pc_names[] = { + "BER_PRIMITIVE", + "BER_CONSTRUCTED" +}; + +const char *ber_type_pc_to_str(enum ber_type_class ber_type_flags) +{ + int idx = (ber_type_flags & BER_TYPE_P_C_MASK) >> + BER_TYPE_PC_SHIFT; + + return ber_pc_names[idx]; +} + +static const char *ber_class_names[] = { + "BER_CLASS_UNIVERSAL", + "BER_CLASS_APPLICATION", + "BER_CLASS_CONTEXT", + "BER_CLASS_PRIVATE" +}; + +const char *ber_type_class_to_str(enum ber_type_class ber_type_flags) +{ + int idx = (ber_type_flags & BER_TYPE_CLASS_MASK) >> + BER_TYPE_CLASS_SHIFT; + + return ber_class_names[idx]; +} diff --git a/qapi/ber-common.h b/qapi/ber-common.h new file mode 100644 index 0000000..c9fd2dd --- /dev/null +++ b/qapi/ber-common.h @@ -0,0 +1,29 @@ +/* + * BER Visitor -- common code + * + * Copyright IBM, Corp. 2013 + * + * Authors: + * 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. + * + */ +#ifndef __QAPI_BER_COMMON_H__ +#define __QAPI_BER_COMMON_H__ + +#include + +#include "qemu/compiler.h" + +struct ieee754_buffer { + uint8_t type; + uint8_t length; + uint8_t first; + uint16_t exponent; + uint32_t mant_hi; + uint32_t mant_lo; +} QEMU_PACKED; + +#endif /* __QAPI_BER_COMMON_H__ */ diff --git a/qapi/ber-output-visitor.c b/qapi/ber-output-visitor.c new file mode 100644 index 0000000..75b5ea1 --- /dev/null +++ b/qapi/ber-output-visitor.c @@ -0,0 +1,673 @@ +/* + * BER Output Visitor + * + * Copyright IBM, Corp. 2011, 2013 + * Copyright Red Hat, Inc. 2011 + * + * Authors: + * Anthony Liguori + * Stefan Berger + * Michael Tsirkin + * + * 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 +#include + +#include "qemu-common.h" +#include "qapi/ber-common.h" +#include "qapi/ber-output-visitor.h" +#include "qemu/queue.h" +#include "qemu-common.h" +#include "include/qapi/qmp/qerror.h" +#include "hw/hw.h" +#include "qapi/ber.h" +#include "qapi/visitor-impl.h" + + +#define CER_FRAGMENT_CHUNK_SIZE 1000 + +/*#define BER_DEBUG*/ + +typedef struct QStackEntry { + QEMUFile *qfile; + bool is_list_head; + QTAILQ_ENTRY(QStackEntry) node; +} QStackEntry; + +typedef QTAILQ_HEAD(QStack, QStackEntry) QStack; + +struct BEROutputVisitor { + Visitor visitor; + QStack stack; + QEMUFile *qfile; + + BERTypePC mode; +}; + +static BEROutputVisitor *to_aov(Visitor *v) +{ + return container_of(v, BEROutputVisitor, visitor); +} + +static void ber_output_push(BEROutputVisitor *qov, QEMUFile *qfile, + Error **errp) +{ + QStackEntry *e = g_malloc0(sizeof(*e)); + + e->qfile = qfile; + e->is_list_head = true; + QTAILQ_INSERT_HEAD(&qov->stack, e, node); +} + +static QEMUFile *ber_output_pop(BEROutputVisitor *qov) +{ + QStackEntry *e = QTAILQ_FIRST(&qov->stack); + QEMUFile *qfile; + + QTAILQ_REMOVE(&qov->stack, e, node); + qfile = e->qfile; + g_free(e); + + return qfile; +} + +static unsigned int ber_encode_type(uint8_t *buffer, uint32_t buflen, + enum ber_type_tag ber_type, + uint8_t ber_type_flags, + Error **errp) +{ + unsigned int idx = 0; + + if (buflen < 1) { + error_set(errp, QERR_BUFFER_OVERRUN); + return 0; + } + + if (ber_type > BER_TYPE_LONG_FORM) { + int byte = 4; + uint32_t mask = 0x7f << (7 * byte); + bool do_write = false; + + buffer[0] = ber_type_flags | BER_TYPE_LONG_FORM; + + while (byte >= 0) { + if (!do_write) { + if ((mask & ber_type)) { + do_write = true; + if (1 + byte + 1 > buflen) { + error_set(errp, QERR_BUFFER_OVERRUN); + return 0; + } + } + } + if (do_write) { + buffer[1 + idx] = (ber_type >> (7 * byte)) & 0x7f; + if (byte > 0) { + buffer[1 + idx] |= 0x80; + } + idx++; + } + byte--; + mask = 0x7f << (7 * byte); + } + } else { + buffer[0] = ber_type | ber_type_flags; + } + return 1 + idx; +} + +static unsigned int ber_encode_len(uint8_t *buffer, uint32_t buflen, + uint64_t len, Error **errp) +{ + uint64_t mask = 0xFF00000000000000ULL; + int shift = 64 - 8; + int c = 0; + + if (len <= 0x7f && buflen >= 1) { + buffer[0] = len; + return 1; + } + + while (mask && (mask & len) == 0) { + mask >>= 8; + shift -= 8; + } + + while (shift >= 0) { + if (1 + c + 1 > buflen) { + error_set(errp, QERR_BUFFER_OVERRUN); + return 0; + } + buffer[1+c] = len >> shift; + c++; + shift -= 8; + } + + buffer[0] = BER_LENGTH_LONG | c; + + return 1 + c; +} + +static void ber_output_start_constructed(Visitor *v, uint32_t ber_type, + Error **errp) +{ + BEROutputVisitor *aov = to_aov(v); + uint8_t buf[20]; + unsigned int tag_bytes_written; + + switch (aov->mode) { + case BER_TYPE_PRIMITIVE: + ber_output_push(aov, aov->qfile, errp); + if (error_is_set(errp)) { + return; + } + aov->qfile = qemu_bufopen("w", NULL); + break; + case BER_TYPE_CONSTRUCTED: + ber_output_push(aov, aov->qfile, errp); /* needed for list support */ + if (error_is_set(errp)) { + return; + } + tag_bytes_written = ber_encode_type(buf, sizeof(buf), + ber_type, BER_TYPE_CONSTRUCTED, + errp); + if (error_is_set(errp)) { + return; + } + buf[tag_bytes_written] = BER_LENGTH_INDEFINITE; + if (qemu_write_bytes(aov->qfile, buf, 1 + tag_bytes_written) != + 1 + tag_bytes_written) { + error_setg(errp, "QEMUFile error: Error while writing " + "constructed type"); + return; + } + } +} + +static void ber_output_constructed_ber_close(BEROutputVisitor *aov, + uint32_t ber_type, + Error **errp) +{ + uint8_t buf[20]; + const QEMUSizedBuffer *qsb; + uint64_t len; + unsigned int num_bytes, tag_bytes_written; + QEMUFile *qfile = ber_output_pop(aov); + + tag_bytes_written = ber_encode_type(buf, sizeof(buf), + ber_type, BER_TYPE_CONSTRUCTED, + errp); + if (error_is_set(errp)) { + return; + } + + qsb = qemu_buf_get(aov->qfile); + len = qsb_get_length(qsb); +#ifdef BER_DEBUG + fprintf(stderr, "constructed type (0x%02x, %p) has length %ld bytes\n", + ber_type, aov->qfile, len); +#endif + + num_bytes = ber_encode_len(&buf[tag_bytes_written], + sizeof(buf) - tag_bytes_written, + len, errp); + if (error_is_set(errp)) { + return; + } + + if (qemu_write_bytes(qfile, buf, tag_bytes_written + num_bytes) != + tag_bytes_written + num_bytes || + qsb_qfile_write(qsb, qfile, 0, qsb_get_length(qsb)) != + qsb_get_length(qsb)) { + error_setg(errp, "QEMUFile error: Error while writing buffer"); + return; + } + + qemu_fclose(aov->qfile); + aov->qfile = qfile; + qemu_fflush(qfile); +} + +static void ber_output_end_constructed(Visitor *v, uint32_t ber_type, + Error **errp) +{ + BEROutputVisitor *aov = to_aov(v); + uint8_t buf[20]; + +#ifdef BER_DEBUG + fprintf(stderr, "end set/struct:\n"); +#endif + + switch (aov->mode) { + case BER_TYPE_PRIMITIVE: + ber_output_constructed_ber_close(aov, ber_type, errp); + break; + + case BER_TYPE_CONSTRUCTED: + ber_output_pop(aov); + buf[0] = BER_TYPE_EOC; + buf[1] = 0; + if (qemu_write_bytes(aov->qfile, buf, 2) != 2) { + error_setg(errp, "QEMUFile error: Error while writing buffer " + "with BER_TYPE_EOC"); + return; + } + break; + } +} + +static void ber_output_start_struct(Visitor *v, void **obj, const char *kind, + const char *name, size_t unused, + Error **errp) +{ + ber_output_start_constructed(v, BER_TYPE_SEQUENCE, errp); +} + +static void ber_output_end_struct(Visitor *v, Error **errp) +{ + ber_output_end_constructed(v, BER_TYPE_SEQUENCE, errp); +} + +static void ber_output_start_list(Visitor *v, const char *name, + Error **errp) +{ + ber_output_start_constructed(v, BER_TYPE_CUSTOM_LIST, errp); +} + +static GenericList *ber_output_next_list(Visitor *v, GenericList **listp, + Error **errp) +{ + GenericList *list = *listp; + BEROutputVisitor *bov = to_aov(v); + QStackEntry *e = QTAILQ_FIRST(&bov->stack); + + assert(e); + if (e->is_list_head) { + e->is_list_head = false; + return list; + } + + return list ? list->next : NULL; +} + +static void ber_output_end_list(Visitor *v, Error **errp) +{ + ber_output_end_constructed(v, BER_TYPE_CUSTOM_LIST, errp); +} + +static void ber_output_fragment(Visitor *v, uint32_t ber_type, + uint8_t *buffer, size_t elem_count, + size_t elem_size, Error **errp) +{ + uint32_t offset = 0; + bool fragmented = false; + uint32_t chunk; + unsigned int num_bytes, type_bytes; + uint8_t buf[20]; + uint32_t chunk_size; + BEROutputVisitor *aov = to_aov(v); + size_t buflen = elem_count * elem_size; + size_t n_elms; +#if __BYTE_ORDER == __LITTLE_ENDIAN + size_t i; + uint16_t *d16, *s16; + uint32_t *d32, *s32; + uint64_t *d64, *s64; +#endif + uint8_t *conv_buffer = NULL; + bool is_bigendian; +#if __BYTE_ORDER == __BIG_ENDIAN + is_bigendian = true; +#elif __BYTE_ORDER == __LITTLE_ENDIAN + is_bigendian = false; +#else +# error Unsupported endianess +#endif + + switch (aov->mode) { + case BER_TYPE_CONSTRUCTED: + /* X.690 9.2 */ + fragmented = (buflen > CER_FRAGMENT_CHUNK_SIZE); + chunk_size = 1000; + break; + case BER_TYPE_PRIMITIVE: + chunk_size = 0xffffffff; + break; + } + + if (fragmented) { + ber_output_start_constructed(&aov->visitor, ber_type, errp); + if (error_is_set(errp)) { + return; + } + } + + if (elem_size != sizeof(uint8_t) && !is_bigendian) { + /* intermediate buffer for endianess conversion */ + conv_buffer = g_malloc(chunk_size); + } + + do { + chunk = (buflen - offset > chunk_size) ? chunk_size : buflen - offset; + + /* calc how many >=2 byte-elements cleanly fit into the chunk */ + n_elms = chunk / elem_size; + chunk = n_elms * elem_size; + + type_bytes = ber_encode_type(buf, sizeof(buf), ber_type, 0, + errp); + if (error_is_set(errp)) { + goto error; + } + num_bytes = ber_encode_len(&buf[type_bytes], sizeof(buf) - type_bytes, + chunk, errp); + if (error_is_set(errp)) { + goto error; + } + + + switch (elem_size) { + case sizeof(uint8_t): +#if __BYTE_ORDER == __BIG_ENDIAN + case sizeof(uint16_t): + case sizeof(uint32_t): + case sizeof(uint64_t): +#endif + /* simple write, no endianess conversion */ + conv_buffer = &buffer[offset]; + break; +#if __BYTE_ORDER == __LITTLE_ENDIAN + case sizeof(uint16_t): + d16 = (uint16_t *)conv_buffer; + s16 = (uint16_t *)&buffer[offset]; + for (i = 0; i < n_elms; i++) { + d16[i] = cpu_to_be16(s16[i]); + } + break; + case sizeof(uint32_t): + d32 = (uint32_t *)conv_buffer; + s32 = (uint32_t *)&buffer[offset]; + for (i = 0; i < n_elms; i++) { + d32[i] = cpu_to_be32(s32[i]); + } + break; + case sizeof(uint64_t): + d64 = (uint64_t *)conv_buffer; + s64 = (uint64_t *)&buffer[offset]; + for (i = 0; i < n_elms; i++) { + d64[i] = cpu_to_be64(s64[i]); + } + break; +#endif + default: + error_setg(errp, "Illegal element size %" PRIu64, elem_size); + goto error; + } + + if (qemu_write_bytes(aov->qfile, buf, type_bytes + num_bytes) != + type_bytes + num_bytes || + qemu_write_bytes(aov->qfile, conv_buffer, chunk) != chunk) { + error_setg(errp, "QEMUFile error: Error while writing buffer"); + goto error; + } + + offset += chunk; + } while (offset < buflen); + + if (fragmented) { + ber_output_end_constructed(&aov->visitor, ber_type, errp); + } + +error: + if (elem_size != sizeof(uint8_t) && !is_bigendian) { + g_free(conv_buffer); + } +} + +static void ber_output_int(Visitor *v, int64_t val, uint8_t maxnumbytes, + Error **errp) +{ + uint8_t buf[20]; + int shift = (maxnumbytes - 1) * 8; + uint64_t mask = 0xFF80ULL << (shift - 8); + bool exp_zeros; + int c = 0; + BEROutputVisitor *aov = to_aov(v); + +#ifdef BER_DEBUG + fprintf(stderr, "Writing int 0x%lx (signed=%d, len=%d)\n", + val, is_signed, maxnumbytes); +#endif + + /* + * We encode ints with fixed-witdh so that they will use + * the same number of bytes indepent of their value. + * The 'universal' encoding would not encode a 32bit '0' + * with 4 bytes, so this is an application-specific encoding. + */ + buf[0] = BER_TYPE_CLASS_APPLICATION | BER_TYPE_PRIMITIVE | + BER_TYPE_INTEGER; + + if (maxnumbytes > 1) { + exp_zeros = ((mask & val) == 0) ? true : false; + while (mask != 0xFF) { + if (exp_zeros) { + if ((mask & val) != 0) { + break; + } + } else { + if ((mask & val) != mask) { + break; + } + } + shift -= 8; + mask >>= 8; + } + } + + while (shift >= 0) { + buf[2+c] = val >> shift; + c++; + shift -= 8; + } + buf[1] = c; + + if (qemu_write_bytes(aov->qfile, buf, 1 + 1 + c) != 1 + 1 + c) { + error_setg(errp, "QEMUFile error: Error while writing integer"); + return; + } +} +static void ber_output_type_int(Visitor *v, int64_t *obj, const char *name, + Error **errp) +{ + ber_output_int(v, *obj, sizeof(*obj), errp); +} + +static void ber_output_type_uint8(Visitor *v, uint8_t *obj, + const char *name, Error **errp) +{ + ber_output_int(v, *obj, sizeof(*obj), errp); +} + +static void ber_output_type_uint16(Visitor *v, uint16_t *obj, + const char *name, Error **errp) +{ + ber_output_int(v, *obj, sizeof(*obj), errp); +} + +static void ber_output_type_uint32(Visitor *v, uint32_t *obj, + const char *name, Error **errp) +{ + ber_output_int(v, *obj, sizeof(*obj), errp); +} + +static void ber_output_type_uint64(Visitor *v, uint64_t *obj, + const char *name, Error **errp) +{ + ber_output_int(v, *obj, sizeof(*obj), errp); +} + +static void ber_output_type_int8(Visitor *v, int8_t *obj, + const char *name, Error **errp) +{ + ber_output_int(v, (int64_t)*obj, sizeof(*obj), errp); +} + +static void ber_output_type_int16(Visitor *v, int16_t *obj, + const char *name, Error **errp) +{ + ber_output_int(v, (int64_t)*obj, sizeof(*obj), errp); +} + +static void ber_output_type_int32(Visitor *v, int32_t *obj, + const char *name, Error **errp) +{ + ber_output_int(v, (int64_t)*obj, sizeof(*obj), errp); +} + +static void ber_output_type_int64(Visitor *v, int64_t *obj, + const char *name, Error **errp) +{ + ber_output_int(v, (int64_t)*obj, sizeof(*obj), errp); +} + +static void ber_output_type_bool(Visitor *v, bool *obj, const char *name, + Error **errp) +{ + BEROutputVisitor *aov = to_aov(v); + bool b = 0; + + switch (aov->mode) { + case BER_TYPE_PRIMITIVE: + b = *obj; + break; + case BER_TYPE_CONSTRUCTED: + b = (*obj) ? 0xff : 0; + break; + } + ber_output_fragment(v, BER_TYPE_BOOLEAN, (uint8_t *)&b, sizeof(b), + sizeof(b), errp); +} + +static void ber_output_type_str(Visitor *v, char **obj, const char *name, + Error **errp) +{ +#ifdef BER_DEBUG + fprintf(stderr, "Writing string %s, len = 0x%02x\n", *obj, + (int)strlen(*obj)); +#endif + ber_output_fragment(v, BER_TYPE_IA5_STRING, + (uint8_t *)*obj, + *obj == NULL ? 0 : strlen(*obj), 1, errp); +} + +static void ber_output_sized_buffer(Visitor *v, void **obj, + const char *name, size_t elem_count, + size_t elem_size, Error **errp) +{ + ber_output_fragment(v, BER_TYPE_OCTET_STRING, + *obj, elem_count, elem_size, errp); +} + +static void ber_output_type_number(Visitor *v, double *obj, const char *name, + Error **errp) +{ + BEROutputVisitor *aov = to_aov(v); + GDoubleIEEE754 num; + uint8_t first; + struct ieee754_buffer number; + + /* encode it as fixed-width double */ + number.type = BER_TYPE_CLASS_APPLICATION | BER_TYPE_PRIMITIVE | + BER_TYPE_REAL; + number.length = sizeof(number) - offsetof(struct ieee754_buffer, first); + + num.v_double = *obj; + + if (isnan(*obj)) { + /* special encoding supported here; not found in spec. */ + first = 0x42; + } else if (isinf(*obj)) { + /* spec. 8.5.8 */ + if (num.mpn.sign) { + first = 0x41; /* -oo */ + } else { + first = 0x40; /* +oo */ + } + } else { + first = 0x80; + if (num.mpn.sign) { + first |= 0x40; + } + /* Base 2; 0 for scaling factor; 2nd and 3rd octet encode exp. */ + first |= 0x1; + } + + number.first = first; + number.exponent = cpu_to_be16(num.mpn.biased_exponent); + number.mant_hi = cpu_to_be32(num.mpn.mantissa_high); + number.mant_lo = cpu_to_be32(num.mpn.mantissa_low); + + if (qemu_write_bytes(aov->qfile, (uint8_t *)&number, sizeof(number)) != + sizeof(number)) { + error_setg(errp, "QEMUFile error: Error while writing double."); + } +} + +void ber_output_visitor_cleanup(BEROutputVisitor *v) +{ + QStackEntry *e, *tmp; + + QTAILQ_FOREACH_SAFE(e, &v->stack, node, tmp) { + QTAILQ_REMOVE(&v->stack, e, node); + if (e->qfile) { + qemu_fclose(e->qfile); + } + g_free(e); + } + + g_free(v); +} + + +Visitor *ber_output_get_visitor(BEROutputVisitor *v) +{ + return &v->visitor; +} + +BEROutputVisitor *ber_output_visitor_new(QEMUFile *qfile, + BERTypePC mode) +{ + BEROutputVisitor *v; + + v = g_malloc0(sizeof(*v)); + + v->visitor.start_struct = ber_output_start_struct; + v->visitor.end_struct = ber_output_end_struct; + v->visitor.start_list = ber_output_start_list; + v->visitor.next_list = ber_output_next_list; + v->visitor.end_list = ber_output_end_list; + v->visitor.type_int = ber_output_type_int; + v->visitor.type_uint8 = ber_output_type_uint8; + v->visitor.type_uint16 = ber_output_type_uint16; + v->visitor.type_uint32 = ber_output_type_uint32; + v->visitor.type_uint64 = ber_output_type_uint64; + v->visitor.type_int8 = ber_output_type_int8; + v->visitor.type_int16 = ber_output_type_int16; + v->visitor.type_int32 = ber_output_type_int32; + v->visitor.type_int64 = ber_output_type_int64; + v->visitor.type_bool = ber_output_type_bool; + v->visitor.type_str = ber_output_type_str; + v->visitor.type_sized_buffer = ber_output_sized_buffer; + v->visitor.type_number = ber_output_type_number; + + QTAILQ_INIT(&v->stack); + v->qfile = qfile; + v->mode = mode; + + return v; +} diff --git a/util/qemu-file.c b/util/qemu-file.c index 89b0614..0ba5ae9 100644 --- a/util/qemu-file.c +++ b/util/qemu-file.c @@ -1069,6 +1069,64 @@ QEMUSizedBuffer *qsb_clone(const QEMUSizedBuffer *qsb) return out; } +/** + * Write the contents of a QEMUSizedBuffer into a QEMUFile. + * + * @qsb: A QEMUSizedBuffer + * @qfile: QEMUFile to write into + * @start: start offset of the data in the @qsb + * @count: number of bytes to write into @qfile + * + * Returns the actual number of bytes that were written, + * -EIO in case of an error. + */ +ssize_t qsb_qfile_write(const QEMUSizedBuffer *qsb, QEMUFile *qfile, + off_t start, size_t count) +{ + size_t all_copy, to_copy; + off_t s_off; + const struct iovec *iov; + ssize_t index; + uint8_t *s; + + if (start > qsb->used) { + return 0; + } + + if (start + count > qsb->used) { + count = qsb->used - start; + } + + all_copy = count; + + index = qsb_get_iovec(qsb, start, &s_off); + if (index < 0) { + return 0; + } + + while (all_copy > 0) { + iov = &qsb->iov[index]; + + s = iov->iov_base; + + to_copy = iov->iov_len - s_off; + if (to_copy > all_copy) { + to_copy = all_copy; + } + + if (qemu_write_bytes(qfile, &s[s_off], to_copy) != to_copy) { + return -EIO; + } + + all_copy -= to_copy; + + s_off = 0; + index++; + } + + return count; +} + typedef struct QEMUBuffer { QEMUSizedBuffer *qsb; QEMUFile *file;