mirror of
https://github.com/VARCem/MiniVHD.git
synced 2026-07-09 02:26:05 +00:00
Slight rework to allow creation of VHDs of arbitrary size up to the max of 2040 GB, unconstrained by the CHS geometry. Also allow block sizes other than 2 MB.
This commit is contained in:
@@ -21,20 +21,44 @@ typedef enum MVHDError {
|
||||
MVHD_ERR_PAR_NOT_FOUND,
|
||||
MVHD_ERR_INVALID_PAR_UUID,
|
||||
MVHD_ERR_INVALID_GEOM,
|
||||
MVHD_ERR_INVALID_PARAMS,
|
||||
MVHD_ERR_INVALID_SIZE,
|
||||
MVHD_ERR_INVALID_BLOCK_SIZE,
|
||||
MVHD_ERR_INVALID_PARAMS,
|
||||
MVHD_ERR_CONV_SIZE
|
||||
} MVHDError;
|
||||
|
||||
typedef enum MVHDType {
|
||||
MVHD_TYPE_FIXED = 2,
|
||||
MVHD_TYPE_DYNAMIC = 3,
|
||||
MVHD_TYPE_DIFF = 4
|
||||
} MVHDType;
|
||||
|
||||
typedef enum MVHDBlockSize {
|
||||
MVHD_BLOCK_DEFAULT = 0, /**< 2 MB blocks */
|
||||
MVHD_BLOCK_SMALL = 1024, /**< 512 KB blocks */
|
||||
MVHD_BLOCK_LARGE = 4096 /**< 2 MB blocks */
|
||||
} MVHDBlockSize;
|
||||
|
||||
typedef struct MVHDGeom {
|
||||
uint16_t cyl;
|
||||
uint8_t heads;
|
||||
uint8_t spt;
|
||||
} MVHDGeom;
|
||||
|
||||
typedef struct MVHDMeta MVHDMeta;
|
||||
|
||||
typedef void (*mvhd_progress_callback)(uint32_t current_sector, uint32_t total_sectors);
|
||||
|
||||
typedef struct MVHDCreationOptions {
|
||||
int type; /** MVHD_TYPE_FIXED, MVHD_TYPE_DYNAMIC, or MVHD_TYPE_DIFF */
|
||||
char* path; /** Absolute path of the new VHD file */
|
||||
char* parent_path; /** For MVHD_TYPE_DIFF, this is the absolute path of the VHD's parent. For non-diff VHDs, this should be NULL. */
|
||||
uint64_t size_in_bytes; /** Total size of the VHD's virtual disk in bytes. Must be a multiple of 512. If 0, the size is auto-calculated from the geometry field. Ignored for MVHD_TYPE_DIFF. */
|
||||
MVHDGeom geometry; /** The geometry of the VHD. If set to 0, the geometry is auto-calculated from the size_in_bytes field. */
|
||||
uint32_t block_size_in_sectors; /** MVHD_BLOCK_LARGE or MVHD_BLOCK_SMALL, or 0 for the default value. The number of sectors per block. */
|
||||
mvhd_progress_callback progress_callback; /** Optional; if not NULL, gets called to indicate progress on the creation operation. Only applies to MVHD_TYPE_FIXED. */
|
||||
} MVHDCreationOptions;
|
||||
|
||||
typedef struct MVHDMeta MVHDMeta;
|
||||
|
||||
/**
|
||||
* \brief Output a string from a MiniVHD error number
|
||||
*
|
||||
@@ -80,10 +104,9 @@ MVHDMeta* mvhd_open(const char* path, bool readonly, int* err);
|
||||
* \param [in] path is the absolute path to the image to create
|
||||
* \param [in] geom is the HDD geometry of the image to create. Determines final image size
|
||||
* \param [out] err indicates what error occurred, if any
|
||||
* \param [out] progress_callback optional; if not NULL, gets called to indicate progress on the creation operation
|
||||
* \param [out] progress_callback optional; if not NULL, gets called to indicate progress on the creation operation
|
||||
*
|
||||
* \retval 0 if success
|
||||
* \retval < 0 if an error occurrs. Check value of *err for actual error
|
||||
* \retval NULL if an error occurrs. Check value of *err for actual error. Otherwise returns pointer to a MVHDMeta struct
|
||||
*/
|
||||
MVHDMeta* mvhd_create_fixed(const char* path, MVHDGeom geom, int* err, mvhd_progress_callback progress_callback);
|
||||
|
||||
@@ -109,6 +132,18 @@ MVHDMeta* mvhd_create_sparse(const char* path, MVHDGeom geom, int* err);
|
||||
*/
|
||||
MVHDMeta* mvhd_create_diff(const char* path, const char* par_path, int* err);
|
||||
|
||||
/**
|
||||
* \brief Create a VHD using the provided options
|
||||
*
|
||||
* Use mvhd_create_ex if you want more control over the VHD's options. For quick creation, you can use mvhd_create_fixed, mvhd_create_sparse, or mvhd_create_diff.
|
||||
*
|
||||
* \param [in] options the VHD creation options.
|
||||
* \param [out] err indicates what error occurred, if any
|
||||
*
|
||||
* \retval NULL if an error occurrs. Check value of *err for actual error. Otherwise returns pointer to a MVHDMeta struct
|
||||
*/
|
||||
MVHDMeta* mvhd_create_ex(MVHDCreationOptions options, int* err);
|
||||
|
||||
/**
|
||||
* \brief Safely close a VHD image
|
||||
*
|
||||
@@ -122,7 +157,9 @@ void mvhd_close(MVHDMeta* vhdm);
|
||||
* The VHD format uses Cylinder, Heads, Sectors per Track (CHS) when accessing the disk.
|
||||
* The size of the disk can be determined from C * H * S * sector_size.
|
||||
*
|
||||
* Note, maximum VHD size (in bytes) is 65535 * 16 * 255 * 512, which is 127GB
|
||||
* Note, maximum geometry size (in bytes) is 65535 * 16 * 255 * 512, which is 127GB.
|
||||
* However, the maximum VHD size is 2040GB. For VHDs larger than 127GB, the geometry size will be
|
||||
* smaller than the actual VHD size.
|
||||
*
|
||||
* This function determines the appropriate CHS geometry from a provided size in bytes.
|
||||
* The calculations used are those provided in "Appendix: CHS Calculation" from the document
|
||||
@@ -179,7 +216,7 @@ FILE* mvhd_convert_to_raw(const char* utf8_vhd_path, const char* utf8_raw_path,
|
||||
*
|
||||
* \return the number of sectors that were not read, or zero
|
||||
*/
|
||||
int mvhd_read_sectors(MVHDMeta* vhdm, int offset, int num_sectors, void* out_buff);
|
||||
int mvhd_read_sectors(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff);
|
||||
|
||||
/**
|
||||
* \brief Write sectors to VHD file
|
||||
@@ -193,7 +230,7 @@ int mvhd_read_sectors(MVHDMeta* vhdm, int offset, int num_sectors, void* out_buf
|
||||
*
|
||||
* \return the number of sectors that were not written, or zero
|
||||
*/
|
||||
int mvhd_write_sectors(MVHDMeta* vhdm, int offset, int num_sectors, void* in_buff);
|
||||
int mvhd_write_sectors(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff);
|
||||
|
||||
/**
|
||||
* \brief Write zeroed sectors to VHD file
|
||||
@@ -208,5 +245,5 @@ int mvhd_write_sectors(MVHDMeta* vhdm, int offset, int num_sectors, void* in_buf
|
||||
*
|
||||
* \return the number of sectors that were not written, or zero
|
||||
*/
|
||||
int mvhd_format_sectors(MVHDMeta* vhdm, int offset, int num_sectors);
|
||||
int mvhd_format_sectors(MVHDMeta* vhdm, uint32_t offset, int num_sectors);
|
||||
#endif
|
||||
@@ -40,7 +40,8 @@ MVHDMeta* mvhd_convert_to_vhd_fixed(const char* utf8_raw_path, const char* utf8_
|
||||
if (raw_img == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
MVHDMeta *vhdm = mvhd_create_fixed_raw(utf8_vhd_path, raw_img, &geom, err, NULL);
|
||||
uint64_t size_in_bytes = mvhd_calc_size_bytes(&geom);
|
||||
MVHDMeta *vhdm = mvhd_create_fixed_raw(utf8_vhd_path, raw_img, size_in_bytes, &geom, err, NULL);
|
||||
if (vhdm == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
#include "minivhd_create.h"
|
||||
#include "minivhd.h"
|
||||
|
||||
static void mvhd_gen_footer(MVHDFooter* footer, MVHDGeom* geom, MVHDType type, uint64_t sparse_header_off);
|
||||
static void mvhd_gen_sparse_header(MVHDSparseHeader* header, uint32_t num_blks, uint64_t bat_offset);
|
||||
static void mvhd_gen_footer(MVHDFooter* footer, uint64_t size_in_bytes, MVHDGeom* geom, MVHDType type, uint64_t sparse_header_off);
|
||||
static void mvhd_gen_sparse_header(MVHDSparseHeader* header, uint32_t num_blks, uint64_t bat_offset, uint32_t block_size_in_sectors);
|
||||
static int mvhd_gen_par_loc(MVHDSparseHeader* header,
|
||||
const char* child_path,
|
||||
const char* par_path,
|
||||
@@ -21,17 +21,18 @@ static int mvhd_gen_par_loc(MVHDSparseHeader* header,
|
||||
mvhd_utf16* w2ku_path_buff,
|
||||
mvhd_utf16* w2ru_path_buff,
|
||||
MVHDError* err);
|
||||
static MVHDMeta* mvhd_create_sparse_diff(const char* path, const char* par_path, MVHDGeom* geom, int* err);
|
||||
static MVHDMeta* mvhd_create_sparse_diff(const char* path, const char* par_path, uint64_t size_in_bytes, MVHDGeom* geom, uint32_t block_size_in_sectors, int* err);
|
||||
|
||||
/**
|
||||
* \brief Populate a VHD footer
|
||||
*
|
||||
* \param [in] footer to populate
|
||||
* \param [in] size_in_bytes is the total size of the virtual hard disk in bytes
|
||||
* \param [in] geom to use
|
||||
* \param [in] type of HVD that is being created
|
||||
* \param [in] sparse_header_off, an absolute file offset to the sparse header. Not used for fixed VHD images
|
||||
*/
|
||||
static void mvhd_gen_footer(MVHDFooter* footer, MVHDGeom* geom, MVHDType type, uint64_t sparse_header_off) {
|
||||
static void mvhd_gen_footer(MVHDFooter* footer, uint64_t size_in_bytes, MVHDGeom* geom, MVHDType type, uint64_t sparse_header_off) {
|
||||
memcpy(footer->cookie, "conectix", sizeof footer->cookie);
|
||||
footer->features = 0x00000002;
|
||||
footer->fi_fmt_vers = 0x00010000;
|
||||
@@ -40,7 +41,7 @@ static void mvhd_gen_footer(MVHDFooter* footer, MVHDGeom* geom, MVHDType type, u
|
||||
memcpy(footer->cr_app, "mvhd", sizeof footer->cr_app);
|
||||
footer->cr_vers = 0x000e0000;
|
||||
memcpy(footer->cr_host_os, "Wi2k", sizeof footer->cr_host_os);
|
||||
footer->orig_sz = footer->curr_sz = mvhd_calc_size_bytes(geom);
|
||||
footer->orig_sz = footer->curr_sz = size_in_bytes;
|
||||
footer->geom.cyl = geom->cyl;
|
||||
footer->geom.heads = geom->heads;
|
||||
footer->geom.spt = geom->spt;
|
||||
@@ -55,14 +56,15 @@ static void mvhd_gen_footer(MVHDFooter* footer, MVHDGeom* geom, MVHDType type, u
|
||||
* \param [in] header for sparse and differencing images
|
||||
* \param [in] num_blks is the number of data blocks that the image contains
|
||||
* \param [in] bat_offset is the absolute file offset for start of the Block Allocation Table
|
||||
* \param [in] block_size_in_sectors is the block size in sectors.
|
||||
*/
|
||||
static void mvhd_gen_sparse_header(MVHDSparseHeader* header, uint32_t num_blks, uint64_t bat_offset) {
|
||||
static void mvhd_gen_sparse_header(MVHDSparseHeader* header, uint32_t num_blks, uint64_t bat_offset, uint32_t block_size_in_sectors) {
|
||||
memcpy(header->cookie, "cxsparse", sizeof header->cookie);
|
||||
header->data_offset = 0xffffffffffffffff;
|
||||
header->bat_offset = bat_offset;
|
||||
header->head_vers = 0x00010000;
|
||||
header->max_bat_ent = num_blks;
|
||||
header->block_sz = (uint32_t)MVHD_SECT_PER_BLOCK * (uint32_t)MVHD_SECTOR_SIZE;
|
||||
header->block_sz = block_size_in_sectors * (uint32_t)MVHD_SECTOR_SIZE;
|
||||
header->checksum = mvhd_gen_sparse_checksum(header);
|
||||
}
|
||||
|
||||
@@ -165,7 +167,8 @@ end:
|
||||
}
|
||||
|
||||
MVHDMeta* mvhd_create_fixed(const char* path, MVHDGeom geom, int* err, mvhd_progress_callback progress_callback) {
|
||||
return mvhd_create_fixed_raw(path, NULL, &geom, err, progress_callback);
|
||||
uint64_t size_in_bytes = mvhd_calc_size_bytes(&geom);
|
||||
return mvhd_create_fixed_raw(path, NULL, size_in_bytes, &geom, err, progress_callback);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -176,7 +179,7 @@ MVHDMeta* mvhd_create_fixed(const char* path, MVHDGeom geom, int* err, mvhd_prog
|
||||
*
|
||||
* \param [in] raw_image file handle to a raw disk image to populate VHD
|
||||
*/
|
||||
MVHDMeta* mvhd_create_fixed_raw(const char* path, FILE* raw_img, MVHDGeom* geom, int* err, mvhd_progress_callback progress_callback) {
|
||||
MVHDMeta* mvhd_create_fixed_raw(const char* path, FILE* raw_img, uint64_t size_in_bytes, MVHDGeom* geom, int* err, mvhd_progress_callback progress_callback) {
|
||||
uint8_t img_data[MVHD_SECTOR_SIZE] = {0};
|
||||
uint8_t footer_buff[MVHD_FOOTER_SIZE] = {0};
|
||||
MVHDMeta* vhdm = calloc(1, sizeof *vhdm);
|
||||
@@ -197,7 +200,7 @@ MVHDMeta* mvhd_create_fixed_raw(const char* path, FILE* raw_img, MVHDGeom* geom,
|
||||
goto cleanup_vhdm;
|
||||
}
|
||||
mvhd_fseeko64(f, 0, SEEK_SET);
|
||||
uint32_t size_sectors = mvhd_calc_size_sectors(geom);
|
||||
uint32_t size_sectors = (uint32_t)(size_in_bytes / MVHD_SECTOR_SIZE);
|
||||
uint32_t s;
|
||||
if (progress_callback)
|
||||
progress_callback(0, size_sectors);
|
||||
@@ -209,7 +212,7 @@ MVHDMeta* mvhd_create_fixed_raw(const char* path, FILE* raw_img, MVHDGeom* geom,
|
||||
*err = MVHD_ERR_CONV_SIZE;
|
||||
goto cleanup_vhdm;
|
||||
}
|
||||
mvhd_gen_footer(&vhdm->footer, geom, MVHD_TYPE_FIXED, 0);
|
||||
mvhd_gen_footer(&vhdm->footer, raw_size, geom, MVHD_TYPE_FIXED, 0);
|
||||
mvhd_fseeko64(raw_img, 0, SEEK_SET);
|
||||
for (s = 0; s < size_sectors; s++) {
|
||||
fread(img_data, sizeof img_data, 1, raw_img);
|
||||
@@ -218,7 +221,7 @@ MVHDMeta* mvhd_create_fixed_raw(const char* path, FILE* raw_img, MVHDGeom* geom,
|
||||
progress_callback(s + 1, size_sectors);
|
||||
}
|
||||
} else {
|
||||
mvhd_gen_footer(&vhdm->footer, geom, MVHD_TYPE_FIXED, 0);
|
||||
mvhd_gen_footer(&vhdm->footer, size_in_bytes, geom, MVHD_TYPE_FIXED, 0);
|
||||
for (s = 0; s < size_sectors; s++) {
|
||||
fwrite(img_data, sizeof img_data, 1, f);
|
||||
if (progress_callback)
|
||||
@@ -245,12 +248,14 @@ end:
|
||||
*
|
||||
* \param [in] path is the absolute path to the VHD file to create
|
||||
* \param [in] par_path is the absolute path to a parent image. If NULL, a sparse image is created, otherwise create a differencing image
|
||||
* \param [in] size_in_bytes is the total size in bytes of the virtual hard disk image
|
||||
* \param [in] geom is the HDD geometry of the image to create. Determines final image size
|
||||
* \param [in] block_size_in_sectors is the block size in sectors
|
||||
* \param [out] err indicates what error occurred, if any
|
||||
*
|
||||
* \return NULL if an error occurrs. Check value of *err for actual error. Otherwise returns pointer to a MVHDMeta struct
|
||||
*/
|
||||
static MVHDMeta* mvhd_create_sparse_diff(const char* path, const char* par_path, MVHDGeom* geom, int* err) {
|
||||
static MVHDMeta* mvhd_create_sparse_diff(const char* path, const char* par_path, uint64_t size_in_bytes, MVHDGeom* geom, uint32_t block_size_in_sectors, int* err) {
|
||||
uint8_t footer_buff[MVHD_FOOTER_SIZE] = {0};
|
||||
uint8_t sparse_buff[MVHD_SPARSE_SIZE] = {0};
|
||||
uint8_t bat_sect[MVHD_SECTOR_SIZE];
|
||||
@@ -278,6 +283,7 @@ static MVHDMeta* mvhd_create_sparse_diff(const char* path, const char* par_path,
|
||||
par_geom.heads = par_vhdm->footer.geom.heads;
|
||||
par_geom.spt = par_vhdm->footer.geom.spt;
|
||||
geom = &par_geom;
|
||||
size_in_bytes = par_vhdm->footer.curr_sz;
|
||||
} else if (geom != NULL && (geom->cyl == 0 || geom->heads == 0 || geom->spt == 0)) {
|
||||
*err = MVHD_ERR_INVALID_GEOM;
|
||||
goto cleanup_vhdm;
|
||||
@@ -293,21 +299,22 @@ static MVHDMeta* mvhd_create_sparse_diff(const char* path, const char* par_path,
|
||||
mvhd_fseeko64(f, 0, SEEK_SET);
|
||||
/* Note, the sparse header follows the footer copy at the beginning of the file */
|
||||
if (par_path == NULL) {
|
||||
mvhd_gen_footer(&vhdm->footer, geom, MVHD_TYPE_DYNAMIC, MVHD_FOOTER_SIZE);
|
||||
mvhd_gen_footer(&vhdm->footer, size_in_bytes, geom, MVHD_TYPE_DYNAMIC, MVHD_FOOTER_SIZE);
|
||||
} else {
|
||||
mvhd_gen_footer(&vhdm->footer, geom, MVHD_TYPE_DIFF, MVHD_FOOTER_SIZE);
|
||||
mvhd_gen_footer(&vhdm->footer, size_in_bytes, geom, MVHD_TYPE_DIFF, MVHD_FOOTER_SIZE);
|
||||
}
|
||||
mvhd_footer_to_buffer(&vhdm->footer, footer_buff);
|
||||
/* As mentioned, start with a copy of the footer */
|
||||
fwrite(footer_buff, sizeof footer_buff, 1, f);
|
||||
/**
|
||||
* Calculate the number of (2MB) data blocks required to store the entire
|
||||
* Calculate the number of (2MB or 512KB) data blocks required to store the entire
|
||||
* contents of the disk image, followed by the number of sectors the
|
||||
* BAT occupies in the image. Note, the BAT is sector aligned, and is padded
|
||||
* to the next sector boundary
|
||||
* */
|
||||
uint32_t num_blks = mvhd_calc_size_sectors(geom) / MVHD_SECT_PER_BLOCK;
|
||||
if (mvhd_calc_size_sectors(geom) % MVHD_SECT_PER_BLOCK != 0) {
|
||||
uint32_t size_in_sectors = (uint32_t)(size_in_bytes / MVHD_SECTOR_SIZE);
|
||||
uint32_t num_blks = size_in_sectors / block_size_in_sectors;
|
||||
if (size_in_sectors % block_size_in_sectors != 0) {
|
||||
num_blks += 1;
|
||||
}
|
||||
uint32_t num_bat_sect = num_blks / MVHD_BAT_ENT_PER_SECT;
|
||||
@@ -344,7 +351,7 @@ static MVHDMeta* mvhd_create_sparse_diff(const char* path, const char* par_path,
|
||||
goto cleanup_vhdm;
|
||||
}
|
||||
}
|
||||
mvhd_gen_sparse_header(&vhdm->sparse, num_blks, bat_offset);
|
||||
mvhd_gen_sparse_header(&vhdm->sparse, num_blks, bat_offset, block_size_in_sectors);
|
||||
mvhd_header_to_buffer(&vhdm->sparse, sparse_buff);
|
||||
fwrite(sparse_buff, sizeof sparse_buff, 1, f);
|
||||
/* The BAT sectors need to be filled with 0xffffffff */
|
||||
@@ -398,9 +405,80 @@ end:
|
||||
}
|
||||
|
||||
MVHDMeta* mvhd_create_sparse(const char* path, MVHDGeom geom, int* err) {
|
||||
return mvhd_create_sparse_diff(path, NULL, &geom, err);
|
||||
uint64_t size_in_bytes = mvhd_calc_size_bytes(&geom);
|
||||
return mvhd_create_sparse_diff(path, NULL, size_in_bytes, &geom, MVHD_BLOCK_LARGE, err);
|
||||
}
|
||||
|
||||
MVHDMeta* mvhd_create_diff(const char* path, const char* par_path, int* err) {
|
||||
return mvhd_create_sparse_diff(path, par_path, NULL, err);
|
||||
return mvhd_create_sparse_diff(path, par_path, 0, NULL, MVHD_BLOCK_LARGE, err);
|
||||
}
|
||||
|
||||
MVHDMeta* mvhd_create_ex(MVHDCreationOptions options, int* err) {
|
||||
uint32_t geom_sector_size;
|
||||
switch (options.type)
|
||||
{
|
||||
case MVHD_TYPE_FIXED:
|
||||
case MVHD_TYPE_DYNAMIC:
|
||||
geom_sector_size = mvhd_calc_size_sectors(&(options.geometry));
|
||||
if ((options.size_in_bytes > 0 && (options.size_in_bytes % MVHD_SECTOR_SIZE) > 0)
|
||||
|| (options.size_in_bytes > MVHD_MAX_SIZE_IN_BYTES)
|
||||
|| (options.size_in_bytes == 0 && geom_sector_size == 0))
|
||||
{
|
||||
*err = MVHD_ERR_INVALID_SIZE;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (options.size_in_bytes > 0 && ((uint64_t)geom_sector_size * MVHD_SECTOR_SIZE) > options.size_in_bytes)
|
||||
{
|
||||
*err = MVHD_ERR_INVALID_GEOM;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (options.size_in_bytes == 0)
|
||||
options.size_in_bytes = (uint64_t)geom_sector_size * MVHD_SECTOR_SIZE;
|
||||
|
||||
if (geom_sector_size == 0)
|
||||
options.geometry = mvhd_calculate_geometry(options.size_in_bytes);
|
||||
break;
|
||||
case MVHD_TYPE_DIFF:
|
||||
if (options.parent_path == NULL)
|
||||
{
|
||||
*err = MVHD_ERR_FILE;
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
*err = MVHD_ERR_TYPE;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (options.path == NULL)
|
||||
{
|
||||
*err = MVHD_ERR_FILE;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (options.type != MVHD_TYPE_FIXED)
|
||||
{
|
||||
if (options.block_size_in_sectors == MVHD_BLOCK_DEFAULT)
|
||||
options.block_size_in_sectors = MVHD_BLOCK_LARGE;
|
||||
|
||||
if (options.block_size_in_sectors != MVHD_BLOCK_LARGE && options.block_size_in_sectors != MVHD_BLOCK_SMALL)
|
||||
{
|
||||
*err = MVHD_ERR_INVALID_BLOCK_SIZE;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
switch (options.type)
|
||||
{
|
||||
case MVHD_TYPE_FIXED:
|
||||
return mvhd_create_fixed_raw(options.path, NULL, options.size_in_bytes, &(options.geometry), err, options.progress_callback);
|
||||
case MVHD_TYPE_DYNAMIC:
|
||||
return mvhd_create_sparse_diff(options.path, NULL, options.size_in_bytes, &(options.geometry), options.block_size_in_sectors, err);
|
||||
case MVHD_TYPE_DIFF:
|
||||
return mvhd_create_sparse_diff(options.path, options.parent_path, 0, NULL, options.block_size_in_sectors, err);
|
||||
}
|
||||
|
||||
return NULL; /* Make the compiler happy */
|
||||
}
|
||||
@@ -3,6 +3,6 @@
|
||||
#include <stdio.h>
|
||||
#include "minivhd.h"
|
||||
|
||||
MVHDMeta* mvhd_create_fixed_raw(const char* path, FILE* raw_img, MVHDGeom* geom, int* err, mvhd_progress_callback progress_callback);
|
||||
MVHDMeta* mvhd_create_fixed_raw(const char* path, FILE* raw_img, uint64_t size_in_bytes, MVHDGeom* geom, int* err, mvhd_progress_callback progress_callback);
|
||||
|
||||
#endif
|
||||
@@ -8,9 +8,10 @@
|
||||
#define MVHD_SPARSE_SIZE 1024
|
||||
|
||||
#define MVHD_SECTOR_SIZE 512
|
||||
#define MVHD_SECT_PER_BLOCK 4096 /* 2MB block size */
|
||||
#define MVHD_BAT_ENT_PER_SECT 128
|
||||
|
||||
#define MVHD_MAX_SIZE_IN_BYTES 0x1fe00000000
|
||||
|
||||
#define MVHD_SPARSE_BLK 0xffffffff
|
||||
/* For simplicity, we don't handle paths longer than this
|
||||
* Note, this is the max path in characters, as that is what
|
||||
@@ -22,12 +23,6 @@
|
||||
#define MVHD_DIF_LOC_W2RU 0x57327275
|
||||
#define MVHD_DIF_LOC_W2KU 0x57326B75
|
||||
|
||||
typedef enum MVHDType {
|
||||
MVHD_TYPE_FIXED = 2,
|
||||
MVHD_TYPE_DYNAMIC = 3,
|
||||
MVHD_TYPE_DIFF = 4
|
||||
} MVHDType;
|
||||
|
||||
typedef struct MVHDSectorBitmap {
|
||||
uint8_t* curr_bitmap;
|
||||
int sector_count;
|
||||
@@ -90,8 +85,8 @@ struct MVHDMeta {
|
||||
uint32_t* block_offset;
|
||||
int sect_per_block;
|
||||
MVHDSectorBitmap bitmap;
|
||||
int (*read_sectors)(MVHDMeta*, int, int, void*);
|
||||
int (*write_sectors)(MVHDMeta*, int, int, void*);
|
||||
int (*read_sectors)(MVHDMeta*, uint32_t, int, void*);
|
||||
int (*write_sectors)(MVHDMeta*, uint32_t, int, void*);
|
||||
struct {
|
||||
uint8_t* zero_data;
|
||||
int sector_count;
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#define VHD_CLEARBIT(A,k) ( A[(k/8)] &= ~(0x80 >> (k%8)) )
|
||||
#define VHD_TESTBIT(A,k) ( A[(k/8)] & (0x80 >> (k%8)) )
|
||||
|
||||
static inline void mvhd_check_sectors(int offset, int num_sectors, int total_sectors, int* transfer_sect, int* trunc_sect);
|
||||
static inline void mvhd_check_sectors(uint32_t offset, int num_sectors, uint32_t total_sectors, int* transfer_sect, int* trunc_sect);
|
||||
static void mvhd_read_sect_bitmap(MVHDMeta* vhdm, int blk);
|
||||
static void mvhd_write_bat_entry(MVHDMeta* vhdm, int blk);
|
||||
static void mvhd_create_block(MVHDMeta* vhdm, int blk);
|
||||
@@ -31,10 +31,10 @@ static void mvhd_write_curr_sect_bitmap(MVHDMeta* vhdm);
|
||||
* This may be lower than num_sectors if offset + num_sectors >= total_sectors
|
||||
* \param [out] trunc_sectors The number of sectors truncated if transfer_sectors < num_sectors
|
||||
*/
|
||||
static inline void mvhd_check_sectors(int offset, int num_sectors, int total_sectors, int* transfer_sect, int* trunc_sect) {
|
||||
static inline void mvhd_check_sectors(uint32_t offset, int num_sectors, uint32_t total_sectors, int* transfer_sect, int* trunc_sect) {
|
||||
*transfer_sect = num_sectors;
|
||||
*trunc_sect = 0;
|
||||
if ((total_sectors - offset) < *transfer_sect) {
|
||||
if ((total_sectors - offset) < (uint32_t)*transfer_sect) {
|
||||
*transfer_sect = total_sectors - offset;
|
||||
*trunc_sect = num_sectors - *transfer_sect;
|
||||
}
|
||||
@@ -128,7 +128,7 @@ static void mvhd_create_block(MVHDMeta* vhdm, int blk) {
|
||||
}
|
||||
abs_offset += padding_amount;
|
||||
}
|
||||
int sect_offset = (int)(abs_offset / MVHD_SECTOR_SIZE);
|
||||
uint32_t sect_offset = (uint32_t)(abs_offset / MVHD_SECTOR_SIZE);
|
||||
int blk_size_sectors = vhdm->sparse.block_sz / MVHD_SECTOR_SIZE;
|
||||
mvhd_write_empty_sectors(vhdm->f, vhdm->bitmap.sector_count + blk_size_sectors);
|
||||
/* Add a bit of padding. That's what Windows appears to do, although it's not strictly necessary... */
|
||||
@@ -140,10 +140,10 @@ static void mvhd_create_block(MVHDMeta* vhdm, int blk) {
|
||||
mvhd_write_bat_entry(vhdm, blk);
|
||||
}
|
||||
|
||||
int mvhd_fixed_read(MVHDMeta* vhdm, int offset, int num_sectors, void* out_buff) {
|
||||
int mvhd_fixed_read(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff) {
|
||||
int64_t addr;
|
||||
int transfer_sectors, truncated_sectors;
|
||||
int total_sectors = vhdm->footer.geom.cyl * vhdm->footer.geom.heads * vhdm->footer.geom.spt;
|
||||
uint32_t total_sectors = (uint32_t)(vhdm->footer.curr_sz / MVHD_SECTOR_SIZE);
|
||||
mvhd_check_sectors(offset, num_sectors, total_sectors, &transfer_sectors, &truncated_sectors);
|
||||
addr = (int64_t)offset * MVHD_SECTOR_SIZE;
|
||||
mvhd_fseeko64(vhdm->f, addr, SEEK_SET);
|
||||
@@ -151,13 +151,14 @@ int mvhd_fixed_read(MVHDMeta* vhdm, int offset, int num_sectors, void* out_buff)
|
||||
return truncated_sectors;
|
||||
}
|
||||
|
||||
int mvhd_sparse_read(MVHDMeta* vhdm, int offset, int num_sectors, void* out_buff) {
|
||||
int mvhd_sparse_read(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff) {
|
||||
int transfer_sectors, truncated_sectors;
|
||||
int total_sectors = vhdm->footer.geom.cyl * vhdm->footer.geom.heads * vhdm->footer.geom.spt;
|
||||
uint32_t total_sectors = (uint32_t)(vhdm->footer.curr_sz / MVHD_SECTOR_SIZE);
|
||||
mvhd_check_sectors(offset, num_sectors, total_sectors, &transfer_sectors, &truncated_sectors);
|
||||
uint8_t* buff = (uint8_t*)out_buff;
|
||||
int64_t addr;
|
||||
int s, ls, blk, prev_blk, sib;
|
||||
uint32_t s, ls;
|
||||
int blk, prev_blk, sib;
|
||||
ls = offset + transfer_sectors;
|
||||
prev_blk = -1;
|
||||
for (s = offset; s < ls; s++) {
|
||||
@@ -184,13 +185,14 @@ int mvhd_sparse_read(MVHDMeta* vhdm, int offset, int num_sectors, void* out_buff
|
||||
return truncated_sectors;
|
||||
}
|
||||
|
||||
int mvhd_diff_read(MVHDMeta* vhdm, int offset, int num_sectors, void* out_buff) {
|
||||
int mvhd_diff_read(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff) {
|
||||
int transfer_sectors, truncated_sectors;
|
||||
int total_sectors = vhdm->footer.geom.cyl * vhdm->footer.geom.heads * vhdm->footer.geom.spt;
|
||||
uint32_t total_sectors = (uint32_t)(vhdm->footer.curr_sz / MVHD_SECTOR_SIZE);
|
||||
mvhd_check_sectors(offset, num_sectors, total_sectors, &transfer_sectors, &truncated_sectors);
|
||||
uint8_t* buff = (uint8_t*)out_buff;
|
||||
MVHDMeta* curr_vhdm = vhdm;
|
||||
int s, ls, blk, sib;
|
||||
uint32_t s, ls;
|
||||
int blk, sib;
|
||||
ls = offset + transfer_sectors;
|
||||
for (s = offset; s < ls; s++) {
|
||||
while (curr_vhdm->footer.disk_type == MVHD_TYPE_DIFF) {
|
||||
@@ -216,10 +218,10 @@ int mvhd_diff_read(MVHDMeta* vhdm, int offset, int num_sectors, void* out_buff)
|
||||
return truncated_sectors;
|
||||
}
|
||||
|
||||
int mvhd_fixed_write(MVHDMeta* vhdm, int offset, int num_sectors, void* in_buff) {
|
||||
int mvhd_fixed_write(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff) {
|
||||
int64_t addr;
|
||||
int transfer_sectors, truncated_sectors;
|
||||
int total_sectors = vhdm->footer.geom.cyl * vhdm->footer.geom.heads * vhdm->footer.geom.spt;
|
||||
uint32_t total_sectors = (uint32_t)(vhdm->footer.curr_sz / MVHD_SECTOR_SIZE);
|
||||
mvhd_check_sectors(offset, num_sectors, total_sectors, &transfer_sectors, &truncated_sectors);
|
||||
addr = (int64_t)offset * MVHD_SECTOR_SIZE;
|
||||
mvhd_fseeko64(vhdm->f, addr, SEEK_SET);
|
||||
@@ -227,13 +229,14 @@ int mvhd_fixed_write(MVHDMeta* vhdm, int offset, int num_sectors, void* in_buff)
|
||||
return truncated_sectors;
|
||||
}
|
||||
|
||||
int mvhd_sparse_diff_write(MVHDMeta* vhdm, int offset, int num_sectors, void* in_buff) {
|
||||
int mvhd_sparse_diff_write(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff) {
|
||||
int transfer_sectors, truncated_sectors;
|
||||
int total_sectors = vhdm->footer.geom.cyl * vhdm->footer.geom.heads * vhdm->footer.geom.spt;
|
||||
uint32_t total_sectors = (uint32_t)(vhdm->footer.curr_sz / MVHD_SECTOR_SIZE);
|
||||
mvhd_check_sectors(offset, num_sectors, total_sectors, &transfer_sectors, &truncated_sectors);
|
||||
uint8_t* buff = (uint8_t*)in_buff;
|
||||
int64_t addr;
|
||||
int s, ls, blk, prev_blk, sib;
|
||||
uint32_t s, ls;
|
||||
int blk, prev_blk, sib;
|
||||
ls = offset + transfer_sectors;
|
||||
prev_blk = -1;
|
||||
for (s = offset; s < ls; s++) {
|
||||
@@ -268,6 +271,6 @@ int mvhd_sparse_diff_write(MVHDMeta* vhdm, int offset, int num_sectors, void* in
|
||||
return truncated_sectors;
|
||||
}
|
||||
|
||||
int mvhd_noop_write(MVHDMeta* vhdm, int offset, int num_sectors, void* in_buff) {
|
||||
int mvhd_noop_write(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff) {
|
||||
return 0;
|
||||
}
|
||||
@@ -28,7 +28,7 @@ void mvhd_write_empty_sectors(FILE* f, int sector_count);
|
||||
* \retval 0 num_sectors were read from file
|
||||
* \retval >0 < num_sectors were read from file
|
||||
*/
|
||||
int mvhd_fixed_read(MVHDMeta* vhdm, int offset, int num_sectors, void* out_buff);
|
||||
int mvhd_fixed_read(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff);
|
||||
|
||||
/**
|
||||
* \brief Read a sparse VHD image
|
||||
@@ -48,7 +48,7 @@ int mvhd_fixed_read(MVHDMeta* vhdm, int offset, int num_sectors, void* out_buff)
|
||||
* \retval 0 num_sectors were read from file
|
||||
* \retval >0 < num_sectors were read from file
|
||||
*/
|
||||
int mvhd_sparse_read(MVHDMeta* vhdm, int offset, int num_sectors, void* out_buff);
|
||||
int mvhd_sparse_read(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff);
|
||||
|
||||
/**
|
||||
* \brief Read a differencing VHD image
|
||||
@@ -71,7 +71,7 @@ int mvhd_sparse_read(MVHDMeta* vhdm, int offset, int num_sectors, void* out_buff
|
||||
* \retval 0 num_sectors were read from file
|
||||
* \retval >0 < num_sectors were read from file
|
||||
*/
|
||||
int mvhd_diff_read(MVHDMeta* vhdm, int offset, int num_sectors, void* out_buff);
|
||||
int mvhd_diff_read(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff);
|
||||
|
||||
/**
|
||||
* \brief Write to a fixed VHD image
|
||||
@@ -88,7 +88,7 @@ int mvhd_diff_read(MVHDMeta* vhdm, int offset, int num_sectors, void* out_buff);
|
||||
* \retval 0 num_sectors were written to file
|
||||
* \retval >0 < num_sectors were written to file
|
||||
*/
|
||||
int mvhd_fixed_write(MVHDMeta* vhdm, int offset, int num_sectors, void* in_buff);
|
||||
int mvhd_fixed_write(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff);
|
||||
|
||||
/**
|
||||
* \brief Write to a sparse or differencing VHD image
|
||||
@@ -113,7 +113,7 @@ int mvhd_fixed_write(MVHDMeta* vhdm, int offset, int num_sectors, void* in_buff)
|
||||
* \retval 0 num_sectors were written to file
|
||||
* \retval >0 < num_sectors were written to file
|
||||
*/
|
||||
int mvhd_sparse_diff_write(MVHDMeta* vhdm, int offset, int num_sectors, void* in_buff);
|
||||
int mvhd_sparse_diff_write(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff);
|
||||
|
||||
/**
|
||||
* \brief A no-op function to "write" to read-only VHD images
|
||||
@@ -127,6 +127,6 @@ int mvhd_sparse_diff_write(MVHDMeta* vhdm, int offset, int num_sectors, void* in
|
||||
* \retval 0 num_sectors were written to file
|
||||
* \retval >0 < num_sectors were written to file
|
||||
*/
|
||||
int mvhd_noop_write(MVHDMeta* vhdm, int offset, int num_sectors, void* in_buff);
|
||||
int mvhd_noop_write(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff);
|
||||
|
||||
#endif
|
||||
@@ -328,19 +328,18 @@ bool mvhd_file_is_vhd(FILE* f) {
|
||||
|
||||
MVHDGeom mvhd_calculate_geometry(uint64_t size) {
|
||||
MVHDGeom chs;
|
||||
uint64_t ts = size / MVHD_SECTOR_SIZE;
|
||||
uint32_t ts = (uint32_t)(size / MVHD_SECTOR_SIZE);
|
||||
uint32_t spt, heads, cyl, cth;
|
||||
if (ts > 65535 * 16 * 255) {
|
||||
ts = 65535 * 16 * 255;
|
||||
}
|
||||
if (ts >= 65535 * 16 * 63) {
|
||||
ts = 65535 * 16 * 63;
|
||||
if (ts >= 65535 * 16 * 63) {
|
||||
spt = 255;
|
||||
heads = 16;
|
||||
cth = (uint32_t)ts / spt;
|
||||
cth = ts / spt;
|
||||
} else {
|
||||
spt = 17;
|
||||
cth = (uint32_t)ts / spt;
|
||||
cth = ts / spt;
|
||||
heads = (cth + 1023) / 1024;
|
||||
if (heads < 4) {
|
||||
heads = 4;
|
||||
@@ -348,12 +347,12 @@ MVHDGeom mvhd_calculate_geometry(uint64_t size) {
|
||||
if (cth >= (heads * 1024) || heads > 16) {
|
||||
spt = 31;
|
||||
heads = 16;
|
||||
cth = (uint32_t)ts / spt;
|
||||
cth = ts / spt;
|
||||
}
|
||||
if (cth >= (heads * 1024)) {
|
||||
spt = 63;
|
||||
heads = 16;
|
||||
cth = (uint32_t)ts / spt;
|
||||
cth = ts / spt;
|
||||
}
|
||||
}
|
||||
cyl = cth / heads;
|
||||
@@ -475,19 +474,20 @@ void mvhd_close(MVHDMeta* vhdm) {
|
||||
}
|
||||
}
|
||||
|
||||
int mvhd_read_sectors(MVHDMeta* vhdm, int offset, int num_sectors, void* out_buff) {
|
||||
int mvhd_read_sectors(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff) {
|
||||
return vhdm->read_sectors(vhdm, offset, num_sectors, out_buff);
|
||||
}
|
||||
|
||||
int mvhd_write_sectors(MVHDMeta* vhdm, int offset, int num_sectors, void* in_buff) {
|
||||
int mvhd_write_sectors(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff) {
|
||||
return vhdm->write_sectors(vhdm, offset, num_sectors, in_buff);
|
||||
}
|
||||
|
||||
int mvhd_format_sectors(MVHDMeta* vhdm, int offset, int num_sectors) {
|
||||
int mvhd_format_sectors(MVHDMeta* vhdm, uint32_t offset, int num_sectors) {
|
||||
int num_full = num_sectors / vhdm->format_buffer.sector_count;
|
||||
int remain = num_sectors % vhdm->format_buffer.sector_count;
|
||||
for (int i = 0; i < num_full; i++) {
|
||||
vhdm->write_sectors(vhdm, offset, vhdm->format_buffer.sector_count, vhdm->format_buffer.zero_data);
|
||||
offset += vhdm->format_buffer.sector_count;
|
||||
}
|
||||
vhdm->write_sectors(vhdm, offset, remain, vhdm->format_buffer.zero_data);
|
||||
return 0;
|
||||
|
||||
@@ -225,6 +225,10 @@ const char* mvhd_strerr(MVHDError err) {
|
||||
return "UUID mismatch between child and parent VHD";
|
||||
case MVHD_ERR_INVALID_GEOM:
|
||||
return "invalid geometry detected";
|
||||
case MVHD_ERR_INVALID_SIZE:
|
||||
return "invalid size";
|
||||
case MVHD_ERR_INVALID_BLOCK_SIZE:
|
||||
return "invalid block size";
|
||||
case MVHD_ERR_INVALID_PARAMS:
|
||||
return "invalid parameters passed to function";
|
||||
case MVHD_ERR_CONV_SIZE:
|
||||
|
||||
Reference in New Issue
Block a user