File:  [Qemu by Fabrice Bellard] / qemu / block-dmg.c
Revision 1.1.1.5 (vendor branch): download - view: text, annotated - select for diffs
Tue Apr 24 16:50:18 2018 UTC (3 years, 3 months ago) by root
Branches: qemu, MAIN
CVS tags: qemu0105, qemu0104, qemu0103, qemu0102, qemu0101, qemu0100, HEAD
qemu 0.10.0

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

unix.superglobalmegacorp.com