├── BUILDING.md ├── CREDITS.md ├── LICENSE ├── README.md ├── contrib ├── unix │ └── Makefile.linux ├── vhdcvt.c └── win32 │ ├── .depends-mingw │ ├── Makefile.MinGW │ ├── Makefile.VC │ ├── getopt.c │ └── getopt.h ├── docs ├── Virtual Hard Disk Format Spec_10_18_06.pdf └── [MS-VHDX].pdf └── src ├── convert.c ├── create.c ├── cwalk.c ├── cwalk.h ├── internal.h ├── io.c ├── manage.c ├── minivhd.h ├── struct_rw.c ├── tester.c ├── unix └── Makefile.linux ├── util.c ├── version.h ├── win32 ├── Makefile.MinGW ├── Makefile.VC ├── icons │ └── minivhd.ico └── minivhd.rc ├── xml2_encoding.c └── xml2_encoding.h /BUILDING.md: -------------------------------------------------------------------------------- 1 | ## Building the MiniVHD library (and tools) from Source 2 | 3 | 4 | #### Fred N. van Kempen, 5 | 6 | #### Last Updated: 3/16/2021 7 | 8 | 9 | These instructions guide you in building the **MiniVHD** library from 10 | its published sources. Currently, it can be built for **Windows** and 11 | **Linux** (Debian-tested) platforms. More supported platforms will be 12 | added as time permits. 13 | 14 | 15 | #### Microsoft Windows 16 | 17 | #### Get the sources. 18 | 19 | For the Windows environment, two methods of compiling these sources are currently possible: 20 | 21 | ##### Using Microsoft's Visual Studio development system. 22 | Tested with versions **2015** ("VC14") and **2017*** ("VC15"), this is now a 23 | fairly easy process. 24 | 25 | 1. Install the Visual Studio system (the *Community* release is sufficient) and make sure it works. 26 | 27 | 2. Go to the *win32\* folder, then to the *vc14* folder for Visual Studio 2015, or to the *vc15* folder for the 2017 release of Visual Studio. 28 | 29 | 3. Double-click on the *MiniVHD.sln* solution file, and Visual Studio will load the project for you. 30 | 31 | 4. Select your preferred type of build (x86 or x64, Debug or Release) and build the project. 32 | 33 | **All done!** You can repeat this for other build types if so desired. 34 | 35 | For those who prefer to use **command-line tools** to process a *Makefile* (the author is such an oddity), you can also open up a **Visual Studio Command Prompt** from the Start Menu, and type the command: 36 | 37 | `D:` 38 | 39 | `cd \minivhd\src` 40 | 41 | `make -f win32\Makefile.VC` 42 | 43 | and voila, there it goes. If things are set up properly, this is about the fastest way to compile the sources on Windows. 44 | 45 | ##### Using the MSYS2 "MinGW" toolset. 46 | Building the emulator is also possible using the free **MinGW** compiler toolset, which is based on the well-known *gcc* toolset. Although also available as a standalone project, we will use the version that is part of the **MSYS2** project, which aims to create a *usable Linux-like environment on the Windows platform*. The MinGW toolset is part of that environment. 47 | 48 | 1. Download the most recent **MSYS2** distribution for your system. 49 | 50 | Please note that MSYS2 is a *64-bit application*, even though it 51 | (also) contains a 32-bit MinGW compiler. 52 | 53 | 2. Install MSYS2 onto your system, using either *C:\msysS2* or the 54 | suggested *C:\msysS64* folders as a root folder. 55 | 56 | 3. The standard installation of MSYS2 does not include a compiler 57 | or other 'build tools'. This is surprising, given the main 58 | objecttive of the project, but so be it. 59 | 60 | The easiest way to install packages like these is to use its 61 | built-in package manager, **pacman**. You can install a full 62 | "base setup" for GCC and friends with the command: 63 | 64 | `pacman -S --needed base-devel mingw-w64-i686-toolchain \` 65 | 66 | `mingw-w64-x86_64-toolchain git` 67 | 68 | (the backslashe is only for clarity; you can type it as one 69 | single line if you wish.) 70 | 71 | Pacman will show you the various packages and sub-packages to 72 | install, just type **ENTER** to agree, followed by a **y** to begin 73 | downloading and installing these packages. 74 | 75 | 4. Make sure this step worked, by typing commands like: 76 | 77 | `cc` 78 | 79 | `gcc` 80 | 81 | `make` 82 | 83 | just so we know this is done. 84 | 85 | 5. Optionally, you may want to install a text editor of choice. 86 | 87 | 6. Finally, we are good to go at compiling the MiniVHD sources. 88 | 89 | You are now ready to build, using a command like: 90 | 91 | `make -f win32/Makefile.MinGW` 92 | 93 | which will hopefully build the application's executables. 94 | 95 | With these steps, you have set up a build environment, suitable 96 | to build the MiniVHD project files for Windows. 97 | -------------------------------------------------------------------------------- /CREDITS.md: -------------------------------------------------------------------------------- 1 | # Credits 2 | MiniVHD Copyright 2019-2021 Sherman Perry. 3 | 4 | MiniVHD was made possible with the help of the following projects: 5 | 6 | ### libxml2 7 | **Project Home:** http://www.xmlsoft.org/ 8 | **License:** MIT (see src/libxml2_encoding.c for details) 9 | 10 | ### cwalk 11 | **Project Home:** https://likle.github.io/cwalk/ 12 | **Licence:** MIT (https://github.com/likle/cwalk/blob/master/LICENSE.md) 13 | 14 | ### VARCem 15 | The MiniVHD was rewritten into a standalone library (both shared as well 16 | as static) by Fred N. van Kempen for use with the VARCem PC Systems 17 | emulator - see https://www.varcem.com/ for more info. 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Sherman Perry 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MiniVHD - Minimalist VHD implementation in C 2 | 3 | **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 OS. 4 | 5 | ## Features 6 | MiniVHD aims to implement as much of the VHD specification as possible. Features include: 7 | * Implemented in pure C. Some C99 features are used (such as stdint, stdbool, array initialisation), so not compatible with strict ANSI C 8 | * Full support for fixed, sparse (dynamic) and differencing VHD images 9 | * Open existing VHD images 10 | * VHD image creation 11 | * Conversion to/from raw disk images 12 | * Read/write sectors to VHD images 13 | * Aims to be cross platform, although not fully there yet. Works with MinGW-w64, and presumably GCC/Clang 14 | * Simple to include and use (I hope) 15 | 16 | ## Usage 17 | 18 | Include `minivhd.h` in your source to get started. See `minivhd.h` for documentation of the API. Then link the application with the library (-lminivhd for UNIX and WinGW, or minivhd.lib for Visual Studio on Windows.) 19 | 20 | **Please note, an older version of this library can be found in the `minivhd-v1` branch of this repository, if required for some reason.** 21 | -------------------------------------------------------------------------------- /contrib/unix/Makefile.linux: -------------------------------------------------------------------------------- 1 | # 2 | # VARCem Virtual ARchaeological Computer EMulator. 3 | # An emulator of (mostly) x86-based PC systems and devices, 4 | # using the ISA,EISA,VLB,MCA and PCI system buses, roughly 5 | # spanning the era between 1981 and 1995. 6 | # 7 | # This file is part of the VARCem Project. 8 | # 9 | # Makefile for UNIX and Linux systems using GCC. 10 | # 11 | # Version: @(#)Makefile.GCC 1.0.1 2021/03/16 12 | # 13 | # Author: Fred N. van Kempen, 14 | # 15 | # Copyright 2021 Fred N. van Kempen. 16 | # 17 | # Redistribution and use in source and binary forms, with 18 | # or without modification, are permitted provided that the 19 | # following conditions are met: 20 | # 21 | # 1. Redistributions of source code must retain the entire 22 | # above notice, this list of conditions and the following 23 | # disclaimer. 24 | # 25 | # 2. Redistributions in binary form must reproduce the above 26 | # copyright notice, this list of conditions and the 27 | # following disclaimer in the documentation and/or other 28 | # materials provided with the distribution. 29 | # 30 | # 3. Neither the name of the copyright holder nor the names 31 | # of its contributors may be used to endorse or promote 32 | # products derived from this software without specific 33 | # prior written permission. 34 | # 35 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 36 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 37 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 38 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 39 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 40 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 41 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 42 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 43 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 44 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 45 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 46 | # 47 | 48 | 49 | # Defaults for several build options (possibly defined in a chained file.) 50 | ifndef AUTODEP 51 | AUTODEP := n 52 | endif 53 | ifndef DEBUG 54 | DEBUG := n 55 | endif 56 | ifndef X64 57 | X64 := n 58 | endif 59 | 60 | 61 | # Name of the projects. 62 | PROGS := vhdcvt 63 | 64 | 65 | # Select the desired platform. 66 | PLAT := Linux 67 | PLATDEFS := -DLINUX -DPLAT=\"$(PLAT)\" 68 | PLATNAME := linux 69 | PLATDIR := unix 70 | 71 | 72 | # Project settings. 73 | DEFS := 74 | 75 | 76 | ######################################################################### 77 | # Nothing should need changing from here on.. # 78 | ######################################################################### 79 | VPATH := unix 80 | 81 | 82 | # Select the required build environment. 83 | ifeq ($(X64), y) 84 | CPP := g++ -m64 85 | CC := gcc -m64 86 | ARCH := x64 87 | else 88 | CPP := g++ -m32 89 | CC := gcc -m32 90 | ARCH := x86 91 | endif 92 | PREPROC := cpp 93 | MCPP := cpp 94 | LINK := gcc 95 | AR := ar 96 | RANLIB := ranlib 97 | STRIP := strip 98 | 99 | SYSINC := 100 | SYSLIB := 101 | 102 | DEPS = -MMD -MF $*.d -c $< 103 | DEPFILE := $(PLATDIR)/.depends-$(PLATNAME) 104 | 105 | 106 | # Set up the correct toolchain flags. 107 | OPTS := -DUNIX $(PLATDEFS) 108 | AFLAGS := -msse2 -mfpmath=sse 109 | COPTS := -Wall 110 | CXXOPTS := -Wall 111 | DOPTS := 112 | LOPTS := 113 | ifeq ($(DEBUG), y) 114 | OPTS += -D_DEBUG 115 | DOPTS := -Og -ggdb 116 | ROPTS += -D_DEBUG 117 | else 118 | DFLAGS := -O3 119 | endif 120 | SYSLIBS := -lgcc #-lpthread 121 | 122 | 123 | # Final versions of the toolchain flags. 124 | LFLAGS := -pthread $(LOPTS) -L../src 125 | CFLAGS := $(DEFS) $(OPTS) $(COPTS) $(DOPTS) -I../src \ 126 | -fPIC -fno-strict-aliasing -fvisibility=hidden \ 127 | -fomit-frame-pointer -mstackrealign -Wall -Wundef 128 | CXXFLAGS := -I/usr/include/c++/4.8.4 \ 129 | $(DEFS) $(OPTS) $(CXXOPTS) $(COPTS) $(DOPTS) -I../src\ 130 | -fPIC -fno-strict-aliasing -fvisibility=hidden \ 131 | -fvisibility-inlines-hidden -Wall -Wundef -Wunused-parameter \ 132 | -Wmissing-declarations -Wno-ctor-dtor-privacy -Woverloaded-virtual 133 | 134 | 135 | ######################################################################### 136 | # Create the (final) list of objects to build. # 137 | ######################################################################### 138 | 139 | LOBJ := 140 | 141 | 142 | # Build module rules. 143 | ifeq ($(AUTODEP), y) 144 | %.o: %.c 145 | @echo $< 146 | @$(CC) $(CFLAGS) $(DEPS) -c $< 147 | 148 | %.o: %.cpp 149 | @echo $< 150 | @$(CPP) $(CXXFLAGS) $(DEPS) -c $< 151 | else 152 | %.o: %.c 153 | @echo $< 154 | @$(CC) $(CFLAGS) -c $< 155 | 156 | %.o: %.cpp 157 | @echo $< 158 | @$(CPP) $(CXXFLAGS) -c $< 159 | 160 | %.d: %.c $(wildcard $*.d) 161 | @echo $< 162 | @$(CC) $(CFLAGS) $(DEPS) -E $< >NUL 163 | 164 | %.d: %.cpp $(wildcard $*.d) 165 | @echo $< 166 | @$(CPP) $(CXXFLAGS) $(DEPS) -E $< >NUL 167 | endif 168 | 169 | 170 | all: $(PROGS) $(PROGS)_s 171 | 172 | 173 | $(PROGS): $(PROGS).o 174 | @echo Linking $@ .. 175 | $(CC) $(LFLAGS) -o $@ $(PROGS).o $(SYSLIBS) -lminivhd 176 | ifneq ($(DEBUG), y) 177 | @$(STRIP) $@ 178 | endif 179 | 180 | $(PROGS)_s: $(PROGS).o 181 | @echo Linking $@ .. 182 | $(CC) $(LFLAGS) -o $@ $(PROGS).o $(SYSLIBS) -static -lminivhd 183 | ifneq ($(DEBUG), y) 184 | @$(STRIP) $@ 185 | endif 186 | 187 | 188 | install: all 189 | @-mkdir ../bin 190 | @-cp $(PROGS) $(PROGS)_s ../bin 191 | 192 | clean: 193 | @echo Cleaning objects.. 194 | @-rm -f *.o 195 | 196 | clobber: clean 197 | @echo Cleaning executables.. 198 | @-rm -f $(PROGS) $(PROGS)_s 199 | @echo Cleaning libraries.. 200 | @-rm -f *.so 201 | @-rm -f *.a 202 | @-rm -f *.d 203 | @-rm -f $(DEPFILE) 204 | 205 | ifneq ($(AUTODEP), y) 206 | depclean: 207 | @-rm -f $(DEPFILE) 208 | @echo Creating dependencies.. 209 | @echo # Run "make depends" to re-create this file. >$(DEPFILE) 210 | 211 | depends: DEPOBJ=$(OBJ:%.o=%.d) 212 | depends: depclean $(OBJ:%.o=%.d) 213 | @$(CAT) $(DEPOBJ) >>$(DEPFILE) 214 | # @-rm -f $(DEPOBJ) 215 | 216 | $(DEPFILE): 217 | endif 218 | 219 | 220 | # Module dependencies. 221 | ifeq ($(AUTODEP), y) 222 | #-include $(OBJ:%.o=%.d) (better, but sloooowwwww) 223 | -include *.d 224 | else 225 | include $(wildcard $(DEPFILE)) 226 | endif 227 | 228 | 229 | # End of Makefile.GCC. 230 | -------------------------------------------------------------------------------- /contrib/vhdcvt.c: -------------------------------------------------------------------------------- 1 | /* 2 | * VARCem Virtual ARchaeological Computer EMulator. 3 | * An emulator of (mostly) x86-based PC systems and devices, 4 | * using the ISA,EISA,VLB,MCA and PCI system buses, roughly 5 | * spanning the era between 1981 and 1995. 6 | * 7 | * This file is part of the VARCem Project. 8 | * 9 | * Convert between RAW and VHD disk images. 10 | * 11 | * Usage: vhdcvt [-qv] [-o out_file] [-s] image.img 12 | * vhdcvt [-qv] [-o out_file] [-r] image.vhd 13 | * 14 | * Version: @(#)vhdcvt.h 1.0.2 2021/03/16 15 | * 16 | * Author: Fred N. van Kempen, 17 | * 18 | * Copyright 2021 Fred N. van Kempen. 19 | * 20 | * Redistribution and use in source and binary forms, with 21 | * or without modification, are permitted provided that the 22 | * following conditions are met: 23 | * 24 | * 1. Redistributions of source code must retain the entire 25 | * above notice, this list of conditions and the following 26 | * disclaimer. 27 | * 28 | * 2. Redistributions in binary form must reproduce the above 29 | * copyright notice, this list of conditions and the 30 | * following disclaimer in the documentation and/or other 31 | * materials provided with the distribution. 32 | * 33 | * 3. Neither the name of the copyright holder nor the names 34 | * of its contributors may be used to endorse or promote 35 | * products derived from this software without specific 36 | * prior written permission. 37 | * 38 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 39 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 40 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 41 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 42 | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 43 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 44 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 45 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 46 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 47 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 48 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 49 | */ 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | #include 57 | 58 | 59 | #define VERSION "1.0.2" 60 | 61 | 62 | static int opt_q, // be quiet 63 | opt_r, // create raw image 64 | opt_s, // create sparse file 65 | opt_v; // verbose mode 66 | 67 | 68 | static void 69 | usage(void) 70 | { 71 | fprintf(stderr, 72 | "Usage: vhdcvt [-qv] [-o out_file] [-s] image.img\n"); 73 | fprintf(stderr, 74 | " vhdcvt [-qv] [-o out_file] [-r] image.vhd\n"); 75 | fprintf(stderr, 76 | "\nIf -r is used, conversion from VHD to RAW will be attempted.\n" 77 | "Otherwise, the (raw) input file will be converted to a VHD\n" 78 | "image, optionally in SPARSE mode if the -s option is present.\n\n"); 79 | 80 | exit(1); 81 | /*NOTREACHED*/ 82 | } 83 | 84 | 85 | int 86 | main(int argc, char *argv[]) 87 | { 88 | char temp[1024], *sp; 89 | char *outname, *name; 90 | MVHDMeta *vhd; 91 | FILE *raw; 92 | int c; 93 | 94 | /* Set defaults. */ 95 | opt_q = opt_r = opt_s = opt_v = 0; 96 | outname = NULL; 97 | 98 | opterr = 0; 99 | while ((c = getopt(argc, argv, "o:qrsv")) != EOF) switch(c) { 100 | case 'q': // be quiet 101 | opt_s = 1; 102 | break; 103 | 104 | case 'v': // verbose mode 105 | opt_v++; 106 | break; 107 | 108 | case 'r': // convert to raw-mode image file 109 | opt_r ^= 1; 110 | break; 111 | 112 | case 's': // create sparse-mode VHD file 113 | opt_s ^= 1; 114 | break; 115 | 116 | case 'o': // specify output filename 117 | outname = optarg; 118 | break; 119 | 120 | default: 121 | usage(); 122 | /*NOTREACHED*/ 123 | } 124 | 125 | /* Say hello unless we have to be quiet. */ 126 | if (! opt_q) { 127 | printf("VHDcvt - Convert between RAW and VHD, version %s.\n", VERSION); 128 | printf("Author: Fred N. van Kempen, \n"); 129 | printf("Copyright 2021, The VARCem Team.\n\n"); 130 | 131 | if (opt_v) { 132 | printf("Library version is %s (%08lX)\n\n", 133 | mvhd_version(), (unsigned long)mvhd_version_id()); 134 | } 135 | } 136 | 137 | /* Sanity checks. */ 138 | if (opt_r && opt_s) { 139 | fprintf(stderr, "The -o and -r options cannot be combined!\n"); 140 | usage(); 141 | } 142 | if (outname && ((argc -optind) > 1)) { 143 | fprintf(stderr, "The -o option cannot be used when multiple files are to be converted!\n"); 144 | usage(); 145 | } 146 | 147 | /* We need at least one argument. */ 148 | if (optind == argc) 149 | usage(); 150 | 151 | /* Now loop over the given files. */ 152 | while (optind != argc) { 153 | name = argv[optind++]; 154 | 155 | /* Generate suitable filename if none given. */ 156 | if (outname == NULL) { 157 | strncpy(temp, name, sizeof(temp) - 5); 158 | if ((sp = strchr(temp, '.')) != NULL) 159 | *sp = '\0'; 160 | if (opt_r) 161 | strcat(temp, ".img"); 162 | else 163 | strcat(temp, ".vhd"); 164 | sp = temp; 165 | } else 166 | sp = outname; 167 | 168 | /* Set "no error". */ 169 | c = 0; 170 | 171 | if (opt_r) { 172 | /* Convert a VHD image to a RAW image. */ 173 | if (! opt_q) 174 | printf("Converting VHD '%s' to RAW image.\n", name); 175 | 176 | raw = mvhd_convert_to_raw(name, sp, &c); 177 | if (raw != NULL) 178 | fclose(raw); 179 | } else { 180 | /* Convert from raw image to VHD. */ 181 | if (! opt_q) 182 | printf("Converting RAW '%s' to %sVHD image.\n", 183 | name, opt_s?"sparse ":""); 184 | if (opt_s) 185 | vhd = mvhd_convert_to_vhd_sparse(name, sp, &c); 186 | else 187 | vhd = mvhd_convert_to_vhd_fixed(name, sp, &c); 188 | if (vhd != NULL) 189 | mvhd_close(vhd); 190 | } 191 | 192 | if (c != 0) { 193 | /* Some kind of error occurred. */ 194 | fprintf(stderr, "\nERROR: %s\n", mvhd_strerr(c)); 195 | break; 196 | } 197 | } 198 | 199 | return(c); 200 | } 201 | -------------------------------------------------------------------------------- /contrib/win32/.depends-mingw: -------------------------------------------------------------------------------- 1 | minivhd_test.o: minivhd_test.c ../include/minivhd.h 2 | vhdcvt.o: vhdcvt.c ../include/minivhd.h 3 | -------------------------------------------------------------------------------- /contrib/win32/Makefile.MinGW: -------------------------------------------------------------------------------- 1 | # 2 | # VARCem Virtual ARchaeological Computer EMulator. 3 | # An emulator of (mostly) x86-based PC systems and devices, 4 | # using the ISA,EISA,VLB,MCA and PCI system buses, roughly 5 | # spanning the era between 1981 and 1995. 6 | # 7 | # This file is part of the VARCem Project. 8 | # 9 | # Makefile for Windows systems using the MinGW32 environment. 10 | # 11 | # Version: @(#)Makefile.MinGW 1.0.2 2021/03/16 12 | # 13 | # Author: Fred N. van Kempen, 14 | # 15 | # Copyright 2021 Fred N. van Kempen. 16 | # 17 | # Redistribution and use in source and binary forms, with 18 | # or without modification, are permitted provided that the 19 | # following conditions are met: 20 | # 21 | # 1. Redistributions of source code must retain the entire 22 | # above notice, this list of conditions and the following 23 | # disclaimer. 24 | # 25 | # 2. Redistributions in binary form must reproduce the above 26 | # copyright notice, this list of conditions and the 27 | # following disclaimer in the documentation and/or other 28 | # materials provided with the distribution. 29 | # 30 | # 3. Neither the name of the copyright holder nor the names 31 | # of its contributors may be used to endorse or promote 32 | # products derived from this software without specific 33 | # prior written permission. 34 | # 35 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 36 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 37 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 38 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 39 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 40 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 41 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 42 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 43 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 44 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 45 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 46 | # 47 | 48 | 49 | # Defaults for several build options (possibly defined in a chained file.) 50 | ifndef DEBUG 51 | DEBUG := n 52 | endif 53 | ifndef X64 54 | X64 := n 55 | endif 56 | 57 | 58 | PROGS := vhdcvt 59 | SYSOBJ := 60 | 61 | 62 | ######################################################################### 63 | # Nothing should need changing from here on.. # 64 | ######################################################################### 65 | 66 | # 67 | # Select the required build environment. 68 | # 69 | ifeq ($(X64), y) 70 | CPP := g++ -m64 71 | CC := gcc -m64 72 | ARCH := x64 73 | LIBX := lib/x64 74 | else 75 | CPP := g++ -m32 76 | CC := gcc -m32 77 | ARCH := x86 78 | LIBX := lib/x86 79 | endif 80 | PREPROC := cpp 81 | MCPP := cpp 82 | STRIP := strip 83 | WINDRES := windres 84 | 85 | SYSINC := 86 | SYSLIB := 87 | 88 | DEPS = -MMD -MF $*.d -c $< 89 | DEPFILE := win32/.depends-mingw 90 | 91 | # Set up the correct toolchain flags. 92 | OPTS := -D_CRT_SECURE_NO_WARNINGS \ 93 | -D_CRT_NON_CONFORMING_SWPRINTFS \ 94 | -D__USE_MINGW_ANSI_STDIO_X 95 | AFLAGS := -msse2 -mfpmath=sse 96 | ifndef RFLAGS 97 | RFLAGS := --input-format=rc -O coff 98 | endif 99 | COPTS := -Wall 100 | CXXOPTS := -Wall 101 | DOPTS := 102 | LOPTS := 103 | ifeq ($(DEBUG), y) 104 | OPTS += -D_DEBUG 105 | DOPTS := -march=i686 -Og -ggdb 106 | ROPTS += -D_DEBUG 107 | else 108 | DFLAGS := -march=i686 -O3 109 | endif 110 | 111 | 112 | # Final versions of the toolchain flags. 113 | LFLAGS := $(LOPTS) -L../$(LIBX) 114 | CFLAGS := $(OPTS) $(COPTS) $(DOPTS) -I../include 115 | CXXFLAGS := $(OPTS) $(CXXOPTS) $(COPTS) $(DOPTS) 116 | 117 | 118 | # Build module rules. 119 | ifeq ($(AUTODEP), y) 120 | %.o: %.c 121 | @echo $< 122 | @$(CC) $(CFLAGS) $(DEPS) -c $< 123 | 124 | %.o: %.cpp 125 | @echo $< 126 | @$(CPP) $(CXXFLAGS) $(DEPS) -c $< 127 | else 128 | %.o: %.c 129 | @echo $< 130 | @$(CC) $(CFLAGS) -c $< 131 | 132 | %.o: %.cpp 133 | @echo $< 134 | @$(CPP) $(CXXFLAGS) -c $< 135 | 136 | %.d: %.c $(wildcard $*.d) 137 | @echo $< 138 | @$(CC) $(CFLAGS) $(DEPS) -E $< >NUL 139 | 140 | %.d: %.cpp $(wildcard $*.d) 141 | @echo $< 142 | @$(CPP) $(CXXFLAGS) $(DEPS) -E $< >NUL 143 | endif 144 | 145 | 146 | all: $(PROGS).exe $(PROGS)_s.exe 147 | 148 | 149 | vhdcvt.exe: vhdcvt.o 150 | @echo Linking $@ .. 151 | @$(CC) $(LFLAGS) -o $@ vhdcvt.o \ 152 | $(SYSOBJ) $(LIBS) -lminivhd.dll 153 | ifneq ($(DEBUG), y) 154 | @$(STRIP) $@ 155 | endif 156 | 157 | vhdcvt_s.exe: vhdcvt.o 158 | @echo Linking $@ .. 159 | @$(CC) $(LFLAGS) -o $@ $(OBJS) \ 160 | $(SYSOBJ) $(LIBS) -lminivhd 161 | ifneq ($(DEBUG), y) 162 | @$(STRIP) $@ 163 | endif 164 | 165 | 166 | install: all 167 | @-copy *.exe ..\bin /y 168 | 169 | 170 | clean: 171 | @echo Cleaning objects.. 172 | @-del *.o 2>NUL 173 | @echo Cleaning resources.. 174 | @-del *.res 2>NUL 175 | 176 | clobber: clean 177 | @echo Cleaning executables.. 178 | @-del *.exe 2>NUL 179 | @echo Cleaning DLLs.. 180 | @-del *.dll 2>NUL 181 | # @-del win\*.manifest 2>NUL 182 | @-del *.d 2>NUL 183 | # @-del $(DEPFILE) 2>NUL 184 | 185 | ifneq ($(AUTODEP), y) 186 | depclean: 187 | @-del $(DEPFILE) 2>NUL 188 | @echo Creating dependencies.. 189 | @echo # Run "make depends" to re-create this file. >$(DEPFILE) 190 | 191 | depends: DEPOBJ=$(OBJ:%.obj=%.d) 192 | depends: depclean $(OBJ:%.obj=%.d) 193 | @-cat $(DEPOBJ) >>$(DEPFILE) 194 | @-del $(DEPOBJ) 195 | 196 | $(DEPFILE): 197 | endif 198 | 199 | 200 | # Module targets: 201 | getopt.o: win32/getopt.c 202 | @echo win32/getopt.c 203 | @$(CC) $(CFLAGS) -c win32/getopt.c 204 | 205 | 206 | # Module dependencies. 207 | ifeq ($(AUTODEP), y) 208 | #-include $(OBJ:%.obj=%.d) (better, but sloooowwwww) 209 | -include *.d 210 | else 211 | include $(wildcard $(DEPFILE)) 212 | endif 213 | 214 | 215 | # End of Makefile.MinGW. 216 | -------------------------------------------------------------------------------- /contrib/win32/Makefile.VC: -------------------------------------------------------------------------------- 1 | # 2 | # VARCem Virtual ARchaeological Computer EMulator. 3 | # An emulator of (mostly) x86-based PC systems and devices, 4 | # using the ISA,EISA,VLB,MCA and PCI system buses, roughly 5 | # spanning the era between 1981 and 1995. 6 | # 7 | # This file is part of the VARCem Project. 8 | # 9 | # Makefile for Windows using Microsoft Visual Studio. 10 | # 11 | # Version: @(#)Makefile.VC 1.0.1 2021/03/16 12 | # 13 | # Author: Fred N. van Kempen, 14 | # 15 | # Copyright 2021 Fred N. van Kempen. 16 | # 17 | # Redistribution and use in source and binary forms, with 18 | # or without modification, are permitted provided that the 19 | # following conditions are met: 20 | # 21 | # 1. Redistributions of source code must retain the entire 22 | # above notice, this list of conditions and the following 23 | # disclaimer. 24 | # 25 | # 2. Redistributions in binary form must reproduce the above 26 | # copyright notice, this list of conditions and the 27 | # following disclaimer in the documentation and/or other 28 | # materials provided with the distribution. 29 | # 30 | # 3. Neither the name of the copyright holder nor the names 31 | # of its contributors may be used to endorse or promote 32 | # products derived from this software without specific 33 | # prior written permission. 34 | # 35 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 36 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 37 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 38 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 39 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 40 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 41 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 42 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 43 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 44 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 45 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 46 | # 47 | 48 | 49 | # Defaults for several build options (possibly defined in a chained file.) 50 | ifndef AUTODEP 51 | AUTODEP := n 52 | endif 53 | ifndef DEBUG 54 | DEBUG := n 55 | endif 56 | ifndef OPTIM 57 | OPTIM := n 58 | endif 59 | ifndef X64 60 | X64 := n 61 | endif 62 | 63 | 64 | # Name of the projects. 65 | PROGS := vhdcvt 66 | ifeq ($(DEBUG), y) 67 | PROGS := $(PROGS)-d 68 | endif 69 | 70 | 71 | ######################################################################### 72 | # Nothing should need changing from here on.. # 73 | ######################################################################### 74 | 75 | VPATH := win32 76 | 77 | 78 | # 79 | # Select the required build environment. 80 | # 81 | VCOPTS := -D_CRT_SECURE_NO_WARNINGS -D__MSC__ 82 | ifeq ($(X64), y) 83 | ARCH := x64 84 | CPP := cl -nologo 85 | CC := cl -nologo 86 | AR := lib -nologo 87 | LIBX := lib/x64 88 | else 89 | ARCH := x86 90 | CPP := cl -nologo 91 | CC := cl -nologo #-TP 92 | AR := lib -nologo 93 | LIBX := lib/x86 94 | endif 95 | PREPROC := cl -nologo -EP 96 | MCPP := mcpp.exe 97 | LINK := link -nologo 98 | WINDRES := rc -nologo -r -d_MSC_VER 99 | 100 | SYSINC := 101 | SYSLIB := 102 | SYSOBJ := setargv.obj 103 | 104 | DEPS = -MMD -MF $*.d 105 | DEPFILE := win32\.depends-msvc 106 | 107 | 108 | # Set up the correct toolchain flags. 109 | OPTS := $(VCOPTS) \ 110 | -D_USE_MATH_DEFINES \ 111 | -D_CRT_NONSTDC_NO_DEPRECATE \ 112 | -D_WINSOCK_DEPRECATED_NO_WARNINGS \ 113 | -D_CRT_SECURE_NO_WARNINGS \ 114 | -D_CRT_STDIO_ISO_WIDE_SPECIFIERS 115 | AFLAGS := #/arch:SSE2 116 | RFLAGS := /n 117 | COPTS := -W3 118 | CXXOPTS := -EHsc 119 | DOPTS := 120 | ifeq ($(X64), y) 121 | LOPTS := -MACHINE:$(ARCH) 122 | LOPTS_C := -SUBSYSTEM:CONSOLE 123 | LOPTS_W := -SUBSYSTEM:WINDOWS 124 | else 125 | LOPTS := -MACHINE:$(ARCH) 126 | LOPTS_C := -SUBSYSTEM:CONSOLE,5.01 127 | LOPTS_W := -SUBSYSTEM:WINDOWS,5.01 128 | endif 129 | OPTS += $(SYSINC) 130 | ifeq ($(OPTIM), y) 131 | AFLAGS := /arch:AVX2 132 | endif 133 | ifeq ($(DEBUG), y) 134 | OPTS += -D_DEBUG 135 | DOPTS += -MTd -Gs -Zi 136 | RFLAGS += -D_DEBUG 137 | LOPTS += -debug 138 | ifndef COPTIM 139 | COPTIM := -Od 140 | endif 141 | else 142 | LOPTS += #-LTCG 143 | DOPTS := -MT #-GL 144 | ifndef COPTIM 145 | COPTIM := -O2 146 | endif 147 | endif 148 | SYSLIBS := 149 | ifeq ($(DEBUG), y) 150 | SYSLIBS += 151 | endif 152 | 153 | 154 | # Final versions of the toolchain flags. 155 | LFLAGS := $(LOPTS) -LIBPATH:../$(LIBX) 156 | CFLAGS := $(OPTS) $(COPTS) $(COPTIM) $(DOPTS) $(AFLAGS) \ 157 | -Iwin32 -I../include 158 | CXXFLAGS := $(OPTS) $(CXXOPTS) $(COPTS) $(COPTIM) $(DOPTS) $(AFLAGS) \ 159 | -Iwin32 -I../include 160 | 161 | 162 | ######################################################################### 163 | # Create the (final) list of objects to build. # 164 | ######################################################################### 165 | 166 | OBJ := 167 | 168 | 169 | # Build module rules. 170 | ifeq ($(AUTODEP), y) 171 | %.obj: %.c 172 | @$(CC) $(CFLAGS) -Fo$@ -c $< 173 | @$(MCPP) $(OPTS) $(DEPS) $< 174 | 175 | %.obj: %.cpp 176 | @$(CPP) $(CXXFLAGS) -Fo$@ -c $< 177 | @$(MCPP) $(OPTS) $(DEPS) $< 178 | else 179 | %.obj: %.c 180 | @$(CC) $(CFLAGS) -Fo$@ -c $< 181 | 182 | %.sbj: %.c 183 | @$(CC) $(CFLAGS) -DSTATIC -Fo$@ -c $< 184 | 185 | %.obj: %.cpp 186 | @$(CPP) $(CXXFLAGS) -Fo$@ -c $< 187 | 188 | %.d: %.c $(wildcard $*.d) 189 | @$(MCPP) $(OPTS) $(DEPS) $< >NUL 190 | 191 | %.d: %.cpp $(wildcard $*.d) 192 | @$(MCPP) $(OPTS) $(DEPS) $< >NUL 193 | endif 194 | 195 | 196 | all: $(PROGS).exe $(PROGS)_s.exe 197 | 198 | 199 | vhdcvt.exe: getopt.obj vhdcvt.obj 200 | @echo Linking $@ .. 201 | @$(LINK) $(LFLAGS) /OUT:$@ \ 202 | $(SYSOBJ) getopt.obj vhdcvt.obj $(SYSLIBS) minivhd.lib 203 | 204 | vhdcvt_s.exe: getopt.obj vhdcvt.sbj 205 | @echo Linking $@ .. 206 | @$(LINK) $(LFLAGS) /OUT:$@ \ 207 | $(SYSOBJ) getopt.obj vhdcvt.sbj $(SYSLIBS) minivhd_s.lib 208 | 209 | 210 | clean: 211 | @echo Cleaning objects.. 212 | @-del *.obj 2>NUL 213 | @-del *.sbj 2>NUL 214 | @-del *.res 2>NUL 215 | 216 | clobber: clean 217 | @echo Cleaning executables.. 218 | @-del *.exe 2>NUL 219 | ifeq ($(DEBUG), y) 220 | @-del *.ilk 2>NUL 221 | @-del *.pdb 2>NUL 222 | endif 223 | @echo Cleaning libraries.. 224 | @-del *.dll 2>NUL 225 | @-del *.exp 2>NUL 226 | @-del *.lib 2>NUL 227 | @-del *.d 2>NUL 228 | # @del $(DEPFILE) 2>NUL 229 | 230 | 231 | ifneq ($(AUTODEP), y) 232 | depclean: 233 | @del $(DEPFILE) 2>NUL 234 | @echo Creating dependencies.. 235 | @echo # Run "make depends" to re-create this file. >$(DEPFILE) 236 | 237 | depends: DEPOBJ=$(OBJ:%.obj=%.d) 238 | depends: depclean $(OBJ:%.obj=%.d) 239 | @-cat $(DEPOBJ) >>$(DEPFILE) 240 | @del $(DEPOBJ) 241 | 242 | $(DEPFILE): 243 | endif 244 | 245 | 246 | # Module dependencies. 247 | ifeq ($(AUTODEP), y) 248 | #-include $(OBJ:%.obj=%.d) (better, but sloooowwwww) 249 | -include *.d 250 | else 251 | include $(wildcard $(DEPFILE)) 252 | endif 253 | 254 | 255 | # End of Makefile.VC. 256 | -------------------------------------------------------------------------------- /contrib/win32/getopt.c: -------------------------------------------------------------------------------- 1 | /* 2 | * VARCem Virtual ARchaeological Computer EMulator. 3 | * An emulator of (mostly) x86-based PC systems and devices, 4 | * using the ISA,EISA,VLB,MCA and PCI system buses, roughly 5 | * spanning the era between 1981 and 1995. 6 | * 7 | * This file is part of the VARCem Project. 8 | * 9 | * Portable GETOPT(3) function. 10 | * 11 | * This is a simple version of the UNIX-standard GetOpt(3) 12 | * library routine. We don't support any of the GNU LibC 13 | * enhancements such as long-name options, double-dash 14 | * options and such. We keep it simple and small here. 15 | * 16 | * Version: @(#)getopt.c 1.0.1 2002/08/13 17 | * 18 | * Author: Fred N. van Kempen, 19 | * 20 | * Copyright 1998-2018 MicroWalt Corporation 21 | * Copyright 2018-2021 The VARCem Team 22 | * 23 | * Redistribution and use in source and binary forms, with 24 | * or without modification, are permitted provided that the 25 | * following conditions are met: 26 | * 27 | * 1. Redistributions of source code must retain the entire 28 | * above notice, this list of conditions and the following 29 | * disclaimer. 30 | * 31 | * 2. Redistributions in binary form must reproduce the above 32 | * copyright notice, this list of conditions and the 33 | * following disclaimer in the documentation and/or other 34 | * materials provided with the distribution. 35 | * 36 | * 3. Neither the name of the copyright holder nor the names 37 | * of its contributors may be used to endorse or promote 38 | * products derived from this software without specific 39 | * prior written permission. 40 | * 41 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 42 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 43 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 44 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 45 | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 46 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 47 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 48 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 49 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 50 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 51 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 52 | */ 53 | #include 54 | #include 55 | 56 | 57 | char *optarg; 58 | int optind = 0; 59 | int opterr = 1; 60 | int optopt; 61 | 62 | 63 | static char *scan = (char *)NULL; 64 | 65 | 66 | int 67 | getopt(int argc, char **argv, const char *optstring) 68 | { 69 | char c; 70 | char *place; 71 | 72 | optarg = (char *)NULL; 73 | 74 | if (scan == (char *)NULL || *scan == '\0') { 75 | if (optind == 0) optind++; 76 | 77 | if (optind >= argc || argv[optind][0] != '-' || argv[optind][1] == '\0') 78 | return(EOF); 79 | if (strcmp(argv[optind], "--") == 0) { 80 | optind++; 81 | return(EOF); 82 | } 83 | scan = argv[optind] + 1; 84 | optind++; 85 | } 86 | optopt = c = *scan++; 87 | place = strchr(optstring, c); 88 | 89 | if (place == (char *)NULL || c == ':') { 90 | if (opterr) fprintf(stderr, "%s: unknown option -%c\n", argv[0], c); 91 | return('?'); 92 | } 93 | place++; 94 | if (*place == ':') { 95 | if (*scan != '\0') { 96 | optarg = scan; 97 | scan = (char *)NULL; 98 | } else if (optind < argc) { 99 | optarg = argv[optind]; 100 | optind++; 101 | } else { 102 | if (opterr) 103 | fprintf(stderr, 104 | "%s: -%c argument missing\n", argv[0], c); 105 | return('?'); 106 | } 107 | } 108 | return(c); 109 | } 110 | -------------------------------------------------------------------------------- /contrib/win32/getopt.h: -------------------------------------------------------------------------------- 1 | /* 2 | * VARCem Virtual ARchaeological Computer EMulator. 3 | * An emulator of (mostly) x86-based PC systems and devices, 4 | * using the ISA,EISA,VLB,MCA and PCI system buses, roughly 5 | * spanning the era between 1981 and 1995. 6 | * 7 | * This file is part of the VARCem Project. 8 | * 9 | * Portable GETOPT(3) function. 10 | * 11 | * Version: @(#)getopt.h 1.0.1 2002/08/13 12 | * 13 | * Author: Fred N. van Kempen, 14 | * 15 | * Copyright 1998-2018 MicroWalt Corporation 16 | * Copyright 2018-2021 The VARCem Team 17 | * 18 | * Redistribution and use in source and binary forms, with 19 | * or without modification, are permitted provided that the 20 | * following conditions are met: 21 | * 22 | * 1. Redistributions of source code must retain the entire 23 | * above notice, this list of conditions and the following 24 | * disclaimer. 25 | * 26 | * 2. Redistributions in binary form must reproduce the above 27 | * copyright notice, this list of conditions and the 28 | * following disclaimer in the documentation and/or other 29 | * materials provided with the distribution. 30 | * 31 | * 3. Neither the name of the copyright holder nor the names 32 | * of its contributors may be used to endorse or promote 33 | * products derived from this software without specific 34 | * prior written permission. 35 | * 36 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 37 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 38 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 39 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 40 | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 41 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 42 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 43 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 44 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 45 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 46 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 47 | */ 48 | #ifndef GETOPT_H 49 | # define GETOPT_H 50 | 51 | 52 | extern int optind, 53 | opterr; 54 | extern char *optarg; 55 | 56 | 57 | #ifdef __cplusplus 58 | extern "C" { 59 | #endif 60 | 61 | extern int getopt(int ac, char *av[], const char *); 62 | 63 | #ifdef __cplusplus 64 | } 65 | #endif 66 | 67 | 68 | #endif /*GETOPT_H*/ 69 | -------------------------------------------------------------------------------- /docs/Virtual Hard Disk Format Spec_10_18_06.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shermp/MiniVHD/bd02b4ad42b3cd4fa1ae3ef38f9f698edfc06582/docs/Virtual Hard Disk Format Spec_10_18_06.pdf -------------------------------------------------------------------------------- /docs/[MS-VHDX].pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shermp/MiniVHD/bd02b4ad42b3cd4fa1ae3ef38f9f698edfc06582/docs/[MS-VHDX].pdf -------------------------------------------------------------------------------- /src/convert.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MiniVHD Minimalist VHD implementation in C. 3 | * 4 | * This file is part of the MiniVHD Project. 5 | * 6 | * Version: @(#)convert.c 1.0.2 2021/04/16 7 | * 8 | * Authors: Sherman Perry, 9 | * Fred N. van Kempen, 10 | * 11 | * Copyright 2019-2021 Sherman Perry. 12 | * Copyright 2021 Fred N. van Kempen. 13 | * 14 | * MIT License 15 | * 16 | * Permission is hereby granted, free of charge, to any person 17 | * obtaining a copy of this software and associated documenta- 18 | * tion files (the "Software"), to deal in the Software without 19 | * restriction, including without limitation the rights to use, 20 | * copy, modify, merge, publish, distribute, sublicense, and/or 21 | * sell copies of the Software, and to permit persons to whom 22 | * the Software is furnished to do so, subject to the following 23 | * conditions: 24 | * 25 | * The above copyright notice and this permission notice shall 26 | * be included in all copies or substantial portions of the 27 | * Software. 28 | * 29 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 30 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 31 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 32 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 33 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 34 | * FROM, OUT OF O R IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 35 | * DEALINGS IN THE SOFTWARE. 36 | */ 37 | #ifndef _FILE_OFFSET_BITS 38 | # define _FILE_OFFSET_BITS 64 39 | #endif 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #define BUILDING_LIBRARY 46 | #include "minivhd.h" 47 | #include "internal.h" 48 | 49 | 50 | static FILE * 51 | open_existing_raw_img(const char* utf8_raw_path, MVHDGeom* geom, int* err) 52 | { 53 | if (geom == NULL) { 54 | *err = MVHD_ERR_INVALID_GEOM; 55 | return NULL; 56 | } 57 | 58 | FILE *raw_img = mvhd_fopen(utf8_raw_path, "rb", err); 59 | if (raw_img == NULL) { 60 | *err = MVHD_ERR_FILE; 61 | return NULL; 62 | } 63 | 64 | mvhd_fseeko64(raw_img, 0, SEEK_END); 65 | uint64_t size_bytes = (uint64_t)mvhd_ftello64(raw_img); 66 | MVHDGeom new_geom = mvhd_calculate_geometry(size_bytes); 67 | if (mvhd_calc_size_bytes(&new_geom) != size_bytes) { 68 | *err = MVHD_ERR_CONV_SIZE; 69 | return NULL; 70 | } 71 | geom->cyl = new_geom.cyl; 72 | geom->heads = new_geom.heads; 73 | geom->spt = new_geom.spt; 74 | mvhd_fseeko64(raw_img, 0, SEEK_SET); 75 | 76 | return raw_img; 77 | } 78 | 79 | 80 | MVHDAPI MVHDMeta * 81 | mvhd_convert_to_vhd_fixed(const char* utf8_raw_path, const char* utf8_vhd_path, int* err) 82 | { 83 | MVHDGeom geom; 84 | 85 | FILE *raw_img = open_existing_raw_img(utf8_raw_path, &geom, err); 86 | if (raw_img == NULL) { 87 | return NULL; 88 | } 89 | 90 | uint64_t size_in_bytes = mvhd_calc_size_bytes(&geom); 91 | MVHDMeta *vhdm = mvhd_create_fixed_raw(utf8_vhd_path, raw_img, size_in_bytes, &geom, err, NULL); 92 | if (vhdm == NULL) { 93 | return NULL; 94 | } 95 | 96 | return vhdm; 97 | } 98 | 99 | 100 | MVHDAPI MVHDMeta * 101 | mvhd_convert_to_vhd_sparse(const char* utf8_raw_path, const char* utf8_vhd_path, int* err) 102 | { 103 | MVHDGeom geom; 104 | MVHDMeta *vhdm = NULL; 105 | 106 | FILE *raw_img = open_existing_raw_img(utf8_raw_path, &geom, err); 107 | if (raw_img == NULL) { 108 | return NULL; 109 | } 110 | 111 | vhdm = mvhd_create_sparse(utf8_vhd_path, geom, err); 112 | if (vhdm == NULL) { 113 | goto end; 114 | } 115 | 116 | uint8_t buff[4096] = {0}; // 8 sectors 117 | uint8_t empty_buff[4096] = {0}; 118 | int total_sectors = mvhd_calc_size_sectors(&geom); 119 | int copy_sect = 0, i; 120 | 121 | for (i = 0; i < total_sectors; i += 8) { 122 | copy_sect = 8; 123 | if ((i + 8) >= total_sectors) { 124 | copy_sect = total_sectors - i; 125 | memset(buff, 0, sizeof buff); 126 | } 127 | fread(buff, MVHD_SECTOR_SIZE, copy_sect, raw_img); 128 | 129 | /* Only write data if there's data to write, to take advantage of the sparse VHD format */ 130 | if (memcmp(buff, empty_buff, sizeof buff) != 0) { 131 | mvhd_write_sectors(vhdm, i, copy_sect, buff); 132 | } 133 | } 134 | end: 135 | fclose(raw_img); 136 | 137 | return vhdm; 138 | } 139 | 140 | 141 | MVHDAPI FILE * 142 | mvhd_convert_to_raw(const char* utf8_vhd_path, const char* utf8_raw_path, int *err) 143 | { 144 | FILE *raw_img = mvhd_fopen(utf8_raw_path, "wb", err); 145 | if (raw_img == NULL) { 146 | return NULL; 147 | } 148 | 149 | MVHDMeta *vhdm = mvhd_open(utf8_vhd_path, true, err); 150 | if (vhdm == NULL) { 151 | fclose(raw_img); 152 | return NULL; 153 | } 154 | 155 | uint8_t buff[4096] = {0}; // 8 sectors 156 | int total_sectors = mvhd_calc_size_sectors((MVHDGeom*)&vhdm->footer.geom); 157 | int copy_sect = 0, i; 158 | for (i = 0; i < total_sectors; i += 8) { 159 | copy_sect = 8; 160 | if ((i + 8) >= total_sectors) { 161 | copy_sect = total_sectors - i; 162 | } 163 | mvhd_read_sectors(vhdm, i, copy_sect, buff); 164 | fwrite(buff, MVHD_SECTOR_SIZE, copy_sect, raw_img); 165 | } 166 | mvhd_close(vhdm); 167 | mvhd_fseeko64(raw_img, 0, SEEK_SET); 168 | 169 | return raw_img; 170 | } 171 | -------------------------------------------------------------------------------- /src/create.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MiniVHD Minimalist VHD implementation in C. 3 | * 4 | * This file is part of the MiniVHD Project. 5 | * 6 | * Version: @(#)create.c 1.0.3 2021/04/16 7 | * 8 | * Authors: Sherman Perry, 9 | * Fred N. van Kempen, 10 | * 11 | * Copyright 2019-2021 Sherman Perry. 12 | * Copyright 2021 Fred N. van Kempen. 13 | * 14 | * MIT License 15 | * 16 | * Permission is hereby granted, free of charge, to any person 17 | * obtaining a copy of this software and associated documenta- 18 | * tion files (the "Software"), to deal in the Software without 19 | * restriction, including without limitation the rights to use, 20 | * copy, modify, merge, publish, distribute, sublicense, and/or 21 | * sell copies of the Software, and to permit persons to whom 22 | * the Software is furnished to do so, subject to the following 23 | * conditions: 24 | * 25 | * The above copyright notice and this permission notice shall 26 | * be included in all copies or substantial portions of the 27 | * Software. 28 | * 29 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 30 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 31 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 32 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 33 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 34 | * FROM, OUT OF O R IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 35 | * DEALINGS IN THE SOFTWARE. 36 | */ 37 | #ifndef _FILE_OFFSET_BITS 38 | # define _FILE_OFFSET_BITS 64 39 | #endif 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #define BUILDING_LIBRARY 47 | #include "minivhd.h" 48 | #include "internal.h" 49 | #include "cwalk.h" 50 | #include "xml2_encoding.h" 51 | 52 | 53 | static const char MVHD_CONECTIX_COOKIE[] = "conectix"; 54 | static const char MVHD_CREATOR[] = "mVHD"; 55 | static const char MVHD_CREATOR_HOST_OS[] = "Wi2k"; 56 | static const char MVHD_CXSPARSE_COOKIE[] = "cxsparse"; 57 | 58 | 59 | /** 60 | * \brief Populate a VHD footer 61 | * 62 | * \param [in] footer to populate 63 | * \param [in] size_in_bytes is the total size of the virtual hard disk in bytes 64 | * \param [in] geom to use 65 | * \param [in] type of HVD that is being created 66 | * \param [in] sparse_header_off, an absolute file offset to the sparse header. Not used for fixed VHD images 67 | */ 68 | static void 69 | gen_footer(MVHDFooter* footer, uint64_t size_in_bytes, MVHDGeom* geom, MVHDType type, uint64_t sparse_header_off) 70 | { 71 | memcpy(footer->cookie, MVHD_CONECTIX_COOKIE, sizeof footer->cookie); 72 | footer->features = 0x00000002; 73 | footer->fi_fmt_vers = 0x00010000; 74 | footer->data_offset = (type == MVHD_TYPE_DIFF || type == MVHD_TYPE_DYNAMIC) ? sparse_header_off : 0xffffffffffffffff; 75 | footer->timestamp = vhd_calc_timestamp(); 76 | memcpy(footer->cr_app, MVHD_CREATOR, sizeof footer->cr_app); 77 | footer->cr_vers = 0x000e0000; 78 | memcpy(footer->cr_host_os, MVHD_CREATOR_HOST_OS, sizeof footer->cr_host_os); 79 | footer->orig_sz = footer->curr_sz = size_in_bytes; 80 | footer->geom.cyl = geom->cyl; 81 | footer->geom.heads = geom->heads; 82 | footer->geom.spt = geom->spt; 83 | footer->disk_type = type; 84 | 85 | mvhd_generate_uuid(footer->uuid); 86 | 87 | footer->checksum = mvhd_gen_footer_checksum(footer); 88 | } 89 | 90 | 91 | /** 92 | * \brief Populate a VHD sparse header 93 | * 94 | * \param [in] header for sparse and differencing images 95 | * \param [in] num_blks is the number of data blocks that the image contains 96 | * \param [in] bat_offset is the absolute file offset for start of the Block Allocation Table 97 | * \param [in] block_size_in_sectors is the block size in sectors. 98 | */ 99 | static void 100 | gen_sparse_header(MVHDSparseHeader* header, uint32_t num_blks, uint64_t bat_offset, uint32_t block_size_in_sectors) 101 | { 102 | memcpy(header->cookie, MVHD_CXSPARSE_COOKIE, sizeof header->cookie); 103 | header->data_offset = 0xffffffffffffffff; 104 | header->bat_offset = bat_offset; 105 | header->head_vers = 0x00010000; 106 | header->max_bat_ent = num_blks; 107 | header->block_sz = block_size_in_sectors * (uint32_t)MVHD_SECTOR_SIZE; 108 | header->checksum = mvhd_gen_sparse_checksum(header); 109 | } 110 | 111 | 112 | /** 113 | * \brief Generate parent locators for differencing VHD images 114 | * 115 | * \param [in] header the sparse header to populate with parent locator entries 116 | * \param [in] child_path is the full path to the VHD being created 117 | * \param [in] par_path is the full path to the parent image 118 | * \param [in] start_offset is the absolute file offset from where to start storing the entries themselves. Must be sector aligned. 119 | * \param [out] w2ku_path_buff is a buffer containing the full path to the parent, encoded as UTF16-LE 120 | * \param [out] w2ru_path_buff is a buffer containing the relative path to the parent, encoded as UTF16-LE 121 | * \param [out] err indicates what error occurred, if any 122 | * 123 | * \retval 0 if success 124 | * \retval < 0 if an error occurrs. Check value of *err for actual error 125 | */ 126 | static int 127 | gen_par_loc(MVHDSparseHeader* header, const char* child_path, 128 | const char* par_path, uint64_t start_offset, 129 | mvhd_utf16* w2ku_path_buff, mvhd_utf16* w2ru_path_buff, 130 | MVHDError* err) 131 | { 132 | /* Get our paths to store in the differencing VHD. We want both the absolute path to the parent, 133 | as well as the relative path from the child VHD */ 134 | int rv = 0; 135 | char* par_filename; 136 | size_t par_fn_len; 137 | char rel_path[MVHD_MAX_PATH_BYTES] = {0}; 138 | char child_dir[MVHD_MAX_PATH_BYTES] = {0}; 139 | size_t child_dir_len; 140 | 141 | if (strlen(child_path) < sizeof child_dir) { 142 | strcpy(child_dir, child_path); 143 | } else { 144 | *err = MVHD_ERR_PATH_LEN; 145 | rv = -1; 146 | goto end; 147 | } 148 | 149 | cwk_path_get_basename(par_path, (const char**)&par_filename, &par_fn_len); 150 | cwk_path_get_dirname(child_dir, &child_dir_len); 151 | child_dir[child_dir_len] = '\0'; 152 | size_t rel_len = cwk_path_get_relative(child_dir, par_path, rel_path, sizeof rel_path); 153 | if (rel_len > sizeof rel_path) { 154 | *err = MVHD_ERR_PATH_LEN; 155 | rv = -1; 156 | goto end; 157 | } 158 | 159 | /* We have our paths, now store the parent filename directly in the sparse header. */ 160 | int outlen = sizeof header->par_utf16_name; 161 | int utf_ret; 162 | utf_ret = UTF8ToUTF16BE((unsigned char*)header->par_utf16_name, &outlen, (const unsigned char*)par_filename, (int*)&par_fn_len); 163 | if (utf_ret < 0) { 164 | mvhd_set_encoding_err(utf_ret, (int*)err); 165 | rv = -1; 166 | goto end; 167 | } 168 | 169 | /* And encode the paths to UTF16-LE */ 170 | size_t par_path_len = strlen(par_path); 171 | outlen = sizeof *w2ku_path_buff * MVHD_MAX_PATH_CHARS; 172 | utf_ret = UTF8ToUTF16LE((unsigned char*)w2ku_path_buff, &outlen, (const unsigned char*)par_path, (int*)&par_path_len); 173 | if (utf_ret < 0) { 174 | mvhd_set_encoding_err(utf_ret, (int*)err); 175 | rv = -1; 176 | goto end; 177 | } 178 | int w2ku_len = utf_ret; 179 | outlen = sizeof *w2ru_path_buff * MVHD_MAX_PATH_CHARS; 180 | utf_ret = UTF8ToUTF16LE((unsigned char*)w2ru_path_buff, &outlen, (const unsigned char*)rel_path, (int*)&rel_len); 181 | if (utf_ret < 0) { 182 | mvhd_set_encoding_err(utf_ret, (int*)err); 183 | rv = -1; 184 | goto end; 185 | } 186 | int w2ru_len = utf_ret; 187 | 188 | /** 189 | * Finally populate the parent locaters in the sparse header. 190 | * This is the information needed to find the paths saved elsewhere 191 | * in the VHD image 192 | */ 193 | 194 | /* Note about the plat_data_space field: The VHD spec says this field stores the number of sectors needed to store the locator path. 195 | * However, Hyper-V and VPC store the number of bytes, not the number of sectors, and will refuse to open VHDs which have the 196 | * number of sectors in this field. 197 | * See https://stackoverflow.com/questions/40760181/mistake-in-virtual-hard-disk-image-format-specification 198 | */ 199 | header->par_loc_entry[0].plat_code = MVHD_DIF_LOC_W2KU; 200 | header->par_loc_entry[0].plat_data_len = (uint32_t)w2ku_len; 201 | header->par_loc_entry[0].plat_data_offset = (uint64_t)start_offset; 202 | header->par_loc_entry[0].plat_data_space = ((header->par_loc_entry[0].plat_data_len / MVHD_SECTOR_SIZE) + 1) * MVHD_SECTOR_SIZE; 203 | header->par_loc_entry[1].plat_code = MVHD_DIF_LOC_W2RU; 204 | header->par_loc_entry[1].plat_data_len = (uint32_t)w2ru_len; 205 | header->par_loc_entry[1].plat_data_offset = (uint64_t)start_offset + ((uint64_t)header->par_loc_entry[0].plat_data_space); 206 | header->par_loc_entry[1].plat_data_space = ((header->par_loc_entry[1].plat_data_len / MVHD_SECTOR_SIZE) + 1) * MVHD_SECTOR_SIZE; 207 | goto end; 208 | 209 | end: 210 | return rv; 211 | } 212 | 213 | 214 | MVHDAPI MVHDMeta * 215 | mvhd_create_fixed(const char* path, MVHDGeom geom, int* err, mvhd_progress_callback progress_callback) 216 | { 217 | uint64_t size_in_bytes = mvhd_calc_size_bytes(&geom); 218 | 219 | return mvhd_create_fixed_raw(path, NULL, size_in_bytes, &geom, err, progress_callback); 220 | } 221 | 222 | 223 | /** 224 | * \brief internal function that implements public mvhd_create_fixed() functionality 225 | * 226 | * Contains one more parameter than the public function, to allow using an existing 227 | * raw disk image as the data source for the new fixed VHD. 228 | * 229 | * \param [in] raw_image file handle to a raw disk image to populate VHD 230 | */ 231 | MVHDMeta * 232 | mvhd_create_fixed_raw(const char* path, FILE* raw_img, uint64_t size_in_bytes, MVHDGeom* geom, int* err, mvhd_progress_callback progress_callback) 233 | { 234 | uint8_t img_data[MVHD_SECTOR_SIZE] = {0}; 235 | uint8_t footer_buff[MVHD_FOOTER_SIZE] = {0}; 236 | 237 | if (geom == NULL || (geom->cyl == 0 || geom->heads == 0 || geom->spt == 0)) { 238 | *err = MVHD_ERR_INVALID_GEOM; 239 | return NULL; 240 | } 241 | 242 | MVHDMeta* vhdm = calloc(1, sizeof *vhdm); 243 | if (vhdm == NULL) { 244 | *err = MVHD_ERR_MEM; 245 | goto end; 246 | } 247 | 248 | FILE* f = mvhd_fopen(path, "wb+", err); 249 | if (f == NULL) { 250 | goto cleanup_vhdm; 251 | } 252 | mvhd_fseeko64(f, 0, SEEK_SET); 253 | 254 | uint32_t size_sectors = (uint32_t)(size_in_bytes / MVHD_SECTOR_SIZE); 255 | uint32_t s; 256 | 257 | if (progress_callback) 258 | progress_callback(0, size_sectors); 259 | 260 | if (raw_img != NULL) { 261 | mvhd_fseeko64(raw_img, 0, SEEK_END); 262 | uint64_t raw_size = (uint64_t)mvhd_ftello64(raw_img); 263 | MVHDGeom raw_geom = mvhd_calculate_geometry(raw_size); 264 | if (mvhd_calc_size_bytes(&raw_geom) != raw_size) { 265 | *err = MVHD_ERR_CONV_SIZE; 266 | goto cleanup_vhdm; 267 | } 268 | gen_footer(&vhdm->footer, raw_size, geom, MVHD_TYPE_FIXED, 0); 269 | mvhd_fseeko64(raw_img, 0, SEEK_SET); 270 | for (s = 0; s < size_sectors; s++) { 271 | fread(img_data, sizeof img_data, 1, raw_img); 272 | fwrite(img_data, sizeof img_data, 1, f); 273 | if (progress_callback) 274 | progress_callback(s + 1, size_sectors); 275 | } 276 | } else { 277 | gen_footer(&vhdm->footer, size_in_bytes, geom, MVHD_TYPE_FIXED, 0); 278 | for (s = 0; s < size_sectors; s++) { 279 | fwrite(img_data, sizeof img_data, 1, f); 280 | if (progress_callback) 281 | progress_callback(s + 1, size_sectors); 282 | } 283 | } 284 | mvhd_footer_to_buffer(&vhdm->footer, footer_buff); 285 | fwrite(footer_buff, sizeof footer_buff, 1, f); 286 | fclose(f); 287 | f = NULL; 288 | free(vhdm); 289 | vhdm = mvhd_open(path, false, err); 290 | goto end; 291 | 292 | cleanup_vhdm: 293 | free(vhdm); 294 | vhdm = NULL; 295 | 296 | end: 297 | return vhdm; 298 | } 299 | 300 | 301 | /** 302 | * \brief Create sparse or differencing VHD image. 303 | * 304 | * \param [in] path is the absolute path to the VHD file to create 305 | * \param [in] par_path is the absolute path to a parent image. If NULL, a sparse image is created, otherwise create a differencing image 306 | * \param [in] size_in_bytes is the total size in bytes of the virtual hard disk image 307 | * \param [in] geom is the HDD geometry of the image to create. Determines final image size 308 | * \param [in] block_size_in_sectors is the block size in sectors 309 | * \param [out] err indicates what error occurred, if any 310 | * 311 | * \return NULL if an error occurrs. Check value of *err for actual error. Otherwise returns pointer to a MVHDMeta struct 312 | */ 313 | static MVHDMeta * 314 | 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) 315 | { 316 | uint8_t footer_buff[MVHD_FOOTER_SIZE] = {0}; 317 | uint8_t sparse_buff[MVHD_SPARSE_SIZE] = {0}; 318 | uint8_t bat_sect[MVHD_SECTOR_SIZE]; 319 | MVHDGeom par_geom = {0}; 320 | memset(bat_sect, 0xffffffff, sizeof bat_sect); 321 | MVHDMeta* vhdm = NULL; 322 | MVHDMeta* par_vhdm = NULL; 323 | mvhd_utf16* w2ku_path_buff = NULL; 324 | mvhd_utf16* w2ru_path_buff = NULL; 325 | uint32_t par_mod_timestamp = 0; 326 | 327 | if (par_path != NULL) { 328 | par_mod_timestamp = mvhd_file_mod_timestamp(par_path, err); 329 | if (*err != 0) { 330 | goto end; 331 | } 332 | par_vhdm = mvhd_open(par_path, true, err); 333 | if (par_vhdm == NULL) { 334 | goto end; 335 | } 336 | } 337 | 338 | vhdm = calloc(1, sizeof *vhdm); 339 | if (vhdm == NULL) { 340 | *err = MVHD_ERR_MEM; 341 | goto cleanup_par_vhdm; 342 | } 343 | if (par_vhdm != NULL) { 344 | /* We use the geometry from the parent VHD, not what was passed in */ 345 | par_geom.cyl = par_vhdm->footer.geom.cyl; 346 | par_geom.heads = par_vhdm->footer.geom.heads; 347 | par_geom.spt = par_vhdm->footer.geom.spt; 348 | geom = &par_geom; 349 | size_in_bytes = par_vhdm->footer.curr_sz; 350 | } else if (geom == NULL || (geom->cyl == 0 || geom->heads == 0 || geom->spt == 0)) { 351 | *err = MVHD_ERR_INVALID_GEOM; 352 | goto cleanup_vhdm; 353 | } 354 | 355 | FILE* f = mvhd_fopen(path, "wb+", err); 356 | if (f == NULL) { 357 | goto cleanup_vhdm; 358 | } 359 | mvhd_fseeko64(f, 0, SEEK_SET); 360 | 361 | /* Note, the sparse header follows the footer copy at the beginning of the file */ 362 | if (par_path == NULL) { 363 | gen_footer(&vhdm->footer, size_in_bytes, geom, MVHD_TYPE_DYNAMIC, MVHD_FOOTER_SIZE); 364 | } else { 365 | gen_footer(&vhdm->footer, size_in_bytes, geom, MVHD_TYPE_DIFF, MVHD_FOOTER_SIZE); 366 | } 367 | mvhd_footer_to_buffer(&vhdm->footer, footer_buff); 368 | 369 | /* As mentioned, start with a copy of the footer */ 370 | fwrite(footer_buff, sizeof footer_buff, 1, f); 371 | 372 | /** 373 | * Calculate the number of (2MB or 512KB) data blocks required to store the entire 374 | * contents of the disk image, followed by the number of sectors the 375 | * BAT occupies in the image. Note, the BAT is sector aligned, and is padded 376 | * to the next sector boundary 377 | * */ 378 | uint32_t size_in_sectors = (uint32_t)(size_in_bytes / MVHD_SECTOR_SIZE); 379 | uint32_t num_blks = size_in_sectors / block_size_in_sectors; 380 | if (size_in_sectors % block_size_in_sectors != 0) { 381 | num_blks += 1; 382 | } 383 | uint32_t num_bat_sect = num_blks / MVHD_BAT_ENT_PER_SECT; 384 | if (num_blks % MVHD_BAT_ENT_PER_SECT != 0) { 385 | num_bat_sect += 1; 386 | } 387 | /* Storing the BAT directly following the footer and header */ 388 | uint64_t bat_offset = MVHD_FOOTER_SIZE + MVHD_SPARSE_SIZE; 389 | uint64_t par_loc_offset = 0; 390 | 391 | /** 392 | * If creating a differencing VHD, populate the sparse header with additional 393 | * data about the parent image, and where to find it, and it's last modified timestamp 394 | * */ 395 | if (par_vhdm != NULL) { 396 | /** 397 | * Create output buffers to encode paths into. 398 | * The paths are not stored directly in the sparse header, hence the need to 399 | * store them in buffers to be written to the VHD image later 400 | */ 401 | w2ku_path_buff = calloc(MVHD_MAX_PATH_CHARS, sizeof * w2ku_path_buff); 402 | if (w2ku_path_buff == NULL) { 403 | *err = MVHD_ERR_MEM; 404 | goto end; 405 | } 406 | w2ru_path_buff = calloc(MVHD_MAX_PATH_CHARS, sizeof * w2ru_path_buff); 407 | if (w2ru_path_buff == NULL) { 408 | *err = MVHD_ERR_MEM; 409 | goto end; 410 | } 411 | memcpy(vhdm->sparse.par_uuid, par_vhdm->footer.uuid, sizeof vhdm->sparse.par_uuid); 412 | par_loc_offset = bat_offset + ((uint64_t)num_bat_sect * MVHD_SECTOR_SIZE) + (5 * MVHD_SECTOR_SIZE); 413 | if (gen_par_loc(&vhdm->sparse, path, par_path, par_loc_offset, w2ku_path_buff, w2ru_path_buff, (MVHDError*)err) < 0) { 414 | goto cleanup_vhdm; 415 | } 416 | vhdm->sparse.par_timestamp = par_mod_timestamp; 417 | } 418 | gen_sparse_header(&vhdm->sparse, num_blks, bat_offset, block_size_in_sectors); 419 | mvhd_header_to_buffer(&vhdm->sparse, sparse_buff); 420 | fwrite(sparse_buff, sizeof sparse_buff, 1, f); 421 | 422 | /* The BAT sectors need to be filled with 0xffffffff */ 423 | uint32_t k; 424 | for (k = 0; k < num_bat_sect; k++) { 425 | fwrite(bat_sect, sizeof bat_sect, 1, f); 426 | } 427 | mvhd_write_empty_sectors(f, 5); 428 | 429 | /** 430 | * If creating a differencing VHD, the paths to the parent image need to be written 431 | * tp the file. Both absolute and relative paths are written 432 | * */ 433 | if (par_vhdm != NULL) { 434 | uint64_t curr_pos = (uint64_t)mvhd_ftello64(f); 435 | 436 | /* Double check my sums... */ 437 | assert(curr_pos == par_loc_offset); 438 | 439 | /* Fill the space required for location data with zero */ 440 | uint8_t empty_sect[MVHD_SECTOR_SIZE] = {0}; 441 | int i; 442 | uint32_t j; 443 | 444 | for (i = 0; i < 2; i++) { 445 | for (j = 0; j < (vhdm->sparse.par_loc_entry[i].plat_data_space / MVHD_SECTOR_SIZE); j++) { 446 | fwrite(empty_sect, sizeof empty_sect, 1, f); 447 | } 448 | } 449 | 450 | /* Now write the location entries */ 451 | mvhd_fseeko64(f, vhdm->sparse.par_loc_entry[0].plat_data_offset, SEEK_SET); 452 | fwrite(w2ku_path_buff, vhdm->sparse.par_loc_entry[0].plat_data_len, 1, f); 453 | mvhd_fseeko64(f, vhdm->sparse.par_loc_entry[1].plat_data_offset, SEEK_SET); 454 | fwrite(w2ru_path_buff, vhdm->sparse.par_loc_entry[1].plat_data_len, 1, f); 455 | 456 | /* and reset the file position to continue */ 457 | mvhd_fseeko64(f, vhdm->sparse.par_loc_entry[1].plat_data_offset + vhdm->sparse.par_loc_entry[1].plat_data_space, SEEK_SET); 458 | mvhd_write_empty_sectors(f, 5); 459 | } 460 | 461 | /* And finish with the footer */ 462 | fwrite(footer_buff, sizeof footer_buff, 1, f); 463 | fclose(f); 464 | f = NULL; 465 | free(vhdm); 466 | vhdm = mvhd_open(path, false, err); 467 | goto end; 468 | 469 | cleanup_vhdm: 470 | free(vhdm); 471 | vhdm = NULL; 472 | 473 | cleanup_par_vhdm: 474 | if (par_vhdm != NULL) { 475 | mvhd_close(par_vhdm); 476 | } 477 | 478 | end: 479 | free(w2ku_path_buff); 480 | free(w2ru_path_buff); 481 | 482 | return vhdm; 483 | } 484 | 485 | 486 | MVHDAPI MVHDMeta * 487 | mvhd_create_sparse(const char* path, MVHDGeom geom, int* err) 488 | { 489 | uint64_t size_in_bytes = mvhd_calc_size_bytes(&geom); 490 | 491 | return create_sparse_diff(path, NULL, size_in_bytes, &geom, MVHD_BLOCK_LARGE, err); 492 | } 493 | 494 | 495 | MVHDAPI MVHDMeta * 496 | mvhd_create_diff(const char* path, const char* par_path, int* err) 497 | { 498 | return create_sparse_diff(path, par_path, 0, NULL, MVHD_BLOCK_LARGE, err); 499 | } 500 | 501 | 502 | MVHDAPI MVHDMeta * 503 | mvhd_create_ex(MVHDCreationOptions options, int* err) 504 | { 505 | uint32_t geom_sector_size; 506 | 507 | switch (options.type) { 508 | case MVHD_TYPE_FIXED: 509 | case MVHD_TYPE_DYNAMIC: 510 | geom_sector_size = mvhd_calc_size_sectors(&(options.geometry)); 511 | if ((options.size_in_bytes > 0 && (options.size_in_bytes % MVHD_SECTOR_SIZE) > 0) 512 | || (options.size_in_bytes > MVHD_MAX_SIZE_IN_BYTES) 513 | || (options.size_in_bytes == 0 && geom_sector_size == 0)) { 514 | *err = MVHD_ERR_INVALID_SIZE; 515 | return NULL; 516 | } 517 | 518 | if (options.size_in_bytes > 0 && ((uint64_t)geom_sector_size * MVHD_SECTOR_SIZE) > options.size_in_bytes) { 519 | *err = MVHD_ERR_INVALID_GEOM; 520 | return NULL; 521 | } 522 | 523 | if (options.size_in_bytes == 0) 524 | options.size_in_bytes = (uint64_t)geom_sector_size * MVHD_SECTOR_SIZE; 525 | 526 | if (geom_sector_size == 0) 527 | options.geometry = mvhd_calculate_geometry(options.size_in_bytes); 528 | break; 529 | 530 | case MVHD_TYPE_DIFF: 531 | if (options.parent_path == NULL) { 532 | *err = MVHD_ERR_FILE; 533 | return NULL; 534 | } 535 | break; 536 | 537 | default: 538 | *err = MVHD_ERR_TYPE; 539 | return NULL; 540 | } 541 | 542 | if (options.path == NULL) { 543 | *err = MVHD_ERR_FILE; 544 | return NULL; 545 | } 546 | 547 | if (options.type != MVHD_TYPE_FIXED) { 548 | if (options.block_size_in_sectors == MVHD_BLOCK_DEFAULT) 549 | options.block_size_in_sectors = MVHD_BLOCK_LARGE; 550 | 551 | if (options.block_size_in_sectors != MVHD_BLOCK_LARGE && options.block_size_in_sectors != MVHD_BLOCK_SMALL) { 552 | *err = MVHD_ERR_INVALID_BLOCK_SIZE; 553 | return NULL; 554 | } 555 | } 556 | 557 | switch (options.type) { 558 | case MVHD_TYPE_FIXED: 559 | return mvhd_create_fixed_raw(options.path, NULL, options.size_in_bytes, &(options.geometry), err, options.progress_callback); 560 | 561 | case MVHD_TYPE_DYNAMIC: 562 | return create_sparse_diff(options.path, NULL, options.size_in_bytes, &(options.geometry), options.block_size_in_sectors, err); 563 | 564 | case MVHD_TYPE_DIFF: 565 | return create_sparse_diff(options.path, options.parent_path, 0, NULL, options.block_size_in_sectors, err); 566 | } 567 | 568 | return NULL; /* Make the compiler happy */ 569 | } 570 | 571 | 572 | bool 573 | mvhd_is_conectix_str(const void* buffer) 574 | { 575 | if (strncmp(buffer, MVHD_CONECTIX_COOKIE, strlen(MVHD_CONECTIX_COOKIE)) == 0) { 576 | return true; 577 | } 578 | 579 | return false; 580 | } 581 | -------------------------------------------------------------------------------- /src/cwalk.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shermp/MiniVHD/bd02b4ad42b3cd4fa1ae3ef38f9f698edfc06582/src/cwalk.c -------------------------------------------------------------------------------- /src/cwalk.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shermp/MiniVHD/bd02b4ad42b3cd4fa1ae3ef38f9f698edfc06582/src/cwalk.h -------------------------------------------------------------------------------- /src/internal.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MiniVHD Minimalist VHD implementation in C. 3 | * 4 | * This file is part of the MiniVHD Project. 5 | * 6 | * Internal definitions. 7 | * 8 | * Version: @(#)internal.h 1.0.1 2021/03/15 9 | * 10 | * Author: Sherman Perry, 11 | * 12 | * Copyright 2019-2021 Sherman Perry. 13 | * 14 | * MIT License 15 | * 16 | * Permission is hereby granted, free of charge, to any person 17 | * obtaining a copy of this software and associated documenta- 18 | * tion files (the "Software"), to deal in the Software without 19 | * restriction, including without limitation the rights to use, 20 | * copy, modify, merge, publish, distribute, sublicense, and/or 21 | * sell copies of the Software, and to permit persons to whom 22 | * the Software is furnished to do so, subject to the following 23 | * conditions: 24 | * 25 | * The above copyright notice and this permission notice shall 26 | * be included in all copies or substantial portions of the 27 | * Software. 28 | * 29 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 30 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 31 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 32 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 33 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 34 | * FROM, OUT OF O R IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 35 | * DEALINGS IN THE SOFTWARE. 36 | */ 37 | #ifndef MINIVHD_INTERNAL_H 38 | # define MINIVHD_INTERNAL_H 39 | 40 | 41 | #define MVHD_FOOTER_SIZE 512 42 | #define MVHD_SPARSE_SIZE 1024 43 | 44 | #define MVHD_SECTOR_SIZE 512 45 | #define MVHD_BAT_ENT_PER_SECT 128 46 | 47 | #define MVHD_MAX_SIZE_IN_BYTES 0x1fe00000000 48 | 49 | #define MVHD_SPARSE_BLK 0xffffffff 50 | 51 | /* For simplicity, we don't handle paths longer than this 52 | * Note, this is the max path in characters, as that is what 53 | * Windows uses 54 | */ 55 | #define MVHD_MAX_PATH_CHARS 260 56 | #define MVHD_MAX_PATH_BYTES 1040 57 | 58 | #define MVHD_DIF_LOC_W2RU 0x57327275 59 | #define MVHD_DIF_LOC_W2KU 0x57326B75 60 | 61 | #define MVHD_START_TS 946684800 62 | 63 | 64 | typedef struct MVHDSectorBitmap { 65 | uint8_t* curr_bitmap; 66 | int sector_count; 67 | int curr_block; 68 | } MVHDSectorBitmap; 69 | 70 | typedef struct MVHDFooter { 71 | uint8_t cookie[8]; 72 | uint32_t features; 73 | uint32_t fi_fmt_vers; 74 | uint64_t data_offset; 75 | uint32_t timestamp; 76 | uint8_t cr_app[4]; 77 | uint32_t cr_vers; 78 | uint8_t cr_host_os[4]; 79 | uint64_t orig_sz; 80 | uint64_t curr_sz; 81 | struct { 82 | uint16_t cyl; 83 | uint8_t heads; 84 | uint8_t spt; 85 | } geom; 86 | uint32_t disk_type; 87 | uint32_t checksum; 88 | uint8_t uuid[16]; 89 | uint8_t saved_st; 90 | uint8_t reserved[427]; 91 | } MVHDFooter; 92 | 93 | typedef struct MVHDSparseHeader { 94 | uint8_t cookie[8]; 95 | uint64_t data_offset; 96 | uint64_t bat_offset; 97 | uint32_t head_vers; 98 | uint32_t max_bat_ent; 99 | uint32_t block_sz; 100 | uint32_t checksum; 101 | uint8_t par_uuid[16]; 102 | uint32_t par_timestamp; 103 | uint32_t reserved_1; 104 | uint8_t par_utf16_name[512]; 105 | struct { 106 | uint32_t plat_code; 107 | uint32_t plat_data_space; 108 | uint32_t plat_data_len; 109 | uint32_t reserved; 110 | uint64_t plat_data_offset; 111 | } par_loc_entry[8]; 112 | uint8_t reserved_2[256]; 113 | } MVHDSparseHeader; 114 | 115 | struct MVHDMeta { 116 | FILE* f; 117 | bool readonly; 118 | char filename[MVHD_MAX_PATH_BYTES]; 119 | struct MVHDMeta* parent; 120 | MVHDFooter footer; 121 | MVHDSparseHeader sparse; 122 | uint32_t* block_offset; 123 | int sect_per_block; 124 | MVHDSectorBitmap bitmap; 125 | int (*read_sectors)(struct MVHDMeta*, uint32_t, int, void*); 126 | int (*write_sectors)(struct MVHDMeta*, uint32_t, int, void*); 127 | struct { 128 | uint8_t* zero_data; 129 | int sector_count; 130 | } format_buffer; 131 | }; 132 | 133 | 134 | #ifdef __cplusplus 135 | extern "C" { 136 | #endif 137 | 138 | /** 139 | * Functions to deal with endian issues 140 | */ 141 | uint16_t mvhd_from_be16(uint16_t val); 142 | uint32_t mvhd_from_be32(uint32_t val); 143 | uint64_t mvhd_from_be64(uint64_t val); 144 | uint16_t mvhd_to_be16(uint16_t val); 145 | uint32_t mvhd_to_be32(uint32_t val); 146 | uint64_t mvhd_to_be64(uint64_t val); 147 | 148 | /** 149 | * \brief Check if provided buffer begins with the string "conectix" 150 | * 151 | * \param [in] buffer The buffer to compare. Must be at least 8 bytes in length 152 | * 153 | * \return true if the buffer begins with "conectix" 154 | * \return false if the buffer does not begin with "conectix" 155 | */ 156 | bool mvhd_is_conectix_str(const void* buffer); 157 | 158 | /** 159 | * \brief Generate a raw 16 byte UUID 160 | * 161 | * \param [out] uuid A 16 byte buffer in which the generated UUID will be stored to 162 | */ 163 | void mvhd_generate_uuid(uint8_t *uuid); 164 | 165 | /** 166 | * \brief Calculate a VHD formatted timestamp from the current time 167 | */ 168 | uint32_t vhd_calc_timestamp(void); 169 | 170 | /** 171 | * \brief Convert an epoch timestamp to a VHD timestamp 172 | * 173 | * \param [in] ts epoch timestamp to convert. 174 | * 175 | * \return The adjusted timestamp, or 0 if the input timestamp is 176 | * earlier that 1 Janurary 2000 177 | */ 178 | uint32_t mvhd_epoch_to_vhd_ts(time_t ts); 179 | 180 | /** 181 | * \brief Return the created time from a VHD image 182 | * 183 | * \param [in] vhdm Pointer to the MiniVHD metadata structure 184 | * 185 | * \return The created time, as a Unix timestamp 186 | */ 187 | time_t vhd_get_created_time(struct MVHDMeta *vhdm); 188 | 189 | /** 190 | * \brief Cross platform, unicode filepath opening 191 | * 192 | * This function accounts for the fact that fopen() handles file paths differently compared to other 193 | * operating systems. Windows version of fopen() will not handle multi byte encoded text like UTF-8. 194 | * 195 | * Unicode filepath support on Windows requires using the _wfopen() function, which expects UTF-16LE 196 | * encoded path and modestring. 197 | * 198 | * \param [in] path The filepath to open as a UTF-8 string 199 | * \param [in] mode The mode string to use (eg: "rb+"") 200 | * \param [out] err The error value, if an error occurrs 201 | * 202 | * \return a FILE pointer if successful, NULL otherwise. If NULL, check the value of err 203 | */ 204 | FILE* mvhd_fopen(const char* path, const char* mode, int* err); 205 | 206 | void mvhd_set_encoding_err(int encoding_retval, int* err); 207 | 208 | /** 209 | * \brief Generate VHD footer checksum 210 | * 211 | * \param [in] vhdm MiniVHD data structure 212 | */ 213 | uint32_t mvhd_gen_footer_checksum(MVHDFooter* footer); 214 | 215 | /** 216 | * \brief Generate VHD sparse header checksum 217 | * 218 | * \param [in] vhdm MiniVHD data structure 219 | */ 220 | uint32_t mvhd_gen_sparse_checksum(MVHDSparseHeader* header); 221 | 222 | uint32_t mvhd_crc32_for_byte(uint32_t r); 223 | 224 | /** 225 | * \brief Get current position in file stream 226 | * 227 | * This is a portable version of the POSIX ftello64(). * 228 | */ 229 | int64_t mvhd_ftello64(FILE* stream); 230 | 231 | /** 232 | * \brief Reposition the file stream's position 233 | * 234 | * This is a portable version of the POSIX fseeko64(). * 235 | */ 236 | int mvhd_fseeko64(FILE* stream, int64_t offset, int origin); 237 | 238 | /** 239 | * \brief Calculate the CRC32 of a data buffer. 240 | * 241 | * This function can be used for verifying data integrity. 242 | * 243 | * \param [in] data The data buffer 244 | * \param [in] n_bytes The size of the data buffer in bytes 245 | * 246 | * \return The CRC32 of the data buffer 247 | */ 248 | uint32_t mvhd_crc32(const void* data, size_t n_bytes); 249 | 250 | /** 251 | * \brief Calculate the file modification timestamp. 252 | * 253 | * This function is primarily to help protect differencing VHD's 254 | * 255 | * \param [in] path the UTF-8 file path 256 | * \param [out] err The error value, if an error occurrs 257 | * 258 | * \return The file modified timestamp, in VHD compatible timestamp. 259 | * 'err' will be set to non-zero on error 260 | */ 261 | uint32_t mvhd_file_mod_timestamp(const char* path, int *err); 262 | 263 | 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); 264 | 265 | /** 266 | * \brief Write zero filled sectors to file. 267 | * 268 | * Note, the caller should set the file position before calling this 269 | * function for correct operation. 270 | * 271 | * \param [in] f File to write sectors to 272 | * \param [in] sector_count The number of sectors to write 273 | */ 274 | void mvhd_write_empty_sectors(FILE* f, int sector_count); 275 | 276 | /** 277 | * \brief Read a fixed VHD image 278 | * 279 | * Fixed VHD images are essentially raw image files with a footer tacked on 280 | * the end. They are therefore straightforward to write 281 | * 282 | * \param [in] vhdm MiniVHD data structure 283 | * \param [in] offset Sector offset to read from 284 | * \param [in] num_sectors The desired number of sectors to read 285 | * \param [out] out_buff An output buffer to store read sectors. Must be 286 | * large enough to hold num_sectors worth of sectors. 287 | * 288 | * \retval 0 num_sectors were read from file 289 | * \retval >0 < num_sectors were read from file 290 | */ 291 | int mvhd_fixed_read(struct MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff); 292 | 293 | /** 294 | * \brief Read a sparse VHD image 295 | * 296 | * Sparse, or dynamic images are VHD images that grow as data is written to them. 297 | * 298 | * This function implements the logic to read sectors from the file, taking into 299 | * account the fact that blocks may be stored on disk in any order, and that the 300 | * read could cross block boundaries. 301 | * 302 | * \param [in] vhdm MiniVHD data structure 303 | * \param [in] offset Sector offset to read from 304 | * \param [in] num_sectors The desired number of sectors to read 305 | * \param [out] out_buff An output buffer to store read sectors. Must be 306 | * large enough to hold num_sectors worth of sectors. 307 | * 308 | * \retval 0 num_sectors were read from file 309 | * \retval >0 < num_sectors were read from file 310 | */ 311 | int mvhd_sparse_read(struct MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff); 312 | 313 | /** 314 | * \brief Read a differencing VHD image 315 | * 316 | * Differencing images are a variant of a sparse image. They contain the grow-on-demand 317 | * properties of sparse images, but also reference a parent image. Data is read from the 318 | * child image only if it is newer than the data stored in the parent image. 319 | * 320 | * This function implements the logic to read sectors from the child, or a parent image. 321 | * Differencing images may have a differencing image as a parent, creating a chain of images. 322 | * There is no theoretical chain length limit, although I do not consider long chains to be 323 | * advisable. Verifying the parent-child relationship is not very robust. 324 | * 325 | * \param [in] vhdm MiniVHD data structure 326 | * \param [in] offset Sector offset to read from 327 | * \param [in] num_sectors The desired number of sectors to read 328 | * \param [out] out_buff An output buffer to store read sectors. Must be 329 | * large enough to hold num_sectors worth of sectors. 330 | * 331 | * \retval 0 num_sectors were read from file 332 | * \retval >0 < num_sectors were read from file 333 | */ 334 | int mvhd_diff_read(struct MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff); 335 | 336 | /** 337 | * \brief Write to a fixed VHD image 338 | * 339 | * Fixed VHD images are essentially raw image files with a footer tacked on 340 | * the end. They are therefore straightforward to write 341 | * 342 | * \param [in] vhdm MiniVHD data structure 343 | * \param [in] offset Sector offset to write to 344 | * \param [in] num_sectors The desired number of sectors to write 345 | * \param [in] in_buff A source buffer to write sectors from. Must be 346 | * large enough to hold num_sectors worth of sectors. 347 | * 348 | * \retval 0 num_sectors were written to file 349 | * \retval >0 < num_sectors were written to file 350 | */ 351 | int mvhd_fixed_write(struct MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff); 352 | 353 | /** 354 | * \brief Write to a sparse or differencing VHD image 355 | * 356 | * Sparse, or dynamic images are VHD images that grow as data is written to them. 357 | * 358 | * Differencing images are a variant of a sparse image. They contain the grow-on-demand 359 | * properties of sparse images, but also reference a parent image. Data is always written 360 | * to the child image. This makes writing to differencing images essentially identical to 361 | * writing to sparse images, hence they use the same function. 362 | * 363 | * This function implements the logic to write sectors to the file, taking into 364 | * account the fact that blocks may be stored on disk in any order, and that the 365 | * write operation could cross block boundaries. 366 | * 367 | * \param [in] vhdm MiniVHD data structure 368 | * \param [in] offset Sector offset to write to 369 | * \param [in] num_sectors The desired number of sectors to write 370 | * \param [in] in_buff A source buffer to write sectors from. Must be 371 | * large enough to hold num_sectors worth of sectors. 372 | * 373 | * \retval 0 num_sectors were written to file 374 | * \retval >0 < num_sectors were written to file 375 | */ 376 | int mvhd_sparse_diff_write(struct MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff); 377 | 378 | /** 379 | * \brief A no-op function to "write" to read-only VHD images 380 | * 381 | * \param [in] vhdm MiniVHD data structure 382 | * \param [in] offset Sector offset to write to 383 | * \param [in] num_sectors The desired number of sectors to write 384 | * \param [in] in_buff A source buffer to write sectors from. Must be 385 | * large enough to hold num_sectors worth of sectors. 386 | * 387 | * \retval 0 num_sectors were written to file 388 | * \retval >0 < num_sectors were written to file 389 | */ 390 | int mvhd_noop_write(struct MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff); 391 | 392 | /** 393 | * \brief Save the contents of a VHD footer from a buffer to a struct 394 | * 395 | * \param [out] footer save contents of buffer into footer 396 | * \param [in] buffer VHD footer in raw bytes 397 | */ 398 | void mvhd_buffer_to_footer(MVHDFooter* footer, uint8_t* buffer); 399 | 400 | /** 401 | * \brief Save the contents of a VHD sparse header from a buffer to a struct 402 | * 403 | * \param [out] header save contents of buffer into header 404 | * \param [in] buffer VHD header in raw bytes 405 | */ 406 | void mvhd_buffer_to_header(MVHDSparseHeader* header, uint8_t* buffer); 407 | 408 | /** 409 | * \brief Save the contents of a VHD footer struct to a buffer 410 | * 411 | * \param [in] footer save contents of struct into buffer 412 | * \param [out] buffer VHD footer in raw bytes 413 | */ 414 | void mvhd_footer_to_buffer(MVHDFooter* footer, uint8_t* buffer); 415 | 416 | /** 417 | * \brief Save the contents of a VHD sparse header struct to a buffer 418 | * 419 | * \param [in] header save contents of struct into buffer 420 | * \param [out] buffer VHD sparse header in raw bytes 421 | */ 422 | void mvhd_header_to_buffer(MVHDSparseHeader* header, uint8_t* buffer); 423 | 424 | #ifdef __cplusplus 425 | } 426 | #endif 427 | 428 | 429 | #endif /*MINIVHD_INTERNAL_H*/ 430 | -------------------------------------------------------------------------------- /src/io.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MiniVHD Minimalist VHD implementation in C. 3 | * 4 | * This file is part of the MiniVHD Project. 5 | * 6 | * Sector reading and writing implementations. 7 | * 8 | * Version: @(#)io.c 1.0.3 2021/04/16 9 | * 10 | * Author: Sherman Perry, 11 | * 12 | * Copyright 2019-2021 Sherman Perry. 13 | * 14 | * MIT License 15 | * 16 | * Permission is hereby granted, free of charge, to any person 17 | * obtaining a copy of this software and associated documenta- 18 | * tion files (the "Software"), to deal in the Software without 19 | * restriction, including without limitation the rights to use, 20 | * copy, modify, merge, publish, distribute, sublicense, and/or 21 | * sell copies of the Software, and to permit persons to whom 22 | * the Software is furnished to do so, subject to the following 23 | * conditions: 24 | * 25 | * The above copyright notice and this permission notice shall 26 | * be included in all copies or substantial portions of the 27 | * Software. 28 | * 29 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 30 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 31 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 32 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 33 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 34 | * FROM, OUT OF O R IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 35 | * DEALINGS IN THE SOFTWARE. 36 | */ 37 | #ifndef _FILE_OFFSET_BITS 38 | # define _FILE_OFFSET_BITS 64 39 | #endif 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #define BUILDING_LIBRARY 46 | #include "minivhd.h" 47 | #include "internal.h" 48 | 49 | 50 | /* 51 | * The following bit array macros adapted from: 52 | * 53 | * http://www.mathcs.emory.edu/~cheung/Courses/255/Syllabus/1-C-intro/bit-array.html 54 | */ 55 | #define VHD_SETBIT(A,k) ( A[(k>>3)] |= (0x80 >> (k&7)) ) 56 | #define VHD_CLEARBIT(A,k) ( A[(k>>3)] &= ~(0x80 >> (k&7)) ) 57 | #define VHD_TESTBIT(A,k) ( A[(k>>3)] & (0x80 >> (k&7)) ) 58 | 59 | 60 | /** 61 | * \brief Check that we will not be overflowing buffers 62 | * 63 | * \param [in] offset The offset from which we are beginning from 64 | * \param [in] num_sectors The number of sectors which we desire to read/write 65 | * \param [in] total_sectors The total number of sectors available 66 | * \param [out] transfer_sect The number of sectors to actually write. 67 | * This may be lower than num_sectors if offset + num_sectors >= total_sectors 68 | * \param [out] trunc_sectors The number of sectors truncated if transfer_sectors < num_sectors 69 | */ 70 | static inline void 71 | check_sectors(uint32_t offset, int num_sectors, uint32_t total_sectors, int* transfer_sect, int* trunc_sect) 72 | { 73 | *transfer_sect = num_sectors; 74 | *trunc_sect = 0; 75 | 76 | if ((total_sectors - offset) < (uint32_t)*transfer_sect) { 77 | *transfer_sect = total_sectors - offset; 78 | *trunc_sect = num_sectors - *transfer_sect; 79 | } 80 | } 81 | 82 | 83 | void 84 | mvhd_write_empty_sectors(FILE* f, int sector_count) 85 | { 86 | uint8_t zero_bytes[MVHD_SECTOR_SIZE] = {0}; 87 | int i; 88 | 89 | for (i = 0; i < sector_count; i++) { 90 | fwrite(zero_bytes, sizeof zero_bytes, 1, f); 91 | } 92 | } 93 | 94 | 95 | /** 96 | * \brief Read the sector bitmap for a block. 97 | * 98 | * If the block is sparse, the sector bitmap in memory will be 99 | * zeroed. Otherwise, the sector bitmap is read from the VHD file. 100 | * 101 | * \param [in] vhdm MiniVHD data structure 102 | * \param [in] blk The block for which to read the sector bitmap from 103 | */ 104 | static void 105 | read_sect_bitmap(MVHDMeta* vhdm, int blk) 106 | { 107 | if (vhdm->block_offset[blk] != MVHD_SPARSE_BLK) { 108 | mvhd_fseeko64(vhdm->f, (uint64_t)vhdm->block_offset[blk] * MVHD_SECTOR_SIZE, SEEK_SET); 109 | fread(vhdm->bitmap.curr_bitmap, vhdm->bitmap.sector_count * MVHD_SECTOR_SIZE, 1, vhdm->f); 110 | } else { 111 | memset(vhdm->bitmap.curr_bitmap, 0, vhdm->bitmap.sector_count * MVHD_SECTOR_SIZE); 112 | } 113 | 114 | vhdm->bitmap.curr_block = blk; 115 | } 116 | 117 | 118 | /** 119 | * \brief Write the current sector bitmap in memory to file 120 | * 121 | * \param [in] vhdm MiniVHD data structure 122 | */ 123 | static void 124 | write_curr_sect_bitmap(MVHDMeta* vhdm) 125 | { 126 | if (vhdm->bitmap.curr_block >= 0) { 127 | int64_t abs_offset = (int64_t)vhdm->block_offset[vhdm->bitmap.curr_block] * MVHD_SECTOR_SIZE; 128 | mvhd_fseeko64(vhdm->f, abs_offset, SEEK_SET); 129 | fwrite(vhdm->bitmap.curr_bitmap, MVHD_SECTOR_SIZE, vhdm->bitmap.sector_count, vhdm->f); 130 | } 131 | } 132 | 133 | 134 | /** 135 | * \brief Write block offset from memory into file 136 | * 137 | * \param [in] vhdm MiniVHD data structure 138 | * \param [in] blk The block for which to write the offset for 139 | */ 140 | static void 141 | write_bat_entry(MVHDMeta* vhdm, int blk) 142 | { 143 | uint64_t table_offset = vhdm->sparse.bat_offset + ((uint64_t)blk * sizeof *vhdm->block_offset); 144 | uint32_t offset = mvhd_to_be32(vhdm->block_offset[blk]); 145 | 146 | mvhd_fseeko64(vhdm->f, table_offset, SEEK_SET); 147 | fwrite(&offset, sizeof offset, 1, vhdm->f); 148 | } 149 | 150 | 151 | /** 152 | * \brief Create an empty block in a sparse or differencing VHD image 153 | * 154 | * VHD images store data in blocks, which are typically 4096 sectors in size 155 | * (~2MB). These blocks may be stored on disk in any order. Blocks are created 156 | * on demand when required. 157 | * 158 | * This function creates new, empty blocks, by replacing the footer at the end of the file 159 | * and then re-inserting the footer at the new file end. The BAT table entry for the 160 | * new block is updated with the new offset. 161 | * 162 | * \param [in] vhdm MiniVHD data structure 163 | * \param [in] blk The block number to create 164 | */ 165 | static void 166 | create_block(MVHDMeta* vhdm, int blk) 167 | { 168 | uint8_t footer[MVHD_FOOTER_SIZE]; 169 | 170 | /* Seek to where the footer SHOULD be */ 171 | mvhd_fseeko64(vhdm->f, -MVHD_FOOTER_SIZE, SEEK_END); 172 | fread(footer, sizeof footer, 1, vhdm->f); 173 | mvhd_fseeko64(vhdm->f, -MVHD_FOOTER_SIZE, SEEK_END); 174 | 175 | if (!mvhd_is_conectix_str(footer)) { 176 | /* Oh dear. We use the header instead, since something has gone wrong at the footer */ 177 | mvhd_fseeko64(vhdm->f, 0, SEEK_SET); 178 | fread(footer, sizeof footer, 1, vhdm->f); 179 | mvhd_fseeko64(vhdm->f, 0, SEEK_END); 180 | } 181 | 182 | int64_t abs_offset = mvhd_ftello64(vhdm->f); 183 | if (abs_offset % MVHD_SECTOR_SIZE != 0) { 184 | /* Yikes! We're supposed to be on a sector boundary. Add some padding */ 185 | int64_t padding_amount = (int64_t)MVHD_SECTOR_SIZE - (abs_offset % MVHD_SECTOR_SIZE); 186 | uint8_t zero_byte = 0; 187 | int i; 188 | for (i = 0; i < padding_amount; i++) { 189 | fwrite(&zero_byte, sizeof zero_byte, 1, vhdm->f); 190 | } 191 | abs_offset += padding_amount; 192 | } 193 | 194 | uint32_t sect_offset = (uint32_t)(abs_offset / MVHD_SECTOR_SIZE); 195 | int blk_size_sectors = vhdm->sparse.block_sz / MVHD_SECTOR_SIZE; 196 | mvhd_write_empty_sectors(vhdm->f, vhdm->bitmap.sector_count + blk_size_sectors); 197 | 198 | /* Add a bit of padding. That's what Windows appears to do, although it's not strictly necessary... */ 199 | mvhd_write_empty_sectors(vhdm->f, 5); 200 | 201 | /* And we finish with the footer */ 202 | fwrite(footer, sizeof footer, 1, vhdm->f); 203 | 204 | /* We no longer have a sparse block. Update that BAT! */ 205 | vhdm->block_offset[blk] = sect_offset; 206 | write_bat_entry(vhdm, blk); 207 | } 208 | 209 | 210 | int 211 | mvhd_fixed_read(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff) { 212 | int64_t addr; 213 | int transfer_sectors, truncated_sectors; 214 | uint32_t total_sectors = (uint32_t)(vhdm->footer.curr_sz / MVHD_SECTOR_SIZE); 215 | 216 | check_sectors(offset, num_sectors, total_sectors, &transfer_sectors, &truncated_sectors); 217 | 218 | addr = (int64_t)offset * MVHD_SECTOR_SIZE; 219 | mvhd_fseeko64(vhdm->f, addr, SEEK_SET); 220 | fread(out_buff, transfer_sectors*MVHD_SECTOR_SIZE, 1, vhdm->f); 221 | 222 | return truncated_sectors; 223 | } 224 | 225 | 226 | int 227 | mvhd_sparse_read(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff) 228 | { 229 | int transfer_sectors, truncated_sectors; 230 | uint32_t total_sectors = (uint32_t)(vhdm->footer.curr_sz / MVHD_SECTOR_SIZE); 231 | 232 | check_sectors(offset, num_sectors, total_sectors, &transfer_sectors, &truncated_sectors); 233 | 234 | uint8_t* buff = (uint8_t*)out_buff; 235 | int64_t addr; 236 | uint32_t s, ls; 237 | int blk, prev_blk, sib; 238 | ls = offset + transfer_sectors; 239 | prev_blk = -1; 240 | 241 | for (s = offset; s < ls; s++) { 242 | blk = s / vhdm->sect_per_block; 243 | sib = s % vhdm->sect_per_block; 244 | if (blk != prev_blk) { 245 | prev_blk = blk; 246 | if (vhdm->bitmap.curr_block != blk) { 247 | read_sect_bitmap(vhdm, blk); 248 | mvhd_fseeko64(vhdm->f, (uint64_t)sib * MVHD_SECTOR_SIZE, SEEK_CUR); 249 | } else { 250 | addr = ((int64_t)vhdm->block_offset[blk] + vhdm->bitmap.sector_count + sib) * MVHD_SECTOR_SIZE; 251 | mvhd_fseeko64(vhdm->f, addr, SEEK_SET); 252 | } 253 | } 254 | 255 | if (VHD_TESTBIT(vhdm->bitmap.curr_bitmap, sib)) { 256 | fread(buff, MVHD_SECTOR_SIZE, 1, vhdm->f); 257 | } else { 258 | memset(buff, 0, MVHD_SECTOR_SIZE); 259 | mvhd_fseeko64(vhdm->f, MVHD_SECTOR_SIZE, SEEK_CUR); 260 | } 261 | buff += MVHD_SECTOR_SIZE; 262 | } 263 | 264 | return truncated_sectors; 265 | } 266 | 267 | 268 | int 269 | mvhd_diff_read(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff) 270 | { 271 | int transfer_sectors, truncated_sectors; 272 | uint32_t total_sectors = (uint32_t)(vhdm->footer.curr_sz / MVHD_SECTOR_SIZE); 273 | 274 | check_sectors(offset, num_sectors, total_sectors, &transfer_sectors, &truncated_sectors); 275 | 276 | uint8_t* buff = (uint8_t*)out_buff; 277 | MVHDMeta* curr_vhdm = vhdm; 278 | uint32_t s, ls; 279 | int blk, sib; 280 | ls = offset + transfer_sectors; 281 | 282 | for (s = offset; s < ls; s++) { 283 | while (curr_vhdm->footer.disk_type == MVHD_TYPE_DIFF) { 284 | blk = s / curr_vhdm->sect_per_block; 285 | sib = s % curr_vhdm->sect_per_block; 286 | if (curr_vhdm->bitmap.curr_block != blk) { 287 | read_sect_bitmap(curr_vhdm, blk); 288 | } 289 | if (!VHD_TESTBIT(curr_vhdm->bitmap.curr_bitmap, sib)) { 290 | curr_vhdm = curr_vhdm->parent; 291 | } else { break; } 292 | } 293 | 294 | /* We handle actual sector reading using the fixed or sparse functions, 295 | as a differencing VHD is also a sparse VHD */ 296 | if (curr_vhdm->footer.disk_type == MVHD_TYPE_DIFF || curr_vhdm->footer.disk_type == MVHD_TYPE_DYNAMIC) { 297 | mvhd_sparse_read(curr_vhdm, s, 1, buff); 298 | } else { 299 | mvhd_fixed_read(curr_vhdm, s, 1, buff); 300 | } 301 | 302 | curr_vhdm = vhdm; 303 | buff += MVHD_SECTOR_SIZE; 304 | } 305 | 306 | return truncated_sectors; 307 | } 308 | 309 | 310 | int 311 | mvhd_fixed_write(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff) 312 | { 313 | int64_t addr; 314 | int transfer_sectors, truncated_sectors; 315 | uint32_t total_sectors = (uint32_t)(vhdm->footer.curr_sz / MVHD_SECTOR_SIZE); 316 | 317 | check_sectors(offset, num_sectors, total_sectors, &transfer_sectors, &truncated_sectors); 318 | 319 | addr = (int64_t)offset * MVHD_SECTOR_SIZE; 320 | mvhd_fseeko64(vhdm->f, addr, SEEK_SET); 321 | fwrite(in_buff, transfer_sectors*MVHD_SECTOR_SIZE, 1, vhdm->f); 322 | 323 | return truncated_sectors; 324 | } 325 | 326 | 327 | int 328 | mvhd_sparse_diff_write(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff) 329 | { 330 | int transfer_sectors, truncated_sectors; 331 | uint32_t total_sectors = (uint32_t)(vhdm->footer.curr_sz / MVHD_SECTOR_SIZE); 332 | 333 | check_sectors(offset, num_sectors, total_sectors, &transfer_sectors, &truncated_sectors); 334 | 335 | uint8_t* buff = (uint8_t*)in_buff; 336 | int64_t addr; 337 | uint32_t s, ls; 338 | int blk, prev_blk, sib; 339 | ls = offset + transfer_sectors; 340 | prev_blk = -1; 341 | 342 | for (s = offset; s < ls; s++) { 343 | blk = s / vhdm->sect_per_block; 344 | sib = s % vhdm->sect_per_block; 345 | if (vhdm->bitmap.curr_block != blk && prev_blk >= 0) { 346 | /* Write the sector bitmap for the previous block, before we replace it. */ 347 | write_curr_sect_bitmap(vhdm); 348 | } 349 | 350 | if (vhdm->block_offset[blk] == MVHD_SPARSE_BLK) { 351 | /* "read" the sector bitmap first, before creating a new block, as the bitmap will be 352 | zero either way */ 353 | read_sect_bitmap(vhdm, blk); 354 | create_block(vhdm, blk); 355 | } 356 | 357 | if (blk != prev_blk) { 358 | if (vhdm->bitmap.curr_block != blk) { 359 | read_sect_bitmap(vhdm, blk); 360 | mvhd_fseeko64(vhdm->f, (uint64_t)sib * MVHD_SECTOR_SIZE, SEEK_CUR); 361 | } else { 362 | addr = ((int64_t)vhdm->block_offset[blk] + vhdm->bitmap.sector_count + sib) * MVHD_SECTOR_SIZE; 363 | mvhd_fseeko64(vhdm->f, addr, SEEK_SET); 364 | } 365 | prev_blk = blk; 366 | } 367 | 368 | fwrite(buff, MVHD_SECTOR_SIZE, 1, vhdm->f); 369 | VHD_SETBIT(vhdm->bitmap.curr_bitmap, sib); 370 | buff += MVHD_SECTOR_SIZE; 371 | } 372 | 373 | /* And write the sector bitmap for the last block we visited to disk */ 374 | write_curr_sect_bitmap(vhdm); 375 | 376 | return truncated_sectors; 377 | } 378 | 379 | 380 | int 381 | mvhd_noop_write(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff) 382 | { 383 | (void)vhdm; 384 | (void)offset; 385 | (void)num_sectors; 386 | (void)in_buff; 387 | 388 | return 0; 389 | } 390 | -------------------------------------------------------------------------------- /src/manage.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MiniVHD Minimalist VHD implementation in C. 3 | * 4 | * This file is part of the MiniVHD Project. 5 | * 6 | * VHD management functions (open, close, read write etc) 7 | * 8 | * Version: @(#)manage.c 1.0.4 2021/04/16 9 | * 10 | * Authors: Sherman Perry, 11 | * Fred N. van Kempen, 12 | * 13 | * Copyright 2019-2021 Sherman Perry. 14 | * Copyright 2021 Fred N. van Kempen. 15 | * 16 | * MIT License 17 | * 18 | * Permission is hereby granted, free of charge, to any person 19 | * obtaining a copy of this software and associated documenta- 20 | * tion files (the "Software"), to deal in the Software without 21 | * restriction, including without limitation the rights to use, 22 | * copy, modify, merge, publish, distribute, sublicense, and/or 23 | * sell copies of the Software, and to permit persons to whom 24 | * the Software is furnished to do so, subject to the following 25 | * conditions: 26 | * 27 | * The above copyright notice and this permission notice shall 28 | * be included in all copies or substantial portions of the 29 | * Software. 30 | * 31 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 32 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 33 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 34 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 35 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 36 | * FROM, OUT OF O R IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 37 | * DEALINGS IN THE SOFTWARE. 38 | */ 39 | #ifndef _FILE_OFFSET_BITS 40 | # define _FILE_OFFSET_BITS 64 41 | #endif 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #define BUILDING_LIBRARY 48 | #include "minivhd.h" 49 | #include "internal.h" 50 | #include "version.h" 51 | #include "cwalk.h" 52 | #include "xml2_encoding.h" 53 | 54 | 55 | struct MVHDPaths { 56 | char dir_path[MVHD_MAX_PATH_BYTES]; 57 | char file_name[MVHD_MAX_PATH_BYTES]; 58 | char w2ku_path[MVHD_MAX_PATH_BYTES]; 59 | char w2ru_path[MVHD_MAX_PATH_BYTES]; 60 | char joined_path[MVHD_MAX_PATH_BYTES]; 61 | uint16_t tmp_src_path[MVHD_MAX_PATH_CHARS]; 62 | }; 63 | 64 | 65 | int mvhd_errno = 0; 66 | 67 | 68 | static char tmp_open_path[MVHD_MAX_PATH_BYTES] = {0}; 69 | 70 | 71 | /** 72 | * \brief Populate data stuctures with content from a VHD footer 73 | * 74 | * \param [in] vhdm MiniVHD data structure 75 | */ 76 | static void 77 | read_footer(MVHDMeta* vhdm) 78 | { 79 | uint8_t buffer[MVHD_FOOTER_SIZE]; 80 | 81 | mvhd_fseeko64(vhdm->f, -MVHD_FOOTER_SIZE, SEEK_END); 82 | fread(buffer, sizeof buffer, 1, vhdm->f); 83 | mvhd_buffer_to_footer(&vhdm->footer, buffer); 84 | } 85 | 86 | 87 | /** 88 | * \brief Populate data stuctures with content from a VHD sparse header 89 | * 90 | * \param [in] vhdm MiniVHD data structure 91 | */ 92 | static void 93 | read_sparse_header(MVHDMeta* vhdm) 94 | { 95 | uint8_t buffer[MVHD_SPARSE_SIZE]; 96 | 97 | mvhd_fseeko64(vhdm->f, vhdm->footer.data_offset, SEEK_SET); 98 | fread(buffer, sizeof buffer, 1, vhdm->f); 99 | mvhd_buffer_to_header(&vhdm->sparse, buffer); 100 | } 101 | 102 | 103 | /** 104 | * \brief Validate VHD footer checksum 105 | * 106 | * This works by generating a checksum from the footer, and comparing it against the stored checksum. 107 | * 108 | * \param [in] vhdm MiniVHD data structure 109 | */ 110 | static bool 111 | footer_checksum_valid(MVHDMeta* vhdm) 112 | { 113 | return vhdm->footer.checksum == mvhd_gen_footer_checksum(&vhdm->footer); 114 | } 115 | 116 | 117 | /** 118 | * \brief Validate VHD sparse header checksum 119 | * 120 | * This works by generating a checksum from the sparse header, and comparing it against the stored checksum. 121 | * 122 | * \param [in] vhdm MiniVHD data structure 123 | */ 124 | static bool 125 | sparse_checksum_valid(MVHDMeta* vhdm) 126 | { 127 | return vhdm->sparse.checksum == mvhd_gen_sparse_checksum(&vhdm->sparse); 128 | } 129 | 130 | 131 | /** 132 | * \brief Read BAT into MiniVHD data structure 133 | * 134 | * The Block Allocation Table (BAT) is the structure in a sparse and differencing VHD which stores 135 | * the 4-byte sector offsets for each data block. This function allocates enough memory to contain 136 | * the entire BAT, and then reads the contents of the BAT into the buffer. 137 | * 138 | * \param [in] vhdm MiniVHD data structure 139 | * \param [out] err this is populated with MVHD_ERR_MEM if the calloc fails 140 | * 141 | * \retval -1 if an error occurrs. Check value of err in this case 142 | * \retval 0 if the function call succeeds 143 | */ 144 | static int 145 | read_bat(MVHDMeta *vhdm, MVHDError* err) 146 | { 147 | uint32_t i; 148 | 149 | vhdm->block_offset = calloc(vhdm->sparse.max_bat_ent, sizeof *vhdm->block_offset); 150 | if (vhdm->block_offset == NULL) { 151 | *err = MVHD_ERR_MEM; 152 | return -1; 153 | } 154 | 155 | mvhd_fseeko64(vhdm->f, vhdm->sparse.bat_offset, SEEK_SET); 156 | 157 | for (i = 0; i < vhdm->sparse.max_bat_ent; i++) { 158 | fread(&vhdm->block_offset[i], sizeof *vhdm->block_offset, 1, vhdm->f); 159 | vhdm->block_offset[i] = mvhd_from_be32(vhdm->block_offset[i]); 160 | } 161 | return 0; 162 | } 163 | 164 | 165 | /** 166 | * \brief Perform a one-time calculation of some sparse VHD values 167 | * 168 | * \param [in] vhdm MiniVHD data structure 169 | */ 170 | static void 171 | calc_sparse_values(MVHDMeta* vhdm) 172 | { 173 | vhdm->sect_per_block = vhdm->sparse.block_sz / MVHD_SECTOR_SIZE; 174 | int bm_bytes = vhdm->sect_per_block / 8; 175 | vhdm->bitmap.sector_count = bm_bytes / MVHD_SECTOR_SIZE; 176 | 177 | if (bm_bytes % MVHD_SECTOR_SIZE > 0) { 178 | vhdm->bitmap.sector_count++; 179 | } 180 | } 181 | 182 | 183 | /** 184 | * \brief Allocate memory for a sector bitmap. 185 | * 186 | * Each data block is preceded by a sector bitmap. Each bit indicates whether the corresponding sector 187 | * is considered 'clean' or 'dirty' (for sparse VHD images), or whether to read from the parent or current 188 | * image (for differencing images). 189 | * 190 | * \param [in] vhdm MiniVHD data structure 191 | * \param [out] err this is populated with MVHD_ERR_MEM if the calloc fails 192 | * 193 | * \retval -1 if an error occurrs. Check value of err in this case 194 | * \retval 0 if the function call succeeds 195 | */ 196 | static int 197 | init_sector_bitmap(MVHDMeta* vhdm, MVHDError* err) 198 | { 199 | vhdm->bitmap.curr_bitmap = calloc(vhdm->bitmap.sector_count, MVHD_SECTOR_SIZE); 200 | if (vhdm->bitmap.curr_bitmap == NULL) { 201 | *err = MVHD_ERR_MEM; 202 | return -1; 203 | } 204 | 205 | vhdm->bitmap.curr_block = -1; 206 | 207 | return 0; 208 | } 209 | 210 | 211 | /** 212 | * \brief Check if the path for a given platform code exists 213 | * 214 | * From the available paths, both relative and absolute, construct a full path 215 | * and attempt to open a file at that path. 216 | * 217 | * Note, this function makes no attempt to verify that the path is the correct 218 | * VHD image, or even a VHD image at all. 219 | * 220 | * \param [in] paths a struct containing all available paths to work with 221 | * \param [in] the platform code to try and obtain a path for. Setting this to zero 222 | * will try using the directory of the child image 223 | * 224 | * \retval true if a file is found 225 | * \retval false if a file is not found 226 | */ 227 | static bool 228 | mvhd_parent_path_exists(struct MVHDPaths* paths, uint32_t plat_code) 229 | { 230 | FILE* f; 231 | int ferr; 232 | size_t cwk_ret; 233 | enum cwk_path_style style; 234 | 235 | memset(paths->joined_path, 0, sizeof paths->joined_path); 236 | style = cwk_path_guess_style((const char*)paths->dir_path); 237 | cwk_path_set_style(style); 238 | cwk_ret = 1; 239 | 240 | if (plat_code == MVHD_DIF_LOC_W2RU && *paths->w2ru_path) { 241 | cwk_ret = cwk_path_join((const char*)paths->dir_path, (const char*)paths->w2ru_path, paths->joined_path, sizeof paths->joined_path); 242 | } else if (plat_code == MVHD_DIF_LOC_W2KU && *paths->w2ku_path) { 243 | memcpy(paths->joined_path, paths->w2ku_path, (sizeof paths->joined_path) - 1); 244 | cwk_ret = 0; 245 | } else if (plat_code == 0) { 246 | cwk_ret = cwk_path_join((const char*)paths->dir_path, (const char*)paths->file_name, paths->joined_path, sizeof paths->joined_path); 247 | } 248 | if (cwk_ret > MVHD_MAX_PATH_BYTES) { 249 | return false; 250 | } 251 | 252 | f = mvhd_fopen((const char*)paths->joined_path, "rb", &ferr); 253 | if (f != NULL) { 254 | /* We found a file at the requested path! */ 255 | memcpy(tmp_open_path, paths->joined_path, (sizeof paths->joined_path) - 1); 256 | tmp_open_path[sizeof tmp_open_path - 1] = '\0'; 257 | fclose(f); 258 | return true; 259 | } 260 | 261 | return false; 262 | } 263 | 264 | 265 | /** 266 | * \brief attempt to obtain a file path to a file that may be a valid VHD image 267 | * 268 | * Differential VHD images store both a UTF-16BE file name (or path), and up to 269 | * eight "parent locator" entries. Using this information, this function tries to 270 | * find a parent image. 271 | * 272 | * This function does not verify if the path returned is a valid parent image. 273 | * 274 | * \param [in] vhdm current MiniVHD data structure 275 | * \param [out] err any errors that may occurr. Check this if NULL is returned 276 | * 277 | * \return a pointer to the global string `tmp_open_path`, or NULL if a path could 278 | * not be found, or some error occurred 279 | */ 280 | static char * 281 | get_diff_parent_path(MVHDMeta* vhdm, int* err) 282 | { 283 | int utf_outlen, utf_inlen, utf_ret; 284 | char *par_fp = NULL; 285 | struct MVHDPaths *paths; 286 | size_t dirlen; 287 | 288 | /* We can't resolve relative paths if we don't have an absolute 289 | path to work with */ 290 | if (! cwk_path_is_absolute((const char*)vhdm->filename)) { 291 | *err = MVHD_ERR_PATH_REL; 292 | goto end; 293 | } 294 | 295 | paths = calloc(1, sizeof *paths); 296 | if (paths == NULL) { 297 | *err = MVHD_ERR_MEM; 298 | goto end; 299 | } 300 | cwk_path_get_dirname((const char*)vhdm->filename, &dirlen); 301 | if (dirlen >= sizeof paths->dir_path) { 302 | *err = MVHD_ERR_PATH_LEN; 303 | goto paths_cleanup; 304 | } 305 | memcpy(paths->dir_path, vhdm->filename, dirlen); 306 | 307 | /* Get the filename field from the sparse header. */ 308 | utf_outlen = (int)sizeof paths->file_name; 309 | utf_inlen = (int)sizeof vhdm->sparse.par_utf16_name; 310 | utf_ret = UTF16BEToUTF8((unsigned char*)paths->file_name, &utf_outlen, (const unsigned char*)vhdm->sparse.par_utf16_name, &utf_inlen); 311 | if (utf_ret < 0) { 312 | mvhd_set_encoding_err(utf_ret, err); 313 | goto paths_cleanup; 314 | } 315 | 316 | /* Now read the parent locator entries, both relative and absolute, if they exist */ 317 | unsigned char* loc_path; 318 | int i; 319 | 320 | for (i = 0; i < 8; i++) { 321 | utf_outlen = MVHD_MAX_PATH_BYTES - 1; 322 | if (vhdm->sparse.par_loc_entry[i].plat_code == MVHD_DIF_LOC_W2RU) { 323 | loc_path = (unsigned char*)paths->w2ru_path; 324 | } else if (vhdm->sparse.par_loc_entry[i].plat_code == MVHD_DIF_LOC_W2KU) { 325 | loc_path = (unsigned char*)paths->w2ku_path; 326 | } else { 327 | continue; 328 | } 329 | 330 | utf_inlen = vhdm->sparse.par_loc_entry[i].plat_data_len; 331 | if (utf_inlen > MVHD_MAX_PATH_BYTES) { 332 | *err = MVHD_ERR_PATH_LEN; 333 | goto paths_cleanup; 334 | } 335 | mvhd_fseeko64(vhdm->f, vhdm->sparse.par_loc_entry[i].plat_data_offset, SEEK_SET); 336 | fread(paths->tmp_src_path, sizeof (uint8_t), utf_inlen, vhdm->f); 337 | 338 | /* Note, the W2*u parent locators are UTF-16LE, unlike the filename field previously obtained, 339 | which is UTF-16BE */ 340 | utf_ret = UTF16LEToUTF8(loc_path, &utf_outlen, (const unsigned char*)paths->tmp_src_path, &utf_inlen); 341 | if (utf_ret < 0) { 342 | mvhd_set_encoding_err(utf_ret, err); 343 | goto paths_cleanup; 344 | } 345 | } 346 | 347 | /* We have paths in UTF-8. We should have enough info to try and find the parent VHD */ 348 | /* Does the relative path exist? */ 349 | if (mvhd_parent_path_exists(paths, MVHD_DIF_LOC_W2RU)) { 350 | par_fp = tmp_open_path; 351 | goto paths_cleanup; 352 | } 353 | 354 | /* What about trying the child directory? */ 355 | if (mvhd_parent_path_exists(paths, 0)) { 356 | par_fp = tmp_open_path; 357 | goto paths_cleanup; 358 | } 359 | 360 | /* Well, all else fails, try the stored absolute path, if it exists */ 361 | if (mvhd_parent_path_exists(paths, MVHD_DIF_LOC_W2KU)) { 362 | par_fp = tmp_open_path; 363 | goto paths_cleanup; 364 | } 365 | 366 | /* If we reach this point, we could not find a path with a valid file */ 367 | par_fp = NULL; 368 | *err = MVHD_ERR_PAR_NOT_FOUND; 369 | 370 | paths_cleanup: 371 | free(paths); 372 | paths = NULL; 373 | 374 | end: 375 | return par_fp; 376 | } 377 | 378 | 379 | /** 380 | * \brief Attach the read/write function pointers to read/write functions 381 | * 382 | * Depending on the VHD type, different sector reading and writing functions are used. 383 | * The functions are called via function pointers stored in the vhdm struct. 384 | * 385 | * \param [in] vhdm MiniVHD data structure 386 | */ 387 | static void 388 | assign_io_funcs(MVHDMeta* vhdm) 389 | { 390 | switch (vhdm->footer.disk_type) { 391 | case MVHD_TYPE_FIXED: 392 | vhdm->read_sectors = mvhd_fixed_read; 393 | vhdm->write_sectors = mvhd_fixed_write; 394 | break; 395 | 396 | case MVHD_TYPE_DYNAMIC: 397 | vhdm->read_sectors = mvhd_sparse_read; 398 | vhdm->write_sectors = mvhd_sparse_diff_write; 399 | break; 400 | 401 | case MVHD_TYPE_DIFF: 402 | vhdm->read_sectors = mvhd_diff_read; 403 | vhdm->write_sectors = mvhd_sparse_diff_write; 404 | break; 405 | } 406 | 407 | if (vhdm->readonly) 408 | vhdm->write_sectors = mvhd_noop_write; 409 | } 410 | 411 | 412 | /** 413 | * \brief Return the library version as a string 414 | */ 415 | MVHDAPI const char * 416 | mvhd_version(void) 417 | { 418 | return LIB_VERSION_4; 419 | } 420 | 421 | 422 | /** 423 | * \brief Return the library version as a number 424 | */ 425 | MVHDAPI uint32_t 426 | mvhd_version_id(void) 427 | { 428 | return (LIB_VER_MAJOR << 24) | (LIB_VER_MINOR << 16) | 429 | (LIB_VER_REV << 16) | LIB_VER_PATCH; 430 | } 431 | 432 | 433 | /** 434 | * \brief A simple test to see if a given file is a VHD 435 | * 436 | * \param [in] f file to test 437 | * 438 | * \retval 1 if f is a VHD 439 | * \retval 0 if f is not a VHD 440 | */ 441 | MVHDAPI int 442 | mvhd_file_is_vhd(FILE* f) 443 | { 444 | uint8_t con_str[8]; 445 | 446 | if (f == NULL) { 447 | return 0; 448 | } 449 | 450 | mvhd_fseeko64(f, -MVHD_FOOTER_SIZE, SEEK_END); 451 | fread(con_str, sizeof con_str, 1, f); 452 | if (mvhd_is_conectix_str(con_str)) { 453 | return 1; 454 | } 455 | 456 | return 0; 457 | } 458 | 459 | 460 | MVHDAPI MVHDGeom 461 | mvhd_calculate_geometry(uint64_t size) 462 | { 463 | MVHDGeom chs; 464 | uint32_t ts = (uint32_t)(size / MVHD_SECTOR_SIZE); 465 | uint32_t spt, heads, cyl, cth; 466 | 467 | if (ts > 65535 * 16 * 255) { 468 | ts = 65535 * 16 * 255; 469 | } 470 | 471 | if (ts >= 65535 * 16 * 63) { 472 | spt = 255; 473 | heads = 16; 474 | cth = ts / spt; 475 | } else { 476 | spt = 17; 477 | cth = ts / spt; 478 | heads = (cth + 1023) / 1024; 479 | if (heads < 4) { 480 | heads = 4; 481 | } 482 | if (cth >= (heads * 1024) || heads > 16) { 483 | spt = 31; 484 | heads = 16; 485 | cth = ts / spt; 486 | } 487 | if (cth >= (heads * 1024)) { 488 | spt = 63; 489 | heads = 16; 490 | cth = ts / spt; 491 | } 492 | } 493 | 494 | cyl = cth / heads; 495 | chs.heads = heads; 496 | chs.spt = spt; 497 | chs.cyl = cyl; 498 | 499 | return chs; 500 | } 501 | 502 | 503 | MVHDAPI MVHDMeta * 504 | mvhd_open(const char* path, int readonly, int* err) 505 | { 506 | MVHDError open_err; 507 | 508 | MVHDMeta *vhdm = calloc(sizeof *vhdm, 1); 509 | if (vhdm == NULL) { 510 | *err = MVHD_ERR_MEM; 511 | goto end; 512 | } 513 | 514 | if (strlen(path) >= sizeof vhdm->filename) { 515 | *err = MVHD_ERR_PATH_LEN; 516 | goto cleanup_vhdm; 517 | } 518 | 519 | //This is safe, as we've just checked for potential overflow above 520 | strcpy(vhdm->filename, path); 521 | 522 | if (readonly) { 523 | vhdm->f = mvhd_fopen((const char*)vhdm->filename, "rb", err); 524 | } else { 525 | vhdm->f = mvhd_fopen((const char*)vhdm->filename, "rb+", err); 526 | } 527 | if (vhdm->f == NULL) { 528 | /* note, mvhd_fopen sets err for us */ 529 | goto cleanup_vhdm; 530 | } 531 | vhdm->readonly = readonly; 532 | 533 | if (! mvhd_file_is_vhd(vhdm->f)) { 534 | *err = MVHD_ERR_NOT_VHD; 535 | goto cleanup_file; 536 | } 537 | 538 | read_footer(vhdm); 539 | if (! footer_checksum_valid(vhdm)) { 540 | *err = MVHD_ERR_FOOTER_CHECKSUM; 541 | goto cleanup_file; 542 | } 543 | if (vhdm->footer.disk_type == MVHD_TYPE_DIFF || vhdm->footer.disk_type == MVHD_TYPE_DYNAMIC) { 544 | read_sparse_header(vhdm); 545 | if (! sparse_checksum_valid(vhdm)) { 546 | *err = MVHD_ERR_SPARSE_CHECKSUM; 547 | goto cleanup_file; 548 | } 549 | if (read_bat(vhdm, &open_err) == -1) { 550 | *err = open_err; 551 | goto cleanup_file; 552 | } 553 | calc_sparse_values(vhdm); 554 | if (init_sector_bitmap(vhdm, &open_err) == -1) { 555 | *err = open_err; 556 | goto cleanup_bat; 557 | } 558 | } else if (vhdm->footer.disk_type != MVHD_TYPE_FIXED) { 559 | *err = MVHD_ERR_TYPE; 560 | goto cleanup_bitmap; 561 | } 562 | assign_io_funcs(vhdm); 563 | 564 | vhdm->format_buffer.zero_data = calloc(64, MVHD_SECTOR_SIZE); 565 | if (vhdm->format_buffer.zero_data == NULL) { 566 | *err = MVHD_ERR_MEM; 567 | goto cleanup_bitmap; 568 | } 569 | 570 | vhdm->format_buffer.sector_count = 64; 571 | if (vhdm->footer.disk_type == MVHD_TYPE_DIFF) { 572 | char* par_path = get_diff_parent_path(vhdm, err); 573 | if (par_path == NULL) { 574 | goto cleanup_format_buff; 575 | } 576 | 577 | uint32_t par_mod_ts = mvhd_file_mod_timestamp(par_path, err); 578 | if (*err != 0) { 579 | goto cleanup_format_buff; 580 | } 581 | 582 | if (vhdm->sparse.par_timestamp != par_mod_ts) { 583 | /* The last-modified timestamp is to fragile to make this a fatal error. 584 | Instead, we inform the caller of the potential problem. */ 585 | *err = MVHD_ERR_TIMESTAMP; 586 | } 587 | vhdm->parent = mvhd_open(par_path, true, err); 588 | if (vhdm->parent == NULL) { 589 | goto cleanup_format_buff; 590 | } 591 | 592 | if (memcmp(vhdm->sparse.par_uuid, vhdm->parent->footer.uuid, sizeof vhdm->sparse.par_uuid) != 0) { 593 | *err = MVHD_ERR_INVALID_PAR_UUID; 594 | goto cleanup_format_buff; 595 | } 596 | } 597 | 598 | /* 599 | * If we've reached this point, we are good to go, 600 | * so skip the cleanup steps. 601 | */ 602 | goto end; 603 | 604 | cleanup_format_buff: 605 | free(vhdm->format_buffer.zero_data); 606 | vhdm->format_buffer.zero_data = NULL; 607 | 608 | cleanup_bitmap: 609 | free(vhdm->bitmap.curr_bitmap); 610 | vhdm->bitmap.curr_bitmap = NULL; 611 | 612 | cleanup_bat: 613 | free(vhdm->block_offset); 614 | vhdm->block_offset = NULL; 615 | 616 | cleanup_file: 617 | fclose(vhdm->f); 618 | vhdm->f = NULL; 619 | 620 | cleanup_vhdm: 621 | free(vhdm); 622 | vhdm = NULL; 623 | 624 | end: 625 | return vhdm; 626 | } 627 | 628 | 629 | MVHDAPI void 630 | mvhd_close(MVHDMeta* vhdm) 631 | { 632 | if (vhdm == NULL) 633 | return; 634 | 635 | if (vhdm->parent != NULL) { 636 | mvhd_close(vhdm->parent); 637 | } 638 | 639 | fclose(vhdm->f); 640 | 641 | if (vhdm->block_offset != NULL) { 642 | free(vhdm->block_offset); 643 | vhdm->block_offset = NULL; 644 | } 645 | if (vhdm->bitmap.curr_bitmap != NULL) { 646 | free(vhdm->bitmap.curr_bitmap); 647 | vhdm->bitmap.curr_bitmap = NULL; 648 | } 649 | if (vhdm->format_buffer.zero_data != NULL) { 650 | free(vhdm->format_buffer.zero_data); 651 | vhdm->format_buffer.zero_data = NULL; 652 | } 653 | 654 | free(vhdm); 655 | } 656 | 657 | 658 | MVHDAPI int 659 | mvhd_diff_update_par_timestamp(MVHDMeta* vhdm, int* err) 660 | { 661 | uint8_t sparse_buff[1024]; 662 | 663 | if (vhdm == NULL || err == NULL) { 664 | *err = MVHD_ERR_INVALID_PARAMS; 665 | return -1; 666 | } 667 | if (vhdm->footer.disk_type != MVHD_TYPE_DIFF) { 668 | *err = MVHD_ERR_TYPE; 669 | return -1; 670 | } 671 | char* par_path = get_diff_parent_path(vhdm, err); 672 | if (par_path == NULL) { 673 | return -1; 674 | } 675 | uint32_t par_mod_ts = mvhd_file_mod_timestamp(par_path, err); 676 | if (*err != 0) { 677 | return -1; 678 | } 679 | 680 | /* Update the timestamp and sparse header checksum */ 681 | vhdm->sparse.par_timestamp = par_mod_ts; 682 | vhdm->sparse.checksum = mvhd_gen_sparse_checksum(&vhdm->sparse); 683 | 684 | /* Generate and write the updated sparse header */ 685 | mvhd_header_to_buffer(&vhdm->sparse, sparse_buff); 686 | mvhd_fseeko64(vhdm->f, (int64_t)vhdm->footer.data_offset, SEEK_SET); 687 | fwrite(sparse_buff, sizeof sparse_buff, 1, vhdm->f); 688 | 689 | return 0; 690 | } 691 | 692 | 693 | MVHDAPI int 694 | mvhd_read_sectors(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff) 695 | { 696 | return vhdm->read_sectors(vhdm, offset, num_sectors, out_buff); 697 | } 698 | 699 | 700 | MVHDAPI int 701 | mvhd_write_sectors(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff) 702 | { 703 | return vhdm->write_sectors(vhdm, offset, num_sectors, in_buff); 704 | } 705 | 706 | 707 | MVHDAPI int 708 | mvhd_format_sectors(MVHDMeta* vhdm, uint32_t offset, int num_sectors) 709 | { 710 | int num_full = num_sectors / vhdm->format_buffer.sector_count; 711 | int remain = num_sectors % vhdm->format_buffer.sector_count; 712 | int i; 713 | 714 | for (i = 0; i < num_full; i++) { 715 | vhdm->write_sectors(vhdm, offset, vhdm->format_buffer.sector_count, vhdm->format_buffer.zero_data); 716 | offset += vhdm->format_buffer.sector_count; 717 | } 718 | 719 | vhdm->write_sectors(vhdm, offset, remain, vhdm->format_buffer.zero_data); 720 | 721 | return 0; 722 | } 723 | 724 | 725 | MVHDAPI MVHDType 726 | mvhd_get_type(MVHDMeta* vhdm) 727 | { 728 | return vhdm->footer.disk_type; 729 | } 730 | -------------------------------------------------------------------------------- /src/minivhd.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MiniVHD Minimalist VHD implementation in C. 3 | * MiniVHD is a minimalist implementation of read/write/creation 4 | * of VHD files. It is designed to read and write to VHD files 5 | * at a sector level. It does not enable file access, or provide 6 | * mounting options. Those features are left to more advanced 7 | * libraries and/or the operating system. 8 | * 9 | * This file is part of the MiniVHD Project. 10 | * 11 | * Definitions for the MiniVHD library. 12 | * 13 | * Version: @(#)minivhd.h 1.0.3 2021/04/16 14 | * 15 | * Authors: Sherman Perry, 16 | * Fred N. van Kempen, 17 | * 18 | * Copyright 2019-2021 Sherman Perry. 19 | * Copyright 2021 Fred N. van Kempen. 20 | * 21 | * MIT License 22 | * 23 | * Permission is hereby granted, free of charge, to any person 24 | * obtaining a copy of this software and associated documenta- 25 | * tion files (the "Software"), to deal in the Software without 26 | * restriction, including without limitation the rights to use, 27 | * copy, modify, merge, publish, distribute, sublicense, and/or 28 | * sell copies of the Software, and to permit persons to whom 29 | * the Software is furnished to do so, subject to the following 30 | * conditions: 31 | * 32 | * The above copyright notice and this permission notice shall 33 | * be included in all copies or substantial portions of the 34 | * Software. 35 | * 36 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 37 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 38 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 39 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 40 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 41 | * FROM, OUT OF O R IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 42 | * DEALINGS IN THE SOFTWARE. 43 | */ 44 | #ifndef MINIVHD_H 45 | # define MINIVHD_H 46 | 47 | 48 | typedef enum MVHDError { 49 | MVHD_ERR_MEM = -128, 50 | MVHD_ERR_FILE, 51 | MVHD_ERR_NOT_VHD, 52 | MVHD_ERR_TYPE, 53 | MVHD_ERR_FOOTER_CHECKSUM, 54 | MVHD_ERR_SPARSE_CHECKSUM, 55 | MVHD_ERR_UTF_TRANSCODING_FAILED, 56 | MVHD_ERR_UTF_SIZE, 57 | MVHD_ERR_PATH_REL, 58 | MVHD_ERR_PATH_LEN, 59 | MVHD_ERR_PAR_NOT_FOUND, 60 | MVHD_ERR_INVALID_PAR_UUID, 61 | MVHD_ERR_INVALID_GEOM, 62 | MVHD_ERR_INVALID_SIZE, 63 | MVHD_ERR_INVALID_BLOCK_SIZE, 64 | MVHD_ERR_INVALID_PARAMS, 65 | MVHD_ERR_CONV_SIZE, 66 | MVHD_ERR_TIMESTAMP 67 | } MVHDError; 68 | 69 | typedef enum MVHDType { 70 | MVHD_TYPE_FIXED = 2, 71 | MVHD_TYPE_DYNAMIC = 3, 72 | MVHD_TYPE_DIFF = 4 73 | } MVHDType; 74 | 75 | typedef enum MVHDBlockSize { 76 | MVHD_BLOCK_DEFAULT = 0, /**< 2 MB blocks */ 77 | MVHD_BLOCK_SMALL = 1024, /**< 512 KB blocks */ 78 | MVHD_BLOCK_LARGE = 4096 /**< 2 MB blocks */ 79 | } MVHDBlockSize; 80 | 81 | typedef struct MVHDGeom { 82 | uint16_t cyl; 83 | uint8_t heads; 84 | uint8_t spt; 85 | } MVHDGeom; 86 | 87 | 88 | #ifdef __cplusplus 89 | extern "C" { 90 | #endif 91 | 92 | typedef void (*mvhd_progress_callback)(uint32_t current_sector, uint32_t total_sectors); 93 | 94 | typedef struct MVHDCreationOptions { 95 | int type; /** MVHD_TYPE_FIXED, MVHD_TYPE_DYNAMIC, or MVHD_TYPE_DIFF */ 96 | char* path; /** Absolute path of the new VHD file */ 97 | char* parent_path; /** For MVHD_TYPE_DIFF, this is the absolute path of the VHD's parent. For non-diff VHDs, this should be NULL. */ 98 | uint64_t size_in_bytes; /** Total size of the VHD's virtual disk in bytes. Must be a multiple of 512. If 0, the size is auto-calculated from the geometry field. Ignored for MVHD_TYPE_DIFF. */ 99 | MVHDGeom geometry; /** The geometry of the VHD. If set to 0, the geometry is auto-calculated from the size_in_bytes field. */ 100 | uint32_t block_size_in_sectors; /** MVHD_BLOCK_LARGE or MVHD_BLOCK_SMALL, or 0 for the default value. The number of sectors per block. */ 101 | mvhd_progress_callback progress_callback; /** Optional; if not NULL, gets called to indicate progress on the creation operation. Only applies to MVHD_TYPE_FIXED. */ 102 | } MVHDCreationOptions; 103 | 104 | typedef struct MVHDMeta MVHDMeta; 105 | 106 | 107 | extern int mvhd_errno; 108 | 109 | 110 | /* Shared-library madness. */ 111 | #if defined(_WIN32) 112 | # ifdef STATIC 113 | # define MVHDAPI /*nothing*/ 114 | # else 115 | # ifdef BUILDING_LIBRARY 116 | # define MVHDAPI __declspec(dllexport) 117 | # else 118 | # define MVHDAPI __declspec(dllimport) 119 | # endif 120 | # endif 121 | #elif defined(__GNUC__) 122 | # ifdef BUILDING_LIBRARY 123 | # define MVHDAPI __attribute__((visibility("default"))) 124 | # else 125 | # define MVHDAPI /*nothing*/ 126 | # endif 127 | #else 128 | # define MVHDAPI /*nothing*/ 129 | #endif 130 | 131 | 132 | /** 133 | * \brief Return the library version as a string 134 | */ 135 | MVHDAPI const char *mvhd_version(void); 136 | 137 | /** 138 | * \brief Return the library version as a number 139 | */ 140 | MVHDAPI uint32_t mvhd_version_id(void); 141 | 142 | /** 143 | * \brief Output a string from a MiniVHD error number 144 | * 145 | * \param [in] err is the error number to return string from 146 | * 147 | * \return Error string 148 | */ 149 | MVHDAPI const char* mvhd_strerr(MVHDError err); 150 | 151 | /** 152 | * \brief A simple test to see if a given file is a VHD 153 | * 154 | * \param [in] f file to test 155 | * 156 | * \retval 1 if f is a VHD 157 | * \retval 0 if f is not a VHD 158 | */ 159 | MVHDAPI int mvhd_file_is_vhd(FILE* f); 160 | 161 | /** 162 | * \brief Return the file type of the given file 163 | * 164 | * \param [in] vhdm VHD to check. 165 | * 166 | * \retval one of the defined MVHDType values 167 | */ 168 | MVHDAPI MVHDType mvhd_get_type(MVHDMeta* vhdm); 169 | 170 | /** 171 | * \brief Open a VHD image for reading and/or writing 172 | * 173 | * The returned pointer contains all required values and structures (and files) to 174 | * read and write to a VHD file. 175 | * 176 | * Remember to call mvhd_close() when you are finished. 177 | * 178 | * \param [in] Absolute path to VHD file. Relative path will cause issues when opening 179 | * a differencing VHD file 180 | * \param [in] readonly set this to 1 to open the VHD in a read only manner 181 | * \param [out] err will be set if the VHD fails to open. Value could be one of 182 | * MVHD_ERR_MEM, MVHD_ERR_FILE, MVHD_ERR_NOT_VHD, MVHD_ERR_FOOTER_CHECKSUM, MVHD_ERR_SPARSE_CHECKSUM, 183 | * MVHD_ERR_TYPE, MVHD_ERR_TIMESTAMP 184 | * If MVHD_ERR_FILE is set, mvhd_errno will be set to the appropriate system errno value 185 | * 186 | * \return MVHDMeta pointer. If NULL, check err. err may also be set to MVHD_ERR_TIMESTAMP if 187 | * opening a differencing VHD. 188 | */ 189 | MVHDAPI MVHDMeta* mvhd_open(const char* path, int readonly, int* err); 190 | 191 | /** 192 | * \brief Update the parent modified timestamp in the VHD file 193 | * 194 | * Differencing VHD's use a parent last modified timestamp to try and detect if the 195 | * parent has been modified after the child has been created. However, this is rather 196 | * fragile and can be broken by moving/copying the parent. Also, MS DiskPart does not 197 | * set this timestamp in the child :( 198 | * 199 | * Be careful when using this function that you don't update the timestamp after the 200 | * parent actually has been modified. 201 | * 202 | * \param [in] vhdm Differencing VHD to update. 203 | * \param [out] err will be set if the timestamp could not be updated 204 | * 205 | * \return non-zero on error, 0 on success 206 | */ 207 | MVHDAPI int mvhd_diff_update_par_timestamp(MVHDMeta* vhdm, int* err); 208 | 209 | /** 210 | * \brief Create a fixed VHD image 211 | * 212 | * \param [in] path is the absolute path to the image to create 213 | * \param [in] geom is the HDD geometry of the image to create. Determines final image size 214 | * \param [out] err indicates what error occurred, if any 215 | * \param [out] progress_callback optional; if not NULL, gets called to indicate progress on the creation operation 216 | * 217 | * \retval NULL if an error occurrs. Check value of *err for actual error. Otherwise returns pointer to a MVHDMeta struct 218 | */ 219 | MVHDAPI MVHDMeta* mvhd_create_fixed(const char* path, MVHDGeom geom, int* err, mvhd_progress_callback progress_callback); 220 | 221 | /** 222 | * \brief Create sparse (dynamic) VHD image. 223 | * 224 | * \param [in] path is the absolute path to the VHD file to create 225 | * \param [in] geom is the HDD geometry of the image to create. Determines final image size 226 | * \param [out] err indicates what error occurred, if any 227 | * 228 | * \return NULL if an error occurrs. Check value of *err for actual error. Otherwise returns pointer to a MVHDMeta struct 229 | */ 230 | MVHDAPI MVHDMeta* mvhd_create_sparse(const char* path, MVHDGeom geom, int* err); 231 | 232 | /** 233 | * \brief Create differencing VHD imagee. 234 | * 235 | * \param [in] path is the absolute path to the VHD file to create 236 | * \param [in] par_path is the absolute path to a parent image. If NULL, a sparse image is created, otherwise create a differencing image 237 | * \param [out] err indicates what error occurred, if any 238 | * 239 | * \return NULL if an error occurrs. Check value of *err for actual error. Otherwise returns pointer to a MVHDMeta struct 240 | */ 241 | MVHDAPI MVHDMeta* mvhd_create_diff(const char* path, const char* par_path, int* err); 242 | 243 | /** 244 | * \brief Create a VHD using the provided options 245 | * 246 | * Use mvhd_create_ex if you want more control over the VHD's options. For quick creation, you can use mvhd_create_fixed, mvhd_create_sparse, or mvhd_create_diff. 247 | * 248 | * \param [in] options the VHD creation options. 249 | * \param [out] err indicates what error occurred, if any 250 | * 251 | * \retval NULL if an error occurrs. Check value of *err for actual error. Otherwise returns pointer to a MVHDMeta struct 252 | */ 253 | MVHDAPI MVHDMeta* mvhd_create_ex(MVHDCreationOptions options, int* err); 254 | 255 | /** 256 | * \brief Safely close a VHD image 257 | * 258 | * \param [in] vhdm MiniVHD data structure to close 259 | */ 260 | MVHDAPI void mvhd_close(MVHDMeta* vhdm); 261 | 262 | /** 263 | * \brief Calculate hard disk geometry from a provided size 264 | * 265 | * The VHD format uses Cylinder, Heads, Sectors per Track (CHS) when accessing the disk. 266 | * The size of the disk can be determined from C * H * S * sector_size. 267 | * 268 | * Note, maximum geometry size (in bytes) is 65535 * 16 * 255 * 512, which is 127GB. 269 | * However, the maximum VHD size is 2040GB. For VHDs larger than 127GB, the geometry size will be 270 | * smaller than the actual VHD size. 271 | * 272 | * This function determines the appropriate CHS geometry from a provided size in bytes. 273 | * The calculations used are those provided in "Appendix: CHS Calculation" from the document 274 | * "Virtual Hard Disk Image Format Specification" provided by Microsoft. 275 | * 276 | * \param [in] size the desired VHD image size, in bytes 277 | * 278 | * \return MVHDGeom the calculated geometry. This can be used in the appropriate create functions. 279 | */ 280 | MVHDAPI MVHDGeom mvhd_calculate_geometry(uint64_t size); 281 | 282 | /** 283 | * \brief Get the CHS geometry from the image 284 | * 285 | * \param [in] vhdm MiniVHD data structure 286 | * 287 | * \return The CHS geometry as stored in the image 288 | */ 289 | MVHDAPI MVHDGeom mvhd_get_geometry(MVHDMeta* vhdm); 290 | 291 | /** 292 | * \brief Get the 'current_size' value from the image 293 | * 294 | * Note that the size returned may not match the size calculated from the 295 | * CHS geometry. It is up to the caller to decide how best to handle this. 296 | * 297 | * \param [in] vhdm MiniVHD data structure 298 | * 299 | * \return The 'current_size' value in bytes, as stored in the image. 300 | * Note, this may not match the CHS geometry. 301 | */ 302 | MVHDAPI uint64_t mvhd_get_current_size(MVHDMeta* vhdm); 303 | 304 | /** 305 | * \brief Calculate CHS geometry size in bytes 306 | * 307 | * \param [in] geom the CHS geometry to calculate 308 | * 309 | * \return the size in bytes 310 | */ 311 | MVHDAPI uint64_t mvhd_calc_size_bytes(MVHDGeom *geom); 312 | 313 | /** 314 | * \brief Calculate CHS geometry size in sectors 315 | * 316 | * \param [in] geom the CHS geometry to calculate 317 | * 318 | * \return the size in sectors 319 | */ 320 | MVHDAPI uint32_t mvhd_calc_size_sectors(MVHDGeom *geom); 321 | 322 | /** 323 | * \brief Convert a raw disk image to a fixed VHD image 324 | * 325 | * \param [in] utf8_raw_path is the path of the raw image to convert 326 | * \param [in] utf8_vhd_path is the path of the VHD to create 327 | * \param [out] err indicates what error occurred, if any 328 | * 329 | * \return NULL if an error occurrs. Check value of *err for actual error. Otherwise returns pointer to a MVHDMeta struct 330 | */ 331 | MVHDAPI MVHDMeta* mvhd_convert_to_vhd_fixed(const char* utf8_raw_path, const char* utf8_vhd_path, int* err); 332 | 333 | /** 334 | * \brief Convert a raw disk image to a sparse VHD image 335 | * 336 | * \param [in] utf8_raw_path is the path of the raw image to convert 337 | * \param [in] utf8_vhd_path is the path of the VHD to create 338 | * \param [out] err indicates what error occurred, if any 339 | * 340 | * \return NULL if an error occurrs. Check value of *err for actual error. Otherwise returns pointer to a MVHDMeta struct 341 | */ 342 | MVHDAPI MVHDMeta* mvhd_convert_to_vhd_sparse(const char* utf8_raw_path, const char* utf8_vhd_path, int* err); 343 | 344 | /** 345 | * \brief Convert a VHD image to a raw disk image 346 | * 347 | * \param [in] utf8_vhd_path is the path of the VHD to convert 348 | * \param [in] utf8_raw_path is the path of the raw image to create 349 | * \param [out] err indicates what error occurred, if any 350 | * 351 | * \return NULL if an error occurrs. Check value of *err for actual error. Otherwise returns the raw disk image FILE pointer 352 | */ 353 | MVHDAPI FILE* mvhd_convert_to_raw(const char* utf8_vhd_path, const char* utf8_raw_path, int *err); 354 | 355 | /** 356 | * \brief Read sectors from VHD file 357 | * 358 | * Read num_sectors, beginning at offset from the VHD file into a buffer 359 | * 360 | * \param [in] vhdm MiniVHD data structure 361 | * \param [in] offset the sector offset from which to start reading from 362 | * \param [in] num_sectors the number of sectors to read 363 | * \param [out] out_buff the buffer to write sector data to 364 | * 365 | * \return the number of sectors that were not read, or zero 366 | */ 367 | MVHDAPI int mvhd_read_sectors(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff); 368 | 369 | /** 370 | * \brief Write sectors to VHD file 371 | * 372 | * Write num_sectors, beginning at offset from a buffer VHD file into the VHD file 373 | * 374 | * \param [in] vhdm MiniVHD data structure 375 | * \param [in] offset the sector offset from which to start writing to 376 | * \param [in] num_sectors the number of sectors to write 377 | * \param [in] in_buffer the buffer to write sector data to 378 | * 379 | * \return the number of sectors that were not written, or zero 380 | */ 381 | MVHDAPI int mvhd_write_sectors(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff); 382 | 383 | /** 384 | * \brief Write zeroed sectors to VHD file 385 | * 386 | * Write num_sectors, beginning at offset, of zero data into the VHD file. 387 | * We reuse the existing write functions, with a preallocated zero buffer as 388 | * our source buffer. 389 | * 390 | * \param [in] vhdm MiniVHD data structure 391 | * \param [in] offset the sector offset from which to start writing to 392 | * \param [in] num_sectors the number of sectors to write 393 | * 394 | * \return the number of sectors that were not written, or zero 395 | */ 396 | MVHDAPI int mvhd_format_sectors(MVHDMeta* vhdm, uint32_t offset, int num_sectors); 397 | 398 | #ifdef __cplusplus 399 | } 400 | #endif 401 | 402 | 403 | #endif /*MINIVHD_H*/ 404 | -------------------------------------------------------------------------------- /src/struct_rw.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MiniVHD Minimalist VHD implementation in C. 3 | * 4 | * This file is part of the MiniVHD Project. 5 | * 6 | * Header and footer serialize/deserialize functions. 7 | * 8 | * Read data from footer into the struct members, swapping 9 | * endian where necessary. 10 | * 11 | * NOTE: Order matters here! 12 | * We must read each field in the order the struct is in. 13 | * Doing this may be less elegant than performing a memcpy 14 | * to a packed struct, but it avoids potential data alignment 15 | * issues, and the endian swapping allows us to use the fields 16 | * directly. 17 | * 18 | * Version: @(#)struct_rw.c 1.0.2 2021/04/16 19 | * 20 | * Author: Sherman Perry, 21 | * 22 | * Copyright 2019-2021 Sherman Perry. 23 | * 24 | * MIT License 25 | * 26 | * Permission is hereby granted, free of charge, to any person 27 | * obtaining a copy of this software and associated documenta- 28 | * tion files (the "Software"), to deal in the Software without 29 | * restriction, including without limitation the rights to use, 30 | * copy, modify, merge, publish, distribute, sublicense, and/or 31 | * sell copies of the Software, and to permit persons to whom 32 | * the Software is furnished to do so, subject to the following 33 | * conditions: 34 | * 35 | * The above copyright notice and this permission notice shall 36 | * be included in all copies or substantial portions of the 37 | * Software. 38 | * 39 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 40 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 41 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 42 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 43 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 44 | * FROM, OUT OF O R IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 45 | * DEALINGS IN THE SOFTWARE. 46 | */ 47 | #ifndef _FILE_OFFSET_BITS 48 | # define _FILE_OFFSET_BITS 64 49 | #endif 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | #define BUILDING_LIBRARY 56 | #include "minivhd.h" 57 | #include "internal.h" 58 | 59 | 60 | /** 61 | * \brief Get the next field from a buffer and store it in a struct member, converting endian if necessary 62 | * 63 | * \param [out] struct_memb struct member to save the field to 64 | * \param [in] memb_size the size of struct_memb, in bytes 65 | * \param [in] req_endian is the field a value that requires endian conversion (eg: uint16, uint32) 66 | * \param [in] buffer the buffer from which fields are read from. Will be advanced at the end of the function call 67 | */ 68 | static void 69 | next_buffer_to_struct(void* struct_memb, size_t memb_size, bool req_endian, uint8_t** buffer) 70 | { 71 | memcpy(struct_memb, *buffer, memb_size); 72 | 73 | if (req_endian) switch (memb_size) { 74 | case 2: 75 | *(uint16_t*)(struct_memb) = mvhd_from_be16(*(uint16_t*)(struct_memb)); 76 | break; 77 | 78 | case 4: 79 | *(uint32_t*)(struct_memb) = mvhd_from_be32(*(uint32_t*)(struct_memb)); 80 | break; 81 | 82 | case 8: 83 | *(uint64_t*)(struct_memb) = mvhd_from_be64(*(uint64_t*)(struct_memb)); 84 | break; 85 | } 86 | 87 | *buffer += memb_size; 88 | } 89 | 90 | 91 | /** 92 | * \brief Save a struct member into a buffer, converting endian if necessary 93 | * 94 | * \param [in] struct_memb struct member read from 95 | * \param [in] memb_size the size of struct_memb, in bytes 96 | * \param [in] req_endian is the field a value that requires endian conversion (eg: uint16, uint32) 97 | * \param [out] buffer the buffer from which struct member is saved to. Will be advanced at the end of the function call 98 | */ 99 | static void 100 | next_struct_to_buffer(void* struct_memb, size_t memb_size, bool req_endian, uint8_t** buffer) 101 | { 102 | uint8_t *buf_ptr = *buffer; 103 | 104 | memcpy(buf_ptr, struct_memb, memb_size); 105 | 106 | if (req_endian) switch (memb_size) { 107 | case 2: 108 | *((uint16_t*)buf_ptr) = mvhd_to_be16(*(uint16_t*)(struct_memb)); 109 | break; 110 | 111 | case 4: 112 | *((uint32_t*)buf_ptr) = mvhd_to_be32(*(uint32_t*)(struct_memb)); 113 | break; 114 | 115 | case 8: 116 | *((uint64_t*)buf_ptr) = mvhd_to_be64(*(uint64_t*)(struct_memb)); 117 | break; 118 | } 119 | 120 | buf_ptr += memb_size; 121 | *buffer = buf_ptr; 122 | } 123 | 124 | 125 | void 126 | mvhd_buffer_to_footer(MVHDFooter* footer, uint8_t* buffer) 127 | { 128 | uint8_t* buff_ptr = buffer; 129 | 130 | next_buffer_to_struct(&footer->cookie, sizeof footer->cookie, false, &buff_ptr); 131 | next_buffer_to_struct(&footer->features, sizeof footer->features, true, &buff_ptr); 132 | next_buffer_to_struct(&footer->fi_fmt_vers, sizeof footer->fi_fmt_vers, true, &buff_ptr); 133 | next_buffer_to_struct(&footer->data_offset, sizeof footer->data_offset, true, &buff_ptr); 134 | next_buffer_to_struct(&footer->timestamp, sizeof footer->timestamp, true, &buff_ptr); 135 | next_buffer_to_struct(&footer->cr_app, sizeof footer->cr_app, false, &buff_ptr); 136 | next_buffer_to_struct(&footer->cr_vers, sizeof footer->cr_vers, true, &buff_ptr); 137 | next_buffer_to_struct(&footer->cr_host_os, sizeof footer->cr_host_os, false, &buff_ptr); 138 | next_buffer_to_struct(&footer->orig_sz, sizeof footer->orig_sz, true, &buff_ptr); 139 | next_buffer_to_struct(&footer->curr_sz, sizeof footer->curr_sz, true, &buff_ptr); 140 | next_buffer_to_struct(&footer->geom.cyl, sizeof footer->geom.cyl, true, &buff_ptr); 141 | next_buffer_to_struct(&footer->geom.heads, sizeof footer->geom.heads, false, &buff_ptr); 142 | next_buffer_to_struct(&footer->geom.spt, sizeof footer->geom.spt, false, &buff_ptr); 143 | next_buffer_to_struct(&footer->disk_type, sizeof footer->disk_type, true, &buff_ptr); 144 | next_buffer_to_struct(&footer->checksum, sizeof footer->checksum, true, &buff_ptr); 145 | next_buffer_to_struct(&footer->uuid, sizeof footer->uuid, false, &buff_ptr); 146 | next_buffer_to_struct(&footer->saved_st, sizeof footer->saved_st, false, &buff_ptr); 147 | next_buffer_to_struct(&footer->reserved, sizeof footer->reserved, false, &buff_ptr); 148 | } 149 | 150 | 151 | void 152 | mvhd_footer_to_buffer(MVHDFooter* footer, uint8_t* buffer) 153 | { 154 | uint8_t* buff_ptr = buffer; 155 | 156 | next_struct_to_buffer(&footer->cookie, sizeof footer->cookie, false, &buff_ptr); 157 | next_struct_to_buffer(&footer->features, sizeof footer->features, true, &buff_ptr); 158 | next_struct_to_buffer(&footer->fi_fmt_vers, sizeof footer->fi_fmt_vers, true, &buff_ptr); 159 | next_struct_to_buffer(&footer->data_offset, sizeof footer->data_offset, true, &buff_ptr); 160 | next_struct_to_buffer(&footer->timestamp, sizeof footer->timestamp, true, &buff_ptr); 161 | next_struct_to_buffer(&footer->cr_app, sizeof footer->cr_app, false, &buff_ptr); 162 | next_struct_to_buffer(&footer->cr_vers, sizeof footer->cr_vers, true, &buff_ptr); 163 | next_struct_to_buffer(&footer->cr_host_os, sizeof footer->cr_host_os, false, &buff_ptr); 164 | next_struct_to_buffer(&footer->orig_sz, sizeof footer->orig_sz, true, &buff_ptr); 165 | next_struct_to_buffer(&footer->curr_sz, sizeof footer->curr_sz, true, &buff_ptr); 166 | next_struct_to_buffer(&footer->geom.cyl, sizeof footer->geom.cyl, true, &buff_ptr); 167 | next_struct_to_buffer(&footer->geom.heads, sizeof footer->geom.heads, false, &buff_ptr); 168 | next_struct_to_buffer(&footer->geom.spt, sizeof footer->geom.spt, false, &buff_ptr); 169 | next_struct_to_buffer(&footer->disk_type, sizeof footer->disk_type, true, &buff_ptr); 170 | next_struct_to_buffer(&footer->checksum, sizeof footer->checksum, true, &buff_ptr); 171 | next_struct_to_buffer(&footer->uuid, sizeof footer->uuid, false, &buff_ptr); 172 | next_struct_to_buffer(&footer->saved_st, sizeof footer->saved_st, false, &buff_ptr); 173 | next_struct_to_buffer(&footer->reserved, sizeof footer->reserved, false, &buff_ptr); 174 | } 175 | 176 | 177 | void 178 | mvhd_buffer_to_header(MVHDSparseHeader* header, uint8_t* buffer) 179 | { 180 | uint8_t* buff_ptr = buffer; 181 | int i; 182 | 183 | next_buffer_to_struct(&header->cookie, sizeof header->cookie, false, &buff_ptr); 184 | next_buffer_to_struct(&header->data_offset, sizeof header->data_offset, true, &buff_ptr); 185 | next_buffer_to_struct(&header->bat_offset, sizeof header->bat_offset, true, &buff_ptr); 186 | next_buffer_to_struct(&header->head_vers, sizeof header->head_vers, true, &buff_ptr); 187 | next_buffer_to_struct(&header->max_bat_ent, sizeof header->max_bat_ent, true, &buff_ptr); 188 | next_buffer_to_struct(&header->block_sz, sizeof header->block_sz, true, &buff_ptr); 189 | next_buffer_to_struct(&header->checksum, sizeof header->checksum, true, &buff_ptr); 190 | next_buffer_to_struct(&header->par_uuid, sizeof header->par_uuid, false, &buff_ptr); 191 | next_buffer_to_struct(&header->par_timestamp, sizeof header->par_timestamp, true, &buff_ptr); 192 | next_buffer_to_struct(&header->reserved_1, sizeof header->reserved_1, true, &buff_ptr); 193 | next_buffer_to_struct(&header->par_utf16_name, sizeof header->par_utf16_name, false, &buff_ptr); 194 | 195 | for (i = 0; i < 8; i++) { 196 | next_buffer_to_struct(&header->par_loc_entry[i].plat_code, sizeof header->par_loc_entry[i].plat_code, true, &buff_ptr); 197 | next_buffer_to_struct(&header->par_loc_entry[i].plat_data_space, sizeof header->par_loc_entry[i].plat_data_space, true, &buff_ptr); 198 | next_buffer_to_struct(&header->par_loc_entry[i].plat_data_len, sizeof header->par_loc_entry[i].plat_data_len, true, &buff_ptr); 199 | next_buffer_to_struct(&header->par_loc_entry[i].reserved, sizeof header->par_loc_entry[i].reserved, true, &buff_ptr); 200 | next_buffer_to_struct(&header->par_loc_entry[i].plat_data_offset, sizeof header->par_loc_entry[i].plat_data_offset, true, &buff_ptr); 201 | } 202 | 203 | next_buffer_to_struct(&header->reserved_2, sizeof header->reserved_2, false, &buff_ptr); 204 | } 205 | 206 | 207 | void 208 | mvhd_header_to_buffer(MVHDSparseHeader* header, uint8_t* buffer) 209 | { 210 | uint8_t* buff_ptr = buffer; 211 | int i; 212 | 213 | next_struct_to_buffer(&header->cookie, sizeof header->cookie, false, &buff_ptr); 214 | next_struct_to_buffer(&header->data_offset, sizeof header->data_offset, true, &buff_ptr); 215 | next_struct_to_buffer(&header->bat_offset, sizeof header->bat_offset, true, &buff_ptr); 216 | next_struct_to_buffer(&header->head_vers, sizeof header->head_vers, true, &buff_ptr); 217 | next_struct_to_buffer(&header->max_bat_ent, sizeof header->max_bat_ent, true, &buff_ptr); 218 | next_struct_to_buffer(&header->block_sz, sizeof header->block_sz, true, &buff_ptr); 219 | next_struct_to_buffer(&header->checksum, sizeof header->checksum, true, &buff_ptr); 220 | next_struct_to_buffer(&header->par_uuid, sizeof header->par_uuid, false, &buff_ptr); 221 | next_struct_to_buffer(&header->par_timestamp, sizeof header->par_timestamp, true, &buff_ptr); 222 | next_struct_to_buffer(&header->reserved_1, sizeof header->reserved_1, true, &buff_ptr); 223 | next_struct_to_buffer(&header->par_utf16_name, sizeof header->par_utf16_name, false, &buff_ptr); 224 | 225 | for (i = 0; i < 8; i++) { 226 | next_struct_to_buffer(&header->par_loc_entry[i].plat_code, sizeof header->par_loc_entry[i].plat_code, true, &buff_ptr); 227 | next_struct_to_buffer(&header->par_loc_entry[i].plat_data_space, sizeof header->par_loc_entry[i].plat_data_space, true, &buff_ptr); 228 | next_struct_to_buffer(&header->par_loc_entry[i].plat_data_len, sizeof header->par_loc_entry[i].plat_data_len, true, &buff_ptr); 229 | next_struct_to_buffer(&header->par_loc_entry[i].reserved, sizeof header->par_loc_entry[i].reserved, true, &buff_ptr); 230 | next_struct_to_buffer(&header->par_loc_entry[i].plat_data_offset, sizeof header->par_loc_entry[i].plat_data_offset, true, &buff_ptr); 231 | } 232 | 233 | next_struct_to_buffer(&header->reserved_2, sizeof header->reserved_2, false, &buff_ptr); 234 | } 235 | -------------------------------------------------------------------------------- /src/tester.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MiniVHD Minimalist VHD implementation in C. 3 | * MiniVHD is a minimalist implementation of read/write/creation 4 | * of VHD files. It is designed to read and write to VHD files 5 | * at a sector level. It does not enable file access, or provide 6 | * mounting options. Those features are left to more advanced 7 | * libraries and/or the operating system. 8 | * 9 | * This file is part of the MiniVHD Project. 10 | * 11 | * Simple test program for the MiniVHD library. 12 | * 13 | * Version: @(#)tester.c 1.0.1 2021/03/15 14 | * 15 | * Author: Sherman Perry, 16 | * 17 | * Copyright 2019-2021 Sherman Perry. 18 | * 19 | * MIT License 20 | * 21 | * Permission is hereby granted, free of charge, to any person 22 | * obtaining a copy of this software and associated documenta- 23 | * tion files (the "Software"), to deal in the Software without 24 | * restriction, including without limitation the rights to use, 25 | * copy, modify, merge, publish, distribute, sublicense, and/or 26 | * sell copies of the Software, and to permit persons to whom 27 | * the Software is furnished to do so, subject to the following 28 | * conditions: 29 | * 30 | * The above copyright notice and this permission notice shall 31 | * be included in all copies or substantial portions of the 32 | * Software. 33 | * 34 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 35 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 36 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 37 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 38 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 39 | * FROM, OUT OF O R IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 40 | * DEALINGS IN THE SOFTWARE. 41 | */ 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | 49 | 50 | int main(int argc, char* argv[]) { 51 | if (argc != 6) { 52 | char *help_text = 53 | "Incorrect num arguments. Expected args as follows:\n" 54 | "minivhd_test RAW_SRC, VHD_FIXED, VHD_SPARSE, RAW_DEST_FIXED, RAW_DEST_SPARSE\n"; 55 | printf("%s\n", help_text); 56 | return 1; 57 | } 58 | const char *raw_src_path = argv[1]; 59 | const char *vhd_fixed_path = argv[2]; 60 | const char *vhd_sparse_path = argv[3]; 61 | const char *raw_dest_fixed_path = argv[4]; 62 | const char *raw_dest_sparse_path = argv[5]; 63 | int err; 64 | /* Conversion tests. This just happens to test VHD creation, 65 | * and the read/write functionality all in one go :) */ 66 | time_t start, end; 67 | printf("Converting raw imaged to fixed VHD\n"); 68 | start = time(0); 69 | MVHDMeta *vhdm = mvhd_convert_to_vhd_fixed(raw_src_path, vhd_fixed_path, &err); 70 | if (vhdm == NULL) { 71 | printf("%s\n", mvhd_strerr(err)); 72 | return EXIT_FAILURE; 73 | } 74 | end = time(0); 75 | printf("Raw image converted to fixed VHD in %f seconds\n", difftime(end, start)); 76 | mvhd_close(vhdm); 77 | 78 | printf("Converting raw imaged to sparse VHD\n"); 79 | start = time(0); 80 | vhdm = mvhd_convert_to_vhd_sparse(raw_src_path, vhd_sparse_path, &err); 81 | if (vhdm == NULL) { 82 | printf("%s\n", mvhd_strerr(err)); 83 | return EXIT_FAILURE; 84 | } 85 | end = time(0); 86 | printf("Raw image converted to fixed VHD in %f seconds\n", difftime(end, start)); 87 | mvhd_close(vhdm); 88 | 89 | printf("Converting fixed VHD to raw image\n"); 90 | start = time(0); 91 | FILE *raw = mvhd_convert_to_raw(vhd_fixed_path, raw_dest_fixed_path, &err); 92 | if (raw == NULL) { 93 | printf("%s\n", mvhd_strerr(err)); 94 | return EXIT_FAILURE; 95 | } 96 | fclose(raw); 97 | end = time(0); 98 | printf("Fixed VHD converted to raw image in %f seconds\n", difftime(end, start)); 99 | 100 | printf("Converting sparse VHD to raw image\n"); 101 | start = time(0); 102 | raw = mvhd_convert_to_raw(vhd_sparse_path, raw_dest_sparse_path, &err); 103 | if (raw == NULL) { 104 | printf("%s\n", mvhd_strerr(err)); 105 | return EXIT_FAILURE; 106 | } 107 | fclose(raw); 108 | end = time(0); 109 | printf("Sparse VHD converted to raw image in %f seconds\n", difftime(end, start)); 110 | return EXIT_SUCCESS; 111 | } 112 | -------------------------------------------------------------------------------- /src/unix/Makefile.linux: -------------------------------------------------------------------------------- 1 | # 2 | # MiniVHD Minimalist VHD implementation in C. 3 | # MiniVHD is a minimalist implementation of read/write/creation 4 | # of VHD files. It is designed to read and write to VHD files 5 | # at a sector level. It does not enable file access, or provide 6 | # mounting options. Those features are left to more advanced 7 | # libraries and/or the operating system. 8 | # 9 | # This file is part of the MiniVHD Project. 10 | # 11 | # Makefile for UNIX and Linux systems using GCC. 12 | # 13 | # Version: @(#)Makefile.GCC 1.0.2 2021/04/16 14 | # 15 | # Author: Fred N. van Kempen, 16 | # 17 | # Copyright 2021 Fred N. van Kempen. 18 | # 19 | # MIT License 20 | # 21 | # Permission is hereby granted, free of charge, to any person 22 | # obtaining a copy of this software and associated documenta- 23 | # tion files (the "Software"), to deal in the Software without 24 | # restriction, including without limitation the rights to use, 25 | # copy, modify, merge, publish, distribute, sublicense, and/or 26 | # sell copies of the Software, and to permit persons to whom 27 | # the Software is furnished to do so, subject to the following 28 | # conditions: 29 | # 30 | # The above copyright notice and this permission notice shall 31 | # be included in all copies or substantial portions of the 32 | # Software. 33 | # 34 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 35 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 36 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 37 | # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 38 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 39 | # FROM, OUT OF O R IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 40 | # DEALINGS IN THE SOFTWARE. 41 | # 42 | 43 | 44 | # Defaults for several build options (possibly defined in a chained file.) 45 | ifndef AUTODEP 46 | AUTODEP := n 47 | endif 48 | ifndef DEBUG 49 | DEBUG := n 50 | endif 51 | ifndef X64 52 | X64 := n 53 | endif 54 | ifndef STATIC 55 | STATIC := n 56 | endif 57 | 58 | 59 | # Name of the projects. 60 | PROGS := tester 61 | LIBS := libminivhd 62 | ifeq ($(DEBUG), y) 63 | PROGS := $(PROGS)-d 64 | LIBS := $(LIBS)-d 65 | endif 66 | 67 | 68 | # Select the desired platform. 69 | PLAT := Linux 70 | PLATDEFS := -DLINUX -DPLAT=\"$(PLAT)\" 71 | PLATNAME := linux 72 | PLATDIR := unix 73 | 74 | 75 | # Project settings. 76 | DEFS := 77 | 78 | 79 | ######################################################################### 80 | # Nothing should need changing from here on.. # 81 | ######################################################################### 82 | VPATH := unix 83 | 84 | 85 | # Select the required build environment. 86 | ifeq ($(X64), y) 87 | CPP := g++ -m64 88 | CC := gcc -m64 89 | ARCH := x64 90 | else 91 | CPP := g++ -m32 92 | CC := gcc -m32 93 | ARCH := x86 94 | endif 95 | PREPROC := cpp 96 | MCPP := cpp 97 | LINK := gcc 98 | AR := ar 99 | RANLIB := ranlib 100 | STRIP := strip 101 | 102 | SYSINC := 103 | SYSLIB := 104 | 105 | DEPS = -MMD -MF $*.d -c $< 106 | DEPFILE := $(PLATDIR)/.depends-$(PLATNAME) 107 | 108 | 109 | # Set up the correct toolchain flags. 110 | OPTS := -DUNIX $(PLATDEFS) 111 | ifeq ($(STATIC), y) 112 | OPTS := -DSTATIC 113 | endif 114 | AFLAGS := -msse2 -mfpmath=sse 115 | COPTS := -Wall 116 | CXXOPTS := -Wall 117 | DOPTS := 118 | LOPTS := 119 | ifeq ($(DEBUG), y) 120 | OPTS += -D_DEBUG 121 | DOPTS := -Og -ggdb 122 | ROPTS += -D_DEBUG 123 | else 124 | DFLAGS := -O3 125 | endif 126 | SYSLIBS := -lgcc #-lpthread 127 | 128 | 129 | # Final versions of the toolchain flags. 130 | LFLAGS := -pthread $(LOPTS) -L. 131 | CFLAGS := $(DEFS) $(OPTS) $(COPTS) $(DOPTS) -I. \ 132 | -fPIC -fno-strict-aliasing -fvisibility=hidden \ 133 | -fomit-frame-pointer -mstackrealign -Wall -Wundef 134 | CXXFLAGS := -I/usr/include/c++/4.8.4 \ 135 | $(DEFS) $(OPTS) $(CXXOPTS) $(COPTS) $(DOPTS) -I.\ 136 | -fPIC -fno-strict-aliasing -fvisibility=hidden \ 137 | -fvisibility-inlines-hidden -Wall -Wundef -Wunused-parameter \ 138 | -Wmissing-declarations -Wno-ctor-dtor-privacy -Woverloaded-virtual 139 | 140 | 141 | ######################################################################### 142 | # Create the (final) list of objects to build. # 143 | ######################################################################### 144 | 145 | LOBJ := cwalk.o xml2_encoding.o \ 146 | convert.o create.o io.o manage.o struct_rw.o util.o 147 | 148 | 149 | # Build module rules. 150 | ifeq ($(AUTODEP), y) 151 | %.o: %.c 152 | @echo $< 153 | @$(CC) $(CFLAGS) $(DEPS) -c $< 154 | 155 | %.o: %.cpp 156 | @echo $< 157 | @$(CPP) $(CXXFLAGS) $(DEPS) -c $< 158 | else 159 | %.o: %.c 160 | @echo $< 161 | $(CC) $(CFLAGS) -c $< 162 | 163 | %.o: %.cpp 164 | @echo $< 165 | @$(CPP) $(CXXFLAGS) -c $< 166 | 167 | %.d: %.c $(wildcard $*.d) 168 | @echo $< 169 | @$(CC) $(CFLAGS) $(DEPS) -E $< >NUL 170 | 171 | %.d: %.cpp $(wildcard $*.d) 172 | @echo $< 173 | @$(CPP) $(CXXFLAGS) $(DEPS) -E $< >NUL 174 | endif 175 | 176 | 177 | ifeq ($(STATIC),y) 178 | all: $(LIBS).a $(PROGS)_s 179 | else 180 | all: $(LIBS).so $(PROGS) 181 | endif 182 | 183 | 184 | $(LIBS).so: $(LOBJ) 185 | @echo Creating $(LIBS).so 186 | @$(CC) $(LFLAGS) -shared -o $(LIBS).so $(LOBJ) 187 | ifneq ($(DEBUG), y) 188 | @$(STRIP) --strip-unneeded $(LIBS).so 189 | endif 190 | 191 | 192 | $(LIBS).a: $(LOBJ) 193 | @echo Creating $@ .. 194 | @$(AR) rv $@ $(LOBJ) 195 | @$(RANLIB) $@ 196 | 197 | $(PROGS): tester.o 198 | @echo Linking $@ .. 199 | $(CC) $(LFLAGS) -o $@ $< $(SYSLIBS) -lminivhd 200 | ifneq ($(DEBUG), y) 201 | @$(STRIP) $@ 202 | endif 203 | 204 | $(PROGS)_s: tester.o 205 | @echo Linking $@ .. 206 | $(CC) $(LFLAGS) -o $@ $< $(SYSLIBS) -static -lminivhd -shared 207 | ifneq ($(DEBUG), y) 208 | @$(STRIP) $@ 209 | endif 210 | 211 | 212 | install: all 213 | @-mkdir ../bin 214 | @-mkdir ../include 215 | @-mkdir ../lib 216 | ifeq ($(X64), y) 217 | @-mkdir ../lib/x64 >NUL 218 | @-cp $(LNAME).a $(LNAME).dll.a ../lib/x64 219 | else 220 | @-mkdir ../lib/x86 >NUL 221 | @-cp $(LNAME).a $(LNAME).dll.a ../lib/x86 222 | endif 223 | @-cp $(LIBS).so ../bin 224 | @-cp minivhd.h ../include 225 | 226 | clean: 227 | @echo Cleaning objects.. 228 | @-rm -f *.o 229 | 230 | clobber: clean 231 | @echo Cleaning executables.. 232 | @-rm -f tester tester_s 233 | @echo Cleaning libraries.. 234 | @-rm -f *.so 235 | @-rm -f *.a 236 | @-rm -f *.d 237 | @-rm -f $(DEPFILE) 238 | 239 | ifneq ($(AUTODEP), y) 240 | depclean: 241 | @-rm -f $(DEPFILE) 242 | @echo Creating dependencies.. 243 | @echo # Run "make depends" to re-create this file. >$(DEPFILE) 244 | 245 | depends: DEPOBJ=$(OBJ:%.o=%.d) 246 | depends: depclean $(OBJ:%.o=%.d) 247 | @$(CAT) $(DEPOBJ) >>$(DEPFILE) 248 | # @-rm -f $(DEPOBJ) 249 | 250 | $(DEPFILE): 251 | endif 252 | 253 | 254 | # Module dependencies. 255 | ifeq ($(AUTODEP), y) 256 | #-include $(OBJ:%.o=%.d) (better, but sloooowwwww) 257 | -include *.d 258 | else 259 | include $(wildcard $(DEPFILE)) 260 | endif 261 | 262 | 263 | # End of Makefile.GCC. 264 | -------------------------------------------------------------------------------- /src/util.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MiniVHD Minimalist VHD implementation in C. 3 | * 4 | * This file is part of the MiniVHD Project. 5 | * 6 | * Utility functions. 7 | * 8 | * Version: @(#)util.c 1.0.4 2021/04/16 9 | * 10 | * Author: Sherman Perry, 11 | * 12 | * Copyright 2019-2021 Sherman Perry. 13 | * 14 | * MIT License 15 | * 16 | * Permission is hereby granted, free of charge, to any person 17 | * obtaining a copy of this software and associated documenta- 18 | * tion files (the "Software"), to deal in the Software without 19 | * restriction, including without limitation the rights to use, 20 | * copy, modify, merge, publish, distribute, sublicense, and/or 21 | * sell copies of the Software, and to permit persons to whom 22 | * the Software is furnished to do so, subject to the following 23 | * conditions: 24 | * 25 | * The above copyright notice and this permission notice shall 26 | * be included in all copies or substantial portions of the 27 | * Software. 28 | * 29 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 30 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 31 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 32 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 33 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 34 | * FROM, OUT OF O R IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 35 | * DEALINGS IN THE SOFTWARE. 36 | */ 37 | #ifndef _FILE_OFFSET_BITS 38 | # define _FILE_OFFSET_BITS 64 39 | #endif 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #define BUILDING_LIBRARY 50 | #include "minivhd.h" 51 | #include "internal.h" 52 | #include "xml2_encoding.h" 53 | 54 | 55 | uint16_t 56 | mvhd_from_be16(uint16_t val) 57 | { 58 | uint8_t *tmp = (uint8_t*)&val; 59 | uint16_t ret = 0; 60 | 61 | ret |= (uint16_t)tmp[0] << 8; 62 | ret |= (uint16_t)tmp[1] << 0; 63 | 64 | return ret; 65 | } 66 | 67 | 68 | uint32_t 69 | mvhd_from_be32(uint32_t val) 70 | { 71 | uint8_t *tmp = (uint8_t*)&val; 72 | uint32_t ret = 0; 73 | 74 | ret = (uint32_t)tmp[0] << 24; 75 | ret |= (uint32_t)tmp[1] << 16; 76 | ret |= (uint32_t)tmp[2] << 8; 77 | ret |= (uint32_t)tmp[3] << 0; 78 | 79 | return ret; 80 | } 81 | 82 | 83 | uint64_t 84 | mvhd_from_be64(uint64_t val) 85 | { 86 | uint8_t *tmp = (uint8_t*)&val; 87 | uint64_t ret = 0; 88 | 89 | ret = (uint64_t)tmp[0] << 56; 90 | ret |= (uint64_t)tmp[1] << 48; 91 | ret |= (uint64_t)tmp[2] << 40; 92 | ret |= (uint64_t)tmp[3] << 32; 93 | ret |= (uint64_t)tmp[4] << 24; 94 | ret |= (uint64_t)tmp[5] << 16; 95 | ret |= (uint64_t)tmp[6] << 8; 96 | ret |= (uint64_t)tmp[7] << 0; 97 | 98 | return ret; 99 | } 100 | 101 | 102 | uint16_t 103 | mvhd_to_be16(uint16_t val) 104 | { 105 | uint16_t ret = 0; 106 | uint8_t *tmp = (uint8_t*)&ret; 107 | 108 | tmp[0] = (val & 0xff00) >> 8; 109 | tmp[1] = (val & 0x00ff) >> 0; 110 | 111 | return ret; 112 | } 113 | 114 | 115 | uint32_t 116 | mvhd_to_be32(uint32_t val) 117 | { 118 | uint32_t ret = 0; 119 | uint8_t *tmp = (uint8_t*)&ret; 120 | 121 | tmp[0] = (val & 0xff000000) >> 24; 122 | tmp[1] = (val & 0x00ff0000) >> 16; 123 | tmp[2] = (val & 0x0000ff00) >> 8; 124 | tmp[3] = (val & 0x000000ff) >> 0; 125 | 126 | return ret; 127 | } 128 | 129 | 130 | uint64_t 131 | mvhd_to_be64(uint64_t val) 132 | { 133 | uint64_t ret = 0; 134 | uint8_t *tmp = (uint8_t*)&ret; 135 | 136 | tmp[0] = (uint8_t)((val & 0xff00000000000000) >> 56); 137 | tmp[1] = (uint8_t)((val & 0x00ff000000000000) >> 48); 138 | tmp[2] = (uint8_t)((val & 0x0000ff0000000000) >> 40); 139 | tmp[3] = (uint8_t)((val & 0x000000ff00000000) >> 32); 140 | tmp[4] = (uint8_t)((val & 0x00000000ff000000) >> 24); 141 | tmp[5] = (uint8_t)((val & 0x0000000000ff0000) >> 16); 142 | tmp[6] = (uint8_t)((val & 0x000000000000ff00) >> 8); 143 | tmp[7] = (uint8_t)((val & 0x00000000000000ff) >> 0); 144 | 145 | return ret; 146 | } 147 | 148 | 149 | void 150 | mvhd_generate_uuid(uint8_t* uuid) 151 | { 152 | int n; 153 | 154 | /* We aren't doing crypto here, so using system time as seed should be good enough */ 155 | srand((unsigned int)time(0)); 156 | 157 | for (n = 0; n < 16; n++) { 158 | uuid[n] = rand(); 159 | } 160 | uuid[6] &= 0x0F; 161 | uuid[6] |= 0x40; /* Type 4 */ 162 | uuid[8] &= 0x3F; 163 | uuid[8] |= 0x80; /* Variant 1 */ 164 | } 165 | 166 | 167 | uint32_t 168 | vhd_calc_timestamp(void) 169 | { 170 | time_t start_time; 171 | time_t curr_time; 172 | double vhd_time; 173 | 174 | start_time = MVHD_START_TS; /* 1 Jan 2000 00:00 */ 175 | curr_time = time(NULL); 176 | vhd_time = difftime(curr_time, start_time); 177 | 178 | return (uint32_t)vhd_time; 179 | } 180 | 181 | 182 | uint32_t 183 | mvhd_epoch_to_vhd_ts(time_t ts) 184 | { 185 | time_t start_time = MVHD_START_TS; 186 | double vhd_time; 187 | 188 | if (ts < start_time) 189 | return (uint32_t)start_time; 190 | 191 | vhd_time = difftime(ts, start_time); 192 | 193 | return (uint32_t)vhd_time; 194 | } 195 | 196 | 197 | time_t 198 | vhd_get_created_time(MVHDMeta *vhdm) 199 | { 200 | time_t vhd_time = (time_t)vhdm->footer.timestamp; 201 | time_t vhd_time_unix = MVHD_START_TS + vhd_time; 202 | 203 | return vhd_time_unix; 204 | } 205 | 206 | 207 | FILE * 208 | mvhd_fopen(const char* path, const char* mode, int* err) 209 | { 210 | FILE* f = NULL; 211 | #ifdef _WIN32 212 | size_t path_len = strlen(path); 213 | size_t mode_len = strlen(mode); 214 | mvhd_utf16 new_path[260] = {0}; 215 | int new_path_len = (sizeof new_path) - 2; 216 | mvhd_utf16 mode_str[5] = {0}; 217 | int new_mode_len = (sizeof mode_str) - 2; 218 | int path_res = UTF8ToUTF16LE((unsigned char*)new_path, &new_path_len, (const unsigned char*)path, (int*)&path_len); 219 | int mode_res = UTF8ToUTF16LE((unsigned char*)mode_str, &new_mode_len, (const unsigned char*)mode, (int*)&mode_len); 220 | 221 | if (path_res > 0 && mode_res > 0) { 222 | f = _wfopen(new_path, mode_str); 223 | if (f == NULL) { 224 | mvhd_errno = errno; 225 | *err = MVHD_ERR_FILE; 226 | } 227 | } else { 228 | if (path_res == -1 || mode_res == -1) { 229 | *err = MVHD_ERR_UTF_SIZE; 230 | } else if (path_res == -2 || mode_res == -2) { 231 | *err = MVHD_ERR_UTF_TRANSCODING_FAILED; 232 | } 233 | } 234 | #else 235 | f = fopen(path, mode); 236 | if (f == NULL) { 237 | mvhd_errno = errno; 238 | *err = MVHD_ERR_FILE; 239 | } 240 | #endif 241 | 242 | return f; 243 | } 244 | 245 | 246 | void 247 | mvhd_set_encoding_err(int encoding_retval, int* err) 248 | { 249 | if (encoding_retval == -1) { 250 | *err = MVHD_ERR_UTF_SIZE; 251 | } else if (encoding_retval == -2) { 252 | *err = MVHD_ERR_UTF_TRANSCODING_FAILED; 253 | } 254 | } 255 | 256 | 257 | uint64_t 258 | mvhd_calc_size_bytes(MVHDGeom *geom) 259 | { 260 | uint64_t img_size = (uint64_t)geom->cyl * (uint64_t)geom->heads * (uint64_t)geom->spt * (uint64_t)MVHD_SECTOR_SIZE; 261 | 262 | return img_size; 263 | } 264 | 265 | 266 | uint32_t 267 | mvhd_calc_size_sectors(MVHDGeom *geom) 268 | { 269 | uint32_t sector_size = (uint32_t)geom->cyl * (uint32_t)geom->heads * (uint32_t)geom->spt; 270 | 271 | return sector_size; 272 | } 273 | 274 | 275 | MVHDAPI MVHDGeom 276 | mvhd_get_geometry(MVHDMeta* vhdm) 277 | { 278 | MVHDGeom geometry = { 279 | .cyl = vhdm->footer.geom.cyl, 280 | .heads = vhdm->footer.geom.heads, 281 | .spt = vhdm->footer.geom.spt 282 | }; 283 | 284 | return geometry; 285 | } 286 | 287 | 288 | MVHDAPI uint64_t 289 | mvhd_get_current_size(MVHDMeta* vhdm) 290 | { 291 | return vhdm->footer.curr_sz; 292 | } 293 | 294 | 295 | uint32_t 296 | mvhd_gen_footer_checksum(MVHDFooter* footer) 297 | { 298 | uint32_t new_chk = 0; 299 | uint32_t orig_chk = footer->checksum; 300 | footer->checksum = 0; 301 | uint8_t* footer_bytes = (uint8_t*)footer; 302 | size_t i; 303 | 304 | for (i = 0; i < sizeof *footer; i++) 305 | new_chk += footer_bytes[i]; 306 | footer->checksum = orig_chk; 307 | 308 | return ~new_chk; 309 | } 310 | 311 | 312 | uint32_t 313 | mvhd_gen_sparse_checksum(MVHDSparseHeader* header) 314 | { 315 | uint32_t new_chk = 0; 316 | uint32_t orig_chk = header->checksum; 317 | header->checksum = 0; 318 | uint8_t* sparse_bytes = (uint8_t*)header; 319 | size_t i; 320 | 321 | for (i = 0; i < sizeof *header; i++) { 322 | new_chk += sparse_bytes[i]; 323 | } 324 | header->checksum = orig_chk; 325 | 326 | return ~new_chk; 327 | } 328 | 329 | 330 | MVHDAPI const char * 331 | mvhd_strerr(MVHDError err) 332 | { 333 | const char *s = "unknown error"; 334 | 335 | switch (err) { 336 | case MVHD_ERR_MEM: 337 | s = "memory allocation error"; 338 | break; 339 | 340 | case MVHD_ERR_FILE: 341 | s = "file error"; 342 | break; 343 | 344 | case MVHD_ERR_NOT_VHD: 345 | s = "file is not a VHD image"; 346 | break; 347 | 348 | case MVHD_ERR_TYPE: 349 | s = "unsupported VHD image type"; 350 | break; 351 | 352 | case MVHD_ERR_FOOTER_CHECKSUM: 353 | s = "invalid VHD footer checksum"; 354 | break; 355 | 356 | case MVHD_ERR_SPARSE_CHECKSUM: 357 | s = "invalid VHD sparse header checksum"; 358 | break; 359 | 360 | case MVHD_ERR_UTF_TRANSCODING_FAILED: 361 | s = "error converting path encoding"; 362 | break; 363 | 364 | case MVHD_ERR_UTF_SIZE: 365 | s = "buffer size mismatch when converting path encoding"; 366 | break; 367 | 368 | case MVHD_ERR_PATH_REL: 369 | s = "relative path detected where absolute path expected"; 370 | break; 371 | 372 | case MVHD_ERR_PATH_LEN: 373 | s = "path length exceeds MVHD_MAX_PATH"; 374 | break; 375 | 376 | case MVHD_ERR_PAR_NOT_FOUND: 377 | s = "parent VHD image not found"; 378 | break; 379 | 380 | case MVHD_ERR_INVALID_PAR_UUID: 381 | s = "UUID mismatch between child and parent VHD"; 382 | break; 383 | 384 | case MVHD_ERR_INVALID_GEOM: 385 | s = "invalid geometry detected"; 386 | break; 387 | 388 | case MVHD_ERR_INVALID_SIZE: 389 | s = "invalid size"; 390 | break; 391 | 392 | case MVHD_ERR_INVALID_BLOCK_SIZE: 393 | s = "invalid block size"; 394 | break; 395 | 396 | case MVHD_ERR_INVALID_PARAMS: 397 | s = "invalid parameters passed to function"; 398 | break; 399 | 400 | case MVHD_ERR_CONV_SIZE: 401 | s = "error converting image. Size mismatch detected"; 402 | break; 403 | 404 | default: 405 | break; 406 | } 407 | 408 | return s; 409 | } 410 | 411 | 412 | int64_t 413 | mvhd_ftello64(FILE* stream) 414 | { 415 | #ifdef _MSC_VER 416 | return _ftelli64(stream); 417 | #elif defined(__MINGW32__) 418 | return ftello64(stream); 419 | #else /* This should work with linux (with _FILE_OFFSET_BITS), and hopefully OS X and BSD */ 420 | return ftello(stream); 421 | #endif 422 | } 423 | 424 | 425 | int 426 | mvhd_fseeko64(FILE* stream, int64_t offset, int origin) 427 | { 428 | #ifdef _MSC_VER 429 | return _fseeki64(stream, offset, origin); 430 | #elif defined(__MINGW32__) 431 | return fseeko64(stream, offset, origin); 432 | #else /* This should work with linux (with _FILE_OFFSET_BITS), and hopefully OS X and BSD */ 433 | return fseeko(stream, offset, origin); 434 | #endif 435 | } 436 | 437 | 438 | uint32_t 439 | mvhd_crc32_for_byte(uint32_t r) 440 | { 441 | int j; 442 | 443 | for (j = 0; j < 8; ++j) 444 | r = (r & 1 ? 0 : (uint32_t)0xEDB88320L) ^ r >> 1; 445 | 446 | return r ^ (uint32_t)0xFF000000L; 447 | } 448 | 449 | 450 | uint32_t 451 | mvhd_crc32(const void* data, size_t n_bytes) 452 | { 453 | static uint32_t table[0x100]; 454 | size_t i; 455 | 456 | if (!*table) 457 | for (i = 0; i < 0x100; ++i) 458 | table[i] = mvhd_crc32_for_byte((uint32_t)i); 459 | 460 | uint32_t crc = 0; 461 | for (i = 0; i < n_bytes; ++i) 462 | crc = table[(uint8_t)crc ^ ((uint8_t*)data)[i]] ^ crc >> 8; 463 | 464 | return crc; 465 | } 466 | 467 | 468 | uint32_t 469 | mvhd_file_mod_timestamp(const char* path, int *err) 470 | { 471 | *err = 0; 472 | #ifdef _WIN32 473 | struct _stat file_stat; 474 | size_t path_len = strlen(path); 475 | mvhd_utf16 new_path[260] = {0}; 476 | int new_path_len = (sizeof new_path) - 2; 477 | int path_res = UTF8ToUTF16LE((unsigned char*)new_path, &new_path_len, (const unsigned char*)path, (int*)&path_len); 478 | 479 | if (path_res > 0) { 480 | int stat_res = _wstat(new_path, &file_stat); 481 | if (stat_res != 0) { 482 | mvhd_errno = errno; 483 | *err = MVHD_ERR_FILE; 484 | return 0; 485 | } 486 | return mvhd_epoch_to_vhd_ts(file_stat.st_mtime); 487 | } else { 488 | if (path_res == -1) { 489 | *err = MVHD_ERR_UTF_SIZE; 490 | } else if (path_res == -2) { 491 | *err = MVHD_ERR_UTF_TRANSCODING_FAILED; 492 | } 493 | return 0; 494 | } 495 | #else 496 | struct stat file_stat; 497 | int stat_res = stat(path, &file_stat); 498 | 499 | if (stat_res != 0) { 500 | mvhd_errno = errno; 501 | *err = MVHD_ERR_FILE; 502 | return 0; 503 | } 504 | return mvhd_epoch_to_vhd_ts(file_stat.st_mtime); 505 | #endif 506 | } 507 | -------------------------------------------------------------------------------- /src/version.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MiniVHD Minimalist VHD implementation in C. 3 | * 4 | * This file is part of the MiniVHD Project. 5 | * 6 | * Define library version and build info. 7 | * 8 | * Version: @(#)version.h 1.034 2021/04/16 9 | * 10 | * Author: Fred N. van Kempen, 11 | * 12 | * Copyright 2021 Fred N. van Kempen. 13 | * 14 | * MIT License 15 | * 16 | * Permission is hereby granted, free of charge, to any person 17 | * obtaining a copy of this software and associated documenta- 18 | * tion files (the "Software"), to deal in the Software without 19 | * restriction, including without limitation the rights to use, 20 | * copy, modify, merge, publish, distribute, sublicense, and/or 21 | * sell copies of the Software, and to permit persons to whom 22 | * the Software is furnished to do so, subject to the following 23 | * conditions: 24 | * 25 | * The above copyright notice and this permission notice shall 26 | * be included in all copies or substantial portions of the 27 | * Software. 28 | * 29 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 30 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 31 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 32 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 33 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 34 | * FROM, OUT OF O R IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 35 | * DEALINGS IN THE SOFTWARE. 36 | */ 37 | #ifndef MINIVHD_VERSION_H 38 | # define MINIVHD_VERSION_H 39 | 40 | 41 | /* Library name. */ 42 | #define LIB_NAME "MiniVHD" 43 | 44 | /* Version info. */ 45 | #define LIB_VER_MAJOR 1 46 | #define LIB_VER_MINOR 0 47 | #define LIB_VER_REV 3 48 | #define LIB_VER_PATCH 0 49 | 50 | 51 | /* Standard C preprocessor macros. */ 52 | #define STR_STRING(x) #x 53 | #define STR(x) STR_STRING(x) 54 | #define STR_RC(a,e) a ## , ## e 55 | 56 | 57 | /* These are used in the application. */ 58 | #define LIB_VER_NUM LIB_VER_MAJOR.LIB_VER_MINOR.LIB_VER_REV 59 | #if defined(LIB_VER_PATCH) && LIB_VER_PATCH > 0 60 | # define LIB_VER_NUM_4 LIB_VER_MAJOR.LIB_VER_MINOR.LIB_VER_REV.LIB_VER_PATCH 61 | #else 62 | # define LIB_VER_NUM_4 LIB_VER_MAJOR.LIB_VER_MINOR.LIB_VER_REV.0 63 | #endif 64 | #define LIB_VERSION STR(LIB_VER_NUM) 65 | #define LIB_VERSION_4 STR(LIB_VER_NUM_4) 66 | 67 | 68 | #endif /*MINIVHD_VERSION_H*/ 69 | -------------------------------------------------------------------------------- /src/win32/Makefile.MinGW: -------------------------------------------------------------------------------- 1 | # 2 | # MiniVHD Minimalist VHD implementation in C. 3 | # MiniVHD is a minimalist implementation of read/write/creation 4 | # of VHD files. It is designed to read and write to VHD files 5 | # at a sector level. It does not enable file access, or provide 6 | # mounting options. Those features are left to more advanced 7 | # libraries and/or the operating system. 8 | # 9 | # This file is part of the MiniVHD Project. 10 | # 11 | # Makefile for Windows systems using the MinGW32 environment. 12 | # 13 | # Version: @(#)Makefile.MinGW 1.0.3 2021/04/16 14 | # 15 | # Author: Fred N. van Kempen, 16 | # 17 | # Copyright 2021 Fred N. van Kempen. 18 | # Copyright 2019-2021 Sherman Perry. 19 | # 20 | # MIT License 21 | # 22 | # Permission is hereby granted, free of charge, to any person 23 | # obtaining a copy of this software and associated documenta- 24 | # tion files (the "Software"), to deal in the Software without 25 | # restriction, including without limitation the rights to use, 26 | # copy, modify, merge, publish, distribute, sublicense, and/or 27 | # sell copies of the Software, and to permit persons to whom 28 | # the Software is furnished to do so, subject to the following 29 | # conditions: 30 | # 31 | # The above copyright notice and this permission notice shall 32 | # be included in all copies or substantial portions of the 33 | # Software. 34 | # 35 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 36 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 37 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 38 | # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 39 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 40 | # FROM, OUT OF O R IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 41 | # DEALINGS IN THE SOFTWARE. 42 | # 43 | 44 | # Defaults for several build options (possibly defined in a chained file.) 45 | ifndef AUTODEP 46 | AUTODEP := n 47 | endif 48 | ifndef CROSS 49 | CROSS := n 50 | endif 51 | ifndef DEBUG 52 | DEBUG := n 53 | endif 54 | ifndef OPTIM 55 | OPTIM := n 56 | endif 57 | ifndef X64 58 | X64 := n 59 | endif 60 | ifndef STATIC 61 | STATIC := n 62 | endif 63 | 64 | 65 | # Name of the projects. 66 | PROGS := tester 67 | LIBS := minivhd 68 | ifeq ($(DEBUG), y) 69 | PROGS := $(PROGS)-d 70 | LIBS := $(LIBS)-d 71 | endif 72 | 73 | 74 | ######################################################################### 75 | # Nothing should need changing from here on.. # 76 | ######################################################################### 77 | VPATH := win32 78 | 79 | 80 | # 81 | # Select the required build environment. We have, uhm, many.. 82 | # 83 | ifneq ($(CROSS), n) 84 | # Cross-compiling (under Linux), select proper version. 85 | PREFIX := /usr/bin/ 86 | ifeq ($(X64), y) 87 | MINGW := x86_64-w64-mingw32 88 | else 89 | MINGW := i686-w64-mingw32 90 | endif 91 | 92 | ifeq ($(X64), y) 93 | CPP := $(PREFIX)/$(MINGW)-g++ -m64 94 | CC := $(PREFIX)/$(MINGW)-gcc -m64 95 | else 96 | CPP := $(PREFIX)/$(MINGW)-g++ -m32 97 | CC := $(PREFIX)/$(MINGW)-gcc -m32 98 | endif 99 | PREPROC := $(PREFIX)/$(MINGW)-cpp 100 | WINDRES := $(PREFIX)/$(MINGW)-windres 101 | 102 | SYSINC := -I/usr/$(MINGW)/include 103 | SYSLIB := -L/usr/$(MINGW)/lib 104 | else 105 | # Native compilation using MinGW for Windows. 106 | ifeq ($(X64), y) 107 | CPP := g++ -m64 108 | CC := gcc -m64 109 | else 110 | CPP := g++ -m32 111 | CC := gcc -m32 112 | endif 113 | PREPROC := cpp 114 | AR := ar 115 | RANLIB := ranlib 116 | STRIP := strip 117 | WINDRES := windres 118 | ifndef CAT 119 | CAT := cat 120 | endif 121 | 122 | SYSINC := 123 | SYSLIB := 124 | endif 125 | 126 | DEPS = -MMD -MF $*.d -c $< 127 | DEPFILE := win32/.depends-mingw 128 | 129 | # Set up the correct toolchain flags. 130 | OPTS := -D_CRT_NON_CONFORMING_SWPRINTFS \ 131 | -D__USE_MINGW_ANSI_STDIO_X 132 | ifeq ($(STATIC), y) 133 | OPTS += -DSTATIC 134 | endif 135 | AFLAGS := -msse2 -mfpmath=sse 136 | RFLAGS := --input-format=rc -O coff 137 | LFLAGS := 138 | ifneq ($(CROSS), n) 139 | OPTS += -DUSE_CROSS 140 | endif 141 | ifeq ($(X64), y) 142 | ifeq ($(OPTIM), y) 143 | DFLAGS := -march=native 144 | else 145 | DFLAGS := 146 | endif 147 | else 148 | ifeq ($(OPTIM), y) 149 | DFLAGS := -march=native 150 | else 151 | DFLAGS := -march=i686 152 | endif 153 | endif 154 | ifeq ($(DEBUG), y) 155 | DFLAGS += -ggdb -D_DEBUG 156 | RFLAGS += -D_DEBUG 157 | AOPTIM := 158 | ifndef COPTIM 159 | COPTIM := -Og 160 | endif 161 | else 162 | ifeq ($(OPTIM), y) 163 | AOPTIM := -mtune=native 164 | ifndef COPTIM 165 | COPTIM := -O3 166 | endif 167 | else 168 | ifndef COPTIM 169 | COPTIM := -O3 170 | endif 171 | endif 172 | endif 173 | 174 | SYSLIBS := #-mwindows 175 | SYSLIBS += -static -lgcc 176 | 177 | 178 | # Final versions of the toolchain flags. 179 | CFLAGS := $(OPTS) $(DFLAGS) $(COPTIM) $(AOPTIM) -I. \ 180 | $(AFLAGS) -fomit-frame-pointer -mstackrealign \ 181 | -Wall -Wundef -Wshadow -Wunused-parameter \ 182 | -Wmissing-declarations 183 | LFLAGS := -L. 184 | 185 | 186 | ######################################################################### 187 | # Create the (final) list of objects to build. # 188 | ######################################################################### 189 | 190 | LNAME := lib$(LIBS) 191 | LOBJ := cwalk.o xml2_encoding.o \ 192 | convert.o create.o io.o manage.o struct_rw.o util.o 193 | 194 | 195 | # Build module rules. 196 | ifeq ($(AUTODEP), y) 197 | %.o: %.c 198 | @echo $< 199 | @$(CC) $(CFLAGS) $(DEPS) -c $< 200 | 201 | %.o: %.cpp 202 | @echo $< 203 | @$(CPP) $(CXXFLAGS) $(DEPS) -c $< 204 | else 205 | %.o: %.c 206 | @echo $< 207 | $(CC) $(CFLAGS) -c $< 208 | 209 | %.o: %.cpp 210 | @echo $< 211 | @$(CPP) $(CXXFLAGS) -c $< 212 | 213 | %.d: %.c $(wildcard $*.d) 214 | @echo $< 215 | @$(CC) $(CFLAGS) $(DEPS) -E $< >NUL 216 | 217 | %.d: %.cpp $(wildcard $*.d) 218 | @echo $< 219 | @$(CPP) $(CXXFLAGS) $(DEPS) -E $< >NUL 220 | endif 221 | 222 | 223 | ifeq ($(STATIC), y) 224 | all: $(LNAME).a $(PROGS)_s.exe 225 | else 226 | all: $(LIBS).dll $(PROGS).exe 227 | endif 228 | 229 | 230 | $(LIBS).res: win32/minivhd.rc 231 | @echo Processing $< 232 | @$(WINDRES) $(RFLAGS) -i $< -o $@ 233 | 234 | $(LIBS).dll: $(LOBJ) $(LIBS).res 235 | @echo Linking $(LIBS).dll .. 236 | @$(CC) $(LFLAGS) -shared -o $@ \ 237 | -Wl,--out-implib,$(LNAME).dll.a \ 238 | $(LOBJ) $(LIBS).res $(SYSLIBS) 239 | ifneq ($(DEBUG), y) 240 | @strip --strip-unneeded $(LIBS).dll 241 | endif 242 | 243 | $(LNAME).a: $(LOBJ) 244 | @echo Creating $@ .. 245 | @$(AR) rv $@ $(LOBJ) 246 | @$(RANLIB) $@ 247 | 248 | $(PROGS).exe: $(PROGS).o 249 | @echo Linking $@ .. 250 | @$(CC) $(LFLAGS) -o $@ $(PROGS).o $(SYSLIBS) -lminivhd.dll 251 | ifneq ($(DEBUG), y) 252 | @$(STRIP) $@ 253 | endif 254 | 255 | $(PROGS)_s.exe: $(PROGS).o 256 | @echo Linking $@ .. 257 | @$(CC) $(LFLAGS) -o $@ $(PROGS).o $(SYSLIBS) -lminivhd 258 | ifneq ($(DEBUG), y) 259 | @$(STRIP) $@ 260 | endif 261 | 262 | 263 | install: all 264 | @-mkdir ..\bin >NUL 265 | @-mkdir ..\include >NUL 266 | @-mkdir ..\lib >NUL 267 | ifeq ($(X64), y) 268 | @-mkdir ..\lib\x64 >NUL 269 | @-cp $(LNAME).a $(LNAME).dll.a ../lib/x64 270 | else 271 | @-mkdir ..\lib\x86 >NUL 272 | @-cp $(LNAME).a $(LNAME).dll.a ../lib/x86 273 | endif 274 | @-cp $(LIBS).dll ../bin 275 | @-cp minivhd.h ../include 276 | 277 | clean: 278 | @echo Cleaning objects.. 279 | @-rm -f *.o 280 | @-rm -f *.res 281 | 282 | clobber: clean 283 | @echo Cleaning executables.. 284 | @-rm -f *.exe 285 | @echo Cleaning libraries.. 286 | @-rm -f *.dll 287 | @-rm -f *.a 288 | @-rm -f *.d 289 | @-rm -f $(DEPFILE) 290 | 291 | ifneq ($(AUTODEP), y) 292 | depclean: 293 | @-rm -f $(DEPFILE) 294 | @echo Creating dependencies.. 295 | @echo # Run "make depends" to re-create this file. >$(DEPFILE) 296 | 297 | depends: DEPOBJ=$(LOBJ:%.o=%.d) 298 | depends: depclean $(LOBJ:%.o=%.d) 299 | @$(CAT) $(DEPOBJ) >>$(DEPFILE) 300 | # @-rm -f $(DEPOBJ) 301 | 302 | $(DEPFILE): 303 | endif 304 | 305 | 306 | # Module dependencies. 307 | ifeq ($(AUTODEP), y) 308 | #-include $(LOBJ:%.o=%.d) (better, but sloooowwwww) 309 | -include *.d 310 | else 311 | include $(wildcard $(DEPFILE)) 312 | endif 313 | 314 | 315 | # End of Makefile.MinGW. 316 | -------------------------------------------------------------------------------- /src/win32/Makefile.VC: -------------------------------------------------------------------------------- 1 | # 2 | # MiniVHD Minimalist VHD implementation in C. 3 | # MiniVHD is a minimalist implementation of read/write/creation 4 | # of VHD files. It is designed to read and write to VHD files 5 | # at a sector level. It does not enable file access, or provide 6 | # mounting options. Those features are left to more advanced 7 | # libraries and/or the operating system. 8 | # 9 | # This file is part of the MiniVHD Project. 10 | # 11 | # Makefile for Windows using Microsoft Visual Studio. 12 | # 13 | # Version: @(#)Makefile.VC 1.0.2 2021/04/16 14 | # 15 | # Author: Fred N. van Kempen, 16 | # 17 | # Copyright 2021 Fred N. van Kempen. 18 | # Copyright 2019-2021 Sherman Perry. 19 | # 20 | # MIT License 21 | # 22 | # Permission is hereby granted, free of charge, to any person 23 | # obtaining a copy of this software and associated documenta- 24 | # tion files (the "Software"), to deal in the Software without 25 | # restriction, including without limitation the rights to use, 26 | # copy, modify, merge, publish, distribute, sublicense, and/or 27 | # sell copies of the Software, and to permit persons to whom 28 | # the Software is furnished to do so, subject to the following 29 | # conditions: 30 | # 31 | # The above copyright notice and this permission notice shall 32 | # be included in all copies or substantial portions of the 33 | # Software. 34 | # 35 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 36 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 37 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 38 | # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 39 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 40 | # FROM, OUT OF O R IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 41 | # DEALINGS IN THE SOFTWARE. 42 | # 43 | 44 | 45 | # Defaults for several build options (possibly defined in a chained file.) 46 | ifndef AUTODEP 47 | AUTODEP := n 48 | endif 49 | ifndef DEBUG 50 | DEBUG := n 51 | endif 52 | ifndef OPTIM 53 | OPTIM := n 54 | endif 55 | ifndef X64 56 | X64 := n 57 | endif 58 | ifndef STATIC 59 | STATIC := n 60 | endif 61 | 62 | 63 | # Name of the projects. 64 | PROGS := tester 65 | LIBS := minivhd 66 | ifeq ($(DEBUG), y) 67 | PROGS := $(PROGS)-d 68 | LIBS := $(LIBS)-d 69 | endif 70 | 71 | 72 | ######################################################################### 73 | # Nothing should need changing from here on.. # 74 | ######################################################################### 75 | 76 | VPATH := win32 77 | 78 | 79 | # 80 | # Select the required build environment. 81 | # 82 | ifeq ($(X64), y) 83 | ARCH := x64 84 | CPP := cl -nologo 85 | CC := cl -nologo 86 | AR := lib -nologo 87 | else 88 | ARCH := x86 89 | CPP := cl -nologo 90 | CC := cl -nologo #-TP 91 | AR := lib -nologo 92 | endif 93 | PREPROC := cl -nologo -EP 94 | MCPP := mcpp.exe 95 | LINK := link -nologo 96 | WINDRES := rc -nologo -r -d_MSC_VER 97 | 98 | SYSINC := 99 | SYSLIB := 100 | 101 | DEPS = -MMD -MF $*.d 102 | DEPFILE := win32\.depends-msvc 103 | 104 | 105 | # Set up the correct toolchain flags. 106 | OPTS := -D__MSC__ \ 107 | -D_USE_MATH_DEFINES \ 108 | -D_CRT_NONSTDC_NO_DEPRECATE \ 109 | -D_WINSOCK_DEPRECATED_NO_WARNINGS \ 110 | -D_CRT_SECURE_NO_WARNINGS \ 111 | -D_CRT_STDIO_ISO_WIDE_SPECIFIERS 112 | ifeq ($(STATIC), y) 113 | OPTS += -DSTATIC 114 | endif 115 | AFLAGS := #/arch:SSE2 116 | RFLAGS := /n 117 | COPTS := -W3 118 | CXXOPTS := -EHsc 119 | DOPTS := 120 | ifeq ($(X64), y) 121 | LOPTS := -MACHINE:$(ARCH) 122 | LOPTS_C := -SUBSYSTEM:CONSOLE 123 | LOPTS_W := -SUBSYSTEM:WINDOWS 124 | else 125 | LOPTS := -MACHINE:$(ARCH) 126 | LOPTS_C := -SUBSYSTEM:CONSOLE,5.01 127 | LOPTS_W := -SUBSYSTEM:WINDOWS,5.01 128 | endif 129 | OPTS += $(SYSINC) 130 | ifeq ($(OPTIM), y) 131 | AFLAGS := /arch:AVX2 132 | endif 133 | ifeq ($(DEBUG), y) 134 | OPTS += -D_DEBUG 135 | DOPTS += -MTd -Gs -Zi 136 | RFLAGS += -D_DEBUG 137 | LOPTS += -debug 138 | ifndef COPTIM 139 | COPTIM := -Od 140 | endif 141 | else 142 | LOPTS += #-LTCG 143 | DOPTS := -MT #-GL 144 | ifndef COPTIM 145 | COPTIM := -O2 146 | endif 147 | endif 148 | SYSLIBS := 149 | ifeq ($(DEBUG), y) 150 | SYSLIBS += 151 | endif 152 | 153 | 154 | # Final versions of the toolchain flags. 155 | LFLAGS := $(LOPTS) 156 | CFLAGS := $(OPTS) $(COPTS) $(COPTIM) $(DOPTS) $(AFLAGS) -I. 157 | CXXFLAGS := $(OPTS) $(CXXOPTS) $(COPTS) $(COPTIM) $(DOPTS) $(AFLAGS) -I. 158 | 159 | 160 | ######################################################################### 161 | # Create the (final) list of objects to build. # 162 | ######################################################################### 163 | 164 | LOBJ := cwalk.obj xml2_encoding.obj \ 165 | convert.obj create.obj io.obj manage.obj \ 166 | struct_rw.obj util.obj 167 | 168 | 169 | # Build module rules. 170 | ifeq ($(AUTODEP), y) 171 | %.obj: %.c 172 | @$(CC) $(CFLAGS) -Fo$@ -c $< 173 | @$(MCPP) $(OPTS) $(DEPS) $< 174 | 175 | %.obj: %.cpp 176 | @$(CPP) $(CXXFLAGS) -Fo$@ -c $< 177 | @$(MCPP) $(OPTS) $(DEPS) $< 178 | else 179 | %.obj: %.c 180 | @$(CC) $(CFLAGS) -Fo$@ -c $< 181 | 182 | %.obj: %.cpp 183 | @$(CPP) $(CXXFLAGS) -Fo$@ -c $< 184 | 185 | %.d: %.c $(wildcard $*.d) 186 | @$(MCPP) $(OPTS) $(DEPS) $< >NUL 187 | 188 | %.d: %.cpp $(wildcard $*.d) 189 | @$(MCPP) $(OPTS) $(DEPS) $< >NUL 190 | endif 191 | 192 | 193 | ifeq ($(STATIC), y) 194 | all: $(LIBS)_s.lib $(PROGS)_s.exe 195 | else 196 | all: $(LIBS).dll $(PROGS).exe 197 | endif 198 | 199 | $(LIBS).res: win32\$(LIBS).rc 200 | @echo Processing $< 201 | @$(WINDRES) $(RFLAGS) -fo$@ $< 202 | 203 | $(LIBS).dll: $(LOBJ) $(LIBS).res 204 | @echo Linking $(LIBS).dll .. 205 | @$(LINK) /DLL $(LFLAGS) $(LOPTS_W) -OUT:$(LIBS).dll \ 206 | $(LOBJ) $(LIBS).res $(SYSLIBS) 207 | 208 | $(LIBS)_s.lib: $(LOBJ) 209 | @echo Creating $@ .. 210 | @$(AR) /OUT:$@ $(LOBJ) 211 | 212 | $(PROGS).exe: $(PROGS).obj 213 | @echo Linking $@ .. 214 | @$(LINK) $(LFLAGS) /OUT:$@ $(PROGS).obj $(SYSLIBS) minivhd.lib 215 | 216 | $(PROGS)_s.exe: $(PROGS).obj 217 | @echo Linking $@ .. 218 | @$(LINK) $(LFLAGS) /OUT:$@ $(PROGS).obj $(SYSLIBS) minivhd_s.lib 219 | 220 | 221 | install: all 222 | @-mkdir ..\bin >NUL 223 | @-mkdir ..\include >NUL 224 | @-mkdir ..\lib >NUL 225 | ifeq ($(X64), y) 226 | @-mkdir ..\lib\x64 >NUL 227 | @-copy *.lib ..\lib\x64 228 | else 229 | @-mkdir ..\lib\x86 >NUL 230 | @-copy *.lib ..\lib\x86 231 | endif 232 | @-copy $(LIBS).dll ..\bin 233 | @-copy minivhd.h ..\include 234 | 235 | 236 | clean: 237 | @echo Cleaning objects.. 238 | @-del *.obj 2>NUL 239 | @-del *.res 2>NUL 240 | 241 | clobber: clean 242 | @echo Cleaning executables.. 243 | @-del *.exe 2>NUL 244 | ifeq ($(DEBUG), y) 245 | @-del *.ilk 2>NUL 246 | @-del *.pdb 2>NUL 247 | endif 248 | @echo Cleaning libraries.. 249 | @-del *.dll 2>NUL 250 | @-del *.exp 2>NUL 251 | @-del *.lib 2>NUL 252 | @-del *.d 2>NUL 253 | # @del $(DEPFILE) 2>NUL 254 | 255 | 256 | ifneq ($(AUTODEP), y) 257 | depclean: 258 | @del $(DEPFILE) 2>NUL 259 | @echo Creating dependencies.. 260 | @echo # Run "make depends" to re-create this file. >$(DEPFILE) 261 | 262 | depends: DEPOBJ=$(OBJ:%.obj=%.d) 263 | depends: depclean $(OBJ:%.obj=%.d) 264 | @-cat $(DEPOBJ) >>$(DEPFILE) 265 | @del $(DEPOBJ) 266 | 267 | $(DEPFILE): 268 | endif 269 | 270 | 271 | # Module dependencies. 272 | ifeq ($(AUTODEP), y) 273 | #-include $(OBJ:%.obj=%.d) (better, but sloooowwwww) 274 | -include *.d 275 | else 276 | include $(wildcard $(DEPFILE)) 277 | endif 278 | 279 | 280 | # End of Makefile.VC. 281 | -------------------------------------------------------------------------------- /src/win32/icons/minivhd.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shermp/MiniVHD/bd02b4ad42b3cd4fa1ae3ef38f9f698edfc06582/src/win32/icons/minivhd.ico -------------------------------------------------------------------------------- /src/win32/minivhd.rc: -------------------------------------------------------------------------------- 1 | /* 2 | * MiniVHD Minimalist VHD implementation in C. 3 | * 4 | * This file is part of the MiniVHD Project. 5 | * 6 | * Application resource script for Windows. 7 | * 8 | * Version: @(#)minivhd.rc 1.0.2 2021/03/22 9 | * 10 | * Author: Sherman Perry, 11 | * 12 | * Copyright 2019-2021 Sherman Perry. 13 | * 14 | * MIT License 15 | * 16 | * Permission is hereby granted, free of charge, to any person 17 | * obtaining a copy of this software and associated documenta- 18 | * tion files (the "Software"), to deal in the Software without 19 | * restriction, including without limitation the rights to use, 20 | * copy, modify, merge, publish, distribute, sublicense, and/or 21 | * sell copies of the Software, and to permit persons to whom 22 | * the Software is furnished to do so, subject to the following 23 | * conditions: 24 | * 25 | * The above copyright notice and this permission notice shall 26 | * be included in all copies or substantial portions of the 27 | * Software. 28 | * 29 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 30 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 31 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 32 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 33 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 34 | * FROM, OUT OF O R IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 35 | * DEALINGS IN THE SOFTWARE. 36 | */ 37 | #include 38 | #include "../version.h" 39 | 40 | 41 | 100 ICON DISCARDABLE "win32/icons/minivhd.ico" 42 | 43 | 44 | VS_VERSION_INFO VERSIONINFO 45 | #ifdef LIB_VER_PATCH 46 | FILEVERSION LIB_VER_MAJOR,LIB_VER_MINOR,LIB_VER_REV,LIB_VER_PATCH 47 | PRODUCTVERSION LIB_VER_MAJOR,LIB_VER_MINOR,LIB_VER_REV,LIB_VER_PATCH 48 | #else 49 | FILEVERSION LIB_VER_MAJOR,LIB_VER_MINOR,LIB_VER_REV,0 50 | PRODUCTVERSION LIB_VER_MAJOR,LIB_VER_MINOR,LIB_VER_REV,0 51 | #endif 52 | FILEFLAGSMASK 0x3fL 53 | #ifndef RELEASE_BUILD 54 | # ifdef _DEBUG 55 | FILEFLAGS VS_FF_SPECIALBUILD | VS_FF_DEBUG 56 | # else 57 | FILEFLAGS VS_FF_SPECIALBUILD 58 | # endif 59 | #else 60 | # ifdef _DEBUG 61 | FILEFLAGS VS_FF_DEBUG 62 | # else 63 | FILEFLAGS 0x0L 64 | # endif 65 | #endif 66 | FILEOS VOS_NT_WINDOWS32 67 | FILETYPE VFT_DLL 68 | FILESUBTYPE 0x0L 69 | BEGIN 70 | BLOCK "StringFileInfo" 71 | BEGIN 72 | BLOCK "0409fde9" 73 | BEGIN 74 | VALUE "CompanyName", "Sherman Perry and Contributors" 75 | VALUE "FileDescription", "MiniVHD" 76 | VALUE "FileVersion", LIB_VERSION 77 | VALUE "InternalName", LIB_NAME 78 | VALUE "LegalCopyright", "Copyright 2020,2021" 79 | VALUE "OriginalFilename", "libminivhd.dll" 80 | VALUE "ProductName", "MiniVHD Library" 81 | VALUE "ProductVersion", LIB_VERSION 82 | END 83 | END 84 | BLOCK "VarFileInfo" 85 | BEGIN 86 | VALUE "Translation", 0x0409, 65001 87 | END 88 | END 89 | -------------------------------------------------------------------------------- /src/xml2_encoding.c: -------------------------------------------------------------------------------- 1 | /* 2 | * encoding.c : implements the encoding conversion functions needed for XML 3 | * 4 | * Related specs: 5 | * rfc2044 (UTF-8 and UTF-16) F. Yergeau Alis Technologies 6 | * rfc2781 UTF-16, an encoding of ISO 10646, P. Hoffman, F. Yergeau 7 | * [ISO-10646] UTF-8 and UTF-16 in Annexes 8 | * [ISO-8859-1] ISO Latin-1 characters codes. 9 | * [UNICODE] The Unicode Consortium, "The Unicode Standard -- 10 | * Worldwide Character Encoding -- Version 1.0", Addison- 11 | * Wesley, Volume 1, 1991, Volume 2, 1992. UTF-8 is 12 | * described in Unicode Technical Report #4. 13 | * [US-ASCII] Coded Character Set--7-bit American Standard Code for 14 | * Information Interchange, ANSI X3.4-1986. 15 | * 16 | * See Copyright for the status of this software. 17 | * 18 | * daniel@veillard.com 19 | * 20 | * Original code for IsoLatin1 and UTF-16 by "Martin J. Duerst" 21 | * 22 | * Adapted and abridged for MiniVHD by Sherman Perry 23 | */ 24 | #include 25 | #include 26 | #include 27 | #include 28 | #define BUILDING_LIBRARY 29 | #include "minivhd.h" 30 | #include "internal.h" 31 | #include "xml2_encoding.h" 32 | 33 | 34 | static int xmlLittleEndian = 1; 35 | 36 | 37 | /* Note: extracted from original 'void xmlInitCharEncodingHandlers(void)' function */ 38 | void xmlEncodingInit(void) 39 | { 40 | unsigned short int tst = 0x1234; 41 | unsigned char *ptr = (unsigned char *) &tst; 42 | 43 | if (*ptr == 0x12) xmlLittleEndian = 0; 44 | else if (*ptr == 0x34) xmlLittleEndian = 1; 45 | } 46 | 47 | /** 48 | * UTF16LEToUTF8: 49 | * @out: a pointer to an array of bytes to store the result 50 | * @outlen: the length of @out 51 | * @inb: a pointer to an array of UTF-16LE passwd as a byte array 52 | * @inlenb: the length of @in in UTF-16LE chars 53 | * 54 | * Take a block of UTF-16LE ushorts in and try to convert it to an UTF-8 55 | * block of chars out. This function assumes the endian property 56 | * is the same between the native type of this machine and the 57 | * inputed one. 58 | * 59 | * Returns the number of bytes written, or -1 if lack of space, or -2 60 | * if the transcoding fails (if *in is not a valid utf16 string) 61 | * The value of *inlen after return is the number of octets consumed 62 | * if the return value is positive, else unpredictable. 63 | */ 64 | int UTF16LEToUTF8(unsigned char* out, int *outlen, 65 | const unsigned char* inb, int *inlenb) 66 | { 67 | unsigned char* outstart = out; 68 | const unsigned char* processed = inb; 69 | unsigned char* outend = out + *outlen; 70 | unsigned short* in = (unsigned short*) inb; 71 | unsigned short* inend; 72 | unsigned int c, d, inlen; 73 | unsigned char *tmp; 74 | int bits; 75 | 76 | if ((*inlenb % 2) == 1) 77 | (*inlenb)--; 78 | inlen = *inlenb / 2; 79 | inend = in + inlen; 80 | while ((in < inend) && (out - outstart + 5 < *outlen)) { 81 | if (xmlLittleEndian) { 82 | c= *in++; 83 | } else { 84 | tmp = (unsigned char *) in; 85 | c = *tmp++; 86 | c = c | (((unsigned int)*tmp) << 8); 87 | in++; 88 | } 89 | if ((c & 0xFC00) == 0xD800) { /* surrogates */ 90 | if (in >= inend) { /* (in > inend) shouldn't happens */ 91 | break; 92 | } 93 | if (xmlLittleEndian) { 94 | d = *in++; 95 | } else { 96 | tmp = (unsigned char *) in; 97 | d = *tmp++; 98 | d = d | (((unsigned int)*tmp) << 8); 99 | in++; 100 | } 101 | if ((d & 0xFC00) == 0xDC00) { 102 | c &= 0x03FF; 103 | c <<= 10; 104 | c |= d & 0x03FF; 105 | c += 0x10000; 106 | } 107 | else { 108 | *outlen = (int)(out - outstart); 109 | *inlenb = (int)(processed - inb); 110 | return(-2); 111 | } 112 | } 113 | 114 | /* assertion: c is a single UTF-4 value */ 115 | if (out >= outend) 116 | break; 117 | if (c < 0x80) { *out++= c; bits= -6; } 118 | else if (c < 0x800) { *out++= ((c >> 6) & 0x1F) | 0xC0; bits= 0; } 119 | else if (c < 0x10000) { *out++= ((c >> 12) & 0x0F) | 0xE0; bits= 6; } 120 | else { *out++= ((c >> 18) & 0x07) | 0xF0; bits= 12; } 121 | 122 | for ( ; bits >= 0; bits-= 6) { 123 | if (out >= outend) 124 | break; 125 | *out++= ((c >> bits) & 0x3F) | 0x80; 126 | } 127 | processed = (const unsigned char*) in; 128 | } 129 | *outlen = (int)(out - outstart); 130 | *inlenb = (int)(processed - inb); 131 | return(*outlen); 132 | } 133 | 134 | /** 135 | * UTF8ToUTF16LE: 136 | * @outb: a pointer to an array of bytes to store the result 137 | * @outlen: the length of @outb 138 | * @in: a pointer to an array of UTF-8 chars 139 | * @inlen: the length of @in 140 | * 141 | * Take a block of UTF-8 chars in and try to convert it to an UTF-16LE 142 | * block of chars out. 143 | * 144 | * Returns the number of bytes written, or -1 if lack of space, or -2 145 | * if the transcoding failed. 146 | */ 147 | int UTF8ToUTF16LE(unsigned char* outb, int *outlen, 148 | const unsigned char* in, int *inlen) 149 | { 150 | unsigned short* out = (unsigned short*) outb; 151 | const unsigned char* processed = in; 152 | const unsigned char *const instart = in; 153 | unsigned short* outstart= out; 154 | unsigned short* outend; 155 | const unsigned char* inend; 156 | unsigned int c, d; 157 | int trailing; 158 | unsigned char *tmp; 159 | unsigned short tmp1, tmp2; 160 | 161 | /* UTF16LE encoding has no BOM */ 162 | if ((out == NULL) || (outlen == NULL) || (inlen == NULL)) return(-1); 163 | if (in == NULL) { 164 | *outlen = 0; 165 | *inlen = 0; 166 | return(0); 167 | } 168 | inend= in + *inlen; 169 | outend = out + (*outlen / 2); 170 | while (in < inend) { 171 | d= *in++; 172 | if (d < 0x80) { c= d; trailing= 0; } 173 | else if (d < 0xC0) { 174 | /* trailing byte in leading position */ 175 | *outlen = (int)((out - outstart) * 2); 176 | *inlen = (int)(processed - instart); 177 | return(-2); 178 | } else if (d < 0xE0) { c= d & 0x1F; trailing= 1; } 179 | else if (d < 0xF0) { c= d & 0x0F; trailing= 2; } 180 | else if (d < 0xF8) { c= d & 0x07; trailing= 3; } 181 | else { 182 | /* no chance for this in UTF-16 */ 183 | *outlen = (int)((out - outstart) * 2); 184 | *inlen = (int)(processed - instart); 185 | return(-2); 186 | } 187 | 188 | if (inend - in < trailing) { 189 | break; 190 | } 191 | 192 | for ( ; trailing; trailing--) { 193 | if ((in >= inend) || (((d= *in++) & 0xC0) != 0x80)) 194 | break; 195 | c <<= 6; 196 | c |= d & 0x3F; 197 | } 198 | 199 | /* assertion: c is a single UTF-4 value */ 200 | if (c < 0x10000) { 201 | if (out >= outend) 202 | break; 203 | if (xmlLittleEndian) { 204 | *out++ = c; 205 | } else { 206 | tmp = (unsigned char *) out; 207 | *tmp = c ; 208 | *(tmp + 1) = c >> 8 ; 209 | out++; 210 | } 211 | } 212 | else if (c < 0x110000) { 213 | if (out+1 >= outend) 214 | break; 215 | c -= 0x10000; 216 | if (xmlLittleEndian) { 217 | *out++ = 0xD800 | (c >> 10); 218 | *out++ = 0xDC00 | (c & 0x03FF); 219 | } else { 220 | tmp1 = 0xD800 | (c >> 10); 221 | tmp = (unsigned char *) out; 222 | *tmp = (unsigned char) tmp1; 223 | *(tmp + 1) = tmp1 >> 8; 224 | out++; 225 | 226 | tmp2 = 0xDC00 | (c & 0x03FF); 227 | tmp = (unsigned char *) out; 228 | *tmp = (unsigned char) tmp2; 229 | *(tmp + 1) = tmp2 >> 8; 230 | out++; 231 | } 232 | } 233 | else 234 | break; 235 | processed = in; 236 | } 237 | *outlen = (int)((out - outstart) * 2); 238 | *inlen = (int)(processed - instart); 239 | return(*outlen); 240 | } 241 | 242 | /** 243 | * UTF16BEToUTF8: 244 | * @out: a pointer to an array of bytes to store the result 245 | * @outlen: the length of @out 246 | * @inb: a pointer to an array of UTF-16 passed as a byte array 247 | * @inlenb: the length of @in in UTF-16 chars 248 | * 249 | * Take a block of UTF-16 ushorts in and try to convert it to an UTF-8 250 | * block of chars out. This function assumes the endian property 251 | * is the same between the native type of this machine and the 252 | * inputed one. 253 | * 254 | * Returns the number of bytes written, or -1 if lack of space, or -2 255 | * if the transcoding fails (if *in is not a valid utf16 string) 256 | * The value of *inlen after return is the number of octets consumed 257 | * if the return value is positive, else unpredictable. 258 | */ 259 | int UTF16BEToUTF8(unsigned char* out, int *outlen, 260 | const unsigned char* inb, int *inlenb) 261 | { 262 | unsigned char* outstart = out; 263 | const unsigned char* processed = inb; 264 | unsigned char* outend = out + *outlen; 265 | unsigned short* in = (unsigned short*) inb; 266 | unsigned short* inend; 267 | unsigned int c, d, inlen; 268 | unsigned char *tmp; 269 | int bits; 270 | 271 | if ((*inlenb % 2) == 1) 272 | (*inlenb)--; 273 | inlen = *inlenb / 2; 274 | inend= in + inlen; 275 | while (in < inend) { 276 | if (xmlLittleEndian) { 277 | tmp = (unsigned char *) in; 278 | c = *tmp++; 279 | c = c << 8; 280 | c = c | (unsigned int) *tmp; 281 | in++; 282 | } else { 283 | c= *in++; 284 | } 285 | if ((c & 0xFC00) == 0xD800) { /* surrogates */ 286 | if (in >= inend) { /* (in > inend) shouldn't happens */ 287 | *outlen = (int)(out - outstart); 288 | *inlenb = (int)(processed - inb); 289 | return(-2); 290 | } 291 | if (xmlLittleEndian) { 292 | tmp = (unsigned char *) in; 293 | d = *tmp++; 294 | d = d << 8; 295 | d = d | (unsigned int) *tmp; 296 | in++; 297 | } else { 298 | d= *in++; 299 | } 300 | if ((d & 0xFC00) == 0xDC00) { 301 | c &= 0x03FF; 302 | c <<= 10; 303 | c |= d & 0x03FF; 304 | c += 0x10000; 305 | } 306 | else { 307 | *outlen = (int)(out - outstart); 308 | *inlenb = (int)(processed - inb); 309 | return(-2); 310 | } 311 | } 312 | 313 | /* assertion: c is a single UTF-4 value */ 314 | if (out >= outend) 315 | break; 316 | if (c < 0x80) { *out++= c; bits= -6; } 317 | else if (c < 0x800) { *out++= ((c >> 6) & 0x1F) | 0xC0; bits= 0; } 318 | else if (c < 0x10000) { *out++= ((c >> 12) & 0x0F) | 0xE0; bits= 6; } 319 | else { *out++= ((c >> 18) & 0x07) | 0xF0; bits= 12; } 320 | 321 | for ( ; bits >= 0; bits-= 6) { 322 | if (out >= outend) 323 | break; 324 | *out++= ((c >> bits) & 0x3F) | 0x80; 325 | } 326 | processed = (const unsigned char*) in; 327 | } 328 | *outlen = (int)(out - outstart); 329 | *inlenb = (int)(processed - inb); 330 | return(*outlen); 331 | } 332 | 333 | /** 334 | * UTF8ToUTF16BE: 335 | * @outb: a pointer to an array of bytes to store the result 336 | * @outlen: the length of @outb 337 | * @in: a pointer to an array of UTF-8 chars 338 | * @inlen: the length of @in 339 | * 340 | * Take a block of UTF-8 chars in and try to convert it to an UTF-16BE 341 | * block of chars out. 342 | * 343 | * Returns the number of byte written, or -1 by lack of space, or -2 344 | * if the transcoding failed. 345 | */ 346 | int UTF8ToUTF16BE(unsigned char* outb, int *outlen, 347 | const unsigned char* in, int *inlen) 348 | { 349 | unsigned short* out = (unsigned short*) outb; 350 | const unsigned char* processed = in; 351 | const unsigned char *const instart = in; 352 | unsigned short* outstart= out; 353 | unsigned short* outend; 354 | const unsigned char* inend; 355 | unsigned int c, d; 356 | int trailing; 357 | unsigned char *tmp; 358 | unsigned short tmp1, tmp2; 359 | 360 | /* UTF-16BE has no BOM */ 361 | if ((outb == NULL) || (outlen == NULL) || (inlen == NULL)) return(-1); 362 | if (in == NULL) { 363 | *outlen = 0; 364 | *inlen = 0; 365 | return(0); 366 | } 367 | inend= in + *inlen; 368 | outend = out + (*outlen / 2); 369 | while (in < inend) { 370 | d= *in++; 371 | if (d < 0x80) { c= d; trailing= 0; } 372 | else if (d < 0xC0) { 373 | /* trailing byte in leading position */ 374 | *outlen = (int)(out - outstart); 375 | *inlen = (int)(processed - instart); 376 | return(-2); 377 | } else if (d < 0xE0) { c= d & 0x1F; trailing= 1; } 378 | else if (d < 0xF0) { c= d & 0x0F; trailing= 2; } 379 | else if (d < 0xF8) { c= d & 0x07; trailing= 3; } 380 | else { 381 | /* no chance for this in UTF-16 */ 382 | *outlen = (int)(out - outstart); 383 | *inlen = (int)(processed - instart); 384 | return(-2); 385 | } 386 | 387 | if (inend - in < trailing) { 388 | break; 389 | } 390 | 391 | for ( ; trailing; trailing--) { 392 | if ((in >= inend) || (((d= *in++) & 0xC0) != 0x80)) break; 393 | c <<= 6; 394 | c |= d & 0x3F; 395 | } 396 | 397 | /* assertion: c is a single UTF-4 value */ 398 | if (c < 0x10000) { 399 | if (out >= outend) break; 400 | if (xmlLittleEndian) { 401 | tmp = (unsigned char *) out; 402 | *tmp = c >> 8; 403 | *(tmp + 1) = c; 404 | out++; 405 | } else { 406 | *out++ = c; 407 | } 408 | } 409 | else if (c < 0x110000) { 410 | if (out+1 >= outend) break; 411 | c -= 0x10000; 412 | if (xmlLittleEndian) { 413 | tmp1 = 0xD800 | (c >> 10); 414 | tmp = (unsigned char *) out; 415 | *tmp = tmp1 >> 8; 416 | *(tmp + 1) = (unsigned char) tmp1; 417 | out++; 418 | 419 | tmp2 = 0xDC00 | (c & 0x03FF); 420 | tmp = (unsigned char *) out; 421 | *tmp = tmp2 >> 8; 422 | *(tmp + 1) = (unsigned char) tmp2; 423 | out++; 424 | } else { 425 | *out++ = 0xD800 | (c >> 10); 426 | *out++ = 0xDC00 | (c & 0x03FF); 427 | } 428 | } 429 | else 430 | break; 431 | processed = in; 432 | } 433 | *outlen = (int)((out - outstart) * 2); 434 | *inlen = (int)(processed - instart); 435 | return(*outlen); 436 | } 437 | 438 | /* This file is licenced under the MIT licence as follows: 439 | 440 | Permission is hereby granted, free of charge, to any person obtaining a copy 441 | of this software and associated documentation files (the "Software"), to deal 442 | in the Software without restriction, including without limitation the rights 443 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 444 | copies of the Software, and to permit persons to whom the Software is fur- 445 | nished to do so, subject to the following conditions: 446 | 447 | The above copyright notice and this permission notice shall be included in 448 | all copies or substantial portions of the Software. 449 | 450 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 451 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FIT- 452 | NESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 453 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 454 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 455 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 456 | THE SOFTWARE. */ 457 | -------------------------------------------------------------------------------- /src/xml2_encoding.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MiniVHD Minimalist VHD implementation in C. 3 | * 4 | * This file is part of the MiniVHD Project. 5 | * 6 | * Version: @(#)xml2_encoding.h 1.0.1 2021/03/15 7 | * 8 | * Author: Sherman Perry, 9 | * 10 | * Copyright 2019-2021 Sherman Perry. 11 | * 12 | * MIT License 13 | * 14 | * Permission is hereby granted, free of charge, to any person 15 | * obtaining a copy of this software and associated documenta- 16 | * tion files (the "Software"), to deal in the Software without 17 | * restriction, including without limitation the rights to use, 18 | * copy, modify, merge, publish, distribute, sublicense, and/or 19 | * sell copies of the Software, and to permit persons to whom 20 | * the Software is furnished to do so, subject to the following 21 | * conditions: 22 | * 23 | * The above copyright notice and this permission notice shall 24 | * be included in all copies or substantial portions of the 25 | * Software. 26 | * 27 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 28 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 29 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 30 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 31 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 32 | * FROM, OUT OF O R IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 33 | * DEALINGS IN THE SOFTWARE. 34 | */ 35 | #ifndef XML2_ENCODING_H 36 | # define XML2_ENCODING_H 37 | 38 | 39 | typedef uint16_t mvhd_utf16; 40 | 41 | 42 | #ifdef __cplusplus 43 | extern "C" { 44 | #endif 45 | 46 | void xmlEncodingInit(void); 47 | 48 | int UTF16LEToUTF8(uint8_t *out, int *outlen, const uint8_t *inb, 49 | int *inlenb); 50 | int UTF8ToUTF16LE(uint8_t *outb, int *outlen, const uint8_t *in, 51 | int *inlen); 52 | int UTF16BEToUTF8(uint8_t *out, int *outlen, const uint8_t *inb, 53 | int *inlenb); 54 | int UTF8ToUTF16BE(uint8_t *outb, int *outlen, const uint8_t *in, 55 | int *inlen); 56 | 57 | #ifdef __cplusplus 58 | } 59 | #endif 60 | 61 | 62 | #endif /*XML2_ENCODING_H*/ 63 | --------------------------------------------------------------------------------