blob: 665d7f040de4c8738678b057c5220b8a39b2bd71 [file] [log] [blame]
// Copyright 2013 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 "storage/browser/fileapi/file_system_operation_runner.h"
#include <stdint.h>
#include <memory>
#include <tuple>
#include <utility>
#include "base/bind.h"
#include "base/macros.h"
#include "base/stl_util.h"
#include "base/thread_task_runner_handle.h"
#include "net/url_request/url_request_context.h"
#include "storage/browser/blob/blob_url_request_job_factory.h"
#include "storage/browser/blob/shareable_file_reference.h"
#include "storage/browser/fileapi/file_observers.h"
#include "storage/browser/fileapi/file_stream_writer.h"
#include "storage/browser/fileapi/file_system_context.h"
#include "storage/browser/fileapi/file_writer_delegate.h"
namespace storage {
typedef FileSystemOperationRunner::OperationID OperationID;
class FileSystemOperationRunner::BeginOperationScoper
: public base::SupportsWeakPtr<
FileSystemOperationRunner::BeginOperationScoper> {
public:
BeginOperationScoper() {}
private:
DISALLOW_COPY_AND_ASSIGN(BeginOperationScoper);
};
FileSystemOperationRunner::OperationHandle::OperationHandle() {}
FileSystemOperationRunner::OperationHandle::OperationHandle(
const OperationHandle& other) = default;
FileSystemOperationRunner::OperationHandle::~OperationHandle() {}
FileSystemOperationRunner::~FileSystemOperationRunner() {
}
void FileSystemOperationRunner::Shutdown() {
operations_.Clear();
}
OperationID FileSystemOperationRunner::CreateFile(
const FileSystemURL& url,
bool exclusive,
const StatusCallback& callback) {
base::File::Error error = base::File::FILE_OK;
FileSystemOperation* operation =
file_system_context_->CreateFileSystemOperation(url, &error);
BeginOperationScoper scope;
OperationHandle handle = BeginOperation(operation, scope.AsWeakPtr());
if (!operation) {
DidFinish(handle, callback, error);
return handle.id;
}
PrepareForWrite(handle.id, url);
operation->CreateFile(
url, exclusive,
base::Bind(&FileSystemOperationRunner::DidFinish, AsWeakPtr(),
handle, callback));
return handle.id;
}
OperationID FileSystemOperationRunner::CreateDirectory(
const FileSystemURL& url,
bool exclusive,
bool recursive,
const StatusCallback& callback) {
base::File::Error error = base::File::FILE_OK;
FileSystemOperation* operation =
file_system_context_->CreateFileSystemOperation(url, &error);
BeginOperationScoper scope;
OperationHandle handle = BeginOperation(operation, scope.AsWeakPtr());
if (!operation) {
DidFinish(handle, callback, error);
return handle.id;
}
PrepareForWrite(handle.id, url);
operation->CreateDirectory(
url, exclusive, recursive,
base::Bind(&FileSystemOperationRunner::DidFinish, AsWeakPtr(),
handle, callback));
return handle.id;
}
OperationID FileSystemOperationRunner::Copy(
const FileSystemURL& src_url,
const FileSystemURL& dest_url,
CopyOrMoveOption option,
ErrorBehavior error_behavior,
const CopyProgressCallback& progress_callback,
const StatusCallback& callback) {
base::File::Error error = base::File::FILE_OK;
FileSystemOperation* operation =
file_system_context_->CreateFileSystemOperation(dest_url, &error);
BeginOperationScoper scope;
OperationHandle handle = BeginOperation(operation, scope.AsWeakPtr());
if (!operation) {
DidFinish(handle, callback, error);
return handle.id;
}
PrepareForWrite(handle.id, dest_url);
PrepareForRead(handle.id, src_url);
operation->Copy(src_url, dest_url, option, error_behavior,
progress_callback.is_null()
? CopyProgressCallback()
: base::Bind(&FileSystemOperationRunner::OnCopyProgress,
AsWeakPtr(), handle, progress_callback),
base::Bind(&FileSystemOperationRunner::DidFinish, AsWeakPtr(),
handle, callback));
return handle.id;
}
OperationID FileSystemOperationRunner::Move(
const FileSystemURL& src_url,
const FileSystemURL& dest_url,
CopyOrMoveOption option,
const StatusCallback& callback) {
base::File::Error error = base::File::FILE_OK;
FileSystemOperation* operation =
file_system_context_->CreateFileSystemOperation(dest_url, &error);
BeginOperationScoper scope;
OperationHandle handle = BeginOperation(operation, scope.AsWeakPtr());
if (!operation) {
DidFinish(handle, callback, error);
return handle.id;
}
PrepareForWrite(handle.id, dest_url);
PrepareForWrite(handle.id, src_url);
operation->Move(
src_url, dest_url, option,
base::Bind(&FileSystemOperationRunner::DidFinish, AsWeakPtr(),
handle, callback));
return handle.id;
}
OperationID FileSystemOperationRunner::DirectoryExists(
const FileSystemURL& url,
const StatusCallback& callback) {
base::File::Error error = base::File::FILE_OK;
FileSystemOperation* operation =
file_system_context_->CreateFileSystemOperation(url, &error);
BeginOperationScoper scope;
OperationHandle handle = BeginOperation(operation, scope.AsWeakPtr());
if (!operation) {
DidFinish(handle, callback, error);
return handle.id;
}
PrepareForRead(handle.id, url);
operation->DirectoryExists(
url,
base::Bind(&FileSystemOperationRunner::DidFinish, AsWeakPtr(),
handle, callback));
return handle.id;
}
OperationID FileSystemOperationRunner::FileExists(
const FileSystemURL& url,
const StatusCallback& callback) {
base::File::Error error = base::File::FILE_OK;
FileSystemOperation* operation =
file_system_context_->CreateFileSystemOperation(url, &error);
BeginOperationScoper scope;
OperationHandle handle = BeginOperation(operation, scope.AsWeakPtr());
if (!operation) {
DidFinish(handle, callback, error);
return handle.id;
}
PrepareForRead(handle.id, url);
operation->FileExists(
url,
base::Bind(&FileSystemOperationRunner::DidFinish, AsWeakPtr(),
handle, callback));
return handle.id;
}
OperationID FileSystemOperationRunner::GetMetadata(
const FileSystemURL& url,
int fields,
const GetMetadataCallback& callback) {
base::File::Error error = base::File::FILE_OK;
FileSystemOperation* operation =
file_system_context_->CreateFileSystemOperation(url, &error);
BeginOperationScoper scope;
OperationHandle handle = BeginOperation(operation, scope.AsWeakPtr());
if (!operation) {
DidGetMetadata(handle, callback, error, base::File::Info());
return handle.id;
}
PrepareForRead(handle.id, url);
operation->GetMetadata(url, fields,
base::Bind(&FileSystemOperationRunner::DidGetMetadata,
AsWeakPtr(), handle, callback));
return handle.id;
}
OperationID FileSystemOperationRunner::ReadDirectory(
const FileSystemURL& url,
const ReadDirectoryCallback& callback) {
base::File::Error error = base::File::FILE_OK;
FileSystemOperation* operation =
file_system_context_->CreateFileSystemOperation(url, &error);
BeginOperationScoper scope;
OperationHandle handle = BeginOperation(operation, scope.AsWeakPtr());
if (!operation) {
DidReadDirectory(handle, callback, error, std::vector<DirectoryEntry>(),
false);
return handle.id;
}
PrepareForRead(handle.id, url);
operation->ReadDirectory(
url,
base::Bind(&FileSystemOperationRunner::DidReadDirectory, AsWeakPtr(),
handle, callback));
return handle.id;
}
OperationID FileSystemOperationRunner::Remove(
const FileSystemURL& url, bool recursive,
const StatusCallback& callback) {
base::File::Error error = base::File::FILE_OK;
FileSystemOperation* operation =
file_system_context_->CreateFileSystemOperation(url, &error);
BeginOperationScoper scope;
OperationHandle handle = BeginOperation(operation, scope.AsWeakPtr());
if (!operation) {
DidFinish(handle, callback, error);
return handle.id;
}
PrepareForWrite(handle.id, url);
operation->Remove(
url, recursive,
base::Bind(&FileSystemOperationRunner::DidFinish, AsWeakPtr(),
handle, callback));
return handle.id;
}
OperationID FileSystemOperationRunner::Write(
const net::URLRequestContext* url_request_context,
const FileSystemURL& url,
std::unique_ptr<storage::BlobDataHandle> blob,
int64_t offset,
const WriteCallback& callback) {
base::File::Error error = base::File::FILE_OK;
FileSystemOperation* operation =
file_system_context_->CreateFileSystemOperation(url, &error);
BeginOperationScoper scope;
OperationHandle handle = BeginOperation(operation, scope.AsWeakPtr());
if (!operation) {
DidWrite(handle, callback, error, 0, true);
return handle.id;
}
std::unique_ptr<FileStreamWriter> writer(
file_system_context_->CreateFileStreamWriter(url, offset));
if (!writer) {
// Write is not supported.
DidWrite(handle, callback, base::File::FILE_ERROR_SECURITY, 0, true);
return handle.id;
}
std::unique_ptr<FileWriterDelegate> writer_delegate(new FileWriterDelegate(
std::move(writer), url.mount_option().flush_policy()));
std::unique_ptr<net::URLRequest> blob_request(
storage::BlobProtocolHandler::CreateBlobRequest(
std::move(blob), url_request_context, writer_delegate.get()));
PrepareForWrite(handle.id, url);
operation->Write(url, std::move(writer_delegate), std::move(blob_request),
base::Bind(&FileSystemOperationRunner::DidWrite, AsWeakPtr(),
handle, callback));
return handle.id;
}
OperationID FileSystemOperationRunner::Truncate(
const FileSystemURL& url,
int64_t length,
const StatusCallback& callback) {
base::File::Error error = base::File::FILE_OK;
FileSystemOperation* operation =
file_system_context_->CreateFileSystemOperation(url, &error);
BeginOperationScoper scope;
OperationHandle handle = BeginOperation(operation, scope.AsWeakPtr());
if (!operation) {
DidFinish(handle, callback, error);
return handle.id;
}
PrepareForWrite(handle.id, url);
operation->Truncate(
url, length,
base::Bind(&FileSystemOperationRunner::DidFinish, AsWeakPtr(),
handle, callback));
return handle.id;
}
void FileSystemOperationRunner::Cancel(
OperationID id,
const StatusCallback& callback) {
if (ContainsKey(finished_operations_, id)) {
DCHECK(!ContainsKey(stray_cancel_callbacks_, id));
stray_cancel_callbacks_[id] = callback;
return;
}
FileSystemOperation* operation = operations_.Lookup(id);
if (!operation) {
// There is no operation with |id|.
callback.Run(base::File::FILE_ERROR_INVALID_OPERATION);
return;
}
operation->Cancel(callback);
}
OperationID FileSystemOperationRunner::TouchFile(
const FileSystemURL& url,
const base::Time& last_access_time,
const base::Time& last_modified_time,
const StatusCallback& callback) {
base::File::Error error = base::File::FILE_OK;
FileSystemOperation* operation =
file_system_context_->CreateFileSystemOperation(url, &error);
BeginOperationScoper scope;
OperationHandle handle = BeginOperation(operation, scope.AsWeakPtr());
if (!operation) {
DidFinish(handle, callback, error);
return handle.id;
}
PrepareForWrite(handle.id, url);
operation->TouchFile(
url, last_access_time, last_modified_time,
base::Bind(&FileSystemOperationRunner::DidFinish, AsWeakPtr(),
handle, callback));
return handle.id;
}
OperationID FileSystemOperationRunner::OpenFile(
const FileSystemURL& url,
int file_flags,
const OpenFileCallback& callback) {
base::File::Error error = base::File::FILE_OK;
FileSystemOperation* operation =
file_system_context_->CreateFileSystemOperation(url, &error);
BeginOperationScoper scope;
OperationHandle handle = BeginOperation(operation, scope.AsWeakPtr());
if (!operation) {
DidOpenFile(handle, callback, base::File(error), base::Closure());
return handle.id;
}
if (file_flags &
(base::File::FLAG_CREATE | base::File::FLAG_OPEN_ALWAYS |
base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_OPEN_TRUNCATED |
base::File::FLAG_WRITE | base::File::FLAG_EXCLUSIVE_WRITE |
base::File::FLAG_DELETE_ON_CLOSE |
base::File::FLAG_WRITE_ATTRIBUTES)) {
PrepareForWrite(handle.id, url);
} else {
PrepareForRead(handle.id, url);
}
operation->OpenFile(
url, file_flags,
base::Bind(&FileSystemOperationRunner::DidOpenFile, AsWeakPtr(),
handle, callback));
return handle.id;
}
OperationID FileSystemOperationRunner::CreateSnapshotFile(
const FileSystemURL& url,
const SnapshotFileCallback& callback) {
base::File::Error error = base::File::FILE_OK;
FileSystemOperation* operation =
file_system_context_->CreateFileSystemOperation(url, &error);
BeginOperationScoper scope;
OperationHandle handle = BeginOperation(operation, scope.AsWeakPtr());
if (!operation) {
DidCreateSnapshot(handle, callback, error, base::File::Info(),
base::FilePath(), NULL);
return handle.id;
}
PrepareForRead(handle.id, url);
operation->CreateSnapshotFile(
url,
base::Bind(&FileSystemOperationRunner::DidCreateSnapshot, AsWeakPtr(),
handle, callback));
return handle.id;
}
OperationID FileSystemOperationRunner::CopyInForeignFile(
const base::FilePath& src_local_disk_path,
const FileSystemURL& dest_url,
const StatusCallback& callback) {
base::File::Error error = base::File::FILE_OK;
FileSystemOperation* operation =
file_system_context_->CreateFileSystemOperation(dest_url, &error);
BeginOperationScoper scope;
OperationHandle handle = BeginOperation(operation, scope.AsWeakPtr());
if (!operation) {
DidFinish(handle, callback, error);
return handle.id;
}
PrepareForWrite(handle.id, dest_url);
operation->CopyInForeignFile(
src_local_disk_path, dest_url,
base::Bind(&FileSystemOperationRunner::DidFinish, AsWeakPtr(),
handle, callback));
return handle.id;
}
OperationID FileSystemOperationRunner::RemoveFile(
const FileSystemURL& url,
const StatusCallback& callback) {
base::File::Error error = base::File::FILE_OK;
FileSystemOperation* operation =
file_system_context_->CreateFileSystemOperation(url, &error);
BeginOperationScoper scope;
OperationHandle handle = BeginOperation(operation, scope.AsWeakPtr());
if (!operation) {
DidFinish(handle, callback, error);
return handle.id;
}
PrepareForWrite(handle.id, url);
operation->RemoveFile(
url,
base::Bind(&FileSystemOperationRunner::DidFinish, AsWeakPtr(),
handle, callback));
return handle.id;
}
OperationID FileSystemOperationRunner::RemoveDirectory(
const FileSystemURL& url,
const StatusCallback& callback) {
base::File::Error error = base::File::FILE_OK;
FileSystemOperation* operation =
file_system_context_->CreateFileSystemOperation(url, &error);
BeginOperationScoper scope;
OperationHandle handle = BeginOperation(operation, scope.AsWeakPtr());
if (!operation) {
DidFinish(handle, callback, error);
return handle.id;
}
PrepareForWrite(handle.id, url);
operation->RemoveDirectory(
url,
base::Bind(&FileSystemOperationRunner::DidFinish, AsWeakPtr(),
handle, callback));
return handle.id;
}
OperationID FileSystemOperationRunner::CopyFileLocal(
const FileSystemURL& src_url,
const FileSystemURL& dest_url,
CopyOrMoveOption option,
const CopyFileProgressCallback& progress_callback,
const StatusCallback& callback) {
base::File::Error error = base::File::FILE_OK;
FileSystemOperation* operation =
file_system_context_->CreateFileSystemOperation(src_url, &error);
BeginOperationScoper scope;
OperationHandle handle = BeginOperation(operation, scope.AsWeakPtr());
if (!operation) {
DidFinish(handle, callback, error);
return handle.id;
}
PrepareForRead(handle.id, src_url);
PrepareForWrite(handle.id, dest_url);
operation->CopyFileLocal(
src_url, dest_url, option, progress_callback,
base::Bind(&FileSystemOperationRunner::DidFinish, AsWeakPtr(),
handle, callback));
return handle.id;
}
OperationID FileSystemOperationRunner::MoveFileLocal(
const FileSystemURL& src_url,
const FileSystemURL& dest_url,
CopyOrMoveOption option,
const StatusCallback& callback) {
base::File::Error error = base::File::FILE_OK;
FileSystemOperation* operation =
file_system_context_->CreateFileSystemOperation(src_url, &error);
BeginOperationScoper scope;
OperationHandle handle = BeginOperation(operation, scope.AsWeakPtr());
if (!operation) {
DidFinish(handle, callback, error);
return handle.id;
}
PrepareForWrite(handle.id, src_url);
PrepareForWrite(handle.id, dest_url);
operation->MoveFileLocal(
src_url, dest_url, option,
base::Bind(&FileSystemOperationRunner::DidFinish, AsWeakPtr(),
handle, callback));
return handle.id;
}
base::File::Error FileSystemOperationRunner::SyncGetPlatformPath(
const FileSystemURL& url,
base::FilePath* platform_path) {
base::File::Error error = base::File::FILE_OK;
std::unique_ptr<FileSystemOperation> operation(
file_system_context_->CreateFileSystemOperation(url, &error));
if (!operation.get())
return error;
return operation->SyncGetPlatformPath(url, platform_path);
}
FileSystemOperationRunner::FileSystemOperationRunner(
FileSystemContext* file_system_context)
: file_system_context_(file_system_context) {}
void FileSystemOperationRunner::DidFinish(
const OperationHandle& handle,
const StatusCallback& callback,
base::File::Error rv) {
if (handle.scope) {
finished_operations_.insert(handle.id);
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::Bind(&FileSystemOperationRunner::DidFinish,
AsWeakPtr(), handle, callback, rv));
return;
}
callback.Run(rv);
FinishOperation(handle.id);
}
void FileSystemOperationRunner::DidGetMetadata(
const OperationHandle& handle,
const GetMetadataCallback& callback,
base::File::Error rv,
const base::File::Info& file_info) {
if (handle.scope) {
finished_operations_.insert(handle.id);
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::Bind(&FileSystemOperationRunner::DidGetMetadata,
AsWeakPtr(), handle, callback, rv, file_info));
return;
}
callback.Run(rv, file_info);
FinishOperation(handle.id);
}
void FileSystemOperationRunner::DidReadDirectory(
const OperationHandle& handle,
const ReadDirectoryCallback& callback,
base::File::Error rv,
const std::vector<DirectoryEntry>& entries,
bool has_more) {
if (handle.scope) {
finished_operations_.insert(handle.id);
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::Bind(&FileSystemOperationRunner::DidReadDirectory,
AsWeakPtr(), handle, callback, rv,
entries, has_more));
return;
}
callback.Run(rv, entries, has_more);
if (rv != base::File::FILE_OK || !has_more)
FinishOperation(handle.id);
}
void FileSystemOperationRunner::DidWrite(const OperationHandle& handle,
const WriteCallback& callback,
base::File::Error rv,
int64_t bytes,
bool complete) {
if (handle.scope) {
finished_operations_.insert(handle.id);
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::Bind(&FileSystemOperationRunner::DidWrite, AsWeakPtr(),
handle, callback, rv, bytes, complete));
return;
}
callback.Run(rv, bytes, complete);
if (rv != base::File::FILE_OK || complete)
FinishOperation(handle.id);
}
void FileSystemOperationRunner::DidOpenFile(
const OperationHandle& handle,
const OpenFileCallback& callback,
base::File file,
const base::Closure& on_close_callback) {
if (handle.scope) {
finished_operations_.insert(handle.id);
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::Bind(&FileSystemOperationRunner::DidOpenFile,
AsWeakPtr(), handle, callback, Passed(&file),
on_close_callback));
return;
}
callback.Run(std::move(file), on_close_callback);
FinishOperation(handle.id);
}
void FileSystemOperationRunner::DidCreateSnapshot(
const OperationHandle& handle,
const SnapshotFileCallback& callback,
base::File::Error rv,
const base::File::Info& file_info,
const base::FilePath& platform_path,
const scoped_refptr<storage::ShareableFileReference>& file_ref) {
if (handle.scope) {
finished_operations_.insert(handle.id);
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::Bind(&FileSystemOperationRunner::DidCreateSnapshot,
AsWeakPtr(), handle, callback, rv, file_info,
platform_path, file_ref));
return;
}
callback.Run(rv, file_info, platform_path, file_ref);
FinishOperation(handle.id);
}
void FileSystemOperationRunner::OnCopyProgress(
const OperationHandle& handle,
const CopyProgressCallback& callback,
FileSystemOperation::CopyProgressType type,
const FileSystemURL& source_url,
const FileSystemURL& dest_url,
int64_t size) {
if (handle.scope) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::Bind(
&FileSystemOperationRunner::OnCopyProgress,
AsWeakPtr(), handle, callback, type, source_url, dest_url, size));
return;
}
callback.Run(type, source_url, dest_url, size);
}
void FileSystemOperationRunner::PrepareForWrite(OperationID id,
const FileSystemURL& url) {
if (file_system_context_->GetUpdateObservers(url.type())) {
file_system_context_->GetUpdateObservers(url.type())->Notify(
&FileUpdateObserver::OnStartUpdate, std::make_tuple(url));
}
write_target_urls_[id].insert(url);
}
void FileSystemOperationRunner::PrepareForRead(OperationID id,
const FileSystemURL& url) {
if (file_system_context_->GetAccessObservers(url.type())) {
file_system_context_->GetAccessObservers(url.type())->Notify(
&FileAccessObserver::OnAccess, std::make_tuple(url));
}
}
FileSystemOperationRunner::OperationHandle
FileSystemOperationRunner::BeginOperation(
FileSystemOperation* operation,
base::WeakPtr<BeginOperationScoper> scope) {
OperationHandle handle;
handle.id = operations_.Add(operation);
handle.scope = scope;
return handle;
}
void FileSystemOperationRunner::FinishOperation(OperationID id) {
OperationToURLSet::iterator found = write_target_urls_.find(id);
if (found != write_target_urls_.end()) {
const FileSystemURLSet& urls = found->second;
for (FileSystemURLSet::const_iterator iter = urls.begin();
iter != urls.end(); ++iter) {
if (file_system_context_->GetUpdateObservers(iter->type())) {
file_system_context_->GetUpdateObservers(iter->type())->Notify(
&FileUpdateObserver::OnEndUpdate, std::make_tuple(*iter));
}
}
write_target_urls_.erase(found);
}
// IDMap::Lookup fails if the operation is NULL, so we don't check
// operations_.Lookup(id) here.
operations_.Remove(id);
finished_operations_.erase(id);
// Dispatch stray cancel callback if exists.
std::map<OperationID, StatusCallback>::iterator found_cancel =
stray_cancel_callbacks_.find(id);
if (found_cancel != stray_cancel_callbacks_.end()) {
// This cancel has been requested after the operation has finished,
// so report that we failed to stop it.
found_cancel->second.Run(base::File::FILE_ERROR_INVALID_OPERATION);
stray_cancel_callbacks_.erase(found_cancel);
}
}
} // namespace storage