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"
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"
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,
49 // Used in histograms, please only add entries at the end.
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
,
61 // Used in histograms, please only add entries at the end.
63 CHECK_EOF_RESULT_SUCCESS
,
64 CHECK_EOF_RESULT_READ_FAILURE
,
65 CHECK_EOF_RESULT_MAGIC_NUMBER_MISMATCH
,
66 CHECK_EOF_RESULT_CRC_MISMATCH
,
70 // Used in histograms, please only add entries at the end.
73 CLOSE_RESULT_WRITE_FAILURE
,
76 void RecordSyncOpenResult(net::CacheType cache_type
,
77 OpenEntryResult result
,
79 DCHECK_LT(result
, OPEN_ENTRY_MAX
);
80 SIMPLE_CACHE_UMA(ENUMERATION
,
81 "SyncOpenResult", cache_type
, result
, OPEN_ENTRY_MAX
);
83 SIMPLE_CACHE_UMA(ENUMERATION
,
84 "SyncOpenResult_WithIndex", cache_type
,
85 result
, OPEN_ENTRY_MAX
);
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);
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
,
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
)
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
)
164 return GetFileSizeFromKeyAndDataSize(key
, total_data_size
);
167 SimpleEntryCreationResults::SimpleEntryCreationResults(
168 SimpleEntryStat entry_stat
)
170 entry_stat(entry_stat
),
171 stream_0_crc32(crc32(0, Z_NULL
, 0)),
175 SimpleEntryCreationResults::~SimpleEntryCreationResults() {
178 SimpleSynchronousEntry::CRCRecord::CRCRecord() : index(-1),
183 SimpleSynchronousEntry::CRCRecord::CRCRecord(int index_p
,
187 has_crc32(has_crc32_p
),
188 data_crc32(data_crc32_p
) {}
190 SimpleSynchronousEntry::EntryOperationData::EntryOperationData(int index_p
,
195 buf_len(buf_len_p
) {}
197 SimpleSynchronousEntry::EntryOperationData::EntryOperationData(int index_p
,
205 truncate(truncate_p
),
208 SimpleSynchronousEntry::EntryOperationData::EntryOperationData(
209 int64 sparse_offset_p
,
211 : sparse_offset(sparse_offset_p
),
212 buf_len(buf_len_p
) {}
215 void SimpleSynchronousEntry::OpenEntry(
216 net::CacheType cache_type
,
217 const FilePath
& path
,
218 const uint64 entry_hash
,
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
) {
232 out_results
->sync_entry
= NULL
;
233 out_results
->stream_0_data
= NULL
;
236 UMA_HISTOGRAM_TIMES("SimpleCache.DiskOpenLatency", open_time
.Elapsed());
237 out_results
->sync_entry
= sync_entry
;
241 void SimpleSynchronousEntry::CreateEntry(
242 net::CacheType cache_type
,
243 const FilePath
& path
,
244 const std::string
& key
,
245 const uint64 entry_hash
,
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
)
257 out_results
->sync_entry
= NULL
;
260 out_results
->sync_entry
= sync_entry
;
264 int SimpleSynchronousEntry::DoomEntry(
265 const FilePath
& path
,
267 const bool deleted_well
= DeleteFilesForEntryHash(path
, entry_hash
);
268 return deleted_well
? net::OK
: net::ERR_FAILED
;
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
,
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
]);
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()),
304 if (bytes_read
>= 0) {
305 *out_result
= bytes_read
;
307 *out_result
= net::ERR_CACHE_READ_FAILURE
;
312 void SimpleSynchronousEntry::WriteData(const EntryOperationData
& in_entry_op
,
313 net::IOBuffer
* in_buf
,
314 SimpleEntryStat
* out_entry_stat
,
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.
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
;
339 if (!MaybeCreateFile(file_index
, FILE_REQUIRED
, &error
)) {
340 RecordWriteResult(cache_type_
, WRITE_RESULT_LAZY_CREATE_FAILURE
);
342 *out_result
= net::ERR_CACHE_WRITE_FAILURE
;
345 CreateEntryResult result
;
346 if (!InitializeCreatedFile(file_index
, &result
)) {
347 RecordWriteResult(cache_type_
, WRITE_RESULT_LAZY_INITIALIZE_FAILURE
);
349 *out_result
= net::ERR_CACHE_WRITE_FAILURE
;
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
);
362 *out_result
= net::ERR_CACHE_WRITE_FAILURE
;
367 if (files_
[file_index
].Write(file_offset
, in_buf
->data(), buf_len
) !=
369 RecordWriteResult(cache_type_
, WRITE_RESULT_WRITE_FAILURE
);
371 *out_result
= net::ERR_CACHE_WRITE_FAILURE
;
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
));
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
);
384 *out_result
= net::ERR_CACHE_WRITE_FAILURE
;
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
,
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();
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.
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
;
431 read_so_far
+= len_to_read
;
436 // Keep reading until the buffer is full or there is not another contiguous
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
;
449 read_so_far
+= len_to_read
;
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
,
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
;
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()) {
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
;
504 written_so_far
+= len_to_write
;
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
) {
515 static_cast<int>(found_range
->offset
- (offset
+ written_so_far
));
516 if (!AppendSparseRange(offset
+ written_so_far
,
518 buf
+ written_so_far
)) {
519 *out_result
= net::ERR_CACHE_WRITE_FAILURE
;
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
,
530 buf
+ written_so_far
)) {
531 *out_result
= net::ERR_CACHE_WRITE_FAILURE
;
534 written_so_far
+= len_to_write
;
538 if (written_so_far
< buf_len
) {
539 int len_to_append
= buf_len
- written_so_far
;
540 if (!AppendSparseRange(offset
+ written_so_far
,
542 buf
+ written_so_far
)) {
543 *out_result
= net::ERR_CACHE_WRITE_FAILURE
;
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
,
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()) {
579 if (it
->second
.offset
+ it
->second
.length
> offset
) {
581 avail_so_far
= (it
->second
.offset
+ it
->second
.length
) - offset
;
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
;
593 int64 len_from_start
= len
- (start
- offset
);
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_
);
607 GetEOFRecordData(index
, entry_stat
, &has_crc32
, &crc32
, &stream_size
);
608 if (*out_result
!= net::OK
) {
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
);
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.";
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
])
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;
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.";
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.";
672 for (int i
= 0; i
< kSimpleEntryFileCount
; ++i
) {
673 if (empty_file_omitted_
[i
])
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;
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
),
707 entry_hash_(entry_hash
),
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_
)
721 bool SimpleSynchronousEntry::MaybeOpenFile(
723 File::Error
* 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;
738 return files_
[file_index
].IsValid();
741 bool SimpleSynchronousEntry::MaybeCreateFile(
743 FileRequired file_required
,
744 File::Error
* out_error
) {
747 if (CanOmitEmptyFile(file_index
) && file_required
== FILE_NOT_REQUIRED
) {
748 empty_file_omitted_
[file_index
] = 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(
765 SimpleEntryStat
* out_entry_stat
) {
766 for (int i
= 0; i
< kSimpleEntryFileCount
; ++i
) {
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
);
777 SIMPLE_CACHE_UMA(ENUMERATION
,
778 "SyncOpenPlatformFileError_WithIndex", cache_type_
,
779 -error
, -base::File::FILE_ERROR_MAX
);
781 SIMPLE_CACHE_UMA(ENUMERATION
,
782 "SyncOpenPlatformFileError_WithoutIndex",
784 -error
, -base::File::FILE_ERROR_MAX
);
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);
801 File::Info file_info
;
802 bool success
= files_
[i
].GetInfo(&file_info
);
803 base::Time file_last_modified
;
805 DLOG(WARNING
) << "Could not get platform file info.";
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
);
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;
843 bool SimpleSynchronousEntry::CreateFiles(
845 SimpleEntryStat
* out_entry_stat
) {
846 for (int i
= 0; i
< kSimpleEntryFileCount
; ++i
) {
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
);
856 SIMPLE_CACHE_UMA(ENUMERATION
,
857 "SyncCreatePlatformFileError_WithIndex", cache_type_
,
858 -error
, -base::File::FILE_ERROR_MAX
);
860 SIMPLE_CACHE_UMA(ENUMERATION
,
861 "SyncCreatePlatformFileError_WithoutIndex",
863 -error
, -base::File::FILE_ERROR_MAX
);
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;
884 void SimpleSynchronousEntry::CloseFile(int index
) {
885 if (empty_file_omitted_
[index
]) {
886 empty_file_omitted_
[index
] = false;
888 DCHECK(files_
[index
].IsValid());
889 files_
[index
].Close();
892 if (sparse_file_open())
896 void SimpleSynchronousEntry::CloseFiles() {
897 for (int i
= 0; i
< kSimpleEntryFileCount
; ++i
)
901 int SimpleSynchronousEntry::InitializeForOpen(
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
])
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(),
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
);
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
;
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_
,
997 RecordSyncOpenResult(cache_type_
, OPEN_ENTRY_SUCCESS
, had_index
);
1002 bool SimpleSynchronousEntry::InitializeCreatedFile(
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
;
1019 bytes_written
= files_
[file_index
].Write(sizeof(header
), key_
.data(),
1021 if (bytes_written
!= implicit_cast
<int>(key_
.size())) {
1022 *out_result
= CREATE_ENTRY_CANT_WRITE_KEY
;
1029 int SimpleSynchronousEntry::InitializeForCreate(
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
])
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;
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
));
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]);
1084 file
->Read(file_offset
, (*stream_0_data
)->data(), stream_0_size
);
1085 if (bytes_read
!= stream_0_size
)
1086 return net::ERR_FAILED
;
1089 uint32 expected_crc32
=
1091 ? crc32(0, Z_NULL
, 0)
1092 : crc32(crc32(0, Z_NULL
, 0),
1093 reinterpret_cast<const Bytef
*>((*stream_0_data
)->data()),
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
);
1105 int SimpleSynchronousEntry::GetEOFRecordData(int index
,
1106 const SimpleEntryStat
& entry_stat
,
1107 bool* out_has_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
);
1135 void SimpleSynchronousEntry::Doom() const {
1136 DeleteFilesForEntryHash(path_
, entry_hash_
);
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
);
1150 bool SimpleSynchronousEntry::DeleteFilesForEntryHash(
1151 const FilePath
& path
,
1152 const uint64 entry_hash
) {
1154 for (int i
= 0; i
< kSimpleEntryFileCount
; ++i
) {
1155 if (!DeleteFileForEntryHash(path
, entry_hash
, i
) && !CanOmitEmptyFile(i
))
1158 FilePath to_delete
= path
.AppendASCII(
1159 GetSparseFilenameFromEntryHash(entry_hash
));
1160 simple_util::SimpleCacheDeleteFile(to_delete
);
1164 void SimpleSynchronousEntry::RecordSyncCreateResult(CreateEntryResult result
,
1166 DCHECK_LT(result
, CREATE_ENTRY_MAX
);
1167 SIMPLE_CACHE_UMA(ENUMERATION
,
1168 "SyncCreateResult", cache_type_
, result
, CREATE_ENTRY_MAX
);
1170 SIMPLE_CACHE_UMA(ENUMERATION
,
1171 "SyncCreateResult_WithIndex", cache_type_
,
1172 result
, CREATE_ENTRY_MAX
);
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())
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";
1228 sparse_ranges_
.clear();
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";
1249 int key_write_result
= sparse_file_
.Write(sizeof(header
), key_
.data(),
1251 if (key_write_result
!= implicit_cast
<int>(key_
.size())) {
1252 DLOG(WARNING
) << "Could not write sparse file key";
1256 sparse_ranges_
.clear();
1257 sparse_tail_offset_
= sizeof(header
) + key_
.size();
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.";
1275 if (header
.initial_magic_number
!= kSimpleInitialMagicNumber
) {
1276 DLOG(WARNING
) << "Sparse file magic number did not match.";
1280 if (header
.version
!= kSimpleVersion
) {
1281 DLOG(WARNING
) << "Sparse file unreadable version.";
1285 sparse_ranges_
.clear();
1287 int64 range_header_offset
= sizeof(header
) + key_
.size();
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)
1296 if (range_header_read_result
!= sizeof(range_header
)) {
1297 DLOG(WARNING
) << "Could not read sparse range header.";
1301 if (range_header
.sparse_range_magic_number
!=
1302 kSimpleSparseRangeMagicNumber
) {
1303 DLOG(WARNING
) << "Invalid sparse range header magic number.";
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
;
1326 bool SimpleSynchronousEntry::ReadSparseRange(const SparseRange
* range
,
1327 int offset
, int len
, char* 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.";
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
),
1344 if (actual_crc32
!= range
->data_crc32
) {
1345 DLOG(WARNING
) << "Sparse range crc32 mismatch.";
1349 // TODO(ttuttle): Incremental crc32 calculation?
1354 bool SimpleSynchronousEntry::WriteSparseRange(SparseRange
* range
,
1355 int offset
, int len
,
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
),
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
),
1381 if (bytes_written
!= implicit_cast
<int>(sizeof(header
))) {
1382 DLOG(WARNING
) << "Could not rewrite sparse range header.";
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.";
1396 bool SimpleSynchronousEntry::AppendSparseRange(int64 offset
,
1399 DCHECK_GE(offset
, 0);
1403 uint32 data_crc32
= crc32(crc32(0L, Z_NULL
, 0),
1404 reinterpret_cast<const Bytef
*>(buf
),
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
),
1416 if (bytes_written
!= implicit_cast
<int>(sizeof(header
))) {
1417 DLOG(WARNING
) << "Could not append sparse range header.";
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.";
1427 int64 data_file_offset
= sparse_tail_offset_
;
1428 sparse_tail_offset_
+= bytes_written
;
1431 range
.offset
= offset
;
1433 range
.data_crc32
= data_crc32
;
1434 range
.file_offset
= data_file_offset
;
1435 sparse_ranges_
.insert(std::make_pair(offset
, range
));
1440 } // namespace disk_cache