blob: d069d9e72abf270e15da5d78d889e0817415d2a6 [file] [log] [blame]
// 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