From 1f70a728f032f7ad3309a1f3333c530cb5f15af4 Mon Sep 17 00:00:00 2001 From: Taj Morton Date: Sat, 29 Jul 2023 20:35:00 -0700 Subject: [PATCH] Kernel/FileSystem/FATFS: Support FAT16 file system clusters --- Kernel/FileSystem/FATFS/Inode.cpp | 84 ++++++++++++++++++++++++++++--- Kernel/FileSystem/FATFS/Inode.h | 16 +++++- 2 files changed, 92 insertions(+), 8 deletions(-) diff --git a/Kernel/FileSystem/FATFS/Inode.cpp b/Kernel/FileSystem/FATFS/Inode.cpp index 306bed48a3..caa80f64a8 100644 --- a/Kernel/FileSystem/FATFS/Inode.cpp +++ b/Kernel/FileSystem/FATFS/Inode.cpp @@ -4,6 +4,8 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include +#include #include #include #include @@ -18,7 +20,7 @@ ErrorOr> FATInode::create(FATFS& fs, FATEntry entry, Vec } FATInode::FATInode(FATFS& fs, FATEntry entry, NonnullOwnPtr filename) - : Inode(fs, first_cluster()) + : Inode(fs, first_cluster(fs.m_fat_version)) , m_entry(entry) , m_filename(move(filename)) { @@ -55,7 +57,7 @@ ErrorOr> FATInode::compute_block_list() auto fat_sector = TRY(KBuffer::try_create_with_size("FATFS: FAT read buffer"sv, fs().m_device_block_size)); auto fat_sector_buffer = UserOrKernelBuffer::for_kernel_buffer(fat_sector->data()); - while (cluster < no_more_clusters) { + while (cluster < end_of_chain_marker()) { dbgln_if(FAT_DEBUG, "FATFS: Appending cluster {} to inode {}'s cluster chain", cluster, index()); auto first_block_and_length = fs().first_block_of_cluster(cluster); @@ -78,19 +80,78 @@ ErrorOr> FATInode::compute_block_list() break; } - u32 fat_offset = cluster * sizeof(u32); + u32 fat_offset = cluster * cluster_size(); u32 fat_sector_index = fs().m_parameter_block->common_bpb()->reserved_sector_count + (fat_offset / fs().m_device_block_size); u32 entry_offset = fat_offset % fs().m_device_block_size; TRY(fs().raw_read(fat_sector_index, fat_sector_buffer)); - cluster = *reinterpret_cast(&fat_sector->data()[entry_offset]); - cluster &= cluster_number_mask; + cluster = cluster_number(*fat_sector, entry_offset); } return block_list; } +u32 FATInode::end_of_chain_marker() const +{ + // Returns the end of chain entry for the given file system. + // Any FAT entry of this value or greater signifies the end + // of the chain has been reached for a given entry. + switch (fs().m_fat_version) { + case FATVersion::FAT12: + return 0xff8; + case FATVersion::FAT16: + return 0xfff8; + case FATVersion::FAT32: + return 0x0FFFFFF8; + default: + VERIFY_NOT_REACHED(); + } +} + +size_t FATInode::cluster_size() const +{ + switch (fs().m_fat_version) { + case FATVersion::FAT12: + VERIFY(false); + break; + case FATVersion::FAT16: + return 2; + case FATVersion::FAT32: + return 4; + default: + VERIFY(false); + } +} + +u32 FATInode::cluster_number(KBuffer const& fat_sector, u32 entry_offset) const +{ + u32 cluster = 0; + switch (fs().m_fat_version) { + case FATVersion::FAT12: + VERIFY(false); + break; + case FATVersion::FAT16: { + u16 cluster_u16_le = 0; + ByteReader::load(fat_sector.bytes().offset(entry_offset), cluster_u16_le); + cluster = LittleEndian { cluster_u16_le }; + break; + } + case FATVersion::FAT32: { + u32 cluster_u32_le = 0; + ByteReader::load(fat_sector.bytes().offset(entry_offset), cluster_u32_le); + cluster = LittleEndian { cluster_u32_le }; + // FAT32 entries use 28-bits to represent the cluster number. The top 4 bits + // may contain flags or other data and must be masked off. + cluster &= 0x0FFFFFFF; + break; + } + default: + VERIFY(false); + } + return cluster; +} + ErrorOr> FATInode::read_block_list() { VERIFY(m_inode_lock.is_locked()); @@ -220,7 +281,18 @@ StringView FATInode::byte_terminated_string(StringView string, u8 fill_byte) u32 FATInode::first_cluster() const { - return (((u32)m_entry.first_cluster_high) << 16) | m_entry.first_cluster_low; + return first_cluster(fs().m_fat_version); +} + +u32 FATInode::first_cluster(FATVersion const version) const +{ + if (version == FATVersion::FAT32) { + return (static_cast(m_entry.first_cluster_high) << 16) | m_entry.first_cluster_low; + } + // The space occupied in a directory entry by `first_cluster_high` (0x14) + // is reserved in FAT12/16, and may be used to store file meta-data. + // As a result, do not include it. + return m_entry.first_cluster_low; } ErrorOr FATInode::read_bytes_locked(off_t offset, size_t size, UserOrKernelBuffer& buffer, OpenFileDescription*) const diff --git a/Kernel/FileSystem/FATFS/Inode.h b/Kernel/FileSystem/FATFS/Inode.h index 16d9cf63fd..0f5ada6598 100644 --- a/Kernel/FileSystem/FATFS/Inode.h +++ b/Kernel/FileSystem/FATFS/Inode.h @@ -30,8 +30,16 @@ public: private: FATInode(FATFS&, FATEntry, NonnullOwnPtr filename); - static constexpr u32 no_more_clusters = 0x0FFFFFF8; - static constexpr u32 cluster_number_mask = 0x0FFFFFFF; + // Number of bytes used to store a cluster within the table. + size_t cluster_size() const; + + // Returns cluster number value that indicates the end of the chain + // has been reached. Any cluster value >= this value indicates this + // is the last cluster. + u32 end_of_chain_marker() const; + + // Reads the cluster number located at the offset within the table. + u32 cluster_number(KBuffer const& fat_sector, u32 entry_offset) const; static constexpr u8 end_entry_byte = 0x00; static constexpr u8 unused_entry_byte = 0xE5; @@ -49,6 +57,10 @@ private: ErrorOr> read_block_list(); ErrorOr> traverse(Function(RefPtr)> callback); u32 first_cluster() const; + // This overload of `first_cluster` does not rely on the base Inode + // already being created to determine the FAT version. It is used + // during FATInode creation (create()). + u32 first_cluster(FATVersion const version) const; // ^Inode virtual ErrorOr write_bytes_locked(off_t, size_t, UserOrKernelBuffer const& data, OpenFileDescription*) override;