13 uint64_t volume_offset,
17 std::vector<unsigned char>& out,
18 std::vector<std::string>& logs) {
19 if (block_size == 0)
return false;
23 uint64_t inline_bytes = 0;
24 for (
const auto& e : fork.
extents) {
25 inline_bytes +=
static_cast<uint64_t
>(e.block_count) * block_size;
28 uint64_t want = std::min<uint64_t>(fork.
logical_size, max_bytes);
29 if (want > inline_bytes) {
31 m <<
"HFS+ fork: requested " << want
32 <<
" bytes but only " << inline_bytes
33 <<
" bytes covered by inline extents (file > 8 extents would "
34 "need Extents Overflow file -- session 4)";
35 logs.push_back(m.str());
40 out.reserve(
static_cast<size_t>(want));
42 uint64_t produced = 0;
43 for (
const auto& e : fork.
extents) {
44 if (produced >= want)
break;
45 if (e.block_count == 0)
continue;
47 const uint64_t ext_disk_off =
49 +
static_cast<uint64_t
>(e.start_block) * block_size;
50 const uint64_t ext_bytes =
51 static_cast<uint64_t
>(e.block_count) * block_size;
52 const uint64_t copy = std::min<uint64_t>(ext_bytes, want - produced);
54 const size_t before = out.size();
55 out.resize(before +
static_cast<size_t>(copy));
56 if (!reader.
read_at(ext_disk_off, out.data() + before, copy)) {
58 m <<
"HFS+ fork: read failed at disk offset " << ext_disk_off
59 <<
" for " << copy <<
" bytes";
60 logs.push_back(m.str());
70 uint64_t volume_offset,
73 uint64_t fork_logical_offset,
74 unsigned char* block_buf) {
75 if (block_size == 0 || block_buf ==
nullptr)
return false;
82 for (
const auto& e : fork.
extents) {
83 if (e.block_count == 0)
continue;
84 const uint64_t ext_bytes =
85 static_cast<uint64_t
>(e.block_count) * block_size;
86 if (fork_logical_offset < covered + ext_bytes) {
88 const uint64_t byte_within_ext = fork_logical_offset - covered;
91 const uint64_t blk_within_ext = byte_within_ext / block_size;
92 const uint64_t disk_off =
94 + (
static_cast<uint64_t
>(e.start_block) + blk_within_ext)
96 return reader.
read_at(disk_off, block_buf, block_size);
201 uint64_t volume_offset,
208 std::vector<unsigned char>& extents_btree_buffer,
209 std::vector<unsigned char>& out,
210 std::vector<std::string>& logs) {
211 if (block_size == 0)
return false;
214 uint64_t inline_blocks = 0;
215 int inline_extent_count = 0;
216 for (
const auto& e : fork.
extents) {
217 if (e.block_count == 0)
break;
218 inline_blocks += e.block_count;
219 ++inline_extent_count;
221 const uint64_t inline_bytes = inline_blocks * block_size;
223 const uint64_t want = std::min<uint64_t>(fork.
logical_size, max_bytes);
226 if (fork.
total_blocks <= inline_blocks || inline_extent_count < 8) {
228 fork, want, out, logs);
233 if (extents_btree_buffer.empty()) {
234 std::vector<std::string> ld_logs;
237 extents_btree_buffer, ld_logs)) {
238 logs.push_back(
"HFS+: cannot load Extents Overflow file content "
239 "(file with >8 extents will be soft-truncated)");
240 for (
auto& l : ld_logs) logs.push_back(std::move(l));
241 extents_btree_buffer.clear();
243 fork, want, out, logs);
246 if (extents_btree_buffer.size() < 14) {
247 logs.push_back(
"HFS+: Extents Overflow file too small for B-tree");
249 fork, want, out, logs);
256 logs.push_back(
"HFS+: Extents Overflow node 0 not a header node");
258 fork, want, out, logs);
262 logs.push_back(
"HFS+: Extents Overflow B-tree header parse failed");
264 fork, want, out, logs);
268 std::vector<ForkExtent> overflow_extents;
269 collect_overflow_extents(extents_btree_buffer, ohdr,
270 forkType, fileID, overflow_extents, logs);
273 std::vector<ForkExtent> all_extents;
274 all_extents.reserve(8 + overflow_extents.size());
275 for (
const auto& e : fork.
extents) {
276 if (e.block_count == 0)
break;
277 all_extents.push_back(e);
279 for (
const auto& e : overflow_extents) all_extents.push_back(e);
283 out.reserve(
static_cast<size_t>(want));
284 uint64_t produced = 0;
285 for (
const auto& e : all_extents) {
286 if (produced >= want)
break;
287 const uint64_t ext_bytes =
288 static_cast<uint64_t
>(e.block_count) * block_size;
289 const uint64_t copy = std::min<uint64_t>(ext_bytes, want - produced);
290 if (!read_one_extent(reader, volume_offset, block_size, e, copy, out)) {
291 std::ostringstream m;
292 m <<
"HFS+: read failure on overflow extent (start_block="
293 << e.start_block <<
", count=" << e.block_count <<
")";
294 logs.push_back(m.str());
301 if (produced < want) {
302 std::ostringstream m;
303 m <<
"HFS+: requested " << want <<
" bytes from fork (fileID="
304 << fileID <<
"), got " << produced
305 <<
" (file may be truncated or overflow records missing)";
306 logs.push_back(m.str());
bool read_fork_full(DiskReader &reader, uint64_t volume_offset, uint32_t block_size, const ForkData &fork, const ForkData &extents_file, uint8_t forkType, uint32_t fileID, uint64_t max_bytes, std::vector< unsigned char > &extents_btree_buffer, std::vector< unsigned char > &out, std::vector< std::string > &logs)
bool read_fork_inline(DiskReader &reader, uint64_t volume_offset, uint32_t block_size, const ForkData &fork, uint64_t max_bytes, std::vector< unsigned char > &out, std::vector< std::string > &logs)