blob: 052209e67d45253bbc55b9dc591f1279dffde223 [file] [log] [blame]
// Copyright 2019 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/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/fdio/vfs.h>
#include <lib/sync/completion.h>
#include <atomic>
#include <memory>
#include <fbl/auto_lock.h>
#include <fbl/mutex.h>
#include <fs/managed_vfs.h>
#include <fs/vfs_types.h>
#include <unittest/unittest.h>
#include "filesystems.h"
namespace {
namespace fio = ::llcpp::fuchsia::io;
bool TestConnectionRights() {
BEGIN_TEST;
async::Loop loop(&kAsyncLoopConfigNoAttachToCurrentThread);
ASSERT_EQ(loop.StartThread(), ZX_OK);
std::unique_ptr<fs::ManagedVfs> vfs = std::make_unique<fs::ManagedVfs>(loop.dispatcher());
// Set up a vnode for the root directory
class TestVNode : public fs::Vnode {
public:
zx_status_t GetNodeInfoForProtocol([[maybe_unused]] fs::VnodeProtocol protocol,
[[maybe_unused]] fs::Rights rights,
fs::VnodeRepresentation* info) override {
*info = fs::VnodeRepresentation::File();
return ZX_OK;
}
fs::VnodeProtocolSet GetProtocols() const final { return fs::VnodeProtocol::kFile; }
zx_status_t GetVmo(int flags, zx_handle_t* out_vmo, size_t* out_size) override {
zx::vmo vmo;
ASSERT_EQ(zx::vmo::create(4096, 0u, &vmo), ZX_OK);
*out_vmo = vmo.release();
*out_size = 0;
return ZX_OK;
}
};
typedef struct test_row {
public:
uint32_t connection_flags; // Or'd ZX_FS_RIGHT_* flags for this connection.
uint32_t request_flags; // Or'd fio::VMO_FLAG_* values.
zx_status_t expected_result; // What we expect FileGetBuffer to return.
} test_row_t;
test_row_t test_data[] = {
// If the connection has all rights, then everything should work.
{.connection_flags = ZX_FS_RIGHT_READABLE | ZX_FS_RIGHT_WRITABLE | ZX_FS_RIGHT_EXECUTABLE,
.request_flags = fio::VMO_FLAG_READ,
.expected_result = ZX_OK},
{.connection_flags = ZX_FS_RIGHT_READABLE | ZX_FS_RIGHT_WRITABLE | ZX_FS_RIGHT_EXECUTABLE,
.request_flags = fio::VMO_FLAG_READ | fio::VMO_FLAG_WRITE,
.expected_result = ZX_OK},
{.connection_flags = ZX_FS_RIGHT_READABLE | ZX_FS_RIGHT_WRITABLE | ZX_FS_RIGHT_EXECUTABLE,
.request_flags = fio::VMO_FLAG_READ | fio::VMO_FLAG_EXEC,
.expected_result = ZX_OK},
// If the connection is missing the EXECUTABLE right, then requests with
// fio::VMO_FLAG_EXEC should fail.
{.connection_flags = ZX_FS_RIGHT_READABLE | ZX_FS_RIGHT_WRITABLE,
.request_flags = fio::VMO_FLAG_READ | fio::VMO_FLAG_EXEC,
.expected_result = ZX_ERR_ACCESS_DENIED},
// If the connection is missing the WRITABLE right, then requests with
// fio::VMO_FLAG_WRITE should fail.
{.connection_flags = ZX_FS_RIGHT_READABLE | ZX_FS_RIGHT_EXECUTABLE,
.request_flags = fio::VMO_FLAG_READ | fio::VMO_FLAG_WRITE,
.expected_result = ZX_ERR_ACCESS_DENIED},
};
{
auto vnode = fbl::AdoptRef<TestVNode>(new TestVNode());
for (test_row_t& row : test_data) {
// Set up a vfs connection with the testcase's connection flags
zx::channel client, server;
ASSERT_EQ(zx::channel::create(0, &client, &server), ZX_OK);
uint32_t flags = row.connection_flags;
vfs->Serve(vnode, std::move(server), fs::VnodeConnectionOptions::FromIoV1Flags(flags));
// Call FileGetBuffer on the channel with the testcase's request flags.
// Check that we get the expected result.
auto result =
fio::File::Call::GetBuffer(zx::unowned_channel(client.get()), row.request_flags);
EXPECT_EQ(result.status(), ZX_OK);
// Verify that the result matches the value in our test table.
EXPECT_EQ(result.Unwrap()->s, row.expected_result);
}
}
// Tear down the VFS. On completion, it will no longer rely on the async
// loop. Then, tear down the async loop.
sync_completion_t completion;
vfs->Shutdown([&completion](zx_status_t status) {
EXPECT_EQ(status, ZX_OK);
sync_completion_signal(&completion);
});
sync_completion_wait(&completion, zx::time::infinite().get());
loop.Shutdown();
END_TEST;
}
} // namespace
BEGIN_TEST_CASE(GetBufferTest)
RUN_TEST(TestConnectionRights)
END_TEST_CASE(GetBufferTest)