From a9083f9be710f9e5d1bc5a47f889087b9f463eee Mon Sep 17 00:00:00 2001 From: implicitfield <114500360+implicitfield@users.noreply.github.com> Date: Mon, 26 Aug 2024 17:52:09 +0300 Subject: [PATCH] Kernel/FATFS: Avoid creating LFN entries when an SFN will suffice --- Kernel/FileSystem/FATFS/Inode.cpp | 92 +++++++++++++++++++++++++++---- Kernel/FileSystem/FATFS/Inode.h | 1 + 2 files changed, 83 insertions(+), 10 deletions(-) diff --git a/Kernel/FileSystem/FATFS/Inode.cpp b/Kernel/FileSystem/FATFS/Inode.cpp index 499316c60c031f..8d6f130515356d 100644 --- a/Kernel/FileSystem/FATFS/Inode.cpp +++ b/Kernel/FileSystem/FATFS/Inode.cpp @@ -93,6 +93,41 @@ static bool is_valid_sfn_char(char c) return valid_misc_sfn_chars.contains_slow(c); } +static bool is_valid_sfn(StringView sfn) +{ + StringView name = {}; + StringView extension = {}; + + auto dot = sfn.find('.'); + if (!dot.has_value()) { + name = sfn; + } else { + if (*dot + 1 >= sfn.length()) + return false; + + name = sfn.substring_view(0, *dot); + extension = sfn.substring_view(*dot + 1, sfn.length() - *dot - 1); + } + + if (name.length() > 8 || extension.length() > 3) + return false; + + for (char c : name) { + if (!is_valid_sfn_char(c)) + return false; + } + + for (char c : extension) { + if (!is_valid_sfn_char(c)) + return false; + } + + if (name.length() == 0 || name[0] == ' ') + return false; + + return true; +} + // http://www.osdever.net/documents/LongFileName.pdf static ErrorOr> create_sfn_from_lfn(StringView lfn) { @@ -177,6 +212,27 @@ ErrorOr FATInode::create_unique_sfn_for(FATEntry& entry, NonnullRefPtr FATInode::encode_known_good_sfn_for(FATEntry& entry, StringView name) +{ + memset(entry.filename, ' ', 8); + memset(entry.extension, ' ', 3); + auto dot = name.find('.'); + if (dot.has_value()) { + auto extension_length = name.length() - dot.value() - 1; + + VERIFY(dot.value() <= 8); + VERIFY(extension_length <= 3); + + memcpy(entry.filename, name.bytes().data(), dot.value()); + memcpy(entry.extension, name.bytes().data() + dot.value() + 1, extension_length); + } else { + VERIFY(name.bytes().size() <= 8); + memcpy(entry.filename, name.bytes().data(), name.bytes().size()); + } + + return {}; +} + ErrorOr> FATInode::create_lfn_entries(StringView name, u8 checksum) { u32 lfn_entry_count = ceil_div(name.length(), characters_per_lfn_entry); @@ -642,9 +698,16 @@ ErrorOr> FATInode::create_child(StringView name, mode_t mod dbgln_if(FAT_DEBUG, "FATInode[{}]::create_child(): creating inode \"{}\"", identifier(), name); FATEntry entry {}; - auto sfn = TRY(create_sfn_from_lfn(name)); - auto existing_sfns = TRY(collect_sfns()); - TRY(create_unique_sfn_for(entry, move(sfn), move(existing_sfns))); + + bool valid_sfn = is_valid_sfn(name); + + if (valid_sfn) { + TRY(encode_known_good_sfn_for(entry, name)); + } else { + auto sfn = TRY(create_sfn_from_lfn(name)); + auto existing_sfns = TRY(collect_sfns()); + TRY(create_unique_sfn_for(entry, move(sfn), move(existing_sfns))); + } // TODO: We should set the hidden attribute if the file starts with a dot or read only (the same way Linux does this). if (mode & S_IFDIR) @@ -652,8 +715,9 @@ ErrorOr> FATInode::create_child(StringView name, mode_t mod // FIXME: Set the dates - // FIXME: For some filenames lfn entries are not necessary - auto lfn_entries = TRY(create_lfn_entries(name, lfn_entry_checksum(entry))); + Vector lfn_entries = {}; + if (!valid_sfn) + lfn_entries = TRY(create_lfn_entries(name, lfn_entry_checksum(entry))); MutexLocker locker(m_inode_lock); @@ -710,9 +774,16 @@ ErrorOr FATInode::add_child(Inode& inode, StringView name, mode_t mode) dbgln_if(FAT_DEBUG, "FATInode[{}]::add_child(): appending inode {} as \"{}\"", identifier(), inode.identifier(), name); auto entry = bit_cast(&inode)->m_entry; - auto sfn = TRY(create_sfn_from_lfn(name)); - auto existing_sfns = TRY(collect_sfns()); - TRY(create_unique_sfn_for(entry, move(sfn), move(existing_sfns))); + + bool valid_sfn = is_valid_sfn(name); + + if (valid_sfn) { + TRY(encode_known_good_sfn_for(entry, name)); + } else { + NonnullRefPtr sfn = TRY(create_sfn_from_lfn(name)); + Vector existing_sfns = TRY(collect_sfns()); + TRY(create_unique_sfn_for(entry, move(sfn), move(existing_sfns))); + } // TODO: We should set the hidden attribute if the file starts with a dot or read only (the same way Linux does this). if (mode & S_IFDIR) @@ -720,8 +791,9 @@ ErrorOr FATInode::add_child(Inode& inode, StringView name, mode_t mode) // FIXME: Set the dates - // FIXME: For some filenames lfn entries are not necessary - auto lfn_entries = TRY(create_lfn_entries(name, lfn_entry_checksum(entry))); + Vector lfn_entries = {}; + if (!valid_sfn) + auto lfn_entries = TRY(create_lfn_entries(name, lfn_entry_checksum(entry))); MutexLocker locker(m_inode_lock); diff --git a/Kernel/FileSystem/FATFS/Inode.h b/Kernel/FileSystem/FATFS/Inode.h index 2512eeca235293..d75c87941eb640 100644 --- a/Kernel/FileSystem/FATFS/Inode.h +++ b/Kernel/FileSystem/FATFS/Inode.h @@ -118,6 +118,7 @@ class FATInode final : public Inode { static StringView byte_terminated_string(StringView, u8); static u8 lfn_entry_checksum(FATEntry const& entry); static ErrorOr create_unique_sfn_for(FATEntry& entry, NonnullRefPtr sfn, Vector existing_sfns); + static ErrorOr encode_known_good_sfn_for(FATEntry& entry, StringView name); static ErrorOr> create_lfn_entries(StringView name, u8 checksum); ErrorOr> compute_cluster_list(FATFS&, u32 first_cluster);