Android-cuttlefish cvd tool
map_ptr.h
Go to the documentation of this file.
1/*
2 * Copyright (C) 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#pragma once
18
20#include <android-base/macros.h>
22
23#include <atomic>
24#include <iterator>
25#include <memory>
26#include <shared_mutex>
27#include <type_traits>
28#include <vector>
29
30#ifdef __ANDROID__
31#include <linux/incrementalfs.h>
32#endif
33
34namespace android {
35
36class FileMap;
37
38namespace incfs {
39
40// Controls whether not verifying the presence of data before de-referencing the pointer aborts
41// program execution.
42#define LIBINCFS_MAP_PTR_DEBUG false
43#if LIBINCFS_MAP_PTR_DEBUG
44#define LIBINCFS_MAP_PTR_DEBUG_CODE(x) x
45#else
46#define LIBINCFS_MAP_PTR_DEBUG_CODE(x)
47#endif
48
49template <typename T, bool Verified = false>
50struct map_ptr;
51
52// This class represents a memory-mapped, read-only file that may exist on an IncFs file system.
53//
54// Files stored on IncFs may not be fully present. This class is able to return a smart pointer
55// (map_ptr<T>) that is able to verify whether the contents of the pointer are fully present on
56// IncFs.
57//
58// This always uses MAP_SHARED.
59class IncFsFileMap final {
60public:
61 IncFsFileMap() noexcept;
63 IncFsFileMap& operator =(IncFsFileMap&&) noexcept;
64 ~IncFsFileMap() noexcept;
65
66 // Initializes the map. Does not take ownership of the file descriptor.
67 // Returns whether or not the file was able to be memory-mapped.
68 bool Create(int fd, off64_t offset, size_t length, const char* file_name);
69
70 // Same thing, but allows verification to be disabled when `verify` is `false`, and enabled when
71 // `verify` is true and the file resides on IncFs.
72 bool Create(int fd, off64_t offset, size_t length, const char* file_name, bool verify);
73
74 // Same thing, but allows verification to be disabled when `verify` is `false`, and enabled when
75 // `verify` is true regardless of whether the file resides on IncFs (used for benchmarks and
76 // testing).
77 bool CreateForceVerification(int fd, off64_t offset, size_t length, const char* file_name,
78 bool verify);
79
80 template <typename T = void>
81 map_ptr<T> data() const {
82 return map_ptr<T>(verification_enabled_ ? this : nullptr,
83 reinterpret_cast<const T*>(unsafe_data()));
84 }
85
86 const void* unsafe_data() const;
87 size_t length() const;
88 off64_t offset() const;
89 const char* file_name() const;
90
91public:
92 // Returns whether the data range is entirely present on IncFs.
93 bool Verify(const uint8_t* const& data_start, const uint8_t* const& data_end,
94 const uint8_t** prev_verified_block) const;
95
96private:
98
99 using bucket_t = uint8_t;
100 static constexpr size_t kBucketBits = sizeof(bucket_t) * 8U;
101
102 // File descriptor of the memory-mapped file (not owned).
103 int fd_ = -1;
106 const uint8_t* start_block_ptr_ = nullptr;
107
108 std::unique_ptr<android::FileMap> map_;
109
110 // Bitwise cache for storing whether a block has already been verified. This cache relies on
111 // IncFs not deleting blocks of a file that is currently memory mapped.
112 mutable std::vector<std::atomic<bucket_t>> loaded_blocks_;
113
114 template <typename, bool>
115 friend struct map_ptr;
116};
117
118// Variant of map_ptr that statically guarantees that the pointed to data is fully present and
119// reading data will not result in IncFs raising a SIGBUS.
120template <typename T>
122
123// Smart pointer that is able to verify whether the contents of the pointer are fully present on
124// the file system before using the pointer. Files residing on IncFs may not be fully present.
125//
126// Before attempting to use the data represented by the smart pointer, the caller should always use
127// the bool operator to verify the presence of the data. The bool operator is not thread-safe. If
128// this pointer must be used in multiple threads concurrently, use verified_map_ptr instead.
129//
130// map_ptr created from raw pointers have less overhead than when created from IncFsFileMap.
131template <typename T, bool Verified>
132struct map_ptr final {
133private:
134 friend class IncFsFileMap;
135
136 // To access internals of map_ptr with a different type
137 template <typename, bool>
138 friend struct map_ptr;
139
140 template <typename T1>
141 using IsVoid = typename std::enable_if_t<std::is_void<T1>::value, int>;
142
143 template <typename T1>
144 using NotVoid = typename std::enable_if_t<!std::is_void<T1>::value, int>;
145
146 template <bool V>
147 using IsVerified = typename std::enable_if_t<V, int>;
148
149 template <bool V>
150 using IsUnverified = typename std::enable_if_t<!V, int>;
151
152public:
153 class const_iterator final {
154 public:
155 friend struct map_ptr<T, Verified>;
156 using iterator_category = std::random_access_iterator_tag;
157 using value_type = const map_ptr<T>;
158 using difference_type = std::ptrdiff_t;
159 using pointer = void;
161
162 const_iterator() = default;
163 const_iterator(const const_iterator& it) = default;
164
165 bool operator==(const const_iterator& other) const { return safe_ptr_ == other.safe_ptr_; }
166 bool operator!=(const const_iterator& other) const { return safe_ptr_ != other.safe_ptr_; }
167 std::ptrdiff_t operator-(const const_iterator& other) const {
168 return safe_ptr_ - other.safe_ptr_;
169 }
170
172 const_iterator other = *this;
173 other += n;
174 return other;
175 }
176
177 reference operator*() const { return safe_ptr_; }
178
180 safe_ptr_++;
181 return *this;
182 }
183
185 safe_ptr_ = safe_ptr_ + n;
186 return *this;
187 }
188
190 const_iterator temp(*this);
191 safe_ptr_++;
192 return temp;
193 }
194
195 private:
196 explicit const_iterator(const map_ptr<T>& ptr) : safe_ptr_(ptr) {}
198 };
199
200 // Default constructor
201 map_ptr() = default;
202
203 // Implicit conversion from raw pointer
204 map_ptr(const T* ptr) : map_ptr(nullptr, ptr, nullptr) {}
205
206 // Copy constructor
207 map_ptr(const map_ptr& other) = default;
208
209 // Implicit copy conversion from verified to unverified map_ptr<T>
210 template <bool V2, bool V1 = Verified, IsUnverified<V1> = 0, IsVerified<V2> = 0>
211 map_ptr(const map_ptr<T, V2>& other) : map_ptr(other.map_, other.ptr_, other.verified_block_) {}
212
213 // Move constructor
214 map_ptr(map_ptr&& other) noexcept = default;
215
216 // Implicit move conversion from verified to unverified map_ptr<T>
217 template <bool V2, bool V1 = Verified, IsUnverified<V1> = 0, IsVerified<V2> = 0>
218 map_ptr(map_ptr&& other) : map_ptr(other.map_, other.ptr_, other.verified_block_) {}
219
220 // Implicit conversion to unverified map_ptr<void>
221 template <typename U, bool V2, typename T1 = T, bool V1 = Verified, IsVoid<T1> = 0,
222 NotVoid<U> = 0, IsUnverified<V1> = 0>
224 : map_ptr(other.map_, reinterpret_cast<const void*>(other.ptr_), other.verified_block_) {}
225
226 // Implicit conversion from regular raw pointer
227 map_ptr& operator=(const T* ptr) {
228 ptr_ = ptr;
229 map_ = nullptr;
230 verified_block_ = nullptr;
231 LIBINCFS_MAP_PTR_DEBUG_CODE(verified_ = Verified);
232 return *this;
233 }
234
235 // Copy assignment operator
236 map_ptr& operator=(const map_ptr& other) = default;
237
238 // Copy assignment operator
239 template <bool V2, bool V1 = Verified, IsUnverified<V1> = 0, IsVerified<V2> = 0>
241 ptr_ = other.ptr_;
242 map_ = other.map_;
244 LIBINCFS_MAP_PTR_DEBUG_CODE(verified_ = other.verified_);
245 return *this;
246 }
247
248 template <bool V2>
249 bool operator==(const map_ptr<T, V2>& other) const {
250 return ptr_ == other.ptr_;
251 }
252
253 template <bool V2>
254 bool operator!=(const map_ptr<T, V2>& other) const {
255 return ptr_ != other.ptr_;
256 }
257
258 template <bool V2>
259 bool operator<(const map_ptr<T, V2>& other) const {
260 return ptr_ < other.ptr_;
261 }
262
263 template <bool V2>
264 std::ptrdiff_t operator-(const map_ptr<T, V2>& other) const {
265 return ptr_ - other.ptr_;
266 }
267
268 template <typename U>
270 return map_ptr<U>(map_, reinterpret_cast<const U*>(ptr_), verified_block_);
271 }
272
273 // Retrieves a map_ptr<T> offset from an original map_ptr<U> by the specified number of `offset`
274 // bytes.
275 map_ptr<T> offset(std::ptrdiff_t offset) const {
276 return map_ptr<T>(map_,
277 reinterpret_cast<const T*>(reinterpret_cast<const uint8_t*>(ptr_) +
278 offset),
280 }
281
282 // Returns a raw pointer to the value of this pointer.
283 const T* unsafe_ptr() const { return ptr_; }
284
285 // Start T == void methods
286
287 template <typename T1 = T, IsVoid<T1> = 0>
288 operator bool() const {
289 return ptr_ != nullptr;
290 }
291
292 // End T == void methods
293 // Start T != void methods
294
295 template <typename T1 = T, NotVoid<T1> = 0, bool V1 = Verified, IsUnverified<V1> = 0>
296 operator bool() const {
297 return verify();
298 }
299
300 template <typename T1 = T, NotVoid<T1> = 0, bool V1 = Verified, IsVerified<V1> = 0>
301 operator bool() const {
302 return ptr_ != nullptr;
303 }
304
305 template <typename T1 = T, NotVoid<T1> = 0>
307 return const_iterator(*this);
308 }
309
310 template <typename T1 = T, NotVoid<T1> = 0>
312 LIBINCFS_MAP_PTR_DEBUG_CODE(verified_ = false);
313 ++ptr_;
314 return *this;
315 }
316
317 template <typename T1 = T, NotVoid<T1> = 0>
319 map_ptr<T1> temp = *this;
320 LIBINCFS_MAP_PTR_DEBUG_CODE(verified_ = false);
321 ++ptr_;
322 return temp;
323 }
324
325 template <typename S, typename T1 = T, NotVoid<T1> = 0>
326 map_ptr<T1> operator+(const S n) const {
327 return map_ptr<T1>(map_, ptr_ + n, verified_block_);
328 }
329
330 template <typename S, typename T1 = T, NotVoid<T1> = 0>
331 map_ptr<T1> operator-(const S n) const {
332 return map_ptr<T1>(map_, ptr_ - n, verified_block_);
333 }
334
335 // Returns the value of the pointer.
336 // The caller should verify the presence of the pointer data before calling this method.
337 template <typename T1 = T, NotVoid<T1> = 0>
338 const T1& value() const {
340 CHECK(verified_) << "Did not verify presence before de-referencing safe pointer");
341 return *ptr_;
342 }
343
344 // Returns a raw pointer to the value this pointer.
345 // The caller should verify the presence of the pointer data before calling this method.
346 template <typename T1 = T, NotVoid<T1> = 0>
347 const T1* operator->() const {
349 CHECK(verified_) << "Did not verify presence before de-referencing safe pointer");
350 return ptr_;
351 }
352
353 // Verifies the presence of `n` elements of `T`.
354 //
355 // Returns true if the elements are completely present; otherwise, returns false.
356 template <typename T1 = T, NotVoid<T1> = 0, bool V1 = Verified, IsUnverified<V1> = 0>
357 bool verify(size_t n = 1) const {
358 if (ptr_ == nullptr) {
359 return false;
360 }
361
362#ifdef __ANDROID__
363 if (LIKELY(map_ == nullptr)) {
364 return ptr_ != nullptr;
365 }
366
367 const size_t verify_size = sizeof(T) * n;
368 LIBINCFS_MAP_PTR_DEBUG_CODE(if (sizeof(T) <= verify_size) verified_ = true;);
369
370 const auto data_start = reinterpret_cast<const uint8_t*>(ptr_);
371 const auto data_end = reinterpret_cast<const uint8_t*>(ptr_) + verify_size;
372
373 // If the data is entirely within the block beginning at the previous verified block
374 // pointer, then the data can safely be used.
375 if (LIKELY(data_start >= verified_block_ &&
376 data_end <= verified_block_ + INCFS_DATA_FILE_BLOCK_SIZE)) {
377 return true;
378 }
379
380 if (LIKELY(map_->Verify(data_start, data_end, &verified_block_))) {
381 return true;
382 }
383
384 LIBINCFS_MAP_PTR_DEBUG_CODE(verified_ = false);
385 return false;
386#else
387 (void)n;
388 return true;
389#endif
390 }
391
392 // Returns a verified version of this pointer.
393 // The caller should verify the presence of the pointer data before calling this method.
394 template <typename T1 = T, NotVoid<T1> = 0>
397 }
398
399private:
400 map_ptr(const IncFsFileMap* map, const T* ptr)
401 : ptr_(ptr), map_(map), verified_block_(nullptr) {}
402 map_ptr(const IncFsFileMap* map, const T* ptr, const uint8_t* verified_block)
403 : ptr_(ptr), map_(map), verified_block_(verified_block) {}
404
405 const T* ptr_ = nullptr;
406 mutable const IncFsFileMap* map_ = nullptr;
407 mutable const uint8_t* verified_block_;
408 LIBINCFS_MAP_PTR_DEBUG_CODE(mutable bool verified_ = Verified);
409};
410
411} // namespace incfs
412
413} // namespace android
Definition: map_ptr.h:59
const char * file_name() const
uint8_t bucket_t
Definition: map_ptr.h:99
const void * unsafe_data() const
int fd_
Definition: map_ptr.h:103
size_t start_block_offset_
Definition: map_ptr.h:105
bool verification_enabled_
Definition: map_ptr.h:104
DISALLOW_COPY_AND_ASSIGN(IncFsFileMap)
static constexpr size_t kBucketBits
Definition: map_ptr.h:100
bool Verify(const uint8_t *const &data_start, const uint8_t *const &data_end, const uint8_t **prev_verified_block) const
const uint8_t * start_block_ptr_
Definition: map_ptr.h:106
map_ptr< T > data() const
Definition: map_ptr.h:81
std::unique_ptr< android::FileMap > map_
Definition: map_ptr.h:108
std::vector< std::atomic< bucket_t > > loaded_blocks_
Definition: map_ptr.h:112
bool Create(int fd, off64_t offset, size_t length, const char *file_name)
bool CreateForceVerification(int fd, off64_t offset, size_t length, const char *file_name, bool verify)
bool operator!=(const const_iterator &other) const
Definition: map_ptr.h:166
map_ptr< T > safe_ptr_
Definition: map_ptr.h:197
std::ptrdiff_t operator-(const const_iterator &other) const
Definition: map_ptr.h:167
void pointer
Definition: map_ptr.h:159
const map_ptr< T > value_type
Definition: map_ptr.h:157
const_iterator & operator+=(int n)
Definition: map_ptr.h:184
const const_iterator & operator++()
Definition: map_ptr.h:179
const_iterator(const map_ptr< T > &ptr)
Definition: map_ptr.h:196
bool operator==(const const_iterator &other) const
Definition: map_ptr.h:165
std::ptrdiff_t difference_type
Definition: map_ptr.h:158
const const_iterator operator++(int)
Definition: map_ptr.h:189
std::random_access_iterator_tag iterator_category
Definition: map_ptr.h:156
const_iterator operator+(int n) const
Definition: map_ptr.h:171
const_iterator(const const_iterator &it)=default
reference operator*() const
Definition: map_ptr.h:177
#define CHECK(x)
Definition: logging.h:251
#define LIKELY(exp)
Definition: macros.h:83
Definition: map_ptr.h:34
Definition: map_ptr.h:132
map_ptr & operator=(const map_ptr &other)=default
bool operator==(const map_ptr< T, V2 > &other) const
Definition: map_ptr.h:249
typename std::enable_if_t< V, int > IsVerified
Definition: map_ptr.h:147
typename std::enable_if_t< std::is_void< T1 >::value, int > IsVoid
Definition: map_ptr.h:141
const map_ptr< T1 > & operator++()
Definition: map_ptr.h:311
bool verify(size_t n=1) const
Definition: map_ptr.h:357
map_ptr(const map_ptr< T, V2 > &other)
Definition: map_ptr.h:211
map_ptr< U > convert() const
Definition: map_ptr.h:269
const IncFsFileMap * map_
Definition: map_ptr.h:406
bool operator<(const map_ptr< T, V2 > &other) const
Definition: map_ptr.h:259
map_ptr(const IncFsFileMap *map, const T *ptr)
Definition: map_ptr.h:400
const uint8_t * verified_block_
Definition: map_ptr.h:407
map_ptr & operator=(const T *ptr)
Definition: map_ptr.h:227
const T * unsafe_ptr() const
Definition: map_ptr.h:283
map_ptr & operator=(const map_ptr< T, V2 > &other)
Definition: map_ptr.h:240
map_ptr(const map_ptr &other)=default
const T1 & value() const
Definition: map_ptr.h:338
const T1 * operator->() const
Definition: map_ptr.h:347
const_iterator iterator() const
Definition: map_ptr.h:306
typename std::enable_if_t<!std::is_void< T1 >::value, int > NotVoid
Definition: map_ptr.h:144
map_ptr< T1 > operator+(const S n) const
Definition: map_ptr.h:326
bool operator!=(const map_ptr< T, V2 > &other) const
Definition: map_ptr.h:254
std::ptrdiff_t operator-(const map_ptr< T, V2 > &other) const
Definition: map_ptr.h:264
map_ptr< T > offset(std::ptrdiff_t offset) const
Definition: map_ptr.h:275
verified_map_ptr< T1 > verified() const
Definition: map_ptr.h:395
LIBINCFS_MAP_PTR_DEBUG_CODE(mutable bool verified_=Verified)
map_ptr< T1 > operator-(const S n) const
Definition: map_ptr.h:331
map_ptr(map_ptr &&other) noexcept=default
map_ptr(const T *ptr)
Definition: map_ptr.h:204
map_ptr(map_ptr &&other)
Definition: map_ptr.h:218
const T * ptr_
Definition: map_ptr.h:405
const map_ptr< T1 > operator++(int)
Definition: map_ptr.h:318
typename std::enable_if_t<!V, int > IsUnverified
Definition: map_ptr.h:150
map_ptr(const map_ptr< U, V2 > &other)
Definition: map_ptr.h:223
map_ptr(const IncFsFileMap *map, const T *ptr, const uint8_t *verified_block)
Definition: map_ptr.h:402