Coverage Report

Created: 2025-08-26 06:11

/src/sleuthkit/tsk/pool/tsk_apfs.hpp
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
/** \@file Public C++ API */
11
#pragma once
12
13
#include "tsk_apfs.h"
14
#include "tsk_pool.hpp"
15
16
#include <array>
17
#include <memory>
18
#include <unordered_map>
19
20
#include "tsk/fs/tsk_apfs.h"
21
#include "tsk/util/lw_shared_ptr.hpp"
22
23
class APFSSuperblock;
24
class APFSFileSystem;
25
class APFSPool;
26
27
class APFSBlock {
28
 protected:
29
  using storage = std::array<char, APFS_BLOCK_SIZE>;
30
31
  storage _storage;
32
  const APFSPool &_pool;
33
  const apfs_block_num _block_num;
34
35
  // disable default construction
36
  APFSBlock() = delete;
37
38
  // Copies are expensive here, so let's disable them
39
  APFSBlock(const APFSBlock &) = delete;
40
  APFSBlock &operator=(const APFSBlock &) = delete;
41
42
 public:
43
  APFSBlock(const APFSPool &pool, const apfs_block_num block_num);
44
45
  // Move constructible
46
0
  APFSBlock(APFSBlock &&) = default;
47
48
5.59k
  virtual ~APFSBlock() = default;
49
50
  void decrypt(const uint8_t *key, const uint8_t *key2 = nullptr) noexcept;
51
52
  void dump() const noexcept;
53
54
228
  inline bool operator==(const APFSBlock &rhs) const noexcept {
55
228
    if (this == &rhs) {
56
228
      return true;
57
228
    }
58
59
0
    return (&_pool == &rhs._pool && _block_num == rhs._block_num);
60
228
  }
61
62
228
  inline bool operator!=(const APFSBlock &rhs) const noexcept {
63
228
    return !this->operator==(rhs);
64
228
  }
65
66
17
  inline apfs_block_num block_num() const noexcept { return _block_num; }
67
4
  inline const APFSPool &pool() const noexcept { return _pool; }
68
0
  inline const char *data() const noexcept { return _storage.data(); }
69
};
70
71
class APFSPool : public TSKPool {
72
  // This should give a worst case of caching ~64 MiB of blocks
73
  static constexpr auto block_cache_size = 1024 * 16;
74
75
 protected:
76
  TSK_IMG_INFO *_img;
77
  TSK_OFF_T _offset;
78
79
  apfs_block_num _nx_block_num;
80
  std::vector<apfs_block_num> _vol_blocks;
81
82
  // TODO(JTS): make thread safe if needed. The locking in the higher-level APIs should prevent issues.
83
  mutable std::unordered_map<apfs_block_num, lw_shared_ptr<APFSBlock>>
84
      _block_cache{};
85
86
  bool _hw_crypto{};
87
88
  using nx_version = struct {
89
    apfs_block_num nx_block_num;
90
    uint64_t xid;
91
  };
92
93
 public:
94
  APFSPool(std::vector<img_t> &&imgs,
95
           apfs_block_num nx_block_num = APFS_POOL_NX_BLOCK_LAST_KNOWN_GOOD);
96
97
  // Moveable
98
  APFSPool(APFSPool &&) = default;
99
  APFSPool &operator=(APFSPool &&) = default;
100
101
  // Not copyable because of TSK_IMG_INFO pointer
102
  APFSPool(const APFSPool &) = delete;
103
  APFSPool &operator=(const APFSPool &) = delete;
104
105
  std::vector<APFSFileSystem> volumes() const;
106
107
  ssize_t read(uint64_t address, char *buf, size_t buf_size) const
108
      noexcept final;
109
110
  // This is not thread safe, but locking in the higher level APIs appears to prevent any issues.
111
  template <typename T, typename... Args>
112
  inline lw_shared_ptr<T> get_block(apfs_block_num block,
113
18
                                    Args &&... args) const {
114
18
    const auto it = _block_cache.find(block);
115
18
    if (it == _block_cache.end()) {
116
11
      if (_block_cache.size() > block_cache_size) {
117
0
        _block_cache.clear();
118
0
      }
119
11
      _block_cache[block] = make_lw_shared<T>(std::forward<Args>(args)...);
120
11
      return lw_static_pointer_cast<T>(_block_cache[block]);
121
11
    }
122
123
7
    return lw_static_pointer_cast<T>(it->second);
124
18
  }
lw_shared_ptr<APFSJObjBtreeNode> APFSPool::get_block<APFSJObjBtreeNode, APFSObjectBtreeNode const* const&, unsigned long&, unsigned char const* const&>(unsigned long, APFSObjectBtreeNode const* const&, unsigned long&, unsigned char const* const&) const
Line
Count
Source
113
4
                                    Args &&... args) const {
114
4
    const auto it = _block_cache.find(block);
115
4
    if (it == _block_cache.end()) {
116
1
      if (_block_cache.size() > block_cache_size) {
117
0
        _block_cache.clear();
118
0
      }
119
1
      _block_cache[block] = make_lw_shared<T>(std::forward<Args>(args)...);
120
1
      return lw_static_pointer_cast<T>(_block_cache[block]);
121
1
    }
122
123
3
    return lw_static_pointer_cast<T>(it->second);
124
4
  }
lw_shared_ptr<APFSBtreeNode<apfs_omap_key, apfs_omap_value> > APFSPool::get_block<APFSBtreeNode<apfs_omap_key, apfs_omap_value>, APFSPool const&, unsigned long&, unsigned char const* const&>(unsigned long, APFSPool const&, unsigned long&, unsigned char const* const&) const
Line
Count
Source
113
14
                                    Args &&... args) const {
114
14
    const auto it = _block_cache.find(block);
115
14
    if (it == _block_cache.end()) {
116
10
      if (_block_cache.size() > block_cache_size) {
117
0
        _block_cache.clear();
118
0
      }
119
10
      _block_cache[block] = make_lw_shared<T>(std::forward<Args>(args)...);
120
10
      return lw_static_pointer_cast<T>(_block_cache[block]);
121
10
    }
122
123
4
    return lw_static_pointer_cast<T>(it->second);
124
14
  }
Unexecuted instantiation: lw_shared_ptr<APFSBtreeNode<memory_view, memory_view> > APFSPool::get_block<APFSBtreeNode<memory_view, memory_view>, APFSPool const&, unsigned long&, unsigned char const* const&>(unsigned long, APFSPool const&, unsigned long&, unsigned char const* const&) const
125
126
  const std::vector<nx_version> known_versions() const;
127
128
  const std::vector<range> unallocated_ranges() const final;
129
130
  std::unique_ptr<APFSSuperblock> nx(bool validate = false) const;
131
132
0
  inline bool hardware_crypto() const noexcept { return _hw_crypto; }
133
134
  void clear_cache() noexcept;
135
136
  friend class APFSBlock;
137
};