blob: f53f167ce0d16198297aee5d486e6fa4ce76efef [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 <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <block-client/cpp/fake-device.h>
#include <gtest/gtest.h>
#include "third_party/f2fs/f2fs.h"
namespace f2fs {
namespace {
using block_client::FakeBlockDevice;
constexpr uint32_t kMountVerifyTest = 0;
constexpr uint32_t kMountDisableExtTest = 1;
constexpr uint32_t kMountActiveLogsTest = 2;
void MountTestVerifyOptions(F2fs *fs, MountOptions &options) {
uint32_t value;
SbInfo &sbi = fs->GetSbInfo();
for (uint32_t i = 0; i < kOptMaxNum; i++) {
ASSERT_EQ(options.GetValue(i, &value), ZX_OK);
switch (i) {
case kOptActiveLogs:
ASSERT_EQ(static_cast<uint32_t>(sbi.active_logs), value);
break;
case kOptDiscard:
ASSERT_EQ((value == 0), (TestOpt(&sbi, kMountDiscard) == 0));
break;
case kOptBgGcOff:
ASSERT_EQ((value == 0), (TestOpt(&sbi, kMountBgGcOff) == 0));
break;
case kOptNoHeap:
ASSERT_EQ((value == 0), (TestOpt(&sbi, kMountNoheap) == 0));
break;
case kOptDisableExtIdentify:
ASSERT_EQ((value == 0), (TestOpt(&sbi, kMountDisableExtIdentify) == 0));
break;
case kOptNoUserXAttr:
ASSERT_EQ((value == 0), (TestOpt(&sbi, kMountNoXAttr) == 0));
break;
case kOptNoAcl:
ASSERT_EQ((value == 0), (TestOpt(&sbi, kMountNoAcl) == 0));
break;
case kOptDisableRollForward:
ASSERT_EQ((value == 0), (TestOpt(&sbi, kMountDisableRollForward) == 0));
break;
};
}
ASSERT_EQ(options.GetValue(kOptMaxNum, &value), ZX_ERR_INVALID_ARGS);
}
void MountTestDisableExt(F2fs *fs, uint32_t expectation) {
fbl::RefPtr<VnodeF2fs> data_root;
bool result = (expectation > 0 ? true : false);
ASSERT_EQ(VnodeF2fs::Vget(fs, fs->RawSb().root_ino, &data_root), ZX_OK);
Dir *root_dir = static_cast<Dir *>(data_root.get());
for (const char *ext_item : kMediaExtList) {
std::string name = "test.";
name += ext_item;
fbl::RefPtr<fs::Vnode> vnode;
// Use S_IFDIR to create regular file
ASSERT_EQ(root_dir->Create(name, S_IFREG, &vnode), ZX_OK);
File *file = static_cast<File *>(vnode.get());
ASSERT_EQ(NodeMgr::IsColdFile(file), result);
vnode->Close();
}
}
void TestSegmentType(F2fs *fs, Dir *root_dir, const std::string_view name, bool is_dir,
std::vector<CursegType> &out) {
fbl::RefPtr<fs::Vnode> vnode;
uint64_t flag = (is_dir ? S_IFDIR : S_IFREG);
nid_t nid = 100;
uint32_t inode_ofs = 0;
uint32_t indirect_node_ofs = 3;
ASSERT_EQ(root_dir->Create(name, flag, &vnode), ZX_OK);
VnodeF2fs *vn = static_cast<VnodeF2fs *>(vnode.get());
// data block test
Page *page = GrabCachePage(vn, vn->Ino(), 0);
CursegType type = fs->Segmgr().GetSegmentType(page, PageType::kData);
out.push_back(type);
F2fsPutPage(page, 1);
// Dnode block test
page = GrabCachePage(nullptr, NodeIno(&fs->GetSbInfo()), vn->Ino());
fs->Nodemgr().FillNodeFooter(page, page->index, vn->Ino(), inode_ofs, true);
fs->Nodemgr().SetColdNode(vn, page);
type = fs->Segmgr().GetSegmentType(page, PageType::kNode);
out.push_back(type);
F2fsPutPage(page, 1);
// indirect node block test
page = GrabCachePage(nullptr, NodeIno(&fs->GetSbInfo()), nid);
fs->Nodemgr().FillNodeFooter(page, page->index, vn->Ino(), indirect_node_ofs, true);
fs->Nodemgr().SetColdNode(vn, page);
type = fs->Segmgr().GetSegmentType(page, PageType::kNode);
out.push_back(type);
F2fsPutPage(page, 1);
vnode->Close();
}
void MountTestActiveLogs(F2fs *fs, MountOptions options) {
fbl::RefPtr<VnodeF2fs> data_root;
ASSERT_EQ(VnodeF2fs::Vget(fs, fs->RawSb().root_ino, &data_root), ZX_OK);
Dir *root_dir = static_cast<Dir *>(data_root.get());
const char *filenames[] = {"dir", "warm.exe", "cold.mp4"};
std::vector<CursegType> results(3, CursegType::kNoCheckType);
uint32_t num_logs = 0;
ASSERT_EQ(options.GetValue(kOptActiveLogs, &num_logs), ZX_OK);
constexpr int dir_file = 0;
constexpr int warm_file = 1;
__UNUSED constexpr int cold_file = 2;
__UNUSED constexpr int data_block = 0;
constexpr int dnode_block = 1;
constexpr int indirect_node_block = 2;
for (int i = 0; i < 3; i++) {
results.clear();
TestSegmentType(fs, root_dir, filenames[i], (i == dir_file), results);
for (int j = 0; j < 3; j++) {
CursegType type = results[j];
if (j == indirect_node_block) {
if (num_logs > 2)
ASSERT_EQ(type, CursegType::kCursegColdNode);
else
ASSERT_EQ(type, CursegType::kCursegHotNode);
} else if (j == dnode_block) {
if (i == dir_file || num_logs == 2) {
ASSERT_EQ(type, CursegType::kCursegHotNode);
} else if (num_logs == 6) {
ASSERT_EQ(type, CursegType::kCursegWarmNode);
} else
ASSERT_EQ(type, CursegType::kCursegColdNode);
} else { // data block case
if (i == dir_file || num_logs == 2) {
ASSERT_EQ(type, CursegType::kCursegHotData);
} else {
if (i == warm_file && num_logs == 6)
ASSERT_EQ(type, CursegType::kCursegWarmData);
else
ASSERT_EQ(type, CursegType::kCursegColdData);
}
}
}
}
}
void MountTestMain(MountOptions &options, uint32_t test, uint32_t priv) {
std::unique_ptr<f2fs::Bcache> bc;
bool readonly_device = false;
auto device = std::make_unique<FakeBlockDevice>(
FakeBlockDevice::Config{.block_count = kMinVolumeSize / kDefaultSectorSize,
.block_size = kDefaultSectorSize,
.supports_trim = false});
ASSERT_TRUE(device);
ASSERT_EQ(CreateBcache(std::move(device), &readonly_device, &bc), ZX_OK);
MkfsWorker mkfs(bc.get());
ASSERT_EQ(mkfs.DoMkfs(), ZX_OK);
std::unique_ptr<F2fs> fs;
async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread);
// Enclose the channels in a scope to ensure they are closed before we shut down.
{
zx::channel mount_channel, remote_mount_channel;
ASSERT_EQ(zx::channel::create(0, &mount_channel, &remote_mount_channel), ZX_OK);
auto fs_or = f2fs::CreateFsAndRoot(
options, loop.dispatcher(), std::move(bc), std::move(mount_channel), [] {},
ServeLayout::kExportDirectory);
ASSERT_EQ(fs_or.is_ok(), true);
fs = std::move(fs_or).value();
switch (test) {
case kMountVerifyTest:
MountTestVerifyOptions(fs.get(), options);
break;
case kMountDisableExtTest:
MountTestDisableExt(fs.get(), priv);
break;
case kMountActiveLogsTest:
MountTestActiveLogs(fs.get(), options);
break;
default:
ASSERT_EQ(0, 1);
break;
};
}
fs->Shutdown([&loop](zx_status_t) { loop.Quit(); });
__UNUSED F2fs *relinquish = fs.release();
ASSERT_EQ(loop.Run(), ZX_ERR_CANCELED);
}
TEST(MountTest, Verify) {
MountOptions options;
MountTestMain(options, kMountVerifyTest, 0);
}
TEST(MountTest, DisableExtOptions) {
constexpr uint32_t ShouldNotBeCold = 0;
MountOptions options{};
ASSERT_EQ(options.SetValue(options.GetNameView(kOptDisableExtIdentify), 1), ZX_OK);
MountTestMain(options, kMountDisableExtTest, ShouldNotBeCold);
}
TEST(MountTest, EnableExtOptions) {
constexpr uint32_t ShouldBeCold = 1;
MountOptions options{};
ASSERT_EQ(options.SetValue(options.GetNameView(kOptDisableExtIdentify), 0), ZX_OK);
MountTestMain(options, kMountDisableExtTest, ShouldBeCold);
}
TEST(MountTest, ActiveLogsOptions) {
for (uint32_t i = 2; i <= 6; i += 2) {
MountOptions options{};
ASSERT_EQ(options.SetValue(options.GetNameView(kOptActiveLogs), i), ZX_OK);
MountTestMain(options, kMountActiveLogsTest, 0);
}
}
} // namespace
} // namespace f2fs