Kernel: Initial work on filesystem
We support now ext2 directory listing. File reading is not yet supported.
This commit is contained in:
parent
247f03c79e
commit
80006ea137
|
@ -38,6 +38,8 @@ kernel/CPUID.o \
|
||||||
kernel/Debug.o \
|
kernel/Debug.o \
|
||||||
kernel/DiskIO.o \
|
kernel/DiskIO.o \
|
||||||
kernel/font.o \
|
kernel/font.o \
|
||||||
|
kernel/FS/Ext2.o \
|
||||||
|
kernel/FS/VirtualFileSystem.o \
|
||||||
kernel/Input.o \
|
kernel/Input.o \
|
||||||
kernel/InterruptController.o \
|
kernel/InterruptController.o \
|
||||||
kernel/kernel.o \
|
kernel/kernel.o \
|
||||||
|
@ -95,6 +97,7 @@ $(ARCHDIR)/crtbegin.o $(ARCHDIR)/crtend.o:
|
||||||
always:
|
always:
|
||||||
mkdir -p $(BUILDDIR)/$(ARCHDIR)
|
mkdir -p $(BUILDDIR)/$(ARCHDIR)
|
||||||
mkdir -p $(BUILDDIR)/kernel
|
mkdir -p $(BUILDDIR)/kernel
|
||||||
|
mkdir -p $(BUILDDIR)/kernel/FS
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf $(BUILDDIR)
|
rm -rf $(BUILDDIR)
|
||||||
|
|
|
@ -41,7 +41,7 @@ namespace Kernel
|
||||||
virtual uint32_t sector_size() const override { return m_sector_words * 2; }
|
virtual uint32_t sector_size() const override { return m_sector_words * 2; }
|
||||||
virtual const char* type() const override { return "PATA"; }
|
virtual const char* type() const override { return "PATA"; }
|
||||||
|
|
||||||
virtual bool read(uint32_t lba, uint32_t sector_count, uint8_t* buffer) override;
|
virtual bool read_sectors(uint32_t lba, uint32_t sector_count, uint8_t* buffer) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual bool initialize() override;
|
virtual bool initialize() override;
|
||||||
|
|
|
@ -5,17 +5,40 @@
|
||||||
namespace Kernel
|
namespace Kernel
|
||||||
{
|
{
|
||||||
|
|
||||||
|
struct GUID
|
||||||
|
{
|
||||||
|
uint32_t data1;
|
||||||
|
uint16_t data2;
|
||||||
|
uint16_t data3;
|
||||||
|
uint8_t data4[8];
|
||||||
|
};
|
||||||
|
|
||||||
class DiskDevice
|
class DiskDevice
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
struct Partition
|
struct Partition
|
||||||
{
|
{
|
||||||
uint8_t type_guid[16];
|
Partition(DiskDevice&, const GUID&, const GUID&, uint64_t, uint64_t, uint64_t, const char*);
|
||||||
uint8_t guid[16];
|
|
||||||
uint64_t start_lba;
|
const GUID& type() const { return m_type; }
|
||||||
uint64_t end_lba;
|
const GUID& guid() const { return m_guid; }
|
||||||
uint64_t attributes;
|
uint64_t lba_start() const { return m_lba_start; }
|
||||||
char name[72];
|
uint64_t lba_end() const { return m_lba_end; }
|
||||||
|
uint64_t attributes() const { return m_attributes; }
|
||||||
|
const char* name() const { return m_name; }
|
||||||
|
const DiskDevice& device() const { return m_device; }
|
||||||
|
|
||||||
|
bool read_sectors(uint32_t lba, uint32_t sector_count, uint8_t* buffer);
|
||||||
|
bool is_used() const { uint8_t zero[16] {}; return memcmp(&m_type, zero, 16); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
DiskDevice& m_device;
|
||||||
|
const GUID m_type;
|
||||||
|
const GUID m_guid;
|
||||||
|
const uint64_t m_lba_start;
|
||||||
|
const uint64_t m_lba_end;
|
||||||
|
const uint64_t m_attributes;
|
||||||
|
char m_name[72];
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -24,9 +47,11 @@ namespace Kernel
|
||||||
virtual bool initialize() = 0;
|
virtual bool initialize() = 0;
|
||||||
bool initialize_partitions();
|
bool initialize_partitions();
|
||||||
|
|
||||||
virtual bool read(uint32_t lba, uint32_t sector_count, uint8_t* buffer) = 0;
|
virtual bool read_sectors(uint32_t lba, uint32_t sector_count, uint8_t* buffer) = 0;
|
||||||
virtual uint32_t sector_size() const = 0;
|
virtual uint32_t sector_size() const = 0;
|
||||||
|
|
||||||
|
BAN::Vector<Partition>& partitions() { return m_partitions; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
BAN::Vector<Partition> m_partitions;
|
BAN::Vector<Partition> m_partitions;
|
||||||
};
|
};
|
||||||
|
@ -39,6 +64,7 @@ namespace Kernel
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DiskIO();
|
DiskIO();
|
||||||
|
void try_add_device(DiskDevice*);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
BAN::Vector<DiskDevice*> m_devices;
|
BAN::Vector<DiskDevice*> m_devices;
|
||||||
|
|
|
@ -0,0 +1,188 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/String.h>
|
||||||
|
#include <kernel/DiskIO.h>
|
||||||
|
#include <kernel/FS/FileSystem.h>
|
||||||
|
|
||||||
|
namespace Kernel
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace Ext2
|
||||||
|
{
|
||||||
|
|
||||||
|
struct Superblock
|
||||||
|
{
|
||||||
|
uint32_t inodes_count;
|
||||||
|
uint32_t blocks_count;
|
||||||
|
uint32_t r_blocks_count;
|
||||||
|
uint32_t free_blocks_count;
|
||||||
|
uint32_t free_inodes_count;
|
||||||
|
uint32_t first_data_block;
|
||||||
|
uint32_t log_block_size;
|
||||||
|
uint32_t log_frag_size;
|
||||||
|
uint32_t blocks_per_group;
|
||||||
|
uint32_t frags_per_group;
|
||||||
|
uint32_t inodes_per_group;
|
||||||
|
uint32_t mtime;
|
||||||
|
uint32_t wtime;
|
||||||
|
uint16_t mnt_count;
|
||||||
|
uint16_t max_mnt_count;
|
||||||
|
uint16_t magic;
|
||||||
|
uint16_t state;
|
||||||
|
uint16_t errors;
|
||||||
|
uint16_t minor_rev_level;
|
||||||
|
uint32_t lastcheck;
|
||||||
|
uint32_t checkinterval;
|
||||||
|
uint32_t creator_os;
|
||||||
|
uint32_t rev_level;
|
||||||
|
uint16_t def_resuid;
|
||||||
|
uint16_t def_resgid;
|
||||||
|
|
||||||
|
// -- EXT2_DYNAMIC_REV Specific --
|
||||||
|
uint8_t __extension_start[0];
|
||||||
|
uint32_t first_ino;
|
||||||
|
uint16_t inode_size;
|
||||||
|
uint16_t block_group_nr;
|
||||||
|
uint32_t feature_compat;
|
||||||
|
uint32_t feature_incompat;
|
||||||
|
uint32_t feature_ro_compat;
|
||||||
|
uint8_t uuid[16];
|
||||||
|
uint8_t volume_name[16];
|
||||||
|
char last_mounted[64];
|
||||||
|
uint32_t algo_bitmap;
|
||||||
|
|
||||||
|
// -- Performance Hints --
|
||||||
|
uint8_t s_prealloc_blocks;
|
||||||
|
uint8_t s_prealloc_dir_blocks;
|
||||||
|
uint16_t __alignment;
|
||||||
|
|
||||||
|
// -- Journaling Support --
|
||||||
|
uint8_t journal_uuid[16];
|
||||||
|
uint32_t journal_inum;
|
||||||
|
uint32_t journal_dev;
|
||||||
|
uint32_t last_orphan;
|
||||||
|
|
||||||
|
// -- Directory Indexing Support --
|
||||||
|
uint32_t hash_seed[4];
|
||||||
|
uint8_t def_hash_version;
|
||||||
|
uint8_t __padding[3];
|
||||||
|
|
||||||
|
// -- Other options --
|
||||||
|
uint32_t default_mount_options;
|
||||||
|
uint32_t first_meta_bg;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BlockGroupDescriptor
|
||||||
|
{
|
||||||
|
uint32_t block_bitmap;
|
||||||
|
uint32_t inode_bitmap;
|
||||||
|
uint32_t inode_table;
|
||||||
|
uint16_t free_blocks_count;
|
||||||
|
uint16_t free_inodes_count;
|
||||||
|
uint16_t used_dirs_count;
|
||||||
|
uint16_t __padding;
|
||||||
|
//uint8_t reserved[12];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Inode
|
||||||
|
{
|
||||||
|
uint16_t mode;
|
||||||
|
uint16_t uid;
|
||||||
|
uint32_t size;
|
||||||
|
uint32_t atime;
|
||||||
|
uint32_t ctime;
|
||||||
|
uint32_t mtime;
|
||||||
|
uint32_t dtime;
|
||||||
|
uint16_t gid;
|
||||||
|
uint16_t links_count;
|
||||||
|
uint32_t blocks;
|
||||||
|
uint32_t flags;
|
||||||
|
uint32_t osd1;
|
||||||
|
uint32_t block[15];
|
||||||
|
uint32_t generation;
|
||||||
|
uint32_t file_acl;
|
||||||
|
uint32_t dir_acl;
|
||||||
|
uint32_t faddr;
|
||||||
|
uint32_t osd2[3];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LinkedDirectoryEntry
|
||||||
|
{
|
||||||
|
uint32_t inode;
|
||||||
|
uint16_t rec_len;
|
||||||
|
uint8_t name_len;
|
||||||
|
uint8_t file_type;
|
||||||
|
char name[0];
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class Ext2FS;
|
||||||
|
|
||||||
|
class Ext2Inode : public Inode
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual bool is_directory() const override;
|
||||||
|
virtual bool is_regular_file() const override;
|
||||||
|
|
||||||
|
virtual uint16_t uid() const override { return m_inode.uid; }
|
||||||
|
virtual uint16_t gid() const override { return m_inode.gid; }
|
||||||
|
virtual uint32_t size() const override { return m_inode.size; }
|
||||||
|
|
||||||
|
virtual BAN::StringView name() const override { return m_name; }
|
||||||
|
|
||||||
|
virtual BAN::ErrorOr<BAN::Vector<uint8_t>> read_all() const override;
|
||||||
|
virtual BAN::ErrorOr<BAN::Vector<BAN::RefCounted<Inode>>> directory_inodes() const override;
|
||||||
|
virtual BAN::ErrorOr<BAN::RefCounted<Inode>> directory_find(BAN::StringView) const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ext2Inode() {}
|
||||||
|
Ext2Inode(Ext2FS* fs, Ext2::Inode inode, BAN::StringView name)
|
||||||
|
: m_fs(fs)
|
||||||
|
, m_inode(inode)
|
||||||
|
, m_name(name)
|
||||||
|
{}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ext2FS* m_fs = nullptr;
|
||||||
|
Ext2::Inode m_inode;
|
||||||
|
BAN::String m_name;
|
||||||
|
|
||||||
|
friend class Ext2FS;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Ext2FS : public FileSystem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static BAN::ErrorOr<Ext2FS*> create(DiskDevice::Partition&);
|
||||||
|
|
||||||
|
virtual const BAN::RefCounted<Inode> root_inode() const override { return m_root_inode; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ext2FS(DiskDevice::Partition& partition)
|
||||||
|
: m_partition(partition)
|
||||||
|
{}
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> initialize_superblock();
|
||||||
|
BAN::ErrorOr<void> initialize_block_group_descriptors();
|
||||||
|
BAN::ErrorOr<void> initialize_root_inode();
|
||||||
|
|
||||||
|
BAN::ErrorOr<Ext2::Inode> read_inode(uint32_t);
|
||||||
|
BAN::ErrorOr<BAN::Vector<uint8_t>> read_block(uint32_t);
|
||||||
|
|
||||||
|
const Ext2::Superblock& superblock() const { return m_superblock; }
|
||||||
|
|
||||||
|
const Ext2::Inode& ext2_root_inode() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
DiskDevice::Partition& m_partition;
|
||||||
|
|
||||||
|
BAN::RefCounted<Inode> m_root_inode;
|
||||||
|
|
||||||
|
Ext2::Superblock m_superblock;
|
||||||
|
BAN::Vector<Ext2::BlockGroupDescriptor> m_block_group_descriptors;
|
||||||
|
|
||||||
|
friend class Ext2Inode;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Memory.h>
|
||||||
|
#include <kernel/FS/Inode.h>
|
||||||
|
|
||||||
|
namespace Kernel
|
||||||
|
{
|
||||||
|
|
||||||
|
class FileSystem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual const BAN::RefCounted<Inode> root_inode() const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/ForwardList.h>
|
||||||
|
#include <BAN/Memory.h>
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
namespace Kernel
|
||||||
|
{
|
||||||
|
|
||||||
|
class Inode
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual bool is_directory() const = 0;
|
||||||
|
virtual bool is_regular_file() const = 0;
|
||||||
|
|
||||||
|
virtual uint16_t uid() const = 0;
|
||||||
|
virtual uint16_t gid() const = 0;
|
||||||
|
virtual uint32_t size() const = 0;
|
||||||
|
|
||||||
|
virtual BAN::StringView name() const = 0;
|
||||||
|
|
||||||
|
virtual BAN::ErrorOr<BAN::Vector<uint8_t>> read_all() const = 0;
|
||||||
|
virtual BAN::ErrorOr<BAN::Vector<BAN::RefCounted<Inode>>> directory_inodes() const = 0;
|
||||||
|
virtual BAN::ErrorOr<BAN::RefCounted<Inode>> directory_find(BAN::StringView) const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <kernel/FS/FileSystem.h>
|
||||||
|
|
||||||
|
namespace Kernel
|
||||||
|
{
|
||||||
|
|
||||||
|
class VirtualFileSystem : public FileSystem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static void initialize(BAN::RefCounted<Inode> root_inode);
|
||||||
|
static VirtualFileSystem& get();
|
||||||
|
static bool is_initialized();
|
||||||
|
|
||||||
|
virtual const BAN::RefCounted<Inode> root_inode() const override { return m_root_inode; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
VirtualFileSystem(BAN::RefCounted<Inode> root_inode)
|
||||||
|
: m_root_inode(root_inode)
|
||||||
|
{}
|
||||||
|
|
||||||
|
private:
|
||||||
|
BAN::RefCounted<Inode> m_root_inode;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -136,13 +136,10 @@ namespace Kernel
|
||||||
dprintln("using {} sector size", m_sector_words * 2);
|
dprintln("using {} sector size", m_sector_words * 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PATADevice::read(uint32_t lba, uint32_t sector_count, uint8_t* buffer)
|
bool PATADevice::read_sectors(uint32_t lba, uint32_t sector_count, uint8_t* buffer)
|
||||||
{
|
{
|
||||||
return read_lba28(lba, sector_count, buffer);
|
return read_lba28(lba, sector_count, buffer);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
#include <BAN/ScopeGuard.h>
|
#include <BAN/ScopeGuard.h>
|
||||||
#include <BAN/StringView.h>
|
#include <BAN/StringView.h>
|
||||||
#include <kernel/ATA.h>
|
#include <kernel/ATA.h>
|
||||||
|
#include <kernel/FS/Ext2.h>
|
||||||
|
#include <kernel/FS/VirtualFileSystem.h>
|
||||||
#include <kernel/DiskIO.h>
|
#include <kernel/DiskIO.h>
|
||||||
|
|
||||||
|
#include <kernel/kprint.h>
|
||||||
|
|
||||||
#define ATA_DEVICE_PRIMARY 0x1F0
|
#define ATA_DEVICE_PRIMARY 0x1F0
|
||||||
#define ATA_DEVICE_SECONDARY 0x170
|
#define ATA_DEVICE_SECONDARY 0x170
|
||||||
#define ATA_DEVICE_SLAVE_BIT 0x10
|
#define ATA_DEVICE_SLAVE_BIT 0x10
|
||||||
|
@ -19,7 +23,7 @@ namespace Kernel
|
||||||
uint64_t my_lba;
|
uint64_t my_lba;
|
||||||
uint64_t first_lba;
|
uint64_t first_lba;
|
||||||
uint64_t last_lba;
|
uint64_t last_lba;
|
||||||
uint8_t guid[16];
|
GUID guid;
|
||||||
uint64_t partition_entry_lba;
|
uint64_t partition_entry_lba;
|
||||||
uint32_t partition_entry_count;
|
uint32_t partition_entry_count;
|
||||||
uint32_t partition_entry_size;
|
uint32_t partition_entry_size;
|
||||||
|
@ -114,6 +118,25 @@ namespace Kernel
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
static T big_endian_to_host(const uint8_t* data)
|
||||||
|
{
|
||||||
|
T result = 0;
|
||||||
|
for (size_t i = 0; i < sizeof(T); i++)
|
||||||
|
result |= data[i] << (8 * (sizeof(T) - i - 1));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GUID parse_guid(const uint8_t* guid)
|
||||||
|
{
|
||||||
|
GUID result;
|
||||||
|
result.data1 = big_endian_to_host<uint32_t>(guid + 0);
|
||||||
|
result.data2 = big_endian_to_host<uint16_t>(guid + 4);
|
||||||
|
result.data3 = big_endian_to_host<uint16_t>(guid + 6);
|
||||||
|
memcpy(result.data4, guid + 8, 8);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
static bool is_valid_gpt_header(const GPTHeader& header, uint32_t sector_size)
|
static bool is_valid_gpt_header(const GPTHeader& header, uint32_t sector_size)
|
||||||
{
|
{
|
||||||
if (memcmp(header.signature, "EFI PART", 8) != 0)
|
if (memcmp(header.signature, "EFI PART", 8) != 0)
|
||||||
|
@ -137,19 +160,19 @@ namespace Kernel
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static GPTHeader parse_gpt_header(const BAN::Vector<uint8_t> lba1)
|
static GPTHeader parse_gpt_header(const BAN::Vector<uint8_t>& lba1)
|
||||||
{
|
{
|
||||||
GPTHeader header;
|
GPTHeader header;
|
||||||
memset(&header, 0, sizeof(header));
|
memset(&header, 0, sizeof(header));
|
||||||
|
|
||||||
memcpy(header.signature, lba1.data(), 8);
|
memcpy(header.signature, lba1.data(), 8);
|
||||||
memcpy(header.guid, lba1.data() + 56, 16);
|
|
||||||
header.revision = little_endian_to_host<uint32_t>(lba1.data() + 8);
|
header.revision = little_endian_to_host<uint32_t>(lba1.data() + 8);
|
||||||
header.size = little_endian_to_host<uint32_t>(lba1.data() + 12);
|
header.size = little_endian_to_host<uint32_t>(lba1.data() + 12);
|
||||||
header.crc32 = little_endian_to_host<uint32_t>(lba1.data() + 16);
|
header.crc32 = little_endian_to_host<uint32_t>(lba1.data() + 16);
|
||||||
header.my_lba = little_endian_to_host<uint64_t>(lba1.data() + 24);
|
header.my_lba = little_endian_to_host<uint64_t>(lba1.data() + 24);
|
||||||
header.first_lba = little_endian_to_host<uint64_t>(lba1.data() + 40);
|
header.first_lba = little_endian_to_host<uint64_t>(lba1.data() + 40);
|
||||||
header.last_lba = little_endian_to_host<uint64_t>(lba1.data() + 48);
|
header.last_lba = little_endian_to_host<uint64_t>(lba1.data() + 48);
|
||||||
|
header.guid = parse_guid(lba1.data() + 56);
|
||||||
header.partition_entry_lba = little_endian_to_host<uint64_t>(lba1.data() + 72);
|
header.partition_entry_lba = little_endian_to_host<uint64_t>(lba1.data() + 72);
|
||||||
header.partition_entry_count = little_endian_to_host<uint32_t>(lba1.data() + 80);
|
header.partition_entry_count = little_endian_to_host<uint32_t>(lba1.data() + 80);
|
||||||
header.partition_entry_size = little_endian_to_host<uint32_t>(lba1.data() + 84);
|
header.partition_entry_size = little_endian_to_host<uint32_t>(lba1.data() + 84);
|
||||||
|
@ -160,7 +183,7 @@ namespace Kernel
|
||||||
bool DiskDevice::initialize_partitions()
|
bool DiskDevice::initialize_partitions()
|
||||||
{
|
{
|
||||||
BAN::Vector<uint8_t> lba1(sector_size());
|
BAN::Vector<uint8_t> lba1(sector_size());
|
||||||
if (!read(1, 1, lba1.data()))
|
if (!read_sectors(1, 1, lba1.data()))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
GPTHeader header = parse_gpt_header(lba1);
|
GPTHeader header = parse_gpt_header(lba1);
|
||||||
|
@ -170,14 +193,13 @@ namespace Kernel
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
BAN::Vector<uint8_t> entry_array;
|
uint32_t size = header.partition_entry_count * header.partition_entry_size;
|
||||||
{
|
if (uint32_t remainder = size % sector_size())
|
||||||
uint32_t bytes = header.partition_entry_count * header.partition_entry_size;
|
size += sector_size() - remainder;
|
||||||
uint32_t sectors = (bytes + sector_size() - 1) / sector_size();
|
|
||||||
MUST(entry_array.resize(sectors * sector_size()));
|
BAN::Vector<uint8_t> entry_array(size);
|
||||||
if (!read(header.partition_entry_lba, sectors, entry_array.data()))
|
if (!read_sectors(header.partition_entry_lba, size / sector_size(), entry_array.data()))
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_valid_gpt_crc32(header, lba1, entry_array))
|
if (!is_valid_gpt_crc32(header, lba1, entry_array))
|
||||||
{
|
{
|
||||||
|
@ -188,31 +210,15 @@ namespace Kernel
|
||||||
for (uint32_t i = 0; i < header.partition_entry_count; i++)
|
for (uint32_t i = 0; i < header.partition_entry_count; i++)
|
||||||
{
|
{
|
||||||
uint8_t* partition_data = entry_array.data() + header.partition_entry_size * i;
|
uint8_t* partition_data = entry_array.data() + header.partition_entry_size * i;
|
||||||
|
MUST(m_partitions.emplace_back(
|
||||||
Partition partition;
|
*this,
|
||||||
memcpy(partition.type_guid, partition_data, 16);
|
parse_guid(partition_data + 0),
|
||||||
memcpy(partition.guid, partition_data + 16, 16);
|
parse_guid(partition_data + 16),
|
||||||
memcpy(partition.name, partition_data + 56, 72);
|
little_endian_to_host<uint64_t>(partition_data + 32),
|
||||||
partition.start_lba = little_endian_to_host<uint64_t>(partition_data + 32);
|
little_endian_to_host<uint64_t>(partition_data + 40),
|
||||||
partition.end_lba = little_endian_to_host<uint64_t>(partition_data + 40);
|
little_endian_to_host<uint64_t>(partition_data + 48),
|
||||||
partition.attributes = little_endian_to_host<uint64_t>(partition_data + 48);
|
(const char*)(partition_data + 56)
|
||||||
|
));
|
||||||
MUST(m_partitions.push_back(partition));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const Partition& partition : m_partitions)
|
|
||||||
{
|
|
||||||
uint8_t zero[16] = {};
|
|
||||||
if (memcmp(partition.type_guid, zero, 16) == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
dprintln("partition:");
|
|
||||||
dprintln(" type {16H}{16H}", *(uint64_t*)partition.type_guid, *(uint64_t*)(partition.type_guid + 1));
|
|
||||||
dprintln(" guid {16H}{16H}", *(uint64_t*)partition.guid, *(uint64_t*)(partition.guid + 1));
|
|
||||||
dprintln(" start {16H}", partition.start_lba);
|
|
||||||
dprintln(" end {16H}", partition.end_lba);
|
|
||||||
dprintln(" attr {16H}", partition.attributes);
|
|
||||||
dprintln(" name {}", partition.name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -224,6 +230,24 @@ namespace Kernel
|
||||||
{
|
{
|
||||||
ASSERT(s_instance == nullptr);
|
ASSERT(s_instance == nullptr);
|
||||||
s_instance = new DiskIO();
|
s_instance = new DiskIO();
|
||||||
|
|
||||||
|
#if 1
|
||||||
|
for (DiskDevice* device : s_instance->m_devices)
|
||||||
|
{
|
||||||
|
for (auto& partition : device->partitions())
|
||||||
|
{
|
||||||
|
if (!partition.is_used())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (memcmp(&partition.type(), "\x0F\xC6\x3D\xAF\x84\x83\x47\x72\x8E\x79\x3D\x69\xD8\x47\x7D\xE4", 16) == 0)
|
||||||
|
{
|
||||||
|
auto ext2fs = MUST(Ext2FS::create(partition));
|
||||||
|
VirtualFileSystem::initialize(ext2fs->root_inode());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,9 +259,14 @@ namespace Kernel
|
||||||
|
|
||||||
DiskIO::DiskIO()
|
DiskIO::DiskIO()
|
||||||
{
|
{
|
||||||
auto add_ata_device = [this](uint16_t io_base, uint16_t ctl_base, uint8_t slave_bit)
|
try_add_device(ATADevice::create(ATA_DEVICE_PRIMARY, ATA_DEVICE_PRIMARY + 0x206, 0));
|
||||||
|
try_add_device(ATADevice::create(ATA_DEVICE_PRIMARY, ATA_DEVICE_PRIMARY + 0x206, ATA_DEVICE_SLAVE_BIT));
|
||||||
|
try_add_device(ATADevice::create(ATA_DEVICE_SECONDARY, ATA_DEVICE_SECONDARY + 0x206, 0));
|
||||||
|
try_add_device(ATADevice::create(ATA_DEVICE_SECONDARY, ATA_DEVICE_SECONDARY + 0x206, ATA_DEVICE_SLAVE_BIT));
|
||||||
|
}
|
||||||
|
|
||||||
|
void DiskIO::try_add_device(DiskDevice* device)
|
||||||
{
|
{
|
||||||
DiskDevice* device = ATADevice::create(io_base, ctl_base, slave_bit);
|
|
||||||
if (!device)
|
if (!device)
|
||||||
return;
|
return;
|
||||||
if (!device->initialize())
|
if (!device->initialize())
|
||||||
|
@ -251,11 +280,25 @@ namespace Kernel
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
MUST(m_devices.push_back(device));
|
MUST(m_devices.push_back(device));
|
||||||
};
|
}
|
||||||
add_ata_device(ATA_DEVICE_PRIMARY, ATA_DEVICE_PRIMARY + 0x206, 0);
|
|
||||||
add_ata_device(ATA_DEVICE_PRIMARY, ATA_DEVICE_PRIMARY + 0x206, ATA_DEVICE_SLAVE_BIT);
|
|
||||||
add_ata_device(ATA_DEVICE_SECONDARY, ATA_DEVICE_SECONDARY + 0x206, 0);
|
DiskDevice::Partition::Partition(DiskDevice& device, const GUID& type, const GUID& guid, uint64_t start, uint64_t end, uint64_t attr, const char* name)
|
||||||
add_ata_device(ATA_DEVICE_SECONDARY, ATA_DEVICE_SECONDARY + 0x206, ATA_DEVICE_SLAVE_BIT);
|
: m_device(device)
|
||||||
|
, m_type(type)
|
||||||
|
, m_guid(guid)
|
||||||
|
, m_lba_start(start)
|
||||||
|
, m_lba_end(end)
|
||||||
|
, m_attributes(attr)
|
||||||
|
{
|
||||||
|
memcpy(m_name, name, sizeof(m_name));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DiskDevice::Partition::read_sectors(uint32_t lba, uint32_t sector_count, uint8_t* buffer)
|
||||||
|
{
|
||||||
|
const uint32_t sectors_in_partition = m_lba_end - m_lba_start;
|
||||||
|
ASSERT(lba + sector_count < sectors_in_partition);
|
||||||
|
return m_device.read_sectors(m_lba_start + lba, sector_count, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -0,0 +1,392 @@
|
||||||
|
#include <BAN/ScopeGuard.h>
|
||||||
|
#include <BAN/StringView.h>
|
||||||
|
#include <kernel/FS/Ext2.h>
|
||||||
|
|
||||||
|
#include <kernel/kprint.h>
|
||||||
|
|
||||||
|
#define EXT2_DEBUG_PRINT 1
|
||||||
|
|
||||||
|
namespace Kernel
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace Ext2::Enum
|
||||||
|
{
|
||||||
|
|
||||||
|
enum State
|
||||||
|
{
|
||||||
|
VALID_FS = 1,
|
||||||
|
ERROR_FS = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Errors
|
||||||
|
{
|
||||||
|
ERRORS_CONTINUE = 1,
|
||||||
|
ERRORS_RO = 2,
|
||||||
|
ERRORS_PANIC = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum CreatorOS
|
||||||
|
{
|
||||||
|
OS_LINUX = 0,
|
||||||
|
OS_HURD = 1,
|
||||||
|
OS_MASIX = 2,
|
||||||
|
OS_FREEBSD = 3,
|
||||||
|
OS_LITES = 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum RevLevel
|
||||||
|
{
|
||||||
|
GOOD_OLD_REV = 0,
|
||||||
|
DYNAMIC_REV = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum FeatureCompat
|
||||||
|
{
|
||||||
|
FEATURE_COMPAT_DIR_PREALLOC = 0x0001,
|
||||||
|
FEATURE_COMPAT_IMAGIC_INODES = 0x0002,
|
||||||
|
FEATURE_COMPAT_HAS_JOURNAL = 0x0004,
|
||||||
|
FEATURE_COMPAT_EXT_ATTR = 0x0008,
|
||||||
|
FEATURE_COMPAT_RESIZE_INO = 0x0010,
|
||||||
|
FEATURE_COMPAT_DIR_INDEX = 0x0020,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum FeaturesIncompat
|
||||||
|
{
|
||||||
|
FEATURE_INCOMPAT_COMPRESSION = 0x0001,
|
||||||
|
FEATURE_INCOMPAT_FILETYPE = 0x0002,
|
||||||
|
FEATURE_INCOMPAT_RECOVER = 0x0004,
|
||||||
|
FEATURE_INCOMPAT_JOURNAL_DEV = 0x0008,
|
||||||
|
FEATURE_INCOMPAT_META_BG = 0x0010,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum FeaturesRoCompat
|
||||||
|
{
|
||||||
|
FEATURE_RO_COMPAT_SPARSE_SUPER = 0x0001,
|
||||||
|
FEATURE_RO_COMPAT_LARGE_FILE = 0x0002,
|
||||||
|
FEATURE_RO_COMPAT_BTREE_DIR = 0x0004,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum AlgoBitmap
|
||||||
|
{
|
||||||
|
LZV1_ALG = 0,
|
||||||
|
LZRW3A_ALG = 1,
|
||||||
|
GZIP_ALG = 2,
|
||||||
|
BZIP2_ALG = 3,
|
||||||
|
LZO_ALG = 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ReservedInodes
|
||||||
|
{
|
||||||
|
BAD_INO = 1,
|
||||||
|
ROOT_INO = 2,
|
||||||
|
ACL_IDX_INO = 3,
|
||||||
|
ACL_DATA_INO = 4,
|
||||||
|
BOOT_LOADER_INO = 5,
|
||||||
|
UNDEL_DIR_INO = 6,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum InodeMode
|
||||||
|
{
|
||||||
|
// -- file format --
|
||||||
|
IFSOKC = 0xC000,
|
||||||
|
IFLNK = 0xA000,
|
||||||
|
IFREG = 0x8000,
|
||||||
|
IFBLK = 0x6000,
|
||||||
|
IFDIR = 0x4000,
|
||||||
|
IFCHR = 0x2000,
|
||||||
|
IFIFO = 0x1000,
|
||||||
|
|
||||||
|
// -- process execution user/group override --
|
||||||
|
ISUID = 0x0800,
|
||||||
|
ISGID = 0x0400,
|
||||||
|
ISVTX = 0x0200,
|
||||||
|
|
||||||
|
// -- access rights --
|
||||||
|
IRUSR = 0x0100,
|
||||||
|
IWUSR = 0x0080,
|
||||||
|
IXUSR = 0x0040,
|
||||||
|
IRGRP = 0x0020,
|
||||||
|
IWGRP = 0x0010,
|
||||||
|
IXGRP = 0x0008,
|
||||||
|
IROTH = 0x0004,
|
||||||
|
IWOTH = 0x0002,
|
||||||
|
IXOTH = 0x0001,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum InodeFlags
|
||||||
|
{
|
||||||
|
SECRM_FL = 0x00000001,
|
||||||
|
UNRM_FL = 0x00000002,
|
||||||
|
COMPR_FL = 0x00000004,
|
||||||
|
SYNC_FL = 0x00000008,
|
||||||
|
IMMUTABLE_FL = 0x00000010,
|
||||||
|
APPEND_FL = 0x00000020,
|
||||||
|
NODUMP_FL = 0x00000040,
|
||||||
|
NOATIME_FL = 0x00000080,
|
||||||
|
// -- Reserved for compression usage --
|
||||||
|
DIRTY_FL = 0x00000100,
|
||||||
|
COMPRBLK_FL = 0x00000200,
|
||||||
|
NOCOMPR_FL = 0x00000400,
|
||||||
|
ECOMPR_FL = 0x00000800,
|
||||||
|
// -- End of compression flags --
|
||||||
|
BTREE_FL = 0x00001000,
|
||||||
|
INDEX_FL = 0x00001000,
|
||||||
|
IMAGIC_FL = 0x00002000,
|
||||||
|
JOURNAL_DATA_FL = 0x00004000,
|
||||||
|
RESERVED_FL = 0x80000000,
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Ext2Inode::is_directory() const
|
||||||
|
{
|
||||||
|
return m_inode.mode & Ext2::Enum::IFDIR;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Ext2Inode::is_regular_file() const
|
||||||
|
{
|
||||||
|
return m_inode.mode & Ext2::Enum::IFREG;
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<BAN::Vector<uint8_t>> Ext2Inode::read_all() const
|
||||||
|
{
|
||||||
|
return BAN::Error::from_string("not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<BAN::RefCounted<Inode>> Ext2Inode::directory_find(BAN::StringView name) const
|
||||||
|
{
|
||||||
|
if (!is_directory())
|
||||||
|
return BAN::Error::from_string("Inode is not a directory");
|
||||||
|
|
||||||
|
uint32_t data_block_count = m_inode.blocks / (2 << m_fs->superblock().log_block_size);
|
||||||
|
uint32_t data_blocks_found = 0;
|
||||||
|
|
||||||
|
for (uint32_t data_block = 0; data_block < 12 && data_blocks_found < data_block_count; data_block++)
|
||||||
|
{
|
||||||
|
if (m_inode.block[0] == 0)
|
||||||
|
continue;
|
||||||
|
data_blocks_found++;
|
||||||
|
|
||||||
|
auto inode_data = TRY(m_fs->read_block(m_inode.block[data_block]));
|
||||||
|
uintptr_t inode_data_end = (uintptr_t)inode_data.data() + inode_data.size();
|
||||||
|
|
||||||
|
uintptr_t entry_addr = (uintptr_t)inode_data.data();
|
||||||
|
while (entry_addr < inode_data_end)
|
||||||
|
{
|
||||||
|
Ext2::LinkedDirectoryEntry* entry = (Ext2::LinkedDirectoryEntry*)entry_addr;
|
||||||
|
|
||||||
|
BAN::StringView entry_name = BAN::StringView(entry->name, entry->name_len);
|
||||||
|
if (entry->inode && name == entry_name)
|
||||||
|
{
|
||||||
|
Ext2::Inode asked_inode = TRY(m_fs->read_inode(entry->inode));
|
||||||
|
return BAN::RefCounted<Inode>(new Ext2Inode(m_fs, BAN::move(asked_inode), entry_name));
|
||||||
|
}
|
||||||
|
|
||||||
|
entry_addr += entry->rec_len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return BAN::Error::from_string("Could not find the asked inode");
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<BAN::Vector<BAN::RefCounted<Inode>>> Ext2Inode::directory_inodes() const
|
||||||
|
{
|
||||||
|
if (!is_directory())
|
||||||
|
return BAN::Error::from_string("Inode is not a directory");
|
||||||
|
|
||||||
|
uint32_t data_block_count = m_inode.blocks / (2 << m_fs->superblock().log_block_size);
|
||||||
|
uint32_t data_blocks_found = 0;
|
||||||
|
|
||||||
|
BAN::Vector<BAN::RefCounted<Inode>> inodes;
|
||||||
|
|
||||||
|
// FIXME: implement indirect pointers
|
||||||
|
for (uint32_t data_block = 0; data_block < 12 && data_blocks_found < data_block_count; data_block++)
|
||||||
|
{
|
||||||
|
if (m_inode.block[0] == 0)
|
||||||
|
continue;
|
||||||
|
data_blocks_found++;
|
||||||
|
|
||||||
|
auto inode_data = TRY(m_fs->read_block(m_inode.block[data_block]));
|
||||||
|
uintptr_t inode_data_end = (uintptr_t)inode_data.data() + inode_data.size();
|
||||||
|
|
||||||
|
uintptr_t entry_addr = (uintptr_t)inode_data.data();
|
||||||
|
while (entry_addr < inode_data_end)
|
||||||
|
{
|
||||||
|
Ext2::LinkedDirectoryEntry* entry = (Ext2::LinkedDirectoryEntry*)entry_addr;
|
||||||
|
|
||||||
|
if (entry->inode)
|
||||||
|
{
|
||||||
|
BAN::StringView entry_name = BAN::StringView(entry->name, entry->name_len);
|
||||||
|
Ext2::Inode current_inode = TRY(m_fs->read_inode(entry->inode));
|
||||||
|
auto ref_counted_inode = BAN::RefCounted<Inode>(new Ext2Inode(m_fs, BAN::move(current_inode), entry_name));
|
||||||
|
TRY(inodes.push_back(BAN::move(ref_counted_inode)));
|
||||||
|
}
|
||||||
|
|
||||||
|
entry_addr += entry->rec_len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: for now we can just assert that we found everything in direct pointers
|
||||||
|
ASSERT(data_blocks_found == data_block_count);
|
||||||
|
|
||||||
|
return inodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<Ext2FS*> Ext2FS::create(DiskDevice::Partition& partition)
|
||||||
|
{
|
||||||
|
Ext2FS* ext2fs = new Ext2FS(partition);
|
||||||
|
if (ext2fs == nullptr)
|
||||||
|
return BAN::Error::from_string("Could not allocate Ext2FS");
|
||||||
|
TRY(ext2fs->initialize_superblock());
|
||||||
|
TRY(ext2fs->initialize_block_group_descriptors());
|
||||||
|
TRY(ext2fs->initialize_root_inode());
|
||||||
|
return ext2fs;
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> Ext2FS::initialize_superblock()
|
||||||
|
{
|
||||||
|
const uint32_t sector_size = m_partition.device().sector_size();
|
||||||
|
ASSERT(1024 % sector_size == 0);
|
||||||
|
|
||||||
|
// Read superblock from disk
|
||||||
|
{
|
||||||
|
uint8_t* superblock_buffer = (uint8_t*)kmalloc(1024);
|
||||||
|
if (superblock_buffer == nullptr)
|
||||||
|
return BAN::Error::from_string("Could not allocate memory for superblocks");
|
||||||
|
BAN::ScopeGuard _([superblock_buffer] { kfree(superblock_buffer); });
|
||||||
|
|
||||||
|
uint32_t lba = 1024 / sector_size;
|
||||||
|
uint32_t sector_count = 1024 / sector_size;
|
||||||
|
|
||||||
|
if (!m_partition.read_sectors(lba, sector_count, superblock_buffer))
|
||||||
|
return BAN::Error::from_string("Could not read from partition");
|
||||||
|
|
||||||
|
memcpy(&m_superblock, superblock_buffer, sizeof(Ext2::Superblock));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_superblock.magic != 0xEF53)
|
||||||
|
return BAN::Error::from_string("Not a ext2 filesystem");
|
||||||
|
|
||||||
|
if (m_superblock.rev_level < 1)
|
||||||
|
{
|
||||||
|
memset(m_superblock.__extension_start, 0, sizeof(Ext2::Superblock) - offsetof(Ext2::Superblock, Ext2::Superblock::__extension_start));
|
||||||
|
m_superblock.first_ino = 11;
|
||||||
|
m_superblock.inode_size = 128;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT(!(m_superblock.feature_incompat & Ext2::Enum::FEATURE_INCOMPAT_COMPRESSION));
|
||||||
|
//ASSERT(!(m_superblock.feature_incompat & Ext2::Enum::FEATURE_INCOMPAT_FILETYPE));
|
||||||
|
ASSERT(!(m_superblock.feature_incompat & Ext2::Enum::FEATURE_INCOMPAT_JOURNAL_DEV));
|
||||||
|
ASSERT(!(m_superblock.feature_incompat & Ext2::Enum::FEATURE_INCOMPAT_META_BG));
|
||||||
|
ASSERT(!(m_superblock.feature_incompat & Ext2::Enum::FEATURE_INCOMPAT_RECOVER));
|
||||||
|
|
||||||
|
#if EXT2_DEBUG_PRINT
|
||||||
|
dprintln("EXT2");
|
||||||
|
dprintln(" inodes {}", m_superblock.inodes_count);
|
||||||
|
dprintln(" blocks {}", m_superblock.blocks_count);
|
||||||
|
dprintln(" version {}.{}", m_superblock.rev_level, m_superblock.minor_rev_level);
|
||||||
|
dprintln(" first data at {}", m_superblock.first_data_block);
|
||||||
|
dprintln(" block size {}", 1024 << m_superblock.log_block_size);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> Ext2FS::initialize_block_group_descriptors()
|
||||||
|
{
|
||||||
|
const uint32_t sector_size = m_partition.device().sector_size();
|
||||||
|
const uint32_t block_size = 1024 << m_superblock.log_block_size;
|
||||||
|
const uint32_t sectors_per_block = block_size / sector_size;
|
||||||
|
ASSERT(block_size % sector_size == 0);
|
||||||
|
|
||||||
|
uint32_t number_of_block_groups = BAN::Math::div_round_up(m_superblock.inodes_count, m_superblock.inodes_per_group);
|
||||||
|
uint32_t number_of_block_groups_check = BAN::Math::div_round_up(m_superblock.blocks_count, m_superblock.blocks_per_group);
|
||||||
|
if (number_of_block_groups != number_of_block_groups_check)
|
||||||
|
return BAN::Error::from_string("Ambiguous number of blocks");
|
||||||
|
|
||||||
|
uint32_t block_group_descriptor_table_block = m_superblock.first_data_block + 1;
|
||||||
|
uint32_t block_group_descriptor_table_sector_count = BAN::Math::div_round_up(32u * number_of_block_groups, sector_size);
|
||||||
|
|
||||||
|
uint8_t* block_group_descriptor_table_buffer = (uint8_t*)kmalloc(block_group_descriptor_table_sector_count * sector_size);
|
||||||
|
if (block_group_descriptor_table_buffer == nullptr)
|
||||||
|
return BAN::Error::from_string("Could not allocate memory for block group descriptor table");
|
||||||
|
BAN::ScopeGuard _([block_group_descriptor_table_buffer] { kfree(block_group_descriptor_table_buffer); });
|
||||||
|
|
||||||
|
if (!m_partition.read_sectors(
|
||||||
|
block_group_descriptor_table_block * sectors_per_block,
|
||||||
|
block_group_descriptor_table_sector_count,
|
||||||
|
block_group_descriptor_table_buffer
|
||||||
|
))
|
||||||
|
return BAN::Error::from_string("Could not read from partition");
|
||||||
|
|
||||||
|
TRY(m_block_group_descriptors.resize(number_of_block_groups));
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < number_of_block_groups; i++)
|
||||||
|
{
|
||||||
|
memcpy(&m_block_group_descriptors[i], block_group_descriptor_table_buffer + 32u * i, sizeof(Ext2::BlockGroupDescriptor));
|
||||||
|
|
||||||
|
#if EXT2_DEBUG_PRINT
|
||||||
|
dprintln("block group descriptor {}", i);
|
||||||
|
dprintln(" block bitmap {}", m_block_group_descriptors[i].block_bitmap);
|
||||||
|
dprintln(" inode bitmap {}", m_block_group_descriptors[i].inode_bitmap);
|
||||||
|
dprintln(" inode table {}", m_block_group_descriptors[i].inode_table);
|
||||||
|
dprintln(" unalloc blocks {}", m_block_group_descriptors[i].free_blocks_count);
|
||||||
|
dprintln(" unalloc inodes {}", m_block_group_descriptors[i].free_inodes_count);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> Ext2FS::initialize_root_inode()
|
||||||
|
{
|
||||||
|
m_root_inode = BAN::RefCounted<Inode>(new Ext2Inode(this, TRY(read_inode(Ext2::Enum::ROOT_INO)), ""));
|
||||||
|
#if EXT2_DEBUG_PRINT
|
||||||
|
dprintln("root inode:");
|
||||||
|
dprintln(" created {}", ext2_root_inode().ctime);
|
||||||
|
dprintln(" modified {}", ext2_root_inode().mtime);
|
||||||
|
dprintln(" accessed {}", ext2_root_inode().atime);
|
||||||
|
#endif
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<Ext2::Inode> Ext2FS::read_inode(uint32_t inode)
|
||||||
|
{
|
||||||
|
uint32_t block_size = 1024 << m_superblock.log_block_size;
|
||||||
|
|
||||||
|
uint32_t inode_block_group = (inode - 1) / m_superblock.inodes_per_group;
|
||||||
|
uint32_t local_inode_index = (inode - 1) % m_superblock.inodes_per_group;
|
||||||
|
|
||||||
|
uint32_t inode_table_offset_blocks = (local_inode_index * m_superblock.inode_size) / block_size;
|
||||||
|
uint32_t inode_block_offset = (local_inode_index * m_superblock.inode_size) % block_size;
|
||||||
|
|
||||||
|
uint32_t inode_block = m_block_group_descriptors[inode_block_group].inode_table + inode_table_offset_blocks;
|
||||||
|
|
||||||
|
auto inode_block_buffer = TRY(read_block(inode_block));
|
||||||
|
Ext2::Inode ext2_inode;
|
||||||
|
memcpy(&ext2_inode, inode_block_buffer.data() + inode_block_offset, sizeof(Ext2::Inode));
|
||||||
|
return ext2_inode;
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<BAN::Vector<uint8_t>> Ext2FS::read_block(uint32_t block)
|
||||||
|
{
|
||||||
|
const uint32_t sector_size = m_partition.device().sector_size();
|
||||||
|
uint32_t block_size = 1024 << m_superblock.log_block_size;
|
||||||
|
ASSERT(block_size % sector_size == 0);
|
||||||
|
uint32_t sectors_per_block = block_size / sector_size;
|
||||||
|
|
||||||
|
BAN::Vector<uint8_t> block_buffer;
|
||||||
|
TRY(block_buffer.resize(block_size));
|
||||||
|
|
||||||
|
if (!m_partition.read_sectors(block * sectors_per_block, sectors_per_block, block_buffer.data()))
|
||||||
|
return BAN::Error::from_string("Could not read from partition");
|
||||||
|
|
||||||
|
return block_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Ext2::Inode& Ext2FS::ext2_root_inode() const
|
||||||
|
{
|
||||||
|
return reinterpret_cast<const Ext2Inode*>(m_root_inode.operator->())->m_inode;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
#include <kernel/FS/VirtualFileSystem.h>
|
||||||
|
|
||||||
|
namespace Kernel
|
||||||
|
{
|
||||||
|
|
||||||
|
static VirtualFileSystem* s_instance = nullptr;
|
||||||
|
|
||||||
|
void VirtualFileSystem::initialize(BAN::RefCounted<Inode> root_inode)
|
||||||
|
{
|
||||||
|
ASSERT(s_instance == nullptr);
|
||||||
|
s_instance = new VirtualFileSystem(root_inode);
|
||||||
|
ASSERT(s_instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualFileSystem& VirtualFileSystem::get()
|
||||||
|
{
|
||||||
|
ASSERT(s_instance);
|
||||||
|
return *s_instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VirtualFileSystem::is_initialized()
|
||||||
|
{
|
||||||
|
return s_instance != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -9,6 +9,8 @@
|
||||||
#include <kernel/Scheduler.h>
|
#include <kernel/Scheduler.h>
|
||||||
#include <kernel/Shell.h>
|
#include <kernel/Shell.h>
|
||||||
|
|
||||||
|
#include <kernel/FS/VirtualFileSystem.h>
|
||||||
|
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
|
||||||
#define TTY_PRINT(...) Formatter::print([this](char c) { m_tty->putchar(c); }, __VA_ARGS__)
|
#define TTY_PRINT(...) Formatter::print([this](char c) { m_tty->putchar(c); }, __VA_ARGS__)
|
||||||
|
@ -144,10 +146,7 @@ argument_done:
|
||||||
else if (arguments.front() == "date")
|
else if (arguments.front() == "date")
|
||||||
{
|
{
|
||||||
if (arguments.size() != 1)
|
if (arguments.size() != 1)
|
||||||
{
|
return TTY_PRINTLN("'date' does not support command line arguments");
|
||||||
TTY_PRINTLN("'date' does not support command line arguments");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto time = RTC::get_current_time();
|
auto time = RTC::get_current_time();
|
||||||
TTY_PRINTLN("{}", time);
|
TTY_PRINTLN("{}", time);
|
||||||
}
|
}
|
||||||
|
@ -155,7 +154,7 @@ argument_done:
|
||||||
{
|
{
|
||||||
if (arguments.size() > 1)
|
if (arguments.size() > 1)
|
||||||
{
|
{
|
||||||
TTY_PRINT("{}", arguments[1]);
|
return TTY_PRINT("{}", arguments[1]);
|
||||||
for (size_t i = 2; i < arguments.size(); i++)
|
for (size_t i = 2; i < arguments.size(); i++)
|
||||||
TTY_PRINT(" {}", arguments[i]);
|
TTY_PRINT(" {}", arguments[i]);
|
||||||
}
|
}
|
||||||
|
@ -164,10 +163,7 @@ argument_done:
|
||||||
else if (arguments.front() == "clear")
|
else if (arguments.front() == "clear")
|
||||||
{
|
{
|
||||||
if (arguments.size() != 1)
|
if (arguments.size() != 1)
|
||||||
{
|
return TTY_PRINTLN("'clear' does not support command line arguments");
|
||||||
TTY_PRINTLN("'clear' does not support command line arguments");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
m_tty->clear();
|
m_tty->clear();
|
||||||
m_tty->set_cursor_position(0, 0);
|
m_tty->set_cursor_position(0, 0);
|
||||||
}
|
}
|
||||||
|
@ -205,29 +201,19 @@ argument_done:
|
||||||
else if (arguments.front() == "memory")
|
else if (arguments.front() == "memory")
|
||||||
{
|
{
|
||||||
if (arguments.size() != 1)
|
if (arguments.size() != 1)
|
||||||
{
|
return TTY_PRINTLN("'memory' does not support command line arguments");
|
||||||
TTY_PRINTLN("'memory' does not support command line arguments");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
kmalloc_dump_info();
|
kmalloc_dump_info();
|
||||||
}
|
}
|
||||||
else if (arguments.front() == "sleep")
|
else if (arguments.front() == "sleep")
|
||||||
{
|
{
|
||||||
if (arguments.size() != 1)
|
if (arguments.size() != 1)
|
||||||
{
|
return TTY_PRINTLN("'sleep' does not support command line arguments");
|
||||||
TTY_PRINTLN("'sleep' does not support command line arguments");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
PIT::sleep(5000);
|
PIT::sleep(5000);
|
||||||
TTY_PRINTLN("done");
|
|
||||||
}
|
}
|
||||||
else if (arguments.front() == "cpuinfo")
|
else if (arguments.front() == "cpuinfo")
|
||||||
{
|
{
|
||||||
if (arguments.size() != 1)
|
if (arguments.size() != 1)
|
||||||
{
|
return TTY_PRINTLN("'cpuinfo' does not support command line arguments");
|
||||||
TTY_PRINTLN("'cpuinfo' does not support command line arguments");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t ecx, edx;
|
uint32_t ecx, edx;
|
||||||
auto vendor = CPUID::get_vendor();
|
auto vendor = CPUID::get_vendor();
|
||||||
|
@ -248,17 +234,11 @@ argument_done:
|
||||||
else if (arguments.front() == "random")
|
else if (arguments.front() == "random")
|
||||||
{
|
{
|
||||||
if (arguments.size() != 1)
|
if (arguments.size() != 1)
|
||||||
{
|
return TTY_PRINTLN("'random' does not support command line arguments");
|
||||||
TTY_PRINTLN("'random' does not support command line arguments");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
uint32_t ecx, edx;
|
uint32_t ecx, edx;
|
||||||
CPUID::get_features(ecx, edx);
|
CPUID::get_features(ecx, edx);
|
||||||
if (!(ecx & CPUID::Features::ECX_RDRND))
|
if (!(ecx & CPUID::Features::ECX_RDRND))
|
||||||
{
|
return TTY_PRINTLN("cpu does not support RDRAND instruction");
|
||||||
TTY_PRINTLN("cpu does not support RDRAND instruction");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < 10; i++)
|
for (int i = 0; i < 10; i++)
|
||||||
{
|
{
|
||||||
|
@ -270,16 +250,56 @@ argument_done:
|
||||||
else if (arguments.front() == "reboot")
|
else if (arguments.front() == "reboot")
|
||||||
{
|
{
|
||||||
if (arguments.size() != 1)
|
if (arguments.size() != 1)
|
||||||
{
|
return TTY_PRINTLN("'reboot' does not support command line arguments");
|
||||||
TTY_PRINTLN("'reboot' does not support command line arguments");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
uint8_t good = 0x02;
|
uint8_t good = 0x02;
|
||||||
while (good & 0x02)
|
while (good & 0x02)
|
||||||
good = IO::inb(0x64);
|
good = IO::inb(0x64);
|
||||||
IO::outb(0x64, 0xFE);
|
IO::outb(0x64, 0xFE);
|
||||||
asm volatile("cli; hlt");
|
asm volatile("cli; hlt");
|
||||||
}
|
}
|
||||||
|
else if (arguments.front() == "ls")
|
||||||
|
{
|
||||||
|
if (!VirtualFileSystem::is_initialized())
|
||||||
|
return TTY_PRINTLN("VFS not initialized :(");
|
||||||
|
|
||||||
|
if (arguments.size() > 2)
|
||||||
|
return TTY_PRINTLN("usage: 'ls [path]'");
|
||||||
|
|
||||||
|
BAN::StringView path = (arguments.size() == 2) ? arguments[1].sv() : "/";
|
||||||
|
if (path.front() != '/')
|
||||||
|
return TTY_PRINTLN("ls currently works only with absolute paths");
|
||||||
|
path = path.substring(1);
|
||||||
|
|
||||||
|
auto directory = VirtualFileSystem::get().root_inode();
|
||||||
|
ASSERT(directory->is_directory());
|
||||||
|
|
||||||
|
if (arguments.size() == 2)
|
||||||
|
{
|
||||||
|
auto path_parts = MUST(arguments[1].sv().split('/'));
|
||||||
|
for (auto part : path_parts)
|
||||||
|
{
|
||||||
|
auto inode_or_error = directory->directory_find(part);
|
||||||
|
if (inode_or_error.is_error())
|
||||||
|
return TTY_PRINTLN("{}", inode_or_error.get_error().get_message());
|
||||||
|
directory = inode_or_error.value();
|
||||||
|
if (!directory->is_directory())
|
||||||
|
return TTY_PRINTLN("expected argument to be path to directory");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto inodes_or_error = directory->directory_inodes();
|
||||||
|
if (inodes_or_error.is_error())
|
||||||
|
return TTY_PRINTLN("{}", inodes_or_error.get_error().get_message());
|
||||||
|
auto& inodes = inodes_or_error.value();
|
||||||
|
|
||||||
|
TTY_PRINTLN("/{}", path);
|
||||||
|
for (auto& inode : inodes)
|
||||||
|
if (inode->is_directory())
|
||||||
|
TTY_PRINTLN(" {7} \e[34m{}\e[m", inode->size(), inode->name());
|
||||||
|
for (auto& inode : inodes)
|
||||||
|
if (!inode->is_directory())
|
||||||
|
TTY_PRINTLN(" {7} {}", inode->size(), inode->name());
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
TTY_PRINTLN("unrecognized command '{}'", arguments.front());
|
TTY_PRINTLN("unrecognized command '{}'", arguments.front());
|
||||||
|
|
Loading…
Reference in New Issue