blob: 0c9eafd18e3e5c90f203f4ff63543a384be7c6a2 [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 <lib/async-loop/cpp/loop.h>
#include <fs/tracked-remote-dir.h>
#include <unittest/unittest.h>
#include <utility>
namespace {
// A Remote Directory which shuts down a dispatch loop when it is destroyed.
// This may be utilized to synchronize destruction of the remote directory
// with a test's dispatch loop.
class TestRemoteDir final : public fs::TrackedRemoteDir {
public:
TestRemoteDir(zx::channel remote, async::Loop* loop)
: TrackedRemoteDir(std::move(remote)), loop_(loop) {}
~TestRemoteDir() {
loop_->Shutdown();
}
private:
async::Loop* loop_;
};
bool TestAddingTrackedDirectory() {
BEGIN_TEST;
async::Loop loop(&kAsyncLoopConfigNoAttachToThread);
zx::channel server, client;
ASSERT_EQ(ZX_OK, zx::channel::create(0u, &server, &client));
fbl::String name = "remote-directory";
auto dir = fbl::AdoptRef<fs::PseudoDir>(new fs::PseudoDir());
// Get attributes.
vnattr_t attr;
EXPECT_EQ(ZX_OK, dir->Getattr(&attr));
EXPECT_EQ(V_TYPE_DIR | V_IRUSR, attr.mode);
EXPECT_EQ(1, attr.nlink);
// "name" should not yet exist within the directory.
fbl::RefPtr<fs::Vnode> node;
EXPECT_EQ(ZX_ERR_NOT_FOUND, dir->Lookup(&node, name));
// Add a remote directory, observe that it can be looked up.
auto remote = fbl::AdoptRef<TestRemoteDir>(new TestRemoteDir(std::move(client), &loop));
EXPECT_EQ(ZX_OK, remote->AddAsTrackedEntry(loop.dispatcher(), dir.get(), name));
remote.reset();
EXPECT_EQ(ZX_OK, dir->Lookup(&node, name));
node.reset();
// Forcing the remote connection to become "peer closed" causes the entry to be removed.
server.reset();
EXPECT_EQ(ZX_ERR_BAD_STATE, loop.Run());
EXPECT_EQ(ZX_ERR_NOT_FOUND, dir->Lookup(&node, name));
END_TEST;
}
bool TestAddingTrackedDirectoryMultiple() {
BEGIN_TEST;
async::Loop loop(&kAsyncLoopConfigNoAttachToThread);
zx::channel server, client;
ASSERT_EQ(ZX_OK, zx::channel::create(0u, &server, &client));
fbl::String name = "remote-directory";
auto dir = fbl::AdoptRef<fs::PseudoDir>(new fs::PseudoDir());
auto remote = fbl::AdoptRef<TestRemoteDir>(new TestRemoteDir(std::move(client), &loop));
EXPECT_EQ(ZX_OK, remote->AddAsTrackedEntry(loop.dispatcher(), dir.get(), name));
// Observe that we cannot track the remote object multiple times.
EXPECT_EQ(ZX_ERR_BAD_STATE, remote->AddAsTrackedEntry(loop.dispatcher(), dir.get(), name));
// Observe that we cannot track the remote directory in a different
// container.
auto dir2 = fbl::AdoptRef<fs::PseudoDir>(new fs::PseudoDir());
EXPECT_EQ(ZX_ERR_BAD_STATE, remote->AddAsTrackedEntry(loop.dispatcher(), dir2.get(), name));
remote.reset();
// Forcing the remote connection to become "peer closed" causes the entry to be removed.
server.reset();
EXPECT_EQ(ZX_ERR_BAD_STATE, loop.Run());
fbl::RefPtr<fs::Vnode> node;
EXPECT_EQ(ZX_ERR_NOT_FOUND, dir->Lookup(&node, name));
END_TEST;
}
bool TestTrackAddingDifferentVnode() {
BEGIN_TEST;
async::Loop loop(&kAsyncLoopConfigNoAttachToThread);
zx::channel server, client;
ASSERT_EQ(ZX_OK, zx::channel::create(0u, &server, &client));
auto dir = fbl::AdoptRef<fs::PseudoDir>(new fs::PseudoDir());
auto remote = fbl::AdoptRef<TestRemoteDir>(new TestRemoteDir(std::move(client), &loop));
auto not_remote = fbl::AdoptRef<fs::PseudoDir>(new fs::PseudoDir());
// Test a subtle behavior:
// - Add |remote| to |dir|, begin tracking the remote handle.
// - Remove |remote| from |dir| (while still tracking).
// - Add a different Vnode to |dir| with the same name.
// - Close the remote connection for the still-tracked, but already-removed
// vnode.
//
// This tests that when |remote| is closed, we don't accidentally remove the
// "wrong" Vnode from the containing pseudodirectory.
fbl::String name = "remote-directory";
EXPECT_EQ(ZX_OK, remote->AddAsTrackedEntry(loop.dispatcher(), dir.get(), name));
EXPECT_EQ(ZX_OK, dir->RemoveEntry(name));
EXPECT_EQ(ZX_OK, dir->AddEntry(name, not_remote));
remote.reset();
server.reset();
EXPECT_EQ(ZX_ERR_BAD_STATE, loop.Run());
fbl::RefPtr<fs::Vnode> node;
// The underlying entry should NOT have been removed.
EXPECT_EQ(ZX_OK, dir->Lookup(&node, name));
END_TEST;
}
} // namespace
BEGIN_TEST_CASE(tracked_remote_dir_tests)
RUN_TEST(TestAddingTrackedDirectory)
RUN_TEST(TestAddingTrackedDirectoryMultiple)
RUN_TEST(TestTrackAddingDifferentVnode)
END_TEST_CASE(tracked_remote_dir_tests)