From 001af5852b92705fd689677856606cf456caaa64 Mon Sep 17 00:00:00 2001 From: Sean Klein <smklein@google.com> Date: Fri, 26 Apr 2019 02:26:27 +0000 Subject: [PATCH] [fshost] Extract pkgfs-specific parts from block watcher No functionality added in this patch; just a refactor to distinguish the "observing block device" component of block watcher from the "filesystem launching" component of block watcher. Change-Id: I66f909f66ba3edaef6cda28183ed7d5fe52be998 --- zircon/system/core/devmgr/fshost/BUILD.gn | 1 + .../core/devmgr/fshost/block-watcher.cpp | 500 +++++------------- .../system/core/devmgr/fshost/block-watcher.h | 59 +++ zircon/system/core/devmgr/fshost/fs-manager.h | 3 - zircon/system/core/devmgr/fshost/main.cpp | 1 + .../core/devmgr/fshost/pkgfs-launcher.cpp | 235 ++++++++ .../core/devmgr/fshost/pkgfs-launcher.h | 14 + 7 files changed, 438 insertions(+), 375 deletions(-) create mode 100644 zircon/system/core/devmgr/fshost/block-watcher.h create mode 100644 zircon/system/core/devmgr/fshost/pkgfs-launcher.cpp create mode 100644 zircon/system/core/devmgr/fshost/pkgfs-launcher.h diff --git a/zircon/system/core/devmgr/fshost/BUILD.gn b/zircon/system/core/devmgr/fshost/BUILD.gn index 73cbd824951..eb5a0e03e27 100644 --- a/zircon/system/core/devmgr/fshost/BUILD.gn +++ b/zircon/system/core/devmgr/fshost/BUILD.gn @@ -32,6 +32,7 @@ executable("fshost") { sources = [ "block-watcher.cpp", "main.cpp", + "pkgfs-launcher.cpp", ] deps = [ ":common", diff --git a/zircon/system/core/devmgr/fshost/block-watcher.cpp b/zircon/system/core/devmgr/fshost/block-watcher.cpp index 7aa1582c830..7b01d7660f0 100644 --- a/zircon/system/core/devmgr/fshost/block-watcher.cpp +++ b/zircon/system/core/devmgr/fshost/block-watcher.cpp @@ -38,256 +38,12 @@ #include <utility> -#include "fs-manager.h" +#include "block-watcher.h" +#include "pkgfs-launcher.h" namespace devmgr { namespace { -class BlockWatcher { -public: - BlockWatcher(fbl::unique_ptr<FsManager> fshost, bool netboot) - : fshost_(std::move(fshost)), netboot_(netboot) {} - - void FuchsiaStart() const { fshost_->FuchsiaStart(); } - - zx_status_t InstallFs(const char* path, zx::channel h) { - return fshost_->InstallFs(path, std::move(h)); - } - - bool Netbooting() const { return netboot_; } - - // Optionally checks the filesystem stored on the device at |device_path|, - // if "zircon.system.filesystem-check" is set. - zx_status_t CheckFilesystem(const char* device_path, disk_format_t df, - const fsck_options_t* options) const; - - // Attempts to mount a block device backed by |fd| to "/data". - // Fails if already mounted. - zx_status_t MountData(fbl::unique_fd fd, mount_options_t* options); - - // Attempts to mount a block device backed by |fd| to "/install". - // Fails if already mounted. - zx_status_t MountInstall(fbl::unique_fd fd, mount_options_t* options); - - // Attempts to mount a block device backed by |fd| to "/blob". - // Fails if already mounted. - zx_status_t MountBlob(fbl::unique_fd fd, mount_options_t* options); - -private: - fbl::unique_ptr<FsManager> fshost_; - bool netboot_ = false; - bool data_mounted_ = false; - bool install_mounted_ = false; - bool blob_mounted_ = false; -}; - -void pkgfs_finish(BlockWatcher* watcher, zx::process proc, zx::channel pkgfs_root) { - auto deadline = zx::deadline_after(zx::sec(5)); - zx_signals_t observed; - zx_status_t status = - proc.wait_one(ZX_USER_SIGNAL_0 | ZX_PROCESS_TERMINATED, deadline, &observed); - if (status != ZX_OK) { - printf("fshost: pkgfs did not signal completion: %d (%s)\n", status, - zx_status_get_string(status)); - return; - } - if (!(observed & ZX_USER_SIGNAL_0)) { - printf("fshost: pkgfs terminated prematurely\n"); - return; - } - // re-export /pkgfs/system as /system - zx::channel system_channel, system_req; - if (zx::channel::create(0, &system_channel, &system_req) != ZX_OK) { - return; - } - if (fdio_open_at(pkgfs_root.get(), "system", FS_READONLY_DIR_FLAGS, - system_req.release()) != ZX_OK) { - return; - } - // re-export /pkgfs/packages/shell-commands/0/bin as /bin - zx::channel bin_chan, bin_req; - if (zx::channel::create(0, &bin_chan, &bin_req) != ZX_OK) { - return; - } - if (fdio_open_at(pkgfs_root.get(), "packages/shell-commands/0/bin", FS_READONLY_DIR_FLAGS, - bin_req.release()) != ZX_OK) { - // non-fatal. - printf("fshost: failed to install /bin (could not open shell-commands)\n"); - } - - if (watcher->InstallFs("/pkgfs", std::move(pkgfs_root)) != ZX_OK) { - printf("fshost: failed to install /pkgfs\n"); - return; - } - - if (watcher->InstallFs("/system", std::move(system_channel)) != ZX_OK) { - printf("fshost: failed to install /system\n"); - return; - } - - // as above, failure of /bin export is non-fatal. - if (watcher->InstallFs("/bin", std::move(bin_chan)) != ZX_OK) { - printf("fshost: failed to install /bin\n"); - } - - // start the appmgr - watcher->FuchsiaStart(); -} - -// Launching pkgfs uses its own loader service and command lookup to run out of -// the blobfs without any real filesystem. Files are found by -// getenv("zircon.system.pkgfs.file.PATH") returning a blob content ID. -// That is, a manifest of name->blob is embedded in /boot/config/devmgr. -zx_status_t pkgfs_ldsvc_load_blob(void* ctx, const char* prefix, const char* name, - zx_handle_t* vmo) { - const int fs_blob_fd = static_cast<int>(reinterpret_cast<intptr_t>(ctx)); - char key[256]; - if (snprintf(key, sizeof(key), "zircon.system.pkgfs.file.%s%s", prefix, name) >= - (int)sizeof(key)) { - return ZX_ERR_BAD_PATH; - } - const char* blob = getenv(key); - if (blob == nullptr) { - return ZX_ERR_NOT_FOUND; - } - int fd = openat(fs_blob_fd, blob, O_RDONLY); - if (fd < 0) { - return ZX_ERR_NOT_FOUND; - } - - zx::vmo nonexec_vmo; - zx::vmo exec_vmo; - zx_status_t status = fdio_get_vmo_clone(fd, nonexec_vmo.reset_and_get_address()); - close(fd); - if (status != ZX_OK) { - return status; - } - status = nonexec_vmo.replace_as_executable(zx::handle(), &exec_vmo); - if (status != ZX_OK) { - return status; - } - status = zx_object_set_property(exec_vmo.get(), ZX_PROP_NAME, key, strlen(key)); - if (status != ZX_OK) { - return status; - } - - *vmo = exec_vmo.release(); - return ZX_OK; -} - -zx_status_t pkgfs_ldsvc_load_object(void* ctx, const char* name, zx_handle_t* vmo) { - return pkgfs_ldsvc_load_blob(ctx, "lib/", name, vmo); -} - -zx_status_t pkgfs_ldsvc_load_abspath(void* ctx, const char* name, zx_handle_t* vmo) { - return pkgfs_ldsvc_load_blob(ctx, "", name + 1, vmo); -} - -zx_status_t pkgfs_ldsvc_publish_data_sink(void* ctx, const char* name, zx_handle_t vmo) { - zx_handle_close(vmo); - return ZX_ERR_NOT_SUPPORTED; -} - -void pkgfs_ldsvc_finalizer(void* ctx) { - close(static_cast<int>(reinterpret_cast<intptr_t>(ctx))); -} - -const loader_service_ops_t pkgfs_ldsvc_ops = { - .load_object = pkgfs_ldsvc_load_object, - .load_abspath = pkgfs_ldsvc_load_abspath, - .publish_data_sink = pkgfs_ldsvc_publish_data_sink, - .finalizer = pkgfs_ldsvc_finalizer, -}; - -// Create a local loader service with a fixed mapping of names to blobs. -zx_status_t pkgfs_ldsvc_start(fbl::unique_fd fs_blob_fd, zx::channel* ldsvc) { - loader_service_t* service; - zx_status_t status = - loader_service_create(nullptr, &pkgfs_ldsvc_ops, - reinterpret_cast<void*>(static_cast<intptr_t>(fs_blob_fd.get())), - &service); - if (status != ZX_OK) { - printf("fshost: cannot create pkgfs loader service: %d (%s)\n", status, - zx_status_get_string(status)); - return status; - } - // The loader service now owns this FD - __UNUSED auto fd = fs_blob_fd.release(); - - status = loader_service_connect(service, ldsvc->reset_and_get_address()); - loader_service_release(service); - if (status != ZX_OK) { - printf("fshost: cannot connect pkgfs loader service: %d (%s)\n", status, - zx_status_get_string(status)); - } - return status; -} - -bool pkgfs_launch(BlockWatcher* watcher) { - const char* cmd = getenv("zircon.system.pkgfs.cmd"); - if (cmd == nullptr) { - return false; - } - - fbl::unique_fd fs_blob_fd(open("/fs/blob", O_RDONLY | O_DIRECTORY)); - if (!fs_blob_fd) { - printf("fshost: open(/fs/blob): %m\n"); - return false; - } - - zx::channel h0, h1; - zx_status_t status = zx::channel::create(0, &h0, &h1); - if (status != ZX_OK) { - printf("fshost: cannot create pkgfs root channel: %d (%s)\n", status, - zx_status_get_string(status)); - return false; - } - - auto args = ArgumentVector::FromCmdline(cmd); - auto argv = args.argv(); - // Remove leading slashes before asking pkgfs_ldsvc_load_blob to load the - // file. - const char* file = argv[0]; - while (file[0] == '/') { - ++file; - } - zx::vmo executable; - status = pkgfs_ldsvc_load_blob(reinterpret_cast<void*>(static_cast<intptr_t>(fs_blob_fd.get())), - "", argv[0], executable.reset_and_get_address()); - if (status != ZX_OK) { - printf("fshost: cannot load pkgfs executable: %d (%s)\n", status, - zx_status_get_string(status)); - return false; - } - - zx::channel loader; - status = pkgfs_ldsvc_start(std::move(fs_blob_fd), &loader); - if (status != ZX_OK) { - printf("fshost: cannot pkgfs loader: %d (%s)\n", status, zx_status_get_string(status)); - return false; - } - - const zx_handle_t raw_h1 = h1.release(); - zx::process proc; - args.Print("fshost"); - status = devmgr_launch_with_loader(*zx::job::default_job(), "pkgfs", - std::move(executable), std::move(loader), - argv, nullptr, -1, &raw_h1, - (const uint32_t[]){PA_HND(PA_USER0, 0)}, 1, &proc, - FS_DATA | FS_BLOB | FS_SVC); - if (status != ZX_OK) { - printf("fshost: failed to launch %s: %d (%s)\n", cmd, status, zx_status_get_string(status)); - return false; - } - - pkgfs_finish(watcher, std::move(proc), std::move(h0)); - return true; -} - -void LaunchBlobInit(BlockWatcher* watcher) { - pkgfs_launch(watcher); -} - zx_status_t LaunchBlobfs(int argc, const char** argv, zx_handle_t* hnd, uint32_t* ids, size_t len) { return devmgr_launch(*zx::job::default_job(), "blobfs:/blob", argv, nullptr, @@ -304,128 +60,6 @@ zx_status_t LaunchFAT(int argc, const char** argv, zx_handle_t* hnd, uint32_t* i -1, hnd, ids, len, nullptr, FS_FOR_FSPROC); } -zx_status_t BlockWatcher::MountData(fbl::unique_fd fd, mount_options_t* options) { - if (data_mounted_) { - return ZX_ERR_ALREADY_BOUND; - } - options->wait_until_ready = true; - - zx_status_t status = - mount(fd.release(), "/fs" PATH_DATA, DISK_FORMAT_MINFS, options, LaunchMinfs); - if (status != ZX_OK) { - printf("fshost: failed to mount %s: %s.\n", PATH_DATA, zx_status_get_string(status)); - } else { - data_mounted_ = true; - } - return status; -} - -zx_status_t BlockWatcher::MountInstall(fbl::unique_fd fd, mount_options_t* options) { - if (install_mounted_) { - return ZX_ERR_ALREADY_BOUND; - } - options->readonly = true; - zx_status_t status = - mount(fd.release(), "/fs" PATH_INSTALL, DISK_FORMAT_MINFS, options, LaunchMinfs); - if (status != ZX_OK) { - printf("fshost: failed to mount %s: %s.\n", PATH_INSTALL, zx_status_get_string(status)); - } else { - install_mounted_ = true; - } - return status; -} - -zx_status_t BlockWatcher::MountBlob(fbl::unique_fd fd, mount_options_t* options) { - if (blob_mounted_) { - return ZX_ERR_ALREADY_BOUND; - } - zx_status_t status = - mount(fd.release(), "/fs" PATH_BLOB, DISK_FORMAT_BLOBFS, options, LaunchBlobfs); - if (status != ZX_OK) { - printf("fshost: failed to mount %s: %s.\n", PATH_BLOB, zx_status_get_string(status)); - } else { - blob_mounted_ = true; - } - return status; -} - -zx_status_t BlockWatcher::CheckFilesystem(const char* device_path, disk_format_t df, - const fsck_options_t* options) const { - if (!getenv_bool("zircon.system.filesystem-check", false)) { - return ZX_OK; - } - - // TODO(ZX-3793): Blobfs' consistency checker is too slow to execute on boot. - // With journaling, it is also unnecessary, but would be a nice mechanism for sanity - // checking. - if (df == DISK_FORMAT_BLOBFS) { - fprintf(stderr, "fshost: Skipping blobfs consistency checker\n"); - return ZX_OK; - } - - zx::ticks before = zx::ticks::now(); - auto timer = fbl::MakeAutoCall([before]() { - auto after = zx::ticks::now(); - auto duration = fzl::TicksToNs(after - before); - printf("fshost: fsck took %" PRId64 ".%" PRId64 " seconds\n", duration.to_secs(), - duration.to_msecs() % 1000); - }); - - printf("fshost: fsck of %s started\n", disk_format_string_[df]); - - auto launch_fsck = [](int argc, const char** argv, zx_handle_t* hnd, uint32_t* ids, - size_t len) { - zx::process proc; - zx_status_t status = devmgr_launch(*zx::job::default_job(), "fsck", argv, - nullptr, -1, hnd, ids, len, &proc, FS_FOR_FSPROC); - if (status != ZX_OK) { - fprintf(stderr, "fshost: Couldn't launch fsck\n"); - return status; - } - status = proc.wait_one(ZX_PROCESS_TERMINATED, zx::time::infinite(), nullptr); - if (status != ZX_OK) { - fprintf(stderr, "fshost: Error waiting for fsck to terminate\n"); - return status; - } - - zx_info_process_t info; - status = proc.get_info(ZX_INFO_PROCESS, &info, sizeof(info), nullptr, nullptr); - if (status != ZX_OK) { - fprintf(stderr, "fshost: Failed to get process info\n"); - return status; - } - - if (info.return_code != 0) { - fprintf(stderr, "fshost: Fsck return code: %" PRId64 "\n", info.return_code); - return ZX_ERR_BAD_STATE; - } - return ZX_OK; - }; - - zx_status_t status = fsck(device_path, df, options, launch_fsck); - if (status != ZX_OK) { - fprintf(stderr, "--------------------------------------------------------------\n"); - fprintf(stderr, "| \n"); - fprintf(stderr, "| WARNING: fshost fsck failure! \n"); - fprintf(stderr, "| Corrupt %s @ %s \n", disk_format_string_[df], device_path); - fprintf(stderr, "| \n"); - fprintf(stderr, "| If your system encountered power-loss due to an unclean \n"); - fprintf(stderr, "| shutdown, this error was expected. Journaling in minfs \n"); - fprintf(stderr, "| is being tracked by ZX-2093. Re-paving will reset your \n"); - fprintf(stderr, "| device. \n"); - fprintf(stderr, "| \n"); - fprintf(stderr, "| If your system was shutdown cleanly (via 'dm poweroff' \n"); - fprintf(stderr, "| or an OTA), report this device to the local-storage \n"); - fprintf(stderr, "| team. Please file bugs with logs before and after reboot. \n"); - fprintf(stderr, "| Please use the 'filesystem' and 'minfs' component tag. \n"); - fprintf(stderr, "| \n"); - fprintf(stderr, "--------------------------------------------------------------\n"); - } else { - printf("fshost: fsck of %s completed OK\n", disk_format_string_[df]); - } - return status; -} - // Attempt to mount the device pointed to be the file descriptor at a known // location. // @@ -468,11 +102,11 @@ zx_status_t MountMinfs(BlockWatcher* watcher, fbl::unique_fd fd, mount_options_t // return value is ignored int UnsealZxcrypt(void* arg) { - fbl::unique_ptr<int> fd_ptr(static_cast<int*>(arg)); + std::unique_ptr<int> fd_ptr(static_cast<int*>(arg)); fbl::unique_fd fd(*fd_ptr); zx_status_t rc; - fbl::unique_ptr<zxcrypt::FdioVolume> zxcrypt_volume; + std::unique_ptr<zxcrypt::FdioVolume> zxcrypt_volume; if ((rc = zxcrypt::FdioVolume::Init(std::move(fd), &zxcrypt_volume)) != ZX_OK) { printf("fshost: couldn't open zxcrypt fdio volume"); return ZX_OK; @@ -499,7 +133,7 @@ zx_status_t FormatMinfs(const fbl::unique_fd& block_device, fprintf(stderr, "fshost: Formatting minfs.\n"); uint64_t device_size = info.block_size * info.block_count; - fbl::unique_ptr<minfs::Bcache> bc; + std::unique_ptr<minfs::Bcache> bc; zx_status_t status; if ((status = minfs::Bcache::Create(&bc, block_device.duplicate(), static_cast<uint32_t>(device_size))) != ZX_OK) { @@ -680,7 +314,129 @@ zx_status_t BlockDeviceAdded(int dirfd, int event, const char* name, void* cooki } // namespace -void BlockDeviceWatcher(fbl::unique_ptr<FsManager> fshost, bool netboot) { +zx_status_t BlockWatcher::MountData(fbl::unique_fd fd, mount_options_t* options) { + if (data_mounted_) { + return ZX_ERR_ALREADY_BOUND; + } + options->wait_until_ready = true; + + zx_status_t status = + mount(fd.release(), "/fs" PATH_DATA, DISK_FORMAT_MINFS, options, LaunchMinfs); + if (status != ZX_OK) { + printf("fshost: failed to mount %s: %s.\n", PATH_DATA, zx_status_get_string(status)); + } else { + data_mounted_ = true; + } + return status; +} + +zx_status_t BlockWatcher::MountInstall(fbl::unique_fd fd, mount_options_t* options) { + if (install_mounted_) { + return ZX_ERR_ALREADY_BOUND; + } + options->readonly = true; + zx_status_t status = + mount(fd.release(), "/fs" PATH_INSTALL, DISK_FORMAT_MINFS, options, LaunchMinfs); + if (status != ZX_OK) { + printf("fshost: failed to mount %s: %s.\n", PATH_INSTALL, zx_status_get_string(status)); + } else { + install_mounted_ = true; + } + return status; +} + +zx_status_t BlockWatcher::MountBlob(fbl::unique_fd fd, mount_options_t* options) { + if (blob_mounted_) { + return ZX_ERR_ALREADY_BOUND; + } + zx_status_t status = + mount(fd.release(), "/fs" PATH_BLOB, DISK_FORMAT_BLOBFS, options, LaunchBlobfs); + if (status != ZX_OK) { + printf("fshost: failed to mount %s: %s.\n", PATH_BLOB, zx_status_get_string(status)); + } else { + blob_mounted_ = true; + } + return status; +} + +zx_status_t BlockWatcher::CheckFilesystem(const char* device_path, disk_format_t df, + const fsck_options_t* options) const { + if (!getenv_bool("zircon.system.filesystem-check", false)) { + return ZX_OK; + } + + // TODO(ZX-3793): Blobfs' consistency checker is too slow to execute on boot. + // With journaling, it is also unnecessary, but would be a nice mechanism for sanity + // checking. + if (df == DISK_FORMAT_BLOBFS) { + fprintf(stderr, "fshost: Skipping blobfs consistency checker\n"); + return ZX_OK; + } + + zx::ticks before = zx::ticks::now(); + auto timer = fbl::MakeAutoCall([before]() { + auto after = zx::ticks::now(); + auto duration = fzl::TicksToNs(after - before); + printf("fshost: fsck took %" PRId64 ".%" PRId64 " seconds\n", duration.to_secs(), + duration.to_msecs() % 1000); + }); + + printf("fshost: fsck of %s started\n", disk_format_string_[df]); + + auto launch_fsck = [](int argc, const char** argv, zx_handle_t* hnd, uint32_t* ids, + size_t len) { + zx::process proc; + zx_status_t status = devmgr_launch(*zx::job::default_job(), "fsck", argv, + nullptr, -1, hnd, ids, len, &proc, FS_FOR_FSPROC); + if (status != ZX_OK) { + fprintf(stderr, "fshost: Couldn't launch fsck\n"); + return status; + } + status = proc.wait_one(ZX_PROCESS_TERMINATED, zx::time::infinite(), nullptr); + if (status != ZX_OK) { + fprintf(stderr, "fshost: Error waiting for fsck to terminate\n"); + return status; + } + + zx_info_process_t info; + status = proc.get_info(ZX_INFO_PROCESS, &info, sizeof(info), nullptr, nullptr); + if (status != ZX_OK) { + fprintf(stderr, "fshost: Failed to get process info\n"); + return status; + } + + if (info.return_code != 0) { + fprintf(stderr, "fshost: Fsck return code: %" PRId64 "\n", info.return_code); + return ZX_ERR_BAD_STATE; + } + return ZX_OK; + }; + + zx_status_t status = fsck(device_path, df, options, launch_fsck); + if (status != ZX_OK) { + fprintf(stderr, "--------------------------------------------------------------\n"); + fprintf(stderr, "| \n"); + fprintf(stderr, "| WARNING: fshost fsck failure! \n"); + fprintf(stderr, "| Corrupt %s @ %s \n", disk_format_string_[df], device_path); + fprintf(stderr, "| \n"); + fprintf(stderr, "| If your system encountered power-loss due to an unclean \n"); + fprintf(stderr, "| shutdown, this error was expected. Journaling in minfs \n"); + fprintf(stderr, "| is being tracked by ZX-2093. Re-paving will reset your \n"); + fprintf(stderr, "| device. \n"); + fprintf(stderr, "| \n"); + fprintf(stderr, "| If your system was shutdown cleanly (via 'dm poweroff' \n"); + fprintf(stderr, "| or an OTA), report this device to the local-storage \n"); + fprintf(stderr, "| team. Please file bugs with logs before and after reboot. \n"); + fprintf(stderr, "| Please use the 'filesystem' and 'minfs' component tag. \n"); + fprintf(stderr, "| \n"); + fprintf(stderr, "--------------------------------------------------------------\n"); + } else { + printf("fshost: fsck of %s completed OK\n", disk_format_string_[df]); + } + return status; +} + +void BlockDeviceWatcher(std::unique_ptr<FsManager> fshost, bool netboot) { BlockWatcher watcher(std::move(fshost), netboot); fbl::unique_fd dirfd(open("/dev/class/block", O_DIRECTORY | O_RDONLY)); diff --git a/zircon/system/core/devmgr/fshost/block-watcher.h b/zircon/system/core/devmgr/fshost/block-watcher.h new file mode 100644 index 00000000000..3a30e2317a7 --- /dev/null +++ b/zircon/system/core/devmgr/fshost/block-watcher.h @@ -0,0 +1,59 @@ +// 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. + +#pragma once + +#include <memory> + +#include <fbl/unique_fd.h> +#include <fs-management/mount.h> +#include <lib/zx/channel.h> +#include <zircon/types.h> + +#include "fs-manager.h" + +namespace devmgr { + +class BlockWatcher { +public: + BlockWatcher(std::unique_ptr<FsManager> fshost, bool netboot) + : fshost_(std::move(fshost)), netboot_(netboot) {} + + void FuchsiaStart() const { fshost_->FuchsiaStart(); } + + zx_status_t InstallFs(const char* path, zx::channel h) { + return fshost_->InstallFs(path, std::move(h)); + } + + bool Netbooting() const { return netboot_; } + + // Optionally checks the filesystem stored on the device at |device_path|, + // if "zircon.system.filesystem-check" is set. + zx_status_t CheckFilesystem(const char* device_path, disk_format_t df, + const fsck_options_t* options) const; + + // Attempts to mount a block device backed by |fd| to "/data". + // Fails if already mounted. + zx_status_t MountData(fbl::unique_fd fd, mount_options_t* options); + + // Attempts to mount a block device backed by |fd| to "/install". + // Fails if already mounted. + zx_status_t MountInstall(fbl::unique_fd fd, mount_options_t* options); + + // Attempts to mount a block device backed by |fd| to "/blob". + // Fails if already mounted. + zx_status_t MountBlob(fbl::unique_fd fd, mount_options_t* options); + +private: + std::unique_ptr<FsManager> fshost_; + bool netboot_ = false; + bool data_mounted_ = false; + bool install_mounted_ = false; + bool blob_mounted_ = false; +}; + +// Monitors "/dev/class/block" for new devices indefinitely. +void BlockDeviceWatcher(std::unique_ptr<FsManager> fshost, bool netboot); + +} // namespace devmgr diff --git a/zircon/system/core/devmgr/fshost/fs-manager.h b/zircon/system/core/devmgr/fshost/fs-manager.h index 4a9e810296b..776f5e52345 100644 --- a/zircon/system/core/devmgr/fshost/fs-manager.h +++ b/zircon/system/core/devmgr/fshost/fs-manager.h @@ -88,7 +88,4 @@ private: fshost::Registry registry_; }; -// Monitors "/dev/class/block" for new devices indefinitely. -void BlockDeviceWatcher(fbl::unique_ptr<FsManager> fshost, bool netboot); - } // namespace devmgr diff --git a/zircon/system/core/devmgr/fshost/main.cpp b/zircon/system/core/devmgr/fshost/main.cpp index a7242551fee..8488770060c 100644 --- a/zircon/system/core/devmgr/fshost/main.cpp +++ b/zircon/system/core/devmgr/fshost/main.cpp @@ -22,6 +22,7 @@ #include <zircon/processargs.h> #include <zircon/status.h> +#include "block-watcher.h" #include "fs-manager.h" namespace devmgr { diff --git a/zircon/system/core/devmgr/fshost/pkgfs-launcher.cpp b/zircon/system/core/devmgr/fshost/pkgfs-launcher.cpp new file mode 100644 index 00000000000..97bda189136 --- /dev/null +++ b/zircon/system/core/devmgr/fshost/pkgfs-launcher.cpp @@ -0,0 +1,235 @@ +// 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 <fcntl.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <fbl/unique_fd.h> +#include <fs-management/mount.h> +#include <lib/fdio/directory.h> +#include <lib/fdio/fd.h> +#include <lib/fdio/fdio.h> +#include <lib/zx/channel.h> +#include <lib/zx/process.h> +#include <loader-service/loader-service.h> +#include <zircon/processargs.h> +#include <zircon/status.h> +#include <zircon/syscalls.h> + +#include <utility> + +#include "pkgfs-launcher.h" + +namespace devmgr { +namespace { + +void pkgfs_finish(BlockWatcher* watcher, zx::process proc, zx::channel pkgfs_root) { + auto deadline = zx::deadline_after(zx::sec(5)); + zx_signals_t observed; + zx_status_t status = + proc.wait_one(ZX_USER_SIGNAL_0 | ZX_PROCESS_TERMINATED, deadline, &observed); + if (status != ZX_OK) { + printf("fshost: pkgfs did not signal completion: %d (%s)\n", status, + zx_status_get_string(status)); + return; + } + if (!(observed & ZX_USER_SIGNAL_0)) { + printf("fshost: pkgfs terminated prematurely\n"); + return; + } + // re-export /pkgfs/system as /system + zx::channel system_channel, system_req; + if (zx::channel::create(0, &system_channel, &system_req) != ZX_OK) { + return; + } + if (fdio_open_at(pkgfs_root.get(), "system", FS_READONLY_DIR_FLAGS, + system_req.release()) != ZX_OK) { + return; + } + // re-export /pkgfs/packages/shell-commands/0/bin as /bin + zx::channel bin_chan, bin_req; + if (zx::channel::create(0, &bin_chan, &bin_req) != ZX_OK) { + return; + } + if (fdio_open_at(pkgfs_root.get(), "packages/shell-commands/0/bin", FS_READONLY_DIR_FLAGS, + bin_req.release()) != ZX_OK) { + // non-fatal. + printf("fshost: failed to install /bin (could not open shell-commands)\n"); + } + if (watcher->InstallFs("/pkgfs", std::move(pkgfs_root)) != ZX_OK) { + printf("fshost: failed to install /pkgfs\n"); + return; + } + if (watcher->InstallFs("/system", std::move(system_channel)) != ZX_OK) { + printf("fshost: failed to install /system\n"); + return; + } + // as above, failure of /bin export is non-fatal. + if (watcher->InstallFs("/bin", std::move(bin_chan)) != ZX_OK) { + printf("fshost: failed to install /bin\n"); + } + // start the appmgr + watcher->FuchsiaStart(); +} + +// Launching pkgfs uses its own loader service and command lookup to run out of +// the blobfs without any real filesystem. Files are found by +// getenv("zircon.system.pkgfs.file.PATH") returning a blob content ID. +// That is, a manifest of name->blob is embedded in /boot/config/devmgr. +zx_status_t pkgfs_ldsvc_load_blob(void* ctx, const char* prefix, const char* name, + zx_handle_t* vmo) { + const int fs_blob_fd = static_cast<int>(reinterpret_cast<intptr_t>(ctx)); + char key[256]; + if (snprintf(key, sizeof(key), "zircon.system.pkgfs.file.%s%s", prefix, name) >= + (int)sizeof(key)) { + return ZX_ERR_BAD_PATH; + } + const char* blob = getenv(key); + if (blob == nullptr) { + return ZX_ERR_NOT_FOUND; + } + int fd = openat(fs_blob_fd, blob, O_RDONLY); + if (fd < 0) { + return ZX_ERR_NOT_FOUND; + } + + zx::vmo nonexec_vmo; + zx::vmo exec_vmo; + zx_status_t status = fdio_get_vmo_clone(fd, nonexec_vmo.reset_and_get_address()); + close(fd); + if (status != ZX_OK) { + return status; + } + status = nonexec_vmo.replace_as_executable(zx::handle(), &exec_vmo); + if (status != ZX_OK) { + return status; + } + status = zx_object_set_property(exec_vmo.get(), ZX_PROP_NAME, key, strlen(key)); + if (status != ZX_OK) { + return status; + } + + *vmo = exec_vmo.release(); + return ZX_OK; +} + +zx_status_t pkgfs_ldsvc_load_object(void* ctx, const char* name, zx_handle_t* vmo) { + return pkgfs_ldsvc_load_blob(ctx, "lib/", name, vmo); +} + +zx_status_t pkgfs_ldsvc_load_abspath(void* ctx, const char* name, zx_handle_t* vmo) { + return pkgfs_ldsvc_load_blob(ctx, "", name + 1, vmo); +} + +zx_status_t pkgfs_ldsvc_publish_data_sink(void* ctx, const char* name, zx_handle_t vmo) { + zx_handle_close(vmo); + return ZX_ERR_NOT_SUPPORTED; +} + +void pkgfs_ldsvc_finalizer(void* ctx) { + close(static_cast<int>(reinterpret_cast<intptr_t>(ctx))); +} + +const loader_service_ops_t pkgfs_ldsvc_ops = { + .load_object = pkgfs_ldsvc_load_object, + .load_abspath = pkgfs_ldsvc_load_abspath, + .publish_data_sink = pkgfs_ldsvc_publish_data_sink, + .finalizer = pkgfs_ldsvc_finalizer, +}; + +// Create a local loader service with a fixed mapping of names to blobs. +zx_status_t pkgfs_ldsvc_start(fbl::unique_fd fs_blob_fd, zx::channel* ldsvc) { + loader_service_t* service; + zx_status_t status = + loader_service_create(nullptr, &pkgfs_ldsvc_ops, + reinterpret_cast<void*>(static_cast<intptr_t>(fs_blob_fd.get())), + &service); + if (status != ZX_OK) { + printf("fshost: cannot create pkgfs loader service: %d (%s)\n", status, + zx_status_get_string(status)); + return status; + } + // The loader service now owns this FD + __UNUSED auto fd = fs_blob_fd.release(); + + status = loader_service_connect(service, ldsvc->reset_and_get_address()); + loader_service_release(service); + if (status != ZX_OK) { + printf("fshost: cannot connect pkgfs loader service: %d (%s)\n", status, + zx_status_get_string(status)); + } + return status; +} + +bool pkgfs_launch(BlockWatcher* watcher) { + const char* cmd = getenv("zircon.system.pkgfs.cmd"); + if (cmd == nullptr) { + return false; + } + + fbl::unique_fd fs_blob_fd(open("/fs/blob", O_RDONLY | O_DIRECTORY)); + if (!fs_blob_fd) { + printf("fshost: open(/fs/blob): %m\n"); + return false; + } + + zx::channel h0, h1; + zx_status_t status = zx::channel::create(0, &h0, &h1); + if (status != ZX_OK) { + printf("fshost: cannot create pkgfs root channel: %d (%s)\n", status, + zx_status_get_string(status)); + return false; + } + + auto args = ArgumentVector::FromCmdline(cmd); + auto argv = args.argv(); + // Remove leading slashes before asking pkgfs_ldsvc_load_blob to load the + // file. + const char* file = argv[0]; + while (file[0] == '/') { + ++file; + } + zx::vmo executable; + status = pkgfs_ldsvc_load_blob(reinterpret_cast<void*>(static_cast<intptr_t>(fs_blob_fd.get())), + "", argv[0], executable.reset_and_get_address()); + if (status != ZX_OK) { + printf("fshost: cannot load pkgfs executable: %d (%s)\n", status, + zx_status_get_string(status)); + return false; + } + + zx::channel loader; + status = pkgfs_ldsvc_start(std::move(fs_blob_fd), &loader); + if (status != ZX_OK) { + printf("fshost: cannot pkgfs loader: %d (%s)\n", status, zx_status_get_string(status)); + return false; + } + + const zx_handle_t raw_h1 = h1.release(); + zx::process proc; + args.Print("fshost"); + status = devmgr_launch_with_loader(*zx::job::default_job(), "pkgfs", + std::move(executable), std::move(loader), + argv, nullptr, -1, &raw_h1, + (const uint32_t[]){PA_HND(PA_USER0, 0)}, 1, &proc, + FS_DATA | FS_BLOB | FS_SVC); + if (status != ZX_OK) { + printf("fshost: failed to launch %s: %d (%s)\n", cmd, status, zx_status_get_string(status)); + return false; + } + + pkgfs_finish(watcher, std::move(proc), std::move(h0)); + return true; +} + +} // namespace + +void LaunchBlobInit(BlockWatcher* watcher) { + pkgfs_launch(watcher); +} + +} // namespace devmgr diff --git a/zircon/system/core/devmgr/fshost/pkgfs-launcher.h b/zircon/system/core/devmgr/fshost/pkgfs-launcher.h new file mode 100644 index 00000000000..03d4a3853a4 --- /dev/null +++ b/zircon/system/core/devmgr/fshost/pkgfs-launcher.h @@ -0,0 +1,14 @@ +// 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. + +#pragma once + +#include "block-watcher.h" + +namespace devmgr { + +// Launches pkgfs from within blobfs by parsing environment variables. +void LaunchBlobInit(BlockWatcher* watcher); + +} // namespace devmgr -- GitLab