block/dmg: extract mish block decoding functionality

Extract the mish block decoder such that this can be used for other
formats in the future. A new DmgHeaderState struct is introduced to
share state while decoding.

The code is kept unchanged as much as possible, a "fail" label is added
for example where a simple return would probably do. In dmg_open, the
variable "tmp" is renamed to "rsrc_data_offset" for clarity and comments
have been added explaining various data.

Note that this patch has one subtle difference with the previous
version which should not affect functionality. In the previous code,
the end of a resource was inferred from the mish block (the offsets
would be increased by the fields). In this patch, the resource length
is used instead to avoid the need to rely on the previous offsets.

Signed-off-by: Peter Wu <peter@lekensteyn.nl>
Reviewed-by: John Snow <jsnow@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Message-id: 1420566495-13284-3-git-send-email-peter@lekensteyn.nl
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
This commit is contained in:
Peter Wu 2015-01-06 18:48:05 +01:00 committed by Kevin Wolf
parent fa8354bd22
commit 65a1c7c96a

View File

@ -171,76 +171,36 @@ static int64_t dmg_find_koly_offset(BlockDriverState *file_bs, Error **errp)
return -EINVAL;
}
static int dmg_open(BlockDriverState *bs, QDict *options, int flags,
Error **errp)
/* used when building the sector table */
typedef struct DmgHeaderState {
/* used internally by dmg_read_mish_block to remember offsets of blocks
* across calls */
uint64_t last_in_offset;
uint64_t last_out_offset;
/* exported for dmg_open */
uint32_t max_compressed_size;
uint32_t max_sectors_per_chunk;
} DmgHeaderState;
static int dmg_read_mish_block(BlockDriverState *bs, DmgHeaderState *ds,
int64_t offset, uint32_t count)
{
BDRVDMGState *s = bs->opaque;
uint64_t info_begin, info_end, last_in_offset, last_out_offset;
uint32_t count, tmp;
uint32_t max_compressed_size = 1, max_sectors_per_chunk = 1, i;
int64_t offset;
uint32_t type, i;
int ret;
bs->read_only = 1;
s->n_chunks = 0;
s->offsets = s->lengths = s->sectors = s->sectorcounts = NULL;
/* locate the UDIF trailer */
offset = dmg_find_koly_offset(bs->file, errp);
if (offset < 0) {
ret = offset;
goto fail;
}
ret = read_uint64(bs, offset + 0x28, &info_begin);
if (ret < 0) {
goto fail;
} else if (info_begin == 0) {
ret = -EINVAL;
goto fail;
}
ret = read_uint32(bs, info_begin, &tmp);
if (ret < 0) {
goto fail;
} else if (tmp != 0x100) {
ret = -EINVAL;
goto fail;
}
ret = read_uint32(bs, info_begin + 4, &count);
if (ret < 0) {
goto fail;
} else if (count == 0) {
ret = -EINVAL;
goto fail;
}
info_end = info_begin + count;
offset = info_begin + 0x100;
/* read offsets */
last_in_offset = last_out_offset = 0;
while (offset < info_end) {
uint32_t type;
ret = read_uint32(bs, offset, &count);
if (ret < 0) {
goto fail;
} else if (count == 0) {
ret = -EINVAL;
goto fail;
}
offset += 4;
size_t new_size;
uint32_t chunk_count;
ret = read_uint32(bs, offset, &type);
if (ret < 0) {
goto fail;
}
if (type == 0x6d697368 && count >= 244) {
size_t new_size;
uint32_t chunk_count;
/* skip data that is not a valid MISH block (invalid magic or too small) */
if (type != 0x6d697368 || count < 244) {
/* assume success for now */
return 0;
}
offset += 4;
offset += 200;
@ -262,8 +222,8 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags,
if (s->types[i] != 0x80000005 && s->types[i] != 1 &&
s->types[i] != 2) {
if (s->types[i] == 0xffffffff && i > 0) {
last_in_offset = s->offsets[i - 1] + s->lengths[i - 1];
last_out_offset = s->sectors[i - 1] +
ds->last_in_offset = s->offsets[i - 1] + s->lengths[i - 1];
ds->last_out_offset = s->sectors[i - 1] +
s->sectorcounts[i - 1];
}
chunk_count--;
@ -277,7 +237,7 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags,
if (ret < 0) {
goto fail;
}
s->sectors[i] += last_out_offset;
s->sectors[i] += ds->last_out_offset;
offset += 8;
ret = read_uint64(bs, offset, &s->sectorcounts[i]);
@ -298,7 +258,7 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags,
if (ret < 0) {
goto fail;
}
s->offsets[i] += last_in_offset;
s->offsets[i] += ds->last_in_offset;
offset += 8;
ret = read_uint64(bs, offset, &s->lengths[i]);
@ -315,18 +275,96 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags,
goto fail;
}
update_max_chunk_size(s, i, &max_compressed_size,
&max_sectors_per_chunk);
update_max_chunk_size(s, i, &ds->max_compressed_size,
&ds->max_sectors_per_chunk);
}
s->n_chunks += chunk_count;
return 0;
fail:
return ret;
}
static int dmg_open(BlockDriverState *bs, QDict *options, int flags,
Error **errp)
{
BDRVDMGState *s = bs->opaque;
DmgHeaderState ds;
uint64_t info_begin, info_end;
uint32_t count, rsrc_data_offset;
int64_t offset;
int ret;
bs->read_only = 1;
s->n_chunks = 0;
s->offsets = s->lengths = s->sectors = s->sectorcounts = NULL;
/* used by dmg_read_mish_block to keep track of the current I/O position */
ds.last_in_offset = 0;
ds.last_out_offset = 0;
ds.max_compressed_size = 1;
ds.max_sectors_per_chunk = 1;
/* locate the UDIF trailer */
offset = dmg_find_koly_offset(bs->file, errp);
if (offset < 0) {
ret = offset;
goto fail;
}
ret = read_uint64(bs, offset + 0x28, &info_begin);
if (ret < 0) {
goto fail;
} else if (info_begin == 0) {
ret = -EINVAL;
goto fail;
}
ret = read_uint32(bs, info_begin, &rsrc_data_offset);
if (ret < 0) {
goto fail;
} else if (rsrc_data_offset != 0x100) {
ret = -EINVAL;
goto fail;
}
ret = read_uint32(bs, info_begin + 4, &count);
if (ret < 0) {
goto fail;
} else if (count == 0) {
ret = -EINVAL;
goto fail;
}
/* end of resource data, ignoring the following resource map */
info_end = info_begin + count;
/* begin of resource data (consisting of one or more resources) */
offset = info_begin + 0x100;
/* read offsets (mish blocks) from one or more resources in resource data */
while (offset < info_end) {
/* size of following resource */
ret = read_uint32(bs, offset, &count);
if (ret < 0) {
goto fail;
} else if (count == 0) {
ret = -EINVAL;
goto fail;
}
offset += 4;
ret = dmg_read_mish_block(bs, &ds, offset, count);
if (ret < 0) {
goto fail;
}
/* advance offset by size of resource */
offset += count;
}
/* initialize zlib engine */
s->compressed_chunk = qemu_try_blockalign(bs->file,
max_compressed_size + 1);
ds.max_compressed_size + 1);
s->uncompressed_chunk = qemu_try_blockalign(bs->file,
512 * max_sectors_per_chunk);
512 * ds.max_sectors_per_chunk);
if (s->compressed_chunk == NULL || s->uncompressed_chunk == NULL) {
ret = -ENOMEM;
goto fail;