blob: 336e8db152ac958d3246f823b010a13957e0f489 [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 <unordered_set>
#include <block-client/cpp/fake-device.h>
#include <gtest/gtest.h>
#include "third_party/f2fs/f2fs.h"
#include "unit_lib.h"
namespace f2fs {
namespace {
TEST(VnodeCache, Basic) {
std::unique_ptr<Bcache> bc;
unittest_lib::MkfsOnFakeDev(&bc);
std::unique_ptr<F2fs> fs;
MountOptions options{};
ASSERT_EQ(options.SetValue(options.GetNameView(kOptInlineDentry), 0), ZX_OK);
unittest_lib::MountWithOptions(options, &bc, &fs);
fbl::RefPtr<VnodeF2fs> root;
unittest_lib::CreateRoot(fs.get(), &root);
fbl::RefPtr<Dir> root_dir = fbl::RefPtr<Dir>::Downcast(std::move(root));
fbl::RefPtr<fs::Vnode> test_dir;
ASSERT_EQ(root_dir->Create("test", S_IFDIR, &test_dir), ZX_OK);
fbl::RefPtr<VnodeF2fs> test_dir_vn = fbl::RefPtr<VnodeF2fs>::Downcast(std::move(test_dir));
Dir *test_dir_ptr = static_cast<Dir *>(test_dir_vn.get());
std::unordered_set<std::string> child_set = {"a", "b", "c", "d", "e"};
std::vector<ino_t> child_ino_set(0);
std::vector<std::string> deleted_child_set(0);
// create a, b, c, d, e in test
for (auto iter : child_set) {
unittest_lib::CreateChild(test_dir_ptr, S_IFDIR, iter);
}
// check if {a, b, c, d, e} vnodes are in both containers.
for (auto iter : child_set) {
fbl::RefPtr<fs::Vnode> vn;
unittest_lib::Lookup(test_dir_ptr, iter, &vn);
ASSERT_TRUE(vn);
VnodeF2fs *raw_vnode = reinterpret_cast<VnodeF2fs *>(vn.get());
ASSERT_TRUE(raw_vnode->IsDirty());
ASSERT_EQ((*raw_vnode).fbl::DoublyLinkedListable<fbl::RefPtr<VnodeF2fs>>::InContainer(), true);
ASSERT_EQ((*raw_vnode).fbl::WAVLTreeContainable<VnodeF2fs *>::InContainer(), true);
child_ino_set.push_back(raw_vnode->GetKey());
vn->Close();
vn.reset();
}
ASSERT_EQ(test_dir_vn->GetSize(), kPageCacheSize);
// flush dirty vnodes.
fs->WriteCheckpoint(false, false);
// check if dirty vnodes are removed from dirty_list_
ASSERT_TRUE(fs->GetVCache().IsDirtyListEmpty());
for (auto iter : child_set) {
fbl::RefPtr<fs::Vnode> vn;
unittest_lib::Lookup(test_dir_ptr, iter, &vn);
ASSERT_TRUE(vn);
VnodeF2fs *raw_vnode = reinterpret_cast<VnodeF2fs *>(vn.get());
ASSERT_FALSE(raw_vnode->IsDirty());
ASSERT_FALSE((*raw_vnode).fbl::DoublyLinkedListable<fbl::RefPtr<VnodeF2fs>>::InContainer());
ASSERT_EQ((*raw_vnode).fbl::WAVLTreeContainable<VnodeF2fs *>::InContainer(), true);
vn->Close();
}
// remove "b" and "d".
unittest_lib::DeleteChild(test_dir_ptr, "b");
deleted_child_set.push_back("b");
unittest_lib::DeleteChild(test_dir_ptr, "d");
deleted_child_set.push_back("d");
// free nids for b and d.
fs->WriteCheckpoint(false, false);
// check if nodemgr and vnode cache remove b and d.
int i = 0;
for (auto iter : child_set) {
fbl::RefPtr<fs::Vnode> vn;
auto child = find(deleted_child_set.begin(), deleted_child_set.end(), iter);
unittest_lib::Lookup(test_dir_ptr, iter, &vn);
if (child != deleted_child_set.end()) {
ASSERT_FALSE(vn);
ino_t ino = child_ino_set.at(i);
fbl::RefPtr<VnodeF2fs> vn2;
ASSERT_EQ(fs->GetVCache().Lookup(ino, &vn2), ZX_ERR_NOT_FOUND);
NodeInfo ni;
fs->Nodemgr().GetNodeInfo(ino, &ni);
ASSERT_FALSE(ni.blk_addr);
} else {
ASSERT_TRUE(vn);
VnodeF2fs *raw_vnode = reinterpret_cast<VnodeF2fs *>(vn.get());
ASSERT_FALSE(raw_vnode->IsDirty());
ASSERT_FALSE((*raw_vnode).fbl::DoublyLinkedListable<fbl::RefPtr<VnodeF2fs>>::InContainer());
ASSERT_EQ((*raw_vnode).fbl::WAVLTreeContainable<VnodeF2fs *>::InContainer(), true);
vn->Close();
ino_t ino = child_ino_set.at(i);
fbl::RefPtr<VnodeF2fs> vn2;
ASSERT_EQ(fs->GetVCache().Lookup(ino, &vn2), ZX_OK);
NodeInfo ni;
fs->Nodemgr().GetNodeInfo(ino, &ni);
ASSERT_TRUE(ni.blk_addr);
}
i++;
}
ASSERT_EQ(test_dir_vn->Close(), ZX_OK);
test_dir_vn = nullptr;
ASSERT_EQ(root_dir->Close(), ZX_OK);
root_dir = nullptr;
unittest_lib::Unmount(std::move(fs), &bc);
}
} // namespace
} // namespace f2fs