blob: b446ae228db77a6297a09410fdeec364eca999e4 [file] [log] [blame]
[email protected]3653146a2012-05-29 13:41:471// 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
3// found in the LICENSE file.
4
yawano3513e142016-04-20 00:42:425#include "components/drive/chromeos/file_cache.h"
[email protected]3653146a2012-05-29 13:41:476
okac6aac502016-05-23 12:16:587#include <linux/fs.h>
8#include <sys/ioctl.h>
9#include <sys/xattr.h>
yawano1c325bf2016-04-20 06:37:0310
Stuart Langleybe4035742018-05-10 05:32:2711#include <memory>
yawano4c36b9a02015-10-23 06:17:2312#include <queue>
[email protected]3653146a2012-05-29 13:41:4713#include <vector>
14
lukasza037c10b12015-06-12 04:21:2515#include "base/bind.h"
16#include "base/bind_helpers.h"
[email protected]8b03ab3a2014-01-15 17:52:4517#include "base/callback_helpers.h"
okac6aac502016-05-23 12:16:5818#include "base/files/file.h"
[email protected]25a4c1c2013-06-08 04:53:3619#include "base/files/file_enumerator.h"
thestig18dfb7a52014-08-26 10:44:0420#include "base/files/file_util.h"
lukasza037c10b12015-06-12 04:21:2521#include "base/location.h"
[email protected]3653146a2012-05-29 13:41:4722#include "base/logging.h"
asvitkine776f9db2017-01-25 21:39:2923#include "base/metrics/histogram_macros.h"
okac6aac502016-05-23 12:16:5824#include "base/stl_util.h"
[email protected]5c073322013-06-11 08:03:3025#include "base/strings/string_util.h"
26#include "base/strings/stringprintf.h"
[email protected]a321b9632012-06-14 03:29:1727#include "base/sys_info.h"
avibc5337b2015-12-25 23:16:3328#include "build/build_config.h"
lukasza01b9d55a2015-07-21 15:19:2529#include "components/drive/drive.pb.h"
lukasza8acc4eb2015-07-20 20:57:2030#include "components/drive/drive_api_util.h"
lukasza6364a022015-08-21 01:13:2431#include "components/drive/file_system_core_util.h"
32#include "components/drive/resource_metadata_storage.h"
[email protected]8b03ab3a2014-01-15 17:52:4533#include "google_apis/drive/task_util.h"
[email protected]d96cf752014-04-09 04:05:2834#include "net/base/filename_util.h"
[email protected]b7af4f12013-10-31 06:57:4535#include "net/base/mime_sniffer.h"
36#include "net/base/mime_util.h"
[email protected]7986b8d2012-06-14 15:05:1437
[email protected]d9d04df2012-10-12 07:06:3538namespace drive {
[email protected]59c7cdec2013-05-07 04:17:1339namespace internal {
[email protected]3653146a2012-05-29 13:41:4740namespace {
41
okac6aac502016-05-23 12:16:5842typedef std::pair<base::File::Info, ResourceEntry> CacheInfo;
43typedef long FileAttributes; // NOLINT(runtime/int)
44
[email protected]c9e4738d2013-08-26 03:04:0745// Returns ID extracted from the path.
46std::string GetIdFromPath(const base::FilePath& path) {
[email protected]91a464e62013-07-10 09:30:0647 return util::UnescapeCacheFileName(path.BaseName().AsUTF8Unsafe());
48}
49
yawano9fd1e632016-02-04 09:00:0650base::FilePath GetPathForId(const base::FilePath& cache_directory,
51 const std::string& id) {
52 return cache_directory.Append(
53 base::FilePath::FromUTF8Unsafe(util::EscapeCacheFileName(id)));
54}
55
oka7bd26bbf2016-10-14 06:59:0656// Returns if the filesystem backing |path| supports file attributes.
57// This will return false if the filesystem is for example tmpfs, which is used
58// for ephemeral mode.
59bool IsFileAttributesSupported(const base::FilePath& path) {
60 if (getxattr(path.value().c_str(), "user.foo", nullptr, 0) >= 0) {
61 return true;
62 }
63 return errno != ENOTSUP;
64}
65
okac6aac502016-05-23 12:16:5866// Sets extended file attribute as |name| |value| pair.
67bool SetExtendedFileAttributes(const base::FilePath& path,
68 const std::string& name, const std::string& value) {
Sergei Datsenkoe5a99eb2018-08-31 06:30:3969 if (setxattr(path.value().c_str(), name.c_str(), value.c_str(),
70 value.size() + 1, 0) != 0) {
71 PLOG(ERROR) << "setxattr: " << path.value();
72 return false;
73 }
74 return true;
75}
76
77// Remove extended file attribute with |name|.
78bool UnsetExtendedFileAttributes(const base::FilePath& path,
79 const std::string& name) {
80 if (removexattr(path.value().c_str(), name.c_str()) != 0) {
Sergei Datsenko50132dc2018-09-11 04:27:2281 PLOG_IF(ERROR, errno != ENODATA) << "removexattr: " << path.value();
Sergei Datsenkoe5a99eb2018-08-31 06:30:3982 return false;
83 }
84 return true;
okac6aac502016-05-23 12:16:5885}
86
87// Changes attributes of the file with |flags|, e.g. FS_NODUMP_FL (cryptohome
88// will remove Drive caches with this attribute).
89// See linux/fs.h for available flags, and chattr(1) which does similar thing.
90// Returns whether operation succeeded.
91bool SetFileAttributes(const base::FilePath& path, FileAttributes flags) {
92 base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_READ);
93 if (!file.IsValid()) {
94 PLOG(ERROR) << "Failed to open file: " << path.value();
95 return false;
96 }
97 if (ioctl(file.GetPlatformFile(), FS_IOC_SETFLAGS, &flags) < 0) {
98 PLOG(ERROR) << "ioctl: " << path.value();
99 return false;
100 }
101 return true;
102}
103
104// Gets file attributes similarly to lsattr(1). Returns flags or -1 on error.
105// See linux/fs.h for the definition of the returned flags e.g. FS_NODUMP_FL.
106FileAttributes GetFileAttributes(const base::FilePath& path) {
107 base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_READ);
108 if (!file.IsValid()) {
109 PLOG(ERROR) << "Failed to open file: " << path.value();
110 return -1;
111 }
112 FileAttributes flags = 0;
113 if (ioctl(file.GetPlatformFile(), FS_IOC_GETFLAGS, &flags) < 0) {
114 PLOG(ERROR) << "ioctl: " << path.value();
115 return -1;
116 }
117 return flags;
118}
119
oka7bd26bbf2016-10-14 06:59:06120// Marks the cache file to be removable by cryptohome, or do nothing if
121// underlying filesystem doesn't support file attributes, as tmpfs for ephemeral
122// mode.
okac6aac502016-05-23 12:16:58123bool SetRemovable(const base::FilePath& path) {
oka7bd26bbf2016-10-14 06:59:06124 // For ephemeral mode.
125 if (!IsFileAttributesSupported(path)) {
126 return true;
127 }
okac6aac502016-05-23 12:16:58128 FileAttributes flags = GetFileAttributes(path);
Sergei Datsenkoe5a99eb2018-08-31 06:30:39129 bool xattr = flags >= 0 && SetFileAttributes(path, flags | FS_NODUMP_FL);
130 bool fattr = SetExtendedFileAttributes(
131 path, FileCache::kGCacheRemovableAttribute, "1");
132 return xattr || fattr;
okac6aac502016-05-23 12:16:58133}
134
oka7bd26bbf2016-10-14 06:59:06135// Marks the cache file to be unremovable by cryptohome, or do nothing if
136// underlying filesystem doesn't support file attributes, as tmpfs for ephemeral
137// mode.
okac6aac502016-05-23 12:16:58138bool UnsetRemovable(const base::FilePath& path) {
oka7bd26bbf2016-10-14 06:59:06139 // For ephemeral mode.
140 if (!IsFileAttributesSupported(path)) {
141 return true;
142 }
okac6aac502016-05-23 12:16:58143 FileAttributes flags = GetFileAttributes(path);
Sergei Datsenkoe5a99eb2018-08-31 06:30:39144 bool xattr = flags >= 0 && SetFileAttributes(path, flags & ~FS_NODUMP_FL);
145 bool fattr =
146 UnsetExtendedFileAttributes(path, FileCache::kGCacheRemovableAttribute);
147 return xattr || fattr;
okac6aac502016-05-23 12:16:58148}
149
oka7bd26bbf2016-10-14 06:59:06150// Marks |path| as drive cache dir, or do nothing if underlying filesystem
151// doesn't support file attributes, as tmpfs for ephemeral mode. Returns if the
152// operation succeeded.
okac6aac502016-05-23 12:16:58153bool MarkAsDriveCacheDir(const base::FilePath& path) {
oka7bd26bbf2016-10-14 06:59:06154 // For ephemeral mode.
155 if (!IsFileAttributesSupported(path)) {
156 return true;
157 }
okac6aac502016-05-23 12:16:58158 return SetRemovable(path)
159 && SetExtendedFileAttributes(path, FileCache::kGCacheFilesAttribute, "");
160}
yawano4c36b9a02015-10-23 06:17:23161
162class CacheInfoLatestCompare {
163 public:
164 bool operator()(const CacheInfo& info_a, const CacheInfo& info_b) {
165 return info_a.first.last_accessed < info_b.first.last_accessed;
166 }
167};
168
fukino6380c07c2016-06-09 07:28:29169// Returns true if the cache file is present.
170bool IsPresent(const ResourceEntry& entry) {
171 return entry.has_file_specific_info() &&
172 entry.file_specific_info().has_cache_state() &&
173 entry.file_specific_info().cache_state().is_present();
174}
175
yawano4c36b9a02015-10-23 06:17:23176const size_t kMaxNumOfEvictedCacheFiles = 30000;
177
[email protected]a321b9632012-06-14 03:29:17178} // namespace
[email protected]32a7fc852012-06-08 17:25:50179
okac6aac502016-05-23 12:16:58180// static
181const char FileCache::kGCacheFilesAttribute[] = "user.GCacheFiles";
Sergei Datsenkoe5a99eb2018-08-31 06:30:39182const char FileCache::kGCacheRemovableAttribute[] = "user.GCacheRemovable";
okac6aac502016-05-23 12:16:58183
[email protected]2df61e12013-06-21 16:00:09184FileCache::FileCache(ResourceMetadataStorage* storage,
[email protected]e07f7b7b2013-06-19 03:43:12185 const base::FilePath& cache_file_directory,
[email protected]eca3fc92013-05-01 03:53:40186 base::SequencedTaskRunner* blocking_task_runner,
187 FreeDiskSpaceGetterInterface* free_disk_space_getter)
[email protected]2df61e12013-06-21 16:00:09188 : cache_file_directory_(cache_file_directory),
[email protected]ddbf2052012-07-13 15:07:02189 blocking_task_runner_(blocking_task_runner),
[email protected]2df61e12013-06-21 16:00:09190 storage_(storage),
[email protected]f6fd98a2012-12-14 00:04:02191 free_disk_space_getter_(free_disk_space_getter),
yawano4c36b9a02015-10-23 06:17:23192 max_num_of_evicted_cache_files_(kMaxNumOfEvictedCacheFiles),
[email protected]9c009092013-05-01 03:14:09193 weak_ptr_factory_(this) {
[email protected]144b6c42013-06-14 07:30:38194 DCHECK(blocking_task_runner_.get());
[email protected]3653146a2012-05-29 13:41:47195}
196
[email protected]eca3fc92013-05-01 03:53:40197FileCache::~FileCache() {
[email protected]17196ee2012-12-13 06:23:51198 // Must be on the sequenced worker pool, as |metadata_| must be deleted on
199 // the sequenced worker pool.
[email protected]73f9c742012-06-15 07:37:13200 AssertOnSequencedWorkerPool();
[email protected]3653146a2012-05-29 13:41:47201}
202
yawano4c36b9a02015-10-23 06:17:23203void FileCache::SetMaxNumOfEvictedCacheFilesForTest(
204 size_t max_num_of_evicted_cache_files) {
205 max_num_of_evicted_cache_files_ = max_num_of_evicted_cache_files;
206}
207
[email protected]c9e4738d2013-08-26 03:04:07208base::FilePath FileCache::GetCacheFilePath(const std::string& id) const {
yawano9fd1e632016-02-04 09:00:06209 return GetPathForId(cache_file_directory_, id);
[email protected]32a7fc852012-06-08 17:25:50210}
211
[email protected]eca3fc92013-05-01 03:53:40212void FileCache::AssertOnSequencedWorkerPool() {
peary2ac764482017-06-25 14:39:53213 DCHECK(blocking_task_runner_->RunsTasksInCurrentSequence());
[email protected]fcc92a52012-06-08 22:54:16214}
215
[email protected]eca3fc92013-05-01 03:53:40216bool FileCache::IsUnderFileCacheDirectory(const base::FilePath& path) const {
[email protected]e07f7b7b2013-06-19 03:43:12217 return cache_file_directory_.IsParent(path);
[email protected]01ba15f72012-06-09 00:41:05218}
219
avibc5337b2015-12-25 23:16:33220bool FileCache::FreeDiskSpaceIfNeededFor(int64_t num_bytes) {
[email protected]ec514362013-05-27 17:52:22221 AssertOnSequencedWorkerPool();
222
223 // Do nothing and return if we have enough space.
yawano4c36b9a02015-10-23 06:17:23224 if (GetAvailableSpace() >= num_bytes)
[email protected]ec514362013-05-27 17:52:22225 return true;
226
227 // Otherwise, try to free up the disk space.
228 DVLOG(1) << "Freeing up disk space for " << num_bytes;
[email protected]f8b1a532013-06-06 08:35:08229
[email protected]9d147582013-06-14 06:25:45230 // Remove all files which have no corresponding cache entries.
[email protected]e07f7b7b2013-06-19 03:43:12231 base::FileEnumerator enumerator(cache_file_directory_,
[email protected]9d147582013-06-14 06:25:45232 false, // not recursive
233 base::FileEnumerator::FILES);
[email protected]cd8fd37f2014-05-20 15:45:21234 ResourceEntry entry;
[email protected]9d147582013-06-14 06:25:45235 for (base::FilePath current = enumerator.Next(); !current.empty();
236 current = enumerator.Next()) {
yawano4c36b9a02015-10-23 06:17:23237 const std::string id = GetIdFromPath(current);
238 const FileError error = storage_->GetEntry(id, &entry);
239
240 if (error == FILE_ERROR_NOT_FOUND)
[email protected]dd3aa792013-07-16 19:10:23241 base::DeleteFile(current, false /* recursive */);
[email protected]996139412014-05-10 06:19:50242 else if (error != FILE_ERROR_OK)
243 return false;
[email protected]9d147582013-06-14 06:25:45244 }
[email protected]ec514362013-05-27 17:52:22245
yawano4c36b9a02015-10-23 06:17:23246 // Check available space again. If we have enough space here, do nothing.
avibc5337b2015-12-25 23:16:33247 const int64_t available_space = GetAvailableSpace();
yawano4c36b9a02015-10-23 06:17:23248 if (available_space >= num_bytes)
249 return true;
250
avibc5337b2015-12-25 23:16:33251 const int64_t requested_space = num_bytes - available_space;
yawano4c36b9a02015-10-23 06:17:23252
253 // Put all entries in priority queue where latest entry becomes top.
254 std::priority_queue<CacheInfo, std::vector<CacheInfo>, CacheInfoLatestCompare>
255 cache_info_queue;
dchengf42750232016-04-12 04:12:27256 std::unique_ptr<ResourceMetadataStorage::Iterator> it =
257 storage_->GetIterator();
yawano4c36b9a02015-10-23 06:17:23258 for (; !it->IsAtEnd(); it->Advance()) {
259 if (IsEvictable(it->GetID(), it->GetValue())) {
260 const ResourceEntry& entry = it->GetValue();
261
262 const base::FilePath& cache_path = GetCacheFilePath(entry.local_id());
263 base::File::Info info;
264 // If it fails to get file info of |cache_path|, use default value as its
265 // file info. i.e. the file becomes least recently used one.
266 base::GetFileInfo(cache_path, &info);
267
268 CacheInfo cache_info = std::make_pair(info, entry);
269
270 if (cache_info_queue.size() < max_num_of_evicted_cache_files_) {
271 cache_info_queue.push(cache_info);
272 } else if (cache_info_queue.size() >= max_num_of_evicted_cache_files_ &&
273 cache_info.first.last_accessed <
274 cache_info_queue.top().first.last_accessed) {
275 // Do not enqueue more than max_num_of_evicted_cache_files_ not to use
276 // up memory with this queue.
277 cache_info_queue.pop();
278 cache_info_queue.push(cache_info);
279 }
280 }
281 }
282 if (it->HasError())
283 return false;
284
285 // Copy entries to the vector. This becomes last-accessed desc order.
286 std::vector<CacheInfo> cache_info_list;
287 while (!cache_info_queue.empty()) {
288 cache_info_list.push_back(cache_info_queue.top());
289 cache_info_queue.pop();
290 }
291
292 // Update DB and delete files with accessing to the vector in ascending order.
avibc5337b2015-12-25 23:16:33293 int64_t evicted_cache_size = 0;
yawano4c36b9a02015-10-23 06:17:23294 auto iter = cache_info_list.rbegin();
295 while (evicted_cache_size < requested_space &&
296 iter != cache_info_list.rend()) {
297 const CacheInfo& cache_info = *iter;
298
299 // Update DB.
300 ResourceEntry entry = cache_info.second;
301 entry.mutable_file_specific_info()->clear_cache_state();
302 storage_->PutEntry(entry);
303
304 // Delete cache file.
305 const base::FilePath& path = GetCacheFilePath(entry.local_id());
306
307 if (base::DeleteFile(path, false /* recursive */))
308 evicted_cache_size += cache_info.first.size;
309
310 ++iter;
311 }
312
[email protected]ec514362013-05-27 17:52:22313 // Check the disk space again.
yawano4c36b9a02015-10-23 06:17:23314 return GetAvailableSpace() >= num_bytes;
[email protected]ec514362013-05-27 17:52:22315}
316
fukino6380c07c2016-06-09 07:28:29317int64_t FileCache::CalculateCacheSize() {
yawano8578abf2015-08-26 09:15:50318 AssertOnSequencedWorkerPool();
319
fukino6380c07c2016-06-09 07:28:29320 int64_t total_cache_size = 0;
321 int64_t cache_size = 0;
322
323 std::unique_ptr<ResourceMetadataStorage::Iterator> it =
324 storage_->GetIterator();
325 for (; !it->IsAtEnd(); it->Advance()) {
326 if (IsPresent(it->GetValue()) &&
327 base::GetFileSize(GetCacheFilePath(it->GetID()), &cache_size)) {
328 DCHECK_GE(cache_size, 0);
329 total_cache_size += cache_size;
330 }
331 }
332
333 if (it->HasError())
334 return 0;
335
336 return total_cache_size;
337}
338
339int64_t FileCache::CalculateEvictableCacheSize() {
340 AssertOnSequencedWorkerPool();
341
342 int64_t evictable_cache_size = 0;
battrea5f64bd2015-08-26 10:47:41343 int64_t cache_size = 0;
yawano8578abf2015-08-26 09:15:50344
dchengf42750232016-04-12 04:12:27345 std::unique_ptr<ResourceMetadataStorage::Iterator> it =
346 storage_->GetIterator();
yawano8578abf2015-08-26 09:15:50347 for (; !it->IsAtEnd(); it->Advance()) {
348 if (IsEvictable(it->GetID(), it->GetValue()) &&
349 base::GetFileSize(GetCacheFilePath(it->GetID()), &cache_size)) {
350 DCHECK_GE(cache_size, 0);
351 evictable_cache_size += cache_size;
352 }
353 }
354
355 if (it->HasError())
356 return 0;
357
358 return evictable_cache_size;
359}
360
[email protected]c9e4738d2013-08-26 03:04:07361FileError FileCache::GetFile(const std::string& id,
[email protected]ec514362013-05-27 17:52:22362 base::FilePath* cache_file_path) {
363 AssertOnSequencedWorkerPool();
364 DCHECK(cache_file_path);
365
[email protected]cd8fd37f2014-05-20 15:45:21366 ResourceEntry entry;
367 FileError error = storage_->GetEntry(id, &entry);
[email protected]996139412014-05-10 06:19:50368 if (error != FILE_ERROR_OK)
369 return error;
[email protected]cd8fd37f2014-05-20 15:45:21370 if (!entry.file_specific_info().cache_state().is_present())
[email protected]ec514362013-05-27 17:52:22371 return FILE_ERROR_NOT_FOUND;
372
[email protected]c9e4738d2013-08-26 03:04:07373 *cache_file_path = GetCacheFilePath(id);
[email protected]ec514362013-05-27 17:52:22374 return FILE_ERROR_OK;
375}
376
[email protected]c9e4738d2013-08-26 03:04:07377FileError FileCache::Store(const std::string& id,
[email protected]82c4eb92013-05-21 11:25:23378 const std::string& md5,
379 const base::FilePath& source_path,
380 FileOperationType file_operation_type) {
381 AssertOnSequencedWorkerPool();
[email protected]d8546c92013-05-02 05:09:59382
[email protected]cd8fd37f2014-05-20 15:45:21383 ResourceEntry entry;
384 FileError error = storage_->GetEntry(id, &entry);
385 if (error != FILE_ERROR_OK)
386 return error;
387
avibc5337b2015-12-25 23:16:33388 int64_t file_size = 0;
[email protected]8e37b9b2013-12-11 09:06:02389 if (file_operation_type == FILE_OPERATION_COPY) {
390 if (!base::GetFileSize(source_path, &file_size)) {
391 LOG(WARNING) << "Couldn't get file size for: " << source_path.value();
392 return FILE_ERROR_FAILED;
393 }
394 }
395 if (!FreeDiskSpaceIfNeededFor(file_size))
396 return FILE_ERROR_NO_LOCAL_SPACE;
[email protected]73f9c742012-06-15 07:37:13397
[email protected]1d0786a2014-02-06 12:37:08398 // If file is mounted, return error.
399 if (mounted_files_.count(id))
[email protected]8e37b9b2013-12-11 09:06:02400 return FILE_ERROR_IN_USE;
401
402 base::FilePath dest_path = GetCacheFilePath(id);
403 bool success = false;
404 switch (file_operation_type) {
405 case FILE_OPERATION_MOVE:
406 success = base::Move(source_path, dest_path);
407 break;
408 case FILE_OPERATION_COPY:
409 success = base::CopyFile(source_path, dest_path);
410 break;
411 default:
412 NOTREACHED();
413 }
414
415 if (!success) {
416 LOG(ERROR) << "Failed to store: "
417 << "source_path = " << source_path.value() << ", "
418 << "dest_path = " << dest_path.value() << ", "
419 << "file_operation_type = " << file_operation_type;
420 return FILE_ERROR_FAILED;
421 }
422
423 // Now that file operations have completed, update metadata.
[email protected]cd8fd37f2014-05-20 15:45:21424 FileCacheEntry* cache_state =
425 entry.mutable_file_specific_info()->mutable_cache_state();
426 cache_state->set_md5(md5);
427 cache_state->set_is_present(true);
[email protected]bae99ae52014-01-29 01:13:14428 if (md5.empty())
[email protected]cd8fd37f2014-05-20 15:45:21429 cache_state->set_is_dirty(true);
okac6aac502016-05-23 12:16:58430
431 if (!cache_state->is_pinned() && !cache_state->is_dirty()) {
432 if (!SetRemovable(dest_path))
433 return FILE_ERROR_FAILED;
434 } else {
435 if (!UnsetRemovable(dest_path))
436 return FILE_ERROR_FAILED;
437 }
438
[email protected]cd8fd37f2014-05-20 15:45:21439 return storage_->PutEntry(entry);
[email protected]73f9c742012-06-15 07:37:13440}
441
[email protected]c9e4738d2013-08-26 03:04:07442FileError FileCache::Pin(const std::string& id) {
[email protected]f8b1a532013-06-06 08:35:08443 AssertOnSequencedWorkerPool();
444
[email protected]cd8fd37f2014-05-20 15:45:21445 ResourceEntry entry;
446 FileError error = storage_->GetEntry(id, &entry);
447 if (error != FILE_ERROR_OK)
[email protected]996139412014-05-10 06:19:50448 return error;
okac6aac502016-05-23 12:16:58449
[email protected]cd8fd37f2014-05-20 15:45:21450 entry.mutable_file_specific_info()->mutable_cache_state()->set_is_pinned(
451 true);
okac6aac502016-05-23 12:16:58452
453 base::FilePath file_path = GetCacheFilePath(entry.local_id());
454 // Cache file can be created later.
455 if (entry.file_specific_info().cache_state().is_present()) {
456 if (!UnsetRemovable(file_path))
457 return FILE_ERROR_FAILED;
458 }
459
[email protected]cd8fd37f2014-05-20 15:45:21460 return storage_->PutEntry(entry);
[email protected]f8b1a532013-06-06 08:35:08461}
462
[email protected]c9e4738d2013-08-26 03:04:07463FileError FileCache::Unpin(const std::string& id) {
[email protected]ec514362013-05-27 17:52:22464 AssertOnSequencedWorkerPool();
465
466 // Unpinning a file means its entry must exist in cache.
[email protected]cd8fd37f2014-05-20 15:45:21467 ResourceEntry entry;
468 FileError error = storage_->GetEntry(id, &entry);
[email protected]996139412014-05-10 06:19:50469 if (error != FILE_ERROR_OK)
470 return error;
[email protected]ec514362013-05-27 17:52:22471
[email protected]ec514362013-05-27 17:52:22472 // Now that file operations have completed, update metadata.
[email protected]cd8fd37f2014-05-20 15:45:21473 if (entry.file_specific_info().cache_state().is_present()) {
474 entry.mutable_file_specific_info()->mutable_cache_state()->set_is_pinned(
475 false);
okac6aac502016-05-23 12:16:58476 if (!entry.file_specific_info().cache_state().is_dirty()) {
477 if (!SetRemovable(GetCacheFilePath(entry.local_id())))
478 return FILE_ERROR_FAILED;
479 }
[email protected]ec514362013-05-27 17:52:22480 } else {
481 // Remove the existing entry if we are unpinning a non-present file.
[email protected]cd8fd37f2014-05-20 15:45:21482 entry.mutable_file_specific_info()->clear_cache_state();
[email protected]ec514362013-05-27 17:52:22483 }
[email protected]cd8fd37f2014-05-20 15:45:21484 error = storage_->PutEntry(entry);
485 if (error != FILE_ERROR_OK)
486 return error;
[email protected]bd2254d2013-06-12 16:00:47487
[email protected]9d147582013-06-14 06:25:45488 // Now it's a chance to free up space if needed.
[email protected]bd2254d2013-06-12 16:00:47489 FreeDiskSpaceIfNeededFor(0);
490
[email protected]ec514362013-05-27 17:52:22491 return FILE_ERROR_OK;
492}
493
Tatsuhisa Yamaguchi48101282018-03-20 02:18:39494bool FileCache::IsMarkedAsMounted(const std::string& id) {
495 AssertOnSequencedWorkerPool();
496 return mounted_files_.count(id);
497}
498
[email protected]c3f65642013-08-28 02:04:33499FileError FileCache::MarkAsMounted(const std::string& id,
500 base::FilePath* cache_file_path) {
501 AssertOnSequencedWorkerPool();
502 DCHECK(cache_file_path);
503
504 // Get cache entry associated with the id and md5
[email protected]cd8fd37f2014-05-20 15:45:21505 ResourceEntry entry;
506 FileError error = storage_->GetEntry(id, &entry);
[email protected]996139412014-05-10 06:19:50507 if (error != FILE_ERROR_OK)
508 return error;
[email protected]cd8fd37f2014-05-20 15:45:21509 if (!entry.file_specific_info().cache_state().is_present())
510 return FILE_ERROR_NOT_FOUND;
[email protected]c3f65642013-08-28 02:04:33511
512 if (mounted_files_.count(id))
513 return FILE_ERROR_INVALID_OPERATION;
514
[email protected]c3f65642013-08-28 02:04:33515 base::FilePath path = GetCacheFilePath(id);
lukasza6364a022015-08-21 01:13:24516
517#if defined(OS_CHROMEOS)
518 // Ensure the file is readable to cros_disks. See crbug.com/236994.
[email protected]b264eab2013-11-27 23:22:08519 if (!base::SetPosixFilePermissions(
[email protected]c3f65642013-08-28 02:04:33520 path,
[email protected]b264eab2013-11-27 23:22:08521 base::FILE_PERMISSION_READ_BY_USER |
522 base::FILE_PERMISSION_WRITE_BY_USER |
523 base::FILE_PERMISSION_READ_BY_GROUP |
524 base::FILE_PERMISSION_READ_BY_OTHERS))
[email protected]c3f65642013-08-28 02:04:33525 return FILE_ERROR_FAILED;
lukasza6364a022015-08-21 01:13:24526#endif
[email protected]c3f65642013-08-28 02:04:33527
528 mounted_files_.insert(id);
529
530 *cache_file_path = path;
531 return FILE_ERROR_OK;
532}
533
[email protected]8b03ab3a2014-01-15 17:52:45534FileError FileCache::OpenForWrite(
535 const std::string& id,
dchengf42750232016-04-12 04:12:27536 std::unique_ptr<base::ScopedClosureRunner>* file_closer) {
[email protected]b568b882013-06-10 04:38:07537 AssertOnSequencedWorkerPool();
538
[email protected]b568b882013-06-10 04:38:07539 // Marking a file dirty means its entry and actual file blob must exist in
540 // cache.
[email protected]cd8fd37f2014-05-20 15:45:21541 ResourceEntry entry;
542 FileError error = storage_->GetEntry(id, &entry);
[email protected]996139412014-05-10 06:19:50543 if (error != FILE_ERROR_OK)
544 return error;
[email protected]cd8fd37f2014-05-20 15:45:21545 if (!entry.file_specific_info().cache_state().is_present()) {
[email protected]c9e4738d2013-08-26 03:04:07546 LOG(WARNING) << "Can't mark dirty a file that wasn't cached: " << id;
[email protected]b568b882013-06-10 04:38:07547 return FILE_ERROR_NOT_FOUND;
548 }
549
[email protected]cd8fd37f2014-05-20 15:45:21550 entry.mutable_file_specific_info()->mutable_cache_state()->set_is_dirty(true);
okac6aac502016-05-23 12:16:58551 if (!UnsetRemovable(GetCacheFilePath(entry.local_id())))
552 return FILE_ERROR_FAILED;
553
[email protected]cd8fd37f2014-05-20 15:45:21554 entry.mutable_file_specific_info()->mutable_cache_state()->clear_md5();
555 error = storage_->PutEntry(entry);
[email protected]996139412014-05-10 06:19:50556 if (error != FILE_ERROR_OK)
557 return error;
[email protected]b568b882013-06-10 04:38:07558
[email protected]8b03ab3a2014-01-15 17:52:45559 write_opened_files_[id]++;
Stuart Langleybe4035742018-05-10 05:32:27560 *file_closer = std::make_unique<base::ScopedClosureRunner>(
561 base::Bind(&google_apis::RunTaskWithTaskRunner, blocking_task_runner_,
[email protected]8b03ab3a2014-01-15 17:52:45562 base::Bind(&FileCache::CloseForWrite,
Stuart Langleybe4035742018-05-10 05:32:27563 weak_ptr_factory_.GetWeakPtr(), id)));
[email protected]8b03ab3a2014-01-15 17:52:45564 return FILE_ERROR_OK;
565}
566
567bool FileCache::IsOpenedForWrite(const std::string& id) {
568 AssertOnSequencedWorkerPool();
lukasza81be9752015-06-17 00:14:35569 return write_opened_files_.count(id) != 0;
[email protected]b568b882013-06-10 04:38:07570}
571
[email protected]b1bf19a2014-01-21 04:45:19572FileError FileCache::UpdateMd5(const std::string& id) {
[email protected]fcf8eafe02013-05-28 11:15:39573 AssertOnSequencedWorkerPool();
[email protected]eca3fc92013-05-01 03:53:40574
[email protected]b1bf19a2014-01-21 04:45:19575 if (IsOpenedForWrite(id))
576 return FILE_ERROR_IN_USE;
577
[email protected]cd8fd37f2014-05-20 15:45:21578 ResourceEntry entry;
579 FileError error = storage_->GetEntry(id, &entry);
[email protected]996139412014-05-10 06:19:50580 if (error != FILE_ERROR_OK)
581 return error;
[email protected]cd8fd37f2014-05-20 15:45:21582 if (!entry.file_specific_info().cache_state().is_present())
[email protected]b1bf19a2014-01-21 04:45:19583 return FILE_ERROR_NOT_FOUND;
584
hashimoto246e4a82015-04-17 07:44:49585 const std::string& md5 =
586 util::GetMd5Digest(GetCacheFilePath(id), &in_shutdown_);
587 if (in_shutdown_.IsSet())
588 return FILE_ERROR_ABORT;
[email protected]b1bf19a2014-01-21 04:45:19589 if (md5.empty())
590 return FILE_ERROR_NOT_FOUND;
591
[email protected]cd8fd37f2014-05-20 15:45:21592 entry.mutable_file_specific_info()->mutable_cache_state()->set_md5(md5);
593 return storage_->PutEntry(entry);
[email protected]b1bf19a2014-01-21 04:45:19594}
595
596FileError FileCache::ClearDirty(const std::string& id) {
597 AssertOnSequencedWorkerPool();
598
599 if (IsOpenedForWrite(id))
600 return FILE_ERROR_IN_USE;
601
[email protected]fcf8eafe02013-05-28 11:15:39602 // Clearing a dirty file means its entry and actual file blob must exist in
603 // cache.
[email protected]cd8fd37f2014-05-20 15:45:21604 ResourceEntry entry;
605 FileError error = storage_->GetEntry(id, &entry);
[email protected]996139412014-05-10 06:19:50606 if (error != FILE_ERROR_OK)
607 return error;
[email protected]cd8fd37f2014-05-20 15:45:21608 if (!entry.file_specific_info().cache_state().is_present()) {
[email protected]fcf8eafe02013-05-28 11:15:39609 LOG(WARNING) << "Can't clear dirty state of a file that wasn't cached: "
[email protected]c9e4738d2013-08-26 03:04:07610 << id;
[email protected]fcf8eafe02013-05-28 11:15:39611 return FILE_ERROR_NOT_FOUND;
612 }
613
[email protected]8b03ab3a2014-01-15 17:52:45614 // If a file is not dirty (it should have been marked dirty via OpenForWrite),
615 // clearing its dirty state is an invalid operation.
[email protected]cd8fd37f2014-05-20 15:45:21616 if (!entry.file_specific_info().cache_state().is_dirty()) {
[email protected]c9e4738d2013-08-26 03:04:07617 LOG(WARNING) << "Can't clear dirty state of a non-dirty file: " << id;
[email protected]fcf8eafe02013-05-28 11:15:39618 return FILE_ERROR_INVALID_OPERATION;
619 }
620
[email protected]cd8fd37f2014-05-20 15:45:21621 entry.mutable_file_specific_info()->mutable_cache_state()->set_is_dirty(
622 false);
okac6aac502016-05-23 12:16:58623 if (!entry.file_specific_info().cache_state().is_pinned()) {
624 if (!SetRemovable(GetCacheFilePath(entry.local_id())))
625 return FILE_ERROR_FAILED;
626 }
627
[email protected]cd8fd37f2014-05-20 15:45:21628 return storage_->PutEntry(entry);
[email protected]73f9c742012-06-15 07:37:13629}
630
[email protected]c9e4738d2013-08-26 03:04:07631FileError FileCache::Remove(const std::string& id) {
[email protected]3361a542013-05-22 17:38:27632 AssertOnSequencedWorkerPool();
633
[email protected]cd8fd37f2014-05-20 15:45:21634 ResourceEntry entry;
[email protected]3361a542013-05-22 17:38:27635
[email protected]4b60a25f2013-06-17 09:43:11636 // If entry doesn't exist, nothing to do.
[email protected]cd8fd37f2014-05-20 15:45:21637 FileError error = storage_->GetEntry(id, &entry);
[email protected]996139412014-05-10 06:19:50638 if (error == FILE_ERROR_NOT_FOUND)
[email protected]3361a542013-05-22 17:38:27639 return FILE_ERROR_OK;
[email protected]996139412014-05-10 06:19:50640 if (error != FILE_ERROR_OK)
641 return error;
[email protected]cd8fd37f2014-05-20 15:45:21642 if (!entry.file_specific_info().has_cache_state())
643 return FILE_ERROR_OK;
[email protected]3361a542013-05-22 17:38:27644
[email protected]d1ad8fa2013-07-11 13:23:20645 // Cannot delete a mounted file.
[email protected]c9e4738d2013-08-26 03:04:07646 if (mounted_files_.count(id))
[email protected]4b60a25f2013-06-17 09:43:11647 return FILE_ERROR_IN_USE;
648
[email protected]91a464e62013-07-10 09:30:06649 // Delete the file.
[email protected]c9e4738d2013-08-26 03:04:07650 base::FilePath path = GetCacheFilePath(id);
[email protected]dd3aa792013-07-16 19:10:23651 if (!base::DeleteFile(path, false /* recursive */))
[email protected]91a464e62013-07-10 09:30:06652 return FILE_ERROR_FAILED;
[email protected]3361a542013-05-22 17:38:27653
654 // Now that all file operations have completed, remove from metadata.
[email protected]cd8fd37f2014-05-20 15:45:21655 entry.mutable_file_specific_info()->clear_cache_state();
656 return storage_->PutEntry(entry);
[email protected]3361a542013-05-22 17:38:27657}
658
[email protected]823ca9712013-09-13 10:09:09659bool FileCache::ClearAll() {
660 AssertOnSequencedWorkerPool();
[email protected]f861b392012-08-03 20:41:12661
[email protected]823ca9712013-09-13 10:09:09662 // Remove files.
663 base::FileEnumerator enumerator(cache_file_directory_,
664 false, // not recursive
665 base::FileEnumerator::FILES);
666 for (base::FilePath file = enumerator.Next(); !file.empty();
667 file = enumerator.Next())
668 base::DeleteFile(file, false /* recursive */);
669
670 return true;
[email protected]f861b392012-08-03 20:41:12671}
672
[email protected]34a1bbf32013-06-17 07:24:02673bool FileCache::Initialize() {
[email protected]ca5f6da2012-06-18 12:54:59674 AssertOnSequencedWorkerPool();
675
[email protected]b1bf19a2014-01-21 04:45:19676 // Older versions do not clear MD5 when marking entries dirty.
677 // Clear MD5 of all dirty entries to deal with old data.
dchengf42750232016-04-12 04:12:27678 std::unique_ptr<ResourceMetadataStorage::Iterator> it =
679 storage_->GetIterator();
[email protected]b1bf19a2014-01-21 04:45:19680 for (; !it->IsAtEnd(); it->Advance()) {
[email protected]cd8fd37f2014-05-20 15:45:21681 if (it->GetValue().file_specific_info().cache_state().is_dirty()) {
682 ResourceEntry new_entry(it->GetValue());
683 new_entry.mutable_file_specific_info()->mutable_cache_state()->
684 clear_md5();
685 if (storage_->PutEntry(new_entry) != FILE_ERROR_OK)
[email protected]b1bf19a2014-01-21 04:45:19686 return false;
687 }
688 }
[email protected]996139412014-05-10 06:19:50689 if (it->HasError())
690 return false;
[email protected]b1bf19a2014-01-21 04:45:19691
[email protected]f2731d12013-10-22 03:23:15692 if (!RenameCacheFilesToNewFormat())
693 return false;
okac6aac502016-05-23 12:16:58694
695 // Run this every time to resolve inconsistency between metadata
696 // and file attributes which possibly occurs on abrupt power failure.
697 if (!FixMetadataAndFileAttributes()) {
698 return false;
699 }
700
[email protected]e8842b192013-06-11 04:05:14701 return true;
[email protected]ca5f6da2012-06-18 12:54:59702}
703
[email protected]34a1bbf32013-06-17 07:24:02704void FileCache::Destroy() {
Stuart Langleybe4035742018-05-10 05:32:27705 DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
[email protected]34a1bbf32013-06-17 07:24:02706
hashimoto246e4a82015-04-17 07:44:49707 in_shutdown_.Set();
708
[email protected]34a1bbf32013-06-17 07:24:02709 // Destroy myself on the blocking pool.
710 // Note that base::DeletePointer<> cannot be used as the destructor of this
711 // class is private.
712 blocking_task_runner_->PostTask(
tzik2bcf8e42018-07-31 11:22:15713 FROM_HERE, base::BindOnce(&FileCache::DestroyOnBlockingPool,
714 base::Unretained(this)));
[email protected]34a1bbf32013-06-17 07:24:02715}
716
[email protected]eca3fc92013-05-01 03:53:40717void FileCache::DestroyOnBlockingPool() {
[email protected]73f9c742012-06-15 07:37:13718 AssertOnSequencedWorkerPool();
719 delete this;
720}
721
[email protected]b7af4f12013-10-31 06:57:45722bool FileCache::RecoverFilesFromCacheDirectory(
[email protected]760abc32013-11-01 05:13:01723 const base::FilePath& dest_directory,
[email protected]026d4a522013-11-05 14:22:18724 const ResourceMetadataStorage::RecoveredCacheInfoMap&
725 recovered_cache_info) {
[email protected]b7af4f12013-10-31 06:57:45726 int file_number = 1;
727
728 base::FileEnumerator enumerator(cache_file_directory_,
729 false, // not recursive
730 base::FileEnumerator::FILES);
731 for (base::FilePath current = enumerator.Next(); !current.empty();
732 current = enumerator.Next()) {
733 const std::string& id = GetIdFromPath(current);
[email protected]cd8fd37f2014-05-20 15:45:21734 ResourceEntry entry;
735 FileError error = storage_->GetEntry(id, &entry);
736 if (error != FILE_ERROR_OK && error != FILE_ERROR_NOT_FOUND)
737 return false;
738 if (error == FILE_ERROR_OK &&
739 entry.file_specific_info().cache_state().is_present()) {
[email protected]b7af4f12013-10-31 06:57:45740 // This file is managed by FileCache, no need to recover it.
741 continue;
742 }
743
[email protected]760abc32013-11-01 05:13:01744 // If a cache entry which is non-dirty and has matching MD5 is found in
745 // |recovered_cache_entries|, it means the current file is already uploaded
746 // to the server. Just delete it instead of recovering it.
[email protected]026d4a522013-11-05 14:22:18747 ResourceMetadataStorage::RecoveredCacheInfoMap::const_iterator it =
748 recovered_cache_info.find(id);
749 if (it != recovered_cache_info.end()) {
750 // Due to the DB corruption, cache info might be recovered from old
751 // revision. Perform MD5 check even when is_dirty is false just in case.
752 if (!it->second.is_dirty &&
hashimoto246e4a82015-04-17 07:44:49753 it->second.md5 == util::GetMd5Digest(current, &in_shutdown_)) {
[email protected]760abc32013-11-01 05:13:01754 base::DeleteFile(current, false /* recursive */);
755 continue;
756 }
757 }
758
[email protected]b7af4f12013-10-31 06:57:45759 // Read file contents to sniff mime type.
760 std::vector<char> content(net::kMaxBytesToSniff);
761 const int read_result =
[email protected]7600d0b2013-12-08 21:43:30762 base::ReadFile(current, &content[0], content.size());
[email protected]b7af4f12013-10-31 06:57:45763 if (read_result < 0) {
764 LOG(WARNING) << "Cannot read: " << current.value();
765 return false;
766 }
767 if (read_result == 0) // Skip empty files.
768 continue;
769
[email protected]026d4a522013-11-05 14:22:18770 // Use recovered file name if available, otherwise decide file name with
771 // sniffed mime type.
[email protected]b7af4f12013-10-31 06:57:45772 base::FilePath dest_base_name(FILE_PATH_LITERAL("file"));
773 std::string mime_type;
[email protected]026d4a522013-11-05 14:22:18774 if (it != recovered_cache_info.end() && !it->second.title.empty()) {
775 // We can use a file name recovered from the trashed DB.
776 dest_base_name = base::FilePath::FromUTF8Unsafe(it->second.title);
Matt Menkefcbb1bd72018-01-31 21:53:12777 } else if (net::SniffMimeType(
778 &content[0], read_result, net::FilePathToFileURL(current),
779 std::string(), net::ForceSniffFileUrlsForHtml::kDisabled,
780 &mime_type) ||
[email protected]026d4a522013-11-05 14:22:18781 net::SniffMimeTypeFromLocalData(&content[0], read_result,
782 &mime_type)) {
[email protected]b7af4f12013-10-31 06:57:45783 // Change base name for common mime types.
784 if (net::MatchesMimeType("image/*", mime_type)) {
785 dest_base_name = base::FilePath(FILE_PATH_LITERAL("image"));
786 } else if (net::MatchesMimeType("video/*", mime_type)) {
787 dest_base_name = base::FilePath(FILE_PATH_LITERAL("video"));
788 } else if (net::MatchesMimeType("audio/*", mime_type)) {
789 dest_base_name = base::FilePath(FILE_PATH_LITERAL("audio"));
790 }
791
792 // Estimate extension from mime type.
793 std::vector<base::FilePath::StringType> extensions;
794 base::FilePath::StringType extension;
795 if (net::GetPreferredExtensionForMimeType(mime_type, &extension))
796 extensions.push_back(extension);
797 else
798 net::GetExtensionsForMimeType(mime_type, &extensions);
799
800 // Add extension if possible.
801 if (!extensions.empty())
802 dest_base_name = dest_base_name.AddExtension(extensions[0]);
803 }
804
805 // Add file number to the file name and move.
806 const base::FilePath& dest_path = dest_directory.Append(dest_base_name)
807 .InsertBeforeExtensionASCII(base::StringPrintf("%08d", file_number++));
[email protected]426d1c92013-12-03 20:08:54808 if (!base::CreateDirectory(dest_directory) ||
[email protected]b7af4f12013-10-31 06:57:45809 !base::Move(current, dest_path)) {
810 LOG(WARNING) << "Failed to move: " << current.value()
811 << " to " << dest_path.value();
812 return false;
813 }
814 }
Steven Holte95922222018-09-14 20:06:23815 UMA_HISTOGRAM_COUNTS_1M("Drive.NumberOfCacheFilesRecoveredAfterDBCorruption",
816 file_number - 1);
[email protected]b7af4f12013-10-31 06:57:45817 return true;
818}
819
[email protected]54ba37502013-05-09 08:43:40820FileError FileCache::MarkAsUnmounted(const base::FilePath& file_path) {
[email protected]9564c1502012-11-28 12:12:16821 AssertOnSequencedWorkerPool();
[email protected]eca3fc92013-05-01 03:53:40822 DCHECK(IsUnderFileCacheDirectory(file_path));
[email protected]9564c1502012-11-28 12:12:16823
[email protected]c9e4738d2013-08-26 03:04:07824 std::string id = GetIdFromPath(file_path);
[email protected]9564c1502012-11-28 12:12:16825
[email protected]cd8fd37f2014-05-20 15:45:21826 // Get the entry associated with the id.
827 ResourceEntry entry;
828 FileError error = storage_->GetEntry(id, &entry);
[email protected]996139412014-05-10 06:19:50829 if (error != FILE_ERROR_OK)
830 return error;
[email protected]9564c1502012-11-28 12:12:16831
[email protected]c9e4738d2013-08-26 03:04:07832 std::set<std::string>::iterator it = mounted_files_.find(id);
[email protected]4b60a25f2013-06-17 09:43:11833 if (it == mounted_files_.end())
[email protected]78a158b2013-04-23 06:57:49834 return FILE_ERROR_INVALID_OPERATION;
[email protected]9564c1502012-11-28 12:12:16835
[email protected]4b60a25f2013-06-17 09:43:11836 mounted_files_.erase(it);
[email protected]78a158b2013-04-23 06:57:49837 return FILE_ERROR_OK;
[email protected]9564c1502012-11-28 12:12:16838}
839
avibc5337b2015-12-25 23:16:33840int64_t FileCache::GetAvailableSpace() {
841 int64_t free_space = 0;
[email protected]f6fd98a2012-12-14 00:04:02842 if (free_disk_space_getter_)
843 free_space = free_disk_space_getter_->AmountOfFreeDiskSpace();
844 else
yawano4c36b9a02015-10-23 06:17:23845 free_space = base::SysInfo::AmountOfFreeDiskSpace(cache_file_directory_);
[email protected]f6fd98a2012-12-14 00:04:02846
847 // Subtract this as if this portion does not exist.
lukasza3fb22622015-08-27 21:04:34848 free_space -= drive::internal::kMinFreeSpaceInBytes;
yawano4c36b9a02015-10-23 06:17:23849 return free_space;
[email protected]f6fd98a2012-12-14 00:04:02850}
851
[email protected]f2731d12013-10-22 03:23:15852bool FileCache::RenameCacheFilesToNewFormat() {
853 base::FileEnumerator enumerator(cache_file_directory_,
854 false, // not recursive
855 base::FileEnumerator::FILES);
856 for (base::FilePath current = enumerator.Next(); !current.empty();
857 current = enumerator.Next()) {
858 base::FilePath new_path = current.RemoveExtension();
859 if (!new_path.Extension().empty()) {
860 // Delete files with multiple extensions.
861 if (!base::DeleteFile(current, false /* recursive */))
862 return false;
863 continue;
864 }
865 const std::string& id = GetIdFromPath(new_path);
866 new_path = GetCacheFilePath(util::CanonicalizeResourceId(id));
867 if (new_path != current && !base::Move(current, new_path))
868 return false;
[email protected]91a464e62013-07-10 09:30:06869 }
[email protected]f2731d12013-10-22 03:23:15870 return true;
[email protected]91a464e62013-07-10 09:30:06871}
872
okac6aac502016-05-23 12:16:58873bool FileCache::FixMetadataAndFileAttributes() {
dchengf42750232016-04-12 04:12:27874 std::unique_ptr<ResourceMetadataStorage::Iterator> it =
okac6aac502016-05-23 12:16:58875 storage_->GetIterator();
876
yawano9fd1e632016-02-04 09:00:06877 for (; !it->IsAtEnd(); it->Advance()) {
okac6aac502016-05-23 12:16:58878 ResourceEntry entry = it->GetValue();
879 FileCacheEntry* file_cache_entry =
880 entry.mutable_file_specific_info()->mutable_cache_state();
yawano9fd1e632016-02-04 09:00:06881
okac6aac502016-05-23 12:16:58882 const base::FilePath filepath = GetPathForId(cache_file_directory_,
883 entry.local_id());
yawano9fd1e632016-02-04 09:00:06884
okac6aac502016-05-23 12:16:58885 if (base::PathExists(filepath)) {
886 if (file_cache_entry->is_present()) {
887 // Update file attribues for cryptohome.
888 if (file_cache_entry->is_pinned() || file_cache_entry->is_dirty()) {
889 if (!UnsetRemovable(filepath)) return false;
890 } else {
891 if (!SetRemovable(filepath)) return false;
892 }
893 } else {
894 // Delete file if the file is present but metadata says not.
895 // It happens only on abrupt shutdown.
896 LOG(WARNING)
897 << "File is present but metadata's state was inconsistent.";
898
899 if (!base::DeleteFile(filepath, false /* recursive */))
900 return false;
yawano1c325bf2016-04-20 06:37:03901 }
okac6aac502016-05-23 12:16:58902 } else {
903 // Update metatadata if there is no file but metadata says there is.
904 // It happens when cryptohome removed the file.
905 // We don't clear is_pinned here, so that file download is restarted on
906 // the following scenario:
907 // 1. The file was pinned but not present.
908 // 2. Then the file was downloaded and became present.
909 // 3. Unclean shutdown happens, metadata update was saved to the disk,
910 // but the file move was not.
911 if (file_cache_entry->is_present()) {
912 file_cache_entry->set_is_present(false);
913 file_cache_entry->set_is_dirty(false);
914 file_cache_entry->clear_md5();
915 if (storage_->PutEntry(entry) != FILE_ERROR_OK)
916 return false;
917 }
yawano9fd1e632016-02-04 09:00:06918 }
yawano9fd1e632016-02-04 09:00:06919 }
920
okac6aac502016-05-23 12:16:58921 return MarkAsDriveCacheDir(cache_file_directory_);
yawano9fd1e632016-02-04 09:00:06922}
923
[email protected]8b03ab3a2014-01-15 17:52:45924void FileCache::CloseForWrite(const std::string& id) {
925 AssertOnSequencedWorkerPool();
926
927 std::map<std::string, int>::iterator it = write_opened_files_.find(id);
928 if (it == write_opened_files_.end())
929 return;
930
931 DCHECK_LT(0, it->second);
932 --it->second;
933 if (it->second == 0)
934 write_opened_files_.erase(it);
[email protected]f92367ae2014-06-02 07:35:43935
936 // Update last modified date.
937 ResourceEntry entry;
938 FileError error = storage_->GetEntry(id, &entry);
939 if (error != FILE_ERROR_OK) {
940 LOG(ERROR) << "Failed to get entry: " << id << ", "
941 << FileErrorToString(error);
942 return;
943 }
Shuhei Takahashia25ace562017-09-15 09:22:17944 int64_t now = base::Time::Now().ToInternalValue();
945 entry.mutable_file_info()->set_last_modified(now);
946 entry.set_last_modified_by_me(now);
[email protected]f92367ae2014-06-02 07:35:43947 error = storage_->PutEntry(entry);
948 if (error != FILE_ERROR_OK) {
949 LOG(ERROR) << "Failed to put entry: " << id << ", "
950 << FileErrorToString(error);
951 }
[email protected]8b03ab3a2014-01-15 17:52:45952}
953
yawano8578abf2015-08-26 09:15:50954bool FileCache::IsEvictable(const std::string& id, const ResourceEntry& entry) {
fukino6380c07c2016-06-09 07:28:29955 return IsPresent(entry) &&
yawano8578abf2015-08-26 09:15:50956 !entry.file_specific_info().cache_state().is_pinned() &&
957 !entry.file_specific_info().cache_state().is_dirty() &&
958 !mounted_files_.count(id);
959}
960
[email protected]59c7cdec2013-05-07 04:17:13961} // namespace internal
[email protected]d9d04df2012-10-12 07:06:35962} // namespace drive