18 if (buf ==
nullptr)
return false;
23 out.
kind =
static_cast<int8_t
>(buf[0x08]);
39 if (buf ==
nullptr)
return false;
74 if (i >= numRecords)
return 0;
75 if (node_buf ==
nullptr)
return 0;
76 if (nodeSize < 14 + 2u * (
static_cast<uint32_t
>(numRecords) + 1)) {
83 const uint32_t off_idx =
static_cast<uint32_t
>(nodeSize)
84 - 2u * (
static_cast<uint32_t
>(i) + 1u);
85 return be16(node_buf + off_idx);
92 if (i >= numRecords)
return 0;
95 if (this_off == 0)
return 0;
99 const uint32_t next_idx =
static_cast<uint32_t
>(nodeSize)
100 - 2u * (
static_cast<uint32_t
>(i) + 2u);
101 if (next_idx + 2 > nodeSize)
return 0;
102 const uint16_t next_off =
be16(node_buf + next_idx);
103 if (next_off < this_off)
return 0;
104 return next_off - this_off;
114 if (key_buf ==
nullptr || max_bytes < 8)
return false;
116 const uint16_t keyLength =
be16(key_buf + 0);
119 if (keyLength < 6)
return false;
120 if (2u + keyLength > max_bytes)
return false;
124 if (8u +
static_cast<uint32_t
>(out.
nameLength) * 2u > 2u + keyLength) {
132 uint32_t record_len) {
133 if (record_buf ==
nullptr || record_len < 8)
return 0;
134 const uint16_t keyLength =
be16(record_buf + 0);
135 const uint32_t key_total = 2u + keyLength;
136 if (key_total > record_len)
return 0;
139 return (key_total + 1u) & ~1u;
166bool descend(uint32_t node_index, uint16_t expected_height,
170 std::set<uint32_t>& visited,
171 size_t& nodes_visited,
172 std::vector<std::string>& logs,
174 bool& callback_stopped) {
175 if (callback_stopped)
return true;
176 if (node_index == 0) {
182 error =
"HFS+ B-tree: traversal cap reached (possible loop)";
185 if (!visited.insert(node_index).second) {
186 std::ostringstream m;
187 m <<
"HFS+ B-tree: cycle detected at node " << node_index <<
", skip";
188 logs.push_back(m.str());
192 std::vector<unsigned char> node;
193 if (!read_node(node_index, node)) {
194 std::ostringstream m;
195 m <<
"HFS+ B-tree: read failed for node " << node_index;
196 logs.push_back(m.str());
199 if (node.size() < header.
nodeSize) {
200 std::ostringstream m;
201 m <<
"HFS+ B-tree: short read for node " << node_index;
202 logs.push_back(m.str());
208 std::ostringstream m;
209 m <<
"HFS+ B-tree: bad descriptor at node " << node_index;
210 logs.push_back(m.str());
215 (void)expected_height;
218 std::ostringstream m;
219 m <<
"HFS+ B-tree: unexpected kind=" <<
static_cast<int>(nd.
kind)
220 <<
" at node " << node_index;
221 logs.push_back(m.str());
225 for (uint16_t i = 0; i < nd.
numRecords; ++i) {
232 if (rec_off == 0 || rec_len == 0)
continue;
233 if (rec_off + rec_len > header.
nodeSize)
continue;
235 const unsigned char* recp = node.data() + rec_off;
238 if (!on_leaf(recp, rec_len)) {
239 callback_stopped =
true;
245 if (body_off == 0 || body_off + 4 > rec_len)
continue;
246 const uint32_t child =
be32(recp + body_off);
247 if (child == 0 || child >= header.
totalNodes) {
248 std::ostringstream m;
249 m <<
"HFS+ B-tree: index node " << node_index
250 <<
" has implausible child pointer " << child;
251 logs.push_back(m.str());
255 (expected_height > 0) ? expected_height - 1 : 0,
256 header, read_node, on_leaf,
257 visited, nodes_visited, logs, error,
261 if (callback_stopped)
return true;
272 std::vector<std::string>& logs,
273 std::string& error) {
275 error =
"HFS+ B-tree: empty/zero rootNode";
279 error =
"HFS+ B-tree: implausible tree depth";
282 std::set<uint32_t> visited;
283 size_t nodes_visited = 0;
284 bool callback_stopped =
false;
286 on_leaf, visited, nodes_visited, logs, error,
bool descend(uint32_t node_index, uint16_t expected_height, const HeaderRec &header, const NodeReader &read_node, const LeafRecordCallback &on_leaf, std::set< uint32_t > &visited, size_t &nodes_visited, std::vector< std::string > &logs, std::string &error, bool &callback_stopped)
constexpr size_t MAX_NODES_VISITED
std::function< bool(const unsigned char *record_buf, uint16_t record_len)> LeafRecordCallback
bool parse_node_descriptor(const unsigned char *buf, NodeDescriptor &out)
std::function< bool(uint32_t node_index, std::vector< unsigned char > &out)> NodeReader
constexpr int8_t kBTIndexNode
uint32_t be32(const unsigned char *p)
uint16_t node_record_length(const unsigned char *node_buf, uint16_t nodeSize, uint16_t numRecords, uint16_t i)
uint16_t node_record_offset(const unsigned char *node_buf, uint16_t nodeSize, uint16_t numRecords, uint16_t i)
bool walk_btree(const HeaderRec &header, const NodeReader &read_node, const LeafRecordCallback &on_leaf, std::vector< std::string > &logs, std::string &error)
bool parse_catalog_key(const unsigned char *key_buf, uint32_t max_bytes, CatalogKey &out)
constexpr int8_t kBTHeaderNode
uint32_t catalog_record_body_offset(const unsigned char *record_buf, uint32_t record_len)
bool parse_header_rec(const unsigned char *buf, HeaderRec &out)
constexpr int8_t kBTMapNode
constexpr int8_t kBTLeafNode
uint16_t be16(const unsigned char *p)
const unsigned char * name_utf16be