15uint16_t
le16(
const unsigned char* p) {
16 return static_cast<uint16_t
>(p[0]) | (
static_cast<uint16_t
>(p[1]) << 8);
19uint32_t
le32(
const unsigned char* p) {
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);
26uint64_t
le64(
const unsigned char* p) {
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);
38 return reader.
read_at(offset, sector.data(), sector.size());
41bool is_fat32(
const std::array<unsigned char, 512>& sector) {
42 return std::string(
reinterpret_cast<const char*
>(sector.data() + 82), 8) ==
"FAT32 " &&
43 sector[510] == 0x55 && sector[511] == 0xAA;
46bool is_exfat(
const std::array<unsigned char, 512>& sector) {
47 return std::string(
reinterpret_cast<const char*
>(sector.data() + 3), 8) ==
"EXFAT " &&
48 sector[510] == 0x55 && sector[511] == 0xAA;
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;
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";
62 std::array<unsigned char, 512> sector{};
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);
77 if (bytes_per_sector == 0 || sectors_per_cluster == 0 || total_sectors == 0) {
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;
102 std::array<unsigned char, 512> sector{};
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];
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;
121 if (volume_length == 0 || bytes_per_sector < 512 || sectors_per_cluster == 0) {
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;
149 std::array<unsigned char, 1024> super{};
150 if (!reader.
read_at(offset + 1024, super.data(), super.size())) {
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);
165 if (magic != 0xEF53) {
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;
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;
197 std::array<unsigned char, 0x2000> sb{};
198 if (!reader.
read_at(offset + 0x10000, sb.data(), sb.size())) {
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);
215 if (sectorsize == 0 || nodesize == 0 || total_bytes == 0) {
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;
248 std::array<unsigned char, 512> sector{};
249 if (!reader.
read_at(offset, sector.data(), sector.size())) {
257 std::ostringstream detail;
260 <<
", " <<
trs(
"Sector size") <<
": " << sb.
sectsize
263 <<
", " <<
trs(
"Allocation groups") <<
": " << sb.
agcount
265 <<
", " <<
trs(
"Total blocks") <<
": " << sb.
dblocks;
267 detail <<
", " <<
trs(
"CRC: V5");
270 detail <<
", FINOBT";
273 detail <<
", REFLINK";
276 const std::string lbl = sb.
label();
278 detail <<
", " <<
trs(
"Label") <<
": " << lbl;
283 const double confidence = sb.
is_v5() ? 0.96 : 0.92;
297 std::array<unsigned char, 512> sector{};
299 sector.data(), sector.size())) {
307 std::ostringstream detail;
309 <<
", " <<
trs(
"Version") <<
": " << vh.
version
316 detail <<
", " <<
trs(
"Journaled");
321 const double confidence = vh.
is_hfsx() ? 0.96 : 0.95;
326 vh.
is_hfsx() ?
"HFSX volume" :
"HFS+ volume",
334 const std::vector<ScanResult>& seed_results,
335 std::vector<std::string>& logs) {
336 std::vector<ScanResult> out;
337 std::set<uint64_t> seen_offsets;
338 logs.push_back(
trs(
"Filesystem probes: analysis started."));
340 for (
const auto& seed : seed_results) {
341 const bool interesting_seed =
346 if (!interesting_seed || seen_offsets.count(seed.offset_bytes) != 0) {
349 seen_offsets.insert(seed.offset_bytes);
351 if (
auto r = fat32_probe(reader, seed.offset_bytes); !r.type.empty()) out.push_back(r);
352 if (
auto r = exfat_probe(reader, seed.offset_bytes); !r.type.empty()) out.push_back(r);
353 if (
auto r = ext4_probe(reader, seed.offset_bytes); !r.type.empty()) out.push_back(r);
354 if (
auto r = btrfs_probe(reader, seed.offset_bytes); !r.type.empty()) out.push_back(r);
355 if (
auto r = xfs_probe(reader, seed.offset_bytes); !r.type.empty()) out.push_back(r);
356 if (
auto r = hfs_probe(reader, seed.offset_bytes); !r.type.empty()) out.push_back(r);
359 logs.push_back(
trs(
"Filesystem probes: analysis completed."));
bool read_at(uint64_t offset, void *buffer, size_t len)
static std::vector< ScanResult > analyze(DiskReader &reader, const std::vector< ScanResult > &seed_results, std::vector< std::string > &logs)
std::string trs(const char *msgid)
bool is_exfat(const std::array< unsigned char, 512 > §or)
bool is_fat32(const std::array< unsigned char, 512 > §or)
ScanResult fat32_probe(DiskReader &reader, uint64_t offset)
constexpr uint64_t SECTOR_SIZE
uint64_t le64(const unsigned char *p)
bool read_sector(DiskReader &reader, uint64_t offset, std::array< unsigned char, 512 > §or)
ScanResult exfat_probe(DiskReader &reader, uint64_t offset)
bool ext4_has_magic(DiskReader &reader, uint64_t offset)
bool btrfs_has_magic(DiskReader &reader, uint64_t offset)
uint16_t le16(const unsigned char *p)
ScanResult hfs_probe(DiskReader &reader, uint64_t offset)
uint32_t le32(const unsigned char *p)
ScanResult xfs_probe(DiskReader &reader, uint64_t offset)
ScanResult ext4_probe(DiskReader &reader, uint64_t offset)
ScanResult btrfs_probe(DiskReader &reader, uint64_t offset)
bool parse_volume_header(const unsigned char *buf, VolumeHeader &out)
constexpr uint32_t HFSPLUS_VOLUME_HEADER_OFFSET
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