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 30 typedef struct BDRVDMGState { 31 /* each chunk contains a certain number of sectors, 32 * offsets[i] is the offset in the .dmg file, 33 * lengths[i] is the length of the compressed chunk, 34 * sectors[i] is the sector beginning at offsets[i], 35 * sectorcounts[i] is the number of sectors in that chunk, 36 * the sectors array is ordered 37 * 0<=i<n_chunks */ 38 39 uint32_t n_chunks; 40 uint32_t* types; 41 uint64_t* offsets; 42 uint64_t* lengths; 43 uint64_t* sectors; 44 uint64_t* sectorcounts; 45 uint32_t current_chunk; 46 uint8_t *compressed_chunk; 47 uint8_t *uncompressed_chunk; 48 z_stream zstream; 49 } BDRVDMGState; 50 51 static int dmg_probe(const uint8_t *buf, int buf_size, const char *filename) 52 { 53 int len=strlen(filename); 54 if(len>4 && !strcmp(filename+len-4,".dmg")) 55 return 2; 56 return 0; 57 } 58 59 static off_t read_off(BlockDriverState *bs, int64_t offset) 60 { 61 uint64_t buffer; 62 if (bdrv_pread(bs->file, offset, &buffer, 8) < 8) 63 return 0; 64 return be64_to_cpu(buffer); 65 } 66 67 static off_t read_uint32(BlockDriverState *bs, int64_t offset) 68 { 69 uint32_t buffer; 70 if (bdrv_pread(bs->file, offset, &buffer, 4) < 4) 71 return 0; 72 return be32_to_cpu(buffer); 73 } 74 75 static int dmg_open(BlockDriverState *bs, int flags) 76 { 77 BDRVDMGState *s = bs->opaque; 78 off_t info_begin,info_end,last_in_offset,last_out_offset; 79 uint32_t count; 80 uint32_t max_compressed_size=1,max_sectors_per_chunk=1,i; 81 int64_t offset; 82 83 bs->read_only = 1; 84 s->n_chunks = 0; 85 s->offsets = s->lengths = s->sectors = s->sectorcounts = NULL; 86 87 /* read offset of info blocks */ 88 offset = bdrv_getlength(bs->file); 89 if (offset < 0) { 90 goto fail; 91 } 92 offset -= 0x1d8; 93 94 info_begin = read_off(bs, offset); 95 if (info_begin == 0) { 96 goto fail; 97 } 98 99 if (read_uint32(bs, info_begin) != 0x100) { 100 goto fail; 101 } 102 103 count = read_uint32(bs, info_begin + 4); 104 if (count == 0) { 105 goto fail; 106 } 107 info_end = info_begin + count; 108 109 offset = info_begin + 0x100; 110 111 /* read offsets */ 112 last_in_offset = last_out_offset = 0; 113 while (offset < info_end) { 114 uint32_t type; 115 116 count = read_uint32(bs, offset); 117 if(count==0) 118 goto fail; 119 offset += 4; 120 121 type = read_uint32(bs, offset); 122 if (type == 0x6d697368 && count >= 244) { 123 int new_size, chunk_count; 124 125 offset += 4; 126 offset += 200; 127 128 chunk_count = (count-204)/40; 129 new_size = sizeof(uint64_t) * (s->n_chunks + chunk_count); 130 s->types = g_realloc(s->types, new_size/2); 131 s->offsets = g_realloc(s->offsets, new_size); 132 s->lengths = g_realloc(s->lengths, new_size); 133 s->sectors = g_realloc(s->sectors, new_size); 134 s->sectorcounts = g_realloc(s->sectorcounts, new_size); 135 136 for(i=s->n_chunks;i<s->n_chunks+chunk_count;i++) { 137 s->types[i] = read_uint32(bs, offset); 138 offset += 4; 139 if(s->types[i]!=0x80000005 && s->types[i]!=1 && s->types[i]!=2) { 140 if(s->types[i]==0xffffffff) { 141 last_in_offset = s->offsets[i-1]+s->lengths[i-1]; 142 last_out_offset = s->sectors[i-1]+s->sectorcounts[i-1]; 143 } 144 chunk_count--; 145 i--; 146 offset += 36; 147 continue; 148 } 149 offset += 4; 150 151 s->sectors[i] = last_out_offset+read_off(bs, offset); 152 offset += 8; 153 154 s->sectorcounts[i] = read_off(bs, offset); 155 offset += 8; 156 157 s->offsets[i] = last_in_offset+read_off(bs, offset); 158 offset += 8; 159 160 s->lengths[i] = read_off(bs, offset); 161 offset += 8; 162 163 if(s->lengths[i]>max_compressed_size) 164 max_compressed_size = s->lengths[i]; 165 if(s->sectorcounts[i]>max_sectors_per_chunk) 166 max_sectors_per_chunk = s->sectorcounts[i]; 167 } 168 s->n_chunks+=chunk_count; 169 } 170 } 171 172 /* initialize zlib engine */ 173 s->compressed_chunk = g_malloc(max_compressed_size+1); 174 s->uncompressed_chunk = g_malloc(512*max_sectors_per_chunk); 175 if(inflateInit(&s->zstream) != Z_OK) 176 goto fail; 177 178 s->current_chunk = s->n_chunks; 179 180 return 0; 181 fail: 182 return -1; 183 } 184 185 static inline int is_sector_in_chunk(BDRVDMGState* s, 186 uint32_t chunk_num,int sector_num) 187 { 188 if(chunk_num>=s->n_chunks || s->sectors[chunk_num]>sector_num || 189 s->sectors[chunk_num]+s->sectorcounts[chunk_num]<=sector_num) 190 return 0; 191 else 192 return -1; 193 } 194 195 static inline uint32_t search_chunk(BDRVDMGState* s,int sector_num) 196 { 197 /* binary search */ 198 uint32_t chunk1=0,chunk2=s->n_chunks,chunk3; 199 while(chunk1!=chunk2) { 200 chunk3 = (chunk1+chunk2)/2; 201 if(s->sectors[chunk3]>sector_num) 202 chunk2 = chunk3; 203 else if(s->sectors[chunk3]+s->sectorcounts[chunk3]>sector_num) 204 return chunk3; 205 else 206 chunk1 = chunk3; 207 } 208 return s->n_chunks; /* error */ 209 } 210 211 static inline int dmg_read_chunk(BlockDriverState *bs, int sector_num) 212 { 213 BDRVDMGState *s = bs->opaque; 214 215 if(!is_sector_in_chunk(s,s->current_chunk,sector_num)) { 216 int ret; 217 uint32_t chunk = search_chunk(s,sector_num); 218 219 if(chunk>=s->n_chunks) 220 return -1; 221 222 s->current_chunk = s->n_chunks; 223 switch(s->types[chunk]) { 224 case 0x80000005: { /* zlib compressed */ 225 int i; 226 227 /* we need to buffer, because only the chunk as whole can be 228 * inflated. */ 229 i=0; 230 do { 231 ret = bdrv_pread(bs->file, s->offsets[chunk] + i, 232 s->compressed_chunk+i, s->lengths[chunk]-i); 233 if(ret<0 && errno==EINTR) 234 ret=0; 235 i+=ret; 236 } while(ret>=0 && ret+i<s->lengths[chunk]); 237 238 if (ret != s->lengths[chunk]) 239 return -1; 240 241 s->zstream.next_in = s->compressed_chunk; 242 s->zstream.avail_in = s->lengths[chunk]; 243 s->zstream.next_out = s->uncompressed_chunk; 244 s->zstream.avail_out = 512*s->sectorcounts[chunk]; 245 ret = inflateReset(&s->zstream); 246 if(ret != Z_OK) 247 return -1; 248 ret = inflate(&s->zstream, Z_FINISH); 249 if(ret != Z_STREAM_END || s->zstream.total_out != 512*s->sectorcounts[chunk]) 250 return -1; 251 break; } 252 case 1: /* copy */ 253 ret = bdrv_pread(bs->file, s->offsets[chunk], 254 s->uncompressed_chunk, s->lengths[chunk]); 255 if (ret != s->lengths[chunk]) 256 return -1; 257 break; 258 case 2: /* zero */ 259 memset(s->uncompressed_chunk, 0, 512*s->sectorcounts[chunk]); 260 break; 261 } 262 s->current_chunk = chunk; 263 } 264 return 0; 265 } 266 267 static int dmg_read(BlockDriverState *bs, int64_t sector_num, 268 uint8_t *buf, int nb_sectors) 269 { 270 BDRVDMGState *s = bs->opaque; 271 int i; 272 273 for(i=0;i<nb_sectors;i++) { 274 uint32_t sector_offset_in_chunk; 275 if(dmg_read_chunk(bs, sector_num+i) != 0) 276 return -1; 277 sector_offset_in_chunk = sector_num+i-s->sectors[s->current_chunk]; 278 memcpy(buf+i*512,s->uncompressed_chunk+sector_offset_in_chunk*512,512); 279 } 280 return 0; 281 } 282 283 static void dmg_close(BlockDriverState *bs) 284 { 285 BDRVDMGState *s = bs->opaque; 286 if(s->n_chunks>0) { 287 free(s->types); 288 free(s->offsets); 289 free(s->lengths); 290 free(s->sectors); 291 free(s->sectorcounts); 292 } 293 free(s->compressed_chunk); 294 free(s->uncompressed_chunk); 295 inflateEnd(&s->zstream); 296 } 297 298 static BlockDriver bdrv_dmg = { 299 .format_name = "dmg", 300 .instance_size = sizeof(BDRVDMGState), 301 .bdrv_probe = dmg_probe, 302 .bdrv_open = dmg_open, 303 .bdrv_read = dmg_read, 304 .bdrv_close = dmg_close, 305 }; 306 307 static void bdrv_dmg_init(void) 308 { 309 bdrv_register(&bdrv_dmg); 310 } 311 312 block_init(bdrv_dmg_init); 313