aboutsummaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'kernel')
-rw-r--r--kernel/kernel/filesystem/ext2/error.hpp9
-rw-r--r--kernel/kernel/filesystem/ext2/filesystem.cpp121
-rw-r--r--kernel/kernel/filesystem/ext2/filesystem.hpp18
-rw-r--r--kernel/kernel/filesystem/ext2/inode.cpp52
-rw-r--r--kernel/kernel/filesystem/ext2/inode.hpp8
5 files changed, 195 insertions, 13 deletions
diff --git a/kernel/kernel/filesystem/ext2/error.hpp b/kernel/kernel/filesystem/ext2/error.hpp
index 79f40517..986ab167 100644
--- a/kernel/kernel/filesystem/ext2/error.hpp
+++ b/kernel/kernel/filesystem/ext2/error.hpp
@@ -20,6 +20,7 @@ namespace kernel::filesystem::ext2
invalid_block_number,
failed_to_read_superblock,
failed_to_read_block_group_descriptors,
+ not_enough_free_blocks,
};
namespace detail
@@ -49,6 +50,8 @@ namespace kernel::filesystem::ext2
return "failed to read superblock";
case ext2_errc::failed_to_read_block_group_descriptors:
return "failed to read block group descriptors";
+ case ext2_errc::not_enough_free_blocks:
+ return "not enough free blocks";
default:
return "unknown ext2 error";
};
@@ -80,6 +83,12 @@ namespace kernel::filesystem::ext2
return condition.value() == static_cast<int>(kstd::errc::io_error);
}
break;
+ case ext2_errc::not_enough_free_blocks:
+ if (condition.category() == kstd::generic_category())
+ {
+ return condition.value() == static_cast<int>(kstd::errc::no_space_on_device);
+ }
+ break;
}
return kstd::error_category::equivalent(code, condition);
}
diff --git a/kernel/kernel/filesystem/ext2/filesystem.cpp b/kernel/kernel/filesystem/ext2/filesystem.cpp
index 34ee1f75..af693230 100644
--- a/kernel/kernel/filesystem/ext2/filesystem.cpp
+++ b/kernel/kernel/filesystem/ext2/filesystem.cpp
@@ -24,6 +24,22 @@
namespace kernel::filesystem::ext2
{
+ namespace
+ {
+ [[nodiscard]] auto bitmap_is_set(kstd::vector<uint8_t> const & bitmap, size_t index) -> bool
+ {
+ auto const byte_index = index / 8;
+ auto const bit_index = index % 8;
+ return (bitmap.at(byte_index) & static_cast<uint8_t>(1u << bit_index)) != 0;
+ }
+
+ auto bitmap_set(kstd::vector<uint8_t> & bitmap, size_t index) -> void
+ {
+ auto const byte_index = index / 8;
+ auto const bit_index = index % 8;
+ bitmap.at(byte_index) |= static_cast<uint8_t>(1u << bit_index);
+ }
+ } // namespace
struct type final : kernel::filesystem::type
{
@@ -107,14 +123,12 @@ namespace kernel::filesystem::ext2
for (uint32_t i = 0; i < inode_block_count(inode_data); ++i)
{
auto const global_block_number = map_inode_block_index_to_global_block_number(i, inode_data);
-
if (!global_block_number)
{
return kstd::failure(global_block_number.error());
}
- auto const block_offset = global_block_number.value() * block_size();
- if (auto read_result = m_backing_inode->read(buffer.data(), block_offset, block_size()); !read_result)
+ if (auto read_result = read_block(*global_block_number, buffer.data()); !read_result)
{
return kstd::failure(read_result.error());
}
@@ -160,7 +174,101 @@ namespace kernel::filesystem::ext2
return kstd::failure(read_result.error());
}
- return kstd::make_shared<inode>(this, new_inode_data);
+ return kstd::make_shared<inode>(const_cast<filesystem *>(this), inode_number, new_inode_data);
+ }
+
+ auto filesystem::write_inode(uint32_t inode_number, inode_data const & data) -> kstd::result<void>
+ {
+ // TODO BA-FS26 refactoring
+ auto const inodes_per_group = m_superblock.inodes_per_group;
+ auto const block_group_index = (inode_number - 1) / inodes_per_group;
+ auto const inode_index_within_group = (inode_number - 1) % inodes_per_group;
+
+ if (block_group_index >= m_block_group_descriptors.size())
+ {
+ return kstd::failure(ext2_errc::invalid_block_group_index);
+ }
+
+ auto const & block_group_descriptor = m_block_group_descriptors.at(block_group_index);
+ auto const inode_table_start_block = block_group_descriptor.inode_table;
+ auto const inode_table_offset = static_cast<size_t>(inode_table_start_block) * block_size();
+ auto const inode_offset = inode_table_offset + inode_index_within_group * inode_size();
+
+ return m_backing_inode->write(&data, inode_offset, sizeof(inode_data)).transform([](auto) {});
+ }
+
+ auto filesystem::allocate_blocks(size_t count) -> kstd::result<kstd::vector<uint32_t>>
+ {
+ if (m_block_group_descriptors.empty() || count > m_superblock.free_blocks_count)
+ {
+ return kstd::failure(ext2_errc::not_enough_free_blocks);
+ }
+
+ kstd::vector<uint32_t> allocated_blocks{};
+
+ for (auto block_group_descriptor_index = 0uz; block_group_descriptor_index < m_block_group_descriptors.size();
+ ++block_group_descriptor_index)
+ {
+ auto & block_group_descriptor = m_block_group_descriptors.at(block_group_descriptor_index);
+ if (block_group_descriptor.free_blocks_count == 0)
+ {
+ continue;
+ }
+
+ kstd::vector<uint8_t> block_bitmap{};
+ block_bitmap.resize(block_size(), 0);
+ if (auto read_result = read_block(block_group_descriptor.block_bitmap, block_bitmap.data()); !read_result)
+ {
+ return kstd::failure(read_result.error());
+ }
+
+ bool is_bitmap_dirty = false;
+ for (auto i = 0uz; i < block_bitmap.size() * 8 && allocated_blocks.size() < count; ++i)
+ {
+ if (!bitmap_is_set(block_bitmap, i))
+ {
+ is_bitmap_dirty = true;
+
+ bitmap_set(block_bitmap, i);
+ block_group_descriptor.free_blocks_count--;
+ m_superblock.free_blocks_count--;
+
+ allocated_blocks.push_back(i + block_group_descriptor_index * m_superblock.blocks_per_group);
+ }
+ }
+
+ if (is_bitmap_dirty)
+ {
+ if (auto write_result = write_block(block_group_descriptor.block_bitmap, block_bitmap.data()); !write_result)
+ {
+ return kstd::failure(write_result.error());
+ }
+
+ // TODO update all block group descriptors
+ if (auto write_result = m_backing_inode->write(
+ &block_group_descriptor,
+ block_group_descriptor_table_offset() + block_group_descriptor_index * sizeof(block_group_descriptor),
+ sizeof(block_group_descriptor));
+ !write_result)
+ {
+ return kstd::failure(write_result.error());
+ }
+ }
+ }
+
+ // TODO update all superblocks
+ if (auto write_result = m_backing_inode->write(&m_superblock, constants::superblock_offset, sizeof(m_superblock));
+ !write_result)
+ {
+ return kstd::failure(write_result.error());
+ }
+
+ if (allocated_blocks.size() != count)
+ {
+ return kstd::failure(ext2_errc::not_enough_free_blocks);
+ }
+
+ return allocated_blocks;
}
auto filesystem::indirect_levels() const -> std::array<indirect_level, 3>
@@ -282,6 +390,11 @@ namespace kernel::filesystem::ext2
return data.blocks / (2 << m_superblock.log_block_size);
}
+ auto filesystem::update_inode_block_count(inode_data & data, uint32_t delta) -> void
+ {
+ data.blocks = delta * (2 << m_superblock.log_block_size);
+ }
+
auto filesystem::block_group_descriptor_table_offset() const -> size_t
{
return block_size() == 1024 ? 2 * block_size() : block_size();
diff --git a/kernel/kernel/filesystem/ext2/filesystem.hpp b/kernel/kernel/filesystem/ext2/filesystem.hpp
index 5e5913bf..7361943a 100644
--- a/kernel/kernel/filesystem/ext2/filesystem.hpp
+++ b/kernel/kernel/filesystem/ext2/filesystem.hpp
@@ -78,6 +78,18 @@ namespace kernel::filesystem::ext2
*/
[[nodiscard]] auto revision_level() const -> uint32_t;
+ auto read_block(uint32_t block_number, void * buffer) const -> kstd::result<size_t>;
+
+ auto write_block(uint32_t block_number, void const * buffer) -> kstd::result<size_t>;
+
+ auto allocate_blocks(size_t count) -> kstd::result<kstd::vector<uint32_t>>;
+
+ [[nodiscard]] auto inode_block_count(inode_data const & data) const -> uint32_t;
+
+ auto update_inode_block_count(inode_data & data, uint32_t delta) -> void;
+
+ auto write_inode(uint32_t inode_number, inode_data const & data) -> kstd::result<void>;
+
/**
@brief Maps an inode block index to a global block number.
@param inode_block_index The index of the block within the inode.
@@ -87,6 +99,9 @@ namespace kernel::filesystem::ext2
[[nodiscard]] auto map_inode_block_index_to_global_block_number(size_t inode_block_index, inode_data data) const
-> kstd::result<std::size_t>;
+ auto write_global_block_number_to_inode_block_index(size_t block_index, inode_data & data,
+ uint32_t global_block_number) -> void;
+
private:
struct indirect_level
{
@@ -99,8 +114,9 @@ namespace kernel::filesystem::ext2
[[nodiscard]] auto read_inode(uint32_t inode_number) const -> kstd::result<kstd::shared_ptr<inode>>;
[[nodiscard]] auto read_block_number_at_index(uint32_t block_number, size_t index) const -> kstd::result<uint32_t>;
+ auto ensure_and_get_block(uint32_t & block_number) -> bool;
+
[[nodiscard]] auto inode_size() const -> uint16_t;
- [[nodiscard]] auto inode_block_count(inode_data const & data) const -> uint32_t;
[[nodiscard]] auto block_group_descriptor_table_offset() const -> size_t;
[[nodiscard]] auto block_numbers_per_block() const -> size_t;
diff --git a/kernel/kernel/filesystem/ext2/inode.cpp b/kernel/kernel/filesystem/ext2/inode.cpp
index 136f37a4..2484d2f5 100644
--- a/kernel/kernel/filesystem/ext2/inode.cpp
+++ b/kernel/kernel/filesystem/ext2/inode.cpp
@@ -14,8 +14,9 @@
namespace kernel::filesystem::ext2
{
- inode::inode(filesystem const * fs, inode_data const & data)
+ inode::inode(filesystem * fs, uint32_t inode_number, inode_data const & data)
: m_filesystem(fs)
+ , m_inode_number(inode_number)
, m_data(data)
{
if (!m_filesystem)
@@ -83,15 +84,35 @@ namespace kernel::filesystem::ext2
kapi::system::panic("[EXT2] ext2::inode write called on inode that is not of type regular");
}
- auto const max_writable = this->size() - offset;
- auto const requested_size = std::min(size, max_writable);
+ // TODO check maximum file size of filesystem
+ // TODO handle sparse files
+
+ auto const new_inode_size = std::max(this->size(), offset + size);
+
+ auto const current_block_count = m_filesystem->inode_block_count(m_data);
+ auto const max_new_inode_size_without_new_blocks = current_block_count * m_filesystem->block_size();
+
+ if (new_inode_size > max_new_inode_size_without_new_blocks)
+ {
+ auto const additional_blocks_needed =
+ (new_inode_size - max_new_inode_size_without_new_blocks + m_filesystem->block_size() - 1) /
+ m_filesystem->block_size();
+
+ if (!append_blocks(additional_blocks_needed))
+ {
+ // TODO handle out of space
+ kapi::system::panic("[EXT2] Failed to allocate blocks for inode write");
+ }
+ }
+
+ // TODO update inode size, ...
auto block_index = offset / m_filesystem->block_size();
auto in_block_offset = offset % m_filesystem->block_size();
auto bytes_written = 0uz;
- while (bytes_written < requested_size)
+ while (bytes_written < size)
{
auto const block_number = m_filesystem->map_inode_block_index_to_global_block_number(block_index, m_data);
// TODO BA-FS26 if blocknumber == 0 --> handle sparse file
@@ -100,8 +121,7 @@ namespace kernel::filesystem::ext2
kstd::failure(block_number.error());
}
- auto const bytes_to_write =
- std::min(requested_size - bytes_written, m_filesystem->block_size() - in_block_offset);
+ auto const bytes_to_write = std::min(size - bytes_written, m_filesystem->block_size() - in_block_offset);
auto const block_start_offset = block_number.value() * m_filesystem->block_size();
auto const write_offset = block_start_offset + in_block_offset;
@@ -122,6 +142,26 @@ namespace kernel::filesystem::ext2
return bytes_written;
}
+ auto inode::append_blocks(size_t count) -> bool
+ {
+ auto new_blocks = m_filesystem->allocate_blocks(count);
+ if (!new_blocks)
+ {
+ return false;
+ }
+
+ for (auto i = 0uz; i < new_blocks->size(); ++i)
+ {
+ auto const block_index = m_filesystem->inode_block_count(m_data) + i;
+ auto const global_block_number = new_blocks->at(i);
+
+ m_filesystem->write_global_block_number_to_inode_block_index(block_index, m_data, global_block_number);
+ }
+
+ m_filesystem->update_inode_block_count(m_data, count);
+ return m_filesystem->write_inode(m_inode_number, m_data).has_value();
+ }
+
[[nodiscard]] auto inode::data() const -> inode_data const &
{
return m_data;
diff --git a/kernel/kernel/filesystem/ext2/inode.hpp b/kernel/kernel/filesystem/ext2/inode.hpp
index f04fb1e1..175264b3 100644
--- a/kernel/kernel/filesystem/ext2/inode.hpp
+++ b/kernel/kernel/filesystem/ext2/inode.hpp
@@ -44,9 +44,10 @@ namespace kernel::filesystem::ext2
/**
@brief Create an ext2 inode associated with the given filesystem.
@param fs The ext2 filesystem that this inode belongs to.
+ @param inode_number The inode number on disk.
@param data The data associated with this inode, read from the disk.
*/
- explicit inode(filesystem const * fs, inode_data const & data);
+ explicit inode(filesystem * fs, uint32_t inode_number, inode_data const & data);
/**
@brief Reads from the ext2 inode into a @p buffer, starting at the specified @p offset and for a given @p size.
@@ -97,7 +98,10 @@ namespace kernel::filesystem::ext2
[[nodiscard]] auto size() const -> uint64_t;
private:
- filesystem const * m_filesystem;
+ auto append_blocks(size_t count) -> bool;
+
+ filesystem * m_filesystem;
+ uint32_t m_inode_number{};
inode_data m_data{};
};
} // namespace kernel::filesystem::ext2