From a6eb42ecded1b66500ae8562b266655f6889cee5 Mon Sep 17 00:00:00 2001 From: shermp <14854761+shermp@users.noreply.github.com> Date: Thu, 20 Jun 2019 22:00:42 +1200 Subject: [PATCH] Add partial VHD creation. Work not yet finished --- src/minivhd.h | 8 +- src/minivhd_create.c | 194 +++++++++++++++++++++++++++++++++++++++++ src/minivhd_create.h | 4 + src/minivhd_internal.h | 3 +- src/minivhd_io.c | 3 +- src/minivhd_io.h | 1 + src/minivhd_manage.c | 40 +-------- src/minivhd_util.c | 44 ++++++++++ src/minivhd_util.h | 5 ++ 9 files changed, 258 insertions(+), 44 deletions(-) create mode 100644 src/minivhd_create.c create mode 100644 src/minivhd_create.h diff --git a/src/minivhd.h b/src/minivhd.h index 0c1f134..f7f6cec 100644 --- a/src/minivhd.h +++ b/src/minivhd.h @@ -19,7 +19,9 @@ typedef enum MVHDError { MVHD_ERR_PATH_REL, MVHD_ERR_PATH_LEN, MVHD_ERR_PAR_NOT_FOUND, - MVHD_ERR_INVALID_PAR_UUID + MVHD_ERR_INVALID_PAR_UUID, + MVHD_ERR_INVALID_GEOM, + MVHD_ERR_INVALID_PARAMS } MVHDError; typedef struct MVHDGeom { @@ -32,9 +34,9 @@ typedef struct MVHDMeta MVHDMeta; bool mvhd_file_is_vhd(FILE* f); MVHDMeta* mvhd_open(const char* path, bool readonly, int* err); -MVHDMeta* mvhd_create_fixed(const char* path, MVHDGeom geom, int* err); +MVHDMeta* mvhd_create_fixed(const char* path, MVHDGeom geom, volatile int* pos, int* err); MVHDMeta* mvhd_create_sparse(const char* path, MVHDGeom geom, int* err); -MVHDMeta* mvhd_create_diff(const char* path, const char* par_path, MVHDGeom geom, int* err); +MVHDMeta* mvhd_create_diff(const char* path, const char* par_path, int* err); void mvhd_close(MVHDMeta* vhdm); MVHDGeom mvhd_calculate_geometry(int size_mb, int* new_size); int mvhd_read_sectors(MVHDMeta* vhdm, int offset, int num_sectors, void* out_buff); diff --git a/src/minivhd_create.c b/src/minivhd_create.c new file mode 100644 index 0000000..52d5b2e --- /dev/null +++ b/src/minivhd_create.c @@ -0,0 +1,194 @@ +#include +#include +#include +#include +#include "cwalk.h" +#include "libxml2_encoding.h" +#include "minivhd_internal.h" +#include "minivhd_util.h" +#include "minivhd_struct_rw.h" +#include "minivhd_io.h" +#include "minivhd.h" + +static void mvhd_gen_footer(MVHDFooter* footer, MVHDGeom* geom, MVHDType type, uint64_t sparse_header_off) { + strncpy(footer->cookie, "conectix", sizeof footer->cookie); + footer->features = 0x00000002; + footer->fi_fmt_vers = 0x00010000; + footer->data_offset = (type == MVHD_TYPE_DIFF || type == MVHD_TYPE_DYNAMIC) ? sparse_header_off : 0xffffffffffffffff; + footer->timestamp = vhd_calc_timestamp(); + strncpy(footer->cr_app, "mvhd", sizeof footer->cr_app); + footer->cr_vers = 0x000e0000; + strncpy(footer->cr_host_os, "Wi2k", sizeof footer->cr_host_os); + footer->orig_sz = footer->curr_sz = mvhd_calc_size_bytes(geom); + footer->geom.cyl = geom->cyl; + footer->geom.heads = geom->heads; + footer->geom.spt = geom->spt; + footer->disk_type = type; + mvhd_generate_uuid(footer->uuid); + footer->checksum = mvhd_gen_footer_checksum(footer); +} + +static void mvhd_gen_sparse_header(MVHDSparseHeader* header, uint32_t num_blks, uint64_t bat_offset) { + strncpy(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->checksum = mvhd_gen_sparse_checksum(header); +} + +static int mvhd_gen_par_loc(MVHDSparseHeader* header, const char* child_path, const char* par_path, uint64_t start_offset, MVHDError* err) { + /* Get our paths to store in the differencing VHD. We want both the absolute path to the parent, + as well as the relative path from the child VHD */ + char* par_filename; + size_t par_fn_len; + char rel_path[MVHD_MAX_PATH_BYTES] = {0}; + char child_dir[MVHD_MAX_PATH_BYTES] = {0}; + size_t child_dir_len; + if (strlen(par_path) < sizeof child_dir) { + strcpy(child_dir, child_path); + } else { + *err = MVHD_ERR_PATH_LEN; + return -1; + } + cwk_path_get_basename(par_path, &par_filename, &par_fn_len); + cwk_path_get_dirname(child_dir, &child_dir_len); + child_dir[child_dir_len] = '\0'; + size_t rel_len = cwk_path_get_relative(child_dir, par_path, rel_path, sizeof rel_path); + if (rel_len > sizeof rel_path) { + *err = MVHD_ERR_PATH_LEN; + return -1; + } + /* We have our paths, store them in the sparse header. */ + int outlen = sizeof header->par_utf16_name; + int utf_ret; + utf_ret = UTF8ToUTF16BE((unsigned char*)header->par_utf16_name, &outlen, (const unsigned char*)par_filename, (int*)&par_fn_len); + if (utf_ret < 0) { + return utf_ret; + } + uint32_t plat_code = MVHD_DIF_LOC_W2KU; + uint64_t dat_offset = start_offset; + for (int i = 0; i < 2; i++) { + header->par_loc_entry[i].plat_code = plat_code; + header->par_loc_entry[i].plat_data_offset = dat_offset; + } +} + +MVHDMeta* mvhd_create_fixed(const char* path, MVHDGeom geom, volatile int* pos, int* err) { + uint8_t zero_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) { + *err = MVHD_ERR_INVALID_GEOM; + goto cleanup_vhdm; + } + FILE* f = mvhd_fopen(path, "wb+", err); + if (f == NULL) { + 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; + } + fwrite(zero_data, sizeof zero_data, 1, f); + } + mvhd_footer_to_buffer(&vhdm->footer, footer_buff); + fwrite(footer_buff, sizeof footer_buff, 1, f); + fclose(f); + f = NULL; + free(vhdm); + vhdm = mvhd_open(path, false, err); + goto end; + +cleanup_vhdm: + free(vhdm); + vhdm = NULL; +end: + return vhdm; +} + +static MVHDMeta* mvhd_create_sparse_diff(const char* path, const char* par_path, MVHDGeom* geom, 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]; + MVHDGeom par_geom = {}; + memset(bat_sect, 0xffffffff, sizeof bat_sect); + MVHDMeta* par_vhdm = NULL; + if (par_path != NULL) { + par_vhdm = mvhd_open(par_path, true, err); + if (par_vhdm == NULL) { + goto end; + } + } + MVHDMeta* vhdm = calloc(1, sizeof *vhdm); + if (vhdm == NULL) { + *err = MVHD_ERR_MEM; + goto end; + } + if (geom != NULL && (geom->cyl == 0 || geom->heads == 0 || geom->spt == 0)) { + *err = MVHD_ERR_INVALID_GEOM; + goto cleanup_vhdm; + } else if (par_vhdm != NULL) { + par_geom.cyl = par_vhdm->footer.geom.cyl; + par_geom.heads = par_vhdm->footer.geom.heads; + par_geom.spt = par_vhdm->footer.geom.spt; + geom = &par_geom; + } else { + *err = MVHD_ERR_INVALID_PARAMS; + goto cleanup_vhdm; + } + FILE* f = mvhd_fopen(path, "wb+", err); + if (f == NULL) { + goto cleanup_vhdm; + } + fseeko64(f, 0, SEEK_SET); + if (par_path == NULL) { + mvhd_gen_footer(&vhdm->footer, geom, MVHD_TYPE_DYNAMIC, MVHD_FOOTER_SIZE); + } else { + mvhd_gen_footer(&vhdm->footer, geom, MVHD_TYPE_DIFF, MVHD_FOOTER_SIZE); + } + mvhd_footer_to_buffer(&vhdm->footer, footer_buff); + fwrite(footer_buff, sizeof footer_buff, 1, f); + uint64_t bat_offset = MVHD_FOOTER_SIZE + MVHD_SPARSE_SIZE; + uint32_t num_blks = mvhd_calc_size_sectors(geom) / MVHD_SECT_PER_BLOCK; + if (mvhd_calc_size_sectors(geom) % MVHD_SECT_PER_BLOCK != 0) { + num_blks += 1; + } + uint32_t num_bat_sect = num_blks / MVHD_BAT_ENT_PER_SECT; + if (num_blks % MVHD_BAT_ENT_PER_SECT != 0) { + num_bat_sect += 1; + } + mvhd_gen_sparse_header(&vhdm->sparse, num_blks, bat_offset); + mvhd_header_to_buffer(&vhdm->sparse, sparse_buff); + fwrite(sparse_buff, sizeof sparse_buff, 1, f); + for (uint32_t i = 0; i < num_bat_sect; i++) { + fwrite(bat_sect, sizeof bat_sect, 1, f); + } + mvhd_write_empty_sectors(f, 5); + fwrite(footer_buff, sizeof footer_buff, 1, f); + fclose(f); + f = NULL; + free(vhdm); + vhdm = mvhd_open(path, false, err); + goto end; + +cleanup_vhdm: + free(vhdm); + vhdm = NULL; +end: + return vhdm; +} +MVHDMeta* mvhd_create_sparse(const char* path, MVHDGeom geom, int* err) { + return mvhd_create_sparse_diff(path, NULL, &geom, err); +} +MVHDMeta* mvhd_create_diff(const char* path, const char* par_path, int* err) { + return mvhd_create_sparse_diff(path, par_path, NULL, err); +} \ No newline at end of file diff --git a/src/minivhd_create.h b/src/minivhd_create.h new file mode 100644 index 0000000..0231e19 --- /dev/null +++ b/src/minivhd_create.h @@ -0,0 +1,4 @@ +#ifndef MINIVHD_CREATE_H +#define MINIVHD_CREATE_H + +#endif \ No newline at end of file diff --git a/src/minivhd_internal.h b/src/minivhd_internal.h index 91c7184..a0e795d 100644 --- a/src/minivhd_internal.h +++ b/src/minivhd_internal.h @@ -8,7 +8,8 @@ #define MVHD_SPARSE_SIZE 1024 #define MVHD_SECTOR_SIZE 512 -#define MVHD_MAX_BLOCK_SIZE 0x00200000 +#define MVHD_SECT_PER_BLOCK 4096 /* 2MB block size */ +#define MVHD_BAT_ENT_PER_SECT 128 #define MVHD_SPARSE_BLK 0xffffffff /* For simplicity, we don't handle paths longer than this diff --git a/src/minivhd_io.c b/src/minivhd_io.c index 8e8ff3c..382d885 100644 --- a/src/minivhd_io.c +++ b/src/minivhd_io.c @@ -17,7 +17,6 @@ #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 void mvhd_write_empty_sectors(FILE* f, int sector_count); 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); @@ -51,7 +50,7 @@ static inline void mvhd_check_sectors(int offset, int num_sectors, int total_sec * \param [in] f File to write sectors to * \param [in] sector_count The number of sectors to write */ -static void mvhd_write_empty_sectors(FILE* f, int sector_count) { +void mvhd_write_empty_sectors(FILE* f, int sector_count) { uint8_t zero_bytes[MVHD_SECTOR_SIZE] = {0}; for (int i = 0; i < sector_count; i++) { fwrite(zero_bytes, sizeof zero_bytes, 1, f); diff --git a/src/minivhd_io.h b/src/minivhd_io.h index 6902f17..5febcae 100644 --- a/src/minivhd_io.h +++ b/src/minivhd_io.h @@ -2,6 +2,7 @@ #define MINIVHD_IO_H #include "minivhd.h" +void mvhd_write_empty_sectors(FILE* f, int sector_count); int mvhd_fixed_read(MVHDMeta* vhdm, int offset, int num_sectors, void* out_buff); int mvhd_sparse_read(MVHDMeta* vhdm, int offset, int num_sectors, void* out_buff); int mvhd_diff_read(MVHDMeta* vhdm, int offset, int num_sectors, void* out_buff); diff --git a/src/minivhd_manage.c b/src/minivhd_manage.c index 9900e44..6708f2d 100644 --- a/src/minivhd_manage.c +++ b/src/minivhd_manage.c @@ -30,8 +30,6 @@ struct MVHDPaths { static void mvhd_read_footer(MVHDMeta* vhdm); static void mvhd_read_sparse_header(MVHDMeta* vhdm); -static uint32_t mvhd_gen_footer_checksum(MVHDMeta* vhdm); -static uint32_t mvhd_gen_sparse_checksum(MVHDMeta* vhdm); static bool mvhd_footer_checksum_valid(MVHDMeta* vhdm); static bool mvhd_sparse_checksum_valid(MVHDMeta* vhdm); static int mvhd_read_bat(MVHDMeta *vhdm, MVHDError* err); @@ -62,40 +60,6 @@ static void mvhd_read_sparse_header(MVHDMeta* vhdm) { mvhd_buffer_to_header(&vhdm->sparse, buffer); } -/** - * \brief Generate VHD footer checksum - * - * \param [in] vhdm MiniVHD data structure - */ -static uint32_t mvhd_gen_footer_checksum(MVHDMeta* vhdm) { - uint32_t new_chk = 0; - uint32_t orig_chk = vhdm->footer.checksum; - vhdm->footer.checksum = 0; - uint8_t* footer_bytes = (uint8_t*)&vhdm->footer; - for (size_t i = 0; i < sizeof vhdm->footer; i++) { - new_chk += footer_bytes[i]; - } - vhdm->footer.checksum = orig_chk; - return ~new_chk; -} - -/** - * \brief Generate VHD sparse header checksum - * - * \param [in] vhdm MiniVHD data structure - */ -static uint32_t mvhd_gen_sparse_checksum(MVHDMeta* vhdm) { - uint32_t new_chk = 0; - uint32_t orig_chk = vhdm->sparse.checksum; - vhdm->sparse.checksum = 0; - uint8_t* sparse_bytes = (uint8_t*)&vhdm->sparse; - for (size_t i = 0; i < sizeof vhdm->sparse; i++) { - new_chk += sparse_bytes[i]; - } - vhdm->sparse.checksum = orig_chk; - return ~new_chk; -} - /** * \brief Validate VHD footer checksum * @@ -104,7 +68,7 @@ static uint32_t mvhd_gen_sparse_checksum(MVHDMeta* vhdm) { * \param [in] vhdm MiniVHD data structure */ static bool mvhd_footer_checksum_valid(MVHDMeta* vhdm) { - return vhdm->footer.checksum == mvhd_gen_footer_checksum(vhdm); + return vhdm->footer.checksum == mvhd_gen_footer_checksum(&vhdm->footer); } /** @@ -115,7 +79,7 @@ static bool mvhd_footer_checksum_valid(MVHDMeta* vhdm) { * \param [in] vhdm MiniVHD data structure */ static bool mvhd_sparse_checksum_valid(MVHDMeta* vhdm) { - return vhdm->sparse.checksum == mvhd_gen_sparse_checksum(vhdm); + return vhdm->sparse.checksum == mvhd_gen_sparse_checksum(&vhdm->sparse); } /** diff --git a/src/minivhd_util.c b/src/minivhd_util.c index c4b9acb..4986366 100644 --- a/src/minivhd_util.c +++ b/src/minivhd_util.c @@ -142,4 +142,48 @@ void mvhd_set_encoding_err(int encoding_retval, int* err) { } else if (encoding_retval == -2) { *err = MVHD_ERR_UTF_TRANSCODING_FAILED; } +} + +uint64_t mvhd_calc_size_bytes(MVHDGeom *geom) { + uint64_t img_size = (uint64_t)geom->cyl * (uint64_t)geom->heads * (uint64_t)geom->spt * (uint64_t)MVHD_SECTOR_SIZE; + return img_size; +} + +uint32_t mvhd_calc_size_sectors(MVHDGeom *geom) { + uint32_t sector_size = (uint32_t)geom->cyl * (uint32_t)geom->heads * (uint32_t)geom->spt; + return sector_size; +} + +/** + * \brief Generate VHD footer checksum + * + * \param [in] vhdm MiniVHD data structure + */ +uint32_t mvhd_gen_footer_checksum(MVHDFooter* footer) { + uint32_t new_chk = 0; + uint32_t orig_chk = footer->checksum; + footer->checksum = 0; + uint8_t* footer_bytes = (uint8_t*)footer; + for (size_t i = 0; i < sizeof *footer; i++) { + new_chk += footer_bytes[i]; + } + footer->checksum = orig_chk; + return ~new_chk; +} + +/** + * \brief Generate VHD sparse header checksum + * + * \param [in] vhdm MiniVHD data structure + */ +uint32_t mvhd_gen_sparse_checksum(MVHDSparseHeader* header) { + uint32_t new_chk = 0; + uint32_t orig_chk = header->checksum; + header->checksum = 0; + uint8_t* sparse_bytes = (uint8_t*)header; + for (size_t i = 0; i < sizeof *header; i++) { + new_chk += sparse_bytes[i]; + } + header->checksum = orig_chk; + return ~new_chk; } \ No newline at end of file diff --git a/src/minivhd_util.h b/src/minivhd_util.h index d3ce312..70ac7bd 100644 --- a/src/minivhd_util.h +++ b/src/minivhd_util.h @@ -3,6 +3,7 @@ #include #include +#include "minivhd_internal.h" #include "minivhd.h" #define MVHD_START_TS 946684800 @@ -12,4 +13,8 @@ uint32_t vhd_calc_timestamp(void); time_t vhd_get_created_time(MVHDMeta *vhdm); FILE* mvhd_fopen(const char* path, const char* mode, int* err); void mvhd_set_encoding_err(int encoding_retval, int* err); +uint64_t mvhd_calc_size_bytes(MVHDGeom *geom); +uint32_t mvhd_calc_size_sectors(MVHDGeom *geom); +uint32_t mvhd_gen_footer_checksum(MVHDFooter* footer); +uint32_t mvhd_gen_sparse_checksum(MVHDSparseHeader* header); #endif \ No newline at end of file