// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/chromeos/file_manager/snapshot_manager.h" #include "base/bind.h" #include "base/sys_info.h" #include "chrome/browser/chromeos/file_manager/app_id.h" #include "chrome/browser/chromeos/file_manager/fileapi_util.h" #include "chrome/browser/profiles/profile.h" #include "content/public/browser/browser_thread.h" #include "google_apis/drive/task_util.h" #include "storage/browser/blob/shareable_file_reference.h" #include "storage/browser/fileapi/file_system_context.h" #include "third_party/cros_system_api/constants/cryptohome.h" namespace file_manager { namespace { typedef base::Callback<void(int64)> GetNecessaryFreeSpaceCallback; // Part of ComputeSpaceNeedToBeFreed. int64 ComputeSpaceNeedToBeFreedAfterGetMetadataOnBlockingPool( const base::FilePath& path, int64 snapshot_size) { int64 free_size = base::SysInfo::AmountOfFreeDiskSpace(path); if (free_size < 0) return -1; // We need to keep cryptohome::kMinFreeSpaceInBytes free space even after // |snapshot_size| is occupied. free_size -= snapshot_size + cryptohome::kMinFreeSpaceInBytes; return (free_size < 0 ? -free_size : 0); } // Part of ComputeSpaceNeedToBeFreed. void ComputeSpaceNeedToBeFreedAfterGetMetadata( const base::FilePath& path, const GetNecessaryFreeSpaceCallback& callback, base::File::Error result, const base::File::Info& file_info) { DCHECK_CURRENTLY_ON(content::BrowserThread::IO); if (result != base::File::FILE_OK) { callback.Run(-1); return; } base::PostTaskAndReplyWithResult( content::BrowserThread::GetBlockingPool(), FROM_HERE, base::Bind(&ComputeSpaceNeedToBeFreedAfterGetMetadataOnBlockingPool, path, file_info.size), callback); } // Part of ComputeSpaceNeedToBeFreed. void GetMetadataOnIOThread(const base::FilePath& path, scoped_refptr<storage::FileSystemContext> context, const storage::FileSystemURL& url, const GetNecessaryFreeSpaceCallback& callback) { DCHECK_CURRENTLY_ON(content::BrowserThread::IO); context->operation_runner()->GetMetadata( url, storage::FileSystemOperation::GET_METADATA_FIELD_SIZE, base::Bind(&ComputeSpaceNeedToBeFreedAfterGetMetadata, path, callback)); } // Computes the size of space that need to be __additionally__ made available // in the |profile|'s data directory for taking the snapshot of |url|. // Returns 0 if no additional space is required, or -1 in the case of an error. void ComputeSpaceNeedToBeFreed( Profile* profile, scoped_refptr<storage::FileSystemContext> context, const storage::FileSystemURL& url, const GetNecessaryFreeSpaceCallback& callback) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); content::BrowserThread::PostTask( content::BrowserThread::IO, FROM_HERE, base::Bind(&GetMetadataOnIOThread, profile->GetPath(), context, url, google_apis::CreateRelayCallback(callback))); } // Part of CreateManagedSnapshot. Runs CreateSnapshotFile method of fileapi. void CreateSnapshotFileOnIOThread( scoped_refptr<storage::FileSystemContext> context, const storage::FileSystemURL& url, const storage::FileSystemOperation::SnapshotFileCallback& callback) { DCHECK_CURRENTLY_ON(content::BrowserThread::IO); context->operation_runner()->CreateSnapshotFile(url, callback); } // Utility for destructing the bound |file_refs| on IO thread. This is meant // to be used together with base::Bind. After this function finishes, the // Bind callback should destruct the bound argument. void FreeReferenceOnIOThread( const std::deque<SnapshotManager::FileReferenceWithSizeInfo>& file_refs) { DCHECK_CURRENTLY_ON(content::BrowserThread::IO); } } // namespace SnapshotManager::FileReferenceWithSizeInfo::FileReferenceWithSizeInfo( scoped_refptr<storage::ShareableFileReference> ref, int64 size) : file_ref(ref), file_size(size) { } SnapshotManager::FileReferenceWithSizeInfo::~FileReferenceWithSizeInfo() { } SnapshotManager::SnapshotManager(Profile* profile) : profile_(profile), weak_ptr_factory_(this) { } SnapshotManager::~SnapshotManager() { if (!file_refs_.empty()) { bool posted = content::BrowserThread::PostTask( content::BrowserThread::IO, FROM_HERE, base::Bind(&FreeReferenceOnIOThread, file_refs_)); DCHECK(posted); } } void SnapshotManager::CreateManagedSnapshot( const base::FilePath& absolute_file_path, const LocalPathCallback& callback) { scoped_refptr<storage::FileSystemContext> context( util::GetFileSystemContextForExtensionId(profile_, kFileManagerAppId)); DCHECK(context.get()); GURL url; if (!util::ConvertAbsoluteFilePathToFileSystemUrl( profile_, absolute_file_path, kFileManagerAppId, &url)) { callback.Run(base::FilePath()); return; } storage::FileSystemURL filesystem_url = context->CrackURL(url); ComputeSpaceNeedToBeFreed(profile_, context, filesystem_url, base::Bind(&SnapshotManager::CreateManagedSnapshotAfterSpaceComputed, weak_ptr_factory_.GetWeakPtr(), filesystem_url, callback)); } void SnapshotManager::CreateManagedSnapshotAfterSpaceComputed( const storage::FileSystemURL& filesystem_url, const LocalPathCallback& callback, int64 needed_space) { scoped_refptr<storage::FileSystemContext> context( util::GetFileSystemContextForExtensionId(profile_, kFileManagerAppId)); DCHECK(context.get()); if (needed_space < 0) { callback.Run(base::FilePath()); return; } // Free up to the required size. std::deque<FileReferenceWithSizeInfo> to_free; while (needed_space > 0 && !file_refs_.empty()) { needed_space -= file_refs_.front().file_size; to_free.push_back(file_refs_.front()); file_refs_.pop_front(); } if (!to_free.empty()) { bool posted = content::BrowserThread::PostTask( content::BrowserThread::IO, FROM_HERE, base::Bind(&FreeReferenceOnIOThread, to_free)); DCHECK(posted); } // If we still could not achieve the space requirement, abort with failure. if (needed_space > 0) { callback.Run(base::FilePath()); return; } // Start creating the snapshot. content::BrowserThread::PostTask( content::BrowserThread::IO, FROM_HERE, base::Bind(&CreateSnapshotFileOnIOThread, context, filesystem_url, google_apis::CreateRelayCallback( base::Bind(&SnapshotManager::OnCreateSnapshotFile, weak_ptr_factory_.GetWeakPtr(), callback)))); } void SnapshotManager::OnCreateSnapshotFile( const LocalPathCallback& callback, base::File::Error result, const base::File::Info& file_info, const base::FilePath& platform_path, const scoped_refptr<storage::ShareableFileReference>& file_ref) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); if (result != base::File::FILE_OK) { callback.Run(base::FilePath()); return; } file_refs_.push_back(FileReferenceWithSizeInfo(file_ref, file_info.size)); callback.Run(platform_path); } } // namespace file_manager