aboutsummaryrefslogtreecommitdiffstats
path: root/android/snaphost-android.c
blob: 4e3d1c4abc9482c1f126bc27fcc991d6109d9993 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
/* 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);

    /* 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);
}