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