mirror of
https://github.com/WerWolv/ImHex-Patterns.git
synced 2026-03-27 23:37:04 -05:00
patterns/fs: Refactor all partition types into a FS module
This commit is contained in:
311
patterns/fs/exfat.hexpat
Normal file
311
patterns/fs/exfat.hexpat
Normal 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
543
patterns/fs/ext4.hexpat
Normal 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 doesn’t 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 doesn’t 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 doesn’t 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 inode’s 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 inode’s 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 snapshot’s 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
108
patterns/fs/fat32.hexpat
Normal 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
997
patterns/fs/ntfs.hexpat
Normal 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
231
patterns/fs/pattern.hexpat
Normal 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
286
patterns/fs/refs.hexpat
Normal 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;
|
||||
Reference in New Issue
Block a user