blob: 036e33bc6e0a9cd1c041574ab76984749c5ea4a6 [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"
[email protected]774727282012-08-06 09:14:4412#include "base/path_service.h"
[email protected]3653146a2012-05-29 13:41:4713#include "base/stringprintf.h"
14#include "base/string_util.h"
[email protected]a321b9632012-06-14 03:29:1715#include "base/sys_info.h"
[email protected]12e4c182012-07-12 21:30:0416#include "chrome/browser/chromeos/gdata/gdata.pb.h"
[email protected]ca5f6da2012-06-18 12:54:5917#include "chrome/browser/chromeos/gdata/gdata_cache_metadata.h"
[email protected]32a7fc852012-06-08 17:25:5018#include "chrome/browser/chromeos/gdata/gdata_util.h"
[email protected]01ba15f72012-06-09 00:41:0519#include "chrome/browser/profiles/profile.h"
20#include "chrome/common/chrome_constants.h"
21#include "chrome/common/chrome_paths_internal.h"
[email protected]7986b8d2012-06-14 15:05:1422#include "content/public/browser/browser_thread.h"
23
24using content::BrowserThread;
[email protected]3653146a2012-05-29 13:41:4725
26namespace gdata {
27namespace {
28
[email protected]01ba15f72012-06-09 00:41:0529const FilePath::CharType kGDataCacheVersionDir[] = FILE_PATH_LITERAL("v1");
[email protected]32a7fc852012-06-08 17:25:5030const FilePath::CharType kGDataCacheMetaDir[] = FILE_PATH_LITERAL("meta");
31const FilePath::CharType kGDataCachePinnedDir[] = FILE_PATH_LITERAL("pinned");
32const FilePath::CharType kGDataCacheOutgoingDir[] =
33 FILE_PATH_LITERAL("outgoing");
34const FilePath::CharType kGDataCachePersistentDir[] =
35 FILE_PATH_LITERAL("persistent");
36const FilePath::CharType kGDataCacheTmpDir[] = FILE_PATH_LITERAL("tmp");
37const FilePath::CharType kGDataCacheTmpDownloadsDir[] =
38 FILE_PATH_LITERAL("tmp/downloads");
39const FilePath::CharType kGDataCacheTmpDocumentsDir[] =
40 FILE_PATH_LITERAL("tmp/documents");
41
[email protected]a321b9632012-06-14 03:29:1742// Used to tweak GetAmountOfFreeDiskSpace() behavior for testing.
43FreeDiskSpaceGetterInterface* global_free_disk_getter_for_testing = NULL;
44
45// Gets the amount of free disk space. Use
46// |global_free_disk_getter_for_testing| if set.
47int64 GetAmountOfFreeDiskSpace() {
48 if (global_free_disk_getter_for_testing)
49 return global_free_disk_getter_for_testing->AmountOfFreeDiskSpace();
50
[email protected]774727282012-08-06 09:14:4451 FilePath path;
52 if (!PathService::Get(base::DIR_HOME, &path)) {
53 LOG(ERROR) << "Home directory not found";
54 return -1;
55 }
56 return base::SysInfo::AmountOfFreeDiskSpace(path);
[email protected]a321b9632012-06-14 03:29:1757}
58
59// Returns true if we have sufficient space to store the given number of
60// bytes, while keeping kMinFreeSpace bytes on the disk.
61bool HasEnoughSpaceFor(int64 num_bytes) {
62 int64 free_space = GetAmountOfFreeDiskSpace();
[email protected]d7754f52012-08-01 08:45:3363 // Subtract this as if this portion does not exist.
[email protected]a321b9632012-06-14 03:29:1764 free_space -= kMinFreeSpace;
65 return (free_space >= num_bytes);
66}
67
[email protected]79c3752d2012-07-17 12:10:0868// Create cache directory paths and set permissions.
69void InitCachePaths(const std::vector<FilePath>& cache_paths) {
70 if (cache_paths.size() < GDataCache::NUM_CACHE_TYPES) {
71 NOTREACHED();
72 LOG(ERROR) << "Size of cache_paths is invalid.";
73 return;
74 }
75
76 if (!GDataCache::CreateCacheDirectories(cache_paths))
77 return;
78
79 // Change permissions of cache persistent directory to u+rwx,og+x (711) in
80 // order to allow archive files in that directory to be mounted by cros-disks.
81 file_util::SetPosixFilePermissions(
82 cache_paths[GDataCache::CACHE_TYPE_PERSISTENT],
83 file_util::FILE_PERMISSION_USER_MASK |
84 file_util::FILE_PERMISSION_EXECUTE_BY_GROUP |
85 file_util::FILE_PERMISSION_EXECUTE_BY_OTHERS);
86}
87
[email protected]a321b9632012-06-14 03:29:1788// Remove all files under the given directory, non-recursively.
[email protected]d7754f52012-08-01 08:45:3389// Do not remove recursively as we don't want to touch <gcache>/tmp/downloads,
[email protected]a321b9632012-06-14 03:29:1790// which is used for user initiated downloads like "Save As"
91void RemoveAllFiles(const FilePath& directory) {
92 using file_util::FileEnumerator;
93
94 FileEnumerator enumerator(directory, false /* recursive */,
95 FileEnumerator::FILES);
96 for (FilePath file_path = enumerator.Next(); !file_path.empty();
97 file_path = enumerator.Next()) {
98 DVLOG(1) << "Removing " << file_path.value();
99 if (!file_util::Delete(file_path, false /* recursive */))
100 LOG(WARNING) << "Failed to delete " << file_path.value();
101 }
102}
103
[email protected]a321b9632012-06-14 03:29:17104// Modifies cache state of file on blocking pool, which involves:
105// - moving or copying file (per |file_operation_type|) from |source_path| to
106// |dest_path| if they're different
107// - deleting symlink if |symlink_path| is not empty
108// - creating symlink if |symlink_path| is not empty and |create_symlink| is
109// true.
[email protected]7f90e77c2012-07-17 09:35:31110GDataFileError ModifyCacheState(
[email protected]a321b9632012-06-14 03:29:17111 const FilePath& source_path,
112 const FilePath& dest_path,
113 GDataCache::FileOperationType file_operation_type,
114 const FilePath& symlink_path,
115 bool create_symlink) {
116 // Move or copy |source_path| to |dest_path| if they are different.
117 if (source_path != dest_path) {
118 bool success = false;
119 if (file_operation_type == GDataCache::FILE_OPERATION_MOVE)
120 success = file_util::Move(source_path, dest_path);
121 else if (file_operation_type == GDataCache::FILE_OPERATION_COPY)
122 success = file_util::CopyFile(source_path, dest_path);
123 if (!success) {
[email protected]750af1d2012-07-13 14:32:43124 LOG(ERROR) << "Failed to "
125 << (file_operation_type == GDataCache::FILE_OPERATION_MOVE ?
126 "move " : "copy ")
127 << source_path.value()
128 << " to " << dest_path.value();
[email protected]7f90e77c2012-07-17 09:35:31129 return GDATA_FILE_ERROR_FAILED;
[email protected]a321b9632012-06-14 03:29:17130 } else {
131 DVLOG(1) << (file_operation_type == GDataCache::FILE_OPERATION_MOVE ?
132 "Moved " : "Copied ")
133 << source_path.value()
134 << " to " << dest_path.value();
135 }
136 } else {
137 DVLOG(1) << "No need to move file: source = destination";
138 }
139
140 if (symlink_path.empty())
[email protected]7f90e77c2012-07-17 09:35:31141 return GDATA_FILE_OK;
[email protected]a321b9632012-06-14 03:29:17142
143 // Remove symlink regardless of |create_symlink| because creating a link will
144 // not overwrite an existing one.
[email protected]a321b9632012-06-14 03:29:17145 // We try to save one file operation by not checking if link exists before
146 // deleting it, so unlink may return error if link doesn't exist, but it
147 // doesn't really matter to us.
[email protected]750af1d2012-07-13 14:32:43148 file_util::Delete(symlink_path, false);
[email protected]a321b9632012-06-14 03:29:17149
150 if (!create_symlink)
[email protected]7f90e77c2012-07-17 09:35:31151 return GDATA_FILE_OK;
[email protected]a321b9632012-06-14 03:29:17152
153 // Create new symlink to |dest_path|.
154 if (!file_util::CreateSymbolicLink(dest_path, symlink_path)) {
[email protected]750af1d2012-07-13 14:32:43155 LOG(ERROR) << "Failed to create a symlink from " << symlink_path.value()
156 << " to " << dest_path.value();
[email protected]7f90e77c2012-07-17 09:35:31157 return GDATA_FILE_ERROR_FAILED;
[email protected]a321b9632012-06-14 03:29:17158 }
159
[email protected]7f90e77c2012-07-17 09:35:31160 return GDATA_FILE_OK;
[email protected]a321b9632012-06-14 03:29:17161}
162
163// Deletes all files that match |path_to_delete_pattern| except for
164// |path_to_keep| on blocking pool.
165// If |path_to_keep| is empty, all files in |path_to_delete_pattern| are
166// deleted.
167void DeleteFilesSelectively(const FilePath& path_to_delete_pattern,
168 const FilePath& path_to_keep) {
169 // Enumerate all files in directory of |path_to_delete_pattern| that match
170 // base name of |path_to_delete_pattern|.
171 // If a file is not |path_to_keep|, delete it.
172 bool success = true;
173 file_util::FileEnumerator enumerator(
174 path_to_delete_pattern.DirName(),
175 false, // not recursive
176 static_cast<file_util::FileEnumerator::FileType>(
177 file_util::FileEnumerator::FILES |
178 file_util::FileEnumerator::SHOW_SYM_LINKS),
179 path_to_delete_pattern.BaseName().value());
180 for (FilePath current = enumerator.Next(); !current.empty();
181 current = enumerator.Next()) {
182 // If |path_to_keep| is not empty and same as current, don't delete it.
183 if (!path_to_keep.empty() && current == path_to_keep)
184 continue;
185
[email protected]0b8d4cee2012-07-02 20:46:26186 success = file_util::Delete(current, false);
[email protected]a321b9632012-06-14 03:29:17187 if (!success)
188 DVLOG(1) << "Error deleting " << current.value();
189 else
190 DVLOG(1) << "Deleted " << current.value();
191 }
192}
193
[email protected]b83e5202012-06-27 07:50:24194// Appends |resource_id| ID to |to_fetch| if the file is pinned but not
195// fetched (not present locally), or to |to_upload| if the file is dirty
196// but not uploaded.
197void CollectBacklog(std::vector<std::string>* to_fetch,
198 std::vector<std::string>* to_upload,
199 const std::string& resource_id,
[email protected]fae353a2012-07-11 23:30:27200 const GDataCacheEntry& cache_entry) {
[email protected]b83e5202012-06-27 07:50:24201 DCHECK(to_fetch);
202 DCHECK(to_upload);
[email protected]8764a392012-06-20 06:43:08203
[email protected]02821102012-07-12 20:19:17204 if (cache_entry.is_pinned() && !cache_entry.is_present())
[email protected]b83e5202012-06-27 07:50:24205 to_fetch->push_back(resource_id);
206
[email protected]02821102012-07-12 20:19:17207 if (cache_entry.is_dirty())
[email protected]b83e5202012-06-27 07:50:24208 to_upload->push_back(resource_id);
[email protected]8764a392012-06-20 06:43:08209}
210
[email protected]85b62192012-06-29 19:56:38211// Appends |resource_id| ID to |resource_ids| if the file is pinned and
212// present (cached locally).
213void CollectExistingPinnedFile(std::vector<std::string>* resource_ids,
214 const std::string& resource_id,
[email protected]fae353a2012-07-11 23:30:27215 const GDataCacheEntry& cache_entry) {
[email protected]85b62192012-06-29 19:56:38216 DCHECK(resource_ids);
217
[email protected]02821102012-07-12 20:19:17218 if (cache_entry.is_pinned() && cache_entry.is_present())
[email protected]85b62192012-06-29 19:56:38219 resource_ids->push_back(resource_id);
220}
[email protected]8764a392012-06-20 06:43:08221
[email protected]cd236432012-07-27 18:03:30222// Appends |resource_id| ID to |resource_ids| unconditionally.
223void CollectAnyFile(std::vector<std::string>* resource_ids,
224 const std::string& resource_id,
225 const GDataCacheEntry& /* cache_entry */) {
226 DCHECK(resource_ids);
227
228 resource_ids->push_back(resource_id);
229}
230
[email protected]7986b8d2012-06-14 15:05:14231// Runs callback with pointers dereferenced.
[email protected]f861b392012-08-03 20:41:12232// Used to implement SetMountedStateOnUIThread and ClearAllOnUIThread.
233void RunChangeCacheStateCallback(const ChangeCacheStateCallback& callback,
234 const GDataFileError* error,
235 const FilePath* cache_file_path) {
[email protected]7986b8d2012-06-14 15:05:14236 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
237 DCHECK(error);
238 DCHECK(cache_file_path);
239
240 if (!callback.is_null())
241 callback.Run(*error, *cache_file_path);
242}
243
[email protected]73f9c742012-06-15 07:37:13244// Runs callback with pointers dereferenced.
245// Used to implement *OnUIThread methods.
246void RunCacheOperationCallback(const CacheOperationCallback& callback,
[email protected]7f90e77c2012-07-17 09:35:31247 GDataFileError* error,
[email protected]73f9c742012-06-15 07:37:13248 const std::string& resource_id,
249 const std::string& md5) {
250 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
251 DCHECK(error);
252
253 if (!callback.is_null())
254 callback.Run(*error, resource_id, md5);
255}
256
[email protected]c960d222012-06-15 10:03:50257// Runs callback with pointers dereferenced.
258// Used to implement *OnUIThread methods.
259void RunGetFileFromCacheCallback(const GetFileFromCacheCallback& callback,
[email protected]7f90e77c2012-07-17 09:35:31260 GDataFileError* error,
[email protected]c960d222012-06-15 10:03:50261 const std::string& resource_id,
262 const std::string& md5,
263 FilePath* cache_file_path) {
264 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
265 DCHECK(error);
266 DCHECK(cache_file_path);
267
268 if (!callback.is_null())
269 callback.Run(*error, resource_id, md5, *cache_file_path);
270}
271
[email protected]8764a392012-06-20 06:43:08272// Runs callback with pointers dereferenced.
[email protected]4324fdc2012-06-29 05:32:48273// Used to implement GetResourceIdsOfBacklogOnUIThread().
[email protected]85b62192012-06-29 19:56:38274void RunGetResourceIdsOfBacklogCallback(
275 const GetResourceIdsOfBacklogCallback& callback,
276 std::vector<std::string>* to_fetch,
277 std::vector<std::string>* to_upload) {
[email protected]8764a392012-06-20 06:43:08278 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]b83e5202012-06-27 07:50:24279 DCHECK(to_fetch);
280 DCHECK(to_upload);
[email protected]8764a392012-06-20 06:43:08281
282 if (!callback.is_null())
[email protected]b83e5202012-06-27 07:50:24283 callback.Run(*to_fetch, *to_upload);
[email protected]8764a392012-06-20 06:43:08284}
285
[email protected]4324fdc2012-06-29 05:32:48286// Runs callback with pointers dereferenced.
[email protected]85b62192012-06-29 19:56:38287// Used to implement GetResourceIdsOfExistingPinnedFilesOnUIThread().
288void RunGetResourceIdsCallback(
289 const GetResourceIdsCallback& callback,
290 std::vector<std::string>* resource_ids) {
291 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
292 DCHECK(resource_ids);
293
294 if (!callback.is_null())
295 callback.Run(*resource_ids);
296}
297
298// Runs callback with pointers dereferenced.
[email protected]4324fdc2012-06-29 05:32:48299// Used to implement GetCacheEntryOnUIThread().
300void RunGetCacheEntryCallback(
[email protected]fae353a2012-07-11 23:30:27301 const GetCacheEntryCallback& callback,
[email protected]4324fdc2012-06-29 05:32:48302 bool* success,
[email protected]fae353a2012-07-11 23:30:27303 GDataCacheEntry* cache_entry) {
[email protected]4324fdc2012-06-29 05:32:48304 DCHECK(success);
305 DCHECK(cache_entry);
306
307 if (!callback.is_null())
308 callback.Run(*success, *cache_entry);
309}
310
[email protected]a321b9632012-06-14 03:29:17311} // namespace
[email protected]32a7fc852012-06-08 17:25:50312
[email protected]ddbf2052012-07-13 15:07:02313GDataCache::GDataCache(const FilePath& cache_root_path,
314 base::SequencedTaskRunner* blocking_task_runner)
[email protected]01ba15f72012-06-09 00:41:05315 : cache_root_path_(cache_root_path),
[email protected]6b70c7b2012-06-14 03:10:43316 cache_paths_(GetCachePaths(cache_root_path_)),
[email protected]ddbf2052012-07-13 15:07:02317 blocking_task_runner_(blocking_task_runner),
[email protected]e53ac8f2012-08-02 07:05:00318 weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
[email protected]73f9c742012-06-15 07:37:13319 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]3653146a2012-05-29 13:41:47320}
321
322GDataCache::~GDataCache() {
[email protected]73f9c742012-06-15 07:37:13323 AssertOnSequencedWorkerPool();
[email protected]3653146a2012-05-29 13:41:47324}
325
[email protected]32a7fc852012-06-08 17:25:50326FilePath GDataCache::GetCacheDirectoryPath(
327 CacheSubDirectoryType sub_dir_type) const {
328 DCHECK_LE(0, sub_dir_type);
329 DCHECK_GT(NUM_CACHE_TYPES, sub_dir_type);
330 return cache_paths_[sub_dir_type];
331}
332
333FilePath GDataCache::GetCacheFilePath(const std::string& resource_id,
334 const std::string& md5,
335 CacheSubDirectoryType sub_dir_type,
336 CachedFileOrigin file_origin) const {
337 DCHECK(sub_dir_type != CACHE_TYPE_META);
338
339 // Runs on any thread.
340 // Filename is formatted as resource_id.md5, i.e. resource_id is the base
341 // name and md5 is the extension.
342 std::string base_name = util::EscapeCacheFileName(resource_id);
343 if (file_origin == CACHED_FILE_LOCALLY_MODIFIED) {
344 DCHECK(sub_dir_type == CACHE_TYPE_PERSISTENT);
345 base_name += FilePath::kExtensionSeparator;
[email protected]b83e5202012-06-27 07:50:24346 base_name += util::kLocallyModifiedFileExtension;
[email protected]32a7fc852012-06-08 17:25:50347 } else if (!md5.empty()) {
348 base_name += FilePath::kExtensionSeparator;
349 base_name += util::EscapeCacheFileName(md5);
350 }
351 // For mounted archives the filename is formatted as resource_id.md5.mounted,
352 // i.e. resource_id.md5 is the base name and ".mounted" is the extension
353 if (file_origin == CACHED_FILE_MOUNTED) {
[email protected]a321b9632012-06-14 03:29:17354 DCHECK(sub_dir_type == CACHE_TYPE_PERSISTENT);
355 base_name += FilePath::kExtensionSeparator;
[email protected]ca5f6da2012-06-18 12:54:59356 base_name += util::kMountedArchiveFileExtension;
[email protected]32a7fc852012-06-08 17:25:50357 }
358 return GetCacheDirectoryPath(sub_dir_type).Append(base_name);
359}
360
[email protected]fcc92a52012-06-08 22:54:16361void GDataCache::AssertOnSequencedWorkerPool() {
[email protected]ddbf2052012-07-13 15:07:02362 DCHECK(!blocking_task_runner_ ||
363 blocking_task_runner_->RunsTasksOnCurrentThread());
[email protected]fcc92a52012-06-08 22:54:16364}
365
[email protected]01ba15f72012-06-09 00:41:05366bool GDataCache::IsUnderGDataCacheDirectory(const FilePath& path) const {
367 return cache_root_path_ == path || cache_root_path_.IsParent(path);
368}
369
[email protected]73f9c742012-06-15 07:37:13370void GDataCache::AddObserver(Observer* observer) {
371 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
372 observers_.AddObserver(observer);
373}
374
375void GDataCache::RemoveObserver(Observer* observer) {
376 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
377 observers_.RemoveObserver(observer);
378}
379
[email protected]4324fdc2012-06-29 05:32:48380void GDataCache::GetCacheEntryOnUIThread(
381 const std::string& resource_id,
382 const std::string& md5,
383 const GetCacheEntryCallback& callback) {
384 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
385
386 bool* success = new bool(false);
[email protected]fae353a2012-07-11 23:30:27387 GDataCacheEntry* cache_entry = new GDataCacheEntry;
[email protected]ddbf2052012-07-13 15:07:02388 blocking_task_runner_->PostTaskAndReply(
[email protected]4324fdc2012-06-29 05:32:48389 FROM_HERE,
390 base::Bind(&GDataCache::GetCacheEntryHelper,
391 base::Unretained(this),
392 resource_id,
393 md5,
394 success,
395 cache_entry),
396 base::Bind(&RunGetCacheEntryCallback,
397 callback,
398 base::Owned(success),
399 base::Owned(cache_entry)));
400}
401
[email protected]b83e5202012-06-27 07:50:24402void GDataCache::GetResourceIdsOfBacklogOnUIThread(
[email protected]85b62192012-06-29 19:56:38403 const GetResourceIdsOfBacklogCallback& callback) {
[email protected]8764a392012-06-20 06:43:08404 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
405
[email protected]b83e5202012-06-27 07:50:24406 std::vector<std::string>* to_fetch = new std::vector<std::string>;
407 std::vector<std::string>* to_upload = new std::vector<std::string>;
[email protected]ddbf2052012-07-13 15:07:02408 blocking_task_runner_->PostTaskAndReply(
[email protected]8764a392012-06-20 06:43:08409 FROM_HERE,
[email protected]b83e5202012-06-27 07:50:24410 base::Bind(&GDataCache::GetResourceIdsOfBacklog,
[email protected]8764a392012-06-20 06:43:08411 base::Unretained(this),
[email protected]b83e5202012-06-27 07:50:24412 to_fetch,
413 to_upload),
[email protected]85b62192012-06-29 19:56:38414 base::Bind(&RunGetResourceIdsOfBacklogCallback,
[email protected]8764a392012-06-20 06:43:08415 callback,
[email protected]b83e5202012-06-27 07:50:24416 base::Owned(to_fetch),
417 base::Owned(to_upload)));
[email protected]8764a392012-06-20 06:43:08418}
419
[email protected]85b62192012-06-29 19:56:38420void GDataCache::GetResourceIdsOfExistingPinnedFilesOnUIThread(
421 const GetResourceIdsCallback& callback) {
422 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
423
424 std::vector<std::string>* resource_ids = new std::vector<std::string>;
[email protected]ddbf2052012-07-13 15:07:02425 blocking_task_runner_->PostTaskAndReply(
[email protected]85b62192012-06-29 19:56:38426 FROM_HERE,
427 base::Bind(&GDataCache::GetResourceIdsOfExistingPinnedFiles,
428 base::Unretained(this),
429 resource_ids),
430 base::Bind(&RunGetResourceIdsCallback,
431 callback,
432 base::Owned(resource_ids)));
433}
434
[email protected]cd236432012-07-27 18:03:30435void GDataCache::GetResourceIdsOfAllFilesOnUIThread(
436 const GetResourceIdsCallback& callback) {
437 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
438
439 std::vector<std::string>* resource_ids = new std::vector<std::string>;
440 blocking_task_runner_->PostTaskAndReply(
441 FROM_HERE,
442 base::Bind(&GDataCache::GetResourceIdsOfAllFiles,
443 base::Unretained(this),
444 resource_ids),
445 base::Bind(&RunGetResourceIdsCallback,
446 callback,
447 base::Owned(resource_ids)));
448}
449
[email protected]a321b9632012-06-14 03:29:17450void GDataCache::FreeDiskSpaceIfNeededFor(int64 num_bytes,
451 bool* has_enough_space) {
452 AssertOnSequencedWorkerPool();
453
454 // Do nothing and return if we have enough space.
455 *has_enough_space = HasEnoughSpaceFor(num_bytes);
456 if (*has_enough_space)
457 return;
458
459 // Otherwise, try to free up the disk space.
460 DVLOG(1) << "Freeing up disk space for " << num_bytes;
461 // First remove temporary files from the cache map.
[email protected]ca5f6da2012-06-18 12:54:59462 metadata_->RemoveTemporaryFiles();
[email protected]a321b9632012-06-14 03:29:17463 // Then remove all files under "tmp" directory.
464 RemoveAllFiles(GetCacheDirectoryPath(GDataCache::CACHE_TYPE_TMP));
465
466 // Check the disk space again.
467 *has_enough_space = HasEnoughSpaceFor(num_bytes);
468}
469
[email protected]c960d222012-06-15 10:03:50470void GDataCache::GetFileOnUIThread(const std::string& resource_id,
471 const std::string& md5,
472 const GetFileFromCacheCallback& callback) {
473 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]a321b9632012-06-14 03:29:17474
[email protected]7f90e77c2012-07-17 09:35:31475 GDataFileError* error =
476 new GDataFileError(GDATA_FILE_OK);
[email protected]c960d222012-06-15 10:03:50477 FilePath* cache_file_path = new FilePath;
[email protected]ddbf2052012-07-13 15:07:02478 blocking_task_runner_->PostTaskAndReply(
[email protected]c960d222012-06-15 10:03:50479 FROM_HERE,
480 base::Bind(&GDataCache::GetFile,
481 base::Unretained(this),
482 resource_id,
483 md5,
484 error,
485 cache_file_path),
486 base::Bind(&RunGetFileFromCacheCallback,
487 callback,
488 base::Owned(error),
489 resource_id,
490 md5,
491 base::Owned(cache_file_path)));
[email protected]a321b9632012-06-14 03:29:17492}
493
[email protected]73f9c742012-06-15 07:37:13494void GDataCache::StoreOnUIThread(const std::string& resource_id,
495 const std::string& md5,
496 const FilePath& source_path,
497 FileOperationType file_operation_type,
498 const CacheOperationCallback& callback) {
499 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
500
[email protected]7f90e77c2012-07-17 09:35:31501 GDataFileError* error =
502 new GDataFileError(GDATA_FILE_OK);
[email protected]ddbf2052012-07-13 15:07:02503 blocking_task_runner_->PostTaskAndReply(
[email protected]73f9c742012-06-15 07:37:13504 FROM_HERE,
505 base::Bind(&GDataCache::Store,
506 base::Unretained(this),
507 resource_id,
508 md5,
509 source_path,
510 file_operation_type,
511 error),
512 base::Bind(&RunCacheOperationCallback,
513 callback,
514 base::Owned(error),
515 resource_id,
516 md5));
517}
518
519void GDataCache::PinOnUIThread(const std::string& resource_id,
[email protected]44c0584e2012-06-15 23:55:41520 const std::string& md5,
521 const CacheOperationCallback& callback) {
[email protected]73f9c742012-06-15 07:37:13522 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
523
[email protected]7f90e77c2012-07-17 09:35:31524 GDataFileError* error =
525 new GDataFileError(GDATA_FILE_OK);
[email protected]ddbf2052012-07-13 15:07:02526 blocking_task_runner_->PostTaskAndReply(
[email protected]73f9c742012-06-15 07:37:13527 FROM_HERE,
528 base::Bind(&GDataCache::Pin,
529 base::Unretained(this),
530 resource_id,
531 md5,
532 GDataCache::FILE_OPERATION_MOVE,
533 error),
534 base::Bind(&GDataCache::OnPinned,
[email protected]e53ac8f2012-08-02 07:05:00535 weak_ptr_factory_.GetWeakPtr(),
[email protected]73f9c742012-06-15 07:37:13536 base::Owned(error),
537 resource_id,
538 md5,
539 callback));
540}
541
542void GDataCache::UnpinOnUIThread(const std::string& resource_id,
543 const std::string& md5,
544 const CacheOperationCallback& callback) {
545 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]7f90e77c2012-07-17 09:35:31546 GDataFileError* error =
547 new GDataFileError(GDATA_FILE_OK);
[email protected]ddbf2052012-07-13 15:07:02548 blocking_task_runner_->PostTaskAndReply(
[email protected]73f9c742012-06-15 07:37:13549 FROM_HERE,
550 base::Bind(&GDataCache::Unpin,
551 base::Unretained(this),
552 resource_id,
553 md5,
554 GDataCache::FILE_OPERATION_MOVE,
555 error),
556 base::Bind(&GDataCache::OnUnpinned,
[email protected]e53ac8f2012-08-02 07:05:00557 weak_ptr_factory_.GetWeakPtr(),
[email protected]73f9c742012-06-15 07:37:13558 base::Owned(error),
559 resource_id,
560 md5,
561 callback));
562}
563
564void GDataCache::SetMountedStateOnUIThread(
565 const FilePath& file_path,
566 bool to_mount,
[email protected]f861b392012-08-03 20:41:12567 const ChangeCacheStateCallback& callback) {
[email protected]73f9c742012-06-15 07:37:13568 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
569
[email protected]7f90e77c2012-07-17 09:35:31570 GDataFileError* error =
571 new GDataFileError(GDATA_FILE_OK);
[email protected]73f9c742012-06-15 07:37:13572 FilePath* cache_file_path = new FilePath;
[email protected]ddbf2052012-07-13 15:07:02573 blocking_task_runner_->PostTaskAndReply(
[email protected]73f9c742012-06-15 07:37:13574 FROM_HERE,
575 base::Bind(&GDataCache::SetMountedState,
576 base::Unretained(this),
577 file_path,
578 to_mount,
579 error,
580 cache_file_path),
[email protected]f861b392012-08-03 20:41:12581 base::Bind(&RunChangeCacheStateCallback,
[email protected]73f9c742012-06-15 07:37:13582 callback,
583 base::Owned(error),
584 base::Owned(cache_file_path)));
585}
586
[email protected]c960d222012-06-15 10:03:50587void GDataCache::MarkDirtyOnUIThread(const std::string& resource_id,
588 const std::string& md5,
589 const GetFileFromCacheCallback& callback) {
590 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]73f9c742012-06-15 07:37:13591
[email protected]7f90e77c2012-07-17 09:35:31592 GDataFileError* error =
593 new GDataFileError(GDATA_FILE_OK);
[email protected]c960d222012-06-15 10:03:50594 FilePath* cache_file_path = new FilePath;
[email protected]ddbf2052012-07-13 15:07:02595 blocking_task_runner_->PostTaskAndReply(
[email protected]c960d222012-06-15 10:03:50596 FROM_HERE,
597 base::Bind(&GDataCache::MarkDirty,
598 base::Unretained(this),
599 resource_id,
600 md5,
601 GDataCache::FILE_OPERATION_MOVE,
602 error,
603 cache_file_path),
604 base::Bind(&RunGetFileFromCacheCallback,
605 callback,
606 base::Owned(error),
607 resource_id,
608 md5,
609 base::Owned(cache_file_path)));
[email protected]73f9c742012-06-15 07:37:13610}
611
612void GDataCache::CommitDirtyOnUIThread(const std::string& resource_id,
613 const std::string& md5,
614 const CacheOperationCallback& callback) {
615 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
616
[email protected]7f90e77c2012-07-17 09:35:31617 GDataFileError* error = new GDataFileError(GDATA_FILE_OK);
[email protected]ddbf2052012-07-13 15:07:02618 blocking_task_runner_->PostTaskAndReply(
[email protected]73f9c742012-06-15 07:37:13619 FROM_HERE,
620 base::Bind(&GDataCache::CommitDirty,
621 base::Unretained(this),
622 resource_id,
623 md5,
624 GDataCache::FILE_OPERATION_MOVE,
625 error),
[email protected]d7664c22012-06-18 19:35:49626 base::Bind(&GDataCache::OnCommitDirty,
[email protected]e53ac8f2012-08-02 07:05:00627 weak_ptr_factory_.GetWeakPtr(),
[email protected]73f9c742012-06-15 07:37:13628 base::Owned(error),
629 resource_id,
[email protected]d7664c22012-06-18 19:35:49630 md5,
631 callback));
[email protected]73f9c742012-06-15 07:37:13632}
633
634void GDataCache::ClearDirtyOnUIThread(const std::string& resource_id,
635 const std::string& md5,
636 const CacheOperationCallback& callback) {
637 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
638
[email protected]7f90e77c2012-07-17 09:35:31639 GDataFileError* error =
640 new GDataFileError(GDATA_FILE_OK);
[email protected]ddbf2052012-07-13 15:07:02641 blocking_task_runner_->PostTaskAndReply(
[email protected]73f9c742012-06-15 07:37:13642 FROM_HERE,
643 base::Bind(&GDataCache::ClearDirty,
644 base::Unretained(this),
645 resource_id,
646 md5,
647 GDataCache::FILE_OPERATION_MOVE,
648 error),
649 base::Bind(&RunCacheOperationCallback,
650 callback,
651 base::Owned(error),
652 resource_id,
653 md5));
654}
655
656void GDataCache::RemoveOnUIThread(const std::string& resource_id,
657 const CacheOperationCallback& callback) {
658 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
659
[email protected]7f90e77c2012-07-17 09:35:31660 GDataFileError* error =
661 new GDataFileError(GDATA_FILE_OK);
[email protected]73f9c742012-06-15 07:37:13662
[email protected]ddbf2052012-07-13 15:07:02663 blocking_task_runner_->PostTaskAndReply(
[email protected]73f9c742012-06-15 07:37:13664 FROM_HERE,
665 base::Bind(&GDataCache::Remove,
666 base::Unretained(this),
667 resource_id,
668 error),
669 base::Bind(&RunCacheOperationCallback,
670 callback,
671 base::Owned(error),
672 resource_id,
673 "" /* md5 */));
674}
675
[email protected]f861b392012-08-03 20:41:12676void GDataCache::ClearAllOnUIThread(const ChangeCacheStateCallback& callback) {
677 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
678
679 GDataFileError* error = new GDataFileError(GDATA_FILE_OK);
680
681 blocking_task_runner_->PostTaskAndReply(
682 FROM_HERE,
683 base::Bind(&GDataCache::ClearAll,
684 base::Unretained(this),
685 error),
686 base::Bind(&RunChangeCacheStateCallback,
687 callback,
688 base::Owned(error),
689 &cache_root_path_));
690}
691
[email protected]73f9c742012-06-15 07:37:13692void GDataCache::RequestInitializeOnUIThread() {
693 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
694
[email protected]ddbf2052012-07-13 15:07:02695 blocking_task_runner_->PostTask(
[email protected]73f9c742012-06-15 07:37:13696 FROM_HERE,
[email protected]1ff88ea52012-06-20 04:54:00697 base::Bind(&GDataCache::Initialize, base::Unretained(this)));
[email protected]73f9c742012-06-15 07:37:13698}
699
[email protected]d310bfc2012-08-10 09:41:28700void GDataCache::RequestInitializeOnUIThreadForTesting() {
701 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
702
703 blocking_task_runner_->PostTask(
704 FROM_HERE,
705 base::Bind(&GDataCache::InitializeForTesting, base::Unretained(this)));
706}
707
[email protected]79c3752d2012-07-17 12:10:08708void GDataCache::ForceRescanOnUIThreadForTesting() {
709 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
710
711 blocking_task_runner_->PostTask(
712 FROM_HERE,
713 base::Bind(&GDataCache::ForceRescanForTesting, base::Unretained(this)));
714}
715
[email protected]b22f87f2012-07-12 10:53:17716bool GDataCache::GetCacheEntry(const std::string& resource_id,
717 const std::string& md5,
718 GDataCacheEntry* entry) {
719 DCHECK(entry);
[email protected]73f9c742012-06-15 07:37:13720 AssertOnSequencedWorkerPool();
[email protected]b22f87f2012-07-12 10:53:17721 return metadata_->GetCacheEntry(resource_id, md5, entry);
[email protected]73f9c742012-06-15 07:37:13722}
723
724// static
725GDataCache* GDataCache::CreateGDataCacheOnUIThread(
726 const FilePath& cache_root_path,
[email protected]ddbf2052012-07-13 15:07:02727 base::SequencedTaskRunner* blocking_task_runner) {
[email protected]73f9c742012-06-15 07:37:13728 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]ddbf2052012-07-13 15:07:02729 return new GDataCache(cache_root_path, blocking_task_runner);
[email protected]73f9c742012-06-15 07:37:13730}
731
732void GDataCache::DestroyOnUIThread() {
733 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
734
735 // Invalidate the weak pointer.
[email protected]e53ac8f2012-08-02 07:05:00736 weak_ptr_factory_.InvalidateWeakPtrs();
[email protected]73f9c742012-06-15 07:37:13737
738 // Destroy myself on the blocking pool.
[email protected]ddbf2052012-07-13 15:07:02739 blocking_task_runner_->PostTask(
[email protected]73f9c742012-06-15 07:37:13740 FROM_HERE,
741 base::Bind(&GDataCache::Destroy,
742 base::Unretained(this)));
743}
744
[email protected]ca5f6da2012-06-18 12:54:59745void GDataCache::Initialize() {
746 AssertOnSequencedWorkerPool();
747
[email protected]79c3752d2012-07-17 12:10:08748 InitCachePaths(cache_paths_);
[email protected]f9e5cc02012-07-13 20:08:56749 metadata_ = GDataCacheMetadata::CreateGDataCacheMetadata(
750 blocking_task_runner_).Pass();
751 metadata_->Initialize(cache_paths_);
[email protected]ca5f6da2012-06-18 12:54:59752}
753
[email protected]d310bfc2012-08-10 09:41:28754void GDataCache::InitializeForTesting() {
755 AssertOnSequencedWorkerPool();
756
757 InitCachePaths(cache_paths_);
758 metadata_ = GDataCacheMetadata::CreateGDataCacheMetadataForTesting(
759 blocking_task_runner_).Pass();
760 metadata_->Initialize(cache_paths_);
761}
762
[email protected]73f9c742012-06-15 07:37:13763void GDataCache::Destroy() {
764 AssertOnSequencedWorkerPool();
765 delete this;
766}
767
[email protected]79c3752d2012-07-17 12:10:08768void GDataCache::ForceRescanForTesting() {
769 AssertOnSequencedWorkerPool();
770 metadata_->ForceRescanForTesting(cache_paths_);
771}
772
[email protected]b83e5202012-06-27 07:50:24773void GDataCache::GetResourceIdsOfBacklog(
774 std::vector<std::string>* to_fetch,
775 std::vector<std::string>* to_upload) {
[email protected]8764a392012-06-20 06:43:08776 AssertOnSequencedWorkerPool();
[email protected]b83e5202012-06-27 07:50:24777 DCHECK(to_fetch);
778 DCHECK(to_upload);
[email protected]8764a392012-06-20 06:43:08779
[email protected]b83e5202012-06-27 07:50:24780 metadata_->Iterate(base::Bind(&CollectBacklog, to_fetch, to_upload));
[email protected]8764a392012-06-20 06:43:08781}
782
[email protected]85b62192012-06-29 19:56:38783void GDataCache::GetResourceIdsOfExistingPinnedFiles(
784 std::vector<std::string>* resource_ids) {
785 AssertOnSequencedWorkerPool();
786 DCHECK(resource_ids);
787
788 metadata_->Iterate(base::Bind(&CollectExistingPinnedFile, resource_ids));
789}
790
[email protected]cd236432012-07-27 18:03:30791void GDataCache::GetResourceIdsOfAllFiles(
792 std::vector<std::string>* resource_ids) {
793 AssertOnSequencedWorkerPool();
794 DCHECK(resource_ids);
795
796 metadata_->Iterate(base::Bind(&CollectAnyFile, resource_ids));
797}
798
[email protected]c960d222012-06-15 10:03:50799void GDataCache::GetFile(const std::string& resource_id,
800 const std::string& md5,
[email protected]7f90e77c2012-07-17 09:35:31801 GDataFileError* error,
[email protected]c960d222012-06-15 10:03:50802 FilePath* cache_file_path) {
803 AssertOnSequencedWorkerPool();
804 DCHECK(error);
805 DCHECK(cache_file_path);
806
[email protected]b22f87f2012-07-12 10:53:17807 GDataCacheEntry cache_entry;
808 if (GetCacheEntry(resource_id, md5, &cache_entry) &&
[email protected]02821102012-07-12 20:19:17809 cache_entry.is_present()) {
[email protected]c960d222012-06-15 10:03:50810 CachedFileOrigin file_origin;
[email protected]02821102012-07-12 20:19:17811 if (cache_entry.is_mounted()) {
[email protected]c960d222012-06-15 10:03:50812 file_origin = CACHED_FILE_MOUNTED;
[email protected]02821102012-07-12 20:19:17813 } else if (cache_entry.is_dirty()) {
[email protected]c960d222012-06-15 10:03:50814 file_origin = CACHED_FILE_LOCALLY_MODIFIED;
815 } else {
816 file_origin = CACHED_FILE_FROM_SERVER;
817 }
818 *cache_file_path = GetCacheFilePath(
819 resource_id,
820 md5,
[email protected]b22f87f2012-07-12 10:53:17821 GetSubDirectoryType(cache_entry),
[email protected]c960d222012-06-15 10:03:50822 file_origin);
[email protected]7f90e77c2012-07-17 09:35:31823 *error = GDATA_FILE_OK;
[email protected]c960d222012-06-15 10:03:50824 } else {
[email protected]7f90e77c2012-07-17 09:35:31825 *error = GDATA_FILE_ERROR_NOT_FOUND;
[email protected]c960d222012-06-15 10:03:50826 }
827}
828
[email protected]a321b9632012-06-14 03:29:17829void GDataCache::Store(const std::string& resource_id,
830 const std::string& md5,
831 const FilePath& source_path,
832 FileOperationType file_operation_type,
[email protected]7f90e77c2012-07-17 09:35:31833 GDataFileError* error) {
[email protected]a321b9632012-06-14 03:29:17834 AssertOnSequencedWorkerPool();
835 DCHECK(error);
836
837 FilePath dest_path;
838 FilePath symlink_path;
[email protected]a321b9632012-06-14 03:29:17839 CacheSubDirectoryType sub_dir_type = CACHE_TYPE_TMP;
840
[email protected]a321b9632012-06-14 03:29:17841 // If file was previously pinned, store it in persistent dir and create
842 // symlink in pinned dir.
[email protected]b22f87f2012-07-12 10:53:17843 GDataCacheEntry cache_entry;
844 if (GetCacheEntry(resource_id, md5, &cache_entry)) { // File exists in cache.
[email protected]a321b9632012-06-14 03:29:17845 // If file is dirty or mounted, return error.
[email protected]02821102012-07-12 20:19:17846 if (cache_entry.is_dirty() || cache_entry.is_mounted()) {
[email protected]a321b9632012-06-14 03:29:17847 LOG(WARNING) << "Can't store a file to replace a "
[email protected]02821102012-07-12 20:19:17848 << (cache_entry.is_dirty() ? "dirty" : "mounted")
[email protected]a321b9632012-06-14 03:29:17849 << " file: res_id=" << resource_id
850 << ", md5=" << md5;
[email protected]7f90e77c2012-07-17 09:35:31851 *error = GDATA_FILE_ERROR_IN_USE;
[email protected]a321b9632012-06-14 03:29:17852 return;
853 }
854
[email protected]a321b9632012-06-14 03:29:17855 // If file is pinned, determines destination path.
[email protected]02821102012-07-12 20:19:17856 if (cache_entry.is_pinned()) {
[email protected]a321b9632012-06-14 03:29:17857 sub_dir_type = CACHE_TYPE_PERSISTENT;
858 dest_path = GetCacheFilePath(resource_id, md5, sub_dir_type,
859 CACHED_FILE_FROM_SERVER);
860 symlink_path = GetCacheFilePath(
861 resource_id, std::string(), CACHE_TYPE_PINNED,
862 CACHED_FILE_FROM_SERVER);
863 }
864 }
865
866 // File wasn't pinned or doesn't exist in cache, store in tmp dir.
867 if (dest_path.empty()) {
868 DCHECK_EQ(CACHE_TYPE_TMP, sub_dir_type);
869 dest_path = GetCacheFilePath(resource_id, md5, sub_dir_type,
870 CACHED_FILE_FROM_SERVER);
871 }
872
873 *error = ModifyCacheState(
874 source_path,
875 dest_path,
876 file_operation_type,
877 symlink_path,
878 !symlink_path.empty()); // create symlink
879
880 // Determine search pattern for stale filenames corrresponding to resource_id,
881 // either "<resource_id>*" or "<resource_id>.*".
882 FilePath stale_filenames_pattern;
883 if (md5.empty()) {
884 // No md5 means no extension, append '*' after base name, i.e.
885 // "<resource_id>*".
886 // Cannot call |dest_path|.ReplaceExtension when there's no md5 extension:
887 // if base name of |dest_path| (i.e. escaped resource_id) contains the
888 // extension separator '.', ReplaceExtension will remove it and everything
889 // after it. The result will be nothing like the escaped resource_id.
[email protected]ca5f6da2012-06-18 12:54:59890 stale_filenames_pattern = FilePath(dest_path.value() + util::kWildCard);
[email protected]a321b9632012-06-14 03:29:17891 } else {
892 // Replace md5 extension with '*' i.e. "<resource_id>.*".
893 // Note that ReplaceExtension automatically prefixes the extension with the
894 // extension separator '.'.
[email protected]ca5f6da2012-06-18 12:54:59895 stale_filenames_pattern = dest_path.ReplaceExtension(util::kWildCard);
[email protected]a321b9632012-06-14 03:29:17896 }
897
898 // Delete files that match |stale_filenames_pattern| except for |dest_path|.
899 DeleteFilesSelectively(stale_filenames_pattern, dest_path);
900
[email protected]7f90e77c2012-07-17 09:35:31901 if (*error == GDATA_FILE_OK) {
[email protected]a321b9632012-06-14 03:29:17902 // Now that file operations have completed, update cache map.
[email protected]b22f87f2012-07-12 10:53:17903 cache_entry.set_md5(md5);
[email protected]02821102012-07-12 20:19:17904 cache_entry.set_is_present(true);
905 cache_entry.set_is_persistent(sub_dir_type == CACHE_TYPE_PERSISTENT);
[email protected]b22f87f2012-07-12 10:53:17906 metadata_->AddOrUpdateCacheEntry(resource_id, cache_entry);
[email protected]a321b9632012-06-14 03:29:17907 }
908}
909
910void GDataCache::Pin(const std::string& resource_id,
911 const std::string& md5,
912 FileOperationType file_operation_type,
[email protected]7f90e77c2012-07-17 09:35:31913 GDataFileError* error) {
[email protected]a321b9632012-06-14 03:29:17914 AssertOnSequencedWorkerPool();
915 DCHECK(error);
916
917 FilePath source_path;
918 FilePath dest_path;
919 FilePath symlink_path;
920 bool create_symlink = true;
[email protected]a321b9632012-06-14 03:29:17921 CacheSubDirectoryType sub_dir_type = CACHE_TYPE_PERSISTENT;
922
[email protected]b22f87f2012-07-12 10:53:17923 GDataCacheEntry cache_entry;
924 if (!GetCacheEntry(resource_id, md5, &cache_entry)) {
925 // Entry does not exist in cache.
[email protected]a321b9632012-06-14 03:29:17926 // Set both |dest_path| and |source_path| to /dev/null, so that:
927 // 1) ModifyCacheState won't move files when |source_path| and |dest_path|
928 // are the same.
929 // 2) symlinks to /dev/null will be picked up by GDataSyncClient to download
930 // pinned files that don't exist in cache.
[email protected]30d9dda2012-06-30 05:56:28931 dest_path = FilePath::FromUTF8Unsafe(util::kSymLinkToDevNull);
[email protected]a321b9632012-06-14 03:29:17932 source_path = dest_path;
933
[email protected]2a6fe6e2012-07-10 06:01:04934 // Set sub_dir_type to TMP. The file will be first downloaded in 'tmp',
935 // then moved to 'persistent'.
936 sub_dir_type = CACHE_TYPE_TMP;
[email protected]109eb5c2012-07-12 03:20:05937 } else { // File exists in cache, determines destination path.
[email protected]a321b9632012-06-14 03:29:17938 // Determine source and destination paths.
939
940 // If file is dirty or mounted, don't move it, so determine |dest_path| and
941 // set |source_path| the same, because ModifyCacheState only moves files if
942 // source and destination are different.
[email protected]02821102012-07-12 20:19:17943 if (cache_entry.is_dirty() || cache_entry.is_mounted()) {
944 DCHECK(cache_entry.is_persistent());
[email protected]a321b9632012-06-14 03:29:17945 dest_path = GetCacheFilePath(resource_id,
946 md5,
[email protected]b22f87f2012-07-12 10:53:17947 GetSubDirectoryType(cache_entry),
[email protected]a321b9632012-06-14 03:29:17948 CACHED_FILE_LOCALLY_MODIFIED);
949 source_path = dest_path;
950 } else {
951 // Gets the current path of the file in cache.
952 source_path = GetCacheFilePath(resource_id,
953 md5,
[email protected]b22f87f2012-07-12 10:53:17954 GetSubDirectoryType(cache_entry),
[email protected]a321b9632012-06-14 03:29:17955 CACHED_FILE_FROM_SERVER);
956
957 // If file was pinned before but actual file blob doesn't exist in cache:
958 // - don't need to move the file, so set |dest_path| to |source_path|,
959 // because ModifyCacheState only moves files if source and destination
960 // are different
961 // - don't create symlink since it already exists.
[email protected]02821102012-07-12 20:19:17962 if (!cache_entry.is_present()) {
[email protected]a321b9632012-06-14 03:29:17963 dest_path = source_path;
964 create_symlink = false;
965 } else { // File exists, move it to persistent dir.
966 dest_path = GetCacheFilePath(resource_id,
967 md5,
968 CACHE_TYPE_PERSISTENT,
969 CACHED_FILE_FROM_SERVER);
970 }
971 }
972 }
973
974 // Create symlink in pinned dir.
975 if (create_symlink) {
976 symlink_path = GetCacheFilePath(resource_id,
977 std::string(),
978 CACHE_TYPE_PINNED,
979 CACHED_FILE_FROM_SERVER);
980 }
981
982 *error = ModifyCacheState(source_path,
983 dest_path,
984 file_operation_type,
985 symlink_path,
986 create_symlink);
987
[email protected]7f90e77c2012-07-17 09:35:31988 if (*error == GDATA_FILE_OK) {
[email protected]a321b9632012-06-14 03:29:17989 // Now that file operations have completed, update cache map.
[email protected]b22f87f2012-07-12 10:53:17990 cache_entry.set_md5(md5);
[email protected]02821102012-07-12 20:19:17991 cache_entry.set_is_pinned(true);
992 cache_entry.set_is_persistent(sub_dir_type == CACHE_TYPE_PERSISTENT);
[email protected]b22f87f2012-07-12 10:53:17993 metadata_->AddOrUpdateCacheEntry(resource_id, cache_entry);
[email protected]a321b9632012-06-14 03:29:17994 }
995}
996
997void GDataCache::Unpin(const std::string& resource_id,
998 const std::string& md5,
999 FileOperationType file_operation_type,
[email protected]7f90e77c2012-07-17 09:35:311000 GDataFileError* error) {
[email protected]a321b9632012-06-14 03:29:171001 AssertOnSequencedWorkerPool();
1002 DCHECK(error);
1003
[email protected]a321b9632012-06-14 03:29:171004 // Unpinning a file means its entry must exist in cache.
[email protected]b22f87f2012-07-12 10:53:171005 GDataCacheEntry cache_entry;
1006 if (!GetCacheEntry(resource_id, md5, &cache_entry)) {
[email protected]a321b9632012-06-14 03:29:171007 LOG(WARNING) << "Can't unpin a file that wasn't pinned or cached: res_id="
1008 << resource_id
1009 << ", md5=" << md5;
[email protected]7f90e77c2012-07-17 09:35:311010 *error = GDATA_FILE_ERROR_NOT_FOUND;
[email protected]a321b9632012-06-14 03:29:171011 return;
1012 }
1013
1014 // Entry exists in cache, determines source and destination paths.
1015
1016 FilePath source_path;
1017 FilePath dest_path;
1018 CacheSubDirectoryType sub_dir_type = CACHE_TYPE_TMP;
1019
1020 // If file is dirty or mounted, don't move it, so determine |dest_path| and
1021 // set |source_path| the same, because ModifyCacheState moves files if source
1022 // and destination are different.
[email protected]02821102012-07-12 20:19:171023 if (cache_entry.is_dirty() || cache_entry.is_mounted()) {
[email protected]a321b9632012-06-14 03:29:171024 sub_dir_type = CACHE_TYPE_PERSISTENT;
[email protected]02821102012-07-12 20:19:171025 DCHECK(cache_entry.is_persistent());
[email protected]a321b9632012-06-14 03:29:171026 dest_path = GetCacheFilePath(resource_id,
1027 md5,
[email protected]b22f87f2012-07-12 10:53:171028 GetSubDirectoryType(cache_entry),
[email protected]a321b9632012-06-14 03:29:171029 CACHED_FILE_LOCALLY_MODIFIED);
1030 source_path = dest_path;
1031 } else {
1032 // Gets the current path of the file in cache.
1033 source_path = GetCacheFilePath(resource_id,
1034 md5,
[email protected]b22f87f2012-07-12 10:53:171035 GetSubDirectoryType(cache_entry),
[email protected]a321b9632012-06-14 03:29:171036 CACHED_FILE_FROM_SERVER);
1037
1038 // If file was pinned but actual file blob still doesn't exist in cache,
1039 // don't need to move the file, so set |dest_path| to |source_path|, because
1040 // ModifyCacheState only moves files if source and destination are
1041 // different.
[email protected]02821102012-07-12 20:19:171042 if (!cache_entry.is_present()) {
[email protected]a321b9632012-06-14 03:29:171043 dest_path = source_path;
1044 } else { // File exists, move it to tmp dir.
1045 dest_path = GetCacheFilePath(resource_id, md5,
1046 CACHE_TYPE_TMP,
1047 CACHED_FILE_FROM_SERVER);
1048 }
1049 }
1050
1051 // If file was pinned, get absolute path of symlink in pinned dir so as to
1052 // remove it.
1053 FilePath symlink_path;
[email protected]02821102012-07-12 20:19:171054 if (cache_entry.is_pinned()) {
[email protected]a321b9632012-06-14 03:29:171055 symlink_path = GetCacheFilePath(resource_id,
1056 std::string(),
1057 CACHE_TYPE_PINNED,
1058 CACHED_FILE_FROM_SERVER);
1059 }
1060
1061 *error = ModifyCacheState(
1062 source_path,
1063 dest_path,
1064 file_operation_type,
1065 symlink_path, // This will be deleted if it exists.
1066 false /* don't create symlink*/);
1067
[email protected]7f90e77c2012-07-17 09:35:311068 if (*error == GDATA_FILE_OK) {
[email protected]a321b9632012-06-14 03:29:171069 // Now that file operations have completed, update cache map.
[email protected]02821102012-07-12 20:19:171070 if (cache_entry.is_present()) {
[email protected]48477fe2012-07-12 17:45:081071 cache_entry.set_md5(md5);
[email protected]02821102012-07-12 20:19:171072 cache_entry.set_is_pinned(false);
1073 cache_entry.set_is_persistent(sub_dir_type == CACHE_TYPE_PERSISTENT);
[email protected]48477fe2012-07-12 17:45:081074 metadata_->AddOrUpdateCacheEntry(resource_id, cache_entry);
[email protected]3423871a2012-07-12 00:41:271075 } else {
1076 // Remove the existing entry if we are unpinning a non-present file.
1077 metadata_->RemoveCacheEntry(resource_id);
1078 }
[email protected]a321b9632012-06-14 03:29:171079 }
1080}
1081
1082void GDataCache::SetMountedState(const FilePath& file_path,
1083 bool to_mount,
[email protected]7f90e77c2012-07-17 09:35:311084 GDataFileError *error,
[email protected]a321b9632012-06-14 03:29:171085 FilePath* cache_file_path) {
1086 AssertOnSequencedWorkerPool();
1087 DCHECK(error);
1088 DCHECK(cache_file_path);
1089
1090 // Parse file path to obtain resource_id, md5 and extra_extension.
1091 std::string resource_id;
1092 std::string md5;
1093 std::string extra_extension;
1094 util::ParseCacheFilePath(file_path, &resource_id, &md5, &extra_extension);
1095 // The extra_extension shall be ".mounted" iff we're unmounting.
[email protected]ca5f6da2012-06-18 12:54:591096 DCHECK(!to_mount == (extra_extension == util::kMountedArchiveFileExtension));
[email protected]a321b9632012-06-14 03:29:171097
1098 // Get cache entry associated with the resource_id and md5
[email protected]b22f87f2012-07-12 10:53:171099 GDataCacheEntry cache_entry;
1100 if (!GetCacheEntry(resource_id, md5, &cache_entry)) {
[email protected]7f90e77c2012-07-17 09:35:311101 *error = GDATA_FILE_ERROR_NOT_FOUND;
[email protected]a321b9632012-06-14 03:29:171102 return;
1103 }
[email protected]02821102012-07-12 20:19:171104 if (to_mount == cache_entry.is_mounted()) {
[email protected]7f90e77c2012-07-17 09:35:311105 *error = GDATA_FILE_ERROR_INVALID_OPERATION;
[email protected]a321b9632012-06-14 03:29:171106 return;
1107 }
1108
1109 // Get the subdir type and path for the unmounted state.
1110 CacheSubDirectoryType unmounted_subdir =
[email protected]02821102012-07-12 20:19:171111 cache_entry.is_pinned() ? CACHE_TYPE_PERSISTENT : CACHE_TYPE_TMP;
[email protected]a321b9632012-06-14 03:29:171112 FilePath unmounted_path = GetCacheFilePath(
1113 resource_id, md5, unmounted_subdir, CACHED_FILE_FROM_SERVER);
1114
1115 // Get the subdir type and path for the mounted state.
1116 CacheSubDirectoryType mounted_subdir = CACHE_TYPE_PERSISTENT;
1117 FilePath mounted_path = GetCacheFilePath(
1118 resource_id, md5, mounted_subdir, CACHED_FILE_MOUNTED);
1119
1120 // Determine the source and destination paths for moving the cache blob.
1121 FilePath source_path;
1122 CacheSubDirectoryType dest_subdir;
[email protected]a321b9632012-06-14 03:29:171123 if (to_mount) {
1124 source_path = unmounted_path;
1125 *cache_file_path = mounted_path;
1126 dest_subdir = mounted_subdir;
[email protected]02821102012-07-12 20:19:171127 cache_entry.set_is_mounted(true);
[email protected]a321b9632012-06-14 03:29:171128 } else {
1129 source_path = mounted_path;
1130 *cache_file_path = unmounted_path;
1131 dest_subdir = unmounted_subdir;
[email protected]02821102012-07-12 20:19:171132 cache_entry.set_is_mounted(false);
[email protected]a321b9632012-06-14 03:29:171133 }
1134
1135 // Move cache blob from source path to destination path.
1136 *error = ModifyCacheState(source_path, *cache_file_path,
1137 FILE_OPERATION_MOVE, FilePath(), false);
[email protected]7f90e77c2012-07-17 09:35:311138 if (*error == GDATA_FILE_OK) {
[email protected]a321b9632012-06-14 03:29:171139 // Now that cache operation is complete, update cache map
[email protected]48477fe2012-07-12 17:45:081140 cache_entry.set_md5(md5);
[email protected]02821102012-07-12 20:19:171141 cache_entry.set_is_persistent(dest_subdir == CACHE_TYPE_PERSISTENT);
[email protected]48477fe2012-07-12 17:45:081142 metadata_->AddOrUpdateCacheEntry(resource_id, cache_entry);
[email protected]a321b9632012-06-14 03:29:171143 }
1144}
1145
[email protected]c960d222012-06-15 10:03:501146void GDataCache::MarkDirty(const std::string& resource_id,
1147 const std::string& md5,
1148 FileOperationType file_operation_type,
[email protected]7f90e77c2012-07-17 09:35:311149 GDataFileError* error,
[email protected]c960d222012-06-15 10:03:501150 FilePath* cache_file_path) {
1151 AssertOnSequencedWorkerPool();
1152 DCHECK(error);
1153 DCHECK(cache_file_path);
1154
1155 // If file has already been marked dirty in previous instance of chrome, we
1156 // would have lost the md5 info during cache initialization, because the file
1157 // would have been renamed to .local extension.
1158 // So, search for entry in cache without comparing md5.
[email protected]c960d222012-06-15 10:03:501159
1160 // Marking a file dirty means its entry and actual file blob must exist in
1161 // cache.
[email protected]b22f87f2012-07-12 10:53:171162 GDataCacheEntry cache_entry;
1163 if (!GetCacheEntry(resource_id, std::string(), &cache_entry) ||
[email protected]02821102012-07-12 20:19:171164 !cache_entry.is_present()) {
[email protected]c960d222012-06-15 10:03:501165 LOG(WARNING) << "Can't mark dirty a file that wasn't cached: res_id="
1166 << resource_id
1167 << ", md5=" << md5;
[email protected]7f90e77c2012-07-17 09:35:311168 *error = GDATA_FILE_ERROR_NOT_FOUND;
[email protected]c960d222012-06-15 10:03:501169 return;
1170 }
1171
1172 // If a file is already dirty (i.e. MarkDirtyInCache was called before),
1173 // delete outgoing symlink if it exists.
1174 // TODO(benchan): We should only delete outgoing symlink if file is currently
1175 // not being uploaded. However, for now, cache doesn't know if uploading of a
1176 // file is in progress. Per zel, the upload process should be canceled before
1177 // MarkDirtyInCache is called again.
[email protected]02821102012-07-12 20:19:171178 if (cache_entry.is_dirty()) {
[email protected]c960d222012-06-15 10:03:501179 // The file must be in persistent dir.
[email protected]02821102012-07-12 20:19:171180 DCHECK(cache_entry.is_persistent());
[email protected]c960d222012-06-15 10:03:501181
1182 // Determine symlink path in outgoing dir, so as to remove it.
1183 FilePath symlink_path = GetCacheFilePath(
1184 resource_id,
1185 std::string(),
1186 CACHE_TYPE_OUTGOING,
1187 CACHED_FILE_FROM_SERVER);
1188
1189 // We're not moving files here, so simply use empty FilePath for both
1190 // |source_path| and |dest_path| because ModifyCacheState only move files
1191 // if source and destination are different.
1192 *error = ModifyCacheState(
1193 FilePath(), // non-applicable source path
1194 FilePath(), // non-applicable dest path
1195 file_operation_type,
1196 symlink_path,
1197 false /* don't create symlink */);
1198
1199 // Determine current path of dirty file.
[email protected]7f90e77c2012-07-17 09:35:311200 if (*error == GDATA_FILE_OK) {
[email protected]c960d222012-06-15 10:03:501201 *cache_file_path = GetCacheFilePath(
1202 resource_id,
1203 md5,
1204 CACHE_TYPE_PERSISTENT,
1205 CACHED_FILE_LOCALLY_MODIFIED);
1206 }
1207 return;
1208 }
1209
1210 // Move file to persistent dir with new .local extension.
1211
1212 // Get the current path of the file in cache.
1213 FilePath source_path = GetCacheFilePath(
1214 resource_id,
1215 md5,
[email protected]b22f87f2012-07-12 10:53:171216 GetSubDirectoryType(cache_entry),
[email protected]c960d222012-06-15 10:03:501217 CACHED_FILE_FROM_SERVER);
1218
1219 // Determine destination path.
[email protected]3dc88ee2012-07-11 21:04:111220 const CacheSubDirectoryType sub_dir_type = CACHE_TYPE_PERSISTENT;
[email protected]c960d222012-06-15 10:03:501221 *cache_file_path = GetCacheFilePath(resource_id,
1222 md5,
1223 sub_dir_type,
1224 CACHED_FILE_LOCALLY_MODIFIED);
1225
1226 // If file is pinned, update symlink in pinned dir.
1227 FilePath symlink_path;
[email protected]02821102012-07-12 20:19:171228 if (cache_entry.is_pinned()) {
[email protected]c960d222012-06-15 10:03:501229 symlink_path = GetCacheFilePath(resource_id,
1230 std::string(),
1231 CACHE_TYPE_PINNED,
1232 CACHED_FILE_FROM_SERVER);
1233 }
1234
1235 *error = ModifyCacheState(
1236 source_path,
1237 *cache_file_path,
1238 file_operation_type,
1239 symlink_path,
1240 !symlink_path.empty() /* create symlink */);
1241
[email protected]7f90e77c2012-07-17 09:35:311242 if (*error == GDATA_FILE_OK) {
[email protected]c960d222012-06-15 10:03:501243 // Now that file operations have completed, update cache map.
[email protected]48477fe2012-07-12 17:45:081244 cache_entry.set_md5(md5);
[email protected]02821102012-07-12 20:19:171245 cache_entry.set_is_dirty(true);
1246 cache_entry.set_is_persistent(sub_dir_type == CACHE_TYPE_PERSISTENT);
[email protected]48477fe2012-07-12 17:45:081247 metadata_->AddOrUpdateCacheEntry(resource_id, cache_entry);
[email protected]c960d222012-06-15 10:03:501248 }
1249}
1250
[email protected]a321b9632012-06-14 03:29:171251void GDataCache::CommitDirty(const std::string& resource_id,
1252 const std::string& md5,
1253 FileOperationType file_operation_type,
[email protected]7f90e77c2012-07-17 09:35:311254 GDataFileError* error) {
[email protected]a321b9632012-06-14 03:29:171255 AssertOnSequencedWorkerPool();
1256 DCHECK(error);
1257
1258 // If file has already been marked dirty in previous instance of chrome, we
1259 // would have lost the md5 info during cache initialization, because the file
1260 // would have been renamed to .local extension.
1261 // So, search for entry in cache without comparing md5.
[email protected]a321b9632012-06-14 03:29:171262
1263 // Committing a file dirty means its entry and actual file blob must exist in
1264 // cache.
[email protected]b22f87f2012-07-12 10:53:171265 GDataCacheEntry cache_entry;
1266 if (!GetCacheEntry(resource_id, std::string(), &cache_entry) ||
[email protected]02821102012-07-12 20:19:171267 !cache_entry.is_present()) {
[email protected]a321b9632012-06-14 03:29:171268 LOG(WARNING) << "Can't commit dirty a file that wasn't cached: res_id="
1269 << resource_id
1270 << ", md5=" << md5;
[email protected]7f90e77c2012-07-17 09:35:311271 *error = GDATA_FILE_ERROR_NOT_FOUND;
[email protected]a321b9632012-06-14 03:29:171272 return;
1273 }
1274
1275 // If a file is not dirty (it should have been marked dirty via
1276 // MarkDirtyInCache), committing it dirty is an invalid operation.
[email protected]02821102012-07-12 20:19:171277 if (!cache_entry.is_dirty()) {
[email protected]a321b9632012-06-14 03:29:171278 LOG(WARNING) << "Can't commit a non-dirty file: res_id="
1279 << resource_id
1280 << ", md5=" << md5;
[email protected]7f90e77c2012-07-17 09:35:311281 *error = GDATA_FILE_ERROR_INVALID_OPERATION;
[email protected]a321b9632012-06-14 03:29:171282 return;
1283 }
1284
1285 // Dirty files must be in persistent dir.
[email protected]02821102012-07-12 20:19:171286 DCHECK(cache_entry.is_persistent());
[email protected]a321b9632012-06-14 03:29:171287
1288 // Create symlink in outgoing dir.
1289 FilePath symlink_path = GetCacheFilePath(resource_id,
1290 std::string(),
1291 CACHE_TYPE_OUTGOING,
1292 CACHED_FILE_FROM_SERVER);
1293
1294 // Get target path of symlink i.e. current path of the file in cache.
1295 FilePath target_path = GetCacheFilePath(resource_id,
1296 md5,
[email protected]b22f87f2012-07-12 10:53:171297 GetSubDirectoryType(cache_entry),
[email protected]a321b9632012-06-14 03:29:171298 CACHED_FILE_LOCALLY_MODIFIED);
1299
1300 // Since there's no need to move files, use |target_path| for both
1301 // |source_path| and |dest_path|, because ModifyCacheState only moves files
1302 // if source and destination are different.
1303 *error = ModifyCacheState(target_path, // source
1304 target_path, // destination
1305 file_operation_type,
1306 symlink_path,
1307 true /* create symlink */);
1308}
1309
1310void GDataCache::ClearDirty(const std::string& resource_id,
1311 const std::string& md5,
1312 FileOperationType file_operation_type,
[email protected]7f90e77c2012-07-17 09:35:311313 GDataFileError* error) {
[email protected]a321b9632012-06-14 03:29:171314 AssertOnSequencedWorkerPool();
1315 DCHECK(error);
1316
1317 // |md5| is the new .<md5> extension to rename the file to.
1318 // So, search for entry in cache without comparing md5.
[email protected]b22f87f2012-07-12 10:53:171319 GDataCacheEntry cache_entry;
[email protected]a321b9632012-06-14 03:29:171320
1321 // Clearing a dirty file means its entry and actual file blob must exist in
1322 // cache.
[email protected]b22f87f2012-07-12 10:53:171323 if (!GetCacheEntry(resource_id, std::string(), &cache_entry) ||
[email protected]02821102012-07-12 20:19:171324 !cache_entry.is_present()) {
[email protected]a321b9632012-06-14 03:29:171325 LOG(WARNING) << "Can't clear dirty state of a file that wasn't cached: "
1326 << "res_id=" << resource_id
1327 << ", md5=" << md5;
[email protected]7f90e77c2012-07-17 09:35:311328 *error = GDATA_FILE_ERROR_NOT_FOUND;
[email protected]a321b9632012-06-14 03:29:171329 return;
1330 }
1331
1332 // If a file is not dirty (it should have been marked dirty via
1333 // MarkDirtyInCache), clearing its dirty state is an invalid operation.
[email protected]02821102012-07-12 20:19:171334 if (!cache_entry.is_dirty()) {
[email protected]a321b9632012-06-14 03:29:171335 LOG(WARNING) << "Can't clear dirty state of a non-dirty file: res_id="
1336 << resource_id
1337 << ", md5=" << md5;
[email protected]7f90e77c2012-07-17 09:35:311338 *error = GDATA_FILE_ERROR_INVALID_OPERATION;
[email protected]a321b9632012-06-14 03:29:171339 return;
1340 }
1341
1342 // File must be dirty and hence in persistent dir.
[email protected]02821102012-07-12 20:19:171343 DCHECK(cache_entry.is_persistent());
[email protected]a321b9632012-06-14 03:29:171344
1345 // Get the current path of the file in cache.
1346 FilePath source_path = GetCacheFilePath(resource_id,
1347 md5,
[email protected]b22f87f2012-07-12 10:53:171348 GetSubDirectoryType(cache_entry),
[email protected]a321b9632012-06-14 03:29:171349 CACHED_FILE_LOCALLY_MODIFIED);
1350
1351 // Determine destination path.
1352 // If file is pinned, move it to persistent dir with .md5 extension;
1353 // otherwise, move it to tmp dir with .md5 extension.
[email protected]3dc88ee2012-07-11 21:04:111354 const CacheSubDirectoryType sub_dir_type =
[email protected]02821102012-07-12 20:19:171355 cache_entry.is_pinned() ? CACHE_TYPE_PERSISTENT : CACHE_TYPE_TMP;
[email protected]a321b9632012-06-14 03:29:171356 FilePath dest_path = GetCacheFilePath(resource_id,
1357 md5,
1358 sub_dir_type,
1359 CACHED_FILE_FROM_SERVER);
1360
1361 // Delete symlink in outgoing dir.
1362 FilePath symlink_path = GetCacheFilePath(resource_id,
1363 std::string(),
1364 CACHE_TYPE_OUTGOING,
1365 CACHED_FILE_FROM_SERVER);
1366
1367 *error = ModifyCacheState(source_path,
1368 dest_path,
1369 file_operation_type,
1370 symlink_path,
1371 false /* don't create symlink */);
1372
1373 // If file is pinned, update symlink in pinned dir.
[email protected]7f90e77c2012-07-17 09:35:311374 if (*error == GDATA_FILE_OK && cache_entry.is_pinned()) {
[email protected]a321b9632012-06-14 03:29:171375 symlink_path = GetCacheFilePath(resource_id,
1376 std::string(),
1377 CACHE_TYPE_PINNED,
1378 CACHED_FILE_FROM_SERVER);
1379
1380 // Since there's no moving of files here, use |dest_path| for both
1381 // |source_path| and |dest_path|, because ModifyCacheState only moves files
1382 // if source and destination are different.
1383 *error = ModifyCacheState(dest_path, // source path
1384 dest_path, // destination path
1385 file_operation_type,
1386 symlink_path,
1387 true /* create symlink */);
1388 }
1389
[email protected]7f90e77c2012-07-17 09:35:311390 if (*error == GDATA_FILE_OK) {
[email protected]a321b9632012-06-14 03:29:171391 // Now that file operations have completed, update cache map.
[email protected]48477fe2012-07-12 17:45:081392 cache_entry.set_md5(md5);
[email protected]02821102012-07-12 20:19:171393 cache_entry.set_is_dirty(false);
1394 cache_entry.set_is_persistent(sub_dir_type == CACHE_TYPE_PERSISTENT);
[email protected]48477fe2012-07-12 17:45:081395 metadata_->AddOrUpdateCacheEntry(resource_id, cache_entry);
[email protected]a321b9632012-06-14 03:29:171396 }
1397}
1398
1399void GDataCache::Remove(const std::string& resource_id,
[email protected]7f90e77c2012-07-17 09:35:311400 GDataFileError* error) {
[email protected]a321b9632012-06-14 03:29:171401 AssertOnSequencedWorkerPool();
1402 DCHECK(error);
1403
[email protected]3423871a2012-07-12 00:41:271404 // MD5 is not passed into RemoveCacheEntry because we would delete all
1405 // cache files corresponding to <resource_id> regardless of the md5.
[email protected]a321b9632012-06-14 03:29:171406 // So, search for entry in cache without taking md5 into account.
[email protected]b22f87f2012-07-12 10:53:171407 GDataCacheEntry cache_entry;
[email protected]a321b9632012-06-14 03:29:171408
1409 // If entry doesn't exist or is dirty or mounted in cache, nothing to do.
[email protected]b22f87f2012-07-12 10:53:171410 const bool entry_found =
1411 GetCacheEntry(resource_id, std::string(), &cache_entry);
[email protected]02821102012-07-12 20:19:171412 if (!entry_found || cache_entry.is_dirty() || cache_entry.is_mounted()) {
[email protected]a321b9632012-06-14 03:29:171413 DVLOG(1) << "Entry is "
[email protected]b22f87f2012-07-12 10:53:171414 << (entry_found ?
[email protected]02821102012-07-12 20:19:171415 (cache_entry.is_dirty() ? "dirty" : "mounted") :
[email protected]a321b9632012-06-14 03:29:171416 "non-existent")
1417 << " in cache, not removing";
[email protected]7f90e77c2012-07-17 09:35:311418 *error = GDATA_FILE_OK;
[email protected]a321b9632012-06-14 03:29:171419 return;
1420 }
1421
1422 // Determine paths to delete all cache versions of |resource_id| in
1423 // persistent, tmp and pinned directories.
1424 std::vector<FilePath> paths_to_delete;
1425
1426 // For files in persistent and tmp dirs, delete files that match
1427 // "<resource_id>.*".
1428 paths_to_delete.push_back(GetCacheFilePath(resource_id,
[email protected]ca5f6da2012-06-18 12:54:591429 util::kWildCard,
[email protected]a321b9632012-06-14 03:29:171430 CACHE_TYPE_PERSISTENT,
1431 CACHED_FILE_FROM_SERVER));
1432 paths_to_delete.push_back(GetCacheFilePath(resource_id,
[email protected]ca5f6da2012-06-18 12:54:591433 util::kWildCard,
[email protected]a321b9632012-06-14 03:29:171434 CACHE_TYPE_TMP,
1435 CACHED_FILE_FROM_SERVER));
1436
1437 // For pinned files, filename is "<resource_id>" with no extension, so delete
1438 // "<resource_id>".
1439 paths_to_delete.push_back(GetCacheFilePath(resource_id,
1440 std::string(),
1441 CACHE_TYPE_PINNED,
1442 CACHED_FILE_FROM_SERVER));
1443
1444 // Don't delete locally modified (i.e. dirty and possibly outgoing) files.
1445 // Since we're not deleting outgoing symlinks, we don't need to append
1446 // outgoing path to |paths_to_delete|.
1447 FilePath path_to_keep = GetCacheFilePath(resource_id,
1448 std::string(),
1449 CACHE_TYPE_PERSISTENT,
1450 CACHED_FILE_LOCALLY_MODIFIED);
1451
1452 for (size_t i = 0; i < paths_to_delete.size(); ++i) {
1453 DeleteFilesSelectively(paths_to_delete[i], path_to_keep);
1454 }
1455
1456 // Now that all file operations have completed, remove from cache map.
[email protected]3423871a2012-07-12 00:41:271457 metadata_->RemoveCacheEntry(resource_id);
[email protected]a321b9632012-06-14 03:29:171458
[email protected]7f90e77c2012-07-17 09:35:311459 *error = GDATA_FILE_OK;
[email protected]a321b9632012-06-14 03:29:171460}
1461
[email protected]f861b392012-08-03 20:41:121462void GDataCache::ClearAll(GDataFileError* error) {
1463 AssertOnSequencedWorkerPool();
1464 DCHECK(error);
1465
1466 bool success = file_util::Delete(cache_root_path_, true);
1467 Initialize();
1468
1469 *error = success ? GDATA_FILE_OK : GDATA_FILE_ERROR_FAILED;
1470}
1471
[email protected]7f90e77c2012-07-17 09:35:311472void GDataCache::OnPinned(GDataFileError* error,
[email protected]73f9c742012-06-15 07:37:131473 const std::string& resource_id,
1474 const std::string& md5,
1475 const CacheOperationCallback& callback) {
1476 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1477 DCHECK(error);
1478
1479 if (!callback.is_null())
1480 callback.Run(*error, resource_id, md5);
1481
[email protected]7f90e77c2012-07-17 09:35:311482 if (*error == GDATA_FILE_OK)
[email protected]73f9c742012-06-15 07:37:131483 FOR_EACH_OBSERVER(Observer, observers_, OnCachePinned(resource_id, md5));
[email protected]3653146a2012-05-29 13:41:471484}
1485
[email protected]7f90e77c2012-07-17 09:35:311486void GDataCache::OnUnpinned(GDataFileError* error,
[email protected]73f9c742012-06-15 07:37:131487 const std::string& resource_id,
1488 const std::string& md5,
1489 const CacheOperationCallback& callback) {
1490 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1491 DCHECK(error);
[email protected]a321b9632012-06-14 03:29:171492
[email protected]73f9c742012-06-15 07:37:131493 if (!callback.is_null())
1494 callback.Run(*error, resource_id, md5);
[email protected]a321b9632012-06-14 03:29:171495
[email protected]7f90e77c2012-07-17 09:35:311496 if (*error == GDATA_FILE_OK)
[email protected]73f9c742012-06-15 07:37:131497 FOR_EACH_OBSERVER(Observer, observers_, OnCacheUnpinned(resource_id, md5));
[email protected]a321b9632012-06-14 03:29:171498
[email protected]73f9c742012-06-15 07:37:131499 // Now the file is moved from "persistent" to "tmp" directory.
1500 // It's a chance to free up space if needed.
1501 bool* has_enough_space = new bool(false);
[email protected]ddbf2052012-07-13 15:07:021502 blocking_task_runner_->PostTask(
[email protected]73f9c742012-06-15 07:37:131503 FROM_HERE,
1504 base::Bind(&GDataCache::FreeDiskSpaceIfNeededFor,
1505 base::Unretained(this),
1506 0,
1507 base::Owned(has_enough_space)));
[email protected]3653146a2012-05-29 13:41:471508}
1509
[email protected]7f90e77c2012-07-17 09:35:311510void GDataCache::OnCommitDirty(GDataFileError* error,
[email protected]d7664c22012-06-18 19:35:491511 const std::string& resource_id,
1512 const std::string& md5,
1513 const CacheOperationCallback& callback) {
1514 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1515 DCHECK(error);
1516
1517 if (!callback.is_null())
1518 callback.Run(*error, resource_id, md5);
1519
[email protected]7f90e77c2012-07-17 09:35:311520 if (*error == GDATA_FILE_OK)
[email protected]d7664c22012-06-18 19:35:491521 FOR_EACH_OBSERVER(Observer, observers_, OnCacheCommitted(resource_id));
1522}
1523
[email protected]4324fdc2012-06-29 05:32:481524void GDataCache::GetCacheEntryHelper(const std::string& resource_id,
1525 const std::string& md5,
1526 bool* success,
[email protected]fae353a2012-07-11 23:30:271527 GDataCacheEntry* cache_entry) {
[email protected]4324fdc2012-06-29 05:32:481528 AssertOnSequencedWorkerPool();
1529 DCHECK(success);
1530 DCHECK(cache_entry);
1531
[email protected]b22f87f2012-07-12 10:53:171532 *success = GetCacheEntry(resource_id, md5, cache_entry);
[email protected]4324fdc2012-06-29 05:32:481533}
1534
[email protected]01ba15f72012-06-09 00:41:051535// static
1536FilePath GDataCache::GetCacheRootPath(Profile* profile) {
1537 FilePath cache_base_path;
1538 chrome::GetUserCacheDirectory(profile->GetPath(), &cache_base_path);
1539 FilePath cache_root_path =
1540 cache_base_path.Append(chrome::kGDataCacheDirname);
1541 return cache_root_path.Append(kGDataCacheVersionDir);
1542}
1543
[email protected]30d9dda2012-06-30 05:56:281544// static
1545std::vector<FilePath> GDataCache::GetCachePaths(
1546 const FilePath& cache_root_path) {
1547 std::vector<FilePath> cache_paths;
1548 // The order should match GDataCache::CacheSubDirectoryType enum.
1549 cache_paths.push_back(cache_root_path.Append(kGDataCacheMetaDir));
1550 cache_paths.push_back(cache_root_path.Append(kGDataCachePinnedDir));
1551 cache_paths.push_back(cache_root_path.Append(kGDataCacheOutgoingDir));
1552 cache_paths.push_back(cache_root_path.Append(kGDataCachePersistentDir));
1553 cache_paths.push_back(cache_root_path.Append(kGDataCacheTmpDir));
1554 cache_paths.push_back(cache_root_path.Append(kGDataCacheTmpDownloadsDir));
1555 cache_paths.push_back(cache_root_path.Append(kGDataCacheTmpDocumentsDir));
1556 return cache_paths;
1557}
1558
1559// static
1560bool GDataCache::CreateCacheDirectories(
1561 const std::vector<FilePath>& paths_to_create) {
1562 bool success = true;
1563
1564 for (size_t i = 0; i < paths_to_create.size(); ++i) {
1565 if (file_util::DirectoryExists(paths_to_create[i]))
1566 continue;
1567
1568 if (!file_util::CreateDirectory(paths_to_create[i])) {
1569 // Error creating this directory, record error and proceed with next one.
1570 success = false;
1571 PLOG(ERROR) << "Error creating directory " << paths_to_create[i].value();
1572 } else {
1573 DVLOG(1) << "Created directory " << paths_to_create[i].value();
1574 }
1575 }
1576 return success;
1577}
1578
[email protected]fae353a2012-07-11 23:30:271579// static
1580GDataCache::CacheSubDirectoryType GDataCache::GetSubDirectoryType(
1581 const GDataCacheEntry& cache_entry) {
[email protected]02821102012-07-12 20:19:171582 return cache_entry.is_persistent() ? CACHE_TYPE_PERSISTENT : CACHE_TYPE_TMP;
[email protected]fae353a2012-07-11 23:30:271583}
1584
[email protected]a321b9632012-06-14 03:29:171585void SetFreeDiskSpaceGetterForTesting(FreeDiskSpaceGetterInterface* getter) {
1586 delete global_free_disk_getter_for_testing; // Safe to delete NULL;
1587 global_free_disk_getter_for_testing = getter;
1588}
1589
[email protected]3653146a2012-05-29 13:41:471590} // namespace gdata