blob: 6e90649c5ee2e46fc9d2f875e18d928d4658423a [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/fshost/admin-server.h"
#include <fidl/fuchsia.device/cpp/wire.h>
#include <lib/async/default.h>
#include <lib/fdio/fd.h>
#include <lib/fidl-async/cpp/bind.h>
#include <lib/syslog/cpp/macros.h>
#include "src/lib/storage/fs_management/cpp/mount.h"
#include "src/lib/storage/vfs/cpp/service.h"
#include "src/storage/fshost/filesystem-mounter.h"
namespace fshost {
fbl::RefPtr<fs::Service> AdminServer::Create(FsManager* fs_manager, async_dispatcher* dispatcher) {
return fbl::MakeRefCounted<fs::Service>(
[dispatcher, fs_manager](fidl::ServerEnd<fuchsia_fshost::Admin> chan) {
zx_status_t status = fidl::BindSingleInFlightOnly(
dispatcher, std::move(chan), std::make_unique<AdminServer>(fs_manager));
if (status != ZX_OK) {
FX_LOGS(ERROR) << "failed to bind admin service: " << zx_status_get_string(status);
return status;
}
return ZX_OK;
});
}
void AdminServer::Shutdown(ShutdownRequestView request, ShutdownCompleter::Sync& completer) {
FX_LOGS(INFO) << "received shutdown command over admin interface";
fs_manager_->Shutdown([completer = completer.ToAsync()](zx_status_t status) mutable {
if (status != ZX_OK) {
FX_LOGS(ERROR) << "filesystem shutdown failed: " << zx_status_get_string(status);
completer.Close(status);
} else {
FX_LOGS(INFO) << "shutdown complete";
completer.Reply();
}
});
}
void AdminServer::Mount(MountRequestView request, MountCompleter::Sync& completer) {
std::string device_path;
if (auto result = fidl::WireCall(fidl::UnownedClientEnd<fuchsia_device::Controller>(
request->device.channel().borrow()))
->GetTopologicalPath();
result.status() != ZX_OK) {
FX_LOGS(WARNING) << "Unable to get device topological path (FIDL error): "
<< zx_status_get_string(result.status());
} else if (result->is_error()) {
FX_LOGS(WARNING) << "Unable to get device topological path: "
<< zx_status_get_string(result->error_value());
} else {
device_path = result->value()->path.get();
}
fbl::unique_fd fd;
if (zx_status_t status =
fdio_fd_create(request->device.TakeChannel().release(), fd.reset_and_get_address());
status != ZX_OK) {
completer.ReplyError(status);
return;
}
fs_management::DiskFormat df = fs_management::DetectDiskFormat(fd.get());
const std::string name(request->name.get());
const auto& o = request->options;
std::string compression_algorithm;
if (o.has_write_compression_algorithm())
compression_algorithm = o.write_compression_algorithm().get();
fs_management::MountOptions options = {
.readonly = o.has_read_only() && o.read_only(),
.verbose_mount = o.has_verbose() && o.verbose(),
.write_compression_algorithm =
o.has_write_compression_algorithm() ? compression_algorithm.c_str() : nullptr,
};
FX_LOGS(INFO) << "Mounting " << fs_management::DiskFormatString(df) << " filesystem at /mnt/"
<< name;
async_dispatcher_t* dispatcher = async_get_default_dispatcher();
// Launching a filesystem requirees accessing the loader which is running on the same async loop
// that we're running on but it's only running on one thread, so if we're not careful, we'll end
// up with a deadlock. To avoid this, spawn a separate thread. Unfortunately, this isn't
// thread-safe if we're shutting down, but since mounting is a debug only thing for now, we don't
// worry about it.
std::thread thread([device_path = std::move(device_path), name = std::move(name),
completer = completer.ToAsync(), options = std::move(options),
fd = std::move(fd), df = std::move(df), fs_manager = fs_manager_,
dispatcher]() mutable {
static int mount_index = 0;
std::string component_child_name = name + "." + std::to_string(mount_index++);
options.component_child_name = component_child_name.c_str();
options.component_collection_name = "fs-collection";
auto mounted_filesystem_or =
fs_management::Mount(std::move(fd), nullptr, df, options, fs_management::LaunchLogsAsync);
if (mounted_filesystem_or.is_error()) {
FX_LOGS(WARNING) << "Mount failed: " << mounted_filesystem_or.status_string();
completer.ReplyError(mounted_filesystem_or.error_value());
return;
}
// fs_manager isn't thread-safe, so we have to post back on to the async loop to attach the
// mount.
async::PostTask(
dispatcher,
[device_path = std::move(device_path),
mounted_filesystem = std::move(*mounted_filesystem_or).TakeExportRoot(),
name = std::move(name), fs_manager, completer = std::move(completer)]() mutable {
if (zx_status_t status =
fs_manager->AttachMount(device_path, std::move(mounted_filesystem), name);
status != ZX_OK) {
FX_LOGS(WARNING) << "Failed to attach mount: " << zx_status_get_string(status);
completer.ReplyError(status);
return;
}
completer.ReplySuccess();
});
});
thread.detach();
}
void AdminServer::Unmount(UnmountRequestView request, UnmountCompleter::Sync& completer) {
FX_LOGS(INFO) << "Unmounting " << request->name.get();
if (zx_status_t status = fs_manager_->DetachMount(request->name.get()); status != ZX_OK) {
FX_LOGS(WARNING) << "Failed to unmount: " << zx_status_get_string(status);
completer.ReplyError(status);
} else {
completer.ReplySuccess();
}
}
void AdminServer::GetDevicePath(GetDevicePathRequestView request,
GetDevicePathCompleter::Sync& completer) {
if (auto device_path_or = fs_manager_->GetDevicePath(request->fs_id); device_path_or.is_error()) {
completer.ReplyError(device_path_or.status_value());
} else {
completer.ReplySuccess(fidl::StringView::FromExternal(*device_path_or));
}
}
} // namespace fshost