xref: /qemu/block/dmg.c (revision 6fb0022b)
1019d6b8fSAnthony Liguori /*
2019d6b8fSAnthony Liguori  * QEMU Block driver for DMG images
3019d6b8fSAnthony Liguori  *
4019d6b8fSAnthony Liguori  * Copyright (c) 2004 Johannes E. Schindelin
5019d6b8fSAnthony Liguori  *
6019d6b8fSAnthony Liguori  * Permission is hereby granted, free of charge, to any person obtaining a copy
7019d6b8fSAnthony Liguori  * of this software and associated documentation files (the "Software"), to deal
8019d6b8fSAnthony Liguori  * in the Software without restriction, including without limitation the rights
9019d6b8fSAnthony Liguori  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10019d6b8fSAnthony Liguori  * copies of the Software, and to permit persons to whom the Software is
11019d6b8fSAnthony Liguori  * furnished to do so, subject to the following conditions:
12019d6b8fSAnthony Liguori  *
13019d6b8fSAnthony Liguori  * The above copyright notice and this permission notice shall be included in
14019d6b8fSAnthony Liguori  * all copies or substantial portions of the Software.
15019d6b8fSAnthony Liguori  *
16019d6b8fSAnthony Liguori  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17019d6b8fSAnthony Liguori  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18019d6b8fSAnthony Liguori  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19019d6b8fSAnthony Liguori  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20019d6b8fSAnthony Liguori  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21019d6b8fSAnthony Liguori  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22019d6b8fSAnthony Liguori  * THE SOFTWARE.
23019d6b8fSAnthony Liguori  */
2480c71a24SPeter Maydell #include "qemu/osdep.h"
25da34e65cSMarkus Armbruster #include "qapi/error.h"
26019d6b8fSAnthony Liguori #include "qemu-common.h"
27737e150eSPaolo Bonzini #include "block/block_int.h"
281de7afc9SPaolo Bonzini #include "qemu/bswap.h"
29d49b6836SMarkus Armbruster #include "qemu/error-report.h"
301de7afc9SPaolo Bonzini #include "qemu/module.h"
3127685a8dSFam Zheng #include "dmg.h"
3227685a8dSFam Zheng 
3327685a8dSFam Zheng int (*dmg_uncompress_bz2)(char *next_in, unsigned int avail_in,
3427685a8dSFam Zheng                           char *next_out, unsigned int avail_out);
35019d6b8fSAnthony Liguori 
36c165f775SStefan Hajnoczi enum {
37c165f775SStefan Hajnoczi     /* Limit chunk sizes to prevent unreasonable amounts of memory being used
38c165f775SStefan Hajnoczi      * or truncating when converting to 32-bit types
39c165f775SStefan Hajnoczi      */
40c165f775SStefan Hajnoczi     DMG_LENGTHS_MAX = 64 * 1024 * 1024, /* 64 MB */
41c165f775SStefan Hajnoczi     DMG_SECTORCOUNTS_MAX = DMG_LENGTHS_MAX / 512,
42c165f775SStefan Hajnoczi };
43c165f775SStefan Hajnoczi 
44019d6b8fSAnthony Liguori static int dmg_probe(const uint8_t *buf, int buf_size, const char *filename)
45019d6b8fSAnthony Liguori {
46f5866fa4SKevin Wolf     int len;
47f5866fa4SKevin Wolf 
48f5866fa4SKevin Wolf     if (!filename) {
49f5866fa4SKevin Wolf         return 0;
50f5866fa4SKevin Wolf     }
51f5866fa4SKevin Wolf 
52f5866fa4SKevin Wolf     len = strlen(filename);
53f5866fa4SKevin Wolf     if (len > 4 && !strcmp(filename + len - 4, ".dmg")) {
54019d6b8fSAnthony Liguori         return 2;
55f5866fa4SKevin Wolf     }
56019d6b8fSAnthony Liguori     return 0;
57019d6b8fSAnthony Liguori }
58019d6b8fSAnthony Liguori 
5969d34a36SKevin Wolf static int read_uint64(BlockDriverState *bs, int64_t offset, uint64_t *result)
60019d6b8fSAnthony Liguori {
61019d6b8fSAnthony Liguori     uint64_t buffer;
6269d34a36SKevin Wolf     int ret;
6369d34a36SKevin Wolf 
64cf2ab8fcSKevin Wolf     ret = bdrv_pread(bs->file, offset, &buffer, 8);
6569d34a36SKevin Wolf     if (ret < 0) {
6669d34a36SKevin Wolf         return ret;
67019d6b8fSAnthony Liguori     }
68019d6b8fSAnthony Liguori 
6969d34a36SKevin Wolf     *result = be64_to_cpu(buffer);
7069d34a36SKevin Wolf     return 0;
7169d34a36SKevin Wolf }
7269d34a36SKevin Wolf 
7369d34a36SKevin Wolf static int read_uint32(BlockDriverState *bs, int64_t offset, uint32_t *result)
74019d6b8fSAnthony Liguori {
75019d6b8fSAnthony Liguori     uint32_t buffer;
7669d34a36SKevin Wolf     int ret;
7769d34a36SKevin Wolf 
78cf2ab8fcSKevin Wolf     ret = bdrv_pread(bs->file, offset, &buffer, 4);
7969d34a36SKevin Wolf     if (ret < 0) {
8069d34a36SKevin Wolf         return ret;
8169d34a36SKevin Wolf     }
8269d34a36SKevin Wolf 
8369d34a36SKevin Wolf     *result = be32_to_cpu(buffer);
84019d6b8fSAnthony Liguori     return 0;
85019d6b8fSAnthony Liguori }
86019d6b8fSAnthony Liguori 
877aee37b9SPeter Wu static inline uint64_t buff_read_uint64(const uint8_t *buffer, int64_t offset)
887aee37b9SPeter Wu {
897aee37b9SPeter Wu     return be64_to_cpu(*(uint64_t *)&buffer[offset]);
907aee37b9SPeter Wu }
917aee37b9SPeter Wu 
927aee37b9SPeter Wu static inline uint32_t buff_read_uint32(const uint8_t *buffer, int64_t offset)
937aee37b9SPeter Wu {
947aee37b9SPeter Wu     return be32_to_cpu(*(uint32_t *)&buffer[offset]);
957aee37b9SPeter Wu }
967aee37b9SPeter Wu 
97f0dce234SStefan Hajnoczi /* Increase max chunk sizes, if necessary.  This function is used to calculate
98f0dce234SStefan Hajnoczi  * the buffer sizes needed for compressed/uncompressed chunk I/O.
99f0dce234SStefan Hajnoczi  */
100f0dce234SStefan Hajnoczi static void update_max_chunk_size(BDRVDMGState *s, uint32_t chunk,
101f0dce234SStefan Hajnoczi                                   uint32_t *max_compressed_size,
102f0dce234SStefan Hajnoczi                                   uint32_t *max_sectors_per_chunk)
103f0dce234SStefan Hajnoczi {
104f0dce234SStefan Hajnoczi     uint32_t compressed_size = 0;
105f0dce234SStefan Hajnoczi     uint32_t uncompressed_sectors = 0;
106f0dce234SStefan Hajnoczi 
107f0dce234SStefan Hajnoczi     switch (s->types[chunk]) {
108f0dce234SStefan Hajnoczi     case 0x80000005: /* zlib compressed */
1096b383c08SPeter Wu     case 0x80000006: /* bzip2 compressed */
110f0dce234SStefan Hajnoczi         compressed_size = s->lengths[chunk];
111f0dce234SStefan Hajnoczi         uncompressed_sectors = s->sectorcounts[chunk];
112f0dce234SStefan Hajnoczi         break;
113f0dce234SStefan Hajnoczi     case 1: /* copy */
114*6fb0022bSMarc-André Lureau         uncompressed_sectors = DIV_ROUND_UP(s->lengths[chunk], 512);
115f0dce234SStefan Hajnoczi         break;
116f0dce234SStefan Hajnoczi     case 2: /* zero */
117177b7510SPeter Wu         /* as the all-zeroes block may be large, it is treated specially: the
118177b7510SPeter Wu          * sector is not copied from a large buffer, a simple memset is used
119177b7510SPeter Wu          * instead. Therefore uncompressed_sectors does not need to be set. */
120f0dce234SStefan Hajnoczi         break;
121f0dce234SStefan Hajnoczi     }
122f0dce234SStefan Hajnoczi 
123f0dce234SStefan Hajnoczi     if (compressed_size > *max_compressed_size) {
124f0dce234SStefan Hajnoczi         *max_compressed_size = compressed_size;
125f0dce234SStefan Hajnoczi     }
126f0dce234SStefan Hajnoczi     if (uncompressed_sectors > *max_sectors_per_chunk) {
127f0dce234SStefan Hajnoczi         *max_sectors_per_chunk = uncompressed_sectors;
128f0dce234SStefan Hajnoczi     }
129f0dce234SStefan Hajnoczi }
130f0dce234SStefan Hajnoczi 
131cf2ab8fcSKevin Wolf static int64_t dmg_find_koly_offset(BdrvChild *file, Error **errp)
132fa8354bdSPeter Wu {
133cf2ab8fcSKevin Wolf     BlockDriverState *file_bs = file->bs;
134fa8354bdSPeter Wu     int64_t length;
135fa8354bdSPeter Wu     int64_t offset = 0;
136fa8354bdSPeter Wu     uint8_t buffer[515];
137fa8354bdSPeter Wu     int i, ret;
138fa8354bdSPeter Wu 
139fa8354bdSPeter Wu     /* bdrv_getlength returns a multiple of block size (512), rounded up. Since
140fa8354bdSPeter Wu      * dmg images can have odd sizes, try to look for the "koly" magic which
141fa8354bdSPeter Wu      * marks the begin of the UDIF trailer (512 bytes). This magic can be found
142fa8354bdSPeter Wu      * in the last 511 bytes of the second-last sector or the first 4 bytes of
143fa8354bdSPeter Wu      * the last sector (search space: 515 bytes) */
144fa8354bdSPeter Wu     length = bdrv_getlength(file_bs);
145fa8354bdSPeter Wu     if (length < 0) {
146fa8354bdSPeter Wu         error_setg_errno(errp, -length,
147fa8354bdSPeter Wu             "Failed to get file size while reading UDIF trailer");
148fa8354bdSPeter Wu         return length;
149fa8354bdSPeter Wu     } else if (length < 512) {
150fa8354bdSPeter Wu         error_setg(errp, "dmg file must be at least 512 bytes long");
151fa8354bdSPeter Wu         return -EINVAL;
152fa8354bdSPeter Wu     }
153fa8354bdSPeter Wu     if (length > 511 + 512) {
154fa8354bdSPeter Wu         offset = length - 511 - 512;
155fa8354bdSPeter Wu     }
156fa8354bdSPeter Wu     length = length < 515 ? length : 515;
157cf2ab8fcSKevin Wolf     ret = bdrv_pread(file, offset, buffer, length);
158fa8354bdSPeter Wu     if (ret < 0) {
159fa8354bdSPeter Wu         error_setg_errno(errp, -ret, "Failed while reading UDIF trailer");
160fa8354bdSPeter Wu         return ret;
161fa8354bdSPeter Wu     }
162fa8354bdSPeter Wu     for (i = 0; i < length - 3; i++) {
163fa8354bdSPeter Wu         if (buffer[i] == 'k' && buffer[i+1] == 'o' &&
164fa8354bdSPeter Wu             buffer[i+2] == 'l' && buffer[i+3] == 'y') {
165fa8354bdSPeter Wu             return offset + i;
166fa8354bdSPeter Wu         }
167fa8354bdSPeter Wu     }
168fa8354bdSPeter Wu     error_setg(errp, "Could not locate UDIF trailer in dmg file");
169fa8354bdSPeter Wu     return -EINVAL;
170fa8354bdSPeter Wu }
171fa8354bdSPeter Wu 
17265a1c7c9SPeter Wu /* used when building the sector table */
17365a1c7c9SPeter Wu typedef struct DmgHeaderState {
17465a1c7c9SPeter Wu     /* used internally by dmg_read_mish_block to remember offsets of blocks
17565a1c7c9SPeter Wu      * across calls */
176c6d34865SPeter Wu     uint64_t data_fork_offset;
17765a1c7c9SPeter Wu     /* exported for dmg_open */
17865a1c7c9SPeter Wu     uint32_t max_compressed_size;
17965a1c7c9SPeter Wu     uint32_t max_sectors_per_chunk;
18065a1c7c9SPeter Wu } DmgHeaderState;
18165a1c7c9SPeter Wu 
182a8b10c6eSPeter Wu static bool dmg_is_known_block_type(uint32_t entry_type)
183a8b10c6eSPeter Wu {
184a8b10c6eSPeter Wu     switch (entry_type) {
185a8b10c6eSPeter Wu     case 0x00000001:    /* uncompressed */
186a8b10c6eSPeter Wu     case 0x00000002:    /* zeroes */
187a8b10c6eSPeter Wu     case 0x80000005:    /* zlib */
188a8b10c6eSPeter Wu         return true;
18927685a8dSFam Zheng     case 0x80000006:    /* bzip2 */
19027685a8dSFam Zheng         return !!dmg_uncompress_bz2;
191a8b10c6eSPeter Wu     default:
192a8b10c6eSPeter Wu         return false;
193a8b10c6eSPeter Wu     }
194a8b10c6eSPeter Wu }
195a8b10c6eSPeter Wu 
1967aee37b9SPeter Wu static int dmg_read_mish_block(BDRVDMGState *s, DmgHeaderState *ds,
1977aee37b9SPeter Wu                                uint8_t *buffer, uint32_t count)
198019d6b8fSAnthony Liguori {
19965a1c7c9SPeter Wu     uint32_t type, i;
20069d34a36SKevin Wolf     int ret;
20165a1c7c9SPeter Wu     size_t new_size;
20265a1c7c9SPeter Wu     uint32_t chunk_count;
2037aee37b9SPeter Wu     int64_t offset = 0;
204c6d34865SPeter Wu     uint64_t data_offset;
205c6d34865SPeter Wu     uint64_t in_offset = ds->data_fork_offset;
20666ec3bbaSPeter Wu     uint64_t out_offset;
20716cdf7ceSChristoph Hellwig 
2087aee37b9SPeter Wu     type = buff_read_uint32(buffer, offset);
20965a1c7c9SPeter Wu     /* skip data that is not a valid MISH block (invalid magic or too small) */
21065a1c7c9SPeter Wu     if (type != 0x6d697368 || count < 244) {
21165a1c7c9SPeter Wu         /* assume success for now */
21265a1c7c9SPeter Wu         return 0;
21365a1c7c9SPeter Wu     }
21416cdf7ceSChristoph Hellwig 
21566ec3bbaSPeter Wu     /* chunk offsets are relative to this sector number */
21666ec3bbaSPeter Wu     out_offset = buff_read_uint64(buffer, offset + 8);
21766ec3bbaSPeter Wu 
218c6d34865SPeter Wu     /* location in data fork for (compressed) blob (in bytes) */
219c6d34865SPeter Wu     data_offset = buff_read_uint64(buffer, offset + 0x18);
220c6d34865SPeter Wu     in_offset += data_offset;
221c6d34865SPeter Wu 
222c6d34865SPeter Wu     /* move to begin of chunk entries */
223c6d34865SPeter Wu     offset += 204;
22416cdf7ceSChristoph Hellwig 
225019d6b8fSAnthony Liguori     chunk_count = (count - 204) / 40;
226019d6b8fSAnthony Liguori     new_size = sizeof(uint64_t) * (s->n_chunks + chunk_count);
2277267c094SAnthony Liguori     s->types = g_realloc(s->types, new_size / 2);
2287267c094SAnthony Liguori     s->offsets = g_realloc(s->offsets, new_size);
2297267c094SAnthony Liguori     s->lengths = g_realloc(s->lengths, new_size);
2307267c094SAnthony Liguori     s->sectors = g_realloc(s->sectors, new_size);
2317267c094SAnthony Liguori     s->sectorcounts = g_realloc(s->sectorcounts, new_size);
232019d6b8fSAnthony Liguori 
233019d6b8fSAnthony Liguori     for (i = s->n_chunks; i < s->n_chunks + chunk_count; i++) {
2347aee37b9SPeter Wu         s->types[i] = buff_read_uint32(buffer, offset);
235a8b10c6eSPeter Wu         if (!dmg_is_known_block_type(s->types[i])) {
236019d6b8fSAnthony Liguori             chunk_count--;
237019d6b8fSAnthony Liguori             i--;
238a8b10c6eSPeter Wu             offset += 40;
239019d6b8fSAnthony Liguori             continue;
240019d6b8fSAnthony Liguori         }
24116cdf7ceSChristoph Hellwig 
242a8b10c6eSPeter Wu         /* sector number */
243a8b10c6eSPeter Wu         s->sectors[i] = buff_read_uint64(buffer, offset + 8);
24466ec3bbaSPeter Wu         s->sectors[i] += out_offset;
24516cdf7ceSChristoph Hellwig 
246a8b10c6eSPeter Wu         /* sector count */
247a8b10c6eSPeter Wu         s->sectorcounts[i] = buff_read_uint64(buffer, offset + 0x10);
24816cdf7ceSChristoph Hellwig 
249177b7510SPeter Wu         /* all-zeroes sector (type 2) does not need to be "uncompressed" and can
250177b7510SPeter Wu          * therefore be unbounded. */
251177b7510SPeter Wu         if (s->types[i] != 2 && s->sectorcounts[i] > DMG_SECTORCOUNTS_MAX) {
252521b2b5dSMax Reitz             error_report("sector count %" PRIu64 " for chunk %" PRIu32
253521b2b5dSMax Reitz                          " is larger than max (%u)",
254c165f775SStefan Hajnoczi                          s->sectorcounts[i], i, DMG_SECTORCOUNTS_MAX);
255c165f775SStefan Hajnoczi             ret = -EINVAL;
256c165f775SStefan Hajnoczi             goto fail;
257c165f775SStefan Hajnoczi         }
258c165f775SStefan Hajnoczi 
259a8b10c6eSPeter Wu         /* offset in (compressed) data fork */
260a8b10c6eSPeter Wu         s->offsets[i] = buff_read_uint64(buffer, offset + 0x18);
261c6d34865SPeter Wu         s->offsets[i] += in_offset;
26216cdf7ceSChristoph Hellwig 
263a8b10c6eSPeter Wu         /* length in (compressed) data fork */
264a8b10c6eSPeter Wu         s->lengths[i] = buff_read_uint64(buffer, offset + 0x20);
26516cdf7ceSChristoph Hellwig 
266c165f775SStefan Hajnoczi         if (s->lengths[i] > DMG_LENGTHS_MAX) {
267521b2b5dSMax Reitz             error_report("length %" PRIu64 " for chunk %" PRIu32
268521b2b5dSMax Reitz                          " is larger than max (%u)",
269c165f775SStefan Hajnoczi                          s->lengths[i], i, DMG_LENGTHS_MAX);
270c165f775SStefan Hajnoczi             ret = -EINVAL;
271c165f775SStefan Hajnoczi             goto fail;
272c165f775SStefan Hajnoczi         }
273c165f775SStefan Hajnoczi 
27465a1c7c9SPeter Wu         update_max_chunk_size(s, i, &ds->max_compressed_size,
27565a1c7c9SPeter Wu                               &ds->max_sectors_per_chunk);
276a8b10c6eSPeter Wu         offset += 40;
2772c1885adSStefan Hajnoczi     }
278019d6b8fSAnthony Liguori     s->n_chunks += chunk_count;
27965a1c7c9SPeter Wu     return 0;
28065a1c7c9SPeter Wu 
28165a1c7c9SPeter Wu fail:
28265a1c7c9SPeter Wu     return ret;
283019d6b8fSAnthony Liguori }
28465a1c7c9SPeter Wu 
285b0e8dc5dSPeter Wu static int dmg_read_resource_fork(BlockDriverState *bs, DmgHeaderState *ds,
286b0e8dc5dSPeter Wu                                   uint64_t info_begin, uint64_t info_length)
287b0e8dc5dSPeter Wu {
2887aee37b9SPeter Wu     BDRVDMGState *s = bs->opaque;
289b0e8dc5dSPeter Wu     int ret;
290b0e8dc5dSPeter Wu     uint32_t count, rsrc_data_offset;
2917aee37b9SPeter Wu     uint8_t *buffer = NULL;
292b0e8dc5dSPeter Wu     uint64_t info_end;
293b0e8dc5dSPeter Wu     uint64_t offset;
294b0e8dc5dSPeter Wu 
295b0e8dc5dSPeter Wu     /* read offset from begin of resource fork (info_begin) to resource data */
296b0e8dc5dSPeter Wu     ret = read_uint32(bs, info_begin, &rsrc_data_offset);
297b0e8dc5dSPeter Wu     if (ret < 0) {
298b0e8dc5dSPeter Wu         goto fail;
299b0e8dc5dSPeter Wu     } else if (rsrc_data_offset > info_length) {
300b0e8dc5dSPeter Wu         ret = -EINVAL;
301b0e8dc5dSPeter Wu         goto fail;
302b0e8dc5dSPeter Wu     }
303b0e8dc5dSPeter Wu 
304b0e8dc5dSPeter Wu     /* read length of resource data */
305b0e8dc5dSPeter Wu     ret = read_uint32(bs, info_begin + 8, &count);
306b0e8dc5dSPeter Wu     if (ret < 0) {
307b0e8dc5dSPeter Wu         goto fail;
308b0e8dc5dSPeter Wu     } else if (count == 0 || rsrc_data_offset + count > info_length) {
309b0e8dc5dSPeter Wu         ret = -EINVAL;
310b0e8dc5dSPeter Wu         goto fail;
311b0e8dc5dSPeter Wu     }
312b0e8dc5dSPeter Wu 
313b0e8dc5dSPeter Wu     /* begin of resource data (consisting of one or more resources) */
314b0e8dc5dSPeter Wu     offset = info_begin + rsrc_data_offset;
315b0e8dc5dSPeter Wu 
316b0e8dc5dSPeter Wu     /* end of resource data (there is possibly a following resource map
317b0e8dc5dSPeter Wu      * which will be ignored). */
318b0e8dc5dSPeter Wu     info_end = offset + count;
319b0e8dc5dSPeter Wu 
320b0e8dc5dSPeter Wu     /* read offsets (mish blocks) from one or more resources in resource data */
321b0e8dc5dSPeter Wu     while (offset < info_end) {
322b0e8dc5dSPeter Wu         /* size of following resource */
323b0e8dc5dSPeter Wu         ret = read_uint32(bs, offset, &count);
324b0e8dc5dSPeter Wu         if (ret < 0) {
325b0e8dc5dSPeter Wu             goto fail;
326f6e6652dSPeter Wu         } else if (count == 0 || count > info_end - offset) {
327b0e8dc5dSPeter Wu             ret = -EINVAL;
328b0e8dc5dSPeter Wu             goto fail;
329b0e8dc5dSPeter Wu         }
330b0e8dc5dSPeter Wu         offset += 4;
331b0e8dc5dSPeter Wu 
3327aee37b9SPeter Wu         buffer = g_realloc(buffer, count);
333cf2ab8fcSKevin Wolf         ret = bdrv_pread(bs->file, offset, buffer, count);
3347aee37b9SPeter Wu         if (ret < 0) {
3357aee37b9SPeter Wu             goto fail;
3367aee37b9SPeter Wu         }
3377aee37b9SPeter Wu 
3387aee37b9SPeter Wu         ret = dmg_read_mish_block(s, ds, buffer, count);
339b0e8dc5dSPeter Wu         if (ret < 0) {
340b0e8dc5dSPeter Wu             goto fail;
341b0e8dc5dSPeter Wu         }
342b0e8dc5dSPeter Wu         /* advance offset by size of resource */
343b0e8dc5dSPeter Wu         offset += count;
344b0e8dc5dSPeter Wu     }
3457aee37b9SPeter Wu     ret = 0;
346b0e8dc5dSPeter Wu 
347b0e8dc5dSPeter Wu fail:
3487aee37b9SPeter Wu     g_free(buffer);
349b0e8dc5dSPeter Wu     return ret;
350b0e8dc5dSPeter Wu }
351b0e8dc5dSPeter Wu 
3520599e56eSPeter Wu static int dmg_read_plist_xml(BlockDriverState *bs, DmgHeaderState *ds,
3530599e56eSPeter Wu                               uint64_t info_begin, uint64_t info_length)
3540599e56eSPeter Wu {
3550599e56eSPeter Wu     BDRVDMGState *s = bs->opaque;
3560599e56eSPeter Wu     int ret;
3570599e56eSPeter Wu     uint8_t *buffer = NULL;
3580599e56eSPeter Wu     char *data_begin, *data_end;
3590599e56eSPeter Wu 
3600599e56eSPeter Wu     /* Have at least some length to avoid NULL for g_malloc. Attempt to set a
3610599e56eSPeter Wu      * safe upper cap on the data length. A test sample had a XML length of
3620599e56eSPeter Wu      * about 1 MiB. */
3630599e56eSPeter Wu     if (info_length == 0 || info_length > 16 * 1024 * 1024) {
3640599e56eSPeter Wu         ret = -EINVAL;
3650599e56eSPeter Wu         goto fail;
3660599e56eSPeter Wu     }
3670599e56eSPeter Wu 
3680599e56eSPeter Wu     buffer = g_malloc(info_length + 1);
3690599e56eSPeter Wu     buffer[info_length] = '\0';
370cf2ab8fcSKevin Wolf     ret = bdrv_pread(bs->file, info_begin, buffer, info_length);
3710599e56eSPeter Wu     if (ret != info_length) {
3720599e56eSPeter Wu         ret = -EINVAL;
3730599e56eSPeter Wu         goto fail;
3740599e56eSPeter Wu     }
3750599e56eSPeter Wu 
3760599e56eSPeter Wu     /* look for <data>...</data>. The data is 284 (0x11c) bytes after base64
3770599e56eSPeter Wu      * decode. The actual data element has 431 (0x1af) bytes which includes tabs
3780599e56eSPeter Wu      * and line feeds. */
3790599e56eSPeter Wu     data_end = (char *)buffer;
3800599e56eSPeter Wu     while ((data_begin = strstr(data_end, "<data>")) != NULL) {
3810599e56eSPeter Wu         guchar *mish;
3820599e56eSPeter Wu         gsize out_len = 0;
3830599e56eSPeter Wu 
3840599e56eSPeter Wu         data_begin += 6;
3850599e56eSPeter Wu         data_end = strstr(data_begin, "</data>");
3860599e56eSPeter Wu         /* malformed XML? */
3870599e56eSPeter Wu         if (data_end == NULL) {
3880599e56eSPeter Wu             ret = -EINVAL;
3890599e56eSPeter Wu             goto fail;
3900599e56eSPeter Wu         }
3910599e56eSPeter Wu         *data_end++ = '\0';
3920599e56eSPeter Wu         mish = g_base64_decode(data_begin, &out_len);
3930599e56eSPeter Wu         ret = dmg_read_mish_block(s, ds, mish, (uint32_t)out_len);
3940599e56eSPeter Wu         g_free(mish);
3950599e56eSPeter Wu         if (ret < 0) {
3960599e56eSPeter Wu             goto fail;
3970599e56eSPeter Wu         }
3980599e56eSPeter Wu     }
3990599e56eSPeter Wu     ret = 0;
4000599e56eSPeter Wu 
4010599e56eSPeter Wu fail:
4020599e56eSPeter Wu     g_free(buffer);
4030599e56eSPeter Wu     return ret;
4040599e56eSPeter Wu }
4050599e56eSPeter Wu 
40665a1c7c9SPeter Wu static int dmg_open(BlockDriverState *bs, QDict *options, int flags,
40765a1c7c9SPeter Wu                     Error **errp)
40865a1c7c9SPeter Wu {
40965a1c7c9SPeter Wu     BDRVDMGState *s = bs->opaque;
41065a1c7c9SPeter Wu     DmgHeaderState ds;
411b0e8dc5dSPeter Wu     uint64_t rsrc_fork_offset, rsrc_fork_length;
4120599e56eSPeter Wu     uint64_t plist_xml_offset, plist_xml_length;
41365a1c7c9SPeter Wu     int64_t offset;
41465a1c7c9SPeter Wu     int ret;
41565a1c7c9SPeter Wu 
4164e4bf5c4SKevin Wolf     bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
4174e4bf5c4SKevin Wolf                                false, errp);
4184e4bf5c4SKevin Wolf     if (!bs->file) {
4194e4bf5c4SKevin Wolf         return -EINVAL;
4204e4bf5c4SKevin Wolf     }
4214e4bf5c4SKevin Wolf 
422e2b8247aSJeff Cody     ret = bdrv_set_read_only(bs, true, errp);
423e2b8247aSJeff Cody     if (ret < 0) {
424e2b8247aSJeff Cody         return ret;
425e2b8247aSJeff Cody     }
426e2b8247aSJeff Cody 
42727685a8dSFam Zheng     block_module_load_one("dmg-bz2");
4283edf1e73SKevin Wolf 
42965a1c7c9SPeter Wu     s->n_chunks = 0;
43065a1c7c9SPeter Wu     s->offsets = s->lengths = s->sectors = s->sectorcounts = NULL;
43165a1c7c9SPeter Wu     /* used by dmg_read_mish_block to keep track of the current I/O position */
432c6d34865SPeter Wu     ds.data_fork_offset = 0;
43365a1c7c9SPeter Wu     ds.max_compressed_size = 1;
43465a1c7c9SPeter Wu     ds.max_sectors_per_chunk = 1;
43565a1c7c9SPeter Wu 
43665a1c7c9SPeter Wu     /* locate the UDIF trailer */
437cf2ab8fcSKevin Wolf     offset = dmg_find_koly_offset(bs->file, errp);
43865a1c7c9SPeter Wu     if (offset < 0) {
43965a1c7c9SPeter Wu         ret = offset;
44065a1c7c9SPeter Wu         goto fail;
44165a1c7c9SPeter Wu     }
44265a1c7c9SPeter Wu 
443c6d34865SPeter Wu     /* offset of data fork (DataForkOffset) */
444c6d34865SPeter Wu     ret = read_uint64(bs, offset + 0x18, &ds.data_fork_offset);
445c6d34865SPeter Wu     if (ret < 0) {
446c6d34865SPeter Wu         goto fail;
447c6d34865SPeter Wu     } else if (ds.data_fork_offset > offset) {
448c6d34865SPeter Wu         ret = -EINVAL;
449c6d34865SPeter Wu         goto fail;
450c6d34865SPeter Wu     }
451c6d34865SPeter Wu 
452b0e8dc5dSPeter Wu     /* offset of resource fork (RsrcForkOffset) */
453b0e8dc5dSPeter Wu     ret = read_uint64(bs, offset + 0x28, &rsrc_fork_offset);
45465a1c7c9SPeter Wu     if (ret < 0) {
45565a1c7c9SPeter Wu         goto fail;
456b0e8dc5dSPeter Wu     }
457b0e8dc5dSPeter Wu     ret = read_uint64(bs, offset + 0x30, &rsrc_fork_length);
458b0e8dc5dSPeter Wu     if (ret < 0) {
459b0e8dc5dSPeter Wu         goto fail;
460b0e8dc5dSPeter Wu     }
461f6e6652dSPeter Wu     if (rsrc_fork_offset >= offset ||
462f6e6652dSPeter Wu         rsrc_fork_length > offset - rsrc_fork_offset) {
463f6e6652dSPeter Wu         ret = -EINVAL;
464f6e6652dSPeter Wu         goto fail;
465f6e6652dSPeter Wu     }
4660599e56eSPeter Wu     /* offset of property list (XMLOffset) */
4670599e56eSPeter Wu     ret = read_uint64(bs, offset + 0xd8, &plist_xml_offset);
4680599e56eSPeter Wu     if (ret < 0) {
4690599e56eSPeter Wu         goto fail;
4700599e56eSPeter Wu     }
4710599e56eSPeter Wu     ret = read_uint64(bs, offset + 0xe0, &plist_xml_length);
4720599e56eSPeter Wu     if (ret < 0) {
4730599e56eSPeter Wu         goto fail;
4740599e56eSPeter Wu     }
4750599e56eSPeter Wu     if (plist_xml_offset >= offset ||
4760599e56eSPeter Wu         plist_xml_length > offset - plist_xml_offset) {
4770599e56eSPeter Wu         ret = -EINVAL;
4780599e56eSPeter Wu         goto fail;
4790599e56eSPeter Wu     }
4808daf4257SPeter Wu     ret = read_uint64(bs, offset + 0x1ec, (uint64_t *)&bs->total_sectors);
4818daf4257SPeter Wu     if (ret < 0) {
4828daf4257SPeter Wu         goto fail;
4838daf4257SPeter Wu     }
4848daf4257SPeter Wu     if (bs->total_sectors < 0) {
4858daf4257SPeter Wu         ret = -EINVAL;
4868daf4257SPeter Wu         goto fail;
4878daf4257SPeter Wu     }
488b0e8dc5dSPeter Wu     if (rsrc_fork_length != 0) {
489b0e8dc5dSPeter Wu         ret = dmg_read_resource_fork(bs, &ds,
490b0e8dc5dSPeter Wu                                      rsrc_fork_offset, rsrc_fork_length);
491b0e8dc5dSPeter Wu         if (ret < 0) {
492b0e8dc5dSPeter Wu             goto fail;
493b0e8dc5dSPeter Wu         }
4940599e56eSPeter Wu     } else if (plist_xml_length != 0) {
4950599e56eSPeter Wu         ret = dmg_read_plist_xml(bs, &ds, plist_xml_offset, plist_xml_length);
4960599e56eSPeter Wu         if (ret < 0) {
4970599e56eSPeter Wu             goto fail;
4980599e56eSPeter Wu         }
499b0e8dc5dSPeter Wu     } else {
50065a1c7c9SPeter Wu         ret = -EINVAL;
50165a1c7c9SPeter Wu         goto fail;
50265a1c7c9SPeter Wu     }
50365a1c7c9SPeter Wu 
504019d6b8fSAnthony Liguori     /* initialize zlib engine */
5059a4f4c31SKevin Wolf     s->compressed_chunk = qemu_try_blockalign(bs->file->bs,
50665a1c7c9SPeter Wu                                               ds.max_compressed_size + 1);
5079a4f4c31SKevin Wolf     s->uncompressed_chunk = qemu_try_blockalign(bs->file->bs,
50865a1c7c9SPeter Wu                                                 512 * ds.max_sectors_per_chunk);
509b546a944SKevin Wolf     if (s->compressed_chunk == NULL || s->uncompressed_chunk == NULL) {
510b546a944SKevin Wolf         ret = -ENOMEM;
511b546a944SKevin Wolf         goto fail;
512b546a944SKevin Wolf     }
513b546a944SKevin Wolf 
51469d34a36SKevin Wolf     if (inflateInit(&s->zstream) != Z_OK) {
51569d34a36SKevin Wolf         ret = -EINVAL;
5161559ca00SChristoph Hellwig         goto fail;
51769d34a36SKevin Wolf     }
518019d6b8fSAnthony Liguori 
519019d6b8fSAnthony Liguori     s->current_chunk = s->n_chunks;
520019d6b8fSAnthony Liguori 
521848c66e8SPaolo Bonzini     qemu_co_mutex_init(&s->lock);
522019d6b8fSAnthony Liguori     return 0;
52369d34a36SKevin Wolf 
5241559ca00SChristoph Hellwig fail:
52569d34a36SKevin Wolf     g_free(s->types);
52669d34a36SKevin Wolf     g_free(s->offsets);
52769d34a36SKevin Wolf     g_free(s->lengths);
52869d34a36SKevin Wolf     g_free(s->sectors);
52969d34a36SKevin Wolf     g_free(s->sectorcounts);
530b546a944SKevin Wolf     qemu_vfree(s->compressed_chunk);
531b546a944SKevin Wolf     qemu_vfree(s->uncompressed_chunk);
53269d34a36SKevin Wolf     return ret;
533019d6b8fSAnthony Liguori }
534019d6b8fSAnthony Liguori 
535a6506481SEric Blake static void dmg_refresh_limits(BlockDriverState *bs, Error **errp)
536a6506481SEric Blake {
537a5b8dd2cSEric Blake     bs->bl.request_alignment = BDRV_SECTOR_SIZE; /* No sub-sector I/O */
538a6506481SEric Blake }
539a6506481SEric Blake 
540019d6b8fSAnthony Liguori static inline int is_sector_in_chunk(BDRVDMGState* s,
541686d7148SStefan Hajnoczi                 uint32_t chunk_num, uint64_t sector_num)
542019d6b8fSAnthony Liguori {
543019d6b8fSAnthony Liguori     if (chunk_num >= s->n_chunks || s->sectors[chunk_num] > sector_num ||
5442c1885adSStefan Hajnoczi             s->sectors[chunk_num] + s->sectorcounts[chunk_num] <= sector_num) {
545019d6b8fSAnthony Liguori         return 0;
5462c1885adSStefan Hajnoczi     } else {
547019d6b8fSAnthony Liguori         return -1;
548019d6b8fSAnthony Liguori     }
5492c1885adSStefan Hajnoczi }
550019d6b8fSAnthony Liguori 
551686d7148SStefan Hajnoczi static inline uint32_t search_chunk(BDRVDMGState *s, uint64_t sector_num)
552019d6b8fSAnthony Liguori {
553019d6b8fSAnthony Liguori     /* binary search */
554019d6b8fSAnthony Liguori     uint32_t chunk1 = 0, chunk2 = s->n_chunks, chunk3;
555019d6b8fSAnthony Liguori     while (chunk1 != chunk2) {
556019d6b8fSAnthony Liguori         chunk3 = (chunk1 + chunk2) / 2;
5572c1885adSStefan Hajnoczi         if (s->sectors[chunk3] > sector_num) {
558019d6b8fSAnthony Liguori             chunk2 = chunk3;
5592c1885adSStefan Hajnoczi         } else if (s->sectors[chunk3] + s->sectorcounts[chunk3] > sector_num) {
560019d6b8fSAnthony Liguori             return chunk3;
5612c1885adSStefan Hajnoczi         } else {
562019d6b8fSAnthony Liguori             chunk1 = chunk3;
563019d6b8fSAnthony Liguori         }
5642c1885adSStefan Hajnoczi     }
565019d6b8fSAnthony Liguori     return s->n_chunks; /* error */
566019d6b8fSAnthony Liguori }
567019d6b8fSAnthony Liguori 
568686d7148SStefan Hajnoczi static inline int dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num)
569019d6b8fSAnthony Liguori {
57064a31d5cSChristoph Hellwig     BDRVDMGState *s = bs->opaque;
57164a31d5cSChristoph Hellwig 
572019d6b8fSAnthony Liguori     if (!is_sector_in_chunk(s, s->current_chunk, sector_num)) {
573019d6b8fSAnthony Liguori         int ret;
574019d6b8fSAnthony Liguori         uint32_t chunk = search_chunk(s, sector_num);
575019d6b8fSAnthony Liguori 
5762c1885adSStefan Hajnoczi         if (chunk >= s->n_chunks) {
577019d6b8fSAnthony Liguori             return -1;
5782c1885adSStefan Hajnoczi         }
579019d6b8fSAnthony Liguori 
580019d6b8fSAnthony Liguori         s->current_chunk = s->n_chunks;
5816b383c08SPeter Wu         switch (s->types[chunk]) { /* block entry type */
582019d6b8fSAnthony Liguori         case 0x80000005: { /* zlib compressed */
583019d6b8fSAnthony Liguori             /* we need to buffer, because only the chunk as whole can be
584019d6b8fSAnthony Liguori              * inflated. */
585cf2ab8fcSKevin Wolf             ret = bdrv_pread(bs->file, s->offsets[chunk],
586b404bf85SStefan Hajnoczi                              s->compressed_chunk, s->lengths[chunk]);
5872c1885adSStefan Hajnoczi             if (ret != s->lengths[chunk]) {
588019d6b8fSAnthony Liguori                 return -1;
5892c1885adSStefan Hajnoczi             }
590019d6b8fSAnthony Liguori 
591019d6b8fSAnthony Liguori             s->zstream.next_in = s->compressed_chunk;
592019d6b8fSAnthony Liguori             s->zstream.avail_in = s->lengths[chunk];
593019d6b8fSAnthony Liguori             s->zstream.next_out = s->uncompressed_chunk;
594019d6b8fSAnthony Liguori             s->zstream.avail_out = 512 * s->sectorcounts[chunk];
595019d6b8fSAnthony Liguori             ret = inflateReset(&s->zstream);
5962c1885adSStefan Hajnoczi             if (ret != Z_OK) {
597019d6b8fSAnthony Liguori                 return -1;
5982c1885adSStefan Hajnoczi             }
599019d6b8fSAnthony Liguori             ret = inflate(&s->zstream, Z_FINISH);
6002c1885adSStefan Hajnoczi             if (ret != Z_STREAM_END ||
6012c1885adSStefan Hajnoczi                 s->zstream.total_out != 512 * s->sectorcounts[chunk]) {
602019d6b8fSAnthony Liguori                 return -1;
6032c1885adSStefan Hajnoczi             }
604019d6b8fSAnthony Liguori             break; }
6056b383c08SPeter Wu         case 0x80000006: /* bzip2 compressed */
60627685a8dSFam Zheng             if (!dmg_uncompress_bz2) {
60727685a8dSFam Zheng                 break;
60827685a8dSFam Zheng             }
6096b383c08SPeter Wu             /* we need to buffer, because only the chunk as whole can be
6106b383c08SPeter Wu              * inflated. */
611cf2ab8fcSKevin Wolf             ret = bdrv_pread(bs->file, s->offsets[chunk],
6126b383c08SPeter Wu                              s->compressed_chunk, s->lengths[chunk]);
6136b383c08SPeter Wu             if (ret != s->lengths[chunk]) {
6146b383c08SPeter Wu                 return -1;
6156b383c08SPeter Wu             }
6166b383c08SPeter Wu 
61727685a8dSFam Zheng             ret = dmg_uncompress_bz2((char *)s->compressed_chunk,
61827685a8dSFam Zheng                                      (unsigned int) s->lengths[chunk],
61927685a8dSFam Zheng                                      (char *)s->uncompressed_chunk,
62027685a8dSFam Zheng                                      (unsigned int)
62127685a8dSFam Zheng                                          (512 * s->sectorcounts[chunk]));
62227685a8dSFam Zheng             if (ret < 0) {
62327685a8dSFam Zheng                 return ret;
6246b383c08SPeter Wu             }
6256b383c08SPeter Wu             break;
626019d6b8fSAnthony Liguori         case 1: /* copy */
627cf2ab8fcSKevin Wolf             ret = bdrv_pread(bs->file, s->offsets[chunk],
62864a31d5cSChristoph Hellwig                              s->uncompressed_chunk, s->lengths[chunk]);
6292c1885adSStefan Hajnoczi             if (ret != s->lengths[chunk]) {
630019d6b8fSAnthony Liguori                 return -1;
6312c1885adSStefan Hajnoczi             }
632019d6b8fSAnthony Liguori             break;
633019d6b8fSAnthony Liguori         case 2: /* zero */
634177b7510SPeter Wu             /* see dmg_read, it is treated specially. No buffer needs to be
635177b7510SPeter Wu              * pre-filled, the zeroes can be set directly. */
636019d6b8fSAnthony Liguori             break;
637019d6b8fSAnthony Liguori         }
638019d6b8fSAnthony Liguori         s->current_chunk = chunk;
639019d6b8fSAnthony Liguori     }
640019d6b8fSAnthony Liguori     return 0;
641019d6b8fSAnthony Liguori }
642019d6b8fSAnthony Liguori 
6433edf1e73SKevin Wolf static int coroutine_fn
6443edf1e73SKevin Wolf dmg_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
6453edf1e73SKevin Wolf               QEMUIOVector *qiov, int flags)
646019d6b8fSAnthony Liguori {
647019d6b8fSAnthony Liguori     BDRVDMGState *s = bs->opaque;
6483edf1e73SKevin Wolf     uint64_t sector_num = offset >> BDRV_SECTOR_BITS;
6493edf1e73SKevin Wolf     int nb_sectors = bytes >> BDRV_SECTOR_BITS;
6503edf1e73SKevin Wolf     int ret, i;
6513edf1e73SKevin Wolf 
6523edf1e73SKevin Wolf     assert((offset & (BDRV_SECTOR_SIZE - 1)) == 0);
6533edf1e73SKevin Wolf     assert((bytes & (BDRV_SECTOR_SIZE - 1)) == 0);
6543edf1e73SKevin Wolf 
6553edf1e73SKevin Wolf     qemu_co_mutex_lock(&s->lock);
656019d6b8fSAnthony Liguori 
657019d6b8fSAnthony Liguori     for (i = 0; i < nb_sectors; i++) {
658019d6b8fSAnthony Liguori         uint32_t sector_offset_in_chunk;
6593edf1e73SKevin Wolf         void *data;
6603edf1e73SKevin Wolf 
6612c1885adSStefan Hajnoczi         if (dmg_read_chunk(bs, sector_num + i) != 0) {
6623edf1e73SKevin Wolf             ret = -EIO;
6633edf1e73SKevin Wolf             goto fail;
6642c1885adSStefan Hajnoczi         }
665177b7510SPeter Wu         /* Special case: current chunk is all zeroes. Do not perform a memcpy as
666177b7510SPeter Wu          * s->uncompressed_chunk may be too small to cover the large all-zeroes
667177b7510SPeter Wu          * section. dmg_read_chunk is called to find s->current_chunk */
668177b7510SPeter Wu         if (s->types[s->current_chunk] == 2) { /* all zeroes block entry */
6693edf1e73SKevin Wolf             qemu_iovec_memset(qiov, i * 512, 0, 512);
670177b7510SPeter Wu             continue;
671177b7510SPeter Wu         }
672019d6b8fSAnthony Liguori         sector_offset_in_chunk = sector_num + i - s->sectors[s->current_chunk];
6733edf1e73SKevin Wolf         data = s->uncompressed_chunk + sector_offset_in_chunk * 512;
6743edf1e73SKevin Wolf         qemu_iovec_from_buf(qiov, i * 512, data, 512);
675019d6b8fSAnthony Liguori     }
676019d6b8fSAnthony Liguori 
6773edf1e73SKevin Wolf     ret = 0;
6783edf1e73SKevin Wolf fail:
6792914caa0SPaolo Bonzini     qemu_co_mutex_unlock(&s->lock);
6802914caa0SPaolo Bonzini     return ret;
6812914caa0SPaolo Bonzini }
6822914caa0SPaolo Bonzini 
683019d6b8fSAnthony Liguori static void dmg_close(BlockDriverState *bs)
684019d6b8fSAnthony Liguori {
685019d6b8fSAnthony Liguori     BDRVDMGState *s = bs->opaque;
6864f8aa2e1SKevin Wolf 
6874f8aa2e1SKevin Wolf     g_free(s->types);
6884f8aa2e1SKevin Wolf     g_free(s->offsets);
6894f8aa2e1SKevin Wolf     g_free(s->lengths);
6904f8aa2e1SKevin Wolf     g_free(s->sectors);
6914f8aa2e1SKevin Wolf     g_free(s->sectorcounts);
692b546a944SKevin Wolf     qemu_vfree(s->compressed_chunk);
693b546a944SKevin Wolf     qemu_vfree(s->uncompressed_chunk);
6944f8aa2e1SKevin Wolf 
695019d6b8fSAnthony Liguori     inflateEnd(&s->zstream);
696019d6b8fSAnthony Liguori }
697019d6b8fSAnthony Liguori 
698019d6b8fSAnthony Liguori static BlockDriver bdrv_dmg = {
699019d6b8fSAnthony Liguori     .format_name    = "dmg",
700019d6b8fSAnthony Liguori     .instance_size  = sizeof(BDRVDMGState),
701019d6b8fSAnthony Liguori     .bdrv_probe     = dmg_probe,
70264a31d5cSChristoph Hellwig     .bdrv_open      = dmg_open,
703a6506481SEric Blake     .bdrv_refresh_limits = dmg_refresh_limits,
704862f215fSKevin Wolf     .bdrv_child_perm     = bdrv_format_default_perms,
7053edf1e73SKevin Wolf     .bdrv_co_preadv = dmg_co_preadv,
706019d6b8fSAnthony Liguori     .bdrv_close     = dmg_close,
707019d6b8fSAnthony Liguori };
708019d6b8fSAnthony Liguori 
709019d6b8fSAnthony Liguori static void bdrv_dmg_init(void)
710019d6b8fSAnthony Liguori {
711019d6b8fSAnthony Liguori     bdrv_register(&bdrv_dmg);
712019d6b8fSAnthony Liguori }
713019d6b8fSAnthony Liguori 
714019d6b8fSAnthony Liguori block_init(bdrv_dmg_init);
715