blob: 65e89c576b80f08a4122bcdcdc508cc3f38432ba [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 <zxtest/zxtest.h>
#include "src/storage/minfs/test/integration/minfs_fixtures.h"
#include "src/storage/minfs/test/integration/utils.h"
namespace {
using fs::FilesystemTest;
using minfs::kMinfsBlockSize;
using minfs::kMinfsMagic0;
using minfs::kSuperblockStart;
using minfs::Superblock;
using SuperblockTest = MinfsTest;
using SuperblockTestWithFvm = MinfsTestWithFvm;
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));
}
// Tests backup superblock functionality.
void RunRepairCorruptSuperblockTest(FilesystemTest* test) {
ASSERT_NO_FAILURES(test->Unmount());
Superblock info;
ReadSuperblock(test->device_path(), kSuperblockStart, &info);
ASSERT_EQ(kMinfsMagic0, info.magic0);
// Corrupt superblock, by erasing it completely from disk.
memset(&info, 0, sizeof(info));
WriteSuperblock(test->device_path(), info);
// Running mount to repair the filesystem.
ASSERT_NO_FAILURES(test->Mount());
ASSERT_NO_FAILURES(test->Unmount());
// Confirm that the corrupted superblock is repaired by backup superblock.
ReadSuperblock(test->device_path(), kSuperblockStart, &info);
ASSERT_EQ(kMinfsMagic0, info.magic0);
}
TEST_F(SuperblockTest, RepairCorruptSuperblock) { RunRepairCorruptSuperblockTest(this); }
TEST_F(SuperblockTestWithFvm, RepairCorruptSuperblock) { RunRepairCorruptSuperblockTest(this); }
enum class Comparison {
kSame,
kDifferent,
};
void CompareSuperblockAndBackupAllocCounts(const std::string& device_path, Comparison value) {
Superblock info;
ReadSuperblock(device_path, kSuperblockStart, &info);
ASSERT_EQ(kMinfsMagic0, info.magic0);
Superblock backup_info;
ReadSuperblock(device_path, minfs::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() {
// Open the root directory to fsync the filesystem.
fbl::unique_fd fd_mount(open(kMountPath, O_RDONLY));
ASSERT_TRUE(fd_mount);
ASSERT_EQ(fsync(fd_mount.get()), 0);
}
// Tests alloc_*_counts write frequency difference for backup superblock.
TEST_F(SuperblockTest, AllocCountWriteFrequency) {
ASSERT_NO_FAILURES(CompareSuperblockAndBackupAllocCounts(device_path(), Comparison::kSame));
// Force allocating inodes as well as data blocks.
ASSERT_TRUE(CreateDirectory("/test_dir"));
fbl::unique_fd fd_file = CreateFile("/test_dir/file");
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();
// Check that superblock and backup alloc counts are now different.
ASSERT_NO_FAILURES(CompareSuperblockAndBackupAllocCounts(device_path(), Comparison::kDifferent));
ASSERT_NO_FAILURES(Unmount());
ASSERT_NO_FAILURES(CompareSuperblockAndBackupAllocCounts(device_path(), Comparison::kSame));
}
} // namespace