Compare commits

...

11 Commits

Author SHA1 Message Date
Bananymous 1ec341e2dd rm: add option to remove recursively 2023-10-26 02:32:49 +03:00
Bananymous d09310f388 Kernel: Fix ext2 inode deletion
fsck now reports clean filesystem even after deleting files
2023-10-26 02:05:05 +03:00
Bananymous 126edea119 Userspace: implement basic rm command 2023-10-25 21:45:27 +03:00
Bananymous 74bfb930f2 Kernel/LibC: Add syscall and wrapper for unlink 2023-10-25 21:45:04 +03:00
Bananymous 091c5b6a66 BAN: Implement Ext2 file unlinking
Ext2 inodes can now be unlinked from directories and after last
inode closes (destructor gets called) we check if link count is 0
and cleanup the inode from filesystem
2023-10-25 21:43:36 +03:00
Bananymous fda4a4ad24 BAN: ByteSpan can be sliced without specified size
This will give span with all remaining size after offset
2023-10-25 21:41:11 +03:00
Bananymous 8bb47aee02 Kernel/LibC/Userspace: Implement mkdir and creat
Touch now uses creat insteadd of open with O_CREAT flag
2023-10-25 21:07:26 +03:00
Bananymous 1f794e4ac0 Kernel: Implement directory creation for RamFS 2023-10-25 19:41:34 +03:00
Bananymous e85f9ac6a1 Kernel: Implement Ext2 directory creation 2023-10-25 19:37:04 +03:00
Bananymous 6ee5576dcc Kernel: Add Inode API for creating directories 2023-10-25 19:36:04 +03:00
Bananymous b890e2fc14 Kernel: Ext2FS now uses Ext2Inodes as cached values 2023-10-25 19:34:00 +03:00
24 changed files with 685 additions and 75 deletions

View File

@ -87,9 +87,12 @@ namespace BAN
return *reinterpret_cast<S*>(m_data);
}
ByteSpanGeneral slice(size_type offset, size_type length)
ByteSpanGeneral slice(size_type offset, size_type length = size_type(-1))
{
ASSERT(m_data);
ASSERT(m_size >= offset);
if (length == size_type(-1))
length = m_size - offset;
ASSERT(m_size >= offset + length);
return ByteSpanGeneral(m_data + offset, length);
}

View File

@ -23,7 +23,7 @@ add_subdirectory(LibELF)
add_subdirectory(userspace)
add_custom_target(sysroot
COMMAND mkdir -p ${BANAN_SYSROOT}
COMMAND ${CMAKE_COMMAND} -E make_directory ${BANAN_SYSROOT}
COMMAND cd ${BANAN_SYSROOT} && sudo tar xf ${BANAN_BASE_SYSROOT}
USES_TERMINAL
)

View File

@ -58,7 +58,7 @@ namespace Kernel
BAN::ErrorOr<void> initialize_root_inode();
BAN::ErrorOr<uint32_t> create_inode(const Ext2::Inode&);
BAN::ErrorOr<void> delete_inode(uint32_t);
void delete_inode(uint32_t ino);
BAN::ErrorOr<void> resize_inode(uint32_t, size_t);
void read_block(uint32_t, BlockBufferWrapper&);
@ -68,8 +68,9 @@ namespace Kernel
BlockBufferWrapper get_block_buffer();
BAN::ErrorOr<uint32_t> reserve_free_block(uint32_t primary_bgd);
void release_block(uint32_t block);
BAN::HashMap<ino_t, BAN::RefPtr<Inode>>& inode_cache() { return m_inode_cache; }
BAN::HashMap<ino_t, BAN::RefPtr<Ext2Inode>>& inode_cache() { return m_inode_cache; }
const Ext2::Superblock& superblock() const { return m_superblock; }
@ -110,7 +111,7 @@ namespace Kernel
BAN::RefPtr<Inode> m_root_inode;
BAN::Vector<uint32_t> m_superblock_backups;
BAN::HashMap<ino_t, BAN::RefPtr<Inode>> m_inode_cache;
BAN::HashMap<ino_t, BAN::RefPtr<Ext2Inode>> m_inode_cache;
BlockBufferManager m_buffer_manager;

View File

@ -13,6 +13,8 @@ namespace Kernel
class Ext2Inode final : public Inode
{
public:
~Ext2Inode();
virtual ino_t ino() const override { return m_ino; };
virtual Mode mode() const override { return { m_inode.mode }; }
virtual nlink_t nlink() const override { return m_inode.links_count; }
@ -31,6 +33,8 @@ namespace Kernel
virtual BAN::ErrorOr<BAN::RefPtr<Inode>> find_inode_impl(BAN::StringView) override;
virtual BAN::ErrorOr<void> list_next_inodes_impl(off_t, DirectoryEntryList*, 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<BAN::String> link_target_impl() override;
@ -42,8 +46,14 @@ namespace Kernel
private:
uint32_t fs_block_of_data_block_index(uint32_t data_block_index);
BAN::ErrorOr<void> link_inode_to_directory(Ext2Inode&, BAN::StringView name);
BAN::ErrorOr<bool> is_directory_empty();
BAN::ErrorOr<void> cleanup_default_links();
void cleanup_from_fs();
BAN::ErrorOr<uint32_t> allocate_new_block();
BAN::ErrorOr<void> sync();
void sync();
uint32_t block_group() const;
@ -53,7 +63,7 @@ namespace Kernel
, m_inode(inode)
, m_ino(ino)
{}
static BAN::ErrorOr<BAN::RefPtr<Inode>> create(Ext2FS&, uint32_t);
static BAN::ErrorOr<BAN::RefPtr<Ext2Inode>> create(Ext2FS&, uint32_t);
private:
Ext2FS& m_fs;

View File

@ -90,7 +90,8 @@ namespace Kernel
BAN::ErrorOr<BAN::RefPtr<Inode>> find_inode(BAN::StringView);
BAN::ErrorOr<void> list_next_inodes(off_t, DirectoryEntryList*, size_t);
BAN::ErrorOr<void> create_file(BAN::StringView, mode_t, uid_t, gid_t);
BAN::ErrorOr<void> delete_inode(BAN::StringView);
BAN::ErrorOr<void> create_directory(BAN::StringView, mode_t, uid_t, gid_t);
BAN::ErrorOr<void> unlink(BAN::StringView);
// Link API
BAN::ErrorOr<BAN::String> link_target();
@ -107,7 +108,8 @@ namespace Kernel
virtual BAN::ErrorOr<BAN::RefPtr<Inode>> find_inode_impl(BAN::StringView) { return BAN::Error::from_errno(ENOTSUP); }
virtual BAN::ErrorOr<void> list_next_inodes_impl(off_t, DirectoryEntryList*, size_t) { return BAN::Error::from_errno(ENOTSUP); }
virtual BAN::ErrorOr<void> create_file_impl(BAN::StringView, mode_t, uid_t, gid_t) { return BAN::Error::from_errno(ENOTSUP); }
virtual BAN::ErrorOr<void> delete_inode_impl(BAN::StringView) { return BAN::Error::from_errno(ENOTSUP); }
virtual BAN::ErrorOr<void> create_directory_impl(BAN::StringView, mode_t, uid_t, gid_t) { return BAN::Error::from_errno(ENOTSUP); }
virtual BAN::ErrorOr<void> unlink_impl(BAN::StringView) { return BAN::Error::from_errno(ENOTSUP); }
// Link API
virtual BAN::ErrorOr<BAN::String> link_target_impl() { return BAN::Error::from_errno(ENOTSUP); }

View File

@ -96,7 +96,8 @@ namespace Kernel
virtual BAN::ErrorOr<BAN::RefPtr<Inode>> find_inode_impl(BAN::StringView) override;
virtual BAN::ErrorOr<void> list_next_inodes_impl(off_t, DirectoryEntryList*, size_t) override;
virtual BAN::ErrorOr<void> create_file_impl(BAN::StringView, mode_t, uid_t, gid_t) override;
virtual BAN::ErrorOr<void> delete_inode_impl(BAN::StringView) 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;
private:
static constexpr size_t m_name_max = NAME_MAX;

View File

@ -89,13 +89,16 @@ namespace Kernel
BAN::ErrorOr<long> sys_getegid() const { return m_credentials.egid(); }
BAN::ErrorOr<long> sys_getpgid(pid_t);
BAN::ErrorOr<void> create_file(BAN::StringView name, mode_t mode);
BAN::ErrorOr<void> create_file_or_dir(BAN::StringView name, mode_t mode);
BAN::ErrorOr<long> open_file(BAN::StringView path, int, mode_t = 0);
BAN::ErrorOr<long> sys_open(const char* path, int, mode_t);
BAN::ErrorOr<long> sys_openat(int, const char* path, int, mode_t);
BAN::ErrorOr<long> sys_close(int fd);
BAN::ErrorOr<long> sys_read(int fd, void* buffer, size_t count);
BAN::ErrorOr<long> sys_write(int fd, const void* buffer, size_t count);
BAN::ErrorOr<long> sys_create(const char*, mode_t);
BAN::ErrorOr<long> sys_create_dir(const char*, mode_t);
BAN::ErrorOr<long> sys_unlink(const char*);
BAN::ErrorOr<long> sys_chmod(const char*, mode_t);

View File

@ -184,6 +184,8 @@ namespace Kernel
write_block(bgd->inode_bitmap, inode_bitmap);
bgd->free_inodes_count--;
if (Inode::Mode(ext2_inode.mode).ifdir())
bgd->used_dirs_count++;
write_block(bgd_location.block, bgd_buffer);
const uint32_t inode_table_offset = ino_index * superblock().inode_size;
@ -212,6 +214,55 @@ namespace Kernel
return BAN::Error::from_error_code(ErrorCode::Ext2_Corrupted);
}
void Ext2FS::delete_inode(uint32_t ino)
{
LockGuard _(m_lock);
ASSERT(ino >= superblock().first_ino);
ASSERT(ino <= superblock().inodes_count);
auto bgd_buffer = get_block_buffer();
auto bitmap_buffer = get_block_buffer();
auto inode_buffer = get_block_buffer();
const uint32_t inode_group = (ino - 1) / superblock().inodes_per_group;
const uint32_t inode_index = (ino - 1) % superblock().inodes_per_group;
auto bgd_location = locate_block_group_descriptior(inode_group);
read_block(bgd_location.block, bgd_buffer);
auto& bgd = bgd_buffer.span().slice(bgd_location.offset).as<Ext2::BlockGroupDescriptor>();
// update inode bitmap
read_block(bgd.inode_bitmap, bitmap_buffer);
const uint32_t byte = inode_index / 8;
const uint32_t bit = inode_index % 8;
ASSERT(bitmap_buffer[byte] & (1 << bit));
bitmap_buffer[byte] &= ~(1 << bit);
write_block(bgd.inode_bitmap, bitmap_buffer);
// memset inode to zero or fsck will complain
auto inode_location = locate_inode(ino);
read_block(inode_location.block, inode_buffer);
auto& inode = inode_buffer.span().slice(inode_location.offset).as<Ext2::Inode>();
bool is_directory = Inode::Mode(inode.mode).ifdir();
memset(&inode, 0x00, m_superblock.inode_size);
write_block(inode_location.block, inode_buffer);
// update bgd counts
bgd.free_inodes_count++;
if (is_directory)
bgd.used_dirs_count--;
write_block(bgd_location.block, bgd_buffer);
// update superblock inode count
m_superblock.free_inodes_count++;
sync_superblock();
// remove inode from cache
if (m_inode_cache.contains(ino))
m_inode_cache.remove(ino);
}
void Ext2FS::read_block(uint32_t block, BlockBufferWrapper& buffer)
{
LockGuard _(m_lock);
@ -255,8 +306,7 @@ namespace Kernel
const uint32_t lba = 1024 / sector_size;
const uint32_t sector_count = BAN::Math::div_round_up<uint32_t>(superblock_bytes, sector_size);
BAN::Vector<uint8_t> superblock_buffer;
MUST(superblock_buffer.resize(sector_count * sector_size));
auto superblock_buffer = get_block_buffer();
MUST(m_partition.read_sectors(lba, sector_count, superblock_buffer.span()));
if (memcmp(superblock_buffer.data(), &m_superblock, superblock_bytes))
@ -266,7 +316,6 @@ namespace Kernel
}
}
Ext2FS::BlockBufferWrapper Ext2FS::get_block_buffer()
{
LockGuard _(m_lock);
@ -334,6 +383,39 @@ namespace Kernel
return BAN::Error::from_error_code(ErrorCode::Ext2_Corrupted);
}
void Ext2FS::release_block(uint32_t block)
{
LockGuard _(m_lock);
ASSERT(block >= m_superblock.first_data_block);
ASSERT(block < m_superblock.blocks_count);
const uint32_t block_group = (block - m_superblock.first_data_block) / m_superblock.blocks_per_group;
const uint32_t block_offset = (block - m_superblock.first_data_block) % m_superblock.blocks_per_group;
auto bgd_buffer = get_block_buffer();
auto bitmap_buffer = get_block_buffer();
auto bgd_location = locate_block_group_descriptior(block_group);
read_block(bgd_location.block, bgd_buffer);
auto& bgd = bgd_buffer.span().slice(bgd_location.offset).as<Ext2::BlockGroupDescriptor>();
read_block(bgd.block_bitmap, bitmap_buffer);
const uint32_t byte = block_offset / 8;
const uint32_t bit = block_offset % 8;
ASSERT(bitmap_buffer[byte] & (1 << bit));
bitmap_buffer[byte] &= ~(1 << bit);
write_block(bgd.block_bitmap, bitmap_buffer);
bgd.free_blocks_count++;
write_block(bgd_location.block, bgd_buffer);
m_superblock.free_blocks_count++;
sync_superblock();
}
Ext2FS::BlockLocation Ext2FS::locate_inode(uint32_t ino)
{
LockGuard _(m_lock);

View File

@ -1,4 +1,5 @@
#include <BAN/Function.h>
#include <BAN/ScopeGuard.h>
#include <kernel/FS/Ext2/FileSystem.h>
#include <kernel/FS/Ext2/Inode.h>
#include <kernel/Timer/Timer.h>
@ -21,7 +22,7 @@ namespace Kernel
return (m_ino - 1) / m_fs.superblock().blocks_per_group;
}
BAN::ErrorOr<BAN::RefPtr<Inode>> Ext2Inode::create(Ext2FS& fs, uint32_t inode_ino)
BAN::ErrorOr<BAN::RefPtr<Ext2Inode>> Ext2Inode::create(Ext2FS& fs, uint32_t inode_ino)
{
if (fs.inode_cache().contains(inode_ino))
return fs.inode_cache()[inode_ino];
@ -36,11 +37,17 @@ namespace Kernel
Ext2Inode* result_ptr = new Ext2Inode(fs, inode, inode_ino);
if (result_ptr == nullptr)
return BAN::Error::from_errno(ENOMEM);
auto result = BAN::RefPtr<Inode>::adopt(result_ptr);
auto result = BAN::RefPtr<Ext2Inode>::adopt(result_ptr);
TRY(fs.inode_cache().insert(inode_ino, result));
return result;
}
Ext2Inode::~Ext2Inode()
{
if (m_inode.links_count == 0)
cleanup_from_fs();
}
#define VERIFY_AND_READ_BLOCK(expr) do { const uint32_t block_index = expr; ASSERT(block_index); m_fs.read_block(block_index, block_buffer); } while (false)
#define VERIFY_AND_RETURN(expr) ({ const uint32_t result = expr; ASSERT(result); return result; })
@ -214,7 +221,7 @@ namespace Kernel
if (new_size < m_inode.size)
{
m_inode.size = new_size;
TRY(sync());
sync();
return {};
}
@ -237,7 +244,7 @@ namespace Kernel
}
m_inode.size = new_size;
TRY(sync());
sync();
return {};
}
@ -248,10 +255,18 @@ namespace Kernel
if (m_inode.mode == mode)
return {};
m_inode.mode = (m_inode.mode & Inode::Mode::TYPE_MASK) | mode;
TRY(sync());
sync();
return {};
}
void Ext2Inode::cleanup_from_fs()
{
ASSERT(m_inode.links_count == 0);
for (uint32_t i = 0; i < blocks(); i++)
m_fs.release_block(fs_block_of_data_block_index(i));
m_fs.delete_inode(ino());
}
BAN::ErrorOr<void> Ext2Inode::list_next_inodes_impl(off_t offset, DirectoryEntryList* list, size_t list_size)
{
ASSERT(mode().ifdir());
@ -317,7 +332,100 @@ namespace Kernel
return {};
}
static bool mode_has_valid_type(mode_t mode)
{
switch (mode & Inode::Mode::TYPE_MASK)
{
case Inode::Mode::IFIFO: return true;
case Inode::Mode::IFCHR: return true;
case Inode::Mode::IFDIR: return true;
case Inode::Mode::IFBLK: return true;
case Inode::Mode::IFREG: return true;
case Inode::Mode::IFLNK: return true;
case Inode::Mode::IFSOCK: return true;
}
return false;
}
static Ext2::Inode initialize_new_inode_info(mode_t mode, uid_t uid, gid_t gid)
{
ASSERT(mode_has_valid_type(mode));
timespec current_time = SystemTimer::get().real_time();
return Ext2::Inode
{
.mode = (uint16_t)mode,
.uid = (uint16_t)uid,
.size = 0,
.atime = (uint32_t)current_time.tv_sec,
.ctime = (uint32_t)current_time.tv_sec,
.mtime = (uint32_t)current_time.tv_sec,
.dtime = 0,
.gid = (uint16_t)gid,
.links_count = 0,
.blocks = 0,
.flags = 0,
.osd1 = 0,
.block = {},
.generation = 0,
.file_acl = 0,
.dir_acl = 0,
.faddr = 0,
.osd2 = {}
};
}
BAN::ErrorOr<void> Ext2Inode::create_file_impl(BAN::StringView name, mode_t mode, uid_t uid, gid_t gid)
{
ASSERT(this->mode().ifdir());
if (!(Mode(mode).ifreg()))
return BAN::Error::from_errno(ENOTSUP);
const uint32_t new_ino = TRY(m_fs.create_inode(initialize_new_inode_info(mode, uid, gid)));
auto inode_or_error = Ext2Inode::create(m_fs, new_ino);
if (inode_or_error.is_error())
{
m_fs.delete_inode(new_ino);
return inode_or_error.release_error();
}
auto inode = inode_or_error.release_value();
TRY(link_inode_to_directory(*inode, name));
return {};
}
BAN::ErrorOr<void> Ext2Inode::create_directory_impl(BAN::StringView name, mode_t mode, uid_t uid, gid_t gid)
{
ASSERT(this->mode().ifdir());
ASSERT(Mode(mode).ifdir());
const uint32_t new_ino = TRY(m_fs.create_inode(initialize_new_inode_info(mode, uid, gid)));
auto inode_or_error = Ext2Inode::create(m_fs, new_ino);
if (inode_or_error.is_error())
{
m_fs.delete_inode(new_ino);
return inode_or_error.release_error();
}
auto inode = inode_or_error.release_value();
BAN::ScopeGuard cleanup([&] { inode->cleanup_from_fs(); });
TRY(inode->link_inode_to_directory(*inode, "."sv));
TRY(inode->link_inode_to_directory(*this, ".."sv));
TRY(link_inode_to_directory(*inode, name));
cleanup.disable();
return {};
}
BAN::ErrorOr<void> Ext2Inode::link_inode_to_directory(Ext2Inode& inode, BAN::StringView name)
{
if (!this->mode().ifdir())
return BAN::Error::from_errno(ENOTDIR);
@ -325,9 +433,6 @@ namespace Kernel
if (name.size() > 255)
return BAN::Error::from_errno(ENAMETOOLONG);
if (!(Mode(mode).ifreg()))
return BAN::Error::from_errno(EINVAL);
if (m_inode.flags & Ext2::Enum::INDEX_FL)
{
dwarnln("file creation to indexed directory not supported");
@ -340,39 +445,13 @@ namespace Kernel
if (error_or.error().get_error_code() != ENOENT)
return error_or.error();
timespec current_time = SystemTimer::get().real_time();
Ext2::Inode ext2_inode
{
.mode = (uint16_t)mode,
.uid = (uint16_t)uid,
.size = 0,
.atime = (uint32_t)current_time.tv_sec,
.ctime = (uint32_t)current_time.tv_sec,
.mtime = (uint32_t)current_time.tv_sec,
.dtime = 0,
.gid = (uint16_t)gid,
.links_count = 1,
.blocks = 0,
.flags = 0,
.osd1 = 0,
.block = {},
.generation = 0,
.file_acl = 0,
.dir_acl = 0,
.faddr = 0,
.osd2 = {}
};
const uint32_t inode_index = TRY(m_fs.create_inode(ext2_inode));
const uint32_t block_size = m_fs.block_size();
auto block_buffer = m_fs.get_block_buffer();
auto write_inode = [&](uint32_t entry_offset, uint32_t entry_rec_len)
{
auto typed_mode = Mode(mode);
auto typed_mode = inode.mode();
uint8_t file_type = (m_fs.superblock().rev_level == Ext2::Enum::GOOD_OLD_REV) ? 0
: typed_mode.ifreg() ? Ext2::Enum::REG_FILE
: typed_mode.ifdir() ? Ext2::Enum::DIR
@ -384,11 +463,14 @@ namespace Kernel
: 0;
auto& new_entry = *(Ext2::LinkedDirectoryEntry*)(block_buffer.data() + entry_offset);
new_entry.inode = inode_index;
new_entry.inode = inode.ino();
new_entry.rec_len = entry_rec_len;
new_entry.name_len = name.size();
new_entry.file_type = file_type;
memcpy(new_entry.name, name.data(), name.size());
inode.m_inode.links_count++;
inode.sync();
};
uint32_t block_index = 0;
@ -443,6 +525,149 @@ needs_new_block:
return {};
}
BAN::ErrorOr<bool> Ext2Inode::is_directory_empty()
{
ASSERT(mode().ifdir());
if (m_inode.flags & Ext2::Enum::INDEX_FL)
{
dwarnln("deletion of indexed directory is not supported");
return BAN::Error::from_errno(ENOTSUP);
}
auto block_buffer = m_fs.get_block_buffer();
// Confirm that this doesn't contain anything else than '.' or '..'
for (uint32_t i = 0; i < blocks(); i++)
{
const uint32_t block = fs_block_of_data_block_index(i);
m_fs.read_block(block, block_buffer);
blksize_t offset = 0;
while (offset < blksize())
{
auto& entry = block_buffer.span().slice(offset).as<Ext2::LinkedDirectoryEntry>();
if (entry.inode)
{
BAN::StringView entry_name(entry.name, entry.name_len);
if (entry_name != "."sv && entry_name != ".."sv)
return false;
}
offset += entry.rec_len;
}
}
return true;
}
BAN::ErrorOr<void> Ext2Inode::cleanup_default_links()
{
ASSERT(mode().ifdir());
auto block_buffer = m_fs.get_block_buffer();
for (uint32_t i = 0; i < blocks(); i++)
{
const uint32_t block = fs_block_of_data_block_index(i);
m_fs.read_block(block, block_buffer);
bool modified = false;
blksize_t offset = 0;
while (offset < blksize())
{
auto& entry = block_buffer.span().slice(offset).as<Ext2::LinkedDirectoryEntry>();
if (entry.inode)
{
BAN::StringView entry_name(entry.name, entry.name_len);
if (entry_name == "."sv)
{
m_inode.links_count--;
sync();
}
else if (entry_name == ".."sv)
{
auto parent = TRY(Ext2Inode::create(m_fs, entry.inode));
parent->m_inode.links_count--;
parent->sync();
}
else
ASSERT_NOT_REACHED();
modified = true;
entry.inode = 0;
}
offset += entry.rec_len;
}
if (modified)
m_fs.write_block(block, block_buffer);
}
return {};
}
BAN::ErrorOr<void> Ext2Inode::unlink_impl(BAN::StringView name)
{
ASSERT(mode().ifdir());
if (m_inode.flags & Ext2::Enum::INDEX_FL)
{
dwarnln("deletion from indexed directory is not supported");
return BAN::Error::from_errno(ENOTSUP);
}
auto block_buffer = m_fs.get_block_buffer();
for (uint32_t i = 0; i < blocks(); i++)
{
const uint32_t block = fs_block_of_data_block_index(i);
m_fs.read_block(block, block_buffer);
blksize_t offset = 0;
while (offset < blksize())
{
auto& entry = block_buffer.span().slice(offset).as<Ext2::LinkedDirectoryEntry>();
if (entry.inode && name == BAN::StringView(entry.name, entry.name_len))
{
auto inode = TRY(Ext2Inode::create(m_fs, entry.inode));
if (inode->mode().ifdir())
{
if (!TRY(inode->is_directory_empty()))
return BAN::Error::from_errno(ENOTEMPTY);
TRY(inode->cleanup_default_links());
}
if (inode->nlink() == 0)
dprintln("Corrupted filesystem. Deleting inode with 0 links");
else
inode->m_inode.links_count--;
sync();
// NOTE: If this was the last link to inode we must
// remove it from inode cache to trigger cleanup
if (inode->nlink() == 0)
{
auto& cache = m_fs.inode_cache();
if (cache.contains(inode->ino()))
cache.remove(inode->ino());
}
// FIXME: This should expand the last inode if exists
entry.inode = 0;
m_fs.write_block(block, block_buffer);
}
offset += entry.rec_len;
}
}
return {};
}
#define READ_OR_ALLOCATE_BASE_BLOCK(index_) \
do { \
if (m_inode.block[index_] != 0) \
@ -496,7 +721,7 @@ needs_new_block:
{
if (mode().ifdir())
m_inode.size += blksize();
MUST(sync());
sync();
};
// direct block
@ -552,7 +777,7 @@ needs_new_block:
#undef READ_OR_ALLOCATE_INDIRECT_BLOCK
#undef WRITE_BLOCK_AND_RETURN
BAN::ErrorOr<void> Ext2Inode::sync()
void Ext2Inode::sync()
{
auto inode_location = m_fs.locate_inode(ino());
auto block_buffer = m_fs.get_block_buffer();
@ -563,8 +788,6 @@ needs_new_block:
memcpy(block_buffer.data() + inode_location.offset, &m_inode, sizeof(Ext2::Inode));
m_fs.write_block(inode_location.block, block_buffer);
}
return {};
}
BAN::ErrorOr<BAN::RefPtr<Inode>> Ext2Inode::find_inode_impl(BAN::StringView file_name)
@ -589,7 +812,7 @@ needs_new_block:
const auto& entry = *(const Ext2::LinkedDirectoryEntry*)entry_addr;
BAN::StringView entry_name(entry.name, entry.name_len);
if (entry.inode && entry_name == file_name)
return TRY(Ext2Inode::create(m_fs, entry.inode));
return BAN::RefPtr<Inode>(TRY(Ext2Inode::create(m_fs, entry.inode)));
entry_addr += entry.rec_len;
}
}

View File

@ -81,16 +81,31 @@ namespace Kernel
Thread::TerminateBlocker blocker(Thread::current());
if (!this->mode().ifdir())
return BAN::Error::from_errno(ENOTDIR);
if (Mode(mode).ifdir())
return BAN::Error::from_errno(EINVAL);
return create_file_impl(name, mode, uid, gid);
}
BAN::ErrorOr<void> Inode::delete_inode(BAN::StringView name)
BAN::ErrorOr<void> Inode::create_directory(BAN::StringView name, mode_t mode, uid_t uid, gid_t gid)
{
LockGuard _(m_lock);
Thread::TerminateBlocker blocker(Thread::current());
if (!this->mode().ifdir())
return BAN::Error::from_errno(ENOTDIR);
if (!Mode(mode).ifdir())
return BAN::Error::from_errno(EINVAL);
return create_directory_impl(name, mode, uid, gid);
}
BAN::ErrorOr<void> Inode::unlink(BAN::StringView name)
{
LockGuard _(m_lock);
Thread::TerminateBlocker blocker(Thread::current());
if (!mode().ifdir())
return BAN::Error::from_errno(ENOTDIR);
return delete_inode_impl(name);
if (name == "."sv || name == ".."sv)
return BAN::Error::from_errno(EINVAL);
return unlink_impl(name);
}
BAN::ErrorOr<BAN::String> Inode::link_target()

View File

@ -40,7 +40,7 @@ namespace Kernel
void ProcFileSystem::on_process_delete(Process& process)
{
auto path = BAN::String::formatted("{}", process.pid());
MUST(m_root_inode->delete_inode(path));
MUST(m_root_inode->unlink(path));
}
}

View File

@ -202,16 +202,23 @@ namespace Kernel
BAN::RefPtr<RamInode> inode;
if (Mode(mode).ifreg())
inode = TRY(RamFileInode::create(m_fs, mode & ~Inode::Mode::TYPE_MASK, uid, gid));
else if (Mode(mode).ifdir())
inode = TRY(RamDirectoryInode::create(m_fs, ino(), mode & ~Inode::Mode::TYPE_MASK, uid, gid));
else
ASSERT_NOT_REACHED();
return BAN::Error::from_errno(ENOTSUP);
TRY(add_inode(name, inode));
return {};
}
BAN::ErrorOr<void> RamDirectoryInode::create_directory_impl(BAN::StringView name, mode_t mode, uid_t uid, gid_t gid)
{
if (!Mode(mode).ifdir())
return BAN::Error::from_errno(EINVAL);
auto inode = TRY(RamDirectoryInode::create(m_fs, ino(), mode & ~Inode::Mode::TYPE_MASK, uid, gid));
TRY(add_inode(name, inode));
return {};
}
static uint8_t get_type(Inode::Mode mode)
{
if (mode.ifreg())
@ -256,8 +263,9 @@ namespace Kernel
return {};
}
BAN::ErrorOr<void> RamDirectoryInode::delete_inode_impl(BAN::StringView name)
BAN::ErrorOr<void> RamDirectoryInode::unlink_impl(BAN::StringView name)
{
// FIXME: delete inodes contents only after they are closed
for (size_t i = 0; i < m_entries.size(); i++)
{
if (name == m_entries[i].name)

View File

@ -611,8 +611,17 @@ namespace Kernel
return 0;
}
BAN::ErrorOr<void> Process::create_file(BAN::StringView path, mode_t mode)
BAN::ErrorOr<void> Process::create_file_or_dir(BAN::StringView path, mode_t mode)
{
switch (mode & Inode::Mode::TYPE_MASK)
{
case Inode::Mode::IFREG: break;
case Inode::Mode::IFDIR: break;
case Inode::Mode::IFIFO: break;
default:
return BAN::Error::from_errno(EINVAL);
}
LockGuard _(m_lock);
auto absolute_path = TRY(absolute_path_of(path));
@ -625,8 +634,12 @@ namespace Kernel
auto directory = absolute_path.sv().substring(0, index);
auto file_name = absolute_path.sv().substring(index);
auto parent_inode = TRY(VirtualFileSystem::get().file_from_absolute_path(m_credentials, directory, O_WRONLY)).inode;
TRY(parent_inode->create_file(file_name, S_IFREG | (mode & 0777), m_credentials.euid(), m_credentials.egid()));
auto parent_inode = TRY(VirtualFileSystem::get().file_from_absolute_path(m_credentials, directory, O_EXEC | O_WRONLY)).inode;
if (Inode::Mode(mode).ifdir())
TRY(parent_inode->create_directory(file_name, mode, m_credentials.euid(), m_credentials.egid()));
else
TRY(parent_inode->create_file(file_name, mode, m_credentials.euid(), m_credentials.egid()));
return {};
}
@ -672,7 +685,7 @@ namespace Kernel
if (file_or_error.is_error())
{
if (file_or_error.error().get_error_code() == ENOENT)
TRY(create_file(path, mode));
TRY(create_file_or_dir(path, Inode::Mode::IFREG | mode));
else
return file_or_error.release_error();
}
@ -733,6 +746,42 @@ namespace Kernel
return TRY(m_open_file_descriptors.write(fd, BAN::ByteSpan((uint8_t*)buffer, count)));
}
BAN::ErrorOr<long> Process::sys_create(const char* path, mode_t mode)
{
LockGuard _(m_lock);
validate_string_access(path);
TRY(create_file_or_dir(path, mode));
return 0;
}
BAN::ErrorOr<long> Process::sys_create_dir(const char* path, mode_t mode)
{
LockGuard _(m_lock);
validate_string_access(path);
TRY(create_file_or_dir(path, Inode::Mode::IFDIR | mode));
return 0;
}
BAN::ErrorOr<long> Process::sys_unlink(const char* path)
{
LockGuard _(m_lock);
validate_string_access(path);
auto absolute_path = TRY(absolute_path_of(path));
size_t index = absolute_path.size();
for (; index > 0; index--)
if (absolute_path[index - 1] == '/')
break;
auto directory = absolute_path.sv().substring(0, index);
auto file_name = absolute_path.sv().substring(index);
auto parent = TRY(VirtualFileSystem::get().file_from_absolute_path(m_credentials, directory, O_EXEC | O_WRONLY)).inode;
TRY(parent->unlink(file_name));
return 0;
}
BAN::ErrorOr<long> Process::sys_chmod(const char* path, mode_t mode)
{
if (mode & S_IFMASK)

View File

@ -202,6 +202,15 @@ namespace Kernel
case SYS_CHMOD:
ret = Process::current().sys_chmod((const char*)arg1, (mode_t)arg2);
break;
case SYS_CREATE:
ret = Process::current().sys_create((const char*)arg1, (mode_t)arg2);
break;
case SYS_CREATE_DIR:
ret = Process::current().sys_create_dir((const char*)arg1, (mode_t)arg2);
break;
case SYS_UNLINK:
ret = Process::current().sys_unlink((const char*)arg1);
break;
default:
dwarnln("Unknown syscall {}", syscall);
break;

View File

@ -4,6 +4,11 @@
#include <sys/syscall.h>
#include <unistd.h>
int creat(const char* path, mode_t mode)
{
return syscall(SYS_CREATE, path, S_IFREG | mode);
}
int open(const char* path, int oflag, ...)
{
va_list args;

View File

@ -56,6 +56,9 @@ __BEGIN_DECLS
#define SYS_TTY_CTRL 53
#define SYS_POWEROFF 54
#define SYS_CHMOD 55
#define SYS_CREATE 56 // creat, mkfifo
#define SYS_CREATE_DIR 57 // mkdir
#define SYS_UNLINK 58
__END_DECLS

View File

@ -28,3 +28,8 @@ int stat(const char* __restrict path, struct stat* __restrict buf)
{
return syscall(SYS_STAT, path, buf, 0);
}
int mkdir(const char* path, mode_t mode)
{
return syscall(SYS_CREATE_DIR, path, mode);
}

View File

@ -210,6 +210,11 @@ void syncsync(int should_block)
syscall(SYS_SYNC, should_block);
}
int unlink(const char* path)
{
return syscall(SYS_UNLINK, path);
}
pid_t getpid(void)
{
return syscall(SYS_GET_PID);

View File

@ -13,8 +13,10 @@ set(USERSPACE_PROJECTS
init
ls
meminfo
mkdir
mmap-shared-test
poweroff
rm
Shell
snake
stat

View File

@ -0,0 +1,17 @@
cmake_minimum_required(VERSION 3.26)
project(mkdir CXX)
set(SOURCES
main.cpp
)
add_executable(mkdir ${SOURCES})
target_compile_options(mkdir PUBLIC -O2 -g)
target_link_libraries(mkdir PUBLIC libc)
add_custom_target(mkdir-install
COMMAND sudo cp ${CMAKE_CURRENT_BINARY_DIR}/mkdir ${BANAN_BIN}/
DEPENDS mkdir
USES_TERMINAL
)

23
userspace/mkdir/main.cpp Normal file
View File

@ -0,0 +1,23 @@
#include <stdio.h>
#include <sys/stat.h>
int main(int argc, char** argv)
{
if (argc <= 1)
{
fprintf(stderr, "Missing operand\n");
return 1;
}
int ret = 0;
for (int i = 1; i < argc; i++)
{
if (mkdir(argv[i], 0755) == -1)
{
perror("mkdir");
ret = 1;
}
}
return ret;
}

View File

@ -0,0 +1,17 @@
cmake_minimum_required(VERSION 3.26)
project(rm CXX)
set(SOURCES
main.cpp
)
add_executable(rm ${SOURCES})
target_compile_options(rm PUBLIC -O2 -g)
target_link_libraries(rm PUBLIC libc ban)
add_custom_target(rm-install
COMMAND sudo cp ${CMAKE_CURRENT_BINARY_DIR}/rm ${BANAN_BIN}/
DEPENDS rm
USES_TERMINAL
)

131
userspace/rm/main.cpp Normal file
View File

@ -0,0 +1,131 @@
#include <BAN/String.h>
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
bool delete_recursive(const char* path)
{
struct stat st;
if (stat(path, &st) == -1)
{
perror(path);
return false;
}
bool ret = true;
if (S_ISDIR(st.st_mode))
{
DIR* dir = opendir(path);
while (struct dirent* dirent = readdir(dir))
{
if (strcmp(dirent->d_name, ".") == 0 || strcmp(dirent->d_name, "..") == 0)
continue;
BAN::String dirent_path;
MUST(dirent_path.append(path));
MUST(dirent_path.push_back('/'));
MUST(dirent_path.append(dirent->d_name));
if (dirent->d_type == DT_DIR)
{
if (!delete_recursive(dirent_path.data()))
ret = false;
}
else
{
if (unlink(dirent_path.data()) == -1)
{
perror(dirent_path.data());
ret = false;
}
}
}
closedir(dir);
}
if (unlink(path) == -1)
{
perror(path);
return false;
}
return true;
}
void usage(const char* argv0, int ret)
{
FILE* out = (ret == 0) ? stdout : stderr;
fprintf(out, "usage: %s [OPTIONS]... FILE...\n");
fprintf(out, " remove each FILE\n");
fprintf(out, "OPTIONS:\n");
fprintf(out, " -r remove directories and their contents recursively\n");
fprintf(out, " -h, --help show this message and exit\n");
exit(ret);
}
int main(int argc, char** argv)
{
bool recursive = false;
int i = 1;
for (; i < argc; i++)
{
if (argv[i][0] != '-')
break;
if (strcmp(argv[i], "-r") == 0)
recursive = true;
else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0)
usage(argv[0], 0);
else
{
fprintf(stderr, "unrecognized argument %s. use --help for more information\n", argv[i]);
return 1;
}
}
if (i >= argc)
{
fprintf(stderr, "missing operand. use --help for more information\n");
return 1;
}
int ret = 0;
for (; i < argc; i++)
{
if (recursive)
{
if (!delete_recursive(argv[i]))
ret = 1;
}
else
{
struct stat st;
if (stat(argv[i], &st) == -1)
{
perror(argv[i]);
ret = 1;
continue;
}
if (S_ISDIR(st.st_mode))
{
fprintf(stderr, "%s: %s\n", argv[i], strerror(EISDIR));
ret = 1;
continue;
}
if (unlink(argv[i]) == -1)
{
perror(argv[i]);
ret = 1;
}
}
}
return ret;
}

View File

@ -4,18 +4,14 @@
int main(int argc, char** argv)
{
int ret = 0;
for (int i = 1; i < argc; i++)
{
int fd = open(argv[i], O_WRONLY | O_CREAT, 0644);
if (fd == -1)
if (creat(argv[i], 0644) == -1 && errno != EEXIST)
{
if (errno != EEXISTS)
perror(argv[i]);
}
else
{
close(fd);
ret = 1;
}
}
return 0;
return ret;
}