Skip to content
API Documentation
BastionGuard™
anonymous_namespace{filesystem_probes.cpp} Namespace Reference

Functions

uint16_t le16 (const unsigned char *p)
uint32_t le32 (const unsigned char *p)
uint64_t le64 (const unsigned char *p)
bool read_sector (DiskReader &reader, uint64_t offset, std::array< unsigned char, 512 > &sector)
bool is_fat32 (const std::array< unsigned char, 512 > &sector)
bool is_exfat (const std::array< unsigned char, 512 > &sector)
bool ext4_has_magic (DiskReader &reader, uint64_t offset)
bool btrfs_has_magic (DiskReader &reader, uint64_t offset)
ScanResult fat32_probe (DiskReader &reader, uint64_t offset)
ScanResult exfat_probe (DiskReader &reader, uint64_t offset)
ScanResult ext4_probe (DiskReader &reader, uint64_t offset)
ScanResult btrfs_probe (DiskReader &reader, uint64_t offset)
ScanResult xfs_probe (DiskReader &reader, uint64_t offset)
ScanResult hfs_probe (DiskReader &reader, uint64_t offset)

Variables

constexpr uint64_t SECTOR_SIZE = 512ull

Function Documentation

◆ btrfs_has_magic()

bool anonymous_namespace{filesystem_probes.cpp}::btrfs_has_magic ( DiskReader & reader,
uint64_t offset )

Definition at line 56 of file filesystem_probes.cpp.

56 {
57 std::array<char, 8> sig{};
58 return reader.read_at(offset + 0x10040, sig.data(), sig.size()) && std::string(sig.data(), sig.size()) == "_BHRfS_M";
59}
bool read_at(uint64_t offset, void *buffer, size_t len)

References btrfs_has_magic(), and DiskReader::read_at().

Referenced by btrfs_has_magic(), and btrfs_probe().

◆ btrfs_probe()

ScanResult anonymous_namespace{filesystem_probes.cpp}::btrfs_probe ( DiskReader & reader,
uint64_t offset )

Definition at line 192 of file filesystem_probes.cpp.

192 {
193 if (!btrfs_has_magic(reader, offset)) {
194 return {};
195 }
196
197 std::array<unsigned char, 0x2000> sb{};
198 if (!reader.read_at(offset + 0x10000, sb.data(), sb.size())) {
199 return {};
200 }
201
202 const uint64_t bytenr = le64(sb.data() + 0x00);
203 const uint64_t flags = le64(sb.data() + 0x30);
204 const uint64_t generation = le64(sb.data() + 0x40);
205 const uint64_t root = le64(sb.data() + 0x50);
206 const uint64_t chunk_root = le64(sb.data() + 0x58);
207 const uint64_t total_bytes = le64(sb.data() + 0x70);
208 const uint64_t bytes_used = le64(sb.data() + 0x78);
209 const uint32_t sectorsize = le32(sb.data() + 0x90);
210 const uint32_t nodesize = le32(sb.data() + 0x94);
211 const uint32_t leafsize = le32(sb.data() + 0x98);
212 const uint32_t stripesize = le32(sb.data() + 0x9C);
213 const uint32_t num_devices = le32(sb.data() + 0xA0);
214
215 if (sectorsize == 0 || nodesize == 0 || total_bytes == 0) {
216 return {};
217 }
218
219 std::ostringstream detail;
220 detail << trs("Bytenr: ") << bytenr
221 << trs(", Generation: ") << generation
222 << ", Flags: 0x" << std::hex << flags << std::dec
223 << ", Root: " << root
224 << ", Chunk root: " << chunk_root
225 << ", Total bytes: " << total_bytes
226 << ", Used bytes: " << bytes_used
227 << ", Sector size: " << sectorsize
228 << ", Node size: " << nodesize
229 << ", Leaf size: " << leafsize
230 << ", Stripe size: " << stripesize
231 << ", Devices: " << num_devices;
232
234 offset,
235 total_bytes,
236 "Btrfs volume",
237 "Btrfs probe",
238 detail.str(),
239 0.92};
240}
std::string trs(const char *msgid)
Definition i18n.cpp:22
@ FilesystemVolume
Definition models.hpp:11
uint64_t le64(const unsigned char *p)
bool btrfs_has_magic(DiskReader &reader, uint64_t offset)
uint32_t le32(const unsigned char *p)

References btrfs_has_magic(), btrfs_probe(), FilesystemVolume, le32(), le64(), DiskReader::read_at(), and trs().

Referenced by btrfs_probe().

◆ exfat_probe()

ScanResult anonymous_namespace{filesystem_probes.cpp}::exfat_probe ( DiskReader & reader,
uint64_t offset )

Definition at line 101 of file filesystem_probes.cpp.

101 {
102 std::array<unsigned char, 512> sector{};
103 if (!read_sector(reader, offset, sector) || !is_exfat(sector)) {
104 return {};
105 }
106
107 const uint64_t partition_offset = le64(sector.data() + 64);
108 const uint64_t volume_length = le64(sector.data() + 72);
109 const uint32_t fat_offset = le32(sector.data() + 80);
110 const uint32_t fat_length = le32(sector.data() + 84);
111 const uint32_t cluster_heap_offset = le32(sector.data() + 88);
112 const uint32_t cluster_count = le32(sector.data() + 92);
113 const uint32_t root_dir_cluster = le32(sector.data() + 96);
114 const uint8_t bytes_per_sector_shift = sector[108];
115 const uint8_t sectors_per_cluster_shift = sector[109];
116
117 const uint64_t bytes_per_sector = 1ull << bytes_per_sector_shift;
118 const uint64_t sectors_per_cluster = 1ull << sectors_per_cluster_shift;
119 const uint64_t volume_size = volume_length * bytes_per_sector;
120
121 if (volume_length == 0 || bytes_per_sector < 512 || sectors_per_cluster == 0) {
122 return {};
123 }
124
125 std::ostringstream detail;
126 detail << trs("Partition offset: ") << partition_offset
127 << ", " << trs("Bytes/sector") << ": " << bytes_per_sector
128 << ", " << trs("Sectors/cluster") << ": " << sectors_per_cluster
129 << trs(", FAT offset: ") << fat_offset
130 << trs(", FAT length: ") << fat_length
131 << trs(", Cluster heap: ") << cluster_heap_offset
132 << trs(", Cluster count: ") << cluster_count
133 << trs(", Root dir cluster: ") << root_dir_cluster;
134
136 offset,
137 volume_size,
138 "exFAT volume",
139 "exFAT probe",
140 detail.str(),
141 0.94};
142}
bool is_exfat(const std::array< unsigned char, 512 > &sector)
bool read_sector(DiskReader &reader, uint64_t offset, std::array< unsigned char, 512 > &sector)

References exfat_probe(), FilesystemVolume, is_exfat(), le32(), le64(), read_sector(), and trs().

Referenced by exfat_probe().

◆ ext4_has_magic()

bool anonymous_namespace{filesystem_probes.cpp}::ext4_has_magic ( DiskReader & reader,
uint64_t offset )

Definition at line 51 of file filesystem_probes.cpp.

51 {
52 std::array<unsigned char, 2> sig{};
53 return reader.read_at(offset + 1024 + 56, sig.data(), sig.size()) && sig[0] == 0x53 && sig[1] == 0xEF;
54}

References ext4_has_magic(), and DiskReader::read_at().

Referenced by ext4_has_magic(), and ext4_probe().

◆ ext4_probe()

ScanResult anonymous_namespace{filesystem_probes.cpp}::ext4_probe ( DiskReader & reader,
uint64_t offset )

Definition at line 144 of file filesystem_probes.cpp.

144 {
145 if (!ext4_has_magic(reader, offset)) {
146 return {};
147 }
148
149 std::array<unsigned char, 1024> super{};
150 if (!reader.read_at(offset + 1024, super.data(), super.size())) {
151 return {};
152 }
153
154 const uint32_t inodes_count = le32(super.data() + 0x00);
155 const uint32_t blocks_count_lo = le32(super.data() + 0x04);
156 const uint32_t log_block_size = le32(super.data() + 0x18);
157 const uint32_t blocks_per_group = le32(super.data() + 0x20);
158 const uint32_t inodes_per_group = le32(super.data() + 0x28);
159 const uint16_t magic = le16(super.data() + 0x38);
160 const uint32_t feature_compat = le32(super.data() + 0x5C);
161 const uint32_t feature_incompat = le32(super.data() + 0x60);
162 const uint32_t feature_ro_compat = le32(super.data() + 0x64);
163 const uint16_t inode_size = le16(super.data() + 0x58);
164
165 if (magic != 0xEF53) {
166 return {};
167 }
168
169 const uint64_t block_size = 1024ull << log_block_size;
170 const uint64_t volume_size = static_cast<uint64_t>(blocks_count_lo) * block_size;
171
172 std::ostringstream detail;
173 detail << trs("Inodes: ") << inodes_count
174 << trs(", Blocks: ") << blocks_count_lo
175 << trs(", Block size: ") << block_size
176 << trs(", Blocks/group: ") << blocks_per_group
177 << trs(", Inodes/group: ") << inodes_per_group
178 << trs(", Inode size: ") << inode_size
179 << ", Compat: 0x" << std::hex << feature_compat
180 << ", Incompat: 0x" << feature_incompat
181 << ", RO compat: 0x" << feature_ro_compat;
182
184 offset,
185 volume_size,
186 "ext4 volume",
187 "ext4 probe",
188 detail.str(),
189 0.91};
190}
bool ext4_has_magic(DiskReader &reader, uint64_t offset)
uint16_t le16(const unsigned char *p)

References ext4_has_magic(), ext4_probe(), FilesystemVolume, le16(), le32(), DiskReader::read_at(), and trs().

Referenced by ext4_probe().

◆ fat32_probe()

ScanResult anonymous_namespace{filesystem_probes.cpp}::fat32_probe ( DiskReader & reader,
uint64_t offset )

Definition at line 61 of file filesystem_probes.cpp.

61 {
62 std::array<unsigned char, 512> sector{};
63 if (!read_sector(reader, offset, sector) || !is_fat32(sector)) {
64 return {};
65 }
66
67 const uint16_t bytes_per_sector = le16(sector.data() + 11);
68 const uint8_t sectors_per_cluster = sector[13];
69 const uint16_t reserved_sectors = le16(sector.data() + 14);
70 const uint8_t fats = sector[16];
71 const uint32_t total_sectors = le32(sector.data() + 32);
72 const uint32_t sectors_per_fat = le32(sector.data() + 36);
73 const uint32_t root_cluster = le32(sector.data() + 44);
74 const uint16_t fsinfo_sector = le16(sector.data() + 48);
75 const uint16_t backup_boot_sector = le16(sector.data() + 50);
76
77 if (bytes_per_sector == 0 || sectors_per_cluster == 0 || total_sectors == 0) {
78 return {};
79 }
80
81 const uint64_t volume_size = static_cast<uint64_t>(bytes_per_sector) * total_sectors;
82 std::ostringstream detail;
83 detail << trs("Bytes/sector") << ": " << bytes_per_sector
84 << ", " << trs("Sectors/cluster") << ": " << static_cast<unsigned>(sectors_per_cluster)
85 << trs(", FATs: ") << static_cast<unsigned>(fats)
86 << trs(", Reserved: ") << reserved_sectors
87 << trs(", Sectors/FAT: ") << sectors_per_fat
88 << trs(", Root cluster: ") << root_cluster
89 << trs(", FSInfo: ") << fsinfo_sector
90 << trs(", Backup boot: ") << backup_boot_sector;
91
93 offset,
94 volume_size,
95 "FAT32 volume",
96 "FAT32 probe",
97 detail.str(),
98 0.93};
99}
bool is_fat32(const std::array< unsigned char, 512 > &sector)

References fat32_probe(), FilesystemVolume, is_fat32(), le16(), le32(), read_sector(), and trs().

Referenced by fat32_probe().

◆ hfs_probe()

ScanResult anonymous_namespace{filesystem_probes.cpp}::hfs_probe ( DiskReader & reader,
uint64_t offset )

Definition at line 296 of file filesystem_probes.cpp.

296 {
297 std::array<unsigned char, 512> sector{};
298 if (!reader.read_at(offset + hfs::HFSPLUS_VOLUME_HEADER_OFFSET,
299 sector.data(), sector.size())) {
300 return {};
301 }
302 hfs::VolumeHeader vh;
303 if (!hfs::parse_volume_header(sector.data(), vh)) {
304 return {};
305 }
306
307 std::ostringstream detail;
308 detail << trs("Variant") << ": " << vh.variant_name()
309 << ", " << trs("Version") << ": " << vh.version
310 << ", " << trs("Block size") << ": " << vh.block_size
311 << ", " << trs("Total blocks") << ": " << vh.total_blocks
312 << ", " << trs("Free blocks") << ": " << vh.free_blocks
313 << ", " << trs("Files") << ": " << vh.file_count
314 << ", " << trs("Folders") << ": " << vh.folder_count;
315 if (vh.is_journaled()) {
316 detail << ", " << trs("Journaled");
317 }
318
319 // Confidence: high. The volume header is a strong magic + structured
320 // sanity (block_size power of 2, fork extents in range, etc.).
321 const double confidence = vh.is_hfsx() ? 0.96 : 0.95;
322
324 offset,
326 vh.is_hfsx() ? "HFSX volume" : "HFS+ volume",
327 "HFS+ probe",
328 detail.str(),
329 confidence};
330}
bool parse_volume_header(const unsigned char *buf, VolumeHeader &out)
constexpr uint32_t HFSPLUS_VOLUME_HEADER_OFFSET
bool is_hfsx() const
uint64_t volume_size_bytes() const
bool is_journaled() const
const char * variant_name() const

References hfs::VolumeHeader::block_size, hfs::VolumeHeader::file_count, FilesystemVolume, hfs::VolumeHeader::folder_count, hfs::VolumeHeader::free_blocks, hfs_probe(), hfs::HFSPLUS_VOLUME_HEADER_OFFSET, hfs::VolumeHeader::is_hfsx(), hfs::VolumeHeader::is_journaled(), hfs::parse_volume_header(), DiskReader::read_at(), hfs::VolumeHeader::total_blocks, trs(), hfs::VolumeHeader::variant_name(), hfs::VolumeHeader::version, and hfs::VolumeHeader::volume_size_bytes().

Referenced by hfs_probe().

◆ is_exfat()

bool anonymous_namespace{filesystem_probes.cpp}::is_exfat ( const std::array< unsigned char, 512 > & sector)

Definition at line 46 of file filesystem_probes.cpp.

46 {
47 return std::string(reinterpret_cast<const char*>(sector.data() + 3), 8) == "EXFAT " &&
48 sector[510] == 0x55 && sector[511] == 0xAA;
49}

References is_exfat().

Referenced by exfat_probe(), and is_exfat().

◆ is_fat32()

bool anonymous_namespace{filesystem_probes.cpp}::is_fat32 ( const std::array< unsigned char, 512 > & sector)

Definition at line 41 of file filesystem_probes.cpp.

41 {
42 return std::string(reinterpret_cast<const char*>(sector.data() + 82), 8) == "FAT32 " &&
43 sector[510] == 0x55 && sector[511] == 0xAA;
44}

References is_fat32().

Referenced by fat32_probe(), and is_fat32().

◆ le16()

uint16_t anonymous_namespace{filesystem_probes.cpp}::le16 ( const unsigned char * p)

Definition at line 15 of file filesystem_probes.cpp.

15 {
16 return static_cast<uint16_t>(p[0]) | (static_cast<uint16_t>(p[1]) << 8);
17}

References le16().

Referenced by ext4_probe(), fat32_probe(), and le16().

◆ le32()

uint32_t anonymous_namespace{filesystem_probes.cpp}::le32 ( const unsigned char * p)

Definition at line 19 of file filesystem_probes.cpp.

19 {
20 return static_cast<uint32_t>(p[0]) |
21 (static_cast<uint32_t>(p[1]) << 8) |
22 (static_cast<uint32_t>(p[2]) << 16) |
23 (static_cast<uint32_t>(p[3]) << 24);
24}

References le32().

Referenced by btrfs_probe(), exfat_probe(), ext4_probe(), fat32_probe(), and le32().

◆ le64()

uint64_t anonymous_namespace{filesystem_probes.cpp}::le64 ( const unsigned char * p)

Definition at line 26 of file filesystem_probes.cpp.

26 {
27 return static_cast<uint64_t>(p[0]) |
28 (static_cast<uint64_t>(p[1]) << 8) |
29 (static_cast<uint64_t>(p[2]) << 16) |
30 (static_cast<uint64_t>(p[3]) << 24) |
31 (static_cast<uint64_t>(p[4]) << 32) |
32 (static_cast<uint64_t>(p[5]) << 40) |
33 (static_cast<uint64_t>(p[6]) << 48) |
34 (static_cast<uint64_t>(p[7]) << 56);
35}

References le64().

Referenced by btrfs_probe(), exfat_probe(), and le64().

◆ read_sector()

bool anonymous_namespace{filesystem_probes.cpp}::read_sector ( DiskReader & reader,
uint64_t offset,
std::array< unsigned char, 512 > & sector )

Definition at line 37 of file filesystem_probes.cpp.

37 {
38 return reader.read_at(offset, sector.data(), sector.size());
39}

References DiskReader::read_at(), and read_sector().

Referenced by exfat_probe(), fat32_probe(), and read_sector().

◆ xfs_probe()

ScanResult anonymous_namespace{filesystem_probes.cpp}::xfs_probe ( DiskReader & reader,
uint64_t offset )

Definition at line 245 of file filesystem_probes.cpp.

245 {
246 // The XFS superblock is at byte 0 of the volume. We always read 512 bytes
247 // because that's what parse_superblock expects (one sector worth).
248 std::array<unsigned char, 512> sector{};
249 if (!reader.read_at(offset, sector.data(), sector.size())) {
250 return {};
251 }
252 xfs::Superblock sb;
253 if (!xfs::parse_superblock(sector.data(), sb)) {
254 return {};
255 }
256
257 std::ostringstream detail;
258 detail << trs("Format version") << ": " << sb.format_version()
259 << ", " << trs("Block size") << ": " << sb.blocksize
260 << ", " << trs("Sector size") << ": " << sb.sectsize
261 << ", " << trs("Inode size") << ": " << sb.inodesize
262 << ", " << trs("Inodes/block") << ": " << sb.inopblock
263 << ", " << trs("Allocation groups") << ": " << sb.agcount
264 << ", " << trs("Blocks/AG") << ": " << sb.agblocks
265 << ", " << trs("Total blocks") << ": " << sb.dblocks;
266 if (sb.is_v5()) {
267 detail << ", " << trs("CRC: V5");
268 if (sb.has_ftype()) detail << ", FTYPE";
270 detail << ", FINOBT";
271 }
273 detail << ", REFLINK";
274 }
275 }
276 const std::string lbl = sb.label();
277 if (!lbl.empty()) {
278 detail << ", " << trs("Label") << ": " << lbl;
279 }
280
281 // Confidence: V5 has CRC and stricter validation, so very high.
282 // V4 is also reliable when it parses, but slightly lower.
283 const double confidence = sb.is_v5() ? 0.96 : 0.92;
284
286 offset,
288 "XFS volume",
289 "XFS probe",
290 detail.str(),
291 confidence};
292}
bool parse_superblock(const unsigned char *buf, Superblock &out)
constexpr uint32_t XFS_SB_FEAT_RO_COMPAT_FINOBT
constexpr uint32_t XFS_SB_FEAT_RO_COMPAT_REFLINK
uint64_t volume_size_bytes() const
std::string label() const
unsigned format_version() const
uint32_t features_ro_compat
bool is_v5() const
bool has_ftype() const

References xfs::Superblock::agblocks, xfs::Superblock::agcount, xfs::Superblock::blocksize, xfs::Superblock::dblocks, xfs::Superblock::features_ro_compat, FilesystemVolume, xfs::Superblock::format_version(), xfs::Superblock::has_ftype(), xfs::Superblock::inodesize, xfs::Superblock::inopblock, xfs::Superblock::is_v5(), xfs::Superblock::label(), xfs::parse_superblock(), DiskReader::read_at(), xfs::Superblock::sectsize, trs(), xfs::Superblock::volume_size_bytes(), xfs_probe(), xfs::XFS_SB_FEAT_RO_COMPAT_FINOBT, and xfs::XFS_SB_FEAT_RO_COMPAT_REFLINK.

Referenced by xfs_probe().

Variable Documentation

◆ SECTOR_SIZE

uint64_t anonymous_namespace{filesystem_probes.cpp}::SECTOR_SIZE = 512ull
constexpr

Definition at line 13 of file filesystem_probes.cpp.