mirror of
https://github.com/VARCem/MiniVHD.git
synced 2026-09-23 23:35:17 +00:00
Add partial VHD creation. Work not yet finished
This commit is contained in:
@@ -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
194
src/minivhd_create.c
Normal 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
4
src/minivhd_create.h
Normal file
@@ -0,0 +1,4 @@
|
||||
#ifndef MINIVHD_CREATE_H
|
||||
#define MINIVHD_CREATE_H
|
||||
|
||||
#endif
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user