diff options
| -rw-r--r-- | kernel/kernel/filesystem/ext2/filesystem.cpp | 134 | ||||
| -rw-r--r-- | kernel/kernel/filesystem/ext2/filesystem.hpp | 6 | ||||
| -rw-r--r-- | kernel/kernel/filesystem/ext2/inode.cpp | 5 | ||||
| -rw-r--r-- | kernel/kernel/filesystem/ext2/inode.hpp | 8 |
4 files changed, 113 insertions, 40 deletions
diff --git a/kernel/kernel/filesystem/ext2/filesystem.cpp b/kernel/kernel/filesystem/ext2/filesystem.cpp index 89f25d42..bd1b99a2 100644 --- a/kernel/kernel/filesystem/ext2/filesystem.cpp +++ b/kernel/kernel/filesystem/ext2/filesystem.cpp @@ -167,6 +167,12 @@ namespace kernel::filesystem::ext2 return kstd::failure(vfs_errc::not_a_directory); } + auto * ext2_parent = static_cast<inode *>(parent.get()); + if (!ext2_parent) + { + return nullptr; + } + auto maybe_inode_number = allocate_inode(); if (!maybe_inode_number) { @@ -185,80 +191,85 @@ namespace kernel::filesystem::ext2 new_inode_data.blocks = 0; new_inode_data.block.fill(0); - if (auto write_result = write_inode(inode_number, new_inode_data); !write_result) + // TODO preallocate blocks depending on type and m_superblock.prealloc_dir_blocks or m_superblock.prealloc_blocks; + + if (auto result = add_directory_entry(ext2_parent, name, inode_number, new_inode_data.mode); !result) { - return kstd::failure(write_result.error()); + return kstd::failure(result.error()); } - // add directory entry to parent - if (auto * ext2_parent = static_cast<inode *>(parent.get())) + auto new_inode = kstd::make_shared<inode>(this, inode_number, new_inode_data); + + bool is_directory = false; // TODO refactor + if (is_directory) { - if (auto result = add_directory_entry(ext2_parent, name, inode_number, new_inode_data.mode); - !result) // TODO handle mode correctly + if (auto result = init_directory(new_inode, ext2_parent->number()); !result) { return kstd::failure(result.error()); } - // TODO + + // '..' inside the new dir counts as an extra hard link to the parent + auto & parent_inode_data = ext2_parent->data_mutable(); + parent_inode_data.links_count++; + + if (auto write_result = write_inode(ext2_parent->number(), parent_inode_data); !write_result) + { + return kstd::failure(write_result.error()); + } } + + if (auto write_result = write_inode(inode_number, new_inode_data); !write_result) + { + return kstd::failure(write_result.error()); + } + + return new_inode; } auto filesystem::add_directory_entry(inode * ext2_parent, std::string_view name, uint32_t child_inode_number, [[maybe_unused]] uint16_t mode) -> kstd::result<void> { auto const last_block_index = inode_block_count(ext2_parent->data()) - 1; - auto const global_block_number = - map_inode_block_index_to_global_block_number(last_block_index, ext2_parent->data()); + auto const global_block = map_inode_block_index_to_global_block_number(last_block_index, ext2_parent->data()); - if (!global_block_number) + if (!global_block) { - return kstd::failure(global_block_number.error()); + return kstd::failure(global_block.error()); } kstd::vector<uint8_t> buffer(block_size()); - if (auto read_result = read_block(*global_block_number, buffer.data()); !read_result) + if (auto read_result = read_block(*global_block, buffer.data()); !read_result) { return kstd::failure(read_result.error()); } + // TODO handle "gaps" in directory entries (e.g., after deletions) in the current implementation, we only + // add new entries at the end of the last block, but we could also reuse space from deleted entries auto offset = 0uz; while (true) { auto const * entry = reinterpret_cast<linked_directory_entry const *>(buffer.data() + offset); - if (offset + entry->rec_len >= block_size()) { break; } - offset += entry->rec_len; } - // auto const * last_entry = reinterpret_cast<linked_directory_entry const *>(buffer.data() + offset); - - // auto const last_entry_actual_len = static_cast<uint16_t>((8u + last_entry->name_len + 3u) & ~3u); - // auto const free_space = last_entry->rec_len - last_entry_actual_len; - - // auto const last_entry_actual_length = 0; // TODO + auto const file_type = map_inode_mode_to_file_type(mode); auto const name_len = static_cast<uint8_t>(name.size()); auto const needed_rec_len = static_cast<uint16_t>((8u + name_len + 3u) & ~3u); auto * last_entry = reinterpret_cast<linked_directory_entry *>(buffer.data() + offset); auto const last_entry_actual_len = static_cast<uint16_t>((8u + last_entry->name_len + 3u) & ~3u); - auto const free_space = last_entry->rec_len - last_entry_actual_len; - if (free_space >= needed_rec_len) + if (last_entry->rec_len - last_entry_actual_len >= needed_rec_len) { last_entry->rec_len = last_entry_actual_len; - offset += last_entry_actual_len; - - auto * new_entry = reinterpret_cast<linked_directory_entry *>(buffer.data() + offset); - new_entry->inode = child_inode_number; - new_entry->rec_len = static_cast<uint16_t>(block_size() - offset); - new_entry->name_len = name_len; - new_entry->file_type = 0x01; // TODO handle mode correctly - kstd::libc::memcpy(new_entry->name.data(), name.data(), name_len); - - return write_block(*global_block_number, buffer.data()).transform([](auto) {}); + write_directory_entry_to_buffer(buffer.data(), offset + last_entry_actual_len, child_inode_number, name, + file_type); + return global_block.and_then([&](auto block_number) { return write_block(block_number, buffer.data()); }) + .transform([](auto) {}); } if (!ext2_parent->append_blocks(1)) @@ -275,19 +286,47 @@ namespace kernel::filesystem::ext2 } kstd::libc::memset(buffer.data(), 0, block_size()); - - auto * entry = reinterpret_cast<linked_directory_entry *>(buffer.data()); - entry->inode = child_inode_number; - entry->rec_len = static_cast<uint16_t>(block_size()); - entry->name_len = name_len; - entry->file_type = 0x01; // TODO handle mode correctly - kstd::libc::memcpy(entry->name.data(), name.data(), name_len); + write_directory_entry_to_buffer(buffer.data(), 0, child_inode_number, name, file_type); return write_block(static_cast<uint32_t>(*new_global_block), buffer.data()).and_then([&](auto) { return write_inode(ext2_parent->number(), ext2_parent->data()); }); } + auto filesystem::init_directory(kstd::shared_ptr<kernel::filesystem::ext2::inode> const & new_inode, + uint32_t parent_inode_number) -> kstd::result<void> + { + if (!new_inode->append_blocks(1)) + { + return kstd::failure(ext2_errc::not_enough_free_blocks); + } + + auto const global_block = map_inode_block_index_to_global_block_number(0, new_inode->data()); + if (!global_block) + { + return kstd::failure(global_block.error()); + } + + kstd::vector<uint8_t> buffer(block_size(), 0); + write_directory_entry_to_buffer(buffer.data(), 0, new_inode->number(), ".", 2); + auto const dot_actual_len = static_cast<uint16_t>((8u + 1 + 3u) & ~3u); + write_directory_entry_to_buffer(buffer.data(), dot_actual_len, parent_inode_number, "..", 2); + return global_block.and_then([&](auto block_number) { return write_block(block_number, buffer.data()); }) + .transform([](auto) {}); + } + + auto filesystem::write_directory_entry_to_buffer(uint8_t * block_buffer, size_t offset, uint32_t child_inode_number, + std::string_view name, uint8_t file_type) -> void + { + auto const name_len = static_cast<uint8_t>(name.size()); + auto * entry = reinterpret_cast<linked_directory_entry *>(block_buffer + offset); + entry->inode = child_inode_number; + entry->rec_len = static_cast<uint16_t>(block_size() - offset); + entry->name_len = name_len; + entry->file_type = file_type; + kstd::libc::memcpy(entry->name.data(), name.data(), name_len); + } + auto filesystem::read_inode(uint32_t inode_number) const -> kstd::result<kstd::shared_ptr<inode>> { if (auto inode_offset = calculate_inode_offset(inode_number)) @@ -671,6 +710,23 @@ namespace kernel::filesystem::ext2 .transform([](auto) {}); } + auto filesystem::map_inode_mode_to_file_type(uint16_t mode) -> uint8_t + { + { + switch (mode & constants::mode_mask) + { + case constants::mode_regular: + return 1; + case constants::mode_directory: + return 2; + case constants::mode_symbolic_link: + return 7; + default: + return 0; + } + } + } + auto filesystem::block_numbers_per_block() const -> size_t { return block_size() / sizeof(uint32_t); diff --git a/kernel/kernel/filesystem/ext2/filesystem.hpp b/kernel/kernel/filesystem/ext2/filesystem.hpp index b3279e09..f45bda97 100644 --- a/kernel/kernel/filesystem/ext2/filesystem.hpp +++ b/kernel/kernel/filesystem/ext2/filesystem.hpp @@ -168,6 +168,10 @@ namespace kernel::filesystem::ext2 auto allocate_inode() -> kstd::result<uint32_t>; auto add_directory_entry(inode * ext2_parent, std::string_view name, uint32_t child_inode_number, uint16_t mode) -> kstd::result<void>; + auto init_directory(kstd::shared_ptr<kernel::filesystem::ext2::inode> const & new_inode, + uint32_t parent_inode_number) -> kstd::result<void>; + auto write_directory_entry_to_buffer(uint8_t * block_buffer, size_t offset, uint32_t child_inode_number, + std::string_view name, uint8_t file_type) -> void; [[nodiscard]] auto calculate_inode_offset(uint32_t inode_number) const -> kstd::result<size_t>; [[nodiscard]] auto read_inode(uint32_t inode_number) const -> kstd::result<kstd::shared_ptr<inode>>; @@ -186,6 +190,8 @@ namespace kernel::filesystem::ext2 size_t block_group_descriptor_index) const -> kstd::result<void>; [[nodiscard]] auto write_superblock() const -> kstd::result<void>; + auto map_inode_mode_to_file_type(uint16_t mode) -> uint8_t; + superblock m_superblock{}; kstd::vector<block_group_descriptor> m_block_group_descriptors; }; diff --git a/kernel/kernel/filesystem/ext2/inode.cpp b/kernel/kernel/filesystem/ext2/inode.cpp index 0c8da9d0..8af168b9 100644 --- a/kernel/kernel/filesystem/ext2/inode.cpp +++ b/kernel/kernel/filesystem/ext2/inode.cpp @@ -176,6 +176,11 @@ namespace kernel::filesystem::ext2 return m_data; } + [[nodiscard]] auto inode::data_mutable() -> inode_data & + { + return m_data; + } + auto inode::is_regular() const -> bool { return (m_data.mode & constants::mode_mask) == constants::mode_regular; diff --git a/kernel/kernel/filesystem/ext2/inode.hpp b/kernel/kernel/filesystem/ext2/inode.hpp index ac17e6ab..61542ef8 100644 --- a/kernel/kernel/filesystem/ext2/inode.hpp +++ b/kernel/kernel/filesystem/ext2/inode.hpp @@ -76,11 +76,17 @@ namespace kernel::filesystem::ext2 /** @brief Get the data associated with this inode. - @return A reference to the inode data. + @return A const reference to the inode data. */ [[nodiscard]] auto data() const -> inode_data const &; /** + @brief Get the data associated with this inode. + @return A reference to the inode data. + */ + [[nodiscard]] auto data_mutable() -> inode_data &; + + /** @brief Check if this inode represents a directory. @return returns true if this inode represents a directory, false otherwise. */ |
