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