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
_cmp_hw_config(IniFile * current,IniFile * saved)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 contains %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*
_build_hwcfg_path(const char * name)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
snaphost_match_configs(IniFile * hw_ini,const char * name)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
snaphost_save_config(const char * name)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