[go: nahoru, domu]

blob: 2a15f62b3c7f85fd21893b34f591da9a5a3ec4fb [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/ash/file_system_provider/fileapi/file_stream_writer.h"
#include <memory>
#include <utility>
#include "base/bind.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/trace_event/trace_event.h"
#include "chrome/browser/ash/file_system_provider/abort_callback.h"
#include "chrome/browser/ash/file_system_provider/fileapi/provider_async_file_util.h"
#include "chrome/browser/ash/file_system_provider/mount_path_util.h"
#include "chrome/browser/ash/file_system_provider/provided_file_system_interface.h"
#include "chrome/browser/ash/file_system_provider/scoped_file_opener.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
using content::BrowserThread;
namespace chromeos {
namespace file_system_provider {
class FileStreamWriter::OperationRunner
: public base::RefCountedThreadSafe<
FileStreamWriter::OperationRunner,
content::BrowserThread::DeleteOnUIThread> {
public:
OperationRunner() : file_handle_(0) {}
// Opens a file for writing and calls the completion callback. Must be called
// on UI thread.
void OpenFileOnUIThread(const storage::FileSystemURL& url,
storage::AsyncFileUtil::StatusCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(abort_callback_.is_null());
util::FileSystemURLParser parser(url);
if (!parser.Parse()) {
content::GetIOThreadTaskRunner({})->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback), base::File::FILE_ERROR_SECURITY));
return;
}
file_system_ = parser.file_system()->GetWeakPtr();
file_opener_ = std::make_unique<ScopedFileOpener>(
parser.file_system(), parser.file_path(), OPEN_FILE_MODE_WRITE,
base::BindOnce(&OperationRunner::OnOpenFileCompletedOnUIThread, this,
std::move(callback)));
}
// Requests writing bytes to the file. In case of either success or a failure
// |callback| is executed. Must be called on UI thread.
void WriteFileOnUIThread(scoped_refptr<net::IOBuffer> buffer,
int64_t offset,
int length,
storage::AsyncFileUtil::StatusCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(abort_callback_.is_null());
// If the file system got unmounted, then abort the writing operation.
if (!file_system_.get()) {
content::GetIOThreadTaskRunner({})->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback), base::File::FILE_ERROR_ABORT));
return;
}
abort_callback_ = file_system_->WriteFile(
file_handle_, buffer.get(), offset, length,
base::BindOnce(&OperationRunner::OnWriteFileCompletedOnUIThread, this,
std::move(callback)));
}
// Aborts the most recent operation (if exists) and closes a file if opened.
// The runner must not be used anymore after calling this method.
void CloseRunnerOnUIThread() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!abort_callback_.is_null())
std::move(abort_callback_).Run();
// Close the file (if opened).
file_opener_.reset();
}
private:
friend struct content::BrowserThread::DeleteOnThread<
content::BrowserThread::UI>;
friend class base::DeleteHelper<OperationRunner>;
virtual ~OperationRunner() {}
// Remembers a file handle for further operations and forwards the result to
// the IO thread.
void OnOpenFileCompletedOnUIThread(
storage::AsyncFileUtil::StatusCallback callback,
int file_handle,
base::File::Error result) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
abort_callback_.Reset();
if (result == base::File::FILE_OK)
file_handle_ = file_handle;
content::GetIOThreadTaskRunner({})->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), result));
}
// Forwards a response of writing to a file to the IO thread.
void OnWriteFileCompletedOnUIThread(
storage::AsyncFileUtil::StatusCallback callback,
base::File::Error result) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
abort_callback_.Reset();
content::GetIOThreadTaskRunner({})->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), result));
}
AbortCallback abort_callback_;
base::WeakPtr<ProvidedFileSystemInterface> file_system_;
std::unique_ptr<ScopedFileOpener> file_opener_;
int file_handle_;
DISALLOW_COPY_AND_ASSIGN(OperationRunner);
};
FileStreamWriter::FileStreamWriter(const storage::FileSystemURL& url,
int64_t initial_offset)
: url_(url),
current_offset_(initial_offset),
runner_(new OperationRunner),
state_(NOT_INITIALIZED) {}
FileStreamWriter::~FileStreamWriter() {
// Close the runner explicitly if the file streamer is
if (state_ != CANCELLING) {
content::GetUIThreadTaskRunner({})->PostTask(
FROM_HERE,
base::BindOnce(&OperationRunner::CloseRunnerOnUIThread, runner_));
}
// If a write is in progress, mark it as completed.
TRACE_EVENT_NESTABLE_ASYNC_END0("file_system_provider",
"FileStreamWriter::Write", this);
}
void FileStreamWriter::Initialize(base::OnceClosure pending_closure,
net::CompletionOnceCallback error_callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK_EQ(NOT_INITIALIZED, state_);
state_ = INITIALIZING;
content::GetUIThreadTaskRunner({})->PostTask(
FROM_HERE,
base::BindOnce(&OperationRunner::OpenFileOnUIThread, runner_, url_,
base::BindOnce(&FileStreamWriter::OnOpenFileCompleted,
weak_ptr_factory_.GetWeakPtr(),
std::move(pending_closure),
std::move(error_callback))));
}
void FileStreamWriter::OnOpenFileCompleted(
base::OnceClosure pending_closure,
net::CompletionOnceCallback error_callback,
base::File::Error result) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(state_ == INITIALIZING || state_ == CANCELLING);
if (state_ == CANCELLING)
return;
// In case of an error, return immediately using the |error_callback| of the
// Write() pending request.
if (result != base::File::FILE_OK) {
state_ = FAILED;
std::move(error_callback).Run(net::FileErrorToNetError(result));
return;
}
DCHECK_EQ(base::File::FILE_OK, result);
state_ = INITIALIZED;
// Run the task waiting for the initialization to be completed.
std::move(pending_closure).Run();
}
int FileStreamWriter::Write(net::IOBuffer* buffer,
int buffer_length,
net::CompletionOnceCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
TRACE_EVENT_NESTABLE_ASYNC_BEGIN1("file_system_provider",
"FileStreamWriter::Write", this,
"buffer_length", buffer_length);
write_callback_ = std::move(callback);
switch (state_) {
case NOT_INITIALIZED:
// Lazily initialize with the first call to Write().
Initialize(
base::BindOnce(&FileStreamWriter::WriteAfterInitialized,
weak_ptr_factory_.GetWeakPtr(),
base::WrapRefCounted(buffer), buffer_length,
base::BindOnce(&FileStreamWriter::OnWriteCompleted,
weak_ptr_factory_.GetWeakPtr())),
base::BindOnce(&FileStreamWriter::OnWriteCompleted,
weak_ptr_factory_.GetWeakPtr()));
break;
case INITIALIZING:
NOTREACHED();
break;
case INITIALIZED:
WriteAfterInitialized(buffer, buffer_length,
base::BindOnce(&FileStreamWriter::OnWriteCompleted,
weak_ptr_factory_.GetWeakPtr()));
break;
case EXECUTING:
case FAILED:
case CANCELLING:
NOTREACHED();
break;
}
return net::ERR_IO_PENDING;
}
int FileStreamWriter::Cancel(net::CompletionOnceCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (state_ != INITIALIZING && state_ != EXECUTING)
return net::ERR_UNEXPECTED;
state_ = CANCELLING;
// Abort and optimistically return an OK result code, as the aborting
// operation is always forced and can't be cancelled. Similarly, for closing
// files.
content::GetUIThreadTaskRunner({})->PostTask(
FROM_HERE,
base::BindOnce(&OperationRunner::CloseRunnerOnUIThread, runner_));
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), net::OK));
// If a write is in progress, mark it as completed.
TRACE_EVENT_NESTABLE_ASYNC_END0("file_system_provider",
"FileStreamWriter::Write", this);
return net::ERR_IO_PENDING;
}
int FileStreamWriter::Flush(net::CompletionOnceCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK_NE(CANCELLING, state_);
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback),
state_ == INITIALIZED ? net::OK : net::ERR_FAILED));
return net::ERR_IO_PENDING;
}
void FileStreamWriter::OnWriteFileCompleted(
int buffer_length,
net::CompletionOnceCallback callback,
base::File::Error result) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(state_ == EXECUTING || state_ == CANCELLING);
if (state_ == CANCELLING)
return;
state_ = INITIALIZED;
if (result != base::File::FILE_OK) {
state_ = FAILED;
std::move(callback).Run(net::FileErrorToNetError(result));
return;
}
current_offset_ += buffer_length;
std::move(callback).Run(buffer_length);
}
void FileStreamWriter::OnWriteCompleted(int result) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (state_ != CANCELLING)
std::move(write_callback_).Run(result);
TRACE_EVENT_NESTABLE_ASYNC_END0("file_system_provider",
"FileStreamWriter::Write", this);
}
void FileStreamWriter::WriteAfterInitialized(
scoped_refptr<net::IOBuffer> buffer,
int buffer_length,
net::CompletionOnceCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(state_ == INITIALIZED || state_ == CANCELLING);
if (state_ == CANCELLING)
return;
state_ = EXECUTING;
content::GetUIThreadTaskRunner({})->PostTask(
FROM_HERE,
base::BindOnce(&OperationRunner::WriteFileOnUIThread, runner_, buffer,
current_offset_, buffer_length,
base::BindOnce(&FileStreamWriter::OnWriteFileCompleted,
weak_ptr_factory_.GetWeakPtr(),
buffer_length, std::move(callback))));
}
} // namespace file_system_provider
} // namespace chromeos