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 18namespace file_manager { 19namespace { 20 21typedef base::Callback<void(int64)> GetNecessaryFreeSpaceCallback; 22 23// Part of ComputeSpaceNeedToBeFreed. 24int64 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. 38void 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. 58void 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. 71void 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. 85void 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. 96void FreeReferenceOnIOThread( 97 const std::deque<SnapshotManager::FileReferenceWithSizeInfo>& file_refs) { 98 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); 99} 100 101} // namespace 102 103SnapshotManager::FileReferenceWithSizeInfo::FileReferenceWithSizeInfo( 104 scoped_refptr<storage::ShareableFileReference> ref, 105 int64 size) 106 : file_ref(ref), file_size(size) { 107} 108 109SnapshotManager::FileReferenceWithSizeInfo::~FileReferenceWithSizeInfo() { 110} 111 112SnapshotManager::SnapshotManager(Profile* profile) 113 : profile_(profile), weak_ptr_factory_(this) { 114} 115 116SnapshotManager::~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 126void 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 148void 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 195void 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