diff options
-rw-r--r-- | Makefile.objs | 1 | ||||
-rw-r--r-- | qemu-file.c | 826 | ||||
-rw-r--r-- | savevm.c | 820 |
3 files changed, 827 insertions, 820 deletions
diff --git a/Makefile.objs b/Makefile.objs index 23eda2b19b..857bb53ae4 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -52,6 +52,7 @@ common-obj-$(CONFIG_LINUX) += fsdev/ common-obj-y += migration.o migration-tcp.o common-obj-y += vmstate.o +common-obj-y += qemu-file.o common-obj-$(CONFIG_RDMA) += migration-rdma.o common-obj-y += qemu-char.o #aio.o common-obj-y += block-migration.o diff --git a/qemu-file.c b/qemu-file.c new file mode 100644 index 0000000000..9473b674ba --- /dev/null +++ b/qemu-file.c @@ -0,0 +1,826 @@ +#include "qemu-common.h" +#include "qemu/iov.h" +#include "qemu/sockets.h" +#include "block/coroutine.h" +#include "migration/migration.h" +#include "migration/qemu-file.h" + +#define IO_BUF_SIZE 32768 +#define MAX_IOV_SIZE MIN(IOV_MAX, 64) + +struct QEMUFile { + const QEMUFileOps *ops; + void *opaque; + + 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]; + + struct iovec iov[MAX_IOV_SIZE]; + unsigned int iovcnt; + + int last_error; +}; + +typedef struct QEMUFileStdio { + FILE *stdio_file; + QEMUFile *file; +} QEMUFileStdio; + +typedef struct QEMUFileSocket { + int fd; + QEMUFile *file; +} QEMUFileSocket; + +static ssize_t socket_writev_buffer(void *opaque, struct iovec *iov, int iovcnt, + int64_t pos) +{ + QEMUFileSocket *s = opaque; + ssize_t len; + ssize_t size = iov_size(iov, iovcnt); + + len = iov_send(s->fd, iov, iovcnt, 0, size); + if (len < size) { + len = -socket_error(); + } + return len; +} + +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_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 || s->file->ops->writev_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; + + if (mode == NULL || (mode[0] != 'r' && mode[0] != 'w') || mode[1] != 0) { + fprintf(stderr, "qemu_popen: Argument validity check failed\n"); + return NULL; + } + + stdio_file = popen(command, mode); + if (stdio_file == NULL) { + 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 +}; + +static ssize_t unix_writev_buffer(void *opaque, struct iovec *iov, int iovcnt, + int64_t pos) +{ + QEMUFileSocket *s = opaque; + ssize_t len, offset; + ssize_t size = iov_size(iov, iovcnt); + ssize_t total = 0; + + assert(iovcnt > 0); + offset = 0; + while (size > 0) { + /* Find the next start position; skip all full-sized vector elements */ + while (offset >= iov[0].iov_len) { + offset -= iov[0].iov_len; + iov++, iovcnt--; + } + + /* skip `offset' bytes from the (now) first element, undo it on exit */ + assert(iovcnt > 0); + iov[0].iov_base += offset; + iov[0].iov_len -= offset; + + do { + len = writev(s->fd, iov, iovcnt); + } while (len == -1 && errno == EINTR); + if (len == -1) { + return -errno; + } + + /* Undo the changes above */ + iov[0].iov_base -= offset; + iov[0].iov_len += offset; + + /* Prepare for the next iteration */ + offset += len; + total += len; + size -= len; + } + + return total; +} + +static int unix_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size) +{ + QEMUFileSocket *s = opaque; + ssize_t len; + + for (;;) { + len = read(s->fd, buf, size); + if (len != -1) { + break; + } + if (errno == EAGAIN) { + yield_until_fd_readable(s->fd); + } else if (errno != EINTR) { + break; + } + } + + if (len == -1) { + len = -errno; + } + return len; +} + +static int unix_close(void *opaque) +{ + QEMUFileSocket *s = opaque; + close(s->fd); + g_free(s); + return 0; +} + +static const QEMUFileOps unix_read_ops = { + .get_fd = socket_get_fd, + .get_buffer = unix_get_buffer, + .close = unix_close +}; + +static const QEMUFileOps unix_write_ops = { + .get_fd = socket_get_fd, + .writev_buffer = unix_writev_buffer, + .close = unix_close +}; + +QEMUFile *qemu_fdopen(int fd, const char *mode) +{ + QEMUFileSocket *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(QEMUFileSocket)); + s->fd = fd; + + if (mode[0] == 'r') { + s->file = qemu_fopen_ops(s, &unix_read_ops); + } else { + s->file = qemu_fopen_ops(s, &unix_write_ops); + } + return s->file; +} + +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, + .writev_buffer = socket_writev_buffer, + .close = socket_close +}; + +bool qemu_file_mode_is_not_valid(const char *mode) +{ + 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 true; + } + + return false; +} + +QEMUFile *qemu_fopen_socket(int fd, const char *mode) +{ + QEMUFileSocket *s; + + if (qemu_file_mode_is_not_valid(mode)) { + return NULL; + } + + s = g_malloc0(sizeof(QEMUFileSocket)); + s->fd = fd; + if (mode[0] == 'w') { + qemu_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 (qemu_file_mode_is_not_valid(mode)) { + 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; +} + +QEMUFile *qemu_fopen_ops(void *opaque, const QEMUFileOps *ops) +{ + QEMUFile *f; + + f = g_malloc0(sizeof(QEMUFile)); + + f->opaque = opaque; + f->ops = ops; + return f; +} + +/* + * Get last error for stream f + * + * Return negative error value if there has been an error on previous + * operations, return 0 if no error happened. + * + */ +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; + } +} + +static inline bool qemu_file_is_writable(QEMUFile *f) +{ + return f->ops->writev_buffer || f->ops->put_buffer; +} + +/** + * Flushes QEMUFile buffer + * + * If there is writev_buffer QEMUFileOps it uses it otherwise uses + * put_buffer ops. + */ +void qemu_fflush(QEMUFile *f) +{ + ssize_t ret = 0; + + if (!qemu_file_is_writable(f)) { + return; + } + + if (f->ops->writev_buffer) { + if (f->iovcnt > 0) { + ret = f->ops->writev_buffer(f->opaque, f->iov, f->iovcnt, f->pos); + } + } else { + if (f->buf_index > 0) { + ret = f->ops->put_buffer(f->opaque, f->buf, f->pos, f->buf_index); + } + } + if (ret >= 0) { + f->pos += ret; + } + f->buf_index = 0; + f->iovcnt = 0; + if (ret < 0) { + qemu_file_set_error(f, ret); + } +} + +void ram_control_before_iterate(QEMUFile *f, uint64_t flags) +{ + int ret = 0; + + if (f->ops->before_ram_iterate) { + ret = f->ops->before_ram_iterate(f, f->opaque, flags); + if (ret < 0) { + qemu_file_set_error(f, ret); + } + } +} + +void ram_control_after_iterate(QEMUFile *f, uint64_t flags) +{ + int ret = 0; + + if (f->ops->after_ram_iterate) { + ret = f->ops->after_ram_iterate(f, f->opaque, flags); + if (ret < 0) { + qemu_file_set_error(f, ret); + } + } +} + +void ram_control_load_hook(QEMUFile *f, uint64_t flags) +{ + int ret = -EINVAL; + + if (f->ops->hook_ram_load) { + ret = f->ops->hook_ram_load(f, f->opaque, flags); + if (ret < 0) { + qemu_file_set_error(f, ret); + } + } else { + qemu_file_set_error(f, ret); + } +} + +size_t ram_control_save_page(QEMUFile *f, ram_addr_t block_offset, + ram_addr_t offset, size_t size, int *bytes_sent) +{ + if (f->ops->save_page) { + int ret = f->ops->save_page(f, f->opaque, block_offset, + offset, size, bytes_sent); + + if (ret != RAM_SAVE_CONTROL_DELAYED) { + if (bytes_sent && *bytes_sent > 0) { + qemu_update_position(f, *bytes_sent); + } else if (ret < 0) { + qemu_file_set_error(f, ret); + } + } + + return ret; + } + + return RAM_SAVE_CONTROL_NOT_SUPP; +} + +static void qemu_fill_buffer(QEMUFile *f) +{ + int len; + int pending; + + assert(!qemu_file_is_writable(f)); + + 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; +} + +void qemu_update_position(QEMUFile *f, size_t size) +{ + f->pos += size; +} + +/** 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; +} + +static void add_to_iovec(QEMUFile *f, const uint8_t *buf, int size) +{ + /* check for adjacent buffer and coalesce them */ + if (f->iovcnt > 0 && buf == f->iov[f->iovcnt - 1].iov_base + + f->iov[f->iovcnt - 1].iov_len) { + f->iov[f->iovcnt - 1].iov_len += size; + } else { + f->iov[f->iovcnt].iov_base = (uint8_t *)buf; + f->iov[f->iovcnt++].iov_len = size; + } + + if (f->iovcnt >= MAX_IOV_SIZE) { + qemu_fflush(f); + } +} + +void qemu_put_buffer_async(QEMUFile *f, const uint8_t *buf, int size) +{ + if (!f->ops->writev_buffer) { + qemu_put_buffer(f, buf, size); + return; + } + + if (f->last_error) { + return; + } + + f->bytes_xfer += size; + add_to_iovec(f, buf, size); +} + +void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size) +{ + int l; + + if (f->last_error) { + return; + } + + while (size > 0) { + l = IO_BUF_SIZE - f->buf_index; + if (l > size) { + l = size; + } + memcpy(f->buf + f->buf_index, buf, l); + f->bytes_xfer += l; + if (f->ops->writev_buffer) { + add_to_iovec(f, f->buf + f->buf_index, l); + } + f->buf_index += l; + if (f->buf_index == IO_BUF_SIZE) { + qemu_fflush(f); + } + if (qemu_file_get_error(f)) { + break; + } + buf += l; + size -= l; + } +} + +void qemu_put_byte(QEMUFile *f, int v) +{ + if (f->last_error) { + return; + } + + f->buf[f->buf_index] = v; + f->bytes_xfer++; + if (f->ops->writev_buffer) { + add_to_iovec(f, f->buf + f->buf_index, 1); + } + f->buf_index++; + 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; + + assert(!qemu_file_is_writable(f)); + + 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; + + assert(!qemu_file_is_writable(f)); + + 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; +} @@ -114,396 +114,6 @@ void qemu_announce_self(void) /***********************************************************/ /* savevm/loadvm support */ -#define IO_BUF_SIZE 32768 -#define MAX_IOV_SIZE MIN(IOV_MAX, 64) - -struct QEMUFile { - const QEMUFileOps *ops; - void *opaque; - - 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]; - - struct iovec iov[MAX_IOV_SIZE]; - unsigned int iovcnt; - - int last_error; -}; - -typedef struct QEMUFileStdio { - FILE *stdio_file; - QEMUFile *file; -} QEMUFileStdio; - -typedef struct QEMUFileSocket { - int fd; - QEMUFile *file; -} QEMUFileSocket; - -static ssize_t socket_writev_buffer(void *opaque, struct iovec *iov, int iovcnt, - int64_t pos) -{ - QEMUFileSocket *s = opaque; - ssize_t len; - ssize_t size = iov_size(iov, iovcnt); - - len = iov_send(s->fd, iov, iovcnt, 0, size); - if (len < size) { - len = -socket_error(); - } - return len; -} - -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_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 || s->file->ops->writev_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; - - if (mode == NULL || (mode[0] != 'r' && mode[0] != 'w') || mode[1] != 0) { - fprintf(stderr, "qemu_popen: Argument validity check failed\n"); - return NULL; - } - - stdio_file = popen(command, mode); - if (stdio_file == NULL) { - 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 -}; - -static ssize_t unix_writev_buffer(void *opaque, struct iovec *iov, int iovcnt, - int64_t pos) -{ - QEMUFileSocket *s = opaque; - ssize_t len, offset; - ssize_t size = iov_size(iov, iovcnt); - ssize_t total = 0; - - assert(iovcnt > 0); - offset = 0; - while (size > 0) { - /* Find the next start position; skip all full-sized vector elements */ - while (offset >= iov[0].iov_len) { - offset -= iov[0].iov_len; - iov++, iovcnt--; - } - - /* skip `offset' bytes from the (now) first element, undo it on exit */ - assert(iovcnt > 0); - iov[0].iov_base += offset; - iov[0].iov_len -= offset; - - do { - len = writev(s->fd, iov, iovcnt); - } while (len == -1 && errno == EINTR); - if (len == -1) { - return -errno; - } - - /* Undo the changes above */ - iov[0].iov_base -= offset; - iov[0].iov_len += offset; - - /* Prepare for the next iteration */ - offset += len; - total += len; - size -= len; - } - - return total; -} - -static int unix_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size) -{ - QEMUFileSocket *s = opaque; - ssize_t len; - - for (;;) { - len = read(s->fd, buf, size); - if (len != -1) { - break; - } - if (errno == EAGAIN) { - yield_until_fd_readable(s->fd); - } else if (errno != EINTR) { - break; - } - } - - if (len == -1) { - len = -errno; - } - return len; -} - -static int unix_close(void *opaque) -{ - QEMUFileSocket *s = opaque; - close(s->fd); - g_free(s); - return 0; -} - -static const QEMUFileOps unix_read_ops = { - .get_fd = socket_get_fd, - .get_buffer = unix_get_buffer, - .close = unix_close -}; - -static const QEMUFileOps unix_write_ops = { - .get_fd = socket_get_fd, - .writev_buffer = unix_writev_buffer, - .close = unix_close -}; - -QEMUFile *qemu_fdopen(int fd, const char *mode) -{ - QEMUFileSocket *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(QEMUFileSocket)); - s->fd = fd; - - if (mode[0] == 'r') { - s->file = qemu_fopen_ops(s, &unix_read_ops); - } else { - s->file = qemu_fopen_ops(s, &unix_write_ops); - } - return s->file; -} - -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, - .writev_buffer = socket_writev_buffer, - .close = socket_close -}; - -bool qemu_file_mode_is_not_valid(const char *mode) -{ - 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 true; - } - - return false; -} - -QEMUFile *qemu_fopen_socket(int fd, const char *mode) -{ - QEMUFileSocket *s; - - if (qemu_file_mode_is_not_valid(mode)) { - return NULL; - } - - s = g_malloc0(sizeof(QEMUFileSocket)); - s->fd = fd; - if (mode[0] == 'w') { - qemu_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 (qemu_file_mode_is_not_valid(mode)) { - 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 ssize_t block_writev_buffer(void *opaque, struct iovec *iov, int iovcnt, int64_t pos) { @@ -555,436 +165,6 @@ static QEMUFile *qemu_fopen_bdrv(BlockDriverState *bs, int is_writable) 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; - return f; -} - -/* - * Get last error for stream f - * - * Return negative error value if there has been an error on previous - * operations, return 0 if no error happened. - * - */ -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; - } -} - -static inline bool qemu_file_is_writable(QEMUFile *f) -{ - return f->ops->writev_buffer || f->ops->put_buffer; -} - -/** - * Flushes QEMUFile buffer - * - * If there is writev_buffer QEMUFileOps it uses it otherwise uses - * put_buffer ops. - */ -void qemu_fflush(QEMUFile *f) -{ - ssize_t ret = 0; - - if (!qemu_file_is_writable(f)) { - return; - } - - if (f->ops->writev_buffer) { - if (f->iovcnt > 0) { - ret = f->ops->writev_buffer(f->opaque, f->iov, f->iovcnt, f->pos); - } - } else { - if (f->buf_index > 0) { - ret = f->ops->put_buffer(f->opaque, f->buf, f->pos, f->buf_index); - } - } - if (ret >= 0) { - f->pos += ret; - } - f->buf_index = 0; - f->iovcnt = 0; - if (ret < 0) { - qemu_file_set_error(f, ret); - } -} - -void ram_control_before_iterate(QEMUFile *f, uint64_t flags) -{ - int ret = 0; - - if (f->ops->before_ram_iterate) { - ret = f->ops->before_ram_iterate(f, f->opaque, flags); - if (ret < 0) { - qemu_file_set_error(f, ret); - } - } -} - -void ram_control_after_iterate(QEMUFile *f, uint64_t flags) -{ - int ret = 0; - - if (f->ops->after_ram_iterate) { - ret = f->ops->after_ram_iterate(f, f->opaque, flags); - if (ret < 0) { - qemu_file_set_error(f, ret); - } - } -} - -void ram_control_load_hook(QEMUFile *f, uint64_t flags) -{ - int ret = -EINVAL; - - if (f->ops->hook_ram_load) { - ret = f->ops->hook_ram_load(f, f->opaque, flags); - if (ret < 0) { - qemu_file_set_error(f, ret); - } - } else { - qemu_file_set_error(f, ret); - } -} - -size_t ram_control_save_page(QEMUFile *f, ram_addr_t block_offset, - ram_addr_t offset, size_t size, int *bytes_sent) -{ - if (f->ops->save_page) { - int ret = f->ops->save_page(f, f->opaque, block_offset, - offset, size, bytes_sent); - - if (ret != RAM_SAVE_CONTROL_DELAYED) { - if (bytes_sent && *bytes_sent > 0) { - qemu_update_position(f, *bytes_sent); - } else if (ret < 0) { - qemu_file_set_error(f, ret); - } - } - - return ret; - } - - return RAM_SAVE_CONTROL_NOT_SUPP; -} - -static void qemu_fill_buffer(QEMUFile *f) -{ - int len; - int pending; - - assert(!qemu_file_is_writable(f)); - - 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; -} - -void qemu_update_position(QEMUFile *f, size_t size) -{ - f->pos += size; -} - -/** 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; -} - -static void add_to_iovec(QEMUFile *f, const uint8_t *buf, int size) -{ - /* check for adjacent buffer and coalesce them */ - if (f->iovcnt > 0 && buf == f->iov[f->iovcnt - 1].iov_base + - f->iov[f->iovcnt - 1].iov_len) { - f->iov[f->iovcnt - 1].iov_len += size; - } else { - f->iov[f->iovcnt].iov_base = (uint8_t *)buf; - f->iov[f->iovcnt++].iov_len = size; - } - - if (f->iovcnt >= MAX_IOV_SIZE) { - qemu_fflush(f); - } -} - -void qemu_put_buffer_async(QEMUFile *f, const uint8_t *buf, int size) -{ - if (!f->ops->writev_buffer) { - qemu_put_buffer(f, buf, size); - return; - } - - if (f->last_error) { - return; - } - - f->bytes_xfer += size; - add_to_iovec(f, buf, size); -} - -void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size) -{ - int l; - - if (f->last_error) { - return; - } - - while (size > 0) { - l = IO_BUF_SIZE - f->buf_index; - if (l > size) { - l = size; - } - memcpy(f->buf + f->buf_index, buf, l); - f->bytes_xfer += l; - if (f->ops->writev_buffer) { - add_to_iovec(f, f->buf + f->buf_index, l); - } - f->buf_index += l; - if (f->buf_index == IO_BUF_SIZE) { - qemu_fflush(f); - } - if (qemu_file_get_error(f)) { - break; - } - buf += l; - size -= l; - } -} - -void qemu_put_byte(QEMUFile *f, int v) -{ - if (f->last_error) { - return; - } - - f->buf[f->buf_index] = v; - f->bytes_xfer++; - if (f->ops->writev_buffer) { - add_to_iovec(f, f->buf + f->buf_index, 1); - } - f->buf_index++; - 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; - - assert(!qemu_file_is_writable(f)); - - 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; - - assert(!qemu_file_is_writable(f)); - - 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 */ |