migration/next for 20141015

-----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABCAAGBQJUPi6iAAoJEPSH7xhYctcjb0IP/iWbk5B+D/BR81x6AAM/4VBO
 McDUOzvQ4Hipo/HSvjWloC1YOOYdpd9XpRHEESULooXNrwJJIvz+TW1RJ4Lba2dt
 AmdPjGAGcvzTwgtOCFNPmvGGIxTO8s/6KRKQW51AxQEkfh8fPsx44ksfZdajK6id
 lizlXfP3gnwciaNZP4lmH5Nsq4sARQ9g/IIoulkL0itXRjBzlpLhjS4S/81mvZxg
 hW9thG8ml/mjLfNuKUKfRCDtmjzdlZClLWeFvZ3hbfR21mH5JaCt5YKjp4x6mimg
 TGfbzJOXQPbo5n/xLxBHGP+LrTgTQEJWgFTSDpS9pI3Z16+xP7mvNWlgSUxLPvEc
 9gXwRuEbXpiMOQzofi+SX4x7HhhUxR0hHVM4dNsR2d520B2EbuZlAn2hVOwUyOpf
 mAT+eCODHCuzJLH4s4Yx7RUFzLS0LVfBgOnqvUYEiPD6qYsj/tTdw3yIdS0ySASw
 0Abh0rqk6yUvDSQVZowVliudS5jOp8YQR/2WdYCEBH+r8Vk0i6Jm+MvScGDUOZmc
 TJCUgAQWRPZcUA4CnbbBiA3O3mQQxf6MzwCbqF2BcF5oMVutql7dnF6NFuN/wc8j
 AlFS2QE/5RX86RT4cjVQSfNk2oMNmmtjJRY4m7Ub0XacU4REznG1AgZf54cMTt1E
 B7TiocinEF0k9i/OTU8u
 =Rlo8
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/juanquintela/tags/migration/20141015' into staging

migration/next for 20141015

# gpg: Signature made Wed 15 Oct 2014 09:21:54 BST using RSA key ID 5872D723
# gpg: Can't check signature: public key not found

* remotes/juanquintela/tags/migration/20141015:
  migration: catch unknown flag combinations in ram_load
  qemu-file: Move stdio implementation to qemu-file-stdio.c
  qemu-file: Move unix and socket implementations to qemu-file-unix.c
  qemu-file: Use qemu_file_is_writable() on stdio_fclose()
  qemu-file: Make qemu_file_is_writable() non-static
  qemu-file: Add copyright header to qemu-file.c
  vmstate: Allow dynamic allocation for VBUFFER during migration
  block/migration: Disable cache invalidate for incoming migration
  Tests: QEMUSizedBuffer/QEMUBuffer
  QEMUSizedBuffer based QEMUFile

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2014-10-15 11:55:53 +01:00
commit 32d9c5613e
14 changed files with 1029 additions and 450 deletions

View File

@ -50,7 +50,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-y += qemu-file.o qemu-file-unix.o qemu-file-stdio.o
common-obj-$(CONFIG_RDMA) += migration-rdma.o
common-obj-y += qemu-char.o #aio.o
common-obj-y += block-migration.o

View File

@ -1040,8 +1040,7 @@ void ram_handle_compressed(void *host, uint8_t ch, uint64_t size)
static int ram_load(QEMUFile *f, void *opaque, int version_id)
{
ram_addr_t addr;
int flags, ret = 0;
int flags = 0, ret = 0;
static uint64_t seq_iter;
seq_iter++;
@ -1050,21 +1049,24 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id)
ret = -EINVAL;
}
while (!ret) {
addr = qemu_get_be64(f);
while (!ret && !(flags & RAM_SAVE_FLAG_EOS)) {
ram_addr_t addr, total_ram_bytes;
void *host;
uint8_t ch;
addr = qemu_get_be64(f);
flags = addr & ~TARGET_PAGE_MASK;
addr &= TARGET_PAGE_MASK;
if (flags & RAM_SAVE_FLAG_MEM_SIZE) {
switch (flags & ~RAM_SAVE_FLAG_CONTINUE) {
case RAM_SAVE_FLAG_MEM_SIZE:
/* Synchronize RAM block list */
char id[256];
ram_addr_t length;
ram_addr_t total_ram_bytes = addr;
while (total_ram_bytes) {
total_ram_bytes = addr;
while (!ret && total_ram_bytes) {
RAMBlock *block;
uint8_t len;
char id[256];
ram_addr_t length;
len = qemu_get_byte(f);
qemu_get_buffer(f, (uint8_t *)id, len);
@ -1088,16 +1090,11 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id)
"accept migration", id);
ret = -EINVAL;
}
if (ret) {
break;
}
total_ram_bytes -= length;
}
} else if (flags & RAM_SAVE_FLAG_COMPRESS) {
void *host;
uint8_t ch;
break;
case RAM_SAVE_FLAG_COMPRESS:
host = host_from_stream_offset(f, addr, flags);
if (!host) {
error_report("Illegal RAM offset " RAM_ADDR_FMT, addr);
@ -1107,9 +1104,8 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id)
ch = qemu_get_byte(f);
ram_handle_compressed(host, ch, TARGET_PAGE_SIZE);
} else if (flags & RAM_SAVE_FLAG_PAGE) {
void *host;
break;
case RAM_SAVE_FLAG_PAGE:
host = host_from_stream_offset(f, addr, flags);
if (!host) {
error_report("Illegal RAM offset " RAM_ADDR_FMT, addr);
@ -1118,8 +1114,9 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id)
}
qemu_get_buffer(f, host, TARGET_PAGE_SIZE);
} else if (flags & RAM_SAVE_FLAG_XBZRLE) {
void *host = host_from_stream_offset(f, addr, flags);
break;
case RAM_SAVE_FLAG_XBZRLE:
host = host_from_stream_offset(f, addr, flags);
if (!host) {
error_report("Illegal RAM offset " RAM_ADDR_FMT, addr);
ret = -EINVAL;
@ -1132,17 +1129,22 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id)
ret = -EINVAL;
break;
}
} else if (flags & RAM_SAVE_FLAG_HOOK) {
ram_control_load_hook(f, flags);
} else if (flags & RAM_SAVE_FLAG_EOS) {
break;
case RAM_SAVE_FLAG_EOS:
/* normal exit */
break;
} else {
error_report("Unknown migration flags: %#x", flags);
ret = -EINVAL;
break;
default:
if (flags & RAM_SAVE_FLAG_HOOK) {
ram_control_load_hook(f, flags);
} else {
error_report("Unknown combination of migration flags: %#x",
flags);
ret = -EINVAL;
}
}
if (!ret) {
ret = qemu_file_get_error(f);
}
ret = qemu_file_get_error(f);
}
DPRINTF("Completed load of VM with exit code %d seq iteration "

18
block.c
View File

@ -5043,6 +5043,11 @@ void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp)
return;
}
if (!(bs->open_flags & BDRV_O_INCOMING)) {
return;
}
bs->open_flags &= ~BDRV_O_INCOMING;
if (bs->drv->bdrv_invalidate_cache) {
bs->drv->bdrv_invalidate_cache(bs, &local_err);
} else if (bs->file) {
@ -5078,19 +5083,6 @@ void bdrv_invalidate_cache_all(Error **errp)
}
}
void bdrv_clear_incoming_migration_all(void)
{
BlockDriverState *bs;
QTAILQ_FOREACH(bs, &bdrv_states, device_list) {
AioContext *aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(aio_context);
bs->open_flags = bs->open_flags & ~(BDRV_O_INCOMING);
aio_context_release(aio_context);
}
}
int bdrv_flush(BlockDriverState *bs)
{
Coroutine *co;

View File

@ -25,6 +25,8 @@
#define QEMU_FILE_H 1
#include "exec/cpu-common.h"
#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.
@ -94,11 +96,19 @@ typedef struct QEMUFileOps {
QEMURamSaveFunc *save_page;
} QEMUFileOps;
struct QEMUSizedBuffer {
struct iovec *iov;
size_t n_iov;
size_t size; /* total allocated size in all iov's */
size_t used; /* number of used bytes */
};
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);
QEMUFile *qemu_fopen_socket(int fd, const char *mode);
QEMUFile *qemu_popen_cmd(const char *command, const char *mode);
QEMUFile *qemu_bufopen(const char *mode, QEMUSizedBuffer *input);
int qemu_get_fd(QEMUFile *f);
int qemu_fclose(QEMUFile *f);
int64_t qemu_ftell(QEMUFile *f);
@ -110,6 +120,23 @@ void qemu_put_byte(QEMUFile *f, int v);
*/
void qemu_put_buffer_async(QEMUFile *f, const uint8_t *buf, int size);
bool qemu_file_mode_is_not_valid(const char *mode);
bool qemu_file_is_writable(QEMUFile *f);
QEMUSizedBuffer *qsb_create(const uint8_t *buffer, size_t len);
QEMUSizedBuffer *qsb_clone(const QEMUSizedBuffer *);
void qsb_free(QEMUSizedBuffer *);
size_t qsb_set_length(QEMUSizedBuffer *qsb, size_t length);
size_t qsb_get_length(const QEMUSizedBuffer *qsb);
ssize_t qsb_get_buffer(const QEMUSizedBuffer *, off_t start, size_t count,
uint8_t *buf);
ssize_t qsb_write_at(QEMUSizedBuffer *qsb, const uint8_t *buf,
off_t pos, size_t count);
/*
* For use on files opened with qemu_bufopen
*/
const QEMUSizedBuffer *qemu_buf_get(QEMUFile *f);
static inline void qemu_put_ubyte(QEMUFile *f, unsigned int v)
{

View File

@ -484,6 +484,17 @@ extern const VMStateInfo vmstate_info_bitmap;
.start = (_start), \
}
#define VMSTATE_VBUFFER_ALLOC_UINT32(_field, _state, _version, _test, _start, _field_size) { \
.name = (stringify(_field)), \
.version_id = (_version), \
.field_exists = (_test), \
.size_offset = vmstate_offset_value(_state, _field_size, uint32_t),\
.info = &vmstate_info_buffer, \
.flags = VMS_VBUFFER|VMS_POINTER|VMS_ALLOC, \
.offset = offsetof(_state, _field), \
.start = (_start), \
}
#define VMSTATE_BUFFER_UNSAFE_INFO(_field, _state, _version, _info, _size) { \
.name = (stringify(_field)), \
.version_id = (_version), \

View File

@ -71,6 +71,7 @@ typedef struct SSIBus SSIBus;
typedef struct EventNotifier EventNotifier;
typedef struct VirtIODevice VirtIODevice;
typedef struct QEMUSGList QEMUSGList;
typedef struct QEMUSizedBuffer QEMUSizedBuffer;
typedef struct SHPCDevice SHPCDevice;
typedef struct FWCfgState FWCfgState;
typedef struct PcGuestInfo PcGuestInfo;

View File

@ -103,7 +103,6 @@ static void process_incoming_migration_co(void *opaque)
}
qemu_announce_self();
bdrv_clear_incoming_migration_all();
/* Make sure all file formats flush their mutable metadata */
bdrv_invalidate_cache_all(&local_err);
if (local_err) {

6
nbd.c
View File

@ -972,6 +972,12 @@ NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset,
exp->ctx = bdrv_get_aio_context(bs);
bdrv_ref(bs);
bdrv_add_aio_context_notifier(bs, bs_aio_attached, bs_aio_detach, exp);
/*
* NBD exports are used for non-shared storage migration. Make sure
* that BDRV_O_INCOMING is cleared and the image is ready for write
* access since the export could be available before migration handover.
*/
bdrv_invalidate_cache(bs, NULL);
return exp;
}

194
qemu-file-stdio.c Normal file
View File

@ -0,0 +1,194 @@
/*
* 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 "block/coroutine.h"
#include "migration/qemu-file.h"
typedef struct QEMUFileStdio {
FILE *stdio_file;
QEMUFile *file;
} QEMUFileStdio;
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;
int res;
res = fwrite(buf, 1, size, s->stdio_file);
if (res != size) {
return -errno;
}
return res;
}
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 (qemu_file_is_writable(s->file)) {
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
};
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;
}

223
qemu-file-unix.c Normal file
View File

@ -0,0 +1,223 @@
/*
* 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 "qemu/iov.h"
#include "qemu/sockets.h"
#include "block/coroutine.h"
#include "migration/qemu-file.h"
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 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
};
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;
}

View File

@ -1,3 +1,26 @@
/*
* 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 "qemu/iov.h"
#include "qemu/sockets.h"
@ -28,324 +51,6 @@ struct QEMUFile {
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;
int res;
res = fwrite(buf, 1, size, s->stdio_file);
if (res != size) {
return -errno;
}
return res;
}
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 ||
@ -358,51 +63,6 @@ bool qemu_file_mode_is_not_valid(const char *mode)
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;
@ -433,7 +93,7 @@ void qemu_file_set_error(QEMUFile *f, int ret)
}
}
static inline bool qemu_file_is_writable(QEMUFile *f)
bool qemu_file_is_writable(QEMUFile *f)
{
return f->ops->writev_buffer || f->ops->put_buffer;
}
@ -878,3 +538,458 @@ uint64_t qemu_get_be64(QEMUFile *f)
v |= qemu_get_be32(f);
return v;
}
#define QSB_CHUNK_SIZE (1 << 10)
#define QSB_MAX_CHUNK_SIZE (16 * QSB_CHUNK_SIZE)
/**
* Create a QEMUSizedBuffer
* This type of buffer uses scatter-gather lists internally and
* can grow to any size. Any data array in the scatter-gather list
* can hold different amount of bytes.
*
* @buffer: Optional buffer to copy into the QSB
* @len: size of initial buffer; if @buffer is given, buffer must
* hold at least len bytes
*
* Returns a pointer to a QEMUSizedBuffer or NULL on allocation failure
*/
QEMUSizedBuffer *qsb_create(const uint8_t *buffer, size_t len)
{
QEMUSizedBuffer *qsb;
size_t alloc_len, num_chunks, i, to_copy;
size_t chunk_size = (len > QSB_MAX_CHUNK_SIZE)
? QSB_MAX_CHUNK_SIZE
: QSB_CHUNK_SIZE;
num_chunks = DIV_ROUND_UP(len ? len : QSB_CHUNK_SIZE, chunk_size);
alloc_len = num_chunks * chunk_size;
qsb = g_try_new0(QEMUSizedBuffer, 1);
if (!qsb) {
return NULL;
}
qsb->iov = g_try_new0(struct iovec, num_chunks);
if (!qsb->iov) {
g_free(qsb);
return NULL;
}
qsb->n_iov = num_chunks;
for (i = 0; i < num_chunks; i++) {
qsb->iov[i].iov_base = g_try_malloc0(chunk_size);
if (!qsb->iov[i].iov_base) {
/* qsb_free is safe since g_free can cope with NULL */
qsb_free(qsb);
return NULL;
}
qsb->iov[i].iov_len = chunk_size;
if (buffer) {
to_copy = (len - qsb->used) > chunk_size
? chunk_size : (len - qsb->used);
memcpy(qsb->iov[i].iov_base, &buffer[qsb->used], to_copy);
qsb->used += to_copy;
}
}
qsb->size = alloc_len;
return qsb;
}
/**
* Free the QEMUSizedBuffer
*
* @qsb: The QEMUSizedBuffer to free
*/
void qsb_free(QEMUSizedBuffer *qsb)
{
size_t i;
if (!qsb) {
return;
}
for (i = 0; i < qsb->n_iov; i++) {
g_free(qsb->iov[i].iov_base);
}
g_free(qsb->iov);
g_free(qsb);
}
/**
* Get the number of used bytes in the QEMUSizedBuffer
*
* @qsb: A QEMUSizedBuffer
*
* Returns the number of bytes currently used in this buffer
*/
size_t qsb_get_length(const QEMUSizedBuffer *qsb)
{
return qsb->used;
}
/**
* Set the length of the buffer; the primary usage of this
* function is to truncate the number of used bytes in the buffer.
* The size will not be extended beyond the current number of
* allocated bytes in the QEMUSizedBuffer.
*
* @qsb: A QEMUSizedBuffer
* @new_len: The new length of bytes in the buffer
*
* Returns the number of bytes the buffer was truncated or extended
* to.
*/
size_t qsb_set_length(QEMUSizedBuffer *qsb, size_t new_len)
{
if (new_len <= qsb->size) {
qsb->used = new_len;
} else {
qsb->used = qsb->size;
}
return qsb->used;
}
/**
* Get the iovec that holds the data for a given position @pos.
*
* @qsb: A QEMUSizedBuffer
* @pos: The index of a byte in the buffer
* @d_off: Pointer to an offset that this function will indicate
* at what position within the returned iovec the byte
* is to be found
*
* Returns the index of the iovec that holds the byte at the given
* index @pos in the byte stream; a negative number if the iovec
* for the given position @pos does not exist.
*/
static ssize_t qsb_get_iovec(const QEMUSizedBuffer *qsb,
off_t pos, off_t *d_off)
{
ssize_t i;
off_t curr = 0;
if (pos > qsb->used) {
return -1;
}
for (i = 0; i < qsb->n_iov; i++) {
if (curr + qsb->iov[i].iov_len > pos) {
*d_off = pos - curr;
return i;
}
curr += qsb->iov[i].iov_len;
}
return -1;
}
/*
* Convert the QEMUSizedBuffer into a flat buffer.
*
* Note: If at all possible, try to avoid this function since it
* may unnecessarily copy memory around.
*
* @qsb: pointer to QEMUSizedBuffer
* @start: offset to start at
* @count: number of bytes to copy
* @buf: a pointer to a buffer to write into (at least @count bytes)
*
* Returns the number of bytes copied into the output buffer
*/
ssize_t qsb_get_buffer(const QEMUSizedBuffer *qsb, off_t start,
size_t count, uint8_t *buffer)
{
const struct iovec *iov;
size_t to_copy, all_copy;
ssize_t index;
off_t s_off;
off_t d_off = 0;
char *s;
if (start > qsb->used) {
return 0;
}
all_copy = qsb->used - start;
if (all_copy > count) {
all_copy = count;
} else {
count = all_copy;
}
index = qsb_get_iovec(qsb, start, &s_off);
if (index < 0) {
return 0;
}
while (all_copy > 0) {
iov = &qsb->iov[index];
s = iov->iov_base;
to_copy = iov->iov_len - s_off;
if (to_copy > all_copy) {
to_copy = all_copy;
}
memcpy(&buffer[d_off], &s[s_off], to_copy);
d_off += to_copy;
all_copy -= to_copy;
s_off = 0;
index++;
}
return count;
}
/**
* Grow the QEMUSizedBuffer to the given size and allocate
* memory for it.
*
* @qsb: A QEMUSizedBuffer
* @new_size: The new size of the buffer
*
* Return:
* a negative error code in case of memory allocation failure
* or
* the new size of the buffer. The returned size may be greater or equal
* to @new_size.
*/
static ssize_t qsb_grow(QEMUSizedBuffer *qsb, size_t new_size)
{
size_t needed_chunks, i;
if (qsb->size < new_size) {
struct iovec *new_iov;
size_t size_diff = new_size - qsb->size;
size_t chunk_size = (size_diff > QSB_MAX_CHUNK_SIZE)
? QSB_MAX_CHUNK_SIZE : QSB_CHUNK_SIZE;
needed_chunks = DIV_ROUND_UP(size_diff, chunk_size);
new_iov = g_try_new(struct iovec, qsb->n_iov + needed_chunks);
if (new_iov == NULL) {
return -ENOMEM;
}
/* Allocate new chunks as needed into new_iov */
for (i = qsb->n_iov; i < qsb->n_iov + needed_chunks; i++) {
new_iov[i].iov_base = g_try_malloc0(chunk_size);
new_iov[i].iov_len = chunk_size;
if (!new_iov[i].iov_base) {
size_t j;
/* Free previously allocated new chunks */
for (j = qsb->n_iov; j < i; j++) {
g_free(new_iov[j].iov_base);
}
g_free(new_iov);
return -ENOMEM;
}
}
/*
* Now we can't get any allocation errors, copy over to new iov
* and switch.
*/
for (i = 0; i < qsb->n_iov; i++) {
new_iov[i] = qsb->iov[i];
}
qsb->n_iov += needed_chunks;
g_free(qsb->iov);
qsb->iov = new_iov;
qsb->size += (needed_chunks * chunk_size);
}
return qsb->size;
}
/**
* Write into the QEMUSizedBuffer at a given position and a given
* number of bytes. This function will automatically grow the
* QEMUSizedBuffer.
*
* @qsb: A QEMUSizedBuffer
* @source: A byte array to copy data from
* @pos: The position within the @qsb to write data to
* @size: The number of bytes to copy into the @qsb
*
* Returns @size or a negative error code in case of memory allocation failure,
* or with an invalid 'pos'
*/
ssize_t qsb_write_at(QEMUSizedBuffer *qsb, const uint8_t *source,
off_t pos, size_t count)
{
ssize_t rc = qsb_grow(qsb, pos + count);
size_t to_copy;
size_t all_copy = count;
const struct iovec *iov;
ssize_t index;
char *dest;
off_t d_off, s_off = 0;
if (rc < 0) {
return rc;
}
if (pos + count > qsb->used) {
qsb->used = pos + count;
}
index = qsb_get_iovec(qsb, pos, &d_off);
if (index < 0) {
return -EINVAL;
}
while (all_copy > 0) {
iov = &qsb->iov[index];
dest = iov->iov_base;
to_copy = iov->iov_len - d_off;
if (to_copy > all_copy) {
to_copy = all_copy;
}
memcpy(&dest[d_off], &source[s_off], to_copy);
s_off += to_copy;
all_copy -= to_copy;
d_off = 0;
index++;
}
return count;
}
/**
* Create a deep copy of the given QEMUSizedBuffer.
*
* @qsb: A QEMUSizedBuffer
*
* Returns a clone of @qsb or NULL on allocation failure
*/
QEMUSizedBuffer *qsb_clone(const QEMUSizedBuffer *qsb)
{
QEMUSizedBuffer *out = qsb_create(NULL, qsb_get_length(qsb));
size_t i;
ssize_t res;
off_t pos = 0;
if (!out) {
return NULL;
}
for (i = 0; i < qsb->n_iov; i++) {
res = qsb_write_at(out, qsb->iov[i].iov_base,
pos, qsb->iov[i].iov_len);
if (res < 0) {
qsb_free(out);
return NULL;
}
pos += res;
}
return out;
}
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;
}
return qsb_get_buffer(s->qsb, pos, len, buf);
}
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 = 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') {
error_report("qemu_bufopen: Argument validity check failed");
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 (!s->qsb) {
g_free(s);
error_report("qemu_bufopen: qsb_create failed");
return NULL;
}
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;
}

View File

@ -258,8 +258,8 @@ tests/test-qdev-global-props$(EXESUF): tests/test-qdev-global-props.o \
$(test-qapi-obj-y) \
libqemuutil.a libqemustub.a
tests/test-vmstate$(EXESUF): tests/test-vmstate.o \
vmstate.o qemu-file.o \
libqemuutil.a
vmstate.o qemu-file.o qemu-file-unix.o \
libqemuutil.a libqemustub.a
tests/test-qapi-types.c tests/test-qapi-types.h :\
$(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-types.py

View File

@ -43,6 +43,12 @@ void yield_until_fd_readable(int fd)
select(fd + 1, &fds, NULL, NULL, NULL);
}
/*
* Some tests use 'open_test_file' to work on a real fd, some use
* an in memory file (QEMUSizedBuffer+qemu_bufopen); we could pick one
* but this way we test both.
*/
/* Duplicate temp_fd and seek to the beginning of the file */
static QEMUFile *open_test_file(bool write)
{
@ -54,6 +60,30 @@ static QEMUFile *open_test_file(bool write)
return qemu_fdopen(fd, write ? "wb" : "rb");
}
/* Open a read-only qemu-file from an existing memory block */
static QEMUFile *open_mem_file_read(const void *data, size_t len)
{
/* The qsb gets freed by qemu_fclose */
QEMUSizedBuffer *qsb = qsb_create(data, len);
g_assert(qsb);
return qemu_bufopen("r", qsb);
}
/*
* Check that the contents of the memory-buffered file f match
* the given size/data.
*/
static void check_mem_file(QEMUFile *f, void *data, size_t size)
{
uint8_t *result = g_malloc(size);
const QEMUSizedBuffer *qsb = qemu_buf_get(f);
g_assert_cmpint(qsb_get_length(qsb), ==, size);
g_assert_cmpint(qsb_get_buffer(qsb, 0, size, result), ==, size);
g_assert_cmpint(memcmp(result, data, size), ==, 0);
g_free(result);
}
#define SUCCESS(val) \
g_assert_cmpint((val), ==, 0)
@ -371,14 +401,12 @@ static const VMStateDescription vmstate_skipping = {
static void test_save_noskip(void)
{
QEMUFile *fsave = open_test_file(true);
QEMUFile *fsave = qemu_bufopen("w", NULL);
TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6,
.skip_c_e = false };
vmstate_save_state(fsave, &vmstate_skipping, &obj);
g_assert(!qemu_file_get_error(fsave));
qemu_fclose(fsave);
QEMUFile *loading = open_test_file(false);
uint8_t expected[] = {
0, 0, 0, 1, /* a */
0, 0, 0, 2, /* b */
@ -387,52 +415,31 @@ static void test_save_noskip(void)
0, 0, 0, 5, /* e */
0, 0, 0, 0, 0, 0, 0, 6, /* f */
};
uint8_t result[sizeof(expected)];
g_assert_cmpint(qemu_get_buffer(loading, result, sizeof(result)), ==,
sizeof(result));
g_assert(!qemu_file_get_error(loading));
g_assert_cmpint(memcmp(result, expected, sizeof(result)), ==, 0);
/* Must reach EOF */
qemu_get_byte(loading);
g_assert_cmpint(qemu_file_get_error(loading), ==, -EIO);
qemu_fclose(loading);
check_mem_file(fsave, expected, sizeof(expected));
qemu_fclose(fsave);
}
static void test_save_skip(void)
{
QEMUFile *fsave = open_test_file(true);
QEMUFile *fsave = qemu_bufopen("w", NULL);
TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6,
.skip_c_e = true };
vmstate_save_state(fsave, &vmstate_skipping, &obj);
g_assert(!qemu_file_get_error(fsave));
qemu_fclose(fsave);
QEMUFile *loading = open_test_file(false);
uint8_t expected[] = {
0, 0, 0, 1, /* a */
0, 0, 0, 2, /* b */
0, 0, 0, 0, 0, 0, 0, 4, /* d */
0, 0, 0, 0, 0, 0, 0, 6, /* f */
};
uint8_t result[sizeof(expected)];
g_assert_cmpint(qemu_get_buffer(loading, result, sizeof(result)), ==,
sizeof(result));
g_assert(!qemu_file_get_error(loading));
g_assert_cmpint(memcmp(result, expected, sizeof(result)), ==, 0);
check_mem_file(fsave, expected, sizeof(expected));
/* Must reach EOF */
qemu_get_byte(loading);
g_assert_cmpint(qemu_file_get_error(loading), ==, -EIO);
qemu_fclose(loading);
qemu_fclose(fsave);
}
static void test_load_noskip(void)
{
QEMUFile *fsave = open_test_file(true);
uint8_t buf[] = {
0, 0, 0, 10, /* a */
0, 0, 0, 20, /* b */
@ -442,10 +449,8 @@ static void test_load_noskip(void)
0, 0, 0, 0, 0, 0, 0, 60, /* f */
QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */
};
qemu_put_buffer(fsave, buf, sizeof(buf));
qemu_fclose(fsave);
QEMUFile *loading = open_test_file(false);
QEMUFile *loading = open_mem_file_read(buf, sizeof(buf));
TestStruct obj = { .skip_c_e = false };
vmstate_load_state(loading, &vmstate_skipping, &obj, 2);
g_assert(!qemu_file_get_error(loading));
@ -460,7 +465,6 @@ static void test_load_noskip(void)
static void test_load_skip(void)
{
QEMUFile *fsave = open_test_file(true);
uint8_t buf[] = {
0, 0, 0, 10, /* a */
0, 0, 0, 20, /* b */
@ -468,10 +472,8 @@ static void test_load_skip(void)
0, 0, 0, 0, 0, 0, 0, 60, /* f */
QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */
};
qemu_put_buffer(fsave, buf, sizeof(buf));
qemu_fclose(fsave);
QEMUFile *loading = open_test_file(false);
QEMUFile *loading = open_mem_file_read(buf, sizeof(buf));
TestStruct obj = { .skip_c_e = true, .c = 300, .e = 500 };
vmstate_load_state(loading, &vmstate_skipping, &obj, 2);
g_assert(!qemu_file_get_error(loading));

View File

@ -49,9 +49,16 @@ static void *vmstate_base_addr(void *opaque, VMStateField *field, bool alloc)
if (field->flags & VMS_POINTER) {
if (alloc && (field->flags & VMS_ALLOC)) {
int n_elems = vmstate_n_elems(opaque, field);
if (n_elems) {
gsize size = n_elems * field->size;
gsize size = 0;
if (field->flags & VMS_VBUFFER) {
size = vmstate_size(opaque, field);
} else {
int n_elems = vmstate_n_elems(opaque, field);
if (n_elems) {
size = n_elems * field->size;
}
}
if (size) {
*((void **)base_addr + field->start) = g_malloc(size);
}
}