xref: /openbmc/qemu/block/parallels-ext.c (revision 36ebc7db)
1 /*
2  * Support of Parallels Format Extension. It's a part of Parallels format
3  * driver.
4  *
5  * Copyright (c) 2021 Virtuozzo International GmbH
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a copy
8  * of this software and associated documentation files (the "Software"), to deal
9  * in the Software without restriction, including without limitation the rights
10  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11  * copies of the Software, and to permit persons to whom the Software is
12  * furnished to do so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in
15  * all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23  * THE SOFTWARE.
24  */
25 
26 #include "qemu/osdep.h"
27 #include "qapi/error.h"
28 #include "block/block-io.h"
29 #include "block/block_int.h"
30 #include "block/dirty-bitmap.h"
31 #include "parallels.h"
32 #include "crypto/hash.h"
33 #include "qemu/uuid.h"
34 #include "qemu/memalign.h"
35 
36 #define PARALLELS_FORMAT_EXTENSION_MAGIC 0xAB234CEF23DCEA87ULL
37 
38 #define PARALLELS_END_OF_FEATURES_MAGIC 0x0ULL
39 #define PARALLELS_DIRTY_BITMAP_FEATURE_MAGIC 0x20385FAE252CB34AULL
40 
41 typedef struct ParallelsFormatExtensionHeader {
42     uint64_t magic; /* PARALLELS_FORMAT_EXTENSION_MAGIC */
43     uint8_t check_sum[16];
44 } QEMU_PACKED ParallelsFormatExtensionHeader;
45 
46 typedef struct ParallelsFeatureHeader {
47     uint64_t magic;
48     uint64_t flags;
49     uint32_t data_size;
50     uint32_t _unused;
51 } QEMU_PACKED ParallelsFeatureHeader;
52 
53 typedef struct ParallelsDirtyBitmapFeature {
54     uint64_t size;
55     uint8_t id[16];
56     uint32_t granularity;
57     uint32_t l1_size;
58     /* L1 table follows */
59 } QEMU_PACKED ParallelsDirtyBitmapFeature;
60 
61 /* Given L1 table read bitmap data from the image and populate @bitmap */
62 static int parallels_load_bitmap_data(BlockDriverState *bs,
63                                       const uint64_t *l1_table,
64                                       uint32_t l1_size,
65                                       BdrvDirtyBitmap *bitmap,
66                                       Error **errp)
67 {
68     BDRVParallelsState *s = bs->opaque;
69     int ret = 0;
70     uint64_t offset, limit;
71     uint64_t bm_size = bdrv_dirty_bitmap_size(bitmap);
72     uint8_t *buf = NULL;
73     uint64_t i, tab_size =
74         DIV_ROUND_UP(bdrv_dirty_bitmap_serialization_size(bitmap, 0, bm_size),
75                      s->cluster_size);
76 
77     if (tab_size != l1_size) {
78         error_setg(errp, "Bitmap table size %" PRIu32 " does not correspond "
79                    "to bitmap size and cluster size. Expected %" PRIu64,
80                    l1_size, tab_size);
81         return -EINVAL;
82     }
83 
84     buf = qemu_blockalign(bs, s->cluster_size);
85     limit = bdrv_dirty_bitmap_serialization_coverage(s->cluster_size, bitmap);
86     for (i = 0, offset = 0; i < tab_size; ++i, offset += limit) {
87         uint64_t count = MIN(bm_size - offset, limit);
88         uint64_t entry = l1_table[i];
89 
90         if (entry == 0) {
91             /* No need to deserialize zeros because @bitmap is cleared. */
92             continue;
93         }
94 
95         if (entry == 1) {
96             bdrv_dirty_bitmap_deserialize_ones(bitmap, offset, count, false);
97         } else {
98             ret = bdrv_pread(bs->file, entry << BDRV_SECTOR_BITS,
99                              s->cluster_size, buf, 0);
100             if (ret < 0) {
101                 error_setg_errno(errp, -ret,
102                                  "Failed to read bitmap data cluster");
103                 goto finish;
104             }
105             bdrv_dirty_bitmap_deserialize_part(bitmap, buf, offset, count,
106                                                false);
107         }
108     }
109     ret = 0;
110 
111     bdrv_dirty_bitmap_deserialize_finish(bitmap);
112 
113 finish:
114     qemu_vfree(buf);
115 
116     return ret;
117 }
118 
119 /*
120  * @data buffer (of @data_size size) is the Dirty bitmaps feature which
121  * consists of ParallelsDirtyBitmapFeature followed by L1 table.
122  */
123 static BdrvDirtyBitmap *parallels_load_bitmap(BlockDriverState *bs,
124                                               uint8_t *data,
125                                               size_t data_size,
126                                               Error **errp)
127 {
128     int ret;
129     ParallelsDirtyBitmapFeature bf;
130     g_autofree uint64_t *l1_table = NULL;
131     BdrvDirtyBitmap *bitmap;
132     QemuUUID uuid;
133     char uuidstr[UUID_FMT_LEN + 1];
134     int i;
135 
136     if (data_size < sizeof(bf)) {
137         error_setg(errp, "Too small Bitmap Feature area in Parallels Format "
138                    "Extension: %zu bytes, expected at least %zu bytes",
139                    data_size, sizeof(bf));
140         return NULL;
141     }
142     memcpy(&bf, data, sizeof(bf));
143     bf.size = le64_to_cpu(bf.size);
144     bf.granularity = le32_to_cpu(bf.granularity) << BDRV_SECTOR_BITS;
145     bf.l1_size = le32_to_cpu(bf.l1_size);
146     data += sizeof(bf);
147     data_size -= sizeof(bf);
148 
149     if (bf.size != bs->total_sectors) {
150         error_setg(errp, "Bitmap size (in sectors) %" PRId64 " differs from "
151                    "disk size in sectors %" PRId64, bf.size, bs->total_sectors);
152         return NULL;
153     }
154 
155     if (bf.l1_size * sizeof(uint64_t) > data_size) {
156         error_setg(errp, "Bitmaps feature corrupted: l1 table exceeds "
157                    "extension data_size");
158         return NULL;
159     }
160 
161     memcpy(&uuid, bf.id, sizeof(uuid));
162     qemu_uuid_unparse(&uuid, uuidstr);
163     bitmap = bdrv_create_dirty_bitmap(bs, bf.granularity, uuidstr, errp);
164     if (!bitmap) {
165         return NULL;
166     }
167 
168     l1_table = g_new(uint64_t, bf.l1_size);
169     for (i = 0; i < bf.l1_size; i++, data += sizeof(uint64_t)) {
170         l1_table[i] = ldq_le_p(data);
171     }
172 
173     ret = parallels_load_bitmap_data(bs, l1_table, bf.l1_size, bitmap, errp);
174     if (ret < 0) {
175         bdrv_release_dirty_bitmap(bitmap);
176         return NULL;
177     }
178 
179     /* We support format extension only for RO parallels images. */
180     assert(!(bs->open_flags & BDRV_O_RDWR));
181     bdrv_dirty_bitmap_set_readonly(bitmap, true);
182 
183     return bitmap;
184 }
185 
186 static int parallels_parse_format_extension(BlockDriverState *bs,
187                                             uint8_t *ext_cluster, Error **errp)
188 {
189     BDRVParallelsState *s = bs->opaque;
190     int ret;
191     int remaining = s->cluster_size;
192     uint8_t *pos = ext_cluster;
193     ParallelsFormatExtensionHeader eh;
194     g_autofree uint8_t *hash = NULL;
195     size_t hash_len = 0;
196     GSList *bitmaps = NULL, *el;
197 
198     memcpy(&eh, pos, sizeof(eh));
199     eh.magic = le64_to_cpu(eh.magic);
200     pos += sizeof(eh);
201     remaining -= sizeof(eh);
202 
203     if (eh.magic != PARALLELS_FORMAT_EXTENSION_MAGIC) {
204         error_setg(errp, "Wrong parallels Format Extension magic: 0x%" PRIx64
205                    ", expected: 0x%llx", eh.magic,
206                    PARALLELS_FORMAT_EXTENSION_MAGIC);
207         goto fail;
208     }
209 
210     ret = qcrypto_hash_bytes(QCRYPTO_HASH_ALG_MD5, (char *)pos, remaining,
211                              &hash, &hash_len, errp);
212     if (ret < 0) {
213         goto fail;
214     }
215 
216     if (hash_len != sizeof(eh.check_sum) ||
217         memcmp(hash, eh.check_sum, sizeof(eh.check_sum)) != 0) {
218         error_setg(errp, "Wrong checksum in Format Extension header. Format "
219                    "extension is corrupted.");
220         goto fail;
221     }
222 
223     while (true) {
224         ParallelsFeatureHeader fh;
225         BdrvDirtyBitmap *bitmap;
226 
227         if (remaining < sizeof(fh)) {
228             error_setg(errp, "Can not read feature header, as remaining bytes "
229                        "(%d) in Format Extension is less than Feature header "
230                        "size (%zu)", remaining, sizeof(fh));
231             goto fail;
232         }
233 
234         memcpy(&fh, pos, sizeof(fh));
235         pos += sizeof(fh);
236         remaining -= sizeof(fh);
237 
238         fh.magic = le64_to_cpu(fh.magic);
239         fh.flags = le64_to_cpu(fh.flags);
240         fh.data_size = le32_to_cpu(fh.data_size);
241 
242         if (fh.flags) {
243             error_setg(errp, "Flags for extension feature are unsupported");
244             goto fail;
245         }
246 
247         if (fh.data_size > remaining) {
248             error_setg(errp, "Feature data_size exceedes Format Extension "
249                        "cluster");
250             goto fail;
251         }
252 
253         switch (fh.magic) {
254         case PARALLELS_END_OF_FEATURES_MAGIC:
255             return 0;
256 
257         case PARALLELS_DIRTY_BITMAP_FEATURE_MAGIC:
258             bitmap = parallels_load_bitmap(bs, pos, fh.data_size, errp);
259             if (!bitmap) {
260                 goto fail;
261             }
262             bitmaps = g_slist_append(bitmaps, bitmap);
263             break;
264 
265         default:
266             error_setg(errp, "Unknown feature: 0x%" PRIx64, fh.magic);
267             goto fail;
268         }
269 
270         pos = ext_cluster + QEMU_ALIGN_UP(pos + fh.data_size - ext_cluster, 8);
271     }
272 
273 fail:
274     for (el = bitmaps; el; el = el->next) {
275         bdrv_release_dirty_bitmap(el->data);
276     }
277     g_slist_free(bitmaps);
278 
279     return -EINVAL;
280 }
281 
282 int parallels_read_format_extension(BlockDriverState *bs,
283                                     int64_t ext_off, Error **errp)
284 {
285     BDRVParallelsState *s = bs->opaque;
286     int ret;
287     uint8_t *ext_cluster = qemu_blockalign(bs, s->cluster_size);
288 
289     assert(ext_off > 0);
290 
291     ret = bdrv_pread(bs->file, ext_off, s->cluster_size, ext_cluster, 0);
292     if (ret < 0) {
293         error_setg_errno(errp, -ret, "Failed to read Format Extension cluster");
294         goto out;
295     }
296 
297     ret = parallels_parse_format_extension(bs, ext_cluster, errp);
298 
299 out:
300     qemu_vfree(ext_cluster);
301 
302     return ret;
303 }
304