blob: 98405106a5686f716896da7fea2daefcd61ffe31 [file] [log] [blame]
[email protected]3653146a2012-05-29 13:41:471// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/chromeos/gdata/gdata_cache.h"
6
7#include <vector>
8
[email protected]a321b9632012-06-14 03:29:179#include "base/chromeos/chromeos_version.h"
10#include "base/file_util.h"
[email protected]3653146a2012-05-29 13:41:4711#include "base/logging.h"
[email protected]774727282012-08-06 09:14:4412#include "base/path_service.h"
[email protected]3653146a2012-05-29 13:41:4713#include "base/stringprintf.h"
14#include "base/string_util.h"
[email protected]a321b9632012-06-14 03:29:1715#include "base/sys_info.h"
[email protected]28a64092012-08-21 10:01:1216#include "chrome/browser/chromeos/gdata/drive.pb.h"
[email protected]ca5f6da2012-06-18 12:54:5917#include "chrome/browser/chromeos/gdata/gdata_cache_metadata.h"
[email protected]32a7fc852012-06-08 17:25:5018#include "chrome/browser/chromeos/gdata/gdata_util.h"
[email protected]01ba15f72012-06-09 00:41:0519#include "chrome/browser/profiles/profile.h"
20#include "chrome/common/chrome_constants.h"
21#include "chrome/common/chrome_paths_internal.h"
[email protected]7986b8d2012-06-14 15:05:1422#include "content/public/browser/browser_thread.h"
23
24using content::BrowserThread;
[email protected]3653146a2012-05-29 13:41:4725
26namespace gdata {
27namespace {
28
[email protected]01ba15f72012-06-09 00:41:0529const FilePath::CharType kGDataCacheVersionDir[] = FILE_PATH_LITERAL("v1");
[email protected]32a7fc852012-06-08 17:25:5030const FilePath::CharType kGDataCacheMetaDir[] = FILE_PATH_LITERAL("meta");
31const FilePath::CharType kGDataCachePinnedDir[] = FILE_PATH_LITERAL("pinned");
32const FilePath::CharType kGDataCacheOutgoingDir[] =
33 FILE_PATH_LITERAL("outgoing");
34const FilePath::CharType kGDataCachePersistentDir[] =
35 FILE_PATH_LITERAL("persistent");
36const FilePath::CharType kGDataCacheTmpDir[] = FILE_PATH_LITERAL("tmp");
37const FilePath::CharType kGDataCacheTmpDownloadsDir[] =
38 FILE_PATH_LITERAL("tmp/downloads");
39const FilePath::CharType kGDataCacheTmpDocumentsDir[] =
40 FILE_PATH_LITERAL("tmp/documents");
41
[email protected]a321b9632012-06-14 03:29:1742// Used to tweak GetAmountOfFreeDiskSpace() behavior for testing.
43FreeDiskSpaceGetterInterface* global_free_disk_getter_for_testing = NULL;
44
45// Gets the amount of free disk space. Use
46// |global_free_disk_getter_for_testing| if set.
47int64 GetAmountOfFreeDiskSpace() {
48 if (global_free_disk_getter_for_testing)
49 return global_free_disk_getter_for_testing->AmountOfFreeDiskSpace();
50
[email protected]774727282012-08-06 09:14:4451 FilePath path;
52 if (!PathService::Get(base::DIR_HOME, &path)) {
53 LOG(ERROR) << "Home directory not found";
54 return -1;
55 }
56 return base::SysInfo::AmountOfFreeDiskSpace(path);
[email protected]a321b9632012-06-14 03:29:1757}
58
59// Returns true if we have sufficient space to store the given number of
60// bytes, while keeping kMinFreeSpace bytes on the disk.
61bool HasEnoughSpaceFor(int64 num_bytes) {
62 int64 free_space = GetAmountOfFreeDiskSpace();
[email protected]d7754f52012-08-01 08:45:3363 // Subtract this as if this portion does not exist.
[email protected]a321b9632012-06-14 03:29:1764 free_space -= kMinFreeSpace;
65 return (free_space >= num_bytes);
66}
67
[email protected]79c3752d2012-07-17 12:10:0868// Create cache directory paths and set permissions.
69void InitCachePaths(const std::vector<FilePath>& cache_paths) {
70 if (cache_paths.size() < GDataCache::NUM_CACHE_TYPES) {
71 NOTREACHED();
72 LOG(ERROR) << "Size of cache_paths is invalid.";
73 return;
74 }
75
76 if (!GDataCache::CreateCacheDirectories(cache_paths))
77 return;
78
79 // Change permissions of cache persistent directory to u+rwx,og+x (711) in
80 // order to allow archive files in that directory to be mounted by cros-disks.
81 file_util::SetPosixFilePermissions(
82 cache_paths[GDataCache::CACHE_TYPE_PERSISTENT],
83 file_util::FILE_PERMISSION_USER_MASK |
84 file_util::FILE_PERMISSION_EXECUTE_BY_GROUP |
85 file_util::FILE_PERMISSION_EXECUTE_BY_OTHERS);
86}
87
[email protected]a321b9632012-06-14 03:29:1788// Remove all files under the given directory, non-recursively.
[email protected]d7754f52012-08-01 08:45:3389// Do not remove recursively as we don't want to touch <gcache>/tmp/downloads,
[email protected]a321b9632012-06-14 03:29:1790// which is used for user initiated downloads like "Save As"
91void RemoveAllFiles(const FilePath& directory) {
92 using file_util::FileEnumerator;
93
94 FileEnumerator enumerator(directory, false /* recursive */,
95 FileEnumerator::FILES);
96 for (FilePath file_path = enumerator.Next(); !file_path.empty();
97 file_path = enumerator.Next()) {
98 DVLOG(1) << "Removing " << file_path.value();
99 if (!file_util::Delete(file_path, false /* recursive */))
100 LOG(WARNING) << "Failed to delete " << file_path.value();
101 }
102}
103
[email protected]a321b9632012-06-14 03:29:17104// Modifies cache state of file on blocking pool, which involves:
105// - moving or copying file (per |file_operation_type|) from |source_path| to
106// |dest_path| if they're different
107// - deleting symlink if |symlink_path| is not empty
108// - creating symlink if |symlink_path| is not empty and |create_symlink| is
109// true.
[email protected]7f90e77c2012-07-17 09:35:31110GDataFileError ModifyCacheState(
[email protected]a321b9632012-06-14 03:29:17111 const FilePath& source_path,
112 const FilePath& dest_path,
113 GDataCache::FileOperationType file_operation_type,
114 const FilePath& symlink_path,
115 bool create_symlink) {
116 // Move or copy |source_path| to |dest_path| if they are different.
117 if (source_path != dest_path) {
118 bool success = false;
119 if (file_operation_type == GDataCache::FILE_OPERATION_MOVE)
120 success = file_util::Move(source_path, dest_path);
121 else if (file_operation_type == GDataCache::FILE_OPERATION_COPY)
122 success = file_util::CopyFile(source_path, dest_path);
123 if (!success) {
[email protected]750af1d2012-07-13 14:32:43124 LOG(ERROR) << "Failed to "
125 << (file_operation_type == GDataCache::FILE_OPERATION_MOVE ?
126 "move " : "copy ")
127 << source_path.value()
128 << " to " << dest_path.value();
[email protected]7f90e77c2012-07-17 09:35:31129 return GDATA_FILE_ERROR_FAILED;
[email protected]a321b9632012-06-14 03:29:17130 } else {
131 DVLOG(1) << (file_operation_type == GDataCache::FILE_OPERATION_MOVE ?
132 "Moved " : "Copied ")
133 << source_path.value()
134 << " to " << dest_path.value();
135 }
136 } else {
137 DVLOG(1) << "No need to move file: source = destination";
138 }
139
140 if (symlink_path.empty())
[email protected]7f90e77c2012-07-17 09:35:31141 return GDATA_FILE_OK;
[email protected]a321b9632012-06-14 03:29:17142
143 // Remove symlink regardless of |create_symlink| because creating a link will
144 // not overwrite an existing one.
[email protected]a321b9632012-06-14 03:29:17145 // We try to save one file operation by not checking if link exists before
146 // deleting it, so unlink may return error if link doesn't exist, but it
147 // doesn't really matter to us.
[email protected]750af1d2012-07-13 14:32:43148 file_util::Delete(symlink_path, false);
[email protected]a321b9632012-06-14 03:29:17149
150 if (!create_symlink)
[email protected]7f90e77c2012-07-17 09:35:31151 return GDATA_FILE_OK;
[email protected]a321b9632012-06-14 03:29:17152
153 // Create new symlink to |dest_path|.
154 if (!file_util::CreateSymbolicLink(dest_path, symlink_path)) {
[email protected]750af1d2012-07-13 14:32:43155 LOG(ERROR) << "Failed to create a symlink from " << symlink_path.value()
156 << " to " << dest_path.value();
[email protected]7f90e77c2012-07-17 09:35:31157 return GDATA_FILE_ERROR_FAILED;
[email protected]a321b9632012-06-14 03:29:17158 }
159
[email protected]7f90e77c2012-07-17 09:35:31160 return GDATA_FILE_OK;
[email protected]a321b9632012-06-14 03:29:17161}
162
163// Deletes all files that match |path_to_delete_pattern| except for
164// |path_to_keep| on blocking pool.
165// If |path_to_keep| is empty, all files in |path_to_delete_pattern| are
166// deleted.
167void DeleteFilesSelectively(const FilePath& path_to_delete_pattern,
168 const FilePath& path_to_keep) {
169 // Enumerate all files in directory of |path_to_delete_pattern| that match
170 // base name of |path_to_delete_pattern|.
171 // If a file is not |path_to_keep|, delete it.
172 bool success = true;
[email protected]84c3f162012-08-12 01:57:23173 file_util::FileEnumerator enumerator(path_to_delete_pattern.DirName(),
[email protected]a321b9632012-06-14 03:29:17174 false, // not recursive
[email protected]84c3f162012-08-12 01:57:23175 file_util::FileEnumerator::FILES |
176 file_util::FileEnumerator::SHOW_SYM_LINKS,
[email protected]a321b9632012-06-14 03:29:17177 path_to_delete_pattern.BaseName().value());
178 for (FilePath current = enumerator.Next(); !current.empty();
179 current = enumerator.Next()) {
180 // If |path_to_keep| is not empty and same as current, don't delete it.
181 if (!path_to_keep.empty() && current == path_to_keep)
182 continue;
183
[email protected]0b8d4cee2012-07-02 20:46:26184 success = file_util::Delete(current, false);
[email protected]a321b9632012-06-14 03:29:17185 if (!success)
186 DVLOG(1) << "Error deleting " << current.value();
187 else
188 DVLOG(1) << "Deleted " << current.value();
189 }
190}
191
[email protected]b83e5202012-06-27 07:50:24192// Appends |resource_id| ID to |to_fetch| if the file is pinned but not
193// fetched (not present locally), or to |to_upload| if the file is dirty
194// but not uploaded.
195void CollectBacklog(std::vector<std::string>* to_fetch,
196 std::vector<std::string>* to_upload,
197 const std::string& resource_id,
[email protected]28a64092012-08-21 10:01:12198 const DriveCacheEntry& cache_entry) {
[email protected]b83e5202012-06-27 07:50:24199 DCHECK(to_fetch);
200 DCHECK(to_upload);
[email protected]8764a392012-06-20 06:43:08201
[email protected]02821102012-07-12 20:19:17202 if (cache_entry.is_pinned() && !cache_entry.is_present())
[email protected]b83e5202012-06-27 07:50:24203 to_fetch->push_back(resource_id);
204
[email protected]02821102012-07-12 20:19:17205 if (cache_entry.is_dirty())
[email protected]b83e5202012-06-27 07:50:24206 to_upload->push_back(resource_id);
[email protected]8764a392012-06-20 06:43:08207}
208
[email protected]85b62192012-06-29 19:56:38209// Appends |resource_id| ID to |resource_ids| if the file is pinned and
210// present (cached locally).
211void CollectExistingPinnedFile(std::vector<std::string>* resource_ids,
212 const std::string& resource_id,
[email protected]28a64092012-08-21 10:01:12213 const DriveCacheEntry& cache_entry) {
[email protected]85b62192012-06-29 19:56:38214 DCHECK(resource_ids);
215
[email protected]02821102012-07-12 20:19:17216 if (cache_entry.is_pinned() && cache_entry.is_present())
[email protected]85b62192012-06-29 19:56:38217 resource_ids->push_back(resource_id);
218}
[email protected]8764a392012-06-20 06:43:08219
[email protected]cd236432012-07-27 18:03:30220// Appends |resource_id| ID to |resource_ids| unconditionally.
221void CollectAnyFile(std::vector<std::string>* resource_ids,
222 const std::string& resource_id,
[email protected]28a64092012-08-21 10:01:12223 const DriveCacheEntry& /* cache_entry */) {
[email protected]cd236432012-07-27 18:03:30224 DCHECK(resource_ids);
225
226 resource_ids->push_back(resource_id);
227}
228
[email protected]7986b8d2012-06-14 15:05:14229// Runs callback with pointers dereferenced.
[email protected]f861b392012-08-03 20:41:12230// Used to implement SetMountedStateOnUIThread and ClearAllOnUIThread.
231void RunChangeCacheStateCallback(const ChangeCacheStateCallback& callback,
232 const GDataFileError* error,
233 const FilePath* cache_file_path) {
[email protected]7986b8d2012-06-14 15:05:14234 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
235 DCHECK(error);
236 DCHECK(cache_file_path);
237
238 if (!callback.is_null())
239 callback.Run(*error, *cache_file_path);
240}
241
[email protected]73f9c742012-06-15 07:37:13242// Runs callback with pointers dereferenced.
243// Used to implement *OnUIThread methods.
244void RunCacheOperationCallback(const CacheOperationCallback& callback,
[email protected]7f90e77c2012-07-17 09:35:31245 GDataFileError* error,
[email protected]73f9c742012-06-15 07:37:13246 const std::string& resource_id,
247 const std::string& md5) {
248 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
249 DCHECK(error);
250
251 if (!callback.is_null())
252 callback.Run(*error, resource_id, md5);
253}
254
[email protected]c960d222012-06-15 10:03:50255// Runs callback with pointers dereferenced.
256// Used to implement *OnUIThread methods.
257void RunGetFileFromCacheCallback(const GetFileFromCacheCallback& callback,
[email protected]7f90e77c2012-07-17 09:35:31258 GDataFileError* error,
[email protected]c960d222012-06-15 10:03:50259 const std::string& resource_id,
260 const std::string& md5,
261 FilePath* cache_file_path) {
262 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
263 DCHECK(error);
264 DCHECK(cache_file_path);
265
266 if (!callback.is_null())
267 callback.Run(*error, resource_id, md5, *cache_file_path);
268}
269
[email protected]8764a392012-06-20 06:43:08270// Runs callback with pointers dereferenced.
[email protected]4324fdc2012-06-29 05:32:48271// Used to implement GetResourceIdsOfBacklogOnUIThread().
[email protected]85b62192012-06-29 19:56:38272void RunGetResourceIdsOfBacklogCallback(
273 const GetResourceIdsOfBacklogCallback& callback,
274 std::vector<std::string>* to_fetch,
275 std::vector<std::string>* to_upload) {
[email protected]8764a392012-06-20 06:43:08276 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]b83e5202012-06-27 07:50:24277 DCHECK(to_fetch);
278 DCHECK(to_upload);
[email protected]8764a392012-06-20 06:43:08279
280 if (!callback.is_null())
[email protected]b83e5202012-06-27 07:50:24281 callback.Run(*to_fetch, *to_upload);
[email protected]8764a392012-06-20 06:43:08282}
283
[email protected]4324fdc2012-06-29 05:32:48284// Runs callback with pointers dereferenced.
[email protected]85b62192012-06-29 19:56:38285// Used to implement GetResourceIdsOfExistingPinnedFilesOnUIThread().
286void RunGetResourceIdsCallback(
287 const GetResourceIdsCallback& callback,
288 std::vector<std::string>* resource_ids) {
289 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
290 DCHECK(resource_ids);
291
292 if (!callback.is_null())
293 callback.Run(*resource_ids);
294}
295
296// Runs callback with pointers dereferenced.
[email protected]4324fdc2012-06-29 05:32:48297// Used to implement GetCacheEntryOnUIThread().
298void RunGetCacheEntryCallback(
[email protected]fae353a2012-07-11 23:30:27299 const GetCacheEntryCallback& callback,
[email protected]4324fdc2012-06-29 05:32:48300 bool* success,
[email protected]28a64092012-08-21 10:01:12301 DriveCacheEntry* cache_entry) {
[email protected]4324fdc2012-06-29 05:32:48302 DCHECK(success);
303 DCHECK(cache_entry);
304
305 if (!callback.is_null())
306 callback.Run(*success, *cache_entry);
307}
308
[email protected]a321b9632012-06-14 03:29:17309} // namespace
[email protected]32a7fc852012-06-08 17:25:50310
[email protected]ddbf2052012-07-13 15:07:02311GDataCache::GDataCache(const FilePath& cache_root_path,
312 base::SequencedTaskRunner* blocking_task_runner)
[email protected]01ba15f72012-06-09 00:41:05313 : cache_root_path_(cache_root_path),
[email protected]6b70c7b2012-06-14 03:10:43314 cache_paths_(GetCachePaths(cache_root_path_)),
[email protected]ddbf2052012-07-13 15:07:02315 blocking_task_runner_(blocking_task_runner),
[email protected]e53ac8f2012-08-02 07:05:00316 weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
[email protected]73f9c742012-06-15 07:37:13317 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]3653146a2012-05-29 13:41:47318}
319
320GDataCache::~GDataCache() {
[email protected]73f9c742012-06-15 07:37:13321 AssertOnSequencedWorkerPool();
[email protected]3653146a2012-05-29 13:41:47322}
323
[email protected]32a7fc852012-06-08 17:25:50324FilePath GDataCache::GetCacheDirectoryPath(
325 CacheSubDirectoryType sub_dir_type) const {
326 DCHECK_LE(0, sub_dir_type);
327 DCHECK_GT(NUM_CACHE_TYPES, sub_dir_type);
328 return cache_paths_[sub_dir_type];
329}
330
331FilePath GDataCache::GetCacheFilePath(const std::string& resource_id,
332 const std::string& md5,
333 CacheSubDirectoryType sub_dir_type,
334 CachedFileOrigin file_origin) const {
335 DCHECK(sub_dir_type != CACHE_TYPE_META);
336
337 // Runs on any thread.
338 // Filename is formatted as resource_id.md5, i.e. resource_id is the base
339 // name and md5 is the extension.
340 std::string base_name = util::EscapeCacheFileName(resource_id);
341 if (file_origin == CACHED_FILE_LOCALLY_MODIFIED) {
342 DCHECK(sub_dir_type == CACHE_TYPE_PERSISTENT);
343 base_name += FilePath::kExtensionSeparator;
[email protected]b83e5202012-06-27 07:50:24344 base_name += util::kLocallyModifiedFileExtension;
[email protected]32a7fc852012-06-08 17:25:50345 } else if (!md5.empty()) {
346 base_name += FilePath::kExtensionSeparator;
347 base_name += util::EscapeCacheFileName(md5);
348 }
349 // For mounted archives the filename is formatted as resource_id.md5.mounted,
350 // i.e. resource_id.md5 is the base name and ".mounted" is the extension
351 if (file_origin == CACHED_FILE_MOUNTED) {
[email protected]a321b9632012-06-14 03:29:17352 DCHECK(sub_dir_type == CACHE_TYPE_PERSISTENT);
353 base_name += FilePath::kExtensionSeparator;
[email protected]ca5f6da2012-06-18 12:54:59354 base_name += util::kMountedArchiveFileExtension;
[email protected]32a7fc852012-06-08 17:25:50355 }
356 return GetCacheDirectoryPath(sub_dir_type).Append(base_name);
357}
358
[email protected]fcc92a52012-06-08 22:54:16359void GDataCache::AssertOnSequencedWorkerPool() {
[email protected]ddbf2052012-07-13 15:07:02360 DCHECK(!blocking_task_runner_ ||
361 blocking_task_runner_->RunsTasksOnCurrentThread());
[email protected]fcc92a52012-06-08 22:54:16362}
363
[email protected]01ba15f72012-06-09 00:41:05364bool GDataCache::IsUnderGDataCacheDirectory(const FilePath& path) const {
365 return cache_root_path_ == path || cache_root_path_.IsParent(path);
366}
367
[email protected]73f9c742012-06-15 07:37:13368void GDataCache::AddObserver(Observer* observer) {
369 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
370 observers_.AddObserver(observer);
371}
372
373void GDataCache::RemoveObserver(Observer* observer) {
374 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
375 observers_.RemoveObserver(observer);
376}
377
[email protected]4324fdc2012-06-29 05:32:48378void GDataCache::GetCacheEntryOnUIThread(
379 const std::string& resource_id,
380 const std::string& md5,
381 const GetCacheEntryCallback& callback) {
382 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
383
384 bool* success = new bool(false);
[email protected]28a64092012-08-21 10:01:12385 DriveCacheEntry* cache_entry = new DriveCacheEntry;
[email protected]ddbf2052012-07-13 15:07:02386 blocking_task_runner_->PostTaskAndReply(
[email protected]4324fdc2012-06-29 05:32:48387 FROM_HERE,
388 base::Bind(&GDataCache::GetCacheEntryHelper,
389 base::Unretained(this),
390 resource_id,
391 md5,
392 success,
393 cache_entry),
394 base::Bind(&RunGetCacheEntryCallback,
395 callback,
396 base::Owned(success),
397 base::Owned(cache_entry)));
398}
399
[email protected]b83e5202012-06-27 07:50:24400void GDataCache::GetResourceIdsOfBacklogOnUIThread(
[email protected]85b62192012-06-29 19:56:38401 const GetResourceIdsOfBacklogCallback& callback) {
[email protected]8764a392012-06-20 06:43:08402 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
403
[email protected]b83e5202012-06-27 07:50:24404 std::vector<std::string>* to_fetch = new std::vector<std::string>;
405 std::vector<std::string>* to_upload = new std::vector<std::string>;
[email protected]ddbf2052012-07-13 15:07:02406 blocking_task_runner_->PostTaskAndReply(
[email protected]8764a392012-06-20 06:43:08407 FROM_HERE,
[email protected]b83e5202012-06-27 07:50:24408 base::Bind(&GDataCache::GetResourceIdsOfBacklog,
[email protected]8764a392012-06-20 06:43:08409 base::Unretained(this),
[email protected]b83e5202012-06-27 07:50:24410 to_fetch,
411 to_upload),
[email protected]85b62192012-06-29 19:56:38412 base::Bind(&RunGetResourceIdsOfBacklogCallback,
[email protected]8764a392012-06-20 06:43:08413 callback,
[email protected]b83e5202012-06-27 07:50:24414 base::Owned(to_fetch),
415 base::Owned(to_upload)));
[email protected]8764a392012-06-20 06:43:08416}
417
[email protected]85b62192012-06-29 19:56:38418void GDataCache::GetResourceIdsOfExistingPinnedFilesOnUIThread(
419 const GetResourceIdsCallback& callback) {
420 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
421
422 std::vector<std::string>* resource_ids = new std::vector<std::string>;
[email protected]ddbf2052012-07-13 15:07:02423 blocking_task_runner_->PostTaskAndReply(
[email protected]85b62192012-06-29 19:56:38424 FROM_HERE,
425 base::Bind(&GDataCache::GetResourceIdsOfExistingPinnedFiles,
426 base::Unretained(this),
427 resource_ids),
428 base::Bind(&RunGetResourceIdsCallback,
429 callback,
430 base::Owned(resource_ids)));
431}
432
[email protected]cd236432012-07-27 18:03:30433void GDataCache::GetResourceIdsOfAllFilesOnUIThread(
434 const GetResourceIdsCallback& callback) {
435 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
436
437 std::vector<std::string>* resource_ids = new std::vector<std::string>;
438 blocking_task_runner_->PostTaskAndReply(
439 FROM_HERE,
440 base::Bind(&GDataCache::GetResourceIdsOfAllFiles,
441 base::Unretained(this),
442 resource_ids),
443 base::Bind(&RunGetResourceIdsCallback,
444 callback,
445 base::Owned(resource_ids)));
446}
447
[email protected]a321b9632012-06-14 03:29:17448void GDataCache::FreeDiskSpaceIfNeededFor(int64 num_bytes,
449 bool* has_enough_space) {
450 AssertOnSequencedWorkerPool();
451
452 // Do nothing and return if we have enough space.
453 *has_enough_space = HasEnoughSpaceFor(num_bytes);
454 if (*has_enough_space)
455 return;
456
457 // Otherwise, try to free up the disk space.
458 DVLOG(1) << "Freeing up disk space for " << num_bytes;
459 // First remove temporary files from the cache map.
[email protected]ca5f6da2012-06-18 12:54:59460 metadata_->RemoveTemporaryFiles();
[email protected]a321b9632012-06-14 03:29:17461 // Then remove all files under "tmp" directory.
462 RemoveAllFiles(GetCacheDirectoryPath(GDataCache::CACHE_TYPE_TMP));
463
464 // Check the disk space again.
465 *has_enough_space = HasEnoughSpaceFor(num_bytes);
466}
467
[email protected]c960d222012-06-15 10:03:50468void GDataCache::GetFileOnUIThread(const std::string& resource_id,
469 const std::string& md5,
470 const GetFileFromCacheCallback& callback) {
471 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]a321b9632012-06-14 03:29:17472
[email protected]7f90e77c2012-07-17 09:35:31473 GDataFileError* error =
474 new GDataFileError(GDATA_FILE_OK);
[email protected]c960d222012-06-15 10:03:50475 FilePath* cache_file_path = new FilePath;
[email protected]ddbf2052012-07-13 15:07:02476 blocking_task_runner_->PostTaskAndReply(
[email protected]c960d222012-06-15 10:03:50477 FROM_HERE,
478 base::Bind(&GDataCache::GetFile,
479 base::Unretained(this),
480 resource_id,
481 md5,
482 error,
483 cache_file_path),
484 base::Bind(&RunGetFileFromCacheCallback,
485 callback,
486 base::Owned(error),
487 resource_id,
488 md5,
489 base::Owned(cache_file_path)));
[email protected]a321b9632012-06-14 03:29:17490}
491
[email protected]73f9c742012-06-15 07:37:13492void GDataCache::StoreOnUIThread(const std::string& resource_id,
493 const std::string& md5,
494 const FilePath& source_path,
495 FileOperationType file_operation_type,
496 const CacheOperationCallback& callback) {
497 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
498
[email protected]7f90e77c2012-07-17 09:35:31499 GDataFileError* error =
500 new GDataFileError(GDATA_FILE_OK);
[email protected]ddbf2052012-07-13 15:07:02501 blocking_task_runner_->PostTaskAndReply(
[email protected]73f9c742012-06-15 07:37:13502 FROM_HERE,
503 base::Bind(&GDataCache::Store,
504 base::Unretained(this),
505 resource_id,
506 md5,
507 source_path,
508 file_operation_type,
509 error),
510 base::Bind(&RunCacheOperationCallback,
511 callback,
512 base::Owned(error),
513 resource_id,
514 md5));
515}
516
517void GDataCache::PinOnUIThread(const std::string& resource_id,
[email protected]44c0584e2012-06-15 23:55:41518 const std::string& md5,
519 const CacheOperationCallback& callback) {
[email protected]73f9c742012-06-15 07:37:13520 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
521
[email protected]7f90e77c2012-07-17 09:35:31522 GDataFileError* error =
523 new GDataFileError(GDATA_FILE_OK);
[email protected]ddbf2052012-07-13 15:07:02524 blocking_task_runner_->PostTaskAndReply(
[email protected]73f9c742012-06-15 07:37:13525 FROM_HERE,
526 base::Bind(&GDataCache::Pin,
527 base::Unretained(this),
528 resource_id,
529 md5,
530 GDataCache::FILE_OPERATION_MOVE,
531 error),
532 base::Bind(&GDataCache::OnPinned,
[email protected]e53ac8f2012-08-02 07:05:00533 weak_ptr_factory_.GetWeakPtr(),
[email protected]73f9c742012-06-15 07:37:13534 base::Owned(error),
535 resource_id,
536 md5,
537 callback));
538}
539
540void GDataCache::UnpinOnUIThread(const std::string& resource_id,
541 const std::string& md5,
542 const CacheOperationCallback& callback) {
543 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]7f90e77c2012-07-17 09:35:31544 GDataFileError* error =
545 new GDataFileError(GDATA_FILE_OK);
[email protected]ddbf2052012-07-13 15:07:02546 blocking_task_runner_->PostTaskAndReply(
[email protected]73f9c742012-06-15 07:37:13547 FROM_HERE,
548 base::Bind(&GDataCache::Unpin,
549 base::Unretained(this),
550 resource_id,
551 md5,
552 GDataCache::FILE_OPERATION_MOVE,
553 error),
554 base::Bind(&GDataCache::OnUnpinned,
[email protected]e53ac8f2012-08-02 07:05:00555 weak_ptr_factory_.GetWeakPtr(),
[email protected]73f9c742012-06-15 07:37:13556 base::Owned(error),
557 resource_id,
558 md5,
559 callback));
560}
561
562void GDataCache::SetMountedStateOnUIThread(
563 const FilePath& file_path,
564 bool to_mount,
[email protected]f861b392012-08-03 20:41:12565 const ChangeCacheStateCallback& callback) {
[email protected]73f9c742012-06-15 07:37:13566 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
567
[email protected]7f90e77c2012-07-17 09:35:31568 GDataFileError* error =
569 new GDataFileError(GDATA_FILE_OK);
[email protected]73f9c742012-06-15 07:37:13570 FilePath* cache_file_path = new FilePath;
[email protected]ddbf2052012-07-13 15:07:02571 blocking_task_runner_->PostTaskAndReply(
[email protected]73f9c742012-06-15 07:37:13572 FROM_HERE,
573 base::Bind(&GDataCache::SetMountedState,
574 base::Unretained(this),
575 file_path,
576 to_mount,
577 error,
578 cache_file_path),
[email protected]f861b392012-08-03 20:41:12579 base::Bind(&RunChangeCacheStateCallback,
[email protected]73f9c742012-06-15 07:37:13580 callback,
581 base::Owned(error),
582 base::Owned(cache_file_path)));
583}
584
[email protected]c960d222012-06-15 10:03:50585void GDataCache::MarkDirtyOnUIThread(const std::string& resource_id,
586 const std::string& md5,
587 const GetFileFromCacheCallback& callback) {
588 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]73f9c742012-06-15 07:37:13589
[email protected]7f90e77c2012-07-17 09:35:31590 GDataFileError* error =
591 new GDataFileError(GDATA_FILE_OK);
[email protected]c960d222012-06-15 10:03:50592 FilePath* cache_file_path = new FilePath;
[email protected]ddbf2052012-07-13 15:07:02593 blocking_task_runner_->PostTaskAndReply(
[email protected]c960d222012-06-15 10:03:50594 FROM_HERE,
595 base::Bind(&GDataCache::MarkDirty,
596 base::Unretained(this),
597 resource_id,
598 md5,
599 GDataCache::FILE_OPERATION_MOVE,
600 error,
601 cache_file_path),
602 base::Bind(&RunGetFileFromCacheCallback,
603 callback,
604 base::Owned(error),
605 resource_id,
606 md5,
607 base::Owned(cache_file_path)));
[email protected]73f9c742012-06-15 07:37:13608}
609
610void GDataCache::CommitDirtyOnUIThread(const std::string& resource_id,
611 const std::string& md5,
612 const CacheOperationCallback& callback) {
613 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
614
[email protected]7f90e77c2012-07-17 09:35:31615 GDataFileError* error = new GDataFileError(GDATA_FILE_OK);
[email protected]ddbf2052012-07-13 15:07:02616 blocking_task_runner_->PostTaskAndReply(
[email protected]73f9c742012-06-15 07:37:13617 FROM_HERE,
618 base::Bind(&GDataCache::CommitDirty,
619 base::Unretained(this),
620 resource_id,
621 md5,
622 GDataCache::FILE_OPERATION_MOVE,
623 error),
[email protected]d7664c22012-06-18 19:35:49624 base::Bind(&GDataCache::OnCommitDirty,
[email protected]e53ac8f2012-08-02 07:05:00625 weak_ptr_factory_.GetWeakPtr(),
[email protected]73f9c742012-06-15 07:37:13626 base::Owned(error),
627 resource_id,
[email protected]d7664c22012-06-18 19:35:49628 md5,
629 callback));
[email protected]73f9c742012-06-15 07:37:13630}
631
632void GDataCache::ClearDirtyOnUIThread(const std::string& resource_id,
633 const std::string& md5,
634 const CacheOperationCallback& callback) {
635 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
636
[email protected]7f90e77c2012-07-17 09:35:31637 GDataFileError* error =
638 new GDataFileError(GDATA_FILE_OK);
[email protected]ddbf2052012-07-13 15:07:02639 blocking_task_runner_->PostTaskAndReply(
[email protected]73f9c742012-06-15 07:37:13640 FROM_HERE,
641 base::Bind(&GDataCache::ClearDirty,
642 base::Unretained(this),
643 resource_id,
644 md5,
645 GDataCache::FILE_OPERATION_MOVE,
646 error),
647 base::Bind(&RunCacheOperationCallback,
648 callback,
649 base::Owned(error),
650 resource_id,
651 md5));
652}
653
654void GDataCache::RemoveOnUIThread(const std::string& resource_id,
655 const CacheOperationCallback& callback) {
656 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
657
[email protected]7f90e77c2012-07-17 09:35:31658 GDataFileError* error =
659 new GDataFileError(GDATA_FILE_OK);
[email protected]73f9c742012-06-15 07:37:13660
[email protected]ddbf2052012-07-13 15:07:02661 blocking_task_runner_->PostTaskAndReply(
[email protected]73f9c742012-06-15 07:37:13662 FROM_HERE,
663 base::Bind(&GDataCache::Remove,
664 base::Unretained(this),
665 resource_id,
666 error),
667 base::Bind(&RunCacheOperationCallback,
668 callback,
669 base::Owned(error),
670 resource_id,
671 "" /* md5 */));
672}
673
[email protected]f861b392012-08-03 20:41:12674void GDataCache::ClearAllOnUIThread(const ChangeCacheStateCallback& callback) {
675 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
676
677 GDataFileError* error = new GDataFileError(GDATA_FILE_OK);
678
679 blocking_task_runner_->PostTaskAndReply(
680 FROM_HERE,
681 base::Bind(&GDataCache::ClearAll,
682 base::Unretained(this),
683 error),
684 base::Bind(&RunChangeCacheStateCallback,
685 callback,
686 base::Owned(error),
687 &cache_root_path_));
688}
689
[email protected]73f9c742012-06-15 07:37:13690void GDataCache::RequestInitializeOnUIThread() {
691 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
692
[email protected]ddbf2052012-07-13 15:07:02693 blocking_task_runner_->PostTask(
[email protected]73f9c742012-06-15 07:37:13694 FROM_HERE,
[email protected]1ff88ea52012-06-20 04:54:00695 base::Bind(&GDataCache::Initialize, base::Unretained(this)));
[email protected]73f9c742012-06-15 07:37:13696}
697
[email protected]d310bfc2012-08-10 09:41:28698void GDataCache::RequestInitializeOnUIThreadForTesting() {
699 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
700
701 blocking_task_runner_->PostTask(
702 FROM_HERE,
703 base::Bind(&GDataCache::InitializeForTesting, base::Unretained(this)));
704}
705
[email protected]79c3752d2012-07-17 12:10:08706void GDataCache::ForceRescanOnUIThreadForTesting() {
707 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
708
709 blocking_task_runner_->PostTask(
710 FROM_HERE,
711 base::Bind(&GDataCache::ForceRescanForTesting, base::Unretained(this)));
712}
713
[email protected]b22f87f2012-07-12 10:53:17714bool GDataCache::GetCacheEntry(const std::string& resource_id,
715 const std::string& md5,
[email protected]28a64092012-08-21 10:01:12716 DriveCacheEntry* entry) {
[email protected]b22f87f2012-07-12 10:53:17717 DCHECK(entry);
[email protected]73f9c742012-06-15 07:37:13718 AssertOnSequencedWorkerPool();
[email protected]b22f87f2012-07-12 10:53:17719 return metadata_->GetCacheEntry(resource_id, md5, entry);
[email protected]73f9c742012-06-15 07:37:13720}
721
722// static
723GDataCache* GDataCache::CreateGDataCacheOnUIThread(
724 const FilePath& cache_root_path,
[email protected]ddbf2052012-07-13 15:07:02725 base::SequencedTaskRunner* blocking_task_runner) {
[email protected]73f9c742012-06-15 07:37:13726 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]ddbf2052012-07-13 15:07:02727 return new GDataCache(cache_root_path, blocking_task_runner);
[email protected]73f9c742012-06-15 07:37:13728}
729
730void GDataCache::DestroyOnUIThread() {
731 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
732
733 // Invalidate the weak pointer.
[email protected]e53ac8f2012-08-02 07:05:00734 weak_ptr_factory_.InvalidateWeakPtrs();
[email protected]73f9c742012-06-15 07:37:13735
736 // Destroy myself on the blocking pool.
[email protected]ddbf2052012-07-13 15:07:02737 blocking_task_runner_->PostTask(
[email protected]73f9c742012-06-15 07:37:13738 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
[email protected]79c3752d2012-07-17 12:10:08746 InitCachePaths(cache_paths_);
[email protected]f9e5cc02012-07-13 20:08:56747 metadata_ = GDataCacheMetadata::CreateGDataCacheMetadata(
748 blocking_task_runner_).Pass();
749 metadata_->Initialize(cache_paths_);
[email protected]ca5f6da2012-06-18 12:54:59750}
751
[email protected]d310bfc2012-08-10 09:41:28752void GDataCache::InitializeForTesting() {
753 AssertOnSequencedWorkerPool();
754
755 InitCachePaths(cache_paths_);
756 metadata_ = GDataCacheMetadata::CreateGDataCacheMetadataForTesting(
757 blocking_task_runner_).Pass();
758 metadata_->Initialize(cache_paths_);
759}
760
[email protected]73f9c742012-06-15 07:37:13761void GDataCache::Destroy() {
762 AssertOnSequencedWorkerPool();
763 delete this;
764}
765
[email protected]79c3752d2012-07-17 12:10:08766void GDataCache::ForceRescanForTesting() {
767 AssertOnSequencedWorkerPool();
768 metadata_->ForceRescanForTesting(cache_paths_);
769}
770
[email protected]b83e5202012-06-27 07:50:24771void GDataCache::GetResourceIdsOfBacklog(
772 std::vector<std::string>* to_fetch,
773 std::vector<std::string>* to_upload) {
[email protected]8764a392012-06-20 06:43:08774 AssertOnSequencedWorkerPool();
[email protected]b83e5202012-06-27 07:50:24775 DCHECK(to_fetch);
776 DCHECK(to_upload);
[email protected]8764a392012-06-20 06:43:08777
[email protected]b83e5202012-06-27 07:50:24778 metadata_->Iterate(base::Bind(&CollectBacklog, to_fetch, to_upload));
[email protected]8764a392012-06-20 06:43:08779}
780
[email protected]85b62192012-06-29 19:56:38781void GDataCache::GetResourceIdsOfExistingPinnedFiles(
782 std::vector<std::string>* resource_ids) {
783 AssertOnSequencedWorkerPool();
784 DCHECK(resource_ids);
785
786 metadata_->Iterate(base::Bind(&CollectExistingPinnedFile, resource_ids));
787}
788
[email protected]cd236432012-07-27 18:03:30789void GDataCache::GetResourceIdsOfAllFiles(
790 std::vector<std::string>* resource_ids) {
791 AssertOnSequencedWorkerPool();
792 DCHECK(resource_ids);
793
794 metadata_->Iterate(base::Bind(&CollectAnyFile, resource_ids));
795}
796
[email protected]c960d222012-06-15 10:03:50797void GDataCache::GetFile(const std::string& resource_id,
798 const std::string& md5,
[email protected]7f90e77c2012-07-17 09:35:31799 GDataFileError* error,
[email protected]c960d222012-06-15 10:03:50800 FilePath* cache_file_path) {
801 AssertOnSequencedWorkerPool();
802 DCHECK(error);
803 DCHECK(cache_file_path);
804
[email protected]28a64092012-08-21 10:01:12805 DriveCacheEntry cache_entry;
[email protected]b22f87f2012-07-12 10:53:17806 if (GetCacheEntry(resource_id, md5, &cache_entry) &&
[email protected]02821102012-07-12 20:19:17807 cache_entry.is_present()) {
[email protected]c960d222012-06-15 10:03:50808 CachedFileOrigin file_origin;
[email protected]02821102012-07-12 20:19:17809 if (cache_entry.is_mounted()) {
[email protected]c960d222012-06-15 10:03:50810 file_origin = CACHED_FILE_MOUNTED;
[email protected]02821102012-07-12 20:19:17811 } else if (cache_entry.is_dirty()) {
[email protected]c960d222012-06-15 10:03:50812 file_origin = CACHED_FILE_LOCALLY_MODIFIED;
813 } else {
814 file_origin = CACHED_FILE_FROM_SERVER;
815 }
816 *cache_file_path = GetCacheFilePath(
817 resource_id,
818 md5,
[email protected]b22f87f2012-07-12 10:53:17819 GetSubDirectoryType(cache_entry),
[email protected]c960d222012-06-15 10:03:50820 file_origin);
[email protected]7f90e77c2012-07-17 09:35:31821 *error = GDATA_FILE_OK;
[email protected]c960d222012-06-15 10:03:50822 } else {
[email protected]7f90e77c2012-07-17 09:35:31823 *error = GDATA_FILE_ERROR_NOT_FOUND;
[email protected]c960d222012-06-15 10:03:50824 }
825}
826
[email protected]a321b9632012-06-14 03:29:17827void GDataCache::Store(const std::string& resource_id,
828 const std::string& md5,
829 const FilePath& source_path,
830 FileOperationType file_operation_type,
[email protected]7f90e77c2012-07-17 09:35:31831 GDataFileError* error) {
[email protected]a321b9632012-06-14 03:29:17832 AssertOnSequencedWorkerPool();
833 DCHECK(error);
834
[email protected]f0c67002012-08-15 04:10:38835 if (file_operation_type == FILE_OPERATION_COPY) {
836 int64 file_size;
837 if (!file_util::GetFileSize(source_path, &file_size)) {
838 LOG(WARNING) << "Couldn't get file size for: " << source_path.value();
839 *error = GDATA_FILE_ERROR_FAILED;
840 return;
841 }
842
843 bool enough_space = false;
844 FreeDiskSpaceIfNeededFor(file_size, &enough_space);
845 if (!enough_space) {
846 *error = GDATA_FILE_ERROR_NO_SPACE;
847 return;
848 }
849 }
850
[email protected]a321b9632012-06-14 03:29:17851 FilePath dest_path;
852 FilePath symlink_path;
[email protected]a321b9632012-06-14 03:29:17853 CacheSubDirectoryType sub_dir_type = CACHE_TYPE_TMP;
854
[email protected]a321b9632012-06-14 03:29:17855 // If file was previously pinned, store it in persistent dir and create
856 // symlink in pinned dir.
[email protected]28a64092012-08-21 10:01:12857 DriveCacheEntry cache_entry;
[email protected]b22f87f2012-07-12 10:53:17858 if (GetCacheEntry(resource_id, md5, &cache_entry)) { // File exists in cache.
[email protected]a321b9632012-06-14 03:29:17859 // If file is dirty or mounted, return error.
[email protected]02821102012-07-12 20:19:17860 if (cache_entry.is_dirty() || cache_entry.is_mounted()) {
[email protected]a321b9632012-06-14 03:29:17861 LOG(WARNING) << "Can't store a file to replace a "
[email protected]02821102012-07-12 20:19:17862 << (cache_entry.is_dirty() ? "dirty" : "mounted")
[email protected]a321b9632012-06-14 03:29:17863 << " file: res_id=" << resource_id
864 << ", md5=" << md5;
[email protected]7f90e77c2012-07-17 09:35:31865 *error = GDATA_FILE_ERROR_IN_USE;
[email protected]a321b9632012-06-14 03:29:17866 return;
867 }
868
[email protected]a321b9632012-06-14 03:29:17869 // If file is pinned, determines destination path.
[email protected]02821102012-07-12 20:19:17870 if (cache_entry.is_pinned()) {
[email protected]a321b9632012-06-14 03:29:17871 sub_dir_type = CACHE_TYPE_PERSISTENT;
872 dest_path = GetCacheFilePath(resource_id, md5, sub_dir_type,
873 CACHED_FILE_FROM_SERVER);
874 symlink_path = GetCacheFilePath(
875 resource_id, std::string(), CACHE_TYPE_PINNED,
876 CACHED_FILE_FROM_SERVER);
877 }
878 }
879
880 // File wasn't pinned or doesn't exist in cache, store in tmp dir.
881 if (dest_path.empty()) {
882 DCHECK_EQ(CACHE_TYPE_TMP, sub_dir_type);
883 dest_path = GetCacheFilePath(resource_id, md5, sub_dir_type,
884 CACHED_FILE_FROM_SERVER);
885 }
886
887 *error = ModifyCacheState(
888 source_path,
889 dest_path,
890 file_operation_type,
891 symlink_path,
892 !symlink_path.empty()); // create symlink
893
894 // Determine search pattern for stale filenames corrresponding to resource_id,
895 // either "<resource_id>*" or "<resource_id>.*".
896 FilePath stale_filenames_pattern;
897 if (md5.empty()) {
898 // No md5 means no extension, append '*' after base name, i.e.
899 // "<resource_id>*".
900 // Cannot call |dest_path|.ReplaceExtension when there's no md5 extension:
901 // if base name of |dest_path| (i.e. escaped resource_id) contains the
902 // extension separator '.', ReplaceExtension will remove it and everything
903 // after it. The result will be nothing like the escaped resource_id.
[email protected]ca5f6da2012-06-18 12:54:59904 stale_filenames_pattern = FilePath(dest_path.value() + util::kWildCard);
[email protected]a321b9632012-06-14 03:29:17905 } else {
906 // Replace md5 extension with '*' i.e. "<resource_id>.*".
907 // Note that ReplaceExtension automatically prefixes the extension with the
908 // extension separator '.'.
[email protected]ca5f6da2012-06-18 12:54:59909 stale_filenames_pattern = dest_path.ReplaceExtension(util::kWildCard);
[email protected]a321b9632012-06-14 03:29:17910 }
911
912 // Delete files that match |stale_filenames_pattern| except for |dest_path|.
913 DeleteFilesSelectively(stale_filenames_pattern, dest_path);
914
[email protected]7f90e77c2012-07-17 09:35:31915 if (*error == GDATA_FILE_OK) {
[email protected]a321b9632012-06-14 03:29:17916 // Now that file operations have completed, update cache map.
[email protected]b22f87f2012-07-12 10:53:17917 cache_entry.set_md5(md5);
[email protected]02821102012-07-12 20:19:17918 cache_entry.set_is_present(true);
919 cache_entry.set_is_persistent(sub_dir_type == CACHE_TYPE_PERSISTENT);
[email protected]b22f87f2012-07-12 10:53:17920 metadata_->AddOrUpdateCacheEntry(resource_id, cache_entry);
[email protected]a321b9632012-06-14 03:29:17921 }
922}
923
924void GDataCache::Pin(const std::string& resource_id,
925 const std::string& md5,
926 FileOperationType file_operation_type,
[email protected]7f90e77c2012-07-17 09:35:31927 GDataFileError* error) {
[email protected]a321b9632012-06-14 03:29:17928 AssertOnSequencedWorkerPool();
929 DCHECK(error);
930
931 FilePath source_path;
932 FilePath dest_path;
933 FilePath symlink_path;
934 bool create_symlink = true;
[email protected]a321b9632012-06-14 03:29:17935 CacheSubDirectoryType sub_dir_type = CACHE_TYPE_PERSISTENT;
936
[email protected]28a64092012-08-21 10:01:12937 DriveCacheEntry cache_entry;
[email protected]b22f87f2012-07-12 10:53:17938 if (!GetCacheEntry(resource_id, md5, &cache_entry)) {
939 // Entry does not exist in cache.
[email protected]a321b9632012-06-14 03:29:17940 // Set both |dest_path| and |source_path| to /dev/null, so that:
941 // 1) ModifyCacheState won't move files when |source_path| and |dest_path|
942 // are the same.
943 // 2) symlinks to /dev/null will be picked up by GDataSyncClient to download
944 // pinned files that don't exist in cache.
[email protected]30d9dda2012-06-30 05:56:28945 dest_path = FilePath::FromUTF8Unsafe(util::kSymLinkToDevNull);
[email protected]a321b9632012-06-14 03:29:17946 source_path = dest_path;
947
[email protected]2a6fe6e2012-07-10 06:01:04948 // Set sub_dir_type to TMP. The file will be first downloaded in 'tmp',
949 // then moved to 'persistent'.
950 sub_dir_type = CACHE_TYPE_TMP;
[email protected]109eb5c2012-07-12 03:20:05951 } else { // File exists in cache, determines destination path.
[email protected]a321b9632012-06-14 03:29:17952 // Determine source and destination paths.
953
954 // If file is dirty or mounted, don't move it, so determine |dest_path| and
955 // set |source_path| the same, because ModifyCacheState only moves files if
956 // source and destination are different.
[email protected]02821102012-07-12 20:19:17957 if (cache_entry.is_dirty() || cache_entry.is_mounted()) {
958 DCHECK(cache_entry.is_persistent());
[email protected]a321b9632012-06-14 03:29:17959 dest_path = GetCacheFilePath(resource_id,
960 md5,
[email protected]b22f87f2012-07-12 10:53:17961 GetSubDirectoryType(cache_entry),
[email protected]a321b9632012-06-14 03:29:17962 CACHED_FILE_LOCALLY_MODIFIED);
963 source_path = dest_path;
964 } else {
965 // Gets the current path of the file in cache.
966 source_path = GetCacheFilePath(resource_id,
967 md5,
[email protected]b22f87f2012-07-12 10:53:17968 GetSubDirectoryType(cache_entry),
[email protected]a321b9632012-06-14 03:29:17969 CACHED_FILE_FROM_SERVER);
970
971 // If file was pinned before but actual file blob doesn't exist in cache:
972 // - don't need to move the file, so set |dest_path| to |source_path|,
973 // because ModifyCacheState only moves files if source and destination
974 // are different
975 // - don't create symlink since it already exists.
[email protected]02821102012-07-12 20:19:17976 if (!cache_entry.is_present()) {
[email protected]a321b9632012-06-14 03:29:17977 dest_path = source_path;
978 create_symlink = false;
979 } else { // File exists, move it to persistent dir.
980 dest_path = GetCacheFilePath(resource_id,
981 md5,
982 CACHE_TYPE_PERSISTENT,
983 CACHED_FILE_FROM_SERVER);
984 }
985 }
986 }
987
988 // Create symlink in pinned dir.
989 if (create_symlink) {
990 symlink_path = GetCacheFilePath(resource_id,
991 std::string(),
992 CACHE_TYPE_PINNED,
993 CACHED_FILE_FROM_SERVER);
994 }
995
996 *error = ModifyCacheState(source_path,
997 dest_path,
998 file_operation_type,
999 symlink_path,
1000 create_symlink);
1001
[email protected]7f90e77c2012-07-17 09:35:311002 if (*error == GDATA_FILE_OK) {
[email protected]a321b9632012-06-14 03:29:171003 // Now that file operations have completed, update cache map.
[email protected]b22f87f2012-07-12 10:53:171004 cache_entry.set_md5(md5);
[email protected]02821102012-07-12 20:19:171005 cache_entry.set_is_pinned(true);
1006 cache_entry.set_is_persistent(sub_dir_type == CACHE_TYPE_PERSISTENT);
[email protected]b22f87f2012-07-12 10:53:171007 metadata_->AddOrUpdateCacheEntry(resource_id, cache_entry);
[email protected]a321b9632012-06-14 03:29:171008 }
1009}
1010
1011void GDataCache::Unpin(const std::string& resource_id,
1012 const std::string& md5,
1013 FileOperationType file_operation_type,
[email protected]7f90e77c2012-07-17 09:35:311014 GDataFileError* error) {
[email protected]a321b9632012-06-14 03:29:171015 AssertOnSequencedWorkerPool();
1016 DCHECK(error);
1017
[email protected]a321b9632012-06-14 03:29:171018 // Unpinning a file means its entry must exist in cache.
[email protected]28a64092012-08-21 10:01:121019 DriveCacheEntry cache_entry;
[email protected]b22f87f2012-07-12 10:53:171020 if (!GetCacheEntry(resource_id, md5, &cache_entry)) {
[email protected]a321b9632012-06-14 03:29:171021 LOG(WARNING) << "Can't unpin a file that wasn't pinned or cached: res_id="
1022 << resource_id
1023 << ", md5=" << md5;
[email protected]7f90e77c2012-07-17 09:35:311024 *error = GDATA_FILE_ERROR_NOT_FOUND;
[email protected]a321b9632012-06-14 03:29:171025 return;
1026 }
1027
1028 // Entry exists in cache, determines source and destination paths.
1029
1030 FilePath source_path;
1031 FilePath dest_path;
1032 CacheSubDirectoryType sub_dir_type = CACHE_TYPE_TMP;
1033
1034 // If file is dirty or mounted, don't move it, so determine |dest_path| and
1035 // set |source_path| the same, because ModifyCacheState moves files if source
1036 // and destination are different.
[email protected]02821102012-07-12 20:19:171037 if (cache_entry.is_dirty() || cache_entry.is_mounted()) {
[email protected]a321b9632012-06-14 03:29:171038 sub_dir_type = CACHE_TYPE_PERSISTENT;
[email protected]02821102012-07-12 20:19:171039 DCHECK(cache_entry.is_persistent());
[email protected]a321b9632012-06-14 03:29:171040 dest_path = GetCacheFilePath(resource_id,
1041 md5,
[email protected]b22f87f2012-07-12 10:53:171042 GetSubDirectoryType(cache_entry),
[email protected]a321b9632012-06-14 03:29:171043 CACHED_FILE_LOCALLY_MODIFIED);
1044 source_path = dest_path;
1045 } else {
1046 // Gets the current path of the file in cache.
1047 source_path = GetCacheFilePath(resource_id,
1048 md5,
[email protected]b22f87f2012-07-12 10:53:171049 GetSubDirectoryType(cache_entry),
[email protected]a321b9632012-06-14 03:29:171050 CACHED_FILE_FROM_SERVER);
1051
1052 // If file was pinned but actual file blob still doesn't exist in cache,
1053 // don't need to move the file, so set |dest_path| to |source_path|, because
1054 // ModifyCacheState only moves files if source and destination are
1055 // different.
[email protected]02821102012-07-12 20:19:171056 if (!cache_entry.is_present()) {
[email protected]a321b9632012-06-14 03:29:171057 dest_path = source_path;
1058 } else { // File exists, move it to tmp dir.
1059 dest_path = GetCacheFilePath(resource_id, md5,
1060 CACHE_TYPE_TMP,
1061 CACHED_FILE_FROM_SERVER);
1062 }
1063 }
1064
1065 // If file was pinned, get absolute path of symlink in pinned dir so as to
1066 // remove it.
1067 FilePath symlink_path;
[email protected]02821102012-07-12 20:19:171068 if (cache_entry.is_pinned()) {
[email protected]a321b9632012-06-14 03:29:171069 symlink_path = GetCacheFilePath(resource_id,
1070 std::string(),
1071 CACHE_TYPE_PINNED,
1072 CACHED_FILE_FROM_SERVER);
1073 }
1074
1075 *error = ModifyCacheState(
1076 source_path,
1077 dest_path,
1078 file_operation_type,
1079 symlink_path, // This will be deleted if it exists.
1080 false /* don't create symlink*/);
1081
[email protected]7f90e77c2012-07-17 09:35:311082 if (*error == GDATA_FILE_OK) {
[email protected]a321b9632012-06-14 03:29:171083 // Now that file operations have completed, update cache map.
[email protected]02821102012-07-12 20:19:171084 if (cache_entry.is_present()) {
[email protected]48477fe2012-07-12 17:45:081085 cache_entry.set_md5(md5);
[email protected]02821102012-07-12 20:19:171086 cache_entry.set_is_pinned(false);
1087 cache_entry.set_is_persistent(sub_dir_type == CACHE_TYPE_PERSISTENT);
[email protected]48477fe2012-07-12 17:45:081088 metadata_->AddOrUpdateCacheEntry(resource_id, cache_entry);
[email protected]3423871a2012-07-12 00:41:271089 } else {
1090 // Remove the existing entry if we are unpinning a non-present file.
1091 metadata_->RemoveCacheEntry(resource_id);
1092 }
[email protected]a321b9632012-06-14 03:29:171093 }
1094}
1095
1096void GDataCache::SetMountedState(const FilePath& file_path,
1097 bool to_mount,
[email protected]7f90e77c2012-07-17 09:35:311098 GDataFileError *error,
[email protected]a321b9632012-06-14 03:29:171099 FilePath* cache_file_path) {
1100 AssertOnSequencedWorkerPool();
1101 DCHECK(error);
1102 DCHECK(cache_file_path);
1103
1104 // Parse file path to obtain resource_id, md5 and extra_extension.
1105 std::string resource_id;
1106 std::string md5;
1107 std::string extra_extension;
1108 util::ParseCacheFilePath(file_path, &resource_id, &md5, &extra_extension);
1109 // The extra_extension shall be ".mounted" iff we're unmounting.
[email protected]ca5f6da2012-06-18 12:54:591110 DCHECK(!to_mount == (extra_extension == util::kMountedArchiveFileExtension));
[email protected]a321b9632012-06-14 03:29:171111
1112 // Get cache entry associated with the resource_id and md5
[email protected]28a64092012-08-21 10:01:121113 DriveCacheEntry cache_entry;
[email protected]b22f87f2012-07-12 10:53:171114 if (!GetCacheEntry(resource_id, md5, &cache_entry)) {
[email protected]7f90e77c2012-07-17 09:35:311115 *error = GDATA_FILE_ERROR_NOT_FOUND;
[email protected]a321b9632012-06-14 03:29:171116 return;
1117 }
[email protected]02821102012-07-12 20:19:171118 if (to_mount == cache_entry.is_mounted()) {
[email protected]7f90e77c2012-07-17 09:35:311119 *error = GDATA_FILE_ERROR_INVALID_OPERATION;
[email protected]a321b9632012-06-14 03:29:171120 return;
1121 }
1122
1123 // Get the subdir type and path for the unmounted state.
1124 CacheSubDirectoryType unmounted_subdir =
[email protected]02821102012-07-12 20:19:171125 cache_entry.is_pinned() ? CACHE_TYPE_PERSISTENT : CACHE_TYPE_TMP;
[email protected]a321b9632012-06-14 03:29:171126 FilePath unmounted_path = GetCacheFilePath(
1127 resource_id, md5, unmounted_subdir, CACHED_FILE_FROM_SERVER);
1128
1129 // Get the subdir type and path for the mounted state.
1130 CacheSubDirectoryType mounted_subdir = CACHE_TYPE_PERSISTENT;
1131 FilePath mounted_path = GetCacheFilePath(
1132 resource_id, md5, mounted_subdir, CACHED_FILE_MOUNTED);
1133
1134 // Determine the source and destination paths for moving the cache blob.
1135 FilePath source_path;
1136 CacheSubDirectoryType dest_subdir;
[email protected]a321b9632012-06-14 03:29:171137 if (to_mount) {
1138 source_path = unmounted_path;
1139 *cache_file_path = mounted_path;
1140 dest_subdir = mounted_subdir;
[email protected]02821102012-07-12 20:19:171141 cache_entry.set_is_mounted(true);
[email protected]a321b9632012-06-14 03:29:171142 } else {
1143 source_path = mounted_path;
1144 *cache_file_path = unmounted_path;
1145 dest_subdir = unmounted_subdir;
[email protected]02821102012-07-12 20:19:171146 cache_entry.set_is_mounted(false);
[email protected]a321b9632012-06-14 03:29:171147 }
1148
1149 // Move cache blob from source path to destination path.
1150 *error = ModifyCacheState(source_path, *cache_file_path,
1151 FILE_OPERATION_MOVE, FilePath(), false);
[email protected]7f90e77c2012-07-17 09:35:311152 if (*error == GDATA_FILE_OK) {
[email protected]a321b9632012-06-14 03:29:171153 // Now that cache operation is complete, update cache map
[email protected]48477fe2012-07-12 17:45:081154 cache_entry.set_md5(md5);
[email protected]02821102012-07-12 20:19:171155 cache_entry.set_is_persistent(dest_subdir == CACHE_TYPE_PERSISTENT);
[email protected]48477fe2012-07-12 17:45:081156 metadata_->AddOrUpdateCacheEntry(resource_id, cache_entry);
[email protected]a321b9632012-06-14 03:29:171157 }
1158}
1159
[email protected]c960d222012-06-15 10:03:501160void GDataCache::MarkDirty(const std::string& resource_id,
1161 const std::string& md5,
1162 FileOperationType file_operation_type,
[email protected]7f90e77c2012-07-17 09:35:311163 GDataFileError* error,
[email protected]c960d222012-06-15 10:03:501164 FilePath* cache_file_path) {
1165 AssertOnSequencedWorkerPool();
1166 DCHECK(error);
1167 DCHECK(cache_file_path);
1168
1169 // If file has already been marked dirty in previous instance of chrome, we
1170 // would have lost the md5 info during cache initialization, because the file
1171 // would have been renamed to .local extension.
1172 // So, search for entry in cache without comparing md5.
[email protected]c960d222012-06-15 10:03:501173
1174 // Marking a file dirty means its entry and actual file blob must exist in
1175 // cache.
[email protected]28a64092012-08-21 10:01:121176 DriveCacheEntry cache_entry;
[email protected]b22f87f2012-07-12 10:53:171177 if (!GetCacheEntry(resource_id, std::string(), &cache_entry) ||
[email protected]02821102012-07-12 20:19:171178 !cache_entry.is_present()) {
[email protected]c960d222012-06-15 10:03:501179 LOG(WARNING) << "Can't mark dirty a file that wasn't cached: res_id="
1180 << resource_id
1181 << ", md5=" << md5;
[email protected]7f90e77c2012-07-17 09:35:311182 *error = GDATA_FILE_ERROR_NOT_FOUND;
[email protected]c960d222012-06-15 10:03:501183 return;
1184 }
1185
1186 // If a file is already dirty (i.e. MarkDirtyInCache was called before),
1187 // delete outgoing symlink if it exists.
1188 // TODO(benchan): We should only delete outgoing symlink if file is currently
1189 // not being uploaded. However, for now, cache doesn't know if uploading of a
1190 // file is in progress. Per zel, the upload process should be canceled before
1191 // MarkDirtyInCache is called again.
[email protected]02821102012-07-12 20:19:171192 if (cache_entry.is_dirty()) {
[email protected]c960d222012-06-15 10:03:501193 // The file must be in persistent dir.
[email protected]02821102012-07-12 20:19:171194 DCHECK(cache_entry.is_persistent());
[email protected]c960d222012-06-15 10:03:501195
1196 // Determine symlink path in outgoing dir, so as to remove it.
1197 FilePath symlink_path = GetCacheFilePath(
1198 resource_id,
1199 std::string(),
1200 CACHE_TYPE_OUTGOING,
1201 CACHED_FILE_FROM_SERVER);
1202
1203 // We're not moving files here, so simply use empty FilePath for both
1204 // |source_path| and |dest_path| because ModifyCacheState only move files
1205 // if source and destination are different.
1206 *error = ModifyCacheState(
1207 FilePath(), // non-applicable source path
1208 FilePath(), // non-applicable dest path
1209 file_operation_type,
1210 symlink_path,
1211 false /* don't create symlink */);
1212
1213 // Determine current path of dirty file.
[email protected]7f90e77c2012-07-17 09:35:311214 if (*error == GDATA_FILE_OK) {
[email protected]c960d222012-06-15 10:03:501215 *cache_file_path = GetCacheFilePath(
1216 resource_id,
1217 md5,
1218 CACHE_TYPE_PERSISTENT,
1219 CACHED_FILE_LOCALLY_MODIFIED);
1220 }
1221 return;
1222 }
1223
1224 // Move file to persistent dir with new .local extension.
1225
1226 // Get the current path of the file in cache.
1227 FilePath source_path = GetCacheFilePath(
1228 resource_id,
1229 md5,
[email protected]b22f87f2012-07-12 10:53:171230 GetSubDirectoryType(cache_entry),
[email protected]c960d222012-06-15 10:03:501231 CACHED_FILE_FROM_SERVER);
1232
1233 // Determine destination path.
[email protected]3dc88ee2012-07-11 21:04:111234 const CacheSubDirectoryType sub_dir_type = CACHE_TYPE_PERSISTENT;
[email protected]c960d222012-06-15 10:03:501235 *cache_file_path = GetCacheFilePath(resource_id,
1236 md5,
1237 sub_dir_type,
1238 CACHED_FILE_LOCALLY_MODIFIED);
1239
1240 // If file is pinned, update symlink in pinned dir.
1241 FilePath symlink_path;
[email protected]02821102012-07-12 20:19:171242 if (cache_entry.is_pinned()) {
[email protected]c960d222012-06-15 10:03:501243 symlink_path = GetCacheFilePath(resource_id,
1244 std::string(),
1245 CACHE_TYPE_PINNED,
1246 CACHED_FILE_FROM_SERVER);
1247 }
1248
1249 *error = ModifyCacheState(
1250 source_path,
1251 *cache_file_path,
1252 file_operation_type,
1253 symlink_path,
1254 !symlink_path.empty() /* create symlink */);
1255
[email protected]7f90e77c2012-07-17 09:35:311256 if (*error == GDATA_FILE_OK) {
[email protected]c960d222012-06-15 10:03:501257 // Now that file operations have completed, update cache map.
[email protected]48477fe2012-07-12 17:45:081258 cache_entry.set_md5(md5);
[email protected]02821102012-07-12 20:19:171259 cache_entry.set_is_dirty(true);
1260 cache_entry.set_is_persistent(sub_dir_type == CACHE_TYPE_PERSISTENT);
[email protected]48477fe2012-07-12 17:45:081261 metadata_->AddOrUpdateCacheEntry(resource_id, cache_entry);
[email protected]c960d222012-06-15 10:03:501262 }
1263}
1264
[email protected]a321b9632012-06-14 03:29:171265void GDataCache::CommitDirty(const std::string& resource_id,
1266 const std::string& md5,
1267 FileOperationType file_operation_type,
[email protected]7f90e77c2012-07-17 09:35:311268 GDataFileError* error) {
[email protected]a321b9632012-06-14 03:29:171269 AssertOnSequencedWorkerPool();
1270 DCHECK(error);
1271
1272 // If file has already been marked dirty in previous instance of chrome, we
1273 // would have lost the md5 info during cache initialization, because the file
1274 // would have been renamed to .local extension.
1275 // So, search for entry in cache without comparing md5.
[email protected]a321b9632012-06-14 03:29:171276
1277 // Committing a file dirty means its entry and actual file blob must exist in
1278 // cache.
[email protected]28a64092012-08-21 10:01:121279 DriveCacheEntry cache_entry;
[email protected]b22f87f2012-07-12 10:53:171280 if (!GetCacheEntry(resource_id, std::string(), &cache_entry) ||
[email protected]02821102012-07-12 20:19:171281 !cache_entry.is_present()) {
[email protected]a321b9632012-06-14 03:29:171282 LOG(WARNING) << "Can't commit dirty a file that wasn't cached: res_id="
1283 << resource_id
1284 << ", md5=" << md5;
[email protected]7f90e77c2012-07-17 09:35:311285 *error = GDATA_FILE_ERROR_NOT_FOUND;
[email protected]a321b9632012-06-14 03:29:171286 return;
1287 }
1288
1289 // If a file is not dirty (it should have been marked dirty via
1290 // MarkDirtyInCache), committing it dirty is an invalid operation.
[email protected]02821102012-07-12 20:19:171291 if (!cache_entry.is_dirty()) {
[email protected]a321b9632012-06-14 03:29:171292 LOG(WARNING) << "Can't commit a non-dirty file: res_id="
1293 << resource_id
1294 << ", md5=" << md5;
[email protected]7f90e77c2012-07-17 09:35:311295 *error = GDATA_FILE_ERROR_INVALID_OPERATION;
[email protected]a321b9632012-06-14 03:29:171296 return;
1297 }
1298
1299 // Dirty files must be in persistent dir.
[email protected]02821102012-07-12 20:19:171300 DCHECK(cache_entry.is_persistent());
[email protected]a321b9632012-06-14 03:29:171301
1302 // Create symlink in outgoing dir.
1303 FilePath symlink_path = GetCacheFilePath(resource_id,
1304 std::string(),
1305 CACHE_TYPE_OUTGOING,
1306 CACHED_FILE_FROM_SERVER);
1307
1308 // Get target path of symlink i.e. current path of the file in cache.
1309 FilePath target_path = GetCacheFilePath(resource_id,
1310 md5,
[email protected]b22f87f2012-07-12 10:53:171311 GetSubDirectoryType(cache_entry),
[email protected]a321b9632012-06-14 03:29:171312 CACHED_FILE_LOCALLY_MODIFIED);
1313
1314 // Since there's no need to move files, use |target_path| for both
1315 // |source_path| and |dest_path|, because ModifyCacheState only moves files
1316 // if source and destination are different.
1317 *error = ModifyCacheState(target_path, // source
1318 target_path, // destination
1319 file_operation_type,
1320 symlink_path,
1321 true /* create symlink */);
1322}
1323
1324void GDataCache::ClearDirty(const std::string& resource_id,
1325 const std::string& md5,
1326 FileOperationType file_operation_type,
[email protected]7f90e77c2012-07-17 09:35:311327 GDataFileError* error) {
[email protected]a321b9632012-06-14 03:29:171328 AssertOnSequencedWorkerPool();
1329 DCHECK(error);
1330
1331 // |md5| is the new .<md5> extension to rename the file to.
1332 // So, search for entry in cache without comparing md5.
[email protected]28a64092012-08-21 10:01:121333 DriveCacheEntry cache_entry;
[email protected]a321b9632012-06-14 03:29:171334
1335 // Clearing a dirty file means its entry and actual file blob must exist in
1336 // cache.
[email protected]b22f87f2012-07-12 10:53:171337 if (!GetCacheEntry(resource_id, std::string(), &cache_entry) ||
[email protected]02821102012-07-12 20:19:171338 !cache_entry.is_present()) {
[email protected]a321b9632012-06-14 03:29:171339 LOG(WARNING) << "Can't clear dirty state of a file that wasn't cached: "
1340 << "res_id=" << resource_id
1341 << ", md5=" << md5;
[email protected]7f90e77c2012-07-17 09:35:311342 *error = GDATA_FILE_ERROR_NOT_FOUND;
[email protected]a321b9632012-06-14 03:29:171343 return;
1344 }
1345
1346 // If a file is not dirty (it should have been marked dirty via
1347 // MarkDirtyInCache), clearing its dirty state is an invalid operation.
[email protected]02821102012-07-12 20:19:171348 if (!cache_entry.is_dirty()) {
[email protected]a321b9632012-06-14 03:29:171349 LOG(WARNING) << "Can't clear dirty state of a non-dirty file: res_id="
1350 << resource_id
1351 << ", md5=" << md5;
[email protected]7f90e77c2012-07-17 09:35:311352 *error = GDATA_FILE_ERROR_INVALID_OPERATION;
[email protected]a321b9632012-06-14 03:29:171353 return;
1354 }
1355
1356 // File must be dirty and hence in persistent dir.
[email protected]02821102012-07-12 20:19:171357 DCHECK(cache_entry.is_persistent());
[email protected]a321b9632012-06-14 03:29:171358
1359 // Get the current path of the file in cache.
1360 FilePath source_path = GetCacheFilePath(resource_id,
1361 md5,
[email protected]b22f87f2012-07-12 10:53:171362 GetSubDirectoryType(cache_entry),
[email protected]a321b9632012-06-14 03:29:171363 CACHED_FILE_LOCALLY_MODIFIED);
1364
1365 // Determine destination path.
1366 // If file is pinned, move it to persistent dir with .md5 extension;
1367 // otherwise, move it to tmp dir with .md5 extension.
[email protected]3dc88ee2012-07-11 21:04:111368 const CacheSubDirectoryType sub_dir_type =
[email protected]02821102012-07-12 20:19:171369 cache_entry.is_pinned() ? CACHE_TYPE_PERSISTENT : CACHE_TYPE_TMP;
[email protected]a321b9632012-06-14 03:29:171370 FilePath dest_path = GetCacheFilePath(resource_id,
1371 md5,
1372 sub_dir_type,
1373 CACHED_FILE_FROM_SERVER);
1374
1375 // Delete symlink in outgoing dir.
1376 FilePath symlink_path = GetCacheFilePath(resource_id,
1377 std::string(),
1378 CACHE_TYPE_OUTGOING,
1379 CACHED_FILE_FROM_SERVER);
1380
1381 *error = ModifyCacheState(source_path,
1382 dest_path,
1383 file_operation_type,
1384 symlink_path,
1385 false /* don't create symlink */);
1386
1387 // If file is pinned, update symlink in pinned dir.
[email protected]7f90e77c2012-07-17 09:35:311388 if (*error == GDATA_FILE_OK && cache_entry.is_pinned()) {
[email protected]a321b9632012-06-14 03:29:171389 symlink_path = GetCacheFilePath(resource_id,
1390 std::string(),
1391 CACHE_TYPE_PINNED,
1392 CACHED_FILE_FROM_SERVER);
1393
1394 // Since there's no moving of files here, use |dest_path| for both
1395 // |source_path| and |dest_path|, because ModifyCacheState only moves files
1396 // if source and destination are different.
1397 *error = ModifyCacheState(dest_path, // source path
1398 dest_path, // destination path
1399 file_operation_type,
1400 symlink_path,
1401 true /* create symlink */);
1402 }
1403
[email protected]7f90e77c2012-07-17 09:35:311404 if (*error == GDATA_FILE_OK) {
[email protected]a321b9632012-06-14 03:29:171405 // Now that file operations have completed, update cache map.
[email protected]48477fe2012-07-12 17:45:081406 cache_entry.set_md5(md5);
[email protected]02821102012-07-12 20:19:171407 cache_entry.set_is_dirty(false);
1408 cache_entry.set_is_persistent(sub_dir_type == CACHE_TYPE_PERSISTENT);
[email protected]48477fe2012-07-12 17:45:081409 metadata_->AddOrUpdateCacheEntry(resource_id, cache_entry);
[email protected]a321b9632012-06-14 03:29:171410 }
1411}
1412
1413void GDataCache::Remove(const std::string& resource_id,
[email protected]7f90e77c2012-07-17 09:35:311414 GDataFileError* error) {
[email protected]a321b9632012-06-14 03:29:171415 AssertOnSequencedWorkerPool();
1416 DCHECK(error);
1417
[email protected]3423871a2012-07-12 00:41:271418 // MD5 is not passed into RemoveCacheEntry because we would delete all
1419 // cache files corresponding to <resource_id> regardless of the md5.
[email protected]a321b9632012-06-14 03:29:171420 // So, search for entry in cache without taking md5 into account.
[email protected]28a64092012-08-21 10:01:121421 DriveCacheEntry cache_entry;
[email protected]a321b9632012-06-14 03:29:171422
1423 // If entry doesn't exist or is dirty or mounted in cache, nothing to do.
[email protected]b22f87f2012-07-12 10:53:171424 const bool entry_found =
1425 GetCacheEntry(resource_id, std::string(), &cache_entry);
[email protected]02821102012-07-12 20:19:171426 if (!entry_found || cache_entry.is_dirty() || cache_entry.is_mounted()) {
[email protected]a321b9632012-06-14 03:29:171427 DVLOG(1) << "Entry is "
[email protected]b22f87f2012-07-12 10:53:171428 << (entry_found ?
[email protected]02821102012-07-12 20:19:171429 (cache_entry.is_dirty() ? "dirty" : "mounted") :
[email protected]a321b9632012-06-14 03:29:171430 "non-existent")
1431 << " in cache, not removing";
[email protected]7f90e77c2012-07-17 09:35:311432 *error = GDATA_FILE_OK;
[email protected]a321b9632012-06-14 03:29:171433 return;
1434 }
1435
1436 // Determine paths to delete all cache versions of |resource_id| in
1437 // persistent, tmp and pinned directories.
1438 std::vector<FilePath> paths_to_delete;
1439
1440 // For files in persistent and tmp dirs, delete files that match
1441 // "<resource_id>.*".
1442 paths_to_delete.push_back(GetCacheFilePath(resource_id,
[email protected]ca5f6da2012-06-18 12:54:591443 util::kWildCard,
[email protected]a321b9632012-06-14 03:29:171444 CACHE_TYPE_PERSISTENT,
1445 CACHED_FILE_FROM_SERVER));
1446 paths_to_delete.push_back(GetCacheFilePath(resource_id,
[email protected]ca5f6da2012-06-18 12:54:591447 util::kWildCard,
[email protected]a321b9632012-06-14 03:29:171448 CACHE_TYPE_TMP,
1449 CACHED_FILE_FROM_SERVER));
1450
1451 // For pinned files, filename is "<resource_id>" with no extension, so delete
1452 // "<resource_id>".
1453 paths_to_delete.push_back(GetCacheFilePath(resource_id,
1454 std::string(),
1455 CACHE_TYPE_PINNED,
1456 CACHED_FILE_FROM_SERVER));
1457
1458 // Don't delete locally modified (i.e. dirty and possibly outgoing) files.
1459 // Since we're not deleting outgoing symlinks, we don't need to append
1460 // outgoing path to |paths_to_delete|.
1461 FilePath path_to_keep = GetCacheFilePath(resource_id,
1462 std::string(),
1463 CACHE_TYPE_PERSISTENT,
1464 CACHED_FILE_LOCALLY_MODIFIED);
1465
1466 for (size_t i = 0; i < paths_to_delete.size(); ++i) {
1467 DeleteFilesSelectively(paths_to_delete[i], path_to_keep);
1468 }
1469
1470 // Now that all file operations have completed, remove from cache map.
[email protected]3423871a2012-07-12 00:41:271471 metadata_->RemoveCacheEntry(resource_id);
[email protected]a321b9632012-06-14 03:29:171472
[email protected]7f90e77c2012-07-17 09:35:311473 *error = GDATA_FILE_OK;
[email protected]a321b9632012-06-14 03:29:171474}
1475
[email protected]f861b392012-08-03 20:41:121476void GDataCache::ClearAll(GDataFileError* error) {
1477 AssertOnSequencedWorkerPool();
1478 DCHECK(error);
1479
1480 bool success = file_util::Delete(cache_root_path_, true);
1481 Initialize();
1482
1483 *error = success ? GDATA_FILE_OK : GDATA_FILE_ERROR_FAILED;
1484}
1485
[email protected]7f90e77c2012-07-17 09:35:311486void GDataCache::OnPinned(GDataFileError* error,
[email protected]73f9c742012-06-15 07:37:131487 const std::string& resource_id,
1488 const std::string& md5,
1489 const CacheOperationCallback& callback) {
1490 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1491 DCHECK(error);
1492
1493 if (!callback.is_null())
1494 callback.Run(*error, resource_id, md5);
1495
[email protected]7f90e77c2012-07-17 09:35:311496 if (*error == GDATA_FILE_OK)
[email protected]73f9c742012-06-15 07:37:131497 FOR_EACH_OBSERVER(Observer, observers_, OnCachePinned(resource_id, md5));
[email protected]3653146a2012-05-29 13:41:471498}
1499
[email protected]7f90e77c2012-07-17 09:35:311500void GDataCache::OnUnpinned(GDataFileError* error,
[email protected]73f9c742012-06-15 07:37:131501 const std::string& resource_id,
1502 const std::string& md5,
1503 const CacheOperationCallback& callback) {
1504 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1505 DCHECK(error);
[email protected]a321b9632012-06-14 03:29:171506
[email protected]73f9c742012-06-15 07:37:131507 if (!callback.is_null())
1508 callback.Run(*error, resource_id, md5);
[email protected]a321b9632012-06-14 03:29:171509
[email protected]7f90e77c2012-07-17 09:35:311510 if (*error == GDATA_FILE_OK)
[email protected]73f9c742012-06-15 07:37:131511 FOR_EACH_OBSERVER(Observer, observers_, OnCacheUnpinned(resource_id, md5));
[email protected]a321b9632012-06-14 03:29:171512
[email protected]73f9c742012-06-15 07:37:131513 // Now the file is moved from "persistent" to "tmp" directory.
1514 // It's a chance to free up space if needed.
1515 bool* has_enough_space = new bool(false);
[email protected]ddbf2052012-07-13 15:07:021516 blocking_task_runner_->PostTask(
[email protected]73f9c742012-06-15 07:37:131517 FROM_HERE,
1518 base::Bind(&GDataCache::FreeDiskSpaceIfNeededFor,
1519 base::Unretained(this),
1520 0,
1521 base::Owned(has_enough_space)));
[email protected]3653146a2012-05-29 13:41:471522}
1523
[email protected]7f90e77c2012-07-17 09:35:311524void GDataCache::OnCommitDirty(GDataFileError* error,
[email protected]d7664c22012-06-18 19:35:491525 const std::string& resource_id,
1526 const std::string& md5,
1527 const CacheOperationCallback& callback) {
1528 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1529 DCHECK(error);
1530
1531 if (!callback.is_null())
1532 callback.Run(*error, resource_id, md5);
1533
[email protected]7f90e77c2012-07-17 09:35:311534 if (*error == GDATA_FILE_OK)
[email protected]d7664c22012-06-18 19:35:491535 FOR_EACH_OBSERVER(Observer, observers_, OnCacheCommitted(resource_id));
1536}
1537
[email protected]4324fdc2012-06-29 05:32:481538void GDataCache::GetCacheEntryHelper(const std::string& resource_id,
1539 const std::string& md5,
1540 bool* success,
[email protected]28a64092012-08-21 10:01:121541 DriveCacheEntry* cache_entry) {
[email protected]4324fdc2012-06-29 05:32:481542 AssertOnSequencedWorkerPool();
1543 DCHECK(success);
1544 DCHECK(cache_entry);
1545
[email protected]b22f87f2012-07-12 10:53:171546 *success = GetCacheEntry(resource_id, md5, cache_entry);
[email protected]4324fdc2012-06-29 05:32:481547}
1548
[email protected]01ba15f72012-06-09 00:41:051549// static
1550FilePath GDataCache::GetCacheRootPath(Profile* profile) {
1551 FilePath cache_base_path;
1552 chrome::GetUserCacheDirectory(profile->GetPath(), &cache_base_path);
1553 FilePath cache_root_path =
1554 cache_base_path.Append(chrome::kGDataCacheDirname);
1555 return cache_root_path.Append(kGDataCacheVersionDir);
1556}
1557
[email protected]30d9dda2012-06-30 05:56:281558// static
1559std::vector<FilePath> GDataCache::GetCachePaths(
1560 const FilePath& cache_root_path) {
1561 std::vector<FilePath> cache_paths;
1562 // The order should match GDataCache::CacheSubDirectoryType enum.
1563 cache_paths.push_back(cache_root_path.Append(kGDataCacheMetaDir));
1564 cache_paths.push_back(cache_root_path.Append(kGDataCachePinnedDir));
1565 cache_paths.push_back(cache_root_path.Append(kGDataCacheOutgoingDir));
1566 cache_paths.push_back(cache_root_path.Append(kGDataCachePersistentDir));
1567 cache_paths.push_back(cache_root_path.Append(kGDataCacheTmpDir));
1568 cache_paths.push_back(cache_root_path.Append(kGDataCacheTmpDownloadsDir));
1569 cache_paths.push_back(cache_root_path.Append(kGDataCacheTmpDocumentsDir));
1570 return cache_paths;
1571}
1572
1573// static
1574bool GDataCache::CreateCacheDirectories(
1575 const std::vector<FilePath>& paths_to_create) {
1576 bool success = true;
1577
1578 for (size_t i = 0; i < paths_to_create.size(); ++i) {
1579 if (file_util::DirectoryExists(paths_to_create[i]))
1580 continue;
1581
1582 if (!file_util::CreateDirectory(paths_to_create[i])) {
1583 // Error creating this directory, record error and proceed with next one.
1584 success = false;
1585 PLOG(ERROR) << "Error creating directory " << paths_to_create[i].value();
1586 } else {
1587 DVLOG(1) << "Created directory " << paths_to_create[i].value();
1588 }
1589 }
1590 return success;
1591}
1592
[email protected]fae353a2012-07-11 23:30:271593// static
1594GDataCache::CacheSubDirectoryType GDataCache::GetSubDirectoryType(
[email protected]28a64092012-08-21 10:01:121595 const DriveCacheEntry& cache_entry) {
[email protected]02821102012-07-12 20:19:171596 return cache_entry.is_persistent() ? CACHE_TYPE_PERSISTENT : CACHE_TYPE_TMP;
[email protected]fae353a2012-07-11 23:30:271597}
1598
[email protected]a321b9632012-06-14 03:29:171599void SetFreeDiskSpaceGetterForTesting(FreeDiskSpaceGetterInterface* getter) {
1600 delete global_free_disk_getter_for_testing; // Safe to delete NULL;
1601 global_free_disk_getter_for_testing = getter;
1602}
1603
[email protected]3653146a2012-05-29 13:41:471604} // namespace gdata