1 /* Copyright (C) 2012 The Android Open Source Project 2 ** 3 ** This software is licensed under the terms of the GNU General Public 4 ** License version 2, as published by the Free Software Foundation, and 5 ** may be copied, distributed, and modified under those terms. 6 ** 7 ** This program is distributed in the hope that it will be useful, 8 ** but WITHOUT ANY WARRANTY; without even the implied warranty of 9 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 ** GNU General Public License for more details. 11 */ 12 13 #include "qemu-common.h" 14 #include "android/globals.h" 15 #include "android/snaphost-android.h" 16 #include "android/utils/debug.h" 17 18 #define E(...) derror(__VA_ARGS__) 19 #define W(...) dwarning(__VA_ARGS__) 20 #define D(...) VERBOSE_PRINT(init,__VA_ARGS__) 21 22 /* Compares two instance of an ini file. 23 * This routine compares all entries (key,value pairs) found in one ini file 24 * against entries in another file. The files are considered to be equal if: 25 * 1. Number of entries in each file is equal. 26 * 2. Each entry in one file has a corresponded entry in another file, and their 27 * values are equal. 28 * Param: 29 * current - Ini file containing the current configuration. 30 * saved - Ini file containing a previously saved configuration. 31 * Return: 32 * 0 if files are equal, or 1 if they are not equal, or -1 if an error has 33 * occurred. 34 */ 35 static int 36 _cmp_hw_config(IniFile* current, IniFile* saved) 37 { 38 int n, ret = 0; 39 const int num_pairs = iniFile_getPairCount(current); 40 41 /* Check 1: must contain same number of entries. */ 42 if (num_pairs != iniFile_getPairCount(saved)) { 43 D("Different numbers of entries in the HW config files. Current contans %d, while saved contains %d entries.", 44 num_pairs, iniFile_getPairCount(saved)); 45 return -1; 46 } 47 48 /* Iterate through the entries in the current file, comparing them to entries 49 * in the saved file. */ 50 for (n = 0; n < num_pairs && ret == 0; n++) { 51 char* key, *value1, *value2; 52 53 if (iniFile_getEntry(current, n, &key, &value1)) { 54 D("Unable to obtain entry %d from the current HW config file", n); 55 return -1; 56 } 57 58 value2 = iniFile_getString(saved, key, ""); 59 if (value2 == NULL) { 60 D("Saved HW config file is missing entry ('%s', '%s') found in the current HW config.", 61 key, value1); 62 free(key); 63 free(value1); 64 return 1; 65 } 66 67 ret = strcmp(value1, value2); 68 if (ret) { 69 D("HW config value mismatch for a key '%s': current is '%s' while saved was '%s'", 70 key, value1, value2); 71 } 72 73 free(value2); 74 free(value1); 75 free(key); 76 } 77 78 return ret ? 1 : 0; 79 } 80 81 /* Builds path to the HW config backup file that is used to store HW config 82 * settings used for that snapshot. The backup path is a concatenation of the 83 * snapshot storage file path, snapshot name, and an 'ini' extension. This 84 * way we can track HW configuration for different snapshot names store in 85 * different storage files. 86 * Param: 87 * name - Name of the snapshot inside the snapshot storage file. 88 * Return: 89 * Path to the HW config backup file on success, or NULL on an error. 90 */ 91 static char* 92 _build_hwcfg_path(const char* name) 93 { 94 const int path_len = strlen(android_hw->disk_snapStorage_path) + 95 strlen(name) + 6; 96 char* bkp_path = malloc(path_len); 97 if (bkp_path == NULL) { 98 E("Unable to allocate %d bytes for HW config path!", path_len); 99 return NULL; 100 } 101 102 snprintf(bkp_path, path_len, "%s.%s.ini", 103 android_hw->disk_snapStorage_path, name); 104 105 return bkp_path; 106 } 107 108 int 109 snaphost_match_configs(IniFile* hw_ini, const char* name) 110 { 111 /* Make sure that snapshot storage path is valid. */ 112 if (android_hw->disk_snapStorage_path == NULL || 113 *android_hw->disk_snapStorage_path == '\0') { 114 return 1; 115 } 116 117 /* Build path to the HW config for the loading VM. */ 118 char* bkp_path = _build_hwcfg_path(name); 119 if (bkp_path == NULL) { 120 return 0; 121 } 122 123 /* Load HW config from the previous emulator launch. */ 124 IniFile* hwcfg_bkp = iniFile_newFromFile(bkp_path); 125 126 if (hwcfg_bkp != NULL) { 127 if (_cmp_hw_config(hw_ini, hwcfg_bkp)) { 128 E("Unable to load VM from snapshot. The snapshot has been saved for a different hardware configuration."); 129 free(bkp_path); 130 return 0; 131 } 132 iniFile_free(hwcfg_bkp); 133 } else { 134 /* It could be that a snapshot file has been copied from another 135 * location without copying the backup file, or snapshot file has not 136 * been created yet. In either case we can't do much checking here, 137 * so, lets be hopefull that user knows what (s)he is doing. */ 138 D("Missing HW config backup file '%s'", bkp_path); 139 } 140 141 free(bkp_path); 142 143 return 1; 144 } 145 146 void 147 snaphost_save_config(const char* name) 148 { 149 /* Make sure that snapshot storage path is valid. */ 150 if (android_hw->disk_snapStorage_path == NULL || 151 *android_hw->disk_snapStorage_path == '\0') { 152 return; 153 } 154 155 /* Build path to the HW config for the saving VM. */ 156 char* bkp_path = _build_hwcfg_path(name); 157 if (bkp_path == NULL) { 158 return; 159 } 160 161 /* Create HW config backup file from the current HW config settings. */ 162 IniFile* hwcfg_bkp = iniFile_newFromMemory("", bkp_path); 163 if (hwcfg_bkp == NULL) { 164 W("Unable to create backup HW config file '%s'. Error: %s", 165 bkp_path, strerror(errno)); 166 return; 167 } 168 androidHwConfig_write(android_hw, hwcfg_bkp); 169 170 /* Invalidate data partition initialization path in the backup copy of HW 171 * config. The reason we need to do this is that we want the system loading 172 * from the snapshot to be in sync with the data partition the snapshot was 173 * saved for. For that we must disalow overwritting it on snapshot load. In 174 * other words, we should allow snapshot loading only on condition 175 * that disk.dataPartition.initPath is empty. */ 176 iniFile_setValue(hwcfg_bkp, "disk.dataPartition.initPath", ""); 177 178 /* Save backup file. */ 179 if (!iniFile_saveToFileClean(hwcfg_bkp, bkp_path)) { 180 D("HW config has been backed up to '%s'", bkp_path); 181 } else { 182 /* Treat this as a "soft" error. Yes, we couldn't save the backup, but 183 * this should not cancel snapshot saving. */ 184 W("Unable to save HW config file '%s'. Error: %s", bkp_path, strerror(errno)); 185 } 186 iniFile_free(hwcfg_bkp); 187 free(bkp_path); 188 } 189