Kernel: Implement read-only FAT12/16/32 driver with long name support
You can now mount FAT filesystems! This code might not work perfectly but my quick testing seemed to work on all (FAT12/16/32) variants.
This commit is contained in:
158
kernel/include/kernel/FS/FAT/Definitions.h
Normal file
158
kernel/include/kernel/FS/FAT/Definitions.h
Normal file
@@ -0,0 +1,158 @@
|
||||
#pragma once
|
||||
|
||||
#include <BAN/String.h>
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
}
|
||||
72
kernel/include/kernel/FS/FAT/FileSystem.h
Normal file
72
kernel/include/kernel/FS/FAT/FileSystem.h
Normal file
@@ -0,0 +1,72 @@
|
||||
#pragma once
|
||||
|
||||
#include <BAN/HashMap.h>
|
||||
|
||||
#include <kernel/FS/FAT/Definitions.h>
|
||||
#include <kernel/FS/FAT/Inode.h>
|
||||
#include <kernel/FS/FileSystem.h>
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
class FATFS final : public FileSystem
|
||||
{
|
||||
public:
|
||||
enum class Type
|
||||
{
|
||||
FAT12 = 12,
|
||||
FAT16 = 16,
|
||||
FAT32 = 32,
|
||||
};
|
||||
|
||||
public:
|
||||
static BAN::ErrorOr<bool> probe(BAN::RefPtr<BlockDevice>);
|
||||
static BAN::ErrorOr<BAN::RefPtr<FATFS>> create(BAN::RefPtr<BlockDevice>);
|
||||
|
||||
virtual BAN::RefPtr<Inode> root_inode() override { return m_root_inode; }
|
||||
|
||||
virtual dev_t dev() const override { return m_block_device->rdev(); };
|
||||
|
||||
BAN::ErrorOr<BAN::RefPtr<FATInode>> open_inode(BAN::RefPtr<FATInode> parent, const FAT::DirectoryEntry& entry, uint32_t cluster_index, uint32_t entry_index);
|
||||
BAN::ErrorOr<void> inode_read_cluster(BAN::RefPtr<FATInode>, size_t index, BAN::ByteSpan buffer);
|
||||
blksize_t inode_block_size(BAN::RefPtr<const FATInode>) const;
|
||||
|
||||
private:
|
||||
FATFS(BAN::RefPtr<BlockDevice> 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<void> initialize();
|
||||
|
||||
BAN::ErrorOr<void> fat_cache_set_sector(uint32_t sector);
|
||||
BAN::ErrorOr<uint32_t> 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<uint32_t>(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<BlockDevice> m_block_device;
|
||||
BAN::RefPtr<FATInode> m_root_inode;
|
||||
|
||||
BAN::HashMap<ino_t, BAN::WeakPtr<FATInode>> m_inode_cache;
|
||||
|
||||
BAN::Vector<uint8_t> m_fat_two_sector_buffer;
|
||||
uint32_t m_fat_sector_buffer_current { 0 };
|
||||
|
||||
Mutex m_mutex;
|
||||
|
||||
friend class BAN::RefPtr<FATFS>;
|
||||
};
|
||||
|
||||
}
|
||||
72
kernel/include/kernel/FS/FAT/Inode.h
Normal file
72
kernel/include/kernel/FS/FAT/Inode.h
Normal file
@@ -0,0 +1,72 @@
|
||||
#pragma once
|
||||
|
||||
#include <BAN/Function.h>
|
||||
#include <BAN/Iteration.h>
|
||||
#include <BAN/WeakPtr.h>
|
||||
|
||||
#include <kernel/FS/FAT/Definitions.h>
|
||||
#include <kernel/FS/Inode.h>
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
class FATFS;
|
||||
|
||||
class FATInode final : public Inode, public BAN::Weakable<FATInode>
|
||||
{
|
||||
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<BAN::RefPtr<Inode>> find_inode_impl(BAN::StringView) override;
|
||||
virtual BAN::ErrorOr<size_t> list_next_inodes_impl(off_t, struct dirent*, size_t) override;
|
||||
//virtual BAN::ErrorOr<void> create_file_impl(BAN::StringView, mode_t, uid_t, gid_t) override;
|
||||
//virtual BAN::ErrorOr<void> create_directory_impl(BAN::StringView, mode_t, uid_t, gid_t) override;
|
||||
//virtual BAN::ErrorOr<void> unlink_impl(BAN::StringView) override;
|
||||
|
||||
virtual BAN::ErrorOr<size_t> read_impl(off_t, BAN::ByteSpan) override;
|
||||
//virtual BAN::ErrorOr<size_t> write_impl(off_t, BAN::ConstByteSpan) override;
|
||||
//virtual BAN::ErrorOr<void> truncate_impl(size_t) override;
|
||||
//virtual BAN::ErrorOr<void> 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<void> for_each_directory_entry(BAN::ConstByteSpan, BAN::Function<BAN::Iteration(const FAT::DirectoryEntry&)>);
|
||||
BAN::ErrorOr<void> for_each_directory_entry(BAN::ConstByteSpan, BAN::Function<BAN::Iteration(const FAT::DirectoryEntry&, BAN::String, uint32_t)>);
|
||||
|
||||
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<FATInode>;
|
||||
};
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user