diff --git a/README.md b/README.md index 375add1f..df18a0f0 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,7 @@ You can find a live demo [here](https://bananymous.com/banan-os) #### Filesystems - [x] Virtual filesystem - [x] Ext2 +- [x] FAT12/16/32 - [x] Dev - [x] Ram - [x] Proc diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 85bb4f3b..4ecff95c 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -32,6 +32,8 @@ set(KERNEL_SOURCES kernel/FS/DevFS/FileSystem.cpp kernel/FS/Ext2/FileSystem.cpp kernel/FS/Ext2/Inode.cpp + kernel/FS/FAT/FileSystem.cpp + kernel/FS/FAT/Inode.cpp kernel/FS/FileSystem.cpp kernel/FS/Inode.cpp kernel/FS/Pipe.cpp diff --git a/kernel/include/kernel/FS/FAT/Definitions.h b/kernel/include/kernel/FS/FAT/Definitions.h new file mode 100644 index 00000000..619eb79d --- /dev/null +++ b/kernel/include/kernel/FS/FAT/Definitions.h @@ -0,0 +1,158 @@ +#pragma once + +#include + +namespace Kernel::FAT +{ + + struct ExtBPB_12_16 + { + uint8_t drive_number; + uint8_t __reserved0; + uint8_t boot_signature; + uint32_t volume_id; + uint8_t volume_label[11]; + uint8_t filesystem_type[8]; + uint8_t __reserved1[448]; + } __attribute__((packed)); + static_assert(sizeof(ExtBPB_12_16) == 510 - 36); + + struct ExtBPB_32 + { + uint32_t fat_size32; + uint16_t extended_flags; + uint16_t filesystem_version; + uint32_t root_cluster; + uint16_t filesystem_info; + uint16_t backup_boot_sector; + uint8_t __reserved0[12]; + uint8_t drive_number; + uint8_t __reserved1; + uint8_t boot_signature; + uint32_t volume_id; + uint8_t volume_label[11]; + uint8_t filesystem_type[8]; + uint8_t __reserved2[420]; + } __attribute__((packed)); + static_assert(sizeof(ExtBPB_32) == 510 - 36); + + struct BPB + { + uint8_t jump_op[3]; + uint8_t oem_name[8]; + uint16_t bytes_per_sector; + uint8_t sectors_per_cluster; + uint16_t reserved_sector_count; + uint8_t number_of_fats; + uint16_t root_entry_count; + uint16_t total_sectors16; + uint8_t media_type; + uint16_t fat_size16; + uint16_t sectors_per_track; + uint16_t number_of_heads; + uint32_t hidden_sector_count; + uint32_t total_sectors32; + union + { + ExtBPB_12_16 ext_12_16; + ExtBPB_32 ext_32; + }; + uint16_t Signature_word; + } __attribute__((packed)); + static_assert(sizeof(BPB) == 512); + + struct Date + { + uint16_t day : 5; + uint16_t month : 4; + uint16_t year : 7; + }; + + struct Time + { + uint16_t second : 5; + uint16_t minute : 6; + uint16_t hour : 5; + }; + + struct DirectoryEntry + { + uint8_t name[11]; + uint8_t attr; + uint8_t ntres; + uint8_t creation_time_hundreth; + Time creation_time; + Date creation_date; + Date last_access_date; + uint16_t first_cluster_hi; + Time write_time; + Date write_date; + uint16_t first_cluster_lo; + uint32_t file_size; + + BAN::String name_as_string() const + { + static_assert(BAN::String::sso_capacity >= 8 + 3 + 1); + + BAN::String short_name; + MUST(short_name.append(BAN::StringView((const char*)&name[0], 8))); + while (short_name.back() == ' ') + short_name.pop_back(); + MUST(short_name.push_back('.')); + MUST(short_name.append(BAN::StringView((const char*)&name[8], 3))); + while (short_name.back() == ' ') + short_name.pop_back(); + if (short_name.back() == '.') + short_name.pop_back(); + return short_name; + } + } __attribute__((packed)); + static_assert(sizeof(DirectoryEntry) == 32); + + struct LongNameEntry + { + uint8_t order; + uint16_t name1[5]; + uint8_t attr; + uint8_t type; + uint8_t checksum; + uint16_t name2[6]; + uint16_t first_cluster_lo; + uint16_t name3[2]; + + BAN::String name_as_string() const + { + static_assert(BAN::String::sso_capacity >= 13); + + BAN::String result; + for (uint16_t ch : name1) { + if (ch == 0) + return result; + MUST(result.push_back(ch)); + } + for (uint16_t ch : name2) { + if (ch == 0) + return result; + MUST(result.push_back(ch)); + } + for (uint16_t ch : name3) { + if (ch == 0) + return result; + MUST(result.push_back(ch)); + } + return result; + } + } __attribute__((packed)); + static_assert(sizeof(LongNameEntry) == 32); + + enum FileAttr : uint8_t + { + READ_ONLY = 0x01, + HIDDEN = 0x02, + SYSTEM = 0x04, + VOLUME_ID = 0x08, + DIRECTORY = 0x10, + ARCHIVE = 0x20, + }; + +} diff --git a/kernel/include/kernel/FS/FAT/FileSystem.h b/kernel/include/kernel/FS/FAT/FileSystem.h new file mode 100644 index 00000000..7003b6c9 --- /dev/null +++ b/kernel/include/kernel/FS/FAT/FileSystem.h @@ -0,0 +1,72 @@ +#pragma once + +#include + +#include +#include +#include + +namespace Kernel +{ + + class FATFS final : public FileSystem + { + public: + enum class Type + { + FAT12 = 12, + FAT16 = 16, + FAT32 = 32, + }; + + public: + static BAN::ErrorOr probe(BAN::RefPtr); + static BAN::ErrorOr> create(BAN::RefPtr); + + virtual BAN::RefPtr root_inode() override { return m_root_inode; } + + virtual dev_t dev() const override { return m_block_device->rdev(); }; + + BAN::ErrorOr> open_inode(BAN::RefPtr parent, const FAT::DirectoryEntry& entry, uint32_t cluster_index, uint32_t entry_index); + BAN::ErrorOr inode_read_cluster(BAN::RefPtr, size_t index, BAN::ByteSpan buffer); + blksize_t inode_block_size(BAN::RefPtr) const; + + private: + FATFS(BAN::RefPtr block_device, FAT::BPB bpb) + : m_block_device(block_device) + , m_bpb(bpb) + , m_type((cluster_count() < 4085) ? Type::FAT12 : (cluster_count() < 65525) ? Type::FAT16 : Type::FAT32) + {} + static bool validate_bpb(const FAT::BPB&); + BAN::ErrorOr initialize(); + + BAN::ErrorOr fat_cache_set_sector(uint32_t sector); + BAN::ErrorOr get_next_cluster(uint32_t cluster); + + // TODO: These probably should be constant variables + uint32_t root_sector_count() const { return BAN::Math::div_round_up(m_bpb.root_entry_count * 32, m_bpb.bytes_per_sector); } + uint32_t fat_sector_count() const { return m_bpb.fat_size16 ? m_bpb.fat_size16 : m_bpb.ext_32.fat_size32; } + uint32_t total_sector_count() const { return m_bpb.total_sectors16 ? m_bpb.total_sectors16 : m_bpb.total_sectors32; } + uint32_t data_sector_count() const { return total_sector_count() - (m_bpb.reserved_sector_count + (m_bpb.number_of_fats * fat_sector_count()) + root_sector_count()); } + uint32_t cluster_count() const { return data_sector_count() / m_bpb.sectors_per_cluster; } + uint32_t first_data_sector() const { return m_bpb.reserved_sector_count + (m_bpb.number_of_fats * fat_sector_count()) + root_sector_count(); } + uint32_t first_fat_sector() const { return m_bpb.reserved_sector_count; } + + private: + const FAT::BPB m_bpb; + const Type m_type; + + BAN::RefPtr m_block_device; + BAN::RefPtr m_root_inode; + + BAN::HashMap> m_inode_cache; + + BAN::Vector m_fat_two_sector_buffer; + uint32_t m_fat_sector_buffer_current { 0 }; + + Mutex m_mutex; + + friend class BAN::RefPtr; + }; + +} diff --git a/kernel/include/kernel/FS/FAT/Inode.h b/kernel/include/kernel/FS/FAT/Inode.h new file mode 100644 index 00000000..a1fbb94e --- /dev/null +++ b/kernel/include/kernel/FS/FAT/Inode.h @@ -0,0 +1,72 @@ +#pragma once + +#include +#include +#include + +#include +#include + +namespace Kernel +{ + + class FATFS; + + class FATInode final : public Inode, public BAN::Weakable + { + public: + virtual ino_t ino() const override { return m_ino; }; + virtual Mode mode() const override { return Mode { ((m_entry.attr & FAT::FileAttr::DIRECTORY) ? Mode::IFDIR : Mode::IFREG) | 0777 }; } + virtual nlink_t nlink() const override { return 1; } + virtual uid_t uid() const override { return 0; } + virtual gid_t gid() const override { return 0; } + virtual off_t size() const override { return m_entry.file_size; } + virtual timespec atime() const override; + virtual timespec mtime() const override; + virtual timespec ctime() const override; + virtual blksize_t blksize() const override; + virtual blkcnt_t blocks() const override { return m_block_count; } + virtual dev_t dev() const override { return 0; } + virtual dev_t rdev() const override { return 0; } + + const FAT::DirectoryEntry& entry() const { return m_entry; } + + protected: + virtual BAN::ErrorOr> find_inode_impl(BAN::StringView) override; + virtual BAN::ErrorOr list_next_inodes_impl(off_t, struct dirent*, size_t) override; + //virtual BAN::ErrorOr create_file_impl(BAN::StringView, mode_t, uid_t, gid_t) override; + //virtual BAN::ErrorOr create_directory_impl(BAN::StringView, mode_t, uid_t, gid_t) override; + //virtual BAN::ErrorOr unlink_impl(BAN::StringView) override; + + virtual BAN::ErrorOr read_impl(off_t, BAN::ByteSpan) override; + //virtual BAN::ErrorOr write_impl(off_t, BAN::ConstByteSpan) override; + //virtual BAN::ErrorOr truncate_impl(size_t) override; + //virtual BAN::ErrorOr chmod_impl(mode_t) override; + + virtual bool can_read_impl() const override { return true; } + virtual bool can_write_impl() const override { return true; } + virtual bool has_error_impl() const override { return false; } + + private: + FATInode(FATFS& fs, const FAT::DirectoryEntry& entry, ino_t ino, uint32_t block_count) + : m_fs(fs) + , m_entry(entry) + , m_ino(ino) + , m_block_count(block_count) + { } + ~FATInode() {} + + BAN::ErrorOr for_each_directory_entry(BAN::ConstByteSpan, BAN::Function); + BAN::ErrorOr for_each_directory_entry(BAN::ConstByteSpan, BAN::Function); + + private: + FATFS& m_fs; + FAT::DirectoryEntry m_entry; + const ino_t m_ino; + uint32_t m_block_count; + + friend class Ext2FS; + friend class BAN::RefPtr; + }; + +} diff --git a/kernel/kernel/FS/FAT/FileSystem.cpp b/kernel/kernel/FS/FAT/FileSystem.cpp new file mode 100644 index 00000000..63391aee --- /dev/null +++ b/kernel/kernel/FS/FAT/FileSystem.cpp @@ -0,0 +1,276 @@ +#include +#include + +#include + +namespace Kernel +{ + + bool FATFS::validate_bpb(const FAT::BPB& bpb) + { + bool valid_jump_op = (bpb.jump_op[0] == 0xEB && bpb.jump_op[2] == 0x90) || (bpb.jump_op[0] == 0xE9); + if (!valid_jump_op) + return false; + + // This is techincally a strict requirement + for (char c : bpb.oem_name) + if (!isprint(c)) + return false; + + if (!BAN::Math::is_power_of_two(bpb.bytes_per_sector) || bpb.bytes_per_sector < 512 || bpb.bytes_per_sector > 4096) + return false; + + if (!BAN::Math::is_power_of_two(bpb.sectors_per_cluster) || bpb.sectors_per_cluster > 128) + return false; + + if (bpb.reserved_sector_count == 0) + return false; + + if (bpb.number_of_fats == 0) + return false; + + switch (bpb.media_type) + { + case 0xF0: + case 0xF8: + case 0xF9: + case 0xFA: + case 0xFB: + case 0xFC: + case 0xFD: + case 0xFE: + case 0xFF: + break; + default: + return false; + } + + // FIXME: There is more possible checks to do + + return true; + } + + BAN::ErrorOr FATFS::probe(BAN::RefPtr block_device) + { + // support only block devices with sectors at least 512 bytes + if (block_device->blksize() < 512) + return false; + + BAN::Vector bpb_buffer; + TRY(bpb_buffer.resize(block_device->blksize())); + auto bpb_span = BAN::ByteSpan(bpb_buffer.span()); + TRY(block_device->read_blocks(0, 1, bpb_span)); + + return validate_bpb(bpb_span.as()); + } + + BAN::ErrorOr> FATFS::create(BAN::RefPtr block_device) + { + // support only block devices with sectors at least 512 bytes + if (block_device->blksize() < 512) + return BAN::Error::from_errno(EINVAL); + + BAN::Vector bpb_buffer; + TRY(bpb_buffer.resize(block_device->blksize())); + auto bpb_span = BAN::ByteSpan(bpb_buffer.span()); + TRY(block_device->read_blocks(0, 1, bpb_span)); + + const auto& bpb = bpb_span.as(); + if (!validate_bpb(bpb)) + return BAN::Error::from_errno(EINVAL); + + auto fatfs = TRY(BAN::RefPtr::create( + block_device, + bpb + )); + TRY(fatfs->initialize()); + return fatfs; + } + + BAN::ErrorOr FATFS::initialize() + { + if (m_bpb.bytes_per_sector != m_block_device->blksize()) + { + dwarnln("FileSystem sector size does not match with block device"); + return BAN::Error::from_errno(ENOTSUP); + } + + TRY(m_fat_two_sector_buffer.resize(m_bpb.bytes_per_sector * 2)); + TRY(m_block_device->read_blocks(first_fat_sector(), 2, BAN::ByteSpan(m_fat_two_sector_buffer.span()))); + m_fat_sector_buffer_current = 0; + + FAT::DirectoryEntry root_entry {}; + root_entry.attr = FAT::FileAttr::DIRECTORY; + m_root_inode = TRY(open_inode(nullptr, root_entry, 0, 0)); + + return {}; + } + + BAN::ErrorOr> FATFS::open_inode(BAN::RefPtr parent, const FAT::DirectoryEntry& entry, uint32_t cluster_index, uint32_t entry_index) + { + LockGuard _(m_mutex); + + uint32_t block_count = 0; + { + uint32_t cluster = entry.first_cluster_lo; + if (m_type == Type::FAT32) + cluster |= static_cast(entry.first_cluster_hi) << 16; + while (cluster >= 2 && cluster < cluster_count()) + { + block_count++; + cluster = TRY(get_next_cluster(cluster)); + } + } + + uint32_t entry_cluster; + switch (m_type) + { + case Type::FAT12: + case Type::FAT16: + if (parent == m_root_inode) + entry_cluster = 1; + else + { + entry_cluster = parent->entry().first_cluster_lo; + for (uint32_t i = 0; i < cluster_index; i++) + entry_cluster = TRY(get_next_cluster(entry_cluster)); + } + break; + case Type::FAT32: + if (parent == m_root_inode) + entry_cluster = m_bpb.ext_32.root_cluster; + else + entry_cluster = (static_cast(parent->entry().first_cluster_hi) << 16) | parent->entry().first_cluster_lo; + for (uint32_t i = 0; i < cluster_index; i++) + entry_cluster = TRY(get_next_cluster(entry_cluster)); + break; + default: + ASSERT_NOT_REACHED(); + } + + const ino_t ino = (static_cast(entry_cluster) << 32) | entry_index; + auto it = m_inode_cache.find(ino); + if (it != m_inode_cache.end()) + { + if (auto inode = it->value.lock()) + return inode; + m_inode_cache.remove(it); + } + + auto inode = TRY(BAN::RefPtr::create(*this, entry, ino, block_count)); + TRY(m_inode_cache.insert(ino, TRY(inode->get_weak_ptr()))); + return inode; + } + + BAN::ErrorOr FATFS::fat_cache_set_sector(uint32_t sector) + { + if (m_fat_sector_buffer_current != sector) + { + TRY(m_block_device->read_blocks(first_fat_sector() + sector, 2, BAN::ByteSpan(m_fat_two_sector_buffer.span()))); + m_fat_sector_buffer_current = sector; + } + return {}; + } + + BAN::ErrorOr FATFS::get_next_cluster(uint32_t cluster) + { + LockGuard _(m_mutex); + + ASSERT(cluster >= 2 && cluster < cluster_count()); + + auto fat_span = BAN::ByteSpan(m_fat_two_sector_buffer.span()); + + switch (m_type) + { + case Type::FAT12: + { + const uint32_t fat_byte_offset = cluster + (cluster / 2); + const uint32_t ent_offset = fat_byte_offset % m_bpb.bytes_per_sector; + TRY(fat_cache_set_sector(fat_byte_offset / m_bpb.bytes_per_sector)); + uint16_t next = (fat_span[ent_offset + 1] << 8) | fat_span[ent_offset]; + return cluster % 2 ? next >> 4 : next & 0xFFF; + } + case Type::FAT16: + { + const uint32_t fat_byte_offset = cluster * sizeof(uint16_t); + const uint32_t ent_offset = (fat_byte_offset % m_bpb.bytes_per_sector) / sizeof(uint16_t); + TRY(fat_cache_set_sector(fat_byte_offset / m_bpb.bytes_per_sector)); + return fat_span.as_span()[ent_offset]; + } + case Type::FAT32: + { + const uint32_t fat_byte_offset = cluster * sizeof(uint32_t); + const uint32_t ent_offset = (fat_byte_offset % m_bpb.bytes_per_sector) / sizeof(uint32_t); + TRY(fat_cache_set_sector(fat_byte_offset / m_bpb.bytes_per_sector)); + return fat_span.as_span()[ent_offset]; + } + } + + ASSERT_NOT_REACHED(); + } + + BAN::ErrorOr FATFS::inode_read_cluster(BAN::RefPtr file, size_t index, BAN::ByteSpan buffer) + { + LockGuard _(m_mutex); + + if (buffer.size() < static_castblksize())>>(file->blksize())) + return BAN::Error::from_errno(ENOBUFS); + + uint32_t cluster; + switch (m_type) + { + case Type::FAT12: + case Type::FAT16: + { + if (file == m_root_inode) + { + if (index >= root_sector_count()) + return BAN::Error::from_errno(ENOENT); + const uint32_t first_root_sector = m_bpb.reserved_sector_count + (m_bpb.number_of_fats * fat_sector_count()); + TRY(m_block_device->read_blocks(first_root_sector + index, 1, buffer)); + return {}; + } + cluster = file->entry().first_cluster_lo; + break; + } + case Type::FAT32: + if (file == m_root_inode) + cluster = m_bpb.ext_32.root_cluster; + else + cluster = (static_cast(file->entry().first_cluster_hi) << 16) | file->entry().first_cluster_lo; + break; + default: + ASSERT_NOT_REACHED(); + } + + if (cluster < 2 || cluster >= cluster_count()) + return BAN::Error::from_errno(ENOENT); + + for (uint32_t i = 0; i < index; i++) + { + cluster = TRY(get_next_cluster(cluster)); + if (cluster < 2 || cluster >= cluster_count()) + return BAN::Error::from_errno(ENOENT); + } + + const uint32_t cluster_start_sector = ((cluster - 2) * m_bpb.sectors_per_cluster) + first_data_sector(); + TRY(m_block_device->read_blocks(cluster_start_sector, m_bpb.sectors_per_cluster, buffer)); + return {}; + } + + blksize_t FATFS::inode_block_size(BAN::RefPtr file) const + { + switch (m_type) + { + case Type::FAT12: + case Type::FAT16: + if (file == m_root_inode) + return m_bpb.bytes_per_sector; + return m_bpb.bytes_per_sector * m_bpb.sectors_per_cluster; + case Type::FAT32: + return m_bpb.bytes_per_sector * m_bpb.sectors_per_cluster; + } + ASSERT_NOT_REACHED(); + } + +} diff --git a/kernel/kernel/FS/FAT/Inode.cpp b/kernel/kernel/FS/FAT/Inode.cpp new file mode 100644 index 00000000..b409592e --- /dev/null +++ b/kernel/kernel/FS/FAT/Inode.cpp @@ -0,0 +1,280 @@ +#include + +#include +#include + +#include + +namespace Kernel +{ + + static uint64_t fat_date_to_epoch(FAT::Date date, FAT::Time time) + { + BAN::Time ban_time {}; + + ban_time.year = (date.year % 128) + 1980; + ban_time.month = ((date.month - 1) % 12) + 1; + ban_time.day = ((date.day - 1) % 31) + 1; + + ban_time.hour = (time.hour % 24); + ban_time.minute = (time.minute % 60); + ban_time.second = (time.second % 30) * 2; + + return BAN::to_unix_time(ban_time); + } + + blksize_t FATInode::blksize() const + { + return m_fs.inode_block_size(this); + } + + timespec FATInode::atime() const + { + uint64_t epoch = fat_date_to_epoch(m_entry.last_access_date, {}); + return timespec { .tv_sec = epoch, .tv_nsec = 0 }; + } + + timespec FATInode::mtime() const + { + uint64_t epoch = fat_date_to_epoch(m_entry.write_date, m_entry.write_time); + return timespec { .tv_sec = epoch, .tv_nsec = 0 }; + } + + timespec FATInode::ctime() const + { + uint64_t epoch = fat_date_to_epoch(m_entry.creation_date, m_entry.creation_time); + return timespec { .tv_sec = epoch, .tv_nsec = 0 }; + } + + BAN::ErrorOr FATInode::for_each_directory_entry(BAN::ConstByteSpan entry_span, BAN::Function callback) + { + ASSERT(mode().ifdir()); + + auto directory_entries = entry_span.as_span(); + for (uint32_t i = 0; i < directory_entries.size(); i++) + { + const auto& directory_entry = directory_entries[i]; + if ((uint8_t)directory_entry.name[0] == 0xE5) + continue; + if (directory_entry.name[0] == 0) + break; + if ((directory_entry.attr & 0x3F) == 0x0F) + continue; + if (callback(directory_entry) == BAN::Iteration::Break) + return {}; + } + + return {}; + } + + BAN::ErrorOr FATInode::for_each_directory_entry(BAN::ConstByteSpan entry_span, BAN::Function callback) + { + ASSERT(mode().ifdir()); + + BAN::String long_name; + + auto directory_entries = entry_span.as_span(); + auto long_name_entries = entry_span.as_span(); + for (uint32_t i = 0; i < directory_entries.size(); i++) + { + const auto& directory_entry = directory_entries[i]; + if ((uint8_t)directory_entry.name[0] == 0xE5) + continue; + if (directory_entry.name[0] == 0) + break; + + if ((directory_entry.attr & 0x3F) == 0x0F) + { + if (!long_name.empty()) + { + dwarnln("Invalid long name entry"); + continue; + } + + const auto& long_name_entry = long_name_entries[i]; + if (!(long_name_entry.order & 0x40)) + { + dwarnln("Invalid long name entry"); + continue; + } + + const uint32_t long_name_entry_count = long_name_entry.order & ~0x40; + if (i + long_name_entry_count >= directory_entries.size()) + { + dwarnln("Invalid long name entry"); + continue; + } + + for (uint32_t j = 0; j < long_name_entry_count; j++) + TRY(long_name.insert(long_name_entries[i + j].name_as_string(), 0)); + i += long_name_entry_count - 1; + continue; + } + + auto ret = callback(directory_entry, BAN::move(long_name), i); + if (ret == BAN::Iteration::Break) + return {}; + long_name.clear(); + } + + return {}; + } + + BAN::ErrorOr> FATInode::find_inode_impl(BAN::StringView name) + { + ASSERT(mode().ifdir()); + + BAN::Vector cluster_buffer; + TRY(cluster_buffer.resize(blksize())); + auto cluster_span = BAN::ByteSpan(cluster_buffer.span()); + + for (uint32_t cluster_index = 0;; cluster_index++) + { + // Returns ENOENT if this cluster is out of bounds + TRY(m_fs.inode_read_cluster(this, cluster_index, cluster_span)); + + auto error = BAN::Error::from_errno(0); + BAN::RefPtr result; + + TRY(for_each_directory_entry(cluster_span, + [&](const FAT::DirectoryEntry& entry, BAN::String long_name, uint32_t entry_index) + { + BAN::String file_name = long_name.empty() ? entry.name_as_string() : BAN::move(long_name); + + if (file_name.size() != name.size()) + return BAN::Iteration::Continue; + + for (size_t i = 0; i < name.size(); i++) + if (tolower(name[i]) != tolower(file_name[i])) + return BAN::Iteration::Continue; + + auto new_inode = m_fs.open_inode(this, entry, cluster_index, entry_index); + if (new_inode.is_error()) + error = new_inode.release_error(); + else + result = new_inode.release_value(); + return BAN::Iteration::Break; + } + )); + + if (error.get_error_code()) + return error; + if (result) + return BAN::RefPtr(result); + } + + return BAN::Error::from_errno(ENOENT); + } + + BAN::ErrorOr FATInode::list_next_inodes_impl(off_t offset, struct dirent* list, size_t list_size) + { + ASSERT(mode().ifdir()); + ASSERT(offset >= 0); + + BAN::Vector cluster_buffer; + TRY(cluster_buffer.resize(blksize())); + auto cluster_span = BAN::ByteSpan(cluster_buffer.span()); + + { + auto maybe_error = m_fs.inode_read_cluster(this, offset, cluster_span); + if (maybe_error.is_error()) + { + if (maybe_error.error().get_error_code() == ENOENT) + return 0; + return maybe_error.release_error(); + } + } + + size_t valid_entry_count = 0; + TRY(for_each_directory_entry(cluster_span, + [&](const FAT::DirectoryEntry&) + { + valid_entry_count++; + return BAN::Iteration::Continue; + } + )); + + if (valid_entry_count > list_size) + return BAN::Error::from_errno(ENOBUFS); + + TRY(for_each_directory_entry(cluster_span, + [&](const FAT::DirectoryEntry& entry, const BAN::String& long_name, uint32_t) + { + BAN::String name = long_name.empty() ? entry.name_as_string() : BAN::move(long_name); + list->d_ino = 0; + list->d_type = (entry.attr & FAT::FileAttr::DIRECTORY) ? DT_DIR : DT_REG; + strncpy(list->d_name, name.data(), sizeof(list->d_name)); + list++; + return BAN::Iteration::Continue; + } + )); + + return valid_entry_count; + } + + BAN::ErrorOr FATInode::read_impl(off_t offset, BAN::ByteSpan buffer) + { + ASSERT(offset >= 0); + + if (offset >= m_entry.file_size) + return 0; + if (offset + buffer.size() > m_entry.file_size) + buffer = buffer.slice(0, m_entry.file_size - offset); + + BAN::Vector cluster_buffer; + TRY(cluster_buffer.resize(blksize())); + auto cluster_span = BAN::ByteSpan(cluster_buffer.span()); + + const uint32_t block_size = blksize(); + + size_t nread = 0; + if (auto rem = offset % block_size) + { + const uint32_t to_read = BAN::Math::min(buffer.size(), block_size - rem); + + if (auto ret = m_fs.inode_read_cluster(this, offset / block_size, cluster_span); ret.is_error()) + { + if (ret.error().get_error_code() == ENOENT) + return nread; + return ret.release_error(); + } + memcpy(buffer.data(), cluster_span.data() + rem, to_read); + + nread += to_read; + offset += to_read; + buffer = buffer.slice(to_read); + } + + while (buffer.size() >= block_size) + { + if (auto ret = m_fs.inode_read_cluster(this, offset / block_size, buffer); ret.is_error()) + { + if (ret.error().get_error_code() == ENOENT) + return nread; + return ret.release_error(); + } + + nread += block_size; + offset += block_size; + buffer = buffer.slice(block_size); + } + + if (buffer.size() > 0) + { + if (auto ret = m_fs.inode_read_cluster(this, offset / block_size, cluster_span); ret.is_error()) + { + if (ret.error().get_error_code() == ENOENT) + return nread; + return ret.release_error(); + } + memcpy(buffer.data(), cluster_span.data(), buffer.size()); + + nread += buffer.size(); + offset += buffer.size(); + buffer = buffer.slice(buffer.size()); + } + + return nread; + } + +} diff --git a/kernel/kernel/FS/FileSystem.cpp b/kernel/kernel/FS/FileSystem.cpp index 2c6967c8..3f73d8bd 100644 --- a/kernel/kernel/FS/FileSystem.cpp +++ b/kernel/kernel/FS/FileSystem.cpp @@ -1,4 +1,5 @@ #include +#include #include namespace Kernel @@ -8,6 +9,8 @@ namespace Kernel { if (auto res = Ext2FS::probe(block_device); !res.is_error() && res.value()) return BAN::RefPtr(TRY(Ext2FS::create(block_device))); + if (auto res = FATFS::probe(block_device); !res.is_error() && res.value()) + return BAN::RefPtr(TRY(FATFS::create(block_device))); dprintln("Unsupported filesystem"); return BAN::Error::from_errno(ENOTSUP); }