blob: 0b56743a4c0dfb0af38885ce4c917de5ae1a7430 [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 "system-instance.h"
#include <fuchsia/io/c/fidl.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/fdio/fd.h>
#include <lib/fdio/namespace.h>
#include <lib/fidl-async/bind.h>
#include <memory>
#include <zxtest/zxtest.h>
namespace {
// Create a subclass to access the protected test-only constructor on SystemInstance.
class SystemInstanceForTest : public SystemInstance {
public:
SystemInstanceForTest(zx::channel fs_root) : SystemInstance(std::move(fs_root)) {}
};
struct Context {
uint32_t open_flags;
uint32_t open_count;
char path[PATH_MAX + 1];
};
static zx_status_t DirectoryOpen(void* ctx, uint32_t flags, uint32_t mode, const char* path_data,
size_t path_size, zx_handle_t object) {
Context* context = reinterpret_cast<Context*>(ctx);
context->open_flags = flags;
context->open_count += 1;
memcpy(context->path, path_data, path_size);
context->path[path_size] = '\0';
// Having this handle still open does not spark joy. Thank it for its
// service, and then let it go.
zx_handle_close(object);
return ZX_OK;
}
static const fuchsia_io_DirectoryAdmin_ops_t kDirectoryAdminOps = []() {
fuchsia_io_DirectoryAdmin_ops_t ops;
ops.Open = DirectoryOpen;
return ops;
}();
class SystemInstanceFsProvider : public zxtest::Test {
protected:
SystemInstanceFsProvider() : loop_(&kAsyncLoopConfigNoAttachToCurrentThread) {
ASSERT_OK(loop_.StartThread());
// Mock out an object that implements DirectoryOpen and records some state.
// Bind it to the server handle, and provide that to SystemInstance as the fs_root connection.
zx::channel client, server;
ASSERT_OK(zx::channel::create(0, &client, &server));
ASSERT_OK(fidl_bind(loop_.dispatcher(), server.release(),
reinterpret_cast<fidl_dispatch_t*>(fuchsia_io_DirectoryAdmin_dispatch),
&context_, &kDirectoryAdminOps));
under_test_.reset(new SystemInstanceForTest(std::move(client)));
}
void CloneFsAndCheckFlags(const char* path, uint32_t expected_flags) {
uint32_t starting_open_count = context_.open_count;
zx::channel fs_connection = under_test_->CloneFs(path);
// Force a describe call on the target of the Open, to resolve the Open. We expect this to fail
// because our mock just closes the channel after Open.
int fd;
EXPECT_EQ(ZX_ERR_PEER_CLOSED, fdio_fd_create(fs_connection.release(), &fd));
EXPECT_EQ(starting_open_count + 1, context_.open_count);
EXPECT_EQ(expected_flags, context_.open_flags);
EXPECT_STR_EQ(path, context_.path);
}
private:
async::Loop loop_;
Context context_;
std::unique_ptr<SystemInstanceForTest> under_test_;
};
// Test that asking SystemInstance's FsProvider impl for blob opens /fs/blob from the current
// installed namespace without the EXEC right
TEST_F(SystemInstanceFsProvider, CloneBlobNonExec) {
// Importantly, this list of expected_flags lacks ZX_FS_RIGHT_EXECUTABLE
uint32_t expected_flags = ZX_FS_RIGHT_READABLE | ZX_FS_RIGHT_WRITABLE | ZX_FS_RIGHT_ADMIN |
ZX_FS_FLAG_DIRECTORY | ZX_FS_FLAG_NOREMOTE;
CloneFsAndCheckFlags("blob", expected_flags);
}
// Cloning /pkgfs should provide READ | EXEC rights, as should /bin and /pkgfs which are both paths
// into pkgfs
TEST_F(SystemInstanceFsProvider, ClonePkgfs) {
uint32_t expected_flags = ZX_FS_RIGHT_READABLE | ZX_FS_RIGHT_EXECUTABLE | ZX_FS_RIGHT_ADMIN |
ZX_FS_FLAG_DIRECTORY | ZX_FS_FLAG_NOREMOTE;
CloneFsAndCheckFlags("pkgfs", expected_flags);
CloneFsAndCheckFlags("bin", expected_flags);
CloneFsAndCheckFlags("system", expected_flags);
}
} // namespace