Compare commits
11 Commits
4f4b8ada8c
...
1ec341e2dd
Author | SHA1 | Date |
---|---|---|
Bananymous | 1ec341e2dd | |
Bananymous | d09310f388 | |
Bananymous | 126edea119 | |
Bananymous | 74bfb930f2 | |
Bananymous | 091c5b6a66 | |
Bananymous | fda4a4ad24 | |
Bananymous | 8bb47aee02 | |
Bananymous | 1f794e4ac0 | |
Bananymous | e85f9ac6a1 | |
Bananymous | 6ee5576dcc | |
Bananymous | b890e2fc14 |
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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); }
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -13,8 +13,10 @@ set(USERSPACE_PROJECTS
|
|||
init
|
||||
ls
|
||||
meminfo
|
||||
mkdir
|
||||
mmap-shared-test
|
||||
poweroff
|
||||
rm
|
||||
Shell
|
||||
snake
|
||||
stat
|
||||
|
|
|
@ -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
|
||||
)
|
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
)
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue