blob: 6faa09a0b1fa870b2e69fb26e13248a19265ee5f [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
msramek4cfdf21b2016-01-06 11:59:025#include "components/drive/file_cache.h"
[email protected]3653146a2012-05-29 13:41:476
yawano4c36b9a02015-10-23 06:17:237#include <queue>
[email protected]3653146a2012-05-29 13:41:478#include <vector>
9
lukasza037c10b12015-06-12 04:21:2510#include "base/bind.h"
11#include "base/bind_helpers.h"
[email protected]8b03ab3a2014-01-15 17:52:4512#include "base/callback_helpers.h"
[email protected]25a4c1c2013-06-08 04:53:3613#include "base/files/file_enumerator.h"
thestig18dfb7a52014-08-26 10:44:0414#include "base/files/file_util.h"
lukasza037c10b12015-06-12 04:21:2515#include "base/location.h"
[email protected]3653146a2012-05-29 13:41:4716#include "base/logging.h"
[email protected]d105b3ad2013-11-01 05:33:1317#include "base/metrics/histogram.h"
[email protected]5c073322013-06-11 08:03:3018#include "base/strings/string_util.h"
19#include "base/strings/stringprintf.h"
[email protected]a321b9632012-06-14 03:29:1720#include "base/sys_info.h"
avibc5337b2015-12-25 23:16:3321#include "build/build_config.h"
lukasza01b9d55a2015-07-21 15:19:2522#include "components/drive/drive.pb.h"
lukasza8acc4eb2015-07-20 20:57:2023#include "components/drive/drive_api_util.h"
lukasza6364a022015-08-21 01:13:2424#include "components/drive/file_system_core_util.h"
25#include "components/drive/resource_metadata_storage.h"
[email protected]8b03ab3a2014-01-15 17:52:4526#include "google_apis/drive/task_util.h"
[email protected]d96cf752014-04-09 04:05:2827#include "net/base/filename_util.h"
[email protected]b7af4f12013-10-31 06:57:4528#include "net/base/mime_sniffer.h"
29#include "net/base/mime_util.h"
[email protected]7986b8d2012-06-14 15:05:1430
[email protected]d9d04df2012-10-12 07:06:3531namespace drive {
[email protected]59c7cdec2013-05-07 04:17:1332namespace internal {
[email protected]3653146a2012-05-29 13:41:4733namespace {
34
[email protected]c9e4738d2013-08-26 03:04:0735// Returns ID extracted from the path.
36std::string GetIdFromPath(const base::FilePath& path) {
[email protected]91a464e62013-07-10 09:30:0637 return util::UnescapeCacheFileName(path.BaseName().AsUTF8Unsafe());
38}
39
yawano9fd1e632016-02-04 09:00:0640base::FilePath GetPathForId(const base::FilePath& cache_directory,
41 const std::string& id) {
42 return cache_directory.Append(
43 base::FilePath::FromUTF8Unsafe(util::EscapeCacheFileName(id)));
44}
45
yawano4c36b9a02015-10-23 06:17:2346typedef std::pair<base::File::Info, ResourceEntry> CacheInfo;
47
48class CacheInfoLatestCompare {
49 public:
50 bool operator()(const CacheInfo& info_a, const CacheInfo& info_b) {
51 return info_a.first.last_accessed < info_b.first.last_accessed;
52 }
53};
54
55const size_t kMaxNumOfEvictedCacheFiles = 30000;
56
[email protected]a321b9632012-06-14 03:29:1757} // namespace
[email protected]32a7fc852012-06-08 17:25:5058
[email protected]2df61e12013-06-21 16:00:0959FileCache::FileCache(ResourceMetadataStorage* storage,
[email protected]e07f7b7b2013-06-19 03:43:1260 const base::FilePath& cache_file_directory,
[email protected]eca3fc92013-05-01 03:53:4061 base::SequencedTaskRunner* blocking_task_runner,
62 FreeDiskSpaceGetterInterface* free_disk_space_getter)
[email protected]2df61e12013-06-21 16:00:0963 : cache_file_directory_(cache_file_directory),
[email protected]ddbf2052012-07-13 15:07:0264 blocking_task_runner_(blocking_task_runner),
[email protected]2df61e12013-06-21 16:00:0965 storage_(storage),
[email protected]f6fd98a2012-12-14 00:04:0266 free_disk_space_getter_(free_disk_space_getter),
yawano4c36b9a02015-10-23 06:17:2367 max_num_of_evicted_cache_files_(kMaxNumOfEvictedCacheFiles),
[email protected]9c009092013-05-01 03:14:0968 weak_ptr_factory_(this) {
[email protected]144b6c42013-06-14 07:30:3869 DCHECK(blocking_task_runner_.get());
[email protected]3653146a2012-05-29 13:41:4770}
71
[email protected]eca3fc92013-05-01 03:53:4072FileCache::~FileCache() {
[email protected]17196ee2012-12-13 06:23:5173 // Must be on the sequenced worker pool, as |metadata_| must be deleted on
74 // the sequenced worker pool.
[email protected]73f9c742012-06-15 07:37:1375 AssertOnSequencedWorkerPool();
[email protected]3653146a2012-05-29 13:41:4776}
77
yawano4c36b9a02015-10-23 06:17:2378void FileCache::SetMaxNumOfEvictedCacheFilesForTest(
79 size_t max_num_of_evicted_cache_files) {
80 max_num_of_evicted_cache_files_ = max_num_of_evicted_cache_files;
81}
82
[email protected]c9e4738d2013-08-26 03:04:0783base::FilePath FileCache::GetCacheFilePath(const std::string& id) const {
yawano9fd1e632016-02-04 09:00:0684 return GetPathForId(cache_file_directory_, id);
[email protected]32a7fc852012-06-08 17:25:5085}
86
[email protected]eca3fc92013-05-01 03:53:4087void FileCache::AssertOnSequencedWorkerPool() {
[email protected]8e37b9b2013-12-11 09:06:0288 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
[email protected]fcc92a52012-06-08 22:54:1689}
90
[email protected]eca3fc92013-05-01 03:53:4091bool FileCache::IsUnderFileCacheDirectory(const base::FilePath& path) const {
[email protected]e07f7b7b2013-06-19 03:43:1292 return cache_file_directory_.IsParent(path);
[email protected]01ba15f72012-06-09 00:41:0593}
94
avibc5337b2015-12-25 23:16:3395bool FileCache::FreeDiskSpaceIfNeededFor(int64_t num_bytes) {
[email protected]ec514362013-05-27 17:52:2296 AssertOnSequencedWorkerPool();
97
98 // Do nothing and return if we have enough space.
yawano4c36b9a02015-10-23 06:17:2399 if (GetAvailableSpace() >= num_bytes)
[email protected]ec514362013-05-27 17:52:22100 return true;
101
102 // Otherwise, try to free up the disk space.
103 DVLOG(1) << "Freeing up disk space for " << num_bytes;
[email protected]f8b1a532013-06-06 08:35:08104
[email protected]9d147582013-06-14 06:25:45105 // Remove all files which have no corresponding cache entries.
[email protected]e07f7b7b2013-06-19 03:43:12106 base::FileEnumerator enumerator(cache_file_directory_,
[email protected]9d147582013-06-14 06:25:45107 false, // not recursive
108 base::FileEnumerator::FILES);
[email protected]cd8fd37f2014-05-20 15:45:21109 ResourceEntry entry;
[email protected]9d147582013-06-14 06:25:45110 for (base::FilePath current = enumerator.Next(); !current.empty();
111 current = enumerator.Next()) {
yawano4c36b9a02015-10-23 06:17:23112 const std::string id = GetIdFromPath(current);
113 const FileError error = storage_->GetEntry(id, &entry);
114
115 if (error == FILE_ERROR_NOT_FOUND)
[email protected]dd3aa792013-07-16 19:10:23116 base::DeleteFile(current, false /* recursive */);
[email protected]996139412014-05-10 06:19:50117 else if (error != FILE_ERROR_OK)
118 return false;
[email protected]9d147582013-06-14 06:25:45119 }
[email protected]ec514362013-05-27 17:52:22120
yawano4c36b9a02015-10-23 06:17:23121 // Check available space again. If we have enough space here, do nothing.
avibc5337b2015-12-25 23:16:33122 const int64_t available_space = GetAvailableSpace();
yawano4c36b9a02015-10-23 06:17:23123 if (available_space >= num_bytes)
124 return true;
125
avibc5337b2015-12-25 23:16:33126 const int64_t requested_space = num_bytes - available_space;
yawano4c36b9a02015-10-23 06:17:23127
128 // Put all entries in priority queue where latest entry becomes top.
129 std::priority_queue<CacheInfo, std::vector<CacheInfo>, CacheInfoLatestCompare>
130 cache_info_queue;
dchengf42750232016-04-12 04:12:27131 std::unique_ptr<ResourceMetadataStorage::Iterator> it =
132 storage_->GetIterator();
yawano4c36b9a02015-10-23 06:17:23133 for (; !it->IsAtEnd(); it->Advance()) {
134 if (IsEvictable(it->GetID(), it->GetValue())) {
135 const ResourceEntry& entry = it->GetValue();
136
137 const base::FilePath& cache_path = GetCacheFilePath(entry.local_id());
138 base::File::Info info;
139 // If it fails to get file info of |cache_path|, use default value as its
140 // file info. i.e. the file becomes least recently used one.
141 base::GetFileInfo(cache_path, &info);
142
143 CacheInfo cache_info = std::make_pair(info, entry);
144
145 if (cache_info_queue.size() < max_num_of_evicted_cache_files_) {
146 cache_info_queue.push(cache_info);
147 } else if (cache_info_queue.size() >= max_num_of_evicted_cache_files_ &&
148 cache_info.first.last_accessed <
149 cache_info_queue.top().first.last_accessed) {
150 // Do not enqueue more than max_num_of_evicted_cache_files_ not to use
151 // up memory with this queue.
152 cache_info_queue.pop();
153 cache_info_queue.push(cache_info);
154 }
155 }
156 }
157 if (it->HasError())
158 return false;
159
160 // Copy entries to the vector. This becomes last-accessed desc order.
161 std::vector<CacheInfo> cache_info_list;
162 while (!cache_info_queue.empty()) {
163 cache_info_list.push_back(cache_info_queue.top());
164 cache_info_queue.pop();
165 }
166
167 // Update DB and delete files with accessing to the vector in ascending order.
avibc5337b2015-12-25 23:16:33168 int64_t evicted_cache_size = 0;
yawano4c36b9a02015-10-23 06:17:23169 auto iter = cache_info_list.rbegin();
170 while (evicted_cache_size < requested_space &&
171 iter != cache_info_list.rend()) {
172 const CacheInfo& cache_info = *iter;
173
174 // Update DB.
175 ResourceEntry entry = cache_info.second;
176 entry.mutable_file_specific_info()->clear_cache_state();
177 storage_->PutEntry(entry);
178
179 // Delete cache file.
180 const base::FilePath& path = GetCacheFilePath(entry.local_id());
181
182 if (base::DeleteFile(path, false /* recursive */))
183 evicted_cache_size += cache_info.first.size;
184
185 ++iter;
186 }
187
[email protected]ec514362013-05-27 17:52:22188 // Check the disk space again.
yawano4c36b9a02015-10-23 06:17:23189 return GetAvailableSpace() >= num_bytes;
[email protected]ec514362013-05-27 17:52:22190}
191
yawano8578abf2015-08-26 09:15:50192uint64_t FileCache::CalculateEvictableCacheSize() {
193 AssertOnSequencedWorkerPool();
194
195 uint64_t evictable_cache_size = 0;
battrea5f64bd2015-08-26 10:47:41196 int64_t cache_size = 0;
yawano8578abf2015-08-26 09:15:50197
dchengf42750232016-04-12 04:12:27198 std::unique_ptr<ResourceMetadataStorage::Iterator> it =
199 storage_->GetIterator();
yawano8578abf2015-08-26 09:15:50200 for (; !it->IsAtEnd(); it->Advance()) {
201 if (IsEvictable(it->GetID(), it->GetValue()) &&
202 base::GetFileSize(GetCacheFilePath(it->GetID()), &cache_size)) {
203 DCHECK_GE(cache_size, 0);
204 evictable_cache_size += cache_size;
205 }
206 }
207
208 if (it->HasError())
209 return 0;
210
211 return evictable_cache_size;
212}
213
[email protected]c9e4738d2013-08-26 03:04:07214FileError FileCache::GetFile(const std::string& id,
[email protected]ec514362013-05-27 17:52:22215 base::FilePath* cache_file_path) {
216 AssertOnSequencedWorkerPool();
217 DCHECK(cache_file_path);
218
[email protected]cd8fd37f2014-05-20 15:45:21219 ResourceEntry entry;
220 FileError error = storage_->GetEntry(id, &entry);
[email protected]996139412014-05-10 06:19:50221 if (error != FILE_ERROR_OK)
222 return error;
[email protected]cd8fd37f2014-05-20 15:45:21223 if (!entry.file_specific_info().cache_state().is_present())
[email protected]ec514362013-05-27 17:52:22224 return FILE_ERROR_NOT_FOUND;
225
[email protected]c9e4738d2013-08-26 03:04:07226 *cache_file_path = GetCacheFilePath(id);
[email protected]ec514362013-05-27 17:52:22227 return FILE_ERROR_OK;
228}
229
[email protected]c9e4738d2013-08-26 03:04:07230FileError FileCache::Store(const std::string& id,
[email protected]82c4eb92013-05-21 11:25:23231 const std::string& md5,
232 const base::FilePath& source_path,
233 FileOperationType file_operation_type) {
234 AssertOnSequencedWorkerPool();
[email protected]d8546c92013-05-02 05:09:59235
[email protected]cd8fd37f2014-05-20 15:45:21236 ResourceEntry entry;
237 FileError error = storage_->GetEntry(id, &entry);
238 if (error != FILE_ERROR_OK)
239 return error;
240
avibc5337b2015-12-25 23:16:33241 int64_t file_size = 0;
[email protected]8e37b9b2013-12-11 09:06:02242 if (file_operation_type == FILE_OPERATION_COPY) {
243 if (!base::GetFileSize(source_path, &file_size)) {
244 LOG(WARNING) << "Couldn't get file size for: " << source_path.value();
245 return FILE_ERROR_FAILED;
246 }
247 }
248 if (!FreeDiskSpaceIfNeededFor(file_size))
249 return FILE_ERROR_NO_LOCAL_SPACE;
[email protected]73f9c742012-06-15 07:37:13250
[email protected]1d0786a2014-02-06 12:37:08251 // If file is mounted, return error.
252 if (mounted_files_.count(id))
[email protected]8e37b9b2013-12-11 09:06:02253 return FILE_ERROR_IN_USE;
254
255 base::FilePath dest_path = GetCacheFilePath(id);
256 bool success = false;
257 switch (file_operation_type) {
258 case FILE_OPERATION_MOVE:
259 success = base::Move(source_path, dest_path);
260 break;
261 case FILE_OPERATION_COPY:
262 success = base::CopyFile(source_path, dest_path);
263 break;
264 default:
265 NOTREACHED();
266 }
267
268 if (!success) {
269 LOG(ERROR) << "Failed to store: "
270 << "source_path = " << source_path.value() << ", "
271 << "dest_path = " << dest_path.value() << ", "
272 << "file_operation_type = " << file_operation_type;
273 return FILE_ERROR_FAILED;
274 }
275
276 // Now that file operations have completed, update metadata.
[email protected]cd8fd37f2014-05-20 15:45:21277 FileCacheEntry* cache_state =
278 entry.mutable_file_specific_info()->mutable_cache_state();
279 cache_state->set_md5(md5);
280 cache_state->set_is_present(true);
[email protected]bae99ae52014-01-29 01:13:14281 if (md5.empty())
[email protected]cd8fd37f2014-05-20 15:45:21282 cache_state->set_is_dirty(true);
283 return storage_->PutEntry(entry);
[email protected]73f9c742012-06-15 07:37:13284}
285
[email protected]c9e4738d2013-08-26 03:04:07286FileError FileCache::Pin(const std::string& id) {
[email protected]f8b1a532013-06-06 08:35:08287 AssertOnSequencedWorkerPool();
288
[email protected]cd8fd37f2014-05-20 15:45:21289 ResourceEntry entry;
290 FileError error = storage_->GetEntry(id, &entry);
291 if (error != FILE_ERROR_OK)
[email protected]996139412014-05-10 06:19:50292 return error;
[email protected]cd8fd37f2014-05-20 15:45:21293 entry.mutable_file_specific_info()->mutable_cache_state()->set_is_pinned(
294 true);
295 return storage_->PutEntry(entry);
[email protected]f8b1a532013-06-06 08:35:08296}
297
[email protected]c9e4738d2013-08-26 03:04:07298FileError FileCache::Unpin(const std::string& id) {
[email protected]ec514362013-05-27 17:52:22299 AssertOnSequencedWorkerPool();
300
301 // Unpinning a file means its entry must exist in cache.
[email protected]cd8fd37f2014-05-20 15:45:21302 ResourceEntry entry;
303 FileError error = storage_->GetEntry(id, &entry);
[email protected]996139412014-05-10 06:19:50304 if (error != FILE_ERROR_OK)
305 return error;
[email protected]ec514362013-05-27 17:52:22306
[email protected]ec514362013-05-27 17:52:22307 // Now that file operations have completed, update metadata.
[email protected]cd8fd37f2014-05-20 15:45:21308 if (entry.file_specific_info().cache_state().is_present()) {
309 entry.mutable_file_specific_info()->mutable_cache_state()->set_is_pinned(
310 false);
[email protected]ec514362013-05-27 17:52:22311 } else {
312 // Remove the existing entry if we are unpinning a non-present file.
[email protected]cd8fd37f2014-05-20 15:45:21313 entry.mutable_file_specific_info()->clear_cache_state();
[email protected]ec514362013-05-27 17:52:22314 }
[email protected]cd8fd37f2014-05-20 15:45:21315 error = storage_->PutEntry(entry);
316 if (error != FILE_ERROR_OK)
317 return error;
[email protected]bd2254d2013-06-12 16:00:47318
[email protected]9d147582013-06-14 06:25:45319 // Now it's a chance to free up space if needed.
[email protected]bd2254d2013-06-12 16:00:47320 FreeDiskSpaceIfNeededFor(0);
321
[email protected]ec514362013-05-27 17:52:22322 return FILE_ERROR_OK;
323}
324
[email protected]c3f65642013-08-28 02:04:33325FileError FileCache::MarkAsMounted(const std::string& id,
326 base::FilePath* cache_file_path) {
327 AssertOnSequencedWorkerPool();
328 DCHECK(cache_file_path);
329
330 // Get cache entry associated with the id and md5
[email protected]cd8fd37f2014-05-20 15:45:21331 ResourceEntry entry;
332 FileError error = storage_->GetEntry(id, &entry);
[email protected]996139412014-05-10 06:19:50333 if (error != FILE_ERROR_OK)
334 return error;
[email protected]cd8fd37f2014-05-20 15:45:21335 if (!entry.file_specific_info().cache_state().is_present())
336 return FILE_ERROR_NOT_FOUND;
[email protected]c3f65642013-08-28 02:04:33337
338 if (mounted_files_.count(id))
339 return FILE_ERROR_INVALID_OPERATION;
340
[email protected]c3f65642013-08-28 02:04:33341 base::FilePath path = GetCacheFilePath(id);
lukasza6364a022015-08-21 01:13:24342
343#if defined(OS_CHROMEOS)
344 // Ensure the file is readable to cros_disks. See crbug.com/236994.
[email protected]b264eab2013-11-27 23:22:08345 if (!base::SetPosixFilePermissions(
[email protected]c3f65642013-08-28 02:04:33346 path,
[email protected]b264eab2013-11-27 23:22:08347 base::FILE_PERMISSION_READ_BY_USER |
348 base::FILE_PERMISSION_WRITE_BY_USER |
349 base::FILE_PERMISSION_READ_BY_GROUP |
350 base::FILE_PERMISSION_READ_BY_OTHERS))
[email protected]c3f65642013-08-28 02:04:33351 return FILE_ERROR_FAILED;
lukasza6364a022015-08-21 01:13:24352#endif
[email protected]c3f65642013-08-28 02:04:33353
354 mounted_files_.insert(id);
355
356 *cache_file_path = path;
357 return FILE_ERROR_OK;
358}
359
[email protected]8b03ab3a2014-01-15 17:52:45360FileError FileCache::OpenForWrite(
361 const std::string& id,
dchengf42750232016-04-12 04:12:27362 std::unique_ptr<base::ScopedClosureRunner>* file_closer) {
[email protected]b568b882013-06-10 04:38:07363 AssertOnSequencedWorkerPool();
364
[email protected]b568b882013-06-10 04:38:07365 // Marking a file dirty means its entry and actual file blob must exist in
366 // cache.
[email protected]cd8fd37f2014-05-20 15:45:21367 ResourceEntry entry;
368 FileError error = storage_->GetEntry(id, &entry);
[email protected]996139412014-05-10 06:19:50369 if (error != FILE_ERROR_OK)
370 return error;
[email protected]cd8fd37f2014-05-20 15:45:21371 if (!entry.file_specific_info().cache_state().is_present()) {
[email protected]c9e4738d2013-08-26 03:04:07372 LOG(WARNING) << "Can't mark dirty a file that wasn't cached: " << id;
[email protected]b568b882013-06-10 04:38:07373 return FILE_ERROR_NOT_FOUND;
374 }
375
[email protected]cd8fd37f2014-05-20 15:45:21376 entry.mutable_file_specific_info()->mutable_cache_state()->set_is_dirty(true);
377 entry.mutable_file_specific_info()->mutable_cache_state()->clear_md5();
378 error = storage_->PutEntry(entry);
[email protected]996139412014-05-10 06:19:50379 if (error != FILE_ERROR_OK)
380 return error;
[email protected]b568b882013-06-10 04:38:07381
[email protected]8b03ab3a2014-01-15 17:52:45382 write_opened_files_[id]++;
383 file_closer->reset(new base::ScopedClosureRunner(
[email protected]c929c932014-07-23 06:06:05384 base::Bind(&google_apis::RunTaskWithTaskRunner,
[email protected]8b03ab3a2014-01-15 17:52:45385 blocking_task_runner_,
386 base::Bind(&FileCache::CloseForWrite,
387 weak_ptr_factory_.GetWeakPtr(),
388 id))));
389 return FILE_ERROR_OK;
390}
391
392bool FileCache::IsOpenedForWrite(const std::string& id) {
393 AssertOnSequencedWorkerPool();
lukasza81be9752015-06-17 00:14:35394 return write_opened_files_.count(id) != 0;
[email protected]b568b882013-06-10 04:38:07395}
396
[email protected]b1bf19a2014-01-21 04:45:19397FileError FileCache::UpdateMd5(const std::string& id) {
[email protected]fcf8eafe02013-05-28 11:15:39398 AssertOnSequencedWorkerPool();
[email protected]eca3fc92013-05-01 03:53:40399
[email protected]b1bf19a2014-01-21 04:45:19400 if (IsOpenedForWrite(id))
401 return FILE_ERROR_IN_USE;
402
[email protected]cd8fd37f2014-05-20 15:45:21403 ResourceEntry entry;
404 FileError error = storage_->GetEntry(id, &entry);
[email protected]996139412014-05-10 06:19:50405 if (error != FILE_ERROR_OK)
406 return error;
[email protected]cd8fd37f2014-05-20 15:45:21407 if (!entry.file_specific_info().cache_state().is_present())
[email protected]b1bf19a2014-01-21 04:45:19408 return FILE_ERROR_NOT_FOUND;
409
hashimoto246e4a82015-04-17 07:44:49410 const std::string& md5 =
411 util::GetMd5Digest(GetCacheFilePath(id), &in_shutdown_);
412 if (in_shutdown_.IsSet())
413 return FILE_ERROR_ABORT;
[email protected]b1bf19a2014-01-21 04:45:19414 if (md5.empty())
415 return FILE_ERROR_NOT_FOUND;
416
[email protected]cd8fd37f2014-05-20 15:45:21417 entry.mutable_file_specific_info()->mutable_cache_state()->set_md5(md5);
418 return storage_->PutEntry(entry);
[email protected]b1bf19a2014-01-21 04:45:19419}
420
421FileError FileCache::ClearDirty(const std::string& id) {
422 AssertOnSequencedWorkerPool();
423
424 if (IsOpenedForWrite(id))
425 return FILE_ERROR_IN_USE;
426
[email protected]fcf8eafe02013-05-28 11:15:39427 // Clearing a dirty file means its entry and actual file blob must exist in
428 // cache.
[email protected]cd8fd37f2014-05-20 15:45:21429 ResourceEntry entry;
430 FileError error = storage_->GetEntry(id, &entry);
[email protected]996139412014-05-10 06:19:50431 if (error != FILE_ERROR_OK)
432 return error;
[email protected]cd8fd37f2014-05-20 15:45:21433 if (!entry.file_specific_info().cache_state().is_present()) {
[email protected]fcf8eafe02013-05-28 11:15:39434 LOG(WARNING) << "Can't clear dirty state of a file that wasn't cached: "
[email protected]c9e4738d2013-08-26 03:04:07435 << id;
[email protected]fcf8eafe02013-05-28 11:15:39436 return FILE_ERROR_NOT_FOUND;
437 }
438
[email protected]8b03ab3a2014-01-15 17:52:45439 // If a file is not dirty (it should have been marked dirty via OpenForWrite),
440 // clearing its dirty state is an invalid operation.
[email protected]cd8fd37f2014-05-20 15:45:21441 if (!entry.file_specific_info().cache_state().is_dirty()) {
[email protected]c9e4738d2013-08-26 03:04:07442 LOG(WARNING) << "Can't clear dirty state of a non-dirty file: " << id;
[email protected]fcf8eafe02013-05-28 11:15:39443 return FILE_ERROR_INVALID_OPERATION;
444 }
445
[email protected]cd8fd37f2014-05-20 15:45:21446 entry.mutable_file_specific_info()->mutable_cache_state()->set_is_dirty(
447 false);
448 return storage_->PutEntry(entry);
[email protected]73f9c742012-06-15 07:37:13449}
450
[email protected]c9e4738d2013-08-26 03:04:07451FileError FileCache::Remove(const std::string& id) {
[email protected]3361a542013-05-22 17:38:27452 AssertOnSequencedWorkerPool();
453
[email protected]cd8fd37f2014-05-20 15:45:21454 ResourceEntry entry;
[email protected]3361a542013-05-22 17:38:27455
[email protected]4b60a25f2013-06-17 09:43:11456 // If entry doesn't exist, nothing to do.
[email protected]cd8fd37f2014-05-20 15:45:21457 FileError error = storage_->GetEntry(id, &entry);
[email protected]996139412014-05-10 06:19:50458 if (error == FILE_ERROR_NOT_FOUND)
[email protected]3361a542013-05-22 17:38:27459 return FILE_ERROR_OK;
[email protected]996139412014-05-10 06:19:50460 if (error != FILE_ERROR_OK)
461 return error;
[email protected]cd8fd37f2014-05-20 15:45:21462 if (!entry.file_specific_info().has_cache_state())
463 return FILE_ERROR_OK;
[email protected]3361a542013-05-22 17:38:27464
[email protected]d1ad8fa2013-07-11 13:23:20465 // Cannot delete a mounted file.
[email protected]c9e4738d2013-08-26 03:04:07466 if (mounted_files_.count(id))
[email protected]4b60a25f2013-06-17 09:43:11467 return FILE_ERROR_IN_USE;
468
[email protected]91a464e62013-07-10 09:30:06469 // Delete the file.
[email protected]c9e4738d2013-08-26 03:04:07470 base::FilePath path = GetCacheFilePath(id);
[email protected]dd3aa792013-07-16 19:10:23471 if (!base::DeleteFile(path, false /* recursive */))
[email protected]91a464e62013-07-10 09:30:06472 return FILE_ERROR_FAILED;
[email protected]3361a542013-05-22 17:38:27473
474 // Now that all file operations have completed, remove from metadata.
[email protected]cd8fd37f2014-05-20 15:45:21475 entry.mutable_file_specific_info()->clear_cache_state();
476 return storage_->PutEntry(entry);
[email protected]3361a542013-05-22 17:38:27477}
478
[email protected]823ca9712013-09-13 10:09:09479bool FileCache::ClearAll() {
480 AssertOnSequencedWorkerPool();
[email protected]f861b392012-08-03 20:41:12481
[email protected]823ca9712013-09-13 10:09:09482 // Remove files.
483 base::FileEnumerator enumerator(cache_file_directory_,
484 false, // not recursive
485 base::FileEnumerator::FILES);
486 for (base::FilePath file = enumerator.Next(); !file.empty();
487 file = enumerator.Next())
488 base::DeleteFile(file, false /* recursive */);
489
490 return true;
[email protected]f861b392012-08-03 20:41:12491}
492
[email protected]34a1bbf32013-06-17 07:24:02493bool FileCache::Initialize() {
[email protected]ca5f6da2012-06-18 12:54:59494 AssertOnSequencedWorkerPool();
495
[email protected]b1bf19a2014-01-21 04:45:19496 // Older versions do not clear MD5 when marking entries dirty.
497 // Clear MD5 of all dirty entries to deal with old data.
dchengf42750232016-04-12 04:12:27498 std::unique_ptr<ResourceMetadataStorage::Iterator> it =
499 storage_->GetIterator();
[email protected]b1bf19a2014-01-21 04:45:19500 for (; !it->IsAtEnd(); it->Advance()) {
[email protected]cd8fd37f2014-05-20 15:45:21501 if (it->GetValue().file_specific_info().cache_state().is_dirty()) {
502 ResourceEntry new_entry(it->GetValue());
503 new_entry.mutable_file_specific_info()->mutable_cache_state()->
504 clear_md5();
505 if (storage_->PutEntry(new_entry) != FILE_ERROR_OK)
[email protected]b1bf19a2014-01-21 04:45:19506 return false;
507 }
508 }
[email protected]996139412014-05-10 06:19:50509 if (it->HasError())
510 return false;
[email protected]b1bf19a2014-01-21 04:45:19511
[email protected]f2731d12013-10-22 03:23:15512 if (!RenameCacheFilesToNewFormat())
513 return false;
[email protected]e8842b192013-06-11 04:05:14514 return true;
[email protected]ca5f6da2012-06-18 12:54:59515}
516
[email protected]34a1bbf32013-06-17 07:24:02517void FileCache::Destroy() {
lukasza037c10b12015-06-12 04:21:25518 DCHECK(thread_checker_.CalledOnValidThread());
[email protected]34a1bbf32013-06-17 07:24:02519
hashimoto246e4a82015-04-17 07:44:49520 in_shutdown_.Set();
521
[email protected]34a1bbf32013-06-17 07:24:02522 // Destroy myself on the blocking pool.
523 // Note that base::DeletePointer<> cannot be used as the destructor of this
524 // class is private.
525 blocking_task_runner_->PostTask(
526 FROM_HERE,
527 base::Bind(&FileCache::DestroyOnBlockingPool, base::Unretained(this)));
528}
529
[email protected]eca3fc92013-05-01 03:53:40530void FileCache::DestroyOnBlockingPool() {
[email protected]73f9c742012-06-15 07:37:13531 AssertOnSequencedWorkerPool();
532 delete this;
533}
534
[email protected]b7af4f12013-10-31 06:57:45535bool FileCache::RecoverFilesFromCacheDirectory(
[email protected]760abc32013-11-01 05:13:01536 const base::FilePath& dest_directory,
[email protected]026d4a522013-11-05 14:22:18537 const ResourceMetadataStorage::RecoveredCacheInfoMap&
538 recovered_cache_info) {
[email protected]b7af4f12013-10-31 06:57:45539 int file_number = 1;
540
541 base::FileEnumerator enumerator(cache_file_directory_,
542 false, // not recursive
543 base::FileEnumerator::FILES);
544 for (base::FilePath current = enumerator.Next(); !current.empty();
545 current = enumerator.Next()) {
546 const std::string& id = GetIdFromPath(current);
[email protected]cd8fd37f2014-05-20 15:45:21547 ResourceEntry entry;
548 FileError error = storage_->GetEntry(id, &entry);
549 if (error != FILE_ERROR_OK && error != FILE_ERROR_NOT_FOUND)
550 return false;
551 if (error == FILE_ERROR_OK &&
552 entry.file_specific_info().cache_state().is_present()) {
[email protected]b7af4f12013-10-31 06:57:45553 // This file is managed by FileCache, no need to recover it.
554 continue;
555 }
556
[email protected]760abc32013-11-01 05:13:01557 // If a cache entry which is non-dirty and has matching MD5 is found in
558 // |recovered_cache_entries|, it means the current file is already uploaded
559 // to the server. Just delete it instead of recovering it.
[email protected]026d4a522013-11-05 14:22:18560 ResourceMetadataStorage::RecoveredCacheInfoMap::const_iterator it =
561 recovered_cache_info.find(id);
562 if (it != recovered_cache_info.end()) {
563 // Due to the DB corruption, cache info might be recovered from old
564 // revision. Perform MD5 check even when is_dirty is false just in case.
565 if (!it->second.is_dirty &&
hashimoto246e4a82015-04-17 07:44:49566 it->second.md5 == util::GetMd5Digest(current, &in_shutdown_)) {
[email protected]760abc32013-11-01 05:13:01567 base::DeleteFile(current, false /* recursive */);
568 continue;
569 }
570 }
571
[email protected]b7af4f12013-10-31 06:57:45572 // Read file contents to sniff mime type.
573 std::vector<char> content(net::kMaxBytesToSniff);
574 const int read_result =
[email protected]7600d0b2013-12-08 21:43:30575 base::ReadFile(current, &content[0], content.size());
[email protected]b7af4f12013-10-31 06:57:45576 if (read_result < 0) {
577 LOG(WARNING) << "Cannot read: " << current.value();
578 return false;
579 }
580 if (read_result == 0) // Skip empty files.
581 continue;
582
[email protected]026d4a522013-11-05 14:22:18583 // Use recovered file name if available, otherwise decide file name with
584 // sniffed mime type.
[email protected]b7af4f12013-10-31 06:57:45585 base::FilePath dest_base_name(FILE_PATH_LITERAL("file"));
586 std::string mime_type;
[email protected]026d4a522013-11-05 14:22:18587 if (it != recovered_cache_info.end() && !it->second.title.empty()) {
588 // We can use a file name recovered from the trashed DB.
589 dest_base_name = base::FilePath::FromUTF8Unsafe(it->second.title);
590 } else if (net::SniffMimeType(&content[0], read_result,
591 net::FilePathToFileURL(current),
592 std::string(), &mime_type) ||
593 net::SniffMimeTypeFromLocalData(&content[0], read_result,
594 &mime_type)) {
[email protected]b7af4f12013-10-31 06:57:45595 // Change base name for common mime types.
596 if (net::MatchesMimeType("image/*", mime_type)) {
597 dest_base_name = base::FilePath(FILE_PATH_LITERAL("image"));
598 } else if (net::MatchesMimeType("video/*", mime_type)) {
599 dest_base_name = base::FilePath(FILE_PATH_LITERAL("video"));
600 } else if (net::MatchesMimeType("audio/*", mime_type)) {
601 dest_base_name = base::FilePath(FILE_PATH_LITERAL("audio"));
602 }
603
604 // Estimate extension from mime type.
605 std::vector<base::FilePath::StringType> extensions;
606 base::FilePath::StringType extension;
607 if (net::GetPreferredExtensionForMimeType(mime_type, &extension))
608 extensions.push_back(extension);
609 else
610 net::GetExtensionsForMimeType(mime_type, &extensions);
611
612 // Add extension if possible.
613 if (!extensions.empty())
614 dest_base_name = dest_base_name.AddExtension(extensions[0]);
615 }
616
617 // Add file number to the file name and move.
618 const base::FilePath& dest_path = dest_directory.Append(dest_base_name)
619 .InsertBeforeExtensionASCII(base::StringPrintf("%08d", file_number++));
[email protected]426d1c92013-12-03 20:08:54620 if (!base::CreateDirectory(dest_directory) ||
[email protected]b7af4f12013-10-31 06:57:45621 !base::Move(current, dest_path)) {
622 LOG(WARNING) << "Failed to move: " << current.value()
623 << " to " << dest_path.value();
624 return false;
625 }
626 }
[email protected]d105b3ad2013-11-01 05:33:13627 UMA_HISTOGRAM_COUNTS("Drive.NumberOfCacheFilesRecoveredAfterDBCorruption",
628 file_number - 1);
[email protected]b7af4f12013-10-31 06:57:45629 return true;
630}
631
[email protected]54ba37502013-05-09 08:43:40632FileError FileCache::MarkAsUnmounted(const base::FilePath& file_path) {
[email protected]9564c1502012-11-28 12:12:16633 AssertOnSequencedWorkerPool();
[email protected]eca3fc92013-05-01 03:53:40634 DCHECK(IsUnderFileCacheDirectory(file_path));
[email protected]9564c1502012-11-28 12:12:16635
[email protected]c9e4738d2013-08-26 03:04:07636 std::string id = GetIdFromPath(file_path);
[email protected]9564c1502012-11-28 12:12:16637
[email protected]cd8fd37f2014-05-20 15:45:21638 // Get the entry associated with the id.
639 ResourceEntry entry;
640 FileError error = storage_->GetEntry(id, &entry);
[email protected]996139412014-05-10 06:19:50641 if (error != FILE_ERROR_OK)
642 return error;
[email protected]9564c1502012-11-28 12:12:16643
[email protected]c9e4738d2013-08-26 03:04:07644 std::set<std::string>::iterator it = mounted_files_.find(id);
[email protected]4b60a25f2013-06-17 09:43:11645 if (it == mounted_files_.end())
[email protected]78a158b2013-04-23 06:57:49646 return FILE_ERROR_INVALID_OPERATION;
[email protected]9564c1502012-11-28 12:12:16647
[email protected]4b60a25f2013-06-17 09:43:11648 mounted_files_.erase(it);
[email protected]78a158b2013-04-23 06:57:49649 return FILE_ERROR_OK;
[email protected]9564c1502012-11-28 12:12:16650}
651
avibc5337b2015-12-25 23:16:33652int64_t FileCache::GetAvailableSpace() {
653 int64_t free_space = 0;
[email protected]f6fd98a2012-12-14 00:04:02654 if (free_disk_space_getter_)
655 free_space = free_disk_space_getter_->AmountOfFreeDiskSpace();
656 else
yawano4c36b9a02015-10-23 06:17:23657 free_space = base::SysInfo::AmountOfFreeDiskSpace(cache_file_directory_);
[email protected]f6fd98a2012-12-14 00:04:02658
659 // Subtract this as if this portion does not exist.
lukasza3fb22622015-08-27 21:04:34660 free_space -= drive::internal::kMinFreeSpaceInBytes;
yawano4c36b9a02015-10-23 06:17:23661 return free_space;
[email protected]f6fd98a2012-12-14 00:04:02662}
663
[email protected]f2731d12013-10-22 03:23:15664bool FileCache::RenameCacheFilesToNewFormat() {
665 base::FileEnumerator enumerator(cache_file_directory_,
666 false, // not recursive
667 base::FileEnumerator::FILES);
668 for (base::FilePath current = enumerator.Next(); !current.empty();
669 current = enumerator.Next()) {
670 base::FilePath new_path = current.RemoveExtension();
671 if (!new_path.Extension().empty()) {
672 // Delete files with multiple extensions.
673 if (!base::DeleteFile(current, false /* recursive */))
674 return false;
675 continue;
676 }
677 const std::string& id = GetIdFromPath(new_path);
678 new_path = GetCacheFilePath(util::CanonicalizeResourceId(id));
679 if (new_path != current && !base::Move(current, new_path))
680 return false;
[email protected]91a464e62013-07-10 09:30:06681 }
[email protected]f2731d12013-10-22 03:23:15682 return true;
[email protected]91a464e62013-07-10 09:30:06683}
684
yawano9fd1e632016-02-04 09:00:06685// static
686bool FileCache::MigrateCacheFiles(const base::FilePath& from,
yawanob3b718d2016-03-18 06:14:15687 const base::FilePath& to,
yawano9fd1e632016-02-04 09:00:06688 ResourceMetadataStorage* metadata_storage) {
dchengf42750232016-04-12 04:12:27689 std::unique_ptr<ResourceMetadataStorage::Iterator> it =
yawano9fd1e632016-02-04 09:00:06690 metadata_storage->GetIterator();
691 for (; !it->IsAtEnd(); it->Advance()) {
692 const ResourceEntry& entry = it->GetValue();
693 if (!entry.file_specific_info().cache_state().is_present()) {
694 continue;
695 }
696
697 const base::FilePath move_from = GetPathForId(from, entry.local_id());
698 if (!base::PathExists(move_from)) {
699 continue;
700 }
701
yawanob3b718d2016-03-18 06:14:15702 const base::FilePath move_to = GetPathForId(to, entry.local_id());
yawano9fd1e632016-02-04 09:00:06703 if (!base::Move(move_from, move_to)) {
704 return false;
705 }
yawanob3b718d2016-03-18 06:14:15706
707 // TODO(yawano): create hard link if entry is marked as pinned or dirty.
yawano9fd1e632016-02-04 09:00:06708 }
709
710 return true;
711}
712
[email protected]8b03ab3a2014-01-15 17:52:45713void FileCache::CloseForWrite(const std::string& id) {
714 AssertOnSequencedWorkerPool();
715
716 std::map<std::string, int>::iterator it = write_opened_files_.find(id);
717 if (it == write_opened_files_.end())
718 return;
719
720 DCHECK_LT(0, it->second);
721 --it->second;
722 if (it->second == 0)
723 write_opened_files_.erase(it);
[email protected]f92367ae2014-06-02 07:35:43724
725 // Update last modified date.
726 ResourceEntry entry;
727 FileError error = storage_->GetEntry(id, &entry);
728 if (error != FILE_ERROR_OK) {
729 LOG(ERROR) << "Failed to get entry: " << id << ", "
730 << FileErrorToString(error);
731 return;
732 }
733 entry.mutable_file_info()->set_last_modified(
734 base::Time::Now().ToInternalValue());
735 error = storage_->PutEntry(entry);
736 if (error != FILE_ERROR_OK) {
737 LOG(ERROR) << "Failed to put entry: " << id << ", "
738 << FileErrorToString(error);
739 }
[email protected]8b03ab3a2014-01-15 17:52:45740}
741
yawano8578abf2015-08-26 09:15:50742bool FileCache::IsEvictable(const std::string& id, const ResourceEntry& entry) {
743 return entry.file_specific_info().has_cache_state() &&
744 !entry.file_specific_info().cache_state().is_pinned() &&
745 !entry.file_specific_info().cache_state().is_dirty() &&
746 !mounted_files_.count(id);
747}
748
[email protected]59c7cdec2013-05-07 04:17:13749} // namespace internal
[email protected]d9d04df2012-10-12 07:06:35750} // namespace drive