Chromium Code Reviews| Index: webkit/fileapi/sandbox_file_writer.cc |
| diff --git a/webkit/fileapi/sandbox_file_writer.cc b/webkit/fileapi/sandbox_file_writer.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..33dad39a69ea08eb100a61802bb041778602405a |
| --- /dev/null |
| +++ b/webkit/fileapi/sandbox_file_writer.cc |
| @@ -0,0 +1,236 @@ |
| +// Copyright (c) 2012 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 "webkit/fileapi/sandbox_file_writer.h" |
| + |
| +#include "base/file_util_proxy.h" |
| +#include "base/platform_file.h" |
| +#include "base/sequenced_task_runner.h" |
| +#include "net/base/io_buffer.h" |
| +#include "net/base/net_errors.h" |
| +#include "webkit/blob/local_file_reader.h" |
| +#include "webkit/fileapi/file_system_context.h" |
| +#include "webkit/fileapi/file_system_operation_interface.h" |
| +#include "webkit/fileapi/file_system_quota_util.h" |
| +#include "webkit/fileapi/file_system_util.h" |
| +#include "webkit/fileapi/local_file_writer.h" |
| +#include "webkit/quota/quota_manager.h" |
| + |
| +namespace fileapi { |
| + |
| +namespace { |
| + |
| +int PlatformFileErrorToNetError(base::PlatformFileError error) { |
| + // TODO(kinuko): Move this static method to more convenient place. |
| + return webkit_blob::LocalFileReader::PlatformFileErrorToNetError(error); |
| +} |
| + |
| +// Adjust the |quota| value with given |file_offset| and |file_size| and |
| +// return the new quota value. |
| +int64 AdjustQuotaWithFileRange(int64 quota, |
| + int64 file_offset, |
| + int64 file_size) { |
|
kinaba
2012/05/15 07:40:04
Not at all related to the intent of the current CL
kinuko
2012/05/15 11:20:37
Good point, done.
|
| + if (quota < 0) |
| + quota = 0; |
| + int64 overlap = file_size - file_offset; |
| + if (kint64max - overlap > quota) |
| + quota += overlap; |
| + return quota; |
| +} |
| + |
| +} // namespace |
| + |
| +SandboxFileWriter::SandboxFileWriter( |
| + FileSystemContext* file_system_context, |
| + const GURL& url, |
| + int64 initial_offset) |
| + : file_system_context_(file_system_context), |
| + url_(url), |
| + initial_offset_(initial_offset), |
| + file_size_(0), |
| + total_bytes_written_(0), |
| + allowed_bytes_to_write_(0), |
| + has_pending_operation_(false), |
| + default_quota_(kint64max), |
| + weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { |
| + const bool result = CrackFileSystemURL( |
| + url_, &origin_, &file_system_type_, &virtual_path_); |
| + DCHECK(result); |
| +} |
| + |
| +SandboxFileWriter::~SandboxFileWriter() { |
| + if (quota_util()) |
| + quota_util()->proxy()->EndUpdateOrigin(origin_, file_system_type_); |
| +} |
| + |
| +int SandboxFileWriter::Write( |
| + net::IOBuffer* buf, int buf_len, |
| + const net::CompletionCallback& callback) { |
| + has_pending_operation_ = true; |
| + if (local_file_writer_.get()) |
| + return WriteInternal(buf, buf_len, callback); |
| + |
| + FileSystemOperationInterface* operation = |
| + file_system_context_->CreateFileSystemOperation(url_); |
| + DCHECK(operation); |
| + net::CompletionCallback write_task = |
| + base::Bind(&SandboxFileWriter::DidInitializeForWrite, |
| + weak_factory_.GetWeakPtr(), |
| + make_scoped_refptr(buf), buf_len, callback); |
| + operation->GetMetadata( |
| + url_, base::Bind(&SandboxFileWriter::DidGetFileInfo, |
| + weak_factory_.GetWeakPtr(), write_task)); |
| + return net::ERR_IO_PENDING; |
| +} |
| + |
| +int SandboxFileWriter::Cancel(const net::CompletionCallback& callback) { |
| + if (!has_pending_operation_) |
| + return net::ERR_UNEXPECTED; |
| + |
| + DCHECK(local_file_writer_.get()); |
| + DCHECK(!callback.is_null()); |
| + cancel_callback_ = callback; |
| + return net::ERR_IO_PENDING; |
| +} |
| + |
| +int SandboxFileWriter::WriteInternal( |
| + net::IOBuffer* buf, int buf_len, |
| + const net::CompletionCallback& callback) { |
| + // allowed_bytes_to_write could be negative if the file size is |
| + // greater than the current (possibly new) quota. |
| + DCHECK(total_bytes_written_ <= allowed_bytes_to_write_ || |
| + allowed_bytes_to_write_ < 0); |
| + if (total_bytes_written_ >= allowed_bytes_to_write_) { |
| + has_pending_operation_ = false; |
| + return net::ERR_FILE_NO_SPACE; |
| + } |
| + |
| + if (buf_len > allowed_bytes_to_write_ - total_bytes_written_) |
| + buf_len = allowed_bytes_to_write_ - total_bytes_written_; |
| + |
| + DCHECK(local_file_writer_.get()); |
| + const int result = local_file_writer_->Write( |
| + buf, buf_len, |
| + base::Bind(&SandboxFileWriter::DidWrite, weak_factory_.GetWeakPtr(), |
| + callback)); |
| + if (result != net::ERR_IO_PENDING) |
| + has_pending_operation_ = false; |
| + return result; |
| +} |
| + |
| +void SandboxFileWriter::DidGetFileInfo( |
| + const net::CompletionCallback& callback, |
| + base::PlatformFileError file_error, |
| + const base::PlatformFileInfo& file_info, |
| + const FilePath& platform_path) { |
| + if (CancelIfRequested()) |
| + return; |
| + if (file_error != base::PLATFORM_FILE_OK) { |
| + callback.Run(PlatformFileErrorToNetError(file_error)); |
| + return; |
| + } |
| + if (file_info.is_directory) { |
| + // We should not be writing to a directory. |
| + callback.Run(net::ERR_ACCESS_DENIED); |
| + return; |
| + } |
| + file_size_ = file_info.size; |
| + DCHECK(!local_file_writer_.get()); |
| + local_file_writer_.reset(new LocalFileWriter(platform_path, initial_offset_)); |
| + |
| + quota::QuotaManagerProxy* quota_manager_proxy = |
| + file_system_context_->quota_manager_proxy(); |
| + if (!quota_manager_proxy || !quota_util()) { |
| + // If we don't have the quota manager or the requested filesystem type |
| + // does not support quota, we should be able to let it go. |
| + allowed_bytes_to_write_ = default_quota_; |
| + callback.Run(net::OK); |
| + return; |
| + } |
| + |
| + quota_util()->proxy()->StartUpdateOrigin(origin_, file_system_type_); |
| + DCHECK(quota_manager_proxy->quota_manager()); |
| + quota_manager_proxy->quota_manager()->GetUsageAndQuota( |
| + origin_, |
| + FileSystemTypeToQuotaStorageType(file_system_type_), |
| + base::Bind(&SandboxFileWriter::DidGetUsageAndQuota, |
| + weak_factory_.GetWeakPtr(), callback)); |
| +} |
| + |
| +void SandboxFileWriter::DidGetUsageAndQuota( |
| + const net::CompletionCallback& callback, |
| + quota::QuotaStatusCode status, |
| + int64 usage, int64 quota) { |
| + if (status != quota::kQuotaStatusOk) { |
| + LOG(WARNING) << "Got unexpected quota error : " << status; |
| + callback.Run(net::ERR_FAILED); |
| + return; |
| + } |
| + |
| + allowed_bytes_to_write_ = quota - usage; |
| + callback.Run(net::OK); |
| +} |
| + |
| +void SandboxFileWriter::DidInitializeForWrite( |
| + net::IOBuffer* buf, int buf_len, |
| + const net::CompletionCallback& callback, |
| + int init_status) { |
| + if (init_status != net::OK) { |
| + has_pending_operation_ = false; |
| + callback.Run(init_status); |
| + return; |
| + } |
| + allowed_bytes_to_write_ = AdjustQuotaWithFileRange( |
| + allowed_bytes_to_write_, initial_offset_, file_size_); |
| + const int result = WriteInternal(buf, buf_len, callback); |
| + if (result != net::ERR_IO_PENDING) |
| + callback.Run(result); |
| +} |
| + |
| +void SandboxFileWriter::DidWrite( |
| + const net::CompletionCallback& callback, |
| + int write_response) { |
| + DCHECK(has_pending_operation_); |
| + has_pending_operation_ = false; |
| + |
| + if (write_response <= 0) { |
| + if (CancelIfRequested()) |
| + return; |
| + callback.Run(write_response); |
| + return; |
| + } |
| + |
| + if (quota_util() && |
| + total_bytes_written_ + write_response + initial_offset_ > file_size_) { |
| + int overlapped = file_size_ - total_bytes_written_ - initial_offset_; |
| + if (overlapped < 0) |
| + overlapped = 0; |
| + quota_util()->proxy()->UpdateOriginUsage( |
| + file_system_context_->quota_manager_proxy(), |
| + origin_, file_system_type_, write_response - overlapped); |
| + } |
| + total_bytes_written_ += write_response; |
| + |
| + if (CancelIfRequested()) |
| + return; |
| + callback.Run(write_response); |
| +} |
| + |
| +bool SandboxFileWriter::CancelIfRequested() { |
| + if (cancel_callback_.is_null()) |
| + return false; |
| + |
| + net::CompletionCallback pending_cancel = cancel_callback_; |
| + has_pending_operation_ = false; |
| + cancel_callback_.Reset(); |
| + pending_cancel.Run(net::OK); |
| + return true; |
| +} |
| + |
| +FileSystemQuotaUtil* SandboxFileWriter::quota_util() const { |
| + DCHECK(file_system_context_.get()); |
| + return file_system_context_->GetQuotaUtil(file_system_type_); |
| +} |
| + |
| +} // namespace fileapi |