| // 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 "src/storage/lib/paver/fvm.h" |
| |
| #include <lib/driver-integration-test/fixture.h> |
| |
| #include <zxtest/zxtest.h> |
| |
| #include "src/lib/storage/fs_management/cpp/fvm.h" |
| #include "src/storage/fvm/format.h" |
| #include "src/storage/fvm/fvm_sparse.h" |
| #include "src/storage/lib/paver/test/test-utils.h" |
| |
| namespace { |
| |
| using device_watcher::RecursiveWaitForFile; |
| using driver_integration_test::IsolatedDevmgr; |
| |
| constexpr size_t kSliceSize = kBlockSize * 2; |
| constexpr uint8_t kFvmType[GPT_GUID_LEN] = GUID_FVM_VALUE; |
| |
| constexpr fvm::SparseImage SparseHeaderForSliceSize(size_t slice_size) { |
| fvm::SparseImage header = {}; |
| header.slice_size = slice_size; |
| return header; |
| } |
| |
| constexpr fvm::SparseImage SparseHeaderForSliceSizeAndMaxDiskSize(size_t slice_size, |
| size_t max_disk_size) { |
| fvm::SparseImage header = SparseHeaderForSliceSize(slice_size); |
| header.maximum_disk_size = max_disk_size; |
| return header; |
| } |
| |
| class FvmTest : public zxtest::Test { |
| public: |
| FvmTest() { |
| IsolatedDevmgr::Args args; |
| args.disable_block_watcher = true; |
| |
| ASSERT_OK(IsolatedDevmgr::Create(&args, &devmgr_)); |
| |
| fbl::unique_fd ctl; |
| ASSERT_OK(RecursiveWaitForFile(devmgr_.devfs_root(), "sys/platform/00:00:2d/ramctl", &ctl)); |
| } |
| |
| void CreateRamdisk() { CreateRamdiskWithBlockCount(); } |
| |
| void CreateRamdiskWithBlockCount(size_t block_count = kBlockCount) { |
| ASSERT_NO_FATAL_FAILURE( |
| BlockDevice::Create(devmgr_.devfs_root(), kFvmType, block_count, &device_)); |
| ASSERT_TRUE(device_); |
| } |
| |
| int borrow_fd() { return device_->fd(); } |
| |
| fbl::unique_fd fd() { return fbl::unique_fd(dup(device_->fd())); } |
| |
| const fbl::unique_fd& devfs_root() { return devmgr_.devfs_root(); } |
| |
| protected: |
| IsolatedDevmgr devmgr_; |
| std::unique_ptr<BlockDevice> device_; |
| }; |
| |
| TEST_F(FvmTest, FormatFvmEmpty) { |
| ASSERT_NO_FAILURES(CreateRamdisk()); |
| fbl::unique_fd fvm_part = FvmPartitionFormat( |
| devfs_root(), fd(), SparseHeaderForSliceSize(kSliceSize), paver::BindOption::Reformat); |
| ASSERT_TRUE(fvm_part.is_valid()); |
| } |
| |
| TEST_F(FvmTest, TryBindEmpty) { |
| ASSERT_NO_FAILURES(CreateRamdisk()); |
| fbl::unique_fd fvm_part = FvmPartitionFormat( |
| devfs_root(), fd(), SparseHeaderForSliceSize(kSliceSize), paver::BindOption::TryBind); |
| ASSERT_TRUE(fvm_part.is_valid()); |
| } |
| |
| TEST_F(FvmTest, TryBindAlreadyFormatted) { |
| ASSERT_NO_FAILURES(CreateRamdisk()); |
| ASSERT_OK(fs_management::FvmInit(borrow_fd(), kSliceSize)); |
| fbl::unique_fd fvm_part = FvmPartitionFormat( |
| devfs_root(), fd(), SparseHeaderForSliceSize(kSliceSize), paver::BindOption::TryBind); |
| ASSERT_TRUE(fvm_part.is_valid()); |
| } |
| |
| TEST_F(FvmTest, TryBindAlreadyBound) { |
| ASSERT_NO_FAILURES(CreateRamdisk()); |
| fbl::unique_fd fvm_part = FvmPartitionFormat( |
| devfs_root(), fd(), SparseHeaderForSliceSize(kSliceSize), paver::BindOption::Reformat); |
| ASSERT_TRUE(fvm_part.is_valid()); |
| |
| fvm_part = FvmPartitionFormat(devfs_root(), fd(), SparseHeaderForSliceSize(kSliceSize), |
| paver::BindOption::TryBind); |
| ASSERT_TRUE(fvm_part.is_valid()); |
| } |
| |
| TEST_F(FvmTest, TryBindAlreadyFormattedWrongSliceSize) { |
| ASSERT_NO_FAILURES(CreateRamdisk()); |
| ASSERT_OK(fs_management::FvmInit(borrow_fd(), kSliceSize * 2)); |
| fbl::unique_fd fvm_part = FvmPartitionFormat( |
| devfs_root(), fd(), SparseHeaderForSliceSize(kSliceSize), paver::BindOption::TryBind); |
| ASSERT_TRUE(fvm_part.is_valid()); |
| } |
| |
| TEST_F(FvmTest, TryBindAlreadyFormattedWithSmallerSize) { |
| constexpr size_t kBlockDeviceInitialSize = 1000 * kSliceSize; |
| constexpr size_t kBlockDeviceMaxSize = 100000 * kSliceSize; |
| ASSERT_NO_FAILURES(CreateRamdiskWithBlockCount(kBlockDeviceMaxSize / kBlockSize)); |
| ASSERT_OK(fs_management::FvmInitPreallocated(borrow_fd(), kBlockDeviceInitialSize, |
| kBlockDeviceMaxSize, kSliceSize)); |
| // Same slice size but can reference up to 200 Slices, which is far less than what the |
| // preallocated can have. |
| fvm::SparseImage header = |
| SparseHeaderForSliceSizeAndMaxDiskSize(kSliceSize, 2 * kBlockDeviceInitialSize); |
| paver::FormatResult result; |
| fbl::unique_fd fvm_part = |
| FvmPartitionFormat(devfs_root(), fd(), header, paver::BindOption::TryBind, &result); |
| ASSERT_TRUE(fvm_part.is_valid()); |
| ASSERT_EQ(paver::FormatResult::kPreserved, result); |
| } |
| |
| TEST_F(FvmTest, TryBindAlreadyFormattedWithBiggerSize) { |
| constexpr size_t kBlockDeviceInitialSize = 1000 * kSliceSize; |
| constexpr size_t kBlockDeviceMaxSize = 100000 * kSliceSize; |
| ASSERT_NO_FAILURES(CreateRamdiskWithBlockCount(kBlockDeviceMaxSize / kBlockSize)); |
| ASSERT_OK(fs_management::FvmInitPreallocated(borrow_fd(), kBlockDeviceInitialSize, |
| kBlockDeviceMaxSize / 100, kSliceSize)); |
| // Same slice size but can reference up to 200 Slices, which is far less than what the |
| // preallocated can have. |
| fvm::SparseImage header = SparseHeaderForSliceSizeAndMaxDiskSize(kSliceSize, kBlockDeviceMaxSize); |
| paver::FormatResult result; |
| fbl::unique_fd fvm_part = |
| FvmPartitionFormat(devfs_root(), fd(), header, paver::BindOption::TryBind, &result); |
| ASSERT_TRUE(fvm_part.is_valid()); |
| ASSERT_EQ(paver::FormatResult::kReformatted, result); |
| } |
| |
| TEST_F(FvmTest, AllocateEmptyPartitions) { |
| ASSERT_NO_FAILURES(CreateRamdisk()); |
| fbl::unique_fd fvm_part = FvmPartitionFormat( |
| devfs_root(), fd(), SparseHeaderForSliceSize(kSliceSize), paver::BindOption::Reformat); |
| ASSERT_TRUE(fvm_part.is_valid()); |
| |
| ASSERT_OK(paver::AllocateEmptyPartitions(devfs_root(), fvm_part)); |
| |
| fbl::unique_fd blob(openat(devfs_root().get(), |
| "sys/platform/00:00:2d/ramctl/ramdisk-0/block/fvm/blobfs-p-1/block", |
| O_RDONLY)); |
| ASSERT_TRUE(blob.is_valid()); |
| |
| fbl::unique_fd data(openat(devfs_root().get(), |
| "sys/platform/00:00:2d/ramctl/ramdisk-0/block/fvm/data-p-2/block", |
| O_RDONLY)); |
| ASSERT_TRUE(data.is_valid()); |
| } |
| |
| TEST_F(FvmTest, WipeWithMultipleFvm) { |
| ASSERT_NO_FAILURES(CreateRamdisk()); |
| fbl::unique_fd fvm_part = FvmPartitionFormat( |
| devfs_root(), fd(), SparseHeaderForSliceSize(kSliceSize), paver::BindOption::Reformat); |
| ASSERT_TRUE(fvm_part.is_valid()); |
| |
| ASSERT_OK(paver::AllocateEmptyPartitions(devfs_root(), fvm_part)); |
| |
| { |
| fbl::unique_fd blob(openat(devfs_root().get(), |
| "sys/platform/00:00:2d/ramctl/ramdisk-0/block/fvm/blobfs-p-1/block", |
| O_RDONLY)); |
| ASSERT_TRUE(blob.is_valid()); |
| |
| fbl::unique_fd data(openat(devfs_root().get(), |
| "sys/platform/00:00:2d/ramctl/ramdisk-0/block/fvm/data-p-2/block", |
| O_RDONLY)); |
| ASSERT_TRUE(data.is_valid()); |
| } |
| |
| // Save the old device. |
| std::unique_ptr<BlockDevice> first_device = std::move(device_); |
| ASSERT_NO_FAILURES(CreateRamdisk()); |
| |
| fbl::unique_fd fvm_part2 = FvmPartitionFormat( |
| devfs_root(), fd(), SparseHeaderForSliceSize(kSliceSize), paver::BindOption::Reformat); |
| ASSERT_TRUE(fvm_part2.is_valid()); |
| |
| ASSERT_OK(paver::AllocateEmptyPartitions(devfs_root(), fvm_part2)); |
| |
| { |
| fbl::unique_fd blob(openat(devfs_root().get(), |
| "sys/platform/00:00:2d/ramctl/ramdisk-1/block/fvm/blobfs-p-1/block", |
| O_RDONLY)); |
| ASSERT_TRUE(blob.is_valid()); |
| |
| fbl::unique_fd data(openat(devfs_root().get(), |
| "sys/platform/00:00:2d/ramctl/ramdisk-1/block/fvm/data-p-2/block", |
| O_RDONLY)); |
| ASSERT_TRUE(data.is_valid()); |
| } |
| |
| std::array<uint8_t, fvm::kGuidSize> blobfs_guid = GUID_BLOB_VALUE; |
| ASSERT_OK(paver::WipeAllFvmPartitionsWithGuid(fvm_part2, blobfs_guid.data())); |
| |
| // Check we can still open the first ramdisk's blobfs: |
| { |
| fbl::unique_fd blob(openat(devfs_root().get(), |
| "sys/platform/00:00:2d/ramctl/ramdisk-0/block/fvm/blobfs-p-1/block", |
| O_RDONLY)); |
| ASSERT_TRUE(blob.is_valid()); |
| } |
| |
| // But not the second's. |
| { |
| fbl::unique_fd blob(openat(devfs_root().get(), |
| "sys/platform/00:00:2d/ramctl/ramdisk-1/block/fvm/blobfs-p-1/block", |
| O_RDONLY)); |
| ASSERT_TRUE(blob.is_valid()); |
| } |
| } |
| |
| TEST_F(FvmTest, Unbind) { |
| ASSERT_NO_FAILURES(CreateRamdisk()); |
| fbl::unique_fd fvm_part = FvmPartitionFormat( |
| devfs_root(), fd(), SparseHeaderForSliceSize(kSliceSize), paver::BindOption::Reformat); |
| ASSERT_TRUE(fvm_part.is_valid()); |
| |
| ASSERT_OK(paver::AllocateEmptyPartitions(devfs_root(), fvm_part)); |
| |
| fbl::unique_fd blob(openat(devfs_root().get(), |
| "sys/platform/00:00:2d/ramctl/ramdisk-0/block/fvm/blobfs-p-1/block", |
| O_RDONLY)); |
| ASSERT_TRUE(blob.is_valid()); |
| |
| fbl::unique_fd data(openat(devfs_root().get(), |
| "sys/platform/00:00:2d/ramctl/ramdisk-0/block/fvm/data-p-2/block", |
| O_RDONLY)); |
| ASSERT_TRUE(data.is_valid()); |
| ASSERT_OK(paver::FvmUnbind(devfs_root(), "/dev/sys/platform/00:00:2d/ramctl/ramdisk-0/block")); |
| fvm_part.reset(); |
| blob.reset(); |
| data.reset(); |
| } |
| |
| TEST_F(FvmTest, UnbindInvalidPath) { |
| ASSERT_NO_FAILURES(CreateRamdisk()); |
| fbl::unique_fd fvm_part = FvmPartitionFormat( |
| devfs_root(), fd(), SparseHeaderForSliceSize(kSliceSize), paver::BindOption::Reformat); |
| ASSERT_TRUE(fvm_part.is_valid()); |
| |
| ASSERT_OK(paver::AllocateEmptyPartitions(devfs_root(), fvm_part)); |
| |
| fbl::unique_fd blob(openat(devfs_root().get(), |
| "sys/platform/00:00:2d/ramctl/ramdisk-0/block/fvm/blobfs-p-1/block", |
| O_RDONLY)); |
| ASSERT_TRUE(blob.is_valid()); |
| |
| fbl::unique_fd data(openat(devfs_root().get(), |
| "sys/platform/00:00:2d/ramctl/ramdisk-0/block/fvm/data-p-2/block", |
| O_RDONLY)); |
| ASSERT_TRUE(data.is_valid()); |
| |
| // Path too short |
| ASSERT_EQ(paver::FvmUnbind(devfs_root(), "/dev"), ZX_ERR_INVALID_ARGS); |
| |
| // Path too long |
| char path[PATH_MAX + 2]; |
| memset(path, 'a', sizeof(path)); |
| path[sizeof(path) - 1] = '\0'; |
| ASSERT_EQ(paver::FvmUnbind(devfs_root(), path), ZX_ERR_INVALID_ARGS); |
| |
| ASSERT_OK(paver::FvmUnbind(devfs_root(), "/dev/sys/platform/00:00:2d/ramctl/ramdisk-0/block")); |
| fvm_part.reset(); |
| blob.reset(); |
| data.reset(); |
| } |
| |
| } // namespace |