diff --git a/LICENSE b/LICENSE
index 7f46823..9aa5af4 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright 2019-2020 dd86k
+Copyright 2019-2021 dd86k
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
diff --git a/Tupfile b/Tupfile
index abeaed0..f9c8719 100644
--- a/Tupfile
+++ b/Tupfile
@@ -1,3 +1,5 @@
+FINPUT = src/*.c src/fs/*.c src/vdisk/*.c src/vdisk/fmt/*.c src/utils/*.c
+
ifeq (@(TUP_PLATFORM),win32)
CC = clang-cl
CF = /Zp -D_CRT_SECURE_NO_WARNINGS -Isrc -c
@@ -12,5 +14,5 @@
OBJEXT = obj
endif
-: foreach src/*.c src/fs/*.c src/vdisk/*.c |> $(CC) $(CF) "%f" -o "%o" |> bin/%B.$(OBJEXT)
+: foreach $(FINPUT) |> $(CC) $(CF) "%f" -o "%o" |> bin/%B.$(OBJEXT)
#: bin/*.o |> $(CC) %f -o $(OUTNAME) |> $(OUTNAME)
\ No newline at end of file
diff --git a/m b/m
index ce9ec47..a2ae923 100755
--- a/m
+++ b/m
@@ -37,7 +37,7 @@
m_build()
{
mkdir -p bin
- for file in src/*.c src/**/*.c; do
+ for file in src/*.c src/**/*.c src/**/**/*.c; do
base=${file##*/}
echo $CC: $base
$CC $CF $file $1 $2 $3 $4 -o bin/${base%%.*}.obj
diff --git a/src/fs/gpt.c b/src/fs/gpt.c
index 1b52296..9722796 100644
--- a/src/fs/gpt.c
+++ b/src/fs/gpt.c
@@ -1,7 +1,190 @@
-#include
#include
-#include // strcpy
-#include
-#include "gpt.h" // includes uid.h
-#include "utils.h"
-#include "vdisk.h"
+#include
+#include "fs/gpt.h" // includes uid.h
+#include "utils/hash.h"
+
+struct gpt_i_entry_t {
+ uint32_t hash; // See hash.c for current implementation
+ const char *value; // GUID string value
+ const char *name; // Partition type name
+};
+
+// https://en.wikipedia.org/wiki/GUID_Partition_Table#Partition_type_GUIDs
+const struct gpt_i_entry_t gpt_i_types[] = {
+ { 0x8E022B3A, "00000000-0000-0000-0000-000000000000", "Empty" },
+ { 0x0514B937, "024DEE41-33E7-11D3-9D69-0008C781F39F", "MBR Partition Scheme" },
+ { 0xACCE99DD, "C12A7328-F81F-11D2-BA4B-00A0C93EC93B", "EFI System Partition" },
+ { 0xC7E0162E, "21686148-6449-6E6F-744E-656564454649", "BIOS Boot Partition" },
+ { 0xC72C7A70, "D3BFE2DE-3DAF-11DF-BA40-E3A556D89593", "Intel Fast Flash" },
+ { 0x10AC1CDD, "F4019732-066E-4E12-8273-346C5641494F", "Sony Boot Partition" },
+ { 0xF752E0C7, "BFBFAFE7-A34F-448A-9A5B-6213EB736C22", "Lenovo Boot Partition" },
+ // Windows
+ { 0x269ADB66, "E3C9E316-0B5C-4DB8-817D-F92DF00215AE", "Microsoft Reserved Partition" },
+ { 0x5E16CC4F, "EBD0A0A2-B9E5-4433-87C0-68B6B72699C7", "Windows Basic data partition" },
+ { 0x41C253B7, "5808C8AA-7E8F-42E0-85D2-E1E90434CFB3", "Windows Logical Disk Manager metadata partition" },
+ { 0x0E6EE3DF, "AF9B60A0-1431-4F62-BC68-3311714A69AD", "Windows Logical Disk Manager data partition" },
+ { 0x067AF665, "DE94BBA4-06D1-4D40-A16A-BFD50179D6AC", "Windows Recovery Environment" },
+ { 0x43D5E6B4, "37AFFC90-EF7D-4E96-91C3-2D7AE055B174", "IBM General Parallel File System partition" },
+ { 0x14246E2B, "E75CAF8F-F680-4CEE-AFA3-B001E56EFC2D", "Windows Storage Spaces partition" },
+ { 0x78218E65, "558D43C5-A1AC-43C0-AAC8-D1472B2923D1", "Windows Storage Replica partition" },
+ // HP-UX
+ { 0xADF08B11, "75894C1E-3AEB-11D3-B7C1-7B03A0000000", "HP-UX Data partition" },
+ { 0x46C5DCAA, "E2A1E728-32E3-11D6-A682-7B03A0000000", "HP-UX Service partition" },
+ // Linux
+ { 0x484E9B70, "0FC63DAF-8483-4772-8E79-3D69D8477DE4", "Linux filesystem data" },
+ { 0x1C0E699B, "A19D880F-05FC-4D3B-A006-743F0F84911E", "Linux RAID partition" },
+ { 0xDABC39F1, "44479540-F297-41B2-9AF7-D131D5F0458A", "Linux Root partition (x86)" },
+ { 0xED1FD152, "4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709", "Linux Root partition (x86-64)" },
+ { 0x4CE50D2E, "69DAD710-2CE4-4E3C-B16C-21A1D49ABED3", "Linux Root partition (32-bit ARM)" },
+ { 0xB3C56AFD, "B921B045-1DF0-41C3-AF44-4C6F280D3FAE", "Linux Root partition (64-bit ARM/AArch64)" },
+ { 0xAA5B0BB4, "BC13C2FF-59E6-4262-A352-B275FD6F7172", "Linux Boot partition" },
+ { 0x1B3093AB, "0657FD6D-A4AB-43C4-84E5-0933C84B4F4F", "Linux Swap partition" },
+ { 0x4CCA9687, "E6D6D379-F507-44C2-A23C-238F2A3DF928", "Linux Logical Volume Manager partition" },
+ { 0xD1EA3E64, "933AC7E1-2EB4-4F13-B844-0E14E2AEF915", "Linux Home Partition" },
+ { 0x05FAA95A, "3B8F8425-20E0-4F3B-907F-1A25A76F98E8", "Linux Server Data (/srv) Partition" },
+ { 0x658D616D, "7FFEC5C9-2D00-49B7-8941-3EA10A5586B7", "Linux Plain dm-crypt partition" },
+ { 0x235DB26A, "CA7D7CCB-63ED-4C53-861C-1742536059CC", "Linux Unified Key Setup partition" },
+ { 0x81C15265, "8DA63339-0007-60C0-C436-083AC8230908", "Linux Reserved" },
+ // FreeBSD
+ { 0x4BB3F4C4, "83BD6B9D-7F41-11DC-BE0B-001560B84F0F", "FreeBSD Boot Partition" },
+ { 0x40D76637, "516E7CB4-6ECF-11D6-8FF8-00022D09712B", "FreeBSD Data Partition" },
+ { 0x601A83A7, "516E7CB5-6ECF-11D6-8FF8-00022D09712B", "FreebSD Swap Partition" },
+ { 0xA2AB033D, "516E7CB6-6ECF-11D6-8FF8-00022D09712B", "FreeBSD Unix File System Partition" },
+ { 0x56D5FEF5, "516E7CB8-6ECF-11D6-8FF8-00022D09712B", "FreeBSD Vinum Volume Manager Partition " },
+ { 0x907CCD98, "516E7CBA-6ECF-11D6-8FF8-00022D09712B", "FreeBSD ZFS Partition" },
+ // macOS
+ { 0x470B460A, "48465300-0000-11AA-AA11-00306543ECAC", "Hierarchical File System Plus (HFS+) partition" },
+ { 0x5FCA3FBC, "7C3457EF-0000-11AA-AA11-00306543ECAC", "Apple APFS or FileVault volume container" },
+ { 0x1429FF52, "55465300-0000-11AA-AA11-00306543ECAC", "Apple UFS container" },
+ { 0x0001C10D, "6A898CC3-1DD2-11B2-99A6-080020736631", "Apple ZFS" },
+ { 0xDC5F557C, "52414944-0000-11AA-AA11-00306543ECAC", "Apple RAID partition" },
+ { 0x962D949D, "52414944-5F4F-11AA-AA11-00306543ECAC", "Apple RAID partition, offline" },
+ { 0xD786CE98, "426F6F74-0000-11AA-AA11-00306543ECAC", "Apple Boot partition (Recovery HD)" },
+ { 0x95841C2E, "4C616265-6C00-11AA-AA11-00306543ECAC", "Apple Label" },
+ { 0xD9360BFE, "5265636F-7665-11AA-AA11-00306543ECAC", "Apple TV Recovery partition" },
+ { 0xE232C3B3, "53746F72-6167-11AA-AA11-00306543ECAC", "Apple HFS+ FileVault volume container" },
+ { 0x39238B68, "B6FA30DA-92D2-4A9A-96F1-871EC6486200", "Apple SoftRAID_Status partition" },
+ { 0xCF12298F, "2E313465-19B9-463F-8126-8A7993773801", "Apple SoftRAID_Scratch partition" },
+ { 0xF3228EEE, "FA709C7E-65B1-4593-BFD5-E71D61DE9B02", "Apple SoftRAID_Volume partition" },
+ { 0xA211B25C, "BBBA6DF5-F46F-4A89-8F59-8765B2727503", "Apple SoftRAID_Cache partition " },
+ // Solaris
+ { 0x58A8CC47, "6A82CB45-1DD2-11B2-99A6-080020736631", "Solaris Boot Partition" },
+ { 0x99F24576, "6A85CF4D-1DD2-11B2-99A6-080020736631", "Solaris Root Partition" },
+ { 0xC47C9EA5, "6A87C46F-1DD2-11B2-99A6-080020736631", "Solaris Swap Partition" },
+ { 0xB764C015, "6A8B642B-1DD2-11B2-99A6-080020736631", "Solaris Backup Partition" },
+ { 0x0001C10D, "6A898CC3-1DD2-11B2-99A6-080020736631", "Solaris User (/usr) Partition" },
+ { 0xEE129404, "6A8EF2E9-1DD2-11B2-99A6-080020736631", "Solaris /var Partition" },
+ { 0xBA86FAD9, "6A90BA39-1DD2-11B2-99A6-080020736631", "Solaris Home (/home) Partition" },
+ { 0xB1EFAAC0, "6A9283A5-1DD2-11B2-99A6-080020736631", "Solaris Alternate Sector" },
+ { 0x08647456, "6A945A3B-1DD2-11B2-99A6-080020736631", "Solaris Reserved" },
+ { 0x3CE40B9B, "6A9630D1-1DD2-11B2-99A6-080020736631", "Solaris Reserved" },
+ { 0x21103731, "6A980767-1DD2-11B2-99A6-080020736631", "Solaris Reserved" },
+ { 0xCB448C01, "6A96237F-1DD2-11B2-99A6-080020736631", "Solaris Reserved" },
+ { 0xF8F6FF5C, "6A8D2AC7-1DD2-11B2-99A6-080020736631", "Solaris Reserved" },
+ // NetBSD
+ { 0x606E6C4E, "49F48D32-B10E-11DC-B99B-0019D1879648", "NetBSD Swap Partition" },
+ { 0x3F0F4034, "49F48D5A-B10E-11DC-B99B-0019D1879648", "NetBSD FFS Partition" },
+ { 0x3486D322, "49F48D82-B10E-11DC-B99B-0019D1879648", "NetBSD LFS Partition" },
+ { 0x25718784, "49F48DAA-B10E-11DC-B99B-0019D1879648", "NetBSD RAID Partition" },
+ { 0xC53767E8, "2DB519C4-B10F-11DC-B99B-0019D1879648", "NetBSD Concatended Partition" },
+ { 0xC6539646, "2DB519EC-B10F-11DC-B99B-0019D1879648", "NetBSD Encrypted Partition" },
+ // ChromeOS
+ { 0xEE26115E, "FE3A2A5D-4F32-41A7-B725-ACCC3285A309", "ChromeOS kernel" },
+ { 0x50868D21, "3CB8E202-3B7E-47DD-8A3C-7FF2A13CFCEC", "ChromeOS rootfs" },
+ { 0xDA299AC0, "2E0A753D-9E48-43B0-8337-B15192CB1B5E", "ChromeOS reserved" },
+ // CoreOS
+ { 0xB791D051, "5DFBF5F4-2848-4BAC-AA5E-0D9A20B745A6", "CoreOS /usr Partition" },
+ { 0x13F8C760, "3884DD41-8582-4404-B9A8-E9B84F2DF50E", "CoreOS Resizable rooffs Partition" },
+ { 0xE94125B6, "C95DC21A-DF0E-4340-8D7B-26CBFA9A03E0", "CoreOS OEM Customized Partition" },
+ { 0xB7AC5EE7, "BE9067B9-EA49-4F15-B4F6-F36F8C9E1818", "CoreOS RAID rootfs Partition" },
+ // Haiku
+ { 0x66BC7C15, "42465331-3BA3-10F1-802A-4861696B7521", "Haiku BFS" },
+ // MidnightBSD
+ { 0x75A67933, "85D5E45E-237C-11E1-B4B3-E89A8F7FC3A7", "MidnightBSD Boot Partition" },
+ { 0xE80FF09F, "85D5E45A-237C-11E1-B4B3-E89A8F7FC3A7", "MidnightBSD Data Partition" },
+ { 0x4A731390, "85D5E45B-237C-11E1-B4B3-E89A8F7FC3A7", "MidnightBSD Swap Partition" },
+ { 0xEB0DD6B8, "0394EF8B-237E-11E1-B4B3-E89A8F7FC3A7", "MidnightBSD UFS Partition" },
+ { 0xF29693F7, "85D5E45C-237C-11E1-B4B3-E89A8F7FC3A7", "MidnightBSD Vinum Volume Manager Partition" },
+ { 0x139BE2B5, "85D5E45D-237C-11E1-B4B3-E89A8F7FC3A7", "MidnightBSD ZFS Partition" },
+ // Ceph
+ { 0xEDCE712A, "45B0969E-9B03-4F30-B4C6-B4B80CEFF106", "Ceph Journal" },
+ { 0x14A24331, "45B0969E-9B03-4F30-B4C6-5EC00CEFF106", "Ceph dm-crypt Journal" },
+ { 0x992ABA9B, "4FBD7E29-9D25-41B8-AFD0-062C0CEFF05D", "Ceph OSD" },
+ { 0x4D709BC7, "4FBD7E29-9D25-41B8-AFD0-5EC00CEFF05D", "Ceph dm-crypt OSD" },
+ { 0xE3E1A7CE, "89C57F98-2FE5-4DC0-89C1-F3AD0CEFF2BE", "Ceph Disk in creation" },
+ { 0xD1E11E1B, "89C57F98-2FE5-4DC0-89C1-5EC00CEFF2BE", "Ceph dm-crypt Disk in creation" },
+ { 0xEBF0EFE4, "CAFECAFE-9B03-4F30-B4C6-B4B80CEFF106", "Ceph Block" },
+ { 0xA8AC9BB2, "30CD0809-C2B2-499C-8879-2D6B78529876", "Ceph Block DB" },
+ { 0x105D4F20, "5CE17FCE-4087-4169-B7FF-056CC58473F9", "Ceph Block write-ahead log" },
+ { 0x019E802F, "FB3AABF9-D25F-47CC-BF5E-721D1816496B", "Ceph Lockbox for dm-crypt keys" },
+ { 0xBA15CFAE, "4FBD7E29-8AE0-4982-BF9D-5A8D867AF560", "Ceph Multipath OSD" },
+ { 0x0C84891F, "45B0969E-8AE0-4982-BF9D-5A8D867AF560", "Ceph Multipath Journal" },
+ { 0xA36A1973, "CAFECAFE-8AE0-4982-BF9D-5A8D867AF560", "Ceph Multipath Block" },
+ { 0x6FA76A64, "7F4A666A-16F3-47A2-8445-152EF4D03F6C", "Ceph Multipath Block" },
+ { 0x07949EE4, "EC6D6385-E346-45DC-BE91-DA2A7C8B3261", "Ceph Multipath Block DB" },
+ { 0xA2CF5925, "01B41E1B-002A-453C-9F17-88793989FF8F", "Ceph Multipath Block write-ahead log" },
+ { 0x74A943D6, "CAFECAFE-9B03-4F30-B4C6-5EC00CEFF106", "Ceph dm-crypt Block" },
+ { 0x5C6D0C5B, "93B0052D-02D9-4D8A-A43B-33A3EE4DFBC3", "Ceph dm-crypt Block DB" },
+ { 0x276146A6, "306E8683-4FE2-4330-B7C0-00A917C16966", "Ceph dm-crypt Block write-ahead log" },
+ { 0xA8D72DB9, "45B0969E-9B03-4F30-B4C6-35865CEFF106", "Ceph dm-crypt LUKS Journal" },
+ { 0x63F9352A, "CAFECAFE-9B03-4F30-B4C6-35865CEFF106", "Ceph dm-crypt LUKS Block" },
+ { 0xF8A5CD43, "166418DA-C469-4022-ADF4-B30AFD37F176", "Ceph dm-crypt LUKS Block DB" },
+ { 0x0F0F3645, "86A32090-3647-40B9-BBBD-38D8C573AA86", "Ceph dm-crypt LUKS Block write-ahead log" },
+ { 0xA5CED512, "4FBD7E29-9D25-41B8-AFD0-35865CEFF05D", "Ceph dm-crypt LUKS OSD" },
+ // OpenBSD
+ { 0xF6E74095, "824CC7A0-36A8-11E3-890A-952519AD3F61", "OpenBSD Data Partition" },
+ // QNX
+ { 0xDB34F656, "CEF5A9AD-73BC-4601-89F3-CDEEEEE321A1", "QNX6 Power-safe FS" },
+ // Plan9
+ { 0xB6B6A42F, "C91818F9-8025-47AF-89D2-F030D7000C2C", "Plan9 Partition" },
+ // VMware ESX
+ { 0x31E75E8A, "9D275380-40AD-11DB-BF97-000C2911D1B8", "VMware ESX vmkcore Partition" },
+ { 0x4186E797, "AA31E02A-400F-11DB-9590-000C2911D1B8", "VMware ESX VMFS Partition" },
+ { 0x34FB36D1, "9198EFFC-31C0-11DB-8F78-000C2911D1B8", "VMware ESX Reserved" },
+ // Android-x86
+ { 0x83A9422A, "2568845D-2332-4675-BC39-8FA5A4748D15", "Android-x86 Bootloader" },
+ { 0x72A01C37, "114EAFFE-1552-4022-B26E-9B053604CF84", "Android-x86 Bootloader2" },
+ { 0xB1905CF7, "49A4D17F-93A3-45C1-A0DE-F50B2EBE2599", "Android-x86 Boot" },
+ { 0x3B969C16, "4177C722-9E92-4AAB-8644-43502BFD5506", "Android-x86 Recovery" },
+ { 0xC2B69556, "EF32A33B-A409-486C-9141-9FFB711F6266", "Android-x86 Misc" },
+ { 0xD34FF551, "20AC26BE-20B7-11E3-84C5-6CFDB94711E9", "Android-x86 Metadata" },
+ { 0x7923A6EA, "38F428E6-D326-425D-9140-6E0EA133647C", "Android-x86 System" },
+ { 0x427737E7, "A893EF21-E428-470A-9E55-0668FD91A2D9", "Android-x86 Cache" },
+ { 0x37237ADB, "DC76DDA9-5AC1-491C-AF42-A82591580C0D", "Android-x86 Data" },
+ { 0x810B4A92, "EBC597D0-2053-4B15-8B64-E0AAC75F4DB1", "Android-x86 Persistent" },
+ { 0xF14F045A, "C5A0AEEC-13EA-11E5-A1B1-001E67CA0C3C", "Android-x86 Vendor" },
+ { 0x9D0020AD, "BD59408B-4514-490D-BF12-9878D963F378", "Android-x86 Config" },
+ { 0x57EC611F, "8F68CC74-C5E5-48DA-BE91-A0C8C15E9C80", "Android-x86 Factory" },
+ { 0x1B09DB30, "9FDAA6EF-4B3F-40D2-BA8D-BFF16BFB887B", "Android-x86 Factory (alt)" },
+ { 0x19B8B344, "767941D0-2085-11E3-AD3B-6CFDB94711E9", "Android-x86 Fastboot/Tertiary" },
+ { 0xCE907A9F, "AC6D7924-EB71-4DF8-B48D-E267B27148FF", "Android-x86 OEM" },
+ // Android
+ { 0x3066CD72, "19A710A2-B3CA-11E4-B026-10604B889DCF", "Android Meta" },
+ { 0x41AA0D1B, "193D1EA4-B3CA-11E4-B075-10604B889DCF", "Android EXT" },
+ // ONIE
+ { 0x85F1605D, "7412F7D5-A156-4B13-81DC-867174929325", "ONIE Boot" },
+ { 0xE1D5C3A2, "D4E6E2CD-4469-46F3-B5CB-1BFF57AFC149", "ONIE Config" },
+ // PowerPC
+ { 0x57D8C5E7, "9E1A2D38-C612-4316-AA26-8B49521E5A8B", "PowerPC PReP Boot" },
+ // freedesktop.org
+ { 0xAA5B0BB4, "BC13C2FF-59E6-4262-A352-B275FD6F7172", "freedesktop.org Shared Boot Loader Configuration" },
+ // Atari TOS
+ { 0x67AA0DD3, "734E5AFE-F61A-11E6-BC64-92361F002671", "Atari TOS Basic Data Partition" },
+ // VeraCrypt
+ { 0xAD1F50BC, "8C8F8EFF-AC95-4770-814A-21994F2DBC8F", "VeraCrypt Encrypted Data Partition" },
+ // Empty marker
+ { 0, NULL, NULL }
+};
+
+const char* gpt_part_type_str(UID *uid) {
+ uint32_t hash = hash_compute((char*)uid, 16);
+
+ const struct gpt_i_entry_t *entry = (struct gpt_i_entry_t*)gpt_i_types;
+ while (entry->hash) {
+ if (hash == entry->hash)
+ return entry->name;
+ ++entry;
+ }
+
+ return NULL;
+}
diff --git a/src/fs/gpt.h b/src/fs/gpt.h
index adaff66..5052af4 100644
--- a/src/fs/gpt.h
+++ b/src/fs/gpt.h
@@ -1,7 +1,7 @@
#pragma once
#include
-#include "uid.h"
+#include "utils/uid.h"
// GNU GRUB "Hah!IdontNeedEFI"
@@ -11,31 +11,127 @@
#define EFI_PART_NAME_LENGTH 36
//
-// EFI Parition Entry flags (GPT_ENTRY::flags)
+// GPT flags
+//
+// Sections
+// * EFI flags
+// * Google flags
+// * Microsoft flags
+//
+// Bits Description
+// 2:0 EFI/General flags
+// 47:3 Reserved
+// 63:48 OS specifics (Google, Microsoft)
//
-#define EFI_PE_PLATFORM_REQUIRED 1 // bit 0, preserve as-is
-#define EFI_PE_EFI_FIRMWARE_IGNORE 2 // bit 1, ignore content of partition
-#define EFI_PE_LEGACY_BIOS_BOOTABLE 4 // bit 2
+/**
+ * The computing platform requires this partition to function properly.
+ */
+static const uint64_t GPT_FLAG_PLATFORM_REQUIRED = 1;
+/**
+ * The EFI firmware should ignore this partition and its data, and avoid
+ * reading from it.
+ */
+static const uint64_t GPT_FLAG_EFI_FIRMWARE_IGNORE = 2;
+/**
+ * Indicates that a legacy BIOS may boot from this partition.
+ */
+static const uint64_t GPT_FLAG_LEGACY_BIOS_BOOTABLE = 4;
//
-// EFI Parition Entry flags (GPT_ENTRY::resflags)
+// Google flags
//
+/**
+ * Google flag for ChromeOS (bit 56)
+ *
+ * Likely set when the OS booted successfully from the partition.
+ */
+static const uint64_t GPT_FLAG_SUCCESSFUL_BOOT = 0x100000000000000;
+/**
+ * Google flag mask for ChromeOS (bits 55:52)
+ *
+ * Likely the number of tries remaining to boot from the partition.
+ */
+static const uint64_t GPT_FLAG_TRIES_REMAINING_MASK = 0xF0000000000000;
+static const uint64_t GPT_FLAG_TRIES_REMAINING_SHIFT = 52;
+/**
+ * Google flag mask for ChromeOS (bits 51:48)
+ *
+ * Likely denotes a priority, 15 being the high, 1 lowest, and 0 not bootable.
+ */
+static const uint64_t GPT_FLAG_PRIORITY_MASK = 0xF000000000000;
+static const uint64_t GPT_FLAG_PRIORITY_SHIFT = 48;
+
//
-// EFI Parition Entry flags (GPT_ENTRY::partflags)
+// Microsoft flags
+//
+// Source: https://docs.microsoft.com/en-us/windows/win32/api/winioctl/ns-winioctl-partition_information_gpt
//
-// Google Chrome OS
-// priority[3:0]
-// tries remiaining[7:4]
-#define EFI_PE_SUCCESSFUL_BOOT 0x10 // bit 8
+/**
+ * Microsoft flag: GPT_BASIC_DATA_ATTRIBUTE_READ_ONLY (bit 60)
+ *
+ * If this attribute is set, the partition is read-only.
+ *
+ * Writes to the partition will fail. IOCTL_DISK_IS_WRITABLE will fail with the
+ * ERROR_WRITE_PROTECT Win32 error code, which causes the file system to mount
+ * as read only, if a file system is present.
+ *
+ * VSS uses this attribute.
+ *
+ * Do not set this attribute for dynamic disks. Setting it can cause I/O errors
+ * and prevent the file system from mounting properly.
+ */
+static const uint64_t GPT_FLAG_READ_ONLY = 0x1000000000000000;
+/**
+ * Microsoft flag: GPT_BASIC_DATA_ATTRIBUTE_SHADOW_COPY (bit 61)
+ *
+ * If this attribute is set, the partition is a shadow copy of another partition.
+ *
+ * VSS uses this attribute. This attribute is an indication for file system
+ * filter driver-based software (such as antivirus programs) to avoid
+ * attaching to the volume.
+ *
+ * An application can use the attribute to differentiate a shadow copy volume
+ * from a production volume. An application that does a fast recovery, for
+ * example, will break a shadow copy LUN and clear the read-only and hidden
+ * attributes and this attribute. This attribute is set when the shadow copy is
+ * created and cleared when the shadow copy is broken.
+ *
+ * Despite its name, this attribute can be set for basic and dynamic disks.
+ *
+ * Windows Server 2003: This attribute is not supported before Windows Server
+ * 2003 with SP1.
+ */
+static const uint64_t GPT_FLAG_SHADOW_COPY = 0x2000000000000000;
+/**
+ * Microsoft flag: GPT_BASIC_DATA_ATTRIBUTE_HIDDEN (bit 62)
+ *
+ * If this attribute is set, the partition is not detected by the Mount Manager.
+ *
+ * As a result, the partition does not receive a drive letter, does not receive
+ * a volume GUID path, does not host mounted folders (also called volume mount
+ * points), and is not enumerated by calls to FindFirstVolume and
+ * FindNextVolume. This ensures that applications such as Disk Defragmenter do
+ * not access the partition. The Volume Shadow Copy Service (VSS) uses this
+ * attribute.
+ *
+ * Despite its name, this attribute can be set for basic and dynamic disks.
+ */
+static const uint64_t GPT_FLAG_HIDDEN = 0x4000000000000000;
+/**
+ * Microsoft flag: GPT_BASIC_DATA_ATTRIBUTE_NO_DRIVE_LETTER (bit 63)
+ *
+ * If this attribute is set, the partition does not receive a drive letter by
+ * default when the disk is moved to another computer or when the disk is seen
+ * for the first time by a computer.
+ */
+static const uint64_t GPT_FLAG_NO_DRIVE_LETTER = 0x8000000000000000;
-// Microsoft
-#define EFI_PE_READ_ONLY 0x1000 // bit 12
-#define EFI_PE_SHADOW_COPY 0x2000 // bit 13
-#define EFI_PE_HIDDEN 0x4000 // bit 14
-#define EFI_PE_NO_DRIVE_LETTER 0x8000 // bit 15
+//
+// Structures
+//
typedef struct LBA64 {
union {
@@ -73,45 +169,13 @@
// GPT entry structure
typedef struct {
- // Unused entry : 00000000-0000-0000-0000-000000000000
- // EFI System Partition: C12A7328-F81F-11D2-BA4B-00A0C93EC93B
- // Contains legacy MBR : 024DEE41-33E7-11D3-9D69-0008C781F39F
UID type; // Partition type GUID
UID part; // Unique partition GUID
LBA64 first;
LBA64 last;
- union {
- uint64_t flagsraw;
- struct {
- // Bit 0 - Required for platform
- // Bit 1 - If set, do not produce EFI_BLOCK_IO_PROTOCOL
- // Bit 2 - Legacy PC-AT BIOS bootable
- uint32_t flags; // GPT entry flags
- uint16_t resflags; // Reserved
- // (Chrome OS) Bit 0:3 - Priority (0: non-boot, 1:lowest, 15:highest)
- // (Chrome OS) Bit 4:7 - Tries remaining
- // (Chrome OS) Bit 8 - Successful boot
- // (Windows) Bit 12 - Read-only
- // (Windows) Bit 13 - Shadow partition copy
- // (Windows) Bit 14 - Hidden
- // (Windows) Bit 15 - No drive letter (no automount)
- uint16_t partflags; // Partition-defined flags
- };
- };
- uint16_t partname[36]; // 72 bytes, 32 UTF-16LE characters
+ uint64_t flags;
+ uint16_t partname[36]; // 72 bytes, 36 UTF-16LE characters
uint8_t pad[384];
} GPT_ENTRY;
-/*#ifndef _GPT_ENTRIES
-#define _GPT_ENTRIES
-//TODO: Array instead?
-const UID GPT_ENTRY_EMPTY = {
- .time_low = 0,
- .time_mid = 0,
- .time_ver = 0,
- .clock = 0,
- .node = { 0, 0, 0, 0, 0, 0 }
-};
-#endif // _GPT_ENTRIES*/
-
-struct VDISK;
+const char* gpt_part_type_str(UID *uid);
diff --git a/src/fs/mbr.c b/src/fs/mbr.c
index 86e2251..864ff9d 100644
--- a/src/fs/mbr.c
+++ b/src/fs/mbr.c
@@ -1,7 +1,7 @@
#include
#include
-#include "mbr.h"
#include
+#include "fs/mbr.h"
//
// mbr_lba
@@ -10,7 +10,7 @@
uint32_t mbr_lba(CHS *chs) {
// LBA = (C * HPC + H) * SPT + (S − 1)
// HPC Max heads per cylinders, typically 16 (28-bit LBA)
- // SPT Max sectors per strack, typically 63 (28-bit LBA)
+ // SPT Max sectors per track, typically 63 (28-bit LBA)
uint8_t sector = chs->sector & 0x3F;
uint16_t cylinder = chs->cylinder | ((chs->sector & 0xC0) << 2);
return (cylinder * 16 * chs->head) * 63 + (sector - 1);
@@ -50,6 +50,7 @@
// - https://www.win.tue.nl/~aeb/partitions/partition_types-1.html
// - https://en.wikipedia.org/wiki/Partition_type
// - fdisk(1) (util-linux)
+ // NOTE: There are only 100 of these, a table isn't worth it yet
switch (type) {
case 0x00: return "Empty";
case 0x01: return "FAT12";
diff --git a/src/fs/mbr.h b/src/fs/mbr.h
index fab13b0..65c33e5 100644
--- a/src/fs/mbr.h
+++ b/src/fs/mbr.h
@@ -2,8 +2,8 @@
#include
#include
-#include "vdisk.h"
-#include "platform.h"
+#include "vdisk/vdisk.h"
+#include "utils/platform.h"
#ifdef ENDIAN_LITTLE
enum {
diff --git a/src/main.c b/src/main.c
index 580ec5e..8f7193e 100644
--- a/src/main.c
+++ b/src/main.c
@@ -9,15 +9,19 @@
#define __DATETIME__ __DATE__ " " __TIME__
#define PROJECT_VERSION "0.0.0"
+#define COPYRIGHT "Copyright (c) 2019-2021 dd86k "
#include
#include
#include
#include
-#include "utils.h"
+#include "utils/bin.h"
+#include "utils/platform.h"
+#include "utils/hash.h"
#include "vvd.h"
-#include "platform.h"
+//TODO: Move tests into its own compilation unit
+// e.g. tests/structs.c, tests/qed.c, etc.
#ifdef DEBUG
#include
#include "fs/gpt.h"
@@ -28,8 +32,6 @@
//
void test() {
- //TODO: Move this to its own (tests/init.c)
- // With others like tests/vdisk_qed.c
fputs(
"* Defines\n"
#ifdef ENDIAN_LITTLE
@@ -115,6 +117,7 @@
//
static void help() {
+ //TODO: help system
puts(
"Manage virtual disks\n"
" Usage: vvd OPERATION [FILE] [OPTIONS]\n"
@@ -148,7 +151,7 @@
#ifdef __VERSION__
"Compiler: " __VERSION__ "\n"
#endif
- "MIT License: Copyright (c) 2019-2020 dd86k \n"
+ "MIT License: " COPYRIGHT "\n"
"Project page: \n"
"Defines: "
#ifdef DEBUG
@@ -159,7 +162,7 @@
#endif
"\n\n"
"FORMAT OPERATIONS\n"
- "VDI info, map, new, compact\n"
+ "VDI info, map, new\n"
"VMDK info\n"
"VHD info, map\n"
"VHDX \n"
@@ -173,7 +176,7 @@
static void license() {
puts(
- "Copyright 2019-2020 dd86k \n"
+ COPYRIGHT "\n"
"\n"
"Permission to use, copy, modify, and/or distribute this software for any\n"
"purpose with or without fee is hereby granted, provided that the above\n"
@@ -191,102 +194,114 @@
}
#ifdef _WIN32
-#define MAIN int wmain(int argc, wchar_t **argv)
+//TODO: Check if UNICODE is defined
+#define MAIN wmain(int argc, wchar_t **argv)
#else
-#define MAIN int main(int argc, char **argv)
+#define MAIN main(int argc, char **argv)
#endif
-//TODO: Consider hashing strings for faster lookups when parsing CLI
-// Either SuperFastHash [1] or xxHash [2]
-// [1] http://www.azillionmonkeys.com/qed/hash.html
-// [2] https://github.com/Cyan4973/xxHash
+// NOTE: Hashes may be cute, but for the command-line interface (CLI), that
+// would require both UTF-8 and UTF-16LE versions for all hashes (and
+// possibly more encodings), so no thanks. Besides, I'm lazy, and this
+// is obviously not critical code.
/**
* Match a patch to an exception with the VDISK_FORMAT_* enum.
*
- * \returns VDISK_FORMAT enum
+ * \returns VDISK_FORMAT_* enumeration value
*/
-static int vdextauto(const oschar *path) {
- if (extcmp(path, osstr("vdi"))) return VDISK_FORMAT_VDI;
- if (extcmp(path, osstr("vmdk"))) return VDISK_FORMAT_VMDK;
- if (extcmp(path, osstr("vhd"))) return VDISK_FORMAT_VHD;
- if (extcmp(path, osstr("vhdx"))) return VDISK_FORMAT_VHDX;
- if (extcmp(path, osstr("qed"))) return VDISK_FORMAT_QED;
+static int vdext(const oschar *path) {
+ if (extcmp(path, osstr("vdi"))) return VDISK_FORMAT_VDI;
+ if (extcmp(path, osstr("vmdk"))) return VDISK_FORMAT_VMDK;
+ if (extcmp(path, osstr("vhd"))) return VDISK_FORMAT_VHD;
+ if (extcmp(path, osstr("vhdx"))) return VDISK_FORMAT_VHDX;
+ if (extcmp(path, osstr("qed"))) return VDISK_FORMAT_QED;
if (extcmp(path, osstr("qcow")) || extcmp(path, osstr("qcow2")))
return VDISK_FORMAT_QCOW;
- if (extcmp(path, osstr("hdd"))) return VDISK_FORMAT_PHDD;
+ if (extcmp(path, osstr("hdd"))) return VDISK_FORMAT_PHDD;
return VDISK_FORMAT_NONE;
}
// Main entry point. This only performs intepreting the command-line options
-// for the core functions.
-MAIN {
+// for the core functions in vvd.c.
+int MAIN {
if (argc <= 1)
help();
- uint32_t mflags = 0; // main: Command-line flags
- uint32_t oflags = 0; // vdisk_open: file flags
- uint32_t cflags = 0; // vdisk_create: file flags
+ struct settings_t settings = {};
VDISK vdin; // vdisk IN
VDISK vdout; // vdisk OUT
- uint64_t vsize = 0; // virtual disk size, used in 'new' and 'resize'
const oschar *defopt = NULL; // Default option for input file
// Additional arguments are processed first, since they're simpler
- //TODO: --verbose: prints those extra lines (>v0.10.0)
- //TODO: --verify-repair
+ //TODO: --verbose
+ //TODO: --verify-repair (or verify/check operation?)
for (size_t argi = 2; argi < argc; ++argi) {
const oschar *arg = argv[argi];
- //
- // Generic
- //
- if (oscmp(arg, osstr("--size")) == 0) {
- if (argi + 1 >= argc) {
- fputs("main: missing argument for --size\n", stderr);
- return EXIT_FAILURE;
+
+ // Option
+ if (arg[0] == '-') {
+ // Long option
+ if (arg[1] == '-') {
+ const oschar *larg = arg + 2;
+
+ // Generic
+ if (osstrcmp(larg, osstr("size")) == 0) {
+ if (argi + 1 >= argc) {
+ fputs("main: missing argument for --size\n", stderr);
+ return EXIT_FAILURE;
+ }
+ if (strtobin(&settings.vsize, argv[++argi])) {
+ fputs("main: failed to convert binary number\n", stderr);
+ return EXIT_FAILURE;
+ }
+ continue;
+ }
+ if (osstrcmp(larg, osstr("progress")) == 0) {
+ settings.progressbar = 1;
+ continue;
+ }
+ if (osstrcmp(larg, osstr("verbose")) == 0) {
+ settings.verbose = 1;
+ continue;
+ }
+ if (osstrcmp(larg, osstr("vverbose")) == 0) {
+ settings.verbose = 2;
+ continue;
+ }
+
+ // vdisk_open flags
+ if (osstrcmp(larg, osstr("raw")) == 0) {
+ settings.vdisk.flags |= VDISK_RAW;
+ continue;
+ }
+
+ // vdisk_create flags
+ if (osstrcmp(larg, osstr("create-raw")) == 0) {
+ settings.vdisk.flags |= VDISK_RAW;
+ continue;
+ }
+ if (osstrcmp(larg, osstr("create-dynamic")) == 0) {
+ settings.vdisk.flags |= VDISK_CREATE_TYPE_DYNAMIC;
+ continue;
+ }
+ if (osstrcmp(larg, osstr("create-fixed")) == 0) {
+ settings.vdisk.flags |= VDISK_CREATE_TYPE_FIXED;
+ continue;
+ }
+
+ // vvd_info flags
+ if (osstrcmp(larg, osstr("info-full")) == 0) {
+ settings.info.full = 1;
+ continue;
+ }
}
- if (strtobin(&vsize, argv[++argi])) {
- fputs("main: failed to convert binary number\n", stderr);
- return EXIT_FAILURE;
- }
- continue;
+
+ // Short option
+ // None at the moment
}
- if (oscmp(arg, osstr("--size")) == 0) {
- mflags |= VVD_PROGRESS;
- continue;
- }
- //
- // vdisk_open flags
- //
- if (oscmp(arg, osstr("--raw")) == 0) {
- oflags |= VDISK_RAW;
- continue;
- }
- //
- // vdisk_create flags
- //
- if (oscmp(arg, osstr("--create-raw")) == 0) {
- cflags |= VDISK_RAW;
- continue;
- }
- if (oscmp(arg, osstr("--create-dynamic")) == 0) {
- cflags |= VDISK_CREATE_TYPE_DYNAMIC;
- continue;
- }
- if (oscmp(arg, osstr("--create-fixed")) == 0) {
- cflags |= VDISK_CREATE_TYPE_FIXED;
- continue;
- }
- //
- // vvd_info flags
- //
- if (oscmp(arg, osstr("--info-raw")) == 0) {
- mflags |= VVD_INFO_RAW;
- continue;
- }
- //
+
// Default argument
- //
if (defopt == NULL) {
defopt = arg;
continue;
@@ -295,122 +310,169 @@
fprintf(stderr, "main: '" OSCHARFMT "' unknown option\n", arg);
return EXIT_FAILURE;
}
-
+
const oschar *action = argv[1];
-
+
//
- // Operations
+ // Main operation actions
//
-
- if (oscmp(action, osstr("info")) == 0) {
+
+ if (osstrcmp(action, osstr("info")) == 0) {
if (defopt == NULL) {
fputs("main: missing vdisk\n", stderr);
return EXIT_FAILURE;
}
- if (vdisk_open(&vdin, defopt, oflags)) {
- vdisk_perror(&vdin);
+ if (vdisk_open(&vdin, defopt, settings.vdisk.flags)) {
+ vvd_perror(&vdin);
return vdin.err.num;
}
- return vvd_info(&vdin, mflags);
+ return vvd_info(&vdin, &settings);
}
-
- if (oscmp(action, osstr("map")) == 0) {
+
+ if (osstrcmp(action, osstr("map")) == 0) {
if (defopt == NULL) {
fputs("main: missing vdisk\n", stderr);
return EXIT_FAILURE;
}
- if (vdisk_open(&vdin, defopt, oflags)) {
- vdisk_perror(&vdin);
+ if (vdisk_open(&vdin, defopt, settings.vdisk.flags)) {
+ vvd_perror(&vdin);
return vdin.err.num;
}
return vvd_map(&vdin, 0);
}
-
- if (oscmp(action, osstr("compact")) == 0) {
+
+ if (osstrcmp(action, osstr("compact")) == 0) {
if (defopt == NULL) {
fputs("main: missing vdisk\n", stderr);
return EXIT_FAILURE;
}
if (vdisk_open(&vdin, defopt, 0)) {
- vdisk_perror(&vdin);
+ vvd_perror(&vdin);
return vdin.err.num;
}
if (vvd_compact(&vdin, 0)) {
- vdisk_perror(&vdin);
+ vvd_perror(&vdin);
return vdin.err.num;
}
return EXIT_FAILURE;
}
-
- if (oscmp(action, osstr("defrag")) == 0) {
+
+ if (osstrcmp(action, osstr("defrag")) == 0) {
fputs("main: not implemented\n", stderr);
return EXIT_FAILURE;
}
-
- if (oscmp(action, osstr("new")) == 0) {
+
+ if (osstrcmp(action, osstr("new")) == 0) {
if (defopt == NULL) {
fputs("main: missing path specifier\n", stderr);
return EXIT_FAILURE;
}
- if (vsize == 0) {
+ if (settings.vsize == 0) {
fputs("main: capacity cannot be zero\n", stderr);
return EXIT_FAILURE;
}
// Get vdisk type out of extension name
- int format = vdextauto(defopt);
+ int format = vdext(defopt);
if (format == VDISK_FORMAT_NONE) {
fputs("main: unknown extension\n", stderr);
return EXIT_FAILURE;
}
- return vvd_new(defopt, format, vsize, cflags);
+ return vvd_new(defopt, format, settings.vsize, &settings);
}
-
- if (oscmp(action, osstr("resize")) == 0) {
+
+ //TODO: Renew command
+ // e.g.,
+ // - VDI: Set new GUIDs
+
+ if (osstrcmp(action, osstr("resize")) == 0) {
fputs("main: not implemented\n", stderr);
return EXIT_FAILURE;
}
-
- if (oscmp(action, osstr("verify")) == 0) {
+
+ if (osstrcmp(action, osstr("verify")) == 0) {
fputs("main: not implemented\n", stderr);
return EXIT_FAILURE;
}
-
- if (oscmp(action, osstr("convert")) == 0) {
+
+ if (osstrcmp(action, osstr("convert")) == 0) {
fputs("main: not implemented\n", stderr);
return EXIT_FAILURE;
}
-
- if (oscmp(action, osstr("upgrade")) == 0) {
+
+ if (osstrcmp(action, osstr("upgrade")) == 0) {
fputs("main: not implemented\n", stderr);
return EXIT_FAILURE;
}
-
- if (oscmp(action, osstr("cleanfs")) == 0) {
- fputs("main: not implemented\n", stderr);
- return EXIT_FAILURE;
+
+ //
+ // Internal things
+ //
+
+ if (osstrcmp(action, osstr("internalhash")) == 0) {
+ if (defopt == NULL) {
+ fputs("main: missing argument\n", stderr);
+ return EXIT_FAILURE;
+ }
+#ifdef _WIN32
+ char buffer[200];
+ wcstombs(buffer, defopt, 200);
+ printf("%08X\n", hash_string((char*)buffer));
+#else
+ printf("%08X\n", hash_string((char*)defopt));
+#endif
+ return EXIT_SUCCESS;
}
-
+
+ if (osstrcmp(action, osstr("internalguidhash")) == 0) {
+ if (defopt == NULL) {
+ fputs("main: missing argument\n", stderr);
+ return EXIT_FAILURE;
+ }
+ UID uid;
+#ifdef _WIN32
+ char buffer[200];
+ wcstombs(buffer, defopt, 200);
+ int r = uid_parse(&uid, buffer, UID_GUID);
+#else
+ int r = uid_parse(&uid, defopt, UID_GUID);
+#endif
+ if (r) {
+ if (r < 0)
+ perror("main");
+ else
+ printf("main: failed to parse UID, got %d items\n", r);
+ return EXIT_FAILURE;
+ }
+ printf("%08X\n", hash_compute((const char*)&uid, 16));
+ return EXIT_SUCCESS;
+ }
+
+ //TODO: internalmbrtype
+ //TODO: internalgpttype
+
//
// Pages
//
-
- if (oscmp(action, osstr("ver")) == 0) {
+
+ if (osstrcmp(action, osstr("ver")) == 0) {
puts(PROJECT_VERSION);
return EXIT_SUCCESS;
}
- if (oscmp(action, osstr("version")) == 0 || oscmp(action, osstr("--version")) == 0)
+ if (osstrcmp(action, osstr("version")) == 0 || osstrcmp(action, osstr("--version")) == 0)
version();
- if (oscmp(action, osstr("help")) == 0 || oscmp(action, osstr("--help")) == 0)
+ //TODO: Help system in its own module
+ if (osstrcmp(action, osstr("help")) == 0 || osstrcmp(action, osstr("--help")) == 0)
help();
- if (oscmp(action, osstr("license")) == 0 || oscmp(action, osstr("--license")) == 0)
+ if (osstrcmp(action, osstr("license")) == 0 || osstrcmp(action, osstr("--license")) == 0)
license();
+
#ifdef DEBUG
- if (oscmp(action, osstr("--test")) == 0)
+ if (osstrcmp(action, osstr("--test")) == 0)
test();
#endif
-
+
fprintf(stderr, "main: '" OSCHARFMT "' unknown operation, see 'vvd help'\n", action);
return EXIT_FAILURE;
}
diff --git a/src/os.c b/src/os.c
deleted file mode 100644
index 8c9d8eb..0000000
--- a/src/os.c
+++ /dev/null
@@ -1,279 +0,0 @@
-#include
-#include "os.h"
-#ifndef _WIN32
-#include // memset
-#include
-#include
-#include
-#endif
-
-//
-// os_fopen
-//
-
-__OSFILE os_fopen(const oschar *path) {
-#ifdef _WIN32
- __OSFILE fd = CreateFileW(
- path, // lpFileName
- GENERIC_READ | GENERIC_WRITE, // dwDesiredAccess
- 0, // dwShareMode
- NULL, // lpSecurityAttributes
- OPEN_EXISTING, // dwCreationDisposition
- 0, // dwFlagsAndAttributes
- NULL // hTemplateFile
- );
- if (fd == INVALID_HANDLE_VALUE)
- return 0;
-#else
- __OSFILE fd = open(path, O_RDWR);
- if (fd == -1)
- return 0;
-#endif
- return fd;
-}
-
-//
-// os_fcreate
-//
-
-__OSFILE os_fcreate(const oschar *path) {
-#ifdef _WIN32
- __OSFILE fd = CreateFileW(
- path, // lpFileName
- GENERIC_READ | GENERIC_WRITE, // dwDesiredAccess
- 0, // dwShareMode
- NULL, // lpSecurityAttributes
- CREATE_ALWAYS, // dwCreationDisposition
- 0, // dwFlagsAndAttributes
- NULL // hTemplateFile
- );
- if (fd == INVALID_HANDLE_VALUE)
- return 0;
-#else
- __OSFILE fd = open(path, O_RDWR | O_CREAT | O_TRUNC);
- if (fd == -1)
- return 0;
-#endif
- return fd;
-}
-
-//
-// os_fseek
-//
-
-int os_fseek(__OSFILE fd, int64_t pos, int flags) {
-#ifdef _WIN32
- LARGE_INTEGER a;
- a.QuadPart = pos;
- if (SetFilePointerEx(fd, a, NULL, flags) == 0)
- return -1;
-#else
- if (lseek(fd, (off_t)pos, flags) == -1)
- return -1;
-#endif
- return 0;
-}
-
-//
-// os_fread
-//
-
-int os_fread(__OSFILE fd, void *buffer, size_t size) {
-#ifdef _WIN32
- DWORD r;
- if (ReadFile(fd, buffer, size, &r, NULL) == 0)
- return -1;
- /*if (r != size) {
- fprintf(stderr, "os_fread: Failed to read %u/%u bytes",
- (uint32_t)r, (uint32_t)size);
- return -2;
- }*/
-#else
- ssize_t r;
- if ((r = read(fd, buffer, size)) == -1)
- return -1;
- /*if (r != size) {
- fprintf(stderr, "os_fread: Failed to read %d/%u bytes",
- (int32_t)r, (uint32_t)size);
- return -2;
- }*/
-#endif
- return 0;
-}
-
-//
-// os_fwrite
-//
-
-int os_fwrite(__OSFILE fd, void *buffer, size_t size) {
-#ifdef _WIN32
- DWORD r;
- if (WriteFile(fd, buffer, size, &r, NULL) == 0)
- return -1;
- /*if (r != size)
- return -2;*/
-#else
- ssize_t r;
- if ((r = write(fd, buffer, size)) == -1)
- return -1;
- /*if (r != size)
- return -2;*/
-#endif
- return 0;
-}
-
-//
-// os_fsize
-//
-
-int os_fsize(__OSFILE fd, uint64_t *size) {
-#if _WIN32
- LARGE_INTEGER li;
- // NOTE: Doc says 0 (FALSE) on failure which apparently doesn't.
- // NOTE: Not supported in Windows Store Apps (use GetFileInformationByHandleEx)
- if (GetFileSizeEx(fd, &li))
- return -1;
- *size = li.QuadPart;
- return 0;
-#else
- // fstat(2) sets st_size to 0 on block devices
- struct stat s;
- if (fstat(fd, &s) == -1)
- return 1;
- switch (s.st_mode & __S_IFMT) {
- case __S_IFREG:
- case __S_IFLNK:
- *size = s.st_size;
- return 0;
- case __S_IFBLK:
- //TODO: Non-linux variant
- ioctl(fd, BLKGETSIZE64, size);
- return 0;
- default: return -1;
- }
-#endif
-}
-
-//
-// os_falloc
-//
-
-int os_falloc(__OSFILE fd, uint64_t fsize) {
- const int bsize = 1024 * 1024; // 1 MiB
- uint8_t *buf = calloc(1, bsize); // zeroed
- if (fsize >= bsize) {
- if (buf == NULL)
- return 1;
- while (fsize > bsize) {
- if (os_fwrite(fd, buf, bsize))
- return -1;
- fsize -= bsize;
- }
- }
- if (fsize > 0) {
- if (os_fwrite(fd, buf, bsize - fsize))
- return -1;
- }
- free(buf);
- return 0;
-}
-
-//
-// os_pinit
-//
-
-int os_pinit(struct progress_t *p, uint32_t flags, uint32_t max) {
-#if _WIN32
- p->fd = GetStdHandle(STD_OUTPUT_HANDLE);
-
- CONSOLE_SCREEN_BUFFER_INFO csbi;
- GetConsoleScreenBufferInfo(p->fd, &csbi);
- p->leny = (csbi.srWindow.Bottom - csbi.srWindow.Top + 1);
- p->lenx = (csbi.srWindow.Right - csbi.srWindow.Left + 1);
- p->inity = csbi.dwCursorPosition.Y;
- p->initx = csbi.dwCursorPosition.X;
-#else
- struct winsize ws;
- ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws);
- p->leny = ws.ws_row;
- p->lenx = ws.ws_col;
-#endif
- p->bfill = malloc(1024);
- p->bspace = malloc(1024);
- memset(p->bfill, '=', 1024);
- memset(p->bspace, ' ', 1024);
- p->maximum = max;
- p->flags = flags;
-
- return 0;
-}
-
-//
-// os_pupdate
-//
-
-int os_pupdate(struct progress_t *p, uint32_t val) {
-#if _WIN32
- COORD c;
- c.X = 0;
- c.Y = p->inity;
- SetConsoleCursorPosition(p->fd, c);
-#else
- //printf("\033[%d;%dH", p->inity, 0);
- fputs("\r", stdout); // "CSI2n" clears line
-#endif
- float cc = (float)val / p->maximum; // current
- int pc; // characters printed
- uint32_t ph; // placeholder
- switch (p->flags & 0xF) {
- case PROG_MODE_CUR_MAX:
- pc = printf("%u/%u [", val, p->maximum);
- break;
- case PROG_MODE_CUR_ONLY:
- pc = printf("%u [", val);
- break;
- case PROG_MODE_POURCENT:
- ph = (uint32_t)(cc * 100.0f);
- /*if (ph == ((float)p->current / p->maximum) * 100.0f) { // same %
- p->current = val;
- return 0; // NOP
- }*/
- pc = printf("%u%% [", ph);
- break;
- default:
- pc = printf("[");
- break;
- }
- if (pc < 0)
- return -11;
-
- uint32_t total = p->lenx - pc - 2;
- uint32_t occup = cc * total; // filled
- uint32_t space = total - occup; // what's left
- printf("%.*s%.*s]", occup, p->bfill, space, p->bspace);
-
- p->current = val;
- return 0;
-}
-
-//
-// os_pfinish
-//
-
-int os_pfinish(struct progress_t *p) {
- if (p->flags & PROG_FLAG_REMOVE) {
-#if _WIN32
- COORD c;
- c.X = 0;
- c.Y = p->inity;
- SetConsoleCursorPosition(p->fd, c);
-#else
- fputs("\r", stdout);
-#endif
- printf("%.*s", p->lenx, p->bspace);
- }
- free(p->bfill);
- free(p->bspace);
- putchar('\n');
- return 0;
-}
diff --git a/src/os.h b/src/os.h
deleted file mode 100644
index a50d736..0000000
--- a/src/os.h
+++ /dev/null
@@ -1,122 +0,0 @@
-#include "utils.h"
-#include "vdisk.h"
-
-#ifdef _WIN32
-#include
-typedef HANDLE __OSFILE;
-#else // Posix
-#include
-#include
-#include
-#include
-#include
-
-#ifndef __OSFILE_H
-#define __OSFILE_H
-typedef int __OSFILE;
-#endif // __OSFILE_H
-#endif
-
-//
-// File functions
-//
-
-/**
- * Open a file stream. File or device must exist. Consult documentation on
- * which devices are supported.
- */
-__OSFILE os_fopen(const oschar *path);
-
-/**
- * Always create and overwrite a file path. This cannot create devices.
- */
-__OSFILE os_fcreate(const oschar *path);
-
-/**
- * Seek into a position within the stream.
- */
-int os_fseek(__OSFILE fd, int64_t position, int flags);
-
-/**
- * Read data from stream from current position.
- */
-int os_fread(__OSFILE fd, void *buffer, size_t size);
-
-/**
- * Write data to stream at current position, overwrites.
- */
-int os_fwrite(__OSFILE fd, void *buffer, size_t size);
-
-/**
- * Get the file size, or the disk size, in bytes. If the handle is a file,
- * the file size is set, otherwise if the handle is a block device, the
- * disk size is set. Uses GetFileSizeEx (Windows) or stat/ioctl (Linux).
- */
-int os_fsize(__OSFILE fd, uint64_t *size);
-
-/**
- * Zero write to file.
- */
-int os_falloc(__OSFILE fd, uint64_t fsize);
-
-//
-// Progress functions
-//
-
-#ifndef DEFINITION_OS_PROGRESS
-#define DEFINITION_OS_PROGRESS
-enum {
- PROG_MODE_NONE = 0,
- PROG_MODE_CUR_MAX = 1,
- PROG_MODE_CUR_ONLY = 2,
- PROG_MODE_POURCENT = 3,
- //TODO: PROG_MODE_INDETERMINATE
-
- PROG_FLAG_REMOVE = 0x100, // Remove progress bar when done
-};
-
-struct progress_t {
- uint32_t current, maximum;
- uint16_t initx, inity, lenx, leny;
- char *bfill; // fill char buffer
- char *bspace; // empty char buffer
- uint32_t flags;
-#if _WIN32
- HANDLE fd;
-#else
- size_t res1;
-#endif
-};
-#endif // DEFINITION_OS_PROGRESS
-
-/**
- * Initiate a new progress bar. This function allocates, but manages its own
- * memory.
- *
- * \param p struct progress_t pointer
- * \param flags See PROG enumerations
- * \param max Maximum value
- *
- * \returns Status code
- */
-int os_pinit(struct progress_t *p, uint32_t flags, uint32_t max);
-
-/**
- * Update the progress bar with a new current value.
- *
- * \param p struct progress_t pointer
- * \param val New current value
- *
- * \returns Status code
- */
-int os_pupdate(struct progress_t *p, uint32_t val);
-
-/**
- * Indiate that the progress bar is finished and will no longer be used. This
- * function frees memory it had previously allocated.
- *
- * \param p struct progress_t pointer
- *
- * \returns Status code
- */
-int os_pfinish(struct progress_t *p);
diff --git a/src/platform.h b/src/platform.h
deleted file mode 100644
index ac8d7cb..0000000
--- a/src/platform.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/**
- * Defines __PLATFORM__, ENDIAN_BIG, and ENDIAN_LITTLE
- *
- * Source: https://sourceforge.net/p/predef/wiki/Architectures/
- */
-#if __amd64 || __amd64__ || __x86_64 || __x86_64__ || _M_AMD64 || _M_X64
- #define __PLATFORM__ "amd64"
-#elif i386 || __i386 || __i386__ || _M_IX86 || __X86__ || __THW_INTEL__ || __I86__ || __INTEL__ || __386
- #define __PLATFORM__ "x86"
-#elif __aarch64__
- #define __PLATFORM__ "arm64"
-#elif __arm__ || __TARGET_ARCH_ARM || _ARM || _M_ARM || __arm
- #if __thumb__ || __TARGET_ARCH_THUMB || _M_ARMT
- #define __PLATFORM__ "armthumb"
- #else
- #define __PLATFORM__ "arm"
- #endif
-#elif _ARCH_PPC64 || __powerpc64__
- #define __PLATFORM__ "powerpc64"
-#elif _ARCH_PPC || __powerpc
- #define __PLATFORM__ "powerpc"
-#elif __ia64__ || __ia64 || _M_IA64 || __itanium__
- #define __PLATFORM__ "ia64"
-#elif __370__ || __THW_370__
- #define __PLATFORM__ "system/370"
-#elif __s390__ || __s390x__
- #define __PLATFORM__ "system/390"
-#elif __zarch__ || __SYSC_ZARCH__
- #define __PLATFORM__ "z/arch"
-#elif __sparc__ || __sparc
- #define __PLATFORM__ "sparc"
-#elif __sh__
- #define __PLATFORM__ "superh"
-#elif __alpha__ || __alpha || _M_ALPHA
- #define __PLATFORM__ "alpha"
-#elif __mips__ || __mips || __MIPS__
- #define __PLATFORM__ "mips"
-#elif __m68k__ || M68000 || __MC68K__
- #define __PLATFORM__ "m68k"
-#elif __bfin || __BFIN__
- #define __PLATFORM__ "blackfin"
-#elif __epiphany__
- #define __PLATFORM__ "epiphany"
-#elif __hppa__ || __HPPA__ || __hppa
- #define __PLATFORM__ "hp/pa"
-#else
- #define __PLATFORM__ "unknown"
-#endif
-
-// Works with GCC and Clang
-#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
- #define ENDIAN_LITTLE 1
-#else
- #define ENDIAN_BIG 1
-#endif
diff --git a/src/uid.c b/src/uid.c
deleted file mode 100644
index db28a31..0000000
--- a/src/uid.c
+++ /dev/null
@@ -1,64 +0,0 @@
-#include
-#include
-#include "uid.h"
-#include "utils.h"
-#include "platform.h"
-
-//TODO: uid_create(UID*,int): Create UID with target in mind
-
-//
-// uid_str
-//
-
-int uid_str(char *buf, UID *uid, int target) {
- #ifdef ENDIAN_LITTLE
- if (target == UID_UUID)
- uid_swap(uid);
- #else
- if (target == UID_GUID)
- uid_swap(uid);
- #endif
- return snprintf(buf, UID_LENGTH,
- "%08X-%04X-%04X-%04X-%02X%02X%02X%02X%02X%02X",
- uid->time_low, uid->time_mid, uid->time_ver, uid->clock,
- uid->data[10], uid->data[11], uid->data[12],
- uid->data[13], uid->data[14], uid->data[15]
- );
-}
-
-//
-// uid_swap
-//
-
-void uid_swap(UID *uid) {
- uid->time_low = bswap32(uid->time_low);
- uid->time_mid = bswap16(uid->time_mid);
- uid->time_ver = bswap16(uid->time_ver);
- uid->clock = bswap16(uid->clock);
-}
-
-//
-// uid_nil
-//
-
-int uid_nil(UID *uid) {
-#if __SIZE_WIDTH__ == 64
- if (uid->u64[0]) return 0;
- if (uid->u64[1]) return 0;
-#else
- if (uid->u32[0]) return 0;
- if (uid->u32[1]) return 0;
- if (uid->u32[2]) return 0;
- if (uid->u32[3]) return 0;
-#endif
- return 1;
-}
-
-//
-// uid_cmp
-//
-
-int uid_cmp(UID *uid1, UID *uid2) {
- assert(0);
- return 0;
-}
diff --git a/src/uid.h b/src/uid.h
deleted file mode 100644
index 263197f..0000000
--- a/src/uid.h
+++ /dev/null
@@ -1,50 +0,0 @@
-#pragma once
-
-#include
-
-enum {
- UID_ASIS = 0, // Leave as-is, no swap intended
- UID_GUID = 1, // Meant as GUID, swap if on big-endian
- UID_UUID = 2, // Meant as UUID, swap if on little-endian
- UID_LENGTH = 40 // usually 36 but.. {} and \0
-};
-typedef char UID_TEXT[UID_LENGTH];
-/**
- * UUID/GUID structure
- */
-typedef struct UID {
- union {
- uint8_t data[16];
- uint16_t u16[8];
- uint32_t u32[4];
- uint64_t u64[2]; // Preferred to use when size width = 64
- struct {
- uint32_t time_low;
- uint16_t time_mid;
- uint16_t time_ver; // and time_hi
- uint16_t clock; // seq_hi and res_clock_low
- uint8_t node[6];
- };
- };
-} UID;
-
-/**
- * Format a UID (GUID/UUID) to a string buffer.
- */
-int uid_str(char *str, UID *uid, int target);
-/**
- * Byte swap GUID/UUID fields to convert a GUID into an UUID or vice-versa.
- * Useful when the endianess differs from a machine. GUIDs is usually
- * little-endian and UUIDs are usually big-endian.
- *
- * \param id UID structure
- */
-void uid_swap(UID *uid);
-/**
- * Verifies if a GUID/UUID is nil (null, empty).
- */
-int uid_nil(UID *uid);
-/**
- * Compares two GUIDs/UUIDs.
- */
-int uid_cmp(UID *uid1, UID *uid2);
diff --git a/src/utils.c b/src/utils.c
deleted file mode 100644
index 963af64..0000000
--- a/src/utils.c
+++ /dev/null
@@ -1,213 +0,0 @@
-#include
-#include
-#include "utils.h"
-
-//
-// bintostr
-//
-
-int bintostr(char *buf, uint64_t n) { // Lazy code 2.0, sorry
- float f = n;
- char *fs;
- //TODO: base-10 {kB|MB|GB|TB|PB}
- if (f >= TiB) {
- fs = "%.2f TiB";
- f /= TiB;
- } else if (f >= GiB) {
- fs = "%.2f GiB";
- f /= GiB;
- } else if (f >= MiB) {
- fs = "%.1f MiB";
- f /= MiB;
- } else if (f >= KiB) {
- fs = "%.1f KiB";
- f /= KiB;
- } else
- fs = "%g B";
- return snprintf(buf, BINSTR_LENGTH, fs, f);
-}
-
-//
-// strtobin
-//
-
-int strtobin(uint64_t *size, const oschar *input) {
- float f;
- char c;
-#ifdef _WIN32
- if (swscanf(input, L"%f%c", &f, &c) != 2) {
- return 1;
- }
-#else
- if (sscanf(input, "%f%c", &f, &c) != 2) {
- return 1;
- }
-#endif
- if (f <= 0) return 2;
- uint64_t u;
- switch (c) {
- case 'T': case 't':
- u = f * 1099511627776; break;
- case 'G': case 'g':
- u = f * 1073741824; break;
- case 'M': case 'm':
- u = f * 1048576; break;
- case 'K': case 'k':
- u = f * 1024; break;
- case 'B': case 'b':
- u = f; break;
- default: return 3;
- }
- *size = u;
- return 0;
-}
-
-//
-// bswap16
-//
-
-uint16_t bswap16(uint16_t v) {
- return v >> 8 | v << 8;
-}
-
-//
-// bswap32
-//
-
-uint32_t bswap32(uint32_t v) {
- v = (v >> 16) | (v << 16);
- return ((v & 0xFF00FF00) >> 8) | ((v & 0x00FF00FF) << 8);
-}
-
-//
-// bswap64
-//
-
-uint64_t bswap64(uint64_t v) {
- v = (v >> 32) | (v << 32);
- v = ((v & 0xFFFF0000FFFF0000) >> 16) | ((v & 0x0000FFFF0000FFFF) << 16);
- return ((v & 0xFF00FF00FF00FF00) >> 8) | ((v & 0x00FF00FF00FF00FF) << 8);
-}
-
-//
-// print_array
-//
-
-void print_array(char *p, uint8_t *a, size_t s) {
- size_t i = 0;
- putout(p);
- while (--s)
- printf(" %02X", a[i++]);
- putchar('\n');
-}
-
-//
-// extcmp
-//
-
-int extcmp(const oschar *s1, const oschar *s2) {
-#ifdef _WIN32
- wchar_t *ext = wcsrchr(s1, '.');
- if (ext == NULL) // Not found
- return 0;
- if (*++ext == 0) // Return if NULL after '.'
- return 0;
- //TODO: Lowercase s1
- return wcscmp(ext, s2) == 0;
-#else
- const char *ext = strrchr(s1, '.');
- if (ext == NULL) // Not found
- return 0;
- if (*++ext == 0) // Return if NULL after '.'
- return 0;
- //TODO: Lowercase s1
- return strcmp(ext, s2) == 0;
-#endif
-}
-
-//
-// putout
-//
-
-void putout(const char *s) {
- fputs(s, stdout);
-}
-
-//
-// pow2
-//
-
-uint32_t pow2(uint32_t n) {
- return (n & (n - 1)) == 0;
-}
-
-//
-// pow264
-//
-
-uint64_t pow264(uint64_t n) {
- return (n & (n - 1)) == 0;
-}
-
-//
-// fpow2
-//
-
-uint32_t fpow2(uint32_t n) {
- // Adapted from VBox/Storage/VDI.cpp getPowerOfTwo
- if (n == 0)
- return 0;
- uint32_t p = 0;
- while ((n & 1) == 0) {
- n >>= 1;
- ++p;
- }
- return n == 1 ? p : 0;
-}
-
-//
-// fpow264
-//
-
-uint64_t fpow264(uint64_t n) {
- if (n == 0)
- return 0;
- uint64_t p = 0;
- while ((n & 1) == 0) {
- n >>= 1;
- ++p;
- }
- return n == 1 ? p : 0;
-}
-
-//
-// str_s
-//
-
-void str_s(char *str, int size) {
- size_t i = 0;
- while (--size > 0 && str[i] != 0) {
- if (str[i] < 0x20 || str[i] > 0x7e)
- str[i] = ' ';
- }
-}
-
-//
-// wstra
-//
-
-int wstra(char *dest, char16 *src, int nchars) {
- char c = src[0];
- if (c == 0) {
- strcpy(dest, "");
- return -1;
- }
- size_t bi = 0; // buffer index
- --nchars; // to include null byte later on
- while (bi < nchars && (c = src[bi])) {
- dest[bi] = c < 0x20 || c > 0x7E ? '?' : c;
- ++bi;
- }
- dest[bi] = 0;
- return (int)bi;
-}
diff --git a/src/utils.h b/src/utils.h
deleted file mode 100644
index d9bd7f8..0000000
--- a/src/utils.h
+++ /dev/null
@@ -1,119 +0,0 @@
-#include
-#include
-
-#ifdef _WIN32
-// Represent a 'native' OS character
-#define oschar wchar_t
-#define osstr(quote) L##quote
-#define OSCHARFMT "%ls"
-#define oscmp wcscmp
-#else // POSIX
-// Represent a 'native' OS character
-#define oschar char
-#define osstr(quote) quote
-#define OSCHARFMT "%s"
-#define oscmp strcmp
-#endif
-
-#ifndef DEF_CHAR16
-#define DEF_CHAR16
-typedef uint16_t char16;
-#endif
-
-// Convert sector number/size to byte offset/size.
-#define SECTOR_TO_BYTE(u) ((uint64_t)(u) << 9)
-
-// Convert byte offset/size to sector number/size.
-#define BYTE_TO_SECTOR(u) ((u) >> 9)
-
-#define TiB 1099511627776
-#define TB 1000000000000
-#define GiB 1073741824
-#define GB 1000000000
-#define MiB 1048576
-#define MB 1000000
-#define KiB 1024
-#define KB 1000
-
-/**
- * Imitates `fputs(*, stdout)` for ease of typing.
- */
-void putout(const char *s);
-
-/// Binary character buffer length
-#define BINSTR_LENGTH 16
-
-/**
- * Get formatted binary (ISO) size with suffix, buffer fixed at 16 characters
- */
-int bintostr(char *buffer, uint64_t size);
-
-/**
- * Unformat a binary number into a 64-bit number.
- */
-int strtobin(uint64_t *size, const oschar *input);
-
-/**
- * Print array with prefix string
- */
-void print_array(char *p, uint8_t *a, size_t s);
-
-/**
- * Byte swap a 16-bit (2-Byte) value.
- */
-uint16_t bswap16(uint16_t);
-/**
- * Byte swap a 32-bit (4-Byte) value.
- */
-uint32_t bswap32(uint32_t);
-/**
- * Byte swap a 64-bit (8-Byte) value.
- */
-uint64_t bswap64(uint64_t);
-
-/**
- * Compare file path with constant extension string. This is currently only
- * used in the 'new' operation to obtain what disk type to create.
- *
- * E.g. `extcmp("test.bin", "bin")` evaluates to non-zero
- */
-int extcmp(const oschar *s1, const oschar *s2);
-
-/**
- * Checks if number is a power of 2.
- *
- * Returns non-zero if number is a power of 2.
- */
-uint32_t pow2(uint32_t);
-
-/**
- * Checks if 64-bit number is a power of 2.
- *
- * Returns non-zero if number is a power of 2.
- */
-uint64_t pow264(uint64_t);
-
-/**
- * Find nearest power of 2.
- */
-uint32_t fpow2(uint32_t);
-
-/**
- * Find nearest power of 2 with a 64-bit number.
- */
-uint64_t fpow264(uint64_t);
-
-/**
- * Make a string safer to print
- */
-void str_s(char *str, int size);
-
-/**
- * Convert an UTF-16 string to an ASCII string and returns number of charaters
- * copied with the destination's maximum buffer size. This function fills up
- * upto dsize-1 characters and inserts a null terminator and is useful when
- * processing GPT entries.
- *
- * \returns Number of characters copied or negative on error
- */
-int wstra(char *dest, char16 *src, int nchars);
diff --git a/src/utils/bin.c b/src/utils/bin.c
new file mode 100644
index 0000000..94369fb
--- /dev/null
+++ b/src/utils/bin.c
@@ -0,0 +1,186 @@
+#include
+#include
+#include "utils/bin.h"
+
+//
+// bintostr
+//
+
+int bintostr(char *buf, uint64_t n) { // Lazy code 2.0, sorry
+ float f = n;
+ char *fs;
+ //TODO: base-10 {kB|MB|GB|TB|PB}
+ if (f >= TiB) {
+ fs = "%.2f TiB";
+ f /= TiB;
+ } else if (f >= GiB) {
+ fs = "%.2f GiB";
+ f /= GiB;
+ } else if (f >= MiB) {
+ fs = "%.1f MiB";
+ f /= MiB;
+ } else if (f >= KiB) {
+ fs = "%.1f KiB";
+ f /= KiB;
+ } else
+ fs = "%g B";
+ return snprintf(buf, BINSTR_LENGTH, fs, f);
+}
+
+//
+// strtobin
+//
+
+int strtobin(uint64_t *size, const oschar *input) {
+ float f; char c;
+#ifdef _WIN32
+ if (swscanf(input, L"%f%c", &f, &c) != 2) {
+ return 1;
+ }
+#else
+ if (sscanf(input, "%f%c", &f, &c) != 2) {
+ return 1;
+ }
+#endif
+ if (f <= 0) return 2;
+ uint64_t u = f;
+ switch (c) {
+ case 'T': case 't': *size = u * TiB; break;
+ case 'G': case 'g': *size = u * GiB; break;
+ case 'M': case 'm': *size = u * MiB; break;
+ case 'K': case 'k': *size = u * KiB; break;
+ case 'B': case 'b': *size = u; break;
+ default: return 3;
+ }
+ return 0;
+}
+
+//
+// bswap16
+//
+
+uint16_t bswap16(uint16_t v) {
+ return v >> 8 | v << 8;
+}
+
+//
+// bswap32
+//
+
+uint32_t bswap32(uint32_t v) {
+ v = (v >> 16) | (v << 16);
+ return ((v & 0xFF00FF00) >> 8) | ((v & 0x00FF00FF) << 8);
+}
+
+//
+// bswap64
+//
+
+uint64_t bswap64(uint64_t v) {
+ v = (v >> 32) | (v << 32);
+ v = ((v & 0xFFFF0000FFFF0000) >> 16) | ((v & 0x0000FFFF0000FFFF) << 16);
+ return ((v & 0xFF00FF00FF00FF00) >> 8) | ((v & 0x00FF00FF00FF00FF) << 8);
+}
+
+//
+// extcmp
+//
+
+int extcmp(const oschar *s1, const oschar *s2) {
+#ifdef _WIN32
+ wchar_t *ext = wcsrchr(s1, '.');
+ if (ext == NULL) // Not found
+ return 0;
+ if (*++ext == 0) // Return if NULL after '.'
+ return 0;
+ //TODO: Lowercase s1
+ return wcscmp(ext, s2) == 0;
+#else
+ const char *ext = strrchr(s1, '.');
+ if (ext == NULL) // Not found
+ return 0;
+ if (*++ext == 0) // Return if NULL after '.'
+ return 0;
+ //TODO: Lowercase s1
+ return strcmp(ext, s2) == 0;
+#endif
+}
+
+//
+// pow2
+//
+
+uint32_t pow2(uint32_t n) {
+ return (n & (n - 1)) == 0;
+}
+
+//
+// pow264
+//
+
+uint64_t pow264(uint64_t n) {
+ return (n & (n - 1)) == 0;
+}
+
+//
+// fpow2
+//
+
+uint32_t fpow2(uint32_t n) {
+ // Adapted from VBox/Storage/VDI.cpp getPowerOfTwo
+ if (n == 0)
+ return 0;
+ uint32_t p = 0;
+ while ((n & 1) == 0) {
+ n >>= 1;
+ ++p;
+ }
+ return n == 1 ? p : 0;
+}
+
+//
+// fpow264
+//
+
+uint64_t fpow264(uint64_t n) {
+ if (n == 0)
+ return 0;
+ uint64_t p = 0;
+ while ((n & 1) == 0) {
+ n >>= 1;
+ ++p;
+ }
+ return n == 1 ? p : 0;
+}
+
+//
+// str_s
+//
+
+void str_s(char *str, int size) {
+ size_t i = 0;
+ while (--size > 0 && str[i] != 0) {
+ if (str[i] < 0x20 || str[i] > 0x7e)
+ str[i] = ' ';
+ }
+}
+
+//
+// wstra
+//
+
+int wstra(char *dest, char16 *src, int nchars) {
+ char c = src[0];
+ if (c == 0) {
+ strcpy(dest, "");
+ return -1;
+ }
+ size_t bi = 0; // buffer index
+ --nchars; // to include null byte later on
+ while (bi < nchars && (c = src[bi])) {
+ dest[bi] = c < 0x20 || c > 0x7E ? '?' : c;
+ ++bi;
+ }
+ dest[bi] = 0;
+ return (int)bi;
+}
diff --git a/src/utils/bin.h b/src/utils/bin.h
new file mode 100644
index 0000000..80d494e
--- /dev/null
+++ b/src/utils/bin.h
@@ -0,0 +1,109 @@
+#include
+#include
+
+#ifdef _WIN32
+// Represent a 'native' OS character
+#define oschar wchar_t
+#define osstr(quote) L##quote
+#define OSCHARFMT "%ls"
+#define osstrcmp wcscmp
+#else // POSIX
+// Represent a 'native' OS character
+#define oschar char
+#define osstr(quote) quote
+#define OSCHARFMT "%s"
+#define osstrcmp strcmp
+#endif
+
+#ifndef DEF_CHAR16
+#define DEF_CHAR16
+typedef uint16_t char16;
+#endif
+
+// Convert sector number/size to byte offset/size.
+#define SECTOR_TO_BYTE(u) ((uint64_t)(u) << 9)
+
+// Convert byte offset/size to sector number/size.
+#define BYTE_TO_SECTOR(u) ((u) >> 9)
+
+#define TiB 1099511627776
+#define TB 1000000000000
+#define GiB 1073741824
+#define GB 1000000000
+#define MiB 1048576
+#define MB 1000000
+#define KiB 1024
+#define KB 1000
+
+/// Binary character buffer length
+#define BINSTR_LENGTH 16
+
+/**
+ * Get formatted binary (ISO) size with suffix, buffer fixed at 16 characters
+ */
+int bintostr(char *buffer, uint64_t size);
+
+/**
+ * Unformat a binary number into a 64-bit number.
+ */
+int strtobin(uint64_t *size, const oschar *input);
+
+/**
+ * Byte swap a 16-bit (2-Byte) value.
+ */
+uint16_t bswap16(uint16_t);
+/**
+ * Byte swap a 32-bit (4-Byte) value.
+ */
+uint32_t bswap32(uint32_t);
+/**
+ * Byte swap a 64-bit (8-Byte) value.
+ */
+uint64_t bswap64(uint64_t);
+
+/**
+ * Compare file path with constant extension string. This is currently only
+ * used in the 'new' operation to obtain what disk type to create.
+ *
+ * E.g. `extcmp("test.bin", "bin")` evaluates to non-zero
+ */
+int extcmp(const oschar *s1, const oschar *s2);
+
+/**
+ * Checks if number is a power of 2.
+ *
+ * Returns non-zero if number is a power of 2.
+ */
+uint32_t pow2(uint32_t);
+
+/**
+ * Checks if 64-bit number is a power of 2.
+ *
+ * Returns non-zero if number is a power of 2.
+ */
+uint64_t pow264(uint64_t);
+
+/**
+ * Find nearest power of 2.
+ */
+uint32_t fpow2(uint32_t);
+
+/**
+ * Find nearest power of 2 with a 64-bit number.
+ */
+uint64_t fpow264(uint64_t);
+
+/**
+ * Make a string safer to print
+ */
+void str_s(char *str, int size);
+
+/**
+ * Convert an UTF-16 string to an ASCII string and returns number of charaters
+ * copied with the destination's maximum buffer size. This function fills up
+ * upto dsize-1 characters and inserts a null terminator and is useful when
+ * processing GPT entries.
+ *
+ * \returns Number of characters copied or negative on error
+ */
+int wstra(char *dest, char16 *src, int nchars);
diff --git a/src/utils/hash.c b/src/utils/hash.c
new file mode 100644
index 0000000..99a5b16
--- /dev/null
+++ b/src/utils/hash.c
@@ -0,0 +1,122 @@
+/**
+ * XXHash32 implementation
+ */
+
+#include
+#include
+#include
+#include "utils/hash.h"
+
+static const uint32_t PRIME32_1 = 2654435761U;
+static const uint32_t PRIME32_2 = 2246822519U;
+static const uint32_t PRIME32_3 = 3266489917U;
+static const uint32_t PRIME32_4 = 668265263U;
+static const uint32_t PRIME32_5 = 374761393U;
+
+// Internal: rotate 32-bit value (v) with offset (o)
+uint32_t hash_rot(uint32_t v, int o) {
+ return (v << o) | (v >> (32 - o));
+}
+
+//
+// hash_string
+//
+
+uint32_t hash_string(const char *data) {
+ return hash_compute_s(data, strlen(data), 0);
+}
+
+//
+// hash_compute
+//
+
+uint32_t hash_compute(const char *data, uint32_t length) {
+ return hash_compute_s(data, length, 0);
+}
+
+//
+// hash_compute_s
+//
+
+uint32_t hash_compute_s(const char *data, uint32_t length, uint32_t seed) {
+ union {
+ uint32_t *u32;
+ uint8_t *u8;
+ const char *c8;
+ } p;
+ uint32_t hash;
+ const char *end = data + length; // End of data pointer
+
+ p.c8 = data;
+
+ //
+ // Main loop
+ //
+
+ if (length >= 16) {
+ const char *limit = end - 16;
+
+ uint32_t v1 = seed + PRIME32_1 + PRIME32_2;
+ uint32_t v2 = seed + PRIME32_2;
+ uint32_t v3 = seed + 0;
+ uint32_t v4 = seed - PRIME32_1;
+
+ // Main loop proccesses 4*4 Bytes of information per iteration
+ do {
+ v1 += p.u32[0] * PRIME32_2;
+ v1 = hash_rot(v1, 13);
+ v1 *= PRIME32_1;
+
+ v2 += p.u32[1] * PRIME32_2;
+ v2 = hash_rot(v2, 13);
+ v2 *= PRIME32_1;
+
+ v3 += p.u32[2] * PRIME32_2;
+ v3 = hash_rot(v3, 13);
+ v3 *= PRIME32_1;
+
+ v4 += p.u32[3] * PRIME32_2;
+ v4 = hash_rot(v4, 13);
+ v4 *= PRIME32_1;
+
+ p.u32 += 4;
+ } while (p.c8 <= limit);
+
+ hash = hash_rot(v1, 1) + hash_rot(v2, 7) +
+ hash_rot(v3, 12) + hash_rot(v4, 18);
+ } else {
+ hash = seed + PRIME32_5;
+ }
+
+ hash += length;
+
+ //
+ // Finalization
+ //
+
+ // Per 4 Bytes
+ while (p.c8 <= end - 4) {
+ hash += *p.u32 * PRIME32_3;
+ hash = hash_rot(hash, 17) * PRIME32_4;
+ ++p.u32;
+ }
+
+ // Per Byte
+ while (p.c8 < end) {
+ hash += *p.u8 * PRIME32_5;
+ hash = hash_rot(hash, 11) * PRIME32_1;
+ ++p.u8;
+ }
+
+ //
+ // Avalanche
+ //
+
+ hash ^= hash >> 15;
+ hash *= PRIME32_2;
+ hash ^= hash >> 13;
+ hash *= PRIME32_3;
+ hash ^= hash >> 16;
+
+ return hash;
+}
diff --git a/src/utils/hash.h b/src/utils/hash.h
new file mode 100644
index 0000000..476ac37
--- /dev/null
+++ b/src/utils/hash.h
@@ -0,0 +1,41 @@
+/**
+ * Hashing module re-written for clarity.
+ *
+ * Original source: http://www.azillionmonkeys.com/qed/hash.html
+ */
+
+/**
+ * Hash a string
+ */
+uint32_t hash_string(const char *data);
+
+/**
+ * Hash data
+ */
+uint32_t hash_compute(const char *data, uint32_t length);
+
+/**
+ * Continue the computation of the hash with the previous result
+ */
+uint32_t hash_compute_s(const char *data, uint32_t length, uint32_t seed);
+
+/**
+ * Hash a string
+ *
+ * Deprecated
+ */
+uint32_t hash_superfashhash_str(const char *data);
+
+/**
+ * Hash data
+ *
+ * Deprecated
+ */
+uint32_t hash_superfashhash(const char *data, uint32_t len);
+
+/**
+ * Continue the computation of the hash with the previous result
+ *
+ * Deprecated
+ */
+uint32_t hash_superfashhash_compute(const char *data, uint32_t len, uint32_t seed);
\ No newline at end of file
diff --git a/src/utils/os.c b/src/utils/os.c
new file mode 100644
index 0000000..8a8ef12
--- /dev/null
+++ b/src/utils/os.c
@@ -0,0 +1,296 @@
+#include
+#include "os.h"
+#ifndef _WIN32
+#include // memset
+#include
+#include
+#include
+#endif
+
+//
+// os_fopen
+//
+
+__OSFILE os_fopen(const oschar *path) {
+#ifdef _WIN32
+ __OSFILE fd = CreateFileW(
+ path, // lpFileName
+ GENERIC_READ | GENERIC_WRITE, // dwDesiredAccess
+ 0, // dwShareMode
+ NULL, // lpSecurityAttributes
+ OPEN_EXISTING, // dwCreationDisposition
+ 0, // dwFlagsAndAttributes
+ NULL // hTemplateFile
+ );
+ if (fd == INVALID_HANDLE_VALUE)
+ return 0;
+#else
+ __OSFILE fd = open(path, O_RDWR);
+ if (fd == -1)
+ return 0;
+#endif
+ return fd;
+}
+
+//
+// os_fcreate
+//
+
+__OSFILE os_fcreate(const oschar *path) {
+#ifdef _WIN32
+ __OSFILE fd = CreateFileW(
+ path, // lpFileName
+ GENERIC_READ | GENERIC_WRITE, // dwDesiredAccess
+ 0, // dwShareMode
+ NULL, // lpSecurityAttributes
+ CREATE_ALWAYS, // dwCreationDisposition
+ 0, // dwFlagsAndAttributes
+ NULL // hTemplateFile
+ );
+ if (fd == INVALID_HANDLE_VALUE)
+ return 0;
+#else
+ __OSFILE fd = open(path, O_RDWR | O_CREAT | O_TRUNC);
+ if (fd == -1)
+ return 0;
+#endif
+ return fd;
+}
+
+//
+// os_fseek
+//
+
+int os_fseek(__OSFILE fd, int64_t pos, int flags) {
+#ifdef _WIN32
+ LARGE_INTEGER a;
+ a.QuadPart = pos;
+ if (SetFilePointerEx(fd, a, NULL, flags) == 0)
+ return -1;
+#else
+ if (lseek(fd, (off_t)pos, flags) == -1)
+ return -1;
+#endif
+ return 0;
+}
+
+//
+// os_fread
+//
+
+int os_fread(__OSFILE fd, void *buffer, size_t size) {
+#ifdef _WIN32
+ DWORD r;
+ if (ReadFile(fd, buffer, size, &r, NULL) == 0)
+ return -1;
+ /*if (r != size) {
+ fprintf(stderr, "os_fread: Failed to read %u/%u bytes",
+ (uint32_t)r, (uint32_t)size);
+ return -2;
+ }*/
+#else
+ ssize_t r;
+ if ((r = read(fd, buffer, size)) == -1)
+ return -1;
+ /*if (r != size) {
+ fprintf(stderr, "os_fread: Failed to read %d/%u bytes",
+ (int32_t)r, (uint32_t)size);
+ return -2;
+ }*/
+#endif
+ return 0;
+}
+
+//
+// os_fwrite
+//
+
+int os_fwrite(__OSFILE fd, void *buffer, size_t size) {
+#ifdef _WIN32
+ DWORD r;
+ if (WriteFile(fd, buffer, size, &r, NULL) == 0)
+ return -1;
+ /*if (r != size)
+ return -2;*/
+#else
+ ssize_t r;
+ if ((r = write(fd, buffer, size)) == -1)
+ return -1;
+ /*if (r != size)
+ return -2;*/
+#endif
+ return 0;
+}
+
+//
+// os_fsize
+//
+
+int os_fsize(__OSFILE fd, uint64_t *size) {
+#if _WIN32
+ LARGE_INTEGER li;
+ // NOTE: Doc says 0 (FALSE) on failure which apparently doesn't.
+ // NOTE: Not supported in Windows Store Apps (use GetFileInformationByHandleEx).
+ // NOTE: Don't even try passing a uint64_t pointer in there!
+ if (GetFileSizeEx(fd, &li))
+ return -1;
+ *size = li.QuadPart;
+ return 0;
+#else
+ struct stat s;
+ if (fstat(fd, &s) == -1)
+ return 1;
+ // NOTE: fstat(2) sets st_size to 0 on block devices
+ switch (s.st_mode & __S_IFMT) {
+ case __S_IFREG:
+ case __S_IFLNK:
+ *size = s.st_size;
+ return 0;
+ case __S_IFBLK:
+ //TODO: BSD variants
+ return ioctl(fd, BLKGETSIZE64, size);
+ default: return -1;
+ }
+#endif
+}
+
+//
+//TODO: os_ftype
+//
+
+/*int os_ftype(__OSFILE fd) {
+#if _WIN32
+ GetFileType(fd);
+#else
+
+#endif
+ return 0;
+}*/
+
+//
+// os_falloc
+//
+
+int os_falloc(__OSFILE fd, uint64_t fsize) {
+ const int bsize = 1024 * 1024; // 1 MiB
+ uint8_t *buf = calloc(1, bsize); // zeroed
+ if (fsize >= bsize) {
+ if (buf == NULL)
+ return 1;
+ while (fsize > bsize) {
+ if (os_fwrite(fd, buf, bsize))
+ return -1;
+ fsize -= bsize;
+ }
+ }
+ if (fsize > 0) {
+ if (os_fwrite(fd, buf, bsize - fsize))
+ return -1;
+ }
+ free(buf);
+ return 0;
+}
+
+//
+// os_pinit
+//
+
+//TODO: Progress bar stuff should be its own module
+
+static const uint32_t OS_P_ALLOC = 2048;
+
+int os_pinit(struct progress_t *p, uint32_t flags, uint32_t max) {
+#if _WIN32
+ p->fd = GetStdHandle(STD_OUTPUT_HANDLE);
+
+ CONSOLE_SCREEN_BUFFER_INFO csbi;
+ GetConsoleScreenBufferInfo(p->fd, &csbi);
+ p->leny = (csbi.srWindow.Bottom - csbi.srWindow.Top + 1);
+ p->lenx = (csbi.srWindow.Right - csbi.srWindow.Left + 1);
+ p->inity = csbi.dwCursorPosition.Y;
+ p->initx = csbi.dwCursorPosition.X;
+#else
+ struct winsize ws;
+ ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws);
+ p->leny = ws.ws_row;
+ p->lenx = ws.ws_col;
+#endif
+ if ((p->bfill = malloc(OS_P_ALLOC << 1)) == NULL) // *2
+ return 2;
+ p->bspace = p->bfill + OS_P_ALLOC;
+ memset(p->bfill, '=', OS_P_ALLOC);
+ memset(p->bspace, ' ', OS_P_ALLOC);
+ p->maximum = max;
+ p->flags = flags;
+
+ return 0;
+}
+
+//
+// os_pupdate
+//
+
+int os_pupdate(struct progress_t *p, uint32_t val) {
+#if _WIN32
+ COORD c;
+ c.X = 0;
+ c.Y = p->inity;
+ SetConsoleCursorPosition(p->fd, c);
+#else
+ //printf("\033[%d;%dH", p->inity, 0);
+ fputs("\r", stdout); // "CSI2n" clears line
+#endif
+ float cc = (float)val / p->maximum; // current
+ int pc; // characters printed
+ uint32_t ph; // placeholder
+ switch (p->flags & 0xF) {
+ case PROG_MODE_CUR_MAX:
+ pc = printf("%u/%u [", val, p->maximum);
+ break;
+ case PROG_MODE_CUR_ONLY:
+ pc = printf("%u [", val);
+ break;
+ case PROG_MODE_POURCENT:
+ ph = (uint32_t)(cc * 100.0f);
+ /*if (ph == ((float)p->current / p->maximum) * 100.0f) { // same %
+ p->current = val;
+ return 0; // NOP
+ }*/
+ pc = printf("%u%% [", ph);
+ break;
+ default:
+ pc = printf("[");
+ break;
+ }
+ if (pc < 0)
+ return -11;
+
+ uint32_t total = p->lenx - pc - 2;
+ uint32_t occup = cc * total; // filled
+ uint32_t space = total - occup; // what's left
+ printf("%.*s%.*s]", occup, p->bfill, space, p->bspace);
+
+ p->current = val;
+ return 0;
+}
+
+//
+// os_pfinish
+//
+
+int os_pfinish(struct progress_t *p) {
+ if (p->flags & PROG_FLAG_REMOVE) {
+#if _WIN32
+ COORD c;
+ c.X = 0;
+ c.Y = p->inity;
+ SetConsoleCursorPosition(p->fd, c);
+#else
+ fputs("\r", stdout);
+#endif
+ printf("%.*s", p->lenx, p->bspace);
+ }
+ free(p->bfill);
+ putchar('\n');
+ return 0;
+}
diff --git a/src/utils/os.h b/src/utils/os.h
new file mode 100644
index 0000000..67f6f3e
--- /dev/null
+++ b/src/utils/os.h
@@ -0,0 +1,122 @@
+#include "utils/bin.h"
+#include "vdisk/vdisk.h"
+
+#ifdef _WIN32
+#include
+typedef HANDLE __OSFILE;
+#else // Posix
+#include
+#include
+#include
+#include
+#include
+
+#ifndef __OSFILE_H
+#define __OSFILE_H
+typedef int __OSFILE;
+#endif // __OSFILE_H
+#endif // _WIN32
+
+//
+// File functions
+//
+
+/**
+ * Open a file stream. File or device must exist. Consult documentation on
+ * which devices are supported.
+ */
+__OSFILE os_fopen(const oschar *path);
+
+/**
+ * Always create and overwrite a file path. This cannot create devices.
+ */
+__OSFILE os_fcreate(const oschar *path);
+
+/**
+ * Seek into a position within the stream.
+ */
+int os_fseek(__OSFILE fd, int64_t position, int flags);
+
+/**
+ * Read data from stream from current position.
+ */
+int os_fread(__OSFILE fd, void *buffer, size_t size);
+
+/**
+ * Write data to stream at current position, overwrites.
+ */
+int os_fwrite(__OSFILE fd, void *buffer, size_t size);
+
+/**
+ * Get the file size, or the disk size, in bytes. If the handle is a file,
+ * the file size is set, otherwise if the handle is a block device, the
+ * disk size is set. Uses GetFileSizeEx (Windows) or stat/ioctl (Linux).
+ */
+int os_fsize(__OSFILE fd, uint64_t *size);
+
+/**
+ * Zero write to file.
+ */
+int os_falloc(__OSFILE fd, uint64_t fsize);
+
+//
+// Progress functions
+//
+
+#ifndef DEFINITION_OS_PROGRESS
+#define DEFINITION_OS_PROGRESS
+enum {
+ PROG_MODE_NONE = 0,
+ PROG_MODE_CUR_MAX = 1,
+ PROG_MODE_CUR_ONLY = 2,
+ PROG_MODE_POURCENT = 3,
+ //TODO: PROG_MODE_INDETERMINATE
+
+ PROG_FLAG_REMOVE = 0x100, // Remove progress bar when done
+};
+
+struct progress_t {
+ uint32_t current, maximum;
+ uint16_t initx, inity, lenx, leny;
+ char *bfill; // fill char buffer
+ char *bspace; // empty char buffer
+ uint32_t flags;
+#if _WIN32
+ HANDLE fd;
+#else
+ size_t res1;
+#endif
+};
+#endif // DEFINITION_OS_PROGRESS
+
+/**
+ * Initiate a new progress bar. This function allocates, but manages its own
+ * memory.
+ *
+ * \param p struct progress_t pointer
+ * \param flags See PROG enumerations
+ * \param max Maximum value
+ *
+ * \returns Status code
+ */
+int os_pinit(struct progress_t *p, uint32_t flags, uint32_t max);
+
+/**
+ * Update the progress bar with a new current value.
+ *
+ * \param p struct progress_t pointer
+ * \param val New current value
+ *
+ * \returns Status code
+ */
+int os_pupdate(struct progress_t *p, uint32_t val);
+
+/**
+ * Indiate that the progress bar is finished and will no longer be used. This
+ * function frees memory it had previously allocated.
+ *
+ * \param p struct progress_t pointer
+ *
+ * \returns Status code
+ */
+int os_pfinish(struct progress_t *p);
diff --git a/src/utils/platform.h b/src/utils/platform.h
new file mode 100644
index 0000000..ac8d7cb
--- /dev/null
+++ b/src/utils/platform.h
@@ -0,0 +1,55 @@
+/**
+ * Defines __PLATFORM__, ENDIAN_BIG, and ENDIAN_LITTLE
+ *
+ * Source: https://sourceforge.net/p/predef/wiki/Architectures/
+ */
+#if __amd64 || __amd64__ || __x86_64 || __x86_64__ || _M_AMD64 || _M_X64
+ #define __PLATFORM__ "amd64"
+#elif i386 || __i386 || __i386__ || _M_IX86 || __X86__ || __THW_INTEL__ || __I86__ || __INTEL__ || __386
+ #define __PLATFORM__ "x86"
+#elif __aarch64__
+ #define __PLATFORM__ "arm64"
+#elif __arm__ || __TARGET_ARCH_ARM || _ARM || _M_ARM || __arm
+ #if __thumb__ || __TARGET_ARCH_THUMB || _M_ARMT
+ #define __PLATFORM__ "armthumb"
+ #else
+ #define __PLATFORM__ "arm"
+ #endif
+#elif _ARCH_PPC64 || __powerpc64__
+ #define __PLATFORM__ "powerpc64"
+#elif _ARCH_PPC || __powerpc
+ #define __PLATFORM__ "powerpc"
+#elif __ia64__ || __ia64 || _M_IA64 || __itanium__
+ #define __PLATFORM__ "ia64"
+#elif __370__ || __THW_370__
+ #define __PLATFORM__ "system/370"
+#elif __s390__ || __s390x__
+ #define __PLATFORM__ "system/390"
+#elif __zarch__ || __SYSC_ZARCH__
+ #define __PLATFORM__ "z/arch"
+#elif __sparc__ || __sparc
+ #define __PLATFORM__ "sparc"
+#elif __sh__
+ #define __PLATFORM__ "superh"
+#elif __alpha__ || __alpha || _M_ALPHA
+ #define __PLATFORM__ "alpha"
+#elif __mips__ || __mips || __MIPS__
+ #define __PLATFORM__ "mips"
+#elif __m68k__ || M68000 || __MC68K__
+ #define __PLATFORM__ "m68k"
+#elif __bfin || __BFIN__
+ #define __PLATFORM__ "blackfin"
+#elif __epiphany__
+ #define __PLATFORM__ "epiphany"
+#elif __hppa__ || __HPPA__ || __hppa
+ #define __PLATFORM__ "hp/pa"
+#else
+ #define __PLATFORM__ "unknown"
+#endif
+
+// Works with GCC and Clang
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+ #define ENDIAN_LITTLE 1
+#else
+ #define ENDIAN_BIG 1
+#endif
diff --git a/src/utils/uid.c b/src/utils/uid.c
new file mode 100644
index 0000000..1423497
--- /dev/null
+++ b/src/utils/uid.c
@@ -0,0 +1,89 @@
+#include
+#include
+#include
+#include "utils/uid.h"
+#include "utils/bin.h"
+#include "utils/platform.h"
+
+//TODO: uid_create(UID*,int): Create UID with target in mind
+
+//
+// uid_str
+//
+
+int uid_str(char *buf, UID *uid, int type) {
+ #ifdef ENDIAN_LITTLE
+ if (type == UID_UUID)
+ uid_swap(uid);
+ #else
+ if (type == UID_GUID)
+ uid_swap(uid);
+ #endif
+ return snprintf(buf, UID_BUFFER_LENGTH,
+ "%08X-%04X-%04X-%04X-%02X%02X%02X%02X%02X%02X",
+ uid->time_low, uid->time_mid, uid->time_ver,
+ bswap16(uid->clock),
+ uid->data[10], uid->data[11], uid->data[12],
+ uid->data[13], uid->data[14], uid->data[15]);
+}
+
+//
+// uid_parse
+//
+
+int uid_parse(UID *uid, const char *buf, int type) {
+ unsigned int time_low, time_mid, time_ver, clock,
+ data10, data11, data12, data13, data14, data15;
+ int r = sscanf(buf, "%08X-%04X-%04X-%04X-%02X%02X%02X%02X%02X%02X",
+ &time_low, &time_mid, &time_ver, &clock,
+ &data10, &data11, &data12,
+ &data13, &data14, &data15);
+ if (r < 0 || r != 10)
+ return r;
+ uid->time_low = time_low;
+ uid->time_mid = (uint16_t)time_mid;
+ uid->time_ver = (uint16_t)time_ver;
+ uid->clock = bswap16(clock);
+ uid->data[10] = (uint8_t)data10;
+ uid->data[11] = (uint8_t)data11;
+ uid->data[12] = (uint8_t)data12;
+ uid->data[13] = (uint8_t)data13;
+ uid->data[14] = (uint8_t)data14;
+ uid->data[15] = (uint8_t)data15;
+ return 0;
+}
+
+//
+// uid_swap
+//
+
+void uid_swap(UID *uid) {
+ uid->time_low = bswap32(uid->time_low);
+ uid->time_mid = bswap16(uid->time_mid);
+ uid->time_ver = bswap16(uid->time_ver);
+ uid->clock = bswap16(uid->clock);
+}
+
+//
+// uid_nil
+//
+
+int uid_nil(UID *uid) {
+#if __SIZE_WIDTH__ == 64
+ if (uid->u64[0] || uid->u64[1])
+ return 0;
+#else
+ if (uid->u32[0] || uid->u32[1] || uid->u32[2] || uid->u32[3])
+ return 0;
+#endif
+ return 1;
+}
+
+//
+// uid_cmp
+//
+
+int uid_cmp(UID *uid1, UID *uid2) {
+ assert(0);
+ return 0;
+}
diff --git a/src/utils/uid.h b/src/utils/uid.h
new file mode 100644
index 0000000..b261d1e
--- /dev/null
+++ b/src/utils/uid.h
@@ -0,0 +1,71 @@
+#pragma once
+
+#include
+
+enum {
+ UID_ASIS = 0, // Leave as-is, no swap intended
+ UID_GUID = 1, // Meant as GUID, swap if on big-endian
+ UID_UUID = 2, // Meant as UUID, swap if on little-endian
+ UID_BUFFER_LENGTH = 40 // usually 36 but.. {} and \0
+};
+// Text buffer
+typedef char UID_TEXT[UID_BUFFER_LENGTH];
+
+/**
+ * UUID/GUID structure
+ */
+typedef struct UID {
+ union {
+ uint8_t data[16];
+ uint16_t u16[8];
+ uint32_t u32[4];
+ uint64_t u64[2]; // Preferred when __SIZE_WIDTH__ == 64
+ struct {
+ uint32_t time_low;
+ uint16_t time_mid;
+ uint16_t time_ver; // and time_hi
+ uint16_t clock; // seq_hi and res_clock_low
+ uint8_t node[6];
+ };
+ };
+} UID;
+
+/**
+ * Format a UID (GUID/UUID) to a string buffer.
+ *
+ * \param str String buffer target
+ * \param uid UID structure source
+ * \param type GUID, UUID, or ASIS
+ * \returns The result of snprintf
+ */
+int uid_str(char *str, UID *uid, int type);
+/**
+ * Parse a string GUID or UUID into a UID structure.
+ *
+ * Uses sscanf for parsing.
+ *
+ * \param uid UID structure target
+ * \param str String buffer source
+ * \param type GUID, UUID, or ASIS
+ * \returns 0 when successful, >0 on error, and <0 on sscanf error
+ */
+int uid_parse(UID *uid, const char *str, int type);
+/**
+ * Byte swap GUID/UUID fields to convert a GUID into an UUID or vice-versa.
+ * Useful when the endianess differs from a machine. GUIDs is usually
+ * little-endian and UUIDs are usually big-endian.
+ *
+ * \param uid UID structure
+ */
+void uid_swap(UID *uid);
+/**
+ * Verifies if a GUID/UUID is nil (null, empty).
+ *
+ * \param uid UID structure
+ * \returns Non-zero if nil
+ */
+int uid_nil(UID *uid);
+/**
+ * Compares two GUIDs/UUIDs.
+ */
+int uid_cmp(UID *uid1, UID *uid2);
diff --git a/src/vdisk.c b/src/vdisk.c
deleted file mode 100644
index 509b069..0000000
--- a/src/vdisk.c
+++ /dev/null
@@ -1,356 +0,0 @@
-#include
-#include
-#include
-#include
-#include
-#include "utils.h"
-#include "vdisk.h"
-
-//
-// vdisk_i_err
-//
-
-int vdisk_i_err(VDISK *vd, int e, int l, const char *f) {
- vd->err.line = l - 1;
- vd->err.func = f;
- return (vd->err.num = e);
-}
-
-// Until all implementations are done, this allows to catch
-// non-implemented functions during operation
-void vdisk_i_pre_init(VDISK *vd) {
- memset(&vd->cb, 0, sizeof(vd->cb));
-}
-
-//
-// vdisk_open
-//
-
-int vdisk_open(VDISK *vd, const oschar *path, uint32_t flags) {
- if ((vd->fd = os_fopen(path)) == 0)
- return vdisk_i_err(vd, VVD_EOS, __LINE__, __func__);
-
- vdisk_i_pre_init(vd);
-
- if (flags & VDISK_RAW)
- return vdisk_raw_open(vd, flags, 0);
-
- //
- // Format detection
- //
- // This hints the function to a format and tests both reading and
- // seeking capabilities on the file or device.
- //
-
- if (os_fread(vd->fd, &vd->format, 4))
- return vdisk_i_err(vd, VVD_EOS, __LINE__, __func__);
- if (os_fseek(vd->fd, 0, SEEK_SET))
- return vdisk_i_err(vd, VVD_EOS, __LINE__, __func__);
-
- //
- // Disk detection and loading
- //
-
- uint32_t internal = 0; // Internal flags
- uint32_t sign32;
- uint64_t sign64;
-
- switch (vd->format) {
- case VDISK_FORMAT_VDI:
- if (vdisk_vdi_open(vd, flags, internal))
- return vd->err.num;
- break;
- case VDISK_FORMAT_VMDK:
- if (vdisk_vmdk_open(vd, flags, internal))
- return vd->err.num;
- break;
- case VDISK_FORMAT_VHD: L_FORMAT_CASE_VHD:
- if (vdisk_vhd_open(vd, flags, internal))
- return vd->err.num;
- break;
- case VDISK_FORMAT_VHDX:
- if (vdisk_vhdx_open(vd, flags, internal))
- return vd->err.num;
- break;
- case VDISK_FORMAT_QED:
- if (vdisk_qed_open(vd, flags, internal))
- return vd->err.num;
- break;
- case VDISK_FORMAT_QCOW:
- if (vdisk_qcow_open(vd, flags, internal))
- return vd->err.num;
- break;
- case VDISK_FORMAT_PHDD:
- if (vdisk_phdd_open(vd, flags, internal))
- return vd->err.num;
- break;
- default: // Attempt at different offsets
-
- // VHD: (Fixed) 512 bytes before EOF
- if (os_fseek(vd->fd, -512, SEEK_END))
- return vdisk_i_err(vd, VVD_EOS, __LINE__, __func__);
- if (os_fread(vd->fd, &sign64, 8))
- return vdisk_i_err(vd, VVD_EOS, __LINE__, __func__);
- if (sign64 == VHD_MAGIC) {
- vd->format = VDISK_FORMAT_VHD;
- internal = 2;
- goto L_FORMAT_CASE_VHD;
- }
-
- return vdisk_i_err(vd, VVD_EVDFORMAT, __LINE__, __func__);
- }
-
- return 0;
-}
-
-//
-// vdisk_create
-//
-
-//TODO: VDISK *vd, void *meta, uint64_t capacity, uint32_t flags
-// The meta pointer serves for cloning/copying
-
-int vdisk_create(VDISK *vd, const oschar *path, int format, uint64_t capacity, uint16_t flags) {
-
- if (flags & VDISK_CREATE_TEMP) {
- //TODO: Attach random number
- path = osstr("vdisk.tmp");
- } else if (path == NULL)
- return vdisk_i_err(vd, VVD_ENULL, __LINE__, __func__);
-
- if (capacity == 0)
- return vdisk_i_err(vd, VVD_EVDBOUND, __LINE__, __func__);
-
- if ((vd->fd = os_fcreate(path)) == 0)
- return vdisk_i_err(vd, VVD_EOS, __LINE__, __func__);
-
- if (flags & VDISK_RAW) {
- vd->format = VDISK_FORMAT_RAW;
- if (os_falloc(vd->fd, capacity))
- return vdisk_i_err(vd, VVD_EOS, __LINE__, __func__);
- return VVD_EOK;
- }
-
- int e;
- switch (format) {
- case VDISK_FORMAT_VDI:
- e = vdisk_vdi_create(vd, capacity, flags);
- break;
- default:
- return vdisk_i_err(vd, VVD_EVDFORMAT, __LINE__, __func__);
- }
-
- return e ? e : vdisk_update(vd);
-}
-
-//
-//TODO: vdisk_close(VDISK *vd)
-//
-
-
-
-//
-// vdisk_str
-//
-
-const char* vdisk_str(VDISK *vd) {
- switch (vd->format) {
- case VDISK_FORMAT_VDI: return "VDI";
- case VDISK_FORMAT_VMDK: return "VMDK";
- case VDISK_FORMAT_VHD: return "VHD";
- case VDISK_FORMAT_VHDX: return "VHDX";
- case VDISK_FORMAT_QED: return "QED";
- case VDISK_FORMAT_QCOW: return "QCOW";
- case VDISK_FORMAT_PHDD: return "Parallels";
- case VDISK_FORMAT_RAW: return "RAW";
- default: return NULL; // Not opened, etc.
- }
-}
-
-//
-// vdisk_update
-//
-
-int vdisk_update(VDISK *vd) {
- switch (vd->format) {
- case VDISK_FORMAT_VDI:
- //TODO: Move pre-header signature in creation function
- if (os_fseek(vd->fd, 0, SEEK_SET))
- return vdisk_i_err(vd, VVD_EOS, __LINE__, __func__);
- if (os_fwrite(vd->fd, VDI_SIGNATURE, 40))
- return vdisk_i_err(vd, VVD_EOS, __LINE__, __func__);
- // skip signature
- if (os_fseek(vd->fd, VDI_SIGNATURE_SIZE, SEEK_SET))
- return vdisk_i_err(vd, VVD_EOS, __LINE__, __func__);
- if (os_fwrite(vd->fd, &vd->vdi->hdr, sizeof(VDI_HDR)))
- return vdisk_i_err(vd, VVD_EOS, __LINE__, __func__);
- if (os_fwrite(vd->fd, &vd->vdi->v1, sizeof(VDI_HEADERv1)))
- return vdisk_i_err(vd, VVD_EOS, __LINE__, __func__);
- // blocks
- if (os_fseek(vd->fd, vd->vdi->v1.offBlocks, SEEK_SET))
- return vdisk_i_err(vd, VVD_EOS, __LINE__, __func__);
- if (os_fwrite(vd->fd, vd->vdi->in.offsets, vd->vdi->v1.blk_total << 2))
- return vdisk_i_err(vd, VVD_EOS, __LINE__, __func__);
- break;
- /*case VDISK_FORMAT_VMDK:
- assert(0);
- break;
- case VDISK_FORMAT_VHD:
- assert(0);
- break;*/
- default:
- return vdisk_i_err(vd, VVD_EVDFORMAT, __LINE__, __func__);
- }
-
- return 0;
-}
-
-//
-//TODO: vdisk_flush(VDISK *vd)
-//
-
-//
-// vdisk_read_sector
-//
-
-int vdisk_read_sector(VDISK *vd, void *buffer, uint64_t lba) {
-
- //TODO: Consider an assert
- if (vd->cb.lba_read == NULL)
- return vdisk_i_err(vd, VVD_EVDTODO, __LINE__, __func__);
-
- return vd->cb.lba_read(vd, buffer, lba);
-}
-
-//
-//TODO: Consider vdisk_read_sectors
-// Read multiple sectors at once
-//
-
-//
-// vdisk_write_lba
-//
-
-int vdisk_write_lba(VDISK *vd, void *buffer, uint64_t lba) {
-
- assert(0);
-
- return VVD_EOK;
-}
-
-//
-// vdisk_read_block
-//
-
-int vdisk_read_block(VDISK *vd, void *buffer, uint64_t index) {
-
- assert(0);
-
- return VVD_EOK;
-}
-
-//
-// vdisk_write_block
-//
-
-int vdisk_write_block(VDISK *vd, void *buffer, uint64_t index) {
-
- assert(0);
-
- return VVD_EOK;
-}
-
-//
-// vdisk_write_block_at
-//
-
-int vdisk_write_block_at(VDISK *vd, void *buffer, uint64_t bindex, uint64_t dindex) {
-
- assert(0);
-
- return VVD_EOK;
-}
-
-//
-// vdisk_op_compact
-//
-
-int vdisk_op_compact(VDISK *vd, void(*cb)(uint32_t, void*)) {
- switch (vd->format) {
- case VDISK_FORMAT_VDI:
- return vdisk_vdi_compact(vd, cb);
- default:
- return vdisk_i_err(vd, VVD_EVDFORMAT, __LINE__, __func__);
- }
-}
-
-//
-// vdisk_error
-//
-
-const char* vdisk_error(VDISK *vd) {
- switch (vd->err.num) {
- case VVD_EOK:
- return "last operation was successful";
- case VVD_ENULL:
- return "input pointer is null";
- case VVD_EVDFORMAT:
- return "unsupported vdisk format";
- case VVD_EVDMAGIC:
- return "invalid magic";
- case VVD_EVDVERSION:
- return "unsupported version";
- case VVD_EVDTYPE:
- return "invalid disk type for vdisk function";
- case VVD_EVDFULL:
- return "vdisk is full";
- case VVD_EVDUNALLOC:
- return "block is unallocated";
- case VVD_EVDBOUND:
- return "block index is out of bounds";
- case VVD_EVDTODO:
- return "currently unimplemented";
- case VVD_EVDMISC:
- return "unknown error happened";
- case VVD_EOS:
-#if _WIN32
- // We're using the Win32 API, not the CRT functions, which may
- // yield different and probably unrelated messages
- static char _errmsgbuf[512];
- vd->err.num = GetLastError();
- int l = GetLocaleInfoEx( // Recommended over MAKELANGID
- LOCALE_NAME_USER_DEFAULT,
- LOCALE_ALL,
- 0,
- 0);
- FormatMessageA(
- FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_MAX_WIDTH_MASK,
- NULL,
- vd->err.num,
- l,
- _errmsgbuf,
- 512,
- NULL);
- return _errmsgbuf;
-#else
- return strerror(vd->err.num = errno);
-#endif
- default:
- assert(0); return NULL;
- }
-}
-
-//
-// vdisk_perror
-//
-
-#if _WIN32
- #define ERRFMT "%08X"
-#else
- #define ERRFMT "%d"
-#endif
-
-void vdisk_perror(VDISK *vd) {
- fprintf(stderr, "%s@%u: (" ERRFMT ") %s\n",
- vd->err.func, vd->err.line, vd->err.num, vdisk_error(vd));
-}
diff --git a/src/vdisk.h b/src/vdisk.h
deleted file mode 100644
index f53b639..0000000
--- a/src/vdisk.h
+++ /dev/null
@@ -1,273 +0,0 @@
-#pragma once
-
-#include "os.h"
-#include "utils.h"
-#include "vdisk/raw.h"
-#include "vdisk/vdi.h"
-#include "vdisk/vmdk.h"
-#include "vdisk/vhd.h"
-#include "vdisk/vhdx.h"
-#include "vdisk/qed.h"
-#include "vdisk/qcow.h"
-#include "vdisk/phdd.h"
-
-#define VDISK_M_ERR(vd,ERR) vdisk_i_err(vd,ERR,__LINE__,__func__)
-
-//
-// Constants
-//
-
-#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
-enum { // DISKFORMAT magical hints (LSB), used for VDISK.format
- VDISK_FORMAT_NONE = 0, // No formats has been specificied yet
- VDISK_FORMAT_RAW = 0xAAAAAAAA, // Raw files and devices
- VDISK_FORMAT_VDI = 0x203C3C3C, // "<<< " VirtualBox
- VDISK_FORMAT_VMDK = 0x564D444B, // "VMDK" VMware
- VDISK_FORMAT_VMDK_COW = 0x44574F43, // "COWD" VMware EXSi COW disk
- VDISK_FORMAT_VHD = 0x656E6F63, // "cone" VirtualPC/Hyper-V
- VDISK_FORMAT_VHDX = 0x78646876, // "vhdx" Hyper-V
- VDISK_FORMAT_QED = 0x00444551, // "QED\0" QEMU Enhanced Disk
- VDISK_FORMAT_QCOW = 0xFB494651, // "QFI\xFB" QEMU Copy-On-Write, v1/v2
- VDISK_FORMAT_PHDD = 0x68746957, // "With" Parallels HDD
- VDISK_FORMAT_BOCHS = 0x68636F68, // "Boch" Bochs Virtual HD Image
-// VDISK_FORMAT_DMG = 0x, // "" Apple DMG
-};
-#else
-
-#endif
-
-enum { // VDISK flags, the open/create flags may overlap
- VDISK_RAW = 0x1, // Open or create vdisk as raw
-
- //
- // vdisk_open flags
- //
-
- VDISK_OPEN_VDI_ONLY = 0x1000, //TODO: Only open successfully if VDISK is VDI
- VDISK_OPEN_VMDK_ONLY = 0x2000, //TODO: Only open successfully if VDISK is VMDK
- VDISK_OPEN_VHD_ONLY = 0x3000, //TODO: Only open successfully if VDISK is VHD
- VDISK_OPEN_VHDX_ONLY = 0x4000, //TODO: Only open successfully if VDISK is VHDX
- VDISK_OPEN_QED_ONLY = 0x5000, //TODO: Only open successfully if VDISK is QED
- VDISK_OPEN_QCOW_ONLY = 0x6000, //TODO: Only open successfully if VDISK is QCOW
- VDISK_OPEN_PHDD_ONLY = 0x7000, //TODO: Only open successfully if VDISK is Parallels HDD
- VDISK_OPEN_BOCHS_ONLY = 0x8000, //TODO: Only open successfully if VDISK is Parallels HDD
-
- //
- // vdisk_create flags
- //
-
- VDISK_CREATE_TEMP = 0x0100, //TODO: Create a temporary (random) vdisk file
-
- VDISK_CREATE_TYPE_DYNAMIC = 0x1000, //TODO: Create a dynamic type VDISK
- VDISK_CREATE_TYPE_FIXED = 0x2000, //TODO: Create a fixed type VDISK
- VDISK_CREATE_TYPE_PARENT = 0x3000, //TODO: Create a parent of the VDISK
- VDISK_CREATE_TYPE_SNAPSHOT = 0x4000, //TODO: Create a snapshot of the VDISK
- VDISK_CREATE_TYPE_MASK = 0x7000, // Type mask used internally
-};
-
-enum { // VDISK error codes
- VVD_EOK = 0, // VDISK OK
- VVD_EOS = -2, // OS/CRT related error
- VVD_ENULL = -3, // Input pointer is NULL
- VVD_ENOMEM = -4, // Could not allocate memory
- VVD_EVDFORMAT = -10, // Invalid VDISK format
- VVD_EVDMAGIC = -11, // Invalid VDISK magic signature
- VVD_EVDVERSION = -12, // Unsupported VDISK version (major)
- VVD_EVDTYPE = -13, // Unsupported VDISK type
- VVD_EVDFULL = -14, // VDISK is full and no more data can be allocated
- VVD_EVDUNALLOC = -15, // Block is unallocated
- VVD_EVDBOUND = -16, // Index was out of block index bounds
- VVD_EVDTODO = -254, // Currently unimplemented
- VVD_EVDMISC = -255, // Unknown
-};
-
-enum {
- // Operation has completed successfully.
- // Parameter: NULL
- VVD_NOTIF_DONE,
- // VDISK was created with type
- // Parameter: const char*
- VVD_NOTIF_VDISK_CREATED_TYPE_NAME,
- // Total amount of blocks before processing
- // Parameter: uint32_t
- VVD_NOTIF_VDISK_TOTAL_BLOCKS,
- // Total amount of blocks before processing (64-bit indexes)
- // Parameter: uint64_t
- VVD_NOTIF_VDISK_TOTAL_BLOCKS64,
- //
- // Parameter: uint32_t
- VVD_NOTIF_VDISK_CURRENT_BLOCK,
- //
- // Parameter: uint64_t
- VVD_NOTIF_VDISK_CURRENT_BLOCK64,
-};
-
-//
-// Structure definitions
-//
-
-// Defines a virtual disk.
-// All fields are more or less internal.
-typedef struct VDISK {
- // Defines the virtual disk format (e.g. VDI, VMDK, etc.).
- // See VDISKFORMAT enumeration.
- uint32_t format;
- // Flags. See VDISK_FLAG enumeration.
- uint32_t flags;
- // Reserved.
- uint32_t cookie;
- // Calculated absolute offset to data.
- // deprecated
- uint64_t offset;
- // Virtual disk capacity in bytes. For RAW files, it's the file size. For
- // RAW devices, it's the disk size. This is populated automatically.
- uint64_t capacity;
- // (Posix) File descriptor (Windows) File HANDLE
- __OSFILE fd;
- // Error structure
- struct {
- int num; // Error number
- int line; // Source file line number
- const char *func; // Function name
- } err;
- // Callback structure
- struct {
- // Read from a disk sector with a LBA index
- int (*lba_read)(struct VDISK*, void*, uint64_t);
- // Write to a disk sector with a LBA index
- int (*lba_write)(struct VDISK*, void*, uint64_t);
- // Read a dynamic block with a block index
- int (*blk_read)(struct VDISK*, void*, uint64_t);
- // Read a sector with a LBA index
- int (*blk_write)(struct VDISK*, void*, uint64_t);
- } cb;
- // Meta union
- union {
- void *meta;
- VDI_META *vdi;
- VMDK_META *vmdk;
- VHD_META *vhd;
- QED_META *qed;
- VHDX_META *vhdx;
- };
-} VDISK;
-
-//
-// SECTION Internal functions
-//
-
-/**
- * (Internal) Set errcode and errline.
- *
- * \returns errcode
- */
-int vdisk_i_err(VDISK *vd, int e, int l, const char *f);
-
-//
-// SECTION Functions
-//
-
-/**
- * Open a VDISK.
- *
- * When opening a file, this function verifies the file path, VDISK format,
- * header structure, version, and other fields.
- *
- * When creating a file, the specified file at the file path is overwritten.
- * An empty, unallocated VDISK is created. If VDISK_CREATE_TEMP is defined,
- * path parameter can be NULL, since the function will create a random
- * filename (OS).
- *
- * \param vd VDISK structure
- * \param path OS string path
- * \param flags Opening flags
- *
- * \returns Exit status
- */
-int vdisk_open(VDISK *vd, const oschar *path, uint32_t flags);
-
-/**
- * Create a VDISK.
- *
- * \param vd VDISK structure
- * \param path OS string path
- * \param format Virtual disk format
- * \param capacity Virtual disk capacity
- * \param flags Creation flags
- *
- * \returns Exit status
- */
-int vdisk_create(VDISK *vd, const oschar *path, int format, uint64_t capacity, uint16_t flags);
-
-/**
- * Returns a string representation of the loaded virtual disk. If a format was
- * not found, a null pointer is returned.
- */
-const char *vdisk_str(VDISK *vd);
-
-/**
- * Update header information and allocation tables into file or device.
- */
-int vdisk_update(VDISK *vd);
-
-/**
- * Seek and read a sector-size (512 bytes) of data from a sector index (LBA).
- *
- * This function checks if sector exists on dynamic type disks, and index
- * tables such as the BAT on VHDs.
- *
- * Returns error code. Non-zero being an error.
- */
-int vdisk_read_sector(VDISK *vd, void *buffer, uint64_t lba);
-
-/**
- * Seek to a block index and read it. The size of the block depends on the size
- * speicified in the VDISK structure. Only certain VDISK types are supported,
- * notably dynamic types. If unsupported, returns EVDFORMAT or EVDTYPE.
- */
-int vdisk_read_block(VDISK *vd, void *buffer, uint64_t index);
-
-/**
- *
- */
-int vdisk_write_lba(VDISK *vd, void *buffer, uint64_t lba);
-
-/**
- *
- */
-int vdisk_write_block(VDISK *vd, void *buffer, uint64_t index);
-
-/**
- *
- */
-int vdisk_write_block_at(VDISK *vd, void *buffer, uint64_t bindex, uint64_t dindex);
-
-//
-// SECTION
-//
-
-/**
- *
- */
-int vdisk_op_compact(VDISK *vd, void(*cb)(uint32_t, void*));
-
-/**
- *
- */
-//int vdisk_op_resize(VDISK *vd, void(*cb_progress)(uint64_t block));
-
-//
-// SECTION Error handling
-//
-
-/**
- * Returns an error message depending on the last value of vdisk_errno. If the
- * error is set to VVD_EOS, the error message will come from the OS (or CRT).
- */
-const char* vdisk_error(VDISK *vd);
-
-/**
- * Print to stdout, with the name of the function, a message with the last
- * value set to vdisk_errno.
- */
-void vdisk_perror(VDISK *vd);
diff --git a/src/vdisk/bochs.c b/src/vdisk/bochs.c
deleted file mode 100644
index 0e071b3..0000000
--- a/src/vdisk/bochs.c
+++ /dev/null
@@ -1,3 +0,0 @@
-#include "vdisk.h"
-#include "utils.h"
-#include "platform.h"
diff --git a/src/vdisk/bochs.h b/src/vdisk/bochs.h
deleted file mode 100644
index cad22c6..0000000
--- a/src/vdisk/bochs.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/**
- * Bochs Virtual HD Image
- *
- * Header is 512 bytes, including the general header.
- *
- * Little-endian
- *
- * http://bochs.sourceforge.net/doc/docbook/development/harddisk-redologs.html
- * §2.10
- */
-
-#include
-
-static const uint32_t BOCHS_VERSION = 0x00020000;
-static const uint32_t BOCHS_V1 = 0x00010000;
-static const uint32_t BOCHS_UNALLOC = 0xffffffff;
-
-typedef struct {
- char signacture[32]; // "Bochs Virtual HD Image"
- char type[16]; // "Redolog"
- char subtype[16]; // "Undoable", "Volatile", "Growing", "vvfat"
- uint32_t version; // 0x00010000, 0x00020000
- uint32_t hdrsize; // 512
-} BOCH_HDR;
-
-typedef struct {
- uint32_t nentries; // Number of entries in catalog
- uint32_t bitmapsize; // Cluster size in bytes
- uint32_t extentsize; // Extent cluster size in bytes
- uint32_t timestamp; // ("Undoable" only) timestamp, FAT format
- uint64_t disksize; // Disk capacity in bytes
-} BOCH_REDOLOG_HDR;
diff --git a/src/vdisk/fmt/bochs.c b/src/vdisk/fmt/bochs.c
new file mode 100644
index 0000000..3ee1b71
--- /dev/null
+++ b/src/vdisk/fmt/bochs.c
@@ -0,0 +1,3 @@
+#include "vdisk/vdisk.h"
+#include "utils/bin.h"
+#include "utils/platform.h"
diff --git a/src/vdisk/fmt/bochs.h b/src/vdisk/fmt/bochs.h
new file mode 100644
index 0000000..cad22c6
--- /dev/null
+++ b/src/vdisk/fmt/bochs.h
@@ -0,0 +1,32 @@
+/**
+ * Bochs Virtual HD Image
+ *
+ * Header is 512 bytes, including the general header.
+ *
+ * Little-endian
+ *
+ * http://bochs.sourceforge.net/doc/docbook/development/harddisk-redologs.html
+ * §2.10
+ */
+
+#include
+
+static const uint32_t BOCHS_VERSION = 0x00020000;
+static const uint32_t BOCHS_V1 = 0x00010000;
+static const uint32_t BOCHS_UNALLOC = 0xffffffff;
+
+typedef struct {
+ char signacture[32]; // "Bochs Virtual HD Image"
+ char type[16]; // "Redolog"
+ char subtype[16]; // "Undoable", "Volatile", "Growing", "vvfat"
+ uint32_t version; // 0x00010000, 0x00020000
+ uint32_t hdrsize; // 512
+} BOCH_HDR;
+
+typedef struct {
+ uint32_t nentries; // Number of entries in catalog
+ uint32_t bitmapsize; // Cluster size in bytes
+ uint32_t extentsize; // Extent cluster size in bytes
+ uint32_t timestamp; // ("Undoable" only) timestamp, FAT format
+ uint64_t disksize; // Disk capacity in bytes
+} BOCH_REDOLOG_HDR;
diff --git a/src/vdisk/fmt/phdd.c b/src/vdisk/fmt/phdd.c
new file mode 100644
index 0000000..06fb87a
--- /dev/null
+++ b/src/vdisk/fmt/phdd.c
@@ -0,0 +1,11 @@
+#include "utils/bin.h"
+#include "vdisk/vdisk.h"
+#include "utils/platform.h"
+#include
+
+int vdisk_phdd_open(VDISK *vd, uint32_t flags, uint32_t internal) {
+
+ assert(0);
+
+ return 0;
+}
diff --git a/src/vdisk/fmt/phdd.h b/src/vdisk/fmt/phdd.h
new file mode 100644
index 0000000..e180104
--- /dev/null
+++ b/src/vdisk/fmt/phdd.h
@@ -0,0 +1,32 @@
+/**
+ * PHDD: Parallels Hard Disk Drive
+ *
+ * https://github.com/qemu/qemu/blob/master/docs/interop/parallels.txt
+ * VBox/Storage/Parallels.cpp
+ */
+
+#include
+
+// Parallels header structure
+typedef struct {
+ // Magic header
+ char magic[16];
+ // Virtual disk version, currently 2
+ uint32_t version;
+ // (CHS) Heads
+ uint32_t heads;
+ // (CHS) Cylinders
+ uint32_t cylinders;
+ // (CHS) Number of sectors per track
+ uint32_t sectorsPerTrack;
+ // Number of entries in the allocation bitmap.
+ uint32_t number_entries;
+ // Total number of sectors
+ uint32_t sectors;
+ // Padding.
+// char Padding[24];
+} PHDD_HDR;
+
+struct VDISK;
+
+int vdisk_phdd_open(struct VDISK *vd, uint32_t flags, uint32_t internal);
diff --git a/src/vdisk/fmt/qcow.c b/src/vdisk/fmt/qcow.c
new file mode 100644
index 0000000..3d8374f
--- /dev/null
+++ b/src/vdisk/fmt/qcow.c
@@ -0,0 +1,11 @@
+#include "utils/bin.h"
+#include "vdisk/vdisk.h"
+#include "utils/platform.h"
+#include
+
+int vdisk_qcow_open(VDISK *vd, uint32_t flags, uint32_t internal) {
+
+ assert(0);
+
+ return 0;
+}
diff --git a/src/vdisk/fmt/qcow.h b/src/vdisk/fmt/qcow.h
new file mode 100644
index 0000000..a0e6529
--- /dev/null
+++ b/src/vdisk/fmt/qcow.h
@@ -0,0 +1,41 @@
+/**
+ * QCOW: QEMU Copy-On-Write disk
+ *
+ * https://github.com/qemu/qemu/blob/master/docs/interop/qcow2.txt
+ */
+
+#include
+
+// QCOW header structure
+typedef struct {
+ //
+ uint32_t magic;
+ //
+ uint32_t version;
+ //
+ uint64_t backing_file_offset;
+ //
+ uint32_t backing_file_size;
+ //
+ uint32_t cluster_bits;
+ // In bytes
+ uint64_t size;
+ // Encryption method
+ uint32_t crypt_method;
+ // L1 table size in bytes
+ uint32_t l1_size;
+ // L1 table offset in bytes
+ uint64_t l1_offset;
+ //
+ uint64_t refcount_table_offset;
+ //
+ uint32_t refcount_table_clusters;
+ //
+ uint32_t nb_snapshots;
+ //
+ uint64_t snapshots_offset;
+} QCOW_HDR;
+
+struct VDISK;
+
+int vdisk_qcow_open(struct VDISK *vd, uint32_t flags, uint32_t internal);
diff --git a/src/vdisk/fmt/qed.c b/src/vdisk/fmt/qed.c
new file mode 100644
index 0000000..ba12818
--- /dev/null
+++ b/src/vdisk/fmt/qed.c
@@ -0,0 +1,90 @@
+#include "vdisk/vdisk.h"
+#include "utils/bin.h"
+#include "utils/platform.h"
+#include
+
+int vdisk_qed_open(VDISK *vd, uint32_t flags, uint32_t internal) {
+ if ((vd->meta = malloc(QED_META_ALLOC)) == NULL)
+ return VDISK_ERROR(vd, VVD_ENOMEM);
+
+ if (os_fread(vd->fd, &vd->qed->hdr, sizeof(QED_HDR)))
+ return VDISK_ERROR(vd, VVD_EOS);
+
+ if (vd->qed->hdr.cluster_size < QED_CLUSTER_MIN ||
+ vd->qed->hdr.cluster_size > QED_CLUSTER_MAX ||
+ pow2(vd->qed->hdr.cluster_size) == 0)
+ return VDISK_ERROR(vd, VVD_EVDMISC);
+ if (vd->qed->hdr.table_size < QED_TABLE_MIN ||
+ vd->qed->hdr.table_size > QED_TABLE_MAX ||
+ pow2(vd->qed->hdr.table_size) == 0)
+ return VDISK_ERROR(vd, VVD_EVDMISC);
+
+ if (vd->qed->hdr.features > QED_FEATS)
+ return VDISK_ERROR(vd, VVD_EVDMISC);
+
+ // assert(header.image_size <= TABLE_NOFFSETS * TABLE_NOFFSETS * header.cluster_size)
+
+ uint32_t table_size = vd->qed->in.tablesize =
+ vd->qed->hdr.cluster_size * vd->qed->hdr.table_size;
+ uint32_t table_entries = vd->qed->in.entries = table_size / sizeof(uint64_t);
+ uint32_t clusterbits = fpow2(vd->qed->hdr.cluster_size);
+ uint32_t tablebits = fpow2(table_entries);
+
+ if ((vd->qed->in.L1.offsets = malloc(table_size)) == NULL)
+ return VDISK_ERROR(vd, VVD_ENOMEM);
+ if ((vd->qed->in.L2.offsets = malloc(table_size)) == NULL)
+ return VDISK_ERROR(vd, VVD_ENOMEM);
+ if (os_fseek(vd->fd, vd->qed->hdr.l1_offset, SEEK_SET))
+ return VDISK_ERROR(vd, VVD_EOS);
+ if (os_fread(vd->fd, vd->qed->in.L1.offsets, table_size))
+ return VDISK_ERROR(vd, VVD_EOS);
+
+ // assert(clusterbits + (2 * tablebits) <= 64);
+
+ vd->qed->in.mask = vd->qed->hdr.cluster_size - 1;
+ vd->qed->in.L2.mask = (table_entries - 1) << clusterbits;
+ vd->qed->in.L2.shift = clusterbits;
+ vd->qed->in.L1.mask = (table_entries - 1) << (clusterbits + tablebits);
+ vd->qed->in.L1.mask = clusterbits + tablebits;
+
+ vd->capacity = vd->qed->hdr.capacity;
+
+ vd->cb.lba_read = vdisk_qed_read_sector;
+
+ return 0;
+}
+
+int vdisk_qed_L2_load(VDISK *vd, uint64_t offset) {
+ if (vd->qed->in.L2.current == offset) // L2 already loaded
+ return 0;
+
+ if (os_fseek(vd->fd, offset, SEEK_SET))
+ return VDISK_ERROR(vd, VVD_EOS);
+ if (os_fread(vd->fd, vd->qed->in.L2.offsets, vd->qed->in.tablesize))
+ return VDISK_ERROR(vd, VVD_EOS);
+
+ vd->qed->in.L2.current = offset;
+
+ return 0;
+}
+
+int vdisk_qed_read_sector(VDISK *vd, void *buffer, uint64_t index) {
+ uint64_t offset = SECTOR_TO_BYTE(index);
+
+ uint32_t l1 = (offset >> vd->qed->in.L1.shift) & vd->qed->in.L1.mask;
+ uint32_t l2 = (offset >> vd->qed->in.L2.shift) & vd->qed->in.L2.mask;
+
+ if (l1 >= vd->qed->in.entries || l2 >= vd->qed->in.entries)
+ return VDISK_ERROR(vd, VVD_EVDMISC);
+ if (vdisk_qed_L2_load(vd, vd->qed->in.L1.offsets[l1]))
+ return vd->err.num;
+
+ offset = (uint64_t)vd->qed->in.L2.offsets[l2] + (offset & vd->qed->in.mask);
+
+ if (os_fseek(vd->fd, offset, SEEK_SET))
+ return VDISK_ERROR(vd, VVD_EOS);
+ if (os_fread(vd->fd, buffer, 512))
+ return VDISK_ERROR(vd, VVD_EOS);
+
+ return 0;
+}
diff --git a/src/vdisk/fmt/qed.h b/src/vdisk/fmt/qed.h
new file mode 100644
index 0000000..cd4a471
--- /dev/null
+++ b/src/vdisk/fmt/qed.h
@@ -0,0 +1,120 @@
+/**
+ * QED: QEMU Enhanced Disk
+ *
+ * Little-endian format. Features clusters, including the header (cluster0).
+ *
+ * Layout:
+ * +--------+----------+----------+----------+-----+
+ * | Header | L1 table | Cluster0 | Cluster1 | ... |
+ * +--------+----------+----------+----------+-----+
+ *
+ * For cluster allocation, there is a 2-level table:
+ *
+ * +----------+
+ * | L1 table | <- Fixed
+ * +----------+
+ * |
+ * +---------+---------+
+ * | | |
+ * +----------+ +-----+ +----------+
+ * | L2 table | | ... | | L2 table | <- Allocated on-demand
+ * +----------+ +-----+ +----------+
+ * |
+ * +------+------+
+ * | | |
+ * +------+ +-----+ +------+
+ * | Data | | ... | | Data | <- Cluster data
+ * +------+ +-----+ +------+
+ *
+ * Both the L1 table and L2 tables are of the same size (table_size * cluster_size).
+ * The L1 table holds absolute file offsets to L2 table clusters, which the
+ * L2 table holds absolute file offsets to data clusters.
+ *
+ * https://wiki.qemu.org/Features/QED/Specification
+ * https://github.com/qemu/qemu/blob/master/docs/interop/qed_spec.txt
+ */
+
+#include
+
+static const uint32_t QED_CLUSTER_DEFAULT = 64 * 1024; // 64K
+static const uint32_t QED_TABLE_DEFAULT = 4; // 4 clusters
+static const uint32_t QED_CLUSTER_MIN = 4096; // 2^12, or 4 * 1024
+static const uint32_t QED_CLUSTER_MAX = 67108864; // 2^26, or 64 * 1024 * 1024
+static const uint32_t QED_TABLE_MIN = 1;
+static const uint32_t QED_TABLE_MAX = 16;
+
+// Disk image uses a backup file for unallocated clusters
+static const uint64_t QED_F_BACKING_FILE = 1; // bit 0
+// Disk image needs to be checked before use
+static const uint64_t QED_F_NEED_CHECK = 2; // bit 1
+// Treat as raw
+static const uint64_t QED_F_BACKING_FILE_NO_PROBE = 4; // bit 2
+// Known features
+static const uint64_t QED_FEATS = QED_F_BACKING_FILE | QED_F_NEED_CHECK | QED_F_BACKING_FILE_NO_PROBE;
+
+// QED header structure
+typedef struct {
+ // Magic signature
+ uint32_t magic;
+ // In bytes, must be a power of 2 within [2^12, 2^26].
+ uint32_t cluster_size;
+ // For L1/L2 tables, in clusters, must be a power of 2 within [1, 16].
+ uint32_t table_size;
+ // In clusters
+ uint32_t header_size;
+ // Format feature flags
+ uint64_t features;
+ // Compat feature flags
+ uint64_t compat_features;
+ // Self-reset feature flags
+ uint64_t autoclear_features;
+ // L1 table offset in bytes
+ uint64_t l1_offset;
+ // Disk logical capacity in bytes
+ uint64_t capacity;
+ // (QED_F_BACKING_FILE) Offset, in bytes from start of header, to the
+ // name of the backing filename.
+ uint32_t backup_name_offset;
+ // (QED_F_BACKING_FILE) Size, in bytes, of the backing filename.
+ uint32_t backup_name_size;
+} QED_HDR;
+
+// Deprecated
+typedef struct {
+ uint64_t *offsets; // L2 table offsets to data clusters
+ uint64_t offset; // Last loaded offset
+ uint32_t tablesize; // table_size
+ uint32_t entries; /// number of entries
+} QED_L2CACHE;
+
+typedef struct {
+ uint32_t tablesize; // Calculated table size in bytes (table_size * cluster_size)
+ uint32_t entries; // Number of entries
+ uint64_t mask; // Offset mask after L1/L2 calculations
+ struct {
+ uint64_t *offsets;
+ uint64_t mask;
+ uint32_t shift;
+ } L1; // L1 table
+ struct {
+ uint64_t *offsets;
+ uint64_t mask;
+ uint32_t shift;
+ uint64_t current; // Last L2 offset loaded
+ } L2; // L2 table
+} QED_INTERNALS;
+
+typedef struct {
+ QED_HDR hdr;
+ QED_INTERNALS in;
+} QED_META;
+
+static const uint32_t QED_META_ALLOC = sizeof(QED_META);
+
+struct VDISK;
+
+int vdisk_qed_open(struct VDISK *vd, uint32_t flags, uint32_t internal);
+
+int vdisk_qed_L2_load(struct VDISK *vd, uint64_t index);
+
+int vdisk_qed_read_sector(struct VDISK *vd, void *buffer, uint64_t index);
diff --git a/src/vdisk/fmt/raw.c b/src/vdisk/fmt/raw.c
new file mode 100644
index 0000000..ee72ee3
--- /dev/null
+++ b/src/vdisk/fmt/raw.c
@@ -0,0 +1,25 @@
+#include "vdisk/vdisk.h"
+#include "utils/bin.h"
+#include "utils/platform.h"
+
+int vdisk_raw_open(VDISK *vd, uint32_t flags, uint32_t internal) {
+ if (os_fsize(vd->fd, &vd->capacity))
+ return VDISK_ERROR(vd, VVD_EOS);
+ vd->format = VDISK_FORMAT_RAW;
+ vd->cb.lba_read = vdisk_raw_read_lba;
+ return 0;
+}
+
+int vdisk_raw_read_lba(VDISK *vd, void *buffer, uint64_t index) {
+ uint64_t offset = SECTOR_TO_BYTE(index);
+
+ if (offset >= vd->capacity)
+ return VDISK_ERROR(vd, VVD_EVDBOUND);
+
+ if (os_fseek(vd->fd, offset, SEEK_SET))
+ return VDISK_ERROR(vd, VVD_EOS);
+ if (os_fread(vd->fd, buffer, 512))
+ return VDISK_ERROR(vd, VVD_EOS);
+
+ return 0;
+}
\ No newline at end of file
diff --git a/src/vdisk/fmt/raw.h b/src/vdisk/fmt/raw.h
new file mode 100644
index 0000000..5f75bbd
--- /dev/null
+++ b/src/vdisk/fmt/raw.h
@@ -0,0 +1,4 @@
+struct VDISK;
+
+int vdisk_raw_open(struct VDISK *vd, uint32_t flags, uint32_t internal);
+int vdisk_raw_read_lba(struct VDISK *vd, void *buffer, uint64_t index);
\ No newline at end of file
diff --git a/src/vdisk/fmt/vdi.c b/src/vdisk/fmt/vdi.c
new file mode 100644
index 0000000..de1f2ff
--- /dev/null
+++ b/src/vdisk/fmt/vdi.c
@@ -0,0 +1,258 @@
+#include // memcpy
+#include "vdisk/vdisk.h"
+#include "utils/bin.h"
+#include "utils/platform.h"
+#ifdef TRACE
+#include
+#include
+#endif
+
+//
+// vdisk_vdi_open
+//
+
+int vdisk_vdi_open(VDISK *vd, uint32_t flags, uint32_t internal) {
+ if ((vd->meta = malloc(VDI_META_ALLOC)) == NULL)
+ return VDISK_ERROR(vd, VVD_ENOMEM);
+
+ if (os_fseek(vd->fd, 0, SEEK_SET))
+ return VDISK_ERROR(vd, VVD_EOS);
+ if (os_fread(vd->fd, &vd->vdi->hdr, sizeof(VDI_HDR)))
+ return VDISK_ERROR(vd, VVD_EOS);
+ if (vd->vdi->hdr.magic != VDI_HEADER_MAGIC)
+ return VDISK_ERROR(vd, VVD_EOS);
+
+ switch (vd->vdi->hdr.majorver) { // Use latest major version natively
+ case 1: // v1.1
+ if (os_fread(vd->fd, &vd->vdi->v1, sizeof(VDI_HEADERv1)))
+ return VDISK_ERROR(vd, VVD_EOS);
+ break;
+ /*case 0:
+ if (os_fread(vd->fd, &vd->vdi->v0, sizeof(VDI_HEADERv0)))
+ return VDISK_ERROR(vd, VVD_EOS);
+ break;*/
+ default:
+ return VDISK_ERROR(vd, VVD_EVDVERSION);
+ }
+
+ switch (vd->vdi->v1.type) {
+ case VDI_DISK_DYN:
+ case VDI_DISK_FIXED: break;
+ default:
+ return VDISK_ERROR(vd, VVD_EVDTYPE);
+ }
+
+ // Allocation table
+
+ //TODO: Consider if this is an error (or warning)
+ if (vd->vdi->v1.blk_size == 0)
+ vd->vdi->v1.blk_size = VDI_BLOCKSIZE;
+ if (os_fseek(vd->fd, vd->vdi->v1.offBlocks, SEEK_SET))
+ return VDISK_ERROR(vd, VVD_EOS);
+
+ int bsize = vd->vdi->v1.blk_total << 2; // * sizeof(u32)
+ if ((vd->vdi->in.offsets = malloc(bsize)) == NULL)
+ return VDISK_ERROR(vd, VVD_ENOMEM);
+ if (os_fread(vd->fd, vd->vdi->in.offsets, bsize))
+ return VDISK_ERROR(vd, VVD_EOS);
+
+ // Internals / calculated values
+
+ vd->capacity = vd->vdi->v1.capacity;
+ vd->vdi->in.mask = vd->vdi->v1.blk_size - 1;
+ vd->vdi->in.shift = fpow2(vd->vdi->v1.blk_size);
+
+ // Function pointers
+
+ vd->cb.lba_read = vdisk_vdi_read_sector;
+
+ return 0;
+}
+
+int vdisk_vdi_create(VDISK *vd, uint64_t capacity, uint32_t flags) {
+ if ((vd->meta = malloc(VDI_META_ALLOC)) == NULL)
+ return VDISK_ERROR(vd, VVD_ENOMEM);
+
+ if (capacity == 0)
+ return VDISK_ERROR(vd, VVD_EVDMISC);
+
+ uint32_t bcount = capacity / VDI_BLOCKSIZE;
+
+ if ((vd->vdi->in.offsets = malloc(bcount << 2)) == NULL)
+ return VDISK_ERROR(vd, VVD_ENOMEM);
+
+ vd->format = VDISK_FORMAT_VDI;
+
+ // Pre-header
+
+ strcpy(vd->vdi->hdr.signature, VDI_SIGNATURE);
+ vd->vdi->hdr.magic = VDI_HEADER_MAGIC;
+ vd->vdi->hdr.majorver = 1;
+ vd->vdi->hdr.minorver = 1;
+
+ // Header
+
+ vd->vdi->v1.blk_alloc = 0;
+ vd->vdi->v1.blk_extra = 0;
+ vd->vdi->v1.blk_size = VDI_BLOCKSIZE;
+ vd->vdi->v1.cbSector = vd->vdi->v1.LegacyGeometry.cbSector = 512;
+ vd->vdi->v1.cCylinders =
+ vd->vdi->v1.cHeads =
+ vd->vdi->v1.cSectors =
+ vd->vdi->v1.LegacyGeometry.cCylinders =
+ vd->vdi->v1.LegacyGeometry.cHeads =
+ vd->vdi->v1.LegacyGeometry.cSectors = 0;
+ vd->vdi->v1.capacity = capacity;
+ vd->vdi->v1.fFlags = 0;
+ vd->vdi->v1.hdrsize = (uint32_t)sizeof(VDI_HEADERv1);
+ vd->vdi->v1.offBlocks = VDI_BLOCKSIZE;
+ vd->vdi->v1.offData = VDI_BLOCKSIZE * 2;
+ vd->vdi->v1.blk_total = 0;
+ vd->vdi->v1.type = VDI_DISK_DYN;
+ vd->vdi->v1.u32Dummy = 0; // Always
+ memset(vd->vdi->v1.szComment, 0, VDI_COMMENT_SIZE);
+ memset(&vd->vdi->v1.uuidCreate, 0, 16);
+ memset(&vd->vdi->v1.uuidLinkage, 0, 16);
+ memset(&vd->vdi->v1.uuidModify, 0, 16);
+ memset(&vd->vdi->v1.uuidParentModify, 0, 16);
+
+ // Data
+
+ uint32_t *offsets = vd->vdi->in.offsets;
+ uint8_t *buffer;
+ uint32_t blk_total = vd->vdi->v1.blk_total;
+
+ switch (flags & VDISK_CREATE_TYPE_MASK) {
+ case VDISK_CREATE_TYPE_DYNAMIC:
+ vd->vdi->v1.type = VDI_DISK_DYN;
+ for (size_t i = 0; i < blk_total; ++i)
+ offsets[i] = VDI_BLOCK_ZERO;
+ break;
+ case VDISK_CREATE_TYPE_FIXED:
+ vd->vdi->v1.type = VDI_DISK_FIXED;
+ if ((buffer = calloc(1, vd->vdi->v1.blk_size)) == NULL)
+ return VDISK_ERROR(vd, VVD_ENOMEM);
+ os_fseek(vd->fd, vd->vdi->v1.offData, SEEK_SET);
+ for (size_t i = 0; i < blk_total; ++i) {
+ offsets[i] = VDI_BLOCK_FREE;
+ os_fwrite(vd->fd, buffer, vd->vdi->v1.blk_size);
+ }
+ break;
+ default:
+ return VDISK_ERROR(vd, VVD_EVDTYPE);
+ }
+
+ return 0;
+}
+
+//
+// vdisk_vdi_read_sector
+//
+
+int vdisk_vdi_read_sector(VDISK *vd, void *buffer, uint64_t index) {
+ uint64_t offset = SECTOR_TO_BYTE(index); // Byte offset
+ size_t bi = offset >> vd->vdi->in.shift;
+
+ if (bi >= vd->vdi->v1.blk_total) // out of bounds
+ return VDISK_ERROR(vd, VVD_EVDBOUND);
+
+ uint32_t block = vd->vdi->in.offsets[bi];
+ switch (block) {
+ case VDI_BLOCK_ZERO: //TODO: Should this be zero'd too?
+ return VDISK_ERROR(vd, VVD_EVDUNALLOC);
+ case VDI_BLOCK_FREE:
+ memset(buffer, 0, 512);
+ return 0;
+ }
+
+ offset = vd->vdi->v1.offData +
+ ((uint64_t)block * vd->vdi->v1.blk_size) +
+ (offset & vd->vdi->in.mask);
+
+#ifdef TRACE
+ printf("%s: lba=%" PRId64 " -> offset=0x%" PRIX64 "\n", __func__, index, offset);
+#endif
+
+ if (os_fseek(vd->fd, offset, SEEK_SET))
+ return VDISK_ERROR(vd, VVD_EOS);
+ if (os_fread(vd->fd, buffer, 512))
+ return VDISK_ERROR(vd, VVD_EOS);
+
+ return 0;
+}
+
+//
+// vdisk_vdi_compact
+//
+
+int vdisk_vdi_compact(VDISK *vd) {
+ if (vd->vdi->hdr.majorver != 1)
+ return VDISK_ERROR(vd, VVD_EVDVERSION);
+ if (vd->vdi->v1.type != VDI_DISK_DYN)
+ return VDISK_ERROR(vd, VVD_EVDTYPE);
+
+ return VDISK_ERROR(vd, VVD_EVDTODO);
+ /*uint32_t *blks2; // back resolving array
+ uint32_t bk_alloc; // blocks allocated (alt)
+
+ // 1. Allocate block array for back resolving.
+
+ uint64_t fsize; // file size
+ if (os_fsize(vd->fd, &fsize))
+ return VDISK_ERROR(vd, VVD_EVDMISC);
+
+ VDI_HEADERv1 vdi = vd->vdi->v1;
+ VDI_INTERNALS in = vd->vdi->in;
+
+ // This verifies that there are actually data blocks available
+ bk_alloc = (uint32_t)((fsize - vdi.offData - vdi.offBlocks) >> in.shift);
+ if (bk_alloc == 0 || vdi.blk_alloc == 0)
+ return 0;
+
+ blks2 = malloc(bk_alloc << 2);
+ if (blks2 == NULL)
+ return VDISK_ERROR(vd, VVD_ENOMEM);
+ for (uint32_t i; i < bk_alloc; ++i)
+ blks2[i] = VDI_BLOCK_FREE;
+
+ uint32_t d = 0;
+ uint32_t blk_index = 0;
+ uint32_t *blks = in.offsets;
+
+ // 2. Check and fix allocation errors before compacting
+
+ for (; blk_index < vdi.blk_total; ++blk_index) {
+ uint32_t bi = blks[blk_index]; // block index
+ if (bi >= VDI_BLOCK_FREE) {
+ continue;
+ }
+
+ if (bi < vdi.blk_alloc) {
+ if (blks[bi] == VDI_BLOCK_FREE) {
+ blks[bi] = blk_index;
+ } else {
+ blks[bi] = VDI_BLOCK_FREE;
+ //TODO: Update header once manipulating source
+ //rc = vdiUpdateBlockInfo(pImage, i);
+ //vdisk_write_block_at(vd, buffer, i, d++);
+ }
+ } else {
+ blks[bi] = VDI_BLOCK_FREE;
+ //TODO: Update header once manipulating source
+ //blocks[bi] = VDI_BLOCK_FREE;
+ //vdisk_write_block_at(vd, buffer, i, d++);
+ }
+ }
+
+ // 3. Find redundant information and update the block pointers accordingly
+
+
+
+ // 4. Fill bubbles with other data if available
+
+// for (i = 0; o < vd->vdiold.blk_alloc
+
+ // 5. Update fields in-memory and on-disk
+
+ //return 0;*/
+}
diff --git a/src/vdisk/fmt/vdi.h b/src/vdisk/fmt/vdi.h
new file mode 100644
index 0000000..391059e
--- /dev/null
+++ b/src/vdisk/fmt/vdi.h
@@ -0,0 +1,123 @@
+/**
+ * VDI: Virtualbox Disk
+ *
+ * Little-endian
+ *
+ * Source: https://forums.virtualbox.org/viewtopic.php?t=8046
+ */
+
+#include
+#include "utils/uid.h"
+
+#define VDI_SIGNATURE "<<< VirtualBox Disk Image >>>\n"
+#define VDI_SIGNATURE_VBOX "<<< Oracle VM VirtualBox Disk Image >>>\n"
+#define VDI_SIGNATURE_OLDER "<<< InnoTek VirtualBox Disk Image >>>\n"
+#define VDI_SIGNATURE_QEMU "<<< QEMU VM Disk Image >>>\n"
+
+/**
+ * Block marked as free is not allocated in image file, read from this
+ * block may returns any random data.
+ */
+static const uint32_t VDI_BLOCK_FREE = 0xffffffff;
+
+/**
+ * Block marked as zero is not allocated in image file, read from this
+ * block returns zeroes. May be also known as "discarded".
+ */
+static const uint32_t VDI_BLOCK_ZERO = 0xfffffffe;
+
+#define VDI_IS_ALLOCATED(x) (x < VDI_BLOCK_FREE)
+
+enum {
+ VDI_HEADER_MAGIC = 0xBEDA107F,
+ VDI_SIGNATURE_SIZE = 64,
+ VDI_COMMENT_SIZE = 256,
+
+ VDI_DISK_DYN = 1,
+ VDI_DISK_FIXED = 2,
+ VDI_DISK_UNDO = 3,
+ VDI_DISK_DIFF = 4,
+
+ VDI_BLOCKSIZE = 1048576, // Default block size, 1 MiB
+};
+
+typedef struct {
+ uint32_t cCylinders;
+ uint32_t cHeads;
+ uint32_t cSectors;
+ uint32_t cbSector;
+} VDI_DISKGEOMETRY;
+
+typedef struct {
+ char signature[64]; // Typically starts with "<<< "
+ uint32_t magic;
+ uint16_t majorver;
+ uint16_t minorver;
+} VDI_HDR;
+
+typedef struct { // v0.0
+ uint32_t type;
+ uint32_t fFlags;
+ uint8_t szComment[VDI_COMMENT_SIZE];
+ VDI_DISKGEOMETRY LegacyGeometry;
+ uint64_t disksize;
+ uint32_t blocksize;
+ uint32_t blockstotal;
+ uint32_t blocksalloc;
+ UID uuidCreate;
+ UID uuidModify;
+ UID uuidLinkage;
+} VDI_HEADERv0;
+
+typedef struct { // v1.1
+ uint32_t hdrsize;
+ uint32_t type;
+ uint32_t fFlags;
+ uint8_t szComment[VDI_COMMENT_SIZE];
+ uint32_t offBlocks; // Byte offset to BAT
+ uint32_t offData; // Byte offset to first block
+ VDI_DISKGEOMETRY LegacyGeometry;
+ uint32_t u32Dummy; // Used to be translation value for geometry
+ uint64_t capacity;
+ uint32_t blk_size; // Block size in bytes
+ uint32_t blk_extra;
+ uint32_t blk_total; // Total amount of blocks
+ uint32_t blk_alloc;
+ UID uuidCreate;
+ UID uuidModify;
+ UID uuidLinkage;
+ UID uuidParentModify;
+ uint32_t cCylinders; // v1.1
+ uint32_t cHeads; // v1.1
+ uint32_t cSectors; // v1.1
+ uint32_t cbSector; // v1.1
+// uint8_t pad[40];
+} VDI_HEADERv1;
+
+typedef struct {
+ uint32_t *offsets; // Offset table
+ uint32_t mask; // Block bit mask
+ uint32_t shift; // Block shift positions
+ uint16_t majorver;
+} VDI_INTERNALS;
+
+typedef struct {
+ VDI_HDR hdr;
+ union {
+ VDI_HEADERv0 v0;
+ VDI_HEADERv1 v1;
+ };
+ VDI_INTERNALS in;
+} VDI_META;
+
+static const uint32_t VDI_META_ALLOC = sizeof(VDI_META);
+
+struct VDISK;
+
+int vdisk_vdi_open(struct VDISK *vd, uint32_t flags, uint32_t internal);
+
+int vdisk_vdi_create(struct VDISK *vd, uint64_t capacity, uint32_t flags);
+
+int vdisk_vdi_read_sector(struct VDISK *vd, void *buffer, uint64_t index);
+
+int vdisk_vdi_compact(struct VDISK *vd);
diff --git a/src/vdisk/fmt/vhd.c b/src/vdisk/fmt/vhd.c
new file mode 100644
index 0000000..e52ac5b
--- /dev/null
+++ b/src/vdisk/fmt/vhd.c
@@ -0,0 +1,166 @@
+#include "vdisk/vdisk.h"
+#include "utils/bin.h"
+#include "utils/platform.h"
+#include "utils/bin.h"
+#ifdef TRACE
+#include
+#include
+#endif
+
+//
+// vdisk_vhd_open
+//
+
+int vdisk_vhd_open(VDISK *vd, uint32_t flags, uint32_t internal) {
+ if ((vd->vmdk = malloc(VHD_META_ALLOC)) == NULL)
+ return VDISK_ERROR(vd, VVD_ENOMEM);
+
+ if (internal & 2) {
+ if (os_fseek(vd->fd, -512, SEEK_END))
+ return VDISK_ERROR(vd, VVD_EOS);
+ }
+
+ if (os_fread(vd->fd, &vd->vhd->hdr, sizeof(VHD_HDR)))
+ return VDISK_ERROR(vd, VVD_EOS);
+ if (vd->vhd->hdr.magic != VHD_MAGIC)
+ return VDISK_ERROR(vd, VVD_EVDMAGIC);
+
+#if ENDIAN_LITTLE
+ vd->vhd->hdr.major = bswap16(vd->vhd->hdr.major);
+#endif
+
+ if (vd->vhd->hdr.major != 1)
+ return VDISK_ERROR(vd, VVD_EVDVERSION);
+
+#if ENDIAN_LITTLE
+ vd->vhd->hdr.type = bswap32(vd->vhd->hdr.type);
+#endif
+
+ switch (vd->vhd->hdr.type) {
+ case VHD_DISK_DIFF:
+ case VHD_DISK_DYN:
+ case VHD_DISK_FIXED: break;
+ default:
+ return VDISK_ERROR(vd, VVD_EVDTYPE);
+ }
+
+#if ENDIAN_LITTLE
+ vd->vhd->hdr.features = bswap32(vd->vhd->hdr.features);
+ vd->vhd->hdr.minor = bswap16(vd->vhd->hdr.minor);
+ vd->vhd->hdr.offset = bswap64(vd->vhd->hdr.offset);
+ vd->vhd->hdr.timestamp = bswap32(vd->vhd->hdr.timestamp);
+ vd->vhd->hdr.creator_major = bswap16(vd->vhd->hdr.creator_major);
+ vd->vhd->hdr.creator_minor = bswap16(vd->vhd->hdr.creator_minor);
+// vd->vhd->creator_os = bswap32(vd->vhd->creator_os);
+ vd->vhd->hdr.size_original = bswap64(vd->vhd->hdr.size_original);
+ vd->vhd->hdr.size_current = bswap64(vd->vhd->hdr.size_current);
+ vd->vhd->hdr.cylinders = bswap16(vd->vhd->hdr.cylinders);
+ vd->vhd->hdr.checksum = bswap32(vd->vhd->hdr.checksum);
+ uid_swap(&vd->vhd->hdr.uuid);
+#endif
+
+ if (vd->vhd->hdr.type != VHD_DISK_FIXED) {
+ if (os_fseek(vd->fd, vd->vhd->hdr.offset, SEEK_SET))
+ return VDISK_ERROR(vd, VVD_EOS);
+ if (os_fread(vd->fd, &vd->vhd->dyn, sizeof(VHD_DYN_HDR)))
+ return VDISK_ERROR(vd, VVD_EOS);
+ if (vd->vhd->dyn.magic != VHD_DYN_MAGIC)
+ return VDISK_ERROR(vd, VVD_EVDMAGIC);
+
+#if ENDIAN_LITTLE
+ vd->vhd->dyn.data_offset = bswap64(vd->vhd->dyn.data_offset);
+ vd->vhd->dyn.table_offset = bswap64(vd->vhd->dyn.table_offset);
+ vd->vhd->dyn.minor = bswap16(vd->vhd->dyn.minor);
+ vd->vhd->dyn.major = bswap16(vd->vhd->dyn.major);
+ vd->vhd->dyn.max_entries = bswap32(vd->vhd->dyn.max_entries);
+ vd->vhd->dyn.blocksize = bswap32(vd->vhd->dyn.blocksize);
+ vd->vhd->dyn.checksum = bswap32(vd->vhd->dyn.checksum);
+ vd->vhd->dyn.parent_timestamp = bswap32(vd->vhd->dyn.parent_timestamp);
+ uid_swap(&vd->vhd->dyn.parent_uuid);
+
+ for (size_t i = 0; i < 8; ++i) {
+ vd->vhd->dyn.parent_locator[i].code =
+ bswap32(vd->vhd->dyn.parent_locator[i].code);
+ vd->vhd->dyn.parent_locator[i].datasize =
+ bswap32(vd->vhd->dyn.parent_locator[i].datasize);
+ vd->vhd->dyn.parent_locator[i].dataspace =
+ bswap32(vd->vhd->dyn.parent_locator[i].dataspace);
+ vd->vhd->dyn.parent_locator[i].offset =
+ bswap64(vd->vhd->dyn.parent_locator[i].offset);
+ }
+#endif
+
+ if (pow2(vd->vhd->dyn.blocksize) == 0 ||
+ vd->vhd->dyn.max_entries != vd->vhd->hdr.size_original / vd->vhd->dyn.blocksize)
+ return VDISK_ERROR(vd, VVD_EVDMISC);
+
+ vd->vhd->in.mask = vd->vhd->dyn.blocksize - 1;
+ vd->vhd->in.shift = fpow2(vd->vhd->dyn.blocksize);
+
+ if (vd->vhd->dyn.max_entries == 0)
+ return VDISK_ERROR(vd, VVD_EVDMAGIC);
+ if (os_fseek(vd->fd, vd->vhd->dyn.table_offset, SEEK_SET))
+ return VDISK_ERROR(vd, VVD_EOS);
+
+ int batsize = vd->vhd->dyn.max_entries << 2; // "* 4"
+ if ((vd->vhd->in.offsets = malloc(batsize)) == NULL)
+ return VDISK_ERROR(vd, VVD_ENOMEM);
+ if (os_fread(vd->fd, vd->vhd->in.offsets, batsize))
+ return VDISK_ERROR(vd, VVD_EOS);
+#if ENDIAN_LITTLE
+ for (size_t i = 0; i < vd->vhd->dyn.max_entries; ++i)
+ vd->vhd->in.offsets[i] = bswap32(vd->vhd->in.offsets[i]);
+#endif
+ vd->cb.lba_read = vdisk_vhd_dyn_read_lba;
+ } else { // Fixed
+ vd->cb.lba_read = vdisk_vhd_fixed_read_lba;
+ }
+
+ vd->capacity = vd->vhd->hdr.size_original;
+ return 0;
+}
+
+//
+// vdisk_vhd_fixed_read_lba
+//
+
+int vdisk_vhd_fixed_read_lba(VDISK *vd, void *buffer, uint64_t index) {
+ uint64_t offset = SECTOR_TO_BYTE(index); // Byte offset
+
+ if (os_fseek(vd->fd, offset, SEEK_SET))
+ return VDISK_ERROR(vd, VVD_EOS);
+ if (os_fread(vd->fd, buffer, 512))
+ return VDISK_ERROR(vd, VVD_EOS);
+
+ return 0;
+}
+
+//
+// vdisk_vhd_dyn_read_lba
+//
+
+int vdisk_vhd_dyn_read_lba(VDISK *vd, void *buffer, uint64_t index) {
+ uint64_t offset = SECTOR_TO_BYTE(index);
+ uint32_t bi = (uint32_t)(offset >> vd->vhd->in.shift);
+#ifdef TRACE
+ printf("%s: bi=%u\n", __func__, bi);
+#endif
+ if (bi >= vd->vhd->dyn.max_entries)
+ return VDISK_ERROR(vd, VVD_EVDBOUND);
+
+ uint32_t block = vd->vhd->in.offsets[bi];
+ if (block == VHD_BLOCK_UNALLOC) // Unallocated
+ return VDISK_ERROR(vd, VVD_EVDUNALLOC);
+
+ uint64_t base = SECTOR_TO_BYTE(block) + 512;
+ offset = base + (offset & vd->vhd->in.mask);
+#ifdef TRACE
+ printf("%s: block=%u offset=%" PRIu64 "\n", __func__, block, offset);
+#endif
+ if (os_fseek(vd->fd, offset, SEEK_SET))
+ return VDISK_ERROR(vd, VVD_EOS);
+ if (os_fread(vd->fd, buffer, 512))
+ return VDISK_ERROR(vd, VVD_EOS);
+
+ return 0;
+}
diff --git a/src/vdisk/fmt/vhd.h b/src/vdisk/fmt/vhd.h
new file mode 100644
index 0000000..8d1addf
--- /dev/null
+++ b/src/vdisk/fmt/vhd.h
@@ -0,0 +1,97 @@
+/**
+ * VHD: (Connectix) Virtual Hard Disk
+ */
+
+#include
+
+#define VHDMAGIC "conectix"
+#define VHD_MAGIC 0x78697463656E6F63 // "conectix"
+#define VHD_DYN_MAGIC 0x6573726170737863 // "cxsparse"
+#define VHD_OS_WIN 0x6B326957 // "Wi2k"
+#define VHD_OS_MAC 0x2063614D // "Mac "
+
+enum {
+ VHD_DISK_NONE = 0,
+ VHD_DISK_RES1 = 1,
+ VHD_DISK_FIXED = 2,
+ VHD_DISK_DYN = 3,
+ VHD_DISK_DIFF = 4,
+ VHD_DISK_RES2 = 5,
+ VHD_DISK_RES3 = 6
+};
+
+enum {
+ VHD_BLOCK_UNALLOC = -1, // Block not allocated on disk
+ VHD_FEAT_TEMP = 1,
+ VHD_FEAT_RES = 2 // reserved, but always set
+};
+
+typedef struct { // v1
+ uint64_t magic; // "conectix"
+ uint32_t features;
+ uint16_t major;
+ uint16_t minor;
+ uint64_t offset;
+ uint32_t timestamp;
+ char creator_app[4];
+ uint16_t creator_major;
+ uint16_t creator_minor;
+ uint32_t creator_os;
+ uint64_t size_original; // Capacity in bytes
+ uint64_t size_current;
+ uint16_t cylinders;
+ uint8_t heads;
+ uint8_t sectors;
+ uint32_t type;
+ uint32_t checksum;
+ UID uuid;
+ uint8_t savedState;
+ uint8_t reserved[427];
+} VHD_HDR;
+
+typedef struct {
+ uint32_t code;
+ uint32_t dataspace;
+ uint32_t datasize;
+ uint32_t res;
+ uint64_t offset;
+} VHD_PARENT_LOCATOR;
+
+typedef struct { // v1
+ uint64_t magic;
+ uint64_t data_offset;
+ uint64_t table_offset;
+ uint16_t minor;
+ uint16_t major;
+ uint32_t max_entries; // For BAT
+ uint32_t blocksize; // In bytes
+ uint32_t checksum;
+ UID parent_uuid; // UUID
+ uint32_t parent_timestamp;
+ uint32_t res;
+ uint16_t parent_name[256]; // UTF-16
+ VHD_PARENT_LOCATOR parent_locator[8];
+ uint8_t res1[256];
+} VHD_DYN_HDR;
+
+typedef struct {
+ uint32_t *offsets;
+ uint32_t mask;
+ uint32_t shift;
+} VHD_INTERNALS;
+
+typedef struct {
+ VHD_HDR hdr;
+ VHD_DYN_HDR dyn;
+ VHD_INTERNALS in;
+} VHD_META;
+
+static const uint32_t VHD_META_ALLOC = sizeof(VHD_META);
+
+struct VDISK;
+
+int vdisk_vhd_open(struct VDISK *vd, uint32_t flags, uint32_t internal);
+
+int vdisk_vhd_dyn_read_lba(struct VDISK *vd, void *buffer, uint64_t index);
+
+int vdisk_vhd_fixed_read_lba(struct VDISK *vd, void *buffer, uint64_t index);
diff --git a/src/vdisk/fmt/vhdx.c b/src/vdisk/fmt/vhdx.c
new file mode 100644
index 0000000..d4c6635
--- /dev/null
+++ b/src/vdisk/fmt/vhdx.c
@@ -0,0 +1,61 @@
+#include "vdisk/vdisk.h"
+#include "utils/bin.h"
+#include "utils/platform.h"
+#include
+
+int vdisk_vhdx_open(VDISK *vd, uint32_t flags, uint32_t internal) {
+ assert(0); //TODO: Continue VHDX
+
+ if ((vd->meta = malloc(VHDX_META_ALLOC)) == NULL)
+ return VDISK_ERROR(vd, VVD_ENOMEM);
+
+ //TODO: Check both headers and regions before doing an error
+
+ //
+ // Headers
+ //
+
+ if (os_fread(vd->fd, &vd->vhdx->hdr, sizeof(VHDX_HDR)))
+ return VDISK_ERROR(vd, VVD_EOS);
+ if (vd->vhdx->hdr.magic != VHDX_MAGIC)
+ return VDISK_ERROR(vd, VVD_EVDMAGIC);
+
+ if (os_fseek(vd->fd, VHDX_HEADER1_LOC, SEEK_SET))
+ return VDISK_ERROR(vd, VVD_EOS);
+ if (os_fread(vd->fd, &vd->vhdx->v1, sizeof(VHDX_HEADER1)))
+ return VDISK_ERROR(vd, VVD_EOS);
+
+ if (os_fseek(vd->fd, VHDX_HEADER2_LOC, SEEK_SET))
+ return VDISK_ERROR(vd, VVD_EOS);
+ if (os_fread(vd->fd, &vd->vhdx->v1_2, sizeof(VHDX_HEADER1)))
+ return VDISK_ERROR(vd, VVD_EOS);
+
+ if (vd->vhdx->v1.magic != VHDX_HDR1_MAGIC || vd->vhdx->v1_2.magic != VHDX_HDR1_MAGIC)
+ return VDISK_ERROR(vd, VVD_EVDMAGIC);
+ if (vd->vhdx->v1.version != 1 || vd->vhdx->v1_2.version != 1)
+ return VDISK_ERROR(vd, VVD_EVDVERSION);
+
+ //
+ // Regions
+ //
+
+ if (os_fseek(vd->fd, VHDX_REGION1_LOC, SEEK_SET))
+ return VDISK_ERROR(vd, VVD_EOS);
+ if (os_fread(vd->fd, &vd->vhdx->reg, sizeof(VHDX_REGION_HDR)))
+ return VDISK_ERROR(vd, VVD_EOS);
+ if (vd->vhdx->reg.magic != VHDX_REGION_MAGIC)
+ return VDISK_ERROR(vd, VVD_EVDMAGIC);
+
+ //
+ //TODO: Log
+ //
+
+ //
+ // BAT
+ //
+
+ // Chunk ratio
+ //(8388608 * ) / // 8 KiB * 512
+
+ return 0;
+}
diff --git a/src/vdisk/fmt/vhdx.h b/src/vdisk/fmt/vhdx.h
new file mode 100644
index 0000000..726f999
--- /dev/null
+++ b/src/vdisk/fmt/vhdx.h
@@ -0,0 +1,145 @@
+/**
+ * VHDX: (Microsoft) Virtual Hard Disk eXtended
+ *
+ * Little Endian
+ *
+ * Source: MS-VHDX v20160714
+ */
+
+#include
+#include "utils/uid.h"
+
+static const uint64_t VHDX_MAGIC = 0x656C696678646876; // "vhdxfile"
+static const uint32_t VHDX_HDR1_MAGIC = 0x64616568; // "head"
+static const uint32_t VHDX_REGION_MAGIC = 0x69676572; // "regi"
+static const uint32_t VHDX_LOG_HDR_MAGIC = 0x65676F6C; // "loge"
+static const uint32_t VHDX_LOG_ZERO_MAGIC = 0x6F72657A; // "zero"
+static const uint32_t VHDX_LOG_DESC_MAGIC = 0x63736564; // "desc"
+static const uint32_t VHDX_LOG_DATA_MAGIC = 0x61746164; // "data"
+static const uint64_t VHDX_METADATA_MAGIC = 0x617461646174656D; // "metadata"
+
+enum {
+ VHDX_HEADER1_LOC = 64 * 1024, // 64 KiB
+ VHDX_HEADER2_LOC = VHDX_HEADER1_LOC * 2, // 128 KiB
+ VHDX_REGION1_LOC = VHDX_HEADER1_LOC * 3, // 192 KiB
+ VHDX_REGION2_LOC = VHDX_HEADER1_LOC * 4, // 256 KiB
+ VDHX_LOG_ALIGN = 4 * 1024, // 4 KiB
+};
+
+typedef struct {
+ uint64_t magic;
+ union {
+ uint8_t u8creator[512];
+ uint16_t u16creator[256];
+ };
+} VHDX_HDR;
+
+typedef struct {
+ uint32_t magic;
+ uint32_t crc32;
+ uint32_t seqnumber;
+ UID filewrite;
+ UID datawrite;
+ UID log;
+ uint16_t logversion;
+ uint16_t version;
+ uint32_t logsize;
+ uint64_t logoffset;
+} VHDX_HEADER1;
+
+typedef struct {
+ uint32_t magic;
+ uint32_t crc32;
+ uint32_t count;
+ uint32_t res;
+} VHDX_REGION_HDR;
+
+typedef struct {
+ // BAT 2DC27766-F623-4200-9D64-115E9BFD4A08 required
+ // METADATA 8B7CA206-4790-4B9A-B8FE-575F050F886E required
+ UID guid;
+ uint64_t offset;
+ uint32_t length;
+ uint32_t required;
+} VHDX_REGION_ENTRY;
+
+typedef struct {
+ uint32_t magic;
+ uint32_t crc32;
+ uint32_t count;
+ uint32_t tail;
+ uint64_t sequence;
+ uint32_t desccount;
+ uint32_t res;
+ UID guid;
+ uint64_t flushedoffset;
+ uint64_t lastoffset;
+} VHDX_LOG_HDR;
+
+typedef struct {
+ uint32_t magic;
+ uint32_t trail; // resolution
+ uint64_t leading; // Multiple of 4 KiB, length
+ uint64_t offset; // Multiple of 4 KiB
+ uint64_t sequence;
+} VDHX_LOG_DESC;
+
+typedef struct {
+ uint32_t magic;
+ union {
+ uint8_t cdata[4096]; // Cluster
+ struct {
+ uint32_t sequenceh;
+ union {
+ uint8_t data[4084]; // as per doc
+ struct {
+ uint64_t lead;
+ uint8_t rdata[4068]; // The real data
+ uint64_t trail;
+ };
+ };
+ uint32_t sequencel;
+ };
+ };
+} VHDX_LOG_DATA;
+
+typedef struct {
+ uint64_t magic;
+ uint16_t res;
+ uint16_t count;
+ uint8_t res2[20];
+} VHDX_METADATA_HDR;
+
+typedef struct {
+ // File Parameters CAA16737-FA36-4D43-B3B6-33F0AA44E76B
+ // Virtual Disk Size 2FA54224-CD1B-4876-B211-5DBED83BF4B8
+ // "Page 83 Data" BECA12AB-B2E6-4523-93EF-C309E000C746
+ // Logical Sector Size 8141BF1D-A96F-4709-BA47-F233A8FAAB5F
+ // Logical Sector Size CDA348C7-445D-4471-9CC9-E9885251C556
+ // Parent Locator A8D35F2D-B30B-454D-ABF7-D3D84834AB0C
+ UID type; // itemID GUID
+ uint32_t offset;
+ uint32_t length;
+ uint32_t flags; // ...plus 2 bits? what the hell?
+} VHDX_METADATA_ENTRY;
+
+typedef struct {
+
+} VHDX_INTERNALS;
+
+typedef struct {
+ VHDX_HDR hdr;
+ VHDX_HEADER1 v1;
+ VHDX_HEADER1 v1_2;
+ VHDX_REGION_HDR reg;
+ VHDX_REGION_HDR reg2;
+ VHDX_LOG_HDR log;
+ VHDX_METADATA_HDR meta;
+ VHDX_INTERNALS in;
+} VHDX_META;
+
+static const uint32_t VHDX_META_ALLOC = sizeof(VHDX_META);
+
+struct VDISK;
+
+int vdisk_vhdx_open(struct VDISK *vd, uint32_t flags, uint32_t internal);
diff --git a/src/vdisk/fmt/vmdk.c b/src/vdisk/fmt/vmdk.c
new file mode 100644
index 0000000..5ac02b1
--- /dev/null
+++ b/src/vdisk/fmt/vmdk.c
@@ -0,0 +1,46 @@
+#include "vdisk/vdisk.h"
+#include "utils/bin.h"
+#include "utils/platform.h"
+
+int vdisk_vmdk_open(VDISK *vd, uint32_t flags, uint32_t internal) {
+ if ((vd->vmdk = malloc(VMDK_META_ALLOC)) == NULL)
+ return VDISK_ERROR(vd, VVD_ENOMEM);
+
+ if (os_fread(vd->fd, &vd->vmdk->hdr, sizeof(VMDK_HDR)))
+ return VDISK_ERROR(vd, VVD_EOS);
+ if (vd->vmdk->hdr.version != 1)
+ return VDISK_ERROR(vd, VVD_EVDVERSION);
+ if (vd->vmdk->hdr.grainSize < 8 || // < 4KiB
+ vd->vmdk->hdr.grainSize > 128 || // > 64KiB
+ pow2(vd->vmdk->hdr.grainSize) == 0)
+ return VDISK_ERROR(vd, VVD_EVDMISC);
+
+ vd->capacity = SECTOR_TO_BYTE(vd->vmdk->hdr.capacity);
+
+ vd->vmdk->in.mask = vd->vmdk->hdr.grainSize - 1;
+ vd->vmdk->in.shift = fpow2((uint32_t)vd->vmdk->hdr.grainSize);
+ vd->vmdk->in.overhead = SECTOR_TO_BYTE(vd->vmdk->hdr.overHead);
+
+ vd->cb.lba_read = vdisk_vmdk_sparse_read_lba;
+
+ return 0;
+}
+
+int vdisk_vmdk_sparse_read_lba(VDISK *vd, void *buffer, uint64_t index) {
+
+ uint64_t offset = SECTOR_TO_BYTE(index); // Byte offset
+
+ if (offset >= vd->capacity)
+ return VDISK_ERROR(vd, VVD_EVDMISC);
+
+ //bi = offset / SECTOR_TO_BYTE(vd->vmdkold.grainSize);
+ //TODO: Work with the grainSize
+ offset += vd->vmdk->in.overhead;
+
+ if (os_fseek(vd->fd, offset, SEEK_SET))
+ return VDISK_ERROR(vd, VVD_EOS);
+ if (os_fread(vd->fd, buffer, 512))
+ return VDISK_ERROR(vd, VVD_EOS);
+
+ return 0;
+}
diff --git a/src/vdisk/fmt/vmdk.h b/src/vdisk/fmt/vmdk.h
new file mode 100644
index 0000000..9a081f0
--- /dev/null
+++ b/src/vdisk/fmt/vmdk.h
@@ -0,0 +1,91 @@
+/**
+ * VMware Disk image
+ *
+ * Little-endian
+ *
+ * +-------------+
+ * | 0 | 1 | ... | Grain Directory Entries
+ * +-------------+
+ * |
+ * +---
+ * | 1 Grain Table Entries
+ *
+ * Sources:
+ * - VMware Virtual Disks Virtual Disk Format 1.1
+ * - VMware Virtual Disk Format 5.0
+ */
+
+#include
+
+enum {
+ VMDK_F_VALID_NL = 0x1, // Valid newline detection
+ VMDK_F_REDUNDANT_TABLE = 0x2, // Redundant grain table will be used
+ VMDK_F_ZEROED_GTE = 0x4, // Zeroed-grain GTE will be used
+ VDMK_F_COMPRESSED = 0x10000, // Grains are compressed
+ VMDK_F_MARKERS = 0x20000, // Markers used
+
+ VMDK_C_NONE = 0, // No compression is used
+ VMDK_C_DEFLATE = 1, // DEFLATE (RFC 1951) is used
+
+ VMDK_2G_SPLIT_SIZE = 2047 * 1024 * 1024, // grainSize*sectorSize = 2 GiB
+ VMDK_TEXT_LENGTH = 10 * 1024, // 10K text overhead buffer
+ VMDK_GRAINSIZE_DEFAULT = 64 * 1024 // Default being 64K
+};
+enum {
+ VMDK_MARKER_EOS = 0, // end-of-stream
+ VMDK_MARKER_GT = 1, // grain table marker
+ VMDK_MARKER_GD = 2, // grain directory marker
+ VMDK_MARKER_FOOTER = 3, // footer marker
+
+ VMDK_DISK_DYN = 1, // (Internal) Sparse
+ VMDK_DISK_FIXED = 2, // (Internal) Monolithic
+};
+
+typedef struct {
+ uint32_t magicNumber;
+ uint32_t version; // v1 or v2
+ uint32_t flags; // See VMDK_F_* values
+ uint64_t capacity; // Disk capacity in sectors
+ uint64_t grainSize; // Block size in sectors
+ uint64_t descriptorOffset; // If set, embedded descriptor offset in sectors
+ uint64_t descriptorSize; // If set, embedded descriptor size in sectors
+ uint32_t numGTEsPerGT; // Number of entries in a grain table, typically 512
+ uint64_t rgdOffset; // Offset to level 0 redundant metadata in sectors
+ uint64_t gdOffset; // Offset to level 0 metadata (grain directory) in sectors
+ uint64_t overHead; // Offset to data in sectors
+ uint8_t uncleanShutdown; // Acts as a boolean value
+ uint8_t singleEndLineChar; // Typically '\n'
+ uint8_t nonEndLineChar; // Typically ' '
+ uint8_t doubleEndLineChar1; // Typically '\r'
+ uint8_t doubleEndLineChar2; // Typically '\n'
+ uint16_t compressAlgorithm; // See VMDK_C_* values
+ uint8_t pad[433];
+} VMDK_HDR;
+
+typedef struct {
+ uint64_t uSector;
+ uint32_t cbSize;
+ uint32_t uType;
+ uint8_t pad[496];
+} VMDK_MARKER;
+
+typedef struct {
+ uint32_t *l0_offsets; // Grain Directory offsets
+ uint32_t *l1_offsets; // Grain Table offsets
+ uint32_t mask; // Bit offset mask
+ uint32_t shift; // Bit offset shift
+ uint64_t overhead; // data overhead in bytes
+} VMDK_INTERNALS;
+
+typedef struct {
+ VMDK_HDR hdr;
+ VMDK_INTERNALS in;
+} VMDK_META;
+
+static const uint32_t VMDK_META_ALLOC = sizeof(VMDK_META);
+
+struct VDISK;
+
+int vdisk_vmdk_open(struct VDISK *vd, uint32_t flags, uint32_t internal);
+
+int vdisk_vmdk_sparse_read_lba(struct VDISK *vd, void *buffer, uint64_t index);
diff --git a/src/vdisk/phdd.c b/src/vdisk/phdd.c
deleted file mode 100644
index c4fb87c..0000000
--- a/src/vdisk/phdd.c
+++ /dev/null
@@ -1,11 +0,0 @@
-#include "utils.h"
-#include "vdisk.h"
-#include "platform.h"
-#include
-
-int vdisk_phdd_open(VDISK *vd, uint32_t flags, uint32_t internal) {
-
- assert(0);
-
- return 0;
-}
diff --git a/src/vdisk/phdd.h b/src/vdisk/phdd.h
deleted file mode 100644
index e180104..0000000
--- a/src/vdisk/phdd.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/**
- * PHDD: Parallels Hard Disk Drive
- *
- * https://github.com/qemu/qemu/blob/master/docs/interop/parallels.txt
- * VBox/Storage/Parallels.cpp
- */
-
-#include
-
-// Parallels header structure
-typedef struct {
- // Magic header
- char magic[16];
- // Virtual disk version, currently 2
- uint32_t version;
- // (CHS) Heads
- uint32_t heads;
- // (CHS) Cylinders
- uint32_t cylinders;
- // (CHS) Number of sectors per track
- uint32_t sectorsPerTrack;
- // Number of entries in the allocation bitmap.
- uint32_t number_entries;
- // Total number of sectors
- uint32_t sectors;
- // Padding.
-// char Padding[24];
-} PHDD_HDR;
-
-struct VDISK;
-
-int vdisk_phdd_open(struct VDISK *vd, uint32_t flags, uint32_t internal);
diff --git a/src/vdisk/qcow.c b/src/vdisk/qcow.c
deleted file mode 100644
index af2a506..0000000
--- a/src/vdisk/qcow.c
+++ /dev/null
@@ -1,11 +0,0 @@
-#include "utils.h"
-#include "vdisk.h"
-#include "platform.h"
-#include
-
-int vdisk_qcow_open(VDISK *vd, uint32_t flags, uint32_t internal) {
-
- assert(0);
-
- return 0;
-}
diff --git a/src/vdisk/qcow.h b/src/vdisk/qcow.h
deleted file mode 100644
index a0e6529..0000000
--- a/src/vdisk/qcow.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/**
- * QCOW: QEMU Copy-On-Write disk
- *
- * https://github.com/qemu/qemu/blob/master/docs/interop/qcow2.txt
- */
-
-#include
-
-// QCOW header structure
-typedef struct {
- //
- uint32_t magic;
- //
- uint32_t version;
- //
- uint64_t backing_file_offset;
- //
- uint32_t backing_file_size;
- //
- uint32_t cluster_bits;
- // In bytes
- uint64_t size;
- // Encryption method
- uint32_t crypt_method;
- // L1 table size in bytes
- uint32_t l1_size;
- // L1 table offset in bytes
- uint64_t l1_offset;
- //
- uint64_t refcount_table_offset;
- //
- uint32_t refcount_table_clusters;
- //
- uint32_t nb_snapshots;
- //
- uint64_t snapshots_offset;
-} QCOW_HDR;
-
-struct VDISK;
-
-int vdisk_qcow_open(struct VDISK *vd, uint32_t flags, uint32_t internal);
diff --git a/src/vdisk/qed.c b/src/vdisk/qed.c
deleted file mode 100644
index 67b42c4..0000000
--- a/src/vdisk/qed.c
+++ /dev/null
@@ -1,90 +0,0 @@
-#include "vdisk.h"
-#include "utils.h"
-#include "platform.h"
-#include
-
-int vdisk_qed_open(VDISK *vd, uint32_t flags, uint32_t internal) {
- if ((vd->meta = malloc(QED_META_ALLOC)) == NULL)
- return vdisk_i_err(vd, VVD_ENOMEM, __LINE__, __func__);
-
- if (os_fread(vd->fd, &vd->qed->hdr, sizeof(QED_HDR)))
- return vdisk_i_err(vd, VVD_EOS, __LINE__, __func__);
-
- if (vd->qed->hdr.cluster_size < QED_CLUSTER_MIN ||
- vd->qed->hdr.cluster_size > QED_CLUSTER_MAX ||
- pow2(vd->qed->hdr.cluster_size) == 0)
- return vdisk_i_err(vd, VVD_EVDMISC, __LINE__, __func__);
- if (vd->qed->hdr.table_size < QED_TABLE_MIN ||
- vd->qed->hdr.table_size > QED_TABLE_MAX ||
- pow2(vd->qed->hdr.table_size) == 0)
- return vdisk_i_err(vd, VVD_EVDMISC, __LINE__, __func__);
-
- if (vd->qed->hdr.features > QED_FEATS)
- return vdisk_i_err(vd, VVD_EVDMISC, __LINE__, __func__);
-
- // assert(header.image_size <= TABLE_NOFFSETS * TABLE_NOFFSETS * header.cluster_size)
-
- uint32_t table_size = vd->qed->in.tablesize =
- vd->qed->hdr.cluster_size * vd->qed->hdr.table_size;
- uint32_t table_entries = vd->qed->in.entries = table_size / sizeof(uint64_t);
- uint32_t clusterbits = fpow2(vd->qed->hdr.cluster_size);
- uint32_t tablebits = fpow2(table_entries);
-
- if ((vd->qed->in.L1.offsets = malloc(table_size)) == NULL)
- return vdisk_i_err(vd, VVD_ENOMEM, __LINE__, __func__);
- if ((vd->qed->in.L2.offsets = malloc(table_size)) == NULL)
- return vdisk_i_err(vd, VVD_ENOMEM, __LINE__, __func__);
- if (os_fseek(vd->fd, vd->qed->hdr.l1_offset, SEEK_SET))
- return vdisk_i_err(vd, VVD_EOS, __LINE__, __func__);
- if (os_fread(vd->fd, vd->qed->in.L1.offsets, table_size))
- return vdisk_i_err(vd, VVD_EOS, __LINE__, __func__);
-
- // assert(clusterbits + (2 * tablebits) <= 64);
-
- vd->qed->in.mask = vd->qed->hdr.cluster_size - 1;
- vd->qed->in.L2.mask = (table_entries - 1) << clusterbits;
- vd->qed->in.L2.shift = clusterbits;
- vd->qed->in.L1.mask = (table_entries - 1) << (clusterbits + tablebits);
- vd->qed->in.L1.mask = clusterbits + tablebits;
-
- vd->capacity = vd->qed->hdr.capacity;
-
- vd->cb.lba_read = vdisk_qed_read_sector;
-
- return 0;
-}
-
-int vdisk_qed_L2_load(VDISK *vd, uint64_t offset) {
- if (vd->qed->in.L2.current == offset) // L2 already loaded
- return 0;
-
- if (os_fseek(vd->fd, offset, SEEK_SET))
- return vdisk_i_err(vd, VVD_EOS, __LINE__, __func__);
- if (os_fread(vd->fd, vd->qed->in.L2.offsets, vd->qed->in.tablesize))
- return vdisk_i_err(vd, VVD_EOS, __LINE__, __func__);
-
- vd->qed->in.L2.current = offset;
-
- return 0;
-}
-
-int vdisk_qed_read_sector(VDISK *vd, void *buffer, uint64_t index) {
- uint64_t offset = SECTOR_TO_BYTE(index);
-
- uint32_t l1 = (offset >> vd->qed->in.L1.shift) & vd->qed->in.L1.mask;
- uint32_t l2 = (offset >> vd->qed->in.L2.shift) & vd->qed->in.L2.mask;
-
- if (l1 >= vd->qed->in.entries || l2 >= vd->qed->in.entries)
- return vdisk_i_err(vd, VVD_EVDMISC, __LINE__, __func__);
- if (vdisk_qed_L2_load(vd, vd->qed->in.L1.offsets[l1]))
- return vd->err.num;
-
- offset = (uint64_t)vd->qed->in.L2.offsets[l2] + (offset & vd->qed->in.mask);
-
- if (os_fseek(vd->fd, offset, SEEK_SET))
- return vdisk_i_err(vd, VVD_EOS, __LINE__, __func__);
- if (os_fread(vd->fd, buffer, 512))
- return vdisk_i_err(vd, VVD_EOS, __LINE__, __func__);
-
- return 0;
-}
diff --git a/src/vdisk/qed.h b/src/vdisk/qed.h
deleted file mode 100644
index cd4a471..0000000
--- a/src/vdisk/qed.h
+++ /dev/null
@@ -1,120 +0,0 @@
-/**
- * QED: QEMU Enhanced Disk
- *
- * Little-endian format. Features clusters, including the header (cluster0).
- *
- * Layout:
- * +--------+----------+----------+----------+-----+
- * | Header | L1 table | Cluster0 | Cluster1 | ... |
- * +--------+----------+----------+----------+-----+
- *
- * For cluster allocation, there is a 2-level table:
- *
- * +----------+
- * | L1 table | <- Fixed
- * +----------+
- * |
- * +---------+---------+
- * | | |
- * +----------+ +-----+ +----------+
- * | L2 table | | ... | | L2 table | <- Allocated on-demand
- * +----------+ +-----+ +----------+
- * |
- * +------+------+
- * | | |
- * +------+ +-----+ +------+
- * | Data | | ... | | Data | <- Cluster data
- * +------+ +-----+ +------+
- *
- * Both the L1 table and L2 tables are of the same size (table_size * cluster_size).
- * The L1 table holds absolute file offsets to L2 table clusters, which the
- * L2 table holds absolute file offsets to data clusters.
- *
- * https://wiki.qemu.org/Features/QED/Specification
- * https://github.com/qemu/qemu/blob/master/docs/interop/qed_spec.txt
- */
-
-#include
-
-static const uint32_t QED_CLUSTER_DEFAULT = 64 * 1024; // 64K
-static const uint32_t QED_TABLE_DEFAULT = 4; // 4 clusters
-static const uint32_t QED_CLUSTER_MIN = 4096; // 2^12, or 4 * 1024
-static const uint32_t QED_CLUSTER_MAX = 67108864; // 2^26, or 64 * 1024 * 1024
-static const uint32_t QED_TABLE_MIN = 1;
-static const uint32_t QED_TABLE_MAX = 16;
-
-// Disk image uses a backup file for unallocated clusters
-static const uint64_t QED_F_BACKING_FILE = 1; // bit 0
-// Disk image needs to be checked before use
-static const uint64_t QED_F_NEED_CHECK = 2; // bit 1
-// Treat as raw
-static const uint64_t QED_F_BACKING_FILE_NO_PROBE = 4; // bit 2
-// Known features
-static const uint64_t QED_FEATS = QED_F_BACKING_FILE | QED_F_NEED_CHECK | QED_F_BACKING_FILE_NO_PROBE;
-
-// QED header structure
-typedef struct {
- // Magic signature
- uint32_t magic;
- // In bytes, must be a power of 2 within [2^12, 2^26].
- uint32_t cluster_size;
- // For L1/L2 tables, in clusters, must be a power of 2 within [1, 16].
- uint32_t table_size;
- // In clusters
- uint32_t header_size;
- // Format feature flags
- uint64_t features;
- // Compat feature flags
- uint64_t compat_features;
- // Self-reset feature flags
- uint64_t autoclear_features;
- // L1 table offset in bytes
- uint64_t l1_offset;
- // Disk logical capacity in bytes
- uint64_t capacity;
- // (QED_F_BACKING_FILE) Offset, in bytes from start of header, to the
- // name of the backing filename.
- uint32_t backup_name_offset;
- // (QED_F_BACKING_FILE) Size, in bytes, of the backing filename.
- uint32_t backup_name_size;
-} QED_HDR;
-
-// Deprecated
-typedef struct {
- uint64_t *offsets; // L2 table offsets to data clusters
- uint64_t offset; // Last loaded offset
- uint32_t tablesize; // table_size
- uint32_t entries; /// number of entries
-} QED_L2CACHE;
-
-typedef struct {
- uint32_t tablesize; // Calculated table size in bytes (table_size * cluster_size)
- uint32_t entries; // Number of entries
- uint64_t mask; // Offset mask after L1/L2 calculations
- struct {
- uint64_t *offsets;
- uint64_t mask;
- uint32_t shift;
- } L1; // L1 table
- struct {
- uint64_t *offsets;
- uint64_t mask;
- uint32_t shift;
- uint64_t current; // Last L2 offset loaded
- } L2; // L2 table
-} QED_INTERNALS;
-
-typedef struct {
- QED_HDR hdr;
- QED_INTERNALS in;
-} QED_META;
-
-static const uint32_t QED_META_ALLOC = sizeof(QED_META);
-
-struct VDISK;
-
-int vdisk_qed_open(struct VDISK *vd, uint32_t flags, uint32_t internal);
-
-int vdisk_qed_L2_load(struct VDISK *vd, uint64_t index);
-
-int vdisk_qed_read_sector(struct VDISK *vd, void *buffer, uint64_t index);
diff --git a/src/vdisk/raw.c b/src/vdisk/raw.c
deleted file mode 100644
index b105826..0000000
--- a/src/vdisk/raw.c
+++ /dev/null
@@ -1,27 +0,0 @@
-#include "vdisk.h"
-#include "utils.h"
-#include "platform.h"
-
-int vdisk_raw_open(VDISK *vd, uint32_t flags, uint32_t internal) {
- if (os_fsize(vd->fd, &vd->capacity))
- return vdisk_i_err(vd, VVD_EOS, __LINE__, __func__);
- vd->format = VDISK_FORMAT_RAW;
- vd->offset = 0;
- vd->cb.lba_read = vdisk_raw_read_lba;
- return 0;
-}
-
-int vdisk_raw_read_lba(VDISK *vd, void *buffer, uint64_t index) {
-
- uint64_t offset = SECTOR_TO_BYTE(index);
-
- if (offset >= vd->capacity)
- return vdisk_i_err(vd, VVD_EVDBOUND, __LINE__, __func__);
-
- if (os_fseek(vd->fd, offset, SEEK_SET))
- return vdisk_i_err(vd, VVD_EOS, __LINE__, __func__);
- if (os_fread(vd->fd, buffer, 512))
- return vdisk_i_err(vd, VVD_EOS, __LINE__, __func__);
-
- return 0;
-}
\ No newline at end of file
diff --git a/src/vdisk/raw.h b/src/vdisk/raw.h
deleted file mode 100644
index 5f75bbd..0000000
--- a/src/vdisk/raw.h
+++ /dev/null
@@ -1,4 +0,0 @@
-struct VDISK;
-
-int vdisk_raw_open(struct VDISK *vd, uint32_t flags, uint32_t internal);
-int vdisk_raw_read_lba(struct VDISK *vd, void *buffer, uint64_t index);
\ No newline at end of file
diff --git a/src/vdisk/vdi.c b/src/vdisk/vdi.c
deleted file mode 100644
index 2bad383..0000000
--- a/src/vdisk/vdi.c
+++ /dev/null
@@ -1,259 +0,0 @@
-#include // memcpy
-#include "vdisk.h"
-#include "utils.h"
-#include "platform.h"
-#ifdef TRACE
-#include
-#include
-#endif
-
-//
-// vdisk_vdi_open
-//
-
-int vdisk_vdi_open(VDISK *vd, uint32_t flags, uint32_t internal) {
- if ((vd->meta = malloc(VDI_META_ALLOC)) == NULL)
- return vdisk_i_err(vd, VVD_ENOMEM, __LINE__, __func__);
-
- if (os_fseek(vd->fd, 0, SEEK_SET))
- return vdisk_i_err(vd, VVD_EOS, __LINE__, __func__);
- if (os_fread(vd->fd, &vd->vdi->hdr, sizeof(VDI_HDR)))
- return vdisk_i_err(vd, VVD_EOS, __LINE__, __func__);
- if (vd->vdi->hdr.magic != VDI_HEADER_MAGIC)
- return vdisk_i_err(vd, VVD_EOS, __LINE__, __func__);
-
- switch (vd->vdi->hdr.majorver) { // Use latest major version natively
- case 1: // v1.1
- if (os_fread(vd->fd, &vd->vdi->v1, sizeof(VDI_HEADERv1)))
- return vdisk_i_err(vd, VVD_EOS, __LINE__, __func__);
- break;
- /*case 0:
- if (os_fread(vd->fd, &vd->vdi->v0, sizeof(VDI_HEADERv0)))
- return vdisk_i_err(vd, VVD_EOS, __LINE__, __func__);
- break;*/
- default:
- return vdisk_i_err(vd, VVD_EVDVERSION, __LINE__, __func__);
- }
-
- switch (vd->vdi->v1.type) {
- case VDI_DISK_DYN:
- case VDI_DISK_FIXED: break;
- default:
- return vdisk_i_err(vd, VVD_EVDTYPE, __LINE__, __func__);
- }
-
- // Allocation table
-
- //TODO: Consider if this is an error (or warning)
- if (vd->vdi->v1.blk_size == 0)
- vd->vdi->v1.blk_size = VDI_BLOCKSIZE;
- if (os_fseek(vd->fd, vd->vdi->v1.offBlocks, SEEK_SET))
- return vdisk_i_err(vd, VVD_EOS, __LINE__, __func__);
-
- int bsize = vd->vdi->v1.blk_total << 2; // * sizeof(u32)
- if ((vd->vdi->in.offsets = malloc(bsize)) == NULL)
- return vdisk_i_err(vd, VVD_ENOMEM, __LINE__, __func__);
- if (os_fread(vd->fd, vd->vdi->in.offsets, bsize))
- return vdisk_i_err(vd, VVD_EOS, __LINE__, __func__);
-
- // Internals / calculated values
-
- vd->capacity = vd->vdi->v1.capacity;
- vd->vdi->in.mask = vd->vdi->v1.blk_size - 1;
- vd->vdi->in.shift = fpow2(vd->vdi->v1.blk_size);
-
- // Function pointers
-
- vd->cb.lba_read = vdisk_vdi_read_sector;
-
- return 0;
-}
-
-int vdisk_vdi_create(VDISK *vd, uint64_t capacity, uint32_t flags) {
- if ((vd->meta = malloc(VDI_META_ALLOC)) == NULL)
- return vdisk_i_err(vd, VVD_ENOMEM, __LINE__, __func__);
-
- if (capacity == 0)
- return vdisk_i_err(vd, VVD_EVDMISC, __LINE__, __func__);
-
- uint32_t bcount = capacity / VDI_BLOCKSIZE;
-
- if ((vd->vdi->in.offsets = malloc(bcount << 2)) == NULL)
- return vdisk_i_err(vd, VVD_ENOMEM, __LINE__, __func__);
-
- vd->format = VDISK_FORMAT_VDI;
-
- // Pre-header
-
- strcpy(vd->vdi->hdr.signature, VDI_SIGNATURE);
- vd->vdi->hdr.magic = VDI_HEADER_MAGIC;
- vd->vdi->hdr.majorver = 1;
- vd->vdi->hdr.minorver = 1;
-
- // Header
-
- vd->vdi->v1.blk_alloc = 0;
- vd->vdi->v1.blk_extra = 0;
- vd->vdi->v1.blk_size = VDI_BLOCKSIZE;
- vd->vdi->v1.cbSector = vd->vdi->v1.LegacyGeometry.cbSector = 512;
- vd->vdi->v1.cCylinders =
- vd->vdi->v1.cHeads =
- vd->vdi->v1.cSectors =
- vd->vdi->v1.LegacyGeometry.cCylinders =
- vd->vdi->v1.LegacyGeometry.cHeads =
- vd->vdi->v1.LegacyGeometry.cSectors = 0;
- vd->vdi->v1.capacity = capacity;
- vd->vdi->v1.fFlags = 0;
- vd->vdi->v1.hdrsize = (uint32_t)sizeof(VDI_HEADERv1);
- vd->vdi->v1.offBlocks = VDI_BLOCKSIZE;
- vd->vdi->v1.offData = VDI_BLOCKSIZE * 2;
- vd->vdi->v1.blk_total = 0;
- vd->vdi->v1.type = VDI_DISK_DYN;
- vd->vdi->v1.u32Dummy = 0; // Always
- memset(vd->vdi->v1.szComment, 0, VDI_COMMENT_SIZE);
- memset(&vd->vdi->v1.uuidCreate, 0, 16);
- memset(&vd->vdi->v1.uuidLinkage, 0, 16);
- memset(&vd->vdi->v1.uuidModify, 0, 16);
- memset(&vd->vdi->v1.uuidParentModify, 0, 16);
-
- // Data
-
- uint32_t *offsets = vd->vdi->in.offsets;
- uint8_t *buffer;
- uint32_t blk_total = vd->vdi->v1.blk_total;
-
- switch (flags & VDISK_CREATE_TYPE_MASK) {
- case VDISK_CREATE_TYPE_DYNAMIC:
- vd->vdi->v1.type = VDI_DISK_DYN;
- for (size_t i = 0; i < blk_total; ++i)
- offsets[i] = VDI_BLOCK_ZERO;
- break;
- case VDISK_CREATE_TYPE_FIXED:
- vd->vdi->v1.type = VDI_DISK_FIXED;
- if ((buffer = calloc(1, vd->vdi->v1.blk_size)) == NULL)
- return vdisk_i_err(vd, VVD_ENOMEM, __LINE__, __func__);
- os_fseek(vd->fd, vd->vdi->v1.offData, SEEK_SET);
- for (size_t i = 0; i < blk_total; ++i) {
- offsets[i] = VDI_BLOCK_FREE;
- os_fwrite(vd->fd, buffer, vd->vdi->v1.blk_size);
- }
- break;
- default:
- return vdisk_i_err(vd, VVD_EVDTYPE, __LINE__, __func__);
- }
-
- return 0;
-}
-
-//
-// vdisk_vdi_read_sector
-//
-
-int vdisk_vdi_read_sector(VDISK *vd, void *buffer, uint64_t index) {
- uint64_t offset = SECTOR_TO_BYTE(index); // Byte offset
- size_t bi = offset >> vd->vdi->in.shift;
-
- if (bi >= vd->vdi->v1.blk_total) // out of bounds
- return vdisk_i_err(vd, VVD_EVDBOUND, __LINE__, __func__);
-
- uint32_t block = vd->vdi->in.offsets[bi];
- switch (block) {
- case VDI_BLOCK_ZERO: //TODO: Should this be zero'd too?
- return vdisk_i_err(vd, VVD_EVDUNALLOC, __LINE__, __func__);
- case VDI_BLOCK_FREE:
- memset(buffer, 0, 512);
- return 0;
- }
-
- offset = vd->vdi->v1.offData +
- ((uint64_t)block * vd->vdi->v1.blk_size) +
- (offset & vd->vdi->in.mask);
-
-#ifdef TRACE
- printf("%s: lba=%" PRId64 " -> offset=0x%" PRIX64 "\n", __func__, index, offset);
-#endif
-
- if (os_fseek(vd->fd, offset, SEEK_SET))
- return vdisk_i_err(vd, VVD_EOS, __LINE__, __func__);
- if (os_fread(vd->fd, buffer, 512))
- return vdisk_i_err(vd, VVD_EOS, __LINE__, __func__);
-
- return 0;
-}
-
-//
-// vdisk_vdi_compact
-//
-
-int vdisk_vdi_compact(VDISK *vd, void(*cb)(uint32_t type, void *data)) {
- if (vd->vdi->v1.type != VDI_DISK_DYN)
- return vdisk_i_err(vd, VVD_EVDTYPE, __LINE__, __func__);
-
- uint32_t *blks2; // back resolving array
- uint32_t bk_alloc; // blocks allocated (alt)
-
- // 1. Allocate block array for back resolving.
-
- uint64_t fsize;
- if (os_fsize(vd->fd, &fsize))
- return vdisk_i_err(vd, VVD_EVDMISC, __LINE__, __func__);
-
- // This verifies that there are actually data blocks available
- bk_alloc = (uint32_t)((fsize - vd->vdi->v1.offData - vd->vdi->v1.offBlocks) >> vd->vdi->in.shift);
- if (bk_alloc == 0 || vd->vdi->v1.blk_alloc == 0)
- return 0;
-
- blks2 = malloc(bk_alloc << 2);
- if (blks2 == NULL)
- return vdisk_i_err(vd, VVD_ENOMEM, __LINE__, __func__);
- for (uint32_t i; i < bk_alloc; ++i)
- blks2[i] = VDI_BLOCK_FREE;
-
- uint32_t d = 0;
- uint32_t blk_index = 0;
- uint32_t *blks = vd->vdi->in.offsets;
-
- // 2. Check and fix allocation errors before compacting
-
- for (; blk_index < vd->vdi->v1.blk_total; ++blk_index) {
- uint32_t bi = blks[blk_index]; // block index
- if (bi >= VDI_BLOCK_FREE) {
- continue;
- }
-
- if (bi < vd->vdi->v1.blk_alloc) {
- if (blks[bi] == VDI_BLOCK_FREE) {
- blks[bi] = blk_index;
- } else {
- blks[bi] = VDI_BLOCK_FREE;
- //TODO: Update header once manipulating source
- //rc = vdiUpdateBlockInfo(pImage, i);
- //vdisk_write_block_at(vd, buffer, i, d++);
- }
- } else {
- blks[bi] = VDI_BLOCK_FREE;
- //TODO: Update header once manipulating source
- //blocks[bi] = VDI_BLOCK_FREE;
- //vdisk_write_block_at(vd, buffer, i, d++);
- }
- }
-
- // 3. Find redundant information and update the block pointers accordingly
-
- uint32_t blk_count = vd->vdi->v1.blk_total;
-
- for (blk_index = 0; blk_index < blk_count; ++blk_index) {
- uint32_t bi = blks[blk_index]; // block index
- if (bi >= VDI_BLOCK_FREE) {
- continue;
- }
- }
-
- // 4. Fill bubbles with other data if available
-
-// for (i = 0; o < vd->vdiold.blk_alloc
-
- // 5. Update fields in-memory and on-disk
-
- return 0;
-}
diff --git a/src/vdisk/vdi.h b/src/vdisk/vdi.h
deleted file mode 100644
index 20ad12f..0000000
--- a/src/vdisk/vdi.h
+++ /dev/null
@@ -1,123 +0,0 @@
-/**
- * VDI: Virtualbox Disk
- *
- * Little-endian
- *
- * Source: https://forums.virtualbox.org/viewtopic.php?t=8046
- */
-
-#include
-#include "uid.h"
-
-#define VDI_SIGNATURE "<<< VirtualBox Disk Image >>>\n"
-#define VDI_SIGNATURE_VBOX "<<< Oracle VM VirtualBox Disk Image >>>\n"
-#define VDI_SIGNATURE_OLDER "<<< InnoTek VirtualBox Disk Image >>>\n"
-#define VDI_SIGNATURE_QEMU "<<< QEMU VM Disk Image >>>\n"
-
-/**
- * Block marked as free is not allocated in image file, read from this
- * block may returns any random data.
- */
-static const uint32_t VDI_BLOCK_FREE = 0xffffffff;
-
-/**
- * Block marked as zero is not allocated in image file, read from this
- * block returns zeroes. May be also known as "discarded".
- */
-static const uint32_t VDI_BLOCK_ZERO = 0xfffffffe;
-
-#define VDI_IS_ALLOCATED(X) ((X) < VDI_BLOCK_ZERO)
-
-enum {
- VDI_HEADER_MAGIC = 0xBEDA107F,
- VDI_SIGNATURE_SIZE = 64,
- VDI_COMMENT_SIZE = 256,
-
- VDI_DISK_DYN = 1,
- VDI_DISK_FIXED = 2,
- VDI_DISK_UNDO = 3,
- VDI_DISK_DIFF = 4,
-
- VDI_BLOCKSIZE = 1048576, // Default block size, 1 MiB
-};
-
-typedef struct {
- uint32_t cCylinders;
- uint32_t cHeads;
- uint32_t cSectors;
- uint32_t cbSector;
-} VDI_DISKGEOMETRY;
-
-typedef struct {
- char signature[64]; // Typically starts with "<<< "
- uint32_t magic;
- uint16_t majorver;
- uint16_t minorver;
-} VDI_HDR;
-
-typedef struct { // v0.0
- uint32_t type;
- uint32_t fFlags;
- uint8_t szComment[VDI_COMMENT_SIZE];
- VDI_DISKGEOMETRY LegacyGeometry;
- uint64_t disksize;
- uint32_t blocksize;
- uint32_t blockstotal;
- uint32_t blocksalloc;
- UID uuidCreate;
- UID uuidModify;
- UID uuidLinkage;
-} VDI_HEADERv0;
-
-typedef struct { // v1.1
- uint32_t hdrsize;
- uint32_t type;
- uint32_t fFlags;
- uint8_t szComment[VDI_COMMENT_SIZE];
- uint32_t offBlocks; // Byte offset to BAT
- uint32_t offData; // Byte offset to first block
- VDI_DISKGEOMETRY LegacyGeometry;
- uint32_t u32Dummy; // Used to be translation value for geometry
- uint64_t capacity;
- uint32_t blk_size; // Block size in bytes
- uint32_t blk_extra;
- uint32_t blk_total; // Total amount of blocks
- uint32_t blk_alloc;
- UID uuidCreate;
- UID uuidModify;
- UID uuidLinkage;
- UID uuidParentModify;
- uint32_t cCylinders; // v1.1
- uint32_t cHeads; // v1.1
- uint32_t cSectors; // v1.1
- uint32_t cbSector; // v1.1
-// uint8_t pad[40];
-} VDI_HEADERv1;
-
-typedef struct {
- uint32_t *offsets; // Offset table
- uint32_t mask; // Block bit mask
- uint32_t shift; // Block shift positions
- uint16_t majorver;
-} VDI_INTERNALS;
-
-typedef struct {
- VDI_HDR hdr;
- union {
- VDI_HEADERv0 v0;
- VDI_HEADERv1 v1;
- };
- VDI_INTERNALS in;
-} VDI_META;
-
-static const uint32_t VDI_META_ALLOC = sizeof(VDI_META);
-
-struct VDISK;
-
-int vdisk_vdi_open(struct VDISK *vd, uint32_t flags, uint32_t internal);
-
-int vdisk_vdi_create(struct VDISK *vd, uint64_t capacity, uint32_t flags);
-
-int vdisk_vdi_read_sector(struct VDISK *vd, void *buffer, uint64_t index);
-
-int vdisk_vdi_compact(struct VDISK *vd, void(*cb)(uint32_t type, void *data));
diff --git a/src/vdisk/vdisk.c b/src/vdisk/vdisk.c
new file mode 100644
index 0000000..6467fd9
--- /dev/null
+++ b/src/vdisk/vdisk.c
@@ -0,0 +1,330 @@
+#include
+#include
+#include
+#include
+#include
+#include "utils/bin.h"
+#include "vdisk/vdisk.h"
+
+//
+// Internal functions
+//
+
+int vdisk_i_err(VDISK *vd, int e, int l, const char *f) {
+ vd->err.line = l;
+ vd->err.func = f;
+ return (vd->err.num = e);
+}
+
+//
+// vdisk_open
+//
+
+int vdisk_open(VDISK *vd, const oschar *path, uint32_t flags) {
+ if ((vd->fd = os_fopen(path)) == 0)
+ return VDISK_ERROR(vd, VVD_EOS);
+
+ // pre-init
+ memset(&vd->cb, 0, sizeof(vd->cb));
+
+ //TODO: Consider detecting file type here to automatically pickup "raw" disks
+
+ if (flags & VDISK_RAW)
+ return vdisk_raw_open(vd, flags, 0);
+
+ //
+ // Disk format detection
+ //
+ // This hints the function to a format and tests both reading and
+ // seeking capabilities on the file or device.
+ //
+
+ if (os_fread(vd->fd, &vd->format, 4))
+ return VDISK_ERROR(vd, VVD_EOS);
+ if (os_fseek(vd->fd, 0, SEEK_SET))
+ return VDISK_ERROR(vd, VVD_EOS);
+
+ uint32_t internal = 0; // Internal flags
+ union {
+ uint32_t u32;
+ uint64_t u64;
+ } sig;
+
+ switch (vd->format) {
+ case VDISK_FORMAT_VDI:
+ if (vdisk_vdi_open(vd, flags, internal))
+ return vd->err.num;
+ break;
+ case VDISK_FORMAT_VMDK:
+ if (vdisk_vmdk_open(vd, flags, internal))
+ return vd->err.num;
+ break;
+ case VDISK_FORMAT_VHD: L_VDISK_FORMAT_VHD:
+ if (vdisk_vhd_open(vd, flags, internal))
+ return vd->err.num;
+ break;
+ case VDISK_FORMAT_VHDX:
+ if (vdisk_vhdx_open(vd, flags, internal))
+ return vd->err.num;
+ break;
+ case VDISK_FORMAT_QED:
+ if (vdisk_qed_open(vd, flags, internal))
+ return vd->err.num;
+ break;
+ case VDISK_FORMAT_QCOW:
+ if (vdisk_qcow_open(vd, flags, internal))
+ return vd->err.num;
+ break;
+ case VDISK_FORMAT_PHDD:
+ if (vdisk_phdd_open(vd, flags, internal))
+ return vd->err.num;
+ break;
+ default: // Attempt at different offsets
+ // VHD: (Fixed) 512 bytes before EOF
+ if (os_fseek(vd->fd, -512, SEEK_END))
+ return VDISK_ERROR(vd, VVD_EOS);
+ if (os_fread(vd->fd, &sig.u64, sizeof(uint64_t)))
+ return VDISK_ERROR(vd, VVD_EOS);
+ if (sig.u64 == VHD_MAGIC) {
+ vd->format = VDISK_FORMAT_VHD;
+ internal = 2;
+ goto L_VDISK_FORMAT_VHD;
+ }
+
+ return VDISK_ERROR(vd, VVD_EVDFORMAT);
+ }
+
+ return 0;
+}
+
+//
+// vdisk_create
+//
+
+int vdisk_create(VDISK *vd, const oschar *path, int format, uint64_t capacity, uint16_t flags) {
+
+ if (flags & VDISK_CREATE_TEMP) {
+ //TODO: Attach random number
+ path = osstr("vdisk.tmp");
+ } else if (path == NULL)
+ return VDISK_ERROR(vd, VVD_ENULL);
+
+ if (capacity == 0)
+ return VDISK_ERROR(vd, VVD_EVDBOUND);
+
+ if ((vd->fd = os_fcreate(path)) == 0)
+ return VDISK_ERROR(vd, VVD_EOS);
+
+ if (flags & VDISK_RAW) {
+ vd->format = VDISK_FORMAT_RAW;
+ if (os_falloc(vd->fd, capacity))
+ return VDISK_ERROR(vd, VVD_EOS);
+ return VVD_EOK;
+ }
+
+ int e;
+ switch (format) {
+ case VDISK_FORMAT_VDI:
+ e = vdisk_vdi_create(vd, capacity, flags);
+ break;
+ default:
+ return VDISK_ERROR(vd, VVD_EVDFORMAT);
+ }
+
+ return e ? e : vdisk_update(vd);
+}
+
+//
+//TODO: vdisk_close(VDISK *vd)
+//
+
+
+
+//
+// vdisk_str
+//
+
+const char* vdisk_str(VDISK *vd) {
+ switch (vd->format) {
+ case VDISK_FORMAT_VDI: return "VDI";
+ case VDISK_FORMAT_VMDK: return "VMDK";
+ case VDISK_FORMAT_VHD: return "VHD";
+ case VDISK_FORMAT_VHDX: return "VHDX";
+ case VDISK_FORMAT_QED: return "QED";
+ case VDISK_FORMAT_QCOW: return "QCOW";
+ case VDISK_FORMAT_PHDD: return "Parallels";
+ case VDISK_FORMAT_RAW: return "RAW";
+ default: return NULL; // Not opened, etc.
+ }
+}
+
+//
+// vdisk_update
+//
+
+int vdisk_update(VDISK *vd) {
+ switch (vd->format) {
+ case VDISK_FORMAT_VDI:
+ //TODO: Move pre-header signature in creation function
+ if (os_fseek(vd->fd, 0, SEEK_SET))
+ return VDISK_ERROR(vd, VVD_EOS);
+ if (os_fwrite(vd->fd, VDI_SIGNATURE, 40))
+ return VDISK_ERROR(vd, VVD_EOS);
+ // skip signature
+ if (os_fseek(vd->fd, VDI_SIGNATURE_SIZE, SEEK_SET))
+ return VDISK_ERROR(vd, VVD_EOS);
+ if (os_fwrite(vd->fd, &vd->vdi->hdr, sizeof(VDI_HDR)))
+ return VDISK_ERROR(vd, VVD_EOS);
+ if (os_fwrite(vd->fd, &vd->vdi->v1, sizeof(VDI_HEADERv1)))
+ return VDISK_ERROR(vd, VVD_EOS);
+ // blocks
+ if (os_fseek(vd->fd, vd->vdi->v1.offBlocks, SEEK_SET))
+ return VDISK_ERROR(vd, VVD_EOS);
+ if (os_fwrite(vd->fd, vd->vdi->in.offsets, vd->vdi->v1.blk_total << 2))
+ return VDISK_ERROR(vd, VVD_EOS);
+ break;
+ /*case VDISK_FORMAT_VMDK:
+ assert(0);
+ break;
+ case VDISK_FORMAT_VHD:
+ assert(0);
+ break;*/
+ default:
+ return VDISK_ERROR(vd, VVD_EVDFORMAT);
+ }
+
+ return 0;
+}
+
+//
+//TODO: vdisk_flush(VDISK *vd)
+//
+
+//
+// vdisk_read_sector
+//
+
+int vdisk_read_sector(VDISK *vd, void *buffer, uint64_t lba) {
+
+ //TODO: Consider an assert
+ if (vd->cb.lba_read == NULL)
+ return VDISK_ERROR(vd, VVD_EVDTODO);
+
+ return vd->cb.lba_read(vd, buffer, lba);
+}
+
+//
+//TODO: Consider vdisk_read_sectors
+// Read multiple sectors at once
+//
+
+//
+// vdisk_write_lba
+//
+
+int vdisk_write_lba(VDISK *vd, void *buffer, uint64_t lba) {
+
+ assert(0);
+
+ return VVD_EOK;
+}
+
+//
+// vdisk_read_block
+//
+
+int vdisk_read_block(VDISK *vd, void *buffer, uint64_t index) {
+
+ assert(0);
+
+ return VVD_EOK;
+}
+
+//
+// vdisk_write_block
+//
+
+int vdisk_write_block(VDISK *vd, void *buffer, uint64_t index) {
+
+ assert(0);
+
+ return VVD_EOK;
+}
+
+//
+// vdisk_write_block_at
+//
+
+int vdisk_write_block_at(VDISK *vd, void *buffer, uint64_t bindex, uint64_t dindex) {
+
+ assert(0);
+
+ return VVD_EOK;
+}
+
+//
+// vdisk_op_compact
+//
+
+int vdisk_op_compact(VDISK *vd) {
+ switch (vd->format) {
+ case VDISK_FORMAT_VDI:
+ return vdisk_vdi_compact(vd);
+ default:
+ return VDISK_ERROR(vd, VVD_EVDFORMAT);
+ }
+}
+
+//
+// vdisk_error
+//
+
+const char* vdisk_error(VDISK *vd) {
+ switch (vd->err.num) {
+ case VVD_EOS:
+#if _WIN32
+ // We're using the Win32 API, not the CRT functions, which may
+ // yield different and probably unrelated messages
+ static char _errmsgbuf[1024];
+ vd->err.num = GetLastError();
+ int l = GetLocaleInfoEx( // Recommended over MAKELANGID
+ LOCALE_NAME_USER_DEFAULT,
+ LOCALE_ALL,
+ 0,
+ 0);
+ FormatMessageA(
+ FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_MAX_WIDTH_MASK,
+ NULL,
+ vd->err.num,
+ l,
+ _errmsgbuf,
+ 512,
+ NULL);
+ return _errmsgbuf;
+#else
+ return strerror(vd->err.num = errno);
+#endif
+ case VVD_ENULL:
+ return "Parameter is null";
+ case VVD_EVDFORMAT:
+ return "Unsupported vdisk format";
+ case VVD_EVDMAGIC:
+ return "Invalid magic signature";
+ case VVD_EVDVERSION:
+ return "Unsupported version";
+ case VVD_EVDTYPE:
+ return "Invalid disk type for vdisk function";
+ case VVD_EVDFULL:
+ return "VDISK is full";
+ case VVD_EVDUNALLOC:
+ return "Block is unallocated";
+ case VVD_EVDBOUND:
+ return "Block index is out of bounds";
+ case VVD_EVDTODO:
+ return "Currently unimplemented";
+ case VVD_EOK:
+ return "Apparently, the last operation was successful";
+ default:
+ return "Unknown error happened";
+ }
+}
diff --git a/src/vdisk/vdisk.h b/src/vdisk/vdisk.h
new file mode 100644
index 0000000..5cb5746
--- /dev/null
+++ b/src/vdisk/vdisk.h
@@ -0,0 +1,252 @@
+#pragma once
+
+#include "utils/os.h"
+#include "utils/bin.h"
+#include "vdisk/fmt/raw.h"
+#include "vdisk/fmt/vdi.h"
+#include "vdisk/fmt/vmdk.h"
+#include "vdisk/fmt/vhd.h"
+#include "vdisk/fmt/vhdx.h"
+#include "vdisk/fmt/qed.h"
+#include "vdisk/fmt/qcow.h"
+#include "vdisk/fmt/phdd.h"
+
+#define VDISK_ERROR(vd,ERR) vdisk_i_err(vd,ERR,__LINE__,__func__)
+
+//
+// Constants
+//
+
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+enum { // DISKFORMAT magical hints (LSB), used for VDISK.format
+ VDISK_FORMAT_NONE = 0, // No formats has been specificied yet
+ VDISK_FORMAT_RAW = 0xAAAAAAAA, // Raw files and storage devices
+ VDISK_FORMAT_VDI = 0x203C3C3C, // "<<< " VirtualBox
+ VDISK_FORMAT_VMDK = 0x564D444B, // "VMDK" VMware
+ VDISK_FORMAT_VMDK_COW = 0x44574F43, // "COWD" VMware EXSi COW disk
+ VDISK_FORMAT_VHD = 0x656E6F63, // "cone" VirtualPC/Hyper-V
+ VDISK_FORMAT_VHDX = 0x78646876, // "vhdx" Hyper-V
+ VDISK_FORMAT_QED = 0x00444551, // "QED\0" QEMU Enhanced Disk
+ VDISK_FORMAT_QCOW = 0xFB494651, // "QFI\xFB" QEMU Copy-On-Write, v1/v2
+ VDISK_FORMAT_PHDD = 0x68746957, // "With" Parallels HDD
+ VDISK_FORMAT_BOCHS = 0x68636F42, // "Boch" Bochs Virtual HD Image
+// VDISK_FORMAT_DMG = 0x, // "" Apple DMG
+};
+#else // Big-endian
+enum { // DISKFORMAT magical hints (MSB), used for VDISK.format
+ VDISK_FORMAT_NONE = 0, // No formats has been specificied yet
+ VDISK_FORMAT_RAW = 0xAAAAAAAA, // Raw files and storage devices
+ VDISK_FORMAT_VDI = 0x3C3C3C20, // "<<< " VirtualBox
+ VDISK_FORMAT_VMDK = 0x4B444D56, // "VMDK" VMware
+ VDISK_FORMAT_VMDK_COW = 0x434F5744, // "COWD" VMware EXSi COW disk
+ VDISK_FORMAT_VHD = 0x636F6E65, // "cone" VirtualPC/Hyper-V
+ VDISK_FORMAT_VHDX = 0x76686478, // "vhdx" Hyper-V
+ VDISK_FORMAT_QED = 0x51454400, // "QED\0" QEMU Enhanced Disk
+ VDISK_FORMAT_QCOW = 0x514649FB, // "QFI\xFB" QEMU Copy-On-Write, v1/v2
+ VDISK_FORMAT_PHDD = 0x57697468, // "With" Parallels HDD
+ VDISK_FORMAT_BOCHS = 0x426F6368, // "Boch" Bochs Virtual HD Image
+// VDISK_FORMAT_DMG = 0x, // "" Apple DMG
+};
+#endif
+
+enum { // VDISK flags
+ VDISK_RAW = 0x1, // Open or create vdisk as raw
+ //
+ // vdisk_create flags
+ //
+
+ VDISK_CREATE_TEMP = 0x0100, //TODO: Create a temporary (random) vdisk file
+
+ VDISK_CREATE_TYPE_DYNAMIC = 0x1000, //TODO: Create a dynamic type VDISK
+ VDISK_CREATE_TYPE_FIXED = 0x2000, //TODO: Create a fixed type VDISK
+ VDISK_CREATE_TYPE_PARENT = 0x3000, //TODO: Create a parent of the VDISK
+ VDISK_CREATE_TYPE_SNAPSHOT = 0x4000, //TODO: Create a snapshot of the VDISK
+ VDISK_CREATE_TYPE_MASK = 0x7000, // Type mask used internally
+};
+
+enum { // VDISK error codes
+ VVD_EOK = 0, // VDISK OK
+ VVD_EOS = -2, // OS/CRT related error
+ VVD_ENULL = -3, // Input pointer is NULL
+ VVD_ENOMEM = -4, // Could not allocate memory
+ VVD_EVDFORMAT = -10, // Invalid VDISK format
+ VVD_EVDMAGIC = -11, // Invalid VDISK magic signature
+ VVD_EVDVERSION = -12, // Unsupported VDISK version (major)
+ VVD_EVDTYPE = -13, // Unsupported VDISK type
+ VVD_EVDFULL = -14, // VDISK is full and no more data can be allocated
+ VVD_EVDUNALLOC = -15, // Block is unallocated
+ VVD_EVDBOUND = -16, // Index was out of block index bounds
+ VVD_EVDTODO = -254, // Currently unimplemented
+ VVD_EVDMISC = -255, // Unknown
+};
+
+enum {
+ VVD_CAP_HAS_TABLE = 1,
+ VVD_CAP_64BIT_INDEXES = 2,
+};
+
+//
+// Structure definitions
+//
+
+// Defines a virtual disk.
+// All fields are more or less internal.
+typedef struct VDISK {
+ // Defines the virtual disk format (e.g. VDI, VMDK, etc.).
+ // See VDISKFORMAT enumeration.
+ uint32_t format;
+ // Flags. See VDISK_FLAG enumeration.
+ uint32_t flags;
+ // Reserved.
+ uint32_t cookie;
+ // Virtual disk capacity in bytes. For RAW files, it's the file size. For
+ // RAW devices, it's the disk size. This is populated automatically.
+ uint64_t capacity;
+ // OS file handle
+ // Windows: HANDLE
+ // Others: int
+ __OSFILE fd;
+ // Error structure
+ struct {
+ int num; // Error number
+ int line; // Source file line number
+ const char *func; // Function name
+ } err;
+ // Implementation functions
+ struct {
+ // Read from a disk sector with a LBA index
+ int (*lba_read)(struct VDISK*, void*, uint64_t);
+ // Write to a disk sector with a LBA index
+ int (*lba_write)(struct VDISK*, void*, uint64_t);
+ // Read a dynamic block with a block index
+ int (*blk_read)(struct VDISK*, void*, uint64_t);
+ // Read a sector with a LBA index
+ int (*blk_write)(struct VDISK*, void*, uint64_t);
+ } cb;
+ // Meta union
+ union {
+ void *meta;
+ VDI_META *vdi;
+ VMDK_META *vmdk;
+ VHD_META *vhd;
+ QED_META *qed;
+ VHDX_META *vhdx;
+ };
+} VDISK;
+
+//
+// SECTION Internal functions
+//
+
+/**
+ * (Internal) Set errcode and errline.
+ *
+ * \returns errcode
+ */
+int vdisk_i_err(VDISK *vd, int e, int l, const char *f);
+
+//
+// SECTION Functions
+//
+
+/**
+ * Open a VDISK.
+ *
+ * When opening a file, this function verifies the file path, VDISK format,
+ * header structure, version, and other fields.
+ *
+ * When creating a file, the specified file at the file path is overwritten.
+ * An empty, unallocated VDISK is created. If VDISK_CREATE_TEMP is defined,
+ * path parameter can be NULL, since the function will create a random
+ * filename (OS).
+ *
+ * \param vd VDISK structure
+ * \param path OS string path
+ * \param flags Opening flags
+ *
+ * \returns Exit status
+ */
+int vdisk_open(VDISK *vd, const oschar *path, uint32_t flags);
+
+/**
+ * Create a VDISK.
+ *
+ * \param vd VDISK structure
+ * \param path OS string path
+ * \param format Virtual disk format
+ * \param capacity Virtual disk capacity
+ * \param flags Creation flags
+ *
+ * \returns Exit status
+ */
+int vdisk_create(VDISK *vd, const oschar *path, int format, uint64_t capacity, uint16_t flags);
+
+//TODO: vdisk_caps or vdisk_info
+// VDISK capabilities
+
+/**
+ * Returns a string representation of the loaded virtual disk. If a format was
+ * not found, a null pointer is returned.
+ */
+const char *vdisk_str(VDISK *vd);
+
+/**
+ * Update header information and allocation tables into file or device.
+ */
+int vdisk_update(VDISK *vd);
+
+/**
+ * Seek and read a sector-size (512 bytes) of data from a sector index (LBA).
+ *
+ * This function checks if sector exists on dynamic type disks, and index
+ * tables such as the BAT on VHDs.
+ *
+ * Returns error code. Non-zero being an error.
+ */
+int vdisk_read_sector(VDISK *vd, void *buffer, uint64_t lba);
+
+/**
+ * Seek to a block index and read it. The size of the block depends on the size
+ * speicified in the VDISK structure. Only certain VDISK types are supported,
+ * notably dynamic types. If unsupported, returns EVDFORMAT or EVDTYPE.
+ */
+int vdisk_read_block(VDISK *vd, void *buffer, uint64_t index);
+
+/**
+ *
+ */
+int vdisk_write_lba(VDISK *vd, void *buffer, uint64_t lba);
+
+/**
+ *
+ */
+int vdisk_write_block(VDISK *vd, void *buffer, uint64_t index);
+
+/**
+ *
+ */
+int vdisk_write_block_at(VDISK *vd, void *buffer, uint64_t bindex, uint64_t dindex);
+
+//
+// SECTION
+//
+
+/**
+ *
+ */
+int vdisk_op_compact(VDISK *vd);
+
+/**
+ *
+ */
+//int vdisk_op_resize(VDISK *vd, void(*cb_progress)(uint64_t block));
+
+//
+// SECTION Error handling
+//
+
+/**
+ * Returns an error message depending on the last value of vdisk_errno. If the
+ * error is set to VVD_EOS, the error message will come from the OS (or CRT).
+ */
+const char* vdisk_error(VDISK *vd);
diff --git a/src/vdisk/vhd.c b/src/vdisk/vhd.c
deleted file mode 100644
index c11c13a..0000000
--- a/src/vdisk/vhd.c
+++ /dev/null
@@ -1,166 +0,0 @@
-#include "vdisk.h"
-#include "utils.h"
-#include "platform.h"
-#include "utils.h"
-#ifdef TRACE
-#include
-#include
-#endif
-
-//
-// vdisk_vhd_open
-//
-
-int vdisk_vhd_open(VDISK *vd, uint32_t flags, uint32_t internal) {
- if ((vd->vmdk = malloc(VHD_META_ALLOC)) == NULL)
- return vdisk_i_err(vd, VVD_ENOMEM, __LINE__, __func__);
-
- if (internal & 2) {
- if (os_fseek(vd->fd, -512, SEEK_END))
- return vdisk_i_err(vd, VVD_EOS, __LINE__, __func__);
- }
-
- if (os_fread(vd->fd, &vd->vhd->hdr, sizeof(VHD_HDR)))
- return vdisk_i_err(vd, VVD_EOS, __LINE__, __func__);
- if (vd->vhd->hdr.magic != VHD_MAGIC)
- return vdisk_i_err(vd, VVD_EVDMAGIC, __LINE__, __func__);
-
-#if ENDIAN_LITTLE
- vd->vhd->hdr.major = bswap16(vd->vhd->hdr.major);
-#endif
-
- if (vd->vhd->hdr.major != 1)
- return vdisk_i_err(vd, VVD_EVDVERSION, __LINE__, __func__);
-
-#if ENDIAN_LITTLE
- vd->vhd->hdr.type = bswap32(vd->vhd->hdr.type);
-#endif
-
- switch (vd->vhd->hdr.type) {
- case VHD_DISK_DIFF:
- case VHD_DISK_DYN:
- case VHD_DISK_FIXED: break;
- default:
- return vdisk_i_err(vd, VVD_EVDTYPE, __LINE__, __func__);
- }
-
-#if ENDIAN_LITTLE
- vd->vhd->hdr.features = bswap32(vd->vhd->hdr.features);
- vd->vhd->hdr.minor = bswap16(vd->vhd->hdr.minor);
- vd->vhd->hdr.offset = bswap64(vd->vhd->hdr.offset);
- vd->vhd->hdr.timestamp = bswap32(vd->vhd->hdr.timestamp);
- vd->vhd->hdr.creator_major = bswap16(vd->vhd->hdr.creator_major);
- vd->vhd->hdr.creator_minor = bswap16(vd->vhd->hdr.creator_minor);
-// vd->vhd->creator_os = bswap32(vd->vhd->creator_os);
- vd->vhd->hdr.size_original = bswap64(vd->vhd->hdr.size_original);
- vd->vhd->hdr.size_current = bswap64(vd->vhd->hdr.size_current);
- vd->vhd->hdr.cylinders = bswap16(vd->vhd->hdr.cylinders);
- vd->vhd->hdr.checksum = bswap32(vd->vhd->hdr.checksum);
- uid_swap(&vd->vhd->hdr.uuid);
-#endif
-
- if (vd->vhd->hdr.type != VHD_DISK_FIXED) {
- if (os_fseek(vd->fd, vd->vhd->hdr.offset, SEEK_SET))
- return vdisk_i_err(vd, VVD_EOS, __LINE__, __func__);
- if (os_fread(vd->fd, &vd->vhd->dyn, sizeof(VHD_DYN_HDR)))
- return vdisk_i_err(vd, VVD_EOS, __LINE__, __func__);
- if (vd->vhd->dyn.magic != VHD_DYN_MAGIC)
- return vdisk_i_err(vd, VVD_EVDMAGIC, __LINE__, __func__);
-
-#if ENDIAN_LITTLE
- vd->vhd->dyn.data_offset = bswap64(vd->vhd->dyn.data_offset);
- vd->vhd->dyn.table_offset = bswap64(vd->vhd->dyn.table_offset);
- vd->vhd->dyn.minor = bswap16(vd->vhd->dyn.minor);
- vd->vhd->dyn.major = bswap16(vd->vhd->dyn.major);
- vd->vhd->dyn.max_entries = bswap32(vd->vhd->dyn.max_entries);
- vd->vhd->dyn.blocksize = bswap32(vd->vhd->dyn.blocksize);
- vd->vhd->dyn.checksum = bswap32(vd->vhd->dyn.checksum);
- vd->vhd->dyn.parent_timestamp = bswap32(vd->vhd->dyn.parent_timestamp);
- uid_swap(&vd->vhd->dyn.parent_uuid);
-
- for (size_t i = 0; i < 8; ++i) {
- vd->vhd->dyn.parent_locator[i].code =
- bswap32(vd->vhd->dyn.parent_locator[i].code);
- vd->vhd->dyn.parent_locator[i].datasize =
- bswap32(vd->vhd->dyn.parent_locator[i].datasize);
- vd->vhd->dyn.parent_locator[i].dataspace =
- bswap32(vd->vhd->dyn.parent_locator[i].dataspace);
- vd->vhd->dyn.parent_locator[i].offset =
- bswap64(vd->vhd->dyn.parent_locator[i].offset);
- }
-#endif
-
- if (pow2(vd->vhd->dyn.blocksize) == 0 ||
- vd->vhd->dyn.max_entries != vd->vhd->hdr.size_original / vd->vhd->dyn.blocksize)
- return vdisk_i_err(vd, VVD_EVDMISC, __LINE__, __func__);
-
- vd->vhd->in.mask = vd->vhd->dyn.blocksize - 1;
- vd->vhd->in.shift = fpow2(vd->vhd->dyn.blocksize);
-
- if (vd->vhd->dyn.max_entries == 0)
- return vdisk_i_err(vd, VVD_EVDMAGIC, __LINE__, __func__);
- if (os_fseek(vd->fd, vd->vhd->dyn.table_offset, SEEK_SET))
- return vdisk_i_err(vd, VVD_EOS, __LINE__, __func__);
-
- int batsize = vd->vhd->dyn.max_entries << 2; // "* 4"
- if ((vd->vhd->in.offsets = malloc(batsize)) == NULL)
- return vdisk_i_err(vd, VVD_ENOMEM, __LINE__, __func__);
- if (os_fread(vd->fd, vd->vhd->in.offsets, batsize))
- return vdisk_i_err(vd, VVD_EOS, __LINE__, __func__);
-#if ENDIAN_LITTLE
- for (size_t i = 0; i < vd->vhd->dyn.max_entries; ++i)
- vd->vhd->in.offsets[i] = bswap32(vd->vhd->in.offsets[i]);
-#endif
- vd->cb.lba_read = vdisk_vhd_dyn_read_lba;
- } else { // Fixed
- vd->cb.lba_read = vdisk_vhd_fixed_read_lba;
- }
-
- vd->capacity = vd->vhd->hdr.size_original;
- return 0;
-}
-
-//
-// vdisk_vhd_fixed_read_lba
-//
-
-int vdisk_vhd_fixed_read_lba(VDISK *vd, void *buffer, uint64_t index) {
- uint64_t offset = SECTOR_TO_BYTE(index); // Byte offset
-
- if (os_fseek(vd->fd, offset, SEEK_SET))
- return vdisk_i_err(vd, VVD_EOS, __LINE__, __func__);
- if (os_fread(vd->fd, buffer, 512))
- return vdisk_i_err(vd, VVD_EOS, __LINE__, __func__);
-
- return 0;
-}
-
-//
-// vdisk_vhd_dyn_read_lba
-//
-
-int vdisk_vhd_dyn_read_lba(VDISK *vd, void *buffer, uint64_t index) {
- uint64_t offset = SECTOR_TO_BYTE(index);
- uint32_t bi = (uint32_t)(offset >> vd->vhd->in.shift);
-#ifdef TRACE
- printf("%s: bi=%u\n", __func__, bi);
-#endif
- if (bi >= vd->vhd->dyn.max_entries)
- return vdisk_i_err(vd, VVD_EVDBOUND, __LINE__, __func__);
-
- uint32_t block = vd->vhd->in.offsets[bi];
- if (block == VHD_BLOCK_UNALLOC) // Unallocated
- return vdisk_i_err(vd, VVD_EVDUNALLOC, __LINE__, __func__);
-
- uint64_t base = SECTOR_TO_BYTE(block) + 512;
- offset = base + (offset & vd->vhd->in.mask);
-#ifdef TRACE
- printf("%s: block=%u offset=%" PRIu64 "\n", __func__, block, offset);
-#endif
- if (os_fseek(vd->fd, offset, SEEK_SET))
- return vdisk_i_err(vd, VVD_EOS, __LINE__, __func__);
- if (os_fread(vd->fd, buffer, 512))
- return vdisk_i_err(vd, VVD_EOS, __LINE__, __func__);
-
- return 0;
-}
diff --git a/src/vdisk/vhd.h b/src/vdisk/vhd.h
deleted file mode 100644
index 8d1addf..0000000
--- a/src/vdisk/vhd.h
+++ /dev/null
@@ -1,97 +0,0 @@
-/**
- * VHD: (Connectix) Virtual Hard Disk
- */
-
-#include
-
-#define VHDMAGIC "conectix"
-#define VHD_MAGIC 0x78697463656E6F63 // "conectix"
-#define VHD_DYN_MAGIC 0x6573726170737863 // "cxsparse"
-#define VHD_OS_WIN 0x6B326957 // "Wi2k"
-#define VHD_OS_MAC 0x2063614D // "Mac "
-
-enum {
- VHD_DISK_NONE = 0,
- VHD_DISK_RES1 = 1,
- VHD_DISK_FIXED = 2,
- VHD_DISK_DYN = 3,
- VHD_DISK_DIFF = 4,
- VHD_DISK_RES2 = 5,
- VHD_DISK_RES3 = 6
-};
-
-enum {
- VHD_BLOCK_UNALLOC = -1, // Block not allocated on disk
- VHD_FEAT_TEMP = 1,
- VHD_FEAT_RES = 2 // reserved, but always set
-};
-
-typedef struct { // v1
- uint64_t magic; // "conectix"
- uint32_t features;
- uint16_t major;
- uint16_t minor;
- uint64_t offset;
- uint32_t timestamp;
- char creator_app[4];
- uint16_t creator_major;
- uint16_t creator_minor;
- uint32_t creator_os;
- uint64_t size_original; // Capacity in bytes
- uint64_t size_current;
- uint16_t cylinders;
- uint8_t heads;
- uint8_t sectors;
- uint32_t type;
- uint32_t checksum;
- UID uuid;
- uint8_t savedState;
- uint8_t reserved[427];
-} VHD_HDR;
-
-typedef struct {
- uint32_t code;
- uint32_t dataspace;
- uint32_t datasize;
- uint32_t res;
- uint64_t offset;
-} VHD_PARENT_LOCATOR;
-
-typedef struct { // v1
- uint64_t magic;
- uint64_t data_offset;
- uint64_t table_offset;
- uint16_t minor;
- uint16_t major;
- uint32_t max_entries; // For BAT
- uint32_t blocksize; // In bytes
- uint32_t checksum;
- UID parent_uuid; // UUID
- uint32_t parent_timestamp;
- uint32_t res;
- uint16_t parent_name[256]; // UTF-16
- VHD_PARENT_LOCATOR parent_locator[8];
- uint8_t res1[256];
-} VHD_DYN_HDR;
-
-typedef struct {
- uint32_t *offsets;
- uint32_t mask;
- uint32_t shift;
-} VHD_INTERNALS;
-
-typedef struct {
- VHD_HDR hdr;
- VHD_DYN_HDR dyn;
- VHD_INTERNALS in;
-} VHD_META;
-
-static const uint32_t VHD_META_ALLOC = sizeof(VHD_META);
-
-struct VDISK;
-
-int vdisk_vhd_open(struct VDISK *vd, uint32_t flags, uint32_t internal);
-
-int vdisk_vhd_dyn_read_lba(struct VDISK *vd, void *buffer, uint64_t index);
-
-int vdisk_vhd_fixed_read_lba(struct VDISK *vd, void *buffer, uint64_t index);
diff --git a/src/vdisk/vhdx.c b/src/vdisk/vhdx.c
deleted file mode 100644
index 65bdb6f..0000000
--- a/src/vdisk/vhdx.c
+++ /dev/null
@@ -1,61 +0,0 @@
-#include "vdisk.h"
-#include "utils.h"
-#include "platform.h"
-#include
-
-int vdisk_vhdx_open(VDISK *vd, uint32_t flags, uint32_t internal) {
- assert(0); //TODO: Continue VHDX
-
- if ((vd->meta = malloc(VHDX_META_ALLOC)) == NULL)
- return vdisk_i_err(vd, VVD_ENOMEM, __LINE__, __func__);
-
- //TODO: Check both headers and regions before doing an error
-
- //
- // Headers
- //
-
- if (os_fread(vd->fd, &vd->vhdx->hdr, sizeof(VHDX_HDR)))
- return vdisk_i_err(vd, VVD_EOS, __LINE__, __func__);
- if (vd->vhdx->hdr.magic != VHDX_MAGIC)
- return vdisk_i_err(vd, VVD_EVDMAGIC, __LINE__, __func__);
-
- if (os_fseek(vd->fd, VHDX_HEADER1_LOC, SEEK_SET))
- return vdisk_i_err(vd, VVD_EOS, __LINE__, __func__);
- if (os_fread(vd->fd, &vd->vhdx->v1, sizeof(VHDX_HEADER1)))
- return vdisk_i_err(vd, VVD_EOS, __LINE__, __func__);
-
- if (os_fseek(vd->fd, VHDX_HEADER2_LOC, SEEK_SET))
- return vdisk_i_err(vd, VVD_EOS, __LINE__, __func__);
- if (os_fread(vd->fd, &vd->vhdx->v1_2, sizeof(VHDX_HEADER1)))
- return vdisk_i_err(vd, VVD_EOS, __LINE__, __func__);
-
- if (vd->vhdx->v1.magic != VHDX_HDR1_MAGIC || vd->vhdx->v1_2.magic != VHDX_HDR1_MAGIC)
- return vdisk_i_err(vd, VVD_EVDMAGIC, __LINE__, __func__);
- if (vd->vhdx->v1.version != 1 || vd->vhdx->v1_2.version != 1)
- return vdisk_i_err(vd, VVD_EVDVERSION, __LINE__, __func__);
-
- //
- // Regions
- //
-
- if (os_fseek(vd->fd, VHDX_REGION1_LOC, SEEK_SET))
- return vdisk_i_err(vd, VVD_EOS, __LINE__, __func__);
- if (os_fread(vd->fd, &vd->vhdx->reg, sizeof(VHDX_REGION_HDR)))
- return vdisk_i_err(vd, VVD_EOS, __LINE__, __func__);
- if (vd->vhdx->reg.magic != VHDX_REGION_MAGIC)
- return vdisk_i_err(vd, VVD_EVDMAGIC, __LINE__, __func__);
-
- //
- //TODO: Log
- //
-
- //
- // BAT
- //
-
- // Chunk ratio
- //(8388608 * ) / // 8 KiB * 512
-
- return 0;
-}
diff --git a/src/vdisk/vhdx.h b/src/vdisk/vhdx.h
deleted file mode 100644
index e089421..0000000
--- a/src/vdisk/vhdx.h
+++ /dev/null
@@ -1,145 +0,0 @@
-/**
- * VHDX: (Microsoft) Virtual Hard Disk eXtended
- *
- * Little Endian
- *
- * Source: MS-VHDX v20160714
- */
-
-#include
-#include "uid.h"
-
-static const uint64_t VHDX_MAGIC = 0x656C696678646876; // "vhdxfile"
-static const uint32_t VHDX_HDR1_MAGIC = 0x64616568; // "head"
-static const uint32_t VHDX_REGION_MAGIC = 0x69676572; // "regi"
-static const uint32_t VHDX_LOG_HDR_MAGIC = 0x65676F6C; // "loge"
-static const uint32_t VHDX_LOG_ZERO_MAGIC = 0x6F72657A; // "zero"
-static const uint32_t VHDX_LOG_DESC_MAGIC = 0x63736564; // "desc"
-static const uint32_t VHDX_LOG_DATA_MAGIC = 0x61746164; // "data"
-static const uint64_t VHDX_METADATA_MAGIC = 0x617461646174656D; // "metadata"
-
-enum {
- VHDX_HEADER1_LOC = 64 * 1024, // 64 KiB
- VHDX_HEADER2_LOC = VHDX_HEADER1_LOC * 2, // 128 KiB
- VHDX_REGION1_LOC = VHDX_HEADER1_LOC * 3, // 192 KiB
- VHDX_REGION2_LOC = VHDX_HEADER1_LOC * 4, // 256 KiB
- VDHX_LOG_ALIGN = 4 * 1024, // 4 KiB
-};
-
-typedef struct {
- uint64_t magic;
- union {
- uint8_t u8creator[512];
- uint16_t u16creator[256];
- };
-} VHDX_HDR;
-
-typedef struct {
- uint32_t magic;
- uint32_t crc32;
- uint32_t seqnumber;
- UID filewrite;
- UID datawrite;
- UID log;
- uint16_t logversion;
- uint16_t version;
- uint32_t logsize;
- uint64_t logoffset;
-} VHDX_HEADER1;
-
-typedef struct {
- uint32_t magic;
- uint32_t crc32;
- uint32_t count;
- uint32_t res;
-} VHDX_REGION_HDR;
-
-typedef struct {
- // BAT 2DC27766-F623-4200-9D64-115E9BFD4A08 required
- // METADATA 8B7CA206-4790-4B9A-B8FE-575F050F886E required
- UID guid;
- uint64_t offset;
- uint32_t length;
- uint32_t required;
-} VHDX_REGION_ENTRY;
-
-typedef struct {
- uint32_t magic;
- uint32_t crc32;
- uint32_t count;
- uint32_t tail;
- uint64_t sequence;
- uint32_t desccount;
- uint32_t res;
- UID guid;
- uint64_t flushedoffset;
- uint64_t lastoffset;
-} VHDX_LOG_HDR;
-
-typedef struct {
- uint32_t magic;
- uint32_t trail; // resolution
- uint64_t leading; // Multiple of 4 KiB, length
- uint64_t offset; // Multiple of 4 KiB
- uint64_t sequence;
-} VDHX_LOG_DESC;
-
-typedef struct {
- uint32_t magic;
- union {
- uint8_t cdata[4096]; // Cluster
- struct {
- uint32_t sequenceh;
- union {
- uint8_t data[4084]; // as per doc
- struct {
- uint64_t lead;
- uint8_t rdata[4068]; // The real data
- uint64_t trail;
- };
- };
- uint32_t sequencel;
- };
- };
-} VHDX_LOG_DATA;
-
-typedef struct {
- uint64_t magic;
- uint16_t res;
- uint16_t count;
- uint8_t res2[20];
-} VHDX_METADATA_HDR;
-
-typedef struct {
- // File Parameters CAA16737-FA36-4D43-B3B6-33F0AA44E76B
- // Virtual Disk Size 2FA54224-CD1B-4876-B211-5DBED83BF4B8
- // "Page 83 Data" BECA12AB-B2E6-4523-93EF-C309E000C746
- // Logical Sector Size 8141BF1D-A96F-4709-BA47-F233A8FAAB5F
- // Logical Sector Size CDA348C7-445D-4471-9CC9-E9885251C556
- // Parent Locator A8D35F2D-B30B-454D-ABF7-D3D84834AB0C
- UID type; // itemID GUID
- uint32_t offset;
- uint32_t length;
- uint32_t flags; // ...plus 2 bits? what the hell?
-} VHDX_METADATA_ENTRY;
-
-typedef struct {
-
-} VHDX_INTERNALS;
-
-typedef struct {
- VHDX_HDR hdr;
- VHDX_HEADER1 v1;
- VHDX_HEADER1 v1_2;
- VHDX_REGION_HDR reg;
- VHDX_REGION_HDR reg2;
- VHDX_LOG_HDR log;
- VHDX_METADATA_HDR meta;
- VHDX_INTERNALS in;
-} VHDX_META;
-
-static const uint32_t VHDX_META_ALLOC = sizeof(VHDX_META);
-
-struct VDISK;
-
-int vdisk_vhdx_open(struct VDISK *vd, uint32_t flags, uint32_t internal);
diff --git a/src/vdisk/vmdk.c b/src/vdisk/vmdk.c
deleted file mode 100644
index 4a5ebf5..0000000
--- a/src/vdisk/vmdk.c
+++ /dev/null
@@ -1,46 +0,0 @@
-#include "vdisk.h"
-#include "utils.h"
-#include "platform.h"
-
-int vdisk_vmdk_open(VDISK *vd, uint32_t flags, uint32_t internal) {
- if ((vd->vmdk = malloc(VMDK_META_ALLOC)) == NULL)
- return vdisk_i_err(vd, VVD_ENOMEM, __LINE__, __func__);
-
- if (os_fread(vd->fd, &vd->vmdk->hdr, sizeof(VMDK_HDR)))
- return vdisk_i_err(vd, VVD_EOS, __LINE__, __func__);
- if (vd->vmdk->hdr.version != 1)
- return vdisk_i_err(vd, VVD_EVDVERSION, __LINE__, __func__);
- if (vd->vmdk->hdr.grainSize < 8 || // < 4KiB
- vd->vmdk->hdr.grainSize > 128 || // > 64KiB
- pow2(vd->vmdk->hdr.grainSize) == 0)
- return vdisk_i_err(vd, VVD_EVDMISC, __LINE__, __func__);
-
- vd->capacity = SECTOR_TO_BYTE(vd->vmdk->hdr.capacity);
-
- vd->vmdk->in.mask = vd->vmdk->hdr.grainSize - 1;
- vd->vmdk->in.shift = fpow2((uint32_t)vd->vmdk->hdr.grainSize);
- vd->vmdk->in.overhead = SECTOR_TO_BYTE(vd->vmdk->hdr.overHead);
-
- vd->cb.lba_read = vdisk_vmdk_sparse_read_lba;
-
- return 0;
-}
-
-int vdisk_vmdk_sparse_read_lba(VDISK *vd, void *buffer, uint64_t index) {
-
- uint64_t offset = SECTOR_TO_BYTE(index); // Byte offset
-
- if (offset >= vd->capacity)
- return vdisk_i_err(vd, VVD_EVDMISC, __LINE__, __func__);
-
- //bi = offset / SECTOR_TO_BYTE(vd->vmdkold.grainSize);
- //TODO: Work with the grainSize
- offset += vd->vmdk->in.overhead;
-
- if (os_fseek(vd->fd, offset, SEEK_SET))
- return vdisk_i_err(vd, VVD_EOS, __LINE__, __func__);
- if (os_fread(vd->fd, buffer, 512))
- return vdisk_i_err(vd, VVD_EOS, __LINE__, __func__);
-
- return 0;
-}
diff --git a/src/vdisk/vmdk.h b/src/vdisk/vmdk.h
deleted file mode 100644
index 9a081f0..0000000
--- a/src/vdisk/vmdk.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/**
- * VMware Disk image
- *
- * Little-endian
- *
- * +-------------+
- * | 0 | 1 | ... | Grain Directory Entries
- * +-------------+
- * |
- * +---
- * | 1 Grain Table Entries
- *
- * Sources:
- * - VMware Virtual Disks Virtual Disk Format 1.1
- * - VMware Virtual Disk Format 5.0
- */
-
-#include
-
-enum {
- VMDK_F_VALID_NL = 0x1, // Valid newline detection
- VMDK_F_REDUNDANT_TABLE = 0x2, // Redundant grain table will be used
- VMDK_F_ZEROED_GTE = 0x4, // Zeroed-grain GTE will be used
- VDMK_F_COMPRESSED = 0x10000, // Grains are compressed
- VMDK_F_MARKERS = 0x20000, // Markers used
-
- VMDK_C_NONE = 0, // No compression is used
- VMDK_C_DEFLATE = 1, // DEFLATE (RFC 1951) is used
-
- VMDK_2G_SPLIT_SIZE = 2047 * 1024 * 1024, // grainSize*sectorSize = 2 GiB
- VMDK_TEXT_LENGTH = 10 * 1024, // 10K text overhead buffer
- VMDK_GRAINSIZE_DEFAULT = 64 * 1024 // Default being 64K
-};
-enum {
- VMDK_MARKER_EOS = 0, // end-of-stream
- VMDK_MARKER_GT = 1, // grain table marker
- VMDK_MARKER_GD = 2, // grain directory marker
- VMDK_MARKER_FOOTER = 3, // footer marker
-
- VMDK_DISK_DYN = 1, // (Internal) Sparse
- VMDK_DISK_FIXED = 2, // (Internal) Monolithic
-};
-
-typedef struct {
- uint32_t magicNumber;
- uint32_t version; // v1 or v2
- uint32_t flags; // See VMDK_F_* values
- uint64_t capacity; // Disk capacity in sectors
- uint64_t grainSize; // Block size in sectors
- uint64_t descriptorOffset; // If set, embedded descriptor offset in sectors
- uint64_t descriptorSize; // If set, embedded descriptor size in sectors
- uint32_t numGTEsPerGT; // Number of entries in a grain table, typically 512
- uint64_t rgdOffset; // Offset to level 0 redundant metadata in sectors
- uint64_t gdOffset; // Offset to level 0 metadata (grain directory) in sectors
- uint64_t overHead; // Offset to data in sectors
- uint8_t uncleanShutdown; // Acts as a boolean value
- uint8_t singleEndLineChar; // Typically '\n'
- uint8_t nonEndLineChar; // Typically ' '
- uint8_t doubleEndLineChar1; // Typically '\r'
- uint8_t doubleEndLineChar2; // Typically '\n'
- uint16_t compressAlgorithm; // See VMDK_C_* values
- uint8_t pad[433];
-} VMDK_HDR;
-
-typedef struct {
- uint64_t uSector;
- uint32_t cbSize;
- uint32_t uType;
- uint8_t pad[496];
-} VMDK_MARKER;
-
-typedef struct {
- uint32_t *l0_offsets; // Grain Directory offsets
- uint32_t *l1_offsets; // Grain Table offsets
- uint32_t mask; // Bit offset mask
- uint32_t shift; // Bit offset shift
- uint64_t overhead; // data overhead in bytes
-} VMDK_INTERNALS;
-
-typedef struct {
- VMDK_HDR hdr;
- VMDK_INTERNALS in;
-} VMDK_META;
-
-static const uint32_t VMDK_META_ALLOC = sizeof(VMDK_META);
-
-struct VDISK;
-
-int vdisk_vmdk_open(struct VDISK *vd, uint32_t flags, uint32_t internal);
-
-int vdisk_vmdk_sparse_read_lba(struct VDISK *vd, void *buffer, uint64_t index);
diff --git a/src/vvd.c b/src/vvd.c
index 3ac4f9a..c9ce12a 100644
--- a/src/vvd.c
+++ b/src/vvd.c
@@ -6,10 +6,12 @@
#include // memcpy
#include
#include "vvd.h"
-#include "utils.h"
+#include "utils/bin.h"
#include "fs/mbr.h"
#include "fs/gpt.h"
+//TODO: Consider a "log" module
+
//
// Global variables
//
@@ -24,45 +26,69 @@
uint32_t g_flags;
//
-// vvd_cb_progress
+// vvd_perror
//
-void vvd_cb_progress(uint32_t type, void *data) {
- switch (type) {
- case VVD_NOTIF_DONE:
- if (g_flags & VVD_PROGRESS)
- if (os_pfinish(&g_progress)) {
- fputs("os_pfinish: Could not finish progress bar", stderr);
- exit(1);
- }
- return;
- case VVD_NOTIF_VDISK_CREATED_TYPE_NAME:
- printf("%s\n", data);
- return;
- case VVD_NOTIF_VDISK_TOTAL_BLOCKS:
- case VVD_NOTIF_VDISK_TOTAL_BLOCKS64:
- if (g_flags & VVD_PROGRESS)
- if (os_pinit(&g_progress, PROG_MODE_POURCENT, 0)) {
- fputs("os_pinit: Could not init progress bar\n", stderr);
- exit(1);
- }
- return;
- case VVD_NOTIF_VDISK_CURRENT_BLOCK:
- case VVD_NOTIF_VDISK_CURRENT_BLOCK64:
- if (g_flags & VVD_PROGRESS)
- if (os_pupdate(&g_progress, 0)) {
- fputs("os_pinit: Could not update progress bar\n", stderr);
- exit(1);
- }
- return;
+#if _WIN32
+ #define ERRFMT "%08X"
+#else
+ #define ERRFMT "%d"
+#endif
+
+void vvd_perror(VDISK *vd) {
+ fprintf(stderr, "%s@%u: (" ERRFMT ") %s\n",
+ vd->err.func, vd->err.line, vd->err.num, vdisk_error(vd));
+}
+
+#if TRACE
+void vvd_trace(const char *msg, const char *func, const int line) {
+ fprintf(stderr, "** [%s:%d] %s\n", func, line, msg);
+}
+#endif // TRACE
+
+//
+// Callback functions
+//
+
+/*
+//
+void vvd_cb_vdisk_created(VDISK *disk) {
+ printf("Disk type %s created\n", vdisk_str(disk));
+}
+
+//
+void vvd_cb_current_blocks(uint64_t total) {
+ if (g_flags & VVD_PROGRESS)
+ if (os_pupdate(&g_progress, 0)) {
+ fputs("os_pinit: Could not update progress bar\n", stderr);
+ exit(1);
}
}
//
+void vvd_cb_total_blocks(uint64_t total) {
+ if (g_flags & VVD_PROGRESS)
+ if (os_pinit(&g_progress, PROG_MODE_POURCENT, 0)) {
+ fputs("os_pinit: Could not init progress bar\n", stderr);
+ exit(1);
+ }
+}
+
+//
+void vvd_cb_done() {
+ if (g_flags & VVD_PROGRESS)
+ if (os_pfinish(&g_progress)) {
+ fputs("os_pfinish: Could not finish progress bar", stderr);
+ exit(1);
+ }
+}
+*/
+
+//
// vvd_info_mbr
//
-void vvd_info_mbr(MBR *mbr, uint32_t flags) {
+void vvd_info_mbr(MBR *mbr, struct settings_t *settings) {
char strsize[BINSTR_LENGTH];
uint64_t totalsize = SECTOR_TO_BYTE(
(uint64_t)mbr->pe[0].sectors + (uint64_t)mbr->pe[1].sectors +
@@ -71,15 +97,14 @@
bintostr(strsize, totalsize);
- if (flags & VVD_INFO_RAW) {
+ if (settings->info.full) {
printf(
- "\n"
"disklabel : MBR\n"
"serial : 0x%08X\n"
"type : 0x%04X\n",
mbr->serial, mbr->type
);
- for (unsigned int i = 0; i < 4; ++i) {
+ for (unsigned int i = 0; i < 4;) {
MBR_PARTITION pe = mbr->pe[i];
printf(
"\n"
@@ -90,7 +115,7 @@
"length : %u sectors\n"
"chs start : %u/%u/%u\n"
"chs end : %u/%u/%u\n",
- i,
+ ++i,
pe.status,
pe.type,
pe.lba,
@@ -103,21 +128,21 @@
pe.chslast.head, pe.chslast.sector & 0x3F
);
}
- } else {
+ } else { // Summary
printf(
- "\n"
"MBR (DOS) disklabel, %s used\n"
- " Boot Start Size Type\n", strsize
+ " Boot Start Size Id Type\n", strsize
);
- for (unsigned int i = 0; i < 4; ++i) {
+ for (unsigned int i = 0; i < 4;) {
MBR_PARTITION pe = mbr->pe[i];
bintostr(strsize, SECTOR_TO_BYTE(pe.sectors));
printf(
- "%u. %c %11u %10s %s\n",
- i,
+ "%u. %c %11u %10s %2x %s\n",
+ ++i,
pe.status >= 0x80 ? '*' : ' ',
pe.lba,
strsize,
+ pe.type,
mbr_part_type_str(pe.type)
);
}
@@ -128,13 +153,13 @@
// vvd_info_gpt
//
-void vvd_info_gpt(GPT *gpt, uint32_t flags) {
+void vvd_info_gpt(GPT *gpt, struct settings_t *settings) {
char gptsize[BINSTR_LENGTH];
UID_TEXT diskguid;
uid_str(diskguid, &gpt->guid, UID_GUID);
- if (flags & VVD_INFO_RAW) {
+ if (settings->info.full) {
printf(
"\n"
"disklabel : GPT\n"
@@ -175,15 +200,15 @@
// vvd_info_gpt_entries
//
-void vvd_info_gpt_entries(VDISK *vd, GPT *gpt, uint64_t lba, uint32_t flags) {
- int max = gpt->pt_entries; // maximum limiter, typically 128
+void vvd_info_gpt_entries(VDISK *vd, GPT *gpt, uint64_t lba, struct settings_t *settings) {
char partname[EFI_PART_NAME_LENGTH];
char partsize[BINSTR_LENGTH];
UID_TEXT partguid, typeguid;
GPT_ENTRY entry; // GPT entry
uint32_t entrynum = 1;
+ char gptbkp = settings->internal.gpt_bkp;
- if ((flags & VVD_INFO_RAW) == 0)
+ if (settings->info.full == 0)
puts("Part Start Size Type");
START:
@@ -199,7 +224,7 @@
uid_str(partguid, &entry.part, UID_GUID);
int wr = wstra(partname, entry.partname, EFI_PART_NAME_LENGTH);
- if (flags & VVD_INFO_RAW) {
+ if (settings->info.full) {
printf(
"\n"
"partition : %u\n"
@@ -208,51 +233,58 @@
"type guid : %s\n"
"lba start : %" PRIu64 "\n"
"lba end : %" PRIu64 "\n"
- "flags : 0x%08X\n"
- "partition flags : 0x%08X\n",
+ "flags : 0x%016" PRIX64 "\n",
entrynum,
- partname,
+ wr > 0 ? partname : "",
partguid,
typeguid,
entry.first.lba,
entry.last.lba,
- entry.flags,
- entry.partflags
+ entry.flags
);
} else {
bintostr(partsize, SECTOR_TO_BYTE(entry.last.lba - entry.first.lba));
+ const char *gpt_type = gpt_part_type_str(&entry.type);
+ if (gpt_type == NULL)
+ gpt_type = "Unknown";
+
//TODO: GPT partition type (after name)
printf(
- "%4u. %12" PRIu64 "%12s s\n",
- entrynum, entry.first.lba, partsize
+ "%4u. %12" PRIu64 "%12s %s\n",
+ entrynum, entry.first.lba, partsize, gpt_type
);
if (wr > 0)
printf(" Name: %-36s", partname);
// GPT flags
- if (entry.flags & EFI_PE_PLATFORM_REQUIRED)
+ if (entry.flags & GPT_FLAG_PLATFORM_REQUIRED)
puts(" + Platform required");
- if (entry.flags & EFI_PE_EFI_FIRMWARE_IGNORE)
+ if (entry.flags & GPT_FLAG_EFI_FIRMWARE_IGNORE)
puts(" + Firmware ignore");
- if (entry.flags & EFI_PE_LEGACY_BIOS_BOOTABLE)
+ if (entry.flags & GPT_FLAG_LEGACY_BIOS_BOOTABLE)
puts(" + Legacy BIOS bootable");
- // Partition flags
- if (entry.partflags & EFI_PE_SUCCESSFUL_BOOT)
+ // Google flags
+ if (entry.flags & GPT_FLAG_SUCCESSFUL_BOOT)
puts(" + (Google) Successful boot");
- if (entry.partflags & EFI_PE_READ_ONLY)
- puts(" + (Microsoft) Read-only");
- if (entry.partflags & EFI_PE_SHADOW_COPY)
- puts(" + (Microsoft) Shadow copy");
- if (entry.partflags & EFI_PE_HIDDEN)
- puts(" + (Microsoft) Hidden");
+
+ // Microsoft flags
+ if (entry.flags & GPT_FLAG_READ_ONLY)
+ puts(" + (Microsoft) GPT_BASIC_DATA_ATTRIBUTE_READ_ONLY");
+ if (entry.flags & GPT_FLAG_SHADOW_COPY)
+ puts(" + (Microsoft) GPT_BASIC_DATA_ATTRIBUTE_SHADOW_COPY");
+ if (entry.flags & GPT_FLAG_HIDDEN)
+ puts(" + (Microsoft) GPT_BASIC_DATA_ATTRIBUTE_HIDDEN");
+ if (entry.flags & GPT_FLAG_NO_DRIVE_LETTER)
+ puts(" + (Microsoft) GPT_BASIC_DATA_ATTRIBUTE_NO_DRIVE_LETTER");
}
if (entrynum > gpt->pt_entries)
return;
+ if (gptbkp) --lba; else ++lba;
- ++lba; --max; ++entrynum;
+ ++entrynum;
goto START;
}
@@ -260,16 +292,17 @@
// vvd_info
//
-int vvd_info(VDISK *vd, uint32_t flags) {
+int vvd_info(VDISK *vd, struct settings_t *settings) {
const char *type; // vdisk type
char disksize[BINSTR_LENGTH], blocksize[BINSTR_LENGTH];
- char uid1[UID_LENGTH], uid2[UID_LENGTH], uid3[UID_LENGTH], uid4[UID_LENGTH];
+ char uid1[UID_BUFFER_LENGTH], uid2[UID_BUFFER_LENGTH],
+ uid3[UID_BUFFER_LENGTH], uid4[UID_BUFFER_LENGTH];
switch (vd->format) {
//
// VDI
//
- case VDISK_FORMAT_VDI: {
+ case VDISK_FORMAT_VDI:
switch (vd->vdi->v1.type) {
case VDI_DISK_DYN: type = "dynamic"; break;
case VDI_DISK_FIXED: type = "fixed"; break;
@@ -280,9 +313,9 @@
bintostr(disksize, vd->vdi->v1.capacity);
- if (flags & VVD_INFO_RAW) {
- char create_uuid[UID_LENGTH], modify_uuid[UID_LENGTH],
- link_uuid[UID_LENGTH], parent_uuid[UID_LENGTH];
+ if (settings->info.full) {
+ char create_uuid[UID_BUFFER_LENGTH], modify_uuid[UID_BUFFER_LENGTH],
+ link_uuid[UID_BUFFER_LENGTH], parent_uuid[UID_BUFFER_LENGTH];
bintostr(blocksize, vd->vdi->v1.blk_size);
uid_str(uid1, &vd->vdi->v1.uuidCreate, UID_UUID);
@@ -341,7 +374,6 @@
);
//TODO: Interpret flags
}
- }
break;
//
// VMDK
@@ -355,7 +387,7 @@
default: comp = "?";
}
- if (flags & VVD_INFO_RAW) {
+ if (settings->info.full) {
printf(
"disk format : VMDK\n"
"version : %u\n"
@@ -439,7 +471,7 @@
uid_str(uid1, &vd->vhd->hdr.uuid, UID_ASIS);
str_s(vd->vhd->hdr.creator_app, 4);
- if (flags & VVD_INFO_RAW) {
+ if (settings->info.full) {
printf(
"disk format : VHD\n"
"version : %u.%u\n"
@@ -464,7 +496,7 @@
uid1
);
if (vd->vhd->hdr.type != VHD_DISK_FIXED) {
- char paruuid[UID_LENGTH];
+ char paruuid[UID_BUFFER_LENGTH];
uid_str(paruuid, &vd->vhd->dyn.parent_uuid, UID_ASIS);
printf(
"dyn. header ver. : %u.%u\n"
@@ -501,7 +533,7 @@
break;
// case VDISK_FORMAT_VHDX:
case VDISK_FORMAT_QED:
- if (flags & VVD_INFO_RAW) {
+ if (settings->info.full) {
printf(
"disk format : QED\n"
"cluster size : %u\n"
@@ -532,12 +564,16 @@
printf("QEMU Enhanced Disk, %s\n", disksize);
}
break;
- case VDISK_FORMAT_RAW: break; // No header info
+ case VDISK_FORMAT_RAW:
+// VVDTRACE("VDISK_FORMAT_RAW");
+ goto L_MBR; // No header info
default:
fputs("vvd_info: Format not supported\n", stderr);
return VVD_EVDFORMAT;
}
+ putchar('\n');
+
//TODO: BSD disklabel detection
//TODO: SGI disklabel detection
@@ -545,41 +581,57 @@
// MBR detection
//
- MBR mbr;
- if (vdisk_read_sector(vd, &mbr, 0)) return EXIT_SUCCESS;
- if (mbr.sig != MBR_SIG) return EXIT_SUCCESS;
- vvd_info_mbr(&mbr, flags);
+ union {
+ MBR mbr;
+ GPT gpt;
+ } label;
+
+L_MBR:
+
+ if (vdisk_read_sector(vd, &label, 0)) {
+ #if TRACE
+ VVDTRACE("vdisk_read_sector failed");
+ #endif
+ return EXIT_SUCCESS;
+ }
+ if (label.mbr.sig != MBR_SIG) {
+ #if TRACE
+ VVDTRACE("label.mbr.sig != MBR_SIG");
+ #endif
+ return EXIT_SUCCESS;
+ }
+ vvd_info_mbr(&label.mbr, settings);
//
// Extended MBR detection (EBR)
//
- uint64_t ebrlba;
+ uint64_t elba; // Extended MBR LBA
for (int i = 0; i < 4; ++i) {
- switch (mbr.pe[i].type) {
+ switch (label.mbr.pe[i].type) {
case 0xEE: // EFI GPT Protective
case 0xEF: // EFI System Partition
// Start of disk
- if (vdisk_read_sector(vd, &mbr, 1)) return VVD_EOK;
- if (((GPT*)&mbr)->sig == EFI_SIG) {
- ebrlba = 2;
+ if (vdisk_read_sector(vd, &label, 1)) return VVD_EOK;
+ if (label.gpt.sig == EFI_SIG) {
+ elba = 2;
goto L_GPT_RDY;
}
// End of disk
- ebrlba = BYTE_TO_SECTOR(vd->capacity) - 1;
- if (vdisk_read_sector(vd, &mbr, ebrlba)) return VVD_EOK;
- if (((GPT*)&mbr)->sig == EFI_SIG) {
- ebrlba -= ((GPT*)&mbr)->pt_entries; // typically 128
+ elba = BYTE_TO_SECTOR(vd->capacity) - 1;
+ if (vdisk_read_sector(vd, &label, elba)) return VVD_EOK;
+ if (label.gpt.sig == EFI_SIG) {
+ settings->internal.gpt_bkp = 1;
goto L_GPT_RDY;
}
continue;
L_GPT_RDY:
- vvd_info_gpt((GPT*)&mbr, flags);
- vvd_info_gpt_entries(vd, (GPT*)&mbr, ebrlba, flags);
+ vvd_info_gpt(&label.gpt, settings);
+ vvd_info_gpt_entries(vd, &label.gpt, elba, settings);
continue;
}
}
-
+
return EXIT_SUCCESS;
}
@@ -587,88 +639,109 @@
// vvd_map
//
-int vvd_map(VDISK *vd, uint32_t flags) {
- char bsizestr[BINSTR_LENGTH]; // If used
+int vvd_map(VDISK *vd, struct settings_t *settings) {
+ //TODO: Iterator interface
+ union {
+ uint32_t *u32;
+ uint64_t *u64;
+ } btable; // block table
+ union {
+ uint32_t u32;
+ uint64_t u64;
+ } bcount; // block table count
+ union {
+ uint32_t u32;
+ uint64_t u64;
+ } bsize; // block index size in bytes
- puts("vvd_map: to be implemented");
-
- /*switch (vd->format) {
+ switch (vd->format) {
case VDISK_FORMAT_VDI:
- bcount = vd->vdiv1.blk_total;
- bsize = vd->vdiv1.blocksize;
+ btable.u32 = vd->vdi->in.offsets;
+ bcount.u32 = vd->vdi->v1.blk_total;
+ bsize.u32 = vd->vdi->v1.blk_size;
break;
case VDISK_FORMAT_VHD:
- if (vd->vhdhdr.type != VHD_DISK_DYN) {
+ if (vd->vhd->hdr.type != VHD_DISK_DYN) {
fputs("vvd_map: vdisk is not dynamic\n", stderr);
return VVD_EVDTYPE;
}
- bcount = vd->u32blockcount;
- bsize = vd->vhddyn.blocksize;
+ btable.u32 = vd->vhd->in.offsets;
+ bcount.u32 = vd->vhd->dyn.max_entries;
+ bsize.u32 = vd->vhd->dyn.blocksize;
break;
case VDISK_FORMAT_QED:
- index64 = 1;
- bcount = vd->u32blockcount;
- bsize = vd->qedhdr.cluster_size;
- break;
+ btable.u64 = vd->qed->in.L1.offsets;
+ bcount.u64 = vd->qed->in.entries;
+ bsize.u64 = vd->qed->hdr.cluster_size;
+ goto L_USES_64BIT_INDEX;
default:
fputs("vvd_map: unsupported format\n", stderr);
return VVD_EVDFORMAT;
}
- bintostr(bsizestr, bsize);
+ char bsizestr[BINSTR_LENGTH];
size_t i = 0;
- size_t bn;
- if (index64) {
+ size_t bn; // Block table limit
+
+ // 32-bit offsets
+
+ bintostr(bsizestr, bsize.u32);
+ printf(
+ "Allocation map: %u blocks of %s each\n"
+ " offset d | 0 | 1 | 2 | 3 |"
+ " 4 | 5 | 6 | 7 |\n"
+ "----------+----------+----------+----------+----------+"
+ "----------+----------+----------+----------+\n",
+ bcount.u32, bsizestr
+ );
+ bn = bcount.u32 - 8;
+ for (; i < bn; i += 8) {
printf(
- "Allocation map: %u blocks to %s blocks\n"
- " offset d | 0 | 1 |"
- " 2 | 3 |\n"
- "----------+------------------+------------------+"
- "------------------+------------------+\n",
- bcount, bsizestr
+ " %8zu | %8X | %8X | %8X | %8X | %8X | %8X | %8X | %8X |\n",
+ i,
+ btable.u32[i], btable.u32[i + 1],
+ btable.u32[i + 2], btable.u32[i + 3],
+ btable.u32[i + 4], btable.u32[i + 5],
+ btable.u32[i + 6], btable.u32[i + 7]
);
- bn = bcount - 4;
- for (; i < bn; i += 4) {
- printf(
- " %8zu | %16"PRIX64" | %16"PRIX64" | %16"PRIX64" | %16"PRIX64" |\n",
- i,
- vd->u64block[i], vd->u64block[i + 1],
- vd->u64block[i + 2], vd->u64block[i + 3]
- );
- }
- if (bcount - i > 0) { // Left over
- printf(" %8zu |", i);
- for (; i < bcount; ++i)
- printf(" %16"PRIX64" |", vd->u64block[i]);
- putchar('\n');
- }
- } else {
+ }
+ if (bcount.u32 - i > 0) { // Left over
+ printf(" %8zu |", i);
+ for (; i < bcount.u32; ++i)
+ printf(" %8X |", btable.u32[i]);
+ putchar('\n');
+ }
+
+ return EXIT_SUCCESS;
+
+ // 64-bit offsets
+
+L_USES_64BIT_INDEX:
+
+ bintostr(bsizestr, bsize.u64);
+ printf(
+ "Allocation map: %" PRIu64 " blocks to %s blocks\n"
+ " offset d | 0 | 1 |"
+ " 2 | 3 |\n"
+ "----------+------------------+------------------+"
+ "------------------+------------------+\n",
+ bcount.u64, bsizestr
+ );
+ bn = bcount.u64 - 4;
+ for (; i < bn; i += 4) {
printf(
- "Allocation map: %u blocks of %s each\n"
- " offset d | 0 | 1 | 2 | 3 |"
- " 4 | 5 | 6 | 7 |\n"
- "----------+----------+----------+----------+----------+"
- "----------+----------+----------+----------+\n",
- bcount, bsizestr
+ " %8zu | %16"PRIX64" | %16"PRIX64" | %16"PRIX64" | %16"PRIX64" |\n",
+ i,
+ btable.u64[i], btable.u64[i + 1],
+ btable.u64[i + 2], btable.u64[i + 3]
);
- bn = bcount - 8;
- for (; i < bn; i += 8) {
- printf(
- " %8zu | %8X | %8X | %8X | %8X | %8X | %8X | %8X | %8X |\n",
- i,
- vd->u32block[i], vd->u32block[i + 1],
- vd->u32block[i + 2], vd->u32block[i + 3],
- vd->u32block[i + 4], vd->u32block[i + 5],
- vd->u32block[i + 6], vd->u32block[i + 7]
- );
- }
- if (bcount - i > 0) { // Left over
- printf(" %8zu |", i);
- for (; i < bcount; ++i)
- printf(" %8X |", vd->u32block[i]);
- putchar('\n');
- }
- }*/
+ }
+ if (bcount.u64 - i > 0) { // Left over
+ printf(" %8zu |", i);
+ for (; i < bcount.u64; ++i)
+ printf(" %16"PRIX64" |", btable.u64[i]);
+ putchar('\n');
+ }
return EXIT_SUCCESS;
}
@@ -677,10 +750,11 @@
// vvd_new
//
-int vvd_new(const oschar *path, uint32_t format, uint64_t capacity, uint32_t flags) {
+int vvd_new(const oschar *path, uint32_t format, uint64_t capacity, struct settings_t *settings) {
VDISK vd;
+ uint16_t flags = 0;
if (vdisk_create(&vd, path, format, capacity, flags)) {
- vdisk_perror(&vd);
+ vvd_perror(&vd);
return vd.err.num;
}
printf("vvd_new: %s disk created successfully\n", vdisk_str(&vd));
@@ -694,8 +768,8 @@
int vvd_compact(VDISK *vd, uint32_t flags) {
puts("vvd_compact: [warning] This function is still work in progress");
g_flags = flags;
- if (vdisk_op_compact(vd, vvd_cb_progress)) {
- vdisk_perror(vd);
+ if (vdisk_op_compact(vd)) {
+ vvd_perror(vd);
return vd->err.num;
}
return EXIT_SUCCESS;
diff --git a/src/vvd.h b/src/vvd.h
index bca971b..dafc7ef 100644
--- a/src/vvd.h
+++ b/src/vvd.h
@@ -1,41 +1,44 @@
-#include "vdisk.h"
+#include "vdisk/vdisk.h"
-// Flags for vvd_* functions which the CLI should populate
-// The lower 16 bits is for "generic" flags and the higher
-// 16 bits is for function-specific flags:
-//
-// bits 31 16 0
-// +--------+--------+--------+--------+
-// | Func. specific | Generic flags |
-// +--------+--------+--------+--------+
-enum {
- // Show a progress bar
- VVD_PROGRESS = 0x10,
- // vvd_info: Show raw information
- VVD_INFO_RAW = 0x10000,
- // vvd_map flags
- //VVD_MAP_ = 0x1000,
- // vvd_compact flags
- //VVD_COMPACT_CLEAN_EMPTY = 0x10000,
+// Command-line options
+struct settings_t {
+ uint64_t vsize; // Virtual disk size, used in 'new' and 'resize'
+ char progressbar; // Show progress bar
+ char verbose; // Verbose level
+ struct settings_info_t {
+ char raw; // Show raw, unformatted data values
+ char full; // Show all fields instead of a summary
+ } info;
+ struct settings_vdisk_t {
+ uint32_t flags;
+ } vdisk;
+ struct settings_internals_t {
+ char gpt_bkp; // Uh uh, go fetch the backup GPT header!
+ } internal;
};
/**
+ * Print VDISK error.
+ */
+void vvd_perror(VDISK *vd);
+
+/**
* Print VDISK information to stdout.
*
* This includes information about the VDISK format and type, MBR, GPT, and
* when available, the operating system filesystem.
*/
-int vvd_info(VDISK *vd, uint32_t flags);
+int vvd_info(VDISK *vd, struct settings_t *);
/**
* Print VDISK allocation map to stdout.
*/
-int vvd_map(VDISK *vd, uint32_t flags);
+int vvd_map(VDISK *vd, struct settings_t *);
/**
*
*/
-int vvd_new(const oschar *vd, uint32_t format, uint64_t capacity, uint32_t flags);
+int vvd_new(const oschar *vd, uint32_t format, uint64_t capacity, struct settings_t *);
/**
* Compact a VDISK.
diff --git a/utils/compute_hashes.cmd b/utils/compute_hashes.cmd
new file mode 100644
index 0000000..061681b
--- /dev/null
+++ b/utils/compute_hashes.cmd
@@ -0,0 +1,10 @@
+@ECHO OFF
+
+CD ..
+
+FOR /F %%G IN (utils\gpt_types) DO (
+ vvd internalguidhash %%G >> utils\gpt_hashes
+ IF ERRORLEVEL 1 GOTO :EOF
+)
+
+CD utils
\ No newline at end of file
diff --git a/utils/gpt_types b/utils/gpt_types
new file mode 100644
index 0000000..82a0c31
--- /dev/null
+++ b/utils/gpt_types
@@ -0,0 +1,138 @@
+00000000-0000-0000-0000-000000000000
+024DEE41-33E7-11D3-9D69-0008C781F39F
+C12A7328-F81F-11D2-BA4B-00A0C93EC93B
+21686148-6449-6E6F-744E-656564454649
+D3BFE2DE-3DAF-11DF-BA40-E3A556D89593
+F4019732-066E-4E12-8273-346C5641494F
+BFBFAFE7-A34F-448A-9A5B-6213EB736C22
+E3C9E316-0B5C-4DB8-817D-F92DF00215AE
+EBD0A0A2-B9E5-4433-87C0-68B6B72699C7
+5808C8AA-7E8F-42E0-85D2-E1E90434CFB3
+AF9B60A0-1431-4F62-BC68-3311714A69AD
+DE94BBA4-06D1-4D40-A16A-BFD50179D6AC
+37AFFC90-EF7D-4E96-91C3-2D7AE055B174
+E75CAF8F-F680-4CEE-AFA3-B001E56EFC2D
+558D43C5-A1AC-43C0-AAC8-D1472B2923D1
+75894C1E-3AEB-11D3-B7C1-7B03A0000000
+E2A1E728-32E3-11D6-A682-7B03A0000000
+0FC63DAF-8483-4772-8E79-3D69D8477DE4
+A19D880F-05FC-4D3B-A006-743F0F84911E
+44479540-F297-41B2-9AF7-D131D5F0458A
+4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709
+69DAD710-2CE4-4E3C-B16C-21A1D49ABED3
+B921B045-1DF0-41C3-AF44-4C6F280D3FAE
+BC13C2FF-59E6-4262-A352-B275FD6F7172
+0657FD6D-A4AB-43C4-84E5-0933C84B4F4F
+E6D6D379-F507-44C2-A23C-238F2A3DF928
+933AC7E1-2EB4-4F13-B844-0E14E2AEF915
+3B8F8425-20E0-4F3B-907F-1A25A76F98E8
+7FFEC5C9-2D00-49B7-8941-3EA10A5586B7
+CA7D7CCB-63ED-4C53-861C-1742536059CC
+8DA63339-0007-60C0-C436-083AC8230908
+83BD6B9D-7F41-11DC-BE0B-001560B84F0F
+516E7CB4-6ECF-11D6-8FF8-00022D09712B
+516E7CB5-6ECF-11D6-8FF8-00022D09712B
+516E7CB6-6ECF-11D6-8FF8-00022D09712B
+516E7CB8-6ECF-11D6-8FF8-00022D09712B
+516E7CBA-6ECF-11D6-8FF8-00022D09712B
+48465300-0000-11AA-AA11-00306543ECAC
+7C3457EF-0000-11AA-AA11-00306543ECAC
+55465300-0000-11AA-AA11-00306543ECAC
+6A898CC3-1DD2-11B2-99A6-080020736631
+52414944-0000-11AA-AA11-00306543ECAC
+52414944-5F4F-11AA-AA11-00306543ECAC
+426F6F74-0000-11AA-AA11-00306543ECAC
+4C616265-6C00-11AA-AA11-00306543ECAC
+5265636F-7665-11AA-AA11-00306543ECAC
+53746F72-6167-11AA-AA11-00306543ECAC
+B6FA30DA-92D2-4A9A-96F1-871EC6486200
+2E313465-19B9-463F-8126-8A7993773801
+FA709C7E-65B1-4593-BFD5-E71D61DE9B02
+BBBA6DF5-F46F-4A89-8F59-8765B2727503
+6A82CB45-1DD2-11B2-99A6-080020736631
+6A85CF4D-1DD2-11B2-99A6-080020736631
+6A87C46F-1DD2-11B2-99A6-080020736631
+6A8B642B-1DD2-11B2-99A6-080020736631
+6A898CC3-1DD2-11B2-99A6-080020736631
+6A8EF2E9-1DD2-11B2-99A6-080020736631
+6A90BA39-1DD2-11B2-99A6-080020736631
+6A9283A5-1DD2-11B2-99A6-080020736631
+6A945A3B-1DD2-11B2-99A6-080020736631
+6A9630D1-1DD2-11B2-99A6-080020736631
+6A980767-1DD2-11B2-99A6-080020736631
+6A96237F-1DD2-11B2-99A6-080020736631
+6A8D2AC7-1DD2-11B2-99A6-080020736631
+49F48D32-B10E-11DC-B99B-0019D1879648
+49F48D5A-B10E-11DC-B99B-0019D1879648
+49F48D82-B10E-11DC-B99B-0019D1879648
+49F48DAA-B10E-11DC-B99B-0019D1879648
+2DB519C4-B10F-11DC-B99B-0019D1879648
+2DB519EC-B10F-11DC-B99B-0019D1879648
+FE3A2A5D-4F32-41A7-B725-ACCC3285A309
+3CB8E202-3B7E-47DD-8A3C-7FF2A13CFCEC
+2E0A753D-9E48-43B0-8337-B15192CB1B5E
+5DFBF5F4-2848-4BAC-AA5E-0D9A20B745A6
+3884DD41-8582-4404-B9A8-E9B84F2DF50E
+C95DC21A-DF0E-4340-8D7B-26CBFA9A03E0
+BE9067B9-EA49-4F15-B4F6-F36F8C9E1818
+42465331-3BA3-10F1-802A-4861696B7521
+85D5E45E-237C-11E1-B4B3-E89A8F7FC3A7
+85D5E45A-237C-11E1-B4B3-E89A8F7FC3A7
+85D5E45B-237C-11E1-B4B3-E89A8F7FC3A7
+0394EF8B-237E-11E1-B4B3-E89A8F7FC3A7
+85D5E45C-237C-11E1-B4B3-E89A8F7FC3A7
+85D5E45D-237C-11E1-B4B3-E89A8F7FC3A7
+45B0969E-9B03-4F30-B4C6-B4B80CEFF106
+45B0969E-9B03-4F30-B4C6-5EC00CEFF106
+4FBD7E29-9D25-41B8-AFD0-062C0CEFF05D
+4FBD7E29-9D25-41B8-AFD0-5EC00CEFF05D
+89C57F98-2FE5-4DC0-89C1-F3AD0CEFF2BE
+89C57F98-2FE5-4DC0-89C1-5EC00CEFF2BE
+CAFECAFE-9B03-4F30-B4C6-B4B80CEFF106
+30CD0809-C2B2-499C-8879-2D6B78529876
+5CE17FCE-4087-4169-B7FF-056CC58473F9
+FB3AABF9-D25F-47CC-BF5E-721D1816496B
+4FBD7E29-8AE0-4982-BF9D-5A8D867AF560
+45B0969E-8AE0-4982-BF9D-5A8D867AF560
+CAFECAFE-8AE0-4982-BF9D-5A8D867AF560
+7F4A666A-16F3-47A2-8445-152EF4D03F6C
+EC6D6385-E346-45DC-BE91-DA2A7C8B3261
+01B41E1B-002A-453C-9F17-88793989FF8F
+CAFECAFE-9B03-4F30-B4C6-5EC00CEFF106
+93B0052D-02D9-4D8A-A43B-33A3EE4DFBC3
+306E8683-4FE2-4330-B7C0-00A917C16966
+45B0969E-9B03-4F30-B4C6-35865CEFF106
+CAFECAFE-9B03-4F30-B4C6-35865CEFF106
+166418DA-C469-4022-ADF4-B30AFD37F176
+86A32090-3647-40B9-BBBD-38D8C573AA86
+4FBD7E29-9D25-41B8-AFD0-35865CEFF05D
+824CC7A0-36A8-11E3-890A-952519AD3F61
+CEF5A9AD-73BC-4601-89F3-CDEEEEE321A1
+C91818F9-8025-47AF-89D2-F030D7000C2C
+9D275380-40AD-11DB-BF97-000C2911D1B8
+AA31E02A-400F-11DB-9590-000C2911D1B8
+9198EFFC-31C0-11DB-8F78-000C2911D1B8
+2568845D-2332-4675-BC39-8FA5A4748D15
+114EAFFE-1552-4022-B26E-9B053604CF84
+49A4D17F-93A3-45C1-A0DE-F50B2EBE2599
+4177C722-9E92-4AAB-8644-43502BFD5506
+EF32A33B-A409-486C-9141-9FFB711F6266
+20AC26BE-20B7-11E3-84C5-6CFDB94711E9
+38F428E6-D326-425D-9140-6E0EA133647C
+A893EF21-E428-470A-9E55-0668FD91A2D9
+DC76DDA9-5AC1-491C-AF42-A82591580C0D
+EBC597D0-2053-4B15-8B64-E0AAC75F4DB1
+C5A0AEEC-13EA-11E5-A1B1-001E67CA0C3C
+BD59408B-4514-490D-BF12-9878D963F378
+8F68CC74-C5E5-48DA-BE91-A0C8C15E9C80
+9FDAA6EF-4B3F-40D2-BA8D-BFF16BFB887B
+767941D0-2085-11E3-AD3B-6CFDB94711E9
+AC6D7924-EB71-4DF8-B48D-E267B27148FF
+19A710A2-B3CA-11E4-B026-10604B889DCF
+193D1EA4-B3CA-11E4-B075-10604B889DCF
+7412F7D5-A156-4B13-81DC-867174929325
+D4E6E2CD-4469-46F3-B5CB-1BFF57AFC149
+9E1A2D38-C612-4316-AA26-8B49521E5A8B
+BC13C2FF-59E6-4262-A352-B275FD6F7172
+734E5AFE-F61A-11E6-BC64-92361F002671
+8C8F8EFF-AC95-4770-814A-21994F2DBC8F
\ No newline at end of file