blob: 092eaff0bbd54f64c6068ddd8034e615ba74d80b [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 "src/storage/blobfs/runner.h"
#include <fidl/fuchsia.fs/cpp/wire.h>
#include <fidl/fuchsia.update.verify/cpp/wire.h>
#include <lib/inspect/service/cpp/service.h>
#include <lib/syslog/cpp/macros.h>
#include "src/lib/storage/vfs/cpp/pseudo_dir.h"
#include "src/storage/blobfs/service/admin.h"
#include "src/storage/blobfs/service/health_check.h"
namespace blobfs {
// static.
zx::status<std::unique_ptr<Runner>> Runner::Create(async::Loop* loop,
std::unique_ptr<BlockDevice> device,
const MountOptions& options,
zx::resource vmex_resource) {
// The runner owns the blobfs, but the runner needs to be created first because it is the Vfs
// object that Blobfs uses.
std::unique_ptr<Runner> runner(new Runner(loop, options.paging_threads));
if (auto status = runner->Init(); status.is_error())
return status.take_error();
// All of our pager threads get the deadline profile for scheduling.
SetDeadlineProfile(runner->GetPagerThreads());
auto blobfs_or = Blobfs::Create(loop->dispatcher(), std::move(device), runner.get(), options,
std::move(vmex_resource));
if (blobfs_or.is_error())
return blobfs_or.take_error();
runner->blobfs_ = std::move(blobfs_or.value());
runner->SetReadonly(runner->blobfs_->writability() != Writability::Writable);
return zx::ok(std::move(runner));
}
Runner::Runner(async::Loop* loop, int32_t paging_threads)
: fs::PagedVfs(loop->dispatcher(), paging_threads), loop_(loop) {}
Runner::~Runner() {
// Inform PagedVfs so that it can stop threads that might call out to blobfs.
TearDown();
}
void Runner::Shutdown(fs::FuchsiaVfs::ShutdownCallback cb) {
TRACE_DURATION("blobfs", "Runner::Unmount");
// Shutdown all external connections to blobfs.
ManagedVfs::Shutdown([this, cb = std::move(cb)](zx_status_t status) mutable {
async::PostTask(dispatcher(), [this, status, cb = std::move(cb)]() mutable {
// Manually destroy the filesystem. The promise of Shutdown is that no connections are active,
// and destroying the Runner object should terminate all background workers.
blobfs_ = nullptr;
// Tell the mounting thread that the filesystem has terminated.
loop_->Quit();
// 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(status);
});
});
}
zx::status<fs::FilesystemInfo> Runner::GetFilesystemInfo() { return blobfs_->GetFilesystemInfo(); }
zx_status_t Runner::ServeRoot(fidl::ServerEnd<fuchsia_io::Directory> root) {
fbl::RefPtr<fs::Vnode> vn;
zx_status_t status = blobfs_->OpenRootNode(&vn);
if (status != ZX_OK) {
FX_LOGS(ERROR) << "mount failed; could not get root blob";
return 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(blobfs_->GetMetrics()->inspector(), loop_->dispatcher(),
settings)](zx::channel chan) mutable {
connector(fidl::InterfaceRequest<fuchsia::inspect::Tree>(std::move(chan)));
return ZX_OK;
});
auto outgoing = fbl::MakeRefCounted<fs::PseudoDir>(this);
outgoing->AddEntry(kOutgoingDataRoot, std::move(vn));
auto diagnostics_dir = fbl::MakeRefCounted<fs::PseudoDir>(this);
outgoing->AddEntry("diagnostics", diagnostics_dir);
diagnostics_dir->AddEntry(fuchsia::inspect::Tree::Name_, inspect_tree);
outgoing->AddEntry(fidl::DiscoverableProtocolName<fuchsia_update_verify::BlobfsVerifier>,
fbl::MakeRefCounted<HealthCheckService>(loop_->dispatcher(), *blobfs_));
outgoing->AddEntry(fidl::DiscoverableProtocolName<fuchsia_fs::Admin>,
fbl::MakeRefCounted<AdminService>(blobfs_->dispatcher(),
[this](fs::FuchsiaVfs::ShutdownCallback cb) {
this->Shutdown(std::move(cb));
}));
status = ServeDirectory(std::move(outgoing), std::move(root));
if (status != ZX_OK) {
FX_LOGS(ERROR) << "mount failed; could not serve root directory";
return status;
}
return ZX_OK;
}
bool Runner::IsReadonly() {
std::lock_guard lock(vfs_lock_);
return ReadonlyLocked();
}
} // namespace blobfs