diff --git a/kernel/include/kernel/API/DirectoryEntry.h b/kernel/include/kernel/API/DirectoryEntry.h new file mode 100644 index 000000000..98a8f3160 --- /dev/null +++ b/kernel/include/kernel/API/DirectoryEntry.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +namespace Kernel::API +{ + + struct DirectoryEntry + { + size_t rec_len { 0 }; + struct dirent dirent; + DirectoryEntry* next() const { return (DirectoryEntry*)((uintptr_t)this + rec_len); } + }; + + struct DirectoryEntryList + { + size_t entry_count { 0 }; + DirectoryEntry array[]; + }; + +} \ No newline at end of file diff --git a/kernel/include/kernel/DeviceManager.h b/kernel/include/kernel/DeviceManager.h index aab63cab8..fa6dacd62 100644 --- a/kernel/include/kernel/DeviceManager.h +++ b/kernel/include/kernel/DeviceManager.h @@ -34,7 +34,7 @@ namespace Kernel virtual BAN::StringView name() const override { return "device-manager"; } virtual BAN::ErrorOr> read_directory_inode(BAN::StringView) override; - virtual BAN::ErrorOr> read_directory_entries(size_t) override; + virtual BAN::ErrorOr read_next_directory_entries(off_t, DirectoryEntryList*, size_t) override; private: DeviceManager() = default; diff --git a/kernel/include/kernel/FS/Ext2.h b/kernel/include/kernel/FS/Ext2.h index 29ab3fb8c..fb76cd46a 100644 --- a/kernel/include/kernel/FS/Ext2.h +++ b/kernel/include/kernel/FS/Ext2.h @@ -141,7 +141,7 @@ namespace Kernel virtual BAN::ErrorOr link_target() override; - virtual BAN::ErrorOr> read_directory_entries(size_t) override; + virtual BAN::ErrorOr read_next_directory_entries(off_t, DirectoryEntryList*, size_t) override; virtual BAN::ErrorOr> read_directory_inode(BAN::StringView) override; virtual BAN::ErrorOr read(size_t, void*, size_t) override; diff --git a/kernel/include/kernel/FS/Inode.h b/kernel/include/kernel/FS/Inode.h index 1573e3854..6ce30b75f 100644 --- a/kernel/include/kernel/FS/Inode.h +++ b/kernel/include/kernel/FS/Inode.h @@ -5,12 +5,16 @@ #include #include +#include + #include #include namespace Kernel { + using namespace API; + class Inode : public BAN::RefCounted { public: @@ -74,13 +78,13 @@ namespace Kernel virtual BAN::ErrorOr link_target() { ASSERT_NOT_REACHED(); } - virtual BAN::ErrorOr> read_directory_inode(BAN::StringView) { if (!mode().ifdir()) return BAN::Error::from_errno(ENOTDIR); ASSERT_NOT_REACHED(); } - virtual BAN::ErrorOr> read_directory_entries(size_t) { if (!mode().ifdir()) return BAN::Error::from_errno(ENOTDIR); ASSERT_NOT_REACHED(); } + virtual BAN::ErrorOr> read_directory_inode(BAN::StringView) { if (!mode().ifdir()) return BAN::Error::from_errno(ENOTDIR); ASSERT_NOT_REACHED(); } + virtual BAN::ErrorOr read_next_directory_entries(off_t, DirectoryEntryList*, size_t) { if (!mode().ifdir()) return BAN::Error::from_errno(ENOTDIR); ASSERT_NOT_REACHED(); } - virtual BAN::ErrorOr read(size_t, void*, size_t) { if (mode().ifdir()) return BAN::Error::from_errno(EISDIR); ASSERT_NOT_REACHED(); } - virtual BAN::ErrorOr write(size_t, const void*, size_t) { if (mode().ifdir()) return BAN::Error::from_errno(EISDIR); ASSERT_NOT_REACHED(); } + virtual BAN::ErrorOr read(size_t, void*, size_t) { if (mode().ifdir()) return BAN::Error::from_errno(EISDIR); ASSERT_NOT_REACHED(); } + virtual BAN::ErrorOr write(size_t, const void*, size_t) { if (mode().ifdir()) return BAN::Error::from_errno(EISDIR); ASSERT_NOT_REACHED(); } - virtual BAN::ErrorOr create_file(BAN::StringView, mode_t) { if (!mode().ifdir()) return BAN::Error::from_errno(ENOTDIR); ASSERT_NOT_REACHED(); } + virtual BAN::ErrorOr create_file(BAN::StringView, mode_t) { if (!mode().ifdir()) return BAN::Error::from_errno(ENOTDIR); ASSERT_NOT_REACHED(); } }; } \ No newline at end of file diff --git a/kernel/include/kernel/Process.h b/kernel/include/kernel/Process.h index 6dc28e7bb..88e731045 100644 --- a/kernel/include/kernel/Process.h +++ b/kernel/include/kernel/Process.h @@ -71,7 +71,7 @@ namespace Kernel BAN::ErrorOr mount(BAN::StringView source, BAN::StringView target); - BAN::ErrorOr> read_directory_entries(int); + BAN::ErrorOr read_next_directory_entries(int fd, DirectoryEntryList* buffer, size_t buffer_size); BAN::ErrorOr working_directory() const; BAN::ErrorOr set_working_directory(BAN::StringView); diff --git a/kernel/kernel/DeviceManager.cpp b/kernel/kernel/DeviceManager.cpp index fb4f55886..930ea7013 100644 --- a/kernel/kernel/DeviceManager.cpp +++ b/kernel/kernel/DeviceManager.cpp @@ -114,19 +114,49 @@ namespace Kernel return BAN::Error::from_errno(ENOENT); } - BAN::ErrorOr> DeviceManager::read_directory_entries(size_t index) + BAN::ErrorOr DeviceManager::read_next_directory_entries(off_t offset, DirectoryEntryList* list, size_t list_size) { - BAN::Vector result; - if (index > 0) - return result; + if (offset != 0) + { + list->entry_count = 0; + return {}; + } LockGuard _(m_lock); - TRY(result.reserve(m_devices.size() + 2)); - MUST(result.emplace_back("."sv)); - MUST(result.emplace_back(".."sv)); + + size_t needed_size = sizeof(DirectoryEntryList); + needed_size += sizeof(DirectoryEntry) + 2; + needed_size += sizeof(DirectoryEntry) + 3; for (Device* device : m_devices) - MUST(result.emplace_back(device->name())); - return result; + needed_size += sizeof(DirectoryEntry) + device->name().size() + 1; + if (needed_size > list_size) + return BAN::Error::from_errno(EINVAL); + + DirectoryEntry* ptr = list->array; + + ptr->dirent.d_ino = ino(); + ptr->rec_len = sizeof(DirectoryEntry) + 2; + memcpy(ptr->dirent.d_name, ".", 2); + ptr = ptr->next(); + + ptr->dirent.d_ino = 69; // FIXME: store parent inode number when we implement proper RAM fs + ptr->rec_len = sizeof(DirectoryEntry) + 3; + memcpy(ptr->dirent.d_name, "..", 3); + ptr = ptr->next(); + + for (Device* device : m_devices) + { + ptr->dirent.d_ino = device->ino(); + ptr->rec_len = sizeof(DirectoryEntry) + device->name().size() + 1; + memcpy(ptr->dirent.d_name, device->name().data(), device->name().size()); + ptr->dirent.d_name[device->name().size()] = '\0'; + + ptr = ptr->next(); + } + + list->entry_count = m_devices.size() + 2; + + return {}; } diff --git a/kernel/kernel/FS/Ext2.cpp b/kernel/kernel/FS/Ext2.cpp index 0fb86e073..43768f145 100644 --- a/kernel/kernel/FS/Ext2.cpp +++ b/kernel/kernel/FS/Ext2.cpp @@ -291,36 +291,69 @@ namespace Kernel return n_read; } - BAN::ErrorOr> Ext2Inode::read_directory_entries(size_t index) + BAN::ErrorOr Ext2Inode::read_next_directory_entries(off_t offset, DirectoryEntryList* list, size_t list_size) { if (!mode().ifdir()) return BAN::Error::from_errno(ENOTDIR); uint32_t data_block_count = blocks(); - if (index >= data_block_count) - return BAN::Vector(); + if (offset >= data_block_count) + { + list->entry_count = 0; + return {}; + } uint32_t block_size = blksize(); - uint32_t block_index = TRY(data_block_index(index)); + uint32_t block_index = TRY(data_block_index(offset)); BAN::Vector block_buffer; TRY(block_buffer.resize(block_size)); m_fs.read_block(block_index, block_buffer.span()); - BAN::Vector entries; - - const uint8_t* block_buffer_end = block_buffer.data() + block_size; - const uint8_t* entry_addr = block_buffer.data(); - while (entry_addr < block_buffer_end) + // First determine if we have big enough list { - auto& entry = *(Ext2::LinkedDirectoryEntry*)entry_addr; - if (entry.inode) - TRY(entries.emplace_back(BAN::StringView(entry.name, entry.name_len))); - entry_addr += entry.rec_len; + const uint8_t* block_buffer_end = block_buffer.data() + block_size; + const uint8_t* entry_addr = block_buffer.data(); + + size_t needed_size = sizeof(DirectoryEntryList); + while (entry_addr < block_buffer_end) + { + auto& entry = *(Ext2::LinkedDirectoryEntry*)entry_addr; + if (entry.inode) + needed_size += sizeof(DirectoryEntry) + entry.name_len + 1; + entry_addr += entry.rec_len; + } + + if (needed_size > list_size) + return BAN::Error::from_errno(EINVAL); } - return entries; + // Second fill the list + { + DirectoryEntry* ptr = list->array; + list->entry_count = 0; + + const uint8_t* block_buffer_end = block_buffer.data() + block_size; + const uint8_t* entry_addr = block_buffer.data(); + while (entry_addr < block_buffer_end) + { + auto& entry = *(Ext2::LinkedDirectoryEntry*)entry_addr; + if (entry.inode) + { + ptr->dirent.d_ino = entry.inode; + ptr->rec_len = sizeof(DirectoryEntry) + entry.name_len + 1; + memcpy(ptr->dirent.d_name, entry.name, entry.name_len); + ptr->dirent.d_name[entry.name_len] = '\0'; + + ptr = ptr->next(); + list->entry_count++; + } + entry_addr += entry.rec_len; + } + } + + return {}; } BAN::ErrorOr Ext2Inode::create_file(BAN::StringView name, mode_t mode) diff --git a/kernel/kernel/Process.cpp b/kernel/kernel/Process.cpp index 4ad9465ee..167859b29 100644 --- a/kernel/kernel/Process.cpp +++ b/kernel/kernel/Process.cpp @@ -629,8 +629,7 @@ namespace Kernel return ret; } - // FIXME: This whole API has to be rewritten - BAN::ErrorOr> Process::read_directory_entries(int fd) + BAN::ErrorOr Process::read_next_directory_entries(int fd, DirectoryEntryList* list, size_t list_size) { OpenFileDescription open_fd_copy; @@ -640,7 +639,7 @@ namespace Kernel open_fd_copy = open_file_description(fd); } - auto result = TRY(open_fd_copy.inode->read_directory_entries(open_fd_copy.offset)); + TRY(open_fd_copy.inode->read_next_directory_entries(open_fd_copy.offset, list, list_size)); { LockGuard _(m_lock); @@ -648,7 +647,7 @@ namespace Kernel open_file_description(fd).offset = open_fd_copy.offset + 1; } - return result; + return {}; } BAN::ErrorOr Process::working_directory() const diff --git a/kernel/kernel/Shell.cpp b/kernel/kernel/Shell.cpp index 4b83611f4..3c2caee67 100644 --- a/kernel/kernel/Shell.cpp +++ b/kernel/kernel/Shell.cpp @@ -516,12 +516,20 @@ argument_done: BAN::Vector all_entries; - BAN::Vector entries; - while (!(entries = TRY(Process::current().read_directory_entries(fd))).empty()) + DirectoryEntryList* directory_list = (DirectoryEntryList*)kmalloc(4096); + if (directory_list == nullptr) + return BAN::Error::from_errno(ENOMEM); + while (true) { - TRY(all_entries.reserve(all_entries.size() + entries.size())); - for (auto& entry : entries) - TRY(all_entries.push_back(entry)); + TRY(Process::current().read_next_directory_entries(fd, directory_list, 4096)); + if (directory_list->entry_count == 0) + break; + DirectoryEntry* current = directory_list->array; + for (size_t i = 0; i < directory_list->entry_count; i++) + { + TRY(all_entries.emplace_back(current->dirent.d_name)); + current = current->next(); + } } BAN::String entry_prefix;