From 4dd0028928a9b63da53d54601345b7ccb4f2606b Mon Sep 17 00:00:00 2001 From: Jeff Seibert <jseibert@google.com> Date: Wed, 8 May 2019 21:12:12 +0000 Subject: [PATCH] [fshost] Reformat zxcrypt and minfs partitions when missing. If the disk format is unknown but the GPT GUID values dictate that it should be a data format, it determines if the volume should be a zxcrypt or minfs partition and formats it as such. Added a unit test that failed without the changes. I added a test that would have caught the vim2 breakage, but I also manually tested that it boots up properly. Change-Id: I276d55fc8dbcc8377d6a6b7d5dfe7b0fd9019eb4 --- .../devmgr/fshost/block-device-interface.h | 8 + .../core/devmgr/fshost/block-device-test.cpp | 73 +++++-- .../core/devmgr/fshost/block-device.cpp | 57 ++++++ .../system/core/devmgr/fshost/block-device.h | 2 + .../core/devmgr/fshost/block-watcher-test.cpp | 133 +++++++++++++ zircon/system/utest/BUILD.gn | 1 + zircon/system/utest/fs-recovery/BUILD.gn | 33 ++++ zircon/system/utest/fs-recovery/recovery.cpp | 181 ++++++++++++++++++ .../utest/fs-test-utils/perftest_test.cpp | 2 +- 9 files changed, 471 insertions(+), 19 deletions(-) create mode 100644 zircon/system/utest/fs-recovery/BUILD.gn create mode 100644 zircon/system/utest/fs-recovery/recovery.cpp diff --git a/zircon/system/core/devmgr/fshost/block-device-interface.h b/zircon/system/core/devmgr/fshost/block-device-interface.h index 99c6302b893..dcde052ee28 100644 --- a/zircon/system/core/devmgr/fshost/block-device-interface.h +++ b/zircon/system/core/devmgr/fshost/block-device-interface.h @@ -17,6 +17,7 @@ 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 kZxcryptDriverPath[] = "/boot/driver/zxcrypt.so"; constexpr char kBootpartDriverPath[] = "/boot/driver/bootpart.so"; // An abstract class representing the operations which may be performed @@ -58,6 +59,13 @@ private: // Unseals the underlying zxcrypt volume. virtual zx_status_t UnsealZxcrypt() = 0; + // Creates the zxcrypt partition. + virtual zx_status_t FormatZxcrypt() = 0; + + // Determines if the underlying volume is unsealed zxcrypt. Assumes the device + // has the data GUID. + virtual zx_status_t IsUnsealedZxcrypt(bool* is_unsealed_zxcrypt) = 0; + // Returns true if the consistency of filesystems should be validated before // mounting. virtual bool ShouldCheckFilesystems() = 0; diff --git a/zircon/system/core/devmgr/fshost/block-device-test.cpp b/zircon/system/core/devmgr/fshost/block-device-test.cpp index 292367e4948..ace6237cd13 100644 --- a/zircon/system/core/devmgr/fshost/block-device-test.cpp +++ b/zircon/system/core/devmgr/fshost/block-device-test.cpp @@ -4,6 +4,7 @@ #include <fcntl.h> +#include <lib/devmgr-integration-test/fixture.h> #include <lib/fdio/namespace.h> #include <ramdevice-client/ramdisk.h> #include <zircon/assert.h> @@ -17,6 +18,8 @@ namespace devmgr { namespace { +using devmgr_integration_test::IsolatedDevmgr; + class BlockDeviceHarness : public zxtest::Test { public: void SetUp() override { @@ -36,6 +39,18 @@ public: ASSERT_OK(fdio_ns_get_installed(&ns)); ASSERT_OK(fdio_ns_bind(ns, "/fs", client.release())); manager_->WatchExit(); + + devmgr_launcher::Args args; + args.disable_block_watcher = true; + args.sys_device_driver = devmgr_integration_test::IsolatedDevmgr::kSysdevDriver; + args.load_drivers.push_back(devmgr_integration_test::IsolatedDevmgr::kSysdevDriver); + args.driver_search_paths.push_back("/boot/driver"); + ASSERT_OK(IsolatedDevmgr::Create(std::move(args), &devmgr_)); + fbl::unique_fd ctl; + ASSERT_EQ(devmgr_integration_test::RecursiveWaitForFile(devmgr_.devfs_root(), "misc/ramctl", + zx::deadline_after(zx::sec(5)), + &ctl), + ZX_OK); } void TearDown() override { @@ -48,9 +63,14 @@ public: return std::move(manager_); } + fbl::unique_fd devfs_root() { + return devmgr_.devfs_root().duplicate(); + } + private: zx::event event_; std::unique_ptr<FsManager> manager_; + IsolatedDevmgr devmgr_; }; TEST_F(BlockDeviceHarness, TestBadHandleDevice) { @@ -89,9 +109,12 @@ TEST_F(BlockDeviceHarness, TestEmptyDevice) { 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_OK(ramdisk_create_at(devfs_root().get(), kBlockSize, kBlockCount, &ramdisk)); + fbl::unique_fd fd; + ASSERT_EQ(devmgr_integration_test::RecursiveWaitForFile(devfs_root(), + ramdisk_get_path(ramdisk), + zx::deadline_after(zx::sec(5)), &fd), + ZX_OK); ASSERT_TRUE(fd); BlockDevice device(&mounter, std::move(fd)); @@ -127,9 +150,12 @@ TEST_F(BlockDeviceHarness, TestMinfsBadGUID) { 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_OK(ramdisk_create_at(devfs_root().get(), kBlockSize, kBlockCount, &ramdisk)); + fbl::unique_fd fd; + ASSERT_EQ(devmgr_integration_test::RecursiveWaitForFile(devfs_root(), + ramdisk_get_path(ramdisk), + zx::deadline_after(zx::sec(5)), &fd), + ZX_OK); ASSERT_TRUE(fd); // We started with an empty block device, but let's lie and say it @@ -158,10 +184,13 @@ TEST_F(BlockDeviceHarness, TestMinfsGoodGUID) { 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_OK(ramdisk_create_at_with_guid(devfs_root().get(), kBlockSize, kBlockCount, + data_guid, sizeof(data_guid), &ramdisk)); + fbl::unique_fd fd; + ASSERT_EQ(devmgr_integration_test::RecursiveWaitForFile(devfs_root(), + ramdisk_get_path(ramdisk), + zx::deadline_after(zx::sec(5)), &fd), + ZX_OK); ASSERT_TRUE(fd); BlockDevice device(&mounter, std::move(fd)); @@ -187,10 +216,15 @@ TEST_F(BlockDeviceHarness, TestMinfsReformat) { 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_OK(ramdisk_create_at_with_guid(devfs_root().get(), kBlockSize, kBlockCount, + data_guid, + sizeof(data_guid), &ramdisk)); + fbl::unique_fd fd; + ASSERT_EQ(devmgr_integration_test::RecursiveWaitForFile(devfs_root(), + ramdisk_get_path(ramdisk), + zx::deadline_after(zx::sec(5)), &fd), + ZX_OK); + ASSERT_TRUE(fd); BlockDevice device(&mounter, std::move(fd)); @@ -222,10 +256,13 @@ TEST_F(BlockDeviceHarness, TestBlobfs) { constexpr uint64_t kBlockCount = 1 << 20; ramdisk_client_t* ramdisk; const uint8_t data_guid[GPT_GUID_LEN] = GUID_BLOB_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_OK(ramdisk_create_at_with_guid(devfs_root().get(), kBlockSize, kBlockCount, data_guid, + sizeof(data_guid), &ramdisk)); + fbl::unique_fd fd; + ASSERT_EQ(devmgr_integration_test::RecursiveWaitForFile(devfs_root(), + ramdisk_get_path(ramdisk), + zx::deadline_after(zx::sec(5)), &fd), + ZX_OK); ASSERT_TRUE(fd); BlockDevice device(&mounter, std::move(fd)); diff --git a/zircon/system/core/devmgr/fshost/block-device.cpp b/zircon/system/core/devmgr/fshost/block-device.cpp index c5e64c85fc9..63866bdb585 100644 --- a/zircon/system/core/devmgr/fshost/block-device.cpp +++ b/zircon/system/core/devmgr/fshost/block-device.cpp @@ -189,6 +189,38 @@ zx_status_t BlockDevice::UnsealZxcrypt() { return ZX_OK; } +zx_status_t BlockDevice::IsUnsealedZxcrypt(bool* is_unsealed_zxcrypt) { + zx_status_t call_status; + fbl::StringBuffer<PATH_MAX> path; + path.Resize(path.capacity()); + size_t path_len; + fzl::UnownedFdioCaller disk_connection(fd_.get()); + // Both the zxcrypt and minfs partitions have the same gpt guid, so here we + // determine which it actually is. We do this by looking up the topological + // path. + if (fuchsia_device_ControllerGetTopologicalPath(disk_connection.borrow_channel(), &call_status, + path.data(), path.capacity(), + &path_len) != ZX_OK) { + return ZX_ERR_NOT_FOUND; + } + if (call_status != ZX_OK) { + return call_status; + } + const fbl::StringPiece kZxcryptPath("/zxcrypt/unsealed/block"); + if (path_len < kZxcryptPath.length()) { + *is_unsealed_zxcrypt = false; + } else { + *is_unsealed_zxcrypt = fbl::StringPiece(path.begin() + path_len - + kZxcryptPath.length()) + .compare(kZxcryptPath) == 0; + } + return ZX_OK; +} + +zx_status_t BlockDevice::FormatZxcrypt() { + return zxcrypt::FdioVolume::CreateWithDeviceKey(fd_.duplicate(), nullptr); +} + bool BlockDevice::ShouldCheckFilesystems() { return mounter_->ShouldCheckFilesystems(); } @@ -418,6 +450,31 @@ zx_status_t BlockDeviceInterface::Add() { return ZX_OK; } default: + // If the disk format is unknown but we know it should be the data + // partition, format the disk properly. + if (gpt_is_data_guid(guid.value, GPT_GUID_LEN)) { + printf("fshost: Data partition has unknown format\n"); + bool is_unsealed_zxcrypt; + if (IsUnsealedZxcrypt(&is_unsealed_zxcrypt) != ZX_OK) { + return ZX_ERR_NOT_SUPPORTED; + } + if (is_unsealed_zxcrypt) { + printf("fshost: Formatting as minfs partition\n"); + SetFormat(DISK_FORMAT_MINFS); + status = FormatFilesystem(); + if (status != ZX_OK) { + return status; + } + } else { + printf("fshost: Formatting as zxcrypt partition\n"); + SetFormat(DISK_FORMAT_ZXCRYPT); + status = FormatZxcrypt(); + if (status != ZX_OK) { + return status; + } + } + return Add(); + } return ZX_ERR_NOT_SUPPORTED; } } diff --git a/zircon/system/core/devmgr/fshost/block-device.h b/zircon/system/core/devmgr/fshost/block-device.h index 980973641b5..a5d4ff67e54 100644 --- a/zircon/system/core/devmgr/fshost/block-device.h +++ b/zircon/system/core/devmgr/fshost/block-device.h @@ -30,6 +30,8 @@ public: 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 IsUnsealedZxcrypt(bool* is_unsealed_zxcrypt) final; + zx_status_t FormatZxcrypt() final; bool ShouldCheckFilesystems() final; zx_status_t CheckFilesystem() final; zx_status_t FormatFilesystem() final; diff --git a/zircon/system/core/devmgr/fshost/block-watcher-test.cpp b/zircon/system/core/devmgr/fshost/block-watcher-test.cpp index b971e9a571d..129d3dede6c 100644 --- a/zircon/system/core/devmgr/fshost/block-watcher-test.cpp +++ b/zircon/system/core/devmgr/fshost/block-watcher-test.cpp @@ -3,6 +3,7 @@ // found in the LICENSE file. #include <zircon/assert.h> +#include <zircon/device/block.h> #include <zircon/hw/gpt.h> #include <zxtest/zxtest.h> @@ -34,6 +35,12 @@ public: zx_status_t UnsealZxcrypt() override { ZX_PANIC("Test should not invoke function %s\n", __FUNCTION__); } + zx_status_t IsUnsealedZxcrypt(bool* is_unsealed_zxcrypt) override { + ZX_PANIC("Test should not invoke function %s\n", __FUNCTION__); + } + zx_status_t FormatZxcrypt() override { + ZX_PANIC("Test should not invoke function %s\n", __FUNCTION__); + } bool ShouldCheckFilesystems() override { ZX_PANIC("Test should not invoke function %s\n", __FUNCTION__); } @@ -261,4 +268,130 @@ TEST(AddDeviceTestCase, AddInvalidMinfsDevice) { EXPECT_TRUE(device.mounted); } +// Tests adding minfs with a valid type GUID and invalid format. Observe that +// the filesystem reformats itself. +TEST(AddDeviceTestCase, AddUnknownFormatMinfsDevice) { + class MinfsDevice : public MockBlockDevice { + public: + disk_format_t GetFormat() final { + return format; + } + 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 FormatFilesystem() final { + formatted = true; + return ZX_OK; + } + zx_status_t CheckFilesystem() final { + return ZX_OK; + } + zx_status_t MountFilesystem() final { + EXPECT_TRUE(formatted); + mounted = true; + return ZX_OK; + } + zx_status_t IsUnsealedZxcrypt(bool* is_unsealed_zxcrypt) final { + *is_unsealed_zxcrypt = true; + return ZX_OK; + } + void SetFormat(disk_format_t f) final { + format = f; + } + + disk_format_t format = DISK_FORMAT_UNKNOWN; + bool formatted = false; + bool mounted = false; + }; + MinfsDevice device; + EXPECT_FALSE(device.formatted); + EXPECT_FALSE(device.mounted); + EXPECT_EQ(ZX_OK, device.Add()); + EXPECT_TRUE(device.formatted); + EXPECT_TRUE(device.mounted); +} + +// Tests adding zxcrypt with a valid type GUID and invalid format. Observe that +// the partition reformats itself. +TEST(AddDeviceTestCase, AddUnknownFormatZxcryptDevice) { + class ZxcryptDevice : public MockBlockDevice { + public: + disk_format_t GetFormat() final { + return format; + } + 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 FormatZxcrypt() final { + formatted_zxcrypt = true; + return ZX_OK; + } + zx_status_t FormatFilesystem() final { + formatted_filesystem = true; + return ZX_OK; + } + zx_status_t CheckFilesystem() final { + return ZX_OK; + } + zx_status_t UnsealZxcrypt() final { + return ZX_OK; + } + zx_status_t IsUnsealedZxcrypt(bool* is_unsealed_zxcrypt) final { + *is_unsealed_zxcrypt = false; + return ZX_OK; + } + void SetFormat(disk_format_t f) final { + format = f; + } + zx_status_t AttachDriver(const fbl::StringPiece& driver) final { + EXPECT_STR_EQ(devmgr::kZxcryptDriverPath, driver.data()); + return ZX_OK; + } + + disk_format_t format = DISK_FORMAT_UNKNOWN; + bool formatted_zxcrypt = false; + bool formatted_filesystem = false; + }; + ZxcryptDevice device; + EXPECT_EQ(ZX_OK, device.Add()); + EXPECT_TRUE(device.formatted_zxcrypt); + EXPECT_FALSE(device.formatted_filesystem); +} + +// Tests adding a boot partition device with unknown format can be added with +// the correct driver. +TEST(AddDeviceTestCase, AddUnknownFormatBootPartitionDevice) { + class BootPartDevice : public MockBlockDevice { + public: + disk_format_t GetFormat() final { + return DISK_FORMAT_UNKNOWN; + } + zx_status_t GetInfo(fuchsia_hardware_block_BlockInfo* out_info) override { + fuchsia_hardware_block_BlockInfo info = {}; + info.flags = BLOCK_FLAG_BOOTPART; + info.block_size = 512; + info.block_count = 1024; + *out_info = info; + return ZX_OK; + } + zx_status_t AttachDriver(const fbl::StringPiece& driver) final { + EXPECT_STR_EQ(devmgr::kBootpartDriverPath, driver.data()); + return ZX_OK; + } + zx_status_t IsUnsealedZxcrypt(bool* is_unsealed_zxcrypt) final { + *is_unsealed_zxcrypt = false; + checked_unsealed_zxcrypt = true; + return ZX_OK; + } + bool checked_unsealed_zxcrypt = false; + }; + BootPartDevice device; + EXPECT_EQ(ZX_OK, device.Add()); + EXPECT_FALSE(device.checked_unsealed_zxcrypt); +} + } // namespace diff --git a/zircon/system/utest/BUILD.gn b/zircon/system/utest/BUILD.gn index b5c3075ae6c..a0f2a7012d7 100644 --- a/zircon/system/utest/BUILD.gn +++ b/zircon/system/utest/BUILD.gn @@ -119,6 +119,7 @@ if (current_cpu != "") { "fs", "fs-bench", "fs-management", + "fs-recovery", "fs-test-utils", "fs-vnode", "futex-ownership", diff --git a/zircon/system/utest/fs-recovery/BUILD.gn b/zircon/system/utest/fs-recovery/BUILD.gn new file mode 100644 index 00000000000..f2e51667ce9 --- /dev/null +++ b/zircon/system/utest/fs-recovery/BUILD.gn @@ -0,0 +1,33 @@ +# 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. + +import("$zx/public/gn/resource.gni") + +generated_resource("zxcrypt_config.txt") { + testonly = true + contents = "null" + outputs = [ + "config/zxcrypt", + ] +} + + +test("fs-recovery") { + sources = [ + "recovery.cpp", + ] + deps = [ + "$zx/system/ulib/devmgr-integration-test", + "$zx/system/ulib/fbl", + "$zx/system/ulib/fdio", + "$zx/system/ulib/fs-management", + "$zx/system/ulib/fvm", + "$zx/system/ulib/fzl", + "$zx/system/ulib/ramdevice-client", + "$zx/system/ulib/unittest", + "$zx/system/ulib/zx", + ":zxcrypt_config.txt", + ] +} + diff --git a/zircon/system/utest/fs-recovery/recovery.cpp b/zircon/system/utest/fs-recovery/recovery.cpp new file mode 100644 index 00000000000..bec94583f11 --- /dev/null +++ b/zircon/system/utest/fs-recovery/recovery.cpp @@ -0,0 +1,181 @@ +// 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 <fs-management/fvm.h> +#include <fs-management/mount.h> +#include <fvm/format.h> +#include <lib/devmgr-integration-test/fixture.h> +#include <lib/fdio/fd.h> +#include <lib/fdio/fdio.h> +#include <lib/fzl/fdio.h> +#include <lib/zx/vmo.h> +#include <ramdevice-client/ramdisk.h> +#include <unittest/unittest.h> + +namespace { + +using devmgr_integration_test::IsolatedDevmgr; + +const uint32_t kBlockCount = 1024 * 256; +const uint32_t kBlockSize = 512; +const uint32_t kSliceSize = (1 << 20); +const size_t kDeviceSize = kBlockCount * kBlockSize; +const char* kDataName = "fs-recovery-data"; +const char* kRamdiskPath = "misc/ramctl"; + +// Test fixture that builds a ramdisk and destroys it when destructed. +class FsRecoveryTest { +public: + // Create an IsolatedDevmgr that can load device drivers such as fvm, + // zxcrypt, etc. + bool Initialize() { + BEGIN_HELPER; + auto args = IsolatedDevmgr::DefaultArgs(); + args.disable_block_watcher = false; + args.sys_device_driver = devmgr_integration_test::IsolatedDevmgr::kSysdevDriver; + args.load_drivers.push_back(devmgr_integration_test::IsolatedDevmgr::kSysdevDriver); + args.driver_search_paths.push_back("/boot/driver"); + ASSERT_EQ(IsolatedDevmgr::Create(std::move(args), &devmgr_), ZX_OK); + END_HELPER; + } + + // Create a ram disk that is back by a VMO, which is formatted to look like + // an FVM volume. + bool CreateFvmRamdisk(size_t device_size, size_t block_size) { + BEGIN_HELPER; + + // Calculate total size of data + metadata. + device_size = fbl::round_up(device_size, fvm::kBlockSize); + size_t old_meta = fvm::MetadataSize(device_size, fvm::kBlockSize); + size_t new_meta = fvm::MetadataSize(old_meta + device_size, fvm::kBlockSize); + while (old_meta != new_meta) { + old_meta = new_meta; + new_meta = fvm::MetadataSize(old_meta + device_size, fvm::kBlockSize); + } + device_size = device_size + (new_meta * 2); + + zx::vmo disk; + ASSERT_EQ(zx::vmo::create(device_size, 0, &disk), ZX_OK); + int fd = -1; + ASSERT_EQ(fdio_fd_create(disk.get(), &fd), ZX_OK); + ASSERT_GE(fd, 0); + ASSERT_EQ(fvm_init_with_size(fd, device_size, kSliceSize), ZX_OK); + + fbl::unique_fd ramdisk; + ASSERT_TRUE(WaitForDevice(kRamdiskPath, &ramdisk)); + + ASSERT_EQ(ramdisk_create_at_from_vmo(devmgr_.devfs_root().get(), disk.get(), + &ramdisk_client_), + ZX_OK); + END_HELPER; + } + + // Create a partition in the FVM volume that has the data guid. + bool CreateFvmPartition(char* fvm_block_path) { + BEGIN_HELPER; + + char fvm_path[PATH_MAX]; + snprintf(fvm_path, PATH_MAX, "%s/fvm", ramdisk_get_path(ramdisk_client_)); + fbl::unique_fd fvm_fd; + ASSERT_TRUE(WaitForDevice(fvm_path, &fvm_fd)); + + // Allocate a FVM partition with the data guid but don't actually format the + // partition. + alloc_req_t req; + memset(&req, 0, sizeof(alloc_req_t)); + req.slice_count = 1; + static const uint8_t data_guid[GPT_GUID_LEN] = GUID_DATA_VALUE; + memcpy(req.type, data_guid, GUID_LEN); + snprintf(req.name, NAME_LEN, "%s", kDataName); + + fuchsia_hardware_block_partition_GUID type_guid; + memcpy(type_guid.value, req.type, GUID_LEN); + fuchsia_hardware_block_partition_GUID instance_guid; + memcpy(instance_guid.value, req.guid, GUID_LEN); + + fzl::UnownedFdioCaller caller(fvm_fd.get()); + zx_status_t status; + ASSERT_EQ(fuchsia_hardware_block_volume_VolumeManagerAllocatePartition( + caller.borrow_channel(), req.slice_count, &type_guid, &instance_guid, + req.name, NAME_LEN, req.flags, &status), + ZX_OK); + ASSERT_EQ(status, ZX_OK); + + snprintf(fvm_block_path, PATH_MAX, "%s/%s-p-1/block", fvm_path, kDataName); + fbl::unique_fd fvm_block_fd; + ASSERT_TRUE(WaitForDevice(fvm_block_path, &fvm_block_fd)); + + END_HELPER; + } + + // Wait for the device to be available and then check to make sure it is + // formatted of the passed in type. Since formatting can take some time + // after the device becomes available, we must recheck. + bool WaitForDiskFormat(const char* path, disk_format_t format, zx::duration deadline) { + fbl::unique_fd fd; + if (!WaitForDevice(path, &fd)) { + return false; + } + while (deadline.get() > 0) { + fd.reset(openat(devmgr_.devfs_root().get(), path, O_RDONLY)); + if (detect_disk_format(fd.get()) == format) + return true; + sleep(1); + deadline -= zx::duration(ZX_SEC(1)); + } + return false; + } + +private: + bool WaitForDevice(const char* path, fbl::unique_fd* fd) { + BEGIN_HELPER; + printf("Wait for device %s\n", path); + ASSERT_EQ(devmgr_integration_test::RecursiveWaitForFile(devmgr_.devfs_root(), path, + zx::deadline_after(zx::sec(5)), fd), + ZX_OK); + + ASSERT_TRUE(*fd); + END_HELPER; + } + + ramdisk_client_t* ramdisk_client_; + devmgr_integration_test::IsolatedDevmgr devmgr_; +}; + +bool EmptyPartitionRecoveryTest() { + BEGIN_TEST; + + char fvm_block_path[PATH_MAX]; + fbl::unique_ptr<FsRecoveryTest> recovery(new FsRecoveryTest()); + ASSERT_TRUE(recovery->Initialize()); + // Creates an FVM partition under an isolated devmgr. It creates, but does + // not properly format the data partition. + ASSERT_TRUE(recovery->CreateFvmRamdisk(kDeviceSize, kBlockSize)); + ASSERT_TRUE(recovery->CreateFvmPartition(fvm_block_path)); + + // We then expect the devmgr to self-recover, i.e., format the zxcrypt/data + // partitions as expected from the FVM partition. + + // First, wait for the zxcrypt partition to be formatted. + EXPECT_TRUE(recovery->WaitForDiskFormat(fvm_block_path, DISK_FORMAT_ZXCRYPT, + zx::duration(ZX_SEC(3)))); + + // Second, wait for the data partition to be formatted. + char data_path[PATH_MAX]; + snprintf(data_path, sizeof(data_path), + "%s/zxcrypt/unsealed/block", fvm_block_path); + EXPECT_TRUE(recovery->WaitForDiskFormat(data_path, DISK_FORMAT_MINFS, + zx::duration(ZX_SEC(3)))); + + END_TEST; +} + +BEGIN_TEST_CASE(FsRecoveryTest) +RUN_TEST(EmptyPartitionRecoveryTest) +END_TEST_CASE(FsRecoveryTest) + +} // namespace + diff --git a/zircon/system/utest/fs-test-utils/perftest_test.cpp b/zircon/system/utest/fs-test-utils/perftest_test.cpp index 1ab4e33cd91..2f718eee534 100644 --- a/zircon/system/utest/fs-test-utils/perftest_test.cpp +++ b/zircon/system/utest/fs-test-utils/perftest_test.cpp @@ -20,7 +20,7 @@ namespace fs_test_utils { namespace { // File used to dump libs stdout. Allows verifying certain options. -constexpr char kFakeStdout[] = "/data/fake_stdout.txt"; +constexpr char kFakeStdout[] = "/tmp/fake_stdout.txt"; bool ResultSetIsValid() { BEGIN_TEST; -- GitLab