Remove IsHighDPIEnabled, move EnableHighDPISupport to only place it's used
[chromium-blink-merge.git] / net / disk_cache / simple / simple_synchronous_entry.cc
blobc50c9a350ec5e2f73ec5d27fd393812cf39b2ed1
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "net/disk_cache/simple/simple_synchronous_entry.h"
7 #include <algorithm>
8 #include <cstring>
9 #include <functional>
10 #include <limits>
12 #include "base/basictypes.h"
13 #include "base/compiler_specific.h"
14 #include "base/files/file_util.h"
15 #include "base/hash.h"
16 #include "base/location.h"
17 #include "base/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"
27 using base::File;
28 using base::FilePath;
29 using base::Time;
31 namespace {
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,
44 OPEN_ENTRY_MAX = 9,
47 // Used in histograms, please only add entries at the end.
48 enum WriteResult {
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,
56 WRITE_RESULT_MAX,
59 // Used in histograms, please only add entries at the end.
60 enum CheckEOFResult {
61 CHECK_EOF_RESULT_SUCCESS,
62 CHECK_EOF_RESULT_READ_FAILURE,
63 CHECK_EOF_RESULT_MAGIC_NUMBER_MISMATCH,
64 CHECK_EOF_RESULT_CRC_MISMATCH,
65 CHECK_EOF_RESULT_MAX,
68 // Used in histograms, please only add entries at the end.
69 enum CloseResult {
70 CLOSE_RESULT_SUCCESS,
71 CLOSE_RESULT_WRITE_FAILURE,
74 void RecordSyncOpenResult(net::CacheType cache_type,
75 OpenEntryResult result,
76 bool had_index) {
77 DCHECK_LT(result, OPEN_ENTRY_MAX);
78 SIMPLE_CACHE_UMA(ENUMERATION,
79 "SyncOpenResult", cache_type, result, OPEN_ENTRY_MAX);
80 if (had_index) {
81 SIMPLE_CACHE_UMA(ENUMERATION,
82 "SyncOpenResult_WithIndex", cache_type,
83 result, OPEN_ENTRY_MAX);
84 } else {
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);
113 } // namespace
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,
135 int offset,
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)
153 : data_size_[2];
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)
160 : data_size_[2];
161 return GetFileSizeFromKeyAndDataSize(key, total_data_size);
164 SimpleEntryCreationResults::SimpleEntryCreationResults(
165 SimpleEntryStat entry_stat)
166 : sync_entry(NULL),
167 entry_stat(entry_stat),
168 stream_0_crc32(crc32(0, Z_NULL, 0)),
169 result(net::OK) {
172 SimpleEntryCreationResults::~SimpleEntryCreationResults() {
175 SimpleSynchronousEntry::CRCRecord::CRCRecord() : index(-1),
176 has_crc32(false),
177 data_crc32(0) {
180 SimpleSynchronousEntry::CRCRecord::CRCRecord(int index_p,
181 bool has_crc32_p,
182 uint32 data_crc32_p)
183 : index(index_p),
184 has_crc32(has_crc32_p),
185 data_crc32(data_crc32_p) {}
187 SimpleSynchronousEntry::EntryOperationData::EntryOperationData(int index_p,
188 int offset_p,
189 int buf_len_p)
190 : index(index_p),
191 offset(offset_p),
192 buf_len(buf_len_p) {}
194 SimpleSynchronousEntry::EntryOperationData::EntryOperationData(int index_p,
195 int offset_p,
196 int buf_len_p,
197 bool truncate_p,
198 bool doomed_p)
199 : index(index_p),
200 offset(offset_p),
201 buf_len(buf_len_p),
202 truncate(truncate_p),
203 doomed(doomed_p) {}
205 SimpleSynchronousEntry::EntryOperationData::EntryOperationData(
206 int64 sparse_offset_p,
207 int buf_len_p)
208 : sparse_offset(sparse_offset_p),
209 buf_len(buf_len_p) {}
211 // static
212 void SimpleSynchronousEntry::OpenEntry(
213 net::CacheType cache_type,
214 const FilePath& path,
215 const uint64 entry_hash,
216 bool had_index,
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) {
226 sync_entry->Doom();
227 delete sync_entry;
228 out_results->sync_entry = NULL;
229 out_results->stream_0_data = NULL;
230 return;
232 out_results->sync_entry = sync_entry;
235 // static
236 void SimpleSynchronousEntry::CreateEntry(
237 net::CacheType cache_type,
238 const FilePath& path,
239 const std::string& key,
240 const uint64 entry_hash,
241 bool had_index,
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)
250 sync_entry->Doom();
251 delete sync_entry;
252 out_results->sync_entry = NULL;
253 return;
255 out_results->sync_entry = sync_entry;
258 // static
259 int SimpleSynchronousEntry::DoomEntry(
260 const FilePath& path,
261 uint64 entry_hash) {
262 const bool deleted_well = DeleteFilesForEntryHash(path, entry_hash);
263 return deleted_well ? net::OK : net::ERR_FAILED;
266 // static
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,
278 uint32* out_crc32,
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]);
291 int bytes_read =
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()),
297 bytes_read);
299 if (bytes_read >= 0) {
300 *out_result = bytes_read;
301 } else {
302 *out_result = net::ERR_CACHE_READ_FAILURE;
303 Doom();
307 void SimpleSynchronousEntry::WriteData(const EntryOperationData& in_entry_op,
308 net::IOBuffer* in_buf,
309 SimpleEntryStat* out_entry_stat,
310 int* out_result) {
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.
326 if (doomed) {
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;
331 return;
333 File::Error error;
334 if (!MaybeCreateFile(file_index, FILE_REQUIRED, &error)) {
335 RecordWriteResult(cache_type_, WRITE_RESULT_LAZY_CREATE_FAILURE);
336 Doom();
337 *out_result = net::ERR_CACHE_WRITE_FAILURE;
338 return;
340 CreateEntryResult result;
341 if (!InitializeCreatedFile(file_index, &result)) {
342 RecordWriteResult(cache_type_, WRITE_RESULT_LAZY_INITIALIZE_FAILURE);
343 Doom();
344 *out_result = net::ERR_CACHE_WRITE_FAILURE;
345 return;
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);
356 Doom();
357 *out_result = net::ERR_CACHE_WRITE_FAILURE;
358 return;
361 if (buf_len > 0) {
362 if (files_[file_index].Write(file_offset, in_buf->data(), buf_len) !=
363 buf_len) {
364 RecordWriteResult(cache_type_, WRITE_RESULT_WRITE_FAILURE);
365 Doom();
366 *out_result = net::ERR_CACHE_WRITE_FAILURE;
367 return;
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));
373 } else {
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);
378 Doom();
379 *out_result = net::ERR_CACHE_WRITE_FAILURE;
380 return;
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,
395 int* out_result) {
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();
401 int read_so_far = 0;
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.
408 --it;
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;
424 return;
426 read_so_far += len_to_read;
428 ++it;
431 // Keep reading until the buffer is full or there is not another contiguous
432 // range.
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;
442 return;
444 read_so_far += len_to_read;
445 ++it;
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,
456 int* out_result) {
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;
467 return;
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()) {
482 --it;
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;
497 return;
499 written_so_far += len_to_write;
501 ++it;
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) {
509 int len_to_append =
510 static_cast<int>(found_range->offset - (offset + written_so_far));
511 if (!AppendSparseRange(offset + written_so_far,
512 len_to_append,
513 buf + written_so_far)) {
514 *out_result = net::ERR_CACHE_WRITE_FAILURE;
515 return;
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,
524 len_to_write,
525 buf + written_so_far)) {
526 *out_result = net::ERR_CACHE_WRITE_FAILURE;
527 return;
529 written_so_far += len_to_write;
530 ++it;
533 if (written_so_far < buf_len) {
534 int len_to_append = buf_len - written_so_far;
535 if (!AppendSparseRange(offset + written_so_far,
536 len_to_append,
537 buf + written_so_far)) {
538 *out_result = net::ERR_CACHE_WRITE_FAILURE;
539 return;
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,
557 int64* out_start,
558 int* out_result) {
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()) {
573 --it;
574 if (it->second.offset + it->second.length > offset) {
575 start = offset;
576 avail_so_far = (it->second.offset + it->second.length) - offset;
578 ++it;
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;
585 ++it;
588 int64 len_from_start = len - (start - offset);
589 *out_start = start;
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_);
598 uint32 crc32;
599 bool has_crc32;
600 int stream_size;
601 *out_result =
602 GetEOFRecordData(index, entry_stat, &has_crc32, &crc32, &stream_size);
603 if (*out_result != net::OK) {
604 Doom();
605 return;
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);
611 Doom();
612 return;
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.";
629 Doom();
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])
637 continue;
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;
643 if (it->has_crc32)
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.";
654 Doom();
655 break;
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.";
663 Doom();
664 break;
667 for (int i = 0; i < kSimpleEntryFileCount; ++i) {
668 if (empty_file_omitted_[i])
669 continue;
671 files_[i].Close();
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;
693 delete this;
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),
701 path_(path),
702 entry_hash_(entry_hash),
703 key_(key),
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_)
713 CloseFiles();
716 bool SimpleSynchronousEntry::MaybeOpenFile(
717 int file_index,
718 File::Error* out_error) {
719 DCHECK(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;
729 return true;
732 return files_[file_index].IsValid();
735 bool SimpleSynchronousEntry::MaybeCreateFile(
736 int file_index,
737 FileRequired file_required,
738 File::Error* out_error) {
739 DCHECK(out_error);
741 if (CanOmitEmptyFile(file_index) && file_required == FILE_NOT_REQUIRED) {
742 empty_file_omitted_[file_index] = true;
743 return 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(
757 bool had_index,
758 SimpleEntryStat* out_entry_stat) {
759 for (int i = 0; i < kSimpleEntryFileCount; ++i) {
760 File::Error error;
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);
769 if (had_index) {
770 SIMPLE_CACHE_UMA(ENUMERATION,
771 "SyncOpenPlatformFileError_WithIndex", cache_type_,
772 -error, -base::File::FILE_ERROR_MAX);
773 } else {
774 SIMPLE_CACHE_UMA(ENUMERATION,
775 "SyncOpenPlatformFileError_WithoutIndex",
776 cache_type_,
777 -error, -base::File::FILE_ERROR_MAX);
779 while (--i >= 0)
780 CloseFile(i);
781 return false;
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);
791 continue;
794 File::Info file_info;
795 bool success = files_[i].GetInfo(&file_info);
796 base::Time file_last_modified;
797 if (!success) {
798 DLOG(WARNING) << "Could not get platform file info.";
799 continue;
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);
804 else
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;
833 return true;
836 bool SimpleSynchronousEntry::CreateFiles(
837 bool had_index,
838 SimpleEntryStat* out_entry_stat) {
839 for (int i = 0; i < kSimpleEntryFileCount; ++i) {
840 File::Error error;
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);
848 if (had_index) {
849 SIMPLE_CACHE_UMA(ENUMERATION,
850 "SyncCreatePlatformFileError_WithIndex", cache_type_,
851 -error, -base::File::FILE_ERROR_MAX);
852 } else {
853 SIMPLE_CACHE_UMA(ENUMERATION,
854 "SyncCreatePlatformFileError_WithoutIndex",
855 cache_type_,
856 -error, -base::File::FILE_ERROR_MAX);
858 while (--i >= 0)
859 CloseFile(i);
860 return false;
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;
874 return true;
877 void SimpleSynchronousEntry::CloseFile(int index) {
878 if (empty_file_omitted_[index]) {
879 empty_file_omitted_[index] = false;
880 } else {
881 DCHECK(files_[index].IsValid());
882 files_[index].Close();
885 if (sparse_file_open())
886 CloseSparseFile();
889 void SimpleSynchronousEntry::CloseFiles() {
890 for (int i = 0; i < kSimpleEntryFileCount; ++i)
891 CloseFile(i);
894 int SimpleSynchronousEntry::InitializeForOpen(
895 bool had_index,
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])
906 continue;
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(),
934 header.key_length);
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);
942 if (i == 0) {
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;
950 } else {
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_,
988 removed_stream2);
990 RecordSyncOpenResult(cache_type_, OPEN_ENTRY_SUCCESS, had_index);
991 initialized_ = true;
992 return net::OK;
995 bool SimpleSynchronousEntry::InitializeCreatedFile(
996 int file_index,
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;
1009 return false;
1012 bytes_written = files_[file_index].Write(sizeof(header), key_.data(),
1013 key_.size());
1014 if (bytes_written != implicit_cast<int>(key_.size())) {
1015 *out_result = CREATE_ENTRY_CANT_WRITE_KEY;
1016 return false;
1019 return true;
1022 int SimpleSynchronousEntry::InitializeForCreate(
1023 bool had_index,
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])
1032 continue;
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;
1042 return net::OK;
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));
1055 bool has_crc32;
1056 uint32 read_crc32;
1057 int stream_0_size;
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]);
1076 int bytes_read =
1077 file->Read(file_offset, (*stream_0_data)->data(), stream_0_size);
1078 if (bytes_read != stream_0_size)
1079 return net::ERR_FAILED;
1081 // Check the CRC32.
1082 uint32 expected_crc32 =
1083 stream_0_size == 0
1084 ? crc32(0, Z_NULL, 0)
1085 : crc32(crc32(0, Z_NULL, 0),
1086 reinterpret_cast<const Bytef*>((*stream_0_data)->data()),
1087 stream_0_size);
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);
1095 return net::OK;
1098 int SimpleSynchronousEntry::GetEOFRecordData(int index,
1099 const SimpleEntryStat& entry_stat,
1100 bool* out_has_crc32,
1101 uint32* out_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);
1125 return net::OK;
1128 void SimpleSynchronousEntry::Doom() const {
1129 DeleteFilesForEntryHash(path_, entry_hash_);
1132 // static
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);
1142 // static
1143 bool SimpleSynchronousEntry::DeleteFilesForEntryHash(
1144 const FilePath& path,
1145 const uint64 entry_hash) {
1146 bool result = true;
1147 for (int i = 0; i < kSimpleEntryFileCount; ++i) {
1148 if (!DeleteFileForEntryHash(path, entry_hash, i) && !CanOmitEmptyFile(i))
1149 result = false;
1151 FilePath to_delete = path.AppendASCII(
1152 GetSparseFilenameFromEntryHash(entry_hash));
1153 base::DeleteFile(to_delete, false);
1154 return result;
1157 void SimpleSynchronousEntry::RecordSyncCreateResult(CreateEntryResult result,
1158 bool had_index) {
1159 DCHECK_LT(result, CREATE_ENTRY_MAX);
1160 SIMPLE_CACHE_UMA(ENUMERATION,
1161 "SyncCreateResult", cache_type_, result, CREATE_ENTRY_MAX);
1162 if (had_index) {
1163 SIMPLE_CACHE_UMA(ENUMERATION,
1164 "SyncCreateResult_WithIndex", cache_type_,
1165 result, CREATE_ENTRY_MAX);
1166 } else {
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())
1200 return false;
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";
1216 return false;
1219 sparse_ranges_.clear();
1221 return true;
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";
1237 return false;
1240 int key_write_result = sparse_file_.Write(sizeof(header), key_.data(),
1241 key_.size());
1242 if (key_write_result != implicit_cast<int>(key_.size())) {
1243 DLOG(WARNING) << "Could not write sparse file key";
1244 return false;
1247 sparse_ranges_.clear();
1248 sparse_tail_offset_ = sizeof(header) + key_.size();
1250 return true;
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.";
1263 return false;
1266 if (header.initial_magic_number != kSimpleInitialMagicNumber) {
1267 DLOG(WARNING) << "Sparse file magic number did not match.";
1268 return false;
1271 if (header.version != kSimpleVersion) {
1272 DLOG(WARNING) << "Sparse file unreadable version.";
1273 return false;
1276 sparse_ranges_.clear();
1278 int64 range_header_offset = sizeof(header) + key_.size();
1279 while (1) {
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)
1286 break;
1287 if (range_header_read_result != sizeof(range_header)) {
1288 DLOG(WARNING) << "Could not read sparse range header.";
1289 return false;
1292 if (range_header.sparse_range_magic_number !=
1293 kSimpleSparseRangeMagicNumber) {
1294 DLOG(WARNING) << "Invalid sparse range header magic number.";
1295 return false;
1298 SparseRange range;
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;
1314 return true;
1317 bool SimpleSynchronousEntry::ReadSparseRange(const SparseRange* range,
1318 int offset, int len, char* buf) {
1319 DCHECK(range);
1320 DCHECK(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.";
1327 return false;
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),
1334 len);
1335 if (actual_crc32 != range->data_crc32) {
1336 DLOG(WARNING) << "Sparse range crc32 mismatch.";
1337 return false;
1340 // TODO(ttuttle): Incremental crc32 calculation?
1342 return true;
1345 bool SimpleSynchronousEntry::WriteSparseRange(SparseRange* range,
1346 int offset, int len,
1347 const char* buf) {
1348 DCHECK(range);
1349 DCHECK(buf);
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),
1357 len);
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),
1371 sizeof(header));
1372 if (bytes_written != implicit_cast<int>(sizeof(header))) {
1373 DLOG(WARNING) << "Could not rewrite sparse range header.";
1374 return false;
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.";
1381 return false;
1384 return true;
1387 bool SimpleSynchronousEntry::AppendSparseRange(int64 offset,
1388 int len,
1389 const char* buf) {
1390 DCHECK_GE(offset, 0);
1391 DCHECK_GT(len, 0);
1392 DCHECK(buf);
1394 uint32 data_crc32 = crc32(crc32(0L, Z_NULL, 0),
1395 reinterpret_cast<const Bytef*>(buf),
1396 len);
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),
1406 sizeof(header));
1407 if (bytes_written != implicit_cast<int>(sizeof(header))) {
1408 DLOG(WARNING) << "Could not append sparse range header.";
1409 return false;
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.";
1416 return false;
1418 int64 data_file_offset = sparse_tail_offset_;
1419 sparse_tail_offset_ += bytes_written;
1421 SparseRange range;
1422 range.offset = offset;
1423 range.length = len;
1424 range.data_crc32 = data_crc32;
1425 range.file_offset = data_file_offset;
1426 sparse_ranges_.insert(std::make_pair(offset, range));
1428 return true;
1431 } // namespace disk_cache