OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2012 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/v3/backend_worker.h" |
| 6 |
| 7 #include "base/bind.h" |
| 8 #include "base/file_util.h" |
| 9 #include "base/message_loop.h" |
| 10 #include "base/stringprintf.h" |
| 11 #include "net/base/net_errors.h" |
| 12 #include "net/disk_cache/cache_util.h" |
| 13 #include "net/disk_cache/errors.h" |
| 14 #include "net/disk_cache/experiments.h" |
| 15 #include "net/disk_cache/mapped_file.h" |
| 16 #include "net/disk_cache/v3/backend_work_item.h" |
| 17 #include "net/disk_cache/v3/disk_format_v3.h" |
| 18 |
| 19 using base::Time; |
| 20 using base::TimeDelta; |
| 21 using base::TimeTicks; |
| 22 |
| 23 namespace { |
| 24 |
| 25 const char kIndexName[] = "index"; |
| 26 const char kIndexBackupName[] = "index_bak"; |
| 27 const char kTable1Name[] = "index_tb1"; |
| 28 const char kTable2Name[] = "index_tb2"; |
| 29 const char kTable2TempName[] = "index_tb2_tmp"; |
| 30 const int kMaxOldFolders = 100; |
| 31 |
| 32 // Seems like ~240 MB correspond to less than 50k entries for 99% of the people. |
| 33 // Note that the actual target is to keep the index table load factor under 55% |
| 34 // for most users. |
| 35 const int k64kEntriesStore = 240 * 1000 * 1000; |
| 36 const int kBaseTableLen = 64 * 1024; |
| 37 const int kDefaultCacheSize = 80 * 1024 * 1024; |
| 38 |
| 39 // Avoid trimming the cache for the first 5 minutes (10 timer ticks). |
| 40 const int kTrimDelay = 10; |
| 41 |
| 42 int DesiredIndexTableLen(int32 storage_size) { |
| 43 if (storage_size <= k64kEntriesStore) |
| 44 return kBaseTableLen; |
| 45 if (storage_size <= k64kEntriesStore * 2) |
| 46 return kBaseTableLen * 2; |
| 47 if (storage_size <= k64kEntriesStore * 4) |
| 48 return kBaseTableLen * 4; |
| 49 if (storage_size <= k64kEntriesStore * 8) |
| 50 return kBaseTableLen * 8; |
| 51 |
| 52 // The biggest storage_size for int32 requires a 4 MB table. |
| 53 return kBaseTableLen * 16; |
| 54 } |
| 55 |
| 56 int MaxStorageSizeForTable(int table_len) { |
| 57 return table_len * (k64kEntriesStore / kBaseTableLen); |
| 58 } |
| 59 |
| 60 size_t GetIndexSize(int table_len) { |
| 61 // |
| 62 //size_t table_size = sizeof(disk_cache::CacheAddr) * table_len; |
| 63 //return sizeof(disk_cache::IndexHeaderV3) + table_size; |
| 64 return 0; |
| 65 } |
| 66 |
| 67 size_t GetIndexBitmapSize(int table_len) { |
| 68 DCHECK_LT(table_len, 1 << 22); |
| 69 size_t base_bits = disk_cache::kBaseBitmapBytes * 8; |
| 70 if (table_len < static_cast<int>(base_bits)) |
| 71 return sizeof(disk_cache::IndexBitmap); |
| 72 |
| 73 size_t extra_pages = (table_len / 8) - disk_cache::kBaseBitmapBytes; |
| 74 extra_pages = (extra_pages + 4095) / 4096; |
| 75 return sizeof(disk_cache::IndexBitmap) + extra_pages * 4096; |
| 76 } |
| 77 |
| 78 // ------------------------------------------------------------------------ |
| 79 |
| 80 // Sets group for the current experiment. Returns false if the files should be |
| 81 // discarded. |
| 82 bool InitExperiment(disk_cache::IndexHeaderV3* header) { |
| 83 header->experiment = disk_cache::NO_EXPERIMENT; |
| 84 return true; |
| 85 } |
| 86 |
| 87 } // namespace |
| 88 |
| 89 // ------------------------------------------------------------------------ |
| 90 |
| 91 namespace disk_cache { |
| 92 |
| 93 BackendImplV3::Worker::Worker(const base::FilePath& path, |
| 94 base::MessageLoopProxy* main_thread) |
| 95 : path_(path), |
| 96 main_thread_(main_thread), |
| 97 cleanup_work_item_(NULL), |
| 98 init_(false), |
| 99 doubling_index_(false), |
| 100 user_flags_(0) { |
| 101 } |
| 102 |
| 103 int BackendImplV3::Worker::Init(uint32 flags, scoped_ptr<InitResult>* result) { |
| 104 DCHECK(!init_); |
| 105 if (init_) |
| 106 return ERR_INIT_FAILED; |
| 107 |
| 108 user_flags_ = flags; |
| 109 result->reset(new InitResult); |
| 110 |
| 111 bool create_files = false; |
| 112 if (!InitBackingStore(&create_files)) |
| 113 return ERR_STORAGE_ERROR; |
| 114 |
| 115 init_ = true; |
| 116 if (!LoadIndex(result->get())) |
| 117 return ERR_INIT_FAILED; |
| 118 |
| 119 int rv = ERR_NO_ERROR; |
| 120 IndexHeaderV3* index = |
| 121 reinterpret_cast<IndexHeaderV3*>(index_header_->buffer()); |
| 122 if (create_files || !index->num_entries) |
| 123 rv = ERR_CACHE_CREATED; |
| 124 |
| 125 if (create_files && (flags & EVICTION_V2)) { |
| 126 index->flags |= CACHE_EVICTION_2; |
| 127 } |
| 128 |
| 129 if (!(flags & BASIC_UNIT_TEST) && !InitExperiment(index)) |
| 130 return ERR_INIT_FAILED; |
| 131 |
| 132 if (index->crash != 0) |
| 133 rv = ERR_PREVIOUS_CRASH; |
| 134 index->crash = 1; |
| 135 |
| 136 block_files_.reset(new BlockFiles(path_)); |
| 137 if (flags & BASIC_UNIT_TEST) |
| 138 block_files_->UseSmallSizeIncrementsForTest(); |
| 139 |
| 140 if (!block_files_->Init(create_files, kFirstAdditionalBlockFileV3)) |
| 141 return ERR_INIT_FAILED; |
| 142 |
| 143 block_files_->GetBitmaps(index->max_block_file, |
| 144 &result->get()->block_bitmaps); |
| 145 index->max_block_file = static_cast<int>(result->get()->block_bitmaps.size()); |
| 146 |
| 147 if (!InitStats(index, result->get())) |
| 148 return ERR_INIT_FAILED; |
| 149 |
| 150 #if defined(STRESS_CACHE_EXTENDED_VALIDATION) |
| 151 trace_object_->EnableTracing(false); |
| 152 int sc = SelfCheck(); |
| 153 if (sc < 0 && sc != ERR_NUM_ENTRIES_MISMATCH) |
| 154 NOTREACHED(); |
| 155 trace_object_->EnableTracing(true); |
| 156 #endif |
| 157 |
| 158 return rv; |
| 159 } |
| 160 |
| 161 int BackendImplV3::Worker::Restart(uint32 flags, |
| 162 scoped_ptr<InitResult>* result) { |
| 163 Trace("Worker::Restart"); |
| 164 if (init_) { |
| 165 init_ = false; |
| 166 } |
| 167 |
| 168 CloseFiles(); |
| 169 DeleteCache(path_, false); |
| 170 |
| 171 return Init(flags, result); |
| 172 } |
| 173 |
| 174 int BackendImplV3::Worker::GrowIndex(uint32 flags, |
| 175 scoped_ptr<InitResult>* result) { |
| 176 Trace("Worker::GrowIndex, flags 0x%x", flags); |
| 177 if (!init_) |
| 178 return ERR_OPERATION_FAILED; |
| 179 |
| 180 if (flags & WorkItem::WORK_COMPLETE) { |
| 181 index_header_ = big_index_header_; |
| 182 big_index_header_ = NULL; |
| 183 if (big_main_table_) { |
| 184 main_table_ = big_main_table_; |
| 185 big_main_table_ = NULL; |
| 186 } |
| 187 if (!big_extra_temp_table_) |
| 188 extra_table_ = big_extra_table_; |
| 189 big_extra_table_ = NULL; |
| 190 |
| 191 // If the index takes time to move the cells, it creates a new work item to |
| 192 // notify completion, which executes this code. |
| 193 if (big_extra_temp_table_) |
| 194 return GrowDone(); |
| 195 |
| 196 return ERR_NO_ERROR; |
| 197 } |
| 198 |
| 199 IndexHeaderV3* header = |
| 200 reinterpret_cast<IndexHeaderV3*>(index_header_->buffer()); |
| 201 |
| 202 int current_main_len = header->table_len / kIndexTablesize * kIndexTablesize; |
| 203 int step_size = std::min(8192, current_main_len / 8); |
| 204 if (user_flags_ & BASIC_UNIT_TEST) |
| 205 step_size = 8; |
| 206 if ((user_flags_ & UNIT_TEST_MODE) && !doubling_index_) |
| 207 step_size = (header->table_len * 3 / 2) & 0x7ffffff0; |
| 208 int new_len = header->table_len + step_size; |
| 209 |
| 210 bool double_index = false; |
| 211 if (!doubling_index_) { |
| 212 DCHECK(!big_extra_table_); |
| 213 DCHECK(!big_main_table_); |
| 214 double_index = (new_len / kIndexTablesize != |
| 215 header->table_len / kIndexTablesize); |
| 216 } |
| 217 |
| 218 int extra_len = new_len - kIndexTablesize; |
| 219 if (double_index) { |
| 220 // We double the table when the extra table is about to reach the size of |
| 221 // the main table. That means that right after this, the new extra table |
| 222 // should be between 19% and 23% of the main table so we start with 25%. |
| 223 extra_len = std::min(8192, current_main_len / 4); |
| 224 extra_len = (user_flags_ & BASIC_UNIT_TEST) ? 128 : extra_len; |
| 225 int main_len = (header->table_len / kIndexTablesize + 1) * kIndexTablesize; |
| 226 new_len = main_len + extra_len; |
| 227 |
| 228 if (!CreateExtraTable(extra_len * kBytesPerCell)) |
| 229 return ERR_OPERATION_FAILED; |
| 230 |
| 231 if (!main_table_->SetLength(main_len * kBytesPerCell)) |
| 232 return ERR_OPERATION_FAILED; |
| 233 } else if (doubling_index_) { |
| 234 if (!big_extra_temp_table_->SetLength(extra_len * kBytesPerCell)) |
| 235 return ERR_OPERATION_FAILED; |
| 236 } else { |
| 237 if (!extra_table_->SetLength(extra_len * kBytesPerCell)) |
| 238 return ERR_OPERATION_FAILED; |
| 239 } |
| 240 |
| 241 if (!index_header_->SetLength(GetIndexBitmapSize(new_len))) |
| 242 return ERR_OPERATION_FAILED; |
| 243 |
| 244 scoped_refptr<MappedFile> big_index_header = new MappedFile(); |
| 245 if (!big_index_header->Init(path_.AppendASCII(kIndexName), 0)) { |
| 246 LOG(ERROR) << "Unable to remap index"; |
| 247 return ERR_OPERATION_FAILED; |
| 248 } |
| 249 |
| 250 scoped_refptr<MappedFile> big_extra_table = new MappedFile(); |
| 251 const char* extra_name = (double_index || doubling_index_) ? kTable2TempName : |
| 252 kTable2Name; |
| 253 if (!big_extra_table->Init(path_.AppendASCII(extra_name), 0)) { |
| 254 LOG(ERROR) << "Unable to remap index_tb2"; |
| 255 return ERR_OPERATION_FAILED; |
| 256 } |
| 257 |
| 258 if (double_index) { |
| 259 scoped_refptr<MappedFile> big_main_table = new MappedFile(); |
| 260 if (!big_main_table->Init(path_.AppendASCII(kTable1Name), 0)) { |
| 261 LOG(ERROR) << "Unable to remap index_tb1"; |
| 262 return ERR_OPERATION_FAILED; |
| 263 } |
| 264 big_main_table_.swap(big_main_table); |
| 265 |
| 266 // Grab an extra reference to the new extra table that can be used for an |
| 267 // extended period, while the index is being rebuilt. The normal reference |
| 268 // (big_extra_table_) will be released when the work item is completed, but |
| 269 // that doesn't mean the index is done with it. |
| 270 // Note that we are able to process slow grow requests even when the index |
| 271 // is being doubled. |
| 272 big_extra_temp_table_ = big_extra_table; |
| 273 } |
| 274 big_index_header_.swap(big_index_header); |
| 275 big_extra_table_.swap(big_extra_table); |
| 276 |
| 277 header = reinterpret_cast<IndexHeaderV3*>(big_index_header_->buffer()); |
| 278 header->table_len = new_len; |
| 279 |
| 280 result->reset(new InitResult); |
| 281 result->get()->main_table = NULL; |
| 282 |
| 283 result->get()->index_bitmap = |
| 284 reinterpret_cast<IndexBitmap*>(big_index_header_->buffer()); |
| 285 result->get()->extra_table = |
| 286 reinterpret_cast<IndexBucket*>(big_extra_table_->buffer()); |
| 287 |
| 288 if (double_index) { |
| 289 result->get()->main_table = |
| 290 reinterpret_cast<IndexBucket*>(big_main_table_->buffer()); |
| 291 doubling_index_ = true; |
| 292 } |
| 293 |
| 294 return ERR_NO_ERROR; |
| 295 } |
| 296 |
| 297 int BackendImplV3::Worker::GrowFiles(uint32 flags, |
| 298 scoped_ptr<InitResult>* result) { |
| 299 Trace("Worker::GrowFiles, flags 0x%x", flags); |
| 300 if (!init_) |
| 301 return ERR_OPERATION_FAILED; |
| 302 |
| 303 if (flags & WorkItem::WORK_COMPLETE) { |
| 304 block_files_.reset(); |
| 305 block_files_.swap(big_block_files_); |
| 306 return ERR_NO_ERROR; |
| 307 } |
| 308 |
| 309 big_block_files_.reset(new BlockFiles(path_)); |
| 310 if (user_flags_ & BASIC_UNIT_TEST) |
| 311 big_block_files_->UseSmallSizeIncrementsForTest(); |
| 312 |
| 313 if (!big_block_files_->Init(false, kFirstAdditionalBlockFileV3)) |
| 314 return ERR_INIT_FAILED; |
| 315 |
| 316 IndexHeaderV3* index = |
| 317 reinterpret_cast<IndexHeaderV3*>(index_header_->buffer()); |
| 318 |
| 319 result->reset(new InitResult); |
| 320 big_block_files_->GetBitmaps(index->max_block_file, |
| 321 &result->get()->block_bitmaps); |
| 322 index->max_block_file = static_cast<int>(result->get()->block_bitmaps.size()); |
| 323 return ERR_NO_ERROR; |
| 324 } |
| 325 |
| 326 int BackendImplV3::Worker::Delete(Addr address) { |
| 327 if (address.is_block_file()) |
| 328 return ERR_OPERATION_FAILED; |
| 329 |
| 330 if (DeleteCacheFile(GetFileName(address))) |
| 331 return ERR_NO_ERROR; |
| 332 |
| 333 return ERR_OPERATION_FAILED; |
| 334 } |
| 335 |
| 336 int BackendImplV3::Worker::Close(Addr address) { |
| 337 if (address.is_block_file()) |
| 338 return ERR_OPERATION_FAILED; |
| 339 |
| 340 FilesMap::iterator it = files_.find(address.value()); |
| 341 if (it != files_.end()) |
| 342 files_.erase(it); |
| 343 |
| 344 return ERR_NO_ERROR; |
| 345 } |
| 346 |
| 347 void BackendImplV3::Worker::OnDoWork(WorkItem* work_item) { |
| 348 if (work_item->type() == WorkItem::WORK_CLEANUP) |
| 349 return Cleanup(work_item); |
| 350 |
| 351 work_item->Start(this); |
| 352 } |
| 353 |
| 354 void BackendImplV3::Worker::DoneWithItem(WorkItem* work_item) { |
| 355 bool rv = main_thread_->PostTask(FROM_HERE, |
| 356 base::Bind(&WorkItem::OnDone, work_item)); |
| 357 DCHECK(rv); |
| 358 } |
| 359 |
| 360 File* BackendImplV3::Worker::GetBackingFile(Addr address, bool for_write) { |
| 361 disk_cache::File* file; |
| 362 if (address.is_separate_file()) |
| 363 file = GetExternalFile(address, for_write); |
| 364 else |
| 365 file = block_files_->GetFile(address); |
| 366 return file; |
| 367 } |
| 368 |
| 369 bool BackendImplV3::Worker::IsValid() { |
| 370 return init_; |
| 371 } |
| 372 |
| 373 // ------------------------------------------------------------------------ |
| 374 |
| 375 BackendImplV3::Worker::~Worker() { |
| 376 if (cleanup_work_item_) |
| 377 main_thread_->PostTask(FROM_HERE, |
| 378 base::Bind(&WorkItem::OnDone, cleanup_work_item_)); |
| 379 } |
| 380 |
| 381 void BackendImplV3::Worker::Cleanup(WorkItem* work_item) { |
| 382 Trace("Worker::Cleanup"); |
| 383 if (!work_item->user_callback().is_null()) |
| 384 cleanup_work_item_ = work_item; |
| 385 |
| 386 if (init_) { |
| 387 IndexHeaderV3* index = |
| 388 reinterpret_cast<IndexHeaderV3*>(index_header_->buffer()); |
| 389 index->crash = 0; |
| 390 } |
| 391 |
| 392 CloseFiles(); |
| 393 init_ = false; |
| 394 |
| 395 if (work_item->user_callback().is_null()) { |
| 396 // This is the only message we don't return to the main thread, we are done |
| 397 // with the work item for good. |
| 398 work_item->Release(); |
| 399 } |
| 400 } |
| 401 |
| 402 void BackendImplV3::Worker::CloseFiles() { |
| 403 index_header_ = NULL; |
| 404 main_table_ = NULL; |
| 405 extra_table_ = NULL; |
| 406 index_backup_ = NULL; |
| 407 block_files_->CloseFiles(); |
| 408 files_.clear(); |
| 409 |
| 410 big_index_header_ = NULL; |
| 411 big_main_table_ = NULL; |
| 412 big_extra_table_ = NULL; |
| 413 big_extra_temp_table_ = NULL; |
| 414 if (big_block_files_.get()) |
| 415 big_block_files_->CloseFiles(); |
| 416 } |
| 417 |
| 418 File* BackendImplV3::Worker::GetExternalFile(Addr address, bool for_write) { |
| 419 FilesMap::iterator it = files_.find(address.value()); |
| 420 if (it != files_.end()) |
| 421 return it->second; |
| 422 |
| 423 scoped_refptr<disk_cache::File> file(new disk_cache::File(false)); |
| 424 if (for_write) |
| 425 file->set_force_creation(); |
| 426 if (file->Init(GetFileName(address))) |
| 427 files_[address.value()] = file.get(); |
| 428 else |
| 429 file = NULL; |
| 430 |
| 431 return file; |
| 432 } |
| 433 |
| 434 base::FilePath BackendImplV3::Worker::GetFileName(Addr address) const { |
| 435 if (!address.is_separate_file() || !address.is_initialized()) { |
| 436 NOTREACHED(); |
| 437 return base::FilePath(); |
| 438 } |
| 439 |
| 440 std::string tmp = base::StringPrintf("f_%06x", address.FileNumber()); |
| 441 return path_.AppendASCII(tmp); |
| 442 } |
| 443 |
| 444 // We just created a new file so we're going to write the header and set the |
| 445 // file length to include the hash table (zero filled). |
| 446 bool BackendImplV3::Worker::CreateBackingStore(disk_cache::File* file) { |
| 447 IndexHeaderV3 header; |
| 448 |
| 449 // Start with 12.5% of the size of the main table. |
| 450 int extra_len = (user_flags_ & BASIC_UNIT_TEST) ? 8 : kIndexTablesize / 8; |
| 451 header.table_len = kIndexTablesize + extra_len; |
| 452 header.max_bucket = kIndexTablesize / 4 - 1; |
| 453 |
| 454 header.create_time = Time::Now().ToInternalValue(); |
| 455 header.base_time = (Time::Now() - TimeDelta::FromDays(20)).ToInternalValue(); |
| 456 |
| 457 if (!file->Write(&header, sizeof(header), 0)) |
| 458 return false; |
| 459 |
| 460 if (!file->SetLength(GetIndexBitmapSize(header.table_len))) |
| 461 return false; |
| 462 |
| 463 int flags = base::PLATFORM_FILE_READ | |
| 464 base::PLATFORM_FILE_WRITE | |
| 465 base::PLATFORM_FILE_CREATE | |
| 466 base::PLATFORM_FILE_EXCLUSIVE_WRITE; |
| 467 |
| 468 base::FilePath name = path_.AppendASCII(kIndexBackupName); |
| 469 scoped_refptr<disk_cache::File> file2(new disk_cache::File( |
| 470 base::CreatePlatformFile(name, flags, NULL, NULL))); |
| 471 |
| 472 if (!file2->IsValid()) |
| 473 return false; |
| 474 |
| 475 if (!file2->Write(&header, sizeof(header), 0)) |
| 476 return false; |
| 477 |
| 478 if (!file2->SetLength(GetIndexBitmapSize(header.table_len))) |
| 479 return false; |
| 480 |
| 481 name = path_.AppendASCII(kTable1Name); |
| 482 file2 = new disk_cache::File(base::CreatePlatformFile(name, flags, NULL, |
| 483 NULL)); |
| 484 if (!file2->IsValid()) |
| 485 return false; |
| 486 |
| 487 if (!file2->SetLength(kIndexTablesize * kBytesPerCell)) |
| 488 return false; |
| 489 |
| 490 name = path_.AppendASCII(kTable2Name); |
| 491 file2 = new disk_cache::File(base::CreatePlatformFile(name, flags, NULL, |
| 492 NULL)); |
| 493 if (!file2->IsValid()) |
| 494 return false; |
| 495 |
| 496 if (!file2->SetLength(extra_len * kBytesPerCell)) |
| 497 return false; |
| 498 |
| 499 return true; |
| 500 } |
| 501 |
| 502 bool BackendImplV3::Worker::CreateExtraTable(int extra_len) { |
| 503 int flags = base::PLATFORM_FILE_READ | |
| 504 base::PLATFORM_FILE_WRITE | |
| 505 base::PLATFORM_FILE_CREATE | |
| 506 base::PLATFORM_FILE_EXCLUSIVE_WRITE; |
| 507 |
| 508 base::FilePath name = path_.AppendASCII(kTable2TempName); |
| 509 scoped_refptr<disk_cache::File> file(new disk_cache::File( |
| 510 base::CreatePlatformFile(name, flags, NULL, NULL))); |
| 511 if (!file->IsValid()) |
| 512 return false; |
| 513 |
| 514 if (!file->SetLength(extra_len * kBytesPerCell)) |
| 515 return false; |
| 516 |
| 517 return true; |
| 518 } |
| 519 |
| 520 bool BackendImplV3::Worker::InitBackingStore(bool* file_created) { |
| 521 if (!file_util::CreateDirectory(path_)) |
| 522 return false; |
| 523 |
| 524 base::FilePath index_name = path_.AppendASCII(kIndexName); |
| 525 |
| 526 int flags = base::PLATFORM_FILE_READ | |
| 527 base::PLATFORM_FILE_WRITE | |
| 528 base::PLATFORM_FILE_OPEN_ALWAYS | |
| 529 base::PLATFORM_FILE_EXCLUSIVE_WRITE; |
| 530 scoped_refptr<disk_cache::File> file(new disk_cache::File( |
| 531 base::CreatePlatformFile(index_name, flags, file_created, NULL))); |
| 532 |
| 533 if (!file->IsValid()) |
| 534 return false; |
| 535 |
| 536 bool ret = true; |
| 537 if (*file_created) |
| 538 ret = CreateBackingStore(file); |
| 539 |
| 540 file = NULL; |
| 541 if (!ret) |
| 542 return false; |
| 543 |
| 544 index_header_ = new MappedFile(); |
| 545 if (!index_header_->Init(index_name, 0)) { |
| 546 LOG(ERROR) << "Unable to map index"; |
| 547 return false; |
| 548 } |
| 549 |
| 550 if (index_header_->GetLength() < sizeof(IndexBitmap)) { |
| 551 // We verify this again on CheckIndex() but it's easier to make sure now |
| 552 // that the header is there. |
| 553 LOG(ERROR) << "Corrupt index file"; |
| 554 return false; |
| 555 } |
| 556 |
| 557 main_table_ = new MappedFile(); |
| 558 if (!main_table_->Init(path_.AppendASCII(kTable1Name), 0)) { |
| 559 LOG(ERROR) << "Unable to map index_tb1"; |
| 560 return false; |
| 561 } |
| 562 |
| 563 extra_table_ = new MappedFile(); |
| 564 if (!extra_table_->Init(path_.AppendASCII(kTable2Name), 0)) { |
| 565 LOG(ERROR) << "Unable to map index_tb2"; |
| 566 return false; |
| 567 } |
| 568 |
| 569 index_backup_ = new MappedFile(); |
| 570 if (!index_backup_->Init(path_.AppendASCII(kIndexBackupName), 0)) { |
| 571 LOG(ERROR) << "Unable to map index_bak"; |
| 572 return false; |
| 573 } |
| 574 |
| 575 return true; |
| 576 } |
| 577 |
| 578 bool BackendImplV3::Worker::LoadIndex(InitResult* init_result) { |
| 579 init_result->index_bitmap = |
| 580 reinterpret_cast<IndexBitmap*>(index_header_->buffer()); |
| 581 init_result->main_table = |
| 582 reinterpret_cast<IndexBucket*>(main_table_->buffer()); |
| 583 init_result->extra_table = |
| 584 reinterpret_cast<IndexBucket*>(extra_table_->buffer()); |
| 585 |
| 586 if (!CheckIndexFile(index_header_)) |
| 587 return false; |
| 588 |
| 589 if (!CheckIndexFile(index_backup_)) |
| 590 return false; |
| 591 |
| 592 IndexHeaderV3& header = init_result->index_bitmap->header; |
| 593 |
| 594 size_t extra_table_len = header.table_len % kIndexTablesize; |
| 595 size_t main_table_len = (kIndexTablesize - extra_table_len) * kBytesPerCell; |
| 596 extra_table_len *= kBytesPerCell; |
| 597 |
| 598 if (main_table_->GetLength() < main_table_len || |
| 599 extra_table_->GetLength() < extra_table_len) { |
| 600 LOG(ERROR) << "Truncated table"; |
| 601 return false; |
| 602 } |
| 603 |
| 604 IndexBitmap* index = reinterpret_cast<IndexBitmap*>(index_backup_->buffer()); |
| 605 |
| 606 init_result->backup_header.reset(new IndexHeaderV3); |
| 607 memcpy(init_result->backup_header.get(), &index->header, |
| 608 sizeof(index->header)); |
| 609 |
| 610 size_t bitmap_len = GetIndexBitmapSize(index->header.table_len) - |
| 611 sizeof(index->header); |
| 612 init_result->backup_bitmap.reset(new uint32[bitmap_len / 4]); |
| 613 memcpy(init_result->backup_bitmap.get(), &index->bitmap, bitmap_len); |
| 614 |
| 615 // Close the backup. |
| 616 index_backup_ = NULL; |
| 617 return true; |
| 618 } |
| 619 |
| 620 bool BackendImplV3::Worker::CheckIndexFile(MappedFile* file) { |
| 621 size_t current_size = file->GetLength(); |
| 622 if (current_size < sizeof(IndexBitmap)) { |
| 623 LOG(ERROR) << "Corrupt Index file"; |
| 624 return false; |
| 625 } |
| 626 |
| 627 IndexHeaderV3* header = reinterpret_cast<IndexHeaderV3*>(file->buffer()); |
| 628 |
| 629 if (kIndexMagic != header->magic || |
| 630 kCurrentVersion != header->version) { |
| 631 LOG(ERROR) << "Invalid file version or magic"; |
| 632 return false; |
| 633 } |
| 634 |
| 635 if (header->table_len <= 0 || header->table_len > 1 << 22) { |
| 636 LOG(ERROR) << "Invalid table size"; |
| 637 return false; |
| 638 } |
| 639 |
| 640 int min_mask = (user_flags_ & BASIC_UNIT_TEST) ? 0x3 : 0xff; |
| 641 if (current_size < GetIndexBitmapSize(header->table_len) || |
| 642 header->table_len & (min_mask)) { |
| 643 LOG(ERROR) << "Corrupt Index file"; |
| 644 return false; |
| 645 } |
| 646 |
| 647 //AdjustMaxCacheSize(header->table_len); |
| 648 |
| 649 #if !defined(NET_BUILD_STRESS_CACHE) |
| 650 if (header->num_bytes < 0 || header->max_bytes < 0 || |
| 651 header->num_bytes > header->max_bytes + kDefaultCacheSize) { |
| 652 LOG(ERROR) << "Invalid cache size"; |
| 653 return false; |
| 654 } |
| 655 #endif |
| 656 |
| 657 if (header->num_entries < 0) { |
| 658 LOG(ERROR) << "Invalid number of entries"; |
| 659 return false; |
| 660 } |
| 661 |
| 662 // Load the table into memory with a single read. |
| 663 //scoped_array<char> buf(new char[current_size]); |
| 664 //return index_->Read(buf.get(), current_size, 0); |
| 665 |
| 666 return true; |
| 667 } |
| 668 |
| 669 bool BackendImplV3::Worker::InitStats(IndexHeaderV3* index, |
| 670 InitResult* result) { |
| 671 Addr address(index->stats); |
| 672 if (!address.is_initialized()) |
| 673 return true; |
| 674 |
| 675 if (!address.is_block_file()) { |
| 676 NOTREACHED(); |
| 677 return false; |
| 678 } |
| 679 |
| 680 int size = address.num_blocks() * address.BlockSize(); |
| 681 |
| 682 // Load the required data. |
| 683 MappedFile* file = GetMappedFile(address); |
| 684 if (!file) |
| 685 return false; |
| 686 |
| 687 scoped_ptr<char[]> data(new char[size]); |
| 688 size_t offset = address.start_block() * address.BlockSize() + |
| 689 kBlockHeaderSize; |
| 690 if (!file->Read(data.get(), size, offset)) |
| 691 return false; |
| 692 |
| 693 result->stats_data = data.Pass(); |
| 694 return true; |
| 695 } |
| 696 |
| 697 int BackendImplV3::Worker::GrowDone() { |
| 698 Trace("Worker::GrowDone"); |
| 699 if (!init_) |
| 700 return ERR_OPERATION_FAILED; |
| 701 |
| 702 DCHECK(doubling_index_); |
| 703 doubling_index_ = false; |
| 704 |
| 705 extra_table_ = big_extra_temp_table_; |
| 706 big_extra_temp_table_ = NULL; |
| 707 |
| 708 return ERR_NO_ERROR; |
| 709 } |
| 710 |
| 711 } // namespace disk_cache |
OLD | NEW |