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