blob: 6ec993ae65c54e8f48b9a2397984704e1cd970ed [file] [log] [blame]
[email protected]3653146a2012-05-29 13:41:471// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/chromeos/gdata/gdata_cache.h"
6
7#include <vector>
8
[email protected]a321b9632012-06-14 03:29:179#include "base/chromeos/chromeos_version.h"
10#include "base/file_util.h"
[email protected]3653146a2012-05-29 13:41:4711#include "base/logging.h"
12#include "base/stringprintf.h"
13#include "base/string_util.h"
[email protected]a321b9632012-06-14 03:29:1714#include "base/sys_info.h"
[email protected]12e4c182012-07-12 21:30:0415#include "chrome/browser/chromeos/gdata/gdata.pb.h"
[email protected]ca5f6da2012-06-18 12:54:5916#include "chrome/browser/chromeos/gdata/gdata_cache_metadata.h"
[email protected]32a7fc852012-06-08 17:25:5017#include "chrome/browser/chromeos/gdata/gdata_util.h"
[email protected]01ba15f72012-06-09 00:41:0518#include "chrome/browser/profiles/profile.h"
19#include "chrome/common/chrome_constants.h"
20#include "chrome/common/chrome_paths_internal.h"
[email protected]7986b8d2012-06-14 15:05:1421#include "content/public/browser/browser_thread.h"
22
23using content::BrowserThread;
[email protected]3653146a2012-05-29 13:41:4724
25namespace gdata {
26namespace {
27
[email protected]01ba15f72012-06-09 00:41:0528const FilePath::CharType kGDataCacheVersionDir[] = FILE_PATH_LITERAL("v1");
[email protected]32a7fc852012-06-08 17:25:5029const FilePath::CharType kGDataCacheMetaDir[] = FILE_PATH_LITERAL("meta");
30const FilePath::CharType kGDataCachePinnedDir[] = FILE_PATH_LITERAL("pinned");
31const FilePath::CharType kGDataCacheOutgoingDir[] =
32 FILE_PATH_LITERAL("outgoing");
33const FilePath::CharType kGDataCachePersistentDir[] =
34 FILE_PATH_LITERAL("persistent");
35const FilePath::CharType kGDataCacheTmpDir[] = FILE_PATH_LITERAL("tmp");
36const FilePath::CharType kGDataCacheTmpDownloadsDir[] =
37 FILE_PATH_LITERAL("tmp/downloads");
38const FilePath::CharType kGDataCacheTmpDocumentsDir[] =
39 FILE_PATH_LITERAL("tmp/documents");
40
[email protected]a321b9632012-06-14 03:29:1741// Returns the home directory path, or an empty string if the home directory
42// is not found.
43// Copied from webkit/chromeos/cros_mount_point_provider.h.
44// TODO(satorux): Share the code.
45std::string GetHomeDirectory() {
46 if (base::chromeos::IsRunningOnChromeOS())
47 return "/home/chronos/user";
[email protected]3653146a2012-05-29 13:41:4748
[email protected]a321b9632012-06-14 03:29:1749 const char* home = getenv("HOME");
50 if (home)
51 return home;
52 return "";
53}
54
55// Used to tweak GetAmountOfFreeDiskSpace() behavior for testing.
56FreeDiskSpaceGetterInterface* global_free_disk_getter_for_testing = NULL;
57
58// Gets the amount of free disk space. Use
59// |global_free_disk_getter_for_testing| if set.
60int64 GetAmountOfFreeDiskSpace() {
61 if (global_free_disk_getter_for_testing)
62 return global_free_disk_getter_for_testing->AmountOfFreeDiskSpace();
63
64 return base::SysInfo::AmountOfFreeDiskSpace(
65 FilePath::FromUTF8Unsafe(GetHomeDirectory()));
66}
67
68// Returns true if we have sufficient space to store the given number of
69// bytes, while keeping kMinFreeSpace bytes on the disk.
70bool HasEnoughSpaceFor(int64 num_bytes) {
71 int64 free_space = GetAmountOfFreeDiskSpace();
72 // Substract this as if this portion does not exist.
73 free_space -= kMinFreeSpace;
74 return (free_space >= num_bytes);
75}
76
77// Remove all files under the given directory, non-recursively.
78// Do not remove recursively as we don't want to touch <gache>/tmp/downloads,
79// which is used for user initiated downloads like "Save As"
80void RemoveAllFiles(const FilePath& directory) {
81 using file_util::FileEnumerator;
82
83 FileEnumerator enumerator(directory, false /* recursive */,
84 FileEnumerator::FILES);
85 for (FilePath file_path = enumerator.Next(); !file_path.empty();
86 file_path = enumerator.Next()) {
87 DVLOG(1) << "Removing " << file_path.value();
88 if (!file_util::Delete(file_path, false /* recursive */))
89 LOG(WARNING) << "Failed to delete " << file_path.value();
90 }
91}
92
[email protected]a321b9632012-06-14 03:29:1793// Modifies cache state of file on blocking pool, which involves:
94// - moving or copying file (per |file_operation_type|) from |source_path| to
95// |dest_path| if they're different
96// - deleting symlink if |symlink_path| is not empty
97// - creating symlink if |symlink_path| is not empty and |create_symlink| is
98// true.
99base::PlatformFileError ModifyCacheState(
100 const FilePath& source_path,
101 const FilePath& dest_path,
102 GDataCache::FileOperationType file_operation_type,
103 const FilePath& symlink_path,
104 bool create_symlink) {
105 // Move or copy |source_path| to |dest_path| if they are different.
106 if (source_path != dest_path) {
107 bool success = false;
108 if (file_operation_type == GDataCache::FILE_OPERATION_MOVE)
109 success = file_util::Move(source_path, dest_path);
110 else if (file_operation_type == GDataCache::FILE_OPERATION_COPY)
111 success = file_util::CopyFile(source_path, dest_path);
112 if (!success) {
[email protected]750af1d2012-07-13 14:32:43113 LOG(ERROR) << "Failed to "
114 << (file_operation_type == GDataCache::FILE_OPERATION_MOVE ?
115 "move " : "copy ")
116 << source_path.value()
117 << " to " << dest_path.value();
118 return base::PLATFORM_FILE_ERROR_FAILED;
[email protected]a321b9632012-06-14 03:29:17119 } else {
120 DVLOG(1) << (file_operation_type == GDataCache::FILE_OPERATION_MOVE ?
121 "Moved " : "Copied ")
122 << source_path.value()
123 << " to " << dest_path.value();
124 }
125 } else {
126 DVLOG(1) << "No need to move file: source = destination";
127 }
128
129 if (symlink_path.empty())
130 return base::PLATFORM_FILE_OK;
131
132 // Remove symlink regardless of |create_symlink| because creating a link will
133 // not overwrite an existing one.
[email protected]a321b9632012-06-14 03:29:17134 // We try to save one file operation by not checking if link exists before
135 // deleting it, so unlink may return error if link doesn't exist, but it
136 // doesn't really matter to us.
[email protected]750af1d2012-07-13 14:32:43137 file_util::Delete(symlink_path, false);
[email protected]a321b9632012-06-14 03:29:17138
139 if (!create_symlink)
140 return base::PLATFORM_FILE_OK;
141
142 // Create new symlink to |dest_path|.
143 if (!file_util::CreateSymbolicLink(dest_path, symlink_path)) {
[email protected]750af1d2012-07-13 14:32:43144 LOG(ERROR) << "Failed to create a symlink from " << symlink_path.value()
145 << " to " << dest_path.value();
146 return base::PLATFORM_FILE_ERROR_FAILED;
[email protected]a321b9632012-06-14 03:29:17147 }
148
149 return base::PLATFORM_FILE_OK;
150}
151
152// Deletes all files that match |path_to_delete_pattern| except for
153// |path_to_keep| on blocking pool.
154// If |path_to_keep| is empty, all files in |path_to_delete_pattern| are
155// deleted.
156void DeleteFilesSelectively(const FilePath& path_to_delete_pattern,
157 const FilePath& path_to_keep) {
158 // Enumerate all files in directory of |path_to_delete_pattern| that match
159 // base name of |path_to_delete_pattern|.
160 // If a file is not |path_to_keep|, delete it.
161 bool success = true;
162 file_util::FileEnumerator enumerator(
163 path_to_delete_pattern.DirName(),
164 false, // not recursive
165 static_cast<file_util::FileEnumerator::FileType>(
166 file_util::FileEnumerator::FILES |
167 file_util::FileEnumerator::SHOW_SYM_LINKS),
168 path_to_delete_pattern.BaseName().value());
169 for (FilePath current = enumerator.Next(); !current.empty();
170 current = enumerator.Next()) {
171 // If |path_to_keep| is not empty and same as current, don't delete it.
172 if (!path_to_keep.empty() && current == path_to_keep)
173 continue;
174
[email protected]0b8d4cee2012-07-02 20:46:26175 success = file_util::Delete(current, false);
[email protected]a321b9632012-06-14 03:29:17176 if (!success)
177 DVLOG(1) << "Error deleting " << current.value();
178 else
179 DVLOG(1) << "Deleted " << current.value();
180 }
181}
182
[email protected]b83e5202012-06-27 07:50:24183// Appends |resource_id| ID to |to_fetch| if the file is pinned but not
184// fetched (not present locally), or to |to_upload| if the file is dirty
185// but not uploaded.
186void CollectBacklog(std::vector<std::string>* to_fetch,
187 std::vector<std::string>* to_upload,
188 const std::string& resource_id,
[email protected]fae353a2012-07-11 23:30:27189 const GDataCacheEntry& cache_entry) {
[email protected]b83e5202012-06-27 07:50:24190 DCHECK(to_fetch);
191 DCHECK(to_upload);
[email protected]8764a392012-06-20 06:43:08192
[email protected]02821102012-07-12 20:19:17193 if (cache_entry.is_pinned() && !cache_entry.is_present())
[email protected]b83e5202012-06-27 07:50:24194 to_fetch->push_back(resource_id);
195
[email protected]02821102012-07-12 20:19:17196 if (cache_entry.is_dirty())
[email protected]b83e5202012-06-27 07:50:24197 to_upload->push_back(resource_id);
[email protected]8764a392012-06-20 06:43:08198}
199
[email protected]85b62192012-06-29 19:56:38200// Appends |resource_id| ID to |resource_ids| if the file is pinned and
201// present (cached locally).
202void CollectExistingPinnedFile(std::vector<std::string>* resource_ids,
203 const std::string& resource_id,
[email protected]fae353a2012-07-11 23:30:27204 const GDataCacheEntry& cache_entry) {
[email protected]85b62192012-06-29 19:56:38205 DCHECK(resource_ids);
206
[email protected]02821102012-07-12 20:19:17207 if (cache_entry.is_pinned() && cache_entry.is_present())
[email protected]85b62192012-06-29 19:56:38208 resource_ids->push_back(resource_id);
209}
[email protected]8764a392012-06-20 06:43:08210
[email protected]7986b8d2012-06-14 15:05:14211// Runs callback with pointers dereferenced.
212// Used to implement SetMountedStateOnUIThread.
[email protected]73f9c742012-06-15 07:37:13213void RunSetMountedStateCallback(const SetMountedStateCallback& callback,
214 base::PlatformFileError* error,
215 FilePath* cache_file_path) {
[email protected]7986b8d2012-06-14 15:05:14216 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
217 DCHECK(error);
218 DCHECK(cache_file_path);
219
220 if (!callback.is_null())
221 callback.Run(*error, *cache_file_path);
222}
223
[email protected]73f9c742012-06-15 07:37:13224// Runs callback with pointers dereferenced.
225// Used to implement *OnUIThread methods.
226void RunCacheOperationCallback(const CacheOperationCallback& callback,
227 base::PlatformFileError* error,
228 const std::string& resource_id,
229 const std::string& md5) {
230 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
231 DCHECK(error);
232
233 if (!callback.is_null())
234 callback.Run(*error, resource_id, md5);
235}
236
[email protected]c960d222012-06-15 10:03:50237// Runs callback with pointers dereferenced.
238// Used to implement *OnUIThread methods.
239void RunGetFileFromCacheCallback(const GetFileFromCacheCallback& callback,
240 base::PlatformFileError* error,
241 const std::string& resource_id,
242 const std::string& md5,
243 FilePath* cache_file_path) {
244 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
245 DCHECK(error);
246 DCHECK(cache_file_path);
247
248 if (!callback.is_null())
249 callback.Run(*error, resource_id, md5, *cache_file_path);
250}
251
[email protected]8764a392012-06-20 06:43:08252// Runs callback with pointers dereferenced.
[email protected]4324fdc2012-06-29 05:32:48253// Used to implement GetResourceIdsOfBacklogOnUIThread().
[email protected]85b62192012-06-29 19:56:38254void RunGetResourceIdsOfBacklogCallback(
255 const GetResourceIdsOfBacklogCallback& callback,
256 std::vector<std::string>* to_fetch,
257 std::vector<std::string>* to_upload) {
[email protected]8764a392012-06-20 06:43:08258 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]b83e5202012-06-27 07:50:24259 DCHECK(to_fetch);
260 DCHECK(to_upload);
[email protected]8764a392012-06-20 06:43:08261
262 if (!callback.is_null())
[email protected]b83e5202012-06-27 07:50:24263 callback.Run(*to_fetch, *to_upload);
[email protected]8764a392012-06-20 06:43:08264}
265
[email protected]4324fdc2012-06-29 05:32:48266// Runs callback with pointers dereferenced.
[email protected]85b62192012-06-29 19:56:38267// Used to implement GetResourceIdsOfExistingPinnedFilesOnUIThread().
268void RunGetResourceIdsCallback(
269 const GetResourceIdsCallback& callback,
270 std::vector<std::string>* resource_ids) {
271 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
272 DCHECK(resource_ids);
273
274 if (!callback.is_null())
275 callback.Run(*resource_ids);
276}
277
278// Runs callback with pointers dereferenced.
[email protected]4324fdc2012-06-29 05:32:48279// Used to implement GetCacheEntryOnUIThread().
280void RunGetCacheEntryCallback(
[email protected]fae353a2012-07-11 23:30:27281 const GetCacheEntryCallback& callback,
[email protected]4324fdc2012-06-29 05:32:48282 bool* success,
[email protected]fae353a2012-07-11 23:30:27283 GDataCacheEntry* cache_entry) {
[email protected]4324fdc2012-06-29 05:32:48284 DCHECK(success);
285 DCHECK(cache_entry);
286
287 if (!callback.is_null())
288 callback.Run(*success, *cache_entry);
289}
290
[email protected]a321b9632012-06-14 03:29:17291} // namespace
[email protected]32a7fc852012-06-08 17:25:50292
[email protected]fcc92a52012-06-08 22:54:16293GDataCache::GDataCache(
294 const FilePath& cache_root_path,
295 base::SequencedWorkerPool* pool,
296 const base::SequencedWorkerPool::SequenceToken& sequence_token)
[email protected]01ba15f72012-06-09 00:41:05297 : cache_root_path_(cache_root_path),
[email protected]6b70c7b2012-06-14 03:10:43298 cache_paths_(GetCachePaths(cache_root_path_)),
[email protected]01ba15f72012-06-09 00:41:05299 pool_(pool),
[email protected]73f9c742012-06-15 07:37:13300 sequence_token_(sequence_token),
301 ui_weak_ptr_factory_(this),
302 ui_weak_ptr_(ui_weak_ptr_factory_.GetWeakPtr()) {
303 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]3653146a2012-05-29 13:41:47304}
305
306GDataCache::~GDataCache() {
[email protected]73f9c742012-06-15 07:37:13307 AssertOnSequencedWorkerPool();
[email protected]3653146a2012-05-29 13:41:47308}
309
[email protected]32a7fc852012-06-08 17:25:50310FilePath GDataCache::GetCacheDirectoryPath(
311 CacheSubDirectoryType sub_dir_type) const {
312 DCHECK_LE(0, sub_dir_type);
313 DCHECK_GT(NUM_CACHE_TYPES, sub_dir_type);
314 return cache_paths_[sub_dir_type];
315}
316
317FilePath GDataCache::GetCacheFilePath(const std::string& resource_id,
318 const std::string& md5,
319 CacheSubDirectoryType sub_dir_type,
320 CachedFileOrigin file_origin) const {
321 DCHECK(sub_dir_type != CACHE_TYPE_META);
322
323 // Runs on any thread.
324 // Filename is formatted as resource_id.md5, i.e. resource_id is the base
325 // name and md5 is the extension.
326 std::string base_name = util::EscapeCacheFileName(resource_id);
327 if (file_origin == CACHED_FILE_LOCALLY_MODIFIED) {
328 DCHECK(sub_dir_type == CACHE_TYPE_PERSISTENT);
329 base_name += FilePath::kExtensionSeparator;
[email protected]b83e5202012-06-27 07:50:24330 base_name += util::kLocallyModifiedFileExtension;
[email protected]32a7fc852012-06-08 17:25:50331 } else if (!md5.empty()) {
332 base_name += FilePath::kExtensionSeparator;
333 base_name += util::EscapeCacheFileName(md5);
334 }
335 // For mounted archives the filename is formatted as resource_id.md5.mounted,
336 // i.e. resource_id.md5 is the base name and ".mounted" is the extension
337 if (file_origin == CACHED_FILE_MOUNTED) {
[email protected]a321b9632012-06-14 03:29:17338 DCHECK(sub_dir_type == CACHE_TYPE_PERSISTENT);
339 base_name += FilePath::kExtensionSeparator;
[email protected]ca5f6da2012-06-18 12:54:59340 base_name += util::kMountedArchiveFileExtension;
[email protected]32a7fc852012-06-08 17:25:50341 }
342 return GetCacheDirectoryPath(sub_dir_type).Append(base_name);
343}
344
[email protected]fcc92a52012-06-08 22:54:16345void GDataCache::AssertOnSequencedWorkerPool() {
346 DCHECK(!pool_ || pool_->IsRunningSequenceOnCurrentThread(sequence_token_));
347}
348
[email protected]01ba15f72012-06-09 00:41:05349bool GDataCache::IsUnderGDataCacheDirectory(const FilePath& path) const {
350 return cache_root_path_ == path || cache_root_path_.IsParent(path);
351}
352
[email protected]73f9c742012-06-15 07:37:13353void GDataCache::AddObserver(Observer* observer) {
354 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
355 observers_.AddObserver(observer);
356}
357
358void GDataCache::RemoveObserver(Observer* observer) {
359 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
360 observers_.RemoveObserver(observer);
361}
362
[email protected]4324fdc2012-06-29 05:32:48363void GDataCache::GetCacheEntryOnUIThread(
364 const std::string& resource_id,
365 const std::string& md5,
366 const GetCacheEntryCallback& callback) {
367 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
368
369 bool* success = new bool(false);
[email protected]fae353a2012-07-11 23:30:27370 GDataCacheEntry* cache_entry = new GDataCacheEntry;
[email protected]4324fdc2012-06-29 05:32:48371 pool_->GetSequencedTaskRunner(sequence_token_)->PostTaskAndReply(
372 FROM_HERE,
373 base::Bind(&GDataCache::GetCacheEntryHelper,
374 base::Unretained(this),
375 resource_id,
376 md5,
377 success,
378 cache_entry),
379 base::Bind(&RunGetCacheEntryCallback,
380 callback,
381 base::Owned(success),
382 base::Owned(cache_entry)));
383}
384
[email protected]b83e5202012-06-27 07:50:24385void GDataCache::GetResourceIdsOfBacklogOnUIThread(
[email protected]85b62192012-06-29 19:56:38386 const GetResourceIdsOfBacklogCallback& callback) {
[email protected]8764a392012-06-20 06:43:08387 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
388
[email protected]b83e5202012-06-27 07:50:24389 std::vector<std::string>* to_fetch = new std::vector<std::string>;
390 std::vector<std::string>* to_upload = new std::vector<std::string>;
[email protected]8764a392012-06-20 06:43:08391 pool_->GetSequencedTaskRunner(sequence_token_)->PostTaskAndReply(
392 FROM_HERE,
[email protected]b83e5202012-06-27 07:50:24393 base::Bind(&GDataCache::GetResourceIdsOfBacklog,
[email protected]8764a392012-06-20 06:43:08394 base::Unretained(this),
[email protected]b83e5202012-06-27 07:50:24395 to_fetch,
396 to_upload),
[email protected]85b62192012-06-29 19:56:38397 base::Bind(&RunGetResourceIdsOfBacklogCallback,
[email protected]8764a392012-06-20 06:43:08398 callback,
[email protected]b83e5202012-06-27 07:50:24399 base::Owned(to_fetch),
400 base::Owned(to_upload)));
[email protected]8764a392012-06-20 06:43:08401}
402
[email protected]85b62192012-06-29 19:56:38403void GDataCache::GetResourceIdsOfExistingPinnedFilesOnUIThread(
404 const GetResourceIdsCallback& callback) {
405 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
406
407 std::vector<std::string>* resource_ids = new std::vector<std::string>;
408 pool_->GetSequencedTaskRunner(sequence_token_)->PostTaskAndReply(
409 FROM_HERE,
410 base::Bind(&GDataCache::GetResourceIdsOfExistingPinnedFiles,
411 base::Unretained(this),
412 resource_ids),
413 base::Bind(&RunGetResourceIdsCallback,
414 callback,
415 base::Owned(resource_ids)));
416}
417
[email protected]a321b9632012-06-14 03:29:17418void GDataCache::FreeDiskSpaceIfNeededFor(int64 num_bytes,
419 bool* has_enough_space) {
420 AssertOnSequencedWorkerPool();
421
422 // Do nothing and return if we have enough space.
423 *has_enough_space = HasEnoughSpaceFor(num_bytes);
424 if (*has_enough_space)
425 return;
426
427 // Otherwise, try to free up the disk space.
428 DVLOG(1) << "Freeing up disk space for " << num_bytes;
429 // First remove temporary files from the cache map.
[email protected]ca5f6da2012-06-18 12:54:59430 metadata_->RemoveTemporaryFiles();
[email protected]a321b9632012-06-14 03:29:17431 // Then remove all files under "tmp" directory.
432 RemoveAllFiles(GetCacheDirectoryPath(GDataCache::CACHE_TYPE_TMP));
433
434 // Check the disk space again.
435 *has_enough_space = HasEnoughSpaceFor(num_bytes);
436}
437
[email protected]c960d222012-06-15 10:03:50438void GDataCache::GetFileOnUIThread(const std::string& resource_id,
439 const std::string& md5,
440 const GetFileFromCacheCallback& callback) {
441 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]a321b9632012-06-14 03:29:17442
[email protected]c960d222012-06-15 10:03:50443 base::PlatformFileError* error =
444 new base::PlatformFileError(base::PLATFORM_FILE_OK);
445 FilePath* cache_file_path = new FilePath;
446 pool_->GetSequencedTaskRunner(sequence_token_)->PostTaskAndReply(
447 FROM_HERE,
448 base::Bind(&GDataCache::GetFile,
449 base::Unretained(this),
450 resource_id,
451 md5,
452 error,
453 cache_file_path),
454 base::Bind(&RunGetFileFromCacheCallback,
455 callback,
456 base::Owned(error),
457 resource_id,
458 md5,
459 base::Owned(cache_file_path)));
[email protected]a321b9632012-06-14 03:29:17460}
461
[email protected]73f9c742012-06-15 07:37:13462void GDataCache::StoreOnUIThread(const std::string& resource_id,
463 const std::string& md5,
464 const FilePath& source_path,
465 FileOperationType file_operation_type,
466 const CacheOperationCallback& callback) {
467 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
468
469 base::PlatformFileError* error =
470 new base::PlatformFileError(base::PLATFORM_FILE_OK);
471 pool_->GetSequencedTaskRunner(sequence_token_)->PostTaskAndReply(
472 FROM_HERE,
473 base::Bind(&GDataCache::Store,
474 base::Unretained(this),
475 resource_id,
476 md5,
477 source_path,
478 file_operation_type,
479 error),
480 base::Bind(&RunCacheOperationCallback,
481 callback,
482 base::Owned(error),
483 resource_id,
484 md5));
485}
486
487void GDataCache::PinOnUIThread(const std::string& resource_id,
[email protected]44c0584e2012-06-15 23:55:41488 const std::string& md5,
489 const CacheOperationCallback& callback) {
[email protected]73f9c742012-06-15 07:37:13490 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
491
492 base::PlatformFileError* error =
493 new base::PlatformFileError(base::PLATFORM_FILE_OK);
494 pool_->GetSequencedTaskRunner(sequence_token_)->PostTaskAndReply(
495 FROM_HERE,
496 base::Bind(&GDataCache::Pin,
497 base::Unretained(this),
498 resource_id,
499 md5,
500 GDataCache::FILE_OPERATION_MOVE,
501 error),
502 base::Bind(&GDataCache::OnPinned,
503 ui_weak_ptr_,
504 base::Owned(error),
505 resource_id,
506 md5,
507 callback));
508}
509
510void GDataCache::UnpinOnUIThread(const std::string& resource_id,
511 const std::string& md5,
512 const CacheOperationCallback& callback) {
513 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
514 base::PlatformFileError* error =
515 new base::PlatformFileError(base::PLATFORM_FILE_OK);
516 pool_->GetSequencedTaskRunner(sequence_token_)->PostTaskAndReply(
517 FROM_HERE,
518 base::Bind(&GDataCache::Unpin,
519 base::Unretained(this),
520 resource_id,
521 md5,
522 GDataCache::FILE_OPERATION_MOVE,
523 error),
524 base::Bind(&GDataCache::OnUnpinned,
525 ui_weak_ptr_,
526 base::Owned(error),
527 resource_id,
528 md5,
529 callback));
530}
531
532void GDataCache::SetMountedStateOnUIThread(
533 const FilePath& file_path,
534 bool to_mount,
535 const SetMountedStateCallback& callback) {
536 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
537
538 base::PlatformFileError* error =
539 new base::PlatformFileError(base::PLATFORM_FILE_OK);
540 FilePath* cache_file_path = new FilePath;
541 pool_->GetSequencedTaskRunner(sequence_token_)->PostTaskAndReply(
542 FROM_HERE,
543 base::Bind(&GDataCache::SetMountedState,
544 base::Unretained(this),
545 file_path,
546 to_mount,
547 error,
548 cache_file_path),
549 base::Bind(&RunSetMountedStateCallback,
550 callback,
551 base::Owned(error),
552 base::Owned(cache_file_path)));
553}
554
[email protected]c960d222012-06-15 10:03:50555void GDataCache::MarkDirtyOnUIThread(const std::string& resource_id,
556 const std::string& md5,
557 const GetFileFromCacheCallback& callback) {
558 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]73f9c742012-06-15 07:37:13559
[email protected]c960d222012-06-15 10:03:50560 base::PlatformFileError* error =
561 new base::PlatformFileError(base::PLATFORM_FILE_OK);
562 FilePath* cache_file_path = new FilePath;
563 pool_->GetSequencedTaskRunner(sequence_token_)->PostTaskAndReply(
564 FROM_HERE,
565 base::Bind(&GDataCache::MarkDirty,
566 base::Unretained(this),
567 resource_id,
568 md5,
569 GDataCache::FILE_OPERATION_MOVE,
570 error,
571 cache_file_path),
572 base::Bind(&RunGetFileFromCacheCallback,
573 callback,
574 base::Owned(error),
575 resource_id,
576 md5,
577 base::Owned(cache_file_path)));
[email protected]73f9c742012-06-15 07:37:13578}
579
580void GDataCache::CommitDirtyOnUIThread(const std::string& resource_id,
581 const std::string& md5,
582 const CacheOperationCallback& callback) {
583 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
584
585 base::PlatformFileError* error =
586 new base::PlatformFileError(base::PLATFORM_FILE_OK);
587 pool_->GetSequencedTaskRunner(sequence_token_)->PostTaskAndReply(
588 FROM_HERE,
589 base::Bind(&GDataCache::CommitDirty,
590 base::Unretained(this),
591 resource_id,
592 md5,
593 GDataCache::FILE_OPERATION_MOVE,
594 error),
[email protected]d7664c22012-06-18 19:35:49595 base::Bind(&GDataCache::OnCommitDirty,
596 ui_weak_ptr_,
[email protected]73f9c742012-06-15 07:37:13597 base::Owned(error),
598 resource_id,
[email protected]d7664c22012-06-18 19:35:49599 md5,
600 callback));
[email protected]73f9c742012-06-15 07:37:13601}
602
603void GDataCache::ClearDirtyOnUIThread(const std::string& resource_id,
604 const std::string& md5,
605 const CacheOperationCallback& callback) {
606 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
607
608 base::PlatformFileError* error =
609 new base::PlatformFileError(base::PLATFORM_FILE_OK);
610 pool_->GetSequencedTaskRunner(sequence_token_)->PostTaskAndReply(
611 FROM_HERE,
612 base::Bind(&GDataCache::ClearDirty,
613 base::Unretained(this),
614 resource_id,
615 md5,
616 GDataCache::FILE_OPERATION_MOVE,
617 error),
618 base::Bind(&RunCacheOperationCallback,
619 callback,
620 base::Owned(error),
621 resource_id,
622 md5));
623}
624
625void GDataCache::RemoveOnUIThread(const std::string& resource_id,
626 const CacheOperationCallback& callback) {
627 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
628
629 base::PlatformFileError* error =
630 new base::PlatformFileError(base::PLATFORM_FILE_OK);
631
632 pool_->GetSequencedTaskRunner(sequence_token_)->PostTaskAndReply(
633 FROM_HERE,
634 base::Bind(&GDataCache::Remove,
635 base::Unretained(this),
636 resource_id,
637 error),
638 base::Bind(&RunCacheOperationCallback,
639 callback,
640 base::Owned(error),
641 resource_id,
642 "" /* md5 */));
643}
644
645void GDataCache::RequestInitializeOnUIThread() {
646 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
647
[email protected]1ff88ea52012-06-20 04:54:00648 pool_->GetSequencedTaskRunner(sequence_token_)->PostTask(
[email protected]73f9c742012-06-15 07:37:13649 FROM_HERE,
[email protected]1ff88ea52012-06-20 04:54:00650 base::Bind(&GDataCache::Initialize, base::Unretained(this)));
[email protected]73f9c742012-06-15 07:37:13651}
652
[email protected]b22f87f2012-07-12 10:53:17653bool GDataCache::GetCacheEntry(const std::string& resource_id,
654 const std::string& md5,
655 GDataCacheEntry* entry) {
656 DCHECK(entry);
[email protected]73f9c742012-06-15 07:37:13657 AssertOnSequencedWorkerPool();
[email protected]b22f87f2012-07-12 10:53:17658 return metadata_->GetCacheEntry(resource_id, md5, entry);
[email protected]73f9c742012-06-15 07:37:13659}
660
661// static
662GDataCache* GDataCache::CreateGDataCacheOnUIThread(
663 const FilePath& cache_root_path,
664 base::SequencedWorkerPool* pool,
665 const base::SequencedWorkerPool::SequenceToken& sequence_token) {
666 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]ca5f6da2012-06-18 12:54:59667 return new GDataCache(cache_root_path, pool, sequence_token);
[email protected]73f9c742012-06-15 07:37:13668}
669
670void GDataCache::DestroyOnUIThread() {
671 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
672
673 // Invalidate the weak pointer.
674 ui_weak_ptr_factory_.InvalidateWeakPtrs();
675
676 // Destroy myself on the blocking pool.
677 pool_->GetSequencedTaskRunner(sequence_token_)->PostTask(
678 FROM_HERE,
679 base::Bind(&GDataCache::Destroy,
680 base::Unretained(this)));
681}
682
[email protected]ca5f6da2012-06-18 12:54:59683void GDataCache::Initialize() {
684 AssertOnSequencedWorkerPool();
685
686 GDataCacheMetadataMap* cache_data =
687 new GDataCacheMetadataMap(pool_, sequence_token_);
688 cache_data->Initialize(cache_paths_);
689 metadata_.reset(cache_data);
690}
691
[email protected]73f9c742012-06-15 07:37:13692void GDataCache::Destroy() {
693 AssertOnSequencedWorkerPool();
694 delete this;
695}
696
[email protected]b83e5202012-06-27 07:50:24697void GDataCache::GetResourceIdsOfBacklog(
698 std::vector<std::string>* to_fetch,
699 std::vector<std::string>* to_upload) {
[email protected]8764a392012-06-20 06:43:08700 AssertOnSequencedWorkerPool();
[email protected]b83e5202012-06-27 07:50:24701 DCHECK(to_fetch);
702 DCHECK(to_upload);
[email protected]8764a392012-06-20 06:43:08703
[email protected]b83e5202012-06-27 07:50:24704 metadata_->Iterate(base::Bind(&CollectBacklog, to_fetch, to_upload));
[email protected]8764a392012-06-20 06:43:08705}
706
[email protected]85b62192012-06-29 19:56:38707void GDataCache::GetResourceIdsOfExistingPinnedFiles(
708 std::vector<std::string>* resource_ids) {
709 AssertOnSequencedWorkerPool();
710 DCHECK(resource_ids);
711
712 metadata_->Iterate(base::Bind(&CollectExistingPinnedFile, resource_ids));
713}
714
[email protected]c960d222012-06-15 10:03:50715void GDataCache::GetFile(const std::string& resource_id,
716 const std::string& md5,
717 base::PlatformFileError* error,
718 FilePath* cache_file_path) {
719 AssertOnSequencedWorkerPool();
720 DCHECK(error);
721 DCHECK(cache_file_path);
722
[email protected]b22f87f2012-07-12 10:53:17723 GDataCacheEntry cache_entry;
724 if (GetCacheEntry(resource_id, md5, &cache_entry) &&
[email protected]02821102012-07-12 20:19:17725 cache_entry.is_present()) {
[email protected]c960d222012-06-15 10:03:50726 CachedFileOrigin file_origin;
[email protected]02821102012-07-12 20:19:17727 if (cache_entry.is_mounted()) {
[email protected]c960d222012-06-15 10:03:50728 file_origin = CACHED_FILE_MOUNTED;
[email protected]02821102012-07-12 20:19:17729 } else if (cache_entry.is_dirty()) {
[email protected]c960d222012-06-15 10:03:50730 file_origin = CACHED_FILE_LOCALLY_MODIFIED;
731 } else {
732 file_origin = CACHED_FILE_FROM_SERVER;
733 }
734 *cache_file_path = GetCacheFilePath(
735 resource_id,
736 md5,
[email protected]b22f87f2012-07-12 10:53:17737 GetSubDirectoryType(cache_entry),
[email protected]c960d222012-06-15 10:03:50738 file_origin);
739 *error = base::PLATFORM_FILE_OK;
740 } else {
741 *error = base::PLATFORM_FILE_ERROR_NOT_FOUND;
742 }
743}
744
[email protected]a321b9632012-06-14 03:29:17745void GDataCache::Store(const std::string& resource_id,
746 const std::string& md5,
747 const FilePath& source_path,
748 FileOperationType file_operation_type,
749 base::PlatformFileError* error) {
750 AssertOnSequencedWorkerPool();
751 DCHECK(error);
752
753 FilePath dest_path;
754 FilePath symlink_path;
[email protected]a321b9632012-06-14 03:29:17755 CacheSubDirectoryType sub_dir_type = CACHE_TYPE_TMP;
756
[email protected]a321b9632012-06-14 03:29:17757 // If file was previously pinned, store it in persistent dir and create
758 // symlink in pinned dir.
[email protected]b22f87f2012-07-12 10:53:17759 GDataCacheEntry cache_entry;
760 if (GetCacheEntry(resource_id, md5, &cache_entry)) { // File exists in cache.
[email protected]a321b9632012-06-14 03:29:17761 // If file is dirty or mounted, return error.
[email protected]02821102012-07-12 20:19:17762 if (cache_entry.is_dirty() || cache_entry.is_mounted()) {
[email protected]a321b9632012-06-14 03:29:17763 LOG(WARNING) << "Can't store a file to replace a "
[email protected]02821102012-07-12 20:19:17764 << (cache_entry.is_dirty() ? "dirty" : "mounted")
[email protected]a321b9632012-06-14 03:29:17765 << " file: res_id=" << resource_id
766 << ", md5=" << md5;
767 *error = base::PLATFORM_FILE_ERROR_IN_USE;
768 return;
769 }
770
[email protected]a321b9632012-06-14 03:29:17771 // If file is pinned, determines destination path.
[email protected]02821102012-07-12 20:19:17772 if (cache_entry.is_pinned()) {
[email protected]a321b9632012-06-14 03:29:17773 sub_dir_type = CACHE_TYPE_PERSISTENT;
774 dest_path = GetCacheFilePath(resource_id, md5, sub_dir_type,
775 CACHED_FILE_FROM_SERVER);
776 symlink_path = GetCacheFilePath(
777 resource_id, std::string(), CACHE_TYPE_PINNED,
778 CACHED_FILE_FROM_SERVER);
779 }
780 }
781
782 // File wasn't pinned or doesn't exist in cache, store in tmp dir.
783 if (dest_path.empty()) {
784 DCHECK_EQ(CACHE_TYPE_TMP, sub_dir_type);
785 dest_path = GetCacheFilePath(resource_id, md5, sub_dir_type,
786 CACHED_FILE_FROM_SERVER);
787 }
788
789 *error = ModifyCacheState(
790 source_path,
791 dest_path,
792 file_operation_type,
793 symlink_path,
794 !symlink_path.empty()); // create symlink
795
796 // Determine search pattern for stale filenames corrresponding to resource_id,
797 // either "<resource_id>*" or "<resource_id>.*".
798 FilePath stale_filenames_pattern;
799 if (md5.empty()) {
800 // No md5 means no extension, append '*' after base name, i.e.
801 // "<resource_id>*".
802 // Cannot call |dest_path|.ReplaceExtension when there's no md5 extension:
803 // if base name of |dest_path| (i.e. escaped resource_id) contains the
804 // extension separator '.', ReplaceExtension will remove it and everything
805 // after it. The result will be nothing like the escaped resource_id.
[email protected]ca5f6da2012-06-18 12:54:59806 stale_filenames_pattern = FilePath(dest_path.value() + util::kWildCard);
[email protected]a321b9632012-06-14 03:29:17807 } else {
808 // Replace md5 extension with '*' i.e. "<resource_id>.*".
809 // Note that ReplaceExtension automatically prefixes the extension with the
810 // extension separator '.'.
[email protected]ca5f6da2012-06-18 12:54:59811 stale_filenames_pattern = dest_path.ReplaceExtension(util::kWildCard);
[email protected]a321b9632012-06-14 03:29:17812 }
813
814 // Delete files that match |stale_filenames_pattern| except for |dest_path|.
815 DeleteFilesSelectively(stale_filenames_pattern, dest_path);
816
817 if (*error == base::PLATFORM_FILE_OK) {
818 // Now that file operations have completed, update cache map.
[email protected]b22f87f2012-07-12 10:53:17819 cache_entry.set_md5(md5);
[email protected]02821102012-07-12 20:19:17820 cache_entry.set_is_present(true);
821 cache_entry.set_is_persistent(sub_dir_type == CACHE_TYPE_PERSISTENT);
[email protected]b22f87f2012-07-12 10:53:17822 metadata_->AddOrUpdateCacheEntry(resource_id, cache_entry);
[email protected]a321b9632012-06-14 03:29:17823 }
824}
825
826void GDataCache::Pin(const std::string& resource_id,
827 const std::string& md5,
828 FileOperationType file_operation_type,
829 base::PlatformFileError* error) {
830 AssertOnSequencedWorkerPool();
831 DCHECK(error);
832
833 FilePath source_path;
834 FilePath dest_path;
835 FilePath symlink_path;
836 bool create_symlink = true;
[email protected]a321b9632012-06-14 03:29:17837 CacheSubDirectoryType sub_dir_type = CACHE_TYPE_PERSISTENT;
838
[email protected]b22f87f2012-07-12 10:53:17839 GDataCacheEntry cache_entry;
840 if (!GetCacheEntry(resource_id, md5, &cache_entry)) {
841 // Entry does not exist in cache.
[email protected]a321b9632012-06-14 03:29:17842 // Set both |dest_path| and |source_path| to /dev/null, so that:
843 // 1) ModifyCacheState won't move files when |source_path| and |dest_path|
844 // are the same.
845 // 2) symlinks to /dev/null will be picked up by GDataSyncClient to download
846 // pinned files that don't exist in cache.
[email protected]30d9dda2012-06-30 05:56:28847 dest_path = FilePath::FromUTF8Unsafe(util::kSymLinkToDevNull);
[email protected]a321b9632012-06-14 03:29:17848 source_path = dest_path;
849
[email protected]2a6fe6e2012-07-10 06:01:04850 // Set sub_dir_type to TMP. The file will be first downloaded in 'tmp',
851 // then moved to 'persistent'.
852 sub_dir_type = CACHE_TYPE_TMP;
[email protected]109eb5c2012-07-12 03:20:05853 } else { // File exists in cache, determines destination path.
[email protected]a321b9632012-06-14 03:29:17854 // Determine source and destination paths.
855
856 // If file is dirty or mounted, don't move it, so determine |dest_path| and
857 // set |source_path| the same, because ModifyCacheState only moves files if
858 // source and destination are different.
[email protected]02821102012-07-12 20:19:17859 if (cache_entry.is_dirty() || cache_entry.is_mounted()) {
860 DCHECK(cache_entry.is_persistent());
[email protected]a321b9632012-06-14 03:29:17861 dest_path = GetCacheFilePath(resource_id,
862 md5,
[email protected]b22f87f2012-07-12 10:53:17863 GetSubDirectoryType(cache_entry),
[email protected]a321b9632012-06-14 03:29:17864 CACHED_FILE_LOCALLY_MODIFIED);
865 source_path = dest_path;
866 } else {
867 // Gets the current path of the file in cache.
868 source_path = GetCacheFilePath(resource_id,
869 md5,
[email protected]b22f87f2012-07-12 10:53:17870 GetSubDirectoryType(cache_entry),
[email protected]a321b9632012-06-14 03:29:17871 CACHED_FILE_FROM_SERVER);
872
873 // If file was pinned before but actual file blob doesn't exist in cache:
874 // - don't need to move the file, so set |dest_path| to |source_path|,
875 // because ModifyCacheState only moves files if source and destination
876 // are different
877 // - don't create symlink since it already exists.
[email protected]02821102012-07-12 20:19:17878 if (!cache_entry.is_present()) {
[email protected]a321b9632012-06-14 03:29:17879 dest_path = source_path;
880 create_symlink = false;
881 } else { // File exists, move it to persistent dir.
882 dest_path = GetCacheFilePath(resource_id,
883 md5,
884 CACHE_TYPE_PERSISTENT,
885 CACHED_FILE_FROM_SERVER);
886 }
887 }
888 }
889
890 // Create symlink in pinned dir.
891 if (create_symlink) {
892 symlink_path = GetCacheFilePath(resource_id,
893 std::string(),
894 CACHE_TYPE_PINNED,
895 CACHED_FILE_FROM_SERVER);
896 }
897
898 *error = ModifyCacheState(source_path,
899 dest_path,
900 file_operation_type,
901 symlink_path,
902 create_symlink);
903
904 if (*error == base::PLATFORM_FILE_OK) {
905 // Now that file operations have completed, update cache map.
[email protected]b22f87f2012-07-12 10:53:17906 cache_entry.set_md5(md5);
[email protected]02821102012-07-12 20:19:17907 cache_entry.set_is_pinned(true);
908 cache_entry.set_is_persistent(sub_dir_type == CACHE_TYPE_PERSISTENT);
[email protected]b22f87f2012-07-12 10:53:17909 metadata_->AddOrUpdateCacheEntry(resource_id, cache_entry);
[email protected]a321b9632012-06-14 03:29:17910 }
911}
912
913void GDataCache::Unpin(const std::string& resource_id,
914 const std::string& md5,
915 FileOperationType file_operation_type,
916 base::PlatformFileError* error) {
917 AssertOnSequencedWorkerPool();
918 DCHECK(error);
919
[email protected]a321b9632012-06-14 03:29:17920 // Unpinning a file means its entry must exist in cache.
[email protected]b22f87f2012-07-12 10:53:17921 GDataCacheEntry cache_entry;
922 if (!GetCacheEntry(resource_id, md5, &cache_entry)) {
[email protected]a321b9632012-06-14 03:29:17923 LOG(WARNING) << "Can't unpin a file that wasn't pinned or cached: res_id="
924 << resource_id
925 << ", md5=" << md5;
926 *error = base::PLATFORM_FILE_ERROR_NOT_FOUND;
927 return;
928 }
929
930 // Entry exists in cache, determines source and destination paths.
931
932 FilePath source_path;
933 FilePath dest_path;
934 CacheSubDirectoryType sub_dir_type = CACHE_TYPE_TMP;
935
936 // If file is dirty or mounted, don't move it, so determine |dest_path| and
937 // set |source_path| the same, because ModifyCacheState moves files if source
938 // and destination are different.
[email protected]02821102012-07-12 20:19:17939 if (cache_entry.is_dirty() || cache_entry.is_mounted()) {
[email protected]a321b9632012-06-14 03:29:17940 sub_dir_type = CACHE_TYPE_PERSISTENT;
[email protected]02821102012-07-12 20:19:17941 DCHECK(cache_entry.is_persistent());
[email protected]a321b9632012-06-14 03:29:17942 dest_path = GetCacheFilePath(resource_id,
943 md5,
[email protected]b22f87f2012-07-12 10:53:17944 GetSubDirectoryType(cache_entry),
[email protected]a321b9632012-06-14 03:29:17945 CACHED_FILE_LOCALLY_MODIFIED);
946 source_path = dest_path;
947 } else {
948 // Gets the current path of the file in cache.
949 source_path = GetCacheFilePath(resource_id,
950 md5,
[email protected]b22f87f2012-07-12 10:53:17951 GetSubDirectoryType(cache_entry),
[email protected]a321b9632012-06-14 03:29:17952 CACHED_FILE_FROM_SERVER);
953
954 // If file was pinned but actual file blob still doesn't exist in cache,
955 // don't need to move the file, so set |dest_path| to |source_path|, because
956 // ModifyCacheState only moves files if source and destination are
957 // different.
[email protected]02821102012-07-12 20:19:17958 if (!cache_entry.is_present()) {
[email protected]a321b9632012-06-14 03:29:17959 dest_path = source_path;
960 } else { // File exists, move it to tmp dir.
961 dest_path = GetCacheFilePath(resource_id, md5,
962 CACHE_TYPE_TMP,
963 CACHED_FILE_FROM_SERVER);
964 }
965 }
966
967 // If file was pinned, get absolute path of symlink in pinned dir so as to
968 // remove it.
969 FilePath symlink_path;
[email protected]02821102012-07-12 20:19:17970 if (cache_entry.is_pinned()) {
[email protected]a321b9632012-06-14 03:29:17971 symlink_path = GetCacheFilePath(resource_id,
972 std::string(),
973 CACHE_TYPE_PINNED,
974 CACHED_FILE_FROM_SERVER);
975 }
976
977 *error = ModifyCacheState(
978 source_path,
979 dest_path,
980 file_operation_type,
981 symlink_path, // This will be deleted if it exists.
982 false /* don't create symlink*/);
983
984 if (*error == base::PLATFORM_FILE_OK) {
985 // Now that file operations have completed, update cache map.
[email protected]02821102012-07-12 20:19:17986 if (cache_entry.is_present()) {
[email protected]48477fe2012-07-12 17:45:08987 cache_entry.set_md5(md5);
[email protected]02821102012-07-12 20:19:17988 cache_entry.set_is_pinned(false);
989 cache_entry.set_is_persistent(sub_dir_type == CACHE_TYPE_PERSISTENT);
[email protected]48477fe2012-07-12 17:45:08990 metadata_->AddOrUpdateCacheEntry(resource_id, cache_entry);
[email protected]3423871a2012-07-12 00:41:27991 } else {
992 // Remove the existing entry if we are unpinning a non-present file.
993 metadata_->RemoveCacheEntry(resource_id);
994 }
[email protected]a321b9632012-06-14 03:29:17995 }
996}
997
998void GDataCache::SetMountedState(const FilePath& file_path,
999 bool to_mount,
1000 base::PlatformFileError *error,
1001 FilePath* cache_file_path) {
1002 AssertOnSequencedWorkerPool();
1003 DCHECK(error);
1004 DCHECK(cache_file_path);
1005
1006 // Parse file path to obtain resource_id, md5 and extra_extension.
1007 std::string resource_id;
1008 std::string md5;
1009 std::string extra_extension;
1010 util::ParseCacheFilePath(file_path, &resource_id, &md5, &extra_extension);
1011 // The extra_extension shall be ".mounted" iff we're unmounting.
[email protected]ca5f6da2012-06-18 12:54:591012 DCHECK(!to_mount == (extra_extension == util::kMountedArchiveFileExtension));
[email protected]a321b9632012-06-14 03:29:171013
1014 // Get cache entry associated with the resource_id and md5
[email protected]b22f87f2012-07-12 10:53:171015 GDataCacheEntry cache_entry;
1016 if (!GetCacheEntry(resource_id, md5, &cache_entry)) {
[email protected]a321b9632012-06-14 03:29:171017 *error = base::PLATFORM_FILE_ERROR_NOT_FOUND;
1018 return;
1019 }
[email protected]02821102012-07-12 20:19:171020 if (to_mount == cache_entry.is_mounted()) {
[email protected]a321b9632012-06-14 03:29:171021 *error = base::PLATFORM_FILE_ERROR_INVALID_OPERATION;
1022 return;
1023 }
1024
1025 // Get the subdir type and path for the unmounted state.
1026 CacheSubDirectoryType unmounted_subdir =
[email protected]02821102012-07-12 20:19:171027 cache_entry.is_pinned() ? CACHE_TYPE_PERSISTENT : CACHE_TYPE_TMP;
[email protected]a321b9632012-06-14 03:29:171028 FilePath unmounted_path = GetCacheFilePath(
1029 resource_id, md5, unmounted_subdir, CACHED_FILE_FROM_SERVER);
1030
1031 // Get the subdir type and path for the mounted state.
1032 CacheSubDirectoryType mounted_subdir = CACHE_TYPE_PERSISTENT;
1033 FilePath mounted_path = GetCacheFilePath(
1034 resource_id, md5, mounted_subdir, CACHED_FILE_MOUNTED);
1035
1036 // Determine the source and destination paths for moving the cache blob.
1037 FilePath source_path;
1038 CacheSubDirectoryType dest_subdir;
[email protected]a321b9632012-06-14 03:29:171039 if (to_mount) {
1040 source_path = unmounted_path;
1041 *cache_file_path = mounted_path;
1042 dest_subdir = mounted_subdir;
[email protected]02821102012-07-12 20:19:171043 cache_entry.set_is_mounted(true);
[email protected]a321b9632012-06-14 03:29:171044 } else {
1045 source_path = mounted_path;
1046 *cache_file_path = unmounted_path;
1047 dest_subdir = unmounted_subdir;
[email protected]02821102012-07-12 20:19:171048 cache_entry.set_is_mounted(false);
[email protected]a321b9632012-06-14 03:29:171049 }
1050
1051 // Move cache blob from source path to destination path.
1052 *error = ModifyCacheState(source_path, *cache_file_path,
1053 FILE_OPERATION_MOVE, FilePath(), false);
1054 if (*error == base::PLATFORM_FILE_OK) {
1055 // Now that cache operation is complete, update cache map
[email protected]48477fe2012-07-12 17:45:081056 cache_entry.set_md5(md5);
[email protected]02821102012-07-12 20:19:171057 cache_entry.set_is_persistent(dest_subdir == CACHE_TYPE_PERSISTENT);
[email protected]48477fe2012-07-12 17:45:081058 metadata_->AddOrUpdateCacheEntry(resource_id, cache_entry);
[email protected]a321b9632012-06-14 03:29:171059 }
1060}
1061
[email protected]c960d222012-06-15 10:03:501062void GDataCache::MarkDirty(const std::string& resource_id,
1063 const std::string& md5,
1064 FileOperationType file_operation_type,
1065 base::PlatformFileError* error,
1066 FilePath* cache_file_path) {
1067 AssertOnSequencedWorkerPool();
1068 DCHECK(error);
1069 DCHECK(cache_file_path);
1070
1071 // If file has already been marked dirty in previous instance of chrome, we
1072 // would have lost the md5 info during cache initialization, because the file
1073 // would have been renamed to .local extension.
1074 // So, search for entry in cache without comparing md5.
[email protected]c960d222012-06-15 10:03:501075
1076 // Marking a file dirty means its entry and actual file blob must exist in
1077 // cache.
[email protected]b22f87f2012-07-12 10:53:171078 GDataCacheEntry cache_entry;
1079 if (!GetCacheEntry(resource_id, std::string(), &cache_entry) ||
[email protected]02821102012-07-12 20:19:171080 !cache_entry.is_present()) {
[email protected]c960d222012-06-15 10:03:501081 LOG(WARNING) << "Can't mark dirty a file that wasn't cached: res_id="
1082 << resource_id
1083 << ", md5=" << md5;
1084 *error = base::PLATFORM_FILE_ERROR_NOT_FOUND;
1085 return;
1086 }
1087
1088 // If a file is already dirty (i.e. MarkDirtyInCache was called before),
1089 // delete outgoing symlink if it exists.
1090 // TODO(benchan): We should only delete outgoing symlink if file is currently
1091 // not being uploaded. However, for now, cache doesn't know if uploading of a
1092 // file is in progress. Per zel, the upload process should be canceled before
1093 // MarkDirtyInCache is called again.
[email protected]02821102012-07-12 20:19:171094 if (cache_entry.is_dirty()) {
[email protected]c960d222012-06-15 10:03:501095 // The file must be in persistent dir.
[email protected]02821102012-07-12 20:19:171096 DCHECK(cache_entry.is_persistent());
[email protected]c960d222012-06-15 10:03:501097
1098 // Determine symlink path in outgoing dir, so as to remove it.
1099 FilePath symlink_path = GetCacheFilePath(
1100 resource_id,
1101 std::string(),
1102 CACHE_TYPE_OUTGOING,
1103 CACHED_FILE_FROM_SERVER);
1104
1105 // We're not moving files here, so simply use empty FilePath for both
1106 // |source_path| and |dest_path| because ModifyCacheState only move files
1107 // if source and destination are different.
1108 *error = ModifyCacheState(
1109 FilePath(), // non-applicable source path
1110 FilePath(), // non-applicable dest path
1111 file_operation_type,
1112 symlink_path,
1113 false /* don't create symlink */);
1114
1115 // Determine current path of dirty file.
1116 if (*error == base::PLATFORM_FILE_OK) {
1117 *cache_file_path = GetCacheFilePath(
1118 resource_id,
1119 md5,
1120 CACHE_TYPE_PERSISTENT,
1121 CACHED_FILE_LOCALLY_MODIFIED);
1122 }
1123 return;
1124 }
1125
1126 // Move file to persistent dir with new .local extension.
1127
1128 // Get the current path of the file in cache.
1129 FilePath source_path = GetCacheFilePath(
1130 resource_id,
1131 md5,
[email protected]b22f87f2012-07-12 10:53:171132 GetSubDirectoryType(cache_entry),
[email protected]c960d222012-06-15 10:03:501133 CACHED_FILE_FROM_SERVER);
1134
1135 // Determine destination path.
[email protected]3dc88ee2012-07-11 21:04:111136 const CacheSubDirectoryType sub_dir_type = CACHE_TYPE_PERSISTENT;
[email protected]c960d222012-06-15 10:03:501137 *cache_file_path = GetCacheFilePath(resource_id,
1138 md5,
1139 sub_dir_type,
1140 CACHED_FILE_LOCALLY_MODIFIED);
1141
1142 // If file is pinned, update symlink in pinned dir.
1143 FilePath symlink_path;
[email protected]02821102012-07-12 20:19:171144 if (cache_entry.is_pinned()) {
[email protected]c960d222012-06-15 10:03:501145 symlink_path = GetCacheFilePath(resource_id,
1146 std::string(),
1147 CACHE_TYPE_PINNED,
1148 CACHED_FILE_FROM_SERVER);
1149 }
1150
1151 *error = ModifyCacheState(
1152 source_path,
1153 *cache_file_path,
1154 file_operation_type,
1155 symlink_path,
1156 !symlink_path.empty() /* create symlink */);
1157
1158 if (*error == base::PLATFORM_FILE_OK) {
1159 // Now that file operations have completed, update cache map.
[email protected]48477fe2012-07-12 17:45:081160 cache_entry.set_md5(md5);
[email protected]02821102012-07-12 20:19:171161 cache_entry.set_is_dirty(true);
1162 cache_entry.set_is_persistent(sub_dir_type == CACHE_TYPE_PERSISTENT);
[email protected]48477fe2012-07-12 17:45:081163 metadata_->AddOrUpdateCacheEntry(resource_id, cache_entry);
[email protected]c960d222012-06-15 10:03:501164 }
1165}
1166
[email protected]a321b9632012-06-14 03:29:171167void GDataCache::CommitDirty(const std::string& resource_id,
1168 const std::string& md5,
1169 FileOperationType file_operation_type,
1170 base::PlatformFileError* error) {
1171 AssertOnSequencedWorkerPool();
1172 DCHECK(error);
1173
1174 // If file has already been marked dirty in previous instance of chrome, we
1175 // would have lost the md5 info during cache initialization, because the file
1176 // would have been renamed to .local extension.
1177 // So, search for entry in cache without comparing md5.
[email protected]a321b9632012-06-14 03:29:171178
1179 // Committing a file dirty means its entry and actual file blob must exist in
1180 // cache.
[email protected]b22f87f2012-07-12 10:53:171181 GDataCacheEntry cache_entry;
1182 if (!GetCacheEntry(resource_id, std::string(), &cache_entry) ||
[email protected]02821102012-07-12 20:19:171183 !cache_entry.is_present()) {
[email protected]a321b9632012-06-14 03:29:171184 LOG(WARNING) << "Can't commit dirty a file that wasn't cached: res_id="
1185 << resource_id
1186 << ", md5=" << md5;
1187 *error = base::PLATFORM_FILE_ERROR_NOT_FOUND;
1188 return;
1189 }
1190
1191 // If a file is not dirty (it should have been marked dirty via
1192 // MarkDirtyInCache), committing it dirty is an invalid operation.
[email protected]02821102012-07-12 20:19:171193 if (!cache_entry.is_dirty()) {
[email protected]a321b9632012-06-14 03:29:171194 LOG(WARNING) << "Can't commit a non-dirty file: res_id="
1195 << resource_id
1196 << ", md5=" << md5;
1197 *error = base::PLATFORM_FILE_ERROR_INVALID_OPERATION;
1198 return;
1199 }
1200
1201 // Dirty files must be in persistent dir.
[email protected]02821102012-07-12 20:19:171202 DCHECK(cache_entry.is_persistent());
[email protected]a321b9632012-06-14 03:29:171203
1204 // Create symlink in outgoing dir.
1205 FilePath symlink_path = GetCacheFilePath(resource_id,
1206 std::string(),
1207 CACHE_TYPE_OUTGOING,
1208 CACHED_FILE_FROM_SERVER);
1209
1210 // Get target path of symlink i.e. current path of the file in cache.
1211 FilePath target_path = GetCacheFilePath(resource_id,
1212 md5,
[email protected]b22f87f2012-07-12 10:53:171213 GetSubDirectoryType(cache_entry),
[email protected]a321b9632012-06-14 03:29:171214 CACHED_FILE_LOCALLY_MODIFIED);
1215
1216 // Since there's no need to move files, use |target_path| for both
1217 // |source_path| and |dest_path|, because ModifyCacheState only moves files
1218 // if source and destination are different.
1219 *error = ModifyCacheState(target_path, // source
1220 target_path, // destination
1221 file_operation_type,
1222 symlink_path,
1223 true /* create symlink */);
1224}
1225
1226void GDataCache::ClearDirty(const std::string& resource_id,
1227 const std::string& md5,
1228 FileOperationType file_operation_type,
1229 base::PlatformFileError* error) {
1230 AssertOnSequencedWorkerPool();
1231 DCHECK(error);
1232
1233 // |md5| is the new .<md5> extension to rename the file to.
1234 // So, search for entry in cache without comparing md5.
[email protected]b22f87f2012-07-12 10:53:171235 GDataCacheEntry cache_entry;
[email protected]a321b9632012-06-14 03:29:171236
1237 // Clearing a dirty file means its entry and actual file blob must exist in
1238 // cache.
[email protected]b22f87f2012-07-12 10:53:171239 if (!GetCacheEntry(resource_id, std::string(), &cache_entry) ||
[email protected]02821102012-07-12 20:19:171240 !cache_entry.is_present()) {
[email protected]a321b9632012-06-14 03:29:171241 LOG(WARNING) << "Can't clear dirty state of a file that wasn't cached: "
1242 << "res_id=" << resource_id
1243 << ", md5=" << md5;
1244 *error = base::PLATFORM_FILE_ERROR_NOT_FOUND;
1245 return;
1246 }
1247
1248 // If a file is not dirty (it should have been marked dirty via
1249 // MarkDirtyInCache), clearing its dirty state is an invalid operation.
[email protected]02821102012-07-12 20:19:171250 if (!cache_entry.is_dirty()) {
[email protected]a321b9632012-06-14 03:29:171251 LOG(WARNING) << "Can't clear dirty state of a non-dirty file: res_id="
1252 << resource_id
1253 << ", md5=" << md5;
1254 *error = base::PLATFORM_FILE_ERROR_INVALID_OPERATION;
1255 return;
1256 }
1257
1258 // File must be dirty and hence in persistent dir.
[email protected]02821102012-07-12 20:19:171259 DCHECK(cache_entry.is_persistent());
[email protected]a321b9632012-06-14 03:29:171260
1261 // Get the current path of the file in cache.
1262 FilePath source_path = GetCacheFilePath(resource_id,
1263 md5,
[email protected]b22f87f2012-07-12 10:53:171264 GetSubDirectoryType(cache_entry),
[email protected]a321b9632012-06-14 03:29:171265 CACHED_FILE_LOCALLY_MODIFIED);
1266
1267 // Determine destination path.
1268 // If file is pinned, move it to persistent dir with .md5 extension;
1269 // otherwise, move it to tmp dir with .md5 extension.
[email protected]3dc88ee2012-07-11 21:04:111270 const CacheSubDirectoryType sub_dir_type =
[email protected]02821102012-07-12 20:19:171271 cache_entry.is_pinned() ? CACHE_TYPE_PERSISTENT : CACHE_TYPE_TMP;
[email protected]a321b9632012-06-14 03:29:171272 FilePath dest_path = GetCacheFilePath(resource_id,
1273 md5,
1274 sub_dir_type,
1275 CACHED_FILE_FROM_SERVER);
1276
1277 // Delete symlink in outgoing dir.
1278 FilePath symlink_path = GetCacheFilePath(resource_id,
1279 std::string(),
1280 CACHE_TYPE_OUTGOING,
1281 CACHED_FILE_FROM_SERVER);
1282
1283 *error = ModifyCacheState(source_path,
1284 dest_path,
1285 file_operation_type,
1286 symlink_path,
1287 false /* don't create symlink */);
1288
1289 // If file is pinned, update symlink in pinned dir.
[email protected]02821102012-07-12 20:19:171290 if (*error == base::PLATFORM_FILE_OK && cache_entry.is_pinned()) {
[email protected]a321b9632012-06-14 03:29:171291 symlink_path = GetCacheFilePath(resource_id,
1292 std::string(),
1293 CACHE_TYPE_PINNED,
1294 CACHED_FILE_FROM_SERVER);
1295
1296 // Since there's no moving of files here, use |dest_path| for both
1297 // |source_path| and |dest_path|, because ModifyCacheState only moves files
1298 // if source and destination are different.
1299 *error = ModifyCacheState(dest_path, // source path
1300 dest_path, // destination path
1301 file_operation_type,
1302 symlink_path,
1303 true /* create symlink */);
1304 }
1305
1306 if (*error == base::PLATFORM_FILE_OK) {
1307 // Now that file operations have completed, update cache map.
[email protected]48477fe2012-07-12 17:45:081308 cache_entry.set_md5(md5);
[email protected]02821102012-07-12 20:19:171309 cache_entry.set_is_dirty(false);
1310 cache_entry.set_is_persistent(sub_dir_type == CACHE_TYPE_PERSISTENT);
[email protected]48477fe2012-07-12 17:45:081311 metadata_->AddOrUpdateCacheEntry(resource_id, cache_entry);
[email protected]a321b9632012-06-14 03:29:171312 }
1313}
1314
1315void GDataCache::Remove(const std::string& resource_id,
1316 base::PlatformFileError* error) {
1317 AssertOnSequencedWorkerPool();
1318 DCHECK(error);
1319
[email protected]3423871a2012-07-12 00:41:271320 // MD5 is not passed into RemoveCacheEntry because we would delete all
1321 // cache files corresponding to <resource_id> regardless of the md5.
[email protected]a321b9632012-06-14 03:29:171322 // So, search for entry in cache without taking md5 into account.
[email protected]b22f87f2012-07-12 10:53:171323 GDataCacheEntry cache_entry;
[email protected]a321b9632012-06-14 03:29:171324
1325 // If entry doesn't exist or is dirty or mounted in cache, nothing to do.
[email protected]b22f87f2012-07-12 10:53:171326 const bool entry_found =
1327 GetCacheEntry(resource_id, std::string(), &cache_entry);
[email protected]02821102012-07-12 20:19:171328 if (!entry_found || cache_entry.is_dirty() || cache_entry.is_mounted()) {
[email protected]a321b9632012-06-14 03:29:171329 DVLOG(1) << "Entry is "
[email protected]b22f87f2012-07-12 10:53:171330 << (entry_found ?
[email protected]02821102012-07-12 20:19:171331 (cache_entry.is_dirty() ? "dirty" : "mounted") :
[email protected]a321b9632012-06-14 03:29:171332 "non-existent")
1333 << " in cache, not removing";
1334 *error = base::PLATFORM_FILE_OK;
1335 return;
1336 }
1337
1338 // Determine paths to delete all cache versions of |resource_id| in
1339 // persistent, tmp and pinned directories.
1340 std::vector<FilePath> paths_to_delete;
1341
1342 // For files in persistent and tmp dirs, delete files that match
1343 // "<resource_id>.*".
1344 paths_to_delete.push_back(GetCacheFilePath(resource_id,
[email protected]ca5f6da2012-06-18 12:54:591345 util::kWildCard,
[email protected]a321b9632012-06-14 03:29:171346 CACHE_TYPE_PERSISTENT,
1347 CACHED_FILE_FROM_SERVER));
1348 paths_to_delete.push_back(GetCacheFilePath(resource_id,
[email protected]ca5f6da2012-06-18 12:54:591349 util::kWildCard,
[email protected]a321b9632012-06-14 03:29:171350 CACHE_TYPE_TMP,
1351 CACHED_FILE_FROM_SERVER));
1352
1353 // For pinned files, filename is "<resource_id>" with no extension, so delete
1354 // "<resource_id>".
1355 paths_to_delete.push_back(GetCacheFilePath(resource_id,
1356 std::string(),
1357 CACHE_TYPE_PINNED,
1358 CACHED_FILE_FROM_SERVER));
1359
1360 // Don't delete locally modified (i.e. dirty and possibly outgoing) files.
1361 // Since we're not deleting outgoing symlinks, we don't need to append
1362 // outgoing path to |paths_to_delete|.
1363 FilePath path_to_keep = GetCacheFilePath(resource_id,
1364 std::string(),
1365 CACHE_TYPE_PERSISTENT,
1366 CACHED_FILE_LOCALLY_MODIFIED);
1367
1368 for (size_t i = 0; i < paths_to_delete.size(); ++i) {
1369 DeleteFilesSelectively(paths_to_delete[i], path_to_keep);
1370 }
1371
1372 // Now that all file operations have completed, remove from cache map.
[email protected]3423871a2012-07-12 00:41:271373 metadata_->RemoveCacheEntry(resource_id);
[email protected]a321b9632012-06-14 03:29:171374
1375 *error = base::PLATFORM_FILE_OK;
1376}
1377
[email protected]73f9c742012-06-15 07:37:131378void GDataCache::OnPinned(base::PlatformFileError* error,
1379 const std::string& resource_id,
1380 const std::string& md5,
1381 const CacheOperationCallback& callback) {
1382 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1383 DCHECK(error);
1384
1385 if (!callback.is_null())
1386 callback.Run(*error, resource_id, md5);
1387
1388 if (*error == base::PLATFORM_FILE_OK)
1389 FOR_EACH_OBSERVER(Observer, observers_, OnCachePinned(resource_id, md5));
[email protected]3653146a2012-05-29 13:41:471390}
1391
[email protected]73f9c742012-06-15 07:37:131392void GDataCache::OnUnpinned(base::PlatformFileError* error,
1393 const std::string& resource_id,
1394 const std::string& md5,
1395 const CacheOperationCallback& callback) {
1396 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1397 DCHECK(error);
[email protected]a321b9632012-06-14 03:29:171398
[email protected]73f9c742012-06-15 07:37:131399 if (!callback.is_null())
1400 callback.Run(*error, resource_id, md5);
[email protected]a321b9632012-06-14 03:29:171401
[email protected]73f9c742012-06-15 07:37:131402 if (*error == base::PLATFORM_FILE_OK)
1403 FOR_EACH_OBSERVER(Observer, observers_, OnCacheUnpinned(resource_id, md5));
[email protected]a321b9632012-06-14 03:29:171404
[email protected]73f9c742012-06-15 07:37:131405 // Now the file is moved from "persistent" to "tmp" directory.
1406 // It's a chance to free up space if needed.
1407 bool* has_enough_space = new bool(false);
1408 pool_->GetSequencedTaskRunner(sequence_token_)->PostTask(
1409 FROM_HERE,
1410 base::Bind(&GDataCache::FreeDiskSpaceIfNeededFor,
1411 base::Unretained(this),
1412 0,
1413 base::Owned(has_enough_space)));
[email protected]3653146a2012-05-29 13:41:471414}
1415
[email protected]d7664c22012-06-18 19:35:491416void GDataCache::OnCommitDirty(base::PlatformFileError* error,
1417 const std::string& resource_id,
1418 const std::string& md5,
1419 const CacheOperationCallback& callback) {
1420 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1421 DCHECK(error);
1422
1423 if (!callback.is_null())
1424 callback.Run(*error, resource_id, md5);
1425
1426 if (*error == base::PLATFORM_FILE_OK)
1427 FOR_EACH_OBSERVER(Observer, observers_, OnCacheCommitted(resource_id));
1428}
1429
[email protected]4324fdc2012-06-29 05:32:481430void GDataCache::GetCacheEntryHelper(const std::string& resource_id,
1431 const std::string& md5,
1432 bool* success,
[email protected]fae353a2012-07-11 23:30:271433 GDataCacheEntry* cache_entry) {
[email protected]4324fdc2012-06-29 05:32:481434 AssertOnSequencedWorkerPool();
1435 DCHECK(success);
1436 DCHECK(cache_entry);
1437
[email protected]b22f87f2012-07-12 10:53:171438 *success = GetCacheEntry(resource_id, md5, cache_entry);
[email protected]4324fdc2012-06-29 05:32:481439}
1440
[email protected]01ba15f72012-06-09 00:41:051441// static
1442FilePath GDataCache::GetCacheRootPath(Profile* profile) {
1443 FilePath cache_base_path;
1444 chrome::GetUserCacheDirectory(profile->GetPath(), &cache_base_path);
1445 FilePath cache_root_path =
1446 cache_base_path.Append(chrome::kGDataCacheDirname);
1447 return cache_root_path.Append(kGDataCacheVersionDir);
1448}
1449
[email protected]30d9dda2012-06-30 05:56:281450// static
1451std::vector<FilePath> GDataCache::GetCachePaths(
1452 const FilePath& cache_root_path) {
1453 std::vector<FilePath> cache_paths;
1454 // The order should match GDataCache::CacheSubDirectoryType enum.
1455 cache_paths.push_back(cache_root_path.Append(kGDataCacheMetaDir));
1456 cache_paths.push_back(cache_root_path.Append(kGDataCachePinnedDir));
1457 cache_paths.push_back(cache_root_path.Append(kGDataCacheOutgoingDir));
1458 cache_paths.push_back(cache_root_path.Append(kGDataCachePersistentDir));
1459 cache_paths.push_back(cache_root_path.Append(kGDataCacheTmpDir));
1460 cache_paths.push_back(cache_root_path.Append(kGDataCacheTmpDownloadsDir));
1461 cache_paths.push_back(cache_root_path.Append(kGDataCacheTmpDocumentsDir));
1462 return cache_paths;
1463}
1464
1465// static
1466bool GDataCache::CreateCacheDirectories(
1467 const std::vector<FilePath>& paths_to_create) {
1468 bool success = true;
1469
1470 for (size_t i = 0; i < paths_to_create.size(); ++i) {
1471 if (file_util::DirectoryExists(paths_to_create[i]))
1472 continue;
1473
1474 if (!file_util::CreateDirectory(paths_to_create[i])) {
1475 // Error creating this directory, record error and proceed with next one.
1476 success = false;
1477 PLOG(ERROR) << "Error creating directory " << paths_to_create[i].value();
1478 } else {
1479 DVLOG(1) << "Created directory " << paths_to_create[i].value();
1480 }
1481 }
1482 return success;
1483}
1484
[email protected]fae353a2012-07-11 23:30:271485// static
1486GDataCache::CacheSubDirectoryType GDataCache::GetSubDirectoryType(
1487 const GDataCacheEntry& cache_entry) {
[email protected]02821102012-07-12 20:19:171488 return cache_entry.is_persistent() ? CACHE_TYPE_PERSISTENT : CACHE_TYPE_TMP;
[email protected]fae353a2012-07-11 23:30:271489}
1490
[email protected]a321b9632012-06-14 03:29:171491void SetFreeDiskSpaceGetterForTesting(FreeDiskSpaceGetterInterface* getter) {
1492 delete global_free_disk_getter_for_testing; // Safe to delete NULL;
1493 global_free_disk_getter_for_testing = getter;
1494}
1495
[email protected]3653146a2012-05-29 13:41:471496} // namespace gdata