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