Coverage Report

Created: 2025-08-26 06:11

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