aboutsummaryrefslogtreecommitdiff
path: root/savevm.c
diff options
context:
space:
mode:
Diffstat (limited to 'savevm.c')
-rw-r--r--savevm.c222
1 files changed, 139 insertions, 83 deletions
diff --git a/savevm.c b/savevm.c
index abb4a60643..cf79a56871 100644
--- a/savevm.c
+++ b/savevm.c
@@ -173,7 +173,7 @@ struct QEMUFile {
int buf_size; /* 0 when writing */
uint8_t buf[IO_BUF_SIZE];
- int has_error;
+ int last_error;
};
typedef struct QEMUFileStdio
@@ -425,14 +425,14 @@ QEMUFile *qemu_fopen_ops(void *opaque, QEMUFilePutBufferFunc *put_buffer,
return f;
}
-int qemu_file_has_error(QEMUFile *f)
+int qemu_file_get_error(QEMUFile *f)
{
- return f->has_error;
+ return f->last_error;
}
-void qemu_file_set_error(QEMUFile *f)
+void qemu_file_set_error(QEMUFile *f, int ret)
{
- f->has_error = 1;
+ f->last_error = ret;
}
void qemu_fflush(QEMUFile *f)
@@ -447,7 +447,7 @@ void qemu_fflush(QEMUFile *f)
if (len > 0)
f->buf_offset += f->buf_index;
else
- f->has_error = 1;
+ f->last_error = -EINVAL;
f->buf_index = 0;
}
}
@@ -455,6 +455,7 @@ void qemu_fflush(QEMUFile *f)
static void qemu_fill_buffer(QEMUFile *f)
{
int len;
+ int pending;
if (!f->get_buffer)
return;
@@ -462,13 +463,20 @@ static void qemu_fill_buffer(QEMUFile *f)
if (f->is_write)
abort();
- len = f->get_buffer(f->opaque, f->buf, f->buf_offset, IO_BUF_SIZE);
+ pending = f->buf_size - f->buf_index;
+ if (pending > 0) {
+ memmove(f->buf, f->buf + f->buf_index, pending);
+ }
+ f->buf_index = 0;
+ f->buf_size = pending;
+
+ len = f->get_buffer(f->opaque, f->buf + pending, f->buf_offset,
+ IO_BUF_SIZE - pending);
if (len > 0) {
- f->buf_index = 0;
- f->buf_size = len;
+ f->buf_size += len;
f->buf_offset += len;
} else if (len != -EAGAIN)
- f->has_error = 1;
+ f->last_error = len;
}
int qemu_fclose(QEMUFile *f)
@@ -490,13 +498,13 @@ void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size)
{
int l;
- if (!f->has_error && f->is_write == 0 && f->buf_index > 0) {
+ if (!f->last_error && f->is_write == 0 && f->buf_index > 0) {
fprintf(stderr,
"Attempted to write to buffer while read buffer is not empty\n");
abort();
}
- while (!f->has_error && size > 0) {
+ while (!f->last_error && size > 0) {
l = IO_BUF_SIZE - f->buf_index;
if (l > size)
l = size;
@@ -512,7 +520,7 @@ void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size)
void qemu_put_byte(QEMUFile *f, int v)
{
- if (!f->has_error && f->is_write == 0 && f->buf_index > 0) {
+ if (!f->last_error && f->is_write == 0 && f->buf_index > 0) {
fprintf(stderr,
"Attempted to write to buffer while read buffer is not empty\n");
abort();
@@ -524,56 +532,86 @@ void qemu_put_byte(QEMUFile *f, int v)
qemu_fflush(f);
}
-int qemu_get_buffer(QEMUFile *f, uint8_t *buf, int size1)
+static void qemu_file_skip(QEMUFile *f, int size)
+{
+ if (f->buf_index + size <= f->buf_size) {
+ f->buf_index += size;
+ }
+}
+
+static int qemu_peek_buffer(QEMUFile *f, uint8_t *buf, int size, size_t offset)
{
- int size, l;
+ int pending;
+ int index;
- if (f->is_write)
+ if (f->is_write) {
abort();
+ }
- size = size1;
- while (size > 0) {
- l = f->buf_size - f->buf_index;
- if (l == 0) {
- qemu_fill_buffer(f);
- l = f->buf_size - f->buf_index;
- if (l == 0)
- break;
+ index = f->buf_index + offset;
+ pending = f->buf_size - index;
+ if (pending < size) {
+ qemu_fill_buffer(f);
+ index = f->buf_index + offset;
+ pending = f->buf_size - index;
+ }
+
+ if (pending <= 0) {
+ return 0;
+ }
+ if (size > pending) {
+ size = pending;
+ }
+
+ memcpy(buf, f->buf + index, size);
+ return size;
+}
+
+int qemu_get_buffer(QEMUFile *f, uint8_t *buf, int size)
+{
+ int pending = size;
+ int done = 0;
+
+ while (pending > 0) {
+ int res;
+
+ res = qemu_peek_buffer(f, buf, pending, 0);
+ if (res == 0) {
+ return done;
}
- if (l > size)
- l = size;
- memcpy(buf, f->buf + f->buf_index, l);
- f->buf_index += l;
- buf += l;
- size -= l;
+ qemu_file_skip(f, res);
+ buf += res;
+ pending -= res;
+ done += res;
}
- return size1 - size;
+ return done;
}
-static int qemu_peek_byte(QEMUFile *f)
+static int qemu_peek_byte(QEMUFile *f, int offset)
{
- if (f->is_write)
+ int index = f->buf_index + offset;
+
+ if (f->is_write) {
abort();
+ }
- if (f->buf_index >= f->buf_size) {
+ if (index >= f->buf_size) {
qemu_fill_buffer(f);
- if (f->buf_index >= f->buf_size)
+ index = f->buf_index + offset;
+ if (index >= f->buf_size) {
return 0;
+ }
}
- return f->buf[f->buf_index];
+ return f->buf[index];
}
int qemu_get_byte(QEMUFile *f)
{
- if (f->is_write)
- abort();
+ int result;
- if (f->buf_index >= f->buf_size) {
- qemu_fill_buffer(f);
- if (f->buf_index >= f->buf_size)
- return 0;
- }
- return f->buf[f->buf_index++];
+ result = qemu_peek_byte(f, 0);
+ qemu_file_skip(f, 1);
+ return result;
}
int64_t qemu_ftell(QEMUFile *f)
@@ -1466,6 +1504,7 @@ int qemu_savevm_state_begin(Monitor *mon, QEMUFile *f, int blk_enable,
int shared)
{
SaveStateEntry *se;
+ int ret;
QTAILQ_FOREACH(se, &savevm_handlers, entry) {
if(se->set_params == NULL) {
@@ -1495,17 +1534,27 @@ int qemu_savevm_state_begin(Monitor *mon, QEMUFile *f, int blk_enable,
qemu_put_be32(f, se->instance_id);
qemu_put_be32(f, se->version_id);
- se->save_live_state(mon, f, QEMU_VM_SECTION_START, se->opaque);
+ ret = se->save_live_state(mon, f, QEMU_VM_SECTION_START, se->opaque);
+ if (ret < 0) {
+ qemu_savevm_state_cancel(mon, f);
+ return ret;
+ }
}
-
- if (qemu_file_has_error(f)) {
+ ret = qemu_file_get_error(f);
+ if (ret != 0) {
qemu_savevm_state_cancel(mon, f);
- return -EIO;
}
- return 0;
+ return ret;
+
}
+/*
+ * this funtion has three return values:
+ * negative: there was one error, and we have -errno.
+ * 0 : We haven't finished, caller have to go again
+ * 1 : We have finished, we can go to complete phase
+ */
int qemu_savevm_state_iterate(Monitor *mon, QEMUFile *f)
{
SaveStateEntry *se;
@@ -1520,7 +1569,7 @@ int qemu_savevm_state_iterate(Monitor *mon, QEMUFile *f)
qemu_put_be32(f, se->section_id);
ret = se->save_live_state(mon, f, QEMU_VM_SECTION_PART, se->opaque);
- if (!ret) {
+ if (ret <= 0) {
/* Do not proceed to the next vmstate before this one reported
completion of the current stage. This serializes the migration
and reduces the probability that a faster changing state is
@@ -1528,21 +1577,20 @@ int qemu_savevm_state_iterate(Monitor *mon, QEMUFile *f)
break;
}
}
-
- if (ret)
- return 1;
-
- if (qemu_file_has_error(f)) {
+ if (ret != 0) {
+ return ret;
+ }
+ ret = qemu_file_get_error(f);
+ if (ret != 0) {
qemu_savevm_state_cancel(mon, f);
- return -EIO;
}
-
- return 0;
+ return ret;
}
int qemu_savevm_state_complete(Monitor *mon, QEMUFile *f)
{
SaveStateEntry *se;
+ int ret;
cpu_synchronize_all_states();
@@ -1554,7 +1602,10 @@ int qemu_savevm_state_complete(Monitor *mon, QEMUFile *f)
qemu_put_byte(f, QEMU_VM_SECTION_END);
qemu_put_be32(f, se->section_id);
- se->save_live_state(mon, f, QEMU_VM_SECTION_END, se->opaque);
+ ret = se->save_live_state(mon, f, QEMU_VM_SECTION_END, se->opaque);
+ if (ret < 0) {
+ return ret;
+ }
}
QTAILQ_FOREACH(se, &savevm_handlers, entry) {
@@ -1580,10 +1631,7 @@ int qemu_savevm_state_complete(Monitor *mon, QEMUFile *f)
qemu_put_byte(f, QEMU_VM_EOF);
- if (qemu_file_has_error(f))
- return -EIO;
-
- return 0;
+ return qemu_file_get_error(f);
}
void qemu_savevm_state_cancel(Monitor *mon, QEMUFile *f)
@@ -1619,8 +1667,9 @@ static int qemu_savevm_state(Monitor *mon, QEMUFile *f)
ret = qemu_savevm_state_complete(mon, f);
out:
- if (qemu_file_has_error(f))
- ret = -EIO;
+ if (ret == 0) {
+ ret = qemu_file_get_error(f);
+ }
return ret;
}
@@ -1659,29 +1708,36 @@ static const VMStateDescription *vmstate_get_subsection(const VMStateSubsection
static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd,
void *opaque)
{
- const VMStateSubsection *sub = vmsd->subsections;
-
- if (!sub || !sub->needed) {
- return 0;
- }
-
- while (qemu_peek_byte(f) == QEMU_VM_SUBSECTION) {
+ while (qemu_peek_byte(f, 0) == QEMU_VM_SUBSECTION) {
char idstr[256];
int ret;
- uint8_t version_id, len;
+ uint8_t version_id, len, size;
const VMStateDescription *sub_vmsd;
- qemu_get_byte(f); /* subsection */
- len = qemu_get_byte(f);
- qemu_get_buffer(f, (uint8_t *)idstr, len);
- idstr[len] = 0;
- version_id = qemu_get_be32(f);
+ len = qemu_peek_byte(f, 1);
+ if (len < strlen(vmsd->name) + 1) {
+ /* subsection name has be be "section_name/a" */
+ return 0;
+ }
+ size = qemu_peek_buffer(f, (uint8_t *)idstr, len, 2);
+ if (size != len) {
+ return 0;
+ }
+ idstr[size] = 0;
- sub_vmsd = vmstate_get_subsection(sub, idstr);
+ if (strncmp(vmsd->name, idstr, strlen(vmsd->name)) != 0) {
+ /* it don't have a valid subsection name */
+ return 0;
+ }
+ sub_vmsd = vmstate_get_subsection(vmsd->subsections, idstr);
if (sub_vmsd == NULL) {
return -ENOENT;
}
- assert(!sub_vmsd->subsections);
+ qemu_file_skip(f, 1); /* subsection */
+ qemu_file_skip(f, 1); /* len */
+ qemu_file_skip(f, len); /* idstr */
+ version_id = qemu_get_be32(f);
+
ret = vmstate_load_state(f, sub_vmsd, opaque, version_id);
if (ret) {
return ret;
@@ -1705,7 +1761,6 @@ static void vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd,
qemu_put_byte(f, len);
qemu_put_buffer(f, (uint8_t *)vmsd->name, len);
qemu_put_be32(f, vmsd->version_id);
- assert(!vmsd->subsections);
vmstate_save_state(f, vmsd, opaque);
}
sub++;
@@ -1831,8 +1886,9 @@ out:
g_free(le);
}
- if (qemu_file_has_error(f))
- ret = -EIO;
+ if (ret == 0) {
+ ret = qemu_file_get_error(f);
+ }
return ret;
}