From cdb43be4eed44c75dd73d45e72f7c07122f053fc Mon Sep 17 00:00:00 2001 From: shermp <14854761+shermp@users.noreply.github.com> Date: Fri, 15 Nov 2019 18:55:56 +1300 Subject: [PATCH] Added initial raw image conversion --- src/minivhd.h | 36 ++++++++++++++- src/minivhd_convert.c | 105 ++++++++++++++++++++++++++++++++++++++++++ src/minivhd_create.c | 54 ++++++++++++++++++---- src/minivhd_create.h | 8 ++++ src/minivhd_util.c | 2 + 5 files changed, 196 insertions(+), 9 deletions(-) create mode 100644 src/minivhd_convert.c create mode 100644 src/minivhd_create.h diff --git a/src/minivhd.h b/src/minivhd.h index b01128c..c4825af 100644 --- a/src/minivhd.h +++ b/src/minivhd.h @@ -21,7 +21,8 @@ typedef enum MVHDError { MVHD_ERR_PAR_NOT_FOUND, MVHD_ERR_INVALID_PAR_UUID, MVHD_ERR_INVALID_GEOM, - MVHD_ERR_INVALID_PARAMS + MVHD_ERR_INVALID_PARAMS, + MVHD_ERR_CONV_SIZE } MVHDError; typedef struct MVHDGeom { @@ -131,6 +132,39 @@ void mvhd_close(MVHDMeta* vhdm); */ MVHDGeom mvhd_calculate_geometry(uint64_t size); +/** + * \brief Convert a raw disk image to a fixed VHD image + * + * \param [in] utf8_raw_path is the path of the raw image to convert + * \param [in] utf8_vhd_path is the path of the VHD to create + * \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 + */ +MVHDMeta* mvhd_convert_to_vhd_fixed(const char* utf8_raw_path, const char* utf8_vhd_path, int* err); + +/** + * \brief Convert a raw disk image to a sparse VHD image + * + * \param [in] utf8_raw_path is the path of the raw image to convert + * \param [in] utf8_vhd_path is the path of the VHD to create + * \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 + */ +MVHDMeta* mvhd_convert_to_vhd_sparse(const char* utf8_raw_path, const char* utf8_vhd_path, int* err); + +/** + * \brief Convert a VHD image to a raw disk image + * + * \param [in] utf8_vhd_path is the path of the VHD to convert + * \param [in] utf8_raw_path is the path of the raw image to create + * \param [out] err indicates what error occurred, if any + * + * \return NULL if an error occurrs. Check value of *err for actual error. Otherwise returns the raw disk image FILE pointer + */ +FILE* mvhd_convert_to_raw(const char* utf8_vhd_path, const char* utf8_raw_path, int *err); + /** * \brief Read sectors from VHD file * diff --git a/src/minivhd_convert.c b/src/minivhd_convert.c new file mode 100644 index 0000000..05e7aa3 --- /dev/null +++ b/src/minivhd_convert.c @@ -0,0 +1,105 @@ +#include +#include +#include +#include +#include +#include "minivhd_create.h" +#include "minivhd_internal.h" +#include "minivhd_util.h" +#include "minivhd.h" + +static FILE* mvhd_open_existing_raw_img(const char* utf8_raw_path, MVHDGeom* geom, int* err); + +static FILE* mvhd_open_existing_raw_img(const char* utf8_raw_path, MVHDGeom* geom, int* err) { + FILE *raw_img = mvhd_fopen(utf8_raw_path, "rb", err); + if (raw_img == NULL) { + *err = MVHD_ERR_FILE; + return NULL; + } + if (geom == NULL) { + *err = MVHD_ERR_INVALID_GEOM; + return NULL; + } + fseeko64(raw_img, 0, SEEK_END); + uint64_t size_bytes = (uint64_t)ftello64(raw_img); + MVHDGeom new_geom = mvhd_calculate_geometry(size_bytes); + if (mvhd_calc_size_bytes(&new_geom) != size_bytes) { + *err = MVHD_ERR_CONV_SIZE; + return NULL; + } + geom->cyl = new_geom.cyl; + geom->heads = new_geom.heads; + geom->spt = new_geom.spt; + fseeko64(raw_img, 0, SEEK_SET); + return raw_img; +} + +MVHDMeta* mvhd_convert_to_vhd_fixed(const char* utf8_raw_path, const char* utf8_vhd_path, int* err) { + MVHDGeom geom; + FILE *raw_img = mvhd_open_existing_raw_img(utf8_raw_path, &geom, err); + if (raw_img == NULL) { + return NULL; + } + int pos; + MVHDMeta *vhdm = mvhd_create_fixed_raw(utf8_vhd_path, raw_img, &geom, (volatile int*)&pos, err); + if (vhdm == NULL) { + return NULL; + } + return vhdm; +} +MVHDMeta* mvhd_convert_to_vhd_sparse(const char* utf8_raw_path, const char* utf8_vhd_path, int* err) { + MVHDGeom geom; + MVHDMeta *vhdm = NULL; + FILE *raw_img = mvhd_open_existing_raw_img(utf8_raw_path, &geom, err); + if (raw_img == NULL) { + return NULL; + } + vhdm = mvhd_create_sparse(utf8_vhd_path, geom, err); + if (vhdm == NULL) { + goto end; + } + uint8_t buff[4096] = {0}; // 8 sectors + uint8_t empty_buff[4096] = {0}; + int total_sectors = mvhd_calc_size_sectors(&geom); + int copy_sect = 0; + for (int i = 0; i < total_sectors; i += 8) { + copy_sect = 8; + if ((i + 8) >= total_sectors) { + copy_sect = total_sectors - i - 1; + memset(buff, 0, sizeof buff); + } + fread(buff, MVHD_SECTOR_SIZE, copy_sect, raw_img); + /* Only write data if there's data to write, to take advantage of the sparse VHD format */ + if (memcmp(buff, empty_buff, sizeof buff) != 0) { + mvhd_write_sectors(vhdm, i, copy_sect, buff); + } + } +end: + fclose(raw_img); + return vhdm; +} +FILE* mvhd_convert_to_raw(const char* utf8_vhd_path, const char* utf8_raw_path, int *err) { + FILE *raw_img = mvhd_fopen(utf8_raw_path, "wb", err); + if (raw_img == NULL) { + return NULL; + } + MVHDMeta *vhdm = mvhd_open(utf8_vhd_path, true, err); + if (vhdm == NULL) { + fclose(raw_img); + return NULL; + } + uint8_t buff[4096] = {0}; // 8 sectors + int total_sectors = mvhd_calc_size_sectors((MVHDGeom*)&vhdm->footer.geom); + int copy_sect = 0; + for (int i = 0; i < total_sectors; i += 8) { + copy_sect = 8; + if ((i + 8) >= total_sectors) { + copy_sect = total_sectors - i - 1; + } + mvhd_read_sectors(vhdm, i, copy_sect, buff); + fwrite(buff, MVHD_SECTOR_SIZE, copy_sect, raw_img); + } + mvhd_close(vhdm); + fseeko64(raw_img, 0, SEEK_SET); + return raw_img; +} \ No newline at end of file diff --git a/src/minivhd_create.c b/src/minivhd_create.c index 73ae16d..d9ba1c2 100644 --- a/src/minivhd_create.c +++ b/src/minivhd_create.c @@ -9,6 +9,7 @@ #include "minivhd_util.h" #include "minivhd_struct_rw.h" #include "minivhd_io.h" +#include "minivhd_create.h" #include "minivhd.h" static void mvhd_gen_footer(MVHDFooter* footer, MVHDGeom* geom, MVHDType type, uint64_t sparse_header_off); @@ -178,14 +179,30 @@ end: } MVHDMeta* mvhd_create_fixed(const char* path, MVHDGeom geom, volatile int* pos, int* err) { - uint8_t zero_data[MVHD_SECTOR_SIZE] = {0}; + return mvhd_create_fixed_raw(path, NULL, &geom, pos, err); +} + +/** + * \brief internal function that implements public mvhd_create_fixed() functionality + * + * Contains one more parameter than the public function, to allow using an existing + * raw disk image as the data source for the new fixed VHD. + * + * \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, volatile int* pos, int* err) { + uint8_t img_data[MVHD_SECTOR_SIZE] = {0}; uint8_t footer_buff[MVHD_FOOTER_SIZE] = {0}; MVHDMeta* vhdm = calloc(1, sizeof *vhdm); if (vhdm == NULL) { *err = MVHD_ERR_MEM; goto end; } - if (geom.cyl == 0 || geom.heads == 0 || geom.spt == 0) { + if (raw_img == NULL && geom == NULL) { + *err = MVHD_ERR_INVALID_GEOM; + goto cleanup_vhdm; + } + if (geom != NULL && (geom->cyl == 0 || geom->heads == 0 || geom->spt == 0)) { *err = MVHD_ERR_INVALID_GEOM; goto cleanup_vhdm; } @@ -194,13 +211,34 @@ MVHDMeta* mvhd_create_fixed(const char* path, MVHDGeom geom, volatile int* pos, goto cleanup_vhdm; } fseeko64(f, 0, SEEK_SET); - mvhd_gen_footer(&vhdm->footer, &geom, MVHD_TYPE_FIXED, 0); - uint32_t size_sectors = mvhd_calc_size_sectors(&geom); - for (uint32_t s = 0; s < size_sectors; s++) { - if (pos != NULL) { - *pos = (int)s; + uint32_t size_sectors, s; + if (raw_img != NULL) { + fseeko64(raw_img, 0, SEEK_END); + uint64_t raw_size = (uint64_t)ftello64(raw_img); + MVHDGeom raw_geom = mvhd_calculate_geometry(raw_size); + if (mvhd_calc_size_bytes(&raw_geom) != raw_size) { + *err = MVHD_ERR_CONV_SIZE; + goto cleanup_vhdm; + } + mvhd_gen_footer(&vhdm->footer, geom, MVHD_TYPE_FIXED, 0); + size_sectors = mvhd_calc_size_sectors(geom); + fseeko64(raw_img, 0, SEEK_SET); + for (s = 0; s < size_sectors; s++) { + if (pos != NULL) { + *pos = (int)s; + } + fread(img_data, sizeof img_data, 1, raw_img); + fwrite(img_data, sizeof img_data, 1, f); + } + } else { + mvhd_gen_footer(&vhdm->footer, geom, MVHD_TYPE_FIXED, 0); + size_sectors = mvhd_calc_size_sectors(geom); + for (s = 0; s < size_sectors; s++) { + if (pos != NULL) { + *pos = (int)s; + } + fwrite(img_data, sizeof img_data, 1, f); } - fwrite(zero_data, sizeof zero_data, 1, f); } mvhd_footer_to_buffer(&vhdm->footer, footer_buff); fwrite(footer_buff, sizeof footer_buff, 1, f); diff --git a/src/minivhd_create.h b/src/minivhd_create.h new file mode 100644 index 0000000..69efb6c --- /dev/null +++ b/src/minivhd_create.h @@ -0,0 +1,8 @@ +#ifndef MINIVHD_CREATE_H +#define MINIVHD_CREATE_H +#include +#include "minivhd.h" + +MVHDMeta* mvhd_create_fixed_raw(const char* path, FILE* raw_img, MVHDGeom* geom, volatile int* pos, int* err); + +#endif \ No newline at end of file diff --git a/src/minivhd_util.c b/src/minivhd_util.c index dc0c913..c07a341 100644 --- a/src/minivhd_util.c +++ b/src/minivhd_util.c @@ -170,6 +170,8 @@ const char* mvhd_strerr(MVHDError err) { return "invalid geometry detected"; case MVHD_ERR_INVALID_PARAMS: return "invalid parameters passed to function"; + case MVHD_ERR_CONV_SIZE: + return "error converting image. Size mismatch detechted"; default: return "unknown error"; }