Kernel: Add possibiliity to create empty files on Ext2

Big rewrite for Ext2 for more easy and optimized code
This commit is contained in:
Bananymous 2023-03-23 22:26:06 +02:00
parent 1be8b2f514
commit 75c4f35e85
6 changed files with 250 additions and 95 deletions

View File

@ -81,6 +81,8 @@ namespace Kernel
uint16_t free_blocks_count; uint16_t free_blocks_count;
uint16_t free_inodes_count; uint16_t free_inodes_count;
uint16_t used_dirs_count; uint16_t used_dirs_count;
uint8_t __padding[2];
uint8_t __reserved[12];
}; };
struct Inode struct Inode
@ -121,16 +123,18 @@ namespace Kernel
class Ext2Inode : public Inode class Ext2Inode : public Inode
{ {
public: public:
virtual uint16_t uid() const override { return m_inode.uid; } virtual uid_t uid() const override { return m_inode.uid; }
virtual uint16_t gid() const override { return m_inode.gid; } virtual gid_t gid() const override { return m_inode.gid; }
virtual uint32_t size() const override { return m_inode.size; } virtual size_t size() const override { return m_inode.size; }
virtual Mode mode() const override { return { .mode = m_inode.mode }; } virtual mode_t mode() const override { return m_inode.mode; }
virtual BAN::StringView name() const override { return m_name; } virtual BAN::StringView name() const override { return m_name; }
virtual BAN::ErrorOr<size_t> read(size_t, void*, size_t) override; virtual BAN::ErrorOr<size_t> read(size_t, void*, size_t) override;
virtual BAN::ErrorOr<void> create_file(BAN::StringView, mode_t) override;
virtual Type type() const override { return Type::Ext2; } virtual Type type() const override { return Type::Ext2; }
virtual bool operator==(const Inode& other) const override; virtual bool operator==(const Inode& other) const override;
@ -177,14 +181,23 @@ namespace Kernel
BAN::ErrorOr<void> initialize_superblock(); BAN::ErrorOr<void> initialize_superblock();
BAN::ErrorOr<void> initialize_root_inode(); BAN::ErrorOr<void> initialize_root_inode();
BAN::ErrorOr<Ext2::Inode> read_inode(uint32_t); BAN::ErrorOr<uint32_t> create_inode(const Ext2::Inode&);
BAN::ErrorOr<void> delete_inode(uint32_t);
BAN::ErrorOr<void> resize_inode(uint32_t, size_t);
BAN::ErrorOr<BAN::Vector<uint8_t>> read_block(uint32_t); BAN::ErrorOr<BAN::Vector<uint8_t>> read_block(uint32_t);
BAN::ErrorOr<void> write_block(uint32_t, BAN::Span<const uint8_t>); BAN::ErrorOr<void> write_block(uint32_t, BAN::Span<const uint8_t>);
BAN::ErrorOr<Ext2::BlockGroupDescriptor> read_block_group_descriptor(uint32_t);
const Ext2::Superblock& superblock() const { return m_superblock; } const Ext2::Superblock& superblock() const { return m_superblock; }
struct BlockLocation
{
uint32_t block;
uint32_t offset;
};
BAN::ErrorOr<BlockLocation> locate_inode(uint32_t);
BlockLocation locate_block_group_descriptior(uint32_t);
uint32_t block_size() const { return 1024 << superblock().log_block_size; } uint32_t block_size() const { return 1024 << superblock().log_block_size; }
private: private:

View File

@ -4,34 +4,32 @@
#include <BAN/String.h> #include <BAN/String.h>
#include <BAN/Vector.h> #include <BAN/Vector.h>
#include <sys/types.h>
namespace Kernel namespace Kernel
{ {
class Inode : public BAN::RefCounted<Inode> class Inode : public BAN::RefCounted<Inode>
{ {
public: public:
union Mode enum Mode : mode_t
{ {
struct IXOTH = 0x0001,
{ IWOTH = 0x0002,
uint16_t IXOTH : 1; // 0x0001 IROTH = 0x0004,
uint16_t IWOTH : 1; // 0x0002 IXGRP = 0x0008,
uint16_t IROTH : 1; // 0x0004 IWGRP = 0x0010,
uint16_t IXGRP : 1; // 0x0008 IRGRP = 0x0020,
uint16_t IWGRP : 1; // 0x0010 IXUSR = 0x0040,
uint16_t IRGRP : 1; // 0x0020 IWUSR = 0x0080,
uint16_t IXUSR : 1; // 0x0040 IRUSR = 0x0100,
uint16_t IWUSR : 1; // 0x0080 ISVTX = 0x0200,
uint16_t IRUSR : 1; // 0x0100 ISGID = 0x0400,
uint16_t ISVTX : 1; // 0x0200 ISUID = 0x0800,
uint16_t ISGID : 1; // 0x0400 IFIFO = 0x1000,
uint16_t ISUID : 1; // 0x0800 IFCHR = 0x2000,
uint16_t IFIFO : 1; // 0x1000 IFDIR = 0x4000,
uint16_t IFCHR : 1; // 0x2000 IFREG = 0x8000,
uint16_t IFDIR : 1; // 0x4000
uint16_t IFREG : 1; // 0x8000
};
uint16_t mode;
}; };
enum class Type enum class Type
@ -42,14 +40,14 @@ namespace Kernel
public: public:
virtual ~Inode() {} virtual ~Inode() {}
bool ifdir() const { return mode().IFDIR; } bool ifdir() const { return mode() & Mode::IFDIR; }
bool ifreg() const { return mode().IFREG; } bool ifreg() const { return mode() & Mode::IFREG; }
virtual uint16_t uid() const = 0; virtual uid_t uid() const = 0;
virtual uint16_t gid() const = 0; virtual gid_t gid() const = 0;
virtual uint32_t size() const = 0; virtual size_t size() const = 0;
virtual Mode mode() const = 0; virtual mode_t mode() const = 0;
virtual BAN::StringView name() const = 0; virtual BAN::StringView name() const = 0;
@ -58,6 +56,8 @@ namespace Kernel
virtual BAN::ErrorOr<size_t> read(size_t, void*, size_t) = 0; virtual BAN::ErrorOr<size_t> read(size_t, void*, size_t) = 0;
virtual BAN::ErrorOr<void> create_file(BAN::StringView, mode_t) = 0;
virtual Type type() const = 0; virtual Type type() const = 0;
virtual bool operator==(const Inode&) const = 0; virtual bool operator==(const Inode&) const = 0;

View File

@ -29,6 +29,7 @@ namespace Kernel
BAN::ErrorOr<int> open(BAN::StringView, int); BAN::ErrorOr<int> open(BAN::StringView, int);
BAN::ErrorOr<void> close(int); BAN::ErrorOr<void> close(int);
BAN::ErrorOr<size_t> read(int, void*, size_t); BAN::ErrorOr<size_t> read(int, void*, size_t);
BAN::ErrorOr<void> creat(BAN::StringView, mode_t);
BAN::StringView working_directory() const { return m_working_directory; } BAN::StringView working_directory() const { return m_working_directory; }
BAN::ErrorOr<void> set_working_directory(BAN::StringView); BAN::ErrorOr<void> set_working_directory(BAN::StringView);

View File

@ -1,8 +1,10 @@
#include <BAN/ScopeGuard.h> #include <BAN/ScopeGuard.h>
#include <BAN/StringView.h> #include <BAN/StringView.h>
#include <kernel/FS/Ext2.h> #include <kernel/FS/Ext2.h>
#include <kernel/RTC.h>
#define EXT2_DEBUG_PRINT 0 #define EXT2_DEBUG_PRINT 0
#define VERIFY_INODE_EXISTANCE 1
namespace Kernel namespace Kernel
{ {
@ -134,12 +136,26 @@ namespace Kernel
RESERVED_FL = 0x80000000, RESERVED_FL = 0x80000000,
}; };
enum FileType
{
UNKNOWN = 0,
REG_FILE = 1,
DIR = 2,
CHRDEV = 3,
BLKDEV = 4,
FIFO = 5,
SOCK = 6,
SYMLINK = 7,
};
} }
BAN::ErrorOr<BAN::RefPtr<Inode>> Ext2Inode::create(Ext2FS& fs, uint32_t inode, BAN::StringView name) BAN::ErrorOr<BAN::RefPtr<Inode>> Ext2Inode::create(Ext2FS& fs, uint32_t inode_inode, BAN::StringView name)
{ {
Ext2::Inode ext2_inode = TRY(fs.read_inode(inode)); auto inode_location = TRY(fs.locate_inode(inode_inode));
Ext2Inode* result = new Ext2Inode(fs, ext2_inode, name, inode); auto inode_buffer = TRY(fs.read_block(inode_location.block));
auto& inode = *(Ext2::Inode*)(inode_buffer.data() + inode_location.offset);
Ext2Inode* result = new Ext2Inode(fs, inode, name, inode_inode);
if (result == nullptr) if (result == nullptr)
return BAN::Error::from_errno(ENOMEM); return BAN::Error::from_errno(ENOMEM);
return BAN::RefPtr<Inode>::adopt(result); return BAN::RefPtr<Inode>::adopt(result);
@ -250,6 +266,87 @@ namespace Kernel
return n_read; return n_read;
} }
BAN::ErrorOr<void> Ext2Inode::create_file(BAN::StringView name, mode_t mode)
{
if (!ifdir())
return BAN::Error::from_errno(ENOTDIR);
if (name.size() > 255)
return BAN::Error::from_errno(ENAMETOOLONG);
auto error_or = directory_find_impl(name);
if (!error_or.is_error())
return BAN::Error::from_errno(EEXISTS);
if (error_or.error().get_error_code() != ENOENT)
return error_or.error();
uint64_t current_time = RTC::get_unix_time();
Ext2::Inode ext2_inode;
ext2_inode.mode = mode;
ext2_inode.uid = 0;
ext2_inode.size = 0;
ext2_inode.atime = current_time;
ext2_inode.ctime = current_time;
ext2_inode.mtime = current_time;
ext2_inode.dtime = current_time;
ext2_inode.gid = 0;
ext2_inode.links_count = 1;
ext2_inode.blocks = 0;
ext2_inode.flags = 0;
ext2_inode.osd1 = 0;
memset(ext2_inode.block, 0, sizeof(ext2_inode.block));
ext2_inode.generation = 0;
ext2_inode.file_acl = 0;
ext2_inode.dir_acl = 0;
ext2_inode.faddr = 0;
memset(ext2_inode.osd2, 0, sizeof(ext2_inode.osd2));
uint32_t inode_index = TRY(m_fs.create_inode(ext2_inode));
// Insert inode to this directory
uint32_t data_block_count = m_inode.blocks / (2 << m_fs.superblock().log_block_size);
uint32_t block_index = TRY(data_block_index(data_block_count - 1));
auto block_data = TRY(m_fs.read_block(block_index));
const uint8_t* block_data_end = block_data.data() + block_data.size();
const uint8_t* entry_addr = block_data.data();
uint32_t needed_entry_len = sizeof(Ext2::LinkedDirectoryEntry) + name.size();
bool insered = false;
while (entry_addr < block_data_end)
{
auto& entry = *(Ext2::LinkedDirectoryEntry*)entry_addr;
if (needed_entry_len <= entry.rec_len - entry.name_len - sizeof(Ext2::LinkedDirectoryEntry))
{
entry.rec_len = sizeof(Ext2::LinkedDirectoryEntry) + entry.name_len;
if (uint32_t rem = entry.rec_len % 4)
entry.rec_len += 4 - rem;
auto& new_entry = *(Ext2::LinkedDirectoryEntry*)(entry_addr + entry.rec_len);
new_entry.inode = inode_index;
new_entry.rec_len = block_data_end - (uint8_t*)&new_entry;
new_entry.name_len = name.size();
new_entry.file_type = Ext2::Enum::REG_FILE;
memcpy(new_entry.name, name.data(), name.size());
TRY(m_fs.write_block(block_index, block_data.span()));
insered = true;
break;
}
entry_addr += entry.rec_len;
}
// FIXME: If an entry cannot completely fit in one block, it must be pushed to the
// next data block and the rec_len of the previous entry properly adjusted.
ASSERT(insered);
return {};
}
BAN::ErrorOr<BAN::RefPtr<Inode>> Ext2Inode::directory_find_impl(BAN::StringView file_name) BAN::ErrorOr<BAN::RefPtr<Inode>> Ext2Inode::directory_find_impl(BAN::StringView file_name)
{ {
if (!ifdir()) if (!ifdir())
@ -350,7 +447,7 @@ namespace Kernel
} }
if (m_superblock.magic != 0xEF53) if (m_superblock.magic != 0xEF53)
return BAN::Error::from_c_string("Not a ext2 filesystem"); return BAN::Error::from_c_string("Not an ext2 filesystem");
if (m_superblock.rev_level < 1) if (m_superblock.rev_level < 1)
{ {
@ -359,6 +456,11 @@ namespace Kernel
m_superblock.inode_size = 128; m_superblock.inode_size = 128;
} }
uint32_t number_of_block_groups = BAN::Math::div_round_up(superblock().inodes_count, superblock().inodes_per_group);
uint32_t number_of_block_groups_check = BAN::Math::div_round_up(superblock().blocks_count, superblock().blocks_per_group);
if (number_of_block_groups != number_of_block_groups_check)
return BAN::Error::from_c_string("Ambiguous number of block groups");
ASSERT(!(m_superblock.feature_incompat & Ext2::Enum::FEATURE_INCOMPAT_COMPRESSION)); 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_FILETYPE));
ASSERT(!(m_superblock.feature_incompat & Ext2::Enum::FEATURE_INCOMPAT_JOURNAL_DEV)); ASSERT(!(m_superblock.feature_incompat & Ext2::Enum::FEATURE_INCOMPAT_JOURNAL_DEV));
@ -390,38 +492,39 @@ namespace Kernel
return {}; return {};
} }
BAN::ErrorOr<uint32_t> Ext2FS::find_free_inode_index() BAN::ErrorOr<uint32_t> Ext2FS::create_inode(const Ext2::Inode& ext2_inode)
{ {
ASSERT(false); ASSERT(ext2_inode.size == 0);
return 0;
}
BAN::ErrorOr<Ext2::Inode> Ext2FS::read_inode(uint32_t inode_index) uint32_t number_of_block_groups = BAN::Math::div_round_up(superblock().inodes_count, superblock().inodes_per_group);
{ for (uint32_t group = 0; group < number_of_block_groups; group++)
if (inode_index >= superblock().inodes_count) {
return BAN::Error::from_format("Asked to read inode {}, but only {} exist in the filesystem", inode_index, superblock().inodes_count); auto bgd_location = this->locate_block_group_descriptior(group);
auto bgd_buffer = TRY(this->read_block(bgd_location.block));
uint32_t block_size = this->block_size(); auto& bgd = *(Ext2::BlockGroupDescriptor*)(bgd_buffer.data() + bgd_location.offset);
if (bgd.free_inodes_count == 0)
continue;
uint32_t inode_block_group = (inode_index - 1) / superblock().inodes_per_group; auto inode_bitmap = TRY(read_block(bgd.inode_bitmap));
uint32_t local_inode_index = (inode_index - 1) % superblock().inodes_per_group; for (uint32_t inode_offset = 0; inode_offset < superblock().inodes_per_group; inode_offset++)
{
uint32_t byte = inode_offset / 8;
uint32_t bit = inode_offset % 8;
if ((inode_bitmap[byte] & (1 << bit)) == 0)
{
inode_bitmap[byte] |= (1 << bit);
TRY(write_block(bgd.inode_bitmap, inode_bitmap.span()));
uint32_t inode_table_byte_offset = (local_inode_index * superblock().inode_size); bgd.free_inodes_count--;
TRY(write_block(bgd_location.block, bgd_buffer.span()));
auto block_group_descriptor = TRY(read_block_group_descriptor(inode_block_group)); return group * superblock().inodes_per_group + inode_offset + 1;
}
}
}
uint32_t inode_block = block_group_descriptor.inode_table + inode_table_byte_offset / block_size; return BAN::Error::from_c_string("No free inodes available in the whole filesystem");
auto inode_block_buffer = TRY(read_block(inode_block));
Ext2::Inode ext2_inode;
memcpy(&ext2_inode, inode_block_buffer.data() + inode_table_byte_offset % block_size, sizeof(Ext2::Inode));
return ext2_inode;
}
BAN::ErrorOr<void> Ext2FS::write_inode(uint32_t inode_index, const Ext2::Inode& ext2_inode)
{
ASSERT(false);
return {};
} }
BAN::ErrorOr<BAN::Vector<uint8_t>> Ext2FS::read_block(uint32_t block) BAN::ErrorOr<BAN::Vector<uint8_t>> Ext2FS::read_block(uint32_t block)
@ -439,40 +542,60 @@ namespace Kernel
BAN::ErrorOr<void> Ext2FS::write_block(uint32_t block, BAN::Span<const uint8_t> data) BAN::ErrorOr<void> Ext2FS::write_block(uint32_t block, BAN::Span<const uint8_t> data)
{ {
const uint32_t sector_size = m_partition.device().sector_size(); uint32_t sector_size = m_partition.device().sector_size();
const uint32_t block_size = 1024 << m_superblock.log_block_size; uint32_t block_size = this->block_size();
ASSERT(block_size % sector_size == 0); uint32_t sectors_per_block = block_size / sector_size;
ASSERT(data.size() <= block_size);
const uint32_t sectors_per_block = block_size / sector_size;
ASSERT(data.size() <= block_size);
TRY(m_partition.write_sectors(block * sectors_per_block, sectors_per_block, data.data())); TRY(m_partition.write_sectors(block * sectors_per_block, sectors_per_block, data.data()));
return {}; return {};
} }
BAN::ErrorOr<Ext2::BlockGroupDescriptor> Ext2FS::read_block_group_descriptor(uint32_t index) BAN::ErrorOr<Ext2FS::BlockLocation> Ext2FS::locate_inode(uint32_t inode_index)
{ {
if (inode_index >= superblock().inodes_count)
return BAN::Error::from_format("Asked to read inode {}, but only {} exist in the filesystem", inode_index, superblock().inodes_count);
uint32_t block_size = this->block_size(); uint32_t block_size = this->block_size();
uint32_t number_of_block_groups = BAN::Math::div_round_up(superblock().inodes_count, superblock().inodes_per_group); uint32_t inode_block_group = (inode_index - 1) / superblock().inodes_per_group;
uint32_t number_of_block_groups_check = BAN::Math::div_round_up(superblock().blocks_count, superblock().blocks_per_group); uint32_t local_inode_index = (inode_index - 1) % superblock().inodes_per_group;
if (number_of_block_groups != number_of_block_groups_check)
return BAN::Error::from_c_string("Ambiguous number of block groups");
ASSERT(index < number_of_block_groups);
// NOTE: We use 32 because we cannot use the sizeof(Ext2::BlockGroupDescriptor) since I have uint32_t inode_table_byte_offset = (local_inode_index * superblock().inode_size);
// left out 14 bytes of padding/reserved memory from the end of the structure
uint32_t block_group_descriptor_byte_offset = 32u * index;
uint32_t block_group_descriptor_block = superblock().first_data_block + block_group_descriptor_byte_offset / block_size + 1; auto bgd_location = locate_block_group_descriptior(inode_block_group);
auto bgd_buffer = TRY(read_block(bgd_location.block));
auto& bgd = *(Ext2::BlockGroupDescriptor*)(bgd_buffer.data() + bgd_location.offset);
auto block_data = TRY(read_block(block_group_descriptor_block)); #if VERIFY_INODE_EXISTANCE
ASSERT(superblock().inodes_per_group <= block_size * 8);
auto inode_bitmap = TRY(read_block(bgd.inode_bitmap));
uint32_t byte = local_inode_index / 8;
uint32_t bit = local_inode_index % 8;
ASSERT(inode_bitmap[byte] & (1 << bit));
#endif
Ext2::BlockGroupDescriptor result; BlockLocation location;
memcpy(&result, block_data.data() + block_group_descriptor_byte_offset % block_size, sizeof(Ext2::BlockGroupDescriptor)); location.block = bgd.inode_table + inode_table_byte_offset / block_size;
location.offset = inode_table_byte_offset % block_size;
return location;
}
return result; Ext2FS::BlockLocation Ext2FS::locate_block_group_descriptior(uint32_t group_index)
{
uint32_t block_size = this->block_size();
uint32_t block_group_count = BAN::Math::div_round_up(superblock().inodes_count, superblock().inodes_per_group);
ASSERT(group_index < block_group_count);
uint32_t bgd_byte_offset = sizeof(Ext2::BlockGroupDescriptor) * group_index;
uint32_t bgd_table_block = superblock().first_data_block + (bgd_byte_offset / block_size) + 1;
BlockLocation location;
location.block = bgd_table_block + (bgd_byte_offset / block_size);
location.offset = (bgd_byte_offset % block_size);
return location;
} }
} }

View File

@ -72,6 +72,18 @@ namespace Kernel
return n_read; return n_read;
} }
BAN::ErrorOr<void> Process::creat(BAN::StringView path, mode_t mode)
{
auto absolute_path = TRY(absolute_path_of(path));
while (absolute_path.sv().back() != '/')
absolute_path.pop_back();
auto parent_inode = TRY(VirtualFileSystem::get().file_from_absolute_path(absolute_path));
if (path.count('/') > 0)
return BAN::Error::from_c_string("You can only create files to current working directory");
TRY(parent_inode.inode->create_file(path, mode));
return {};
}
Inode& Process::inode_for_fd(int fd) Inode& Process::inode_for_fd(int fd)
{ {
MUST(validate_fd(fd)); MUST(validate_fd(fd));

View File

@ -364,19 +364,19 @@ argument_done:
auto& directory = Process::current()->inode_for_fd(fd); auto& directory = Process::current()->inode_for_fd(fd);
auto inodes = TRY(directory.directory_inodes()); auto inodes = TRY(directory.directory_inodes());
auto mode_string = [](Inode::Mode mode) auto mode_string = [](mode_t mode)
{ {
static char buffer[11] {}; static char buffer[11] {};
buffer[0] = mode.IFDIR ? 'd' : '-'; buffer[0] = (mode & Inode::Mode::IFDIR) ? 'd' : '-';
buffer[1] = mode.IRUSR ? 'r' : '-'; buffer[1] = (mode & Inode::Mode::IRUSR) ? 'r' : '-';
buffer[2] = mode.IWUSR ? 'w' : '-'; buffer[2] = (mode & Inode::Mode::IWUSR) ? 'w' : '-';
buffer[3] = mode.IXUSR ? 'x' : '-'; buffer[3] = (mode & Inode::Mode::IXUSR) ? 'x' : '-';
buffer[4] = mode.IRGRP ? 'r' : '-'; buffer[4] = (mode & Inode::Mode::IRGRP) ? 'r' : '-';
buffer[5] = mode.IWGRP ? 'w' : '-'; buffer[5] = (mode & Inode::Mode::IWGRP) ? 'w' : '-';
buffer[6] = mode.IXGRP ? 'x' : '-'; buffer[6] = (mode & Inode::Mode::IXGRP) ? 'x' : '-';
buffer[7] = mode.IROTH ? 'r' : '-'; buffer[7] = (mode & Inode::Mode::IROTH) ? 'r' : '-';
buffer[8] = mode.IWOTH ? 'w' : '-'; buffer[8] = (mode & Inode::Mode::IWOTH) ? 'w' : '-';
buffer[9] = mode.IXOTH ? 'x' : '-'; buffer[9] = (mode & Inode::Mode::IXOTH) ? 'x' : '-';
return (const char*)buffer; return (const char*)buffer;
}; };
@ -413,6 +413,12 @@ argument_done:
TRY(Process::current()->set_working_directory(path)); TRY(Process::current()->set_working_directory(path));
TRY(update_prompt()); TRY(update_prompt());
} }
else if (arguments.front() == "touch")
{
if (arguments.size() != 2)
return BAN::Error::from_c_string("usage 'touch path'");
TRY(Process::current()->creat(arguments[1], 0));
}
else if (arguments.front() == "cksum") else if (arguments.front() == "cksum")
{ {
if (arguments.size() < 2) if (arguments.size() < 2)