1 #pragma once 2 3 #include "../base/tsk_base.h" 4 #include "../img/tsk_img.h" 5 #include "../pool/tsk_apfs.hpp" 6 #include "../util/lw_shared_ptr.hpp" 7 #include "../util/span.hpp" 8 9 #include "tsk_apfs.h" 10 11 #include <algorithm> 12 #include <array> 13 #include <memory> 14 #include <mutex> 15 #include <new> 16 #include <stack> 17 #include <stdexcept> 18 #include <type_traits> 19 #include <vector> 20 21 #include "../auto/guid.h" 22 23 // Helper function to see if a bitfield flag is set 24 template <typename T, typename U, 25 typename = std::enable_if_t<std::numeric_limits<T>::is_integer && 26 std::numeric_limits<U>::is_integer>> 27 constexpr bool bit_is_set(T bitfield, U bitmask) noexcept { 28 return ((bitfield & static_cast<T>(bitmask)) != 0); 29 } 30 31 // Helper function to extract bitfield value 32 template <typename T, 33 typename = std::enable_if_t<std::numeric_limits<T>::is_integer>> 34 constexpr T bitfield_value(T bitfield, int bits, int shift) noexcept { 35 return (bitfield >> shift) & ((T{1} << bits) - 1); 36 } 37 38 class APFSPool; 39 40 class APFSObject : public APFSBlock { 41 protected: 42 inline const apfs_obj_header *obj() const noexcept { 43 return reinterpret_cast<const apfs_obj_header *>(_storage.data()); 44 } 45 46 public: 47 // Use the constructors from APFSBlock 48 using APFSBlock::APFSBlock; 49 50 bool validate_checksum() const noexcept; 51 52 inline APFS_OBJ_TYPE_ENUM obj_type() const noexcept { 53 return APFS_OBJ_TYPE_ENUM(obj()->type); 54 } 55 56 inline uint32_t obj_type_and_flags() const noexcept { 57 return obj()->type_and_flags; 58 } 59 60 inline uint64_t oid() const noexcept { return obj()->oid; } 61 62 inline uint64_t xid() const noexcept { return obj()->xid; } 63 64 inline uint32_t subtype() const noexcept { return obj()->subtype; } 65 }; 66 67 class APFSOmap : public APFSObject { 68 protected: 69 inline const apfs_omap *omap() const noexcept { 70 return reinterpret_cast<const apfs_omap *>(_storage.data()); 71 } 72 73 public: 74 // Use constructors from APFSObject 75 using APFSObject::APFSObject; 76 77 APFSOmap(const APFSPool &pool, const apfs_block_num block_num); 78 79 inline uint32_t snapshot_count() const noexcept { 80 return omap()->snapshot_count; 81 } 82 83 inline APFS_OMAP_TREE_TYPE_ENUM tree_type() const noexcept { 84 return APFS_OMAP_TREE_TYPE_ENUM(omap()->tree_type); 85 } 86 87 inline apfs_block_num root_block() const noexcept { return omap()->tree_oid; } 88 89 struct node_tag {}; ///< Tag used to identify OMAP nodes 90 91 template <typename T, 92 typename = std::enable_if_t<std::is_base_of<node_tag, T>::value>> 93 T root() const { 94 return {_pool, root_block()}; 95 } 96 }; 97 98 class APFSJObjBtreeNode; 99 100 template <typename Node> 101 class APFSBtreeNodeIterator { 102 public: 103 using iterator_category = std::forward_iterator_tag; 104 using difference_type = uint32_t; 105 using value_type = struct { 106 typename Node::key_type key; 107 typename Node::value_type value; 108 }; 109 using reference = const value_type &; 110 using pointer = const value_type *; 111 112 protected: 113 lw_shared_ptr<Node> _node{}; 114 uint32_t _index{0}; 115 116 // Leaf nodes will have values and non-leaf nodes will have iterators 117 // to the child node. 118 // 119 // TODO(JTS): If we ever switch to c++17 then we can use a std::variant 120 std::unique_ptr<typename Node::iterator> _child_it{}; 121 value_type _val{}; 122 123 inline lw_shared_ptr<Node> own_node(const Node *node) { 124 return own_node(node, node->block_num()); 125 } 126 127 inline lw_shared_ptr<Node> own_node(const Node *node, 128 apfs_block_num block_num) { 129 return node->_pool.template get_block<Node>( 130 block_num, node->_pool, block_num, node->_decryption_key); 131 } 132 133 template <typename Void = void> 134 auto init_value() 135 -> std::enable_if_t<Node::is_variable_kv_node::value, Void> { 136 if (this->_node->has_fixed_kv_size()) { 137 throw std::runtime_error("btree does not have variable sized keys"); 138 } 139 const auto &t = _node->_table_data.toc.variable[_index]; 140 const auto key_data = _node->_table_data.koff + t.key_offset; 141 const auto val_data = _node->_table_data.voff - t.val_offset; 142 143 memory_view key{key_data, t.key_length}; 144 145 if (_node->is_leaf()) { 146 memory_view value{val_data, t.val_length}; 147 148 _val = {key, value}; 149 } else { 150 const auto block_num = *((apfs_block_num *)val_data); 151 152 _child_it = std::make_unique<typename Node::iterator>( 153 own_node(_node.get(), block_num), 0); 154 } 155 } 156 157 template <typename Void = void> 158 auto init_value() -> std::enable_if_t<Node::is_fixed_kv_node::value, Void> { 159 if (!this->_node->has_fixed_kv_size()) { 160 throw std::runtime_error("btree does not have fixed sized keys"); 161 } 162 const auto &t = _node->_table_data.toc.fixed[_index]; 163 const auto key_data = _node->_table_data.koff + t.key_offset; 164 const auto val_data = _node->_table_data.voff - t.val_offset; 165 166 if (_node->is_leaf()) { 167 _val = {(typename Node::key_type)key_data, 168 (typename Node::value_type)val_data}; 169 } else { 170 const auto block_num = *((apfs_block_num *)val_data); 171 172 _child_it = std::make_unique<typename Node::iterator>( 173 own_node(_node.get(), block_num), 0); 174 } 175 } 176 177 public: 178 // Forward iterators must be DefaultConstructible 179 APFSBtreeNodeIterator() = default; 180 181 APFSBtreeNodeIterator(const Node *node, uint32_t index); 182 183 APFSBtreeNodeIterator(lw_shared_ptr<Node> &&node, uint32_t index); 184 185 APFSBtreeNodeIterator(const Node *node, uint32_t index, 186 typename Node::iterator &&child); 187 188 virtual ~APFSBtreeNodeIterator() = default; 189 190 APFSBtreeNodeIterator(const APFSBtreeNodeIterator &rhs) noexcept 191 : _node{rhs._node}, _index{rhs._index} { 192 if (_node->is_leaf()) { 193 _val = rhs._val; 194 } else if (rhs._child_it != nullptr) { 195 _child_it = std::make_unique<typename Node::iterator>(*rhs._child_it); 196 } 197 } 198 199 APFSBtreeNodeIterator &operator=(const APFSBtreeNodeIterator &rhs) noexcept { 200 if (this != &rhs) { 201 this->~APFSBtreeNodeIterator(); 202 new (this) APFSBtreeNodeIterator(rhs); 203 } 204 205 return (*this); 206 }; 207 208 APFSBtreeNodeIterator(APFSBtreeNodeIterator &&rhs) noexcept 209 : _node{std::move(rhs._node)}, _index{std::move(rhs._index)} { 210 if (_node->is_leaf()) { 211 _val = std::move(rhs._val); 212 } else { 213 _child_it = std::move(rhs._child_it); 214 } 215 }; 216 217 APFSBtreeNodeIterator &operator=(APFSBtreeNodeIterator &&rhs) noexcept { 218 if (this != &rhs) { 219 this->~APFSBtreeNodeIterator(); 220 new (this) 221 APFSBtreeNodeIterator(std::forward<APFSBtreeNodeIterator>(rhs)); 222 } 223 224 return (*this); 225 } 226 227 bool is_valid() const noexcept { 228 if (_node == nullptr) { 229 return false; 230 } 231 232 return (_index < _node->key_count()); 233 } 234 235 reference operator*() const noexcept { 236 if (_index >= _node->key_count()) { 237 return _val; 238 } 239 240 // Leaf nodes return the value 241 if (_node->is_leaf()) { 242 return _val; 243 } 244 245 // Non-Leaf nodes return the pointer 246 return _child_it->operator*(); 247 } 248 249 pointer operator->() const noexcept { 250 if (_index >= _node->key_count()) { 251 return nullptr; 252 } 253 254 // Leaf nodes return the value 255 if (_node->is_leaf()) { 256 return &_val; 257 } 258 259 // Non-Leaf nodes return the pointer 260 return _child_it->operator->(); 261 } 262 263 virtual APFSBtreeNodeIterator &operator++() { 264 // If we're a leaf node then we just need to iterate the count 265 if (_node->is_leaf()) { 266 if (_index < _node->key_count()) { 267 _index++; 268 269 auto node{std::move(_node)}; 270 auto index{_index}; 271 272 this->~APFSBtreeNodeIterator(); 273 new (this) APFSBtreeNodeIterator(std::move(node), index); 274 } 275 return (*this); 276 } 277 278 _child_it->operator++(); 279 280 if (*_child_it != _child_it->_node->end()) { 281 return (*this); 282 } 283 284 _index++; 285 286 auto node{std::move(_node)}; 287 auto index{_index}; 288 289 this->~APFSBtreeNodeIterator(); 290 new (this) APFSBtreeNodeIterator(std::move(node), index); 291 292 return (*this); 293 } 294 295 APFSBtreeNodeIterator operator++(int) { 296 APFSBtreeNodeIterator it{(*this)}; 297 298 this->operator++(); 299 300 return it; 301 } 302 303 bool operator==(const APFSBtreeNodeIterator &rhs) const noexcept { 304 // Self check 305 if (this == &rhs) { 306 return true; 307 } 308 309 // If only one of the nodes is nullptr then we're not a match, but if they 310 // both are then we are a match 311 if (_node == nullptr || rhs._node == nullptr) { 312 return (_node == rhs._node); 313 } 314 315 // Ensure we have equivalent nodes and indexes 316 if (*_node != *rhs._node || _index != rhs._index) { 317 return false; 318 } 319 320 // If we're leaves then we're good. 321 if (_node->is_leaf()) { 322 return true; 323 } 324 325 // Otherwise, let's compare the child iterators. 326 return (*_child_it == *rhs._child_it); 327 } 328 329 bool operator!=(const APFSBtreeNodeIterator &rhs) const noexcept { 330 return !this->operator==(rhs); 331 } 332 333 friend Node; 334 friend APFSJObjBtreeNode; 335 }; 336 337 template <typename Key = memory_view, typename Value = memory_view> 338 class APFSBtreeNode : public APFSObject, public APFSOmap::node_tag { 339 using is_variable_kv_node = std::is_same<APFSBtreeNode, APFSBtreeNode<>>; 340 using is_fixed_kv_node = 341 std::integral_constant<bool, !is_variable_kv_node::value>; 342 343 using key_type = 344 std::conditional_t<is_variable_kv_node::value, Key, const Key *>; 345 using value_type = 346 std::conditional_t<is_variable_kv_node::value, Value, const Value *>; 347 ; 348 349 protected: 350 struct { 351 union { 352 void *v; 353 apfs_btentry_fixed *fixed; 354 apfs_btentry_variable *variable; 355 } toc; 356 char *voff; 357 char *koff; 358 } _table_data; 359 360 const uint8_t *_decryption_key{}; 361 362 inline const apfs_btree_node *bn() const noexcept { 363 return reinterpret_cast<const apfs_btree_node *>(_storage.data()); 364 } 365 366 inline ptrdiff_t toffset() const noexcept { 367 // The table space offset is relative to the end of the header 368 return sizeof(apfs_btree_node) + bn()->table_space_offset; 369 } 370 371 inline ptrdiff_t koffset() const noexcept { 372 // The keys table is immediately after the table space. 373 return toffset() + bn()->table_space_length; 374 } 375 376 inline ptrdiff_t voffset() const noexcept { 377 // The value table is a negative index relative to the end of the block 378 // unless the node is a root node then it's relative to the footer 379 ptrdiff_t off = _pool.block_size(); 380 381 if (is_root()) { 382 off -= sizeof(apfs_btree_info); 383 } 384 385 return off; 386 } 387 388 template <typename KeyType = key_type> 389 inline auto key(uint32_t index) const 390 -> std::enable_if_t<is_variable_kv_node::value, KeyType> { 391 const auto &t = _table_data.toc.variable[index]; 392 const auto key_data = _table_data.koff + t.key_offset; 393 394 return {key_data, t.key_length}; 395 } 396 397 template <typename KeyType = key_type> 398 inline auto key(uint32_t index) const 399 -> std::enable_if_t<is_fixed_kv_node::value, KeyType> { 400 const auto &t = _table_data.toc.fixed[index]; 401 const auto key_data = _table_data.koff + t.key_offset; 402 403 return reinterpret_cast<KeyType>(key_data); 404 } 405 406 template <typename Compare> 407 inline uint32_t contains_key(const key_type &key, Compare comp) const { 408 for (auto i = 0U; i < key_count(); i++) { 409 const auto k = this->key(i); 410 if (comp(k, key) > 0) { 411 if (i == 0) { 412 break; 413 } 414 415 return i - 1; 416 } 417 } 418 419 return key_count(); 420 } 421 422 public: 423 APFSBtreeNode(const APFSPool &pool, const apfs_block_num block_num, 424 const uint8_t *key = nullptr) 425 : APFSObject(pool, block_num), _decryption_key{key} { 426 // Decrypt node if needed 427 if (key != nullptr) { 428 decrypt(key); 429 } 430 431 if (obj_type() != APFS_OBJ_TYPE_BTREE_NODE && 432 obj_type() != APFS_OBJ_TYPE_BTREE_ROOTNODE) { 433 throw std::runtime_error("APFSBtreeNode: invalid object type"); 434 } 435 436 _table_data.toc = {_storage.data() + toffset()}; 437 _table_data.voff = _storage.data() + voffset(); 438 _table_data.koff = _storage.data() + koffset(); 439 } 440 441 inline bool is_root() const noexcept { 442 return bit_is_set(bn()->flags, APFS_BTNODE_ROOT); 443 } 444 445 inline bool is_leaf() const noexcept { 446 return bit_is_set(bn()->flags, APFS_BTNODE_LEAF); 447 } 448 449 inline bool has_fixed_kv_size() const noexcept { 450 return bit_is_set(bn()->flags, APFS_BTNODE_FIXED_KV_SIZE); 451 } 452 453 inline uint16_t level() const noexcept { return bn()->level; } 454 455 inline uint32_t key_count() const noexcept { return bn()->key_count; } 456 457 inline auto entries() const { 458 const auto vec = [&] { 459 std::vector<typename iterator::value_type> v{}; 460 461 std::for_each(begin(), end(), [&v](const auto e) { v.push_back(e); }); 462 463 return v; 464 }(); 465 466 return vec; 467 } 468 469 inline const apfs_btree_info *info() const noexcept { 470 // Only root nodes contain the info struct 471 if (!is_root()) { 472 return nullptr; 473 } 474 475 // The info structure is at the end of the object 476 const auto ptr = 477 _storage.data() + _storage.size() - sizeof(apfs_btree_info); 478 479 return reinterpret_cast<const apfs_btree_info *>(ptr); 480 } 481 482 // Iterators 483 484 public: 485 using iterator = APFSBtreeNodeIterator<APFSBtreeNode>; 486 487 iterator begin() const { return {this, 0}; } 488 iterator end() const { return {this, key_count()}; } 489 490 template <typename T, typename Compare> 491 iterator find(const T &value, Compare comp) const { 492 // TODO(JTS): It turns out, when a disk has snapshots, there can be more 493 // than one entry in the objects tree that corresponds to the same oid. 494 // Since we do not currently support snapshots, we're always returning the 495 // last object with the id, because that should always be the newest object. 496 // When we support snapshots, this logic likely needs to change. 497 498 // For leaf nodes we can just search the entries directly 499 if (is_leaf()) { 500 // Search for key that's equal to the value 501 for (auto i = key_count(); i > 0; i--) { 502 const auto &k = key(i - 1); 503 504 const auto res = comp(k, value); 505 506 if (res == 0) { 507 // We've found it! 508 return {this, i - 1}; 509 } 510 511 if (res < 0) { 512 // We've gone too far 513 break; 514 } 515 } 516 517 // Not found 518 return end(); 519 } 520 521 // For non-leaf nodes we can be more efficient by skipping searches of 522 // sub-trees that don't contain the object 523 524 // Search for the last key that's <= the value 525 for (auto i = key_count(); i > 0; i--) { 526 const auto &k = key(i - 1); 527 528 if (comp(k, value) <= 0) { 529 iterator it{this, i - 1}; 530 531 auto ret = it._child_it->_node->find(value, comp); 532 if (ret == it._child_it->_node->end()) { 533 return end(); 534 } 535 536 return {this, i - 1, std::move(ret)}; 537 } 538 } 539 540 // Not Found 541 return end(); 542 } 543 544 friend iterator; 545 546 template <typename T> 547 friend class APFSBtreeNodeIterator; 548 }; 549 550 class APFSObjectBtreeNode 551 : public APFSBtreeNode<apfs_omap_key, apfs_omap_value> { 552 uint64_t _xid; 553 554 public: 555 APFSObjectBtreeNode(const APFSPool &pool, apfs_block_num block_num); 556 APFSObjectBtreeNode(const APFSPool &pool, apfs_block_num block_num, 557 uint64_t snap_xid); 558 559 iterator find(uint64_t oid) const; 560 561 inline void snapshot(uint64_t snap_xid) { _xid = snap_xid; } 562 }; 563 564 class APFSSnapshotMetaBtreeNode : public APFSBtreeNode<> { 565 public: 566 APFSSnapshotMetaBtreeNode(const APFSPool &pool, apfs_block_num block_num); 567 }; 568 569 class APFSJObjBtreeNode : public APFSBtreeNode<> { 570 const APFSObjectBtreeNode *_obj_root; 571 572 public: 573 APFSJObjBtreeNode(const APFSObjectBtreeNode *obj_root, 574 apfs_block_num block_num, const uint8_t *key); 575 576 577 APFSJObjBtreeNode(APFSJObjBtreeNode &&) = default; 578 579 using iterator = APFSBtreeNodeIterator<APFSJObjBtreeNode>; 580 581 inline bool is_leaf() const noexcept { return (bn()->level == 0); } 582 583 inline iterator begin() const { return {this, 0}; } 584 inline iterator end() const { return {this, key_count()}; } 585 586 template <typename T, typename Compare> 587 inline iterator find(const T &value, Compare comp) const { 588 // For leaf nodes we can just search the entries directly 589 if (is_leaf()) { 590 // Search for key that's equal to the value 591 for (auto i = 0U; i < key_count(); i++) { 592 const auto &k = key(i); 593 594 const auto res = comp(k, value); 595 596 if (res == 0) { 597 // We've found it! 598 return {this, i}; 599 } 600 601 if (res > 0) { 602 // We've gone too far 603 break; 604 } 605 } 606 607 // Not found 608 return end(); 609 } 610 611 // For non-leaf nodes we can be more efficient by skipping searches of 612 // sub-trees that don't contain the object 613 614 uint32_t last = std::numeric_limits<uint32_t>::max(); 615 // Search for key that's <= the value 616 for (auto i = 0U; i < key_count(); i++) { 617 const auto &k = key(i); 618 619 const auto v = comp(k, value); 620 621 if (v > 0) { 622 break; 623 } 624 625 last = i; 626 627 if (v == 0) { 628 // We need to see if the jobj might be in the last node 629 if (last != 0) { 630 iterator it{this, last - 1}; 631 632 auto ret = it._child_it->_node->find(value, comp); 633 if (ret != it._child_it->_node->end()) { 634 return {this, last - 1, std::move(ret)}; 635 } 636 } 637 638 break; 639 } 640 } 641 642 if (last == std::numeric_limits<uint32_t>::max()) { 643 // Not Found 644 return end(); 645 } 646 647 iterator it{this, last}; 648 649 auto ret = it._child_it->_node->find(value, comp); 650 if (ret == it._child_it->_node->end()) { 651 return end(); 652 } 653 654 return {this, last, std::move(ret)}; 655 } 656 657 template <typename T, typename Compare> 658 inline std::pair<iterator, iterator> find_range(const T &value, 659 Compare comp) const { 660 auto s = find(value, comp); 661 662 if (s == end()) { 663 // Not found 664 return {end(), end()}; 665 } 666 667 auto e = std::find_if( 668 s, end(), [&](const auto &a) noexcept(noexcept(comp(a.key, value))) { 669 return comp(a.key, value) != 0; 670 }); 671 672 return std::make_pair(std::move(s), std::move(e)); 673 } 674 675 friend iterator; 676 }; 677 678 class APFSSpacemanCIB : public APFSObject { 679 protected: 680 inline const apfs_spaceman_cib *cib() const noexcept { 681 return reinterpret_cast<const apfs_spaceman_cib *>(_storage.data()); 682 } 683 684 public: 685 using APFSObject::APFSObject; 686 APFSSpacemanCIB(const APFSPool &pool, const apfs_block_num block_num); 687 688 using bm_entry = struct { 689 uint64_t offset; 690 uint32_t total_blocks; 691 uint32_t free_blocks; 692 apfs_block_num bm_block; 693 }; 694 695 const std::vector<bm_entry> bm_entries() const; 696 }; 697 698 class APFSSpacemanCAB : public APFSObject { 699 protected: 700 inline const apfs_spaceman_cab *cab() const noexcept { 701 return reinterpret_cast<const apfs_spaceman_cab *>(_storage.data()); 702 } 703 704 public: 705 using APFSObject::APFSObject; 706 APFSSpacemanCAB(const APFSPool &pool, const apfs_block_num block_num); 707 708 inline uint32_t index() const noexcept { return cab()->index; } 709 710 inline uint32_t cib_count() const noexcept { return cab()->cib_count; } 711 712 const std::vector<apfs_block_num> cib_blocks() const; 713 }; 714 715 class APFSSpaceman : public APFSObject { 716 mutable std::vector<APFSSpacemanCIB::bm_entry> _bm_entries{}; 717 718 #ifdef TSK_MULTITHREAD_LIB 719 mutable std::mutex _bm_entries_init_lock; 720 #endif 721 722 protected: 723 inline const apfs_spaceman *sm() const noexcept { 724 return reinterpret_cast<const apfs_spaceman *>(_storage.data()); 725 } 726 727 inline const apfs_block_num *entries() const noexcept { 728 return reinterpret_cast<const apfs_block_num *>( 729 (uintptr_t)sm() + sm()->devs[APFS_SD_MAIN].addr_offset); 730 } 731 732 public: 733 using APFSObject::APFSObject; 734 APFSSpaceman(const APFSPool &pool, const apfs_block_num block_num); 735 736 const std::vector<APFSSpacemanCIB::bm_entry> &bm_entries() const; 737 738 using range = APFSPool::range; 739 740 inline uint64_t num_free_blocks() const noexcept { 741 return sm()->devs[APFS_SD_MAIN].free_count; 742 } 743 744 const std::vector<range> unallocated_ranges() const; 745 }; 746 747 class APFSBitmapBlock : public APFSBlock { 748 enum class mode { 749 unset, 750 set, 751 }; 752 753 // A special return value for next that is returned when there are no more 754 // bits to scan. 755 static constexpr auto no_bits_left = std::numeric_limits<uint32_t>::max(); 756 757 // Number of bits in cache 758 static constexpr uint32_t cached_bits = sizeof(uintptr_t) * 8; 759 760 const APFSSpacemanCIB::bm_entry _entry; 761 uint32_t _hint{}; 762 mode _mode{mode::unset}; 763 uintptr_t _cache{}; 764 765 inline bool done() const noexcept { return (_hint >= _entry.total_blocks); } 766 767 inline void reset() noexcept { _hint = 0; } 768 769 // Find the index of the next scanned bit. If the scan mode is 770 // set to "set" then this will be a 1 bit and if the mode is 771 // "unset" then it will be a zero bit. If no more bits are found 772 // then no_bits_left is returned. 773 // 774 // Returns the index of the next scanned bit or no_bits_left 775 // 776 uint32_t next() noexcept; 777 778 // Cache the next set of bits from the buffer. 779 inline void cache_next() noexcept { 780 // 781 // Interpret the buffer as an array of 32-bit ints. 782 // 783 const auto array = reinterpret_cast<uintptr_t *>(_storage.data()); 784 785 // 786 // Fetch the next integer to the cache. 787 // 788 _cache = array[_hint / cached_bits]; 789 790 // 791 // If we're scanning for unset bits then we need to invert the cached 792 // bits, since we only actually have logic for searching for set bits. 793 // 794 if (_mode == mode::unset) { 795 _cache = ~_cache; 796 } 797 } 798 799 // 800 // Toggles the scan mode from set to unset or vice-versa. 801 // 802 // Returns the new scan mode 803 // 804 inline void toggle_mode() noexcept { 805 // Toggle the scan mode based on the current mode. 806 if (_mode == mode::set) { 807 _mode = mode::unset; 808 } else { 809 _mode = mode::set; 810 } 811 812 // Invert the cached bits 813 _cache = ~_cache; 814 } 815 816 public: 817 using APFSBlock::APFSBlock; 818 819 APFSBitmapBlock(const APFSPool &pool, const APFSSpacemanCIB::bm_entry &entry); 820 821 const std::vector<APFSSpaceman::range> unallocated_ranges(); 822 }; 823 824 class APFSKeybag : public APFSObject { 825 protected: 826 inline const apfs_keybag *kb() const noexcept { 827 return reinterpret_cast<const apfs_keybag *>(_storage.data()); 828 } 829 830 using key = struct { 831 Guid uuid; 832 std::unique_ptr<uint8_t[]> data; 833 uint16_t type; 834 }; 835 836 public: 837 APFSKeybag(const APFSPool &pool, const apfs_block_num block_num, 838 const uint8_t *key, const uint8_t *key2 = nullptr); 839 840 std::unique_ptr<uint8_t[]> get_key(const Guid &uuid, uint16_t type) const; 841 842 std::vector<key> get_keys() const; 843 }; 844 845 class APFSSuperblock : public APFSObject { 846 mutable std::unique_ptr<APFSSpaceman> _spaceman{}; 847 848 #ifdef TSK_MULTITHREAD_LIB 849 mutable std::mutex _spaceman_init_lock; 850 #endif 851 852 protected: 853 inline const apfs_nx_superblock *sb() const noexcept { 854 return reinterpret_cast<const apfs_nx_superblock *>(_storage.data()); 855 } 856 857 inline APFSOmap omap() const { return {_pool, sb()->omap_oid}; }; 858 859 const APFSSpaceman &spaceman() const; 860 861 class Keybag : public APFSKeybag { 862 public: 863 Keybag(const APFSSuperblock &sb); 864 }; 865 866 public: 867 using APFSObject::APFSObject; 868 869 APFSSuperblock(const APFSPool &pool, const apfs_block_num block_num); 870 871 inline uint32_t block_size() const noexcept { return sb()->block_size; } 872 873 inline uint64_t num_blocks() const noexcept { return sb()->block_count; } 874 875 inline uint64_t num_free_blocks() const { 876 return spaceman().num_free_blocks(); 877 } 878 879 inline Guid uuid() const { return {sb()->uuid}; } 880 881 const std::vector<apfs_block_num> volume_blocks() const; 882 const std::vector<apfs_block_num> sm_bitmap_blocks() const; 883 inline const std::vector<APFSSpaceman::range> unallocated_ranges() const { 884 return spaceman().unallocated_ranges(); 885 } 886 887 const std::vector<uint64_t> volume_oids() const; 888 889 apfs_block_num checkpoint_desc_block() const; 890 891 Keybag keybag() const; 892 893 friend APFSPool; 894 }; 895 896 class APFSCheckpointMap : public APFSObject { 897 protected: 898 inline const apfs_checkpoint_map *map() const noexcept { 899 return reinterpret_cast<const apfs_checkpoint_map *>(_storage.data()); 900 } 901 902 public: 903 using APFSObject::APFSObject; 904 APFSCheckpointMap(const APFSPool &pool, const apfs_block_num block_num); 905 906 apfs_block_num get_object_block(uint64_t oid, APFS_OBJ_TYPE_ENUM type) const; 907 }; 908 909 // Object representation of an APFS Physical Extent Reference 910 #pragma pack(push, 1) 911 struct APFSPhysicalExtentRef : apfs_phys_extent { 912 inline apfs_phys_extent_kind kind() const noexcept { 913 return static_cast<apfs_phys_extent_kind>(bitfield_value( 914 len_and_kind, APFS_PHYS_EXTENT_KIND_BITS, APFS_PHYS_EXTENT_KIND_SHIFT)); 915 } 916 917 inline uint64_t block_count() const noexcept { 918 return bitfield_value(len_and_kind, APFS_PHYS_EXTENT_LEN_BITS, 919 APFS_PHYS_EXTENT_LEN_SHIFT); 920 } 921 922 inline uint64_t owner_oid() const noexcept { return owning_obj_id; } 923 924 inline uint32_t ref_count() const noexcept { return refcnt; } 925 }; 926 static_assert(sizeof(APFSPhysicalExtentRef) == sizeof(apfs_phys_extent), 927 "No member fields can be added to APFSPhysicalExtentRef"); 928 929 struct APFSPhysicalExtentKey : apfs_phys_extent_key { 930 inline apfs_block_num start_block() const noexcept { 931 return bitfield_value(start_block_and_type, 932 APFS_PHYS_EXTENT_START_BLOCK_BITS, 933 APFS_PHYS_EXTENT_START_BLOCK_SHIFT); 934 } 935 }; 936 static_assert(sizeof(APFSPhysicalExtentKey) == sizeof(apfs_phys_extent_key), 937 "No member fields can be added to APFSPhysicalExtentKey"); 938 #pragma pack(pop) 939 940 class APFSExtentRefBtreeNode : public APFSBtreeNode<> { 941 public: 942 APFSExtentRefBtreeNode(const APFSPool &pool, apfs_block_num block_num); 943 944 iterator find(apfs_block_num) const; 945 }; 946 947 class APFSJObjTree; 948 class APFSFileSystem : public APFSObject { 949 public: 950 using unmount_log_t = struct { 951 uint64_t timestamp; 952 std::string logstr; 953 uint64_t last_xid; 954 }; 955 956 using snapshot_t = struct { 957 std::string name; 958 uint64_t timestamp; 959 uint64_t snap_xid; 960 bool dataless; 961 }; 962 963 struct wrapped_kek { 964 Guid uuid; 965 uint8_t data[0x28]; 966 uint64_t iterations; 967 uint64_t flags; 968 uint8_t salt[0x10]; 969 wrapped_kek(Guid &&uuid, const std::unique_ptr<uint8_t[]> &); 970 971 inline bool hw_crypt() const noexcept { 972 // If this bit is set, some sort of hardware encryption is used. 973 return bit_is_set(flags, 1ULL << 56); 974 } 975 976 inline bool cs() const noexcept { 977 // If this bit is set the KEK is 0x10 bytes instead of 0x20 978 return bit_is_set(flags, 1ULL << 57); 979 } 980 }; 981 982 struct crypto_info_t { 983 apfs_block_num recs_block_num{}; 984 std::string password_hint{}; 985 std::string password{}; 986 std::vector<wrapped_kek> wrapped_keks{}; 987 uint64_t vek_flags{}; 988 uint8_t wrapped_vek[0x28]{}; 989 uint8_t vek_uuid[0x10]{}; 990 uint8_t vek[0x20]{}; 991 bool unlocked{}; 992 993 inline uint64_t unk16() const noexcept { 994 // If this byte is not zero (1) then some other sort of decryption is used 995 return bitfield_value(vek_flags, 8, 16); 996 } 997 998 inline bool hw_crypt() const noexcept { 999 // If this bit is set, some sort of hardware encryption is used. 1000 return bit_is_set(vek_flags, 1ULL << 56); 1001 } 1002 1003 inline bool cs() const noexcept { 1004 // If this bit is set the VEK is 0x10 bytes instead of 0x20 1005 return bit_is_set(vek_flags, 1ULL << 57); 1006 } 1007 }; 1008 1009 protected: 1010 class Keybag : public APFSKeybag { 1011 public: 1012 Keybag(const APFSFileSystem &, apfs_block_num); 1013 }; 1014 1015 inline const apfs_superblock *fs() const noexcept { 1016 return reinterpret_cast<const apfs_superblock *>(_storage.data()); 1017 } 1018 1019 inline uint64_t rdo() const noexcept { return fs()->root_tree_oid; } 1020 1021 void init_crypto_info(); 1022 1023 crypto_info_t _crypto{}; 1024 1025 public: 1026 using APFSObject::APFSObject; 1027 APFSFileSystem(const APFSPool &pool, const apfs_block_num block_num); 1028 APFSFileSystem(const APFSPool &pool, const apfs_block_num block_num, 1029 const std::string &password); 1030 1031 const std::vector<snapshot_t> snapshots() const; 1032 1033 bool unlock(const std::string &password) noexcept; 1034 1035 inline Guid uuid() const noexcept { return {fs()->uuid}; } 1036 1037 inline std::string name() const { return {fs()->name}; } 1038 1039 inline std::string formatted_by() const { return {fs()->formatted_by}; } 1040 1041 inline const std::string &password_hint() const noexcept { 1042 return _crypto.password_hint; 1043 } 1044 1045 inline const auto &crypto_info() const noexcept { return _crypto; } 1046 1047 inline const uint8_t *decryption_key() const noexcept { 1048 if (_crypto.unlocked) { 1049 return _crypto.vek; 1050 } 1051 1052 return nullptr; 1053 } 1054 1055 inline APFS_VOLUME_ROLE role() const noexcept { 1056 return APFS_VOLUME_ROLE(fs()->role); 1057 } 1058 1059 inline uint64_t reserved() const noexcept { 1060 return fs()->reserve_blocks * _pool.block_size(); 1061 } 1062 1063 inline uint64_t quota() const noexcept { 1064 return fs()->quota_blocks * _pool.block_size(); 1065 } 1066 1067 inline uint64_t used() const noexcept { 1068 return fs()->alloc_blocks * _pool.block_size(); 1069 } 1070 1071 inline uint64_t reserved_blocks() const noexcept { 1072 return fs()->reserve_blocks; 1073 } 1074 1075 inline uint64_t quota_blocks() const noexcept { return fs()->quota_blocks; } 1076 1077 inline uint64_t alloc_blocks() const noexcept { return fs()->alloc_blocks; } 1078 1079 inline uint64_t last_inum() const noexcept { return fs()->next_inum - 1; } 1080 1081 inline bool encrypted() const noexcept { 1082 return !bit_is_set(fs()->flags, APFS_SB_UNENCRYPTED); 1083 } 1084 1085 inline bool case_sensitive() const noexcept { 1086 return !bit_is_set(fs()->incompatible_features, 1087 APFS_SB_INCOMPAT_CASE_INSENSITIVE); 1088 } 1089 1090 inline uint64_t created() const noexcept { return fs()->created_timestamp; } 1091 1092 inline uint64_t changed() const noexcept { return fs()->last_mod_time; } 1093 1094 const std::vector<unmount_log_t> unmount_log() const; 1095 1096 apfs_block_num omap_root() const; 1097 1098 APFSJObjTree root_jobj_tree() const; 1099 1100 APFSExtentRefBtreeNode extent_ref_tree() const { 1101 return {pool(), fs()->extentref_tree_oid}; 1102 } 1103 1104 APFSSnapshotMetaBtreeNode snap_meta_tree() const { 1105 return {pool(), fs()->snap_meta_tree_oid}; 1106 } 1107 1108 friend APFSJObjTree; 1109 }; 1110 1111 struct APFSJObjKey { 1112 uint64_t oid_and_type; 1113 1114 inline uint64_t oid() const noexcept { 1115 return bitfield_value(oid_and_type, 60, 0); 1116 } 1117 1118 inline uint64_t type() const noexcept { 1119 return bitfield_value(oid_and_type, 4, 60); 1120 } 1121 }; 1122 static_assert(sizeof(APFSJObjKey) == 0x08, "invalid struct padding"); 1123 1124 // Template Specializations 1125 1126 // Initializes the value for variable-sized key/values 1127 1128 template <> 1129 inline lw_shared_ptr<APFSJObjBtreeNode> 1130 APFSBtreeNodeIterator<APFSJObjBtreeNode>::own_node( 1131 const APFSJObjBtreeNode *node, apfs_block_num block_num) { 1132 return node->_pool.template get_block<APFSJObjBtreeNode>( 1133 block_num, node->_obj_root, block_num, node->_decryption_key); 1134 } 1135 1136 template <> 1137 template <> 1138 inline void APFSBtreeNodeIterator<APFSJObjBtreeNode>::init_value<void>() { 1139 const auto &t = _node->_table_data.toc.variable[_index]; 1140 const auto key_data = _node->_table_data.koff + t.key_offset; 1141 const auto val_data = _node->_table_data.voff - t.val_offset; 1142 1143 memory_view key{key_data, t.key_length}; 1144 1145 if (_node->is_leaf()) { 1146 memory_view value{val_data, t.val_length}; 1147 1148 _val = {key, value}; 1149 } else { 1150 const auto obj_num = *((uint64_t *)val_data); 1151 1152 const auto it = _node->_obj_root->find(obj_num); 1153 1154 if (it == _node->_obj_root->end()) { 1155 throw std::runtime_error("can not find jobj"); 1156 } 1157 1158 _child_it = std::make_unique<typename APFSJObjBtreeNode::iterator>( 1159 own_node(_node.get(), it->value->paddr), 0); 1160 } 1161 } 1162 1163 template <typename Node> 1164 APFSBtreeNodeIterator<Node>::APFSBtreeNodeIterator(const Node *node, 1165 uint32_t index) 1166 : _node{own_node(node)}, _index{index} { 1167 // If we're the end, then there's nothing to do 1168 if (index >= _node->key_count()) { 1169 return; 1170 } 1171 1172 init_value(); 1173 } 1174 1175 template <typename Node> 1176 APFSBtreeNodeIterator<Node>::APFSBtreeNodeIterator(lw_shared_ptr<Node> &&node, 1177 uint32_t index) 1178 : _node{std::forward<lw_shared_ptr<Node>>(node)}, _index{index} { 1179 // If we're the end, then there's nothing to do 1180 if (index >= _node->key_count()) { 1181 return; 1182 } 1183 1184 init_value(); 1185 } 1186 1187 template <typename Node> 1188 APFSBtreeNodeIterator<Node>::APFSBtreeNodeIterator( 1189 const Node *node, uint32_t index, typename Node::iterator &&child) 1190 : _node{own_node(node)}, _index{index} { 1191 _child_it = std::make_unique<typename Node::iterator>( 1192 std::forward<typename Node::iterator>(child)); 1193 } 1194