VMDK: read/write compressed extent

Add support for reading/writing compressed extent.

Signed-off-by: Fam Zheng <famcool@gmail.com>
Reviewed-by: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
This commit is contained in:
Fam Zheng 2011-08-12 23:19:31 +08:00 committed by Kevin Wolf
parent 432bb170af
commit 2b2c8c5dec

View File

@ -775,10 +775,12 @@ static int get_cluster_offset(BlockDriverState *bs,
/* Avoid the L2 tables update for the images that have snapshots. */ /* Avoid the L2 tables update for the images that have snapshots. */
*cluster_offset = bdrv_getlength(extent->file); *cluster_offset = bdrv_getlength(extent->file);
if (!extent->compressed) {
bdrv_truncate( bdrv_truncate(
extent->file, extent->file,
*cluster_offset + (extent->cluster_sectors << 9) *cluster_offset + (extent->cluster_sectors << 9)
); );
}
*cluster_offset >>= 9; *cluster_offset >>= 9;
tmp = cpu_to_le32(*cluster_offset); tmp = cpu_to_le32(*cluster_offset);
@ -854,9 +856,28 @@ static int vmdk_write_extent(VmdkExtent *extent, int64_t cluster_offset,
int nb_sectors, int64_t sector_num) int nb_sectors, int64_t sector_num)
{ {
int ret; int ret;
VmdkGrainMarker *data = NULL;
uLongf buf_len;
const uint8_t *write_buf = buf; const uint8_t *write_buf = buf;
int write_len = nb_sectors * 512; int write_len = nb_sectors * 512;
if (extent->compressed) {
if (!extent->has_marker) {
ret = -EINVAL;
goto out;
}
buf_len = (extent->cluster_sectors << 9) * 2;
data = g_malloc(buf_len + sizeof(VmdkGrainMarker));
if (compress(data->data, &buf_len, buf, nb_sectors << 9) != Z_OK ||
buf_len == 0) {
ret = -EINVAL;
goto out;
}
data->lba = sector_num;
data->size = buf_len;
write_buf = (uint8_t *)data;
write_len = buf_len + sizeof(VmdkGrainMarker);
}
ret = bdrv_pwrite(extent->file, ret = bdrv_pwrite(extent->file,
cluster_offset + offset_in_cluster, cluster_offset + offset_in_cluster,
write_buf, write_buf,
@ -867,6 +888,7 @@ static int vmdk_write_extent(VmdkExtent *extent, int64_t cluster_offset,
} }
ret = 0; ret = 0;
out: out:
g_free(data);
return ret; return ret;
} }
@ -875,7 +897,15 @@ static int vmdk_read_extent(VmdkExtent *extent, int64_t cluster_offset,
int nb_sectors) int nb_sectors)
{ {
int ret; int ret;
int cluster_bytes, buf_bytes;
uint8_t *cluster_buf, *compressed_data;
uint8_t *uncomp_buf;
uint32_t data_len;
VmdkGrainMarker *marker;
uLongf buf_len;
if (!extent->compressed) {
ret = bdrv_pread(extent->file, ret = bdrv_pread(extent->file,
cluster_offset + offset_in_cluster, cluster_offset + offset_in_cluster,
buf, nb_sectors * 512); buf, nb_sectors * 512);
@ -884,6 +914,48 @@ static int vmdk_read_extent(VmdkExtent *extent, int64_t cluster_offset,
} else { } else {
return -EIO; return -EIO;
} }
}
cluster_bytes = extent->cluster_sectors * 512;
/* Read two clusters in case GrainMarker + compressed data > one cluster */
buf_bytes = cluster_bytes * 2;
cluster_buf = g_malloc(buf_bytes);
uncomp_buf = g_malloc(cluster_bytes);
ret = bdrv_pread(extent->file,
cluster_offset,
cluster_buf, buf_bytes);
if (ret < 0) {
goto out;
}
compressed_data = cluster_buf;
buf_len = cluster_bytes;
data_len = cluster_bytes;
if (extent->has_marker) {
marker = (VmdkGrainMarker *)cluster_buf;
compressed_data = marker->data;
data_len = le32_to_cpu(marker->size);
}
if (!data_len || data_len > buf_bytes) {
ret = -EINVAL;
goto out;
}
ret = uncompress(uncomp_buf, &buf_len, compressed_data, data_len);
if (ret != Z_OK) {
ret = -EINVAL;
goto out;
}
if (offset_in_cluster < 0 ||
offset_in_cluster + nb_sectors * 512 > buf_len) {
ret = -EINVAL;
goto out;
}
memcpy(buf, uncomp_buf + offset_in_cluster, nb_sectors * 512);
ret = 0;
out:
g_free(uncomp_buf);
g_free(cluster_buf);
return ret;
} }
static int vmdk_read(BlockDriverState *bs, int64_t sector_num, static int vmdk_read(BlockDriverState *bs, int64_t sector_num,
@ -959,12 +1031,29 @@ static int vmdk_write(BlockDriverState *bs, int64_t sector_num,
if (!extent) { if (!extent) {
return -EIO; return -EIO;
} }
ret = get_cluster_offset(
bs,
extent,
&m_data,
sector_num << 9, !extent->compressed,
&cluster_offset);
if (extent->compressed) {
if (ret == 0) {
/* Refuse write to allocated cluster for streamOptimized */
fprintf(stderr,
"VMDK: can't write to allocated cluster"
" for streamOptimized\n");
return -EIO;
} else {
/* allocate */
ret = get_cluster_offset( ret = get_cluster_offset(
bs, bs,
extent, extent,
&m_data, &m_data,
sector_num << 9, 1, sector_num << 9, 1,
&cluster_offset); &cluster_offset);
}
}
if (ret) { if (ret) {
return -EINVAL; return -EINVAL;
} }