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_work_item.h" |
| 6 |
| 7 #include "base/hash.h" |
| 8 #include "base/sha1.h" |
| 9 #include "net/base/net_errors.h" |
| 10 #include "net/disk_cache/errors.h" |
| 11 #include "net/disk_cache/file.h" |
| 12 #include "net/disk_cache/storage_block-inl.h" |
| 13 #include "net/disk_cache/v3/backend_worker.h" |
| 14 #include "net/disk_cache/v3/entry_impl_v3.h" |
| 15 |
| 16 namespace { |
| 17 |
| 18 // Simple adaptor for the sha1 interface. |
| 19 void ComputeCryptoHash(const std::string& source, |
| 20 disk_cache::ShortEntryRecord* record) { |
| 21 DCHECK(!source.empty()); |
| 22 base::SHA1HashBytes(reinterpret_cast<const unsigned char*>(source.data()), |
| 23 source.size(), |
| 24 reinterpret_cast<unsigned char*>(record->long_hash)); |
| 25 } |
| 26 |
| 27 bool CryptoHashMatches(const std::string& source, |
| 28 const disk_cache::ShortEntryRecord& record) { |
| 29 DCHECK(!source.empty()); |
| 30 unsigned char result[20]; |
| 31 base::SHA1HashBytes(reinterpret_cast<const unsigned char*>(source.data()), |
| 32 source.size(), result); |
| 33 |
| 34 return !memcmp(result, record.long_hash, sizeof(result)); |
| 35 } |
| 36 |
| 37 } // namespace |
| 38 |
| 39 // ------------------------------------------------------------------------ |
| 40 |
| 41 namespace disk_cache { |
| 42 |
| 43 class BackendImplV3::IOCallback : public disk_cache::FileIOCallback { |
| 44 public: |
| 45 explicit IOCallback(BackendImplV3::WorkItem* work_item); |
| 46 virtual ~IOCallback() {} |
| 47 |
| 48 // FileIOCallback implementation. |
| 49 virtual void OnFileIOComplete(int bytes_copied) OVERRIDE; |
| 50 void Discard(); |
| 51 |
| 52 private: |
| 53 // Even though we need the work item alive until the operation completes, we |
| 54 // don't grab an extra reference because the work item itself keeps an extra |
| 55 // reference until it reaches the main thread again. |
| 56 BackendImplV3::WorkItem* work_item_; |
| 57 |
| 58 DISALLOW_COPY_AND_ASSIGN(IOCallback); |
| 59 }; |
| 60 |
| 61 BackendImplV3::IOCallback::IOCallback(BackendImplV3::WorkItem* work_item) |
| 62 : work_item_(work_item) { |
| 63 } |
| 64 |
| 65 void BackendImplV3::IOCallback::OnFileIOComplete(int bytes_copied) { |
| 66 if (work_item_) |
| 67 work_item_->DoLoop(bytes_copied); |
| 68 |
| 69 delete this; |
| 70 } |
| 71 |
| 72 void BackendImplV3::IOCallback::Discard() { |
| 73 work_item_ = NULL; |
| 74 OnFileIOComplete(0); |
| 75 } |
| 76 |
| 77 // ------------------------------------------------------------------------ |
| 78 |
| 79 BackendImplV3::WorkItem::WorkItem(WorkType type) |
| 80 : type_(type), |
| 81 result_(0), |
| 82 flags_(0) {//init everything |
| 83 } |
| 84 |
| 85 void BackendImplV3::WorkItem::Start(BackendImplV3::Worker* worker) { |
| 86 Trace("Work 0x%p %d", this, type()); |
| 87 worker_ = worker; |
| 88 switch (type_) { |
| 89 case WORK_INIT: |
| 90 return CompleteItem(worker_->Init(flags_, &init_result_)); |
| 91 case WORK_RESTART: |
| 92 return CompleteItem(worker_->Restart(flags_, &init_result_)); |
| 93 case WORK_GROW_INDEX: |
| 94 return CompleteItem(worker_->GrowIndex(flags_, &init_result_)); |
| 95 case WORK_GROW_FILES: |
| 96 return CompleteItem(worker_->GrowFiles(flags_, &init_result_)); |
| 97 case WORK_WRITE_INDEX: |
| 98 next_state_ = STATE_WRITE_DATA; |
| 99 break; |
| 100 case WORK_OPEN_ENTRY: |
| 101 next_state_ = STATE_OPEN_ENTRY; |
| 102 break; |
| 103 case WORK_READ_DATA: |
| 104 next_state_ = STATE_READ_DATA; |
| 105 break; |
| 106 case WORK_WRITE_DATA: |
| 107 next_state_ = STATE_WRITE_DATA; |
| 108 break; |
| 109 case WORK_MOVE_DATA: |
| 110 next_state_ = STATE_MOVE_DATA; |
| 111 break; |
| 112 case WORK_TRUNCATE: |
| 113 next_state_ = STATE_TRUNCATE_DATA; |
| 114 break; |
| 115 case WORK_DELETE: |
| 116 return CompleteItem(worker_->Delete(address_)); |
| 117 case WORK_CLOSE: |
| 118 return CompleteItem(worker_->Close(address_)); |
| 119 case WORK_NONE: |
| 120 return CompleteItem(ERR_NO_ERROR); |
| 121 default: NOTREACHED(); |
| 122 } |
| 123 DoLoop(ERR_NO_ERROR); |
| 124 } |
| 125 |
| 126 void BackendImplV3::WorkItem::DoLoop(int result) { |
| 127 DCHECK(next_state_ != STATE_NONE); |
| 128 if (!worker_->IsValid()) |
| 129 return CompleteItem(result); |
| 130 |
| 131 int rv = result; |
| 132 do { |
| 133 State state = next_state_; |
| 134 next_state_ = STATE_NONE; |
| 135 switch (state) { |
| 136 case STATE_OPEN_ENTRY: |
| 137 DCHECK_EQ(ERR_NO_ERROR, rv); |
| 138 rv = DoOpenEntry(); |
| 139 break; |
| 140 case STATE_OPEN_ENTRY_COMPLETE: |
| 141 rv = DoOpenEntryComplete(rv); |
| 142 break; |
| 143 case STATE_READ_KEY: |
| 144 DCHECK_EQ(ERR_NO_ERROR, rv); |
| 145 rv = DoReadKey(); |
| 146 break; |
| 147 case STATE_READ_KEY_COMPLETE: |
| 148 DCHECK_EQ(ERR_NO_ERROR, rv); |
| 149 rv = DoReadKeyComplete(); |
| 150 break; |
| 151 case STATE_READ_DATA: |
| 152 DCHECK_EQ(ERR_NO_ERROR, rv); |
| 153 rv = DoReadData(); |
| 154 break; |
| 155 case STATE_READ_DATA_COMPLETE: |
| 156 rv = DoReadDataComplete(rv); |
| 157 break; |
| 158 case STATE_WRITE_DATA: |
| 159 DCHECK_EQ(ERR_NO_ERROR, rv); |
| 160 rv = DoWriteData(); |
| 161 break; |
| 162 case STATE_WRITE_DATA_COMPLETE: |
| 163 rv = DoWriteDataComplete(rv); |
| 164 break; |
| 165 case STATE_MOVE_DATA: |
| 166 DCHECK_EQ(ERR_NO_ERROR, rv); |
| 167 rv = DoMoveData(); |
| 168 break; |
| 169 case STATE_TRUNCATE_DATA: |
| 170 DCHECK_EQ(ERR_NO_ERROR, rv); |
| 171 rv = DoTruncateData(); |
| 172 break; |
| 173 case STATE_COPY_ENTRY: |
| 174 DCHECK_EQ(ERR_NO_ERROR, rv); |
| 175 rv = DoCopyEntry(); |
| 176 break; |
| 177 case STATE_COPY_ENTRY_COMPLETE: |
| 178 rv = DoCopyEntryComplete(rv); |
| 179 break; |
| 180 } |
| 181 } while (rv != ERR_PENDING && next_state_ != STATE_NONE); |
| 182 |
| 183 if (rv != ERR_PENDING) |
| 184 CompleteItem(rv); |
| 185 } |
| 186 |
| 187 void BackendImplV3::WorkItem::OnDone() { |
| 188 closure_.Run(this); |
| 189 } |
| 190 |
| 191 // ------------------------------------------------------------------------ |
| 192 |
| 193 BackendImplV3::WorkItem::~WorkItem() { |
| 194 } |
| 195 |
| 196 void BackendImplV3::WorkItem::CompleteItem(int result) { |
| 197 Trace("Work done 0x%p %d %d", this, type(), result); |
| 198 result_ = result; |
| 199 entry_block_.reset(); // Release resources while on the worker thread. |
| 200 worker_->DoneWithItem(this); |
| 201 } |
| 202 |
| 203 int BackendImplV3::WorkItem::DoOpenEntry() { |
| 204 next_state_ = STATE_OPEN_ENTRY_COMPLETE; |
| 205 for (; entries_.current < entries_.cells.size(); entries_.current++) { |
| 206 Addr address = entries_.cells[entries_.current].GetAddress(); |
| 207 if (entries_.cells[entries_.current].GetGroup() == ENTRY_EVICTED) { |
| 208 if (flags_ & WORK_FOR_RESURRECT) |
| 209 return LoadShortEntryBlock(address); |
| 210 continue; |
| 211 } |
| 212 |
| 213 if (flags_ & WORK_FOR_RESURRECT) |
| 214 continue; |
| 215 return LoadEntryBlock(address); |
| 216 } |
| 217 next_state_ = STATE_NONE; |
| 218 return ERR_OPERATION_FAILED; |
| 219 } |
| 220 |
| 221 int BackendImplV3::WorkItem::DoOpenEntryComplete(int result) { |
| 222 if (entries_.cells[entries_.current].GetGroup() == ENTRY_EVICTED) { |
| 223 if (result != static_cast<int>(sizeof(ShortEntryRecord))) |
| 224 return ERR_READ_FAILURE; |
| 225 |
| 226 if (!EntryImplV3::DeletedSanityCheck(*short_entry_block_->Data())) |
| 227 return ERR_INVALID_ENTRY; |
| 228 } else { |
| 229 if (result != static_cast<int>(sizeof(EntryRecord))) |
| 230 return ERR_READ_FAILURE; |
| 231 |
| 232 if (!EntryImplV3::BasicSanityCheck(*entry_block_->Data())) |
| 233 return ERR_INVALID_ENTRY; |
| 234 } |
| 235 |
| 236 if (entries_.cells[entries_.current].GetGroup() == ENTRY_EVICTED) { |
| 237 DCHECK(!(flags_ & WORK_FOR_EVICT)); |
| 238 if (CryptoHashMatches(key_, *short_entry_block_->Data())) { |
| 239 // We have a match. |
| 240 short_entry_record_.reset(short_entry_block_->ReleaseData()); |
| 241 return ERR_NO_ERROR; |
| 242 } |
| 243 } else { |
| 244 next_state_ = STATE_READ_KEY; |
| 245 return ERR_NO_ERROR; |
| 246 } |
| 247 |
| 248 next_state_ = STATE_OPEN_ENTRY; |
| 249 entries_.current++; |
| 250 return ERR_NO_ERROR; |
| 251 } |
| 252 |
| 253 int BackendImplV3::WorkItem::DoReadKey() { |
| 254 address_.set_value(entry_block_->Data()->data_addr[0]); |
| 255 offset_ = 0; |
| 256 buffer_ = new net::IOBuffer(entry_block_->Data()->key_len); |
| 257 buffer_len_ = entry_block_->Data()->key_len; |
| 258 |
| 259 next_state_ = STATE_READ_DATA; |
| 260 return ERR_NO_ERROR; |
| 261 } |
| 262 |
| 263 int BackendImplV3::WorkItem::DoReadKeyComplete() { |
| 264 std::string key(buffer_->data(), buffer_len_); |
| 265 uint32 hash = base::Hash(key); |
| 266 Trace("DoReadKeyComplete hash 0x%x, 0x%p", hash, this); |
| 267 DCHECK_EQ(hash, entries_.cells[entries_.current].hash()); |
| 268 if (flags() & WORK_FOR_ITERATION) |
| 269 key_ = key; |
| 270 |
| 271 if (flags_ & WORK_FOR_EVICT) { |
| 272 key_ = key; |
| 273 if (!(flags_ & WORK_NO_COPY)) { |
| 274 next_state_ = STATE_COPY_ENTRY; |
| 275 return ERR_NO_ERROR; |
| 276 } |
| 277 } |
| 278 |
| 279 if (key == key_) { |
| 280 // We have a match. |
| 281 entry_record_.reset(entry_block_->ReleaseData()); |
| 282 return ERR_NO_ERROR; |
| 283 } |
| 284 |
| 285 next_state_ = STATE_OPEN_ENTRY; |
| 286 entries_.current++; |
| 287 return ERR_NO_ERROR; |
| 288 } |
| 289 |
| 290 int BackendImplV3::WorkItem::DoReadData() { |
| 291 disk_cache::File* file = worker_->GetBackingFile(address_, false); |
| 292 if (!file) |
| 293 return net::ERR_FILE_NOT_FOUND; |
| 294 |
| 295 |
| 296 size_t file_offset = offset_; |
| 297 if (address_.is_block_file()) { |
| 298 DCHECK_LE(offset_ + buffer_len_, kMaxBlockSize); |
| 299 file_offset += address_.start_block() * address_.BlockSize(); |
| 300 } |
| 301 DCHECK(buffer_len_); |
| 302 |
| 303 bool completed; |
| 304 IOCallback* callback = new IOCallback(this); |
| 305 |
| 306 if (!file->Read(buffer_->data(), buffer_len_, file_offset, callback, |
| 307 &completed)) { |
| 308 callback->Discard(); |
| 309 return net::ERR_CACHE_READ_FAILURE; |
| 310 } |
| 311 next_state_ = STATE_READ_DATA_COMPLETE; |
| 312 |
| 313 if (completed) { |
| 314 callback->Discard(); |
| 315 return NO_ERROR; |
| 316 } |
| 317 |
| 318 return ERR_PENDING; |
| 319 } |
| 320 |
| 321 int BackendImplV3::WorkItem::DoReadDataComplete(int result) { |
| 322 if (result != buffer_len_) |
| 323 return net::ERR_CACHE_READ_FAILURE; |
| 324 |
| 325 if (type() == WORK_OPEN_ENTRY) { |
| 326 next_state_ = STATE_READ_KEY_COMPLETE; |
| 327 return ERR_NO_ERROR; |
| 328 } |
| 329 |
| 330 return result; |
| 331 } |
| 332 |
| 333 int BackendImplV3::WorkItem::DoWriteData() { |
| 334 disk_cache::File* file = NULL; |
| 335 if (type_ == WORK_WRITE_INDEX) |
| 336 file = worker_->GetBackupIndexFile(); |
| 337 else |
| 338 file = worker_->GetBackingFile(address_, true); |
| 339 |
| 340 if (!file) |
| 341 return net::ERR_CACHE_WRITE_FAILURE; |
| 342 |
| 343 DCHECK(buffer_len_); |
| 344 |
| 345 size_t file_offset = offset_; |
| 346 if (type_ != WORK_WRITE_INDEX && address_.is_block_file()) { |
| 347 DCHECK_LE(offset_ + buffer_len_, kMaxBlockSize); |
| 348 file_offset += address_.start_block() * address_.BlockSize(); |
| 349 } |
| 350 |
| 351 bool completed; |
| 352 IOCallback* callback = new IOCallback(this); |
| 353 |
| 354 if (!file->Write(buffer_->data(), buffer_len_, file_offset, callback, |
| 355 &completed)) { |
| 356 callback->Discard(); |
| 357 return net::ERR_CACHE_WRITE_FAILURE; |
| 358 } |
| 359 next_state_ = STATE_WRITE_DATA_COMPLETE; |
| 360 |
| 361 if (completed) { |
| 362 callback->Discard(); |
| 363 return NO_ERROR; |
| 364 } |
| 365 |
| 366 return ERR_PENDING; |
| 367 } |
| 368 |
| 369 int BackendImplV3::WorkItem::DoWriteDataComplete(int result) { |
| 370 if (type_ == WORK_WRITE_INDEX) |
| 371 worker_->CloseBackupIndexFile(); |
| 372 |
| 373 if (result != buffer_len_) |
| 374 return net::ERR_CACHE_WRITE_FAILURE; |
| 375 |
| 376 return result; |
| 377 } |
| 378 |
| 379 int BackendImplV3::WorkItem::DoMoveData() { |
| 380 disk_cache::File* file = worker_->GetBackingFile(address_, false); |
| 381 if (!file) |
| 382 return ERR_OPERATION_FAILED; |
| 383 |
| 384 DCHECK(buffer_len_); |
| 385 |
| 386 if (!address_.is_block_file()) { |
| 387 NOTREACHED(); |
| 388 return ERR_OPERATION_FAILED; |
| 389 } |
| 390 |
| 391 offset_ = 0; |
| 392 size_t file_offset = 0; |
| 393 DCHECK_LE(buffer_len_, kMaxBlockSize); |
| 394 file_offset += address_.start_block() * address_.BlockSize(); |
| 395 |
| 396 buffer_ = new net::IOBufferWithSize(buffer_len_); |
| 397 |
| 398 // We could optimize this to be an async read, but we'd have to do that from |
| 399 // the main thread, not from here. |
| 400 if (!file->Read(buffer_->data(), buffer_len_, file_offset)) |
| 401 return ERR_WRITE_FAILURE; |
| 402 |
| 403 address_ = address2_; |
| 404 next_state_ = STATE_WRITE_DATA; |
| 405 return NO_ERROR; |
| 406 } |
| 407 |
| 408 int BackendImplV3::WorkItem::DoTruncateData() { |
| 409 if (address_.is_block_file()) |
| 410 return ERR_OPERATION_FAILED; |
| 411 |
| 412 disk_cache::File* file = worker_->GetBackingFile(address_, false); |
| 413 if (!file) |
| 414 return ERR_OPERATION_FAILED; |
| 415 |
| 416 if (!file->SetLength(offset_)) |
| 417 return ERR_OPERATION_FAILED; |
| 418 |
| 419 return ERR_NO_ERROR; |
| 420 } |
| 421 |
| 422 int BackendImplV3::WorkItem::DoCopyEntry() { |
| 423 next_state_ = STATE_COPY_ENTRY_COMPLETE; |
| 424 Addr address = entries_.cells[1].GetAddress(); |
| 425 short_entry_block_.reset( |
| 426 new CacheShortEntryBlock(worker_->GetMappedFile(address), address)); |
| 427 |
| 428 ShortEntryRecord* short_record = short_entry_block_->Data(); |
| 429 EntryRecord* long_record = entry_block_->Data(); |
| 430 |
| 431 short_record->hash = long_record->hash; |
| 432 short_record->reuse_count = long_record->reuse_count; |
| 433 short_record->refetch_count = long_record->refetch_count; |
| 434 short_record->refetch_count = long_record->refetch_count; |
| 435 short_record->key_len = long_record->key_len; |
| 436 short_record->last_access_time = long_record->last_access_time; |
| 437 ComputeCryptoHash(key_, short_record); |
| 438 |
| 439 bool completed; |
| 440 IOCallback* callback = new IOCallback(this); |
| 441 if (!short_entry_block_->Store(callback, &completed)) { |
| 442 callback->Discard(); |
| 443 next_state_ = STATE_NONE; |
| 444 return ERR_OPERATION_FAILED; |
| 445 } |
| 446 |
| 447 if (completed) { |
| 448 callback->Discard(); |
| 449 return ERR_NO_ERROR; |
| 450 } |
| 451 |
| 452 return ERR_PENDING; |
| 453 } |
| 454 |
| 455 int BackendImplV3::WorkItem::DoCopyEntryComplete(int result) { |
| 456 if (result != static_cast<int>(sizeof(ShortEntryRecord))) |
| 457 return ERR_READ_FAILURE; |
| 458 |
| 459 entry_record_.reset(entry_block_->ReleaseData()); |
| 460 return ERR_NO_ERROR; |
| 461 } |
| 462 |
| 463 int BackendImplV3::WorkItem::LoadEntryBlock(Addr address) { |
| 464 entry_block_.reset(new CacheEntryBlockV3(worker_->GetMappedFile(address), |
| 465 address)); |
| 466 bool completed; |
| 467 IOCallback* callback = new IOCallback(this); |
| 468 if (!entry_block_->Load(callback, &completed)) { |
| 469 callback->Discard(); |
| 470 next_state_ = STATE_NONE; |
| 471 return ERR_OPERATION_FAILED; |
| 472 } |
| 473 |
| 474 if (completed) { |
| 475 callback->Discard(); |
| 476 return ERR_NO_ERROR; |
| 477 } |
| 478 |
| 479 return ERR_PENDING; |
| 480 } |
| 481 |
| 482 int BackendImplV3::WorkItem::LoadShortEntryBlock(Addr address) { |
| 483 short_entry_block_.reset( |
| 484 new CacheShortEntryBlock(worker_->GetMappedFile(address), address)); |
| 485 bool completed; |
| 486 IOCallback* callback = new IOCallback(this); |
| 487 if (!short_entry_block_->Load(callback, &completed)) { |
| 488 callback->Discard(); |
| 489 next_state_ = STATE_NONE; |
| 490 return ERR_OPERATION_FAILED; |
| 491 } |
| 492 |
| 493 if (completed) { |
| 494 callback->Discard(); |
| 495 return ERR_NO_ERROR; |
| 496 } |
| 497 |
| 498 return ERR_PENDING; |
| 499 } |
| 500 |
| 501 } // namespace disk_cache |
OLD | NEW |