simple_index_file.cc 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634
  1. // Copyright (c) 2013 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "net/disk_cache/simple/simple_index_file.h"
  5. #include <utility>
  6. #include <vector>
  7. #include "base/bind.h"
  8. #include "base/files/file.h"
  9. #include "base/files/file_util.h"
  10. #include "base/logging.h"
  11. #include "base/numerics/safe_conversions.h"
  12. #include "base/pickle.h"
  13. #include "base/strings/string_util.h"
  14. #include "base/task/thread_pool.h"
  15. #include "base/threading/thread_restrictions.h"
  16. #include "base/time/time.h"
  17. #include "build/build_config.h"
  18. #include "net/disk_cache/simple/simple_backend_impl.h"
  19. #include "net/disk_cache/simple/simple_entry_format.h"
  20. #include "net/disk_cache/simple/simple_file_enumerator.h"
  21. #include "net/disk_cache/simple/simple_histogram_macros.h"
  22. #include "net/disk_cache/simple/simple_index.h"
  23. #include "net/disk_cache/simple/simple_synchronous_entry.h"
  24. #include "net/disk_cache/simple/simple_util.h"
  25. namespace disk_cache {
  26. namespace {
  27. const int kEntryFilesHashLength = 16;
  28. const int kEntryFilesSuffixLength = 2;
  29. // Limit on how big a file we are willing to work with, to avoid crashes
  30. // when its corrupt.
  31. const int kMaxEntriesInIndex = 1000000;
  32. // Here 8 comes from the key size.
  33. const int64_t kMaxIndexFileSizeBytes =
  34. kMaxEntriesInIndex * (8 + EntryMetadata::kOnDiskSizeBytes);
  35. uint32_t CalculatePickleCRC(const base::Pickle& pickle) {
  36. return simple_util::Crc32(pickle.payload(), pickle.payload_size());
  37. }
  38. // Used in histograms. Please only add new values at the end.
  39. enum IndexFileState {
  40. INDEX_STATE_CORRUPT = 0,
  41. INDEX_STATE_STALE = 1,
  42. INDEX_STATE_FRESH = 2,
  43. INDEX_STATE_FRESH_CONCURRENT_UPDATES = 3,
  44. INDEX_STATE_MAX = 4,
  45. };
  46. enum StaleIndexQuality {
  47. STALE_INDEX_OK = 0,
  48. STALE_INDEX_MISSED_ENTRIES = 1,
  49. STALE_INDEX_EXTRA_ENTRIES = 2,
  50. STALE_INDEX_BOTH_MISSED_AND_EXTRA_ENTRIES = 3,
  51. STALE_INDEX_MAX = 4,
  52. };
  53. void UmaRecordIndexFileState(IndexFileState state, net::CacheType cache_type) {
  54. SIMPLE_CACHE_UMA(ENUMERATION,
  55. "IndexFileStateOnLoad", cache_type, state, INDEX_STATE_MAX);
  56. }
  57. void UmaRecordIndexInitMethod(SimpleIndex::IndexInitMethod method,
  58. net::CacheType cache_type) {
  59. SIMPLE_CACHE_UMA(ENUMERATION, "IndexInitializeMethod", cache_type, method,
  60. SimpleIndex::INITIALIZE_METHOD_MAX);
  61. }
  62. void UmaRecordStaleIndexQuality(int missed_entry_count,
  63. int extra_entry_count,
  64. net::CacheType cache_type) {
  65. SIMPLE_CACHE_UMA(CUSTOM_COUNTS, "StaleIndexMissedEntryCount", cache_type,
  66. missed_entry_count, 1, 100, 5);
  67. SIMPLE_CACHE_UMA(CUSTOM_COUNTS, "StaleIndexExtraEntryCount", cache_type,
  68. extra_entry_count, 1, 100, 5);
  69. StaleIndexQuality quality;
  70. if (missed_entry_count > 0 && extra_entry_count > 0)
  71. quality = STALE_INDEX_BOTH_MISSED_AND_EXTRA_ENTRIES;
  72. else if (missed_entry_count > 0)
  73. quality = STALE_INDEX_MISSED_ENTRIES;
  74. else if (extra_entry_count > 0)
  75. quality = STALE_INDEX_EXTRA_ENTRIES;
  76. else
  77. quality = STALE_INDEX_OK;
  78. SIMPLE_CACHE_UMA(ENUMERATION, "StaleIndexQuality", cache_type, quality,
  79. STALE_INDEX_MAX);
  80. }
  81. struct PickleHeader : public base::Pickle::Header {
  82. uint32_t crc;
  83. };
  84. class SimpleIndexPickle : public base::Pickle {
  85. public:
  86. SimpleIndexPickle() : base::Pickle(sizeof(PickleHeader)) {}
  87. SimpleIndexPickle(const char* data, int data_len)
  88. : base::Pickle(data, data_len) {}
  89. bool HeaderValid() const { return header_size() == sizeof(PickleHeader); }
  90. };
  91. bool WritePickleFile(BackendFileOperations* file_operations,
  92. base::Pickle* pickle,
  93. const base::FilePath& file_name) {
  94. base::File file = file_operations->OpenFile(
  95. file_name, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE |
  96. base::File::FLAG_WIN_SHARE_DELETE);
  97. if (!file.IsValid())
  98. return false;
  99. int bytes_written =
  100. file.Write(0, static_cast<const char*>(pickle->data()), pickle->size());
  101. if (bytes_written != base::checked_cast<int>(pickle->size())) {
  102. file_operations->DeleteFile(
  103. file_name,
  104. BackendFileOperations::DeleteFileMode::kEnsureImmediateAvailability);
  105. return false;
  106. }
  107. return true;
  108. }
  109. // Called for each cache directory traversal iteration.
  110. void ProcessEntryFile(BackendFileOperations* file_operations,
  111. net::CacheType cache_type,
  112. SimpleIndex::EntrySet* entries,
  113. const base::FilePath& file_path,
  114. base::Time last_accessed,
  115. base::Time last_modified,
  116. int64_t size) {
  117. static const size_t kEntryFilesLength =
  118. kEntryFilesHashLength + kEntryFilesSuffixLength;
  119. // Converting to std::string is OK since we never use UTF8 wide chars in our
  120. // file names.
  121. const base::FilePath::StringType base_name = file_path.BaseName().value();
  122. const std::string file_name(base_name.begin(), base_name.end());
  123. // Cleanup any left over doomed entries.
  124. if (base::StartsWith(file_name, "todelete_", base::CompareCase::SENSITIVE)) {
  125. file_operations->DeleteFile(file_path);
  126. return;
  127. }
  128. if (file_name.size() != kEntryFilesLength)
  129. return;
  130. const auto hash_string = base::MakeStringPiece(
  131. file_name.begin(), file_name.begin() + kEntryFilesHashLength);
  132. uint64_t hash_key = 0;
  133. if (!simple_util::GetEntryHashKeyFromHexString(hash_string, &hash_key)) {
  134. LOG(WARNING) << "Invalid entry hash key filename while restoring index from"
  135. << " disk: " << file_name;
  136. return;
  137. }
  138. base::Time last_used_time;
  139. #if BUILDFLAG(IS_POSIX)
  140. // For POSIX systems, a last access time is available. However, it's not
  141. // guaranteed to be more accurate than mtime. It is no worse though.
  142. last_used_time = last_accessed;
  143. #endif
  144. if (last_used_time.is_null())
  145. last_used_time = last_modified;
  146. auto it = entries->find(hash_key);
  147. base::CheckedNumeric<uint32_t> total_entry_size = size;
  148. // Sometimes we see entry sizes here which are nonsense. We can't use them
  149. // as-is, as they simply won't fit the type. The options that come to mind
  150. // are:
  151. // 1) Ignore the file.
  152. // 2) Make something up.
  153. // 3) Delete the files for the hash.
  154. // ("crash the browser" isn't considered a serious alternative).
  155. //
  156. // The problem with doing (1) is that we are recovering the index here, so if
  157. // we don't include the info on the file here, we may completely lose track of
  158. // the entry and never clean the file up.
  159. //
  160. // (2) is actually mostly fine: we may trigger eviction too soon or too late,
  161. // but we can't really do better since we can't trust the size. If the entry
  162. // is never opened, it will eventually get evicted. If it is opened, we will
  163. // re-check the file size, and if it's nonsense delete it there, and if it's
  164. // fine we will fix up the index via a UpdateDataFromEntryStat to have the
  165. // correct size.
  166. //
  167. // (3) does the best thing except when the wrong size is some weird interim
  168. // thing just on directory listing (in which case it may evict an entry
  169. // prematurely). It's a little harder to think about since it involves
  170. // mutating the disk while there are other mutations going on, however,
  171. // while (2) is single-threaded.
  172. //
  173. // Hence this picks (2).
  174. const int kPlaceHolderSizeWhenInvalid = 32768;
  175. if (!total_entry_size.IsValid()) {
  176. LOG(WARNING) << "Invalid file size while restoring index from disk: "
  177. << size << " on file:" << file_name;
  178. }
  179. if (it == entries->end()) {
  180. uint32_t size_to_use =
  181. total_entry_size.ValueOrDefault(kPlaceHolderSizeWhenInvalid);
  182. if (cache_type == net::APP_CACHE) {
  183. SimpleIndex::InsertInEntrySet(
  184. hash_key, EntryMetadata(0 /* trailer_prefetch_size */, size_to_use),
  185. entries);
  186. } else {
  187. SimpleIndex::InsertInEntrySet(
  188. hash_key, EntryMetadata(last_used_time, size_to_use), entries);
  189. }
  190. } else {
  191. // Summing up the total size of the entry through all the *_[0-1] files
  192. total_entry_size += it->second.GetEntrySize();
  193. it->second.SetEntrySize(
  194. total_entry_size.ValueOrDefault(kPlaceHolderSizeWhenInvalid));
  195. }
  196. }
  197. } // namespace
  198. SimpleIndexLoadResult::SimpleIndexLoadResult() = default;
  199. SimpleIndexLoadResult::~SimpleIndexLoadResult() = default;
  200. void SimpleIndexLoadResult::Reset() {
  201. did_load = false;
  202. index_write_reason = SimpleIndex::INDEX_WRITE_REASON_MAX;
  203. flush_required = false;
  204. entries.clear();
  205. }
  206. // static
  207. const char SimpleIndexFile::kIndexFileName[] = "the-real-index";
  208. // static
  209. const char SimpleIndexFile::kIndexDirectory[] = "index-dir";
  210. // static
  211. const char SimpleIndexFile::kTempIndexFileName[] = "temp-index";
  212. SimpleIndexFile::IndexMetadata::IndexMetadata()
  213. : reason_(SimpleIndex::INDEX_WRITE_REASON_MAX),
  214. entry_count_(0),
  215. cache_size_(0) {}
  216. SimpleIndexFile::IndexMetadata::IndexMetadata(
  217. SimpleIndex::IndexWriteToDiskReason reason,
  218. uint64_t entry_count,
  219. uint64_t cache_size)
  220. : reason_(reason), entry_count_(entry_count), cache_size_(cache_size) {}
  221. void SimpleIndexFile::IndexMetadata::Serialize(base::Pickle* pickle) const {
  222. DCHECK(pickle);
  223. pickle->WriteUInt64(magic_number_);
  224. pickle->WriteUInt32(version_);
  225. pickle->WriteUInt64(entry_count_);
  226. pickle->WriteUInt64(cache_size_);
  227. pickle->WriteUInt32(static_cast<uint32_t>(reason_));
  228. }
  229. // static
  230. void SimpleIndexFile::SerializeFinalData(base::Time cache_modified,
  231. base::Pickle* pickle) {
  232. pickle->WriteInt64(cache_modified.ToInternalValue());
  233. PickleHeader* header_p = pickle->headerT<PickleHeader>();
  234. header_p->crc = CalculatePickleCRC(*pickle);
  235. }
  236. bool SimpleIndexFile::IndexMetadata::Deserialize(base::PickleIterator* it) {
  237. DCHECK(it);
  238. bool v6_format_index_read_results =
  239. it->ReadUInt64(&magic_number_) && it->ReadUInt32(&version_) &&
  240. it->ReadUInt64(&entry_count_) && it->ReadUInt64(&cache_size_);
  241. if (!v6_format_index_read_results)
  242. return false;
  243. if (version_ >= 7) {
  244. uint32_t tmp_reason;
  245. if (!it->ReadUInt32(&tmp_reason))
  246. return false;
  247. reason_ = static_cast<SimpleIndex::IndexWriteToDiskReason>(tmp_reason);
  248. }
  249. return true;
  250. }
  251. void SimpleIndexFile::SyncWriteToDisk(
  252. std::unique_ptr<BackendFileOperations> file_operations,
  253. net::CacheType cache_type,
  254. const base::FilePath& cache_directory,
  255. const base::FilePath& index_filename,
  256. const base::FilePath& temp_index_filename,
  257. std::unique_ptr<base::Pickle> pickle) {
  258. DCHECK_EQ(index_filename.DirName().value(),
  259. temp_index_filename.DirName().value());
  260. base::FilePath index_file_directory = temp_index_filename.DirName();
  261. if (!file_operations->DirectoryExists(index_file_directory) &&
  262. !file_operations->CreateDirectory(index_file_directory)) {
  263. LOG(ERROR) << "Could not create a directory to hold the index file";
  264. return;
  265. }
  266. // There is a chance that the index containing all the necessary data about
  267. // newly created entries will appear to be stale. This can happen if on-disk
  268. // part of a Create operation does not fit into the time budget for the index
  269. // flush delay. This simple approach will be reconsidered if it does not allow
  270. // for maintaining freshness.
  271. base::Time cache_dir_mtime;
  272. absl::optional<base::File::Info> file_info =
  273. file_operations->GetFileInfo(cache_directory);
  274. if (!file_info) {
  275. LOG(ERROR) << "Could not obtain information about cache age";
  276. return;
  277. }
  278. cache_dir_mtime = file_info->last_modified;
  279. SerializeFinalData(cache_dir_mtime, pickle.get());
  280. if (!WritePickleFile(file_operations.get(), pickle.get(),
  281. temp_index_filename)) {
  282. LOG(ERROR) << "Failed to write the temporary index file";
  283. return;
  284. }
  285. // Atomically rename the temporary index file to become the real one.
  286. if (!file_operations->ReplaceFile(temp_index_filename, index_filename,
  287. nullptr)) {
  288. return;
  289. }
  290. }
  291. bool SimpleIndexFile::IndexMetadata::CheckIndexMetadata() {
  292. if (entry_count_ > kMaxEntriesInIndex ||
  293. magic_number_ != kSimpleIndexMagicNumber) {
  294. return false;
  295. }
  296. static_assert(kSimpleVersion == 9, "index metadata reader out of date");
  297. // No |reason_| is saved in the version 6 file format.
  298. if (version_ == 6)
  299. return reason_ == SimpleIndex::INDEX_WRITE_REASON_MAX;
  300. return (version_ == 7 || version_ == 8 || version_ == 9) &&
  301. reason_ < SimpleIndex::INDEX_WRITE_REASON_MAX;
  302. }
  303. SimpleIndexFile::SimpleIndexFile(
  304. scoped_refptr<base::SequencedTaskRunner> cache_runner,
  305. scoped_refptr<BackendFileOperationsFactory> file_operations_factory,
  306. net::CacheType cache_type,
  307. const base::FilePath& cache_directory)
  308. : cache_runner_(std::move(cache_runner)),
  309. file_operations_factory_(std::move(file_operations_factory)),
  310. cache_type_(cache_type),
  311. cache_directory_(cache_directory),
  312. index_file_(cache_directory_.AppendASCII(kIndexDirectory)
  313. .AppendASCII(kIndexFileName)),
  314. temp_index_file_(cache_directory_.AppendASCII(kIndexDirectory)
  315. .AppendASCII(kTempIndexFileName)) {}
  316. SimpleIndexFile::~SimpleIndexFile() = default;
  317. void SimpleIndexFile::LoadIndexEntries(base::Time cache_last_modified,
  318. base::OnceClosure callback,
  319. SimpleIndexLoadResult* out_result) {
  320. auto task_runner = base::ThreadPool::CreateSequencedTaskRunner(
  321. SimpleBackendImpl::kWorkerPoolTaskTraits);
  322. base::OnceClosure task = base::BindOnce(
  323. &SimpleIndexFile::SyncLoadIndexEntries,
  324. file_operations_factory_->Create(task_runner), cache_type_,
  325. cache_last_modified, cache_directory_, index_file_, out_result);
  326. task_runner->PostTaskAndReply(FROM_HERE, std::move(task),
  327. std::move(callback));
  328. }
  329. void SimpleIndexFile::WriteToDisk(net::CacheType cache_type,
  330. SimpleIndex::IndexWriteToDiskReason reason,
  331. const SimpleIndex::EntrySet& entry_set,
  332. uint64_t cache_size,
  333. base::OnceClosure callback) {
  334. IndexMetadata index_metadata(reason, entry_set.size(), cache_size);
  335. std::unique_ptr<base::Pickle> pickle =
  336. Serialize(cache_type, index_metadata, entry_set);
  337. auto file_operations = file_operations_factory_->Create(cache_runner_);
  338. base::OnceClosure task =
  339. base::BindOnce(&SimpleIndexFile::SyncWriteToDisk,
  340. std::move(file_operations), cache_type_, cache_directory_,
  341. index_file_, temp_index_file_, std::move(pickle));
  342. if (callback.is_null())
  343. cache_runner_->PostTask(FROM_HERE, std::move(task));
  344. else
  345. cache_runner_->PostTaskAndReply(FROM_HERE, std::move(task),
  346. std::move(callback));
  347. }
  348. // static
  349. void SimpleIndexFile::SyncLoadIndexEntries(
  350. std::unique_ptr<BackendFileOperations> file_operations,
  351. net::CacheType cache_type,
  352. base::Time cache_last_modified,
  353. const base::FilePath& cache_directory,
  354. const base::FilePath& index_file_path,
  355. SimpleIndexLoadResult* out_result) {
  356. // Load the index and find its age.
  357. base::Time last_cache_seen_by_index;
  358. SyncLoadFromDisk(file_operations.get(), cache_type, index_file_path,
  359. &last_cache_seen_by_index, out_result);
  360. // Consider the index loaded if it is fresh.
  361. const bool index_file_existed = file_operations->PathExists(index_file_path);
  362. if (!out_result->did_load) {
  363. if (index_file_existed)
  364. UmaRecordIndexFileState(INDEX_STATE_CORRUPT, cache_type);
  365. } else {
  366. if (cache_last_modified <= last_cache_seen_by_index) {
  367. base::Time latest_dir_mtime;
  368. if (auto info = file_operations->GetFileInfo(cache_directory)) {
  369. latest_dir_mtime = info->last_modified;
  370. }
  371. if (LegacyIsIndexFileStale(file_operations.get(), latest_dir_mtime,
  372. index_file_path)) {
  373. UmaRecordIndexFileState(INDEX_STATE_FRESH_CONCURRENT_UPDATES,
  374. cache_type);
  375. } else {
  376. UmaRecordIndexFileState(INDEX_STATE_FRESH, cache_type);
  377. }
  378. out_result->init_method = SimpleIndex::INITIALIZE_METHOD_LOADED;
  379. UmaRecordIndexInitMethod(out_result->init_method, cache_type);
  380. return;
  381. }
  382. UmaRecordIndexFileState(INDEX_STATE_STALE, cache_type);
  383. }
  384. // Reconstruct the index by scanning the disk for entries.
  385. SimpleIndex::EntrySet entries_from_stale_index;
  386. entries_from_stale_index.swap(out_result->entries);
  387. const base::TimeTicks start = base::TimeTicks::Now();
  388. SyncRestoreFromDisk(file_operations.get(), cache_type, cache_directory,
  389. index_file_path, out_result);
  390. SIMPLE_CACHE_UMA(MEDIUM_TIMES, "IndexRestoreTime", cache_type,
  391. base::TimeTicks::Now() - start);
  392. if (index_file_existed) {
  393. out_result->init_method = SimpleIndex::INITIALIZE_METHOD_RECOVERED;
  394. int missed_entry_count = 0;
  395. for (const auto& i : out_result->entries) {
  396. if (entries_from_stale_index.count(i.first) == 0)
  397. ++missed_entry_count;
  398. }
  399. int extra_entry_count = 0;
  400. for (const auto& i : entries_from_stale_index) {
  401. if (out_result->entries.count(i.first) == 0)
  402. ++extra_entry_count;
  403. }
  404. UmaRecordStaleIndexQuality(missed_entry_count, extra_entry_count,
  405. cache_type);
  406. } else {
  407. out_result->init_method = SimpleIndex::INITIALIZE_METHOD_NEWCACHE;
  408. SIMPLE_CACHE_UMA(COUNTS_1M,
  409. "IndexCreatedEntryCount", cache_type,
  410. out_result->entries.size());
  411. }
  412. UmaRecordIndexInitMethod(out_result->init_method, cache_type);
  413. }
  414. // static
  415. void SimpleIndexFile::SyncLoadFromDisk(BackendFileOperations* file_operations,
  416. net::CacheType cache_type,
  417. const base::FilePath& index_filename,
  418. base::Time* out_last_cache_seen_by_index,
  419. SimpleIndexLoadResult* out_result) {
  420. out_result->Reset();
  421. base::File file = file_operations->OpenFile(
  422. index_filename, base::File::FLAG_OPEN | base::File::FLAG_READ |
  423. base::File::FLAG_WIN_SHARE_DELETE |
  424. base::File::FLAG_WIN_SEQUENTIAL_SCAN);
  425. if (!file.IsValid())
  426. return;
  427. // Sanity-check the length. We don't want to crash trying to read some corrupt
  428. // 10GiB file or such.
  429. int64_t file_length = file.GetLength();
  430. if (file_length < 0 || file_length > kMaxIndexFileSizeBytes) {
  431. file_operations->DeleteFile(
  432. index_filename,
  433. BackendFileOperations::DeleteFileMode::kEnsureImmediateAvailability);
  434. return;
  435. }
  436. // Make sure to preallocate in one chunk, so we don't induce fragmentation
  437. // reallocating a growing buffer.
  438. auto buffer = std::make_unique<char[]>(file_length);
  439. int read = file.Read(0, buffer.get(), file_length);
  440. if (read < file_length) {
  441. file_operations->DeleteFile(
  442. index_filename,
  443. BackendFileOperations::DeleteFileMode::kEnsureImmediateAvailability);
  444. return;
  445. }
  446. SimpleIndexFile::Deserialize(cache_type, buffer.get(), read,
  447. out_last_cache_seen_by_index, out_result);
  448. if (!out_result->did_load) {
  449. file_operations->DeleteFile(
  450. index_filename,
  451. BackendFileOperations::DeleteFileMode::kEnsureImmediateAvailability);
  452. }
  453. }
  454. // static
  455. std::unique_ptr<base::Pickle> SimpleIndexFile::Serialize(
  456. net::CacheType cache_type,
  457. const SimpleIndexFile::IndexMetadata& index_metadata,
  458. const SimpleIndex::EntrySet& entries) {
  459. std::unique_ptr<base::Pickle> pickle = std::make_unique<SimpleIndexPickle>();
  460. index_metadata.Serialize(pickle.get());
  461. for (const auto& entry : entries) {
  462. pickle->WriteUInt64(entry.first);
  463. entry.second.Serialize(cache_type, pickle.get());
  464. }
  465. return pickle;
  466. }
  467. // static
  468. void SimpleIndexFile::Deserialize(net::CacheType cache_type,
  469. const char* data,
  470. int data_len,
  471. base::Time* out_cache_last_modified,
  472. SimpleIndexLoadResult* out_result) {
  473. DCHECK(data);
  474. out_result->Reset();
  475. SimpleIndex::EntrySet* entries = &out_result->entries;
  476. SimpleIndexPickle pickle(data, data_len);
  477. if (!pickle.data() || !pickle.HeaderValid()) {
  478. LOG(WARNING) << "Corrupt Simple Index File.";
  479. return;
  480. }
  481. base::PickleIterator pickle_it(pickle);
  482. PickleHeader* header_p = pickle.headerT<PickleHeader>();
  483. const uint32_t crc_read = header_p->crc;
  484. const uint32_t crc_calculated = CalculatePickleCRC(pickle);
  485. if (crc_read != crc_calculated) {
  486. LOG(WARNING) << "Invalid CRC in Simple Index file.";
  487. return;
  488. }
  489. SimpleIndexFile::IndexMetadata index_metadata;
  490. if (!index_metadata.Deserialize(&pickle_it)) {
  491. LOG(ERROR) << "Invalid index_metadata on Simple Cache Index.";
  492. return;
  493. }
  494. if (!index_metadata.CheckIndexMetadata()) {
  495. LOG(ERROR) << "Invalid index_metadata on Simple Cache Index.";
  496. return;
  497. }
  498. entries->reserve(index_metadata.entry_count() + kExtraSizeForMerge);
  499. while (entries->size() < index_metadata.entry_count()) {
  500. uint64_t hash_key;
  501. EntryMetadata entry_metadata;
  502. if (!pickle_it.ReadUInt64(&hash_key) ||
  503. !entry_metadata.Deserialize(
  504. cache_type, &pickle_it, index_metadata.has_entry_in_memory_data(),
  505. index_metadata.app_cache_has_trailer_prefetch_size())) {
  506. LOG(WARNING) << "Invalid EntryMetadata in Simple Index file.";
  507. entries->clear();
  508. return;
  509. }
  510. SimpleIndex::InsertInEntrySet(hash_key, entry_metadata, entries);
  511. }
  512. int64_t cache_last_modified;
  513. if (!pickle_it.ReadInt64(&cache_last_modified)) {
  514. entries->clear();
  515. return;
  516. }
  517. DCHECK(out_cache_last_modified);
  518. *out_cache_last_modified = base::Time::FromInternalValue(cache_last_modified);
  519. out_result->index_write_reason = index_metadata.reason();
  520. out_result->did_load = true;
  521. }
  522. // static
  523. void SimpleIndexFile::SyncRestoreFromDisk(
  524. BackendFileOperations* file_operations,
  525. net::CacheType cache_type,
  526. const base::FilePath& cache_directory,
  527. const base::FilePath& index_file_path,
  528. SimpleIndexLoadResult* out_result) {
  529. VLOG(1) << "Simple Cache Index is being restored from disk.";
  530. file_operations->DeleteFile(
  531. index_file_path,
  532. BackendFileOperations::DeleteFileMode::kEnsureImmediateAvailability);
  533. out_result->Reset();
  534. SimpleIndex::EntrySet* entries = &out_result->entries;
  535. auto enumerator = file_operations->EnumerateFiles(cache_directory);
  536. while (absl::optional<SimpleFileEnumerator::Entry> entry =
  537. enumerator->Next()) {
  538. ProcessEntryFile(file_operations, cache_type, entries, entry->path,
  539. entry->last_accessed, entry->last_modified, entry->size);
  540. }
  541. if (enumerator->HasError()) {
  542. LOG(ERROR) << "Could not reconstruct index from disk";
  543. return;
  544. }
  545. out_result->did_load = true;
  546. // When we restore from disk we write the merged index file to disk right
  547. // away, this might save us from having to restore again next time.
  548. out_result->flush_required = true;
  549. }
  550. // static
  551. bool SimpleIndexFile::LegacyIsIndexFileStale(
  552. BackendFileOperations* file_operations,
  553. base::Time cache_last_modified,
  554. const base::FilePath& index_file_path) {
  555. if (auto info = file_operations->GetFileInfo(index_file_path)) {
  556. return info->last_modified < cache_last_modified;
  557. }
  558. return true;
  559. }
  560. } // namespace disk_cache