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