summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordmikurube@chromium.org <dmikurube@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-04-21 05:04:51 +0000
committerdmikurube@chromium.org <dmikurube@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-04-21 05:04:51 +0000
commit078c6971a60c9a375e433ded4399a23a57382053 (patch)
tree94b9294bcb406857553f34cb4520a89e4231a5ee
parentcbf61bccc4488871222e616065b1104e57c5e9ac (diff)
downloadchromium_src-078c6971a60c9a375e433ded4399a23a57382053.zip
chromium_src-078c6971a60c9a375e433ded4399a23a57382053.tar.gz
chromium_src-078c6971a60c9a375e433ded4399a23a57382053.tar.bz2
Add "allowed growth" for writing operations, such as copy, move, write and truncate.
A writing operation which increases the file size more than "allowed growth" results in PLATFORM_FILE_ERROR_NO_SPACE. BUG=74841 TEST=FileWriterDelegateTest.*,QuotaFileUtilTest.*,FileSystemOperationTest.* Review URL: http://codereview.chromium.org/6725021 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@82441 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/chrome_tests.gypi1
-rw-r--r--webkit/fileapi/file_system_file_util.cc31
-rw-r--r--webkit/fileapi/file_system_file_util.h23
-rw-r--r--webkit/fileapi/file_system_operation.cc7
-rw-r--r--webkit/fileapi/file_system_operation_context.h7
-rw-r--r--webkit/fileapi/file_writer_delegate.cc50
-rw-r--r--webkit/fileapi/file_writer_delegate.h19
-rw-r--r--webkit/fileapi/file_writer_delegate_unittest.cc257
-rw-r--r--webkit/fileapi/quota_file_util.cc82
-rw-r--r--webkit/fileapi/quota_file_util.h43
-rw-r--r--webkit/fileapi/quota_file_util_unittest.cc303
-rw-r--r--webkit/fileapi/webkit_fileapi.gypi2
-rw-r--r--webkit/tools/test_shell/test_shell.gypi1
13 files changed, 789 insertions, 37 deletions
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 395aaf3..cbf8ea0 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -1924,6 +1924,7 @@
'../webkit/fileapi/file_system_dir_url_request_job_unittest.cc',
'../webkit/fileapi/file_system_operation_write_unittest.cc',
'../webkit/fileapi/file_system_url_request_job_unittest.cc',
+ '../webkit/fileapi/file_writer_delegate_unittest.cc',
],
'conditions': [
['p2p_apis==1', {
diff --git a/webkit/fileapi/file_system_file_util.cc b/webkit/fileapi/file_system_file_util.cc
index f012a33..3e4e6c6 100644
--- a/webkit/fileapi/file_system_file_util.cc
+++ b/webkit/fileapi/file_system_file_util.cc
@@ -142,9 +142,11 @@ PlatformFileError FileSystemFileUtil::Copy(
return error_code;
if (file_util::DirectoryExists(src_file_path))
- return CopyDirectory(context, src_file_path, dest_file_path);
+ return CopyOrMoveDirectory(context, src_file_path, dest_file_path,
+ true /* copy */);
else
- return CopyOrMoveFile(context, src_file_path, dest_file_path, true);
+ return CopyOrMoveFile(context, src_file_path, dest_file_path,
+ true /* copy */);
}
PlatformFileError FileSystemFileUtil::Move(
@@ -158,15 +160,13 @@ PlatformFileError FileSystemFileUtil::Move(
if (error_code != base::PLATFORM_FILE_OK)
return error_code;
- // TODO(dmikurube): ReplaceFile if in the same filesystem.
+ // TODO(dmikurube): ReplaceFile if in the same domain and filesystem type.
if (file_util::DirectoryExists(src_file_path)) {
- PlatformFileError error =
- CopyDirectory(context, src_file_path, dest_file_path);
- if (error != base::PLATFORM_FILE_OK)
- return error;
- return Delete(context, src_file_path, true);
+ return CopyOrMoveDirectory(context, src_file_path, dest_file_path,
+ false /* copy */);
} else
- return CopyOrMoveFile(context, src_file_path, dest_file_path, false);
+ return CopyOrMoveFile(context, src_file_path, dest_file_path,
+ false /* copy */);
}
PlatformFileError FileSystemFileUtil::Delete(
@@ -285,10 +285,11 @@ PlatformFileError FileSystemFileUtil::CopyOrMoveFile(
return base::PLATFORM_FILE_ERROR_FAILED;
}
-PlatformFileError FileSystemFileUtil::CopyDirectory(
+PlatformFileError FileSystemFileUtil::CopyOrMoveDirectory(
FileSystemOperationContext* context,
const FilePath& src_file_path,
- const FilePath& dest_file_path) {
+ const FilePath& dest_file_path,
+ bool copy) {
// Re-check PerformCommonCheckAndPreparationForMoveAndCopy() by DCHECK.
DCHECK(DirectoryExists(context, src_file_path));
DCHECK(DirectoryExists(context, dest_file_path.DirName()));
@@ -317,11 +318,17 @@ PlatformFileError FileSystemFileUtil::CopyDirectory(
} else {
// CopyOrMoveFile here is the virtual overridden member function.
PlatformFileError error = CopyOrMoveFile(
- context, src_file_path_each, dest_file_path_each, true);
+ context, src_file_path_each, dest_file_path_each, copy);
if (error != base::PLATFORM_FILE_OK)
return error;
}
}
+
+ if (!copy) {
+ PlatformFileError error = Delete(context, src_file_path, true);
+ if (error != base::PLATFORM_FILE_OK)
+ return error;
+ }
return base::PLATFORM_FILE_OK;
}
diff --git a/webkit/fileapi/file_system_file_util.h b/webkit/fileapi/file_system_file_util.h
index 9e62af3..79ad9aa 100644
--- a/webkit/fileapi/file_system_file_util.h
+++ b/webkit/fileapi/file_system_file_util.h
@@ -93,6 +93,13 @@ class FileSystemFileUtil {
bool exclusive,
bool recursive);
+ // Copies or moves a single file.
+ virtual PlatformFileError CopyOrMoveFile(
+ FileSystemOperationContext* context,
+ const FilePath& src_file_path,
+ const FilePath& dest_file_path,
+ bool copy);
+
// TODO(dmikurube): Make this method non-virtual if it's possible.
// It conflicts with LocalFileSystemFileUtil for now.
//
@@ -175,22 +182,16 @@ class FileSystemFileUtil {
FileSystemOperationContext* unused,
const FilePath& file_path);
- // Copies or moves a single file.
- virtual PlatformFileError CopyOrMoveFile(
- FileSystemOperationContext* context,
- const FilePath& src_file_path,
- const FilePath& dest_file_path,
- bool copy);
-
- // Performs recursive copy by calling CopyOrMoveFile for individual files.
- // Operations for recursive traversal are encapsulated in this method.
+ // Performs recursive copy or move by calling CopyOrMoveFile for individual
+ // files. Operations for recursive traversal are encapsulated in this method.
// It assumes src_file_path and dest_file_path have passed
// PerformCommonCheckAndPreparationForMoveAndCopy().
// This method is non-virtual, not to be overridden.
- PlatformFileError CopyDirectory(
+ PlatformFileError CopyOrMoveDirectory(
FileSystemOperationContext* context,
const FilePath& src_file_path,
- const FilePath& dest_file_path);
+ const FilePath& dest_file_path,
+ bool copy);
// Returns a pointer to a new instance of AbstractFileEnumerator which is
// implemented for each FileUtil subclass. The instance needs to be freed
diff --git a/webkit/fileapi/file_system_operation.cc b/webkit/fileapi/file_system_operation.cc
index 046a16f..12ec587 100644
--- a/webkit/fileapi/file_system_operation.cc
+++ b/webkit/fileapi/file_system_operation.cc
@@ -16,6 +16,7 @@
#include "webkit/fileapi/file_system_util.h"
#include "webkit/fileapi/file_writer_delegate.h"
#include "webkit/fileapi/local_file_system_file_util.h"
+#include "webkit/fileapi/quota_file_util.h"
namespace fileapi {
@@ -34,6 +35,9 @@ FileSystemOperation::FileSystemOperation(
#ifndef NDEBUG
pending_operation_ = kOperationNone;
#endif
+ // TODO(dmikurube): Read and set available bytes from the Quota Manager.
+ file_system_operation_context_.set_allowed_bytes_growth(
+ QuotaFileUtil::kNoLimit);
}
FileSystemOperation::~FileSystemOperation() {
@@ -624,7 +628,8 @@ void FileSystemOperation::OnFileOpenedForWrite(
delete this;
return;
}
- file_writer_delegate_->Start(file.ReleaseValue(), blob_request_.get());
+ file_writer_delegate_->Start(file.ReleaseValue(), blob_request_.get(),
+ file_system_operation_context_.allowed_bytes_growth(), proxy_);
}
bool FileSystemOperation::VerifyFileSystemPathForRead(
diff --git a/webkit/fileapi/file_system_operation_context.h b/webkit/fileapi/file_system_operation_context.h
index 994739a..dedc30e0 100644
--- a/webkit/fileapi/file_system_operation_context.h
+++ b/webkit/fileapi/file_system_operation_context.h
@@ -61,6 +61,12 @@ class FileSystemOperationContext {
dest_type_ = dest_type;
}
+ void set_allowed_bytes_growth(const int64& allowed_bytes_growth) {
+ allowed_bytes_growth_ = allowed_bytes_growth;
+ }
+
+ int64 allowed_bytes_growth() const { return allowed_bytes_growth_; }
+
private:
// This file_system_file_util_ is not "owned" by FileSystemOperationContext.
// It is supposed to be a pointer to a singleton.
@@ -71,6 +77,7 @@ class FileSystemOperationContext {
GURL dest_origin_url_;
FileSystemType src_type_; // Also used for any single-path operation.
FileSystemType dest_type_;
+ int64 allowed_bytes_growth_;
};
} // namespace fileapi
diff --git a/webkit/fileapi/file_writer_delegate.cc b/webkit/fileapi/file_writer_delegate.cc
index c0574fca..a06c80e 100644
--- a/webkit/fileapi/file_writer_delegate.cc
+++ b/webkit/fileapi/file_writer_delegate.cc
@@ -4,45 +4,65 @@
#include "webkit/fileapi/file_writer_delegate.h"
+#include "base/file_util_proxy.h"
#include "base/message_loop.h"
#include "base/threading/thread_restrictions.h"
#include "net/base/net_errors.h"
#include "webkit/fileapi/file_system_operation.h"
+#include "webkit/fileapi/quota_file_util.h"
namespace fileapi {
static const int kReadBufSize = 32768;
FileWriterDelegate::FileWriterDelegate(
- FileSystemOperation* file_system_operation,
- int64 offset)
+ FileSystemOperation* file_system_operation, int64 offset)
: file_system_operation_(file_system_operation),
file_(base::kInvalidPlatformFileValue),
offset_(offset),
bytes_read_backlog_(0),
bytes_written_(0),
bytes_read_(0),
+ total_bytes_written_(0),
+ allowed_bytes_to_write_(0),
io_buffer_(new net::IOBufferWithSize(kReadBufSize)),
io_callback_(ALLOW_THIS_IN_INITIALIZER_LIST(this),
&FileWriterDelegate::OnDataWritten),
- method_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
+ method_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
+ callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
}
FileWriterDelegate::~FileWriterDelegate() {
}
-void FileWriterDelegate::Start(base::PlatformFile file,
- net::URLRequest* request) {
- file_ = file;
- request_ = request;
+void FileWriterDelegate::OnGetFileInfoForWrite(
+ base::PlatformFileError error,
+ const base::PlatformFileInfo& file_info) {
+ if (allowed_bytes_growth_ != QuotaFileUtil::kNoLimit)
+ allowed_bytes_to_write_ = file_info.size - offset_ + allowed_bytes_growth_;
+ else
+ allowed_bytes_to_write_ = QuotaFileUtil::kNoLimit;
file_stream_.reset(
new net::FileStream(
- file,
+ file_,
base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_WRITE |
base::PLATFORM_FILE_ASYNC));
request_->Start();
}
+void FileWriterDelegate::Start(base::PlatformFile file,
+ net::URLRequest* request,
+ int64 allowed_bytes_growth,
+ scoped_refptr<base::MessageLoopProxy> proxy) {
+ file_ = file;
+ request_ = request;
+ allowed_bytes_growth_ = allowed_bytes_growth;
+
+ base::FileUtilProxy::GetFileInfoFromPlatformFile(
+ proxy, file, callback_factory_.NewCallback(
+ &FileWriterDelegate::OnGetFileInfoForWrite));
+}
+
void FileWriterDelegate::OnReceivedRedirect(
net::URLRequest* request, const GURL& new_url, bool* defer_redirect) {
NOTREACHED();
@@ -120,8 +140,18 @@ void FileWriterDelegate::OnDataReceived(int bytes_read) {
}
void FileWriterDelegate::Write() {
+ DCHECK(total_bytes_written_ <= allowed_bytes_to_write_);
+ 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_;
+
int write_response = file_stream_->Write(io_buffer_->data() + bytes_written_,
- bytes_read_ - bytes_written_,
+ static_cast<int>(bytes_to_write),
&io_callback_);
if (write_response > 0)
MessageLoop::current()->PostTask(
@@ -136,6 +166,7 @@ void FileWriterDelegate::OnDataWritten(int write_response) {
if (write_response > 0) {
OnProgress(write_response, false);
bytes_written_ += write_response;
+ total_bytes_written_ += write_response;
if (bytes_written_ == bytes_read_)
Read();
else
@@ -146,6 +177,7 @@ void FileWriterDelegate::OnDataWritten(int write_response) {
}
void FileWriterDelegate::OnError(base::PlatformFileError error) {
+ request_->set_delegate(NULL);
request_->Cancel();
file_system_operation_->DidWrite(error, 0, true);
}
diff --git a/webkit/fileapi/file_writer_delegate.h b/webkit/fileapi/file_writer_delegate.h
index d2d306d..35fd21b 100644
--- a/webkit/fileapi/file_writer_delegate.h
+++ b/webkit/fileapi/file_writer_delegate.h
@@ -7,7 +7,9 @@
#include "base/file_path.h"
#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_callback_factory.h"
#include "base/memory/scoped_ptr.h"
+#include "base/message_loop_proxy.h"
#include "base/platform_file.h"
#include "base/task.h"
#include "base/time.h"
@@ -22,12 +24,13 @@ class FileSystemOperation;
class FileWriterDelegate : public net::URLRequest::Delegate {
public:
- FileWriterDelegate(
- FileSystemOperation* write_operation,
- int64 offset);
+ FileWriterDelegate(FileSystemOperation* write_operation, int64 offset);
virtual ~FileWriterDelegate();
- void Start(base::PlatformFile file, net::URLRequest* request);
+ void Start(base::PlatformFile file,
+ net::URLRequest* request,
+ int64 allowed_bytes_growth,
+ scoped_refptr<base::MessageLoopProxy> proxy);
base::PlatformFile file() {
return file_;
}
@@ -44,6 +47,9 @@ class FileWriterDelegate : public net::URLRequest::Delegate {
virtual void OnReadCompleted(net::URLRequest* request, int bytes_read);
private:
+ void OnGetFileInfoForWrite(
+ base::PlatformFileError error,
+ const base::PlatformFileInfo& file_info);
void Read();
void OnDataReceived(int bytes_read);
void Write();
@@ -53,16 +59,21 @@ class FileWriterDelegate : public net::URLRequest::Delegate {
FileSystemOperation* file_system_operation_;
base::PlatformFile file_;
+ base::PlatformFileInfo file_info_;
int64 offset_;
base::Time last_progress_event_time_;
int bytes_read_backlog_;
int bytes_written_;
int bytes_read_;
+ int64 total_bytes_written_;
+ int64 allowed_bytes_growth_;
+ int64 allowed_bytes_to_write_;
scoped_refptr<net::IOBufferWithSize> io_buffer_;
scoped_ptr<net::FileStream> file_stream_;
net::URLRequest* request_;
net::CompletionCallbackImpl<FileWriterDelegate> io_callback_;
ScopedRunnableMethodFactory<FileWriterDelegate> method_factory_;
+ base::ScopedCallbackFactory<FileWriterDelegate> callback_factory_;
};
} // namespace fileapi
diff --git a/webkit/fileapi/file_writer_delegate_unittest.cc b/webkit/fileapi/file_writer_delegate_unittest.cc
new file mode 100644
index 0000000..38dffda
--- /dev/null
+++ b/webkit/fileapi/file_writer_delegate_unittest.cc
@@ -0,0 +1,257 @@
+// Copyright (c) 2011 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.
+//
+// NOTE: These tests are run as part of "unit_tests" (in chrome/test/unit)
+// rather than as part of test_shell_tests because they rely on being able
+// to instantiate a MessageLoop of type TYPE_IO. test_shell_tests uses
+// TYPE_UI, which URLRequest doesn't allow.
+//
+
+#include <string>
+
+#include "base/file_util_proxy.h"
+#include "base/memory/scoped_temp_dir.h"
+#include "base/message_loop.h"
+#include "googleurl/src/gurl.h"
+#include "net/base/io_buffer.h"
+#include "net/url_request/url_request.h"
+#include "net/url_request/url_request_job.h"
+#include "net/url_request/url_request_status.h"
+#include "testing/platform_test.h"
+#include "webkit/fileapi/file_system_callback_dispatcher.h"
+#include "webkit/fileapi/file_system_operation.h"
+#include "webkit/fileapi/file_writer_delegate.h"
+#include "webkit/fileapi/quota_file_util.h"
+
+namespace fileapi {
+
+class FileWriterDelegateTest : public PlatformTest {
+ public:
+ FileWriterDelegateTest()
+ : loop_(MessageLoop::TYPE_IO),
+ status_(base::PLATFORM_FILE_OK),
+ bytes_written_(0),
+ complete_(false) {}
+
+ void set_failure_status(base::PlatformFileError status) {
+ EXPECT_FALSE(complete_);
+ EXPECT_EQ(status_, base::PLATFORM_FILE_OK);
+ EXPECT_NE(status, base::PLATFORM_FILE_OK);
+ complete_ = true;
+ status_ = status;
+ }
+ base::PlatformFileError status() const { return status_; }
+ void add_bytes_written(int64 bytes, bool complete) {
+ bytes_written_ += bytes;
+ EXPECT_FALSE(complete_);
+ complete_ = complete;
+ }
+ int64 bytes_written() const { return bytes_written_; }
+ bool complete() const { return complete_; }
+
+ protected:
+ virtual void SetUp();
+ virtual void TearDown();
+
+ static net::URLRequest::ProtocolFactory Factory;
+
+ scoped_ptr<FileWriterDelegate> file_writer_delegate_;
+ scoped_ptr<net::URLRequest> request_;
+
+ MessageLoop loop_;
+
+ ScopedTempDir dir_;
+ FilePath file_path_;
+ PlatformFile file_;
+
+ // For post-operation status.
+ base::PlatformFileError status_;
+ int64 bytes_written_;
+ bool complete_;
+};
+
+namespace {
+
+static std::string g_content;
+
+class FileWriterDelegateTestJob : public net::URLRequestJob {
+ public:
+ FileWriterDelegateTestJob(net::URLRequest* request,
+ const std::string& content)
+ : net::URLRequestJob(request),
+ content_(content),
+ remaining_bytes_(content.length()),
+ cursor_(0) {
+ }
+
+ void Start() {
+ MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &FileWriterDelegateTestJob::NotifyHeadersComplete));
+ }
+
+ bool ReadRawData(net::IOBuffer* buf, int buf_size, int *bytes_read) {
+ if (remaining_bytes_ < buf_size)
+ buf_size = static_cast<int>(remaining_bytes_);
+
+ for (int i = 0; i < buf_size; ++i)
+ buf->data()[i] = content_[cursor_++];
+ remaining_bytes_ -= buf_size;
+
+ SetStatus(net::URLRequestStatus());
+ *bytes_read = buf_size;
+ return true;
+ }
+
+private:
+ std::string content_;
+ int remaining_bytes_;
+ int cursor_;
+};
+
+class MockDispatcher : public FileSystemCallbackDispatcher {
+ public:
+ MockDispatcher(FileWriterDelegateTest* test) : test_(test) { }
+
+ virtual void DidFail(base::PlatformFileError status) {
+ test_->set_failure_status(status);
+ MessageLoop::current()->Quit();
+ }
+
+ virtual void DidSucceed() {
+ ADD_FAILURE();
+ }
+
+ virtual void DidReadMetadata(
+ const base::PlatformFileInfo& info,
+ const FilePath& platform_path) {
+ ADD_FAILURE();
+ }
+
+ virtual void DidReadDirectory(
+ const std::vector<base::FileUtilProxy::Entry>& entries,
+ bool /* has_more */) {
+ ADD_FAILURE();
+ }
+
+ virtual void DidOpenFileSystem(const std::string&, const GURL&) {
+ ADD_FAILURE();
+ }
+
+ virtual void DidWrite(int64 bytes, bool complete) {
+ test_->add_bytes_written(bytes, complete);
+ if (complete)
+ MessageLoop::current()->Quit();
+ }
+
+ private:
+ FileWriterDelegateTest* test_;
+};
+
+} // namespace (anonymous)
+
+// static
+net::URLRequestJob* FileWriterDelegateTest::Factory(
+ net::URLRequest* request,
+ const std::string& scheme) {
+ return new FileWriterDelegateTestJob(request, g_content);
+}
+
+void FileWriterDelegateTest::SetUp() {
+ ASSERT_TRUE(dir_.CreateUniqueTempDir());
+ ASSERT_TRUE(file_util::CreateTemporaryFileInDir(dir_.path(), &file_path_));
+
+ 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);
+
+ net::URLRequest::RegisterProtocolFactory("blob", &Factory);
+}
+
+void FileWriterDelegateTest::TearDown() {
+ net::URLRequest::RegisterProtocolFactory("blob", NULL);
+ base::ClosePlatformFile(file_);
+}
+
+TEST_F(FileWriterDelegateTest, WriteSuccessWithoutQuotaLimit) {
+ GURL blob_url("blob:nolimit");
+ g_content = std::string("The quick brown fox jumps over the lazy dog.\n");
+ file_writer_delegate_.reset(new FileWriterDelegate(
+ new FileSystemOperation(new MockDispatcher(this), NULL, NULL,
+ QuotaFileUtil::GetInstance()), 0));
+ request_.reset(new net::URLRequest(blob_url, file_writer_delegate_.get()));
+
+ file_writer_delegate_->Start(file_, request_.get(), QuotaFileUtil::kNoLimit,
+ base::MessageLoopProxy::CreateForCurrentThread());
+ MessageLoop::current()->Run();
+
+ file_writer_delegate_.reset(NULL);
+
+ EXPECT_EQ(45, bytes_written());
+ EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_TRUE(complete());
+}
+
+TEST_F(FileWriterDelegateTest, WriteSuccessWithJustQuota) {
+ GURL blob_url("blob:just");
+ g_content = std::string("The quick brown fox jumps over the lazy dog.\n");
+ file_writer_delegate_.reset(new FileWriterDelegate(
+ new FileSystemOperation(new MockDispatcher(this), NULL, NULL,
+ QuotaFileUtil::GetInstance()), 0));
+ request_.reset(new net::URLRequest(blob_url, file_writer_delegate_.get()));
+
+ file_writer_delegate_->Start(file_, request_.get(), 45,
+ base::MessageLoopProxy::CreateForCurrentThread());
+ MessageLoop::current()->Run();
+
+ file_writer_delegate_.reset(NULL);
+
+ EXPECT_EQ(45, bytes_written());
+ EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_TRUE(complete());
+}
+
+TEST_F(FileWriterDelegateTest, WriteFailureByQuota) {
+ GURL blob_url("blob:failure");
+ g_content = std::string("The quick brown fox jumps over the lazy dog.\n");
+ file_writer_delegate_.reset(new FileWriterDelegate(
+ new FileSystemOperation(new MockDispatcher(this), NULL, NULL,
+ QuotaFileUtil::GetInstance()), 0));
+ request_.reset(new net::URLRequest(blob_url, file_writer_delegate_.get()));
+
+ file_writer_delegate_->Start(file_, request_.get(), 44,
+ base::MessageLoopProxy::CreateForCurrentThread());
+ MessageLoop::current()->Run();
+
+ file_writer_delegate_.reset(NULL);
+
+ EXPECT_EQ(44, bytes_written());
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE, status());
+ EXPECT_TRUE(complete());
+}
+
+TEST_F(FileWriterDelegateTest, WriteZeroBytesSuccessfullyWithZeroQuota) {
+ GURL blob_url("blob:zero");
+ g_content = std::string("");
+ file_writer_delegate_.reset(new FileWriterDelegate(
+ new FileSystemOperation(new MockDispatcher(this), NULL, NULL,
+ QuotaFileUtil::GetInstance()), 0));
+ request_.reset(new net::URLRequest(blob_url, file_writer_delegate_.get()));
+
+ file_writer_delegate_->Start(file_, request_.get(), 0,
+ base::MessageLoopProxy::CreateForCurrentThread());
+ MessageLoop::current()->Run();
+
+ file_writer_delegate_.reset(NULL);
+
+ EXPECT_EQ(0, bytes_written());
+ EXPECT_EQ(base::PLATFORM_FILE_OK, status());
+ EXPECT_TRUE(complete());
+}
+
+} // namespace fileapi
diff --git a/webkit/fileapi/quota_file_util.cc b/webkit/fileapi/quota_file_util.cc
new file mode 100644
index 0000000..3a78e62
--- /dev/null
+++ b/webkit/fileapi/quota_file_util.cc
@@ -0,0 +1,82 @@
+// Copyright (c) 2011 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/quota_file_util.h"
+
+#include "base/file_util.h"
+#include "base/logging.h"
+
+namespace fileapi {
+
+const int64 QuotaFileUtil::kNoLimit = kint64max;
+
+namespace {
+
+// Checks if copying in the same filesystem can be performed.
+// This method is not called for moving within a single filesystem.
+static bool CanCopy(
+ const FilePath& src_file_path,
+ const FilePath& dest_file_path,
+ int64 allowed_bytes_growth,
+ int64* growth) {
+ base::PlatformFileInfo src_file_info;
+ if (!file_util::GetFileInfo(src_file_path, &src_file_info)) {
+ // Falling through to the actual copy/move operation.
+ return true;
+ }
+ base::PlatformFileInfo dest_file_info;
+ if (!file_util::GetFileInfo(dest_file_path, &dest_file_info))
+ dest_file_info.size = 0;
+ if (allowed_bytes_growth != QuotaFileUtil::kNoLimit &&
+ src_file_info.size - dest_file_info.size > allowed_bytes_growth)
+ return false;
+ if (growth != NULL)
+ *growth = src_file_info.size - dest_file_info.size;
+
+ return true;
+}
+
+} // namespace (anonymous)
+
+// static
+QuotaFileUtil* QuotaFileUtil::GetInstance() {
+ return Singleton<QuotaFileUtil>::get();
+}
+
+base::PlatformFileError QuotaFileUtil::CopyOrMoveFile(
+ FileSystemOperationContext* fs_context,
+ const FilePath& src_file_path,
+ const FilePath& dest_file_path,
+ bool copy) {
+ // It assumes copy/move operations are always in the same fs currently.
+ // TODO(dmikurube): Do quota check if moving between different fs.
+ if (copy) {
+ int64 allowed_bytes_growth = fs_context->allowed_bytes_growth();
+ // The third argument (growth) is not used for now.
+ if (!CanCopy(src_file_path, dest_file_path, allowed_bytes_growth, NULL))
+ return base::PLATFORM_FILE_ERROR_NO_SPACE;
+ }
+ return FileSystemFileUtil::GetInstance()->CopyOrMoveFile(fs_context,
+ src_file_path, dest_file_path, copy);
+}
+
+base::PlatformFileError QuotaFileUtil::Truncate(
+ FileSystemOperationContext* fs_context,
+ const FilePath& path,
+ int64 length) {
+ int64 allowed_bytes_growth = fs_context->allowed_bytes_growth();
+
+ if (allowed_bytes_growth != kNoLimit) {
+ base::PlatformFileInfo file_info;
+ if (!file_util::GetFileInfo(path, &file_info))
+ return base::PLATFORM_FILE_ERROR_FAILED;
+ if (length - file_info.size > allowed_bytes_growth)
+ return base::PLATFORM_FILE_ERROR_NO_SPACE;
+ }
+
+ return FileSystemFileUtil::GetInstance()->Truncate(
+ fs_context, path, length);
+}
+
+} // namespace fileapi
diff --git a/webkit/fileapi/quota_file_util.h b/webkit/fileapi/quota_file_util.h
new file mode 100644
index 0000000..e9ad6dd
--- /dev/null
+++ b/webkit/fileapi/quota_file_util.h
@@ -0,0 +1,43 @@
+// Copyright (c) 2011 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_QUOTA_FILE_UTIL_H_
+#define WEBKIT_FILEAPI_QUOTA_FILE_UTIL_H_
+
+#include "webkit/fileapi/file_system_file_util.h"
+#include "webkit/fileapi/file_system_operation_context.h"
+#pragma once
+
+namespace fileapi {
+
+class QuotaFileUtil : public FileSystemFileUtil {
+ public:
+ static QuotaFileUtil* GetInstance();
+ ~QuotaFileUtil() {}
+
+ static const int64 kNoLimit;
+
+ base::PlatformFileError CopyOrMoveFile(
+ FileSystemOperationContext* fs_context,
+ const FilePath& src_file_path,
+ const FilePath& dest_file_path,
+ bool copy);
+
+ // TODO(dmikurube): Charge some amount of quota for directories.
+
+ base::PlatformFileError Truncate(
+ FileSystemOperationContext* fs_context,
+ const FilePath& path,
+ int64 length);
+
+ friend struct DefaultSingletonTraits<QuotaFileUtil>;
+ DISALLOW_COPY_AND_ASSIGN(QuotaFileUtil);
+
+ protected:
+ QuotaFileUtil() {}
+};
+
+} // namespace fileapi
+
+#endif // WEBKIT_FILEAPI_QUOTA_FILE_UTIL_H_
diff --git a/webkit/fileapi/quota_file_util_unittest.cc b/webkit/fileapi/quota_file_util_unittest.cc
new file mode 100644
index 0000000..05fe840
--- /dev/null
+++ b/webkit/fileapi/quota_file_util_unittest.cc
@@ -0,0 +1,303 @@
+// Copyright (c) 2011 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/quota_file_util.h"
+
+#include "base/file_path.h"
+#include "base/memory/scoped_callback_factory.h"
+#include "base/memory/scoped_temp_dir.h"
+#include "base/platform_file.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/fileapi/file_system_file_util.h"
+#include "webkit/fileapi/file_system_types.h"
+#include "webkit/fileapi/file_system_operation_context.h"
+
+using namespace fileapi;
+
+class QuotaFileUtilTest : public testing::Test {
+ public:
+ QuotaFileUtilTest()
+ : callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
+ }
+
+ void SetUp() {
+ ASSERT_TRUE(data_dir_.CreateUniqueTempDir());
+ }
+
+ protected:
+ FileSystemOperationContext* Context() {
+ return new FileSystemOperationContext(NULL, QuotaFileUtil::GetInstance());
+ }
+
+ QuotaFileUtil* FileUtil() {
+ return QuotaFileUtil::GetInstance();
+ }
+
+ FilePath Path(const char *file_name) {
+ return data_dir_.path().AppendASCII(file_name);
+ }
+
+ base::PlatformFileError CreateFile(const char* file_name,
+ base::PlatformFile* file_handle, bool* created) {
+ int file_flags = base::PLATFORM_FILE_CREATE |
+ base::PLATFORM_FILE_WRITE | base::PLATFORM_FILE_ASYNC;
+
+ return FileUtil()->CreateOrOpen(Context(),
+ data_dir_.path().AppendASCII(file_name),
+ file_flags, file_handle, created);
+ }
+
+ base::PlatformFileError EnsureFileExists(const char* file_name,
+ bool* created) {
+ return FileUtil()->EnsureFileExists(Context(),
+ data_dir_.path().AppendASCII(file_name), created);
+ }
+
+ private:
+ ScopedTempDir data_dir_;
+ base::ScopedCallbackFactory<QuotaFileUtilTest> callback_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuotaFileUtilTest);
+};
+
+TEST_F(QuotaFileUtilTest, CreateAndClose) {
+ const char *file_name = "test_file";
+ base::PlatformFile file_handle;
+ bool created;
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ CreateFile(file_name, &file_handle, &created));
+ ASSERT_TRUE(created);
+
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ FileUtil()->Close(Context(), file_handle));
+}
+
+TEST_F(QuotaFileUtilTest, EnsureFileExists) {
+ const char *file_name = "foobar";
+ bool created;
+ ASSERT_EQ(base::PLATFORM_FILE_OK, EnsureFileExists(file_name, &created));
+ ASSERT_TRUE(created);
+
+ ASSERT_EQ(base::PLATFORM_FILE_OK, EnsureFileExists(file_name, &created));
+ EXPECT_FALSE(created);
+}
+
+TEST_F(QuotaFileUtilTest, Truncate) {
+ const char *file_name = "truncated";
+ bool created;
+ ASSERT_EQ(base::PLATFORM_FILE_OK, EnsureFileExists(file_name, &created));
+ ASSERT_TRUE(created);
+
+ FileSystemOperationContext *truncate_context;
+
+ truncate_context = Context();
+ truncate_context->set_allowed_bytes_growth(1020);
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ QuotaFileUtil::GetInstance()->Truncate(truncate_context,
+ Path(file_name),
+ 1020));
+
+ truncate_context = Context();
+ truncate_context->set_allowed_bytes_growth(0);
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ QuotaFileUtil::GetInstance()->Truncate(truncate_context,
+ Path(file_name),
+ 0));
+
+ truncate_context = Context();
+ truncate_context->set_allowed_bytes_growth(1020);
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE,
+ QuotaFileUtil::GetInstance()->Truncate(truncate_context,
+ Path(file_name),
+ 1021));
+}
+
+TEST_F(QuotaFileUtilTest, CopyFile) {
+ const char *from_file = "fromfile";
+ const char *obstacle_file = "obstaclefile";
+ const char *to_file1 = "tofile1";
+ const char *to_file2 = "tofile2";
+ bool created;
+ ASSERT_EQ(base::PLATFORM_FILE_OK, EnsureFileExists(from_file, &created));
+ ASSERT_TRUE(created);
+ ASSERT_EQ(base::PLATFORM_FILE_OK, EnsureFileExists(obstacle_file, &created));
+ ASSERT_TRUE(created);
+ FileSystemOperationContext *context;
+
+ context = Context();
+ context->set_allowed_bytes_growth(QuotaFileUtil::kNoLimit);
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ QuotaFileUtil::GetInstance()->Truncate(context,
+ Path(from_file),
+ 1020));
+
+ context = Context();
+ context->set_allowed_bytes_growth(QuotaFileUtil::kNoLimit);
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ QuotaFileUtil::GetInstance()->Truncate(context,
+ Path(obstacle_file),
+ 1));
+
+ context = Context();
+ context->set_allowed_bytes_growth(1020);
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ QuotaFileUtil::GetInstance()->Copy(context,
+ Path(from_file),
+ Path(to_file1)));
+
+ context = Context();
+ context->set_allowed_bytes_growth(1019);
+ ASSERT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE,
+ QuotaFileUtil::GetInstance()->Copy(context,
+ Path(from_file),
+ Path(to_file2)));
+
+ context = Context();
+ context->set_allowed_bytes_growth(1019);
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ QuotaFileUtil::GetInstance()->Copy(context,
+ Path(from_file),
+ Path(obstacle_file)));
+}
+
+TEST_F(QuotaFileUtilTest, CopyDirectory) {
+ const char *from_dir = "fromdir";
+ const char *from_file = "fromdir/fromfile";
+ const char *to_dir1 = "todir1";
+ const char *to_dir2 = "todir2";
+ bool created;
+ FileSystemOperationContext *context;
+
+ context = Context();
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ QuotaFileUtil::GetInstance()->CreateDirectory(context,
+ Path(from_dir),
+ false, false));
+ ASSERT_EQ(base::PLATFORM_FILE_OK, EnsureFileExists(from_file, &created));
+ ASSERT_TRUE(created);
+
+ context = Context();
+ context->set_allowed_bytes_growth(QuotaFileUtil::kNoLimit);
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ QuotaFileUtil::GetInstance()->Truncate(context,
+ Path(from_file),
+ 1020));
+
+ context = Context();
+ context->set_allowed_bytes_growth(1020);
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ QuotaFileUtil::GetInstance()->Copy(context,
+ Path(from_dir),
+ Path(to_dir1)));
+
+ context = Context();
+ context->set_allowed_bytes_growth(1019);
+ EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE,
+ QuotaFileUtil::GetInstance()->Copy(context,
+ Path(from_dir),
+ Path(to_dir2)));
+}
+
+TEST_F(QuotaFileUtilTest, MoveFile) {
+ const char *from_file = "fromfile";
+ const char *obstacle_file = "obstaclefile";
+ const char *to_file = "tofile";
+ bool created;
+ ASSERT_EQ(base::PLATFORM_FILE_OK, EnsureFileExists(from_file, &created));
+ ASSERT_TRUE(created);
+ FileSystemOperationContext *context;
+
+ context = Context();
+ context->set_allowed_bytes_growth(QuotaFileUtil::kNoLimit);
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ QuotaFileUtil::GetInstance()->Truncate(context,
+ Path(from_file),
+ 1020));
+
+ context = Context();
+ context->set_allowed_bytes_growth(0);
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ QuotaFileUtil::GetInstance()->Move(context,
+ Path(from_file),
+ Path(to_file)));
+
+ ASSERT_EQ(base::PLATFORM_FILE_OK, EnsureFileExists(from_file, &created));
+ ASSERT_TRUE(created);
+ ASSERT_EQ(base::PLATFORM_FILE_OK, EnsureFileExists(obstacle_file, &created));
+ ASSERT_TRUE(created);
+
+ context = Context();
+ context->set_allowed_bytes_growth(QuotaFileUtil::kNoLimit);
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ QuotaFileUtil::GetInstance()->Truncate(context,
+ Path(from_file),
+ 1020));
+
+ context = Context();
+ context->set_allowed_bytes_growth(QuotaFileUtil::kNoLimit);
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ QuotaFileUtil::GetInstance()->Truncate(context,
+ Path(obstacle_file),
+ 1));
+
+ context = Context();
+ context->set_allowed_bytes_growth(0);
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ QuotaFileUtil::GetInstance()->Move(context,
+ Path(from_file),
+ Path(obstacle_file)));
+}
+
+TEST_F(QuotaFileUtilTest, MoveDirectory) {
+ const char *from_dir = "fromdir";
+ const char *from_file = "fromdir/fromfile";
+ const char *to_dir1 = "todir1";
+ const char *to_dir2 = "todir2";
+ bool created;
+ FileSystemOperationContext *context;
+
+ context = Context();
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ QuotaFileUtil::GetInstance()->CreateDirectory(context,
+ Path(from_dir),
+ false, false));
+ ASSERT_EQ(base::PLATFORM_FILE_OK, EnsureFileExists(from_file, &created));
+ ASSERT_TRUE(created);
+
+ context = Context();
+ context->set_allowed_bytes_growth(QuotaFileUtil::kNoLimit);
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ QuotaFileUtil::GetInstance()->Truncate(context,
+ Path(from_file),
+ 1020));
+
+ context = Context();
+ context->set_allowed_bytes_growth(1020);
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ QuotaFileUtil::GetInstance()->Move(context,
+ Path(from_dir),
+ Path(to_dir1)));
+
+ context = Context();
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ QuotaFileUtil::GetInstance()->CreateDirectory(context,
+ Path(from_dir),
+ false, false));
+ ASSERT_EQ(base::PLATFORM_FILE_OK, EnsureFileExists(from_file, &created));
+ ASSERT_TRUE(created);
+
+ context = Context();
+ context->set_allowed_bytes_growth(QuotaFileUtil::kNoLimit);
+ ASSERT_EQ(base::PLATFORM_FILE_OK,
+ QuotaFileUtil::GetInstance()->Truncate(context,
+ Path(from_file),
+ 1020));
+
+ context = Context();
+ context->set_allowed_bytes_growth(1019);
+ EXPECT_EQ(base::PLATFORM_FILE_OK,
+ QuotaFileUtil::GetInstance()->Move(context,
+ Path(from_dir),
+ Path(to_dir2)));
+}
diff --git a/webkit/fileapi/webkit_fileapi.gypi b/webkit/fileapi/webkit_fileapi.gypi
index 70de6fc..258db99 100644
--- a/webkit/fileapi/webkit_fileapi.gypi
+++ b/webkit/fileapi/webkit_fileapi.gypi
@@ -48,6 +48,8 @@
'local_file_system_file_util.h',
'sandbox_mount_point_provider.cc',
'sandbox_mount_point_provider.h',
+ 'quota_file_util.cc',
+ 'quota_file_util.h',
'webfilewriter_base.cc',
'webfilewriter_base.h',
],
diff --git a/webkit/tools/test_shell/test_shell.gypi b/webkit/tools/test_shell/test_shell.gypi
index 8ce59cc..d843493 100644
--- a/webkit/tools/test_shell/test_shell.gypi
+++ b/webkit/tools/test_shell/test_shell.gypi
@@ -382,6 +382,7 @@
'../../fileapi/file_system_usage_tracker_unittest.cc',
'../../fileapi/file_system_util_unittest.cc',
'../../fileapi/sandbox_mount_point_provider_unittest.cc',
+ '../../fileapi/quota_file_util_unittest.cc',
'../../fileapi/webfilewriter_base_unittest.cc',
'../../glue/bookmarklet_unittest.cc',
'../../glue/context_menu_unittest.cc',