Kernel/Ext2: Write superblock backups

We don't ever read them out, but this should make fsck a lot less mad.
This commit is contained in:
kleines Filmröllchen 2023-07-18 17:38:04 +02:00 committed by Jelle Raaijmakers
parent cc1cb72fb5
commit a0705202ea
4 changed files with 54 additions and 2 deletions

View file

@ -54,4 +54,18 @@ constexpr I pow(I base, I exponent)
return res;
}
template<auto base, Unsigned U = decltype(base)>
constexpr bool is_power_of(U x)
{
if constexpr (base == 2)
return is_power_of_two(x);
// FIXME: I am naive! A log2-based approach (pow<U>(base, (log2(x) / log2(base))) == x) does not work due to rounding errors.
for (U exponent = 0; exponent <= log2(x) / log2(base) + 1; ++exponent) {
if (pow<U>(base, exponent) == x)
return true;
}
return false;
}
}

View file

@ -5,6 +5,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/IntegralMath.h>
#include <Kernel/Debug.h>
#include <Kernel/FileSystem/Ext2FS/FileSystem.h>
#include <Kernel/FileSystem/Ext2FS/Inode.h>
@ -30,7 +31,23 @@ ErrorOr<void> Ext2FS::flush_super_block()
MutexLocker locker(m_lock);
VERIFY((sizeof(ext2_super_block) % logical_block_size()) == 0);
auto super_block_buffer = UserOrKernelBuffer::for_kernel_buffer((u8*)&m_super_block);
return raw_write_blocks(2, (sizeof(ext2_super_block) / logical_block_size()), super_block_buffer);
auto const superblock_physical_block_count = (sizeof(ext2_super_block) / logical_block_size());
// First superblock is always at offset 1024 (physical block index 2).
TRY(raw_write_blocks(2, superblock_physical_block_count, super_block_buffer));
auto is_sparse = has_flag(get_features_readonly(), FeaturesReadOnly::SparseSuperblock);
for (auto group = 1u; group < m_block_group_count; ++group) {
auto first_block_in_group = first_block_of_group(group);
// Superblock copies with sparse layout are in group number 2 and powers of 3, 5, and 7.
if (!is_sparse || group == 2 || AK::is_power_of<3>(group - 1) || AK::is_power_of<5>(group - 1) || AK::is_power_of<7>(group - 1)) {
dbgln_if(EXT2_DEBUG, "Writing superblock backup to block group {} (block {})", group, first_block_in_group);
TRY(write_blocks(first_block_in_group, 1, super_block_buffer));
}
}
return {};
}
ext2_group_desc const& Ext2FS::group_descriptor(GroupIndex group_index) const

View file

@ -22,10 +22,13 @@ class Ext2FS final : public BlockBasedFileSystem {
friend class Ext2FSInode;
public:
// s_feature_ro_compat
enum class FeaturesReadOnly : u32 {
None = 0,
FileSize64bits = 1 << 1,
SparseSuperblock = EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER,
FileSize64bits = EXT2_FEATURE_RO_COMPAT_LARGE_FILE,
};
AK_ENUM_BITWISE_FRIEND_OPERATORS(FeaturesReadOnly);
static ErrorOr<NonnullRefPtr<FileSystem>> try_create(OpenFileDescription&, ReadonlyBytes);

View file

@ -7,6 +7,7 @@
#include <LibTest/TestCase.h>
#include <AK/IntegralMath.h>
#include <initializer_list>
TEST_CASE(pow)
{
@ -18,3 +19,20 @@ TEST_CASE(pow)
EXPECT_EQ(AK::pow<u64>(10, 5), 100'000ull);
EXPECT_EQ(AK::pow<u64>(10, 6), 1'000'000ull);
}
TEST_CASE(is_power_of)
{
constexpr auto check_prime = []<u64 prime>(u64 limit) {
for (u64 power = 0; power < limit; ++power)
EXPECT(AK::is_power_of<prime>(AK::pow(prime, power)));
};
// Limits calculated as floor( log_{prime}(2^64) ) to prevent overflows.
check_prime.operator()<2>(64);
check_prime.operator()<3>(40);
check_prime.operator()<5>(27);
check_prime.operator()<7>(20);
check_prime.operator()<11>(18);
check_prime.operator()<97>(9);
check_prime.operator()<257>(7);
}