| OLD | NEW |
| (Empty) |
| 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. | |
| 4 | |
| 5 #include "net/disk_cache/simple/simple_synchronous_entry.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 #include <cstring> | |
| 9 #include <functional> | |
| 10 #include <limits> | |
| 11 | |
| 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" | |
| 26 | |
| 27 using base::File; | |
| 28 using base::FilePath; | |
| 29 using base::Time; | |
| 30 | |
| 31 namespace { | |
| 32 | |
| 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, | |
| 45 }; | |
| 46 | |
| 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, | |
| 57 }; | |
| 58 | |
| 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, | |
| 66 }; | |
| 67 | |
| 68 // Used in histograms, please only add entries at the end. | |
| 69 enum CloseResult { | |
| 70 CLOSE_RESULT_SUCCESS, | |
| 71 CLOSE_RESULT_WRITE_FAILURE, | |
| 72 }; | |
| 73 | |
| 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); | |
| 88 } | |
| 89 } | |
| 90 | |
| 91 void RecordWriteResult(net::CacheType cache_type, WriteResult result) { | |
| 92 SIMPLE_CACHE_UMA(ENUMERATION, | |
| 93 "SyncWriteResult", cache_type, result, WRITE_RESULT_MAX); | |
| 94 } | |
| 95 | |
| 96 void RecordCheckEOFResult(net::CacheType cache_type, CheckEOFResult result) { | |
| 97 SIMPLE_CACHE_UMA(ENUMERATION, | |
| 98 "SyncCheckEOFResult", cache_type, | |
| 99 result, CHECK_EOF_RESULT_MAX); | |
| 100 } | |
| 101 | |
| 102 void RecordCloseResult(net::CacheType cache_type, CloseResult result) { | |
| 103 SIMPLE_CACHE_UMA(ENUMERATION, | |
| 104 "SyncCloseResult", cache_type, result, WRITE_RESULT_MAX); | |
| 105 } | |
| 106 | |
| 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); | |
| 111 } | |
| 112 | |
| 113 } // namespace | |
| 114 | |
| 115 namespace disk_cache { | |
| 116 | |
| 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; | |
| 123 | |
| 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_)); | |
| 132 } | |
| 133 | |
| 134 int SimpleEntryStat::GetOffsetInFile(const std::string& key, | |
| 135 int offset, | |
| 136 int stream_index) const { | |
| 137 const size_t headers_size = sizeof(SimpleFileHeader) + key.size(); | |
| 138 const size_t additional_offset = | |
| 139 stream_index == 0 ? data_size_[1] + sizeof(SimpleFileEOF) : 0; | |
| 140 return headers_size + offset + additional_offset; | |
| 141 } | |
| 142 | |
| 143 int SimpleEntryStat::GetEOFOffsetInFile(const std::string& key, | |
| 144 int stream_index) const { | |
| 145 return GetOffsetInFile(key, data_size_[stream_index], stream_index); | |
| 146 } | |
| 147 | |
| 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); | |
| 155 } | |
| 156 | |
| 157 int64 SimpleEntryStat::GetFileSize(const std::string& key, | |
| 158 int file_index) const { | |
| 159 const int32 total_data_size = | |
| 160 file_index == 0 ? data_size_[0] + data_size_[1] + sizeof(SimpleFileEOF) | |
| 161 : data_size_[2]; | |
| 162 return GetFileSizeFromKeyAndDataSize(key, total_data_size); | |
| 163 } | |
| 164 | |
| 165 SimpleEntryCreationResults::SimpleEntryCreationResults( | |
| 166 SimpleEntryStat entry_stat) | |
| 167 : sync_entry(NULL), | |
| 168 entry_stat(entry_stat), | |
| 169 stream_0_crc32(crc32(0, Z_NULL, 0)), | |
| 170 result(net::OK) { | |
| 171 } | |
| 172 | |
| 173 SimpleEntryCreationResults::~SimpleEntryCreationResults() { | |
| 174 } | |
| 175 | |
| 176 SimpleSynchronousEntry::CRCRecord::CRCRecord() : index(-1), | |
| 177 has_crc32(false), | |
| 178 data_crc32(0) { | |
| 179 } | |
| 180 | |
| 181 SimpleSynchronousEntry::CRCRecord::CRCRecord(int index_p, | |
| 182 bool has_crc32_p, | |
| 183 uint32 data_crc32_p) | |
| 184 : index(index_p), | |
| 185 has_crc32(has_crc32_p), | |
| 186 data_crc32(data_crc32_p) {} | |
| 187 | |
| 188 SimpleSynchronousEntry::EntryOperationData::EntryOperationData(int index_p, | |
| 189 int offset_p, | |
| 190 int buf_len_p) | |
| 191 : index(index_p), | |
| 192 offset(offset_p), | |
| 193 buf_len(buf_len_p) {} | |
| 194 | |
| 195 SimpleSynchronousEntry::EntryOperationData::EntryOperationData(int index_p, | |
| 196 int offset_p, | |
| 197 int buf_len_p, | |
| 198 bool truncate_p, | |
| 199 bool doomed_p) | |
| 200 : index(index_p), | |
| 201 offset(offset_p), | |
| 202 buf_len(buf_len_p), | |
| 203 truncate(truncate_p), | |
| 204 doomed(doomed_p) {} | |
| 205 | |
| 206 SimpleSynchronousEntry::EntryOperationData::EntryOperationData( | |
| 207 int64 sparse_offset_p, | |
| 208 int buf_len_p) | |
| 209 : sparse_offset(sparse_offset_p), | |
| 210 buf_len(buf_len_p) {} | |
| 211 | |
| 212 // static | |
| 213 void SimpleSynchronousEntry::OpenEntry( | |
| 214 net::CacheType cache_type, | |
| 215 const FilePath& path, | |
| 216 const uint64 entry_hash, | |
| 217 bool had_index, | |
| 218 SimpleEntryCreationResults *out_results) { | |
| 219 SimpleSynchronousEntry* sync_entry = | |
| 220 new SimpleSynchronousEntry(cache_type, path, "", entry_hash); | |
| 221 out_results->result = | |
| 222 sync_entry->InitializeForOpen(had_index, | |
| 223 &out_results->entry_stat, | |
| 224 &out_results->stream_0_data, | |
| 225 &out_results->stream_0_crc32); | |
| 226 if (out_results->result != net::OK) { | |
| 227 sync_entry->Doom(); | |
| 228 delete sync_entry; | |
| 229 out_results->sync_entry = NULL; | |
| 230 out_results->stream_0_data = NULL; | |
| 231 return; | |
| 232 } | |
| 233 out_results->sync_entry = sync_entry; | |
| 234 } | |
| 235 | |
| 236 // static | |
| 237 void SimpleSynchronousEntry::CreateEntry( | |
| 238 net::CacheType cache_type, | |
| 239 const FilePath& path, | |
| 240 const std::string& key, | |
| 241 const uint64 entry_hash, | |
| 242 bool had_index, | |
| 243 SimpleEntryCreationResults *out_results) { | |
| 244 DCHECK_EQ(entry_hash, GetEntryHashKey(key)); | |
| 245 SimpleSynchronousEntry* sync_entry = | |
| 246 new SimpleSynchronousEntry(cache_type, path, key, entry_hash); | |
| 247 out_results->result = sync_entry->InitializeForCreate( | |
| 248 had_index, &out_results->entry_stat); | |
| 249 if (out_results->result != net::OK) { | |
| 250 if (out_results->result != net::ERR_FILE_EXISTS) | |
| 251 sync_entry->Doom(); | |
| 252 delete sync_entry; | |
| 253 out_results->sync_entry = NULL; | |
| 254 return; | |
| 255 } | |
| 256 out_results->sync_entry = sync_entry; | |
| 257 } | |
| 258 | |
| 259 // static | |
| 260 int SimpleSynchronousEntry::DoomEntry( | |
| 261 const FilePath& path, | |
| 262 uint64 entry_hash) { | |
| 263 const bool deleted_well = DeleteFilesForEntryHash(path, entry_hash); | |
| 264 return deleted_well ? net::OK : net::ERR_FAILED; | |
| 265 } | |
| 266 | |
| 267 // static | |
| 268 int SimpleSynchronousEntry::DoomEntrySet( | |
| 269 const std::vector<uint64>* key_hashes, | |
| 270 const FilePath& path) { | |
| 271 const size_t did_delete_count = std::count_if( | |
| 272 key_hashes->begin(), key_hashes->end(), std::bind1st( | |
| 273 std::ptr_fun(SimpleSynchronousEntry::DeleteFilesForEntryHash), path)); | |
| 274 return (did_delete_count == key_hashes->size()) ? net::OK : net::ERR_FAILED; | |
| 275 } | |
| 276 | |
| 277 void SimpleSynchronousEntry::ReadData(const EntryOperationData& in_entry_op, | |
| 278 net::IOBuffer* out_buf, | |
| 279 uint32* out_crc32, | |
| 280 SimpleEntryStat* entry_stat, | |
| 281 int* out_result) const { | |
| 282 DCHECK(initialized_); | |
| 283 DCHECK_NE(0, in_entry_op.index); | |
| 284 const int64 file_offset = | |
| 285 entry_stat->GetOffsetInFile(key_, in_entry_op.offset, in_entry_op.index); | |
| 286 int file_index = GetFileIndexFromStreamIndex(in_entry_op.index); | |
| 287 // Zero-length reads and reads to the empty streams of omitted files should | |
| 288 // be handled in the SimpleEntryImpl. | |
| 289 DCHECK_GT(in_entry_op.buf_len, 0); | |
| 290 DCHECK(!empty_file_omitted_[file_index]); | |
| 291 File* file = const_cast<File*>(&files_[file_index]); | |
| 292 int bytes_read = | |
| 293 file->Read(file_offset, out_buf->data(), in_entry_op.buf_len); | |
| 294 if (bytes_read > 0) { | |
| 295 entry_stat->set_last_used(Time::Now()); | |
| 296 *out_crc32 = crc32(crc32(0L, Z_NULL, 0), | |
| 297 reinterpret_cast<const Bytef*>(out_buf->data()), | |
| 298 bytes_read); | |
| 299 } | |
| 300 if (bytes_read >= 0) { | |
| 301 *out_result = bytes_read; | |
| 302 } else { | |
| 303 *out_result = net::ERR_CACHE_READ_FAILURE; | |
| 304 Doom(); | |
| 305 } | |
| 306 } | |
| 307 | |
| 308 void SimpleSynchronousEntry::WriteData(const EntryOperationData& in_entry_op, | |
| 309 net::IOBuffer* in_buf, | |
| 310 SimpleEntryStat* out_entry_stat, | |
| 311 int* out_result) { | |
| 312 DCHECK(initialized_); | |
| 313 DCHECK_NE(0, in_entry_op.index); | |
| 314 int index = in_entry_op.index; | |
| 315 int file_index = GetFileIndexFromStreamIndex(index); | |
| 316 int offset = in_entry_op.offset; | |
| 317 int buf_len = in_entry_op.buf_len; | |
| 318 bool truncate = in_entry_op.truncate; | |
| 319 bool doomed = in_entry_op.doomed; | |
| 320 const int64 file_offset = out_entry_stat->GetOffsetInFile( | |
| 321 key_, in_entry_op.offset, in_entry_op.index); | |
| 322 bool extending_by_write = offset + buf_len > out_entry_stat->data_size(index); | |
| 323 | |
| 324 if (empty_file_omitted_[file_index]) { | |
| 325 // Don't create a new file if the entry has been doomed, to avoid it being | |
| 326 // mixed up with a newly-created entry with the same key. | |
| 327 if (doomed) { | |
| 328 DLOG(WARNING) << "Rejecting write to lazily omitted stream " | |
| 329 << in_entry_op.index << " of doomed cache entry."; | |
| 330 RecordWriteResult(cache_type_, WRITE_RESULT_LAZY_STREAM_ENTRY_DOOMED); | |
| 331 *out_result = net::ERR_CACHE_WRITE_FAILURE; | |
| 332 return; | |
| 333 } | |
| 334 File::Error error; | |
| 335 if (!MaybeCreateFile(file_index, FILE_REQUIRED, &error)) { | |
| 336 RecordWriteResult(cache_type_, WRITE_RESULT_LAZY_CREATE_FAILURE); | |
| 337 Doom(); | |
| 338 *out_result = net::ERR_CACHE_WRITE_FAILURE; | |
| 339 return; | |
| 340 } | |
| 341 CreateEntryResult result; | |
| 342 if (!InitializeCreatedFile(file_index, &result)) { | |
| 343 RecordWriteResult(cache_type_, WRITE_RESULT_LAZY_INITIALIZE_FAILURE); | |
| 344 Doom(); | |
| 345 *out_result = net::ERR_CACHE_WRITE_FAILURE; | |
| 346 return; | |
| 347 } | |
| 348 } | |
| 349 DCHECK(!empty_file_omitted_[file_index]); | |
| 350 | |
| 351 if (extending_by_write) { | |
| 352 // The EOF record and the eventual stream afterward need to be zeroed out. | |
| 353 const int64 file_eof_offset = | |
| 354 out_entry_stat->GetEOFOffsetInFile(key_, index); | |
| 355 if (!files_[file_index].SetLength(file_eof_offset)) { | |
| 356 RecordWriteResult(cache_type_, WRITE_RESULT_PRETRUNCATE_FAILURE); | |
| 357 Doom(); | |
| 358 *out_result = net::ERR_CACHE_WRITE_FAILURE; | |
| 359 return; | |
| 360 } | |
| 361 } | |
| 362 if (buf_len > 0) { | |
| 363 if (files_[file_index].Write(file_offset, in_buf->data(), buf_len) != | |
| 364 buf_len) { | |
| 365 RecordWriteResult(cache_type_, WRITE_RESULT_WRITE_FAILURE); | |
| 366 Doom(); | |
| 367 *out_result = net::ERR_CACHE_WRITE_FAILURE; | |
| 368 return; | |
| 369 } | |
| 370 } | |
| 371 if (!truncate && (buf_len > 0 || !extending_by_write)) { | |
| 372 out_entry_stat->set_data_size( | |
| 373 index, std::max(out_entry_stat->data_size(index), offset + buf_len)); | |
| 374 } else { | |
| 375 out_entry_stat->set_data_size(index, offset + buf_len); | |
| 376 int file_eof_offset = out_entry_stat->GetLastEOFOffsetInFile(key_, index); | |
| 377 if (!files_[file_index].SetLength(file_eof_offset)) { | |
| 378 RecordWriteResult(cache_type_, WRITE_RESULT_TRUNCATE_FAILURE); | |
| 379 Doom(); | |
| 380 *out_result = net::ERR_CACHE_WRITE_FAILURE; | |
| 381 return; | |
| 382 } | |
| 383 } | |
| 384 | |
| 385 RecordWriteResult(cache_type_, WRITE_RESULT_SUCCESS); | |
| 386 base::Time modification_time = Time::Now(); | |
| 387 out_entry_stat->set_last_used(modification_time); | |
| 388 out_entry_stat->set_last_modified(modification_time); | |
| 389 *out_result = buf_len; | |
| 390 } | |
| 391 | |
| 392 void SimpleSynchronousEntry::ReadSparseData( | |
| 393 const EntryOperationData& in_entry_op, | |
| 394 net::IOBuffer* out_buf, | |
| 395 base::Time* out_last_used, | |
| 396 int* out_result) { | |
| 397 DCHECK(initialized_); | |
| 398 int64 offset = in_entry_op.sparse_offset; | |
| 399 int buf_len = in_entry_op.buf_len; | |
| 400 | |
| 401 char* buf = out_buf->data(); | |
| 402 int read_so_far = 0; | |
| 403 | |
| 404 // Find the first sparse range at or after the requested offset. | |
| 405 SparseRangeIterator it = sparse_ranges_.lower_bound(offset); | |
| 406 | |
| 407 if (it != sparse_ranges_.begin()) { | |
| 408 // Hop back one range and read the one overlapping with the start. | |
| 409 --it; | |
| 410 SparseRange* found_range = &it->second; | |
| 411 DCHECK_EQ(it->first, found_range->offset); | |
| 412 if (found_range->offset + found_range->length > offset) { | |
| 413 DCHECK_GE(found_range->length, 0); | |
| 414 DCHECK_LE(found_range->length, kint32max); | |
| 415 DCHECK_GE(offset - found_range->offset, 0); | |
| 416 DCHECK_LE(offset - found_range->offset, kint32max); | |
| 417 int net_offset = static_cast<int>(offset - found_range->offset); | |
| 418 int range_len_after_offset = | |
| 419 static_cast<int>(found_range->length - net_offset); | |
| 420 DCHECK_GE(range_len_after_offset, 0); | |
| 421 | |
| 422 int len_to_read = std::min(buf_len, range_len_after_offset); | |
| 423 if (!ReadSparseRange(found_range, net_offset, len_to_read, buf)) { | |
| 424 *out_result = net::ERR_CACHE_READ_FAILURE; | |
| 425 return; | |
| 426 } | |
| 427 read_so_far += len_to_read; | |
| 428 } | |
| 429 ++it; | |
| 430 } | |
| 431 | |
| 432 // Keep reading until the buffer is full or there is not another contiguous | |
| 433 // range. | |
| 434 while (read_so_far < buf_len && | |
| 435 it != sparse_ranges_.end() && | |
| 436 it->second.offset == offset + read_so_far) { | |
| 437 SparseRange* found_range = &it->second; | |
| 438 DCHECK_EQ(it->first, found_range->offset); | |
| 439 int range_len = base::saturated_cast<int>(found_range->length); | |
| 440 int len_to_read = std::min(buf_len - read_so_far, range_len); | |
| 441 if (!ReadSparseRange(found_range, 0, len_to_read, buf + read_so_far)) { | |
| 442 *out_result = net::ERR_CACHE_READ_FAILURE; | |
| 443 return; | |
| 444 } | |
| 445 read_so_far += len_to_read; | |
| 446 ++it; | |
| 447 } | |
| 448 | |
| 449 *out_result = read_so_far; | |
| 450 } | |
| 451 | |
| 452 void SimpleSynchronousEntry::WriteSparseData( | |
| 453 const EntryOperationData& in_entry_op, | |
| 454 net::IOBuffer* in_buf, | |
| 455 uint64 max_sparse_data_size, | |
| 456 SimpleEntryStat* out_entry_stat, | |
| 457 int* out_result) { | |
| 458 DCHECK(initialized_); | |
| 459 int64 offset = in_entry_op.sparse_offset; | |
| 460 int buf_len = in_entry_op.buf_len; | |
| 461 | |
| 462 const char* buf = in_buf->data(); | |
| 463 int written_so_far = 0; | |
| 464 int appended_so_far = 0; | |
| 465 | |
| 466 if (!sparse_file_open() && !CreateSparseFile()) { | |
| 467 *out_result = net::ERR_CACHE_WRITE_FAILURE; | |
| 468 return; | |
| 469 } | |
| 470 | |
| 471 uint64 sparse_data_size = out_entry_stat->sparse_data_size(); | |
| 472 // This is a pessimistic estimate; it assumes the entire buffer is going to | |
| 473 // be appended as a new range, not written over existing ranges. | |
| 474 if (sparse_data_size + buf_len > max_sparse_data_size) { | |
| 475 DVLOG(1) << "Truncating sparse data file (" << sparse_data_size << " + " | |
| 476 << buf_len << " > " << max_sparse_data_size << ")"; | |
| 477 TruncateSparseFile(); | |
| 478 } | |
| 479 | |
| 480 SparseRangeIterator it = sparse_ranges_.lower_bound(offset); | |
| 481 | |
| 482 if (it != sparse_ranges_.begin()) { | |
| 483 --it; | |
| 484 SparseRange* found_range = &it->second; | |
| 485 if (found_range->offset + found_range->length > offset) { | |
| 486 DCHECK_GE(found_range->length, 0); | |
| 487 DCHECK_LE(found_range->length, kint32max); | |
| 488 DCHECK_GE(offset - found_range->offset, 0); | |
| 489 DCHECK_LE(offset - found_range->offset, kint32max); | |
| 490 int net_offset = static_cast<int>(offset - found_range->offset); | |
| 491 int range_len_after_offset = | |
| 492 static_cast<int>(found_range->length - net_offset); | |
| 493 DCHECK_GE(range_len_after_offset, 0); | |
| 494 | |
| 495 int len_to_write = std::min(buf_len, range_len_after_offset); | |
| 496 if (!WriteSparseRange(found_range, net_offset, len_to_write, buf)) { | |
| 497 *out_result = net::ERR_CACHE_WRITE_FAILURE; | |
| 498 return; | |
| 499 } | |
| 500 written_so_far += len_to_write; | |
| 501 } | |
| 502 ++it; | |
| 503 } | |
| 504 | |
| 505 while (written_so_far < buf_len && | |
| 506 it != sparse_ranges_.end() && | |
| 507 it->second.offset < offset + buf_len) { | |
| 508 SparseRange* found_range = &it->second; | |
| 509 if (offset + written_so_far < found_range->offset) { | |
| 510 int len_to_append = | |
| 511 static_cast<int>(found_range->offset - (offset + written_so_far)); | |
| 512 if (!AppendSparseRange(offset + written_so_far, | |
| 513 len_to_append, | |
| 514 buf + written_so_far)) { | |
| 515 *out_result = net::ERR_CACHE_WRITE_FAILURE; | |
| 516 return; | |
| 517 } | |
| 518 written_so_far += len_to_append; | |
| 519 appended_so_far += len_to_append; | |
| 520 } | |
| 521 int range_len = base::saturated_cast<int>(found_range->length); | |
| 522 int len_to_write = std::min(buf_len - written_so_far, range_len); | |
| 523 if (!WriteSparseRange(found_range, | |
| 524 0, | |
| 525 len_to_write, | |
| 526 buf + written_so_far)) { | |
| 527 *out_result = net::ERR_CACHE_WRITE_FAILURE; | |
| 528 return; | |
| 529 } | |
| 530 written_so_far += len_to_write; | |
| 531 ++it; | |
| 532 } | |
| 533 | |
| 534 if (written_so_far < buf_len) { | |
| 535 int len_to_append = buf_len - written_so_far; | |
| 536 if (!AppendSparseRange(offset + written_so_far, | |
| 537 len_to_append, | |
| 538 buf + written_so_far)) { | |
| 539 *out_result = net::ERR_CACHE_WRITE_FAILURE; | |
| 540 return; | |
| 541 } | |
| 542 written_so_far += len_to_append; | |
| 543 appended_so_far += len_to_append; | |
| 544 } | |
| 545 | |
| 546 DCHECK_EQ(buf_len, written_so_far); | |
| 547 | |
| 548 base::Time modification_time = Time::Now(); | |
| 549 out_entry_stat->set_last_used(modification_time); | |
| 550 out_entry_stat->set_last_modified(modification_time); | |
| 551 int32 old_sparse_data_size = out_entry_stat->sparse_data_size(); | |
| 552 out_entry_stat->set_sparse_data_size(old_sparse_data_size + appended_so_far); | |
| 553 *out_result = written_so_far; | |
| 554 } | |
| 555 | |
| 556 void SimpleSynchronousEntry::GetAvailableRange( | |
| 557 const EntryOperationData& in_entry_op, | |
| 558 int64* out_start, | |
| 559 int* out_result) { | |
| 560 DCHECK(initialized_); | |
| 561 int64 offset = in_entry_op.sparse_offset; | |
| 562 int len = in_entry_op.buf_len; | |
| 563 | |
| 564 SparseRangeIterator it = sparse_ranges_.lower_bound(offset); | |
| 565 | |
| 566 int64 start = offset; | |
| 567 int64 avail_so_far = 0; | |
| 568 | |
| 569 if (it != sparse_ranges_.end() && it->second.offset < offset + len) | |
| 570 start = it->second.offset; | |
| 571 | |
| 572 if ((it == sparse_ranges_.end() || it->second.offset > offset) && | |
| 573 it != sparse_ranges_.begin()) { | |
| 574 --it; | |
| 575 if (it->second.offset + it->second.length > offset) { | |
| 576 start = offset; | |
| 577 avail_so_far = (it->second.offset + it->second.length) - offset; | |
| 578 } | |
| 579 ++it; | |
| 580 } | |
| 581 | |
| 582 while (start + avail_so_far < offset + len && | |
| 583 it != sparse_ranges_.end() && | |
| 584 it->second.offset == start + avail_so_far) { | |
| 585 avail_so_far += it->second.length; | |
| 586 ++it; | |
| 587 } | |
| 588 | |
| 589 int64 len_from_start = len - (start - offset); | |
| 590 *out_start = start; | |
| 591 *out_result = static_cast<int>(std::min(avail_so_far, len_from_start)); | |
| 592 } | |
| 593 | |
| 594 void SimpleSynchronousEntry::CheckEOFRecord(int index, | |
| 595 const SimpleEntryStat& entry_stat, | |
| 596 uint32 expected_crc32, | |
| 597 int* out_result) const { | |
| 598 DCHECK(initialized_); | |
| 599 uint32 crc32; | |
| 600 bool has_crc32; | |
| 601 int stream_size; | |
| 602 *out_result = | |
| 603 GetEOFRecordData(index, entry_stat, &has_crc32, &crc32, &stream_size); | |
| 604 if (*out_result != net::OK) { | |
| 605 Doom(); | |
| 606 return; | |
| 607 } | |
| 608 if (has_crc32 && crc32 != expected_crc32) { | |
| 609 DVLOG(1) << "EOF record had bad crc."; | |
| 610 *out_result = net::ERR_CACHE_CHECKSUM_MISMATCH; | |
| 611 RecordCheckEOFResult(cache_type_, CHECK_EOF_RESULT_CRC_MISMATCH); | |
| 612 Doom(); | |
| 613 return; | |
| 614 } | |
| 615 RecordCheckEOFResult(cache_type_, CHECK_EOF_RESULT_SUCCESS); | |
| 616 } | |
| 617 | |
| 618 void SimpleSynchronousEntry::Close( | |
| 619 const SimpleEntryStat& entry_stat, | |
| 620 scoped_ptr<std::vector<CRCRecord> > crc32s_to_write, | |
| 621 net::GrowableIOBuffer* stream_0_data) { | |
| 622 DCHECK(stream_0_data); | |
| 623 // Write stream 0 data. | |
| 624 int stream_0_offset = entry_stat.GetOffsetInFile(key_, 0, 0); | |
| 625 if (files_[0].Write(stream_0_offset, stream_0_data->data(), | |
| 626 entry_stat.data_size(0)) != | |
| 627 entry_stat.data_size(0)) { | |
| 628 RecordCloseResult(cache_type_, CLOSE_RESULT_WRITE_FAILURE); | |
| 629 DVLOG(1) << "Could not write stream 0 data."; | |
| 630 Doom(); | |
| 631 } | |
| 632 | |
| 633 for (std::vector<CRCRecord>::const_iterator it = crc32s_to_write->begin(); | |
| 634 it != crc32s_to_write->end(); ++it) { | |
| 635 const int stream_index = it->index; | |
| 636 const int file_index = GetFileIndexFromStreamIndex(stream_index); | |
| 637 if (empty_file_omitted_[file_index]) | |
| 638 continue; | |
| 639 | |
| 640 SimpleFileEOF eof_record; | |
| 641 eof_record.stream_size = entry_stat.data_size(stream_index); | |
| 642 eof_record.final_magic_number = kSimpleFinalMagicNumber; | |
| 643 eof_record.flags = 0; | |
| 644 if (it->has_crc32) | |
| 645 eof_record.flags |= SimpleFileEOF::FLAG_HAS_CRC32; | |
| 646 eof_record.data_crc32 = it->data_crc32; | |
| 647 int eof_offset = entry_stat.GetEOFOffsetInFile(key_, stream_index); | |
| 648 // If stream 0 changed size, the file needs to be resized, otherwise the | |
| 649 // next open will yield wrong stream sizes. On stream 1 and stream 2 proper | |
| 650 // resizing of the file is handled in SimpleSynchronousEntry::WriteData(). | |
| 651 if (stream_index == 0 && | |
| 652 !files_[file_index].SetLength(eof_offset)) { | |
| 653 RecordCloseResult(cache_type_, CLOSE_RESULT_WRITE_FAILURE); | |
| 654 DVLOG(1) << "Could not truncate stream 0 file."; | |
| 655 Doom(); | |
| 656 break; | |
| 657 } | |
| 658 if (files_[file_index].Write(eof_offset, | |
| 659 reinterpret_cast<const char*>(&eof_record), | |
| 660 sizeof(eof_record)) != | |
| 661 sizeof(eof_record)) { | |
| 662 RecordCloseResult(cache_type_, CLOSE_RESULT_WRITE_FAILURE); | |
| 663 DVLOG(1) << "Could not write eof record."; | |
| 664 Doom(); | |
| 665 break; | |
| 666 } | |
| 667 } | |
| 668 for (int i = 0; i < kSimpleEntryFileCount; ++i) { | |
| 669 if (empty_file_omitted_[i]) | |
| 670 continue; | |
| 671 | |
| 672 files_[i].Close(); | |
| 673 const int64 file_size = entry_stat.GetFileSize(key_, i); | |
| 674 SIMPLE_CACHE_UMA(CUSTOM_COUNTS, | |
| 675 "LastClusterSize", cache_type_, | |
| 676 file_size % 4096, 0, 4097, 50); | |
| 677 const int64 cluster_loss = file_size % 4096 ? 4096 - file_size % 4096 : 0; | |
| 678 SIMPLE_CACHE_UMA(PERCENTAGE, | |
| 679 "LastClusterLossPercent", cache_type_, | |
| 680 static_cast<base::HistogramBase::Sample>( | |
| 681 cluster_loss * 100 / (cluster_loss + file_size))); | |
| 682 } | |
| 683 | |
| 684 if (sparse_file_open()) | |
| 685 sparse_file_.Close(); | |
| 686 | |
| 687 if (files_created_) { | |
| 688 const int stream2_file_index = GetFileIndexFromStreamIndex(2); | |
| 689 SIMPLE_CACHE_UMA(BOOLEAN, "EntryCreatedAndStream2Omitted", cache_type_, | |
| 690 empty_file_omitted_[stream2_file_index]); | |
| 691 } | |
| 692 RecordCloseResult(cache_type_, CLOSE_RESULT_SUCCESS); | |
| 693 have_open_files_ = false; | |
| 694 delete this; | |
| 695 } | |
| 696 | |
| 697 SimpleSynchronousEntry::SimpleSynchronousEntry(net::CacheType cache_type, | |
| 698 const FilePath& path, | |
| 699 const std::string& key, | |
| 700 const uint64 entry_hash) | |
| 701 : cache_type_(cache_type), | |
| 702 path_(path), | |
| 703 entry_hash_(entry_hash), | |
| 704 key_(key), | |
| 705 have_open_files_(false), | |
| 706 initialized_(false) { | |
| 707 for (int i = 0; i < kSimpleEntryFileCount; ++i) | |
| 708 empty_file_omitted_[i] = false; | |
| 709 } | |
| 710 | |
| 711 SimpleSynchronousEntry::~SimpleSynchronousEntry() { | |
| 712 DCHECK(!(have_open_files_ && initialized_)); | |
| 713 if (have_open_files_) | |
| 714 CloseFiles(); | |
| 715 } | |
| 716 | |
| 717 bool SimpleSynchronousEntry::MaybeOpenFile( | |
| 718 int file_index, | |
| 719 File::Error* out_error) { | |
| 720 DCHECK(out_error); | |
| 721 | |
| 722 FilePath filename = GetFilenameFromFileIndex(file_index); | |
| 723 int flags = File::FLAG_OPEN | File::FLAG_READ | File::FLAG_WRITE | | |
| 724 File::FLAG_SHARE_DELETE; | |
| 725 files_[file_index].Initialize(filename, flags); | |
| 726 *out_error = files_[file_index].error_details(); | |
| 727 | |
| 728 if (CanOmitEmptyFile(file_index) && !files_[file_index].IsValid() && | |
| 729 *out_error == File::FILE_ERROR_NOT_FOUND) { | |
| 730 empty_file_omitted_[file_index] = true; | |
| 731 return true; | |
| 732 } | |
| 733 | |
| 734 return files_[file_index].IsValid(); | |
| 735 } | |
| 736 | |
| 737 bool SimpleSynchronousEntry::MaybeCreateFile( | |
| 738 int file_index, | |
| 739 FileRequired file_required, | |
| 740 File::Error* out_error) { | |
| 741 DCHECK(out_error); | |
| 742 | |
| 743 if (CanOmitEmptyFile(file_index) && file_required == FILE_NOT_REQUIRED) { | |
| 744 empty_file_omitted_[file_index] = true; | |
| 745 return true; | |
| 746 } | |
| 747 | |
| 748 FilePath filename = GetFilenameFromFileIndex(file_index); | |
| 749 int flags = File::FLAG_CREATE | File::FLAG_READ | File::FLAG_WRITE | | |
| 750 File::FLAG_SHARE_DELETE; | |
| 751 files_[file_index].Initialize(filename, flags); | |
| 752 *out_error = files_[file_index].error_details(); | |
| 753 | |
| 754 empty_file_omitted_[file_index] = false; | |
| 755 | |
| 756 return files_[file_index].IsValid(); | |
| 757 } | |
| 758 | |
| 759 bool SimpleSynchronousEntry::OpenFiles( | |
| 760 bool had_index, | |
| 761 SimpleEntryStat* out_entry_stat) { | |
| 762 for (int i = 0; i < kSimpleEntryFileCount; ++i) { | |
| 763 File::Error error; | |
| 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); | |
| 772 if (had_index) { | |
| 773 SIMPLE_CACHE_UMA(ENUMERATION, | |
| 774 "SyncOpenPlatformFileError_WithIndex", cache_type_, | |
| 775 -error, -base::File::FILE_ERROR_MAX); | |
| 776 } else { | |
| 777 SIMPLE_CACHE_UMA(ENUMERATION, | |
| 778 "SyncOpenPlatformFileError_WithoutIndex", | |
| 779 cache_type_, | |
| 780 -error, -base::File::FILE_ERROR_MAX); | |
| 781 } | |
| 782 while (--i >= 0) | |
| 783 CloseFile(i); | |
| 784 return false; | |
| 785 } | |
| 786 } | |
| 787 | |
| 788 have_open_files_ = true; | |
| 789 | |
| 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); | |
| 794 continue; | |
| 795 } | |
| 796 | |
| 797 File::Info file_info; | |
| 798 bool success = files_[i].GetInfo(&file_info); | |
| 799 base::Time file_last_modified; | |
| 800 if (!success) { | |
| 801 DLOG(WARNING) << "Could not get platform file info."; | |
| 802 continue; | |
| 803 } | |
| 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); | |
| 807 else | |
| 808 out_entry_stat->set_last_modified(file_info.last_modified); | |
| 809 | |
| 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; | |
| 814 | |
| 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. | |
| 819 // | |
| 820 // The size for file 0 and 1 is temporarily kept in | |
| 821 // |data_size(1)| and |data_size(2)| respectively. Reading the key in | |
| 822 // InitializeForOpen yields the data size for each file. In the case of | |
| 823 // file hash_1, this is the total size of stream 2, and is assigned to | |
| 824 // data_size(2). In the case of file 0, it is the combined size of stream | |
| 825 // 0, stream 1 and one EOF record. The exact distribution of sizes between | |
| 826 // stream 1 and stream 0 is only determined after reading the EOF record | |
| 827 // for stream 0 in ReadAndValidateStream0. | |
| 828 out_entry_stat->set_data_size(i + 1, static_cast<int>(file_info.size)); | |
| 829 } | |
| 830 SIMPLE_CACHE_UMA(CUSTOM_COUNTS, | |
| 831 "SyncOpenEntryAge", cache_type_, | |
| 832 entry_age.InHours(), 1, 1000, 50); | |
| 833 | |
| 834 files_created_ = false; | |
| 835 | |
| 836 return true; | |
| 837 } | |
| 838 | |
| 839 bool SimpleSynchronousEntry::CreateFiles( | |
| 840 bool had_index, | |
| 841 SimpleEntryStat* out_entry_stat) { | |
| 842 for (int i = 0; i < kSimpleEntryFileCount; ++i) { | |
| 843 File::Error error; | |
| 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); | |
| 851 if (had_index) { | |
| 852 SIMPLE_CACHE_UMA(ENUMERATION, | |
| 853 "SyncCreatePlatformFileError_WithIndex", cache_type_, | |
| 854 -error, -base::File::FILE_ERROR_MAX); | |
| 855 } else { | |
| 856 SIMPLE_CACHE_UMA(ENUMERATION, | |
| 857 "SyncCreatePlatformFileError_WithoutIndex", | |
| 858 cache_type_, | |
| 859 -error, -base::File::FILE_ERROR_MAX); | |
| 860 } | |
| 861 while (--i >= 0) | |
| 862 CloseFile(i); | |
| 863 return false; | |
| 864 } | |
| 865 } | |
| 866 | |
| 867 have_open_files_ = true; | |
| 868 | |
| 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); | |
| 874 | |
| 875 files_created_ = true; | |
| 876 | |
| 877 return true; | |
| 878 } | |
| 879 | |
| 880 void SimpleSynchronousEntry::CloseFile(int index) { | |
| 881 if (empty_file_omitted_[index]) { | |
| 882 empty_file_omitted_[index] = false; | |
| 883 } else { | |
| 884 DCHECK(files_[index].IsValid()); | |
| 885 files_[index].Close(); | |
| 886 } | |
| 887 | |
| 888 if (sparse_file_open()) | |
| 889 CloseSparseFile(); | |
| 890 } | |
| 891 | |
| 892 void SimpleSynchronousEntry::CloseFiles() { | |
| 893 for (int i = 0; i < kSimpleEntryFileCount; ++i) | |
| 894 CloseFile(i); | |
| 895 } | |
| 896 | |
| 897 int SimpleSynchronousEntry::InitializeForOpen( | |
| 898 bool had_index, | |
| 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; | |
| 906 } | |
| 907 for (int i = 0; i < kSimpleEntryFileCount; ++i) { | |
| 908 if (empty_file_omitted_[i]) | |
| 909 continue; | |
| 910 | |
| 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; | |
| 918 } | |
| 919 | |
| 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; | |
| 927 } | |
| 928 | |
| 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; | |
| 933 } | |
| 934 | |
| 935 scoped_ptr<char[]> key(new char[header.key_length]); | |
| 936 int key_read_result = files_[i].Read(sizeof(header), key.get(), | |
| 937 header.key_length); | |
| 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; | |
| 942 } | |
| 943 | |
| 944 key_ = std::string(key.get(), header.key_length); | |
| 945 if (i == 0) { | |
| 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; | |
| 953 } else { | |
| 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; | |
| 959 } | |
| 960 } | |
| 961 | |
| 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; | |
| 967 } | |
| 968 } | |
| 969 | |
| 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; | |
| 975 } | |
| 976 out_entry_stat->set_sparse_data_size(sparse_data_size); | |
| 977 | |
| 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; | |
| 988 } | |
| 989 | |
| 990 SIMPLE_CACHE_UMA(BOOLEAN, "EntryOpenedAndStream2Removed", cache_type_, | |
| 991 removed_stream2); | |
| 992 | |
| 993 RecordSyncOpenResult(cache_type_, OPEN_ENTRY_SUCCESS, had_index); | |
| 994 initialized_ = true; | |
| 995 return net::OK; | |
| 996 } | |
| 997 | |
| 998 bool SimpleSynchronousEntry::InitializeCreatedFile( | |
| 999 int file_index, | |
| 1000 CreateEntryResult* out_result) { | |
| 1001 SimpleFileHeader header; | |
| 1002 header.initial_magic_number = kSimpleInitialMagicNumber; | |
| 1003 header.version = kSimpleEntryVersionOnDisk; | |
| 1004 | |
| 1005 header.key_length = key_.size(); | |
| 1006 header.key_hash = base::Hash(key_); | |
| 1007 | |
| 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; | |
| 1012 return false; | |
| 1013 } | |
| 1014 | |
| 1015 bytes_written = files_[file_index].Write(sizeof(header), key_.data(), | |
| 1016 key_.size()); | |
| 1017 if (bytes_written != implicit_cast<int>(key_.size())) { | |
| 1018 *out_result = CREATE_ENTRY_CANT_WRITE_KEY; | |
| 1019 return false; | |
| 1020 } | |
| 1021 | |
| 1022 return true; | |
| 1023 } | |
| 1024 | |
| 1025 int SimpleSynchronousEntry::InitializeForCreate( | |
| 1026 bool had_index, | |
| 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; | |
| 1032 } | |
| 1033 for (int i = 0; i < kSimpleEntryFileCount; ++i) { | |
| 1034 if (empty_file_omitted_[i]) | |
| 1035 continue; | |
| 1036 | |
| 1037 CreateEntryResult result; | |
| 1038 if (!InitializeCreatedFile(i, &result)) { | |
| 1039 RecordSyncCreateResult(result, had_index); | |
| 1040 return net::ERR_FAILED; | |
| 1041 } | |
| 1042 } | |
| 1043 RecordSyncCreateResult(CREATE_ENTRY_SUCCESS, had_index); | |
| 1044 initialized_ = true; | |
| 1045 return net::OK; | |
| 1046 } | |
| 1047 | |
| 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)); | |
| 1057 | |
| 1058 bool has_crc32; | |
| 1059 uint32 read_crc32; | |
| 1060 int stream_0_size; | |
| 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; | |
| 1065 | |
| 1066 if (stream_0_size > out_entry_stat->data_size(1)) | |
| 1067 return net::ERR_FAILED; | |
| 1068 | |
| 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); | |
| 1073 | |
| 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]); | |
| 1079 int bytes_read = | |
| 1080 file->Read(file_offset, (*stream_0_data)->data(), stream_0_size); | |
| 1081 if (bytes_read != stream_0_size) | |
| 1082 return net::ERR_FAILED; | |
| 1083 | |
| 1084 // Check the CRC32. | |
| 1085 uint32 expected_crc32 = | |
| 1086 stream_0_size == 0 | |
| 1087 ? crc32(0, Z_NULL, 0) | |
| 1088 : crc32(crc32(0, Z_NULL, 0), | |
| 1089 reinterpret_cast<const Bytef*>((*stream_0_data)->data()), | |
| 1090 stream_0_size); | |
| 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; | |
| 1095 } | |
| 1096 *out_stream_0_crc32 = expected_crc32; | |
| 1097 RecordCheckEOFResult(cache_type_, CHECK_EOF_RESULT_SUCCESS); | |
| 1098 return net::OK; | |
| 1099 } | |
| 1100 | |
| 1101 int SimpleSynchronousEntry::GetEOFRecordData(int index, | |
| 1102 const SimpleEntryStat& entry_stat, | |
| 1103 bool* out_has_crc32, | |
| 1104 uint32* out_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; | |
| 1115 } | |
| 1116 | |
| 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; | |
| 1121 } | |
| 1122 | |
| 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); | |
| 1128 return net::OK; | |
| 1129 } | |
| 1130 | |
| 1131 void SimpleSynchronousEntry::Doom() const { | |
| 1132 DeleteFilesForEntryHash(path_, entry_hash_); | |
| 1133 } | |
| 1134 | |
| 1135 // static | |
| 1136 bool SimpleSynchronousEntry::DeleteFileForEntryHash( | |
| 1137 const FilePath& path, | |
| 1138 const uint64 entry_hash, | |
| 1139 const int file_index) { | |
| 1140 FilePath to_delete = path.AppendASCII( | |
| 1141 GetFilenameFromEntryHashAndFileIndex(entry_hash, file_index)); | |
| 1142 return simple_util::SimpleCacheDeleteFile(to_delete); | |
| 1143 } | |
| 1144 | |
| 1145 // static | |
| 1146 bool SimpleSynchronousEntry::DeleteFilesForEntryHash( | |
| 1147 const FilePath& path, | |
| 1148 const uint64 entry_hash) { | |
| 1149 bool result = true; | |
| 1150 for (int i = 0; i < kSimpleEntryFileCount; ++i) { | |
| 1151 if (!DeleteFileForEntryHash(path, entry_hash, i) && !CanOmitEmptyFile(i)) | |
| 1152 result = false; | |
| 1153 } | |
| 1154 FilePath to_delete = path.AppendASCII( | |
| 1155 GetSparseFilenameFromEntryHash(entry_hash)); | |
| 1156 simple_util::SimpleCacheDeleteFile(to_delete); | |
| 1157 return result; | |
| 1158 } | |
| 1159 | |
| 1160 void SimpleSynchronousEntry::RecordSyncCreateResult(CreateEntryResult result, | |
| 1161 bool had_index) { | |
| 1162 DCHECK_LT(result, CREATE_ENTRY_MAX); | |
| 1163 SIMPLE_CACHE_UMA(ENUMERATION, | |
| 1164 "SyncCreateResult", cache_type_, result, CREATE_ENTRY_MAX); | |
| 1165 if (had_index) { | |
| 1166 SIMPLE_CACHE_UMA(ENUMERATION, | |
| 1167 "SyncCreateResult_WithIndex", cache_type_, | |
| 1168 result, CREATE_ENTRY_MAX); | |
| 1169 } else { | |
| 1170 SIMPLE_CACHE_UMA(ENUMERATION, | |
| 1171 "SyncCreateResult_WithoutIndex", cache_type_, | |
| 1172 result, CREATE_ENTRY_MAX); | |
| 1173 } | |
| 1174 } | |
| 1175 | |
| 1176 FilePath SimpleSynchronousEntry::GetFilenameFromFileIndex(int file_index) { | |
| 1177 return path_.AppendASCII( | |
| 1178 GetFilenameFromEntryHashAndFileIndex(entry_hash_, file_index)); | |
| 1179 } | |
| 1180 | |
| 1181 bool SimpleSynchronousEntry::OpenSparseFileIfExists( | |
| 1182 int32* out_sparse_data_size) { | |
| 1183 DCHECK(!sparse_file_open()); | |
| 1184 | |
| 1185 FilePath filename = path_.AppendASCII( | |
| 1186 GetSparseFilenameFromEntryHash(entry_hash_)); | |
| 1187 int flags = File::FLAG_OPEN | File::FLAG_READ | File::FLAG_WRITE | | |
| 1188 File::FLAG_SHARE_DELETE; | |
| 1189 sparse_file_.Initialize(filename, flags); | |
| 1190 if (sparse_file_.IsValid()) | |
| 1191 return ScanSparseFile(out_sparse_data_size); | |
| 1192 | |
| 1193 return sparse_file_.error_details() == File::FILE_ERROR_NOT_FOUND; | |
| 1194 } | |
| 1195 | |
| 1196 bool SimpleSynchronousEntry::CreateSparseFile() { | |
| 1197 DCHECK(!sparse_file_open()); | |
| 1198 | |
| 1199 FilePath filename = path_.AppendASCII( | |
| 1200 GetSparseFilenameFromEntryHash(entry_hash_)); | |
| 1201 int flags = File::FLAG_CREATE | File::FLAG_READ | File::FLAG_WRITE | | |
| 1202 File::FLAG_SHARE_DELETE; | |
| 1203 sparse_file_.Initialize(filename, flags); | |
| 1204 if (!sparse_file_.IsValid()) | |
| 1205 return false; | |
| 1206 | |
| 1207 return InitializeSparseFile(); | |
| 1208 } | |
| 1209 | |
| 1210 void SimpleSynchronousEntry::CloseSparseFile() { | |
| 1211 DCHECK(sparse_file_open()); | |
| 1212 sparse_file_.Close(); | |
| 1213 } | |
| 1214 | |
| 1215 bool SimpleSynchronousEntry::TruncateSparseFile() { | |
| 1216 DCHECK(sparse_file_open()); | |
| 1217 | |
| 1218 int64 header_and_key_length = sizeof(SimpleFileHeader) + key_.size(); | |
| 1219 if (!sparse_file_.SetLength(header_and_key_length)) { | |
| 1220 DLOG(WARNING) << "Could not truncate sparse file"; | |
| 1221 return false; | |
| 1222 } | |
| 1223 | |
| 1224 sparse_ranges_.clear(); | |
| 1225 | |
| 1226 return true; | |
| 1227 } | |
| 1228 | |
| 1229 bool SimpleSynchronousEntry::InitializeSparseFile() { | |
| 1230 DCHECK(sparse_file_open()); | |
| 1231 | |
| 1232 SimpleFileHeader header; | |
| 1233 header.initial_magic_number = kSimpleInitialMagicNumber; | |
| 1234 header.version = kSimpleVersion; | |
| 1235 header.key_length = key_.size(); | |
| 1236 header.key_hash = base::Hash(key_); | |
| 1237 | |
| 1238 int header_write_result = | |
| 1239 sparse_file_.Write(0, reinterpret_cast<char*>(&header), sizeof(header)); | |
| 1240 if (header_write_result != sizeof(header)) { | |
| 1241 DLOG(WARNING) << "Could not write sparse file header"; | |
| 1242 return false; | |
| 1243 } | |
| 1244 | |
| 1245 int key_write_result = sparse_file_.Write(sizeof(header), key_.data(), | |
| 1246 key_.size()); | |
| 1247 if (key_write_result != implicit_cast<int>(key_.size())) { | |
| 1248 DLOG(WARNING) << "Could not write sparse file key"; | |
| 1249 return false; | |
| 1250 } | |
| 1251 | |
| 1252 sparse_ranges_.clear(); | |
| 1253 sparse_tail_offset_ = sizeof(header) + key_.size(); | |
| 1254 | |
| 1255 return true; | |
| 1256 } | |
| 1257 | |
| 1258 bool SimpleSynchronousEntry::ScanSparseFile(int32* out_sparse_data_size) { | |
| 1259 DCHECK(sparse_file_open()); | |
| 1260 | |
| 1261 int64 sparse_data_size = 0; | |
| 1262 | |
| 1263 SimpleFileHeader header; | |
| 1264 int header_read_result = | |
| 1265 sparse_file_.Read(0, reinterpret_cast<char*>(&header), sizeof(header)); | |
| 1266 if (header_read_result != sizeof(header)) { | |
| 1267 DLOG(WARNING) << "Could not read header from sparse file."; | |
| 1268 return false; | |
| 1269 } | |
| 1270 | |
| 1271 if (header.initial_magic_number != kSimpleInitialMagicNumber) { | |
| 1272 DLOG(WARNING) << "Sparse file magic number did not match."; | |
| 1273 return false; | |
| 1274 } | |
| 1275 | |
| 1276 if (header.version != kSimpleVersion) { | |
| 1277 DLOG(WARNING) << "Sparse file unreadable version."; | |
| 1278 return false; | |
| 1279 } | |
| 1280 | |
| 1281 sparse_ranges_.clear(); | |
| 1282 | |
| 1283 int64 range_header_offset = sizeof(header) + key_.size(); | |
| 1284 while (1) { | |
| 1285 SimpleFileSparseRangeHeader range_header; | |
| 1286 int range_header_read_result = | |
| 1287 sparse_file_.Read(range_header_offset, | |
| 1288 reinterpret_cast<char*>(&range_header), | |
| 1289 sizeof(range_header)); | |
| 1290 if (range_header_read_result == 0) | |
| 1291 break; | |
| 1292 if (range_header_read_result != sizeof(range_header)) { | |
| 1293 DLOG(WARNING) << "Could not read sparse range header."; | |
| 1294 return false; | |
| 1295 } | |
| 1296 | |
| 1297 if (range_header.sparse_range_magic_number != | |
| 1298 kSimpleSparseRangeMagicNumber) { | |
| 1299 DLOG(WARNING) << "Invalid sparse range header magic number."; | |
| 1300 return false; | |
| 1301 } | |
| 1302 | |
| 1303 SparseRange range; | |
| 1304 range.offset = range_header.offset; | |
| 1305 range.length = range_header.length; | |
| 1306 range.data_crc32 = range_header.data_crc32; | |
| 1307 range.file_offset = range_header_offset + sizeof(range_header); | |
| 1308 sparse_ranges_.insert(std::make_pair(range.offset, range)); | |
| 1309 | |
| 1310 range_header_offset += sizeof(range_header) + range.length; | |
| 1311 | |
| 1312 DCHECK_GE(sparse_data_size + range.length, sparse_data_size); | |
| 1313 sparse_data_size += range.length; | |
| 1314 } | |
| 1315 | |
| 1316 *out_sparse_data_size = static_cast<int32>(sparse_data_size); | |
| 1317 sparse_tail_offset_ = range_header_offset; | |
| 1318 | |
| 1319 return true; | |
| 1320 } | |
| 1321 | |
| 1322 bool SimpleSynchronousEntry::ReadSparseRange(const SparseRange* range, | |
| 1323 int offset, int len, char* buf) { | |
| 1324 DCHECK(range); | |
| 1325 DCHECK(buf); | |
| 1326 DCHECK_LE(offset, range->length); | |
| 1327 DCHECK_LE(offset + len, range->length); | |
| 1328 | |
| 1329 int bytes_read = sparse_file_.Read(range->file_offset + offset, buf, len); | |
| 1330 if (bytes_read < len) { | |
| 1331 DLOG(WARNING) << "Could not read sparse range."; | |
| 1332 return false; | |
| 1333 } | |
| 1334 | |
| 1335 // If we read the whole range and we have a crc32, check it. | |
| 1336 if (offset == 0 && len == range->length && range->data_crc32 != 0) { | |
| 1337 uint32 actual_crc32 = crc32(crc32(0L, Z_NULL, 0), | |
| 1338 reinterpret_cast<const Bytef*>(buf), | |
| 1339 len); | |
| 1340 if (actual_crc32 != range->data_crc32) { | |
| 1341 DLOG(WARNING) << "Sparse range crc32 mismatch."; | |
| 1342 return false; | |
| 1343 } | |
| 1344 } | |
| 1345 // TODO(ttuttle): Incremental crc32 calculation? | |
| 1346 | |
| 1347 return true; | |
| 1348 } | |
| 1349 | |
| 1350 bool SimpleSynchronousEntry::WriteSparseRange(SparseRange* range, | |
| 1351 int offset, int len, | |
| 1352 const char* buf) { | |
| 1353 DCHECK(range); | |
| 1354 DCHECK(buf); | |
| 1355 DCHECK_LE(offset, range->length); | |
| 1356 DCHECK_LE(offset + len, range->length); | |
| 1357 | |
| 1358 uint32 new_crc32 = 0; | |
| 1359 if (offset == 0 && len == range->length) { | |
| 1360 new_crc32 = crc32(crc32(0L, Z_NULL, 0), | |
| 1361 reinterpret_cast<const Bytef*>(buf), | |
| 1362 len); | |
| 1363 } | |
| 1364 | |
| 1365 if (new_crc32 != range->data_crc32) { | |
| 1366 range->data_crc32 = new_crc32; | |
| 1367 | |
| 1368 SimpleFileSparseRangeHeader header; | |
| 1369 header.sparse_range_magic_number = kSimpleSparseRangeMagicNumber; | |
| 1370 header.offset = range->offset; | |
| 1371 header.length = range->length; | |
| 1372 header.data_crc32 = range->data_crc32; | |
| 1373 | |
| 1374 int bytes_written = sparse_file_.Write(range->file_offset - sizeof(header), | |
| 1375 reinterpret_cast<char*>(&header), | |
| 1376 sizeof(header)); | |
| 1377 if (bytes_written != implicit_cast<int>(sizeof(header))) { | |
| 1378 DLOG(WARNING) << "Could not rewrite sparse range header."; | |
| 1379 return false; | |
| 1380 } | |
| 1381 } | |
| 1382 | |
| 1383 int bytes_written = sparse_file_.Write(range->file_offset + offset, buf, len); | |
| 1384 if (bytes_written < len) { | |
| 1385 DLOG(WARNING) << "Could not write sparse range."; | |
| 1386 return false; | |
| 1387 } | |
| 1388 | |
| 1389 return true; | |
| 1390 } | |
| 1391 | |
| 1392 bool SimpleSynchronousEntry::AppendSparseRange(int64 offset, | |
| 1393 int len, | |
| 1394 const char* buf) { | |
| 1395 DCHECK_GE(offset, 0); | |
| 1396 DCHECK_GT(len, 0); | |
| 1397 DCHECK(buf); | |
| 1398 | |
| 1399 uint32 data_crc32 = crc32(crc32(0L, Z_NULL, 0), | |
| 1400 reinterpret_cast<const Bytef*>(buf), | |
| 1401 len); | |
| 1402 | |
| 1403 SimpleFileSparseRangeHeader header; | |
| 1404 header.sparse_range_magic_number = kSimpleSparseRangeMagicNumber; | |
| 1405 header.offset = offset; | |
| 1406 header.length = len; | |
| 1407 header.data_crc32 = data_crc32; | |
| 1408 | |
| 1409 int bytes_written = sparse_file_.Write(sparse_tail_offset_, | |
| 1410 reinterpret_cast<char*>(&header), | |
| 1411 sizeof(header)); | |
| 1412 if (bytes_written != implicit_cast<int>(sizeof(header))) { | |
| 1413 DLOG(WARNING) << "Could not append sparse range header."; | |
| 1414 return false; | |
| 1415 } | |
| 1416 sparse_tail_offset_ += bytes_written; | |
| 1417 | |
| 1418 bytes_written = sparse_file_.Write(sparse_tail_offset_, buf, len); | |
| 1419 if (bytes_written < len) { | |
| 1420 DLOG(WARNING) << "Could not append sparse range data."; | |
| 1421 return false; | |
| 1422 } | |
| 1423 int64 data_file_offset = sparse_tail_offset_; | |
| 1424 sparse_tail_offset_ += bytes_written; | |
| 1425 | |
| 1426 SparseRange range; | |
| 1427 range.offset = offset; | |
| 1428 range.length = len; | |
| 1429 range.data_crc32 = data_crc32; | |
| 1430 range.file_offset = data_file_offset; | |
| 1431 sparse_ranges_.insert(std::make_pair(offset, range)); | |
| 1432 | |
| 1433 return true; | |
| 1434 } | |
| 1435 | |
| 1436 } // namespace disk_cache | |
| OLD | NEW |