Coverage Report

Created: 2025-07-01 06:59

/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
}