blob: 7558f7c0bcdbb1539d64d31b40e63c4d67c9d58e [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
5#include "chrome/browser/chromeos/gdata/gdata_cache.h"
6
7#include <vector>
8
[email protected]a321b9632012-06-14 03:29:179#include "base/chromeos/chromeos_version.h"
10#include "base/file_util.h"
[email protected]3653146a2012-05-29 13:41:4711#include "base/logging.h"
12#include "base/stringprintf.h"
13#include "base/string_util.h"
[email protected]a321b9632012-06-14 03:29:1714#include "base/sys_info.h"
[email protected]12e4c182012-07-12 21:30:0415#include "chrome/browser/chromeos/gdata/gdata.pb.h"
[email protected]ca5f6da2012-06-18 12:54:5916#include "chrome/browser/chromeos/gdata/gdata_cache_metadata.h"
[email protected]32a7fc852012-06-08 17:25:5017#include "chrome/browser/chromeos/gdata/gdata_util.h"
[email protected]01ba15f72012-06-09 00:41:0518#include "chrome/browser/profiles/profile.h"
19#include "chrome/common/chrome_constants.h"
20#include "chrome/common/chrome_paths_internal.h"
[email protected]7986b8d2012-06-14 15:05:1421#include "content/public/browser/browser_thread.h"
22
23using content::BrowserThread;
[email protected]3653146a2012-05-29 13:41:4724
25namespace gdata {
26namespace {
27
[email protected]01ba15f72012-06-09 00:41:0528const FilePath::CharType kGDataCacheVersionDir[] = FILE_PATH_LITERAL("v1");
[email protected]32a7fc852012-06-08 17:25:5029const FilePath::CharType kGDataCacheMetaDir[] = FILE_PATH_LITERAL("meta");
30const FilePath::CharType kGDataCachePinnedDir[] = FILE_PATH_LITERAL("pinned");
31const FilePath::CharType kGDataCacheOutgoingDir[] =
32 FILE_PATH_LITERAL("outgoing");
33const FilePath::CharType kGDataCachePersistentDir[] =
34 FILE_PATH_LITERAL("persistent");
35const FilePath::CharType kGDataCacheTmpDir[] = FILE_PATH_LITERAL("tmp");
36const FilePath::CharType kGDataCacheTmpDownloadsDir[] =
37 FILE_PATH_LITERAL("tmp/downloads");
38const FilePath::CharType kGDataCacheTmpDocumentsDir[] =
39 FILE_PATH_LITERAL("tmp/documents");
40
[email protected]a321b9632012-06-14 03:29:1741// Returns the home directory path, or an empty string if the home directory
42// is not found.
43// Copied from webkit/chromeos/cros_mount_point_provider.h.
44// TODO(satorux): Share the code.
45std::string GetHomeDirectory() {
46 if (base::chromeos::IsRunningOnChromeOS())
47 return "/home/chronos/user";
[email protected]3653146a2012-05-29 13:41:4748
[email protected]a321b9632012-06-14 03:29:1749 const char* home = getenv("HOME");
50 if (home)
51 return home;
52 return "";
53}
54
55// Used to tweak GetAmountOfFreeDiskSpace() behavior for testing.
56FreeDiskSpaceGetterInterface* global_free_disk_getter_for_testing = NULL;
57
58// Gets the amount of free disk space. Use
59// |global_free_disk_getter_for_testing| if set.
60int64 GetAmountOfFreeDiskSpace() {
61 if (global_free_disk_getter_for_testing)
62 return global_free_disk_getter_for_testing->AmountOfFreeDiskSpace();
63
64 return base::SysInfo::AmountOfFreeDiskSpace(
65 FilePath::FromUTF8Unsafe(GetHomeDirectory()));
66}
67
68// Returns true if we have sufficient space to store the given number of
69// bytes, while keeping kMinFreeSpace bytes on the disk.
70bool HasEnoughSpaceFor(int64 num_bytes) {
71 int64 free_space = GetAmountOfFreeDiskSpace();
72 // Substract this as if this portion does not exist.
73 free_space -= kMinFreeSpace;
74 return (free_space >= num_bytes);
75}
76
[email protected]79c3752d2012-07-17 12:10:0877// Create cache directory paths and set permissions.
78void InitCachePaths(const std::vector<FilePath>& cache_paths) {
79 if (cache_paths.size() < GDataCache::NUM_CACHE_TYPES) {
80 NOTREACHED();
81 LOG(ERROR) << "Size of cache_paths is invalid.";
82 return;
83 }
84
85 if (!GDataCache::CreateCacheDirectories(cache_paths))
86 return;
87
88 // Change permissions of cache persistent directory to u+rwx,og+x (711) in
89 // order to allow archive files in that directory to be mounted by cros-disks.
90 file_util::SetPosixFilePermissions(
91 cache_paths[GDataCache::CACHE_TYPE_PERSISTENT],
92 file_util::FILE_PERMISSION_USER_MASK |
93 file_util::FILE_PERMISSION_EXECUTE_BY_GROUP |
94 file_util::FILE_PERMISSION_EXECUTE_BY_OTHERS);
95}
96
[email protected]a321b9632012-06-14 03:29:1797// Remove all files under the given directory, non-recursively.
98// Do not remove recursively as we don't want to touch <gache>/tmp/downloads,
99// which is used for user initiated downloads like "Save As"
100void RemoveAllFiles(const FilePath& directory) {
101 using file_util::FileEnumerator;
102
103 FileEnumerator enumerator(directory, false /* recursive */,
104 FileEnumerator::FILES);
105 for (FilePath file_path = enumerator.Next(); !file_path.empty();
106 file_path = enumerator.Next()) {
107 DVLOG(1) << "Removing " << file_path.value();
108 if (!file_util::Delete(file_path, false /* recursive */))
109 LOG(WARNING) << "Failed to delete " << file_path.value();
110 }
111}
112
[email protected]a321b9632012-06-14 03:29:17113// Modifies cache state of file on blocking pool, which involves:
114// - moving or copying file (per |file_operation_type|) from |source_path| to
115// |dest_path| if they're different
116// - deleting symlink if |symlink_path| is not empty
117// - creating symlink if |symlink_path| is not empty and |create_symlink| is
118// true.
[email protected]7f90e77c2012-07-17 09:35:31119GDataFileError ModifyCacheState(
[email protected]a321b9632012-06-14 03:29:17120 const FilePath& source_path,
121 const FilePath& dest_path,
122 GDataCache::FileOperationType file_operation_type,
123 const FilePath& symlink_path,
124 bool create_symlink) {
125 // Move or copy |source_path| to |dest_path| if they are different.
126 if (source_path != dest_path) {
127 bool success = false;
128 if (file_operation_type == GDataCache::FILE_OPERATION_MOVE)
129 success = file_util::Move(source_path, dest_path);
130 else if (file_operation_type == GDataCache::FILE_OPERATION_COPY)
131 success = file_util::CopyFile(source_path, dest_path);
132 if (!success) {
[email protected]750af1d2012-07-13 14:32:43133 LOG(ERROR) << "Failed to "
134 << (file_operation_type == GDataCache::FILE_OPERATION_MOVE ?
135 "move " : "copy ")
136 << source_path.value()
137 << " to " << dest_path.value();
[email protected]7f90e77c2012-07-17 09:35:31138 return GDATA_FILE_ERROR_FAILED;
[email protected]a321b9632012-06-14 03:29:17139 } else {
140 DVLOG(1) << (file_operation_type == GDataCache::FILE_OPERATION_MOVE ?
141 "Moved " : "Copied ")
142 << source_path.value()
143 << " to " << dest_path.value();
144 }
145 } else {
146 DVLOG(1) << "No need to move file: source = destination";
147 }
148
149 if (symlink_path.empty())
[email protected]7f90e77c2012-07-17 09:35:31150 return GDATA_FILE_OK;
[email protected]a321b9632012-06-14 03:29:17151
152 // Remove symlink regardless of |create_symlink| because creating a link will
153 // not overwrite an existing one.
[email protected]a321b9632012-06-14 03:29:17154 // We try to save one file operation by not checking if link exists before
155 // deleting it, so unlink may return error if link doesn't exist, but it
156 // doesn't really matter to us.
[email protected]750af1d2012-07-13 14:32:43157 file_util::Delete(symlink_path, false);
[email protected]a321b9632012-06-14 03:29:17158
159 if (!create_symlink)
[email protected]7f90e77c2012-07-17 09:35:31160 return GDATA_FILE_OK;
[email protected]a321b9632012-06-14 03:29:17161
162 // Create new symlink to |dest_path|.
163 if (!file_util::CreateSymbolicLink(dest_path, symlink_path)) {
[email protected]750af1d2012-07-13 14:32:43164 LOG(ERROR) << "Failed to create a symlink from " << symlink_path.value()
165 << " to " << dest_path.value();
[email protected]7f90e77c2012-07-17 09:35:31166 return GDATA_FILE_ERROR_FAILED;
[email protected]a321b9632012-06-14 03:29:17167 }
168
[email protected]7f90e77c2012-07-17 09:35:31169 return GDATA_FILE_OK;
[email protected]a321b9632012-06-14 03:29:17170}
171
172// Deletes all files that match |path_to_delete_pattern| except for
173// |path_to_keep| on blocking pool.
174// If |path_to_keep| is empty, all files in |path_to_delete_pattern| are
175// deleted.
176void DeleteFilesSelectively(const FilePath& path_to_delete_pattern,
177 const FilePath& path_to_keep) {
178 // Enumerate all files in directory of |path_to_delete_pattern| that match
179 // base name of |path_to_delete_pattern|.
180 // If a file is not |path_to_keep|, delete it.
181 bool success = true;
182 file_util::FileEnumerator enumerator(
183 path_to_delete_pattern.DirName(),
184 false, // not recursive
185 static_cast<file_util::FileEnumerator::FileType>(
186 file_util::FileEnumerator::FILES |
187 file_util::FileEnumerator::SHOW_SYM_LINKS),
188 path_to_delete_pattern.BaseName().value());
189 for (FilePath current = enumerator.Next(); !current.empty();
190 current = enumerator.Next()) {
191 // If |path_to_keep| is not empty and same as current, don't delete it.
192 if (!path_to_keep.empty() && current == path_to_keep)
193 continue;
194
[email protected]0b8d4cee2012-07-02 20:46:26195 success = file_util::Delete(current, false);
[email protected]a321b9632012-06-14 03:29:17196 if (!success)
197 DVLOG(1) << "Error deleting " << current.value();
198 else
199 DVLOG(1) << "Deleted " << current.value();
200 }
201}
202
[email protected]b83e5202012-06-27 07:50:24203// Appends |resource_id| ID to |to_fetch| if the file is pinned but not
204// fetched (not present locally), or to |to_upload| if the file is dirty
205// but not uploaded.
206void CollectBacklog(std::vector<std::string>* to_fetch,
207 std::vector<std::string>* to_upload,
208 const std::string& resource_id,
[email protected]fae353a2012-07-11 23:30:27209 const GDataCacheEntry& cache_entry) {
[email protected]b83e5202012-06-27 07:50:24210 DCHECK(to_fetch);
211 DCHECK(to_upload);
[email protected]8764a392012-06-20 06:43:08212
[email protected]02821102012-07-12 20:19:17213 if (cache_entry.is_pinned() && !cache_entry.is_present())
[email protected]b83e5202012-06-27 07:50:24214 to_fetch->push_back(resource_id);
215
[email protected]02821102012-07-12 20:19:17216 if (cache_entry.is_dirty())
[email protected]b83e5202012-06-27 07:50:24217 to_upload->push_back(resource_id);
[email protected]8764a392012-06-20 06:43:08218}
219
[email protected]85b62192012-06-29 19:56:38220// Appends |resource_id| ID to |resource_ids| if the file is pinned and
221// present (cached locally).
222void CollectExistingPinnedFile(std::vector<std::string>* resource_ids,
223 const std::string& resource_id,
[email protected]fae353a2012-07-11 23:30:27224 const GDataCacheEntry& cache_entry) {
[email protected]85b62192012-06-29 19:56:38225 DCHECK(resource_ids);
226
[email protected]02821102012-07-12 20:19:17227 if (cache_entry.is_pinned() && cache_entry.is_present())
[email protected]85b62192012-06-29 19:56:38228 resource_ids->push_back(resource_id);
229}
[email protected]8764a392012-06-20 06:43:08230
[email protected]cd236432012-07-27 18:03:30231// Appends |resource_id| ID to |resource_ids| unconditionally.
232void CollectAnyFile(std::vector<std::string>* resource_ids,
233 const std::string& resource_id,
234 const GDataCacheEntry& /* cache_entry */) {
235 DCHECK(resource_ids);
236
237 resource_ids->push_back(resource_id);
238}
239
[email protected]7986b8d2012-06-14 15:05:14240// Runs callback with pointers dereferenced.
241// Used to implement SetMountedStateOnUIThread.
[email protected]73f9c742012-06-15 07:37:13242void RunSetMountedStateCallback(const SetMountedStateCallback& callback,
[email protected]7f90e77c2012-07-17 09:35:31243 GDataFileError* error,
[email protected]73f9c742012-06-15 07:37:13244 FilePath* cache_file_path) {
[email protected]7986b8d2012-06-14 15:05:14245 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
246 DCHECK(error);
247 DCHECK(cache_file_path);
248
249 if (!callback.is_null())
250 callback.Run(*error, *cache_file_path);
251}
252
[email protected]73f9c742012-06-15 07:37:13253// Runs callback with pointers dereferenced.
254// Used to implement *OnUIThread methods.
255void RunCacheOperationCallback(const CacheOperationCallback& callback,
[email protected]7f90e77c2012-07-17 09:35:31256 GDataFileError* error,
[email protected]73f9c742012-06-15 07:37:13257 const std::string& resource_id,
258 const std::string& md5) {
259 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
260 DCHECK(error);
261
262 if (!callback.is_null())
263 callback.Run(*error, resource_id, md5);
264}
265
[email protected]c960d222012-06-15 10:03:50266// Runs callback with pointers dereferenced.
267// Used to implement *OnUIThread methods.
268void RunGetFileFromCacheCallback(const GetFileFromCacheCallback& callback,
[email protected]7f90e77c2012-07-17 09:35:31269 GDataFileError* error,
[email protected]c960d222012-06-15 10:03:50270 const std::string& resource_id,
271 const std::string& md5,
272 FilePath* cache_file_path) {
273 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
274 DCHECK(error);
275 DCHECK(cache_file_path);
276
277 if (!callback.is_null())
278 callback.Run(*error, resource_id, md5, *cache_file_path);
279}
280
[email protected]8764a392012-06-20 06:43:08281// Runs callback with pointers dereferenced.
[email protected]4324fdc2012-06-29 05:32:48282// Used to implement GetResourceIdsOfBacklogOnUIThread().
[email protected]85b62192012-06-29 19:56:38283void RunGetResourceIdsOfBacklogCallback(
284 const GetResourceIdsOfBacklogCallback& callback,
285 std::vector<std::string>* to_fetch,
286 std::vector<std::string>* to_upload) {
[email protected]8764a392012-06-20 06:43:08287 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]b83e5202012-06-27 07:50:24288 DCHECK(to_fetch);
289 DCHECK(to_upload);
[email protected]8764a392012-06-20 06:43:08290
291 if (!callback.is_null())
[email protected]b83e5202012-06-27 07:50:24292 callback.Run(*to_fetch, *to_upload);
[email protected]8764a392012-06-20 06:43:08293}
294
[email protected]4324fdc2012-06-29 05:32:48295// Runs callback with pointers dereferenced.
[email protected]85b62192012-06-29 19:56:38296// Used to implement GetResourceIdsOfExistingPinnedFilesOnUIThread().
297void RunGetResourceIdsCallback(
298 const GetResourceIdsCallback& callback,
299 std::vector<std::string>* resource_ids) {
300 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
301 DCHECK(resource_ids);
302
303 if (!callback.is_null())
304 callback.Run(*resource_ids);
305}
306
307// Runs callback with pointers dereferenced.
[email protected]4324fdc2012-06-29 05:32:48308// Used to implement GetCacheEntryOnUIThread().
309void RunGetCacheEntryCallback(
[email protected]fae353a2012-07-11 23:30:27310 const GetCacheEntryCallback& callback,
[email protected]4324fdc2012-06-29 05:32:48311 bool* success,
[email protected]fae353a2012-07-11 23:30:27312 GDataCacheEntry* cache_entry) {
[email protected]4324fdc2012-06-29 05:32:48313 DCHECK(success);
314 DCHECK(cache_entry);
315
316 if (!callback.is_null())
317 callback.Run(*success, *cache_entry);
318}
319
[email protected]a321b9632012-06-14 03:29:17320} // namespace
[email protected]32a7fc852012-06-08 17:25:50321
[email protected]ddbf2052012-07-13 15:07:02322GDataCache::GDataCache(const FilePath& cache_root_path,
323 base::SequencedTaskRunner* blocking_task_runner)
[email protected]01ba15f72012-06-09 00:41:05324 : cache_root_path_(cache_root_path),
[email protected]6b70c7b2012-06-14 03:10:43325 cache_paths_(GetCachePaths(cache_root_path_)),
[email protected]ddbf2052012-07-13 15:07:02326 blocking_task_runner_(blocking_task_runner),
[email protected]73f9c742012-06-15 07:37:13327 ui_weak_ptr_factory_(this),
328 ui_weak_ptr_(ui_weak_ptr_factory_.GetWeakPtr()) {
329 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]3653146a2012-05-29 13:41:47330}
331
332GDataCache::~GDataCache() {
[email protected]73f9c742012-06-15 07:37:13333 AssertOnSequencedWorkerPool();
[email protected]3653146a2012-05-29 13:41:47334}
335
[email protected]32a7fc852012-06-08 17:25:50336FilePath GDataCache::GetCacheDirectoryPath(
337 CacheSubDirectoryType sub_dir_type) const {
338 DCHECK_LE(0, sub_dir_type);
339 DCHECK_GT(NUM_CACHE_TYPES, sub_dir_type);
340 return cache_paths_[sub_dir_type];
341}
342
343FilePath GDataCache::GetCacheFilePath(const std::string& resource_id,
344 const std::string& md5,
345 CacheSubDirectoryType sub_dir_type,
346 CachedFileOrigin file_origin) const {
347 DCHECK(sub_dir_type != CACHE_TYPE_META);
348
349 // Runs on any thread.
350 // Filename is formatted as resource_id.md5, i.e. resource_id is the base
351 // name and md5 is the extension.
352 std::string base_name = util::EscapeCacheFileName(resource_id);
353 if (file_origin == CACHED_FILE_LOCALLY_MODIFIED) {
354 DCHECK(sub_dir_type == CACHE_TYPE_PERSISTENT);
355 base_name += FilePath::kExtensionSeparator;
[email protected]b83e5202012-06-27 07:50:24356 base_name += util::kLocallyModifiedFileExtension;
[email protected]32a7fc852012-06-08 17:25:50357 } else if (!md5.empty()) {
358 base_name += FilePath::kExtensionSeparator;
359 base_name += util::EscapeCacheFileName(md5);
360 }
361 // For mounted archives the filename is formatted as resource_id.md5.mounted,
362 // i.e. resource_id.md5 is the base name and ".mounted" is the extension
363 if (file_origin == CACHED_FILE_MOUNTED) {
[email protected]a321b9632012-06-14 03:29:17364 DCHECK(sub_dir_type == CACHE_TYPE_PERSISTENT);
365 base_name += FilePath::kExtensionSeparator;
[email protected]ca5f6da2012-06-18 12:54:59366 base_name += util::kMountedArchiveFileExtension;
[email protected]32a7fc852012-06-08 17:25:50367 }
368 return GetCacheDirectoryPath(sub_dir_type).Append(base_name);
369}
370
[email protected]fcc92a52012-06-08 22:54:16371void GDataCache::AssertOnSequencedWorkerPool() {
[email protected]ddbf2052012-07-13 15:07:02372 DCHECK(!blocking_task_runner_ ||
373 blocking_task_runner_->RunsTasksOnCurrentThread());
[email protected]fcc92a52012-06-08 22:54:16374}
375
[email protected]01ba15f72012-06-09 00:41:05376bool GDataCache::IsUnderGDataCacheDirectory(const FilePath& path) const {
377 return cache_root_path_ == path || cache_root_path_.IsParent(path);
378}
379
[email protected]73f9c742012-06-15 07:37:13380void GDataCache::AddObserver(Observer* observer) {
381 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
382 observers_.AddObserver(observer);
383}
384
385void GDataCache::RemoveObserver(Observer* observer) {
386 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
387 observers_.RemoveObserver(observer);
388}
389
[email protected]4324fdc2012-06-29 05:32:48390void GDataCache::GetCacheEntryOnUIThread(
391 const std::string& resource_id,
392 const std::string& md5,
393 const GetCacheEntryCallback& callback) {
394 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
395
396 bool* success = new bool(false);
[email protected]fae353a2012-07-11 23:30:27397 GDataCacheEntry* cache_entry = new GDataCacheEntry;
[email protected]ddbf2052012-07-13 15:07:02398 blocking_task_runner_->PostTaskAndReply(
[email protected]4324fdc2012-06-29 05:32:48399 FROM_HERE,
400 base::Bind(&GDataCache::GetCacheEntryHelper,
401 base::Unretained(this),
402 resource_id,
403 md5,
404 success,
405 cache_entry),
406 base::Bind(&RunGetCacheEntryCallback,
407 callback,
408 base::Owned(success),
409 base::Owned(cache_entry)));
410}
411
[email protected]b83e5202012-06-27 07:50:24412void GDataCache::GetResourceIdsOfBacklogOnUIThread(
[email protected]85b62192012-06-29 19:56:38413 const GetResourceIdsOfBacklogCallback& callback) {
[email protected]8764a392012-06-20 06:43:08414 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
415
[email protected]b83e5202012-06-27 07:50:24416 std::vector<std::string>* to_fetch = new std::vector<std::string>;
417 std::vector<std::string>* to_upload = new std::vector<std::string>;
[email protected]ddbf2052012-07-13 15:07:02418 blocking_task_runner_->PostTaskAndReply(
[email protected]8764a392012-06-20 06:43:08419 FROM_HERE,
[email protected]b83e5202012-06-27 07:50:24420 base::Bind(&GDataCache::GetResourceIdsOfBacklog,
[email protected]8764a392012-06-20 06:43:08421 base::Unretained(this),
[email protected]b83e5202012-06-27 07:50:24422 to_fetch,
423 to_upload),
[email protected]85b62192012-06-29 19:56:38424 base::Bind(&RunGetResourceIdsOfBacklogCallback,
[email protected]8764a392012-06-20 06:43:08425 callback,
[email protected]b83e5202012-06-27 07:50:24426 base::Owned(to_fetch),
427 base::Owned(to_upload)));
[email protected]8764a392012-06-20 06:43:08428}
429
[email protected]85b62192012-06-29 19:56:38430void GDataCache::GetResourceIdsOfExistingPinnedFilesOnUIThread(
431 const GetResourceIdsCallback& callback) {
432 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
433
434 std::vector<std::string>* resource_ids = new std::vector<std::string>;
[email protected]ddbf2052012-07-13 15:07:02435 blocking_task_runner_->PostTaskAndReply(
[email protected]85b62192012-06-29 19:56:38436 FROM_HERE,
437 base::Bind(&GDataCache::GetResourceIdsOfExistingPinnedFiles,
438 base::Unretained(this),
439 resource_ids),
440 base::Bind(&RunGetResourceIdsCallback,
441 callback,
442 base::Owned(resource_ids)));
443}
444
[email protected]cd236432012-07-27 18:03:30445void GDataCache::GetResourceIdsOfAllFilesOnUIThread(
446 const GetResourceIdsCallback& callback) {
447 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
448
449 std::vector<std::string>* resource_ids = new std::vector<std::string>;
450 blocking_task_runner_->PostTaskAndReply(
451 FROM_HERE,
452 base::Bind(&GDataCache::GetResourceIdsOfAllFiles,
453 base::Unretained(this),
454 resource_ids),
455 base::Bind(&RunGetResourceIdsCallback,
456 callback,
457 base::Owned(resource_ids)));
458}
459
[email protected]a321b9632012-06-14 03:29:17460void GDataCache::FreeDiskSpaceIfNeededFor(int64 num_bytes,
461 bool* has_enough_space) {
462 AssertOnSequencedWorkerPool();
463
464 // Do nothing and return if we have enough space.
465 *has_enough_space = HasEnoughSpaceFor(num_bytes);
466 if (*has_enough_space)
467 return;
468
469 // Otherwise, try to free up the disk space.
470 DVLOG(1) << "Freeing up disk space for " << num_bytes;
471 // First remove temporary files from the cache map.
[email protected]ca5f6da2012-06-18 12:54:59472 metadata_->RemoveTemporaryFiles();
[email protected]a321b9632012-06-14 03:29:17473 // Then remove all files under "tmp" directory.
474 RemoveAllFiles(GetCacheDirectoryPath(GDataCache::CACHE_TYPE_TMP));
475
476 // Check the disk space again.
477 *has_enough_space = HasEnoughSpaceFor(num_bytes);
478}
479
[email protected]c960d222012-06-15 10:03:50480void GDataCache::GetFileOnUIThread(const std::string& resource_id,
481 const std::string& md5,
482 const GetFileFromCacheCallback& callback) {
483 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]a321b9632012-06-14 03:29:17484
[email protected]7f90e77c2012-07-17 09:35:31485 GDataFileError* error =
486 new GDataFileError(GDATA_FILE_OK);
[email protected]c960d222012-06-15 10:03:50487 FilePath* cache_file_path = new FilePath;
[email protected]ddbf2052012-07-13 15:07:02488 blocking_task_runner_->PostTaskAndReply(
[email protected]c960d222012-06-15 10:03:50489 FROM_HERE,
490 base::Bind(&GDataCache::GetFile,
491 base::Unretained(this),
492 resource_id,
493 md5,
494 error,
495 cache_file_path),
496 base::Bind(&RunGetFileFromCacheCallback,
497 callback,
498 base::Owned(error),
499 resource_id,
500 md5,
501 base::Owned(cache_file_path)));
[email protected]a321b9632012-06-14 03:29:17502}
503
[email protected]73f9c742012-06-15 07:37:13504void GDataCache::StoreOnUIThread(const std::string& resource_id,
505 const std::string& md5,
506 const FilePath& source_path,
507 FileOperationType file_operation_type,
508 const CacheOperationCallback& callback) {
509 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
510
[email protected]7f90e77c2012-07-17 09:35:31511 GDataFileError* error =
512 new GDataFileError(GDATA_FILE_OK);
[email protected]ddbf2052012-07-13 15:07:02513 blocking_task_runner_->PostTaskAndReply(
[email protected]73f9c742012-06-15 07:37:13514 FROM_HERE,
515 base::Bind(&GDataCache::Store,
516 base::Unretained(this),
517 resource_id,
518 md5,
519 source_path,
520 file_operation_type,
521 error),
522 base::Bind(&RunCacheOperationCallback,
523 callback,
524 base::Owned(error),
525 resource_id,
526 md5));
527}
528
529void GDataCache::PinOnUIThread(const std::string& resource_id,
[email protected]44c0584e2012-06-15 23:55:41530 const std::string& md5,
531 const CacheOperationCallback& callback) {
[email protected]73f9c742012-06-15 07:37:13532 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
533
[email protected]7f90e77c2012-07-17 09:35:31534 GDataFileError* error =
535 new GDataFileError(GDATA_FILE_OK);
[email protected]ddbf2052012-07-13 15:07:02536 blocking_task_runner_->PostTaskAndReply(
[email protected]73f9c742012-06-15 07:37:13537 FROM_HERE,
538 base::Bind(&GDataCache::Pin,
539 base::Unretained(this),
540 resource_id,
541 md5,
542 GDataCache::FILE_OPERATION_MOVE,
543 error),
544 base::Bind(&GDataCache::OnPinned,
545 ui_weak_ptr_,
546 base::Owned(error),
547 resource_id,
548 md5,
549 callback));
550}
551
552void GDataCache::UnpinOnUIThread(const std::string& resource_id,
553 const std::string& md5,
554 const CacheOperationCallback& callback) {
555 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]7f90e77c2012-07-17 09:35:31556 GDataFileError* error =
557 new GDataFileError(GDATA_FILE_OK);
[email protected]ddbf2052012-07-13 15:07:02558 blocking_task_runner_->PostTaskAndReply(
[email protected]73f9c742012-06-15 07:37:13559 FROM_HERE,
560 base::Bind(&GDataCache::Unpin,
561 base::Unretained(this),
562 resource_id,
563 md5,
564 GDataCache::FILE_OPERATION_MOVE,
565 error),
566 base::Bind(&GDataCache::OnUnpinned,
567 ui_weak_ptr_,
568 base::Owned(error),
569 resource_id,
570 md5,
571 callback));
572}
573
574void GDataCache::SetMountedStateOnUIThread(
575 const FilePath& file_path,
576 bool to_mount,
577 const SetMountedStateCallback& callback) {
578 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
579
[email protected]7f90e77c2012-07-17 09:35:31580 GDataFileError* error =
581 new GDataFileError(GDATA_FILE_OK);
[email protected]73f9c742012-06-15 07:37:13582 FilePath* cache_file_path = new FilePath;
[email protected]ddbf2052012-07-13 15:07:02583 blocking_task_runner_->PostTaskAndReply(
[email protected]73f9c742012-06-15 07:37:13584 FROM_HERE,
585 base::Bind(&GDataCache::SetMountedState,
586 base::Unretained(this),
587 file_path,
588 to_mount,
589 error,
590 cache_file_path),
591 base::Bind(&RunSetMountedStateCallback,
592 callback,
593 base::Owned(error),
594 base::Owned(cache_file_path)));
595}
596
[email protected]c960d222012-06-15 10:03:50597void GDataCache::MarkDirtyOnUIThread(const std::string& resource_id,
598 const std::string& md5,
599 const GetFileFromCacheCallback& callback) {
600 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]73f9c742012-06-15 07:37:13601
[email protected]7f90e77c2012-07-17 09:35:31602 GDataFileError* error =
603 new GDataFileError(GDATA_FILE_OK);
[email protected]c960d222012-06-15 10:03:50604 FilePath* cache_file_path = new FilePath;
[email protected]ddbf2052012-07-13 15:07:02605 blocking_task_runner_->PostTaskAndReply(
[email protected]c960d222012-06-15 10:03:50606 FROM_HERE,
607 base::Bind(&GDataCache::MarkDirty,
608 base::Unretained(this),
609 resource_id,
610 md5,
611 GDataCache::FILE_OPERATION_MOVE,
612 error,
613 cache_file_path),
614 base::Bind(&RunGetFileFromCacheCallback,
615 callback,
616 base::Owned(error),
617 resource_id,
618 md5,
619 base::Owned(cache_file_path)));
[email protected]73f9c742012-06-15 07:37:13620}
621
622void GDataCache::CommitDirtyOnUIThread(const std::string& resource_id,
623 const std::string& md5,
624 const CacheOperationCallback& callback) {
625 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
626
[email protected]7f90e77c2012-07-17 09:35:31627 GDataFileError* error = new GDataFileError(GDATA_FILE_OK);
[email protected]ddbf2052012-07-13 15:07:02628 blocking_task_runner_->PostTaskAndReply(
[email protected]73f9c742012-06-15 07:37:13629 FROM_HERE,
630 base::Bind(&GDataCache::CommitDirty,
631 base::Unretained(this),
632 resource_id,
633 md5,
634 GDataCache::FILE_OPERATION_MOVE,
635 error),
[email protected]d7664c22012-06-18 19:35:49636 base::Bind(&GDataCache::OnCommitDirty,
637 ui_weak_ptr_,
[email protected]73f9c742012-06-15 07:37:13638 base::Owned(error),
639 resource_id,
[email protected]d7664c22012-06-18 19:35:49640 md5,
641 callback));
[email protected]73f9c742012-06-15 07:37:13642}
643
644void GDataCache::ClearDirtyOnUIThread(const std::string& resource_id,
645 const std::string& md5,
646 const CacheOperationCallback& callback) {
647 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
648
[email protected]7f90e77c2012-07-17 09:35:31649 GDataFileError* error =
650 new GDataFileError(GDATA_FILE_OK);
[email protected]ddbf2052012-07-13 15:07:02651 blocking_task_runner_->PostTaskAndReply(
[email protected]73f9c742012-06-15 07:37:13652 FROM_HERE,
653 base::Bind(&GDataCache::ClearDirty,
654 base::Unretained(this),
655 resource_id,
656 md5,
657 GDataCache::FILE_OPERATION_MOVE,
658 error),
659 base::Bind(&RunCacheOperationCallback,
660 callback,
661 base::Owned(error),
662 resource_id,
663 md5));
664}
665
666void GDataCache::RemoveOnUIThread(const std::string& resource_id,
667 const CacheOperationCallback& callback) {
668 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
669
[email protected]7f90e77c2012-07-17 09:35:31670 GDataFileError* error =
671 new GDataFileError(GDATA_FILE_OK);
[email protected]73f9c742012-06-15 07:37:13672
[email protected]ddbf2052012-07-13 15:07:02673 blocking_task_runner_->PostTaskAndReply(
[email protected]73f9c742012-06-15 07:37:13674 FROM_HERE,
675 base::Bind(&GDataCache::Remove,
676 base::Unretained(this),
677 resource_id,
678 error),
679 base::Bind(&RunCacheOperationCallback,
680 callback,
681 base::Owned(error),
682 resource_id,
683 "" /* md5 */));
684}
685
686void GDataCache::RequestInitializeOnUIThread() {
687 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
688
[email protected]ddbf2052012-07-13 15:07:02689 blocking_task_runner_->PostTask(
[email protected]73f9c742012-06-15 07:37:13690 FROM_HERE,
[email protected]1ff88ea52012-06-20 04:54:00691 base::Bind(&GDataCache::Initialize, base::Unretained(this)));
[email protected]73f9c742012-06-15 07:37:13692}
693
[email protected]79c3752d2012-07-17 12:10:08694void GDataCache::ForceRescanOnUIThreadForTesting() {
695 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
696
697 blocking_task_runner_->PostTask(
698 FROM_HERE,
699 base::Bind(&GDataCache::ForceRescanForTesting, base::Unretained(this)));
700}
701
[email protected]b22f87f2012-07-12 10:53:17702bool GDataCache::GetCacheEntry(const std::string& resource_id,
703 const std::string& md5,
704 GDataCacheEntry* entry) {
705 DCHECK(entry);
[email protected]73f9c742012-06-15 07:37:13706 AssertOnSequencedWorkerPool();
[email protected]b22f87f2012-07-12 10:53:17707 return metadata_->GetCacheEntry(resource_id, md5, entry);
[email protected]73f9c742012-06-15 07:37:13708}
709
710// static
711GDataCache* GDataCache::CreateGDataCacheOnUIThread(
712 const FilePath& cache_root_path,
[email protected]ddbf2052012-07-13 15:07:02713 base::SequencedTaskRunner* blocking_task_runner) {
[email protected]73f9c742012-06-15 07:37:13714 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]ddbf2052012-07-13 15:07:02715 return new GDataCache(cache_root_path, blocking_task_runner);
[email protected]73f9c742012-06-15 07:37:13716}
717
718void GDataCache::DestroyOnUIThread() {
719 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
720
721 // Invalidate the weak pointer.
722 ui_weak_ptr_factory_.InvalidateWeakPtrs();
723
724 // Destroy myself on the blocking pool.
[email protected]ddbf2052012-07-13 15:07:02725 blocking_task_runner_->PostTask(
[email protected]73f9c742012-06-15 07:37:13726 FROM_HERE,
727 base::Bind(&GDataCache::Destroy,
728 base::Unretained(this)));
729}
730
[email protected]ca5f6da2012-06-18 12:54:59731void GDataCache::Initialize() {
732 AssertOnSequencedWorkerPool();
733
[email protected]79c3752d2012-07-17 12:10:08734 InitCachePaths(cache_paths_);
[email protected]f9e5cc02012-07-13 20:08:56735 metadata_ = GDataCacheMetadata::CreateGDataCacheMetadata(
736 blocking_task_runner_).Pass();
737 metadata_->Initialize(cache_paths_);
[email protected]ca5f6da2012-06-18 12:54:59738}
739
[email protected]73f9c742012-06-15 07:37:13740void GDataCache::Destroy() {
741 AssertOnSequencedWorkerPool();
742 delete this;
743}
744
[email protected]79c3752d2012-07-17 12:10:08745void GDataCache::ForceRescanForTesting() {
746 AssertOnSequencedWorkerPool();
747 metadata_->ForceRescanForTesting(cache_paths_);
748}
749
[email protected]b83e5202012-06-27 07:50:24750void GDataCache::GetResourceIdsOfBacklog(
751 std::vector<std::string>* to_fetch,
752 std::vector<std::string>* to_upload) {
[email protected]8764a392012-06-20 06:43:08753 AssertOnSequencedWorkerPool();
[email protected]b83e5202012-06-27 07:50:24754 DCHECK(to_fetch);
755 DCHECK(to_upload);
[email protected]8764a392012-06-20 06:43:08756
[email protected]b83e5202012-06-27 07:50:24757 metadata_->Iterate(base::Bind(&CollectBacklog, to_fetch, to_upload));
[email protected]8764a392012-06-20 06:43:08758}
759
[email protected]85b62192012-06-29 19:56:38760void GDataCache::GetResourceIdsOfExistingPinnedFiles(
761 std::vector<std::string>* resource_ids) {
762 AssertOnSequencedWorkerPool();
763 DCHECK(resource_ids);
764
765 metadata_->Iterate(base::Bind(&CollectExistingPinnedFile, resource_ids));
766}
767
[email protected]cd236432012-07-27 18:03:30768void GDataCache::GetResourceIdsOfAllFiles(
769 std::vector<std::string>* resource_ids) {
770 AssertOnSequencedWorkerPool();
771 DCHECK(resource_ids);
772
773 metadata_->Iterate(base::Bind(&CollectAnyFile, resource_ids));
774}
775
[email protected]c960d222012-06-15 10:03:50776void GDataCache::GetFile(const std::string& resource_id,
777 const std::string& md5,
[email protected]7f90e77c2012-07-17 09:35:31778 GDataFileError* error,
[email protected]c960d222012-06-15 10:03:50779 FilePath* cache_file_path) {
780 AssertOnSequencedWorkerPool();
781 DCHECK(error);
782 DCHECK(cache_file_path);
783
[email protected]b22f87f2012-07-12 10:53:17784 GDataCacheEntry cache_entry;
785 if (GetCacheEntry(resource_id, md5, &cache_entry) &&
[email protected]02821102012-07-12 20:19:17786 cache_entry.is_present()) {
[email protected]c960d222012-06-15 10:03:50787 CachedFileOrigin file_origin;
[email protected]02821102012-07-12 20:19:17788 if (cache_entry.is_mounted()) {
[email protected]c960d222012-06-15 10:03:50789 file_origin = CACHED_FILE_MOUNTED;
[email protected]02821102012-07-12 20:19:17790 } else if (cache_entry.is_dirty()) {
[email protected]c960d222012-06-15 10:03:50791 file_origin = CACHED_FILE_LOCALLY_MODIFIED;
792 } else {
793 file_origin = CACHED_FILE_FROM_SERVER;
794 }
795 *cache_file_path = GetCacheFilePath(
796 resource_id,
797 md5,
[email protected]b22f87f2012-07-12 10:53:17798 GetSubDirectoryType(cache_entry),
[email protected]c960d222012-06-15 10:03:50799 file_origin);
[email protected]7f90e77c2012-07-17 09:35:31800 *error = GDATA_FILE_OK;
[email protected]c960d222012-06-15 10:03:50801 } else {
[email protected]7f90e77c2012-07-17 09:35:31802 *error = GDATA_FILE_ERROR_NOT_FOUND;
[email protected]c960d222012-06-15 10:03:50803 }
804}
805
[email protected]a321b9632012-06-14 03:29:17806void GDataCache::Store(const std::string& resource_id,
807 const std::string& md5,
808 const FilePath& source_path,
809 FileOperationType file_operation_type,
[email protected]7f90e77c2012-07-17 09:35:31810 GDataFileError* error) {
[email protected]a321b9632012-06-14 03:29:17811 AssertOnSequencedWorkerPool();
812 DCHECK(error);
813
814 FilePath dest_path;
815 FilePath symlink_path;
[email protected]a321b9632012-06-14 03:29:17816 CacheSubDirectoryType sub_dir_type = CACHE_TYPE_TMP;
817
[email protected]a321b9632012-06-14 03:29:17818 // If file was previously pinned, store it in persistent dir and create
819 // symlink in pinned dir.
[email protected]b22f87f2012-07-12 10:53:17820 GDataCacheEntry cache_entry;
821 if (GetCacheEntry(resource_id, md5, &cache_entry)) { // File exists in cache.
[email protected]a321b9632012-06-14 03:29:17822 // If file is dirty or mounted, return error.
[email protected]02821102012-07-12 20:19:17823 if (cache_entry.is_dirty() || cache_entry.is_mounted()) {
[email protected]a321b9632012-06-14 03:29:17824 LOG(WARNING) << "Can't store a file to replace a "
[email protected]02821102012-07-12 20:19:17825 << (cache_entry.is_dirty() ? "dirty" : "mounted")
[email protected]a321b9632012-06-14 03:29:17826 << " file: res_id=" << resource_id
827 << ", md5=" << md5;
[email protected]7f90e77c2012-07-17 09:35:31828 *error = GDATA_FILE_ERROR_IN_USE;
[email protected]a321b9632012-06-14 03:29:17829 return;
830 }
831
[email protected]a321b9632012-06-14 03:29:17832 // If file is pinned, determines destination path.
[email protected]02821102012-07-12 20:19:17833 if (cache_entry.is_pinned()) {
[email protected]a321b9632012-06-14 03:29:17834 sub_dir_type = CACHE_TYPE_PERSISTENT;
835 dest_path = GetCacheFilePath(resource_id, md5, sub_dir_type,
836 CACHED_FILE_FROM_SERVER);
837 symlink_path = GetCacheFilePath(
838 resource_id, std::string(), CACHE_TYPE_PINNED,
839 CACHED_FILE_FROM_SERVER);
840 }
841 }
842
843 // File wasn't pinned or doesn't exist in cache, store in tmp dir.
844 if (dest_path.empty()) {
845 DCHECK_EQ(CACHE_TYPE_TMP, sub_dir_type);
846 dest_path = GetCacheFilePath(resource_id, md5, sub_dir_type,
847 CACHED_FILE_FROM_SERVER);
848 }
849
850 *error = ModifyCacheState(
851 source_path,
852 dest_path,
853 file_operation_type,
854 symlink_path,
855 !symlink_path.empty()); // create symlink
856
857 // Determine search pattern for stale filenames corrresponding to resource_id,
858 // either "<resource_id>*" or "<resource_id>.*".
859 FilePath stale_filenames_pattern;
860 if (md5.empty()) {
861 // No md5 means no extension, append '*' after base name, i.e.
862 // "<resource_id>*".
863 // Cannot call |dest_path|.ReplaceExtension when there's no md5 extension:
864 // if base name of |dest_path| (i.e. escaped resource_id) contains the
865 // extension separator '.', ReplaceExtension will remove it and everything
866 // after it. The result will be nothing like the escaped resource_id.
[email protected]ca5f6da2012-06-18 12:54:59867 stale_filenames_pattern = FilePath(dest_path.value() + util::kWildCard);
[email protected]a321b9632012-06-14 03:29:17868 } else {
869 // Replace md5 extension with '*' i.e. "<resource_id>.*".
870 // Note that ReplaceExtension automatically prefixes the extension with the
871 // extension separator '.'.
[email protected]ca5f6da2012-06-18 12:54:59872 stale_filenames_pattern = dest_path.ReplaceExtension(util::kWildCard);
[email protected]a321b9632012-06-14 03:29:17873 }
874
875 // Delete files that match |stale_filenames_pattern| except for |dest_path|.
876 DeleteFilesSelectively(stale_filenames_pattern, dest_path);
877
[email protected]7f90e77c2012-07-17 09:35:31878 if (*error == GDATA_FILE_OK) {
[email protected]a321b9632012-06-14 03:29:17879 // Now that file operations have completed, update cache map.
[email protected]b22f87f2012-07-12 10:53:17880 cache_entry.set_md5(md5);
[email protected]02821102012-07-12 20:19:17881 cache_entry.set_is_present(true);
882 cache_entry.set_is_persistent(sub_dir_type == CACHE_TYPE_PERSISTENT);
[email protected]b22f87f2012-07-12 10:53:17883 metadata_->AddOrUpdateCacheEntry(resource_id, cache_entry);
[email protected]a321b9632012-06-14 03:29:17884 }
885}
886
887void GDataCache::Pin(const std::string& resource_id,
888 const std::string& md5,
889 FileOperationType file_operation_type,
[email protected]7f90e77c2012-07-17 09:35:31890 GDataFileError* error) {
[email protected]a321b9632012-06-14 03:29:17891 AssertOnSequencedWorkerPool();
892 DCHECK(error);
893
894 FilePath source_path;
895 FilePath dest_path;
896 FilePath symlink_path;
897 bool create_symlink = true;
[email protected]a321b9632012-06-14 03:29:17898 CacheSubDirectoryType sub_dir_type = CACHE_TYPE_PERSISTENT;
899
[email protected]b22f87f2012-07-12 10:53:17900 GDataCacheEntry cache_entry;
901 if (!GetCacheEntry(resource_id, md5, &cache_entry)) {
902 // Entry does not exist in cache.
[email protected]a321b9632012-06-14 03:29:17903 // Set both |dest_path| and |source_path| to /dev/null, so that:
904 // 1) ModifyCacheState won't move files when |source_path| and |dest_path|
905 // are the same.
906 // 2) symlinks to /dev/null will be picked up by GDataSyncClient to download
907 // pinned files that don't exist in cache.
[email protected]30d9dda2012-06-30 05:56:28908 dest_path = FilePath::FromUTF8Unsafe(util::kSymLinkToDevNull);
[email protected]a321b9632012-06-14 03:29:17909 source_path = dest_path;
910
[email protected]2a6fe6e2012-07-10 06:01:04911 // Set sub_dir_type to TMP. The file will be first downloaded in 'tmp',
912 // then moved to 'persistent'.
913 sub_dir_type = CACHE_TYPE_TMP;
[email protected]109eb5c2012-07-12 03:20:05914 } else { // File exists in cache, determines destination path.
[email protected]a321b9632012-06-14 03:29:17915 // Determine source and destination paths.
916
917 // If file is dirty or mounted, don't move it, so determine |dest_path| and
918 // set |source_path| the same, because ModifyCacheState only moves files if
919 // source and destination are different.
[email protected]02821102012-07-12 20:19:17920 if (cache_entry.is_dirty() || cache_entry.is_mounted()) {
921 DCHECK(cache_entry.is_persistent());
[email protected]a321b9632012-06-14 03:29:17922 dest_path = GetCacheFilePath(resource_id,
923 md5,
[email protected]b22f87f2012-07-12 10:53:17924 GetSubDirectoryType(cache_entry),
[email protected]a321b9632012-06-14 03:29:17925 CACHED_FILE_LOCALLY_MODIFIED);
926 source_path = dest_path;
927 } else {
928 // Gets the current path of the file in cache.
929 source_path = GetCacheFilePath(resource_id,
930 md5,
[email protected]b22f87f2012-07-12 10:53:17931 GetSubDirectoryType(cache_entry),
[email protected]a321b9632012-06-14 03:29:17932 CACHED_FILE_FROM_SERVER);
933
934 // If file was pinned before but actual file blob doesn't exist in cache:
935 // - don't need to move the file, so set |dest_path| to |source_path|,
936 // because ModifyCacheState only moves files if source and destination
937 // are different
938 // - don't create symlink since it already exists.
[email protected]02821102012-07-12 20:19:17939 if (!cache_entry.is_present()) {
[email protected]a321b9632012-06-14 03:29:17940 dest_path = source_path;
941 create_symlink = false;
942 } else { // File exists, move it to persistent dir.
943 dest_path = GetCacheFilePath(resource_id,
944 md5,
945 CACHE_TYPE_PERSISTENT,
946 CACHED_FILE_FROM_SERVER);
947 }
948 }
949 }
950
951 // Create symlink in pinned dir.
952 if (create_symlink) {
953 symlink_path = GetCacheFilePath(resource_id,
954 std::string(),
955 CACHE_TYPE_PINNED,
956 CACHED_FILE_FROM_SERVER);
957 }
958
959 *error = ModifyCacheState(source_path,
960 dest_path,
961 file_operation_type,
962 symlink_path,
963 create_symlink);
964
[email protected]7f90e77c2012-07-17 09:35:31965 if (*error == GDATA_FILE_OK) {
[email protected]a321b9632012-06-14 03:29:17966 // Now that file operations have completed, update cache map.
[email protected]b22f87f2012-07-12 10:53:17967 cache_entry.set_md5(md5);
[email protected]02821102012-07-12 20:19:17968 cache_entry.set_is_pinned(true);
969 cache_entry.set_is_persistent(sub_dir_type == CACHE_TYPE_PERSISTENT);
[email protected]b22f87f2012-07-12 10:53:17970 metadata_->AddOrUpdateCacheEntry(resource_id, cache_entry);
[email protected]a321b9632012-06-14 03:29:17971 }
972}
973
974void GDataCache::Unpin(const std::string& resource_id,
975 const std::string& md5,
976 FileOperationType file_operation_type,
[email protected]7f90e77c2012-07-17 09:35:31977 GDataFileError* error) {
[email protected]a321b9632012-06-14 03:29:17978 AssertOnSequencedWorkerPool();
979 DCHECK(error);
980
[email protected]a321b9632012-06-14 03:29:17981 // Unpinning a file means its entry must exist in cache.
[email protected]b22f87f2012-07-12 10:53:17982 GDataCacheEntry cache_entry;
983 if (!GetCacheEntry(resource_id, md5, &cache_entry)) {
[email protected]a321b9632012-06-14 03:29:17984 LOG(WARNING) << "Can't unpin a file that wasn't pinned or cached: res_id="
985 << resource_id
986 << ", md5=" << md5;
[email protected]7f90e77c2012-07-17 09:35:31987 *error = GDATA_FILE_ERROR_NOT_FOUND;
[email protected]a321b9632012-06-14 03:29:17988 return;
989 }
990
991 // Entry exists in cache, determines source and destination paths.
992
993 FilePath source_path;
994 FilePath dest_path;
995 CacheSubDirectoryType sub_dir_type = CACHE_TYPE_TMP;
996
997 // If file is dirty or mounted, don't move it, so determine |dest_path| and
998 // set |source_path| the same, because ModifyCacheState moves files if source
999 // and destination are different.
[email protected]02821102012-07-12 20:19:171000 if (cache_entry.is_dirty() || cache_entry.is_mounted()) {
[email protected]a321b9632012-06-14 03:29:171001 sub_dir_type = CACHE_TYPE_PERSISTENT;
[email protected]02821102012-07-12 20:19:171002 DCHECK(cache_entry.is_persistent());
[email protected]a321b9632012-06-14 03:29:171003 dest_path = GetCacheFilePath(resource_id,
1004 md5,
[email protected]b22f87f2012-07-12 10:53:171005 GetSubDirectoryType(cache_entry),
[email protected]a321b9632012-06-14 03:29:171006 CACHED_FILE_LOCALLY_MODIFIED);
1007 source_path = dest_path;
1008 } else {
1009 // Gets the current path of the file in cache.
1010 source_path = GetCacheFilePath(resource_id,
1011 md5,
[email protected]b22f87f2012-07-12 10:53:171012 GetSubDirectoryType(cache_entry),
[email protected]a321b9632012-06-14 03:29:171013 CACHED_FILE_FROM_SERVER);
1014
1015 // If file was pinned but actual file blob still doesn't exist in cache,
1016 // don't need to move the file, so set |dest_path| to |source_path|, because
1017 // ModifyCacheState only moves files if source and destination are
1018 // different.
[email protected]02821102012-07-12 20:19:171019 if (!cache_entry.is_present()) {
[email protected]a321b9632012-06-14 03:29:171020 dest_path = source_path;
1021 } else { // File exists, move it to tmp dir.
1022 dest_path = GetCacheFilePath(resource_id, md5,
1023 CACHE_TYPE_TMP,
1024 CACHED_FILE_FROM_SERVER);
1025 }
1026 }
1027
1028 // If file was pinned, get absolute path of symlink in pinned dir so as to
1029 // remove it.
1030 FilePath symlink_path;
[email protected]02821102012-07-12 20:19:171031 if (cache_entry.is_pinned()) {
[email protected]a321b9632012-06-14 03:29:171032 symlink_path = GetCacheFilePath(resource_id,
1033 std::string(),
1034 CACHE_TYPE_PINNED,
1035 CACHED_FILE_FROM_SERVER);
1036 }
1037
1038 *error = ModifyCacheState(
1039 source_path,
1040 dest_path,
1041 file_operation_type,
1042 symlink_path, // This will be deleted if it exists.
1043 false /* don't create symlink*/);
1044
[email protected]7f90e77c2012-07-17 09:35:311045 if (*error == GDATA_FILE_OK) {
[email protected]a321b9632012-06-14 03:29:171046 // Now that file operations have completed, update cache map.
[email protected]02821102012-07-12 20:19:171047 if (cache_entry.is_present()) {
[email protected]48477fe2012-07-12 17:45:081048 cache_entry.set_md5(md5);
[email protected]02821102012-07-12 20:19:171049 cache_entry.set_is_pinned(false);
1050 cache_entry.set_is_persistent(sub_dir_type == CACHE_TYPE_PERSISTENT);
[email protected]48477fe2012-07-12 17:45:081051 metadata_->AddOrUpdateCacheEntry(resource_id, cache_entry);
[email protected]3423871a2012-07-12 00:41:271052 } else {
1053 // Remove the existing entry if we are unpinning a non-present file.
1054 metadata_->RemoveCacheEntry(resource_id);
1055 }
[email protected]a321b9632012-06-14 03:29:171056 }
1057}
1058
1059void GDataCache::SetMountedState(const FilePath& file_path,
1060 bool to_mount,
[email protected]7f90e77c2012-07-17 09:35:311061 GDataFileError *error,
[email protected]a321b9632012-06-14 03:29:171062 FilePath* cache_file_path) {
1063 AssertOnSequencedWorkerPool();
1064 DCHECK(error);
1065 DCHECK(cache_file_path);
1066
1067 // Parse file path to obtain resource_id, md5 and extra_extension.
1068 std::string resource_id;
1069 std::string md5;
1070 std::string extra_extension;
1071 util::ParseCacheFilePath(file_path, &resource_id, &md5, &extra_extension);
1072 // The extra_extension shall be ".mounted" iff we're unmounting.
[email protected]ca5f6da2012-06-18 12:54:591073 DCHECK(!to_mount == (extra_extension == util::kMountedArchiveFileExtension));
[email protected]a321b9632012-06-14 03:29:171074
1075 // Get cache entry associated with the resource_id and md5
[email protected]b22f87f2012-07-12 10:53:171076 GDataCacheEntry cache_entry;
1077 if (!GetCacheEntry(resource_id, md5, &cache_entry)) {
[email protected]7f90e77c2012-07-17 09:35:311078 *error = GDATA_FILE_ERROR_NOT_FOUND;
[email protected]a321b9632012-06-14 03:29:171079 return;
1080 }
[email protected]02821102012-07-12 20:19:171081 if (to_mount == cache_entry.is_mounted()) {
[email protected]7f90e77c2012-07-17 09:35:311082 *error = GDATA_FILE_ERROR_INVALID_OPERATION;
[email protected]a321b9632012-06-14 03:29:171083 return;
1084 }
1085
1086 // Get the subdir type and path for the unmounted state.
1087 CacheSubDirectoryType unmounted_subdir =
[email protected]02821102012-07-12 20:19:171088 cache_entry.is_pinned() ? CACHE_TYPE_PERSISTENT : CACHE_TYPE_TMP;
[email protected]a321b9632012-06-14 03:29:171089 FilePath unmounted_path = GetCacheFilePath(
1090 resource_id, md5, unmounted_subdir, CACHED_FILE_FROM_SERVER);
1091
1092 // Get the subdir type and path for the mounted state.
1093 CacheSubDirectoryType mounted_subdir = CACHE_TYPE_PERSISTENT;
1094 FilePath mounted_path = GetCacheFilePath(
1095 resource_id, md5, mounted_subdir, CACHED_FILE_MOUNTED);
1096
1097 // Determine the source and destination paths for moving the cache blob.
1098 FilePath source_path;
1099 CacheSubDirectoryType dest_subdir;
[email protected]a321b9632012-06-14 03:29:171100 if (to_mount) {
1101 source_path = unmounted_path;
1102 *cache_file_path = mounted_path;
1103 dest_subdir = mounted_subdir;
[email protected]02821102012-07-12 20:19:171104 cache_entry.set_is_mounted(true);
[email protected]a321b9632012-06-14 03:29:171105 } else {
1106 source_path = mounted_path;
1107 *cache_file_path = unmounted_path;
1108 dest_subdir = unmounted_subdir;
[email protected]02821102012-07-12 20:19:171109 cache_entry.set_is_mounted(false);
[email protected]a321b9632012-06-14 03:29:171110 }
1111
1112 // Move cache blob from source path to destination path.
1113 *error = ModifyCacheState(source_path, *cache_file_path,
1114 FILE_OPERATION_MOVE, FilePath(), false);
[email protected]7f90e77c2012-07-17 09:35:311115 if (*error == GDATA_FILE_OK) {
[email protected]a321b9632012-06-14 03:29:171116 // Now that cache operation is complete, update cache map
[email protected]48477fe2012-07-12 17:45:081117 cache_entry.set_md5(md5);
[email protected]02821102012-07-12 20:19:171118 cache_entry.set_is_persistent(dest_subdir == CACHE_TYPE_PERSISTENT);
[email protected]48477fe2012-07-12 17:45:081119 metadata_->AddOrUpdateCacheEntry(resource_id, cache_entry);
[email protected]a321b9632012-06-14 03:29:171120 }
1121}
1122
[email protected]c960d222012-06-15 10:03:501123void GDataCache::MarkDirty(const std::string& resource_id,
1124 const std::string& md5,
1125 FileOperationType file_operation_type,
[email protected]7f90e77c2012-07-17 09:35:311126 GDataFileError* error,
[email protected]c960d222012-06-15 10:03:501127 FilePath* cache_file_path) {
1128 AssertOnSequencedWorkerPool();
1129 DCHECK(error);
1130 DCHECK(cache_file_path);
1131
1132 // If file has already been marked dirty in previous instance of chrome, we
1133 // would have lost the md5 info during cache initialization, because the file
1134 // would have been renamed to .local extension.
1135 // So, search for entry in cache without comparing md5.
[email protected]c960d222012-06-15 10:03:501136
1137 // Marking a file dirty means its entry and actual file blob must exist in
1138 // cache.
[email protected]b22f87f2012-07-12 10:53:171139 GDataCacheEntry cache_entry;
1140 if (!GetCacheEntry(resource_id, std::string(), &cache_entry) ||
[email protected]02821102012-07-12 20:19:171141 !cache_entry.is_present()) {
[email protected]c960d222012-06-15 10:03:501142 LOG(WARNING) << "Can't mark dirty a file that wasn't cached: res_id="
1143 << resource_id
1144 << ", md5=" << md5;
[email protected]7f90e77c2012-07-17 09:35:311145 *error = GDATA_FILE_ERROR_NOT_FOUND;
[email protected]c960d222012-06-15 10:03:501146 return;
1147 }
1148
1149 // If a file is already dirty (i.e. MarkDirtyInCache was called before),
1150 // delete outgoing symlink if it exists.
1151 // TODO(benchan): We should only delete outgoing symlink if file is currently
1152 // not being uploaded. However, for now, cache doesn't know if uploading of a
1153 // file is in progress. Per zel, the upload process should be canceled before
1154 // MarkDirtyInCache is called again.
[email protected]02821102012-07-12 20:19:171155 if (cache_entry.is_dirty()) {
[email protected]c960d222012-06-15 10:03:501156 // The file must be in persistent dir.
[email protected]02821102012-07-12 20:19:171157 DCHECK(cache_entry.is_persistent());
[email protected]c960d222012-06-15 10:03:501158
1159 // Determine symlink path in outgoing dir, so as to remove it.
1160 FilePath symlink_path = GetCacheFilePath(
1161 resource_id,
1162 std::string(),
1163 CACHE_TYPE_OUTGOING,
1164 CACHED_FILE_FROM_SERVER);
1165
1166 // We're not moving files here, so simply use empty FilePath for both
1167 // |source_path| and |dest_path| because ModifyCacheState only move files
1168 // if source and destination are different.
1169 *error = ModifyCacheState(
1170 FilePath(), // non-applicable source path
1171 FilePath(), // non-applicable dest path
1172 file_operation_type,
1173 symlink_path,
1174 false /* don't create symlink */);
1175
1176 // Determine current path of dirty file.
[email protected]7f90e77c2012-07-17 09:35:311177 if (*error == GDATA_FILE_OK) {
[email protected]c960d222012-06-15 10:03:501178 *cache_file_path = GetCacheFilePath(
1179 resource_id,
1180 md5,
1181 CACHE_TYPE_PERSISTENT,
1182 CACHED_FILE_LOCALLY_MODIFIED);
1183 }
1184 return;
1185 }
1186
1187 // Move file to persistent dir with new .local extension.
1188
1189 // Get the current path of the file in cache.
1190 FilePath source_path = GetCacheFilePath(
1191 resource_id,
1192 md5,
[email protected]b22f87f2012-07-12 10:53:171193 GetSubDirectoryType(cache_entry),
[email protected]c960d222012-06-15 10:03:501194 CACHED_FILE_FROM_SERVER);
1195
1196 // Determine destination path.
[email protected]3dc88ee2012-07-11 21:04:111197 const CacheSubDirectoryType sub_dir_type = CACHE_TYPE_PERSISTENT;
[email protected]c960d222012-06-15 10:03:501198 *cache_file_path = GetCacheFilePath(resource_id,
1199 md5,
1200 sub_dir_type,
1201 CACHED_FILE_LOCALLY_MODIFIED);
1202
1203 // If file is pinned, update symlink in pinned dir.
1204 FilePath symlink_path;
[email protected]02821102012-07-12 20:19:171205 if (cache_entry.is_pinned()) {
[email protected]c960d222012-06-15 10:03:501206 symlink_path = GetCacheFilePath(resource_id,
1207 std::string(),
1208 CACHE_TYPE_PINNED,
1209 CACHED_FILE_FROM_SERVER);
1210 }
1211
1212 *error = ModifyCacheState(
1213 source_path,
1214 *cache_file_path,
1215 file_operation_type,
1216 symlink_path,
1217 !symlink_path.empty() /* create symlink */);
1218
[email protected]7f90e77c2012-07-17 09:35:311219 if (*error == GDATA_FILE_OK) {
[email protected]c960d222012-06-15 10:03:501220 // Now that file operations have completed, update cache map.
[email protected]48477fe2012-07-12 17:45:081221 cache_entry.set_md5(md5);
[email protected]02821102012-07-12 20:19:171222 cache_entry.set_is_dirty(true);
1223 cache_entry.set_is_persistent(sub_dir_type == CACHE_TYPE_PERSISTENT);
[email protected]48477fe2012-07-12 17:45:081224 metadata_->AddOrUpdateCacheEntry(resource_id, cache_entry);
[email protected]c960d222012-06-15 10:03:501225 }
1226}
1227
[email protected]a321b9632012-06-14 03:29:171228void GDataCache::CommitDirty(const std::string& resource_id,
1229 const std::string& md5,
1230 FileOperationType file_operation_type,
[email protected]7f90e77c2012-07-17 09:35:311231 GDataFileError* error) {
[email protected]a321b9632012-06-14 03:29:171232 AssertOnSequencedWorkerPool();
1233 DCHECK(error);
1234
1235 // If file has already been marked dirty in previous instance of chrome, we
1236 // would have lost the md5 info during cache initialization, because the file
1237 // would have been renamed to .local extension.
1238 // So, search for entry in cache without comparing md5.
[email protected]a321b9632012-06-14 03:29:171239
1240 // Committing a file dirty means its entry and actual file blob must exist in
1241 // cache.
[email protected]b22f87f2012-07-12 10:53:171242 GDataCacheEntry cache_entry;
1243 if (!GetCacheEntry(resource_id, std::string(), &cache_entry) ||
[email protected]02821102012-07-12 20:19:171244 !cache_entry.is_present()) {
[email protected]a321b9632012-06-14 03:29:171245 LOG(WARNING) << "Can't commit dirty a file that wasn't cached: res_id="
1246 << resource_id
1247 << ", md5=" << md5;
[email protected]7f90e77c2012-07-17 09:35:311248 *error = GDATA_FILE_ERROR_NOT_FOUND;
[email protected]a321b9632012-06-14 03:29:171249 return;
1250 }
1251
1252 // If a file is not dirty (it should have been marked dirty via
1253 // MarkDirtyInCache), committing it dirty is an invalid operation.
[email protected]02821102012-07-12 20:19:171254 if (!cache_entry.is_dirty()) {
[email protected]a321b9632012-06-14 03:29:171255 LOG(WARNING) << "Can't commit a non-dirty file: res_id="
1256 << resource_id
1257 << ", md5=" << md5;
[email protected]7f90e77c2012-07-17 09:35:311258 *error = GDATA_FILE_ERROR_INVALID_OPERATION;
[email protected]a321b9632012-06-14 03:29:171259 return;
1260 }
1261
1262 // Dirty files must be in persistent dir.
[email protected]02821102012-07-12 20:19:171263 DCHECK(cache_entry.is_persistent());
[email protected]a321b9632012-06-14 03:29:171264
1265 // Create symlink in outgoing dir.
1266 FilePath symlink_path = GetCacheFilePath(resource_id,
1267 std::string(),
1268 CACHE_TYPE_OUTGOING,
1269 CACHED_FILE_FROM_SERVER);
1270
1271 // Get target path of symlink i.e. current path of the file in cache.
1272 FilePath target_path = GetCacheFilePath(resource_id,
1273 md5,
[email protected]b22f87f2012-07-12 10:53:171274 GetSubDirectoryType(cache_entry),
[email protected]a321b9632012-06-14 03:29:171275 CACHED_FILE_LOCALLY_MODIFIED);
1276
1277 // Since there's no need to move files, use |target_path| for both
1278 // |source_path| and |dest_path|, because ModifyCacheState only moves files
1279 // if source and destination are different.
1280 *error = ModifyCacheState(target_path, // source
1281 target_path, // destination
1282 file_operation_type,
1283 symlink_path,
1284 true /* create symlink */);
1285}
1286
1287void GDataCache::ClearDirty(const std::string& resource_id,
1288 const std::string& md5,
1289 FileOperationType file_operation_type,
[email protected]7f90e77c2012-07-17 09:35:311290 GDataFileError* error) {
[email protected]a321b9632012-06-14 03:29:171291 AssertOnSequencedWorkerPool();
1292 DCHECK(error);
1293
1294 // |md5| is the new .<md5> extension to rename the file to.
1295 // So, search for entry in cache without comparing md5.
[email protected]b22f87f2012-07-12 10:53:171296 GDataCacheEntry cache_entry;
[email protected]a321b9632012-06-14 03:29:171297
1298 // Clearing a dirty file means its entry and actual file blob must exist in
1299 // cache.
[email protected]b22f87f2012-07-12 10:53:171300 if (!GetCacheEntry(resource_id, std::string(), &cache_entry) ||
[email protected]02821102012-07-12 20:19:171301 !cache_entry.is_present()) {
[email protected]a321b9632012-06-14 03:29:171302 LOG(WARNING) << "Can't clear dirty state of a file that wasn't cached: "
1303 << "res_id=" << resource_id
1304 << ", md5=" << md5;
[email protected]7f90e77c2012-07-17 09:35:311305 *error = GDATA_FILE_ERROR_NOT_FOUND;
[email protected]a321b9632012-06-14 03:29:171306 return;
1307 }
1308
1309 // If a file is not dirty (it should have been marked dirty via
1310 // MarkDirtyInCache), clearing its dirty state is an invalid operation.
[email protected]02821102012-07-12 20:19:171311 if (!cache_entry.is_dirty()) {
[email protected]a321b9632012-06-14 03:29:171312 LOG(WARNING) << "Can't clear dirty state of a non-dirty file: res_id="
1313 << resource_id
1314 << ", md5=" << md5;
[email protected]7f90e77c2012-07-17 09:35:311315 *error = GDATA_FILE_ERROR_INVALID_OPERATION;
[email protected]a321b9632012-06-14 03:29:171316 return;
1317 }
1318
1319 // File must be dirty and hence in persistent dir.
[email protected]02821102012-07-12 20:19:171320 DCHECK(cache_entry.is_persistent());
[email protected]a321b9632012-06-14 03:29:171321
1322 // Get the current path of the file in cache.
1323 FilePath source_path = GetCacheFilePath(resource_id,
1324 md5,
[email protected]b22f87f2012-07-12 10:53:171325 GetSubDirectoryType(cache_entry),
[email protected]a321b9632012-06-14 03:29:171326 CACHED_FILE_LOCALLY_MODIFIED);
1327
1328 // Determine destination path.
1329 // If file is pinned, move it to persistent dir with .md5 extension;
1330 // otherwise, move it to tmp dir with .md5 extension.
[email protected]3dc88ee2012-07-11 21:04:111331 const CacheSubDirectoryType sub_dir_type =
[email protected]02821102012-07-12 20:19:171332 cache_entry.is_pinned() ? CACHE_TYPE_PERSISTENT : CACHE_TYPE_TMP;
[email protected]a321b9632012-06-14 03:29:171333 FilePath dest_path = GetCacheFilePath(resource_id,
1334 md5,
1335 sub_dir_type,
1336 CACHED_FILE_FROM_SERVER);
1337
1338 // Delete symlink in outgoing dir.
1339 FilePath symlink_path = GetCacheFilePath(resource_id,
1340 std::string(),
1341 CACHE_TYPE_OUTGOING,
1342 CACHED_FILE_FROM_SERVER);
1343
1344 *error = ModifyCacheState(source_path,
1345 dest_path,
1346 file_operation_type,
1347 symlink_path,
1348 false /* don't create symlink */);
1349
1350 // If file is pinned, update symlink in pinned dir.
[email protected]7f90e77c2012-07-17 09:35:311351 if (*error == GDATA_FILE_OK && cache_entry.is_pinned()) {
[email protected]a321b9632012-06-14 03:29:171352 symlink_path = GetCacheFilePath(resource_id,
1353 std::string(),
1354 CACHE_TYPE_PINNED,
1355 CACHED_FILE_FROM_SERVER);
1356
1357 // Since there's no moving of files here, use |dest_path| for both
1358 // |source_path| and |dest_path|, because ModifyCacheState only moves files
1359 // if source and destination are different.
1360 *error = ModifyCacheState(dest_path, // source path
1361 dest_path, // destination path
1362 file_operation_type,
1363 symlink_path,
1364 true /* create symlink */);
1365 }
1366
[email protected]7f90e77c2012-07-17 09:35:311367 if (*error == GDATA_FILE_OK) {
[email protected]a321b9632012-06-14 03:29:171368 // Now that file operations have completed, update cache map.
[email protected]48477fe2012-07-12 17:45:081369 cache_entry.set_md5(md5);
[email protected]02821102012-07-12 20:19:171370 cache_entry.set_is_dirty(false);
1371 cache_entry.set_is_persistent(sub_dir_type == CACHE_TYPE_PERSISTENT);
[email protected]48477fe2012-07-12 17:45:081372 metadata_->AddOrUpdateCacheEntry(resource_id, cache_entry);
[email protected]a321b9632012-06-14 03:29:171373 }
1374}
1375
1376void GDataCache::Remove(const std::string& resource_id,
[email protected]7f90e77c2012-07-17 09:35:311377 GDataFileError* error) {
[email protected]a321b9632012-06-14 03:29:171378 AssertOnSequencedWorkerPool();
1379 DCHECK(error);
1380
[email protected]3423871a2012-07-12 00:41:271381 // MD5 is not passed into RemoveCacheEntry because we would delete all
1382 // cache files corresponding to <resource_id> regardless of the md5.
[email protected]a321b9632012-06-14 03:29:171383 // So, search for entry in cache without taking md5 into account.
[email protected]b22f87f2012-07-12 10:53:171384 GDataCacheEntry cache_entry;
[email protected]a321b9632012-06-14 03:29:171385
1386 // If entry doesn't exist or is dirty or mounted in cache, nothing to do.
[email protected]b22f87f2012-07-12 10:53:171387 const bool entry_found =
1388 GetCacheEntry(resource_id, std::string(), &cache_entry);
[email protected]02821102012-07-12 20:19:171389 if (!entry_found || cache_entry.is_dirty() || cache_entry.is_mounted()) {
[email protected]a321b9632012-06-14 03:29:171390 DVLOG(1) << "Entry is "
[email protected]b22f87f2012-07-12 10:53:171391 << (entry_found ?
[email protected]02821102012-07-12 20:19:171392 (cache_entry.is_dirty() ? "dirty" : "mounted") :
[email protected]a321b9632012-06-14 03:29:171393 "non-existent")
1394 << " in cache, not removing";
[email protected]7f90e77c2012-07-17 09:35:311395 *error = GDATA_FILE_OK;
[email protected]a321b9632012-06-14 03:29:171396 return;
1397 }
1398
1399 // Determine paths to delete all cache versions of |resource_id| in
1400 // persistent, tmp and pinned directories.
1401 std::vector<FilePath> paths_to_delete;
1402
1403 // For files in persistent and tmp dirs, delete files that match
1404 // "<resource_id>.*".
1405 paths_to_delete.push_back(GetCacheFilePath(resource_id,
[email protected]ca5f6da2012-06-18 12:54:591406 util::kWildCard,
[email protected]a321b9632012-06-14 03:29:171407 CACHE_TYPE_PERSISTENT,
1408 CACHED_FILE_FROM_SERVER));
1409 paths_to_delete.push_back(GetCacheFilePath(resource_id,
[email protected]ca5f6da2012-06-18 12:54:591410 util::kWildCard,
[email protected]a321b9632012-06-14 03:29:171411 CACHE_TYPE_TMP,
1412 CACHED_FILE_FROM_SERVER));
1413
1414 // For pinned files, filename is "<resource_id>" with no extension, so delete
1415 // "<resource_id>".
1416 paths_to_delete.push_back(GetCacheFilePath(resource_id,
1417 std::string(),
1418 CACHE_TYPE_PINNED,
1419 CACHED_FILE_FROM_SERVER));
1420
1421 // Don't delete locally modified (i.e. dirty and possibly outgoing) files.
1422 // Since we're not deleting outgoing symlinks, we don't need to append
1423 // outgoing path to |paths_to_delete|.
1424 FilePath path_to_keep = GetCacheFilePath(resource_id,
1425 std::string(),
1426 CACHE_TYPE_PERSISTENT,
1427 CACHED_FILE_LOCALLY_MODIFIED);
1428
1429 for (size_t i = 0; i < paths_to_delete.size(); ++i) {
1430 DeleteFilesSelectively(paths_to_delete[i], path_to_keep);
1431 }
1432
1433 // Now that all file operations have completed, remove from cache map.
[email protected]3423871a2012-07-12 00:41:271434 metadata_->RemoveCacheEntry(resource_id);
[email protected]a321b9632012-06-14 03:29:171435
[email protected]7f90e77c2012-07-17 09:35:311436 *error = GDATA_FILE_OK;
[email protected]a321b9632012-06-14 03:29:171437}
1438
[email protected]7f90e77c2012-07-17 09:35:311439void GDataCache::OnPinned(GDataFileError* error,
[email protected]73f9c742012-06-15 07:37:131440 const std::string& resource_id,
1441 const std::string& md5,
1442 const CacheOperationCallback& callback) {
1443 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1444 DCHECK(error);
1445
1446 if (!callback.is_null())
1447 callback.Run(*error, resource_id, md5);
1448
[email protected]7f90e77c2012-07-17 09:35:311449 if (*error == GDATA_FILE_OK)
[email protected]73f9c742012-06-15 07:37:131450 FOR_EACH_OBSERVER(Observer, observers_, OnCachePinned(resource_id, md5));
[email protected]3653146a2012-05-29 13:41:471451}
1452
[email protected]7f90e77c2012-07-17 09:35:311453void GDataCache::OnUnpinned(GDataFileError* error,
[email protected]73f9c742012-06-15 07:37:131454 const std::string& resource_id,
1455 const std::string& md5,
1456 const CacheOperationCallback& callback) {
1457 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1458 DCHECK(error);
[email protected]a321b9632012-06-14 03:29:171459
[email protected]73f9c742012-06-15 07:37:131460 if (!callback.is_null())
1461 callback.Run(*error, resource_id, md5);
[email protected]a321b9632012-06-14 03:29:171462
[email protected]7f90e77c2012-07-17 09:35:311463 if (*error == GDATA_FILE_OK)
[email protected]73f9c742012-06-15 07:37:131464 FOR_EACH_OBSERVER(Observer, observers_, OnCacheUnpinned(resource_id, md5));
[email protected]a321b9632012-06-14 03:29:171465
[email protected]73f9c742012-06-15 07:37:131466 // Now the file is moved from "persistent" to "tmp" directory.
1467 // It's a chance to free up space if needed.
1468 bool* has_enough_space = new bool(false);
[email protected]ddbf2052012-07-13 15:07:021469 blocking_task_runner_->PostTask(
[email protected]73f9c742012-06-15 07:37:131470 FROM_HERE,
1471 base::Bind(&GDataCache::FreeDiskSpaceIfNeededFor,
1472 base::Unretained(this),
1473 0,
1474 base::Owned(has_enough_space)));
[email protected]3653146a2012-05-29 13:41:471475}
1476
[email protected]7f90e77c2012-07-17 09:35:311477void GDataCache::OnCommitDirty(GDataFileError* error,
[email protected]d7664c22012-06-18 19:35:491478 const std::string& resource_id,
1479 const std::string& md5,
1480 const CacheOperationCallback& callback) {
1481 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1482 DCHECK(error);
1483
1484 if (!callback.is_null())
1485 callback.Run(*error, resource_id, md5);
1486
[email protected]7f90e77c2012-07-17 09:35:311487 if (*error == GDATA_FILE_OK)
[email protected]d7664c22012-06-18 19:35:491488 FOR_EACH_OBSERVER(Observer, observers_, OnCacheCommitted(resource_id));
1489}
1490
[email protected]4324fdc2012-06-29 05:32:481491void GDataCache::GetCacheEntryHelper(const std::string& resource_id,
1492 const std::string& md5,
1493 bool* success,
[email protected]fae353a2012-07-11 23:30:271494 GDataCacheEntry* cache_entry) {
[email protected]4324fdc2012-06-29 05:32:481495 AssertOnSequencedWorkerPool();
1496 DCHECK(success);
1497 DCHECK(cache_entry);
1498
[email protected]b22f87f2012-07-12 10:53:171499 *success = GetCacheEntry(resource_id, md5, cache_entry);
[email protected]4324fdc2012-06-29 05:32:481500}
1501
[email protected]01ba15f72012-06-09 00:41:051502// static
1503FilePath GDataCache::GetCacheRootPath(Profile* profile) {
1504 FilePath cache_base_path;
1505 chrome::GetUserCacheDirectory(profile->GetPath(), &cache_base_path);
1506 FilePath cache_root_path =
1507 cache_base_path.Append(chrome::kGDataCacheDirname);
1508 return cache_root_path.Append(kGDataCacheVersionDir);
1509}
1510
[email protected]30d9dda2012-06-30 05:56:281511// static
1512std::vector<FilePath> GDataCache::GetCachePaths(
1513 const FilePath& cache_root_path) {
1514 std::vector<FilePath> cache_paths;
1515 // The order should match GDataCache::CacheSubDirectoryType enum.
1516 cache_paths.push_back(cache_root_path.Append(kGDataCacheMetaDir));
1517 cache_paths.push_back(cache_root_path.Append(kGDataCachePinnedDir));
1518 cache_paths.push_back(cache_root_path.Append(kGDataCacheOutgoingDir));
1519 cache_paths.push_back(cache_root_path.Append(kGDataCachePersistentDir));
1520 cache_paths.push_back(cache_root_path.Append(kGDataCacheTmpDir));
1521 cache_paths.push_back(cache_root_path.Append(kGDataCacheTmpDownloadsDir));
1522 cache_paths.push_back(cache_root_path.Append(kGDataCacheTmpDocumentsDir));
1523 return cache_paths;
1524}
1525
1526// static
1527bool GDataCache::CreateCacheDirectories(
1528 const std::vector<FilePath>& paths_to_create) {
1529 bool success = true;
1530
1531 for (size_t i = 0; i < paths_to_create.size(); ++i) {
1532 if (file_util::DirectoryExists(paths_to_create[i]))
1533 continue;
1534
1535 if (!file_util::CreateDirectory(paths_to_create[i])) {
1536 // Error creating this directory, record error and proceed with next one.
1537 success = false;
1538 PLOG(ERROR) << "Error creating directory " << paths_to_create[i].value();
1539 } else {
1540 DVLOG(1) << "Created directory " << paths_to_create[i].value();
1541 }
1542 }
1543 return success;
1544}
1545
[email protected]fae353a2012-07-11 23:30:271546// static
1547GDataCache::CacheSubDirectoryType GDataCache::GetSubDirectoryType(
1548 const GDataCacheEntry& cache_entry) {
[email protected]02821102012-07-12 20:19:171549 return cache_entry.is_persistent() ? CACHE_TYPE_PERSISTENT : CACHE_TYPE_TMP;
[email protected]fae353a2012-07-11 23:30:271550}
1551
[email protected]a321b9632012-06-14 03:29:171552void SetFreeDiskSpaceGetterForTesting(FreeDiskSpaceGetterInterface* getter) {
1553 delete global_free_disk_getter_for_testing; // Safe to delete NULL;
1554 global_free_disk_getter_for_testing = getter;
1555}
1556
[email protected]3653146a2012-05-29 13:41:471557} // namespace gdata