diff --git a/zircon/system/core/devmgr/fshost/BUILD.gn b/zircon/system/core/devmgr/fshost/BUILD.gn
index eb5a0e03e274c8d2995f97c19c58f6336359ccd9..b766170bfe284e1ef9748200fa0b9b24d80a94c9 100644
--- a/zircon/system/core/devmgr/fshost/BUILD.gn
+++ b/zircon/system/core/devmgr/fshost/BUILD.gn
@@ -2,7 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-source_set("common") {
+source_set("fshost-registry") {
   sources = [
     "fs-manager.cpp",
     "registry.cpp",
@@ -28,20 +28,28 @@ source_set("common") {
   ]
 }
 
-executable("fshost") {
+source_set("block-watcher") {
   sources = [
+    "block-device.cpp",
     "block-watcher.cpp",
-    "main.cpp",
+    "filesystem-mounter.cpp",
     "pkgfs-launcher.cpp",
   ]
+  public_deps = [
+    "$zx/system/fidl/fuchsia-fshost:c",
+    "$zx/system/ulib/async-loop:async-loop-cpp",
+    "$zx/system/ulib/fit",
+    "$zx/system/ulib/fs",
+    "$zx/system/ulib/memfs",
+    "$zx/system/ulib/zx",
+    "../shared",
+  ]
   deps = [
-    ":common",
+    ":fshost-registry",
     "$zx/system/fidl/fuchsia-device:c",
     "$zx/system/fidl/fuchsia-hardware-block:c",
     "$zx/system/fidl/fuchsia-hardware-block-partition:c",
     "$zx/system/fidl/fuchsia-hardware-zxcrypt:c",
-    "$zx/system/ulib/async-loop:async-loop-cpp",
-    "$zx/system/ulib/bootdata",
     "$zx/system/ulib/fbl",
     "$zx/system/ulib/fdio",
     "$zx/system/ulib/fit",
@@ -50,15 +58,27 @@ executable("fshost") {
     "$zx/system/ulib/gpt",
     "$zx/system/ulib/loader-service",
     "$zx/system/ulib/minfs",
-    "$zx/system/ulib/ramdevice-client",
     "$zx/system/ulib/trace",
     "$zx/system/ulib/trace-engine",
     "$zx/system/ulib/zircon",
     "$zx/system/ulib/zx",
     "$zx/system/ulib/zxcpp",
     "$zx/system/ulib/zxcrypt",
-    "$zx/third_party/ulib/cksum",
-    "$zx/third_party/ulib/lz4",
+  ]
+}
+
+
+executable("fshost") {
+  sources = [
+    "main.cpp",
+  ]
+  deps = [
+    ":block-watcher",
+    ":fshost-registry",
+    "$zx/system/ulib/bootdata",
+    "$zx/system/ulib/fs-management",
+    "$zx/system/ulib/loader-service",
+    "$zx/system/ulib/ramdevice-client",
   ]
 }
 
@@ -68,7 +88,21 @@ test("fshost-test") {
     "fshost-test.cpp",
   ]
   deps = [
-    ":common",
+    ":fshost-registry",
+    "$zx/system/ulib/zxtest",
+  ]
+}
+
+test("block-watcher-test") {
+  test_group = "ddk"
+  sources = [
+    "block-device-test.cpp",
+    "block-watcher-test.cpp",
+  ]
+  deps = [
+    ":block-watcher",
+    "$zx/system/ulib/fs-management",
+    "$zx/system/ulib/ramdevice-client",
     "$zx/system/ulib/zxtest",
   ]
 }
diff --git a/zircon/system/core/devmgr/fshost/block-device-interface.h b/zircon/system/core/devmgr/fshost/block-device-interface.h
new file mode 100644
index 0000000000000000000000000000000000000000..f774fd681fc5796dac7e698d497cd6679f566bc4
--- /dev/null
+++ b/zircon/system/core/devmgr/fshost/block-device-interface.h
@@ -0,0 +1,72 @@
+// 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 <fs-management/mount.h>
+#include <fuchsia/hardware/block/c/fidl.h>
+#include <fuchsia/hardware/block/partition/c/fidl.h>
+#include <lib/zx/channel.h>
+#include <zircon/types.h>
+
+namespace devmgr {
+
+constexpr char kFVMDriverPath[] = "/boot/driver/fvm.so";
+constexpr char kGPTDriverPath[] = "/boot/driver/gpt.so";
+constexpr char kMBRDriverPath[] = "/boot/driver/mbr.so";
+constexpr char kBootpartDriverPath[] = "/boot/driver/bootpart.so";
+
+// An abstract class representing the operations which may be performed
+// on a block device, from the perspective of fshost.
+class BlockDeviceInterface {
+public:
+    virtual ~BlockDeviceInterface() = default;
+
+    zx_status_t Add();
+
+private:
+    // Returns the expected on-disk format of the underlying device.
+    //
+    // If unknown or unreadable, DISK_FORMAT_UNKNOWN should be returned.
+    virtual disk_format_t GetFormat() = 0;
+
+    // Modifies the expected on-disk format of the underlying device.
+    //
+    // This may be useful if the block device data was corrupted, and we want
+    // to force a new format based on external information.
+    virtual void SetFormat(disk_format_t format) = 0;
+
+    // Returns "true" if the device is booted from in-memory partitions,
+    // and expects that filesystems and encrypted partitions will not be
+    // automatically mounted.
+    virtual bool Netbooting() = 0;
+
+    // Queries (using the block interface) for info about the underlying device.
+    virtual zx_status_t GetInfo(fuchsia_hardware_block_BlockInfo* out_info) = 0;
+
+    // Queries (using the partition interface) for the GUID of the underlying device.
+    virtual zx_status_t GetTypeGUID(fuchsia_hardware_block_partition_GUID* out_guid) = 0;
+
+    // Attempts to directly bind a driver to the device. This is typically used
+    // by partition drivers, which may be loaded on top of a device exposing the
+    // block interface.
+    virtual zx_status_t AttachDriver(const fbl::StringPiece& driver) = 0;
+
+    // Unseals the underlying zxcrypt volume.
+    virtual zx_status_t UnsealZxcrypt() = 0;
+
+    // Validates the state of the filesystem, and returns ZX_OK if it appears
+    // consistent (or if the consistency check should be skipped).
+    virtual zx_status_t CheckFilesystem() = 0;
+
+    // Reformats the underlying block device with the format returned by |GetFormat()|.
+    virtual zx_status_t FormatFilesystem() = 0;
+
+    // Attempts to mount the filesystem with the format returned by |GetFormat()|.
+    virtual zx_status_t MountFilesystem() = 0;
+};
+
+} // namespace devmgr
diff --git a/zircon/system/core/devmgr/fshost/block-device-test.cpp b/zircon/system/core/devmgr/fshost/block-device-test.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..641ef04590fbb3d4ed0a794ba2c2aed2e5f52e9f
--- /dev/null
+++ b/zircon/system/core/devmgr/fshost/block-device-test.cpp
@@ -0,0 +1,180 @@
+// 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.
+
+#include <fcntl.h>
+
+#include <lib/fdio/namespace.h>
+#include <ramdevice-client/ramdisk.h>
+#include <zircon/assert.h>
+#include <zircon/hw/gpt.h>
+#include <zxtest/zxtest.h>
+
+#include "block-device.h"
+#include "filesystem-mounter.h"
+#include "fs-manager.h"
+
+namespace devmgr {
+namespace {
+
+class BlockDeviceHarness : public zxtest::Test {
+public:
+    void SetUp() override {
+        zx::event event;
+        ASSERT_OK(zx::event::create(0, &event));
+        ASSERT_OK(event.duplicate(ZX_RIGHT_SAME_RIGHTS, &event_));
+
+        // Initialize FilesystemMounter.
+        ASSERT_OK(FsManager::Create(std::move(event), &manager_));
+
+        // Fshost really likes mounting filesystems at "/fs".
+        // Let's make that available in our namespace.
+        zx::channel client, server;
+        ASSERT_OK(zx::channel::create(0, &client, &server));
+        ASSERT_OK(manager_->ServeRoot(std::move(server)));
+        fdio_ns_t* ns;
+        ASSERT_OK(fdio_ns_get_installed(&ns));
+        ASSERT_OK(fdio_ns_bind(ns, "/fs", client.release()));
+        manager_->WatchExit();
+    }
+
+    void TearDown() override {
+        fdio_ns_t* ns;
+        ASSERT_OK(fdio_ns_get_installed(&ns));
+        fdio_ns_unbind(ns, "/fs");
+    }
+
+    std::unique_ptr<FsManager> TakeManager() {
+        return std::move(manager_);
+    }
+
+private:
+    zx::event event_;
+    std::unique_ptr<FsManager> manager_;
+};
+
+TEST_F(BlockDeviceHarness, TestBadHandleDevice) {
+    std::unique_ptr<FsManager> manager = TakeManager();
+    bool netboot = false;
+    FilesystemMounter mounter(std::move(manager), netboot);
+    fbl::unique_fd fd;
+    BlockDevice device(&mounter, std::move(fd));
+    EXPECT_EQ(device.Netbooting(), netboot);
+    EXPECT_EQ(device.GetFormat(), DISK_FORMAT_UNKNOWN);
+    fuchsia_hardware_block_BlockInfo info;
+    EXPECT_EQ(device.GetInfo(&info), ZX_ERR_BAD_HANDLE);
+    fuchsia_hardware_block_partition_GUID guid;
+    EXPECT_EQ(device.GetTypeGUID(&guid), ZX_ERR_BAD_HANDLE);
+    EXPECT_EQ(device.AttachDriver("/foobar"), ZX_ERR_BAD_HANDLE);
+
+    // Returns ZX_OK because zxcrypt currently passes the empty fd to a background
+    // thread without observing the results.
+    EXPECT_OK(device.UnsealZxcrypt());
+
+    // Returns ZX_OK because filesystem checks are only enabled via environment variables.
+    EXPECT_OK(device.CheckFilesystem());
+
+    EXPECT_EQ(device.FormatFilesystem(), ZX_ERR_BAD_HANDLE);
+    EXPECT_EQ(device.MountFilesystem(), ZX_ERR_BAD_HANDLE);
+}
+
+TEST_F(BlockDeviceHarness, TestEmptyDevice) {
+    std::unique_ptr<FsManager> manager = TakeManager();
+    bool netboot = false;
+    FilesystemMounter mounter(std::move(manager), netboot);
+
+    // Initialize Ramdisk.
+    constexpr uint64_t kBlockSize = 512;
+    constexpr uint64_t kBlockCount = 1 << 20;
+    ramdisk_client_t* ramdisk;
+    ASSERT_OK(ramdisk_create(kBlockSize, kBlockCount, &ramdisk));
+    ASSERT_OK(wait_for_device(ramdisk_get_path(ramdisk), zx::sec(5).get()));
+    fbl::unique_fd fd(open(ramdisk_get_path(ramdisk), O_RDWR));
+    ASSERT_TRUE(fd);
+
+    BlockDevice device(&mounter, std::move(fd));
+    EXPECT_EQ(device.Netbooting(), netboot);
+    EXPECT_EQ(device.GetFormat(), DISK_FORMAT_UNKNOWN);
+    fuchsia_hardware_block_BlockInfo info;
+    EXPECT_OK(device.GetInfo(&info));
+    EXPECT_EQ(info.block_count, kBlockCount);
+    EXPECT_EQ(info.block_size, kBlockSize);
+
+    // Black-box: Since we're caching info, double check that re-calling GetInfo
+    // works correctly.
+    memset(&info, 0, sizeof(info));
+    EXPECT_OK(device.GetInfo(&info));
+    EXPECT_EQ(info.block_count, kBlockCount);
+    EXPECT_EQ(info.block_size, kBlockSize);
+
+    fuchsia_hardware_block_partition_GUID guid;
+    EXPECT_OK(device.GetTypeGUID(&guid));
+
+    EXPECT_EQ(device.FormatFilesystem(), ZX_ERR_NOT_SUPPORTED);
+    EXPECT_EQ(device.MountFilesystem(), ZX_ERR_NOT_SUPPORTED);
+    ASSERT_OK(ramdisk_destroy(ramdisk));
+}
+
+TEST_F(BlockDeviceHarness, TestMinfsBadGUID) {
+    std::unique_ptr<FsManager> manager = TakeManager();
+    bool netboot = false;
+    FilesystemMounter mounter(std::move(manager), netboot);
+
+    // Initialize Ramdisk with an empty GUID.
+    constexpr uint64_t kBlockSize = 512;
+    constexpr uint64_t kBlockCount = 1 << 20;
+    ramdisk_client_t* ramdisk;
+    ASSERT_OK(ramdisk_create(kBlockSize, kBlockCount, &ramdisk));
+    ASSERT_OK(wait_for_device(ramdisk_get_path(ramdisk), zx::sec(5).get()));
+    fbl::unique_fd fd(open(ramdisk_get_path(ramdisk), O_RDWR));
+    ASSERT_TRUE(fd);
+
+    // We started with an empty block device, but let's lie and say it
+    // should have been a minfs device.
+    BlockDevice device(&mounter, std::move(fd));
+    device.SetFormat(DISK_FORMAT_MINFS);
+    EXPECT_EQ(device.GetFormat(), DISK_FORMAT_MINFS);
+    EXPECT_OK(device.FormatFilesystem());
+
+    // Unlike earlier, where we received "ERR_NOT_SUPPORTED", we get "ERR_WRONG_TYPE"
+    // because the ramdisk doesn't have a data GUID.
+    EXPECT_EQ(device.MountFilesystem(), ZX_ERR_WRONG_TYPE);
+
+    ASSERT_OK(ramdisk_destroy(ramdisk));
+}
+
+TEST_F(BlockDeviceHarness, TestMinfsGoodGUID) {
+    std::unique_ptr<FsManager> manager = TakeManager();
+
+    bool netboot = false;
+    FilesystemMounter mounter(std::move(manager), netboot);
+
+    // Initialize Ramdisk with a data GUID.
+    constexpr uint64_t kBlockSize = 512;
+    constexpr uint64_t kBlockCount = 1 << 20;
+    ramdisk_client_t* ramdisk;
+    const uint8_t data_guid[GPT_GUID_LEN] = GUID_DATA_VALUE;
+    ASSERT_OK(ramdisk_create_with_guid(kBlockSize, kBlockCount, data_guid, sizeof(data_guid),
+                                       &ramdisk));
+    ASSERT_OK(wait_for_device(ramdisk_get_path(ramdisk), zx::sec(5).get()));
+    fbl::unique_fd fd(open(ramdisk_get_path(ramdisk), O_RDWR));
+    ASSERT_TRUE(fd);
+
+    BlockDevice device(&mounter, std::move(fd));
+    device.SetFormat(DISK_FORMAT_MINFS);
+    EXPECT_EQ(device.GetFormat(), DISK_FORMAT_MINFS);
+    EXPECT_OK(device.FormatFilesystem());
+
+    EXPECT_OK(device.MountFilesystem());
+    EXPECT_EQ(device.MountFilesystem(), ZX_ERR_ALREADY_BOUND);
+
+    ASSERT_OK(ramdisk_destroy(ramdisk));
+}
+
+// TODO(smklein): Plumb through the "check filesystem" decision using
+// something other than environment variables, and add a test for it.
+
+// TODO: Add tests for Zxcrypt binding.
+
+} // namespace
+} // namespace devmgr
diff --git a/zircon/system/core/devmgr/fshost/block-device.cpp b/zircon/system/core/devmgr/fshost/block-device.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e4388d73824c1772194a379f2dccc455965dfcf9
--- /dev/null
+++ b/zircon/system/core/devmgr/fshost/block-device.cpp
@@ -0,0 +1,441 @@
+// 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 <unistd.h>
+
+#include <fbl/algorithm.h>
+#include <fbl/auto_call.h>
+#include <fbl/string_buffer.h>
+#include <fbl/unique_fd.h>
+#include <fs-management/mount.h>
+#include <fuchsia/device/c/fidl.h>
+#include <fuchsia/hardware/block/c/fidl.h>
+#include <fuchsia/hardware/block/partition/c/fidl.h>
+#include <gpt/gpt.h>
+#include <lib/fdio/directory.h>
+#include <lib/fdio/fd.h>
+#include <lib/fdio/fdio.h>
+#include <lib/fdio/unsafe.h>
+#include <lib/fdio/watcher.h>
+#include <lib/fzl/fdio.h>
+#include <lib/fzl/time.h>
+#include <lib/zx/channel.h>
+#include <lib/zx/time.h>
+#include <loader-service/loader-service.h>
+#include <minfs/fsck.h>
+#include <minfs/minfs.h>
+#include <zircon/device/block.h>
+#include <zircon/processargs.h>
+#include <zircon/status.h>
+#include <zircon/syscalls.h>
+#include <zxcrypt/fdio-volume.h>
+
+#include <utility>
+
+#include "block-device.h"
+#include "block-watcher.h"
+#include "pkgfs-launcher.h"
+
+namespace devmgr {
+namespace {
+
+// Attempt to mount the device pointed to be the file descriptor at a known
+// location.
+//
+// Returns ZX_ERR_ALREADY_BOUND if the device could be mounted, but something
+// is already mounted at that location. Returns ZX_ERR_INVALID_ARGS if the
+// GUID of the device does not match a known valid one. Returns
+// ZX_ERR_NOT_SUPPORTED if the GUID is a system GUID. Returns ZX_OK if an
+// attempt to mount is made, without checking mount success.
+zx_status_t MountMinfs(FilesystemMounter* mounter, fbl::unique_fd fd, mount_options_t* options) {
+    fuchsia_hardware_block_partition_GUID type_guid;
+    {
+        fzl::UnownedFdioCaller disk_connection(fd.get());
+        zx::unowned_channel channel(disk_connection.borrow_channel());
+        zx_status_t io_status, status;
+        io_status = fuchsia_hardware_block_partition_PartitionGetTypeGuid(channel->get(), &status,
+                                                                          &type_guid);
+        if (io_status != ZX_OK)
+            return io_status;
+        if (status != ZX_OK)
+            return status;
+    }
+
+    if (gpt_is_sys_guid(type_guid.value, GPT_GUID_LEN)) {
+        return ZX_ERR_NOT_SUPPORTED;
+    } else if (gpt_is_data_guid(type_guid.value, GPT_GUID_LEN)) {
+        return mounter->MountData(std::move(fd), options);
+    } else if (gpt_is_install_guid(type_guid.value, GPT_GUID_LEN)) {
+        return mounter->MountInstall(std::move(fd), options);
+    }
+    printf("fshost: Unrecognized partition GUID for minfs; not mounting\n");
+    return ZX_ERR_WRONG_TYPE;
+}
+
+// return value is ignored
+int UnsealZxcryptThread(void* arg) {
+    std::unique_ptr<int> fd_ptr(static_cast<int*>(arg));
+    fbl::unique_fd fd(*fd_ptr);
+
+    zx_status_t rc;
+    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;
+    }
+
+    zx::channel zxcrypt_volume_manager_chan;
+    if ((rc = zxcrypt_volume->OpenManager(zx::sec(2),
+                                          zxcrypt_volume_manager_chan.reset_and_get_address())) !=
+        ZX_OK) {
+        printf("fshost: couldn't open zxcrypt manager device");
+        return 0;
+    }
+
+    zxcrypt::FdioVolumeManager zxcrypt_volume_manager(std::move(zxcrypt_volume_manager_chan));
+    uint8_t slot = 0;
+    if ((rc = zxcrypt_volume_manager.UnsealWithDeviceKey(slot)) != ZX_OK) {
+        printf("fshost: couldn't unseal zxcrypt manager device");
+        return 0;
+    }
+
+    return 0;
+}
+
+zx_status_t FormatMinfs(const fbl::unique_fd& block_device,
+                        const fuchsia_hardware_block_BlockInfo& info) {
+    fprintf(stderr, "fshost: Formatting minfs.\n");
+    uint64_t device_size = info.block_size * info.block_count / minfs::kMinfsBlockSize;
+    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) {
+        fprintf(stderr, "fshost: Could not initialize minfs bcache.\n");
+        return status;
+    }
+    minfs::MountOptions options = {};
+    if ((status = Mkfs(options, std::move(bc))) != ZX_OK) {
+        fprintf(stderr, "fshost: Could not format minfs filesystem.\n");
+        return status;
+    }
+    printf("fshost: Minfs filesystem re-formatted. Expect data loss.\n");
+    return ZX_OK;
+}
+
+} // namespace
+
+BlockDevice::BlockDevice(FilesystemMounter* mounter, fbl::unique_fd fd)
+    : mounter_(mounter), fd_(std::move(fd)),
+      format_(detect_disk_format(fd_.get())) {}
+
+disk_format_t BlockDevice::GetFormat() {
+    return format_;
+}
+
+void BlockDevice::SetFormat(disk_format_t format) {
+    format_ = format;
+}
+
+bool BlockDevice::Netbooting() {
+    return mounter_->Netbooting();
+}
+
+zx_status_t BlockDevice::GetInfo(fuchsia_hardware_block_BlockInfo* out_info) {
+    if (info_.has_value()) {
+        memcpy(out_info, &*info_, sizeof(*out_info));
+        return ZX_OK;
+    }
+    fzl::UnownedFdioCaller connection(fd_.get());
+    zx_status_t io_status, call_status;
+    io_status = fuchsia_hardware_block_BlockGetInfo(connection.borrow_channel(), &call_status,
+                                                    out_info);
+    if (io_status != ZX_OK) {
+        return io_status;
+    }
+    info_ = *out_info;
+    return call_status;
+}
+
+zx_status_t BlockDevice::GetTypeGUID(fuchsia_hardware_block_partition_GUID* out_guid) {
+    fzl::UnownedFdioCaller connection(fd_.get());
+    zx_status_t io_status, call_status;
+    io_status = fuchsia_hardware_block_partition_PartitionGetTypeGuid(connection.borrow_channel(),
+                                                                      &call_status,
+                                                                      out_guid);
+    if (io_status != ZX_OK) {
+        return io_status;
+    }
+    return call_status;
+}
+
+zx_status_t BlockDevice::AttachDriver(const fbl::StringPiece& driver) {
+    printf("fshost: Binding: %.*s\n", static_cast<int>(driver.length()), driver.data());
+    fzl::UnownedFdioCaller connection(fd_.get());
+    zx_status_t io_status, call_status;
+    io_status = fuchsia_device_ControllerBind(connection.borrow_channel(), driver.data(),
+                                              driver.length(), &call_status);
+    if (io_status != ZX_OK) {
+        return io_status;
+    }
+    return call_status;
+}
+
+zx_status_t BlockDevice::UnsealZxcrypt() {
+    printf("fshost: unsealing zxcrypt\n");
+    // Bind and unseal the driver from a separate thread, since we
+    // have to wait for a number of devices to do I/O and settle,
+    // and we don't want to block block-watcher for any nontrivial
+    // length of time.
+
+    // We transfer fd to the spawned thread.  Since it's UB to cast
+    // ints to pointers and back, we allocate the fd on the heap.
+    int loose_fd = fd_.release();
+    int* raw_fd_ptr = new int(loose_fd);
+    thrd_t th;
+    int err = thrd_create_with_name(&th, &UnsealZxcryptThread, raw_fd_ptr, "zxcrypt-unseal");
+    if (err != thrd_success) {
+        printf("fshost: failed to spawn zxcrypt unseal thread");
+        close(loose_fd);
+        delete raw_fd_ptr;
+    } else {
+        thrd_detach(th);
+    }
+    return ZX_OK;
+}
+
+zx_status_t BlockDevice::CheckFilesystem() {
+    if (!getenv_bool("zircon.system.filesystem-check", false)) {
+        return ZX_OK;
+    }
+
+    zx_status_t status;
+    fuchsia_hardware_block_BlockInfo info;
+    if ((status = GetInfo(&info)) != ZX_OK) {
+        return status;
+    }
+
+    switch (format_) {
+    case DISK_FORMAT_BLOBFS: {
+        fprintf(stderr, "fshost: Skipping blobfs consistency checker.\n");
+        return ZX_OK;
+    }
+    case DISK_FORMAT_MINFS: {
+        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_[format_]);
+        uint64_t device_size = info.block_size * info.block_count / minfs::kMinfsBlockSize;
+        std::unique_ptr<minfs::Bcache> bc;
+        zx_status_t status;
+        if ((status = minfs::Bcache::Create(&bc, fd_.duplicate(),
+                                            static_cast<uint32_t>(device_size))) != ZX_OK) {
+            fprintf(stderr, "fshost: Could not initialize minfs bcache.\n");
+            return status;
+        }
+        status = minfs::Fsck(std::move(bc));
+
+        if (status != ZX_OK) {
+            fprintf(stderr, "--------------------------------------------------------------\n");
+            fprintf(stderr, "|                                                             \n");
+            fprintf(stderr, "|   WARNING: fshost fsck failure!                             \n");
+            fprintf(stderr, "|   Corrupt %s filesystem\n", disk_format_string_[format_]);
+            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_[format_]);
+        }
+        return status;
+    }
+    default:
+        fprintf(stderr, "fshost: Not checking unknown filesystem\n");
+        return ZX_ERR_NOT_SUPPORTED;
+    }
+}
+
+zx_status_t BlockDevice::FormatFilesystem() {
+    zx_status_t status;
+    fuchsia_hardware_block_BlockInfo info;
+    if ((status = GetInfo(&info)) != ZX_OK) {
+        return status;
+    }
+
+    switch (format_) {
+    case DISK_FORMAT_BLOBFS: {
+        fprintf(stderr, "fshost: Not formatting blobfs.\n");
+        return ZX_ERR_NOT_SUPPORTED;
+    }
+    case DISK_FORMAT_MINFS: {
+        fprintf(stderr, "fshost: Formatting minfs.\n");
+        uint64_t blocks = info.block_size * info.block_count / minfs::kMinfsBlockSize;
+        std::unique_ptr<minfs::Bcache> bc;
+        zx_status_t status;
+        if ((status = minfs::Bcache::Create(&bc, fd_.duplicate(),
+                                            static_cast<uint32_t>(blocks))) != ZX_OK) {
+            fprintf(stderr, "fshost: Could not initialize minfs bcache.\n");
+            return status;
+        }
+        minfs::MountOptions options = {};
+        if ((status = minfs::Mkfs(options, std::move(bc))) != ZX_OK) {
+            fprintf(stderr, "fshost: Could not format minfs filesystem.\n");
+            return status;
+        }
+        printf("fshost: Minfs filesystem re-formatted. Expect data loss.\n");
+        return ZX_OK;
+    }
+    default:
+        fprintf(stderr, "fshost: Not formatting unknown filesystem.\n");
+        return ZX_ERR_NOT_SUPPORTED;
+    }
+}
+
+zx_status_t BlockDevice::MountFilesystem() {
+    // Go through the song-and-dance of cloning our reference to |fd_|
+    // so we can hand off a cloned connection to the mount functions.
+    // The mount functions are very possessive of their fds, and don't like
+    // operating on dup-ed descriptors.
+    //
+    // In the future, this could be simplified by passing channels directly,
+    // and avoiding file descriptors altogether.
+    fbl::unique_fd cloned_fd;
+    {
+        fzl::UnownedFdioCaller disk_connection(fd_.get());
+        zx::unowned_channel channel(disk_connection.borrow_channel());
+        zx::channel cloned_channel(fdio_service_clone(channel->get()));
+        fdio_t* io;
+        zx_status_t status = fdio_create(cloned_channel.release(), &io);
+        if (status != ZX_OK) {
+            return status;
+        }
+        cloned_fd.reset(fdio_bind_to_fd(io, -1, 0));
+        if (!cloned_fd) {
+            return ZX_ERR_BAD_STATE;
+        }
+    }
+
+    switch (format_) {
+    case DISK_FORMAT_BLOBFS: {
+        fprintf(stderr, "fshost: BlockDevice::MountFilesystem(blobfs)\n");
+        mount_options_t options = default_mount_options;
+        options.enable_journal = true;
+        options.collect_metrics = true;
+        zx_status_t status = mounter_->MountBlob(std::move(cloned_fd), &options);
+        if (status != ZX_OK) {
+            printf("fshost: Failed to mount blobfs partition: %s.\n",
+                   zx_status_get_string(status));
+            return status;
+        } else {
+            LaunchBlobInit(mounter_);
+        }
+        return ZX_OK;
+    }
+    case DISK_FORMAT_MINFS: {
+        mount_options_t options = default_mount_options;
+        fprintf(stderr, "fshost: BlockDevice::MountFilesystem(minfs)\n");
+        return MountMinfs(mounter_, std::move(cloned_fd), &options);
+    }
+    default:
+        fprintf(stderr, "fshost: BlockDevice::MountFilesystem(unknown)\n");
+        return ZX_ERR_NOT_SUPPORTED;
+    }
+}
+
+zx_status_t BlockDeviceInterface::Add() {
+    disk_format_t df = GetFormat();
+    fuchsia_hardware_block_BlockInfo info;
+    zx_status_t status;
+    if ((status = GetInfo(&info)) != ZX_OK) {
+        return status;
+    }
+
+    if (info.flags & BLOCK_FLAG_BOOTPART) {
+        return AttachDriver(kBootpartDriverPath);
+    }
+
+    switch (df) {
+    case DISK_FORMAT_GPT: {
+        return AttachDriver(kGPTDriverPath);
+    }
+    case DISK_FORMAT_FVM: {
+        return AttachDriver(kFVMDriverPath);
+    }
+    case DISK_FORMAT_MBR: {
+        return AttachDriver(kMBRDriverPath);
+    }
+    case DISK_FORMAT_ZXCRYPT: {
+        if (!Netbooting()) {
+            return UnsealZxcrypt();
+        }
+        return ZX_OK;
+    }
+    default:
+        break;
+    }
+
+    fuchsia_hardware_block_partition_GUID guid;
+    if ((status = GetTypeGUID(&guid)) != ZX_OK) {
+        return status;
+    }
+
+    // If we're in netbooting mode, then only bind drivers for partition
+    // containers and the install partition, not regular filesystems.
+    if (Netbooting()) {
+        if (gpt_is_install_guid(guid.value, GPT_GUID_LEN)) {
+            printf("fshost: mounting install partition\n");
+            return MountFilesystem();
+        }
+        return ZX_OK;
+    }
+
+    switch (df) {
+    case DISK_FORMAT_BLOBFS: {
+        const uint8_t expected_guid[GPT_GUID_LEN] = GUID_BLOB_VALUE;
+
+        if (memcmp(guid.value, expected_guid, GPT_GUID_LEN)) {
+            return ZX_ERR_INVALID_ARGS;
+        }
+        if ((status = CheckFilesystem()) != ZX_OK) {
+            return status;
+        }
+
+        return MountFilesystem();
+    }
+    case DISK_FORMAT_MINFS: {
+        printf("fshost: mounting minfs\n");
+        if (CheckFilesystem() != ZX_OK) {
+            if ((status = FormatFilesystem()) != ZX_OK) {
+                return status;
+            }
+        }
+        status = MountFilesystem();
+        if (status != ZX_OK) {
+            printf("fshost: failed to mount filesystem: %s\n", zx_status_get_string(status));
+            return status;
+        }
+        return ZX_OK;
+    }
+    default:
+        return ZX_ERR_NOT_SUPPORTED;
+    }
+}
+
+} // namespace devmgr
diff --git a/zircon/system/core/devmgr/fshost/block-device.h b/zircon/system/core/devmgr/fshost/block-device.h
new file mode 100644
index 0000000000000000000000000000000000000000..c12e9df57f31b4cad31db0aeaf8cebd53cd89cfa
--- /dev/null
+++ b/zircon/system/core/devmgr/fshost/block-device.h
@@ -0,0 +1,44 @@
+// 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 <optional>
+
+#include <fs-management/mount.h>
+#include <zircon/types.h>
+
+#include "block-device-interface.h"
+#include "filesystem-mounter.h"
+
+namespace devmgr {
+
+// A concrete implementation of the block device interface.
+//
+// Used by fshost to attach either drivers or filesystems to
+// incoming block devices.
+class BlockDevice final : public BlockDeviceInterface {
+public:
+    BlockDevice(FilesystemMounter* mounter, fbl::unique_fd fd);
+
+    disk_format_t GetFormat() final;
+    void SetFormat(disk_format_t format) final;
+    bool Netbooting() final;
+    zx_status_t GetInfo(fuchsia_hardware_block_BlockInfo* out_info) final;
+    zx_status_t GetTypeGUID(fuchsia_hardware_block_partition_GUID* out_guid) final;
+    zx_status_t AttachDriver(const fbl::StringPiece& driver) final;
+    zx_status_t UnsealZxcrypt() final;
+    zx_status_t CheckFilesystem() final;
+    zx_status_t FormatFilesystem() final;
+    zx_status_t MountFilesystem() final;
+
+private:
+    FilesystemMounter* mounter_ = nullptr;
+    fbl::unique_fd fd_;
+    std::optional<fuchsia_hardware_block_BlockInfo> info_ = {};
+    disk_format_t format_ = DISK_FORMAT_UNKNOWN;
+};
+
+} // namespace devmgr
diff --git a/zircon/system/core/devmgr/fshost/block-watcher-test.cpp b/zircon/system/core/devmgr/fshost/block-watcher-test.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b2a6c475ff850dfdfa413c22fd342eccd5a14018
--- /dev/null
+++ b/zircon/system/core/devmgr/fshost/block-watcher-test.cpp
@@ -0,0 +1,261 @@
+// 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.
+
+#include <zircon/assert.h>
+#include <zircon/hw/gpt.h>
+#include <zxtest/zxtest.h>
+
+#include "block-device-interface.h"
+
+namespace {
+
+class MockBlockDevice : public devmgr::BlockDeviceInterface {
+public:
+    disk_format_t GetFormat() override = 0;
+    void SetFormat(disk_format_t format) override {
+        ZX_PANIC("Test should not invoke function %s\n", __FUNCTION__);
+    }
+    bool Netbooting() override { return false; }
+    zx_status_t GetInfo(fuchsia_hardware_block_BlockInfo* out_info) override {
+        fuchsia_hardware_block_BlockInfo info = {};
+        info.flags = 0;
+        info.block_size = 512;
+        info.block_count = 1024;
+        *out_info = info;
+        return ZX_OK;
+    }
+    zx_status_t GetTypeGUID(fuchsia_hardware_block_partition_GUID* out_guid) override {
+        ZX_PANIC("Test should not invoke function %s\n", __FUNCTION__);
+    }
+    zx_status_t AttachDriver(const fbl::StringPiece& driver) override {
+        ZX_PANIC("Test should not invoke function %s\n", __FUNCTION__);
+    }
+    zx_status_t UnsealZxcrypt() override {
+        ZX_PANIC("Test should not invoke function %s\n", __FUNCTION__);
+    }
+    zx_status_t CheckFilesystem() override {
+        ZX_PANIC("Test should not invoke function %s\n", __FUNCTION__);
+    }
+    zx_status_t FormatFilesystem() override {
+        ZX_PANIC("Test should not invoke function %s\n", __FUNCTION__);
+    }
+    zx_status_t MountFilesystem() override {
+        ZX_PANIC("Test should not invoke function %s\n", __FUNCTION__);
+    }
+};
+
+// Tests adding a device which has no GUID and an unknown format.
+TEST(AddDeviceTestCase, AddUnknownDevice) {
+    class UnknownDevice : public MockBlockDevice {
+    public:
+        disk_format_t GetFormat() final {
+            return DISK_FORMAT_UNKNOWN;
+        }
+        zx_status_t GetTypeGUID(fuchsia_hardware_block_partition_GUID* out_guid) final {
+            return ZX_ERR_NOT_SUPPORTED;
+        }
+    };
+    UnknownDevice device;
+    EXPECT_EQ(ZX_ERR_NOT_SUPPORTED, device.Add());
+}
+
+// Tests adding a device with an unknown GUID and unknown format.
+TEST(AddDeviceTestCase, AddUnknownPartition) {
+    class UnknownDevice : public MockBlockDevice {
+    public:
+        disk_format_t GetFormat() final {
+            return DISK_FORMAT_UNKNOWN;
+        }
+        zx_status_t GetTypeGUID(fuchsia_hardware_block_partition_GUID* out_guid) final {
+            const uint8_t expected[GPT_GUID_LEN] = GUID_EMPTY_VALUE;
+            memcpy(out_guid->value, expected, sizeof(expected));
+            return ZX_OK;
+        }
+    };
+    UnknownDevice device;
+    EXPECT_EQ(ZX_ERR_NOT_SUPPORTED, device.Add());
+}
+
+// Tests adding a device with a GPT format.
+TEST(AddDeviceTestCase, AddGPTDevice) {
+    class GptDevice : public MockBlockDevice {
+    public:
+        disk_format_t GetFormat() final {
+            return DISK_FORMAT_GPT;
+        }
+        zx_status_t AttachDriver(const fbl::StringPiece& driver) final {
+            EXPECT_STR_EQ(devmgr::kGPTDriverPath, driver.data());
+            attached = true;
+            return ZX_OK;
+        }
+        bool attached = false;
+    };
+    GptDevice device;
+    EXPECT_EQ(ZX_OK, device.Add());
+    EXPECT_TRUE(device.attached);
+}
+
+// Tests adding a device with an FVM format.
+TEST(AddDeviceTestCase, AddFVMDevice) {
+    class FvmDevice : public MockBlockDevice {
+    public:
+        disk_format_t GetFormat() final {
+            return DISK_FORMAT_FVM;
+        }
+        zx_status_t AttachDriver(const fbl::StringPiece& driver) final {
+            EXPECT_STR_EQ(devmgr::kFVMDriverPath, driver.data());
+            attached = true;
+            return ZX_OK;
+        }
+        bool attached = false;
+    };
+    FvmDevice device;
+    EXPECT_EQ(ZX_OK, device.Add());
+    EXPECT_TRUE(device.attached);
+}
+
+// Tests adding a device with an MBR format.
+TEST(AddDeviceTestCase, AddMBRDevice) {
+    class MbrDevice : public MockBlockDevice {
+    public:
+        disk_format_t GetFormat() final {
+            return DISK_FORMAT_MBR;
+        }
+        zx_status_t AttachDriver(const fbl::StringPiece& driver) final {
+            EXPECT_STR_EQ(devmgr::kMBRDriverPath, driver.data());
+            attached = true;
+            return ZX_OK;
+        }
+        bool attached = false;
+    };
+    MbrDevice device;
+    EXPECT_EQ(ZX_OK, device.Add());
+    EXPECT_TRUE(device.attached);
+}
+
+// Tests adding blobfs which does not not have a type GUID.
+TEST(AddDeviceTestCase, AddNoGUIDBlobDevice) {
+    class BlobDevice : public MockBlockDevice {
+    public:
+        disk_format_t GetFormat() final {
+            return DISK_FORMAT_BLOBFS;
+        }
+        zx_status_t GetTypeGUID(fuchsia_hardware_block_partition_GUID* out_guid) final {
+            return ZX_ERR_NOT_SUPPORTED;
+        }
+    };
+    BlobDevice device;
+    EXPECT_EQ(ZX_ERR_NOT_SUPPORTED, device.Add());
+}
+
+// Tests adding blobfs with a valid type GUID, but invalid metadata.
+TEST(AddDeviceTestCase, AddInvalidBlobDevice) {
+    class BlobDevice : public MockBlockDevice {
+    public:
+        disk_format_t GetFormat() final {
+            return DISK_FORMAT_BLOBFS;
+        }
+        zx_status_t GetTypeGUID(fuchsia_hardware_block_partition_GUID* out_guid) final {
+            const uint8_t expected[GPT_GUID_LEN] = GUID_BLOB_VALUE;
+            memcpy(out_guid->value, expected, sizeof(expected));
+            return ZX_OK;
+        }
+        zx_status_t CheckFilesystem() final {
+            checked = true;
+            return ZX_ERR_BAD_STATE;
+        }
+        zx_status_t FormatFilesystem() final {
+            formatted = true;
+            return ZX_OK;
+        }
+        zx_status_t MountFilesystem() final {
+            mounted = true;
+            return ZX_OK;
+        }
+
+        bool checked = false;
+        bool formatted = false;
+        bool mounted = false;
+    };
+    BlobDevice device;
+    EXPECT_EQ(ZX_ERR_BAD_STATE, device.Add());
+    EXPECT_TRUE(device.checked);
+    EXPECT_FALSE(device.formatted);
+    EXPECT_FALSE(device.mounted);
+}
+
+// Tests adding blobfs with a valid type GUID and valid metadata.
+TEST(AddDeviceTestCase, AddValidBlobDevice) {
+    class BlobDevice : public MockBlockDevice {
+    public:
+        disk_format_t GetFormat() final {
+            return DISK_FORMAT_BLOBFS;
+        }
+        zx_status_t GetTypeGUID(fuchsia_hardware_block_partition_GUID* out_guid) final {
+            const uint8_t expected[GPT_GUID_LEN] = GUID_BLOB_VALUE;
+            memcpy(out_guid->value, expected, sizeof(expected));
+            return ZX_OK;
+        }
+        zx_status_t CheckFilesystem() final {
+            checked = true;
+            return ZX_OK;
+        }
+        zx_status_t FormatFilesystem() final {
+            formatted = true;
+            return ZX_OK;
+        }
+        zx_status_t MountFilesystem() final {
+            mounted = true;
+            return ZX_OK;
+        }
+
+        bool checked = false;
+        bool formatted = false;
+        bool mounted = false;
+    };
+    BlobDevice device;
+    EXPECT_EQ(ZX_OK, device.Add());
+    EXPECT_TRUE(device.checked);
+    EXPECT_FALSE(device.formatted);
+    EXPECT_TRUE(device.mounted);
+}
+
+// Tests adding minfs with a valid type GUID and invalid metadata. Observe that
+// the filesystem reformats itself.
+TEST(AddDeviceTestCase, AddInvalidMinfsDevice) {
+    class MinfsDevice : public MockBlockDevice {
+    public:
+        disk_format_t GetFormat() final {
+            return DISK_FORMAT_MINFS;
+        }
+        zx_status_t GetTypeGUID(fuchsia_hardware_block_partition_GUID* out_guid) final {
+            const uint8_t expected[GPT_GUID_LEN] = GUID_DATA_VALUE;
+            memcpy(out_guid->value, expected, sizeof(expected));
+            return ZX_OK;
+        }
+        zx_status_t CheckFilesystem() final {
+            checked = true;
+            return ZX_ERR_BAD_STATE;
+        }
+        zx_status_t FormatFilesystem() final {
+            formatted = true;
+            return ZX_OK;
+        }
+        zx_status_t MountFilesystem() final {
+            mounted = true;
+            return ZX_OK;
+        }
+
+        bool checked = false;
+        bool formatted = false;
+        bool mounted = false;
+    };
+    MinfsDevice device;
+    EXPECT_EQ(ZX_OK, device.Add());
+    EXPECT_TRUE(device.checked);
+    EXPECT_TRUE(device.formatted);
+    EXPECT_TRUE(device.mounted);
+}
+
+} // namespace
diff --git a/zircon/system/core/devmgr/fshost/block-watcher.cpp b/zircon/system/core/devmgr/fshost/block-watcher.cpp
index 8c9b4e36d8b2234bdc1633993c6815bbac6188bd..4266d5d4f3f07678599f52d67acf4d4982f371c0 100644
--- a/zircon/system/core/devmgr/fshost/block-watcher.cpp
+++ b/zircon/system/core/devmgr/fshost/block-watcher.cpp
@@ -10,7 +10,10 @@
 
 #include <fbl/algorithm.h>
 #include <fbl/auto_call.h>
+#include <fbl/string.h>
 #include <fbl/string_buffer.h>
+#include <fbl/string_piece.h>
+#include <fbl/string_printf.h>
 #include <fbl/unique_fd.h>
 #include <fs-management/mount.h>
 #include <fuchsia/device/c/fidl.h>
@@ -29,7 +32,6 @@
 #include <lib/zx/time.h>
 #include <loader-service/loader-service.h>
 #include <minfs/minfs.h>
-#include <ramdevice-client/ramdisk.h>
 #include <zircon/device/block.h>
 #include <zircon/processargs.h>
 #include <zircon/status.h>
@@ -38,410 +40,38 @@
 
 #include <utility>
 
+#include "block-device.h"
 #include "block-watcher.h"
 #include "pkgfs-launcher.h"
 
 namespace devmgr {
 namespace {
 
-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,
-                         -1, hnd, ids, len, nullptr, FS_FOR_FSPROC);
-}
-
-zx_status_t LaunchMinfs(int argc, const char** argv, zx_handle_t* hnd, uint32_t* ids, size_t len) {
-    return devmgr_launch(*zx::job::default_job(), "minfs:/data", argv, nullptr,
-                         -1, hnd, ids, len, nullptr, FS_FOR_FSPROC);
-}
-
-zx_status_t LaunchFAT(int argc, const char** argv, zx_handle_t* hnd, uint32_t* ids, size_t len) {
-    return devmgr_launch(*zx::job::default_job(), "fatfs:/volume", argv, nullptr,
-                         -1, hnd, ids, len, nullptr, FS_FOR_FSPROC);
-}
-
-// Attempt to mount the device pointed to be the file descriptor at a known
-// location.
-//
-// Returns ZX_ERR_ALREADY_BOUND if the device could be mounted, but something
-// is already mounted at that location. Returns ZX_ERR_INVALID_ARGS if the
-// GUID of the device does not match a known valid one. Returns
-// ZX_ERR_NOT_SUPPORTED if the GUID is a system GUID. Returns ZX_OK if an
-// attempt to mount is made, without checking mount success.
-zx_status_t MountMinfs(FilesystemMounter* filesystems, fbl::unique_fd fd, mount_options_t* options) {
-    fuchsia_hardware_block_partition_GUID type_guid;
-    {
-        fzl::UnownedFdioCaller disk_connection(fd.get());
-        zx::unowned_channel channel(disk_connection.borrow_channel());
-        zx_status_t io_status, status;
-        io_status = fuchsia_hardware_block_partition_PartitionGetTypeGuid(channel->get(), &status,
-                                                                          &type_guid);
-        if (io_status != ZX_OK)
-            return io_status;
-        if (status != ZX_OK)
-            return status;
-    }
-
-    if (gpt_is_sys_guid(type_guid.value, GPT_GUID_LEN)) {
-        return ZX_ERR_NOT_SUPPORTED;
-    } else if (gpt_is_data_guid(type_guid.value, GPT_GUID_LEN)) {
-        return filesystems->MountData(std::move(fd), options);
-    } else if (gpt_is_install_guid(type_guid.value, GPT_GUID_LEN)) {
-        return filesystems->MountInstall(std::move(fd), options);
-    }
-    printf("fshost: Unrecognized partition GUID for minfs; not mounting\n");
-    return ZX_ERR_INVALID_ARGS;
-}
-
-#define FVM_DRIVER_LIB "/boot/driver/fvm.so"
-#define GPT_DRIVER_LIB "/boot/driver/gpt.so"
-#define MBR_DRIVER_LIB "/boot/driver/mbr.so"
-#define BOOTPART_DRIVER_LIB "/boot/driver/bootpart.so"
-#define ZXCRYPT_DRIVER_LIB "/boot/driver/zxcrypt.so"
-#define STRLEN(s) (sizeof(s) / sizeof((s)[0]))
-
-// return value is ignored
-int UnsealZxcrypt(void* arg) {
-    std::unique_ptr<int> fd_ptr(static_cast<int*>(arg));
-    fbl::unique_fd fd(*fd_ptr);
-
-    zx_status_t rc;
-    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;
-    }
-
-    zx::channel zxcrypt_volume_manager_chan;
-    if ((rc = zxcrypt_volume->OpenManager(zx::sec(2), zxcrypt_volume_manager_chan.reset_and_get_address())) != ZX_OK) {
-        printf("fshost: couldn't open zxcrypt manager device");
-        return 0;
-    }
-
-    zxcrypt::FdioVolumeManager zxcrypt_volume_manager(std::move(zxcrypt_volume_manager_chan));
-    uint8_t slot = 0;
-    if ((rc = zxcrypt_volume_manager.UnsealWithDeviceKey(slot)) != ZX_OK) {
-        printf("fshost: couldn't unseal zxcrypt manager device");
-        return 0;
-    }
-
-    return 0;
-}
-
-zx_status_t FormatMinfs(const fbl::unique_fd& block_device,
-                        const fuchsia_hardware_block_BlockInfo& info) {
-
-    fprintf(stderr, "fshost: Formatting minfs.\n");
-    uint64_t device_size = info.block_size * info.block_count;
-    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) {
-        fprintf(stderr, "fshost: Could not initialize minfs bcache.\n");
-        return status;
-    }
-    minfs::MountOptions options = {};
-    if ((status = Mkfs(options, std::move(bc))) != ZX_OK) {
-        fprintf(stderr, "fshost: Could not format minfs filesystem.\n");
-        return status;
-    }
-    printf("fshost: Minfs filesystem re-formatted. Expect data loss.\n");
-    return ZX_OK;
-}
-
-zx_status_t BlockDeviceAdded(int dirfd, int event, const char* name, void* cookie) {
-    auto filesystems = static_cast<FilesystemMounter*>(cookie);
+constexpr char kPathBlockDeviceRoot[] = "/dev/class/block";
 
+zx_status_t BlockDeviceCallback(int dirfd, int event, const char* name, void* cookie) {
     if (event != WATCH_EVENT_ADD_FILE) {
         return ZX_OK;
     }
-
-    char device_path[PATH_MAX];
-    sprintf(device_path, "%s/%s", PATH_DEV_BLOCK, name);
-
-    fbl::unique_fd fd(openat(dirfd, name, O_RDWR));
-    if (!fd) {
-        return ZX_OK;
-    }
-
-    disk_format_t df = detect_disk_format(fd.get());
-    fuchsia_hardware_block_BlockInfo info;
-    fuchsia_hardware_block_partition_GUID guid;
-    {
-        fzl::UnownedFdioCaller disk_connection(fd.get());
-        zx::unowned_channel disk(disk_connection.borrow_channel());
-
-        zx_status_t io_status, call_status;
-        io_status = fuchsia_hardware_block_BlockGetInfo(disk->get(), &call_status, &info);
-        if (io_status != ZX_OK || call_status != ZX_OK) {
-            return ZX_OK;
-        }
-
-        if (info.flags & BLOCK_FLAG_BOOTPART) {
-            fuchsia_device_ControllerBind(disk->get(), BOOTPART_DRIVER_LIB,
-                                          STRLEN(BOOTPART_DRIVER_LIB), &call_status);
-            return ZX_OK;
-        }
-
-        switch (df) {
-        case DISK_FORMAT_GPT: {
-            printf("fshost: %s: GPT?\n", device_path);
-            // probe for partition table
-            fuchsia_device_ControllerBind(disk->get(), GPT_DRIVER_LIB, STRLEN(GPT_DRIVER_LIB),
-                                          &call_status);
-            return ZX_OK;
-        }
-        case DISK_FORMAT_FVM: {
-            printf("fshost: /dev/class/block/%s: FVM?\n", name);
-            // probe for partition table
-            fuchsia_device_ControllerBind(disk->get(), FVM_DRIVER_LIB, STRLEN(FVM_DRIVER_LIB),
-                                          &call_status);
-            return ZX_OK;
-        }
-        case DISK_FORMAT_MBR: {
-            printf("fshost: %s: MBR?\n", device_path);
-            // probe for partition table
-            fuchsia_device_ControllerBind(disk->get(), MBR_DRIVER_LIB, STRLEN(MBR_DRIVER_LIB),
-                                          &call_status);
-            return ZX_OK;
-        }
-        case DISK_FORMAT_ZXCRYPT: {
-            if (!filesystems->Netbooting()) {
-                printf("fshost: %s: zxcrypt?\n", device_path);
-                // Bind and unseal the driver from a separate thread, since we
-                // have to wait for a number of devices to do I/O and settle,
-                // and we don't want to block block-watcher for any nontrivial
-                // length of time.
-
-                // We transfer fd to the spawned thread.  Since it's UB to cast
-                // ints to pointers and back, we allocate the fd on the heap.
-                int loose_fd = fd.release();
-                int* raw_fd_ptr = new int(loose_fd);
-                thrd_t th;
-                int err = thrd_create_with_name(&th, &UnsealZxcrypt, raw_fd_ptr, "zxcrypt-unseal");
-                if (err != thrd_success) {
-                    printf("fshost: failed to spawn zxcrypt unseal thread");
-                    close(loose_fd);
-                    delete raw_fd_ptr;
-                } else {
-                    thrd_detach(th);
-                }
-            }
-            return ZX_OK;
-        }
-        default:
-            break;
-        }
-
-        io_status = fuchsia_hardware_block_partition_PartitionGetTypeGuid(disk->get(), &call_status,
-                                                                          &guid);
-        if (io_status != ZX_OK || call_status != ZX_OK) {
-            return ZX_OK;
-        }
-    }
-
-    // If we're in netbooting mode, then only bind drivers for partition
-    // containers and the install partition, not regular filesystems.
-    if (filesystems->Netbooting()) {
-        if (gpt_is_install_guid(guid.value, GPT_GUID_LEN)) {
-            printf("fshost: mounting install partition\n");
-            mount_options_t options = default_mount_options;
-            MountMinfs(filesystems, std::move(fd), &options);
-            return ZX_OK;
-        }
-
+    fbl::unique_fd device_fd(openat(dirfd, name, O_RDWR));
+    if (!device_fd) {
         return ZX_OK;
     }
 
-    switch (df) {
-    case DISK_FORMAT_BLOBFS: {
-        const uint8_t expected_guid[GPT_GUID_LEN] = GUID_BLOB_VALUE;
-
-        if (memcmp(guid.value, expected_guid, GPT_GUID_LEN)) {
-            return ZX_OK;
-        }
-        fsck_options_t fsck_options = default_fsck_options;
-        fsck_options.apply_journal = true;
-        if (filesystems->CheckFilesystem(device_path, DISK_FORMAT_BLOBFS, &fsck_options) != ZX_OK) {
-            return ZX_OK;
-        }
-
-        mount_options_t options = default_mount_options;
-        options.enable_journal = true;
-        options.collect_metrics = true;
-        zx_status_t status = filesystems->MountBlob(std::move(fd), &options);
-        if (status != ZX_OK) {
-            printf("fshost: Failed to mount blobfs partition %s at %s: %s.\n", device_path,
-                   PATH_BLOB, zx_status_get_string(status));
-        } else {
-            LaunchBlobInit(filesystems);
-        }
-        return ZX_OK;
-    }
-    case DISK_FORMAT_MINFS: {
-        printf("fshost: mounting minfs\n");
-        fsck_options_t fsck_options = default_fsck_options;
-        if (filesystems->CheckFilesystem(device_path, DISK_FORMAT_MINFS, &fsck_options) != ZX_OK) {
-            if (FormatMinfs(fd, info) != ZX_OK) {
-                return ZX_OK;
-            }
-        }
-        mount_options_t options = default_mount_options;
-        MountMinfs(filesystems, std::move(fd), &options);
-        return ZX_OK;
-    }
-    case DISK_FORMAT_FAT: {
-        // Use the GUID to avoid auto-mounting the EFI partition
-        bool efi = gpt_is_efi_guid(guid.value, GPT_GUID_LEN);
-        if (efi) {
-            printf("fshost: not automounting efi\n");
-            return ZX_OK;
-        }
-        mount_options_t options = default_mount_options;
-        options.create_mountpoint = true;
-        static int fat_counter = 0;
-        char mountpath[FDIO_MAX_FILENAME + 64];
-        snprintf(mountpath, sizeof(mountpath), "%s/fat-%d", "/fs" PATH_VOLUME, fat_counter++);
-        options.wait_until_ready = false;
-        printf("fshost: mounting fatfs\n");
-        mount(fd.release(), mountpath, df, &options, LaunchFAT);
-        return ZX_OK;
-    }
-    default:
-        return ZX_OK;
-    }
+    auto mounter = static_cast<FilesystemMounter*>(cookie);
+    BlockDevice device(mounter, std::move(device_fd));
+    device.Add();
+    return ZX_OK;
 }
 
 } // namespace
 
-zx_status_t FilesystemMounter::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 FilesystemMounter::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 FilesystemMounter::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 FilesystemMounter::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) {
-    FilesystemMounter filesystems(std::move(fshost), netboot);
+    FilesystemMounter mounter(std::move(fshost), netboot);
 
-    fbl::unique_fd dirfd(open("/dev/class/block", O_DIRECTORY | O_RDONLY));
+    fbl::unique_fd dirfd(open(kPathBlockDeviceRoot, O_DIRECTORY | O_RDONLY));
     if (dirfd) {
-        fdio_watch_directory(dirfd.get(), BlockDeviceAdded, ZX_TIME_INFINITE, &filesystems);
+        fdio_watch_directory(dirfd.get(), BlockDeviceCallback, ZX_TIME_INFINITE, &mounter);
     }
 }
 
diff --git a/zircon/system/core/devmgr/fshost/block-watcher.h b/zircon/system/core/devmgr/fshost/block-watcher.h
index d4c01a54e8f88b064884abb33961d8e0f0ead54a..115a2603629228216c7390ed76be24ac1ca3a537 100644
--- a/zircon/system/core/devmgr/fshost/block-watcher.h
+++ b/zircon/system/core/devmgr/fshost/block-watcher.h
@@ -6,53 +6,10 @@
 
 #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 FilesystemMounter {
-public:
-    FilesystemMounter(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);
 
diff --git a/zircon/system/core/devmgr/fshost/filesystem-mounter.cpp b/zircon/system/core/devmgr/fshost/filesystem-mounter.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4c1f6ac5d1459ed4426546fdb84ca59d8c4dc247
--- /dev/null
+++ b/zircon/system/core/devmgr/fshost/filesystem-mounter.cpp
@@ -0,0 +1,75 @@
+// 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 <fbl/unique_fd.h>
+#include <fs-management/mount.h>
+#include <lib/zx/process.h>
+#include <minfs/minfs.h>
+#include <zircon/status.h>
+
+#include "filesystem-mounter.h"
+#include "pkgfs-launcher.h"
+
+namespace devmgr {
+namespace {
+
+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,
+                         -1, hnd, ids, len, nullptr, FS_FOR_FSPROC);
+}
+
+zx_status_t LaunchMinfs(int argc, const char** argv, zx_handle_t* hnd, uint32_t* ids, size_t len) {
+    return devmgr_launch(*zx::job::default_job(), "minfs:/data", argv, nullptr,
+                         -1, hnd, ids, len, nullptr, FS_FOR_FSPROC);
+}
+
+} // namespace
+
+zx_status_t FilesystemMounter::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 FilesystemMounter::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 FilesystemMounter::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;
+}
+
+} // namespace devmgr
diff --git a/zircon/system/core/devmgr/fshost/filesystem-mounter.h b/zircon/system/core/devmgr/fshost/filesystem-mounter.h
new file mode 100644
index 0000000000000000000000000000000000000000..656ab6a420bed94a80a67e429f41146764c168fe
--- /dev/null
+++ b/zircon/system/core/devmgr/fshost/filesystem-mounter.h
@@ -0,0 +1,53 @@
+// 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 {
+
+// FilesystemMounter is a utility class which wraps the Fsmanager
+// and helps clients mount filesystems within the fshost namespace.
+class FilesystemMounter {
+public:
+    FilesystemMounter(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_; }
+
+    // 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;
+};
+
+} // namespace devmgr
diff --git a/zircon/system/core/devmgr/fshost/fs-manager.cpp b/zircon/system/core/devmgr/fshost/fs-manager.cpp
index 605f31afcbc81f469d7e024a2e2e183258aba3b3..ba904cef1a0216e70765d0e12b0aea92a21d9d5a 100644
--- a/zircon/system/core/devmgr/fshost/fs-manager.cpp
+++ b/zircon/system/core/devmgr/fshost/fs-manager.cpp
@@ -105,6 +105,16 @@ FsManager::FsManager(zx::event fshost_event)
     ZX_ASSERT(global_root_ == nullptr);
 }
 
+// In the event that we haven't been explicitly signalled, tear ourself down.
+FsManager::~FsManager() {
+    if (global_shutdown_.has_handler()) {
+        event_.signal(0, FSHOST_SIGNAL_EXIT);
+        auto deadline = zx::deadline_after(zx::sec(2));
+        zx_signals_t pending;
+        event_.wait_one(FSHOST_SIGNAL_EXIT_DONE, deadline, &pending);
+    }
+}
+
 zx_status_t FsManager::Create(zx::event fshost_event, fbl::unique_ptr<FsManager>* out) {
     auto fs_manager = fbl::unique_ptr<FsManager>(new FsManager(std::move(fshost_event)));
     zx_status_t status = fs_manager->Initialize();
diff --git a/zircon/system/core/devmgr/fshost/fs-manager.h b/zircon/system/core/devmgr/fshost/fs-manager.h
index 776f5e52345785bbf3ba404773cbcadd9be9dfe5..cca2ddeb3c069edce5a23233eb09a0e16952173a 100644
--- a/zircon/system/core/devmgr/fshost/fs-manager.h
+++ b/zircon/system/core/devmgr/fshost/fs-manager.h
@@ -27,6 +27,8 @@ class FsManager {
 public:
     static zx_status_t Create(zx::event fshost_event, fbl::unique_ptr<FsManager>* out);
 
+    ~FsManager();
+
     // Signals that "/system" has been mounted.
     void FuchsiaStart() const { event_.signal(0, FSHOST_SIGNAL_READY); }
 
diff --git a/zircon/system/core/devmgr/fshost/pkgfs-launcher.h b/zircon/system/core/devmgr/fshost/pkgfs-launcher.h
index aa8f01b24c316df87eed45942355ae54be54de2c..c9f4b66bfaace73f957f85dd10d17ee46623d6ee 100644
--- a/zircon/system/core/devmgr/fshost/pkgfs-launcher.h
+++ b/zircon/system/core/devmgr/fshost/pkgfs-launcher.h
@@ -4,7 +4,7 @@
 
 #pragma once
 
-#include "block-watcher.h"
+#include "filesystem-mounter.h"
 
 namespace devmgr {
 
diff --git a/zircon/system/utest/BUILD.gn b/zircon/system/utest/BUILD.gn
index 8dab404f96c7e023592f6034d54d69ac91858e68..31e1717cbe65cd2a892d8b3b80032eb9b97a2148 100644
--- a/zircon/system/utest/BUILD.gn
+++ b/zircon/system/utest/BUILD.gn
@@ -14,6 +14,7 @@ if (current_cpu != "") {
       ":host",  # TODO(mcgrathr): reach this differently?
       "$zx/system/core/bootsvc:bootsvc-tests",
       "$zx/system/core/devmgr/devcoordinator:devcoordinator-test",
+      "$zx/system/core/devmgr/fshost:block-watcher-test",
       "$zx/system/core/devmgr/fshost:fshost-test",
       "$zx/system/core/virtcon:virtual-console-test",
       "$zx/system/dev/backlight/sg-micro:sgm37603a-test",