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 int64 headers_size
= sizeof(SimpleFileHeader
) + key
.size();
138 const int64 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 int SimpleEntryStat::GetFileSize(const std::string
& key
, int file_index
) const {
158 const int total_data_size
=
159 file_index
== 0 ? data_size_
[0] + data_size_
[1] + sizeof(SimpleFileEOF
)
161 return GetFileSizeFromKeyAndDataSize(key
, total_data_size
);
164 SimpleEntryCreationResults::SimpleEntryCreationResults(
165 SimpleEntryStat entry_stat
)
167 entry_stat(entry_stat
),
168 stream_0_crc32(crc32(0, Z_NULL
, 0)),
172 SimpleEntryCreationResults::~SimpleEntryCreationResults() {
175 SimpleSynchronousEntry::CRCRecord::CRCRecord() : index(-1),
180 SimpleSynchronousEntry::CRCRecord::CRCRecord(int index_p
,
184 has_crc32(has_crc32_p
),
185 data_crc32(data_crc32_p
) {}
187 SimpleSynchronousEntry::EntryOperationData::EntryOperationData(int index_p
,
192 buf_len(buf_len_p
) {}
194 SimpleSynchronousEntry::EntryOperationData::EntryOperationData(int index_p
,
202 truncate(truncate_p
),
205 SimpleSynchronousEntry::EntryOperationData::EntryOperationData(
206 int64 sparse_offset_p
,
208 : sparse_offset(sparse_offset_p
),
209 buf_len(buf_len_p
) {}
212 void SimpleSynchronousEntry::OpenEntry(
213 net::CacheType cache_type
,
214 const FilePath
& path
,
215 const uint64 entry_hash
,
217 SimpleEntryCreationResults
*out_results
) {
218 SimpleSynchronousEntry
* sync_entry
=
219 new SimpleSynchronousEntry(cache_type
, path
, "", entry_hash
);
220 out_results
->result
=
221 sync_entry
->InitializeForOpen(had_index
,
222 &out_results
->entry_stat
,
223 &out_results
->stream_0_data
,
224 &out_results
->stream_0_crc32
);
225 if (out_results
->result
!= net::OK
) {
228 out_results
->sync_entry
= NULL
;
229 out_results
->stream_0_data
= NULL
;
232 out_results
->sync_entry
= sync_entry
;
236 void SimpleSynchronousEntry::CreateEntry(
237 net::CacheType cache_type
,
238 const FilePath
& path
,
239 const std::string
& key
,
240 const uint64 entry_hash
,
242 SimpleEntryCreationResults
*out_results
) {
243 DCHECK_EQ(entry_hash
, GetEntryHashKey(key
));
244 SimpleSynchronousEntry
* sync_entry
=
245 new SimpleSynchronousEntry(cache_type
, path
, key
, entry_hash
);
246 out_results
->result
= sync_entry
->InitializeForCreate(
247 had_index
, &out_results
->entry_stat
);
248 if (out_results
->result
!= net::OK
) {
249 if (out_results
->result
!= net::ERR_FILE_EXISTS
)
252 out_results
->sync_entry
= NULL
;
255 out_results
->sync_entry
= sync_entry
;
259 int SimpleSynchronousEntry::DoomEntry(
260 const FilePath
& path
,
262 const bool deleted_well
= DeleteFilesForEntryHash(path
, entry_hash
);
263 return deleted_well
? net::OK
: net::ERR_FAILED
;
267 int SimpleSynchronousEntry::DoomEntrySet(
268 const std::vector
<uint64
>* key_hashes
,
269 const FilePath
& path
) {
270 const size_t did_delete_count
= std::count_if(
271 key_hashes
->begin(), key_hashes
->end(), std::bind1st(
272 std::ptr_fun(SimpleSynchronousEntry::DeleteFilesForEntryHash
), path
));
273 return (did_delete_count
== key_hashes
->size()) ? net::OK
: net::ERR_FAILED
;
276 void SimpleSynchronousEntry::ReadData(const EntryOperationData
& in_entry_op
,
277 net::IOBuffer
* out_buf
,
279 SimpleEntryStat
* entry_stat
,
280 int* out_result
) const {
281 DCHECK(initialized_
);
282 DCHECK_NE(0, in_entry_op
.index
);
283 const int64 file_offset
=
284 entry_stat
->GetOffsetInFile(key_
, in_entry_op
.offset
, in_entry_op
.index
);
285 int file_index
= GetFileIndexFromStreamIndex(in_entry_op
.index
);
286 // Zero-length reads and reads to the empty streams of omitted files should
287 // be handled in the SimpleEntryImpl.
288 DCHECK_GT(in_entry_op
.buf_len
, 0);
289 DCHECK(!empty_file_omitted_
[file_index
]);
290 File
* file
= const_cast<File
*>(&files_
[file_index
]);
292 file
->Read(file_offset
, out_buf
->data(), in_entry_op
.buf_len
);
293 if (bytes_read
> 0) {
294 entry_stat
->set_last_used(Time::Now());
295 *out_crc32
= crc32(crc32(0L, Z_NULL
, 0),
296 reinterpret_cast<const Bytef
*>(out_buf
->data()),
299 if (bytes_read
>= 0) {
300 *out_result
= bytes_read
;
302 *out_result
= net::ERR_CACHE_READ_FAILURE
;
307 void SimpleSynchronousEntry::WriteData(const EntryOperationData
& in_entry_op
,
308 net::IOBuffer
* in_buf
,
309 SimpleEntryStat
* out_entry_stat
,
311 DCHECK(initialized_
);
312 DCHECK_NE(0, in_entry_op
.index
);
313 int index
= in_entry_op
.index
;
314 int file_index
= GetFileIndexFromStreamIndex(index
);
315 int offset
= in_entry_op
.offset
;
316 int buf_len
= in_entry_op
.buf_len
;
317 bool truncate
= in_entry_op
.truncate
;
318 bool doomed
= in_entry_op
.doomed
;
319 const int64 file_offset
= out_entry_stat
->GetOffsetInFile(
320 key_
, in_entry_op
.offset
, in_entry_op
.index
);
321 bool extending_by_write
= offset
+ buf_len
> out_entry_stat
->data_size(index
);
323 if (empty_file_omitted_
[file_index
]) {
324 // Don't create a new file if the entry has been doomed, to avoid it being
325 // mixed up with a newly-created entry with the same key.
327 DLOG(WARNING
) << "Rejecting write to lazily omitted stream "
328 << in_entry_op
.index
<< " of doomed cache entry.";
329 RecordWriteResult(cache_type_
, WRITE_RESULT_LAZY_STREAM_ENTRY_DOOMED
);
330 *out_result
= net::ERR_CACHE_WRITE_FAILURE
;
334 if (!MaybeCreateFile(file_index
, FILE_REQUIRED
, &error
)) {
335 RecordWriteResult(cache_type_
, WRITE_RESULT_LAZY_CREATE_FAILURE
);
337 *out_result
= net::ERR_CACHE_WRITE_FAILURE
;
340 CreateEntryResult result
;
341 if (!InitializeCreatedFile(file_index
, &result
)) {
342 RecordWriteResult(cache_type_
, WRITE_RESULT_LAZY_INITIALIZE_FAILURE
);
344 *out_result
= net::ERR_CACHE_WRITE_FAILURE
;
348 DCHECK(!empty_file_omitted_
[file_index
]);
350 if (extending_by_write
) {
351 // The EOF record and the eventual stream afterward need to be zeroed out.
352 const int64 file_eof_offset
=
353 out_entry_stat
->GetEOFOffsetInFile(key_
, index
);
354 if (!files_
[file_index
].SetLength(file_eof_offset
)) {
355 RecordWriteResult(cache_type_
, WRITE_RESULT_PRETRUNCATE_FAILURE
);
357 *out_result
= net::ERR_CACHE_WRITE_FAILURE
;
362 if (files_
[file_index
].Write(file_offset
, in_buf
->data(), buf_len
) !=
364 RecordWriteResult(cache_type_
, WRITE_RESULT_WRITE_FAILURE
);
366 *out_result
= net::ERR_CACHE_WRITE_FAILURE
;
370 if (!truncate
&& (buf_len
> 0 || !extending_by_write
)) {
371 out_entry_stat
->set_data_size(
372 index
, std::max(out_entry_stat
->data_size(index
), offset
+ buf_len
));
374 out_entry_stat
->set_data_size(index
, offset
+ buf_len
);
375 int file_eof_offset
= out_entry_stat
->GetLastEOFOffsetInFile(key_
, index
);
376 if (!files_
[file_index
].SetLength(file_eof_offset
)) {
377 RecordWriteResult(cache_type_
, WRITE_RESULT_TRUNCATE_FAILURE
);
379 *out_result
= net::ERR_CACHE_WRITE_FAILURE
;
384 RecordWriteResult(cache_type_
, WRITE_RESULT_SUCCESS
);
385 base::Time modification_time
= Time::Now();
386 out_entry_stat
->set_last_used(modification_time
);
387 out_entry_stat
->set_last_modified(modification_time
);
388 *out_result
= buf_len
;
391 void SimpleSynchronousEntry::ReadSparseData(
392 const EntryOperationData
& in_entry_op
,
393 net::IOBuffer
* out_buf
,
394 base::Time
* out_last_used
,
396 DCHECK(initialized_
);
397 int64 offset
= in_entry_op
.sparse_offset
;
398 int buf_len
= in_entry_op
.buf_len
;
400 char* buf
= out_buf
->data();
403 // Find the first sparse range at or after the requested offset.
404 SparseRangeIterator it
= sparse_ranges_
.lower_bound(offset
);
406 if (it
!= sparse_ranges_
.begin()) {
407 // Hop back one range and read the one overlapping with the start.
409 SparseRange
* found_range
= &it
->second
;
410 DCHECK_EQ(it
->first
, found_range
->offset
);
411 if (found_range
->offset
+ found_range
->length
> offset
) {
412 DCHECK_GE(found_range
->length
, 0);
413 DCHECK_LE(found_range
->length
, kint32max
);
414 DCHECK_GE(offset
- found_range
->offset
, 0);
415 DCHECK_LE(offset
- found_range
->offset
, kint32max
);
416 int net_offset
= static_cast<int>(offset
- found_range
->offset
);
417 int range_len_after_offset
=
418 static_cast<int>(found_range
->length
- net_offset
);
419 DCHECK_GE(range_len_after_offset
, 0);
421 int len_to_read
= std::min(buf_len
, range_len_after_offset
);
422 if (!ReadSparseRange(found_range
, net_offset
, len_to_read
, buf
)) {
423 *out_result
= net::ERR_CACHE_READ_FAILURE
;
426 read_so_far
+= len_to_read
;
431 // Keep reading until the buffer is full or there is not another contiguous
433 while (read_so_far
< buf_len
&&
434 it
!= sparse_ranges_
.end() &&
435 it
->second
.offset
== offset
+ read_so_far
) {
436 SparseRange
* found_range
= &it
->second
;
437 DCHECK_EQ(it
->first
, found_range
->offset
);
438 int range_len
= base::saturated_cast
<int>(found_range
->length
);
439 int len_to_read
= std::min(buf_len
- read_so_far
, range_len
);
440 if (!ReadSparseRange(found_range
, 0, len_to_read
, buf
+ read_so_far
)) {
441 *out_result
= net::ERR_CACHE_READ_FAILURE
;
444 read_so_far
+= len_to_read
;
448 *out_result
= read_so_far
;
451 void SimpleSynchronousEntry::WriteSparseData(
452 const EntryOperationData
& in_entry_op
,
453 net::IOBuffer
* in_buf
,
454 int64 max_sparse_data_size
,
455 SimpleEntryStat
* out_entry_stat
,
457 DCHECK(initialized_
);
458 int64 offset
= in_entry_op
.sparse_offset
;
459 int buf_len
= in_entry_op
.buf_len
;
461 const char* buf
= in_buf
->data();
462 int written_so_far
= 0;
463 int appended_so_far
= 0;
465 if (!sparse_file_open() && !CreateSparseFile()) {
466 *out_result
= net::ERR_CACHE_WRITE_FAILURE
;
470 int64 sparse_data_size
= out_entry_stat
->sparse_data_size();
471 // This is a pessimistic estimate; it assumes the entire buffer is going to
472 // be appended as a new range, not written over existing ranges.
473 if (sparse_data_size
+ buf_len
> max_sparse_data_size
) {
474 DVLOG(1) << "Truncating sparse data file (" << sparse_data_size
<< " + "
475 << buf_len
<< " > " << max_sparse_data_size
<< ")";
476 TruncateSparseFile();
479 SparseRangeIterator it
= sparse_ranges_
.lower_bound(offset
);
481 if (it
!= sparse_ranges_
.begin()) {
483 SparseRange
* found_range
= &it
->second
;
484 if (found_range
->offset
+ found_range
->length
> offset
) {
485 DCHECK_GE(found_range
->length
, 0);
486 DCHECK_LE(found_range
->length
, kint32max
);
487 DCHECK_GE(offset
- found_range
->offset
, 0);
488 DCHECK_LE(offset
- found_range
->offset
, kint32max
);
489 int net_offset
= static_cast<int>(offset
- found_range
->offset
);
490 int range_len_after_offset
=
491 static_cast<int>(found_range
->length
- net_offset
);
492 DCHECK_GE(range_len_after_offset
, 0);
494 int len_to_write
= std::min(buf_len
, range_len_after_offset
);
495 if (!WriteSparseRange(found_range
, net_offset
, len_to_write
, buf
)) {
496 *out_result
= net::ERR_CACHE_WRITE_FAILURE
;
499 written_so_far
+= len_to_write
;
504 while (written_so_far
< buf_len
&&
505 it
!= sparse_ranges_
.end() &&
506 it
->second
.offset
< offset
+ buf_len
) {
507 SparseRange
* found_range
= &it
->second
;
508 if (offset
+ written_so_far
< found_range
->offset
) {
510 static_cast<int>(found_range
->offset
- (offset
+ written_so_far
));
511 if (!AppendSparseRange(offset
+ written_so_far
,
513 buf
+ written_so_far
)) {
514 *out_result
= net::ERR_CACHE_WRITE_FAILURE
;
517 written_so_far
+= len_to_append
;
518 appended_so_far
+= len_to_append
;
520 int range_len
= base::saturated_cast
<int>(found_range
->length
);
521 int len_to_write
= std::min(buf_len
- written_so_far
, range_len
);
522 if (!WriteSparseRange(found_range
,
525 buf
+ written_so_far
)) {
526 *out_result
= net::ERR_CACHE_WRITE_FAILURE
;
529 written_so_far
+= len_to_write
;
533 if (written_so_far
< buf_len
) {
534 int len_to_append
= buf_len
- written_so_far
;
535 if (!AppendSparseRange(offset
+ written_so_far
,
537 buf
+ written_so_far
)) {
538 *out_result
= net::ERR_CACHE_WRITE_FAILURE
;
541 written_so_far
+= len_to_append
;
542 appended_so_far
+= len_to_append
;
545 DCHECK_EQ(buf_len
, written_so_far
);
547 base::Time modification_time
= Time::Now();
548 out_entry_stat
->set_last_used(modification_time
);
549 out_entry_stat
->set_last_modified(modification_time
);
550 int32 old_sparse_data_size
= out_entry_stat
->sparse_data_size();
551 out_entry_stat
->set_sparse_data_size(old_sparse_data_size
+ appended_so_far
);
552 *out_result
= written_so_far
;
555 void SimpleSynchronousEntry::GetAvailableRange(
556 const EntryOperationData
& in_entry_op
,
559 DCHECK(initialized_
);
560 int64 offset
= in_entry_op
.sparse_offset
;
561 int len
= in_entry_op
.buf_len
;
563 SparseRangeIterator it
= sparse_ranges_
.lower_bound(offset
);
565 int64 start
= offset
;
566 int64 avail_so_far
= 0;
568 if (it
!= sparse_ranges_
.end() && it
->second
.offset
< offset
+ len
)
569 start
= it
->second
.offset
;
571 if ((it
== sparse_ranges_
.end() || it
->second
.offset
> offset
) &&
572 it
!= sparse_ranges_
.begin()) {
574 if (it
->second
.offset
+ it
->second
.length
> offset
) {
576 avail_so_far
= (it
->second
.offset
+ it
->second
.length
) - offset
;
581 while (start
+ avail_so_far
< offset
+ len
&&
582 it
!= sparse_ranges_
.end() &&
583 it
->second
.offset
== start
+ avail_so_far
) {
584 avail_so_far
+= it
->second
.length
;
588 int64 len_from_start
= len
- (start
- offset
);
590 *out_result
= static_cast<int>(std::min(avail_so_far
, len_from_start
));
593 void SimpleSynchronousEntry::CheckEOFRecord(int index
,
594 const SimpleEntryStat
& entry_stat
,
595 uint32 expected_crc32
,
596 int* out_result
) const {
597 DCHECK(initialized_
);
602 GetEOFRecordData(index
, entry_stat
, &has_crc32
, &crc32
, &stream_size
);
603 if (*out_result
!= net::OK
) {
607 if (has_crc32
&& crc32
!= expected_crc32
) {
608 DVLOG(1) << "EOF record had bad crc.";
609 *out_result
= net::ERR_CACHE_CHECKSUM_MISMATCH
;
610 RecordCheckEOFResult(cache_type_
, CHECK_EOF_RESULT_CRC_MISMATCH
);
614 RecordCheckEOFResult(cache_type_
, CHECK_EOF_RESULT_SUCCESS
);
617 void SimpleSynchronousEntry::Close(
618 const SimpleEntryStat
& entry_stat
,
619 scoped_ptr
<std::vector
<CRCRecord
> > crc32s_to_write
,
620 net::GrowableIOBuffer
* stream_0_data
) {
621 DCHECK(stream_0_data
);
622 // Write stream 0 data.
623 int stream_0_offset
= entry_stat
.GetOffsetInFile(key_
, 0, 0);
624 if (files_
[0].Write(stream_0_offset
, stream_0_data
->data(),
625 entry_stat
.data_size(0)) !=
626 entry_stat
.data_size(0)) {
627 RecordCloseResult(cache_type_
, CLOSE_RESULT_WRITE_FAILURE
);
628 DVLOG(1) << "Could not write stream 0 data.";
632 for (std::vector
<CRCRecord
>::const_iterator it
= crc32s_to_write
->begin();
633 it
!= crc32s_to_write
->end(); ++it
) {
634 const int stream_index
= it
->index
;
635 const int file_index
= GetFileIndexFromStreamIndex(stream_index
);
636 if (empty_file_omitted_
[file_index
])
639 SimpleFileEOF eof_record
;
640 eof_record
.stream_size
= entry_stat
.data_size(stream_index
);
641 eof_record
.final_magic_number
= kSimpleFinalMagicNumber
;
642 eof_record
.flags
= 0;
644 eof_record
.flags
|= SimpleFileEOF::FLAG_HAS_CRC32
;
645 eof_record
.data_crc32
= it
->data_crc32
;
646 int eof_offset
= entry_stat
.GetEOFOffsetInFile(key_
, stream_index
);
647 // If stream 0 changed size, the file needs to be resized, otherwise the
648 // next open will yield wrong stream sizes. On stream 1 and stream 2 proper
649 // resizing of the file is handled in SimpleSynchronousEntry::WriteData().
650 if (stream_index
== 0 &&
651 !files_
[file_index
].SetLength(eof_offset
)) {
652 RecordCloseResult(cache_type_
, CLOSE_RESULT_WRITE_FAILURE
);
653 DVLOG(1) << "Could not truncate stream 0 file.";
657 if (files_
[file_index
].Write(eof_offset
,
658 reinterpret_cast<const char*>(&eof_record
),
659 sizeof(eof_record
)) !=
660 sizeof(eof_record
)) {
661 RecordCloseResult(cache_type_
, CLOSE_RESULT_WRITE_FAILURE
);
662 DVLOG(1) << "Could not write eof record.";
667 for (int i
= 0; i
< kSimpleEntryFileCount
; ++i
) {
668 if (empty_file_omitted_
[i
])
672 const int64 file_size
= entry_stat
.GetFileSize(key_
, i
);
673 SIMPLE_CACHE_UMA(CUSTOM_COUNTS
,
674 "LastClusterSize", cache_type_
,
675 file_size
% 4096, 0, 4097, 50);
676 const int64 cluster_loss
= file_size
% 4096 ? 4096 - file_size
% 4096 : 0;
677 SIMPLE_CACHE_UMA(PERCENTAGE
,
678 "LastClusterLossPercent", cache_type_
,
679 static_cast<base::HistogramBase::Sample
>(
680 cluster_loss
* 100 / (cluster_loss
+ file_size
)));
683 if (sparse_file_open())
684 sparse_file_
.Close();
686 if (files_created_
) {
687 const int stream2_file_index
= GetFileIndexFromStreamIndex(2);
688 SIMPLE_CACHE_UMA(BOOLEAN
, "EntryCreatedAndStream2Omitted", cache_type_
,
689 empty_file_omitted_
[stream2_file_index
]);
691 RecordCloseResult(cache_type_
, CLOSE_RESULT_SUCCESS
);
692 have_open_files_
= false;
696 SimpleSynchronousEntry::SimpleSynchronousEntry(net::CacheType cache_type
,
697 const FilePath
& path
,
698 const std::string
& key
,
699 const uint64 entry_hash
)
700 : cache_type_(cache_type
),
702 entry_hash_(entry_hash
),
704 have_open_files_(false),
705 initialized_(false) {
706 for (int i
= 0; i
< kSimpleEntryFileCount
; ++i
)
707 empty_file_omitted_
[i
] = false;
710 SimpleSynchronousEntry::~SimpleSynchronousEntry() {
711 DCHECK(!(have_open_files_
&& initialized_
));
712 if (have_open_files_
)
716 bool SimpleSynchronousEntry::MaybeOpenFile(
718 File::Error
* out_error
) {
721 FilePath filename
= GetFilenameFromFileIndex(file_index
);
722 int flags
= File::FLAG_OPEN
| File::FLAG_READ
| File::FLAG_WRITE
;
723 files_
[file_index
].Initialize(filename
, flags
);
724 *out_error
= files_
[file_index
].error_details();
726 if (CanOmitEmptyFile(file_index
) && !files_
[file_index
].IsValid() &&
727 *out_error
== File::FILE_ERROR_NOT_FOUND
) {
728 empty_file_omitted_
[file_index
] = true;
732 return files_
[file_index
].IsValid();
735 bool SimpleSynchronousEntry::MaybeCreateFile(
737 FileRequired file_required
,
738 File::Error
* out_error
) {
741 if (CanOmitEmptyFile(file_index
) && file_required
== FILE_NOT_REQUIRED
) {
742 empty_file_omitted_
[file_index
] = true;
746 FilePath filename
= GetFilenameFromFileIndex(file_index
);
747 int flags
= File::FLAG_CREATE
| File::FLAG_READ
| File::FLAG_WRITE
;
748 files_
[file_index
].Initialize(filename
, flags
);
749 *out_error
= files_
[file_index
].error_details();
751 empty_file_omitted_
[file_index
] = false;
753 return files_
[file_index
].IsValid();
756 bool SimpleSynchronousEntry::OpenFiles(
758 SimpleEntryStat
* out_entry_stat
) {
759 for (int i
= 0; i
< kSimpleEntryFileCount
; ++i
) {
761 if (!MaybeOpenFile(i
, &error
)) {
762 // TODO(ttuttle,gavinp): Remove one each of these triplets of histograms.
763 // We can calculate the third as the sum or difference of the other two.
764 RecordSyncOpenResult(
765 cache_type_
, OPEN_ENTRY_PLATFORM_FILE_ERROR
, had_index
);
766 SIMPLE_CACHE_UMA(ENUMERATION
,
767 "SyncOpenPlatformFileError", cache_type_
,
768 -error
, -base::File::FILE_ERROR_MAX
);
770 SIMPLE_CACHE_UMA(ENUMERATION
,
771 "SyncOpenPlatformFileError_WithIndex", cache_type_
,
772 -error
, -base::File::FILE_ERROR_MAX
);
774 SIMPLE_CACHE_UMA(ENUMERATION
,
775 "SyncOpenPlatformFileError_WithoutIndex",
777 -error
, -base::File::FILE_ERROR_MAX
);
785 have_open_files_
= true;
787 base::TimeDelta entry_age
= base::Time::Now() - base::Time::UnixEpoch();
788 for (int i
= 0; i
< kSimpleEntryFileCount
; ++i
) {
789 if (empty_file_omitted_
[i
]) {
790 out_entry_stat
->set_data_size(i
+ 1, 0);
794 File::Info file_info
;
795 bool success
= files_
[i
].GetInfo(&file_info
);
796 base::Time file_last_modified
;
798 DLOG(WARNING
) << "Could not get platform file info.";
801 out_entry_stat
->set_last_used(file_info
.last_accessed
);
802 if (simple_util::GetMTime(path_
, &file_last_modified
))
803 out_entry_stat
->set_last_modified(file_last_modified
);
805 out_entry_stat
->set_last_modified(file_info
.last_modified
);
807 base::TimeDelta stream_age
=
808 base::Time::Now() - out_entry_stat
->last_modified();
809 if (stream_age
< entry_age
)
810 entry_age
= stream_age
;
812 // Two things prevent from knowing the right values for |data_size|:
813 // 1) The key is not known, hence its length is unknown.
814 // 2) Stream 0 and stream 1 are in the same file, and the exact size for
815 // each will only be known when reading the EOF record for stream 0.
817 // The size for file 0 and 1 is temporarily kept in
818 // |data_size(1)| and |data_size(2)| respectively. Reading the key in
819 // InitializeForOpen yields the data size for each file. In the case of
820 // file hash_1, this is the total size of stream 2, and is assigned to
821 // data_size(2). In the case of file 0, it is the combined size of stream
822 // 0, stream 1 and one EOF record. The exact distribution of sizes between
823 // stream 1 and stream 0 is only determined after reading the EOF record
824 // for stream 0 in ReadAndValidateStream0.
825 out_entry_stat
->set_data_size(i
+ 1, static_cast<int>(file_info
.size
));
827 SIMPLE_CACHE_UMA(CUSTOM_COUNTS
,
828 "SyncOpenEntryAge", cache_type_
,
829 entry_age
.InHours(), 1, 1000, 50);
831 files_created_
= false;
836 bool SimpleSynchronousEntry::CreateFiles(
838 SimpleEntryStat
* out_entry_stat
) {
839 for (int i
= 0; i
< kSimpleEntryFileCount
; ++i
) {
841 if (!MaybeCreateFile(i
, FILE_NOT_REQUIRED
, &error
)) {
842 // TODO(ttuttle,gavinp): Remove one each of these triplets of histograms.
843 // We can calculate the third as the sum or difference of the other two.
844 RecordSyncCreateResult(CREATE_ENTRY_PLATFORM_FILE_ERROR
, had_index
);
845 SIMPLE_CACHE_UMA(ENUMERATION
,
846 "SyncCreatePlatformFileError", cache_type_
,
847 -error
, -base::File::FILE_ERROR_MAX
);
849 SIMPLE_CACHE_UMA(ENUMERATION
,
850 "SyncCreatePlatformFileError_WithIndex", cache_type_
,
851 -error
, -base::File::FILE_ERROR_MAX
);
853 SIMPLE_CACHE_UMA(ENUMERATION
,
854 "SyncCreatePlatformFileError_WithoutIndex",
856 -error
, -base::File::FILE_ERROR_MAX
);
864 have_open_files_
= true;
866 base::Time creation_time
= Time::Now();
867 out_entry_stat
->set_last_modified(creation_time
);
868 out_entry_stat
->set_last_used(creation_time
);
869 for (int i
= 0; i
< kSimpleEntryStreamCount
; ++i
)
870 out_entry_stat
->set_data_size(i
, 0);
872 files_created_
= true;
877 void SimpleSynchronousEntry::CloseFile(int index
) {
878 if (empty_file_omitted_
[index
]) {
879 empty_file_omitted_
[index
] = false;
881 DCHECK(files_
[index
].IsValid());
882 files_
[index
].Close();
885 if (sparse_file_open())
889 void SimpleSynchronousEntry::CloseFiles() {
890 for (int i
= 0; i
< kSimpleEntryFileCount
; ++i
)
894 int SimpleSynchronousEntry::InitializeForOpen(
896 SimpleEntryStat
* out_entry_stat
,
897 scoped_refptr
<net::GrowableIOBuffer
>* stream_0_data
,
898 uint32
* out_stream_0_crc32
) {
899 DCHECK(!initialized_
);
900 if (!OpenFiles(had_index
, out_entry_stat
)) {
901 DLOG(WARNING
) << "Could not open platform files for entry.";
902 return net::ERR_FAILED
;
904 for (int i
= 0; i
< kSimpleEntryFileCount
; ++i
) {
905 if (empty_file_omitted_
[i
])
908 SimpleFileHeader header
;
909 int header_read_result
=
910 files_
[i
].Read(0, reinterpret_cast<char*>(&header
), sizeof(header
));
911 if (header_read_result
!= sizeof(header
)) {
912 DLOG(WARNING
) << "Cannot read header from entry.";
913 RecordSyncOpenResult(cache_type_
, OPEN_ENTRY_CANT_READ_HEADER
, had_index
);
914 return net::ERR_FAILED
;
917 if (header
.initial_magic_number
!= kSimpleInitialMagicNumber
) {
918 // TODO(gavinp): This seems very bad; for now we log at WARNING, but we
919 // should give consideration to not saturating the log with these if that
920 // becomes a problem.
921 DLOG(WARNING
) << "Magic number did not match.";
922 RecordSyncOpenResult(cache_type_
, OPEN_ENTRY_BAD_MAGIC_NUMBER
, had_index
);
923 return net::ERR_FAILED
;
926 if (header
.version
!= kSimpleEntryVersionOnDisk
) {
927 DLOG(WARNING
) << "Unreadable version.";
928 RecordSyncOpenResult(cache_type_
, OPEN_ENTRY_BAD_VERSION
, had_index
);
929 return net::ERR_FAILED
;
932 scoped_ptr
<char[]> key(new char[header
.key_length
]);
933 int key_read_result
= files_
[i
].Read(sizeof(header
), key
.get(),
935 if (key_read_result
!= implicit_cast
<int>(header
.key_length
)) {
936 DLOG(WARNING
) << "Cannot read key from entry.";
937 RecordSyncOpenResult(cache_type_
, OPEN_ENTRY_CANT_READ_KEY
, had_index
);
938 return net::ERR_FAILED
;
941 key_
= std::string(key
.get(), header
.key_length
);
943 // File size for stream 0 has been stored temporarily in data_size[1].
944 int total_data_size
=
945 GetDataSizeFromKeyAndFileSize(key_
, out_entry_stat
->data_size(1));
946 int ret_value_stream_0
= ReadAndValidateStream0(
947 total_data_size
, out_entry_stat
, stream_0_data
, out_stream_0_crc32
);
948 if (ret_value_stream_0
!= net::OK
)
949 return ret_value_stream_0
;
951 out_entry_stat
->set_data_size(
952 2, GetDataSizeFromKeyAndFileSize(key_
, out_entry_stat
->data_size(2)));
953 if (out_entry_stat
->data_size(2) < 0) {
954 DLOG(WARNING
) << "Stream 2 file is too small.";
955 return net::ERR_FAILED
;
959 if (base::Hash(key
.get(), header
.key_length
) != header
.key_hash
) {
960 DLOG(WARNING
) << "Hash mismatch on key.";
961 RecordSyncOpenResult(
962 cache_type_
, OPEN_ENTRY_KEY_HASH_MISMATCH
, had_index
);
963 return net::ERR_FAILED
;
967 int32 sparse_data_size
= 0;
968 if (!OpenSparseFileIfExists(&sparse_data_size
)) {
969 RecordSyncOpenResult(
970 cache_type_
, OPEN_ENTRY_SPARSE_OPEN_FAILED
, had_index
);
971 return net::ERR_FAILED
;
973 out_entry_stat
->set_sparse_data_size(sparse_data_size
);
975 bool removed_stream2
= false;
976 const int stream2_file_index
= GetFileIndexFromStreamIndex(2);
977 DCHECK(CanOmitEmptyFile(stream2_file_index
));
978 if (!empty_file_omitted_
[stream2_file_index
] &&
979 out_entry_stat
->data_size(2) == 0) {
980 DVLOG(1) << "Removing empty stream 2 file.";
981 CloseFile(stream2_file_index
);
982 DeleteFileForEntryHash(path_
, entry_hash_
, stream2_file_index
);
983 empty_file_omitted_
[stream2_file_index
] = true;
984 removed_stream2
= true;
987 SIMPLE_CACHE_UMA(BOOLEAN
, "EntryOpenedAndStream2Removed", cache_type_
,
990 RecordSyncOpenResult(cache_type_
, OPEN_ENTRY_SUCCESS
, had_index
);
995 bool SimpleSynchronousEntry::InitializeCreatedFile(
997 CreateEntryResult
* out_result
) {
998 SimpleFileHeader header
;
999 header
.initial_magic_number
= kSimpleInitialMagicNumber
;
1000 header
.version
= kSimpleEntryVersionOnDisk
;
1002 header
.key_length
= key_
.size();
1003 header
.key_hash
= base::Hash(key_
);
1005 int bytes_written
= files_
[file_index
].Write(
1006 0, reinterpret_cast<char*>(&header
), sizeof(header
));
1007 if (bytes_written
!= sizeof(header
)) {
1008 *out_result
= CREATE_ENTRY_CANT_WRITE_HEADER
;
1012 bytes_written
= files_
[file_index
].Write(sizeof(header
), key_
.data(),
1014 if (bytes_written
!= implicit_cast
<int>(key_
.size())) {
1015 *out_result
= CREATE_ENTRY_CANT_WRITE_KEY
;
1022 int SimpleSynchronousEntry::InitializeForCreate(
1024 SimpleEntryStat
* out_entry_stat
) {
1025 DCHECK(!initialized_
);
1026 if (!CreateFiles(had_index
, out_entry_stat
)) {
1027 DLOG(WARNING
) << "Could not create platform files.";
1028 return net::ERR_FILE_EXISTS
;
1030 for (int i
= 0; i
< kSimpleEntryFileCount
; ++i
) {
1031 if (empty_file_omitted_
[i
])
1034 CreateEntryResult result
;
1035 if (!InitializeCreatedFile(i
, &result
)) {
1036 RecordSyncCreateResult(result
, had_index
);
1037 return net::ERR_FAILED
;
1040 RecordSyncCreateResult(CREATE_ENTRY_SUCCESS
, had_index
);
1041 initialized_
= true;
1045 int SimpleSynchronousEntry::ReadAndValidateStream0(
1046 int total_data_size
,
1047 SimpleEntryStat
* out_entry_stat
,
1048 scoped_refptr
<net::GrowableIOBuffer
>* stream_0_data
,
1049 uint32
* out_stream_0_crc32
) const {
1050 // Temporarily assign all the data size to stream 1 in order to read the
1051 // EOF record for stream 0, which contains the size of stream 0.
1052 out_entry_stat
->set_data_size(0, 0);
1053 out_entry_stat
->set_data_size(1, total_data_size
- sizeof(SimpleFileEOF
));
1058 int ret_value_crc32
= GetEOFRecordData(
1059 0, *out_entry_stat
, &has_crc32
, &read_crc32
, &stream_0_size
);
1060 if (ret_value_crc32
!= net::OK
)
1061 return ret_value_crc32
;
1063 if (stream_0_size
> out_entry_stat
->data_size(1))
1064 return net::ERR_FAILED
;
1066 // These are the real values of data size.
1067 out_entry_stat
->set_data_size(0, stream_0_size
);
1068 out_entry_stat
->set_data_size(
1069 1, out_entry_stat
->data_size(1) - stream_0_size
);
1071 // Put stream 0 data in memory.
1072 *stream_0_data
= new net::GrowableIOBuffer();
1073 (*stream_0_data
)->SetCapacity(stream_0_size
);
1074 int file_offset
= out_entry_stat
->GetOffsetInFile(key_
, 0, 0);
1075 File
* file
= const_cast<File
*>(&files_
[0]);
1077 file
->Read(file_offset
, (*stream_0_data
)->data(), stream_0_size
);
1078 if (bytes_read
!= stream_0_size
)
1079 return net::ERR_FAILED
;
1082 uint32 expected_crc32
=
1084 ? crc32(0, Z_NULL
, 0)
1085 : crc32(crc32(0, Z_NULL
, 0),
1086 reinterpret_cast<const Bytef
*>((*stream_0_data
)->data()),
1088 if (has_crc32
&& read_crc32
!= expected_crc32
) {
1089 DVLOG(1) << "EOF record had bad crc.";
1090 RecordCheckEOFResult(cache_type_
, CHECK_EOF_RESULT_CRC_MISMATCH
);
1091 return net::ERR_FAILED
;
1093 *out_stream_0_crc32
= expected_crc32
;
1094 RecordCheckEOFResult(cache_type_
, CHECK_EOF_RESULT_SUCCESS
);
1098 int SimpleSynchronousEntry::GetEOFRecordData(int index
,
1099 const SimpleEntryStat
& entry_stat
,
1100 bool* out_has_crc32
,
1102 int* out_data_size
) const {
1103 SimpleFileEOF eof_record
;
1104 int file_offset
= entry_stat
.GetEOFOffsetInFile(key_
, index
);
1105 int file_index
= GetFileIndexFromStreamIndex(index
);
1106 File
* file
= const_cast<File
*>(&files_
[file_index
]);
1107 if (file
->Read(file_offset
, reinterpret_cast<char*>(&eof_record
),
1108 sizeof(eof_record
)) !=
1109 sizeof(eof_record
)) {
1110 RecordCheckEOFResult(cache_type_
, CHECK_EOF_RESULT_READ_FAILURE
);
1111 return net::ERR_CACHE_CHECKSUM_READ_FAILURE
;
1114 if (eof_record
.final_magic_number
!= kSimpleFinalMagicNumber
) {
1115 RecordCheckEOFResult(cache_type_
, CHECK_EOF_RESULT_MAGIC_NUMBER_MISMATCH
);
1116 DVLOG(1) << "EOF record had bad magic number.";
1117 return net::ERR_CACHE_CHECKSUM_READ_FAILURE
;
1120 *out_has_crc32
= (eof_record
.flags
& SimpleFileEOF::FLAG_HAS_CRC32
) ==
1121 SimpleFileEOF::FLAG_HAS_CRC32
;
1122 *out_crc32
= eof_record
.data_crc32
;
1123 *out_data_size
= eof_record
.stream_size
;
1124 SIMPLE_CACHE_UMA(BOOLEAN
, "SyncCheckEOFHasCrc", cache_type_
, *out_has_crc32
);
1128 void SimpleSynchronousEntry::Doom() const {
1129 DeleteFilesForEntryHash(path_
, entry_hash_
);
1133 bool SimpleSynchronousEntry::DeleteFileForEntryHash(
1134 const FilePath
& path
,
1135 const uint64 entry_hash
,
1136 const int file_index
) {
1137 FilePath to_delete
= path
.AppendASCII(
1138 GetFilenameFromEntryHashAndFileIndex(entry_hash
, file_index
));
1139 return base::DeleteFile(to_delete
, false);
1143 bool SimpleSynchronousEntry::DeleteFilesForEntryHash(
1144 const FilePath
& path
,
1145 const uint64 entry_hash
) {
1147 for (int i
= 0; i
< kSimpleEntryFileCount
; ++i
) {
1148 if (!DeleteFileForEntryHash(path
, entry_hash
, i
) && !CanOmitEmptyFile(i
))
1151 FilePath to_delete
= path
.AppendASCII(
1152 GetSparseFilenameFromEntryHash(entry_hash
));
1153 base::DeleteFile(to_delete
, false);
1157 void SimpleSynchronousEntry::RecordSyncCreateResult(CreateEntryResult result
,
1159 DCHECK_LT(result
, CREATE_ENTRY_MAX
);
1160 SIMPLE_CACHE_UMA(ENUMERATION
,
1161 "SyncCreateResult", cache_type_
, result
, CREATE_ENTRY_MAX
);
1163 SIMPLE_CACHE_UMA(ENUMERATION
,
1164 "SyncCreateResult_WithIndex", cache_type_
,
1165 result
, CREATE_ENTRY_MAX
);
1167 SIMPLE_CACHE_UMA(ENUMERATION
,
1168 "SyncCreateResult_WithoutIndex", cache_type_
,
1169 result
, CREATE_ENTRY_MAX
);
1173 FilePath
SimpleSynchronousEntry::GetFilenameFromFileIndex(int file_index
) {
1174 return path_
.AppendASCII(
1175 GetFilenameFromEntryHashAndFileIndex(entry_hash_
, file_index
));
1178 bool SimpleSynchronousEntry::OpenSparseFileIfExists(
1179 int32
* out_sparse_data_size
) {
1180 DCHECK(!sparse_file_open());
1182 FilePath filename
= path_
.AppendASCII(
1183 GetSparseFilenameFromEntryHash(entry_hash_
));
1184 int flags
= File::FLAG_OPEN
| File::FLAG_READ
| File::FLAG_WRITE
;
1185 sparse_file_
.Initialize(filename
, flags
);
1186 if (sparse_file_
.IsValid())
1187 return ScanSparseFile(out_sparse_data_size
);
1189 return sparse_file_
.error_details() == File::FILE_ERROR_NOT_FOUND
;
1192 bool SimpleSynchronousEntry::CreateSparseFile() {
1193 DCHECK(!sparse_file_open());
1195 FilePath filename
= path_
.AppendASCII(
1196 GetSparseFilenameFromEntryHash(entry_hash_
));
1197 int flags
= File::FLAG_CREATE
| File::FLAG_READ
| File::FLAG_WRITE
;
1198 sparse_file_
.Initialize(filename
, flags
);
1199 if (!sparse_file_
.IsValid())
1202 return InitializeSparseFile();
1205 void SimpleSynchronousEntry::CloseSparseFile() {
1206 DCHECK(sparse_file_open());
1207 sparse_file_
.Close();
1210 bool SimpleSynchronousEntry::TruncateSparseFile() {
1211 DCHECK(sparse_file_open());
1213 int64 header_and_key_length
= sizeof(SimpleFileHeader
) + key_
.size();
1214 if (!sparse_file_
.SetLength(header_and_key_length
)) {
1215 DLOG(WARNING
) << "Could not truncate sparse file";
1219 sparse_ranges_
.clear();
1224 bool SimpleSynchronousEntry::InitializeSparseFile() {
1225 DCHECK(sparse_file_open());
1227 SimpleFileHeader header
;
1228 header
.initial_magic_number
= kSimpleInitialMagicNumber
;
1229 header
.version
= kSimpleVersion
;
1230 header
.key_length
= key_
.size();
1231 header
.key_hash
= base::Hash(key_
);
1233 int header_write_result
=
1234 sparse_file_
.Write(0, reinterpret_cast<char*>(&header
), sizeof(header
));
1235 if (header_write_result
!= sizeof(header
)) {
1236 DLOG(WARNING
) << "Could not write sparse file header";
1240 int key_write_result
= sparse_file_
.Write(sizeof(header
), key_
.data(),
1242 if (key_write_result
!= implicit_cast
<int>(key_
.size())) {
1243 DLOG(WARNING
) << "Could not write sparse file key";
1247 sparse_ranges_
.clear();
1248 sparse_tail_offset_
= sizeof(header
) + key_
.size();
1253 bool SimpleSynchronousEntry::ScanSparseFile(int32
* out_sparse_data_size
) {
1254 DCHECK(sparse_file_open());
1256 int64 sparse_data_size
= 0;
1258 SimpleFileHeader header
;
1259 int header_read_result
=
1260 sparse_file_
.Read(0, reinterpret_cast<char*>(&header
), sizeof(header
));
1261 if (header_read_result
!= sizeof(header
)) {
1262 DLOG(WARNING
) << "Could not read header from sparse file.";
1266 if (header
.initial_magic_number
!= kSimpleInitialMagicNumber
) {
1267 DLOG(WARNING
) << "Sparse file magic number did not match.";
1271 if (header
.version
!= kSimpleVersion
) {
1272 DLOG(WARNING
) << "Sparse file unreadable version.";
1276 sparse_ranges_
.clear();
1278 int64 range_header_offset
= sizeof(header
) + key_
.size();
1280 SimpleFileSparseRangeHeader range_header
;
1281 int range_header_read_result
=
1282 sparse_file_
.Read(range_header_offset
,
1283 reinterpret_cast<char*>(&range_header
),
1284 sizeof(range_header
));
1285 if (range_header_read_result
== 0)
1287 if (range_header_read_result
!= sizeof(range_header
)) {
1288 DLOG(WARNING
) << "Could not read sparse range header.";
1292 if (range_header
.sparse_range_magic_number
!=
1293 kSimpleSparseRangeMagicNumber
) {
1294 DLOG(WARNING
) << "Invalid sparse range header magic number.";
1299 range
.offset
= range_header
.offset
;
1300 range
.length
= range_header
.length
;
1301 range
.data_crc32
= range_header
.data_crc32
;
1302 range
.file_offset
= range_header_offset
+ sizeof(range_header
);
1303 sparse_ranges_
.insert(std::make_pair(range
.offset
, range
));
1305 range_header_offset
+= sizeof(range_header
) + range
.length
;
1307 DCHECK_GE(sparse_data_size
+ range
.length
, sparse_data_size
);
1308 sparse_data_size
+= range
.length
;
1311 *out_sparse_data_size
= static_cast<int32
>(sparse_data_size
);
1312 sparse_tail_offset_
= range_header_offset
;
1317 bool SimpleSynchronousEntry::ReadSparseRange(const SparseRange
* range
,
1318 int offset
, int len
, char* buf
) {
1321 DCHECK_LE(offset
, range
->length
);
1322 DCHECK_LE(offset
+ len
, range
->length
);
1324 int bytes_read
= sparse_file_
.Read(range
->file_offset
+ offset
, buf
, len
);
1325 if (bytes_read
< len
) {
1326 DLOG(WARNING
) << "Could not read sparse range.";
1330 // If we read the whole range and we have a crc32, check it.
1331 if (offset
== 0 && len
== range
->length
&& range
->data_crc32
!= 0) {
1332 uint32 actual_crc32
= crc32(crc32(0L, Z_NULL
, 0),
1333 reinterpret_cast<const Bytef
*>(buf
),
1335 if (actual_crc32
!= range
->data_crc32
) {
1336 DLOG(WARNING
) << "Sparse range crc32 mismatch.";
1340 // TODO(ttuttle): Incremental crc32 calculation?
1345 bool SimpleSynchronousEntry::WriteSparseRange(SparseRange
* range
,
1346 int offset
, int len
,
1350 DCHECK_LE(offset
, range
->length
);
1351 DCHECK_LE(offset
+ len
, range
->length
);
1353 uint32 new_crc32
= 0;
1354 if (offset
== 0 && len
== range
->length
) {
1355 new_crc32
= crc32(crc32(0L, Z_NULL
, 0),
1356 reinterpret_cast<const Bytef
*>(buf
),
1360 if (new_crc32
!= range
->data_crc32
) {
1361 range
->data_crc32
= new_crc32
;
1363 SimpleFileSparseRangeHeader header
;
1364 header
.sparse_range_magic_number
= kSimpleSparseRangeMagicNumber
;
1365 header
.offset
= range
->offset
;
1366 header
.length
= range
->length
;
1367 header
.data_crc32
= range
->data_crc32
;
1369 int bytes_written
= sparse_file_
.Write(range
->file_offset
- sizeof(header
),
1370 reinterpret_cast<char*>(&header
),
1372 if (bytes_written
!= implicit_cast
<int>(sizeof(header
))) {
1373 DLOG(WARNING
) << "Could not rewrite sparse range header.";
1378 int bytes_written
= sparse_file_
.Write(range
->file_offset
+ offset
, buf
, len
);
1379 if (bytes_written
< len
) {
1380 DLOG(WARNING
) << "Could not write sparse range.";
1387 bool SimpleSynchronousEntry::AppendSparseRange(int64 offset
,
1390 DCHECK_GE(offset
, 0);
1394 uint32 data_crc32
= crc32(crc32(0L, Z_NULL
, 0),
1395 reinterpret_cast<const Bytef
*>(buf
),
1398 SimpleFileSparseRangeHeader header
;
1399 header
.sparse_range_magic_number
= kSimpleSparseRangeMagicNumber
;
1400 header
.offset
= offset
;
1401 header
.length
= len
;
1402 header
.data_crc32
= data_crc32
;
1404 int bytes_written
= sparse_file_
.Write(sparse_tail_offset_
,
1405 reinterpret_cast<char*>(&header
),
1407 if (bytes_written
!= implicit_cast
<int>(sizeof(header
))) {
1408 DLOG(WARNING
) << "Could not append sparse range header.";
1411 sparse_tail_offset_
+= bytes_written
;
1413 bytes_written
= sparse_file_
.Write(sparse_tail_offset_
, buf
, len
);
1414 if (bytes_written
< len
) {
1415 DLOG(WARNING
) << "Could not append sparse range data.";
1418 int64 data_file_offset
= sparse_tail_offset_
;
1419 sparse_tail_offset_
+= bytes_written
;
1422 range
.offset
= offset
;
1424 range
.data_crc32
= data_crc32
;
1425 range
.file_offset
= data_file_offset
;
1426 sparse_ranges_
.insert(std::make_pair(offset
, range
));
1431 } // namespace disk_cache