Merge "Respect HW configs when loading VM from snapshots."
diff --git a/Makefile.common b/Makefile.common
index 59c18ea..4a711c6 100644
--- a/Makefile.common
+++ b/Makefile.common
@@ -429,7 +429,8 @@
     android/camera/camera-format-converters.c \
     android/camera/camera-service.c \
     android/adb-server.c \
-    android/adb-qemud.c
+    android/adb-qemud.c \
+    android/snaphost-android.c
 
 $(call gen-hw-config-defs)
 
diff --git a/android/main.c b/android/main.c
index 1adb6a1..c56bb5c 100644
--- a/android/main.c
+++ b/android/main.c
@@ -1308,7 +1308,11 @@
              coreHwIniPath = tempfile_path(tempIni);
         }
 
-        if (iniFile_saveToFile(hwIni, coreHwIniPath) < 0) {
+        /* While saving HW config, ignore valueless entries. This will not break
+         * anything, but will significantly simplify comparing the current HW
+         * config with the one that has been associated with a snapshot (in case
+         * VM starts from a snapshot for this instance of emulator). */
+        if (iniFile_saveToFileClean(hwIni, coreHwIniPath) < 0) {
             derror("Could not write hardware.ini to %s: %s", coreHwIniPath, strerror(errno));
             exit(2);
         }
diff --git a/android/snaphost-android.c b/android/snaphost-android.c
new file mode 100644
index 0000000..205531c
--- /dev/null
+++ b/android/snaphost-android.c
@@ -0,0 +1,180 @@
+/* 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 contans %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);
+
+    /* 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);
+}
diff --git a/android/snaphost-android.h b/android/snaphost-android.h
new file mode 100644
index 0000000..ae558ba
--- /dev/null
+++ b/android/snaphost-android.h
@@ -0,0 +1,32 @@
+/* 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.
+*/
+
+#ifndef _ANDROID_SNAPHOST_ANDROID_H_
+#define _ANDROID_SNAPHOST_ANDROID_H_
+
+/* Matches HW config saved for a VM snapshot against the current HW config.
+ * Param:
+ *  hw_ini - IniFile instance containing the current HW config settings.
+ *  name - Name of the snapshot for which the VM is loading.
+ * Return:
+ *  Boolean: 1 if HW configurations match, or 0 if they don't match.
+ */
+extern int snaphost_match_configs(IniFile* hw_ini, const char* name);
+
+/* Saves HW config settings for the current VM.
+ * Param:
+ *  name - Name of the snapshot for the current VM.
+ */
+extern void snaphost_save_config(const char* name);
+
+#endif  /* _ANDROID_SNAPHOST_ANDROID_H_ */
+
diff --git a/android/utils/ini.c b/android/utils/ini.c
index ff4a8af..43f1321 100644
--- a/android/utils/ini.c
+++ b/android/utils/ini.c
@@ -298,8 +298,18 @@
     return ini;
 }
 
-int
-iniFile_saveToFile( IniFile*  f, const char*  filepath )
+/* Common routine for saving IniFile instance to the given file.
+ * Param:
+ *  f - IniFile instance to save.
+ *  filepath - Path to a file where to save the instance.
+ *  strip - If 1, ignore (don't save) pairs with empty values. If 0, save all
+ *      pairs found in the IniFile instance, including the ones that contain
+ *      empty values.
+ * Returns:
+ *  0 on success, -1 on error (see errno for error code)
+ */
+static int
+iniFile_saveToFileCommon( IniFile*  f, const char*  filepath, int strip )
 {
     FILE*  fp = fopen(filepath, "wt");
     IniPair*  pair    = f->pairs;
@@ -313,11 +323,13 @@
     }
 
     for ( ; pair < pairEnd; pair++ ) {
-        char  temp[PATH_MAX], *p=temp, *end=p+sizeof(temp);
-        p = bufprint(temp, end, "%s = %s\n", pair->key, pair->value);
-        if (fwrite(temp, p - temp, 1, fp) != 1) {
-            result = -1;
-            break;
+        if ((pair->value && *pair->value) || !strip) {
+            char  temp[PATH_MAX], *p=temp, *end=p+sizeof(temp);
+            p = bufprint(temp, end, "%s = %s\n", pair->key, pair->value);
+            if (fwrite(temp, p - temp, 1, fp) != 1) {
+                result = -1;
+                break;
+            }
         }
     }
 
@@ -325,6 +337,33 @@
     return result;
 }
 
+int
+iniFile_saveToFile( IniFile*  f, const char*  filepath )
+{
+    return iniFile_saveToFileCommon(f, filepath, 0);
+}
+
+int
+iniFile_saveToFileClean( IniFile*  f, const char*  filepath )
+{
+    return iniFile_saveToFileCommon(f, filepath, 1);
+}
+
+int
+iniFile_getEntry(IniFile* f, int index, char** key, char** value)
+{
+    if (index >= f->numPairs) {
+        D("Index %d exceeds the number of ini file entries %d",
+          index, f->numPairs);
+        return -1;
+    }
+
+    *key = ASTRDUP(f->pairs[index].key);
+    *value = ASTRDUP(f->pairs[index].value);
+
+    return 0;
+}
+
 char*
 iniFile_getString( IniFile*  f, const char*  key, const char* defaultValue )
 {
diff --git a/android/utils/ini.h b/android/utils/ini.h
index 5730ee0..fd56575 100644
--- a/android/utils/ini.h
+++ b/android/utils/ini.h
@@ -37,6 +37,11 @@
  */
 int       iniFile_saveToFile( IniFile*  f, const char*  filePath );
 
+/* try to write an IniFile into a given file, ignorig pairs with empty values.
+ * returns 0 on success, -1 on error (see errno for error code)
+ */
+int       iniFile_saveToFileClean( IniFile*  f, const char*  filepath );
+
 /* free an IniFile object */
 void      iniFile_free( IniFile*  f );
 
@@ -48,6 +53,18 @@
  */
 const char*  iniFile_getValue( IniFile*  f, const char*  key );
 
+/* Copies a 'key, value' pair for an entry in the file.
+ * Param:
+ *  f - Initialized IniFile instance.
+ *  index - Index of the entry to copy. Must be less than value returned from the
+ *      iniFile_getPairCount routine.
+ *  key, value - Receives key, and value strings for the entry. If this routine
+ *      succeeds, the caller must free the buffers allocated for the strings.
+ * Return:
+ *  0 on success, -1 if the index exceeds the capacity of the file
+ */
+int     iniFile_getEntry(IniFile* f, int index, char** key, char** value);
+
 /* returns a copy of the value of a given key, or NULL if defaultValue is NULL.
  * caller must free() it.
  */
diff --git a/vl-android.c b/vl-android.c
index 322ddca..8f439ac 100644
--- a/vl-android.c
+++ b/vl-android.c
@@ -208,6 +208,8 @@
 #include "android/core-init-utils.h"
 #include "android/audio-test.h"
 
+#include "android/snaphost-android.h"
+
 #ifdef CONFIG_STANDALONE_CORE
 /* Verbose value used by the standalone emulator core (without UI) */
 unsigned long   android_verbose;
@@ -235,8 +237,6 @@
 #include "libslirp.h"
 #endif
 
-
-
 #define DEFAULT_RAM_SIZE 128
 
 /* Max number of USB devices that can be specified on the commandline.  */
@@ -2025,6 +2025,10 @@
                 no_shutdown = 0;
             } else {
                 if (savevm_on_exit != NULL) {
+                  /* Prior to saving VM to the snapshot file, save HW config
+                   * settings for that VM, so we can match them when VM gets
+                   * loaded from the snapshot. */
+                  snaphost_save_config(savevm_on_exit);
                   do_savevm(cur_mon, savevm_on_exit);
                 }
                 break;
@@ -3511,6 +3515,12 @@
     androidHwConfig_init(android_hw, 0);
     androidHwConfig_read(android_hw, hw_ini);
 
+    /* If we're loading VM from a snapshot, make sure that the current HW config
+     * matches the one with which the VM has been saved. */
+    if (loadvm && *loadvm && !snaphost_match_configs(hw_ini, loadvm)) {
+        exit(0);
+    }
+
     iniFile_free(hw_ini);
 
     {
@@ -4229,7 +4239,6 @@
         stralloc_reset(kernel_config);
     }
 
-
     for (env = first_cpu; env != NULL; env = env->next_cpu) {
         for (i = 0; i < nb_numa_nodes; i++) {
             if (node_cpumask[i] & (1 << env->cpu_index)) {