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