Home | History | Annotate | Download | only in file_manager
      1 // Copyright 2014 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/file_manager/snapshot_manager.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/sys_info.h"
      9 #include "chrome/browser/chromeos/file_manager/app_id.h"
     10 #include "chrome/browser/chromeos/file_manager/fileapi_util.h"
     11 #include "chrome/browser/profiles/profile.h"
     12 #include "content/public/browser/browser_thread.h"
     13 #include "google_apis/drive/task_util.h"
     14 #include "storage/browser/fileapi/file_system_context.h"
     15 #include "storage/common/blob/shareable_file_reference.h"
     16 #include "third_party/cros_system_api/constants/cryptohome.h"
     17 
     18 namespace file_manager {
     19 namespace {
     20 
     21 typedef base::Callback<void(int64)> GetNecessaryFreeSpaceCallback;
     22 
     23 // Part of ComputeSpaceNeedToBeFreed.
     24 int64 ComputeSpaceNeedToBeFreedAfterGetMetadataOnBlockingPool(
     25     const base::FilePath& path,
     26     int64 snapshot_size) {
     27   int64 free_size = base::SysInfo::AmountOfFreeDiskSpace(path);
     28   if (free_size < 0)
     29     return -1;
     30 
     31   // We need to keep cryptohome::kMinFreeSpaceInBytes free space even after
     32   // |snapshot_size| is occupied.
     33   free_size -= snapshot_size + cryptohome::kMinFreeSpaceInBytes;
     34   return (free_size < 0 ? -free_size : 0);
     35 }
     36 
     37 // Part of ComputeSpaceNeedToBeFreed.
     38 void ComputeSpaceNeedToBeFreedAfterGetMetadata(
     39     const base::FilePath& path,
     40     const GetNecessaryFreeSpaceCallback& callback,
     41     base::File::Error result,
     42     const base::File::Info& file_info) {
     43   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
     44   if (result != base::File::FILE_OK) {
     45     callback.Run(-1);
     46     return;
     47   }
     48 
     49   base::PostTaskAndReplyWithResult(
     50       content::BrowserThread::GetBlockingPool(),
     51       FROM_HERE,
     52       base::Bind(&ComputeSpaceNeedToBeFreedAfterGetMetadataOnBlockingPool,
     53                  path, file_info.size),
     54       callback);
     55 }
     56 
     57 // Part of ComputeSpaceNeedToBeFreed.
     58 void GetMetadataOnIOThread(const base::FilePath& path,
     59                            scoped_refptr<storage::FileSystemContext> context,
     60                            const storage::FileSystemURL& url,
     61                            const GetNecessaryFreeSpaceCallback& callback) {
     62   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
     63   context->operation_runner()->GetMetadata(
     64       url,
     65       base::Bind(&ComputeSpaceNeedToBeFreedAfterGetMetadata, path, callback));
     66 }
     67 
     68 // Computes the size of space that need to be __additionally__ made available
     69 // in the |profile|'s data directory for taking the snapshot of |url|.
     70 // Returns 0 if no additional space is required, or -1 in the case of an error.
     71 void ComputeSpaceNeedToBeFreed(
     72     Profile* profile,
     73     scoped_refptr<storage::FileSystemContext> context,
     74     const storage::FileSystemURL& url,
     75     const GetNecessaryFreeSpaceCallback& callback) {
     76   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
     77   content::BrowserThread::PostTask(
     78       content::BrowserThread::IO,
     79       FROM_HERE,
     80       base::Bind(&GetMetadataOnIOThread, profile->GetPath(), context, url,
     81                  google_apis::CreateRelayCallback(callback)));
     82 }
     83 
     84 // Part of CreateManagedSnapshot. Runs CreateSnapshotFile method of fileapi.
     85 void CreateSnapshotFileOnIOThread(
     86     scoped_refptr<storage::FileSystemContext> context,
     87     const storage::FileSystemURL& url,
     88     const storage::FileSystemOperation::SnapshotFileCallback& callback) {
     89   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
     90   context->operation_runner()->CreateSnapshotFile(url, callback);
     91 }
     92 
     93 // Utility for destructing the bound |file_refs| on IO thread. This is meant
     94 // to be used together with base::Bind. After this function finishes, the
     95 // Bind callback should destruct the bound argument.
     96 void FreeReferenceOnIOThread(
     97     const std::deque<SnapshotManager::FileReferenceWithSizeInfo>& file_refs) {
     98   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
     99 }
    100 
    101 }  // namespace
    102 
    103 SnapshotManager::FileReferenceWithSizeInfo::FileReferenceWithSizeInfo(
    104     scoped_refptr<storage::ShareableFileReference> ref,
    105     int64 size)
    106     : file_ref(ref), file_size(size) {
    107 }
    108 
    109 SnapshotManager::FileReferenceWithSizeInfo::~FileReferenceWithSizeInfo() {
    110 }
    111 
    112 SnapshotManager::SnapshotManager(Profile* profile)
    113     : profile_(profile), weak_ptr_factory_(this) {
    114 }
    115 
    116 SnapshotManager::~SnapshotManager() {
    117   if (!file_refs_.empty()) {
    118     bool posted = content::BrowserThread::PostTask(
    119         content::BrowserThread::IO,
    120         FROM_HERE,
    121         base::Bind(&FreeReferenceOnIOThread, file_refs_));
    122     DCHECK(posted);
    123   }
    124 }
    125 
    126 void SnapshotManager::CreateManagedSnapshot(
    127     const base::FilePath& absolute_file_path,
    128     const LocalPathCallback& callback) {
    129   scoped_refptr<storage::FileSystemContext> context(
    130       util::GetFileSystemContextForExtensionId(profile_, kFileManagerAppId));
    131   DCHECK(context.get());
    132 
    133   GURL url;
    134   if (!util::ConvertAbsoluteFilePathToFileSystemUrl(
    135           profile_, absolute_file_path, kFileManagerAppId, &url)) {
    136     callback.Run(base::FilePath());
    137     return;
    138   }
    139   storage::FileSystemURL filesystem_url = context->CrackURL(url);
    140 
    141   ComputeSpaceNeedToBeFreed(profile_, context, filesystem_url,
    142       base::Bind(&SnapshotManager::CreateManagedSnapshotAfterSpaceComputed,
    143                  weak_ptr_factory_.GetWeakPtr(),
    144                  filesystem_url,
    145                  callback));
    146 }
    147 
    148 void SnapshotManager::CreateManagedSnapshotAfterSpaceComputed(
    149     const storage::FileSystemURL& filesystem_url,
    150     const LocalPathCallback& callback,
    151     int64 needed_space) {
    152   scoped_refptr<storage::FileSystemContext> context(
    153       util::GetFileSystemContextForExtensionId(profile_, kFileManagerAppId));
    154   DCHECK(context.get());
    155 
    156   if (needed_space < 0) {
    157     callback.Run(base::FilePath());
    158     return;
    159   }
    160 
    161   // Free up to the required size.
    162   std::deque<FileReferenceWithSizeInfo> to_free;
    163   while (needed_space > 0 && !file_refs_.empty()) {
    164     needed_space -= file_refs_.front().file_size;
    165     to_free.push_back(file_refs_.front());
    166     file_refs_.pop_front();
    167   }
    168   if (!to_free.empty()) {
    169     bool posted = content::BrowserThread::PostTask(
    170         content::BrowserThread::IO,
    171         FROM_HERE,
    172         base::Bind(&FreeReferenceOnIOThread, to_free));
    173     DCHECK(posted);
    174   }
    175 
    176   // If we still could not achieve the space requirement, abort with failure.
    177   if (needed_space > 0) {
    178     callback.Run(base::FilePath());
    179     return;
    180   }
    181 
    182   // Start creating the snapshot.
    183   content::BrowserThread::PostTask(
    184       content::BrowserThread::IO,
    185       FROM_HERE,
    186       base::Bind(&CreateSnapshotFileOnIOThread,
    187                  context,
    188                  filesystem_url,
    189                  google_apis::CreateRelayCallback(
    190                      base::Bind(&SnapshotManager::OnCreateSnapshotFile,
    191                                 weak_ptr_factory_.GetWeakPtr(),
    192                                 callback))));
    193 }
    194 
    195 void SnapshotManager::OnCreateSnapshotFile(
    196     const LocalPathCallback& callback,
    197     base::File::Error result,
    198     const base::File::Info& file_info,
    199     const base::FilePath& platform_path,
    200     const scoped_refptr<storage::ShareableFileReference>& file_ref) {
    201   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
    202 
    203   if (result != base::File::FILE_OK) {
    204     callback.Run(base::FilePath());
    205     return;
    206   }
    207 
    208   file_refs_.push_back(FileReferenceWithSizeInfo(file_ref, file_info.size));
    209   callback.Run(platform_path);
    210 }
    211 
    212 }  // namespace file_manager
    213