blob: 69122ff498c1f481255355100cbd0f08892af3a6 [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 "src/storage/lib/fs_management/cpp/mount.h"
#include <errno.h>
#include <fidl/fuchsia.component/cpp/wire.h>
#include <fidl/fuchsia.fs.startup/cpp/wire.h>
#include <fidl/fuchsia.fs/cpp/wire.h>
#include <fidl/fuchsia.hardware.block/cpp/wire.h>
#include <fidl/fuchsia.io/cpp/wire.h>
#include <fuchsia/hardware/block/driver/c/banjo.h>
#include <lib/component/incoming/cpp/protocol.h>
#include <lib/fdio/cpp/caller.h>
#include <lib/fdio/directory.h>
#include <lib/fdio/fd.h>
#include <lib/fdio/fdio.h>
#include <lib/fdio/limits.h>
#include <lib/fdio/namespace.h>
#include <lib/fdio/vfs.h>
#include <lib/fit/defer.h>
#include <lib/zx/channel.h>
#include <lib/zx/result.h>
#include <string.h>
#include <unistd.h>
#include <zircon/compiler.h>
#include <zircon/errors.h>
#include <zircon/processargs.h>
#include <zircon/syscalls.h>
#include <iostream>
#include <memory>
#include <utility>
#include <fbl/algorithm.h>
#include <fbl/alloc_checker.h>
#include <fbl/vector.h>
#include <pretty/hexdump.h>
#include "fidl/fuchsia.io/cpp/markers.h"
#include "src/storage/lib/fs_management/cpp/admin.h"
#include "src/storage/lib/fs_management/cpp/component.h"
#include "src/storage/lib/fs_management/cpp/format.h"
#include "src/storage/lib/fs_management/cpp/options.h"
#include "src/storage/lib/fs_management/cpp/volumes.h"
namespace fs_management {
namespace {
using Directory = fuchsia_io::Directory;
zx::result<> StartFsComponent(fidl::UnownedClientEnd<Directory> exposed_dir,
fidl::ClientEnd<fuchsia_hardware_block::Block> device,
const MountOptions& options) {
auto startup_client_end = component::ConnectAt<fuchsia_fs_startup::Startup>(exposed_dir);
if (startup_client_end.is_error())
return startup_client_end.take_error();
fidl::WireSyncClient startup_client{std::move(*startup_client_end)};
auto start_options_or = options.as_start_options();
if (start_options_or.is_error())
return start_options_or.take_error();
auto res = startup_client->Start(std::move(device), std::move(*start_options_or));
if (!res.ok())
return zx::error(res.status());
if (res->is_error())
return zx::error(res->error_value());
return zx::ok();
}
zx::result<fidl::ClientEnd<Directory>> InitFsComponent(
fidl::ClientEnd<fuchsia_hardware_block::Block> device, FsComponent& component,
const MountOptions& options) {
auto exposed_dir = component.Connect();
if (exposed_dir.is_error())
return exposed_dir.take_error();
if (zx::result<> start_status = StartFsComponent(*exposed_dir, std::move(device), options);
start_status.is_error()) {
return start_status.take_error();
}
return exposed_dir;
}
std::string StripTrailingSlash(const char* in) {
if (!in)
return std::string();
std::string_view view(in, strlen(in));
if (!view.empty() && view.back() == '/') {
return std::string(view.substr(0, view.length() - 1));
}
return std::string(view);
}
} // namespace
__EXPORT zx::result<NamespaceBinding> NamespaceBinding::Create(
const char* path, fidl::ClientEnd<fuchsia_io::Directory> dir) {
auto stripped_path = StripTrailingSlash(path);
if (stripped_path.empty()) {
return zx::ok(NamespaceBinding());
}
fdio_ns_t* ns;
if (zx_status_t status = fdio_ns_get_installed(&ns); status != ZX_OK)
return zx::error(status);
if (zx_status_t status = fdio_ns_bind(ns, stripped_path.c_str(), dir.TakeHandle().release());
status != ZX_OK)
return zx::error(status);
return zx::ok(NamespaceBinding(std::move(stripped_path)));
}
__EXPORT void NamespaceBinding::Reset() {
if (!path_.empty()) {
fdio_ns_t* ns;
if (fdio_ns_get_installed(&ns) == ZX_OK)
fdio_ns_unbind(ns, path_.c_str());
path_.clear();
}
}
__EXPORT NamespaceBinding::~NamespaceBinding() { Reset(); }
__EXPORT
StartedSingleVolumeFilesystem::~StartedSingleVolumeFilesystem() {
[[maybe_unused]] auto res = Unmount();
}
__EXPORT
fidl::ClientEnd<fuchsia_io::Directory> StartedSingleVolumeFilesystem::Release() {
return fidl::ClientEnd<fuchsia_io::Directory>(export_root_.TakeChannel());
}
__EXPORT
zx::result<> StartedSingleVolumeFilesystem::Unmount() {
auto res = Shutdown(ExportRoot());
export_root_.reset();
return res;
}
__EXPORT
zx::result<fidl::ClientEnd<fuchsia_io::Directory>> StartedSingleVolumeFilesystem::DataRoot() const {
return FsRootHandle(export_root_);
}
__EXPORT
fidl::ClientEnd<fuchsia_io::Directory> MountedVolume::Release() {
return fidl::ClientEnd<fuchsia_io::Directory>(export_root_.TakeChannel());
}
__EXPORT
zx::result<fidl::ClientEnd<fuchsia_io::Directory>> MountedVolume::DataRoot() const {
return FsRootHandle(export_root_);
}
__EXPORT
StartedMultiVolumeFilesystem::~StartedMultiVolumeFilesystem() {
[[maybe_unused]] auto res = Unmount();
}
__EXPORT
std::pair<fidl::ClientEnd<fuchsia_io::Directory>,
std::map<std::string, fidl::ClientEnd<fuchsia_io::Directory>>>
StartedMultiVolumeFilesystem::Release() {
std::map<std::string, fidl::ClientEnd<fuchsia_io::Directory>> volumes;
for (auto&& [k, v] : std::move(volumes_)) {
volumes.insert(std::make_pair(k, v.Release()));
}
return std::make_pair(fidl::ClientEnd<fuchsia_io::Directory>(exposed_dir_.TakeChannel()),
std::move(volumes));
}
__EXPORT
zx::result<> StartedMultiVolumeFilesystem::Unmount() {
volumes_.clear();
auto res = Shutdown(exposed_dir_);
exposed_dir_.reset();
return res;
}
__EXPORT
zx::result<MountedVolume*> StartedMultiVolumeFilesystem::OpenVolume(
std::string_view name, fuchsia_fxfs::wire::MountOptions options) {
if (volumes_.find(name) != volumes_.end()) {
return zx::error(ZX_ERR_ALREADY_BOUND);
}
auto endpoints_or = fidl::CreateEndpoints<fuchsia_io::Directory>();
if (endpoints_or.is_error())
return endpoints_or.take_error();
auto [client, server] = std::move(*endpoints_or);
auto res = fs_management::OpenVolume(exposed_dir_, name, std::move(server), std::move(options));
if (res.is_error()) {
return res.take_error();
}
auto [iter, inserted] = volumes_.emplace(std::string(name), MountedVolume(std::move(client)));
return zx::ok(&iter->second);
}
__EXPORT
zx::result<MountedVolume*> StartedMultiVolumeFilesystem::CreateVolume(
std::string_view name, fuchsia_fxfs::wire::MountOptions options) {
if (volumes_.find(name) != volumes_.end()) {
return zx::error(ZX_ERR_ALREADY_BOUND);
}
auto endpoints_or = fidl::CreateEndpoints<fuchsia_io::Directory>();
if (endpoints_or.is_error())
return endpoints_or.take_error();
auto [client, server] = std::move(*endpoints_or);
auto res = fs_management::CreateVolume(exposed_dir_, name, std::move(server), std::move(options));
if (res.is_error()) {
return res.take_error();
}
auto [iter, inserted] = volumes_.emplace(std::string(name), MountedVolume(std::move(client)));
return zx::ok(&iter->second);
}
__EXPORT zx::result<> StartedMultiVolumeFilesystem::CheckVolume(std::string_view volume_name,
zx::channel crypt_client) {
return fs_management::CheckVolume(exposed_dir_, volume_name, std::move(crypt_client));
}
__EXPORT bool StartedMultiVolumeFilesystem::HasVolume(std::string_view volume_name) {
if (volumes_.find(volume_name) != volumes_.end()) {
return true;
}
return fs_management::HasVolume(exposed_dir_, volume_name);
}
__EXPORT
StartedSingleVolumeMultiVolumeFilesystem::~StartedSingleVolumeMultiVolumeFilesystem() {
[[maybe_unused]] auto res = Unmount();
}
__EXPORT
fidl::ClientEnd<fuchsia_io::Directory> StartedSingleVolumeMultiVolumeFilesystem::Release() {
volume_.reset();
return fidl::ClientEnd<fuchsia_io::Directory>(exposed_dir_.TakeChannel());
}
__EXPORT
zx::result<> StartedSingleVolumeMultiVolumeFilesystem::Unmount() {
volume_.reset();
auto res = Shutdown(exposed_dir_);
exposed_dir_.reset();
return res;
}
__EXPORT SingleVolumeFilesystemInterface::~SingleVolumeFilesystemInterface() = default;
__EXPORT
zx::result<StartedSingleVolumeFilesystem> Mount(
fidl::ClientEnd<fuchsia_hardware_block::Block> device, FsComponent& component,
const MountOptions& options) {
if (component.is_multi_volume()) {
return zx::error(ZX_ERR_NOT_SUPPORTED);
}
StartedSingleVolumeFilesystem fs;
auto exposed_dir = InitFsComponent(std::move(device), component, options);
if (exposed_dir.is_error()) {
return exposed_dir.take_error();
}
return zx::ok(StartedSingleVolumeFilesystem(std::move(*exposed_dir)));
}
__EXPORT
zx::result<StartedMultiVolumeFilesystem> MountMultiVolume(
fidl::ClientEnd<fuchsia_hardware_block::Block> device, FsComponent& component,
const MountOptions& options) {
if (!component.is_multi_volume()) {
return zx::error(ZX_ERR_NOT_SUPPORTED);
}
auto outgoing_dir = InitFsComponent(std::move(device), component, options);
if (outgoing_dir.is_error()) {
return outgoing_dir.take_error();
}
return zx::ok(StartedMultiVolumeFilesystem(std::move(*outgoing_dir)));
}
__EXPORT
zx::result<StartedSingleVolumeMultiVolumeFilesystem> MountMultiVolumeWithDefault(
fidl::ClientEnd<fuchsia_hardware_block::Block> device, FsComponent& component,
const MountOptions& options, const char* volume_name) {
if (!component.is_multi_volume()) {
return zx::error(ZX_ERR_NOT_SUPPORTED);
}
auto outgoing_dir_or = InitFsComponent(std::move(device), component, options);
if (outgoing_dir_or.is_error()) {
return outgoing_dir_or.take_error();
}
auto endpoints = fidl::CreateEndpoints<fuchsia_io::Directory>();
if (endpoints.is_error()) {
return endpoints.take_error();
}
auto [client, server] = std::move(*endpoints);
auto volume = OpenVolume(
*outgoing_dir_or, volume_name, std::move(server),
options.crypt_client
? fuchsia_fxfs::wire::MountOptions{.crypt = fidl::ClientEnd<fuchsia_fxfs::Crypt>(
options.crypt_client())}
: fuchsia_fxfs::wire::MountOptions());
if (volume.is_error()) {
std::cerr << "Volume status " << volume.status_string() << std::endl;
return volume.take_error();
}
return zx::ok(StartedSingleVolumeMultiVolumeFilesystem(std::move(*outgoing_dir_or),
MountedVolume(std::move(client))));
}
__EXPORT
zx::result<> Shutdown(fidl::UnownedClientEnd<Directory> svc_dir) {
auto admin_or = component::ConnectAt<fuchsia_fs::Admin>(svc_dir);
if (admin_or.is_error()) {
return admin_or.take_error();
}
auto resp = fidl::WireCall(*admin_or)->Shutdown();
if (resp.status() != ZX_OK)
return zx::error(resp.status());
return zx::ok();
}
} // namespace fs_management