blob: 9f247ef36f5d3da753f12ec8d36ad7783a82b1d8 [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]ca5f6da2012-06-18 12:54:5915#include "chrome/browser/chromeos/gdata/gdata_cache_metadata.h"
[email protected]32a7fc852012-06-08 17:25:5016#include "chrome/browser/chromeos/gdata/gdata_util.h"
[email protected]01ba15f72012-06-09 00:41:0517#include "chrome/browser/profiles/profile.h"
18#include "chrome/common/chrome_constants.h"
19#include "chrome/common/chrome_paths_internal.h"
[email protected]7986b8d2012-06-14 15:05:1420#include "content/public/browser/browser_thread.h"
21
22using content::BrowserThread;
[email protected]3653146a2012-05-29 13:41:4723
24namespace gdata {
25namespace {
26
[email protected]01ba15f72012-06-09 00:41:0527const FilePath::CharType kGDataCacheVersionDir[] = FILE_PATH_LITERAL("v1");
[email protected]32a7fc852012-06-08 17:25:5028const FilePath::CharType kGDataCacheMetaDir[] = FILE_PATH_LITERAL("meta");
29const FilePath::CharType kGDataCachePinnedDir[] = FILE_PATH_LITERAL("pinned");
30const FilePath::CharType kGDataCacheOutgoingDir[] =
31 FILE_PATH_LITERAL("outgoing");
32const FilePath::CharType kGDataCachePersistentDir[] =
33 FILE_PATH_LITERAL("persistent");
34const FilePath::CharType kGDataCacheTmpDir[] = FILE_PATH_LITERAL("tmp");
35const FilePath::CharType kGDataCacheTmpDownloadsDir[] =
36 FILE_PATH_LITERAL("tmp/downloads");
37const FilePath::CharType kGDataCacheTmpDocumentsDir[] =
38 FILE_PATH_LITERAL("tmp/documents");
39
[email protected]a321b9632012-06-14 03:29:1740// Returns the home directory path, or an empty string if the home directory
41// is not found.
42// Copied from webkit/chromeos/cros_mount_point_provider.h.
43// TODO(satorux): Share the code.
44std::string GetHomeDirectory() {
45 if (base::chromeos::IsRunningOnChromeOS())
46 return "/home/chronos/user";
[email protected]3653146a2012-05-29 13:41:4747
[email protected]a321b9632012-06-14 03:29:1748 const char* home = getenv("HOME");
49 if (home)
50 return home;
51 return "";
52}
53
54// Used to tweak GetAmountOfFreeDiskSpace() behavior for testing.
55FreeDiskSpaceGetterInterface* global_free_disk_getter_for_testing = NULL;
56
57// Gets the amount of free disk space. Use
58// |global_free_disk_getter_for_testing| if set.
59int64 GetAmountOfFreeDiskSpace() {
60 if (global_free_disk_getter_for_testing)
61 return global_free_disk_getter_for_testing->AmountOfFreeDiskSpace();
62
63 return base::SysInfo::AmountOfFreeDiskSpace(
64 FilePath::FromUTF8Unsafe(GetHomeDirectory()));
65}
66
67// Returns true if we have sufficient space to store the given number of
68// bytes, while keeping kMinFreeSpace bytes on the disk.
69bool HasEnoughSpaceFor(int64 num_bytes) {
70 int64 free_space = GetAmountOfFreeDiskSpace();
71 // Substract this as if this portion does not exist.
72 free_space -= kMinFreeSpace;
73 return (free_space >= num_bytes);
74}
75
76// Remove all files under the given directory, non-recursively.
77// Do not remove recursively as we don't want to touch <gache>/tmp/downloads,
78// which is used for user initiated downloads like "Save As"
79void RemoveAllFiles(const FilePath& directory) {
80 using file_util::FileEnumerator;
81
82 FileEnumerator enumerator(directory, false /* recursive */,
83 FileEnumerator::FILES);
84 for (FilePath file_path = enumerator.Next(); !file_path.empty();
85 file_path = enumerator.Next()) {
86 DVLOG(1) << "Removing " << file_path.value();
87 if (!file_util::Delete(file_path, false /* recursive */))
88 LOG(WARNING) << "Failed to delete " << file_path.value();
89 }
90}
91
92// Converts system error to file platform error code.
93// This is copied and modified from base/platform_file_posix.cc.
94// TODO(satorux): Remove this copy-pasted function. crbug.com/132656
95base::PlatformFileError SystemToPlatformError(int error) {
96 switch (error) {
97 case 0:
98 return base::PLATFORM_FILE_OK;
99 case EACCES:
100 case EISDIR:
101 case EROFS:
102 case EPERM:
103 return base::PLATFORM_FILE_ERROR_ACCESS_DENIED;
104 case ETXTBSY:
105 return base::PLATFORM_FILE_ERROR_IN_USE;
106 case EEXIST:
107 return base::PLATFORM_FILE_ERROR_EXISTS;
108 case ENOENT:
109 return base::PLATFORM_FILE_ERROR_NOT_FOUND;
110 case EMFILE:
111 return base::PLATFORM_FILE_ERROR_TOO_MANY_OPENED;
112 case ENOMEM:
113 return base::PLATFORM_FILE_ERROR_NO_MEMORY;
114 case ENOSPC:
115 return base::PLATFORM_FILE_ERROR_NO_SPACE;
116 case ENOTDIR:
117 return base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY;
118 case EINTR:
119 return base::PLATFORM_FILE_ERROR_ABORT;
120 default:
121 return base::PLATFORM_FILE_ERROR_FAILED;
122 }
123}
124
[email protected]a321b9632012-06-14 03:29:17125// Modifies cache state of file on blocking pool, which involves:
126// - moving or copying file (per |file_operation_type|) from |source_path| to
127// |dest_path| if they're different
128// - deleting symlink if |symlink_path| is not empty
129// - creating symlink if |symlink_path| is not empty and |create_symlink| is
130// true.
131base::PlatformFileError ModifyCacheState(
132 const FilePath& source_path,
133 const FilePath& dest_path,
134 GDataCache::FileOperationType file_operation_type,
135 const FilePath& symlink_path,
136 bool create_symlink) {
137 // Move or copy |source_path| to |dest_path| if they are different.
138 if (source_path != dest_path) {
139 bool success = false;
140 if (file_operation_type == GDataCache::FILE_OPERATION_MOVE)
141 success = file_util::Move(source_path, dest_path);
142 else if (file_operation_type == GDataCache::FILE_OPERATION_COPY)
143 success = file_util::CopyFile(source_path, dest_path);
144 if (!success) {
145 base::PlatformFileError error = SystemToPlatformError(errno);
146 PLOG(ERROR) << "Error "
147 << (file_operation_type == GDataCache::FILE_OPERATION_MOVE ?
148 "moving " : "copying ")
149 << source_path.value()
150 << " to " << dest_path.value();
151 return error;
152 } else {
153 DVLOG(1) << (file_operation_type == GDataCache::FILE_OPERATION_MOVE ?
154 "Moved " : "Copied ")
155 << source_path.value()
156 << " to " << dest_path.value();
157 }
158 } else {
159 DVLOG(1) << "No need to move file: source = destination";
160 }
161
162 if (symlink_path.empty())
163 return base::PLATFORM_FILE_OK;
164
165 // Remove symlink regardless of |create_symlink| because creating a link will
166 // not overwrite an existing one.
[email protected]a321b9632012-06-14 03:29:17167 // We try to save one file operation by not checking if link exists before
168 // deleting it, so unlink may return error if link doesn't exist, but it
169 // doesn't really matter to us.
[email protected]0b8d4cee2012-07-02 20:46:26170 bool deleted = file_util::Delete(symlink_path, false);
[email protected]a321b9632012-06-14 03:29:17171 if (deleted) {
172 DVLOG(1) << "Deleted symlink " << symlink_path.value();
173 } else {
174 // Since we didn't check if symlink exists before deleting it, don't log
175 // if symlink doesn't exist.
176 if (errno != ENOENT)
177 PLOG(WARNING) << "Error deleting symlink " << symlink_path.value();
178 }
179
180 if (!create_symlink)
181 return base::PLATFORM_FILE_OK;
182
183 // Create new symlink to |dest_path|.
184 if (!file_util::CreateSymbolicLink(dest_path, symlink_path)) {
185 base::PlatformFileError error = SystemToPlatformError(errno);
186 PLOG(ERROR) << "Error creating symlink " << symlink_path.value()
187 << " for " << dest_path.value();
188 return error;
189 } else {
190 DVLOG(1) << "Created symlink " << symlink_path.value()
191 << " to " << dest_path.value();
192 }
193
194 return base::PLATFORM_FILE_OK;
195}
196
197// Deletes all files that match |path_to_delete_pattern| except for
198// |path_to_keep| on blocking pool.
199// If |path_to_keep| is empty, all files in |path_to_delete_pattern| are
200// deleted.
201void DeleteFilesSelectively(const FilePath& path_to_delete_pattern,
202 const FilePath& path_to_keep) {
203 // Enumerate all files in directory of |path_to_delete_pattern| that match
204 // base name of |path_to_delete_pattern|.
205 // If a file is not |path_to_keep|, delete it.
206 bool success = true;
207 file_util::FileEnumerator enumerator(
208 path_to_delete_pattern.DirName(),
209 false, // not recursive
210 static_cast<file_util::FileEnumerator::FileType>(
211 file_util::FileEnumerator::FILES |
212 file_util::FileEnumerator::SHOW_SYM_LINKS),
213 path_to_delete_pattern.BaseName().value());
214 for (FilePath current = enumerator.Next(); !current.empty();
215 current = enumerator.Next()) {
216 // If |path_to_keep| is not empty and same as current, don't delete it.
217 if (!path_to_keep.empty() && current == path_to_keep)
218 continue;
219
[email protected]0b8d4cee2012-07-02 20:46:26220 success = file_util::Delete(current, false);
[email protected]a321b9632012-06-14 03:29:17221 if (!success)
222 DVLOG(1) << "Error deleting " << current.value();
223 else
224 DVLOG(1) << "Deleted " << current.value();
225 }
226}
227
[email protected]b83e5202012-06-27 07:50:24228// Appends |resource_id| ID to |to_fetch| if the file is pinned but not
229// fetched (not present locally), or to |to_upload| if the file is dirty
230// but not uploaded.
231void CollectBacklog(std::vector<std::string>* to_fetch,
232 std::vector<std::string>* to_upload,
233 const std::string& resource_id,
[email protected]fae353a2012-07-11 23:30:27234 const GDataCacheEntry& cache_entry) {
[email protected]b83e5202012-06-27 07:50:24235 DCHECK(to_fetch);
236 DCHECK(to_upload);
[email protected]8764a392012-06-20 06:43:08237
238 if (cache_entry.IsPinned() && !cache_entry.IsPresent())
[email protected]b83e5202012-06-27 07:50:24239 to_fetch->push_back(resource_id);
240
241 if (cache_entry.IsDirty())
242 to_upload->push_back(resource_id);
[email protected]8764a392012-06-20 06:43:08243}
244
[email protected]85b62192012-06-29 19:56:38245// Appends |resource_id| ID to |resource_ids| if the file is pinned and
246// present (cached locally).
247void CollectExistingPinnedFile(std::vector<std::string>* resource_ids,
248 const std::string& resource_id,
[email protected]fae353a2012-07-11 23:30:27249 const GDataCacheEntry& cache_entry) {
[email protected]85b62192012-06-29 19:56:38250 DCHECK(resource_ids);
251
252 if (cache_entry.IsPinned() && cache_entry.IsPresent())
253 resource_ids->push_back(resource_id);
254}
[email protected]8764a392012-06-20 06:43:08255
[email protected]7986b8d2012-06-14 15:05:14256// Runs callback with pointers dereferenced.
257// Used to implement SetMountedStateOnUIThread.
[email protected]73f9c742012-06-15 07:37:13258void RunSetMountedStateCallback(const SetMountedStateCallback& callback,
259 base::PlatformFileError* error,
260 FilePath* cache_file_path) {
[email protected]7986b8d2012-06-14 15:05:14261 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
262 DCHECK(error);
263 DCHECK(cache_file_path);
264
265 if (!callback.is_null())
266 callback.Run(*error, *cache_file_path);
267}
268
[email protected]73f9c742012-06-15 07:37:13269// Runs callback with pointers dereferenced.
270// Used to implement *OnUIThread methods.
271void RunCacheOperationCallback(const CacheOperationCallback& callback,
272 base::PlatformFileError* error,
273 const std::string& resource_id,
274 const std::string& md5) {
275 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
276 DCHECK(error);
277
278 if (!callback.is_null())
279 callback.Run(*error, resource_id, md5);
280}
281
[email protected]c960d222012-06-15 10:03:50282// Runs callback with pointers dereferenced.
283// Used to implement *OnUIThread methods.
284void RunGetFileFromCacheCallback(const GetFileFromCacheCallback& callback,
285 base::PlatformFileError* error,
286 const std::string& resource_id,
287 const std::string& md5,
288 FilePath* cache_file_path) {
289 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
290 DCHECK(error);
291 DCHECK(cache_file_path);
292
293 if (!callback.is_null())
294 callback.Run(*error, resource_id, md5, *cache_file_path);
295}
296
[email protected]8764a392012-06-20 06:43:08297// Runs callback with pointers dereferenced.
[email protected]4324fdc2012-06-29 05:32:48298// Used to implement GetResourceIdsOfBacklogOnUIThread().
[email protected]85b62192012-06-29 19:56:38299void RunGetResourceIdsOfBacklogCallback(
300 const GetResourceIdsOfBacklogCallback& callback,
301 std::vector<std::string>* to_fetch,
302 std::vector<std::string>* to_upload) {
[email protected]8764a392012-06-20 06:43:08303 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]b83e5202012-06-27 07:50:24304 DCHECK(to_fetch);
305 DCHECK(to_upload);
[email protected]8764a392012-06-20 06:43:08306
307 if (!callback.is_null())
[email protected]b83e5202012-06-27 07:50:24308 callback.Run(*to_fetch, *to_upload);
[email protected]8764a392012-06-20 06:43:08309}
310
[email protected]4324fdc2012-06-29 05:32:48311// Runs callback with pointers dereferenced.
[email protected]85b62192012-06-29 19:56:38312// Used to implement GetResourceIdsOfExistingPinnedFilesOnUIThread().
313void RunGetResourceIdsCallback(
314 const GetResourceIdsCallback& callback,
315 std::vector<std::string>* resource_ids) {
316 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
317 DCHECK(resource_ids);
318
319 if (!callback.is_null())
320 callback.Run(*resource_ids);
321}
322
323// Runs callback with pointers dereferenced.
[email protected]4324fdc2012-06-29 05:32:48324// Used to implement GetCacheEntryOnUIThread().
325void RunGetCacheEntryCallback(
[email protected]fae353a2012-07-11 23:30:27326 const GetCacheEntryCallback& callback,
[email protected]4324fdc2012-06-29 05:32:48327 bool* success,
[email protected]fae353a2012-07-11 23:30:27328 GDataCacheEntry* cache_entry) {
[email protected]4324fdc2012-06-29 05:32:48329 DCHECK(success);
330 DCHECK(cache_entry);
331
332 if (!callback.is_null())
333 callback.Run(*success, *cache_entry);
334}
335
[email protected]a321b9632012-06-14 03:29:17336} // namespace
[email protected]32a7fc852012-06-08 17:25:50337
[email protected]fae353a2012-07-11 23:30:27338std::string GDataCacheEntry::ToString() const {
[email protected]3653146a2012-05-29 13:41:47339 std::vector<std::string> cache_states;
[email protected]bbb101b92012-07-11 05:31:59340 if (IsPresent())
[email protected]3653146a2012-05-29 13:41:47341 cache_states.push_back("present");
[email protected]bbb101b92012-07-11 05:31:59342 if (IsPinned())
[email protected]3653146a2012-05-29 13:41:47343 cache_states.push_back("pinned");
[email protected]bbb101b92012-07-11 05:31:59344 if (IsDirty())
[email protected]3653146a2012-05-29 13:41:47345 cache_states.push_back("dirty");
[email protected]bbb101b92012-07-11 05:31:59346 if (IsPersistent())
[email protected]a6689d32012-07-10 20:42:31347 cache_states.push_back("persistent");
[email protected]3653146a2012-05-29 13:41:47348
[email protected]a6689d32012-07-10 20:42:31349 return base::StringPrintf("md5=%s, cache_state=%s",
[email protected]e9adce9e2012-07-11 22:23:08350 md5_.c_str(),
[email protected]3653146a2012-05-29 13:41:47351 JoinString(cache_states, ',').c_str());
352}
353
[email protected]fcc92a52012-06-08 22:54:16354GDataCache::GDataCache(
355 const FilePath& cache_root_path,
356 base::SequencedWorkerPool* pool,
357 const base::SequencedWorkerPool::SequenceToken& sequence_token)
[email protected]01ba15f72012-06-09 00:41:05358 : cache_root_path_(cache_root_path),
[email protected]6b70c7b2012-06-14 03:10:43359 cache_paths_(GetCachePaths(cache_root_path_)),
[email protected]01ba15f72012-06-09 00:41:05360 pool_(pool),
[email protected]73f9c742012-06-15 07:37:13361 sequence_token_(sequence_token),
362 ui_weak_ptr_factory_(this),
363 ui_weak_ptr_(ui_weak_ptr_factory_.GetWeakPtr()) {
364 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]3653146a2012-05-29 13:41:47365}
366
367GDataCache::~GDataCache() {
[email protected]73f9c742012-06-15 07:37:13368 AssertOnSequencedWorkerPool();
[email protected]3653146a2012-05-29 13:41:47369}
370
[email protected]32a7fc852012-06-08 17:25:50371FilePath GDataCache::GetCacheDirectoryPath(
372 CacheSubDirectoryType sub_dir_type) const {
373 DCHECK_LE(0, sub_dir_type);
374 DCHECK_GT(NUM_CACHE_TYPES, sub_dir_type);
375 return cache_paths_[sub_dir_type];
376}
377
378FilePath GDataCache::GetCacheFilePath(const std::string& resource_id,
379 const std::string& md5,
380 CacheSubDirectoryType sub_dir_type,
381 CachedFileOrigin file_origin) const {
382 DCHECK(sub_dir_type != CACHE_TYPE_META);
383
384 // Runs on any thread.
385 // Filename is formatted as resource_id.md5, i.e. resource_id is the base
386 // name and md5 is the extension.
387 std::string base_name = util::EscapeCacheFileName(resource_id);
388 if (file_origin == CACHED_FILE_LOCALLY_MODIFIED) {
389 DCHECK(sub_dir_type == CACHE_TYPE_PERSISTENT);
390 base_name += FilePath::kExtensionSeparator;
[email protected]b83e5202012-06-27 07:50:24391 base_name += util::kLocallyModifiedFileExtension;
[email protected]32a7fc852012-06-08 17:25:50392 } else if (!md5.empty()) {
393 base_name += FilePath::kExtensionSeparator;
394 base_name += util::EscapeCacheFileName(md5);
395 }
396 // For mounted archives the filename is formatted as resource_id.md5.mounted,
397 // i.e. resource_id.md5 is the base name and ".mounted" is the extension
398 if (file_origin == CACHED_FILE_MOUNTED) {
[email protected]a321b9632012-06-14 03:29:17399 DCHECK(sub_dir_type == CACHE_TYPE_PERSISTENT);
400 base_name += FilePath::kExtensionSeparator;
[email protected]ca5f6da2012-06-18 12:54:59401 base_name += util::kMountedArchiveFileExtension;
[email protected]32a7fc852012-06-08 17:25:50402 }
403 return GetCacheDirectoryPath(sub_dir_type).Append(base_name);
404}
405
[email protected]fcc92a52012-06-08 22:54:16406void GDataCache::AssertOnSequencedWorkerPool() {
407 DCHECK(!pool_ || pool_->IsRunningSequenceOnCurrentThread(sequence_token_));
408}
409
[email protected]01ba15f72012-06-09 00:41:05410bool GDataCache::IsUnderGDataCacheDirectory(const FilePath& path) const {
411 return cache_root_path_ == path || cache_root_path_.IsParent(path);
412}
413
[email protected]73f9c742012-06-15 07:37:13414void GDataCache::AddObserver(Observer* observer) {
415 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
416 observers_.AddObserver(observer);
417}
418
419void GDataCache::RemoveObserver(Observer* observer) {
420 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
421 observers_.RemoveObserver(observer);
422}
423
[email protected]4324fdc2012-06-29 05:32:48424void GDataCache::GetCacheEntryOnUIThread(
425 const std::string& resource_id,
426 const std::string& md5,
427 const GetCacheEntryCallback& callback) {
428 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
429
430 bool* success = new bool(false);
[email protected]fae353a2012-07-11 23:30:27431 GDataCacheEntry* cache_entry = new GDataCacheEntry;
[email protected]4324fdc2012-06-29 05:32:48432 pool_->GetSequencedTaskRunner(sequence_token_)->PostTaskAndReply(
433 FROM_HERE,
434 base::Bind(&GDataCache::GetCacheEntryHelper,
435 base::Unretained(this),
436 resource_id,
437 md5,
438 success,
439 cache_entry),
440 base::Bind(&RunGetCacheEntryCallback,
441 callback,
442 base::Owned(success),
443 base::Owned(cache_entry)));
444}
445
[email protected]b83e5202012-06-27 07:50:24446void GDataCache::GetResourceIdsOfBacklogOnUIThread(
[email protected]85b62192012-06-29 19:56:38447 const GetResourceIdsOfBacklogCallback& callback) {
[email protected]8764a392012-06-20 06:43:08448 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
449
[email protected]b83e5202012-06-27 07:50:24450 std::vector<std::string>* to_fetch = new std::vector<std::string>;
451 std::vector<std::string>* to_upload = new std::vector<std::string>;
[email protected]8764a392012-06-20 06:43:08452 pool_->GetSequencedTaskRunner(sequence_token_)->PostTaskAndReply(
453 FROM_HERE,
[email protected]b83e5202012-06-27 07:50:24454 base::Bind(&GDataCache::GetResourceIdsOfBacklog,
[email protected]8764a392012-06-20 06:43:08455 base::Unretained(this),
[email protected]b83e5202012-06-27 07:50:24456 to_fetch,
457 to_upload),
[email protected]85b62192012-06-29 19:56:38458 base::Bind(&RunGetResourceIdsOfBacklogCallback,
[email protected]8764a392012-06-20 06:43:08459 callback,
[email protected]b83e5202012-06-27 07:50:24460 base::Owned(to_fetch),
461 base::Owned(to_upload)));
[email protected]8764a392012-06-20 06:43:08462}
463
[email protected]85b62192012-06-29 19:56:38464void GDataCache::GetResourceIdsOfExistingPinnedFilesOnUIThread(
465 const GetResourceIdsCallback& callback) {
466 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
467
468 std::vector<std::string>* resource_ids = new std::vector<std::string>;
469 pool_->GetSequencedTaskRunner(sequence_token_)->PostTaskAndReply(
470 FROM_HERE,
471 base::Bind(&GDataCache::GetResourceIdsOfExistingPinnedFiles,
472 base::Unretained(this),
473 resource_ids),
474 base::Bind(&RunGetResourceIdsCallback,
475 callback,
476 base::Owned(resource_ids)));
477}
478
[email protected]a321b9632012-06-14 03:29:17479void GDataCache::FreeDiskSpaceIfNeededFor(int64 num_bytes,
480 bool* has_enough_space) {
481 AssertOnSequencedWorkerPool();
482
483 // Do nothing and return if we have enough space.
484 *has_enough_space = HasEnoughSpaceFor(num_bytes);
485 if (*has_enough_space)
486 return;
487
488 // Otherwise, try to free up the disk space.
489 DVLOG(1) << "Freeing up disk space for " << num_bytes;
490 // First remove temporary files from the cache map.
[email protected]ca5f6da2012-06-18 12:54:59491 metadata_->RemoveTemporaryFiles();
[email protected]a321b9632012-06-14 03:29:17492 // Then remove all files under "tmp" directory.
493 RemoveAllFiles(GetCacheDirectoryPath(GDataCache::CACHE_TYPE_TMP));
494
495 // Check the disk space again.
496 *has_enough_space = HasEnoughSpaceFor(num_bytes);
497}
498
[email protected]c960d222012-06-15 10:03:50499void GDataCache::GetFileOnUIThread(const std::string& resource_id,
500 const std::string& md5,
501 const GetFileFromCacheCallback& callback) {
502 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]a321b9632012-06-14 03:29:17503
[email protected]c960d222012-06-15 10:03:50504 base::PlatformFileError* error =
505 new base::PlatformFileError(base::PLATFORM_FILE_OK);
506 FilePath* cache_file_path = new FilePath;
507 pool_->GetSequencedTaskRunner(sequence_token_)->PostTaskAndReply(
508 FROM_HERE,
509 base::Bind(&GDataCache::GetFile,
510 base::Unretained(this),
511 resource_id,
512 md5,
513 error,
514 cache_file_path),
515 base::Bind(&RunGetFileFromCacheCallback,
516 callback,
517 base::Owned(error),
518 resource_id,
519 md5,
520 base::Owned(cache_file_path)));
[email protected]a321b9632012-06-14 03:29:17521}
522
[email protected]73f9c742012-06-15 07:37:13523void GDataCache::StoreOnUIThread(const std::string& resource_id,
524 const std::string& md5,
525 const FilePath& source_path,
526 FileOperationType file_operation_type,
527 const CacheOperationCallback& callback) {
528 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
529
530 base::PlatformFileError* error =
531 new base::PlatformFileError(base::PLATFORM_FILE_OK);
532 pool_->GetSequencedTaskRunner(sequence_token_)->PostTaskAndReply(
533 FROM_HERE,
534 base::Bind(&GDataCache::Store,
535 base::Unretained(this),
536 resource_id,
537 md5,
538 source_path,
539 file_operation_type,
540 error),
541 base::Bind(&RunCacheOperationCallback,
542 callback,
543 base::Owned(error),
544 resource_id,
545 md5));
546}
547
548void GDataCache::PinOnUIThread(const std::string& resource_id,
[email protected]44c0584e2012-06-15 23:55:41549 const std::string& md5,
550 const CacheOperationCallback& callback) {
[email protected]73f9c742012-06-15 07:37:13551 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
552
553 base::PlatformFileError* error =
554 new base::PlatformFileError(base::PLATFORM_FILE_OK);
555 pool_->GetSequencedTaskRunner(sequence_token_)->PostTaskAndReply(
556 FROM_HERE,
557 base::Bind(&GDataCache::Pin,
558 base::Unretained(this),
559 resource_id,
560 md5,
561 GDataCache::FILE_OPERATION_MOVE,
562 error),
563 base::Bind(&GDataCache::OnPinned,
564 ui_weak_ptr_,
565 base::Owned(error),
566 resource_id,
567 md5,
568 callback));
569}
570
571void GDataCache::UnpinOnUIThread(const std::string& resource_id,
572 const std::string& md5,
573 const CacheOperationCallback& callback) {
574 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
575 base::PlatformFileError* error =
576 new base::PlatformFileError(base::PLATFORM_FILE_OK);
577 pool_->GetSequencedTaskRunner(sequence_token_)->PostTaskAndReply(
578 FROM_HERE,
579 base::Bind(&GDataCache::Unpin,
580 base::Unretained(this),
581 resource_id,
582 md5,
583 GDataCache::FILE_OPERATION_MOVE,
584 error),
585 base::Bind(&GDataCache::OnUnpinned,
586 ui_weak_ptr_,
587 base::Owned(error),
588 resource_id,
589 md5,
590 callback));
591}
592
593void GDataCache::SetMountedStateOnUIThread(
594 const FilePath& file_path,
595 bool to_mount,
596 const SetMountedStateCallback& callback) {
597 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
598
599 base::PlatformFileError* error =
600 new base::PlatformFileError(base::PLATFORM_FILE_OK);
601 FilePath* cache_file_path = new FilePath;
602 pool_->GetSequencedTaskRunner(sequence_token_)->PostTaskAndReply(
603 FROM_HERE,
604 base::Bind(&GDataCache::SetMountedState,
605 base::Unretained(this),
606 file_path,
607 to_mount,
608 error,
609 cache_file_path),
610 base::Bind(&RunSetMountedStateCallback,
611 callback,
612 base::Owned(error),
613 base::Owned(cache_file_path)));
614}
615
[email protected]c960d222012-06-15 10:03:50616void GDataCache::MarkDirtyOnUIThread(const std::string& resource_id,
617 const std::string& md5,
618 const GetFileFromCacheCallback& callback) {
619 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]73f9c742012-06-15 07:37:13620
[email protected]c960d222012-06-15 10:03:50621 base::PlatformFileError* error =
622 new base::PlatformFileError(base::PLATFORM_FILE_OK);
623 FilePath* cache_file_path = new FilePath;
624 pool_->GetSequencedTaskRunner(sequence_token_)->PostTaskAndReply(
625 FROM_HERE,
626 base::Bind(&GDataCache::MarkDirty,
627 base::Unretained(this),
628 resource_id,
629 md5,
630 GDataCache::FILE_OPERATION_MOVE,
631 error,
632 cache_file_path),
633 base::Bind(&RunGetFileFromCacheCallback,
634 callback,
635 base::Owned(error),
636 resource_id,
637 md5,
638 base::Owned(cache_file_path)));
[email protected]73f9c742012-06-15 07:37:13639}
640
641void GDataCache::CommitDirtyOnUIThread(const std::string& resource_id,
642 const std::string& md5,
643 const CacheOperationCallback& callback) {
644 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
645
646 base::PlatformFileError* error =
647 new base::PlatformFileError(base::PLATFORM_FILE_OK);
648 pool_->GetSequencedTaskRunner(sequence_token_)->PostTaskAndReply(
649 FROM_HERE,
650 base::Bind(&GDataCache::CommitDirty,
651 base::Unretained(this),
652 resource_id,
653 md5,
654 GDataCache::FILE_OPERATION_MOVE,
655 error),
[email protected]d7664c22012-06-18 19:35:49656 base::Bind(&GDataCache::OnCommitDirty,
657 ui_weak_ptr_,
[email protected]73f9c742012-06-15 07:37:13658 base::Owned(error),
659 resource_id,
[email protected]d7664c22012-06-18 19:35:49660 md5,
661 callback));
[email protected]73f9c742012-06-15 07:37:13662}
663
664void GDataCache::ClearDirtyOnUIThread(const std::string& resource_id,
665 const std::string& md5,
666 const CacheOperationCallback& callback) {
667 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
668
669 base::PlatformFileError* error =
670 new base::PlatformFileError(base::PLATFORM_FILE_OK);
671 pool_->GetSequencedTaskRunner(sequence_token_)->PostTaskAndReply(
672 FROM_HERE,
673 base::Bind(&GDataCache::ClearDirty,
674 base::Unretained(this),
675 resource_id,
676 md5,
677 GDataCache::FILE_OPERATION_MOVE,
678 error),
679 base::Bind(&RunCacheOperationCallback,
680 callback,
681 base::Owned(error),
682 resource_id,
683 md5));
684}
685
686void GDataCache::RemoveOnUIThread(const std::string& resource_id,
687 const CacheOperationCallback& callback) {
688 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
689
690 base::PlatformFileError* error =
691 new base::PlatformFileError(base::PLATFORM_FILE_OK);
692
693 pool_->GetSequencedTaskRunner(sequence_token_)->PostTaskAndReply(
694 FROM_HERE,
695 base::Bind(&GDataCache::Remove,
696 base::Unretained(this),
697 resource_id,
698 error),
699 base::Bind(&RunCacheOperationCallback,
700 callback,
701 base::Owned(error),
702 resource_id,
703 "" /* md5 */));
704}
705
706void GDataCache::RequestInitializeOnUIThread() {
707 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
708
[email protected]1ff88ea52012-06-20 04:54:00709 pool_->GetSequencedTaskRunner(sequence_token_)->PostTask(
[email protected]73f9c742012-06-15 07:37:13710 FROM_HERE,
[email protected]1ff88ea52012-06-20 04:54:00711 base::Bind(&GDataCache::Initialize, base::Unretained(this)));
[email protected]73f9c742012-06-15 07:37:13712}
713
[email protected]fae353a2012-07-11 23:30:27714scoped_ptr<GDataCacheEntry> GDataCache::GetCacheEntry(
[email protected]73f9c742012-06-15 07:37:13715 const std::string& resource_id,
716 const std::string& md5) {
717 AssertOnSequencedWorkerPool();
[email protected]ca5f6da2012-06-18 12:54:59718 return metadata_->GetCacheEntry(resource_id, md5);
[email protected]73f9c742012-06-15 07:37:13719}
720
721// static
722GDataCache* GDataCache::CreateGDataCacheOnUIThread(
723 const FilePath& cache_root_path,
724 base::SequencedWorkerPool* pool,
725 const base::SequencedWorkerPool::SequenceToken& sequence_token) {
726 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]ca5f6da2012-06-18 12:54:59727 return new GDataCache(cache_root_path, pool, sequence_token);
[email protected]73f9c742012-06-15 07:37:13728}
729
730void GDataCache::DestroyOnUIThread() {
731 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
732
733 // Invalidate the weak pointer.
734 ui_weak_ptr_factory_.InvalidateWeakPtrs();
735
736 // Destroy myself on the blocking pool.
737 pool_->GetSequencedTaskRunner(sequence_token_)->PostTask(
738 FROM_HERE,
739 base::Bind(&GDataCache::Destroy,
740 base::Unretained(this)));
741}
742
[email protected]ca5f6da2012-06-18 12:54:59743void GDataCache::Initialize() {
744 AssertOnSequencedWorkerPool();
745
746 GDataCacheMetadataMap* cache_data =
747 new GDataCacheMetadataMap(pool_, sequence_token_);
748 cache_data->Initialize(cache_paths_);
749 metadata_.reset(cache_data);
750}
751
[email protected]73f9c742012-06-15 07:37:13752void GDataCache::Destroy() {
753 AssertOnSequencedWorkerPool();
754 delete this;
755}
756
[email protected]b83e5202012-06-27 07:50:24757void GDataCache::GetResourceIdsOfBacklog(
758 std::vector<std::string>* to_fetch,
759 std::vector<std::string>* to_upload) {
[email protected]8764a392012-06-20 06:43:08760 AssertOnSequencedWorkerPool();
[email protected]b83e5202012-06-27 07:50:24761 DCHECK(to_fetch);
762 DCHECK(to_upload);
[email protected]8764a392012-06-20 06:43:08763
[email protected]b83e5202012-06-27 07:50:24764 metadata_->Iterate(base::Bind(&CollectBacklog, to_fetch, to_upload));
[email protected]8764a392012-06-20 06:43:08765}
766
[email protected]85b62192012-06-29 19:56:38767void GDataCache::GetResourceIdsOfExistingPinnedFiles(
768 std::vector<std::string>* resource_ids) {
769 AssertOnSequencedWorkerPool();
770 DCHECK(resource_ids);
771
772 metadata_->Iterate(base::Bind(&CollectExistingPinnedFile, resource_ids));
773}
774
[email protected]c960d222012-06-15 10:03:50775void GDataCache::GetFile(const std::string& resource_id,
776 const std::string& md5,
777 base::PlatformFileError* error,
778 FilePath* cache_file_path) {
779 AssertOnSequencedWorkerPool();
780 DCHECK(error);
781 DCHECK(cache_file_path);
782
[email protected]fae353a2012-07-11 23:30:27783 scoped_ptr<GDataCacheEntry> cache_entry = GetCacheEntry(
[email protected]c960d222012-06-15 10:03:50784 resource_id, md5);
785 if (cache_entry.get() && cache_entry->IsPresent()) {
786 CachedFileOrigin file_origin;
787 if (cache_entry->IsMounted()) {
788 file_origin = CACHED_FILE_MOUNTED;
789 } else if (cache_entry->IsDirty()) {
790 file_origin = CACHED_FILE_LOCALLY_MODIFIED;
791 } else {
792 file_origin = CACHED_FILE_FROM_SERVER;
793 }
794 *cache_file_path = GetCacheFilePath(
795 resource_id,
796 md5,
[email protected]fae353a2012-07-11 23:30:27797 GetSubDirectoryType(*cache_entry),
[email protected]c960d222012-06-15 10:03:50798 file_origin);
799 *error = base::PLATFORM_FILE_OK;
800 } else {
801 *error = base::PLATFORM_FILE_ERROR_NOT_FOUND;
802 }
803}
804
[email protected]a321b9632012-06-14 03:29:17805void GDataCache::Store(const std::string& resource_id,
806 const std::string& md5,
807 const FilePath& source_path,
808 FileOperationType file_operation_type,
809 base::PlatformFileError* error) {
810 AssertOnSequencedWorkerPool();
811 DCHECK(error);
812
813 FilePath dest_path;
814 FilePath symlink_path;
[email protected]fae353a2012-07-11 23:30:27815 GDataCacheEntry new_cache_entry(md5, CACHE_STATE_NONE);
[email protected]a321b9632012-06-14 03:29:17816 CacheSubDirectoryType sub_dir_type = CACHE_TYPE_TMP;
817
[email protected]fae353a2012-07-11 23:30:27818 scoped_ptr<GDataCacheEntry> cache_entry = GetCacheEntry(resource_id, md5);
[email protected]a321b9632012-06-14 03:29:17819
820 // If file was previously pinned, store it in persistent dir and create
821 // symlink in pinned dir.
822 if (cache_entry.get()) { // File exists in cache.
823 // If file is dirty or mounted, return error.
824 if (cache_entry->IsDirty() || cache_entry->IsMounted()) {
825 LOG(WARNING) << "Can't store a file to replace a "
826 << (cache_entry->IsDirty() ? "dirty" : "mounted")
827 << " file: res_id=" << resource_id
828 << ", md5=" << md5;
829 *error = base::PLATFORM_FILE_ERROR_IN_USE;
830 return;
831 }
832
[email protected]e9adce9e2012-07-11 22:23:08833 new_cache_entry.set_cache_state(cache_entry->cache_state());
[email protected]a321b9632012-06-14 03:29:17834
835 // If file is pinned, determines destination path.
836 if (cache_entry->IsPinned()) {
837 sub_dir_type = CACHE_TYPE_PERSISTENT;
838 dest_path = GetCacheFilePath(resource_id, md5, sub_dir_type,
839 CACHED_FILE_FROM_SERVER);
840 symlink_path = GetCacheFilePath(
841 resource_id, std::string(), CACHE_TYPE_PINNED,
842 CACHED_FILE_FROM_SERVER);
843 }
844 }
845
846 // File wasn't pinned or doesn't exist in cache, store in tmp dir.
847 if (dest_path.empty()) {
848 DCHECK_EQ(CACHE_TYPE_TMP, sub_dir_type);
849 dest_path = GetCacheFilePath(resource_id, md5, sub_dir_type,
850 CACHED_FILE_FROM_SERVER);
851 }
852
853 *error = ModifyCacheState(
854 source_path,
855 dest_path,
856 file_operation_type,
857 symlink_path,
858 !symlink_path.empty()); // create symlink
859
860 // Determine search pattern for stale filenames corrresponding to resource_id,
861 // either "<resource_id>*" or "<resource_id>.*".
862 FilePath stale_filenames_pattern;
863 if (md5.empty()) {
864 // No md5 means no extension, append '*' after base name, i.e.
865 // "<resource_id>*".
866 // Cannot call |dest_path|.ReplaceExtension when there's no md5 extension:
867 // if base name of |dest_path| (i.e. escaped resource_id) contains the
868 // extension separator '.', ReplaceExtension will remove it and everything
869 // after it. The result will be nothing like the escaped resource_id.
[email protected]ca5f6da2012-06-18 12:54:59870 stale_filenames_pattern = FilePath(dest_path.value() + util::kWildCard);
[email protected]a321b9632012-06-14 03:29:17871 } else {
872 // Replace md5 extension with '*' i.e. "<resource_id>.*".
873 // Note that ReplaceExtension automatically prefixes the extension with the
874 // extension separator '.'.
[email protected]ca5f6da2012-06-18 12:54:59875 stale_filenames_pattern = dest_path.ReplaceExtension(util::kWildCard);
[email protected]a321b9632012-06-14 03:29:17876 }
877
878 // Delete files that match |stale_filenames_pattern| except for |dest_path|.
879 DeleteFilesSelectively(stale_filenames_pattern, dest_path);
880
881 if (*error == base::PLATFORM_FILE_OK) {
882 // Now that file operations have completed, update cache map.
[email protected]a288c382012-07-11 20:05:48883 new_cache_entry.SetPresent(true);
[email protected]3dc88ee2012-07-11 21:04:11884 new_cache_entry.SetPersistent(sub_dir_type == CACHE_TYPE_PERSISTENT);
[email protected]3423871a2012-07-12 00:41:27885 metadata_->AddOrUpdateCacheEntry(resource_id, new_cache_entry);
[email protected]a321b9632012-06-14 03:29:17886 }
887}
888
889void GDataCache::Pin(const std::string& resource_id,
890 const std::string& md5,
891 FileOperationType file_operation_type,
892 base::PlatformFileError* error) {
893 AssertOnSequencedWorkerPool();
894 DCHECK(error);
895
896 FilePath source_path;
897 FilePath dest_path;
898 FilePath symlink_path;
899 bool create_symlink = true;
[email protected]fae353a2012-07-11 23:30:27900 GDataCacheEntry new_cache_entry(md5, CACHE_STATE_NONE);
[email protected]a321b9632012-06-14 03:29:17901 CacheSubDirectoryType sub_dir_type = CACHE_TYPE_PERSISTENT;
902
[email protected]fae353a2012-07-11 23:30:27903 scoped_ptr<GDataCacheEntry> cache_entry = GetCacheEntry(resource_id, md5);
[email protected]a321b9632012-06-14 03:29:17904
905 if (!cache_entry.get()) { // Entry does not exist in cache.
906 // Set both |dest_path| and |source_path| to /dev/null, so that:
907 // 1) ModifyCacheState won't move files when |source_path| and |dest_path|
908 // are the same.
909 // 2) symlinks to /dev/null will be picked up by GDataSyncClient to download
910 // pinned files that don't exist in cache.
[email protected]30d9dda2012-06-30 05:56:28911 dest_path = FilePath::FromUTF8Unsafe(util::kSymLinkToDevNull);
[email protected]a321b9632012-06-14 03:29:17912 source_path = dest_path;
913
[email protected]2a6fe6e2012-07-10 06:01:04914 // Set sub_dir_type to TMP. The file will be first downloaded in 'tmp',
915 // then moved to 'persistent'.
916 sub_dir_type = CACHE_TYPE_TMP;
[email protected]a321b9632012-06-14 03:29:17917 } else { // File exists in cache, determines destination path.
[email protected]e9adce9e2012-07-11 22:23:08918 new_cache_entry.set_cache_state(cache_entry->cache_state());
[email protected]a321b9632012-06-14 03:29:17919
920 // Determine source and destination paths.
921
922 // If file is dirty or mounted, don't move it, so determine |dest_path| and
923 // set |source_path| the same, because ModifyCacheState only moves files if
924 // source and destination are different.
925 if (cache_entry->IsDirty() || cache_entry->IsMounted()) {
[email protected]a6689d32012-07-10 20:42:31926 DCHECK(cache_entry->IsPersistent());
[email protected]a321b9632012-06-14 03:29:17927 dest_path = GetCacheFilePath(resource_id,
928 md5,
[email protected]fae353a2012-07-11 23:30:27929 GetSubDirectoryType(*cache_entry),
[email protected]a321b9632012-06-14 03:29:17930 CACHED_FILE_LOCALLY_MODIFIED);
931 source_path = dest_path;
932 } else {
933 // Gets the current path of the file in cache.
934 source_path = GetCacheFilePath(resource_id,
935 md5,
[email protected]fae353a2012-07-11 23:30:27936 GetSubDirectoryType(*cache_entry),
[email protected]a321b9632012-06-14 03:29:17937 CACHED_FILE_FROM_SERVER);
938
939 // If file was pinned before but actual file blob doesn't exist in cache:
940 // - don't need to move the file, so set |dest_path| to |source_path|,
941 // because ModifyCacheState only moves files if source and destination
942 // are different
943 // - don't create symlink since it already exists.
[email protected]4f8583e2012-07-09 19:46:56944 if (!cache_entry->IsPresent()) {
[email protected]a321b9632012-06-14 03:29:17945 dest_path = source_path;
946 create_symlink = false;
947 } else { // File exists, move it to persistent dir.
948 dest_path = GetCacheFilePath(resource_id,
949 md5,
950 CACHE_TYPE_PERSISTENT,
951 CACHED_FILE_FROM_SERVER);
952 }
953 }
954 }
955
956 // Create symlink in pinned dir.
957 if (create_symlink) {
958 symlink_path = GetCacheFilePath(resource_id,
959 std::string(),
960 CACHE_TYPE_PINNED,
961 CACHED_FILE_FROM_SERVER);
962 }
963
964 *error = ModifyCacheState(source_path,
965 dest_path,
966 file_operation_type,
967 symlink_path,
968 create_symlink);
969
970 if (*error == base::PLATFORM_FILE_OK) {
971 // Now that file operations have completed, update cache map.
[email protected]a288c382012-07-11 20:05:48972 new_cache_entry.SetPinned(true);
[email protected]3dc88ee2012-07-11 21:04:11973 new_cache_entry.SetPersistent(sub_dir_type == CACHE_TYPE_PERSISTENT);
[email protected]3423871a2012-07-12 00:41:27974 metadata_->AddOrUpdateCacheEntry(resource_id, new_cache_entry);
[email protected]a321b9632012-06-14 03:29:17975 }
976}
977
978void GDataCache::Unpin(const std::string& resource_id,
979 const std::string& md5,
980 FileOperationType file_operation_type,
981 base::PlatformFileError* error) {
982 AssertOnSequencedWorkerPool();
983 DCHECK(error);
984
[email protected]fae353a2012-07-11 23:30:27985 scoped_ptr<GDataCacheEntry> cache_entry = GetCacheEntry(resource_id, md5);
[email protected]a321b9632012-06-14 03:29:17986
987 // Unpinning a file means its entry must exist in cache.
988 if (!cache_entry.get()) {
989 LOG(WARNING) << "Can't unpin a file that wasn't pinned or cached: res_id="
990 << resource_id
991 << ", md5=" << md5;
992 *error = base::PLATFORM_FILE_ERROR_NOT_FOUND;
993 return;
994 }
995
996 // Entry exists in cache, determines source and destination paths.
997
998 FilePath source_path;
999 FilePath dest_path;
1000 CacheSubDirectoryType sub_dir_type = CACHE_TYPE_TMP;
1001
1002 // If file is dirty or mounted, don't move it, so determine |dest_path| and
1003 // set |source_path| the same, because ModifyCacheState moves files if source
1004 // and destination are different.
1005 if (cache_entry->IsDirty() || cache_entry->IsMounted()) {
1006 sub_dir_type = CACHE_TYPE_PERSISTENT;
[email protected]a6689d32012-07-10 20:42:311007 DCHECK(cache_entry->IsPersistent());
[email protected]a321b9632012-06-14 03:29:171008 dest_path = GetCacheFilePath(resource_id,
1009 md5,
[email protected]fae353a2012-07-11 23:30:271010 GetSubDirectoryType(*cache_entry),
[email protected]a321b9632012-06-14 03:29:171011 CACHED_FILE_LOCALLY_MODIFIED);
1012 source_path = dest_path;
1013 } else {
1014 // Gets the current path of the file in cache.
1015 source_path = GetCacheFilePath(resource_id,
1016 md5,
[email protected]fae353a2012-07-11 23:30:271017 GetSubDirectoryType(*cache_entry),
[email protected]a321b9632012-06-14 03:29:171018 CACHED_FILE_FROM_SERVER);
1019
1020 // If file was pinned but actual file blob still doesn't exist in cache,
1021 // don't need to move the file, so set |dest_path| to |source_path|, because
1022 // ModifyCacheState only moves files if source and destination are
1023 // different.
[email protected]4f8583e2012-07-09 19:46:561024 if (!cache_entry->IsPresent()) {
[email protected]a321b9632012-06-14 03:29:171025 dest_path = source_path;
1026 } else { // File exists, move it to tmp dir.
1027 dest_path = GetCacheFilePath(resource_id, md5,
1028 CACHE_TYPE_TMP,
1029 CACHED_FILE_FROM_SERVER);
1030 }
1031 }
1032
1033 // If file was pinned, get absolute path of symlink in pinned dir so as to
1034 // remove it.
1035 FilePath symlink_path;
1036 if (cache_entry->IsPinned()) {
1037 symlink_path = GetCacheFilePath(resource_id,
1038 std::string(),
1039 CACHE_TYPE_PINNED,
1040 CACHED_FILE_FROM_SERVER);
1041 }
1042
1043 *error = ModifyCacheState(
1044 source_path,
1045 dest_path,
1046 file_operation_type,
1047 symlink_path, // This will be deleted if it exists.
1048 false /* don't create symlink*/);
1049
1050 if (*error == base::PLATFORM_FILE_OK) {
1051 // Now that file operations have completed, update cache map.
[email protected]3423871a2012-07-12 00:41:271052 if (cache_entry->IsPresent()) {
1053 GDataCacheEntry new_cache_entry(md5, cache_entry->cache_state());
1054 new_cache_entry.SetPinned(false);
1055 new_cache_entry.SetPersistent(sub_dir_type == CACHE_TYPE_PERSISTENT);
1056 metadata_->AddOrUpdateCacheEntry(resource_id, new_cache_entry);
1057 } else {
1058 // Remove the existing entry if we are unpinning a non-present file.
1059 metadata_->RemoveCacheEntry(resource_id);
1060 }
[email protected]a321b9632012-06-14 03:29:171061 }
1062}
1063
1064void GDataCache::SetMountedState(const FilePath& file_path,
1065 bool to_mount,
1066 base::PlatformFileError *error,
1067 FilePath* cache_file_path) {
1068 AssertOnSequencedWorkerPool();
1069 DCHECK(error);
1070 DCHECK(cache_file_path);
1071
1072 // Parse file path to obtain resource_id, md5 and extra_extension.
1073 std::string resource_id;
1074 std::string md5;
1075 std::string extra_extension;
1076 util::ParseCacheFilePath(file_path, &resource_id, &md5, &extra_extension);
1077 // The extra_extension shall be ".mounted" iff we're unmounting.
[email protected]ca5f6da2012-06-18 12:54:591078 DCHECK(!to_mount == (extra_extension == util::kMountedArchiveFileExtension));
[email protected]a321b9632012-06-14 03:29:171079
1080 // Get cache entry associated with the resource_id and md5
[email protected]fae353a2012-07-11 23:30:271081 scoped_ptr<GDataCacheEntry> cache_entry = GetCacheEntry(
[email protected]a321b9632012-06-14 03:29:171082 resource_id, md5);
1083 if (!cache_entry.get()) {
1084 *error = base::PLATFORM_FILE_ERROR_NOT_FOUND;
1085 return;
1086 }
1087 if (to_mount == cache_entry->IsMounted()) {
1088 *error = base::PLATFORM_FILE_ERROR_INVALID_OPERATION;
1089 return;
1090 }
1091
1092 // Get the subdir type and path for the unmounted state.
1093 CacheSubDirectoryType unmounted_subdir =
1094 cache_entry->IsPinned() ? CACHE_TYPE_PERSISTENT : CACHE_TYPE_TMP;
1095 FilePath unmounted_path = GetCacheFilePath(
1096 resource_id, md5, unmounted_subdir, CACHED_FILE_FROM_SERVER);
1097
1098 // Get the subdir type and path for the mounted state.
1099 CacheSubDirectoryType mounted_subdir = CACHE_TYPE_PERSISTENT;
1100 FilePath mounted_path = GetCacheFilePath(
1101 resource_id, md5, mounted_subdir, CACHED_FILE_MOUNTED);
1102
1103 // Determine the source and destination paths for moving the cache blob.
1104 FilePath source_path;
1105 CacheSubDirectoryType dest_subdir;
[email protected]fae353a2012-07-11 23:30:271106 GDataCacheEntry new_cache_entry(md5, cache_entry->cache_state());
[email protected]a321b9632012-06-14 03:29:171107 if (to_mount) {
1108 source_path = unmounted_path;
1109 *cache_file_path = mounted_path;
1110 dest_subdir = mounted_subdir;
[email protected]a288c382012-07-11 20:05:481111 new_cache_entry.SetMounted(true);
[email protected]a321b9632012-06-14 03:29:171112 } else {
1113 source_path = mounted_path;
1114 *cache_file_path = unmounted_path;
1115 dest_subdir = unmounted_subdir;
[email protected]a288c382012-07-11 20:05:481116 new_cache_entry.SetMounted(false);
[email protected]a321b9632012-06-14 03:29:171117 }
1118
1119 // Move cache blob from source path to destination path.
1120 *error = ModifyCacheState(source_path, *cache_file_path,
1121 FILE_OPERATION_MOVE, FilePath(), false);
1122 if (*error == base::PLATFORM_FILE_OK) {
1123 // Now that cache operation is complete, update cache map
[email protected]3dc88ee2012-07-11 21:04:111124 new_cache_entry.SetPersistent(dest_subdir == CACHE_TYPE_PERSISTENT);
[email protected]3423871a2012-07-12 00:41:271125 metadata_->AddOrUpdateCacheEntry(resource_id, new_cache_entry);
[email protected]a321b9632012-06-14 03:29:171126 }
1127}
1128
[email protected]c960d222012-06-15 10:03:501129void GDataCache::MarkDirty(const std::string& resource_id,
1130 const std::string& md5,
1131 FileOperationType file_operation_type,
1132 base::PlatformFileError* error,
1133 FilePath* cache_file_path) {
1134 AssertOnSequencedWorkerPool();
1135 DCHECK(error);
1136 DCHECK(cache_file_path);
1137
1138 // If file has already been marked dirty in previous instance of chrome, we
1139 // would have lost the md5 info during cache initialization, because the file
1140 // would have been renamed to .local extension.
1141 // So, search for entry in cache without comparing md5.
[email protected]fae353a2012-07-11 23:30:271142 scoped_ptr<GDataCacheEntry> cache_entry =
[email protected]c960d222012-06-15 10:03:501143 GetCacheEntry(resource_id, std::string());
1144
1145 // Marking a file dirty means its entry and actual file blob must exist in
1146 // cache.
[email protected]4f8583e2012-07-09 19:46:561147 if (!cache_entry.get() || !cache_entry->IsPresent()) {
[email protected]c960d222012-06-15 10:03:501148 LOG(WARNING) << "Can't mark dirty a file that wasn't cached: res_id="
1149 << resource_id
1150 << ", md5=" << md5;
1151 *error = base::PLATFORM_FILE_ERROR_NOT_FOUND;
1152 return;
1153 }
1154
1155 // If a file is already dirty (i.e. MarkDirtyInCache was called before),
1156 // delete outgoing symlink if it exists.
1157 // TODO(benchan): We should only delete outgoing symlink if file is currently
1158 // not being uploaded. However, for now, cache doesn't know if uploading of a
1159 // file is in progress. Per zel, the upload process should be canceled before
1160 // MarkDirtyInCache is called again.
1161 if (cache_entry->IsDirty()) {
1162 // The file must be in persistent dir.
[email protected]a6689d32012-07-10 20:42:311163 DCHECK(cache_entry->IsPersistent());
[email protected]c960d222012-06-15 10:03:501164
1165 // Determine symlink path in outgoing dir, so as to remove it.
1166 FilePath symlink_path = GetCacheFilePath(
1167 resource_id,
1168 std::string(),
1169 CACHE_TYPE_OUTGOING,
1170 CACHED_FILE_FROM_SERVER);
1171
1172 // We're not moving files here, so simply use empty FilePath for both
1173 // |source_path| and |dest_path| because ModifyCacheState only move files
1174 // if source and destination are different.
1175 *error = ModifyCacheState(
1176 FilePath(), // non-applicable source path
1177 FilePath(), // non-applicable dest path
1178 file_operation_type,
1179 symlink_path,
1180 false /* don't create symlink */);
1181
1182 // Determine current path of dirty file.
1183 if (*error == base::PLATFORM_FILE_OK) {
1184 *cache_file_path = GetCacheFilePath(
1185 resource_id,
1186 md5,
1187 CACHE_TYPE_PERSISTENT,
1188 CACHED_FILE_LOCALLY_MODIFIED);
1189 }
1190 return;
1191 }
1192
1193 // Move file to persistent dir with new .local extension.
1194
1195 // Get the current path of the file in cache.
1196 FilePath source_path = GetCacheFilePath(
1197 resource_id,
1198 md5,
[email protected]fae353a2012-07-11 23:30:271199 GetSubDirectoryType(*cache_entry),
[email protected]c960d222012-06-15 10:03:501200 CACHED_FILE_FROM_SERVER);
1201
1202 // Determine destination path.
[email protected]3dc88ee2012-07-11 21:04:111203 const CacheSubDirectoryType sub_dir_type = CACHE_TYPE_PERSISTENT;
[email protected]c960d222012-06-15 10:03:501204 *cache_file_path = GetCacheFilePath(resource_id,
1205 md5,
1206 sub_dir_type,
1207 CACHED_FILE_LOCALLY_MODIFIED);
1208
1209 // If file is pinned, update symlink in pinned dir.
1210 FilePath symlink_path;
1211 if (cache_entry->IsPinned()) {
1212 symlink_path = GetCacheFilePath(resource_id,
1213 std::string(),
1214 CACHE_TYPE_PINNED,
1215 CACHED_FILE_FROM_SERVER);
1216 }
1217
1218 *error = ModifyCacheState(
1219 source_path,
1220 *cache_file_path,
1221 file_operation_type,
1222 symlink_path,
1223 !symlink_path.empty() /* create symlink */);
1224
1225 if (*error == base::PLATFORM_FILE_OK) {
1226 // Now that file operations have completed, update cache map.
[email protected]fae353a2012-07-11 23:30:271227 GDataCacheEntry new_cache_entry(md5, cache_entry->cache_state());
[email protected]a288c382012-07-11 20:05:481228 new_cache_entry.SetDirty(true);
[email protected]3dc88ee2012-07-11 21:04:111229 new_cache_entry.SetPersistent(sub_dir_type == CACHE_TYPE_PERSISTENT);
[email protected]3423871a2012-07-12 00:41:271230 metadata_->AddOrUpdateCacheEntry(resource_id, new_cache_entry);
[email protected]c960d222012-06-15 10:03:501231 }
1232}
1233
[email protected]a321b9632012-06-14 03:29:171234void GDataCache::CommitDirty(const std::string& resource_id,
1235 const std::string& md5,
1236 FileOperationType file_operation_type,
1237 base::PlatformFileError* error) {
1238 AssertOnSequencedWorkerPool();
1239 DCHECK(error);
1240
1241 // If file has already been marked dirty in previous instance of chrome, we
1242 // would have lost the md5 info during cache initialization, because the file
1243 // would have been renamed to .local extension.
1244 // So, search for entry in cache without comparing md5.
[email protected]fae353a2012-07-11 23:30:271245 scoped_ptr<GDataCacheEntry> cache_entry =
[email protected]a321b9632012-06-14 03:29:171246 GetCacheEntry(resource_id, std::string());
1247
1248 // Committing a file dirty means its entry and actual file blob must exist in
1249 // cache.
[email protected]4f8583e2012-07-09 19:46:561250 if (!cache_entry.get() || !cache_entry->IsPresent()) {
[email protected]a321b9632012-06-14 03:29:171251 LOG(WARNING) << "Can't commit dirty a file that wasn't cached: res_id="
1252 << resource_id
1253 << ", md5=" << md5;
1254 *error = base::PLATFORM_FILE_ERROR_NOT_FOUND;
1255 return;
1256 }
1257
1258 // If a file is not dirty (it should have been marked dirty via
1259 // MarkDirtyInCache), committing it dirty is an invalid operation.
1260 if (!cache_entry->IsDirty()) {
1261 LOG(WARNING) << "Can't commit a non-dirty file: res_id="
1262 << resource_id
1263 << ", md5=" << md5;
1264 *error = base::PLATFORM_FILE_ERROR_INVALID_OPERATION;
1265 return;
1266 }
1267
1268 // Dirty files must be in persistent dir.
[email protected]a6689d32012-07-10 20:42:311269 DCHECK(cache_entry->IsPersistent());
[email protected]a321b9632012-06-14 03:29:171270
1271 // Create symlink in outgoing dir.
1272 FilePath symlink_path = GetCacheFilePath(resource_id,
1273 std::string(),
1274 CACHE_TYPE_OUTGOING,
1275 CACHED_FILE_FROM_SERVER);
1276
1277 // Get target path of symlink i.e. current path of the file in cache.
1278 FilePath target_path = GetCacheFilePath(resource_id,
1279 md5,
[email protected]fae353a2012-07-11 23:30:271280 GetSubDirectoryType(*cache_entry),
[email protected]a321b9632012-06-14 03:29:171281 CACHED_FILE_LOCALLY_MODIFIED);
1282
1283 // Since there's no need to move files, use |target_path| for both
1284 // |source_path| and |dest_path|, because ModifyCacheState only moves files
1285 // if source and destination are different.
1286 *error = ModifyCacheState(target_path, // source
1287 target_path, // destination
1288 file_operation_type,
1289 symlink_path,
1290 true /* create symlink */);
1291}
1292
1293void GDataCache::ClearDirty(const std::string& resource_id,
1294 const std::string& md5,
1295 FileOperationType file_operation_type,
1296 base::PlatformFileError* error) {
1297 AssertOnSequencedWorkerPool();
1298 DCHECK(error);
1299
1300 // |md5| is the new .<md5> extension to rename the file to.
1301 // So, search for entry in cache without comparing md5.
[email protected]fae353a2012-07-11 23:30:271302 scoped_ptr<GDataCacheEntry> cache_entry =
[email protected]a321b9632012-06-14 03:29:171303 GetCacheEntry(resource_id, std::string());
1304
1305 // Clearing a dirty file means its entry and actual file blob must exist in
1306 // cache.
[email protected]4f8583e2012-07-09 19:46:561307 if (!cache_entry.get() || !cache_entry->IsPresent()) {
[email protected]a321b9632012-06-14 03:29:171308 LOG(WARNING) << "Can't clear dirty state of a file that wasn't cached: "
1309 << "res_id=" << resource_id
1310 << ", md5=" << md5;
1311 *error = base::PLATFORM_FILE_ERROR_NOT_FOUND;
1312 return;
1313 }
1314
1315 // If a file is not dirty (it should have been marked dirty via
1316 // MarkDirtyInCache), clearing its dirty state is an invalid operation.
1317 if (!cache_entry->IsDirty()) {
1318 LOG(WARNING) << "Can't clear dirty state of a non-dirty file: res_id="
1319 << resource_id
1320 << ", md5=" << md5;
1321 *error = base::PLATFORM_FILE_ERROR_INVALID_OPERATION;
1322 return;
1323 }
1324
1325 // File must be dirty and hence in persistent dir.
[email protected]a6689d32012-07-10 20:42:311326 DCHECK(cache_entry->IsPersistent());
[email protected]a321b9632012-06-14 03:29:171327
1328 // Get the current path of the file in cache.
1329 FilePath source_path = GetCacheFilePath(resource_id,
1330 md5,
[email protected]fae353a2012-07-11 23:30:271331 GetSubDirectoryType(*cache_entry),
[email protected]a321b9632012-06-14 03:29:171332 CACHED_FILE_LOCALLY_MODIFIED);
1333
1334 // Determine destination path.
1335 // If file is pinned, move it to persistent dir with .md5 extension;
1336 // otherwise, move it to tmp dir with .md5 extension.
[email protected]3dc88ee2012-07-11 21:04:111337 const CacheSubDirectoryType sub_dir_type =
[email protected]a321b9632012-06-14 03:29:171338 cache_entry->IsPinned() ? CACHE_TYPE_PERSISTENT : CACHE_TYPE_TMP;
1339 FilePath dest_path = GetCacheFilePath(resource_id,
1340 md5,
1341 sub_dir_type,
1342 CACHED_FILE_FROM_SERVER);
1343
1344 // Delete symlink in outgoing dir.
1345 FilePath symlink_path = GetCacheFilePath(resource_id,
1346 std::string(),
1347 CACHE_TYPE_OUTGOING,
1348 CACHED_FILE_FROM_SERVER);
1349
1350 *error = ModifyCacheState(source_path,
1351 dest_path,
1352 file_operation_type,
1353 symlink_path,
1354 false /* don't create symlink */);
1355
1356 // If file is pinned, update symlink in pinned dir.
1357 if (*error == base::PLATFORM_FILE_OK && cache_entry->IsPinned()) {
1358 symlink_path = GetCacheFilePath(resource_id,
1359 std::string(),
1360 CACHE_TYPE_PINNED,
1361 CACHED_FILE_FROM_SERVER);
1362
1363 // Since there's no moving of files here, use |dest_path| for both
1364 // |source_path| and |dest_path|, because ModifyCacheState only moves files
1365 // if source and destination are different.
1366 *error = ModifyCacheState(dest_path, // source path
1367 dest_path, // destination path
1368 file_operation_type,
1369 symlink_path,
1370 true /* create symlink */);
1371 }
1372
1373 if (*error == base::PLATFORM_FILE_OK) {
1374 // Now that file operations have completed, update cache map.
[email protected]fae353a2012-07-11 23:30:271375 GDataCacheEntry new_cache_entry(md5, cache_entry->cache_state());
[email protected]a288c382012-07-11 20:05:481376 new_cache_entry.SetDirty(false);
[email protected]3dc88ee2012-07-11 21:04:111377 new_cache_entry.SetPersistent(sub_dir_type == CACHE_TYPE_PERSISTENT);
[email protected]3423871a2012-07-12 00:41:271378 metadata_->AddOrUpdateCacheEntry(resource_id, new_cache_entry);
[email protected]a321b9632012-06-14 03:29:171379 }
1380}
1381
1382void GDataCache::Remove(const std::string& resource_id,
1383 base::PlatformFileError* error) {
1384 AssertOnSequencedWorkerPool();
1385 DCHECK(error);
1386
[email protected]3423871a2012-07-12 00:41:271387 // MD5 is not passed into RemoveCacheEntry because we would delete all
1388 // cache files corresponding to <resource_id> regardless of the md5.
[email protected]a321b9632012-06-14 03:29:171389 // So, search for entry in cache without taking md5 into account.
[email protected]fae353a2012-07-11 23:30:271390 scoped_ptr<GDataCacheEntry> cache_entry =
[email protected]a321b9632012-06-14 03:29:171391 GetCacheEntry(resource_id, std::string());
1392
1393 // If entry doesn't exist or is dirty or mounted in cache, nothing to do.
1394 if (!cache_entry.get() ||
1395 cache_entry->IsDirty() ||
1396 cache_entry->IsMounted()) {
1397 DVLOG(1) << "Entry is "
1398 << (cache_entry.get() ?
1399 (cache_entry->IsDirty() ? "dirty" : "mounted") :
1400 "non-existent")
1401 << " in cache, not removing";
1402 *error = base::PLATFORM_FILE_OK;
1403 return;
1404 }
1405
1406 // Determine paths to delete all cache versions of |resource_id| in
1407 // persistent, tmp and pinned directories.
1408 std::vector<FilePath> paths_to_delete;
1409
1410 // For files in persistent and tmp dirs, delete files that match
1411 // "<resource_id>.*".
1412 paths_to_delete.push_back(GetCacheFilePath(resource_id,
[email protected]ca5f6da2012-06-18 12:54:591413 util::kWildCard,
[email protected]a321b9632012-06-14 03:29:171414 CACHE_TYPE_PERSISTENT,
1415 CACHED_FILE_FROM_SERVER));
1416 paths_to_delete.push_back(GetCacheFilePath(resource_id,
[email protected]ca5f6da2012-06-18 12:54:591417 util::kWildCard,
[email protected]a321b9632012-06-14 03:29:171418 CACHE_TYPE_TMP,
1419 CACHED_FILE_FROM_SERVER));
1420
1421 // For pinned files, filename is "<resource_id>" with no extension, so delete
1422 // "<resource_id>".
1423 paths_to_delete.push_back(GetCacheFilePath(resource_id,
1424 std::string(),
1425 CACHE_TYPE_PINNED,
1426 CACHED_FILE_FROM_SERVER));
1427
1428 // Don't delete locally modified (i.e. dirty and possibly outgoing) files.
1429 // Since we're not deleting outgoing symlinks, we don't need to append
1430 // outgoing path to |paths_to_delete|.
1431 FilePath path_to_keep = GetCacheFilePath(resource_id,
1432 std::string(),
1433 CACHE_TYPE_PERSISTENT,
1434 CACHED_FILE_LOCALLY_MODIFIED);
1435
1436 for (size_t i = 0; i < paths_to_delete.size(); ++i) {
1437 DeleteFilesSelectively(paths_to_delete[i], path_to_keep);
1438 }
1439
1440 // Now that all file operations have completed, remove from cache map.
[email protected]3423871a2012-07-12 00:41:271441 metadata_->RemoveCacheEntry(resource_id);
[email protected]a321b9632012-06-14 03:29:171442
1443 *error = base::PLATFORM_FILE_OK;
1444}
1445
[email protected]73f9c742012-06-15 07:37:131446void GDataCache::OnPinned(base::PlatformFileError* error,
1447 const std::string& resource_id,
1448 const std::string& md5,
1449 const CacheOperationCallback& callback) {
1450 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1451 DCHECK(error);
1452
1453 if (!callback.is_null())
1454 callback.Run(*error, resource_id, md5);
1455
1456 if (*error == base::PLATFORM_FILE_OK)
1457 FOR_EACH_OBSERVER(Observer, observers_, OnCachePinned(resource_id, md5));
[email protected]3653146a2012-05-29 13:41:471458}
1459
[email protected]73f9c742012-06-15 07:37:131460void GDataCache::OnUnpinned(base::PlatformFileError* error,
1461 const std::string& resource_id,
1462 const std::string& md5,
1463 const CacheOperationCallback& callback) {
1464 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1465 DCHECK(error);
[email protected]a321b9632012-06-14 03:29:171466
[email protected]73f9c742012-06-15 07:37:131467 if (!callback.is_null())
1468 callback.Run(*error, resource_id, md5);
[email protected]a321b9632012-06-14 03:29:171469
[email protected]73f9c742012-06-15 07:37:131470 if (*error == base::PLATFORM_FILE_OK)
1471 FOR_EACH_OBSERVER(Observer, observers_, OnCacheUnpinned(resource_id, md5));
[email protected]a321b9632012-06-14 03:29:171472
[email protected]73f9c742012-06-15 07:37:131473 // Now the file is moved from "persistent" to "tmp" directory.
1474 // It's a chance to free up space if needed.
1475 bool* has_enough_space = new bool(false);
1476 pool_->GetSequencedTaskRunner(sequence_token_)->PostTask(
1477 FROM_HERE,
1478 base::Bind(&GDataCache::FreeDiskSpaceIfNeededFor,
1479 base::Unretained(this),
1480 0,
1481 base::Owned(has_enough_space)));
[email protected]3653146a2012-05-29 13:41:471482}
1483
[email protected]d7664c22012-06-18 19:35:491484void GDataCache::OnCommitDirty(base::PlatformFileError* error,
1485 const std::string& resource_id,
1486 const std::string& md5,
1487 const CacheOperationCallback& callback) {
1488 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1489 DCHECK(error);
1490
1491 if (!callback.is_null())
1492 callback.Run(*error, resource_id, md5);
1493
1494 if (*error == base::PLATFORM_FILE_OK)
1495 FOR_EACH_OBSERVER(Observer, observers_, OnCacheCommitted(resource_id));
1496}
1497
[email protected]4324fdc2012-06-29 05:32:481498void GDataCache::GetCacheEntryHelper(const std::string& resource_id,
1499 const std::string& md5,
1500 bool* success,
[email protected]fae353a2012-07-11 23:30:271501 GDataCacheEntry* cache_entry) {
[email protected]4324fdc2012-06-29 05:32:481502 AssertOnSequencedWorkerPool();
1503 DCHECK(success);
1504 DCHECK(cache_entry);
1505
[email protected]fae353a2012-07-11 23:30:271506 scoped_ptr<GDataCacheEntry> value(GetCacheEntry(resource_id, md5));
[email protected]4324fdc2012-06-29 05:32:481507 *success = value.get();
1508 if (*success)
1509 *cache_entry = *value;
1510}
1511
[email protected]01ba15f72012-06-09 00:41:051512// static
1513FilePath GDataCache::GetCacheRootPath(Profile* profile) {
1514 FilePath cache_base_path;
1515 chrome::GetUserCacheDirectory(profile->GetPath(), &cache_base_path);
1516 FilePath cache_root_path =
1517 cache_base_path.Append(chrome::kGDataCacheDirname);
1518 return cache_root_path.Append(kGDataCacheVersionDir);
1519}
1520
[email protected]30d9dda2012-06-30 05:56:281521// static
1522std::vector<FilePath> GDataCache::GetCachePaths(
1523 const FilePath& cache_root_path) {
1524 std::vector<FilePath> cache_paths;
1525 // The order should match GDataCache::CacheSubDirectoryType enum.
1526 cache_paths.push_back(cache_root_path.Append(kGDataCacheMetaDir));
1527 cache_paths.push_back(cache_root_path.Append(kGDataCachePinnedDir));
1528 cache_paths.push_back(cache_root_path.Append(kGDataCacheOutgoingDir));
1529 cache_paths.push_back(cache_root_path.Append(kGDataCachePersistentDir));
1530 cache_paths.push_back(cache_root_path.Append(kGDataCacheTmpDir));
1531 cache_paths.push_back(cache_root_path.Append(kGDataCacheTmpDownloadsDir));
1532 cache_paths.push_back(cache_root_path.Append(kGDataCacheTmpDocumentsDir));
1533 return cache_paths;
1534}
1535
1536// static
1537bool GDataCache::CreateCacheDirectories(
1538 const std::vector<FilePath>& paths_to_create) {
1539 bool success = true;
1540
1541 for (size_t i = 0; i < paths_to_create.size(); ++i) {
1542 if (file_util::DirectoryExists(paths_to_create[i]))
1543 continue;
1544
1545 if (!file_util::CreateDirectory(paths_to_create[i])) {
1546 // Error creating this directory, record error and proceed with next one.
1547 success = false;
1548 PLOG(ERROR) << "Error creating directory " << paths_to_create[i].value();
1549 } else {
1550 DVLOG(1) << "Created directory " << paths_to_create[i].value();
1551 }
1552 }
1553 return success;
1554}
1555
[email protected]fae353a2012-07-11 23:30:271556// static
1557GDataCache::CacheSubDirectoryType GDataCache::GetSubDirectoryType(
1558 const GDataCacheEntry& cache_entry) {
1559 return cache_entry.IsPersistent() ? CACHE_TYPE_PERSISTENT : CACHE_TYPE_TMP;
1560}
1561
[email protected]a321b9632012-06-14 03:29:171562void SetFreeDiskSpaceGetterForTesting(FreeDiskSpaceGetterInterface* getter) {
1563 delete global_free_disk_getter_for_testing; // Safe to delete NULL;
1564 global_free_disk_getter_for_testing = getter;
1565}
1566
[email protected]3653146a2012-05-29 13:41:471567} // namespace gdata