| // 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 |