| OLD | NEW |
| (Empty) |
| 1 // 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 | |
| 9 #include "base/chromeos/chromeos_version.h" | |
| 10 #include "base/file_util.h" | |
| 11 #include "base/logging.h" | |
| 12 #include "base/path_service.h" | |
| 13 #include "base/stringprintf.h" | |
| 14 #include "base/string_util.h" | |
| 15 #include "base/sys_info.h" | |
| 16 #include "chrome/browser/chromeos/gdata/drive.pb.h" | |
| 17 #include "chrome/browser/chromeos/gdata/gdata_cache_metadata.h" | |
| 18 #include "chrome/browser/chromeos/gdata/gdata_util.h" | |
| 19 #include "chrome/browser/profiles/profile.h" | |
| 20 #include "chrome/common/chrome_constants.h" | |
| 21 #include "chrome/common/chrome_paths_internal.h" | |
| 22 #include "content/public/browser/browser_thread.h" | |
| 23 | |
| 24 using content::BrowserThread; | |
| 25 | |
| 26 namespace gdata { | |
| 27 namespace { | |
| 28 | |
| 29 const FilePath::CharType kGDataCacheVersionDir[] = FILE_PATH_LITERAL("v1"); | |
| 30 const FilePath::CharType kGDataCacheMetaDir[] = FILE_PATH_LITERAL("meta"); | |
| 31 const FilePath::CharType kGDataCachePinnedDir[] = FILE_PATH_LITERAL("pinned"); | |
| 32 const FilePath::CharType kGDataCacheOutgoingDir[] = | |
| 33 FILE_PATH_LITERAL("outgoing"); | |
| 34 const FilePath::CharType kGDataCachePersistentDir[] = | |
| 35 FILE_PATH_LITERAL("persistent"); | |
| 36 const FilePath::CharType kGDataCacheTmpDir[] = FILE_PATH_LITERAL("tmp"); | |
| 37 const FilePath::CharType kGDataCacheTmpDownloadsDir[] = | |
| 38 FILE_PATH_LITERAL("tmp/downloads"); | |
| 39 const FilePath::CharType kGDataCacheTmpDocumentsDir[] = | |
| 40 FILE_PATH_LITERAL("tmp/documents"); | |
| 41 | |
| 42 // Used to tweak GetAmountOfFreeDiskSpace() behavior for testing. | |
| 43 FreeDiskSpaceGetterInterface* 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. | |
| 47 int64 GetAmountOfFreeDiskSpace() { | |
| 48 if (global_free_disk_getter_for_testing) | |
| 49 return global_free_disk_getter_for_testing->AmountOfFreeDiskSpace(); | |
| 50 | |
| 51 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); | |
| 57 } | |
| 58 | |
| 59 // Returns true if we have sufficient space to store the given number of | |
| 60 // bytes, while keeping kMinFreeSpace bytes on the disk. | |
| 61 bool HasEnoughSpaceFor(int64 num_bytes) { | |
| 62 int64 free_space = GetAmountOfFreeDiskSpace(); | |
| 63 // Subtract this as if this portion does not exist. | |
| 64 free_space -= kMinFreeSpace; | |
| 65 return (free_space >= num_bytes); | |
| 66 } | |
| 67 | |
| 68 // Create cache directory paths and set permissions. | |
| 69 void 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 | |
| 88 // Remove all files under the given directory, non-recursively. | |
| 89 // Do not remove recursively as we don't want to touch <gcache>/tmp/downloads, | |
| 90 // which is used for user initiated downloads like "Save As" | |
| 91 void 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 | |
| 104 // 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. | |
| 110 GDataFileError ModifyCacheState( | |
| 111 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) { | |
| 124 LOG(ERROR) << "Failed to " | |
| 125 << (file_operation_type == GDataCache::FILE_OPERATION_MOVE ? | |
| 126 "move " : "copy ") | |
| 127 << source_path.value() | |
| 128 << " to " << dest_path.value(); | |
| 129 return GDATA_FILE_ERROR_FAILED; | |
| 130 } 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()) | |
| 141 return GDATA_FILE_OK; | |
| 142 | |
| 143 // Remove symlink regardless of |create_symlink| because creating a link will | |
| 144 // not overwrite an existing one. | |
| 145 // 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. | |
| 148 file_util::Delete(symlink_path, false); | |
| 149 | |
| 150 if (!create_symlink) | |
| 151 return GDATA_FILE_OK; | |
| 152 | |
| 153 // Create new symlink to |dest_path|. | |
| 154 if (!file_util::CreateSymbolicLink(dest_path, symlink_path)) { | |
| 155 LOG(ERROR) << "Failed to create a symlink from " << symlink_path.value() | |
| 156 << " to " << dest_path.value(); | |
| 157 return GDATA_FILE_ERROR_FAILED; | |
| 158 } | |
| 159 | |
| 160 return GDATA_FILE_OK; | |
| 161 } | |
| 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. | |
| 167 void DeleteFilesSelectively(const FilePath& path_to_delete_pattern, | |
| 168 const FilePath& path_to_keep) { | |
| 169 // Enumerate all files in directory of |path_to_delete_pattern| that match | |
| 170 // base name of |path_to_delete_pattern|. | |
| 171 // If a file is not |path_to_keep|, delete it. | |
| 172 bool success = true; | |
| 173 file_util::FileEnumerator enumerator(path_to_delete_pattern.DirName(), | |
| 174 false, // not recursive | |
| 175 file_util::FileEnumerator::FILES | | |
| 176 file_util::FileEnumerator::SHOW_SYM_LINKS, | |
| 177 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 | |
| 184 success = file_util::Delete(current, false); | |
| 185 if (!success) | |
| 186 DVLOG(1) << "Error deleting " << current.value(); | |
| 187 else | |
| 188 DVLOG(1) << "Deleted " << current.value(); | |
| 189 } | |
| 190 } | |
| 191 | |
| 192 // 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. | |
| 195 void CollectBacklog(std::vector<std::string>* to_fetch, | |
| 196 std::vector<std::string>* to_upload, | |
| 197 const std::string& resource_id, | |
| 198 const DriveCacheEntry& cache_entry) { | |
| 199 DCHECK(to_fetch); | |
| 200 DCHECK(to_upload); | |
| 201 | |
| 202 if (cache_entry.is_pinned() && !cache_entry.is_present()) | |
| 203 to_fetch->push_back(resource_id); | |
| 204 | |
| 205 if (cache_entry.is_dirty()) | |
| 206 to_upload->push_back(resource_id); | |
| 207 } | |
| 208 | |
| 209 // Appends |resource_id| ID to |resource_ids| if the file is pinned and | |
| 210 // present (cached locally). | |
| 211 void CollectExistingPinnedFile(std::vector<std::string>* resource_ids, | |
| 212 const std::string& resource_id, | |
| 213 const DriveCacheEntry& cache_entry) { | |
| 214 DCHECK(resource_ids); | |
| 215 | |
| 216 if (cache_entry.is_pinned() && cache_entry.is_present()) | |
| 217 resource_ids->push_back(resource_id); | |
| 218 } | |
| 219 | |
| 220 // Appends |resource_id| ID to |resource_ids| unconditionally. | |
| 221 void CollectAnyFile(std::vector<std::string>* resource_ids, | |
| 222 const std::string& resource_id, | |
| 223 const DriveCacheEntry& /* cache_entry */) { | |
| 224 DCHECK(resource_ids); | |
| 225 | |
| 226 resource_ids->push_back(resource_id); | |
| 227 } | |
| 228 | |
| 229 // Runs callback with pointers dereferenced. | |
| 230 // Used to implement SetMountedStateOnUIThread and ClearAllOnUIThread. | |
| 231 void RunChangeCacheStateCallback(const ChangeCacheStateCallback& callback, | |
| 232 const GDataFileError* error, | |
| 233 const FilePath* cache_file_path) { | |
| 234 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 | |
| 242 // Runs callback with pointers dereferenced. | |
| 243 // Used to implement *OnUIThread methods. | |
| 244 void RunCacheOperationCallback(const CacheOperationCallback& callback, | |
| 245 GDataFileError* error, | |
| 246 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 | |
| 255 // Runs callback with pointers dereferenced. | |
| 256 // Used to implement *OnUIThread methods. | |
| 257 void RunGetFileFromCacheCallback(const GetFileFromCacheCallback& callback, | |
| 258 GDataFileError* error, | |
| 259 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 | |
| 270 // Runs callback with pointers dereferenced. | |
| 271 // Used to implement GetResourceIdsOfBacklogOnUIThread(). | |
| 272 void RunGetResourceIdsOfBacklogCallback( | |
| 273 const GetResourceIdsOfBacklogCallback& callback, | |
| 274 std::vector<std::string>* to_fetch, | |
| 275 std::vector<std::string>* to_upload) { | |
| 276 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 277 DCHECK(to_fetch); | |
| 278 DCHECK(to_upload); | |
| 279 | |
| 280 if (!callback.is_null()) | |
| 281 callback.Run(*to_fetch, *to_upload); | |
| 282 } | |
| 283 | |
| 284 // Runs callback with pointers dereferenced. | |
| 285 // Used to implement GetResourceIdsOfExistingPinnedFilesOnUIThread(). | |
| 286 void 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. | |
| 297 // Used to implement GetCacheEntryOnUIThread(). | |
| 298 void RunGetCacheEntryCallback( | |
| 299 const GetCacheEntryCallback& callback, | |
| 300 bool* success, | |
| 301 DriveCacheEntry* cache_entry) { | |
| 302 DCHECK(success); | |
| 303 DCHECK(cache_entry); | |
| 304 | |
| 305 if (!callback.is_null()) | |
| 306 callback.Run(*success, *cache_entry); | |
| 307 } | |
| 308 | |
| 309 } // namespace | |
| 310 | |
| 311 GDataCache::GDataCache(const FilePath& cache_root_path, | |
| 312 base::SequencedTaskRunner* blocking_task_runner) | |
| 313 : cache_root_path_(cache_root_path), | |
| 314 cache_paths_(GetCachePaths(cache_root_path_)), | |
| 315 blocking_task_runner_(blocking_task_runner), | |
| 316 weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { | |
| 317 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 318 } | |
| 319 | |
| 320 GDataCache::~GDataCache() { | |
| 321 AssertOnSequencedWorkerPool(); | |
| 322 } | |
| 323 | |
| 324 FilePath 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 | |
| 331 FilePath 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; | |
| 344 base_name += util::kLocallyModifiedFileExtension; | |
| 345 } 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) { | |
| 352 DCHECK(sub_dir_type == CACHE_TYPE_PERSISTENT); | |
| 353 base_name += FilePath::kExtensionSeparator; | |
| 354 base_name += util::kMountedArchiveFileExtension; | |
| 355 } | |
| 356 return GetCacheDirectoryPath(sub_dir_type).Append(base_name); | |
| 357 } | |
| 358 | |
| 359 void GDataCache::AssertOnSequencedWorkerPool() { | |
| 360 DCHECK(!blocking_task_runner_ || | |
| 361 blocking_task_runner_->RunsTasksOnCurrentThread()); | |
| 362 } | |
| 363 | |
| 364 bool GDataCache::IsUnderGDataCacheDirectory(const FilePath& path) const { | |
| 365 return cache_root_path_ == path || cache_root_path_.IsParent(path); | |
| 366 } | |
| 367 | |
| 368 void GDataCache::AddObserver(Observer* observer) { | |
| 369 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 370 observers_.AddObserver(observer); | |
| 371 } | |
| 372 | |
| 373 void GDataCache::RemoveObserver(Observer* observer) { | |
| 374 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 375 observers_.RemoveObserver(observer); | |
| 376 } | |
| 377 | |
| 378 void 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); | |
| 385 DriveCacheEntry* cache_entry = new DriveCacheEntry; | |
| 386 blocking_task_runner_->PostTaskAndReply( | |
| 387 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 | |
| 400 void GDataCache::GetResourceIdsOfBacklogOnUIThread( | |
| 401 const GetResourceIdsOfBacklogCallback& callback) { | |
| 402 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 403 | |
| 404 std::vector<std::string>* to_fetch = new std::vector<std::string>; | |
| 405 std::vector<std::string>* to_upload = new std::vector<std::string>; | |
| 406 blocking_task_runner_->PostTaskAndReply( | |
| 407 FROM_HERE, | |
| 408 base::Bind(&GDataCache::GetResourceIdsOfBacklog, | |
| 409 base::Unretained(this), | |
| 410 to_fetch, | |
| 411 to_upload), | |
| 412 base::Bind(&RunGetResourceIdsOfBacklogCallback, | |
| 413 callback, | |
| 414 base::Owned(to_fetch), | |
| 415 base::Owned(to_upload))); | |
| 416 } | |
| 417 | |
| 418 void 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>; | |
| 423 blocking_task_runner_->PostTaskAndReply( | |
| 424 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 | |
| 433 void 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 | |
| 448 void 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. | |
| 460 metadata_->RemoveTemporaryFiles(); | |
| 461 // 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 | |
| 468 void GDataCache::GetFileOnUIThread(const std::string& resource_id, | |
| 469 const std::string& md5, | |
| 470 const GetFileFromCacheCallback& callback) { | |
| 471 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 472 | |
| 473 GDataFileError* error = | |
| 474 new GDataFileError(GDATA_FILE_OK); | |
| 475 FilePath* cache_file_path = new FilePath; | |
| 476 blocking_task_runner_->PostTaskAndReply( | |
| 477 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))); | |
| 490 } | |
| 491 | |
| 492 void 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 | |
| 499 GDataFileError* error = | |
| 500 new GDataFileError(GDATA_FILE_OK); | |
| 501 blocking_task_runner_->PostTaskAndReply( | |
| 502 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 | |
| 517 void GDataCache::PinOnUIThread(const std::string& resource_id, | |
| 518 const std::string& md5, | |
| 519 const CacheOperationCallback& callback) { | |
| 520 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 521 | |
| 522 GDataFileError* error = | |
| 523 new GDataFileError(GDATA_FILE_OK); | |
| 524 blocking_task_runner_->PostTaskAndReply( | |
| 525 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, | |
| 533 weak_ptr_factory_.GetWeakPtr(), | |
| 534 base::Owned(error), | |
| 535 resource_id, | |
| 536 md5, | |
| 537 callback)); | |
| 538 } | |
| 539 | |
| 540 void GDataCache::UnpinOnUIThread(const std::string& resource_id, | |
| 541 const std::string& md5, | |
| 542 const CacheOperationCallback& callback) { | |
| 543 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 544 GDataFileError* error = | |
| 545 new GDataFileError(GDATA_FILE_OK); | |
| 546 blocking_task_runner_->PostTaskAndReply( | |
| 547 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, | |
| 555 weak_ptr_factory_.GetWeakPtr(), | |
| 556 base::Owned(error), | |
| 557 resource_id, | |
| 558 md5, | |
| 559 callback)); | |
| 560 } | |
| 561 | |
| 562 void GDataCache::SetMountedStateOnUIThread( | |
| 563 const FilePath& file_path, | |
| 564 bool to_mount, | |
| 565 const ChangeCacheStateCallback& callback) { | |
| 566 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 567 | |
| 568 GDataFileError* error = | |
| 569 new GDataFileError(GDATA_FILE_OK); | |
| 570 FilePath* cache_file_path = new FilePath; | |
| 571 blocking_task_runner_->PostTaskAndReply( | |
| 572 FROM_HERE, | |
| 573 base::Bind(&GDataCache::SetMountedState, | |
| 574 base::Unretained(this), | |
| 575 file_path, | |
| 576 to_mount, | |
| 577 error, | |
| 578 cache_file_path), | |
| 579 base::Bind(&RunChangeCacheStateCallback, | |
| 580 callback, | |
| 581 base::Owned(error), | |
| 582 base::Owned(cache_file_path))); | |
| 583 } | |
| 584 | |
| 585 void GDataCache::MarkDirtyOnUIThread(const std::string& resource_id, | |
| 586 const std::string& md5, | |
| 587 const GetFileFromCacheCallback& callback) { | |
| 588 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 589 | |
| 590 GDataFileError* error = | |
| 591 new GDataFileError(GDATA_FILE_OK); | |
| 592 FilePath* cache_file_path = new FilePath; | |
| 593 blocking_task_runner_->PostTaskAndReply( | |
| 594 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))); | |
| 608 } | |
| 609 | |
| 610 void GDataCache::CommitDirtyOnUIThread(const std::string& resource_id, | |
| 611 const std::string& md5, | |
| 612 const CacheOperationCallback& callback) { | |
| 613 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 614 | |
| 615 GDataFileError* error = new GDataFileError(GDATA_FILE_OK); | |
| 616 blocking_task_runner_->PostTaskAndReply( | |
| 617 FROM_HERE, | |
| 618 base::Bind(&GDataCache::CommitDirty, | |
| 619 base::Unretained(this), | |
| 620 resource_id, | |
| 621 md5, | |
| 622 GDataCache::FILE_OPERATION_MOVE, | |
| 623 error), | |
| 624 base::Bind(&GDataCache::OnCommitDirty, | |
| 625 weak_ptr_factory_.GetWeakPtr(), | |
| 626 base::Owned(error), | |
| 627 resource_id, | |
| 628 md5, | |
| 629 callback)); | |
| 630 } | |
| 631 | |
| 632 void GDataCache::ClearDirtyOnUIThread(const std::string& resource_id, | |
| 633 const std::string& md5, | |
| 634 const CacheOperationCallback& callback) { | |
| 635 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 636 | |
| 637 GDataFileError* error = | |
| 638 new GDataFileError(GDATA_FILE_OK); | |
| 639 blocking_task_runner_->PostTaskAndReply( | |
| 640 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 | |
| 654 void GDataCache::RemoveOnUIThread(const std::string& resource_id, | |
| 655 const CacheOperationCallback& callback) { | |
| 656 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 657 | |
| 658 GDataFileError* error = | |
| 659 new GDataFileError(GDATA_FILE_OK); | |
| 660 | |
| 661 blocking_task_runner_->PostTaskAndReply( | |
| 662 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 | |
| 674 void 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 | |
| 690 void GDataCache::RequestInitializeOnUIThread() { | |
| 691 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 692 | |
| 693 blocking_task_runner_->PostTask( | |
| 694 FROM_HERE, | |
| 695 base::Bind(&GDataCache::Initialize, base::Unretained(this))); | |
| 696 } | |
| 697 | |
| 698 void 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 | |
| 706 void 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 | |
| 714 bool GDataCache::GetCacheEntry(const std::string& resource_id, | |
| 715 const std::string& md5, | |
| 716 DriveCacheEntry* entry) { | |
| 717 DCHECK(entry); | |
| 718 AssertOnSequencedWorkerPool(); | |
| 719 return metadata_->GetCacheEntry(resource_id, md5, entry); | |
| 720 } | |
| 721 | |
| 722 // static | |
| 723 GDataCache* GDataCache::CreateGDataCacheOnUIThread( | |
| 724 const FilePath& cache_root_path, | |
| 725 base::SequencedTaskRunner* blocking_task_runner) { | |
| 726 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 727 return new GDataCache(cache_root_path, blocking_task_runner); | |
| 728 } | |
| 729 | |
| 730 void GDataCache::DestroyOnUIThread() { | |
| 731 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 732 | |
| 733 // Invalidate the weak pointer. | |
| 734 weak_ptr_factory_.InvalidateWeakPtrs(); | |
| 735 | |
| 736 // Destroy myself on the blocking pool. | |
| 737 blocking_task_runner_->PostTask( | |
| 738 FROM_HERE, | |
| 739 base::Bind(&GDataCache::Destroy, | |
| 740 base::Unretained(this))); | |
| 741 } | |
| 742 | |
| 743 void GDataCache::Initialize() { | |
| 744 AssertOnSequencedWorkerPool(); | |
| 745 | |
| 746 InitCachePaths(cache_paths_); | |
| 747 metadata_ = GDataCacheMetadata::CreateGDataCacheMetadata( | |
| 748 blocking_task_runner_).Pass(); | |
| 749 metadata_->Initialize(cache_paths_); | |
| 750 } | |
| 751 | |
| 752 void 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 | |
| 761 void GDataCache::Destroy() { | |
| 762 AssertOnSequencedWorkerPool(); | |
| 763 delete this; | |
| 764 } | |
| 765 | |
| 766 void GDataCache::ForceRescanForTesting() { | |
| 767 AssertOnSequencedWorkerPool(); | |
| 768 metadata_->ForceRescanForTesting(cache_paths_); | |
| 769 } | |
| 770 | |
| 771 void GDataCache::GetResourceIdsOfBacklog( | |
| 772 std::vector<std::string>* to_fetch, | |
| 773 std::vector<std::string>* to_upload) { | |
| 774 AssertOnSequencedWorkerPool(); | |
| 775 DCHECK(to_fetch); | |
| 776 DCHECK(to_upload); | |
| 777 | |
| 778 metadata_->Iterate(base::Bind(&CollectBacklog, to_fetch, to_upload)); | |
| 779 } | |
| 780 | |
| 781 void 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 | |
| 789 void 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 | |
| 797 void GDataCache::GetFile(const std::string& resource_id, | |
| 798 const std::string& md5, | |
| 799 GDataFileError* error, | |
| 800 FilePath* cache_file_path) { | |
| 801 AssertOnSequencedWorkerPool(); | |
| 802 DCHECK(error); | |
| 803 DCHECK(cache_file_path); | |
| 804 | |
| 805 DriveCacheEntry cache_entry; | |
| 806 if (GetCacheEntry(resource_id, md5, &cache_entry) && | |
| 807 cache_entry.is_present()) { | |
| 808 CachedFileOrigin file_origin; | |
| 809 if (cache_entry.is_mounted()) { | |
| 810 file_origin = CACHED_FILE_MOUNTED; | |
| 811 } else if (cache_entry.is_dirty()) { | |
| 812 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, | |
| 819 GetSubDirectoryType(cache_entry), | |
| 820 file_origin); | |
| 821 *error = GDATA_FILE_OK; | |
| 822 } else { | |
| 823 *error = GDATA_FILE_ERROR_NOT_FOUND; | |
| 824 } | |
| 825 } | |
| 826 | |
| 827 void GDataCache::Store(const std::string& resource_id, | |
| 828 const std::string& md5, | |
| 829 const FilePath& source_path, | |
| 830 FileOperationType file_operation_type, | |
| 831 GDataFileError* error) { | |
| 832 AssertOnSequencedWorkerPool(); | |
| 833 DCHECK(error); | |
| 834 | |
| 835 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 | |
| 851 FilePath dest_path; | |
| 852 FilePath symlink_path; | |
| 853 CacheSubDirectoryType sub_dir_type = CACHE_TYPE_TMP; | |
| 854 | |
| 855 // If file was previously pinned, store it in persistent dir and create | |
| 856 // symlink in pinned dir. | |
| 857 DriveCacheEntry cache_entry; | |
| 858 if (GetCacheEntry(resource_id, md5, &cache_entry)) { // File exists in cache. | |
| 859 // If file is dirty or mounted, return error. | |
| 860 if (cache_entry.is_dirty() || cache_entry.is_mounted()) { | |
| 861 LOG(WARNING) << "Can't store a file to replace a " | |
| 862 << (cache_entry.is_dirty() ? "dirty" : "mounted") | |
| 863 << " file: res_id=" << resource_id | |
| 864 << ", md5=" << md5; | |
| 865 *error = GDATA_FILE_ERROR_IN_USE; | |
| 866 return; | |
| 867 } | |
| 868 | |
| 869 // If file is pinned, determines destination path. | |
| 870 if (cache_entry.is_pinned()) { | |
| 871 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. | |
| 904 stale_filenames_pattern = FilePath(dest_path.value() + util::kWildCard); | |
| 905 } else { | |
| 906 // Replace md5 extension with '*' i.e. "<resource_id>.*". | |
| 907 // Note that ReplaceExtension automatically prefixes the extension with the | |
| 908 // extension separator '.'. | |
| 909 stale_filenames_pattern = dest_path.ReplaceExtension(util::kWildCard); | |
| 910 } | |
| 911 | |
| 912 // Delete files that match |stale_filenames_pattern| except for |dest_path|. | |
| 913 DeleteFilesSelectively(stale_filenames_pattern, dest_path); | |
| 914 | |
| 915 if (*error == GDATA_FILE_OK) { | |
| 916 // Now that file operations have completed, update cache map. | |
| 917 cache_entry.set_md5(md5); | |
| 918 cache_entry.set_is_present(true); | |
| 919 cache_entry.set_is_persistent(sub_dir_type == CACHE_TYPE_PERSISTENT); | |
| 920 metadata_->AddOrUpdateCacheEntry(resource_id, cache_entry); | |
| 921 } | |
| 922 } | |
| 923 | |
| 924 void GDataCache::Pin(const std::string& resource_id, | |
| 925 const std::string& md5, | |
| 926 FileOperationType file_operation_type, | |
| 927 GDataFileError* error) { | |
| 928 AssertOnSequencedWorkerPool(); | |
| 929 DCHECK(error); | |
| 930 | |
| 931 FilePath source_path; | |
| 932 FilePath dest_path; | |
| 933 FilePath symlink_path; | |
| 934 bool create_symlink = true; | |
| 935 CacheSubDirectoryType sub_dir_type = CACHE_TYPE_PERSISTENT; | |
| 936 | |
| 937 DriveCacheEntry cache_entry; | |
| 938 if (!GetCacheEntry(resource_id, md5, &cache_entry)) { | |
| 939 // Entry does not exist in cache. | |
| 940 // 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. | |
| 945 dest_path = FilePath::FromUTF8Unsafe(util::kSymLinkToDevNull); | |
| 946 source_path = dest_path; | |
| 947 | |
| 948 // 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; | |
| 951 } else { // File exists in cache, determines destination path. | |
| 952 // 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. | |
| 957 if (cache_entry.is_dirty() || cache_entry.is_mounted()) { | |
| 958 DCHECK(cache_entry.is_persistent()); | |
| 959 dest_path = GetCacheFilePath(resource_id, | |
| 960 md5, | |
| 961 GetSubDirectoryType(cache_entry), | |
| 962 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, | |
| 968 GetSubDirectoryType(cache_entry), | |
| 969 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. | |
| 976 if (!cache_entry.is_present()) { | |
| 977 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 | |
| 1002 if (*error == GDATA_FILE_OK) { | |
| 1003 // Now that file operations have completed, update cache map. | |
| 1004 cache_entry.set_md5(md5); | |
| 1005 cache_entry.set_is_pinned(true); | |
| 1006 cache_entry.set_is_persistent(sub_dir_type == CACHE_TYPE_PERSISTENT); | |
| 1007 metadata_->AddOrUpdateCacheEntry(resource_id, cache_entry); | |
| 1008 } | |
| 1009 } | |
| 1010 | |
| 1011 void GDataCache::Unpin(const std::string& resource_id, | |
| 1012 const std::string& md5, | |
| 1013 FileOperationType file_operation_type, | |
| 1014 GDataFileError* error) { | |
| 1015 AssertOnSequencedWorkerPool(); | |
| 1016 DCHECK(error); | |
| 1017 | |
| 1018 // Unpinning a file means its entry must exist in cache. | |
| 1019 DriveCacheEntry cache_entry; | |
| 1020 if (!GetCacheEntry(resource_id, md5, &cache_entry)) { | |
| 1021 LOG(WARNING) << "Can't unpin a file that wasn't pinned or cached: res_id=" | |
| 1022 << resource_id | |
| 1023 << ", md5=" << md5; | |
| 1024 *error = GDATA_FILE_ERROR_NOT_FOUND; | |
| 1025 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. | |
| 1037 if (cache_entry.is_dirty() || cache_entry.is_mounted()) { | |
| 1038 sub_dir_type = CACHE_TYPE_PERSISTENT; | |
| 1039 DCHECK(cache_entry.is_persistent()); | |
| 1040 dest_path = GetCacheFilePath(resource_id, | |
| 1041 md5, | |
| 1042 GetSubDirectoryType(cache_entry), | |
| 1043 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, | |
| 1049 GetSubDirectoryType(cache_entry), | |
| 1050 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. | |
| 1056 if (!cache_entry.is_present()) { | |
| 1057 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; | |
| 1068 if (cache_entry.is_pinned()) { | |
| 1069 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 | |
| 1082 if (*error == GDATA_FILE_OK) { | |
| 1083 // Now that file operations have completed, update cache map. | |
| 1084 if (cache_entry.is_present()) { | |
| 1085 cache_entry.set_md5(md5); | |
| 1086 cache_entry.set_is_pinned(false); | |
| 1087 cache_entry.set_is_persistent(sub_dir_type == CACHE_TYPE_PERSISTENT); | |
| 1088 metadata_->AddOrUpdateCacheEntry(resource_id, cache_entry); | |
| 1089 } else { | |
| 1090 // Remove the existing entry if we are unpinning a non-present file. | |
| 1091 metadata_->RemoveCacheEntry(resource_id); | |
| 1092 } | |
| 1093 } | |
| 1094 } | |
| 1095 | |
| 1096 void GDataCache::SetMountedState(const FilePath& file_path, | |
| 1097 bool to_mount, | |
| 1098 GDataFileError *error, | |
| 1099 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. | |
| 1110 DCHECK(!to_mount == (extra_extension == util::kMountedArchiveFileExtension)); | |
| 1111 | |
| 1112 // Get cache entry associated with the resource_id and md5 | |
| 1113 DriveCacheEntry cache_entry; | |
| 1114 if (!GetCacheEntry(resource_id, md5, &cache_entry)) { | |
| 1115 *error = GDATA_FILE_ERROR_NOT_FOUND; | |
| 1116 return; | |
| 1117 } | |
| 1118 if (to_mount == cache_entry.is_mounted()) { | |
| 1119 *error = GDATA_FILE_ERROR_INVALID_OPERATION; | |
| 1120 return; | |
| 1121 } | |
| 1122 | |
| 1123 // Get the subdir type and path for the unmounted state. | |
| 1124 CacheSubDirectoryType unmounted_subdir = | |
| 1125 cache_entry.is_pinned() ? CACHE_TYPE_PERSISTENT : CACHE_TYPE_TMP; | |
| 1126 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; | |
| 1137 if (to_mount) { | |
| 1138 source_path = unmounted_path; | |
| 1139 *cache_file_path = mounted_path; | |
| 1140 dest_subdir = mounted_subdir; | |
| 1141 cache_entry.set_is_mounted(true); | |
| 1142 } else { | |
| 1143 source_path = mounted_path; | |
| 1144 *cache_file_path = unmounted_path; | |
| 1145 dest_subdir = unmounted_subdir; | |
| 1146 cache_entry.set_is_mounted(false); | |
| 1147 } | |
| 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); | |
| 1152 if (*error == GDATA_FILE_OK) { | |
| 1153 // Now that cache operation is complete, update cache map | |
| 1154 cache_entry.set_md5(md5); | |
| 1155 cache_entry.set_is_persistent(dest_subdir == CACHE_TYPE_PERSISTENT); | |
| 1156 metadata_->AddOrUpdateCacheEntry(resource_id, cache_entry); | |
| 1157 } | |
| 1158 } | |
| 1159 | |
| 1160 void GDataCache::MarkDirty(const std::string& resource_id, | |
| 1161 const std::string& md5, | |
| 1162 FileOperationType file_operation_type, | |
| 1163 GDataFileError* error, | |
| 1164 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. | |
| 1173 | |
| 1174 // Marking a file dirty means its entry and actual file blob must exist in | |
| 1175 // cache. | |
| 1176 DriveCacheEntry cache_entry; | |
| 1177 if (!GetCacheEntry(resource_id, std::string(), &cache_entry) || | |
| 1178 !cache_entry.is_present()) { | |
| 1179 LOG(WARNING) << "Can't mark dirty a file that wasn't cached: res_id=" | |
| 1180 << resource_id | |
| 1181 << ", md5=" << md5; | |
| 1182 *error = GDATA_FILE_ERROR_NOT_FOUND; | |
| 1183 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. | |
| 1192 if (cache_entry.is_dirty()) { | |
| 1193 // The file must be in persistent dir. | |
| 1194 DCHECK(cache_entry.is_persistent()); | |
| 1195 | |
| 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. | |
| 1214 if (*error == GDATA_FILE_OK) { | |
| 1215 *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, | |
| 1230 GetSubDirectoryType(cache_entry), | |
| 1231 CACHED_FILE_FROM_SERVER); | |
| 1232 | |
| 1233 // Determine destination path. | |
| 1234 const CacheSubDirectoryType sub_dir_type = CACHE_TYPE_PERSISTENT; | |
| 1235 *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; | |
| 1242 if (cache_entry.is_pinned()) { | |
| 1243 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 | |
| 1256 if (*error == GDATA_FILE_OK) { | |
| 1257 // Now that file operations have completed, update cache map. | |
| 1258 cache_entry.set_md5(md5); | |
| 1259 cache_entry.set_is_dirty(true); | |
| 1260 cache_entry.set_is_persistent(sub_dir_type == CACHE_TYPE_PERSISTENT); | |
| 1261 metadata_->AddOrUpdateCacheEntry(resource_id, cache_entry); | |
| 1262 } | |
| 1263 } | |
| 1264 | |
| 1265 void GDataCache::CommitDirty(const std::string& resource_id, | |
| 1266 const std::string& md5, | |
| 1267 FileOperationType file_operation_type, | |
| 1268 GDataFileError* error) { | |
| 1269 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. | |
| 1276 | |
| 1277 // Committing a file dirty means its entry and actual file blob must exist in | |
| 1278 // cache. | |
| 1279 DriveCacheEntry cache_entry; | |
| 1280 if (!GetCacheEntry(resource_id, std::string(), &cache_entry) || | |
| 1281 !cache_entry.is_present()) { | |
| 1282 LOG(WARNING) << "Can't commit dirty a file that wasn't cached: res_id=" | |
| 1283 << resource_id | |
| 1284 << ", md5=" << md5; | |
| 1285 *error = GDATA_FILE_ERROR_NOT_FOUND; | |
| 1286 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. | |
| 1291 if (!cache_entry.is_dirty()) { | |
| 1292 LOG(WARNING) << "Can't commit a non-dirty file: res_id=" | |
| 1293 << resource_id | |
| 1294 << ", md5=" << md5; | |
| 1295 *error = GDATA_FILE_ERROR_INVALID_OPERATION; | |
| 1296 return; | |
| 1297 } | |
| 1298 | |
| 1299 // Dirty files must be in persistent dir. | |
| 1300 DCHECK(cache_entry.is_persistent()); | |
| 1301 | |
| 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, | |
| 1311 GetSubDirectoryType(cache_entry), | |
| 1312 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 | |
| 1324 void GDataCache::ClearDirty(const std::string& resource_id, | |
| 1325 const std::string& md5, | |
| 1326 FileOperationType file_operation_type, | |
| 1327 GDataFileError* error) { | |
| 1328 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. | |
| 1333 DriveCacheEntry cache_entry; | |
| 1334 | |
| 1335 // Clearing a dirty file means its entry and actual file blob must exist in | |
| 1336 // cache. | |
| 1337 if (!GetCacheEntry(resource_id, std::string(), &cache_entry) || | |
| 1338 !cache_entry.is_present()) { | |
| 1339 LOG(WARNING) << "Can't clear dirty state of a file that wasn't cached: " | |
| 1340 << "res_id=" << resource_id | |
| 1341 << ", md5=" << md5; | |
| 1342 *error = GDATA_FILE_ERROR_NOT_FOUND; | |
| 1343 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. | |
| 1348 if (!cache_entry.is_dirty()) { | |
| 1349 LOG(WARNING) << "Can't clear dirty state of a non-dirty file: res_id=" | |
| 1350 << resource_id | |
| 1351 << ", md5=" << md5; | |
| 1352 *error = GDATA_FILE_ERROR_INVALID_OPERATION; | |
| 1353 return; | |
| 1354 } | |
| 1355 | |
| 1356 // File must be dirty and hence in persistent dir. | |
| 1357 DCHECK(cache_entry.is_persistent()); | |
| 1358 | |
| 1359 // Get the current path of the file in cache. | |
| 1360 FilePath source_path = GetCacheFilePath(resource_id, | |
| 1361 md5, | |
| 1362 GetSubDirectoryType(cache_entry), | |
| 1363 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. | |
| 1368 const CacheSubDirectoryType sub_dir_type = | |
| 1369 cache_entry.is_pinned() ? CACHE_TYPE_PERSISTENT : CACHE_TYPE_TMP; | |
| 1370 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. | |
| 1388 if (*error == GDATA_FILE_OK && cache_entry.is_pinned()) { | |
| 1389 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 | |
| 1404 if (*error == GDATA_FILE_OK) { | |
| 1405 // Now that file operations have completed, update cache map. | |
| 1406 cache_entry.set_md5(md5); | |
| 1407 cache_entry.set_is_dirty(false); | |
| 1408 cache_entry.set_is_persistent(sub_dir_type == CACHE_TYPE_PERSISTENT); | |
| 1409 metadata_->AddOrUpdateCacheEntry(resource_id, cache_entry); | |
| 1410 } | |
| 1411 } | |
| 1412 | |
| 1413 void GDataCache::Remove(const std::string& resource_id, | |
| 1414 GDataFileError* error) { | |
| 1415 AssertOnSequencedWorkerPool(); | |
| 1416 DCHECK(error); | |
| 1417 | |
| 1418 // MD5 is not passed into RemoveCacheEntry because we would delete all | |
| 1419 // cache files corresponding to <resource_id> regardless of the md5. | |
| 1420 // So, search for entry in cache without taking md5 into account. | |
| 1421 DriveCacheEntry cache_entry; | |
| 1422 | |
| 1423 // If entry doesn't exist or is dirty or mounted in cache, nothing to do. | |
| 1424 const bool entry_found = | |
| 1425 GetCacheEntry(resource_id, std::string(), &cache_entry); | |
| 1426 if (!entry_found || cache_entry.is_dirty() || cache_entry.is_mounted()) { | |
| 1427 DVLOG(1) << "Entry is " | |
| 1428 << (entry_found ? | |
| 1429 (cache_entry.is_dirty() ? "dirty" : "mounted") : | |
| 1430 "non-existent") | |
| 1431 << " in cache, not removing"; | |
| 1432 *error = GDATA_FILE_OK; | |
| 1433 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, | |
| 1443 util::kWildCard, | |
| 1444 CACHE_TYPE_PERSISTENT, | |
| 1445 CACHED_FILE_FROM_SERVER)); | |
| 1446 paths_to_delete.push_back(GetCacheFilePath(resource_id, | |
| 1447 util::kWildCard, | |
| 1448 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. | |
| 1471 metadata_->RemoveCacheEntry(resource_id); | |
| 1472 | |
| 1473 *error = GDATA_FILE_OK; | |
| 1474 } | |
| 1475 | |
| 1476 void 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 | |
| 1486 void GDataCache::OnPinned(GDataFileError* error, | |
| 1487 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 | |
| 1496 if (*error == GDATA_FILE_OK) | |
| 1497 FOR_EACH_OBSERVER(Observer, observers_, OnCachePinned(resource_id, md5)); | |
| 1498 } | |
| 1499 | |
| 1500 void GDataCache::OnUnpinned(GDataFileError* error, | |
| 1501 const std::string& resource_id, | |
| 1502 const std::string& md5, | |
| 1503 const CacheOperationCallback& callback) { | |
| 1504 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 1505 DCHECK(error); | |
| 1506 | |
| 1507 if (!callback.is_null()) | |
| 1508 callback.Run(*error, resource_id, md5); | |
| 1509 | |
| 1510 if (*error == GDATA_FILE_OK) | |
| 1511 FOR_EACH_OBSERVER(Observer, observers_, OnCacheUnpinned(resource_id, md5)); | |
| 1512 | |
| 1513 // 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); | |
| 1516 blocking_task_runner_->PostTask( | |
| 1517 FROM_HERE, | |
| 1518 base::Bind(&GDataCache::FreeDiskSpaceIfNeededFor, | |
| 1519 base::Unretained(this), | |
| 1520 0, | |
| 1521 base::Owned(has_enough_space))); | |
| 1522 } | |
| 1523 | |
| 1524 void GDataCache::OnCommitDirty(GDataFileError* error, | |
| 1525 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 | |
| 1534 if (*error == GDATA_FILE_OK) | |
| 1535 FOR_EACH_OBSERVER(Observer, observers_, OnCacheCommitted(resource_id)); | |
| 1536 } | |
| 1537 | |
| 1538 void GDataCache::GetCacheEntryHelper(const std::string& resource_id, | |
| 1539 const std::string& md5, | |
| 1540 bool* success, | |
| 1541 DriveCacheEntry* cache_entry) { | |
| 1542 AssertOnSequencedWorkerPool(); | |
| 1543 DCHECK(success); | |
| 1544 DCHECK(cache_entry); | |
| 1545 | |
| 1546 *success = GetCacheEntry(resource_id, md5, cache_entry); | |
| 1547 } | |
| 1548 | |
| 1549 // static | |
| 1550 FilePath 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 | |
| 1558 // static | |
| 1559 std::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 | |
| 1574 bool 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 | |
| 1593 // static | |
| 1594 GDataCache::CacheSubDirectoryType GDataCache::GetSubDirectoryType( | |
| 1595 const DriveCacheEntry& cache_entry) { | |
| 1596 return cache_entry.is_persistent() ? CACHE_TYPE_PERSISTENT : CACHE_TYPE_TMP; | |
| 1597 } | |
| 1598 | |
| 1599 void 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 | |
| 1604 } // namespace gdata | |
| OLD | NEW |