blob: 47edf56227c359100a9f975bd74a376589a00b67 [file] [log] [blame]
// Copyright 2023 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 <unordered_set>
#include <gtest/gtest.h>
#include "src/storage/f2fs/f2fs.h"
#include "src/storage/lib/block_client/cpp/fake_block_device.h"
#include "unit_lib.h"
namespace f2fs {
namespace {
class BitmapTest : public F2fsFakeDevTestFixture {
public:
BitmapTest()
: F2fsFakeDevTestFixture(TestOptions{
.block_count = 100 * 1024 * 1024 / kDefaultSectorSize,
}) {}
zx::result<fbl::RefPtr<VnodeF2fs>> Create(std::string name, bool is_file = true) {
zx::result vnode =
root_dir_->Create(name, is_file ? fs::CreationType::kFile : fs::CreationType::kDirectory);
if (vnode.is_error()) {
return vnode.take_error();
}
return zx::ok(fbl::RefPtr<VnodeF2fs>::Downcast(*std::move(vnode)));
}
};
TEST_F(BitmapTest, GetBitmap) {
auto test_file = Create("file");
auto test_dir1 = Create("dir1", false);
auto test_dir2 = Create("dir2", false);
ASSERT_TRUE(test_file.is_ok());
ASSERT_TRUE(test_dir1.is_ok());
ASSERT_TRUE(test_dir2.is_ok());
LockedPage dir_page1, file_page, dir_page2, dir1_node_page;
ASSERT_EQ(test_dir1->GrabCachePage(0, &dir_page1), ZX_OK);
ASSERT_EQ(test_dir2->GrabCachePage(0, &dir_page2), ZX_OK);
ASSERT_EQ(test_file->GrabCachePage(0, &file_page), ZX_OK);
auto bits = test_file->GetBitmap(file_page.CopyRefPtr());
ASSERT_EQ(bits.status_value(), ZX_ERR_NOT_SUPPORTED);
bits = test_dir1->GetBitmap(dir_page1.CopyRefPtr());
ASSERT_EQ(bits.status_value(), ZX_ERR_INVALID_ARGS);
test_dir1->ClearFlag(InodeInfoFlag::kInlineDentry);
bits = test_dir1->GetBitmap(dir_page2.CopyRefPtr());
ASSERT_EQ(bits.status_value(), ZX_ERR_INVALID_ARGS);
bits = test_dir1->GetBitmap(dir_page1.CopyRefPtr());
ASSERT_TRUE(bits.is_ok());
test_dir1->SetFlag(InodeInfoFlag::kInlineDentry);
fs_->GetNodeManager().GetNodePage(test_dir1->GetKey(), &dir1_node_page);
bits = test_dir1->GetBitmap(dir1_node_page.CopyRefPtr());
ASSERT_TRUE(bits.is_ok());
test_file->Close();
test_dir1->Close();
test_dir2->Close();
}
TEST_F(BitmapTest, BasicOp) {
auto file = Create("dir", false);
ASSERT_TRUE(file.is_ok());
LockedPage page;
ASSERT_EQ(file->GrabCachePage(0, &page), ZX_OK);
size_t size = GetBitSize(page->Size());
size_t off = size;
PageBitmap bits(page.CopyRefPtr(), page->GetAddress(), off);
ASSERT_EQ(bits.Set(off), false);
ASSERT_EQ(bits.Test(off), false);
ASSERT_EQ(bits.Clear(off), false);
ASSERT_EQ(bits.FindNextZeroBit(0), 0U);
ASSERT_EQ(bits.FindNextZeroBit(off), off);
ASSERT_EQ(bits.FindNextBit(0), off);
ASSERT_EQ(bits.FindNextBit(off), off);
--off;
ASSERT_EQ(bits.Set(off), false);
ASSERT_EQ(bits.Test(off), true);
ASSERT_EQ(bits.FindNextZeroBit(0), 0U);
ASSERT_EQ(bits.FindNextBit(0), off);
ASSERT_EQ(bits.Clear(off), true);
size_t msb_first = off - (off & kLastNodeMask) + (7 - off & kLastNodeMask);
ASSERT_EQ(ToMsbFirst(off), msb_first);
ASSERT_EQ(bits.Set(ToMsbFirst(0)), false);
ASSERT_EQ(bits.Test(7), true);
ASSERT_EQ(bits.Test(0), false);
file->Close();
}
TEST_F(BitmapTest, CountBits) {
size_t kNumBits = GetBitSize(kPageSize * 2);
RawBitmap bits;
bits.Reset(kNumBits);
ASSERT_EQ(CountBits(bits, kNumBits, kNumBits), 0UL);
ASSERT_EQ(CountBits(bits, 0, kNumBits), 0UL);
ASSERT_EQ(bits.SetOne(kPageSize), ZX_OK);
ASSERT_EQ(CountBits(bits, 0, kNumBits), 1UL);
ASSERT_EQ(bits.ClearOne(kPageSize), ZX_OK);
for (size_t i = 0; i < kNumBits; ++i) {
if (i & 1U)
bits.SetOne(i);
}
ASSERT_EQ(CountBits(bits, 0, kNumBits), kNumBits / 2);
ASSERT_EQ(CountBits(bits, kNumBits, kNumBits), 0UL);
}
TEST_F(BitmapTest, CloneBits) {
size_t num_bytes = kPageSize * 2;
size_t num_bits = GetBitSize(kPageSize * 2);
RawBitmap bits1;
RawBitmap bits2;
auto raw_bits = std::make_unique<uint8_t[]>(num_bytes);
memset(raw_bits.get(), 0xAA, num_bytes);
bits1.Reset(num_bits);
bits2.Reset(num_bits);
// Byte-aligned copy
ASSERT_EQ(CloneBits(bits1, raw_bits.get(), num_bits, num_bits), ZX_ERR_INVALID_ARGS);
ASSERT_EQ(CloneBits(bits1, raw_bits.get(), 0, num_bits), ZX_OK);
ASSERT_EQ(memcmp(bits1.StorageUnsafe()->GetData(), raw_bits.get(), num_bytes), 0);
size_t offset = GetBitSize(kPageSize);
ASSERT_EQ(CloneBits(bits2, bits1, num_bits, num_bits), ZX_ERR_INVALID_ARGS);
ASSERT_EQ(CloneBits(bits2, bits1, offset, num_bits - offset), ZX_OK);
ASSERT_NE(memcmp(bits2.StorageUnsafe()->GetData(), raw_bits.get(), num_bytes), 0);
ASSERT_EQ(memcmp(static_cast<uint8_t *>(bits2.StorageUnsafe()->GetData()) + GetByteSize(offset),
raw_bits.get(), GetByteSize(num_bits - offset)),
0);
ASSERT_EQ(CloneBits(raw_bits.get(), bits2, 0, num_bits * 2), ZX_ERR_INVALID_ARGS);
ASSERT_EQ(CloneBits(raw_bits.get(), bits2, 0, num_bits), ZX_OK);
ASSERT_EQ(memcmp(bits2.StorageUnsafe()->GetData(), raw_bits.get(), num_bytes), 0);
ASSERT_NE(memcmp(bits1.StorageUnsafe()->GetData(), raw_bits.get(), num_bytes), 0);
// Byte-unaligned copy
ASSERT_EQ(CloneBits(bits1, raw_bits.get(), 1, num_bits), ZX_ERR_INVALID_ARGS);
}
} // namespace
} // namespace f2fs