patterns/fs: Refactor all partition types into a FS module

This commit is contained in:
Nik
2025-05-25 19:51:32 +02:00
committed by GitHub
parent d96bfbb942
commit db4d62aa20
9 changed files with 366 additions and 99 deletions

311
patterns/fs/exfat.hexpat Normal file
View File

@@ -0,0 +1,311 @@
#pragma magic [45 58 46 41 54 20 20 20] @ 0x03
#pragma description Extensible File Allocation Table (exFAT)
#pragma author Khoo Hao Yit
import std.mem;
import std.string;
import std.core;
#pragma eval_depth 0
struct BootSector {
std::mem::Bytes<3> jumpBoot;
char fileSystemName[8];
std::mem::Bytes<53> mustBeZero;
u64 partitionOffset;
u64 volumeLength;
u32 fatOffset;
u32 fatLength;
u32 clusterHeapOffset;
u32 clusterCount;
u32 firstClusterOfRootDirectory;
u32 volumeSerialNumber;
u16 fileSystemRevision;
u16 volumeFlags;
u8 bytesPerSectorShift;
u8 sectorsPerClusterShift;
u8 numberOfFats;
u8 driveSelect;
u8 percentInUse;
std::mem::Bytes<7> reserved;
std::mem::Bytes<390> bootCode;
std::mem::Bytes<2> bootSignature;
};
struct ExtendedBootSector {
std::mem::Bytes<bytesPerSector - 4> bootCode;
std::mem::Bytes<4> bootSignature;
};
struct Parameter {
std::mem::Bytes<16> guid;
std::mem::Bytes<32> data;
};
struct OemParameter {
Parameter parameters[10];
std::mem::Bytes<bytesPerSector - 480> reserved;
};
u64 bytesPerSector;
u64 startOfClusterHeap;
u64 bytesPerCluster;
u64 fatAddress;
struct BootRegion {
BootSector bootSector;
bytesPerSector = 1 << bootSector.bytesPerSectorShift;
bytesPerCluster = bytesPerSector << bootSector.sectorsPerClusterShift;
ExtendedBootSector extendedBootSectors[8];
OemParameter oemParameter;
std::mem::Bytes<bytesPerSector> reservedSector;
std::mem::Bytes<bytesPerSector> bootChecksum;
};
struct VolumeLabelEntry {
u8 entryType;
u8 characterCount;
char volumeLabel[22];
std::mem::Bytes<8> reserved;
};
bitfield FileAttribute {
readOnly : 1;
hidden : 1;
system : 1;
reserved : 1;
directory : 1;
archive : 1;
reserved1 : 10;
};
auto currentClusterIndex = -1;
auto currentClusterSize = -1;
auto currentIsDirectory = -1;
struct FileEntry {
u8 entryType;
u8 secondayEntryCount;
u16 checksum;
FileAttribute attributes;
std::mem::Bytes<2> reserved;
u32 creationDatetime;
u32 modificationDatetime;
u32 accessDatetime;
u8 creationTimeHundredths;
u8 modificationTimeHundredths;
u8 creationUtcOffset;
u8 modificationUtcOffset;
u8 accessUtcOffset;
std::mem::Bytes<7> reserved1;
currentIsDirectory = attributes.directory;
};
bitfield GeneralSecondaryFlags {
allocationPossible : 1;
noFatChain : 1;
customDefined : 6;
};
struct FileNameEntry {
u8 entryType;
u8 flags;
char16 name[15];
};
fn divideCeil(auto a, auto b) {
auto result = a / b;
auto remain = a % b;
if (remain) {
result += 1;
}
return result;
};
struct ContinuousFatRange<auto ClusterIndex, auto ClusterSize> {
u32 fatRange[ClusterSize] @ fatAddress + ClusterIndex * 4 [[sealed]];
} [[name(std::format("ContinuousFatRange<{}, {}>", ClusterIndex, ClusterSize))]];
struct FatChain {
auto clusterIndex = currentClusterIndex;
auto clusterSize = currentClusterSize;
currentClusterIndex = -1;
currentClusterSize = -1;
if (clusterIndex == -1) {
std::error(std::format("Invalid cluster index: {}", clusterIndex));
}
if (clusterSize == -1) {
auto startingFatRange = fatAddress + clusterIndex * 4;
u32 clusters[while(
$ == startingFatRange
|| $ == fatAddress + std::mem::read_unsigned($ - 4, 4) * 4
)] @ startingFatRange [[hidden]];
clusterSize = std::core::member_count(clusters);
}
ContinuousFatRange<clusterIndex, clusterSize> data @ 0;
try {
auto nextClusterIndex = clusters[clusterSize - 1];
if (nextClusterIndex != 0xffffffff && nextClusterIndex != 0) {
currentClusterIndex = nextClusterIndex;
FatChain next @ 0 [[inline]];
}
}
} [[name(std::format("FatChain<{}>", clusterIndex))]];
struct ContinuousDataRange<auto ClusterIndex, auto ClusterSize> {
std::mem::Bytes<ClusterSize * bytesPerCluster> dataRange @
startOfClusterHeap + ClusterIndex * bytesPerCluster;
} [[name(std::format("ContinuousDataRange<{}, {}>", ClusterIndex, ClusterSize))]];
struct DataChain {
auto clusterIndex = currentClusterIndex;
auto clusterSize = currentClusterSize;
currentClusterIndex = -1;
currentClusterSize = -1;
if (clusterIndex == -1) {
std::error(std::format("Invalid cluster index: {}", clusterIndex));
}
if (clusterSize == -1) {
auto startingFatRange = fatAddress + clusterIndex * 4;
u32 clusters[while(
$ == startingFatRange
|| $ == fatAddress + std::mem::read_unsigned($ - 4, 4) * 4
)] @ startingFatRange [[hidden]];
clusterSize = std::core::member_count(clusters);
}
ContinuousDataRange<clusterIndex, clusterSize> data @ 0;
try {
auto nextClusterIndex = clusters[clusterSize - 1];
if (nextClusterIndex != 0xffffffff && nextClusterIndex != 0) {
currentClusterIndex = nextClusterIndex;
DataChain next @ 0 [[inline]];
}
}
} [[name(std::format("DataChain<{}>", clusterIndex))]];
struct UpcaseTableEntry {
u8 entryType;
std::mem::Bytes<3> reserved;
u32 tableChecksum;
std::mem::Bytes<12> reserved1;
u32 firstCluster;
u64 dataLength;
ContinuousFatRange<firstCluster, 1> fatRange;
ContinuousDataRange<firstCluster, 1> dataRange;
};
struct AllocationBitmapEntry {
u8 entryType;
u8 bitmapFlags;
std::mem::Bytes<18> reserved;
u32 firstCluster;
u64 dataLength;
ContinuousFatRange<firstCluster, 1> fatRange;
ContinuousDataRange<firstCluster, 1> dataRange;
};
using StreamExtensionEntry;
struct DirectoryEntry {
u8 entryType @ addressof(this) [[hidden]];
match (entryType) {
(0x83 | 0x03): VolumeLabelEntry;
(0x81): AllocationBitmapEntry;
(0x82): UpcaseTableEntry;
(0x85 | 0x05): FileEntry;
(0xc0 | 0x40): StreamExtensionEntry;
(0xc1 | 0x41): FileNameEntry;
(_): std::mem::Bytes<32> [[name(std::format("UnknownEntry @ {:#X}", addressof(this)))]];
}
} [[inline]];
struct ContinuousDirectoryEntry<auto ClusterIndex, auto ClusterSize> {
DirectoryEntry entry[ClusterSize * bytesPerCluster / 32] @
startOfClusterHeap + ClusterIndex * bytesPerCluster
[[inline]];
} [[name(std::format("ContinuousDirectoryEntry<{}, {}>", ClusterIndex, ClusterSize))]];
struct DirectoryEntryChain {
auto clusterIndex = currentClusterIndex;
auto clusterSize = currentClusterSize;
currentClusterIndex = -1;
currentClusterSize = -1;
if (clusterIndex == -1) {
std::error(std::format("Invalid cluster index: {}", clusterIndex));
}
if (clusterSize == -1) {
auto startingFatRange = fatAddress + clusterIndex * 4;
u32 clusters[while(
$ == startingFatRange
|| $ == fatAddress + std::mem::read_unsigned($ - 4, 4) * 4
)] @ startingFatRange [[hidden]];
clusterSize = std::core::member_count(clusters);
}
ContinuousDirectoryEntry<clusterIndex, clusterSize> data @ 0;
try {
auto nextClusterIndex = clusters[clusterSize - 1];
if (nextClusterIndex != 0xffffffff && nextClusterIndex != 0) {
currentClusterIndex = nextClusterIndex;
DirectoryEntryChain next @ 0 [[inline]];
}
}
currentIsDirectory = -1;
} [[name(std::format("DirectoryEntryChain<{}>", clusterIndex))]];
struct StreamExtensionEntry {
u8 entryType;
GeneralSecondaryFlags secondaryFlags;
std::mem::Bytes<1> reserved;
u8 nameLength;
u16 nameHash;
std::mem::Bytes<2> reserved1;
u64 validDateLength;
std::mem::Bytes<4> reserved2;
u32 firstCluster;
u64 dataLength;
if (entryType & 0x80 && currentIsDirectory == 1) {
currentClusterIndex = firstCluster;
if (secondaryFlags.noFatChain) {
currentClusterSize = divideCeil(dataLength, bytesPerCluster);
}
FatChain fatChain;
currentClusterIndex = firstCluster;
if (secondaryFlags.noFatChain) {
currentClusterSize = divideCeil(dataLength, bytesPerCluster);
}
DirectoryEntryChain directoryChain;
}
else if (dataLength) {
currentClusterIndex = firstCluster;
if (secondaryFlags.noFatChain) {
currentClusterSize = divideCeil(dataLength, bytesPerCluster);
}
FatChain fatChain;
currentClusterIndex = firstCluster;
if (secondaryFlags.noFatChain) {
currentClusterSize = divideCeil(dataLength, bytesPerCluster);
}
DataChain data @ 0;
}
};
BootRegion bootRegion @ 0 [[name("BootRegion")]];
BootRegion backupBootRegion @ 12 * bytesPerSector [[name("BackupBootRegion")]];
startOfClusterHeap =
bootRegion.bootSector.clusterHeapOffset * bytesPerSector
- 2 * bytesPerCluster;
fatAddress =
bootRegion.bootSector.fatOffset * bytesPerSector;
std::mem::Bytes<bootRegion.bootSector.fatLength * bytesPerSector> fat @ fatAddress [[hidden]];
currentClusterIndex = bootRegion.bootSector.firstClusterOfRootDirectory;
FatChain rootDirectoryFatChain @ 0;
currentClusterIndex = bootRegion.bootSector.firstClusterOfRootDirectory;
DirectoryEntryChain rootDirectory @ 0;

543
patterns/fs/ext4.hexpat Normal file
View File

@@ -0,0 +1,543 @@
#pragma author endes
#pragma description ext4 volume layout parser (until inodes)
// Decodes the ext4 superblock, group descriptors and inodes.
// Does not decode the directory entries, inode data, jornal or superblock backups.
// Heavily based on the linux kernel documentation:
// https://www.kernel.org/doc/html/latest/filesystems/ext4/
#pragma endian little
#pragma magic [53 EF] @ 0x438
#pragma pattern_limit 0x90000
import type.time;
import type.size;
import type.magic;
import std.core;
import std.mem;
import std.math;
enum ext4_super_state : u16 {
Cleanlyumounted = 0x01,
Errorsdetected = 0x02,
Orphansbeingrecovered = 0x03
};
enum ext4_super_errors : u16 {
Continue = 0x01,
RemountReadOnly = 0x02,
Panic = 0x03
};
enum ext4_super_creator : u32 {
Linux = 0x00,
Hurd = 0x01,
Masix = 0x02,
FreeBSD = 0x03,
Lites = 0x04
};
enum ext4_super_revision : u32 {
ORIGINAL = 0x00,
V2_DYNAMIC_REV = 0x01
};
bitfield ext4_super_compat {
COMPAT_DIR_PREALLOC : 1 [[comment("Directory preallocation.")]];
COMPAT_IMAGIC_INODES : 1 [[comment("“imagic inodes”. Not clear from the code what this does.")]];
COMPAT_HAS_JOURNAL : 1 [[comment("Has a journal.")]];
COMPAT_EXT_ATTR : 1 [[comment("Supports extended attributes.")]];
COMPAT_RESIZE_INODE : 1 [[comment("Has reserved GDT blocks for filesystem expansion.")]];
COMPAT_DIR_INDEX : 1 [[comment("Has directory indices.")]];
COMPAT_LAZY_BG : 1 [[comment("“Lazy BG”. Not in Linux kernel, seems to have been for uninitialized block groups?.")]];
COMPAT_EXCLUDE_INODE : 1 [[comment("“Exclude inode”. Not used.")]];
COMPAT_EXCLUDE_BITMAP : 1 [[comment("“Exclude bitmap”. Seems to be used to indicate the presence of snapshot-related exclude bitmaps? Not defined in linux kernel or used in e2fsprogs.")]];
COMPAT_SPARSE_SUPER2 : 1 [[comment("Sparse Super Block, v2. If this flag is set, the SB field s_backup_bgs points to the two block groups that contain backup superblocks.")]];
COMPAT_FAST_COMMIT : 1 [[comment("Journal fast commits supported.")]];
padding : 1;
RO_COMPAT_ORPHAN_PRESENT : 1 [[comment("Orphan file allocated. This is the special file for more efficient tracking of unlinked but still open inodes.")]];
};
bitfield ext4_super_incompat {
INCOMPAT_COMPRESSION : 1;
INCOMPAT_FILETYPE : 1 [[comment("Directory entries record the file type.")]];
INCOMPAT_RECOVER : 1 [[comment("Filesystem needs recovery.")]];
INCOMPAT_JOURNAL_DEV : 1 [[comment("Filesystem has a separate journal device.")]];
INCOMPAT_META_BG : 1 [[comment("Meta block groups.")]];
padding : 1;
INCOMPAT_EXTENTS : 1 [[comment("Files in this filesystem use extents.")]];
INCOMPAT_64BIT : 1 [[comment("Enable a filesystem size of 2^64 blocks.")]];
INCOMPAT_MMP : 1 [[comment("Multiple mount protection.")]];
INCOMPAT_FLEX_BG : 1 [[comment("Flexible block groups.")]];
INCOMPAT_EA_INODE : 1 [[comment("Inodes can be used to store large extended attribute values.")]];
padding : 1 [[comment("Data in directory entry.")]];
INCOMPAT_DIRDATA : 1;
INCOMPAT_CSUM_SEED : 1 [[comment("Metadata checksum seed is stored in the superblock.")]];
INCOMPAT_LARGEDIR : 1 [[comment("Large directory >2GB or 3-level htree.")]];
INCOMPAT_INLINE_DATA : 1 [[comment("Data in inode.")]];
INCOMPAT_ENCRYPT : 1 [[comment("Encrypted inodes are present on the filesystem.")]];
};
bitfield ext4_super_compat_ro {
RO_COMPAT_SPARSE_SUPER : 1 [[comment("Sparse superblocks.")]];
RO_COMPAT_LARGE_FILE : 1 [[comment("This filesystem has been used to store a file greater than 2GiB.")]];
RO_COMPAT_BTREE_DIR : 1 [[comment("Not used in linux kernel or e2fsprogs.")]];
RO_COMPAT_HUGE_FILE : 1 [[comment("This filesystem has files whose sizes are represented in units of logical blocks, not 512-byte sectors. This implies a very large file indeed!.")]];
RO_COMPAT_GDT_CSUM : 1 [[comment("Group descriptors have checksums.")]];
RO_COMPAT_DIR_NLINK : 1 [[comment("Indicates that the old ext3 32,000 subdirectory limit no longer applies.")]];
RO_COMPAT_EXTRA_ISIZE : 1 [[comment("Indicates that large inodes exist on this filesystem.")]];
RO_COMPAT_HAS_SNAPSHOT : 1 [[comment("This filesystem has a snapshot.")]];
RO_COMPAT_QUOTA : 1;
RO_COMPAT_BIGALLOC : 1 [[comment("This filesystem supports “bigalloc”, which means that file extents are tracked in units of clusters (of blocks) instead of blocks.")]];
RO_COMPAT_METADATA_CSUM : 1 [[comment("This filesystem supports metadata checksumming.")]];
RO_COMPAT_REPLICA : 1 [[comment("Filesystem supports replicas. This feature is neither in the kernel nor e2fsprogs.")]];
RO_COMPAT_READONLY : 1 [[comment("Read-only filesystem image.")]];
RO_COMPAT_PROJECT : 1 [[comment("Filesystem tracks project quotas.")]];
padding : 1;
RO_COMPAT_VERITY : 1 [[comment("Verity inodes may be present on the filesystem.")]];
RO_COMPAT_ORPHAN_PRESENT : 1 [[comment("Indicates orphan file may have valid orphan entries and thus we need to clean them up when mounting the filesystem.")]];
};
enum ext4_super_def_hash : u8 {
LEGACY = 0x00,
HALF_MD4 = 0x01,
TEA = 0x02,
LEGACY_UNSIGNED = 0x03,
HALF_MD4_UNSIGNED = 0x04,
TEA_UNSIGNED = 0x05
};
bitfield ext4_super_mountopts {
EXT4_DEFM_DEBUG : 1 [[comment("Print debugging info upon (re)mount.")]];
EXT4_DEFM_BSDGROUPS : 1 [[comment("New files take the gid of the containing directory (instead of the fsgid of the current process).")]];
EXT4_DEFM_XATTR_USER : 1 [[comment("Support userspace-provided extended attributes.")]];
EXT4_DEFM_ACL : 1 [[comment("Support POSIX access control lists (ACLs).")]];
EXT4_DEFM_UID16 : 1 [[comment("Do not support 32-bit UIDs.")]];
EXT4_DEFM_JMODE_DATA : 1 [[comment("All data and metadata are committed to the journal.")]];
EXT4_DEFM_JMODE_ORDER : 1 [[comment("All data are flushed to the disk before metadata are committed to the journal.")]];
padding : 1;
EXT4_DEFM_NOBARRIER : 1 [[comment("Disable write flushes.")]];
EXT4_DEFM_BLOCK_VALIDITY : 1 [[comment("Track which blocks in a filesystem are metadata and therefore should not be used as data blocks.")]];
EXT4_DEFM_DISCARD : 1 [[comment("Enable DISCARD support, where the storage device is told about blocks becoming unused.")]];
EXT4_DEFM_NODELALLOC : 1 [[comment("Disable delayed allocation.")]];
};
bitfield ext4_super_flags {
SIGNED_HASH_DIRECTORY : 1;
UNSIGNED_HASH_DIRECTORY : 1;
TEST_DEV_CODE : 1;
};
enum ext4_super_encrypt_algos : u8 {
ENCRYPTION_MODE_INVALID = 0x00,
ENCRYPTION_MODE_AES_256_XTS = 0x01,
ENCRYPTION_MODE_AES_256_GCM = 0x02,
ENCRYPTION_MODE_AES_256_CBC = 0x03,
};
struct ext4_super_block {
u32 s_inodes_count [[comment("Total inode count.")]];
u32 s_blocks_count_lo [[comment("Total block count.")]];
u32 s_r_blocks_count_lo [[comment("This number of blocks can only be allocated by the super-user.")]];
u32 s_free_blocks_count_lo [[comment("Free block count.")]];
u32 s_free_inodes_count [[comment("Free inode count.")]];
u32 s_first_data_block [[comment("First data block. This must be at least 1 for 1k-block filesystems and is typically 0 for all other block sizes.")]];
u32 s_log_block_size;
u32 s_log_cluster_size;
u64 block_size = std::math::pow(2, 10+s_log_block_size);
u64 cluster_size = std::math::pow(2, 10+s_log_cluster_size);
u32 s_blocks_per_group [[comment("Blocks per group.")]];
u32 s_clusters_per_group [[comment("Clusters per group, if bigalloc is enabled. Otherwise s_clusters_per_group must equal s_blocks_per_group.")]];
u32 s_inodes_per_group [[comment("Inodes per group.")]];
type::time32_t s_mtime [[comment("Last mount time, in seconds since the epoch.")]];
type::time32_t s_wtime [[comment("Last write time, in seconds since the epoch.")]];
u16 s_mnt_count [[comment("Number of mounts since the last fsck.")]];
u16 s_max_mnt_count [[comment("Number of mounts beyond which a fsck is needed.")]];
type::Magic<"\x53\xEF"> s_magic;
ext4_super_state s_state [[comment("File system state.")]];
ext4_super_errors s_errors [[comment("Behaviour when detecting errors.")]];
u16 s_minor_rev_level [[comment("Minor revision level.")]];
type::time32_t s_lastcheck [[comment("Time of last check, in seconds since the epoch.")]];
u32 s_checkinterval [[comment("Maximum time between checks, in seconds.")]];
ext4_super_creator s_creator_os [[comment("Creator OS.")]];
ext4_super_revision s_rev_level [[comment("Revision level.")]];
u16 s_def_resuid [[comment("Default uid for reserved blocks.")]];
u16 s_def_resgid [[comment("Default gid for reserved blocks.")]];
// EXT2_DYNAMIC_REV superblock
if (s_rev_level >= ext4_super_revision::V2_DYNAMIC_REV) {
u32 s_first_ino [[comment("First non-reserved inode.")]];
u16 s_inode_size [[comment("Size of inode structure, in bytes.")]];
u16 s_block_group_nr [[comment("Block group number of this superblock.")]];
ext4_super_compat s_feature_compat [[comment("Compatible feature set flags. Kernel can still read/write this fs even if it doesnt understand a flag; fsck should not do that.")]];
padding[2];
ext4_super_incompat s_feature_incompat [[comment("Incompatible feature set. If the kernel or fsck doesnt understand one of these bits, it should stop.")]];
padding[1];
ext4_super_compat_ro s_feature_ro_compat [[comment("Readonly-compatible feature set. If the kernel doesnt understand one of these bits, it can still mount read-only.")]];
padding[1];
u8 s_uuid[16] [[comment("128-bit UUID for volume.")]];
char s_volume_name[16] [[comment("Volume label.")]];
char s_last_mounted[64] [[comment("Directory where filesystem was last mounted.")]];
if (s_feature_incompat.INCOMPAT_COMPRESSION) {
u32 s_algorithm_usage_bitmap;
} else {
padding[4];
}
// Performance hints
if (s_feature_compat.COMPAT_DIR_PREALLOC) {
u8 s_prealloc_blocks [[comment("Number of blocks to try to preallocate for ... files? (Not used in e2fsprogs/Linux).")]];
u8 s_prealloc_dir_blocks [[comment("Number of blocks to preallocate for directories. (Not used in e2fsprogs/Linux).")]];
} else {
padding[2];
}
u16 s_reserved_gdt_blocks [[comment("Number of reserved GDT entries for future filesystem expansion.")]];
// Journaling support
if (s_feature_compat.COMPAT_HAS_JOURNAL) {
u8 s_journal_uuid[16] [[comment("UUID of journal superblock.")]];
u32 s_journal_inum [[comment("Inode number of journal file.")]];
if (s_feature_incompat.INCOMPAT_JOURNAL_DEV) {
u32 s_journal_dev [[comment("Device number of journal file.")]];
} else {
padding[4];
}
} else {
padding[24];
}
u32 s_last_orphan [[comment("Inode start of list of orphaned inodes to delete.")]];
u32 s_hash_seed[4] [[comment("HTREE hash seed.")]];
ext4_super_def_hash s_def_hash_version [[comment("Default hash algorithm to use for directory hashes.")]];
u8 s_jnl_backup_type [[comment("If this value is 0 or EXT3_JNL_BACKUP_BLOCKS (1), then the s_jnl_blocks field contains a duplicate copy of the inodes i_block[] array and i_size.")]];
if (s_feature_incompat.INCOMPAT_64BIT) {
u16 s_desc_size [[comment("Size of group descriptors, in bytes, if the 64bit incompat feature flag is set.")]];
} else {
padding[2];
}
ext4_super_mountopts s_default_mount_opts [[comment("Default mount options.")]];
padding[2];
if (s_feature_incompat.INCOMPAT_META_BG) {
u32 s_first_meta_bg [[comment("First metablock block group, if the meta_bg feature is enabled.")]];
} else {
padding[4];
}
type::time32_t s_mkfs_time [[comment("When the filesystem was created, in seconds since the epoch.")]];
u32 s_jnl_blocks[17] [[comment("Backup copy of the journal inodes i_block[] array in the first 15 elements and i_size_high and i_size in the 16th and 17th elements, respectively.")]];
if (s_feature_incompat.INCOMPAT_64BIT) {
u32 s_blocks_count_hi [[comment("High 32-bits of the block count.")]];
u32 s_r_blocks_count_hi [[comment("High 32-bits of the reserved block count.")]];
u32 s_free_blocks_count_hi [[comment("High 32-bits of the free block count.")]];
u64 s_blocks_count = (u64(s_blocks_count_hi) << 32) + s_blocks_count_lo;
u64 s_r_blocks_count = (u64(s_r_blocks_count_hi) << 32) + s_r_blocks_count_lo;
u64 s_free_blocks_count = (u64(s_free_blocks_count_hi) << 32) + s_free_blocks_count_lo;
u64 groups_count = std::math::ceil(s_blocks_count/float(s_blocks_per_group));
} else {
padding[12];
u64 s_blocks_count = s_blocks_count_lo;
u64 s_r_blocks_count = s_r_blocks_count_lo;
u64 s_free_blocks_count = s_free_blocks_count_lo;
u64 groups_count = std::math::ceil(s_blocks_count/float(s_blocks_per_group));
}
if (s_feature_ro_compat.RO_COMPAT_EXTRA_ISIZE) {
u16 s_min_extra_isize [[comment("All inodes have at least # bytes.")]];
u16 s_want_extra_isize [[comment("New inodes should reserve # bytes.")]];
} else {
padding[4];
}
ext4_super_flags s_flags [[comment("Miscellaneous flags.")]];
padding[3];
u16 s_raid_stride [[comment("RAID stride. This is the number of logical blocks read from or written to the disk before moving to the next disk. This affects the placement of filesystem metadata.")]];
if (s_feature_incompat.INCOMPAT_MMP) {
u16 s_mmp_interval [[comment("Number of seconds to wait in multi-mount prevention (MMP) checking.")]];
u64 s_mmp_block [[comment("Block number for multi-mount protection data.")]];
} else {
padding[10];
}
u32 s_raid_stripe_width [[comment("RAID stripe width. This is the number of logical blocks read from or written to the disk before coming back to the current disk.")]];
if (s_feature_incompat.INCOMPAT_FLEX_BG) {
u8 s_log_groups_per_flex;
u64 groups_per_flex = std::math::pow(2, s_log_groups_per_flex);
} else {
padding[1];
}
if (s_feature_ro_compat.RO_COMPAT_METADATA_CSUM) {
u8 s_checksum_type [[comment("Metadata checksum algorithm type.")]];
} else {
padding[1];
}
padding[2];
u64 s_kbytes_written [[comment("Number of KiB written to this filesystem over its lifetime.")]];
if (s_feature_ro_compat.RO_COMPAT_HAS_SNAPSHOT) {
u32 s_snapshot_inum [[comment("inode number of active snapshot. (Not used in e2fsprogs/Linux.)")]];
u32 s_snapshot_id [[comment("Sequential ID of active snapshot. (Not used in e2fsprogs/Linux.)")]];
u64 s_snapshot_r_blocks_count [[comment("Number of blocks reserved for active snapshots future use. (Not used in e2fsprogs/Linux.)")]];
u32 s_snapshot_list [[comment("inode number of the head of the on-disk snapshot list. (Not used in e2fsprogs/Linux.)")]];
} else {
padding[20];
}
u32 s_error_count [[comment("Number of errors seen.")]];
if (s_error_count > 0) {
type::time32_t s_first_error_time [[comment("First time an error happened.")]];
u32 s_first_error_ino [[comment("inode involved in first error.")]];
u64 s_first_error_block [[comment("Number of block involved of first error.")]];
char s_first_error_func[32] [[comment("Name of function where the error happened.")]];
u32 s_first_error_line [[comment("Line number where error happened.")]];
type::time32_t s_last_error_time [[comment("Last time an error happened.")]];
u32 s_last_error_ino [[comment("inode involved in most recent error.")]];
u32 s_last_error_line [[comment("Line number where most recent error happened.")]];
u64 s_last_error_block [[comment("Number of block involved in most recent error.")]];
char s_last_error_func[32] [[comment("Name of function where the most recent error happened.")]];
} else {
padding[104];
}
char s_mount_opts[64] [[comment("ASCIIZ string of mount options.")]];
if (s_feature_ro_compat.RO_COMPAT_QUOTA) {
u32 s_usr_quota_inum [[comment("Inode number of user quota file.")]];
u32 s_grp_quota_inum [[comment("Inode number of group quota file.")]];
} else {
padding[8];
}
u32 s_overhead_blocks [[comment("Overhead blocks/clusters in fs. (Huh? This field is always zero, which means that the linux kernel calculates it dynamically.)")]];
if (s_feature_compat.COMPAT_SPARSE_SUPER2) {
u32 s_backup_bgs[2] [[comment("Block groups containing superblock backups.")]];
} else {
padding[8];
}
if (s_feature_incompat.INCOMPAT_ENCRYPT) {
ext4_super_encrypt_algos s_encrypt_algos[4] [[comment("Encryption algorithms in use. There can be up to four algorithms in use at any time.")]];
u8 s_encrypt_pw_salt[16] [[comment("Salt for the string2key algorithm for encryption.")]];
} else {
padding[20];
}
u32 s_lpf_ino [[comment("Inode number of lost+found.")]];
if (s_feature_ro_compat.RO_COMPAT_PROJECT) {
u32 s_prj_quota_inum [[comment("Inode that tracks project quotas.")]];
} else {
padding[4];
}
if (s_feature_ro_compat.RO_COMPAT_METADATA_CSUM) {
u32 s_checksum_seed [[comment("Checksum seed used for metadata_csum calculations. This value is crc32c(~0, $orig_fs_uuid).")]];
} else {
padding[4];
}
u8 s_wtime_hi [[comment("Upper 8 bits of the s_wtime field.")]];
u8 s_mtime_hi [[comment("Upper 8 bits of the s_mtime field.")]];
u8 s_mkfs_time_hi [[comment("Upper 8 bits of the s_mkfs_time field.")]];
u8 s_lastcheck_hi [[comment("Upper 8 bits of the s_lastcheck field.")]];
u8 s_first_error_time_hi [[comment("Upper 8 bits of the s_first_error_time field.")]];
u8 s_last_error_time_hi [[comment("Upper 8 bits of the s_last_error_time field.")]];
padding[2];
u16 s_encoding [[comment("Filename charset encoding.")]];
u16 s_encoding_flags [[comment("Filename charset encoding flags.")]];
if (s_feature_compat.RO_COMPAT_ORPHAN_PRESENT) {
u32 s_orphan_file_inum [[comment("Orphan file inode number.")]];
} else {
padding[4];
}
padding[376];
if (s_feature_ro_compat.RO_COMPAT_METADATA_CSUM) {
u32 s_checksum [[comment("Superblock checksum.")]];
} else {
padding[4];
}
}
};
ext4_super_block super_block @ 0x400;
fn block_to_address(u32 block) {
return super_block.block_size * block;
};
fn block_pointer_to_address(u32 block) {
return block_to_address(block) - block;
};
struct ext4_bitmap {
u8 data[super_block.block_size];
};
bitfield ext4_i_mode {
S_IXOTH : 1 [[comment("Others may execute.")]];
S_IWOTH : 1 [[comment("Others may write.")]];
S_IROTH : 1 [[comment("Others may read.")]];
S_IXGRP : 1 [[comment("Group members may execute.")]];
S_IWGRP : 1 [[comment("Group members may write.")]];
S_IRGRP : 1 [[comment("Group members may read.")]];
S_IXUSR : 1 [[comment("Owner may execute.")]];
S_IWUSR : 1 [[comment("Owner may write.")]];
S_IRUSR : 1 [[comment("Owner may read.")]];
S_ISVTX : 1 [[comment("Sticky bit.")]];
S_ISGID : 1 [[comment("Set GID.")]];
S_ISUID : 1 [[comment("Set UID.")]];
S_IFIFO : 1 [[comment("FIFO.")]];
S_IFCHR : 1 [[comment("Character device.")]];
S_IFDIR : 1 [[comment("Directory.")]];
S_IFREG : 1 [[comment("Regular file.")]];
};
bitfield ext4_i_flags {
EXT4_SECRM_FL : 1 [[comment("This file requires secure deletion.")]];
EXT4_UNRM_FL : 1 [[comment("This file should be preserved, should undeletion be desired.")]];
EXT4_COMPR_FL : 1 [[comment("File is compressed.")]];
EXT4_SYNC_FL : 1 [[comment("All writes to the file must be synchronous.")]];
EXT4_IMMUTABLE_FL : 1 [[comment("File is immutable.")]];
EXT4_APPEND_FL : 1 [[comment("File can only be appended.")]];
EXT4_NODUMP_FL : 1 [[comment("The dump utility should not dump this file.")]];
EXT4_NOATIME_FL : 1 [[comment("Do not update access time.")]];
EXT4_DIRTY_FL : 1 [[comment("Dirty compressed file.")]];
EXT4_COMPRBLK_FL : 1 [[comment("File has one or more compressed clusters.")]];
EXT4_NOCOMPR_FL : 1 [[comment("Do not compress file.")]];
EXT4_ENCRYPT_FL : 1 [[comment("Encrypted inode.")]];
EXT4_INDEX_FL : 1 [[comment("Directory has hashed indexes.")]];
EXT4_IMAGIC_FL : 1 [[comment("AFS magic directory.")]];
EXT4_JOURNAL_DATA_FL : 1 [[comment("File data must always be written through the journal.")]];
EXT4_NOTAIL_FL : 1 [[comment("File tail should not be merged.")]];
EXT4_DIRSYNC_FL : 1 [[comment("All directory entry data should be written synchronously.")]];
EXT4_TOPDIR_FL : 1 [[comment("Top of directory hierarchy.")]];
EXT4_HUGE_FILE_FL : 1 [[comment("This is a huge file.")]];
EXT4_EXTENTS_FL : 1 [[comment("Inode uses extents.")]];
EXT4_VERITY_FL : 1 [[comment("Verity protected file.")]];
EXT4_EA_INODE_FL : 1 [[comment("Inode stores a large extended attribute value in its data blocks.")]];
EXT4_EOFBLOCKS_FL : 1 [[comment("This file has blocks allocated past EOF.")]];
padding : 1;
EXT4_SNAPFILE_FL : 1 [[comment("Inode is a snapshot.")]];
padding : 1;
EXT4_SNAPFILE_DELETED_FL : 1 [[comment("Snapshot is being deleted.")]];
EXT4_SNAPFILE_SHRUNK_FL : 1 [[comment("Snapshot shrink has completed.")]];
EXT4_INLINE_DATA_FL : 1 [[comment("Inode has inline data.")]];
EXT4_PROJINHERIT_FL : 1 [[comment("Create children with the same project ID.")]];
padding : 1;
EXT4_RESERVED_FL : 1 [[comment("Reserved for ext4 library.")]];
};
struct ext4_inode {
ext4_i_mode i_mode [[comment("File mode.")]];
u16 i_uid [[comment("Lower 16-bits of Owner UID.")]];
u32 i_size [[comment("Lower 32-bits of size in bytes.")]];
type::time32_t i_atime [[comment("Last access time.")]];
type::time32_t i_ctime [[comment("Last inode change time.")]];
type::time32_t i_mtime [[comment("Last data modification time.")]];
type::time32_t i_dtime [[comment("Deletion Time.")]];
u16 i_gid [[comment("Lower 16-bits of GID.")]];
u16 i_links_count [[comment("Hard link count.")]];
u32 i_blocks_lo [[comment("Lower 32-bits of “block” count.")]];
ext4_i_flags i_flags [[comment("Inode flags.")]];
u32 i_osd1 [[comment("Depends of the OS.")]];
u32 i_block[15] [[comment("Block map or extent tree.")]];
u32 i_generation [[comment("File version (for NFS).")]];
u32 i_file_acl [[comment("Lower 32-bits of extended attribute block.")]];
u32 i_dir_acl [[comment("Upper 32-bits of file/directory size.")]];
u32 i_faddr [[comment("Fragment address.")]];
u8 i_osd2[12] [[comment("Depends of the OS.")]];
if (super_block.s_rev_level >= ext4_super_revision::V2_DYNAMIC_REV && super_block.s_inode_size > 0x80) {
u16 i_extra_isize [[comment("Size of this inode - 128.")]];
u16 i_checksum_hi [[comment("Upper 16-bits of the inode checksum.")]];
if (super_block.s_inode_size > 0x84) {
u32 i_ctime_extra [[comment("Extra change time bits.")]];
u32 i_mtime_extra [[comment("Extra modification time bits.")]];
u32 i_atime_extra [[comment("Extra access time bits.")]];
u32 i_crtime [[comment("File creation time.")]];
u32 i_crtime_extra [[comment("Extra file creation time bits.")]];
if (super_block.s_inode_size > 0x98) {
u32 i_version_hi [[comment("Upper 32-bits for version number.")]];
if (super_block.s_inode_size > 0x9C) {
u32 i_projid [[comment("Project ID.")]];
if (super_block.s_inode_size > 0xA0) {
padding[super_block.s_inode_size - 0xA0];
}
}
}
}
}
};
bitfield ext4_bg_flags {
EXT4_BG_INODE_UNINIT : 1 [[comment("Inode table and bitmap are not initialized.")]];
EXT4_BG_BLOCK_UNINIT : 1 [[comment("Block bitmap is not initialized.")]];
EXT4_BG_INODE_ZEROED : 1 [[comment("Inode table is zeroed.")]];
};
struct ext4_group_desc {
ext4_bitmap *bg_block_bitmap : u32 [[pointer_base("block_pointer_to_address"), comment("Lower 32-bits of location of block bitmap.")]];
ext4_bitmap *bg_inode_bitmap : u32 [[pointer_base("block_pointer_to_address"), comment("Lower 32-bits of location of inode bitmap.")]];
ext4_inode *bg_inode_table[super_block.s_inodes_per_group] : u32 [[pointer_base("block_pointer_to_address"), comment("Lower 32-bits of location of inode table.")]];
u16 bg_free_blocks_count [[comment("Lower 16-bits of free block count.")]];
u16 bg_free_inodes_count [[comment("Lower 16-bits of free inode count.")]];
u16 bg_used_dirs_count [[comment("Lower 16-bits of directory count.")]];
ext4_bg_flags bg_flags [[comment("Block group flags.")]];
padding[1];
u32 bg_exclude_bitmap_lo [[comment("Lower 32-bits of location of snapshot exclusion bitmap.")]];
u16 bg_block_bitmap_csum_lo [[comment("Lower 16-bits of the block bitmap checksum.")]];
u16 bg_inode_bitmap_csum_lo [[comment("Lower 16-bits of the inode bitmap checksum.")]];
u16 bg_itable_unused_lo [[comment("Lower 16-bits of unused inode count.")]];
u16 bg_checksum [[comment("Group descriptor checksum.")]];
};
struct ext4_group_desc_64_bit : ext4_group_desc {
u32 bg_block_bitmap_hi [[comment("Upper 32-bits of location of block bitmap.")]];
u32 bg_inode_bitmap_hi [[comment("Upper 32-bits of location of inodes bitmap.")]];
u32 bg_inode_table_hi [[comment("Upper 32-bits of location of inodes table.")]];
u16 bg_free_blocks_count_hi [[comment("Upper 16-bits of free block count.")]];
u16 bg_free_inodes_count_hi [[comment("Upper 16-bits of free inode count.")]];
u16 bg_used_dirs_count_hi [[comment("Upper 16-bits of directory count.")]];
u16 bg_itable_unused_hi [[comment("Upper 16-bits of unused inode count.")]];
u32 bg_exclude_bitmap_hi [[comment("Upper 32-bits of location of snapshot exclusion bitmap.")]];
u16 bg_block_bitmap_csum_hi [[comment("Upper 16-bits of the block bitmap checksum.")]];
u16 bg_inode_bitmap_csum_hi [[comment("Upper 16-bits of the inode bitmap checksum.")]];
padding[4];
};
struct ext4_group_descriptors {
if (super_block.s_rev_level >= ext4_super_revision::V2_DYNAMIC_REV && super_block.s_feature_incompat.INCOMPAT_64BIT) {
ext4_group_desc_64_bit group_desc[super_block.groups_count];
} else {
ext4_group_desc group_desc[super_block.groups_count];
}
};
ext4_group_descriptors group_descs @ block_to_address(2);

108
patterns/fs/fat32.hexpat Normal file
View File

@@ -0,0 +1,108 @@
import std.core;
u64 bytesPerCluster;
struct FSInfo {
u32 leadSignature;
padding[480];
u32 structSignature;
u32 freeClusterCount;
u32 nextFreeCluster;
padding[12];
u32 trailSignature;
};
bitfield SequenceNumber {
padding : 1;
lastLogical : 1;
padding : 1;
number : 5;
} [[bitfield_order(std::core::BitfieldOrder::MostToLeastSignificant, 8)]];
enum EntryStatus : u8 {
Regular = 0x00,
DotEntry = 0x2E,
DeletedEntry = 0xE5
};
union EntryStatusOrSequenceNumber {
EntryStatus entryStatus;
SequenceNumber sequenceNumber;
};
bitfield Attributes {
readOnly : 1;
hidden : 1;
systemFile : 1;
volumeLabel : 1;
subdirectory : 1;
archive : 1;
padding : 2;
} [[bitfield_order(std::core::BitfieldOrder::LeastToMostSignificant, 8)]];
struct DirEntry {
char fileName[8];
char extension[3];
Attributes attributes;
u8 reserved[10];
u16 time, date;
u16 startingCluster;
u32 fileSize;
u8 data[fileSize] @ startingCluster * bytesPerCluster;
};
struct VFATDirEntry {
EntryStatusOrSequenceNumber entryStatusOrSequenceNumber;
char16 name1[5];
Attributes attributes;
u8 type;
u8 nameChecksum;
char16 name2[6];
u16 startingCluster;
char16 name3[2];
if (entryStatusOrSequenceNumber.sequenceNumber.number > 1)
VFATDirEntry nextLogicalEntry;
else
DirEntry physicalEntry;
};
struct FAT32 {
u8 jmpCode[3];
char oemName[8];
u16 bytesPerSector;
u8 sectorsPerCluster;
u16 reservedAreaSize;
u8 numFats;
u16 rootEntryCount;
u16 numSectors;
u8 mediaType;
u16 fatSize;
u16 sectorsPerTrack;
u16 numHeads;
u32 numHiddenSectors;
u32 numFsSectors;
u32 numFatSectors;
u16 extFlags;
u16 fsVersion;
u32 rootCluster;
u16 fsInfoSector;
u16 backupBootSector;
padding[12];
u8 driveNumber;
padding[1];
u8 bootSignature;
u32 volumeID;
char volumeLabel[11];
char fsType[8];
u8 bootstrapCode[420];
u16 signature;
bytesPerCluster = (sectorsPerCluster * 1024) * bytesPerSector;
FSInfo fsInfo @ addressof(this) + fsInfoSector * bytesPerSector;
VFATDirEntry rootDirEntry @ addressof(this) + rootCluster * bytesPerCluster;
};
FAT32 fat32 @ 0x00;

997
patterns/fs/ntfs.hexpat Normal file
View File

@@ -0,0 +1,997 @@
#pragma author Hrant Tadevosyan (Axcient, now ConnectWise)
#pragma description NT File System (NTFS)
#pragma endian little
// refs:
// - https://github.com/libyal/libfsntfs
// - https://github.com/tuxera/ntfs-3g
import std.core;
import std.array;
import std.ptr;
import std.io;
import std.mem;
import type.magic;
using ntfschar = u16;
using leVCN = u64;
using leLCN = u64;
using leLSN = u64;
using leMFT_REF = u64;
struct GUID {
u32 Data1;
u16 Data2;
u16 Data3;
u8 Data4[8];
} [[static]];
bitfield RUNLIST_HEADER {
unsigned offset : 4;
unsigned length : 4;
} [[bitfield_order(std::core::BitfieldOrder::MostToLeastSignificant, 8)]];
struct RUNLIST {
RUNLIST_HEADER header;
u8 count[header.length];
u8 lcn[header.offset];
if (header.length == 0)
break;
};
struct BIOS_PARAMETER_BLOCK {
u16 bytes_per_sector;
u8 sectors_per_cluster;
u16 reserved_sectors;
u8 fats;
u16 root_entries;
u16 sectors;
u8 media_type;
u16 sectors_per_fat;
u16 sectors_per_track;
u16 heads;
u32 hidden_sectors;
u32 large_sectors;
};
enum NTFS_RECORD_TYPES : u32 {
magic_FILE = 0x454c4946,
magic_INDX = 0x58444e49,
magic_HOLE = 0x454c4f48,
magic_RSTR = 0x52545352,
magic_RCRD = 0x44524352,
magic_CHKD = 0x444b4843,
magic_BAAD = 0x44414142,
magic_empty = 0xffffffff,
};
struct NTFS_RECORD {
NTFS_RECORD_TYPES magic;
u16 usa_ofs;
u16 usa_count;
};
enum MFT_RECORD_FLAGS : u16 {
MFT_RECORD_IN_USE = 0x0001,
MFT_RECORD_IS_DIRECTORY = 0x0002,
MFT_RECORD_IS_4 = 0x0004,
MFT_RECORD_IS_VIEW_INDEX = 0x0008,
MFT_REC_SPACE_FILLER = 0xffff,
};
enum ATTR_TYPES : u32 {
AT_UNUSED = 0,
AT_STANDARD_INFORMATION = 0x10,
AT_ATTRIBUTE_LIST = 0x20,
AT_FILE_NAME = 0x30,
AT_OBJECT_ID = 0x40,
AT_SECURITY_DESCRIPTOR = 0x50,
AT_VOLUME_NAME = 0x60,
AT_VOLUME_INFORMATION = 0x70,
AT_DATA = 0x80,
AT_INDEX_ROOT = 0x90,
AT_INDEX_ALLOCATION = 0xa0,
AT_BITMAP = 0xb0,
AT_REPARSE_POINT = 0xc0,
AT_EA_INFORMATION = 0xd0,
AT_EA = 0xe0,
AT_PROPERTY_SET = 0xf0,
AT_LOGGED_UTILITY_STREAM = 0x100,
AT_FIRST_USER_DEFINED_ATTRIBUTE = 0x1000,
AT_END = 0xffffffff,
};
enum ATTR_DEF_FLAGS : u32 {
ATTR_DEF_INDEXABLE = 0x02,
ATTR_DEF_MULTIPLE = 0x04,
ATTR_DEF_NOT_ZERO = 0x08,
ATTR_DEF_INDEXED_UNIQUE = 0x10,
ATTR_DEF_NAMED_UNIQUE = 0x20,
ATTR_DEF_RESIDENT = 0x40,
ATTR_DEF_ALWAYS_LOG = 0x80,
};
enum COLLATION_RULES : u32 {
COLLATION_BINARY = 0,
COLLATION_FILE_NAME = 1,
COLLATION_UNICODE_STRING = 2,
COLLATION_NTOFS_ULONG = 16,
COLLATION_NTOFS_SID = 17,
COLLATION_NTOFS_SECURITY_HASH = 18,
COLLATION_NTOFS_ULONGS = 19,
};
struct ATTR_DEF {
ntfschar name[0x40];
ATTR_TYPES type;
u32 display_rule;
COLLATION_RULES collation_rule;
ATTR_DEF_FLAGS flags;
u64 min_size;
u64 max_size;
};
enum ATTR_FLAGS : u16 {
ATTR_IS_COMPRESSED = 0x0001,
ATTR_COMPRESSION_MASK = 0x00ff,
ATTR_IS_ENCRYPTED = 0x4000,
ATTR_IS_SPARSE = 0x8000,
};
enum RESIDENT_ATTR_FLAGS : u8 {
RESIDENT_ATTR_IS_INDEXED = 0x01
};
enum FILE_ATTR_FLAGS : u32 {
FILE_ATTR_READONLY = 0x00000001,
FILE_ATTR_HIDDEN = 0x00000002,
FILE_ATTR_SYSTEM = 0x00000004,
FILE_ATTR_DIRECTORY = 0x00000010,
FILE_ATTR_ARCHIVE = 0x00000020,
FILE_ATTR_DEVICE = 0x00000040,
FILE_ATTR_NORMAL = 0x00000080,
FILE_ATTR_TEMPORARY = 0x00000100,
FILE_ATTR_SPARSE_FILE = 0x00000200,
FILE_ATTR_REPARSE_POINT = 0x00000400,
FILE_ATTR_COMPRESSED = 0x00000800,
FILE_ATTR_OFFLINE = 0x00001000,
FILE_ATTR_NOT_CONTENT_INDEXED = 0x00002000,
FILE_ATTR_ENCRYPTED = 0x00004000,
FILE_ATTRIBUTE_RECALL_ON_OPEN = 0x00040000,
FILE_ATTR_VALID_FLAGS = 0x00047fb7,
FILE_ATTR_VALID_SET_FLAGS = 0x000031a7,
FILE_ATTR_I30_INDEX_PRESENT = 0x10000000,
FILE_ATTR_VIEW_INDEX_PRESENT = 0x20000000,
};
enum FILE_NAME_TYPE_FLAGS : u8 {
FILE_NAME_POSIX = 0x00,
FILE_NAME_WIN32 = 0x01,
FILE_NAME_DOS = 0x02,
FILE_NAME_WIN32_AND_DOS = 0x03,
};
struct STANDARD_INFORMATION_HEADER {
u64 creation_time;
u64 last_data_change_time;
u64 last_mft_change_time;
u64 last_access_time;
FILE_ATTR_FLAGS file_attributes;
} [[static]];
struct STANDARD_INFORMATION_OLD {
STANDARD_INFORMATION_HEADER header [[inline]];
u8 reserved12[12];
} [[static]];
struct STANDARD_INFORMATION {
STANDARD_INFORMATION_HEADER header [[inline]];
u32 maximum_versions;
u32 version_number;
u32 class_id;
u32 owner_id;
u32 security_id;
u64 quota_charged;
u64 usn;
} [[static]];
struct FILE_NAME_ATTR_PACKED {
u16 packed_ea_size;
u16 reserved;
} [[static]];
union FILE_NAME_ATTR_FORM {
FILE_NAME_ATTR_PACKED packed [[inline]];
u32 reparse_point_tag;
} [[static]];
struct FILE_NAME_ATTR {
leMFT_REF parent_directory;
u64 creation_time;
u64 last_data_change_time;
u64 last_mft_change_time;
u64 last_access_time;
u64 allocated_size;
u64 data_size;
FILE_ATTR_FLAGS file_attributes;
FILE_NAME_ATTR_FORM form [[inline]];
u8 file_name_length;
FILE_NAME_TYPE_FLAGS file_name_type;
ntfschar file_name[file_name_length];
};
struct OBJECT_ID_ATTR_INFO {
GUID birth_volume_id;
GUID birth_object_id;
GUID domain_id;
};
union OBJECT_ID_ATTR_FORM {
OBJECT_ID_ATTR_INFO info [[inline]];
u8 extended_info[48];
};
struct OBJECT_ID_ATTR {
GUID object_id;
OBJECT_ID_ATTR_FORM form [[inline]];
};
enum VOLUME_FLAGS : u16 {
VOLUME_IS_DIRTY = 0x0001,
VOLUME_RESIZE_LOG_FILE = 0x0002,
VOLUME_UPGRADE_ON_MOUNT = 0x0004,
VOLUME_MOUNTED_ON_NT4 = 0x0008,
VOLUME_DELETE_USN_UNDERWAY = 0x0010,
VOLUME_REPAIR_OBJECT_ID = 0x0020,
VOLUME_CHKDSK_UNDERWAY = 0x4000,
VOLUME_MODIFIED_BY_CHKDSK = 0x8000,
VOLUME_FLAGS_MASK = 0xc03f,
};
struct VOLUME_INFORMATION {
u64 reserved;
u8 major_ver;
u8 minor_ver;
VOLUME_FLAGS flags;
} [[static]];
enum SECURITY_DESCRIPTOR_CONTROL : u16 {
SE_OWNER_DEFAULTED = 0x0001,
SE_GROUP_DEFAULTED = 0x0002,
SE_DACL_PRESENT = 0x0004,
SE_DACL_DEFAULTED = 0x0008,
SE_SACL_PRESENT = 0x0010,
SE_SACL_DEFAULTED = 0x0020,
SE_DACL_AUTO_INHERIT_REQ = 0x0100,
SE_SACL_AUTO_INHERIT_REQ = 0x0200,
SE_DACL_AUTO_INHERITED = 0x0400,
SE_SACL_AUTO_INHERITED = 0x0800,
SE_DACL_PROTECTED = 0x1000,
SE_SACL_PROTECTED = 0x2000,
SE_RM_CONTROL_VALID = 0x4000,
SE_SELF_RELATIVE = 0x8000,
};
enum ACE_TYPES : u8 {
ACCESS_MIN_MS_ACE_TYPE = 0,
ACCESS_ALLOWED_ACE_TYPE = 0,
ACCESS_DENIED_ACE_TYPE = 1,
SYSTEM_AUDIT_ACE_TYPE = 2,
SYSTEM_ALARM_ACE_TYPE = 3,
ACCESS_MAX_MS_V2_ACE_TYPE = 3,
ACCESS_ALLOWED_COMPOUND_ACE_TYPE = 4,
ACCESS_MAX_MS_V3_ACE_TYPE = 4,
ACCESS_MIN_MS_OBJECT_ACE_TYPE = 5,
ACCESS_ALLOWED_OBJECT_ACE_TYPE = 5,
ACCESS_DENIED_OBJECT_ACE_TYPE = 6,
SYSTEM_AUDIT_OBJECT_ACE_TYPE = 7,
SYSTEM_ALARM_OBJECT_ACE_TYPE = 8,
ACCESS_MAX_MS_OBJECT_ACE_TYPE = 8,
ACCESS_MAX_MS_V4_ACE_TYPE = 8,
ACCESS_MAX_MS_ACE_TYPE = 8,
ACCESS_ALLOWED_CALLBACK_ACE_TYPE = 9,
ACCESS_DENIED_CALLBACK_ACE_TYPE = 10,
ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE = 11,
ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE = 12,
SYSTEM_AUDIT_CALLBACK_ACE_TYPE = 13,
SYSTEM_ALARM_CALLBACK_ACE_TYPE = 14,
SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE = 15,
SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE = 16,
SYSTEM_MANDATORY_LABEL_ACE_TYPE = 17,
SYSTEM_RESOURCE_ATTRIBUTE_ACE_TYPE = 18,
SYSTEM_SCOPED_POLICY_ID_ACE_TYPE = 19,
SYSTEM_PROCESS_TRUST_LABEL_ACE_TYPE = 20,
};
enum ACE_FLAGS : u8 {
OBJECT_INHERIT_ACE = 0x01,
CONTAINER_INHERIT_ACE = 0x02,
NO_PROPAGATE_INHERIT_ACE = 0x04,
INHERIT_ONLY_ACE = 0x08,
INHERITED_ACE = 0x10,
VALID_INHERIT_FLAGS = 0x1f,
SUCCESSFUL_ACCESS_ACE_FLAG = 0x40,
FAILED_ACCESS_ACE_FLAG = 0x80,
};
struct ACE_HEADER {
ACE_TYPES type;
ACE_FLAGS flags;
u16 size;
};
struct SID {
u8 revision;
u8 sub_authority_count;
u8 identifier_authority[6];
u32 sub_authority[sub_authority_count];
};
enum ACCESS_MASK : u32 {
FILE_READ_DATA = 0x00000001,
FILE_LIST_DIRECTORY = 0x00000001,
FILE_WRITE_DATA = 0x00000002,
FILE_ADD_FILE = 0x00000002,
FILE_APPEND_DATA = 0x00000004,
FILE_ADD_SUBDIRECTORY = 0x00000004,
FILE_READ_EA = 0x00000008,
FILE_WRITE_EA = 0x00000010,
FILE_EXECUTE = 0x00000020,
FILE_TRAVERSE = 0x00000020,
FILE_DELETE_CHILD = 0x00000040,
FILE_READ_ATTRIBUTES = 0x00000080,
FILE_WRITE_ATTRIBUTES = 0x00000100,
DELETE = 0x00010000,
READ_CONTROL = 0x00020000,
WRITE_DAC = 0x00040000,
WRITE_OWNER = 0x00080000,
SYNCHRONIZE = 0x00100000,
STANDARD_RIGHTS_READ = 0x00020000,
STANDARD_RIGHTS_WRITE = 0x00020000,
STANDARD_RIGHTS_EXECUTE = 0x00020000,
STANDARD_RIGHTS_REQUIRED = 0x000f0000,
STANDARD_RIGHTS_ALL = 0x001f0000,
ACCESS_SYSTEM_SECURITY = 0x01000000,
MAXIMUM_ALLOWED = 0x02000000,
GENERIC_ALL = 0x10000000,
GENERIC_EXECUTE = 0x20000000,
GENERIC_WRITE = 0x40000000,
GENERIC_READ = 0x80000000,
};
struct ACCESS_ALLOWED_ACE {
ACE_HEADER header [[inline]];
ACCESS_MASK mask;
SID sid;
};
using ACCESS_DENIED_ACE = ACCESS_ALLOWED_ACE;
using SYSTEM_ALARM_ACE = ACCESS_ALLOWED_ACE;
using SYSTEM_AUDIT_ACE = ACCESS_ALLOWED_ACE;
enum OBJECT_ACE_FLAGS : u32 {
ACE_OBJECT_TYPE_PRESENT = 1,
ACE_INHERITED_OBJECT_TYPE_PRESENT = 2,
};
struct ACCESS_ALLOWED_OBJECT_ACE {
ACE_HEADER header [[inline]];
ACCESS_MASK mask;
OBJECT_ACE_FLAGS object_flags;
GUID object_type;
GUID inherited_object_type;
SID sid;
};
using ACCESS_DENIED_OBJECT_ACE = ACCESS_ALLOWED_OBJECT_ACE;
using SYSTEM_AUDIT_OBJECT_ACE = ACCESS_ALLOWED_OBJECT_ACE;
using SYSTEM_ALARM_OBJECT_ACE = ACCESS_ALLOWED_OBJECT_ACE;
struct ACL {
u8 revision;
u8 alignment1;
u16 size;
u16 ace_count;
u16 alignment2;
std::mem::AlignTo<4>;
padding[size];
std::mem::AlignTo<4>;
};
#define SECURITY_DESCRIPTOR_RELATIVE_SIZE (20)
struct SECURITY_DESCRIPTOR_RELATIVE {
u32 struct_start = $;
u8 revision;
u8 alignment;
u16 control; // SECURITY_DESCRIPTOR_CONTROL
u32 owner;
u32 group;
u32 sacl;
u32 dacl;
if (owner > 0) {
u32 owner_bytes = $ - struct_start;
if (owner > owner_bytes) {
padding[owner - owner_bytes];
}
SID owner_sid;
}
if (group > 0) {
u32 group_bytes = $ - struct_start;
if (group > group_bytes) {
padding[group - group_bytes];
}
SID group_sid;
}
if (control & SECURITY_DESCRIPTOR_CONTROL::SE_SACL_PRESENT) {
u32 sacl_bytes = $ - struct_start;
if (sacl > sacl_bytes) {
padding[sacl - sacl_bytes];
ACL acl;
}
}
if (control & SECURITY_DESCRIPTOR_CONTROL::SE_DACL_PRESENT) {
u32 dacl_bytes = $ - struct_start;
if (dacl > dacl_bytes) {
padding[dacl - dacl_bytes];
ACL acl;
}
}
};
enum INDEX_HEADER_FLAGS : u8 {
SMALL_INDEX = 0,
LARGE_INDEX = 1,
LEAF_NODE = 0,
INDEX_NODE = 1,
NODE_MASK = 1,
};
struct INDEX_HEADER {
u32 entries_offset;
u32 index_length;
u32 allocated_size;
INDEX_HEADER_FLAGS ih_flags;
u8 reserved[3];
};
struct REPARSE_INDEX_KEY {
u32 reparse_tag;
leMFT_REF file_id;
};
struct SDS_ENTRY {
u32 hash;
u32 security_id;
u64 offset;
u32 length;
SECURITY_DESCRIPTOR_RELATIVE sid;
};
using SII_INDEX_KEY = u32 ;
struct SDH_INDEX_KEY {
u32 hash;
u32 security_id;
};
enum INDEX_ENTRY_FLAGS : u16 {
INDEX_ENTRY_NODE = 1,
INDEX_ENTRY_END = 2,
INDEX_ENTRY_SPACE_FILLER = 0xffff,
};
struct INDEX_ENTR_HEADER_PTR {
u16 data_offset;
u16 data_length;
u32 reservedV;
};
union INDEX_ENTR_HEADER_REF {
leMFT_REF indexed_file;
INDEX_ENTR_HEADER_PTR ptr;
};
struct INDEX_ENTRY_HEADER {
INDEX_ENTR_HEADER_REF file_ref;
u16 length;
u16 key_length;
u16 flags; // INDEX_ENTRY_FLAGS
u16 reserved;
};
struct INDEX_ENTRY_FILE_NAME {
INDEX_ENTRY_HEADER header;
if (!(header.flags & INDEX_ENTRY_FLAGS::INDEX_ENTRY_END)) {
FILE_NAME_ATTR key;
}
if (header.flags & INDEX_ENTRY_FLAGS::INDEX_ENTRY_NODE) {
leVCN vcn;
}
std::mem::AlignTo<8>;
};
struct INDEX_ENTRY_SII {
INDEX_ENTRY_HEADER header;
if (!(header.flags & INDEX_ENTRY_FLAGS::INDEX_ENTRY_END)) {
SII_INDEX_KEY key;
}
if (header.flags & INDEX_ENTRY_FLAGS::INDEX_ENTRY_NODE) {
leVCN vcn;
}
std::mem::AlignTo<8>;
};
struct INDEX_ENTRY_SDH {
INDEX_ENTRY_HEADER header;
if (!(header.flags & INDEX_ENTRY_FLAGS::INDEX_ENTRY_END)) {
SDH_INDEX_KEY key;
}
if (header.flags & INDEX_ENTRY_FLAGS::INDEX_ENTRY_NODE) {
leVCN vcn;
}
std::mem::AlignTo<8>;
};
struct INDEX_ENTRY_OBJ_ID {
INDEX_ENTRY_HEADER header;
if (!(header.flags & INDEX_ENTRY_FLAGS::INDEX_ENTRY_END)) {
GUID key;
}
if (header.flags & INDEX_ENTRY_FLAGS::INDEX_ENTRY_NODE) {
leVCN vcn;
}
std::mem::AlignTo<8>;
};
struct INDEX_ENTRY_REPARSE {
INDEX_ENTRY_HEADER header;
if (!(header.flags & INDEX_ENTRY_FLAGS::INDEX_ENTRY_END)) {
REPARSE_INDEX_KEY key;
}
if (header.flags & INDEX_ENTRY_FLAGS::INDEX_ENTRY_NODE) {
leVCN vcn;
}
std::mem::AlignTo<8>;
};
struct INDEX_ENTRY_SID {
INDEX_ENTRY_HEADER header;
if (!(header.flags & INDEX_ENTRY_FLAGS::INDEX_ENTRY_END)) {
SID key;
}
if (header.flags & INDEX_ENTRY_FLAGS::INDEX_ENTRY_NODE) {
leVCN vcn;
}
std::mem::AlignTo<8>;
};
struct INDEX_ENTRY_OWNER_ID {
INDEX_ENTRY_HEADER header;
if (!(header.flags & INDEX_ENTRY_FLAGS::INDEX_ENTRY_END)) {
u64 key;
}
if (header.flags & INDEX_ENTRY_FLAGS::INDEX_ENTRY_NODE) {
leVCN vcn;
}
std::mem::AlignTo<8>;
};
fn index_get_flags(u64 offset) {
INDEX_ENTRY_HEADER index_entry_header @ offset;
return index_entry_header.flags;
};
struct INDEX_BLOCK {
NTFS_RECORD header [[inline]];
leLSN lsn;
leVCN index_block_vcn;
u32 index_head = $;
INDEX_HEADER index;
if (index.entries_offset > sizeof (index)) {
padding[index.entries_offset - sizeof (index)];
}
INDEX_ENTRY_FILE_NAME ents[while(!(index_get_flags($) & INDEX_ENTRY_FLAGS::INDEX_ENTRY_END))];
INDEX_ENTRY_FILE_NAME ent_end;
u32 index_used = $ - index_head;
if (index.index_length > index_used) {
padding[index.index_length - index_used];
}
};
struct INDEX_ROOT {
ATTR_TYPES type;
COLLATION_RULES collation_rule;
u32 index_block_size;
s8 clusters_per_index_block;
u8 reserved[3];
u32 index_head = $;
INDEX_HEADER index;
if (index.entries_offset > sizeof (index)) {
padding[index.entries_offset - sizeof (index)];
}
match (type) {
(ATTR_TYPES::AT_FILE_NAME): {
INDEX_ENTRY_FILE_NAME ents[while(!(index_get_flags($) & INDEX_ENTRY_FLAGS::INDEX_ENTRY_END))];
INDEX_ENTRY_FILE_NAME ent_end;
}
}
u32 index_used = $ - index_head;
if (index.index_length > index_used) {
padding[index.index_length - index_used];
}
};
struct RESTART_PAGE_HEADER {
NTFS_RECORD_TYPES magic;
u16 usa_ofs;
u16 usa_count;
leLSN chkdsk_lsn;
u32 system_page_size;
u32 log_page_size;
u16 restart_area_offset;
u16 minor_ver;
u16 major_ver;
u16 usn;
};
enum RESTART_AREA_FLAGS : u16 {
RESTART_VOLUME_IS_CLEAN = 0x0002,
RESTART_SPACE_FILLER = 0xffff,
};
struct RESTART_AREA {
leLSN current_lsn;
u16 log_clients;
u16 client_free_list;
u16 client_in_use_list;
RESTART_AREA_FLAGS flags;
u32 seq_number_bits;
u16 restart_area_length;
u16 client_array_offset;
u64 file_size;
u32 last_lsn_data_length;
u16 log_record_header_length;
u16 log_page_data_offset;
u32 restart_log_open_count;
u32 reserved;
};
struct LOG_CLIENT_RECORD {
leLSN oldest_lsn;
leLSN client_restart_lsn;
u16 prev_client;
u16 next_client;
u16 seq_number;
u8 reserved[6];
u32 client_name_length;
ntfschar client_name[64];
};
#define ATTR_RECORD_HEADER_SIZE (16)
#define ATTR_RECORD_RESIDENT_SIZE (16 + 8)
#define ATTR_RECORD_NONRESIDENT_SIZE (16 + 48)
#define ATTR_RECORD_NONRESIDENT_CMPR_SIZE (16 + 48 + 8)
struct ATTR_RECORD {
ATTR_TYPES type;
u32 length;
u8 non_resident;
u8 name_length;
u16 name_offset;
u16 flags; // ATTR_FLAGS
u16 instance;
u32 name_offset_delta = 0;
if (!non_resident) {
u32 value_length;
u16 value_offset;
RESIDENT_ATTR_FLAGS resident_flags;
s8 reservedR;
if (name_offset > ATTR_RECORD_RESIDENT_SIZE) {
name_offset_delta = name_offset - ATTR_RECORD_RESIDENT_SIZE;
padding[name_offset_delta];
}
} else {
leVCN lowest_vcn;
leVCN highest_vcn;
u16 mapping_pairs_offset;
u8 compression_unit;
u8 reserved1[5];
u64 allocated_size;
u64 data_size;
u64 initialized_size;
if (flags & ATTR_FLAGS::ATTR_IS_COMPRESSED) {
u64 compressed_size;
if (name_offset > ATTR_RECORD_NONRESIDENT_CMPR_SIZE) {
name_offset_delta = name_offset - ATTR_RECORD_NONRESIDENT_CMPR_SIZE;
padding[name_offset_delta];
}
} else {
if (name_offset > ATTR_RECORD_NONRESIDENT_SIZE) {
name_offset_delta = name_offset - ATTR_RECORD_NONRESIDENT_SIZE;
padding[name_offset_delta];
}
}
}
u32 name_length_bytes = name_length * sizeof (ntfschar);
if (name_length > 0) {
ntfschar name[name_length];
}
if (non_resident) {
if (flags & ATTR_FLAGS::ATTR_IS_COMPRESSED) {
if (mapping_pairs_offset > (ATTR_RECORD_NONRESIDENT_CMPR_SIZE + name_offset_delta + name_length_bytes)) {
padding[mapping_pairs_offset - (ATTR_RECORD_NONRESIDENT_CMPR_SIZE + name_length_bytes)];
}
} else {
if (mapping_pairs_offset > (ATTR_RECORD_NONRESIDENT_SIZE + name_offset_delta + name_length_bytes)) {
padding[mapping_pairs_offset - (ATTR_RECORD_NONRESIDENT_SIZE + name_length_bytes)];
}
}
RUNLIST runlist[while(true)];
} else {
u32 value_offset_delta = 0;
if (value_offset > (ATTR_RECORD_RESIDENT_SIZE + name_offset_delta + name_length_bytes)) {
value_offset_delta = value_offset - (ATTR_RECORD_RESIDENT_SIZE + name_offset_delta + name_length_bytes);
padding[value_offset_delta];
}
match (type) {
(ATTR_TYPES::AT_UNUSED): {
u32 attr_sum = (ATTR_RECORD_RESIDENT_SIZE + name_offset_delta + name_length_bytes + value_offset_delta);
if (length > attr_sum) {
padding[length - attr_sum];
}
}
(ATTR_TYPES::AT_STANDARD_INFORMATION): {
if (value_length > sizeof (STANDARD_INFORMATION_OLD)) {
STANDARD_INFORMATION info;
} else {
STANDARD_INFORMATION_OLD info;
}
u32 attr_sum = (ATTR_RECORD_RESIDENT_SIZE + name_offset_delta + name_length_bytes + value_offset_delta + sizeof (info));
if (length > attr_sum) {
padding[length - attr_sum];
}
}
(ATTR_TYPES::AT_ATTRIBUTE_LIST): {
u32 attr_sum = (ATTR_RECORD_RESIDENT_SIZE + name_offset_delta + name_length_bytes + value_offset_delta);
if (length > attr_sum) {
padding[length - attr_sum];
}
}
(ATTR_TYPES::AT_FILE_NAME): FILE_NAME_ATTR file;
(ATTR_TYPES::AT_OBJECT_ID): {
u32 attr_sum = (ATTR_RECORD_RESIDENT_SIZE + name_offset_delta + name_length_bytes + value_offset_delta);
if (length > attr_sum) {
padding[length - attr_sum];
}
}
(ATTR_TYPES::AT_SECURITY_DESCRIPTOR): {
SECURITY_DESCRIPTOR_RELATIVE sec;
u32 attr_sum = (ATTR_RECORD_RESIDENT_SIZE + name_offset_delta + name_length_bytes + value_offset_delta + sizeof (sec));
if (length > attr_sum) {
padding[length - attr_sum];
}
}
(ATTR_TYPES::AT_VOLUME_NAME): ntfschar vol_name[value_length / sizeof (ntfschar)];
(ATTR_TYPES::AT_VOLUME_INFORMATION): VOLUME_INFORMATION vol_info;
(ATTR_TYPES::AT_DATA): {
u8 buffer[value_length];
u32 attr_sum = (ATTR_RECORD_RESIDENT_SIZE + name_offset_delta + name_length_bytes + value_offset_delta + sizeof (buffer));
if (length > attr_sum) {
padding[length - attr_sum];
}
}
(ATTR_TYPES::AT_INDEX_ROOT): {
INDEX_ROOT root;
}
(ATTR_TYPES::AT_INDEX_ALLOCATION): {
u32 attr_sum = (ATTR_RECORD_RESIDENT_SIZE + name_offset_delta + name_length_bytes + value_offset_delta);
if (length > attr_sum) {
padding[length - attr_sum];
}
}
(ATTR_TYPES::AT_BITMAP): u8 buffer[value_length];
(ATTR_TYPES::AT_REPARSE_POINT): {
u32 attr_sum = (ATTR_RECORD_RESIDENT_SIZE + name_offset_delta + name_length_bytes + value_offset_delta);
if (length > attr_sum) {
padding[length - attr_sum];
}
}
(ATTR_TYPES::AT_EA_INFORMATION): {
u32 attr_sum = (ATTR_RECORD_RESIDENT_SIZE + name_offset_delta + name_length_bytes + value_offset_delta);
if (length > attr_sum) {
padding[length - attr_sum];
}
}
(ATTR_TYPES::AT_EA): {
u32 attr_sum = (ATTR_RECORD_RESIDENT_SIZE + name_offset_delta + name_length_bytes + value_offset_delta);
if (length > attr_sum) {
padding[length - attr_sum];
}
}
(ATTR_TYPES::AT_PROPERTY_SET): {
u32 attr_sum = (ATTR_RECORD_RESIDENT_SIZE + name_offset_delta + name_length_bytes + value_offset_delta);
if (length > attr_sum) {
padding[length - attr_sum];
}
}
(ATTR_TYPES::AT_LOGGED_UTILITY_STREAM): {
u32 attr_sum = (ATTR_RECORD_RESIDENT_SIZE + name_offset_delta + name_length_bytes + value_offset_delta);
if (length > attr_sum) {
padding[length - attr_sum];
}
}
(ATTR_TYPES::AT_FIRST_USER_DEFINED_ATTRIBUTE): {
u32 attr_sum = (ATTR_RECORD_RESIDENT_SIZE + name_offset_delta + name_length_bytes + value_offset_delta);
if (length > attr_sum) {
padding[length - attr_sum];
}
}
(ATTR_TYPES::AT_END): {
// length is no longer valid
}
}
}
std::mem::AlignTo<8>;
};
fn attr_get_type(u64 offset) {
ATTR_RECORD attr @ offset;
return attr.type;
};
#define MFT_RECORD_SIZE (sizeof (NTFS_RECORD) + 40)
struct MFT_RECORD {
NTFS_RECORD header [[inline]];
leLSN lsn;
u16 sequence_number;
u16 link_count;
u16 attrs_offset;
MFT_RECORD_FLAGS flags;
u32 bytes_in_use;
u32 bytes_allocated;
leMFT_REF base_mft_record;
u16 next_attr_instance;
u16 reserved;
u32 mft_record_number;
if (header.usa_count > 0) {
padding[header.usa_ofs - MFT_RECORD_SIZE];
}
u16 update_sequence[header.usa_count];
std::mem::AlignTo<8>;
ATTR_RECORD attrs[while(attr_get_type($) != ATTR_TYPES::AT_END)];
ATTR_RECORD attr_end;
};
struct NTFS_BOOT_SECTOR {
u8 jump[3];
u64 oem_id;
BIOS_PARAMETER_BLOCK bpb;
u8 physical_drive;
u8 current_head;
u8 extended_boot_signature;
u8 reserved2;
u64 number_of_sectors;
u64 mft_lcn;
u64 mftmirr_lcn;
s8 clusters_per_mft_record;
u8 reserved0[3];
s8 clusters_per_index_record;
u8 reserved1[3];
u64 volume_serial_number;
u32 checksum;
u8 bootstrap[426];
u16 end_of_sector_marker;
};
fn mft_get_dat_attr_lcn(MFT_RECORD record, ATTR_TYPES type = ATTR_TYPES::AT_DATA) {
u64 lcn = 0;
for (u64 i = 0, i < record.next_attr_instance, i += 1) {
if (record.attrs[i].type == type) {
for (s64 j = record.attrs[i].runlist[0].header.offset - 1, j >= 0, j -= 1) {
lcn |= record.attrs[i].runlist[0].lcn[j] << (8 * j);
}
break;
}
}
return lcn;
};
// ============= VBR =============
NTFS_BOOT_SECTOR nbs @ 0x00 [[name("VBR")]];
u32 cluster_size = nbs.bpb.bytes_per_sector * nbs.bpb.sectors_per_cluster;
u32 mft_rec_size = 1 << -nbs.clusters_per_mft_record;
// ============= $MFT =============
MFT_RECORD mft @ nbs.mft_lcn * cluster_size + 0 * mft_rec_size [[name("$MFT")]];
MFT_RECORD mftmirr @ nbs.mftmirr_lcn * cluster_size [[name("$MFT Copy")]];
// ============= $MFTMirr =============
MFT_RECORD mft_mirr @ nbs.mft_lcn * cluster_size + 1 * mft_rec_size [[name("$MFTMirr")]];
// ============= $LogFile =============
MFT_RECORD mft_log @ nbs.mft_lcn * cluster_size + 2 * mft_rec_size [[name("$LogFile")]];
// ============= $Volume =============
MFT_RECORD mft_vol @ nbs.mft_lcn * cluster_size + 3 * mft_rec_size [[name("$Volume")]];
// ============= $AttrDef =============
MFT_RECORD mft_adef @ nbs.mft_lcn * cluster_size + 4 * mft_rec_size [[name("$AttrDef")]];
ATTR_DEF adef_dat_buf[16] @ mft_get_dat_attr_lcn(mft_adef) * cluster_size [[name("$AttrDef, array")]];
// ============= $I30 (Root) =============
MFT_RECORD mft_root @ nbs.mft_lcn * cluster_size + 5 * mft_rec_size [[name("$I30 (Root)")]];
INDEX_BLOCK root_index_block @ mft_get_dat_attr_lcn(mft_root, ATTR_TYPES::AT_INDEX_ALLOCATION) * cluster_size [[name("$I30 (Root), INDEX_BLOCK")]];
// ============= $Bitmap =============
MFT_RECORD mft_bm @ nbs.mft_lcn * cluster_size + 6 * mft_rec_size [[name("$Bitmap")]];
// ============= $Boot =============
MFT_RECORD mft_boot @ nbs.mft_lcn * cluster_size + 7 * mft_rec_size [[name("$Boot")]];
// ============= $BadClus =============
MFT_RECORD mft_badclus @ nbs.mft_lcn * cluster_size + 8 * mft_rec_size [[name("$BadClus")]];
// ============= $Secure ($Quota for NTFS 1.2) =============
MFT_RECORD mft_sec @ nbs.mft_lcn * cluster_size + 9 * mft_rec_size [[name("$Secure")]];
// ============= $UpCase =============
MFT_RECORD mft_uc @ nbs.mft_lcn * cluster_size + 10 * mft_rec_size [[name("$UpCase")]];
u8 uc_tbl[1] @ mft_get_dat_attr_lcn(mft_uc) * cluster_size [[name("$UpCase, tabl")]];
// ============= $Extend =============
MFT_RECORD mft_ext @ nbs.mft_lcn * cluster_size + 11 * mft_rec_size [[name("$Extend")]];

231
patterns/fs/pattern.hexpat Normal file
View File

@@ -0,0 +1,231 @@
#pragma author WerWolv
#pragma description Drive File System
#pragma MIME application/x-ima
import std.io;
import std.core;
import type.magic;
import type.guid;
import type.base;
import * from fs.fat32 as FAT32Partition;
import * from fs.exfat as ExFATPartition;
import * from fs.ntfs as NTFSPartition;
const u32 SectorSize = 512;
struct DiskTimeStamp {
u8 seconds, minutes, hours;
};
enum DiskProtection : u16 {
None = 0x0000,
CopyProtected = 0x5A5A
};
bitfield CHS {
h : 8;
s : 6;
c : 10;
} [[format("chs_formatter")]];
fn chs_formatter(CHS chs) {
return std::format("({:X}, {:X}, {:X}) | 0x{:X}", chs.c, chs.h, chs.s, (chs.c * 16 + chs.h) * 63 + (chs.s - 1));
};
enum PartitionStatus : u8 {
None = 0x00,
Active = 0x80
};
enum MBRPartitionType : u8 {
Empty = 0x00,
FAT12 = 0x01,
XENIXRoot = 0x02,
XENIXUsr = 0x03,
FAT16_16_32MB = 0x04,
ExtendedCHS = 0x05,
FAT16_32MBPlus = 0x06,
NTFS = 0x07,
AIX = 0x08,
AIXBootable = 0x09,
OS2BootManager = 0x0A,
FAT32_CHS = 0x0B,
FAT32_LBA = 0x0C,
FAT16_LBA = 0x0E,
ExtendedLBA = 0x0F,
OPUS = 0x10,
HiddenFAT12 = 0x11,
CompaqDiagnostics = 0x12,
HiddenFAT16_16_32MB = 0x14,
HiddenFAT16_32MBPlus = 0x16,
HiddenNTFS = 0x17,
ASTSmartSleep = 0x18,
HiddenFAT32_CHS = 0x1B,
HiddenFAT32_LBA = 0x1C,
HiddenFAT16_LBA = 0x1E,
NEC_DOS = 0x24,
WindowsRecovery = 0x27,
Plan9 = 0x39,
PowerQuest = 0x3C,
Venix286 = 0x40,
PPC_PReP_Boot = 0x41,
SFS = 0x42,
QNX4_x = 0x4D,
QNX4_x_2ndPart = 0x4E,
QNX4_x_3rdPart = 0x4F,
OnTrackDM = 0x50,
OnTrackDM6Aux1 = 0x51,
CP_M = 0x52,
OnTrackDM6Aux3 = 0x53,
OnTrackDM6 = 0x54,
EZDrive = 0x55,
GoldenBow = 0x56,
PriamEDisk = 0x5C,
SpeedStor = 0x61,
GNU_HURD = 0x63,
NovellNetware286 = 0x64,
NovellNetware386 = 0x65,
DiskSecureMultiBoot = 0x70,
PC_IX = 0x75,
XOSL = 0x78,
OldMinix = 0x80,
LinuxMinix = 0x81,
LinuxSwap = 0x82,
Linux = 0x83,
OS2HiddenCDrive = 0x84,
LinuxExtended = 0x85,
NTFSVolumeSet = 0x86,
NTFSVolumeSet2 = 0x87,
LinuxLVM = 0x8E,
Amoeba = 0x93,
AmoebaBBT = 0x94,
BSD_OS = 0x9F,
IBMThinkpadHibernation = 0xA0,
FreeBSD = 0xA5,
OpenBSD = 0xA6,
NeXTSTEP = 0xA7,
MacOSX = 0xA8,
NetBSD = 0xA9,
BSDIFS = 0xB7,
BSDISwap = 0xB8,
BootWizardHidden = 0xBB,
DRDOSFAT12 = 0xC1,
DRDOSFAT16 = 0xC4,
DRDOSFAT16B = 0xC6,
Syrinx = 0xC7,
NonFSData = 0xDA,
CP_M_CTOS = 0xDB,
DellUtility = 0xDE,
BootIt = 0xDF,
DOSAccess = 0xE1,
DOSRO = 0xE3,
SpeedStor2 = 0xE4,
BeOS = 0xEB,
GPTProtective = 0xEE,
EFI_System = 0xEF,
LinuxPA_RISC = 0xF0,
SpeedStor3 = 0xF1,
DOSSecondary = 0xF2,
LinuxRAID = 0xFD,
LANstep = 0xFE,
Unknown = 0xFF
};
enum GPTPartitionType : u128 {
UnusedEntry = u128("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"),
MicrosoftReservedPartition = u128("\x16\xE3\xC9\xE3\x5C\x0B\xB8\x4D\x81\x7D\xF9\x2D\xF0\x02\x15\xAE"),
PartitionBasicData = u128("\xA2\xA0\xD0\xEB\xE5\xB9\x33\x44\x87\xC0\x68\xB6\xB7\x26\x99\xC7")
};
bitfield GPTAttributes {
bool platform_required : 1;
padding : 59;
bool read_only : 1;
bool shadow_copy : 1;
bool hidden : 1;
bool no_drive_letter : 1;
};
struct GPTEntry {
GPTPartitionType partitionType [[no_unique_address]];
type::GUID partitionTypeGuid;
type::GUID partitionGuid;
u64 firstLba;
u64 lastLba;
GPTAttributes attribute;
char16 partitionName[36];
padding[parent.entrySize - 128];
u64 partitionOffset = firstLba * SectorSize [[export]];
match (partitionType) {
(GPTPartitionType::UnusedEntry): {}
(GPTPartitionType::MicrosoftReservedPartition):
std::mem::Bytes<(lastLba - firstLba + 1) * SectorSize> microsoftReservedPartition @ partitionOffset;
(GPTPartitionType::PartitionBasicData): {}
(_): std::error(std::format("Unknown GPT Partition Type {}", partitionType));
}
};
struct GUIDPartitionTable {
type::Magic<"EFI PART"> signature;
u16 versionMinor;
u16 versionMajor;
u32 headerSize;
type::Hex<u32> headerCrc32;
u32 reserved;
u64 currentLba;
u64 backupLba;
u64 firstUsableLba;
u64 lastUsableLba;
type::GUID diskGuid;
u64 entryLba;
u32 entryCount;
u32 entrySize;
type::Hex<u32> entryCrc32;
GPTEntry entries[entryCount] @ addressof(this) + (entryLba - currentLba) * SectorSize;
};
struct PartitionEntry {
PartitionStatus status;
CHS chsFirstSectorAddress;
MBRPartitionType type;
CHS chsLastSectorAddress;
u32 lbaFirstSectorAddress;
u32 numSectors;
u64 partitionOffset = lbaFirstSectorAddress * SectorSize [[export]];
match (type) {
(MBRPartitionType::Empty): continue;
(MBRPartitionType::FAT32_LBA | MBRPartitionType::FAT32_CHS):
FAT32Partition fat32Partition @ partitionOffset;
(MBRPartitionType::NTFS): {
char magic[8] @ partitionOffset + 3;
if (magic == "NTFS ")
NTFSPartition ntfsPartition @ partitionOffset;
else if (magic == "EXFAT ")
ExFATPartition exfatPartiton @ partitionOffset;
else
std::error("Unknown MBR partition with NTFS Type ID");
}
(MBRPartitionType::GPTProtective):
GUIDPartitionTable gpt @ partitionOffset;
(_): std::error(std::format("Unknown MBR Partition Type {}", type));
}
};
struct MasterBootRecord {
u8 bootstrapCodeArea1[218];
padding[2];
u8 originalPhysicalDrive;
DiskTimeStamp diskTimeStamp;
u8 bootstrapCodeArea2[216];
u32 diskSignature;
DiskProtection diskProtection;
PartitionEntry partitionEntries[4];
u16 bootSignature;
};
MasterBootRecord mbr @ 0x00;

286
patterns/fs/refs.hexpat Normal file
View File

@@ -0,0 +1,286 @@
#pragma author 5h4rrK // https://www.github.com/5h4rrK
#pragma organization temabi0s // (https://bi0s.in)
#pragma description ReFS-File-System
#pragma array_limit 10000
import type.types.win32;
import type.guid;
import std.mem;
enum FILESYSTEM : u64 {
ReFS = 0x53466552
};
struct ReFS_Version {
u8 Major;
u8 Minor;
};
struct CheckPoint_REFS_Version {
u16 Major;
u16 Minor;
};
enum BLOCK : u32 {
SuperBlock = 0x42505553,
CheckPoint = 0x504b4843,
MSBPlus = 0x2b42534d
};
struct SELF_DESCRIPTOR{
u32 start = $;
u64 LCN1[[name("FirstLCN")]];
u64 LCN2[[name("SecondLCN")]];
u64 LCN3[[name("ThirdLCN")]];
u64 LCN4[[name("FourthLCN")]];
$ = $+2;
u8 checksumtype[[name("ChecksumType")]];
u8 checksumoffset[[name("ChecksumOffset")]];
u16 checksumlen[[name("CheckSumLength")]];
$ = $+2;
if(checksumtype == 2){
u64 chksum[[comment("Crc64"),name("CRC64-CheckSum") ]];}
else if(checksumtype == 1){
u32 chksum[[comment("Crc32"),name("CRC32-CheckSum") ]];
}
u32 rem = this.parent.lenselfdescriptor - ($ - start);
BYTE buff[rem];
};
struct META_HEADERS {
BLOCK Signature[[comment("Block Signature"), name("BlockSignature")]];
u32 unk[[comment("Fixed Value 0x02"), name("Unknown")]];
u32 zero[[name("AlwaysZero")]];
u32 volsig[[name("VolumeSignature")]];
u128 blockNo[[comment("Most Recent Block"), name("PageRecency")]];
u64 LCN1[[comment("MetaPage 1st LogicalClusterNumber"), name("FirstLCN")]];
u64 LCN2[[comment("MetaPage 2nd LogicalClusterNumber"), name("SecondLCN")]];
u64 LCN3[[comment("MetaPage 3rd LogicalClusterNumber"), name("ThirdLCN")]];
u64 LCN4[[comment("MetaPage 4th LogicalClusterNumber"), name("FourthLCN")]];
u64 tableidlo[[name("TableIdentifierHigh")]];
u64 tableidhi[[name("TableIdentifierLow")]];
};
struct OFFSET_ENTRIES{
u16 offset[[name("Offset")]];
u16 unk[[name("Ignore")]];
};
struct ATTRIBUTE {
u64 LCN1[[name("FirstLCN")]];
u64 LCN2[[name("SecondLCN")]];
u64 LCN3[[name("ThirdLCN")]];
u64 LCN4[[name("FourthLCN")]];
u32 Unk1[[comment("UnknownField"), name("Unknown1")]];
u32 Unk2[[comment("UnknownField"), name("Unknown2")]];
u64 checksum[[name("CheckSum")]];
BYTE ZeroPadding[56][[name("Padding")]];
};
enum NodeType : BYTE {
InnerNode = 0x01,
RootNode = 0x02,
StreamNode = 0x03,
};
struct INDEX_HEADER {
u32 size[[name("DataStartOffset")]];
u32 dataendoff[[name("DataAreaEndOffset")]];
u32 freebuff[[name("FreeBuff")]];
BYTE height[[name("Height")]];
NodeType ntype[[name("NodeType")]];
u16 unused1[[name("UnUsed")]];
u32 keyindxoff[[name("KeyIndexOffset")]];
u32 keycount[[name("KeysCount")]];
u64 unused2[[name("UnUsed")]];
u32 end[[name("KeyIndexEnd")]];
u32 align[[name("Alignment")]];
$ = this.parent.start_pos + 0x50 + this.parent.rootsize + keyindxoff;
OFFSET_ENTRIES entries[keycount][[name("KeyEntries")]];
};
enum Flags : u16 {
RightMost = 0x02,
DeletedEntry = 0x04,
StreamIndexEntry = 0x40,
};
struct INDEX_ENTRY_STRUCTURE {
u32 start_pos = $;
u32 indxentlen[[name("IndxEntryLen")]];
u16 keyoff[[name("KeyOffset")]];
u16 keylen[[name("KeyLen")]];
Flags flag[[name("Flags")]];
u16 valoff[[name("ValOffset")]];
u16 vallen[[name("ValLen")]];
$ = start_pos + keyoff;
char key[keylen][[name("Key")]];
$ = start_pos + valoff;
char value[vallen][[name("Value")]];
$ = start_pos + indxentlen;
};
struct ROOT_NODE{
u32 start_pos = $;
META_HEADERS pagehdr[[name("PageHeader")]];
u32 rootsize[[name("RootSize")]];
u16 fixed[[name("Fixed(0x28)")]];
u16 unk1[[name("Unknown")]];
u16 unk2[[name("Unknown")]];
u16 unk3[[name("Unknown")]];
u16 schema[[name("TableSchema")]];
u16 unk4[[name("Unknown")]];
u16 schemadup[[name("TableSchema")]];
u16 unk5[[name("Unknown")]];
u16 unk6[[name("Unknown")]];
u16 unk7[[name("Unknown")]];
u64 noofextends[[name("ExtendCount")]];
u64 noofrows[[name("RowsCount")]];
char comps[rootsize - ($ - start_pos) + 0x50][[name("Components")]];
$ = (start_pos + 0x50 + rootsize);
INDEX_HEADER indxhdr[[name("IndexHeader")]];
// $ = start_pos + 0x50 + rootsize + indxhdr.keyindxoff;
$ = (start_pos + 0x50 + rootsize + indxhdr.size);
INDEX_ENTRY_STRUCTURE indxentrystruct[indxhdr.keycount][[name("IndexEntry")]];
};
u32 keeptrack = 0;
struct GLOBALROOTNODE {
u32 GlobalElemEntryOff;
u32 prev = $;
$ = (0x1000 * ($ / 0x1000));
$ = $ + GlobalElemEntryOff;
keeptrack += 1;
if (keeptrack == 1) {
ATTRIBUTE ObjectIDTable;
}
else if (keeptrack == 2) {
ATTRIBUTE MediumAllocatorTable;
}
else if (keeptrack == 3) {
ATTRIBUTE ContainerAllocatorTable;
}
else if (keeptrack == 4) {
ATTRIBUTE SchemaTable;
}
else if (keeptrack == 5) {
ATTRIBUTE ParentChildTable[[comment("Parent Child Table")]];
}
else if (keeptrack == 6) {
ATTRIBUTE ObjectIDDup;
}
else if (keeptrack == 7) {
ATTRIBUTE BlockCountTable;
}
else if (keeptrack == 8) {
ATTRIBUTE ContainerTable;
u32 prev_pos = $;
ROOT_NODE node1 @ (ContainerTable.LCN1 * clustersz)[[name("ContainerNode")]];
$ = prev_pos;
}
else if (keeptrack == 9) {
ATTRIBUTE ContainerTableDup;
u32 prev_pos = $;
ROOT_NODE node1 @ (ContainerTableDup.LCN1 * clustersz)[[name("ContainerDupNode")]];
$ = prev_pos;
}
else if (keeptrack == 10) {
ATTRIBUTE SchemaTableDup;
}
else if (keeptrack == 11) {
ATTRIBUTE ContainerIndexTable;
}
else if (keeptrack == 12) {
ATTRIBUTE IntegrityStateTable;
}
else if (keeptrack == 13) {
ATTRIBUTE SmallAllocatorTable;
u32 prev_pos = $;
ROOT_NODE node1 @ (SmallAllocatorTable.LCN1 * clustersz)[[name("SmallAllocatorNode")]];
$ = prev_pos;
}
$ = prev;
};
struct CHECKPOINT {
META_HEADERS CheckPointMetaHeader[[name("FSPageMetaHeader")]];
$ += (0x04);
CheckPoint_REFS_Version ReFSVersion;
u32 offsetselfdescriptor[[comment("Self Descriptor Offset")]];
u32 lenselfdescriptor[[comment("Self Descriptor Size"),name("SelfDescriptorSz")]];
u64 blockno[[comment("Most Recent CheckPoint") , name("BlockNumber")]];
u32 prev_pos = $;
$ = ($ / clustersz) *clustersz + offsetselfdescriptor;
SELF_DESCRIPTOR selfdes[[name("SelfDescriptor")]];
$ = prev_pos;
$ += (0x28);
u32 NumOfEntries;
GLOBALROOTNODE GlobalRootNodes[NumOfEntries];
$ += (0x08);
};
struct SUPERBLOCK {
META_HEADERS SuperBlockMetaHeader[[name("FSPageMetaHeader")]];
type::GUID GUID;
$ = $ + (0x10 * 0x01);
u32 checkpointOffset[[name("CheckPointOffset")]];
u32 noofcheckpoint[[name("NoOfCheckPointEntry")]];
u32 offsetselfdescriptor[[name("SelfDescriptorOffset")]];
u32 lenselfdescriptor[[name("SelfDescriptorLength")]];
$ = $ + (0x10 * 0x04);
u64 primarychekpoint[[name("PrimaryCheckPoint")]];
u64 secondaychekpoint[[name("SecondaryCheckPoint")]];
SELF_DESCRIPTOR selfdes[[name("SelfDescriptor")]];
};
struct VOLUME_BOOT_RECORD {
BYTE jmpInstruction[3] [[comment("Jump Instruction"), name("JumpInstruction")]];
FILESYSTEM FileSystem[[comment("FileSystemName"), name("FileSystem")]];
BYTE UnKnown[5];
char Identifier[4][[comment("File System Recognition Structure, allows OS to recognise the structure"), name("FSRSIdentifier")]];
u16 Length[[comment("Size of VBR"), name("Length") ]];
u16 Checksum[[comment("Computed FileSystem Information Checksum"), name("CheckSum")]];
u64 TotalNoOfSectors;
u32 BytesPerSec[[comment("Bytes Per Sector"), name("BytesPerSector")]];
u32 SectorPerCluster[[comment("Sector Per Cluster"), name("SectorPerCluster")]];
ReFS_Version ReFSVersion;
BYTE UnknownBuff[0x0e][[name("Unknown")]];
u64 SerialNo[[name("SerialNumber")]];
u64 BytesPerContainer[[name("BytesPerContainer")]];
};
struct REFS_FILE_SYSTEM {
u64 checkVal = std::mem::read_unsigned($+3, 8);
$ = 0;
if(checkVal == FILESYSTEM::ReFS){
VOLUME_BOOT_RECORD vbr @ 0x00[[name("VolumeBootRecord")]];
SUPERBLOCK SuperBlock1 @ (0x1e * 0x1000)[[name("SuperBlock1")]];
u32 cluster_size = vbr.BytesPerSec * vbr.SectorPerCluster;
CHECKPOINT PrimaryCheckPoint @(SuperBlock1.primarychekpoint * cluster_size)[[name("PrimaryCheckPoint1")]];
keeptrack = 0;
CHECKPOINT SecondaryCheckPoint @(SuperBlock1.secondaychekpoint * cluster_size)[[name("SecondaryCheckPoint1")]];
SUPERBLOCK SuperBlock2 @(((vbr.TotalNoOfSectors / vbr.SectorPerCluster) - 3) * cluster_size)[[name("SuperBlock2")]];
keeptrack = 0;
CHECKPOINT PrimaryCheckPoint2 @(SuperBlock2.primarychekpoint * cluster_size)[[name("PrimaryCheckPoint2")]];
keeptrack = 0;
CHECKPOINT SecondaryCheckPoint2 @(SuperBlock2.secondaychekpoint * cluster_size)[[name("SecondaryCheckPoint2")]];
SUPERBLOCK SuperBlock3 @(((vbr.TotalNoOfSectors / vbr.SectorPerCluster) - 2) * cluster_size)[[name("SuperBlock3")]];
keeptrack = 0;
CHECKPOINT PrimaryCheckPoint3 @(SuperBlock3.primarychekpoint * cluster_size)[[name("PrimaryCheckPoint3")]];
keeptrack = 0;
CHECKPOINT SecondaryCheckPoint3 @(SuperBlock3.secondaychekpoint * cluster_size)[[name("SecondaryCheckPoint3")]];
}
else{
exit(0);
}
};
u32 clustersz = std::mem::read_unsigned($ + 0x20, $+4) * std::mem::read_unsigned($ + 0x24, $+4);
REFS_FILE_SYSTEM ReFSFileSystem @0x00;