diff options
author | kinuko@chromium.org <kinuko@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-05-22 09:19:31 +0000 |
---|---|---|
committer | kinuko@chromium.org <kinuko@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-05-22 09:19:31 +0000 |
commit | 397a1f4db17560722bcb45108ccf7bf07fd5139f (patch) | |
tree | 4527d92f136e2e32c2cea613e69e1cd468cab89a | |
parent | 1adcd7531107e881f31ce3f1cb18a87d94935347 (diff) | |
download | chromium_src-397a1f4db17560722bcb45108ccf7bf07fd5139f.zip chromium_src-397a1f4db17560722bcb45108ccf7bf07fd5139f.tar.gz chromium_src-397a1f4db17560722bcb45108ccf7bf07fd5139f.tar.bz2 |
Implement SandboxFileWriter and rewrite FileWriterDelegate to use it
BUG=127529
TEST=FileWriterDelegate*, FileSystemOperationWrite*
Review URL: https://chromiumcodereview.appspot.com/10387054
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@138236 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | webkit/fileapi/file_system_operation.cc | 54 | ||||
-rw-r--r-- | webkit/fileapi/file_system_operation.h | 8 | ||||
-rw-r--r-- | webkit/fileapi/file_writer.h | 14 | ||||
-rw-r--r-- | webkit/fileapi/file_writer_delegate.cc | 192 | ||||
-rw-r--r-- | webkit/fileapi/file_writer_delegate.h | 26 | ||||
-rw-r--r-- | webkit/fileapi/file_writer_delegate_unittest.cc | 195 | ||||
-rw-r--r-- | webkit/fileapi/sandbox_file_writer.cc | 248 | ||||
-rw-r--r-- | webkit/fileapi/sandbox_file_writer.h | 90 | ||||
-rw-r--r-- | webkit/fileapi/webkit_fileapi.gypi | 2 |
9 files changed, 495 insertions, 334 deletions
diff --git a/webkit/fileapi/file_system_operation.cc b/webkit/fileapi/file_system_operation.cc index 0ca2490..95ee278 100644 --- a/webkit/fileapi/file_system_operation.cc +++ b/webkit/fileapi/file_system_operation.cc @@ -19,6 +19,7 @@ #include "webkit/fileapi/file_system_types.h" #include "webkit/fileapi/file_system_util.h" #include "webkit/fileapi/file_writer_delegate.h" +#include "webkit/fileapi/sandbox_file_writer.h" #include "webkit/quota/quota_manager.h" #include "webkit/quota/quota_types.h" @@ -79,17 +80,6 @@ FileSystemOperation::TaskParamsForDidGetQuota::TaskParamsForDidGetQuota() FileSystemOperation::TaskParamsForDidGetQuota::~TaskParamsForDidGetQuota() {} FileSystemOperation::~FileSystemOperation() { - if (file_writer_delegate_.get()) { - FileSystemOperationContext* c = - new FileSystemOperationContext(operation_context_); - base::FileUtilProxy::RelayClose( - file_system_context()->file_task_runner(), - base::Bind(&FileSystemFileUtil::Close, - base::Unretained(src_util_), - base::Owned(c)), - file_writer_delegate_->file(), - base::FileUtilProxy::StatusCallback()); - } } void FileSystemOperation::CreateFile(const GURL& path_url, @@ -288,17 +278,15 @@ void FileSystemOperation::Write( } DCHECK(blob_url.is_valid()); file_writer_delegate_.reset(new FileWriterDelegate( - this, src_path_, offset)); + base::Bind(&FileSystemOperation::DidWrite, weak_factory_.GetWeakPtr()), + scoped_ptr<FileWriter>( + new SandboxFileWriter(file_system_context(), path_url, offset)))); set_write_callback(callback); scoped_ptr<net::URLRequest> blob_request( new net::URLRequest(blob_url, file_writer_delegate_.get())); blob_request->set_context(url_request_context); - GetUsageAndQuotaThenRunTask( - src_path_.origin(), src_path_.type(), - base::Bind(&FileSystemOperation::DoWrite, weak_factory_.GetWeakPtr(), - base::Passed(&blob_request)), - base::Bind(callback, base::PLATFORM_FILE_ERROR_FAILED, 0, true)); + file_writer_delegate_->Start(blob_request.Pass()); } void FileSystemOperation::Truncate(const GURL& path_url, int64 length, @@ -542,23 +530,6 @@ void FileSystemOperation::DoMove(const StatusCallback& callback) { base::Owned(this), callback)); } -void FileSystemOperation::DoWrite(scoped_ptr<net::URLRequest> blob_request) { - int file_flags = base::PLATFORM_FILE_OPEN | - base::PLATFORM_FILE_WRITE | - base::PLATFORM_FILE_ASYNC; - - // We may get deleted on the way so allocate a new operation context - // to keep it alive. - FileSystemOperationContext* write_context = new FileSystemOperationContext( - operation_context_); - FileSystemFileUtilProxy::CreateOrOpen( - write_context, src_util_, src_path_, file_flags, - base::Bind(&FileSystemOperation::OnFileOpenedForWrite, - weak_factory_.GetWeakPtr(), - base::Passed(&blob_request), - base::Owned(write_context))); -} - void FileSystemOperation::DoTruncate(const StatusCallback& callback, int64 length) { FileSystemFileUtilProxy::Truncate( @@ -671,21 +642,6 @@ void FileSystemOperation::DidOpenFile( callback.Run(rv, file.ReleaseValue(), peer_handle_); } -void FileSystemOperation::OnFileOpenedForWrite( - scoped_ptr<net::URLRequest> blob_request, - FileSystemOperationContext* unused, - base::PlatformFileError rv, - base::PassPlatformFile file, - bool created) { - if (rv != base::PLATFORM_FILE_OK) { - if (!write_callback_.is_null()) - write_callback_.Run(rv, 0, false); - delete this; - return; - } - file_writer_delegate_->Start(file.ReleaseValue(), blob_request.Pass()); -} - base::PlatformFileError FileSystemOperation::SetUpFileSystemPath( const GURL& path_url, FileSystemPath* file_system_path, diff --git a/webkit/fileapi/file_system_operation.h b/webkit/fileapi/file_system_operation.h index 1373df4..6036bc6 100644 --- a/webkit/fileapi/file_system_operation.h +++ b/webkit/fileapi/file_system_operation.h @@ -174,7 +174,6 @@ class FileSystemOperation : public FileSystemOperationInterface { bool recursive); void DoCopy(const StatusCallback& callback); void DoMove(const StatusCallback& callback); - void DoWrite(scoped_ptr<net::URLRequest> blob_request); void DoTruncate(const StatusCallback& callback, int64 length); void DoOpenFile(const OpenFileCallback& callback, int file_flags); @@ -218,13 +217,6 @@ class FileSystemOperation : public FileSystemOperationInterface { base::PassPlatformFile file, bool created); - // Helper for Write(). - void OnFileOpenedForWrite(scoped_ptr<net::URLRequest> blob_request, - FileSystemOperationContext* context_unused, - base::PlatformFileError rv, - base::PassPlatformFile file, - bool created); - // Checks the validity of a given |path_url| and and populates // |path| and |file_util| for |mode|. base::PlatformFileError SetUpFileSystemPath( diff --git a/webkit/fileapi/file_writer.h b/webkit/fileapi/file_writer.h index c28c5d9..109b8c7 100644 --- a/webkit/fileapi/file_writer.h +++ b/webkit/fileapi/file_writer.h @@ -16,6 +16,12 @@ class IOBuffer; namespace fileapi { // A generic interface for writing to a file-like object. +// +// TODO(kinuko): Consider better naming. (http://crbug.com/128483) +// Note: this does not directly correspond to FileWriter in File API (which +// is implemented by WebCore::FileWriter), though this class is used to +// implement a part of it. FileWriterDelegate is NOT a delegate of this +// class either. class FileWriter { public: // Closes the file. If there's an in-flight operation, it is canceled (i.e., @@ -31,6 +37,13 @@ class FileWriter { // callback will be run on the thread where Write() was called when the write // has completed. // + // This errors out (either synchronously or via callback) with: + // net::ERR_FILE_NOT_FOUND: When the target file is not found. + // net::ERR_ACCESS_DENIED: When the target file is a directory or + // the writer has no permission on the file. + // net::ERR_FILE_NO_SPACE: When the write will result in out of quota + // or there is not enough room left on the disk. + // // It is invalid to call Write while there is an in-flight async operation. virtual int Write(net::IOBuffer* buf, int buf_len, const net::CompletionCallback& callback) = 0; @@ -53,4 +66,3 @@ class FileWriter { } // namespace fileapi #endif // WEBKIT_FILEAPI_FILE_WRITER_H_ - diff --git a/webkit/fileapi/file_writer_delegate.cc b/webkit/fileapi/file_writer_delegate.cc index 283cbe0..d8c33ba 100644 --- a/webkit/fileapi/file_writer_delegate.cc +++ b/webkit/fileapi/file_writer_delegate.cc @@ -13,9 +13,7 @@ #include "base/threading/thread_restrictions.h" #include "net/base/net_errors.h" #include "webkit/fileapi/file_system_context.h" -#include "webkit/fileapi/file_system_operation.h" -#include "webkit/fileapi/file_system_operation_context.h" -#include "webkit/fileapi/file_system_quota_util.h" +#include "webkit/fileapi/file_writer.h" namespace fileapi { @@ -23,70 +21,30 @@ static const int kReadBufSize = 32768; namespace { -typedef base::Callback<void(base::PlatformFileError /* error code */, - const base::PlatformFileInfo& /* file_info */)> - InitializeTaskCallback; - -class InitializeTask : public base::RefCountedThreadSafe<InitializeTask> { - public: - InitializeTask( - base::PlatformFile file, - const InitializeTaskCallback& callback) - : original_loop_(base::MessageLoopProxy::current()), - error_code_(base::PLATFORM_FILE_OK), - file_(file), - callback_(callback) { - DCHECK_EQ(false, callback.is_null()); - } - - bool Start(base::SequencedTaskRunner* task_runner, - const tracked_objects::Location& from_here) { - return task_runner->PostTask( - from_here, - base::Bind(&InitializeTask::ProcessOnTargetThread, this)); - } - - private: - friend class base::RefCountedThreadSafe<InitializeTask>; - ~InitializeTask() {} - - void RunCallback() { - callback_.Run(error_code_, file_info_); +base::PlatformFileError NetErrorToPlatformFileError(int error) { +// TODO(kinuko): Move this static method to more convenient place. + switch (error) { + case net::ERR_FILE_NO_SPACE: + return base::PLATFORM_FILE_ERROR_NO_SPACE; + case net::ERR_FILE_NOT_FOUND: + return base::PLATFORM_FILE_ERROR_NOT_FOUND; + case net::ERR_ACCESS_DENIED: + return base::PLATFORM_FILE_ERROR_ACCESS_DENIED; + default: + return base::PLATFORM_FILE_ERROR_FAILED; } +} - void ProcessOnTargetThread() { - if (!base::GetPlatformFileInfo(file_, &file_info_)) - error_code_ = base::PLATFORM_FILE_ERROR_FAILED; - original_loop_->PostTask( - FROM_HERE, - base::Bind(&InitializeTask::RunCallback, this)); - } - - scoped_refptr<base::MessageLoopProxy> original_loop_; - base::PlatformFileError error_code_; - - base::PlatformFile file_; - InitializeTaskCallback callback_; - - base::PlatformFileInfo file_info_; -}; - -} // namespace (anonymous) +} // namespace FileWriterDelegate::FileWriterDelegate( - FileSystemOperation* file_system_operation, - const FileSystemPath& path, - int64 offset) - : file_system_operation_(file_system_operation), - file_(base::kInvalidPlatformFileValue), - path_(path), - offset_(offset), - has_pending_write_(false), + const FileSystemOperationInterface::WriteCallback& write_callback, + scoped_ptr<FileWriter> file_writer) + : write_callback_(write_callback), + file_writer_(file_writer.Pass()), bytes_written_backlog_(0), bytes_written_(0), bytes_read_(0), - total_bytes_written_(0), - allowed_bytes_to_write_(0), io_buffer_(new net::IOBufferWithSize(kReadBufSize)), ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) { } @@ -94,44 +52,11 @@ FileWriterDelegate::FileWriterDelegate( FileWriterDelegate::~FileWriterDelegate() { } -void FileWriterDelegate::OnGetFileInfoAndStartRequest( - scoped_ptr<net::URLRequest> request, - base::PlatformFileError error, - const base::PlatformFileInfo& file_info) { - if (error != base::PLATFORM_FILE_OK) { - OnError(error); - return; - } - int64 allowed_bytes_growth = - file_system_operation_context()->allowed_bytes_growth(); - if (allowed_bytes_growth < 0) - allowed_bytes_growth = 0; - int64 overlap = file_info.size - offset_; - allowed_bytes_to_write_ = allowed_bytes_growth; - if (kint64max - overlap > allowed_bytes_growth) - allowed_bytes_to_write_ += overlap; - size_ = file_info.size; - file_stream_.reset(new net::FileStream( - file_, - base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_WRITE | - base::PLATFORM_FILE_ASYNC, - NULL)); - DCHECK(!request_.get()); +void FileWriterDelegate::Start(scoped_ptr<net::URLRequest> request) { request_ = request.Pass(); request_->Start(); } -void FileWriterDelegate::Start(base::PlatformFile file, - scoped_ptr<net::URLRequest> request) { - file_ = file; - - scoped_refptr<InitializeTask> relay = new InitializeTask( - file_, - base::Bind(&FileWriterDelegate::OnGetFileInfoAndStartRequest, - weak_factory_.GetWeakPtr(), base::Passed(&request))); - relay->Start(file_system_operation_context()->file_task_runner(), FROM_HERE); -} - bool FileWriterDelegate::Cancel() { if (request_.get()) { // This halts any callbacks on this delegate. @@ -139,9 +64,12 @@ bool FileWriterDelegate::Cancel() { request_->Cancel(); } - // Return true to finish immediately if we're not writing. - // Otherwise we'll do the final cleanup in the write callback. - return !has_pending_write_; + const int status = file_writer_->Cancel( + base::Bind(&FileWriterDelegate::OnWriteCancelled, + weak_factory_.GetWeakPtr())); + // Return true to finish immediately if we have no pending writes. + // Otherwise we'll do the final cleanup in the Cancel callback. + return (status != net::ERR_IO_PENDING); } void FileWriterDelegate::OnReceivedRedirect(net::URLRequest* request, @@ -173,18 +101,10 @@ void FileWriterDelegate::OnSSLCertificateError(net::URLRequest* request, void FileWriterDelegate::OnResponseStarted(net::URLRequest* request) { DCHECK_EQ(request_.get(), request); - // file_stream_->Seek() blocks the IO thread. - // See http://crbug.com/75548. - base::ThreadRestrictions::ScopedAllowIO allow_io; if (!request->status().is_success() || request->GetResponseCode() != 200) { OnError(base::PLATFORM_FILE_ERROR_FAILED); return; } - int64 error = file_stream_->SeekSync(net::FROM_BEGIN, offset_); - if (error != offset_) { - OnError(base::PLATFORM_FILE_ERROR_FAILED); - return; - } Read(); } @@ -201,8 +121,7 @@ void FileWriterDelegate::OnReadCompleted(net::URLRequest* request, void FileWriterDelegate::Read() { bytes_written_ = 0; bytes_read_ = 0; - if (request_->Read(io_buffer_.get(), io_buffer_->size(), - &bytes_read_)) { + if (request_->Read(io_buffer_.get(), io_buffer_->size(), &bytes_read_)) { MessageLoop::current()->PostTask( FROM_HERE, base::Bind(&FileWriterDelegate::OnDataReceived, @@ -226,24 +145,9 @@ void FileWriterDelegate::OnDataReceived(int bytes_read) { } void FileWriterDelegate::Write() { - // allowed_bytes_to_write could be negative if the file size is - // greater than the current (possibly new) quota. - // (The UI should clear the entire origin data if the smaller quota size - // is set in general, though the UI/deletion code is not there yet.) - DCHECK(total_bytes_written_ <= allowed_bytes_to_write_ || - allowed_bytes_to_write_ < 0); - if (total_bytes_written_ >= allowed_bytes_to_write_) { - OnError(base::PLATFORM_FILE_ERROR_NO_SPACE); - return; - } - int64 bytes_to_write = bytes_read_ - bytes_written_; - if (bytes_to_write > allowed_bytes_to_write_ - total_bytes_written_) - bytes_to_write = allowed_bytes_to_write_ - total_bytes_written_; - - has_pending_write_ = true; int write_response = - file_stream_->Write(cursor_, + file_writer_->Write(cursor_, static_cast<int>(bytes_to_write), base::Bind(&FileWriterDelegate::OnDataWritten, weak_factory_.GetWeakPtr())); @@ -253,26 +157,20 @@ void FileWriterDelegate::Write() { base::Bind(&FileWriterDelegate::OnDataWritten, weak_factory_.GetWeakPtr(), write_response)); else if (net::ERR_IO_PENDING != write_response) - OnError(base::PLATFORM_FILE_ERROR_FAILED); + OnError(NetErrorToPlatformFileError(write_response)); } void FileWriterDelegate::OnDataWritten(int write_response) { - has_pending_write_ = false; if (write_response > 0) { - if (request_->status().status() == net::URLRequestStatus::CANCELED) { - OnProgress(write_response, true); - return; - } OnProgress(write_response, false); cursor_->DidConsume(write_response); bytes_written_ += write_response; - total_bytes_written_ += write_response; if (bytes_written_ == bytes_read_) Read(); else Write(); } else { - OnError(base::PLATFORM_FILE_ERROR_FAILED); + OnError(NetErrorToPlatformFileError(write_response)); } } @@ -282,22 +180,11 @@ void FileWriterDelegate::OnError(base::PlatformFileError error) { request_->Cancel(); } - file_system_operation_->DidWrite(error, 0, true); + write_callback_.Run(error, 0, true); } void FileWriterDelegate::OnProgress(int bytes_written, bool done) { DCHECK(bytes_written + bytes_written_backlog_ >= bytes_written_backlog_); - if (quota_util() && - bytes_written > 0 && - total_bytes_written_ + bytes_written + offset_ > size_) { - int overlapped = 0; - if (total_bytes_written_ + offset_ < size_) - overlapped = size_ - total_bytes_written_ - offset_; - quota_util()->proxy()->UpdateOriginUsage( - file_system_operation_->file_system_context()->quota_manager_proxy(), - path_.origin(), path_.type(), - bytes_written - overlapped); - } static const int kMinProgressDelayMS = 200; base::Time currentTime = base::Time::Now(); if (done || last_progress_event_time_.is_null() || @@ -306,28 +193,15 @@ void FileWriterDelegate::OnProgress(int bytes_written, bool done) { bytes_written += bytes_written_backlog_; last_progress_event_time_ = currentTime; bytes_written_backlog_ = 0; - if (done && quota_util()) - quota_util()->proxy()->EndUpdateOrigin(path_.origin(), path_.type()); - file_system_operation_->DidWrite( + write_callback_.Run( base::PLATFORM_FILE_OK, bytes_written, done); return; } bytes_written_backlog_ += bytes_written; } -FileSystemOperationContext* -FileWriterDelegate::file_system_operation_context() const { - DCHECK(file_system_operation_); - DCHECK(file_system_operation_->file_system_operation_context()); - return file_system_operation_->file_system_operation_context(); -} - -FileSystemQuotaUtil* FileWriterDelegate::quota_util() const { - DCHECK(file_system_operation_); - DCHECK(file_system_operation_->file_system_context()); - DCHECK(file_system_operation_->file_system_operation_context()); - return file_system_operation_->file_system_context()->GetQuotaUtil( - path_.type()); +void FileWriterDelegate::OnWriteCancelled(int status) { + write_callback_.Run(base::PLATFORM_FILE_ERROR_ABORT, 0, true); } } // namespace fileapi diff --git a/webkit/fileapi/file_writer_delegate.h b/webkit/fileapi/file_writer_delegate.h index 3b58ae0..e2bf74d 100644 --- a/webkit/fileapi/file_writer_delegate.h +++ b/webkit/fileapi/file_writer_delegate.h @@ -13,24 +13,23 @@ #include "net/base/file_stream.h" #include "net/base/io_buffer.h" #include "net/url_request/url_request.h" +#include "webkit/fileapi/file_system_operation_interface.h" #include "webkit/fileapi/file_system_path.h" namespace fileapi { -class FileSystemOperation; class FileSystemOperationContext; class FileSystemQuotaUtil; +class FileWriter; class FileWriterDelegate : public net::URLRequest::Delegate { public: FileWriterDelegate( - FileSystemOperation* write_operation, - const FileSystemPath& path, - int64 offset); + const FileSystemOperationInterface::WriteCallback& write_callback, + scoped_ptr<FileWriter> file_writer); virtual ~FileWriterDelegate(); - void Start(base::PlatformFile file, - scoped_ptr<net::URLRequest> request); + void Start(scoped_ptr<net::URLRequest> request); // Cancels the current write operation. Returns true if it is ok to // delete this instance immediately. Otherwise this will call @@ -38,8 +37,6 @@ class FileWriterDelegate : public net::URLRequest::Delegate { // perform the final cleanup. bool Cancel(); - base::PlatformFile file() const { return file_; } - virtual void OnReceivedRedirect(net::URLRequest* request, const GURL& new_url, bool* defer_redirect) OVERRIDE; @@ -66,25 +63,18 @@ class FileWriterDelegate : public net::URLRequest::Delegate { void OnDataWritten(int write_response); void OnError(base::PlatformFileError error); void OnProgress(int bytes_read, bool done); + void OnWriteCancelled(int status); - FileSystemOperationContext* file_system_operation_context() const; FileSystemQuotaUtil* quota_util() const; - FileSystemOperation* file_system_operation_; - base::PlatformFile file_; - FileSystemPath path_; - int64 size_; - int64 offset_; - bool has_pending_write_; + FileSystemOperationInterface::WriteCallback write_callback_; + scoped_ptr<FileWriter> file_writer_; base::Time last_progress_event_time_; int bytes_written_backlog_; int bytes_written_; int bytes_read_; - int64 total_bytes_written_; - int64 allowed_bytes_to_write_; scoped_refptr<net::IOBufferWithSize> io_buffer_; scoped_refptr<net::DrainableIOBuffer> cursor_; - scoped_ptr<net::FileStream> file_stream_; scoped_ptr<net::URLRequest> request_; base::WeakPtrFactory<FileWriterDelegate> weak_factory_; }; diff --git a/webkit/fileapi/file_writer_delegate_unittest.cc b/webkit/fileapi/file_writer_delegate_unittest.cc index 1ce152e..1ff0e83 100644 --- a/webkit/fileapi/file_writer_delegate_unittest.cc +++ b/webkit/fileapi/file_writer_delegate_unittest.cc @@ -13,7 +13,6 @@ #include "base/basictypes.h" #include "base/bind.h" -#include "base/file_util_proxy.h" #include "base/message_loop.h" #include "base/scoped_temp_dir.h" #include "googleurl/src/gurl.h" @@ -24,12 +23,9 @@ #include "testing/platform_test.h" #include "webkit/fileapi/file_system_context.h" #include "webkit/fileapi/file_system_operation.h" -#include "webkit/fileapi/file_system_operation_context.h" #include "webkit/fileapi/file_system_test_helper.h" -#include "webkit/fileapi/file_system_usage_cache.h" #include "webkit/fileapi/file_writer_delegate.h" -#include "webkit/quota/quota_manager.h" -#include "webkit/fileapi/sandbox_mount_point_provider.h" +#include "webkit/fileapi/sandbox_file_writer.h" namespace fileapi { @@ -81,46 +77,54 @@ class FileWriterDelegateTest : public PlatformTest { public: FileWriterDelegateTest() : loop_(MessageLoop::TYPE_IO), - test_helper_(GURL("http://example.com"), kFileSystemTypeTest), - file_(base::kInvalidPlatformFileValue) {} + test_helper_(GURL("http://example.com"), kFileSystemTypeTest) {} protected: - virtual void SetUp(); - virtual void TearDown(); + virtual void SetUp() OVERRIDE; + virtual void TearDown() OVERRIDE; - virtual void SetUpTestHelper(const FilePath& base_dir) { - test_helper_.SetUp(base_dir, NULL); + FileSystemFileUtil* file_util() { + return test_helper_.file_util(); } int64 ComputeCurrentOriginUsage() { - base::FlushPlatformFile(file_); return test_helper_.ComputeCurrentOriginUsage(); } - // Creates and sets up a FileWriterDelegate for writing the given |blob_url| - // to a file (file_) from |offset| with |allowed_growth| quota setting. + FileSystemPath GetFileSystemPath(const char* file_name) const { + return test_helper_.CreatePathFromUTF8(file_name); + } + + GURL GetFileSystemURL(const char* file_name) const { + return test_helper_.GetURLForPath(FilePath().AppendASCII(file_name)); + } + + FileWriterDelegate* CreateWriterDelegate( + const char* test_file_path, + int64 offset, + int64 allowed_growth, + Result* result) { + SandboxFileWriter* writer = new SandboxFileWriter( + test_helper_.file_system_context(), + GetFileSystemURL(test_file_path), + offset); + writer->set_default_quota(allowed_growth); + return new FileWriterDelegate( + base::Bind(&Result::DidWrite, base::Unretained(result)), + scoped_ptr<FileWriter>(writer)); + } + + // Creates and sets up a FileWriterDelegate for writing the given |blob_url|, + // and creates a new FileWriterDelegate for the file. void PrepareForWrite(const GURL& blob_url, int64 offset, int64 allowed_growth) { - bool created; - base::PlatformFileError error_code; - file_ = base::CreatePlatformFile( - file_path_, - base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_WRITE | - base::PLATFORM_FILE_ASYNC, - &created, &error_code); - ASSERT_EQ(base::PLATFORM_FILE_OK, error_code); - result_.reset(new Result()); - file_writer_delegate_.reset(new FileWriterDelegate( - CreateNewOperation(result_.get(), allowed_growth), - test_helper_.CreatePath(file_path_), - offset)); + file_writer_delegate_.reset( + CreateWriterDelegate("test", offset, allowed_growth, result_.get())); request_.reset(new net::URLRequest(blob_url, file_writer_delegate_.get())); } - FileSystemOperation* CreateNewOperation(Result* result, int64 quota); - static net::URLRequest::ProtocolFactory Factory; // This should be alive until the very end of this instance. @@ -132,11 +136,10 @@ class FileWriterDelegateTest : public PlatformTest { FileSystemTestOriginHelper test_helper_; ScopedTempDir dir_; - FilePath file_path_; - PlatformFile file_; static const char* content_; }; + const char* FileWriterDelegateTest::content_ = NULL; namespace { @@ -200,29 +203,26 @@ net::URLRequestJob* FileWriterDelegateTest::Factory( void FileWriterDelegateTest::SetUp() { ASSERT_TRUE(dir_.CreateUniqueTempDir()); FilePath base_dir = dir_.path().AppendASCII("filesystem"); - SetUpTestHelper(base_dir); - ASSERT_TRUE(file_util::CreateTemporaryFileInDir( - test_helper_.GetOriginRootPath(), &file_path_)); + test_helper_.SetUp(base_dir, NULL); + + scoped_ptr<FileSystemOperationContext> context( + test_helper_.NewOperationContext()); + context->set_allowed_bytes_growth(kint64max); + bool created = false; + base::PlatformFileError error = file_util()->EnsureFileExists( + context.get(), + GetFileSystemPath("test"), + &created); + ASSERT_EQ(base::PLATFORM_FILE_OK, error); + ASSERT_TRUE(created); net::URLRequest::Deprecated::RegisterProtocolFactory("blob", &Factory); } void FileWriterDelegateTest::TearDown() { net::URLRequest::Deprecated::RegisterProtocolFactory("blob", NULL); - base::ClosePlatformFile(file_); test_helper_.TearDown(); } -FileSystemOperation* FileWriterDelegateTest::CreateNewOperation( - Result* result, int64 quota) { - FileSystemOperation* operation = test_helper_.NewOperation(); - operation->set_write_callback(base::Bind(&Result::DidWrite, - base::Unretained(result))); - FileSystemOperationContext* context = - operation->file_system_operation_context(); - context->set_allowed_bytes_growth(quota); - return operation; -} - TEST_F(FileWriterDelegateTest, WriteSuccessWithoutQuotaLimit) { const GURL kBlobURL("blob:nolimit"); content_ = kData; @@ -230,16 +230,16 @@ TEST_F(FileWriterDelegateTest, WriteSuccessWithoutQuotaLimit) { PrepareForWrite(kBlobURL, 0, quota::QuotaManager::kNoLimit); ASSERT_EQ(0, test_helper_.GetCachedOriginUsage()); - file_writer_delegate_->Start(file_, request_.Pass()); + file_writer_delegate_->Start(request_.Pass()); MessageLoop::current()->Run(); + + ASSERT_TRUE(result_->complete()); + file_writer_delegate_.reset(); + ASSERT_EQ(kDataSize, test_helper_.GetCachedOriginUsage()); EXPECT_EQ(ComputeCurrentOriginUsage(), test_helper_.GetCachedOriginUsage()); - EXPECT_EQ(kDataSize, result_->bytes_written()); EXPECT_EQ(base::PLATFORM_FILE_OK, result_->status()); - EXPECT_TRUE(result_->complete()); - - file_writer_delegate_.reset(); } TEST_F(FileWriterDelegateTest, WriteSuccessWithJustQuota) { @@ -249,16 +249,16 @@ TEST_F(FileWriterDelegateTest, WriteSuccessWithJustQuota) { PrepareForWrite(kBlobURL, 0, kAllowedGrowth); ASSERT_EQ(0, test_helper_.GetCachedOriginUsage()); - file_writer_delegate_->Start(file_, request_.Pass()); + file_writer_delegate_->Start(request_.Pass()); MessageLoop::current()->Run(); + ASSERT_TRUE(result_->complete()); + file_writer_delegate_.reset(); + ASSERT_EQ(kAllowedGrowth, test_helper_.GetCachedOriginUsage()); EXPECT_EQ(ComputeCurrentOriginUsage(), test_helper_.GetCachedOriginUsage()); - file_writer_delegate_.reset(); - EXPECT_EQ(kAllowedGrowth, result_->bytes_written()); EXPECT_EQ(base::PLATFORM_FILE_OK, result_->status()); - EXPECT_TRUE(result_->complete()); } TEST_F(FileWriterDelegateTest, WriteFailureByQuota) { @@ -268,13 +268,14 @@ TEST_F(FileWriterDelegateTest, WriteFailureByQuota) { PrepareForWrite(kBlobURL, 0, kAllowedGrowth); ASSERT_EQ(0, test_helper_.GetCachedOriginUsage()); - file_writer_delegate_->Start(file_, request_.Pass()); + file_writer_delegate_->Start(request_.Pass()); MessageLoop::current()->Run(); + ASSERT_TRUE(result_->complete()); + file_writer_delegate_.reset(); + ASSERT_EQ(kAllowedGrowth, test_helper_.GetCachedOriginUsage()); EXPECT_EQ(ComputeCurrentOriginUsage(), test_helper_.GetCachedOriginUsage()); - file_writer_delegate_.reset(); - EXPECT_EQ(kAllowedGrowth, result_->bytes_written()); EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE, result_->status()); EXPECT_TRUE(result_->complete()); @@ -287,13 +288,14 @@ TEST_F(FileWriterDelegateTest, WriteZeroBytesSuccessfullyWithZeroQuota) { PrepareForWrite(kBlobURL, 0, kAllowedGrowth); ASSERT_EQ(0, test_helper_.GetCachedOriginUsage()); - file_writer_delegate_->Start(file_, request_.Pass()); + file_writer_delegate_->Start(request_.Pass()); MessageLoop::current()->Run(); + ASSERT_TRUE(result_->complete()); + file_writer_delegate_.reset(); + ASSERT_EQ(kAllowedGrowth, test_helper_.GetCachedOriginUsage()); EXPECT_EQ(ComputeCurrentOriginUsage(), test_helper_.GetCachedOriginUsage()); - file_writer_delegate_.reset(); - EXPECT_EQ(kAllowedGrowth, result_->bytes_written()); EXPECT_EQ(base::PLATFORM_FILE_OK, result_->status()); EXPECT_TRUE(result_->complete()); @@ -304,18 +306,13 @@ TEST_F(FileWriterDelegateTest, WriteSuccessWithoutQuotaLimitConcurrent) { scoped_ptr<net::URLRequest> request2; scoped_ptr<Result> result2; - FilePath file_path2; - PlatformFile file2; - bool created; - base::PlatformFileError error_code; - ASSERT_TRUE(file_util::CreateTemporaryFileInDir( - test_helper_.GetOriginRootPath(), &file_path2)); - file2 = base::CreatePlatformFile( - file_path2, - base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_WRITE | - base::PLATFORM_FILE_ASYNC, - &created, &error_code); - ASSERT_EQ(base::PLATFORM_FILE_OK, error_code); + scoped_ptr<FileSystemOperationContext> context( + test_helper_.NewOperationContext()); + bool created = false; + file_util()->EnsureFileExists(context.get(), + GetFileSystemPath("test2"), + &created); + ASSERT_TRUE(created); const GURL kBlobURL("blob:nolimitconcurrent"); const GURL kBlobURL2("blob:nolimitconcurrent2"); @@ -325,32 +322,29 @@ TEST_F(FileWriterDelegateTest, WriteSuccessWithoutQuotaLimitConcurrent) { // Credate another FileWriterDelegate for concurrent write. result2.reset(new Result()); - file_writer_delegate2.reset(new FileWriterDelegate( - CreateNewOperation(result2.get(), quota::QuotaManager::kNoLimit), - test_helper_.CreatePath(file_path2), 0)); + file_writer_delegate2.reset(CreateWriterDelegate( + "test2", 0, quota::QuotaManager::kNoLimit, result2.get())); request2.reset(new net::URLRequest(kBlobURL2, file_writer_delegate2.get())); ASSERT_EQ(0, test_helper_.GetCachedOriginUsage()); - file_writer_delegate_->Start(file_, request_.Pass()); - file_writer_delegate2->Start(file2, request2.Pass()); + file_writer_delegate_->Start(request_.Pass()); + file_writer_delegate2->Start(request2.Pass()); MessageLoop::current()->Run(); if (!result_->complete() || !result2->complete()) MessageLoop::current()->Run(); + ASSERT_TRUE(result_->complete()); + ASSERT_TRUE(result2->complete()); + file_writer_delegate_.reset(); + file_writer_delegate2.reset(); + ASSERT_EQ(kDataSize * 2, test_helper_.GetCachedOriginUsage()); - base::FlushPlatformFile(file2); EXPECT_EQ(ComputeCurrentOriginUsage(), test_helper_.GetCachedOriginUsage()); - file_writer_delegate_.reset(); - EXPECT_EQ(kDataSize, result_->bytes_written()); EXPECT_EQ(base::PLATFORM_FILE_OK, result_->status()); - EXPECT_TRUE(result_->complete()); EXPECT_EQ(kDataSize, result2->bytes_written()); EXPECT_EQ(base::PLATFORM_FILE_OK, result2->status()); - EXPECT_TRUE(result2->complete()); - - base::ClosePlatformFile(file2); } TEST_F(FileWriterDelegateTest, WritesWithQuotaAndOffset) { @@ -364,20 +358,22 @@ TEST_F(FileWriterDelegateTest, WritesWithQuotaAndOffset) { PrepareForWrite(kBlobURL, offset, allowed_growth); ASSERT_EQ(0, test_helper_.GetCachedOriginUsage()); - file_writer_delegate_->Start(file_, request_.Pass()); + file_writer_delegate_->Start(request_.Pass()); MessageLoop::current()->Run(); + ASSERT_TRUE(result_->complete()); + file_writer_delegate_.reset(); + ASSERT_EQ(kDataSize, test_helper_.GetCachedOriginUsage()); EXPECT_EQ(ComputeCurrentOriginUsage(), test_helper_.GetCachedOriginUsage()); EXPECT_EQ(kDataSize, result_->bytes_written()); EXPECT_EQ(base::PLATFORM_FILE_OK, result_->status()); - EXPECT_TRUE(result_->complete()); // Trying to overwrite kDataSize bytes data while allowed_growth is 20. offset = 0; allowed_growth = 20; PrepareForWrite(kBlobURL, offset, allowed_growth); - file_writer_delegate_->Start(file_, request_.Pass()); + file_writer_delegate_->Start(request_.Pass()); MessageLoop::current()->Run(); EXPECT_EQ(kDataSize, test_helper_.GetCachedOriginUsage()); EXPECT_EQ(ComputeCurrentOriginUsage(), test_helper_.GetCachedOriginUsage()); @@ -391,13 +387,15 @@ TEST_F(FileWriterDelegateTest, WritesWithQuotaAndOffset) { allowed_growth = 55; PrepareForWrite(kBlobURL, offset, allowed_growth); - file_writer_delegate_->Start(file_, request_.Pass()); + file_writer_delegate_->Start(request_.Pass()); MessageLoop::current()->Run(); + ASSERT_TRUE(result_->complete()); + file_writer_delegate_.reset(); + EXPECT_EQ(offset + kDataSize, test_helper_.GetCachedOriginUsage()); EXPECT_EQ(ComputeCurrentOriginUsage(), test_helper_.GetCachedOriginUsage()); EXPECT_EQ(kDataSize, result_->bytes_written()); EXPECT_EQ(base::PLATFORM_FILE_OK, result_->status()); - EXPECT_TRUE(result_->complete()); // Trying to overwrite 45 bytes data while allowed_growth is -20. offset = 0; @@ -405,13 +403,15 @@ TEST_F(FileWriterDelegateTest, WritesWithQuotaAndOffset) { PrepareForWrite(kBlobURL, offset, allowed_growth); int64 pre_write_usage = ComputeCurrentOriginUsage(); - file_writer_delegate_->Start(file_, request_.Pass()); + file_writer_delegate_->Start(request_.Pass()); MessageLoop::current()->Run(); + ASSERT_TRUE(result_->complete()); + file_writer_delegate_.reset(); + EXPECT_EQ(pre_write_usage, test_helper_.GetCachedOriginUsage()); EXPECT_EQ(ComputeCurrentOriginUsage(), test_helper_.GetCachedOriginUsage()); EXPECT_EQ(kDataSize, result_->bytes_written()); EXPECT_EQ(base::PLATFORM_FILE_OK, result_->status()); - EXPECT_TRUE(result_->complete()); // Trying to overwrite 45 bytes data with offset pre_write_usage - 20, // while allowed_growth is 10. @@ -420,19 +420,16 @@ TEST_F(FileWriterDelegateTest, WritesWithQuotaAndOffset) { allowed_growth = 10; PrepareForWrite(kBlobURL, offset, allowed_growth); - file_writer_delegate_->Start(file_, request_.Pass()); + file_writer_delegate_->Start(request_.Pass()); MessageLoop::current()->Run(); + ASSERT_TRUE(result_->complete()); + file_writer_delegate_.reset(); + EXPECT_EQ(pre_write_usage + allowed_growth, test_helper_.GetCachedOriginUsage()); EXPECT_EQ(ComputeCurrentOriginUsage(), test_helper_.GetCachedOriginUsage()); EXPECT_EQ(kOverlap + allowed_growth, result_->bytes_written()); EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE, result_->status()); - EXPECT_TRUE(result_->complete()); } -class FileWriterDelegateUnlimitedTest : public FileWriterDelegateTest { - protected: - virtual void SetUpTestHelper(const FilePath& path) OVERRIDE; -}; - } // namespace fileapi diff --git a/webkit/fileapi/sandbox_file_writer.cc b/webkit/fileapi/sandbox_file_writer.cc new file mode 100644 index 0000000..e686d03 --- /dev/null +++ b/webkit/fileapi/sandbox_file_writer.cc @@ -0,0 +1,248 @@ +// 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 in overwriting case (i.e. |file_size| > 0 and +// |file_offset| < |file_size|) to make the remaining quota calculation easier. +// Specifically this widens the quota for overlapping range (so that we can +// simply compare written bytes against the adjusted quota). +int64 AdjustQuotaForOverlap(int64 quota, + int64 file_offset, + int64 file_size) { + DCHECK_LE(file_offset, file_size); + 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(!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; + if (initial_offset_ > file_size_) { + LOG(ERROR) << initial_offset_ << ", " << file_size_; + // This shouldn't happen as long as we check offset in the renderer. + NOTREACHED(); + initial_offset_ = file_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 (CancelIfRequested()) + return; + 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 (CancelIfRequested()) + return; + if (init_status != net::OK) { + has_pending_operation_ = false; + callback.Run(init_status); + return; + } + allowed_bytes_to_write_ = AdjustQuotaForOverlap( + 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 diff --git a/webkit/fileapi/sandbox_file_writer.h b/webkit/fileapi/sandbox_file_writer.h new file mode 100644 index 0000000..7f03a6c8 --- /dev/null +++ b/webkit/fileapi/sandbox_file_writer.h @@ -0,0 +1,90 @@ +// 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. + +#ifndef WEBKIT_FILEAPI_SANDBOX_FILE_WRITER_H_ +#define WEBKIT_FILEAPI_SANDBOX_FILE_WRITER_H_ + +#include "base/file_path.h" +#include "base/memory/scoped_ptr.h" +#include "base/platform_file.h" +#include "googleurl/src/gurl.h" +#include "webkit/fileapi/file_system_types.h" +#include "webkit/fileapi/file_writer.h" +#include "webkit/quota/quota_types.h" + +namespace fileapi { + +class FileSystemContext; +class FileSystemQuotaUtil; +class LocalFileWriter; + +class SandboxFileWriter : public FileWriter { + public: + SandboxFileWriter(FileSystemContext* file_system_context, + const GURL& url, + int64 initial_offset); + virtual ~SandboxFileWriter(); + + // FileWriter overrides. + virtual int Write(net::IOBuffer* buf, int buf_len, + const net::CompletionCallback& callback) OVERRIDE; + virtual int Cancel(const net::CompletionCallback& callback) OVERRIDE; + + // Used only by tests. + void set_default_quota(int64 quota) { + default_quota_ = quota; + } + + private: + // Performs quota calculation and calls local_file_writer_->Write(). + int WriteInternal(net::IOBuffer* buf, int buf_len, + const net::CompletionCallback& callback); + + // Callbacks that are chained for the first write. This eventually calls + // WriteInternal. + void DidGetFileInfo(const net::CompletionCallback& callback, + base::PlatformFileError file_error, + const base::PlatformFileInfo& file_info, + const FilePath& platform_path); + void DidGetUsageAndQuota(const net::CompletionCallback& callback, + quota::QuotaStatusCode status, + int64 usage, int64 quota); + void DidInitializeForWrite(net::IOBuffer* buf, int buf_len, + const net::CompletionCallback& callback, + int init_status); + + void DidWrite(const net::CompletionCallback& callback, int write_response); + + // Stops the in-flight operation, calls |cancel_callback_| and returns true + // if there's a pending cancel request. + bool CancelIfRequested(); + + FileSystemQuotaUtil* quota_util() const; + + scoped_refptr<FileSystemContext> file_system_context_; + const GURL url_; + int64 initial_offset_; + scoped_ptr<LocalFileWriter> local_file_writer_; + net::CompletionCallback cancel_callback_; + + GURL origin_; + FileSystemType file_system_type_; + FilePath virtual_path_; + + FilePath file_path_; + int64 file_size_; + int64 total_bytes_written_; + int64 allowed_bytes_to_write_; + bool has_pending_operation_; + + int64 default_quota_; + + base::WeakPtrFactory<SandboxFileWriter> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(SandboxFileWriter); +}; + +} // namespace fileapi + +#endif // WEBKIT_FILEAPI_SANDBOX_FILE_WRITER_H_ diff --git a/webkit/fileapi/webkit_fileapi.gypi b/webkit/fileapi/webkit_fileapi.gypi index aaf5237..0543c74 100644 --- a/webkit/fileapi/webkit_fileapi.gypi +++ b/webkit/fileapi/webkit_fileapi.gypi @@ -74,6 +74,8 @@ 'native_file_util.h', 'obfuscated_file_util.cc', 'obfuscated_file_util.h', + 'sandbox_file_writer.cc', + 'sandbox_file_writer.h', 'sandbox_mount_point_provider.cc', 'sandbox_mount_point_provider.h', 'test_mount_point_provider.cc', |