blob: 30b8ffe35c80f63b7d5b043997b6f57549632a9e [file] [log] [blame]
// Copyright 2019 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 <fcntl.h>
#include <fbl/unique_fd.h>
#include "src/storage/fs_test/fs_test_fixture.h"
#include "src/storage/minfs/format.h"
namespace minfs {
namespace {
using SuperblockTest = fs_test::FilesystemTest;
void ReadSuperblock(const std::string& device_path, uint64_t block_num, Superblock* info) {
fbl::unique_fd device(open(device_path.c_str(), O_RDWR));
ASSERT_TRUE(device);
ASSERT_EQ(kMinfsBlockSize,
pread(device.get(), info, kMinfsBlockSize, block_num * kMinfsBlockSize));
}
void WriteSuperblock(const std::string& device_path, const Superblock& info) {
fbl::unique_fd device(open(device_path.c_str(), O_RDWR));
ASSERT_TRUE(device);
ASSERT_EQ(kMinfsBlockSize,
pwrite(device.get(), &info, kMinfsBlockSize, kSuperblockStart * kMinfsBlockSize));
}
TEST_P(SuperblockTest, RepairCorruptSuperblock) {
ASSERT_EQ(fs().Unmount().status_value(), ZX_OK);
Superblock info;
ReadSuperblock(fs().DevicePath().value(), kSuperblockStart, &info);
ASSERT_EQ(kMinfsMagic0, info.magic0);
// Corrupt superblock, by erasing it completely from disk.
memset(&info, 0, sizeof(info));
WriteSuperblock(fs().DevicePath().value(), info);
// Running mount to repair the filesystem.
ASSERT_EQ(fs().Mount().status_value(), ZX_OK);
ASSERT_EQ(fs().Unmount().status_value(), ZX_OK);
// Confirm that the corrupted superblock is repaired by backup superblock.
ReadSuperblock(fs().DevicePath().value(), kSuperblockStart, &info);
ASSERT_EQ(kMinfsMagic0, info.magic0);
}
INSTANTIATE_TEST_SUITE_P(/*no prefix*/, SuperblockTest,
testing::ValuesIn(fs_test::AllTestFilesystems()),
testing::PrintToStringParamName());
enum class Comparison {
kSame,
kDifferent,
};
void CompareSuperblockAndBackupAllocCounts(const fs_test::TestFilesystem& fs, Comparison value) {
Superblock info;
ASSERT_NO_FATAL_FAILURE(ReadSuperblock(fs.DevicePath().value(), kSuperblockStart, &info));
ASSERT_EQ(kMinfsMagic0, info.magic0);
Superblock backup_info;
ASSERT_NO_FATAL_FAILURE(
ReadSuperblock(fs.DevicePath().value(), kNonFvmSuperblockBackup, &backup_info));
ASSERT_EQ(kMinfsMagic0, backup_info.magic0);
if (value == Comparison::kSame) {
EXPECT_EQ(info.alloc_block_count, backup_info.alloc_block_count);
EXPECT_EQ(info.alloc_inode_count, backup_info.alloc_inode_count);
} else {
EXPECT_NE(info.alloc_block_count, backup_info.alloc_block_count);
EXPECT_NE(info.alloc_inode_count, backup_info.alloc_inode_count);
}
}
void FsyncFilesystem(const std::string& mount_path) {
// Open the root directory to fsync the filesystem.
fbl::unique_fd fd_mount(open(mount_path.c_str(), O_RDONLY));
ASSERT_TRUE(fd_mount);
ASSERT_EQ(fsync(fd_mount.get()), 0);
}
class SuperblockTestNoFvm : public fs_test::BaseFilesystemTest {
public:
SuperblockTestNoFvm() : BaseFilesystemTest(fs_test::OptionsWithDescription("MinfsWithoutFvm")) {}
};
// Tests alloc_*_counts write frequency difference for backup superblock.
TEST_F(SuperblockTestNoFvm, AllocCountWriteFrequency) {
ASSERT_NO_FATAL_FAILURE(CompareSuperblockAndBackupAllocCounts(fs(), Comparison::kSame));
// Force allocating inodes as well as data blocks.
ASSERT_EQ(mkdir(GetPath("test_dir").c_str(), 0755), 0);
fbl::unique_fd fd_file(open(GetPath("test_dir/file").c_str(), O_CREAT | O_RDWR, 0666));
ASSERT_TRUE(fd_file);
// Write something to the file.
char data[kMinfsBlockSize];
memset(data, 0xb0b, sizeof(data));
ASSERT_EQ(kMinfsBlockSize, write(fd_file.get(), data, kMinfsBlockSize));
ASSERT_EQ(0, fsync(fd_file.get()));
fd_file.reset();
FsyncFilesystem(fs().mount_path());
// Check that superblock and backup alloc counts are now different.
ASSERT_NO_FATAL_FAILURE(CompareSuperblockAndBackupAllocCounts(fs(), Comparison::kDifferent));
ASSERT_EQ(fs().Unmount().status_value(), ZX_OK);
ASSERT_NO_FATAL_FAILURE(CompareSuperblockAndBackupAllocCounts(fs(), Comparison::kSame));
}
} // namespace
} // namespace minfs