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/file_util.h"
15 #include "base/hash.h"
16 #include "base/location.h"
17 #include "base/sha1.h"
18 #include "base/strings/stringprintf.h"
19 #include "net/base/io_buffer.h"
20 #include "net/base/net_errors.h"
21 #include "net/disk_cache/simple/simple_backend_version.h"
22 #include "net/disk_cache/simple/simple_histogram_macros.h"
23 #include "net/disk_cache/simple/simple_util.h"
24 #include "third_party/zlib/zlib.h"
32 // Used in histograms, please only add entries at the end.
33 enum OpenEntryResult
{
34 OPEN_ENTRY_SUCCESS
= 0,
35 OPEN_ENTRY_PLATFORM_FILE_ERROR
= 1,
36 OPEN_ENTRY_CANT_READ_HEADER
= 2,
37 OPEN_ENTRY_BAD_MAGIC_NUMBER
= 3,
38 OPEN_ENTRY_BAD_VERSION
= 4,
39 OPEN_ENTRY_CANT_READ_KEY
= 5,
40 // OPEN_ENTRY_KEY_MISMATCH = 6, Deprecated.
41 OPEN_ENTRY_KEY_HASH_MISMATCH
= 7,
42 OPEN_ENTRY_SPARSE_OPEN_FAILED
= 8,
46 // Used in histograms, please only add entries at the end.
48 WRITE_RESULT_SUCCESS
= 0,
49 WRITE_RESULT_PRETRUNCATE_FAILURE
,
50 WRITE_RESULT_WRITE_FAILURE
,
51 WRITE_RESULT_TRUNCATE_FAILURE
,
52 WRITE_RESULT_LAZY_STREAM_ENTRY_DOOMED
,
53 WRITE_RESULT_LAZY_CREATE_FAILURE
,
54 WRITE_RESULT_LAZY_INITIALIZE_FAILURE
,
58 // Used in histograms, please only add entries at the end.
60 CHECK_EOF_RESULT_SUCCESS
,
61 CHECK_EOF_RESULT_READ_FAILURE
,
62 CHECK_EOF_RESULT_MAGIC_NUMBER_MISMATCH
,
63 CHECK_EOF_RESULT_CRC_MISMATCH
,
67 // Used in histograms, please only add entries at the end.
70 CLOSE_RESULT_WRITE_FAILURE
,
73 void RecordSyncOpenResult(net::CacheType cache_type
,
74 OpenEntryResult result
,
76 DCHECK_GT(OPEN_ENTRY_MAX
, result
);
77 SIMPLE_CACHE_UMA(ENUMERATION
,
78 "SyncOpenResult", cache_type
, result
, OPEN_ENTRY_MAX
);
80 SIMPLE_CACHE_UMA(ENUMERATION
,
81 "SyncOpenResult_WithIndex", cache_type
,
82 result
, OPEN_ENTRY_MAX
);
84 SIMPLE_CACHE_UMA(ENUMERATION
,
85 "SyncOpenResult_WithoutIndex", cache_type
,
86 result
, OPEN_ENTRY_MAX
);
90 void RecordWriteResult(net::CacheType cache_type
, WriteResult result
) {
91 SIMPLE_CACHE_UMA(ENUMERATION
,
92 "SyncWriteResult", cache_type
, result
, WRITE_RESULT_MAX
);
95 void RecordCheckEOFResult(net::CacheType cache_type
, CheckEOFResult result
) {
96 SIMPLE_CACHE_UMA(ENUMERATION
,
97 "SyncCheckEOFResult", cache_type
,
98 result
, CHECK_EOF_RESULT_MAX
);
101 void RecordCloseResult(net::CacheType cache_type
, CloseResult result
) {
102 SIMPLE_CACHE_UMA(ENUMERATION
,
103 "SyncCloseResult", cache_type
, result
, WRITE_RESULT_MAX
);
106 bool CanOmitEmptyFile(int file_index
) {
107 DCHECK_LE(0, file_index
);
108 DCHECK_GT(disk_cache::kSimpleEntryFileCount
, file_index
);
109 return file_index
== disk_cache::simple_util::GetFileIndexFromStreamIndex(2);
114 namespace disk_cache
{
116 using simple_util::GetEntryHashKey
;
117 using simple_util::GetFilenameFromEntryHashAndFileIndex
;
118 using simple_util::GetSparseFilenameFromEntryHash
;
119 using simple_util::GetDataSizeFromKeyAndFileSize
;
120 using simple_util::GetFileSizeFromKeyAndDataSize
;
121 using simple_util::GetFileIndexFromStreamIndex
;
123 SimpleEntryStat::SimpleEntryStat(base::Time last_used
,
124 base::Time last_modified
,
125 const int32 data_size
[],
126 const int32 sparse_data_size
)
127 : last_used_(last_used
),
128 last_modified_(last_modified
),
129 sparse_data_size_(sparse_data_size
) {
130 memcpy(data_size_
, data_size
, sizeof(data_size_
));
133 int SimpleEntryStat::GetOffsetInFile(const std::string
& key
,
135 int stream_index
) const {
136 const int64 headers_size
= sizeof(SimpleFileHeader
) + key
.size();
137 const int64 additional_offset
=
138 stream_index
== 0 ? data_size_
[1] + sizeof(SimpleFileEOF
) : 0;
139 return headers_size
+ offset
+ additional_offset
;
142 int SimpleEntryStat::GetEOFOffsetInFile(const std::string
& key
,
143 int stream_index
) const {
144 return GetOffsetInFile(key
, data_size_
[stream_index
], stream_index
);
147 int SimpleEntryStat::GetLastEOFOffsetInFile(const std::string
& key
,
148 int stream_index
) const {
149 const int file_index
= GetFileIndexFromStreamIndex(stream_index
);
150 const int eof_data_offset
=
151 file_index
== 0 ? data_size_
[0] + data_size_
[1] + sizeof(SimpleFileEOF
)
153 return GetOffsetInFile(key
, eof_data_offset
, stream_index
);
156 int SimpleEntryStat::GetFileSize(const std::string
& key
, int file_index
) const {
157 const int total_data_size
=
158 file_index
== 0 ? data_size_
[0] + data_size_
[1] + sizeof(SimpleFileEOF
)
160 return GetFileSizeFromKeyAndDataSize(key
, total_data_size
);
163 SimpleEntryCreationResults::SimpleEntryCreationResults(
164 SimpleEntryStat entry_stat
)
166 entry_stat(entry_stat
),
167 stream_0_crc32(crc32(0, Z_NULL
, 0)),
171 SimpleEntryCreationResults::~SimpleEntryCreationResults() {
174 SimpleSynchronousEntry::CRCRecord::CRCRecord() : index(-1),
179 SimpleSynchronousEntry::CRCRecord::CRCRecord(int index_p
,
183 has_crc32(has_crc32_p
),
184 data_crc32(data_crc32_p
) {}
186 SimpleSynchronousEntry::EntryOperationData::EntryOperationData(int index_p
,
191 buf_len(buf_len_p
) {}
193 SimpleSynchronousEntry::EntryOperationData::EntryOperationData(int index_p
,
201 truncate(truncate_p
),
204 SimpleSynchronousEntry::EntryOperationData::EntryOperationData(
205 int64 sparse_offset_p
,
207 : sparse_offset(sparse_offset_p
),
208 buf_len(buf_len_p
) {}
211 void SimpleSynchronousEntry::OpenEntry(
212 net::CacheType cache_type
,
213 const FilePath
& path
,
214 const uint64 entry_hash
,
216 SimpleEntryCreationResults
*out_results
) {
217 SimpleSynchronousEntry
* sync_entry
=
218 new SimpleSynchronousEntry(cache_type
, path
, "", entry_hash
);
219 out_results
->result
=
220 sync_entry
->InitializeForOpen(had_index
,
221 &out_results
->entry_stat
,
222 &out_results
->stream_0_data
,
223 &out_results
->stream_0_crc32
);
224 if (out_results
->result
!= net::OK
) {
227 out_results
->sync_entry
= NULL
;
228 out_results
->stream_0_data
= NULL
;
231 out_results
->sync_entry
= sync_entry
;
235 void SimpleSynchronousEntry::CreateEntry(
236 net::CacheType cache_type
,
237 const FilePath
& path
,
238 const std::string
& key
,
239 const uint64 entry_hash
,
241 SimpleEntryCreationResults
*out_results
) {
242 DCHECK_EQ(entry_hash
, GetEntryHashKey(key
));
243 SimpleSynchronousEntry
* sync_entry
=
244 new SimpleSynchronousEntry(cache_type
, path
, key
, entry_hash
);
245 out_results
->result
= sync_entry
->InitializeForCreate(
246 had_index
, &out_results
->entry_stat
);
247 if (out_results
->result
!= net::OK
) {
248 if (out_results
->result
!= net::ERR_FILE_EXISTS
)
251 out_results
->sync_entry
= NULL
;
254 out_results
->sync_entry
= sync_entry
;
258 int SimpleSynchronousEntry::DoomEntry(
259 const FilePath
& path
,
261 const bool deleted_well
= DeleteFilesForEntryHash(path
, entry_hash
);
262 return deleted_well
? net::OK
: net::ERR_FAILED
;
266 int SimpleSynchronousEntry::DoomEntrySet(
267 const std::vector
<uint64
>* key_hashes
,
268 const FilePath
& path
) {
269 const size_t did_delete_count
= std::count_if(
270 key_hashes
->begin(), key_hashes
->end(), std::bind1st(
271 std::ptr_fun(SimpleSynchronousEntry::DeleteFilesForEntryHash
), path
));
272 return (did_delete_count
== key_hashes
->size()) ? net::OK
: net::ERR_FAILED
;
275 void SimpleSynchronousEntry::ReadData(const EntryOperationData
& in_entry_op
,
276 net::IOBuffer
* out_buf
,
278 SimpleEntryStat
* entry_stat
,
279 int* out_result
) const {
280 DCHECK(initialized_
);
281 DCHECK_NE(0, in_entry_op
.index
);
282 const int64 file_offset
=
283 entry_stat
->GetOffsetInFile(key_
, in_entry_op
.offset
, in_entry_op
.index
);
284 int file_index
= GetFileIndexFromStreamIndex(in_entry_op
.index
);
285 // Zero-length reads and reads to the empty streams of omitted files should
286 // be handled in the SimpleEntryImpl.
287 DCHECK_LT(0, in_entry_op
.buf_len
);
288 DCHECK(!empty_file_omitted_
[file_index
]);
289 File
* file
= const_cast<File
*>(&files_
[file_index
]);
291 file
->Read(file_offset
, out_buf
->data(), in_entry_op
.buf_len
);
292 if (bytes_read
> 0) {
293 entry_stat
->set_last_used(Time::Now());
294 *out_crc32
= crc32(crc32(0L, Z_NULL
, 0),
295 reinterpret_cast<const Bytef
*>(out_buf
->data()),
298 if (bytes_read
>= 0) {
299 *out_result
= bytes_read
;
301 *out_result
= net::ERR_CACHE_READ_FAILURE
;
306 void SimpleSynchronousEntry::WriteData(const EntryOperationData
& in_entry_op
,
307 net::IOBuffer
* in_buf
,
308 SimpleEntryStat
* out_entry_stat
,
310 DCHECK(initialized_
);
311 DCHECK_NE(0, in_entry_op
.index
);
312 int index
= in_entry_op
.index
;
313 int file_index
= GetFileIndexFromStreamIndex(index
);
314 int offset
= in_entry_op
.offset
;
315 int buf_len
= in_entry_op
.buf_len
;
316 bool truncate
= in_entry_op
.truncate
;
317 bool doomed
= in_entry_op
.doomed
;
318 const int64 file_offset
= out_entry_stat
->GetOffsetInFile(
319 key_
, in_entry_op
.offset
, in_entry_op
.index
);
320 bool extending_by_write
= offset
+ buf_len
> out_entry_stat
->data_size(index
);
322 if (empty_file_omitted_
[file_index
]) {
323 // Don't create a new file if the entry has been doomed, to avoid it being
324 // mixed up with a newly-created entry with the same key.
326 DLOG(WARNING
) << "Rejecting write to lazily omitted stream "
327 << in_entry_op
.index
<< " of doomed cache entry.";
328 RecordWriteResult(cache_type_
, WRITE_RESULT_LAZY_STREAM_ENTRY_DOOMED
);
329 *out_result
= net::ERR_CACHE_WRITE_FAILURE
;
333 if (!MaybeCreateFile(file_index
, FILE_REQUIRED
, &error
)) {
334 RecordWriteResult(cache_type_
, WRITE_RESULT_LAZY_CREATE_FAILURE
);
336 *out_result
= net::ERR_CACHE_WRITE_FAILURE
;
339 CreateEntryResult result
;
340 if (!InitializeCreatedFile(file_index
, &result
)) {
341 RecordWriteResult(cache_type_
, WRITE_RESULT_LAZY_INITIALIZE_FAILURE
);
343 *out_result
= net::ERR_CACHE_WRITE_FAILURE
;
347 DCHECK(!empty_file_omitted_
[file_index
]);
349 if (extending_by_write
) {
350 // The EOF record and the eventual stream afterward need to be zeroed out.
351 const int64 file_eof_offset
=
352 out_entry_stat
->GetEOFOffsetInFile(key_
, index
);
353 if (!files_
[file_index
].SetLength(file_eof_offset
)) {
354 RecordWriteResult(cache_type_
, WRITE_RESULT_PRETRUNCATE_FAILURE
);
356 *out_result
= net::ERR_CACHE_WRITE_FAILURE
;
361 if (files_
[file_index
].Write(file_offset
, in_buf
->data(), buf_len
) !=
363 RecordWriteResult(cache_type_
, WRITE_RESULT_WRITE_FAILURE
);
365 *out_result
= net::ERR_CACHE_WRITE_FAILURE
;
369 if (!truncate
&& (buf_len
> 0 || !extending_by_write
)) {
370 out_entry_stat
->set_data_size(
371 index
, std::max(out_entry_stat
->data_size(index
), offset
+ buf_len
));
373 out_entry_stat
->set_data_size(index
, offset
+ buf_len
);
374 int file_eof_offset
= out_entry_stat
->GetLastEOFOffsetInFile(key_
, index
);
375 if (!files_
[file_index
].SetLength(file_eof_offset
)) {
376 RecordWriteResult(cache_type_
, WRITE_RESULT_TRUNCATE_FAILURE
);
378 *out_result
= net::ERR_CACHE_WRITE_FAILURE
;
383 RecordWriteResult(cache_type_
, WRITE_RESULT_SUCCESS
);
384 base::Time modification_time
= Time::Now();
385 out_entry_stat
->set_last_used(modification_time
);
386 out_entry_stat
->set_last_modified(modification_time
);
387 *out_result
= buf_len
;
390 void SimpleSynchronousEntry::ReadSparseData(
391 const EntryOperationData
& in_entry_op
,
392 net::IOBuffer
* out_buf
,
393 base::Time
* out_last_used
,
395 DCHECK(initialized_
);
396 int64 offset
= in_entry_op
.sparse_offset
;
397 int buf_len
= in_entry_op
.buf_len
;
399 char* buf
= out_buf
->data();
402 // Find the first sparse range at or after the requested offset.
403 SparseRangeIterator it
= sparse_ranges_
.lower_bound(offset
);
405 if (it
!= sparse_ranges_
.begin()) {
406 // Hop back one range and read the one overlapping with the start.
408 SparseRange
* found_range
= &it
->second
;
409 DCHECK_EQ(it
->first
, found_range
->offset
);
410 if (found_range
->offset
+ found_range
->length
> offset
) {
411 DCHECK_LE(0, found_range
->length
);
412 DCHECK_GE(kint32max
, found_range
->length
);
413 DCHECK_LE(0, offset
- found_range
->offset
);
414 DCHECK_GE(kint32max
, offset
- found_range
->offset
);
415 int range_len_after_offset
= found_range
->length
-
416 (offset
- found_range
->offset
);
417 DCHECK_LE(0, range_len_after_offset
);
419 int len_to_read
= std::min(buf_len
, range_len_after_offset
);
420 if (!ReadSparseRange(found_range
,
421 offset
- found_range
->offset
,
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
= (found_range
->length
> kint32max
) ?
440 kint32max
: found_range
->length
;
441 int len_to_read
= std::min(buf_len
- read_so_far
, range_len
);
442 if (!ReadSparseRange(found_range
, 0, len_to_read
, buf
+ read_so_far
)) {
443 *out_result
= net::ERR_CACHE_READ_FAILURE
;
446 read_so_far
+= len_to_read
;
450 *out_result
= read_so_far
;
453 void SimpleSynchronousEntry::WriteSparseData(
454 const EntryOperationData
& in_entry_op
,
455 net::IOBuffer
* in_buf
,
456 int64 max_sparse_data_size
,
457 SimpleEntryStat
* out_entry_stat
,
459 DCHECK(initialized_
);
460 int64 offset
= in_entry_op
.sparse_offset
;
461 int buf_len
= in_entry_op
.buf_len
;
463 const char* buf
= in_buf
->data();
464 int written_so_far
= 0;
465 int appended_so_far
= 0;
467 if (!sparse_file_open() && !CreateSparseFile()) {
468 *out_result
= net::ERR_CACHE_WRITE_FAILURE
;
472 int64 sparse_data_size
= out_entry_stat
->sparse_data_size();
473 // This is a pessimistic estimate; it assumes the entire buffer is going to
474 // be appended as a new range, not written over existing ranges.
475 if (sparse_data_size
+ buf_len
> max_sparse_data_size
) {
476 DVLOG(1) << "Truncating sparse data file (" << sparse_data_size
<< " + "
477 << buf_len
<< " > " << max_sparse_data_size
<< ")";
478 TruncateSparseFile();
481 SparseRangeIterator it
= sparse_ranges_
.lower_bound(offset
);
483 if (it
!= sparse_ranges_
.begin()) {
485 SparseRange
* found_range
= &it
->second
;
486 if (found_range
->offset
+ found_range
->length
> offset
) {
487 DCHECK_LE(0, found_range
->length
);
488 DCHECK_GE(kint32max
, found_range
->length
);
489 DCHECK_LE(0, offset
- found_range
->offset
);
490 DCHECK_GE(kint32max
, offset
- found_range
->offset
);
491 int range_len_after_offset
= found_range
->length
-
492 (offset
- found_range
->offset
);
493 DCHECK_LE(0, range_len_after_offset
);
495 int len_to_write
= std::min(buf_len
, range_len_after_offset
);
496 if (!WriteSparseRange(found_range
,
497 offset
- found_range
->offset
,
500 *out_result
= net::ERR_CACHE_WRITE_FAILURE
;
503 written_so_far
+= len_to_write
;
508 while (written_so_far
< buf_len
&&
509 it
!= sparse_ranges_
.end() &&
510 it
->second
.offset
< offset
+ buf_len
) {
511 SparseRange
* found_range
= &it
->second
;
512 if (offset
+ written_so_far
< found_range
->offset
) {
513 int len_to_append
= found_range
->offset
- (offset
+ written_so_far
);
514 if (!AppendSparseRange(offset
+ written_so_far
,
516 buf
+ written_so_far
)) {
517 *out_result
= net::ERR_CACHE_WRITE_FAILURE
;
520 written_so_far
+= len_to_append
;
521 appended_so_far
+= len_to_append
;
523 int range_len
= (found_range
->length
> kint32max
) ?
524 kint32max
: found_range
->length
;
525 int len_to_write
= std::min(buf_len
- written_so_far
, range_len
);
526 if (!WriteSparseRange(found_range
,
529 buf
+ written_so_far
)) {
530 *out_result
= net::ERR_CACHE_WRITE_FAILURE
;
533 written_so_far
+= len_to_write
;
537 if (written_so_far
< buf_len
) {
538 int len_to_append
= buf_len
- written_so_far
;
539 if (!AppendSparseRange(offset
+ written_so_far
,
541 buf
+ written_so_far
)) {
542 *out_result
= net::ERR_CACHE_WRITE_FAILURE
;
545 written_so_far
+= len_to_append
;
546 appended_so_far
+= len_to_append
;
549 DCHECK_EQ(buf_len
, written_so_far
);
551 base::Time modification_time
= Time::Now();
552 out_entry_stat
->set_last_used(modification_time
);
553 out_entry_stat
->set_last_modified(modification_time
);
554 int32 old_sparse_data_size
= out_entry_stat
->sparse_data_size();
555 out_entry_stat
->set_sparse_data_size(old_sparse_data_size
+ appended_so_far
);
556 *out_result
= written_so_far
;
559 void SimpleSynchronousEntry::GetAvailableRange(
560 const EntryOperationData
& in_entry_op
,
563 DCHECK(initialized_
);
564 int64 offset
= in_entry_op
.sparse_offset
;
565 int len
= in_entry_op
.buf_len
;
567 SparseRangeIterator it
= sparse_ranges_
.lower_bound(offset
);
569 int64 start
= offset
;
570 int avail_so_far
= 0;
572 if (it
!= sparse_ranges_
.end() && it
->second
.offset
< offset
+ len
)
573 start
= it
->second
.offset
;
575 if ((it
== sparse_ranges_
.end() || it
->second
.offset
> offset
) &&
576 it
!= sparse_ranges_
.begin()) {
578 if (it
->second
.offset
+ it
->second
.length
> offset
) {
580 avail_so_far
= (it
->second
.offset
+ it
->second
.length
) - offset
;
585 while (start
+ avail_so_far
< offset
+ len
&&
586 it
!= sparse_ranges_
.end() &&
587 it
->second
.offset
== start
+ avail_so_far
) {
588 avail_so_far
+= it
->second
.length
;
592 int len_from_start
= len
- (start
- offset
);
594 *out_result
= std::min(avail_so_far
, len_from_start
);
597 void SimpleSynchronousEntry::CheckEOFRecord(int index
,
598 const SimpleEntryStat
& entry_stat
,
599 uint32 expected_crc32
,
600 int* out_result
) const {
601 DCHECK(initialized_
);
606 GetEOFRecordData(index
, entry_stat
, &has_crc32
, &crc32
, &stream_size
);
607 if (*out_result
!= net::OK
) {
611 if (has_crc32
&& crc32
!= expected_crc32
) {
612 DVLOG(1) << "EOF record had bad crc.";
613 *out_result
= net::ERR_CACHE_CHECKSUM_MISMATCH
;
614 RecordCheckEOFResult(cache_type_
, CHECK_EOF_RESULT_CRC_MISMATCH
);
618 RecordCheckEOFResult(cache_type_
, CHECK_EOF_RESULT_SUCCESS
);
621 void SimpleSynchronousEntry::Close(
622 const SimpleEntryStat
& entry_stat
,
623 scoped_ptr
<std::vector
<CRCRecord
> > crc32s_to_write
,
624 net::GrowableIOBuffer
* stream_0_data
) {
625 DCHECK(stream_0_data
);
626 // Write stream 0 data.
627 int stream_0_offset
= entry_stat
.GetOffsetInFile(key_
, 0, 0);
628 if (files_
[0].Write(stream_0_offset
, stream_0_data
->data(),
629 entry_stat
.data_size(0)) !=
630 entry_stat
.data_size(0)) {
631 RecordCloseResult(cache_type_
, CLOSE_RESULT_WRITE_FAILURE
);
632 DVLOG(1) << "Could not write stream 0 data.";
636 for (std::vector
<CRCRecord
>::const_iterator it
= crc32s_to_write
->begin();
637 it
!= crc32s_to_write
->end(); ++it
) {
638 const int stream_index
= it
->index
;
639 const int file_index
= GetFileIndexFromStreamIndex(stream_index
);
640 if (empty_file_omitted_
[file_index
])
643 SimpleFileEOF eof_record
;
644 eof_record
.stream_size
= entry_stat
.data_size(stream_index
);
645 eof_record
.final_magic_number
= kSimpleFinalMagicNumber
;
646 eof_record
.flags
= 0;
648 eof_record
.flags
|= SimpleFileEOF::FLAG_HAS_CRC32
;
649 eof_record
.data_crc32
= it
->data_crc32
;
650 int eof_offset
= entry_stat
.GetEOFOffsetInFile(key_
, stream_index
);
651 // If stream 0 changed size, the file needs to be resized, otherwise the
652 // next open will yield wrong stream sizes. On stream 1 and stream 2 proper
653 // resizing of the file is handled in SimpleSynchronousEntry::WriteData().
654 if (stream_index
== 0 &&
655 !files_
[file_index
].SetLength(eof_offset
)) {
656 RecordCloseResult(cache_type_
, CLOSE_RESULT_WRITE_FAILURE
);
657 DVLOG(1) << "Could not truncate stream 0 file.";
661 if (files_
[file_index
].Write(eof_offset
,
662 reinterpret_cast<const char*>(&eof_record
),
663 sizeof(eof_record
)) !=
664 sizeof(eof_record
)) {
665 RecordCloseResult(cache_type_
, CLOSE_RESULT_WRITE_FAILURE
);
666 DVLOG(1) << "Could not write eof record.";
671 for (int i
= 0; i
< kSimpleEntryFileCount
; ++i
) {
672 if (empty_file_omitted_
[i
])
676 const int64 file_size
= entry_stat
.GetFileSize(key_
, i
);
677 SIMPLE_CACHE_UMA(CUSTOM_COUNTS
,
678 "LastClusterSize", cache_type_
,
679 file_size
% 4096, 0, 4097, 50);
680 const int64 cluster_loss
= file_size
% 4096 ? 4096 - file_size
% 4096 : 0;
681 SIMPLE_CACHE_UMA(PERCENTAGE
,
682 "LastClusterLossPercent", cache_type_
,
683 cluster_loss
* 100 / (cluster_loss
+ file_size
));
686 if (sparse_file_open())
687 sparse_file_
.Close();
689 if (files_created_
) {
690 const int stream2_file_index
= GetFileIndexFromStreamIndex(2);
691 SIMPLE_CACHE_UMA(BOOLEAN
, "EntryCreatedAndStream2Omitted", cache_type_
,
692 empty_file_omitted_
[stream2_file_index
]);
694 RecordCloseResult(cache_type_
, CLOSE_RESULT_SUCCESS
);
695 have_open_files_
= false;
699 SimpleSynchronousEntry::SimpleSynchronousEntry(net::CacheType cache_type
,
700 const FilePath
& path
,
701 const std::string
& key
,
702 const uint64 entry_hash
)
703 : cache_type_(cache_type
),
705 entry_hash_(entry_hash
),
707 have_open_files_(false),
708 initialized_(false) {
709 for (int i
= 0; i
< kSimpleEntryFileCount
; ++i
)
710 empty_file_omitted_
[i
] = false;
713 SimpleSynchronousEntry::~SimpleSynchronousEntry() {
714 DCHECK(!(have_open_files_
&& initialized_
));
715 if (have_open_files_
)
719 bool SimpleSynchronousEntry::MaybeOpenFile(
721 File::Error
* out_error
) {
724 FilePath filename
= GetFilenameFromFileIndex(file_index
);
725 int flags
= File::FLAG_OPEN
| File::FLAG_READ
| File::FLAG_WRITE
;
726 files_
[file_index
].Initialize(filename
, flags
);
727 *out_error
= files_
[file_index
].error_details();
729 if (CanOmitEmptyFile(file_index
) && !files_
[file_index
].IsValid() &&
730 *out_error
== File::FILE_ERROR_NOT_FOUND
) {
731 empty_file_omitted_
[file_index
] = true;
735 return files_
[file_index
].IsValid();
738 bool SimpleSynchronousEntry::MaybeCreateFile(
740 FileRequired file_required
,
741 File::Error
* out_error
) {
744 if (CanOmitEmptyFile(file_index
) && file_required
== FILE_NOT_REQUIRED
) {
745 empty_file_omitted_
[file_index
] = true;
749 FilePath filename
= GetFilenameFromFileIndex(file_index
);
750 int flags
= File::FLAG_CREATE
| File::FLAG_READ
| File::FLAG_WRITE
;
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, 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 base::DeleteFile(to_delete
, false);
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 base::DeleteFile(to_delete
, false);
1160 void SimpleSynchronousEntry::RecordSyncCreateResult(CreateEntryResult result
,
1162 DCHECK_GT(CREATE_ENTRY_MAX
, result
);
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 sparse_file_
.Initialize(filename
, flags
);
1189 if (sparse_file_
.IsValid())
1190 return ScanSparseFile(out_sparse_data_size
);
1192 return sparse_file_
.error_details() == File::FILE_ERROR_NOT_FOUND
;
1195 bool SimpleSynchronousEntry::CreateSparseFile() {
1196 DCHECK(!sparse_file_open());
1198 FilePath filename
= path_
.AppendASCII(
1199 GetSparseFilenameFromEntryHash(entry_hash_
));
1200 int flags
= File::FLAG_CREATE
| File::FLAG_READ
| File::FLAG_WRITE
;
1201 sparse_file_
.Initialize(filename
, flags
);
1202 if (!sparse_file_
.IsValid())
1205 return InitializeSparseFile();
1208 void SimpleSynchronousEntry::CloseSparseFile() {
1209 DCHECK(sparse_file_open());
1210 sparse_file_
.Close();
1213 bool SimpleSynchronousEntry::TruncateSparseFile() {
1214 DCHECK(sparse_file_open());
1216 int64 header_and_key_length
= sizeof(SimpleFileHeader
) + key_
.size();
1217 if (!sparse_file_
.SetLength(header_and_key_length
)) {
1218 DLOG(WARNING
) << "Could not truncate sparse file";
1222 sparse_ranges_
.clear();
1227 bool SimpleSynchronousEntry::InitializeSparseFile() {
1228 DCHECK(sparse_file_open());
1230 SimpleFileHeader header
;
1231 header
.initial_magic_number
= kSimpleInitialMagicNumber
;
1232 header
.version
= kSimpleVersion
;
1233 header
.key_length
= key_
.size();
1234 header
.key_hash
= base::Hash(key_
);
1236 int header_write_result
=
1237 sparse_file_
.Write(0, reinterpret_cast<char*>(&header
), sizeof(header
));
1238 if (header_write_result
!= sizeof(header
)) {
1239 DLOG(WARNING
) << "Could not write sparse file header";
1243 int key_write_result
= sparse_file_
.Write(sizeof(header
), key_
.data(),
1245 if (key_write_result
!= implicit_cast
<int>(key_
.size())) {
1246 DLOG(WARNING
) << "Could not write sparse file key";
1250 sparse_ranges_
.clear();
1251 sparse_tail_offset_
= sizeof(header
) + key_
.size();
1256 bool SimpleSynchronousEntry::ScanSparseFile(int32
* out_sparse_data_size
) {
1257 DCHECK(sparse_file_open());
1259 int32 sparse_data_size
= 0;
1261 SimpleFileHeader header
;
1262 int header_read_result
=
1263 sparse_file_
.Read(0, reinterpret_cast<char*>(&header
), sizeof(header
));
1264 if (header_read_result
!= sizeof(header
)) {
1265 DLOG(WARNING
) << "Could not read header from sparse file.";
1269 if (header
.initial_magic_number
!= kSimpleInitialMagicNumber
) {
1270 DLOG(WARNING
) << "Sparse file magic number did not match.";
1274 if (header
.version
!= kSimpleVersion
) {
1275 DLOG(WARNING
) << "Sparse file unreadable version.";
1279 sparse_ranges_
.clear();
1281 int64 range_header_offset
= sizeof(header
) + key_
.size();
1283 SimpleFileSparseRangeHeader range_header
;
1284 int range_header_read_result
=
1285 sparse_file_
.Read(range_header_offset
,
1286 reinterpret_cast<char*>(&range_header
),
1287 sizeof(range_header
));
1288 if (range_header_read_result
== 0)
1290 if (range_header_read_result
!= sizeof(range_header
)) {
1291 DLOG(WARNING
) << "Could not read sparse range header.";
1295 if (range_header
.sparse_range_magic_number
!=
1296 kSimpleSparseRangeMagicNumber
) {
1297 DLOG(WARNING
) << "Invalid sparse range header magic number.";
1302 range
.offset
= range_header
.offset
;
1303 range
.length
= range_header
.length
;
1304 range
.data_crc32
= range_header
.data_crc32
;
1305 range
.file_offset
= range_header_offset
+ sizeof(range_header
);
1306 sparse_ranges_
.insert(std::make_pair(range
.offset
, range
));
1308 range_header_offset
+= sizeof(range_header
) + range
.length
;
1310 DCHECK_LE(sparse_data_size
, sparse_data_size
+ range
.length
);
1311 sparse_data_size
+= range
.length
;
1314 *out_sparse_data_size
= sparse_data_size
;
1315 sparse_tail_offset_
= range_header_offset
;
1320 bool SimpleSynchronousEntry::ReadSparseRange(const SparseRange
* range
,
1321 int offset
, int len
, char* buf
) {
1324 DCHECK_GE(range
->length
, offset
);
1325 DCHECK_GE(range
->length
, offset
+ len
);
1327 int bytes_read
= sparse_file_
.Read(range
->file_offset
+ offset
, buf
, len
);
1328 if (bytes_read
< len
) {
1329 DLOG(WARNING
) << "Could not read sparse range.";
1333 // If we read the whole range and we have a crc32, check it.
1334 if (offset
== 0 && len
== range
->length
&& range
->data_crc32
!= 0) {
1335 uint32 actual_crc32
= crc32(crc32(0L, Z_NULL
, 0),
1336 reinterpret_cast<const Bytef
*>(buf
),
1338 if (actual_crc32
!= range
->data_crc32
) {
1339 DLOG(WARNING
) << "Sparse range crc32 mismatch.";
1343 // TODO(ttuttle): Incremental crc32 calculation?
1348 bool SimpleSynchronousEntry::WriteSparseRange(SparseRange
* range
,
1349 int offset
, int len
,
1353 DCHECK_GE(range
->length
, offset
);
1354 DCHECK_GE(range
->length
, offset
+ len
);
1356 uint32 new_crc32
= 0;
1357 if (offset
== 0 && len
== range
->length
) {
1358 new_crc32
= crc32(crc32(0L, Z_NULL
, 0),
1359 reinterpret_cast<const Bytef
*>(buf
),
1363 if (new_crc32
!= range
->data_crc32
) {
1364 range
->data_crc32
= new_crc32
;
1366 SimpleFileSparseRangeHeader header
;
1367 header
.sparse_range_magic_number
= kSimpleSparseRangeMagicNumber
;
1368 header
.offset
= range
->offset
;
1369 header
.length
= range
->length
;
1370 header
.data_crc32
= range
->data_crc32
;
1372 int bytes_written
= sparse_file_
.Write(range
->file_offset
- sizeof(header
),
1373 reinterpret_cast<char*>(&header
),
1375 if (bytes_written
!= implicit_cast
<int>(sizeof(header
))) {
1376 DLOG(WARNING
) << "Could not rewrite sparse range header.";
1381 int bytes_written
= sparse_file_
.Write(range
->file_offset
+ offset
, buf
, len
);
1382 if (bytes_written
< len
) {
1383 DLOG(WARNING
) << "Could not write sparse range.";
1390 bool SimpleSynchronousEntry::AppendSparseRange(int64 offset
,
1393 DCHECK_LE(0, offset
);
1397 uint32 data_crc32
= crc32(crc32(0L, Z_NULL
, 0),
1398 reinterpret_cast<const Bytef
*>(buf
),
1401 SimpleFileSparseRangeHeader header
;
1402 header
.sparse_range_magic_number
= kSimpleSparseRangeMagicNumber
;
1403 header
.offset
= offset
;
1404 header
.length
= len
;
1405 header
.data_crc32
= data_crc32
;
1407 int bytes_written
= sparse_file_
.Write(sparse_tail_offset_
,
1408 reinterpret_cast<char*>(&header
),
1410 if (bytes_written
!= implicit_cast
<int>(sizeof(header
))) {
1411 DLOG(WARNING
) << "Could not append sparse range header.";
1414 sparse_tail_offset_
+= bytes_written
;
1416 bytes_written
= sparse_file_
.Write(sparse_tail_offset_
, buf
, len
);
1417 if (bytes_written
< len
) {
1418 DLOG(WARNING
) << "Could not append sparse range data.";
1421 int64 data_file_offset
= sparse_tail_offset_
;
1422 sparse_tail_offset_
+= bytes_written
;
1425 range
.offset
= offset
;
1427 range
.data_crc32
= data_crc32
;
1428 range
.file_offset
= data_file_offset
;
1429 sparse_ranges_
.insert(std::make_pair(offset
, range
));
1434 } // namespace disk_cache