[paver] Use isolated devmgr for paver test

Tested: runtests -t install-disk-image-test
Also paved astro, pixelbook, and vim2

Change-Id: Idfe4d71033b4318194c20e988d57d7041c3335ea
diff --git a/system/uapp/disk-pave/device-partitioner.cpp b/system/uapp/disk-pave/device-partitioner.cpp
index 0210efb..6612668 100644
--- a/system/uapp/disk-pave/device-partitioner.cpp
+++ b/system/uapp/disk-pave/device-partitioner.cpp
@@ -30,7 +30,6 @@
 namespace paver {
 
 bool (*TestBlockFilter)(const fbl::unique_fd&) = nullptr;
-bool (*TestSkipBlockFilter)(const fbl::unique_fd&) = nullptr;
 
 namespace {
 
@@ -100,7 +99,7 @@
 }
 
 
-zx_status_t OpenPartition(const char* path,
+zx_status_t OpenPartition(const fbl::unique_fd& devfs_root, const char* path,
                           fbl::Function<bool(const fbl::unique_fd&)> should_filter_file,
                           zx_duration_t timeout, fbl::unique_fd* out_partition) {
     ZX_ASSERT(path != nullptr);
@@ -136,7 +135,11 @@
         return ZX_ERR_STOP;
     };
 
-    DIR* dir = opendir(path);
+    fbl::unique_fd dir_fd(openat(devfs_root.get(), path, O_RDWR));
+    if (!dir_fd) {
+        return ZX_ERR_IO;
+    }
+    DIR* dir = fdopendir(dir_fd.release());
     if (dir == nullptr) {
         return ZX_ERR_IO;
     }
@@ -149,10 +152,11 @@
     return ZX_OK;
 }
 
-constexpr char kBlockDevPath[] = "/dev/class/block/";
+constexpr char kBlockDevPath[] = "class/block/";
 
-zx_status_t OpenBlockPartition(const uint8_t* unique_guid, const uint8_t* type_guid,
-                               zx_duration_t timeout, fbl::unique_fd* out_fd) {
+zx_status_t OpenBlockPartition(const fbl::unique_fd& devfs_root, const uint8_t* unique_guid,
+                               const uint8_t* type_guid, zx_duration_t timeout,
+                               fbl::unique_fd* out_fd) {
     ZX_ASSERT(unique_guid || type_guid);
 
     auto cb = [&](const fbl::unique_fd& fd) {
@@ -175,20 +179,16 @@
         return false;
     };
 
-    return OpenPartition(kBlockDevPath, cb, timeout, out_fd);
+    return OpenPartition(devfs_root, kBlockDevPath, cb, timeout, out_fd);
 }
 
-constexpr char kSkipBlockDevPath[] = "/dev/class/skip-block/";
+constexpr char kSkipBlockDevPath[] = "class/skip-block/";
 
-zx_status_t OpenSkipBlockPartition(const uint8_t* type_guid, zx_duration_t timeout,
-                                   fbl::unique_fd* out_fd) {
+zx_status_t OpenSkipBlockPartition(const fbl::unique_fd& devfs_root, const uint8_t* type_guid,
+                                   zx_duration_t timeout, fbl::unique_fd* out_fd) {
     ZX_ASSERT(type_guid);
 
     auto cb = [&](const fbl::unique_fd& fd) {
-        if (TestSkipBlockFilter && TestSkipBlockFilter(fd)) {
-            return true;
-        }
-
         fzl::FdioCaller caller(fbl::unique_fd(fd.get()));
 
         zx_status_t status;
@@ -201,24 +201,25 @@
         return false;
     };
 
-    return OpenPartition(kSkipBlockDevPath, cb, timeout, out_fd);
+    return OpenPartition(devfs_root, kSkipBlockDevPath, cb, timeout, out_fd);
 }
 
-bool HasSkipBlockDevice() {
+bool HasSkipBlockDevice(const fbl::unique_fd& devfs_root) {
     // Our proxy for detected a skip-block device is by checking for the
     // existence of a device enumerated under the skip-block class.
     const uint8_t type[GPT_GUID_LEN] = GUID_ZIRCON_A_VALUE;
-    return OpenSkipBlockPartition(type, ZX_SEC(1), nullptr) == ZX_OK;
+    return OpenSkipBlockPartition(devfs_root, type, ZX_SEC(1), nullptr) == ZX_OK;
 }
 
 // Attempts to open and overwrite the first block of the underlying
 // partition. Does not rebind partition drivers.
 //
 // At most one of |unique_guid| and |type_guid| may be nullptr.
-zx_status_t WipeBlockPartition(const uint8_t* unique_guid, const uint8_t* type_guid) {
+zx_status_t WipeBlockPartition(const fbl::unique_fd& devfs_root, const uint8_t* unique_guid,
+                               const uint8_t* type_guid) {
     zx_status_t status = ZX_OK;
     fbl::unique_fd fd;
-    if ((status = OpenBlockPartition(unique_guid, type_guid, ZX_SEC(3), &fd)) != ZX_OK) {
+    if ((status = OpenBlockPartition(devfs_root, unique_guid, type_guid, ZX_SEC(3), &fd)) != ZX_OK) {
         ERROR("Warning: Could not open partition to wipe: %s\n",
               zx_status_get_string(status));
         return status;
@@ -255,15 +256,17 @@
 } // namespace
 
 fbl::unique_ptr<DevicePartitioner> DevicePartitioner::Create() {
+    fbl::unique_fd devfs_root(open("/dev", O_RDWR));
     fbl::unique_ptr<DevicePartitioner> device_partitioner;
 #if defined(__x86_64__)
-    if ((CrosDevicePartitioner::Initialize(&device_partitioner) == ZX_OK) ||
-        (EfiDevicePartitioner::Initialize(&device_partitioner) == ZX_OK)) {
+    if ((CrosDevicePartitioner::Initialize(devfs_root.duplicate(), &device_partitioner) == ZX_OK) ||
+        (EfiDevicePartitioner::Initialize(std::move(devfs_root), &device_partitioner) == ZX_OK)) {
         return device_partitioner;
     }
 #elif defined(__aarch64__)
-    if ((SkipBlockDevicePartitioner::Initialize(&device_partitioner) == ZX_OK) ||
-        (FixedDevicePartitioner::Initialize(&device_partitioner) == ZX_OK)) {
+    if ((SkipBlockDevicePartitioner::Initialize(devfs_root.duplicate(),
+                                                &device_partitioner) == ZX_OK) ||
+        (FixedDevicePartitioner::Initialize(std::move(devfs_root), &device_partitioner) == ZX_OK)) {
         return device_partitioner;
     }
 #endif
@@ -274,8 +277,13 @@
  *                  GPT Common                        *
  *====================================================*/
 
-bool GptDevicePartitioner::FindTargetGptPath(fbl::String* out) {
-    DIR* d = opendir(kBlockDevPath);
+bool GptDevicePartitioner::FindTargetGptPath(const fbl::unique_fd& devfs_root, fbl::String* out) {
+    fbl::unique_fd d_fd(openat(devfs_root.get(), kBlockDevPath, O_RDWR));
+    if (!d_fd) {
+        ERROR("Cannot inspect block devices\n");
+        return false;
+    }
+    DIR* d = fdopendir(d_fd.release());
     if (d == nullptr) {
         ERROR("Cannot inspect block devices\n");
         return false;
@@ -313,9 +321,10 @@
     return false;
 }
 
-zx_status_t GptDevicePartitioner::InitializeGpt(fbl::unique_ptr<GptDevicePartitioner>* gpt_out) {
+zx_status_t GptDevicePartitioner::InitializeGpt(fbl::unique_fd devfs_root,
+                                                fbl::unique_ptr<GptDevicePartitioner>* gpt_out) {
     fbl::String gpt_path;
-    if (!FindTargetGptPath(&gpt_path)) {
+    if (!FindTargetGptPath(devfs_root, &gpt_path)) {
         ERROR("Failed to find GPT\n");
         return ZX_ERR_NOT_FOUND;
     }
@@ -363,7 +372,8 @@
     }
 
     releaser.cancel();
-    *gpt_out = WrapUnique(new GptDevicePartitioner(std::move(fd), gpt, block_info));
+    *gpt_out = WrapUnique(new GptDevicePartitioner(std::move(devfs_root), std::move(fd), gpt,
+                                                   block_info));
     return ZX_OK;
 }
 
@@ -493,7 +503,7 @@
     }
     LOG("Added partition, waiting for bind\n");
 
-    if ((status = OpenBlockPartition(guid, type, ZX_SEC(5), out_fd)) != ZX_OK) {
+    if ((status = OpenBlockPartition(devfs_root_, guid, type, ZX_SEC(5), out_fd)) != ZX_OK) {
         ERROR("Added partition, waiting for bind - NOT FOUND\n");
         return status;
     }
@@ -516,7 +526,8 @@
             }
             if (out_fd) {
                 zx_status_t status;
-                if ((status = OpenBlockPartition(p->guid, p->type, ZX_SEC(5), out_fd)) != ZX_OK) {
+                status = OpenBlockPartition(devfs_root_, p->guid, p->type, ZX_SEC(5), out_fd);
+                if (status != ZX_OK) {
                     ERROR("Couldn't open partition\n");
                     return status;
                 }
@@ -539,7 +550,8 @@
             LOG("Found partition in GPT, partition %zu\n", i);
             if (out_fd) {
                 zx_status_t status;
-                if ((status = OpenBlockPartition(p->guid, p->type, ZX_SEC(5), out_fd)) != ZX_OK) {
+                status = OpenBlockPartition(devfs_root_, p->guid, p->type, ZX_SEC(5), out_fd);
+                if (status != ZX_OK) {
                     ERROR("Couldn't open partition\n");
                     return status;
                 }
@@ -564,7 +576,7 @@
         modify = true;
 
         // Ignore the return status; wiping is a best-effort approach anyway.
-        WipeBlockPartition(p->guid, p->type);
+        WipeBlockPartition(devfs_root_, p->guid, p->type);
 
         if (gpt_partition_remove(gpt_, p->guid)) {
             ERROR("Warning: Could not remove partition\n");
@@ -588,10 +600,11 @@
  *                 EFI SPECIFIC                       *
  *====================================================*/
 
-zx_status_t EfiDevicePartitioner::Initialize(fbl::unique_ptr<DevicePartitioner>* partitioner) {
+zx_status_t EfiDevicePartitioner::Initialize(fbl::unique_fd devfs_root,
+                                             fbl::unique_ptr<DevicePartitioner>* partitioner) {
     fbl::unique_ptr<GptDevicePartitioner> gpt;
     zx_status_t status;
-    if ((status = GptDevicePartitioner::InitializeGpt(&gpt)) != ZX_OK) {
+    if ((status = GptDevicePartitioner::InitializeGpt(std::move(devfs_root), &gpt)) != ZX_OK) {
         return status;
     }
     if (is_cros(gpt->GetGpt())) {
@@ -709,10 +722,12 @@
  *                CROS SPECIFIC                       *
  *====================================================*/
 
-zx_status_t CrosDevicePartitioner::Initialize(fbl::unique_ptr<DevicePartitioner>* partitioner) {
+zx_status_t CrosDevicePartitioner::Initialize(fbl::unique_fd devfs_root,
+                                              fbl::unique_ptr<DevicePartitioner>* partitioner) {
     fbl::unique_ptr<GptDevicePartitioner> gpt_partitioner;
     zx_status_t status;
-    if ((status = GptDevicePartitioner::InitializeGpt(&gpt_partitioner)) != ZX_OK) {
+    if ((status = GptDevicePartitioner::InitializeGpt(std::move(devfs_root),
+                                                      &gpt_partitioner)) != ZX_OK) {
         return status;
     }
 
@@ -871,12 +886,13 @@
  *               FIXED PARTITION MAP                  *
  *====================================================*/
 
-zx_status_t FixedDevicePartitioner::Initialize(fbl::unique_ptr<DevicePartitioner>* partitioner) {
-    if (HasSkipBlockDevice()) {
+zx_status_t FixedDevicePartitioner::Initialize(fbl::unique_fd devfs_root,
+                                               fbl::unique_ptr<DevicePartitioner>* partitioner) {
+    if (HasSkipBlockDevice(devfs_root)) {
         return ZX_ERR_NOT_SUPPORTED;
     }
     LOG("Successfully initialized FixedDevicePartitioner Device Partitioner\n");
-    *partitioner = WrapUnique(new FixedDevicePartitioner);
+    *partitioner = WrapUnique(new FixedDevicePartitioner(std::move(devfs_root)));
     return ZX_OK;
 }
 
@@ -920,13 +936,13 @@
         return ZX_ERR_NOT_SUPPORTED;
     }
 
-    return OpenBlockPartition(nullptr, type, ZX_SEC(5), out_fd);
+    return OpenBlockPartition(devfs_root_, nullptr, type, ZX_SEC(5), out_fd);
 }
 
 zx_status_t FixedDevicePartitioner::WipePartitions() {
     const uint8_t fvm_type[GPT_GUID_LEN] = GUID_FVM_VALUE;
     zx_status_t status;
-    if ((status = WipeBlockPartition(nullptr, fvm_type)) != ZX_OK) {
+    if ((status = WipeBlockPartition(devfs_root_, nullptr, fvm_type)) != ZX_OK) {
         ERROR("Failed to wipe FVM.\n");
     } else {
         LOG("Wiped FVM successfully.\n");
@@ -951,13 +967,18 @@
  *====================================================*/
 
 zx_status_t SkipBlockDevicePartitioner::Initialize(
-    fbl::unique_ptr<DevicePartitioner>* partitioner) {
+    fbl::unique_fd devfs_root, fbl::unique_ptr<DevicePartitioner>* partitioner) {
 
-    if (!HasSkipBlockDevice()) {
+    // TODO(surajmalhtora): Use common devfs_root fd for both block and
+    // skip-block devices.
+    fbl::unique_fd block_devfs_root(open("/dev", O_RDWR));
+
+    if (!HasSkipBlockDevice(devfs_root)) {
         return ZX_ERR_NOT_SUPPORTED;
     }
     LOG("Successfully initialized SkipBlockDevicePartitioner Device Partitioner\n");
-    *partitioner = WrapUnique(new SkipBlockDevicePartitioner);
+    *partitioner = WrapUnique(new SkipBlockDevicePartitioner(std::move(devfs_root),
+                                                             std::move(block_devfs_root)));
     return ZX_OK;
 }
 
@@ -1000,21 +1021,22 @@
         const uint8_t fvm_type[GPT_GUID_LEN] = GUID_FVM_VALUE;
         memcpy(type, fvm_type, GPT_GUID_LEN);
         // FVM partition is managed so it should expose a normal block device.
-        return OpenBlockPartition(nullptr, type, ZX_SEC(5), out_fd);
+        return OpenBlockPartition(block_devfs_root_, nullptr, type, ZX_SEC(5), out_fd);
     }
     default:
         ERROR("partition_type is invalid!\n");
         return ZX_ERR_NOT_SUPPORTED;
     }
 
-    return OpenSkipBlockPartition(type, ZX_SEC(5), out_fd);
+    return OpenSkipBlockPartition(devfs_root_, type, ZX_SEC(5), out_fd);
 }
 
 zx_status_t SkipBlockDevicePartitioner::WipePartitions() {
     const uint8_t fvm_type[GPT_GUID_LEN] = GUID_FVM_VALUE;
     zx_status_t status;
     fbl::unique_fd block_fd;
-    if ((status = OpenBlockPartition(nullptr, fvm_type, ZX_SEC(3), &block_fd)) != ZX_OK) {
+    status = OpenBlockPartition(block_devfs_root_, nullptr, fvm_type, ZX_SEC(3), &block_fd);
+    if (status != ZX_OK) {
         ERROR("Warning: Could not open partition to wipe: %s\n", zx_status_get_string(status));
         return ZX_OK;
     }
diff --git a/system/uapp/disk-pave/device-partitioner.h b/system/uapp/disk-pave/device-partitioner.h
index b4732f0..d037de4 100644
--- a/system/uapp/disk-pave/device-partitioner.h
+++ b/system/uapp/disk-pave/device-partitioner.h
@@ -39,7 +39,6 @@
 // A special filter for test injection.
 // API should return true if device passed in should be filtered out.
 extern bool (*TestBlockFilter)(const fbl::unique_fd&);
-extern bool (*TestSkipBlockFilter)(const fbl::unique_fd&);
 
 // Abstract device partitioner definition.
 // This class defines common APIs for interacting with a device partitioner.
@@ -83,7 +82,8 @@
     using FilterCallback = fbl::Function<bool(const gpt_partition_t&)>;
 
     // Find and initialize a GPT based device.
-    static zx_status_t InitializeGpt(fbl::unique_ptr<GptDevicePartitioner>* gpt_out);
+    static zx_status_t InitializeGpt(fbl::unique_fd devfs_root,
+                                     fbl::unique_ptr<GptDevicePartitioner>* gpt_out);
 
     virtual ~GptDevicePartitioner() {
         if (gpt_) {
@@ -124,14 +124,17 @@
 
 private:
     // Find and return the topological path of the GPT which we will pave.
-    static bool FindTargetGptPath(fbl::String* out);
+    static bool FindTargetGptPath(const fbl::unique_fd& devfs_root, fbl::String* out);
 
-    GptDevicePartitioner(fbl::unique_fd fd, gpt_device_t* gpt, block_info_t block_info)
-        : fd_(std::move(fd)), gpt_(gpt), block_info_(block_info) {}
+    GptDevicePartitioner(fbl::unique_fd devfs_root, fbl::unique_fd fd, gpt_device_t* gpt,
+                         block_info_t block_info)
+        : devfs_root_(std::move(devfs_root)), fd_(std::move(fd)), gpt_(gpt),
+          block_info_(block_info) {}
 
     zx_status_t CreateGptPartition(const char* name, uint8_t* type, uint64_t offset,
                                    uint64_t blocks, uint8_t* out_guid);
 
+    fbl::unique_fd devfs_root_;
     fbl::unique_fd fd_;
     gpt_device_t* gpt_;
     block_info_t block_info_;
@@ -140,7 +143,8 @@
 // DevicePartitioner implementation for EFI based devices.
 class EfiDevicePartitioner : public DevicePartitioner {
 public:
-    static zx_status_t Initialize(fbl::unique_ptr<DevicePartitioner>* partitioner);
+    static zx_status_t Initialize(fbl::unique_fd devfs_root,
+                                  fbl::unique_ptr<DevicePartitioner>* partitioner);
 
     bool IsCros() const override { return false; }
 
@@ -166,7 +170,8 @@
 // DevicePartitioner implementation for ChromeOS devices.
 class CrosDevicePartitioner : public DevicePartitioner {
 public:
-    static zx_status_t Initialize(fbl::unique_ptr<DevicePartitioner>* partitioner);
+    static zx_status_t Initialize(fbl::unique_fd devfs_root,
+                                  fbl::unique_ptr<DevicePartitioner>* partitioner);
 
     bool IsCros() const override { return true; }
 
@@ -195,7 +200,8 @@
 // ZIRCON-R).
 class FixedDevicePartitioner : public DevicePartitioner {
 public:
-    static zx_status_t Initialize(fbl::unique_ptr<DevicePartitioner>* partitioner);
+    static zx_status_t Initialize(fbl::unique_fd devfs_root,
+                                  fbl::unique_ptr<DevicePartitioner>* partitioner);
 
     bool IsCros() const override { return false; }
 
@@ -214,7 +220,10 @@
     zx_status_t GetBlockSize(const fbl::unique_fd& device_fd, uint32_t* block_size) const override;
 
 private:
-    FixedDevicePartitioner() {}
+    FixedDevicePartitioner(fbl::unique_fd devfs_root)
+        : devfs_root_(std::move(devfs_root)) {}
+
+    fbl::unique_fd devfs_root_;
 };
 
 // DevicePartitioner implementation for devices which have fixed partition maps, but do not expose a
@@ -224,7 +233,8 @@
 // ZIRCON-R).
 class SkipBlockDevicePartitioner : public DevicePartitioner {
 public:
-    static zx_status_t Initialize(fbl::unique_ptr<DevicePartitioner>* partitioner);
+    static zx_status_t Initialize(fbl::unique_fd devfs_root,
+                                  fbl::unique_ptr<DevicePartitioner>* partitioner);
 
     bool IsCros() const override { return false; }
 
@@ -243,6 +253,10 @@
     zx_status_t GetBlockSize(const fbl::unique_fd& device_fd, uint32_t* block_size) const override;
 
 private:
-    SkipBlockDevicePartitioner() {}
+    SkipBlockDevicePartitioner(fbl::unique_fd devfs_root, fbl::unique_fd block_devfs_root)
+        : devfs_root_(std::move(devfs_root)), block_devfs_root_(std::move(block_devfs_root)) {}
+
+    fbl::unique_fd devfs_root_;
+    fbl::unique_fd block_devfs_root_;
 };
 } // namespace paver
diff --git a/system/uapp/disk-pave/test/device-partitioner-test.cpp b/system/uapp/disk-pave/test/device-partitioner-test.cpp
index ccbc4cf..abbbef0 100644
--- a/system/uapp/disk-pave/test/device-partitioner-test.cpp
+++ b/system/uapp/disk-pave/test/device-partitioner-test.cpp
@@ -139,12 +139,11 @@
     .export_partition_map = true,
 };
 
-
 static fbl::Vector<fbl::String> test_block_devices;
 static fbl::Vector<fbl::String> test_skip_block_devices;
 
 bool FilterRealBlockDevices(const fbl::unique_fd& fd) {
-    char topo_path[PATH_MAX] = { '\0' };
+    char topo_path[PATH_MAX] = {'\0'};
     if (ioctl_device_get_topo_path(fd.get(), topo_path, PATH_MAX) < 0) {
         return false;
     }
@@ -156,39 +155,20 @@
     return true;
 }
 
-bool FilterRealSkipBlockDevices(const fbl::unique_fd& fd) {
-    char topo_path[PATH_MAX] = { '\0' };
-    if (ioctl_device_get_topo_path(fd.get(), topo_path, PATH_MAX) < 0) {
-        return false;
-    }
-    for (const auto& device : test_skip_block_devices) {
-        if (strstr(topo_path, device.data()) == topo_path) {
-            return false;
-        }
-    }
-    return true;
-}
-
 bool Initialize() {
     test_block_devices.reset();
-    test_skip_block_devices.reset();
     paver::TestBlockFilter = FilterRealBlockDevices;
-    paver::TestSkipBlockFilter = FilterRealSkipBlockDevices;
     return true;
 }
 
-bool InsertTestDevices(fbl::StringPiece path, bool skip) {
+bool InsertTestDevices(fbl::StringPiece path) {
     BEGIN_HELPER;
     fbl::unique_fd fd(open(path.data(), O_RDWR));
     ASSERT_TRUE(fd.is_valid());
     fbl::String topo_path(PATH_MAX, '\0');
     ASSERT_GE(ioctl_device_get_topo_path(fd.get(), const_cast<char*>(topo_path.data()), PATH_MAX),
               0);
-    if (skip) {
-        test_skip_block_devices.push_back(std::move(topo_path));
-    } else {
-        test_block_devices.push_back(std::move(topo_path));
-    }
+    test_block_devices.push_back(std::move(topo_path));
     END_HELPER;
 }
 
@@ -200,7 +180,7 @@
         ASSERT_EQ(create_ramdisk_with_guid(kBlockSize, kBlockCount, guid, ZBI_PARTITION_GUID_LEN,
                                            const_cast<char*>(path.data())),
                   ZX_OK);
-        ASSERT_TRUE(InsertTestDevices(path.ToStringPiece(), false));
+        ASSERT_TRUE(InsertTestDevices(path.ToStringPiece()));
         device->reset(new BlockDevice(std::move(path)));
         END_HELPER;
     }
@@ -250,19 +230,25 @@
 
         zircon_nand_RamNandInfo info = kNandInfo;
         info.vmo = dup.release();
+        fbl::RefPtr<fs_mgmt::RamNandCtl> ctl;
+        ASSERT_EQ(fs_mgmt::RamNandCtl::Create(&ctl), ZX_OK);
         std::optional<fs_mgmt::RamNand> ram_nand;
-        ASSERT_EQ(fs_mgmt::RamNand::Create(&info, &ram_nand), ZX_OK);
-        ASSERT_TRUE(InsertTestDevices(ram_nand->path(), true));
-        device->reset(new SkipBlockDevice(std::move(*ram_nand), std::move(mapper)));
+        ASSERT_EQ(fs_mgmt::RamNand::Create(ctl, &info, &ram_nand), ZX_OK);
+        device->reset(new SkipBlockDevice(std::move(ctl), std::move(*ram_nand),
+                                          std::move(mapper)));
         END_HELPER;
     }
 
+    fbl::unique_fd devfs_root() { return fbl::unique_fd(dup(ctl_->devfs_root().get())); }
+
     ~SkipBlockDevice() = default;
 
 private:
-    SkipBlockDevice(fs_mgmt::RamNand ram_nand, fzl::VmoMapper mapper)
-        : ram_nand_(std::move(ram_nand)), mapper_(std::move(mapper)) {}
+    SkipBlockDevice(fbl::RefPtr<fs_mgmt::RamNandCtl> ctl, fs_mgmt::RamNand ram_nand,
+                    fzl::VmoMapper mapper)
+        : ctl_(std::move(ctl)), ram_nand_(std::move(ram_nand)), mapper_(std::move(mapper)) {}
 
+    fbl::RefPtr<fs_mgmt::RamNandCtl> ctl_;
     fs_mgmt::RamNand ram_nand_;
     fzl::VmoMapper mapper_;
 };
@@ -313,8 +299,9 @@
 bool IsCrosTest() {
     BEGIN_TEST;
 
+    fbl::unique_fd dev_fs(open("/dev", O_RDWR));
     fbl::unique_ptr<paver::DevicePartitioner> partitioner;
-    ASSERT_EQ(paver::FixedDevicePartitioner::Initialize(&partitioner), ZX_OK);
+    ASSERT_EQ(paver::FixedDevicePartitioner::Initialize(std::move(dev_fs), &partitioner), ZX_OK);
     ASSERT_FALSE(partitioner->IsCros());
 
     END_TEST;
@@ -323,8 +310,9 @@
 bool UseBlockInterfaceTest() {
     BEGIN_TEST;
 
+    fbl::unique_fd devfs(open("/dev", O_RDWR));
     fbl::unique_ptr<paver::DevicePartitioner> partitioner;
-    ASSERT_EQ(paver::FixedDevicePartitioner::Initialize(&partitioner), ZX_OK);
+    ASSERT_EQ(paver::FixedDevicePartitioner::Initialize(std::move(devfs), &partitioner), ZX_OK);
     ASSERT_FALSE(partitioner->UseSkipBlockInterface());
 
     END_TEST;
@@ -333,8 +321,9 @@
 bool AddPartitionTest() {
     BEGIN_TEST;
 
+    fbl::unique_fd devfs(open("/dev", O_RDWR));
     fbl::unique_ptr<paver::DevicePartitioner> partitioner;
-    ASSERT_EQ(paver::FixedDevicePartitioner::Initialize(&partitioner), ZX_OK);
+    ASSERT_EQ(paver::FixedDevicePartitioner::Initialize(std::move(devfs), &partitioner), ZX_OK);
     ASSERT_EQ(partitioner->AddPartition(paver::Partition::kZirconB, nullptr), ZX_ERR_NOT_SUPPORTED);
 
     END_TEST;
@@ -343,8 +332,9 @@
 bool WipePartitionsTest() {
     BEGIN_TEST;
 
+    fbl::unique_fd devfs(open("/dev", O_RDWR));
     fbl::unique_ptr<paver::DevicePartitioner> partitioner;
-    ASSERT_EQ(paver::FixedDevicePartitioner::Initialize(&partitioner), ZX_OK);
+    ASSERT_EQ(paver::FixedDevicePartitioner::Initialize(std::move(devfs), &partitioner), ZX_OK);
     ASSERT_EQ(partitioner->WipePartitions(), ZX_OK);
 
     END_TEST;
@@ -353,8 +343,9 @@
 bool FinalizePartitionTest() {
     BEGIN_TEST;
 
+    fbl::unique_fd devfs(open("/dev", O_RDWR));
     fbl::unique_ptr<paver::DevicePartitioner> partitioner;
-    ASSERT_EQ(paver::FixedDevicePartitioner::Initialize(&partitioner), ZX_OK);
+    ASSERT_EQ(paver::FixedDevicePartitioner::Initialize(std::move(devfs), &partitioner), ZX_OK);
 
     ASSERT_EQ(partitioner->FinalizePartition(paver::Partition::kZirconA), ZX_OK);
     ASSERT_EQ(partitioner->FinalizePartition(paver::Partition::kZirconB), ZX_OK);
@@ -378,8 +369,9 @@
     ASSERT_TRUE(BlockDevice::Create(kVbMetaBType, &vbmeta_b));
     ASSERT_TRUE(BlockDevice::Create(kFvmType, &fvm));
 
+    fbl::unique_fd devfs(open("/dev", O_RDWR));
     fbl::unique_ptr<paver::DevicePartitioner> partitioner;
-    ASSERT_EQ(paver::FixedDevicePartitioner::Initialize(&partitioner), ZX_OK);
+    ASSERT_EQ(paver::FixedDevicePartitioner::Initialize(std::move(devfs), &partitioner), ZX_OK);
 
     fbl::unique_fd fd;
     ASSERT_EQ(partitioner->FindPartition(paver::Partition::kZirconA, &fd), ZX_OK);
@@ -404,8 +396,9 @@
     ASSERT_TRUE(BlockDevice::Create(kVbMetaBType, &vbmeta_b));
     ASSERT_TRUE(BlockDevice::Create(kFvmType, &fvm));
 
+    fbl::unique_fd devfs(open("/dev", O_RDWR));
     fbl::unique_ptr<paver::DevicePartitioner> partitioner;
-    ASSERT_EQ(paver::FixedDevicePartitioner::Initialize(&partitioner), ZX_OK);
+    ASSERT_EQ(paver::FixedDevicePartitioner::Initialize(std::move(devfs), &partitioner), ZX_OK);
 
     fbl::unique_fd fd;
     uint32_t block_size;
@@ -455,7 +448,8 @@
     ASSERT_TRUE(SkipBlockDevice::Create(&device));
 
     fbl::unique_ptr<paver::DevicePartitioner> partitioner;
-    ASSERT_EQ(paver::SkipBlockDevicePartitioner::Initialize(&partitioner), ZX_OK);
+    ASSERT_EQ(paver::SkipBlockDevicePartitioner::Initialize(device->devfs_root(), &partitioner),
+              ZX_OK);
     ASSERT_FALSE(partitioner->IsCros());
 
     END_TEST;
@@ -469,7 +463,8 @@
     ASSERT_TRUE(SkipBlockDevice::Create(&device));
 
     fbl::unique_ptr<paver::DevicePartitioner> partitioner;
-    ASSERT_EQ(paver::SkipBlockDevicePartitioner::Initialize(&partitioner), ZX_OK);
+    ASSERT_EQ(paver::SkipBlockDevicePartitioner::Initialize(device->devfs_root(), &partitioner),
+              ZX_OK);
     ASSERT_TRUE(partitioner->UseSkipBlockInterface());
 
     END_TEST;
@@ -483,7 +478,8 @@
     ASSERT_TRUE(SkipBlockDevice::Create(&device));
 
     fbl::unique_ptr<paver::DevicePartitioner> partitioner;
-    ASSERT_EQ(paver::SkipBlockDevicePartitioner::Initialize(&partitioner), ZX_OK);
+    ASSERT_EQ(paver::SkipBlockDevicePartitioner::Initialize(device->devfs_root(), &partitioner),
+              ZX_OK);
     ASSERT_EQ(partitioner->AddPartition(paver::Partition::kZirconB, nullptr), ZX_ERR_NOT_SUPPORTED);
 
     END_TEST;
@@ -497,7 +493,8 @@
     ASSERT_TRUE(SkipBlockDevice::Create(&device));
 
     fbl::unique_ptr<paver::DevicePartitioner> partitioner;
-    ASSERT_EQ(paver::SkipBlockDevicePartitioner::Initialize(&partitioner), ZX_OK);
+    ASSERT_EQ(paver::SkipBlockDevicePartitioner::Initialize(device->devfs_root(), &partitioner),
+              ZX_OK);
     ASSERT_EQ(partitioner->WipePartitions(), ZX_OK);
 
     END_TEST;
@@ -511,7 +508,8 @@
     ASSERT_TRUE(SkipBlockDevice::Create(&device));
 
     fbl::unique_ptr<paver::DevicePartitioner> partitioner;
-    ASSERT_EQ(paver::SkipBlockDevicePartitioner::Initialize(&partitioner), ZX_OK);
+    ASSERT_EQ(paver::SkipBlockDevicePartitioner::Initialize(device->devfs_root(), &partitioner),
+              ZX_OK);
 
     ASSERT_EQ(partitioner->FinalizePartition(paver::Partition::kBootloader), ZX_OK);
     ASSERT_EQ(partitioner->FinalizePartition(paver::Partition::kZirconA), ZX_OK);
@@ -533,7 +531,8 @@
     ASSERT_TRUE(BlockDevice::Create(kFvmType, &fvm));
 
     fbl::unique_ptr<paver::DevicePartitioner> partitioner;
-    ASSERT_EQ(paver::SkipBlockDevicePartitioner::Initialize(&partitioner), ZX_OK);
+    ASSERT_EQ(paver::SkipBlockDevicePartitioner::Initialize(device->devfs_root(), &partitioner),
+              ZX_OK);
 
     fbl::unique_fd fd;
     ASSERT_EQ(partitioner->FindPartition(paver::Partition::kBootloader, &fd), ZX_OK);
@@ -558,7 +557,8 @@
     ASSERT_TRUE(BlockDevice::Create(kFvmType, &fvm));
 
     fbl::unique_ptr<paver::DevicePartitioner> partitioner;
-    ASSERT_EQ(paver::SkipBlockDevicePartitioner::Initialize(&partitioner), ZX_OK);
+    ASSERT_EQ(paver::SkipBlockDevicePartitioner::Initialize(device->devfs_root(), &partitioner),
+              ZX_OK);
 
     fbl::unique_fd fd;
     uint32_t block_size;