Skip to content

Commit

Permalink
Kernel/FATFS: Avoid creating LFN entries when an SFN will suffice
Browse files Browse the repository at this point in the history
  • Loading branch information
implicitfield authored and nico committed Nov 2, 2024
1 parent 0ba7a03 commit a9083f9
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 10 deletions.
92 changes: 82 additions & 10 deletions Kernel/FileSystem/FATFS/Inode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<NonnullRefPtr<SFN>> create_sfn_from_lfn(StringView lfn)
{
Expand Down Expand Up @@ -177,6 +212,27 @@ ErrorOr<void> FATInode::create_unique_sfn_for(FATEntry& entry, NonnullRefPtr<SFN
return {};
}

ErrorOr<void> 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<Vector<FATLongFileNameEntry>> FATInode::create_lfn_entries(StringView name, u8 checksum)
{
u32 lfn_entry_count = ceil_div(name.length(), characters_per_lfn_entry);
Expand Down Expand Up @@ -642,18 +698,26 @@ ErrorOr<NonnullRefPtr<Inode>> 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)
entry.attributes |= FATAttributes::Directory;

// 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<FATLongFileNameEntry> lfn_entries = {};
if (!valid_sfn)
lfn_entries = TRY(create_lfn_entries(name, lfn_entry_checksum(entry)));

MutexLocker locker(m_inode_lock);

Expand Down Expand Up @@ -710,18 +774,26 @@ ErrorOr<void> 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<FATInode*>(&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> sfn = TRY(create_sfn_from_lfn(name));
Vector<ByteBuffer> 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)
entry.attributes |= FATAttributes::Directory;

// 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<FATLongFileNameEntry> lfn_entries = {};
if (!valid_sfn)
auto lfn_entries = TRY(create_lfn_entries(name, lfn_entry_checksum(entry)));

MutexLocker locker(m_inode_lock);

Expand Down
1 change: 1 addition & 0 deletions Kernel/FileSystem/FATFS/Inode.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<void> create_unique_sfn_for(FATEntry& entry, NonnullRefPtr<SFN> sfn, Vector<ByteBuffer> existing_sfns);
static ErrorOr<void> encode_known_good_sfn_for(FATEntry& entry, StringView name);
static ErrorOr<Vector<FATLongFileNameEntry>> create_lfn_entries(StringView name, u8 checksum);

ErrorOr<Vector<u32>> compute_cluster_list(FATFS&, u32 first_cluster);
Expand Down

0 comments on commit a9083f9

Please sign in to comment.