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

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

Issue 1918243004: Mark removable Drive caches for cryptohome. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Nit 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
37 // Returns ID extracted from the path. 41 // Returns ID extracted from the path.
38 std::string GetIdFromPath(const base::FilePath& path) { 42 std::string GetIdFromPath(const base::FilePath& path) {
39 return util::UnescapeCacheFileName(path.BaseName().AsUTF8Unsafe()); 43 return util::UnescapeCacheFileName(path.BaseName().AsUTF8Unsafe());
40 } 44 }
41 45
42 base::FilePath GetPathForId(const base::FilePath& cache_directory, 46 base::FilePath GetPathForId(const base::FilePath& cache_directory,
43 const std::string& id) { 47 const std::string& id) {
44 return cache_directory.Append( 48 return cache_directory.Append(
45 base::FilePath::FromUTF8Unsafe(util::EscapeCacheFileName(id))); 49 base::FilePath::FromUTF8Unsafe(util::EscapeCacheFileName(id)));
46 } 50 }
47 51
52 // Sets extended file attribute as |name| |value| pair.
53 bool SetExtendedFileAttributes(const base::FilePath& path,
54 const std::string& name, const std::string& value) {
55 return setxattr(path.value().c_str(), name.c_str(), value.c_str(),
56 value.size() + 1, 0) == 0;
57 }
58
59 // Changes attributes of the file with |flags|, e.g. FS_NODUMP_FL (cryptohome
60 // will remove Drive caches with this attribute).
61 // See linux/fs.h for available flags, and chattr(1) which does similar thing.
62 // Returns whether operation succeeded.
63 bool SetFileAttributes(const base::FilePath& path, int64_t flags) {
64 base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_READ);
65 if (!file.IsValid()) {
66 PLOG(ERROR) << "Failed to open file: " << path.value();
67 return false;
68 }
69 long f = static_cast<long>(flags); // NOLINT long
70 if (ioctl(file.GetPlatformFile(), FS_IOC_SETFLAGS, &f) < 0) {
71 PLOG(ERROR) << "ioctl: " << path.value();
72 return false;
73 }
74 return true;
75 }
76
77 // Gets file attributes similarly to lsattr(1). Returns flags or -1 on error.
78 // See linux/fs.h for the definition of the returned flags e.g. FS_NODUMP_FL.
79 int64_t GetFileAttributes(const base::FilePath& path) {
80 base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_READ);
81 if (!file.IsValid()) {
82 PLOG(ERROR) << "Failed to open file: " << path.value();
83 return -1;
84 }
85 long flags = 0; // NOLINT long
86 if (ioctl(file.GetPlatformFile(), FS_IOC_GETFLAGS, &flags) < 0) {
87 PLOG(ERROR) << "ioctl: " << path.value();
88 return -1;
89 }
90 return static_cast<int64_t>(flags);
91 }
92
93 // Marks the cache file to be removable by cryptohome.
94 bool SetRemovable(const base::FilePath& path) {
95 int64_t flags = GetFileAttributes(path);
96 if (flags < 0) return false;
97 if ((flags & FS_NODUMP_FL) == FS_NODUMP_FL) return true;
98
99 return SetFileAttributes(path, flags | FS_NODUMP_FL);
100 }
101
102 // Marks the cache file to be unremovable by cryptohome.
103 bool UnsetRemovable(const base::FilePath& path) {
104 int64_t flags = GetFileAttributes(path);
105 if (flags < 0) return false;
106 if ((flags & FS_NODUMP_FL) == 0) return true;
107
108 return SetFileAttributes(path, flags & ~(static_cast<int64_t>(FS_NODUMP_FL)));
109 }
110
111 // Marks |path| as drive cache dir.
112 // Returns if the operation succeeded.
113 bool MarkAsDriveCacheDir(const base::FilePath& path) {
114 return SetRemovable(path)
115 && SetExtendedFileAttributes(path, FileCache::kGCacheFilesAttribute, "");
116 }
117
48 typedef std::pair<base::File::Info, ResourceEntry> CacheInfo; 118 typedef std::pair<base::File::Info, ResourceEntry> CacheInfo;
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 (!MigrateCacheFilesResolvingInconsistency()) {
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::MigrateCacheFilesResolvingInconsistency() {
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 =
804 entry.mutable_file_specific_info()->mutable_cache_state();
805
806 const base::FilePath filepath = GetPathForId(cache_file_directory_,
807 entry.local_id());
808 const bool file_is_present = base::PathExists(filepath);
809
810 // Delete file if the file is present but metadata says not.
811 // It happens only on abrupt shutdown.
812 if (file_is_present && !file_cache_entry->is_present()) {
813 LOG(WARNING) << "File is present but metadata's state was inconsistent.";
814
815 if (!base::DeleteFile(filepath, false /* recursive */))
hashimoto 2016/05/11 06:29:12 Sorry for not saying this earlier, but I realized
oka 2016/05/11 09:35:04 Discussed offline. Keeping this logic will be OK,
816 return false;
817 continue;
818 }
819 if (!file_is_present) {
820 // Update metatadata if there is no file but metadata says there is.
821 // It happens when cryptohome removed the file.
822 if (file_cache_entry->is_present()) {
823 file_cache_entry->set_is_present(false);
824 file_cache_entry->set_is_pinned(false);
825 file_cache_entry->set_is_dirty(false);
826 file_cache_entry->clear_md5();
827 if (storage_->PutEntry(entry) != FILE_ERROR_OK)
828 return false;
829 }
697 continue; 830 continue;
698 } 831 }
699 832
700 // Ignore missing cache file case since it never succeeds. 833 // Update file attribues for cryptohome.
hashimoto 2016/05/11 06:29:12 As the proposed code is using "continue" in many p
oka 2016/05/11 09:35:04 Done.
701 // TODO(yawano): handle this case at metadata validation in FileCache. 834 if (file_cache_entry->is_pinned() || file_cache_entry->is_dirty()) {
702 const base::FilePath move_from = GetPathForId(from, entry.local_id()); 835 if (!UnsetRemovable(filepath)) return false;
703 if (!base::PathExists(move_from)) { 836 } else {
704 continue; 837 if (!SetRemovable(filepath)) return false;
705 }
706
707 // Create hard link to cache file if it's pinned or dirty. cryptohome will
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 }
719
720 // Move cache file.
721 const base::FilePath move_to = GetPathForId(to_files, entry.local_id());
722 if (!base::Move(move_from, move_to)) {
723 return false;
724 } 838 }
725 } 839 }
726 840
727 return true; 841 return MarkAsDriveCacheDir(cache_file_directory_);
728 } 842 }
729 843
730 void FileCache::CloseForWrite(const std::string& id) { 844 void FileCache::CloseForWrite(const std::string& id) {
731 AssertOnSequencedWorkerPool(); 845 AssertOnSequencedWorkerPool();
732 846
733 std::map<std::string, int>::iterator it = write_opened_files_.find(id); 847 std::map<std::string, int>::iterator it = write_opened_files_.find(id);
734 if (it == write_opened_files_.end()) 848 if (it == write_opened_files_.end())
735 return; 849 return;
736 850
737 DCHECK_LT(0, it->second); 851 DCHECK_LT(0, it->second);
(...skipping 20 matching lines...) Expand all
758 872
759 bool FileCache::IsEvictable(const std::string& id, const ResourceEntry& entry) { 873 bool FileCache::IsEvictable(const std::string& id, const ResourceEntry& entry) {
760 return entry.file_specific_info().has_cache_state() && 874 return entry.file_specific_info().has_cache_state() &&
761 !entry.file_specific_info().cache_state().is_pinned() && 875 !entry.file_specific_info().cache_state().is_pinned() &&
762 !entry.file_specific_info().cache_state().is_dirty() && 876 !entry.file_specific_info().cache_state().is_dirty() &&
763 !mounted_files_.count(id); 877 !mounted_files_.count(id);
764 } 878 }
765 879
766 } // namespace internal 880 } // namespace internal
767 } // namespace drive 881 } // namespace drive
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698