| // Copyright 2018 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 <limits.h> |
| #include <stdint.h> |
| |
| namespace { |
| |
| enum class FsTestType { |
| // The partition may appear as any generic block device |
| kNormal, |
| |
| // The partition should appear on top of a resizable |
| // FVM device |
| kFvm, |
| }; |
| |
| enum class FsTestState { |
| kInit, // Just created, waiting to be initialized. |
| kMinimal, // Initialized in a minimal state, i.e. ramdisk only. |
| kRunning, // Initialized and ready to start testing. |
| kComplete, // Indicates that the test has completed. |
| kError, // Indicates that an error has occurred. |
| }; |
| |
| class BlobfsTest { |
| public: |
| BlobfsTest(FsTestType type) : type_(type) {} |
| ~BlobfsTest(); |
| |
| // Creates a ramdisk, formats it, and mounts it at a mount point. |
| // |state| indicates the intended state once initialization is complete. |
| // This value must be either kMinimal or kRunning. |
| // If |state| is kMinimal, the mkfs and mount methods will be skipped. |
| bool Init(FsTestState state = FsTestState::kRunning); |
| |
| // Unmounts and remounts the blobfs partition. |
| bool Remount(); |
| |
| // Forcibly unmounts and remounts the blobfs partition, regardless of the current test state. |
| // If the partition is successfully remounted, the test is restored to a kRunning state. |
| // If |fsck_result| is not nullptr, fsck is run before remount and |fsck_result| is set to |
| // the result. |
| bool ForceRemount(zx_status_t* fsck_result = nullptr); |
| |
| // Unmounts a blobfs, runs fsck, and removes the backing ramdisk device. |
| // If the state_ is not kRunning, the umount and fsck methods will be skipped. |
| bool Teardown(); |
| |
| FsTestType GetType() const { |
| return type_; |
| } |
| |
| int GetFd() const { |
| return open(ramdisk_path_, O_RDWR); |
| } |
| |
| off_t GetDiskSize() const { |
| return blk_size_ * blk_count_; |
| } |
| |
| uint64_t GetBlockSize() const { |
| return blk_size_; |
| } |
| |
| uint64_t GetBlockCount() const { |
| return blk_count_; |
| } |
| |
| // Returns the full device path of blobfs. |
| bool GetDevicePath(char* path, size_t len) const; |
| |
| // Given a new disk size, updates the block count. Block size doesn't change. |
| bool SetBlockCount(uint64_t block_count) { |
| BEGIN_HELPER; |
| ASSERT_EQ(state_, FsTestState::kInit); |
| blk_count_ = block_count; |
| END_HELPER; |
| } |
| |
| // Sets readonly to |readonly|, defaulting to true. |
| void SetReadOnly(bool read_only) { |
| read_only_ = read_only; |
| } |
| |
| // Determine if the mounted filesystem should have output to stdio. |
| void SetStdio(bool stdio) { |
| stdio_ = stdio; |
| } |
| |
| // Reset to initial state, given that the test was successfully torn down. |
| bool Reset() { |
| BEGIN_HELPER; |
| ASSERT_EQ(state_, FsTestState::kComplete); |
| state_ = FsTestState::kInit; |
| END_HELPER; |
| } |
| |
| // Forcibly resets a running test by destroying and recreating the Blobfs partition. |
| bool ForceReset(); |
| |
| // Sleeps or wakes the ramdisk underlying the blobfs partition, depending on its current state. |
| bool ToggleSleep(uint64_t blk_count = 0); |
| |
| // Returns the current total transaction block count from the underlying ramdisk. |
| bool GetRamdiskCount(uint64_t* blk_count) const; |
| |
| // Checks info of mounted blobfs. |
| // |
| // Returns total number of bytes available as |total_bytes|. |
| // Returns total number of used bytes as |used_bytes|. |
| bool CheckInfo(uint64_t* total_bytes = nullptr, uint64_t* used_bytes = nullptr); |
| |
| private: |
| // Mounts the blobfs partition. |
| bool Mount(); |
| |
| FsTestType type_; |
| FsTestState state_ = FsTestState::kInit; |
| uint64_t blk_size_ = 512; |
| uint64_t blk_count_ = 1 << 20; |
| char ramdisk_path_[PATH_MAX]; |
| char fvm_path_[PATH_MAX]; |
| bool read_only_ = false; |
| bool asleep_ = false; |
| bool stdio_ = true; |
| }; |
| |
| // Mock journal implementation which can be used to test JournalEntry / JournalProcessor |
| // functionality. |
| class FakeJournal : public blobfs::JournalBase { |
| public: |
| FakeJournal() : readonly_(false), capacity_(0) {} |
| |
| void SendSignal(zx_status_t status) final { |
| if (status != ZX_OK) { |
| readonly_ = true; |
| } |
| } |
| |
| fbl::unique_ptr<blobfs::WritebackWork> CreateDefaultWork() { |
| return CreateWork(); |
| } |
| |
| fbl::unique_ptr<blobfs::WritebackWork> CreateBufferedWork(size_t block_count) { |
| fbl::unique_ptr<blobfs::WritebackWork> work = CreateWork(); |
| work->Enqueue(1, 0, 0, block_count); |
| work->SetBuffer(2); |
| return fbl::move(work); |
| } |
| |
| private: |
| size_t GetCapacity() const final { |
| return capacity_; |
| } |
| |
| bool IsReadOnly() const final { |
| return readonly_; |
| } |
| |
| fbl::unique_ptr<blobfs::WritebackWork> CreateWork() final { |
| return fbl::make_unique<blobfs::WritebackWork>(nullptr, nullptr); |
| } |
| |
| // The following functions are no-ops, and only exist so they can be called by the |
| // JournalProcessor. |
| void PrepareBuffer(blobfs::JournalEntry* entry) final {} |
| void PrepareDelete(blobfs::JournalEntry* entry, blobfs::WritebackWork* work) final {} |
| zx_status_t EnqueueEntryWork(fbl::unique_ptr<blobfs::WritebackWork> work) final { |
| return ZX_OK; |
| } |
| |
| bool readonly_; |
| size_t capacity_; |
| }; |
| |
| } // namespace |