• 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_synchronous_entry.h"
6 
7 #include <cstring>
8 #include <functional>
9 #include <limits>
10 #include <zlib.h>
11 
12 #include "base/compiler_specific.h"
13 #include "base/containers/stack_container.h"
14 #include "base/files/file_util.h"
15 #include "base/hash/hash.h"
16 #include "base/location.h"
17 #include "base/memory/ptr_util.h"
18 #include "base/memory/raw_ptr.h"
19 #include "base/metrics/field_trial_params.h"
20 #include "base/metrics/histogram_macros.h"
21 #include "base/metrics/histogram_macros_local.h"
22 #include "base/numerics/checked_math.h"
23 #include "base/numerics/safe_conversions.h"
24 #include "base/ranges/algorithm.h"
25 #include "base/strings/string_piece.h"
26 #include "base/task/sequenced_task_runner.h"
27 #include "base/timer/elapsed_timer.h"
28 #include "crypto/secure_hash.h"
29 #include "net/base/hash_value.h"
30 #include "net/base/io_buffer.h"
31 #include "net/base/net_errors.h"
32 #include "net/disk_cache/cache_util.h"
33 #include "net/disk_cache/simple/simple_backend_version.h"
34 #include "net/disk_cache/simple/simple_histogram_enums.h"
35 #include "net/disk_cache/simple/simple_histogram_macros.h"
36 #include "net/disk_cache/simple/simple_util.h"
37 
38 using base::FilePath;
39 using base::Time;
40 
41 namespace disk_cache {
42 
43 namespace {
44 
RecordSyncOpenResult(net::CacheType cache_type,OpenEntryResult result)45 void RecordSyncOpenResult(net::CacheType cache_type, OpenEntryResult result) {
46   DCHECK_LT(result, OPEN_ENTRY_MAX);
47   SIMPLE_CACHE_LOCAL(ENUMERATION, "SyncOpenResult", cache_type, result,
48                      OPEN_ENTRY_MAX);
49 }
50 
RecordWriteResult(net::CacheType cache_type,SyncWriteResult result)51 void RecordWriteResult(net::CacheType cache_type, SyncWriteResult result) {
52   SIMPLE_CACHE_LOCAL(ENUMERATION, "SyncWriteResult", cache_type, result,
53                      SYNC_WRITE_RESULT_MAX);
54 }
55 
RecordCheckEOFResult(net::CacheType cache_type,CheckEOFResult result)56 void RecordCheckEOFResult(net::CacheType cache_type, CheckEOFResult result) {
57   SIMPLE_CACHE_LOCAL(ENUMERATION, "SyncCheckEOFResult", cache_type, result,
58                      CHECK_EOF_RESULT_MAX);
59 }
60 
RecordCloseResult(net::CacheType cache_type,CloseResult result)61 void RecordCloseResult(net::CacheType cache_type, CloseResult result) {
62   SIMPLE_CACHE_LOCAL(ENUMERATION, "SyncCloseResult", cache_type, result,
63                      CLOSE_RESULT_MAX);
64 }
65 
RecordOpenPrefetchMode(net::CacheType cache_type,OpenPrefetchMode mode)66 void RecordOpenPrefetchMode(net::CacheType cache_type, OpenPrefetchMode mode) {
67   SIMPLE_CACHE_UMA(ENUMERATION, "SyncOpenPrefetchMode", cache_type, mode,
68                    OPEN_PREFETCH_MAX);
69 }
70 
RecordDiskCreateLatency(net::CacheType cache_type,base::TimeDelta delay)71 void RecordDiskCreateLatency(net::CacheType cache_type, base::TimeDelta delay) {
72   SIMPLE_CACHE_LOCAL(TIMES, "DiskCreateLatency", cache_type, delay);
73 }
74 
CanOmitEmptyFile(int file_index)75 bool CanOmitEmptyFile(int file_index) {
76   DCHECK_GE(file_index, 0);
77   DCHECK_LT(file_index, kSimpleEntryNormalFileCount);
78   return file_index == simple_util::GetFileIndexFromStreamIndex(2);
79 }
80 
TruncatePath(const FilePath & filename_to_truncate,BackendFileOperations * file_operations)81 bool TruncatePath(const FilePath& filename_to_truncate,
82                   BackendFileOperations* file_operations) {
83   int flags = base::File::FLAG_OPEN | base::File::FLAG_READ |
84               base::File::FLAG_WRITE | base::File::FLAG_WIN_SHARE_DELETE;
85   base::File file_to_truncate =
86       file_operations->OpenFile(filename_to_truncate, flags);
87   if (!file_to_truncate.IsValid())
88     return false;
89   if (!file_to_truncate.SetLength(0))
90     return false;
91   return true;
92 }
93 
CalculateSHA256OfKey(const std::string & key,net::SHA256HashValue * out_hash_value)94 void CalculateSHA256OfKey(const std::string& key,
95                           net::SHA256HashValue* out_hash_value) {
96   std::unique_ptr<crypto::SecureHash> hash(
97       crypto::SecureHash::Create(crypto::SecureHash::SHA256));
98   hash->Update(key.data(), key.size());
99   hash->Finish(out_hash_value, sizeof(*out_hash_value));
100 }
101 
SubFileForFileIndex(int file_index)102 SimpleFileTracker::SubFile SubFileForFileIndex(int file_index) {
103   DCHECK_GT(kSimpleEntryNormalFileCount, file_index);
104   return file_index == 0 ? SimpleFileTracker::SubFile::FILE_0
105                          : SimpleFileTracker::SubFile::FILE_1;
106 }
107 
FileIndexForSubFile(SimpleFileTracker::SubFile sub_file)108 int FileIndexForSubFile(SimpleFileTracker::SubFile sub_file) {
109   DCHECK_NE(SimpleFileTracker::SubFile::FILE_SPARSE, sub_file);
110   return sub_file == SimpleFileTracker::SubFile::FILE_0 ? 0 : 1;
111 }
112 
113 }  // namespace
114 
115 // Helper class to track a range of data prefetched from a file.
116 class SimpleSynchronousEntry::PrefetchData final {
117  public:
PrefetchData(size_t file_size)118   explicit PrefetchData(size_t file_size)
119       : file_size_(file_size), earliest_requested_offset_(file_size) {}
120 
121   // Returns true if the specified range within the file has been completely
122   // prefetched.  Returns false if any part of the range has not been
123   // prefetched.
HasData(size_t offset,size_t length)124   bool HasData(size_t offset, size_t length) {
125     size_t end = 0;
126     if (!base::CheckAdd(offset, length).AssignIfValid(&end))
127       return false;
128     UpdateEarliestOffset(offset);
129     return offset >= offset_in_file_ &&
130            end <= (offset_in_file_ + buffer_->size());
131   }
132 
133   // Read the given range out of the prefetch buffer into the target
134   // destination buffer.  If the range is not wholely contained within
135   // the prefetch buffer than no data will be written to the target
136   // buffer.  Returns true if the range has been copied.
ReadData(size_t offset,size_t length,char * dest)137   bool ReadData(size_t offset, size_t length, char* dest) {
138     DCHECK(dest);
139     if (!length)
140       return true;
141     if (!HasData(offset, length))
142       return false;
143     DCHECK(offset >= offset_in_file_);
144     size_t buffer_offset = offset - offset_in_file_;
145     memcpy(dest, buffer_->data() + buffer_offset, length);
146     return true;
147   }
148 
149   // Populate the prefetch buffer from the given file and range.  Returns
150   // true if the data is successfully read.
PrefetchFromFile(SimpleFileTracker::FileHandle * file,size_t offset,size_t length)151   bool PrefetchFromFile(SimpleFileTracker::FileHandle* file,
152                         size_t offset,
153                         size_t length) {
154     DCHECK(file);
155     if (!buffer_->empty())
156       return false;
157     buffer_->resize(length);
158     if (file->get()->Read(offset, buffer_->data(), length) !=
159         static_cast<int>(length)) {
160       buffer_->resize(0);
161       return false;
162     }
163     offset_in_file_ = offset;
164     return true;
165   }
166 
167   // Return how much trailing data has been requested via HasData() or
168   // ReadData().  The intent is that this value can be used to tune
169   // future prefetching behavior.
GetDesiredTrailerPrefetchSize() const170   size_t GetDesiredTrailerPrefetchSize() const {
171     return file_size_ - earliest_requested_offset_;
172   }
173 
174  private:
175   // Track the earliest offset requested in order to return an optimal trailer
176   // prefetch amount in GetDesiredTrailerPrefetchSize().
UpdateEarliestOffset(size_t offset)177   void UpdateEarliestOffset(size_t offset) {
178     DCHECK_LE(earliest_requested_offset_, file_size_);
179     earliest_requested_offset_ = std::min(earliest_requested_offset_, offset);
180   }
181 
182   const size_t file_size_;
183 
184   // Prefer to read the prefetch data into a stack buffer to minimize
185   // memory pressure on the OS disk cache.
186   base::StackVector<char, 1024> buffer_;
187   size_t offset_in_file_ = 0;
188 
189   size_t earliest_requested_offset_;
190 };
191 
192 class SimpleSynchronousEntry::ScopedFileOperationsBinding final {
193  public:
ScopedFileOperationsBinding(SimpleSynchronousEntry * owner,BackendFileOperations ** file_operations)194   ScopedFileOperationsBinding(SimpleSynchronousEntry* owner,
195                               BackendFileOperations** file_operations)
196       : owner_(owner),
197         file_operations_(owner->unbound_file_operations_->Bind(
198             base::SequencedTaskRunner::GetCurrentDefault())) {
199     *file_operations = file_operations_.get();
200   }
~ScopedFileOperationsBinding()201   ~ScopedFileOperationsBinding() {
202     owner_->unbound_file_operations_ = file_operations_->Unbind();
203   }
204 
205  private:
206   const raw_ptr<SimpleSynchronousEntry> owner_;
207   std::unique_ptr<BackendFileOperations> file_operations_;
208 };
209 
210 using simple_util::GetEntryHashKey;
211 using simple_util::GetFilenameFromEntryFileKeyAndFileIndex;
212 using simple_util::GetSparseFilenameFromEntryFileKey;
213 using simple_util::GetHeaderSize;
214 using simple_util::GetDataSizeFromFileSize;
215 using simple_util::GetFileSizeFromDataSize;
216 using simple_util::GetFileIndexFromStreamIndex;
217 
218 BASE_FEATURE(kSimpleCachePrefetchExperiment,
219              "SimpleCachePrefetchExperiment2",
220              base::FEATURE_DISABLED_BY_DEFAULT);
221 
222 const char kSimpleCacheFullPrefetchBytesParam[] = "FullPrefetchBytes";
223 constexpr base::FeatureParam<int> kSimpleCacheFullPrefetchSize{
224     &kSimpleCachePrefetchExperiment, kSimpleCacheFullPrefetchBytesParam, 0};
225 
226 const char kSimpleCacheTrailerPrefetchSpeculativeBytesParam[] =
227     "TrailerPrefetchSpeculativeBytes";
228 constexpr base::FeatureParam<int> kSimpleCacheTrailerPrefetchSpeculativeBytes{
229     &kSimpleCachePrefetchExperiment,
230     kSimpleCacheTrailerPrefetchSpeculativeBytesParam, 0};
231 
GetSimpleCacheFullPrefetchSize()232 int GetSimpleCacheFullPrefetchSize() {
233   return kSimpleCacheFullPrefetchSize.Get();
234 }
235 
GetSimpleCacheTrailerPrefetchSize(int hint_size)236 int GetSimpleCacheTrailerPrefetchSize(int hint_size) {
237   if (hint_size > 0)
238     return hint_size;
239   return kSimpleCacheTrailerPrefetchSpeculativeBytes.Get();
240 }
241 
SimpleEntryStat(base::Time last_used,base::Time last_modified,const int32_t data_size[],const int32_t sparse_data_size)242 SimpleEntryStat::SimpleEntryStat(base::Time last_used,
243                                  base::Time last_modified,
244                                  const int32_t data_size[],
245                                  const int32_t sparse_data_size)
246     : last_used_(last_used),
247       last_modified_(last_modified),
248       sparse_data_size_(sparse_data_size) {
249   memcpy(data_size_, data_size, sizeof(data_size_));
250 }
251 
252 // These size methods all assume the presence of the SHA256 on stream zero,
253 // since this version of the cache always writes it. In the read case, it may
254 // not be present and these methods can't be relied upon.
255 
GetOffsetInFile(size_t key_length,int offset,int stream_index) const256 int SimpleEntryStat::GetOffsetInFile(size_t key_length,
257                                      int offset,
258                                      int stream_index) const {
259   const size_t headers_size = sizeof(SimpleFileHeader) + key_length;
260   const size_t additional_offset =
261       stream_index == 0 ? data_size_[1] + sizeof(SimpleFileEOF) : 0;
262   return headers_size + offset + additional_offset;
263 }
264 
GetEOFOffsetInFile(size_t key_length,int stream_index) const265 int SimpleEntryStat::GetEOFOffsetInFile(size_t key_length,
266                                         int stream_index) const {
267   size_t additional_offset;
268   if (stream_index != 0)
269     additional_offset = 0;
270   else
271     additional_offset = sizeof(net::SHA256HashValue);
272   return additional_offset +
273          GetOffsetInFile(key_length, data_size_[stream_index], stream_index);
274 }
275 
GetLastEOFOffsetInFile(size_t key_length,int stream_index) const276 int SimpleEntryStat::GetLastEOFOffsetInFile(size_t key_length,
277                                             int stream_index) const {
278   if (stream_index == 1)
279     return GetEOFOffsetInFile(key_length, 0);
280   return GetEOFOffsetInFile(key_length, stream_index);
281 }
282 
GetFileSize(size_t key_length,int file_index) const283 int64_t SimpleEntryStat::GetFileSize(size_t key_length, int file_index) const {
284   int32_t total_data_size;
285   if (file_index == 0) {
286     total_data_size = data_size_[0] + data_size_[1] +
287                       sizeof(net::SHA256HashValue) + sizeof(SimpleFileEOF);
288   } else {
289     total_data_size = data_size_[2];
290   }
291   return GetFileSizeFromDataSize(key_length, total_data_size);
292 }
293 
SimpleStreamPrefetchData()294 SimpleStreamPrefetchData::SimpleStreamPrefetchData()
295     : stream_crc32(crc32(0, Z_NULL, 0)) {}
296 
297 SimpleStreamPrefetchData::~SimpleStreamPrefetchData() = default;
298 
SimpleEntryCreationResults(SimpleEntryStat entry_stat)299 SimpleEntryCreationResults::SimpleEntryCreationResults(
300     SimpleEntryStat entry_stat)
301     : sync_entry(nullptr), entry_stat(entry_stat) {}
302 
303 SimpleEntryCreationResults::~SimpleEntryCreationResults() = default;
304 
CRCRecord()305 SimpleSynchronousEntry::CRCRecord::CRCRecord() : index(-1),
306                                                  has_crc32(false),
307                                                  data_crc32(0) {
308 }
309 
CRCRecord(int index_p,bool has_crc32_p,uint32_t data_crc32_p)310 SimpleSynchronousEntry::CRCRecord::CRCRecord(int index_p,
311                                              bool has_crc32_p,
312                                              uint32_t data_crc32_p)
313     : index(index_p), has_crc32(has_crc32_p), data_crc32(data_crc32_p) {}
314 
ReadRequest(int index_p,int offset_p,int buf_len_p)315 SimpleSynchronousEntry::ReadRequest::ReadRequest(int index_p,
316                                                  int offset_p,
317                                                  int buf_len_p)
318     : index(index_p), offset(offset_p), buf_len(buf_len_p) {}
319 
WriteRequest(int index_p,int offset_p,int buf_len_p,uint32_t previous_crc32_p,bool truncate_p,bool doomed_p,bool request_update_crc_p)320 SimpleSynchronousEntry::WriteRequest::WriteRequest(int index_p,
321                                                    int offset_p,
322                                                    int buf_len_p,
323                                                    uint32_t previous_crc32_p,
324                                                    bool truncate_p,
325                                                    bool doomed_p,
326                                                    bool request_update_crc_p)
327     : index(index_p),
328       offset(offset_p),
329       buf_len(buf_len_p),
330       previous_crc32(previous_crc32_p),
331       truncate(truncate_p),
332       doomed(doomed_p),
333       request_update_crc(request_update_crc_p) {}
334 
SparseRequest(int64_t sparse_offset_p,int buf_len_p)335 SimpleSynchronousEntry::SparseRequest::SparseRequest(int64_t sparse_offset_p,
336                                                      int buf_len_p)
337     : sparse_offset(sparse_offset_p), buf_len(buf_len_p) {}
338 
339 // static
OpenEntry(net::CacheType cache_type,const FilePath & path,const std::string & key,const uint64_t entry_hash,SimpleFileTracker * file_tracker,std::unique_ptr<UnboundBackendFileOperations> file_operations,int32_t trailer_prefetch_size,SimpleEntryCreationResults * out_results)340 void SimpleSynchronousEntry::OpenEntry(
341     net::CacheType cache_type,
342     const FilePath& path,
343     const std::string& key,
344     const uint64_t entry_hash,
345     SimpleFileTracker* file_tracker,
346     std::unique_ptr<UnboundBackendFileOperations> file_operations,
347     int32_t trailer_prefetch_size,
348     SimpleEntryCreationResults* out_results) {
349   base::TimeTicks start_sync_open_entry = base::TimeTicks::Now();
350 
351   auto sync_entry = std::make_unique<SimpleSynchronousEntry>(
352       cache_type, path, key, entry_hash, file_tracker,
353       std::move(file_operations), trailer_prefetch_size);
354   {
355     BackendFileOperations* bound_file_operations = nullptr;
356     ScopedFileOperationsBinding binding(sync_entry.get(),
357                                         &bound_file_operations);
358     out_results->result = sync_entry->InitializeForOpen(
359         bound_file_operations, &out_results->entry_stat,
360         out_results->stream_prefetch_data);
361   }
362   if (out_results->result != net::OK) {
363     sync_entry->Doom();
364     sync_entry->CloseFiles();
365     out_results->sync_entry = nullptr;
366     out_results->unbound_file_operations =
367         std::move(sync_entry->unbound_file_operations_);
368     out_results->stream_prefetch_data[0].data = nullptr;
369     out_results->stream_prefetch_data[1].data = nullptr;
370     return;
371   }
372   SIMPLE_CACHE_UMA(TIMES, "DiskOpenLatency", cache_type,
373                    base::TimeTicks::Now() - start_sync_open_entry);
374   out_results->sync_entry = sync_entry.release();
375   out_results->computed_trailer_prefetch_size =
376       out_results->sync_entry->computed_trailer_prefetch_size();
377 }
378 
379 // static
CreateEntry(net::CacheType cache_type,const FilePath & path,const std::string & key,const uint64_t entry_hash,SimpleFileTracker * file_tracker,std::unique_ptr<UnboundBackendFileOperations> file_operations,SimpleEntryCreationResults * out_results)380 void SimpleSynchronousEntry::CreateEntry(
381     net::CacheType cache_type,
382     const FilePath& path,
383     const std::string& key,
384     const uint64_t entry_hash,
385     SimpleFileTracker* file_tracker,
386     std::unique_ptr<UnboundBackendFileOperations> file_operations,
387     SimpleEntryCreationResults* out_results) {
388   DCHECK_EQ(entry_hash, GetEntryHashKey(key));
389   base::TimeTicks start_sync_create_entry = base::TimeTicks::Now();
390 
391   auto sync_entry = std::make_unique<SimpleSynchronousEntry>(
392       cache_type, path, key, entry_hash, file_tracker,
393       std::move(file_operations), -1);
394   {
395     BackendFileOperations* bound_file_operations = nullptr;
396     ScopedFileOperationsBinding binding(sync_entry.get(),
397                                         &bound_file_operations);
398     out_results->result = sync_entry->InitializeForCreate(
399         bound_file_operations, &out_results->entry_stat);
400   }
401   if (out_results->result != net::OK) {
402     if (out_results->result != net::ERR_FILE_EXISTS)
403       sync_entry->Doom();
404     sync_entry->CloseFiles();
405     out_results->unbound_file_operations =
406         std::move(sync_entry->unbound_file_operations_);
407     out_results->sync_entry = nullptr;
408     return;
409   }
410   out_results->sync_entry = sync_entry.release();
411   out_results->created = true;
412   RecordDiskCreateLatency(cache_type,
413                           base::TimeTicks::Now() - start_sync_create_entry);
414 }
415 
416 // static
OpenOrCreateEntry(net::CacheType cache_type,const FilePath & path,const std::string & key,const uint64_t entry_hash,OpenEntryIndexEnum index_state,bool optimistic_create,SimpleFileTracker * file_tracker,std::unique_ptr<UnboundBackendFileOperations> file_operations,int32_t trailer_prefetch_size,SimpleEntryCreationResults * out_results)417 void SimpleSynchronousEntry::OpenOrCreateEntry(
418     net::CacheType cache_type,
419     const FilePath& path,
420     const std::string& key,
421     const uint64_t entry_hash,
422     OpenEntryIndexEnum index_state,
423     bool optimistic_create,
424     SimpleFileTracker* file_tracker,
425     std::unique_ptr<UnboundBackendFileOperations> file_operations,
426     int32_t trailer_prefetch_size,
427     SimpleEntryCreationResults* out_results) {
428   base::TimeTicks start = base::TimeTicks::Now();
429   if (index_state == INDEX_MISS) {
430     // Try to just create.
431     auto sync_entry = std::make_unique<SimpleSynchronousEntry>(
432         cache_type, path, key, entry_hash, file_tracker,
433         std::move(file_operations), trailer_prefetch_size);
434     {
435       BackendFileOperations* bound_file_operations = nullptr;
436       ScopedFileOperationsBinding binding(sync_entry.get(),
437                                           &bound_file_operations);
438       out_results->result = sync_entry->InitializeForCreate(
439           bound_file_operations, &out_results->entry_stat);
440     }
441     switch (out_results->result) {
442       case net::OK:
443         out_results->sync_entry = sync_entry.release();
444         out_results->created = true;
445         RecordDiskCreateLatency(cache_type, base::TimeTicks::Now() - start);
446         return;
447       case net::ERR_FILE_EXISTS:
448         // Our index was messed up.
449         if (optimistic_create) {
450           // In this case, ::OpenOrCreateEntry already returned claiming it made
451           // a new entry. Try extra-hard to make that the actual case.
452           sync_entry->Doom();
453           sync_entry->CloseFiles();
454           file_operations = std::move(sync_entry->unbound_file_operations_);
455           sync_entry = nullptr;
456           CreateEntry(cache_type, path, key, entry_hash, file_tracker,
457                       std::move(file_operations), out_results);
458           return;
459         }
460         // Otherwise can just try opening.
461         break;
462       default:
463         // Trouble. Fail this time.
464         sync_entry->Doom();
465         sync_entry->CloseFiles();
466         out_results->unbound_file_operations =
467             std::move(sync_entry->unbound_file_operations_);
468         return;
469     }
470     file_operations = std::move(sync_entry->unbound_file_operations_);
471   }
472 
473   DCHECK(file_operations);
474   // Try open, then if that fails create.
475   OpenEntry(cache_type, path, key, entry_hash, file_tracker,
476             std::move(file_operations), trailer_prefetch_size, out_results);
477   if (out_results->sync_entry)
478     return;
479   file_operations = std::move(out_results->unbound_file_operations);
480   DCHECK(file_operations);
481   CreateEntry(cache_type, path, key, entry_hash, file_tracker,
482               std::move(file_operations), out_results);
483 }
484 
485 // static
DeleteEntryFiles(const FilePath & path,net::CacheType cache_type,uint64_t entry_hash,std::unique_ptr<UnboundBackendFileOperations> unbound_file_operations)486 int SimpleSynchronousEntry::DeleteEntryFiles(
487     const FilePath& path,
488     net::CacheType cache_type,
489     uint64_t entry_hash,
490     std::unique_ptr<UnboundBackendFileOperations> unbound_file_operations) {
491   auto file_operations = unbound_file_operations->Bind(
492       base::SequencedTaskRunner::GetCurrentDefault());
493   return DeleteEntryFilesInternal(path, cache_type, entry_hash,
494                                   file_operations.get());
495 }
496 
497 // static
DeleteEntryFilesInternal(const FilePath & path,net::CacheType cache_type,uint64_t entry_hash,BackendFileOperations * file_operations)498 int SimpleSynchronousEntry::DeleteEntryFilesInternal(
499     const FilePath& path,
500     net::CacheType cache_type,
501     uint64_t entry_hash,
502     BackendFileOperations* file_operations) {
503   base::TimeTicks start = base::TimeTicks::Now();
504   const bool deleted_well =
505       DeleteFilesForEntryHash(path, entry_hash, file_operations);
506   SIMPLE_CACHE_UMA(TIMES, "DiskDoomLatency", cache_type,
507                    base::TimeTicks::Now() - start);
508   return deleted_well ? net::OK : net::ERR_FAILED;
509 }
510 
Doom()511 int SimpleSynchronousEntry::Doom() {
512   BackendFileOperations* file_operations = nullptr;
513   ScopedFileOperationsBinding binding(this, &file_operations);
514   return DoomInternal(file_operations);
515 }
516 
DoomInternal(BackendFileOperations * file_operations)517 int SimpleSynchronousEntry::DoomInternal(
518     BackendFileOperations* file_operations) {
519   if (entry_file_key_.doom_generation != 0u) {
520     // Already doomed.
521     return true;
522   }
523 
524   if (have_open_files_) {
525     base::TimeTicks start = base::TimeTicks::Now();
526     bool ok = true;
527     SimpleFileTracker::EntryFileKey orig_key = entry_file_key_;
528     file_tracker_->Doom(this, &entry_file_key_);
529 
530     for (int i = 0; i < kSimpleEntryNormalFileCount; ++i) {
531       if (!empty_file_omitted_[i]) {
532         base::File::Error out_error;
533         FilePath old_name = path_.AppendASCII(
534             GetFilenameFromEntryFileKeyAndFileIndex(orig_key, i));
535         FilePath new_name = path_.AppendASCII(
536             GetFilenameFromEntryFileKeyAndFileIndex(entry_file_key_, i));
537         ok = file_operations->ReplaceFile(old_name, new_name, &out_error) && ok;
538       }
539     }
540 
541     if (sparse_file_open()) {
542       base::File::Error out_error;
543       FilePath old_name =
544           path_.AppendASCII(GetSparseFilenameFromEntryFileKey(orig_key));
545       FilePath new_name =
546           path_.AppendASCII(GetSparseFilenameFromEntryFileKey(entry_file_key_));
547       ok = file_operations->ReplaceFile(old_name, new_name, &out_error) && ok;
548     }
549 
550     SIMPLE_CACHE_UMA(TIMES, "DiskDoomLatency", cache_type_,
551                      base::TimeTicks::Now() - start);
552 
553     return ok ? net::OK : net::ERR_FAILED;
554   } else {
555     // No one has ever called Create or Open on us, so we don't have to worry
556     // about being accessible to other ops after doom.
557     return DeleteEntryFilesInternal(
558         path_, cache_type_, entry_file_key_.entry_hash, file_operations);
559   }
560 }
561 
562 // static
TruncateEntryFiles(const base::FilePath & path,uint64_t entry_hash,std::unique_ptr<UnboundBackendFileOperations> unbound_file_operations)563 int SimpleSynchronousEntry::TruncateEntryFiles(
564     const base::FilePath& path,
565     uint64_t entry_hash,
566     std::unique_ptr<UnboundBackendFileOperations> unbound_file_operations) {
567   auto file_operations = unbound_file_operations->Bind(
568       base::SequencedTaskRunner::GetCurrentDefault());
569   const bool deleted_well =
570       TruncateFilesForEntryHash(path, entry_hash, file_operations.get());
571   return deleted_well ? net::OK : net::ERR_FAILED;
572 }
573 
574 // static
DeleteEntrySetFiles(const std::vector<uint64_t> * key_hashes,const FilePath & path,std::unique_ptr<UnboundBackendFileOperations> unbound_file_operations)575 int SimpleSynchronousEntry::DeleteEntrySetFiles(
576     const std::vector<uint64_t>* key_hashes,
577     const FilePath& path,
578     std::unique_ptr<UnboundBackendFileOperations> unbound_file_operations) {
579   auto file_operations = unbound_file_operations->Bind(
580       base::SequencedTaskRunner::GetCurrentDefault());
581   const size_t did_delete_count = base::ranges::count_if(
582       *key_hashes, [&path, &file_operations](const uint64_t& key_hash) {
583         return SimpleSynchronousEntry::DeleteFilesForEntryHash(
584             path, key_hash, file_operations.get());
585       });
586   return (did_delete_count == key_hashes->size()) ? net::OK : net::ERR_FAILED;
587 }
588 
ReadData(const ReadRequest & in_entry_op,SimpleEntryStat * entry_stat,net::IOBuffer * out_buf,ReadResult * out_result)589 void SimpleSynchronousEntry::ReadData(const ReadRequest& in_entry_op,
590                                       SimpleEntryStat* entry_stat,
591                                       net::IOBuffer* out_buf,
592                                       ReadResult* out_result) {
593   DCHECK(initialized_);
594   DCHECK_NE(0, in_entry_op.index);
595   BackendFileOperations* file_operations = nullptr;
596   ScopedFileOperationsBinding binding(this, &file_operations);
597   int file_index = GetFileIndexFromStreamIndex(in_entry_op.index);
598   SimpleFileTracker::FileHandle file = file_tracker_->Acquire(
599       file_operations, this, SubFileForFileIndex(file_index));
600 
601   out_result->crc_updated = false;
602   if (!file.IsOK() || (header_and_key_check_needed_[file_index] &&
603                        !CheckHeaderAndKey(file.get(), file_index))) {
604     out_result->result = net::ERR_FAILED;
605     DoomInternal(file_operations);
606     return;
607   }
608   const int64_t file_offset = entry_stat->GetOffsetInFile(
609       key_.size(), in_entry_op.offset, in_entry_op.index);
610   // Zero-length reads and reads to the empty streams of omitted files should
611   // be handled in the SimpleEntryImpl.
612   DCHECK_GT(in_entry_op.buf_len, 0);
613   DCHECK(!empty_file_omitted_[file_index]);
614   int bytes_read =
615       file->Read(file_offset, out_buf->data(), in_entry_op.buf_len);
616   if (bytes_read > 0) {
617     entry_stat->set_last_used(Time::Now());
618     if (in_entry_op.request_update_crc) {
619       out_result->updated_crc32 = simple_util::IncrementalCrc32(
620           in_entry_op.previous_crc32, out_buf->data(), bytes_read);
621       out_result->crc_updated = true;
622       // Verify checksum after last read, if we've been asked to.
623       if (in_entry_op.request_verify_crc &&
624           in_entry_op.offset + bytes_read ==
625               entry_stat->data_size(in_entry_op.index)) {
626         int checksum_result =
627             CheckEOFRecord(file_operations, file.get(), in_entry_op.index,
628                            *entry_stat, out_result->updated_crc32);
629         if (checksum_result < 0) {
630           out_result->result = checksum_result;
631           return;
632         }
633       }
634     }
635   }
636   if (bytes_read >= 0) {
637     out_result->result = bytes_read;
638   } else {
639     out_result->result = net::ERR_CACHE_READ_FAILURE;
640     DoomInternal(file_operations);
641   }
642 }
643 
WriteData(const WriteRequest & in_entry_op,net::IOBuffer * in_buf,SimpleEntryStat * out_entry_stat,WriteResult * out_write_result)644 void SimpleSynchronousEntry::WriteData(const WriteRequest& in_entry_op,
645                                        net::IOBuffer* in_buf,
646                                        SimpleEntryStat* out_entry_stat,
647                                        WriteResult* out_write_result) {
648   BackendFileOperations* file_operations = nullptr;
649   ScopedFileOperationsBinding binding(this, &file_operations);
650   base::ElapsedTimer write_time;
651   DCHECK(initialized_);
652   DCHECK_NE(0, in_entry_op.index);
653   int index = in_entry_op.index;
654   int file_index = GetFileIndexFromStreamIndex(index);
655   if (header_and_key_check_needed_[file_index] &&
656       !empty_file_omitted_[file_index]) {
657     SimpleFileTracker::FileHandle file = file_tracker_->Acquire(
658         file_operations, this, SubFileForFileIndex(file_index));
659     if (!file.IsOK() || !CheckHeaderAndKey(file.get(), file_index)) {
660       out_write_result->result = net::ERR_FAILED;
661       DoomInternal(file_operations);
662       return;
663     }
664   }
665   int offset = in_entry_op.offset;
666   int buf_len = in_entry_op.buf_len;
667   bool truncate = in_entry_op.truncate;
668   bool doomed = in_entry_op.doomed;
669   const int64_t file_offset = out_entry_stat->GetOffsetInFile(
670       key_.size(), in_entry_op.offset, in_entry_op.index);
671   bool extending_by_write = offset + buf_len > out_entry_stat->data_size(index);
672 
673   if (empty_file_omitted_[file_index]) {
674     // Don't create a new file if the entry has been doomed, to avoid it being
675     // mixed up with a newly-created entry with the same key.
676     if (doomed) {
677       DLOG(WARNING) << "Rejecting write to lazily omitted stream "
678                     << in_entry_op.index << " of doomed cache entry.";
679       RecordWriteResult(cache_type_,
680                         SYNC_WRITE_RESULT_LAZY_STREAM_ENTRY_DOOMED);
681       out_write_result->result = net::ERR_CACHE_WRITE_FAILURE;
682       return;
683     }
684     base::File::Error error;
685     if (!MaybeCreateFile(file_operations, file_index, FILE_REQUIRED, &error)) {
686       RecordWriteResult(cache_type_, SYNC_WRITE_RESULT_LAZY_CREATE_FAILURE);
687       DoomInternal(file_operations);
688       out_write_result->result = net::ERR_CACHE_WRITE_FAILURE;
689       return;
690     }
691     if (!InitializeCreatedFile(file_operations, file_index)) {
692       RecordWriteResult(cache_type_, SYNC_WRITE_RESULT_LAZY_INITIALIZE_FAILURE);
693       DoomInternal(file_operations);
694       out_write_result->result = net::ERR_CACHE_WRITE_FAILURE;
695       return;
696     }
697   }
698   DCHECK(!empty_file_omitted_[file_index]);
699 
700   // This needs to be grabbed after the above block, since that's what may
701   // create the file (for stream 2/file 1).
702   SimpleFileTracker::FileHandle file = file_tracker_->Acquire(
703       file_operations, this, SubFileForFileIndex(file_index));
704   if (!file.IsOK()) {
705     out_write_result->result = net::ERR_FAILED;
706     DoomInternal(file_operations);
707     return;
708   }
709 
710   if (extending_by_write) {
711     // The EOF record and the eventual stream afterward need to be zeroed out.
712     const int64_t file_eof_offset =
713         out_entry_stat->GetEOFOffsetInFile(key_.size(), index);
714     if (!file->SetLength(file_eof_offset)) {
715       RecordWriteResult(cache_type_, SYNC_WRITE_RESULT_PRETRUNCATE_FAILURE);
716       DoomInternal(file_operations);
717       out_write_result->result = net::ERR_CACHE_WRITE_FAILURE;
718       return;
719     }
720   }
721   if (buf_len > 0) {
722     if (file->Write(file_offset, in_buf->data(), buf_len) != buf_len) {
723       RecordWriteResult(cache_type_, SYNC_WRITE_RESULT_WRITE_FAILURE);
724       DoomInternal(file_operations);
725       out_write_result->result = net::ERR_CACHE_WRITE_FAILURE;
726       return;
727     }
728   }
729   if (!truncate && (buf_len > 0 || !extending_by_write)) {
730     out_entry_stat->set_data_size(
731         index, std::max(out_entry_stat->data_size(index), offset + buf_len));
732   } else {
733     out_entry_stat->set_data_size(index, offset + buf_len);
734     int file_eof_offset =
735         out_entry_stat->GetLastEOFOffsetInFile(key_.size(), index);
736     if (!file->SetLength(file_eof_offset)) {
737       RecordWriteResult(cache_type_, SYNC_WRITE_RESULT_TRUNCATE_FAILURE);
738       DoomInternal(file_operations);
739       out_write_result->result = net::ERR_CACHE_WRITE_FAILURE;
740       return;
741     }
742   }
743 
744   if (in_entry_op.request_update_crc && buf_len > 0) {
745     out_write_result->updated_crc32 = simple_util::IncrementalCrc32(
746         in_entry_op.previous_crc32, in_buf->data(), buf_len);
747     out_write_result->crc_updated = true;
748   }
749 
750   SIMPLE_CACHE_UMA(TIMES, "DiskWriteLatency", cache_type_,
751                    write_time.Elapsed());
752   RecordWriteResult(cache_type_, SYNC_WRITE_RESULT_SUCCESS);
753   base::Time modification_time = Time::Now();
754   out_entry_stat->set_last_used(modification_time);
755   out_entry_stat->set_last_modified(modification_time);
756   out_write_result->result = buf_len;
757 }
758 
ReadSparseData(const SparseRequest & in_entry_op,net::IOBuffer * out_buf,base::Time * out_last_used,int * out_result)759 void SimpleSynchronousEntry::ReadSparseData(const SparseRequest& in_entry_op,
760                                             net::IOBuffer* out_buf,
761                                             base::Time* out_last_used,
762                                             int* out_result) {
763   DCHECK(initialized_);
764   BackendFileOperations* file_operations = nullptr;
765   ScopedFileOperationsBinding binding(this, &file_operations);
766   int64_t offset = in_entry_op.sparse_offset;
767   int buf_len = in_entry_op.buf_len;
768 
769   char* buf = out_buf->data();
770   int read_so_far = 0;
771 
772   if (!sparse_file_open()) {
773     *out_result = 0;
774     return;
775   }
776 
777   SimpleFileTracker::FileHandle sparse_file = file_tracker_->Acquire(
778       file_operations, this, SimpleFileTracker::SubFile::FILE_SPARSE);
779   if (!sparse_file.IsOK()) {
780     DoomInternal(file_operations);
781     *out_result = net::ERR_CACHE_READ_FAILURE;
782     return;
783   }
784 
785   // Find the first sparse range at or after the requested offset.
786   auto it = sparse_ranges_.lower_bound(offset);
787 
788   if (it != sparse_ranges_.begin()) {
789     // Hop back one range and read the one overlapping with the start.
790     --it;
791     SparseRange* found_range = &it->second;
792     DCHECK_EQ(it->first, found_range->offset);
793     if (found_range->offset + found_range->length > offset) {
794       DCHECK_GE(found_range->length, 0);
795       DCHECK_LE(found_range->length, std::numeric_limits<int32_t>::max());
796       DCHECK_GE(offset - found_range->offset, 0);
797       DCHECK_LE(offset - found_range->offset,
798                 std::numeric_limits<int32_t>::max());
799       int net_offset = static_cast<int>(offset - found_range->offset);
800       int range_len_after_offset =
801           static_cast<int>(found_range->length - net_offset);
802       DCHECK_GE(range_len_after_offset, 0);
803 
804       int len_to_read = std::min(buf_len, range_len_after_offset);
805       if (!ReadSparseRange(sparse_file.get(), found_range, net_offset,
806                            len_to_read, buf)) {
807         DoomInternal(file_operations);
808         *out_result = net::ERR_CACHE_READ_FAILURE;
809         return;
810       }
811       read_so_far += len_to_read;
812     }
813     ++it;
814   }
815 
816   // Keep reading until the buffer is full or there is not another contiguous
817   // range.
818   while (read_so_far < buf_len &&
819          it != sparse_ranges_.end() &&
820          it->second.offset == offset + read_so_far) {
821     SparseRange* found_range = &it->second;
822     DCHECK_EQ(it->first, found_range->offset);
823     int range_len = base::saturated_cast<int>(found_range->length);
824     int len_to_read = std::min(buf_len - read_so_far, range_len);
825     if (!ReadSparseRange(sparse_file.get(), found_range, 0, len_to_read,
826                          buf + read_so_far)) {
827       DoomInternal(file_operations);
828       *out_result = net::ERR_CACHE_READ_FAILURE;
829       return;
830     }
831     read_so_far += len_to_read;
832     ++it;
833   }
834 
835   *out_result = read_so_far;
836 }
837 
WriteSparseData(const SparseRequest & in_entry_op,net::IOBuffer * in_buf,uint64_t max_sparse_data_size,SimpleEntryStat * out_entry_stat,int * out_result)838 void SimpleSynchronousEntry::WriteSparseData(const SparseRequest& in_entry_op,
839                                              net::IOBuffer* in_buf,
840                                              uint64_t max_sparse_data_size,
841                                              SimpleEntryStat* out_entry_stat,
842                                              int* out_result) {
843   DCHECK(initialized_);
844   BackendFileOperations* file_operations = nullptr;
845   ScopedFileOperationsBinding binding(this, &file_operations);
846   int64_t offset = in_entry_op.sparse_offset;
847   int buf_len = in_entry_op.buf_len;
848 
849   const char* buf = in_buf->data();
850   int written_so_far = 0;
851   int appended_so_far = 0;
852 
853   if (!sparse_file_open() && !CreateSparseFile(file_operations)) {
854     DoomInternal(file_operations);
855     *out_result = net::ERR_CACHE_WRITE_FAILURE;
856     return;
857   }
858   SimpleFileTracker::FileHandle sparse_file = file_tracker_->Acquire(
859       file_operations, this, SimpleFileTracker::SubFile::FILE_SPARSE);
860   if (!sparse_file.IsOK()) {
861     DoomInternal(file_operations);
862     *out_result = net::ERR_CACHE_WRITE_FAILURE;
863     return;
864   }
865 
866   int32_t sparse_data_size = out_entry_stat->sparse_data_size();
867   int32_t future_sparse_data_size;
868   if (!base::CheckAdd(sparse_data_size, buf_len)
869            .AssignIfValid(&future_sparse_data_size) ||
870       future_sparse_data_size < 0) {
871     DoomInternal(file_operations);
872     *out_result = net::ERR_CACHE_WRITE_FAILURE;
873     return;
874   }
875   // This is a pessimistic estimate; it assumes the entire buffer is going to
876   // be appended as a new range, not written over existing ranges.
877   if (static_cast<uint64_t>(future_sparse_data_size) > max_sparse_data_size) {
878     DVLOG(1) << "Truncating sparse data file (" << sparse_data_size << " + "
879              << buf_len << " > " << max_sparse_data_size << ")";
880     TruncateSparseFile(sparse_file.get());
881     out_entry_stat->set_sparse_data_size(0);
882   }
883 
884   auto it = sparse_ranges_.lower_bound(offset);
885 
886   if (it != sparse_ranges_.begin()) {
887     --it;
888     SparseRange* found_range = &it->second;
889     if (found_range->offset + found_range->length > offset) {
890       DCHECK_GE(found_range->length, 0);
891       DCHECK_LE(found_range->length, std::numeric_limits<int32_t>::max());
892       DCHECK_GE(offset - found_range->offset, 0);
893       DCHECK_LE(offset - found_range->offset,
894                 std::numeric_limits<int32_t>::max());
895       int net_offset = static_cast<int>(offset - found_range->offset);
896       int range_len_after_offset =
897           static_cast<int>(found_range->length - net_offset);
898       DCHECK_GE(range_len_after_offset, 0);
899 
900       int len_to_write = std::min(buf_len, range_len_after_offset);
901       if (!WriteSparseRange(sparse_file.get(), found_range, net_offset,
902                             len_to_write, buf)) {
903         DoomInternal(file_operations);
904         *out_result = net::ERR_CACHE_WRITE_FAILURE;
905         return;
906       }
907       written_so_far += len_to_write;
908     }
909     ++it;
910   }
911 
912   while (written_so_far < buf_len &&
913          it != sparse_ranges_.end() &&
914          it->second.offset < offset + buf_len) {
915     SparseRange* found_range = &it->second;
916     if (offset + written_so_far < found_range->offset) {
917       int len_to_append =
918           static_cast<int>(found_range->offset - (offset + written_so_far));
919       if (!AppendSparseRange(sparse_file.get(), offset + written_so_far,
920                              len_to_append, buf + written_so_far)) {
921         DoomInternal(file_operations);
922         *out_result = net::ERR_CACHE_WRITE_FAILURE;
923         return;
924       }
925       written_so_far += len_to_append;
926       appended_so_far += len_to_append;
927     }
928     int range_len = base::saturated_cast<int>(found_range->length);
929     int len_to_write = std::min(buf_len - written_so_far, range_len);
930     if (!WriteSparseRange(sparse_file.get(), found_range, 0, len_to_write,
931                           buf + written_so_far)) {
932       DoomInternal(file_operations);
933       *out_result = net::ERR_CACHE_WRITE_FAILURE;
934       return;
935     }
936     written_so_far += len_to_write;
937     ++it;
938   }
939 
940   if (written_so_far < buf_len) {
941     int len_to_append = buf_len - written_so_far;
942     if (!AppendSparseRange(sparse_file.get(), offset + written_so_far,
943                            len_to_append, buf + written_so_far)) {
944       DoomInternal(file_operations);
945       *out_result = net::ERR_CACHE_WRITE_FAILURE;
946       return;
947     }
948     written_so_far += len_to_append;
949     appended_so_far += len_to_append;
950   }
951 
952   DCHECK_EQ(buf_len, written_so_far);
953 
954   base::Time modification_time = Time::Now();
955   out_entry_stat->set_last_used(modification_time);
956   out_entry_stat->set_last_modified(modification_time);
957   int32_t old_sparse_data_size = out_entry_stat->sparse_data_size();
958   out_entry_stat->set_sparse_data_size(old_sparse_data_size + appended_so_far);
959   *out_result = written_so_far;
960 }
961 
GetAvailableRange(const SparseRequest & in_entry_op,RangeResult * out_result)962 void SimpleSynchronousEntry::GetAvailableRange(const SparseRequest& in_entry_op,
963                                                RangeResult* out_result) {
964   DCHECK(initialized_);
965   int64_t offset = in_entry_op.sparse_offset;
966   int len = in_entry_op.buf_len;
967 
968   auto it = sparse_ranges_.lower_bound(offset);
969 
970   int64_t start = offset;
971   int64_t avail_so_far = 0;
972 
973   if (it != sparse_ranges_.end() && it->second.offset < offset + len)
974     start = it->second.offset;
975 
976   if ((it == sparse_ranges_.end() || it->second.offset > offset) &&
977       it != sparse_ranges_.begin()) {
978     --it;
979     if (it->second.offset + it->second.length > offset) {
980       start = offset;
981       avail_so_far = (it->second.offset + it->second.length) - offset;
982     }
983     ++it;
984   }
985 
986   while (start + avail_so_far < offset + len &&
987          it != sparse_ranges_.end() &&
988          it->second.offset == start + avail_so_far) {
989     avail_so_far += it->second.length;
990     ++it;
991   }
992 
993   int64_t len_from_start = len - (start - offset);
994   *out_result = RangeResult(
995       start, static_cast<int>(std::min(avail_so_far, len_from_start)));
996 }
997 
CheckEOFRecord(BackendFileOperations * file_operations,base::File * file,int stream_index,const SimpleEntryStat & entry_stat,uint32_t expected_crc32)998 int SimpleSynchronousEntry::CheckEOFRecord(
999     BackendFileOperations* file_operations,
1000     base::File* file,
1001     int stream_index,
1002     const SimpleEntryStat& entry_stat,
1003     uint32_t expected_crc32) {
1004   DCHECK(initialized_);
1005   SimpleFileEOF eof_record;
1006   int file_offset = entry_stat.GetEOFOffsetInFile(key_.size(), stream_index);
1007   int file_index = GetFileIndexFromStreamIndex(stream_index);
1008   int rv =
1009       GetEOFRecordData(file, nullptr, file_index, file_offset, &eof_record);
1010 
1011   if (rv != net::OK) {
1012     DoomInternal(file_operations);
1013     return rv;
1014   }
1015   if ((eof_record.flags & SimpleFileEOF::FLAG_HAS_CRC32) &&
1016       eof_record.data_crc32 != expected_crc32) {
1017     DVLOG(1) << "EOF record had bad crc.";
1018     RecordCheckEOFResult(cache_type_, CHECK_EOF_RESULT_CRC_MISMATCH);
1019     DoomInternal(file_operations);
1020     return net::ERR_CACHE_CHECKSUM_MISMATCH;
1021   }
1022   RecordCheckEOFResult(cache_type_, CHECK_EOF_RESULT_SUCCESS);
1023   return net::OK;
1024 }
1025 
PreReadStreamPayload(base::File * file,PrefetchData * prefetch_data,int stream_index,int extra_size,const SimpleEntryStat & entry_stat,const SimpleFileEOF & eof_record,SimpleStreamPrefetchData * out)1026 int SimpleSynchronousEntry::PreReadStreamPayload(
1027     base::File* file,
1028     PrefetchData* prefetch_data,
1029     int stream_index,
1030     int extra_size,
1031     const SimpleEntryStat& entry_stat,
1032     const SimpleFileEOF& eof_record,
1033     SimpleStreamPrefetchData* out) {
1034   DCHECK(stream_index == 0 || stream_index == 1);
1035 
1036   int stream_size = entry_stat.data_size(stream_index);
1037   int read_size = stream_size + extra_size;
1038   out->data = base::MakeRefCounted<net::GrowableIOBuffer>();
1039   out->data->SetCapacity(read_size);
1040   int file_offset = entry_stat.GetOffsetInFile(key_.size(), 0, stream_index);
1041   if (!ReadFromFileOrPrefetched(file, prefetch_data, 0, file_offset, read_size,
1042                                 out->data->data()))
1043     return net::ERR_FAILED;
1044 
1045   // Check the CRC32.
1046   uint32_t expected_crc32 = simple_util::Crc32(out->data->data(), stream_size);
1047   if ((eof_record.flags & SimpleFileEOF::FLAG_HAS_CRC32) &&
1048       eof_record.data_crc32 != expected_crc32) {
1049     DVLOG(1) << "EOF record had bad crc.";
1050     RecordCheckEOFResult(cache_type_, CHECK_EOF_RESULT_CRC_MISMATCH);
1051     return net::ERR_CACHE_CHECKSUM_MISMATCH;
1052   }
1053   out->stream_crc32 = expected_crc32;
1054   RecordCheckEOFResult(cache_type_, CHECK_EOF_RESULT_SUCCESS);
1055   return net::OK;
1056 }
1057 
Close(const SimpleEntryStat & entry_stat,std::unique_ptr<std::vector<CRCRecord>> crc32s_to_write,net::GrowableIOBuffer * stream_0_data,SimpleEntryCloseResults * out_results)1058 void SimpleSynchronousEntry::Close(
1059     const SimpleEntryStat& entry_stat,
1060     std::unique_ptr<std::vector<CRCRecord>> crc32s_to_write,
1061     net::GrowableIOBuffer* stream_0_data,
1062     SimpleEntryCloseResults* out_results) {
1063   // As we delete `this`, we cannot use ScopedFileOperationsBinding here.
1064   std::unique_ptr<BackendFileOperations> file_operations =
1065       unbound_file_operations_->Bind(
1066           base::SequencedTaskRunner::GetCurrentDefault());
1067   unbound_file_operations_ = nullptr;
1068   base::ElapsedTimer close_time;
1069   DCHECK(stream_0_data);
1070 
1071   for (auto& crc_record : *crc32s_to_write) {
1072     const int stream_index = crc_record.index;
1073     const int file_index = GetFileIndexFromStreamIndex(stream_index);
1074     if (empty_file_omitted_[file_index])
1075       continue;
1076 
1077     SimpleFileTracker::FileHandle file = file_tracker_->Acquire(
1078         file_operations.get(), this, SubFileForFileIndex(file_index));
1079     if (!file.IsOK()) {
1080       RecordCloseResult(cache_type_, CLOSE_RESULT_WRITE_FAILURE);
1081       DoomInternal(file_operations.get());
1082       break;
1083     }
1084 
1085     if (stream_index == 0) {
1086       // Write stream 0 data.
1087       int stream_0_offset = entry_stat.GetOffsetInFile(key_.size(), 0, 0);
1088       if (file->Write(stream_0_offset, stream_0_data->data(),
1089                       entry_stat.data_size(0)) != entry_stat.data_size(0)) {
1090         RecordCloseResult(cache_type_, CLOSE_RESULT_WRITE_FAILURE);
1091         DVLOG(1) << "Could not write stream 0 data.";
1092         DoomInternal(file_operations.get());
1093       }
1094       net::SHA256HashValue hash_value;
1095       CalculateSHA256OfKey(key_, &hash_value);
1096       if (file->Write(stream_0_offset + entry_stat.data_size(0),
1097                       reinterpret_cast<char*>(hash_value.data),
1098                       sizeof(hash_value)) != sizeof(hash_value)) {
1099         RecordCloseResult(cache_type_, CLOSE_RESULT_WRITE_FAILURE);
1100         DVLOG(1) << "Could not write stream 0 data.";
1101         DoomInternal(file_operations.get());
1102       }
1103 
1104       // Re-compute stream 0 CRC if the data got changed (we may be here even
1105       // if it didn't change if stream 0's position on disk got changed due to
1106       // stream 1 write).
1107       if (!crc_record.has_crc32) {
1108         crc_record.data_crc32 =
1109             simple_util::Crc32(stream_0_data->data(), entry_stat.data_size(0));
1110         crc_record.has_crc32 = true;
1111       }
1112 
1113       out_results->estimated_trailer_prefetch_size =
1114           entry_stat.data_size(0) + sizeof(hash_value) + sizeof(SimpleFileEOF);
1115     }
1116 
1117     SimpleFileEOF eof_record;
1118     eof_record.stream_size = entry_stat.data_size(stream_index);
1119     eof_record.final_magic_number = kSimpleFinalMagicNumber;
1120     eof_record.flags = 0;
1121     if (crc_record.has_crc32)
1122       eof_record.flags |= SimpleFileEOF::FLAG_HAS_CRC32;
1123     if (stream_index == 0)
1124       eof_record.flags |= SimpleFileEOF::FLAG_HAS_KEY_SHA256;
1125     eof_record.data_crc32 = crc_record.data_crc32;
1126     int eof_offset = entry_stat.GetEOFOffsetInFile(key_.size(), stream_index);
1127     // If stream 0 changed size, the file needs to be resized, otherwise the
1128     // next open will yield wrong stream sizes. On stream 1 and stream 2 proper
1129     // resizing of the file is handled in SimpleSynchronousEntry::WriteData().
1130     if (stream_index == 0 && !file->SetLength(eof_offset)) {
1131       RecordCloseResult(cache_type_, CLOSE_RESULT_WRITE_FAILURE);
1132       DVLOG(1) << "Could not truncate stream 0 file.";
1133       DoomInternal(file_operations.get());
1134       break;
1135     }
1136     if (file->Write(eof_offset, reinterpret_cast<const char*>(&eof_record),
1137                     sizeof(eof_record)) != sizeof(eof_record)) {
1138       RecordCloseResult(cache_type_, CLOSE_RESULT_WRITE_FAILURE);
1139       DVLOG(1) << "Could not write eof record.";
1140       DoomInternal(file_operations.get());
1141       break;
1142     }
1143   }
1144   for (int i = 0; i < kSimpleEntryNormalFileCount; ++i) {
1145     if (empty_file_omitted_[i])
1146       continue;
1147 
1148     if (header_and_key_check_needed_[i]) {
1149       SimpleFileTracker::FileHandle file = file_tracker_->Acquire(
1150           file_operations.get(), this, SubFileForFileIndex(i));
1151       if (!file.IsOK() || !CheckHeaderAndKey(file.get(), i))
1152         DoomInternal(file_operations.get());
1153     }
1154     CloseFile(file_operations.get(), i);
1155   }
1156 
1157   if (sparse_file_open()) {
1158     CloseSparseFile(file_operations.get());
1159   }
1160 
1161   SIMPLE_CACHE_UMA(TIMES, "DiskCloseLatency", cache_type_,
1162                    close_time.Elapsed());
1163   RecordCloseResult(cache_type_, CLOSE_RESULT_SUCCESS);
1164   have_open_files_ = false;
1165   delete this;
1166 }
1167 
SimpleSynchronousEntry(net::CacheType cache_type,const FilePath & path,const std::string & key,const uint64_t entry_hash,SimpleFileTracker * file_tracker,std::unique_ptr<UnboundBackendFileOperations> unbound_file_operations,int32_t trailer_prefetch_size)1168 SimpleSynchronousEntry::SimpleSynchronousEntry(
1169     net::CacheType cache_type,
1170     const FilePath& path,
1171     const std::string& key,
1172     const uint64_t entry_hash,
1173     SimpleFileTracker* file_tracker,
1174     std::unique_ptr<UnboundBackendFileOperations> unbound_file_operations,
1175     int32_t trailer_prefetch_size)
1176     : cache_type_(cache_type),
1177       path_(path),
1178       entry_file_key_(entry_hash),
1179       key_(key),
1180       file_tracker_(file_tracker),
1181       unbound_file_operations_(std::move(unbound_file_operations)),
1182       trailer_prefetch_size_(trailer_prefetch_size) {
1183   for (bool& empty_file_omitted : empty_file_omitted_) {
1184     empty_file_omitted = false;
1185   }
1186 }
1187 
~SimpleSynchronousEntry()1188 SimpleSynchronousEntry::~SimpleSynchronousEntry() {
1189   DCHECK(!have_open_files_);
1190 }
1191 
MaybeOpenFile(BackendFileOperations * file_operations,int file_index,base::File::Error * out_error)1192 bool SimpleSynchronousEntry::MaybeOpenFile(
1193     BackendFileOperations* file_operations,
1194     int file_index,
1195     base::File::Error* out_error) {
1196   DCHECK(out_error);
1197 
1198   FilePath filename = GetFilenameFromFileIndex(file_index);
1199   int flags = base::File::FLAG_OPEN | base::File::FLAG_READ |
1200               base::File::FLAG_WRITE | base::File::FLAG_WIN_SHARE_DELETE;
1201   auto file = std::make_unique<base::File>();
1202   *file = file_operations->OpenFile(filename, flags);
1203   *out_error = file->error_details();
1204 
1205   if (CanOmitEmptyFile(file_index) && !file->IsValid() &&
1206       *out_error == base::File::FILE_ERROR_NOT_FOUND) {
1207     empty_file_omitted_[file_index] = true;
1208     return true;
1209   }
1210 
1211   if (file->IsValid()) {
1212     file_tracker_->Register(this, SubFileForFileIndex(file_index),
1213                             std::move(file));
1214     return true;
1215   }
1216   return false;
1217 }
1218 
MaybeCreateFile(BackendFileOperations * file_operations,int file_index,FileRequired file_required,base::File::Error * out_error)1219 bool SimpleSynchronousEntry::MaybeCreateFile(
1220     BackendFileOperations* file_operations,
1221     int file_index,
1222     FileRequired file_required,
1223     base::File::Error* out_error) {
1224   DCHECK(out_error);
1225 
1226   if (CanOmitEmptyFile(file_index) && file_required == FILE_NOT_REQUIRED) {
1227     empty_file_omitted_[file_index] = true;
1228     return true;
1229   }
1230 
1231   FilePath filename = GetFilenameFromFileIndex(file_index);
1232   int flags = base::File::FLAG_CREATE | base::File::FLAG_READ |
1233               base::File::FLAG_WRITE | base::File::FLAG_WIN_SHARE_DELETE;
1234   auto file =
1235       std::make_unique<base::File>(file_operations->OpenFile(filename, flags));
1236 
1237   // It's possible that the creation failed because someone deleted the
1238   // directory (e.g. because someone pressed "clear cache" on Android).
1239   // If so, we would keep failing for a while until periodic index snapshot
1240   // re-creates the cache dir, so try to recover from it quickly here.
1241   //
1242   // This previously also checked whether the directory was missing, but that
1243   // races against other entry creations attempting the same recovery.
1244   if (!file->IsValid() &&
1245       file->error_details() == base::File::FILE_ERROR_NOT_FOUND) {
1246     file_operations->CreateDirectory(path_);
1247     *file = file_operations->OpenFile(filename, flags);
1248   }
1249 
1250   *out_error = file->error_details();
1251   if (file->IsValid()) {
1252     file_tracker_->Register(this, SubFileForFileIndex(file_index),
1253                             std::move(file));
1254     empty_file_omitted_[file_index] = false;
1255     return true;
1256   }
1257   return false;
1258 }
1259 
OpenFiles(BackendFileOperations * file_operations,SimpleEntryStat * out_entry_stat)1260 bool SimpleSynchronousEntry::OpenFiles(BackendFileOperations* file_operations,
1261                                        SimpleEntryStat* out_entry_stat) {
1262   for (int i = 0; i < kSimpleEntryNormalFileCount; ++i) {
1263     base::File::Error error;
1264 
1265     if (!MaybeOpenFile(file_operations, i, &error)) {
1266       RecordSyncOpenResult(cache_type_, OPEN_ENTRY_PLATFORM_FILE_ERROR);
1267       SIMPLE_CACHE_LOCAL(ENUMERATION, "SyncOpenPlatformFileError", cache_type_,
1268                          -error, -base::File::FILE_ERROR_MAX);
1269       while (--i >= 0)
1270         CloseFile(file_operations, i);
1271       return false;
1272     }
1273   }
1274 
1275   have_open_files_ = true;
1276 
1277   for (int i = 0; i < kSimpleEntryNormalFileCount; ++i) {
1278     if (empty_file_omitted_[i]) {
1279       out_entry_stat->set_data_size(i + 1, 0);
1280       continue;
1281     }
1282 
1283     base::File::Info file_info;
1284     SimpleFileTracker::FileHandle file =
1285         file_tracker_->Acquire(file_operations, this, SubFileForFileIndex(i));
1286     bool success = file.IsOK() && file->GetInfo(&file_info);
1287     if (!success) {
1288       DLOG(WARNING) << "Could not get platform file info.";
1289       continue;
1290     }
1291     out_entry_stat->set_last_used(file_info.last_accessed);
1292     out_entry_stat->set_last_modified(file_info.last_modified);
1293 
1294     // Two things prevent from knowing the right values for |data_size|:
1295     // 1) The key might not be known, hence its length might be unknown.
1296     // 2) Stream 0 and stream 1 are in the same file, and the exact size for
1297     // each will only be known when reading the EOF record for stream 0.
1298     //
1299     // The size for file 0 and 1 is temporarily kept in
1300     // |data_size(1)| and |data_size(2)| respectively. Reading the key in
1301     // InitializeForOpen yields the data size for each file. In the case of
1302     // file hash_1, this is the total size of stream 2, and is assigned to
1303     // data_size(2). In the case of file 0, it is the combined size of stream
1304     // 0, stream 1 and one EOF record. The exact distribution of sizes between
1305     // stream 1 and stream 0 is only determined after reading the EOF record
1306     // for stream 0 in ReadAndValidateStream0AndMaybe1.
1307     if (!base::IsValueInRangeForNumericType<int>(file_info.size)) {
1308       RecordSyncOpenResult(cache_type_, OPEN_ENTRY_INVALID_FILE_LENGTH);
1309       return false;
1310     }
1311     out_entry_stat->set_data_size(i + 1, static_cast<int>(file_info.size));
1312   }
1313 
1314   return true;
1315 }
1316 
CreateFiles(BackendFileOperations * file_operations,SimpleEntryStat * out_entry_stat)1317 bool SimpleSynchronousEntry::CreateFiles(BackendFileOperations* file_operations,
1318                                          SimpleEntryStat* out_entry_stat) {
1319   for (int i = 0; i < kSimpleEntryNormalFileCount; ++i) {
1320     base::File::Error error;
1321     if (!MaybeCreateFile(file_operations, i, FILE_NOT_REQUIRED, &error)) {
1322       SIMPLE_CACHE_LOCAL(ENUMERATION, "SyncCreatePlatformFileError",
1323                          cache_type_, -error, -base::File::FILE_ERROR_MAX);
1324       while (--i >= 0)
1325         CloseFile(file_operations, i);
1326       return false;
1327     }
1328   }
1329 
1330   have_open_files_ = true;
1331 
1332   base::Time creation_time = Time::Now();
1333   out_entry_stat->set_last_modified(creation_time);
1334   out_entry_stat->set_last_used(creation_time);
1335   for (int i = 0; i < kSimpleEntryNormalFileCount; ++i)
1336     out_entry_stat->set_data_size(i, 0);
1337 
1338   return true;
1339 }
1340 
CloseFile(BackendFileOperations * file_operations,int index)1341 void SimpleSynchronousEntry::CloseFile(BackendFileOperations* file_operations,
1342                                        int index) {
1343   if (empty_file_omitted_[index]) {
1344     empty_file_omitted_[index] = false;
1345   } else {
1346     // We want to delete files that were renamed for doom here; and we should do
1347     // this before calling SimpleFileTracker::Close, since that would make the
1348     // name available to other threads.
1349     if (entry_file_key_.doom_generation != 0u) {
1350       file_operations->DeleteFile(path_.AppendASCII(
1351           GetFilenameFromEntryFileKeyAndFileIndex(entry_file_key_, index)));
1352     }
1353     file_tracker_->Close(this, SubFileForFileIndex(index));
1354   }
1355 }
1356 
CloseFiles()1357 void SimpleSynchronousEntry::CloseFiles() {
1358   if (!have_open_files_) {
1359     return;
1360   }
1361   BackendFileOperations* file_operations = nullptr;
1362   ScopedFileOperationsBinding binding(this, &file_operations);
1363   for (int i = 0; i < kSimpleEntryNormalFileCount; ++i)
1364     CloseFile(file_operations, i);
1365   if (sparse_file_open())
1366     CloseSparseFile(file_operations);
1367   have_open_files_ = false;
1368 }
1369 
CheckHeaderAndKey(base::File * file,int file_index)1370 bool SimpleSynchronousEntry::CheckHeaderAndKey(base::File* file,
1371                                                int file_index) {
1372   std::vector<char> header_data(key_.empty() ? kInitialHeaderRead
1373                                              : GetHeaderSize(key_.size()));
1374   int bytes_read = file->Read(0, header_data.data(), header_data.size());
1375   const SimpleFileHeader* header =
1376       reinterpret_cast<const SimpleFileHeader*>(header_data.data());
1377 
1378   if (bytes_read == -1 || static_cast<size_t>(bytes_read) < sizeof(*header)) {
1379     RecordSyncOpenResult(cache_type_, OPEN_ENTRY_CANT_READ_HEADER);
1380     return false;
1381   }
1382   // This resize will not invalidate iterators since it does not enlarge the
1383   // header_data.
1384   DCHECK_LE(static_cast<size_t>(bytes_read), header_data.size());
1385   header_data.resize(bytes_read);
1386 
1387   if (header->initial_magic_number != kSimpleInitialMagicNumber) {
1388     RecordSyncOpenResult(cache_type_, OPEN_ENTRY_BAD_MAGIC_NUMBER);
1389     return false;
1390   }
1391 
1392   if (header->version != kSimpleEntryVersionOnDisk) {
1393     RecordSyncOpenResult(cache_type_, OPEN_ENTRY_BAD_VERSION);
1394     return false;
1395   }
1396 
1397   size_t expected_header_size = GetHeaderSize(header->key_length);
1398   if (header_data.size() < expected_header_size) {
1399     size_t old_size = header_data.size();
1400     int bytes_to_read = expected_header_size - old_size;
1401     // This resize will invalidate iterators, since it is enlarging header_data.
1402     header_data.resize(expected_header_size);
1403     int read_result =
1404         file->Read(old_size, header_data.data() + old_size, bytes_to_read);
1405     if (read_result != bytes_to_read) {
1406       RecordSyncOpenResult(cache_type_, OPEN_ENTRY_CANT_READ_KEY);
1407       return false;
1408     }
1409     header = reinterpret_cast<const SimpleFileHeader*>(header_data.data());
1410   }
1411 
1412   char* key_data = header_data.data() + sizeof(*header);
1413   if (base::PersistentHash(key_data, header->key_length) != header->key_hash) {
1414     RecordSyncOpenResult(cache_type_, OPEN_ENTRY_KEY_HASH_MISMATCH);
1415     return false;
1416   }
1417 
1418   std::string key_from_header(key_data, header->key_length);
1419   if (key_.empty()) {
1420     key_.swap(key_from_header);
1421   } else {
1422     if (key_ != key_from_header) {
1423       RecordSyncOpenResult(cache_type_, OPEN_ENTRY_KEY_MISMATCH);
1424       return false;
1425     }
1426   }
1427 
1428   header_and_key_check_needed_[file_index] = false;
1429   return true;
1430 }
1431 
InitializeForOpen(BackendFileOperations * file_operations,SimpleEntryStat * out_entry_stat,SimpleStreamPrefetchData stream_prefetch_data[2])1432 int SimpleSynchronousEntry::InitializeForOpen(
1433     BackendFileOperations* file_operations,
1434     SimpleEntryStat* out_entry_stat,
1435     SimpleStreamPrefetchData stream_prefetch_data[2]) {
1436   DCHECK(!initialized_);
1437   if (!OpenFiles(file_operations, out_entry_stat)) {
1438     DLOG(WARNING) << "Could not open platform files for entry.";
1439     return net::ERR_FAILED;
1440   }
1441   for (int i = 0; i < kSimpleEntryNormalFileCount; ++i) {
1442     if (empty_file_omitted_[i])
1443       continue;
1444 
1445     if (key_.empty()) {
1446       SimpleFileTracker::FileHandle file =
1447           file_tracker_->Acquire(file_operations, this, SubFileForFileIndex(i));
1448       // If |key_| is empty, we were opened via the iterator interface, without
1449       // knowing what our key is. We must therefore read the header immediately
1450       // to discover it, so SimpleEntryImpl can make it available to
1451       // disk_cache::Entry::GetKey().
1452       if (!file.IsOK() || !CheckHeaderAndKey(file.get(), i))
1453         return net::ERR_FAILED;
1454     } else {
1455       // If we do know which key were are looking for, we still need to
1456       // check that the file actually has it (rather than just being a hash
1457       // collision or some sort of file system accident), but that can be put
1458       // off until opportune time: either the read of the footer, or when we
1459       // start reading in the data, depending on stream # and format revision.
1460       header_and_key_check_needed_[i] = true;
1461     }
1462 
1463     if (i == 0) {
1464       // File size for stream 0 has been stored temporarily in data_size[1].
1465       int ret_value_stream_0 = ReadAndValidateStream0AndMaybe1(
1466           file_operations, out_entry_stat->data_size(1), out_entry_stat,
1467           stream_prefetch_data);
1468       if (ret_value_stream_0 != net::OK)
1469         return ret_value_stream_0;
1470     } else {
1471       out_entry_stat->set_data_size(
1472           2,
1473           GetDataSizeFromFileSize(key_.size(), out_entry_stat->data_size(2)));
1474       const int32_t data_size_2 = out_entry_stat->data_size(2);
1475       int ret_value_stream_2 = net::OK;
1476       if (data_size_2 < 0) {
1477         DLOG(WARNING) << "Stream 2 file is too small.";
1478         ret_value_stream_2 = net::ERR_FAILED;
1479       } else if (data_size_2 > 0) {
1480         // Validate non empty stream 2.
1481         SimpleFileEOF eof_record;
1482         SimpleFileTracker::FileHandle file = file_tracker_->Acquire(
1483             file_operations, this, SubFileForFileIndex(i));
1484         int file_offset =
1485             out_entry_stat->GetEOFOffsetInFile(key_.size(), 2 /*stream index*/);
1486         ret_value_stream_2 =
1487             GetEOFRecordData(file.get(), nullptr, i, file_offset, &eof_record);
1488       }
1489 
1490       if (ret_value_stream_2 != net::OK) {
1491         DCHECK_EQ(i, GetFileIndexFromStreamIndex(2));
1492         DCHECK(CanOmitEmptyFile(GetFileIndexFromStreamIndex(2)));
1493         // Stream 2 is broken, set its size to zero to have it automatically
1494         // deleted further down in this function. For V8 this preserves the
1495         // cached source when only the code cache was corrupted.
1496         out_entry_stat->set_data_size(2, 0);
1497       }
1498     }
1499   }
1500 
1501   int32_t sparse_data_size = 0;
1502   if (!OpenSparseFileIfExists(file_operations, &sparse_data_size)) {
1503     RecordSyncOpenResult(cache_type_, OPEN_ENTRY_SPARSE_OPEN_FAILED);
1504     return net::ERR_FAILED;
1505   }
1506   out_entry_stat->set_sparse_data_size(sparse_data_size);
1507 
1508   const int stream2_file_index = GetFileIndexFromStreamIndex(2);
1509   DCHECK(CanOmitEmptyFile(stream2_file_index));
1510   if (!empty_file_omitted_[stream2_file_index] &&
1511       out_entry_stat->data_size(2) == 0) {
1512     CloseFile(file_operations, stream2_file_index);
1513     DeleteFileForEntryHash(path_, entry_file_key_.entry_hash,
1514                            stream2_file_index, file_operations);
1515     empty_file_omitted_[stream2_file_index] = true;
1516   }
1517 
1518   RecordSyncOpenResult(cache_type_, OPEN_ENTRY_SUCCESS);
1519   initialized_ = true;
1520   return net::OK;
1521 }
1522 
InitializeCreatedFile(BackendFileOperations * file_operations,int file_index)1523 bool SimpleSynchronousEntry::InitializeCreatedFile(
1524     BackendFileOperations* file_operations,
1525     int file_index) {
1526   SimpleFileTracker::FileHandle file = file_tracker_->Acquire(
1527       file_operations, this, SubFileForFileIndex(file_index));
1528   if (!file.IsOK())
1529     return false;
1530 
1531   SimpleFileHeader header;
1532   header.initial_magic_number = kSimpleInitialMagicNumber;
1533   header.version = kSimpleEntryVersionOnDisk;
1534 
1535   header.key_length = key_.size();
1536   header.key_hash = base::PersistentHash(key_);
1537 
1538   int bytes_written =
1539       file->Write(0, reinterpret_cast<char*>(&header), sizeof(header));
1540   if (bytes_written != sizeof(header))
1541     return false;
1542 
1543   bytes_written = file->Write(sizeof(header), key_.data(), key_.size());
1544   if (bytes_written != base::checked_cast<int>(key_.size()))
1545     return false;
1546 
1547   return true;
1548 }
1549 
InitializeForCreate(BackendFileOperations * file_operations,SimpleEntryStat * out_entry_stat)1550 int SimpleSynchronousEntry::InitializeForCreate(
1551     BackendFileOperations* file_operations,
1552     SimpleEntryStat* out_entry_stat) {
1553   DCHECK(!initialized_);
1554   if (!CreateFiles(file_operations, out_entry_stat)) {
1555     DLOG(WARNING) << "Could not create platform files.";
1556     return net::ERR_FILE_EXISTS;
1557   }
1558   for (int i = 0; i < kSimpleEntryNormalFileCount; ++i) {
1559     if (empty_file_omitted_[i])
1560       continue;
1561 
1562     if (!InitializeCreatedFile(file_operations, i))
1563       return net::ERR_FAILED;
1564   }
1565   initialized_ = true;
1566   return net::OK;
1567 }
1568 
ReadAndValidateStream0AndMaybe1(BackendFileOperations * file_operations,int file_size,SimpleEntryStat * out_entry_stat,SimpleStreamPrefetchData stream_prefetch_data[2])1569 int SimpleSynchronousEntry::ReadAndValidateStream0AndMaybe1(
1570     BackendFileOperations* file_operations,
1571     int file_size,
1572     SimpleEntryStat* out_entry_stat,
1573     SimpleStreamPrefetchData stream_prefetch_data[2]) {
1574   SimpleFileTracker::FileHandle file =
1575       file_tracker_->Acquire(file_operations, this, SubFileForFileIndex(0));
1576   if (!file.IsOK())
1577     return net::ERR_FAILED;
1578 
1579   // We may prefetch data from file in a couple cases:
1580   //  1) If the file is small enough we may prefetch it entirely.
1581   //  2) We may also prefetch a block of trailer bytes from the end of
1582   //     the file.
1583   // In these cases the PrefetchData object is used to store the
1584   // bytes read from the file.  The PrefetchData object also keeps track
1585   // of what range within the file has been prefetched.  It will only
1586   // allow reads wholely within this range to be accessed via its
1587   // ReadData() method.
1588   PrefetchData prefetch_data(file_size);
1589 
1590   // Determine a threshold for fully prefetching the entire entry file.  If
1591   // the entry file is less than or equal to this number of bytes it will
1592   // be fully prefetched.
1593   int full_prefetch_size = GetSimpleCacheFullPrefetchSize();
1594 
1595   // Determine how much trailer data to prefetch.  If the full file prefetch
1596   // does not trigger then this is the number of bytes to read from the end
1597   // of the file in a single file operation.  Ideally the trailer prefetch
1598   // will contain at least stream 0 and its EOF record.
1599   int trailer_prefetch_size =
1600       GetSimpleCacheTrailerPrefetchSize(trailer_prefetch_size_);
1601 
1602   OpenPrefetchMode prefetch_mode = OPEN_PREFETCH_NONE;
1603   if (file_size <= full_prefetch_size || file_size <= trailer_prefetch_size) {
1604     // Prefetch the entire file.
1605     prefetch_mode = OPEN_PREFETCH_FULL;
1606     RecordOpenPrefetchMode(cache_type_, prefetch_mode);
1607     if (!prefetch_data.PrefetchFromFile(&file, 0, file_size))
1608       return net::ERR_FAILED;
1609   } else if (trailer_prefetch_size > 0) {
1610     // Prefetch trailer data from the end of the file.
1611     prefetch_mode = OPEN_PREFETCH_TRAILER;
1612     RecordOpenPrefetchMode(cache_type_, prefetch_mode);
1613     size_t length = std::min(trailer_prefetch_size, file_size);
1614     size_t offset = file_size - length;
1615     if (!prefetch_data.PrefetchFromFile(&file, offset, length))
1616       return net::ERR_FAILED;
1617   } else {
1618     // Do no prefetching.
1619     RecordOpenPrefetchMode(cache_type_, prefetch_mode);
1620   }
1621 
1622   // Read stream 0 footer first --- it has size/feature info required to figure
1623   // out file 0's layout.
1624   SimpleFileEOF stream_0_eof;
1625   int rv = GetEOFRecordData(
1626       file.get(), &prefetch_data, /* file_index = */ 0,
1627       /* file_offset = */ file_size - sizeof(SimpleFileEOF), &stream_0_eof);
1628   if (rv != net::OK)
1629     return rv;
1630 
1631   int32_t stream_0_size = stream_0_eof.stream_size;
1632   if (stream_0_size < 0 || stream_0_size > file_size)
1633     return net::ERR_FAILED;
1634   out_entry_stat->set_data_size(0, stream_0_size);
1635 
1636   // Calculate size for stream 1, now we know stream 0's.
1637   // See comments in simple_entry_format.h for background.
1638   bool has_key_sha256 =
1639       (stream_0_eof.flags & SimpleFileEOF::FLAG_HAS_KEY_SHA256) ==
1640       SimpleFileEOF::FLAG_HAS_KEY_SHA256;
1641   int extra_post_stream_0_read = 0;
1642   if (has_key_sha256)
1643     extra_post_stream_0_read += sizeof(net::SHA256HashValue);
1644 
1645   int32_t stream1_size = file_size - 2 * sizeof(SimpleFileEOF) - stream_0_size -
1646                          sizeof(SimpleFileHeader) - key_.size() -
1647                          extra_post_stream_0_read;
1648   if (stream1_size < 0 || stream1_size > file_size)
1649     return net::ERR_FAILED;
1650 
1651   out_entry_stat->set_data_size(1, stream1_size);
1652 
1653   // Put stream 0 data in memory --- plus maybe the sha256(key) footer.
1654   rv = PreReadStreamPayload(file.get(), &prefetch_data, /* stream_index = */ 0,
1655                             extra_post_stream_0_read, *out_entry_stat,
1656                             stream_0_eof, &stream_prefetch_data[0]);
1657   if (rv != net::OK)
1658     return rv;
1659 
1660   // Note the exact range needed in order to read the EOF record and stream 0.
1661   // In APP_CACHE mode this will be stored directly in the index so we can
1662   // know exactly how much to read next time.
1663   computed_trailer_prefetch_size_ =
1664       prefetch_data.GetDesiredTrailerPrefetchSize();
1665 
1666   // If prefetch buffer is available, and we have sha256(key) (so we don't need
1667   // to look at the header), extract out stream 1 info as well.
1668   int stream_1_offset = out_entry_stat->GetOffsetInFile(
1669       key_.size(), /* offset= */ 0, /* stream_index = */ 1);
1670   int stream_1_read_size =
1671       sizeof(SimpleFileEOF) + out_entry_stat->data_size(/* stream_index = */ 1);
1672   if (has_key_sha256 &&
1673       prefetch_data.HasData(stream_1_offset, stream_1_read_size)) {
1674     SimpleFileEOF stream_1_eof;
1675     int stream_1_eof_offset =
1676         out_entry_stat->GetEOFOffsetInFile(key_.size(), /* stream_index = */ 1);
1677     rv = GetEOFRecordData(file.get(), &prefetch_data, /* file_index = */ 0,
1678                           stream_1_eof_offset, &stream_1_eof);
1679     if (rv != net::OK)
1680       return rv;
1681 
1682     rv = PreReadStreamPayload(file.get(), &prefetch_data,
1683                               /* stream_index = */ 1,
1684                               /* extra_size = */ 0, *out_entry_stat,
1685                               stream_1_eof, &stream_prefetch_data[1]);
1686     if (rv != net::OK)
1687       return rv;
1688   }
1689 
1690   // If present, check the key SHA256.
1691   if (has_key_sha256) {
1692     net::SHA256HashValue hash_value;
1693     CalculateSHA256OfKey(key_, &hash_value);
1694     bool matched =
1695         std::memcmp(&hash_value,
1696                     stream_prefetch_data[0].data->data() + stream_0_size,
1697                     sizeof(hash_value)) == 0;
1698     if (!matched)
1699       return net::ERR_FAILED;
1700 
1701     // Elide header check if we verified sha256(key) via footer.
1702     header_and_key_check_needed_[0] = false;
1703   }
1704 
1705   // Ensure the key is validated before completion.
1706   if (!has_key_sha256 && header_and_key_check_needed_[0])
1707     CheckHeaderAndKey(file.get(), 0);
1708 
1709   return net::OK;
1710 }
1711 
ReadFromFileOrPrefetched(base::File * file,PrefetchData * prefetch_data,int file_index,int offset,int size,char * dest)1712 bool SimpleSynchronousEntry::ReadFromFileOrPrefetched(
1713     base::File* file,
1714     PrefetchData* prefetch_data,
1715     int file_index,
1716     int offset,
1717     int size,
1718     char* dest) {
1719   if (offset < 0 || size < 0)
1720     return false;
1721   if (size == 0)
1722     return true;
1723 
1724   base::CheckedNumeric<size_t> start(offset);
1725   size_t start_numeric;
1726   if (!start.AssignIfValid(&start_numeric))
1727     return false;
1728 
1729   base::CheckedNumeric<size_t> length(size);
1730   size_t length_numeric;
1731   if (!length.AssignIfValid(&length_numeric))
1732     return false;
1733 
1734   // First try to extract the desired range from the PrefetchData.
1735   if (file_index == 0 && prefetch_data &&
1736       prefetch_data->ReadData(start_numeric, length_numeric, dest)) {
1737     return true;
1738   }
1739 
1740   // If we have not prefetched the range then we must read it from disk.
1741   return file->Read(start_numeric, dest, length_numeric) == size;
1742 }
1743 
GetEOFRecordData(base::File * file,PrefetchData * prefetch_data,int file_index,int file_offset,SimpleFileEOF * eof_record)1744 int SimpleSynchronousEntry::GetEOFRecordData(base::File* file,
1745                                              PrefetchData* prefetch_data,
1746                                              int file_index,
1747                                              int file_offset,
1748                                              SimpleFileEOF* eof_record) {
1749   if (!ReadFromFileOrPrefetched(file, prefetch_data, file_index, file_offset,
1750                                 sizeof(SimpleFileEOF),
1751                                 reinterpret_cast<char*>(eof_record))) {
1752     RecordCheckEOFResult(cache_type_, CHECK_EOF_RESULT_READ_FAILURE);
1753     return net::ERR_CACHE_CHECKSUM_READ_FAILURE;
1754   }
1755 
1756   if (eof_record->final_magic_number != kSimpleFinalMagicNumber) {
1757     RecordCheckEOFResult(cache_type_, CHECK_EOF_RESULT_MAGIC_NUMBER_MISMATCH);
1758     DVLOG(1) << "EOF record had bad magic number.";
1759     return net::ERR_CACHE_CHECKSUM_READ_FAILURE;
1760   }
1761 
1762   if (!base::IsValueInRangeForNumericType<int32_t>(eof_record->stream_size))
1763     return net::ERR_FAILED;
1764   return net::OK;
1765 }
1766 
1767 // static
DeleteFileForEntryHash(const FilePath & path,const uint64_t entry_hash,const int file_index,BackendFileOperations * file_operations)1768 bool SimpleSynchronousEntry::DeleteFileForEntryHash(
1769     const FilePath& path,
1770     const uint64_t entry_hash,
1771     const int file_index,
1772     BackendFileOperations* file_operations) {
1773   FilePath to_delete = path.AppendASCII(GetFilenameFromEntryFileKeyAndFileIndex(
1774       SimpleFileTracker::EntryFileKey(entry_hash), file_index));
1775   return file_operations->DeleteFile(to_delete);
1776 }
1777 
1778 // static
DeleteFilesForEntryHash(const FilePath & path,const uint64_t entry_hash,BackendFileOperations * file_operations)1779 bool SimpleSynchronousEntry::DeleteFilesForEntryHash(
1780     const FilePath& path,
1781     const uint64_t entry_hash,
1782     BackendFileOperations* file_operations) {
1783   bool result = true;
1784   for (int i = 0; i < kSimpleEntryNormalFileCount; ++i) {
1785     if (!DeleteFileForEntryHash(path, entry_hash, i, file_operations) &&
1786         !CanOmitEmptyFile(i)) {
1787       result = false;
1788     }
1789   }
1790   FilePath to_delete = path.AppendASCII(GetSparseFilenameFromEntryFileKey(
1791       SimpleFileTracker::EntryFileKey(entry_hash)));
1792   file_operations->DeleteFile(
1793       to_delete,
1794       BackendFileOperations::DeleteFileMode::kEnsureImmediateAvailability);
1795   return result;
1796 }
1797 
1798 // static
TruncateFilesForEntryHash(const FilePath & path,const uint64_t entry_hash,BackendFileOperations * file_operations)1799 bool SimpleSynchronousEntry::TruncateFilesForEntryHash(
1800     const FilePath& path,
1801     const uint64_t entry_hash,
1802     BackendFileOperations* file_operations) {
1803   SimpleFileTracker::EntryFileKey file_key(entry_hash);
1804   bool result = true;
1805   for (int i = 0; i < kSimpleEntryNormalFileCount; ++i) {
1806     FilePath filename_to_truncate =
1807         path.AppendASCII(GetFilenameFromEntryFileKeyAndFileIndex(file_key, i));
1808     if (!TruncatePath(filename_to_truncate, file_operations))
1809       result = false;
1810   }
1811   FilePath to_delete =
1812       path.AppendASCII(GetSparseFilenameFromEntryFileKey(file_key));
1813   TruncatePath(to_delete, file_operations);
1814   return result;
1815 }
1816 
GetFilenameFromFileIndex(int file_index) const1817 FilePath SimpleSynchronousEntry::GetFilenameFromFileIndex(
1818     int file_index) const {
1819   return path_.AppendASCII(
1820       GetFilenameFromEntryFileKeyAndFileIndex(entry_file_key_, file_index));
1821 }
1822 
GetFilenameForSubfile(SimpleFileTracker::SubFile sub_file) const1823 base::FilePath SimpleSynchronousEntry::GetFilenameForSubfile(
1824     SimpleFileTracker::SubFile sub_file) const {
1825   if (sub_file == SimpleFileTracker::SubFile::FILE_SPARSE)
1826     return path_.AppendASCII(
1827         GetSparseFilenameFromEntryFileKey(entry_file_key_));
1828   else
1829     return GetFilenameFromFileIndex(FileIndexForSubFile(sub_file));
1830 }
1831 
OpenSparseFileIfExists(BackendFileOperations * file_operations,int32_t * out_sparse_data_size)1832 bool SimpleSynchronousEntry::OpenSparseFileIfExists(
1833     BackendFileOperations* file_operations,
1834     int32_t* out_sparse_data_size) {
1835   DCHECK(!sparse_file_open());
1836 
1837   FilePath filename =
1838       path_.AppendASCII(GetSparseFilenameFromEntryFileKey(entry_file_key_));
1839   int flags = base::File::FLAG_OPEN | base::File::FLAG_READ |
1840               base::File::FLAG_WRITE | base::File::FLAG_WIN_SHARE_DELETE;
1841   auto sparse_file =
1842       std::make_unique<base::File>(file_operations->OpenFile(filename, flags));
1843   if (!sparse_file->IsValid()) {
1844     // No file -> OK, file open error -> 'trouble.
1845     return sparse_file->error_details() == base::File::FILE_ERROR_NOT_FOUND;
1846   }
1847 
1848   if (!ScanSparseFile(sparse_file.get(), out_sparse_data_size))
1849     return false;
1850 
1851   file_tracker_->Register(this, SimpleFileTracker::SubFile::FILE_SPARSE,
1852                           std::move(sparse_file));
1853   sparse_file_open_ = true;
1854   return true;
1855 }
1856 
CreateSparseFile(BackendFileOperations * file_operations)1857 bool SimpleSynchronousEntry::CreateSparseFile(
1858     BackendFileOperations* file_operations) {
1859   DCHECK(!sparse_file_open());
1860 
1861   FilePath filename =
1862       path_.AppendASCII(GetSparseFilenameFromEntryFileKey(entry_file_key_));
1863   int flags = base::File::FLAG_CREATE | base::File::FLAG_READ |
1864               base::File::FLAG_WRITE | base::File::FLAG_WIN_SHARE_DELETE;
1865   std::unique_ptr<base::File> sparse_file =
1866       std::make_unique<base::File>(file_operations->OpenFile(filename, flags));
1867   if (!sparse_file->IsValid())
1868     return false;
1869   if (!InitializeSparseFile(sparse_file.get()))
1870     return false;
1871   file_tracker_->Register(this, SimpleFileTracker::SubFile::FILE_SPARSE,
1872                           std::move(sparse_file));
1873   sparse_file_open_ = true;
1874   return true;
1875 }
1876 
CloseSparseFile(BackendFileOperations * file_operations)1877 void SimpleSynchronousEntry::CloseSparseFile(
1878     BackendFileOperations* file_operations) {
1879   DCHECK(sparse_file_open());
1880   if (entry_file_key_.doom_generation != 0u) {
1881     file_operations->DeleteFile(
1882         path_.AppendASCII(GetSparseFilenameFromEntryFileKey(entry_file_key_)));
1883   }
1884   file_tracker_->Close(this, SimpleFileTracker::SubFile::FILE_SPARSE);
1885   sparse_file_open_ = false;
1886 }
1887 
TruncateSparseFile(base::File * sparse_file)1888 bool SimpleSynchronousEntry::TruncateSparseFile(base::File* sparse_file) {
1889   DCHECK(sparse_file_open());
1890 
1891   int64_t header_and_key_length = sizeof(SimpleFileHeader) + key_.size();
1892   if (!sparse_file->SetLength(header_and_key_length)) {
1893     DLOG(WARNING) << "Could not truncate sparse file";
1894     return false;
1895   }
1896 
1897   sparse_ranges_.clear();
1898   sparse_tail_offset_ = header_and_key_length;
1899 
1900   return true;
1901 }
1902 
InitializeSparseFile(base::File * sparse_file)1903 bool SimpleSynchronousEntry::InitializeSparseFile(base::File* sparse_file) {
1904   SimpleFileHeader header;
1905   header.initial_magic_number = kSimpleInitialMagicNumber;
1906   header.version = kSimpleVersion;
1907   header.key_length = key_.size();
1908   header.key_hash = base::PersistentHash(key_);
1909 
1910   int header_write_result =
1911       sparse_file->Write(0, reinterpret_cast<char*>(&header), sizeof(header));
1912   if (header_write_result != sizeof(header)) {
1913     DLOG(WARNING) << "Could not write sparse file header";
1914     return false;
1915   }
1916 
1917   int key_write_result =
1918       sparse_file->Write(sizeof(header), key_.data(), key_.size());
1919   if (key_write_result != base::checked_cast<int>(key_.size())) {
1920     DLOG(WARNING) << "Could not write sparse file key";
1921     return false;
1922   }
1923 
1924   sparse_ranges_.clear();
1925   sparse_tail_offset_ = sizeof(header) + key_.size();
1926 
1927   return true;
1928 }
1929 
ScanSparseFile(base::File * sparse_file,int32_t * out_sparse_data_size)1930 bool SimpleSynchronousEntry::ScanSparseFile(base::File* sparse_file,
1931                                             int32_t* out_sparse_data_size) {
1932   int64_t sparse_data_size = 0;
1933 
1934   SimpleFileHeader header;
1935   int header_read_result =
1936       sparse_file->Read(0, reinterpret_cast<char*>(&header), sizeof(header));
1937   if (header_read_result != sizeof(header)) {
1938     DLOG(WARNING) << "Could not read header from sparse file.";
1939     return false;
1940   }
1941 
1942   if (header.initial_magic_number != kSimpleInitialMagicNumber) {
1943     DLOG(WARNING) << "Sparse file magic number did not match.";
1944     return false;
1945   }
1946 
1947   if (header.version < kLastCompatSparseVersion ||
1948       header.version > kSimpleVersion) {
1949     DLOG(WARNING) << "Sparse file unreadable version.";
1950     return false;
1951   }
1952 
1953   sparse_ranges_.clear();
1954 
1955   int64_t range_header_offset = sizeof(header) + key_.size();
1956   while (true) {
1957     SimpleFileSparseRangeHeader range_header;
1958     int range_header_read_result = sparse_file->Read(
1959         range_header_offset, reinterpret_cast<char*>(&range_header),
1960         sizeof(range_header));
1961     if (range_header_read_result == 0)
1962       break;
1963     if (range_header_read_result != sizeof(range_header)) {
1964       DLOG(WARNING) << "Could not read sparse range header.";
1965       return false;
1966     }
1967 
1968     if (range_header.sparse_range_magic_number !=
1969         kSimpleSparseRangeMagicNumber) {
1970       DLOG(WARNING) << "Invalid sparse range header magic number.";
1971       return false;
1972     }
1973 
1974     SparseRange range;
1975     range.offset = range_header.offset;
1976     range.length = range_header.length;
1977     range.data_crc32 = range_header.data_crc32;
1978     range.file_offset = range_header_offset + sizeof(range_header);
1979     sparse_ranges_.insert(std::make_pair(range.offset, range));
1980 
1981     range_header_offset += sizeof(range_header) + range.length;
1982 
1983     DCHECK_GE(sparse_data_size + range.length, sparse_data_size);
1984     sparse_data_size += range.length;
1985   }
1986 
1987   *out_sparse_data_size = static_cast<int32_t>(sparse_data_size);
1988   sparse_tail_offset_ = range_header_offset;
1989 
1990   return true;
1991 }
1992 
ReadSparseRange(base::File * sparse_file,const SparseRange * range,int offset,int len,char * buf)1993 bool SimpleSynchronousEntry::ReadSparseRange(base::File* sparse_file,
1994                                              const SparseRange* range,
1995                                              int offset,
1996                                              int len,
1997                                              char* buf) {
1998   DCHECK(range);
1999   DCHECK(buf);
2000   DCHECK_LE(offset, range->length);
2001   DCHECK_LE(offset + len, range->length);
2002 
2003   int bytes_read = sparse_file->Read(range->file_offset + offset, buf, len);
2004   if (bytes_read < len) {
2005     DLOG(WARNING) << "Could not read sparse range.";
2006     return false;
2007   }
2008 
2009   // If we read the whole range and we have a crc32, check it.
2010   if (offset == 0 && len == range->length && range->data_crc32 != 0) {
2011     if (simple_util::Crc32(buf, len) != range->data_crc32) {
2012       DLOG(WARNING) << "Sparse range crc32 mismatch.";
2013       return false;
2014     }
2015   }
2016   // TODO(morlovich): Incremental crc32 calculation?
2017 
2018   return true;
2019 }
2020 
WriteSparseRange(base::File * sparse_file,SparseRange * range,int offset,int len,const char * buf)2021 bool SimpleSynchronousEntry::WriteSparseRange(base::File* sparse_file,
2022                                               SparseRange* range,
2023                                               int offset,
2024                                               int len,
2025                                               const char* buf) {
2026   DCHECK(range);
2027   DCHECK(buf);
2028   DCHECK_LE(offset, range->length);
2029   DCHECK_LE(offset + len, range->length);
2030 
2031   uint32_t new_crc32 = 0;
2032   if (offset == 0 && len == range->length) {
2033     new_crc32 = simple_util::Crc32(buf, len);
2034   }
2035 
2036   if (new_crc32 != range->data_crc32) {
2037     range->data_crc32 = new_crc32;
2038 
2039     SimpleFileSparseRangeHeader header;
2040     header.sparse_range_magic_number = kSimpleSparseRangeMagicNumber;
2041     header.offset = range->offset;
2042     header.length = range->length;
2043     header.data_crc32 = range->data_crc32;
2044 
2045     int bytes_written =
2046         sparse_file->Write(range->file_offset - sizeof(header),
2047                            reinterpret_cast<char*>(&header), sizeof(header));
2048     if (bytes_written != base::checked_cast<int>(sizeof(header))) {
2049       DLOG(WARNING) << "Could not rewrite sparse range header.";
2050       return false;
2051     }
2052   }
2053 
2054   int bytes_written = sparse_file->Write(range->file_offset + offset, buf, len);
2055   if (bytes_written < len) {
2056     DLOG(WARNING) << "Could not write sparse range.";
2057     return false;
2058   }
2059 
2060   return true;
2061 }
2062 
AppendSparseRange(base::File * sparse_file,int64_t offset,int len,const char * buf)2063 bool SimpleSynchronousEntry::AppendSparseRange(base::File* sparse_file,
2064                                                int64_t offset,
2065                                                int len,
2066                                                const char* buf) {
2067   DCHECK_GE(offset, 0);
2068   DCHECK_GT(len, 0);
2069   DCHECK(buf);
2070 
2071   uint32_t data_crc32 = simple_util::Crc32(buf, len);
2072 
2073   SimpleFileSparseRangeHeader header;
2074   header.sparse_range_magic_number = kSimpleSparseRangeMagicNumber;
2075   header.offset = offset;
2076   header.length = len;
2077   header.data_crc32 = data_crc32;
2078 
2079   int bytes_written = sparse_file->Write(
2080       sparse_tail_offset_, reinterpret_cast<char*>(&header), sizeof(header));
2081   if (bytes_written != base::checked_cast<int>(sizeof(header))) {
2082     DLOG(WARNING) << "Could not append sparse range header.";
2083     return false;
2084   }
2085   sparse_tail_offset_ += bytes_written;
2086 
2087   bytes_written = sparse_file->Write(sparse_tail_offset_, buf, len);
2088   if (bytes_written < len) {
2089     DLOG(WARNING) << "Could not append sparse range data.";
2090     return false;
2091   }
2092   int64_t data_file_offset = sparse_tail_offset_;
2093   sparse_tail_offset_ += bytes_written;
2094 
2095   SparseRange range;
2096   range.offset = offset;
2097   range.length = len;
2098   range.data_crc32 = data_crc32;
2099   range.file_offset = data_file_offset;
2100   sparse_ranges_.insert(std::make_pair(offset, range));
2101 
2102   return true;
2103 }
2104 
2105 }  // namespace disk_cache
2106