diff options
author | sbc@chromium.org <sbc@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-11-08 18:45:09 +0000 |
---|---|---|
committer | sbc@chromium.org <sbc@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-11-08 18:45:09 +0000 |
commit | e9177515127e6f37247b3875248d0589d42209c9 (patch) | |
tree | 946bd7862017f8b08ba68ab0ac12521534f19770 /native_client_sdk | |
parent | a5e38520049d0335802646ddb9840ea6b0e9c91d (diff) | |
download | chromium_src-e9177515127e6f37247b3875248d0589d42209c9.zip chromium_src-e9177515127e6f37247b3875248d0589d42209c9.tar.gz chromium_src-e9177515127e6f37247b3875248d0589d42209c9.tar.bz2 |
[NaCl SDK] add rename(2) support to nacl_io.
Currently only supported by memfs.
R=binji@chromium.org
Review URL: https://codereview.chromium.org/64823004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@233947 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'native_client_sdk')
19 files changed, 240 insertions, 15 deletions
diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_proxy.cc b/native_client_sdk/src/libraries/nacl_io/kernel_proxy.cc index 1f15a9b..1d26e0b 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_proxy.cc +++ b/native_client_sdk/src/libraries/nacl_io/kernel_proxy.cc @@ -612,8 +612,39 @@ int KernelProxy::lstat(const char* path, struct stat* buf) { } int KernelProxy::rename(const char* path, const char* newpath) { - errno = ENOSYS; - return -1; + ScopedMount mnt; + Path rel; + Error error = AcquireMountAndRelPath(path, &mnt, &rel); + if (error) { + errno = error; + return -1; + } + + ScopedMount newmnt; + Path newrel; + error = AcquireMountAndRelPath(newpath, &newmnt, &newrel); + if (error) { + errno = error; + return -1; + } + + if (newmnt.get() != mnt.get()) { + // Renaming accross mountpoints is not allowed + errno = EXDEV; + return -1; + } + + // They already point to the same path + if (rel == newrel) + return 0; + + error = mnt->Rename(rel, newrel); + if (error) { + errno = error; + return -1; + } + + return 0; } int KernelProxy::remove(const char* path) { diff --git a/native_client_sdk/src/libraries/nacl_io/mount.h b/native_client_sdk/src/libraries/nacl_io/mount.h index 65a6e7a..b13fc08 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount.h +++ b/native_client_sdk/src/libraries/nacl_io/mount.h @@ -68,6 +68,7 @@ class Mount : public sdk_util::RefObject { virtual Error Mkdir(const Path& path, int permissions) = 0; virtual Error Rmdir(const Path& path) = 0; virtual Error Remove(const Path& path) = 0; + virtual Error Rename(const Path& path, const Path& newpath) = 0; // Assumes that |node| is non-NULL. void OnNodeCreated(MountNode* node); diff --git a/native_client_sdk/src/libraries/nacl_io/mount_dev.cc b/native_client_sdk/src/libraries/nacl_io/mount_dev.cc index fdc6379..14acb6d 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_dev.cc +++ b/native_client_sdk/src/libraries/nacl_io/mount_dev.cc @@ -288,13 +288,15 @@ Error MountDev::Open(const Path& path, return root_->FindChild(path.Join(), out_node); } -Error MountDev::Unlink(const Path& path) { return EINVAL; } +Error MountDev::Unlink(const Path& path) { return EPERM; } -Error MountDev::Mkdir(const Path& path, int permissions) { return EINVAL; } +Error MountDev::Mkdir(const Path& path, int permissions) { return EPERM; } -Error MountDev::Rmdir(const Path& path) { return EINVAL; } +Error MountDev::Rmdir(const Path& path) { return EPERM; } -Error MountDev::Remove(const Path& path) { return EINVAL; } +Error MountDev::Remove(const Path& path) { return EPERM; } + +Error MountDev::Rename(const Path& path, const Path& newpath) { return EPERM; } MountDev::MountDev() {} diff --git a/native_client_sdk/src/libraries/nacl_io/mount_dev.h b/native_client_sdk/src/libraries/nacl_io/mount_dev.h index 5b253cd..640158b 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_dev.h +++ b/native_client_sdk/src/libraries/nacl_io/mount_dev.h @@ -22,6 +22,7 @@ class MountDev : public Mount { virtual Error Mkdir(const Path& path, int permissions); virtual Error Rmdir(const Path& path); virtual Error Remove(const Path& path); + virtual Error Rename(const Path& path, const Path& newpath); protected: MountDev(); diff --git a/native_client_sdk/src/libraries/nacl_io/mount_html5fs.cc b/native_client_sdk/src/libraries/nacl_io/mount_html5fs.cc index da8b4bb..3c9695f 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_html5fs.cc +++ b/native_client_sdk/src/libraries/nacl_io/mount_html5fs.cc @@ -103,6 +103,21 @@ Error MountHtml5Fs::Remove(const Path& path) { return 0; } +Error MountHtml5Fs::Rename(const Path& path, const Path& newpath) { + Error error = BlockUntilFilesystemOpen(); + if (error) + return error; + + ScopedResource fileref_resource( + ppapi(), + ppapi()->GetFileRefInterface()->Create(filesystem_resource_, + path.Join().c_str())); + if (!fileref_resource.pp_resource()) + return ENOENT; + + return EACCES; +} + MountHtml5Fs::MountHtml5Fs() : filesystem_resource_(0), filesystem_open_has_result_(false), diff --git a/native_client_sdk/src/libraries/nacl_io/mount_html5fs.h b/native_client_sdk/src/libraries/nacl_io/mount_html5fs.h index 8efa112..0408c79 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_html5fs.h +++ b/native_client_sdk/src/libraries/nacl_io/mount_html5fs.h @@ -24,6 +24,7 @@ class MountHtml5Fs : public Mount { virtual Error Mkdir(const Path& path, int permissions); virtual Error Rmdir(const Path& path); virtual Error Remove(const Path& path); + virtual Error Rename(const Path& path, const Path& newpath); PP_Resource filesystem_resource() { return filesystem_resource_; } diff --git a/native_client_sdk/src/libraries/nacl_io/mount_http.cc b/native_client_sdk/src/libraries/nacl_io/mount_http.cc index 2d0eaa0..dcc1332 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_http.cc +++ b/native_client_sdk/src/libraries/nacl_io/mount_http.cc @@ -138,6 +138,14 @@ Error MountHttp::Remove(const Path& path) { return EACCES; } +Error MountHttp::Rename(const Path& path, const Path& newpath) { + NodeMap_t::iterator iter = node_cache_.find(path.Join()); + if (iter == node_cache_.end()) + return ENOENT; + + return EACCES; +} + PP_Resource MountHttp::MakeUrlRequestInfo(const std::string& url, const char* method, StringMap_t* additional_headers) { diff --git a/native_client_sdk/src/libraries/nacl_io/mount_http.h b/native_client_sdk/src/libraries/nacl_io/mount_http.h index adec0e3..21d688e 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_http.h +++ b/native_client_sdk/src/libraries/nacl_io/mount_http.h @@ -29,6 +29,7 @@ class MountHttp : public Mount { virtual Error Mkdir(const Path& path, int permissions); virtual Error Rmdir(const Path& path); virtual Error Remove(const Path& path); + virtual Error Rename(const Path& path, const Path& newpath); PP_Resource MakeUrlRequestInfo(const std::string& url, const char* method, diff --git a/native_client_sdk/src/libraries/nacl_io/mount_mem.cc b/native_client_sdk/src/libraries/nacl_io/mount_mem.cc index b7f2e77..dd4a402 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_mem.cc +++ b/native_client_sdk/src/libraries/nacl_io/mount_mem.cc @@ -184,6 +184,75 @@ Error MountMem::Remove(const Path& path) { return RemoveInternal(path, REMOVE_ALL); } +Error MountMem::Rename(const Path& src_path, const Path& target_path) { + ScopedMountNode src_node; + ScopedMountNode src_parent; + ScopedMountNode target_node; + ScopedMountNode target_parent; + int error = FindNode(src_path, 0, &src_node); + if (error) + return error; + + // The source must exist + error = FindNode(src_path.Parent(), S_IFDIR, &src_parent); + if (error) + return error; + + // The parent of the target must exist + error = FindNode(target_path.Parent(), 0, &target_parent); + if (error) + return error; + + std::string target_name = target_path.Basename(); + + // The target itself need not exist but if it does there are + // certain restrictions + error = FindNode(target_path, 0, &target_node); + bool replacing_target = error == 0; + if (replacing_target) { + if (target_node->IsaDir()) { + // If the target is a direcotry it must be empty + if (target_node->ChildCount()) { + return ENOTEMPTY; + } + + if (src_node->IsaDir()) { + // Replacing an existing directory. + RemoveInternal(target_path, REMOVE_ALL); + } else { + // Renaming into an existing directory. + target_name = src_path.Basename(); + target_parent = target_node; + } + } else { + if (src_node->IsaDir()) + // Can't replace a file with a direcotory + return EISDIR; + + // Replacing an existing file. + target_parent->RemoveChild(target_path.Basename()); + } + } + + // Perform that actual rename. Simply re-parent the original source node + // onto its new parent node. + error = src_parent->RemoveChild(src_path.Basename()); + if (error) + return error; + + error = target_parent->AddChild(target_name, src_node); + if (error) { + // Re-parent the old target_node if we failed to add the new one. + if (replacing_target) + target_parent->AddChild(target_path.Basename(), target_node); + // Re-parent the src_node + target_parent->AddChild(target_path.Basename(), src_node); + return error; + } + + return 0; +} + Error MountMem::RemoveInternal(const Path& path, int remove_type) { bool dir_only = remove_type == REMOVE_DIR; bool file_only = remove_type == REMOVE_FILE; @@ -223,4 +292,3 @@ Error MountMem::RemoveInternal(const Path& path, int remove_type) { } } // namespace nacl_io - diff --git a/native_client_sdk/src/libraries/nacl_io/mount_mem.h b/native_client_sdk/src/libraries/nacl_io/mount_mem.h index 04ee665..4966975 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_mem.h +++ b/native_client_sdk/src/libraries/nacl_io/mount_mem.h @@ -34,6 +34,7 @@ class MountMem : public Mount { virtual Error Mkdir(const Path& path, int perm); virtual Error Rmdir(const Path& path); virtual Error Remove(const Path& path); + virtual Error Rename(const Path& path, const Path& newpath); private: static const int REMOVE_DIR = 1; diff --git a/native_client_sdk/src/libraries/nacl_io/mount_passthrough.cc b/native_client_sdk/src/libraries/nacl_io/mount_passthrough.cc index f4c9951..33e6675 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_passthrough.cc +++ b/native_client_sdk/src/libraries/nacl_io/mount_passthrough.cc @@ -167,5 +167,10 @@ Error MountPassthrough::Remove(const Path& path) { return ENOSYS; } +Error MountPassthrough::Rename(const Path& path, const Path& newpath) { + // Not implemented by NaCl. + return ENOSYS; +} + } // namespace nacl_io diff --git a/native_client_sdk/src/libraries/nacl_io/mount_passthrough.h b/native_client_sdk/src/libraries/nacl_io/mount_passthrough.h index de4f81c..7b9aa61 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_passthrough.h +++ b/native_client_sdk/src/libraries/nacl_io/mount_passthrough.h @@ -25,6 +25,7 @@ class MountPassthrough : public Mount { virtual Error Mkdir(const Path& path, int perm); virtual Error Rmdir(const Path& path); virtual Error Remove(const Path& path); + virtual Error Rename(const Path& path, const Path& newpath); private: friend class TypedMountFactory<MountPassthrough>; diff --git a/native_client_sdk/src/libraries/nacl_io/mount_stream.cc b/native_client_sdk/src/libraries/nacl_io/mount_stream.cc index e38c8c9..e7b23ef 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_stream.cc +++ b/native_client_sdk/src/libraries/nacl_io/mount_stream.cc @@ -86,13 +86,21 @@ MountStream::~MountStream() { } Error MountStream::Access(const Path& path, int a_mode) { return EACCES; } + Error MountStream::Open(const Path& path, int o_flags, ScopedMountNode* out_node) { return EACCES; } Error MountStream::Unlink(const Path& path) { return EACCES; } + Error MountStream::Mkdir(const Path& path, int permissions) { return EACCES; } + Error MountStream::Rmdir(const Path& path) { return EACCES; } + Error MountStream::Remove(const Path& path) { return EACCES; } +Error MountStream::Rename(const Path& path, const Path& newpath) { + return EACCES; +} + } // namespace nacl_io diff --git a/native_client_sdk/src/libraries/nacl_io/mount_stream.h b/native_client_sdk/src/libraries/nacl_io/mount_stream.h index 757e132..e03abe7 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_stream.h +++ b/native_client_sdk/src/libraries/nacl_io/mount_stream.h @@ -63,6 +63,7 @@ class MountStream : public Mount { virtual Error Mkdir(const Path& path, int permissions); virtual Error Rmdir(const Path& path); virtual Error Remove(const Path& path); + virtual Error Rename(const Path& path, const Path& newpath); static void* StreamThreadThunk(void*); void StreamThread(); diff --git a/native_client_sdk/src/libraries/nacl_io/path.h b/native_client_sdk/src/libraries/nacl_io/path.h index 9131d6a..4b39daf 100644 --- a/native_client_sdk/src/libraries/nacl_io/path.h +++ b/native_client_sdk/src/libraries/nacl_io/path.h @@ -25,7 +25,6 @@ class Path { explicit Path(const std::string& path); ~Path(); - // Return true of the first path item is '/'. bool IsAbsolute() const; @@ -60,6 +59,8 @@ class Path { // Operator versions Path& operator=(const Path& p); Path& operator=(const std::string& str); + bool operator==(const Path& other) { return Split() == other.Split(); } + bool operator!=(const Path& other) { return !operator==(other); } private: // Internal representation of the path stored an array of string representing diff --git a/native_client_sdk/src/tests/nacl_io_test/kernel_object_test.cc b/native_client_sdk/src/tests/nacl_io_test/kernel_object_test.cc index d86bd93..67e70f4 100644 --- a/native_client_sdk/src/tests/nacl_io_test/kernel_object_test.cc +++ b/native_client_sdk/src/tests/nacl_io_test/kernel_object_test.cc @@ -40,6 +40,7 @@ class MountRefMock : public Mount { Error Mkdir(const Path& path, int permissions) { return 0; } Error Rmdir(const Path& path) { return 0; } Error Remove(const Path& path) { return 0; } + Error Rename(const Path& path, const Path& newpath) { return 0; } }; class KernelHandleRefMock : public KernelHandle { diff --git a/native_client_sdk/src/tests/nacl_io_test/kernel_proxy_test.cc b/native_client_sdk/src/tests/nacl_io_test/kernel_proxy_test.cc index 5c0a772..6d9af12 100644 --- a/native_client_sdk/src/tests/nacl_io_test/kernel_proxy_test.cc +++ b/native_client_sdk/src/tests/nacl_io_test/kernel_proxy_test.cc @@ -127,6 +127,21 @@ TEST_F(KernelProxyTest, FileLeak) { ASSERT_EQ(0, root->ChildCount()); } +TEST_F(KernelProxyTest, Rename) { + // Create a dummy file + int file1 = ki_open("/test1.txt", O_RDWR | O_CREAT); + ASSERT_GT(file1, -1); + ASSERT_EQ(0, ki_close(file1)); + + // Test the renaming works + ASSERT_EQ(0, ki_rename("/test1.txt", "/test2.txt")); + + // Test that renaming across mount points fails + ASSERT_EQ(0, ki_mount("", "/foo", "memfs", 0, "")); + ASSERT_EQ(-1, ki_rename("/test2.txt", "/foo/test2.txt")); + ASSERT_EQ(EXDEV, errno); +} + TEST_F(KernelProxyTest, WorkingDirectory) { char text[1024]; @@ -407,6 +422,7 @@ class MountMockMMap : public Mount { virtual Error Mkdir(const Path& path, int permissions) { return ENOSYS; } virtual Error Rmdir(const Path& path) { return ENOSYS; } virtual Error Remove(const Path& path) { return ENOSYS; } + virtual Error Rename(const Path& path, const Path& newpath) { return ENOSYS; } friend class TypedMountFactory<MountMockMMap>; }; @@ -565,4 +581,3 @@ TEST_F(KernelProxyErrorTest, ReadError) { // propagate through. EXPECT_EQ(1234, errno); } - diff --git a/native_client_sdk/src/tests/nacl_io_test/mount_mock.h b/native_client_sdk/src/tests/nacl_io_test/mount_mock.h index 10d1cf2..b0e5700 100644 --- a/native_client_sdk/src/tests/nacl_io_test/mount_mock.h +++ b/native_client_sdk/src/tests/nacl_io_test/mount_mock.h @@ -29,6 +29,7 @@ class MountMock : public nacl_io::Mount { MOCK_METHOD2(Mkdir, Error(const Path&, int)); MOCK_METHOD1(Rmdir, Error(const Path&)); MOCK_METHOD1(Remove, Error(const Path&)); + MOCK_METHOD2(Rename, Error(const Path&, const Path&)); }; #endif // LIBRARIES_NACL_IO_TEST_MOUNT_MOCK_H_ diff --git a/native_client_sdk/src/tests/nacl_io_test/mount_test.cc b/native_client_sdk/src/tests/nacl_io_test/mount_test.cc index 3f43ac9..ed6d674 100644 --- a/native_client_sdk/src/tests/nacl_io_test/mount_test.cc +++ b/native_client_sdk/src/tests/nacl_io_test/mount_test.cc @@ -188,8 +188,8 @@ TEST(MountTest, MemMountRemove) { ScopedMountNode file; ScopedMountNode result_node; - EXPECT_EQ(0, mnt.Mkdir(Path("/dir"), O_RDWR)); - EXPECT_EQ(0, mnt.Open(Path("/file"), O_RDWR | O_CREAT | O_EXCL, &file)); + ASSERT_EQ(0, mnt.Mkdir(Path("/dir"), O_RDWR)); + ASSERT_EQ(0, mnt.Open(Path("/file"), O_RDWR | O_CREAT | O_EXCL, &file)); EXPECT_NE(NULL_NODE, file.get()); EXPECT_EQ(3, mnt.num_nodes()); file.reset(); @@ -199,11 +199,74 @@ TEST(MountTest, MemMountRemove) { EXPECT_EQ(0, mnt.Remove(Path("/file"))); EXPECT_EQ(1, mnt.num_nodes()); - EXPECT_EQ(ENOENT, + ASSERT_EQ(ENOENT, mnt.Open(Path("/dir/foo"), O_CREAT | O_RDWR, &result_node)); - EXPECT_EQ(NULL_NODE, result_node.get()); - EXPECT_EQ(ENOENT, mnt.Open(Path("/file"), O_RDONLY, &result_node)); - EXPECT_EQ(NULL_NODE, result_node.get()); + ASSERT_EQ(NULL_NODE, result_node.get()); + ASSERT_EQ(ENOENT, mnt.Open(Path("/file"), O_RDONLY, &result_node)); + ASSERT_EQ(NULL_NODE, result_node.get()); +} + +TEST(MountTest, MemMountRename) { + MountMemMock mnt; + ASSERT_EQ(0, mnt.Mkdir(Path("/dir1"), O_RDWR)); + ASSERT_EQ(0, mnt.Mkdir(Path("/dir2"), O_RDWR)); + ASSERT_EQ(3, mnt.num_nodes()); + + ScopedMountNode file; + ASSERT_EQ(0, mnt.Open(Path("/dir1/file"), O_RDWR | O_CREAT | O_EXCL, &file)); + ASSERT_EQ(0, mnt.Access(Path("/dir1/file"), R_OK)); + ASSERT_EQ(4, mnt.num_nodes()); + + // Move from one directory to another should ok + ASSERT_EQ(0, mnt.Rename(Path("/dir1/file"), Path("/dir2/new_file"))); + ASSERT_NE(0, mnt.Access(Path("/dir1/file"), R_OK)); + ASSERT_EQ(0, mnt.Access(Path("/dir2/new_file"), R_OK)); + ASSERT_EQ(4, mnt.num_nodes()); + + // Move within the same directory + ASSERT_EQ(0, mnt.Rename(Path("/dir2/new_file"), Path("/dir2/new_file2"))); + ASSERT_NE(0, mnt.Access(Path("/dir2/new_file"), R_OK)); + ASSERT_EQ(0, mnt.Access(Path("/dir2/new_file2"), R_OK)); + ASSERT_EQ(4, mnt.num_nodes()); + + // Move to another directory but without a filename + ASSERT_EQ(0, mnt.Rename(Path("/dir2/new_file2"), Path("/dir1"))); + ASSERT_NE(0, mnt.Access(Path("/dir2/new_file2"), R_OK)); + ASSERT_EQ(0, mnt.Access(Path("/dir1/new_file2"), R_OK)); + ASSERT_EQ(4, mnt.num_nodes()); +} + +TEST(MountTest, MemMountRenameDir) { + MountMemMock mnt; + + ASSERT_EQ(0, mnt.Mkdir(Path("/dir1"), O_RDWR)); + ASSERT_EQ(0, mnt.Mkdir(Path("/dir2"), O_RDWR)); + EXPECT_EQ(3, mnt.num_nodes()); + + // Renaming one directory to another should work + ASSERT_EQ(0, mnt.Rename(Path("/dir1"), Path("/dir2"))); + ASSERT_NE(0, mnt.Access(Path("/dir1"), R_OK)); + ASSERT_EQ(0, mnt.Access(Path("/dir2"), R_OK)); + EXPECT_EQ(2, mnt.num_nodes()); + + // Reset to initial state + ASSERT_EQ(0, mnt.Mkdir(Path("/dir1"), O_RDWR)); + EXPECT_EQ(3, mnt.num_nodes()); + + // Renaming a directory to a new name within another + ASSERT_EQ(0, mnt.Rename(Path("/dir1"), Path("/dir2/foo"))); + ASSERT_EQ(0, mnt.Access(Path("/dir2"), R_OK)); + ASSERT_EQ(0, mnt.Access(Path("/dir2/foo"), R_OK)); + EXPECT_EQ(3, mnt.num_nodes()); + + // Reset to initial state + ASSERT_EQ(0, mnt.Rmdir(Path("/dir2/foo"))); + ASSERT_EQ(0, mnt.Mkdir(Path("/dir1"), O_RDWR)); + EXPECT_EQ(3, mnt.num_nodes()); + + // Renaming one directory to another should fail if the target is non-empty + ASSERT_EQ(0, mnt.Mkdir(Path("/dir2/dir3"), O_RDWR)); + ASSERT_EQ(ENOTEMPTY, mnt.Rename(Path("/dir1"), Path("/dir2"))); } TEST(MountTest, DevAccess) { |