30 const std::string& attr_name,
31 std::vector<unsigned char>& out,
32 std::vector<std::string>& logs) {
34 if (attributes_btree_buffer.size() < 14)
return false;
40 logs.push_back(
"HFS+ Attributes: node 0 is not a header node");
45 logs.push_back(
"HFS+ Attributes: B-tree header parse failed");
52 const auto target_name_utf16be = utf8_to_utf16be_ascii(attr_name);
55 auto node_reader = [&](uint32_t node_index,
56 std::vector<unsigned char>& nodebuf) ->
bool {
57 const uint64_t off =
static_cast<uint64_t
>(node_index) * hdr.
nodeSize;
58 if (off + hdr.
nodeSize > attributes_btree_buffer.size())
return false;
59 nodebuf.assign(attributes_btree_buffer.begin() + off,
60 attributes_btree_buffer.begin() + off + hdr.
nodeSize);
65 auto on_leaf = [&](
const unsigned char* rec, uint16_t rec_len) ->
bool {
72 if (rec_len < 14)
return true;
73 const uint16_t keyLength =
be16(rec + 0);
74 if (keyLength < 12 || 2u + keyLength > rec_len)
return true;
76 const uint32_t rec_fileID =
be32(rec + 4);
77 if (rec_fileID != fileID)
return true;
79 const uint32_t rec_startBlock =
be32(rec + 8);
80 if (rec_startBlock != 0)
return true;
81 const uint16_t rec_nameLen =
be16(rec + 12);
82 if (rec_nameLen != target_name_utf16be.size() / 2)
return true;
84 const size_t name_offset = 14;
85 const size_t name_bytes =
static_cast<size_t>(rec_nameLen) * 2;
86 if (name_offset + name_bytes > 2u + keyLength)
return true;
87 if (std::memcmp(rec + name_offset, target_name_utf16be.data(),
88 name_bytes) != 0)
return true;
92 const uint32_t key_total = 2u + keyLength;
93 const uint32_t body_off = (key_total + 1u) & ~1u;
94 if (body_off + 4 > rec_len)
return true;
96 const uint32_t recordType =
be32(rec + body_off);
100 m <<
"HFS+ Attributes: attribute '" << attr_name
101 <<
"' on fileID " << fileID
102 <<
" uses non-inline storage (recordType=0x"
103 << std::hex << recordType <<
") -- not supported";
104 logs.push_back(m.str());
113 if (body_off + 16 > rec_len)
return true;
114 const uint32_t attrSize =
be32(rec + body_off + 12);
115 if (attrSize == 0)
return false;
116 if (body_off + 16 + attrSize > rec_len) {
117 logs.push_back(
"HFS+ Attributes: inline data overruns record");
120 out.assign(rec + body_off + 16, rec + body_off + 16 + attrSize);
125 std::vector<std::string> walk_logs;
126 std::string walk_err;
127 walk_btree(hdr, node_reader, on_leaf, walk_logs, walk_err);
128 for (
auto& l : walk_logs) logs.push_back(std::move(l));
129 if (!walk_err.empty()) {
130 logs.push_back(
"HFS+ Attributes walk: " + walk_err);