blob: 62ea3094acce1c6a2df3e7b43867c2118a941681 [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 <fuchsia/fshost/llcpp/fidl.h>
#include <fuchsia/io/llcpp/fidl.h>
#include <lib/fdio/directory.h>
#include <lib/fdio/vfs.h>
#include <lib/zx/channel.h>
#include <zircon/processargs.h>
#include <zircon/syscalls.h>
#include <array>
#include <fbl/auto_call.h>
#include <fbl/vector.h>
#include <fs-management/admin.h>
#include <fs/vfs.h>
namespace fio = ::llcpp::fuchsia::io;
namespace fshost = ::llcpp::fuchsia::fshost;
namespace {
void UnmountHandle(zx_handle_t export_root, bool wait_until_ready) {
zx::channel root;
fs_root_handle(export_root, root.reset_and_get_address());
fs::Vfs::UnmountHandle(std::move(root), wait_until_ready ? zx::time::infinite() : zx::time(0));
}
zx_status_t InitNativeFs(const char* binary, zx::channel device, const init_options_t& options,
zx_handle_t* out_export_root) {
zx_status_t status;
constexpr size_t kNumHandles = 2;
zx::channel export_client, export_server;
if ((status = zx::channel::create(0, &export_client, &export_server)) != ZX_OK) {
return status;
}
std::array<zx_handle_t, kNumHandles> handles = {device.release(), export_server.release()};
std::array<uint32_t, kNumHandles> ids = {FS_HANDLE_BLOCK_DEVICE_ID, PA_DIRECTORY_REQUEST};
std::vector<const char*> argv;
argv.push_back(binary);
if (options.readonly) {
argv.push_back("--readonly");
}
if (options.verbose_mount) {
argv.push_back("--verbose");
}
if (options.collect_metrics) {
argv.push_back("--metrics");
}
if (options.enable_journal) {
argv.push_back("--journal");
}
if (options.enable_pager) {
argv.push_back("--pager");
}
if (options.write_compression_algorithm) {
argv.push_back("--compression");
argv.push_back(options.write_compression_algorithm);
}
if (options.fsck_after_every_transaction) {
argv.push_back("--fsck_after_every_transaction");
}
argv.push_back("mount");
argv.push_back(nullptr);
int argc = static_cast<int>(argv.size() - 1);
auto cleanup = fbl::MakeAutoCall([&export_client, options]() {
UnmountHandle(export_client.get(), options.wait_until_ready);
});
if ((status = options.callback(argc, argv.data(), handles.data(), ids.data(), kNumHandles)) !=
ZX_OK) {
return status;
}
if (options.wait_until_ready) {
// Wait until the filesystem is ready to take incoming requests
zx_signals_t observed;
zx_signals_t signals = ZX_USER_SIGNAL_0 // the filesystem is initialized and serving requests
| ZX_CHANNEL_PEER_CLOSED; // filesystem closed the channel on error
status = export_client.wait_one(signals, zx::time::infinite(), &observed);
if (status != ZX_OK) {
return status;
}
if (observed & ZX_CHANNEL_PEER_CLOSED) {
return ZX_ERR_BAD_STATE;
}
}
cleanup.cancel();
*out_export_root = export_client.release();
return ZX_OK;
}
} // namespace
const init_options_t default_init_options = {
.readonly = false,
.verbose_mount = false,
.collect_metrics = false,
.wait_until_ready = true,
.enable_journal = true,
.enable_pager = false,
.write_compression_algorithm = nullptr,
.fsck_after_every_transaction = false,
.callback = launch_stdio_async,
};
__EXPORT
zx_status_t fs_init(zx_handle_t device_handle, disk_format_t df, const init_options_t* options,
zx_handle_t* out_export_root) {
zx::channel device(device_handle);
switch (df) {
case DISK_FORMAT_MINFS:
return InitNativeFs("/boot/bin/minfs", std::move(device), *options, out_export_root);
case DISK_FORMAT_BLOBFS:
return InitNativeFs("/boot/bin/blobfs", std::move(device), *options, out_export_root);
default:
return ZX_ERR_NOT_SUPPORTED;
}
}
__EXPORT
zx_status_t fs_register(zx_handle_t export_root) {
zx_status_t status;
zx::channel export_client, export_server;
if ((status = zx::channel::create(0, &export_client, &export_server)) != ZX_OK) {
return status;
}
auto clone_resp = fio::Node::Call::Clone(zx::unowned_channel(export_root),
fio::CLONE_FLAG_SAME_RIGHTS, std::move(export_server));
if (!clone_resp.ok()) {
return clone_resp.status();
}
zx::channel registry_client_chan, registry_server;
if ((status = zx::channel::create(0, &registry_client_chan, &registry_server)) != ZX_OK) {
return status;
}
std::string path = std::string("/svc/") + fshost::Registry::Name;
if ((status = fdio_service_connect(path.c_str(), registry_server.release())) != ZX_OK) {
return status;
}
fshost::Registry::SyncClient registry_client(std::move(registry_client_chan));
auto register_resp = registry_client.RegisterFilesystem(std::move(export_client));
if (!register_resp.ok()) {
return register_resp.status();
}
if (register_resp.value().s != ZX_OK) {
return register_resp.value().s;
}
return ZX_OK;
}
__EXPORT
zx_status_t fs_root_handle(zx_handle_t export_root, zx_handle_t* out_root) {
zx_status_t status;
zx::channel root_client, root_server;
if ((status = zx::channel::create(0, &root_client, &root_server)) != ZX_OK) {
return status;
}
// The POSIX flag here requests that the old connection rights be inherited by the new connection.
// This specifically ensures that WRITABLE connections continue to have the WRITABLE right, while
// read-only connections do not.
uint32_t flags = fio::OPEN_RIGHT_READABLE | fio::OPEN_FLAG_POSIX | fio::OPEN_RIGHT_ADMIN;
auto resp = fio::Directory::Call::Open(zx::unowned_channel(export_root), flags, 0,
fidl::StringView("root"), std::move(root_server));
if (!resp.ok()) {
return resp.status();
}
*out_root = root_client.release();
return ZX_OK;
}