xref: /qemu/block/dmg.c (revision 862f215f)
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 */
114f0dce234SStefan Hajnoczi         uncompressed_sectors = (s->lengths[chunk] + 511) / 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 
42227685a8dSFam Zheng     block_module_load_one("dmg-bz2");
42354115412SEric Blake     bs->read_only = true;
4243edf1e73SKevin Wolf 
42565a1c7c9SPeter Wu     s->n_chunks = 0;
42665a1c7c9SPeter Wu     s->offsets = s->lengths = s->sectors = s->sectorcounts = NULL;
42765a1c7c9SPeter Wu     /* used by dmg_read_mish_block to keep track of the current I/O position */
428c6d34865SPeter Wu     ds.data_fork_offset = 0;
42965a1c7c9SPeter Wu     ds.max_compressed_size = 1;
43065a1c7c9SPeter Wu     ds.max_sectors_per_chunk = 1;
43165a1c7c9SPeter Wu 
43265a1c7c9SPeter Wu     /* locate the UDIF trailer */
433cf2ab8fcSKevin Wolf     offset = dmg_find_koly_offset(bs->file, errp);
43465a1c7c9SPeter Wu     if (offset < 0) {
43565a1c7c9SPeter Wu         ret = offset;
43665a1c7c9SPeter Wu         goto fail;
43765a1c7c9SPeter Wu     }
43865a1c7c9SPeter Wu 
439c6d34865SPeter Wu     /* offset of data fork (DataForkOffset) */
440c6d34865SPeter Wu     ret = read_uint64(bs, offset + 0x18, &ds.data_fork_offset);
441c6d34865SPeter Wu     if (ret < 0) {
442c6d34865SPeter Wu         goto fail;
443c6d34865SPeter Wu     } else if (ds.data_fork_offset > offset) {
444c6d34865SPeter Wu         ret = -EINVAL;
445c6d34865SPeter Wu         goto fail;
446c6d34865SPeter Wu     }
447c6d34865SPeter Wu 
448b0e8dc5dSPeter Wu     /* offset of resource fork (RsrcForkOffset) */
449b0e8dc5dSPeter Wu     ret = read_uint64(bs, offset + 0x28, &rsrc_fork_offset);
45065a1c7c9SPeter Wu     if (ret < 0) {
45165a1c7c9SPeter Wu         goto fail;
452b0e8dc5dSPeter Wu     }
453b0e8dc5dSPeter Wu     ret = read_uint64(bs, offset + 0x30, &rsrc_fork_length);
454b0e8dc5dSPeter Wu     if (ret < 0) {
455b0e8dc5dSPeter Wu         goto fail;
456b0e8dc5dSPeter Wu     }
457f6e6652dSPeter Wu     if (rsrc_fork_offset >= offset ||
458f6e6652dSPeter Wu         rsrc_fork_length > offset - rsrc_fork_offset) {
459f6e6652dSPeter Wu         ret = -EINVAL;
460f6e6652dSPeter Wu         goto fail;
461f6e6652dSPeter Wu     }
4620599e56eSPeter Wu     /* offset of property list (XMLOffset) */
4630599e56eSPeter Wu     ret = read_uint64(bs, offset + 0xd8, &plist_xml_offset);
4640599e56eSPeter Wu     if (ret < 0) {
4650599e56eSPeter Wu         goto fail;
4660599e56eSPeter Wu     }
4670599e56eSPeter Wu     ret = read_uint64(bs, offset + 0xe0, &plist_xml_length);
4680599e56eSPeter Wu     if (ret < 0) {
4690599e56eSPeter Wu         goto fail;
4700599e56eSPeter Wu     }
4710599e56eSPeter Wu     if (plist_xml_offset >= offset ||
4720599e56eSPeter Wu         plist_xml_length > offset - plist_xml_offset) {
4730599e56eSPeter Wu         ret = -EINVAL;
4740599e56eSPeter Wu         goto fail;
4750599e56eSPeter Wu     }
4768daf4257SPeter Wu     ret = read_uint64(bs, offset + 0x1ec, (uint64_t *)&bs->total_sectors);
4778daf4257SPeter Wu     if (ret < 0) {
4788daf4257SPeter Wu         goto fail;
4798daf4257SPeter Wu     }
4808daf4257SPeter Wu     if (bs->total_sectors < 0) {
4818daf4257SPeter Wu         ret = -EINVAL;
4828daf4257SPeter Wu         goto fail;
4838daf4257SPeter Wu     }
484b0e8dc5dSPeter Wu     if (rsrc_fork_length != 0) {
485b0e8dc5dSPeter Wu         ret = dmg_read_resource_fork(bs, &ds,
486b0e8dc5dSPeter Wu                                      rsrc_fork_offset, rsrc_fork_length);
487b0e8dc5dSPeter Wu         if (ret < 0) {
488b0e8dc5dSPeter Wu             goto fail;
489b0e8dc5dSPeter Wu         }
4900599e56eSPeter Wu     } else if (plist_xml_length != 0) {
4910599e56eSPeter Wu         ret = dmg_read_plist_xml(bs, &ds, plist_xml_offset, plist_xml_length);
4920599e56eSPeter Wu         if (ret < 0) {
4930599e56eSPeter Wu             goto fail;
4940599e56eSPeter Wu         }
495b0e8dc5dSPeter Wu     } else {
49665a1c7c9SPeter Wu         ret = -EINVAL;
49765a1c7c9SPeter Wu         goto fail;
49865a1c7c9SPeter Wu     }
49965a1c7c9SPeter Wu 
500019d6b8fSAnthony Liguori     /* initialize zlib engine */
5019a4f4c31SKevin Wolf     s->compressed_chunk = qemu_try_blockalign(bs->file->bs,
50265a1c7c9SPeter Wu                                               ds.max_compressed_size + 1);
5039a4f4c31SKevin Wolf     s->uncompressed_chunk = qemu_try_blockalign(bs->file->bs,
50465a1c7c9SPeter Wu                                                 512 * ds.max_sectors_per_chunk);
505b546a944SKevin Wolf     if (s->compressed_chunk == NULL || s->uncompressed_chunk == NULL) {
506b546a944SKevin Wolf         ret = -ENOMEM;
507b546a944SKevin Wolf         goto fail;
508b546a944SKevin Wolf     }
509b546a944SKevin Wolf 
51069d34a36SKevin Wolf     if (inflateInit(&s->zstream) != Z_OK) {
51169d34a36SKevin Wolf         ret = -EINVAL;
5121559ca00SChristoph Hellwig         goto fail;
51369d34a36SKevin Wolf     }
514019d6b8fSAnthony Liguori 
515019d6b8fSAnthony Liguori     s->current_chunk = s->n_chunks;
516019d6b8fSAnthony Liguori 
517848c66e8SPaolo Bonzini     qemu_co_mutex_init(&s->lock);
518019d6b8fSAnthony Liguori     return 0;
51969d34a36SKevin Wolf 
5201559ca00SChristoph Hellwig fail:
52169d34a36SKevin Wolf     g_free(s->types);
52269d34a36SKevin Wolf     g_free(s->offsets);
52369d34a36SKevin Wolf     g_free(s->lengths);
52469d34a36SKevin Wolf     g_free(s->sectors);
52569d34a36SKevin Wolf     g_free(s->sectorcounts);
526b546a944SKevin Wolf     qemu_vfree(s->compressed_chunk);
527b546a944SKevin Wolf     qemu_vfree(s->uncompressed_chunk);
52869d34a36SKevin Wolf     return ret;
529019d6b8fSAnthony Liguori }
530019d6b8fSAnthony Liguori 
531a6506481SEric Blake static void dmg_refresh_limits(BlockDriverState *bs, Error **errp)
532a6506481SEric Blake {
533a5b8dd2cSEric Blake     bs->bl.request_alignment = BDRV_SECTOR_SIZE; /* No sub-sector I/O */
534a6506481SEric Blake }
535a6506481SEric Blake 
536019d6b8fSAnthony Liguori static inline int is_sector_in_chunk(BDRVDMGState* s,
537686d7148SStefan Hajnoczi                 uint32_t chunk_num, uint64_t sector_num)
538019d6b8fSAnthony Liguori {
539019d6b8fSAnthony Liguori     if (chunk_num >= s->n_chunks || s->sectors[chunk_num] > sector_num ||
5402c1885adSStefan Hajnoczi             s->sectors[chunk_num] + s->sectorcounts[chunk_num] <= sector_num) {
541019d6b8fSAnthony Liguori         return 0;
5422c1885adSStefan Hajnoczi     } else {
543019d6b8fSAnthony Liguori         return -1;
544019d6b8fSAnthony Liguori     }
5452c1885adSStefan Hajnoczi }
546019d6b8fSAnthony Liguori 
547686d7148SStefan Hajnoczi static inline uint32_t search_chunk(BDRVDMGState *s, uint64_t sector_num)
548019d6b8fSAnthony Liguori {
549019d6b8fSAnthony Liguori     /* binary search */
550019d6b8fSAnthony Liguori     uint32_t chunk1 = 0, chunk2 = s->n_chunks, chunk3;
551019d6b8fSAnthony Liguori     while (chunk1 != chunk2) {
552019d6b8fSAnthony Liguori         chunk3 = (chunk1 + chunk2) / 2;
5532c1885adSStefan Hajnoczi         if (s->sectors[chunk3] > sector_num) {
554019d6b8fSAnthony Liguori             chunk2 = chunk3;
5552c1885adSStefan Hajnoczi         } else if (s->sectors[chunk3] + s->sectorcounts[chunk3] > sector_num) {
556019d6b8fSAnthony Liguori             return chunk3;
5572c1885adSStefan Hajnoczi         } else {
558019d6b8fSAnthony Liguori             chunk1 = chunk3;
559019d6b8fSAnthony Liguori         }
5602c1885adSStefan Hajnoczi     }
561019d6b8fSAnthony Liguori     return s->n_chunks; /* error */
562019d6b8fSAnthony Liguori }
563019d6b8fSAnthony Liguori 
564686d7148SStefan Hajnoczi static inline int dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num)
565019d6b8fSAnthony Liguori {
56664a31d5cSChristoph Hellwig     BDRVDMGState *s = bs->opaque;
56764a31d5cSChristoph Hellwig 
568019d6b8fSAnthony Liguori     if (!is_sector_in_chunk(s, s->current_chunk, sector_num)) {
569019d6b8fSAnthony Liguori         int ret;
570019d6b8fSAnthony Liguori         uint32_t chunk = search_chunk(s, sector_num);
571019d6b8fSAnthony Liguori 
5722c1885adSStefan Hajnoczi         if (chunk >= s->n_chunks) {
573019d6b8fSAnthony Liguori             return -1;
5742c1885adSStefan Hajnoczi         }
575019d6b8fSAnthony Liguori 
576019d6b8fSAnthony Liguori         s->current_chunk = s->n_chunks;
5776b383c08SPeter Wu         switch (s->types[chunk]) { /* block entry type */
578019d6b8fSAnthony Liguori         case 0x80000005: { /* zlib compressed */
579019d6b8fSAnthony Liguori             /* we need to buffer, because only the chunk as whole can be
580019d6b8fSAnthony Liguori              * inflated. */
581cf2ab8fcSKevin Wolf             ret = bdrv_pread(bs->file, s->offsets[chunk],
582b404bf85SStefan Hajnoczi                              s->compressed_chunk, s->lengths[chunk]);
5832c1885adSStefan Hajnoczi             if (ret != s->lengths[chunk]) {
584019d6b8fSAnthony Liguori                 return -1;
5852c1885adSStefan Hajnoczi             }
586019d6b8fSAnthony Liguori 
587019d6b8fSAnthony Liguori             s->zstream.next_in = s->compressed_chunk;
588019d6b8fSAnthony Liguori             s->zstream.avail_in = s->lengths[chunk];
589019d6b8fSAnthony Liguori             s->zstream.next_out = s->uncompressed_chunk;
590019d6b8fSAnthony Liguori             s->zstream.avail_out = 512 * s->sectorcounts[chunk];
591019d6b8fSAnthony Liguori             ret = inflateReset(&s->zstream);
5922c1885adSStefan Hajnoczi             if (ret != Z_OK) {
593019d6b8fSAnthony Liguori                 return -1;
5942c1885adSStefan Hajnoczi             }
595019d6b8fSAnthony Liguori             ret = inflate(&s->zstream, Z_FINISH);
5962c1885adSStefan Hajnoczi             if (ret != Z_STREAM_END ||
5972c1885adSStefan Hajnoczi                 s->zstream.total_out != 512 * s->sectorcounts[chunk]) {
598019d6b8fSAnthony Liguori                 return -1;
5992c1885adSStefan Hajnoczi             }
600019d6b8fSAnthony Liguori             break; }
6016b383c08SPeter Wu         case 0x80000006: /* bzip2 compressed */
60227685a8dSFam Zheng             if (!dmg_uncompress_bz2) {
60327685a8dSFam Zheng                 break;
60427685a8dSFam Zheng             }
6056b383c08SPeter Wu             /* we need to buffer, because only the chunk as whole can be
6066b383c08SPeter Wu              * inflated. */
607cf2ab8fcSKevin Wolf             ret = bdrv_pread(bs->file, s->offsets[chunk],
6086b383c08SPeter Wu                              s->compressed_chunk, s->lengths[chunk]);
6096b383c08SPeter Wu             if (ret != s->lengths[chunk]) {
6106b383c08SPeter Wu                 return -1;
6116b383c08SPeter Wu             }
6126b383c08SPeter Wu 
61327685a8dSFam Zheng             ret = dmg_uncompress_bz2((char *)s->compressed_chunk,
61427685a8dSFam Zheng                                      (unsigned int) s->lengths[chunk],
61527685a8dSFam Zheng                                      (char *)s->uncompressed_chunk,
61627685a8dSFam Zheng                                      (unsigned int)
61727685a8dSFam Zheng                                          (512 * s->sectorcounts[chunk]));
61827685a8dSFam Zheng             if (ret < 0) {
61927685a8dSFam Zheng                 return ret;
6206b383c08SPeter Wu             }
6216b383c08SPeter Wu             break;
622019d6b8fSAnthony Liguori         case 1: /* copy */
623cf2ab8fcSKevin Wolf             ret = bdrv_pread(bs->file, s->offsets[chunk],
62464a31d5cSChristoph Hellwig                              s->uncompressed_chunk, s->lengths[chunk]);
6252c1885adSStefan Hajnoczi             if (ret != s->lengths[chunk]) {
626019d6b8fSAnthony Liguori                 return -1;
6272c1885adSStefan Hajnoczi             }
628019d6b8fSAnthony Liguori             break;
629019d6b8fSAnthony Liguori         case 2: /* zero */
630177b7510SPeter Wu             /* see dmg_read, it is treated specially. No buffer needs to be
631177b7510SPeter Wu              * pre-filled, the zeroes can be set directly. */
632019d6b8fSAnthony Liguori             break;
633019d6b8fSAnthony Liguori         }
634019d6b8fSAnthony Liguori         s->current_chunk = chunk;
635019d6b8fSAnthony Liguori     }
636019d6b8fSAnthony Liguori     return 0;
637019d6b8fSAnthony Liguori }
638019d6b8fSAnthony Liguori 
6393edf1e73SKevin Wolf static int coroutine_fn
6403edf1e73SKevin Wolf dmg_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
6413edf1e73SKevin Wolf               QEMUIOVector *qiov, int flags)
642019d6b8fSAnthony Liguori {
643019d6b8fSAnthony Liguori     BDRVDMGState *s = bs->opaque;
6443edf1e73SKevin Wolf     uint64_t sector_num = offset >> BDRV_SECTOR_BITS;
6453edf1e73SKevin Wolf     int nb_sectors = bytes >> BDRV_SECTOR_BITS;
6463edf1e73SKevin Wolf     int ret, i;
6473edf1e73SKevin Wolf 
6483edf1e73SKevin Wolf     assert((offset & (BDRV_SECTOR_SIZE - 1)) == 0);
6493edf1e73SKevin Wolf     assert((bytes & (BDRV_SECTOR_SIZE - 1)) == 0);
6503edf1e73SKevin Wolf 
6513edf1e73SKevin Wolf     qemu_co_mutex_lock(&s->lock);
652019d6b8fSAnthony Liguori 
653019d6b8fSAnthony Liguori     for (i = 0; i < nb_sectors; i++) {
654019d6b8fSAnthony Liguori         uint32_t sector_offset_in_chunk;
6553edf1e73SKevin Wolf         void *data;
6563edf1e73SKevin Wolf 
6572c1885adSStefan Hajnoczi         if (dmg_read_chunk(bs, sector_num + i) != 0) {
6583edf1e73SKevin Wolf             ret = -EIO;
6593edf1e73SKevin Wolf             goto fail;
6602c1885adSStefan Hajnoczi         }
661177b7510SPeter Wu         /* Special case: current chunk is all zeroes. Do not perform a memcpy as
662177b7510SPeter Wu          * s->uncompressed_chunk may be too small to cover the large all-zeroes
663177b7510SPeter Wu          * section. dmg_read_chunk is called to find s->current_chunk */
664177b7510SPeter Wu         if (s->types[s->current_chunk] == 2) { /* all zeroes block entry */
6653edf1e73SKevin Wolf             qemu_iovec_memset(qiov, i * 512, 0, 512);
666177b7510SPeter Wu             continue;
667177b7510SPeter Wu         }
668019d6b8fSAnthony Liguori         sector_offset_in_chunk = sector_num + i - s->sectors[s->current_chunk];
6693edf1e73SKevin Wolf         data = s->uncompressed_chunk + sector_offset_in_chunk * 512;
6703edf1e73SKevin Wolf         qemu_iovec_from_buf(qiov, i * 512, data, 512);
671019d6b8fSAnthony Liguori     }
672019d6b8fSAnthony Liguori 
6733edf1e73SKevin Wolf     ret = 0;
6743edf1e73SKevin Wolf fail:
6752914caa0SPaolo Bonzini     qemu_co_mutex_unlock(&s->lock);
6762914caa0SPaolo Bonzini     return ret;
6772914caa0SPaolo Bonzini }
6782914caa0SPaolo Bonzini 
679019d6b8fSAnthony Liguori static void dmg_close(BlockDriverState *bs)
680019d6b8fSAnthony Liguori {
681019d6b8fSAnthony Liguori     BDRVDMGState *s = bs->opaque;
6824f8aa2e1SKevin Wolf 
6834f8aa2e1SKevin Wolf     g_free(s->types);
6844f8aa2e1SKevin Wolf     g_free(s->offsets);
6854f8aa2e1SKevin Wolf     g_free(s->lengths);
6864f8aa2e1SKevin Wolf     g_free(s->sectors);
6874f8aa2e1SKevin Wolf     g_free(s->sectorcounts);
688b546a944SKevin Wolf     qemu_vfree(s->compressed_chunk);
689b546a944SKevin Wolf     qemu_vfree(s->uncompressed_chunk);
6904f8aa2e1SKevin Wolf 
691019d6b8fSAnthony Liguori     inflateEnd(&s->zstream);
692019d6b8fSAnthony Liguori }
693019d6b8fSAnthony Liguori 
694019d6b8fSAnthony Liguori static BlockDriver bdrv_dmg = {
695019d6b8fSAnthony Liguori     .format_name    = "dmg",
696019d6b8fSAnthony Liguori     .instance_size  = sizeof(BDRVDMGState),
697019d6b8fSAnthony Liguori     .bdrv_probe     = dmg_probe,
69864a31d5cSChristoph Hellwig     .bdrv_open      = dmg_open,
699a6506481SEric Blake     .bdrv_refresh_limits = dmg_refresh_limits,
700*862f215fSKevin Wolf     .bdrv_child_perm     = bdrv_format_default_perms,
7013edf1e73SKevin Wolf     .bdrv_co_preadv = dmg_co_preadv,
702019d6b8fSAnthony Liguori     .bdrv_close     = dmg_close,
703019d6b8fSAnthony Liguori };
704019d6b8fSAnthony Liguori 
705019d6b8fSAnthony Liguori static void bdrv_dmg_init(void)
706019d6b8fSAnthony Liguori {
707019d6b8fSAnthony Liguori     bdrv_register(&bdrv_dmg);
708019d6b8fSAnthony Liguori }
709019d6b8fSAnthony Liguori 
710019d6b8fSAnthony Liguori block_init(bdrv_dmg_init);
711