blob: 1216dfd77d27957296af6a256575b5c4cd69bced [file] [log] [blame]
// Copyright 2017 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 "filesystem-mounter.h"
#include <fuchsia/io/llcpp/fidl.h>
#include <fuchsia/update/verify/llcpp/fidl.h>
#include <lib/fdio/directory.h>
#include <lib/inspect/service/cpp/service.h>
#include <lib/syslog/cpp/macros.h>
#include <lib/zx/process.h>
#include <zircon/status.h>
#include <fbl/ref_ptr.h>
#include "fdio.h"
#include "fshost-fs-provider.h"
#include "pkgfs-launcher.h"
#include "src/storage/blobfs/mount.h"
#include "src/storage/minfs/minfs.h"
namespace devmgr {
namespace fio = llcpp::fuchsia::io;
zx_status_t FilesystemMounter::LaunchFs(int argc, const char** argv, zx_handle_t* hnd,
uint32_t* ids, size_t len, uint32_t fs_flags) {
FshostFsProvider fs_provider;
DevmgrLauncher launcher(&fs_provider);
return launcher.Launch(*zx::job::default_job(), argv[0], argv, nullptr, -1,
/* TODO(fxbug.dev/32044) */ zx::resource(), hnd, ids, len, nullptr,
fs_flags);
}
zx::status<zx::channel> FilesystemMounter::MountFilesystem(FsManager::MountPoint point,
const char* binary,
const mount_options_t& options,
zx::channel block_device_client,
uint32_t fs_flags) {
zx::channel client, server;
zx_status_t status = zx::channel::create(0, &client, &server);
if (status != ZX_OK) {
return zx::error(status);
}
size_t num_handles = 2;
zx_handle_t handles[2] = {server.release(), block_device_client.release()};
uint32_t ids[2] = {PA_DIRECTORY_REQUEST, FS_HANDLE_BLOCK_DEVICE_ID};
fbl::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.write_compression_algorithm != nullptr) {
argv.push_back("--compression");
argv.push_back(options.write_compression_algorithm);
}
if (options.sandbox_decompression) {
argv.push_back("--sandbox_decompression");
}
if (options.cache_eviction_policy != nullptr) {
argv.push_back("--eviction_policy");
argv.push_back(options.cache_eviction_policy);
}
argv.push_back("mount");
argv.push_back(nullptr);
status =
LaunchFs(static_cast<int>(argv.size() - 1), argv.data(), handles, ids, num_handles, fs_flags);
if (status != ZX_OK) {
return zx::error(status);
}
zx_signals_t observed = 0;
status =
client.wait_one(ZX_USER_SIGNAL_0 | ZX_CHANNEL_PEER_CLOSED, zx::time::infinite(), &observed);
if ((status != ZX_OK) || (observed & ZX_CHANNEL_PEER_CLOSED)) {
status = (status != ZX_OK) ? status : ZX_ERR_BAD_STATE;
return zx::error(status);
}
zx::channel root;
status = fs_root_handle(client.get(), root.reset_and_get_address());
if (status != ZX_OK) {
return zx::error(status);
}
status = InstallFs(point, std::move(root));
if (status != ZX_OK) {
return zx::error(status);
}
return zx::ok(std::move(client));
}
zx_status_t FilesystemMounter::MountData(zx::channel block_device, const mount_options_t& options) {
if (data_mounted_) {
return ZX_ERR_ALREADY_BOUND;
}
zx::status ret = MountFilesystem(FsManager::MountPoint::kData, "/pkg/bin/minfs", options,
std::move(block_device), FS_SVC);
if (ret.is_error()) {
return ret.error_value();
}
data_mounted_ = true;
return ZX_OK;
}
zx_status_t FilesystemMounter::MountInstall(zx::channel block_device,
const mount_options_t& options) {
if (install_mounted_) {
return ZX_ERR_ALREADY_BOUND;
}
zx::status ret = MountFilesystem(FsManager::MountPoint::kInstall, "/pkg/bin/minfs", options,
std::move(block_device), FS_SVC);
if (ret.is_error()) {
return ret.error_value();
}
install_mounted_ = true;
return ZX_OK;
}
zx_status_t FilesystemMounter::MountFactoryFs(zx::channel block_device,
const mount_options_t& options) {
if (factory_mounted_) {
return ZX_ERR_ALREADY_BOUND;
}
zx::status ret = MountFilesystem(FsManager::MountPoint::kFactory, "/pkg/bin/factoryfs", options,
std::move(block_device), FS_SVC);
if (ret.is_error()) {
return ret.error_value();
}
factory_mounted_ = true;
return ZX_OK;
}
zx_status_t FilesystemMounter::MountDurable(zx::channel block_device,
const mount_options_t& options) {
if (durable_mounted_) {
return ZX_ERR_ALREADY_BOUND;
}
zx::status ret = MountFilesystem(FsManager::MountPoint::kDurable, "/pkg/bin/minfs", options,
std::move(block_device), FS_SVC);
if (ret.is_error()) {
return ret.error_value();
}
durable_mounted_ = true;
return ZX_OK;
}
zx_status_t FilesystemMounter::MountBlob(zx::channel block_device, const mount_options_t& options) {
if (blob_mounted_) {
return ZX_ERR_ALREADY_BOUND;
}
zx::channel fs_diagnostics_dir_client, fs_diagnostics_dir_server;
zx_status_t status =
zx::channel::create(0, &fs_diagnostics_dir_client, &fs_diagnostics_dir_server);
if (status != ZX_OK) {
FX_LOGS(ERROR) << "failed to create channel for diagnostics dir: "
<< zx_status_get_string(status);
return status;
}
zx::status ret = MountFilesystem(FsManager::MountPoint::kBlob, "/pkg/bin/blobfs", options,
std::move(block_device), FS_SVC | FS_SVC_BLOBFS);
if (ret.is_error()) {
return ret.error_value();
}
status = fshost_.SetFsExportRoot(FsManager::MountPoint::kBlob, std::move(ret).value());
if (status != ZX_OK) {
return status;
}
status = fshost_.ForwardFsDiagnosticsDirectory(FsManager::MountPoint::kBlob, "blobfs");
if (status != ZX_OK) {
FX_LOGS(ERROR) << "failed to add diagnostic directory for blobfs: "
<< zx_status_get_string(status);
}
status = fshost_.ForwardFsService(FsManager::MountPoint::kBlob,
llcpp::fuchsia::update::verify::BlobfsVerifier::Name);
if (status != ZX_OK) {
FX_LOGS(ERROR) << "failed to forward BlobfsVerifier service for blobfs: "
<< zx_status_get_string(status);
}
blob_mounted_ = true;
return ZX_OK;
}
void FilesystemMounter::TryMountPkgfs() {
// Pkgfs waits for the following to mount before initializing:
// - Blobfs. Pkgfs is launched from blobfs, so this is a hard requirement.
// - Minfs. Pkgfs and other components want minfs to exist, so although they
// could launch and query for it later, this synchronization point means that
// subsequent clients will no longer need to query.
//
// TODO(fxbug.dev/38621): In the future, this mechanism may be replaced with a feed-forward
// design to the mounted filesystems.
if (!pkgfs_mounted_ && blob_mounted_ && (data_mounted_ || !WaitForData())) {
// Historically we don't retry if pkgfs fails to launch, which seems reasonable since the
// cause of a launch failure is unlikely to be transient.
// TODO(fxbug.dev/58363): fshost should handle failures to mount critical filesystems better.
auto status = LaunchPkgfs(this);
if (status.is_error()) {
FX_LOGS(ERROR) << "failed to launch pkgfs: " << status.status_string();
}
pkgfs_mounted_ = true;
}
}
} // namespace devmgr