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