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