Kernel: Rework processes and VFS so we don't expose inodes
Everything is now done through a file descriptor.
This commit is contained in:
parent
a011c0384f
commit
770f7716a0
|
@ -123,15 +123,22 @@ namespace Kernel
|
|||
class Ext2Inode : public Inode
|
||||
{
|
||||
public:
|
||||
virtual ino_t ino() const override { return m_index; };
|
||||
virtual mode_t mode() const override { return m_inode.mode; }
|
||||
virtual nlink_t nlink() const override { return m_inode.links_count; }
|
||||
virtual uid_t uid() const override { return m_inode.uid; }
|
||||
virtual gid_t gid() const override { return m_inode.gid; }
|
||||
virtual size_t size() const override { return m_inode.size; }
|
||||
|
||||
virtual mode_t mode() const override { return m_inode.mode; }
|
||||
virtual off_t size() const override { return m_inode.size; }
|
||||
virtual timespec atime() const override { return timespec { .tv_sec = m_inode.atime, .tv_nsec = 0 }; }
|
||||
virtual timespec mtime() const override { return timespec { .tv_sec = m_inode.mtime, .tv_nsec = 0 }; }
|
||||
virtual timespec ctime() const override { return timespec { .tv_sec = m_inode.ctime, .tv_nsec = 0 }; }
|
||||
virtual blksize_t blksize() const override;
|
||||
virtual blkcnt_t blocks() const override;
|
||||
|
||||
virtual BAN::StringView name() const override { return m_name; }
|
||||
|
||||
virtual BAN::ErrorOr<size_t> read(size_t, void*, size_t) override;
|
||||
virtual BAN::ErrorOr<BAN::Vector<BAN::String>> read_directory_entries_impl(size_t) override;
|
||||
|
||||
virtual BAN::ErrorOr<void> create_file(BAN::StringView, mode_t) override;
|
||||
|
||||
|
@ -139,8 +146,7 @@ namespace Kernel
|
|||
virtual bool operator==(const Inode& other) const override;
|
||||
|
||||
protected:
|
||||
virtual BAN::ErrorOr<BAN::Vector<BAN::RefPtr<Inode>>> directory_inodes_impl() override;
|
||||
virtual BAN::ErrorOr<BAN::RefPtr<Inode>> directory_find_impl(BAN::StringView) override;
|
||||
virtual BAN::ErrorOr<BAN::RefPtr<Inode>> read_directory_inode_impl(BAN::StringView) override;
|
||||
|
||||
private:
|
||||
BAN::ErrorOr<uint32_t> data_block_index(uint32_t);
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <BAN/Vector.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
@ -43,27 +44,32 @@ namespace Kernel
|
|||
bool ifdir() const { return mode() & Mode::IFDIR; }
|
||||
bool ifreg() const { return mode() & Mode::IFREG; }
|
||||
|
||||
virtual ino_t ino() const = 0;
|
||||
virtual mode_t mode() const = 0;
|
||||
virtual nlink_t nlink() const = 0;
|
||||
virtual uid_t uid() const = 0;
|
||||
virtual gid_t gid() const = 0;
|
||||
virtual size_t size() const = 0;
|
||||
|
||||
virtual mode_t mode() const = 0;
|
||||
virtual off_t size() const = 0;
|
||||
virtual timespec atime() const = 0;
|
||||
virtual timespec mtime() const = 0;
|
||||
virtual timespec ctime() const = 0;
|
||||
virtual blksize_t blksize() const = 0;
|
||||
virtual blkcnt_t blocks() const = 0;
|
||||
|
||||
virtual BAN::StringView name() const = 0;
|
||||
|
||||
BAN::ErrorOr<BAN::Vector<BAN::RefPtr<Inode>>> directory_inodes();
|
||||
BAN::ErrorOr<BAN::RefPtr<Inode>> directory_find(BAN::StringView);
|
||||
BAN::ErrorOr<BAN::RefPtr<Inode>> read_directory_inode(BAN::StringView);
|
||||
BAN::ErrorOr<BAN::Vector<BAN::String>> read_directory_entries(size_t);
|
||||
|
||||
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 bool operator==(const Inode&) const = 0;
|
||||
|
||||
protected:
|
||||
virtual BAN::ErrorOr<BAN::Vector<BAN::RefPtr<Inode>>> directory_inodes_impl() = 0;
|
||||
virtual BAN::ErrorOr<BAN::RefPtr<Inode>> directory_find_impl(BAN::StringView) = 0;
|
||||
virtual BAN::ErrorOr<BAN::RefPtr<Inode>> read_directory_inode_impl(BAN::StringView) = 0;
|
||||
virtual BAN::ErrorOr<BAN::Vector<BAN::String>> read_directory_entries_impl(size_t) = 0;
|
||||
};
|
||||
|
||||
}
|
|
@ -7,6 +7,8 @@
|
|||
#include <kernel/SpinLock.h>
|
||||
#include <kernel/Thread.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
|
@ -32,11 +34,14 @@ namespace Kernel
|
|||
BAN::ErrorOr<size_t> read(int, void*, size_t);
|
||||
BAN::ErrorOr<void> creat(BAN::StringView, mode_t);
|
||||
|
||||
BAN::ErrorOr<void> fstat(int, stat*);
|
||||
BAN::ErrorOr<void> stat(BAN::StringView, stat*);
|
||||
|
||||
BAN::ErrorOr<BAN::Vector<BAN::String>> read_directory_entries(int);
|
||||
|
||||
BAN::String working_directory() const;
|
||||
BAN::ErrorOr<void> set_working_directory(BAN::StringView);
|
||||
|
||||
Inode& inode_for_fd(int);
|
||||
|
||||
static BAN::RefPtr<Process> current() { return Thread::current()->process(); }
|
||||
|
||||
private:
|
||||
|
@ -51,8 +56,6 @@ namespace Kernel
|
|||
BAN::String path;
|
||||
size_t offset = 0;
|
||||
uint8_t flags = 0;
|
||||
|
||||
BAN::ErrorOr<size_t> read(void*, size_t);
|
||||
};
|
||||
|
||||
BAN::ErrorOr<void> validate_fd(int);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include <BAN/ScopeGuard.h>
|
||||
#include <BAN/StringView.h>
|
||||
#include <kernel/FS/Ext2.h>
|
||||
#include <kernel/Process.h>
|
||||
#include <kernel/RTC.h>
|
||||
|
||||
#define EXT2_DEBUG_PRINT 0
|
||||
|
@ -150,6 +151,16 @@ namespace Kernel
|
|||
|
||||
}
|
||||
|
||||
blksize_t Ext2Inode::blksize() const
|
||||
{
|
||||
return m_fs.block_size();
|
||||
}
|
||||
|
||||
blkcnt_t Ext2Inode::blocks() const
|
||||
{
|
||||
return m_inode.blocks / (2 << m_fs.superblock().log_block_size);
|
||||
}
|
||||
|
||||
BAN::ErrorOr<BAN::RefPtr<Inode>> Ext2Inode::create(Ext2FS& fs, uint32_t inode_inode, BAN::StringView name)
|
||||
{
|
||||
BAN::Vector<uint8_t> block_buffer;
|
||||
|
@ -273,6 +284,38 @@ namespace Kernel
|
|||
return n_read;
|
||||
}
|
||||
|
||||
BAN::ErrorOr<BAN::Vector<BAN::String>> Ext2Inode::read_directory_entries_impl(size_t index)
|
||||
{
|
||||
if (!ifdir())
|
||||
return BAN::Error::from_errno(ENOTDIR);
|
||||
|
||||
uint32_t data_block_count = blocks();
|
||||
if (index >= data_block_count)
|
||||
return BAN::Vector<BAN::String>();
|
||||
|
||||
uint32_t block_size = blksize();
|
||||
uint32_t block_index = TRY(data_block_index(index));
|
||||
|
||||
BAN::Vector<uint8_t> block_buffer;
|
||||
TRY(block_buffer.resize(block_size));
|
||||
|
||||
m_fs.read_block(block_index, block_buffer.span());
|
||||
|
||||
BAN::Vector<BAN::String> entries;
|
||||
|
||||
const uint8_t* block_buffer_end = block_buffer.data() + block_size;
|
||||
const uint8_t* entry_addr = block_buffer.data();
|
||||
while (entry_addr < block_buffer_end)
|
||||
{
|
||||
auto& entry = *(Ext2::LinkedDirectoryEntry*)entry_addr;
|
||||
if (entry.inode)
|
||||
TRY(entries.emplace_back(BAN::StringView(entry.name, entry.name_len)));
|
||||
entry_addr += entry.rec_len;
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> Ext2Inode::create_file(BAN::StringView name, mode_t mode)
|
||||
{
|
||||
if (!ifdir())
|
||||
|
@ -285,7 +328,7 @@ namespace Kernel
|
|||
BAN::Vector<uint8_t> block_buffer;
|
||||
TRY(block_buffer.resize(block_size));
|
||||
|
||||
auto error_or = directory_find_impl(name);
|
||||
auto error_or = read_directory_inode_impl(name);
|
||||
if (!error_or.is_error())
|
||||
return BAN::Error::from_errno(EEXISTS);
|
||||
if (error_or.error().get_error_code() != ENOENT)
|
||||
|
@ -358,7 +401,7 @@ namespace Kernel
|
|||
return {};
|
||||
}
|
||||
|
||||
BAN::ErrorOr<BAN::RefPtr<Inode>> Ext2Inode::directory_find_impl(BAN::StringView file_name)
|
||||
BAN::ErrorOr<BAN::RefPtr<Inode>> Ext2Inode::read_directory_inode_impl(BAN::StringView file_name)
|
||||
{
|
||||
if (!ifdir())
|
||||
return BAN::Error::from_errno(ENOTDIR);
|
||||
|
@ -367,7 +410,7 @@ namespace Kernel
|
|||
BAN::Vector<uint8_t> block_buffer;
|
||||
TRY(block_buffer.resize(block_size));
|
||||
|
||||
uint32_t data_block_count = m_inode.blocks / (2 << m_fs.superblock().log_block_size);
|
||||
uint32_t data_block_count = blocks();
|
||||
|
||||
for (uint32_t i = 0; i < data_block_count; i++)
|
||||
{
|
||||
|
@ -390,42 +433,6 @@ namespace Kernel
|
|||
return BAN::Error::from_errno(ENOENT);
|
||||
}
|
||||
|
||||
BAN::ErrorOr<BAN::Vector<BAN::RefPtr<Inode>>> Ext2Inode::directory_inodes_impl()
|
||||
{
|
||||
if (!ifdir())
|
||||
return BAN::Error::from_errno(ENOTDIR);
|
||||
|
||||
uint32_t block_size = m_fs.block_size();
|
||||
BAN::Vector<uint8_t> block_buffer;
|
||||
TRY(block_buffer.resize(block_size));
|
||||
|
||||
uint32_t data_block_count = m_inode.blocks / (2 << m_fs.superblock().log_block_size);
|
||||
|
||||
BAN::Vector<BAN::RefPtr<Inode>> inodes;
|
||||
|
||||
for (uint32_t i = 0; i < data_block_count; i++)
|
||||
{
|
||||
uint32_t block_index = TRY(data_block_index(i));
|
||||
m_fs.read_block(block_index, block_buffer.span());
|
||||
|
||||
const uint8_t* block_buffer_end = block_buffer.data() + block_size;
|
||||
const uint8_t* entry_addr = block_buffer.data();
|
||||
while (entry_addr < block_buffer_end)
|
||||
{
|
||||
const auto& entry = *(const Ext2::LinkedDirectoryEntry*)entry_addr;
|
||||
if (entry.inode)
|
||||
{
|
||||
BAN::StringView entry_name(entry.name, entry.name_len);
|
||||
auto inode = TRY(Ext2Inode::create(m_fs, entry.inode, entry_name));
|
||||
TRY(inodes.push_back(inode));
|
||||
}
|
||||
entry_addr += entry.rec_len;
|
||||
}
|
||||
}
|
||||
|
||||
return inodes;
|
||||
}
|
||||
|
||||
bool Ext2Inode::operator==(const Inode& other) const
|
||||
{
|
||||
if (type() != other.type())
|
||||
|
|
|
@ -5,23 +5,22 @@
|
|||
namespace Kernel
|
||||
{
|
||||
|
||||
|
||||
BAN::ErrorOr<BAN::Vector<BAN::RefPtr<Inode>>> Inode::directory_inodes()
|
||||
{
|
||||
for (const auto& mount : VirtualFileSystem::get().mount_points())
|
||||
if (*mount.inode == *this)
|
||||
return mount.target->root_inode()->directory_inodes_impl();
|
||||
return directory_inodes_impl();
|
||||
}
|
||||
|
||||
BAN::ErrorOr<BAN::RefPtr<Inode>> Inode::directory_find(BAN::StringView name)
|
||||
BAN::ErrorOr<BAN::RefPtr<Inode>> Inode::read_directory_inode(BAN::StringView name)
|
||||
{
|
||||
if (name == ".."sv)
|
||||
return directory_find_impl(name);
|
||||
return read_directory_inode_impl(name);
|
||||
for (const auto& mount : VirtualFileSystem::get().mount_points())
|
||||
if (*mount.inode == *this)
|
||||
return mount.target->root_inode()->directory_find_impl(name);
|
||||
return directory_find_impl(name);
|
||||
return mount.target->root_inode()->read_directory_inode_impl(name);
|
||||
return read_directory_inode_impl(name);
|
||||
}
|
||||
|
||||
BAN::ErrorOr<BAN::Vector<BAN::String>> Inode::read_directory_entries(size_t index)
|
||||
{
|
||||
for (const auto& mount : VirtualFileSystem::get().mount_points())
|
||||
if (*mount.inode == *this)
|
||||
return mount.target->root_inode()->read_directory_entries_impl(index);
|
||||
return read_directory_entries_impl(index);
|
||||
}
|
||||
|
||||
}
|
|
@ -117,11 +117,9 @@ namespace Kernel
|
|||
|
||||
BAN::ErrorOr<void> VirtualFileSystem::mount_test()
|
||||
{
|
||||
auto mount = TRY(root_inode()->directory_find("mnt"sv));
|
||||
auto mount = TRY(root_inode()->read_directory_inode("mnt"sv));
|
||||
if (!mount->ifdir())
|
||||
return BAN::Error::from_errno(ENOTDIR);
|
||||
if (TRY(mount->directory_inodes()).size() > 2)
|
||||
return BAN::Error::from_errno(ENOTEMPTY);
|
||||
|
||||
for (auto* controller : m_storage_controllers)
|
||||
{
|
||||
|
@ -159,7 +157,7 @@ namespace Kernel
|
|||
}
|
||||
else if (path_parts[i] == ".."sv)
|
||||
{
|
||||
inode = TRY(inode->directory_find(path_parts[i]));
|
||||
inode = TRY(inode->read_directory_inode(path_parts[i]));
|
||||
path_parts.remove(i);
|
||||
if (i > 0)
|
||||
{
|
||||
|
@ -169,7 +167,7 @@ namespace Kernel
|
|||
}
|
||||
else
|
||||
{
|
||||
inode = TRY(inode->directory_find(path_parts[i]));
|
||||
inode = TRY(inode->read_directory_inode(path_parts[i]));
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,10 +40,12 @@ namespace Kernel
|
|||
int fd = TRY(Process::current()->open(path, O_RDONLY));
|
||||
BAN::ScopeGuard _([fd] { MUST(Process::current()->close(fd)); });
|
||||
|
||||
size_t file_size = Process::current()->inode_for_fd(fd).size();
|
||||
stat st;
|
||||
TRY(Process::current()->fstat(fd, &st));
|
||||
|
||||
BAN::Vector<uint8_t> file_data;
|
||||
TRY(file_data.resize(file_size));
|
||||
TRY(Process::current()->read(fd, file_data.data(), file_size));
|
||||
TRY(file_data.resize(st.st_size));
|
||||
TRY(Process::current()->read(fd, file_data.data(), st.st_size));
|
||||
|
||||
if (file_data.size() < 4)
|
||||
return BAN::Error::from_c_string("Font file is too small");
|
||||
|
|
|
@ -36,7 +36,10 @@ namespace Kernel
|
|||
void Process::on_thread_exit(Thread& thread)
|
||||
{
|
||||
LockGuard _(m_lock);
|
||||
(void)thread;
|
||||
dprintln("thread {} exit", thread.tid());
|
||||
for (size_t i = 0; i < m_threads.size(); i++)
|
||||
if (m_threads[i].ptr() == &thread)
|
||||
m_threads.remove(i);
|
||||
}
|
||||
|
||||
BAN::ErrorOr<int> Process::open(BAN::StringView path, int flags)
|
||||
|
@ -63,7 +66,6 @@ namespace Kernel
|
|||
BAN::ErrorOr<void> Process::close(int fd)
|
||||
{
|
||||
LockGuard _(m_lock);
|
||||
|
||||
TRY(validate_fd(fd));
|
||||
auto& open_file_description = this->open_file_description(fd);
|
||||
open_file_description.inode = nullptr;
|
||||
|
@ -73,19 +75,18 @@ namespace Kernel
|
|||
BAN::ErrorOr<size_t> Process::read(int fd, void* buffer, size_t count)
|
||||
{
|
||||
LockGuard _(m_lock);
|
||||
|
||||
TRY(validate_fd(fd));
|
||||
auto& open_file_description = this->open_file_description(fd);
|
||||
if (open_file_description.offset >= open_file_description.inode->size())
|
||||
return 0;
|
||||
size_t n_read = TRY(open_file_description.read(buffer, count));
|
||||
auto& open_fd = open_file_description(fd);
|
||||
if (!(open_fd.flags & O_RDONLY))
|
||||
return BAN::Error::from_errno(EBADF);
|
||||
size_t n_read = TRY(open_fd.inode->read(open_fd.offset, buffer, count));
|
||||
open_fd.offset += n_read;
|
||||
return n_read;
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> Process::creat(BAN::StringView path, mode_t mode)
|
||||
{
|
||||
LockGuard _(m_lock);
|
||||
|
||||
auto absolute_path = TRY(absolute_path_of(path));
|
||||
while (absolute_path.sv().back() != '/')
|
||||
absolute_path.pop_back();
|
||||
|
@ -96,12 +97,47 @@ namespace Kernel
|
|||
return {};
|
||||
}
|
||||
|
||||
Inode& Process::inode_for_fd(int fd)
|
||||
BAN::ErrorOr<void> Process::fstat(int fd, struct stat* out)
|
||||
{
|
||||
LockGuard _(m_lock);
|
||||
|
||||
MUST(validate_fd(fd));
|
||||
return *open_file_description(fd).inode;
|
||||
TRY(validate_fd(fd));
|
||||
const auto& open_fd = open_file_description(fd);
|
||||
|
||||
out->st_dev = 0;
|
||||
out->st_ino = open_fd.inode->ino();
|
||||
out->st_mode = open_fd.inode->mode();
|
||||
out->st_nlink = open_fd.inode->nlink();
|
||||
out->st_uid = open_fd.inode->uid();
|
||||
out->st_gid = open_fd.inode->gid();
|
||||
out->st_rdev = 0;
|
||||
out->st_size = open_fd.inode->size();
|
||||
out->st_atim = open_fd.inode->atime();
|
||||
out->st_mtim = open_fd.inode->mtime();
|
||||
out->st_ctim = open_fd.inode->ctime();
|
||||
out->st_blksize = open_fd.inode->blksize();
|
||||
out->st_blocks = open_fd.inode->blocks();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> Process::stat(BAN::StringView path, struct stat* out)
|
||||
{
|
||||
LockGuard _(m_lock);
|
||||
int fd = TRY(open(path, O_RDONLY));
|
||||
auto ret = fstat(fd, out);
|
||||
MUST(close(fd));
|
||||
return ret;
|
||||
}
|
||||
|
||||
BAN::ErrorOr<BAN::Vector<BAN::String>> Process::read_directory_entries(int fd)
|
||||
{
|
||||
LockGuard _(m_lock);
|
||||
TRY(validate_fd(fd));
|
||||
auto& open_fd = open_file_description(fd);
|
||||
auto result = TRY(open_fd.inode->read_directory_entries(open_fd.offset));
|
||||
open_fd.offset++;
|
||||
return result;
|
||||
}
|
||||
|
||||
BAN::String Process::working_directory() const
|
||||
|
@ -142,19 +178,9 @@ namespace Kernel
|
|||
return absolute_path;
|
||||
}
|
||||
|
||||
BAN::ErrorOr<size_t> Process::OpenFileDescription::read(void* buffer, size_t count)
|
||||
{
|
||||
if (!(flags & O_RDONLY))
|
||||
return BAN::Error::from_errno(EBADF);
|
||||
size_t n_read = TRY(inode->read(offset, buffer, count));
|
||||
offset += n_read;
|
||||
return n_read;
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> Process::validate_fd(int fd)
|
||||
{
|
||||
LockGuard _(m_lock);
|
||||
|
||||
if (fd < 0 || m_open_files.size() <= (size_t)fd || !m_open_files[fd].inode)
|
||||
return BAN::Error::from_errno(EBADF);
|
||||
return {};
|
||||
|
|
|
@ -356,14 +356,6 @@ argument_done:
|
|||
if (arguments.size() > 2)
|
||||
return BAN::Error::from_c_string("usage: 'ls [path]'");
|
||||
|
||||
BAN::String path = (arguments.size() == 2) ? arguments[1] : Process::current()->working_directory();
|
||||
|
||||
int fd = TRY(Process::current()->open(path, O_RDONLY));
|
||||
BAN::ScopeGuard _([fd] { MUST(Process::current()->close(fd)); });
|
||||
|
||||
auto& directory = Process::current()->inode_for_fd(fd);
|
||||
auto inodes = TRY(directory.directory_inodes());
|
||||
|
||||
auto mode_string = [](mode_t mode)
|
||||
{
|
||||
static char buffer[11] {};
|
||||
|
@ -380,12 +372,40 @@ argument_done:
|
|||
return (const char*)buffer;
|
||||
};
|
||||
|
||||
for (auto& inode : inodes)
|
||||
if (inode->ifdir())
|
||||
TTY_PRINTLN(" {} {7} \e[34m{}\e[m", mode_string(inode->mode()), inode->size(), inode->name());
|
||||
for (auto& inode : inodes)
|
||||
if (!inode->ifdir())
|
||||
TTY_PRINTLN(" {} {7} {}", mode_string(inode->mode()), inode->size(), inode->name());
|
||||
BAN::String path = (arguments.size() == 2) ? arguments[1] : Process::current()->working_directory();
|
||||
|
||||
int fd = TRY(Process::current()->open(path, O_RDONLY));
|
||||
BAN::ScopeGuard _([fd] { MUST(Process::current()->close(fd)); });
|
||||
|
||||
BAN::Vector<BAN::String> all_entries;
|
||||
|
||||
BAN::Vector<BAN::String> entries;
|
||||
while (!(entries = TRY(Process::current()->read_directory_entries(fd))).empty())
|
||||
{
|
||||
TRY(all_entries.reserve(all_entries.size() + entries.size()));
|
||||
for (auto& entry : entries)
|
||||
TRY(all_entries.push_back(entry));
|
||||
}
|
||||
|
||||
BAN::String entry_prefix;
|
||||
TRY(entry_prefix.append(path));
|
||||
TRY(entry_prefix.push_back('/'));
|
||||
for (const auto& entry : all_entries)
|
||||
{
|
||||
stat st;
|
||||
|
||||
BAN::String entry_path;
|
||||
TRY(entry_path.append(entry_prefix));
|
||||
TRY(entry_path.append(entry));
|
||||
TRY(Process::current()->stat(entry_path, &st));
|
||||
|
||||
const char* color =
|
||||
(st.st_mode & Inode::Mode::IFDIR) ? "34" :
|
||||
(st.st_mode & Inode::Mode::IXUSR) ? "32" :
|
||||
"";
|
||||
|
||||
TTY_PRINTLN(" {} {7} \e[{}m{}\e[m", mode_string(st.st_mode), st.st_size, color, entry);
|
||||
}
|
||||
}
|
||||
else if (arguments.front() == "cat")
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue