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

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 <fcntl.h>
8 #include <linux/fs.h>
9 #include <sys/ioctl.h>
10 #include <sys/stat.h>
11 #include <sys/types.h>
12 #include <sys/xattr.h>
7 #include <unistd.h> 13 #include <unistd.h>
8 14
9 #include <queue> 15 #include <queue>
10 #include <vector> 16 #include <vector>
11 17
12 #include "base/bind.h" 18 #include "base/bind.h"
13 #include "base/bind_helpers.h" 19 #include "base/bind_helpers.h"
14 #include "base/callback_helpers.h" 20 #include "base/callback_helpers.h"
15 #include "base/files/file_enumerator.h" 21 #include "base/files/file_enumerator.h"
16 #include "base/files/file_util.h" 22 #include "base/files/file_util.h"
17 #include "base/location.h" 23 #include "base/location.h"
18 #include "base/logging.h" 24 #include "base/logging.h"
19 #include "base/metrics/histogram.h" 25 #include "base/metrics/histogram.h"
26 #include "base/stl_util.h"
20 #include "base/strings/string_util.h" 27 #include "base/strings/string_util.h"
21 #include "base/strings/stringprintf.h" 28 #include "base/strings/stringprintf.h"
22 #include "base/sys_info.h" 29 #include "base/sys_info.h"
23 #include "build/build_config.h" 30 #include "build/build_config.h"
24 #include "components/drive/drive.pb.h" 31 #include "components/drive/drive.pb.h"
25 #include "components/drive/drive_api_util.h" 32 #include "components/drive/drive_api_util.h"
26 #include "components/drive/file_system_core_util.h" 33 #include "components/drive/file_system_core_util.h"
27 #include "components/drive/resource_metadata_storage.h" 34 #include "components/drive/resource_metadata_storage.h"
28 #include "google_apis/drive/task_util.h" 35 #include "google_apis/drive/task_util.h"
29 #include "net/base/filename_util.h" 36 #include "net/base/filename_util.h"
30 #include "net/base/mime_sniffer.h" 37 #include "net/base/mime_sniffer.h"
31 #include "net/base/mime_util.h" 38 #include "net/base/mime_util.h"
32 39
33 namespace drive { 40 namespace drive {
34 namespace internal { 41 namespace internal {
35 namespace { 42 namespace {
36 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
55 // Set extended file attribute as |name| |value| pair.
hashimoto 2016/05/02 08:29:20 nit: "Set" -> "Sets" The same goes for others.
oka 2016/05/09 14:27:39 Done.
56 bool Setxattr(const std::string& path, const std::string& name,
hashimoto 2016/05/02 08:29:20 How about making the name more descriptive (e.g. S
oka 2016/05/09 14:27:39 Done.
57 const std::string& value) {
58 return setxattr(path.c_str(), name.c_str(), value.c_str(), value.size() + 1,
59 0) == 0;
60 }
61
62 // Get extended file attribute associated with |name| and set it to |value|.
63 // If nothing is associated with |name|, set "" to |value|.
64 bool Getxattr(const std::string& path, const std::string& name,
65 std::string* value, size_t size) {
hashimoto 2016/05/02 08:29:20 Do we actually care about the contents of the attr
oka 2016/05/09 14:27:40 That's a good idea. Though I removed the func, I'l
66 value->resize(size - 1);
67 char* v = string_as_array(value);
68 ssize_t ret_size = getxattr(path.c_str(), name.c_str(), v, size);
69 if (ret_size < 0) {
70 if (errno == ENODATA) {
71 value->assign("");
72 return true;
73 } else {
74 PLOG(ERROR) << "getxattr: " << name;
75 return false;
76 }
77 }
78 value->resize(ret_size - 1);
79 return true;
80 }
81
82 // Change attributes of the file with |flags|,
83 // e.g. FS_UNRM_FL | FS_IMMUTABLE_FL. See linux/fs.h for available flags.
hashimoto 2016/05/02 08:29:20 nit: FS_UNRM_FL and FS_IMMUTABLE_FL are totally un
oka 2016/05/09 14:27:40 Done.
84 // Returns whether operation succeeded.
85 bool Chattr(const std::string& path, int64_t flags) {
hashimoto 2016/05/02 08:29:20 Could you give this function a more readable name
oka 2016/05/09 14:27:40 Done.
86 int fd = HANDLE_EINTR(open(path.c_str(), O_RDONLY));
hashimoto 2016/05/02 08:29:20 Why don't you use base::File with which you don't
oka 2016/05/09 14:27:40 Done.
87 if (fd < 0) {
88 PLOG(ERROR) << "open: " << path;
89 return false;
90 }
91 if (ioctl(fd, FS_IOC_SETFLAGS, &flags) < 0) {
92 PLOG(ERROR) << "ioctl" << path;
93 IGNORE_EINTR(close(fd));
94 return false;
95 }
96 IGNORE_EINTR(close(fd));
97 return true;
98 }
99
100 int64_t Lsattr(const std::string& path) {
hashimoto 2016/05/02 08:29:20 Please add a comment.
oka 2016/05/09 14:27:40 Done.
101 int fd = HANDLE_EINTR(open(path.c_str(), O_RDONLY));
102 if (fd < 0) {
103 PLOG(ERROR) << "open: " << path;
104 return -1;
105 }
106 int64_t flags = 0;
107 if (ioctl(fd, FS_IOC_GETFLAGS, &flags) < 0) {
108 PLOG(ERROR) << "ioctl";
109 IGNORE_EINTR(close(fd));
110 return -1;
111 }
112 IGNORE_EINTR(close(fd));
113 return flags;
114 }
115
116 // Mark the cache file to be removable by cryptohome.
117 bool SetRemovable(const base::FilePath& path) {
118 int64_t flags = Lsattr(path.value());
119 if (flags < 0) return false;
120 if ((flags & FS_NODUMP_FL) == FS_NODUMP_FL) return true;
121
122 return Chattr(path.value(), flags | FS_NODUMP_FL);
123 }
124
125 // Mark the cache file to be unremovable by cryptohome.
126 bool UnsetRemovable(const base::FilePath& path) {
127 // File is to be created.
128 if (!base::PathExists(path)) return true;
hashimoto 2016/05/02 08:29:20 This behavior is surprising, and not symmetric wit
oka 2016/05/09 14:27:40 Done. Added existence check to Pin.
129
130 int64_t flags = Lsattr(path.value());
131 if (flags < 0) return false;
132 if ((flags & FS_NODUMP_FL) == 0) return true;
133
134 return Chattr(path.value(), flags & ~(static_cast<int64_t>(FS_NODUMP_FL)));
135 }
136
137 // Mark |path| as drive cache dir.
138 // Returns if the operation succeeded.
139 bool MarkAsDriveCacheDir(const base::FilePath& path) {
140 return SetRemovable(path)
141 && Setxattr(path.value(), "user.GCacheFiles", "true");
hashimoto 2016/05/02 08:29:20 What is the point of using "true" instead of an em
oka 2016/05/09 14:27:39 Done.
142 }
143
48 typedef std::pair<base::File::Info, ResourceEntry> CacheInfo; 144 typedef std::pair<base::File::Info, ResourceEntry> CacheInfo;
49 145
50 class CacheInfoLatestCompare { 146 class CacheInfoLatestCompare {
51 public: 147 public:
52 bool operator()(const CacheInfo& info_a, const CacheInfo& info_b) { 148 bool operator()(const CacheInfo& info_a, const CacheInfo& info_b) {
53 return info_a.first.last_accessed < info_b.first.last_accessed; 149 return info_a.first.last_accessed < info_b.first.last_accessed;
54 } 150 }
55 }; 151 };
56 152
57 const size_t kMaxNumOfEvictedCacheFiles = 30000; 153 const size_t kMaxNumOfEvictedCacheFiles = 30000;
(...skipping 217 matching lines...) Expand 10 before | Expand all | Expand 10 after
275 return FILE_ERROR_FAILED; 371 return FILE_ERROR_FAILED;
276 } 372 }
277 373
278 // Now that file operations have completed, update metadata. 374 // Now that file operations have completed, update metadata.
279 FileCacheEntry* cache_state = 375 FileCacheEntry* cache_state =
280 entry.mutable_file_specific_info()->mutable_cache_state(); 376 entry.mutable_file_specific_info()->mutable_cache_state();
281 cache_state->set_md5(md5); 377 cache_state->set_md5(md5);
282 cache_state->set_is_present(true); 378 cache_state->set_is_present(true);
283 if (md5.empty()) 379 if (md5.empty())
284 cache_state->set_is_dirty(true); 380 cache_state->set_is_dirty(true);
381
382 if (!cache_state->is_pinned() && !cache_state->is_dirty()) {
383 if (!SetRemovable(dest_path))
384 return FILE_ERROR_FAILED;
385 } else {
386 if (!UnsetRemovable(dest_path))
387 return FILE_ERROR_FAILED;
388 }
389
285 return storage_->PutEntry(entry); 390 return storage_->PutEntry(entry);
286 } 391 }
287 392
288 FileError FileCache::Pin(const std::string& id) { 393 FileError FileCache::Pin(const std::string& id) {
289 AssertOnSequencedWorkerPool(); 394 AssertOnSequencedWorkerPool();
290 395
291 ResourceEntry entry; 396 ResourceEntry entry;
292 FileError error = storage_->GetEntry(id, &entry); 397 FileError error = storage_->GetEntry(id, &entry);
293 if (error != FILE_ERROR_OK) 398 if (error != FILE_ERROR_OK)
294 return error; 399 return error;
400
295 entry.mutable_file_specific_info()->mutable_cache_state()->set_is_pinned( 401 entry.mutable_file_specific_info()->mutable_cache_state()->set_is_pinned(
296 true); 402 true);
403 if (!UnsetRemovable(GetCacheFilePath(entry.local_id())))
404 return FILE_ERROR_FAILED;
405
297 return storage_->PutEntry(entry); 406 return storage_->PutEntry(entry);
298 } 407 }
299 408
300 FileError FileCache::Unpin(const std::string& id) { 409 FileError FileCache::Unpin(const std::string& id) {
301 AssertOnSequencedWorkerPool(); 410 AssertOnSequencedWorkerPool();
302 411
303 // Unpinning a file means its entry must exist in cache. 412 // Unpinning a file means its entry must exist in cache.
304 ResourceEntry entry; 413 ResourceEntry entry;
305 FileError error = storage_->GetEntry(id, &entry); 414 FileError error = storage_->GetEntry(id, &entry);
306 if (error != FILE_ERROR_OK) 415 if (error != FILE_ERROR_OK)
307 return error; 416 return error;
308 417
309 // Now that file operations have completed, update metadata. 418 // Now that file operations have completed, update metadata.
310 if (entry.file_specific_info().cache_state().is_present()) { 419 if (entry.file_specific_info().cache_state().is_present()) {
311 entry.mutable_file_specific_info()->mutable_cache_state()->set_is_pinned( 420 entry.mutable_file_specific_info()->mutable_cache_state()->set_is_pinned(
312 false); 421 false);
422 if (!entry.file_specific_info().cache_state().is_dirty()) {
423 if (!SetRemovable(GetCacheFilePath(entry.local_id())))
424 return FILE_ERROR_FAILED;
425 }
313 } else { 426 } else {
314 // Remove the existing entry if we are unpinning a non-present file. 427 // Remove the existing entry if we are unpinning a non-present file.
315 entry.mutable_file_specific_info()->clear_cache_state(); 428 entry.mutable_file_specific_info()->clear_cache_state();
316 } 429 }
317 error = storage_->PutEntry(entry); 430 error = storage_->PutEntry(entry);
318 if (error != FILE_ERROR_OK) 431 if (error != FILE_ERROR_OK)
319 return error; 432 return error;
320 433
321 // Now it's a chance to free up space if needed. 434 // Now it's a chance to free up space if needed.
322 FreeDiskSpaceIfNeededFor(0); 435 FreeDiskSpaceIfNeededFor(0);
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
369 ResourceEntry entry; 482 ResourceEntry entry;
370 FileError error = storage_->GetEntry(id, &entry); 483 FileError error = storage_->GetEntry(id, &entry);
371 if (error != FILE_ERROR_OK) 484 if (error != FILE_ERROR_OK)
372 return error; 485 return error;
373 if (!entry.file_specific_info().cache_state().is_present()) { 486 if (!entry.file_specific_info().cache_state().is_present()) {
374 LOG(WARNING) << "Can't mark dirty a file that wasn't cached: " << id; 487 LOG(WARNING) << "Can't mark dirty a file that wasn't cached: " << id;
375 return FILE_ERROR_NOT_FOUND; 488 return FILE_ERROR_NOT_FOUND;
376 } 489 }
377 490
378 entry.mutable_file_specific_info()->mutable_cache_state()->set_is_dirty(true); 491 entry.mutable_file_specific_info()->mutable_cache_state()->set_is_dirty(true);
492 if (!UnsetRemovable(GetCacheFilePath(entry.local_id())))
493 return FILE_ERROR_FAILED;
494
379 entry.mutable_file_specific_info()->mutable_cache_state()->clear_md5(); 495 entry.mutable_file_specific_info()->mutable_cache_state()->clear_md5();
380 error = storage_->PutEntry(entry); 496 error = storage_->PutEntry(entry);
381 if (error != FILE_ERROR_OK) 497 if (error != FILE_ERROR_OK)
382 return error; 498 return error;
383 499
384 write_opened_files_[id]++; 500 write_opened_files_[id]++;
385 file_closer->reset(new base::ScopedClosureRunner( 501 file_closer->reset(new base::ScopedClosureRunner(
386 base::Bind(&google_apis::RunTaskWithTaskRunner, 502 base::Bind(&google_apis::RunTaskWithTaskRunner,
387 blocking_task_runner_, 503 blocking_task_runner_,
388 base::Bind(&FileCache::CloseForWrite, 504 base::Bind(&FileCache::CloseForWrite,
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
440 556
441 // If a file is not dirty (it should have been marked dirty via OpenForWrite), 557 // If a file is not dirty (it should have been marked dirty via OpenForWrite),
442 // clearing its dirty state is an invalid operation. 558 // clearing its dirty state is an invalid operation.
443 if (!entry.file_specific_info().cache_state().is_dirty()) { 559 if (!entry.file_specific_info().cache_state().is_dirty()) {
444 LOG(WARNING) << "Can't clear dirty state of a non-dirty file: " << id; 560 LOG(WARNING) << "Can't clear dirty state of a non-dirty file: " << id;
445 return FILE_ERROR_INVALID_OPERATION; 561 return FILE_ERROR_INVALID_OPERATION;
446 } 562 }
447 563
448 entry.mutable_file_specific_info()->mutable_cache_state()->set_is_dirty( 564 entry.mutable_file_specific_info()->mutable_cache_state()->set_is_dirty(
449 false); 565 false);
566 if (!entry.file_specific_info().cache_state().is_pinned()) {
567 if (!SetRemovable(GetCacheFilePath(entry.local_id())))
568 return FILE_ERROR_FAILED;
569 }
570
450 return storage_->PutEntry(entry); 571 return storage_->PutEntry(entry);
451 } 572 }
452 573
453 FileError FileCache::Remove(const std::string& id) { 574 FileError FileCache::Remove(const std::string& id) {
454 AssertOnSequencedWorkerPool(); 575 AssertOnSequencedWorkerPool();
455 576
456 ResourceEntry entry; 577 ResourceEntry entry;
457 578
458 // If entry doesn't exist, nothing to do. 579 // If entry doesn't exist, nothing to do.
459 FileError error = storage_->GetEntry(id, &entry); 580 FileError error = storage_->GetEntry(id, &entry);
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
506 clear_md5(); 627 clear_md5();
507 if (storage_->PutEntry(new_entry) != FILE_ERROR_OK) 628 if (storage_->PutEntry(new_entry) != FILE_ERROR_OK)
508 return false; 629 return false;
509 } 630 }
510 } 631 }
511 if (it->HasError()) 632 if (it->HasError())
512 return false; 633 return false;
513 634
514 if (!RenameCacheFilesToNewFormat()) 635 if (!RenameCacheFilesToNewFormat())
515 return false; 636 return false;
637
638 if (!ResolveMetadataInconsistency()) {
639 return false;
640 }
641
642 if (ShouldStartDriveCacheMigration()) {
643 if (!MigrateCacheFiles()) {
644 return false;
645 }
646 }
647
516 return true; 648 return true;
517 } 649 }
518 650
651 bool FileCache::ShouldStartDriveCacheMigration() {
hashimoto 2016/05/02 08:29:19 IIUC, because of IO scheduling, a system crash or
oka 2016/05/09 14:27:39 I experimentally confirmed setting attributes is n
652 std::string value = "";
653 if (!Getxattr(cache_file_directory_.value(), "user.GCacheFiles", &value, 5)) {
654 PLOG(WARNING) << "Getxattr: " << cache_file_directory_.value();
655 }
656 int64_t flags = Lsattr(cache_file_directory_.value());
657 if (flags < 0) return false;
658
659 return value != "true" || ((flags & FS_NODUMP_FL) == 0);
660 }
661
519 void FileCache::Destroy() { 662 void FileCache::Destroy() {
520 DCHECK(thread_checker_.CalledOnValidThread()); 663 DCHECK(thread_checker_.CalledOnValidThread());
521 664
522 in_shutdown_.Set(); 665 in_shutdown_.Set();
523 666
524 // Destroy myself on the blocking pool. 667 // Destroy myself on the blocking pool.
525 // Note that base::DeletePointer<> cannot be used as the destructor of this 668 // Note that base::DeletePointer<> cannot be used as the destructor of this
526 // class is private. 669 // class is private.
527 blocking_task_runner_->PostTask( 670 blocking_task_runner_->PostTask(
528 FROM_HERE, 671 FROM_HERE,
(...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after
677 continue; 820 continue;
678 } 821 }
679 const std::string& id = GetIdFromPath(new_path); 822 const std::string& id = GetIdFromPath(new_path);
680 new_path = GetCacheFilePath(util::CanonicalizeResourceId(id)); 823 new_path = GetCacheFilePath(util::CanonicalizeResourceId(id));
681 if (new_path != current && !base::Move(current, new_path)) 824 if (new_path != current && !base::Move(current, new_path))
682 return false; 825 return false;
683 } 826 }
684 return true; 827 return true;
685 } 828 }
686 829
687 // static 830 bool FileCache::MigrateCacheFiles() {
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 = 831 std::unique_ptr<ResourceMetadataStorage::Iterator> it =
693 metadata_storage->GetIterator(); 832 storage_->GetIterator();
833
694 for (; !it->IsAtEnd(); it->Advance()) { 834 for (; !it->IsAtEnd(); it->Advance()) {
695 const ResourceEntry& entry = it->GetValue(); 835 const ResourceEntry& entry = it->GetValue();
696 if (!entry.file_specific_info().cache_state().is_present()) { 836 if (!entry.file_specific_info().cache_state().is_present()) {
697 continue; 837 continue;
698 } 838 }
699 839
700 // Ignore missing cache file case since it never succeeds. 840 const base::FilePath filepath = GetPathForId(cache_file_directory_,
701 // TODO(yawano): handle this case at metadata validation in FileCache. 841 entry.local_id());
702 const base::FilePath move_from = GetPathForId(from, entry.local_id()); 842
703 if (!base::PathExists(move_from)) { 843 if (!base::PathExists(filepath)) {
844 PLOG(WARNING) << "Cache and metadata are inconsistent.";
hashimoto 2016/05/02 08:29:19 It's strange to abort the initialization with a wa
oka 2016/05/09 14:27:39 Merged two funcs, and removed this warning.
845 return false;
846 }
847
848 const FileCacheEntry& file_cache_entry =
849 entry.file_specific_info().cache_state();
850 if (file_cache_entry.is_pinned() || file_cache_entry.is_dirty()) {
851 if (!UnsetRemovable(filepath)) return false;
852 } else {
853 if (!SetRemovable(filepath)) return false;
854 }
855 }
856
857 return MarkAsDriveCacheDir(cache_file_directory_);
858 }
859
860 bool FileCache::ResolveMetadataInconsistency() {
861 std::unique_ptr<ResourceMetadataStorage::Iterator>
862 it = storage_->GetIterator();
863 for (; !it->IsAtEnd(); it->Advance()) {
864 ResourceEntry entry = it->GetValue();
865
866 if (!entry.file_specific_info().has_cache_state() ||
867 !entry.file_specific_info().cache_state().is_present()) {
hashimoto 2016/05/02 08:29:20 Shouldn't we handle a case where !is_present but t
oka 2016/05/09 14:27:40 Done. I merged ResolveMetadataInconsistency and Mi
704 continue; 868 continue;
705 } 869 }
706 870
707 // Create hard link to cache file if it's pinned or dirty. cryptohome will 871 if (base::PathExists(GetCacheFilePath(entry.local_id()))) {
708 // not delete a cache file if there is a hard link to it. 872 continue;
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 } 873 }
719 874
720 // Move cache file. 875 entry.mutable_file_specific_info()->mutable_cache_state()->set_is_present(
721 const base::FilePath move_to = GetPathForId(to_files, entry.local_id()); 876 false);
722 if (!base::Move(move_from, move_to)) { 877
878 if (storage_->PutEntry(entry) != FILE_ERROR_OK)
723 return false; 879 return false;
724 }
725 } 880 }
726
727 return true; 881 return true;
728 } 882 }
729 883
730 void FileCache::CloseForWrite(const std::string& id) { 884 void FileCache::CloseForWrite(const std::string& id) {
731 AssertOnSequencedWorkerPool(); 885 AssertOnSequencedWorkerPool();
732 886
733 std::map<std::string, int>::iterator it = write_opened_files_.find(id); 887 std::map<std::string, int>::iterator it = write_opened_files_.find(id);
734 if (it == write_opened_files_.end()) 888 if (it == write_opened_files_.end())
735 return; 889 return;
736 890
(...skipping 21 matching lines...) Expand all
758 912
759 bool FileCache::IsEvictable(const std::string& id, const ResourceEntry& entry) { 913 bool FileCache::IsEvictable(const std::string& id, const ResourceEntry& entry) {
760 return entry.file_specific_info().has_cache_state() && 914 return entry.file_specific_info().has_cache_state() &&
761 !entry.file_specific_info().cache_state().is_pinned() && 915 !entry.file_specific_info().cache_state().is_pinned() &&
762 !entry.file_specific_info().cache_state().is_dirty() && 916 !entry.file_specific_info().cache_state().is_dirty() &&
763 !mounted_files_.count(id); 917 !mounted_files_.count(id);
764 } 918 }
765 919
766 } // namespace internal 920 } // namespace internal
767 } // namespace drive 921 } // namespace drive
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698