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