diff options
Diffstat (limited to 'chromium/content/browser/native_io')
3 files changed, 157 insertions, 14 deletions
diff --git a/chromium/content/browser/native_io/native_io_context_unittest.cc b/chromium/content/browser/native_io/native_io_context_unittest.cc index f362d35e465..cd6d262df9d 100644 --- a/chromium/content/browser/native_io/native_io_context_unittest.cc +++ b/chromium/content/browser/native_io/native_io_context_unittest.cc @@ -77,6 +77,18 @@ class NativeIOHostSync { return names; } + bool RenameFile(const std::string& old_name, const std::string& new_name) { + bool success = false; + base::RunLoop run_loop; + io_host_->RenameFile(old_name, new_name, + base::BindLambdaForTesting([&](bool backend_success) { + success = backend_success; + run_loop.Quit(); + })); + run_loop.Run(); + return success; + } + private: blink::mojom::NativeIOHost* const io_host_; }; @@ -142,17 +154,18 @@ class NativeIOContextTest : public testing::Test { mojo::Remote<blink::mojom::NativeIOHost> google_host_remote_; std::unique_ptr<NativeIOHostSync> example_host_; std::unique_ptr<NativeIOHostSync> google_host_; -}; -TEST_F(NativeIOContextTest, OpenFile_BadNames) { - std::vector<std::string> bad_names = { + // Names disallowed by NativeIO + const std::vector<std::string> bad_names_ = { "Uppercase", "has-dash", "has.dot", "has/slash", }; +}; - for (const std::string& bad_name : bad_names) { +TEST_F(NativeIOContextTest, OpenFile_BadNames) { + for (const std::string& bad_name : bad_names_) { mojo::test::BadMessageObserver bad_message_observer; mojo::Remote<blink::mojom::NativeIOFileHost> file_host; @@ -199,14 +212,7 @@ TEST_F(NativeIOContextTest, OpenFile_SameName) { } TEST_F(NativeIOContextTest, DeleteFile_BadNames) { - std::vector<std::string> bad_names = { - "Uppercase", - "has-dash", - "has.dot", - "has/slash", - }; - - for (const std::string& bad_name : bad_names) { + for (const std::string& bad_name : bad_names_) { mojo::test::BadMessageObserver bad_message_observer; EXPECT_FALSE(example_host_->DeleteFile(bad_name)); @@ -223,6 +229,30 @@ TEST_F(NativeIOContextTest, OpenFile_Locks_DeleteFile) { EXPECT_FALSE(example_host_->DeleteFile("test_file")); } +TEST_F(NativeIOContextTest, OpenFile_Locks_RenameFile) { + mojo::Remote<blink::mojom::NativeIOFileHost> file_host; + base::File file = example_host_->OpenFile( + "test_file_in_use", file_host.BindNewPipeAndPassReceiver()); + EXPECT_TRUE(file.IsValid()); + + mojo::Remote<blink::mojom::NativeIOFileHost> file_host2; + base::File file2 = example_host_->OpenFile( + "test_file_closed", file_host2.BindNewPipeAndPassReceiver()); + EXPECT_TRUE(file2.IsValid()); + file2.Close(); + NativeIOFileHostSync file_host2_sync(file_host2.get()); + file_host2_sync.Close(); + + EXPECT_FALSE( + example_host_->RenameFile("test_file_in_use", "renamed_test_file")) + << "An open file cannot be renamed"; + + EXPECT_FALSE( + example_host_->RenameFile("test_file_closed", "test_file_in_use")) + << "An open file cannot be overwritten"; + ; +} + TEST_F(NativeIOContextTest, DeleteFile_WipesData) { const std::string kTestData("Test Data"); @@ -264,6 +294,39 @@ TEST_F(NativeIOContextTest, GetAllFiles_AfterOpen) { EXPECT_EQ("test_file", file_names[0]); } +TEST_F(NativeIOContextTest, RenameFile_AfterOpenAndRename) { + mojo::Remote<blink::mojom::NativeIOFileHost> file_host_remote; + base::File file = example_host_->OpenFile( + "test_file", file_host_remote.BindNewPipeAndPassReceiver()); + file.Close(); + NativeIOFileHostSync file_host(file_host_remote.get()); + file_host.Close(); + + example_host_->RenameFile("test_file", "renamed_test_file"); + std::vector<std::string> file_names = example_host_->GetAllFileNames(); + EXPECT_EQ(1u, file_names.size()); + EXPECT_EQ("renamed_test_file", file_names[0]); +} + +TEST_F(NativeIOContextTest, RenameFile_BadNames) { + mojo::Remote<blink::mojom::NativeIOFileHost> file_host_remote; + base::File file = example_host_->OpenFile( + "test_file", file_host_remote.BindNewPipeAndPassReceiver()); + file.Close(); + NativeIOFileHostSync file_host(file_host_remote.get()); + file_host.Close(); + + for (const std::string& bad_name : bad_names_) { + mojo::test::BadMessageObserver bad_message_observer; + + EXPECT_FALSE(example_host_->RenameFile("test_file", bad_name)); + EXPECT_EQ("Invalid file name", bad_message_observer.WaitForBadMessage()); + + EXPECT_FALSE(example_host_->RenameFile(bad_name, "inexistant_test_file")); + EXPECT_EQ("Invalid file name", bad_message_observer.WaitForBadMessage()); + } +} + TEST_F(NativeIOContextTest, OriginIsolation) { const std::string kTestData("Test Data"); diff --git a/chromium/content/browser/native_io/native_io_host.cc b/chromium/content/browser/native_io/native_io_host.cc index 2df37b9d470..d152638ee90 100644 --- a/chromium/content/browser/native_io/native_io_host.cc +++ b/chromium/content/browser/native_io/native_io_host.cc @@ -56,10 +56,9 @@ base::FilePath GetNativeIOFilePath(const base::FilePath& root_path, scoped_refptr<base::TaskRunner> CreateFileTaskRunner() { // We use a SequencedTaskRunner so that there is a global ordering to an // origin's directory operations. - return base::CreateSequencedTaskRunner({ + return base::ThreadPool::CreateSequencedTaskRunner({ // Needed for file I/O. base::MayBlock(), - base::ThreadPool(), // Reasonable compromise, given that a few database operations are // blocking, while most operations are not. We should be able to do better @@ -156,6 +155,27 @@ void DidGetAllFileNames( std::move(result.second)); } +// Performs the file I/O work in RenameFile(). +bool DoRenameFile(const base::FilePath& root_path, + const std::string& old_name, + const std::string& new_name) { + DCHECK(IsValidNativeIOName(old_name)); + DCHECK(IsValidNativeIOName(new_name)); + + // If the origin's directory wasn't created yet, there's nothing to rename. + if (!base::PathExists(root_path)) + return false; + + // Do not overwrite an existing file. + if (base::PathExists(GetNativeIOFilePath(root_path, new_name))) + return false; + + // TODO(rstz): Report error. + base::File::Error error; + return base::ReplaceFile(GetNativeIOFilePath(root_path, old_name), + GetNativeIOFilePath(root_path, new_name), &error); +} + } // namespace NativeIOHost::NativeIOHost(NativeIOContext* context, @@ -256,6 +276,42 @@ void NativeIOHost::GetAllFileNames(GetAllFileNamesCallback callback) { base::BindOnce(&DidGetAllFileNames, std::move(callback))); } +void NativeIOHost::RenameFile(const std::string& old_name, + const std::string& new_name, + RenameFileCallback callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + if (!IsValidNativeIOName(old_name) || !IsValidNativeIOName(new_name)) { + mojo::ReportBadMessage("Invalid file name"); + std::move(callback).Run(false); + return; + } + + if (open_file_hosts_.find(old_name) != open_file_hosts_.end() || + open_file_hosts_.find(new_name) != open_file_hosts_.end()) { + // TODO(rstz): Report that the file is locked. + std::move(callback).Run(false); + return; + } + + auto old_iterator_and_success = io_pending_files_.insert(old_name); + if (!old_iterator_and_success.second) { + std::move(callback).Run(false); + return; + } + auto new_iterator_and_success = io_pending_files_.insert(new_name); + if (!new_iterator_and_success.second) { + io_pending_files_.erase(old_iterator_and_success.first); + std::move(callback).Run(false); + return; + } + + file_task_runner_->PostTaskAndReplyWithResult( + FROM_HERE, base::BindOnce(&DoRenameFile, root_path_, old_name, new_name), + base::BindOnce(&NativeIOHost::DidRenameFile, weak_factory_.GetWeakPtr(), + old_name, new_name, std::move(callback))); +} + void NativeIOHost::OnFileClose(NativeIOFileHost* file_host) { DCHECK(open_file_hosts_.count(file_host->file_name()) > 0); DCHECK_EQ(open_file_hosts_[file_host->file_name()].get(), file_host); @@ -302,4 +358,19 @@ void NativeIOHost::DidDeleteFile(const std::string& name, return; } +void NativeIOHost::DidRenameFile(const std::string& old_name, + const std::string& new_name, + RenameFileCallback callback, + bool success) { + DCHECK(io_pending_files_.count(old_name)); + DCHECK(!open_file_hosts_.count(old_name)); + DCHECK(io_pending_files_.count(new_name)); + DCHECK(!open_file_hosts_.count(new_name)); + io_pending_files_.erase(old_name); + io_pending_files_.erase(new_name); + + std::move(callback).Run(success); + return; +} + } // namespace content diff --git a/chromium/content/browser/native_io/native_io_host.h b/chromium/content/browser/native_io/native_io_host.h index bf2a50308f3..56bcd6d2f43 100644 --- a/chromium/content/browser/native_io/native_io_host.h +++ b/chromium/content/browser/native_io/native_io_host.h @@ -67,6 +67,9 @@ class NativeIOHost : public blink::mojom::NativeIOHost { void DeleteFile(const std::string& name, DeleteFileCallback callback) override; void GetAllFileNames(GetAllFileNamesCallback callback) override; + void RenameFile(const std::string& old_name, + const std::string& new_name, + RenameFileCallback callback) override; // Called when one of the open files for this origin closes. // @@ -90,6 +93,12 @@ class NativeIOHost : public blink::mojom::NativeIOHost { DeleteFileCallback callback, bool success); + // Called after the file I/O part of RenameFile() completed. + void DidRenameFile(const std::string& old_name, + const std::string& new_name, + RenameFileCallback callback, + bool success); + // The directory holding all the files for this origin. const base::FilePath root_path_; |