Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(102)

Side by Side Diff: components/drive/chromeos/file_cache.cc

Issue 2006503002: Reland of Mark removable Drive caches for cryptohome. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
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
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
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
OLDNEW
« no previous file with comments | « components/drive/chromeos/file_cache.h ('k') | components/drive/chromeos/file_cache_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698