blob: f6b90ac757de2fce61437088a25b4edfed03e53c [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 <gtest/gtest.h>
#include <safemath/checked_math.h>
#include "src/storage/f2fs/f2fs.h"
#include "src/storage/lib/block_client/cpp/fake_block_device.h"
#include "unit_lib.h"
namespace f2fs {
namespace {
using DirEntryCacheTest = F2fsFakeDevTestFixture;
TEST_F(DirEntryCacheTest, Basic) {
std::unordered_set<std::string> child_set = {"alpha", "bravo", "charlie", "delta", "echo"};
// Create children
for (auto &child : child_set) {
FileTester::CreateChild(root_dir_.get(), S_IFDIR, child.c_str());
}
// Check if all children exist on the cache
for (auto &child : child_set) {
ASSERT_TRUE(root_dir_->GetDirEntryCache().IsElementInCache(child));
}
// Remove "bravo"
FileTester::DeleteChild(root_dir_.get(), "bravo");
child_set.erase("bravo");
// Check if "bravo" is not exist on the cache
ASSERT_FALSE(root_dir_->GetDirEntryCache().IsElementInCache("bravo"));
// Check if all other children still exist on the cache
for (auto &child : child_set) {
ASSERT_TRUE(root_dir_->GetDirEntryCache().IsElementInCache(child));
}
}
TEST_F(DirEntryCacheTest, SubDirectory) {
// Create "alpha"
FileTester::CreateChild(root_dir_.get(), S_IFDIR, "alpha");
fbl::RefPtr<fs::Vnode> child_dir_vn;
FileTester::Lookup(root_dir_.get(), "alpha", &child_dir_vn);
ASSERT_TRUE(root_dir_->GetDirEntryCache().IsElementInCache("alpha"));
// Create "alpha/bravo"
fbl::RefPtr<Dir> child_dir = fbl::RefPtr<Dir>::Downcast(std::move(child_dir_vn));
FileTester::CreateChild(child_dir.get(), S_IFDIR, "bravo");
ASSERT_TRUE(child_dir->GetDirEntryCache().IsElementInCache("bravo"));
// Delete "alpha/bravo"
FileTester::DeleteChild(child_dir.get(), "bravo");
ASSERT_FALSE(child_dir->GetDirEntryCache().IsElementInCache("bravo"));
// Delete "alpha"
ASSERT_EQ(child_dir->Close(), ZX_OK);
FileTester::DeleteChild(root_dir_.get(), "alpha");
ASSERT_FALSE(root_dir_->GetDirEntryCache().IsElementInCache("alpha"));
// Create "alpha", and check if "alpha/bravo" is not exist
FileTester::CreateChild(root_dir_.get(), S_IFDIR, "alpha");
ASSERT_TRUE(root_dir_->GetDirEntryCache().IsElementInCache("alpha"));
FileTester::Lookup(root_dir_.get(), "alpha", &child_dir_vn);
child_dir = fbl::RefPtr<Dir>::Downcast(std::move(child_dir_vn));
ASSERT_FALSE(child_dir->GetDirEntryCache().IsElementInCache("bravo"));
// Create "alpha/bravo", move "alpha" to "charlie", and check if "charlie/bravo" is exist
FileTester::CreateChild(child_dir.get(), S_IFDIR, "bravo");
ASSERT_EQ(child_dir->Close(), ZX_OK);
ASSERT_EQ(root_dir_->Rename(root_dir_, "alpha", "charlie", true, true), ZX_OK);
FileTester::Lookup(root_dir_.get(), "charlie", &child_dir_vn);
child_dir = fbl::RefPtr<Dir>::Downcast(std::move(child_dir_vn));
ASSERT_TRUE(child_dir->GetDirEntryCache().IsElementInCache("bravo"));
ASSERT_EQ(child_dir->Close(), ZX_OK);
}
TEST_F(DirEntryCacheTest, CacheDataValidation) {
const uint32_t nr_child = kNrDentryInBlock * 4;
// Create children
for (uint32_t i = 0; i < nr_child; ++i) {
std::string child = std::to_string(i);
FileTester::CreateChild(root_dir_.get(), S_IFDIR, child);
ASSERT_TRUE(root_dir_->GetDirEntryCache().IsElementInCache(child));
}
// Check whether cached data is valid
auto &map = root_dir_->GetDirEntryCache().GetMap();
for (auto &cached : map) {
std::string child_name_from_key = cached.first;
auto element = cached.second;
ASSERT_EQ(element->GetName(), child_name_from_key);
// To validate cached parent ino, read a page for cached index
auto page_or = root_dir_->FindDataPage(element->GetInfo().page_index);
ASSERT_TRUE(page_or.is_ok());
DentryBlock *dentry_block = page_or->GetAddress<DentryBlock>();
PageBitmap dentry_bitmap(dentry_block->dentry_bitmap, kNrDentryInBlock);
size_t bit_pos = 0;
while ((bit_pos = dentry_bitmap.FindNextBit(bit_pos)) < kNrDentryInBlock) {
DirEntry *de = &dentry_block->dentry[bit_pos];
uint32_t slots = (LeToCpu(de->name_len) + kNameLen - 1) / kNameLen;
// If child name is found in the block, check if contents are valid
if (std::string(reinterpret_cast<char *>(dentry_block->filename[bit_pos]),
element->GetName().length()) == element->GetName()) {
// Validate cached dir entry
// Make a copy for alignment
DirEntry de_copied = *de;
ASSERT_EQ(element->GetInfo().ino, de_copied.ino);
break;
}
bit_pos += slots;
}
// If not found, |bit_pos| exceeds the bitmap length, |kNrDentryInBlock|
ASSERT_LT(bit_pos, safemath::checked_cast<uint32_t>(kNrDentryInBlock));
}
}
} // namespace
} // namespace f2fs