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