For the fixed VHD format, round the raw image size to the next

multiple of the cylinder size. This is what qemu-img seems to
be doing. Make sure to handle boundary cases where increasing
the image size by 1 cyclinder's worth would also result in a
change of geometry.
This commit is contained in:
Marcel Moolenaar 2015-06-11 14:30:31 +00:00
parent 728d82cdd2
commit 2697d1ee89

View File

@ -62,6 +62,12 @@ __FBSDID("$FreeBSD$");
#define VHD_SECTOR_SIZE 512
#define VHD_BLOCK_SIZE (4096 * VHD_SECTOR_SIZE) /* 2MB blocks */
struct vhd_geom {
uint16_t cylinders;
uint8_t heads;
uint8_t sectors;
};
struct vhd_footer {
uint64_t cookie;
#define VHD_FOOTER_COOKIE 0x636f6e6563746978ULL
@ -80,9 +86,7 @@ struct vhd_footer {
#define VHD_CREATOR_OS 0x46425344
uint64_t original_size;
uint64_t current_size;
uint16_t cylinders;
uint8_t heads;
uint8_t sectors;
struct vhd_geom geometry;
uint32_t disk_type;
#define VHD_DISK_TYPE_FIXED 2
#define VHD_DISK_TYPE_DYNAMIC 3
@ -111,7 +115,7 @@ vhd_checksum(void *buf, size_t sz)
}
static void
vhd_geometry(struct vhd_footer *footer, uint64_t image_size)
vhd_geometry(uint64_t image_size, struct vhd_geom *geom)
{
lba_t imgsz;
long cth;
@ -120,9 +124,9 @@ vhd_geometry(struct vhd_footer *footer, uint64_t image_size)
if (nheads > 1 && nheads < 256 &&
nsecs > 1 && nsecs < 256 &&
ncyls < 65536) {
be16enc(&footer->cylinders, ncyls);
footer->heads = nheads;
footer->sectors = nsecs;
geom->cylinders = ncyls;
geom->heads = nheads;
geom->sectors = nsecs;
return;
}
@ -130,27 +134,27 @@ vhd_geometry(struct vhd_footer *footer, uint64_t image_size)
if (imgsz > 65536 * 16 * 255)
imgsz = 65536 * 16 * 255;
if (imgsz >= 65535 * 16 * 63) {
be16enc(&footer->cylinders, imgsz / (16 * 255));
footer->heads = 16;
footer->sectors = 255;
geom->cylinders = imgsz / (16 * 255);
geom->heads = 16;
geom->sectors = 255;
return;
}
footer->sectors = 17;
geom->sectors = 17;
cth = imgsz / 17;
footer->heads = (cth + 1023) / 1024;
if (footer->heads < 4)
footer->heads = 4;
if (cth >= (footer->heads * 1024) || footer->heads > 16) {
footer->heads = 16;
footer->sectors = 31;
geom->heads = (cth + 1023) / 1024;
if (geom->heads < 4)
geom->heads = 4;
if (cth >= (geom->heads * 1024) || geom->heads > 16) {
geom->heads = 16;
geom->sectors = 31;
cth = imgsz / 31;
}
if (cth >= (footer->heads * 1024)) {
footer->heads = 16;
footer->sectors = 63;
if (cth >= (geom->heads * 1024)) {
geom->heads = 16;
geom->sectors = 63;
cth = imgsz / 63;
}
be16enc(&footer->cylinders, cth / footer->heads);
geom->cylinders = cth / geom->heads;
}
static uint32_t
@ -198,30 +202,14 @@ vhd_make_footer(struct vhd_footer *footer, uint64_t image_size,
be32enc(&footer->creator_os, VHD_CREATOR_OS);
be64enc(&footer->original_size, image_size);
be64enc(&footer->current_size, image_size);
vhd_geometry(footer, image_size);
vhd_geometry(image_size, &footer->geometry);
be16enc(&footer->geometry.cylinders, footer->geometry.cylinders);
be32enc(&footer->disk_type, disk_type);
mkimg_uuid(&id);
vhd_uuid_enc(&footer->id, &id);
be32enc(&footer->checksum, vhd_checksum(footer, sizeof(*footer)));
}
/*
* We round the image size to 2MB for both the dynamic and
* fixed VHD formats. For dynamic VHD, this is needed to
* have the image size be a multiple of the grain size. For
* fixed VHD this is not really needed, but makes sure that
* it's easy to convert from fixed VHD to dynamic VHD.
*/
static int
vhd_resize(lba_t imgsz)
{
uint64_t imagesz;
imagesz = imgsz * secsz;
imagesz = (imagesz + VHD_BLOCK_SIZE - 1) & ~(VHD_BLOCK_SIZE - 1);
return (image_set_size(imagesz / secsz));
}
/*
* PART 2: Dynamic VHD support
*
@ -261,6 +249,16 @@ _Static_assert(sizeof(struct vhd_dyn_header) == VHD_SECTOR_SIZE * 2,
"Wrong size for header");
#endif
static int
vhd_dyn_resize(lba_t imgsz)
{
uint64_t imagesz;
imagesz = imgsz * secsz;
imagesz = (imagesz + VHD_BLOCK_SIZE - 1) & ~(VHD_BLOCK_SIZE - 1);
return (image_set_size(imagesz / secsz));
}
static int
vhd_dyn_write(int fd)
{
@ -349,16 +347,35 @@ vhd_dyn_write(int fd)
static struct mkimg_format vhd_dyn_format = {
.name = "vhd",
.description = "Virtual Hard Disk",
.resize = vhd_resize,
.resize = vhd_dyn_resize,
.write = vhd_dyn_write,
};
FORMAT_DEFINE(vhd_dyn_format);
/*
* PART 2: Fixed VHD
* PART 3: Fixed VHD
*/
static int
vhd_fix_resize(lba_t imgsz)
{
struct vhd_geom geom;
int64_t imagesz;
imgsz *= secsz;
imagesz = imgsz;
while (1) {
vhd_geometry(imagesz, &geom);
imagesz = (int64_t)geom.cylinders * geom.heads *
geom.sectors * VHD_SECTOR_SIZE;
if (imagesz >= imgsz)
break;
imagesz += geom.heads * geom.sectors * VHD_SECTOR_SIZE;
}
return (image_set_size(imagesz / secsz));
}
static int
vhd_fix_write(int fd)
{
@ -379,7 +396,7 @@ vhd_fix_write(int fd)
static struct mkimg_format vhd_fix_format = {
.name = "vhdf",
.description = "Fixed Virtual Hard Disk",
.resize = vhd_resize,
.resize = vhd_fix_resize,
.write = vhd_fix_write,
};