blob: de03404f3549cd78298900bb0a423c83e053713c [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/minfs/component_runner.h"
#include <fidl/fuchsia.fs.startup/cpp/wire.h>
#include <lib/inspect/service/cpp/service.h>
#include <lib/syslog/cpp/macros.h>
#include "src/lib/storage/vfs/cpp/remote_dir.h"
#include "src/lib/storage/vfs/cpp/trace.h"
#include "src/storage/minfs/service/admin.h"
#include "src/storage/minfs/service/lifecycle.h"
#include "src/storage/minfs/service/startup.h"
namespace minfs {
ComponentRunner::ComponentRunner(async_dispatcher_t* dispatcher)
: fs::ManagedVfs(dispatcher), dispatcher_(dispatcher) {
outgoing_ = fbl::MakeRefCounted<fs::PseudoDir>(this);
auto startup = fbl::MakeRefCounted<fs::PseudoDir>(this);
outgoing_->AddEntry("startup", startup);
FX_LOGS(INFO) << "setting up startup service";
auto startup_svc = fbl::MakeRefCounted<StartupService>(
dispatcher_, [this](std::unique_ptr<Bcache> device, const MountOptions& options) {
FX_LOGS(INFO) << "configure callback is called";
zx::status<> status = Configure(std::move(device), options);
if (status.is_error()) {
FX_LOGS(ERROR) << "Could not configure minfs: " << status.status_string();
}
return status;
});
startup->AddEntry(fidl::DiscoverableProtocolName<fuchsia_fs_startup::Startup>, startup_svc);
}
zx::status<> ComponentRunner::ServeRoot(
fidl::ServerEnd<fuchsia_io::Directory> root,
fidl::ServerEnd<fuchsia_process_lifecycle::Lifecycle> lifecycle) {
LifecycleServer::Create(
dispatcher_, [this](fs::FuchsiaVfs::ShutdownCallback cb) { this->Shutdown(std::move(cb)); },
std::move(lifecycle));
// Make dangling endpoints for the root directory and the service directory. Creating the
// endpoints and putting them into the filesystem tree has the effect of queuing incoming
// requests until the server end of the endpoints is bound.
auto svc_endpoints = fidl::CreateEndpoints<fuchsia_io::Directory>();
if (svc_endpoints.is_error()) {
FX_LOGS(ERROR) << "mount failed; could not create service directory endpoints";
return svc_endpoints.take_error();
}
outgoing_->AddEntry("svc", fbl::MakeRefCounted<fs::RemoteDir>(std::move(svc_endpoints->client)));
svc_server_end_ = std::move(svc_endpoints->server);
auto root_endpoints = fidl::CreateEndpoints<fuchsia_io::Directory>();
if (root_endpoints.is_error()) {
FX_LOGS(ERROR) << "mount failed; could not create root directory endpoints";
return root_endpoints.take_error();
}
outgoing_->AddEntry("root",
fbl::MakeRefCounted<fs::RemoteDir>(std::move(root_endpoints->client)));
root_server_end_ = std::move(root_endpoints->server);
zx_status_t status = ServeDirectory(outgoing_, std::move(root));
if (status != ZX_OK) {
FX_LOGS(ERROR) << "mount failed; could not serve root directory";
return zx::error(status);
}
return zx::ok();
}
zx::status<> ComponentRunner::Configure(std::unique_ptr<Bcache> bcache,
const MountOptions& options) {
auto minfs = Minfs::Create(dispatcher_, std::move(bcache), options, this);
if (minfs.is_error()) {
FX_LOGS(ERROR) << "configure failed; could not create minfs: " << minfs.status_string();
return minfs.take_error();
}
minfs_ = *std::move(minfs);
SetReadonly(options.writability != Writability::Writable);
auto root = minfs_->OpenRootNode();
if (root.is_error()) {
FX_LOGS(ERROR) << "cannot find root inode: " << root.status_string();
return root.take_error();
}
zx_status_t status = ServeDirectory(*std::move(root), std::move(root_server_end_));
if (status != ZX_OK) {
FX_LOGS(ERROR) << "configure failed; could not serve root directory: "
<< zx_status_get_string(status);
return zx::error(status);
}
// Specify to fall back to DeepCopy mode instead of Live mode (the default) on failures to send
// a Frozen copy of the tree (e.g. if we could not create a child copy of the backing VMO).
// This helps prevent any issues with querying the inspect tree while the filesystem is under
// load, since snapshots at the receiving end must be consistent. See fxbug.dev/57330 for
// details.
inspect::TreeHandlerSettings settings{.snapshot_behavior =
inspect::TreeServerSendPreference::Frozen(
inspect::TreeServerSendPreference::Type::DeepCopy)};
auto inspect_tree = fbl::MakeRefCounted<fs::Service>(
[connector = inspect::MakeTreeHandler(&minfs_->InspectTree()->Inspector(), dispatcher_,
settings)](zx::channel chan) mutable {
connector(fidl::InterfaceRequest<fuchsia::inspect::Tree>(std::move(chan)));
return ZX_OK;
});
// Add the diagnostics directory straight to the outgoing directory. Nothing should be relying on
// the diagnostics directory queuing incoming requests.
auto diagnostics_dir = fbl::MakeRefCounted<fs::PseudoDir>(this);
outgoing_->AddEntry("diagnostics", diagnostics_dir);
diagnostics_dir->AddEntry(fuchsia::inspect::Tree::Name_, inspect_tree);
auto svc_dir = fbl::MakeRefCounted<fs::PseudoDir>(this);
svc_dir->AddEntry(
fidl::DiscoverableProtocolName<fuchsia_fs::Admin>,
fbl::MakeRefCounted<AdminService>(dispatcher_, [this](fs::FuchsiaVfs::ShutdownCallback cb) {
this->Shutdown(std::move(cb));
}));
status = ServeDirectory(std::move(svc_dir), std::move(svc_server_end_));
if (status != ZX_OK) {
FX_LOGS(ERROR) << "configure failed; could not serve svc dir: " << zx_status_get_string(status);
return zx::error(status);
}
return zx::ok();
}
void ComponentRunner::Shutdown(fs::FuchsiaVfs::ShutdownCallback cb) {
TRACE_DURATION("minfs", "ComponentRunner::Shutdown");
FX_LOGS(INFO) << "Shutting down";
ManagedVfs::Shutdown([this, cb = std::move(cb)](zx_status_t status) mutable {
if (status != ZX_OK) {
FX_LOGS(ERROR) << "Managed VFS shutdown failed with status: " << zx_status_get_string(status);
}
if (minfs_) {
minfs_->Sync([this, cb = std::move(cb)](zx_status_t sync_status) mutable {
if (sync_status != ZX_OK) {
FX_LOGS(ERROR) << "Sync at unmount failed with status: "
<< zx_status_get_string(sync_status);
}
async::PostTask(dispatcher(), [this, cb = std::move(cb)]() mutable {
std::unique_ptr<Bcache> bc = Minfs::Destroy(std::move(minfs_));
bc.reset();
if (on_unmount_) {
on_unmount_();
}
// Tell the unmounting channel that we've completed teardown. This *must* be the last
// thing we do because after this, the caller can assume that it's safe to destroy the
// runner.
cb(ZX_OK);
});
});
} else {
async::PostTask(dispatcher(), [this, cb = std::move(cb)]() mutable {
if (on_unmount_) {
on_unmount_();
}
cb(ZX_OK);
});
}
});
}
zx::status<fs::FilesystemInfo> ComponentRunner::GetFilesystemInfo() {
return minfs_->GetFilesystemInfo();
}
void ComponentRunner::OnNoConnections() {
if (IsTerminating()) {
return;
}
Shutdown([](zx_status_t status) mutable {
ZX_ASSERT_MSG(status == ZX_OK, "Filesystem shutdown failed on OnNoConnections(): %s",
zx_status_get_string(status));
});
}
} // namespace minfs