blob: 4c9ae4c313f1aa5b2f953025cb73826b2ef78338 [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();
[email protected]d7754f52012-08-01 08:45:3372 // Subtract this as if this portion does not exist.
[email protected]a321b9632012-06-14 03:29:1773 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.
[email protected]d7754f52012-08-01 08:45:3398// Do not remove recursively as we don't want to touch <gcache>/tmp/downloads,
[email protected]a321b9632012-06-14 03:29:1799// 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]e53ac8f2012-08-02 07:05:00327 weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
[email protected]73f9c742012-06-15 07:37:13328 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]3653146a2012-05-29 13:41:47329}
330
331GDataCache::~GDataCache() {
[email protected]73f9c742012-06-15 07:37:13332 AssertOnSequencedWorkerPool();
[email protected]3653146a2012-05-29 13:41:47333}
334
[email protected]32a7fc852012-06-08 17:25:50335FilePath GDataCache::GetCacheDirectoryPath(
336 CacheSubDirectoryType sub_dir_type) const {
337 DCHECK_LE(0, sub_dir_type);
338 DCHECK_GT(NUM_CACHE_TYPES, sub_dir_type);
339 return cache_paths_[sub_dir_type];
340}
341
342FilePath GDataCache::GetCacheFilePath(const std::string& resource_id,
343 const std::string& md5,
344 CacheSubDirectoryType sub_dir_type,
345 CachedFileOrigin file_origin) const {
346 DCHECK(sub_dir_type != CACHE_TYPE_META);
347
348 // Runs on any thread.
349 // Filename is formatted as resource_id.md5, i.e. resource_id is the base
350 // name and md5 is the extension.
351 std::string base_name = util::EscapeCacheFileName(resource_id);
352 if (file_origin == CACHED_FILE_LOCALLY_MODIFIED) {
353 DCHECK(sub_dir_type == CACHE_TYPE_PERSISTENT);
354 base_name += FilePath::kExtensionSeparator;
[email protected]b83e5202012-06-27 07:50:24355 base_name += util::kLocallyModifiedFileExtension;
[email protected]32a7fc852012-06-08 17:25:50356 } else if (!md5.empty()) {
357 base_name += FilePath::kExtensionSeparator;
358 base_name += util::EscapeCacheFileName(md5);
359 }
360 // For mounted archives the filename is formatted as resource_id.md5.mounted,
361 // i.e. resource_id.md5 is the base name and ".mounted" is the extension
362 if (file_origin == CACHED_FILE_MOUNTED) {
[email protected]a321b9632012-06-14 03:29:17363 DCHECK(sub_dir_type == CACHE_TYPE_PERSISTENT);
364 base_name += FilePath::kExtensionSeparator;
[email protected]ca5f6da2012-06-18 12:54:59365 base_name += util::kMountedArchiveFileExtension;
[email protected]32a7fc852012-06-08 17:25:50366 }
367 return GetCacheDirectoryPath(sub_dir_type).Append(base_name);
368}
369
[email protected]fcc92a52012-06-08 22:54:16370void GDataCache::AssertOnSequencedWorkerPool() {
[email protected]ddbf2052012-07-13 15:07:02371 DCHECK(!blocking_task_runner_ ||
372 blocking_task_runner_->RunsTasksOnCurrentThread());
[email protected]fcc92a52012-06-08 22:54:16373}
374
[email protected]01ba15f72012-06-09 00:41:05375bool GDataCache::IsUnderGDataCacheDirectory(const FilePath& path) const {
376 return cache_root_path_ == path || cache_root_path_.IsParent(path);
377}
378
[email protected]73f9c742012-06-15 07:37:13379void GDataCache::AddObserver(Observer* observer) {
380 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
381 observers_.AddObserver(observer);
382}
383
384void GDataCache::RemoveObserver(Observer* observer) {
385 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
386 observers_.RemoveObserver(observer);
387}
388
[email protected]4324fdc2012-06-29 05:32:48389void GDataCache::GetCacheEntryOnUIThread(
390 const std::string& resource_id,
391 const std::string& md5,
392 const GetCacheEntryCallback& callback) {
393 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
394
395 bool* success = new bool(false);
[email protected]fae353a2012-07-11 23:30:27396 GDataCacheEntry* cache_entry = new GDataCacheEntry;
[email protected]ddbf2052012-07-13 15:07:02397 blocking_task_runner_->PostTaskAndReply(
[email protected]4324fdc2012-06-29 05:32:48398 FROM_HERE,
399 base::Bind(&GDataCache::GetCacheEntryHelper,
400 base::Unretained(this),
401 resource_id,
402 md5,
403 success,
404 cache_entry),
405 base::Bind(&RunGetCacheEntryCallback,
406 callback,
407 base::Owned(success),
408 base::Owned(cache_entry)));
409}
410
[email protected]b83e5202012-06-27 07:50:24411void GDataCache::GetResourceIdsOfBacklogOnUIThread(
[email protected]85b62192012-06-29 19:56:38412 const GetResourceIdsOfBacklogCallback& callback) {
[email protected]8764a392012-06-20 06:43:08413 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
414
[email protected]b83e5202012-06-27 07:50:24415 std::vector<std::string>* to_fetch = new std::vector<std::string>;
416 std::vector<std::string>* to_upload = new std::vector<std::string>;
[email protected]ddbf2052012-07-13 15:07:02417 blocking_task_runner_->PostTaskAndReply(
[email protected]8764a392012-06-20 06:43:08418 FROM_HERE,
[email protected]b83e5202012-06-27 07:50:24419 base::Bind(&GDataCache::GetResourceIdsOfBacklog,
[email protected]8764a392012-06-20 06:43:08420 base::Unretained(this),
[email protected]b83e5202012-06-27 07:50:24421 to_fetch,
422 to_upload),
[email protected]85b62192012-06-29 19:56:38423 base::Bind(&RunGetResourceIdsOfBacklogCallback,
[email protected]8764a392012-06-20 06:43:08424 callback,
[email protected]b83e5202012-06-27 07:50:24425 base::Owned(to_fetch),
426 base::Owned(to_upload)));
[email protected]8764a392012-06-20 06:43:08427}
428
[email protected]85b62192012-06-29 19:56:38429void GDataCache::GetResourceIdsOfExistingPinnedFilesOnUIThread(
430 const GetResourceIdsCallback& callback) {
431 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
432
433 std::vector<std::string>* resource_ids = new std::vector<std::string>;
[email protected]ddbf2052012-07-13 15:07:02434 blocking_task_runner_->PostTaskAndReply(
[email protected]85b62192012-06-29 19:56:38435 FROM_HERE,
436 base::Bind(&GDataCache::GetResourceIdsOfExistingPinnedFiles,
437 base::Unretained(this),
438 resource_ids),
439 base::Bind(&RunGetResourceIdsCallback,
440 callback,
441 base::Owned(resource_ids)));
442}
443
[email protected]cd236432012-07-27 18:03:30444void GDataCache::GetResourceIdsOfAllFilesOnUIThread(
445 const GetResourceIdsCallback& callback) {
446 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
447
448 std::vector<std::string>* resource_ids = new std::vector<std::string>;
449 blocking_task_runner_->PostTaskAndReply(
450 FROM_HERE,
451 base::Bind(&GDataCache::GetResourceIdsOfAllFiles,
452 base::Unretained(this),
453 resource_ids),
454 base::Bind(&RunGetResourceIdsCallback,
455 callback,
456 base::Owned(resource_ids)));
457}
458
[email protected]a321b9632012-06-14 03:29:17459void GDataCache::FreeDiskSpaceIfNeededFor(int64 num_bytes,
460 bool* has_enough_space) {
461 AssertOnSequencedWorkerPool();
462
463 // Do nothing and return if we have enough space.
464 *has_enough_space = HasEnoughSpaceFor(num_bytes);
465 if (*has_enough_space)
466 return;
467
468 // Otherwise, try to free up the disk space.
469 DVLOG(1) << "Freeing up disk space for " << num_bytes;
470 // First remove temporary files from the cache map.
[email protected]ca5f6da2012-06-18 12:54:59471 metadata_->RemoveTemporaryFiles();
[email protected]a321b9632012-06-14 03:29:17472 // Then remove all files under "tmp" directory.
473 RemoveAllFiles(GetCacheDirectoryPath(GDataCache::CACHE_TYPE_TMP));
474
475 // Check the disk space again.
476 *has_enough_space = HasEnoughSpaceFor(num_bytes);
477}
478
[email protected]c960d222012-06-15 10:03:50479void GDataCache::GetFileOnUIThread(const std::string& resource_id,
480 const std::string& md5,
481 const GetFileFromCacheCallback& callback) {
482 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]a321b9632012-06-14 03:29:17483
[email protected]7f90e77c2012-07-17 09:35:31484 GDataFileError* error =
485 new GDataFileError(GDATA_FILE_OK);
[email protected]c960d222012-06-15 10:03:50486 FilePath* cache_file_path = new FilePath;
[email protected]ddbf2052012-07-13 15:07:02487 blocking_task_runner_->PostTaskAndReply(
[email protected]c960d222012-06-15 10:03:50488 FROM_HERE,
489 base::Bind(&GDataCache::GetFile,
490 base::Unretained(this),
491 resource_id,
492 md5,
493 error,
494 cache_file_path),
495 base::Bind(&RunGetFileFromCacheCallback,
496 callback,
497 base::Owned(error),
498 resource_id,
499 md5,
500 base::Owned(cache_file_path)));
[email protected]a321b9632012-06-14 03:29:17501}
502
[email protected]73f9c742012-06-15 07:37:13503void GDataCache::StoreOnUIThread(const std::string& resource_id,
504 const std::string& md5,
505 const FilePath& source_path,
506 FileOperationType file_operation_type,
507 const CacheOperationCallback& callback) {
508 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
509
[email protected]7f90e77c2012-07-17 09:35:31510 GDataFileError* error =
511 new GDataFileError(GDATA_FILE_OK);
[email protected]ddbf2052012-07-13 15:07:02512 blocking_task_runner_->PostTaskAndReply(
[email protected]73f9c742012-06-15 07:37:13513 FROM_HERE,
514 base::Bind(&GDataCache::Store,
515 base::Unretained(this),
516 resource_id,
517 md5,
518 source_path,
519 file_operation_type,
520 error),
521 base::Bind(&RunCacheOperationCallback,
522 callback,
523 base::Owned(error),
524 resource_id,
525 md5));
526}
527
528void GDataCache::PinOnUIThread(const std::string& resource_id,
[email protected]44c0584e2012-06-15 23:55:41529 const std::string& md5,
530 const CacheOperationCallback& callback) {
[email protected]73f9c742012-06-15 07:37:13531 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
532
[email protected]7f90e77c2012-07-17 09:35:31533 GDataFileError* error =
534 new GDataFileError(GDATA_FILE_OK);
[email protected]ddbf2052012-07-13 15:07:02535 blocking_task_runner_->PostTaskAndReply(
[email protected]73f9c742012-06-15 07:37:13536 FROM_HERE,
537 base::Bind(&GDataCache::Pin,
538 base::Unretained(this),
539 resource_id,
540 md5,
541 GDataCache::FILE_OPERATION_MOVE,
542 error),
543 base::Bind(&GDataCache::OnPinned,
[email protected]e53ac8f2012-08-02 07:05:00544 weak_ptr_factory_.GetWeakPtr(),
[email protected]73f9c742012-06-15 07:37:13545 base::Owned(error),
546 resource_id,
547 md5,
548 callback));
549}
550
551void GDataCache::UnpinOnUIThread(const std::string& resource_id,
552 const std::string& md5,
553 const CacheOperationCallback& callback) {
554 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]7f90e77c2012-07-17 09:35:31555 GDataFileError* error =
556 new GDataFileError(GDATA_FILE_OK);
[email protected]ddbf2052012-07-13 15:07:02557 blocking_task_runner_->PostTaskAndReply(
[email protected]73f9c742012-06-15 07:37:13558 FROM_HERE,
559 base::Bind(&GDataCache::Unpin,
560 base::Unretained(this),
561 resource_id,
562 md5,
563 GDataCache::FILE_OPERATION_MOVE,
564 error),
565 base::Bind(&GDataCache::OnUnpinned,
[email protected]e53ac8f2012-08-02 07:05:00566 weak_ptr_factory_.GetWeakPtr(),
[email protected]73f9c742012-06-15 07:37:13567 base::Owned(error),
568 resource_id,
569 md5,
570 callback));
571}
572
573void GDataCache::SetMountedStateOnUIThread(
574 const FilePath& file_path,
575 bool to_mount,
576 const SetMountedStateCallback& callback) {
577 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
578
[email protected]7f90e77c2012-07-17 09:35:31579 GDataFileError* error =
580 new GDataFileError(GDATA_FILE_OK);
[email protected]73f9c742012-06-15 07:37:13581 FilePath* cache_file_path = new FilePath;
[email protected]ddbf2052012-07-13 15:07:02582 blocking_task_runner_->PostTaskAndReply(
[email protected]73f9c742012-06-15 07:37:13583 FROM_HERE,
584 base::Bind(&GDataCache::SetMountedState,
585 base::Unretained(this),
586 file_path,
587 to_mount,
588 error,
589 cache_file_path),
590 base::Bind(&RunSetMountedStateCallback,
591 callback,
592 base::Owned(error),
593 base::Owned(cache_file_path)));
594}
595
[email protected]c960d222012-06-15 10:03:50596void GDataCache::MarkDirtyOnUIThread(const std::string& resource_id,
597 const std::string& md5,
598 const GetFileFromCacheCallback& callback) {
599 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]73f9c742012-06-15 07:37:13600
[email protected]7f90e77c2012-07-17 09:35:31601 GDataFileError* error =
602 new GDataFileError(GDATA_FILE_OK);
[email protected]c960d222012-06-15 10:03:50603 FilePath* cache_file_path = new FilePath;
[email protected]ddbf2052012-07-13 15:07:02604 blocking_task_runner_->PostTaskAndReply(
[email protected]c960d222012-06-15 10:03:50605 FROM_HERE,
606 base::Bind(&GDataCache::MarkDirty,
607 base::Unretained(this),
608 resource_id,
609 md5,
610 GDataCache::FILE_OPERATION_MOVE,
611 error,
612 cache_file_path),
613 base::Bind(&RunGetFileFromCacheCallback,
614 callback,
615 base::Owned(error),
616 resource_id,
617 md5,
618 base::Owned(cache_file_path)));
[email protected]73f9c742012-06-15 07:37:13619}
620
621void GDataCache::CommitDirtyOnUIThread(const std::string& resource_id,
622 const std::string& md5,
623 const CacheOperationCallback& callback) {
624 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
625
[email protected]7f90e77c2012-07-17 09:35:31626 GDataFileError* error = new GDataFileError(GDATA_FILE_OK);
[email protected]ddbf2052012-07-13 15:07:02627 blocking_task_runner_->PostTaskAndReply(
[email protected]73f9c742012-06-15 07:37:13628 FROM_HERE,
629 base::Bind(&GDataCache::CommitDirty,
630 base::Unretained(this),
631 resource_id,
632 md5,
633 GDataCache::FILE_OPERATION_MOVE,
634 error),
[email protected]d7664c22012-06-18 19:35:49635 base::Bind(&GDataCache::OnCommitDirty,
[email protected]e53ac8f2012-08-02 07:05:00636 weak_ptr_factory_.GetWeakPtr(),
[email protected]73f9c742012-06-15 07:37:13637 base::Owned(error),
638 resource_id,
[email protected]d7664c22012-06-18 19:35:49639 md5,
640 callback));
[email protected]73f9c742012-06-15 07:37:13641}
642
643void GDataCache::ClearDirtyOnUIThread(const std::string& resource_id,
644 const std::string& md5,
645 const CacheOperationCallback& callback) {
646 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
647
[email protected]7f90e77c2012-07-17 09:35:31648 GDataFileError* error =
649 new GDataFileError(GDATA_FILE_OK);
[email protected]ddbf2052012-07-13 15:07:02650 blocking_task_runner_->PostTaskAndReply(
[email protected]73f9c742012-06-15 07:37:13651 FROM_HERE,
652 base::Bind(&GDataCache::ClearDirty,
653 base::Unretained(this),
654 resource_id,
655 md5,
656 GDataCache::FILE_OPERATION_MOVE,
657 error),
658 base::Bind(&RunCacheOperationCallback,
659 callback,
660 base::Owned(error),
661 resource_id,
662 md5));
663}
664
665void GDataCache::RemoveOnUIThread(const std::string& resource_id,
666 const CacheOperationCallback& callback) {
667 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
668
[email protected]7f90e77c2012-07-17 09:35:31669 GDataFileError* error =
670 new GDataFileError(GDATA_FILE_OK);
[email protected]73f9c742012-06-15 07:37:13671
[email protected]ddbf2052012-07-13 15:07:02672 blocking_task_runner_->PostTaskAndReply(
[email protected]73f9c742012-06-15 07:37:13673 FROM_HERE,
674 base::Bind(&GDataCache::Remove,
675 base::Unretained(this),
676 resource_id,
677 error),
678 base::Bind(&RunCacheOperationCallback,
679 callback,
680 base::Owned(error),
681 resource_id,
682 "" /* md5 */));
683}
684
685void GDataCache::RequestInitializeOnUIThread() {
686 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
687
[email protected]ddbf2052012-07-13 15:07:02688 blocking_task_runner_->PostTask(
[email protected]73f9c742012-06-15 07:37:13689 FROM_HERE,
[email protected]1ff88ea52012-06-20 04:54:00690 base::Bind(&GDataCache::Initialize, base::Unretained(this)));
[email protected]73f9c742012-06-15 07:37:13691}
692
[email protected]79c3752d2012-07-17 12:10:08693void GDataCache::ForceRescanOnUIThreadForTesting() {
694 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
695
696 blocking_task_runner_->PostTask(
697 FROM_HERE,
698 base::Bind(&GDataCache::ForceRescanForTesting, base::Unretained(this)));
699}
700
[email protected]b22f87f2012-07-12 10:53:17701bool GDataCache::GetCacheEntry(const std::string& resource_id,
702 const std::string& md5,
703 GDataCacheEntry* entry) {
704 DCHECK(entry);
[email protected]73f9c742012-06-15 07:37:13705 AssertOnSequencedWorkerPool();
[email protected]b22f87f2012-07-12 10:53:17706 return metadata_->GetCacheEntry(resource_id, md5, entry);
[email protected]73f9c742012-06-15 07:37:13707}
708
709// static
710GDataCache* GDataCache::CreateGDataCacheOnUIThread(
711 const FilePath& cache_root_path,
[email protected]ddbf2052012-07-13 15:07:02712 base::SequencedTaskRunner* blocking_task_runner) {
[email protected]73f9c742012-06-15 07:37:13713 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]ddbf2052012-07-13 15:07:02714 return new GDataCache(cache_root_path, blocking_task_runner);
[email protected]73f9c742012-06-15 07:37:13715}
716
717void GDataCache::DestroyOnUIThread() {
718 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
719
720 // Invalidate the weak pointer.
[email protected]e53ac8f2012-08-02 07:05:00721 weak_ptr_factory_.InvalidateWeakPtrs();
[email protected]73f9c742012-06-15 07:37:13722
723 // Destroy myself on the blocking pool.
[email protected]ddbf2052012-07-13 15:07:02724 blocking_task_runner_->PostTask(
[email protected]73f9c742012-06-15 07:37:13725 FROM_HERE,
726 base::Bind(&GDataCache::Destroy,
727 base::Unretained(this)));
728}
729
[email protected]ca5f6da2012-06-18 12:54:59730void GDataCache::Initialize() {
731 AssertOnSequencedWorkerPool();
732
[email protected]79c3752d2012-07-17 12:10:08733 InitCachePaths(cache_paths_);
[email protected]f9e5cc02012-07-13 20:08:56734 metadata_ = GDataCacheMetadata::CreateGDataCacheMetadata(
735 blocking_task_runner_).Pass();
736 metadata_->Initialize(cache_paths_);
[email protected]ca5f6da2012-06-18 12:54:59737}
738
[email protected]73f9c742012-06-15 07:37:13739void GDataCache::Destroy() {
740 AssertOnSequencedWorkerPool();
741 delete this;
742}
743
[email protected]79c3752d2012-07-17 12:10:08744void GDataCache::ForceRescanForTesting() {
745 AssertOnSequencedWorkerPool();
746 metadata_->ForceRescanForTesting(cache_paths_);
747}
748
[email protected]b83e5202012-06-27 07:50:24749void GDataCache::GetResourceIdsOfBacklog(
750 std::vector<std::string>* to_fetch,
751 std::vector<std::string>* to_upload) {
[email protected]8764a392012-06-20 06:43:08752 AssertOnSequencedWorkerPool();
[email protected]b83e5202012-06-27 07:50:24753 DCHECK(to_fetch);
754 DCHECK(to_upload);
[email protected]8764a392012-06-20 06:43:08755
[email protected]b83e5202012-06-27 07:50:24756 metadata_->Iterate(base::Bind(&CollectBacklog, to_fetch, to_upload));
[email protected]8764a392012-06-20 06:43:08757}
758
[email protected]85b62192012-06-29 19:56:38759void GDataCache::GetResourceIdsOfExistingPinnedFiles(
760 std::vector<std::string>* resource_ids) {
761 AssertOnSequencedWorkerPool();
762 DCHECK(resource_ids);
763
764 metadata_->Iterate(base::Bind(&CollectExistingPinnedFile, resource_ids));
765}
766
[email protected]cd236432012-07-27 18:03:30767void GDataCache::GetResourceIdsOfAllFiles(
768 std::vector<std::string>* resource_ids) {
769 AssertOnSequencedWorkerPool();
770 DCHECK(resource_ids);
771
772 metadata_->Iterate(base::Bind(&CollectAnyFile, resource_ids));
773}
774
[email protected]c960d222012-06-15 10:03:50775void GDataCache::GetFile(const std::string& resource_id,
776 const std::string& md5,
[email protected]7f90e77c2012-07-17 09:35:31777 GDataFileError* error,
[email protected]c960d222012-06-15 10:03:50778 FilePath* cache_file_path) {
779 AssertOnSequencedWorkerPool();
780 DCHECK(error);
781 DCHECK(cache_file_path);
782
[email protected]b22f87f2012-07-12 10:53:17783 GDataCacheEntry cache_entry;
784 if (GetCacheEntry(resource_id, md5, &cache_entry) &&
[email protected]02821102012-07-12 20:19:17785 cache_entry.is_present()) {
[email protected]c960d222012-06-15 10:03:50786 CachedFileOrigin file_origin;
[email protected]02821102012-07-12 20:19:17787 if (cache_entry.is_mounted()) {
[email protected]c960d222012-06-15 10:03:50788 file_origin = CACHED_FILE_MOUNTED;
[email protected]02821102012-07-12 20:19:17789 } else if (cache_entry.is_dirty()) {
[email protected]c960d222012-06-15 10:03:50790 file_origin = CACHED_FILE_LOCALLY_MODIFIED;
791 } else {
792 file_origin = CACHED_FILE_FROM_SERVER;
793 }
794 *cache_file_path = GetCacheFilePath(
795 resource_id,
796 md5,
[email protected]b22f87f2012-07-12 10:53:17797 GetSubDirectoryType(cache_entry),
[email protected]c960d222012-06-15 10:03:50798 file_origin);
[email protected]7f90e77c2012-07-17 09:35:31799 *error = GDATA_FILE_OK;
[email protected]c960d222012-06-15 10:03:50800 } else {
[email protected]7f90e77c2012-07-17 09:35:31801 *error = GDATA_FILE_ERROR_NOT_FOUND;
[email protected]c960d222012-06-15 10:03:50802 }
803}
804
[email protected]a321b9632012-06-14 03:29:17805void GDataCache::Store(const std::string& resource_id,
806 const std::string& md5,
807 const FilePath& source_path,
808 FileOperationType file_operation_type,
[email protected]7f90e77c2012-07-17 09:35:31809 GDataFileError* error) {
[email protected]a321b9632012-06-14 03:29:17810 AssertOnSequencedWorkerPool();
811 DCHECK(error);
812
813 FilePath dest_path;
814 FilePath symlink_path;
[email protected]a321b9632012-06-14 03:29:17815 CacheSubDirectoryType sub_dir_type = CACHE_TYPE_TMP;
816
[email protected]a321b9632012-06-14 03:29:17817 // If file was previously pinned, store it in persistent dir and create
818 // symlink in pinned dir.
[email protected]b22f87f2012-07-12 10:53:17819 GDataCacheEntry cache_entry;
820 if (GetCacheEntry(resource_id, md5, &cache_entry)) { // File exists in cache.
[email protected]a321b9632012-06-14 03:29:17821 // If file is dirty or mounted, return error.
[email protected]02821102012-07-12 20:19:17822 if (cache_entry.is_dirty() || cache_entry.is_mounted()) {
[email protected]a321b9632012-06-14 03:29:17823 LOG(WARNING) << "Can't store a file to replace a "
[email protected]02821102012-07-12 20:19:17824 << (cache_entry.is_dirty() ? "dirty" : "mounted")
[email protected]a321b9632012-06-14 03:29:17825 << " file: res_id=" << resource_id
826 << ", md5=" << md5;
[email protected]7f90e77c2012-07-17 09:35:31827 *error = GDATA_FILE_ERROR_IN_USE;
[email protected]a321b9632012-06-14 03:29:17828 return;
829 }
830
[email protected]a321b9632012-06-14 03:29:17831 // If file is pinned, determines destination path.
[email protected]02821102012-07-12 20:19:17832 if (cache_entry.is_pinned()) {
[email protected]a321b9632012-06-14 03:29:17833 sub_dir_type = CACHE_TYPE_PERSISTENT;
834 dest_path = GetCacheFilePath(resource_id, md5, sub_dir_type,
835 CACHED_FILE_FROM_SERVER);
836 symlink_path = GetCacheFilePath(
837 resource_id, std::string(), CACHE_TYPE_PINNED,
838 CACHED_FILE_FROM_SERVER);
839 }
840 }
841
842 // File wasn't pinned or doesn't exist in cache, store in tmp dir.
843 if (dest_path.empty()) {
844 DCHECK_EQ(CACHE_TYPE_TMP, sub_dir_type);
845 dest_path = GetCacheFilePath(resource_id, md5, sub_dir_type,
846 CACHED_FILE_FROM_SERVER);
847 }
848
849 *error = ModifyCacheState(
850 source_path,
851 dest_path,
852 file_operation_type,
853 symlink_path,
854 !symlink_path.empty()); // create symlink
855
856 // Determine search pattern for stale filenames corrresponding to resource_id,
857 // either "<resource_id>*" or "<resource_id>.*".
858 FilePath stale_filenames_pattern;
859 if (md5.empty()) {
860 // No md5 means no extension, append '*' after base name, i.e.
861 // "<resource_id>*".
862 // Cannot call |dest_path|.ReplaceExtension when there's no md5 extension:
863 // if base name of |dest_path| (i.e. escaped resource_id) contains the
864 // extension separator '.', ReplaceExtension will remove it and everything
865 // after it. The result will be nothing like the escaped resource_id.
[email protected]ca5f6da2012-06-18 12:54:59866 stale_filenames_pattern = FilePath(dest_path.value() + util::kWildCard);
[email protected]a321b9632012-06-14 03:29:17867 } else {
868 // Replace md5 extension with '*' i.e. "<resource_id>.*".
869 // Note that ReplaceExtension automatically prefixes the extension with the
870 // extension separator '.'.
[email protected]ca5f6da2012-06-18 12:54:59871 stale_filenames_pattern = dest_path.ReplaceExtension(util::kWildCard);
[email protected]a321b9632012-06-14 03:29:17872 }
873
874 // Delete files that match |stale_filenames_pattern| except for |dest_path|.
875 DeleteFilesSelectively(stale_filenames_pattern, dest_path);
876
[email protected]7f90e77c2012-07-17 09:35:31877 if (*error == GDATA_FILE_OK) {
[email protected]a321b9632012-06-14 03:29:17878 // Now that file operations have completed, update cache map.
[email protected]b22f87f2012-07-12 10:53:17879 cache_entry.set_md5(md5);
[email protected]02821102012-07-12 20:19:17880 cache_entry.set_is_present(true);
881 cache_entry.set_is_persistent(sub_dir_type == CACHE_TYPE_PERSISTENT);
[email protected]b22f87f2012-07-12 10:53:17882 metadata_->AddOrUpdateCacheEntry(resource_id, cache_entry);
[email protected]a321b9632012-06-14 03:29:17883 }
884}
885
886void GDataCache::Pin(const std::string& resource_id,
887 const std::string& md5,
888 FileOperationType file_operation_type,
[email protected]7f90e77c2012-07-17 09:35:31889 GDataFileError* error) {
[email protected]a321b9632012-06-14 03:29:17890 AssertOnSequencedWorkerPool();
891 DCHECK(error);
892
893 FilePath source_path;
894 FilePath dest_path;
895 FilePath symlink_path;
896 bool create_symlink = true;
[email protected]a321b9632012-06-14 03:29:17897 CacheSubDirectoryType sub_dir_type = CACHE_TYPE_PERSISTENT;
898
[email protected]b22f87f2012-07-12 10:53:17899 GDataCacheEntry cache_entry;
900 if (!GetCacheEntry(resource_id, md5, &cache_entry)) {
901 // Entry does not exist in cache.
[email protected]a321b9632012-06-14 03:29:17902 // Set both |dest_path| and |source_path| to /dev/null, so that:
903 // 1) ModifyCacheState won't move files when |source_path| and |dest_path|
904 // are the same.
905 // 2) symlinks to /dev/null will be picked up by GDataSyncClient to download
906 // pinned files that don't exist in cache.
[email protected]30d9dda2012-06-30 05:56:28907 dest_path = FilePath::FromUTF8Unsafe(util::kSymLinkToDevNull);
[email protected]a321b9632012-06-14 03:29:17908 source_path = dest_path;
909
[email protected]2a6fe6e2012-07-10 06:01:04910 // Set sub_dir_type to TMP. The file will be first downloaded in 'tmp',
911 // then moved to 'persistent'.
912 sub_dir_type = CACHE_TYPE_TMP;
[email protected]109eb5c2012-07-12 03:20:05913 } else { // File exists in cache, determines destination path.
[email protected]a321b9632012-06-14 03:29:17914 // Determine source and destination paths.
915
916 // If file is dirty or mounted, don't move it, so determine |dest_path| and
917 // set |source_path| the same, because ModifyCacheState only moves files if
918 // source and destination are different.
[email protected]02821102012-07-12 20:19:17919 if (cache_entry.is_dirty() || cache_entry.is_mounted()) {
920 DCHECK(cache_entry.is_persistent());
[email protected]a321b9632012-06-14 03:29:17921 dest_path = GetCacheFilePath(resource_id,
922 md5,
[email protected]b22f87f2012-07-12 10:53:17923 GetSubDirectoryType(cache_entry),
[email protected]a321b9632012-06-14 03:29:17924 CACHED_FILE_LOCALLY_MODIFIED);
925 source_path = dest_path;
926 } else {
927 // Gets the current path of the file in cache.
928 source_path = GetCacheFilePath(resource_id,
929 md5,
[email protected]b22f87f2012-07-12 10:53:17930 GetSubDirectoryType(cache_entry),
[email protected]a321b9632012-06-14 03:29:17931 CACHED_FILE_FROM_SERVER);
932
933 // If file was pinned before but actual file blob doesn't exist in cache:
934 // - don't need to move the file, so set |dest_path| to |source_path|,
935 // because ModifyCacheState only moves files if source and destination
936 // are different
937 // - don't create symlink since it already exists.
[email protected]02821102012-07-12 20:19:17938 if (!cache_entry.is_present()) {
[email protected]a321b9632012-06-14 03:29:17939 dest_path = source_path;
940 create_symlink = false;
941 } else { // File exists, move it to persistent dir.
942 dest_path = GetCacheFilePath(resource_id,
943 md5,
944 CACHE_TYPE_PERSISTENT,
945 CACHED_FILE_FROM_SERVER);
946 }
947 }
948 }
949
950 // Create symlink in pinned dir.
951 if (create_symlink) {
952 symlink_path = GetCacheFilePath(resource_id,
953 std::string(),
954 CACHE_TYPE_PINNED,
955 CACHED_FILE_FROM_SERVER);
956 }
957
958 *error = ModifyCacheState(source_path,
959 dest_path,
960 file_operation_type,
961 symlink_path,
962 create_symlink);
963
[email protected]7f90e77c2012-07-17 09:35:31964 if (*error == GDATA_FILE_OK) {
[email protected]a321b9632012-06-14 03:29:17965 // Now that file operations have completed, update cache map.
[email protected]b22f87f2012-07-12 10:53:17966 cache_entry.set_md5(md5);
[email protected]02821102012-07-12 20:19:17967 cache_entry.set_is_pinned(true);
968 cache_entry.set_is_persistent(sub_dir_type == CACHE_TYPE_PERSISTENT);
[email protected]b22f87f2012-07-12 10:53:17969 metadata_->AddOrUpdateCacheEntry(resource_id, cache_entry);
[email protected]a321b9632012-06-14 03:29:17970 }
971}
972
973void GDataCache::Unpin(const std::string& resource_id,
974 const std::string& md5,
975 FileOperationType file_operation_type,
[email protected]7f90e77c2012-07-17 09:35:31976 GDataFileError* error) {
[email protected]a321b9632012-06-14 03:29:17977 AssertOnSequencedWorkerPool();
978 DCHECK(error);
979
[email protected]a321b9632012-06-14 03:29:17980 // Unpinning a file means its entry must exist in cache.
[email protected]b22f87f2012-07-12 10:53:17981 GDataCacheEntry cache_entry;
982 if (!GetCacheEntry(resource_id, md5, &cache_entry)) {
[email protected]a321b9632012-06-14 03:29:17983 LOG(WARNING) << "Can't unpin a file that wasn't pinned or cached: res_id="
984 << resource_id
985 << ", md5=" << md5;
[email protected]7f90e77c2012-07-17 09:35:31986 *error = GDATA_FILE_ERROR_NOT_FOUND;
[email protected]a321b9632012-06-14 03:29:17987 return;
988 }
989
990 // Entry exists in cache, determines source and destination paths.
991
992 FilePath source_path;
993 FilePath dest_path;
994 CacheSubDirectoryType sub_dir_type = CACHE_TYPE_TMP;
995
996 // If file is dirty or mounted, don't move it, so determine |dest_path| and
997 // set |source_path| the same, because ModifyCacheState moves files if source
998 // and destination are different.
[email protected]02821102012-07-12 20:19:17999 if (cache_entry.is_dirty() || cache_entry.is_mounted()) {
[email protected]a321b9632012-06-14 03:29:171000 sub_dir_type = CACHE_TYPE_PERSISTENT;
[email protected]02821102012-07-12 20:19:171001 DCHECK(cache_entry.is_persistent());
[email protected]a321b9632012-06-14 03:29:171002 dest_path = GetCacheFilePath(resource_id,
1003 md5,
[email protected]b22f87f2012-07-12 10:53:171004 GetSubDirectoryType(cache_entry),
[email protected]a321b9632012-06-14 03:29:171005 CACHED_FILE_LOCALLY_MODIFIED);
1006 source_path = dest_path;
1007 } else {
1008 // Gets the current path of the file in cache.
1009 source_path = GetCacheFilePath(resource_id,
1010 md5,
[email protected]b22f87f2012-07-12 10:53:171011 GetSubDirectoryType(cache_entry),
[email protected]a321b9632012-06-14 03:29:171012 CACHED_FILE_FROM_SERVER);
1013
1014 // If file was pinned but actual file blob still doesn't exist in cache,
1015 // don't need to move the file, so set |dest_path| to |source_path|, because
1016 // ModifyCacheState only moves files if source and destination are
1017 // different.
[email protected]02821102012-07-12 20:19:171018 if (!cache_entry.is_present()) {
[email protected]a321b9632012-06-14 03:29:171019 dest_path = source_path;
1020 } else { // File exists, move it to tmp dir.
1021 dest_path = GetCacheFilePath(resource_id, md5,
1022 CACHE_TYPE_TMP,
1023 CACHED_FILE_FROM_SERVER);
1024 }
1025 }
1026
1027 // If file was pinned, get absolute path of symlink in pinned dir so as to
1028 // remove it.
1029 FilePath symlink_path;
[email protected]02821102012-07-12 20:19:171030 if (cache_entry.is_pinned()) {
[email protected]a321b9632012-06-14 03:29:171031 symlink_path = GetCacheFilePath(resource_id,
1032 std::string(),
1033 CACHE_TYPE_PINNED,
1034 CACHED_FILE_FROM_SERVER);
1035 }
1036
1037 *error = ModifyCacheState(
1038 source_path,
1039 dest_path,
1040 file_operation_type,
1041 symlink_path, // This will be deleted if it exists.
1042 false /* don't create symlink*/);
1043
[email protected]7f90e77c2012-07-17 09:35:311044 if (*error == GDATA_FILE_OK) {
[email protected]a321b9632012-06-14 03:29:171045 // Now that file operations have completed, update cache map.
[email protected]02821102012-07-12 20:19:171046 if (cache_entry.is_present()) {
[email protected]48477fe2012-07-12 17:45:081047 cache_entry.set_md5(md5);
[email protected]02821102012-07-12 20:19:171048 cache_entry.set_is_pinned(false);
1049 cache_entry.set_is_persistent(sub_dir_type == CACHE_TYPE_PERSISTENT);
[email protected]48477fe2012-07-12 17:45:081050 metadata_->AddOrUpdateCacheEntry(resource_id, cache_entry);
[email protected]3423871a2012-07-12 00:41:271051 } else {
1052 // Remove the existing entry if we are unpinning a non-present file.
1053 metadata_->RemoveCacheEntry(resource_id);
1054 }
[email protected]a321b9632012-06-14 03:29:171055 }
1056}
1057
1058void GDataCache::SetMountedState(const FilePath& file_path,
1059 bool to_mount,
[email protected]7f90e77c2012-07-17 09:35:311060 GDataFileError *error,
[email protected]a321b9632012-06-14 03:29:171061 FilePath* cache_file_path) {
1062 AssertOnSequencedWorkerPool();
1063 DCHECK(error);
1064 DCHECK(cache_file_path);
1065
1066 // Parse file path to obtain resource_id, md5 and extra_extension.
1067 std::string resource_id;
1068 std::string md5;
1069 std::string extra_extension;
1070 util::ParseCacheFilePath(file_path, &resource_id, &md5, &extra_extension);
1071 // The extra_extension shall be ".mounted" iff we're unmounting.
[email protected]ca5f6da2012-06-18 12:54:591072 DCHECK(!to_mount == (extra_extension == util::kMountedArchiveFileExtension));
[email protected]a321b9632012-06-14 03:29:171073
1074 // Get cache entry associated with the resource_id and md5
[email protected]b22f87f2012-07-12 10:53:171075 GDataCacheEntry cache_entry;
1076 if (!GetCacheEntry(resource_id, md5, &cache_entry)) {
[email protected]7f90e77c2012-07-17 09:35:311077 *error = GDATA_FILE_ERROR_NOT_FOUND;
[email protected]a321b9632012-06-14 03:29:171078 return;
1079 }
[email protected]02821102012-07-12 20:19:171080 if (to_mount == cache_entry.is_mounted()) {
[email protected]7f90e77c2012-07-17 09:35:311081 *error = GDATA_FILE_ERROR_INVALID_OPERATION;
[email protected]a321b9632012-06-14 03:29:171082 return;
1083 }
1084
1085 // Get the subdir type and path for the unmounted state.
1086 CacheSubDirectoryType unmounted_subdir =
[email protected]02821102012-07-12 20:19:171087 cache_entry.is_pinned() ? CACHE_TYPE_PERSISTENT : CACHE_TYPE_TMP;
[email protected]a321b9632012-06-14 03:29:171088 FilePath unmounted_path = GetCacheFilePath(
1089 resource_id, md5, unmounted_subdir, CACHED_FILE_FROM_SERVER);
1090
1091 // Get the subdir type and path for the mounted state.
1092 CacheSubDirectoryType mounted_subdir = CACHE_TYPE_PERSISTENT;
1093 FilePath mounted_path = GetCacheFilePath(
1094 resource_id, md5, mounted_subdir, CACHED_FILE_MOUNTED);
1095
1096 // Determine the source and destination paths for moving the cache blob.
1097 FilePath source_path;
1098 CacheSubDirectoryType dest_subdir;
[email protected]a321b9632012-06-14 03:29:171099 if (to_mount) {
1100 source_path = unmounted_path;
1101 *cache_file_path = mounted_path;
1102 dest_subdir = mounted_subdir;
[email protected]02821102012-07-12 20:19:171103 cache_entry.set_is_mounted(true);
[email protected]a321b9632012-06-14 03:29:171104 } else {
1105 source_path = mounted_path;
1106 *cache_file_path = unmounted_path;
1107 dest_subdir = unmounted_subdir;
[email protected]02821102012-07-12 20:19:171108 cache_entry.set_is_mounted(false);
[email protected]a321b9632012-06-14 03:29:171109 }
1110
1111 // Move cache blob from source path to destination path.
1112 *error = ModifyCacheState(source_path, *cache_file_path,
1113 FILE_OPERATION_MOVE, FilePath(), false);
[email protected]7f90e77c2012-07-17 09:35:311114 if (*error == GDATA_FILE_OK) {
[email protected]a321b9632012-06-14 03:29:171115 // Now that cache operation is complete, update cache map
[email protected]48477fe2012-07-12 17:45:081116 cache_entry.set_md5(md5);
[email protected]02821102012-07-12 20:19:171117 cache_entry.set_is_persistent(dest_subdir == CACHE_TYPE_PERSISTENT);
[email protected]48477fe2012-07-12 17:45:081118 metadata_->AddOrUpdateCacheEntry(resource_id, cache_entry);
[email protected]a321b9632012-06-14 03:29:171119 }
1120}
1121
[email protected]c960d222012-06-15 10:03:501122void GDataCache::MarkDirty(const std::string& resource_id,
1123 const std::string& md5,
1124 FileOperationType file_operation_type,
[email protected]7f90e77c2012-07-17 09:35:311125 GDataFileError* error,
[email protected]c960d222012-06-15 10:03:501126 FilePath* cache_file_path) {
1127 AssertOnSequencedWorkerPool();
1128 DCHECK(error);
1129 DCHECK(cache_file_path);
1130
1131 // If file has already been marked dirty in previous instance of chrome, we
1132 // would have lost the md5 info during cache initialization, because the file
1133 // would have been renamed to .local extension.
1134 // So, search for entry in cache without comparing md5.
[email protected]c960d222012-06-15 10:03:501135
1136 // Marking a file dirty means its entry and actual file blob must exist in
1137 // cache.
[email protected]b22f87f2012-07-12 10:53:171138 GDataCacheEntry cache_entry;
1139 if (!GetCacheEntry(resource_id, std::string(), &cache_entry) ||
[email protected]02821102012-07-12 20:19:171140 !cache_entry.is_present()) {
[email protected]c960d222012-06-15 10:03:501141 LOG(WARNING) << "Can't mark dirty a file that wasn't cached: res_id="
1142 << resource_id
1143 << ", md5=" << md5;
[email protected]7f90e77c2012-07-17 09:35:311144 *error = GDATA_FILE_ERROR_NOT_FOUND;
[email protected]c960d222012-06-15 10:03:501145 return;
1146 }
1147
1148 // If a file is already dirty (i.e. MarkDirtyInCache was called before),
1149 // delete outgoing symlink if it exists.
1150 // TODO(benchan): We should only delete outgoing symlink if file is currently
1151 // not being uploaded. However, for now, cache doesn't know if uploading of a
1152 // file is in progress. Per zel, the upload process should be canceled before
1153 // MarkDirtyInCache is called again.
[email protected]02821102012-07-12 20:19:171154 if (cache_entry.is_dirty()) {
[email protected]c960d222012-06-15 10:03:501155 // The file must be in persistent dir.
[email protected]02821102012-07-12 20:19:171156 DCHECK(cache_entry.is_persistent());
[email protected]c960d222012-06-15 10:03:501157
1158 // Determine symlink path in outgoing dir, so as to remove it.
1159 FilePath symlink_path = GetCacheFilePath(
1160 resource_id,
1161 std::string(),
1162 CACHE_TYPE_OUTGOING,
1163 CACHED_FILE_FROM_SERVER);
1164
1165 // We're not moving files here, so simply use empty FilePath for both
1166 // |source_path| and |dest_path| because ModifyCacheState only move files
1167 // if source and destination are different.
1168 *error = ModifyCacheState(
1169 FilePath(), // non-applicable source path
1170 FilePath(), // non-applicable dest path
1171 file_operation_type,
1172 symlink_path,
1173 false /* don't create symlink */);
1174
1175 // Determine current path of dirty file.
[email protected]7f90e77c2012-07-17 09:35:311176 if (*error == GDATA_FILE_OK) {
[email protected]c960d222012-06-15 10:03:501177 *cache_file_path = GetCacheFilePath(
1178 resource_id,
1179 md5,
1180 CACHE_TYPE_PERSISTENT,
1181 CACHED_FILE_LOCALLY_MODIFIED);
1182 }
1183 return;
1184 }
1185
1186 // Move file to persistent dir with new .local extension.
1187
1188 // Get the current path of the file in cache.
1189 FilePath source_path = GetCacheFilePath(
1190 resource_id,
1191 md5,
[email protected]b22f87f2012-07-12 10:53:171192 GetSubDirectoryType(cache_entry),
[email protected]c960d222012-06-15 10:03:501193 CACHED_FILE_FROM_SERVER);
1194
1195 // Determine destination path.
[email protected]3dc88ee2012-07-11 21:04:111196 const CacheSubDirectoryType sub_dir_type = CACHE_TYPE_PERSISTENT;
[email protected]c960d222012-06-15 10:03:501197 *cache_file_path = GetCacheFilePath(resource_id,
1198 md5,
1199 sub_dir_type,
1200 CACHED_FILE_LOCALLY_MODIFIED);
1201
1202 // If file is pinned, update symlink in pinned dir.
1203 FilePath symlink_path;
[email protected]02821102012-07-12 20:19:171204 if (cache_entry.is_pinned()) {
[email protected]c960d222012-06-15 10:03:501205 symlink_path = GetCacheFilePath(resource_id,
1206 std::string(),
1207 CACHE_TYPE_PINNED,
1208 CACHED_FILE_FROM_SERVER);
1209 }
1210
1211 *error = ModifyCacheState(
1212 source_path,
1213 *cache_file_path,
1214 file_operation_type,
1215 symlink_path,
1216 !symlink_path.empty() /* create symlink */);
1217
[email protected]7f90e77c2012-07-17 09:35:311218 if (*error == GDATA_FILE_OK) {
[email protected]c960d222012-06-15 10:03:501219 // Now that file operations have completed, update cache map.
[email protected]48477fe2012-07-12 17:45:081220 cache_entry.set_md5(md5);
[email protected]02821102012-07-12 20:19:171221 cache_entry.set_is_dirty(true);
1222 cache_entry.set_is_persistent(sub_dir_type == CACHE_TYPE_PERSISTENT);
[email protected]48477fe2012-07-12 17:45:081223 metadata_->AddOrUpdateCacheEntry(resource_id, cache_entry);
[email protected]c960d222012-06-15 10:03:501224 }
1225}
1226
[email protected]a321b9632012-06-14 03:29:171227void GDataCache::CommitDirty(const std::string& resource_id,
1228 const std::string& md5,
1229 FileOperationType file_operation_type,
[email protected]7f90e77c2012-07-17 09:35:311230 GDataFileError* error) {
[email protected]a321b9632012-06-14 03:29:171231 AssertOnSequencedWorkerPool();
1232 DCHECK(error);
1233
1234 // If file has already been marked dirty in previous instance of chrome, we
1235 // would have lost the md5 info during cache initialization, because the file
1236 // would have been renamed to .local extension.
1237 // So, search for entry in cache without comparing md5.
[email protected]a321b9632012-06-14 03:29:171238
1239 // Committing a file dirty means its entry and actual file blob must exist in
1240 // cache.
[email protected]b22f87f2012-07-12 10:53:171241 GDataCacheEntry cache_entry;
1242 if (!GetCacheEntry(resource_id, std::string(), &cache_entry) ||
[email protected]02821102012-07-12 20:19:171243 !cache_entry.is_present()) {
[email protected]a321b9632012-06-14 03:29:171244 LOG(WARNING) << "Can't commit dirty a file that wasn't cached: res_id="
1245 << resource_id
1246 << ", md5=" << md5;
[email protected]7f90e77c2012-07-17 09:35:311247 *error = GDATA_FILE_ERROR_NOT_FOUND;
[email protected]a321b9632012-06-14 03:29:171248 return;
1249 }
1250
1251 // If a file is not dirty (it should have been marked dirty via
1252 // MarkDirtyInCache), committing it dirty is an invalid operation.
[email protected]02821102012-07-12 20:19:171253 if (!cache_entry.is_dirty()) {
[email protected]a321b9632012-06-14 03:29:171254 LOG(WARNING) << "Can't commit a non-dirty file: res_id="
1255 << resource_id
1256 << ", md5=" << md5;
[email protected]7f90e77c2012-07-17 09:35:311257 *error = GDATA_FILE_ERROR_INVALID_OPERATION;
[email protected]a321b9632012-06-14 03:29:171258 return;
1259 }
1260
1261 // Dirty files must be in persistent dir.
[email protected]02821102012-07-12 20:19:171262 DCHECK(cache_entry.is_persistent());
[email protected]a321b9632012-06-14 03:29:171263
1264 // Create symlink in outgoing dir.
1265 FilePath symlink_path = GetCacheFilePath(resource_id,
1266 std::string(),
1267 CACHE_TYPE_OUTGOING,
1268 CACHED_FILE_FROM_SERVER);
1269
1270 // Get target path of symlink i.e. current path of the file in cache.
1271 FilePath target_path = GetCacheFilePath(resource_id,
1272 md5,
[email protected]b22f87f2012-07-12 10:53:171273 GetSubDirectoryType(cache_entry),
[email protected]a321b9632012-06-14 03:29:171274 CACHED_FILE_LOCALLY_MODIFIED);
1275
1276 // Since there's no need to move files, use |target_path| for both
1277 // |source_path| and |dest_path|, because ModifyCacheState only moves files
1278 // if source and destination are different.
1279 *error = ModifyCacheState(target_path, // source
1280 target_path, // destination
1281 file_operation_type,
1282 symlink_path,
1283 true /* create symlink */);
1284}
1285
1286void GDataCache::ClearDirty(const std::string& resource_id,
1287 const std::string& md5,
1288 FileOperationType file_operation_type,
[email protected]7f90e77c2012-07-17 09:35:311289 GDataFileError* error) {
[email protected]a321b9632012-06-14 03:29:171290 AssertOnSequencedWorkerPool();
1291 DCHECK(error);
1292
1293 // |md5| is the new .<md5> extension to rename the file to.
1294 // So, search for entry in cache without comparing md5.
[email protected]b22f87f2012-07-12 10:53:171295 GDataCacheEntry cache_entry;
[email protected]a321b9632012-06-14 03:29:171296
1297 // Clearing a dirty file means its entry and actual file blob must exist in
1298 // cache.
[email protected]b22f87f2012-07-12 10:53:171299 if (!GetCacheEntry(resource_id, std::string(), &cache_entry) ||
[email protected]02821102012-07-12 20:19:171300 !cache_entry.is_present()) {
[email protected]a321b9632012-06-14 03:29:171301 LOG(WARNING) << "Can't clear dirty state of a file that wasn't cached: "
1302 << "res_id=" << resource_id
1303 << ", md5=" << md5;
[email protected]7f90e77c2012-07-17 09:35:311304 *error = GDATA_FILE_ERROR_NOT_FOUND;
[email protected]a321b9632012-06-14 03:29:171305 return;
1306 }
1307
1308 // If a file is not dirty (it should have been marked dirty via
1309 // MarkDirtyInCache), clearing its dirty state is an invalid operation.
[email protected]02821102012-07-12 20:19:171310 if (!cache_entry.is_dirty()) {
[email protected]a321b9632012-06-14 03:29:171311 LOG(WARNING) << "Can't clear dirty state of a non-dirty file: res_id="
1312 << resource_id
1313 << ", md5=" << md5;
[email protected]7f90e77c2012-07-17 09:35:311314 *error = GDATA_FILE_ERROR_INVALID_OPERATION;
[email protected]a321b9632012-06-14 03:29:171315 return;
1316 }
1317
1318 // File must be dirty and hence in persistent dir.
[email protected]02821102012-07-12 20:19:171319 DCHECK(cache_entry.is_persistent());
[email protected]a321b9632012-06-14 03:29:171320
1321 // Get the current path of the file in cache.
1322 FilePath source_path = GetCacheFilePath(resource_id,
1323 md5,
[email protected]b22f87f2012-07-12 10:53:171324 GetSubDirectoryType(cache_entry),
[email protected]a321b9632012-06-14 03:29:171325 CACHED_FILE_LOCALLY_MODIFIED);
1326
1327 // Determine destination path.
1328 // If file is pinned, move it to persistent dir with .md5 extension;
1329 // otherwise, move it to tmp dir with .md5 extension.
[email protected]3dc88ee2012-07-11 21:04:111330 const CacheSubDirectoryType sub_dir_type =
[email protected]02821102012-07-12 20:19:171331 cache_entry.is_pinned() ? CACHE_TYPE_PERSISTENT : CACHE_TYPE_TMP;
[email protected]a321b9632012-06-14 03:29:171332 FilePath dest_path = GetCacheFilePath(resource_id,
1333 md5,
1334 sub_dir_type,
1335 CACHED_FILE_FROM_SERVER);
1336
1337 // Delete symlink in outgoing dir.
1338 FilePath symlink_path = GetCacheFilePath(resource_id,
1339 std::string(),
1340 CACHE_TYPE_OUTGOING,
1341 CACHED_FILE_FROM_SERVER);
1342
1343 *error = ModifyCacheState(source_path,
1344 dest_path,
1345 file_operation_type,
1346 symlink_path,
1347 false /* don't create symlink */);
1348
1349 // If file is pinned, update symlink in pinned dir.
[email protected]7f90e77c2012-07-17 09:35:311350 if (*error == GDATA_FILE_OK && cache_entry.is_pinned()) {
[email protected]a321b9632012-06-14 03:29:171351 symlink_path = GetCacheFilePath(resource_id,
1352 std::string(),
1353 CACHE_TYPE_PINNED,
1354 CACHED_FILE_FROM_SERVER);
1355
1356 // Since there's no moving of files here, use |dest_path| for both
1357 // |source_path| and |dest_path|, because ModifyCacheState only moves files
1358 // if source and destination are different.
1359 *error = ModifyCacheState(dest_path, // source path
1360 dest_path, // destination path
1361 file_operation_type,
1362 symlink_path,
1363 true /* create symlink */);
1364 }
1365
[email protected]7f90e77c2012-07-17 09:35:311366 if (*error == GDATA_FILE_OK) {
[email protected]a321b9632012-06-14 03:29:171367 // Now that file operations have completed, update cache map.
[email protected]48477fe2012-07-12 17:45:081368 cache_entry.set_md5(md5);
[email protected]02821102012-07-12 20:19:171369 cache_entry.set_is_dirty(false);
1370 cache_entry.set_is_persistent(sub_dir_type == CACHE_TYPE_PERSISTENT);
[email protected]48477fe2012-07-12 17:45:081371 metadata_->AddOrUpdateCacheEntry(resource_id, cache_entry);
[email protected]a321b9632012-06-14 03:29:171372 }
1373}
1374
1375void GDataCache::Remove(const std::string& resource_id,
[email protected]7f90e77c2012-07-17 09:35:311376 GDataFileError* error) {
[email protected]a321b9632012-06-14 03:29:171377 AssertOnSequencedWorkerPool();
1378 DCHECK(error);
1379
[email protected]3423871a2012-07-12 00:41:271380 // MD5 is not passed into RemoveCacheEntry because we would delete all
1381 // cache files corresponding to <resource_id> regardless of the md5.
[email protected]a321b9632012-06-14 03:29:171382 // So, search for entry in cache without taking md5 into account.
[email protected]b22f87f2012-07-12 10:53:171383 GDataCacheEntry cache_entry;
[email protected]a321b9632012-06-14 03:29:171384
1385 // If entry doesn't exist or is dirty or mounted in cache, nothing to do.
[email protected]b22f87f2012-07-12 10:53:171386 const bool entry_found =
1387 GetCacheEntry(resource_id, std::string(), &cache_entry);
[email protected]02821102012-07-12 20:19:171388 if (!entry_found || cache_entry.is_dirty() || cache_entry.is_mounted()) {
[email protected]a321b9632012-06-14 03:29:171389 DVLOG(1) << "Entry is "
[email protected]b22f87f2012-07-12 10:53:171390 << (entry_found ?
[email protected]02821102012-07-12 20:19:171391 (cache_entry.is_dirty() ? "dirty" : "mounted") :
[email protected]a321b9632012-06-14 03:29:171392 "non-existent")
1393 << " in cache, not removing";
[email protected]7f90e77c2012-07-17 09:35:311394 *error = GDATA_FILE_OK;
[email protected]a321b9632012-06-14 03:29:171395 return;
1396 }
1397
1398 // Determine paths to delete all cache versions of |resource_id| in
1399 // persistent, tmp and pinned directories.
1400 std::vector<FilePath> paths_to_delete;
1401
1402 // For files in persistent and tmp dirs, delete files that match
1403 // "<resource_id>.*".
1404 paths_to_delete.push_back(GetCacheFilePath(resource_id,
[email protected]ca5f6da2012-06-18 12:54:591405 util::kWildCard,
[email protected]a321b9632012-06-14 03:29:171406 CACHE_TYPE_PERSISTENT,
1407 CACHED_FILE_FROM_SERVER));
1408 paths_to_delete.push_back(GetCacheFilePath(resource_id,
[email protected]ca5f6da2012-06-18 12:54:591409 util::kWildCard,
[email protected]a321b9632012-06-14 03:29:171410 CACHE_TYPE_TMP,
1411 CACHED_FILE_FROM_SERVER));
1412
1413 // For pinned files, filename is "<resource_id>" with no extension, so delete
1414 // "<resource_id>".
1415 paths_to_delete.push_back(GetCacheFilePath(resource_id,
1416 std::string(),
1417 CACHE_TYPE_PINNED,
1418 CACHED_FILE_FROM_SERVER));
1419
1420 // Don't delete locally modified (i.e. dirty and possibly outgoing) files.
1421 // Since we're not deleting outgoing symlinks, we don't need to append
1422 // outgoing path to |paths_to_delete|.
1423 FilePath path_to_keep = GetCacheFilePath(resource_id,
1424 std::string(),
1425 CACHE_TYPE_PERSISTENT,
1426 CACHED_FILE_LOCALLY_MODIFIED);
1427
1428 for (size_t i = 0; i < paths_to_delete.size(); ++i) {
1429 DeleteFilesSelectively(paths_to_delete[i], path_to_keep);
1430 }
1431
1432 // Now that all file operations have completed, remove from cache map.
[email protected]3423871a2012-07-12 00:41:271433 metadata_->RemoveCacheEntry(resource_id);
[email protected]a321b9632012-06-14 03:29:171434
[email protected]7f90e77c2012-07-17 09:35:311435 *error = GDATA_FILE_OK;
[email protected]a321b9632012-06-14 03:29:171436}
1437
[email protected]7f90e77c2012-07-17 09:35:311438void GDataCache::OnPinned(GDataFileError* error,
[email protected]73f9c742012-06-15 07:37:131439 const std::string& resource_id,
1440 const std::string& md5,
1441 const CacheOperationCallback& callback) {
1442 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1443 DCHECK(error);
1444
1445 if (!callback.is_null())
1446 callback.Run(*error, resource_id, md5);
1447
[email protected]7f90e77c2012-07-17 09:35:311448 if (*error == GDATA_FILE_OK)
[email protected]73f9c742012-06-15 07:37:131449 FOR_EACH_OBSERVER(Observer, observers_, OnCachePinned(resource_id, md5));
[email protected]3653146a2012-05-29 13:41:471450}
1451
[email protected]7f90e77c2012-07-17 09:35:311452void GDataCache::OnUnpinned(GDataFileError* error,
[email protected]73f9c742012-06-15 07:37:131453 const std::string& resource_id,
1454 const std::string& md5,
1455 const CacheOperationCallback& callback) {
1456 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1457 DCHECK(error);
[email protected]a321b9632012-06-14 03:29:171458
[email protected]73f9c742012-06-15 07:37:131459 if (!callback.is_null())
1460 callback.Run(*error, resource_id, md5);
[email protected]a321b9632012-06-14 03:29:171461
[email protected]7f90e77c2012-07-17 09:35:311462 if (*error == GDATA_FILE_OK)
[email protected]73f9c742012-06-15 07:37:131463 FOR_EACH_OBSERVER(Observer, observers_, OnCacheUnpinned(resource_id, md5));
[email protected]a321b9632012-06-14 03:29:171464
[email protected]73f9c742012-06-15 07:37:131465 // Now the file is moved from "persistent" to "tmp" directory.
1466 // It's a chance to free up space if needed.
1467 bool* has_enough_space = new bool(false);
[email protected]ddbf2052012-07-13 15:07:021468 blocking_task_runner_->PostTask(
[email protected]73f9c742012-06-15 07:37:131469 FROM_HERE,
1470 base::Bind(&GDataCache::FreeDiskSpaceIfNeededFor,
1471 base::Unretained(this),
1472 0,
1473 base::Owned(has_enough_space)));
[email protected]3653146a2012-05-29 13:41:471474}
1475
[email protected]7f90e77c2012-07-17 09:35:311476void GDataCache::OnCommitDirty(GDataFileError* error,
[email protected]d7664c22012-06-18 19:35:491477 const std::string& resource_id,
1478 const std::string& md5,
1479 const CacheOperationCallback& callback) {
1480 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1481 DCHECK(error);
1482
1483 if (!callback.is_null())
1484 callback.Run(*error, resource_id, md5);
1485
[email protected]7f90e77c2012-07-17 09:35:311486 if (*error == GDATA_FILE_OK)
[email protected]d7664c22012-06-18 19:35:491487 FOR_EACH_OBSERVER(Observer, observers_, OnCacheCommitted(resource_id));
1488}
1489
[email protected]4324fdc2012-06-29 05:32:481490void GDataCache::GetCacheEntryHelper(const std::string& resource_id,
1491 const std::string& md5,
1492 bool* success,
[email protected]fae353a2012-07-11 23:30:271493 GDataCacheEntry* cache_entry) {
[email protected]4324fdc2012-06-29 05:32:481494 AssertOnSequencedWorkerPool();
1495 DCHECK(success);
1496 DCHECK(cache_entry);
1497
[email protected]b22f87f2012-07-12 10:53:171498 *success = GetCacheEntry(resource_id, md5, cache_entry);
[email protected]4324fdc2012-06-29 05:32:481499}
1500
[email protected]01ba15f72012-06-09 00:41:051501// static
1502FilePath GDataCache::GetCacheRootPath(Profile* profile) {
1503 FilePath cache_base_path;
1504 chrome::GetUserCacheDirectory(profile->GetPath(), &cache_base_path);
1505 FilePath cache_root_path =
1506 cache_base_path.Append(chrome::kGDataCacheDirname);
1507 return cache_root_path.Append(kGDataCacheVersionDir);
1508}
1509
[email protected]30d9dda2012-06-30 05:56:281510// static
1511std::vector<FilePath> GDataCache::GetCachePaths(
1512 const FilePath& cache_root_path) {
1513 std::vector<FilePath> cache_paths;
1514 // The order should match GDataCache::CacheSubDirectoryType enum.
1515 cache_paths.push_back(cache_root_path.Append(kGDataCacheMetaDir));
1516 cache_paths.push_back(cache_root_path.Append(kGDataCachePinnedDir));
1517 cache_paths.push_back(cache_root_path.Append(kGDataCacheOutgoingDir));
1518 cache_paths.push_back(cache_root_path.Append(kGDataCachePersistentDir));
1519 cache_paths.push_back(cache_root_path.Append(kGDataCacheTmpDir));
1520 cache_paths.push_back(cache_root_path.Append(kGDataCacheTmpDownloadsDir));
1521 cache_paths.push_back(cache_root_path.Append(kGDataCacheTmpDocumentsDir));
1522 return cache_paths;
1523}
1524
1525// static
1526bool GDataCache::CreateCacheDirectories(
1527 const std::vector<FilePath>& paths_to_create) {
1528 bool success = true;
1529
1530 for (size_t i = 0; i < paths_to_create.size(); ++i) {
1531 if (file_util::DirectoryExists(paths_to_create[i]))
1532 continue;
1533
1534 if (!file_util::CreateDirectory(paths_to_create[i])) {
1535 // Error creating this directory, record error and proceed with next one.
1536 success = false;
1537 PLOG(ERROR) << "Error creating directory " << paths_to_create[i].value();
1538 } else {
1539 DVLOG(1) << "Created directory " << paths_to_create[i].value();
1540 }
1541 }
1542 return success;
1543}
1544
[email protected]fae353a2012-07-11 23:30:271545// static
1546GDataCache::CacheSubDirectoryType GDataCache::GetSubDirectoryType(
1547 const GDataCacheEntry& cache_entry) {
[email protected]02821102012-07-12 20:19:171548 return cache_entry.is_persistent() ? CACHE_TYPE_PERSISTENT : CACHE_TYPE_TMP;
[email protected]fae353a2012-07-11 23:30:271549}
1550
[email protected]a321b9632012-06-14 03:29:171551void SetFreeDiskSpaceGetterForTesting(FreeDiskSpaceGetterInterface* getter) {
1552 delete global_free_disk_getter_for_testing; // Safe to delete NULL;
1553 global_free_disk_getter_for_testing = getter;
1554}
1555
[email protected]3653146a2012-05-29 13:41:471556} // namespace gdata