| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "components/drive/chromeos/file_cache.h" | 5 #include "components/drive/chromeos/file_cache.h" |
| 6 | 6 |
| 7 #include <unistd.h> | 7 #include <linux/fs.h> |
| 8 #include <sys/ioctl.h> |
| 9 #include <sys/xattr.h> |
| 8 | 10 |
| 9 #include <queue> | 11 #include <queue> |
| 10 #include <vector> | 12 #include <vector> |
| 11 | 13 |
| 12 #include "base/bind.h" | 14 #include "base/bind.h" |
| 13 #include "base/bind_helpers.h" | 15 #include "base/bind_helpers.h" |
| 14 #include "base/callback_helpers.h" | 16 #include "base/callback_helpers.h" |
| 17 #include "base/files/file.h" |
| 15 #include "base/files/file_enumerator.h" | 18 #include "base/files/file_enumerator.h" |
| 16 #include "base/files/file_util.h" | 19 #include "base/files/file_util.h" |
| 17 #include "base/location.h" | 20 #include "base/location.h" |
| 18 #include "base/logging.h" | 21 #include "base/logging.h" |
| 19 #include "base/metrics/histogram.h" | 22 #include "base/metrics/histogram.h" |
| 23 #include "base/stl_util.h" |
| 20 #include "base/strings/string_util.h" | 24 #include "base/strings/string_util.h" |
| 21 #include "base/strings/stringprintf.h" | 25 #include "base/strings/stringprintf.h" |
| 22 #include "base/sys_info.h" | 26 #include "base/sys_info.h" |
| 23 #include "build/build_config.h" | 27 #include "build/build_config.h" |
| 24 #include "components/drive/drive.pb.h" | 28 #include "components/drive/drive.pb.h" |
| 25 #include "components/drive/drive_api_util.h" | 29 #include "components/drive/drive_api_util.h" |
| 26 #include "components/drive/file_system_core_util.h" | 30 #include "components/drive/file_system_core_util.h" |
| 27 #include "components/drive/resource_metadata_storage.h" | 31 #include "components/drive/resource_metadata_storage.h" |
| 28 #include "google_apis/drive/task_util.h" | 32 #include "google_apis/drive/task_util.h" |
| 29 #include "net/base/filename_util.h" | 33 #include "net/base/filename_util.h" |
| 30 #include "net/base/mime_sniffer.h" | 34 #include "net/base/mime_sniffer.h" |
| 31 #include "net/base/mime_util.h" | 35 #include "net/base/mime_util.h" |
| 32 | 36 |
| 33 namespace drive { | 37 namespace drive { |
| 34 namespace internal { | 38 namespace internal { |
| 35 namespace { | 39 namespace { |
| 36 | 40 |
| 41 typedef std::pair<base::File::Info, ResourceEntry> CacheInfo; |
| 42 typedef long FileAttributes; // NOLINT(runtime/int) |
| 43 |
| 37 // Returns ID extracted from the path. | 44 // Returns ID extracted from the path. |
| 38 std::string GetIdFromPath(const base::FilePath& path) { | 45 std::string GetIdFromPath(const base::FilePath& path) { |
| 39 return util::UnescapeCacheFileName(path.BaseName().AsUTF8Unsafe()); | 46 return util::UnescapeCacheFileName(path.BaseName().AsUTF8Unsafe()); |
| 40 } | 47 } |
| 41 | 48 |
| 42 base::FilePath GetPathForId(const base::FilePath& cache_directory, | 49 base::FilePath GetPathForId(const base::FilePath& cache_directory, |
| 43 const std::string& id) { | 50 const std::string& id) { |
| 44 return cache_directory.Append( | 51 return cache_directory.Append( |
| 45 base::FilePath::FromUTF8Unsafe(util::EscapeCacheFileName(id))); | 52 base::FilePath::FromUTF8Unsafe(util::EscapeCacheFileName(id))); |
| 46 } | 53 } |
| 47 | 54 |
| 48 typedef std::pair<base::File::Info, ResourceEntry> CacheInfo; | 55 // Sets extended file attribute as |name| |value| pair. |
| 56 bool SetExtendedFileAttributes(const base::FilePath& path, |
| 57 const std::string& name, const std::string& value) { |
| 58 return setxattr(path.value().c_str(), name.c_str(), value.c_str(), |
| 59 value.size() + 1, 0) == 0; |
| 60 } |
| 61 |
| 62 // Changes attributes of the file with |flags|, e.g. FS_NODUMP_FL (cryptohome |
| 63 // will remove Drive caches with this attribute). |
| 64 // See linux/fs.h for available flags, and chattr(1) which does similar thing. |
| 65 // Returns whether operation succeeded. |
| 66 bool SetFileAttributes(const base::FilePath& path, FileAttributes flags) { |
| 67 base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_READ); |
| 68 if (!file.IsValid()) { |
| 69 PLOG(ERROR) << "Failed to open file: " << path.value(); |
| 70 return false; |
| 71 } |
| 72 if (ioctl(file.GetPlatformFile(), FS_IOC_SETFLAGS, &flags) < 0) { |
| 73 PLOG(ERROR) << "ioctl: " << path.value(); |
| 74 return false; |
| 75 } |
| 76 return true; |
| 77 } |
| 78 |
| 79 // Gets file attributes similarly to lsattr(1). Returns flags or -1 on error. |
| 80 // See linux/fs.h for the definition of the returned flags e.g. FS_NODUMP_FL. |
| 81 FileAttributes GetFileAttributes(const base::FilePath& path) { |
| 82 base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_READ); |
| 83 if (!file.IsValid()) { |
| 84 PLOG(ERROR) << "Failed to open file: " << path.value(); |
| 85 return -1; |
| 86 } |
| 87 FileAttributes flags = 0; |
| 88 if (ioctl(file.GetPlatformFile(), FS_IOC_GETFLAGS, &flags) < 0) { |
| 89 PLOG(ERROR) << "ioctl: " << path.value(); |
| 90 return -1; |
| 91 } |
| 92 return flags; |
| 93 } |
| 94 |
| 95 // Marks the cache file to be removable by cryptohome. |
| 96 bool SetRemovable(const base::FilePath& path) { |
| 97 FileAttributes flags = GetFileAttributes(path); |
| 98 if (flags < 0) return false; |
| 99 if ((flags & FS_NODUMP_FL) == FS_NODUMP_FL) return true; |
| 100 |
| 101 return SetFileAttributes(path, flags | FS_NODUMP_FL); |
| 102 } |
| 103 |
| 104 // Marks the cache file to be unremovable by cryptohome. |
| 105 bool UnsetRemovable(const base::FilePath& path) { |
| 106 FileAttributes flags = GetFileAttributes(path); |
| 107 if (flags < 0) return false; |
| 108 if ((flags & FS_NODUMP_FL) == 0) return true; |
| 109 |
| 110 return SetFileAttributes(path, flags & ~FS_NODUMP_FL); |
| 111 } |
| 112 |
| 113 // Marks |path| as drive cache dir. |
| 114 // Returns if the operation succeeded. |
| 115 bool MarkAsDriveCacheDir(const base::FilePath& path) { |
| 116 return SetRemovable(path) |
| 117 && SetExtendedFileAttributes(path, FileCache::kGCacheFilesAttribute, ""); |
| 118 } |
| 49 | 119 |
| 50 class CacheInfoLatestCompare { | 120 class CacheInfoLatestCompare { |
| 51 public: | 121 public: |
| 52 bool operator()(const CacheInfo& info_a, const CacheInfo& info_b) { | 122 bool operator()(const CacheInfo& info_a, const CacheInfo& info_b) { |
| 53 return info_a.first.last_accessed < info_b.first.last_accessed; | 123 return info_a.first.last_accessed < info_b.first.last_accessed; |
| 54 } | 124 } |
| 55 }; | 125 }; |
| 56 | 126 |
| 57 const size_t kMaxNumOfEvictedCacheFiles = 30000; | 127 const size_t kMaxNumOfEvictedCacheFiles = 30000; |
| 58 | 128 |
| 59 } // namespace | 129 } // namespace |
| 60 | 130 |
| 131 // static |
| 132 const char FileCache::kGCacheFilesAttribute[] = "user.GCacheFiles"; |
| 133 |
| 61 FileCache::FileCache(ResourceMetadataStorage* storage, | 134 FileCache::FileCache(ResourceMetadataStorage* storage, |
| 62 const base::FilePath& cache_file_directory, | 135 const base::FilePath& cache_file_directory, |
| 63 base::SequencedTaskRunner* blocking_task_runner, | 136 base::SequencedTaskRunner* blocking_task_runner, |
| 64 FreeDiskSpaceGetterInterface* free_disk_space_getter) | 137 FreeDiskSpaceGetterInterface* free_disk_space_getter) |
| 65 : cache_file_directory_(cache_file_directory), | 138 : cache_file_directory_(cache_file_directory), |
| 66 blocking_task_runner_(blocking_task_runner), | 139 blocking_task_runner_(blocking_task_runner), |
| 67 storage_(storage), | 140 storage_(storage), |
| 68 free_disk_space_getter_(free_disk_space_getter), | 141 free_disk_space_getter_(free_disk_space_getter), |
| 69 max_num_of_evicted_cache_files_(kMaxNumOfEvictedCacheFiles), | 142 max_num_of_evicted_cache_files_(kMaxNumOfEvictedCacheFiles), |
| 70 weak_ptr_factory_(this) { | 143 weak_ptr_factory_(this) { |
| (...skipping 204 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 275 return FILE_ERROR_FAILED; | 348 return FILE_ERROR_FAILED; |
| 276 } | 349 } |
| 277 | 350 |
| 278 // Now that file operations have completed, update metadata. | 351 // Now that file operations have completed, update metadata. |
| 279 FileCacheEntry* cache_state = | 352 FileCacheEntry* cache_state = |
| 280 entry.mutable_file_specific_info()->mutable_cache_state(); | 353 entry.mutable_file_specific_info()->mutable_cache_state(); |
| 281 cache_state->set_md5(md5); | 354 cache_state->set_md5(md5); |
| 282 cache_state->set_is_present(true); | 355 cache_state->set_is_present(true); |
| 283 if (md5.empty()) | 356 if (md5.empty()) |
| 284 cache_state->set_is_dirty(true); | 357 cache_state->set_is_dirty(true); |
| 358 |
| 359 if (!cache_state->is_pinned() && !cache_state->is_dirty()) { |
| 360 if (!SetRemovable(dest_path)) |
| 361 return FILE_ERROR_FAILED; |
| 362 } else { |
| 363 if (!UnsetRemovable(dest_path)) |
| 364 return FILE_ERROR_FAILED; |
| 365 } |
| 366 |
| 285 return storage_->PutEntry(entry); | 367 return storage_->PutEntry(entry); |
| 286 } | 368 } |
| 287 | 369 |
| 288 FileError FileCache::Pin(const std::string& id) { | 370 FileError FileCache::Pin(const std::string& id) { |
| 289 AssertOnSequencedWorkerPool(); | 371 AssertOnSequencedWorkerPool(); |
| 290 | 372 |
| 291 ResourceEntry entry; | 373 ResourceEntry entry; |
| 292 FileError error = storage_->GetEntry(id, &entry); | 374 FileError error = storage_->GetEntry(id, &entry); |
| 293 if (error != FILE_ERROR_OK) | 375 if (error != FILE_ERROR_OK) |
| 294 return error; | 376 return error; |
| 377 |
| 295 entry.mutable_file_specific_info()->mutable_cache_state()->set_is_pinned( | 378 entry.mutable_file_specific_info()->mutable_cache_state()->set_is_pinned( |
| 296 true); | 379 true); |
| 380 |
| 381 base::FilePath file_path = GetCacheFilePath(entry.local_id()); |
| 382 // Cache file can be created later. |
| 383 if (entry.file_specific_info().cache_state().is_present()) { |
| 384 if (!UnsetRemovable(file_path)) |
| 385 return FILE_ERROR_FAILED; |
| 386 } |
| 387 |
| 297 return storage_->PutEntry(entry); | 388 return storage_->PutEntry(entry); |
| 298 } | 389 } |
| 299 | 390 |
| 300 FileError FileCache::Unpin(const std::string& id) { | 391 FileError FileCache::Unpin(const std::string& id) { |
| 301 AssertOnSequencedWorkerPool(); | 392 AssertOnSequencedWorkerPool(); |
| 302 | 393 |
| 303 // Unpinning a file means its entry must exist in cache. | 394 // Unpinning a file means its entry must exist in cache. |
| 304 ResourceEntry entry; | 395 ResourceEntry entry; |
| 305 FileError error = storage_->GetEntry(id, &entry); | 396 FileError error = storage_->GetEntry(id, &entry); |
| 306 if (error != FILE_ERROR_OK) | 397 if (error != FILE_ERROR_OK) |
| 307 return error; | 398 return error; |
| 308 | 399 |
| 309 // Now that file operations have completed, update metadata. | 400 // Now that file operations have completed, update metadata. |
| 310 if (entry.file_specific_info().cache_state().is_present()) { | 401 if (entry.file_specific_info().cache_state().is_present()) { |
| 311 entry.mutable_file_specific_info()->mutable_cache_state()->set_is_pinned( | 402 entry.mutable_file_specific_info()->mutable_cache_state()->set_is_pinned( |
| 312 false); | 403 false); |
| 404 if (!entry.file_specific_info().cache_state().is_dirty()) { |
| 405 if (!SetRemovable(GetCacheFilePath(entry.local_id()))) |
| 406 return FILE_ERROR_FAILED; |
| 407 } |
| 313 } else { | 408 } else { |
| 314 // Remove the existing entry if we are unpinning a non-present file. | 409 // Remove the existing entry if we are unpinning a non-present file. |
| 315 entry.mutable_file_specific_info()->clear_cache_state(); | 410 entry.mutable_file_specific_info()->clear_cache_state(); |
| 316 } | 411 } |
| 317 error = storage_->PutEntry(entry); | 412 error = storage_->PutEntry(entry); |
| 318 if (error != FILE_ERROR_OK) | 413 if (error != FILE_ERROR_OK) |
| 319 return error; | 414 return error; |
| 320 | 415 |
| 321 // Now it's a chance to free up space if needed. | 416 // Now it's a chance to free up space if needed. |
| 322 FreeDiskSpaceIfNeededFor(0); | 417 FreeDiskSpaceIfNeededFor(0); |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 369 ResourceEntry entry; | 464 ResourceEntry entry; |
| 370 FileError error = storage_->GetEntry(id, &entry); | 465 FileError error = storage_->GetEntry(id, &entry); |
| 371 if (error != FILE_ERROR_OK) | 466 if (error != FILE_ERROR_OK) |
| 372 return error; | 467 return error; |
| 373 if (!entry.file_specific_info().cache_state().is_present()) { | 468 if (!entry.file_specific_info().cache_state().is_present()) { |
| 374 LOG(WARNING) << "Can't mark dirty a file that wasn't cached: " << id; | 469 LOG(WARNING) << "Can't mark dirty a file that wasn't cached: " << id; |
| 375 return FILE_ERROR_NOT_FOUND; | 470 return FILE_ERROR_NOT_FOUND; |
| 376 } | 471 } |
| 377 | 472 |
| 378 entry.mutable_file_specific_info()->mutable_cache_state()->set_is_dirty(true); | 473 entry.mutable_file_specific_info()->mutable_cache_state()->set_is_dirty(true); |
| 474 if (!UnsetRemovable(GetCacheFilePath(entry.local_id()))) |
| 475 return FILE_ERROR_FAILED; |
| 476 |
| 379 entry.mutable_file_specific_info()->mutable_cache_state()->clear_md5(); | 477 entry.mutable_file_specific_info()->mutable_cache_state()->clear_md5(); |
| 380 error = storage_->PutEntry(entry); | 478 error = storage_->PutEntry(entry); |
| 381 if (error != FILE_ERROR_OK) | 479 if (error != FILE_ERROR_OK) |
| 382 return error; | 480 return error; |
| 383 | 481 |
| 384 write_opened_files_[id]++; | 482 write_opened_files_[id]++; |
| 385 file_closer->reset(new base::ScopedClosureRunner( | 483 file_closer->reset(new base::ScopedClosureRunner( |
| 386 base::Bind(&google_apis::RunTaskWithTaskRunner, | 484 base::Bind(&google_apis::RunTaskWithTaskRunner, |
| 387 blocking_task_runner_, | 485 blocking_task_runner_, |
| 388 base::Bind(&FileCache::CloseForWrite, | 486 base::Bind(&FileCache::CloseForWrite, |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 440 | 538 |
| 441 // If a file is not dirty (it should have been marked dirty via OpenForWrite), | 539 // If a file is not dirty (it should have been marked dirty via OpenForWrite), |
| 442 // clearing its dirty state is an invalid operation. | 540 // clearing its dirty state is an invalid operation. |
| 443 if (!entry.file_specific_info().cache_state().is_dirty()) { | 541 if (!entry.file_specific_info().cache_state().is_dirty()) { |
| 444 LOG(WARNING) << "Can't clear dirty state of a non-dirty file: " << id; | 542 LOG(WARNING) << "Can't clear dirty state of a non-dirty file: " << id; |
| 445 return FILE_ERROR_INVALID_OPERATION; | 543 return FILE_ERROR_INVALID_OPERATION; |
| 446 } | 544 } |
| 447 | 545 |
| 448 entry.mutable_file_specific_info()->mutable_cache_state()->set_is_dirty( | 546 entry.mutable_file_specific_info()->mutable_cache_state()->set_is_dirty( |
| 449 false); | 547 false); |
| 548 if (!entry.file_specific_info().cache_state().is_pinned()) { |
| 549 if (!SetRemovable(GetCacheFilePath(entry.local_id()))) |
| 550 return FILE_ERROR_FAILED; |
| 551 } |
| 552 |
| 450 return storage_->PutEntry(entry); | 553 return storage_->PutEntry(entry); |
| 451 } | 554 } |
| 452 | 555 |
| 453 FileError FileCache::Remove(const std::string& id) { | 556 FileError FileCache::Remove(const std::string& id) { |
| 454 AssertOnSequencedWorkerPool(); | 557 AssertOnSequencedWorkerPool(); |
| 455 | 558 |
| 456 ResourceEntry entry; | 559 ResourceEntry entry; |
| 457 | 560 |
| 458 // If entry doesn't exist, nothing to do. | 561 // If entry doesn't exist, nothing to do. |
| 459 FileError error = storage_->GetEntry(id, &entry); | 562 FileError error = storage_->GetEntry(id, &entry); |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 506 clear_md5(); | 609 clear_md5(); |
| 507 if (storage_->PutEntry(new_entry) != FILE_ERROR_OK) | 610 if (storage_->PutEntry(new_entry) != FILE_ERROR_OK) |
| 508 return false; | 611 return false; |
| 509 } | 612 } |
| 510 } | 613 } |
| 511 if (it->HasError()) | 614 if (it->HasError()) |
| 512 return false; | 615 return false; |
| 513 | 616 |
| 514 if (!RenameCacheFilesToNewFormat()) | 617 if (!RenameCacheFilesToNewFormat()) |
| 515 return false; | 618 return false; |
| 619 |
| 620 // Run this every time to resolve inconsistency between metadata |
| 621 // and file attributes which possibly occurs on abrupt power failure. |
| 622 if (!FixMetadataAndFileAttributes()) { |
| 623 return false; |
| 624 } |
| 625 |
| 516 return true; | 626 return true; |
| 517 } | 627 } |
| 518 | 628 |
| 519 void FileCache::Destroy() { | 629 void FileCache::Destroy() { |
| 520 DCHECK(thread_checker_.CalledOnValidThread()); | 630 DCHECK(thread_checker_.CalledOnValidThread()); |
| 521 | 631 |
| 522 in_shutdown_.Set(); | 632 in_shutdown_.Set(); |
| 523 | 633 |
| 524 // Destroy myself on the blocking pool. | 634 // Destroy myself on the blocking pool. |
| 525 // Note that base::DeletePointer<> cannot be used as the destructor of this | 635 // Note that base::DeletePointer<> cannot be used as the destructor of this |
| (...skipping 151 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 677 continue; | 787 continue; |
| 678 } | 788 } |
| 679 const std::string& id = GetIdFromPath(new_path); | 789 const std::string& id = GetIdFromPath(new_path); |
| 680 new_path = GetCacheFilePath(util::CanonicalizeResourceId(id)); | 790 new_path = GetCacheFilePath(util::CanonicalizeResourceId(id)); |
| 681 if (new_path != current && !base::Move(current, new_path)) | 791 if (new_path != current && !base::Move(current, new_path)) |
| 682 return false; | 792 return false; |
| 683 } | 793 } |
| 684 return true; | 794 return true; |
| 685 } | 795 } |
| 686 | 796 |
| 687 // static | 797 bool FileCache::FixMetadataAndFileAttributes() { |
| 688 bool FileCache::MigrateCacheFiles(const base::FilePath& from, | |
| 689 const base::FilePath& to_files, | |
| 690 const base::FilePath& to_links, | |
| 691 ResourceMetadataStorage* metadata_storage) { | |
| 692 std::unique_ptr<ResourceMetadataStorage::Iterator> it = | 798 std::unique_ptr<ResourceMetadataStorage::Iterator> it = |
| 693 metadata_storage->GetIterator(); | 799 storage_->GetIterator(); |
| 800 |
| 694 for (; !it->IsAtEnd(); it->Advance()) { | 801 for (; !it->IsAtEnd(); it->Advance()) { |
| 695 const ResourceEntry& entry = it->GetValue(); | 802 ResourceEntry entry = it->GetValue(); |
| 696 if (!entry.file_specific_info().cache_state().is_present()) { | 803 FileCacheEntry* file_cache_entry = |
| 697 continue; | 804 entry.mutable_file_specific_info()->mutable_cache_state(); |
| 698 } | |
| 699 | 805 |
| 700 // Ignore missing cache file case since it never succeeds. | 806 const base::FilePath filepath = GetPathForId(cache_file_directory_, |
| 701 // TODO(yawano): handle this case at metadata validation in FileCache. | 807 entry.local_id()); |
| 702 const base::FilePath move_from = GetPathForId(from, entry.local_id()); | |
| 703 if (!base::PathExists(move_from)) { | |
| 704 continue; | |
| 705 } | |
| 706 | 808 |
| 707 // Create hard link to cache file if it's pinned or dirty. cryptohome will | 809 if (base::PathExists(filepath)) { |
| 708 // not delete a cache file if there is a hard link to it. | 810 if (file_cache_entry->is_present()) { |
| 709 const FileCacheEntry& file_cache_entry = | 811 // Update file attribues for cryptohome. |
| 710 entry.file_specific_info().cache_state(); | 812 if (file_cache_entry->is_pinned() || file_cache_entry->is_dirty()) { |
| 711 if (file_cache_entry.is_pinned() || file_cache_entry.is_dirty()) { | 813 if (!UnsetRemovable(filepath)) return false; |
| 712 const base::FilePath link_path = GetPathForId(to_links, entry.local_id()); | 814 } else { |
| 713 int link_result = link(move_from.AsUTF8Unsafe().c_str(), | 815 if (!SetRemovable(filepath)) return false; |
| 714 link_path.AsUTF8Unsafe().c_str()); | 816 } |
| 715 if (link_result != 0 && errno != EEXIST) { | 817 } else { |
| 716 return false; | 818 // Delete file if the file is present but metadata says not. |
| 819 // It happens only on abrupt shutdown. |
| 820 LOG(WARNING) |
| 821 << "File is present but metadata's state was inconsistent."; |
| 822 |
| 823 if (!base::DeleteFile(filepath, false /* recursive */)) |
| 824 return false; |
| 717 } | 825 } |
| 718 } | 826 } else { |
| 719 | 827 // Update metatadata if there is no file but metadata says there is. |
| 720 // Move cache file. | 828 // It happens when cryptohome removed the file. |
| 721 const base::FilePath move_to = GetPathForId(to_files, entry.local_id()); | 829 // We don't clear is_pinned here, so that file download is restarted on |
| 722 if (!base::Move(move_from, move_to)) { | 830 // the following scenario: |
| 723 return false; | 831 // 1. The file was pinned but not present. |
| 832 // 2. Then the file was downloaded and became present. |
| 833 // 3. Unclean shutdown happens, metadata update was saved to the disk, |
| 834 // but the file move was not. |
| 835 if (file_cache_entry->is_present()) { |
| 836 file_cache_entry->set_is_present(false); |
| 837 file_cache_entry->set_is_dirty(false); |
| 838 file_cache_entry->clear_md5(); |
| 839 if (storage_->PutEntry(entry) != FILE_ERROR_OK) |
| 840 return false; |
| 841 } |
| 724 } | 842 } |
| 725 } | 843 } |
| 726 | 844 |
| 727 return true; | 845 return MarkAsDriveCacheDir(cache_file_directory_); |
| 728 } | 846 } |
| 729 | 847 |
| 730 void FileCache::CloseForWrite(const std::string& id) { | 848 void FileCache::CloseForWrite(const std::string& id) { |
| 731 AssertOnSequencedWorkerPool(); | 849 AssertOnSequencedWorkerPool(); |
| 732 | 850 |
| 733 std::map<std::string, int>::iterator it = write_opened_files_.find(id); | 851 std::map<std::string, int>::iterator it = write_opened_files_.find(id); |
| 734 if (it == write_opened_files_.end()) | 852 if (it == write_opened_files_.end()) |
| 735 return; | 853 return; |
| 736 | 854 |
| 737 DCHECK_LT(0, it->second); | 855 DCHECK_LT(0, it->second); |
| (...skipping 20 matching lines...) Expand all Loading... |
| 758 | 876 |
| 759 bool FileCache::IsEvictable(const std::string& id, const ResourceEntry& entry) { | 877 bool FileCache::IsEvictable(const std::string& id, const ResourceEntry& entry) { |
| 760 return entry.file_specific_info().has_cache_state() && | 878 return entry.file_specific_info().has_cache_state() && |
| 761 !entry.file_specific_info().cache_state().is_pinned() && | 879 !entry.file_specific_info().cache_state().is_pinned() && |
| 762 !entry.file_specific_info().cache_state().is_dirty() && | 880 !entry.file_specific_info().cache_state().is_dirty() && |
| 763 !mounted_files_.count(id); | 881 !mounted_files_.count(id); |
| 764 } | 882 } |
| 765 | 883 |
| 766 } // namespace internal | 884 } // namespace internal |
| 767 } // namespace drive | 885 } // namespace drive |
| OLD | NEW |