Home | History | Annotate | Download | only in drive
      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