|
|
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 {
1.1.1.4 ! root 31: CoMutex lock;
1.1 root 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:
52: static 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:
1.1.1.3 root 60: static off_t read_off(BlockDriverState *bs, int64_t offset)
1.1 root 61: {
62: uint64_t buffer;
1.1.1.3 root 63: if (bdrv_pread(bs->file, offset, &buffer, 8) < 8)
1.1 root 64: return 0;
65: return be64_to_cpu(buffer);
66: }
67:
1.1.1.3 root 68: static off_t read_uint32(BlockDriverState *bs, int64_t offset)
1.1 root 69: {
70: uint32_t buffer;
1.1.1.3 root 71: if (bdrv_pread(bs->file, offset, &buffer, 4) < 4)
1.1 root 72: return 0;
73: return be32_to_cpu(buffer);
74: }
75:
1.1.1.3 root 76: static int dmg_open(BlockDriverState *bs, int flags)
1.1 root 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;
1.1.1.3 root 82: int64_t offset;
1.1 root 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 */
1.1.1.3 root 89: offset = bdrv_getlength(bs->file);
90: if (offset < 0) {
1.1.1.2 root 91: goto fail;
1.1 root 92: }
1.1.1.3 root 93: offset -= 0x1d8;
1.1.1.2 root 94:
1.1.1.3 root 95: info_begin = read_off(bs, offset);
96: if (info_begin == 0) {
1.1.1.2 root 97: goto fail;
1.1.1.3 root 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;
1.1 root 111:
112: /* read offsets */
113: last_in_offset = last_out_offset = 0;
1.1.1.3 root 114: while (offset < info_end) {
1.1 root 115: uint32_t type;
116:
1.1.1.3 root 117: count = read_uint32(bs, offset);
1.1 root 118: if(count==0)
1.1.1.2 root 119: goto fail;
1.1.1.3 root 120: offset += 4;
121:
122: type = read_uint32(bs, offset);
123: if (type == 0x6d697368 && count >= 244) {
1.1 root 124: int new_size, chunk_count;
1.1.1.3 root 125:
126: offset += 4;
127: offset += 200;
128:
1.1 root 129: chunk_count = (count-204)/40;
130: new_size = sizeof(uint64_t) * (s->n_chunks + chunk_count);
1.1.1.4 ! root 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);
1.1 root 136:
137: for(i=s->n_chunks;i<s->n_chunks+chunk_count;i++) {
1.1.1.3 root 138: s->types[i] = read_uint32(bs, offset);
139: offset += 4;
1.1 root 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--;
1.1.1.3 root 147: offset += 36;
1.1 root 148: continue;
149: }
1.1.1.3 root 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:
1.1 root 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 */
1.1.1.4 ! root 174: s->compressed_chunk = g_malloc(max_compressed_size+1);
! 175: s->uncompressed_chunk = g_malloc(512*max_sectors_per_chunk);
1.1 root 176: if(inflateInit(&s->zstream) != Z_OK)
1.1.1.2 root 177: goto fail;
1.1 root 178:
179: s->current_chunk = s->n_chunks;
180:
1.1.1.4 ! root 181: qemu_co_mutex_init(&s->lock);
1.1 root 182: return 0;
1.1.1.2 root 183: fail:
184: return -1;
1.1 root 185: }
186:
187: static 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:
197: static 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:
1.1.1.3 root 213: static inline int dmg_read_chunk(BlockDriverState *bs, int sector_num)
1.1 root 214: {
1.1.1.3 root 215: BDRVDMGState *s = bs->opaque;
216:
1.1 root 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 {
1.1.1.3 root 233: ret = bdrv_pread(bs->file, s->offsets[chunk] + i,
234: s->compressed_chunk+i, s->lengths[chunk]-i);
1.1 root 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 */
1.1.1.3 root 255: ret = bdrv_pread(bs->file, s->offsets[chunk],
256: s->uncompressed_chunk, s->lengths[chunk]);
1.1 root 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:
269: static 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;
1.1.1.3 root 277: if(dmg_read_chunk(bs, sector_num+i) != 0)
1.1 root 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:
1.1.1.4 ! root 285: static 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:
1.1 root 296: static 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:
311: static BlockDriver bdrv_dmg = {
312: .format_name = "dmg",
313: .instance_size = sizeof(BDRVDMGState),
314: .bdrv_probe = dmg_probe,
315: .bdrv_open = dmg_open,
1.1.1.4 ! root 316: .bdrv_read = dmg_co_read,
1.1 root 317: .bdrv_close = dmg_close,
318: };
319:
320: static void bdrv_dmg_init(void)
321: {
322: bdrv_register(&bdrv_dmg);
323: }
324:
325: block_init(bdrv_dmg_init);
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.