blob: d941163c7a1d3de6bbdd839945333347f7637030 [file] [log] [blame]
// Copyright 2021 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 "unit_lib.h"
#include <fcntl.h>
#include <lib/zircon-internal/thread_annotations.h>
#include <fbl/unique_fd.h>
#include <gtest/gtest.h>
#include <zstd/zstd.h>
#include "src/storage/f2fs/f2fs.h"
#include "src/storage/lib/block_client/cpp/fake_block_device.h"
#include "src/storage/lib/vfs/cpp/shared_mutex.h"
#include "src/storage/volume_image/utils/lz4_decompress_reader.h"
#include "src/storage/volume_image/utils/lz4_decompressor.h"
namespace f2fs {
using block_client::FakeBlockDevice;
using Runner = ComponentRunner;
F2fsFakeDevTestFixture::F2fsFakeDevTestFixture(const TestOptions &options)
: block_count_(options.block_count),
block_size_(options.block_size),
run_fsck_(options.run_fsck),
image_file_(options.image_file) {
mkfs_options_ = options.mkfs_options;
for (auto opt : options.mount_options) {
mount_options_.SetValue(opt.first, opt.second);
}
}
void F2fsFakeDevTestFixture::SetUp() {
if (image_file_ != std::nullopt)
return LoadImage();
fbl::RefPtr<VnodeF2fs> root;
FileTester::MkfsOnFakeDevWithOptions(&bc_, mkfs_options_, block_count_);
FileTester::MountWithOptions(loop_.dispatcher(), mount_options_, &bc_, &fs_);
FileTester::CreateRoot(fs_.get(), &root);
root_dir_ = fbl::RefPtr<Dir>::Downcast(std::move(root));
}
void F2fsFakeDevTestFixture::TearDown() {
ASSERT_EQ(root_dir_->Close(), ZX_OK);
root_dir_ = nullptr;
FileTester::Unmount(std::move(fs_), &bc_);
if (run_fsck_) {
FsckWorker fsck(std::move(bc_), FsckOptions{.repair = false});
ASSERT_EQ(fsck.Run(), ZX_OK);
}
}
void F2fsFakeDevTestFixture::Remount() {
if (!root_dir_ || !fs_) {
return;
}
ASSERT_EQ(root_dir_->Close(), ZX_OK);
root_dir_ = nullptr;
FileTester::Unmount(std::move(fs_), &bc_);
FileTester::MountWithOptions(loop_.dispatcher(), mount_options_, &bc_, &fs_);
fbl::RefPtr<VnodeF2fs> root;
FileTester::CreateRoot(fs_.get(), &root);
root_dir_ = fbl::RefPtr<Dir>::Downcast(std::move(root));
}
void F2fsFakeDevTestFixture::LoadImage() {
auto device =
std::make_unique<block_client::FakeBlockDevice>(block_client::FakeBlockDevice::Config{
.block_count = block_count_, .block_size = kDefaultSectorSize, .supports_trim = false});
auto bc_or = CreateBcacheMapper(std::move(device), true);
ASSERT_TRUE(bc_or.is_ok());
bc_ = std::move(*bc_or);
std::string file = "pkg/data/" + image_file_.value();
auto fd = fbl::unique_fd(open(file.data(), O_RDONLY));
ASSERT_TRUE(fd.is_valid());
struct stat s;
fstat(fd.get(), &s);
size_t compressed_size = s.st_size;
auto compressed = std::make_unique<char[]>(compressed_size);
size_t iter = 0;
while (iter < compressed_size) {
size_t cur = read(fd.get(), &(compressed[iter]), compressed_size - iter);
iter += cur;
}
size_t uncompressed_size = block_count_ * block_size_;
auto uncompressed = std::make_unique<char[]>(uncompressed_size);
ZSTD_decompress(uncompressed.get(), uncompressed_size, compressed.get(), compressed_size);
for (uint32_t i = 0; i < uncompressed_size / bc_->BlockSize(); ++i) {
ASSERT_EQ(bc_->Writeblk(i, uncompressed.get() + i * bc_->BlockSize()), ZX_OK);
}
if (run_fsck_) {
FsckWorker fsck(std::move(bc_), FsckOptions{.repair = false});
ASSERT_EQ(fsck.Run(), ZX_OK);
bc_ = fsck.Destroy();
}
fbl::RefPtr<VnodeF2fs> root;
FileTester::MountWithOptions(loop_.dispatcher(), mount_options_, &bc_, &fs_);
FileTester::CreateRoot(fs_.get(), &root);
root_dir_ = fbl::RefPtr<Dir>::Downcast(std::move(root));
}
void FileTester::MkfsOnFakeDev(std::unique_ptr<BcacheMapper> *bc, uint64_t block_count,
uint32_t block_size, bool btrim) {
auto device = std::make_unique<FakeBlockDevice>(FakeBlockDevice::Config{
.block_count = block_count, .block_size = block_size, .supports_trim = btrim});
auto bc_or = CreateBcacheMapper(std::move(device), true);
ASSERT_TRUE(bc_or.is_ok());
MkfsOptions options;
MkfsWorker mkfs(std::move(*bc_or), options);
auto ret = mkfs.DoMkfs();
ASSERT_EQ(ret.is_error(), false);
*bc = std::move(*ret);
}
void FileTester::MkfsOnFakeDevWithOptions(std::unique_ptr<BcacheMapper> *bc,
const MkfsOptions &options, uint64_t block_count,
uint32_t block_size, bool btrim) {
auto device = std::make_unique<FakeBlockDevice>(FakeBlockDevice::Config{
.block_count = block_count, .block_size = block_size, .supports_trim = btrim});
auto bc_or = CreateBcacheMapper(std::move(device), true);
ASSERT_TRUE(bc_or.is_ok());
MkfsWorker mkfs(std::move(*bc_or), options);
auto ret = mkfs.DoMkfs();
ASSERT_EQ(ret.is_error(), false);
*bc = std::move(*ret);
}
void FileTester::MountWithOptions(async_dispatcher_t *dispatcher, const MountOptions &options,
std::unique_ptr<BcacheMapper> *bc, std::unique_ptr<F2fs> *fs) {
// Create a vfs object for unit tests.
auto vfs_or = Runner::CreateRunner(dispatcher);
ASSERT_TRUE(vfs_or.is_ok());
auto readonly_or = options.GetValue(MountOption::kReadOnly);
if (*readonly_or) {
vfs_or->SetReadonly(true);
}
auto fs_or = F2fs::Create(nullptr, std::move(*bc), options, (*vfs_or).get());
ASSERT_TRUE(fs_or.is_ok());
(*fs_or)->SetVfsForTests(std::move(*vfs_or));
*fs = std::move(*fs_or);
}
void FileTester::Unmount(std::unique_ptr<F2fs> fs, std::unique_ptr<BcacheMapper> *bc) {
fs->Sync();
fs->PutSuper();
auto vfs_or = fs->TakeVfsForTests();
ASSERT_TRUE(vfs_or.is_ok());
auto bc_or = fs->TakeBc();
ASSERT_TRUE(bc_or.is_ok());
*bc = std::move(*bc_or);
// Trigger teardown before deleting fs.
(*vfs_or).reset();
}
void FileTester::SuddenPowerOff(std::unique_ptr<F2fs> fs, std::unique_ptr<BcacheMapper> *bc) {
fs->GetVCache().ForDirtyVnodesIf([&](fbl::RefPtr<VnodeF2fs> &vnode) {
vnode->ResetFileCache();
vnode->ClearDirty();
return ZX_OK;
});
fs->GetVCache().Reset();
// destroy f2fs internal modules
fs->Reset();
auto vfs_for_tests = fs->TakeVfsForTests();
ASSERT_TRUE(vfs_for_tests.is_ok());
auto bc_or = fs->TakeBc();
ASSERT_TRUE(bc_or.is_ok());
*bc = std::move(*bc_or);
// Trigger teardown before deleting fs.
(*vfs_for_tests).reset();
}
void FileTester::CreateRoot(F2fs *fs, fbl::RefPtr<VnodeF2fs> *out) {
auto vnode_or = fs->GetVnode(fs->GetSuperblockInfo().GetRootIno());
ASSERT_TRUE(vnode_or.is_ok());
ASSERT_EQ(vnode_or->Open(nullptr), ZX_OK);
*out = *std::move(vnode_or);
}
void FileTester::Lookup(VnodeF2fs *parent, std::string_view name, fbl::RefPtr<fs::Vnode> *out) {
fbl::RefPtr<fs::Vnode> vn = nullptr;
if (zx_status_t status = parent->Lookup(name, &vn); status != ZX_OK) {
*out = nullptr;
return;
}
ASSERT_TRUE(vn);
ASSERT_EQ(vn->Open(nullptr), ZX_OK);
*out = std::move(vn);
}
void FileTester::CreateChild(Dir *vn, umode_t mode, std::string_view name) {
zx::result tmp_child = vn->CreateWithMode(name, mode);
ASSERT_TRUE(tmp_child.is_ok()) << tmp_child.status_string();
ASSERT_EQ(tmp_child->Close(), ZX_OK);
}
void FileTester::DeleteChild(Dir *vn, std::string_view name, bool is_dir) {
ASSERT_EQ(vn->Unlink(name, is_dir), ZX_OK);
// TODO: After EvictInode available, check if nids of the child are correctly freed
}
void FileTester::RenameChild(fbl::RefPtr<Dir> &old_vnode, fbl::RefPtr<Dir> &new_vnode,
std::string_view oldname, std::string_view newname) {
ASSERT_EQ(old_vnode->Rename(new_vnode, oldname, newname, false, false), ZX_OK);
}
void FileTester::CreateChildren(F2fs *fs, std::vector<fbl::RefPtr<VnodeF2fs>> &vnodes,
std::vector<uint32_t> &inos, fbl::RefPtr<Dir> &parent,
std::string name, size_t inode_cnt) {
for (uint32_t i = 0; i < inode_cnt; ++i) {
std::string file_name = name + std::to_string(i);
zx::result test_file = parent->Create(file_name, fs::CreationType::kFile);
ASSERT_TRUE(test_file.is_ok()) << test_file.status_string();
fbl::RefPtr<VnodeF2fs> test_file_vn = fbl::RefPtr<VnodeF2fs>::Downcast(*std::move(test_file));
inos.push_back(test_file_vn->Ino());
vnodes.push_back(std::move(test_file_vn));
}
}
void FileTester::DeleteChildren(std::vector<fbl::RefPtr<VnodeF2fs>> &vnodes,
fbl::RefPtr<Dir> &parent, size_t inode_cnt) {
uint32_t deleted_file_cnt = 0;
for (const auto &iter : vnodes) {
ASSERT_EQ(parent->Unlink(iter->GetNameView(), false), ZX_OK);
++deleted_file_cnt;
}
ASSERT_EQ(deleted_file_cnt, inode_cnt);
}
void FileTester::VnodeWithoutParent(F2fs *fs, umode_t mode,
fbl::RefPtr<VnodeF2fs> &vnode) TA_NO_THREAD_SAFETY_ANALYSIS {
nid_t inode_nid;
auto nid_or = fs->GetNodeManager().AllocNid();
ASSERT_TRUE(nid_or.is_ok());
inode_nid = *nid_or;
if (S_ISDIR(mode)) {
vnode = fbl::MakeRefCounted<Dir>(fs, inode_nid, mode);
} else {
vnode = fbl::MakeRefCounted<File>(fs, inode_nid, mode);
}
vnode->ClearFlag(InodeInfoFlag::kNewInode);
ASSERT_EQ(vnode->Open(nullptr), ZX_OK);
fs->GetVCache().Add(vnode.get());
vnode->ClearLinkCount();
vnode->SetOrphan();
}
void FileTester::CheckInlineDir(VnodeF2fs *vn) {
ASSERT_NE(vn->TestFlag(InodeInfoFlag::kInlineDentry), false);
ASSERT_EQ(vn->GetSize(), vn->MaxInlineData());
}
void FileTester::CheckNonInlineDir(VnodeF2fs *vn) {
ASSERT_EQ(vn->TestFlag(InodeInfoFlag::kInlineDentry), false);
ASSERT_GT(vn->GetSize(), vn->MaxInlineData());
}
void FileTester::CheckInlineFile(VnodeF2fs *vn) {
ASSERT_NE(vn->TestFlag(InodeInfoFlag::kInlineData), false);
}
void FileTester::CheckNonInlineFile(VnodeF2fs *vn) {
ASSERT_EQ(vn->TestFlag(InodeInfoFlag::kInlineData), false);
}
void FileTester::CheckDataExistFlagSet(VnodeF2fs *vn) {
ASSERT_NE(vn->TestFlag(InodeInfoFlag::kDataExist), false);
}
void FileTester::CheckDataExistFlagUnset(VnodeF2fs *vn) {
ASSERT_EQ(vn->TestFlag(InodeInfoFlag::kDataExist), false);
}
void FileTester::CheckInlineXattr(VnodeF2fs *vn) {
ASSERT_NE(vn->TestFlag(InodeInfoFlag::kInlineXattr), false);
}
void FileTester::CheckChildrenFromReaddir(Dir *dir, std::unordered_set<std::string> childs) {
childs.insert(".");
fs::VdirCookie cookie;
uint8_t buf[kBlockSize];
size_t len;
ASSERT_EQ(dir->Readdir(&cookie, buf, sizeof(buf), &len), ZX_OK);
uint8_t *buf_ptr = buf;
while (len > 0 && buf_ptr < buf + kBlockSize) {
auto entry = reinterpret_cast<const fs::DirectoryEntry *>(buf_ptr);
size_t entry_size = entry->name_length + sizeof(fs::DirectoryEntry);
std::string_view entry_name(entry->name, entry->name_length);
auto iter = childs.begin();
for (; iter != childs.end(); ++iter) {
if (entry_name == *iter) {
break;
}
}
ASSERT_NE(iter, childs.end());
childs.erase(iter);
buf_ptr += entry_size;
len -= entry_size;
}
ASSERT_TRUE(childs.empty());
}
void FileTester::CheckChildrenInBlock(Dir *vn, uint64_t bidx,
std::unordered_set<std::string> childs) {
if (bidx == 0) {
childs.insert(".");
childs.insert("..");
}
if (childs.empty()) {
ASSERT_EQ(vn->FindDataPage(bidx).status_value(), ZX_ERR_NOT_FOUND);
return;
}
auto page_or = vn->FindDataPage(bidx);
ZX_ASSERT(page_or.is_ok());
DentryBlock *dentry_blk = page_or->GetAddress<DentryBlock>();
PageBitmap dentry_bitmap(dentry_blk->dentry_bitmap, kNrDentryInBlock);
size_t bit_pos = 0;
while ((bit_pos = dentry_bitmap.FindNextBit(bit_pos)) < kNrDentryInBlock) {
DirEntry *de = &dentry_blk->dentry[bit_pos];
uint32_t slots = (LeToCpu(de->name_len) + kNameLen - 1) / kNameLen;
std::string_view dir_entry_name(reinterpret_cast<char *>(dentry_blk->filename[bit_pos]),
LeToCpu(de->name_len));
auto iter = childs.begin();
for (; iter != childs.end(); ++iter) {
if (dir_entry_name == *iter) {
break;
}
}
ASSERT_NE(iter, childs.end());
childs.erase(iter);
bit_pos += slots;
}
ASSERT_TRUE(childs.empty());
}
std::string FileTester::GetRandomName(unsigned int len) {
const char *char_list = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
auto char_list_len = strlen(char_list);
auto generator = [&]() { return char_list[rand() % char_list_len]; };
std::string str(len, 0);
std::generate_n(str.begin(), len, generator);
return str;
}
void FileTester::AppendToInline(File *file, const void *data, size_t len) {
size_t offset = file->GetSize();
size_t ret = 0;
if (file->TestFlag(InodeInfoFlag::kInlineData) && offset + len < file->MaxInlineData()) {
ASSERT_EQ(file->WriteInline(data, len, offset, &ret), ZX_OK);
file->SetSize(std::max(file->GetSize(), offset + len));
ASSERT_EQ(ret, len);
}
}
void FileTester::AppendToFile(File *file, const void *data, size_t len) {
size_t actual;
size_t end;
ASSERT_EQ(Append(file, data, len, &end, &actual), ZX_OK);
ASSERT_EQ(actual, len);
}
void FileTester::ReadFromFile(File *file, void *data, size_t len, size_t off) {
size_t actual;
ASSERT_EQ(Read(file, data, len, off, &actual), ZX_OK);
ASSERT_EQ(actual, len);
}
zx_status_t FileTester::Read(File *file, void *data, size_t len, size_t off, size_t *out_actual) {
zx::result<zx::stream> stream = file->CreateStream(ZX_STREAM_MODE_READ);
if (stream.is_error()) {
return stream.error_value();
}
zx_iovec_t iov = {
.buffer = data,
.capacity = len,
};
return stream->readv_at(0, off, &iov, 1, out_actual);
}
zx_status_t FileTester::Write(File *file, const void *data, size_t len, size_t offset,
size_t *out_actual) {
zx::result<zx::stream> stream = file->CreateStream(ZX_STREAM_MODE_WRITE);
if (stream.is_error()) {
return stream.error_value();
}
// Since zx_iovec_t::buffer is not a const type, we make a copied buffer and use it.
auto copied = std::make_unique<uint8_t[]>(len);
std::memcpy(copied.get(), data, len);
zx_iovec_t iov = {
.buffer = copied.get(),
.capacity = len,
};
return stream->writev_at(0, offset, &iov, 1, out_actual);
}
zx_status_t FileTester::Append(File *file, const void *data, size_t len, size_t *out_end,
size_t *out_actual) {
*out_end = file->GetSize();
return Write(file, data, len, *out_end, out_actual);
}
void MapTester::CheckNodeLevel(F2fs *fs, VnodeF2fs *vn, uint32_t level) {
LockedPage ipage;
ASSERT_EQ(fs->GetNodeManager().GetNodePage(vn->Ino(), &ipage), ZX_OK);
Inode &inode = ipage->GetAddress<Node>()->i;
uint32_t i;
for (i = 0; i < level; ++i)
ASSERT_NE(inode.i_nid[i], 0U);
for (; i < kNidsPerInode; ++i)
ASSERT_EQ(inode.i_nid[i], 0U);
}
void MapTester::CheckNidsFree(F2fs *fs, std::unordered_set<nid_t> &nids) {
NodeManager &nm_i = fs->GetNodeManager();
std::lock_guard lock(nm_i.free_nid_tree_lock_);
for (auto nid : nids) {
bool found = false;
for (const auto &iter : nm_i.free_nid_tree_) {
if (iter == nid) {
found = true;
break;
}
}
ASSERT_TRUE(found);
}
}
void MapTester::CheckNidsInuse(F2fs *fs, std::unordered_set<nid_t> &nids) {
NodeManager &nm_i = fs->GetNodeManager();
std::lock_guard lock(nm_i.free_nid_tree_lock_);
for (auto nid : nids) {
bool found = false;
for (const auto &iter : nm_i.free_nid_tree_) {
if (iter == nid) {
found = true;
break;
}
}
ASSERT_FALSE(found);
}
}
void MapTester::CheckBlkaddrsFree(F2fs *fs, std::unordered_set<block_t> &blkaddrs) {
SuperblockInfo &superblock_info = fs->GetSuperblockInfo();
for (auto blkaddr : blkaddrs) {
SegmentManager &manager = fs->GetSegmentManager();
const SegmentEntry &se = manager.GetSegmentEntry(manager.GetSegmentNumber(blkaddr));
uint32_t offset = manager.GetSegOffFromSeg0(blkaddr) & (superblock_info.GetBlocksPerSeg() - 1);
ASSERT_EQ(se.ckpt_valid_map.GetOne(ToMsbFirst(offset)), false);
}
}
void MapTester::CheckBlkaddrsInuse(F2fs *fs, std::unordered_set<block_t> &blkaddrs) {
SuperblockInfo &superblock_info = fs->GetSuperblockInfo();
for (auto blkaddr : blkaddrs) {
SegmentManager &manager = fs->GetSegmentManager();
const SegmentEntry &se = manager.GetSegmentEntry(manager.GetSegmentNumber(blkaddr));
uint32_t offset = manager.GetSegOffFromSeg0(blkaddr) & (superblock_info.GetBlocksPerSeg() - 1);
ASSERT_NE(se.ckpt_valid_map.GetOne(ToMsbFirst(offset)), false);
}
}
void MapTester::CheckDnodePage(NodePage &page, nid_t exp_nid) {
ASSERT_EQ(page.NidOfNode(), exp_nid);
ASSERT_EQ(page.GetBlockAddr(1U), 0U);
}
bool MapTester::IsCachedNat(NodeManager &node_manager, nid_t n) {
fs::SharedLock nat_lock(node_manager.nat_tree_lock_);
auto entry = node_manager.nat_cache_.find(n);
return entry != node_manager.nat_cache_.end();
}
void MapTester::RemoveTruncatedNode(NodeManager &node_manager, std::vector<nid_t> &nids) {
fs::SharedLock nat_lock(node_manager.nat_tree_lock_);
for (auto iter = nids.begin(); iter != nids.end();) {
auto cache_entry = node_manager.nat_cache_.find(*iter);
if (cache_entry != node_manager.nat_cache_.end()) {
if ((*cache_entry).GetBlockAddress() == kNullAddr) {
iter = nids.erase(iter);
} else {
++iter;
}
}
}
}
void MapTester::DoWriteNat(F2fs *fs, nid_t nid, block_t blkaddr, uint8_t version) {
NodeManager *nm_i = &fs->GetNodeManager();
std::unique_ptr<NatEntry> nat_entry = std::make_unique<NatEntry>();
auto cache_entry = nat_entry.get();
cache_entry->SetNid(nid);
ZX_ASSERT(!(*cache_entry).fbl::WAVLTreeContainable<std::unique_ptr<NatEntry>>::InContainer());
std::lock_guard nat_lock(nm_i->nat_tree_lock_);
nm_i->nat_cache_.insert(std::move(nat_entry));
ZX_ASSERT(!(*cache_entry).fbl::DoublyLinkedListable<NatEntry *>::InContainer());
nm_i->clean_nat_list_.push_back(cache_entry);
++nm_i->nat_entries_count_;
cache_entry->ClearCheckpointed();
cache_entry->SetBlockAddress(blkaddr);
cache_entry->SetVersion(version);
ZX_ASSERT((*cache_entry).fbl::DoublyLinkedListable<NatEntry *>::InContainer());
nm_i->clean_nat_list_.erase(*cache_entry);
ZX_ASSERT(!(*cache_entry).fbl::DoublyLinkedListable<NatEntry *>::InContainer());
nm_i->dirty_nat_list_.push_back(cache_entry);
}
void MapTester::DoWriteSit(F2fs *fs, CursegType type, uint32_t exp_segno,
block_t *new_blkaddr) TA_NO_THREAD_SAFETY_ANALYSIS {
SuperblockInfo &superblock_info = fs->GetSuperblockInfo();
SegmentManager &segment_manager = fs->GetSegmentManager();
if (!segment_manager.HasCursegSpace(type)) {
segment_manager.AllocateSegmentByDefault(type, false);
}
CursegInfo *curseg = segment_manager.CURSEG_I(type);
if (exp_segno != kNullSegNo) {
ASSERT_EQ(curseg->segno, exp_segno);
}
std::lock_guard curseg_lock(curseg->curseg_mutex);
*new_blkaddr = segment_manager.NextFreeBlkAddr(type);
uint32_t old_cursegno = curseg->segno;
segment_manager.RefreshNextBlkoff(curseg);
superblock_info.IncBlockCount(curseg->alloc_type);
segment_manager.RefreshSitEntry(kNullSegNo, *new_blkaddr);
segment_manager.LocateDirtySegment(old_cursegno);
}
void MapTester::RemoveAllNatEntries(NodeManager &manager) {
std::lock_guard nat_lock(manager.nat_tree_lock_);
for (auto &nat_entry : manager.nat_cache_) {
ZX_ASSERT((nat_entry).fbl::DoublyLinkedListable<NatEntry *>::InContainer());
manager.clean_nat_list_.erase(nat_entry);
ZX_ASSERT((nat_entry).fbl::WAVLTreeContainable<std::unique_ptr<NatEntry>>::InContainer());
--manager.nat_entries_count_;
}
manager.nat_cache_.clear();
}
nid_t MapTester::ScanFreeNidList(NodeManager &manager) {
std::lock_guard free_nid_lock(manager.free_nid_tree_lock_);
return manager.free_nid_tree_.empty() ? 0 : *manager.free_nid_tree_.rbegin();
}
void MapTester::GetCachedNatEntryBlockAddress(NodeManager &manager, nid_t nid, block_t &out) {
fs::SharedLock nat_lock(manager.nat_tree_lock_);
auto entry = manager.nat_cache_.find(nid);
ASSERT_TRUE(entry != manager.nat_cache_.end());
ASSERT_EQ(entry->GetNodeInfo().nid, nid);
out = entry->GetBlockAddress();
}
void MapTester::SetCachedNatEntryBlockAddress(NodeManager &manager, nid_t nid, block_t address) {
std::lock_guard nat_lock(manager.nat_tree_lock_);
auto entry = manager.nat_cache_.find(nid);
ASSERT_TRUE(entry != manager.nat_cache_.end());
ASSERT_EQ(entry->GetNodeInfo().nid, nid);
entry->SetBlockAddress(address);
}
void MapTester::SetCachedNatEntryCheckpointed(NodeManager &manager, nid_t nid) {
std::lock_guard nat_lock(manager.nat_tree_lock_);
auto entry = manager.nat_cache_.find(nid);
ASSERT_TRUE(entry != manager.nat_cache_.end());
ASSERT_EQ(entry->GetNodeInfo().nid, nid);
entry->SetCheckpointed();
ASSERT_TRUE(entry->IsCheckpointed());
}
zx_status_t MkfsTester::InitAndGetDeviceInfo(MkfsWorker &mkfs) {
mkfs.InitGlobalParameters();
return mkfs.GetDeviceInfo();
}
zx::result<std::unique_ptr<BcacheMapper>> MkfsTester::FormatDevice(MkfsWorker &mkfs) {
if (zx_status_t ret = mkfs.FormatDevice(); ret != ZX_OK)
return zx::error(ret);
return zx::ok(std::move(mkfs.bc_));
}
zx_status_t GcTester::DoGarbageCollect(SegmentManager &manager, uint32_t segno, GcType gc_type) {
std::lock_guard lock(f2fs::GetGlobalLock());
return manager.DoGarbageCollect(segno, gc_type);
}
zx_status_t GcTester::GcDataSegment(SegmentManager &manager, const SummaryBlock &sum_blk,
unsigned int segno, GcType gc_type) {
std::lock_guard lock(f2fs::GetGlobalLock());
return manager.GcDataSegment(sum_blk, segno, gc_type);
}
void DeviceTester::SetHook(F2fs *fs, DeviceTester::Hook hook) {
fs->GetBc().ForEachBcache([](Bcache *f2fs_device) {
static_cast<block_client::FakeBlockDevice *>(f2fs_device->GetDevice())->Pause();
});
fs->GetBc().ForEachBcache([hook](Bcache *f2fs_device) {
static_cast<block_client::FakeBlockDevice *>(f2fs_device->GetDevice())->set_hook(hook);
});
fs->GetBc().ForEachBcache([](Bcache *f2fs_device) {
static_cast<block_client::FakeBlockDevice *>(f2fs_device->GetDevice())->Resume();
});
}
} // namespace f2fs