Add partial VHD creation. Work not yet finished

This commit is contained in:
shermp
2019-06-20 22:00:42 +12:00
parent 917344cf17
commit a6eb42ecde
9 changed files with 258 additions and 44 deletions

View File

@@ -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);

194
src/minivhd_create.c Normal file
View File

@@ -0,0 +1,194 @@
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#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);
}

4
src/minivhd_create.h Normal file
View File

@@ -0,0 +1,4 @@
#ifndef MINIVHD_CREATE_H
#define MINIVHD_CREATE_H
#endif

View File

@@ -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

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);
}
/**

View File

@@ -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;
}

View File

@@ -3,6 +3,7 @@
#include <stdint.h>
#include <stdio.h>
#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