Skip to content
API Documentation
BastionGuard™
hfs Namespace Reference

Namespaces

namespace  anonymous_namespace{hfs_attributes.cpp}
namespace  anonymous_namespace{hfs_btree.cpp}
namespace  anonymous_namespace{hfs_catalog.cpp}
namespace  anonymous_namespace{hfs_decmpfs.cpp}
namespace  anonymous_namespace{hfs_fork_io.cpp}
namespace  anonymous_namespace{hfs_unicode.cpp}

Classes

struct  BSDInfo
struct  CatalogFile
struct  CatalogFolder
struct  CatalogKey
struct  ChildEntry
struct  DecmpfsHeader
struct  ExtentsOverflowKey
struct  FinderInfo16
struct  ForkData
struct  ForkExtent
struct  HeaderRec
struct  NodeDescriptor
struct  VolumeContext
struct  VolumeHeader

Typedefs

using NodeReader
using LeafRecordCallback

Functions

bool find_inline_attribute (const std::vector< unsigned char > &attributes_btree_buffer, uint32_t fileID, const std::string &attr_name, std::vector< unsigned char > &out, std::vector< std::string > &logs)
bool parse_node_descriptor (const unsigned char *buf, NodeDescriptor &out)
bool parse_header_rec (const unsigned char *buf, HeaderRec &out)
uint16_t node_record_offset (const unsigned char *node_buf, uint16_t nodeSize, uint16_t numRecords, uint16_t i)
uint16_t node_record_length (const unsigned char *node_buf, uint16_t nodeSize, uint16_t numRecords, uint16_t i)
bool parse_catalog_key (const unsigned char *key_buf, uint32_t max_bytes, CatalogKey &out)
uint32_t catalog_record_body_offset (const unsigned char *record_buf, uint32_t record_len)
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_folder (const unsigned char *body, uint32_t body_len, CatalogFolder &out)
bool parse_catalog_file (const unsigned char *body, uint32_t body_len, CatalogFile &out)
std::string catalog_name_to_host (const unsigned char *utf16be_name, uint16_t name_units)
bool is_private_data_folder_name (const unsigned char *utf16be_name, uint16_t name_units)
uint32_t parse_inode_filename (const unsigned char *utf16be_name, uint16_t name_units)
bool parse_decmpfs_header (const unsigned char *buf, DecmpfsHeader &out)
const char * decmpfs_type_name (uint32_t type)
bool decmpfs_supported (uint32_t type)
bool decompress_xattr_zlib (const unsigned char *xattr_buf, size_t xattr_len, uint64_t expected_uncompressed_size, std::vector< unsigned char > &out, std::string &error)
bool decompress_resource_fork_zlib (const unsigned char *rsrc_buf, size_t rsrc_len, uint64_t expected_uncompressed_size, std::vector< unsigned char > &out, std::string &error)
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)
bool read_fork_block_inline (DiskReader &reader, uint64_t volume_offset, uint32_t block_size, const ForkData &fork, uint64_t fork_logical_offset, unsigned char *block_buf)
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 parse_fork_data (const unsigned char *buf, ForkData &out)
bool parse_volume_header (const unsigned char *buf, VolumeHeader &out)
uint16_t be16 (const unsigned char *p)
uint32_t be32 (const unsigned char *p)
uint64_t be64 (const unsigned char *p)
uint64_t alloc_block_to_disk_offset (uint64_t volume_offset, uint32_t alloc_block, uint32_t block_size)
std::string utf16be_to_utf8 (const unsigned char *src, size_t code_units)
std::string nfd_to_nfc (const std::string &utf8)
std::string hfs_filename_to_host (const unsigned char *src, size_t code_units)

Variables

constexpr uint32_t kHFSPlusAttrInlineData = 0x10
constexpr uint32_t kHFSPlusAttrForkData = 0x20
constexpr uint32_t kHFSPlusAttrExtents = 0x30
constexpr int8_t kBTLeafNode = -1
constexpr int8_t kBTIndexNode = 0
constexpr int8_t kBTHeaderNode = 1
constexpr int8_t kBTMapNode = 2
constexpr uint8_t kHFSCaseFolding = 0xCF
constexpr uint8_t kHFSBinaryCompare = 0xBC
constexpr uint8_t kHFSBTreeType = 0xFF
constexpr uint8_t kUserBTreeType = 0x80
constexpr uint8_t kReservedBTreeType = 0x00
constexpr int16_t kHFSPlusFolderRecord = 0x0001
constexpr int16_t kHFSPlusFileRecord = 0x0002
constexpr int16_t kHFSPlusFolderThreadRecord = 0x0003
constexpr int16_t kHFSPlusFileThreadRecord = 0x0004
constexpr uint32_t kSymLinkFileType = 0x736C6E6B
constexpr uint32_t kSymLinkCreator = 0x72686170
constexpr uint32_t kHardLinkFileType = 0x686C6E6B
constexpr uint32_t kHFSPlusCreator = 0x6866732B
constexpr uint16_t kHFSFileLockedBit = 0x0001
constexpr uint16_t kHFSThreadExistsBit = 0x0002
constexpr uint16_t kHFSHasAttributesBit = 0x0004
constexpr uint16_t kHFSHasSecurityBit = 0x0008
constexpr uint16_t kHFSHasFolderCountBit = 0x0010
constexpr uint16_t kHFSHasLinkChainBit = 0x0020
constexpr uint16_t kHFSHasChildLinkBit = 0x0040
constexpr uint16_t kHFSHasDateAddedBit = 0x0080
constexpr uint8_t UF_COMPRESSED = 0x20
constexpr int kHFSPrivateDataNameLength = 21
constexpr uint32_t DECMPFS_MAGIC = 0x66706D63
constexpr size_t DECMPFS_HEADER_SIZE = 16
constexpr size_t kDecmpfsBlockSize = 64 * 1024
constexpr uint32_t kCompressionType_None = 1
constexpr uint32_t kCompressionType_ZlibInXattr = 3
constexpr uint32_t kCompressionType_ZlibInRsrc = 4
constexpr uint32_t kCompressionType_LzvnInXattr = 7
constexpr uint32_t kCompressionType_LzvnInRsrc = 8
constexpr uint32_t kCompressionType_LzfseInXattr = 11
constexpr uint32_t kCompressionType_LzfseInRsrc = 12
constexpr uint8_t kForkTypeData = 0x00
constexpr uint8_t kForkTypeResource = 0xFF
constexpr uint16_t HFSPLUS_SIGNATURE = 0x482B
constexpr uint16_t HFSX_SIGNATURE = 0x4858
constexpr uint16_t HFSPLUS_VERSION = 4
constexpr uint16_t HFSX_VERSION = 5
constexpr uint32_t HFSPLUS_VOLUME_HEADER_OFFSET = 1024
constexpr uint32_t kHFSVolumeUnmountedBit = (1u << 8)
constexpr uint32_t kHFSVolumeJournaledBit = (1u << 13)
constexpr uint32_t kHFSVolumeSoftwareLockBit = (1u << 15)
constexpr uint32_t kHFSRootParentID = 1
constexpr uint32_t kHFSRootFolderID = 2
constexpr uint32_t kHFSExtentsFileID = 3
constexpr uint32_t kHFSCatalogFileID = 4
constexpr uint32_t kHFSBadBlockFileID = 5
constexpr uint32_t kHFSAllocationFileID = 6
constexpr uint32_t kHFSStartupFileID = 7
constexpr uint32_t kHFSAttributesFileID = 8
constexpr uint32_t kHFSRepairCatalogFileID = 14
constexpr uint32_t kHFSBogusExtentFileID = 15
constexpr uint32_t kHFSFirstUserCatalogNodeID = 16

Typedef Documentation

◆ LeafRecordCallback

Initial value:
std::function<bool(const unsigned char* record_buf, uint16_t record_len)>

Definition at line 174 of file hfs_btree.hpp.

◆ NodeReader

Initial value:
std::function<bool(uint32_t node_index, std::vector<unsigned char>& out)>

Definition at line 171 of file hfs_btree.hpp.

Function Documentation

◆ alloc_block_to_disk_offset()

uint64_t hfs::alloc_block_to_disk_offset ( uint64_t volume_offset,
uint32_t alloc_block,
uint32_t block_size )
inline

Definition at line 185 of file hfs_format.hpp.

187 {
188 return volume_offset + static_cast<uint64_t>(alloc_block) * block_size;
189}

References alloc_block_to_disk_offset().

Referenced by alloc_block_to_disk_offset().

◆ be16()

◆ be32()

uint32_t hfs::be32 ( const unsigned char * p)
inline

◆ be64()

uint64_t hfs::be64 ( const unsigned char * p)
inline

Definition at line 58 of file hfs_format.hpp.

58 {
59 return (static_cast<uint64_t>(p[0]) << 56) |
60 (static_cast<uint64_t>(p[1]) << 48) |
61 (static_cast<uint64_t>(p[2]) << 40) |
62 (static_cast<uint64_t>(p[3]) << 32) |
63 (static_cast<uint64_t>(p[4]) << 24) |
64 (static_cast<uint64_t>(p[5]) << 16) |
65 (static_cast<uint64_t>(p[6]) << 8) |
66 static_cast<uint64_t>(p[7]);
67}

References be64().

Referenced by be64(), parse_fork_data(), and parse_volume_header().

◆ catalog_name_to_host()

std::string hfs::catalog_name_to_host ( const unsigned char * utf16be_name,
uint16_t name_units )

Definition at line 114 of file hfs_catalog.cpp.

115 {
116 // Use the unicode helper from session 1: utf16be -> utf8 + NFD->NFC
117 std::string nfc = hfs::hfs_filename_to_host(utf16be_name, name_units);
118 // hfs_filename_to_host already strips '/', NUL and Windows reserved chars.
119 // Defensive: if the result is empty (weird file), name it "_".
120 if (nfc.empty()) return "_";
121 // HFS+ allows colons in names (they're path separators on classic Mac);
122 // they map to '/' on the POSIX side which we already block. So '/' in
123 // the user-facing name on Mac actually corresponds to ':' on disk.
124 // Convert any embedded ':' -> '_' as additional safety.
125 std::replace(nfc.begin(), nfc.end(), ':', '_');
126 return nfc;
127}
std::string hfs_filename_to_host(const unsigned char *src, size_t code_units)

References catalog_name_to_host(), and hfs_filename_to_host().

Referenced by catalog_name_to_host(), and HfsRecovery::recover().

◆ catalog_record_body_offset()

uint32_t hfs::catalog_record_body_offset ( const unsigned char * record_buf,
uint32_t record_len )

Definition at line 131 of file hfs_btree.cpp.

132 {
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;
137 // After the key, records are aligned on a 16-bit boundary in the
138 // catalog file. Round up the key end to the next even byte.
139 return (key_total + 1u) & ~1u;
140}
uint16_t be16(const unsigned char *p)

References be16(), and catalog_record_body_offset().

Referenced by catalog_record_body_offset(), hfs::anonymous_namespace{hfs_btree.cpp}::descend(), and HfsRecovery::recover().

◆ decmpfs_supported()

bool hfs::decmpfs_supported ( uint32_t type)

Definition at line 107 of file hfs_decmpfs.cpp.

107 {
108 return type == kCompressionType_ZlibInRsrc ||
110}
constexpr uint32_t kCompressionType_ZlibInXattr
constexpr uint32_t kCompressionType_ZlibInRsrc

References decmpfs_supported(), kCompressionType_ZlibInRsrc, and kCompressionType_ZlibInXattr.

Referenced by decmpfs_supported().

◆ decmpfs_type_name()

const char * hfs::decmpfs_type_name ( uint32_t type)

Definition at line 94 of file hfs_decmpfs.cpp.

94 {
95 switch (type) {
96 case kCompressionType_None: return "raw";
97 case kCompressionType_ZlibInXattr: return "zlib-xattr";
98 case kCompressionType_ZlibInRsrc: return "zlib-rsrc";
99 case kCompressionType_LzvnInXattr: return "lzvn-xattr";
100 case kCompressionType_LzvnInRsrc: return "lzvn-rsrc";
101 case kCompressionType_LzfseInXattr: return "lzfse-xattr";
102 case kCompressionType_LzfseInRsrc: return "lzfse-rsrc";
103 default: return "unknown";
104 }
105}
constexpr uint32_t kCompressionType_LzfseInXattr
constexpr uint32_t kCompressionType_LzvnInRsrc
constexpr uint32_t kCompressionType_LzvnInXattr
constexpr uint32_t kCompressionType_LzfseInRsrc
constexpr uint32_t kCompressionType_None

References decmpfs_type_name(), kCompressionType_LzfseInRsrc, kCompressionType_LzfseInXattr, kCompressionType_LzvnInRsrc, kCompressionType_LzvnInXattr, kCompressionType_None, kCompressionType_ZlibInRsrc, and kCompressionType_ZlibInXattr.

Referenced by decmpfs_type_name().

◆ decompress_resource_fork_zlib()

bool hfs::decompress_resource_fork_zlib ( const unsigned char * rsrc_buf,
size_t rsrc_len,
uint64_t expected_uncompressed_size,
std::vector< unsigned char > & out,
std::string & error )

Definition at line 198 of file hfs_decmpfs.cpp.

202 {
203 out.clear();
204 if (rsrc_len < 16) {
205 error = "resource fork too small for header";
206 return false;
207 }
208 // Resource fork header (big-endian):
209 // __be32 dataOffset
210 // __be32 mapOffset
211 // __be32 dataLength
212 // __be32 mapLength
213 const uint32_t dataOffset = be32(rsrc_buf + 0);
214 // const uint32_t mapOffset = be32(rsrc_buf + 4);
215 const uint32_t dataLength = be32(rsrc_buf + 8);
216 // const uint32_t mapLength = be32(rsrc_buf + 12);
217
218 if (static_cast<uint64_t>(dataOffset) + dataLength > rsrc_len) {
219 error = "resource fork data section out of range";
220 return false;
221 }
222
223 const unsigned char* data = rsrc_buf + dataOffset;
224 if (dataLength < 4) {
225 error = "resource fork data section too small";
226 return false;
227 }
228
229 // First 4 bytes of the data section: __be32 stream length
230 // (= dataLength - 4)
231 const uint32_t streamLen = be32(data + 0);
232 if (streamLen + 4u > dataLength) {
233 error = "resource fork stream length inconsistent";
234 return false;
235 }
236 const unsigned char* stream = data + 4;
237
238 // Chunked stream layout (little-endian!):
239 // __le32 numChunks
240 // struct { __le32 offset; __le32 size; } chunks[numChunks]
241 // chunk bytes
242 if (streamLen < 4) {
243 error = "stream too small for chunk count";
244 return false;
245 }
246 const uint32_t numChunks = le32(stream + 0);
247 if (numChunks == 0 || numChunks > 1'000'000) {
248 std::ostringstream m;
249 m << "implausible chunk count: " << numChunks;
250 error = m.str();
251 return false;
252 }
253 // Chunk index table starts at offset 4 within stream
254 const size_t chunk_table_size = static_cast<size_t>(numChunks) * 8;
255 if (4u + chunk_table_size > streamLen) {
256 error = "chunk table overruns stream";
257 return false;
258 }
259 const unsigned char* chunk_table = stream + 4;
260
261 // Pre-allocate output
262 out.reserve(static_cast<size_t>(expected_uncompressed_size));
263
264 // Decompress each chunk
265 for (uint32_t i = 0; i < numChunks; ++i) {
266 const uint32_t c_off = le32(chunk_table + i * 8 + 0);
267 const uint32_t c_size = le32(chunk_table + i * 8 + 4);
268 if (c_off + c_size > streamLen) {
269 std::ostringstream m;
270 m << "chunk " << i << " out of stream bounds";
271 error = m.str();
272 return false;
273 }
274 const unsigned char* c_data = stream + c_off;
275
276 // Each chunk decompresses to up to kDecmpfsBlockSize bytes
277 std::vector<unsigned char> chunk_out(kDecmpfsBlockSize);
278 size_t produced = 0;
279 std::string ce;
280 if (!inflate_chunk(c_data, c_size, chunk_out.data(),
281 chunk_out.size(), produced, ce)) {
282 std::ostringstream m;
283 m << "chunk " << i << ": " << ce;
284 error = m.str();
285 return false;
286 }
287 out.insert(out.end(), chunk_out.begin(), chunk_out.begin() + produced);
288 if (out.size() > expected_uncompressed_size) {
289 // Some files report a smaller logical size than the chunks
290 // produce -- truncate.
291 out.resize(static_cast<size_t>(expected_uncompressed_size));
292 break;
293 }
294 }
295
296 if (out.size() != expected_uncompressed_size) {
297 std::ostringstream m;
298 m << "decompressed size " << out.size()
299 << " differs from expected " << expected_uncompressed_size;
300 // This is a soft warning, not a fatal error: some volumes have
301 // size accounting that doesn't include trailing zeros, etc.
302 // We DO accept the buffer as long as it's non-empty, but log.
303 error = m.str();
304 // Still return true if we have nonzero bytes (caller checks
305 // error and decides). Return false only on truly empty output.
306 if (out.empty()) return false;
307 }
308 return true;
309}
uint32_t le32(const unsigned char *p)
bool inflate_chunk(const unsigned char *in_buf, size_t in_size, unsigned char *out_buf, size_t out_size, size_t &out_produced, std::string &error)
constexpr size_t kDecmpfsBlockSize
uint32_t be32(const unsigned char *p)

References be32(), decompress_resource_fork_zlib(), and kDecmpfsBlockSize.

Referenced by decompress_resource_fork_zlib(), and HfsRecovery::recover().

◆ decompress_xattr_zlib()

bool hfs::decompress_xattr_zlib ( const unsigned char * xattr_buf,
size_t xattr_len,
uint64_t expected_uncompressed_size,
std::vector< unsigned char > & out,
std::string & error )

Definition at line 131 of file hfs_decmpfs.cpp.

135 {
136 out.clear();
137 if (xattr_len < DECMPFS_HEADER_SIZE) {
138 error = "xattr too small for decmpfs header";
139 return false;
140 }
141 DecmpfsHeader h;
142 if (!parse_decmpfs_header(xattr_buf, h)) {
143 error = "decmpfs header magic mismatch";
144 return false;
145 }
146 if (h.type != kCompressionType_ZlibInXattr) {
147 error = "compression type is not zlib-xattr";
148 return false;
149 }
150 if (h.size != expected_uncompressed_size) {
151 // The catalog logical_size and the decmpfs header should agree.
152 // We tolerate a mismatch (use the header value).
153 }
154 const unsigned char* payload = xattr_buf + DECMPFS_HEADER_SIZE;
155 const size_t payload_len = xattr_len - DECMPFS_HEADER_SIZE;
156 if (payload_len == 0) {
157 error = "decmpfs xattr has no payload";
158 return false;
159 }
160
161 // The 0xFF marker means "raw uncompressed data follows"
162 if (payload[0] == 0xFF) {
163 const size_t raw_len = payload_len - 1;
164 if (raw_len < h.size) {
165 // Tolerate: maybe the size declared is wrong; use what we
166 // have.
167 out.assign(payload + 1, payload + 1 + raw_len);
168 } else {
169 out.assign(payload + 1, payload + 1 + h.size);
170 }
171 return true;
172 }
173
174 // Standard zlib stream
175 out.resize(static_cast<size_t>(h.size));
176 z_stream zs{};
177 zs.next_in = const_cast<Bytef*>(payload);
178 zs.avail_in = static_cast<uInt>(payload_len);
179 zs.next_out = out.data();
180 zs.avail_out = static_cast<uInt>(out.size());
181 int ret = inflateInit(&zs);
182 if (ret != Z_OK) {
183 error = "inflateInit failed";
184 return false;
185 }
186 ret = inflate(&zs, Z_FINISH);
187 if (ret != Z_STREAM_END && ret != Z_OK) {
188 error = std::string("inflate failed: ") +
189 (zs.msg ? zs.msg : "unknown error");
190 inflateEnd(&zs);
191 return false;
192 }
193 out.resize(static_cast<size_t>(zs.total_out));
194 inflateEnd(&zs);
195 return true;
196}
bool parse_decmpfs_header(const unsigned char *buf, DecmpfsHeader &out)
constexpr size_t DECMPFS_HEADER_SIZE

References DECMPFS_HEADER_SIZE, decompress_xattr_zlib(), kCompressionType_ZlibInXattr, parse_decmpfs_header(), hfs::DecmpfsHeader::size, and hfs::DecmpfsHeader::type.

Referenced by decompress_xattr_zlib(), and HfsRecovery::recover().

◆ find_inline_attribute()

bool hfs::find_inline_attribute ( const std::vector< unsigned char > & attributes_btree_buffer,
uint32_t fileID,
const std::string & attr_name,
std::vector< unsigned char > & out,
std::vector< std::string > & logs )

Definition at line 28 of file hfs_attributes.cpp.

32 {
33 out.clear();
34 if (attributes_btree_buffer.size() < 14) return false;
35
36 // Step 1: parse the B-tree header (node 0)
38 if (!parse_node_descriptor(attributes_btree_buffer.data(), n0) ||
39 n0.kind != kBTHeaderNode) {
40 logs.push_back("HFS+ Attributes: node 0 is not a header node");
41 return false;
42 }
43 HeaderRec hdr;
44 if (!parse_header_rec(attributes_btree_buffer.data() + 14, hdr)) {
45 logs.push_back("HFS+ Attributes: B-tree header parse failed");
46 return false;
47 }
48
49 // Step 2: build the UTF-16BE search key (we only match by name + fileID;
50 // attribute records may also have startBlock != 0 for extent
51 // continuation records, but those are not Inline records)
52 const auto target_name_utf16be = utf8_to_utf16be_ascii(attr_name);
53
54 // Step 3: walk the tree looking for matching records
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);
61 return true;
62 };
63
64 bool found = false;
65 auto on_leaf = [&](const unsigned char* rec, uint16_t rec_len) -> bool {
66 // Attribute KEY layout (after the 2-byte keyLength):
67 // __be16 pad
68 // __be32 fileID
69 // __be32 startBlock
70 // __be16 attrNameLength
71 // UTF-16BE attrName[attrNameLength]
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;
75 // pad at +2
76 const uint32_t rec_fileID = be32(rec + 4);
77 if (rec_fileID != fileID) return true;
78 // startBlock at +8 (we want startBlock=0 for the primary record)
79 const uint32_t rec_startBlock = be32(rec + 8);
80 if (rec_startBlock != 0) return true; // skip overflow records
81 const uint16_t rec_nameLen = be16(rec + 12);
82 if (rec_nameLen != target_name_utf16be.size() / 2) return true;
83 // Compare UTF-16BE name byte-by-byte
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;
89
90 // Found a key match. Body starts AFTER the key, on a 2-byte
91 // alignment boundary.
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;
95
96 const uint32_t recordType = be32(rec + body_off);
97 if (recordType != kHFSPlusAttrInlineData) {
98 // ForkData or Extents -- not supported here
99 std::ostringstream m;
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());
105 return false; // stop walk: this is the only record we expected
106 }
107
108 // Inline record body layout:
109 // __be32 recordType (= 0x10) already read
110 // __be32 reserved[2] +4..+12
111 // __be32 attrSize +12
112 // attribute data[attrSize] +16
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");
118 return false;
119 }
120 out.assign(rec + body_off + 16, rec + body_off + 16 + attrSize);
121 found = true;
122 return false; // stop walk -- we got what we wanted
123 };
124
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);
131 }
132 return found;
133}
std::vector< unsigned char > utf8_to_utf16be_ascii(const std::string &s)
constexpr uint32_t kHFSPlusAttrInlineData
bool parse_node_descriptor(const unsigned char *buf, NodeDescriptor &out)
Definition hfs_btree.cpp:17
bool walk_btree(const HeaderRec &header, const NodeReader &read_node, const LeafRecordCallback &on_leaf, std::vector< std::string > &logs, std::string &error)
constexpr int8_t kBTHeaderNode
Definition hfs_btree.hpp:34
bool parse_header_rec(const unsigned char *buf, HeaderRec &out)
Definition hfs_btree.cpp:38

References be16(), be32(), find_inline_attribute(), kBTHeaderNode, kHFSPlusAttrInlineData, hfs::NodeDescriptor::kind, hfs::HeaderRec::nodeSize, parse_header_rec(), parse_node_descriptor(), and walk_btree().

Referenced by find_inline_attribute(), and HfsRecovery::recover().

◆ hfs_filename_to_host()

std::string hfs::hfs_filename_to_host ( const unsigned char * src,
size_t code_units )

Definition at line 285 of file hfs_unicode.cpp.

285 {
286 auto cps = utf16be_to_codepoints(src, code_units);
287 cps = compose_nfc(cps);
288 std::string raw = codepoints_to_utf8(cps);
289
290 // Sanitise for host filesystem: replace NUL and '/' with '_'.
291 // (We don't apply Windows-specific stripping here; the project's
292 // text_utils helpers handle that elsewhere. We just produce a
293 // string safe for posix paths.)
294 std::string out;
295 out.reserve(raw.size());
296 for (char c : raw) {
297 unsigned char uc = static_cast<unsigned char>(c);
298 if (uc == 0 || uc == '/') out.push_back('_');
299 else out.push_back(c);
300 }
301 if (out.empty()) out = "_";
302 return out;
303}

References hfs_filename_to_host().

Referenced by catalog_name_to_host(), and hfs_filename_to_host().

◆ is_private_data_folder_name()

bool hfs::is_private_data_folder_name ( const unsigned char * utf16be_name,
uint16_t name_units )

Definition at line 129 of file hfs_catalog.cpp.

130 {
131 // Folder name on disk is 4 NUL (UTF-16) units followed by
132 // "HFS+ Private Data" (17 ASCII chars).
133 if (name_units != kHFSPrivateDataNameLength) return false;
134 // First 4 code units (8 bytes) must be all zero.
135 for (int i = 0; i < 8; ++i) {
136 if (utf16be_name[i] != 0) return false;
137 }
138 // Remaining 17 code units must be ASCII "HFS+ Private Data"
139 static const char* expected = "HFS+ Private Data";
140 for (int i = 0; i < 17; ++i) {
141 const unsigned char hi = utf16be_name[8 + i * 2];
142 const unsigned char lo = utf16be_name[8 + i * 2 + 1];
143 if (hi != 0) return false;
144 if (lo != static_cast<unsigned char>(expected[i])) return false;
145 }
146 return true;
147}
constexpr int kHFSPrivateDataNameLength

References is_private_data_folder_name(), and kHFSPrivateDataNameLength.

Referenced by is_private_data_folder_name(), and HfsRecovery::recover().

◆ nfd_to_nfc()

std::string hfs::nfd_to_nfc ( const std::string & utf8)

Definition at line 260 of file hfs_unicode.cpp.

260 {
261 // Decode UTF-8 -> code points (we assume well-formed UTF-8 since
262 // it came from utf16be_to_utf8 above).
263 std::vector<uint32_t> cps;
264 cps.reserve(utf8.size());
265 size_t i = 0;
266 while (i < utf8.size()) {
267 const unsigned char b = static_cast<unsigned char>(utf8[i]);
268 uint32_t cp = 0;
269 size_t len = 0;
270 if (b < 0x80) { cp = b; len = 1; }
271 else if ((b & 0xE0) == 0xC0) { cp = b & 0x1F; len = 2; }
272 else if ((b & 0xF0) == 0xE0) { cp = b & 0x0F; len = 3; }
273 else if ((b & 0xF8) == 0xF0) { cp = b & 0x07; len = 4; }
274 else { cp = 0xFFFD; len = 1; }
275 if (i + len > utf8.size()) { cps.push_back(0xFFFD); break; }
276 for (size_t j = 1; j < len; ++j) {
277 cp = (cp << 6) | (static_cast<unsigned char>(utf8[i + j]) & 0x3F);
278 }
279 cps.push_back(cp);
280 i += len;
281 }
282 return codepoints_to_utf8(compose_nfc(cps));
283}
std::vector< uint32_t > compose_nfc(const std::vector< uint32_t > &nfd)
std::string codepoints_to_utf8(const std::vector< uint32_t > &cps)

References nfd_to_nfc().

Referenced by nfd_to_nfc().

◆ node_record_length()

uint16_t hfs::node_record_length ( const unsigned char * node_buf,
uint16_t nodeSize,
uint16_t numRecords,
uint16_t i )

Definition at line 88 of file hfs_btree.cpp.

91 {
92 if (i >= numRecords) return 0;
93 const uint16_t this_off = node_record_offset(node_buf, nodeSize,
94 numRecords, i);
95 if (this_off == 0) return 0;
96 // Next offset (which is at index i+1 in the descending table) gives
97 // the END of record `i`. For i == numRecords-1, the next entry IS
98 // the "free space" offset, which is the end of the records area.
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; // corrupt
104 return next_off - this_off;
105}
uint16_t node_record_offset(const unsigned char *node_buf, uint16_t nodeSize, uint16_t numRecords, uint16_t i)
Definition hfs_btree.cpp:70

References be16(), node_record_length(), and node_record_offset().

Referenced by hfs::anonymous_namespace{hfs_btree.cpp}::descend(), and node_record_length().

◆ node_record_offset()

uint16_t hfs::node_record_offset ( const unsigned char * node_buf,
uint16_t nodeSize,
uint16_t numRecords,
uint16_t i )

Definition at line 70 of file hfs_btree.cpp.

73 {
74 if (i >= numRecords) return 0;
75 if (node_buf == nullptr) return 0;
76 if (nodeSize < 14 + 2u * (static_cast<uint32_t>(numRecords) + 1)) {
77 return 0; // not enough room for offset table
78 }
79 // The offsets table sits at the END of the node, with one 16-bit
80 // entry per record (numRecords entries) plus a "free space" offset
81 // (numRecords+1 entries total). The LAST 2 bytes hold the offset
82 // of record 0; the 2 before that hold record 1; and so on.
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);
86}

References be16(), and node_record_offset().

Referenced by hfs::anonymous_namespace{hfs_btree.cpp}::descend(), node_record_length(), and node_record_offset().

◆ parse_catalog_file()

bool hfs::parse_catalog_file ( const unsigned char * body,
uint32_t body_len,
CatalogFile & out )

Definition at line 78 of file hfs_catalog.cpp.

79 {
80 if (body == nullptr || body_len < 246) return false;
81 // Body layout (after the 2-byte recordType):
82 // 0x00 __be16 flags
83 // 0x02 __be32 reserved1
84 // 0x06 __be32 fileID
85 // 0x0A __be32 createDate
86 // 0x0E __be32 contentModDate
87 // 0x12 __be32 attributeModDate
88 // 0x16 __be32 accessDate
89 // 0x1A __be32 backupDate
90 // 0x1E permissions (16)
91 // 0x2E userInfo (16)
92 // 0x3E ExtendedFileInfo (16)
93 // 0x4E __be32 textEncoding
94 // 0x52 __be32 reserved2
95 // 0x56 dataFork (80)
96 // 0xA6 resourceFork (80)
97 // Total body = 246 bytes.
98 out = {};
99 out.flags = be16(body + 0x00);
100 out.fileID = be32(body + 0x06);
101 out.createDate = be32(body + 0x0A);
102 out.contentModDate = be32(body + 0x0E);
103 out.attributeModDate = be32(body + 0x12);
104 out.accessDate = be32(body + 0x16);
105 out.backupDate = be32(body + 0x1A);
106 out.permissions = parse_bsd_info(body + 0x1E);
107 out.userInfo = parse_finder_info(body + 0x2E);
108 out.textEncoding = be32(body + 0x4E);
109 if (!parse_fork_data(body + 0x56, out.dataFork)) return false;
110 if (!parse_fork_data(body + 0xA6, out.resourceFork)) return false;
111 return true;
112}
bool parse_fork_data(const unsigned char *buf, ForkData &out)
uint32_t attributeModDate
FinderInfo16 userInfo
uint32_t contentModDate
uint32_t textEncoding
ForkData resourceFork

References hfs::CatalogFile::accessDate, hfs::CatalogFile::attributeModDate, hfs::CatalogFile::backupDate, be16(), be32(), hfs::CatalogFile::contentModDate, hfs::CatalogFile::createDate, hfs::CatalogFile::dataFork, hfs::CatalogFile::fileID, hfs::CatalogFile::flags, parse_catalog_file(), parse_fork_data(), hfs::CatalogFile::permissions, hfs::CatalogFile::resourceFork, hfs::CatalogFile::textEncoding, and hfs::CatalogFile::userInfo.

Referenced by parse_catalog_file(), and HfsRecovery::recover().

◆ parse_catalog_folder()

bool hfs::parse_catalog_folder ( const unsigned char * body,
uint32_t body_len,
CatalogFolder & out )

Definition at line 44 of file hfs_catalog.cpp.

45 {
46 if (body == nullptr || body_len < 86) return false;
47 // Body layout (after the 2-byte recordType already consumed):
48 // 0x00 __be16 flags
49 // 0x02 __be32 valence
50 // 0x06 __be32 folderID
51 // 0x0A __be32 createDate
52 // 0x0E __be32 contentModDate
53 // 0x12 __be32 attributeModDate
54 // 0x16 __be32 accessDate
55 // 0x1A __be32 backupDate
56 // 0x1E HFSPlusBSDInfo permissions (16)
57 // 0x2E userInfo (16)
58 // 0x3E ExtendedFolderInfo (16) -- skipped
59 // 0x4E __be32 textEncoding
60 // 0x52 __be32 reserved
61 // Total body = 86 bytes (not 88 -- the 2 bytes of recordType are
62 // separate from this "body" view).
63 out = {};
64 out.flags = be16(body + 0x00);
65 out.valence = be32(body + 0x02);
66 out.folderID = be32(body + 0x06);
67 out.createDate = be32(body + 0x0A);
68 out.contentModDate = be32(body + 0x0E);
69 out.attributeModDate = be32(body + 0x12);
70 out.accessDate = be32(body + 0x16);
71 out.backupDate = be32(body + 0x1A);
72 out.permissions = parse_bsd_info(body + 0x1E);
73 out.userInfo = parse_finder_info(body + 0x2E);
74 out.textEncoding = be32(body + 0x4E);
75 return true;
76}
FinderInfo16 userInfo
uint32_t attributeModDate

References hfs::CatalogFolder::accessDate, hfs::CatalogFolder::attributeModDate, hfs::CatalogFolder::backupDate, be16(), be32(), hfs::CatalogFolder::contentModDate, hfs::CatalogFolder::createDate, hfs::CatalogFolder::flags, hfs::CatalogFolder::folderID, parse_catalog_folder(), hfs::CatalogFolder::permissions, hfs::CatalogFolder::textEncoding, hfs::CatalogFolder::userInfo, and hfs::CatalogFolder::valence.

Referenced by parse_catalog_folder(), and HfsRecovery::recover().

◆ parse_catalog_key()

bool hfs::parse_catalog_key ( const unsigned char * key_buf,
uint32_t max_bytes,
CatalogKey & out )

Definition at line 111 of file hfs_btree.cpp.

113 {
114 if (key_buf == nullptr || max_bytes < 8) return false;
115 out = {};
116 const uint16_t keyLength = be16(key_buf + 0);
117 // Total key size = 2 (length field) + keyLength bytes of payload.
118 // Payload = parentID(4) + nameLength(2) + name[nameLength*2].
119 if (keyLength < 6) return false; // must include parentID + nameLen
120 if (2u + keyLength > max_bytes) return false;
121 out.total_key_bytes = 2u + keyLength;
122 out.parentID = be32(key_buf + 2);
123 out.nameLength = be16(key_buf + 6);
124 if (8u + static_cast<uint32_t>(out.nameLength) * 2u > 2u + keyLength) {
125 return false; // name overflows declared length
126 }
127 out.name_utf16be = key_buf + 8;
128 return true;
129}
uint32_t parentID
const unsigned char * name_utf16be
uint16_t total_key_bytes
uint16_t nameLength

References be16(), be32(), hfs::CatalogKey::name_utf16be, hfs::CatalogKey::nameLength, hfs::CatalogKey::parentID, parse_catalog_key(), and hfs::CatalogKey::total_key_bytes.

Referenced by parse_catalog_key(), and HfsRecovery::recover().

◆ parse_decmpfs_header()

bool hfs::parse_decmpfs_header ( const unsigned char * buf,
DecmpfsHeader & out )

Definition at line 81 of file hfs_decmpfs.cpp.

81 {
82 if (buf == nullptr) return false;
83 out = {};
84 out.magic = le32(buf + 0);
85 if (out.magic != DECMPFS_MAGIC) return false;
86 out.type = le32(buf + 4);
87 // The size field is little-endian 64-bit
88 uint64_t size_lo = le32(buf + 8);
89 uint64_t size_hi = le32(buf + 12);
90 out.size = size_lo | (size_hi << 32);
91 return true;
92}
constexpr uint32_t DECMPFS_MAGIC

References DECMPFS_MAGIC, hfs::DecmpfsHeader::magic, parse_decmpfs_header(), hfs::DecmpfsHeader::size, and hfs::DecmpfsHeader::type.

Referenced by decompress_xattr_zlib(), and parse_decmpfs_header().

◆ parse_fork_data()

bool hfs::parse_fork_data ( const unsigned char * buf,
ForkData & out )

Definition at line 13 of file hfs_format.cpp.

13 {
14 if (buf == nullptr) return false;
15 out = {};
16 out.logical_size = be64(buf + 0x00);
17 out.clump_size = be32(buf + 0x08);
18 out.total_blocks = be32(buf + 0x0C);
19
20 // 8 extent descriptors, each 8 bytes (start_block + block_count)
21 for (size_t i = 0; i < 8; ++i) {
22 const unsigned char* p = buf + 0x10 + i * 8;
23 out.extents[i].start_block = be32(p + 0);
24 out.extents[i].block_count = be32(p + 4);
25 }
26 return true;
27}
uint64_t be64(const unsigned char *p)
uint64_t logical_size
std::array< ForkExtent, 8 > extents
uint32_t total_blocks
uint32_t clump_size

References be32(), be64(), hfs::ForkData::clump_size, hfs::ForkData::extents, hfs::ForkData::logical_size, parse_fork_data(), and hfs::ForkData::total_blocks.

Referenced by parse_catalog_file(), parse_fork_data(), and parse_volume_header().

◆ parse_header_rec()

bool hfs::parse_header_rec ( const unsigned char * buf,
HeaderRec & out )

Definition at line 38 of file hfs_btree.cpp.

38 {
39 if (buf == nullptr) return false;
40 out = {};
41 out.treeDepth = be16(buf + 0x00);
42 out.rootNode = be32(buf + 0x02);
43 out.leafRecords = be32(buf + 0x06);
44 out.firstLeafNode = be32(buf + 0x0A);
45 out.lastLeafNode = be32(buf + 0x0E);
46 out.nodeSize = be16(buf + 0x12);
47 out.maxKeyLength = be16(buf + 0x14);
48 out.totalNodes = be32(buf + 0x16);
49 out.freeNodes = be32(buf + 0x1A);
50 // 2 bytes reserved at +0x1E
51 out.clumpSize = be32(buf + 0x20);
52 out.btreeType = buf[0x24];
53 out.keyCompareType = buf[0x25];
54 out.attributes = be32(buf + 0x26);
55 // 64 bytes of reserved at +0x2A..+0x6A
56
57 // Sanity: nodeSize must be a power of 2 in [512, 32768]
58 // (uint16_t max is 65535, so the upper bound is implicit; any
59 // power-of-two value in our range is necessarily <= 32768)
60 if (out.nodeSize < 512 ||
61 (out.nodeSize & (out.nodeSize - 1)) != 0) {
62 return false;
63 }
64 // treeDepth must agree with totalNodes (depth==0 means empty tree;
65 // depth==1 means a single leaf; depth>16 is implausible).
66 if (out.treeDepth > 16) return false;
67 return true;
68}
uint32_t rootNode
Definition hfs_btree.hpp:65
uint16_t nodeSize
Definition hfs_btree.hpp:69
uint32_t clumpSize
Definition hfs_btree.hpp:73
uint32_t totalNodes
Definition hfs_btree.hpp:71
uint32_t lastLeafNode
Definition hfs_btree.hpp:68
uint32_t leafRecords
Definition hfs_btree.hpp:66
uint8_t btreeType
Definition hfs_btree.hpp:74
uint16_t maxKeyLength
Definition hfs_btree.hpp:70
uint8_t keyCompareType
Definition hfs_btree.hpp:75
uint32_t attributes
Definition hfs_btree.hpp:76
uint32_t firstLeafNode
Definition hfs_btree.hpp:67
uint32_t freeNodes
Definition hfs_btree.hpp:72
uint16_t treeDepth
Definition hfs_btree.hpp:64

References hfs::HeaderRec::attributes, be16(), be32(), hfs::HeaderRec::btreeType, hfs::HeaderRec::clumpSize, hfs::HeaderRec::firstLeafNode, hfs::HeaderRec::freeNodes, hfs::HeaderRec::keyCompareType, hfs::HeaderRec::lastLeafNode, hfs::HeaderRec::leafRecords, hfs::HeaderRec::maxKeyLength, hfs::HeaderRec::nodeSize, parse_header_rec(), hfs::HeaderRec::rootNode, hfs::HeaderRec::totalNodes, and hfs::HeaderRec::treeDepth.

Referenced by find_inline_attribute(), parse_header_rec(), read_fork_full(), and HfsRecovery::recover().

◆ parse_inode_filename()

uint32_t hfs::parse_inode_filename ( const unsigned char * utf16be_name,
uint16_t name_units )

Definition at line 149 of file hfs_catalog.cpp.

150 {
151 // "iNode" prefix: 5 ASCII chars + at least 1 digit
152 if (name_units < 6) return 0;
153 static const char* prefix = "iNode";
154 for (int i = 0; i < 5; ++i) {
155 const unsigned char hi = utf16be_name[i * 2];
156 const unsigned char lo = utf16be_name[i * 2 + 1];
157 if (hi != 0) return 0;
158 if (lo != static_cast<unsigned char>(prefix[i])) return 0;
159 }
160 // Decode the trailing digits
161 uint32_t num = 0;
162 for (uint16_t i = 5; i < name_units; ++i) {
163 const unsigned char hi = utf16be_name[i * 2];
164 const unsigned char lo = utf16be_name[i * 2 + 1];
165 if (hi != 0) return 0;
166 if (lo < '0' || lo > '9') return 0;
167 const uint32_t prev = num;
168 num = num * 10 + (lo - '0');
169 if (num < prev) return 0; // overflow
170 }
171 return num;
172}

References parse_inode_filename().

Referenced by parse_inode_filename(), and HfsRecovery::recover().

◆ parse_node_descriptor()

bool hfs::parse_node_descriptor ( const unsigned char * buf,
NodeDescriptor & out )

Definition at line 17 of file hfs_btree.cpp.

17 {
18 if (buf == nullptr) return false;
19 out = {};
20 out.fLink = be32(buf + 0x00);
21 out.bLink = be32(buf + 0x04);
22 // kind is a SIGNED 8-bit value (-1 for leaf, 0/1/2 otherwise)
23 out.kind = static_cast<int8_t>(buf[0x08]);
24 out.height = buf[0x09];
25 out.numRecords = be16(buf + 0x0A);
26 // reserved at +0x0C..+0x0E ignored
27
28 // Sanity: kind must be -1, 0, 1, or 2
29 if (out.kind != kBTLeafNode &&
30 out.kind != kBTIndexNode &&
31 out.kind != kBTHeaderNode &&
32 out.kind != kBTMapNode) {
33 return false;
34 }
35 return true;
36}
constexpr int8_t kBTIndexNode
Definition hfs_btree.hpp:33
constexpr int8_t kBTMapNode
Definition hfs_btree.hpp:35
constexpr int8_t kBTLeafNode
Definition hfs_btree.hpp:32

References be16(), be32(), hfs::NodeDescriptor::bLink, hfs::NodeDescriptor::fLink, hfs::NodeDescriptor::height, kBTHeaderNode, kBTIndexNode, kBTLeafNode, kBTMapNode, hfs::NodeDescriptor::kind, hfs::NodeDescriptor::numRecords, and parse_node_descriptor().

Referenced by hfs::anonymous_namespace{hfs_btree.cpp}::descend(), find_inline_attribute(), parse_node_descriptor(), read_fork_full(), and HfsRecovery::recover().

◆ parse_volume_header()

bool hfs::parse_volume_header ( const unsigned char * buf,
VolumeHeader & out )

Definition at line 29 of file hfs_format.cpp.

29 {
30 if (buf == nullptr) return false;
31
32 out = {};
33 out.signature = be16(buf + 0x00);
35 return false;
36 }
37 out.version = be16(buf + 0x02);
38 // For HFS+ classic the version must be 4; for HFSX it must be 5.
39 if (out.is_hfsplus() && out.version != HFSPLUS_VERSION) return false;
40 if (out.is_hfsx() && out.version != HFSX_VERSION) return false;
41
42 out.attributes = be32(buf + 0x04);
43 out.last_mounted_version = be32(buf + 0x08);
44 out.journal_info_block = be32(buf + 0x0C);
45 out.create_date = be32(buf + 0x10);
46 out.modify_date = be32(buf + 0x14);
47 out.backup_date = be32(buf + 0x18);
48 out.checked_date = be32(buf + 0x1C);
49 out.file_count = be32(buf + 0x20);
50 out.folder_count = be32(buf + 0x24);
51 out.block_size = be32(buf + 0x28);
52 out.total_blocks = be32(buf + 0x2C);
53 out.free_blocks = be32(buf + 0x30);
54 out.next_allocation = be32(buf + 0x34);
55 out.rsrc_clump_size = be32(buf + 0x38);
56 out.data_clump_size = be32(buf + 0x3C);
57 out.next_catalog_id = be32(buf + 0x40);
58 out.write_count = be32(buf + 0x44);
59 out.encodings_bitmap = be64(buf + 0x48);
60
61 // finderInfo is 32 bytes at offset 0x50 (8 * uint32 in raw form, but
62 // we treat it opaquely)
63 std::copy(buf + 0x50, buf + 0x70, out.finder_info.begin());
64
65 // Five fork-data structs, each 80 bytes, starting at offset 0x70:
66 // allocationFile @ 0x070
67 // extentsFile @ 0x0C0
68 // catalogFile @ 0x110
69 // attributesFile @ 0x160
70 // startupFile @ 0x1B0
71 // End: 0x1B0 + 0x50 = 0x200 (= 512 bytes total).
72 if (!parse_fork_data(buf + 0x070, out.allocation_file)) return false;
73 if (!parse_fork_data(buf + 0x0C0, out.extents_file)) return false;
74 if (!parse_fork_data(buf + 0x110, out.catalog_file)) return false;
75 if (!parse_fork_data(buf + 0x160, out.attributes_file)) return false;
76 if (!parse_fork_data(buf + 0x1B0, out.startup_file)) return false;
77
78 // Sanity checks
79 // block_size must be a power of 2 in [512, 65536]
80 if (out.block_size < 512 || out.block_size > 65536 ||
81 (out.block_size & (out.block_size - 1)) != 0) {
82 return false;
83 }
84 // total_blocks must be plausible: at least the volume header occupies
85 // some blocks; reject zero. Also reject absurdly large values.
86 if (out.total_blocks == 0) return false;
87
88 // The catalog file must have a non-zero size (every HFS+ volume has
89 // at least the root folder and root thread record).
90 if (out.catalog_file.logical_size == 0) return false;
91 if (out.catalog_file.total_blocks == 0) return false;
92
93 // Sanity: the first extent of each special file must point INTO the
94 // volume range. Extent start_block + block_count <= total_blocks.
95 auto fork_in_range = [&](const ForkData& f) -> bool {
96 for (const auto& e : f.extents) {
97 if (e.block_count == 0) continue;
98 // Defensive: prevent overflow
99 if (e.start_block > out.total_blocks) return false;
100 const uint64_t end = static_cast<uint64_t>(e.start_block)
101 + e.block_count;
102 if (end > out.total_blocks) return false;
103 }
104 return true;
105 };
106 if (!fork_in_range(out.allocation_file)) return false;
107 if (!fork_in_range(out.extents_file)) return false;
108 if (!fork_in_range(out.catalog_file)) return false;
109 if (!fork_in_range(out.attributes_file)) return false;
110 if (!fork_in_range(out.startup_file)) return false;
111
112 return true;
113}
constexpr uint16_t HFSX_SIGNATURE
constexpr uint16_t HFSPLUS_SIGNATURE
constexpr uint16_t HFSX_VERSION
constexpr uint16_t HFSPLUS_VERSION
bool is_hfsx() const
uint32_t next_allocation
ForkData attributes_file
uint64_t encodings_bitmap
uint32_t data_clump_size
uint32_t next_catalog_id
uint32_t last_mounted_version
uint32_t rsrc_clump_size
uint32_t journal_info_block
ForkData allocation_file
bool is_hfsplus() const
std::array< uint8_t, 32 > finder_info

References hfs::VolumeHeader::allocation_file, hfs::VolumeHeader::attributes, hfs::VolumeHeader::attributes_file, hfs::VolumeHeader::backup_date, be16(), be32(), be64(), hfs::VolumeHeader::block_size, hfs::VolumeHeader::catalog_file, hfs::VolumeHeader::checked_date, hfs::VolumeHeader::create_date, hfs::VolumeHeader::data_clump_size, hfs::VolumeHeader::encodings_bitmap, hfs::VolumeHeader::extents_file, hfs::VolumeHeader::file_count, hfs::VolumeHeader::finder_info, hfs::VolumeHeader::folder_count, hfs::VolumeHeader::free_blocks, HFSPLUS_SIGNATURE, HFSPLUS_VERSION, HFSX_SIGNATURE, HFSX_VERSION, hfs::VolumeHeader::is_hfsplus(), hfs::VolumeHeader::is_hfsx(), hfs::VolumeHeader::journal_info_block, hfs::VolumeHeader::last_mounted_version, hfs::ForkData::logical_size, hfs::VolumeHeader::modify_date, hfs::VolumeHeader::next_allocation, hfs::VolumeHeader::next_catalog_id, parse_fork_data(), parse_volume_header(), hfs::VolumeHeader::rsrc_clump_size, hfs::VolumeHeader::signature, hfs::VolumeHeader::startup_file, hfs::ForkData::total_blocks, hfs::VolumeHeader::total_blocks, hfs::VolumeHeader::version, and hfs::VolumeHeader::write_count.

Referenced by anonymous_namespace{filesystem_probes.cpp}::hfs_probe(), anonymous_namespace{lost_partition_finder.cpp}::hfs_size_bytes(), parse_volume_header(), and HfsRecovery::recover().

◆ read_fork_block_inline()

bool hfs::read_fork_block_inline ( DiskReader & reader,
uint64_t volume_offset,
uint32_t block_size,
const ForkData & fork,
uint64_t fork_logical_offset,
unsigned char * block_buf )

Definition at line 69 of file hfs_fork_io.cpp.

74 {
75 if (block_size == 0 || block_buf == nullptr) return false;
76
77 // The fork is a concatenation of its extents. We walk extents
78 // accumulating their byte coverage; when we reach the extent that
79 // contains fork_logical_offset, we compute the absolute disk offset
80 // and issue a single read.
81 uint64_t covered = 0;
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) {
87 // Hit. The byte we want is at:
88 const uint64_t byte_within_ext = fork_logical_offset - covered;
89 // Round to a block boundary (fork_logical_offset should be
90 // block-aligned, but be defensive).
91 const uint64_t blk_within_ext = byte_within_ext / block_size;
92 const uint64_t disk_off =
93 volume_offset
94 + (static_cast<uint64_t>(e.start_block) + blk_within_ext)
95 * block_size;
96 return reader.read_at(disk_off, block_buf, block_size);
97 }
98 covered += ext_bytes;
99 }
100 // Offset beyond inline coverage -- needs Extents Overflow file.
101 return false;
102}
bool read_at(uint64_t offset, void *buffer, size_t len)

References hfs::ForkData::extents, DiskReader::read_at(), and read_fork_block_inline().

Referenced by read_fork_block_inline().

◆ read_fork_full()

bool hfs::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 )

Definition at line 200 of file hfs_fork_io.cpp.

210 {
211 if (block_size == 0) return false;
212
213 // Compute inline coverage.
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; // run terminator
218 inline_blocks += e.block_count;
219 ++inline_extent_count;
220 }
221 const uint64_t inline_bytes = inline_blocks * block_size;
222
223 const uint64_t want = std::min<uint64_t>(fork.logical_size, max_bytes);
224
225 // Fast path: fork fits in inline extents.
226 if (fork.total_blocks <= inline_blocks || inline_extent_count < 8) {
227 return read_fork_inline(reader, volume_offset, block_size,
228 fork, want, out, logs);
229 }
230
231 // Slow path: file has > 8 extents (overflow needed).
232 // Step 1: load the Extents Overflow B-tree if not already cached.
233 if (extents_btree_buffer.empty()) {
234 std::vector<std::string> ld_logs;
235 if (!read_fork_inline(reader, volume_offset, block_size,
236 extents_file, extents_file.logical_size,
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(); // mark as "tried but failed"
242 return read_fork_inline(reader, volume_offset, block_size,
243 fork, want, out, logs);
244 }
245 }
246 if (extents_btree_buffer.size() < 14) {
247 logs.push_back("HFS+: Extents Overflow file too small for B-tree");
248 return read_fork_inline(reader, volume_offset, block_size,
249 fork, want, out, logs);
250 }
251
252 // Step 2: parse overflow B-tree header (node 0).
254 if (!parse_node_descriptor(extents_btree_buffer.data(), n0) ||
255 n0.kind != kBTHeaderNode) {
256 logs.push_back("HFS+: Extents Overflow node 0 not a header node");
257 return read_fork_inline(reader, volume_offset, block_size,
258 fork, want, out, logs);
259 }
260 HeaderRec ohdr;
261 if (!parse_header_rec(extents_btree_buffer.data() + 14, ohdr)) {
262 logs.push_back("HFS+: Extents Overflow B-tree header parse failed");
263 return read_fork_inline(reader, volume_offset, block_size,
264 fork, want, out, logs);
265 }
266
267 // Step 3: collect extra extents from overflow B-tree.
268 std::vector<ForkExtent> overflow_extents;
269 collect_overflow_extents(extents_btree_buffer, ohdr,
270 forkType, fileID, overflow_extents, logs);
271
272 // Step 4: build a unified extent list (8 inline followed by overflow).
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);
278 }
279 for (const auto& e : overflow_extents) all_extents.push_back(e);
280
281 // Step 5: read all blocks in order.
282 out.clear();
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());
295 return false;
296 }
297 produced += copy;
298 }
299
300 // If we have less than `want`, log it but don't fail (best-effort).
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());
307 }
308
309 (void)inline_bytes;
310 return true;
311}
size_t collect_overflow_extents(const std::vector< unsigned char > &btree_buf, const HeaderRec &hdr, uint8_t forkType, uint32_t fileID, std::vector< ForkExtent > &out, std::vector< std::string > &logs)
bool read_one_extent(DiskReader &reader, uint64_t volume_offset, uint32_t block_size, const ForkExtent &e, uint64_t want_bytes, std::vector< unsigned char > &out)
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)

References hfs::ForkData::extents, kBTHeaderNode, hfs::NodeDescriptor::kind, hfs::ForkData::logical_size, parse_header_rec(), parse_node_descriptor(), read_fork_full(), read_fork_inline(), and hfs::ForkData::total_blocks.

Referenced by read_fork_full(), and HfsRecovery::recover().

◆ read_fork_inline()

bool hfs::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 )

Definition at line 12 of file hfs_fork_io.cpp.

18 {
19 if (block_size == 0) return false;
20
21 // Total bytes we want to read: min of fork.logical_size, max_bytes,
22 // and inline-coverage (sum of all extent lengths in bytes).
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;
26 }
27
28 uint64_t want = std::min<uint64_t>(fork.logical_size, max_bytes);
29 if (want > inline_bytes) {
30 std::ostringstream m;
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());
36 want = inline_bytes; // soft truncate
37 }
38
39 out.clear();
40 out.reserve(static_cast<size_t>(want));
41
42 uint64_t produced = 0;
43 for (const auto& e : fork.extents) {
44 if (produced >= want) break;
45 if (e.block_count == 0) continue;
46
47 const uint64_t ext_disk_off =
48 volume_offset
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);
53
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)) {
57 std::ostringstream m;
58 m << "HFS+ fork: read failed at disk offset " << ext_disk_off
59 << " for " << copy << " bytes";
60 logs.push_back(m.str());
61 out.resize(before);
62 return false;
63 }
64 produced += copy;
65 }
66 return true;
67}

References hfs::ForkData::extents, hfs::ForkData::logical_size, DiskReader::read_at(), and read_fork_inline().

Referenced by read_fork_full(), read_fork_inline(), and HfsRecovery::recover().

◆ utf16be_to_utf8()

std::string hfs::utf16be_to_utf8 ( const unsigned char * src,
size_t code_units )

Definition at line 254 of file hfs_unicode.cpp.

254 {
255 if (src == nullptr || code_units == 0) return {};
256 auto cps = utf16be_to_codepoints(src, code_units);
257 return codepoints_to_utf8(cps);
258}

References utf16be_to_utf8().

Referenced by utf16be_to_utf8().

◆ walk_btree()

bool hfs::walk_btree ( const HeaderRec & header,
const NodeReader & read_node,
const LeafRecordCallback & on_leaf,
std::vector< std::string > & logs,
std::string & error )

Definition at line 269 of file hfs_btree.cpp.

273 {
274 if (header.rootNode == 0 || header.nodeSize == 0) {
275 error = "HFS+ B-tree: empty/zero rootNode";
276 return false;
277 }
278 if (header.treeDepth > 16) {
279 error = "HFS+ B-tree: implausible tree depth";
280 return false;
281 }
282 std::set<uint32_t> visited;
283 size_t nodes_visited = 0;
284 bool callback_stopped = false;
285 return descend(header.rootNode, header.treeDepth, header, read_node,
286 on_leaf, visited, nodes_visited, logs, error,
287 callback_stopped);
288}
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)

References hfs::HeaderRec::nodeSize, hfs::HeaderRec::rootNode, hfs::HeaderRec::treeDepth, and walk_btree().

Referenced by hfs::anonymous_namespace{hfs_fork_io.cpp}::collect_overflow_extents(), find_inline_attribute(), HfsRecovery::recover(), and walk_btree().

Variable Documentation

◆ DECMPFS_HEADER_SIZE

size_t hfs::DECMPFS_HEADER_SIZE = 16
constexpr

Definition at line 66 of file hfs_decmpfs.hpp.

Referenced by decompress_xattr_zlib().

◆ DECMPFS_MAGIC

uint32_t hfs::DECMPFS_MAGIC = 0x66706D63
constexpr

Definition at line 64 of file hfs_decmpfs.hpp.

Referenced by parse_decmpfs_header().

◆ HFSPLUS_SIGNATURE

uint16_t hfs::HFSPLUS_SIGNATURE = 0x482B
constexpr

◆ HFSPLUS_VERSION

uint16_t hfs::HFSPLUS_VERSION = 4
constexpr

Definition at line 75 of file hfs_format.hpp.

Referenced by parse_volume_header().

◆ HFSPLUS_VOLUME_HEADER_OFFSET

◆ HFSX_SIGNATURE

uint16_t hfs::HFSX_SIGNATURE = 0x4858
constexpr

◆ HFSX_VERSION

uint16_t hfs::HFSX_VERSION = 5
constexpr

Definition at line 76 of file hfs_format.hpp.

Referenced by parse_volume_header().

◆ kBTHeaderNode

int8_t hfs::kBTHeaderNode = 1
constexpr

◆ kBTIndexNode

int8_t hfs::kBTIndexNode = 0
constexpr

◆ kBTLeafNode

int8_t hfs::kBTLeafNode = -1
constexpr

◆ kBTMapNode

int8_t hfs::kBTMapNode = 2
constexpr

Definition at line 35 of file hfs_btree.hpp.

Referenced by parse_node_descriptor().

◆ kCompressionType_LzfseInRsrc

uint32_t hfs::kCompressionType_LzfseInRsrc = 12
constexpr

Definition at line 76 of file hfs_decmpfs.hpp.

Referenced by decmpfs_type_name().

◆ kCompressionType_LzfseInXattr

uint32_t hfs::kCompressionType_LzfseInXattr = 11
constexpr

Definition at line 75 of file hfs_decmpfs.hpp.

Referenced by decmpfs_type_name().

◆ kCompressionType_LzvnInRsrc

uint32_t hfs::kCompressionType_LzvnInRsrc = 8
constexpr

Definition at line 74 of file hfs_decmpfs.hpp.

Referenced by decmpfs_type_name().

◆ kCompressionType_LzvnInXattr

uint32_t hfs::kCompressionType_LzvnInXattr = 7
constexpr

Definition at line 73 of file hfs_decmpfs.hpp.

Referenced by decmpfs_type_name().

◆ kCompressionType_None

uint32_t hfs::kCompressionType_None = 1
constexpr

Definition at line 70 of file hfs_decmpfs.hpp.

Referenced by decmpfs_type_name().

◆ kCompressionType_ZlibInRsrc

uint32_t hfs::kCompressionType_ZlibInRsrc = 4
constexpr

Definition at line 72 of file hfs_decmpfs.hpp.

Referenced by decmpfs_supported(), and decmpfs_type_name().

◆ kCompressionType_ZlibInXattr

uint32_t hfs::kCompressionType_ZlibInXattr = 3
constexpr

Definition at line 71 of file hfs_decmpfs.hpp.

Referenced by decmpfs_supported(), decmpfs_type_name(), and decompress_xattr_zlib().

◆ kDecmpfsBlockSize

size_t hfs::kDecmpfsBlockSize = 64 * 1024
constexpr

Definition at line 67 of file hfs_decmpfs.hpp.

Referenced by decompress_resource_fork_zlib().

◆ kForkTypeData

uint8_t hfs::kForkTypeData = 0x00
constexpr

Definition at line 86 of file hfs_fork_io.hpp.

Referenced by HfsRecovery::recover().

◆ kForkTypeResource

uint8_t hfs::kForkTypeResource = 0xFF
constexpr

Definition at line 87 of file hfs_fork_io.hpp.

Referenced by HfsRecovery::recover().

◆ kHardLinkFileType

uint32_t hfs::kHardLinkFileType = 0x686C6E6B
constexpr

Definition at line 56 of file hfs_catalog.hpp.

Referenced by hfs::CatalogFile::is_hardlink(), and HfsRecovery::recover().

◆ kHFSAllocationFileID

uint32_t hfs::kHFSAllocationFileID = 6
constexpr

Definition at line 92 of file hfs_format.hpp.

◆ kHFSAttributesFileID

uint32_t hfs::kHFSAttributesFileID = 8
constexpr

Definition at line 94 of file hfs_format.hpp.

◆ kHFSBadBlockFileID

uint32_t hfs::kHFSBadBlockFileID = 5
constexpr

Definition at line 91 of file hfs_format.hpp.

◆ kHFSBinaryCompare

uint8_t hfs::kHFSBinaryCompare = 0xBC
constexpr

Definition at line 39 of file hfs_btree.hpp.

◆ kHFSBogusExtentFileID

uint32_t hfs::kHFSBogusExtentFileID = 15
constexpr

Definition at line 96 of file hfs_format.hpp.

◆ kHFSBTreeType

uint8_t hfs::kHFSBTreeType = 0xFF
constexpr

Definition at line 42 of file hfs_btree.hpp.

◆ kHFSCaseFolding

uint8_t hfs::kHFSCaseFolding = 0xCF
constexpr

Definition at line 38 of file hfs_btree.hpp.

◆ kHFSCatalogFileID

uint32_t hfs::kHFSCatalogFileID = 4
constexpr

Definition at line 90 of file hfs_format.hpp.

◆ kHFSExtentsFileID

uint32_t hfs::kHFSExtentsFileID = 3
constexpr

Definition at line 89 of file hfs_format.hpp.

◆ kHFSFileLockedBit

uint16_t hfs::kHFSFileLockedBit = 0x0001
constexpr

Definition at line 101 of file hfs_catalog.hpp.

◆ kHFSFirstUserCatalogNodeID

uint32_t hfs::kHFSFirstUserCatalogNodeID = 16
constexpr

Definition at line 97 of file hfs_format.hpp.

◆ kHFSHasAttributesBit

uint16_t hfs::kHFSHasAttributesBit = 0x0004
constexpr

Definition at line 103 of file hfs_catalog.hpp.

Referenced by HfsRecovery::recover().

◆ kHFSHasChildLinkBit

uint16_t hfs::kHFSHasChildLinkBit = 0x0040
constexpr

Definition at line 107 of file hfs_catalog.hpp.

◆ kHFSHasDateAddedBit

uint16_t hfs::kHFSHasDateAddedBit = 0x0080
constexpr

Definition at line 108 of file hfs_catalog.hpp.

◆ kHFSHasFolderCountBit

uint16_t hfs::kHFSHasFolderCountBit = 0x0010
constexpr

Definition at line 105 of file hfs_catalog.hpp.

◆ kHFSHasLinkChainBit

uint16_t hfs::kHFSHasLinkChainBit = 0x0020
constexpr

Definition at line 106 of file hfs_catalog.hpp.

◆ kHFSHasSecurityBit

uint16_t hfs::kHFSHasSecurityBit = 0x0008
constexpr

Definition at line 104 of file hfs_catalog.hpp.

◆ kHFSPlusAttrExtents

uint32_t hfs::kHFSPlusAttrExtents = 0x30
constexpr

Definition at line 51 of file hfs_attributes.hpp.

◆ kHFSPlusAttrForkData

uint32_t hfs::kHFSPlusAttrForkData = 0x20
constexpr

Definition at line 50 of file hfs_attributes.hpp.

◆ kHFSPlusAttrInlineData

uint32_t hfs::kHFSPlusAttrInlineData = 0x10
constexpr

Definition at line 49 of file hfs_attributes.hpp.

Referenced by find_inline_attribute().

◆ kHFSPlusCreator

uint32_t hfs::kHFSPlusCreator = 0x6866732B
constexpr

Definition at line 57 of file hfs_catalog.hpp.

Referenced by hfs::CatalogFile::is_hardlink(), and HfsRecovery::recover().

◆ kHFSPlusFileRecord

int16_t hfs::kHFSPlusFileRecord = 0x0002
constexpr

Definition at line 115 of file hfs_btree.hpp.

Referenced by HfsRecovery::recover().

◆ kHFSPlusFileThreadRecord

int16_t hfs::kHFSPlusFileThreadRecord = 0x0004
constexpr

Definition at line 117 of file hfs_btree.hpp.

Referenced by HfsRecovery::recover().

◆ kHFSPlusFolderRecord

int16_t hfs::kHFSPlusFolderRecord = 0x0001
constexpr

Definition at line 114 of file hfs_btree.hpp.

Referenced by HfsRecovery::recover().

◆ kHFSPlusFolderThreadRecord

int16_t hfs::kHFSPlusFolderThreadRecord = 0x0003
constexpr

Definition at line 116 of file hfs_btree.hpp.

Referenced by HfsRecovery::recover().

◆ kHFSPrivateDataNameLength

int hfs::kHFSPrivateDataNameLength = 21
constexpr

Definition at line 135 of file hfs_catalog.hpp.

Referenced by is_private_data_folder_name().

◆ kHFSRepairCatalogFileID

uint32_t hfs::kHFSRepairCatalogFileID = 14
constexpr

Definition at line 95 of file hfs_format.hpp.

◆ kHFSRootFolderID

uint32_t hfs::kHFSRootFolderID = 2
constexpr

Definition at line 88 of file hfs_format.hpp.

Referenced by HfsRecovery::recover().

◆ kHFSRootParentID

uint32_t hfs::kHFSRootParentID = 1
constexpr

Definition at line 87 of file hfs_format.hpp.

◆ kHFSStartupFileID

uint32_t hfs::kHFSStartupFileID = 7
constexpr

Definition at line 93 of file hfs_format.hpp.

◆ kHFSThreadExistsBit

uint16_t hfs::kHFSThreadExistsBit = 0x0002
constexpr

Definition at line 102 of file hfs_catalog.hpp.

◆ kHFSVolumeJournaledBit

uint32_t hfs::kHFSVolumeJournaledBit = (1u << 13)
constexpr

Definition at line 83 of file hfs_format.hpp.

Referenced by hfs::VolumeHeader::is_journaled().

◆ kHFSVolumeSoftwareLockBit

uint32_t hfs::kHFSVolumeSoftwareLockBit = (1u << 15)
constexpr

Definition at line 84 of file hfs_format.hpp.

◆ kHFSVolumeUnmountedBit

uint32_t hfs::kHFSVolumeUnmountedBit = (1u << 8)
constexpr

Definition at line 82 of file hfs_format.hpp.

◆ kReservedBTreeType

uint8_t hfs::kReservedBTreeType = 0x00
constexpr

Definition at line 44 of file hfs_btree.hpp.

◆ kSymLinkCreator

uint32_t hfs::kSymLinkCreator = 0x72686170
constexpr

Definition at line 53 of file hfs_catalog.hpp.

Referenced by hfs::CatalogFile::is_symlink(), and HfsRecovery::recover().

◆ kSymLinkFileType

uint32_t hfs::kSymLinkFileType = 0x736C6E6B
constexpr

Definition at line 52 of file hfs_catalog.hpp.

Referenced by hfs::CatalogFile::is_symlink(), and HfsRecovery::recover().

◆ kUserBTreeType

uint8_t hfs::kUserBTreeType = 0x80
constexpr

Definition at line 43 of file hfs_btree.hpp.

◆ UF_COMPRESSED

uint8_t hfs::UF_COMPRESSED = 0x20
constexpr

Definition at line 112 of file hfs_catalog.hpp.

Referenced by HfsRecovery::recover().