|
@@ -51,6 +51,36 @@ vmstate_field_exists(const VMStateDescription *vmsd, const VMStateField *field,
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * Create a fake nullptr field when there's a NULL pointer detected in the
|
|
|
+ * array of a VMS_ARRAY_OF_POINTER VMSD field. It's needed because we
|
|
|
+ * can't dereference the NULL pointer.
|
|
|
+ */
|
|
|
+static const VMStateField *
|
|
|
+vmsd_create_fake_nullptr_field(const VMStateField *field)
|
|
|
+{
|
|
|
+ VMStateField *fake = g_new0(VMStateField, 1);
|
|
|
+
|
|
|
+ /* It can only happen on an array of pointers! */
|
|
|
+ assert(field->flags & VMS_ARRAY_OF_POINTER);
|
|
|
+
|
|
|
+ /* Some of fake's properties should match the original's */
|
|
|
+ fake->name = field->name;
|
|
|
+ fake->version_id = field->version_id;
|
|
|
+
|
|
|
+ /* Do not need "field_exists" check as it always exists (which is null) */
|
|
|
+ fake->field_exists = NULL;
|
|
|
+
|
|
|
+ /* See vmstate_info_nullptr - use 1 byte to represent nullptr */
|
|
|
+ fake->size = 1;
|
|
|
+ fake->info = &vmstate_info_nullptr;
|
|
|
+ fake->flags = VMS_SINGLE;
|
|
|
+
|
|
|
+ /* All the rest fields shouldn't matter.. */
|
|
|
+
|
|
|
+ return (const VMStateField *)fake;
|
|
|
+}
|
|
|
+
|
|
|
static int vmstate_n_elems(void *opaque, const VMStateField *field)
|
|
|
{
|
|
|
int n_elems = 1;
|
|
@@ -143,23 +173,39 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
|
|
|
}
|
|
|
for (i = 0; i < n_elems; i++) {
|
|
|
void *curr_elem = first_elem + size * i;
|
|
|
+ const VMStateField *inner_field;
|
|
|
|
|
|
if (field->flags & VMS_ARRAY_OF_POINTER) {
|
|
|
curr_elem = *(void **)curr_elem;
|
|
|
}
|
|
|
+
|
|
|
if (!curr_elem && size) {
|
|
|
- /* if null pointer check placeholder and do not follow */
|
|
|
- assert(field->flags & VMS_ARRAY_OF_POINTER);
|
|
|
- ret = vmstate_info_nullptr.get(f, curr_elem, size, NULL);
|
|
|
- } else if (field->flags & VMS_STRUCT) {
|
|
|
- ret = vmstate_load_state(f, field->vmsd, curr_elem,
|
|
|
- field->vmsd->version_id);
|
|
|
- } else if (field->flags & VMS_VSTRUCT) {
|
|
|
- ret = vmstate_load_state(f, field->vmsd, curr_elem,
|
|
|
- field->struct_version_id);
|
|
|
+ /*
|
|
|
+ * If null pointer found (which should only happen in
|
|
|
+ * an array of pointers), use null placeholder and do
|
|
|
+ * not follow.
|
|
|
+ */
|
|
|
+ inner_field = vmsd_create_fake_nullptr_field(field);
|
|
|
+ } else {
|
|
|
+ inner_field = field;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (inner_field->flags & VMS_STRUCT) {
|
|
|
+ ret = vmstate_load_state(f, inner_field->vmsd, curr_elem,
|
|
|
+ inner_field->vmsd->version_id);
|
|
|
+ } else if (inner_field->flags & VMS_VSTRUCT) {
|
|
|
+ ret = vmstate_load_state(f, inner_field->vmsd, curr_elem,
|
|
|
+ inner_field->struct_version_id);
|
|
|
} else {
|
|
|
- ret = field->info->get(f, curr_elem, size, field);
|
|
|
+ ret = inner_field->info->get(f, curr_elem, size,
|
|
|
+ inner_field);
|
|
|
}
|
|
|
+
|
|
|
+ /* If we used a fake temp field.. free it now */
|
|
|
+ if (inner_field != field) {
|
|
|
+ g_clear_pointer((gpointer *)&inner_field, g_free);
|
|
|
+ }
|
|
|
+
|
|
|
if (ret >= 0) {
|
|
|
ret = qemu_file_get_error(f);
|
|
|
}
|
|
@@ -387,29 +433,50 @@ int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd,
|
|
|
}
|
|
|
for (i = 0; i < n_elems; i++) {
|
|
|
void *curr_elem = first_elem + size * i;
|
|
|
+ const VMStateField *inner_field;
|
|
|
|
|
|
- vmsd_desc_field_start(vmsd, vmdesc_loop, field, i, n_elems);
|
|
|
old_offset = qemu_file_transferred(f);
|
|
|
if (field->flags & VMS_ARRAY_OF_POINTER) {
|
|
|
assert(curr_elem);
|
|
|
curr_elem = *(void **)curr_elem;
|
|
|
}
|
|
|
+
|
|
|
if (!curr_elem && size) {
|
|
|
- /* if null pointer write placeholder and do not follow */
|
|
|
- assert(field->flags & VMS_ARRAY_OF_POINTER);
|
|
|
- ret = vmstate_info_nullptr.put(f, curr_elem, size, NULL,
|
|
|
- NULL);
|
|
|
- } else if (field->flags & VMS_STRUCT) {
|
|
|
- ret = vmstate_save_state(f, field->vmsd, curr_elem,
|
|
|
- vmdesc_loop);
|
|
|
- } else if (field->flags & VMS_VSTRUCT) {
|
|
|
- ret = vmstate_save_state_v(f, field->vmsd, curr_elem,
|
|
|
- vmdesc_loop,
|
|
|
- field->struct_version_id, errp);
|
|
|
+ /*
|
|
|
+ * If null pointer found (which should only happen in
|
|
|
+ * an array of pointers), use null placeholder and do
|
|
|
+ * not follow.
|
|
|
+ */
|
|
|
+ inner_field = vmsd_create_fake_nullptr_field(field);
|
|
|
+ } else {
|
|
|
+ inner_field = field;
|
|
|
+ }
|
|
|
+
|
|
|
+ vmsd_desc_field_start(vmsd, vmdesc_loop, inner_field,
|
|
|
+ i, n_elems);
|
|
|
+
|
|
|
+ if (inner_field->flags & VMS_STRUCT) {
|
|
|
+ ret = vmstate_save_state(f, inner_field->vmsd,
|
|
|
+ curr_elem, vmdesc_loop);
|
|
|
+ } else if (inner_field->flags & VMS_VSTRUCT) {
|
|
|
+ ret = vmstate_save_state_v(f, inner_field->vmsd,
|
|
|
+ curr_elem, vmdesc_loop,
|
|
|
+ inner_field->struct_version_id,
|
|
|
+ errp);
|
|
|
} else {
|
|
|
- ret = field->info->put(f, curr_elem, size, field,
|
|
|
- vmdesc_loop);
|
|
|
+ ret = inner_field->info->put(f, curr_elem, size,
|
|
|
+ inner_field, vmdesc_loop);
|
|
|
}
|
|
|
+
|
|
|
+ written_bytes = qemu_file_transferred(f) - old_offset;
|
|
|
+ vmsd_desc_field_end(vmsd, vmdesc_loop, inner_field,
|
|
|
+ written_bytes);
|
|
|
+
|
|
|
+ /* If we used a fake temp field.. free it now */
|
|
|
+ if (inner_field != field) {
|
|
|
+ g_clear_pointer((gpointer *)&inner_field, g_free);
|
|
|
+ }
|
|
|
+
|
|
|
if (ret) {
|
|
|
error_setg(errp, "Save of field %s/%s failed",
|
|
|
vmsd->name, field->name);
|
|
@@ -419,9 +486,6 @@ int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd,
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
- written_bytes = qemu_file_transferred(f) - old_offset;
|
|
|
- vmsd_desc_field_end(vmsd, vmdesc_loop, field, written_bytes);
|
|
|
-
|
|
|
/* Compressed arrays only care about the first element */
|
|
|
if (vmdesc_loop && vmsd_can_compress(field)) {
|
|
|
vmdesc_loop = NULL;
|