blob: 4f384d325dcae955fb4b9457962ca94b4ebf0b91 [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 <dirent.h>
#include <fidl/fuchsia.device/cpp/wire.h>
#include <fidl/fuchsia.fxfs/cpp/wire_types.h>
#include <fidl/fuchsia.io/cpp/wire.h>
#include <fidl/fuchsia.io/cpp/wire_types.h>
#include <fidl/fuchsia.update.verify/cpp/wire.h>
#include <lib/fdio/directory.h>
#include <lib/fidl/llcpp/vector_view.h>
#include <lib/inspect/service/cpp/service.h>
#include <lib/service/llcpp/service.h>
#include <lib/syslog/cpp/macros.h>
#include <lib/zx/process.h>
#include <sys/stat.h>
#include <zircon/status.h>
#include <fbl/ref_ptr.h>
#include "constants.h"
#include "fdio.h"
#include "src/storage/blobfs/mount.h"
#include "src/storage/minfs/minfs.h"
namespace fshost {
namespace fio = fuchsia_io;
constexpr unsigned char kInsecureCryptDataKey[32] = {
0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
};
constexpr unsigned char kInsecureCryptMetadataKey[32] = {
0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0,
0xef, 0xee, 0xed, 0xec, 0xeb, 0xea, 0xe9, 0xe8, 0xe7, 0xe6, 0xe5, 0xe4, 0xe3, 0xe2, 0xe1, 0xe0,
};
zx::status<> FilesystemMounter::LaunchFsComponent(zx::channel block_device,
fuchsia_fs_startup::wire::StartOptions options,
const std::string& partition_name) {
std::string startup_service_path =
std::string("/") + partition_name + "/fuchsia.fs.startup.Startup";
auto startup_client_end =
service::Connect<fuchsia_fs_startup::Startup>(startup_service_path.c_str());
if (startup_client_end.is_error()) {
FX_LOGS(ERROR) << "failed to connect to startup service at " << startup_service_path << ": "
<< startup_client_end.status_string();
return startup_client_end.take_error();
}
auto startup_client = fidl::BindSyncClient(std::move(*startup_client_end));
fidl::ClientEnd<fuchsia_hardware_block::Block> block_client_end(std::move(block_device));
auto startup_res = startup_client->Start(std::move(block_client_end), std::move(options));
if (!startup_res.ok()) {
FX_LOGS(ERROR) << "failed to start through startup service at " << startup_service_path << ": "
<< startup_res.status_string();
return zx::make_status(startup_res.status());
}
FX_LOGS(INFO) << "successfully mounted " << partition_name;
return zx::ok();
}
zx_status_t FilesystemMounter::LaunchFs(int argc, const char** argv, zx_handle_t* hnd,
uint32_t* ids, size_t len) {
return Launch(*zx::job::default_job(), argv[0], argv, nullptr, -1,
/* TODO(fxbug.dev/32044) */ zx::resource(), hnd, ids, len, nullptr);
}
std::string FilesystemMounter::GetDevicePath(const zx::channel& block_device) {
std::string device_path;
if (auto result =
fidl::WireCall(fidl::UnownedClientEnd<fuchsia_device::Controller>(block_device.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();
}
return device_path;
}
zx::status<> FilesystemMounter::MountFilesystem(FsManager::MountPoint point, const char* binary,
const fs_management::MountOptions& options,
zx::channel block_device_client,
fidl::ClientEnd<fuchsia_fxfs::Crypt> crypt_client) {
FX_LOGS(INFO) << "FilesystemMounter::MountFilesystem(" << binary << ")";
std::string device_path = GetDevicePath(block_device_client);
FX_LOGS(INFO) << "Mounting device " << device_path << " with " << binary << " at "
<< FsManager::MountPointPath(point);
std::optional endpoints_or = fshost_.TakeMountPointServerEnd(point);
if (!endpoints_or.has_value()) {
return zx::error(ZX_ERR_BAD_STATE);
}
auto [export_root, server_end] = std::move(endpoints_or.value());
size_t num_handles = crypt_client ? 3 : 2;
zx_handle_t handles[] = {server_end.TakeChannel().release(), block_device_client.release(),
crypt_client.TakeChannel().release()};
uint32_t ids[] = {PA_DIRECTORY_REQUEST, FS_HANDLE_BLOCK_DEVICE_ID, PA_HND(PA_USER0, 2)};
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.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);
if (zx_status_t status =
LaunchFs(static_cast<int>(argv.size() - 1), argv.data(), handles, ids, num_handles);
status != ZX_OK) {
return zx::error(status);
}
auto result = fidl::WireCall(export_root)->Describe();
if (!result.ok()) {
return zx::error(result.status());
}
fshost_.RegisterDevicePath(point, device_path);
return zx::ok();
}
zx_status_t FilesystemMounter::MountData(zx::channel block_device,
const fs_management::MountOptions& options,
fs_management::DiskFormat format) {
if (data_mounted_) {
return ZX_ERR_ALREADY_BOUND;
}
if (format == fs_management::kDiskFormatF2fs) {
std::string binary_path(BinaryPathForFormat(format));
if (binary_path.empty()) {
FX_LOGS(ERROR) << "Unsupported format: " << DiskFormatString(format);
return ZX_ERR_INVALID_ARGS;
}
if (auto result = MountFilesystem(FsManager::MountPoint::kData, binary_path.c_str(), options,
std::move(block_device), {});
result.is_error()) {
return result.error_value();
}
if (zx_status_t status = fshost_.ForwardFsDiagnosticsDirectory(
FsManager::MountPoint::kData, fs_management::DiskFormatString(format));
status != ZX_OK) {
FX_PLOGS(ERROR, status) << "failed to add diagnostic directory for "
<< fs_management::DiskFormatString(format);
}
} else {
std::string device_path = GetDevicePath(block_device);
auto fidl_options = options.as_start_options();
if (format == fs_management::kDiskFormatFxfs) {
// TODO(fxbug.dev/99591): Statically route the crypt service.
auto crypt_client_or = service::Connect<fuchsia_fxfs::Crypt>();
if (crypt_client_or.is_error()) {
return crypt_client_or.error_value();
}
fidl_options->crypt = std::move(crypt_client_or).value();
}
if (zx::status<> status =
LaunchFsComponent(std::move(block_device), *std::move(fidl_options), "data");
status.is_error()) {
return status.error_value();
}
fshost_.RegisterDevicePath(FsManager::MountPoint::kData, device_path);
}
// Obtain data root used for serving disk usage statistics. Must be valid otherwise the lazy node
// serving the statistics will hang indefinitely. If we fail to get the data root, just log
// loudly, but don't fail. Serving stats is best-effort and shouldn't cause mounting to fail.
auto data_root_or = manager().GetRoot(FsManager::MountPoint::kData);
if (data_root_or.is_ok()) {
inspect_manager().ServeStats("data", std::move(data_root_or.value()));
} else {
FX_LOGS(WARNING) << "failed to get data root to serve inspect stats. Assuming test environment "
"and continuing.";
}
data_mounted_ = true;
return ZX_OK;
}
zx_status_t FilesystemMounter::MountFactoryFs(zx::channel block_device,
const fs_management::MountOptions& options) {
if (factory_mounted_) {
return ZX_ERR_ALREADY_BOUND;
}
if (auto result = MountFilesystem(FsManager::MountPoint::kFactory, kFactoryfsPath, options,
std::move(block_device));
result.is_error()) {
return result.error_value();
}
factory_mounted_ = true;
return ZX_OK;
}
zx_status_t FilesystemMounter::MountDurable(zx::channel block_device,
const fs_management::MountOptions& options) {
if (durable_mounted_) {
return ZX_ERR_ALREADY_BOUND;
}
if (auto result = MountFilesystem(FsManager::MountPoint::kDurable, kMinfsPath, options,
std::move(block_device));
result.is_error()) {
return result.error_value();
}
durable_mounted_ = true;
return ZX_OK;
}
zx_status_t FilesystemMounter::MountBlob(zx::channel block_device,
fuchsia_fs_startup::wire::StartOptions options) {
if (blob_mounted_) {
return ZX_ERR_ALREADY_BOUND;
}
zx::status ret = LaunchFsComponent(std::move(block_device), std::move(options), "blobfs");
if (ret.is_error()) {
return ret.error_value();
}
blob_mounted_ = true;
return ZX_OK;
}
void FilesystemMounter::ReportPartitionCorrupted(fs_management::DiskFormat format) {
fshost_.inspect_manager().LogCorruption(format);
// Currently the only reason we report a partition as being corrupt is if it fails fsck.
// This may need to change in the future should we want to file synthetic crash reports for
// other possible failure modes.
fshost_.FileReport(format, FsManager::ReportReason::kFsckFailure);
}
zx::status<> FilesystemMounter::MaybeInitCryptClient() {
if (config_.data_filesystem_format() != "fxfs") {
FX_LOGS(INFO) << "Not initializing Crypt client due to configuration";
return zx::ok();
}
FX_LOGS(INFO) << "Initializing Crypt client";
auto management_endpoints_or = fidl::CreateEndpoints<fuchsia_fxfs::CryptManagement>();
if (management_endpoints_or.is_error())
return zx::error(management_endpoints_or.status_value());
if (zx_status_t status =
fdio_service_connect(fidl::DiscoverableProtocolDefaultPath<fuchsia_fxfs::CryptManagement>,
management_endpoints_or->server.TakeChannel().release());
status != ZX_OK) {
return zx::error(status);
}
auto client = fidl::BindSyncClient(std::move(management_endpoints_or->client));
// TODO(fxbug.dev/94587): A hardware source should be used for keys.
unsigned char key0[32] = {0};
unsigned char key1[32] = {0};
std::copy(std::begin(kInsecureCryptDataKey), std::end(kInsecureCryptDataKey), key0);
std::copy(std::begin(kInsecureCryptMetadataKey), std::end(kInsecureCryptMetadataKey), key1);
if (auto result = client->AddWrappingKey(0, fidl::VectorView<unsigned char>::FromExternal(key0));
!result.ok()) {
FX_LOGS(ERROR) << "Failed to add wrapping key: " << zx_status_get_string(result.status());
return zx::error(result.status());
}
if (auto result = client->AddWrappingKey(1, fidl::VectorView<unsigned char>::FromExternal(key1));
!result.ok()) {
FX_LOGS(ERROR) << "Failed to add wrapping key: " << zx_status_get_string(result.status());
return zx::error(result.status());
}
if (auto result = client->SetActiveKey(fuchsia_fxfs::wire::KeyPurpose::kData, 0); !result.ok()) {
FX_LOGS(ERROR) << "Failed to set active data key: " << zx_status_get_string(result.status());
return zx::error(result.status());
}
if (auto result = client->SetActiveKey(fuchsia_fxfs::wire::KeyPurpose::kMetadata, 1);
!result.ok()) {
FX_LOGS(ERROR) << "Failed to set active metadata key: "
<< zx_status_get_string(result.status());
return zx::error(result.status());
}
return zx::ok();
}
std::string_view BinaryPathForFormat(fs_management::DiskFormat format) {
switch (format) {
case fs_management::kDiskFormatFxfs:
return kFxfsPath;
case fs_management::kDiskFormatF2fs:
return kF2fsPath;
case fs_management::kDiskFormatMinfs:
return kMinfsPath;
default:
return "";
}
}
} // namespace fshost