blob: 11fd8d881b9c2d3b5c7c709718e5a8f08f984a61 [file] [log] [blame]
// 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 <fuchsia/io/llcpp/fidl.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(uint32_t 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(), dir_flags | fio::wire::kOpenFlagDirectory,
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) {
uint32_t child_flags =
fio::wire::kOpenFlagCreate | fio::wire::kOpenRightReadable | fio::wire::kOpenFlagDescribe;
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::WireSyncEventHandler<fio::Node> {
public:
EventHandler() = default;
zx_status_t status() const { return status_; }
void OnOpen(fidl::WireResponse<fio::Node::OnOpen>* event) override { status_ = event->s; }
zx_status_t Unknown() override { return ZX_ERR_IO; }
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) {
uint32_t flags = fio::wire::kOpenRightReadable | fio::wire::kOpenRightWritable;
auto parent = CreateDirectory(flags, GetPath("a"));
EXPECT_EQ(OpenFileWithCreate(parent, "b"), ZX_OK);
}
TEST_P(OpenTest, OpenFileWithCreateFailsInReadOnlyDir) {
uint32_t flags = fio::wire::kOpenRightReadable;
auto parent = CreateDirectory(flags, GetPath("a"));
EXPECT_EQ(OpenFileWithCreate(parent, "b"), ZX_ERR_ACCESS_DENIED);
}
TEST_P(OpenTest, OpenFileWithCreateCreatesInReadWriteDirPosixOpen) {
// kOpenFlagPosix expands the rights of the connection to be the maximum level of rights
// available, based on the connection used to call open.
uint32_t flags = fio::wire::kOpenRightReadable | fio::wire::kOpenRightWritable;
auto parent = CreateDirectory(flags, GetPath("a"));
flags = fio::wire::kOpenRightReadable | fio::wire::kOpenFlagPosix | fio::wire::kOpenFlagDirectory;
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) {
uint32_t flags = fio::wire::kOpenRightReadable;
auto parent = CreateDirectory(flags, GetPath("a"));
flags = fio::wire::kOpenRightReadable | fio::wire::kOpenFlagPosix | fio::wire::kOpenFlagDirectory;
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) {
// kOpenFlagPosix only does the rights expansion with the open call though.
uint32_t flags = fio::wire::kOpenRightReadable | fio::wire::kOpenRightWritable;
auto parent = CreateDirectory(flags, GetPath("a"));
flags = fio::wire::kOpenRightReadable | fio::wire::kOpenFlagPosix | fio::wire::kOpenFlagDirectory;
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) {
uint32_t flags = fio::wire::kOpenRightReadable;
auto parent = CreateDirectory(flags, GetPath("a"));
flags = fio::wire::kOpenRightReadable | fio::wire::kOpenFlagPosix | fio::wire::kOpenFlagDirectory;
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);
}
// TODO(fxbug.dev/45624): fix this issue in the rust vfs, then enable these tests for fat.
std::vector<TestFilesystemOptions> GetTestCombinations() {
return MapAndFilterAllTestFilesystems(
[](const TestFilesystemOptions& options) -> std::optional<TestFilesystemOptions> {
if (!options.filesystem->GetTraits().is_fat) {
return options;
} else {
return std::nullopt;
}
});
}
INSTANTIATE_TEST_SUITE_P(/*no prefix*/, OpenTest, testing::ValuesIn(GetTestCombinations()),
testing::PrintToStringParamName());
} // namespace
} // namespace fs_test