|  | // Copyright 2020 The Fuchsia 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 <fidl/fuchsia.io/cpp/wire.h> | 
|  | #include <fidl/fuchsia.io/cpp/wire_test_base.h> | 
|  | #include <lib/fdio/directory.h> | 
|  | #include <lib/fidl/llcpp/connect_service.h> | 
|  | #include <lib/zx/channel.h> | 
|  |  | 
|  | #include <string> | 
|  |  | 
|  | #include <fbl/unique_fd.h> | 
|  |  | 
|  | #include "src/storage/fs_test/fs_test_fixture.h" | 
|  | #include "src/storage/fs_test/misc.h" | 
|  |  | 
|  | namespace fs_test { | 
|  | namespace { | 
|  |  | 
|  | namespace fio = fuchsia_io; | 
|  |  | 
|  | using OpenTest = FilesystemTest; | 
|  |  | 
|  | fidl::ClientEnd<fio::Directory> CreateDirectory(fio::wire::OpenFlags dir_flags, | 
|  | const std::string& path) { | 
|  | EXPECT_EQ(mkdir(path.c_str(), 0755), 0); | 
|  |  | 
|  | auto endpoints = fidl::CreateEndpoints<fio::Directory>(); | 
|  | EXPECT_EQ(endpoints.status_value(), ZX_OK); | 
|  | EXPECT_EQ( | 
|  | fdio_open(path.c_str(), static_cast<uint32_t>(dir_flags | fio::wire::OpenFlags::kDirectory), | 
|  | endpoints->server.TakeChannel().release()), | 
|  | ZX_OK); | 
|  |  | 
|  | return std::move(endpoints->client); | 
|  | } | 
|  |  | 
|  | zx_status_t OpenFileWithCreate(const fidl::ClientEnd<fio::Directory>& dir, | 
|  | const std::string& path) { | 
|  | fio::wire::OpenFlags child_flags = fio::wire::OpenFlags::kCreate | | 
|  | fio::wire::OpenFlags::kRightReadable | | 
|  | fio::wire::OpenFlags::kDescribe; | 
|  | auto child_endpoints = fidl::CreateEndpoints<fio::Node>(); | 
|  | EXPECT_EQ(child_endpoints.status_value(), ZX_OK); | 
|  | auto open_res = fidl::WireCall(dir)->Open(child_flags, fio::wire::kModeTypeFile, | 
|  | fidl::StringView::FromExternal(path), | 
|  | std::move(child_endpoints->server)); | 
|  | EXPECT_EQ(open_res.status(), ZX_OK); | 
|  | auto child = fidl::BindSyncClient(std::move(child_endpoints->client)); | 
|  |  | 
|  | class EventHandler : public fidl::testing::WireSyncEventHandlerTestBase<fio::Node> { | 
|  | public: | 
|  | EventHandler() = default; | 
|  | zx_status_t status() const { return status_; } | 
|  |  | 
|  | void OnOpen(fidl::WireEvent<fio::Node::OnOpen>* event) override { status_ = event->s; } | 
|  |  | 
|  | void NotImplemented_(const std::string& name) override { FAIL() << "Unexpected " << name; } | 
|  |  | 
|  | private: | 
|  | zx_status_t status_ = ZX_OK; | 
|  | }; | 
|  |  | 
|  | EventHandler event_handler; | 
|  | auto handle_res = child.HandleOneEvent(event_handler); | 
|  | EXPECT_EQ(handle_res.status(), ZX_OK); | 
|  |  | 
|  | return event_handler.status(); | 
|  | } | 
|  |  | 
|  | TEST_P(OpenTest, OpenFileWithCreateCreatesInReadWriteDir) { | 
|  | fio::wire::OpenFlags flags = | 
|  | fio::wire::OpenFlags::kRightReadable | fio::wire::OpenFlags::kRightWritable; | 
|  | auto parent = CreateDirectory(flags, GetPath("a")); | 
|  | EXPECT_EQ(OpenFileWithCreate(parent, "b"), ZX_OK); | 
|  | } | 
|  |  | 
|  | TEST_P(OpenTest, OpenFileWithCreateFailsInReadOnlyDir) { | 
|  | fio::wire::OpenFlags flags = fio::wire::OpenFlags::kRightReadable; | 
|  | auto parent = CreateDirectory(flags, GetPath("a")); | 
|  | EXPECT_EQ(OpenFileWithCreate(parent, "b"), ZX_ERR_ACCESS_DENIED); | 
|  | } | 
|  |  | 
|  | TEST_P(OpenTest, OpenFileWithCreateCreatesInReadWriteDirPosixOpen) { | 
|  | // kOpenFlagPosixWritable expand the rights of the directory connection to include write rights if | 
|  | // the parent connection has them. | 
|  | fio::wire::OpenFlags flags = | 
|  | fio::wire::OpenFlags::kRightReadable | fio::wire::OpenFlags::kRightWritable; | 
|  | auto parent = CreateDirectory(flags, GetPath("a")); | 
|  |  | 
|  | flags = fio::wire::OpenFlags::kRightReadable | fio::wire::OpenFlags::kPosixWritable | | 
|  | fio::wire::OpenFlags::kDirectory; | 
|  | uint32_t mode = fio::wire::kModeTypeDirectory; | 
|  | std::string path = "."; | 
|  | auto clone_endpoints = fidl::CreateEndpoints<fio::Node>(); | 
|  | ASSERT_EQ(clone_endpoints.status_value(), ZX_OK); | 
|  | auto clone_res = fidl::WireCall(parent)->Open(flags, mode, fidl::StringView::FromExternal(path), | 
|  | std::move(clone_endpoints->server)); | 
|  | ASSERT_EQ(clone_res.status(), ZX_OK); | 
|  | fidl::ClientEnd<fio::Directory> clone_dir(clone_endpoints->client.TakeChannel()); | 
|  |  | 
|  | EXPECT_EQ(OpenFileWithCreate(clone_dir, "b"), ZX_OK); | 
|  | } | 
|  |  | 
|  | TEST_P(OpenTest, OpenFileWithCreateFailsInReadOnlyDirPosixOpen) { | 
|  | fio::wire::OpenFlags flags = fio::wire::OpenFlags::kRightReadable; | 
|  | auto parent = CreateDirectory(flags, GetPath("a")); | 
|  |  | 
|  | flags = fio::wire::OpenFlags::kRightReadable | fio::wire::OpenFlags::kPosixWritable | | 
|  | fio::wire::OpenFlags::kDirectory; | 
|  | uint32_t mode = fio::wire::kModeTypeDirectory; | 
|  | std::string path = "."; | 
|  | auto clone_endpoints = fidl::CreateEndpoints<fio::Node>(); | 
|  | ASSERT_EQ(clone_endpoints.status_value(), ZX_OK); | 
|  | auto clone_res = fidl::WireCall(parent)->Open(flags, mode, fidl::StringView::FromExternal(path), | 
|  | std::move(clone_endpoints->server)); | 
|  | ASSERT_EQ(clone_res.status(), ZX_OK); | 
|  | fidl::ClientEnd<fio::Directory> clone_dir(clone_endpoints->client.TakeChannel()); | 
|  |  | 
|  | EXPECT_EQ(OpenFileWithCreate(clone_dir, "b"), ZX_ERR_ACCESS_DENIED); | 
|  | } | 
|  |  | 
|  | TEST_P(OpenTest, OpenFileWithCreateFailsInReadWriteDirPosixClone) { | 
|  | // kOpenFlagPosixWritable only does the rights expansion with the open call though. | 
|  | fio::wire::OpenFlags flags = | 
|  | fio::wire::OpenFlags::kRightReadable | fio::wire::OpenFlags::kRightWritable; | 
|  | auto parent = CreateDirectory(flags, GetPath("a")); | 
|  |  | 
|  | flags = fio::wire::OpenFlags::kRightReadable | fio::wire::OpenFlags::kPosixWritable | | 
|  | fio::wire::OpenFlags::kDirectory; | 
|  | auto clone_endpoints = fidl::CreateEndpoints<fio::Node>(); | 
|  | ASSERT_EQ(clone_endpoints.status_value(), ZX_OK); | 
|  | auto clone_res = fidl::WireCall(parent)->Clone(flags, std::move(clone_endpoints->server)); | 
|  | ASSERT_EQ(clone_res.status(), ZX_OK); | 
|  | fidl::ClientEnd<fio::Directory> clone_dir(clone_endpoints->client.TakeChannel()); | 
|  |  | 
|  | EXPECT_EQ(OpenFileWithCreate(clone_dir, "b"), ZX_ERR_ACCESS_DENIED); | 
|  | } | 
|  |  | 
|  | TEST_P(OpenTest, OpenFileWithCreateFailsInReadOnlyDirPosixClone) { | 
|  | fio::wire::OpenFlags flags = fio::wire::OpenFlags::kRightReadable; | 
|  | auto parent = CreateDirectory(flags, GetPath("a")); | 
|  |  | 
|  | flags = fio::wire::OpenFlags::kRightReadable | fio::wire::OpenFlags::kPosixWritable | | 
|  | fio::wire::OpenFlags::kDirectory; | 
|  | auto clone_endpoints = fidl::CreateEndpoints<fio::Node>(); | 
|  | ASSERT_EQ(clone_endpoints.status_value(), ZX_OK); | 
|  | auto clone_res = fidl::WireCall(parent)->Clone(flags, std::move(clone_endpoints->server)); | 
|  | ASSERT_EQ(clone_res.status(), ZX_OK); | 
|  | fidl::ClientEnd<fio::Directory> clone_dir(clone_endpoints->client.TakeChannel()); | 
|  |  | 
|  | EXPECT_EQ(OpenFileWithCreate(clone_dir, "b"), ZX_ERR_ACCESS_DENIED); | 
|  | } | 
|  |  | 
|  | INSTANTIATE_TEST_SUITE_P(/*no prefix*/, OpenTest, testing::ValuesIn(AllTestFilesystems()), | 
|  | testing::PrintToStringParamName()); | 
|  |  | 
|  | }  // namespace | 
|  | }  // namespace fs_test |