qemu/block/dmg.c
<<
>>
Prefs
   1/*
   2 * QEMU Block driver for DMG images
   3 *
   4 * Copyright (c) 2004 Johannes E. Schindelin
   5 *
   6 * Permission is hereby granted, free of charge, to any person obtaining a copy
   7 * of this software and associated documentation files (the "Software"), to deal
   8 * in the Software without restriction, including without limitation the rights
   9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 * copies of the Software, and to permit persons to whom the Software is
  11 * furnished to do so, subject to the following conditions:
  12 *
  13 * The above copyright notice and this permission notice shall be included in
  14 * all copies or substantial portions of the Software.
  15 *
  16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 * THE SOFTWARE.
  23 */
  24#include "qemu-common.h"
  25#include "block_int.h"
  26#include "bswap.h"
  27#include "module.h"
  28#include <zlib.h>
  29
  30typedef struct BDRVDMGState {
  31    CoMutex lock;
  32    /* each chunk contains a certain number of sectors,
  33     * offsets[i] is the offset in the .dmg file,
  34     * lengths[i] is the length of the compressed chunk,
  35     * sectors[i] is the sector beginning at offsets[i],
  36     * sectorcounts[i] is the number of sectors in that chunk,
  37     * the sectors array is ordered
  38     * 0<=i<n_chunks */
  39
  40    uint32_t n_chunks;
  41    uint32_t* types;
  42    uint64_t* offsets;
  43    uint64_t* lengths;
  44    uint64_t* sectors;
  45    uint64_t* sectorcounts;
  46    uint32_t current_chunk;
  47    uint8_t *compressed_chunk;
  48    uint8_t *uncompressed_chunk;
  49    z_stream zstream;
  50} BDRVDMGState;
  51
  52static int dmg_probe(const uint8_t *buf, int buf_size, const char *filename)
  53{
  54    int len=strlen(filename);
  55    if(len>4 && !strcmp(filename+len-4,".dmg"))
  56        return 2;
  57    return 0;
  58}
  59
  60static off_t read_off(BlockDriverState *bs, int64_t offset)
  61{
  62        uint64_t buffer;
  63        if (bdrv_pread(bs->file, offset, &buffer, 8) < 8)
  64                return 0;
  65        return be64_to_cpu(buffer);
  66}
  67
  68static off_t read_uint32(BlockDriverState *bs, int64_t offset)
  69{
  70        uint32_t buffer;
  71        if (bdrv_pread(bs->file, offset, &buffer, 4) < 4)
  72                return 0;
  73        return be32_to_cpu(buffer);
  74}
  75
  76static int dmg_open(BlockDriverState *bs, int flags)
  77{
  78    BDRVDMGState *s = bs->opaque;
  79    off_t info_begin,info_end,last_in_offset,last_out_offset;
  80    uint32_t count;
  81    uint32_t max_compressed_size=1,max_sectors_per_chunk=1,i;
  82    int64_t offset;
  83
  84    bs->read_only = 1;
  85    s->n_chunks = 0;
  86    s->offsets = s->lengths = s->sectors = s->sectorcounts = NULL;
  87
  88    /* read offset of info blocks */
  89    offset = bdrv_getlength(bs->file);
  90    if (offset < 0) {
  91        goto fail;
  92    }
  93    offset -= 0x1d8;
  94
  95    info_begin = read_off(bs, offset);
  96    if (info_begin == 0) {
  97        goto fail;
  98    }
  99
 100    if (read_uint32(bs, info_begin) != 0x100) {
 101        goto fail;
 102    }
 103
 104    count = read_uint32(bs, info_begin + 4);
 105    if (count == 0) {
 106        goto fail;
 107    }
 108    info_end = info_begin + count;
 109
 110    offset = info_begin + 0x100;
 111
 112    /* read offsets */
 113    last_in_offset = last_out_offset = 0;
 114    while (offset < info_end) {
 115        uint32_t type;
 116
 117        count = read_uint32(bs, offset);
 118        if(count==0)
 119            goto fail;
 120        offset += 4;
 121
 122        type = read_uint32(bs, offset);
 123        if (type == 0x6d697368 && count >= 244) {
 124            int new_size, chunk_count;
 125
 126            offset += 4;
 127            offset += 200;
 128
 129            chunk_count = (count-204)/40;
 130            new_size = sizeof(uint64_t) * (s->n_chunks + chunk_count);
 131            s->types = g_realloc(s->types, new_size/2);
 132            s->offsets = g_realloc(s->offsets, new_size);
 133            s->lengths = g_realloc(s->lengths, new_size);
 134            s->sectors = g_realloc(s->sectors, new_size);
 135            s->sectorcounts = g_realloc(s->sectorcounts, new_size);
 136
 137            for(i=s->n_chunks;i<s->n_chunks+chunk_count;i++) {
 138                s->types[i] = read_uint32(bs, offset);
 139                offset += 4;
 140                if(s->types[i]!=0x80000005 && s->types[i]!=1 && s->types[i]!=2) {
 141                    if(s->types[i]==0xffffffff) {
 142                        last_in_offset = s->offsets[i-1]+s->lengths[i-1];
 143                        last_out_offset = s->sectors[i-1]+s->sectorcounts[i-1];
 144                    }
 145                    chunk_count--;
 146                    i--;
 147                    offset += 36;
 148                    continue;
 149                }
 150                offset += 4;
 151
 152                s->sectors[i] = last_out_offset+read_off(bs, offset);
 153                offset += 8;
 154
 155                s->sectorcounts[i] = read_off(bs, offset);
 156                offset += 8;
 157
 158                s->offsets[i] = last_in_offset+read_off(bs, offset);
 159                offset += 8;
 160
 161                s->lengths[i] = read_off(bs, offset);
 162                offset += 8;
 163
 164                if(s->lengths[i]>max_compressed_size)
 165                    max_compressed_size = s->lengths[i];
 166                if(s->sectorcounts[i]>max_sectors_per_chunk)
 167                    max_sectors_per_chunk = s->sectorcounts[i];
 168            }
 169            s->n_chunks+=chunk_count;
 170        }
 171    }
 172
 173    /* initialize zlib engine */
 174    s->compressed_chunk = g_malloc(max_compressed_size+1);
 175    s->uncompressed_chunk = g_malloc(512*max_sectors_per_chunk);
 176    if(inflateInit(&s->zstream) != Z_OK)
 177        goto fail;
 178
 179    s->current_chunk = s->n_chunks;
 180
 181    qemu_co_mutex_init(&s->lock);
 182    return 0;
 183fail:
 184    return -1;
 185}
 186
 187static inline int is_sector_in_chunk(BDRVDMGState* s,
 188                uint32_t chunk_num,int sector_num)
 189{
 190    if(chunk_num>=s->n_chunks || s->sectors[chunk_num]>sector_num ||
 191            s->sectors[chunk_num]+s->sectorcounts[chunk_num]<=sector_num)
 192        return 0;
 193    else
 194        return -1;
 195}
 196
 197static inline uint32_t search_chunk(BDRVDMGState* s,int sector_num)
 198{
 199    /* binary search */
 200    uint32_t chunk1=0,chunk2=s->n_chunks,chunk3;
 201    while(chunk1!=chunk2) {
 202        chunk3 = (chunk1+chunk2)/2;
 203        if(s->sectors[chunk3]>sector_num)
 204            chunk2 = chunk3;
 205        else if(s->sectors[chunk3]+s->sectorcounts[chunk3]>sector_num)
 206            return chunk3;
 207        else
 208            chunk1 = chunk3;
 209    }
 210    return s->n_chunks; /* error */
 211}
 212
 213static inline int dmg_read_chunk(BlockDriverState *bs, int sector_num)
 214{
 215    BDRVDMGState *s = bs->opaque;
 216
 217    if(!is_sector_in_chunk(s,s->current_chunk,sector_num)) {
 218        int ret;
 219        uint32_t chunk = search_chunk(s,sector_num);
 220
 221        if(chunk>=s->n_chunks)
 222            return -1;
 223
 224        s->current_chunk = s->n_chunks;
 225        switch(s->types[chunk]) {
 226        case 0x80000005: { /* zlib compressed */
 227            int i;
 228
 229            /* we need to buffer, because only the chunk as whole can be
 230             * inflated. */
 231            i=0;
 232            do {
 233                ret = bdrv_pread(bs->file, s->offsets[chunk] + i,
 234                                 s->compressed_chunk+i, s->lengths[chunk]-i);
 235                if(ret<0 && errno==EINTR)
 236                    ret=0;
 237                i+=ret;
 238            } while(ret>=0 && ret+i<s->lengths[chunk]);
 239
 240            if (ret != s->lengths[chunk])
 241                return -1;
 242
 243            s->zstream.next_in = s->compressed_chunk;
 244            s->zstream.avail_in = s->lengths[chunk];
 245            s->zstream.next_out = s->uncompressed_chunk;
 246            s->zstream.avail_out = 512*s->sectorcounts[chunk];
 247            ret = inflateReset(&s->zstream);
 248            if(ret != Z_OK)
 249                return -1;
 250            ret = inflate(&s->zstream, Z_FINISH);
 251            if(ret != Z_STREAM_END || s->zstream.total_out != 512*s->sectorcounts[chunk])
 252                return -1;
 253            break; }
 254        case 1: /* copy */
 255            ret = bdrv_pread(bs->file, s->offsets[chunk],
 256                             s->uncompressed_chunk, s->lengths[chunk]);
 257            if (ret != s->lengths[chunk])
 258                return -1;
 259            break;
 260        case 2: /* zero */
 261            memset(s->uncompressed_chunk, 0, 512*s->sectorcounts[chunk]);
 262            break;
 263        }
 264        s->current_chunk = chunk;
 265    }
 266    return 0;
 267}
 268
 269static int dmg_read(BlockDriverState *bs, int64_t sector_num,
 270                    uint8_t *buf, int nb_sectors)
 271{
 272    BDRVDMGState *s = bs->opaque;
 273    int i;
 274
 275    for(i=0;i<nb_sectors;i++) {
 276        uint32_t sector_offset_in_chunk;
 277        if(dmg_read_chunk(bs, sector_num+i) != 0)
 278            return -1;
 279        sector_offset_in_chunk = sector_num+i-s->sectors[s->current_chunk];
 280        memcpy(buf+i*512,s->uncompressed_chunk+sector_offset_in_chunk*512,512);
 281    }
 282    return 0;
 283}
 284
 285static coroutine_fn int dmg_co_read(BlockDriverState *bs, int64_t sector_num,
 286                                    uint8_t *buf, int nb_sectors)
 287{
 288    int ret;
 289    BDRVDMGState *s = bs->opaque;
 290    qemu_co_mutex_lock(&s->lock);
 291    ret = dmg_read(bs, sector_num, buf, nb_sectors);
 292    qemu_co_mutex_unlock(&s->lock);
 293    return ret;
 294}
 295
 296static void dmg_close(BlockDriverState *bs)
 297{
 298    BDRVDMGState *s = bs->opaque;
 299    if(s->n_chunks>0) {
 300        free(s->types);
 301        free(s->offsets);
 302        free(s->lengths);
 303        free(s->sectors);
 304        free(s->sectorcounts);
 305    }
 306    free(s->compressed_chunk);
 307    free(s->uncompressed_chunk);
 308    inflateEnd(&s->zstream);
 309}
 310
 311static BlockDriver bdrv_dmg = {
 312    .format_name        = "dmg",
 313    .instance_size      = sizeof(BDRVDMGState),
 314    .bdrv_probe         = dmg_probe,
 315    .bdrv_open          = dmg_open,
 316    .bdrv_read          = dmg_co_read,
 317    .bdrv_close         = dmg_close,
 318};
 319
 320static void bdrv_dmg_init(void)
 321{
 322    bdrv_register(&bdrv_dmg);
 323}
 324
 325block_init(bdrv_dmg_init);
 326