blob: 495e96a1f23d7b229eb6b0045075078a59367f42 [file] [log] [blame]
// Copyright 2016 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 "fs-manager.h"
#include <fcntl.h>
#include <fuchsia/fshost/llcpp/fidl.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/async/cpp/wait.h>
#include <lib/fdio/directory.h>
#include <lib/fdio/io.h>
#include <lib/syslog/cpp/macros.h>
#include <lib/zircon-internal/debug.h>
#include <lib/zircon-internal/thread_annotations.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <threads.h>
#include <zircon/device/vfs.h>
#include <zircon/processargs.h>
#include <zircon/status.h>
#include <zircon/syscalls.h>
#include <iterator>
#include <memory>
#include <string_view>
#include <utility>
#include <fbl/algorithm.h>
#include <fbl/alloc_checker.h>
#include <fbl/auto_lock.h>
#include <fs/remote_dir.h>
#include <fs/vfs.h>
#include <fs/vfs_types.h>
#include "admin-server.h"
#include "block-watcher.h"
#include "fshost-boot-args.h"
#include "lib/async/cpp/task.h"
#include "lifecycle.h"
#include "metrics.h"
#define ZXDEBUG 0
namespace devmgr {
FsManager::FsManager(std::shared_ptr<FshostBootArgs> boot_args,
std::unique_ptr<FsHostMetrics> metrics)
: global_loop_(new async::Loop(&kAsyncLoopConfigNoAttachToCurrentThread)),
outgoing_vfs_(fs::ManagedVfs(global_loop_->dispatcher())),
registry_(global_loop_.get()),
metrics_(std::move(metrics)),
boot_args_(boot_args) {
ZX_ASSERT(global_root_ == nullptr);
}
// In the event that we haven't been explicitly signalled, tear ourself down.
FsManager::~FsManager() {
if (global_shutdown_.has_handler()) {
event_.signal(0, FSHOST_SIGNAL_EXIT);
auto deadline = zx::deadline_after(zx::sec(2));
zx_signals_t pending;
event_.wait_one(FSHOST_SIGNAL_EXIT_DONE, deadline, &pending);
}
sync_completion_t sync;
outgoing_vfs_.Shutdown([&](zx_status_t status) { sync_completion_signal(&sync); });
sync_completion_wait(&sync, ZX_TIME_INFINITE);
// Ensure all asynchronous work on global_loop_ finishes.
// Some of the asynchronous work references memory owned by this instance, so we need to ensure
// the work is complete before destruction.
// TODO(sdemos): Clean up ordering of fields to let the natural destructor ordering handle
// shutdown.
global_loop_->Shutdown();
}
zx_status_t FsManager::SetupLifecycleServer(zx::channel lifecycle_request) {
return devmgr::LifecycleServer::Create(global_loop_->dispatcher(), this,
std::move(lifecycle_request));
}
// Sets up the outgoing directory, and runs it on the PA_DIRECTORY_REQUEST
// handle if it exists. See fshost.cml for a list of what's in the directory.
zx_status_t FsManager::SetupOutgoingDirectory(zx::channel dir_request,
std::shared_ptr<loader::LoaderServiceBase> loader,
BlockWatcher& watcher) {
auto outgoing_dir = fbl::MakeRefCounted<fs::PseudoDir>();
// TODO(unknown): fshost exposes two separate service directories, one here and one in
// the registry vfs that's mounted under fs-manager-svc further down in this
// function. These should be combined by either pulling the registry services
// into this VFS or by pushing the services in this directory into the
// registry.
// Add loader and admin services to the vfs
auto svc_dir = fbl::MakeRefCounted<fs::PseudoDir>();
// This service name is breaking the convention whereby the directory entry
// name matches the protocol name. This is an implementation of
// fuchsia.ldsvc.Loader, and is renamed to make it easier to identify that
// this implementation comes from fshost.
svc_dir->AddEntry(
"fuchsia.fshost.Loader", fbl::MakeRefCounted<fs::Service>([loader](zx::channel chan) {
auto status = loader->Bind(std::move(chan));
if (status.is_error()) {
FX_LOGS(ERROR) << "failed to attach loader service: " << status.status_string();
}
return status.status_value();
}));
svc_dir->AddEntry(llcpp::fuchsia::fshost::Admin::Name,
AdminServer::Create(this, global_loop_->dispatcher()));
svc_dir->AddEntry(llcpp::fuchsia::fshost::BlockWatcher::Name,
BlockWatcherServer::Create(global_loop_->dispatcher(), watcher));
outgoing_dir->AddEntry("svc", std::move(svc_dir));
// Add /fs to the outgoing vfs
zx::channel filesystems_client, filesystems_server;
zx_status_t status = zx::channel::create(0, &filesystems_client, &filesystems_server);
if (status != ZX_OK) {
FX_LOGS(ERROR) << "failed to create channel";
return status;
}
status = this->ServeRoot(std::move(filesystems_server));
if (status != ZX_OK) {
FX_LOGS(ERROR) << "Cannot serve root filesystem";
return status;
}
outgoing_dir->AddEntry("fs", fbl::MakeRefCounted<fs::RemoteDir>(std::move(filesystems_client)));
// Add /fs-manager-svc to the vfs
zx::channel services_client, services_server;
status = zx::channel::create(0, &services_client, &services_server);
if (status != ZX_OK) {
FX_LOGS(ERROR) << "failed to create channel";
return status;
}
status = this->ServeFshostRoot(std::move(services_server));
if (status != ZX_OK) {
FX_LOGS(ERROR) << "Cannot serve export directory";
return status;
}
outgoing_dir->AddEntry("fs-manager-svc",
fbl::MakeRefCounted<fs::RemoteDir>(std::move(services_client)));
// TODO(fxbug.dev/39588): delete this
// Add the delayed directory
zx::channel filesystems_client_2, filesystems_server_2;
status = zx::channel::create(0, &filesystems_client_2, &filesystems_server_2);
if (status != ZX_OK) {
FX_LOGS(ERROR) << "failed to create channel";
return status;
}
status = this->ServeRoot(std::move(filesystems_server_2));
if (status != ZX_OK) {
FX_LOGS(ERROR) << "cannot serve root filesystem";
return status;
}
outgoing_dir->AddEntry("delayed", delayed_outdir_.Initialize(std::move(filesystems_client_2)));
// Add the diagnostics directory
diagnostics_dir_ = inspect_.Initialize(global_loop_->dispatcher());
outgoing_dir->AddEntry("diagnostics", diagnostics_dir_);
// Run the outgoing directory
outgoing_vfs_.ServeDirectory(outgoing_dir, std::move(dir_request));
return ZX_OK;
}
zx_status_t FsManager::Initialize(zx::channel dir_request, zx::channel lifecycle_request,
std::shared_ptr<loader::LoaderServiceBase> loader,
BlockWatcher& watcher) {
zx_status_t status = memfs::Vfs::Create("<root>", &root_vfs_, &global_root_);
if (status != ZX_OK) {
return status;
}
fbl::RefPtr<fs::Vnode> vn;
if ((status = global_root_->Create("boot", S_IFDIR, &vn)) != ZX_OK) {
return status;
}
if ((status = global_root_->Create("tmp", S_IFDIR, &vn)) != ZX_OK) {
return status;
}
for (unsigned n = 0; n < std::size(kMountPoints); n++) {
auto open_result = root_vfs_->Open(global_root_, std::string_view(kMountPoints[n]),
fs::VnodeConnectionOptions::ReadWrite().set_create(),
fs::Rights::ReadWrite(), S_IFDIR);
if (open_result.is_error()) {
return open_result.error();
}
ZX_ASSERT(open_result.is_ok());
mount_nodes[n] = std::move(open_result.ok().vnode);
}
auto open_result =
root_vfs_->Open(global_root_, std::string_view("/data"),
fs::VnodeConnectionOptions::ReadOnly(), fs::Rights::ReadOnly(), S_IFDIR);
if (open_result.is_ok()) {
inspect_.ServeStats("data", open_result.ok().vnode);
} else {
FX_LOGS(ERROR) << "failed to serve /data stats";
}
status = zx::event::create(0, &event_);
if (status != ZX_OK) {
FX_LOGS(ERROR) << "failed to create fs-manager event: " << status;
return status;
}
global_loop_->StartThread("root-dispatcher");
root_vfs_->SetDispatcher(global_loop_->dispatcher());
if (dir_request.is_valid()) {
status = SetupOutgoingDirectory(std::move(dir_request), std::move(loader), watcher);
if (status != ZX_OK) {
return status;
}
}
if (lifecycle_request.is_valid()) {
status = SetupLifecycleServer(std::move(lifecycle_request));
if (status != ZX_OK) {
return status;
}
}
return ZX_OK;
}
void FsManager::FlushMetrics() { mutable_metrics()->Flush(); }
zx_status_t FsManager::InstallFs(const char* path, zx::channel h) {
for (unsigned n = 0; n < std::size(kMountPoints); n++) {
if (!strcmp(path, kMountPoints[n])) {
return root_vfs_->InstallRemote(mount_nodes[n], fs::MountChannel(std::move(h)));
}
}
return ZX_ERR_NOT_FOUND;
}
zx_status_t FsManager::ServeRoot(zx::channel server) {
fs::Rights rights;
rights.read = true;
rights.write = true;
rights.admin = true;
rights.execute = true;
return root_vfs_->ServeDirectory(global_root_, std::move(server), rights);
}
void FsManager::WatchExit() {
FX_LOGS(INFO) << "watching for exit";
global_shutdown_.set_handler([this](async_dispatcher_t* dispatcher, async::Wait* wait,
zx_status_t status, const zx_packet_signal_t* signal) {
FX_LOGS(INFO) << "exit signal detected";
root_vfs_->UninstallAll(zx::time::infinite());
event_.signal(0, FSHOST_SIGNAL_EXIT_DONE);
});
global_shutdown_.set_object(event_.get());
global_shutdown_.set_trigger(FSHOST_SIGNAL_EXIT);
global_shutdown_.Begin(global_loop_->dispatcher());
}
void FsManager::Shutdown(fit::function<void(zx_status_t)> callback) {
zx_status_t status = event_.signal(0, FSHOST_SIGNAL_EXIT);
if (status != ZX_OK) {
FX_LOGS(ERROR) << "error signalling event: " << zx_status_get_string(status);
return;
}
shutdown_waiter_ = std::make_unique<async::Wait>(event_.get(), FSHOST_SIGNAL_EXIT_DONE);
shutdown_waiter_->set_handler(
[callback = std::move(callback)](
async_dispatcher_t* unused_dispatched, async::Wait* unused_wait, zx_status_t status,
/*signal*/ const zx_packet_signal_t* unused_packet_signal) { callback(status); });
shutdown_waiter_->Begin(global_loop_->dispatcher());
}
zx_status_t FsManager::AddFsDiagnosticsDirectory(const char* diagnostics_dir_name,
zx::channel fs_diagnostics_dir_client) {
// The diagnostics directory may not be initialized in tests.
if (diagnostics_dir_ == nullptr) {
return ZX_ERR_INTERNAL;
}
auto fs_diagnostics_dir =
fbl::MakeRefCounted<fs::RemoteDir>(std::move(fs_diagnostics_dir_client));
return diagnostics_dir_->AddEntry(diagnostics_dir_name, fs_diagnostics_dir);
}
} // namespace devmgr