summaryrefslogtreecommitdiff
path: root/block/vpc.c
diff options
context:
space:
mode:
Diffstat (limited to 'block/vpc.c')
-rw-r--r--block/vpc.c106
1 files changed, 37 insertions, 69 deletions
diff --git a/block/vpc.c b/block/vpc.c
index 1533b6a64d..43e768ee76 100644
--- a/block/vpc.c
+++ b/block/vpc.c
@@ -46,6 +46,7 @@ enum vhd_type {
#define VHD_TIMESTAMP_BASE 946684800
#define VHD_MAX_SECTORS (65535LL * 255 * 255)
+#define VHD_MAX_GEOMETRY (65535LL * 16 * 255)
// always big-endian
typedef struct vhd_footer {
@@ -65,7 +66,7 @@ typedef struct vhd_footer {
char creator_os[4]; // "Wi2k"
uint64_t orig_size;
- uint64_t size;
+ uint64_t current_size;
uint16_t cyls;
uint8_t heads;
@@ -215,13 +216,12 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
bs->total_sectors = (int64_t)
be16_to_cpu(footer->cyls) * footer->heads * footer->secs_per_cyl;
- /* images created with disk2vhd report a far higher virtual size
- * than expected with the cyls * heads * sectors_per_cyl formula.
- * use the footer->size instead if the image was created with
- * disk2vhd.
- */
- if (!strncmp(footer->creator_app, "d2v", 4)) {
- bs->total_sectors = be64_to_cpu(footer->size) / BDRV_SECTOR_SIZE;
+ /* Images that have exactly the maximum geometry are probably bigger and
+ * would be truncated if we adhered to the geometry for them. Rely on
+ * footer->current_size for them. */
+ if (bs->total_sectors == VHD_MAX_GEOMETRY) {
+ bs->total_sectors = be64_to_cpu(footer->current_size) /
+ BDRV_SECTOR_SIZE;
}
/* Allow a maximum disk size of approximately 2 TB */
@@ -376,38 +376,6 @@ static inline int64_t get_sector_offset(BlockDriverState *bs,
bdrv_pwrite_sync(bs->file, bitmap_offset, bitmap, s->bitmap_size);
}
-// printf("sector: %" PRIx64 ", index: %x, offset: %x, bioff: %" PRIx64 ", bloff: %" PRIx64 "\n",
-// sector_num, pagetable_index, pageentry_index,
-// bitmap_offset, block_offset);
-
-// disabled by reason
-#if 0
-#ifdef CACHE
- if (bitmap_offset != s->last_bitmap)
- {
- lseek(s->fd, bitmap_offset, SEEK_SET);
-
- s->last_bitmap = bitmap_offset;
-
- // Scary! Bitmap is stored as big endian 32bit entries,
- // while we used to look it up byte by byte
- read(s->fd, s->pageentry_u8, 512);
- for (i = 0; i < 128; i++)
- be32_to_cpus(&s->pageentry_u32[i]);
- }
-
- if ((s->pageentry_u8[pageentry_index / 8] >> (pageentry_index % 8)) & 1)
- return -1;
-#else
- lseek(s->fd, bitmap_offset + (pageentry_index / 8), SEEK_SET);
-
- read(s->fd, &bitmap_entry, 1);
-
- if ((bitmap_entry >> (pageentry_index % 8)) & 1)
- return -1; // not allocated
-#endif
-#endif
-
return block_offset;
}
@@ -602,7 +570,7 @@ static int64_t coroutine_fn vpc_co_get_block_status(BlockDriverState *bs,
{
BDRVVPCState *s = bs->opaque;
VHDFooter *footer = (VHDFooter*) s->footer_buf;
- int64_t start, offset, next;
+ int64_t start, offset;
bool allocated;
int n;
@@ -626,20 +594,18 @@ static int64_t coroutine_fn vpc_co_get_block_status(BlockDriverState *bs,
*pnum += n;
sector_num += n;
nb_sectors -= n;
- next = start + (*pnum * BDRV_SECTOR_SIZE);
-
+ /* *pnum can't be greater than one block for allocated
+ * sectors since there is always a bitmap in between. */
+ if (allocated) {
+ return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | start;
+ }
if (nb_sectors == 0) {
break;
}
-
offset = get_sector_offset(bs, sector_num, 0);
- } while ((allocated && offset == next) || (!allocated && offset == -1));
+ } while (offset == -1);
- if (allocated) {
- return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | start;
- } else {
- return 0;
- }
+ return 0;
}
/*
@@ -659,26 +625,20 @@ static int calculate_geometry(int64_t total_sectors, uint16_t* cyls,
{
uint32_t cyls_times_heads;
- /* Allow a maximum disk size of approximately 2 TB */
- if (total_sectors > 65535LL * 255 * 255) {
- return -EFBIG;
- }
+ total_sectors = MIN(total_sectors, VHD_MAX_GEOMETRY);
- if (total_sectors > 65535 * 16 * 63) {
+ if (total_sectors >= 65535LL * 16 * 63) {
*secs_per_cyl = 255;
- if (total_sectors > 65535 * 16 * 255) {
- *heads = 255;
- } else {
- *heads = 16;
- }
+ *heads = 16;
cyls_times_heads = total_sectors / *secs_per_cyl;
} else {
*secs_per_cyl = 17;
cyls_times_heads = total_sectors / *secs_per_cyl;
*heads = (cyls_times_heads + 1023) / 1024;
- if (*heads < 4)
+ if (*heads < 4) {
*heads = 4;
+ }
if (cyls_times_heads >= (*heads * 1024) || *heads > 16) {
*secs_per_cyl = 31;
@@ -834,20 +794,28 @@ static int vpc_create(const char *filename, QemuOpts *opts, Error **errp)
* Calculate matching total_size and geometry. Increase the number of
* sectors requested until we get enough (or fail). This ensures that
* qemu-img convert doesn't truncate images, but rather rounds up.
+ *
+ * If the image size can't be represented by a spec conform CHS geometry,
+ * we set the geometry to 65535 x 16 x 255 (CxHxS) sectors and use
+ * the image size from the VHD footer to calculate total_sectors.
*/
- total_sectors = total_size / BDRV_SECTOR_SIZE;
+ total_sectors = MIN(VHD_MAX_GEOMETRY, total_size / BDRV_SECTOR_SIZE);
for (i = 0; total_sectors > (int64_t)cyls * heads * secs_per_cyl; i++) {
- if (calculate_geometry(total_sectors + i, &cyls, &heads,
- &secs_per_cyl))
- {
+ calculate_geometry(total_sectors + i, &cyls, &heads, &secs_per_cyl);
+ }
+
+ if ((int64_t)cyls * heads * secs_per_cyl == VHD_MAX_GEOMETRY) {
+ total_sectors = total_size / BDRV_SECTOR_SIZE;
+ /* Allow a maximum disk size of approximately 2 TB */
+ if (total_sectors > VHD_MAX_SECTORS) {
ret = -EFBIG;
goto out;
}
+ } else {
+ total_sectors = (int64_t)cyls * heads * secs_per_cyl;
+ total_size = total_sectors * BDRV_SECTOR_SIZE;
}
- total_sectors = (int64_t) cyls * heads * secs_per_cyl;
- total_size = total_sectors * BDRV_SECTOR_SIZE;
-
/* Prepare the Hard Disk Footer */
memset(buf, 0, 1024);
@@ -869,7 +837,7 @@ static int vpc_create(const char *filename, QemuOpts *opts, Error **errp)
footer->major = cpu_to_be16(0x0005);
footer->minor = cpu_to_be16(0x0003);
footer->orig_size = cpu_to_be64(total_size);
- footer->size = cpu_to_be64(total_size);
+ footer->current_size = cpu_to_be64(total_size);
footer->cyls = cpu_to_be16(cyls);
footer->heads = heads;
footer->secs_per_cyl = secs_per_cyl;