blob: e28eab35c831c4a32054d190f17f7b020ef2acc2 [file] [log] [blame]
// Copyright 2022 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 "src/storage/fs_test/test_filesystem.h"
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/async/cpp/executor.h>
#include <lib/diagnostics/reader/cpp/archive_reader.h>
#include <lib/fdio/cpp/caller.h>
#include <lib/fdio/directory.h>
#include <lib/fidl/cpp/wire/channel.h>
#include <lib/sys/cpp/component_context.h>
#include <gtest/gtest.h>
#include "sdk/lib/syslog/cpp/macros.h"
#include "src/storage/fs_test/crypt_service.h"
namespace fs_test {
using diagnostics::reader::InspectData;
fs_management::MountOptions TestFilesystem::DefaultMountOptions() const {
fs_management::MountOptions options;
// Blobfs-specific options:
if (options_.blob_compression_algorithm) {
options.write_compression_algorithm =
blobfs::CompressionAlgorithmToString(*options_.blob_compression_algorithm);
}
// Other filesystem options:
if (GetTraits().uses_crypt)
options.crypt_client = [] { return *GetCryptService(); };
return options;
}
zx::result<TestFilesystem> TestFilesystem::FromInstance(
const TestFilesystemOptions& options, std::unique_ptr<FilesystemInstance> instance) {
static uint32_t mount_index;
TestFilesystem filesystem(options, std::move(instance),
std::string("/fs_test." + std::to_string(mount_index++) + "/"));
auto status = filesystem.Mount();
if (status.is_error()) {
return status.take_error();
}
return zx::ok(std::move(filesystem));
}
zx::result<TestFilesystem> TestFilesystem::Create(const TestFilesystemOptions& options) {
auto instance_or = options.filesystem->Make(options);
if (instance_or.is_error()) {
return instance_or.take_error();
}
return FromInstance(options, std::move(instance_or).value());
}
zx::result<TestFilesystem> TestFilesystem::Open(const TestFilesystemOptions& options) {
auto instance_or = options.filesystem->Open(options);
if (instance_or.is_error()) {
return instance_or.take_error();
}
return FromInstance(options, std::move(instance_or).value());
}
TestFilesystem::~TestFilesystem() {
if (filesystem_) {
if (mounted_) {
auto status = Unmount();
if (status.is_error()) {
FX_LOGS(WARNING) << "Failed to unmount: " << status.status_string() << std::endl;
}
}
rmdir(mount_path_.c_str());
}
}
zx::result<> TestFilesystem::Mount(const fs_management::MountOptions& options) {
auto status = filesystem_->Mount(mount_path_, options);
if (status.is_ok()) {
mounted_ = true;
}
return status;
}
zx::result<> TestFilesystem::Unmount() {
if (!filesystem_) {
return zx::ok();
}
auto status = filesystem_->Unmount(mount_path_);
if (status.is_ok()) {
mounted_ = false;
}
return status;
}
zx::result<> TestFilesystem::Fsck() { return filesystem_->Fsck(); }
zx::result<std::string> TestFilesystem::DevicePath() const { return filesystem_->DevicePath(); }
zx::result<fuchsia_io::wire::FilesystemInfo> TestFilesystem::GetFsInfo() const {
fbl::unique_fd root_fd = fbl::unique_fd(open(mount_path().c_str(), O_RDONLY | O_DIRECTORY));
fdio_cpp::UnownedFdioCaller root_connection(root_fd);
const auto& result = fidl::WireCall(fidl::UnownedClientEnd<fuchsia_io::Directory>(
zx::unowned_channel(root_connection.borrow_channel())))
->QueryFilesystem();
if (!result.ok()) {
return zx::error(result.status());
}
if (result.value().s != ZX_OK) {
return zx::error(result.value().s);
}
return zx::ok(*result.value().info);
}
void TestFilesystem::TakeSnapshot(std::optional<InspectData>* out) const {
ASSERT_NE(nullptr, out) << "out parameter must be non-null";
ASSERT_EQ(std::nullopt, *out) << "out parameter will overwrite value already held";
async::Loop loop = async::Loop(&kAsyncLoopConfigNoAttachToCurrentThread);
loop.StartThread("inspect-snapshot-thread");
async::Executor executor(loop.dispatcher());
std::condition_variable cv;
std::mutex m;
bool done = false;
fpromise::result<std::vector<InspectData>, std::string> data_or_err;
auto component_selector =
diagnostics::reader::SanitizeMonikerForSelectors(filesystem_->GetMoniker());
{
diagnostics::reader::ArchiveReader reader(executor.dispatcher(),
{component_selector + ":root"});
auto promise =
reader.SnapshotInspectUntilPresent({filesystem_->GetMoniker()})
.then([&](fpromise::result<std::vector<InspectData>, std::string>& inspect_data) {
{
std::unique_lock<std::mutex> lock(m);
data_or_err = std::move(inspect_data);
done = true;
}
cv.notify_all();
});
executor.schedule_task(std::move(promise));
std::unique_lock<std::mutex> lock(m);
cv.wait(lock, [&done]() { return done; });
}
loop.Quit();
loop.JoinThreads();
ASSERT_TRUE(data_or_err.is_ok())
<< "Failed to obtain inspect tree snapshot: " << data_or_err.take_error();
auto all = data_or_err.take_value();
ASSERT_EQ(all.size(), 1ul) << "There should be exactly one matching Inspect hierarchy";
*out = {std::move(all[0])};
}
} // namespace fs_test