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:
commit
32d9c5613e
|
@ -50,7 +50,7 @@ common-obj-$(CONFIG_LINUX) += fsdev/
|
||||||
|
|
||||||
common-obj-y += migration.o migration-tcp.o
|
common-obj-y += migration.o migration-tcp.o
|
||||||
common-obj-y += vmstate.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-$(CONFIG_RDMA) += migration-rdma.o
|
||||||
common-obj-y += qemu-char.o #aio.o
|
common-obj-y += qemu-char.o #aio.o
|
||||||
common-obj-y += block-migration.o
|
common-obj-y += block-migration.o
|
||||||
|
|
56
arch_init.c
56
arch_init.c
|
@ -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)
|
static int ram_load(QEMUFile *f, void *opaque, int version_id)
|
||||||
{
|
{
|
||||||
ram_addr_t addr;
|
int flags = 0, ret = 0;
|
||||||
int flags, ret = 0;
|
|
||||||
static uint64_t seq_iter;
|
static uint64_t seq_iter;
|
||||||
|
|
||||||
seq_iter++;
|
seq_iter++;
|
||||||
|
@ -1050,21 +1049,24 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id)
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (!ret) {
|
while (!ret && !(flags & RAM_SAVE_FLAG_EOS)) {
|
||||||
addr = qemu_get_be64(f);
|
ram_addr_t addr, total_ram_bytes;
|
||||||
|
void *host;
|
||||||
|
uint8_t ch;
|
||||||
|
|
||||||
|
addr = qemu_get_be64(f);
|
||||||
flags = addr & ~TARGET_PAGE_MASK;
|
flags = addr & ~TARGET_PAGE_MASK;
|
||||||
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 */
|
/* Synchronize RAM block list */
|
||||||
char id[256];
|
total_ram_bytes = addr;
|
||||||
ram_addr_t length;
|
while (!ret && total_ram_bytes) {
|
||||||
ram_addr_t total_ram_bytes = addr;
|
|
||||||
|
|
||||||
while (total_ram_bytes) {
|
|
||||||
RAMBlock *block;
|
RAMBlock *block;
|
||||||
uint8_t len;
|
uint8_t len;
|
||||||
|
char id[256];
|
||||||
|
ram_addr_t length;
|
||||||
|
|
||||||
len = qemu_get_byte(f);
|
len = qemu_get_byte(f);
|
||||||
qemu_get_buffer(f, (uint8_t *)id, len);
|
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);
|
"accept migration", id);
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
}
|
}
|
||||||
if (ret) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
total_ram_bytes -= length;
|
total_ram_bytes -= length;
|
||||||
}
|
}
|
||||||
} else if (flags & RAM_SAVE_FLAG_COMPRESS) {
|
break;
|
||||||
void *host;
|
case RAM_SAVE_FLAG_COMPRESS:
|
||||||
uint8_t ch;
|
|
||||||
|
|
||||||
host = host_from_stream_offset(f, addr, flags);
|
host = host_from_stream_offset(f, addr, flags);
|
||||||
if (!host) {
|
if (!host) {
|
||||||
error_report("Illegal RAM offset " RAM_ADDR_FMT, addr);
|
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);
|
ch = qemu_get_byte(f);
|
||||||
ram_handle_compressed(host, ch, TARGET_PAGE_SIZE);
|
ram_handle_compressed(host, ch, TARGET_PAGE_SIZE);
|
||||||
} else if (flags & RAM_SAVE_FLAG_PAGE) {
|
break;
|
||||||
void *host;
|
case RAM_SAVE_FLAG_PAGE:
|
||||||
|
|
||||||
host = host_from_stream_offset(f, addr, flags);
|
host = host_from_stream_offset(f, addr, flags);
|
||||||
if (!host) {
|
if (!host) {
|
||||||
error_report("Illegal RAM offset " RAM_ADDR_FMT, addr);
|
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);
|
qemu_get_buffer(f, host, TARGET_PAGE_SIZE);
|
||||||
} else if (flags & RAM_SAVE_FLAG_XBZRLE) {
|
break;
|
||||||
void *host = host_from_stream_offset(f, addr, flags);
|
case RAM_SAVE_FLAG_XBZRLE:
|
||||||
|
host = host_from_stream_offset(f, addr, flags);
|
||||||
if (!host) {
|
if (!host) {
|
||||||
error_report("Illegal RAM offset " RAM_ADDR_FMT, addr);
|
error_report("Illegal RAM offset " RAM_ADDR_FMT, addr);
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
|
@ -1132,18 +1129,23 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id)
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else if (flags & RAM_SAVE_FLAG_HOOK) {
|
break;
|
||||||
ram_control_load_hook(f, flags);
|
case RAM_SAVE_FLAG_EOS:
|
||||||
} else if (flags & RAM_SAVE_FLAG_EOS) {
|
|
||||||
/* normal exit */
|
/* normal exit */
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
if (flags & RAM_SAVE_FLAG_HOOK) {
|
||||||
|
ram_control_load_hook(f, flags);
|
||||||
} else {
|
} else {
|
||||||
error_report("Unknown migration flags: %#x", flags);
|
error_report("Unknown combination of migration flags: %#x",
|
||||||
|
flags);
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
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 "
|
DPRINTF("Completed load of VM with exit code %d seq iteration "
|
||||||
"%" PRIu64 "\n", ret, seq_iter);
|
"%" PRIu64 "\n", ret, seq_iter);
|
||||||
|
|
18
block.c
18
block.c
|
@ -5043,6 +5043,11 @@ void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!(bs->open_flags & BDRV_O_INCOMING)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bs->open_flags &= ~BDRV_O_INCOMING;
|
||||||
|
|
||||||
if (bs->drv->bdrv_invalidate_cache) {
|
if (bs->drv->bdrv_invalidate_cache) {
|
||||||
bs->drv->bdrv_invalidate_cache(bs, &local_err);
|
bs->drv->bdrv_invalidate_cache(bs, &local_err);
|
||||||
} else if (bs->file) {
|
} 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)
|
int bdrv_flush(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
Coroutine *co;
|
Coroutine *co;
|
||||||
|
|
|
@ -25,6 +25,8 @@
|
||||||
#define QEMU_FILE_H 1
|
#define QEMU_FILE_H 1
|
||||||
#include "exec/cpu-common.h"
|
#include "exec/cpu-common.h"
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
/* This function writes a chunk of data to a file at the given position.
|
/* 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
|
* 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.
|
* streaming. The handler should try to write all of the data it can.
|
||||||
|
@ -94,11 +96,19 @@ typedef struct QEMUFileOps {
|
||||||
QEMURamSaveFunc *save_page;
|
QEMURamSaveFunc *save_page;
|
||||||
} QEMUFileOps;
|
} 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_ops(void *opaque, const QEMUFileOps *ops);
|
||||||
QEMUFile *qemu_fopen(const char *filename, const char *mode);
|
QEMUFile *qemu_fopen(const char *filename, const char *mode);
|
||||||
QEMUFile *qemu_fdopen(int fd, const char *mode);
|
QEMUFile *qemu_fdopen(int fd, const char *mode);
|
||||||
QEMUFile *qemu_fopen_socket(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_popen_cmd(const char *command, const char *mode);
|
||||||
|
QEMUFile *qemu_bufopen(const char *mode, QEMUSizedBuffer *input);
|
||||||
int qemu_get_fd(QEMUFile *f);
|
int qemu_get_fd(QEMUFile *f);
|
||||||
int qemu_fclose(QEMUFile *f);
|
int qemu_fclose(QEMUFile *f);
|
||||||
int64_t qemu_ftell(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);
|
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_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)
|
static inline void qemu_put_ubyte(QEMUFile *f, unsigned int v)
|
||||||
{
|
{
|
||||||
|
|
|
@ -484,6 +484,17 @@ extern const VMStateInfo vmstate_info_bitmap;
|
||||||
.start = (_start), \
|
.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) { \
|
#define VMSTATE_BUFFER_UNSAFE_INFO(_field, _state, _version, _info, _size) { \
|
||||||
.name = (stringify(_field)), \
|
.name = (stringify(_field)), \
|
||||||
.version_id = (_version), \
|
.version_id = (_version), \
|
||||||
|
|
|
@ -71,6 +71,7 @@ typedef struct SSIBus SSIBus;
|
||||||
typedef struct EventNotifier EventNotifier;
|
typedef struct EventNotifier EventNotifier;
|
||||||
typedef struct VirtIODevice VirtIODevice;
|
typedef struct VirtIODevice VirtIODevice;
|
||||||
typedef struct QEMUSGList QEMUSGList;
|
typedef struct QEMUSGList QEMUSGList;
|
||||||
|
typedef struct QEMUSizedBuffer QEMUSizedBuffer;
|
||||||
typedef struct SHPCDevice SHPCDevice;
|
typedef struct SHPCDevice SHPCDevice;
|
||||||
typedef struct FWCfgState FWCfgState;
|
typedef struct FWCfgState FWCfgState;
|
||||||
typedef struct PcGuestInfo PcGuestInfo;
|
typedef struct PcGuestInfo PcGuestInfo;
|
||||||
|
|
|
@ -103,7 +103,6 @@ static void process_incoming_migration_co(void *opaque)
|
||||||
}
|
}
|
||||||
qemu_announce_self();
|
qemu_announce_self();
|
||||||
|
|
||||||
bdrv_clear_incoming_migration_all();
|
|
||||||
/* Make sure all file formats flush their mutable metadata */
|
/* Make sure all file formats flush their mutable metadata */
|
||||||
bdrv_invalidate_cache_all(&local_err);
|
bdrv_invalidate_cache_all(&local_err);
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
|
|
6
nbd.c
6
nbd.c
|
@ -972,6 +972,12 @@ NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset,
|
||||||
exp->ctx = bdrv_get_aio_context(bs);
|
exp->ctx = bdrv_get_aio_context(bs);
|
||||||
bdrv_ref(bs);
|
bdrv_ref(bs);
|
||||||
bdrv_add_aio_context_notifier(bs, bs_aio_attached, bs_aio_detach, exp);
|
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;
|
return exp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
194
qemu-file-stdio.c
Normal file
194
qemu-file-stdio.c
Normal 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
223
qemu-file-unix.c
Normal 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;
|
||||||
|
}
|
843
qemu-file.c
843
qemu-file.c
|
@ -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-common.h"
|
||||||
#include "qemu/iov.h"
|
#include "qemu/iov.h"
|
||||||
#include "qemu/sockets.h"
|
#include "qemu/sockets.h"
|
||||||
|
@ -28,324 +51,6 @@ struct QEMUFile {
|
||||||
int last_error;
|
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)
|
bool qemu_file_mode_is_not_valid(const char *mode)
|
||||||
{
|
{
|
||||||
if (mode == NULL ||
|
if (mode == NULL ||
|
||||||
|
@ -358,51 +63,6 @@ bool qemu_file_mode_is_not_valid(const char *mode)
|
||||||
return false;
|
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 *qemu_fopen_ops(void *opaque, const QEMUFileOps *ops)
|
||||||
{
|
{
|
||||||
QEMUFile *f;
|
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;
|
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);
|
v |= qemu_get_be32(f);
|
||||||
return v;
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -258,8 +258,8 @@ tests/test-qdev-global-props$(EXESUF): tests/test-qdev-global-props.o \
|
||||||
$(test-qapi-obj-y) \
|
$(test-qapi-obj-y) \
|
||||||
libqemuutil.a libqemustub.a
|
libqemuutil.a libqemustub.a
|
||||||
tests/test-vmstate$(EXESUF): tests/test-vmstate.o \
|
tests/test-vmstate$(EXESUF): tests/test-vmstate.o \
|
||||||
vmstate.o qemu-file.o \
|
vmstate.o qemu-file.o qemu-file-unix.o \
|
||||||
libqemuutil.a
|
libqemuutil.a libqemustub.a
|
||||||
|
|
||||||
tests/test-qapi-types.c tests/test-qapi-types.h :\
|
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
|
$(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-types.py
|
||||||
|
|
|
@ -43,6 +43,12 @@ void yield_until_fd_readable(int fd)
|
||||||
select(fd + 1, &fds, NULL, NULL, NULL);
|
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 */
|
/* Duplicate temp_fd and seek to the beginning of the file */
|
||||||
static QEMUFile *open_test_file(bool write)
|
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");
|
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) \
|
#define SUCCESS(val) \
|
||||||
g_assert_cmpint((val), ==, 0)
|
g_assert_cmpint((val), ==, 0)
|
||||||
|
|
||||||
|
@ -371,14 +401,12 @@ static const VMStateDescription vmstate_skipping = {
|
||||||
|
|
||||||
static void test_save_noskip(void)
|
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,
|
TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6,
|
||||||
.skip_c_e = false };
|
.skip_c_e = false };
|
||||||
vmstate_save_state(fsave, &vmstate_skipping, &obj);
|
vmstate_save_state(fsave, &vmstate_skipping, &obj);
|
||||||
g_assert(!qemu_file_get_error(fsave));
|
g_assert(!qemu_file_get_error(fsave));
|
||||||
qemu_fclose(fsave);
|
|
||||||
|
|
||||||
QEMUFile *loading = open_test_file(false);
|
|
||||||
uint8_t expected[] = {
|
uint8_t expected[] = {
|
||||||
0, 0, 0, 1, /* a */
|
0, 0, 0, 1, /* a */
|
||||||
0, 0, 0, 2, /* b */
|
0, 0, 0, 2, /* b */
|
||||||
|
@ -387,52 +415,31 @@ static void test_save_noskip(void)
|
||||||
0, 0, 0, 5, /* e */
|
0, 0, 0, 5, /* e */
|
||||||
0, 0, 0, 0, 0, 0, 0, 6, /* f */
|
0, 0, 0, 0, 0, 0, 0, 6, /* f */
|
||||||
};
|
};
|
||||||
uint8_t result[sizeof(expected)];
|
check_mem_file(fsave, expected, sizeof(expected));
|
||||||
g_assert_cmpint(qemu_get_buffer(loading, result, sizeof(result)), ==,
|
qemu_fclose(fsave);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_save_skip(void)
|
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,
|
TestStruct obj = { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6,
|
||||||
.skip_c_e = true };
|
.skip_c_e = true };
|
||||||
vmstate_save_state(fsave, &vmstate_skipping, &obj);
|
vmstate_save_state(fsave, &vmstate_skipping, &obj);
|
||||||
g_assert(!qemu_file_get_error(fsave));
|
g_assert(!qemu_file_get_error(fsave));
|
||||||
qemu_fclose(fsave);
|
|
||||||
|
|
||||||
QEMUFile *loading = open_test_file(false);
|
|
||||||
uint8_t expected[] = {
|
uint8_t expected[] = {
|
||||||
0, 0, 0, 1, /* a */
|
0, 0, 0, 1, /* a */
|
||||||
0, 0, 0, 2, /* b */
|
0, 0, 0, 2, /* b */
|
||||||
0, 0, 0, 0, 0, 0, 0, 4, /* d */
|
0, 0, 0, 0, 0, 0, 0, 4, /* d */
|
||||||
0, 0, 0, 0, 0, 0, 0, 6, /* f */
|
0, 0, 0, 0, 0, 0, 0, 6, /* f */
|
||||||
};
|
};
|
||||||
uint8_t result[sizeof(expected)];
|
check_mem_file(fsave, expected, 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);
|
|
||||||
|
|
||||||
|
qemu_fclose(fsave);
|
||||||
/* Must reach EOF */
|
|
||||||
qemu_get_byte(loading);
|
|
||||||
g_assert_cmpint(qemu_file_get_error(loading), ==, -EIO);
|
|
||||||
|
|
||||||
qemu_fclose(loading);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_load_noskip(void)
|
static void test_load_noskip(void)
|
||||||
{
|
{
|
||||||
QEMUFile *fsave = open_test_file(true);
|
|
||||||
uint8_t buf[] = {
|
uint8_t buf[] = {
|
||||||
0, 0, 0, 10, /* a */
|
0, 0, 0, 10, /* a */
|
||||||
0, 0, 0, 20, /* b */
|
0, 0, 0, 20, /* b */
|
||||||
|
@ -442,10 +449,8 @@ static void test_load_noskip(void)
|
||||||
0, 0, 0, 0, 0, 0, 0, 60, /* f */
|
0, 0, 0, 0, 0, 0, 0, 60, /* f */
|
||||||
QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */
|
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 };
|
TestStruct obj = { .skip_c_e = false };
|
||||||
vmstate_load_state(loading, &vmstate_skipping, &obj, 2);
|
vmstate_load_state(loading, &vmstate_skipping, &obj, 2);
|
||||||
g_assert(!qemu_file_get_error(loading));
|
g_assert(!qemu_file_get_error(loading));
|
||||||
|
@ -460,7 +465,6 @@ static void test_load_noskip(void)
|
||||||
|
|
||||||
static void test_load_skip(void)
|
static void test_load_skip(void)
|
||||||
{
|
{
|
||||||
QEMUFile *fsave = open_test_file(true);
|
|
||||||
uint8_t buf[] = {
|
uint8_t buf[] = {
|
||||||
0, 0, 0, 10, /* a */
|
0, 0, 0, 10, /* a */
|
||||||
0, 0, 0, 20, /* b */
|
0, 0, 0, 20, /* b */
|
||||||
|
@ -468,10 +472,8 @@ static void test_load_skip(void)
|
||||||
0, 0, 0, 0, 0, 0, 0, 60, /* f */
|
0, 0, 0, 0, 0, 0, 0, 60, /* f */
|
||||||
QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */
|
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 };
|
TestStruct obj = { .skip_c_e = true, .c = 300, .e = 500 };
|
||||||
vmstate_load_state(loading, &vmstate_skipping, &obj, 2);
|
vmstate_load_state(loading, &vmstate_skipping, &obj, 2);
|
||||||
g_assert(!qemu_file_get_error(loading));
|
g_assert(!qemu_file_get_error(loading));
|
||||||
|
|
|
@ -49,9 +49,16 @@ static void *vmstate_base_addr(void *opaque, VMStateField *field, bool alloc)
|
||||||
|
|
||||||
if (field->flags & VMS_POINTER) {
|
if (field->flags & VMS_POINTER) {
|
||||||
if (alloc && (field->flags & VMS_ALLOC)) {
|
if (alloc && (field->flags & VMS_ALLOC)) {
|
||||||
|
gsize size = 0;
|
||||||
|
if (field->flags & VMS_VBUFFER) {
|
||||||
|
size = vmstate_size(opaque, field);
|
||||||
|
} else {
|
||||||
int n_elems = vmstate_n_elems(opaque, field);
|
int n_elems = vmstate_n_elems(opaque, field);
|
||||||
if (n_elems) {
|
if (n_elems) {
|
||||||
gsize size = n_elems * field->size;
|
size = n_elems * field->size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (size) {
|
||||||
*((void **)base_addr + field->start) = g_malloc(size);
|
*((void **)base_addr + field->start) = g_malloc(size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue