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