blob: 3ea5e3dcf91e73245b91a608e1445bf05168f063 [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 <block-client/cpp/fake-device.h>
#include <gtest/gtest.h>
#include "third_party/f2fs/f2fs.h"
namespace f2fs {
namespace unittest_lib {
using block_client::FakeBlockDevice;
void MkfsOnFakeDev(std::unique_ptr<Bcache> *bc, uint64_t blockCount, uint32_t blockSize) {
auto device = std::make_unique<FakeBlockDevice>(blockCount, blockSize);
bool readonly_device = false;
ASSERT_EQ(CreateBcache(std::move(device), &readonly_device, bc), ZX_OK);
MkfsWorker mkfs(bc->get());
ASSERT_EQ(mkfs.DoMkfs(), ZX_OK);
}
void MountWithOptions(MountOptions &options, std::unique_ptr<Bcache> *bc,
std::unique_ptr<F2fs> *fs) {
ASSERT_EQ(F2fs::Create(std::move(*bc), options, fs), ZX_OK);
}
void Unmount(std::unique_ptr<F2fs> fs, std::unique_ptr<Bcache> *bc) {
fs->PutSuper();
*bc = std::move(fs->bc_);
fs.reset();
}
void CreateRoot(F2fs *fs, fbl::RefPtr<VnodeF2fs> *out) {
ASSERT_EQ(VnodeF2fs::Vget(fs, fs->RawSb().root_ino, out), ZX_OK);
ASSERT_EQ((*out)->Open((*out)->ValidateOptions(fs::VnodeConnectionOptions()).value(), nullptr),
ZX_OK);
}
void 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(vn->ValidateOptions(fs::VnodeConnectionOptions()).value(), nullptr), ZX_OK);
*out = std::move(vn);
}
void CreateChild(Dir *vn, uint32_t mode, std::string_view name) {
fbl::RefPtr<fs::Vnode> tmp_child;
ASSERT_EQ(vn->Create(name, mode, &tmp_child), ZX_OK);
ASSERT_EQ(tmp_child->Close(), ZX_OK);
}
void DeleteChild(Dir *vn, std::string_view name) {
ASSERT_EQ(vn->Unlink(name, true), ZX_OK);
// TODO: After EvictInode available, check if nids of the child are correctly freed
}
void CheckInlineDir(VnodeF2fs *vn) {
ASSERT_NE(vn->IsInodeFlagSet(InodeInfoFlag::kInlineDentry), 0);
ASSERT_EQ(vn->GetSize(), kMaxInlineData);
}
void CheckNonInlineDir(VnodeF2fs *vn) {
ASSERT_EQ(vn->IsInodeFlagSet(InodeInfoFlag::kInlineDentry), 0);
ASSERT_GT(vn->GetSize(), kMaxInlineData);
}
void CheckChildrenFromReaddir(Dir *dir, std::unordered_set<std::string> childs) {
childs.insert(".");
fs::VdirCookie cookie;
uint8_t buf[kPageSize];
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 + kPageSize) {
auto entry = reinterpret_cast<const vdirent_t *>(buf_ptr);
size_t entry_size = entry->size + sizeof(vdirent_t);
auto iter = childs.begin();
for (; iter != childs.end(); iter++) {
if (memcmp(entry->name, (*iter).c_str(), (*iter).length()) == 0) {
break;
}
}
ASSERT_NE(iter, childs.end());
childs.erase(iter);
buf_ptr += entry_size;
len -= entry_size;
}
ASSERT_TRUE(childs.empty());
}
void CheckChildrenInBlock(Dir *vn, unsigned int bidx, std::unordered_set<std::string> childs) {
if (bidx == 0) {
childs.insert(".");
childs.insert("..");
}
Page *page = nullptr;
if (childs.empty()) {
ASSERT_EQ(vn->FindDataPage(bidx, &page), ZX_ERR_NOT_FOUND);
return;
}
ASSERT_EQ(vn->FindDataPage(bidx, &page), ZX_OK);
DentryBlock *dentry_blk = reinterpret_cast<DentryBlock *>(page);
uint64_t bit_pos = find_next_bit_le(&dentry_blk->dentry_bitmap, kNrDentryInBlock, 0);
while (bit_pos < kNrDentryInBlock) {
DirEntry *de = &dentry_blk->dentry[bit_pos];
uint64_t slots = (LeToCpu(de->name_len) + kNameLen - 1) / kNameLen;
auto iter = childs.begin();
for (; iter != childs.end(); iter++) {
if (memcmp(dentry_blk->filename[bit_pos], (*iter).c_str(), (*iter).length()) == 0) {
break;
}
}
ASSERT_NE(iter, childs.end());
childs.erase(iter);
bit_pos = find_next_bit_le(&dentry_blk->dentry_bitmap, kNrDentryInBlock, bit_pos + slots);
}
ASSERT_TRUE(childs.empty());
F2fsPutPage(page, 0);
}
std::string GetRandomName(unsigned int len) {
const char *char_list = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
unsigned int 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 AppendToFile(File *file, const void *data, size_t len) {
size_t end = 0;
size_t ret = 0;
ASSERT_EQ(file->Append(data, len, &end, &ret), ZX_OK);
ASSERT_EQ(ret, kPageSize);
}
void CheckNodeLevel(F2fs *fs, VnodeF2fs *vn, int level) {
Page *ipage = nullptr;
ASSERT_EQ(fs->Nodemgr().GetNodePage(vn->Ino(), &ipage), ZX_OK);
Inode *inode = &(static_cast<Node *>(PageAddress(ipage))->i);
int 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);
F2fsPutPage(ipage, 0);
}
void CheckNidsFree(F2fs *fs, std::unordered_set<nid_t> &nids) {
SbInfo &sbi = fs->GetSbInfo();
NmInfo *nm_i = GetNmInfo(&sbi);
fbl::AutoLock lock(&nm_i->free_nid_list_lock);
for (auto nid : nids) {
bool found = false;
list_node_t *iter;
list_for_every(&nm_i->free_nid_list, iter) {
FreeNid *fnid = containerof(iter, FreeNid, list);
if (fnid->nid == nid) {
found = true;
break;
}
}
ASSERT_TRUE(found);
}
}
void CheckNidsInuse(F2fs *fs, std::unordered_set<nid_t> &nids) {
SbInfo &sbi = fs->GetSbInfo();
NmInfo *nm_i = GetNmInfo(&sbi);
fbl::AutoLock lock(&nm_i->free_nid_list_lock);
for (auto nid : nids) {
bool found = false;
list_node_t *iter;
list_for_every(&nm_i->free_nid_list, iter) {
FreeNid *fnid = containerof(iter, FreeNid, list);
if (fnid->nid == nid) {
found = true;
break;
}
}
ASSERT_FALSE(found);
}
}
void CheckBlkaddrsFree(F2fs *fs, std::unordered_set<block_t> &blkaddrs) {
SbInfo &sbi = fs->GetSbInfo();
for (auto blkaddr : blkaddrs) {
SegEntry *se = fs->Segmgr().GetSegEntry(GetSegNo(&sbi, blkaddr));
uint32_t offset = GetSegOffFromSeg0(&sbi, blkaddr) & (sbi.blocks_per_seg - 1);
ASSERT_EQ(TestValidBitmap(offset, reinterpret_cast<char *>(se->ckpt_valid_map)), 0);
}
}
void CheckBlkaddrsInuse(F2fs *fs, std::unordered_set<block_t> &blkaddrs) {
SbInfo &sbi = fs->GetSbInfo();
for (auto blkaddr : blkaddrs) {
SegEntry *se = fs->Segmgr().GetSegEntry(GetSegNo(&sbi, blkaddr));
uint32_t offset = GetSegOffFromSeg0(&sbi, blkaddr) & (sbi.blocks_per_seg - 1);
ASSERT_NE(TestValidBitmap(offset, reinterpret_cast<char *>(se->ckpt_valid_map)), 0);
}
}
} // namespace unittest_lib
} // namespace f2fs