* [Qemu-devel] [PATCH 0/9 v2] Implement and test asn1 ber visitors
@ 2013-03-13 3:09 Joel Schopp
2013-03-13 3:09 ` [Qemu-devel] [PATCH 1/9] qemu-file Joel Schopp
` (8 more replies)
0 siblings, 9 replies; 18+ messages in thread
From: Joel Schopp @ 2013-03-13 3:09 UTC (permalink / raw)
To: qemu-devel; +Cc: Joel Schopp
These patches implement asn1 ber visitors for encoding and decoding data.
changed since v1:
Moved .c files into qapi directory
Moved .h files into include/qapi
Added sized buffer code
cleaned up Makefile changes to play nicer
Broke out patches for output/input visitors
New tests in tests/test-visitor-serialization.c
Fixed width integer encoding now marked with application specific bit
Removed qapi array code
Added Mike Roth's previously sent carray qapi patch
misc small cleanups
forward ported to keep up with git HEAD
added real number support
^ permalink raw reply [flat|nested] 18+ messages in thread
* [Qemu-devel] [PATCH 1/9] qemu-file
2013-03-13 3:09 [Qemu-devel] [PATCH 0/9 v2] Implement and test asn1 ber visitors Joel Schopp
@ 2013-03-13 3:09 ` Joel Schopp
2013-03-13 3:09 ` [Qemu-devel] [PATCH 2/9] qapi_c_arrays.diff Joel Schopp
` (7 subsequent siblings)
8 siblings, 0 replies; 18+ messages in thread
From: Joel Schopp @ 2013-03-13 3:09 UTC (permalink / raw)
To: qemu-devel; +Cc: Joel Schopp, Stefan Berger, Michael Tsirkin
This patch reorganizes qemu file operations to be in their own source file
instead of being lumped in savevm.c. Besides being more logical for maintenance
it also makes it easier for future users of the file functions to add tests.
Cc: Michael Tsirkin <mst@redhat.com>
Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
Signed-off-by: Joel Schopp <jschopp@linux.vnet.ibm.com>
---
Makefile.objs | 2 +-
include/migration/qemu-file.h | 6 +
qemu-file.c | 715 +++++++++++++++++++++++++++++++++++++++++
savevm.c | 690 ---------------------------------------
4 files changed, 722 insertions(+), 691 deletions(-)
create mode 100644 qemu-file.c
diff --git a/Makefile.objs b/Makefile.objs
index 8c90b92..fa95873 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -1,7 +1,7 @@
#######################################################################
# Common libraries for tools and emulators
stub-obj-y = stubs/
-util-obj-y = util/ qobject/ qapi/ trace/
+util-obj-y = util/ qobject/ qapi/ trace/ qemu-file.o
#######################################################################
# block-obj-y is code used by both qemu system emulation and qemu-img
diff --git a/include/migration/qemu-file.h b/include/migration/qemu-file.h
index df81261..7194b84 100644
--- a/include/migration/qemu-file.h
+++ b/include/migration/qemu-file.h
@@ -98,6 +98,12 @@ void qemu_file_reset_rate_limit(QEMUFile *f);
void qemu_file_set_rate_limit(QEMUFile *f, int64_t new_rate);
int64_t qemu_file_get_rate_limit(QEMUFile *f);
int qemu_file_get_error(QEMUFile *f);
+void qemu_file_set_error(QEMUFile *f, int ret);
+QEMUFile *qemu_fopen_bdrv(BlockDriverState *bs, int is_writable);
+int qemu_peek_buffer(QEMUFile *f, uint8_t *buf, int size, size_t offset);
+int qemu_peek_byte(QEMUFile *f, int offset);
+void qemu_file_skip(QEMUFile *f, int size);
+void qemu_fflush(QEMUFile *f);
static inline void qemu_put_be64s(QEMUFile *f, const uint64_t *pv)
{
diff --git a/qemu-file.c b/qemu-file.c
new file mode 100644
index 0000000..4fed6d5
--- /dev/null
+++ b/qemu-file.c
@@ -0,0 +1,715 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-common.h"
+#include "hw/hw.h"
+#include "block/block.h"
+#include "qemu/sockets.h"
+
+
+#define IO_BUF_SIZE 32768
+
+struct QEMUFile {
+ const QEMUFileOps *ops;
+ void *opaque;
+ int is_write;
+
+ int64_t bytes_xfer;
+ int64_t xfer_limit;
+
+ int64_t pos; /* start of buffer when writing, end of buffer
+ when reading */
+ int buf_index;
+ int buf_size; /* 0 when writing */
+ uint8_t buf[IO_BUF_SIZE];
+
+ int last_error;
+};
+
+typedef struct QEMUFileStdio
+{
+ FILE *stdio_file;
+ QEMUFile *file;
+} QEMUFileStdio;
+
+typedef struct QEMUFileSocket
+{
+ int fd;
+ QEMUFile *file;
+} QEMUFileSocket;
+
+typedef struct {
+ Coroutine *co;
+ int fd;
+} FDYieldUntilData;
+
+static void fd_coroutine_enter(void *opaque)
+{
+ FDYieldUntilData *data = opaque;
+ qemu_set_fd_handler(data->fd, NULL, NULL, NULL);
+ qemu_coroutine_enter(data->co, NULL);
+}
+
+/**
+ * Yield until a file descriptor becomes readable
+ *
+ * Note that this function clobbers the handlers for the file descriptor.
+ */
+static void coroutine_fn yield_until_fd_readable(int fd)
+{
+ FDYieldUntilData data;
+
+ assert(qemu_in_coroutine());
+ data.co = qemu_coroutine_self();
+ data.fd = fd;
+ qemu_set_fd_handler(fd, fd_coroutine_enter, NULL, &data);
+ qemu_coroutine_yield();
+}
+
+static int socket_get_fd(void *opaque)
+{
+ QEMUFileSocket *s = opaque;
+
+ return s->fd;
+}
+
+static int socket_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
+{
+ QEMUFileSocket *s = opaque;
+ ssize_t len;
+
+ for (;;) {
+ len = qemu_recv(s->fd, buf, size, 0);
+ if (len != -1) {
+ break;
+ }
+ if (socket_error() == EAGAIN) {
+ yield_until_fd_readable(s->fd);
+ } else if (socket_error() != EINTR) {
+ break;
+ }
+ }
+
+ if (len == -1) {
+ len = -socket_error();
+ }
+ return len;
+}
+
+static int socket_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, int size)
+{
+ QEMUFileSocket *s = opaque;
+ ssize_t len;
+
+ len = qemu_send_full(s->fd, buf, size, 0);
+ if (len < size) {
+ len = -socket_error();
+ }
+ return len;
+}
+
+static int socket_close(void *opaque)
+{
+ QEMUFileSocket *s = opaque;
+ closesocket(s->fd);
+ g_free(s);
+ return 0;
+}
+
+static int stdio_get_fd(void *opaque)
+{
+ QEMUFileStdio *s = opaque;
+
+ return fileno(s->stdio_file);
+}
+
+static int stdio_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, int size)
+{
+ QEMUFileStdio *s = opaque;
+ return fwrite(buf, 1, size, s->stdio_file);
+}
+
+static int stdio_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
+{
+ QEMUFileStdio *s = opaque;
+ FILE *fp = s->stdio_file;
+ int bytes;
+
+ for (;;) {
+ clearerr(fp);
+ bytes = fread(buf, 1, size, fp);
+ if (bytes != 0 || !ferror(fp)) {
+ break;
+ }
+ if (errno == EAGAIN) {
+ yield_until_fd_readable(fileno(fp));
+ } else if (errno != EINTR) {
+ break;
+ }
+ }
+ return bytes;
+}
+
+static int stdio_pclose(void *opaque)
+{
+ QEMUFileStdio *s = opaque;
+ int ret;
+ ret = pclose(s->stdio_file);
+ if (ret == -1) {
+ ret = -errno;
+ } else if (!WIFEXITED(ret) || WEXITSTATUS(ret) != 0) {
+ /* close succeeded, but non-zero exit code: */
+ ret = -EIO; /* fake errno value */
+ }
+ g_free(s);
+ return ret;
+}
+
+static int stdio_fclose(void *opaque)
+{
+ QEMUFileStdio *s = opaque;
+ int ret = 0;
+
+ if (s->file->ops->put_buffer) {
+ int fd = fileno(s->stdio_file);
+ struct stat st;
+
+ ret = fstat(fd, &st);
+ if (ret == 0 && S_ISREG(st.st_mode)) {
+ /*
+ * If the file handle is a regular file make sure the
+ * data is flushed to disk before signaling success.
+ */
+ ret = fsync(fd);
+ if (ret != 0) {
+ ret = -errno;
+ return ret;
+ }
+ }
+ }
+ if (fclose(s->stdio_file) == EOF) {
+ ret = -errno;
+ }
+ g_free(s);
+ return ret;
+}
+
+static const QEMUFileOps stdio_pipe_read_ops = {
+ .get_fd = stdio_get_fd,
+ .get_buffer = stdio_get_buffer,
+ .close = stdio_pclose
+};
+
+static const QEMUFileOps stdio_pipe_write_ops = {
+ .get_fd = stdio_get_fd,
+ .put_buffer = stdio_put_buffer,
+ .close = stdio_pclose
+};
+
+QEMUFile *qemu_popen_cmd(const char *command, const char *mode)
+{
+ FILE *stdio_file;
+ QEMUFileStdio *s;
+
+ stdio_file = popen(command, mode);
+ if (stdio_file == NULL) {
+ return NULL;
+ }
+
+ if (mode == NULL || (mode[0] != 'r' && mode[0] != 'w') || mode[1] != 0) {
+ fprintf(stderr, "qemu_popen: Argument validity check failed\n");
+ return NULL;
+ }
+
+ s = g_malloc0(sizeof(QEMUFileStdio));
+
+ s->stdio_file = stdio_file;
+
+ if(mode[0] == 'r') {
+ s->file = qemu_fopen_ops(s, &stdio_pipe_read_ops);
+ } else {
+ s->file = qemu_fopen_ops(s, &stdio_pipe_write_ops);
+ }
+ return s->file;
+}
+
+static const QEMUFileOps stdio_file_read_ops = {
+ .get_fd = stdio_get_fd,
+ .get_buffer = stdio_get_buffer,
+ .close = stdio_fclose
+};
+
+static const QEMUFileOps stdio_file_write_ops = {
+ .get_fd = stdio_get_fd,
+ .put_buffer = stdio_put_buffer,
+ .close = stdio_fclose
+};
+
+QEMUFile *qemu_fdopen(int fd, const char *mode)
+{
+ QEMUFileStdio *s;
+
+ if (mode == NULL ||
+ (mode[0] != 'r' && mode[0] != 'w') ||
+ mode[1] != 'b' || mode[2] != 0) {
+ fprintf(stderr, "qemu_fdopen: Argument validity check failed\n");
+ return NULL;
+ }
+
+ s = g_malloc0(sizeof(QEMUFileStdio));
+ s->stdio_file = fdopen(fd, mode);
+ if (!s->stdio_file)
+ goto fail;
+
+ if(mode[0] == 'r') {
+ s->file = qemu_fopen_ops(s, &stdio_file_read_ops);
+ } else {
+ s->file = qemu_fopen_ops(s, &stdio_file_write_ops);
+ }
+ return s->file;
+
+fail:
+ g_free(s);
+ return NULL;
+}
+
+static const QEMUFileOps socket_read_ops = {
+ .get_fd = socket_get_fd,
+ .get_buffer = socket_get_buffer,
+ .close = socket_close
+};
+
+static const QEMUFileOps socket_write_ops = {
+ .get_fd = socket_get_fd,
+ .put_buffer = socket_put_buffer,
+ .close = socket_close
+};
+
+QEMUFile *qemu_fopen_socket(int fd, const char *mode)
+{
+ QEMUFileSocket *s = g_malloc0(sizeof(QEMUFileSocket));
+
+ if (mode == NULL ||
+ (mode[0] != 'r' && mode[0] != 'w') ||
+ mode[1] != 'b' || mode[2] != 0) {
+ fprintf(stderr, "qemu_fopen: Argument validity check failed\n");
+ return NULL;
+ }
+
+ s->fd = fd;
+ if (mode[0] == 'w') {
+ socket_set_block(s->fd);
+ s->file = qemu_fopen_ops(s, &socket_write_ops);
+ } else {
+ s->file = qemu_fopen_ops(s, &socket_read_ops);
+ }
+ return s->file;
+}
+
+QEMUFile *qemu_fopen(const char *filename, const char *mode)
+{
+ QEMUFileStdio *s;
+
+ if (mode == NULL ||
+ (mode[0] != 'r' && mode[0] != 'w') ||
+ mode[1] != 'b' || mode[2] != 0) {
+ fprintf(stderr, "qemu_fopen: Argument validity check failed\n");
+ return NULL;
+ }
+
+ s = g_malloc0(sizeof(QEMUFileStdio));
+
+ s->stdio_file = fopen(filename, mode);
+ if (!s->stdio_file)
+ goto fail;
+
+ if(mode[0] == 'w') {
+ s->file = qemu_fopen_ops(s, &stdio_file_write_ops);
+ } else {
+ s->file = qemu_fopen_ops(s, &stdio_file_read_ops);
+ }
+ return s->file;
+fail:
+ g_free(s);
+ return NULL;
+}
+
+static int block_put_buffer(void *opaque, const uint8_t *buf,
+ int64_t pos, int size)
+{
+ bdrv_save_vmstate(opaque, buf, pos, size);
+ return size;
+}
+
+static int block_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
+{
+ return bdrv_load_vmstate(opaque, buf, pos, size);
+}
+
+static int bdrv_fclose(void *opaque)
+{
+ return bdrv_flush(opaque);
+}
+
+static const QEMUFileOps bdrv_read_ops = {
+ .get_buffer = block_get_buffer,
+ .close = bdrv_fclose
+};
+
+static const QEMUFileOps bdrv_write_ops = {
+ .put_buffer = block_put_buffer,
+ .close = bdrv_fclose
+};
+
+QEMUFile *qemu_fopen_bdrv(BlockDriverState *bs, int is_writable)
+{
+ if (is_writable)
+ return qemu_fopen_ops(bs, &bdrv_write_ops);
+ return qemu_fopen_ops(bs, &bdrv_read_ops);
+}
+
+QEMUFile *qemu_fopen_ops(void *opaque, const QEMUFileOps *ops)
+{
+ QEMUFile *f;
+
+ f = g_malloc0(sizeof(QEMUFile));
+
+ f->opaque = opaque;
+ f->ops = ops;
+ f->is_write = 0;
+ return f;
+}
+
+int qemu_file_get_error(QEMUFile *f)
+{
+ return f->last_error;
+}
+
+void qemu_file_set_error(QEMUFile *f, int ret)
+{
+ if (f->last_error == 0) {
+ f->last_error = ret;
+ }
+}
+
+/** Flushes QEMUFile buffer
+ *
+ */
+void qemu_fflush(QEMUFile *f)
+{
+ int ret = 0;
+
+ if (!f->ops->put_buffer) {
+ return;
+ }
+ if (f->is_write && f->buf_index > 0) {
+ ret = f->ops->put_buffer(f->opaque, f->buf, f->pos, f->buf_index);
+ if (ret >= 0) {
+ f->pos += f->buf_index;
+ }
+ f->buf_index = 0;
+ }
+ if (ret < 0) {
+ qemu_file_set_error(f, ret);
+ }
+}
+
+static void qemu_fill_buffer(QEMUFile *f)
+{
+ int len;
+ int pending;
+
+ if (!f->ops->get_buffer)
+ return;
+
+ if (f->is_write)
+ abort();
+
+ pending = f->buf_size - f->buf_index;
+ if (pending > 0) {
+ memmove(f->buf, f->buf + f->buf_index, pending);
+ }
+ f->buf_index = 0;
+ f->buf_size = pending;
+
+ len = f->ops->get_buffer(f->opaque, f->buf + pending, f->pos,
+ IO_BUF_SIZE - pending);
+ if (len > 0) {
+ f->buf_size += len;
+ f->pos += len;
+ } else if (len == 0) {
+ qemu_file_set_error(f, -EIO);
+ } else if (len != -EAGAIN)
+ qemu_file_set_error(f, len);
+}
+
+int qemu_get_fd(QEMUFile *f)
+{
+ if (f->ops->get_fd) {
+ return f->ops->get_fd(f->opaque);
+ }
+ return -1;
+}
+
+/** Closes the file
+ *
+ * Returns negative error value if any error happened on previous operations or
+ * while closing the file. Returns 0 or positive number on success.
+ *
+ * The meaning of return value on success depends on the specific backend
+ * being used.
+ */
+int qemu_fclose(QEMUFile *f)
+{
+ int ret;
+ qemu_fflush(f);
+ ret = qemu_file_get_error(f);
+
+ if (f->ops->close) {
+ int ret2 = f->ops->close(f->opaque);
+ if (ret >= 0) {
+ ret = ret2;
+ }
+ }
+ /* If any error was spotted before closing, we should report it
+ * instead of the close() return value.
+ */
+ if (f->last_error) {
+ ret = f->last_error;
+ }
+ g_free(f);
+ return ret;
+}
+
+void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size)
+{
+ int l;
+
+ if (f->last_error) {
+ return;
+ }
+
+ if (f->is_write == 0 && f->buf_index > 0) {
+ fprintf(stderr,
+ "Attempted to write to buffer while read buffer is not empty\n");
+ abort();
+ }
+
+ while (size > 0) {
+ l = IO_BUF_SIZE - f->buf_index;
+ if (l > size)
+ l = size;
+ memcpy(f->buf + f->buf_index, buf, l);
+ f->is_write = 1;
+ f->buf_index += l;
+ f->bytes_xfer += l;
+ buf += l;
+ size -= l;
+ if (f->buf_index >= IO_BUF_SIZE) {
+ qemu_fflush(f);
+ if (qemu_file_get_error(f)) {
+ break;
+ }
+ }
+ }
+}
+
+void qemu_put_byte(QEMUFile *f, int v)
+{
+ if (f->last_error) {
+ return;
+ }
+
+ if (f->is_write == 0 && f->buf_index > 0) {
+ fprintf(stderr,
+ "Attempted to write to buffer while read buffer is not empty\n");
+ abort();
+ }
+
+ f->buf[f->buf_index++] = v;
+ f->is_write = 1;
+ if (f->buf_index >= IO_BUF_SIZE) {
+ qemu_fflush(f);
+ }
+}
+
+void qemu_file_skip(QEMUFile *f, int size)
+{
+ if (f->buf_index + size <= f->buf_size) {
+ f->buf_index += size;
+ }
+}
+
+int qemu_peek_buffer(QEMUFile *f, uint8_t *buf, int size, size_t offset)
+{
+ int pending;
+ int index;
+
+ if (f->is_write) {
+ abort();
+ }
+
+ index = f->buf_index + offset;
+ pending = f->buf_size - index;
+ if (pending < size) {
+ qemu_fill_buffer(f);
+ index = f->buf_index + offset;
+ pending = f->buf_size - index;
+ }
+
+ if (pending <= 0) {
+ return 0;
+ }
+ if (size > pending) {
+ size = pending;
+ }
+
+ memcpy(buf, f->buf + index, size);
+ return size;
+}
+
+int qemu_get_buffer(QEMUFile *f, uint8_t *buf, int size)
+{
+ int pending = size;
+ int done = 0;
+
+ while (pending > 0) {
+ int res;
+
+ res = qemu_peek_buffer(f, buf, pending, 0);
+ if (res == 0) {
+ return done;
+ }
+ qemu_file_skip(f, res);
+ buf += res;
+ pending -= res;
+ done += res;
+ }
+ return done;
+}
+
+int qemu_peek_byte(QEMUFile *f, int offset)
+{
+ int index = f->buf_index + offset;
+
+ if (f->is_write) {
+ abort();
+ }
+
+ if (index >= f->buf_size) {
+ qemu_fill_buffer(f);
+ index = f->buf_index + offset;
+ if (index >= f->buf_size) {
+ return 0;
+ }
+ }
+ return f->buf[index];
+}
+
+int qemu_get_byte(QEMUFile *f)
+{
+ int result;
+
+ result = qemu_peek_byte(f, 0);
+ qemu_file_skip(f, 1);
+ return result;
+}
+
+int64_t qemu_ftell(QEMUFile *f)
+{
+ qemu_fflush(f);
+ return f->pos;
+}
+
+int qemu_file_rate_limit(QEMUFile *f)
+{
+ if (qemu_file_get_error(f)) {
+ return 1;
+ }
+ if (f->xfer_limit > 0 && f->bytes_xfer > f->xfer_limit) {
+ return 1;
+ }
+ return 0;
+}
+
+int64_t qemu_file_get_rate_limit(QEMUFile *f)
+{
+ return f->xfer_limit;
+}
+
+void qemu_file_set_rate_limit(QEMUFile *f, int64_t limit)
+{
+ f->xfer_limit = limit;
+}
+
+void qemu_file_reset_rate_limit(QEMUFile *f)
+{
+ f->bytes_xfer = 0;
+}
+
+void qemu_put_be16(QEMUFile *f, unsigned int v)
+{
+ qemu_put_byte(f, v >> 8);
+ qemu_put_byte(f, v);
+}
+
+void qemu_put_be32(QEMUFile *f, unsigned int v)
+{
+ qemu_put_byte(f, v >> 24);
+ qemu_put_byte(f, v >> 16);
+ qemu_put_byte(f, v >> 8);
+ qemu_put_byte(f, v);
+}
+
+void qemu_put_be64(QEMUFile *f, uint64_t v)
+{
+ qemu_put_be32(f, v >> 32);
+ qemu_put_be32(f, v);
+}
+
+unsigned int qemu_get_be16(QEMUFile *f)
+{
+ unsigned int v;
+ v = qemu_get_byte(f) << 8;
+ v |= qemu_get_byte(f);
+ return v;
+}
+
+unsigned int qemu_get_be32(QEMUFile *f)
+{
+ unsigned int v;
+ v = qemu_get_byte(f) << 24;
+ v |= qemu_get_byte(f) << 16;
+ v |= qemu_get_byte(f) << 8;
+ v |= qemu_get_byte(f);
+ return v;
+}
+
+uint64_t qemu_get_be64(QEMUFile *f)
+{
+ uint64_t v;
+ v = (uint64_t)qemu_get_be32(f) << 32;
+ v |= qemu_get_be32(f);
+ return v;
+}
+
diff --git a/savevm.c b/savevm.c
index 147e2d2..6b4bd78 100644
--- a/savevm.c
+++ b/savevm.c
@@ -109,696 +109,6 @@ void qemu_announce_self(void)
qemu_announce_self_once(&timer);
}
-/***********************************************************/
-/* savevm/loadvm support */
-
-#define IO_BUF_SIZE 32768
-
-struct QEMUFile {
- const QEMUFileOps *ops;
- void *opaque;
- int is_write;
-
- int64_t bytes_xfer;
- int64_t xfer_limit;
-
- int64_t pos; /* start of buffer when writing, end of buffer
- when reading */
- int buf_index;
- int buf_size; /* 0 when writing */
- uint8_t buf[IO_BUF_SIZE];
-
- int last_error;
-};
-
-typedef struct QEMUFileStdio
-{
- FILE *stdio_file;
- QEMUFile *file;
-} QEMUFileStdio;
-
-typedef struct QEMUFileSocket
-{
- int fd;
- QEMUFile *file;
-} QEMUFileSocket;
-
-typedef struct {
- Coroutine *co;
- int fd;
-} FDYieldUntilData;
-
-static void fd_coroutine_enter(void *opaque)
-{
- FDYieldUntilData *data = opaque;
- qemu_set_fd_handler(data->fd, NULL, NULL, NULL);
- qemu_coroutine_enter(data->co, NULL);
-}
-
-/**
- * Yield until a file descriptor becomes readable
- *
- * Note that this function clobbers the handlers for the file descriptor.
- */
-static void coroutine_fn yield_until_fd_readable(int fd)
-{
- FDYieldUntilData data;
-
- assert(qemu_in_coroutine());
- data.co = qemu_coroutine_self();
- data.fd = fd;
- qemu_set_fd_handler(fd, fd_coroutine_enter, NULL, &data);
- qemu_coroutine_yield();
-}
-
-static int socket_get_fd(void *opaque)
-{
- QEMUFileSocket *s = opaque;
-
- return s->fd;
-}
-
-static int socket_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
-{
- QEMUFileSocket *s = opaque;
- ssize_t len;
-
- for (;;) {
- len = qemu_recv(s->fd, buf, size, 0);
- if (len != -1) {
- break;
- }
- if (socket_error() == EAGAIN) {
- yield_until_fd_readable(s->fd);
- } else if (socket_error() != EINTR) {
- break;
- }
- }
-
- if (len == -1) {
- len = -socket_error();
- }
- return len;
-}
-
-static int socket_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, int size)
-{
- QEMUFileSocket *s = opaque;
- ssize_t len;
-
- len = qemu_send_full(s->fd, buf, size, 0);
- if (len < size) {
- len = -socket_error();
- }
- return len;
-}
-
-static int socket_close(void *opaque)
-{
- QEMUFileSocket *s = opaque;
- closesocket(s->fd);
- g_free(s);
- return 0;
-}
-
-static int stdio_get_fd(void *opaque)
-{
- QEMUFileStdio *s = opaque;
-
- return fileno(s->stdio_file);
-}
-
-static int stdio_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, int size)
-{
- QEMUFileStdio *s = opaque;
- return fwrite(buf, 1, size, s->stdio_file);
-}
-
-static int stdio_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
-{
- QEMUFileStdio *s = opaque;
- FILE *fp = s->stdio_file;
- int bytes;
-
- for (;;) {
- clearerr(fp);
- bytes = fread(buf, 1, size, fp);
- if (bytes != 0 || !ferror(fp)) {
- break;
- }
- if (errno == EAGAIN) {
- yield_until_fd_readable(fileno(fp));
- } else if (errno != EINTR) {
- break;
- }
- }
- return bytes;
-}
-
-static int stdio_pclose(void *opaque)
-{
- QEMUFileStdio *s = opaque;
- int ret;
- ret = pclose(s->stdio_file);
- if (ret == -1) {
- ret = -errno;
- } else if (!WIFEXITED(ret) || WEXITSTATUS(ret) != 0) {
- /* close succeeded, but non-zero exit code: */
- ret = -EIO; /* fake errno value */
- }
- g_free(s);
- return ret;
-}
-
-static int stdio_fclose(void *opaque)
-{
- QEMUFileStdio *s = opaque;
- int ret = 0;
-
- if (s->file->ops->put_buffer) {
- int fd = fileno(s->stdio_file);
- struct stat st;
-
- ret = fstat(fd, &st);
- if (ret == 0 && S_ISREG(st.st_mode)) {
- /*
- * If the file handle is a regular file make sure the
- * data is flushed to disk before signaling success.
- */
- ret = fsync(fd);
- if (ret != 0) {
- ret = -errno;
- return ret;
- }
- }
- }
- if (fclose(s->stdio_file) == EOF) {
- ret = -errno;
- }
- g_free(s);
- return ret;
-}
-
-static const QEMUFileOps stdio_pipe_read_ops = {
- .get_fd = stdio_get_fd,
- .get_buffer = stdio_get_buffer,
- .close = stdio_pclose
-};
-
-static const QEMUFileOps stdio_pipe_write_ops = {
- .get_fd = stdio_get_fd,
- .put_buffer = stdio_put_buffer,
- .close = stdio_pclose
-};
-
-QEMUFile *qemu_popen_cmd(const char *command, const char *mode)
-{
- FILE *stdio_file;
- QEMUFileStdio *s;
-
- stdio_file = popen(command, mode);
- if (stdio_file == NULL) {
- return NULL;
- }
-
- if (mode == NULL || (mode[0] != 'r' && mode[0] != 'w') || mode[1] != 0) {
- fprintf(stderr, "qemu_popen: Argument validity check failed\n");
- return NULL;
- }
-
- s = g_malloc0(sizeof(QEMUFileStdio));
-
- s->stdio_file = stdio_file;
-
- if(mode[0] == 'r') {
- s->file = qemu_fopen_ops(s, &stdio_pipe_read_ops);
- } else {
- s->file = qemu_fopen_ops(s, &stdio_pipe_write_ops);
- }
- return s->file;
-}
-
-static const QEMUFileOps stdio_file_read_ops = {
- .get_fd = stdio_get_fd,
- .get_buffer = stdio_get_buffer,
- .close = stdio_fclose
-};
-
-static const QEMUFileOps stdio_file_write_ops = {
- .get_fd = stdio_get_fd,
- .put_buffer = stdio_put_buffer,
- .close = stdio_fclose
-};
-
-QEMUFile *qemu_fdopen(int fd, const char *mode)
-{
- QEMUFileStdio *s;
-
- if (mode == NULL ||
- (mode[0] != 'r' && mode[0] != 'w') ||
- mode[1] != 'b' || mode[2] != 0) {
- fprintf(stderr, "qemu_fdopen: Argument validity check failed\n");
- return NULL;
- }
-
- s = g_malloc0(sizeof(QEMUFileStdio));
- s->stdio_file = fdopen(fd, mode);
- if (!s->stdio_file)
- goto fail;
-
- if(mode[0] == 'r') {
- s->file = qemu_fopen_ops(s, &stdio_file_read_ops);
- } else {
- s->file = qemu_fopen_ops(s, &stdio_file_write_ops);
- }
- return s->file;
-
-fail:
- g_free(s);
- return NULL;
-}
-
-static const QEMUFileOps socket_read_ops = {
- .get_fd = socket_get_fd,
- .get_buffer = socket_get_buffer,
- .close = socket_close
-};
-
-static const QEMUFileOps socket_write_ops = {
- .get_fd = socket_get_fd,
- .put_buffer = socket_put_buffer,
- .close = socket_close
-};
-
-QEMUFile *qemu_fopen_socket(int fd, const char *mode)
-{
- QEMUFileSocket *s = g_malloc0(sizeof(QEMUFileSocket));
-
- if (mode == NULL ||
- (mode[0] != 'r' && mode[0] != 'w') ||
- mode[1] != 'b' || mode[2] != 0) {
- fprintf(stderr, "qemu_fopen: Argument validity check failed\n");
- return NULL;
- }
-
- s->fd = fd;
- if (mode[0] == 'w') {
- socket_set_block(s->fd);
- s->file = qemu_fopen_ops(s, &socket_write_ops);
- } else {
- s->file = qemu_fopen_ops(s, &socket_read_ops);
- }
- return s->file;
-}
-
-QEMUFile *qemu_fopen(const char *filename, const char *mode)
-{
- QEMUFileStdio *s;
-
- if (mode == NULL ||
- (mode[0] != 'r' && mode[0] != 'w') ||
- mode[1] != 'b' || mode[2] != 0) {
- fprintf(stderr, "qemu_fopen: Argument validity check failed\n");
- return NULL;
- }
-
- s = g_malloc0(sizeof(QEMUFileStdio));
-
- s->stdio_file = fopen(filename, mode);
- if (!s->stdio_file)
- goto fail;
-
- if(mode[0] == 'w') {
- s->file = qemu_fopen_ops(s, &stdio_file_write_ops);
- } else {
- s->file = qemu_fopen_ops(s, &stdio_file_read_ops);
- }
- return s->file;
-fail:
- g_free(s);
- return NULL;
-}
-
-static int block_put_buffer(void *opaque, const uint8_t *buf,
- int64_t pos, int size)
-{
- bdrv_save_vmstate(opaque, buf, pos, size);
- return size;
-}
-
-static int block_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
-{
- return bdrv_load_vmstate(opaque, buf, pos, size);
-}
-
-static int bdrv_fclose(void *opaque)
-{
- return bdrv_flush(opaque);
-}
-
-static const QEMUFileOps bdrv_read_ops = {
- .get_buffer = block_get_buffer,
- .close = bdrv_fclose
-};
-
-static const QEMUFileOps bdrv_write_ops = {
- .put_buffer = block_put_buffer,
- .close = bdrv_fclose
-};
-
-static QEMUFile *qemu_fopen_bdrv(BlockDriverState *bs, int is_writable)
-{
- if (is_writable)
- return qemu_fopen_ops(bs, &bdrv_write_ops);
- return qemu_fopen_ops(bs, &bdrv_read_ops);
-}
-
-QEMUFile *qemu_fopen_ops(void *opaque, const QEMUFileOps *ops)
-{
- QEMUFile *f;
-
- f = g_malloc0(sizeof(QEMUFile));
-
- f->opaque = opaque;
- f->ops = ops;
- f->is_write = 0;
- return f;
-}
-
-int qemu_file_get_error(QEMUFile *f)
-{
- return f->last_error;
-}
-
-static void qemu_file_set_error(QEMUFile *f, int ret)
-{
- if (f->last_error == 0) {
- f->last_error = ret;
- }
-}
-
-/** Flushes QEMUFile buffer
- *
- */
-static void qemu_fflush(QEMUFile *f)
-{
- int ret = 0;
-
- if (!f->ops->put_buffer) {
- return;
- }
- if (f->is_write && f->buf_index > 0) {
- ret = f->ops->put_buffer(f->opaque, f->buf, f->pos, f->buf_index);
- if (ret >= 0) {
- f->pos += f->buf_index;
- }
- f->buf_index = 0;
- }
- if (ret < 0) {
- qemu_file_set_error(f, ret);
- }
-}
-
-static void qemu_fill_buffer(QEMUFile *f)
-{
- int len;
- int pending;
-
- if (!f->ops->get_buffer)
- return;
-
- if (f->is_write)
- abort();
-
- pending = f->buf_size - f->buf_index;
- if (pending > 0) {
- memmove(f->buf, f->buf + f->buf_index, pending);
- }
- f->buf_index = 0;
- f->buf_size = pending;
-
- len = f->ops->get_buffer(f->opaque, f->buf + pending, f->pos,
- IO_BUF_SIZE - pending);
- if (len > 0) {
- f->buf_size += len;
- f->pos += len;
- } else if (len == 0) {
- qemu_file_set_error(f, -EIO);
- } else if (len != -EAGAIN)
- qemu_file_set_error(f, len);
-}
-
-int qemu_get_fd(QEMUFile *f)
-{
- if (f->ops->get_fd) {
- return f->ops->get_fd(f->opaque);
- }
- return -1;
-}
-
-/** Closes the file
- *
- * Returns negative error value if any error happened on previous operations or
- * while closing the file. Returns 0 or positive number on success.
- *
- * The meaning of return value on success depends on the specific backend
- * being used.
- */
-int qemu_fclose(QEMUFile *f)
-{
- int ret;
- qemu_fflush(f);
- ret = qemu_file_get_error(f);
-
- if (f->ops->close) {
- int ret2 = f->ops->close(f->opaque);
- if (ret >= 0) {
- ret = ret2;
- }
- }
- /* If any error was spotted before closing, we should report it
- * instead of the close() return value.
- */
- if (f->last_error) {
- ret = f->last_error;
- }
- g_free(f);
- return ret;
-}
-
-void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size)
-{
- int l;
-
- if (f->last_error) {
- return;
- }
-
- if (f->is_write == 0 && f->buf_index > 0) {
- fprintf(stderr,
- "Attempted to write to buffer while read buffer is not empty\n");
- abort();
- }
-
- while (size > 0) {
- l = IO_BUF_SIZE - f->buf_index;
- if (l > size)
- l = size;
- memcpy(f->buf + f->buf_index, buf, l);
- f->is_write = 1;
- f->buf_index += l;
- f->bytes_xfer += l;
- buf += l;
- size -= l;
- if (f->buf_index >= IO_BUF_SIZE) {
- qemu_fflush(f);
- if (qemu_file_get_error(f)) {
- break;
- }
- }
- }
-}
-
-void qemu_put_byte(QEMUFile *f, int v)
-{
- if (f->last_error) {
- return;
- }
-
- if (f->is_write == 0 && f->buf_index > 0) {
- fprintf(stderr,
- "Attempted to write to buffer while read buffer is not empty\n");
- abort();
- }
-
- f->buf[f->buf_index++] = v;
- f->is_write = 1;
- if (f->buf_index >= IO_BUF_SIZE) {
- qemu_fflush(f);
- }
-}
-
-static void qemu_file_skip(QEMUFile *f, int size)
-{
- if (f->buf_index + size <= f->buf_size) {
- f->buf_index += size;
- }
-}
-
-static int qemu_peek_buffer(QEMUFile *f, uint8_t *buf, int size, size_t offset)
-{
- int pending;
- int index;
-
- if (f->is_write) {
- abort();
- }
-
- index = f->buf_index + offset;
- pending = f->buf_size - index;
- if (pending < size) {
- qemu_fill_buffer(f);
- index = f->buf_index + offset;
- pending = f->buf_size - index;
- }
-
- if (pending <= 0) {
- return 0;
- }
- if (size > pending) {
- size = pending;
- }
-
- memcpy(buf, f->buf + index, size);
- return size;
-}
-
-int qemu_get_buffer(QEMUFile *f, uint8_t *buf, int size)
-{
- int pending = size;
- int done = 0;
-
- while (pending > 0) {
- int res;
-
- res = qemu_peek_buffer(f, buf, pending, 0);
- if (res == 0) {
- return done;
- }
- qemu_file_skip(f, res);
- buf += res;
- pending -= res;
- done += res;
- }
- return done;
-}
-
-static int qemu_peek_byte(QEMUFile *f, int offset)
-{
- int index = f->buf_index + offset;
-
- if (f->is_write) {
- abort();
- }
-
- if (index >= f->buf_size) {
- qemu_fill_buffer(f);
- index = f->buf_index + offset;
- if (index >= f->buf_size) {
- return 0;
- }
- }
- return f->buf[index];
-}
-
-int qemu_get_byte(QEMUFile *f)
-{
- int result;
-
- result = qemu_peek_byte(f, 0);
- qemu_file_skip(f, 1);
- return result;
-}
-
-int64_t qemu_ftell(QEMUFile *f)
-{
- qemu_fflush(f);
- return f->pos;
-}
-
-int qemu_file_rate_limit(QEMUFile *f)
-{
- if (qemu_file_get_error(f)) {
- return 1;
- }
- if (f->xfer_limit > 0 && f->bytes_xfer > f->xfer_limit) {
- return 1;
- }
- return 0;
-}
-
-int64_t qemu_file_get_rate_limit(QEMUFile *f)
-{
- return f->xfer_limit;
-}
-
-void qemu_file_set_rate_limit(QEMUFile *f, int64_t limit)
-{
- f->xfer_limit = limit;
-}
-
-void qemu_file_reset_rate_limit(QEMUFile *f)
-{
- f->bytes_xfer = 0;
-}
-
-void qemu_put_be16(QEMUFile *f, unsigned int v)
-{
- qemu_put_byte(f, v >> 8);
- qemu_put_byte(f, v);
-}
-
-void qemu_put_be32(QEMUFile *f, unsigned int v)
-{
- qemu_put_byte(f, v >> 24);
- qemu_put_byte(f, v >> 16);
- qemu_put_byte(f, v >> 8);
- qemu_put_byte(f, v);
-}
-
-void qemu_put_be64(QEMUFile *f, uint64_t v)
-{
- qemu_put_be32(f, v >> 32);
- qemu_put_be32(f, v);
-}
-
-unsigned int qemu_get_be16(QEMUFile *f)
-{
- unsigned int v;
- v = qemu_get_byte(f) << 8;
- v |= qemu_get_byte(f);
- return v;
-}
-
-unsigned int qemu_get_be32(QEMUFile *f)
-{
- unsigned int v;
- v = qemu_get_byte(f) << 24;
- v |= qemu_get_byte(f) << 16;
- v |= qemu_get_byte(f) << 8;
- v |= qemu_get_byte(f);
- return v;
-}
-
-uint64_t qemu_get_be64(QEMUFile *f)
-{
- uint64_t v;
- v = (uint64_t)qemu_get_be32(f) << 32;
- v |= qemu_get_be32(f);
- return v;
-}
-
-
/* timer */
void qemu_put_timer(QEMUFile *f, QEMUTimer *ts)
--
1.7.10.4
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [Qemu-devel] [PATCH 2/9] qapi_c_arrays.diff
2013-03-13 3:09 [Qemu-devel] [PATCH 0/9 v2] Implement and test asn1 ber visitors Joel Schopp
2013-03-13 3:09 ` [Qemu-devel] [PATCH 1/9] qemu-file Joel Schopp
@ 2013-03-13 3:09 ` Joel Schopp
2013-03-13 12:08 ` Eric Blake
2013-03-13 3:09 ` [Qemu-devel] [PATCH 3/9] two new file wrappers Joel Schopp
` (6 subsequent siblings)
8 siblings, 1 reply; 18+ messages in thread
From: Joel Schopp @ 2013-03-13 3:09 UTC (permalink / raw)
To: qemu-devel; +Cc: Joel Schopp, Paolo Bonzini, Michael Roth, Michael Tsirkin
Forward ported Mike's previously sent patch
(see http://lists.gnu.org/archive/html/qemu-devel/2012-10/msg05782.html ) in my
series since it impelments a qapi array interface the ASN.1 BER visitor needs.
Generally these will be serialized into lists, but the
representation can be of any form so long as it can
be deserialized into a single-dimension C array.
Cc: Michael Tsirkin <mst@redhat.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
Signed-off-by: Joel Schopp <jschopp@linux.vnet.ibm.com>
---
include/qapi/visitor-impl.h | 4 ++++
include/qapi/visitor.h | 4 ++++
qapi/qapi-visit-core.c | 25 +++++++++++++++++++++++++
3 files changed, 33 insertions(+)
diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
index 5159964..79fe039 100644
--- a/include/qapi/visitor-impl.h
+++ b/include/qapi/visitor-impl.h
@@ -34,6 +34,10 @@ struct Visitor
void (*type_str)(Visitor *v, char **obj, const char *name, Error **errp);
void (*type_number)(Visitor *v, double *obj, const char *name,
Error **errp);
+ void (*start_carray)(Visitor *v, void **obj, const char *name,
+ size_t elem_count, size_t elem_size, Error **errp);
+ void (*next_carray)(Visitor *v, Error **errp);
+ void (*end_carray)(Visitor *v, Error **errp);
/* May be NULL */
void (*start_optional)(Visitor *v, bool *present, const char *name,
diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
index 1fef18c..49f411f 100644
--- a/include/qapi/visitor.h
+++ b/include/qapi/visitor.h
@@ -51,5 +51,9 @@ void visit_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp);
void visit_type_bool(Visitor *v, bool *obj, const char *name, Error **errp);
void visit_type_str(Visitor *v, char **obj, const char *name, Error **errp);
void visit_type_number(Visitor *v, double *obj, const char *name, Error **errp);
+void visit_start_carray(Visitor *v, void **obj, const char *name,
+ size_t elem_count, size_t elem_size, Error **errp);
+void visit_next_carray(Visitor *v, Error **errp);
+void visit_end_carray(Visitor *v, Error **errp);
#endif
diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
index 401ee6e..d9982f8 100644
--- a/qapi/qapi-visit-core.c
+++ b/qapi/qapi-visit-core.c
@@ -313,3 +313,28 @@ void input_type_enum(Visitor *v, int *obj, const char *strings[],
g_free(enum_str);
*obj = value;
}
+
+void visit_start_carray(Visitor *v, void **obj, const char *name,
+ size_t elem_count, size_t elem_size, Error **errp)
+{
+ g_assert(v->start_carray);
+ if (!error_is_set(errp)) {
+ v->start_carray(v, obj, name, elem_count, elem_size, errp);
+ }
+}
+
+void visit_next_carray(Visitor *v, Error **errp)
+{
+ g_assert(v->next_carray);
+ if (!error_is_set(errp)) {
+ v->next_carray(v, errp);
+ }
+}
+
+void visit_end_carray(Visitor *v, Error **errp)
+{
+ g_assert(v->end_carray);
+ if (!error_is_set(errp)) {
+ v->end_carray(v, errp);
+ }
+}
--
1.7.10.4
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [Qemu-devel] [PATCH 3/9] two new file wrappers
2013-03-13 3:09 [Qemu-devel] [PATCH 0/9 v2] Implement and test asn1 ber visitors Joel Schopp
2013-03-13 3:09 ` [Qemu-devel] [PATCH 1/9] qemu-file Joel Schopp
2013-03-13 3:09 ` [Qemu-devel] [PATCH 2/9] qapi_c_arrays.diff Joel Schopp
@ 2013-03-13 3:09 ` Joel Schopp
2013-03-13 3:09 ` [Qemu-devel] [PATCH 4/9] qemu_qsb.diff Joel Schopp
` (5 subsequent siblings)
8 siblings, 0 replies; 18+ messages in thread
From: Joel Schopp @ 2013-03-13 3:09 UTC (permalink / raw)
To: qemu-devel; +Cc: Joel Schopp, Stefan Berger, Michael Tsirkin
Add a 3 very short file wrapper functions to make code that follows more
readable. Also export an existing function that is currently static.
Cc: Michael Tsirkin <mst@redhat.com>
Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
Signed-off-by: Joel Schopp <jschopp@linux.vnet.ibm.com>
---
include/migration/qemu-file.h | 3 +++
qemu-file.c | 30 ++++++++++++++++++++++++++++++
2 files changed, 33 insertions(+)
diff --git a/include/migration/qemu-file.h b/include/migration/qemu-file.h
index 7194b84..728b6e2 100644
--- a/include/migration/qemu-file.h
+++ b/include/migration/qemu-file.h
@@ -68,6 +68,9 @@ int qemu_fclose(QEMUFile *f);
int64_t qemu_ftell(QEMUFile *f);
void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size);
void qemu_put_byte(QEMUFile *f, int v);
+int qemu_read_bytes(QEMUFile *f, uint8_t *buf, int size);
+int qemu_peek_bytes(QEMUFile *f, uint8_t *buf, int size, size_t offset);
+int qemu_write_bytes(QEMUFile *f, const uint8_t *buf, int size);
static inline void qemu_put_ubyte(QEMUFile *f, unsigned int v)
{
diff --git a/qemu-file.c b/qemu-file.c
index 4fed6d5..f8a54e7 100644
--- a/qemu-file.c
+++ b/qemu-file.c
@@ -713,3 +713,33 @@ uint64_t qemu_get_be64(QEMUFile *f)
return v;
}
+int qemu_read_bytes(QEMUFile *f, uint8_t *buf, int size)
+{
+ if (qemu_file_get_error(f)) {
+ return -1;
+ }
+ return qemu_get_buffer(f, buf, size);
+}
+
+int qemu_peek_bytes(QEMUFile *f, uint8_t *buf, int size, size_t offset)
+{
+ if (qemu_file_get_error(f)) {
+ return -1;
+ }
+ return qemu_peek_buffer(f, buf, size, offset);
+}
+
+int qemu_write_bytes(QEMUFile *f, const uint8_t *buf, int size)
+{
+ if (qemu_file_get_error(f)) {
+ return -1;
+ }
+
+ qemu_put_buffer(f, buf, size);
+
+ if (qemu_file_get_error(f)) {
+ return -1;
+ }
+
+ return size;
+}
--
1.7.10.4
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [Qemu-devel] [PATCH 4/9] qemu_qsb.diff
2013-03-13 3:09 [Qemu-devel] [PATCH 0/9 v2] Implement and test asn1 ber visitors Joel Schopp
` (2 preceding siblings ...)
2013-03-13 3:09 ` [Qemu-devel] [PATCH 3/9] two new file wrappers Joel Schopp
@ 2013-03-13 3:09 ` Joel Schopp
2013-03-13 3:09 ` [Qemu-devel] [PATCH 5/9] qapi_sized_buffer Joel Schopp
` (4 subsequent siblings)
8 siblings, 0 replies; 18+ messages in thread
From: Joel Schopp @ 2013-03-13 3:09 UTC (permalink / raw)
To: qemu-devel; +Cc: Joel Schopp, Stefan Berger
This patch adds support functions for operating on in memory sized file buffers.
Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
Signed-off-by: Joel Schopp <jschopp@linux.vnet.ibm.com>
---
include/migration/qemu-file.h | 12 +++
include/qemu-common.h | 15 ++++
qemu-file.c | 184 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 211 insertions(+)
diff --git a/include/migration/qemu-file.h b/include/migration/qemu-file.h
index 728b6e2..de8408a 100644
--- a/include/migration/qemu-file.h
+++ b/include/migration/qemu-file.h
@@ -24,6 +24,8 @@
#ifndef QEMU_FILE_H
#define QEMU_FILE_H 1
+#include <stdint.h>
+
/* This function writes a chunk of data to a file at the given position.
* The pos argument can be ignored if the file is only being used for
* streaming. The handler should try to write all of the data it can.
@@ -58,6 +60,14 @@ typedef struct QEMUFileOps {
QEMUFileGetFD *get_fd;
} QEMUFileOps;
+struct QEMUSizedBuffer {
+ unsigned char *buffer;
+ uint64_t size;
+ uint64_t used;
+};
+
+typedef struct QEMUSizedBuffer QEMUSizedBuffer;
+
QEMUFile *qemu_fopen_ops(void *opaque, const QEMUFileOps *ops);
QEMUFile *qemu_fopen(const char *filename, const char *mode);
QEMUFile *qemu_fdopen(int fd, const char *mode);
@@ -71,6 +81,8 @@ void qemu_put_byte(QEMUFile *f, int v);
int qemu_read_bytes(QEMUFile *f, uint8_t *buf, int size);
int qemu_peek_bytes(QEMUFile *f, uint8_t *buf, int size, size_t offset);
int qemu_write_bytes(QEMUFile *f, const uint8_t *buf, int size);
+QEMUFile *qemu_bufopen(const char *mode, QEMUSizedBuffer *input);
+const QEMUSizedBuffer *qemu_buf_get(QEMUFile *f);
static inline void qemu_put_ubyte(QEMUFile *f, unsigned int v)
{
diff --git a/include/qemu-common.h b/include/qemu-common.h
index 5e13708..de1cdc0 100644
--- a/include/qemu-common.h
+++ b/include/qemu-common.h
@@ -442,4 +442,19 @@ int64_t pow2floor(int64_t value);
int uleb128_encode_small(uint8_t *out, uint32_t n);
int uleb128_decode_small(const uint8_t *in, uint32_t *n);
+/* QEMU Sized Buffer */
+#include "include/migration/qemu-file.h"
+QEMUSizedBuffer *qsb_create(const uint8_t *buffer, uint64_t len);
+QEMUSizedBuffer *qsb_clone(const QEMUSizedBuffer *);
+void qsb_free(QEMUSizedBuffer *);
+uint64_t qsb_set_length(QEMUSizedBuffer *qsb, uint64_t length);
+uint64_t qsb_get_length(const QEMUSizedBuffer *qsb);
+const unsigned char *qsb_get_buffer(const QEMUSizedBuffer *, int64_t pos);
+int qsb_write_at(QEMUSizedBuffer *qsb, const uint8_t *buf,
+ int64_t pos, int size);
+int qsb_append_qsb(QEMUSizedBuffer *dest, const QEMUSizedBuffer *src);
+int qsb_append(QEMUSizedBuffer *dest, const uint8_t *buffer, uint64_t len);
+
+
+
#endif
diff --git a/qemu-file.c b/qemu-file.c
index f8a54e7..a1528e5 100644
--- a/qemu-file.c
+++ b/qemu-file.c
@@ -743,3 +743,187 @@ int qemu_write_bytes(QEMUFile *f, const uint8_t *buf, int size)
return size;
}
+
+
+QEMUSizedBuffer *qsb_create(const uint8_t *buffer, uint64_t len)
+{
+ QEMUSizedBuffer *qsb;
+ uint64_t alloc_len;
+
+ alloc_len = (len > 1024) ? len : 1024;
+
+ qsb = g_new0(QEMUSizedBuffer, 1);
+ if (!qsb) {
+ return NULL;
+ }
+
+ qsb->buffer = g_malloc(alloc_len);
+ if (!qsb->buffer) {
+ g_free(qsb);
+ return NULL;
+ }
+ qsb->size = alloc_len;
+
+ if (buffer) {
+ memcpy(qsb->buffer, buffer, len);
+ qsb->used = len;
+ }
+
+ return qsb;
+}
+
+void qsb_free(QEMUSizedBuffer *qsb)
+{
+ if (!qsb) {
+ return;
+ }
+ g_free(qsb->buffer);
+ g_free(qsb);
+}
+
+uint64_t qsb_get_length(const QEMUSizedBuffer *qsb)
+{
+ return qsb->used;
+}
+
+uint64_t qsb_set_length(QEMUSizedBuffer *qsb, uint64_t new_len)
+{
+ if (new_len <= qsb->size) {
+ qsb->used = new_len;
+ } else {
+ qsb->used = qsb->size;
+ }
+ return qsb->used;
+}
+
+const unsigned char *qsb_get_buffer(const QEMUSizedBuffer *qsb, int64_t pos)
+{
+ if (pos < qsb->used) {
+ return &qsb->buffer[pos];
+ }
+ return NULL;
+}
+
+int qsb_write_at(QEMUSizedBuffer *qsb, const uint8_t *buf,
+ int64_t pos, int size)
+{
+ if (pos + size > qsb->size) {
+ qsb->buffer = g_realloc(qsb->buffer, pos + size + 1024);
+ if (qsb->buffer == NULL) {
+ return -ENOMEM;
+ }
+ qsb->size = pos + size;
+ }
+ memcpy(&qsb->buffer[pos], buf, size);
+ if (pos + size > qsb->used) {
+ qsb->used = pos + size;
+ }
+
+ return size;
+}
+
+int qsb_append_qsb(QEMUSizedBuffer *dest, const QEMUSizedBuffer *src)
+{
+ return qsb_write_at(dest, qsb_get_buffer(src, 0),
+ qsb_get_length(dest), qsb_get_length(src));
+}
+
+int qsb_append(QEMUSizedBuffer *dest, const uint8_t *buf, uint64_t len)
+{
+ return qsb_write_at(dest, buf,
+ qsb_get_length(dest), len);
+}
+
+QEMUSizedBuffer *qsb_clone(const QEMUSizedBuffer *in)
+{
+ return qsb_create(qsb_get_buffer(in, 0),
+ qsb_get_length(in));
+}
+
+typedef struct QEMUBuffer {
+ QEMUSizedBuffer *qsb;
+ QEMUFile *file;
+} QEMUBuffer;
+
+static int buf_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size)
+{
+ QEMUBuffer *s = opaque;
+ ssize_t len = qsb_get_length(s->qsb) - pos;
+
+ if (len <= 0) {
+ return 0;
+ }
+
+ if (len > size) {
+ len = size;
+ }
+ memcpy(buf, qsb_get_buffer(s->qsb, pos), len);
+
+ return len;
+}
+
+static int buf_put_buffer(void *opaque, const uint8_t *buf,
+ int64_t pos, int size)
+{
+ QEMUBuffer *s = opaque;
+
+ return qsb_write_at(s->qsb, buf, pos, size);
+}
+
+static int buf_close(void *opaque)
+{
+ QEMUBuffer *s = opaque;
+
+ qsb_free(s->qsb);
+
+ g_free(s);
+
+ return 0;
+}
+
+const QEMUSizedBuffer *qemu_buf_get(QEMUFile *f)
+{
+ QEMUBuffer *p;
+
+ qemu_fflush(f);
+
+ p = (QEMUBuffer *)f->opaque;
+
+ return p->qsb;
+}
+
+static const QEMUFileOps buf_read_ops = {
+ .get_buffer = buf_get_buffer,
+ .close = buf_close
+};
+
+static const QEMUFileOps buf_write_ops = {
+ .put_buffer = buf_put_buffer,
+ .close = buf_close
+};
+
+QEMUFile *qemu_bufopen(const char *mode, QEMUSizedBuffer *input)
+{
+ QEMUBuffer *s;
+
+ if (mode == NULL || (mode[0] != 'r' && mode[0] != 'w') || mode[1] != 0) {
+ fprintf(stderr, "qemu_bufopen: Argument validity check failed\n");
+ return NULL;
+ }
+
+ s = g_malloc0(sizeof(QEMUBuffer));
+ if (mode[0] == 'r') {
+ s->qsb = input;
+ }
+
+ if (s->qsb == NULL) {
+ s->qsb = qsb_create(NULL, 0);
+ }
+
+ if (mode[0] == 'r') {
+ s->file = qemu_fopen_ops(s, &buf_read_ops);
+ } else {
+ s->file = qemu_fopen_ops(s, &buf_write_ops);
+ }
+ return s->file;
+}
--
1.7.10.4
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [Qemu-devel] [PATCH 5/9] qapi_sized_buffer
2013-03-13 3:09 [Qemu-devel] [PATCH 0/9 v2] Implement and test asn1 ber visitors Joel Schopp
` (3 preceding siblings ...)
2013-03-13 3:09 ` [Qemu-devel] [PATCH 4/9] qemu_qsb.diff Joel Schopp
@ 2013-03-13 3:09 ` Joel Schopp
2013-03-13 3:09 ` [Qemu-devel] [PATCH 6/9] asn1_output-visitor.diff Joel Schopp
` (3 subsequent siblings)
8 siblings, 0 replies; 18+ messages in thread
From: Joel Schopp @ 2013-03-13 3:09 UTC (permalink / raw)
To: qemu-devel; +Cc: Joel Schopp, Stefan Berger, Michael Tsirkin
Add a sized buffer interface to qapi.
Cc: Michael Tsirkin <mst@redhat.com>
Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
Signed-off-by: Joel Schopp <jschopp@linux.vnet.ibm.com>
---
include/qapi/visitor-impl.h | 2 ++
include/qapi/visitor.h | 2 ++
qapi/qapi-visit-core.c | 8 ++++++++
3 files changed, 12 insertions(+)
diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
index 79fe039..cf743f6 100644
--- a/include/qapi/visitor-impl.h
+++ b/include/qapi/visitor-impl.h
@@ -38,6 +38,8 @@ struct Visitor
size_t elem_count, size_t elem_size, Error **errp);
void (*next_carray)(Visitor *v, Error **errp);
void (*end_carray)(Visitor *v, Error **errp);
+ void (*type_sized_buffer)(Visitor *v, uint8_t **obj, size_t size,
+ const char *name, Error **errp);
/* May be NULL */
void (*start_optional)(Visitor *v, bool *present, const char *name,
diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
index 49f411f..8a8c4e9 100644
--- a/include/qapi/visitor.h
+++ b/include/qapi/visitor.h
@@ -55,5 +55,7 @@ void visit_start_carray(Visitor *v, void **obj, const char *name,
size_t elem_count, size_t elem_size, Error **errp);
void visit_next_carray(Visitor *v, Error **errp);
void visit_end_carray(Visitor *v, Error **errp);
+void visit_type_sized_buffer(Visitor *v, uint8_t **obj, size_t len,
+ const char *name, Error **errp);
#endif
diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
index d9982f8..4b36a54 100644
--- a/qapi/qapi-visit-core.c
+++ b/qapi/qapi-visit-core.c
@@ -338,3 +338,11 @@ void visit_end_carray(Visitor *v, Error **errp)
v->end_carray(v, errp);
}
}
+
+void visit_type_sized_buffer(Visitor *v, uint8_t **obj, size_t len,
+ const char *name, Error **errp)
+{
+ if (!error_is_set(errp)) {
+ v->type_sized_buffer(v, obj, len, name, errp);
+ }
+}
--
1.7.10.4
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [Qemu-devel] [PATCH 6/9] asn1_output-visitor.diff
2013-03-13 3:09 [Qemu-devel] [PATCH 0/9 v2] Implement and test asn1 ber visitors Joel Schopp
` (4 preceding siblings ...)
2013-03-13 3:09 ` [Qemu-devel] [PATCH 5/9] qapi_sized_buffer Joel Schopp
@ 2013-03-13 3:09 ` Joel Schopp
2013-03-13 20:46 ` Eric Blake
2013-03-13 3:09 ` [Qemu-devel] [PATCH 7/9] asn1_input-visitor.diff Joel Schopp
` (2 subsequent siblings)
8 siblings, 1 reply; 18+ messages in thread
From: Joel Schopp @ 2013-03-13 3:09 UTC (permalink / raw)
To: qemu-devel; +Cc: Joel Schopp, Stefan Berger, Michael Tsirkin
Implement an output visitor for ASN.1 BER encoding.
Cc: Michael Tsirkin <mst@redhat.com>
Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
Signed-off-by: Joel Schopp <jschopp@linux.vnet.ibm.com>
---
configure | 2 +-
include/qapi/ber-output-visitor.h | 28 ++
include/qapi/ber.h | 107 +++++++
qapi/Makefile.objs | 1 +
qapi/ber-common.c | 86 ++++++
qapi/ber-common.h | 29 ++
qapi/ber-output-visitor.c | 617 +++++++++++++++++++++++++++++++++++++
7 files changed, 869 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 84317c6..6dcf00e 100755
--- a/configure
+++ b/configure
@@ -2826,7 +2826,7 @@ fi
# Do we need libm
cat > $TMPC << EOF
#include <math.h>
-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 <aliguori@us.ibm.com>
+ * Stefan Berger <stefanb@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#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..9314b28
--- /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 <stefanb@us.ibm.com>
+ * Michael Tsirkin <mst@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+#ifndef 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/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 <stefanb@us.ibm.com>
+ * Michael Tsirkin <mst@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 <stdint.h>
+
+#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 <stefanb@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+#ifndef __QAPI_BER_COMMON_H__
+#define __QAPI_BER_COMMON_H__
+
+#include <stdint.h>
+
+#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..f32469d
--- /dev/null
+++ b/qapi/ber-output-visitor.c
@@ -0,0 +1,617 @@
+/*
+ * BER Output Visitor
+ *
+ * Copyright IBM, Corp. 2011, 2013
+ * Copyright Red Hat, Inc. 2011
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ * Stefan Berger <stefanb@us.ibm.com>
+ * Michael Tsirkin <mst@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 <math.h>
+#include <glib.h>
+
+#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 ||
+ qemu_write_bytes(qfile, qsb_get_buffer(qsb, 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_carray(Visitor *v, void **obj,
+ const char *name, size_t elem_count,
+ size_t elem_size, Error **errp)
+{
+ ber_output_start_constructed(v, BER_TYPE_SET, errp);
+}
+
+static void ber_output_next_carray(Visitor *v, Error **errp)
+{
+ /* nothing to do here */
+}
+
+static void ber_output_end_carray(Visitor *v, Error **errp)
+{
+ ber_output_end_constructed(v, BER_TYPE_SET, 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 buflen, 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);
+
+ 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;
+ }
+ }
+
+ do {
+ chunk = (buflen - offset > chunk_size) ? chunk_size : buflen - offset;
+
+ type_bytes = ber_encode_type(buf, sizeof(buf), ber_type, 0,
+ errp);
+ if (error_is_set(errp)) {
+ return;
+ }
+ num_bytes = ber_encode_len(&buf[type_bytes], sizeof(buf) - type_bytes,
+ chunk, errp);
+ if (error_is_set(errp)) {
+ return;
+ }
+ if (qemu_write_bytes(aov->qfile, buf, type_bytes + num_bytes) !=
+ type_bytes + num_bytes ||
+ qemu_write_bytes(aov->qfile, &buffer[offset], chunk) != chunk) {
+ error_setg(errp, "QEMUFile error: Error while writing buffer");
+ return;
+ }
+ offset += chunk;
+ } while (offset < buflen);
+
+ if (fragmented) {
+ ber_output_end_constructed(&aov->visitor, ber_type, errp);
+ }
+}
+
+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), 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), errp);
+}
+
+static void ber_output_sized_buffer(Visitor *v, uint8_t **obj,
+ size_t size, const char *name,
+ Error **errp)
+{
+ ber_output_fragment(v, BER_TYPE_OCTET_STRING,
+ *obj, 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_carray = ber_output_start_carray;
+ v->visitor.next_carray = ber_output_next_carray;
+ v->visitor.end_carray = ber_output_end_carray;
+ 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;
+}
--
1.7.10.4
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [Qemu-devel] [PATCH 7/9] asn1_input-visitor.diff
2013-03-13 3:09 [Qemu-devel] [PATCH 0/9 v2] Implement and test asn1 ber visitors Joel Schopp
` (5 preceding siblings ...)
2013-03-13 3:09 ` [Qemu-devel] [PATCH 6/9] asn1_output-visitor.diff Joel Schopp
@ 2013-03-13 3:09 ` Joel Schopp
2013-03-13 20:51 ` Eric Blake
2013-03-13 3:09 ` [Qemu-devel] [PATCH 8/9] asn1_test_visitor_serialization.diff Joel Schopp
2013-03-13 3:09 ` [Qemu-devel] [PATCH 9/9] update_maintainers.diff Joel Schopp
8 siblings, 1 reply; 18+ messages in thread
From: Joel Schopp @ 2013-03-13 3:09 UTC (permalink / raw)
To: qemu-devel; +Cc: Joel Schopp, Stefan Berger, Michael Tsirkin
Implement an input visitor for ASN.1 BER encoding.
Cc: Michael Tsirkin <mst@redhat.com>
Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
Signed-off-by: Joel Schopp <jschopp@linux.vnet.ibm.com>
---
include/qapi/ber-input-visitor.h | 30 ++
qapi/Makefile.objs | 2 +-
qapi/ber-input-visitor.c | 1073 ++++++++++++++++++++++++++++++++++++++
3 files changed, 1104 insertions(+), 1 deletion(-)
create mode 100644 include/qapi/ber-input-visitor.h
create mode 100644 qapi/ber-input-visitor.c
diff --git a/include/qapi/ber-input-visitor.h b/include/qapi/ber-input-visitor.h
new file mode 100644
index 0000000..eaa3d0e
--- /dev/null
+++ b/include/qapi/ber-input-visitor.h
@@ -0,0 +1,30 @@
+/*
+ * BER Input Visitor header
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ * Stefan Berger <stefanb@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#ifndef BER_INPUT_VISITOR_H
+#define BER_INPUT_VISITOR_H
+
+#include "qapi/visitor.h"
+
+typedef struct BERInputVisitor BERInputVisitor;
+
+BERInputVisitor *ber_input_visitor_new(QEMUFile *,
+ uint64_t max_allowd_buffer_size);
+void ber_input_visitor_cleanup(BERInputVisitor *v);
+uint64_t ber_input_get_parser_position(BERInputVisitor *v);
+uint64_t ber_input_get_largest_needed_buffer(BERInputVisitor *v);
+
+Visitor *ber_input_get_visitor(BERInputVisitor *v);
+
+#endif
diff --git a/qapi/Makefile.objs b/qapi/Makefile.objs
index 519e3ee..f7f080a 100644
--- a/qapi/Makefile.objs
+++ b/qapi/Makefile.objs
@@ -3,4 +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
+util-obj-y += ber-common.o ber-output-visitor.o ber-input-visitor.o
diff --git a/qapi/ber-input-visitor.c b/qapi/ber-input-visitor.c
new file mode 100644
index 0000000..b17224f
--- /dev/null
+++ b/qapi/ber-input-visitor.c
@@ -0,0 +1,1073 @@
+/*
+ * BER Input Visitor
+ *
+ * Copyright IBM, Corp. 2011, 2013
+ * Copyright Red Hat, Inc. 2011
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ * Stefan Berger <stefanb@us.ibm.com>
+ * Michael Tsirkin <mst@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 <math.h>
+#include <glib.h>
+
+#include "qemu-common.h"
+#include "qapi/ber-common.h"
+#include "qapi/ber-input-visitor.h"
+#include "qemu/queue.h"
+#include "qemu-common.h"
+#include "hw/hw.h"
+#include "qapi/ber.h"
+#include "include/qapi/qmp/qerror.h"
+#include "migration/qemu-file.h"
+#include "qapi/visitor-impl.h"
+
+#define AIV_STACK_SIZE 1024
+
+/* whether to allow the parsing of primitives that are fragmented */
+#define BER_ALLOW_FRAGMENTED_PRIMITIVES
+
+/* #define BER_DEBUG */
+
+typedef struct StackEntry {
+ uint64_t cur_pos;
+} StackEntry;
+
+struct BERInputVisitor {
+ Visitor visitor;
+ QEMUFile *qfile;
+ uint64_t cur_pos;
+ StackEntry stack[AIV_STACK_SIZE];
+ int nb_stack;
+ uint64_t max_allowed_buffer_size;
+ uint64_t largest_needed_buffer;
+};
+
+static BERInputVisitor *to_biv(Visitor *v)
+{
+ return container_of(v, BERInputVisitor, visitor);
+}
+
+static void ber_input_push(BERInputVisitor *aiv,
+ uint64_t cur_pos, Error **errp)
+{
+ aiv->stack[aiv->nb_stack].cur_pos = cur_pos;
+ aiv->nb_stack++;
+
+ if (aiv->nb_stack >= AIV_STACK_SIZE) {
+ error_set(errp, QERR_BUFFER_OVERRUN);
+ }
+}
+
+static uint64_t ber_input_pop(BERInputVisitor *aiv, Error **errp)
+{
+ aiv->nb_stack--;
+
+ if (aiv->nb_stack < 0) {
+ error_set(errp, QERR_BUFFER_OVERRUN);
+ return 0;
+ }
+
+ return aiv->stack[aiv->nb_stack].cur_pos;
+}
+
+/*
+ * 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.
+ */
+static uint32_t ber_read_type(BERInputVisitor *aiv, uint8_t *ber_type_flags,
+ Error **errp)
+{
+ uint32_t type;
+ uint8_t byte;
+ uint8_t ctr = 0;
+ char buf[128];
+
+ if (qemu_read_bytes(aiv->qfile, &byte, 1) != 1) {
+ error_setg(errp, "QEMUFile has an error, error was '%s'",
+ "Error while reading type");
+ return 0;
+ }
+ aiv->cur_pos++;
+ 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_read_bytes(aiv->qfile, &byte, 1) != 1) {
+ error_setg(errp, "QEMUFile has an error, error was '%s'",
+ "Error while reading long type");
+ return 0;
+ }
+ aiv->cur_pos++;
+
+ type |= (byte & 0x7f);
+ if ((byte & 0x80) == 0) {
+ break;
+ }
+ ctr += 7; /* read 7 bits */
+ if (ctr >= (sizeof(type) * 8)) {
+ /* only support 32 bit length identifiers */
+ snprintf(buf, sizeof(buf),
+ "type tag is larger than 32 bit (offset %" PRIu64
+ ")", aiv->cur_pos);
+ error_setg(errp, "Data stream is invalid, error was '%s'", buf);
+ return 0;
+ }
+ }
+ } else {
+ type &= BER_TYPE_TAG_MASK;
+ }
+
+ return type;
+}
+
+static uint64_t ber_read_length(BERInputVisitor *aiv, bool *is_indefinite,
+ Error **errp)
+{
+ uint8_t byte, c, int_len;
+ uint64_t len = 0;
+ QEMUFile *qfile = aiv->qfile;
+ unsigned char int_array[sizeof(len)];
+ char buf[128];
+
+ *is_indefinite = false;
+
+ if (qemu_read_bytes(qfile, &byte, 1) != 1) {
+ error_setg(errp, "QEMUFile has an error, error was '%s'",
+ "Error while reading length indicator");
+ return ~0x0ULL;
+ }
+ aiv->cur_pos++;
+
+ 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_read_bytes(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];
+ }
+ aiv->cur_pos += int_len;
+ }
+
+ if (len > aiv->max_allowed_buffer_size) {
+ snprintf(buf, sizeof(buf),
+ "Length indicator (%"PRIu64") in input byte stream "
+ "exceeds maximum allowed length (%"PRIu64").",
+ len, aiv->max_allowed_buffer_size);
+ error_setg(errp, "Data stream is invalid, error was '%s'", buf);
+ return ~0x0ULL;
+ }
+
+ if (len > aiv->largest_needed_buffer) {
+ aiv->largest_needed_buffer = len;
+ }
+
+ return len;
+}
+
+static uint64_t ber_peek_is_eoc(BERInputVisitor *biv, Error **errp)
+{
+ uint8_t buf[2];
+ QEMUFile *qfile = biv->qfile;
+
+ if (qemu_peek_bytes(qfile, buf, 2, 0) != 2) {
+ error_setg(errp, "QEMUFile has an error, error was '%s'",
+ "Error while peeking for EOC");
+ return ~0x0ULL;
+ }
+
+ if (buf[0] == BER_TYPE_EOC && buf[1] == 0) {
+ return 1;
+ }
+
+ return 0;
+}
+
+static void ber_skip_bytes(BERInputVisitor *aiv, uint64_t to_skip,
+ Error **errp)
+{
+ uint8_t buf[1024];
+ uint32_t skip;
+
+ /* skip length bytes */
+ while (to_skip > 0) {
+ skip = MIN(to_skip, sizeof(buf));
+ if (qemu_read_bytes(aiv->qfile, buf, skip) != skip) {
+ error_setg(errp, "QEMUFile error: Error while skipping over bytes");
+ return;
+ }
+ aiv->cur_pos += skip;
+ to_skip -= skip;
+ }
+}
+
+static void ber_skip_until_eoc(BERInputVisitor *aiv, Error **errp)
+{
+ uint32_t ber_type_tag;
+ uint64_t length;
+ bool is_indefinite;
+ uint8_t ber_type_flags;
+ uint64_t indefinite_nesting = 1;
+ char buf[128];
+
+ while (!error_is_set(errp)) {
+ ber_type_tag = ber_read_type(aiv, &ber_type_flags, errp);
+ if (error_is_set(errp)) {
+ return;
+ }
+
+ length = ber_read_length(aiv, &is_indefinite, errp);
+ if (error_is_set(errp)) {
+ return;
+ }
+ if (ber_type_tag == BER_TYPE_EOC) {
+ if (length) {
+ snprintf(buf, sizeof(buf),
+ "ASN.1 EOC length field at offset %" PRIu64
+ " is invalid", aiv->cur_pos);
+ error_set(errp, QERR_INVALID_PARAMETER, buf);
+ return;
+ }
+ if (!indefinite_nesting) {
+ snprintf(buf, sizeof(buf),
+ "ASN.1 EOC at offset %" PRIu64
+ "not within an indefinite length",
+ aiv->cur_pos);
+ error_set(errp, QERR_INVALID_PARAMETER, buf);
+ return;
+ }
+#ifdef BER_DEBUG
+ fprintf(stderr, "found end! nesting=%" PRIdMAX
+ ", pos=%" PRIu64 "\n",
+ indefinite_nesting, aiv->cur_pos);
+#endif
+ if (!--indefinite_nesting) {
+ return;
+ }
+ }
+ if (is_indefinite) {
+ if ((ber_type_flags & BER_TYPE_P_C_MASK) == BER_TYPE_PRIMITIVE) {
+ snprintf(buf, sizeof(buf),
+ "ASN.1 indefinite length in a primitive type "
+ "at offset %" PRIu64,
+ aiv->cur_pos);
+ error_set(errp, QERR_INVALID_PARAMETER, buf);
+ return;
+ }
+ if (indefinite_nesting == ~0x0ULL) {
+ snprintf(buf, sizeof(buf),
+ "ASN.1 indefinite nesting level is too large "
+ "(offset %" PRIu64 ")",
+ aiv->cur_pos);
+ error_set(errp, QERR_INVALID_PARAMETER, buf);
+ return;
+ }
+ ++indefinite_nesting;
+ } else {
+#ifdef BER_DEBUG
+ fprintf(stderr, "skipping type '%s' of length "
+ "%" PRIu64 " at %" PRIu64 ".\n",
+ ber_type_to_str(ber_type_tag), length, aiv->cur_pos);
+#endif
+ ber_skip_bytes(aiv, length, errp);
+ }
+ }
+}
+
+static void ber_input_start_constructed(Visitor *v, uint32_t exp_ber_type,
+ uint8_t exp_ber_flags, void **obj,
+ const char *kind, const char *name,
+ size_t size, Error **errp)
+{
+ BERInputVisitor *aiv = to_biv(v);
+ uint32_t ber_type_tag;
+ uint8_t ber_type_flags;
+ int64_t len;
+ bool is_indefinite;
+ char buf[128];
+
+ ber_type_tag = ber_read_type(aiv, &ber_type_flags, errp);
+ if (error_is_set(errp)) {
+ return;
+ }
+
+ if (ber_type_tag != exp_ber_type || ber_type_flags != exp_ber_flags) {
+ sprintf(buf, "%s at offset %" PRIu64,
+ ber_type_to_str(exp_ber_type), aiv->cur_pos);
+
+ error_set(errp, QERR_INVALID_PARAMETER_TYPE,
+ ber_type_to_str(ber_type_tag),
+ buf);
+ return;
+ }
+
+ if ((ber_type_flags & BER_TYPE_P_C_MASK) == BER_TYPE_PRIMITIVE) {
+ snprintf(buf, sizeof(buf), "primitive type (%s)",
+ ber_type_to_str(ber_type_tag));
+ error_set(errp, QERR_INVALID_PARAMETER_TYPE,
+ buf, "constructed type");
+ return;
+ }
+
+ len = ber_read_length(aiv, &is_indefinite, errp);
+ if (error_is_set(errp)) {
+ return;
+ }
+
+ if (!is_indefinite) {
+#ifdef BER_DEBUG
+ fprintf(stderr, "structure/set len: %" PRIi64 "\n", len);
+#endif
+ ber_input_push(aiv, aiv->cur_pos + len, errp);
+ } else {
+#ifdef BER_DEBUG
+ fprintf(stderr, "indefinite length encoding!\n");
+#endif
+ ber_input_push(aiv, 0, errp);
+ }
+
+ if (error_is_set(errp)) {
+ return;
+ }
+
+ if (size > 0 && *obj == NULL) {
+ *obj = g_malloc0(size);
+#ifdef BER_DEBUG
+ fprintf(stderr, "for type '%s' allocated buffer at %p, size = %zu\n",
+ ber_type_to_str(ber_type_tag), *obj, size);
+#endif
+ }
+}
+
+static void ber_input_end_constructed(Visitor *v, Error **errp)
+{
+ uint64_t new_pos;
+ BERInputVisitor *aiv = to_biv(v);
+
+ new_pos = ber_input_pop(aiv, errp);
+
+ if (new_pos != 0) {
+#ifdef BER_DEBUG
+ fprintf(stderr, "new_pos = %" PRIu64 "\n", new_pos);
+#endif
+ aiv->cur_pos = new_pos;
+ } else {
+#ifdef BER_DEBUG
+ fprintf(stderr, "searching for end...\n");
+ fprintf(stderr, "cur_pos = %" PRIu64 "\n", aiv->cur_pos);
+#endif
+ ber_skip_until_eoc(aiv, errp);
+ }
+}
+
+static void ber_input_start_struct(Visitor *v, void **obj, const char *kind,
+ const char *name, size_t size, Error **errp)
+{
+ ber_input_start_constructed(v, BER_TYPE_SEQUENCE, BER_TYPE_CONSTRUCTED,
+ obj, kind, name, size, errp);
+}
+
+static void ber_input_end_struct(Visitor *v, Error **errp)
+{
+ ber_input_end_constructed(v, errp);
+}
+
+static void ber_input_start_carray(Visitor *v, void **obj,
+ const char *name, size_t elem_count,
+ size_t elem_size, Error **errp)
+{
+ ber_input_start_constructed(v, BER_TYPE_SET, BER_TYPE_CONSTRUCTED,
+ obj, NULL, name,
+ elem_count * elem_size, errp);
+}
+
+static void ber_input_next_carray(Visitor *v, Error **errp)
+{
+ /* nothing to do here */
+}
+
+static void ber_input_end_carray(Visitor *v, Error **errp)
+{
+ ber_input_end_constructed(v, errp);
+}
+
+static void ber_input_start_list(Visitor *v, const char *name,
+ Error **errp)
+{
+ void *obj = NULL;
+ ber_input_start_constructed(v, BER_TYPE_CUSTOM_LIST, BER_TYPE_CONSTRUCTED,
+ obj, NULL, name, 0, errp);
+ g_free(obj);
+}
+
+static GenericList *ber_input_next_list(Visitor *v, GenericList **list,
+ Error **errp)
+{
+ BERInputVisitor *biv = to_biv(v);
+ GenericList *entry;
+ StackEntry *se = &biv->stack[biv->nb_stack - 1];
+
+ if (se->cur_pos == 0) {
+ /* indefinite lenght encoding is used */
+ se = &biv->stack[biv->nb_stack];
+ if (ber_peek_is_eoc(biv, errp) != 0) {
+ return NULL;
+ }
+ } else if (se->cur_pos <= biv->cur_pos) {
+ return NULL;
+ }
+
+ entry = g_malloc0(sizeof(*entry));
+ if (*list) {
+ (*list)->next = entry;
+ }
+
+ return entry;
+}
+
+static void ber_input_end_list(Visitor *v, Error **errp)
+{
+ ber_input_end_constructed(v, errp);
+}
+
+static void ber_input_integer(Visitor *v, uint8_t *obj, uint8_t maxbytes,
+ Error **errp)
+{
+ BERInputVisitor *aiv = to_biv(v);
+ uint32_t ber_type_tag;
+ uint8_t ber_type_flags;
+ bool is_indefinite;
+ uint64_t len;
+ uint64_t val = 0;
+ unsigned char int_array[sizeof(val)];
+ int c;
+ char buf[128], buf2[128];
+
+#ifdef BER_DEBUG
+ fprintf(stderr, "reading int to %p\n", obj);
+#endif
+
+ ber_type_tag = ber_read_type(aiv, &ber_type_flags, errp);
+ if (error_is_set(errp)) {
+ return;
+ }
+
+#ifdef BER_DEBUG
+ fprintf(stderr, "%s: got type: 0x%02x, expected 0x%02x\n",
+ __func__, ber_type_tag, BER_TYPE_INTEGER);
+#endif
+
+ if (ber_type_tag != BER_TYPE_INTEGER ||
+ ber_type_flags != (BER_TYPE_CLASS_APPLICATION|BER_TYPE_PRIMITIVE)) {
+ snprintf(buf, sizeof(buf), "%s/%s/%s",
+ ber_type_class_to_str(ber_type_flags),
+ ber_type_pc_to_str(ber_type_flags),
+ ber_type_to_str(ber_type_tag));
+ snprintf(buf2, sizeof(buf2), "%s/%s/%s",
+ ber_type_class_to_str(BER_TYPE_CLASS_APPLICATION),
+ ber_type_pc_to_str(BER_TYPE_PRIMITIVE),
+ ber_type_to_str(BER_TYPE_INTEGER));
+ error_set(errp, QERR_INVALID_PARAMETER_TYPE, buf, buf2);
+ return;
+ }
+
+ len = ber_read_length(aiv, &is_indefinite, errp);
+ if (error_is_set(errp)) {
+ return;
+ }
+#ifdef BER_DEBUG
+ fprintf(stderr, "pos: %" PRIu64 " int len: %" PRIi64 "\n",
+ aiv->cur_pos, len);
+#endif
+
+ if (is_indefinite) {
+ error_set(errp, QERR_INVALID_PARAMETER_VALUE,
+ "ASN.1 int indicator is indefinite",
+ "[1..8]");
+ return;
+ }
+
+ if (maxbytes > sizeof(val)) {
+ snprintf(buf, sizeof(buf), "ASN.1 integers cannot have a length of "
+ "%" PRIi32 " bytes", maxbytes);
+ error_set(errp, QERR_INVALID_PARAMETER_VALUE,
+ buf, "[1..8]");
+ return;
+ }
+
+ if (len > maxbytes) {
+ snprintf(buf, sizeof(buf), "ASN.1 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_read_bytes(aiv->qfile, int_array, len) != len) {
+ error_setg(errp, "QEMUFile error: Error while reading integer");
+ return;
+ }
+
+ for (c = 0; c < len ; c++) {
+ val <<= 8;
+ val |= int_array[c];
+ if (c == 0 && (val & 0x80) == 0x80) {
+ /* sign extend */
+ val |= 0xFFFFFFFFFFFFFF00ULL;
+ }
+ }
+ aiv->cur_pos += len;
+#ifdef BER_DEBUG
+ fprintf(stderr, "pos: %" PRIu64 " int: %" PRIu64 "\n", aiv->cur_pos, val);
+#endif
+
+ memcpy(obj, &val, maxbytes);
+}
+
+static void ber_input_type_int(Visitor *v, int64_t *obj, const char *name,
+ Error **errp)
+{
+ ber_input_integer(v, (uint8_t *)obj, sizeof(*obj), errp);
+}
+
+static void ber_input_type_uint8(Visitor *v, uint8_t *obj,
+ const char *name, Error **errp)
+{
+ ber_input_integer(v, (uint8_t *)obj, sizeof(*obj), errp);
+}
+
+static void ber_input_type_uint16(Visitor *v, uint16_t *obj,
+ const char *name, Error **errp)
+{
+ ber_input_integer(v, (uint8_t *)obj, sizeof(*obj), errp);
+}
+
+static void ber_input_type_uint32(Visitor *v, uint32_t *obj,
+ const char *name, Error **errp)
+{
+ ber_input_integer(v, (uint8_t *)obj, sizeof(*obj), errp);
+}
+
+static void ber_input_type_uint64(Visitor *v, uint64_t *obj,
+ const char *name, Error **errp)
+{
+ ber_input_integer(v, (uint8_t *)obj, sizeof(*obj), errp);
+}
+
+static void ber_input_type_int8(Visitor *v, int8_t *obj,
+ const char *name, Error **errp)
+{
+ ber_input_integer(v, (uint8_t *)obj, sizeof(*obj), errp);
+}
+
+static void ber_input_type_int16(Visitor *v, int16_t *obj,
+ const char *name, Error **errp)
+{
+ ber_input_integer(v, (uint8_t *)obj, sizeof(*obj), errp);
+}
+
+static void ber_input_type_int32(Visitor *v, int32_t *obj,
+ const char *name, Error **errp)
+{
+ ber_input_integer(v, (uint8_t *)obj, sizeof(*obj), errp);
+}
+
+static void ber_input_type_int64(Visitor *v, int64_t *obj,
+ const char *name, Error **errp)
+{
+ ber_input_integer(v, (uint8_t *)obj, sizeof(*obj), errp);
+}
+
+static void ber_input_type_bool(Visitor *v, bool *obj, const char *name,
+ Error **errp)
+{
+ BERInputVisitor *aiv = to_biv(v);
+ uint32_t ber_type_tag;
+ uint8_t ber_type_flags, byte;
+ bool is_indefinite;
+ uint64_t len;
+ char buf[128];
+
+ ber_type_tag = ber_read_type(aiv, &ber_type_flags, errp);
+ if (error_is_set(errp)) {
+ return;
+ }
+
+ if (ber_type_tag != BER_TYPE_BOOLEAN || ber_type_flags != 0) {
+ error_set(errp, QERR_INVALID_PARAMETER_TYPE,
+ ber_type_to_str(ber_type_tag),
+ ber_type_to_str(BER_TYPE_BOOLEAN));
+ return;
+ }
+ len = ber_read_length(aiv, &is_indefinite, errp);
+ if (error_is_set(errp)) {
+ return;
+ }
+#ifdef BER_DEBUG
+ fprintf(stderr, "pos: %" PRIu64 " bool len: %" PRIi64 "\n",
+ aiv->cur_pos, len);
+#endif
+
+ if (is_indefinite || len != 1) {
+ snprintf(buf, sizeof(buf),
+ "ASN.1 bool length indicator at offset %" PRIu64
+ " is indefinite or != 1",
+ aiv->cur_pos);
+ error_set(errp, QERR_INVALID_PARAMETER_VALUE,
+ buf, "1");
+ return;
+ }
+ if (qemu_read_bytes(aiv->qfile, &byte, 1) != 1) {
+ error_setg(errp, "QEMUFile error: Error while reading boolean");
+ return;
+ }
+ aiv->cur_pos++;
+ *obj = byte;
+
+#ifdef BER_DEBUG
+ fprintf(stderr, "pos: %" PRIu64 " bool: %d\n", aiv->cur_pos, *obj);
+#endif
+}
+
+/* Function for recursive reading of fragmented primitives */
+static uint32_t ber_input_fragment(BERInputVisitor *aiv,
+ uint32_t exp_type_tag,
+ uint8_t exp_type_flags,
+ uint8_t **buffer, size_t *buffer_len,
+ bool may_realloc,
+ uint32_t offset, uint32_t nesting,
+ bool indefinite, uint64_t max_pos,
+ const char *name, Error **errp)
+{
+ uint32_t ber_type_tag;
+ uint8_t ber_type_flags;
+ uint32_t bytes_read = 0;
+ bool is_indefinite;
+ uint64_t len;
+ char buf[128];
+
+ assert((exp_type_flags & BER_TYPE_CONSTRUCTED) == BER_TYPE_PRIMITIVE);
+
+ ber_type_tag = ber_read_type(aiv, &ber_type_flags, errp);
+ if (error_is_set(errp)) {
+ return 0;
+ }
+
+ if (ber_type_tag != exp_type_tag) {
+ error_set(errp, QERR_INVALID_PARAMETER_TYPE,
+ ber_type_to_str(ber_type_tag & BER_TYPE_TAG_MASK),
+ ber_type_to_str(exp_type_tag));
+ return 0;
+ }
+
+ if ((ber_type_flags & BER_TYPE_CONSTRUCTED)) {
+#ifndef BER_ALLOW_FRAGMENTED_PRIMITIVES
+ error_setg(errp, "Data stream is invalid, error was '%s'",
+ "constructed encoding of primitive types is not supported");
+ goto err_exit;
+#else
+ if (nesting == 1) {
+ /* don't allow further nesting */
+ error_setg(errp, "Data stream is invalid, error was invalid nesting");
+ goto err_exit;
+ }
+ len = ber_read_length(aiv, &is_indefinite, errp);
+ if (error_is_set(errp)) {
+ goto err_exit;
+ }
+#ifdef BER_DEBUG
+ fprintf(stderr, "pos: %" PRIu64 " string len: %" PRIi64 "\n",
+ aiv->cur_pos, len);
+#endif
+
+ if (!is_indefinite) {
+ if ((*buffer) == NULL) {
+ /* allocate buffer once; due to the ASN.1 overhead it
+ * will be bigger than what we need */
+ *buffer = g_malloc0(len);
+ *buffer_len = len;
+ may_realloc = false;
+ }
+ }
+#ifdef BER_DEBUG
+ fprintf(stderr, "recursing now to read constructed type.\n");
+ fprintf(stderr, " is_indefinite: %d\n", is_indefinite);
+#endif
+ bytes_read += ber_input_fragment(aiv, exp_type_tag, exp_type_flags,
+ buffer, buffer_len, may_realloc,
+ offset, nesting + 1, is_indefinite,
+ aiv->cur_pos + len, name, errp);
+ return bytes_read;
+#endif
+ }
+
+ while (true) {
+ /* Would reading the length carry us beyond what we are allowed to
+ * read?
+ */
+ if (!indefinite &&
+ max_pos != 0 &&
+ aiv->cur_pos + 1 > max_pos) {
+ snprintf(buf, sizeof(buf),
+ "data stream would cause parsing beyond "
+ "allowed offset at %" PRIu64,
+ max_pos);
+ /* input stream is malformed */
+ error_setg(errp, "Data stream is invalid, error was '%s'", buf);
+ goto err_exit;
+ }
+
+ /* not-constructed case */
+ len = ber_read_length(aiv, &is_indefinite, errp);
+ if (error_is_set(errp)) {
+ goto err_exit;
+ }
+#ifdef BER_DEBUG
+ fprintf(stderr, "pos: %" PRIu64 " string len: %" PRIi64 "\n",
+ aiv->cur_pos, len);
+#endif
+ if (is_indefinite) {
+ snprintf(buf, sizeof(buf),
+ "Got indefinite type length in primitive type (%s) at"
+ "offset %" PRIu64,
+ ber_type_to_str(ber_type_tag), aiv->cur_pos);
+ error_set(errp, QERR_INVALID_PARAMETER, buf);
+ goto err_exit;
+ }
+ /* if max_pos is not set, set it here */
+ if (!indefinite && max_pos == 0) {
+ max_pos = aiv->cur_pos + len;
+ }
+
+ /* Would reading the data carry us beyond what we are allowed to
+ * read ?
+ */
+ if (!indefinite && aiv->cur_pos + len > max_pos) {
+ /* input stream is malformed */
+ snprintf(buf, sizeof(buf),
+ "data stream would cause parsing beyond "
+ "allowed offset at %" PRIu64,
+ max_pos);
+ error_setg(errp, "Data stream is invalid, error was '%s'", buf);
+ goto err_exit;
+ }
+
+ if (offset + len > *buffer_len) {
+ if (!may_realloc) {
+ snprintf(buf, sizeof(buf),
+ "given buffer is too small (%lu < %"PRIu64")",
+ (unsigned long)*buffer_len, offset + len);
+ error_setg(errp, "Data stream is invalid, error was '%s'", buf);
+ }
+ /* allocate one more byte for strings, set to 0 */
+ *buffer = g_realloc(*buffer, offset + len + 1);
+ *buffer_len = offset + len;
+ (*buffer)[offset+len] = 0;
+ }
+
+ if (qemu_read_bytes(aiv->qfile,
+ &((uint8_t *)*buffer)[offset], len) != len) {
+ error_setg(errp, "QEMUFile error: Error while reading data");
+ goto err_exit;
+ }
+
+ offset += len;
+ bytes_read += len;
+
+ aiv->cur_pos += len;
+#ifdef BER_DEBUG
+ if (exp_type_tag == BER_TYPE_IA5_STRING) {
+ fprintf(stderr, "pos: %" PRIu64 " string: %.*s\n", aiv->cur_pos,
+ offset, *buffer);
+ }
+#endif
+
+ if (nesting == 0) {
+ break;
+ }
+
+ /* indefinite length case: loop until we encounter EOC */
+ if (indefinite) {
+ ber_type_tag = ber_read_type(aiv, &ber_type_flags, errp);
+ if (error_is_set(errp)) {
+ goto err_exit;
+ }
+
+ if (ber_type_tag == BER_TYPE_EOC) {
+ uint8_t byte;
+ if (qemu_read_bytes(aiv->qfile, &byte, 1) != 1) {
+ error_setg(errp, "QEMUFile error: Error while reading BER_TYPE_EOC length");
+ goto err_exit;
+ }
+ aiv->cur_pos++;
+
+ if (byte != 0) {
+ snprintf(buf, sizeof(buf),
+ "ASN.1 EOC length field is invalid at offset "
+ "%" PRIu64,
+ aiv->cur_pos);
+ error_set(errp, QERR_INVALID_PARAMETER, buf);
+ goto err_exit;
+ }
+ return bytes_read;
+ }
+
+ if (ber_type_tag != exp_type_tag ||
+ ber_type_flags != exp_type_flags) {
+ snprintf(buf, sizeof(buf),
+ "ASN.1 type field or flags are wrong. Found "
+ "0x%x/%u, expected "
+ "0x%x/%u at offset %" PRIu64,
+ ber_type_tag, ber_type_flags,
+ exp_type_tag, exp_type_flags,
+ aiv->cur_pos);
+ error_set(errp, QERR_INVALID_PARAMETER, buf);
+ goto err_exit;
+ }
+ continue;
+ }
+
+ /* in definite length coding case; caller told us how far to read */
+ if (aiv->cur_pos == max_pos) {
+ return bytes_read;
+ }
+
+ ber_type_tag = ber_read_type(aiv, &ber_type_flags, errp);
+ if (error_is_set(errp)) {
+ goto err_exit;
+ }
+
+ if ((ber_type_flags & BER_TYPE_P_C_MASK) == BER_TYPE_CONSTRUCTED) {
+ error_set(errp, QERR_INVALID_PARAMETER_TYPE,
+ "constructed BER type",
+ ber_type_to_str(exp_type_tag));
+ goto err_exit;
+ }
+
+ if (ber_type_tag != exp_type_tag) {
+ error_set(errp, QERR_INVALID_PARAMETER_TYPE,
+ ber_type_to_str(ber_type_tag & BER_TYPE_TAG_MASK),
+ ber_type_to_str(exp_type_tag));
+ goto err_exit;
+ }
+ }
+ return bytes_read;
+
+err_exit:
+ if (may_realloc) {
+ g_free(*buffer);
+ *buffer = NULL;
+ }
+ return 0;
+}
+
+static void ber_input_type_str(Visitor *v, char **obj, const char *name,
+ Error **errp)
+{
+ BERInputVisitor *aiv = to_biv(v);
+ size_t buffer_len = 0;
+
+ ber_input_fragment(aiv, BER_TYPE_IA5_STRING, 0,
+ (uint8_t **)obj, &buffer_len, (*obj == NULL),
+ 0, 0, false, 0, name, errp);
+
+ if (!error_is_set(errp) && *obj == NULL) {
+ /* adjust NULL string to "" */
+ *obj = g_strdup("");
+ }
+}
+
+static void ber_input_sized_buffer(Visitor *v, uint8_t **obj, size_t len,
+ const char *name, Error **errp)
+{
+ BERInputVisitor *aiv = to_biv(v);
+
+ ber_input_fragment(aiv, BER_TYPE_OCTET_STRING, 0,
+ (uint8_t **)obj, &len, (*obj == NULL),
+ 0, 0, false, 0, name, errp);
+
+#ifdef BER_DEBUG
+ fprintf(stderr, "pos: %" PRIu64 " data at: %p data:\n",
+ aiv->cur_pos, *obj);
+ int i;
+ for (i = 0; i < len; i++) {
+ fprintf(stderr, "%02x ", (*obj)[i]);
+ if ((i & 0xf) == 0xf) {
+ fprintf(stderr, "\n");
+ }
+ }
+ fprintf(stderr, "\n");
+#endif
+}
+
+static void ber_input_type_number(Visitor *v, double *obj, const char *name,
+ Error **errp)
+{
+ BERInputVisitor *aiv = to_biv(v);
+ uint32_t ber_type_tag;
+ uint8_t ber_type_flags;
+ uint32_t len;
+ bool is_indefinite;
+ char buf[128], buf2[128];
+ GDoubleIEEE754 num;
+ struct ieee754_buffer number;
+ size_t to_read;
+
+ ber_type_tag = ber_read_type(aiv, &ber_type_flags, errp);
+ if (error_is_set(errp)) {
+ return;
+ }
+
+ if (ber_type_tag != BER_TYPE_REAL ||
+ ber_type_flags != (BER_TYPE_CLASS_APPLICATION|BER_TYPE_PRIMITIVE)) {
+ snprintf(buf, sizeof(buf), "%s/%s/%s",
+ ber_type_class_to_str(ber_type_flags),
+ ber_type_pc_to_str(ber_type_flags),
+ ber_type_to_str(ber_type_tag));
+ snprintf(buf2, sizeof(buf2), "%s/%s/%s",
+ ber_type_class_to_str(BER_TYPE_CLASS_APPLICATION),
+ ber_type_pc_to_str(BER_TYPE_PRIMITIVE),
+ ber_type_to_str(BER_TYPE_REAL));
+ error_set(errp, QERR_INVALID_PARAMETER_TYPE, buf, buf2);
+ return;
+ }
+
+ len = ber_read_length(aiv, &is_indefinite, errp);
+ if (error_is_set(errp)) {
+ return;
+ }
+
+ to_read = sizeof(number) - offsetof(struct ieee754_buffer, first);
+
+ if (len != to_read) {
+ snprintf(buf, sizeof(buf),
+ "Length indicator in input byte stream "
+ "of real has unexpected length %"PRIu32"; "
+ "expected %" PRIu64,
+ len, sizeof(buf));
+ error_set(errp, QERR_INVALID_PARAMETER, buf);
+ return;
+ }
+
+ if (is_indefinite) {
+ snprintf(buf, sizeof(buf),
+ "ASN.1 indefinite length in a real type "
+ "at offset %" PRIu64,
+ aiv->cur_pos);
+ error_set(errp, QERR_INVALID_PARAMETER, buf);
+ return;
+ }
+
+ if (qemu_read_bytes(aiv->qfile, &number.first, to_read) != to_read) {
+ error_setg(errp, "QEMUFile error: Error while reading real");
+ return;
+ }
+
+ switch (number.first) {
+ case 0x42:
+ *obj = nan("NAN");
+ break;
+ case 0x41:
+ case 0x40:
+ num.mpn.sign = ((number.first & 0x1) != 0);
+ num.mpn.biased_exponent = ~0;
+ num.mpn.mantissa_low = 0;
+ num.mpn.mantissa_high = 0;
+ *obj = num.v_double;
+ break;
+ default:
+ num.mpn.sign = ((number.first & 0x40) != 0);
+ num.mpn.biased_exponent = be16_to_cpu(number.exponent);
+ num.mpn.mantissa_low = be32_to_cpu(number.mant_lo);
+ num.mpn.mantissa_high = be32_to_cpu(number.mant_hi);
+ *obj = num.v_double;
+ }
+}
+
+Visitor *ber_input_get_visitor(BERInputVisitor *v)
+{
+ return &v->visitor;
+}
+
+uint64_t ber_input_get_parser_position(BERInputVisitor *v)
+{
+ return v->cur_pos;
+}
+
+uint64_t ber_input_get_largest_needed_buffer(BERInputVisitor *v)
+{
+ return v->largest_needed_buffer;
+}
+
+void ber_input_visitor_cleanup(BERInputVisitor *v)
+{
+ g_free(v);
+}
+
+BERInputVisitor *ber_input_visitor_new(QEMUFile *qfile,
+ uint64_t max_allowed_buffer_size)
+{
+ BERInputVisitor *v;
+
+ v = g_malloc0(sizeof(*v));
+
+ v->visitor.start_struct = ber_input_start_struct;
+ v->visitor.end_struct = ber_input_end_struct;
+ v->visitor.start_carray = ber_input_start_carray;
+ v->visitor.next_carray = ber_input_next_carray;
+ v->visitor.end_carray = ber_input_end_carray;
+ v->visitor.start_list = ber_input_start_list;
+ v->visitor.next_list = ber_input_next_list;
+ v->visitor.end_list = ber_input_end_list;
+ v->visitor.type_int = ber_input_type_int;
+ v->visitor.type_uint8 = ber_input_type_uint8;
+ v->visitor.type_uint16 = ber_input_type_uint16;
+ v->visitor.type_uint32 = ber_input_type_uint32;
+ v->visitor.type_uint64 = ber_input_type_uint64;
+ v->visitor.type_int8 = ber_input_type_int8;
+ v->visitor.type_int16 = ber_input_type_int16;
+ v->visitor.type_int32 = ber_input_type_int32;
+ v->visitor.type_int64 = ber_input_type_int64;
+ v->visitor.type_bool = ber_input_type_bool;
+ v->visitor.type_str = ber_input_type_str;
+ v->visitor.type_sized_buffer = ber_input_sized_buffer;
+ v->visitor.type_number = ber_input_type_number;
+
+ v->qfile = qfile;
+ v->cur_pos = 0;
+ v->max_allowed_buffer_size = max_allowed_buffer_size;
+ v->largest_needed_buffer = 0;
+
+ return v;
+}
--
1.7.10.4
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [Qemu-devel] [PATCH 8/9] asn1_test_visitor_serialization.diff
2013-03-13 3:09 [Qemu-devel] [PATCH 0/9 v2] Implement and test asn1 ber visitors Joel Schopp
` (6 preceding siblings ...)
2013-03-13 3:09 ` [Qemu-devel] [PATCH 7/9] asn1_input-visitor.diff Joel Schopp
@ 2013-03-13 3:09 ` Joel Schopp
2013-03-13 3:09 ` [Qemu-devel] [PATCH 9/9] update_maintainers.diff Joel Schopp
8 siblings, 0 replies; 18+ messages in thread
From: Joel Schopp @ 2013-03-13 3:09 UTC (permalink / raw)
To: qemu-devel; +Cc: Joel Schopp, Stefan Berger, Michael Tsirkin
Add BER Visitor hooks to test-visitor-serialization
Cc: Michael Tsirkin <mst@redhat.com>
Cc: Stefan Berger <stefanb@linux.vnet.ibm.com>
Signed-off-by: Joel Schopp <jschopp@linux.vnet.ibm.com>
---
tests/Makefile | 2 +-
tests/test-visitor-serialization.c | 72 ++++++++++++++++++++++++++++++++++++
2 files changed, 73 insertions(+), 1 deletion(-)
diff --git a/tests/Makefile b/tests/Makefile
index 567e36e..578d732 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -124,7 +124,7 @@ tests/test-qmp-output-visitor$(EXESUF): tests/test-qmp-output-visitor.o $(test-q
tests/test-qmp-input-visitor$(EXESUF): tests/test-qmp-input-visitor.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a
tests/test-qmp-input-strict$(EXESUF): tests/test-qmp-input-strict.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a
tests/test-qmp-commands$(EXESUF): tests/test-qmp-commands.o tests/test-qmp-marshal.o $(test-qapi-obj-y) qapi-types.o qapi-visit.o libqemuutil.a libqemustub.a
-tests/test-visitor-serialization$(EXESUF): tests/test-visitor-serialization.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a
+tests/test-visitor-serialization$(EXESUF): tests/test-visitor-serialization.o $(test-qapi-obj-y) $(block-obj-y) libqemuutil.a libqemustub.a
tests/test-mul64$(EXESUF): tests/test-mul64.o libqemuutil.a
diff --git a/tests/test-visitor-serialization.c b/tests/test-visitor-serialization.c
index 3c6b8df..aae7464 100644
--- a/tests/test-visitor-serialization.c
+++ b/tests/test-visitor-serialization.c
@@ -14,6 +14,7 @@
#include <stdlib.h>
#include <stdint.h>
#include <float.h>
+#include <math.h>
#include "qemu-common.h"
#include "test-qapi-types.h"
@@ -23,6 +24,9 @@
#include "qapi/qmp-output-visitor.h"
#include "qapi/string-input-visitor.h"
#include "qapi/string-output-visitor.h"
+#include "qapi/ber-input-visitor.h"
+#include "qapi/ber-output-visitor.h"
+#include "migration/qemu-file.h"
typedef struct PrimitiveType {
union {
@@ -701,6 +705,59 @@ static void string_cleanup(void *datap)
string_input_visitor_cleanup(d->siv);
}
+
+typedef struct BERSerializeData {
+ BEROutputVisitor *sov;
+ QEMUFile *qoutfile;
+ BERInputVisitor *siv;
+ QEMUFile *qinfile;
+} BERSerializeData;
+
+static void ber_serialize(void *native_in, void **datap,
+ VisitorFunc visit, Error **errp, BERTypePC ber_type_pc)
+{
+ BERSerializeData *d = g_malloc0(sizeof(*d));
+
+ d->qoutfile = qemu_bufopen("w", NULL);
+ d->sov = ber_output_visitor_new(d->qoutfile, ber_type_pc);
+ visit(ber_output_get_visitor(d->sov), &native_in, errp);
+ *datap = d;
+}
+
+static void ber_primitive_serialize(void *native_in, void **datap,
+ VisitorFunc visit, Error **errp)
+{
+ ber_serialize(native_in, datap, visit, errp, BER_TYPE_PRIMITIVE);
+}
+
+static void ber_constructed_serialize(void *native_in, void **datap,
+ VisitorFunc visit, Error **errp)
+{
+ ber_serialize(native_in, datap, visit, errp, BER_TYPE_CONSTRUCTED);
+}
+
+static void ber_deserialize(void **native_out, void *datap,
+ VisitorFunc visit, Error **errp)
+{
+ BERSerializeData *d = datap;
+ const QEMUSizedBuffer *qsb = qemu_buf_get(d->qoutfile);
+ QEMUSizedBuffer *new_qsb = qsb_clone(qsb);
+ g_assert(new_qsb != NULL);
+
+ d->qinfile = qemu_bufopen("r", new_qsb);
+
+ d->siv = ber_input_visitor_new(d->qinfile, ~0);
+ visit(ber_input_get_visitor(d->siv), native_out, errp);
+}
+
+static void ber_cleanup(void *datap)
+{
+ BERSerializeData *d = datap;
+ ber_output_visitor_cleanup(d->sov);
+ ber_input_visitor_cleanup(d->siv);
+}
+
+
/* visitor registration, test harness */
/* note: to function interchangeably as a serialization mechanism your
@@ -722,6 +779,21 @@ static const SerializeOps visitors[] = {
.cleanup = string_cleanup,
.caps = VCAP_PRIMITIVES
},
+ {
+ .type = "ASN.1 BER primitives",
+ .serialize = ber_primitive_serialize,
+ .deserialize = ber_deserialize,
+ .cleanup = ber_cleanup,
+ .caps = VCAP_PRIMITIVES
+ },
+ {
+ .type = "ASN.1 BER constructed",
+ .serialize = ber_constructed_serialize,
+ .deserialize = ber_deserialize,
+ .cleanup = ber_cleanup,
+ .caps = VCAP_PRIMITIVES | VCAP_LISTS
+ },
+
{ NULL }
};
--
1.7.10.4
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [Qemu-devel] [PATCH 9/9] update_maintainers.diff
2013-03-13 3:09 [Qemu-devel] [PATCH 0/9 v2] Implement and test asn1 ber visitors Joel Schopp
` (7 preceding siblings ...)
2013-03-13 3:09 ` [Qemu-devel] [PATCH 8/9] asn1_test_visitor_serialization.diff Joel Schopp
@ 2013-03-13 3:09 ` Joel Schopp
2013-03-13 20:53 ` Eric Blake
8 siblings, 1 reply; 18+ messages in thread
From: Joel Schopp @ 2013-03-13 3:09 UTC (permalink / raw)
To: qemu-devel; +Cc: Joel Schopp, Stefan Berger, Michael Tsirkin
Since I'm throwing all this code out there I'm also signing up to maintain it.
Send me your bug reports.
Cc: Michael Tsirkin <mst@redhat.com>
Cc: Stefan Berger <stefanb@linux.vnet.ibm.com>
Signed-off-by: Joel Schopp <jschopp@linux.vnet.ibm.com>
---
MAINTAINERS | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 0ca7e1d..8a2f444 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -790,3 +790,11 @@ Stable 0.10
L: qemu-stable@nongnu.org
T: git git://git.qemu.org/qemu-stable-0.10.git
S: Orphan
+
+Visitors
+---------------
+ASN.1 BER
+M: Joel Schopp <jschopp@linux.vnet.ibm.com>
+S: Maintained
+F: /qapi/ber*
+F: include/qapi/ber*
\ No newline at end of file
--
1.7.10.4
^ permalink raw reply related [flat|nested] 18+ messages in thread
* Re: [Qemu-devel] [PATCH 2/9] qapi_c_arrays.diff
2013-03-13 3:09 ` [Qemu-devel] [PATCH 2/9] qapi_c_arrays.diff Joel Schopp
@ 2013-03-13 12:08 ` Eric Blake
0 siblings, 0 replies; 18+ messages in thread
From: Eric Blake @ 2013-03-13 12:08 UTC (permalink / raw)
To: Joel Schopp; +Cc: Paolo Bonzini, Michael Tsirkin, qemu-devel, Michael Roth
[-- Attachment #1: Type: text/plain, Size: 1567 bytes --]
On 03/12/2013 09:09 PM, Joel Schopp wrote:
> Forward ported Mike's previously sent patch
> (see http://lists.gnu.org/archive/html/qemu-devel/2012-10/msg05782.html ) in my
> series since it impelments a qapi array interface the ASN.1 BER visitor needs.
s/impelments/implements/
>
> Generally these will be serialized into lists, but the
> representation can be of any form so long as it can
> be deserialized into a single-dimension C array.
>
> +++ b/include/qapi/visitor-impl.h
> @@ -34,6 +34,10 @@ struct Visitor
> void (*type_str)(Visitor *v, char **obj, const char *name, Error **errp);
> void (*type_number)(Visitor *v, double *obj, const char *name,
> Error **errp);
> + void (*start_carray)(Visitor *v, void **obj, const char *name,
> + size_t elem_count, size_t elem_size, Error **errp);
Indentation looks off.
> +++ b/include/qapi/visitor.h
> @@ -51,5 +51,9 @@ void visit_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp);
> void visit_type_bool(Visitor *v, bool *obj, const char *name, Error **errp);
> void visit_type_str(Visitor *v, char **obj, const char *name, Error **errp);
> void visit_type_number(Visitor *v, double *obj, const char *name, Error **errp);
> +void visit_start_carray(Visitor *v, void **obj, const char *name,
> + size_t elem_count, size_t elem_size, Error **errp);
Indentation.
--
Eric Blake eblake redhat com +1-919-301-3266
Libvirt virtualization library http://libvirt.org
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 621 bytes --]
^ permalink raw reply [flat|nested] 18+ messages in thread
* [Qemu-devel] [PATCH 9/9] update_maintainers.diff
2013-03-13 18:56 [Qemu-devel] [PATCH 0/9 v3] Implement and test asn1 ber visitors Joel Schopp
@ 2013-03-13 18:56 ` Joel Schopp
0 siblings, 0 replies; 18+ messages in thread
From: Joel Schopp @ 2013-03-13 18:56 UTC (permalink / raw)
To: qemu-devel; +Cc: Joel Schopp, Stefan Berger, Michael Tsirkin
Since I'm throwing all this code out there I'm also signing up to maintain it.
Send me your bug reports.
Cc: Michael Tsirkin <mst@redhat.com>
Cc: Stefan Berger <stefanb@linux.vnet.ibm.com>
Signed-off-by: Joel Schopp <jschopp@linux.vnet.ibm.com>
---
MAINTAINERS | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 0ca7e1d..8a2f444 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -790,3 +790,11 @@ Stable 0.10
L: qemu-stable@nongnu.org
T: git git://git.qemu.org/qemu-stable-0.10.git
S: Orphan
+
+Visitors
+---------------
+ASN.1 BER
+M: Joel Schopp <jschopp@linux.vnet.ibm.com>
+S: Maintained
+F: /qapi/ber*
+F: include/qapi/ber*
\ No newline at end of file
--
1.7.10.4
^ permalink raw reply related [flat|nested] 18+ messages in thread
* Re: [Qemu-devel] [PATCH 6/9] asn1_output-visitor.diff
2013-03-13 3:09 ` [Qemu-devel] [PATCH 6/9] asn1_output-visitor.diff Joel Schopp
@ 2013-03-13 20:46 ` Eric Blake
0 siblings, 0 replies; 18+ messages in thread
From: Eric Blake @ 2013-03-13 20:46 UTC (permalink / raw)
To: Joel Schopp; +Cc: Michael Tsirkin, qemu-devel, Stefan Berger
[-- Attachment #1: Type: text/plain, Size: 2060 bytes --]
On 03/12/2013 09:09 PM, Joel Schopp wrote:
> Implement an output visitor for ASN.1 BER encoding.
>
> Cc: Michael Tsirkin <mst@redhat.com>
> Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
> Signed-off-by: Joel Schopp <jschopp@linux.vnet.ibm.com>
> ---
> +++ b/include/qapi/ber-output-visitor.h
> @@ -0,0 +1,28 @@
> +/*
> + * BER Output Visitor header
> + *
> + * Copyright IBM, Corp. 2011
It's 2013.
> +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);
Space around binary operators.
> +
> + if (maxnumbytes > 1) {
> + exp_zeros = ((mask & val) == 0) ? true : false;
I'm not a fan of 'bool expr ? true : false', when just 'bool expr' will do.
> + 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);
Another case of spaces around binary operator (I didn't look closely, so
you may want to scrub the entire patch for more instances than what I
saw on a casual quick read)
--
Eric Blake eblake redhat com +1-919-301-3266
Libvirt virtualization library http://libvirt.org
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 621 bytes --]
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [Qemu-devel] [PATCH 7/9] asn1_input-visitor.diff
2013-03-13 3:09 ` [Qemu-devel] [PATCH 7/9] asn1_input-visitor.diff Joel Schopp
@ 2013-03-13 20:51 ` Eric Blake
2013-03-13 21:20 ` Stefan Berger
0 siblings, 1 reply; 18+ messages in thread
From: Eric Blake @ 2013-03-13 20:51 UTC (permalink / raw)
To: Joel Schopp; +Cc: Michael Tsirkin, qemu-devel, Stefan Berger
[-- Attachment #1: Type: text/plain, Size: 1944 bytes --]
On 03/12/2013 09:09 PM, Joel Schopp wrote:
> Implement an input visitor for ASN.1 BER encoding.
>
> Cc: Michael Tsirkin <mst@redhat.com>
> Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
> Signed-off-by: Joel Schopp <jschopp@linux.vnet.ibm.com>
> ---
> include/qapi/ber-input-visitor.h | 30 ++
> qapi/Makefile.objs | 2 +-
> qapi/ber-input-visitor.c | 1073 ++++++++++++++++++++++++++++++++++++++
> 3 files changed, 1104 insertions(+), 1 deletion(-)
> create mode 100644 include/qapi/ber-input-visitor.h
> create mode 100644 qapi/ber-input-visitor.c
>
> diff --git a/include/qapi/ber-input-visitor.h b/include/qapi/ber-input-visitor.h
> new file mode 100644
> index 0000000..eaa3d0e
> --- /dev/null
> +++ b/include/qapi/ber-input-visitor.h
> @@ -0,0 +1,30 @@
> +/*
> + * BER Input Visitor header
> + *
> + * Copyright IBM, Corp. 2011
It's 2013
> +static void ber_input_type_number(Visitor *v, double *obj, const char *name,
> + Error **errp)
> +{
> + BERInputVisitor *aiv = to_biv(v);
> + uint32_t ber_type_tag;
> + uint8_t ber_type_flags;
> + uint32_t len;
> + bool is_indefinite;
> + char buf[128], buf2[128];
> + GDoubleIEEE754 num;
> + struct ieee754_buffer number;
> + size_t to_read;
> +
> + switch (number.first) {
> + case 0x42:
> + *obj = nan("NAN");
> + break;
> + case 0x41:
> + case 0x40:
> + num.mpn.sign = ((number.first & 0x1) != 0);
> + num.mpn.biased_exponent = ~0;
> + num.mpn.mantissa_low = 0;
> + num.mpn.mantissa_high = 0;
> + *obj = num.v_double;
Is this really portable enough? In other words, do we really require
that qemu only compiles on platforms where double is in IEEE format?
--
Eric Blake eblake redhat com +1-919-301-3266
Libvirt virtualization library http://libvirt.org
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 621 bytes --]
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [Qemu-devel] [PATCH 9/9] update_maintainers.diff
2013-03-13 3:09 ` [Qemu-devel] [PATCH 9/9] update_maintainers.diff Joel Schopp
@ 2013-03-13 20:53 ` Eric Blake
0 siblings, 0 replies; 18+ messages in thread
From: Eric Blake @ 2013-03-13 20:53 UTC (permalink / raw)
To: Joel Schopp; +Cc: Michael Tsirkin, qemu-devel, Stefan Berger
[-- Attachment #1: Type: text/plain, Size: 588 bytes --]
On 03/12/2013 09:09 PM, Joel Schopp wrote:
> Since I'm throwing all this code out there I'm also signing up to maintain it.
> Send me your bug reports.
>
> Cc: Michael Tsirkin <mst@redhat.com>
> Cc: Stefan Berger <stefanb@linux.vnet.ibm.com>
> Signed-off-by: Joel Schopp <jschopp@linux.vnet.ibm.com>
> ---
> MAINTAINERS | 8 ++++++++
> 1 file changed, 8 insertions(+)
> +++ b/MAINTAINERS
> +F: include/qapi/ber*
> \ No newline at end of file
Fix that.
--
Eric Blake eblake redhat com +1-919-301-3266
Libvirt virtualization library http://libvirt.org
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 621 bytes --]
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [Qemu-devel] [PATCH 7/9] asn1_input-visitor.diff
2013-03-13 20:51 ` Eric Blake
@ 2013-03-13 21:20 ` Stefan Berger
2013-03-14 1:22 ` Joel Schopp
0 siblings, 1 reply; 18+ messages in thread
From: Stefan Berger @ 2013-03-13 21:20 UTC (permalink / raw)
To: Eric Blake; +Cc: Joel Schopp, qemu-devel, Michael Tsirkin
On 03/13/2013 04:51 PM, Eric Blake wrote:
> On 03/12/2013 09:09 PM, Joel Schopp wrote:
>> Implement an input visitor for ASN.1 BER encoding.
>>
>> Cc: Michael Tsirkin <mst@redhat.com>
>> Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
>> Signed-off-by: Joel Schopp <jschopp@linux.vnet.ibm.com>
>> ---
>> include/qapi/ber-input-visitor.h | 30 ++
>> qapi/Makefile.objs | 2 +-
>> qapi/ber-input-visitor.c | 1073 ++++++++++++++++++++++++++++++++++++++
>> 3 files changed, 1104 insertions(+), 1 deletion(-)
>> create mode 100644 include/qapi/ber-input-visitor.h
>> create mode 100644 qapi/ber-input-visitor.c
>>
>> diff --git a/include/qapi/ber-input-visitor.h b/include/qapi/ber-input-visitor.h
>> new file mode 100644
>> index 0000000..eaa3d0e
>> --- /dev/null
>> +++ b/include/qapi/ber-input-visitor.h
>> @@ -0,0 +1,30 @@
>> +/*
>> + * BER Input Visitor header
>> + *
>> + * Copyright IBM, Corp. 2011
> It's 2013
>
>
>> +static void ber_input_type_number(Visitor *v, double *obj, const char *name,
>> + Error **errp)
>> +{
>> + BERInputVisitor *aiv = to_biv(v);
>> + uint32_t ber_type_tag;
>> + uint8_t ber_type_flags;
>> + uint32_t len;
>> + bool is_indefinite;
>> + char buf[128], buf2[128];
>> + GDoubleIEEE754 num;
>> + struct ieee754_buffer number;
>> + size_t to_read;
>> +
>> + switch (number.first) {
>> + case 0x42:
>> + *obj = nan("NAN");
>> + break;
>> + case 0x41:
>> + case 0x40:
>> + num.mpn.sign = ((number.first & 0x1) != 0);
>> + num.mpn.biased_exponent = ~0;
>> + num.mpn.mantissa_low = 0;
>> + num.mpn.mantissa_high = 0;
>> + *obj = num.v_double;
> Is this really portable enough? In other words, do we really require
> that qemu only compiles on platforms where double is in IEEE format?
>
Good question. I don't know in what format the other platforms are,
though. I know it's used on i386, x86_64, ppc, ppc64. My guess is that
it would have to be a really exotic CPU to not use this standard.
I hope this applies to ARM in general...
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0067d/Cihgejjg.html
Regards,
Stefan
Stefan
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [Qemu-devel] [PATCH 7/9] asn1_input-visitor.diff
2013-03-13 21:20 ` Stefan Berger
@ 2013-03-14 1:22 ` Joel Schopp
2013-03-14 1:53 ` Stefan Berger
0 siblings, 1 reply; 18+ messages in thread
From: Joel Schopp @ 2013-03-14 1:22 UTC (permalink / raw)
To: Stefan Berger; +Cc: qemu-devel, Michael Tsirkin
>>> + case 0x41:
>>> + case 0x40:
>>> + num.mpn.sign = ((number.first & 0x1) != 0);
>>> + num.mpn.biased_exponent = ~0;
>>> + num.mpn.mantissa_low = 0;
>>> + num.mpn.mantissa_high = 0;
>>> + *obj = num.v_double;
>> Is this really portable enough? In other words, do we really require
>> that qemu only compiles on platforms where double is in IEEE format?
>>
> Good question. I don't know in what format the other platforms are,
> though. I know it's used on i386, x86_64, ppc, ppc64. My guess is that
> it would have to be a really exotic CPU to not use this standard.
>
> I hope this applies to ARM in general...
> http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0067d/Cihgejjg.html
>
There are chips without hardware floating point, for example the ppc
404. However, these use software emulation of IEE floating point when
doing floating point math. What we are looking for is a platform that
has floating point that isn't IEEE.
Cell SPE comes to mind, though I don't think qemu emulates it and it is
a dead architecture (RIP). It also has an IEEE mode that makes its
non-standard floating point compliant. Even in non-compliant mode the
code above would work, it's more rounding that might be off.
In summary I can't think of a platform we run on or emulate that has a
floating point mode the above would not work on. If somebody can think
of one please let me know so we can #ifdef a special case for it.
Otherwise let's assume the above works for all the platforms we care about.
-Joel
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [Qemu-devel] [PATCH 7/9] asn1_input-visitor.diff
2013-03-14 1:22 ` Joel Schopp
@ 2013-03-14 1:53 ` Stefan Berger
0 siblings, 0 replies; 18+ messages in thread
From: Stefan Berger @ 2013-03-14 1:53 UTC (permalink / raw)
To: Joel Schopp; +Cc: qemu-devel, Michael Tsirkin
On 03/13/2013 09:22 PM, Joel Schopp wrote:
>
>>>> + case 0x41:
>>>> + case 0x40:
>>>> + num.mpn.sign = ((number.first & 0x1) != 0);
>>>> + num.mpn.biased_exponent = ~0;
>>>> + num.mpn.mantissa_low = 0;
>>>> + num.mpn.mantissa_high = 0;
>>>> + *obj = num.v_double;
>>> Is this really portable enough? In other words, do we really require
>>> that qemu only compiles on platforms where double is in IEEE format?
>>>
>> Good question. I don't know in what format the other platforms are,
>> though. I know it's used on i386, x86_64, ppc, ppc64. My guess is that
>> it would have to be a really exotic CPU to not use this standard.
>>
>> I hope this applies to ARM in general...
>> http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0067d/Cihgejjg.html
>>
>>
>
> There are chips without hardware floating point, for example the ppc
> 404. However, these use software emulation of IEE floating point when
> doing floating point math. What we are looking for is a platform that
> has floating point that isn't IEEE.
>
> Cell SPE comes to mind, though I don't think qemu emulates it and it
> is a dead architecture (RIP). It also has an IEEE mode that makes its
> non-standard floating point compliant. Even in non-compliant mode the
> code above would work, it's more rounding that might be off.
>
> In summary I can't think of a platform we run on or emulate that has a
> floating point mode the above would not work on. If somebody can
> think of one please let me know so we can #ifdef a special case for
> it. Otherwise let's assume the above works for all the platforms we
> care about.
One other choice could be string-encoding as also suggested by the
standard with possible loss of precision. Though I think that any exotic
processor could be dealt with a #ifdef around the code pressing its
format into 2 bytes of exponent and 8 bytes of mantissa.
Stefan
^ permalink raw reply [flat|nested] 18+ messages in thread
end of thread, other threads:[~2013-03-14 1:53 UTC | newest]
Thread overview: 18+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-03-13 3:09 [Qemu-devel] [PATCH 0/9 v2] Implement and test asn1 ber visitors Joel Schopp
2013-03-13 3:09 ` [Qemu-devel] [PATCH 1/9] qemu-file Joel Schopp
2013-03-13 3:09 ` [Qemu-devel] [PATCH 2/9] qapi_c_arrays.diff Joel Schopp
2013-03-13 12:08 ` Eric Blake
2013-03-13 3:09 ` [Qemu-devel] [PATCH 3/9] two new file wrappers Joel Schopp
2013-03-13 3:09 ` [Qemu-devel] [PATCH 4/9] qemu_qsb.diff Joel Schopp
2013-03-13 3:09 ` [Qemu-devel] [PATCH 5/9] qapi_sized_buffer Joel Schopp
2013-03-13 3:09 ` [Qemu-devel] [PATCH 6/9] asn1_output-visitor.diff Joel Schopp
2013-03-13 20:46 ` Eric Blake
2013-03-13 3:09 ` [Qemu-devel] [PATCH 7/9] asn1_input-visitor.diff Joel Schopp
2013-03-13 20:51 ` Eric Blake
2013-03-13 21:20 ` Stefan Berger
2013-03-14 1:22 ` Joel Schopp
2013-03-14 1:53 ` Stefan Berger
2013-03-13 3:09 ` [Qemu-devel] [PATCH 8/9] asn1_test_visitor_serialization.diff Joel Schopp
2013-03-13 3:09 ` [Qemu-devel] [PATCH 9/9] update_maintainers.diff Joel Schopp
2013-03-13 20:53 ` Eric Blake
-- strict thread matches above, loose matches on Subject: below --
2013-03-13 18:56 [Qemu-devel] [PATCH 0/9 v3] Implement and test asn1 ber visitors Joel Schopp
2013-03-13 18:56 ` [Qemu-devel] [PATCH 9/9] update_maintainers.diff Joel Schopp
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).