Update MiniVHD to 1.0.3

This commit is contained in:
Jasmine Iwanek
2023-04-11 19:00:14 -04:00
parent 36f7f91bec
commit 8dba80e6a6
24 changed files with 2046 additions and 1005 deletions

View File

@@ -13,6 +13,5 @@
# Copyright 2020,2021 David Hrdlička.
#
add_library(minivhd STATIC cwalk.c libxml2_encoding.c minivhd_convert.c
minivhd_create.c minivhd_io.c minivhd_manage.c minivhd_struct_rw.c
minivhd_util.c)
add_library(minivhd STATIC cwalk.c xml2_encoding.c convert.c
create.c minivhd_io.c manage.c struct_rw.c minivhd_util.c)

View File

@@ -1,7 +1,7 @@
# Credits
MiniVHD Copyright (c) 2019 Sherman Perry
MiniVHD Copyright 2019-2021 Sherman Perry.
MiniVHD was made possible with the help of the following projects
MiniVHD was made possible with the help of the following projects:
### libxml2
**Project Home:** http://www.xmlsoft.org/
@@ -10,3 +10,8 @@ MiniVHD was made possible with the help of the following projects
### cwalk
**Project Home:** https://likle.github.io/cwalk/
**Licence:** MIT (https://github.com/likle/cwalk/blob/master/LICENSE.md)
### VARCem
The MiniVHD was rewritten into a standalone library (both shared as well
as static) by Fred N. van Kempen for use with the VARCem PC Systems
emulator - see https://www.varcem.com/ for more info.

View File

@@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
SOFTWARE.

View File

@@ -1,28 +1,65 @@
/*
* MiniVHD Minimalist VHD implementation in C.
*
* This file is part of the MiniVHD Project.
*
* Version: @(#)convert.c 1.0.2 2021/04/16
*
* Authors: Sherman Perry, <shermperry@gmail.com>
* Fred N. van Kempen, <waltje@varcem.com>
*
* Copyright 2019-2021 Sherman Perry.
* Copyright 2021 Fred N. van Kempen.
*
* MIT License
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documenta-
* tion files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall
* be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF O R IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef _FILE_OFFSET_BITS
#define _FILE_OFFSET_BITS 64
# define _FILE_OFFSET_BITS 64
#endif
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include "minivhd_create.h"
#include "minivhd_internal.h"
#include "minivhd_util.h"
#include "minivhd.h"
#include "internal.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) {
static FILE*
open_existing_raw_img(const char* utf8_raw_path, MVHDGeom* geom, int* err)
{
if (geom == NULL) {
*err = MVHD_ERR_INVALID_GEOM;
return NULL;
}
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;
}
mvhd_fseeko64(raw_img, 0, SEEK_END);
uint64_t size_bytes = (uint64_t)mvhd_ftello64(raw_img);
MVHDGeom new_geom = mvhd_calculate_geometry(size_bytes);
@@ -34,37 +71,52 @@ static FILE* mvhd_open_existing_raw_img(const char* utf8_raw_path, MVHDGeom* geo
geom->heads = new_geom.heads;
geom->spt = new_geom.spt;
mvhd_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) {
MVHDAPI 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);
FILE *raw_img = open_existing_raw_img(utf8_raw_path, &geom, err);
if (raw_img == NULL) {
return 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;
}
return vhdm;
}
MVHDMeta* mvhd_convert_to_vhd_sparse(const char* utf8_raw_path, const char* utf8_vhd_path, int* err) {
MVHDAPI 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);
FILE *raw_img = 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) {
@@ -72,6 +124,7 @@ MVHDMeta* mvhd_convert_to_vhd_sparse(const char* utf8_raw_path, const char* utf8
memset(buff, 0, sizeof buff);
}
(void) !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);
@@ -79,18 +132,25 @@ MVHDMeta* mvhd_convert_to_vhd_sparse(const char* utf8_raw_path, const char* utf8
}
end:
fclose(raw_img);
return vhdm;
}
FILE* mvhd_convert_to_raw(const char* utf8_vhd_path, const char* utf8_raw_path, int *err) {
MVHDAPI 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;
@@ -104,5 +164,6 @@ FILE* mvhd_convert_to_raw(const char* utf8_vhd_path, const char* utf8_raw_path,
}
mvhd_close(vhdm);
mvhd_fseeko64(raw_img, 0, SEEK_SET);
return raw_img;
}

View File

@@ -1,30 +1,59 @@
/*
* MiniVHD Minimalist VHD implementation in C.
*
* This file is part of the MiniVHD Project.
*
* Version: @(#)create.c 1.0.3 2021/04/16
*
* Authors: Sherman Perry, <shermperry@gmail.com>
* Fred N. van Kempen, <waltje@varcem.com>
*
* Copyright 2019-2021 Sherman Perry.
* Copyright 2021 Fred N. van Kempen.
*
* MIT License
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documenta-
* tion files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall
* be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF O R IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef _FILE_OFFSET_BITS
#define _FILE_OFFSET_BITS 64
# define _FILE_OFFSET_BITS 64
#endif
#include <assert.h>
#include <stdbool.h>
#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_create.h"
#include "minivhd.h"
#include "internal.h"
#include "cwalk.h"
#include "xml2_encoding.h"
static const char MVHD_CONECTIX_COOKIE[] = "conectix";
static const char MVHD_CREATOR[] = "mVHD";
static const char MVHD_CREATOR_HOST_OS[] = "Wi2k";
static const char MVHD_CXSPARSE_COOKIE[] = "cxsparse";
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,
uint64_t start_offset,
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, uint64_t size_in_bytes, MVHDGeom* geom, uint32_t block_size_in_sectors, int* err);
/**
* \brief Populate a VHD footer
@@ -35,24 +64,29 @@ static MVHDMeta* mvhd_create_sparse_diff(const char* path, const char* par_path,
* \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, uint64_t size_in_bytes, MVHDGeom* geom, MVHDType type, uint64_t sparse_header_off) {
memcpy(footer->cookie, "conectix", sizeof footer->cookie);
static void
gen_footer(MVHDFooter* footer, uint64_t size_in_bytes, MVHDGeom* geom, MVHDType type, uint64_t sparse_header_off)
{
memcpy(footer->cookie, MVHD_CONECTIX_COOKIE, 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();
memcpy(footer->cr_app, "mvhd", sizeof footer->cr_app);
memcpy(footer->cr_app, MVHD_CREATOR, sizeof footer->cr_app);
footer->cr_vers = 0x000e0000;
memcpy(footer->cr_host_os, "Wi2k", sizeof footer->cr_host_os);
memcpy(footer->cr_host_os, MVHD_CREATOR_HOST_OS, sizeof footer->cr_host_os);
footer->orig_sz = footer->curr_sz = size_in_bytes;
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);
}
/**
* \brief Populate a VHD sparse header
*
@@ -61,8 +95,10 @@ static void mvhd_gen_footer(MVHDFooter* footer, uint64_t size_in_bytes, MVHDGeom
* \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, uint32_t block_size_in_sectors) {
memcpy(header->cookie, "cxsparse", sizeof header->cookie);
static void
gen_sparse_header(MVHDSparseHeader* header, uint32_t num_blks, uint64_t bat_offset, uint32_t block_size_in_sectors)
{
memcpy(header->cookie, MVHD_CXSPARSE_COOKIE, sizeof header->cookie);
header->data_offset = 0xffffffffffffffff;
header->bat_offset = bat_offset;
header->head_vers = 0x00010000;
@@ -71,6 +107,7 @@ static void mvhd_gen_sparse_header(MVHDSparseHeader* header, uint32_t num_blks,
header->checksum = mvhd_gen_sparse_checksum(header);
}
/**
* \brief Generate parent locators for differencing VHD images
*
@@ -85,13 +122,12 @@ static void mvhd_gen_sparse_header(MVHDSparseHeader* header, uint32_t num_blks,
* \retval 0 if success
* \retval < 0 if an error occurrs. Check value of *err for actual error
*/
static int mvhd_gen_par_loc(MVHDSparseHeader* header,
const char* child_path,
const char* par_path,
uint64_t start_offset,
mvhd_utf16* w2ku_path_buff,
mvhd_utf16* w2ru_path_buff,
MVHDError* err) {
static int
gen_par_loc(MVHDSparseHeader* header, const char* child_path,
const char* par_path, uint64_t start_offset,
mvhd_utf16* w2ku_path_buff, mvhd_utf16* w2ru_path_buff,
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 */
int rv = 0;
@@ -100,6 +136,7 @@ static int mvhd_gen_par_loc(MVHDSparseHeader* header,
char rel_path[MVHD_MAX_PATH_BYTES] = {0};
char child_dir[MVHD_MAX_PATH_BYTES] = {0};
size_t child_dir_len;
if (strlen(child_path) < sizeof child_dir) {
strcpy(child_dir, child_path);
} else {
@@ -107,6 +144,7 @@ static int mvhd_gen_par_loc(MVHDSparseHeader* header,
rv = -1;
goto end;
}
cwk_path_get_basename(par_path, (const char**)&par_filename, &par_fn_len);
cwk_path_get_dirname(child_dir, &child_dir_len);
child_dir[child_dir_len] = '\0';
@@ -116,6 +154,7 @@ static int mvhd_gen_par_loc(MVHDSparseHeader* header,
rv = -1;
goto end;
}
/* We have our paths, now store the parent filename directly in the sparse header. */
int outlen = sizeof header->par_utf16_name;
int utf_ret;
@@ -144,6 +183,7 @@ static int mvhd_gen_par_loc(MVHDSparseHeader* header,
goto end;
}
int w2ru_len = utf_ret;
/**
* Finally populate the parent locaters in the sparse header.
* This is the information needed to find the paths saved elsewhere
@@ -169,11 +209,16 @@ end:
return rv;
}
MVHDMeta* mvhd_create_fixed(const char* path, MVHDGeom geom, int* err, mvhd_progress_callback progress_callback) {
MVHDAPI MVHDMeta*
mvhd_create_fixed(const char* path, MVHDGeom geom, int* err, mvhd_progress_callback 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);
}
/**
* \brief internal function that implements public mvhd_create_fixed() functionality
*
@@ -182,27 +227,35 @@ 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, uint64_t size_in_bytes, 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};
if (geom == NULL || (geom->cyl == 0 || geom->heads == 0 || geom->spt == 0)) {
*err = MVHD_ERR_INVALID_GEOM;
return NULL;
}
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;
}
FILE* f = mvhd_fopen(path, "wb+", err);
if (f == NULL) {
goto cleanup_vhdm;
}
mvhd_fseeko64(f, 0, SEEK_SET);
uint32_t size_sectors = (uint32_t)(size_in_bytes / MVHD_SECTOR_SIZE);
uint32_t s;
if (progress_callback)
progress_callback(0, size_sectors);
if (raw_img != NULL) {
mvhd_fseeko64(raw_img, 0, SEEK_END);
uint64_t raw_size = (uint64_t)mvhd_ftello64(raw_img);
@@ -211,7 +264,7 @@ MVHDMeta* mvhd_create_fixed_raw(const char* path, FILE* raw_img, uint64_t size_i
*err = MVHD_ERR_CONV_SIZE;
goto cleanup_vhdm;
}
mvhd_gen_footer(&vhdm->footer, raw_size, geom, MVHD_TYPE_FIXED, 0);
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++) {
(void) !fread(img_data, sizeof img_data, 1, raw_img);
@@ -220,7 +273,7 @@ MVHDMeta* mvhd_create_fixed_raw(const char* path, FILE* raw_img, uint64_t size_i
progress_callback(s + 1, size_sectors);
}
} else {
mvhd_gen_footer(&vhdm->footer, size_in_bytes, geom, MVHD_TYPE_FIXED, 0);
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)
@@ -238,10 +291,12 @@ MVHDMeta* mvhd_create_fixed_raw(const char* path, FILE* raw_img, uint64_t size_i
cleanup_vhdm:
free(vhdm);
vhdm = NULL;
end:
return vhdm;
}
/**
* \brief Create sparse or differencing VHD image.
*
@@ -254,7 +309,9 @@ end:
*
* \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, uint64_t size_in_bytes, MVHDGeom* geom, uint32_t block_size_in_sectors, int* err) {
static MVHDMeta*
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];
@@ -265,6 +322,7 @@ static MVHDMeta* mvhd_create_sparse_diff(const char* path, const char* par_path,
mvhd_utf16* w2ku_path_buff = NULL;
mvhd_utf16* w2ru_path_buff = NULL;
uint32_t par_mod_timestamp = 0;
if (par_path != NULL) {
par_mod_timestamp = mvhd_file_mod_timestamp(par_path, err);
if (*err != 0) {
@@ -275,6 +333,7 @@ static MVHDMeta* mvhd_create_sparse_diff(const char* path, const char* par_path,
goto end;
}
}
vhdm = calloc(1, sizeof *vhdm);
if (vhdm == NULL) {
*err = MVHD_ERR_MEM;
@@ -297,15 +356,18 @@ static MVHDMeta* mvhd_create_sparse_diff(const char* path, const char* par_path,
goto cleanup_vhdm;
}
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, size_in_bytes, geom, MVHD_TYPE_DYNAMIC, MVHD_FOOTER_SIZE);
gen_footer(&vhdm->footer, size_in_bytes, geom, MVHD_TYPE_DYNAMIC, MVHD_FOOTER_SIZE);
} else {
mvhd_gen_footer(&vhdm->footer, size_in_bytes, geom, MVHD_TYPE_DIFF, MVHD_FOOTER_SIZE);
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 or 512KB) data blocks required to store the entire
* contents of the disk image, followed by the number of sectors the
@@ -347,43 +409,51 @@ static MVHDMeta* mvhd_create_sparse_diff(const char* path, const char* par_path,
}
memcpy(vhdm->sparse.par_uuid, par_vhdm->footer.uuid, sizeof vhdm->sparse.par_uuid);
par_loc_offset = bat_offset + ((uint64_t)num_bat_sect * MVHD_SECTOR_SIZE) + (5 * MVHD_SECTOR_SIZE);
if (mvhd_gen_par_loc(&vhdm->sparse, path, par_path, par_loc_offset, w2ku_path_buff, w2ru_path_buff, (MVHDError*)err) < 0) {
if (gen_par_loc(&vhdm->sparse, path, par_path, par_loc_offset, w2ku_path_buff, w2ru_path_buff, (MVHDError*)err) < 0) {
goto cleanup_vhdm;
}
vhdm->sparse.par_timestamp = par_mod_timestamp;
}
mvhd_gen_sparse_header(&vhdm->sparse, num_blks, bat_offset, block_size_in_sectors);
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 */
for (uint32_t i = 0; i < num_bat_sect; i++) {
for (uint32_t k = 0; k < num_bat_sect; k++) {
fwrite(bat_sect, sizeof bat_sect, 1, f);
}
mvhd_write_empty_sectors(f, 5);
/**
* If creating a differencing VHD, the paths to the parent image need to be written
* tp the file. Both absolute and relative paths are written
* */
if (par_vhdm != NULL) {
uint64_t curr_pos = (uint64_t)mvhd_ftello64(f);
/* Double check my sums... */
assert(curr_pos == par_loc_offset);
/* Fill the space required for location data with zero */
uint8_t empty_sect[MVHD_SECTOR_SIZE] = {0};
for (int i = 0; i < 2; i++) {
for (uint32_t j = 0; j < (vhdm->sparse.par_loc_entry[i].plat_data_space / MVHD_SECTOR_SIZE); j++) {
fwrite(empty_sect, sizeof empty_sect, 1, f);
}
}
/* Now write the location entries */
mvhd_fseeko64(f, vhdm->sparse.par_loc_entry[0].plat_data_offset, SEEK_SET);
fwrite(w2ku_path_buff, vhdm->sparse.par_loc_entry[0].plat_data_len, 1, f);
mvhd_fseeko64(f, vhdm->sparse.par_loc_entry[1].plat_data_offset, SEEK_SET);
fwrite(w2ru_path_buff, vhdm->sparse.par_loc_entry[1].plat_data_len, 1, f);
/* and reset the file position to continue */
mvhd_fseeko64(f, vhdm->sparse.par_loc_entry[1].plat_data_offset + vhdm->sparse.par_loc_entry[1].plat_data_space, SEEK_SET);
mvhd_write_empty_sectors(f, 5);
}
/* And finish with the footer */
fwrite(footer_buff, sizeof footer_buff, 1, f);
fclose(f);
@@ -395,91 +465,112 @@ static MVHDMeta* mvhd_create_sparse_diff(const char* path, const char* par_path,
cleanup_vhdm:
free(vhdm);
vhdm = NULL;
cleanup_par_vhdm:
if (par_vhdm != NULL) {
mvhd_close(par_vhdm);
}
end:
free(w2ku_path_buff);
free(w2ru_path_buff);
return vhdm;
}
MVHDMeta* mvhd_create_sparse(const char* path, MVHDGeom geom, int* err) {
MVHDAPI MVHDMeta*
mvhd_create_sparse(const char* path, MVHDGeom geom, int* 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);
return 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, 0, NULL, MVHD_BLOCK_LARGE, err);
MVHDAPI MVHDMeta*
mvhd_create_diff(const char* path, const char* par_path, int* err)
{
return create_sparse_diff(path, par_path, 0, NULL, MVHD_BLOCK_LARGE, err);
}
MVHDMeta* mvhd_create_ex(MVHDCreationOptions options, int* err) {
MVHDAPI 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;
}
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)
options.size_in_bytes = (uint64_t)geom_sector_size * MVHD_SECTOR_SIZE;
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 (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;
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;
}
break;
default:
*err = MVHD_ERR_TYPE;
return NULL;
}
if (options.path == NULL)
{
if (options.path == NULL) {
*err = MVHD_ERR_FILE;
return NULL;
}
if (options.type != MVHD_TYPE_FIXED)
{
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)
{
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);
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 create_sparse_diff(options.path, NULL, options.size_in_bytes, &(options.geometry), options.block_size_in_sectors, err);
case MVHD_TYPE_DIFF:
return create_sparse_diff(options.path, options.parent_path, 0, NULL, options.block_size_in_sectors, err);
}
return NULL; /* Make the compiler happy */
}
bool
mvhd_is_conectix_str(const void* buffer)
{
if (strncmp(buffer, MVHD_CONECTIX_COOKIE, strlen(MVHD_CONECTIX_COOKIE)) == 0) {
return true;
}
return false;
}

View File

@@ -1,12 +1,49 @@
/*
* libCWALK Path library for C/C++
*
* Version: @(#)cwalk.c 1.0.2 2021/03/16
*
* Authors: Sherman Perry, <shermperry@gmail.com>
* Leonard Ikl<6B>, <https://github.com/likle>
*
* Copyright 2019-2021 Sherman Perry.
* Copyright 2020 Leonard Ikl<6B>.
*
* MIT License
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documenta-
* tion files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall
* be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF O R IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef _FILE_OFFSET_BITS
#define _FILE_OFFSET_BITS 64
# define _FILE_OFFSET_BITS 64
#endif
#include <assert.h>
#include <ctype.h>
#include <stdbool.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include "cwalk.h"
/**
* We try to default to a different path style depending on the operating
* system. So this should detect whether we should use windows or unix paths.

View File

@@ -1,10 +1,40 @@
#pragma once
/*
* libCWALK path library for C/C++
*
* Version: @(#)cwalk.h 1.0.3 2021/03/22
*
* Authors: Sherman Perry, <shermperry@gmail.com>
* Leonard Ikl<6B>, <https://github.com/likle>
*
* Copyright 2019-2021 Sherman Perry.
* Copyright 2020 Leonard Ikl<6B>.
*
* MIT License
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documenta-
* tion files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall
* be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF O R IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef CWK_LIBRARY_H
#define CWK_LIBRARY_H
# define CWK_LIBRARY_H
#include <stdbool.h>
#include <stddef.h>
/**
* A segment represents a single component of a path. For instance, on linux a
@@ -45,6 +75,11 @@ enum cwk_path_style
CWK_STYLE_UNIX
};
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Generates an absolute path based on a base.
*
@@ -454,4 +489,9 @@ void cwk_path_set_style(enum cwk_path_style style);
*/
enum cwk_path_style cwk_path_get_style(void);
#ifdef __cplusplus
}
#endif
#endif /*CWK_LIBRARY_H*/

429
src/disk/minivhd/internal.h Normal file
View File

@@ -0,0 +1,429 @@
/*
* MiniVHD Minimalist VHD implementation in C.
*
* This file is part of the MiniVHD Project.
*
* Internal definitions.
*
* Version: @(#)internal.h 1.0.1 2021/03/15
*
* Author: Sherman Perry, <shermperry@gmail.com>
*
* Copyright 2019-2021 Sherman Perry.
*
* MIT License
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documenta-
* tion files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall
* be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF O R IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef MINIVHD_INTERNAL_H
# define MINIVHD_INTERNAL_H
#define MVHD_FOOTER_SIZE 512
#define MVHD_SPARSE_SIZE 1024
#define MVHD_SECTOR_SIZE 512
#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
* Windows uses
*/
#define MVHD_MAX_PATH_CHARS 260
#define MVHD_MAX_PATH_BYTES 1040
#define MVHD_DIF_LOC_W2RU 0x57327275
#define MVHD_DIF_LOC_W2KU 0x57326B75
#define MVHD_START_TS 946684800
typedef struct MVHDSectorBitmap {
uint8_t* curr_bitmap;
int sector_count;
int curr_block;
} MVHDSectorBitmap;
typedef struct MVHDFooter {
uint8_t cookie[8];
uint32_t features;
uint32_t fi_fmt_vers;
uint64_t data_offset;
uint32_t timestamp;
uint8_t cr_app[4];
uint32_t cr_vers;
uint8_t cr_host_os[4];
uint64_t orig_sz;
uint64_t curr_sz;
struct {
uint16_t cyl;
uint8_t heads;
uint8_t spt;
} geom;
uint32_t disk_type;
uint32_t checksum;
uint8_t uuid[16];
uint8_t saved_st;
uint8_t reserved[427];
} MVHDFooter;
typedef struct MVHDSparseHeader {
uint8_t cookie[8];
uint64_t data_offset;
uint64_t bat_offset;
uint32_t head_vers;
uint32_t max_bat_ent;
uint32_t block_sz;
uint32_t checksum;
uint8_t par_uuid[16];
uint32_t par_timestamp;
uint32_t reserved_1;
uint8_t par_utf16_name[512];
struct {
uint32_t plat_code;
uint32_t plat_data_space;
uint32_t plat_data_len;
uint32_t reserved;
uint64_t plat_data_offset;
} par_loc_entry[8];
uint8_t reserved_2[256];
} MVHDSparseHeader;
struct MVHDMeta {
FILE* f;
bool readonly;
char filename[MVHD_MAX_PATH_BYTES];
struct MVHDMeta* parent;
MVHDFooter footer;
MVHDSparseHeader sparse;
uint32_t* block_offset;
int sect_per_block;
MVHDSectorBitmap bitmap;
int (*read_sectors)(struct MVHDMeta*, uint32_t, int, void*);
int (*write_sectors)(struct MVHDMeta*, uint32_t, int, void*);
struct {
uint8_t* zero_data;
int sector_count;
} format_buffer;
};
#ifdef __cplusplus
extern "C" {
#endif
/**
* Functions to deal with endian issues
*/
uint16_t mvhd_from_be16(uint16_t val);
uint32_t mvhd_from_be32(uint32_t val);
uint64_t mvhd_from_be64(uint64_t val);
uint16_t mvhd_to_be16(uint16_t val);
uint32_t mvhd_to_be32(uint32_t val);
uint64_t mvhd_to_be64(uint64_t val);
/**
* \brief Check if provided buffer begins with the string "conectix"
*
* \param [in] buffer The buffer to compare. Must be at least 8 bytes in length
*
* \return true if the buffer begins with "conectix"
* \return false if the buffer does not begin with "conectix"
*/
bool mvhd_is_conectix_str(const void* buffer);
/**
* \brief Generate a raw 16 byte UUID
*
* \param [out] uuid A 16 byte buffer in which the generated UUID will be stored to
*/
void mvhd_generate_uuid(uint8_t *uuid);
/**
* \brief Calculate a VHD formatted timestamp from the current time
*/
uint32_t vhd_calc_timestamp(void);
/**
* \brief Convert an epoch timestamp to a VHD timestamp
*
* \param [in] ts epoch timestamp to convert.
*
* \return The adjusted timestamp, or 0 if the input timestamp is
* earlier that 1 Janurary 2000
*/
uint32_t mvhd_epoch_to_vhd_ts(time_t ts);
/**
* \brief Return the created time from a VHD image
*
* \param [in] vhdm Pointer to the MiniVHD metadata structure
*
* \return The created time, as a Unix timestamp
*/
time_t vhd_get_created_time(struct MVHDMeta *vhdm);
/**
* \brief Cross platform, unicode filepath opening
*
* This function accounts for the fact that fopen() handles file paths differently compared to other
* operating systems. Windows version of fopen() will not handle multi byte encoded text like UTF-8.
*
* Unicode filepath support on Windows requires using the _wfopen() function, which expects UTF-16LE
* encoded path and modestring.
*
* \param [in] path The filepath to open as a UTF-8 string
* \param [in] mode The mode string to use (eg: "rb+"")
* \param [out] err The error value, if an error occurrs
*
* \return a FILE pointer if successful, NULL otherwise. If NULL, check the value of err
*/
FILE* mvhd_fopen(const char* path, const char* mode, int* err);
void mvhd_set_encoding_err(int encoding_retval, int* err);
/**
* \brief Generate VHD footer checksum
*
* \param [in] vhdm MiniVHD data structure
*/
uint32_t mvhd_gen_footer_checksum(MVHDFooter* footer);
/**
* \brief Generate VHD sparse header checksum
*
* \param [in] vhdm MiniVHD data structure
*/
uint32_t mvhd_gen_sparse_checksum(MVHDSparseHeader* header);
uint32_t mvhd_crc32_for_byte(uint32_t r);
/**
* \brief Get current position in file stream
*
* This is a portable version of the POSIX ftello64(). *
*/
int64_t mvhd_ftello64(FILE* stream);
/**
* \brief Reposition the file stream's position
*
* This is a portable version of the POSIX fseeko64(). *
*/
int mvhd_fseeko64(FILE* stream, int64_t offset, int origin);
/**
* \brief Calculate the CRC32 of a data buffer.
*
* This function can be used for verifying data integrity.
*
* \param [in] data The data buffer
* \param [in] n_bytes The size of the data buffer in bytes
*
* \return The CRC32 of the data buffer
*/
uint32_t mvhd_crc32(const void* data, size_t n_bytes);
/**
* \brief Calculate the file modification timestamp.
*
* This function is primarily to help protect differencing VHD's
*
* \param [in] path the UTF-8 file path
* \param [out] err The error value, if an error occurrs
*
* \return The file modified timestamp, in VHD compatible timestamp.
* 'err' will be set to non-zero on error
*/
uint32_t mvhd_file_mod_timestamp(const char* path, int *err);
struct 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);
/**
* \brief Write zero filled sectors to file.
*
* Note, the caller should set the file position before calling this
* function for correct operation.
*
* \param [in] f File to write sectors to
* \param [in] sector_count The number of sectors to write
*/
void mvhd_write_empty_sectors(FILE* f, int sector_count);
/**
* \brief Read a fixed VHD image
*
* Fixed VHD images are essentially raw image files with a footer tacked on
* the end. They are therefore straightforward to write
*
* \param [in] vhdm MiniVHD data structure
* \param [in] offset Sector offset to read from
* \param [in] num_sectors The desired number of sectors to read
* \param [out] out_buff An output buffer to store read sectors. Must be
* large enough to hold num_sectors worth of sectors.
*
* \retval 0 num_sectors were read from file
* \retval >0 < num_sectors were read from file
*/
int mvhd_fixed_read(struct MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff);
/**
* \brief Read a sparse VHD image
*
* Sparse, or dynamic images are VHD images that grow as data is written to them.
*
* This function implements the logic to read sectors from the file, taking into
* account the fact that blocks may be stored on disk in any order, and that the
* read could cross block boundaries.
*
* \param [in] vhdm MiniVHD data structure
* \param [in] offset Sector offset to read from
* \param [in] num_sectors The desired number of sectors to read
* \param [out] out_buff An output buffer to store read sectors. Must be
* large enough to hold num_sectors worth of sectors.
*
* \retval 0 num_sectors were read from file
* \retval >0 < num_sectors were read from file
*/
int mvhd_sparse_read(struct MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff);
/**
* \brief Read a differencing VHD image
*
* Differencing images are a variant of a sparse image. They contain the grow-on-demand
* properties of sparse images, but also reference a parent image. Data is read from the
* child image only if it is newer than the data stored in the parent image.
*
* This function implements the logic to read sectors from the child, or a parent image.
* Differencing images may have a differencing image as a parent, creating a chain of images.
* There is no theoretical chain length limit, although I do not consider long chains to be
* advisable. Verifying the parent-child relationship is not very robust.
*
* \param [in] vhdm MiniVHD data structure
* \param [in] offset Sector offset to read from
* \param [in] num_sectors The desired number of sectors to read
* \param [out] out_buff An output buffer to store read sectors. Must be
* large enough to hold num_sectors worth of sectors.
*
* \retval 0 num_sectors were read from file
* \retval >0 < num_sectors were read from file
*/
int mvhd_diff_read(struct MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff);
/**
* \brief Write to a fixed VHD image
*
* Fixed VHD images are essentially raw image files with a footer tacked on
* the end. They are therefore straightforward to write
*
* \param [in] vhdm MiniVHD data structure
* \param [in] offset Sector offset to write to
* \param [in] num_sectors The desired number of sectors to write
* \param [in] in_buff A source buffer to write sectors from. Must be
* large enough to hold num_sectors worth of sectors.
*
* \retval 0 num_sectors were written to file
* \retval >0 < num_sectors were written to file
*/
int mvhd_fixed_write(struct MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff);
/**
* \brief Write to a sparse or differencing VHD image
*
* Sparse, or dynamic images are VHD images that grow as data is written to them.
*
* Differencing images are a variant of a sparse image. They contain the grow-on-demand
* properties of sparse images, but also reference a parent image. Data is always written
* to the child image. This makes writing to differencing images essentially identical to
* writing to sparse images, hence they use the same function.
*
* This function implements the logic to write sectors to the file, taking into
* account the fact that blocks may be stored on disk in any order, and that the
* write operation could cross block boundaries.
*
* \param [in] vhdm MiniVHD data structure
* \param [in] offset Sector offset to write to
* \param [in] num_sectors The desired number of sectors to write
* \param [in] in_buff A source buffer to write sectors from. Must be
* large enough to hold num_sectors worth of sectors.
*
* \retval 0 num_sectors were written to file
* \retval >0 < num_sectors were written to file
*/
int mvhd_sparse_diff_write(struct MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff);
/**
* \brief A no-op function to "write" to read-only VHD images
*
* \param [in] vhdm MiniVHD data structure
* \param [in] offset Sector offset to write to
* \param [in] num_sectors The desired number of sectors to write
* \param [in] in_buff A source buffer to write sectors from. Must be
* large enough to hold num_sectors worth of sectors.
*
* \retval 0 num_sectors were written to file
* \retval >0 < num_sectors were written to file
*/
int mvhd_noop_write(struct MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff);
/**
* \brief Save the contents of a VHD footer from a buffer to a struct
*
* \param [out] footer save contents of buffer into footer
* \param [in] buffer VHD footer in raw bytes
*/
void mvhd_buffer_to_footer(MVHDFooter* footer, uint8_t* buffer);
/**
* \brief Save the contents of a VHD sparse header from a buffer to a struct
*
* \param [out] header save contents of buffer into header
* \param [in] buffer VHD header in raw bytes
*/
void mvhd_buffer_to_header(MVHDSparseHeader* header, uint8_t* buffer);
/**
* \brief Save the contents of a VHD footer struct to a buffer
*
* \param [in] footer save contents of struct into buffer
* \param [out] buffer VHD footer in raw bytes
*/
void mvhd_footer_to_buffer(MVHDFooter* footer, uint8_t* buffer);
/**
* \brief Save the contents of a VHD sparse header struct to a buffer
*
* \param [in] header save contents of struct into buffer
* \param [out] buffer VHD sparse header in raw bytes
*/
void mvhd_header_to_buffer(MVHDSparseHeader* header, uint8_t* buffer);
#ifdef __cplusplus
}
#endif
#endif /*MINIVHD_INTERNAL_H*/

View File

@@ -1,12 +0,0 @@
#ifndef LIBXML2_ENCODING_H
#define LIBXML2_ENCODING_H
#include <stdint.h>
typedef uint16_t mvhd_utf16;
void xmlEncodingInit(void);
int UTF16LEToUTF8(unsigned char* out, int *outlen, const unsigned char* inb, int *inlenb);
int UTF8ToUTF16LE(unsigned char* outb, int *outlen, const unsigned char* in, int *inlen);
int UTF16BEToUTF8(unsigned char* out, int *outlen, const unsigned char* inb, int *inlenb);
int UTF8ToUTF16BE(unsigned char* outb, int *outlen, const unsigned char* in, int *inlen);
#endif

View File

@@ -1,66 +1,104 @@
/**
* \file
* \brief VHD management functions (open, close, read write etc)
/*
* MiniVHD Minimalist VHD implementation in C.
*
* This file is part of the MiniVHD Project.
*
* VHD management functions (open, close, read write etc)
*
* Version: @(#)manage.c 1.0.4 2021/04/16
*
* Authors: Sherman Perry, <shermperry@gmail.com>
* Fred N. van Kempen, <waltje@varcem.com>
*
* Copyright 2019-2021 Sherman Perry.
* Copyright 2021 Fred N. van Kempen.
*
* MIT License
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documenta-
* tion files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall
* be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF O R IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef _FILE_OFFSET_BITS
#define _FILE_OFFSET_BITS 64
# define _FILE_OFFSET_BITS 64
#endif
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include "cwalk.h"
#include "libxml2_encoding.h"
#include "minivhd_internal.h"
#include "minivhd_io.h"
#include "minivhd_util.h"
#include "minivhd_struct_rw.h"
#include "minivhd.h"
#include "internal.h"
#include "version.h"
#include "cwalk.h"
#include "xml2_encoding.h"
int mvhd_errno = 0;
static char tmp_open_path[MVHD_MAX_PATH_BYTES] = {0};
struct MVHDPaths {
char dir_path[MVHD_MAX_PATH_BYTES];
char file_name[MVHD_MAX_PATH_BYTES];
char w2ku_path[MVHD_MAX_PATH_BYTES];
char w2ru_path[MVHD_MAX_PATH_BYTES];
char joined_path[MVHD_MAX_PATH_BYTES];
char dir_path[MVHD_MAX_PATH_BYTES];
char file_name[MVHD_MAX_PATH_BYTES];
char w2ku_path[MVHD_MAX_PATH_BYTES];
char w2ru_path[MVHD_MAX_PATH_BYTES];
char joined_path[MVHD_MAX_PATH_BYTES];
uint16_t tmp_src_path[MVHD_MAX_PATH_CHARS];
};
static void mvhd_read_footer(MVHDMeta* vhdm);
static void mvhd_read_sparse_header(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);
static void mvhd_calc_sparse_values(MVHDMeta* vhdm);
static int mvhd_init_sector_bitmap(MVHDMeta* vhdm, MVHDError* err);
int mvhd_errno = 0;
static char tmp_open_path[MVHD_MAX_PATH_BYTES] = {0};
/**
* \brief Populate data stuctures with content from a VHD footer
*
* \param [in] vhdm MiniVHD data structure
*/
static void mvhd_read_footer(MVHDMeta* vhdm) {
static void
read_footer(MVHDMeta* vhdm)
{
uint8_t buffer[MVHD_FOOTER_SIZE];
mvhd_fseeko64(vhdm->f, -MVHD_FOOTER_SIZE, SEEK_END);
(void) !fread(buffer, sizeof buffer, 1, vhdm->f);
mvhd_buffer_to_footer(&vhdm->footer, buffer);
}
/**
* \brief Populate data stuctures with content from a VHD sparse header
*
* \param [in] vhdm MiniVHD data structure
*/
static void mvhd_read_sparse_header(MVHDMeta* vhdm) {
static void
read_sparse_header(MVHDMeta* vhdm)
{
uint8_t buffer[MVHD_SPARSE_SIZE];
mvhd_fseeko64(vhdm->f, vhdm->footer.data_offset, SEEK_SET);
(void) !fread(buffer, sizeof buffer, 1, vhdm->f);
mvhd_buffer_to_header(&vhdm->sparse, buffer);
}
/**
* \brief Validate VHD footer checksum
*
@@ -68,10 +106,13 @@ static void mvhd_read_sparse_header(MVHDMeta* vhdm) {
*
* \param [in] vhdm MiniVHD data structure
*/
static bool mvhd_footer_checksum_valid(MVHDMeta* vhdm) {
static bool
footer_checksum_valid(MVHDMeta* vhdm)
{
return vhdm->footer.checksum == mvhd_gen_footer_checksum(&vhdm->footer);
}
/**
* \brief Validate VHD sparse header checksum
*
@@ -79,10 +120,13 @@ static bool mvhd_footer_checksum_valid(MVHDMeta* vhdm) {
*
* \param [in] vhdm MiniVHD data structure
*/
static bool mvhd_sparse_checksum_valid(MVHDMeta* vhdm) {
static bool
sparse_checksum_valid(MVHDMeta* vhdm)
{
return vhdm->sparse.checksum == mvhd_gen_sparse_checksum(&vhdm->sparse);
}
/**
* \brief Read BAT into MiniVHD data structure
*
@@ -96,13 +140,17 @@ static bool mvhd_sparse_checksum_valid(MVHDMeta* vhdm) {
* \retval -1 if an error occurrs. Check value of err in this case
* \retval 0 if the function call succeeds
*/
static int mvhd_read_bat(MVHDMeta *vhdm, MVHDError* err) {
static int
read_bat(MVHDMeta *vhdm, MVHDError* err)
{
vhdm->block_offset = calloc(vhdm->sparse.max_bat_ent, sizeof *vhdm->block_offset);
if (vhdm->block_offset == NULL) {
*err = MVHD_ERR_MEM;
return -1;
}
mvhd_fseeko64(vhdm->f, vhdm->sparse.bat_offset, SEEK_SET);
for (uint32_t i = 0; i < vhdm->sparse.max_bat_ent; i++) {
(void) !fread(&vhdm->block_offset[i], sizeof *vhdm->block_offset, 1, vhdm->f);
vhdm->block_offset[i] = mvhd_from_be32(vhdm->block_offset[i]);
@@ -110,20 +158,25 @@ static int mvhd_read_bat(MVHDMeta *vhdm, MVHDError* err) {
return 0;
}
/**
* \brief Perform a one-time calculation of some sparse VHD values
*
* \param [in] vhdm MiniVHD data structure
*/
static void mvhd_calc_sparse_values(MVHDMeta* vhdm) {
static void
calc_sparse_values(MVHDMeta* vhdm)
{
vhdm->sect_per_block = vhdm->sparse.block_sz / MVHD_SECTOR_SIZE;
int bm_bytes = vhdm->sect_per_block / 8;
vhdm->bitmap.sector_count = bm_bytes / MVHD_SECTOR_SIZE;
if (bm_bytes % MVHD_SECTOR_SIZE > 0) {
vhdm->bitmap.sector_count++;
}
}
/**
* \brief Allocate memory for a sector bitmap.
*
@@ -137,16 +190,21 @@ static void mvhd_calc_sparse_values(MVHDMeta* vhdm) {
* \retval -1 if an error occurrs. Check value of err in this case
* \retval 0 if the function call succeeds
*/
static int mvhd_init_sector_bitmap(MVHDMeta* vhdm, MVHDError* err) {
static int
init_sector_bitmap(MVHDMeta* vhdm, MVHDError* err)
{
vhdm->bitmap.curr_bitmap = calloc(vhdm->bitmap.sector_count, MVHD_SECTOR_SIZE);
if (vhdm->bitmap.curr_bitmap == NULL) {
*err = MVHD_ERR_MEM;
return -1;
}
vhdm->bitmap.curr_block = -1;
return 0;
}
/**
* \brief Check if the path for a given platform code exists
*
@@ -163,13 +221,19 @@ static int mvhd_init_sector_bitmap(MVHDMeta* vhdm, MVHDError* err) {
* \retval true if a file is found
* \retval false if a file is not found
*/
static bool mvhd_parent_path_exists(struct MVHDPaths* paths, uint32_t plat_code) {
memset(paths->joined_path, 0, sizeof paths->joined_path);
static bool
mvhd_parent_path_exists(struct MVHDPaths* paths, uint32_t plat_code)
{
FILE* f;
int cwk_ret, ferr;
enum cwk_path_style style = cwk_path_guess_style((const char*)paths->dir_path);
int ferr;
size_t cwk_ret;
enum cwk_path_style style;
memset(paths->joined_path, 0, sizeof paths->joined_path);
style = cwk_path_guess_style((const char*)paths->dir_path);
cwk_path_set_style(style);
cwk_ret = 1;
if (plat_code == MVHD_DIF_LOC_W2RU && *paths->w2ru_path) {
cwk_ret = cwk_path_join((const char*)paths->dir_path, (const char*)paths->w2ru_path, paths->joined_path, sizeof paths->joined_path);
} else if (plat_code == MVHD_DIF_LOC_W2KU && *paths->w2ku_path) {
@@ -181,6 +245,7 @@ static bool mvhd_parent_path_exists(struct MVHDPaths* paths, uint32_t plat_code)
if (cwk_ret > MVHD_MAX_PATH_BYTES) {
return false;
}
f = mvhd_fopen((const char*)paths->joined_path, "rb", &ferr);
if (f != NULL) {
/* We found a file at the requested path! */
@@ -188,11 +253,12 @@ static bool mvhd_parent_path_exists(struct MVHDPaths* paths, uint32_t plat_code)
tmp_open_path[sizeof tmp_open_path - 1] = '\0';
fclose(f);
return true;
} else {
return false;
}
return false;
}
/**
* \brief attempt to obtain a file path to a file that may be a valid VHD image
*
@@ -208,27 +274,33 @@ static bool mvhd_parent_path_exists(struct MVHDPaths* paths, uint32_t plat_code)
* \return a pointer to the global string `tmp_open_path`, or NULL if a path could
* not be found, or some error occurred
*/
static char* mvhd_get_diff_parent_path(MVHDMeta* vhdm, int* err) {
static char*
get_diff_parent_path(MVHDMeta* vhdm, int* err)
{
int utf_outlen, utf_inlen, utf_ret;
char* par_fp = NULL;
char *par_fp = NULL;
struct MVHDPaths *paths;
size_t dirlen;
/* We can't resolve relative paths if we don't have an absolute
path to work with */
if (!cwk_path_is_absolute((const char*)vhdm->filename)) {
*err = MVHD_ERR_PATH_REL;
goto end;
}
struct MVHDPaths* paths = calloc(1, sizeof *paths);
paths = calloc(1, sizeof *paths);
if (paths == NULL) {
*err = MVHD_ERR_MEM;
goto end;
}
size_t dirlen;
cwk_path_get_dirname((const char*)vhdm->filename, &dirlen);
if (dirlen >= sizeof paths->dir_path) {
*err = MVHD_ERR_PATH_LEN;
goto paths_cleanup;
}
memcpy(paths->dir_path, vhdm->filename, dirlen);
/* Get the filename field from the sparse header. */
utf_outlen = (int)sizeof paths->file_name;
utf_inlen = (int)sizeof vhdm->sparse.par_utf16_name;
@@ -237,8 +309,10 @@ static char* mvhd_get_diff_parent_path(MVHDMeta* vhdm, int* err) {
mvhd_set_encoding_err(utf_ret, err);
goto paths_cleanup;
}
/* Now read the parent locator entries, both relative and absolute, if they exist */
unsigned char* loc_path;
for (int i = 0; i < 8; i++) {
utf_outlen = MVHD_MAX_PATH_BYTES - 1;
if (vhdm->sparse.par_loc_entry[i].plat_code == MVHD_DIF_LOC_W2RU) {
@@ -248,6 +322,7 @@ static char* mvhd_get_diff_parent_path(MVHDMeta* vhdm, int* err) {
} else {
continue;
}
utf_inlen = vhdm->sparse.par_loc_entry[i].plat_data_len;
if (utf_inlen > MVHD_MAX_PATH_BYTES) {
*err = MVHD_ERR_PATH_LEN;
@@ -255,6 +330,7 @@ static char* mvhd_get_diff_parent_path(MVHDMeta* vhdm, int* err) {
}
mvhd_fseeko64(vhdm->f, vhdm->sparse.par_loc_entry[i].plat_data_offset, SEEK_SET);
(void) !fread(paths->tmp_src_path, sizeof (uint8_t), utf_inlen, vhdm->f);
/* Note, the W2*u parent locators are UTF-16LE, unlike the filename field previously obtained,
which is UTF-16BE */
utf_ret = UTF16LEToUTF8(loc_path, &utf_outlen, (const unsigned char*)paths->tmp_src_path, &utf_inlen);
@@ -263,22 +339,26 @@ static char* mvhd_get_diff_parent_path(MVHDMeta* vhdm, int* err) {
goto paths_cleanup;
}
}
/* We have paths in UTF-8. We should have enough info to try and find the parent VHD */
/* Does the relative path exist? */
if (mvhd_parent_path_exists(paths, MVHD_DIF_LOC_W2RU)) {
par_fp = tmp_open_path;
goto paths_cleanup;
}
/* What about trying the child directory? */
if (mvhd_parent_path_exists(paths, 0)) {
par_fp = tmp_open_path;
goto paths_cleanup;
}
/* Well, all else fails, try the stored absolute path, if it exists */
if (mvhd_parent_path_exists(paths, MVHD_DIF_LOC_W2KU)) {
par_fp = tmp_open_path;
goto paths_cleanup;
}
/* If we reach this point, we could not find a path with a valid file */
par_fp = NULL;
*err = MVHD_ERR_PAR_NOT_FOUND;
@@ -286,10 +366,12 @@ static char* mvhd_get_diff_parent_path(MVHDMeta* vhdm, int* err) {
paths_cleanup:
free(paths);
paths = NULL;
end:
return par_fp;
}
/**
* \brief Attach the read/write function pointers to read/write functions
*
@@ -298,44 +380,90 @@ end:
*
* \param [in] vhdm MiniVHD data structure
*/
static void mvhd_assign_io_funcs(MVHDMeta* vhdm) {
static void
assign_io_funcs(MVHDMeta* vhdm)
{
switch (vhdm->footer.disk_type) {
case MVHD_TYPE_FIXED:
vhdm->read_sectors = mvhd_fixed_read;
vhdm->write_sectors = mvhd_fixed_write;
break;
case MVHD_TYPE_DYNAMIC:
vhdm->read_sectors = mvhd_sparse_read;
vhdm->write_sectors = mvhd_sparse_diff_write;
break;
case MVHD_TYPE_DIFF:
vhdm->read_sectors = mvhd_diff_read;
vhdm->write_sectors = mvhd_sparse_diff_write;
break;
case MVHD_TYPE_FIXED:
vhdm->read_sectors = mvhd_fixed_read;
vhdm->write_sectors = mvhd_fixed_write;
break;
case MVHD_TYPE_DYNAMIC:
vhdm->read_sectors = mvhd_sparse_read;
vhdm->write_sectors = mvhd_sparse_diff_write;
break;
case MVHD_TYPE_DIFF:
vhdm->read_sectors = mvhd_diff_read;
vhdm->write_sectors = mvhd_sparse_diff_write;
break;
}
if (vhdm->readonly) {
if (vhdm->readonly)
vhdm->write_sectors = mvhd_noop_write;
}
}
bool mvhd_file_is_vhd(FILE* f) {
if (f) {
uint8_t con_str[8];
mvhd_fseeko64(f, -MVHD_FOOTER_SIZE, SEEK_END);
(void) !fread(con_str, sizeof con_str, 1, f);
return mvhd_is_conectix_str(con_str);
} else {
return false;
}
/**
* \brief Return the library version as a string
*/
MVHDAPI const char *
mvhd_version(void)
{
return LIB_VERSION_4;
}
MVHDGeom mvhd_calculate_geometry(uint64_t size) {
/**
* \brief Return the library version as a number
*/
MVHDAPI uint32_t
mvhd_version_id(void)
{
return (LIB_VER_MAJOR << 24) | (LIB_VER_MINOR << 16) |
(LIB_VER_REV << 16) | LIB_VER_PATCH;
}
/**
* \brief A simple test to see if a given file is a VHD
*
* \param [in] f file to test
*
* \retval 1 if f is a VHD
* \retval 0 if f is not a VHD
*/
MVHDAPI int
mvhd_file_is_vhd(FILE* f)
{
uint8_t con_str[8];
if (f == NULL) {
return 0;
}
mvhd_fseeko64(f, -MVHD_FOOTER_SIZE, SEEK_END);
fread(con_str, sizeof con_str, 1, f);
if (mvhd_is_conectix_str(con_str)) {
return 1;
}
return 0;
}
MVHDAPI MVHDGeom
mvhd_calculate_geometry(uint64_t size)
{
MVHDGeom chs;
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) {
spt = 255;
heads = 16;
@@ -358,77 +486,95 @@ MVHDGeom mvhd_calculate_geometry(uint64_t size) {
cth = ts / spt;
}
}
cyl = cth / heads;
chs.heads = heads;
chs.spt = spt;
chs.cyl = cyl;
return chs;
}
MVHDMeta* mvhd_open(const char* path, bool readonly, int* err) {
MVHDAPI MVHDMeta*
mvhd_open(const char* path, int readonly, int* err)
{
MVHDError open_err;
MVHDMeta *vhdm = calloc(sizeof *vhdm, 1);
if (vhdm == NULL) {
*err = MVHD_ERR_MEM;
goto end;
}
if (strlen(path) >= sizeof vhdm->filename) {
*err = MVHD_ERR_PATH_LEN;
goto cleanup_vhdm;
}
//This is safe, as we've just checked for potential overflow above
strcpy(vhdm->filename, path);
vhdm->f = readonly ? mvhd_fopen((const char*)vhdm->filename, "rb", err) : mvhd_fopen((const char*)vhdm->filename, "rb+", err);
if (readonly) {
vhdm->f = mvhd_fopen((const char*)vhdm->filename, "rb", err);
} else {
vhdm->f = mvhd_fopen((const char*)vhdm->filename, "rb+", err);
}
if (vhdm->f == NULL) {
/* note, mvhd_fopen sets err for us */
goto cleanup_vhdm;
}
vhdm->readonly = readonly;
if (!mvhd_file_is_vhd(vhdm->f)) {
*err = MVHD_ERR_NOT_VHD;
goto cleanup_file;
}
mvhd_read_footer(vhdm);
if (!mvhd_footer_checksum_valid(vhdm)) {
read_footer(vhdm);
if (!footer_checksum_valid(vhdm)) {
*err = MVHD_ERR_FOOTER_CHECKSUM;
goto cleanup_file;
}
if (vhdm->footer.disk_type == MVHD_TYPE_DIFF || vhdm->footer.disk_type == MVHD_TYPE_DYNAMIC) {
mvhd_read_sparse_header(vhdm);
if (!mvhd_sparse_checksum_valid(vhdm)) {
read_sparse_header(vhdm);
if (!sparse_checksum_valid(vhdm)) {
*err = MVHD_ERR_SPARSE_CHECKSUM;
goto cleanup_file;
}
if (mvhd_read_bat(vhdm, &open_err) == -1) {
if (read_bat(vhdm, &open_err) == -1) {
*err = open_err;
goto cleanup_file;
}
mvhd_calc_sparse_values(vhdm);
if (mvhd_init_sector_bitmap(vhdm, &open_err) == -1) {
calc_sparse_values(vhdm);
if (init_sector_bitmap(vhdm, &open_err) == -1) {
*err = open_err;
goto cleanup_bat;
}
} else if (vhdm->footer.disk_type != MVHD_TYPE_FIXED) {
*err = MVHD_ERR_TYPE;
goto cleanup_bitmap;
}
mvhd_assign_io_funcs(vhdm);
assign_io_funcs(vhdm);
vhdm->format_buffer.zero_data = calloc(64, MVHD_SECTOR_SIZE);
if (vhdm->format_buffer.zero_data == NULL) {
*err = MVHD_ERR_MEM;
goto cleanup_bitmap;
}
vhdm->format_buffer.sector_count = 64;
if (vhdm->footer.disk_type == MVHD_TYPE_DIFF) {
char* par_path = mvhd_get_diff_parent_path(vhdm, err);
char* par_path = get_diff_parent_path(vhdm, err);
if (par_path == NULL) {
goto cleanup_format_buff;
}
uint32_t par_mod_ts = mvhd_file_mod_timestamp(par_path, err);
if (*err != 0) {
goto cleanup_format_buff;
}
if (vhdm->sparse.par_timestamp != par_mod_ts) {
/* The last-modified timestamp is to fragile to make this a fatal error.
Instead, we inform the caller of the potential problem. */
@@ -438,57 +584,78 @@ MVHDMeta* mvhd_open(const char* path, bool readonly, int* err) {
if (vhdm->parent == NULL) {
goto cleanup_format_buff;
}
if (memcmp(vhdm->sparse.par_uuid, vhdm->parent->footer.uuid, sizeof vhdm->sparse.par_uuid) != 0) {
*err = MVHD_ERR_INVALID_PAR_UUID;
goto cleanup_format_buff;
}
}
/* If we've reached this point, we are good to go, so skip the cleanup steps */
/*
* If we've reached this point, we are good to go,
* so skip the cleanup steps.
*/
goto end;
cleanup_format_buff:
free(vhdm->format_buffer.zero_data);
vhdm->format_buffer.zero_data = NULL;
cleanup_bitmap:
free(vhdm->bitmap.curr_bitmap);
vhdm->bitmap.curr_bitmap = NULL;
cleanup_bat:
free(vhdm->block_offset);
vhdm->block_offset = NULL;
cleanup_file:
fclose(vhdm->f);
vhdm->f = NULL;
cleanup_vhdm:
free(vhdm);
vhdm = NULL;
end:
return vhdm;
}
void mvhd_close(MVHDMeta* vhdm) {
if (vhdm != NULL) {
if (vhdm->parent != NULL) {
mvhd_close(vhdm->parent);
}
fclose(vhdm->f);
if (vhdm->block_offset != NULL) {
free(vhdm->block_offset);
vhdm->block_offset = NULL;
}
if (vhdm->bitmap.curr_bitmap != NULL) {
free(vhdm->bitmap.curr_bitmap);
vhdm->bitmap.curr_bitmap = NULL;
}
if (vhdm->format_buffer.zero_data != NULL) {
free(vhdm->format_buffer.zero_data);
vhdm->format_buffer.zero_data = NULL;
}
free(vhdm);
vhdm = NULL;
MVHDAPI void
mvhd_close(MVHDMeta* vhdm)
{
if (vhdm == NULL)
return;
if (vhdm->parent != NULL) {
mvhd_close(vhdm->parent);
}
fclose(vhdm->f);
if (vhdm->block_offset != NULL) {
free(vhdm->block_offset);
vhdm->block_offset = NULL;
}
if (vhdm->bitmap.curr_bitmap != NULL) {
free(vhdm->bitmap.curr_bitmap);
vhdm->bitmap.curr_bitmap = NULL;
}
if (vhdm->format_buffer.zero_data != NULL) {
free(vhdm->format_buffer.zero_data);
vhdm->format_buffer.zero_data = NULL;
}
free(vhdm);
}
int mvhd_diff_update_par_timestamp(MVHDMeta* vhdm, int* err) {
MVHDAPI int
mvhd_diff_update_par_timestamp(MVHDMeta* vhdm, int* err)
{
uint8_t sparse_buff[1024];
if (vhdm == NULL || err == NULL) {
*err = MVHD_ERR_INVALID_PARAMS;
return -1;
@@ -497,7 +664,7 @@ int mvhd_diff_update_par_timestamp(MVHDMeta* vhdm, int* err) {
*err = MVHD_ERR_TYPE;
return -1;
}
char* par_path = mvhd_get_diff_parent_path(vhdm, err);
char* par_path = get_diff_parent_path(vhdm, err);
if (par_path == NULL) {
return -1;
}
@@ -505,31 +672,53 @@ int mvhd_diff_update_par_timestamp(MVHDMeta* vhdm, int* err) {
if (*err != 0) {
return -1;
}
/* Update the timestamp and sparse header checksum */
vhdm->sparse.par_timestamp = par_mod_ts;
vhdm->sparse.checksum = mvhd_gen_sparse_checksum(&vhdm->sparse);
/* Generate and write the updated sparse header */
mvhd_header_to_buffer(&vhdm->sparse, sparse_buff);
mvhd_fseeko64(vhdm->f, (int64_t)vhdm->footer.data_offset, SEEK_SET);
fwrite(sparse_buff, sizeof sparse_buff, 1, vhdm->f);
return 0;
}
int mvhd_read_sectors(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff) {
MVHDAPI 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, uint32_t offset, int num_sectors, void* in_buff) {
MVHDAPI 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, uint32_t offset, int num_sectors) {
MVHDAPI 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;
}
MVHDAPI MVHDType
mvhd_get_type(MVHDMeta* vhdm)
{
return vhdm->footer.disk_type;
}

View File

@@ -1,11 +1,49 @@
/*
* MiniVHD Minimalist VHD implementation in C.
* MiniVHD is a minimalist implementation of read/write/creation
* of VHD files. It is designed to read and write to VHD files
* at a sector level. It does not enable file access, or provide
* mounting options. Those features are left to more advanced
* libraries and/or the operating system.
*
* This file is part of the MiniVHD Project.
*
* Definitions for the MiniVHD library.
*
* Version: @(#)minivhd.h 1.0.3 2021/04/16
*
* Authors: Sherman Perry, <shermperry@gmail.com>
* Fred N. van Kempen, <waltje@varcem.com>
*
* Copyright 2019-2021 Sherman Perry.
* Copyright 2021 Fred N. van Kempen.
*
* MIT License
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documenta-
* tion files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall
* be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF O R IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef MINIVHD_H
#define MINIVHD_H
# define MINIVHD_H
#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>
extern int mvhd_errno;
typedef enum MVHDError {
MVHD_ERR_MEM = -128,
@@ -46,6 +84,11 @@ typedef struct MVHDGeom {
uint8_t spt;
} MVHDGeom;
#ifdef __cplusplus
extern "C" {
#endif
typedef void (*mvhd_progress_callback)(uint32_t current_sector, uint32_t total_sectors);
typedef struct MVHDCreationOptions {
@@ -60,6 +103,42 @@ typedef struct MVHDCreationOptions {
typedef struct MVHDMeta MVHDMeta;
extern int mvhd_errno;
/* Shared-library madness. */
//#if defined(_WIN32)
//# ifdef STATIC
# define MVHDAPI /*nothing*/
//# else
//# ifdef BUILDING_LIBRARY
//# define MVHDAPI __declspec(dllexport)
//# else
//# define MVHDAPI __declspec(dllimport)
//# endif
//# endif
//#elif defined(__GNUC__)
//# ifdef BUILDING_LIBRARY
//# define MVHDAPI __attribute__((visibility("default")))
//# else
//# define MVHDAPI /*nothing*/
//# endif
//#else
//# define MVHDAPI /*nothing*/
//#endif
/**
* \brief Return the library version as a string
*/
MVHDAPI const char *mvhd_version(void);
/**
* \brief Return the library version as a number
*/
MVHDAPI uint32_t mvhd_version_id(void);
/**
* \brief Output a string from a MiniVHD error number
*
@@ -67,17 +146,26 @@ typedef struct MVHDMeta MVHDMeta;
*
* \return Error string
*/
const char* mvhd_strerr(MVHDError err);
MVHDAPI const char* mvhd_strerr(MVHDError err);
/**
* \brief A simple test to see if a given file is a VHD
*
* \param [in] f file to test
*
* \retval true if f is a VHD
* \retval false if f is not a VHD
* \retval 1 if f is a VHD
* \retval 0 if f is not a VHD
*/
bool mvhd_file_is_vhd(FILE* f);
MVHDAPI int mvhd_file_is_vhd(FILE* f);
/**
* \brief Return the file type of the given file
*
* \param [in] vhdm VHD to check.
*
* \retval one of the defined MVHDType values
*/
MVHDAPI MVHDType mvhd_get_type(MVHDMeta* vhdm);
/**
* \brief Open a VHD image for reading and/or writing
@@ -89,7 +177,7 @@ bool mvhd_file_is_vhd(FILE* f);
*
* \param [in] Absolute path to VHD file. Relative path will cause issues when opening
* a differencing VHD file
* \param [in] readonly set this to true to open the VHD in a read only manner
* \param [in] readonly set this to 1 to open the VHD in a read only manner
* \param [out] err will be set if the VHD fails to open. Value could be one of
* MVHD_ERR_MEM, MVHD_ERR_FILE, MVHD_ERR_NOT_VHD, MVHD_ERR_FOOTER_CHECKSUM, MVHD_ERR_SPARSE_CHECKSUM,
* MVHD_ERR_TYPE, MVHD_ERR_TIMESTAMP
@@ -98,7 +186,7 @@ bool mvhd_file_is_vhd(FILE* f);
* \return MVHDMeta pointer. If NULL, check err. err may also be set to MVHD_ERR_TIMESTAMP if
* opening a differencing VHD.
*/
MVHDMeta* mvhd_open(const char* path, bool readonly, int* err);
MVHDAPI MVHDMeta* mvhd_open(const char* path, int readonly, int* err);
/**
* \brief Update the parent modified timestamp in the VHD file
@@ -116,7 +204,7 @@ MVHDMeta* mvhd_open(const char* path, bool readonly, int* err);
*
* \return non-zero on error, 0 on success
*/
int mvhd_diff_update_par_timestamp(MVHDMeta* vhdm, int* err);
MVHDAPI int mvhd_diff_update_par_timestamp(MVHDMeta* vhdm, int* err);
/**
* \brief Create a fixed VHD image
@@ -128,7 +216,7 @@ int mvhd_diff_update_par_timestamp(MVHDMeta* vhdm, int* err);
*
* \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);
MVHDAPI MVHDMeta* mvhd_create_fixed(const char* path, MVHDGeom geom, int* err, mvhd_progress_callback progress_callback);
/**
* \brief Create sparse (dynamic) VHD image.
@@ -139,7 +227,7 @@ MVHDMeta* mvhd_create_fixed(const char* path, MVHDGeom geom, int* err, mvhd_prog
*
* \return NULL if an error occurrs. Check value of *err for actual error. Otherwise returns pointer to a MVHDMeta struct
*/
MVHDMeta* mvhd_create_sparse(const char* path, MVHDGeom geom, int* err);
MVHDAPI MVHDMeta* mvhd_create_sparse(const char* path, MVHDGeom geom, int* err);
/**
* \brief Create differencing VHD imagee.
@@ -150,7 +238,7 @@ MVHDMeta* mvhd_create_sparse(const char* path, MVHDGeom geom, int* err);
*
* \return NULL if an error occurrs. Check value of *err for actual error. Otherwise returns pointer to a MVHDMeta struct
*/
MVHDMeta* mvhd_create_diff(const char* path, const char* par_path, int* err);
MVHDAPI MVHDMeta* mvhd_create_diff(const char* path, const char* par_path, int* err);
/**
* \brief Create a VHD using the provided options
@@ -162,14 +250,14 @@ MVHDMeta* mvhd_create_diff(const char* path, const char* par_path, int* err);
*
* \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);
MVHDAPI MVHDMeta* mvhd_create_ex(MVHDCreationOptions options, int* err);
/**
* \brief Safely close a VHD image
*
* \param [in] vhdm MiniVHD data structure to close
*/
void mvhd_close(MVHDMeta* vhdm);
MVHDAPI void mvhd_close(MVHDMeta* vhdm);
/**
* \brief Calculate hard disk geometry from a provided size
@@ -189,7 +277,47 @@ void mvhd_close(MVHDMeta* vhdm);
*
* \return MVHDGeom the calculated geometry. This can be used in the appropriate create functions.
*/
MVHDGeom mvhd_calculate_geometry(uint64_t size);
MVHDAPI MVHDGeom mvhd_calculate_geometry(uint64_t size);
/**
* \brief Get the CHS geometry from the image
*
* \param [in] vhdm MiniVHD data structure
*
* \return The CHS geometry as stored in the image
*/
MVHDAPI MVHDGeom mvhd_get_geometry(MVHDMeta* vhdm);
/**
* \brief Get the 'current_size' value from the image
*
* Note that the size returned may not match the size calculated from the
* CHS geometry. It is up to the caller to decide how best to handle this.
*
* \param [in] vhdm MiniVHD data structure
*
* \return The 'current_size' value in bytes, as stored in the image.
* Note, this may not match the CHS geometry.
*/
MVHDAPI uint64_t mvhd_get_current_size(MVHDMeta* vhdm);
/**
* \brief Calculate CHS geometry size in bytes
*
* \param [in] geom the CHS geometry to calculate
*
* \return the size in bytes
*/
MVHDAPI uint64_t mvhd_calc_size_bytes(MVHDGeom *geom);
/**
* \brief Calculate CHS geometry size in sectors
*
* \param [in] geom the CHS geometry to calculate
*
* \return the size in sectors
*/
MVHDAPI uint32_t mvhd_calc_size_sectors(MVHDGeom *geom);
/**
* \brief Convert a raw disk image to a fixed VHD image
@@ -200,7 +328,7 @@ MVHDGeom mvhd_calculate_geometry(uint64_t size);
*
* \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);
MVHDAPI 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
@@ -211,7 +339,7 @@ MVHDMeta* mvhd_convert_to_vhd_fixed(const char* utf8_raw_path, const char* utf8_
*
* \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);
MVHDAPI 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
@@ -222,7 +350,7 @@ MVHDMeta* mvhd_convert_to_vhd_sparse(const char* utf8_raw_path, const char* utf8
*
* \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);
MVHDAPI FILE* mvhd_convert_to_raw(const char* utf8_vhd_path, const char* utf8_raw_path, int *err);
/**
* \brief Read sectors from VHD file
@@ -236,7 +364,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, uint32_t offset, int num_sectors, void* out_buff);
MVHDAPI int mvhd_read_sectors(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff);
/**
* \brief Write sectors to VHD file
@@ -250,7 +378,7 @@ int mvhd_read_sectors(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* ou
*
* \return the number of sectors that were not written, or zero
*/
int mvhd_write_sectors(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff);
MVHDAPI int mvhd_write_sectors(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff);
/**
* \brief Write zeroed sectors to VHD file
@@ -265,5 +393,11 @@ int mvhd_write_sectors(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* i
*
* \return the number of sectors that were not written, or zero
*/
int mvhd_format_sectors(MVHDMeta* vhdm, uint32_t offset, int num_sectors);
MVHDAPI int mvhd_format_sectors(MVHDMeta* vhdm, uint32_t offset, int num_sectors);
#ifdef __cplusplus
}
#endif
#endif /*MINIVHD_H*/

View File

@@ -1,8 +0,0 @@
#ifndef MINIVHD_CREATE_H
#define MINIVHD_CREATE_H
#include <stdio.h>
#include "minivhd.h"
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

View File

@@ -1,96 +0,0 @@
#ifndef MINIVHD_INTERNAL_H
#define MINIVHD_INTERNAL_H
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#define MVHD_FOOTER_SIZE 512
#define MVHD_SPARSE_SIZE 1024
#define MVHD_SECTOR_SIZE 512
#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
* Windows uses
*/
#define MVHD_MAX_PATH_CHARS 260
#define MVHD_MAX_PATH_BYTES 1040
#define MVHD_DIF_LOC_W2RU 0x57327275
#define MVHD_DIF_LOC_W2KU 0x57326B75
typedef struct MVHDSectorBitmap {
uint8_t* curr_bitmap;
int sector_count;
int curr_block;
} MVHDSectorBitmap;
typedef struct MVHDFooter {
uint8_t cookie[8];
uint32_t features;
uint32_t fi_fmt_vers;
uint64_t data_offset;
uint32_t timestamp;
uint8_t cr_app[4];
uint32_t cr_vers;
uint8_t cr_host_os[4];
uint64_t orig_sz;
uint64_t curr_sz;
struct {
uint16_t cyl;
uint8_t heads;
uint8_t spt;
} geom;
uint32_t disk_type;
uint32_t checksum;
uint8_t uuid[16];
uint8_t saved_st;
uint8_t reserved[427];
} MVHDFooter;
typedef struct MVHDSparseHeader {
uint8_t cookie[8];
uint64_t data_offset;
uint64_t bat_offset;
uint32_t head_vers;
uint32_t max_bat_ent;
uint32_t block_sz;
uint32_t checksum;
uint8_t par_uuid[16];
uint32_t par_timestamp;
uint32_t reserved_1;
uint8_t par_utf16_name[512];
struct {
uint32_t plat_code;
uint32_t plat_data_space;
uint32_t plat_data_len;
uint32_t reserved;
uint64_t plat_data_offset;
} par_loc_entry[8];
uint8_t reserved_2[256];
} MVHDSparseHeader;
typedef struct MVHDMeta MVHDMeta;
struct MVHDMeta {
FILE* f;
bool readonly;
char filename[MVHD_MAX_PATH_BYTES];
struct MVHDMeta* parent;
MVHDFooter footer;
MVHDSparseHeader sparse;
uint32_t* block_offset;
int sect_per_block;
MVHDSectorBitmap bitmap;
int (*read_sectors)(MVHDMeta*, uint32_t, int, void*);
int (*write_sectors)(MVHDMeta*, uint32_t, int, void*);
struct {
uint8_t* zero_data;
int sector_count;
} format_buffer;
};
#endif

View File

@@ -1,28 +1,60 @@
/**
* \file
* \brief Sector reading and writing implementations
/*
* MiniVHD Minimalist VHD implementation in C.
*
* This file is part of the MiniVHD Project.
*
* Sector reading and writing implementations.
*
* Version: @(#)io.c 1.0.3 2021/04/16
*
* Author: Sherman Perry, <shermperry@gmail.com>
*
* Copyright 2019-2021 Sherman Perry.
*
* MIT License
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documenta-
* tion files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall
* be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF O R IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef _FILE_OFFSET_BITS
#define _FILE_OFFSET_BITS 64
# define _FILE_OFFSET_BITS 64
#endif
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "minivhd_internal.h"
#include "minivhd_util.h"
#include "minivhd.h"
#include "internal.h"
/* The following bit array macros adapted from
http://www.mathcs.emory.edu/~cheung/Courses/255/Syllabus/1-C-intro/bit-array.html */
#define VHD_SETBIT(A,k) ( A[(k/8)] |= (0x80 >> (k%8)) )
#define VHD_CLEARBIT(A,k) ( A[(k/8)] &= ~(0x80 >> (k%8)) )
#define VHD_TESTBIT(A,k) ( A[(k/8)] & (0x80 >> (k%8)) )
/*
* The following bit array macros adapted from:
*
* http://www.mathcs.emory.edu/~cheung/Courses/255/Syllabus/1-C-intro/bit-array.html
*/
#define VHD_SETBIT(A,k) ( A[(k>>3)] |= (0x80 >> (k&7)) )
#define VHD_CLEARBIT(A,k) ( A[(k>>3)] &= ~(0x80 >> (k&7)) )
#define VHD_TESTBIT(A,k) ( A[(k>>3)] & (0x80 >> (k&7)) )
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);
static void mvhd_write_curr_sect_bitmap(MVHDMeta* vhdm);
/**
* \brief Check that we will not be overflowing buffers
@@ -34,22 +66,30 @@ 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(uint32_t offset, int num_sectors, uint32_t total_sectors, int* transfer_sect, int* trunc_sect) {
static inline void
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) < (uint32_t)*transfer_sect) {
*transfer_sect = total_sectors - offset;
*trunc_sect = num_sectors - *transfer_sect;
}
}
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);
}
}
/**
* \brief Read the sector bitmap for a block.
*
@@ -59,22 +99,28 @@ void mvhd_write_empty_sectors(FILE* f, int sector_count) {
* \param [in] vhdm MiniVHD data structure
* \param [in] blk The block for which to read the sector bitmap from
*/
static void mvhd_read_sect_bitmap(MVHDMeta* vhdm, int blk) {
static void
read_sect_bitmap(MVHDMeta* vhdm, int blk)
{
if (vhdm->block_offset[blk] != MVHD_SPARSE_BLK) {
mvhd_fseeko64(vhdm->f, (uint64_t)vhdm->block_offset[blk] * MVHD_SECTOR_SIZE, SEEK_SET);
(void) !fread(vhdm->bitmap.curr_bitmap, vhdm->bitmap.sector_count * MVHD_SECTOR_SIZE, 1, vhdm->f);
} else {
memset(vhdm->bitmap.curr_bitmap, 0, vhdm->bitmap.sector_count * MVHD_SECTOR_SIZE);
}
vhdm->bitmap.curr_block = blk;
}
/**
* \brief Write the current sector bitmap in memory to file
*
* \param [in] vhdm MiniVHD data structure
*/
static void mvhd_write_curr_sect_bitmap(MVHDMeta* vhdm) {
static void
write_curr_sect_bitmap(MVHDMeta* vhdm)
{
if (vhdm->bitmap.curr_block >= 0) {
int64_t abs_offset = (int64_t)vhdm->block_offset[vhdm->bitmap.curr_block] * MVHD_SECTOR_SIZE;
mvhd_fseeko64(vhdm->f, abs_offset, SEEK_SET);
@@ -82,19 +128,24 @@ static void mvhd_write_curr_sect_bitmap(MVHDMeta* vhdm) {
}
}
/**
* \brief Write block offset from memory into file
*
* \param [in] vhdm MiniVHD data structure
* \param [in] blk The block for which to write the offset for
*/
static void mvhd_write_bat_entry(MVHDMeta* vhdm, int blk) {
static void
write_bat_entry(MVHDMeta* vhdm, int blk)
{
uint64_t table_offset = vhdm->sparse.bat_offset + ((uint64_t)blk * sizeof *vhdm->block_offset);
uint32_t offset = mvhd_to_be32(vhdm->block_offset[blk]);
mvhd_fseeko64(vhdm->f, table_offset, SEEK_SET);
fwrite(&offset, sizeof offset, 1, vhdm->f);
}
/**
* \brief Create an empty block in a sparse or differencing VHD image
*
@@ -109,18 +160,23 @@ static void mvhd_write_bat_entry(MVHDMeta* vhdm, int blk) {
* \param [in] vhdm MiniVHD data structure
* \param [in] blk The block number to create
*/
static void mvhd_create_block(MVHDMeta* vhdm, int blk) {
static void
create_block(MVHDMeta* vhdm, int blk)
{
uint8_t footer[MVHD_FOOTER_SIZE];
/* Seek to where the footer SHOULD be */
mvhd_fseeko64(vhdm->f, -MVHD_FOOTER_SIZE, SEEK_END);
(void) !fread(footer, sizeof footer, 1, vhdm->f);
mvhd_fseeko64(vhdm->f, -MVHD_FOOTER_SIZE, SEEK_END);
if (!mvhd_is_conectix_str(footer)) {
/* Oh dear. We use the header instead, since something has gone wrong at the footer */
mvhd_fseeko64(vhdm->f, 0, SEEK_SET);
(void) !fread(footer, sizeof footer, 1, vhdm->f);
mvhd_fseeko64(vhdm->f, 0, SEEK_END);
}
int64_t abs_offset = mvhd_ftello64(vhdm->f);
if (abs_offset % MVHD_SECTOR_SIZE != 0) {
/* Yikes! We're supposed to be on a sector boundary. Add some padding */
@@ -131,52 +187,68 @@ static void mvhd_create_block(MVHDMeta* vhdm, int blk) {
}
abs_offset += padding_amount;
}
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... */
mvhd_write_empty_sectors(vhdm->f, 5);
/* And we finish with the footer */
fwrite(footer, sizeof footer, 1, vhdm->f);
/* We no longer have a sparse block. Update that BAT! */
vhdm->block_offset[blk] = sect_offset;
mvhd_write_bat_entry(vhdm, blk);
write_bat_entry(vhdm, blk);
}
int mvhd_fixed_read(MVHDMeta* vhdm, uint32_t 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;
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);
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);
(void) !fread(out_buff, transfer_sectors*MVHD_SECTOR_SIZE, 1, vhdm->f);
return truncated_sectors;
}
int mvhd_sparse_read(MVHDMeta* vhdm, uint32_t 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;
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);
check_sectors(offset, num_sectors, total_sectors, &transfer_sectors, &truncated_sectors);
uint8_t* buff = (uint8_t*)out_buff;
int64_t addr;
uint32_t s, ls;
int blk, prev_blk, sib;
ls = offset + transfer_sectors;
prev_blk = -1;
for (s = offset; s < ls; s++) {
blk = s / vhdm->sect_per_block;
sib = s % vhdm->sect_per_block;
if (blk != prev_blk) {
prev_blk = blk;
if (vhdm->bitmap.curr_block != blk) {
mvhd_read_sect_bitmap(vhdm, blk);
read_sect_bitmap(vhdm, blk);
mvhd_fseeko64(vhdm->f, (uint64_t)sib * MVHD_SECTOR_SIZE, SEEK_CUR);
} else {
addr = ((int64_t)vhdm->block_offset[blk] + vhdm->bitmap.sector_count + sib) * MVHD_SECTOR_SIZE;
mvhd_fseeko64(vhdm->f, addr, SEEK_SET);
}
}
if (VHD_TESTBIT(vhdm->bitmap.curr_bitmap, sib)) {
(void) !fread(buff, MVHD_SECTOR_SIZE, 1, vhdm->f);
} else {
@@ -185,29 +257,37 @@ int mvhd_sparse_read(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out
}
buff += MVHD_SECTOR_SIZE;
}
return truncated_sectors;
}
int mvhd_diff_read(MVHDMeta* vhdm, uint32_t 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;
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);
check_sectors(offset, num_sectors, total_sectors, &transfer_sectors, &truncated_sectors);
uint8_t* buff = (uint8_t*)out_buff;
MVHDMeta* curr_vhdm = vhdm;
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) {
blk = s / curr_vhdm->sect_per_block;
sib = s % curr_vhdm->sect_per_block;
if (curr_vhdm->bitmap.curr_block != blk) {
mvhd_read_sect_bitmap(curr_vhdm, blk);
read_sect_bitmap(curr_vhdm, blk);
}
if (!VHD_TESTBIT(curr_vhdm->bitmap.curr_bitmap, sib)) {
curr_vhdm = curr_vhdm->parent;
} else { break; }
}
/* We handle actual sector reading using the fixed or sparse functions,
as a differencing VHD is also a sparse VHD */
if (curr_vhdm->footer.disk_type == MVHD_TYPE_DIFF || curr_vhdm->footer.disk_type == MVHD_TYPE_DYNAMIC) {
@@ -215,49 +295,65 @@ int mvhd_diff_read(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_b
} else {
mvhd_fixed_read(curr_vhdm, s, 1, buff);
}
curr_vhdm = vhdm;
buff += MVHD_SECTOR_SIZE;
}
return truncated_sectors;
}
int mvhd_fixed_write(MVHDMeta* vhdm, uint32_t 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;
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);
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);
fwrite(in_buff, transfer_sectors*MVHD_SECTOR_SIZE, 1, vhdm->f);
return truncated_sectors;
}
int mvhd_sparse_diff_write(MVHDMeta* vhdm, uint32_t 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;
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);
check_sectors(offset, num_sectors, total_sectors, &transfer_sectors, &truncated_sectors);
uint8_t* buff = (uint8_t*)in_buff;
int64_t addr;
uint32_t s, ls;
int blk, prev_blk, sib;
ls = offset + transfer_sectors;
prev_blk = -1;
for (s = offset; s < ls; s++) {
blk = s / vhdm->sect_per_block;
sib = s % vhdm->sect_per_block;
if (vhdm->bitmap.curr_block != blk && prev_blk >= 0) {
/* Write the sector bitmap for the previous block, before we replace it. */
mvhd_write_curr_sect_bitmap(vhdm);
write_curr_sect_bitmap(vhdm);
}
if (vhdm->block_offset[blk] == MVHD_SPARSE_BLK) {
/* "read" the sector bitmap first, before creating a new block, as the bitmap will be
zero either way */
mvhd_read_sect_bitmap(vhdm, blk);
mvhd_create_block(vhdm, blk);
read_sect_bitmap(vhdm, blk);
create_block(vhdm, blk);
}
if (blk != prev_blk) {
if (vhdm->bitmap.curr_block != blk) {
mvhd_read_sect_bitmap(vhdm, blk);
read_sect_bitmap(vhdm, blk);
mvhd_fseeko64(vhdm->f, (uint64_t)sib * MVHD_SECTOR_SIZE, SEEK_CUR);
} else {
addr = ((int64_t)vhdm->block_offset[blk] + vhdm->bitmap.sector_count + sib) * MVHD_SECTOR_SIZE;
@@ -265,15 +361,26 @@ int mvhd_sparse_diff_write(MVHDMeta* vhdm, uint32_t offset, int num_sectors, voi
}
prev_blk = blk;
}
fwrite(buff, MVHD_SECTOR_SIZE, 1, vhdm->f);
VHD_SETBIT(vhdm->bitmap.curr_bitmap, sib);
buff += MVHD_SECTOR_SIZE;
}
/* And write the sector bitmap for the last block we visited to disk */
mvhd_write_curr_sect_bitmap(vhdm);
write_curr_sect_bitmap(vhdm);
return truncated_sectors;
}
int mvhd_noop_write(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff) {
int
mvhd_noop_write(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff)
{
(void)vhdm;
(void)offset;
(void)num_sectors;
(void)in_buff;
return 0;
}

View File

@@ -1,132 +0,0 @@
#ifndef MINIVHD_IO_H
#define MINIVHD_IO_H
#include "minivhd.h"
/**
* \brief Write zero filled sectors to file.
*
* Note, the caller should set the file position before calling this
* function for correct operation.
*
* \param [in] f File to write sectors to
* \param [in] sector_count The number of sectors to write
*/
void mvhd_write_empty_sectors(FILE* f, int sector_count);
/**
* \brief Read a fixed VHD image
*
* Fixed VHD images are essentially raw image files with a footer tacked on
* the end. They are therefore straightforward to write
*
* \param [in] vhdm MiniVHD data structure
* \param [in] offset Sector offset to read from
* \param [in] num_sectors The desired number of sectors to read
* \param [out] out_buff An output buffer to store read sectors. Must be
* large enough to hold num_sectors worth of sectors.
*
* \retval 0 num_sectors were read from file
* \retval >0 < num_sectors were read from file
*/
int mvhd_fixed_read(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff);
/**
* \brief Read a sparse VHD image
*
* Sparse, or dynamic images are VHD images that grow as data is written to them.
*
* This function implements the logic to read sectors from the file, taking into
* account the fact that blocks may be stored on disk in any order, and that the
* read could cross block boundaries.
*
* \param [in] vhdm MiniVHD data structure
* \param [in] offset Sector offset to read from
* \param [in] num_sectors The desired number of sectors to read
* \param [out] out_buff An output buffer to store read sectors. Must be
* large enough to hold num_sectors worth of sectors.
*
* \retval 0 num_sectors were read from file
* \retval >0 < num_sectors were read from file
*/
int mvhd_sparse_read(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff);
/**
* \brief Read a differencing VHD image
*
* Differencing images are a variant of a sparse image. They contain the grow-on-demand
* properties of sparse images, but also reference a parent image. Data is read from the
* child image only if it is newer than the data stored in the parent image.
*
* This function implements the logic to read sectors from the child, or a parent image.
* Differencing images may have a differencing image as a parent, creating a chain of images.
* There is no theoretical chain length limit, although I do not consider long chains to be
* advisable. Verifying the parent-child relationship is not very robust.
*
* \param [in] vhdm MiniVHD data structure
* \param [in] offset Sector offset to read from
* \param [in] num_sectors The desired number of sectors to read
* \param [out] out_buff An output buffer to store read sectors. Must be
* large enough to hold num_sectors worth of sectors.
*
* \retval 0 num_sectors were read from file
* \retval >0 < num_sectors were read from file
*/
int mvhd_diff_read(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff);
/**
* \brief Write to a fixed VHD image
*
* Fixed VHD images are essentially raw image files with a footer tacked on
* the end. They are therefore straightforward to write
*
* \param [in] vhdm MiniVHD data structure
* \param [in] offset Sector offset to write to
* \param [in] num_sectors The desired number of sectors to write
* \param [in] in_buff A source buffer to write sectors from. Must be
* large enough to hold num_sectors worth of sectors.
*
* \retval 0 num_sectors were written to file
* \retval >0 < num_sectors were written to file
*/
int mvhd_fixed_write(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff);
/**
* \brief Write to a sparse or differencing VHD image
*
* Sparse, or dynamic images are VHD images that grow as data is written to them.
*
* Differencing images are a variant of a sparse image. They contain the grow-on-demand
* properties of sparse images, but also reference a parent image. Data is always written
* to the child image. This makes writing to differencing images essentially identical to
* writing to sparse images, hence they use the same function.
*
* This function implements the logic to write sectors to the file, taking into
* account the fact that blocks may be stored on disk in any order, and that the
* write operation could cross block boundaries.
*
* \param [in] vhdm MiniVHD data structure
* \param [in] offset Sector offset to write to
* \param [in] num_sectors The desired number of sectors to write
* \param [in] in_buff A source buffer to write sectors from. Must be
* large enough to hold num_sectors worth of sectors.
*
* \retval 0 num_sectors were written to file
* \retval >0 < num_sectors were written to file
*/
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
*
* \param [in] vhdm MiniVHD data structure
* \param [in] offset Sector offset to write to
* \param [in] num_sectors The desired number of sectors to write
* \param [in] in_buff A source buffer to write sectors from. Must be
* large enough to hold num_sectors worth of sectors.
*
* \retval 0 num_sectors were written to file
* \retval >0 < num_sectors were written to file
*/
int mvhd_noop_write(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff);
#endif

View File

@@ -1,167 +0,0 @@
/**
* \file
* \brief Header and footer serialize/deserialize functions
*/
#ifndef _FILE_OFFSET_BITS
#define _FILE_OFFSET_BITS 64
#endif
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include "minivhd_util.h"
#include "minivhd_internal.h"
/* Read data from footer into the struct members, swapping endian where necessary
Note: order matters here! We must read each field in the order the struct is in.
Doing this may be less elegant than performing a memcpy to a packed struct, but
it avoids potential data alignment issues, and the endian swapping allows us to
use the fields directly. */
static void mvhd_next_buffer_to_struct(void* struct_memb, size_t memb_size, bool req_endian, uint8_t** buffer);
static void mvhd_next_struct_to_buffer(void* struct_memb, size_t memb_size, bool req_endian, uint8_t** buffer);
/**
* \brief Get the next field from a buffer and store it in a struct member, converting endian if necessary
*
* \param [out] struct_memb struct member to save the field to
* \param [in] memb_size the size of struct_memb, in bytes
* \param [in] req_endian is the field a value that requires endian conversion (eg: uint16, uint32)
* \param [in] buffer the buffer from which fields are read from. Will be advanced at the end of the function call
*/
static void mvhd_next_buffer_to_struct(void* struct_memb, size_t memb_size, bool req_endian, uint8_t** buffer) {
memcpy(struct_memb, *buffer, memb_size);
if (req_endian) {
switch (memb_size) {
case 2:
*(uint16_t*)(struct_memb) = mvhd_from_be16(*(uint16_t*)(struct_memb));
break;
case 4:
*(uint32_t*)(struct_memb) = mvhd_from_be32(*(uint32_t*)(struct_memb));
break;
case 8:
*(uint64_t*)(struct_memb) = mvhd_from_be64(*(uint64_t*)(struct_memb));
break;
}
}
*buffer += memb_size;
}
/**
* \brief Save a struct member into a buffer, converting endian if necessary
*
* \param [in] struct_memb struct member read from
* \param [in] memb_size the size of struct_memb, in bytes
* \param [in] req_endian is the field a value that requires endian conversion (eg: uint16, uint32)
* \param [out] buffer the buffer from which struct member is saved to. Will be advanced at the end of the function call
*/
static void mvhd_next_struct_to_buffer(void* struct_memb, size_t memb_size, bool req_endian, uint8_t** buffer) {
uint8_t *buf_ptr = *buffer;
memcpy(buf_ptr, struct_memb, memb_size);
if (req_endian) {
switch (memb_size) {
case 2:
*((uint16_t*)buf_ptr) = mvhd_to_be16(*(uint16_t*)(struct_memb));
break;
case 4:
*((uint32_t*)buf_ptr) = mvhd_to_be32(*(uint32_t*)(struct_memb));
break;
case 8:
*((uint64_t*)buf_ptr) = mvhd_to_be64(*(uint64_t*)(struct_memb));
break;
}
}
buf_ptr += memb_size;
*buffer = buf_ptr;
}
void mvhd_buffer_to_footer(MVHDFooter* footer, uint8_t* buffer) {
uint8_t* buff_ptr = buffer;
mvhd_next_buffer_to_struct(&footer->cookie, sizeof footer->cookie, false, &buff_ptr);
mvhd_next_buffer_to_struct(&footer->features, sizeof footer->features, true, &buff_ptr);
mvhd_next_buffer_to_struct(&footer->fi_fmt_vers, sizeof footer->fi_fmt_vers, true, &buff_ptr);
mvhd_next_buffer_to_struct(&footer->data_offset, sizeof footer->data_offset, true, &buff_ptr);
mvhd_next_buffer_to_struct(&footer->timestamp, sizeof footer->timestamp, true, &buff_ptr);
mvhd_next_buffer_to_struct(&footer->cr_app, sizeof footer->cr_app, false, &buff_ptr);
mvhd_next_buffer_to_struct(&footer->cr_vers, sizeof footer->cr_vers, true, &buff_ptr);
mvhd_next_buffer_to_struct(&footer->cr_host_os, sizeof footer->cr_host_os, false, &buff_ptr);
mvhd_next_buffer_to_struct(&footer->orig_sz, sizeof footer->orig_sz, true, &buff_ptr);
mvhd_next_buffer_to_struct(&footer->curr_sz, sizeof footer->curr_sz, true, &buff_ptr);
mvhd_next_buffer_to_struct(&footer->geom.cyl, sizeof footer->geom.cyl, true, &buff_ptr);
mvhd_next_buffer_to_struct(&footer->geom.heads, sizeof footer->geom.heads, false, &buff_ptr);
mvhd_next_buffer_to_struct(&footer->geom.spt, sizeof footer->geom.spt, false, &buff_ptr);
mvhd_next_buffer_to_struct(&footer->disk_type, sizeof footer->disk_type, true, &buff_ptr);
mvhd_next_buffer_to_struct(&footer->checksum, sizeof footer->checksum, true, &buff_ptr);
mvhd_next_buffer_to_struct(&footer->uuid, sizeof footer->uuid, false, &buff_ptr);
mvhd_next_buffer_to_struct(&footer->saved_st, sizeof footer->saved_st, false, &buff_ptr);
mvhd_next_buffer_to_struct(&footer->reserved, sizeof footer->reserved, false, &buff_ptr);
}
void mvhd_footer_to_buffer(MVHDFooter* footer, uint8_t* buffer) {
uint8_t* buff_ptr = buffer;
mvhd_next_struct_to_buffer(&footer->cookie, sizeof footer->cookie, false, &buff_ptr);
mvhd_next_struct_to_buffer(&footer->features, sizeof footer->features, true, &buff_ptr);
mvhd_next_struct_to_buffer(&footer->fi_fmt_vers, sizeof footer->fi_fmt_vers, true, &buff_ptr);
mvhd_next_struct_to_buffer(&footer->data_offset, sizeof footer->data_offset, true, &buff_ptr);
mvhd_next_struct_to_buffer(&footer->timestamp, sizeof footer->timestamp, true, &buff_ptr);
mvhd_next_struct_to_buffer(&footer->cr_app, sizeof footer->cr_app, false, &buff_ptr);
mvhd_next_struct_to_buffer(&footer->cr_vers, sizeof footer->cr_vers, true, &buff_ptr);
mvhd_next_struct_to_buffer(&footer->cr_host_os, sizeof footer->cr_host_os, false, &buff_ptr);
mvhd_next_struct_to_buffer(&footer->orig_sz, sizeof footer->orig_sz, true, &buff_ptr);
mvhd_next_struct_to_buffer(&footer->curr_sz, sizeof footer->curr_sz, true, &buff_ptr);
mvhd_next_struct_to_buffer(&footer->geom.cyl, sizeof footer->geom.cyl, true, &buff_ptr);
mvhd_next_struct_to_buffer(&footer->geom.heads, sizeof footer->geom.heads, false, &buff_ptr);
mvhd_next_struct_to_buffer(&footer->geom.spt, sizeof footer->geom.spt, false, &buff_ptr);
mvhd_next_struct_to_buffer(&footer->disk_type, sizeof footer->disk_type, true, &buff_ptr);
mvhd_next_struct_to_buffer(&footer->checksum, sizeof footer->checksum, true, &buff_ptr);
mvhd_next_struct_to_buffer(&footer->uuid, sizeof footer->uuid, false, &buff_ptr);
mvhd_next_struct_to_buffer(&footer->saved_st, sizeof footer->saved_st, false, &buff_ptr);
mvhd_next_struct_to_buffer(&footer->reserved, sizeof footer->reserved, false, &buff_ptr);
}
void mvhd_buffer_to_header(MVHDSparseHeader* header, uint8_t* buffer) {
uint8_t* buff_ptr = buffer;
mvhd_next_buffer_to_struct(&header->cookie, sizeof header->cookie, false, &buff_ptr);
mvhd_next_buffer_to_struct(&header->data_offset, sizeof header->data_offset, true, &buff_ptr);
mvhd_next_buffer_to_struct(&header->bat_offset, sizeof header->bat_offset, true, &buff_ptr);
mvhd_next_buffer_to_struct(&header->head_vers, sizeof header->head_vers, true, &buff_ptr);
mvhd_next_buffer_to_struct(&header->max_bat_ent, sizeof header->max_bat_ent, true, &buff_ptr);
mvhd_next_buffer_to_struct(&header->block_sz, sizeof header->block_sz, true, &buff_ptr);
mvhd_next_buffer_to_struct(&header->checksum, sizeof header->checksum, true, &buff_ptr);
mvhd_next_buffer_to_struct(&header->par_uuid, sizeof header->par_uuid, false, &buff_ptr);
mvhd_next_buffer_to_struct(&header->par_timestamp, sizeof header->par_timestamp, true, &buff_ptr);
mvhd_next_buffer_to_struct(&header->reserved_1, sizeof header->reserved_1, true, &buff_ptr);
mvhd_next_buffer_to_struct(&header->par_utf16_name, sizeof header->par_utf16_name, false, &buff_ptr);
for (int i = 0; i < 8; i++) {
mvhd_next_buffer_to_struct(&header->par_loc_entry[i].plat_code, sizeof header->par_loc_entry[i].plat_code, true, &buff_ptr);
mvhd_next_buffer_to_struct(&header->par_loc_entry[i].plat_data_space, sizeof header->par_loc_entry[i].plat_data_space, true, &buff_ptr);
mvhd_next_buffer_to_struct(&header->par_loc_entry[i].plat_data_len, sizeof header->par_loc_entry[i].plat_data_len, true, &buff_ptr);
mvhd_next_buffer_to_struct(&header->par_loc_entry[i].reserved, sizeof header->par_loc_entry[i].reserved, true, &buff_ptr);
mvhd_next_buffer_to_struct(&header->par_loc_entry[i].plat_data_offset, sizeof header->par_loc_entry[i].plat_data_offset, true, &buff_ptr);
}
mvhd_next_buffer_to_struct(&header->reserved_2, sizeof header->reserved_2, false, &buff_ptr);
}
void mvhd_header_to_buffer(MVHDSparseHeader* header, uint8_t* buffer) {
uint8_t* buff_ptr = buffer;
mvhd_next_struct_to_buffer(&header->cookie, sizeof header->cookie, false, &buff_ptr);
mvhd_next_struct_to_buffer(&header->data_offset, sizeof header->data_offset, true, &buff_ptr);
mvhd_next_struct_to_buffer(&header->bat_offset, sizeof header->bat_offset, true, &buff_ptr);
mvhd_next_struct_to_buffer(&header->head_vers, sizeof header->head_vers, true, &buff_ptr);
mvhd_next_struct_to_buffer(&header->max_bat_ent, sizeof header->max_bat_ent, true, &buff_ptr);
mvhd_next_struct_to_buffer(&header->block_sz, sizeof header->block_sz, true, &buff_ptr);
mvhd_next_struct_to_buffer(&header->checksum, sizeof header->checksum, true, &buff_ptr);
mvhd_next_struct_to_buffer(&header->par_uuid, sizeof header->par_uuid, false, &buff_ptr);
mvhd_next_struct_to_buffer(&header->par_timestamp, sizeof header->par_timestamp, true, &buff_ptr);
mvhd_next_struct_to_buffer(&header->reserved_1, sizeof header->reserved_1, true, &buff_ptr);
mvhd_next_struct_to_buffer(&header->par_utf16_name, sizeof header->par_utf16_name, false, &buff_ptr);
for (int i = 0; i < 8; i++) {
mvhd_next_struct_to_buffer(&header->par_loc_entry[i].plat_code, sizeof header->par_loc_entry[i].plat_code, true, &buff_ptr);
mvhd_next_struct_to_buffer(&header->par_loc_entry[i].plat_data_space, sizeof header->par_loc_entry[i].plat_data_space, true, &buff_ptr);
mvhd_next_struct_to_buffer(&header->par_loc_entry[i].plat_data_len, sizeof header->par_loc_entry[i].plat_data_len, true, &buff_ptr);
mvhd_next_struct_to_buffer(&header->par_loc_entry[i].reserved, sizeof header->par_loc_entry[i].reserved, true, &buff_ptr);
mvhd_next_struct_to_buffer(&header->par_loc_entry[i].plat_data_offset, sizeof header->par_loc_entry[i].plat_data_offset, true, &buff_ptr);
}
mvhd_next_struct_to_buffer(&header->reserved_2, sizeof header->reserved_2, false, &buff_ptr);
}

View File

@@ -1,38 +0,0 @@
#ifndef MINIVHD_STRUCT_RW_H
#define MINIVHD_STRUCT_RW_H
#include "minivhd_internal.h"
/**
* \brief Save the contents of a VHD footer from a buffer to a struct
*
* \param [out] footer save contents of buffer into footer
* \param [in] buffer VHD footer in raw bytes
*/
void mvhd_buffer_to_footer(MVHDFooter* footer, uint8_t* buffer);
/**
* \brief Save the contents of a VHD sparse header from a buffer to a struct
*
* \param [out] header save contents of buffer into header
* \param [in] buffer VHD header in raw bytes
*/
void mvhd_buffer_to_header(MVHDSparseHeader* header, uint8_t* buffer);
/**
* \brief Save the contents of a VHD footer struct to a buffer
*
* \param [in] footer save contents of struct into buffer
* \param [out] buffer VHD footer in raw bytes
*/
void mvhd_footer_to_buffer(MVHDFooter* footer, uint8_t* buffer);
/**
* \brief Save the contents of a VHD sparse header struct to a buffer
*
* \param [in] header save contents of struct into buffer
* \param [out] buffer VHD sparse header in raw bytes
*/
void mvhd_header_to_buffer(MVHDSparseHeader* header, uint8_t* buffer);
#endif

View File

@@ -1,46 +1,90 @@
/**
* \file
* \brief Utility functions
/*
* MiniVHD Minimalist VHD implementation in C.
*
* This file is part of the MiniVHD Project.
*
* Utility functions.
*
* Version: @(#)util.c 1.0.4 2021/04/16
*
* Author: Sherman Perry, <shermperry@gmail.com>
*
* Copyright 2019-2021 Sherman Perry.
*
* MIT License
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documenta-
* tion files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall
* be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF O R IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef _FILE_OFFSET_BITS
#define _FILE_OFFSET_BITS 64
# define _FILE_OFFSET_BITS 64
#endif
#include <errno.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "libxml2_encoding.h"
#include "minivhd_internal.h"
#include "minivhd_util.h"
#include "minivhd.h"
#include "internal.h"
#include "xml2_encoding.h"
const char MVHD_CONECTIX_COOKIE[] = "conectix";
const char MVHD_CREATOR[] = "pcem";
const char MVHD_CREATOR_HOST_OS[] = "Wi2k";
const char MVHD_CXSPARSE_COOKIE[] = "cxsparse";
uint16_t mvhd_from_be16(uint16_t val) {
uint16_t
mvhd_from_be16(uint16_t val)
{
uint8_t *tmp = (uint8_t*)&val;
uint16_t ret = 0;
ret |= (uint16_t)tmp[0] << 8;
ret |= (uint16_t)tmp[1] << 0;
return ret;
}
uint32_t mvhd_from_be32(uint32_t val) {
uint32_t
mvhd_from_be32(uint32_t val)
{
uint8_t *tmp = (uint8_t*)&val;
uint32_t ret = 0;
ret = (uint32_t)tmp[0] << 24;
ret |= (uint32_t)tmp[1] << 16;
ret |= (uint32_t)tmp[2] << 8;
ret |= (uint32_t)tmp[3] << 0;
return ret;
}
uint64_t mvhd_from_be64(uint64_t val) {
uint64_t
mvhd_from_be64(uint64_t val)
{
uint8_t *tmp = (uint8_t*)&val;
uint64_t ret = 0;
ret = (uint64_t)tmp[0] << 56;
ret |= (uint64_t)tmp[1] << 48;
ret |= (uint64_t)tmp[2] << 40;
@@ -49,27 +93,45 @@ uint64_t mvhd_from_be64(uint64_t val) {
ret |= (uint64_t)tmp[5] << 16;
ret |= (uint64_t)tmp[6] << 8;
ret |= (uint64_t)tmp[7] << 0;
return ret;
}
uint16_t mvhd_to_be16(uint16_t val) {
uint16_t
mvhd_to_be16(uint16_t val)
{
uint16_t ret = 0;
uint8_t *tmp = (uint8_t*)&ret;
tmp[0] = (val & 0xff00) >> 8;
tmp[1] = (val & 0x00ff) >> 0;
return ret;
}
uint32_t mvhd_to_be32(uint32_t val) {
uint32_t
mvhd_to_be32(uint32_t val)
{
uint32_t ret = 0;
uint8_t *tmp = (uint8_t*)&ret;
tmp[0] = (val & 0xff000000) >> 24;
tmp[1] = (val & 0x00ff0000) >> 16;
tmp[2] = (val & 0x0000ff00) >> 8;
tmp[3] = (val & 0x000000ff) >> 0;
return ret;
}
uint64_t mvhd_to_be64(uint64_t val) {
uint64_t
mvhd_to_be64(uint64_t val)
{
uint64_t ret = 0;
uint8_t *tmp = (uint8_t*)&ret;
tmp[0] = (uint8_t)((val & 0xff00000000000000) >> 56);
tmp[1] = (uint8_t)((val & 0x00ff000000000000) >> 48);
tmp[2] = (uint8_t)((val & 0x0000ff0000000000) >> 40);
@@ -78,21 +140,17 @@ uint64_t mvhd_to_be64(uint64_t val) {
tmp[5] = (uint8_t)((val & 0x0000000000ff0000) >> 16);
tmp[6] = (uint8_t)((val & 0x000000000000ff00) >> 8);
tmp[7] = (uint8_t)((val & 0x00000000000000ff) >> 0);
return ret;
}
bool mvhd_is_conectix_str(const void* buffer) {
if (strncmp(buffer, MVHD_CONECTIX_COOKIE, strlen(MVHD_CONECTIX_COOKIE)) == 0) {
return true;
} else {
return false;
}
}
void mvhd_generate_uuid(uint8_t* uuid)
void
mvhd_generate_uuid(uint8_t* uuid)
{
/* We aren't doing crypto here, so using system time as seed should be good enough */
srand((unsigned int)time(0));
for (int n = 0; n < 16; n++) {
uuid[n] = rand();
}
@@ -102,34 +160,50 @@ void mvhd_generate_uuid(uint8_t* uuid)
uuid[8] |= 0x80; /* Variant 1 */
}
uint32_t vhd_calc_timestamp(void)
{
time_t start_time;
time_t curr_time;
double vhd_time;
start_time = MVHD_START_TS; /* 1 Jan 2000 00:00 */
curr_time = time(NULL);
vhd_time = difftime(curr_time, start_time);
return (uint32_t)vhd_time;
}
uint32_t mvhd_epoch_to_vhd_ts(time_t ts) {
time_t start_time = MVHD_START_TS;
if (ts < start_time) {
return start_time;
}
double vhd_time = difftime(ts, start_time);
uint32_t
vhd_calc_timestamp(void)
{
time_t start_time;
time_t curr_time;
double vhd_time;
start_time = MVHD_START_TS; /* 1 Jan 2000 00:00 */
curr_time = time(NULL);
vhd_time = difftime(curr_time, start_time);
return (uint32_t)vhd_time;
}
time_t vhd_get_created_time(MVHDMeta *vhdm)
uint32_t
mvhd_epoch_to_vhd_ts(time_t ts)
{
time_t vhd_time = (time_t)vhdm->footer.timestamp;
time_t vhd_time_unix = MVHD_START_TS + vhd_time;
return vhd_time_unix;
time_t start_time = MVHD_START_TS;
double vhd_time;
if (ts < start_time)
return (uint32_t)start_time;
vhd_time = difftime(ts, start_time);
return (uint32_t)vhd_time;
}
FILE* mvhd_fopen(const char* path, const char* mode, int* err) {
time_t
vhd_get_created_time(MVHDMeta *vhdm)
{
time_t vhd_time = (time_t)vhdm->footer.timestamp;
time_t vhd_time_unix = MVHD_START_TS + vhd_time;
return vhd_time_unix;
}
FILE*
mvhd_fopen(const char* path, const char* mode, int* err)
{
FILE* f = NULL;
#ifdef _WIN32
size_t path_len = strlen(path);
@@ -140,6 +214,7 @@ FILE* mvhd_fopen(const char* path, const char* mode, int* err) {
int new_mode_len = (sizeof mode_str) - 2;
int path_res = UTF8ToUTF16LE((unsigned char*)new_path, &new_path_len, (const unsigned char*)path, (int*)&path_len);
int mode_res = UTF8ToUTF16LE((unsigned char*)mode_str, &new_mode_len, (const unsigned char*)mode, (int*)&mode_len);
if (path_res > 0 && mode_res > 0) {
f = _wfopen(new_path, mode_str);
if (f == NULL) {
@@ -160,10 +235,14 @@ FILE* mvhd_fopen(const char* path, const char* mode, int* err) {
*err = MVHD_ERR_FILE;
}
#endif
return f;
}
void mvhd_set_encoding_err(int encoding_retval, int* err) {
void
mvhd_set_encoding_err(int encoding_retval, int* err)
{
if (encoding_retval == -1) {
*err = MVHD_ERR_UTF_SIZE;
} else if (encoding_retval == -2) {
@@ -171,87 +250,162 @@ void mvhd_set_encoding_err(int encoding_retval, int* err) {
}
}
uint64_t mvhd_calc_size_bytes(MVHDGeom *geom) {
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
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;
}
MVHDGeom mvhd_get_geometry(MVHDMeta* vhdm) {
MVHDGeom geometry = { .cyl = vhdm->footer.geom.cyl, .heads = vhdm->footer.geom.heads, .spt = vhdm->footer.geom.spt };
MVHDAPI MVHDGeom
mvhd_get_geometry(MVHDMeta* vhdm)
{
MVHDGeom geometry = {
.cyl = vhdm->footer.geom.cyl,
.heads = vhdm->footer.geom.heads,
.spt = vhdm->footer.geom.spt
};
return geometry;
}
uint32_t mvhd_gen_footer_checksum(MVHDFooter* footer) {
MVHDAPI uint64_t
mvhd_get_current_size(MVHDMeta* vhdm)
{
return vhdm->footer.curr_sz;
}
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++) {
for (size_t i = 0; i < sizeof *footer; i++)
new_chk += footer_bytes[i];
}
footer->checksum = orig_chk;
return ~new_chk;
}
uint32_t mvhd_gen_sparse_checksum(MVHDSparseHeader* header) {
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;
}
const char* mvhd_strerr(MVHDError err) {
MVHDAPI const char*
mvhd_strerr(MVHDError err)
{
const char *s = "unknown error";
switch (err) {
case MVHD_ERR_MEM:
return "memory allocation error";
case MVHD_ERR_FILE:
return "file error";
case MVHD_ERR_NOT_VHD:
return "file is not a VHD image";
case MVHD_ERR_TYPE:
return "unsupported VHD image type";
case MVHD_ERR_FOOTER_CHECKSUM:
return "invalid VHD footer checksum";
case MVHD_ERR_SPARSE_CHECKSUM:
return "invalid VHD sparse header checksum";
case MVHD_ERR_UTF_TRANSCODING_FAILED:
return "error converting path encoding";
case MVHD_ERR_UTF_SIZE:
return "buffer size mismatch when converting path encoding";
case MVHD_ERR_PATH_REL:
return "relative path detected where absolute path expected";
case MVHD_ERR_PATH_LEN:
return "path length exceeds MVHD_MAX_PATH";
case MVHD_ERR_PAR_NOT_FOUND:
return "parent VHD image not found";
case MVHD_ERR_INVALID_PAR_UUID:
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:
return "error converting image. Size mismatch detechted";
default:
return "unknown error";
case MVHD_ERR_MEM:
s = "memory allocation error";
break;
case MVHD_ERR_FILE:
s = "file error";
break;
case MVHD_ERR_NOT_VHD:
s = "file is not a VHD image";
break;
case MVHD_ERR_TYPE:
s = "unsupported VHD image type";
break;
case MVHD_ERR_FOOTER_CHECKSUM:
s = "invalid VHD footer checksum";
break;
case MVHD_ERR_SPARSE_CHECKSUM:
s = "invalid VHD sparse header checksum";
break;
case MVHD_ERR_UTF_TRANSCODING_FAILED:
s = "error converting path encoding";
break;
case MVHD_ERR_UTF_SIZE:
s = "buffer size mismatch when converting path encoding";
break;
case MVHD_ERR_PATH_REL:
s = "relative path detected where absolute path expected";
break;
case MVHD_ERR_PATH_LEN:
s = "path length exceeds MVHD_MAX_PATH";
break;
case MVHD_ERR_PAR_NOT_FOUND:
s = "parent VHD image not found";
break;
case MVHD_ERR_INVALID_PAR_UUID:
s = "UUID mismatch between child and parent VHD";
break;
case MVHD_ERR_INVALID_GEOM:
s = "invalid geometry detected";
break;
case MVHD_ERR_INVALID_SIZE:
s = "invalid size";
break;
case MVHD_ERR_INVALID_BLOCK_SIZE:
s = "invalid block size";
break;
case MVHD_ERR_INVALID_PARAMS:
s = "invalid parameters passed to function";
break;
case MVHD_ERR_CONV_SIZE:
s = "error converting image. Size mismatch detected";
break;
default:
break;
}
return s;
}
int64_t mvhd_ftello64(FILE* stream)
int64_t
mvhd_ftello64(FILE* stream)
{
#ifdef _MSC_VER
return _ftelli64(stream);
@@ -262,7 +416,9 @@ int64_t mvhd_ftello64(FILE* stream)
#endif
}
int mvhd_fseeko64(FILE* stream, int64_t offset, int origin)
int
mvhd_fseeko64(FILE* stream, int64_t offset, int origin)
{
#ifdef _MSC_VER
return _fseeki64(stream, offset, origin);
@@ -273,17 +429,25 @@ int mvhd_fseeko64(FILE* stream, int64_t offset, int origin)
#endif
}
uint32_t mvhd_crc32_for_byte(uint32_t r) {
uint32_t
mvhd_crc32_for_byte(uint32_t r)
{
for (int j = 0; j < 8; ++j)
r = (r & 1 ? 0 : (uint32_t)0xEDB88320L) ^ r >> 1;
return r ^ (uint32_t)0xFF000000L;
}
uint32_t mvhd_crc32(const void* data, size_t n_bytes) {
uint32_t
mvhd_crc32(const void* data, size_t n_bytes)
{
static uint32_t table[0x100];
if (!*table)
for (size_t i = 0; i < 0x100; ++i)
table[i] = mvhd_crc32_for_byte(i);
table[i] = mvhd_crc32_for_byte((uint32_t)i);
uint32_t crc = 0;
for (size_t i = 0; i < n_bytes; ++i)
@@ -292,7 +456,10 @@ uint32_t mvhd_crc32(const void* data, size_t n_bytes) {
return crc;
}
uint32_t mvhd_file_mod_timestamp(const char* path, int *err) {
uint32_t
mvhd_file_mod_timestamp(const char* path, int *err)
{
*err = 0;
#ifdef _WIN32
struct _stat file_stat;
@@ -300,6 +467,7 @@ uint32_t mvhd_file_mod_timestamp(const char* path, int *err) {
mvhd_utf16 new_path[260] = {0};
int new_path_len = (sizeof new_path) - 2;
int path_res = UTF8ToUTF16LE((unsigned char*)new_path, &new_path_len, (const unsigned char*)path, (int*)&path_len);
if (path_res > 0) {
int stat_res = _wstat(new_path, &file_stat);
if (stat_res != 0) {
@@ -319,6 +487,7 @@ uint32_t mvhd_file_mod_timestamp(const char* path, int *err) {
#else
struct stat file_stat;
int stat_res = stat(path, &file_stat);
if (stat_res != 0) {
mvhd_errno = errno;
*err = MVHD_ERR_FILE;

View File

@@ -1,136 +0,0 @@
#ifndef MINIVHD_UTIL_H
#define MINIVHD_UTIL_H
#include <stdint.h>
#include <stdio.h>
#include <time.h>
#include "minivhd_internal.h"
#include "minivhd.h"
#define MVHD_START_TS 946684800
/**
* Functions to deal with endian issues
*/
uint16_t mvhd_from_be16(uint16_t val);
uint32_t mvhd_from_be32(uint32_t val);
uint64_t mvhd_from_be64(uint64_t val);
uint16_t mvhd_to_be16(uint16_t val);
uint32_t mvhd_to_be32(uint32_t val);
uint64_t mvhd_to_be64(uint64_t val);
/**
* \brief Check if provided buffer begins with the string "conectix"
*
* \param [in] buffer The buffer to compare. Must be at least 8 bytes in length
*
* \return true if the buffer begins with "conectix"
* \return false if the buffer does not begin with "conectix"
*/
bool mvhd_is_conectix_str(const void* buffer);
/**
* \brief Generate a raw 16 byte UUID
*
* \param [out] uuid A 16 byte buffer in which the generated UUID will be stored to
*/
void mvhd_generate_uuid(uint8_t *uuid);
/**
* \brief Calculate a VHD formatted timestamp from the current time
*/
uint32_t vhd_calc_timestamp(void);
/**
* \brief Convert an epoch timestamp to a VHD timestamp
*
* \param [in] ts epoch timestamp to convert.
*
* \return The adjusted timestamp, or 0 if the input timestamp is
* earlier that 1 Janurary 2000
*/
uint32_t mvhd_epoch_to_vhd_ts(time_t ts);
/**
* \brief Return the created time from a VHD image
*
* \param [in] vhdm Pointer to the MiniVHD metadata structure
*
* \return The created time, as a Unix timestamp
*/
time_t vhd_get_created_time(MVHDMeta *vhdm);
/**
* \brief Cross platform, unicode filepath opening
*
* This function accounts for the fact that fopen() handles file paths differently compared to other
* operating systems. Windows version of fopen() will not handle multi byte encoded text like UTF-8.
*
* Unicode filepath support on Windows requires using the _wfopen() function, which expects UTF-16LE
* encoded path and modestring.
*
* \param [in] path The filepath to open as a UTF-8 string
* \param [in] mode The mode string to use (eg: "rb+"")
* \param [out] err The error value, if an error occurrs
*
* \return a FILE pointer if successful, NULL otherwise. If NULL, check the value of err
*/
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);
MVHDGeom mvhd_get_geometry(MVHDMeta* vhdm);
/**
* \brief Generate VHD footer checksum
*
* \param [in] vhdm MiniVHD data structure
*/
uint32_t mvhd_gen_footer_checksum(MVHDFooter* footer);
/**
* \brief Generate VHD sparse header checksum
*
* \param [in] vhdm MiniVHD data structure
*/
uint32_t mvhd_gen_sparse_checksum(MVHDSparseHeader* header);
/**
* \brief Get current position in file stream
*
* This is a portable version of the POSIX ftello64(). *
*/
int64_t mvhd_ftello64(FILE* stream);
/**
* \brief Reposition the file stream's position
*
* This is a portable version of the POSIX fseeko64(). *
*/
int mvhd_fseeko64(FILE* stream, int64_t offset, int origin);
/**
* \brief Calculate the CRC32 of a data buffer.
*
* This function can be used for verifying data integrity.
*
* \param [in] data The data buffer
* \param [in] n_bytes The size of the data buffer in bytes
*
* \return The CRC32 of the data buffer
*/
uint32_t mvhd_crc32(const void* data, size_t n_bytes);
/**
* \brief Calculate the file modification timestamp.
*
* This function is primarily to help protect differencing VHD's
*
* \param [in] path the UTF-8 file path
* \param [out] err The error value, if an error occurrs
*
* \return The file modified timestamp, in VHD compatible timestamp.
* 'err' will be set to non-zero on error
*/
uint32_t mvhd_file_mod_timestamp(const char* path, int *err);
#endif

View File

@@ -0,0 +1,231 @@
/*
* MiniVHD Minimalist VHD implementation in C.
*
* This file is part of the MiniVHD Project.
*
* Header and footer serialize/deserialize functions.
*
* Read data from footer into the struct members, swapping
* endian where necessary.
*
* NOTE: Order matters here!
* We must read each field in the order the struct is in.
* Doing this may be less elegant than performing a memcpy
* to a packed struct, but it avoids potential data alignment
* issues, and the endian swapping allows us to use the fields
* directly.
*
* Version: @(#)struct_rw.c 1.0.2 2021/04/16
*
* Author: Sherman Perry, <shermperry@gmail.com>
*
* Copyright 2019-2021 Sherman Perry.
*
* MIT License
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documenta-
* tion files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall
* be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF O R IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef _FILE_OFFSET_BITS
# define _FILE_OFFSET_BITS 64
#endif
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include "minivhd.h"
#include "internal.h"
/**
* \brief Get the next field from a buffer and store it in a struct member, converting endian if necessary
*
* \param [out] struct_memb struct member to save the field to
* \param [in] memb_size the size of struct_memb, in bytes
* \param [in] req_endian is the field a value that requires endian conversion (eg: uint16, uint32)
* \param [in] buffer the buffer from which fields are read from. Will be advanced at the end of the function call
*/
static void
next_buffer_to_struct(void* struct_memb, size_t memb_size, bool req_endian, uint8_t** buffer)
{
memcpy(struct_memb, *buffer, memb_size);
if (req_endian) switch (memb_size) {
case 2:
*(uint16_t*)(struct_memb) = mvhd_from_be16(*(uint16_t*)(struct_memb));
break;
case 4:
*(uint32_t*)(struct_memb) = mvhd_from_be32(*(uint32_t*)(struct_memb));
break;
case 8:
*(uint64_t*)(struct_memb) = mvhd_from_be64(*(uint64_t*)(struct_memb));
break;
}
*buffer += memb_size;
}
/**
* \brief Save a struct member into a buffer, converting endian if necessary
*
* \param [in] struct_memb struct member read from
* \param [in] memb_size the size of struct_memb, in bytes
* \param [in] req_endian is the field a value that requires endian conversion (eg: uint16, uint32)
* \param [out] buffer the buffer from which struct member is saved to. Will be advanced at the end of the function call
*/
static void
next_struct_to_buffer(void* struct_memb, size_t memb_size, bool req_endian, uint8_t** buffer)
{
uint8_t *buf_ptr = *buffer;
memcpy(buf_ptr, struct_memb, memb_size);
if (req_endian) switch (memb_size) {
case 2:
*((uint16_t*)buf_ptr) = mvhd_to_be16(*(uint16_t*)(struct_memb));
break;
case 4:
*((uint32_t*)buf_ptr) = mvhd_to_be32(*(uint32_t*)(struct_memb));
break;
case 8:
*((uint64_t*)buf_ptr) = mvhd_to_be64(*(uint64_t*)(struct_memb));
break;
}
buf_ptr += memb_size;
*buffer = buf_ptr;
}
void
mvhd_buffer_to_footer(MVHDFooter* footer, uint8_t* buffer)
{
uint8_t* buff_ptr = buffer;
next_buffer_to_struct(&footer->cookie, sizeof footer->cookie, false, &buff_ptr);
next_buffer_to_struct(&footer->features, sizeof footer->features, true, &buff_ptr);
next_buffer_to_struct(&footer->fi_fmt_vers, sizeof footer->fi_fmt_vers, true, &buff_ptr);
next_buffer_to_struct(&footer->data_offset, sizeof footer->data_offset, true, &buff_ptr);
next_buffer_to_struct(&footer->timestamp, sizeof footer->timestamp, true, &buff_ptr);
next_buffer_to_struct(&footer->cr_app, sizeof footer->cr_app, false, &buff_ptr);
next_buffer_to_struct(&footer->cr_vers, sizeof footer->cr_vers, true, &buff_ptr);
next_buffer_to_struct(&footer->cr_host_os, sizeof footer->cr_host_os, false, &buff_ptr);
next_buffer_to_struct(&footer->orig_sz, sizeof footer->orig_sz, true, &buff_ptr);
next_buffer_to_struct(&footer->curr_sz, sizeof footer->curr_sz, true, &buff_ptr);
next_buffer_to_struct(&footer->geom.cyl, sizeof footer->geom.cyl, true, &buff_ptr);
next_buffer_to_struct(&footer->geom.heads, sizeof footer->geom.heads, false, &buff_ptr);
next_buffer_to_struct(&footer->geom.spt, sizeof footer->geom.spt, false, &buff_ptr);
next_buffer_to_struct(&footer->disk_type, sizeof footer->disk_type, true, &buff_ptr);
next_buffer_to_struct(&footer->checksum, sizeof footer->checksum, true, &buff_ptr);
next_buffer_to_struct(&footer->uuid, sizeof footer->uuid, false, &buff_ptr);
next_buffer_to_struct(&footer->saved_st, sizeof footer->saved_st, false, &buff_ptr);
next_buffer_to_struct(&footer->reserved, sizeof footer->reserved, false, &buff_ptr);
}
void
mvhd_footer_to_buffer(MVHDFooter* footer, uint8_t* buffer)
{
uint8_t* buff_ptr = buffer;
next_struct_to_buffer(&footer->cookie, sizeof footer->cookie, false, &buff_ptr);
next_struct_to_buffer(&footer->features, sizeof footer->features, true, &buff_ptr);
next_struct_to_buffer(&footer->fi_fmt_vers, sizeof footer->fi_fmt_vers, true, &buff_ptr);
next_struct_to_buffer(&footer->data_offset, sizeof footer->data_offset, true, &buff_ptr);
next_struct_to_buffer(&footer->timestamp, sizeof footer->timestamp, true, &buff_ptr);
next_struct_to_buffer(&footer->cr_app, sizeof footer->cr_app, false, &buff_ptr);
next_struct_to_buffer(&footer->cr_vers, sizeof footer->cr_vers, true, &buff_ptr);
next_struct_to_buffer(&footer->cr_host_os, sizeof footer->cr_host_os, false, &buff_ptr);
next_struct_to_buffer(&footer->orig_sz, sizeof footer->orig_sz, true, &buff_ptr);
next_struct_to_buffer(&footer->curr_sz, sizeof footer->curr_sz, true, &buff_ptr);
next_struct_to_buffer(&footer->geom.cyl, sizeof footer->geom.cyl, true, &buff_ptr);
next_struct_to_buffer(&footer->geom.heads, sizeof footer->geom.heads, false, &buff_ptr);
next_struct_to_buffer(&footer->geom.spt, sizeof footer->geom.spt, false, &buff_ptr);
next_struct_to_buffer(&footer->disk_type, sizeof footer->disk_type, true, &buff_ptr);
next_struct_to_buffer(&footer->checksum, sizeof footer->checksum, true, &buff_ptr);
next_struct_to_buffer(&footer->uuid, sizeof footer->uuid, false, &buff_ptr);
next_struct_to_buffer(&footer->saved_st, sizeof footer->saved_st, false, &buff_ptr);
next_struct_to_buffer(&footer->reserved, sizeof footer->reserved, false, &buff_ptr);
}
void
mvhd_buffer_to_header(MVHDSparseHeader* header, uint8_t* buffer)
{
uint8_t* buff_ptr = buffer;
next_buffer_to_struct(&header->cookie, sizeof header->cookie, false, &buff_ptr);
next_buffer_to_struct(&header->data_offset, sizeof header->data_offset, true, &buff_ptr);
next_buffer_to_struct(&header->bat_offset, sizeof header->bat_offset, true, &buff_ptr);
next_buffer_to_struct(&header->head_vers, sizeof header->head_vers, true, &buff_ptr);
next_buffer_to_struct(&header->max_bat_ent, sizeof header->max_bat_ent, true, &buff_ptr);
next_buffer_to_struct(&header->block_sz, sizeof header->block_sz, true, &buff_ptr);
next_buffer_to_struct(&header->checksum, sizeof header->checksum, true, &buff_ptr);
next_buffer_to_struct(&header->par_uuid, sizeof header->par_uuid, false, &buff_ptr);
next_buffer_to_struct(&header->par_timestamp, sizeof header->par_timestamp, true, &buff_ptr);
next_buffer_to_struct(&header->reserved_1, sizeof header->reserved_1, true, &buff_ptr);
next_buffer_to_struct(&header->par_utf16_name, sizeof header->par_utf16_name, false, &buff_ptr);
for (int i = 0; i < 8; i++) {
next_buffer_to_struct(&header->par_loc_entry[i].plat_code, sizeof header->par_loc_entry[i].plat_code, true, &buff_ptr);
next_buffer_to_struct(&header->par_loc_entry[i].plat_data_space, sizeof header->par_loc_entry[i].plat_data_space, true, &buff_ptr);
next_buffer_to_struct(&header->par_loc_entry[i].plat_data_len, sizeof header->par_loc_entry[i].plat_data_len, true, &buff_ptr);
next_buffer_to_struct(&header->par_loc_entry[i].reserved, sizeof header->par_loc_entry[i].reserved, true, &buff_ptr);
next_buffer_to_struct(&header->par_loc_entry[i].plat_data_offset, sizeof header->par_loc_entry[i].plat_data_offset, true, &buff_ptr);
}
next_buffer_to_struct(&header->reserved_2, sizeof header->reserved_2, false, &buff_ptr);
}
void
mvhd_header_to_buffer(MVHDSparseHeader* header, uint8_t* buffer)
{
uint8_t* buff_ptr = buffer;
next_struct_to_buffer(&header->cookie, sizeof header->cookie, false, &buff_ptr);
next_struct_to_buffer(&header->data_offset, sizeof header->data_offset, true, &buff_ptr);
next_struct_to_buffer(&header->bat_offset, sizeof header->bat_offset, true, &buff_ptr);
next_struct_to_buffer(&header->head_vers, sizeof header->head_vers, true, &buff_ptr);
next_struct_to_buffer(&header->max_bat_ent, sizeof header->max_bat_ent, true, &buff_ptr);
next_struct_to_buffer(&header->block_sz, sizeof header->block_sz, true, &buff_ptr);
next_struct_to_buffer(&header->checksum, sizeof header->checksum, true, &buff_ptr);
next_struct_to_buffer(&header->par_uuid, sizeof header->par_uuid, false, &buff_ptr);
next_struct_to_buffer(&header->par_timestamp, sizeof header->par_timestamp, true, &buff_ptr);
next_struct_to_buffer(&header->reserved_1, sizeof header->reserved_1, true, &buff_ptr);
next_struct_to_buffer(&header->par_utf16_name, sizeof header->par_utf16_name, false, &buff_ptr);
for (int i = 0; i < 8; i++) {
next_struct_to_buffer(&header->par_loc_entry[i].plat_code, sizeof header->par_loc_entry[i].plat_code, true, &buff_ptr);
next_struct_to_buffer(&header->par_loc_entry[i].plat_data_space, sizeof header->par_loc_entry[i].plat_data_space, true, &buff_ptr);
next_struct_to_buffer(&header->par_loc_entry[i].plat_data_len, sizeof header->par_loc_entry[i].plat_data_len, true, &buff_ptr);
next_struct_to_buffer(&header->par_loc_entry[i].reserved, sizeof header->par_loc_entry[i].reserved, true, &buff_ptr);
next_struct_to_buffer(&header->par_loc_entry[i].plat_data_offset, sizeof header->par_loc_entry[i].plat_data_offset, true, &buff_ptr);
}
next_struct_to_buffer(&header->reserved_2, sizeof header->reserved_2, false, &buff_ptr);
}

View File

@@ -0,0 +1,68 @@
/*
* MiniVHD Minimalist VHD implementation in C.
*
* This file is part of the MiniVHD Project.
*
* Define library version and build info.
*
* Version: @(#)version.h 1.034 2021/04/16
*
* Author: Fred N. van Kempen, <waltje@varcem.com>
*
* Copyright 2021 Fred N. van Kempen.
*
* MIT License
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documenta-
* tion files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall
* be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF O R IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef MINIVHD_VERSION_H
# define MINIVHD_VERSION_H
/* Library name. */
#define LIB_NAME "MiniVHD"
/* Version info. */
#define LIB_VER_MAJOR 1
#define LIB_VER_MINOR 0
#define LIB_VER_REV 3
#define LIB_VER_PATCH 0
/* Standard C preprocessor macros. */
#define STR_STRING(x) #x
#define STR(x) STR_STRING(x)
#define STR_RC(a,e) a ## , ## e
/* These are used in the application. */
#define LIB_VER_NUM LIB_VER_MAJOR.LIB_VER_MINOR.LIB_VER_REV
#if defined(LIB_VER_PATCH) && LIB_VER_PATCH > 0
# define LIB_VER_NUM_4 LIB_VER_MAJOR.LIB_VER_MINOR.LIB_VER_REV.LIB_VER_PATCH
#else
# define LIB_VER_NUM_4 LIB_VER_MAJOR.LIB_VER_MINOR.LIB_VER_REV.0
#endif
#define LIB_VERSION STR(LIB_VER_NUM)
#define LIB_VERSION_4 STR(LIB_VER_NUM_4)
#endif /*MINIVHD_VERSION_H*/

View File

@@ -22,9 +22,18 @@
* Adapted and abridged for MiniVHD by Sherman Perry
*/
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#define BUILDING_LIBRARY
#include "minivhd.h"
#include "internal.h"
#include "xml2_encoding.h"
static int xmlLittleEndian = 1;
/* Note: extracted from original 'void xmlInitCharEncodingHandlers(void)' function */
void xmlEncodingInit(void)
{
@@ -96,8 +105,8 @@ int UTF16LEToUTF8(unsigned char* out, int *outlen,
c += 0x10000;
}
else {
*outlen = out - outstart;
*inlenb = processed - inb;
*outlen = (int)(out - outstart);
*inlenb = (int)(processed - inb);
return(-2);
}
}
@@ -117,8 +126,8 @@ int UTF16LEToUTF8(unsigned char* out, int *outlen,
}
processed = (const unsigned char*) in;
}
*outlen = out - outstart;
*inlenb = processed - inb;
*outlen = (int)(out - outstart);
*inlenb = (int)(processed - inb);
return(*outlen);
}
@@ -163,16 +172,16 @@ int UTF8ToUTF16LE(unsigned char* outb, int *outlen,
if (d < 0x80) { c= d; trailing= 0; }
else if (d < 0xC0) {
/* trailing byte in leading position */
*outlen = (out - outstart) * 2;
*inlen = processed - instart;
*outlen = (int)((out - outstart) * 2);
*inlen = (int)(processed - instart);
return(-2);
} else if (d < 0xE0) { c= d & 0x1F; trailing= 1; }
else if (d < 0xF0) { c= d & 0x0F; trailing= 2; }
else if (d < 0xF8) { c= d & 0x07; trailing= 3; }
else {
/* no chance for this in UTF-16 */
*outlen = (out - outstart) * 2;
*inlen = processed - instart;
*outlen = (int)((out - outstart) * 2);
*inlen = (int)(processed - instart);
return(-2);
}
@@ -225,8 +234,8 @@ int UTF8ToUTF16LE(unsigned char* outb, int *outlen,
break;
processed = in;
}
*outlen = (out - outstart) * 2;
*inlen = processed - instart;
*outlen = (int)((out - outstart) * 2);
*inlen = (int)(processed - instart);
return(*outlen);
}
@@ -275,8 +284,8 @@ int UTF16BEToUTF8(unsigned char* out, int *outlen,
}
if ((c & 0xFC00) == 0xD800) { /* surrogates */
if (in >= inend) { /* (in > inend) shouldn't happens */
*outlen = out - outstart;
*inlenb = processed - inb;
*outlen = (int)(out - outstart);
*inlenb = (int)(processed - inb);
return(-2);
}
if (xmlLittleEndian) {
@@ -295,8 +304,8 @@ int UTF16BEToUTF8(unsigned char* out, int *outlen,
c += 0x10000;
}
else {
*outlen = out - outstart;
*inlenb = processed - inb;
*outlen = (int)(out - outstart);
*inlenb = (int)(processed - inb);
return(-2);
}
}
@@ -316,8 +325,8 @@ int UTF16BEToUTF8(unsigned char* out, int *outlen,
}
processed = (const unsigned char*) in;
}
*outlen = out - outstart;
*inlenb = processed - inb;
*outlen = (int)(out - outstart);
*inlenb = (int)(processed - inb);
return(*outlen);
}
@@ -362,16 +371,16 @@ int UTF8ToUTF16BE(unsigned char* outb, int *outlen,
if (d < 0x80) { c= d; trailing= 0; }
else if (d < 0xC0) {
/* trailing byte in leading position */
*outlen = out - outstart;
*inlen = processed - instart;
*outlen = (int)(out - outstart);
*inlen = (int)(processed - instart);
return(-2);
} else if (d < 0xE0) { c= d & 0x1F; trailing= 1; }
else if (d < 0xF0) { c= d & 0x0F; trailing= 2; }
else if (d < 0xF8) { c= d & 0x07; trailing= 3; }
else {
/* no chance for this in UTF-16 */
*outlen = out - outstart;
*inlen = processed - instart;
*outlen = (int)(out - outstart);
*inlen = (int)(processed - instart);
return(-2);
}
@@ -421,8 +430,8 @@ int UTF8ToUTF16BE(unsigned char* outb, int *outlen,
break;
processed = in;
}
*outlen = (out - outstart) * 2;
*inlen = processed - instart;
*outlen = (int)((out - outstart) * 2);
*inlen = (int)(processed - instart);
return(*outlen);
}

View File

@@ -0,0 +1,62 @@
/*
* MiniVHD Minimalist VHD implementation in C.
*
* This file is part of the MiniVHD Project.
*
* Version: @(#)xml2_encoding.h 1.0.1 2021/03/15
*
* Author: Sherman Perry, <shermperry@gmail.com>
*
* Copyright 2019-2021 Sherman Perry.
*
* MIT License
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documenta-
* tion files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall
* be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF O R IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef XML2_ENCODING_H
# define XML2_ENCODING_H
typedef uint16_t mvhd_utf16;
#ifdef __cplusplus
extern "C" {
#endif
void xmlEncodingInit(void);
int UTF16LEToUTF8(uint8_t *out, int *outlen, const uint8_t *inb,
int *inlenb);
int UTF8ToUTF16LE(uint8_t *outb, int *outlen, const uint8_t *in,
int *inlen);
int UTF16BEToUTF8(uint8_t *out, int *outlen, const uint8_t *inb,
int *inlenb);
int UTF8ToUTF16BE(uint8_t *outb, int *outlen, const uint8_t *in,
int *inlen);
#ifdef __cplusplus
}
#endif
#endif /*XML2_ENCODING_H*/

View File

@@ -631,9 +631,8 @@ HDDOBJ := hdd.o \
hdc_ide_cmd640.o hdc_ide_cmd646.o \
hdc_ide_sff8038i.o
MINIVHDOBJ := cwalk.o libxml2_encoding.o minivhd_convert.o \
minivhd_create.o minivhd_io.o minivhd_manage.o \
minivhd_struct_rw.o minivhd_util.o
MINIVHDOBJ := cwalk.o xml2_encoding.o convert.o \
create.o minivhd_io.o manage.o struct_rw.o minivhd_util.o
CDROMOBJ := cdrom.o \
cdrom_image_backend.o cdrom_image_viso.o cdrom_image.o cdrom_mitsumi.o