174 {
175 if (callback_stopped) return true;
176 if (node_index == 0) {
177
178
179 return true;
180 }
182 error = "HFS+ B-tree: traversal cap reached (possible loop)";
183 return false;
184 }
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());
189 return true;
190 }
191
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());
197 return true;
198 }
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());
203 return true;
204 }
205
206 NodeDescriptor nd;
208 std::ostringstream m;
209 m << "HFS+ B-tree: bad descriptor at node " << node_index;
210 logs.push_back(m.str());
211 return true;
212 }
213
214
215 (void)expected_height;
216
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());
222 return true;
223 }
224
225 for (uint16_t i = 0; i < nd.numRecords; ++i) {
228 nd.numRecords, i);
231 nd.numRecords, i);
232 if (rec_off == 0 || rec_len == 0) continue;
233 if (rec_off + rec_len > header.
nodeSize)
continue;
234
235 const unsigned char* recp = node.data() + rec_off;
236
238 if (!on_leaf(recp, rec_len)) {
239 callback_stopped = true;
240 return true;
241 }
242 } else {
243
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());
252 continue;
253 }
255 (expected_height > 0) ? expected_height - 1 : 0,
256 header, read_node, on_leaf,
257 visited, nodes_visited, logs, error,
258 callback_stopped)) {
259 return false;
260 }
261 if (callback_stopped) return true;
262 }
263 }
264 return true;
265}
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
bool parse_node_descriptor(const unsigned char *buf, NodeDescriptor &out)
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)
uint32_t catalog_record_body_offset(const unsigned char *record_buf, uint32_t record_len)
constexpr int8_t kBTLeafNode