diff --git a/AK/IntegralMath.h b/AK/IntegralMath.h index 4bf90181395..80d74694ae7 100644 --- a/AK/IntegralMath.h +++ b/AK/IntegralMath.h @@ -54,4 +54,18 @@ constexpr I pow(I base, I exponent) return res; } +template +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(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(base, exponent) == x) + return true; + } + return false; +} + } diff --git a/Kernel/FileSystem/Ext2FS/FileSystem.cpp b/Kernel/FileSystem/Ext2FS/FileSystem.cpp index f41901bde3e..1cb2e185e22 100644 --- a/Kernel/FileSystem/Ext2FS/FileSystem.cpp +++ b/Kernel/FileSystem/Ext2FS/FileSystem.cpp @@ -5,6 +5,7 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include #include #include #include @@ -30,7 +31,23 @@ ErrorOr 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 diff --git a/Kernel/FileSystem/Ext2FS/FileSystem.h b/Kernel/FileSystem/Ext2FS/FileSystem.h index 36087d76340..8080837dff6 100644 --- a/Kernel/FileSystem/Ext2FS/FileSystem.h +++ b/Kernel/FileSystem/Ext2FS/FileSystem.h @@ -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> try_create(OpenFileDescription&, ReadonlyBytes); diff --git a/Tests/AK/TestIntegerMath.cpp b/Tests/AK/TestIntegerMath.cpp index c43c747c5d1..73168a52d46 100644 --- a/Tests/AK/TestIntegerMath.cpp +++ b/Tests/AK/TestIntegerMath.cpp @@ -7,6 +7,7 @@ #include #include +#include TEST_CASE(pow) { @@ -18,3 +19,20 @@ TEST_CASE(pow) EXPECT_EQ(AK::pow(10, 5), 100'000ull); EXPECT_EQ(AK::pow(10, 6), 1'000'000ull); } + +TEST_CASE(is_power_of) +{ + constexpr auto check_prime = [](u64 limit) { + for (u64 power = 0; power < limit; ++power) + EXPECT(AK::is_power_of(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); +}