[f2fs] Add unit tests for RecoverOrphanInodes
Test: f2fs-unittest f2fs-fs-tests f2fs-slow-fs-tests
Change-Id: Iac26b1481353784880a9464f37d28606380382cb
Reviewed-on: https://fuchsia-review.googlesource.com/c/third_party/f2fs/+/568221
Reviewed-by: Brett Wilson <brettw@google.com>
diff --git a/checkpoint.cc b/checkpoint.cc
index 3290833..7c4cbc9 100644
--- a/checkpoint.cc
+++ b/checkpoint.cc
@@ -175,6 +175,7 @@
ZX_ASSERT(GetVCache().RemoveDirty(vnode) == ZX_OK);
}
}
+
void F2fs::AddOrphanInode(nid_t ino) {
SbInfo &sbi = GetSbInfo();
OrphanInodeEntry *new_entry = nullptr, *orphan = nullptr;
@@ -244,17 +245,16 @@
vnode->ClearNlink();
// truncate all the data and nodes in VnodeF2fs::Recycle()
- Iput(vnode.get());
+ // Iput(vnode.get());
vnode.reset();
}
-int F2fs::RecoverOrphanInodes() {
+zx_status_t F2fs::RecoverOrphanInodes() {
SbInfo &sbi = GetSbInfo();
block_t start_blk, orphan_blkaddr, i, j;
if (!(GetCheckpoint(&sbi)->ckpt_flags & kCpOrphanPresentFlag))
- return 0;
-
+ return ZX_OK;
sbi.por_doing = 1;
start_blk = StartCpAddr(&sbi) + 1;
orphan_blkaddr = StartSumAddr(&sbi) - 1;
@@ -273,7 +273,7 @@
/* clear Orphan Flag */
GetCheckpoint(&sbi)->ckpt_flags &= (~kCpOrphanPresentFlag);
sbi.por_doing = 0;
- return 0;
+ return ZX_OK;
}
void F2fs::WriteOrphanInodes(block_t start_blk) {
diff --git a/super.cc b/super.cc
index 448237c..0725c75 100644
--- a/super.cc
+++ b/super.cc
@@ -362,7 +362,7 @@
/* if there are nt orphan nodes free them */
err = ZX_ERR_INVALID_ARGS;
- if (!(sbi_->ckpt->ckpt_flags & kCpUmountFlag) && RecoverOrphanInodes())
+ if (RecoverOrphanInodes() != ZX_OK)
goto free_node_inode;
/* read root inode and dentry */
diff --git a/test/BUILD.gn b/test/BUILD.gn
index 77bb5ad..940db47 100644
--- a/test/BUILD.gn
+++ b/test/BUILD.gn
@@ -27,6 +27,7 @@
"unit/mkfs.cc",
"unit/mount.cc",
"unit/node.cc",
+ "unit/orphan.cc",
"unit/segment.cc",
"unit/unit_lib.cc",
"unit/vnode_cache.cc",
diff --git a/test/unit/orphan.cc b/test/unit/orphan.cc
new file mode 100644
index 0000000..aa3ad69
--- /dev/null
+++ b/test/unit/orphan.cc
@@ -0,0 +1,102 @@
+// 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 <lib/syslog/cpp/macros.h>
+
+#include <cstddef>
+#include <vector>
+
+#include <block-client/cpp/fake-device.h>
+#include <gtest/gtest.h>
+
+#include "third_party/f2fs/f2fs.h"
+#include "unit_lib.h"
+
+namespace f2fs {
+namespace {
+
+constexpr uint32_t kOrphanCnt = 10;
+
+TEST(OrphanInode, RecoverOrphanInode) {
+ std::unique_ptr<Bcache> bc;
+ unittest_lib::MkfsOnFakeDev(&bc);
+
+ std::unique_ptr<F2fs> fs;
+ MountOptions options{};
+ unittest_lib::MountWithOptions(options, &bc, &fs);
+
+ SbInfo &sbi = fs->GetSbInfo();
+
+ fbl::RefPtr<VnodeF2fs> root;
+ unittest_lib::CreateRoot(fs.get(), &root);
+ fbl::RefPtr<Dir> root_dir = fbl::RefPtr<Dir>::Downcast(std::move(root));
+
+ ASSERT_FALSE(GetCheckpoint(&sbi)->ckpt_flags & kCpOrphanPresentFlag);
+
+ // 1. Create files
+ std::vector<fbl::RefPtr<VnodeF2fs>> vnodes;
+ std::vector<uint32_t> inos;
+
+ ASSERT_EQ(fs->ValidInodeCount(), static_cast<uint64_t>(1));
+ ASSERT_EQ(fs->ValidNodeCount(), static_cast<uint64_t>(1));
+ ASSERT_EQ(fs->ValidUserBlocks(), static_cast<uint64_t>(2));
+
+ unittest_lib::CreateChildren(fs.get(), vnodes, inos, root_dir, "orphan_", kOrphanCnt);
+ ASSERT_EQ(vnodes.size(), kOrphanCnt);
+ ASSERT_EQ(inos.size(), kOrphanCnt);
+
+ ASSERT_EQ(fs->ValidInodeCount(), static_cast<uint64_t>(kOrphanCnt + 1));
+ ASSERT_EQ(fs->ValidNodeCount(), static_cast<uint64_t>(kOrphanCnt + 1));
+ ASSERT_EQ(fs->ValidUserBlocks(), static_cast<uint64_t>(kOrphanCnt + 2));
+
+ for (const auto &iter : vnodes) {
+ ASSERT_EQ(iter->GetNlink(), static_cast<uint32_t>(1));
+ }
+
+ // 2. Make orphan inodes
+ ASSERT_EQ(fs->GetSbInfo().n_orphans, static_cast<uint64_t>(0));
+ unittest_lib::DeleteChildren(vnodes, root_dir, kOrphanCnt);
+ ASSERT_EQ(fs->GetSbInfo().n_orphans, kOrphanCnt);
+
+ for (const auto &iter : vnodes) {
+ ASSERT_EQ(iter->GetNlink(), (uint32_t)0);
+ }
+
+ fs->WriteCheckpoint(false, true);
+
+ // 3. Sudden power off
+ for (const auto &iter : vnodes) {
+ iter->Close();
+ }
+
+ vnodes.clear();
+ vnodes.shrink_to_fit();
+ ASSERT_EQ(root_dir->Close(), ZX_OK);
+ root_dir.reset();
+
+ unittest_lib::SuddenPowerOff(std::move(fs), &bc);
+
+ // 4. Remount and recover orphan inodes
+ unittest_lib::MountWithOptions(options, &bc, &fs);
+
+ ASSERT_EQ(fs->GetSbInfo().n_orphans, static_cast<uint64_t>(0));
+
+ ASSERT_EQ(fs->ValidInodeCount(), static_cast<uint64_t>(1));
+ ASSERT_EQ(fs->ValidNodeCount(), static_cast<uint64_t>(1));
+ ASSERT_EQ(fs->ValidUserBlocks(), static_cast<uint64_t>(2));
+
+ // Check Orphan nids has been freed
+ for (const auto &iter : inos) {
+ NodeInfo ni;
+ fs->Nodemgr().GetNodeInfo(iter, &ni);
+ ASSERT_EQ(ni.blk_addr, kNullAddr);
+ }
+
+ unittest_lib::Unmount(std::move(fs), &bc);
+}
+
+} // namespace
+} // namespace f2fs
diff --git a/test/unit/unit_lib.cc b/test/unit/unit_lib.cc
index 0e6004e..504f628 100644
--- a/test/unit/unit_lib.cc
+++ b/test/unit/unit_lib.cc
@@ -36,6 +36,20 @@
fs.reset();
}
+void SuddenPowerOff(std::unique_ptr<F2fs> fs, std::unique_ptr<Bcache> *bc) {
+ SbInfo &sbi = fs->GetSbInfo();
+
+ fs->GetVCache().Reset();
+
+ // destroy f2fs internal modules
+ fs->Nodemgr().DestroyNodeManager();
+ fs->Segmgr().DestroySegmentManager();
+
+ delete GetCheckpoint(&sbi);
+ fs->ResetBc(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),
@@ -79,6 +93,16 @@
}
}
+void DeleteChildren(std::vector<fbl::RefPtr<VnodeF2fs>> &vnodes, fbl::RefPtr<Dir> &parent,
+ uint32_t inode_cnt) {
+ uint32_t deleted_file_cnt = 0;
+ for (const auto &iter : vnodes) {
+ ASSERT_EQ(parent->Unlink(iter->GetName(), false), ZX_OK);
+ deleted_file_cnt++;
+ }
+ ASSERT_EQ(deleted_file_cnt, inode_cnt);
+}
+
void VnodeWithoutParent(F2fs *fs, uint32_t mode, fbl::RefPtr<VnodeF2fs> &vnode) {
nid_t inode_nid;
ASSERT_TRUE(fs->Nodemgr().AllocNid(&inode_nid));
diff --git a/test/unit/unit_lib.h b/test/unit/unit_lib.h
index 0ba44a0..ef995f9 100644
--- a/test/unit/unit_lib.h
+++ b/test/unit/unit_lib.h
@@ -17,6 +17,7 @@
void MountWithOptions(MountOptions &options, std::unique_ptr<Bcache> *bc,
std::unique_ptr<F2fs> *fs);
void Unmount(std::unique_ptr<F2fs> fs, std::unique_ptr<Bcache> *bc);
+void SuddenPowerOff(std::unique_ptr<F2fs> fs, std::unique_ptr<Bcache> *bc);
void CreateRoot(F2fs *fs, fbl::RefPtr<VnodeF2fs> *out);
void Lookup(VnodeF2fs *parent, std::string_view name, fbl::RefPtr<fs::Vnode> *out);
@@ -26,6 +27,8 @@
void CreateChildren(F2fs *fs, std::vector<fbl::RefPtr<VnodeF2fs>> &vnodes,
std::vector<uint32_t> &inos, fbl::RefPtr<Dir> &parent, std::string name,
uint32_t inode_cnt);
+void DeleteChildren(std::vector<fbl::RefPtr<VnodeF2fs>> &vnodes, fbl::RefPtr<Dir> &parent,
+ uint32_t inode_cnt);
void VnodeWithoutParent(F2fs *fs, uint32_t mode, fbl::RefPtr<VnodeF2fs> &vnode);