Compare commits
5 Commits
766439db6d
...
ea7fc7f6c4
Author | SHA1 | Date |
---|---|---|
Bananymous | ea7fc7f6c4 | |
Bananymous | 6b1d5d28be | |
Bananymous | a9b0bfa740 | |
Bananymous | cc6b80a55b | |
Bananymous | 6707989cd5 |
|
@ -32,7 +32,7 @@ namespace BAN
|
|||
new (m_storage) CallableMemberConst<Own>(function, owner);
|
||||
}
|
||||
template<typename Lambda>
|
||||
Function(Lambda lambda)
|
||||
Function(Lambda lambda) requires requires(Lambda lamda, Args... args) { { lambda(args...) } -> BAN::same_as<Ret>; }
|
||||
{
|
||||
static_assert(sizeof(CallableLambda<Lambda>) <= m_size);
|
||||
new (m_storage) CallableLambda<Lambda>(lambda);
|
||||
|
|
|
@ -42,6 +42,7 @@ namespace BAN
|
|||
template<typename T, typename S> struct is_same : false_type {};
|
||||
template<typename T> struct is_same<T, T> : true_type {};
|
||||
template<typename T, typename S> inline constexpr bool is_same_v = is_same<T, S>::value;
|
||||
template<typename T, typename S> concept same_as = BAN::is_same_v<T, S>;
|
||||
|
||||
template<typename T> struct is_lvalue_reference : false_type {};
|
||||
template<typename T> struct is_lvalue_reference<T&> : true_type {};
|
||||
|
|
|
@ -138,10 +138,13 @@ namespace BAN
|
|||
template<typename T>
|
||||
Vector<T>& Vector<T>::operator=(const Vector<T>& other)
|
||||
{
|
||||
clear();
|
||||
MUST(ensure_capacity(other.size()));
|
||||
for (size_type i = 0; i < other.size(); i++)
|
||||
for (size_type i = 0; i < BAN::Math::min(size(), other.size()); i++)
|
||||
m_data[i] = other.m_data[i];
|
||||
for (size_type i = size(); i < other.size(); i++)
|
||||
new (m_data + i) T(other[i]);
|
||||
for (size_type i = other.size(); i < size(); i++)
|
||||
m_data[i].~T();
|
||||
m_size = other.m_size;
|
||||
return *this;
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@ You can find a live demo [here](https://bananymous.com/banan-os)
|
|||
#### Filesystems
|
||||
- [x] Virtual filesystem
|
||||
- [x] Ext2
|
||||
- [x] FAT12/16/32
|
||||
- [x] Dev
|
||||
- [x] Ram
|
||||
- [x] Proc
|
||||
|
|
|
@ -32,6 +32,8 @@ set(KERNEL_SOURCES
|
|||
kernel/FS/DevFS/FileSystem.cpp
|
||||
kernel/FS/Ext2/FileSystem.cpp
|
||||
kernel/FS/Ext2/Inode.cpp
|
||||
kernel/FS/FAT/FileSystem.cpp
|
||||
kernel/FS/FAT/Inode.cpp
|
||||
kernel/FS/FileSystem.cpp
|
||||
kernel/FS/Inode.cpp
|
||||
kernel/FS/Pipe.cpp
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
}
|
|
@ -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>;
|
||||
};
|
||||
|
||||
}
|
|
@ -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>;
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,276 @@
|
|||
#include <kernel/FS/FAT/FileSystem.h>
|
||||
#include <kernel/Lock/LockGuard.h>
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
bool FATFS::validate_bpb(const FAT::BPB& bpb)
|
||||
{
|
||||
bool valid_jump_op = (bpb.jump_op[0] == 0xEB && bpb.jump_op[2] == 0x90) || (bpb.jump_op[0] == 0xE9);
|
||||
if (!valid_jump_op)
|
||||
return false;
|
||||
|
||||
// This is techincally a strict requirement
|
||||
for (char c : bpb.oem_name)
|
||||
if (!isprint(c))
|
||||
return false;
|
||||
|
||||
if (!BAN::Math::is_power_of_two(bpb.bytes_per_sector) || bpb.bytes_per_sector < 512 || bpb.bytes_per_sector > 4096)
|
||||
return false;
|
||||
|
||||
if (!BAN::Math::is_power_of_two(bpb.sectors_per_cluster) || bpb.sectors_per_cluster > 128)
|
||||
return false;
|
||||
|
||||
if (bpb.reserved_sector_count == 0)
|
||||
return false;
|
||||
|
||||
if (bpb.number_of_fats == 0)
|
||||
return false;
|
||||
|
||||
switch (bpb.media_type)
|
||||
{
|
||||
case 0xF0:
|
||||
case 0xF8:
|
||||
case 0xF9:
|
||||
case 0xFA:
|
||||
case 0xFB:
|
||||
case 0xFC:
|
||||
case 0xFD:
|
||||
case 0xFE:
|
||||
case 0xFF:
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
// FIXME: There is more possible checks to do
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
BAN::ErrorOr<bool> FATFS::probe(BAN::RefPtr<BlockDevice> block_device)
|
||||
{
|
||||
// support only block devices with sectors at least 512 bytes
|
||||
if (block_device->blksize() < 512)
|
||||
return false;
|
||||
|
||||
BAN::Vector<uint8_t> bpb_buffer;
|
||||
TRY(bpb_buffer.resize(block_device->blksize()));
|
||||
auto bpb_span = BAN::ByteSpan(bpb_buffer.span());
|
||||
TRY(block_device->read_blocks(0, 1, bpb_span));
|
||||
|
||||
return validate_bpb(bpb_span.as<const FAT::BPB>());
|
||||
}
|
||||
|
||||
BAN::ErrorOr<BAN::RefPtr<FATFS>> FATFS::create(BAN::RefPtr<BlockDevice> block_device)
|
||||
{
|
||||
// support only block devices with sectors at least 512 bytes
|
||||
if (block_device->blksize() < 512)
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
|
||||
BAN::Vector<uint8_t> bpb_buffer;
|
||||
TRY(bpb_buffer.resize(block_device->blksize()));
|
||||
auto bpb_span = BAN::ByteSpan(bpb_buffer.span());
|
||||
TRY(block_device->read_blocks(0, 1, bpb_span));
|
||||
|
||||
const auto& bpb = bpb_span.as<const FAT::BPB>();
|
||||
if (!validate_bpb(bpb))
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
|
||||
auto fatfs = TRY(BAN::RefPtr<FATFS>::create(
|
||||
block_device,
|
||||
bpb
|
||||
));
|
||||
TRY(fatfs->initialize());
|
||||
return fatfs;
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> FATFS::initialize()
|
||||
{
|
||||
if (m_bpb.bytes_per_sector != m_block_device->blksize())
|
||||
{
|
||||
dwarnln("FileSystem sector size does not match with block device");
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
}
|
||||
|
||||
TRY(m_fat_two_sector_buffer.resize(m_bpb.bytes_per_sector * 2));
|
||||
TRY(m_block_device->read_blocks(first_fat_sector(), 2, BAN::ByteSpan(m_fat_two_sector_buffer.span())));
|
||||
m_fat_sector_buffer_current = 0;
|
||||
|
||||
FAT::DirectoryEntry root_entry {};
|
||||
root_entry.attr = FAT::FileAttr::DIRECTORY;
|
||||
m_root_inode = TRY(open_inode(nullptr, root_entry, 0, 0));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::ErrorOr<BAN::RefPtr<FATInode>> FATFS::open_inode(BAN::RefPtr<FATInode> parent, const FAT::DirectoryEntry& entry, uint32_t cluster_index, uint32_t entry_index)
|
||||
{
|
||||
LockGuard _(m_mutex);
|
||||
|
||||
uint32_t block_count = 0;
|
||||
{
|
||||
uint32_t cluster = entry.first_cluster_lo;
|
||||
if (m_type == Type::FAT32)
|
||||
cluster |= static_cast<uint32_t>(entry.first_cluster_hi) << 16;
|
||||
while (cluster >= 2 && cluster < cluster_count())
|
||||
{
|
||||
block_count++;
|
||||
cluster = TRY(get_next_cluster(cluster));
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t entry_cluster;
|
||||
switch (m_type)
|
||||
{
|
||||
case Type::FAT12:
|
||||
case Type::FAT16:
|
||||
if (parent == m_root_inode)
|
||||
entry_cluster = 1;
|
||||
else
|
||||
{
|
||||
entry_cluster = parent->entry().first_cluster_lo;
|
||||
for (uint32_t i = 0; i < cluster_index; i++)
|
||||
entry_cluster = TRY(get_next_cluster(entry_cluster));
|
||||
}
|
||||
break;
|
||||
case Type::FAT32:
|
||||
if (parent == m_root_inode)
|
||||
entry_cluster = m_bpb.ext_32.root_cluster;
|
||||
else
|
||||
entry_cluster = (static_cast<uint32_t>(parent->entry().first_cluster_hi) << 16) | parent->entry().first_cluster_lo;
|
||||
for (uint32_t i = 0; i < cluster_index; i++)
|
||||
entry_cluster = TRY(get_next_cluster(entry_cluster));
|
||||
break;
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
const ino_t ino = (static_cast<ino_t>(entry_cluster) << 32) | entry_index;
|
||||
auto it = m_inode_cache.find(ino);
|
||||
if (it != m_inode_cache.end())
|
||||
{
|
||||
if (auto inode = it->value.lock())
|
||||
return inode;
|
||||
m_inode_cache.remove(it);
|
||||
}
|
||||
|
||||
auto inode = TRY(BAN::RefPtr<FATInode>::create(*this, entry, ino, block_count));
|
||||
TRY(m_inode_cache.insert(ino, TRY(inode->get_weak_ptr())));
|
||||
return inode;
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> FATFS::fat_cache_set_sector(uint32_t sector)
|
||||
{
|
||||
if (m_fat_sector_buffer_current != sector)
|
||||
{
|
||||
TRY(m_block_device->read_blocks(first_fat_sector() + sector, 2, BAN::ByteSpan(m_fat_two_sector_buffer.span())));
|
||||
m_fat_sector_buffer_current = sector;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::ErrorOr<uint32_t> FATFS::get_next_cluster(uint32_t cluster)
|
||||
{
|
||||
LockGuard _(m_mutex);
|
||||
|
||||
ASSERT(cluster >= 2 && cluster < cluster_count());
|
||||
|
||||
auto fat_span = BAN::ByteSpan(m_fat_two_sector_buffer.span());
|
||||
|
||||
switch (m_type)
|
||||
{
|
||||
case Type::FAT12:
|
||||
{
|
||||
const uint32_t fat_byte_offset = cluster + (cluster / 2);
|
||||
const uint32_t ent_offset = fat_byte_offset % m_bpb.bytes_per_sector;
|
||||
TRY(fat_cache_set_sector(fat_byte_offset / m_bpb.bytes_per_sector));
|
||||
uint16_t next = (fat_span[ent_offset + 1] << 8) | fat_span[ent_offset];
|
||||
return cluster % 2 ? next >> 4 : next & 0xFFF;
|
||||
}
|
||||
case Type::FAT16:
|
||||
{
|
||||
const uint32_t fat_byte_offset = cluster * sizeof(uint16_t);
|
||||
const uint32_t ent_offset = (fat_byte_offset % m_bpb.bytes_per_sector) / sizeof(uint16_t);
|
||||
TRY(fat_cache_set_sector(fat_byte_offset / m_bpb.bytes_per_sector));
|
||||
return fat_span.as_span<uint16_t>()[ent_offset];
|
||||
}
|
||||
case Type::FAT32:
|
||||
{
|
||||
const uint32_t fat_byte_offset = cluster * sizeof(uint32_t);
|
||||
const uint32_t ent_offset = (fat_byte_offset % m_bpb.bytes_per_sector) / sizeof(uint32_t);
|
||||
TRY(fat_cache_set_sector(fat_byte_offset / m_bpb.bytes_per_sector));
|
||||
return fat_span.as_span<uint32_t>()[ent_offset];
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> FATFS::inode_read_cluster(BAN::RefPtr<FATInode> file, size_t index, BAN::ByteSpan buffer)
|
||||
{
|
||||
LockGuard _(m_mutex);
|
||||
|
||||
if (buffer.size() < static_cast<BAN::make_unsigned_t<decltype(file->blksize())>>(file->blksize()))
|
||||
return BAN::Error::from_errno(ENOBUFS);
|
||||
|
||||
uint32_t cluster;
|
||||
switch (m_type)
|
||||
{
|
||||
case Type::FAT12:
|
||||
case Type::FAT16:
|
||||
{
|
||||
if (file == m_root_inode)
|
||||
{
|
||||
if (index >= root_sector_count())
|
||||
return BAN::Error::from_errno(ENOENT);
|
||||
const uint32_t first_root_sector = m_bpb.reserved_sector_count + (m_bpb.number_of_fats * fat_sector_count());
|
||||
TRY(m_block_device->read_blocks(first_root_sector + index, 1, buffer));
|
||||
return {};
|
||||
}
|
||||
cluster = file->entry().first_cluster_lo;
|
||||
break;
|
||||
}
|
||||
case Type::FAT32:
|
||||
if (file == m_root_inode)
|
||||
cluster = m_bpb.ext_32.root_cluster;
|
||||
else
|
||||
cluster = (static_cast<uint32_t>(file->entry().first_cluster_hi) << 16) | file->entry().first_cluster_lo;
|
||||
break;
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
if (cluster < 2 || cluster >= cluster_count())
|
||||
return BAN::Error::from_errno(ENOENT);
|
||||
|
||||
for (uint32_t i = 0; i < index; i++)
|
||||
{
|
||||
cluster = TRY(get_next_cluster(cluster));
|
||||
if (cluster < 2 || cluster >= cluster_count())
|
||||
return BAN::Error::from_errno(ENOENT);
|
||||
}
|
||||
|
||||
const uint32_t cluster_start_sector = ((cluster - 2) * m_bpb.sectors_per_cluster) + first_data_sector();
|
||||
TRY(m_block_device->read_blocks(cluster_start_sector, m_bpb.sectors_per_cluster, buffer));
|
||||
return {};
|
||||
}
|
||||
|
||||
blksize_t FATFS::inode_block_size(BAN::RefPtr<const FATInode> file) const
|
||||
{
|
||||
switch (m_type)
|
||||
{
|
||||
case Type::FAT12:
|
||||
case Type::FAT16:
|
||||
if (file == m_root_inode)
|
||||
return m_bpb.bytes_per_sector;
|
||||
return m_bpb.bytes_per_sector * m_bpb.sectors_per_cluster;
|
||||
case Type::FAT32:
|
||||
return m_bpb.bytes_per_sector * m_bpb.sectors_per_cluster;
|
||||
}
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,280 @@
|
|||
#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 offset, BAN::ByteSpan buffer)
|
||||
{
|
||||
ASSERT(offset >= 0);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
#include <kernel/FS/Ext2/FileSystem.h>
|
||||
#include <kernel/FS/FAT/FileSystem.h>
|
||||
#include <kernel/FS/FileSystem.h>
|
||||
|
||||
namespace Kernel
|
||||
|
@ -8,6 +9,8 @@ namespace Kernel
|
|||
{
|
||||
if (auto res = Ext2FS::probe(block_device); !res.is_error() && res.value())
|
||||
return BAN::RefPtr<FileSystem>(TRY(Ext2FS::create(block_device)));
|
||||
if (auto res = FATFS::probe(block_device); !res.is_error() && res.value())
|
||||
return BAN::RefPtr<FileSystem>(TRY(FATFS::create(block_device)));
|
||||
dprintln("Unsupported filesystem");
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
}
|
||||
|
|
|
@ -12,18 +12,18 @@ namespace Kernel
|
|||
|
||||
static BAN::RefPtr<VirtualFileSystem> s_instance;
|
||||
|
||||
void VirtualFileSystem::initialize(BAN::StringView root)
|
||||
void VirtualFileSystem::initialize(BAN::StringView root_path)
|
||||
{
|
||||
ASSERT(!s_instance);
|
||||
s_instance = MUST(BAN::RefPtr<VirtualFileSystem>::create());
|
||||
|
||||
ASSERT(root.size() >= 5 && root.substring(0, 5) == "/dev/"sv);;
|
||||
root = root.substring(5);
|
||||
ASSERT(root_path.size() >= 5 && root_path.substring(0, 5) == "/dev/"sv);;
|
||||
root_path = root_path.substring(5);
|
||||
|
||||
auto partition_inode = MUST(DevFileSystem::get().root_inode()->find_inode(root));
|
||||
if (!partition_inode->is_device() || !static_cast<Device*>(partition_inode.ptr())->is_partition())
|
||||
Kernel::panic("Specified root '/dev/{}' does not name a partition", root);
|
||||
s_instance->m_root_fs = MUST(FileSystem::from_block_device(static_cast<BlockDevice*>(partition_inode.ptr())));
|
||||
auto root_inode = MUST(DevFileSystem::get().root_inode()->find_inode(root_path));
|
||||
if (!root_inode->mode().ifblk())
|
||||
Kernel::panic("Specified root '/dev/{}' does not name a block device", root_path);
|
||||
s_instance->m_root_fs = MUST(FileSystem::from_block_device(static_cast<BlockDevice*>(root_inode.ptr())));
|
||||
|
||||
Credentials root_creds { 0, 0, 0, 0 };
|
||||
MUST(s_instance->mount(root_creds, &DevFileSystem::get(), "/dev"sv));
|
||||
|
|
|
@ -104,7 +104,7 @@ __BEGIN_DECLS
|
|||
|
||||
#if !defined(__ino_t_defined) && (defined(__need_all_types) || defined(__need_ino_t))
|
||||
#define __ino_t_defined 1
|
||||
typedef unsigned long ino_t;
|
||||
typedef unsigned long long ino_t;
|
||||
#endif
|
||||
#undef __need_ino_t
|
||||
|
||||
|
|
|
@ -66,7 +66,7 @@ int main(int argc, char** argv)
|
|||
|
||||
printf(" File: %s\n", argv[i]);
|
||||
printf(" Size: %-15ld Blocks: %-10ld IO Block: %-6ld %s\n", st.st_size, st.st_blocks, st.st_blksize, type);
|
||||
printf("Device: %lu,%-5lu Inode: %-11lu Links: %-5lu Device type: %lu,%lu\n", major(st.st_dev), minor(st.st_dev), st.st_ino, st.st_nlink, major(st.st_rdev), minor(st.st_rdev));
|
||||
printf("Device: %lu,%-5lu Inode: %-11llu Links: %-5lu Device type: %lu,%lu\n", major(st.st_dev), minor(st.st_dev), st.st_ino, st.st_nlink, major(st.st_rdev), minor(st.st_rdev));
|
||||
printf("Access: (%04o/%s) Uid: %5d Gid: %5d\n", st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISVTX), access, st.st_uid, st.st_gid);
|
||||
printf("Access: "); print_timestamp(st.st_atim); printf("\n");
|
||||
printf("Modify: "); print_timestamp(st.st_mtim); printf("\n");
|
||||
|
|
Loading…
Reference in New Issue