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

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

Issue 1989863005: Revert of Mark removable Drive caches for cryptohome. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebase. 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 <linux/fs.h> 7 #include <unistd.h>
8 #include <sys/ioctl.h>
9 #include <sys/xattr.h>
10 8
11 #include <queue> 9 #include <queue>
12 #include <vector> 10 #include <vector>
13 11
14 #include "base/bind.h" 12 #include "base/bind.h"
15 #include "base/bind_helpers.h" 13 #include "base/bind_helpers.h"
16 #include "base/callback_helpers.h" 14 #include "base/callback_helpers.h"
17 #include "base/files/file.h"
18 #include "base/files/file_enumerator.h" 15 #include "base/files/file_enumerator.h"
19 #include "base/files/file_util.h" 16 #include "base/files/file_util.h"
20 #include "base/location.h" 17 #include "base/location.h"
21 #include "base/logging.h" 18 #include "base/logging.h"
22 #include "base/metrics/histogram.h" 19 #include "base/metrics/histogram.h"
23 #include "base/stl_util.h"
24 #include "base/strings/string_util.h" 20 #include "base/strings/string_util.h"
25 #include "base/strings/stringprintf.h" 21 #include "base/strings/stringprintf.h"
26 #include "base/sys_info.h" 22 #include "base/sys_info.h"
27 #include "build/build_config.h" 23 #include "build/build_config.h"
28 #include "components/drive/drive.pb.h" 24 #include "components/drive/drive.pb.h"
29 #include "components/drive/drive_api_util.h" 25 #include "components/drive/drive_api_util.h"
30 #include "components/drive/file_system_core_util.h" 26 #include "components/drive/file_system_core_util.h"
31 #include "components/drive/resource_metadata_storage.h" 27 #include "components/drive/resource_metadata_storage.h"
32 #include "google_apis/drive/task_util.h" 28 #include "google_apis/drive/task_util.h"
33 #include "net/base/filename_util.h" 29 #include "net/base/filename_util.h"
34 #include "net/base/mime_sniffer.h" 30 #include "net/base/mime_sniffer.h"
35 #include "net/base/mime_util.h" 31 #include "net/base/mime_util.h"
36 32
37 namespace drive { 33 namespace drive {
38 namespace internal { 34 namespace internal {
39 namespace { 35 namespace {
40 36
41 typedef std::pair<base::File::Info, ResourceEntry> CacheInfo;
42 typedef long FileAttributes; // NOLINT(runtime/int)
43
44 // Returns ID extracted from the path. 37 // Returns ID extracted from the path.
45 std::string GetIdFromPath(const base::FilePath& path) { 38 std::string GetIdFromPath(const base::FilePath& path) {
46 return util::UnescapeCacheFileName(path.BaseName().AsUTF8Unsafe()); 39 return util::UnescapeCacheFileName(path.BaseName().AsUTF8Unsafe());
47 } 40 }
48 41
49 base::FilePath GetPathForId(const base::FilePath& cache_directory, 42 base::FilePath GetPathForId(const base::FilePath& cache_directory,
50 const std::string& id) { 43 const std::string& id) {
51 return cache_directory.Append( 44 return cache_directory.Append(
52 base::FilePath::FromUTF8Unsafe(util::EscapeCacheFileName(id))); 45 base::FilePath::FromUTF8Unsafe(util::EscapeCacheFileName(id)));
53 } 46 }
54 47
55 // Sets extended file attribute as |name| |value| pair. 48 typedef std::pair<base::File::Info, ResourceEntry> CacheInfo;
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 }
119 49
120 class CacheInfoLatestCompare { 50 class CacheInfoLatestCompare {
121 public: 51 public:
122 bool operator()(const CacheInfo& info_a, const CacheInfo& info_b) { 52 bool operator()(const CacheInfo& info_a, const CacheInfo& info_b) {
123 return info_a.first.last_accessed < info_b.first.last_accessed; 53 return info_a.first.last_accessed < info_b.first.last_accessed;
124 } 54 }
125 }; 55 };
126 56
127 const size_t kMaxNumOfEvictedCacheFiles = 30000; 57 const size_t kMaxNumOfEvictedCacheFiles = 30000;
128 58
129 } // namespace 59 } // namespace
130 60
131 // static
132 const char FileCache::kGCacheFilesAttribute[] = "user.GCacheFiles";
133
134 FileCache::FileCache(ResourceMetadataStorage* storage, 61 FileCache::FileCache(ResourceMetadataStorage* storage,
135 const base::FilePath& cache_file_directory, 62 const base::FilePath& cache_file_directory,
136 base::SequencedTaskRunner* blocking_task_runner, 63 base::SequencedTaskRunner* blocking_task_runner,
137 FreeDiskSpaceGetterInterface* free_disk_space_getter) 64 FreeDiskSpaceGetterInterface* free_disk_space_getter)
138 : cache_file_directory_(cache_file_directory), 65 : cache_file_directory_(cache_file_directory),
139 blocking_task_runner_(blocking_task_runner), 66 blocking_task_runner_(blocking_task_runner),
140 storage_(storage), 67 storage_(storage),
141 free_disk_space_getter_(free_disk_space_getter), 68 free_disk_space_getter_(free_disk_space_getter),
142 max_num_of_evicted_cache_files_(kMaxNumOfEvictedCacheFiles), 69 max_num_of_evicted_cache_files_(kMaxNumOfEvictedCacheFiles),
143 weak_ptr_factory_(this) { 70 weak_ptr_factory_(this) {
(...skipping 204 matching lines...) Expand 10 before | Expand all | Expand 10 after
348 return FILE_ERROR_FAILED; 275 return FILE_ERROR_FAILED;
349 } 276 }
350 277
351 // Now that file operations have completed, update metadata. 278 // Now that file operations have completed, update metadata.
352 FileCacheEntry* cache_state = 279 FileCacheEntry* cache_state =
353 entry.mutable_file_specific_info()->mutable_cache_state(); 280 entry.mutable_file_specific_info()->mutable_cache_state();
354 cache_state->set_md5(md5); 281 cache_state->set_md5(md5);
355 cache_state->set_is_present(true); 282 cache_state->set_is_present(true);
356 if (md5.empty()) 283 if (md5.empty())
357 cache_state->set_is_dirty(true); 284 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
367 return storage_->PutEntry(entry); 285 return storage_->PutEntry(entry);
368 } 286 }
369 287
370 FileError FileCache::Pin(const std::string& id) { 288 FileError FileCache::Pin(const std::string& id) {
371 AssertOnSequencedWorkerPool(); 289 AssertOnSequencedWorkerPool();
372 290
373 ResourceEntry entry; 291 ResourceEntry entry;
374 FileError error = storage_->GetEntry(id, &entry); 292 FileError error = storage_->GetEntry(id, &entry);
375 if (error != FILE_ERROR_OK) 293 if (error != FILE_ERROR_OK)
376 return error; 294 return error;
377
378 entry.mutable_file_specific_info()->mutable_cache_state()->set_is_pinned( 295 entry.mutable_file_specific_info()->mutable_cache_state()->set_is_pinned(
379 true); 296 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
388 return storage_->PutEntry(entry); 297 return storage_->PutEntry(entry);
389 } 298 }
390 299
391 FileError FileCache::Unpin(const std::string& id) { 300 FileError FileCache::Unpin(const std::string& id) {
392 AssertOnSequencedWorkerPool(); 301 AssertOnSequencedWorkerPool();
393 302
394 // Unpinning a file means its entry must exist in cache. 303 // Unpinning a file means its entry must exist in cache.
395 ResourceEntry entry; 304 ResourceEntry entry;
396 FileError error = storage_->GetEntry(id, &entry); 305 FileError error = storage_->GetEntry(id, &entry);
397 if (error != FILE_ERROR_OK) 306 if (error != FILE_ERROR_OK)
398 return error; 307 return error;
399 308
400 // Now that file operations have completed, update metadata. 309 // Now that file operations have completed, update metadata.
401 if (entry.file_specific_info().cache_state().is_present()) { 310 if (entry.file_specific_info().cache_state().is_present()) {
402 entry.mutable_file_specific_info()->mutable_cache_state()->set_is_pinned( 311 entry.mutable_file_specific_info()->mutable_cache_state()->set_is_pinned(
403 false); 312 false);
404 if (!entry.file_specific_info().cache_state().is_dirty()) {
405 if (!SetRemovable(GetCacheFilePath(entry.local_id())))
406 return FILE_ERROR_FAILED;
407 }
408 } else { 313 } else {
409 // Remove the existing entry if we are unpinning a non-present file. 314 // Remove the existing entry if we are unpinning a non-present file.
410 entry.mutable_file_specific_info()->clear_cache_state(); 315 entry.mutable_file_specific_info()->clear_cache_state();
411 } 316 }
412 error = storage_->PutEntry(entry); 317 error = storage_->PutEntry(entry);
413 if (error != FILE_ERROR_OK) 318 if (error != FILE_ERROR_OK)
414 return error; 319 return error;
415 320
416 // Now it's a chance to free up space if needed. 321 // Now it's a chance to free up space if needed.
417 FreeDiskSpaceIfNeededFor(0); 322 FreeDiskSpaceIfNeededFor(0);
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
464 ResourceEntry entry; 369 ResourceEntry entry;
465 FileError error = storage_->GetEntry(id, &entry); 370 FileError error = storage_->GetEntry(id, &entry);
466 if (error != FILE_ERROR_OK) 371 if (error != FILE_ERROR_OK)
467 return error; 372 return error;
468 if (!entry.file_specific_info().cache_state().is_present()) { 373 if (!entry.file_specific_info().cache_state().is_present()) {
469 LOG(WARNING) << "Can't mark dirty a file that wasn't cached: " << id; 374 LOG(WARNING) << "Can't mark dirty a file that wasn't cached: " << id;
470 return FILE_ERROR_NOT_FOUND; 375 return FILE_ERROR_NOT_FOUND;
471 } 376 }
472 377
473 entry.mutable_file_specific_info()->mutable_cache_state()->set_is_dirty(true); 378 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
477 entry.mutable_file_specific_info()->mutable_cache_state()->clear_md5(); 379 entry.mutable_file_specific_info()->mutable_cache_state()->clear_md5();
478 error = storage_->PutEntry(entry); 380 error = storage_->PutEntry(entry);
479 if (error != FILE_ERROR_OK) 381 if (error != FILE_ERROR_OK)
480 return error; 382 return error;
481 383
482 write_opened_files_[id]++; 384 write_opened_files_[id]++;
483 file_closer->reset(new base::ScopedClosureRunner( 385 file_closer->reset(new base::ScopedClosureRunner(
484 base::Bind(&google_apis::RunTaskWithTaskRunner, 386 base::Bind(&google_apis::RunTaskWithTaskRunner,
485 blocking_task_runner_, 387 blocking_task_runner_,
486 base::Bind(&FileCache::CloseForWrite, 388 base::Bind(&FileCache::CloseForWrite,
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
538 440
539 // If a file is not dirty (it should have been marked dirty via OpenForWrite), 441 // If a file is not dirty (it should have been marked dirty via OpenForWrite),
540 // clearing its dirty state is an invalid operation. 442 // clearing its dirty state is an invalid operation.
541 if (!entry.file_specific_info().cache_state().is_dirty()) { 443 if (!entry.file_specific_info().cache_state().is_dirty()) {
542 LOG(WARNING) << "Can't clear dirty state of a non-dirty file: " << id; 444 LOG(WARNING) << "Can't clear dirty state of a non-dirty file: " << id;
543 return FILE_ERROR_INVALID_OPERATION; 445 return FILE_ERROR_INVALID_OPERATION;
544 } 446 }
545 447
546 entry.mutable_file_specific_info()->mutable_cache_state()->set_is_dirty( 448 entry.mutable_file_specific_info()->mutable_cache_state()->set_is_dirty(
547 false); 449 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
553 return storage_->PutEntry(entry); 450 return storage_->PutEntry(entry);
554 } 451 }
555 452
556 FileError FileCache::Remove(const std::string& id) { 453 FileError FileCache::Remove(const std::string& id) {
557 AssertOnSequencedWorkerPool(); 454 AssertOnSequencedWorkerPool();
558 455
559 ResourceEntry entry; 456 ResourceEntry entry;
560 457
561 // If entry doesn't exist, nothing to do. 458 // If entry doesn't exist, nothing to do.
562 FileError error = storage_->GetEntry(id, &entry); 459 FileError error = storage_->GetEntry(id, &entry);
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
609 clear_md5(); 506 clear_md5();
610 if (storage_->PutEntry(new_entry) != FILE_ERROR_OK) 507 if (storage_->PutEntry(new_entry) != FILE_ERROR_OK)
611 return false; 508 return false;
612 } 509 }
613 } 510 }
614 if (it->HasError()) 511 if (it->HasError())
615 return false; 512 return false;
616 513
617 if (!RenameCacheFilesToNewFormat()) 514 if (!RenameCacheFilesToNewFormat())
618 return false; 515 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
626 return true; 516 return true;
627 } 517 }
628 518
629 void FileCache::Destroy() { 519 void FileCache::Destroy() {
630 DCHECK(thread_checker_.CalledOnValidThread()); 520 DCHECK(thread_checker_.CalledOnValidThread());
631 521
632 in_shutdown_.Set(); 522 in_shutdown_.Set();
633 523
634 // Destroy myself on the blocking pool. 524 // Destroy myself on the blocking pool.
635 // Note that base::DeletePointer<> cannot be used as the destructor of this 525 // Note that base::DeletePointer<> cannot be used as the destructor of this
(...skipping 151 matching lines...) Expand 10 before | Expand all | Expand 10 after
787 continue; 677 continue;
788 } 678 }
789 const std::string& id = GetIdFromPath(new_path); 679 const std::string& id = GetIdFromPath(new_path);
790 new_path = GetCacheFilePath(util::CanonicalizeResourceId(id)); 680 new_path = GetCacheFilePath(util::CanonicalizeResourceId(id));
791 if (new_path != current && !base::Move(current, new_path)) 681 if (new_path != current && !base::Move(current, new_path))
792 return false; 682 return false;
793 } 683 }
794 return true; 684 return true;
795 } 685 }
796 686
797 bool FileCache::FixMetadataAndFileAttributes() { 687 // static
688 bool FileCache::MigrateCacheFiles(const base::FilePath& from,
689 const base::FilePath& to_files,
690 const base::FilePath& to_links,
691 ResourceMetadataStorage* metadata_storage) {
798 std::unique_ptr<ResourceMetadataStorage::Iterator> it = 692 std::unique_ptr<ResourceMetadataStorage::Iterator> it =
799 storage_->GetIterator(); 693 metadata_storage->GetIterator();
694 for (; !it->IsAtEnd(); it->Advance()) {
695 const ResourceEntry& entry = it->GetValue();
696 if (!entry.file_specific_info().cache_state().is_present()) {
697 continue;
698 }
800 699
801 for (; !it->IsAtEnd(); it->Advance()) { 700 // Ignore missing cache file case since it never succeeds.
802 ResourceEntry entry = it->GetValue(); 701 // TODO(yawano): handle this case at metadata validation in FileCache.
803 FileCacheEntry* file_cache_entry = 702 const base::FilePath move_from = GetPathForId(from, entry.local_id());
804 entry.mutable_file_specific_info()->mutable_cache_state(); 703 if (!base::PathExists(move_from)) {
704 continue;
705 }
805 706
806 const base::FilePath filepath = GetPathForId(cache_file_directory_, 707 // Create hard link to cache file if it's pinned or dirty. cryptohome will
807 entry.local_id()); 708 // not delete a cache file if there is a hard link to it.
709 const FileCacheEntry& file_cache_entry =
710 entry.file_specific_info().cache_state();
711 if (file_cache_entry.is_pinned() || file_cache_entry.is_dirty()) {
712 const base::FilePath link_path = GetPathForId(to_links, entry.local_id());
713 int link_result = link(move_from.AsUTF8Unsafe().c_str(),
714 link_path.AsUTF8Unsafe().c_str());
715 if (link_result != 0 && errno != EEXIST) {
716 return false;
717 }
718 }
808 719
809 if (base::PathExists(filepath)) { 720 // Move cache file.
810 if (file_cache_entry->is_present()) { 721 const base::FilePath move_to = GetPathForId(to_files, entry.local_id());
811 // Update file attribues for cryptohome. 722 if (!base::Move(move_from, move_to)) {
812 if (file_cache_entry->is_pinned() || file_cache_entry->is_dirty()) { 723 return false;
813 if (!UnsetRemovable(filepath)) return false;
814 } else {
815 if (!SetRemovable(filepath)) return false;
816 }
817 } else {
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;
825 }
826 } else {
827 // Update metatadata if there is no file but metadata says there is.
828 // It happens when cryptohome removed the file.
829 // We don't clear is_pinned here, so that file download is restarted on
830 // the following scenario:
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 }
842 } 724 }
843 } 725 }
844 726
845 return MarkAsDriveCacheDir(cache_file_directory_); 727 return true;
846 } 728 }
847 729
848 void FileCache::CloseForWrite(const std::string& id) { 730 void FileCache::CloseForWrite(const std::string& id) {
849 AssertOnSequencedWorkerPool(); 731 AssertOnSequencedWorkerPool();
850 732
851 std::map<std::string, int>::iterator it = write_opened_files_.find(id); 733 std::map<std::string, int>::iterator it = write_opened_files_.find(id);
852 if (it == write_opened_files_.end()) 734 if (it == write_opened_files_.end())
853 return; 735 return;
854 736
855 DCHECK_LT(0, it->second); 737 DCHECK_LT(0, it->second);
(...skipping 20 matching lines...) Expand all
876 758
877 bool FileCache::IsEvictable(const std::string& id, const ResourceEntry& entry) { 759 bool FileCache::IsEvictable(const std::string& id, const ResourceEntry& entry) {
878 return entry.file_specific_info().has_cache_state() && 760 return entry.file_specific_info().has_cache_state() &&
879 !entry.file_specific_info().cache_state().is_pinned() && 761 !entry.file_specific_info().cache_state().is_pinned() &&
880 !entry.file_specific_info().cache_state().is_dirty() && 762 !entry.file_specific_info().cache_state().is_dirty() &&
881 !mounted_files_.count(id); 763 !mounted_files_.count(id);
882 } 764 }
883 765
884 } // namespace internal 766 } // namespace internal
885 } // namespace drive 767 } // 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