file_cache.cc revision bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3
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/drive/file_cache.h" 6 7#include <vector> 8 9#include "base/file_util.h" 10#include "base/files/file_enumerator.h" 11#include "base/logging.h" 12#include "base/strings/string_util.h" 13#include "base/strings/stringprintf.h" 14#include "base/sys_info.h" 15#include "base/task_runner_util.h" 16#include "chrome/browser/chromeos/drive/drive.pb.h" 17#include "chrome/browser/chromeos/drive/file_cache_metadata.h" 18#include "chrome/browser/chromeos/drive/file_system_util.h" 19#include "chrome/browser/chromeos/drive/resource_metadata_storage.h" 20#include "chrome/browser/google_apis/task_util.h" 21#include "chromeos/chromeos_constants.h" 22#include "content/public/browser/browser_thread.h" 23 24using content::BrowserThread; 25 26namespace drive { 27namespace internal { 28namespace { 29 30typedef std::map<std::string, FileCacheEntry> CacheMap; 31 32// Returns true if |md5| matches the one in |cache_entry| with some 33// exceptions. See the function definition for details. 34bool CheckIfMd5Matches(const std::string& md5, 35 const FileCacheEntry& cache_entry) { 36 if (cache_entry.is_dirty()) { 37 // If the entry is dirty, its MD5 may have been replaced by "local" 38 // during cache initialization, so we don't compare MD5. 39 return true; 40 } else if (cache_entry.is_pinned() && cache_entry.md5().empty()) { 41 // If the entry is pinned, it's ok for the entry to have an empty 42 // MD5. This can happen if the pinned file is not fetched. 43 return true; 44 } else if (md5.empty()) { 45 // If the MD5 matching is not requested, don't check MD5. 46 return true; 47 } else if (md5 == cache_entry.md5()) { 48 // Otherwise, compare the MD5. 49 return true; 50 } 51 return false; 52} 53 54// Returns resource ID extracted from the path. 55std::string GetResourceIdFromPath(const base::FilePath& path) { 56 return util::UnescapeCacheFileName(path.BaseName().AsUTF8Unsafe()); 57} 58 59// Scans cache subdirectory and insert found files to |cache_map|. 60void ScanCacheDirectory(const base::FilePath& directory_path, 61 CacheMap* cache_map) { 62 base::FileEnumerator enumerator(directory_path, 63 false, // not recursive 64 base::FileEnumerator::FILES); 65 for (base::FilePath current = enumerator.Next(); !current.empty(); 66 current = enumerator.Next()) { 67 std::string resource_id = GetResourceIdFromPath(current); 68 69 // Calculate MD5. 70 std::string md5 = util::GetMd5Digest(current); 71 if (md5.empty()) 72 continue; 73 74 // Determine cache state. 75 FileCacheEntry cache_entry; 76 cache_entry.set_md5(md5); 77 cache_entry.set_is_present(true); 78 79 // Create and insert new entry into cache map. 80 cache_map->insert(std::make_pair(resource_id, cache_entry)); 81 } 82} 83 84// Runs callback with pointers dereferenced. 85// Used to implement GetFile, MarkAsMounted. 86void RunGetFileFromCacheCallback(const GetFileFromCacheCallback& callback, 87 base::FilePath* file_path, 88 FileError error) { 89 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 90 DCHECK(!callback.is_null()); 91 DCHECK(file_path); 92 93 callback.Run(error, *file_path); 94} 95 96// Runs callback with pointers dereferenced. 97// Used to implement GetCacheEntry(). 98void RunGetCacheEntryCallback(const GetCacheEntryCallback& callback, 99 FileCacheEntry* cache_entry, 100 bool success) { 101 DCHECK(cache_entry); 102 DCHECK(!callback.is_null()); 103 callback.Run(success, *cache_entry); 104} 105 106// Calls |iteration_callback| with each entry in |cache|. 107void IterateCache(FileCache* cache, 108 const CacheIterateCallback& iteration_callback) { 109 scoped_ptr<FileCache::Iterator> it = cache->GetIterator(); 110 for (; !it->IsAtEnd(); it->Advance()) 111 iteration_callback.Run(it->GetID(), it->GetValue()); 112 DCHECK(!it->HasError()); 113} 114 115} // namespace 116 117const base::FilePath::CharType FileCache::kOldCacheMetadataDBName[] = 118 FILE_PATH_LITERAL("cache_metadata.db"); 119 120FileCache::FileCache(ResourceMetadataStorage* storage, 121 const base::FilePath& cache_file_directory, 122 base::SequencedTaskRunner* blocking_task_runner, 123 FreeDiskSpaceGetterInterface* free_disk_space_getter) 124 : cache_file_directory_(cache_file_directory), 125 blocking_task_runner_(blocking_task_runner), 126 storage_(storage), 127 free_disk_space_getter_(free_disk_space_getter), 128 weak_ptr_factory_(this) { 129 DCHECK(blocking_task_runner_.get()); 130 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 131} 132 133FileCache::~FileCache() { 134 // Must be on the sequenced worker pool, as |metadata_| must be deleted on 135 // the sequenced worker pool. 136 AssertOnSequencedWorkerPool(); 137} 138 139base::FilePath FileCache::GetCacheFilePath( 140 const std::string& resource_id) const { 141 return cache_file_directory_.Append( 142 base::FilePath::FromUTF8Unsafe(util::EscapeCacheFileName(resource_id))); 143} 144 145void FileCache::AssertOnSequencedWorkerPool() { 146 DCHECK(!blocking_task_runner_.get() || 147 blocking_task_runner_->RunsTasksOnCurrentThread()); 148} 149 150bool FileCache::IsUnderFileCacheDirectory(const base::FilePath& path) const { 151 return cache_file_directory_.IsParent(path); 152} 153 154void FileCache::GetCacheEntryOnUIThread(const std::string& resource_id, 155 const std::string& md5, 156 const GetCacheEntryCallback& callback) { 157 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 158 DCHECK(!callback.is_null()); 159 160 FileCacheEntry* cache_entry = new FileCacheEntry; 161 base::PostTaskAndReplyWithResult( 162 blocking_task_runner_.get(), 163 FROM_HERE, 164 base::Bind(&FileCache::GetCacheEntry, 165 base::Unretained(this), 166 resource_id, 167 md5, 168 cache_entry), 169 base::Bind( 170 &RunGetCacheEntryCallback, callback, base::Owned(cache_entry))); 171} 172 173bool FileCache::GetCacheEntry(const std::string& resource_id, 174 const std::string& md5, 175 FileCacheEntry* entry) { 176 DCHECK(entry); 177 AssertOnSequencedWorkerPool(); 178 return storage_->GetCacheEntry(resource_id, entry) && 179 CheckIfMd5Matches(md5, *entry); 180} 181 182void FileCache::IterateOnUIThread( 183 const CacheIterateCallback& iteration_callback, 184 const base::Closure& completion_callback) { 185 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 186 DCHECK(!iteration_callback.is_null()); 187 DCHECK(!completion_callback.is_null()); 188 189 blocking_task_runner_->PostTaskAndReply( 190 FROM_HERE, 191 base::Bind(&IterateCache, 192 base::Unretained(this), 193 google_apis::CreateRelayCallback(iteration_callback)), 194 completion_callback); 195} 196 197scoped_ptr<FileCache::Iterator> FileCache::GetIterator() { 198 AssertOnSequencedWorkerPool(); 199 return storage_->GetCacheEntryIterator(); 200} 201 202bool FileCache::FreeDiskSpaceIfNeededFor(int64 num_bytes) { 203 AssertOnSequencedWorkerPool(); 204 205 // Do nothing and return if we have enough space. 206 if (HasEnoughSpaceFor(num_bytes, cache_file_directory_)) 207 return true; 208 209 // Otherwise, try to free up the disk space. 210 DVLOG(1) << "Freeing up disk space for " << num_bytes; 211 212 // Remove all entries unless specially marked. 213 scoped_ptr<ResourceMetadataStorage::CacheEntryIterator> it = 214 storage_->GetCacheEntryIterator(); 215 for (; !it->IsAtEnd(); it->Advance()) { 216 const FileCacheEntry& entry = it->GetValue(); 217 if (!entry.is_pinned() && 218 !entry.is_dirty() && 219 !mounted_files_.count(it->GetID())) 220 storage_->RemoveCacheEntry(it->GetID()); 221 } 222 DCHECK(!it->HasError()); 223 224 // Remove all files which have no corresponding cache entries. 225 base::FileEnumerator enumerator(cache_file_directory_, 226 false, // not recursive 227 base::FileEnumerator::FILES); 228 FileCacheEntry entry; 229 for (base::FilePath current = enumerator.Next(); !current.empty(); 230 current = enumerator.Next()) { 231 std::string resource_id = GetResourceIdFromPath(current); 232 if (!storage_->GetCacheEntry(resource_id, &entry)) 233 base::DeleteFile(current, false /* recursive */); 234 } 235 236 // Check the disk space again. 237 return HasEnoughSpaceFor(num_bytes, cache_file_directory_); 238} 239 240void FileCache::GetFileOnUIThread(const std::string& resource_id, 241 const std::string& md5, 242 const GetFileFromCacheCallback& callback) { 243 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 244 DCHECK(!callback.is_null()); 245 246 base::FilePath* cache_file_path = new base::FilePath; 247 base::PostTaskAndReplyWithResult(blocking_task_runner_.get(), 248 FROM_HERE, 249 base::Bind(&FileCache::GetFile, 250 base::Unretained(this), 251 resource_id, 252 md5, 253 cache_file_path), 254 base::Bind(&RunGetFileFromCacheCallback, 255 callback, 256 base::Owned(cache_file_path))); 257} 258 259FileError FileCache::GetFile(const std::string& resource_id, 260 const std::string& md5, 261 base::FilePath* cache_file_path) { 262 AssertOnSequencedWorkerPool(); 263 DCHECK(cache_file_path); 264 265 FileCacheEntry cache_entry; 266 if (!GetCacheEntry(resource_id, md5, &cache_entry) || 267 !cache_entry.is_present()) 268 return FILE_ERROR_NOT_FOUND; 269 270 *cache_file_path = GetCacheFilePath(resource_id); 271 return FILE_ERROR_OK; 272} 273 274void FileCache::StoreOnUIThread(const std::string& resource_id, 275 const std::string& md5, 276 const base::FilePath& source_path, 277 FileOperationType file_operation_type, 278 const FileOperationCallback& callback) { 279 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 280 DCHECK(!callback.is_null()); 281 282 base::PostTaskAndReplyWithResult(blocking_task_runner_.get(), 283 FROM_HERE, 284 base::Bind(&FileCache::Store, 285 base::Unretained(this), 286 resource_id, 287 md5, 288 source_path, 289 file_operation_type), 290 callback); 291} 292 293FileError FileCache::Store(const std::string& resource_id, 294 const std::string& md5, 295 const base::FilePath& source_path, 296 FileOperationType file_operation_type) { 297 AssertOnSequencedWorkerPool(); 298 return StoreInternal(resource_id, md5, source_path, file_operation_type); 299} 300 301void FileCache::PinOnUIThread(const std::string& resource_id, 302 const FileOperationCallback& callback) { 303 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 304 DCHECK(!callback.is_null()); 305 306 base::PostTaskAndReplyWithResult( 307 blocking_task_runner_.get(), 308 FROM_HERE, 309 base::Bind(&FileCache::Pin, base::Unretained(this), resource_id), 310 callback); 311} 312 313FileError FileCache::Pin(const std::string& resource_id) { 314 AssertOnSequencedWorkerPool(); 315 316 FileCacheEntry cache_entry; 317 storage_->GetCacheEntry(resource_id, &cache_entry); 318 cache_entry.set_is_pinned(true); 319 return storage_->PutCacheEntry(resource_id, cache_entry) ? 320 FILE_ERROR_OK : FILE_ERROR_FAILED; 321} 322 323void FileCache::UnpinOnUIThread(const std::string& resource_id, 324 const FileOperationCallback& callback) { 325 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 326 DCHECK(!callback.is_null()); 327 328 base::PostTaskAndReplyWithResult( 329 blocking_task_runner_.get(), 330 FROM_HERE, 331 base::Bind(&FileCache::Unpin, base::Unretained(this), resource_id), 332 callback); 333} 334 335FileError FileCache::Unpin(const std::string& resource_id) { 336 AssertOnSequencedWorkerPool(); 337 338 // Unpinning a file means its entry must exist in cache. 339 FileCacheEntry cache_entry; 340 if (!storage_->GetCacheEntry(resource_id, &cache_entry)) 341 return FILE_ERROR_NOT_FOUND; 342 343 // Now that file operations have completed, update metadata. 344 if (cache_entry.is_present()) { 345 cache_entry.set_is_pinned(false); 346 if (!storage_->PutCacheEntry(resource_id, cache_entry)) 347 return FILE_ERROR_FAILED; 348 } else { 349 // Remove the existing entry if we are unpinning a non-present file. 350 if (!storage_->RemoveCacheEntry(resource_id)) 351 return FILE_ERROR_FAILED; 352 } 353 354 // Now it's a chance to free up space if needed. 355 FreeDiskSpaceIfNeededFor(0); 356 357 return FILE_ERROR_OK; 358} 359 360void FileCache::MarkAsMountedOnUIThread( 361 const std::string& resource_id, 362 const GetFileFromCacheCallback& callback) { 363 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 364 DCHECK(!callback.is_null()); 365 366 base::FilePath* cache_file_path = new base::FilePath; 367 base::PostTaskAndReplyWithResult( 368 blocking_task_runner_.get(), 369 FROM_HERE, 370 base::Bind(&FileCache::MarkAsMounted, 371 base::Unretained(this), 372 resource_id, 373 cache_file_path), 374 base::Bind( 375 RunGetFileFromCacheCallback, callback, base::Owned(cache_file_path))); 376} 377 378void FileCache::MarkAsUnmountedOnUIThread( 379 const base::FilePath& file_path, 380 const FileOperationCallback& callback) { 381 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 382 DCHECK(!callback.is_null()); 383 384 base::PostTaskAndReplyWithResult( 385 blocking_task_runner_.get(), 386 FROM_HERE, 387 base::Bind( 388 &FileCache::MarkAsUnmounted, base::Unretained(this), file_path), 389 callback); 390} 391 392void FileCache::MarkDirtyOnUIThread(const std::string& resource_id, 393 const FileOperationCallback& callback) { 394 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 395 DCHECK(!callback.is_null()); 396 397 base::PostTaskAndReplyWithResult( 398 blocking_task_runner_.get(), 399 FROM_HERE, 400 base::Bind(&FileCache::MarkDirty, base::Unretained(this), resource_id), 401 callback); 402} 403 404FileError FileCache::MarkDirty(const std::string& resource_id) { 405 AssertOnSequencedWorkerPool(); 406 407 // Marking a file dirty means its entry and actual file blob must exist in 408 // cache. 409 FileCacheEntry cache_entry; 410 if (!storage_->GetCacheEntry(resource_id, &cache_entry) || 411 !cache_entry.is_present()) { 412 LOG(WARNING) << "Can't mark dirty a file that wasn't cached: " 413 << resource_id; 414 return FILE_ERROR_NOT_FOUND; 415 } 416 417 if (cache_entry.is_dirty()) 418 return FILE_ERROR_OK; 419 420 // Now that file operations have completed, update metadata. 421 cache_entry.set_is_dirty(true); 422 return storage_->PutCacheEntry(resource_id, cache_entry) ? 423 FILE_ERROR_OK : FILE_ERROR_FAILED; 424} 425 426FileError FileCache::ClearDirty(const std::string& resource_id, 427 const std::string& md5) { 428 AssertOnSequencedWorkerPool(); 429 430 // |md5| is the new .<md5> extension to rename the file to. 431 // So, search for entry in cache without comparing md5. 432 FileCacheEntry cache_entry; 433 434 // Clearing a dirty file means its entry and actual file blob must exist in 435 // cache. 436 if (!storage_->GetCacheEntry(resource_id, &cache_entry) || 437 !cache_entry.is_present()) { 438 LOG(WARNING) << "Can't clear dirty state of a file that wasn't cached: " 439 << "res_id=" << resource_id 440 << ", md5=" << md5; 441 return FILE_ERROR_NOT_FOUND; 442 } 443 444 // If a file is not dirty (it should have been marked dirty via 445 // MarkDirtyInCache), clearing its dirty state is an invalid operation. 446 if (!cache_entry.is_dirty()) { 447 LOG(WARNING) << "Can't clear dirty state of a non-dirty file: res_id=" 448 << resource_id 449 << ", md5=" << md5; 450 return FILE_ERROR_INVALID_OPERATION; 451 } 452 453 // Now that file operations have completed, update metadata. 454 cache_entry.set_md5(md5); 455 cache_entry.set_is_dirty(false); 456 return storage_->PutCacheEntry(resource_id, cache_entry) ? 457 FILE_ERROR_OK : FILE_ERROR_FAILED; 458} 459 460void FileCache::RemoveOnUIThread(const std::string& resource_id, 461 const FileOperationCallback& callback) { 462 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 463 DCHECK(!callback.is_null()); 464 465 base::PostTaskAndReplyWithResult( 466 blocking_task_runner_.get(), 467 FROM_HERE, 468 base::Bind(&FileCache::Remove, base::Unretained(this), resource_id), 469 callback); 470} 471 472FileError FileCache::Remove(const std::string& resource_id) { 473 AssertOnSequencedWorkerPool(); 474 475 FileCacheEntry cache_entry; 476 477 // If entry doesn't exist, nothing to do. 478 if (!storage_->GetCacheEntry(resource_id, &cache_entry)) 479 return FILE_ERROR_OK; 480 481 // Cannot delete a mounted file. 482 if (mounted_files_.count(resource_id)) 483 return FILE_ERROR_IN_USE; 484 485 // Delete the file. 486 base::FilePath path = GetCacheFilePath(resource_id); 487 if (!base::DeleteFile(path, false /* recursive */)) 488 return FILE_ERROR_FAILED; 489 490 // Now that all file operations have completed, remove from metadata. 491 return storage_->RemoveCacheEntry(resource_id) ? 492 FILE_ERROR_OK : FILE_ERROR_FAILED; 493} 494 495void FileCache::ClearAllOnUIThread(const ClearAllCallback& callback) { 496 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 497 DCHECK(!callback.is_null()); 498 499 base::PostTaskAndReplyWithResult( 500 blocking_task_runner_.get(), 501 FROM_HERE, 502 base::Bind(&FileCache::ClearAll, base::Unretained(this)), 503 callback); 504} 505 506bool FileCache::Initialize() { 507 AssertOnSequencedWorkerPool(); 508 509 RenameCacheFilesToNewFormat(); 510 511 if (!ImportOldDB(storage_->directory_path().Append( 512 kOldCacheMetadataDBName)) && 513 !storage_->opened_existing_db()) { 514 CacheMap cache_map; 515 ScanCacheDirectory(cache_file_directory_, &cache_map); 516 for (CacheMap::const_iterator it = cache_map.begin(); 517 it != cache_map.end(); ++it) { 518 storage_->PutCacheEntry(it->first, it->second); 519 } 520 } 521 return true; 522} 523 524void FileCache::Destroy() { 525 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 526 527 // Invalidate the weak pointer. 528 weak_ptr_factory_.InvalidateWeakPtrs(); 529 530 // Destroy myself on the blocking pool. 531 // Note that base::DeletePointer<> cannot be used as the destructor of this 532 // class is private. 533 blocking_task_runner_->PostTask( 534 FROM_HERE, 535 base::Bind(&FileCache::DestroyOnBlockingPool, base::Unretained(this))); 536} 537 538void FileCache::DestroyOnBlockingPool() { 539 AssertOnSequencedWorkerPool(); 540 delete this; 541} 542 543FileError FileCache::StoreInternal(const std::string& resource_id, 544 const std::string& md5, 545 const base::FilePath& source_path, 546 FileOperationType file_operation_type) { 547 AssertOnSequencedWorkerPool(); 548 549 int64 file_size = 0; 550 if (file_operation_type == FILE_OPERATION_COPY) { 551 if (!file_util::GetFileSize(source_path, &file_size)) { 552 LOG(WARNING) << "Couldn't get file size for: " << source_path.value(); 553 return FILE_ERROR_FAILED; 554 } 555 } 556 if (!FreeDiskSpaceIfNeededFor(file_size)) 557 return FILE_ERROR_NO_SPACE; 558 559 FileCacheEntry cache_entry; 560 storage_->GetCacheEntry(resource_id, &cache_entry); 561 562 // If file is dirty or mounted, return error. 563 if (cache_entry.is_dirty() || mounted_files_.count(resource_id)) 564 return FILE_ERROR_IN_USE; 565 566 base::FilePath dest_path = GetCacheFilePath(resource_id); 567 bool success = false; 568 switch (file_operation_type) { 569 case FILE_OPERATION_MOVE: 570 success = base::Move(source_path, dest_path); 571 break; 572 case FILE_OPERATION_COPY: 573 success = base::CopyFile(source_path, dest_path); 574 break; 575 default: 576 NOTREACHED(); 577 } 578 579 if (!success) { 580 LOG(ERROR) << "Failed to store: " 581 << "source_path = " << source_path.value() << ", " 582 << "dest_path = " << dest_path.value() << ", " 583 << "file_operation_type = " << file_operation_type; 584 return FILE_ERROR_FAILED; 585 } 586 587 // Now that file operations have completed, update metadata. 588 cache_entry.set_md5(md5); 589 cache_entry.set_is_present(true); 590 cache_entry.set_is_dirty(false); 591 return storage_->PutCacheEntry(resource_id, cache_entry) ? 592 FILE_ERROR_OK : FILE_ERROR_FAILED; 593} 594 595FileError FileCache::MarkAsMounted(const std::string& resource_id, 596 base::FilePath* cache_file_path) { 597 AssertOnSequencedWorkerPool(); 598 DCHECK(cache_file_path); 599 600 // Get cache entry associated with the resource_id and md5 601 FileCacheEntry cache_entry; 602 if (!storage_->GetCacheEntry(resource_id, &cache_entry)) 603 return FILE_ERROR_NOT_FOUND; 604 605 if (mounted_files_.count(resource_id)) 606 return FILE_ERROR_INVALID_OPERATION; 607 608 // Ensure the file is readable to cros_disks. See crbug.com/236994. 609 base::FilePath path = GetCacheFilePath(resource_id); 610 if (!file_util::SetPosixFilePermissions( 611 path, 612 file_util::FILE_PERMISSION_READ_BY_USER | 613 file_util::FILE_PERMISSION_WRITE_BY_USER | 614 file_util::FILE_PERMISSION_READ_BY_GROUP | 615 file_util::FILE_PERMISSION_READ_BY_OTHERS)) 616 return FILE_ERROR_FAILED; 617 618 mounted_files_.insert(resource_id); 619 620 *cache_file_path = path; 621 return FILE_ERROR_OK; 622} 623 624FileError FileCache::MarkAsUnmounted(const base::FilePath& file_path) { 625 AssertOnSequencedWorkerPool(); 626 DCHECK(IsUnderFileCacheDirectory(file_path)); 627 628 std::string resource_id = GetResourceIdFromPath(file_path); 629 630 // Get cache entry associated with the resource_id and md5 631 FileCacheEntry cache_entry; 632 if (!storage_->GetCacheEntry(resource_id, &cache_entry)) 633 return FILE_ERROR_NOT_FOUND; 634 635 std::set<std::string>::iterator it = mounted_files_.find(resource_id); 636 if (it == mounted_files_.end()) 637 return FILE_ERROR_INVALID_OPERATION; 638 639 mounted_files_.erase(it); 640 return FILE_ERROR_OK; 641} 642 643bool FileCache::ClearAll() { 644 AssertOnSequencedWorkerPool(); 645 646 // Remove entries on the metadata. 647 scoped_ptr<ResourceMetadataStorage::CacheEntryIterator> it = 648 storage_->GetCacheEntryIterator(); 649 for (; !it->IsAtEnd(); it->Advance()) { 650 if (!storage_->RemoveCacheEntry(it->GetID())) 651 return false; 652 } 653 654 if (it->HasError()) 655 return false; 656 657 // Remove files. 658 base::FileEnumerator enumerator(cache_file_directory_, 659 false, // not recursive 660 base::FileEnumerator::FILES); 661 for (base::FilePath file = enumerator.Next(); !file.empty(); 662 file = enumerator.Next()) 663 base::DeleteFile(file, false /* recursive */); 664 665 return true; 666} 667 668bool FileCache::HasEnoughSpaceFor(int64 num_bytes, 669 const base::FilePath& path) { 670 int64 free_space = 0; 671 if (free_disk_space_getter_) 672 free_space = free_disk_space_getter_->AmountOfFreeDiskSpace(); 673 else 674 free_space = base::SysInfo::AmountOfFreeDiskSpace(path); 675 676 // Subtract this as if this portion does not exist. 677 free_space -= kMinFreeSpace; 678 return (free_space >= num_bytes); 679} 680 681bool FileCache::ImportOldDB(const base::FilePath& old_db_path) { 682 if (!base::PathExists(old_db_path)) // Old DB is not there, do nothing. 683 return false; 684 685 // Copy all entries stored in the old DB. 686 bool imported = false; 687 { 688 FileCacheMetadata old_data(blocking_task_runner_.get()); 689 if (old_data.Initialize(old_db_path) == 690 FileCacheMetadata::INITIALIZE_OPENED) { 691 scoped_ptr<FileCacheMetadata::Iterator> it = old_data.GetIterator(); 692 for (; !it->IsAtEnd(); it->Advance()) { 693 FileCacheEntry entry; 694 if (storage_->GetCacheEntry(it->GetKey(), &entry)) 695 continue; // Do not overwrite. 696 697 storage_->PutCacheEntry(it->GetKey(), it->GetValue()); 698 } 699 imported = true; 700 } 701 } 702 703 // Delete old DB. 704 base::DeleteFile(old_db_path, true /* recursive */ ); 705 return imported; 706} 707 708void FileCache::RenameCacheFilesToNewFormat() { 709 // First, remove all files with multiple extensions just in case. 710 { 711 base::FileEnumerator enumerator(cache_file_directory_, 712 false, // not recursive 713 base::FileEnumerator::FILES, 714 "*.*.*"); 715 for (base::FilePath current = enumerator.Next(); !current.empty(); 716 current = enumerator.Next()) 717 base::DeleteFile(current, false /* recursive */); 718 } 719 720 // Rename files. 721 { 722 base::FileEnumerator enumerator(cache_file_directory_, 723 false, // not recursive 724 base::FileEnumerator::FILES); 725 for (base::FilePath current = enumerator.Next(); !current.empty(); 726 current = enumerator.Next()) 727 base::Move(current, current.RemoveExtension()); 728 } 729} 730 731} // namespace internal 732} // namespace drive 733