blob: 96d963c402d4b274c21fe23485492ce88ff542f8 [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
[email protected]eca3fc92013-05-01 03:53:405#include "chrome/browser/chromeos/drive/file_cache.h"
[email protected]3653146a2012-05-29 13:41:476
7#include <vector>
8
[email protected]a321b9632012-06-14 03:29:179#include "base/file_util.h"
[email protected]25a4c1c2013-06-08 04:53:3610#include "base/files/file_enumerator.h"
[email protected]3653146a2012-05-29 13:41:4711#include "base/logging.h"
[email protected]3653146a2012-05-29 13:41:4712#include "base/string_util.h"
[email protected]c2b557732013-04-24 01:46:3413#include "base/stringprintf.h"
[email protected]a321b9632012-06-14 03:29:1714#include "base/sys_info.h"
[email protected]bdd947c2012-11-06 04:35:3415#include "base/task_runner_util.h"
[email protected]15de8142012-10-11 06:00:5416#include "chrome/browser/chromeos/drive/drive.pb.h"
[email protected]aa7365c2013-05-01 05:50:4717#include "chrome/browser/chromeos/drive/file_cache_metadata.h"
18#include "chrome/browser/chromeos/drive/file_cache_observer.h"
[email protected]4fa2fd5d2013-04-26 03:42:5219#include "chrome/browser/chromeos/drive/file_system_util.h"
[email protected]d68ede02012-11-07 14:30:5320#include "chrome/browser/google_apis/task_util.h"
[email protected]e8f3f9982013-05-10 20:59:5321#include "chromeos/chromeos_constants.h"
[email protected]7986b8d2012-06-14 15:05:1422#include "content/public/browser/browser_thread.h"
23
24using content::BrowserThread;
[email protected]3653146a2012-05-29 13:41:4725
[email protected]d9d04df2012-10-12 07:06:3526namespace drive {
[email protected]59c7cdec2013-05-07 04:17:1327namespace internal {
[email protected]3653146a2012-05-29 13:41:4728namespace {
29
[email protected]eca3fc92013-05-01 03:53:4030const base::FilePath::CharType kFileCacheMetaDir[] = FILE_PATH_LITERAL("meta");
[email protected]eca3fc92013-05-01 03:53:4031const base::FilePath::CharType kFileCachePersistentDir[] =
[email protected]32a7fc852012-06-08 17:25:5032 FILE_PATH_LITERAL("persistent");
[email protected]eca3fc92013-05-01 03:53:4033const base::FilePath::CharType kFileCacheTmpDir[] = FILE_PATH_LITERAL("tmp");
34const base::FilePath::CharType kFileCacheTmpDownloadsDir[] =
[email protected]32a7fc852012-06-08 17:25:5035 FILE_PATH_LITERAL("tmp/downloads");
[email protected]eca3fc92013-05-01 03:53:4036const base::FilePath::CharType kFileCacheTmpDocumentsDir[] =
[email protected]32a7fc852012-06-08 17:25:5037 FILE_PATH_LITERAL("tmp/documents");
38
[email protected]79c3752d2012-07-17 12:10:0839// Create cache directory paths and set permissions.
[email protected]650b2d52013-02-10 03:41:4540bool InitCachePaths(const std::vector<base::FilePath>& cache_paths) {
[email protected]eca3fc92013-05-01 03:53:4041 if (cache_paths.size() < FileCache::NUM_CACHE_TYPES) {
[email protected]79c3752d2012-07-17 12:10:0842 NOTREACHED();
43 LOG(ERROR) << "Size of cache_paths is invalid.";
[email protected]322e0032012-10-07 01:55:5344 return false;
[email protected]79c3752d2012-07-17 12:10:0845 }
46
[email protected]eca3fc92013-05-01 03:53:4047 if (!FileCache::CreateCacheDirectories(cache_paths))
[email protected]322e0032012-10-07 01:55:5348 return false;
[email protected]79c3752d2012-07-17 12:10:0849
50 // Change permissions of cache persistent directory to u+rwx,og+x (711) in
51 // order to allow archive files in that directory to be mounted by cros-disks.
52 file_util::SetPosixFilePermissions(
[email protected]eca3fc92013-05-01 03:53:4053 cache_paths[FileCache::CACHE_TYPE_PERSISTENT],
[email protected]79c3752d2012-07-17 12:10:0854 file_util::FILE_PERMISSION_USER_MASK |
55 file_util::FILE_PERMISSION_EXECUTE_BY_GROUP |
56 file_util::FILE_PERMISSION_EXECUTE_BY_OTHERS);
[email protected]322e0032012-10-07 01:55:5357
58 return true;
[email protected]79c3752d2012-07-17 12:10:0859}
60
[email protected]a321b9632012-06-14 03:29:1761// Remove all files under the given directory, non-recursively.
[email protected]d7754f52012-08-01 08:45:3362// Do not remove recursively as we don't want to touch <gcache>/tmp/downloads,
[email protected]a321b9632012-06-14 03:29:1763// which is used for user initiated downloads like "Save As"
[email protected]650b2d52013-02-10 03:41:4564void RemoveAllFiles(const base::FilePath& directory) {
[email protected]25a4c1c2013-06-08 04:53:3665 base::FileEnumerator enumerator(directory, false /* recursive */,
66 base::FileEnumerator::FILES);
[email protected]650b2d52013-02-10 03:41:4567 for (base::FilePath file_path = enumerator.Next(); !file_path.empty();
[email protected]a321b9632012-06-14 03:29:1768 file_path = enumerator.Next()) {
69 DVLOG(1) << "Removing " << file_path.value();
70 if (!file_util::Delete(file_path, false /* recursive */))
71 LOG(WARNING) << "Failed to delete " << file_path.value();
72 }
73}
74
[email protected]c80ca12f2012-11-09 10:56:0375// Moves the file.
[email protected]650b2d52013-02-10 03:41:4576bool MoveFile(const base::FilePath& source_path,
77 const base::FilePath& dest_path) {
[email protected]c80ca12f2012-11-09 10:56:0378 if (!file_util::Move(source_path, dest_path)) {
79 LOG(ERROR) << "Failed to move " << source_path.value()
80 << " to " << dest_path.value();
81 return false;
82 }
83 DVLOG(1) << "Moved " << source_path.value() << " to " << dest_path.value();
84 return true;
85}
86
87// Copies the file.
[email protected]650b2d52013-02-10 03:41:4588bool CopyFile(const base::FilePath& source_path,
89 const base::FilePath& dest_path) {
[email protected]c80ca12f2012-11-09 10:56:0390 if (!file_util::CopyFile(source_path, dest_path)) {
91 LOG(ERROR) << "Failed to copy " << source_path.value()
92 << " to " << dest_path.value();
93 return false;
94 }
95 DVLOG(1) << "Copied " << source_path.value() << " to " << dest_path.value();
96 return true;
[email protected]a321b9632012-06-14 03:29:1797}
98
99// Deletes all files that match |path_to_delete_pattern| except for
100// |path_to_keep| on blocking pool.
101// If |path_to_keep| is empty, all files in |path_to_delete_pattern| are
102// deleted.
[email protected]650b2d52013-02-10 03:41:45103void DeleteFilesSelectively(const base::FilePath& path_to_delete_pattern,
104 const base::FilePath& path_to_keep) {
[email protected]a321b9632012-06-14 03:29:17105 // Enumerate all files in directory of |path_to_delete_pattern| that match
106 // base name of |path_to_delete_pattern|.
107 // If a file is not |path_to_keep|, delete it.
108 bool success = true;
[email protected]25a4c1c2013-06-08 04:53:36109 base::FileEnumerator enumerator(
[email protected]9e66a9b2013-05-08 05:46:20110 path_to_delete_pattern.DirName(),
[email protected]a321b9632012-06-14 03:29:17111 false, // not recursive
[email protected]25a4c1c2013-06-08 04:53:36112 base::FileEnumerator::FILES,
[email protected]a321b9632012-06-14 03:29:17113 path_to_delete_pattern.BaseName().value());
[email protected]650b2d52013-02-10 03:41:45114 for (base::FilePath current = enumerator.Next(); !current.empty();
[email protected]a321b9632012-06-14 03:29:17115 current = enumerator.Next()) {
116 // If |path_to_keep| is not empty and same as current, don't delete it.
117 if (!path_to_keep.empty() && current == path_to_keep)
118 continue;
119
[email protected]0b8d4cee2012-07-02 20:46:26120 success = file_util::Delete(current, false);
[email protected]a321b9632012-06-14 03:29:17121 if (!success)
122 DVLOG(1) << "Error deleting " << current.value();
123 else
124 DVLOG(1) << "Deleted " << current.value();
125 }
126}
127
[email protected]7986b8d2012-06-14 15:05:14128// Runs callback with pointers dereferenced.
[email protected]9564c1502012-11-28 12:12:16129// Used to implement GetFile, MarkAsMounted.
[email protected]bdd947c2012-11-06 04:35:34130void RunGetFileFromCacheCallback(
131 const GetFileFromCacheCallback& callback,
[email protected]7197485b2013-05-23 15:59:34132 base::FilePath* file_path,
133 FileError error) {
[email protected]c960d222012-06-15 10:03:50134 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]9564c1502012-11-28 12:12:16135 DCHECK(!callback.is_null());
[email protected]7197485b2013-05-23 15:59:34136 DCHECK(file_path);
[email protected]c960d222012-06-15 10:03:50137
[email protected]7197485b2013-05-23 15:59:34138 callback.Run(error, *file_path);
[email protected]c960d222012-06-15 10:03:50139}
140
[email protected]8764a392012-06-20 06:43:08141// Runs callback with pointers dereferenced.
[email protected]77fb1a62012-11-01 13:42:32142// Used to implement GetCacheEntry().
143void RunGetCacheEntryCallback(const GetCacheEntryCallback& callback,
[email protected]aa7365c2013-05-01 05:50:47144 FileCacheEntry* cache_entry,
[email protected]bdd947c2012-11-06 04:35:34145 bool success) {
[email protected]4324fdc2012-06-29 05:32:48146 DCHECK(cache_entry);
[email protected]bdd947c2012-11-06 04:35:34147 DCHECK(!callback.is_null());
148 callback.Run(success, *cache_entry);
[email protected]322e0032012-10-07 01:55:53149}
150
[email protected]a321b9632012-06-14 03:29:17151} // namespace
[email protected]32a7fc852012-06-08 17:25:50152
[email protected]eca3fc92013-05-01 03:53:40153FileCache::FileCache(const base::FilePath& cache_root_path,
154 base::SequencedTaskRunner* blocking_task_runner,
155 FreeDiskSpaceGetterInterface* free_disk_space_getter)
[email protected]01ba15f72012-06-09 00:41:05156 : cache_root_path_(cache_root_path),
[email protected]6b70c7b2012-06-14 03:10:43157 cache_paths_(GetCachePaths(cache_root_path_)),
[email protected]ddbf2052012-07-13 15:07:02158 blocking_task_runner_(blocking_task_runner),
[email protected]f6fd98a2012-12-14 00:04:02159 free_disk_space_getter_(free_disk_space_getter),
[email protected]9c009092013-05-01 03:14:09160 weak_ptr_factory_(this) {
[email protected]17196ee2012-12-13 06:23:51161 DCHECK(blocking_task_runner_);
[email protected]73f9c742012-06-15 07:37:13162 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]3653146a2012-05-29 13:41:47163}
164
[email protected]eca3fc92013-05-01 03:53:40165FileCache::~FileCache() {
[email protected]17196ee2012-12-13 06:23:51166 // Must be on the sequenced worker pool, as |metadata_| must be deleted on
167 // the sequenced worker pool.
[email protected]73f9c742012-06-15 07:37:13168 AssertOnSequencedWorkerPool();
[email protected]3653146a2012-05-29 13:41:47169}
170
[email protected]eca3fc92013-05-01 03:53:40171base::FilePath FileCache::GetCacheDirectoryPath(
[email protected]32a7fc852012-06-08 17:25:50172 CacheSubDirectoryType sub_dir_type) const {
173 DCHECK_LE(0, sub_dir_type);
174 DCHECK_GT(NUM_CACHE_TYPES, sub_dir_type);
175 return cache_paths_[sub_dir_type];
176}
177
[email protected]eca3fc92013-05-01 03:53:40178base::FilePath FileCache::GetCacheFilePath(
[email protected]650b2d52013-02-10 03:41:45179 const std::string& resource_id,
180 const std::string& md5,
181 CacheSubDirectoryType sub_dir_type,
182 CachedFileOrigin file_origin) const {
[email protected]32a7fc852012-06-08 17:25:50183 DCHECK(sub_dir_type != CACHE_TYPE_META);
184
185 // Runs on any thread.
186 // Filename is formatted as resource_id.md5, i.e. resource_id is the base
187 // name and md5 is the extension.
188 std::string base_name = util::EscapeCacheFileName(resource_id);
189 if (file_origin == CACHED_FILE_LOCALLY_MODIFIED) {
190 DCHECK(sub_dir_type == CACHE_TYPE_PERSISTENT);
[email protected]650b2d52013-02-10 03:41:45191 base_name += base::FilePath::kExtensionSeparator;
[email protected]b83e5202012-06-27 07:50:24192 base_name += util::kLocallyModifiedFileExtension;
[email protected]32a7fc852012-06-08 17:25:50193 } else if (!md5.empty()) {
[email protected]650b2d52013-02-10 03:41:45194 base_name += base::FilePath::kExtensionSeparator;
[email protected]32a7fc852012-06-08 17:25:50195 base_name += util::EscapeCacheFileName(md5);
196 }
197 // For mounted archives the filename is formatted as resource_id.md5.mounted,
198 // i.e. resource_id.md5 is the base name and ".mounted" is the extension
199 if (file_origin == CACHED_FILE_MOUNTED) {
[email protected]a321b9632012-06-14 03:29:17200 DCHECK(sub_dir_type == CACHE_TYPE_PERSISTENT);
[email protected]650b2d52013-02-10 03:41:45201 base_name += base::FilePath::kExtensionSeparator;
[email protected]ca5f6da2012-06-18 12:54:59202 base_name += util::kMountedArchiveFileExtension;
[email protected]32a7fc852012-06-08 17:25:50203 }
[email protected]2333b2a12013-04-26 07:22:22204 return GetCacheDirectoryPath(sub_dir_type).Append(
205 base::FilePath::FromUTF8Unsafe(base_name));
[email protected]32a7fc852012-06-08 17:25:50206}
207
[email protected]eca3fc92013-05-01 03:53:40208void FileCache::AssertOnSequencedWorkerPool() {
[email protected]ddbf2052012-07-13 15:07:02209 DCHECK(!blocking_task_runner_ ||
210 blocking_task_runner_->RunsTasksOnCurrentThread());
[email protected]fcc92a52012-06-08 22:54:16211}
212
[email protected]eca3fc92013-05-01 03:53:40213bool FileCache::IsUnderFileCacheDirectory(const base::FilePath& path) const {
[email protected]01ba15f72012-06-09 00:41:05214 return cache_root_path_ == path || cache_root_path_.IsParent(path);
215}
216
[email protected]aa7365c2013-05-01 05:50:47217void FileCache::AddObserver(FileCacheObserver* observer) {
[email protected]73f9c742012-06-15 07:37:13218 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
219 observers_.AddObserver(observer);
220}
221
[email protected]aa7365c2013-05-01 05:50:47222void FileCache::RemoveObserver(FileCacheObserver* observer) {
[email protected]73f9c742012-06-15 07:37:13223 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
224 observers_.RemoveObserver(observer);
225}
226
[email protected]54ba37502013-05-09 08:43:40227void FileCache::GetCacheEntryOnUIThread(const std::string& resource_id,
228 const std::string& md5,
229 const GetCacheEntryCallback& callback) {
[email protected]4324fdc2012-06-29 05:32:48230 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]bdd947c2012-11-06 04:35:34231 DCHECK(!callback.is_null());
[email protected]4324fdc2012-06-29 05:32:48232
[email protected]aa7365c2013-05-01 05:50:47233 FileCacheEntry* cache_entry = new FileCacheEntry;
[email protected]bdd947c2012-11-06 04:35:34234 base::PostTaskAndReplyWithResult(
235 blocking_task_runner_,
[email protected]4324fdc2012-06-29 05:32:48236 FROM_HERE,
[email protected]54ba37502013-05-09 08:43:40237 base::Bind(&FileCache::GetCacheEntry,
[email protected]3f5e2902012-11-06 08:21:15238 base::Unretained(this), resource_id, md5, cache_entry),
[email protected]4324fdc2012-06-29 05:32:48239 base::Bind(&RunGetCacheEntryCallback,
[email protected]3f5e2902012-11-06 08:21:15240 callback, base::Owned(cache_entry)));
[email protected]4324fdc2012-06-29 05:32:48241}
242
[email protected]8e38c9db2013-05-10 02:52:29243bool FileCache::GetCacheEntry(const std::string& resource_id,
244 const std::string& md5,
245 FileCacheEntry* entry) {
246 DCHECK(entry);
247 AssertOnSequencedWorkerPool();
248 return metadata_->GetCacheEntry(resource_id, md5, entry);
249}
250
[email protected]54ba37502013-05-09 08:43:40251void FileCache::IterateOnUIThread(
252 const CacheIterateCallback& iteration_callback,
253 const base::Closure& completion_callback) {
[email protected]8764a392012-06-20 06:43:08254 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]d68ede02012-11-07 14:30:53255 DCHECK(!iteration_callback.is_null());
256 DCHECK(!completion_callback.is_null());
[email protected]8764a392012-06-20 06:43:08257
[email protected]ddbf2052012-07-13 15:07:02258 blocking_task_runner_->PostTaskAndReply(
[email protected]8764a392012-06-20 06:43:08259 FROM_HERE,
[email protected]54ba37502013-05-09 08:43:40260 base::Bind(&FileCache::Iterate,
[email protected]d68ede02012-11-07 14:30:53261 base::Unretained(this),
262 google_apis::CreateRelayCallback(iteration_callback)),
263 completion_callback);
[email protected]cd236432012-07-27 18:03:30264}
265
[email protected]3361a542013-05-22 17:38:27266void FileCache::Iterate(const CacheIterateCallback& iteration_callback) {
267 AssertOnSequencedWorkerPool();
268 DCHECK(!iteration_callback.is_null());
269
[email protected]2f0bf592013-06-05 08:07:23270 scoped_ptr<FileCacheMetadata::Iterator> it = metadata_->GetIterator();
271 for (; !it->IsAtEnd(); it->Advance())
272 iteration_callback.Run(it->GetKey(), it->GetValue());
273 DCHECK(!it->HasError());
[email protected]3361a542013-05-22 17:38:27274}
275
[email protected]54ba37502013-05-09 08:43:40276void FileCache::FreeDiskSpaceIfNeededForOnUIThread(
[email protected]f423c0b2012-11-22 09:51:10277 int64 num_bytes,
278 const InitializeCacheCallback& callback) {
279 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
280 DCHECK(!callback.is_null());
[email protected]a321b9632012-06-14 03:29:17281
[email protected]f423c0b2012-11-22 09:51:10282 base::PostTaskAndReplyWithResult(
283 blocking_task_runner_,
284 FROM_HERE,
[email protected]54ba37502013-05-09 08:43:40285 base::Bind(&FileCache::FreeDiskSpaceIfNeededFor,
[email protected]f423c0b2012-11-22 09:51:10286 base::Unretained(this),
287 num_bytes),
288 callback);
[email protected]a321b9632012-06-14 03:29:17289}
290
[email protected]ec514362013-05-27 17:52:22291bool FileCache::FreeDiskSpaceIfNeededFor(int64 num_bytes) {
292 AssertOnSequencedWorkerPool();
293
294 // Do nothing and return if we have enough space.
295 if (HasEnoughSpaceFor(num_bytes, cache_root_path_))
296 return true;
297
298 // Otherwise, try to free up the disk space.
299 DVLOG(1) << "Freeing up disk space for " << num_bytes;
[email protected]f8b1a532013-06-06 08:35:08300
[email protected]ec514362013-05-27 17:52:22301 // First remove temporary files from the metadata.
[email protected]f8b1a532013-06-06 08:35:08302 scoped_ptr<FileCacheMetadata::Iterator> it = metadata_->GetIterator();
303 for (; !it->IsAtEnd(); it->Advance()) {
304 if (!it->GetValue().is_persistent())
305 metadata_->RemoveCacheEntry(it->GetKey());
306 }
307 DCHECK(!it->HasError());
308
[email protected]ec514362013-05-27 17:52:22309 // Then remove all files under "tmp" directory.
310 RemoveAllFiles(GetCacheDirectoryPath(CACHE_TYPE_TMP));
311
312 // Check the disk space again.
313 return HasEnoughSpaceFor(num_bytes, cache_root_path_);
314}
315
[email protected]54ba37502013-05-09 08:43:40316void FileCache::GetFileOnUIThread(const std::string& resource_id,
317 const std::string& md5,
318 const GetFileFromCacheCallback& callback) {
[email protected]c960d222012-06-15 10:03:50319 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]bdd947c2012-11-06 04:35:34320 DCHECK(!callback.is_null());
[email protected]a321b9632012-06-14 03:29:17321
[email protected]7197485b2013-05-23 15:59:34322 base::FilePath* cache_file_path = new base::FilePath;
[email protected]bdd947c2012-11-06 04:35:34323 base::PostTaskAndReplyWithResult(
324 blocking_task_runner_,
[email protected]c960d222012-06-15 10:03:50325 FROM_HERE,
[email protected]54ba37502013-05-09 08:43:40326 base::Bind(&FileCache::GetFile,
[email protected]7197485b2013-05-23 15:59:34327 base::Unretained(this), resource_id, md5, cache_file_path),
328 base::Bind(&RunGetFileFromCacheCallback,
329 callback, base::Owned(cache_file_path)));
[email protected]a321b9632012-06-14 03:29:17330}
331
[email protected]ec514362013-05-27 17:52:22332FileError FileCache::GetFile(const std::string& resource_id,
333 const std::string& md5,
334 base::FilePath* cache_file_path) {
335 AssertOnSequencedWorkerPool();
336 DCHECK(cache_file_path);
337
338 FileCacheEntry cache_entry;
339 if (!GetCacheEntry(resource_id, md5, &cache_entry) ||
340 !cache_entry.is_present())
341 return FILE_ERROR_NOT_FOUND;
342
343 CachedFileOrigin file_origin;
344 if (cache_entry.is_mounted()) {
345 file_origin = CACHED_FILE_MOUNTED;
346 } else if (cache_entry.is_dirty()) {
347 file_origin = CACHED_FILE_LOCALLY_MODIFIED;
348 } else {
349 file_origin = CACHED_FILE_FROM_SERVER;
350 }
351
352 *cache_file_path = GetCacheFilePath(resource_id,
[email protected]b568b882013-06-10 04:38:07353 cache_entry.md5(),
[email protected]ec514362013-05-27 17:52:22354 GetSubDirectoryType(cache_entry),
355 file_origin);
356 return FILE_ERROR_OK;
357}
358
[email protected]54ba37502013-05-09 08:43:40359void FileCache::StoreOnUIThread(const std::string& resource_id,
360 const std::string& md5,
361 const base::FilePath& source_path,
362 FileOperationType file_operation_type,
363 const FileOperationCallback& callback) {
[email protected]73f9c742012-06-15 07:37:13364 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]2a2c4152012-11-26 11:34:50365 DCHECK(!callback.is_null());
[email protected]73f9c742012-06-15 07:37:13366
[email protected]bdd947c2012-11-06 04:35:34367 base::PostTaskAndReplyWithResult(
368 blocking_task_runner_,
[email protected]73f9c742012-06-15 07:37:13369 FROM_HERE,
[email protected]54ba37502013-05-09 08:43:40370 base::Bind(&FileCache::Store,
[email protected]73f9c742012-06-15 07:37:13371 base::Unretained(this),
[email protected]82c4eb92013-05-21 11:25:23372 resource_id, md5, source_path, file_operation_type),
[email protected]2a2c4152012-11-26 11:34:50373 callback);
[email protected]73f9c742012-06-15 07:37:13374}
375
[email protected]82c4eb92013-05-21 11:25:23376FileError FileCache::Store(const std::string& resource_id,
377 const std::string& md5,
378 const base::FilePath& source_path,
379 FileOperationType file_operation_type) {
380 AssertOnSequencedWorkerPool();
381 return StoreInternal(resource_id, md5, source_path, file_operation_type,
382 CACHED_FILE_FROM_SERVER);
383}
384
[email protected]54ba37502013-05-09 08:43:40385void FileCache::StoreLocallyModifiedOnUIThread(
[email protected]d8546c92013-05-02 05:09:59386 const std::string& resource_id,
387 const std::string& md5,
388 const base::FilePath& source_path,
389 FileOperationType file_operation_type,
390 const FileOperationCallback& callback) {
391 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
392 DCHECK(!callback.is_null());
393
394 base::PostTaskAndReplyWithResult(
395 blocking_task_runner_,
396 FROM_HERE,
[email protected]82c4eb92013-05-21 11:25:23397 base::Bind(&FileCache::StoreInternal,
[email protected]d8546c92013-05-02 05:09:59398 base::Unretained(this),
399 resource_id, md5, source_path, file_operation_type,
400 CACHED_FILE_LOCALLY_MODIFIED),
401 base::Bind(&FileCache::OnCommitDirty,
402 weak_ptr_factory_.GetWeakPtr(), resource_id, callback));
403}
404
[email protected]54ba37502013-05-09 08:43:40405void FileCache::PinOnUIThread(const std::string& resource_id,
406 const std::string& md5,
407 const FileOperationCallback& callback) {
[email protected]73f9c742012-06-15 07:37:13408 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]2a2c4152012-11-26 11:34:50409 DCHECK(!callback.is_null());
[email protected]73f9c742012-06-15 07:37:13410
[email protected]bdd947c2012-11-06 04:35:34411 base::PostTaskAndReplyWithResult(
412 blocking_task_runner_,
[email protected]73f9c742012-06-15 07:37:13413 FROM_HERE,
[email protected]54ba37502013-05-09 08:43:40414 base::Bind(&FileCache::Pin,
[email protected]3f5e2902012-11-06 08:21:15415 base::Unretained(this), resource_id, md5),
[email protected]eca3fc92013-05-01 03:53:40416 base::Bind(&FileCache::OnPinned,
[email protected]3f5e2902012-11-06 08:21:15417 weak_ptr_factory_.GetWeakPtr(), resource_id, md5, callback));
[email protected]73f9c742012-06-15 07:37:13418}
419
[email protected]f8b1a532013-06-06 08:35:08420FileError FileCache::Pin(const std::string& resource_id,
421 const std::string& md5) {
422 AssertOnSequencedWorkerPool();
423
424 bool is_persistent = true;
425 FileCacheEntry cache_entry;
426 if (!GetCacheEntry(resource_id, md5, &cache_entry)) {
427 // The file will be first downloaded in 'tmp', then moved to 'persistent'.
428 is_persistent = false;
429 } else { // File exists in cache, determines destination path.
430 // Determine source and destination paths.
431
432 // If file is dirty or mounted, don't move it.
433 if (!cache_entry.is_dirty() && !cache_entry.is_mounted()) {
434 // If file was pinned before but actual file blob doesn't exist in cache:
435 // - don't need to move the file.
436 if (!cache_entry.is_present()) {
437 DCHECK(cache_entry.is_pinned());
438 return FILE_ERROR_OK;
439 }
440 // File exists, move it to persistent dir.
441 // Gets the current path of the file in cache.
442 base::FilePath source_path = GetCacheFilePath(
443 resource_id,
444 md5,
445 GetSubDirectoryType(cache_entry),
446 CACHED_FILE_FROM_SERVER);
447 base::FilePath dest_path = GetCacheFilePath(resource_id,
448 md5,
449 CACHE_TYPE_PERSISTENT,
450 CACHED_FILE_FROM_SERVER);
451 if (!MoveFile(source_path, dest_path))
452 return FILE_ERROR_FAILED;
453 }
454 }
455
456 // Now that file operations have completed, update metadata.
457 cache_entry.set_md5(md5);
458 cache_entry.set_is_pinned(true);
459 cache_entry.set_is_persistent(is_persistent);
460 metadata_->AddOrUpdateCacheEntry(resource_id, cache_entry);
461 return FILE_ERROR_OK;
462}
463
[email protected]54ba37502013-05-09 08:43:40464void FileCache::UnpinOnUIThread(const std::string& resource_id,
465 const std::string& md5,
[email protected]eca3fc92013-05-01 03:53:40466 const FileOperationCallback& callback) {
[email protected]9564c1502012-11-28 12:12:16467 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
468 DCHECK(!callback.is_null());
469
470 base::PostTaskAndReplyWithResult(
471 blocking_task_runner_,
472 FROM_HERE,
[email protected]54ba37502013-05-09 08:43:40473 base::Bind(&FileCache::Unpin,
474 base::Unretained(this), resource_id, md5),
475 base::Bind(&FileCache::OnUnpinned,
476 weak_ptr_factory_.GetWeakPtr(), resource_id, md5, callback));
477}
478
[email protected]ec514362013-05-27 17:52:22479FileError FileCache::Unpin(const std::string& resource_id,
480 const std::string& md5) {
481 AssertOnSequencedWorkerPool();
482
483 // Unpinning a file means its entry must exist in cache.
484 FileCacheEntry cache_entry;
485 if (!GetCacheEntry(resource_id, md5, &cache_entry)) {
486 LOG(WARNING) << "Can't unpin a file that wasn't pinned or cached: res_id="
487 << resource_id
488 << ", md5=" << md5;
489 return FILE_ERROR_NOT_FOUND;
490 }
491
492 CacheSubDirectoryType sub_dir_type = CACHE_TYPE_TMP;
493
494 // If file is dirty or mounted, don't move it.
495 if (cache_entry.is_dirty() || cache_entry.is_mounted()) {
496 sub_dir_type = CACHE_TYPE_PERSISTENT;
497 DCHECK(cache_entry.is_persistent());
498 } else {
499 // If file was pinned but actual file blob still doesn't exist in cache,
500 // don't need to move the file.
501 if (cache_entry.is_present()) {
502 // Gets the current path of the file in cache.
503 base::FilePath source_path = GetCacheFilePath(
504 resource_id,
505 md5,
506 GetSubDirectoryType(cache_entry),
507 CACHED_FILE_FROM_SERVER);
508 // File exists, move it to tmp dir.
509 base::FilePath dest_path = GetCacheFilePath(
510 resource_id,
511 md5,
512 CACHE_TYPE_TMP,
513 CACHED_FILE_FROM_SERVER);
514 if (!MoveFile(source_path, dest_path))
515 return FILE_ERROR_FAILED;
516 }
517 }
518
519 // Now that file operations have completed, update metadata.
520 if (cache_entry.is_present()) {
521 cache_entry.set_md5(md5);
522 cache_entry.set_is_pinned(false);
523 cache_entry.set_is_persistent(sub_dir_type == CACHE_TYPE_PERSISTENT);
524 metadata_->AddOrUpdateCacheEntry(resource_id, cache_entry);
525 } else {
526 // Remove the existing entry if we are unpinning a non-present file.
527 metadata_->RemoveCacheEntry(resource_id);
528 }
529 return FILE_ERROR_OK;
530}
531
[email protected]54ba37502013-05-09 08:43:40532void FileCache::MarkAsMountedOnUIThread(
533 const std::string& resource_id,
534 const std::string& md5,
535 const GetFileFromCacheCallback& callback) {
536 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
537 DCHECK(!callback.is_null());
538
[email protected]7197485b2013-05-23 15:59:34539 base::FilePath* cache_file_path = new base::FilePath;
[email protected]54ba37502013-05-09 08:43:40540 base::PostTaskAndReplyWithResult(
541 blocking_task_runner_,
542 FROM_HERE,
543 base::Bind(&FileCache::MarkAsMounted,
[email protected]7197485b2013-05-23 15:59:34544 base::Unretained(this), resource_id, md5, cache_file_path),
545 base::Bind(RunGetFileFromCacheCallback,
546 callback, base::Owned(cache_file_path)));
[email protected]54ba37502013-05-09 08:43:40547}
548
549void FileCache::MarkAsUnmountedOnUIThread(
550 const base::FilePath& file_path,
551 const FileOperationCallback& callback) {
552 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
553 DCHECK(!callback.is_null());
554
555 base::PostTaskAndReplyWithResult(
556 blocking_task_runner_,
557 FROM_HERE,
558 base::Bind(&FileCache::MarkAsUnmounted,
[email protected]9564c1502012-11-28 12:12:16559 base::Unretained(this), file_path),
560 callback);
561}
562
[email protected]54ba37502013-05-09 08:43:40563void FileCache::MarkDirtyOnUIThread(const std::string& resource_id,
564 const std::string& md5,
565 const FileOperationCallback& callback) {
[email protected]c960d222012-06-15 10:03:50566 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]bdd947c2012-11-06 04:35:34567 DCHECK(!callback.is_null());
[email protected]73f9c742012-06-15 07:37:13568
[email protected]bdd947c2012-11-06 04:35:34569 base::PostTaskAndReplyWithResult(
570 blocking_task_runner_,
[email protected]c960d222012-06-15 10:03:50571 FROM_HERE,
[email protected]54ba37502013-05-09 08:43:40572 base::Bind(&FileCache::MarkDirty,
[email protected]bdd947c2012-11-06 04:35:34573 base::Unretained(this), resource_id, md5),
[email protected]bc809e42012-11-28 04:46:29574 callback);
[email protected]73f9c742012-06-15 07:37:13575}
576
[email protected]b568b882013-06-10 04:38:07577FileError FileCache::MarkDirty(const std::string& resource_id,
578 const std::string& md5) {
579 AssertOnSequencedWorkerPool();
580
581 // If file has already been marked dirty in previous instance of chrome, we
582 // would have lost the md5 info during cache initialization, because the file
583 // would have been renamed to .local extension.
584 // So, search for entry in cache without comparing md5.
585
586 // Marking a file dirty means its entry and actual file blob must exist in
587 // cache.
588 FileCacheEntry cache_entry;
589 if (!GetCacheEntry(resource_id, std::string(), &cache_entry) ||
590 !cache_entry.is_present()) {
591 LOG(WARNING) << "Can't mark dirty a file that wasn't cached: res_id="
592 << resource_id
593 << ", md5=" << md5;
594 return FILE_ERROR_NOT_FOUND;
595 }
596
597 if (cache_entry.is_dirty()) {
598 // The file must be in persistent dir.
599 DCHECK(cache_entry.is_persistent());
600 return FILE_ERROR_OK;
601 }
602
603 // Move file to persistent dir with new .local extension.
604
605 // Get the current path of the file in cache.
606 base::FilePath source_path = GetCacheFilePath(
607 resource_id,
608 md5,
609 GetSubDirectoryType(cache_entry),
610 CACHED_FILE_FROM_SERVER);
611 // Determine destination path.
612 const CacheSubDirectoryType sub_dir_type = CACHE_TYPE_PERSISTENT;
613 base::FilePath cache_file_path = GetCacheFilePath(
614 resource_id,
615 md5,
616 sub_dir_type,
617 CACHED_FILE_LOCALLY_MODIFIED);
618
619 if (!MoveFile(source_path, cache_file_path))
620 return FILE_ERROR_FAILED;
621
622 // Now that file operations have completed, update metadata.
623 cache_entry.set_md5(md5);
624 cache_entry.set_is_dirty(true);
625 cache_entry.set_is_persistent(sub_dir_type == CACHE_TYPE_PERSISTENT);
626 metadata_->AddOrUpdateCacheEntry(resource_id, cache_entry);
627 return FILE_ERROR_OK;
628}
629
[email protected]54ba37502013-05-09 08:43:40630void FileCache::CommitDirtyOnUIThread(const std::string& resource_id,
631 const std::string& md5,
632 const FileOperationCallback& callback) {
[email protected]73f9c742012-06-15 07:37:13633 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]2a2c4152012-11-26 11:34:50634 DCHECK(!callback.is_null());
[email protected]73f9c742012-06-15 07:37:13635
[email protected]89572fe2013-05-16 12:51:59636 // TODO(hashimoto): Move logic around OnCommitDirty to FileSystem and remove
637 // this method.
638 base::MessageLoopProxy::current()->PostTask(
[email protected]73f9c742012-06-15 07:37:13639 FROM_HERE,
[email protected]eca3fc92013-05-01 03:53:40640 base::Bind(&FileCache::OnCommitDirty,
[email protected]89572fe2013-05-16 12:51:59641 weak_ptr_factory_.GetWeakPtr(), resource_id, callback,
642 FILE_ERROR_OK));
[email protected]eca3fc92013-05-01 03:53:40643}
644
[email protected]fcf8eafe02013-05-28 11:15:39645FileError FileCache::ClearDirty(const std::string& resource_id,
646 const std::string& md5) {
647 AssertOnSequencedWorkerPool();
[email protected]eca3fc92013-05-01 03:53:40648
[email protected]fcf8eafe02013-05-28 11:15:39649 // |md5| is the new .<md5> extension to rename the file to.
650 // So, search for entry in cache without comparing md5.
651 FileCacheEntry cache_entry;
652
653 // Clearing a dirty file means its entry and actual file blob must exist in
654 // cache.
655 if (!GetCacheEntry(resource_id, std::string(), &cache_entry) ||
656 !cache_entry.is_present()) {
657 LOG(WARNING) << "Can't clear dirty state of a file that wasn't cached: "
658 << "res_id=" << resource_id
659 << ", md5=" << md5;
660 return FILE_ERROR_NOT_FOUND;
661 }
662
663 // If a file is not dirty (it should have been marked dirty via
664 // MarkDirtyInCache), clearing its dirty state is an invalid operation.
665 if (!cache_entry.is_dirty()) {
666 LOG(WARNING) << "Can't clear dirty state of a non-dirty file: res_id="
667 << resource_id
668 << ", md5=" << md5;
669 return FILE_ERROR_INVALID_OPERATION;
670 }
671
672 // File must be dirty and hence in persistent dir.
673 DCHECK(cache_entry.is_persistent());
674
675 // Get the current path of the file in cache.
676 base::FilePath source_path =
677 GetCacheFilePath(resource_id,
678 md5,
679 GetSubDirectoryType(cache_entry),
680 CACHED_FILE_LOCALLY_MODIFIED);
681
682 // Determine destination path.
683 // If file is pinned, move it to persistent dir with .md5 extension;
684 // otherwise, move it to tmp dir with .md5 extension.
685 const CacheSubDirectoryType sub_dir_type =
686 cache_entry.is_pinned() ? CACHE_TYPE_PERSISTENT : CACHE_TYPE_TMP;
687 base::FilePath dest_path = GetCacheFilePath(resource_id,
688 md5,
689 sub_dir_type,
690 CACHED_FILE_FROM_SERVER);
691
692 if (!MoveFile(source_path, dest_path))
693 return FILE_ERROR_FAILED;
694
695 // Now that file operations have completed, update metadata.
696 cache_entry.set_md5(md5);
697 cache_entry.set_is_dirty(false);
698 cache_entry.set_is_persistent(sub_dir_type == CACHE_TYPE_PERSISTENT);
699 metadata_->AddOrUpdateCacheEntry(resource_id, cache_entry);
700 return FILE_ERROR_OK;
[email protected]73f9c742012-06-15 07:37:13701}
702
[email protected]54ba37502013-05-09 08:43:40703void FileCache::RemoveOnUIThread(const std::string& resource_id,
704 const FileOperationCallback& callback) {
[email protected]73f9c742012-06-15 07:37:13705 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]2a2c4152012-11-26 11:34:50706 DCHECK(!callback.is_null());
[email protected]73f9c742012-06-15 07:37:13707
[email protected]bdd947c2012-11-06 04:35:34708 base::PostTaskAndReplyWithResult(
709 blocking_task_runner_,
[email protected]73f9c742012-06-15 07:37:13710 FROM_HERE,
[email protected]54ba37502013-05-09 08:43:40711 base::Bind(&FileCache::Remove,
[email protected]3f5e2902012-11-06 08:21:15712 base::Unretained(this), resource_id),
[email protected]2a2c4152012-11-26 11:34:50713 callback);
[email protected]73f9c742012-06-15 07:37:13714}
715
[email protected]3361a542013-05-22 17:38:27716FileError FileCache::Remove(const std::string& resource_id) {
717 AssertOnSequencedWorkerPool();
718
719 // MD5 is not passed into RemoveCacheEntry because we would delete all
720 // cache files corresponding to <resource_id> regardless of the md5.
721 // So, search for entry in cache without taking md5 into account.
722 FileCacheEntry cache_entry;
723
724 // If entry doesn't exist or is dirty or mounted in cache, nothing to do.
725 const bool entry_found =
726 GetCacheEntry(resource_id, std::string(), &cache_entry);
727 if (!entry_found || cache_entry.is_dirty() || cache_entry.is_mounted()) {
728 DVLOG(1) << "Entry is "
729 << (entry_found ?
730 (cache_entry.is_dirty() ? "dirty" : "mounted") :
731 "non-existent")
732 << " in cache, not removing";
733 return FILE_ERROR_OK;
734 }
735
736 // Determine paths to delete all cache versions of |resource_id| in
737 // persistent, tmp and pinned directories.
738 std::vector<base::FilePath> paths_to_delete;
739
740 // For files in persistent and tmp dirs, delete files that match
741 // "<resource_id>.*".
742 paths_to_delete.push_back(GetCacheFilePath(resource_id,
743 util::kWildCard,
744 CACHE_TYPE_PERSISTENT,
745 CACHED_FILE_FROM_SERVER));
746 paths_to_delete.push_back(GetCacheFilePath(resource_id,
747 util::kWildCard,
748 CACHE_TYPE_TMP,
749 CACHED_FILE_FROM_SERVER));
750
751 // Don't delete locally modified files.
752 base::FilePath path_to_keep = GetCacheFilePath(resource_id,
753 std::string(),
754 CACHE_TYPE_PERSISTENT,
755 CACHED_FILE_LOCALLY_MODIFIED);
756
757 for (size_t i = 0; i < paths_to_delete.size(); ++i) {
758 DeleteFilesSelectively(paths_to_delete[i], path_to_keep);
759 }
760
761 // Now that all file operations have completed, remove from metadata.
762 metadata_->RemoveCacheEntry(resource_id);
763
764 return FILE_ERROR_OK;
765}
766
[email protected]54ba37502013-05-09 08:43:40767void FileCache::ClearAllOnUIThread(const InitializeCacheCallback& callback) {
[email protected]f861b392012-08-03 20:41:12768 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]bdd947c2012-11-06 04:35:34769 DCHECK(!callback.is_null());
[email protected]f861b392012-08-03 20:41:12770
[email protected]bdd947c2012-11-06 04:35:34771 base::PostTaskAndReplyWithResult(
772 blocking_task_runner_,
[email protected]f861b392012-08-03 20:41:12773 FROM_HERE,
[email protected]54ba37502013-05-09 08:43:40774 base::Bind(&FileCache::ClearAll, base::Unretained(this)),
[email protected]bdd947c2012-11-06 04:35:34775 callback);
[email protected]f861b392012-08-03 20:41:12776}
777
[email protected]eca3fc92013-05-01 03:53:40778void FileCache::RequestInitialize(const InitializeCacheCallback& callback) {
[email protected]73f9c742012-06-15 07:37:13779 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]322e0032012-10-07 01:55:53780 DCHECK(!callback.is_null());
[email protected]73f9c742012-06-15 07:37:13781
[email protected]bdd947c2012-11-06 04:35:34782 base::PostTaskAndReplyWithResult(
783 blocking_task_runner_,
[email protected]73f9c742012-06-15 07:37:13784 FROM_HERE,
[email protected]eca3fc92013-05-01 03:53:40785 base::Bind(&FileCache::InitializeOnBlockingPool, base::Unretained(this)),
[email protected]bdd947c2012-11-06 04:35:34786 callback);
[email protected]73f9c742012-06-15 07:37:13787}
788
[email protected]eca3fc92013-05-01 03:53:40789void FileCache::Destroy() {
[email protected]73f9c742012-06-15 07:37:13790 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
791
792 // Invalidate the weak pointer.
[email protected]e53ac8f2012-08-02 07:05:00793 weak_ptr_factory_.InvalidateWeakPtrs();
[email protected]73f9c742012-06-15 07:37:13794
795 // Destroy myself on the blocking pool.
[email protected]17196ee2012-12-13 06:23:51796 // Note that base::DeletePointer<> cannot be used as the destructor of this
797 // class is private.
[email protected]ddbf2052012-07-13 15:07:02798 blocking_task_runner_->PostTask(
[email protected]73f9c742012-06-15 07:37:13799 FROM_HERE,
[email protected]eca3fc92013-05-01 03:53:40800 base::Bind(&FileCache::DestroyOnBlockingPool, base::Unretained(this)));
[email protected]73f9c742012-06-15 07:37:13801}
802
[email protected]eca3fc92013-05-01 03:53:40803bool FileCache::InitializeOnBlockingPool() {
[email protected]ca5f6da2012-06-18 12:54:59804 AssertOnSequencedWorkerPool();
805
[email protected]bdd947c2012-11-06 04:35:34806 if (!InitCachePaths(cache_paths_))
807 return false;
[email protected]322e0032012-10-07 01:55:53808
[email protected]29b131df2013-06-04 06:54:44809 metadata_.reset(new FileCacheMetadata(blocking_task_runner_));
[email protected]bdd947c2012-11-06 04:35:34810 return metadata_->Initialize(cache_paths_);
[email protected]ca5f6da2012-06-18 12:54:59811}
812
[email protected]eca3fc92013-05-01 03:53:40813void FileCache::DestroyOnBlockingPool() {
[email protected]73f9c742012-06-15 07:37:13814 AssertOnSequencedWorkerPool();
815 delete this;
816}
817
[email protected]82c4eb92013-05-21 11:25:23818FileError FileCache::StoreInternal(const std::string& resource_id,
819 const std::string& md5,
820 const base::FilePath& source_path,
821 FileOperationType file_operation_type,
822 CachedFileOrigin origin) {
[email protected]a321b9632012-06-14 03:29:17823 AssertOnSequencedWorkerPool();
[email protected]a321b9632012-06-14 03:29:17824
[email protected]fb1b37b2013-04-09 04:37:49825 int64 file_size = 0;
[email protected]f0c67002012-08-15 04:10:38826 if (file_operation_type == FILE_OPERATION_COPY) {
[email protected]f0c67002012-08-15 04:10:38827 if (!file_util::GetFileSize(source_path, &file_size)) {
828 LOG(WARNING) << "Couldn't get file size for: " << source_path.value();
[email protected]78a158b2013-04-23 06:57:49829 return FILE_ERROR_FAILED;
[email protected]f0c67002012-08-15 04:10:38830 }
[email protected]f0c67002012-08-15 04:10:38831 }
[email protected]54ba37502013-05-09 08:43:40832 if (!FreeDiskSpaceIfNeededFor(file_size))
[email protected]78a158b2013-04-23 06:57:49833 return FILE_ERROR_NO_SPACE;
[email protected]f0c67002012-08-15 04:10:38834
[email protected]aa7365c2013-05-01 05:50:47835 FileCacheEntry cache_entry;
[email protected]54ba37502013-05-09 08:43:40836 GetCacheEntry(resource_id, std::string(), &cache_entry);
[email protected]d8546c92013-05-02 05:09:59837
838 CacheSubDirectoryType sub_dir_type = CACHE_TYPE_TMP;
839 if (origin == CACHED_FILE_FROM_SERVER) {
[email protected]a321b9632012-06-14 03:29:17840 // If file is dirty or mounted, return error.
[email protected]02821102012-07-12 20:19:17841 if (cache_entry.is_dirty() || cache_entry.is_mounted()) {
[email protected]a321b9632012-06-14 03:29:17842 LOG(WARNING) << "Can't store a file to replace a "
[email protected]02821102012-07-12 20:19:17843 << (cache_entry.is_dirty() ? "dirty" : "mounted")
[email protected]a321b9632012-06-14 03:29:17844 << " file: res_id=" << resource_id
845 << ", md5=" << md5;
[email protected]78a158b2013-04-23 06:57:49846 return FILE_ERROR_IN_USE;
[email protected]a321b9632012-06-14 03:29:17847 }
848
[email protected]d8546c92013-05-02 05:09:59849 // If file was previously pinned, store it in persistent dir.
[email protected]c80ca12f2012-11-09 10:56:03850 if (cache_entry.is_pinned())
[email protected]a321b9632012-06-14 03:29:17851 sub_dir_type = CACHE_TYPE_PERSISTENT;
[email protected]d8546c92013-05-02 05:09:59852 } else {
853 sub_dir_type = CACHE_TYPE_PERSISTENT;
[email protected]a321b9632012-06-14 03:29:17854 }
855
[email protected]650b2d52013-02-10 03:41:45856 base::FilePath dest_path = GetCacheFilePath(resource_id, md5, sub_dir_type,
[email protected]d8546c92013-05-02 05:09:59857 origin);
[email protected]c80ca12f2012-11-09 10:56:03858 bool success = false;
859 switch (file_operation_type) {
860 case FILE_OPERATION_MOVE:
861 success = MoveFile(source_path, dest_path);
862 break;
863 case FILE_OPERATION_COPY:
864 success = CopyFile(source_path, dest_path);
865 break;
866 default:
867 NOTREACHED();
[email protected]a321b9632012-06-14 03:29:17868 }
869
[email protected]f60c670b2012-09-13 06:19:25870 // Determine search pattern for stale filenames corresponding to resource_id,
[email protected]a321b9632012-06-14 03:29:17871 // either "<resource_id>*" or "<resource_id>.*".
[email protected]650b2d52013-02-10 03:41:45872 base::FilePath stale_filenames_pattern;
[email protected]a321b9632012-06-14 03:29:17873 if (md5.empty()) {
874 // No md5 means no extension, append '*' after base name, i.e.
875 // "<resource_id>*".
876 // Cannot call |dest_path|.ReplaceExtension when there's no md5 extension:
877 // if base name of |dest_path| (i.e. escaped resource_id) contains the
878 // extension separator '.', ReplaceExtension will remove it and everything
879 // after it. The result will be nothing like the escaped resource_id.
[email protected]650b2d52013-02-10 03:41:45880 stale_filenames_pattern =
881 base::FilePath(dest_path.value() + util::kWildCard);
[email protected]a321b9632012-06-14 03:29:17882 } else {
883 // Replace md5 extension with '*' i.e. "<resource_id>.*".
884 // Note that ReplaceExtension automatically prefixes the extension with the
885 // extension separator '.'.
[email protected]ca5f6da2012-06-18 12:54:59886 stale_filenames_pattern = dest_path.ReplaceExtension(util::kWildCard);
[email protected]a321b9632012-06-14 03:29:17887 }
888
889 // Delete files that match |stale_filenames_pattern| except for |dest_path|.
890 DeleteFilesSelectively(stale_filenames_pattern, dest_path);
891
[email protected]c80ca12f2012-11-09 10:56:03892 if (success) {
893 // Now that file operations have completed, update metadata.
[email protected]b22f87f2012-07-12 10:53:17894 cache_entry.set_md5(md5);
[email protected]02821102012-07-12 20:19:17895 cache_entry.set_is_present(true);
896 cache_entry.set_is_persistent(sub_dir_type == CACHE_TYPE_PERSISTENT);
[email protected]d8546c92013-05-02 05:09:59897 cache_entry.set_is_dirty(origin == CACHED_FILE_LOCALLY_MODIFIED);
[email protected]b22f87f2012-07-12 10:53:17898 metadata_->AddOrUpdateCacheEntry(resource_id, cache_entry);
[email protected]a321b9632012-06-14 03:29:17899 }
[email protected]bdd947c2012-11-06 04:35:34900
[email protected]78a158b2013-04-23 06:57:49901 return success ? FILE_ERROR_OK : FILE_ERROR_FAILED;
[email protected]a321b9632012-06-14 03:29:17902}
903
[email protected]7197485b2013-05-23 15:59:34904FileError FileCache::MarkAsMounted(const std::string& resource_id,
905 const std::string& md5,
906 base::FilePath* cache_file_path) {
[email protected]a321b9632012-06-14 03:29:17907 AssertOnSequencedWorkerPool();
[email protected]7197485b2013-05-23 15:59:34908 DCHECK(cache_file_path);
[email protected]a321b9632012-06-14 03:29:17909
[email protected]a321b9632012-06-14 03:29:17910 // Get cache entry associated with the resource_id and md5
[email protected]aa7365c2013-05-01 05:50:47911 FileCacheEntry cache_entry;
[email protected]7197485b2013-05-23 15:59:34912 if (!GetCacheEntry(resource_id, md5, &cache_entry))
913 return FILE_ERROR_NOT_FOUND;
[email protected]bdd947c2012-11-06 04:35:34914
[email protected]7197485b2013-05-23 15:59:34915 if (cache_entry.is_mounted())
916 return FILE_ERROR_INVALID_OPERATION;
[email protected]a321b9632012-06-14 03:29:17917
918 // Get the subdir type and path for the unmounted state.
919 CacheSubDirectoryType unmounted_subdir =
[email protected]02821102012-07-12 20:19:17920 cache_entry.is_pinned() ? CACHE_TYPE_PERSISTENT : CACHE_TYPE_TMP;
[email protected]650b2d52013-02-10 03:41:45921 base::FilePath unmounted_path = GetCacheFilePath(
[email protected]a321b9632012-06-14 03:29:17922 resource_id, md5, unmounted_subdir, CACHED_FILE_FROM_SERVER);
923
924 // Get the subdir type and path for the mounted state.
925 CacheSubDirectoryType mounted_subdir = CACHE_TYPE_PERSISTENT;
[email protected]650b2d52013-02-10 03:41:45926 base::FilePath mounted_path = GetCacheFilePath(
[email protected]a321b9632012-06-14 03:29:17927 resource_id, md5, mounted_subdir, CACHED_FILE_MOUNTED);
928
[email protected]9564c1502012-11-28 12:12:16929 // Move cache file.
[email protected]7197485b2013-05-23 15:59:34930 if (!MoveFile(unmounted_path, mounted_path))
931 return FILE_ERROR_FAILED;
[email protected]c80ca12f2012-11-09 10:56:03932
[email protected]7197485b2013-05-23 15:59:34933 // Ensures the file is readable to cros_disks. See crbug.com/236994.
934 file_util::SetPosixFilePermissions(
935 mounted_path,
936 file_util::FILE_PERMISSION_READ_BY_USER |
937 file_util::FILE_PERMISSION_WRITE_BY_USER |
938 file_util::FILE_PERMISSION_READ_BY_GROUP |
939 file_util::FILE_PERMISSION_READ_BY_OTHERS);
[email protected]5ee73852013-05-03 03:09:58940
[email protected]7197485b2013-05-23 15:59:34941 // Now that cache operation is complete, update metadata.
942 cache_entry.set_md5(md5);
943 cache_entry.set_is_mounted(true);
944 cache_entry.set_is_persistent(true);
945 metadata_->AddOrUpdateCacheEntry(resource_id, cache_entry);
946
947 *cache_file_path = mounted_path;
948 return FILE_ERROR_OK;
[email protected]a321b9632012-06-14 03:29:17949}
950
[email protected]54ba37502013-05-09 08:43:40951FileError FileCache::MarkAsUnmounted(const base::FilePath& file_path) {
[email protected]9564c1502012-11-28 12:12:16952 AssertOnSequencedWorkerPool();
[email protected]eca3fc92013-05-01 03:53:40953 DCHECK(IsUnderFileCacheDirectory(file_path));
[email protected]9564c1502012-11-28 12:12:16954
955 // Parse file path to obtain resource_id, md5 and extra_extension.
956 std::string resource_id;
957 std::string md5;
958 std::string extra_extension;
959 util::ParseCacheFilePath(file_path, &resource_id, &md5, &extra_extension);
960 // The extra_extension shall be ".mounted" iff we're unmounting.
[email protected]3a680f32013-03-01 04:07:27961 DCHECK_EQ(util::kMountedArchiveFileExtension, extra_extension);
[email protected]9564c1502012-11-28 12:12:16962
963 // Get cache entry associated with the resource_id and md5
[email protected]aa7365c2013-05-01 05:50:47964 FileCacheEntry cache_entry;
[email protected]54ba37502013-05-09 08:43:40965 if (!GetCacheEntry(resource_id, md5, &cache_entry))
[email protected]78a158b2013-04-23 06:57:49966 return FILE_ERROR_NOT_FOUND;
[email protected]9564c1502012-11-28 12:12:16967
968 if (!cache_entry.is_mounted())
[email protected]78a158b2013-04-23 06:57:49969 return FILE_ERROR_INVALID_OPERATION;
[email protected]9564c1502012-11-28 12:12:16970
971 // Get the subdir type and path for the unmounted state.
972 CacheSubDirectoryType unmounted_subdir =
973 cache_entry.is_pinned() ? CACHE_TYPE_PERSISTENT : CACHE_TYPE_TMP;
[email protected]650b2d52013-02-10 03:41:45974 base::FilePath unmounted_path = GetCacheFilePath(
[email protected]9564c1502012-11-28 12:12:16975 resource_id, md5, unmounted_subdir, CACHED_FILE_FROM_SERVER);
976
977 // Get the subdir type and path for the mounted state.
978 CacheSubDirectoryType mounted_subdir = CACHE_TYPE_PERSISTENT;
[email protected]650b2d52013-02-10 03:41:45979 base::FilePath mounted_path = GetCacheFilePath(
[email protected]9564c1502012-11-28 12:12:16980 resource_id, md5, mounted_subdir, CACHED_FILE_MOUNTED);
981
982 // Move cache file.
983 if (!MoveFile(mounted_path, unmounted_path))
[email protected]78a158b2013-04-23 06:57:49984 return FILE_ERROR_FAILED;
[email protected]9564c1502012-11-28 12:12:16985
986 // Now that cache operation is complete, update metadata.
987 cache_entry.set_md5(md5);
988 cache_entry.set_is_mounted(false);
989 cache_entry.set_is_persistent(unmounted_subdir == CACHE_TYPE_PERSISTENT);
990 metadata_->AddOrUpdateCacheEntry(resource_id, cache_entry);
[email protected]78a158b2013-04-23 06:57:49991 return FILE_ERROR_OK;
[email protected]9564c1502012-11-28 12:12:16992}
993
[email protected]54ba37502013-05-09 08:43:40994bool FileCache::ClearAll() {
[email protected]f861b392012-08-03 20:41:12995 AssertOnSequencedWorkerPool();
[email protected]f861b392012-08-03 20:41:12996
[email protected]bdd947c2012-11-06 04:35:34997 if (!file_util::Delete(cache_root_path_, true)) {
[email protected]322e0032012-10-07 01:55:53998 LOG(WARNING) << "Failed to delete the cache directory";
[email protected]bdd947c2012-11-06 04:35:34999 return false;
[email protected]322e0032012-10-07 01:55:531000 }
[email protected]f861b392012-08-03 20:41:121001
[email protected]bdd947c2012-11-06 04:35:341002 if (!InitializeOnBlockingPool()) {
[email protected]322e0032012-10-07 01:55:531003 LOG(WARNING) << "Failed to initialize the cache";
[email protected]bdd947c2012-11-06 04:35:341004 return false;
[email protected]322e0032012-10-07 01:55:531005 }
[email protected]bdd947c2012-11-06 04:35:341006 return true;
[email protected]f861b392012-08-03 20:41:121007}
1008
[email protected]eca3fc92013-05-01 03:53:401009void FileCache::OnPinned(const std::string& resource_id,
1010 const std::string& md5,
1011 const FileOperationCallback& callback,
1012 FileError error) {
[email protected]73f9c742012-06-15 07:37:131013 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]2a2c4152012-11-26 11:34:501014 DCHECK(!callback.is_null());
[email protected]73f9c742012-06-15 07:37:131015
[email protected]2a2c4152012-11-26 11:34:501016 callback.Run(error);
[email protected]73f9c742012-06-15 07:37:131017
[email protected]78a158b2013-04-23 06:57:491018 if (error == FILE_ERROR_OK)
[email protected]aa7365c2013-05-01 05:50:471019 FOR_EACH_OBSERVER(FileCacheObserver,
[email protected]a09275502012-10-10 04:48:011020 observers_,
1021 OnCachePinned(resource_id, md5));
[email protected]3653146a2012-05-29 13:41:471022}
1023
[email protected]eca3fc92013-05-01 03:53:401024void FileCache::OnUnpinned(const std::string& resource_id,
1025 const std::string& md5,
1026 const FileOperationCallback& callback,
1027 FileError error) {
[email protected]73f9c742012-06-15 07:37:131028 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]2a2c4152012-11-26 11:34:501029 DCHECK(!callback.is_null());
[email protected]a321b9632012-06-14 03:29:171030
[email protected]2a2c4152012-11-26 11:34:501031 callback.Run(error);
[email protected]a321b9632012-06-14 03:29:171032
[email protected]78a158b2013-04-23 06:57:491033 if (error == FILE_ERROR_OK)
[email protected]aa7365c2013-05-01 05:50:471034 FOR_EACH_OBSERVER(FileCacheObserver,
[email protected]a09275502012-10-10 04:48:011035 observers_,
1036 OnCacheUnpinned(resource_id, md5));
[email protected]a321b9632012-06-14 03:29:171037
[email protected]73f9c742012-06-15 07:37:131038 // Now the file is moved from "persistent" to "tmp" directory.
1039 // It's a chance to free up space if needed.
[email protected]ddbf2052012-07-13 15:07:021040 blocking_task_runner_->PostTask(
[email protected]73f9c742012-06-15 07:37:131041 FROM_HERE,
[email protected]77fb1a62012-11-01 13:42:321042 base::Bind(
[email protected]54ba37502013-05-09 08:43:401043 base::IgnoreResult(&FileCache::FreeDiskSpaceIfNeededFor),
[email protected]77fb1a62012-11-01 13:42:321044 base::Unretained(this), 0));
[email protected]3653146a2012-05-29 13:41:471045}
1046
[email protected]eca3fc92013-05-01 03:53:401047void FileCache::OnCommitDirty(const std::string& resource_id,
1048 const FileOperationCallback& callback,
1049 FileError error) {
[email protected]d7664c22012-06-18 19:35:491050 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]2a2c4152012-11-26 11:34:501051 DCHECK(!callback.is_null());
[email protected]d7664c22012-06-18 19:35:491052
[email protected]2a2c4152012-11-26 11:34:501053 callback.Run(error);
[email protected]d7664c22012-06-18 19:35:491054
[email protected]78a158b2013-04-23 06:57:491055 if (error == FILE_ERROR_OK)
[email protected]aa7365c2013-05-01 05:50:471056 FOR_EACH_OBSERVER(FileCacheObserver,
[email protected]a09275502012-10-10 04:48:011057 observers_,
1058 OnCacheCommitted(resource_id));
[email protected]d7664c22012-06-18 19:35:491059}
1060
[email protected]eca3fc92013-05-01 03:53:401061bool FileCache::HasEnoughSpaceFor(int64 num_bytes,
1062 const base::FilePath& path) {
[email protected]f6fd98a2012-12-14 00:04:021063 int64 free_space = 0;
1064 if (free_disk_space_getter_)
1065 free_space = free_disk_space_getter_->AmountOfFreeDiskSpace();
1066 else
1067 free_space = base::SysInfo::AmountOfFreeDiskSpace(path);
1068
1069 // Subtract this as if this portion does not exist.
1070 free_space -= kMinFreeSpace;
1071 return (free_space >= num_bytes);
1072}
1073
[email protected]01ba15f72012-06-09 00:41:051074// static
[email protected]eca3fc92013-05-01 03:53:401075std::vector<base::FilePath> FileCache::GetCachePaths(
[email protected]650b2d52013-02-10 03:41:451076 const base::FilePath& cache_root_path) {
1077 std::vector<base::FilePath> cache_paths;
[email protected]eca3fc92013-05-01 03:53:401078 // The order should match FileCache::CacheSubDirectoryType enum.
1079 cache_paths.push_back(cache_root_path.Append(kFileCacheMetaDir));
[email protected]eca3fc92013-05-01 03:53:401080 cache_paths.push_back(cache_root_path.Append(kFileCachePersistentDir));
1081 cache_paths.push_back(cache_root_path.Append(kFileCacheTmpDir));
1082 cache_paths.push_back(cache_root_path.Append(kFileCacheTmpDownloadsDir));
1083 cache_paths.push_back(cache_root_path.Append(kFileCacheTmpDocumentsDir));
[email protected]30d9dda2012-06-30 05:56:281084 return cache_paths;
1085}
1086
1087// static
[email protected]eca3fc92013-05-01 03:53:401088bool FileCache::CreateCacheDirectories(
[email protected]650b2d52013-02-10 03:41:451089 const std::vector<base::FilePath>& paths_to_create) {
[email protected]30d9dda2012-06-30 05:56:281090 bool success = true;
1091
1092 for (size_t i = 0; i < paths_to_create.size(); ++i) {
1093 if (file_util::DirectoryExists(paths_to_create[i]))
1094 continue;
1095
1096 if (!file_util::CreateDirectory(paths_to_create[i])) {
1097 // Error creating this directory, record error and proceed with next one.
1098 success = false;
1099 PLOG(ERROR) << "Error creating directory " << paths_to_create[i].value();
1100 } else {
1101 DVLOG(1) << "Created directory " << paths_to_create[i].value();
1102 }
1103 }
1104 return success;
1105}
1106
[email protected]fae353a2012-07-11 23:30:271107// static
[email protected]eca3fc92013-05-01 03:53:401108FileCache::CacheSubDirectoryType FileCache::GetSubDirectoryType(
[email protected]aa7365c2013-05-01 05:50:471109 const FileCacheEntry& cache_entry) {
[email protected]02821102012-07-12 20:19:171110 return cache_entry.is_persistent() ? CACHE_TYPE_PERSISTENT : CACHE_TYPE_TMP;
[email protected]fae353a2012-07-11 23:30:271111}
1112
[email protected]59c7cdec2013-05-07 04:17:131113} // namespace internal
[email protected]d9d04df2012-10-12 07:06:351114} // namespace drive