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_NONE: |
| 98 return CompleteItem(ERR_NO_ERROR); |
| 99 case WORK_OPEN_ENTRY: |
| 100 next_state_ = STATE_OPEN_ENTRY; |
| 101 break; |
| 102 case WORK_READ_DATA: |
| 103 next_state_ = STATE_READ_DATA; |
| 104 break; |
| 105 case WORK_WRITE_DATA: |
| 106 next_state_ = STATE_WRITE_DATA; |
| 107 break; |
| 108 case WORK_MOVE_DATA: |
| 109 next_state_ = STATE_MOVE_DATA; |
| 110 break; |
| 111 case WORK_TRUNCATE: |
| 112 next_state_ = STATE_TRUNCATE_DATA; |
| 113 break; |
| 114 case WORK_DELETE: |
| 115 return CompleteItem(worker_->Delete(address_)); |
| 116 case WORK_CLOSE: |
| 117 return CompleteItem(worker_->Close(address_)); |
| 118 default: NOTREACHED(); |
| 119 } |
| 120 DoLoop(ERR_NO_ERROR); |
| 121 } |
| 122 |
| 123 void BackendImplV3::WorkItem::DoLoop(int result) { |
| 124 DCHECK(next_state_ != STATE_NONE); |
| 125 if (!worker_->IsValid()) |
| 126 return CompleteItem(result); |
| 127 |
| 128 int rv = result; |
| 129 do { |
| 130 State state = next_state_; |
| 131 next_state_ = STATE_NONE; |
| 132 switch (state) { |
| 133 case STATE_OPEN_ENTRY: |
| 134 DCHECK_EQ(ERR_NO_ERROR, rv); |
| 135 rv = DoOpenEntry(); |
| 136 break; |
| 137 case STATE_OPEN_ENTRY_COMPLETE: |
| 138 rv = DoOpenEntryComplete(rv); |
| 139 break; |
| 140 case STATE_READ_KEY: |
| 141 DCHECK_EQ(ERR_NO_ERROR, rv); |
| 142 rv = DoReadKey(); |
| 143 break; |
| 144 case STATE_READ_KEY_COMPLETE: |
| 145 DCHECK_EQ(ERR_NO_ERROR, rv); |
| 146 rv = DoReadKeyComplete(); |
| 147 break; |
| 148 case STATE_READ_DATA: |
| 149 DCHECK_EQ(ERR_NO_ERROR, rv); |
| 150 rv = DoReadData(); |
| 151 break; |
| 152 case STATE_READ_DATA_COMPLETE: |
| 153 rv = DoReadDataComplete(rv); |
| 154 break; |
| 155 case STATE_WRITE_DATA: |
| 156 DCHECK_EQ(ERR_NO_ERROR, rv); |
| 157 rv = DoWriteData(); |
| 158 break; |
| 159 case STATE_WRITE_DATA_COMPLETE: |
| 160 rv = DoWriteDataComplete(rv); |
| 161 break; |
| 162 case STATE_MOVE_DATA: |
| 163 DCHECK_EQ(ERR_NO_ERROR, rv); |
| 164 rv = DoMoveData(); |
| 165 break; |
| 166 case STATE_TRUNCATE_DATA: |
| 167 DCHECK_EQ(ERR_NO_ERROR, rv); |
| 168 rv = DoTruncateData(); |
| 169 break; |
| 170 case STATE_COPY_ENTRY: |
| 171 DCHECK_EQ(ERR_NO_ERROR, rv); |
| 172 rv = DoCopyEntry(); |
| 173 break; |
| 174 case STATE_COPY_ENTRY_COMPLETE: |
| 175 rv = DoCopyEntryComplete(rv); |
| 176 break; |
| 177 } |
| 178 } while (rv != ERR_PENDING && next_state_ != STATE_NONE); |
| 179 |
| 180 if (rv != ERR_PENDING) |
| 181 CompleteItem(rv); |
| 182 } |
| 183 |
| 184 void BackendImplV3::WorkItem::OnDone() { |
| 185 closure_.Run(this); |
| 186 } |
| 187 |
| 188 // ------------------------------------------------------------------------ |
| 189 |
| 190 BackendImplV3::WorkItem::~WorkItem() { |
| 191 } |
| 192 |
| 193 void BackendImplV3::WorkItem::CompleteItem(int result) { |
| 194 Trace("Work done 0x%p %d %d", this, type(), result); |
| 195 result_ = result; |
| 196 entry_block_.reset(); // Release resources while on the worker thread. |
| 197 worker_->DoneWithItem(this); |
| 198 } |
| 199 |
| 200 int BackendImplV3::WorkItem::DoOpenEntry() { |
| 201 next_state_ = STATE_OPEN_ENTRY_COMPLETE; |
| 202 for (; entries_.current < entries_.cells.size(); entries_.current++) { |
| 203 Addr address = entries_.cells[entries_.current].GetAddress(); |
| 204 if (entries_.cells[entries_.current].group() == ENTRY_EVICTED) { |
| 205 if (flags_ & WORK_FOR_RESURRECT) |
| 206 return LoadShortEntryBlock(address); |
| 207 continue; |
| 208 } |
| 209 |
| 210 if (flags_ & WORK_FOR_RESURRECT) |
| 211 continue; |
| 212 return LoadEntryBlock(address); |
| 213 } |
| 214 next_state_ = STATE_NONE; |
| 215 return ERR_OPERATION_FAILED; |
| 216 } |
| 217 |
| 218 int BackendImplV3::WorkItem::DoOpenEntryComplete(int result) { |
| 219 if (entries_.cells[entries_.current].group() == ENTRY_EVICTED) { |
| 220 if (result != static_cast<int>(sizeof(ShortEntryRecord))) |
| 221 return ERR_READ_FAILURE; |
| 222 |
| 223 if (!EntryImplV3::DeletedSanityCheck(*short_entry_block_->Data())) |
| 224 return ERR_INVALID_ENTRY; |
| 225 } else { |
| 226 if (result != static_cast<int>(sizeof(EntryRecord))) |
| 227 return ERR_READ_FAILURE; |
| 228 |
| 229 if (!EntryImplV3::BasicSanityCheck(*entry_block_->Data())) |
| 230 return ERR_INVALID_ENTRY; |
| 231 } |
| 232 |
| 233 if (entries_.cells[entries_.current].group() == ENTRY_EVICTED) { |
| 234 DCHECK(!(flags_ & WORK_FOR_EVICT)); |
| 235 if (CryptoHashMatches(key_, *short_entry_block_->Data())) { |
| 236 // We have a match. |
| 237 short_entry_record_.reset(short_entry_block_->ReleaseData()); |
| 238 return ERR_NO_ERROR; |
| 239 } |
| 240 } else { |
| 241 next_state_ = STATE_READ_KEY; |
| 242 return ERR_NO_ERROR; |
| 243 } |
| 244 |
| 245 next_state_ = STATE_OPEN_ENTRY; |
| 246 entries_.current++; |
| 247 return ERR_NO_ERROR; |
| 248 } |
| 249 |
| 250 int BackendImplV3::WorkItem::DoReadKey() { |
| 251 address_.set_value(entry_block_->Data()->data_addr[0]); |
| 252 offset_ = 0; |
| 253 buffer_ = new net::IOBuffer(entry_block_->Data()->key_len); |
| 254 buffer_len_ = entry_block_->Data()->key_len; |
| 255 |
| 256 next_state_ = STATE_READ_DATA; |
| 257 return ERR_NO_ERROR; |
| 258 } |
| 259 |
| 260 int BackendImplV3::WorkItem::DoReadKeyComplete() { |
| 261 std::string key(buffer_->data(), buffer_len_); |
| 262 uint32 hash = base::Hash(key); |
| 263 Trace("DoReadKeyComplete hash 0x%x, 0x%p", hash, this); |
| 264 DCHECK_EQ(hash, entries_.cells[entries_.current].hash()); |
| 265 if (flags() & WORK_FOR_ITERATION) |
| 266 key_ = key; |
| 267 |
| 268 if (flags_ & WORK_FOR_EVICT) { |
| 269 key_ = key; |
| 270 if (!(flags_ & WORK_NO_COPY)) { |
| 271 next_state_ = STATE_COPY_ENTRY; |
| 272 return ERR_NO_ERROR; |
| 273 } |
| 274 } |
| 275 |
| 276 if (key == key_) { |
| 277 // We have a match. |
| 278 entry_record_.reset(entry_block_->ReleaseData()); |
| 279 return ERR_NO_ERROR; |
| 280 } |
| 281 |
| 282 next_state_ = STATE_OPEN_ENTRY; |
| 283 entries_.current++; |
| 284 return ERR_NO_ERROR; |
| 285 } |
| 286 |
| 287 int BackendImplV3::WorkItem::DoReadData() { |
| 288 disk_cache::File* file = worker_->GetBackingFile(address_, false); |
| 289 if (!file) |
| 290 return net::ERR_FILE_NOT_FOUND; |
| 291 |
| 292 |
| 293 size_t file_offset = offset_; |
| 294 if (address_.is_block_file()) { |
| 295 DCHECK_LE(offset_ + buffer_len_, kMaxBlockSize); |
| 296 file_offset += address_.start_block() * address_.BlockSize(); |
| 297 } |
| 298 DCHECK(buffer_len_); |
| 299 |
| 300 bool completed; |
| 301 IOCallback* callback = new IOCallback(this); |
| 302 |
| 303 if (!file->Read(buffer_->data(), buffer_len_, file_offset, callback, |
| 304 &completed)) { |
| 305 callback->Discard(); |
| 306 return net::ERR_CACHE_READ_FAILURE; |
| 307 } |
| 308 next_state_ = STATE_READ_DATA_COMPLETE; |
| 309 |
| 310 if (completed) { |
| 311 callback->Discard(); |
| 312 return NO_ERROR; |
| 313 } |
| 314 |
| 315 return ERR_PENDING; |
| 316 } |
| 317 |
| 318 int BackendImplV3::WorkItem::DoReadDataComplete(int result) { |
| 319 if (result != buffer_len_) |
| 320 return net::ERR_CACHE_READ_FAILURE; |
| 321 |
| 322 if (type() == WORK_OPEN_ENTRY) { |
| 323 next_state_ = STATE_READ_KEY_COMPLETE; |
| 324 return ERR_NO_ERROR; |
| 325 } |
| 326 |
| 327 return result; |
| 328 } |
| 329 |
| 330 int BackendImplV3::WorkItem::DoWriteData() { |
| 331 disk_cache::File* file = worker_->GetBackingFile(address_, true); |
| 332 if (!file) |
| 333 return net::ERR_CACHE_WRITE_FAILURE; |
| 334 |
| 335 DCHECK(buffer_len_); |
| 336 |
| 337 size_t file_offset = offset_; |
| 338 if (address_.is_block_file()) { |
| 339 DCHECK_LE(offset_ + buffer_len_, kMaxBlockSize); |
| 340 file_offset += address_.start_block() * address_.BlockSize(); |
| 341 } |
| 342 |
| 343 bool completed; |
| 344 IOCallback* callback = new IOCallback(this); |
| 345 |
| 346 if (!file->Write(buffer_->data(), buffer_len_, file_offset, callback, |
| 347 &completed)) { |
| 348 callback->Discard(); |
| 349 return net::ERR_CACHE_WRITE_FAILURE; |
| 350 } |
| 351 next_state_ = STATE_WRITE_DATA_COMPLETE; |
| 352 |
| 353 if (completed) { |
| 354 callback->Discard(); |
| 355 return NO_ERROR; |
| 356 } |
| 357 |
| 358 return ERR_PENDING; |
| 359 } |
| 360 |
| 361 int BackendImplV3::WorkItem::DoWriteDataComplete(int result) { |
| 362 if (result != buffer_len_) |
| 363 return net::ERR_CACHE_WRITE_FAILURE; |
| 364 |
| 365 return result; |
| 366 } |
| 367 |
| 368 int BackendImplV3::WorkItem::DoMoveData() { |
| 369 disk_cache::File* file = worker_->GetBackingFile(address_, false); |
| 370 if (!file) |
| 371 return ERR_OPERATION_FAILED; |
| 372 |
| 373 DCHECK(buffer_len_); |
| 374 |
| 375 if (!address_.is_block_file()) { |
| 376 NOTREACHED(); |
| 377 return ERR_OPERATION_FAILED; |
| 378 } |
| 379 |
| 380 offset_ = 0; |
| 381 size_t file_offset = 0; |
| 382 DCHECK_LE(buffer_len_, kMaxBlockSize); |
| 383 file_offset += address_.start_block() * address_.BlockSize(); |
| 384 |
| 385 buffer_ = new net::IOBufferWithSize(buffer_len_); |
| 386 |
| 387 // We could optimize this to be an async read, but we'd have to do that from |
| 388 // the main thread, not from here. |
| 389 if (!file->Read(buffer_->data(), buffer_len_, file_offset)) |
| 390 return ERR_WRITE_FAILURE; |
| 391 |
| 392 address_ = address2_; |
| 393 next_state_ = STATE_WRITE_DATA; |
| 394 return NO_ERROR; |
| 395 } |
| 396 |
| 397 int BackendImplV3::WorkItem::DoTruncateData() { |
| 398 if (address_.is_block_file()) |
| 399 return ERR_OPERATION_FAILED; |
| 400 |
| 401 disk_cache::File* file = worker_->GetBackingFile(address_, false); |
| 402 if (!file) |
| 403 return ERR_OPERATION_FAILED; |
| 404 |
| 405 if (!file->SetLength(offset_)) |
| 406 return ERR_OPERATION_FAILED; |
| 407 |
| 408 return ERR_NO_ERROR; |
| 409 } |
| 410 |
| 411 int BackendImplV3::WorkItem::DoCopyEntry() { |
| 412 next_state_ = STATE_COPY_ENTRY_COMPLETE; |
| 413 Addr address = entries_.cells[1].GetAddress(); |
| 414 short_entry_block_.reset( |
| 415 new CacheShortEntryBlock(worker_->GetMappedFile(address), address)); |
| 416 |
| 417 ShortEntryRecord* short_record = short_entry_block_->Data(); |
| 418 EntryRecord* long_record = entry_block_->Data(); |
| 419 |
| 420 short_record->hash = long_record->hash; |
| 421 short_record->reuse_count = long_record->reuse_count; |
| 422 short_record->refetch_count = long_record->refetch_count; |
| 423 short_record->refetch_count = long_record->refetch_count; |
| 424 short_record->key_len = long_record->key_len; |
| 425 short_record->last_access_time = long_record->last_access_time; |
| 426 ComputeCryptoHash(key_, short_record); |
| 427 |
| 428 bool completed; |
| 429 IOCallback* callback = new IOCallback(this); |
| 430 if (!short_entry_block_->Store(callback, &completed)) { |
| 431 callback->Discard(); |
| 432 next_state_ = STATE_NONE; |
| 433 return ERR_OPERATION_FAILED; |
| 434 } |
| 435 |
| 436 if (completed) { |
| 437 callback->Discard(); |
| 438 return ERR_NO_ERROR; |
| 439 } |
| 440 |
| 441 return ERR_PENDING; |
| 442 } |
| 443 |
| 444 int BackendImplV3::WorkItem::DoCopyEntryComplete(int result) { |
| 445 if (result != static_cast<int>(sizeof(ShortEntryRecord))) |
| 446 return ERR_READ_FAILURE; |
| 447 |
| 448 entry_record_.reset(entry_block_->ReleaseData()); |
| 449 return ERR_NO_ERROR; |
| 450 } |
| 451 |
| 452 int BackendImplV3::WorkItem::LoadEntryBlock(Addr address) { |
| 453 entry_block_.reset(new CacheEntryBlockV3(worker_->GetMappedFile(address), |
| 454 address)); |
| 455 bool completed; |
| 456 IOCallback* callback = new IOCallback(this); |
| 457 if (!entry_block_->Load(callback, &completed)) { |
| 458 callback->Discard(); |
| 459 next_state_ = STATE_NONE; |
| 460 return ERR_OPERATION_FAILED; |
| 461 } |
| 462 |
| 463 if (completed) { |
| 464 callback->Discard(); |
| 465 return ERR_NO_ERROR; |
| 466 } |
| 467 |
| 468 return ERR_PENDING; |
| 469 } |
| 470 |
| 471 int BackendImplV3::WorkItem::LoadShortEntryBlock(Addr address) { |
| 472 short_entry_block_.reset( |
| 473 new CacheShortEntryBlock(worker_->GetMappedFile(address), address)); |
| 474 bool completed; |
| 475 IOCallback* callback = new IOCallback(this); |
| 476 if (!short_entry_block_->Load(callback, &completed)) { |
| 477 callback->Discard(); |
| 478 next_state_ = STATE_NONE; |
| 479 return ERR_OPERATION_FAILED; |
| 480 } |
| 481 |
| 482 if (completed) { |
| 483 callback->Discard(); |
| 484 return ERR_NO_ERROR; |
| 485 } |
| 486 |
| 487 return ERR_PENDING; |
| 488 } |
| 489 |
| 490 } // namespace disk_cache |
OLD | NEW |