Roll src/third_party/WebKit d9c6159:8139f33 (svn 201974:201975)
[chromium-blink-merge.git] / net / disk_cache / simple / simple_synchronous_entry.cc
blob2c0412db17f061e11e940113556a026418a05b55
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "net/disk_cache/simple/simple_synchronous_entry.h"
7 #include <algorithm>
8 #include <cstring>
9 #include <functional>
10 #include <limits>
12 #include "base/basictypes.h"
13 #include "base/compiler_specific.h"
14 #include "base/files/file_util.h"
15 #include "base/hash.h"
16 #include "base/location.h"
17 #include "base/metrics/histogram_macros.h"
18 #include "base/numerics/safe_conversions.h"
19 #include "base/sha1.h"
20 #include "base/strings/stringprintf.h"
21 #include "base/timer/elapsed_timer.h"
22 #include "net/base/io_buffer.h"
23 #include "net/base/net_errors.h"
24 #include "net/disk_cache/simple/simple_backend_version.h"
25 #include "net/disk_cache/simple/simple_histogram_macros.h"
26 #include "net/disk_cache/simple/simple_util.h"
27 #include "third_party/zlib/zlib.h"
29 using base::File;
30 using base::FilePath;
31 using base::Time;
33 namespace {
35 // Used in histograms, please only add entries at the end.
36 enum OpenEntryResult {
37 OPEN_ENTRY_SUCCESS = 0,
38 OPEN_ENTRY_PLATFORM_FILE_ERROR = 1,
39 OPEN_ENTRY_CANT_READ_HEADER = 2,
40 OPEN_ENTRY_BAD_MAGIC_NUMBER = 3,
41 OPEN_ENTRY_BAD_VERSION = 4,
42 OPEN_ENTRY_CANT_READ_KEY = 5,
43 // OPEN_ENTRY_KEY_MISMATCH = 6, Deprecated.
44 OPEN_ENTRY_KEY_HASH_MISMATCH = 7,
45 OPEN_ENTRY_SPARSE_OPEN_FAILED = 8,
46 OPEN_ENTRY_MAX = 9,
49 // Used in histograms, please only add entries at the end.
50 enum WriteResult {
51 WRITE_RESULT_SUCCESS = 0,
52 WRITE_RESULT_PRETRUNCATE_FAILURE,
53 WRITE_RESULT_WRITE_FAILURE,
54 WRITE_RESULT_TRUNCATE_FAILURE,
55 WRITE_RESULT_LAZY_STREAM_ENTRY_DOOMED,
56 WRITE_RESULT_LAZY_CREATE_FAILURE,
57 WRITE_RESULT_LAZY_INITIALIZE_FAILURE,
58 WRITE_RESULT_MAX,
61 // Used in histograms, please only add entries at the end.
62 enum CheckEOFResult {
63 CHECK_EOF_RESULT_SUCCESS,
64 CHECK_EOF_RESULT_READ_FAILURE,
65 CHECK_EOF_RESULT_MAGIC_NUMBER_MISMATCH,
66 CHECK_EOF_RESULT_CRC_MISMATCH,
67 CHECK_EOF_RESULT_MAX,
70 // Used in histograms, please only add entries at the end.
71 enum CloseResult {
72 CLOSE_RESULT_SUCCESS,
73 CLOSE_RESULT_WRITE_FAILURE,
76 void RecordSyncOpenResult(net::CacheType cache_type,
77 OpenEntryResult result,
78 bool had_index) {
79 DCHECK_LT(result, OPEN_ENTRY_MAX);
80 SIMPLE_CACHE_UMA(ENUMERATION,
81 "SyncOpenResult", cache_type, result, OPEN_ENTRY_MAX);
82 if (had_index) {
83 SIMPLE_CACHE_UMA(ENUMERATION,
84 "SyncOpenResult_WithIndex", cache_type,
85 result, OPEN_ENTRY_MAX);
86 } else {
87 SIMPLE_CACHE_UMA(ENUMERATION,
88 "SyncOpenResult_WithoutIndex", cache_type,
89 result, OPEN_ENTRY_MAX);
93 void RecordWriteResult(net::CacheType cache_type, WriteResult result) {
94 SIMPLE_CACHE_UMA(ENUMERATION,
95 "SyncWriteResult", cache_type, result, WRITE_RESULT_MAX);
98 void RecordCheckEOFResult(net::CacheType cache_type, CheckEOFResult result) {
99 SIMPLE_CACHE_UMA(ENUMERATION,
100 "SyncCheckEOFResult", cache_type,
101 result, CHECK_EOF_RESULT_MAX);
104 void RecordCloseResult(net::CacheType cache_type, CloseResult result) {
105 SIMPLE_CACHE_UMA(ENUMERATION,
106 "SyncCloseResult", cache_type, result, WRITE_RESULT_MAX);
109 bool CanOmitEmptyFile(int file_index) {
110 DCHECK_GE(file_index, 0);
111 DCHECK_LT(file_index, disk_cache::kSimpleEntryFileCount);
112 return file_index == disk_cache::simple_util::GetFileIndexFromStreamIndex(2);
115 } // namespace
117 namespace disk_cache {
119 using simple_util::GetEntryHashKey;
120 using simple_util::GetFilenameFromEntryHashAndFileIndex;
121 using simple_util::GetSparseFilenameFromEntryHash;
122 using simple_util::GetDataSizeFromKeyAndFileSize;
123 using simple_util::GetFileSizeFromKeyAndDataSize;
124 using simple_util::GetFileIndexFromStreamIndex;
126 SimpleEntryStat::SimpleEntryStat(base::Time last_used,
127 base::Time last_modified,
128 const int32 data_size[],
129 const int32 sparse_data_size)
130 : last_used_(last_used),
131 last_modified_(last_modified),
132 sparse_data_size_(sparse_data_size) {
133 memcpy(data_size_, data_size, sizeof(data_size_));
136 int SimpleEntryStat::GetOffsetInFile(const std::string& key,
137 int offset,
138 int stream_index) const {
139 const size_t headers_size = sizeof(SimpleFileHeader) + key.size();
140 const size_t additional_offset =
141 stream_index == 0 ? data_size_[1] + sizeof(SimpleFileEOF) : 0;
142 return headers_size + offset + additional_offset;
145 int SimpleEntryStat::GetEOFOffsetInFile(const std::string& key,
146 int stream_index) const {
147 return GetOffsetInFile(key, data_size_[stream_index], stream_index);
150 int SimpleEntryStat::GetLastEOFOffsetInFile(const std::string& key,
151 int stream_index) const {
152 const int file_index = GetFileIndexFromStreamIndex(stream_index);
153 const int eof_data_offset =
154 file_index == 0 ? data_size_[0] + data_size_[1] + sizeof(SimpleFileEOF)
155 : data_size_[2];
156 return GetOffsetInFile(key, eof_data_offset, stream_index);
159 int64 SimpleEntryStat::GetFileSize(const std::string& key,
160 int file_index) const {
161 const int32 total_data_size =
162 file_index == 0 ? data_size_[0] + data_size_[1] + sizeof(SimpleFileEOF)
163 : data_size_[2];
164 return GetFileSizeFromKeyAndDataSize(key, total_data_size);
167 SimpleEntryCreationResults::SimpleEntryCreationResults(
168 SimpleEntryStat entry_stat)
169 : sync_entry(NULL),
170 entry_stat(entry_stat),
171 stream_0_crc32(crc32(0, Z_NULL, 0)),
172 result(net::OK) {
175 SimpleEntryCreationResults::~SimpleEntryCreationResults() {
178 SimpleSynchronousEntry::CRCRecord::CRCRecord() : index(-1),
179 has_crc32(false),
180 data_crc32(0) {
183 SimpleSynchronousEntry::CRCRecord::CRCRecord(int index_p,
184 bool has_crc32_p,
185 uint32 data_crc32_p)
186 : index(index_p),
187 has_crc32(has_crc32_p),
188 data_crc32(data_crc32_p) {}
190 SimpleSynchronousEntry::EntryOperationData::EntryOperationData(int index_p,
191 int offset_p,
192 int buf_len_p)
193 : index(index_p),
194 offset(offset_p),
195 buf_len(buf_len_p) {}
197 SimpleSynchronousEntry::EntryOperationData::EntryOperationData(int index_p,
198 int offset_p,
199 int buf_len_p,
200 bool truncate_p,
201 bool doomed_p)
202 : index(index_p),
203 offset(offset_p),
204 buf_len(buf_len_p),
205 truncate(truncate_p),
206 doomed(doomed_p) {}
208 SimpleSynchronousEntry::EntryOperationData::EntryOperationData(
209 int64 sparse_offset_p,
210 int buf_len_p)
211 : sparse_offset(sparse_offset_p),
212 buf_len(buf_len_p) {}
214 // static
215 void SimpleSynchronousEntry::OpenEntry(
216 net::CacheType cache_type,
217 const FilePath& path,
218 const uint64 entry_hash,
219 bool had_index,
220 SimpleEntryCreationResults *out_results) {
221 base::ElapsedTimer open_time;
222 SimpleSynchronousEntry* sync_entry =
223 new SimpleSynchronousEntry(cache_type, path, "", entry_hash);
224 out_results->result =
225 sync_entry->InitializeForOpen(had_index,
226 &out_results->entry_stat,
227 &out_results->stream_0_data,
228 &out_results->stream_0_crc32);
229 if (out_results->result != net::OK) {
230 sync_entry->Doom();
231 delete sync_entry;
232 out_results->sync_entry = NULL;
233 out_results->stream_0_data = NULL;
234 return;
236 UMA_HISTOGRAM_TIMES("SimpleCache.DiskOpenLatency", open_time.Elapsed());
237 out_results->sync_entry = sync_entry;
240 // static
241 void SimpleSynchronousEntry::CreateEntry(
242 net::CacheType cache_type,
243 const FilePath& path,
244 const std::string& key,
245 const uint64 entry_hash,
246 bool had_index,
247 SimpleEntryCreationResults *out_results) {
248 DCHECK_EQ(entry_hash, GetEntryHashKey(key));
249 SimpleSynchronousEntry* sync_entry =
250 new SimpleSynchronousEntry(cache_type, path, key, entry_hash);
251 out_results->result = sync_entry->InitializeForCreate(
252 had_index, &out_results->entry_stat);
253 if (out_results->result != net::OK) {
254 if (out_results->result != net::ERR_FILE_EXISTS)
255 sync_entry->Doom();
256 delete sync_entry;
257 out_results->sync_entry = NULL;
258 return;
260 out_results->sync_entry = sync_entry;
263 // static
264 int SimpleSynchronousEntry::DoomEntry(
265 const FilePath& path,
266 uint64 entry_hash) {
267 const bool deleted_well = DeleteFilesForEntryHash(path, entry_hash);
268 return deleted_well ? net::OK : net::ERR_FAILED;
271 // static
272 int SimpleSynchronousEntry::DoomEntrySet(
273 const std::vector<uint64>* key_hashes,
274 const FilePath& path) {
275 const size_t did_delete_count = std::count_if(
276 key_hashes->begin(), key_hashes->end(), std::bind1st(
277 std::ptr_fun(SimpleSynchronousEntry::DeleteFilesForEntryHash), path));
278 return (did_delete_count == key_hashes->size()) ? net::OK : net::ERR_FAILED;
281 void SimpleSynchronousEntry::ReadData(const EntryOperationData& in_entry_op,
282 net::IOBuffer* out_buf,
283 uint32* out_crc32,
284 SimpleEntryStat* entry_stat,
285 int* out_result) const {
286 DCHECK(initialized_);
287 DCHECK_NE(0, in_entry_op.index);
288 const int64 file_offset =
289 entry_stat->GetOffsetInFile(key_, in_entry_op.offset, in_entry_op.index);
290 int file_index = GetFileIndexFromStreamIndex(in_entry_op.index);
291 // Zero-length reads and reads to the empty streams of omitted files should
292 // be handled in the SimpleEntryImpl.
293 DCHECK_GT(in_entry_op.buf_len, 0);
294 DCHECK(!empty_file_omitted_[file_index]);
295 File* file = const_cast<File*>(&files_[file_index]);
296 int bytes_read =
297 file->Read(file_offset, out_buf->data(), in_entry_op.buf_len);
298 if (bytes_read > 0) {
299 entry_stat->set_last_used(Time::Now());
300 *out_crc32 = crc32(crc32(0L, Z_NULL, 0),
301 reinterpret_cast<const Bytef*>(out_buf->data()),
302 bytes_read);
304 if (bytes_read >= 0) {
305 *out_result = bytes_read;
306 } else {
307 *out_result = net::ERR_CACHE_READ_FAILURE;
308 Doom();
312 void SimpleSynchronousEntry::WriteData(const EntryOperationData& in_entry_op,
313 net::IOBuffer* in_buf,
314 SimpleEntryStat* out_entry_stat,
315 int* out_result) {
316 DCHECK(initialized_);
317 DCHECK_NE(0, in_entry_op.index);
318 int index = in_entry_op.index;
319 int file_index = GetFileIndexFromStreamIndex(index);
320 int offset = in_entry_op.offset;
321 int buf_len = in_entry_op.buf_len;
322 bool truncate = in_entry_op.truncate;
323 bool doomed = in_entry_op.doomed;
324 const int64 file_offset = out_entry_stat->GetOffsetInFile(
325 key_, in_entry_op.offset, in_entry_op.index);
326 bool extending_by_write = offset + buf_len > out_entry_stat->data_size(index);
328 if (empty_file_omitted_[file_index]) {
329 // Don't create a new file if the entry has been doomed, to avoid it being
330 // mixed up with a newly-created entry with the same key.
331 if (doomed) {
332 DLOG(WARNING) << "Rejecting write to lazily omitted stream "
333 << in_entry_op.index << " of doomed cache entry.";
334 RecordWriteResult(cache_type_, WRITE_RESULT_LAZY_STREAM_ENTRY_DOOMED);
335 *out_result = net::ERR_CACHE_WRITE_FAILURE;
336 return;
338 File::Error error;
339 if (!MaybeCreateFile(file_index, FILE_REQUIRED, &error)) {
340 RecordWriteResult(cache_type_, WRITE_RESULT_LAZY_CREATE_FAILURE);
341 Doom();
342 *out_result = net::ERR_CACHE_WRITE_FAILURE;
343 return;
345 CreateEntryResult result;
346 if (!InitializeCreatedFile(file_index, &result)) {
347 RecordWriteResult(cache_type_, WRITE_RESULT_LAZY_INITIALIZE_FAILURE);
348 Doom();
349 *out_result = net::ERR_CACHE_WRITE_FAILURE;
350 return;
353 DCHECK(!empty_file_omitted_[file_index]);
355 if (extending_by_write) {
356 // The EOF record and the eventual stream afterward need to be zeroed out.
357 const int64 file_eof_offset =
358 out_entry_stat->GetEOFOffsetInFile(key_, index);
359 if (!files_[file_index].SetLength(file_eof_offset)) {
360 RecordWriteResult(cache_type_, WRITE_RESULT_PRETRUNCATE_FAILURE);
361 Doom();
362 *out_result = net::ERR_CACHE_WRITE_FAILURE;
363 return;
366 if (buf_len > 0) {
367 if (files_[file_index].Write(file_offset, in_buf->data(), buf_len) !=
368 buf_len) {
369 RecordWriteResult(cache_type_, WRITE_RESULT_WRITE_FAILURE);
370 Doom();
371 *out_result = net::ERR_CACHE_WRITE_FAILURE;
372 return;
375 if (!truncate && (buf_len > 0 || !extending_by_write)) {
376 out_entry_stat->set_data_size(
377 index, std::max(out_entry_stat->data_size(index), offset + buf_len));
378 } else {
379 out_entry_stat->set_data_size(index, offset + buf_len);
380 int file_eof_offset = out_entry_stat->GetLastEOFOffsetInFile(key_, index);
381 if (!files_[file_index].SetLength(file_eof_offset)) {
382 RecordWriteResult(cache_type_, WRITE_RESULT_TRUNCATE_FAILURE);
383 Doom();
384 *out_result = net::ERR_CACHE_WRITE_FAILURE;
385 return;
389 RecordWriteResult(cache_type_, WRITE_RESULT_SUCCESS);
390 base::Time modification_time = Time::Now();
391 out_entry_stat->set_last_used(modification_time);
392 out_entry_stat->set_last_modified(modification_time);
393 *out_result = buf_len;
396 void SimpleSynchronousEntry::ReadSparseData(
397 const EntryOperationData& in_entry_op,
398 net::IOBuffer* out_buf,
399 base::Time* out_last_used,
400 int* out_result) {
401 DCHECK(initialized_);
402 int64 offset = in_entry_op.sparse_offset;
403 int buf_len = in_entry_op.buf_len;
405 char* buf = out_buf->data();
406 int read_so_far = 0;
408 // Find the first sparse range at or after the requested offset.
409 SparseRangeIterator it = sparse_ranges_.lower_bound(offset);
411 if (it != sparse_ranges_.begin()) {
412 // Hop back one range and read the one overlapping with the start.
413 --it;
414 SparseRange* found_range = &it->second;
415 DCHECK_EQ(it->first, found_range->offset);
416 if (found_range->offset + found_range->length > offset) {
417 DCHECK_GE(found_range->length, 0);
418 DCHECK_LE(found_range->length, kint32max);
419 DCHECK_GE(offset - found_range->offset, 0);
420 DCHECK_LE(offset - found_range->offset, kint32max);
421 int net_offset = static_cast<int>(offset - found_range->offset);
422 int range_len_after_offset =
423 static_cast<int>(found_range->length - net_offset);
424 DCHECK_GE(range_len_after_offset, 0);
426 int len_to_read = std::min(buf_len, range_len_after_offset);
427 if (!ReadSparseRange(found_range, net_offset, len_to_read, buf)) {
428 *out_result = net::ERR_CACHE_READ_FAILURE;
429 return;
431 read_so_far += len_to_read;
433 ++it;
436 // Keep reading until the buffer is full or there is not another contiguous
437 // range.
438 while (read_so_far < buf_len &&
439 it != sparse_ranges_.end() &&
440 it->second.offset == offset + read_so_far) {
441 SparseRange* found_range = &it->second;
442 DCHECK_EQ(it->first, found_range->offset);
443 int range_len = base::saturated_cast<int>(found_range->length);
444 int len_to_read = std::min(buf_len - read_so_far, range_len);
445 if (!ReadSparseRange(found_range, 0, len_to_read, buf + read_so_far)) {
446 *out_result = net::ERR_CACHE_READ_FAILURE;
447 return;
449 read_so_far += len_to_read;
450 ++it;
453 *out_result = read_so_far;
456 void SimpleSynchronousEntry::WriteSparseData(
457 const EntryOperationData& in_entry_op,
458 net::IOBuffer* in_buf,
459 uint64 max_sparse_data_size,
460 SimpleEntryStat* out_entry_stat,
461 int* out_result) {
462 DCHECK(initialized_);
463 int64 offset = in_entry_op.sparse_offset;
464 int buf_len = in_entry_op.buf_len;
466 const char* buf = in_buf->data();
467 int written_so_far = 0;
468 int appended_so_far = 0;
470 if (!sparse_file_open() && !CreateSparseFile()) {
471 *out_result = net::ERR_CACHE_WRITE_FAILURE;
472 return;
475 uint64 sparse_data_size = out_entry_stat->sparse_data_size();
476 // This is a pessimistic estimate; it assumes the entire buffer is going to
477 // be appended as a new range, not written over existing ranges.
478 if (sparse_data_size + buf_len > max_sparse_data_size) {
479 DVLOG(1) << "Truncating sparse data file (" << sparse_data_size << " + "
480 << buf_len << " > " << max_sparse_data_size << ")";
481 TruncateSparseFile();
484 SparseRangeIterator it = sparse_ranges_.lower_bound(offset);
486 if (it != sparse_ranges_.begin()) {
487 --it;
488 SparseRange* found_range = &it->second;
489 if (found_range->offset + found_range->length > offset) {
490 DCHECK_GE(found_range->length, 0);
491 DCHECK_LE(found_range->length, kint32max);
492 DCHECK_GE(offset - found_range->offset, 0);
493 DCHECK_LE(offset - found_range->offset, kint32max);
494 int net_offset = static_cast<int>(offset - found_range->offset);
495 int range_len_after_offset =
496 static_cast<int>(found_range->length - net_offset);
497 DCHECK_GE(range_len_after_offset, 0);
499 int len_to_write = std::min(buf_len, range_len_after_offset);
500 if (!WriteSparseRange(found_range, net_offset, len_to_write, buf)) {
501 *out_result = net::ERR_CACHE_WRITE_FAILURE;
502 return;
504 written_so_far += len_to_write;
506 ++it;
509 while (written_so_far < buf_len &&
510 it != sparse_ranges_.end() &&
511 it->second.offset < offset + buf_len) {
512 SparseRange* found_range = &it->second;
513 if (offset + written_so_far < found_range->offset) {
514 int len_to_append =
515 static_cast<int>(found_range->offset - (offset + written_so_far));
516 if (!AppendSparseRange(offset + written_so_far,
517 len_to_append,
518 buf + written_so_far)) {
519 *out_result = net::ERR_CACHE_WRITE_FAILURE;
520 return;
522 written_so_far += len_to_append;
523 appended_so_far += len_to_append;
525 int range_len = base::saturated_cast<int>(found_range->length);
526 int len_to_write = std::min(buf_len - written_so_far, range_len);
527 if (!WriteSparseRange(found_range,
529 len_to_write,
530 buf + written_so_far)) {
531 *out_result = net::ERR_CACHE_WRITE_FAILURE;
532 return;
534 written_so_far += len_to_write;
535 ++it;
538 if (written_so_far < buf_len) {
539 int len_to_append = buf_len - written_so_far;
540 if (!AppendSparseRange(offset + written_so_far,
541 len_to_append,
542 buf + written_so_far)) {
543 *out_result = net::ERR_CACHE_WRITE_FAILURE;
544 return;
546 written_so_far += len_to_append;
547 appended_so_far += len_to_append;
550 DCHECK_EQ(buf_len, written_so_far);
552 base::Time modification_time = Time::Now();
553 out_entry_stat->set_last_used(modification_time);
554 out_entry_stat->set_last_modified(modification_time);
555 int32 old_sparse_data_size = out_entry_stat->sparse_data_size();
556 out_entry_stat->set_sparse_data_size(old_sparse_data_size + appended_so_far);
557 *out_result = written_so_far;
560 void SimpleSynchronousEntry::GetAvailableRange(
561 const EntryOperationData& in_entry_op,
562 int64* out_start,
563 int* out_result) {
564 DCHECK(initialized_);
565 int64 offset = in_entry_op.sparse_offset;
566 int len = in_entry_op.buf_len;
568 SparseRangeIterator it = sparse_ranges_.lower_bound(offset);
570 int64 start = offset;
571 int64 avail_so_far = 0;
573 if (it != sparse_ranges_.end() && it->second.offset < offset + len)
574 start = it->second.offset;
576 if ((it == sparse_ranges_.end() || it->second.offset > offset) &&
577 it != sparse_ranges_.begin()) {
578 --it;
579 if (it->second.offset + it->second.length > offset) {
580 start = offset;
581 avail_so_far = (it->second.offset + it->second.length) - offset;
583 ++it;
586 while (start + avail_so_far < offset + len &&
587 it != sparse_ranges_.end() &&
588 it->second.offset == start + avail_so_far) {
589 avail_so_far += it->second.length;
590 ++it;
593 int64 len_from_start = len - (start - offset);
594 *out_start = start;
595 *out_result = static_cast<int>(std::min(avail_so_far, len_from_start));
598 void SimpleSynchronousEntry::CheckEOFRecord(int index,
599 const SimpleEntryStat& entry_stat,
600 uint32 expected_crc32,
601 int* out_result) const {
602 DCHECK(initialized_);
603 uint32 crc32;
604 bool has_crc32;
605 int stream_size;
606 *out_result =
607 GetEOFRecordData(index, entry_stat, &has_crc32, &crc32, &stream_size);
608 if (*out_result != net::OK) {
609 Doom();
610 return;
612 if (has_crc32 && crc32 != expected_crc32) {
613 DVLOG(1) << "EOF record had bad crc.";
614 *out_result = net::ERR_CACHE_CHECKSUM_MISMATCH;
615 RecordCheckEOFResult(cache_type_, CHECK_EOF_RESULT_CRC_MISMATCH);
616 Doom();
617 return;
619 RecordCheckEOFResult(cache_type_, CHECK_EOF_RESULT_SUCCESS);
622 void SimpleSynchronousEntry::Close(
623 const SimpleEntryStat& entry_stat,
624 scoped_ptr<std::vector<CRCRecord> > crc32s_to_write,
625 net::GrowableIOBuffer* stream_0_data) {
626 DCHECK(stream_0_data);
627 // Write stream 0 data.
628 int stream_0_offset = entry_stat.GetOffsetInFile(key_, 0, 0);
629 if (files_[0].Write(stream_0_offset, stream_0_data->data(),
630 entry_stat.data_size(0)) !=
631 entry_stat.data_size(0)) {
632 RecordCloseResult(cache_type_, CLOSE_RESULT_WRITE_FAILURE);
633 DVLOG(1) << "Could not write stream 0 data.";
634 Doom();
637 for (std::vector<CRCRecord>::const_iterator it = crc32s_to_write->begin();
638 it != crc32s_to_write->end(); ++it) {
639 const int stream_index = it->index;
640 const int file_index = GetFileIndexFromStreamIndex(stream_index);
641 if (empty_file_omitted_[file_index])
642 continue;
644 SimpleFileEOF eof_record;
645 eof_record.stream_size = entry_stat.data_size(stream_index);
646 eof_record.final_magic_number = kSimpleFinalMagicNumber;
647 eof_record.flags = 0;
648 if (it->has_crc32)
649 eof_record.flags |= SimpleFileEOF::FLAG_HAS_CRC32;
650 eof_record.data_crc32 = it->data_crc32;
651 int eof_offset = entry_stat.GetEOFOffsetInFile(key_, stream_index);
652 // If stream 0 changed size, the file needs to be resized, otherwise the
653 // next open will yield wrong stream sizes. On stream 1 and stream 2 proper
654 // resizing of the file is handled in SimpleSynchronousEntry::WriteData().
655 if (stream_index == 0 &&
656 !files_[file_index].SetLength(eof_offset)) {
657 RecordCloseResult(cache_type_, CLOSE_RESULT_WRITE_FAILURE);
658 DVLOG(1) << "Could not truncate stream 0 file.";
659 Doom();
660 break;
662 if (files_[file_index].Write(eof_offset,
663 reinterpret_cast<const char*>(&eof_record),
664 sizeof(eof_record)) !=
665 sizeof(eof_record)) {
666 RecordCloseResult(cache_type_, CLOSE_RESULT_WRITE_FAILURE);
667 DVLOG(1) << "Could not write eof record.";
668 Doom();
669 break;
672 for (int i = 0; i < kSimpleEntryFileCount; ++i) {
673 if (empty_file_omitted_[i])
674 continue;
676 files_[i].Close();
677 const int64 file_size = entry_stat.GetFileSize(key_, i);
678 SIMPLE_CACHE_UMA(CUSTOM_COUNTS,
679 "LastClusterSize", cache_type_,
680 file_size % 4096, 0, 4097, 50);
681 const int64 cluster_loss = file_size % 4096 ? 4096 - file_size % 4096 : 0;
682 SIMPLE_CACHE_UMA(PERCENTAGE,
683 "LastClusterLossPercent", cache_type_,
684 static_cast<base::HistogramBase::Sample>(
685 cluster_loss * 100 / (cluster_loss + file_size)));
688 if (sparse_file_open())
689 sparse_file_.Close();
691 if (files_created_) {
692 const int stream2_file_index = GetFileIndexFromStreamIndex(2);
693 SIMPLE_CACHE_UMA(BOOLEAN, "EntryCreatedAndStream2Omitted", cache_type_,
694 empty_file_omitted_[stream2_file_index]);
696 RecordCloseResult(cache_type_, CLOSE_RESULT_SUCCESS);
697 have_open_files_ = false;
698 delete this;
701 SimpleSynchronousEntry::SimpleSynchronousEntry(net::CacheType cache_type,
702 const FilePath& path,
703 const std::string& key,
704 const uint64 entry_hash)
705 : cache_type_(cache_type),
706 path_(path),
707 entry_hash_(entry_hash),
708 key_(key),
709 have_open_files_(false),
710 initialized_(false) {
711 for (int i = 0; i < kSimpleEntryFileCount; ++i)
712 empty_file_omitted_[i] = false;
715 SimpleSynchronousEntry::~SimpleSynchronousEntry() {
716 DCHECK(!(have_open_files_ && initialized_));
717 if (have_open_files_)
718 CloseFiles();
721 bool SimpleSynchronousEntry::MaybeOpenFile(
722 int file_index,
723 File::Error* out_error) {
724 DCHECK(out_error);
726 FilePath filename = GetFilenameFromFileIndex(file_index);
727 int flags = File::FLAG_OPEN | File::FLAG_READ | File::FLAG_WRITE |
728 File::FLAG_SHARE_DELETE;
729 files_[file_index].Initialize(filename, flags);
730 *out_error = files_[file_index].error_details();
732 if (CanOmitEmptyFile(file_index) && !files_[file_index].IsValid() &&
733 *out_error == File::FILE_ERROR_NOT_FOUND) {
734 empty_file_omitted_[file_index] = true;
735 return true;
738 return files_[file_index].IsValid();
741 bool SimpleSynchronousEntry::MaybeCreateFile(
742 int file_index,
743 FileRequired file_required,
744 File::Error* out_error) {
745 DCHECK(out_error);
747 if (CanOmitEmptyFile(file_index) && file_required == FILE_NOT_REQUIRED) {
748 empty_file_omitted_[file_index] = true;
749 return true;
752 FilePath filename = GetFilenameFromFileIndex(file_index);
753 int flags = File::FLAG_CREATE | File::FLAG_READ | File::FLAG_WRITE |
754 File::FLAG_SHARE_DELETE;
755 files_[file_index].Initialize(filename, flags);
756 *out_error = files_[file_index].error_details();
758 empty_file_omitted_[file_index] = false;
760 return files_[file_index].IsValid();
763 bool SimpleSynchronousEntry::OpenFiles(
764 bool had_index,
765 SimpleEntryStat* out_entry_stat) {
766 for (int i = 0; i < kSimpleEntryFileCount; ++i) {
767 File::Error error;
768 if (!MaybeOpenFile(i, &error)) {
769 // TODO(ttuttle,gavinp): Remove one each of these triplets of histograms.
770 // We can calculate the third as the sum or difference of the other two.
771 RecordSyncOpenResult(
772 cache_type_, OPEN_ENTRY_PLATFORM_FILE_ERROR, had_index);
773 SIMPLE_CACHE_UMA(ENUMERATION,
774 "SyncOpenPlatformFileError", cache_type_,
775 -error, -base::File::FILE_ERROR_MAX);
776 if (had_index) {
777 SIMPLE_CACHE_UMA(ENUMERATION,
778 "SyncOpenPlatformFileError_WithIndex", cache_type_,
779 -error, -base::File::FILE_ERROR_MAX);
780 } else {
781 SIMPLE_CACHE_UMA(ENUMERATION,
782 "SyncOpenPlatformFileError_WithoutIndex",
783 cache_type_,
784 -error, -base::File::FILE_ERROR_MAX);
786 while (--i >= 0)
787 CloseFile(i);
788 return false;
792 have_open_files_ = true;
794 base::TimeDelta entry_age = base::Time::Now() - base::Time::UnixEpoch();
795 for (int i = 0; i < kSimpleEntryFileCount; ++i) {
796 if (empty_file_omitted_[i]) {
797 out_entry_stat->set_data_size(i + 1, 0);
798 continue;
801 File::Info file_info;
802 bool success = files_[i].GetInfo(&file_info);
803 base::Time file_last_modified;
804 if (!success) {
805 DLOG(WARNING) << "Could not get platform file info.";
806 continue;
808 out_entry_stat->set_last_used(file_info.last_accessed);
809 if (simple_util::GetMTime(path_, &file_last_modified))
810 out_entry_stat->set_last_modified(file_last_modified);
811 else
812 out_entry_stat->set_last_modified(file_info.last_modified);
814 base::TimeDelta stream_age =
815 base::Time::Now() - out_entry_stat->last_modified();
816 if (stream_age < entry_age)
817 entry_age = stream_age;
819 // Two things prevent from knowing the right values for |data_size|:
820 // 1) The key is not known, hence its length is unknown.
821 // 2) Stream 0 and stream 1 are in the same file, and the exact size for
822 // each will only be known when reading the EOF record for stream 0.
824 // The size for file 0 and 1 is temporarily kept in
825 // |data_size(1)| and |data_size(2)| respectively. Reading the key in
826 // InitializeForOpen yields the data size for each file. In the case of
827 // file hash_1, this is the total size of stream 2, and is assigned to
828 // data_size(2). In the case of file 0, it is the combined size of stream
829 // 0, stream 1 and one EOF record. The exact distribution of sizes between
830 // stream 1 and stream 0 is only determined after reading the EOF record
831 // for stream 0 in ReadAndValidateStream0.
832 out_entry_stat->set_data_size(i + 1, static_cast<int>(file_info.size));
834 SIMPLE_CACHE_UMA(CUSTOM_COUNTS,
835 "SyncOpenEntryAge", cache_type_,
836 entry_age.InHours(), 1, 1000, 50);
838 files_created_ = false;
840 return true;
843 bool SimpleSynchronousEntry::CreateFiles(
844 bool had_index,
845 SimpleEntryStat* out_entry_stat) {
846 for (int i = 0; i < kSimpleEntryFileCount; ++i) {
847 File::Error error;
848 if (!MaybeCreateFile(i, FILE_NOT_REQUIRED, &error)) {
849 // TODO(ttuttle,gavinp): Remove one each of these triplets of histograms.
850 // We can calculate the third as the sum or difference of the other two.
851 RecordSyncCreateResult(CREATE_ENTRY_PLATFORM_FILE_ERROR, had_index);
852 SIMPLE_CACHE_UMA(ENUMERATION,
853 "SyncCreatePlatformFileError", cache_type_,
854 -error, -base::File::FILE_ERROR_MAX);
855 if (had_index) {
856 SIMPLE_CACHE_UMA(ENUMERATION,
857 "SyncCreatePlatformFileError_WithIndex", cache_type_,
858 -error, -base::File::FILE_ERROR_MAX);
859 } else {
860 SIMPLE_CACHE_UMA(ENUMERATION,
861 "SyncCreatePlatformFileError_WithoutIndex",
862 cache_type_,
863 -error, -base::File::FILE_ERROR_MAX);
865 while (--i >= 0)
866 CloseFile(i);
867 return false;
871 have_open_files_ = true;
873 base::Time creation_time = Time::Now();
874 out_entry_stat->set_last_modified(creation_time);
875 out_entry_stat->set_last_used(creation_time);
876 for (int i = 0; i < kSimpleEntryStreamCount; ++i)
877 out_entry_stat->set_data_size(i, 0);
879 files_created_ = true;
881 return true;
884 void SimpleSynchronousEntry::CloseFile(int index) {
885 if (empty_file_omitted_[index]) {
886 empty_file_omitted_[index] = false;
887 } else {
888 DCHECK(files_[index].IsValid());
889 files_[index].Close();
892 if (sparse_file_open())
893 CloseSparseFile();
896 void SimpleSynchronousEntry::CloseFiles() {
897 for (int i = 0; i < kSimpleEntryFileCount; ++i)
898 CloseFile(i);
901 int SimpleSynchronousEntry::InitializeForOpen(
902 bool had_index,
903 SimpleEntryStat* out_entry_stat,
904 scoped_refptr<net::GrowableIOBuffer>* stream_0_data,
905 uint32* out_stream_0_crc32) {
906 DCHECK(!initialized_);
907 if (!OpenFiles(had_index, out_entry_stat)) {
908 DLOG(WARNING) << "Could not open platform files for entry.";
909 return net::ERR_FAILED;
911 for (int i = 0; i < kSimpleEntryFileCount; ++i) {
912 if (empty_file_omitted_[i])
913 continue;
915 SimpleFileHeader header;
916 int header_read_result =
917 files_[i].Read(0, reinterpret_cast<char*>(&header), sizeof(header));
918 if (header_read_result != sizeof(header)) {
919 DLOG(WARNING) << "Cannot read header from entry.";
920 RecordSyncOpenResult(cache_type_, OPEN_ENTRY_CANT_READ_HEADER, had_index);
921 return net::ERR_FAILED;
924 if (header.initial_magic_number != kSimpleInitialMagicNumber) {
925 // TODO(gavinp): This seems very bad; for now we log at WARNING, but we
926 // should give consideration to not saturating the log with these if that
927 // becomes a problem.
928 DLOG(WARNING) << "Magic number did not match.";
929 RecordSyncOpenResult(cache_type_, OPEN_ENTRY_BAD_MAGIC_NUMBER, had_index);
930 return net::ERR_FAILED;
933 if (header.version != kSimpleEntryVersionOnDisk) {
934 DLOG(WARNING) << "Unreadable version.";
935 RecordSyncOpenResult(cache_type_, OPEN_ENTRY_BAD_VERSION, had_index);
936 return net::ERR_FAILED;
939 scoped_ptr<char[]> key(new char[header.key_length]);
940 int key_read_result = files_[i].Read(sizeof(header), key.get(),
941 header.key_length);
942 if (key_read_result != implicit_cast<int>(header.key_length)) {
943 DLOG(WARNING) << "Cannot read key from entry.";
944 RecordSyncOpenResult(cache_type_, OPEN_ENTRY_CANT_READ_KEY, had_index);
945 return net::ERR_FAILED;
948 key_ = std::string(key.get(), header.key_length);
949 if (i == 0) {
950 // File size for stream 0 has been stored temporarily in data_size[1].
951 int total_data_size =
952 GetDataSizeFromKeyAndFileSize(key_, out_entry_stat->data_size(1));
953 int ret_value_stream_0 = ReadAndValidateStream0(
954 total_data_size, out_entry_stat, stream_0_data, out_stream_0_crc32);
955 if (ret_value_stream_0 != net::OK)
956 return ret_value_stream_0;
957 } else {
958 out_entry_stat->set_data_size(
959 2, GetDataSizeFromKeyAndFileSize(key_, out_entry_stat->data_size(2)));
960 if (out_entry_stat->data_size(2) < 0) {
961 DLOG(WARNING) << "Stream 2 file is too small.";
962 return net::ERR_FAILED;
966 if (base::Hash(key.get(), header.key_length) != header.key_hash) {
967 DLOG(WARNING) << "Hash mismatch on key.";
968 RecordSyncOpenResult(
969 cache_type_, OPEN_ENTRY_KEY_HASH_MISMATCH, had_index);
970 return net::ERR_FAILED;
974 int32 sparse_data_size = 0;
975 if (!OpenSparseFileIfExists(&sparse_data_size)) {
976 RecordSyncOpenResult(
977 cache_type_, OPEN_ENTRY_SPARSE_OPEN_FAILED, had_index);
978 return net::ERR_FAILED;
980 out_entry_stat->set_sparse_data_size(sparse_data_size);
982 bool removed_stream2 = false;
983 const int stream2_file_index = GetFileIndexFromStreamIndex(2);
984 DCHECK(CanOmitEmptyFile(stream2_file_index));
985 if (!empty_file_omitted_[stream2_file_index] &&
986 out_entry_stat->data_size(2) == 0) {
987 DVLOG(1) << "Removing empty stream 2 file.";
988 CloseFile(stream2_file_index);
989 DeleteFileForEntryHash(path_, entry_hash_, stream2_file_index);
990 empty_file_omitted_[stream2_file_index] = true;
991 removed_stream2 = true;
994 SIMPLE_CACHE_UMA(BOOLEAN, "EntryOpenedAndStream2Removed", cache_type_,
995 removed_stream2);
997 RecordSyncOpenResult(cache_type_, OPEN_ENTRY_SUCCESS, had_index);
998 initialized_ = true;
999 return net::OK;
1002 bool SimpleSynchronousEntry::InitializeCreatedFile(
1003 int file_index,
1004 CreateEntryResult* out_result) {
1005 SimpleFileHeader header;
1006 header.initial_magic_number = kSimpleInitialMagicNumber;
1007 header.version = kSimpleEntryVersionOnDisk;
1009 header.key_length = key_.size();
1010 header.key_hash = base::Hash(key_);
1012 int bytes_written = files_[file_index].Write(
1013 0, reinterpret_cast<char*>(&header), sizeof(header));
1014 if (bytes_written != sizeof(header)) {
1015 *out_result = CREATE_ENTRY_CANT_WRITE_HEADER;
1016 return false;
1019 bytes_written = files_[file_index].Write(sizeof(header), key_.data(),
1020 key_.size());
1021 if (bytes_written != implicit_cast<int>(key_.size())) {
1022 *out_result = CREATE_ENTRY_CANT_WRITE_KEY;
1023 return false;
1026 return true;
1029 int SimpleSynchronousEntry::InitializeForCreate(
1030 bool had_index,
1031 SimpleEntryStat* out_entry_stat) {
1032 DCHECK(!initialized_);
1033 if (!CreateFiles(had_index, out_entry_stat)) {
1034 DLOG(WARNING) << "Could not create platform files.";
1035 return net::ERR_FILE_EXISTS;
1037 for (int i = 0; i < kSimpleEntryFileCount; ++i) {
1038 if (empty_file_omitted_[i])
1039 continue;
1041 CreateEntryResult result;
1042 if (!InitializeCreatedFile(i, &result)) {
1043 RecordSyncCreateResult(result, had_index);
1044 return net::ERR_FAILED;
1047 RecordSyncCreateResult(CREATE_ENTRY_SUCCESS, had_index);
1048 initialized_ = true;
1049 return net::OK;
1052 int SimpleSynchronousEntry::ReadAndValidateStream0(
1053 int total_data_size,
1054 SimpleEntryStat* out_entry_stat,
1055 scoped_refptr<net::GrowableIOBuffer>* stream_0_data,
1056 uint32* out_stream_0_crc32) const {
1057 // Temporarily assign all the data size to stream 1 in order to read the
1058 // EOF record for stream 0, which contains the size of stream 0.
1059 out_entry_stat->set_data_size(0, 0);
1060 out_entry_stat->set_data_size(1, total_data_size - sizeof(SimpleFileEOF));
1062 bool has_crc32;
1063 uint32 read_crc32;
1064 int stream_0_size;
1065 int ret_value_crc32 = GetEOFRecordData(
1066 0, *out_entry_stat, &has_crc32, &read_crc32, &stream_0_size);
1067 if (ret_value_crc32 != net::OK)
1068 return ret_value_crc32;
1070 if (stream_0_size > out_entry_stat->data_size(1))
1071 return net::ERR_FAILED;
1073 // These are the real values of data size.
1074 out_entry_stat->set_data_size(0, stream_0_size);
1075 out_entry_stat->set_data_size(
1076 1, out_entry_stat->data_size(1) - stream_0_size);
1078 // Put stream 0 data in memory.
1079 *stream_0_data = new net::GrowableIOBuffer();
1080 (*stream_0_data)->SetCapacity(stream_0_size);
1081 int file_offset = out_entry_stat->GetOffsetInFile(key_, 0, 0);
1082 File* file = const_cast<File*>(&files_[0]);
1083 int bytes_read =
1084 file->Read(file_offset, (*stream_0_data)->data(), stream_0_size);
1085 if (bytes_read != stream_0_size)
1086 return net::ERR_FAILED;
1088 // Check the CRC32.
1089 uint32 expected_crc32 =
1090 stream_0_size == 0
1091 ? crc32(0, Z_NULL, 0)
1092 : crc32(crc32(0, Z_NULL, 0),
1093 reinterpret_cast<const Bytef*>((*stream_0_data)->data()),
1094 stream_0_size);
1095 if (has_crc32 && read_crc32 != expected_crc32) {
1096 DVLOG(1) << "EOF record had bad crc.";
1097 RecordCheckEOFResult(cache_type_, CHECK_EOF_RESULT_CRC_MISMATCH);
1098 return net::ERR_FAILED;
1100 *out_stream_0_crc32 = expected_crc32;
1101 RecordCheckEOFResult(cache_type_, CHECK_EOF_RESULT_SUCCESS);
1102 return net::OK;
1105 int SimpleSynchronousEntry::GetEOFRecordData(int index,
1106 const SimpleEntryStat& entry_stat,
1107 bool* out_has_crc32,
1108 uint32* out_crc32,
1109 int* out_data_size) const {
1110 SimpleFileEOF eof_record;
1111 int file_offset = entry_stat.GetEOFOffsetInFile(key_, index);
1112 int file_index = GetFileIndexFromStreamIndex(index);
1113 File* file = const_cast<File*>(&files_[file_index]);
1114 if (file->Read(file_offset, reinterpret_cast<char*>(&eof_record),
1115 sizeof(eof_record)) !=
1116 sizeof(eof_record)) {
1117 RecordCheckEOFResult(cache_type_, CHECK_EOF_RESULT_READ_FAILURE);
1118 return net::ERR_CACHE_CHECKSUM_READ_FAILURE;
1121 if (eof_record.final_magic_number != kSimpleFinalMagicNumber) {
1122 RecordCheckEOFResult(cache_type_, CHECK_EOF_RESULT_MAGIC_NUMBER_MISMATCH);
1123 DVLOG(1) << "EOF record had bad magic number.";
1124 return net::ERR_CACHE_CHECKSUM_READ_FAILURE;
1127 *out_has_crc32 = (eof_record.flags & SimpleFileEOF::FLAG_HAS_CRC32) ==
1128 SimpleFileEOF::FLAG_HAS_CRC32;
1129 *out_crc32 = eof_record.data_crc32;
1130 *out_data_size = eof_record.stream_size;
1131 SIMPLE_CACHE_UMA(BOOLEAN, "SyncCheckEOFHasCrc", cache_type_, *out_has_crc32);
1132 return net::OK;
1135 void SimpleSynchronousEntry::Doom() const {
1136 DeleteFilesForEntryHash(path_, entry_hash_);
1139 // static
1140 bool SimpleSynchronousEntry::DeleteFileForEntryHash(
1141 const FilePath& path,
1142 const uint64 entry_hash,
1143 const int file_index) {
1144 FilePath to_delete = path.AppendASCII(
1145 GetFilenameFromEntryHashAndFileIndex(entry_hash, file_index));
1146 return simple_util::SimpleCacheDeleteFile(to_delete);
1149 // static
1150 bool SimpleSynchronousEntry::DeleteFilesForEntryHash(
1151 const FilePath& path,
1152 const uint64 entry_hash) {
1153 bool result = true;
1154 for (int i = 0; i < kSimpleEntryFileCount; ++i) {
1155 if (!DeleteFileForEntryHash(path, entry_hash, i) && !CanOmitEmptyFile(i))
1156 result = false;
1158 FilePath to_delete = path.AppendASCII(
1159 GetSparseFilenameFromEntryHash(entry_hash));
1160 simple_util::SimpleCacheDeleteFile(to_delete);
1161 return result;
1164 void SimpleSynchronousEntry::RecordSyncCreateResult(CreateEntryResult result,
1165 bool had_index) {
1166 DCHECK_LT(result, CREATE_ENTRY_MAX);
1167 SIMPLE_CACHE_UMA(ENUMERATION,
1168 "SyncCreateResult", cache_type_, result, CREATE_ENTRY_MAX);
1169 if (had_index) {
1170 SIMPLE_CACHE_UMA(ENUMERATION,
1171 "SyncCreateResult_WithIndex", cache_type_,
1172 result, CREATE_ENTRY_MAX);
1173 } else {
1174 SIMPLE_CACHE_UMA(ENUMERATION,
1175 "SyncCreateResult_WithoutIndex", cache_type_,
1176 result, CREATE_ENTRY_MAX);
1180 FilePath SimpleSynchronousEntry::GetFilenameFromFileIndex(int file_index) {
1181 return path_.AppendASCII(
1182 GetFilenameFromEntryHashAndFileIndex(entry_hash_, file_index));
1185 bool SimpleSynchronousEntry::OpenSparseFileIfExists(
1186 int32* out_sparse_data_size) {
1187 DCHECK(!sparse_file_open());
1189 FilePath filename = path_.AppendASCII(
1190 GetSparseFilenameFromEntryHash(entry_hash_));
1191 int flags = File::FLAG_OPEN | File::FLAG_READ | File::FLAG_WRITE |
1192 File::FLAG_SHARE_DELETE;
1193 sparse_file_.Initialize(filename, flags);
1194 if (sparse_file_.IsValid())
1195 return ScanSparseFile(out_sparse_data_size);
1197 return sparse_file_.error_details() == File::FILE_ERROR_NOT_FOUND;
1200 bool SimpleSynchronousEntry::CreateSparseFile() {
1201 DCHECK(!sparse_file_open());
1203 FilePath filename = path_.AppendASCII(
1204 GetSparseFilenameFromEntryHash(entry_hash_));
1205 int flags = File::FLAG_CREATE | File::FLAG_READ | File::FLAG_WRITE |
1206 File::FLAG_SHARE_DELETE;
1207 sparse_file_.Initialize(filename, flags);
1208 if (!sparse_file_.IsValid())
1209 return false;
1211 return InitializeSparseFile();
1214 void SimpleSynchronousEntry::CloseSparseFile() {
1215 DCHECK(sparse_file_open());
1216 sparse_file_.Close();
1219 bool SimpleSynchronousEntry::TruncateSparseFile() {
1220 DCHECK(sparse_file_open());
1222 int64 header_and_key_length = sizeof(SimpleFileHeader) + key_.size();
1223 if (!sparse_file_.SetLength(header_and_key_length)) {
1224 DLOG(WARNING) << "Could not truncate sparse file";
1225 return false;
1228 sparse_ranges_.clear();
1230 return true;
1233 bool SimpleSynchronousEntry::InitializeSparseFile() {
1234 DCHECK(sparse_file_open());
1236 SimpleFileHeader header;
1237 header.initial_magic_number = kSimpleInitialMagicNumber;
1238 header.version = kSimpleVersion;
1239 header.key_length = key_.size();
1240 header.key_hash = base::Hash(key_);
1242 int header_write_result =
1243 sparse_file_.Write(0, reinterpret_cast<char*>(&header), sizeof(header));
1244 if (header_write_result != sizeof(header)) {
1245 DLOG(WARNING) << "Could not write sparse file header";
1246 return false;
1249 int key_write_result = sparse_file_.Write(sizeof(header), key_.data(),
1250 key_.size());
1251 if (key_write_result != implicit_cast<int>(key_.size())) {
1252 DLOG(WARNING) << "Could not write sparse file key";
1253 return false;
1256 sparse_ranges_.clear();
1257 sparse_tail_offset_ = sizeof(header) + key_.size();
1259 return true;
1262 bool SimpleSynchronousEntry::ScanSparseFile(int32* out_sparse_data_size) {
1263 DCHECK(sparse_file_open());
1265 int64 sparse_data_size = 0;
1267 SimpleFileHeader header;
1268 int header_read_result =
1269 sparse_file_.Read(0, reinterpret_cast<char*>(&header), sizeof(header));
1270 if (header_read_result != sizeof(header)) {
1271 DLOG(WARNING) << "Could not read header from sparse file.";
1272 return false;
1275 if (header.initial_magic_number != kSimpleInitialMagicNumber) {
1276 DLOG(WARNING) << "Sparse file magic number did not match.";
1277 return false;
1280 if (header.version != kSimpleVersion) {
1281 DLOG(WARNING) << "Sparse file unreadable version.";
1282 return false;
1285 sparse_ranges_.clear();
1287 int64 range_header_offset = sizeof(header) + key_.size();
1288 while (1) {
1289 SimpleFileSparseRangeHeader range_header;
1290 int range_header_read_result =
1291 sparse_file_.Read(range_header_offset,
1292 reinterpret_cast<char*>(&range_header),
1293 sizeof(range_header));
1294 if (range_header_read_result == 0)
1295 break;
1296 if (range_header_read_result != sizeof(range_header)) {
1297 DLOG(WARNING) << "Could not read sparse range header.";
1298 return false;
1301 if (range_header.sparse_range_magic_number !=
1302 kSimpleSparseRangeMagicNumber) {
1303 DLOG(WARNING) << "Invalid sparse range header magic number.";
1304 return false;
1307 SparseRange range;
1308 range.offset = range_header.offset;
1309 range.length = range_header.length;
1310 range.data_crc32 = range_header.data_crc32;
1311 range.file_offset = range_header_offset + sizeof(range_header);
1312 sparse_ranges_.insert(std::make_pair(range.offset, range));
1314 range_header_offset += sizeof(range_header) + range.length;
1316 DCHECK_GE(sparse_data_size + range.length, sparse_data_size);
1317 sparse_data_size += range.length;
1320 *out_sparse_data_size = static_cast<int32>(sparse_data_size);
1321 sparse_tail_offset_ = range_header_offset;
1323 return true;
1326 bool SimpleSynchronousEntry::ReadSparseRange(const SparseRange* range,
1327 int offset, int len, char* buf) {
1328 DCHECK(range);
1329 DCHECK(buf);
1330 DCHECK_LE(offset, range->length);
1331 DCHECK_LE(offset + len, range->length);
1333 int bytes_read = sparse_file_.Read(range->file_offset + offset, buf, len);
1334 if (bytes_read < len) {
1335 DLOG(WARNING) << "Could not read sparse range.";
1336 return false;
1339 // If we read the whole range and we have a crc32, check it.
1340 if (offset == 0 && len == range->length && range->data_crc32 != 0) {
1341 uint32 actual_crc32 = crc32(crc32(0L, Z_NULL, 0),
1342 reinterpret_cast<const Bytef*>(buf),
1343 len);
1344 if (actual_crc32 != range->data_crc32) {
1345 DLOG(WARNING) << "Sparse range crc32 mismatch.";
1346 return false;
1349 // TODO(ttuttle): Incremental crc32 calculation?
1351 return true;
1354 bool SimpleSynchronousEntry::WriteSparseRange(SparseRange* range,
1355 int offset, int len,
1356 const char* buf) {
1357 DCHECK(range);
1358 DCHECK(buf);
1359 DCHECK_LE(offset, range->length);
1360 DCHECK_LE(offset + len, range->length);
1362 uint32 new_crc32 = 0;
1363 if (offset == 0 && len == range->length) {
1364 new_crc32 = crc32(crc32(0L, Z_NULL, 0),
1365 reinterpret_cast<const Bytef*>(buf),
1366 len);
1369 if (new_crc32 != range->data_crc32) {
1370 range->data_crc32 = new_crc32;
1372 SimpleFileSparseRangeHeader header;
1373 header.sparse_range_magic_number = kSimpleSparseRangeMagicNumber;
1374 header.offset = range->offset;
1375 header.length = range->length;
1376 header.data_crc32 = range->data_crc32;
1378 int bytes_written = sparse_file_.Write(range->file_offset - sizeof(header),
1379 reinterpret_cast<char*>(&header),
1380 sizeof(header));
1381 if (bytes_written != implicit_cast<int>(sizeof(header))) {
1382 DLOG(WARNING) << "Could not rewrite sparse range header.";
1383 return false;
1387 int bytes_written = sparse_file_.Write(range->file_offset + offset, buf, len);
1388 if (bytes_written < len) {
1389 DLOG(WARNING) << "Could not write sparse range.";
1390 return false;
1393 return true;
1396 bool SimpleSynchronousEntry::AppendSparseRange(int64 offset,
1397 int len,
1398 const char* buf) {
1399 DCHECK_GE(offset, 0);
1400 DCHECK_GT(len, 0);
1401 DCHECK(buf);
1403 uint32 data_crc32 = crc32(crc32(0L, Z_NULL, 0),
1404 reinterpret_cast<const Bytef*>(buf),
1405 len);
1407 SimpleFileSparseRangeHeader header;
1408 header.sparse_range_magic_number = kSimpleSparseRangeMagicNumber;
1409 header.offset = offset;
1410 header.length = len;
1411 header.data_crc32 = data_crc32;
1413 int bytes_written = sparse_file_.Write(sparse_tail_offset_,
1414 reinterpret_cast<char*>(&header),
1415 sizeof(header));
1416 if (bytes_written != implicit_cast<int>(sizeof(header))) {
1417 DLOG(WARNING) << "Could not append sparse range header.";
1418 return false;
1420 sparse_tail_offset_ += bytes_written;
1422 bytes_written = sparse_file_.Write(sparse_tail_offset_, buf, len);
1423 if (bytes_written < len) {
1424 DLOG(WARNING) << "Could not append sparse range data.";
1425 return false;
1427 int64 data_file_offset = sparse_tail_offset_;
1428 sparse_tail_offset_ += bytes_written;
1430 SparseRange range;
1431 range.offset = offset;
1432 range.length = len;
1433 range.data_crc32 = data_crc32;
1434 range.file_offset = data_file_offset;
1435 sparse_ranges_.insert(std::make_pair(offset, range));
1437 return true;
1440 } // namespace disk_cache