blob: c208f1f25065033c3796a7dc4929dbee79f7393b [file] [log] [blame]
// 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/device/block.h>
#include <zircon/hw/gpt.h>
#include <zxtest/zxtest.h>
#include "block-device-interface.h"
#include "encrypted-volume-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 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__);
}
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 which is smaller than the expected header size
TEST(AddDeviceTestCase, AddSmallDevice) {
class SmallDevice : 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;
}
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 = 1;
*out_info = info;
return ZX_OK;
}
};
SmallDevice 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_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_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_OK(device.Add());
EXPECT_TRUE(device.attached);
}
// Tests adding blobfs which does not not have a valid 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 {
*out_guid = GUID_TEST_VALUE;
return ZX_OK;
}
zx_status_t CheckFilesystem() final {
ADD_FATAL_FAILURE("Should not check filesystem");
return ZX_OK;
}
zx_status_t MountFilesystem() final {
ADD_FATAL_FAILURE("Should not mount filesystem");
return ZX_OK;
}
};
BlobDevice device;
EXPECT_EQ(ZX_ERR_INVALID_ARGS, 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_OK(device.Add());
EXPECT_TRUE(device.checked);
EXPECT_FALSE(device.formatted);
EXPECT_TRUE(device.mounted);
}
// Tests adding minfs which does not not have a valid type GUID.
TEST(AddDeviceTestCase, AddNoGUIDMinfsDevice) {
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 {
*out_guid = GUID_TEST_VALUE;
return ZX_OK;
}
zx_status_t CheckFilesystem() final {
ADD_FATAL_FAILURE("Should not check filesystem");
return ZX_OK;
}
zx_status_t MountFilesystem() final {
ADD_FATAL_FAILURE("Should not mount filesystem");
return ZX_OK;
}
};
MinfsDevice device;
EXPECT_EQ(ZX_ERR_INVALID_ARGS, device.Add());
}
// 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_OK(device.Add());
EXPECT_TRUE(device.checked);
EXPECT_TRUE(device.formatted);
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_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_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_OK(device.Add());
EXPECT_FALSE(device.checked_unsealed_zxcrypt);
}
TEST(AddDeviceTestCase, AddPermanentlyMiskeyedZxcryptVolume) {
class ZxcryptVolume : public devmgr::EncryptedVolumeInterface {
public:
zx_status_t Unseal() final {
// Simulate a device where we've lost the key -- can't unlock until we
// format the device with a new key, but can afterwards.
if (formatted) {
postformat_unseal_attempt_count++;
return ZX_OK;
} else {
preformat_unseal_attempt_count++;
return ZX_ERR_ACCESS_DENIED;
}
}
zx_status_t Format() final {
formatted = true;
return ZX_OK;
}
int preformat_unseal_attempt_count = 0;
int postformat_unseal_attempt_count = 0;
bool formatted = false;
};
ZxcryptVolume volume;
EXPECT_OK(volume.EnsureUnsealedAndFormatIfNeeded());
EXPECT_TRUE(volume.preformat_unseal_attempt_count > 1);
EXPECT_TRUE(volume.formatted);
EXPECT_EQ(volume.postformat_unseal_attempt_count, 1);
}
TEST(AddDeviceTestCase, AddTransientlyMiskeyedZxcryptVolume) {
class ZxcryptVolume : public devmgr::EncryptedVolumeInterface {
public:
zx_status_t Unseal() final {
// Simulate a transient error -- fail the first time we try to unseal the
// volume, but succeed on a retry or any subsequent attempt.
unseal_attempt_count++;
if (unseal_attempt_count > 1) {
return ZX_OK;
} else {
return ZX_ERR_ACCESS_DENIED;
}
}
zx_status_t Format() final {
// We expect this to never be called.
formatted = true;
return ZX_OK;
}
int unseal_attempt_count = 0;
bool formatted = false;
};
ZxcryptVolume volume;
EXPECT_OK(volume.EnsureUnsealedAndFormatIfNeeded());
EXPECT_FALSE(volume.formatted);
EXPECT_EQ(volume.unseal_attempt_count, 2);
}
TEST(AddDeviceTestCase, AddFailingZxcryptVolumeShouldNotFormat) {
class ZxcryptVolume : public devmgr::EncryptedVolumeInterface {
public:
zx_status_t Unseal() final {
// Errors that are not ZX_ERR_ACCESS_DENIED should not trigger
// formatting.
return ZX_ERR_INTERNAL;
}
zx_status_t Format() final {
// Expect this to not be called.
formatted = true;
return ZX_OK;
}
bool formatted = false;
};
ZxcryptVolume volume;
EXPECT_EQ(ZX_ERR_INTERNAL, volume.EnsureUnsealedAndFormatIfNeeded());
EXPECT_FALSE(volume.formatted);
}
} // namespace