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/numerics/safe_conversions.h"
18 #include "base/sha1.h"
19 #include "base/strings/stringprintf.h"
20 #include "net/base/io_buffer.h"
21 #include "net/base/net_errors.h"
22 #include "net/disk_cache/simple/simple_backend_version.h"
23 #include "net/disk_cache/simple/simple_histogram_macros.h"
24 #include "net/disk_cache/simple/simple_util.h"
25 #include "third_party/zlib/zlib.h"
33 // Used in histograms, please only add entries at the end.
34 enum OpenEntryResult
{
35 OPEN_ENTRY_SUCCESS
= 0,
36 OPEN_ENTRY_PLATFORM_FILE_ERROR
= 1,
37 OPEN_ENTRY_CANT_READ_HEADER
= 2,
38 OPEN_ENTRY_BAD_MAGIC_NUMBER
= 3,
39 OPEN_ENTRY_BAD_VERSION
= 4,
40 OPEN_ENTRY_CANT_READ_KEY
= 5,
41 // OPEN_ENTRY_KEY_MISMATCH = 6, Deprecated.
42 OPEN_ENTRY_KEY_HASH_MISMATCH
= 7,
43 OPEN_ENTRY_SPARSE_OPEN_FAILED
= 8,
47 // Used in histograms, please only add entries at the end.
49 WRITE_RESULT_SUCCESS
= 0,
50 WRITE_RESULT_PRETRUNCATE_FAILURE
,
51 WRITE_RESULT_WRITE_FAILURE
,
52 WRITE_RESULT_TRUNCATE_FAILURE
,
53 WRITE_RESULT_LAZY_STREAM_ENTRY_DOOMED
,
54 WRITE_RESULT_LAZY_CREATE_FAILURE
,
55 WRITE_RESULT_LAZY_INITIALIZE_FAILURE
,
59 // Used in histograms, please only add entries at the end.
61 CHECK_EOF_RESULT_SUCCESS
,
62 CHECK_EOF_RESULT_READ_FAILURE
,
63 CHECK_EOF_RESULT_MAGIC_NUMBER_MISMATCH
,
64 CHECK_EOF_RESULT_CRC_MISMATCH
,
68 // Used in histograms, please only add entries at the end.
71 CLOSE_RESULT_WRITE_FAILURE
,
74 void RecordSyncOpenResult(net::CacheType cache_type
,
75 OpenEntryResult result
,
77 DCHECK_LT(result
, OPEN_ENTRY_MAX
);
78 SIMPLE_CACHE_UMA(ENUMERATION
,
79 "SyncOpenResult", cache_type
, result
, OPEN_ENTRY_MAX
);
81 SIMPLE_CACHE_UMA(ENUMERATION
,
82 "SyncOpenResult_WithIndex", cache_type
,
83 result
, OPEN_ENTRY_MAX
);
85 SIMPLE_CACHE_UMA(ENUMERATION
,
86 "SyncOpenResult_WithoutIndex", cache_type
,
87 result
, OPEN_ENTRY_MAX
);
91 void RecordWriteResult(net::CacheType cache_type
, WriteResult result
) {
92 SIMPLE_CACHE_UMA(ENUMERATION
,
93 "SyncWriteResult", cache_type
, result
, WRITE_RESULT_MAX
);
96 void RecordCheckEOFResult(net::CacheType cache_type
, CheckEOFResult result
) {
97 SIMPLE_CACHE_UMA(ENUMERATION
,
98 "SyncCheckEOFResult", cache_type
,
99 result
, CHECK_EOF_RESULT_MAX
);
102 void RecordCloseResult(net::CacheType cache_type
, CloseResult result
) {
103 SIMPLE_CACHE_UMA(ENUMERATION
,
104 "SyncCloseResult", cache_type
, result
, WRITE_RESULT_MAX
);
107 bool CanOmitEmptyFile(int file_index
) {
108 DCHECK_GE(file_index
, 0);
109 DCHECK_LT(file_index
, disk_cache::kSimpleEntryFileCount
);
110 return file_index
== disk_cache::simple_util::GetFileIndexFromStreamIndex(2);
115 namespace disk_cache
{
117 using simple_util::GetEntryHashKey
;
118 using simple_util::GetFilenameFromEntryHashAndFileIndex
;
119 using simple_util::GetSparseFilenameFromEntryHash
;
120 using simple_util::GetDataSizeFromKeyAndFileSize
;
121 using simple_util::GetFileSizeFromKeyAndDataSize
;
122 using simple_util::GetFileIndexFromStreamIndex
;
124 SimpleEntryStat::SimpleEntryStat(base::Time last_used
,
125 base::Time last_modified
,
126 const int32 data_size
[],
127 const int32 sparse_data_size
)
128 : last_used_(last_used
),
129 last_modified_(last_modified
),
130 sparse_data_size_(sparse_data_size
) {
131 memcpy(data_size_
, data_size
, sizeof(data_size_
));
134 int SimpleEntryStat::GetOffsetInFile(const std::string
& key
,
136 int stream_index
) const {
137 const size_t headers_size
= sizeof(SimpleFileHeader
) + key
.size();
138 const size_t additional_offset
=
139 stream_index
== 0 ? data_size_
[1] + sizeof(SimpleFileEOF
) : 0;
140 return headers_size
+ offset
+ additional_offset
;
143 int SimpleEntryStat::GetEOFOffsetInFile(const std::string
& key
,
144 int stream_index
) const {
145 return GetOffsetInFile(key
, data_size_
[stream_index
], stream_index
);
148 int SimpleEntryStat::GetLastEOFOffsetInFile(const std::string
& key
,
149 int stream_index
) const {
150 const int file_index
= GetFileIndexFromStreamIndex(stream_index
);
151 const int eof_data_offset
=
152 file_index
== 0 ? data_size_
[0] + data_size_
[1] + sizeof(SimpleFileEOF
)
154 return GetOffsetInFile(key
, eof_data_offset
, stream_index
);
157 int64
SimpleEntryStat::GetFileSize(const std::string
& key
,
158 int file_index
) const {
159 const int32 total_data_size
=
160 file_index
== 0 ? data_size_
[0] + data_size_
[1] + sizeof(SimpleFileEOF
)
162 return GetFileSizeFromKeyAndDataSize(key
, total_data_size
);
165 SimpleEntryCreationResults::SimpleEntryCreationResults(
166 SimpleEntryStat entry_stat
)
168 entry_stat(entry_stat
),
169 stream_0_crc32(crc32(0, Z_NULL
, 0)),
173 SimpleEntryCreationResults::~SimpleEntryCreationResults() {
176 SimpleSynchronousEntry::CRCRecord::CRCRecord() : index(-1),
181 SimpleSynchronousEntry::CRCRecord::CRCRecord(int index_p
,
185 has_crc32(has_crc32_p
),
186 data_crc32(data_crc32_p
) {}
188 SimpleSynchronousEntry::EntryOperationData::EntryOperationData(int index_p
,
193 buf_len(buf_len_p
) {}
195 SimpleSynchronousEntry::EntryOperationData::EntryOperationData(int index_p
,
203 truncate(truncate_p
),
206 SimpleSynchronousEntry::EntryOperationData::EntryOperationData(
207 int64 sparse_offset_p
,
209 : sparse_offset(sparse_offset_p
),
210 buf_len(buf_len_p
) {}
213 void SimpleSynchronousEntry::OpenEntry(
214 net::CacheType cache_type
,
215 const FilePath
& path
,
216 const uint64 entry_hash
,
218 SimpleEntryCreationResults
*out_results
) {
219 SimpleSynchronousEntry
* sync_entry
=
220 new SimpleSynchronousEntry(cache_type
, path
, "", entry_hash
);
221 out_results
->result
=
222 sync_entry
->InitializeForOpen(had_index
,
223 &out_results
->entry_stat
,
224 &out_results
->stream_0_data
,
225 &out_results
->stream_0_crc32
);
226 if (out_results
->result
!= net::OK
) {
229 out_results
->sync_entry
= NULL
;
230 out_results
->stream_0_data
= NULL
;
233 out_results
->sync_entry
= sync_entry
;
237 void SimpleSynchronousEntry::CreateEntry(
238 net::CacheType cache_type
,
239 const FilePath
& path
,
240 const std::string
& key
,
241 const uint64 entry_hash
,
243 SimpleEntryCreationResults
*out_results
) {
244 DCHECK_EQ(entry_hash
, GetEntryHashKey(key
));
245 SimpleSynchronousEntry
* sync_entry
=
246 new SimpleSynchronousEntry(cache_type
, path
, key
, entry_hash
);
247 out_results
->result
= sync_entry
->InitializeForCreate(
248 had_index
, &out_results
->entry_stat
);
249 if (out_results
->result
!= net::OK
) {
250 if (out_results
->result
!= net::ERR_FILE_EXISTS
)
253 out_results
->sync_entry
= NULL
;
256 out_results
->sync_entry
= sync_entry
;
260 int SimpleSynchronousEntry::DoomEntry(
261 const FilePath
& path
,
263 const bool deleted_well
= DeleteFilesForEntryHash(path
, entry_hash
);
264 return deleted_well
? net::OK
: net::ERR_FAILED
;
268 int SimpleSynchronousEntry::DoomEntrySet(
269 const std::vector
<uint64
>* key_hashes
,
270 const FilePath
& path
) {
271 const size_t did_delete_count
= std::count_if(
272 key_hashes
->begin(), key_hashes
->end(), std::bind1st(
273 std::ptr_fun(SimpleSynchronousEntry::DeleteFilesForEntryHash
), path
));
274 return (did_delete_count
== key_hashes
->size()) ? net::OK
: net::ERR_FAILED
;
277 void SimpleSynchronousEntry::ReadData(const EntryOperationData
& in_entry_op
,
278 net::IOBuffer
* out_buf
,
280 SimpleEntryStat
* entry_stat
,
281 int* out_result
) const {
282 DCHECK(initialized_
);
283 DCHECK_NE(0, in_entry_op
.index
);
284 const int64 file_offset
=
285 entry_stat
->GetOffsetInFile(key_
, in_entry_op
.offset
, in_entry_op
.index
);
286 int file_index
= GetFileIndexFromStreamIndex(in_entry_op
.index
);
287 // Zero-length reads and reads to the empty streams of omitted files should
288 // be handled in the SimpleEntryImpl.
289 DCHECK_GT(in_entry_op
.buf_len
, 0);
290 DCHECK(!empty_file_omitted_
[file_index
]);
291 File
* file
= const_cast<File
*>(&files_
[file_index
]);
293 file
->Read(file_offset
, out_buf
->data(), in_entry_op
.buf_len
);
294 if (bytes_read
> 0) {
295 entry_stat
->set_last_used(Time::Now());
296 *out_crc32
= crc32(crc32(0L, Z_NULL
, 0),
297 reinterpret_cast<const Bytef
*>(out_buf
->data()),
300 if (bytes_read
>= 0) {
301 *out_result
= bytes_read
;
303 *out_result
= net::ERR_CACHE_READ_FAILURE
;
308 void SimpleSynchronousEntry::WriteData(const EntryOperationData
& in_entry_op
,
309 net::IOBuffer
* in_buf
,
310 SimpleEntryStat
* out_entry_stat
,
312 DCHECK(initialized_
);
313 DCHECK_NE(0, in_entry_op
.index
);
314 int index
= in_entry_op
.index
;
315 int file_index
= GetFileIndexFromStreamIndex(index
);
316 int offset
= in_entry_op
.offset
;
317 int buf_len
= in_entry_op
.buf_len
;
318 bool truncate
= in_entry_op
.truncate
;
319 bool doomed
= in_entry_op
.doomed
;
320 const int64 file_offset
= out_entry_stat
->GetOffsetInFile(
321 key_
, in_entry_op
.offset
, in_entry_op
.index
);
322 bool extending_by_write
= offset
+ buf_len
> out_entry_stat
->data_size(index
);
324 if (empty_file_omitted_
[file_index
]) {
325 // Don't create a new file if the entry has been doomed, to avoid it being
326 // mixed up with a newly-created entry with the same key.
328 DLOG(WARNING
) << "Rejecting write to lazily omitted stream "
329 << in_entry_op
.index
<< " of doomed cache entry.";
330 RecordWriteResult(cache_type_
, WRITE_RESULT_LAZY_STREAM_ENTRY_DOOMED
);
331 *out_result
= net::ERR_CACHE_WRITE_FAILURE
;
335 if (!MaybeCreateFile(file_index
, FILE_REQUIRED
, &error
)) {
336 RecordWriteResult(cache_type_
, WRITE_RESULT_LAZY_CREATE_FAILURE
);
338 *out_result
= net::ERR_CACHE_WRITE_FAILURE
;
341 CreateEntryResult result
;
342 if (!InitializeCreatedFile(file_index
, &result
)) {
343 RecordWriteResult(cache_type_
, WRITE_RESULT_LAZY_INITIALIZE_FAILURE
);
345 *out_result
= net::ERR_CACHE_WRITE_FAILURE
;
349 DCHECK(!empty_file_omitted_
[file_index
]);
351 if (extending_by_write
) {
352 // The EOF record and the eventual stream afterward need to be zeroed out.
353 const int64 file_eof_offset
=
354 out_entry_stat
->GetEOFOffsetInFile(key_
, index
);
355 if (!files_
[file_index
].SetLength(file_eof_offset
)) {
356 RecordWriteResult(cache_type_
, WRITE_RESULT_PRETRUNCATE_FAILURE
);
358 *out_result
= net::ERR_CACHE_WRITE_FAILURE
;
363 if (files_
[file_index
].Write(file_offset
, in_buf
->data(), buf_len
) !=
365 RecordWriteResult(cache_type_
, WRITE_RESULT_WRITE_FAILURE
);
367 *out_result
= net::ERR_CACHE_WRITE_FAILURE
;
371 if (!truncate
&& (buf_len
> 0 || !extending_by_write
)) {
372 out_entry_stat
->set_data_size(
373 index
, std::max(out_entry_stat
->data_size(index
), offset
+ buf_len
));
375 out_entry_stat
->set_data_size(index
, offset
+ buf_len
);
376 int file_eof_offset
= out_entry_stat
->GetLastEOFOffsetInFile(key_
, index
);
377 if (!files_
[file_index
].SetLength(file_eof_offset
)) {
378 RecordWriteResult(cache_type_
, WRITE_RESULT_TRUNCATE_FAILURE
);
380 *out_result
= net::ERR_CACHE_WRITE_FAILURE
;
385 RecordWriteResult(cache_type_
, WRITE_RESULT_SUCCESS
);
386 base::Time modification_time
= Time::Now();
387 out_entry_stat
->set_last_used(modification_time
);
388 out_entry_stat
->set_last_modified(modification_time
);
389 *out_result
= buf_len
;
392 void SimpleSynchronousEntry::ReadSparseData(
393 const EntryOperationData
& in_entry_op
,
394 net::IOBuffer
* out_buf
,
395 base::Time
* out_last_used
,
397 DCHECK(initialized_
);
398 int64 offset
= in_entry_op
.sparse_offset
;
399 int buf_len
= in_entry_op
.buf_len
;
401 char* buf
= out_buf
->data();
404 // Find the first sparse range at or after the requested offset.
405 SparseRangeIterator it
= sparse_ranges_
.lower_bound(offset
);
407 if (it
!= sparse_ranges_
.begin()) {
408 // Hop back one range and read the one overlapping with the start.
410 SparseRange
* found_range
= &it
->second
;
411 DCHECK_EQ(it
->first
, found_range
->offset
);
412 if (found_range
->offset
+ found_range
->length
> offset
) {
413 DCHECK_GE(found_range
->length
, 0);
414 DCHECK_LE(found_range
->length
, kint32max
);
415 DCHECK_GE(offset
- found_range
->offset
, 0);
416 DCHECK_LE(offset
- found_range
->offset
, kint32max
);
417 int net_offset
= static_cast<int>(offset
- found_range
->offset
);
418 int range_len_after_offset
=
419 static_cast<int>(found_range
->length
- net_offset
);
420 DCHECK_GE(range_len_after_offset
, 0);
422 int len_to_read
= std::min(buf_len
, range_len_after_offset
);
423 if (!ReadSparseRange(found_range
, net_offset
, len_to_read
, buf
)) {
424 *out_result
= net::ERR_CACHE_READ_FAILURE
;
427 read_so_far
+= len_to_read
;
432 // Keep reading until the buffer is full or there is not another contiguous
434 while (read_so_far
< buf_len
&&
435 it
!= sparse_ranges_
.end() &&
436 it
->second
.offset
== offset
+ read_so_far
) {
437 SparseRange
* found_range
= &it
->second
;
438 DCHECK_EQ(it
->first
, found_range
->offset
);
439 int range_len
= base::saturated_cast
<int>(found_range
->length
);
440 int len_to_read
= std::min(buf_len
- read_so_far
, range_len
);
441 if (!ReadSparseRange(found_range
, 0, len_to_read
, buf
+ read_so_far
)) {
442 *out_result
= net::ERR_CACHE_READ_FAILURE
;
445 read_so_far
+= len_to_read
;
449 *out_result
= read_so_far
;
452 void SimpleSynchronousEntry::WriteSparseData(
453 const EntryOperationData
& in_entry_op
,
454 net::IOBuffer
* in_buf
,
455 uint64 max_sparse_data_size
,
456 SimpleEntryStat
* out_entry_stat
,
458 DCHECK(initialized_
);
459 int64 offset
= in_entry_op
.sparse_offset
;
460 int buf_len
= in_entry_op
.buf_len
;
462 const char* buf
= in_buf
->data();
463 int written_so_far
= 0;
464 int appended_so_far
= 0;
466 if (!sparse_file_open() && !CreateSparseFile()) {
467 *out_result
= net::ERR_CACHE_WRITE_FAILURE
;
471 uint64 sparse_data_size
= out_entry_stat
->sparse_data_size();
472 // This is a pessimistic estimate; it assumes the entire buffer is going to
473 // be appended as a new range, not written over existing ranges.
474 if (sparse_data_size
+ buf_len
> max_sparse_data_size
) {
475 DVLOG(1) << "Truncating sparse data file (" << sparse_data_size
<< " + "
476 << buf_len
<< " > " << max_sparse_data_size
<< ")";
477 TruncateSparseFile();
480 SparseRangeIterator it
= sparse_ranges_
.lower_bound(offset
);
482 if (it
!= sparse_ranges_
.begin()) {
484 SparseRange
* found_range
= &it
->second
;
485 if (found_range
->offset
+ found_range
->length
> offset
) {
486 DCHECK_GE(found_range
->length
, 0);
487 DCHECK_LE(found_range
->length
, kint32max
);
488 DCHECK_GE(offset
- found_range
->offset
, 0);
489 DCHECK_LE(offset
- found_range
->offset
, kint32max
);
490 int net_offset
= static_cast<int>(offset
- found_range
->offset
);
491 int range_len_after_offset
=
492 static_cast<int>(found_range
->length
- net_offset
);
493 DCHECK_GE(range_len_after_offset
, 0);
495 int len_to_write
= std::min(buf_len
, range_len_after_offset
);
496 if (!WriteSparseRange(found_range
, net_offset
, len_to_write
, buf
)) {
497 *out_result
= net::ERR_CACHE_WRITE_FAILURE
;
500 written_so_far
+= len_to_write
;
505 while (written_so_far
< buf_len
&&
506 it
!= sparse_ranges_
.end() &&
507 it
->second
.offset
< offset
+ buf_len
) {
508 SparseRange
* found_range
= &it
->second
;
509 if (offset
+ written_so_far
< found_range
->offset
) {
511 static_cast<int>(found_range
->offset
- (offset
+ written_so_far
));
512 if (!AppendSparseRange(offset
+ written_so_far
,
514 buf
+ written_so_far
)) {
515 *out_result
= net::ERR_CACHE_WRITE_FAILURE
;
518 written_so_far
+= len_to_append
;
519 appended_so_far
+= len_to_append
;
521 int range_len
= base::saturated_cast
<int>(found_range
->length
);
522 int len_to_write
= std::min(buf_len
- written_so_far
, range_len
);
523 if (!WriteSparseRange(found_range
,
526 buf
+ written_so_far
)) {
527 *out_result
= net::ERR_CACHE_WRITE_FAILURE
;
530 written_so_far
+= len_to_write
;
534 if (written_so_far
< buf_len
) {
535 int len_to_append
= buf_len
- written_so_far
;
536 if (!AppendSparseRange(offset
+ written_so_far
,
538 buf
+ written_so_far
)) {
539 *out_result
= net::ERR_CACHE_WRITE_FAILURE
;
542 written_so_far
+= len_to_append
;
543 appended_so_far
+= len_to_append
;
546 DCHECK_EQ(buf_len
, written_so_far
);
548 base::Time modification_time
= Time::Now();
549 out_entry_stat
->set_last_used(modification_time
);
550 out_entry_stat
->set_last_modified(modification_time
);
551 int32 old_sparse_data_size
= out_entry_stat
->sparse_data_size();
552 out_entry_stat
->set_sparse_data_size(old_sparse_data_size
+ appended_so_far
);
553 *out_result
= written_so_far
;
556 void SimpleSynchronousEntry::GetAvailableRange(
557 const EntryOperationData
& in_entry_op
,
560 DCHECK(initialized_
);
561 int64 offset
= in_entry_op
.sparse_offset
;
562 int len
= in_entry_op
.buf_len
;
564 SparseRangeIterator it
= sparse_ranges_
.lower_bound(offset
);
566 int64 start
= offset
;
567 int64 avail_so_far
= 0;
569 if (it
!= sparse_ranges_
.end() && it
->second
.offset
< offset
+ len
)
570 start
= it
->second
.offset
;
572 if ((it
== sparse_ranges_
.end() || it
->second
.offset
> offset
) &&
573 it
!= sparse_ranges_
.begin()) {
575 if (it
->second
.offset
+ it
->second
.length
> offset
) {
577 avail_so_far
= (it
->second
.offset
+ it
->second
.length
) - offset
;
582 while (start
+ avail_so_far
< offset
+ len
&&
583 it
!= sparse_ranges_
.end() &&
584 it
->second
.offset
== start
+ avail_so_far
) {
585 avail_so_far
+= it
->second
.length
;
589 int64 len_from_start
= len
- (start
- offset
);
591 *out_result
= static_cast<int>(std::min(avail_so_far
, len_from_start
));
594 void SimpleSynchronousEntry::CheckEOFRecord(int index
,
595 const SimpleEntryStat
& entry_stat
,
596 uint32 expected_crc32
,
597 int* out_result
) const {
598 DCHECK(initialized_
);
603 GetEOFRecordData(index
, entry_stat
, &has_crc32
, &crc32
, &stream_size
);
604 if (*out_result
!= net::OK
) {
608 if (has_crc32
&& crc32
!= expected_crc32
) {
609 DVLOG(1) << "EOF record had bad crc.";
610 *out_result
= net::ERR_CACHE_CHECKSUM_MISMATCH
;
611 RecordCheckEOFResult(cache_type_
, CHECK_EOF_RESULT_CRC_MISMATCH
);
615 RecordCheckEOFResult(cache_type_
, CHECK_EOF_RESULT_SUCCESS
);
618 void SimpleSynchronousEntry::Close(
619 const SimpleEntryStat
& entry_stat
,
620 scoped_ptr
<std::vector
<CRCRecord
> > crc32s_to_write
,
621 net::GrowableIOBuffer
* stream_0_data
) {
622 DCHECK(stream_0_data
);
623 // Write stream 0 data.
624 int stream_0_offset
= entry_stat
.GetOffsetInFile(key_
, 0, 0);
625 if (files_
[0].Write(stream_0_offset
, stream_0_data
->data(),
626 entry_stat
.data_size(0)) !=
627 entry_stat
.data_size(0)) {
628 RecordCloseResult(cache_type_
, CLOSE_RESULT_WRITE_FAILURE
);
629 DVLOG(1) << "Could not write stream 0 data.";
633 for (std::vector
<CRCRecord
>::const_iterator it
= crc32s_to_write
->begin();
634 it
!= crc32s_to_write
->end(); ++it
) {
635 const int stream_index
= it
->index
;
636 const int file_index
= GetFileIndexFromStreamIndex(stream_index
);
637 if (empty_file_omitted_
[file_index
])
640 SimpleFileEOF eof_record
;
641 eof_record
.stream_size
= entry_stat
.data_size(stream_index
);
642 eof_record
.final_magic_number
= kSimpleFinalMagicNumber
;
643 eof_record
.flags
= 0;
645 eof_record
.flags
|= SimpleFileEOF::FLAG_HAS_CRC32
;
646 eof_record
.data_crc32
= it
->data_crc32
;
647 int eof_offset
= entry_stat
.GetEOFOffsetInFile(key_
, stream_index
);
648 // If stream 0 changed size, the file needs to be resized, otherwise the
649 // next open will yield wrong stream sizes. On stream 1 and stream 2 proper
650 // resizing of the file is handled in SimpleSynchronousEntry::WriteData().
651 if (stream_index
== 0 &&
652 !files_
[file_index
].SetLength(eof_offset
)) {
653 RecordCloseResult(cache_type_
, CLOSE_RESULT_WRITE_FAILURE
);
654 DVLOG(1) << "Could not truncate stream 0 file.";
658 if (files_
[file_index
].Write(eof_offset
,
659 reinterpret_cast<const char*>(&eof_record
),
660 sizeof(eof_record
)) !=
661 sizeof(eof_record
)) {
662 RecordCloseResult(cache_type_
, CLOSE_RESULT_WRITE_FAILURE
);
663 DVLOG(1) << "Could not write eof record.";
668 for (int i
= 0; i
< kSimpleEntryFileCount
; ++i
) {
669 if (empty_file_omitted_
[i
])
673 const int64 file_size
= entry_stat
.GetFileSize(key_
, i
);
674 SIMPLE_CACHE_UMA(CUSTOM_COUNTS
,
675 "LastClusterSize", cache_type_
,
676 file_size
% 4096, 0, 4097, 50);
677 const int64 cluster_loss
= file_size
% 4096 ? 4096 - file_size
% 4096 : 0;
678 SIMPLE_CACHE_UMA(PERCENTAGE
,
679 "LastClusterLossPercent", cache_type_
,
680 static_cast<base::HistogramBase::Sample
>(
681 cluster_loss
* 100 / (cluster_loss
+ file_size
)));
684 if (sparse_file_open())
685 sparse_file_
.Close();
687 if (files_created_
) {
688 const int stream2_file_index
= GetFileIndexFromStreamIndex(2);
689 SIMPLE_CACHE_UMA(BOOLEAN
, "EntryCreatedAndStream2Omitted", cache_type_
,
690 empty_file_omitted_
[stream2_file_index
]);
692 RecordCloseResult(cache_type_
, CLOSE_RESULT_SUCCESS
);
693 have_open_files_
= false;
697 SimpleSynchronousEntry::SimpleSynchronousEntry(net::CacheType cache_type
,
698 const FilePath
& path
,
699 const std::string
& key
,
700 const uint64 entry_hash
)
701 : cache_type_(cache_type
),
703 entry_hash_(entry_hash
),
705 have_open_files_(false),
706 initialized_(false) {
707 for (int i
= 0; i
< kSimpleEntryFileCount
; ++i
)
708 empty_file_omitted_
[i
] = false;
711 SimpleSynchronousEntry::~SimpleSynchronousEntry() {
712 DCHECK(!(have_open_files_
&& initialized_
));
713 if (have_open_files_
)
717 bool SimpleSynchronousEntry::MaybeOpenFile(
719 File::Error
* out_error
) {
722 FilePath filename
= GetFilenameFromFileIndex(file_index
);
723 int flags
= File::FLAG_OPEN
| File::FLAG_READ
| File::FLAG_WRITE
|
724 File::FLAG_SHARE_DELETE
;
725 files_
[file_index
].Initialize(filename
, flags
);
726 *out_error
= files_
[file_index
].error_details();
728 if (CanOmitEmptyFile(file_index
) && !files_
[file_index
].IsValid() &&
729 *out_error
== File::FILE_ERROR_NOT_FOUND
) {
730 empty_file_omitted_
[file_index
] = true;
734 return files_
[file_index
].IsValid();
737 bool SimpleSynchronousEntry::MaybeCreateFile(
739 FileRequired file_required
,
740 File::Error
* out_error
) {
743 if (CanOmitEmptyFile(file_index
) && file_required
== FILE_NOT_REQUIRED
) {
744 empty_file_omitted_
[file_index
] = true;
748 FilePath filename
= GetFilenameFromFileIndex(file_index
);
749 int flags
= File::FLAG_CREATE
| File::FLAG_READ
| File::FLAG_WRITE
|
750 File::FLAG_SHARE_DELETE
;
751 files_
[file_index
].Initialize(filename
, flags
);
752 *out_error
= files_
[file_index
].error_details();
754 empty_file_omitted_
[file_index
] = false;
756 return files_
[file_index
].IsValid();
759 bool SimpleSynchronousEntry::OpenFiles(
761 SimpleEntryStat
* out_entry_stat
) {
762 for (int i
= 0; i
< kSimpleEntryFileCount
; ++i
) {
764 if (!MaybeOpenFile(i
, &error
)) {
765 // TODO(ttuttle,gavinp): Remove one each of these triplets of histograms.
766 // We can calculate the third as the sum or difference of the other two.
767 RecordSyncOpenResult(
768 cache_type_
, OPEN_ENTRY_PLATFORM_FILE_ERROR
, had_index
);
769 SIMPLE_CACHE_UMA(ENUMERATION
,
770 "SyncOpenPlatformFileError", cache_type_
,
771 -error
, -base::File::FILE_ERROR_MAX
);
773 SIMPLE_CACHE_UMA(ENUMERATION
,
774 "SyncOpenPlatformFileError_WithIndex", cache_type_
,
775 -error
, -base::File::FILE_ERROR_MAX
);
777 SIMPLE_CACHE_UMA(ENUMERATION
,
778 "SyncOpenPlatformFileError_WithoutIndex",
780 -error
, -base::File::FILE_ERROR_MAX
);
788 have_open_files_
= true;
790 base::TimeDelta entry_age
= base::Time::Now() - base::Time::UnixEpoch();
791 for (int i
= 0; i
< kSimpleEntryFileCount
; ++i
) {
792 if (empty_file_omitted_
[i
]) {
793 out_entry_stat
->set_data_size(i
+ 1, 0);
797 File::Info file_info
;
798 bool success
= files_
[i
].GetInfo(&file_info
);
799 base::Time file_last_modified
;
801 DLOG(WARNING
) << "Could not get platform file info.";
804 out_entry_stat
->set_last_used(file_info
.last_accessed
);
805 if (simple_util::GetMTime(path_
, &file_last_modified
))
806 out_entry_stat
->set_last_modified(file_last_modified
);
808 out_entry_stat
->set_last_modified(file_info
.last_modified
);
810 base::TimeDelta stream_age
=
811 base::Time::Now() - out_entry_stat
->last_modified();
812 if (stream_age
< entry_age
)
813 entry_age
= stream_age
;
815 // Two things prevent from knowing the right values for |data_size|:
816 // 1) The key is not known, hence its length is unknown.
817 // 2) Stream 0 and stream 1 are in the same file, and the exact size for
818 // each will only be known when reading the EOF record for stream 0.
820 // The size for file 0 and 1 is temporarily kept in
821 // |data_size(1)| and |data_size(2)| respectively. Reading the key in
822 // InitializeForOpen yields the data size for each file. In the case of
823 // file hash_1, this is the total size of stream 2, and is assigned to
824 // data_size(2). In the case of file 0, it is the combined size of stream
825 // 0, stream 1 and one EOF record. The exact distribution of sizes between
826 // stream 1 and stream 0 is only determined after reading the EOF record
827 // for stream 0 in ReadAndValidateStream0.
828 out_entry_stat
->set_data_size(i
+ 1, static_cast<int>(file_info
.size
));
830 SIMPLE_CACHE_UMA(CUSTOM_COUNTS
,
831 "SyncOpenEntryAge", cache_type_
,
832 entry_age
.InHours(), 1, 1000, 50);
834 files_created_
= false;
839 bool SimpleSynchronousEntry::CreateFiles(
841 SimpleEntryStat
* out_entry_stat
) {
842 for (int i
= 0; i
< kSimpleEntryFileCount
; ++i
) {
844 if (!MaybeCreateFile(i
, FILE_NOT_REQUIRED
, &error
)) {
845 // TODO(ttuttle,gavinp): Remove one each of these triplets of histograms.
846 // We can calculate the third as the sum or difference of the other two.
847 RecordSyncCreateResult(CREATE_ENTRY_PLATFORM_FILE_ERROR
, had_index
);
848 SIMPLE_CACHE_UMA(ENUMERATION
,
849 "SyncCreatePlatformFileError", cache_type_
,
850 -error
, -base::File::FILE_ERROR_MAX
);
852 SIMPLE_CACHE_UMA(ENUMERATION
,
853 "SyncCreatePlatformFileError_WithIndex", cache_type_
,
854 -error
, -base::File::FILE_ERROR_MAX
);
856 SIMPLE_CACHE_UMA(ENUMERATION
,
857 "SyncCreatePlatformFileError_WithoutIndex",
859 -error
, -base::File::FILE_ERROR_MAX
);
867 have_open_files_
= true;
869 base::Time creation_time
= Time::Now();
870 out_entry_stat
->set_last_modified(creation_time
);
871 out_entry_stat
->set_last_used(creation_time
);
872 for (int i
= 0; i
< kSimpleEntryStreamCount
; ++i
)
873 out_entry_stat
->set_data_size(i
, 0);
875 files_created_
= true;
880 void SimpleSynchronousEntry::CloseFile(int index
) {
881 if (empty_file_omitted_
[index
]) {
882 empty_file_omitted_
[index
] = false;
884 DCHECK(files_
[index
].IsValid());
885 files_
[index
].Close();
888 if (sparse_file_open())
892 void SimpleSynchronousEntry::CloseFiles() {
893 for (int i
= 0; i
< kSimpleEntryFileCount
; ++i
)
897 int SimpleSynchronousEntry::InitializeForOpen(
899 SimpleEntryStat
* out_entry_stat
,
900 scoped_refptr
<net::GrowableIOBuffer
>* stream_0_data
,
901 uint32
* out_stream_0_crc32
) {
902 DCHECK(!initialized_
);
903 if (!OpenFiles(had_index
, out_entry_stat
)) {
904 DLOG(WARNING
) << "Could not open platform files for entry.";
905 return net::ERR_FAILED
;
907 for (int i
= 0; i
< kSimpleEntryFileCount
; ++i
) {
908 if (empty_file_omitted_
[i
])
911 SimpleFileHeader header
;
912 int header_read_result
=
913 files_
[i
].Read(0, reinterpret_cast<char*>(&header
), sizeof(header
));
914 if (header_read_result
!= sizeof(header
)) {
915 DLOG(WARNING
) << "Cannot read header from entry.";
916 RecordSyncOpenResult(cache_type_
, OPEN_ENTRY_CANT_READ_HEADER
, had_index
);
917 return net::ERR_FAILED
;
920 if (header
.initial_magic_number
!= kSimpleInitialMagicNumber
) {
921 // TODO(gavinp): This seems very bad; for now we log at WARNING, but we
922 // should give consideration to not saturating the log with these if that
923 // becomes a problem.
924 DLOG(WARNING
) << "Magic number did not match.";
925 RecordSyncOpenResult(cache_type_
, OPEN_ENTRY_BAD_MAGIC_NUMBER
, had_index
);
926 return net::ERR_FAILED
;
929 if (header
.version
!= kSimpleEntryVersionOnDisk
) {
930 DLOG(WARNING
) << "Unreadable version.";
931 RecordSyncOpenResult(cache_type_
, OPEN_ENTRY_BAD_VERSION
, had_index
);
932 return net::ERR_FAILED
;
935 scoped_ptr
<char[]> key(new char[header
.key_length
]);
936 int key_read_result
= files_
[i
].Read(sizeof(header
), key
.get(),
938 if (key_read_result
!= implicit_cast
<int>(header
.key_length
)) {
939 DLOG(WARNING
) << "Cannot read key from entry.";
940 RecordSyncOpenResult(cache_type_
, OPEN_ENTRY_CANT_READ_KEY
, had_index
);
941 return net::ERR_FAILED
;
944 key_
= std::string(key
.get(), header
.key_length
);
946 // File size for stream 0 has been stored temporarily in data_size[1].
947 int total_data_size
=
948 GetDataSizeFromKeyAndFileSize(key_
, out_entry_stat
->data_size(1));
949 int ret_value_stream_0
= ReadAndValidateStream0(
950 total_data_size
, out_entry_stat
, stream_0_data
, out_stream_0_crc32
);
951 if (ret_value_stream_0
!= net::OK
)
952 return ret_value_stream_0
;
954 out_entry_stat
->set_data_size(
955 2, GetDataSizeFromKeyAndFileSize(key_
, out_entry_stat
->data_size(2)));
956 if (out_entry_stat
->data_size(2) < 0) {
957 DLOG(WARNING
) << "Stream 2 file is too small.";
958 return net::ERR_FAILED
;
962 if (base::Hash(key
.get(), header
.key_length
) != header
.key_hash
) {
963 DLOG(WARNING
) << "Hash mismatch on key.";
964 RecordSyncOpenResult(
965 cache_type_
, OPEN_ENTRY_KEY_HASH_MISMATCH
, had_index
);
966 return net::ERR_FAILED
;
970 int32 sparse_data_size
= 0;
971 if (!OpenSparseFileIfExists(&sparse_data_size
)) {
972 RecordSyncOpenResult(
973 cache_type_
, OPEN_ENTRY_SPARSE_OPEN_FAILED
, had_index
);
974 return net::ERR_FAILED
;
976 out_entry_stat
->set_sparse_data_size(sparse_data_size
);
978 bool removed_stream2
= false;
979 const int stream2_file_index
= GetFileIndexFromStreamIndex(2);
980 DCHECK(CanOmitEmptyFile(stream2_file_index
));
981 if (!empty_file_omitted_
[stream2_file_index
] &&
982 out_entry_stat
->data_size(2) == 0) {
983 DVLOG(1) << "Removing empty stream 2 file.";
984 CloseFile(stream2_file_index
);
985 DeleteFileForEntryHash(path_
, entry_hash_
, stream2_file_index
);
986 empty_file_omitted_
[stream2_file_index
] = true;
987 removed_stream2
= true;
990 SIMPLE_CACHE_UMA(BOOLEAN
, "EntryOpenedAndStream2Removed", cache_type_
,
993 RecordSyncOpenResult(cache_type_
, OPEN_ENTRY_SUCCESS
, had_index
);
998 bool SimpleSynchronousEntry::InitializeCreatedFile(
1000 CreateEntryResult
* out_result
) {
1001 SimpleFileHeader header
;
1002 header
.initial_magic_number
= kSimpleInitialMagicNumber
;
1003 header
.version
= kSimpleEntryVersionOnDisk
;
1005 header
.key_length
= key_
.size();
1006 header
.key_hash
= base::Hash(key_
);
1008 int bytes_written
= files_
[file_index
].Write(
1009 0, reinterpret_cast<char*>(&header
), sizeof(header
));
1010 if (bytes_written
!= sizeof(header
)) {
1011 *out_result
= CREATE_ENTRY_CANT_WRITE_HEADER
;
1015 bytes_written
= files_
[file_index
].Write(sizeof(header
), key_
.data(),
1017 if (bytes_written
!= implicit_cast
<int>(key_
.size())) {
1018 *out_result
= CREATE_ENTRY_CANT_WRITE_KEY
;
1025 int SimpleSynchronousEntry::InitializeForCreate(
1027 SimpleEntryStat
* out_entry_stat
) {
1028 DCHECK(!initialized_
);
1029 if (!CreateFiles(had_index
, out_entry_stat
)) {
1030 DLOG(WARNING
) << "Could not create platform files.";
1031 return net::ERR_FILE_EXISTS
;
1033 for (int i
= 0; i
< kSimpleEntryFileCount
; ++i
) {
1034 if (empty_file_omitted_
[i
])
1037 CreateEntryResult result
;
1038 if (!InitializeCreatedFile(i
, &result
)) {
1039 RecordSyncCreateResult(result
, had_index
);
1040 return net::ERR_FAILED
;
1043 RecordSyncCreateResult(CREATE_ENTRY_SUCCESS
, had_index
);
1044 initialized_
= true;
1048 int SimpleSynchronousEntry::ReadAndValidateStream0(
1049 int total_data_size
,
1050 SimpleEntryStat
* out_entry_stat
,
1051 scoped_refptr
<net::GrowableIOBuffer
>* stream_0_data
,
1052 uint32
* out_stream_0_crc32
) const {
1053 // Temporarily assign all the data size to stream 1 in order to read the
1054 // EOF record for stream 0, which contains the size of stream 0.
1055 out_entry_stat
->set_data_size(0, 0);
1056 out_entry_stat
->set_data_size(1, total_data_size
- sizeof(SimpleFileEOF
));
1061 int ret_value_crc32
= GetEOFRecordData(
1062 0, *out_entry_stat
, &has_crc32
, &read_crc32
, &stream_0_size
);
1063 if (ret_value_crc32
!= net::OK
)
1064 return ret_value_crc32
;
1066 if (stream_0_size
> out_entry_stat
->data_size(1))
1067 return net::ERR_FAILED
;
1069 // These are the real values of data size.
1070 out_entry_stat
->set_data_size(0, stream_0_size
);
1071 out_entry_stat
->set_data_size(
1072 1, out_entry_stat
->data_size(1) - stream_0_size
);
1074 // Put stream 0 data in memory.
1075 *stream_0_data
= new net::GrowableIOBuffer();
1076 (*stream_0_data
)->SetCapacity(stream_0_size
);
1077 int file_offset
= out_entry_stat
->GetOffsetInFile(key_
, 0, 0);
1078 File
* file
= const_cast<File
*>(&files_
[0]);
1080 file
->Read(file_offset
, (*stream_0_data
)->data(), stream_0_size
);
1081 if (bytes_read
!= stream_0_size
)
1082 return net::ERR_FAILED
;
1085 uint32 expected_crc32
=
1087 ? crc32(0, Z_NULL
, 0)
1088 : crc32(crc32(0, Z_NULL
, 0),
1089 reinterpret_cast<const Bytef
*>((*stream_0_data
)->data()),
1091 if (has_crc32
&& read_crc32
!= expected_crc32
) {
1092 DVLOG(1) << "EOF record had bad crc.";
1093 RecordCheckEOFResult(cache_type_
, CHECK_EOF_RESULT_CRC_MISMATCH
);
1094 return net::ERR_FAILED
;
1096 *out_stream_0_crc32
= expected_crc32
;
1097 RecordCheckEOFResult(cache_type_
, CHECK_EOF_RESULT_SUCCESS
);
1101 int SimpleSynchronousEntry::GetEOFRecordData(int index
,
1102 const SimpleEntryStat
& entry_stat
,
1103 bool* out_has_crc32
,
1105 int* out_data_size
) const {
1106 SimpleFileEOF eof_record
;
1107 int file_offset
= entry_stat
.GetEOFOffsetInFile(key_
, index
);
1108 int file_index
= GetFileIndexFromStreamIndex(index
);
1109 File
* file
= const_cast<File
*>(&files_
[file_index
]);
1110 if (file
->Read(file_offset
, reinterpret_cast<char*>(&eof_record
),
1111 sizeof(eof_record
)) !=
1112 sizeof(eof_record
)) {
1113 RecordCheckEOFResult(cache_type_
, CHECK_EOF_RESULT_READ_FAILURE
);
1114 return net::ERR_CACHE_CHECKSUM_READ_FAILURE
;
1117 if (eof_record
.final_magic_number
!= kSimpleFinalMagicNumber
) {
1118 RecordCheckEOFResult(cache_type_
, CHECK_EOF_RESULT_MAGIC_NUMBER_MISMATCH
);
1119 DVLOG(1) << "EOF record had bad magic number.";
1120 return net::ERR_CACHE_CHECKSUM_READ_FAILURE
;
1123 *out_has_crc32
= (eof_record
.flags
& SimpleFileEOF::FLAG_HAS_CRC32
) ==
1124 SimpleFileEOF::FLAG_HAS_CRC32
;
1125 *out_crc32
= eof_record
.data_crc32
;
1126 *out_data_size
= eof_record
.stream_size
;
1127 SIMPLE_CACHE_UMA(BOOLEAN
, "SyncCheckEOFHasCrc", cache_type_
, *out_has_crc32
);
1131 void SimpleSynchronousEntry::Doom() const {
1132 DeleteFilesForEntryHash(path_
, entry_hash_
);
1136 bool SimpleSynchronousEntry::DeleteFileForEntryHash(
1137 const FilePath
& path
,
1138 const uint64 entry_hash
,
1139 const int file_index
) {
1140 FilePath to_delete
= path
.AppendASCII(
1141 GetFilenameFromEntryHashAndFileIndex(entry_hash
, file_index
));
1142 return simple_util::SimpleCacheDeleteFile(to_delete
);
1146 bool SimpleSynchronousEntry::DeleteFilesForEntryHash(
1147 const FilePath
& path
,
1148 const uint64 entry_hash
) {
1150 for (int i
= 0; i
< kSimpleEntryFileCount
; ++i
) {
1151 if (!DeleteFileForEntryHash(path
, entry_hash
, i
) && !CanOmitEmptyFile(i
))
1154 FilePath to_delete
= path
.AppendASCII(
1155 GetSparseFilenameFromEntryHash(entry_hash
));
1156 simple_util::SimpleCacheDeleteFile(to_delete
);
1160 void SimpleSynchronousEntry::RecordSyncCreateResult(CreateEntryResult result
,
1162 DCHECK_LT(result
, CREATE_ENTRY_MAX
);
1163 SIMPLE_CACHE_UMA(ENUMERATION
,
1164 "SyncCreateResult", cache_type_
, result
, CREATE_ENTRY_MAX
);
1166 SIMPLE_CACHE_UMA(ENUMERATION
,
1167 "SyncCreateResult_WithIndex", cache_type_
,
1168 result
, CREATE_ENTRY_MAX
);
1170 SIMPLE_CACHE_UMA(ENUMERATION
,
1171 "SyncCreateResult_WithoutIndex", cache_type_
,
1172 result
, CREATE_ENTRY_MAX
);
1176 FilePath
SimpleSynchronousEntry::GetFilenameFromFileIndex(int file_index
) {
1177 return path_
.AppendASCII(
1178 GetFilenameFromEntryHashAndFileIndex(entry_hash_
, file_index
));
1181 bool SimpleSynchronousEntry::OpenSparseFileIfExists(
1182 int32
* out_sparse_data_size
) {
1183 DCHECK(!sparse_file_open());
1185 FilePath filename
= path_
.AppendASCII(
1186 GetSparseFilenameFromEntryHash(entry_hash_
));
1187 int flags
= File::FLAG_OPEN
| File::FLAG_READ
| File::FLAG_WRITE
|
1188 File::FLAG_SHARE_DELETE
;
1189 sparse_file_
.Initialize(filename
, flags
);
1190 if (sparse_file_
.IsValid())
1191 return ScanSparseFile(out_sparse_data_size
);
1193 return sparse_file_
.error_details() == File::FILE_ERROR_NOT_FOUND
;
1196 bool SimpleSynchronousEntry::CreateSparseFile() {
1197 DCHECK(!sparse_file_open());
1199 FilePath filename
= path_
.AppendASCII(
1200 GetSparseFilenameFromEntryHash(entry_hash_
));
1201 int flags
= File::FLAG_CREATE
| File::FLAG_READ
| File::FLAG_WRITE
|
1202 File::FLAG_SHARE_DELETE
;
1203 sparse_file_
.Initialize(filename
, flags
);
1204 if (!sparse_file_
.IsValid())
1207 return InitializeSparseFile();
1210 void SimpleSynchronousEntry::CloseSparseFile() {
1211 DCHECK(sparse_file_open());
1212 sparse_file_
.Close();
1215 bool SimpleSynchronousEntry::TruncateSparseFile() {
1216 DCHECK(sparse_file_open());
1218 int64 header_and_key_length
= sizeof(SimpleFileHeader
) + key_
.size();
1219 if (!sparse_file_
.SetLength(header_and_key_length
)) {
1220 DLOG(WARNING
) << "Could not truncate sparse file";
1224 sparse_ranges_
.clear();
1229 bool SimpleSynchronousEntry::InitializeSparseFile() {
1230 DCHECK(sparse_file_open());
1232 SimpleFileHeader header
;
1233 header
.initial_magic_number
= kSimpleInitialMagicNumber
;
1234 header
.version
= kSimpleVersion
;
1235 header
.key_length
= key_
.size();
1236 header
.key_hash
= base::Hash(key_
);
1238 int header_write_result
=
1239 sparse_file_
.Write(0, reinterpret_cast<char*>(&header
), sizeof(header
));
1240 if (header_write_result
!= sizeof(header
)) {
1241 DLOG(WARNING
) << "Could not write sparse file header";
1245 int key_write_result
= sparse_file_
.Write(sizeof(header
), key_
.data(),
1247 if (key_write_result
!= implicit_cast
<int>(key_
.size())) {
1248 DLOG(WARNING
) << "Could not write sparse file key";
1252 sparse_ranges_
.clear();
1253 sparse_tail_offset_
= sizeof(header
) + key_
.size();
1258 bool SimpleSynchronousEntry::ScanSparseFile(int32
* out_sparse_data_size
) {
1259 DCHECK(sparse_file_open());
1261 int64 sparse_data_size
= 0;
1263 SimpleFileHeader header
;
1264 int header_read_result
=
1265 sparse_file_
.Read(0, reinterpret_cast<char*>(&header
), sizeof(header
));
1266 if (header_read_result
!= sizeof(header
)) {
1267 DLOG(WARNING
) << "Could not read header from sparse file.";
1271 if (header
.initial_magic_number
!= kSimpleInitialMagicNumber
) {
1272 DLOG(WARNING
) << "Sparse file magic number did not match.";
1276 if (header
.version
!= kSimpleVersion
) {
1277 DLOG(WARNING
) << "Sparse file unreadable version.";
1281 sparse_ranges_
.clear();
1283 int64 range_header_offset
= sizeof(header
) + key_
.size();
1285 SimpleFileSparseRangeHeader range_header
;
1286 int range_header_read_result
=
1287 sparse_file_
.Read(range_header_offset
,
1288 reinterpret_cast<char*>(&range_header
),
1289 sizeof(range_header
));
1290 if (range_header_read_result
== 0)
1292 if (range_header_read_result
!= sizeof(range_header
)) {
1293 DLOG(WARNING
) << "Could not read sparse range header.";
1297 if (range_header
.sparse_range_magic_number
!=
1298 kSimpleSparseRangeMagicNumber
) {
1299 DLOG(WARNING
) << "Invalid sparse range header magic number.";
1304 range
.offset
= range_header
.offset
;
1305 range
.length
= range_header
.length
;
1306 range
.data_crc32
= range_header
.data_crc32
;
1307 range
.file_offset
= range_header_offset
+ sizeof(range_header
);
1308 sparse_ranges_
.insert(std::make_pair(range
.offset
, range
));
1310 range_header_offset
+= sizeof(range_header
) + range
.length
;
1312 DCHECK_GE(sparse_data_size
+ range
.length
, sparse_data_size
);
1313 sparse_data_size
+= range
.length
;
1316 *out_sparse_data_size
= static_cast<int32
>(sparse_data_size
);
1317 sparse_tail_offset_
= range_header_offset
;
1322 bool SimpleSynchronousEntry::ReadSparseRange(const SparseRange
* range
,
1323 int offset
, int len
, char* buf
) {
1326 DCHECK_LE(offset
, range
->length
);
1327 DCHECK_LE(offset
+ len
, range
->length
);
1329 int bytes_read
= sparse_file_
.Read(range
->file_offset
+ offset
, buf
, len
);
1330 if (bytes_read
< len
) {
1331 DLOG(WARNING
) << "Could not read sparse range.";
1335 // If we read the whole range and we have a crc32, check it.
1336 if (offset
== 0 && len
== range
->length
&& range
->data_crc32
!= 0) {
1337 uint32 actual_crc32
= crc32(crc32(0L, Z_NULL
, 0),
1338 reinterpret_cast<const Bytef
*>(buf
),
1340 if (actual_crc32
!= range
->data_crc32
) {
1341 DLOG(WARNING
) << "Sparse range crc32 mismatch.";
1345 // TODO(ttuttle): Incremental crc32 calculation?
1350 bool SimpleSynchronousEntry::WriteSparseRange(SparseRange
* range
,
1351 int offset
, int len
,
1355 DCHECK_LE(offset
, range
->length
);
1356 DCHECK_LE(offset
+ len
, range
->length
);
1358 uint32 new_crc32
= 0;
1359 if (offset
== 0 && len
== range
->length
) {
1360 new_crc32
= crc32(crc32(0L, Z_NULL
, 0),
1361 reinterpret_cast<const Bytef
*>(buf
),
1365 if (new_crc32
!= range
->data_crc32
) {
1366 range
->data_crc32
= new_crc32
;
1368 SimpleFileSparseRangeHeader header
;
1369 header
.sparse_range_magic_number
= kSimpleSparseRangeMagicNumber
;
1370 header
.offset
= range
->offset
;
1371 header
.length
= range
->length
;
1372 header
.data_crc32
= range
->data_crc32
;
1374 int bytes_written
= sparse_file_
.Write(range
->file_offset
- sizeof(header
),
1375 reinterpret_cast<char*>(&header
),
1377 if (bytes_written
!= implicit_cast
<int>(sizeof(header
))) {
1378 DLOG(WARNING
) << "Could not rewrite sparse range header.";
1383 int bytes_written
= sparse_file_
.Write(range
->file_offset
+ offset
, buf
, len
);
1384 if (bytes_written
< len
) {
1385 DLOG(WARNING
) << "Could not write sparse range.";
1392 bool SimpleSynchronousEntry::AppendSparseRange(int64 offset
,
1395 DCHECK_GE(offset
, 0);
1399 uint32 data_crc32
= crc32(crc32(0L, Z_NULL
, 0),
1400 reinterpret_cast<const Bytef
*>(buf
),
1403 SimpleFileSparseRangeHeader header
;
1404 header
.sparse_range_magic_number
= kSimpleSparseRangeMagicNumber
;
1405 header
.offset
= offset
;
1406 header
.length
= len
;
1407 header
.data_crc32
= data_crc32
;
1409 int bytes_written
= sparse_file_
.Write(sparse_tail_offset_
,
1410 reinterpret_cast<char*>(&header
),
1412 if (bytes_written
!= implicit_cast
<int>(sizeof(header
))) {
1413 DLOG(WARNING
) << "Could not append sparse range header.";
1416 sparse_tail_offset_
+= bytes_written
;
1418 bytes_written
= sparse_file_
.Write(sparse_tail_offset_
, buf
, len
);
1419 if (bytes_written
< len
) {
1420 DLOG(WARNING
) << "Could not append sparse range data.";
1423 int64 data_file_offset
= sparse_tail_offset_
;
1424 sparse_tail_offset_
+= bytes_written
;
1427 range
.offset
= offset
;
1429 range
.data_crc32
= data_crc32
;
1430 range
.file_offset
= data_file_offset
;
1431 sparse_ranges_
.insert(std::make_pair(offset
, range
));
1436 } // namespace disk_cache