diff options
Diffstat (limited to 'block/vpc.c')
-rw-r--r-- | block/vpc.c | 106 |
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; |