aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--kernel/kernel/filesystem/ext2/directory_iterator.cpp14
-rw-r--r--kernel/kernel/filesystem/ext2/directory_iterator.hpp5
-rw-r--r--kernel/kernel/filesystem/ext2/directory_iterator.tests.cpp9
-rw-r--r--kernel/kernel/filesystem/ext2/filesystem.cpp84
-rw-r--r--kernel/kernel/filesystem/ext2/filesystem.hpp29
-rw-r--r--kernel/kernel/filesystem/ext2/inode.cpp79
-rw-r--r--kernel/kernel/filesystem/ext2/mount_state.hpp3
7 files changed, 146 insertions, 77 deletions
diff --git a/kernel/kernel/filesystem/ext2/directory_iterator.cpp b/kernel/kernel/filesystem/ext2/directory_iterator.cpp
index 8475cdd9..3e614f26 100644
--- a/kernel/kernel/filesystem/ext2/directory_iterator.cpp
+++ b/kernel/kernel/filesystem/ext2/directory_iterator.cpp
@@ -1,6 +1,8 @@
#include <kernel/filesystem/ext2/directory_iterator.hpp>
+#include <kernel/filesystem/ext2/filesystem.hpp>
#include <kernel/filesystem/ext2/inode.hpp>
+#include <kernel/filesystem/ext2/mount_state.hpp>
#include <kapi/system.hpp>
@@ -10,8 +12,10 @@
namespace kernel::filesystem::ext2
{
- directory_iterator::directory_iterator(inode const & inode)
- : m_inode(&inode)
+ directory_iterator::directory_iterator(inode const & inode, filesystem const & filesystem, mount_state & state)
+ : m_inode{&inode}
+ , m_filesystem{&filesystem}
+ , m_state{&state}
, m_buffer{sizeof(value_type)}
{
if (!inode.is_directory())
@@ -33,7 +37,7 @@ namespace kernel::filesystem::ext2
auto directory_iterator::operator++() -> directory_iterator &
{
- if (!m_inode || m_file_offset >= m_inode->size())
+ if (!m_inode || m_file_offset >= m_filesystem->data_size(*m_inode, *m_state))
{
m_inode = nullptr;
return *this;
@@ -60,7 +64,7 @@ namespace kernel::filesystem::ext2
auto directory_iterator::read() -> void
{
- if (auto result = m_inode->read(m_buffer, m_file_offset); !result)
+ if (auto result = m_filesystem->do_read(*m_inode, m_buffer, m_file_offset, *m_state); !result)
{
kapi::system::panic("[FS:ext2] failed to read directory entry", result.error());
}
@@ -73,7 +77,7 @@ namespace kernel::filesystem::ext2
{
m_buffer.resize(m_buffer.size() + remainder);
- if (auto result = m_inode->read(m_buffer, m_file_offset); !result)
+ if (auto result = m_filesystem->do_read(*m_inode, m_buffer, m_file_offset, *m_state); !result)
{
kapi::system::panic("[FS:ext2] failed to read directory entry", result.error());
}
diff --git a/kernel/kernel/filesystem/ext2/directory_iterator.hpp b/kernel/kernel/filesystem/ext2/directory_iterator.hpp
index 92dd2b51..4e2fd9d8 100644
--- a/kernel/kernel/filesystem/ext2/directory_iterator.hpp
+++ b/kernel/kernel/filesystem/ext2/directory_iterator.hpp
@@ -3,6 +3,7 @@
#include <kernel/filesystem/ext2/inode.hpp>
#include <kernel/filesystem/ext2/linked_directory_entry.hpp>
+#include <kernel/filesystem/ext2/mount_state.hpp>
#include <kstd/memory.hpp>
#include <kstd/units.hpp>
@@ -23,7 +24,7 @@ namespace kernel::filesystem::ext2
using reference = linked_directory_entry const &;
constexpr directory_iterator() = default;
- explicit directory_iterator(inode const & inode);
+ directory_iterator(inode const & inode, filesystem const & filesystem, mount_state & state);
auto operator*() const -> reference;
@@ -39,6 +40,8 @@ namespace kernel::filesystem::ext2
auto read() -> void;
kstd::observer_ptr<inode const> m_inode{};
+ kstd::observer_ptr<filesystem const> m_filesystem{};
+ kstd::observer_ptr<mount_state> m_state{};
kstd::bytes m_file_offset{};
kstd::vector<std::byte> m_buffer{};
};
diff --git a/kernel/kernel/filesystem/ext2/directory_iterator.tests.cpp b/kernel/kernel/filesystem/ext2/directory_iterator.tests.cpp
index 20ef7009..2d817e0b 100644
--- a/kernel/kernel/filesystem/ext2/directory_iterator.tests.cpp
+++ b/kernel/kernel/filesystem/ext2/directory_iterator.tests.cpp
@@ -4,6 +4,7 @@
#include <kernel/filesystem/device_inode.hpp>
#include <kernel/filesystem/ext2/filesystem.hpp>
#include <kernel/filesystem/ext2/inode.hpp>
+#include <kernel/filesystem/ext2/mount_state.hpp>
#include <kernel/filesystem/mount.hpp>
#include <kernel/test_support/filesystem/storage_boot_module_fixture.hpp>
@@ -37,12 +38,14 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_fixture,
auto root = (*mount)->root_dentry()->inode();
auto const & root_ext2_inode = static_cast<kernel::filesystem::ext2::inode const &>(*root);
+ auto mount_state = static_pointer_cast<kernel::filesystem::ext2::mount_state>((*mount)->driver_data());
+
WHEN("iterating from begin to the default-constructed end sentinel")
{
auto names = std::vector<std::string>{};
auto guard = 0;
- for (auto it = kernel::filesystem::ext2::directory_iterator{root_ext2_inode};
+ for (auto it = kernel::filesystem::ext2::directory_iterator{root_ext2_inode, *fs, *mount_state};
it != kernel::filesystem::ext2::directory_iterator{}; ++it)
{
REQUIRE(guard++ < 64); // fails loudly on a non-terminating loop rather than hanging the suite
@@ -82,7 +85,9 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_fixture,
auto root = (*mount)->root_dentry()->inode();
auto const & root_ext2_inode = static_cast<kernel::filesystem::ext2::inode const &>(*root);
- auto original = kernel::filesystem::ext2::directory_iterator{root_ext2_inode};
+ auto mount_state = static_pointer_cast<kernel::filesystem::ext2::mount_state>((*mount)->driver_data());
+
+ auto original = kernel::filesystem::ext2::directory_iterator{root_ext2_inode, *fs, *mount_state};
auto const first_name = std::string{&original->name_start, original->name_len};
WHEN("the iterator is copied, then only the original is advanced")
diff --git a/kernel/kernel/filesystem/ext2/filesystem.cpp b/kernel/kernel/filesystem/ext2/filesystem.cpp
index 49b78d0c..bf977c46 100644
--- a/kernel/kernel/filesystem/ext2/filesystem.cpp
+++ b/kernel/kernel/filesystem/ext2/filesystem.cpp
@@ -17,12 +17,14 @@
#include <kstd/cstring.hpp>
#include <kstd/memory.hpp>
+#include <kstd/mutex.hpp>
#include <kstd/result.hpp>
#include <kstd/span.hpp>
#include <kstd/system_error.hpp>
#include <kstd/units.hpp>
#include <kstd/vector.hpp>
+#include <algorithm>
#include <array>
#include <cstddef>
#include <cstdint>
@@ -143,7 +145,7 @@ namespace kernel::filesystem::ext2
auto filesystem::mount(inode_ptr const & backing_inode) -> kstd::result<mount_result>
{
auto const mount_state = kstd::make_shared<struct mount_state>();
- auto & [superblock, block_group_descriptors, backing] = *mount_state;
+ auto & [superblock, block_group_descriptors, backing, _] = *mount_state;
backing = backing_inode;
@@ -205,7 +207,9 @@ namespace kernel::filesystem::ext2
return kstd::failure(vfs_errc::not_mounted);
}
- for (auto it = directory_iterator{*directory}; it != directory_iterator{}; ++it)
+ auto guard = kstd::lock_guard{mount_state->lock};
+
+ for (auto it = directory_iterator{*directory, *this, *mount_state}; it != directory_iterator{}; ++it)
{
if (it->inode == 0)
{
@@ -253,6 +257,7 @@ namespace kernel::filesystem::ext2
return kstd::failure(vfs_errc::invalid_driver_data);
}
+ auto guard = kstd::lock_guard{mount_state->lock};
auto batch = write_batch{*this, *mount_state};
auto maybe_inode_number = allocate_inode(*mount_state, batch);
@@ -295,7 +300,7 @@ namespace kernel::filesystem::ext2
{
return kstd::failure(result.error());
}
- created->set_size(block_size(*mount_state));
+ set_data_size(*created, block_size(*mount_state), *mount_state);
}
batch.mark_inode(*created);
@@ -579,7 +584,7 @@ namespace kernel::filesystem::ext2
auto filesystem::allocate_blocks(size_t count, mount_state & state, write_batch & batch)
-> kstd::result<kstd::vector<uint32_t>>
{
- auto & [superblock, block_group_descriptors, backing_inode] = state;
+ auto & [superblock, block_group_descriptors, backing_inode, _] = state;
if (block_group_descriptors.empty() || count > superblock.free_blocks_count)
{
@@ -649,9 +654,78 @@ namespace kernel::filesystem::ext2
return allocated_blocks;
}
+ auto filesystem::data_size(inode const & inode, mount_state const & state) const -> kstd::bytes
+ {
+ uint64_t size = inode.data().size;
+
+ if (revision_level(state) > constants::good_old_revision && inode.is_regular())
+ {
+ size |= static_cast<uint64_t>(inode.data().dir_acl) << 32;
+ }
+
+ return kstd::bytes{size};
+ }
+
+ auto filesystem::set_data_size(inode & inode, kstd::bytes new_size, mount_state const & state) const -> void
+ {
+ if (revision_level(state) > constants::good_old_revision && inode.is_regular())
+ {
+ inode.data().dir_acl = static_cast<uint32_t>(new_size.value >> 32);
+ }
+ inode.data().size = static_cast<uint32_t>(new_size.value);
+ }
+
+ auto filesystem::do_read(inode const & inode, std::span<std::byte> buffer, kstd::bytes offset,
+ mount_state const & state) const -> kstd::result<kstd::bytes>
+ {
+ auto const max_readable = data_size(inode, state) - offset;
+ auto const requested_size = std::min(kstd::bytes{buffer.size()}, max_readable);
+
+ auto const block_size = this->block_size(state);
+ auto block_index = offset / block_size;
+ auto in_block_offset = offset % block_size;
+
+ auto bytes_read = 0_B;
+
+ while (bytes_read < requested_size)
+ {
+ auto const block_number = inode_block_number(block_index, inode, state);
+ if (!block_number)
+ {
+ break;
+ }
+
+ auto const bytes_to_read = std::min(requested_size - bytes_read, block_size - in_block_offset);
+ if (block_number == 0)
+ {
+ kstd::libc::memset(buffer.data() + bytes_read, 0, bytes_to_read.value);
+ bytes_read += bytes_to_read;
+ }
+ else
+ {
+ auto const block_start_offset = block_number.value() * block_size;
+ auto const read_offset = block_start_offset + in_block_offset;
+
+ auto const read_result =
+ state.backing_inode->read(buffer.subspan(bytes_read.value, bytes_to_read.value), read_offset);
+ if (!read_result)
+ {
+ return kstd::failure(read_result.error());
+ }
+
+ bytes_read += read_result.value();
+ }
+
+ block_index++;
+ in_block_offset = 0_B;
+ }
+
+ return bytes_read;
+ }
+
auto filesystem::allocate_inode(mount_state & state, write_batch & batch) -> kstd::result<uint32_t>
{
- auto & [superblock, block_group_descriptors, _] = state;
+ auto & [superblock, block_group_descriptors, _, __] = state;
if (block_group_descriptors.empty() || superblock.free_inodes_count == 0)
{
diff --git a/kernel/kernel/filesystem/ext2/filesystem.hpp b/kernel/kernel/filesystem/ext2/filesystem.hpp
index 1bce8d71..6d5720a2 100644
--- a/kernel/kernel/filesystem/ext2/filesystem.hpp
+++ b/kernel/kernel/filesystem/ext2/filesystem.hpp
@@ -2,6 +2,7 @@
#define TEACHOS_KERNEL_FILESYSTEM_EXT2_FILESYSTEM_HPP
#include <kernel/filesystem/ext2/block_group_descriptor.hpp>
+#include <kernel/filesystem/ext2/directory_iterator.hpp>
#include <kernel/filesystem/ext2/inode.hpp>
#include <kernel/filesystem/ext2/linked_directory_entry.hpp>
#include <kernel/filesystem/ext2/mount_state.hpp>
@@ -195,7 +196,33 @@ namespace kernel::filesystem::ext2
write_batch & batch) -> kstd::result<void>;
private:
- friend struct write_batch;
+ friend write_batch;
+ friend inode;
+ friend directory_iterator;
+
+ //! Perform the actual read of the requested data.
+ //!
+ //! @param inode The inode whose data to read.
+ //! @param buffer The buffer to read into.
+ //! @param offset The offset inside the inode's data to start at.
+ //! @param state The driver state to operate on.
+ //! @return The number of bytes read on success, an error otherwise.
+ [[nodiscard]] auto do_read(inode const & inode, std::span<std::byte> buffer, kstd::bytes offset,
+ mount_state const & state) const -> kstd::result<kstd::bytes>;
+
+ //! Determine the size of an inode's data on disk.
+ //!
+ //! @param inode The inode whose data size to determine.
+ //! @param state The driver state to operate on.
+ //! @return The size of the data referenced by the inode.
+ [[nodiscard]] auto data_size(inode const & inode, mount_state const & state) const -> kstd::bytes;
+
+ //! Set the size of an inode's data on disk.
+ //!
+ //! @param inode The inode whose data size to set.
+ //! @param new_size The size to set.
+ //! @param state The driver state to operate on.
+ auto set_data_size(inode & inode, kstd::bytes new_size, mount_state const & state) const -> void;
//! Allocate a single inode in the file system.
//!
diff --git a/kernel/kernel/filesystem/ext2/inode.cpp b/kernel/kernel/filesystem/ext2/inode.cpp
index 56174638..075d6c5f 100644
--- a/kernel/kernel/filesystem/ext2/inode.cpp
+++ b/kernel/kernel/filesystem/ext2/inode.cpp
@@ -13,6 +13,7 @@
#include <kstd/cstring.hpp>
#include <kstd/memory.hpp>
+#include <kstd/mutex.hpp>
#include <kstd/result.hpp>
#include <kstd/units.hpp>
@@ -33,11 +34,10 @@ namespace kernel::filesystem::ext2
auto inode::read(std::span<std::byte> buffer, kstd::bytes offset) const -> kstd::result<kstd::bytes>
{
- auto const max_readable = this->size() - offset;
- auto const requested_size = std::min(kstd::bytes{buffer.size()}, max_readable);
-
if (is_symbolic_link() && this->size() <= kstd::bytes{sizeof(m_data.block)})
{
+ auto const max_readable = this->size() - offset;
+ auto const requested_size = std::min(kstd::bytes{buffer.size()}, max_readable);
auto inline_target = reinterpret_cast<uint8_t const *>(m_data.block.data());
kstd::libc::memcpy(buffer.data(), inline_target + offset, requested_size.value);
return requested_size;
@@ -61,46 +61,8 @@ namespace kernel::filesystem::ext2
return kstd::failure(vfs_errc::not_mounted);
}
- auto block_size = (*filesystem)->block_size(**state);
- auto block_index = offset / block_size;
- auto in_block_offset = offset % block_size;
-
- auto bytes_read = 0_B;
-
- while (bytes_read < requested_size)
- {
- auto const block_number = (*filesystem)->inode_block_number(block_index, *this, **state);
- if (!block_number)
- {
- break;
- }
-
- auto const bytes_to_read = std::min(requested_size - bytes_read, block_size - in_block_offset);
- if (block_number == 0)
- {
- kstd::libc::memset(buffer.data() + bytes_read, 0, bytes_to_read.value);
- bytes_read += bytes_to_read;
- }
- else
- {
- auto const block_start_offset = block_number.value() * block_size;
- auto const read_offset = block_start_offset + in_block_offset;
-
- auto const read_result =
- backing_inode->read(buffer.subspan(bytes_read.value, bytes_to_read.value), read_offset);
- if (!read_result)
- {
- return kstd::failure(read_result.error());
- }
-
- bytes_read += read_result.value();
- }
-
- block_index++;
- in_block_offset = 0_B; // After the first block, we always start at the beginning of the block
- }
-
- return bytes_read;
+ auto guard = kstd::lock_guard{(*state)->lock};
+ return (*filesystem)->do_read(*this, buffer, offset, **state);
}
auto inode::write(std::span<std::byte const> buffer, kstd::bytes offset) -> kstd::result<kstd::bytes>
@@ -113,8 +75,6 @@ namespace kernel::filesystem::ext2
// TODO check maximum file size of filesystem
// TODO handle sparse files
- auto const new_inode_size = std::max(this->size(), offset + kstd::bytes{buffer.size()});
-
auto filesystem = get_filesystem();
if (!filesystem)
{
@@ -133,13 +93,15 @@ namespace kernel::filesystem::ext2
return kstd::failure(vfs_errc::not_mounted);
}
- auto block_size = (*filesystem)->block_size(**state);
+ auto guard = kstd::lock_guard{(*state)->lock};
+ auto batch = write_batch{**filesystem, **state};
+ auto const new_inode_size = std::max((*filesystem)->data_size(*this, **state), offset + kstd::bytes{buffer.size()});
+
+ auto block_size = (*filesystem)->block_size(**state);
auto const current_block_count = (*filesystem)->block_count(*this, **state);
auto const max_new_inode_size_without_new_blocks = current_block_count * block_size;
- auto batch = write_batch{**filesystem, **state};
-
if (new_inode_size > max_new_inode_size_without_new_blocks)
{
auto const additional_blocks_needed =
@@ -183,7 +145,8 @@ namespace kernel::filesystem::ext2
in_block_offset = 0_B;
}
- set_size(std::max(this->size(), offset + bytes_written));
+ (*filesystem)
+ ->set_data_size(*this, std::max((*filesystem)->data_size(*this, **state), offset + bytes_written), **state);
batch.mark_inode(*this);
return bytes_written;
}
@@ -297,12 +260,8 @@ namespace kernel::filesystem::ext2
}
auto state = static_pointer_cast<mount_state>(*driver_data);
-
- if ((*filesystem)->revision_level(*state) > constants::good_old_revision && is_regular())
- {
- m_data.dir_acl = static_cast<uint32_t>(new_size.value >> 32);
- }
- m_data.size = static_cast<uint32_t>(new_size.value);
+ auto guard = kstd::lock_guard{state->lock};
+ return (*filesystem)->set_data_size(*this, new_size, *state);
}
[[nodiscard]] auto inode::data() -> inode_data &
@@ -317,8 +276,6 @@ namespace kernel::filesystem::ext2
auto inode::size() const -> kstd::bytes
{
- uint64_t size = m_data.size;
-
auto filesystem = get_filesystem();
if (!filesystem)
{
@@ -331,12 +288,8 @@ namespace kernel::filesystem::ext2
return 0_B;
}
- if ((*filesystem)->revision_level(**state) > constants::good_old_revision && is_regular())
- {
- size |= static_cast<uint64_t>(m_data.dir_acl) << 32;
- }
-
- return kstd::bytes{size};
+ auto guard = kstd::lock_guard{(*state)->lock};
+ return (*filesystem)->data_size(*this, **state);
}
[[nodiscard]] auto inode::number() const -> uint32_t
diff --git a/kernel/kernel/filesystem/ext2/mount_state.hpp b/kernel/kernel/filesystem/ext2/mount_state.hpp
index e8ae8fd7..ba705382 100644
--- a/kernel/kernel/filesystem/ext2/mount_state.hpp
+++ b/kernel/kernel/filesystem/ext2/mount_state.hpp
@@ -5,6 +5,8 @@
#include <kernel/filesystem/ext2/superblock.hpp>
#include <kernel/filesystem/inode.hpp>
+#include <kapi/tracked_mutex.hpp>
+
#include <kstd/memory.hpp>
#include <kstd/vector.hpp>
@@ -16,6 +18,7 @@ namespace kernel::filesystem::ext2
struct superblock superblock{};
kstd::vector<block_group_descriptor> block_group_descriptors{};
kstd::shared_ptr<kernel::filesystem::inode> backing_inode{};
+ mutable kapi::tracked_mutex lock{};
};
} // namespace kernel::filesystem::ext2