blob: b95b7dae7302b22bb85f442e748e14f0a1a14886 [file] [log] [blame]
// Copyright 2022 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 "src/storage/f2fs/test/compatibility/compatibility.h"
namespace f2fs {
namespace {
using InlineCompatibilityTest = F2fsGuestTest;
TEST_F(InlineCompatibilityTest, InlineDentryLinuxToFuchsia) {
// Inline dentry on Linux F2FS is available from v3.17
GetEnclosedGuest().GetLinuxOperator().CheckLinuxVersion(3, 17);
const std::string inline_dir_path = "/inline";
const std::string noninline_dir_path = "/noninline";
const uint32_t max_inline_dentry = GetEnclosedGuest().GetFuchsiaOperator().MaxInlineDentrySlots();
const uint32_t nr_child_of_inline_dir = max_inline_dentry / 2;
const uint32_t nr_child_of_noninline_dir = max_inline_dentry * 2;
// Create child on Linux
{
GetEnclosedGuest().GetLinuxOperator().Mkfs();
GetEnclosedGuest().GetLinuxOperator().Mount();
auto umount = fit::defer([&] { GetEnclosedGuest().GetLinuxOperator().Umount(); });
GetEnclosedGuest().GetLinuxOperator().Mkdir(std::string(kLinuxPathPrefix) + inline_dir_path,
0755);
for (uint32_t i = 0; i < nr_child_of_inline_dir; ++i) {
std::string child_name(std::string(kLinuxPathPrefix) + inline_dir_path);
child_name.append("/").append(std::to_string(i));
GetEnclosedGuest().GetLinuxOperator().Mkdir(child_name, 0755);
}
GetEnclosedGuest().GetLinuxOperator().Mkdir(std::string(kLinuxPathPrefix) + noninline_dir_path,
0755);
for (uint32_t i = 0; i < nr_child_of_noninline_dir; ++i) {
std::string child_name(std::string(kLinuxPathPrefix) + noninline_dir_path);
child_name.append("/").append(std::to_string(i));
GetEnclosedGuest().GetLinuxOperator().Mkdir(child_name, 0755);
}
}
{
GetEnclosedGuest().GetFuchsiaOperator().Fsck();
GetEnclosedGuest().GetFuchsiaOperator().Mount();
auto umount = fit::defer([&] { GetEnclosedGuest().GetFuchsiaOperator().Umount(); });
// Check if inline directory is still inline on Fuchsia
auto inline_dir = GetEnclosedGuest().GetFuchsiaOperator().Open(inline_dir_path, O_RDWR, 0644);
ASSERT_TRUE(inline_dir->IsValid());
VnodeF2fs *raw_vn_ptr = static_cast<FuchsiaTestFile *>(inline_dir.get())->GetRawVnodePtr();
ASSERT_TRUE(raw_vn_ptr->TestFlag(InodeInfoFlag::kInlineDentry));
// Check if children of inline directory are accessible
for (uint32_t i = 0; i < nr_child_of_inline_dir; ++i) {
std::string child_name(inline_dir_path);
child_name.append("/").append(std::to_string(i));
auto child = GetEnclosedGuest().GetFuchsiaOperator().Open(child_name, O_RDWR, 0644);
ASSERT_TRUE(child->IsValid());
}
// Create one more child, and the directory should remain inline
std::string additional_child(inline_dir_path);
additional_child.append("/").append(std::to_string(nr_child_of_inline_dir));
GetEnclosedGuest().GetFuchsiaOperator().Mkdir(additional_child, 0755);
ASSERT_TRUE(raw_vn_ptr->TestFlag(InodeInfoFlag::kInlineDentry));
// Check if noninline directory is still noninline on Fuchsia
auto noninline_dir =
GetEnclosedGuest().GetFuchsiaOperator().Open(noninline_dir_path, O_RDWR, 0644);
ASSERT_TRUE(inline_dir->IsValid());
raw_vn_ptr = static_cast<FuchsiaTestFile *>(noninline_dir.get())->GetRawVnodePtr();
ASSERT_FALSE(raw_vn_ptr->TestFlag(InodeInfoFlag::kInlineDentry));
// Check if children of noninline directory are accessible
for (uint32_t i = 0; i < nr_child_of_noninline_dir; ++i) {
std::string child_name(noninline_dir_path);
child_name.append("/").append(std::to_string(i));
auto child = GetEnclosedGuest().GetFuchsiaOperator().Open(child_name, O_RDWR, 0644);
ASSERT_TRUE(child->IsValid());
}
}
// Check new child exists on Linux
{
GetEnclosedGuest().GetLinuxOperator().Fsck();
GetEnclosedGuest().GetLinuxOperator().Mount();
auto umount = fit::defer([&] { GetEnclosedGuest().GetLinuxOperator().Umount(); });
std::string child_name(std::string(kLinuxPathPrefix) + inline_dir_path);
child_name.append("/").append(std::to_string(nr_child_of_inline_dir));
auto new_child = GetEnclosedGuest().GetLinuxOperator().Open(child_name, O_RDWR, 0644);
ASSERT_TRUE(new_child->IsValid());
}
}
TEST_F(InlineCompatibilityTest, InlineDentryFuchsiaToLinux) {
// Inline dentry on Linux F2FS is available from v3.17
GetEnclosedGuest().GetLinuxOperator().CheckLinuxVersion(3, 17);
const std::string inline_dir_path = "/inline";
const std::string noninline_dir_path = "/noninline";
const uint32_t max_inline_dentry = GetEnclosedGuest().GetFuchsiaOperator().MaxInlineDentrySlots();
const uint32_t nr_child_of_inline_dir = max_inline_dentry / 2;
const uint32_t nr_child_of_noninline_dir = max_inline_dentry * 2;
// Create child on Fuchsia
{
GetEnclosedGuest().GetFuchsiaOperator().Mkfs();
MountOptions options;
ASSERT_EQ(options.SetValue(MountOption::kInlineDentry, 1), ZX_OK);
GetEnclosedGuest().GetFuchsiaOperator().Mount(options);
auto umount = fit::defer([&] { GetEnclosedGuest().GetFuchsiaOperator().Umount(); });
// Create inline directory
GetEnclosedGuest().GetFuchsiaOperator().Mkdir(inline_dir_path, 0755);
auto inline_dir = GetEnclosedGuest().GetFuchsiaOperator().Open(inline_dir_path, O_RDWR, 0644);
ASSERT_TRUE(inline_dir->IsValid());
VnodeF2fs *raw_inline_vn_ptr =
static_cast<FuchsiaTestFile *>(inline_dir.get())->GetRawVnodePtr();
ASSERT_TRUE(raw_inline_vn_ptr->TestFlag(InodeInfoFlag::kInlineDentry));
// Create children up to |nr_child_of_inline_dir|, and the directory should be inline
for (uint32_t i = 0; i < nr_child_of_inline_dir; ++i) {
std::string child_name(inline_dir_path);
child_name.append("/").append(std::to_string(i));
GetEnclosedGuest().GetFuchsiaOperator().Mkdir(child_name, 0755);
}
ASSERT_TRUE(raw_inline_vn_ptr->TestFlag(InodeInfoFlag::kInlineDentry));
// Create inline directory
GetEnclosedGuest().GetFuchsiaOperator().Mkdir(noninline_dir_path, 0755);
auto noninline_dir =
GetEnclosedGuest().GetFuchsiaOperator().Open(noninline_dir_path, O_RDWR, 0644);
ASSERT_TRUE(noninline_dir->IsValid());
auto *raw_noninline_vn_ptr =
static_cast<FuchsiaTestFile *>(noninline_dir.get())->GetRawVnodePtr();
ASSERT_TRUE(raw_noninline_vn_ptr->TestFlag(InodeInfoFlag::kInlineDentry));
// Create children up to |nr_child_of_noninline_dir|, and the directory should be non inline
for (uint32_t i = 0; i < nr_child_of_noninline_dir; ++i) {
std::string child_name(noninline_dir_path);
child_name.append("/").append(std::to_string(i));
GetEnclosedGuest().GetFuchsiaOperator().Mkdir(child_name, 0755);
}
ASSERT_FALSE(raw_noninline_vn_ptr->TestFlag(InodeInfoFlag::kInlineDentry));
}
// Check children exist on Linux
{
GetEnclosedGuest().GetLinuxOperator().Fsck();
GetEnclosedGuest().GetLinuxOperator().Mount();
auto umount = fit::defer([&] { GetEnclosedGuest().GetLinuxOperator().Umount(); });
for (uint32_t i = 0; i < nr_child_of_inline_dir; ++i) {
std::string child_name(std::string(kLinuxPathPrefix) + inline_dir_path);
child_name.append("/").append(std::to_string(i));
auto child = GetEnclosedGuest().GetLinuxOperator().Open(child_name, O_RDWR, 0644);
ASSERT_TRUE(child->IsValid());
}
for (uint32_t i = 0; i < nr_child_of_noninline_dir; ++i) {
std::string child_name(std::string(kLinuxPathPrefix) + noninline_dir_path);
child_name.append("/").append(std::to_string(i));
auto child = GetEnclosedGuest().GetLinuxOperator().Open(child_name, O_RDWR, 0644);
ASSERT_TRUE(child->IsValid());
}
}
}
TEST_F(InlineCompatibilityTest, InlineDataLinuxToFuchsia) {
// Inline data on Linux F2FS is available from v3.13
GetEnclosedGuest().GetLinuxOperator().CheckLinuxVersion(3, 13);
const std::string inline_file_name = "inline";
const uint32_t data_length = GetEnclosedGuest().GetFuchsiaOperator().MaxInlineDataLength() / 2;
uint32_t r_buf[kPageSize / sizeof(uint32_t)];
uint32_t w_buf[kPageSize / sizeof(uint32_t)];
for (uint32_t i = 0; i < kPageSize / sizeof(uint32_t); ++i) {
w_buf[i] = CpuToLe(i);
}
// Create and write inline file on Linux
{
GetEnclosedGuest().GetLinuxOperator().Mkfs();
GetEnclosedGuest().GetLinuxOperator().Mount();
auto umount = fit::defer([&] { GetEnclosedGuest().GetLinuxOperator().Umount(); });
auto test_file = GetEnclosedGuest().GetLinuxOperator().Open(
std::string(kLinuxPathPrefix) + inline_file_name, O_RDWR | O_CREAT, 0644);
ASSERT_TRUE(test_file->IsValid());
ASSERT_EQ(test_file->Write(w_buf, data_length), data_length);
}
// Verify on Fuchsia
{
GetEnclosedGuest().GetFuchsiaOperator().Fsck();
GetEnclosedGuest().GetFuchsiaOperator().Mount();
auto umount = fit::defer([&] { GetEnclosedGuest().GetFuchsiaOperator().Umount(); });
// Check if inline file is still inline on Fuchsia
auto test_file = GetEnclosedGuest().GetFuchsiaOperator().Open(inline_file_name, O_RDWR, 0644);
ASSERT_TRUE(test_file->IsValid());
VnodeF2fs *raw_vn_ptr = static_cast<FuchsiaTestFile *>(test_file.get())->GetRawVnodePtr();
ASSERT_TRUE(raw_vn_ptr->TestFlag(InodeInfoFlag::kInlineData));
// Read verify
ASSERT_EQ(test_file->Read(r_buf, data_length), data_length);
ASSERT_EQ(memcmp(r_buf, w_buf, data_length), 0);
}
}
TEST_F(InlineCompatibilityTest, DataExistFlagLinuxToFuchsia) {
// |DataExist| flag on Linux F2FS is available from v3.18
GetEnclosedGuest().GetLinuxOperator().CheckLinuxVersion(3, 18);
const std::string filenames[4] = {"alpha", "bravo", "charlie", "delta"};
const std::string test_string = "hello";
// Create and write inline files on Linux
{
GetEnclosedGuest().GetLinuxOperator().Mkfs();
GetEnclosedGuest().GetLinuxOperator().Mount();
auto umount = fit::defer([&] { GetEnclosedGuest().GetLinuxOperator().Umount(); });
// Create file
auto test_file = GetEnclosedGuest().GetLinuxOperator().Open(
std::string(kLinuxPathPrefix) + filenames[0], O_RDWR | O_CREAT, 0644);
ASSERT_TRUE(test_file->IsValid());
// Write some data
test_file = GetEnclosedGuest().GetLinuxOperator().Open(
std::string(kLinuxPathPrefix) + filenames[1], O_RDWR | O_CREAT, 0644);
ASSERT_TRUE(test_file->IsValid());
ASSERT_EQ(test_file->Write(test_string.data(), test_string.size()),
static_cast<ssize_t>(test_string.size()));
// Truncate to non-zero size
test_file = GetEnclosedGuest().GetLinuxOperator().Open(
std::string(kLinuxPathPrefix) + filenames[2], O_RDWR | O_CREAT, 0644);
ASSERT_TRUE(test_file->IsValid());
ASSERT_EQ(test_file->Write(test_string.data(), test_string.size()),
static_cast<ssize_t>(test_string.size()));
ASSERT_EQ(test_file->Ftruncate(test_string.size() / 2), 0);
// Truncate to zero size
test_file = GetEnclosedGuest().GetLinuxOperator().Open(
std::string(kLinuxPathPrefix) + filenames[3], O_RDWR | O_CREAT, 0644);
ASSERT_TRUE(test_file->IsValid());
ASSERT_EQ(test_file->Write(test_string.data(), test_string.size()),
static_cast<ssize_t>(test_string.size()));
ASSERT_EQ(test_file->Ftruncate(0), 0);
}
// Check if all files have correct flags on Fuchsia
{
GetEnclosedGuest().GetFuchsiaOperator().Fsck();
GetEnclosedGuest().GetFuchsiaOperator().Mount();
auto umount = fit::defer([&] { GetEnclosedGuest().GetFuchsiaOperator().Umount(); });
// Only created, kDataExist should be unset
auto test_file =
GetEnclosedGuest().GetFuchsiaOperator().Open(filenames[0], O_RDWR | O_CREAT, 0644);
ASSERT_TRUE(test_file->IsValid());
VnodeF2fs *raw_vn_ptr = static_cast<FuchsiaTestFile *>(test_file.get())->GetRawVnodePtr();
ASSERT_TRUE(raw_vn_ptr->TestFlag(InodeInfoFlag::kInlineData));
ASSERT_FALSE(raw_vn_ptr->TestFlag(InodeInfoFlag::kDataExist));
// Data written, kDataExist should be set
test_file = GetEnclosedGuest().GetFuchsiaOperator().Open(filenames[1], O_RDWR | O_CREAT, 0644);
ASSERT_TRUE(test_file->IsValid());
raw_vn_ptr = static_cast<FuchsiaTestFile *>(test_file.get())->GetRawVnodePtr();
ASSERT_TRUE(raw_vn_ptr->TestFlag(InodeInfoFlag::kInlineData));
ASSERT_TRUE(raw_vn_ptr->TestFlag(InodeInfoFlag::kDataExist));
// Truncated to non-zero size, kDataExist should be set
test_file = GetEnclosedGuest().GetFuchsiaOperator().Open(filenames[2], O_RDWR | O_CREAT, 0644);
ASSERT_TRUE(test_file->IsValid());
raw_vn_ptr = static_cast<FuchsiaTestFile *>(test_file.get())->GetRawVnodePtr();
ASSERT_TRUE(raw_vn_ptr->TestFlag(InodeInfoFlag::kInlineData));
ASSERT_TRUE(raw_vn_ptr->TestFlag(InodeInfoFlag::kDataExist));
// Truncated to zero size, kDataExist should be unset
test_file = GetEnclosedGuest().GetFuchsiaOperator().Open(filenames[3], O_RDWR | O_CREAT, 0644);
ASSERT_TRUE(test_file->IsValid());
raw_vn_ptr = static_cast<FuchsiaTestFile *>(test_file.get())->GetRawVnodePtr();
ASSERT_TRUE(raw_vn_ptr->TestFlag(InodeInfoFlag::kInlineData));
ASSERT_FALSE(raw_vn_ptr->TestFlag(InodeInfoFlag::kDataExist));
}
}
} // namespace
} // namespace f2fs