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

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) {
hashimoto 2016/05/10 10:33:48 Does this code work on a 32-bit device (e.g. arm)?
oka 2016/05/11 04:24:10 Used long. Same for GetFileAttributes.
hashimoto 2016/05/11 06:29:12 Please also replace int64_t above with long. The s
oka 2016/05/11 09:35:04 Done. Used typedef.
64 base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_READ);
65 if (!file.IsValid()) return false;
66 if (ioctl(file.GetPlatformFile(), FS_IOC_SETFLAGS, &flags) < 0) {
67 PLOG(ERROR) << "ioctl: " << path.value();
68 return false;
69 }
70 return true;
71 }
72
73 // Gets file attributes similarly to lsattr(1). Returns flags or -1 on error.
74 // See linux/fs.h for the definition of the returned flags e.g. FS_NODUMP_FL.
75 int64_t GetFileAttributes(const base::FilePath& path) {
76 base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_READ);
77 if (!file.IsValid()) {
78 return -1;
79 }
80 int64_t flags = 0;
81 if (ioctl(file.GetPlatformFile(), FS_IOC_GETFLAGS, &flags) < 0) {
82 PLOG(ERROR) << "ioctl: " << path.value();
83 return -1;
84 }
85 return flags;
86 }
87
88 // Marks the cache file to be removable by cryptohome.
89 bool SetRemovable(const base::FilePath& path) {
90 int64_t flags = GetFileAttributes(path);
91 if (flags < 0) return false;
92 if ((flags & FS_NODUMP_FL) == FS_NODUMP_FL) return true;
93
94 return SetFileAttributes(path, flags | FS_NODUMP_FL);
95 }
96
97 // Marks the cache file to be unremovable by cryptohome.
98 bool UnsetRemovable(const base::FilePath& path) {
99 int64_t flags = GetFileAttributes(path);
100 if (flags < 0) return false;
101 if ((flags & FS_NODUMP_FL) == 0) return true;
102
103 return SetFileAttributes(path, flags & ~(static_cast<int64_t>(FS_NODUMP_FL)));
104 }
105
106 // Marks |path| as drive cache dir.
107 // Returns if the operation succeeded.
108 bool MarkAsDriveCacheDir(const base::FilePath& path) {
109 return SetRemovable(path)
110 && SetExtendedFileAttributes(path, FileCache::kGCacheFilesAttribute, "");
111 }
112
48 typedef std::pair<base::File::Info, ResourceEntry> CacheInfo; 113 typedef std::pair<base::File::Info, ResourceEntry> CacheInfo;
49 114
50 class CacheInfoLatestCompare { 115 class CacheInfoLatestCompare {
51 public: 116 public:
52 bool operator()(const CacheInfo& info_a, const CacheInfo& info_b) { 117 bool operator()(const CacheInfo& info_a, const CacheInfo& info_b) {
53 return info_a.first.last_accessed < info_b.first.last_accessed; 118 return info_a.first.last_accessed < info_b.first.last_accessed;
54 } 119 }
55 }; 120 };
56 121
57 const size_t kMaxNumOfEvictedCacheFiles = 30000; 122 const size_t kMaxNumOfEvictedCacheFiles = 30000;
58 123
59 } // namespace 124 } // namespace
60 125
126 // static
127 const std::string FileCache::kGCacheFilesAttribute = "user.GCacheFiles";
128
61 FileCache::FileCache(ResourceMetadataStorage* storage, 129 FileCache::FileCache(ResourceMetadataStorage* storage,
62 const base::FilePath& cache_file_directory, 130 const base::FilePath& cache_file_directory,
63 base::SequencedTaskRunner* blocking_task_runner, 131 base::SequencedTaskRunner* blocking_task_runner,
64 FreeDiskSpaceGetterInterface* free_disk_space_getter) 132 FreeDiskSpaceGetterInterface* free_disk_space_getter)
65 : cache_file_directory_(cache_file_directory), 133 : cache_file_directory_(cache_file_directory),
66 blocking_task_runner_(blocking_task_runner), 134 blocking_task_runner_(blocking_task_runner),
67 storage_(storage), 135 storage_(storage),
68 free_disk_space_getter_(free_disk_space_getter), 136 free_disk_space_getter_(free_disk_space_getter),
69 max_num_of_evicted_cache_files_(kMaxNumOfEvictedCacheFiles), 137 max_num_of_evicted_cache_files_(kMaxNumOfEvictedCacheFiles),
70 weak_ptr_factory_(this) { 138 weak_ptr_factory_(this) {
(...skipping 204 matching lines...) Expand 10 before | Expand all | Expand 10 after
275 return FILE_ERROR_FAILED; 343 return FILE_ERROR_FAILED;
276 } 344 }
277 345
278 // Now that file operations have completed, update metadata. 346 // Now that file operations have completed, update metadata.
279 FileCacheEntry* cache_state = 347 FileCacheEntry* cache_state =
280 entry.mutable_file_specific_info()->mutable_cache_state(); 348 entry.mutable_file_specific_info()->mutable_cache_state();
281 cache_state->set_md5(md5); 349 cache_state->set_md5(md5);
282 cache_state->set_is_present(true); 350 cache_state->set_is_present(true);
283 if (md5.empty()) 351 if (md5.empty())
284 cache_state->set_is_dirty(true); 352 cache_state->set_is_dirty(true);
353
354 if (!cache_state->is_pinned() && !cache_state->is_dirty()) {
355 if (!SetRemovable(dest_path))
356 return FILE_ERROR_FAILED;
357 } else {
358 if (!UnsetRemovable(dest_path))
359 return FILE_ERROR_FAILED;
360 }
361
285 return storage_->PutEntry(entry); 362 return storage_->PutEntry(entry);
286 } 363 }
287 364
288 FileError FileCache::Pin(const std::string& id) { 365 FileError FileCache::Pin(const std::string& id) {
289 AssertOnSequencedWorkerPool(); 366 AssertOnSequencedWorkerPool();
290 367
291 ResourceEntry entry; 368 ResourceEntry entry;
292 FileError error = storage_->GetEntry(id, &entry); 369 FileError error = storage_->GetEntry(id, &entry);
293 if (error != FILE_ERROR_OK) 370 if (error != FILE_ERROR_OK)
294 return error; 371 return error;
372
295 entry.mutable_file_specific_info()->mutable_cache_state()->set_is_pinned( 373 entry.mutable_file_specific_info()->mutable_cache_state()->set_is_pinned(
296 true); 374 true);
375
376 base::FilePath file_path = GetCacheFilePath(entry.local_id());
377 // Cache file can be created later.
378 if (base::PathExists(file_path)) {
hashimoto 2016/05/10 10:33:48 Checking is_present() is better than base::PathExi
oka 2016/05/11 04:24:10 Done.
379 if (!UnsetRemovable(file_path))
380 return FILE_ERROR_FAILED;
381 }
382
297 return storage_->PutEntry(entry); 383 return storage_->PutEntry(entry);
298 } 384 }
299 385
300 FileError FileCache::Unpin(const std::string& id) { 386 FileError FileCache::Unpin(const std::string& id) {
301 AssertOnSequencedWorkerPool(); 387 AssertOnSequencedWorkerPool();
302 388
303 // Unpinning a file means its entry must exist in cache. 389 // Unpinning a file means its entry must exist in cache.
304 ResourceEntry entry; 390 ResourceEntry entry;
305 FileError error = storage_->GetEntry(id, &entry); 391 FileError error = storage_->GetEntry(id, &entry);
306 if (error != FILE_ERROR_OK) 392 if (error != FILE_ERROR_OK)
307 return error; 393 return error;
308 394
309 // Now that file operations have completed, update metadata. 395 // Now that file operations have completed, update metadata.
310 if (entry.file_specific_info().cache_state().is_present()) { 396 if (entry.file_specific_info().cache_state().is_present()) {
311 entry.mutable_file_specific_info()->mutable_cache_state()->set_is_pinned( 397 entry.mutable_file_specific_info()->mutable_cache_state()->set_is_pinned(
312 false); 398 false);
399 if (!entry.file_specific_info().cache_state().is_dirty()) {
400 if (!SetRemovable(GetCacheFilePath(entry.local_id())))
401 return FILE_ERROR_FAILED;
402 }
313 } else { 403 } else {
314 // Remove the existing entry if we are unpinning a non-present file. 404 // Remove the existing entry if we are unpinning a non-present file.
315 entry.mutable_file_specific_info()->clear_cache_state(); 405 entry.mutable_file_specific_info()->clear_cache_state();
316 } 406 }
317 error = storage_->PutEntry(entry); 407 error = storage_->PutEntry(entry);
318 if (error != FILE_ERROR_OK) 408 if (error != FILE_ERROR_OK)
319 return error; 409 return error;
320 410
321 // Now it's a chance to free up space if needed. 411 // Now it's a chance to free up space if needed.
322 FreeDiskSpaceIfNeededFor(0); 412 FreeDiskSpaceIfNeededFor(0);
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
369 ResourceEntry entry; 459 ResourceEntry entry;
370 FileError error = storage_->GetEntry(id, &entry); 460 FileError error = storage_->GetEntry(id, &entry);
371 if (error != FILE_ERROR_OK) 461 if (error != FILE_ERROR_OK)
372 return error; 462 return error;
373 if (!entry.file_specific_info().cache_state().is_present()) { 463 if (!entry.file_specific_info().cache_state().is_present()) {
374 LOG(WARNING) << "Can't mark dirty a file that wasn't cached: " << id; 464 LOG(WARNING) << "Can't mark dirty a file that wasn't cached: " << id;
375 return FILE_ERROR_NOT_FOUND; 465 return FILE_ERROR_NOT_FOUND;
376 } 466 }
377 467
378 entry.mutable_file_specific_info()->mutable_cache_state()->set_is_dirty(true); 468 entry.mutable_file_specific_info()->mutable_cache_state()->set_is_dirty(true);
469 if (!UnsetRemovable(GetCacheFilePath(entry.local_id())))
470 return FILE_ERROR_FAILED;
471
379 entry.mutable_file_specific_info()->mutable_cache_state()->clear_md5(); 472 entry.mutable_file_specific_info()->mutable_cache_state()->clear_md5();
380 error = storage_->PutEntry(entry); 473 error = storage_->PutEntry(entry);
381 if (error != FILE_ERROR_OK) 474 if (error != FILE_ERROR_OK)
382 return error; 475 return error;
383 476
384 write_opened_files_[id]++; 477 write_opened_files_[id]++;
385 file_closer->reset(new base::ScopedClosureRunner( 478 file_closer->reset(new base::ScopedClosureRunner(
386 base::Bind(&google_apis::RunTaskWithTaskRunner, 479 base::Bind(&google_apis::RunTaskWithTaskRunner,
387 blocking_task_runner_, 480 blocking_task_runner_,
388 base::Bind(&FileCache::CloseForWrite, 481 base::Bind(&FileCache::CloseForWrite,
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
440 533
441 // If a file is not dirty (it should have been marked dirty via OpenForWrite), 534 // If a file is not dirty (it should have been marked dirty via OpenForWrite),
442 // clearing its dirty state is an invalid operation. 535 // clearing its dirty state is an invalid operation.
443 if (!entry.file_specific_info().cache_state().is_dirty()) { 536 if (!entry.file_specific_info().cache_state().is_dirty()) {
444 LOG(WARNING) << "Can't clear dirty state of a non-dirty file: " << id; 537 LOG(WARNING) << "Can't clear dirty state of a non-dirty file: " << id;
445 return FILE_ERROR_INVALID_OPERATION; 538 return FILE_ERROR_INVALID_OPERATION;
446 } 539 }
447 540
448 entry.mutable_file_specific_info()->mutable_cache_state()->set_is_dirty( 541 entry.mutable_file_specific_info()->mutable_cache_state()->set_is_dirty(
449 false); 542 false);
543 if (!entry.file_specific_info().cache_state().is_pinned()) {
544 if (!SetRemovable(GetCacheFilePath(entry.local_id())))
545 return FILE_ERROR_FAILED;
546 }
547
450 return storage_->PutEntry(entry); 548 return storage_->PutEntry(entry);
451 } 549 }
452 550
453 FileError FileCache::Remove(const std::string& id) { 551 FileError FileCache::Remove(const std::string& id) {
454 AssertOnSequencedWorkerPool(); 552 AssertOnSequencedWorkerPool();
455 553
456 ResourceEntry entry; 554 ResourceEntry entry;
457 555
458 // If entry doesn't exist, nothing to do. 556 // If entry doesn't exist, nothing to do.
459 FileError error = storage_->GetEntry(id, &entry); 557 FileError error = storage_->GetEntry(id, &entry);
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
506 clear_md5(); 604 clear_md5();
507 if (storage_->PutEntry(new_entry) != FILE_ERROR_OK) 605 if (storage_->PutEntry(new_entry) != FILE_ERROR_OK)
508 return false; 606 return false;
509 } 607 }
510 } 608 }
511 if (it->HasError()) 609 if (it->HasError())
512 return false; 610 return false;
513 611
514 if (!RenameCacheFilesToNewFormat()) 612 if (!RenameCacheFilesToNewFormat())
515 return false; 613 return false;
614
615 // Run this every time to resolve inconsistency between metadata
616 // and file attributes which possibly occurs on abrupt power failure.
617 if (!MigrateCacheFilesResolvingInconsistency()) {
618 return false;
619 }
620
516 return true; 621 return true;
517 } 622 }
518 623
519 void FileCache::Destroy() { 624 void FileCache::Destroy() {
520 DCHECK(thread_checker_.CalledOnValidThread()); 625 DCHECK(thread_checker_.CalledOnValidThread());
521 626
522 in_shutdown_.Set(); 627 in_shutdown_.Set();
523 628
524 // Destroy myself on the blocking pool. 629 // Destroy myself on the blocking pool.
525 // Note that base::DeletePointer<> cannot be used as the destructor of this 630 // 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; 782 continue;
678 } 783 }
679 const std::string& id = GetIdFromPath(new_path); 784 const std::string& id = GetIdFromPath(new_path);
680 new_path = GetCacheFilePath(util::CanonicalizeResourceId(id)); 785 new_path = GetCacheFilePath(util::CanonicalizeResourceId(id));
681 if (new_path != current && !base::Move(current, new_path)) 786 if (new_path != current && !base::Move(current, new_path))
682 return false; 787 return false;
683 } 788 }
684 return true; 789 return true;
685 } 790 }
686 791
687 // static 792 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 = 793 std::unique_ptr<ResourceMetadataStorage::Iterator> it =
693 metadata_storage->GetIterator(); 794 storage_->GetIterator();
795
694 for (; !it->IsAtEnd(); it->Advance()) { 796 for (; !it->IsAtEnd(); it->Advance()) {
695 const ResourceEntry& entry = it->GetValue(); 797 ResourceEntry entry = it->GetValue();
696 if (!entry.file_specific_info().cache_state().is_present()) { 798 const base::FilePath filepath = GetPathForId(cache_file_directory_,
697 continue; 799 entry.local_id());
800 const bool fileIsPresent = base::PathExists(filepath);
hashimoto 2016/05/10 10:33:48 nit: The names of variables and data members shoul
oka 2016/05/11 04:24:10 Done.
801
802 // Update metadata on inconsistency.
803 if (fileIsPresent !=
804 entry.file_specific_info().cache_state().is_present()) {
805 if (fileIsPresent) {
806 // This happens only on abrupt shutdown. Opposite is expected
807 // after cryptohome runs.
808 LOG(WARNING) << "File is present but metadata's state is inconsistent.";
809 }
810 entry.mutable_file_specific_info()->mutable_cache_state()->set_is_present(
hashimoto 2016/05/10 10:33:48 !file exists && is_present case: Shouldn't we clea
oka 2016/05/11 04:24:10 Done. I think removing the file is fine since this
811 fileIsPresent);
812 if (storage_->PutEntry(entry) != FILE_ERROR_OK)
813 return false;
698 } 814 }
699 815
700 // Ignore missing cache file case since it never succeeds. 816 if (!fileIsPresent) continue;
701 // TODO(yawano): handle this case at metadata validation in FileCache.
702 const base::FilePath move_from = GetPathForId(from, entry.local_id());
703 if (!base::PathExists(move_from)) {
704 continue;
705 }
706 817
707 // Create hard link to cache file if it's pinned or dirty. cryptohome will 818 // Update file attribues for cryptohome.
708 // not delete a cache file if there is a hard link to it.
709 const FileCacheEntry& file_cache_entry = 819 const FileCacheEntry& file_cache_entry =
710 entry.file_specific_info().cache_state(); 820 entry.file_specific_info().cache_state();
711 if (file_cache_entry.is_pinned() || file_cache_entry.is_dirty()) { 821 if (file_cache_entry.is_pinned() || file_cache_entry.is_dirty()) {
712 const base::FilePath link_path = GetPathForId(to_links, entry.local_id()); 822 if (!UnsetRemovable(filepath)) return false;
713 int link_result = link(move_from.AsUTF8Unsafe().c_str(), 823 } else {
714 link_path.AsUTF8Unsafe().c_str()); 824 if (!SetRemovable(filepath)) return false;
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 } 825 }
725 } 826 }
726 827
727 return true; 828 return MarkAsDriveCacheDir(cache_file_directory_);
728 } 829 }
729 830
730 void FileCache::CloseForWrite(const std::string& id) { 831 void FileCache::CloseForWrite(const std::string& id) {
731 AssertOnSequencedWorkerPool(); 832 AssertOnSequencedWorkerPool();
732 833
733 std::map<std::string, int>::iterator it = write_opened_files_.find(id); 834 std::map<std::string, int>::iterator it = write_opened_files_.find(id);
734 if (it == write_opened_files_.end()) 835 if (it == write_opened_files_.end())
735 return; 836 return;
736 837
737 DCHECK_LT(0, it->second); 838 DCHECK_LT(0, it->second);
(...skipping 20 matching lines...) Expand all
758 859
759 bool FileCache::IsEvictable(const std::string& id, const ResourceEntry& entry) { 860 bool FileCache::IsEvictable(const std::string& id, const ResourceEntry& entry) {
760 return entry.file_specific_info().has_cache_state() && 861 return entry.file_specific_info().has_cache_state() &&
761 !entry.file_specific_info().cache_state().is_pinned() && 862 !entry.file_specific_info().cache_state().is_pinned() &&
762 !entry.file_specific_info().cache_state().is_dirty() && 863 !entry.file_specific_info().cache_state().is_dirty() &&
763 !mounted_files_.count(id); 864 !mounted_files_.count(id);
764 } 865 }
765 866
766 } // namespace internal 867 } // namespace internal
767 } // namespace drive 868 } // namespace drive
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698