banan-os/kernel/kernel/FS/FAT/Inode.cpp

282 lines
7.5 KiB
C++

#include <BAN/Time.h>
#include <kernel/FS/FAT/FileSystem.h>
#include <kernel/FS/FAT/Inode.h>
#include <ctype.h>
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<void> FATInode::for_each_directory_entry(BAN::ConstByteSpan entry_span, BAN::Function<BAN::Iteration(const FAT::DirectoryEntry&)> callback)
{
ASSERT(mode().ifdir());
auto directory_entries = entry_span.as_span<const FAT::DirectoryEntry>();
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<void> FATInode::for_each_directory_entry(BAN::ConstByteSpan entry_span, BAN::Function<BAN::Iteration(const FAT::DirectoryEntry&, BAN::String, uint32_t)> callback)
{
ASSERT(mode().ifdir());
BAN::String long_name;
auto directory_entries = entry_span.as_span<const FAT::DirectoryEntry>();
auto long_name_entries = entry_span.as_span<const FAT::LongNameEntry>();
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<BAN::RefPtr<Inode>> FATInode::find_inode_impl(BAN::StringView name)
{
ASSERT(mode().ifdir());
BAN::Vector<uint8_t> 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<FATInode> 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<Inode>(result);
}
return BAN::Error::from_errno(ENOENT);
}
BAN::ErrorOr<size_t> FATInode::list_next_inodes_impl(off_t offset, struct dirent* list, size_t list_size)
{
ASSERT(mode().ifdir());
ASSERT(offset >= 0);
BAN::Vector<uint8_t> 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<size_t> FATInode::read_impl(off_t s_offset, BAN::ByteSpan buffer)
{
ASSERT(s_offset >= 0);
uint32_t offset = s_offset;
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<uint8_t> 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<uint32_t>(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;
}
}