/src/sleuthkit/tsk/pool/apfs_pool.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * The Sleuth Kit |
3 | | * |
4 | | * Brian Carrier [carrier <at> sleuthkit [dot] org] |
5 | | * Copyright (c) 2019-2020 Brian Carrier. All Rights reserved |
6 | | * Copyright (c) 2018-2019 BlackBag Technologies. All Rights reserved |
7 | | * |
8 | | * This software is distributed under the Common Public License 1.0 |
9 | | */ |
10 | | #include "tsk_apfs.hpp" |
11 | | |
12 | | #include "tsk/libtsk.h" |
13 | | |
14 | | #include "tsk/fs/tsk_apfs.hpp" |
15 | | #include "tsk/fs/tsk_fs_i.h" |
16 | | #include "tsk/img/legacy_cache.h" |
17 | | |
18 | | #include <stdexcept> |
19 | | |
20 | | APFSPool::APFSPool(std::vector<img_t>&& imgs, apfs_block_num nx_block_num) |
21 | 46 | : TSKPool(std::forward<std::vector<img_t>>(imgs)), |
22 | 46 | _nx_block_num{nx_block_num} { |
23 | 46 | if (_members.size() != 1) { |
24 | 0 | throw std::runtime_error( |
25 | 0 | "Only single physical store APFS pools are currently supported"); |
26 | 0 | } |
27 | | |
28 | | // If we're scanning for the latest NXSB then we need to start with the |
29 | | // last known good NXSB first |
30 | 46 | if (_nx_block_num == APFS_POOL_NX_BLOCK_LATEST) { |
31 | 46 | _nx_block_num = APFS_POOL_NX_BLOCK_LAST_KNOWN_GOOD; |
32 | 46 | } |
33 | | |
34 | 46 | std::tie(_img, _offset) = _members[0]; |
35 | | |
36 | 46 | auto nxsb = nx(true); |
37 | | |
38 | | // Update the base members |
39 | 46 | _uuid = nxsb->uuid(); |
40 | 46 | _block_size = nxsb->block_size(); |
41 | 46 | _dev_block_size = _img->sector_size; |
42 | 46 | _num_blocks = nxsb->num_blocks(); |
43 | | |
44 | | // Check to see if we need to scan for a newer pool |
45 | 46 | if (nx_block_num == APFS_POOL_NX_BLOCK_LATEST) { |
46 | 38 | const auto versions = known_versions(); |
47 | | |
48 | 38 | if (versions.empty()) { |
49 | 1 | _nx_block_num = APFS_POOL_NX_BLOCK_LAST_KNOWN_GOOD; |
50 | 1 | if (tsk_verbose) { |
51 | 0 | tsk_fprintf(stderr, |
52 | 0 | "APFSPool: No checkpoint superblocks found. Attempting to " |
53 | 0 | "fall back to last known good superblock\n"); |
54 | 0 | } |
55 | 37 | } else { |
56 | 37 | const auto highest = std::max_element( |
57 | 37 | versions.begin(), versions.end(), |
58 | 315 | [](const auto& a, const auto& b) { return a.xid < b.xid; }); |
59 | | |
60 | | // No need to do anything, we're already the highest version |
61 | 37 | if (highest->xid != nxsb->xid()) { |
62 | 9 | _nx_block_num = highest->nx_block_num; |
63 | | |
64 | 9 | try { |
65 | 9 | nxsb = nx(true); |
66 | 9 | } catch (const std::runtime_error&) { |
67 | | // Fallback to last known good block if the latest block is not valid |
68 | 0 | _nx_block_num = APFS_POOL_NX_BLOCK_LAST_KNOWN_GOOD; |
69 | 0 | nxsb = nx(true); |
70 | 0 | } |
71 | 9 | } |
72 | 37 | } |
73 | 38 | } |
74 | | |
75 | 46 | _vol_blocks = nxsb->volume_blocks(); |
76 | 46 | _num_vols = static_cast<int>(_vol_blocks.size()); |
77 | | |
78 | | // If the software crypto bit is not set, then either hardware crypto is used |
79 | | // or there are no volumes that are encrypted. |
80 | 46 | if (bit_is_set(nxsb->sb()->flags, APFS_NXSB_FLAGS_CRYPTO_SW) == false) { |
81 | | // We need to check each volume to determine if any of them have encryption |
82 | | // enabled. |
83 | 3 | for (const auto& volume : volumes()) { |
84 | 1 | if (volume.encrypted()) { |
85 | 0 | _hw_crypto = true; |
86 | 0 | break; |
87 | 0 | } |
88 | 1 | } |
89 | 3 | } |
90 | 46 | } |
91 | | |
92 | 93 | std::unique_ptr<APFSSuperblock> APFSPool::nx(bool validate) const { |
93 | 93 | auto nxsb = std::make_unique<APFSSuperblock>((*this), _nx_block_num); |
94 | | |
95 | 93 | if (validate && nxsb->validate_checksum() == false) { |
96 | 0 | throw std::runtime_error("NXSB object checksum failed"); |
97 | 0 | } |
98 | | |
99 | 93 | return nxsb; |
100 | 93 | } |
101 | | |
102 | 4 | std::vector<APFSFileSystem> APFSPool::volumes() const { |
103 | 4 | std::vector<APFSFileSystem> v{}; |
104 | 4 | v.reserve(_vol_blocks.size()); |
105 | | |
106 | 4 | for (const auto block : _vol_blocks) { |
107 | 3 | v.emplace_back((*this), block); |
108 | 3 | } |
109 | | |
110 | 4 | return v; |
111 | 4 | } |
112 | | |
113 | | ssize_t APFSPool::read(uint64_t address, char* buf, size_t buf_size) const |
114 | 5.62k | noexcept { |
115 | 5.62k | return tsk_img_read(_img, address + _offset, buf, buf_size); |
116 | 5.62k | } |
117 | | |
118 | 38 | const std::vector<APFSPool::nx_version> APFSPool::known_versions() const { |
119 | 38 | std::vector<nx_version> vers{}; |
120 | | |
121 | 38 | const auto nxsb = nx(); |
122 | 38 | const auto sb = nxsb->sb(); |
123 | | |
124 | 38 | for (auto addr = sb->chkpt_desc_base_addr; |
125 | 5.52k | addr < sb->chkpt_desc_base_addr + sb->chkpt_desc_block_count; addr++) { |
126 | 5.48k | APFSObject obj{(*this), addr}; |
127 | | |
128 | 5.48k | if (obj.obj_type() != APFS_OBJ_TYPE_SUPERBLOCK || |
129 | 5.48k | obj.oid() != nxsb->oid()) { |
130 | 4.38k | continue; |
131 | 4.38k | } |
132 | | |
133 | 1.10k | if (!obj.validate_checksum()) { |
134 | 202 | continue; |
135 | 202 | } |
136 | | |
137 | 898 | vers.emplace_back(nx_version{addr, obj.xid()}); |
138 | 898 | } |
139 | | |
140 | 38 | return vers; |
141 | 38 | } |
142 | | |
143 | 0 | const std::vector<APFSPool::range> APFSPool::unallocated_ranges() const { |
144 | 0 | return nx()->unallocated_ranges(); |
145 | 0 | } |
146 | | |
147 | 0 | void APFSPool::clear_cache() noexcept { |
148 | 0 | _block_cache.clear(); |
149 | |
|
150 | 0 | auto cache = static_cast<LegacyCache*>(reinterpret_cast<IMG_INFO*>(_img)->cache); |
151 | 0 | cache->lock(); |
152 | 0 | cache->clear(); |
153 | 0 | cache->unlock(); |
154 | 0 | } |