/*
* vpc.c
* Layered data source for Virtual PC hard disk images.
*
* Copyright (c) 2003 Christoph Pfisterer
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "global.h"
/*
* types
*/
typedef struct vhd_chunk {
int present;
u8 off;
u1 bitmap[1];
} VHD_CHUNK;
typedef struct vhd_source {
SOURCE c;
u8 off;
u4 chunk_size;
u4 chunk_count;
u4 *raw_map;
VHD_CHUNK **chunks;
} VHD_SOURCE;
/*
* helper functions
*/
static SOURCE *init_vhd_source(SECTION *section, int level,
u8 total_size, u8 sparse_offset);
static int read_block_vhd(SOURCE *s, u8 pos, void *buf);
static void close_vhd(SOURCE *s);
/*
* cd image detection
*/
void detect_vhd(SECTION *section, int level)
{
unsigned char *buf;
int found, type;
u8 sparse_offset, total_size;
char s[256];
SOURCE *src;
found = 0;
/* check for info block at the beginning */
if (get_buffer(section, 0, 511, (void **)&buf) < 511)
return;
if (memcmp(buf, "conectix", 8) == 0) {
found = 1;
}
/* check for info block at the end if possible */
if (!found && section->size > 1024 && !section->source->sequential) {
if (get_buffer(section, section->size - 511, 511, (void **)&buf) < 511)
return;
if (memcmp(buf, "conectix", 8) == 0) {
found = 1;
}
}
if (!found)
return;
/* okay, now buf points to the info block, wherever it was found */
type = get_be_long(buf + 0x3c);
total_size = get_be_quad(buf + 0x28); /* copy at 0x30 ... ??? */
if (type == 2) {
print_line(level, "Connectix Virtual PC hard disk image, fixed size");
} else if (type == 3) {
print_line(level, "Connectix Virtual PC hard disk image, dynamic size");
} else if (type == 4) {
print_line(level, "Connectix Virtual PC hard disk image, differential");
} else {
print_line(level, "Connectix Virtual PC hard disk image, unknown type %d",
type);
}
format_size_verbose(s, total_size);
print_line(level + 1, "Disk size %s", s);
if (type == 3) {
/* dynamically sized, set up a mapping data source */
sparse_offset = get_be_quad(buf + 16);
src = init_vhd_source(section, level, total_size, sparse_offset);
if (src != NULL) {
/* analyze it */
analyze_source(src, level);
close_source(src);
}
}
if (type == 3 || type == 4)
stop_detect();
}
/*
* initialize the mapping source
*/
static SOURCE *init_vhd_source(SECTION *section, int level,
u8 total_size, u8 sparse_offset)
{
VHD_SOURCE *vs;
unsigned char *buf;
u8 map_offset;
u4 map_size;
char s[256];
/* allocate and init source structure */
vs = (VHD_SOURCE *)malloc(sizeof(VHD_SOURCE));
if (vs == NULL)
bailout("Out of memory");
memset(vs, 0, sizeof(VHD_SOURCE));
vs->c.size_known = 1;
vs->c.size = total_size;
vs->c.blocksize = 512;
vs->c.foundation = section->source;
vs->c.read_block = read_block_vhd;
vs->c.close = close_vhd;
vs->off = section->pos;
/* read sparse information block */
if (get_buffer(section, sparse_offset, 512, (void **)&buf) < 512) {
print_line(level + 1, "Error reading the sparse image info block");
goto errorexit;
}
map_offset = get_be_quad(buf + 16);
vs->chunk_count = get_be_long(buf + 28);
vs->chunk_size = get_be_long(buf + 32);
format_size(s, vs->chunk_size);
print_line(level + 1, "Dynamic sizing uses %lu chunks of %s",
vs->chunk_count, s);
if ((u8)vs->chunk_count * vs->chunk_size < total_size) {
print_line(level + 1, "Error: Sparse parameters don't match total size");
goto errorexit;
}
if (vs->chunk_size < 4096) {
print_line(level + 1, "Error: Sparse chunk size too small (%lu bytes)",
vs->chunk_size);
goto errorexit;
}
if (vs->chunk_size > 2*1024*1024) {
/* written-to bitmap wouldn't fit in one sector */
print_line(level + 1, "Error: Sparse chunk size too large (%lu bytes)",
vs->chunk_size);
goto errorexit;
}
/* allocate further data structures */
map_size = vs->chunk_count * 4;
vs->raw_map = (u4 *)malloc(map_size);
if (vs->raw_map == NULL)
bailout("Out of memory");
vs->chunks = (VHD_CHUNK **)malloc(vs->chunk_count * sizeof(VHD_CHUNK *));
if (vs->chunks == NULL)
bailout("Out of memory");
memset(vs->chunks, 0, vs->chunk_count * sizeof(VHD_CHUNK *));
/* read the chunk map */
if (get_buffer_real(section->source, vs->off + map_offset, map_size,
(void *)vs->raw_map, NULL) < map_size) {
print_line(level + 1, "Error reading the sparse image map");
goto errorexit;
}
return (SOURCE *)vs;
errorexit:
close_vhd((SOURCE *)vs);
free(vs);
return NULL;
}
/*
* mapping read
*/
static int read_block_vhd(SOURCE *s, u8 pos, void *buf)
{
VHD_SOURCE *vs = (VHD_SOURCE *)s;
SOURCE *fs = s->foundation;
u4 chunk, chunk_start_sector, sector;
u8 chunk_disk_off, sector_pos;
unsigned char *filebuf;
int present;
chunk = (u4)(pos / vs->chunk_size);
if (chunk >= vs->chunk_count)
return 0;
if (vs->chunks[chunk] == NULL) {
/* create data structure for the chunk */
chunk_start_sector = get_be_long(vs->raw_map + chunk);
/* NOTE: raw_map is a u4*, so C does the arithmetics for us */
if (chunk_start_sector == 0xffffffff) {
present = 0;
} else {
chunk_disk_off = vs->off + (u8)chunk_start_sector * 512;
if (get_buffer_real(fs, chunk_disk_off, 512,
NULL, (void **)&filebuf) < 512)
present = 0;
else
present = 1;
}
if (!present) {
vs->chunks[chunk] = (VHD_CHUNK *)malloc(sizeof(VHD_CHUNK));
if (vs->chunks[chunk] == NULL)
bailout("Out of memory");
vs->chunks[chunk]->present = 0;
} else {
vs->chunks[chunk] = (VHD_CHUNK *)malloc(sizeof(VHD_CHUNK) + 512);
if (vs->chunks[chunk] == NULL)
bailout("Out of memory");
vs->chunks[chunk]->present = 1;
vs->chunks[chunk]->off = chunk_disk_off + 512;
memcpy(vs->chunks[chunk]->bitmap, filebuf, 512);
}
}
if (!vs->chunks[chunk]->present) {
/* whole chunk is missing */
memset(buf, 0, 512);
return 1;
}
sector = (u4)((pos - (u8)chunk * vs->chunk_size) / 512);
if (vs->chunks[chunk]->bitmap[sector >> 3] & (128 >> (sector & 7))) {
/* sector is present and in use */
sector_pos = vs->chunks[chunk]->off + (u8)sector * 512;
if (get_buffer_real(fs, sector_pos, 512, buf, NULL) < 512)
return 0;
} else {
/* sector has not been written to (although it's present on disk) */
memset(buf, 0, 512);
}
return 1;
}
/*
* cleanup
*/
static void close_vhd(SOURCE *s)
{
VHD_SOURCE *vs = (VHD_SOURCE *)s;
u4 chunk;
/* free raw chunk map */
if (vs->raw_map != NULL)
free(vs->raw_map);
/* free chunk info data */
if (vs->chunks != NULL) {
for (chunk = 0; chunk < vs->chunk_count; chunk++) {
if (vs->chunks[chunk] != NULL)
free(vs->chunks[chunk]);
}
free(vs->chunks);
}
}
/* EOF */
syntax highlighted by Code2HTML, v. 0.9.1