blob: 7e4b55039ea41aea107b19667037115b60c436d5 [file] [log] [blame]
/* Copyright (C) 2012 The Android Open Source Project
**
** This software is licensed under the terms of the GNU General Public
** License version 2, as published by the Free Software Foundation, and
** may be copied, distributed, and modified under those terms.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
*/
#include "qemu-common.h"
#include "android/globals.h"
#include "android/snaphost-android.h"
#include "android/utils/debug.h"
#define E(...) derror(__VA_ARGS__)
#define W(...) dwarning(__VA_ARGS__)
#define D(...) VERBOSE_PRINT(init,__VA_ARGS__)
/* Compares two instance of an ini file.
* This routine compares all entries (key,value pairs) found in one ini file
* against entries in another file. The files are considered to be equal if:
* 1. Number of entries in each file is equal.
* 2. Each entry in one file has a corresponded entry in another file, and their
* values are equal.
* Param:
* current - Ini file containing the current configuration.
* saved - Ini file containing a previously saved configuration.
* Return:
* 0 if files are equal, or 1 if they are not equal, or -1 if an error has
* occurred.
*/
static int
_cmp_hw_config(IniFile* current, IniFile* saved)
{
int n, ret = 0;
const int num_pairs = iniFile_getPairCount(current);
/* Check 1: must contain same number of entries. */
if (num_pairs != iniFile_getPairCount(saved)) {
D("Different numbers of entries in the HW config files. Current contains %d, while saved contains %d entries.",
num_pairs, iniFile_getPairCount(saved));
return -1;
}
/* Iterate through the entries in the current file, comparing them to entries
* in the saved file. */
for (n = 0; n < num_pairs && ret == 0; n++) {
char* key, *value1, *value2;
if (iniFile_getEntry(current, n, &key, &value1)) {
D("Unable to obtain entry %d from the current HW config file", n);
return -1;
}
value2 = iniFile_getString(saved, key, "");
if (value2 == NULL) {
D("Saved HW config file is missing entry ('%s', '%s') found in the current HW config.",
key, value1);
free(key);
free(value1);
return 1;
}
ret = strcmp(value1, value2);
if (ret) {
D("HW config value mismatch for a key '%s': current is '%s' while saved was '%s'",
key, value1, value2);
}
free(value2);
free(value1);
free(key);
}
return ret ? 1 : 0;
}
/* Builds path to the HW config backup file that is used to store HW config
* settings used for that snapshot. The backup path is a concatenation of the
* snapshot storage file path, snapshot name, and an 'ini' extension. This
* way we can track HW configuration for different snapshot names store in
* different storage files.
* Param:
* name - Name of the snapshot inside the snapshot storage file.
* Return:
* Path to the HW config backup file on success, or NULL on an error.
*/
static char*
_build_hwcfg_path(const char* name)
{
const int path_len = strlen(android_hw->disk_snapStorage_path) +
strlen(name) + 6;
char* bkp_path = malloc(path_len);
if (bkp_path == NULL) {
E("Unable to allocate %d bytes for HW config path!", path_len);
return NULL;
}
snprintf(bkp_path, path_len, "%s.%s.ini",
android_hw->disk_snapStorage_path, name);
return bkp_path;
}
int
snaphost_match_configs(IniFile* hw_ini, const char* name)
{
/* Make sure that snapshot storage path is valid. */
if (android_hw->disk_snapStorage_path == NULL ||
*android_hw->disk_snapStorage_path == '\0') {
return 1;
}
/* Build path to the HW config for the loading VM. */
char* bkp_path = _build_hwcfg_path(name);
if (bkp_path == NULL) {
return 0;
}
/* Load HW config from the previous emulator launch. */
IniFile* hwcfg_bkp = iniFile_newFromFile(bkp_path);
if (hwcfg_bkp != NULL) {
if (_cmp_hw_config(hw_ini, hwcfg_bkp)) {
E("Unable to load VM from snapshot. The snapshot has been saved for a different hardware configuration.");
free(bkp_path);
return 0;
}
iniFile_free(hwcfg_bkp);
} else {
/* It could be that a snapshot file has been copied from another
* location without copying the backup file, or snapshot file has not
* been created yet. In either case we can't do much checking here,
* so, lets be hopefull that user knows what (s)he is doing. */
D("Missing HW config backup file '%s'", bkp_path);
}
free(bkp_path);
return 1;
}
void
snaphost_save_config(const char* name)
{
/* Make sure that snapshot storage path is valid. */
if (android_hw->disk_snapStorage_path == NULL ||
*android_hw->disk_snapStorage_path == '\0') {
return;
}
/* Build path to the HW config for the saving VM. */
char* bkp_path = _build_hwcfg_path(name);
if (bkp_path == NULL) {
return;
}
/* Create HW config backup file from the current HW config settings. */
IniFile* hwcfg_bkp = iniFile_newFromMemory("", bkp_path);
if (hwcfg_bkp == NULL) {
W("Unable to create backup HW config file '%s'. Error: %s",
bkp_path, strerror(errno));
return;
}
androidHwConfig_write(android_hw, hwcfg_bkp);
/* Invalidate data partition initialization path in the backup copy of HW
* config. The reason we need to do this is that we want the system loading
* from the snapshot to be in sync with the data partition the snapshot was
* saved for. For that we must disalow overwritting it on snapshot load. In
* other words, we should allow snapshot loading only on condition
* that disk.dataPartition.initPath is empty. */
iniFile_setValue(hwcfg_bkp, "disk.dataPartition.initPath", "");
/* Save backup file. */
if (!iniFile_saveToFileClean(hwcfg_bkp, bkp_path)) {
D("HW config has been backed up to '%s'", bkp_path);
} else {
/* Treat this as a "soft" error. Yes, we couldn't save the backup, but
* this should not cancel snapshot saving. */
W("Unable to save HW config file '%s'. Error: %s", bkp_path, strerror(errno));
}
iniFile_free(hwcfg_bkp);
free(bkp_path);
}