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