Compare commits

...

21 Commits

Author SHA1 Message Date
1dc26d3c06 Kernel: Cleanup inode stat updating
Inode now handles stat upates itself and calls sync function to make
updates visible on the underlying filesystem
2026-05-19 13:00:05 +03:00
a05fcdde8c Kernel: Move UTIME_OMIT handling to the syscall from inode 2026-05-19 11:56:12 +03:00
deb2f52a35 Kernel: Add support for named pipes
These only copy inodes stat when created, if you fchmod/fchown a named
pipe, the change will not be visible on the filesystem
2026-05-19 00:15:25 +03:00
8224659c48 Kernel: Allow creating FIFOs in tmpfs 2026-05-19 00:05:22 +03:00
1922d78661 Kernel: Add support for mkfifo{,at} mkdir at
Opening FIFOs still dont work as expected but at least you can create
them now :D
2026-05-18 23:53:09 +03:00
6d1ecc2388 Kernel: Clean up file creation 2026-05-18 15:26:59 +03:00
5cf658c175 Kernel: Fix 2 memory pinning bugs
If pinning a region succeeded but pushing the region to a vector failed,
we would leak the pin preventing the process from cleaning up
2026-05-17 03:38:18 +03:00
ef2738bfb7 Kernel: Disable devfs device add/remove logging 2026-05-17 03:38:18 +03:00
d7865b2929 Kernel: Don't msync file backed pages that were never mapped writable 2026-05-17 03:21:08 +03:00
9c79971bdc LibC/Kernel: Add support for detached pthreads 2026-05-17 00:40:56 +03:00
ff75c15ba3 LibC: Move pthread keys to TCB
This removes all TLS relocations from libc which may become handy ;)
2026-05-17 00:29:20 +03:00
9e6fa0a1ba Kernel: Add fast path for invalid address during region pin
Before I was falling through to the write lock which does allocation,
but as the read loop already saw, specified address was not mapped.
2026-05-17 00:29:20 +03:00
6e95519acc Kernel: Make TTY write lock a mutex instead of a spinlock
The mutex could not be locked while processing ANSI DGR in VTTY
2026-05-17 00:29:20 +03:00
d081655913 Kernel: Add API for mutex to check if it is locked by current thread
Also add assertion that you have interrupts enabled :^)
2026-05-17 00:29:20 +03:00
9c3eb8d270 userspace: Implement tr utility 2026-05-17 00:29:20 +03:00
68479bf07e userspace: Fix getopt_long usage
- Add missing null entry after the last long option
- Don't duplicate illegal option message, getopt already prints
  the message as I do not suppress it. Also handle missing arguments.
2026-05-17 00:29:20 +03:00
d528314ae3 Kernel: Make SYS_FUTEX restartable 2026-05-17 00:29:20 +03:00
40dd29b876 LibC: Cleanup syscall macros 2026-05-17 00:29:20 +03:00
dc1d7e3fae ls: Print total field with -l 2026-05-15 22:46:50 +03:00
8f8ba2751c ls: Add support for -A and -h 2026-05-15 22:35:58 +03:00
928d3e3fe7 dirname: Fix help message 2026-05-15 22:34:48 +03:00
47 changed files with 1079 additions and 515 deletions

View File

@@ -49,6 +49,8 @@
#define DEBUG_VTTY 1
#define DEBUG_DEVFS 0
#define DEBUG_PCI 0
#define DEBUG_SCHEDULER 0
#define DEBUG_PS2 1

View File

@@ -23,7 +23,9 @@ namespace Kernel
protected:
Device(mode_t, uid_t, gid_t);
virtual BAN::ErrorOr<void> fsync_impl() final override { return BAN::Error::from_errno(EINVAL); }
private:
BAN::ErrorOr<void> sync_inode(SyncType) final override { return {}; }
BAN::ErrorOr<void> sync_data() final override { return {}; }
};
class BlockDevice : public Device, public BAN::Weakable<BlockDevice>

View File

@@ -21,24 +21,8 @@ namespace Kernel
void notify(BAN::RefPtr<Inode> inode, uint32_t event);
private:
Epoll() {
m_ino = 0;
m_mode = Mode::IRUSR | Mode::IWUSR;
m_nlink = 0;
m_uid = 0;
m_gid = 0;
m_size = 0;
m_atime = {};
m_mtime = {};
m_ctime = {};
m_blksize = PAGE_SIZE;
m_blocks = 0;
m_dev = 0;
m_rdev = 0;
m_kind = InodeKind::EPOLL;
}
Epoll();
public:
const FileSystem* filesystem() const override { return nullptr; }
bool can_read_impl() const override { return false; }
@@ -46,7 +30,8 @@ namespace Kernel
bool has_error_impl() const override { return false; }
bool has_hungup_impl() const override { return false; }
BAN::ErrorOr<void> fsync_impl() override { return {}; }
BAN::ErrorOr<void> sync_inode(SyncType) override { return {}; }
BAN::ErrorOr<void> sync_data() override { return {}; }
private:
struct ListenEventList

View File

@@ -11,38 +11,22 @@ namespace Kernel
public:
static BAN::ErrorOr<BAN::RefPtr<Inode>> create(uint64_t initval, bool semaphore);
private:
EventFD(uint64_t initval, bool is_semaphore);
const FileSystem* filesystem() const override { return nullptr; }
protected:
BAN::ErrorOr<void> sync_inode(SyncType) override { return {}; }
BAN::ErrorOr<void> sync_data() override { return {}; }
BAN::ErrorOr<size_t> read_impl(off_t, BAN::ByteSpan) override;
BAN::ErrorOr<size_t> write_impl(off_t, BAN::ConstByteSpan) override;
BAN::ErrorOr<void> fsync_impl() final override { return {}; }
bool can_read_impl() const override { return m_value > 0; }
bool can_write_impl() const override { return m_value < UINT64_MAX - 1; }
bool has_error_impl() const override { return false; }
bool has_hungup_impl() const override { return false; }
private:
EventFD(uint64_t initval, bool is_semaphore)
: m_is_semaphore(is_semaphore)
, m_value(initval)
{
m_ino = 0;
m_mode = { Mode::IFCHR | Mode::IRUSR | Mode::IWUSR };
m_nlink = 0;
m_uid = 0;
m_gid = 0;
m_size = 0;
m_atime = {};
m_mtime = {};
m_ctime = {};
m_blksize = 8;
m_blocks = 0;
m_dev = 0;
m_rdev = 0;
}
private:
const bool m_is_semaphore;
BAN::Atomic<uint64_t> m_value;

View File

@@ -16,25 +16,12 @@ namespace Kernel
public:
~Ext2Inode();
#if 0
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; }
virtual uid_t uid() const override { return m_inode.uid; }
virtual gid_t gid() const override { return m_inode.gid; }
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 dev_t dev() const override { return 0; }
virtual dev_t rdev() const override { return 0; }
#endif
virtual const FileSystem* filesystem() const override;
protected:
private:
virtual BAN::ErrorOr<void> sync_inode(SyncType) override;
virtual BAN::ErrorOr<void> sync_data() override;
virtual BAN::ErrorOr<BAN::RefPtr<Inode>> find_inode_impl(BAN::StringView) override;
virtual BAN::ErrorOr<size_t> list_next_inodes_impl(off_t, struct dirent*, size_t) override;
virtual BAN::ErrorOr<void> create_file_impl(BAN::StringView, mode_t, uid_t, gid_t) override;
@@ -49,10 +36,6 @@ namespace Kernel
virtual BAN::ErrorOr<size_t> read_impl(off_t, BAN::ByteSpan) override;
virtual BAN::ErrorOr<size_t> write_impl(off_t, BAN::ConstByteSpan) override;
virtual BAN::ErrorOr<void> truncate_impl(size_t) override;
virtual BAN::ErrorOr<void> chmod_impl(mode_t) override;
virtual BAN::ErrorOr<void> chown_impl(uid_t, gid_t) override;
virtual BAN::ErrorOr<void> utimens_impl(const timespec[2]) override;
virtual BAN::ErrorOr<void> fsync_impl() override;
virtual bool can_read_impl() const override { return true; }
virtual bool can_write_impl() const override { return true; }
@@ -66,7 +49,7 @@ namespace Kernel
// NOTE: the inode might have more blocks than what this suggests if it has been shrinked
uint32_t max_used_data_block_count() const { return size() / blksize(); }
BAN::ErrorOr<void> sync_no_lock();
BAN::ErrorOr<void> sync_inode_no_lock();
BAN::ErrorOr<bool> is_directory_empty_no_lock();
BAN::ErrorOr<BAN::RefPtr<Inode>> find_inode_no_lock(BAN::StringView);
@@ -101,7 +84,7 @@ namespace Kernel
{
// TODO: there was some memcmp smarty pants stuff here.
// How do we wanna approach it?
if (auto ret = inode.sync_no_lock(); ret.is_error())
if (auto ret = inode.sync_inode_no_lock(); ret.is_error())
dwarnln("failed to sync inode: {}", ret.error());
}

View File

@@ -19,7 +19,10 @@ namespace Kernel
const FAT::DirectoryEntry& entry() const { return m_entry; }
protected:
private:
virtual BAN::ErrorOr<void> sync_inode(SyncType) override { return {}; }
virtual BAN::ErrorOr<void> sync_data() override { return {}; }
virtual BAN::ErrorOr<BAN::RefPtr<Inode>> find_inode_impl(BAN::StringView) override;
virtual BAN::ErrorOr<size_t> list_next_inodes_impl(off_t, struct dirent*, size_t) override;
//virtual BAN::ErrorOr<void> create_file_impl(BAN::StringView, mode_t, uid_t, gid_t) override;
@@ -29,9 +32,6 @@ namespace Kernel
virtual BAN::ErrorOr<size_t> read_impl(off_t, BAN::ByteSpan) override;
//virtual BAN::ErrorOr<size_t> write_impl(off_t, BAN::ConstByteSpan) override;
//virtual BAN::ErrorOr<void> truncate_impl(size_t) override;
//virtual BAN::ErrorOr<void> chmod_impl(mode_t) override;
//virtual BAN::ErrorOr<void> utimens_impl(const timespec[2]) override;
virtual BAN::ErrorOr<void> fsync_impl() override { return {}; }
virtual bool can_read_impl() const override { return true; }
virtual bool can_write_impl() const override { return true; }

View File

@@ -61,7 +61,9 @@ namespace Kernel
bool ifsock() const { return (mode & Mask::TYPE_MASK) == Mask::IFSOCK; }
mode_t mode;
};
enum InodeKind : uint8_t {
enum InodeKind : uint8_t
{
DEVICE = 0x01,
EPOLL = 0x02,
PIPE = 0x04,
@@ -69,6 +71,15 @@ namespace Kernel
PARTITION = 0x10,
STORAGE = 0x20,
};
enum class SyncType
{
General,
Mode,
UidGid,
Times,
};
public:
virtual ~Inode() {}
@@ -134,10 +145,10 @@ namespace Kernel
BAN::ErrorOr<void> fsync();
// Select/Non blocking API
bool can_read() const;
bool can_write() const;
bool has_error() const;
bool has_hungup() const;
bool can_read() const { return can_read_impl(); }
bool can_write() const { return can_write_impl(); }
bool has_error() const { return has_error_impl(); }
bool has_hungup() const { return has_hungup_impl(); }
BAN::ErrorOr<long> ioctl(int request, void* arg);
@@ -148,6 +159,9 @@ namespace Kernel
virtual void on_close(int status_flags) { (void)status_flags; }
virtual void on_clone(int status_flags) { (void)status_flags; }
virtual BAN::ErrorOr<void> sync_inode(SyncType) = 0;
virtual BAN::ErrorOr<void> sync_data() = 0;
protected:
// Directory API
virtual BAN::ErrorOr<BAN::RefPtr<Inode>> find_inode_impl(BAN::StringView) { return BAN::Error::from_errno(ENOTSUP); }
@@ -178,10 +192,6 @@ namespace Kernel
virtual BAN::ErrorOr<size_t> read_impl(off_t, BAN::ByteSpan) { return BAN::Error::from_errno(ENOTSUP); }
virtual BAN::ErrorOr<size_t> write_impl(off_t, BAN::ConstByteSpan) { return BAN::Error::from_errno(ENOTSUP); }
virtual BAN::ErrorOr<void> truncate_impl(size_t) { return BAN::Error::from_errno(ENOTSUP); }
virtual BAN::ErrorOr<void> chmod_impl(mode_t) { return BAN::Error::from_errno(ENOTSUP); }
virtual BAN::ErrorOr<void> chown_impl(uid_t, gid_t) { return BAN::Error::from_errno(ENOTSUP); }
virtual BAN::ErrorOr<void> utimens_impl(const timespec[2]) { return BAN::Error::from_errno(ENOTSUP); }
virtual BAN::ErrorOr<void> fsync_impl() = 0;
// Select/Non blocking API
virtual bool can_read_impl() const = 0;
@@ -196,6 +206,7 @@ namespace Kernel
// But the thing is I would have to refactor a big chunk of the codebase
// to add it as a parameter to Inode() soooooo yeah no, not doing that rn.
uint8_t m_kind = 0;
BAN::Atomic<ino_t> m_ino;
BAN::Atomic<mode_t> m_mode;
BAN::Atomic<nlink_t> m_nlink;
@@ -210,12 +221,14 @@ namespace Kernel
BAN::Atomic<blkcnt_t> m_blocks;
BAN::Atomic<dev_t> m_dev;
BAN::Atomic<dev_t> m_rdev;
private:
SpinLock m_shared_region_lock;
BAN::WeakPtr<SharedFileData> m_shared_region;
SpinLock m_epoll_lock;
BAN::LinkedList<class Epoll*> m_epolls;
friend class Epoll;
friend class FileBackedRegion;
friend class OpenFileDescriptorSet;

View File

@@ -5,23 +5,30 @@
#include <kernel/Memory/ByteRingBuffer.h>
#include <kernel/ThreadBlocker.h>
#include <sys/stat.h>
namespace Kernel
{
class Pipe : public Inode
class Pipe final : public Inode, public BAN::Weakable<Pipe>
{
public:
static BAN::ErrorOr<BAN::RefPtr<Inode>> create(const Credentials&);
static BAN::ErrorOr<BAN::RefPtr<Inode>> open(BAN::RefPtr<Inode>, int status_flags);
static BAN::ErrorOr<BAN::RefPtr<Inode>> create(uid_t, gid_t);
~Pipe();
void on_close(int status_flags) override;
void on_clone(int status_flags) override;
virtual const FileSystem* filesystem() const override { return nullptr; }
protected:
private:
virtual BAN::ErrorOr<void> sync_inode(SyncType) override;
virtual BAN::ErrorOr<void> sync_data() override;
virtual BAN::ErrorOr<size_t> read_impl(off_t, BAN::ByteSpan) override;
virtual BAN::ErrorOr<size_t> write_impl(off_t, BAN::ConstByteSpan) override;
virtual BAN::ErrorOr<void> fsync_impl() final override { return {}; }
virtual BAN::ErrorOr<void> truncate_impl(size_t) override;
virtual bool can_read_impl() const override { return !m_buffer->empty(); }
virtual bool can_write_impl() const override { return true; }
@@ -31,7 +38,7 @@ namespace Kernel
virtual BAN::ErrorOr<long> ioctl_impl(int, void*) override;
private:
Pipe(const Credentials&);
Pipe(const struct stat&);
private:
Mutex m_mutex;
@@ -39,8 +46,10 @@ namespace Kernel
BAN::UniqPtr<ByteRingBuffer> m_buffer;
BAN::Atomic<uint32_t> m_writing_count { 1 };
BAN::Atomic<uint32_t> m_reading_count { 1 };
BAN::Atomic<uint32_t> m_writing_count { 0 };
BAN::Atomic<uint32_t> m_reading_count { 0 };
BAN::RefPtr<Inode> m_named_inode;
};
}

View File

@@ -40,9 +40,9 @@ namespace Kernel
m_gid = info.gid;
}
BAN::ErrorOr<void> fsync_impl() final override { return {}; }
private:
BAN::ErrorOr<void> sync_inode(SyncType) final override { return {}; }
BAN::ErrorOr<void> sync_data() final override { return {}; }
};
}

View File

@@ -26,18 +26,14 @@ namespace Kernel
{
public:
static BAN::ErrorOr<BAN::RefPtr<TmpInode>> create_from_existing(TmpFileSystem&, ino_t, const TmpInodeInfo&);
~TmpInode();
virtual ~TmpInode();
virtual const FileSystem* filesystem() const override;
protected:
TmpInode(TmpFileSystem&, ino_t, const TmpInodeInfo&);
virtual BAN::ErrorOr<void> chmod_impl(mode_t) override;
virtual BAN::ErrorOr<void> chown_impl(uid_t, gid_t) override;
virtual BAN::ErrorOr<void> utimens_impl(const timespec[2]) override;
virtual BAN::ErrorOr<void> fsync_impl() override { return {}; }
void sync();
void write_inode_to_fs();
virtual BAN::ErrorOr<void> prepare_unlink_no_lock() { return {}; };
void free_all_blocks();
@@ -49,6 +45,10 @@ namespace Kernel
BAN::ErrorOr<size_t> block_index_with_allocation(size_t data_block_index);
BAN::ErrorOr<size_t> block_index_from_indirect_with_allocation_no_lock(size_t& block, size_t index, uint32_t depth);
private:
BAN::ErrorOr<void> sync_inode(SyncType) override;
BAN::ErrorOr<void> sync_data() override;
protected:
TmpFileSystem& m_fs;
TmpBlocks m_tmp_blocks;
@@ -66,7 +66,7 @@ namespace Kernel
static BAN::ErrorOr<BAN::RefPtr<TmpFileInode>> create_new(TmpFileSystem&, mode_t, uid_t, gid_t);
~TmpFileInode();
protected:
private:
virtual BAN::ErrorOr<size_t> read_impl(off_t, BAN::ByteSpan) override;
virtual BAN::ErrorOr<size_t> write_impl(off_t, BAN::ConstByteSpan) override;
virtual BAN::ErrorOr<void> truncate_impl(size_t) override;
@@ -82,13 +82,36 @@ namespace Kernel
friend class TmpInode;
};
// NOTE: this is just a dummy, when opening a fifo a pipe is created
class TmpFIFOInode : public TmpInode
{
public:
static BAN::ErrorOr<BAN::RefPtr<TmpFIFOInode>> create_new(TmpFileSystem&, mode_t, uid_t, gid_t);
~TmpFIFOInode();
private:
virtual BAN::ErrorOr<size_t> read_impl(off_t, BAN::ByteSpan) override { return BAN::Error::from_errno(ENODEV); }
virtual BAN::ErrorOr<size_t> write_impl(off_t, BAN::ConstByteSpan) override { return BAN::Error::from_errno(ENODEV); }
virtual BAN::ErrorOr<void> truncate_impl(size_t) override { return BAN::Error::from_errno(ENODEV); }
virtual bool can_read_impl() const override { return false; }
virtual bool can_write_impl() const override { return false; }
virtual bool has_error_impl() const override { return false; }
virtual bool has_hungup_impl() const override { return false; }
private:
TmpFIFOInode(TmpFileSystem&, ino_t, const TmpInodeInfo&);
friend class TmpInode;
};
class TmpSocketInode : public TmpInode
{
public:
static BAN::ErrorOr<BAN::RefPtr<TmpSocketInode>> create_new(TmpFileSystem&, mode_t, uid_t, gid_t);
~TmpSocketInode();
protected:
private:
virtual BAN::ErrorOr<size_t> read_impl(off_t, BAN::ByteSpan) override { return BAN::Error::from_errno(ENODEV); }
virtual BAN::ErrorOr<size_t> write_impl(off_t, BAN::ConstByteSpan) override { return BAN::Error::from_errno(ENODEV); }
virtual BAN::ErrorOr<void> truncate_impl(size_t) override { return BAN::Error::from_errno(ENODEV); }
@@ -110,7 +133,7 @@ namespace Kernel
static BAN::ErrorOr<BAN::RefPtr<TmpSymlinkInode>> create_new(TmpFileSystem&, mode_t, uid_t, gid_t, BAN::StringView target);
~TmpSymlinkInode();
protected:
private:
BAN::ErrorOr<BAN::String> link_target_impl() override;
BAN::ErrorOr<void> set_link_target_impl(BAN::StringView) override;

View File

@@ -21,7 +21,7 @@ namespace Kernel
virtual uint32_t lock_depth() const = 0;
};
class Mutex : public BaseMutex
class Mutex final : public BaseMutex
{
BAN_NON_COPYABLE(Mutex);
BAN_NON_MOVABLE(Mutex);
@@ -40,6 +40,7 @@ namespace Kernel
pid_t expected = -1;
while (!m_locker.compare_exchange(expected, tid))
{
ASSERT(Processor::get_interrupt_state() == InterruptState::Enabled);
Processor::yield();
expected = -1;
}
@@ -84,13 +85,14 @@ namespace Kernel
pid_t locker() const override { return m_locker; }
bool is_locked() const override { return m_locker != -1; }
uint32_t lock_depth() const override { return m_lock_depth; }
bool is_locked_by_current_thread() const { return m_locker == Thread::current_tid(); }
private:
BAN::Atomic<pid_t> m_locker { -1 };
uint32_t m_lock_depth { 0 };
};
class PriorityMutex : public BaseMutex
class PriorityMutex final : public BaseMutex
{
BAN_NON_COPYABLE(PriorityMutex);
BAN_NON_MOVABLE(PriorityMutex);
@@ -113,6 +115,7 @@ namespace Kernel
pid_t expected = -1;
while (!(has_priority || m_queue_length == 0) || !m_locker.compare_exchange(expected, tid))
{
ASSERT(Processor::get_interrupt_state() == InterruptState::Enabled);
Processor::yield();
expected = -1;
}
@@ -164,6 +167,7 @@ namespace Kernel
pid_t locker() const override { return m_locker; }
bool is_locked() const override { return m_locker != -1; }
uint32_t lock_depth() const override { return m_lock_depth; }
bool is_locked_by_current_thread() const { return m_locker == Thread::current_tid(); }
private:
BAN::Atomic<pid_t> m_locker { -1 };

View File

@@ -18,6 +18,7 @@ namespace Kernel
// FIXME: this should probably be ordered tree like map
// for fast lookup and less memory usage
BAN::Vector<paddr_t> pages;
BAN::Vector<uint32_t> writers;
BAN::RefPtr<Inode> inode;
};

View File

@@ -100,13 +100,14 @@ namespace Kernel
BAN::ErrorOr<long> open_inode(VirtualFileSystem::File&&, int flags);
BAN::ErrorOr<void> create_file_or_dir(int fd, const char* path, mode_t mode) const;
BAN::ErrorOr<long> sys_openat(int, const char* path, int, mode_t);
BAN::ErrorOr<void> create_file(int fd, const char* path, mode_t) const;
BAN::ErrorOr<long> sys_openat(int fd, const char* path, int flags, 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_access(const char* path, int amode);
BAN::ErrorOr<long> sys_create_dir(const char*, mode_t);
BAN::ErrorOr<long> sys_mkdirat(int fd, const char* path, mode_t);
BAN::ErrorOr<long> sys_mkfifoat(int fd, const char* path, mode_t);
BAN::ErrorOr<long> sys_hardlinkat(int fd1, const char* path1, int fd2, const char* path2, int flag);
BAN::ErrorOr<long> sys_renameat(int oldfd, const char* old, int newfd, const char* _new);
BAN::ErrorOr<long> sys_unlinkat(int fd, const char* path, int flag);
@@ -214,6 +215,7 @@ namespace Kernel
BAN::ErrorOr<long> sys_pthread_join(pthread_t thread, void** value);
BAN::ErrorOr<long> sys_pthread_self();
BAN::ErrorOr<long> sys_pthread_kill(pthread_t thread, int signal);
BAN::ErrorOr<long> sys_pthread_detach(pthread_t thread);
BAN::ErrorOr<long> sys_clock_gettime(clockid_t, timespec*);

View File

@@ -51,15 +51,6 @@ namespace Kernel
virtual void clear() = 0;
virtual BAN::ErrorOr<void> chmod_impl(mode_t) override;
virtual BAN::ErrorOr<void> chown_impl(uid_t, gid_t) override;
virtual BAN::ErrorOr<long> ioctl_impl(int, void*) override;
virtual bool can_read_impl() const override { return m_output.flush; }
virtual bool has_error_impl() const override { return false; }
virtual bool has_hungup_impl() const override { return false; }
virtual bool master_has_closed() const { return false; }
protected:
@@ -68,10 +59,16 @@ namespace Kernel
virtual bool putchar_impl(uint8_t ch) = 0;
virtual void after_write() {}
void update_winsize(unsigned short cols, unsigned short rows);
virtual BAN::ErrorOr<size_t> read_impl(off_t, BAN::ByteSpan) final override;
virtual BAN::ErrorOr<size_t> write_impl(off_t, BAN::ConstByteSpan) final override;
void update_winsize(unsigned short cols, unsigned short rows);
virtual bool can_read_impl() const override { return m_output.flush; }
virtual bool has_error_impl() const override { return false; }
virtual bool has_hungup_impl() const override { return false; }
virtual BAN::ErrorOr<long> ioctl_impl(int, void*) override;
private:
bool putchar(uint8_t ch);
@@ -105,7 +102,7 @@ namespace Kernel
protected:
Mutex m_mutex;
RecursiveSpinLock m_write_lock;
Mutex m_write_lock;
ThreadBlocker m_write_blocker;
};

View File

@@ -117,6 +117,9 @@ namespace Kernel
const Process& process() const;
bool has_process() const { return m_process; }
void detach() { m_is_detached = true; }
bool is_detached() const { return m_is_detached; }
bool is_userspace() const { return m_is_userspace; }
uint64_t cpu_time_ns() const;
@@ -176,6 +179,7 @@ namespace Kernel
State m_state { State::NotStarted };
Process* m_process { nullptr };
bool m_is_userspace { false };
BAN::Atomic<bool> m_is_detached { false };
bool m_delete_process { false };
vaddr_t m_fsbase { 0 };

View File

@@ -14,6 +14,24 @@ namespace Kernel
return BAN::RefPtr<Epoll>::adopt(epoll_ptr);
}
Epoll::Epoll()
{
m_ino = 0;
m_mode = Mode::IRUSR | Mode::IWUSR;
m_nlink = 0;
m_uid = 0;
m_gid = 0;
m_size = 0;
m_atime = {};
m_mtime = {};
m_ctime = {};
m_blksize = PAGE_SIZE;
m_blocks = 0;
m_dev = 0;
m_rdev = 0;
m_kind = InodeKind::EPOLL;
}
Epoll::~Epoll()
{
for (auto& [inode, _] : m_listening_events)

View File

@@ -153,7 +153,7 @@ namespace Kernel
MUST(static_cast<TmpDirectoryInode*>(root_inode().ptr())->link_inode(*device, device->name()));
MUST(m_devices.push_back(device));
dprintln("Added device /dev/{}", device->name());
dprintln_if(DEBUG_DEVFS, "Added device /dev/{}", device->name());
}
void DevFileSystem::remove_device(BAN::RefPtr<Device> device)
@@ -170,7 +170,7 @@ namespace Kernel
}
}
dprintln("Removed device /dev/{}", device->name());
dprintln_if(DEBUG_DEVFS, "Removed device /dev/{}", device->name());
}
void DevFileSystem::add_inode(BAN::StringView path, BAN::RefPtr<TmpInode> inode)

View File

@@ -14,6 +14,25 @@ namespace Kernel
return BAN::RefPtr<Inode>(BAN::RefPtr<EventFD>::adopt(eventfd_ptr));
}
EventFD::EventFD(uint64_t initval, bool is_semaphore)
: m_is_semaphore(is_semaphore)
, m_value(initval)
{
m_ino = 0;
m_mode = Mode::IFCHR | Mode::IRUSR | Mode::IWUSR;
m_nlink = 0;
m_uid = 0;
m_gid = 0;
m_size = 0;
m_atime = {};
m_mtime = {};
m_ctime = {};
m_blksize = 8;
m_blocks = 0;
m_dev = 0;
m_rdev = 0;
}
BAN::ErrorOr<size_t> EventFD::read_impl(off_t, BAN::ByteSpan buffer)
{
if (buffer.size() < sizeof(uint64_t))

View File

@@ -73,6 +73,22 @@ namespace Kernel
return &m_fs;
}
BAN::ErrorOr<void> Ext2Inode::sync_inode(SyncType)
{
RWLockRDGuard _(m_lock);
TRY(sync_inode_no_lock());
return {};
}
BAN::ErrorOr<void> Ext2Inode::sync_data()
{
RWLockRDGuard _(m_lock);
for (size_t i = 0; i < max_used_data_block_count(); i++)
if (const auto fs_block = TRY(fs_block_of_data_block_index_no_lock(i, false)); fs_block.has_value())
TRY(m_fs.sync_block(fs_block.value()));
return {};
}
BAN::ErrorOr<BAN::Optional<uint32_t>> Ext2Inode::block_from_indirect_block_no_lock(uint32_t& block, uint32_t index, uint32_t depth, bool allocate)
{
const uint32_t indices_per_fs_block = blksize() / sizeof(uint32_t);
@@ -195,7 +211,7 @@ namespace Kernel
memset(m_ext2_blocks.block, 0, sizeof(m_ext2_blocks.block));
memcpy(m_ext2_blocks.block, target.data(), target.size());
m_size = target.size();
TRY(sync_no_lock());
TRY(sync_inode_no_lock());
return {};
}
@@ -334,7 +350,7 @@ namespace Kernel
const auto old_size = static_cast<size_t>(m_size);
m_size = new_size;
if (auto ret = sync_no_lock(); ret.is_error())
if (auto ret = sync_inode_no_lock(); ret.is_error())
{
m_size = old_size;
return ret.release_error();
@@ -343,82 +359,6 @@ namespace Kernel
return {};
}
BAN::ErrorOr<void> Ext2Inode::chmod_impl(mode_t mode)
{
ASSERT((mode & Inode::Mode::TYPE_MASK) == 0);
RWLockWRGuard _(m_lock);
if (m_mode == mode)
return {};
const auto old_mode = m_mode.load();
m_mode = (m_mode & Inode::Mode::TYPE_MASK) | mode;
if (auto ret = sync_no_lock(); ret.is_error())
{
m_mode = old_mode;
return ret.release_error();
}
return {};
}
BAN::ErrorOr<void> Ext2Inode::chown_impl(uid_t uid, gid_t gid)
{
RWLockWRGuard _(m_lock);
if (m_uid == uid && m_gid == gid)
return {};
const auto old_uid = m_uid.load();
const auto old_gid = m_gid.load();
m_uid = uid;
m_gid = gid;
if (auto ret = sync_no_lock(); ret.is_error())
{
m_uid = old_uid;
m_gid = old_gid;
return ret.release_error();
}
return {};
}
BAN::ErrorOr<void> Ext2Inode::utimens_impl(const timespec times[2])
{
RWLockWRGuard _(m_lock);
const uint32_t old_times[2] {
static_cast<uint32_t>(m_atime.tv_sec),
static_cast<uint32_t>(m_mtime.tv_sec),
};
if (times[0].tv_nsec != UTIME_OMIT)
m_atime.tv_sec = times[0].tv_sec;
if (times[1].tv_nsec != UTIME_OMIT)
m_mtime.tv_sec = times[1].tv_sec;
if (auto ret = sync_no_lock(); ret.is_error())
{
m_atime.tv_sec = old_times[0];
m_mtime.tv_sec = old_times[1];
return ret.release_error();
}
return {};
}
BAN::ErrorOr<void> Ext2Inode::fsync_impl()
{
RWLockRDGuard _(m_lock);
for (size_t i = 0; i < max_used_data_block_count(); i++)
if (const auto fs_block = TRY(fs_block_of_data_block_index_no_lock(i, false)); fs_block.has_value())
TRY(m_fs.sync_block(fs_block.value()));
return {};
}
BAN::ErrorOr<void> Ext2Inode::cleanup_indirect_block_no_lock(uint32_t block, uint32_t depth)
{
ASSERT(block);
@@ -467,7 +407,7 @@ done:
// mark blocks as deleted
memset(m_ext2_blocks.block, 0x00, sizeof(m_ext2_blocks.block));
TRY(sync_no_lock());
TRY(sync_inode_no_lock());
return {};
}
@@ -747,7 +687,7 @@ done:
memcpy(new_entry.name, name.data(), name.size());
inode.m_nlink++;
TRY(inode.sync_no_lock());
TRY(inode.sync_inode_no_lock());
return {};
};
@@ -872,13 +812,13 @@ needs_new_block:
if (entry_name == "."_sv)
{
m_nlink--;
TRY(sync_no_lock());
TRY(sync_inode_no_lock());
}
else if (entry_name == ".."_sv)
{
auto parent = TRY(Ext2Inode::create(m_fs, entry.inode));
parent->m_nlink--;
TRY(parent->sync_no_lock());
TRY(parent->sync_inode_no_lock());
}
else
ASSERT_NOT_REACHED();
@@ -935,7 +875,7 @@ needs_new_block:
else
inode->m_nlink--;
TRY(sync_no_lock());
TRY(sync_inode_no_lock());
// NOTE: If this was the last link to inode we must
// remove it from inode cache to trigger cleanup
@@ -964,13 +904,9 @@ needs_new_block:
return {};
}
BAN::ErrorOr<void> Ext2Inode::sync_no_lock()
BAN::ErrorOr<void> Ext2Inode::sync_inode_no_lock()
{
auto inode_location = TRY(m_fs.locate_inode(ino()));
auto block_buffer = TRY(m_fs.get_block_buffer());
TRY(m_fs.read_block(inode_location.block, block_buffer));
Ext2::Inode inode {
const Ext2::Inode inode {
.mode = static_cast<uint16_t>(m_mode),
.uid = static_cast<uint16_t>(m_uid),
.size = static_cast<uint32_t>(m_size),
@@ -988,8 +924,13 @@ needs_new_block:
.file_acl = m_og_file_acl,
.dir_acl = m_og_dir_acl,
.faddr = m_og_faddr,
.osd2 = m_og_osd2
.osd2 = m_og_osd2,
};
auto inode_location = TRY(m_fs.locate_inode(ino()));
auto block_buffer = TRY(m_fs.get_block_buffer());
TRY(m_fs.read_block(inode_location.block, block_buffer));
if (memcmp(block_buffer.data() + inode_location.offset, &inode, sizeof(Ext2::Inode)))
{
memcpy(block_buffer.data() + inode_location.offset, &inode, sizeof(Ext2::Inode));

View File

@@ -246,50 +246,73 @@ namespace Kernel
BAN::ErrorOr<void> Inode::chmod(mode_t mode)
{
ASSERT((mode & Inode::Mode::TYPE_MASK) == 0);
if (auto* fs = filesystem(); fs && (fs->flag() & ST_RDONLY))
return BAN::Error::from_errno(EROFS);
return chmod_impl(mode);
ASSERT((mode & Inode::Mode::TYPE_MASK) == 0);
mode |= m_mode & Inode::Mode::TYPE_MASK;
const auto old_mode = m_mode.exchange(mode);
if (auto ret = sync_inode(SyncType::Mode); ret.is_error())
{
m_mode.compare_exchange(mode, old_mode);
return ret.release_error();
}
return {};
}
BAN::ErrorOr<void> Inode::chown(uid_t uid, gid_t gid)
{
if (auto* fs = filesystem(); fs && (fs->flag() & ST_RDONLY))
return BAN::Error::from_errno(EROFS);
return chown_impl(uid, gid);
// TODO: unify uid and gid to a single atomic operation.
// this needs 64 bit atomic support from 32 bit target
const auto old_uid = m_uid.exchange(uid);
const auto old_gid = m_gid.exchange(gid);
if (auto ret = sync_inode(SyncType::UidGid); ret.is_error())
{
m_uid.compare_exchange(uid, old_uid);
m_gid.compare_exchange(gid, old_gid);
return ret.release_error();
}
return {};
}
BAN::ErrorOr<void> Inode::utimens(const timespec times[2])
{
if (auto* fs = filesystem(); fs && (fs->flag() & ST_RDONLY))
return BAN::Error::from_errno(EROFS);
return utimens_impl(times);
// TODO: make these atomic
const auto old_atime = m_atime;
const auto old_mtime = m_mtime;
m_atime = times[0];
m_mtime = times[1];
if (auto ret = sync_inode(SyncType::Times); ret.is_error())
{
m_atime = old_atime;
m_mtime = old_mtime;
return ret.release_error();
}
return {};
}
BAN::ErrorOr<void> Inode::fsync()
{
// TODO: should we sync shared data?
return fsync_impl();
}
bool Inode::can_read() const
{
return can_read_impl();
}
bool Inode::can_write() const
{
return can_write_impl();
}
bool Inode::has_error() const
{
return has_error_impl();
}
bool Inode::has_hungup() const
{
return has_hungup_impl();
// TODO: should we sync MAP_SHARED data?
TRY(sync_inode(SyncType::General));
TRY(sync_data());
return {};
}
BAN::ErrorOr<long> Inode::ioctl(int request, void* arg)

View File

@@ -1,3 +1,4 @@
#include <BAN/HashMap.h>
#include <kernel/FS/Pipe.h>
#include <kernel/Lock/LockGuard.h>
#include <kernel/Thread.h>
@@ -12,34 +13,124 @@ namespace Kernel
static constexpr size_t s_pipe_buffer_size = 0x10000;
BAN::ErrorOr<BAN::RefPtr<Inode>> Pipe::create(const Credentials& credentials)
static Mutex s_named_pipe_mutex;
static BAN::HashMap<BAN::RefPtr<Inode>, BAN::WeakPtr<Pipe>> s_named_pipes;
BAN::ErrorOr<BAN::RefPtr<Inode>> Pipe::open(BAN::RefPtr<Inode> inode, int status_flags)
{
auto* pipe_ptr = new Pipe(credentials);
BAN::RefPtr<Pipe> pipe;
{
LockGuard _(s_named_pipe_mutex);
auto it = s_named_pipes.find(inode);
if (it == s_named_pipes.end())
it = TRY(s_named_pipes.insert(inode, {}));
if (!(pipe = it->value.lock()))
{
// FIXME: these should probably reference the underlying inode(?)
const struct stat st {
.st_dev = inode->dev(),
.st_ino = inode->ino(),
.st_mode = inode->mode().mode,
.st_nlink = inode->nlink(),
.st_uid = inode->uid(),
.st_gid = inode->gid(),
.st_rdev = inode->rdev(),
.st_size = inode->size(),
.st_atim = inode->atime(),
.st_mtim = inode->mtime(),
.st_ctim = inode->ctime(),
.st_blksize = inode->blksize(),
.st_blocks = inode->blocks(),
};
auto* pipe_ptr = new Pipe(st);
if (pipe_ptr == nullptr)
return BAN::Error::from_errno(ENOMEM);
pipe = BAN::RefPtr<Pipe>::adopt(pipe_ptr);
pipe->m_buffer = TRY(ByteRingBuffer::create(s_pipe_buffer_size));
pipe->m_named_inode = inode;
it->value = TRY(pipe->get_weak_ptr());
}
}
LockGuard _(pipe->m_mutex);
if (status_flags & O_RDONLY)
pipe->m_reading_count++;
if (status_flags & O_WRONLY)
pipe->m_writing_count++;
if (status_flags & O_NONBLOCK)
{
if ((status_flags & O_WRONLY) && pipe->m_writing_count == 0)
return BAN::Error::from_errno(ENXIO);
return BAN::RefPtr<Inode>(pipe);
}
auto& block_value = (status_flags & O_WRONLY) ? pipe->m_reading_count : pipe->m_writing_count;
while (block_value == 0)
TRY(Thread::current().block_or_eintr_indefinite(pipe->m_thread_blocker, &pipe->m_mutex));
return BAN::RefPtr<Inode>(pipe);
}
BAN::ErrorOr<BAN::RefPtr<Inode>> Pipe::create(uid_t uid, gid_t gid)
{
const timespec current_time = SystemTimer::get().real_time();
const struct stat st {
.st_dev = 0, // FIXME
.st_ino = 0, // FIXME
.st_mode = Mode::IFIFO | Mode::IRUSR | Mode::IWUSR,
.st_nlink = 0,
.st_uid = uid,
.st_gid = gid,
.st_rdev = 0, // FIXME
.st_size = 0,
.st_atim = current_time,
.st_mtim = current_time,
.st_ctim = current_time,
.st_blksize = PAGE_SIZE,
.st_blocks = 0,
};
auto* pipe_ptr = new Pipe(st);
if (pipe_ptr == nullptr)
return BAN::Error::from_errno(ENOMEM);
auto pipe = BAN::RefPtr<Pipe>::adopt(pipe_ptr);
pipe->m_buffer = TRY(ByteRingBuffer::create(s_pipe_buffer_size));
pipe->m_reading_count++;
pipe->m_writing_count++;
return BAN::RefPtr<Inode>(pipe);
}
Pipe::Pipe(const Credentials& credentials)
Pipe::Pipe(const struct stat& st)
{
timespec current_time = SystemTimer::get().real_time();
m_atime = current_time;
m_mtime = current_time;
m_ctime = current_time;
m_uid = credentials.euid();
m_gid = credentials.egid();
m_ino = st.st_ino;
m_mode = st.st_mode;
m_nlink = st.st_nlink;
m_uid = st.st_uid;
m_gid = st.st_gid;
m_size = st.st_size;
m_atime = st.st_atim;
m_mtime = st.st_mtim;
m_ctime = st.st_ctim;
m_blksize = st.st_blksize;
m_blocks = st.st_blocks;
m_dev = st.st_dev;
m_rdev = st.st_rdev;
m_ino = 0; // FIXME
m_mode = { Mode::IFIFO | Mode::IRUSR | Mode::IWUSR };
m_nlink = 1;
m_size = 0;
m_blksize = 4096;
m_blocks = 0;
m_dev = 0; // FIXME
m_rdev = 0; // FIXME
m_kind = InodeKind::PIPE;
m_kind |= InodeKind::PIPE;
}
Pipe::~Pipe()
{
if (!m_named_inode)
return;
LockGuard _(s_named_pipe_mutex);
s_named_pipes.remove(m_named_inode);
}
void Pipe::on_clone(int status_flags)
@@ -82,6 +173,44 @@ namespace Kernel
m_thread_blocker.unblock();
}
BAN::ErrorOr<void> Pipe::sync_inode(SyncType type)
{
if (!m_named_inode)
return {};
switch (type)
{
case SyncType::General:
break;
case SyncType::Mode:
TRY(m_named_inode->chmod(m_mode));
break;
case SyncType::UidGid:
TRY(m_named_inode->chown(m_uid, m_gid));
break;
case SyncType::Times:
const timespec times[] { m_atime, m_mtime };
TRY(m_named_inode->utimens(times));
break;
}
m_mode = m_named_inode->mode().mode;
m_uid = m_named_inode->uid();
m_gid = m_named_inode->gid();
m_atime = m_named_inode->atime();
m_mtime = m_named_inode->mtime();
m_ctime = m_named_inode->ctime();
return {};
}
BAN::ErrorOr<void> Pipe::sync_data()
{
return {};
}
BAN::ErrorOr<size_t> Pipe::read_impl(off_t, BAN::ByteSpan buffer)
{
LockGuard _(m_mutex);
@@ -134,6 +263,10 @@ namespace Kernel
return to_copy;
}
BAN::ErrorOr<void> Pipe::truncate_impl(size_t)
{
return BAN::Error::from_errno(ENODEV);
}
BAN::ErrorOr<long> Pipe::ioctl_impl(int cmd, void* arg)
{

View File

@@ -92,41 +92,14 @@ namespace Kernel
{
if (nlink() > 0)
{
sync();
write_inode_to_fs();
return;
}
free_all_blocks();
m_fs.delete_inode(ino());
}
BAN::ErrorOr<void> TmpInode::chmod_impl(mode_t new_mode)
{
// FIXME: make this atomic
ASSERT(!(new_mode & Inode::Mode::TYPE_MASK));
m_mode &= Inode::Mode::TYPE_MASK;
m_mode |= new_mode;
return {};
}
BAN::ErrorOr<void> TmpInode::chown_impl(uid_t new_uid, gid_t new_gid)
{
// FIXME: make this atomic
m_uid = new_uid;
m_gid = new_gid;
return {};
}
BAN::ErrorOr<void> TmpInode::utimens_impl(const timespec times[2])
{
// FIXME: make this atomic
if (times[0].tv_nsec != UTIME_OMIT)
m_atime = times[0];
if (times[1].tv_nsec != UTIME_OMIT)
m_atime = times[1];
return {};
}
void TmpInode::sync()
void TmpInode::write_inode_to_fs()
{
TmpInodeInfo info = {
.mode = m_mode.load(),
@@ -143,6 +116,16 @@ namespace Kernel
m_fs.write_inode(m_ino, info);
}
BAN::ErrorOr<void> TmpInode::sync_inode(SyncType)
{
return {};
}
BAN::ErrorOr<void> TmpInode::sync_data()
{
return {};
}
void TmpInode::free_all_blocks()
{
LockGuard _(m_lock);
@@ -408,7 +391,32 @@ namespace Kernel
return {};
}
/* FIFO INODE */
BAN::ErrorOr<BAN::RefPtr<TmpFIFOInode>> TmpFIFOInode::create_new(TmpFileSystem& fs, mode_t mode, uid_t uid, gid_t gid)
{
auto info = create_inode_info(Mode::IFIFO | mode, uid, gid);
ino_t ino = TRY(fs.allocate_inode(info));
auto* inode_ptr = new TmpFIFOInode(fs, ino, info);
if (inode_ptr == nullptr)
return BAN::Error::from_errno(ENOMEM);
return BAN::RefPtr<TmpFIFOInode>::adopt(inode_ptr);
}
TmpFIFOInode::TmpFIFOInode(TmpFileSystem& fs, ino_t ino, const TmpInodeInfo& info)
: TmpInode(fs, ino, info)
{
ASSERT(mode().ififo());
}
TmpFIFOInode::~TmpFIFOInode()
{
}
/* SOCKET INODE */
BAN::ErrorOr<BAN::RefPtr<TmpSocketInode>> TmpSocketInode::create_new(TmpFileSystem& fs, mode_t mode, uid_t uid, gid_t gid)
{
auto info = create_inode_info(Mode::IFSOCK | mode, uid, gid);
@@ -679,6 +687,9 @@ namespace Kernel
case Mode::IFLNK:
new_inode = TRY(TmpSymlinkInode::create_new(m_fs, mode, uid, gid, ""_sv));
break;
case Mode::IFIFO:
new_inode = TRY(TmpFIFOInode::create_new(m_fs, mode, uid, gid));
break;
case Mode::IFSOCK:
new_inode = TRY(TmpSocketInode::create_new(m_fs, mode, uid, gid));
break;

View File

@@ -39,7 +39,9 @@ namespace Kernel
if (!(region->m_shared_data = inode->m_shared_region.lock()))
{
auto shared_data = TRY(BAN::RefPtr<SharedFileData>::create());
TRY(shared_data->pages.resize(BAN::Math::div_round_up<size_t>(inode->size(), PAGE_SIZE)));
const size_t page_count = BAN::Math::div_round_up<size_t>(inode->size(), PAGE_SIZE);
TRY(shared_data->pages.resize(page_count, 0));
TRY(shared_data->writers.resize(page_count, 0));
shared_data->inode = inode;
inode->m_shared_region = TRY(shared_data->get_weak_ptr());
region->m_shared_data = BAN::move(shared_data);
@@ -59,9 +61,21 @@ namespace Kernel
{
if (m_vaddr == 0)
return;
for (paddr_t dirty_page : m_dirty_pages)
if (dirty_page)
Heap::get().release_page(dirty_page);
switch (m_type)
{
case Type::PRIVATE:
for (paddr_t dirty_page : m_dirty_pages)
if (dirty_page)
Heap::get().release_page(dirty_page);
break;
case Type::SHARED:
const size_t page_count = BAN::Math::div_round_up<size_t>(size(), PAGE_SIZE);
for (size_t i = 0; i < page_count; i++)
if (m_page_table.get_page_flags(m_vaddr + i * PAGE_SIZE) & PageTable::Flags::ReadWrite)
BAN::atomic_sub_fetch(m_shared_data->writers[m_offset / PAGE_SIZE + i], 1);
break;
}
}
SharedFileData::~SharedFileData()
@@ -79,7 +93,7 @@ namespace Kernel
void SharedFileData::sync_no_lock(size_t page_index)
{
if (pages[page_index] == 0)
if (pages[page_index] == 0 || BAN::atomic_load(writers[page_index]) == 0)
return;
uint8_t page_buffer[PAGE_SIZE];
@@ -179,11 +193,12 @@ namespace Kernel
}
else
{
const paddr_t paddr = m_shared_data->pages[shared_page_index];
auto flags = m_flags;
if (m_type == Type::PRIVATE)
flags &= ~PageTable::Flags::ReadWrite;
m_page_table.map_page_at(paddr, vaddr, flags);
if (flags & PageTable::Flags::ReadWrite)
BAN::atomic_add_fetch(m_shared_data->writers[shared_page_index], 1);
m_page_table.map_page_at(m_shared_data->pages[shared_page_index], vaddr, flags);
}
}
else

View File

@@ -31,8 +31,10 @@ namespace Kernel
return BAN::Error::from_errno(EINVAL);
size_t length = 0;
while (length < address_len - sizeof(sa_family_t) && sockaddr_un.sun_path[length])
while (length < sizeof(sockaddr_un::sun_path) && sockaddr_un.sun_path[length])
length++;
if (length >= sizeof(sockaddr_un::sun_path))
return BAN::Error::from_errno(ENAMETOOLONG);
return BAN::StringView { sockaddr_un.sun_path, length };
}
@@ -269,7 +271,7 @@ namespace Kernel
auto parent_file = sun_path.front() == '/'
? TRY(Process::current().root_file().clone())
: TRY(Process::current().working_directory().clone());
if (auto ret = Process::current().create_file_or_dir(AT_FDCWD, sun_path.data(), 0755 | S_IFSOCK); ret.is_error())
if (auto ret = Process::current().create_file(AT_FDCWD, sun_path.data(), 0755 | Inode::Mode::IFSOCK); ret.is_error())
{
if (ret.error().get_error_code() == EEXIST)
return BAN::Error::from_errno(EADDRINUSE);

View File

@@ -85,6 +85,9 @@ namespace Kernel
constexpr int status_mask = O_APPEND | O_DSYNC | O_NONBLOCK | O_RSYNC | O_SYNC | O_ACCMODE;
if (file.inode->mode().ififo())
file.inode = TRY(Pipe::open(file.inode, flags & status_mask));
LockGuard _(m_mutex);
const int fd = TRY(get_free_fd());
@@ -214,7 +217,7 @@ namespace Kernel
TRY(get_free_fd_pair(fds));
auto pipe = TRY(Pipe::create(m_credentials));
auto pipe = TRY(Pipe::create(m_credentials.euid(), m_credentials.egid()));
m_open_files[fds[0]] = TRY(BAN::RefPtr<OpenFileDescription>::create(VirtualFileSystem::File(pipe, "<pipe rd>"_sv), 0, O_RDONLY));
m_open_files[fds[1]] = TRY(BAN::RefPtr<OpenFileDescription>::create(VirtualFileSystem::File(pipe, "<pipe wr>"_sv), 0, O_WRONLY));

View File

@@ -439,6 +439,8 @@ namespace Kernel
.cancel_type = 0,
.cancel_state = 0,
.canceled = 0,
.specific_keys = {},
.specific_values = {},
.dtv = { 0, region->vaddr() }
};
@@ -1161,26 +1163,27 @@ namespace Kernel
}
}
BAN::ErrorOr<void> Process::create_file_or_dir(int fd, const char* path, mode_t mode) const
BAN::ErrorOr<void> Process::create_file(int fd, const char* path, mode_t mode) const
{
switch (mode & Inode::Mode::TYPE_MASK)
{
case Inode::Mode::IFREG:
case Inode::Mode::IFDIR:
case Inode::Mode::IFLNK:
case Inode::Mode::IFIFO:
case Inode::Mode::IFSOCK:
break;
default:
return BAN::Error::from_errno(ENOTSUP);
ASSERT_NOT_REACHED();
}
auto [parent, file_name] = TRY(find_parent_file(fd, path, O_EXEC | O_WRONLY));
struct uid_gid_t { uid_t uid; gid_t gid; };
const auto [uid, gid] = ({
LockGuard _(m_process_lock);
uid_gid_t { m_credentials.euid(), m_credentials.egid() };
});
if (Inode::Mode(mode).ifdir())
TRY(parent.inode->create_directory(file_name, mode, m_credentials.euid(), parent.inode->gid()));
else
TRY(parent.inode->create_file(file_name, mode, m_credentials.euid(), parent.inode->gid()));
auto [parent, file_name] = TRY(find_parent_file(fd, path, O_EXEC | O_WRONLY));
TRY(parent.inode->create_file(file_name, mode, uid, gid));
return {};
}
@@ -1299,26 +1302,40 @@ namespace Kernel
return 0;
}
BAN::ErrorOr<long> Process::sys_create_dir(const char* user_path, mode_t mode)
BAN::ErrorOr<long> Process::sys_mkdirat(int fd, const char* user_path, mode_t mode)
{
char path[PATH_MAX];
TRY(read_string_from_user(user_path, path, PATH_MAX));
uid_t uid;
gid_t gid;
{
struct uid_gid_t { uid_t uid; gid_t gid; };
const auto [uid, gid] = ({
LockGuard _(m_process_lock);
uid = m_credentials.euid();
gid = m_credentials.egid();
}
uid_gid_t { m_credentials.euid(), m_credentials.egid() };
});
auto [parent, file_name] = TRY(find_parent_file(AT_FDCWD, path, O_WRONLY));
auto [parent, file_name] = TRY(find_parent_file(fd, path, O_WRONLY));
TRY(parent.inode->create_directory(file_name, (mode & 0777) | Inode::Mode::IFDIR, uid, gid));
return 0;
}
BAN::ErrorOr<long> Process::sys_mkfifoat(int fd, const char* user_path, mode_t mode)
{
char path[PATH_MAX];
TRY(read_string_from_user(user_path, path, PATH_MAX));
struct uid_gid_t { uid_t uid; gid_t gid; };
const auto [uid, gid] = ({
LockGuard _(m_process_lock);
uid_gid_t { m_credentials.euid(), m_credentials.egid() };
});
auto [parent, file_name] = TRY(find_parent_file(fd, path, O_WRONLY));
TRY(parent.inode->create_file(file_name, (mode & 0777) | Inode::Mode::IFIFO, uid, gid));
return 0;
}
BAN::ErrorOr<long> Process::sys_hardlinkat(int fd1, const char* user_path1, int fd2, const char* user_path2, int flag)
{
char path1[PATH_MAX];
@@ -1374,7 +1391,6 @@ namespace Kernel
if (user_path != nullptr)
TRY(read_string_from_user(user_path, path, PATH_MAX));
auto [parent, file_name] = TRY(find_parent_file(fd, user_path ? path : nullptr, O_WRONLY));
const auto inode = TRY(parent.inode->find_inode(file_name));
@@ -1416,15 +1432,11 @@ namespace Kernel
TRY(read_string_from_user(user_path1, path1, PATH_MAX));
char path2[PATH_MAX];
if (user_path2 != nullptr)
TRY(read_string_from_user(user_path2, path2, PATH_MAX));
TRY(read_string_from_user(user_path2, path2, PATH_MAX));
if (!find_file(fd, user_path2 ? path2 : nullptr, O_NOFOLLOW).is_error())
return BAN::Error::from_errno(EEXIST);
TRY(create_file(fd, path2, 0777 | Inode::Mode::IFLNK));
TRY(create_file_or_dir(fd, user_path2 ? path2 : nullptr, 0777 | Inode::Mode::IFLNK));
auto symlink = TRY(find_file(fd, user_path2 ? path2 : nullptr, O_NOFOLLOW));
auto symlink = TRY(find_file(fd, path2, O_NOFOLLOW));
TRY(symlink.inode->set_link_target(path1));
return 0;
@@ -1523,11 +1535,7 @@ namespace Kernel
if (flag == AT_SYMLINK_NOFOLLOW)
flag = O_NOFOLLOW;
const uint64_t current_ns = SystemTimer::get().ns_since_boot();
const timespec current_ts = {
.tv_sec = static_cast<time_t>(current_ns / 1'000'000),
.tv_nsec = static_cast<long>(current_ns % 1'000'000),
};
const timespec current_ts = SystemTimer::get().real_time();
timespec times[2];
if (user_times == nullptr)
@@ -1548,7 +1556,6 @@ namespace Kernel
else if (times[i].tv_nsec < 0 || times[i].tv_nsec >= 1'000'000'000)
return BAN::Error::from_errno(EINVAL);
}
}
if (times[0].tv_nsec == UTIME_OMIT && times[1].tv_nsec == UTIME_OMIT)
@@ -1569,6 +1576,11 @@ namespace Kernel
}
}
if (times[0].tv_nsec == UTIME_OMIT)
times[0] = inode->atime();
if (times[1].tv_nsec == UTIME_OMIT)
times[1] = inode->mtime();
TRY(inode->utimens(times));
return 0;
@@ -1763,12 +1775,13 @@ namespace Kernel
TRY(read_from_user(user_message, &message, sizeof(msghdr)));
BAN::Vector<MemoryRegion*> regions;
TRY(regions.reserve(!!message.msg_name + !!message.msg_control + !!message.msg_iov));
BAN::ScopeGuard _([&regions] {
for (auto* region : regions)
region->unpin();
});
// FIXME: this can leak memory if push to regions fails but pinning succeeded
if (message.msg_name)
TRY(regions.push_back(TRY(validate_and_pin_pointer_access(message.msg_name, message.msg_namelen, true))));
if (message.msg_control)
@@ -1776,6 +1789,7 @@ namespace Kernel
if (message.msg_iov)
{
TRY(regions.push_back(TRY(validate_and_pin_pointer_access(message.msg_iov, message.msg_iovlen * sizeof(iovec), true))));
TRY(regions.reserve(regions.size() + message.msg_iovlen));
for (int i = 0; i < message.msg_iovlen; i++)
TRY(regions.push_back(TRY(validate_and_pin_pointer_access(message.msg_iov[i].iov_base, message.msg_iov[i].iov_len, true))));
}
@@ -1793,6 +1807,8 @@ namespace Kernel
TRY(read_from_user(user_message, &message, sizeof(msghdr)));
BAN::Vector<MemoryRegion*> regions;
TRY(regions.reserve(!!message.msg_name + !!message.msg_control + !!message.msg_iov));
BAN::ScopeGuard _([&regions] {
for (auto* region : regions)
region->unpin();
@@ -1805,6 +1821,7 @@ namespace Kernel
if (message.msg_iov)
{
TRY(regions.push_back(TRY(validate_and_pin_pointer_access(message.msg_iov, message.msg_iovlen * sizeof(iovec), false))));
TRY(regions.reserve(regions.size() + message.msg_iovlen));
for (int i = 0; i < message.msg_iovlen; i++)
TRY(regions.push_back(TRY(validate_and_pin_pointer_access(message.msg_iov[i].iov_base, message.msg_iov[i].iov_len, false))));
}
@@ -3332,20 +3349,24 @@ namespace Kernel
if (m_threads.size() == 1)
return sys_exit(0);
TRY(m_exited_pthreads.emplace_back(Thread::current().tid(), value));
auto& thread = Thread::current();
if (!thread.is_detached())
{
TRY(m_exited_pthreads.emplace_back(thread.tid(), value));
m_pthread_exit_blocker.unblock();
}
m_pthread_exit_blocker.unblock();
m_process_lock.unlock();
Thread::current().on_exit();
thread.on_exit();
ASSERT_NOT_REACHED();
}
BAN::ErrorOr<long> Process::sys_pthread_join(pthread_t thread, void** user_value)
BAN::ErrorOr<long> Process::sys_pthread_join(pthread_t tid, void** user_value)
{
LockGuard _(m_process_lock);
if (thread == Thread::current().tid())
if (tid == Thread::current().tid())
return BAN::Error::from_errno(EINVAL);
const auto check_thread =
@@ -3353,13 +3374,10 @@ namespace Kernel
{
for (size_t i = 0; i < m_exited_pthreads.size(); i++)
{
if (m_exited_pthreads[i].thread != thread)
if (m_exited_pthreads[i].thread != tid)
continue;
void* ret = m_exited_pthreads[i].value;
m_exited_pthreads.remove(i);
return ret;
}
@@ -3377,10 +3395,18 @@ namespace Kernel
{
bool found = false;
for (auto* _thread : m_threads)
if (_thread->tid() == thread)
found = true;
bool joinable = false;
for (auto* thread : m_threads)
{
if (thread->tid() != tid)
continue;
found = true;
joinable = !thread->is_detached();
break;
}
if (!found)
return BAN::Error::from_errno(ESRCH);
if (!joinable)
return BAN::Error::from_errno(EINVAL);
}
@@ -3423,6 +3449,24 @@ namespace Kernel
return BAN::Error::from_errno(ESRCH);
}
BAN::ErrorOr<long> Process::sys_pthread_detach(pthread_t tid)
{
LockGuard _(m_process_lock);
for (auto* thread : m_threads)
{
if (thread->tid() != tid)
continue;
if (thread->is_detached())
return BAN::Error::from_errno(EINVAL);
thread->detach();
m_pthread_exit_blocker.unblock();
return 0;
}
return BAN::Error::from_errno(ESRCH);
}
BAN::ErrorOr<long> Process::sys_setuid(uid_t uid)
{
if (uid < 0 || uid >= 1'000'000'000)
@@ -3884,6 +3928,8 @@ namespace Kernel
region->pin();
return region.ptr();
}
return BAN::Error::from_errno(EFAULT);
}
validate_and_pin_pointer_access_with_allocation:

View File

@@ -124,6 +124,7 @@ namespace Kernel
case SYS_RECVMSG:
case SYS_SENDMSG:
case SYS_FLOCK:
case SYS_FUTEX:
return true;
default:
return false;

View File

@@ -155,22 +155,6 @@ namespace Kernel
DevFileSystem::get().add_inode("tty", MUST(DevTTY::create(0666, 0, 0)));
}
BAN::ErrorOr<void> TTY::chmod_impl(mode_t mode)
{
// FIXME: make this atomic
ASSERT((mode & Inode::Mode::TYPE_MASK) == 0);
m_mode &= Inode::Mode::TYPE_MASK;
m_mode |= mode;
return {};
}
BAN::ErrorOr<void> TTY::chown_impl(uid_t uid, gid_t gid)
{
m_uid = uid;
m_gid = gid;
return {};
}
void TTY::update_winsize(unsigned short cols, unsigned short rows)
{
// FIXME: make this atomic
@@ -424,7 +408,7 @@ namespace Kernel
const auto termios = get_termios();
SpinLockGuard _1(m_write_lock);
LockGuard _(m_write_lock);
if (termios.c_oflag & OPOST)
{
if ((termios.c_oflag & ONLCR) && ch == NL)
@@ -471,15 +455,13 @@ namespace Kernel
BAN::ErrorOr<size_t> TTY::write_impl(off_t, BAN::ConstByteSpan buffer)
{
SpinLockGuard write_guard(m_write_lock);
LockGuard write_guard(m_write_lock);
while (!can_write())
{
if (master_has_closed())
return BAN::Error::from_errno(EIO);
SpinLockGuardAsMutex smutex(write_guard);
TRY(Thread::current().block_or_eintr_indefinite(m_write_blocker, &smutex));
TRY(Thread::current().block_or_eintr_indefinite(m_write_blocker, &m_write_lock));
}
size_t written = 0;
@@ -497,7 +479,7 @@ namespace Kernel
void TTY::putchar_current(uint8_t ch)
{
ASSERT(s_tty);
SpinLockGuard _(s_tty->m_write_lock);
LockGuard _(s_tty->m_write_lock);
s_tty->putchar(ch);
s_tty->after_write();
}

View File

@@ -61,7 +61,7 @@ namespace Kernel
void VirtualTTY::clear()
{
SpinLockGuard _(m_write_lock);
LockGuard _(m_write_lock);
for (uint32_t i = 0; i < m_width * m_height; i++)
m_buffer[i] = { .foreground = m_foreground, .background = m_background, .codepoint = ' ' };
m_terminal_driver->clear(m_background);
@@ -73,7 +73,7 @@ namespace Kernel
return BAN::Error::from_errno(EINVAL);
{
SpinLockGuard _(m_write_lock);
LockGuard _(m_write_lock);
TRY(m_terminal_driver->set_font(BAN::move(font)));
@@ -110,7 +110,7 @@ namespace Kernel
void VirtualTTY::reset_ansi()
{
ASSERT(m_write_lock.current_processor_has_lock());
ASSERT(m_write_lock.is_locked_by_current_thread());
m_ansi_state = {
.nums = { -1, -1, -1, -1, -1 },
.index = 0,
@@ -121,7 +121,7 @@ namespace Kernel
void VirtualTTY::handle_ansi_csi_color(uint8_t value)
{
ASSERT(m_write_lock.current_processor_has_lock());
ASSERT(m_write_lock.is_locked_by_current_thread());
switch (value)
{
case 0:
@@ -201,7 +201,7 @@ namespace Kernel
void VirtualTTY::handle_ansi_csi(uint8_t ch)
{
ASSERT(m_write_lock.current_processor_has_lock());
ASSERT(m_write_lock.is_locked_by_current_thread());
switch (ch)
{
case '0': case '1': case '2': case '3': case '4':
@@ -446,7 +446,7 @@ namespace Kernel
void VirtualTTY::render_from_buffer(uint32_t x, uint32_t y)
{
ASSERT(m_write_lock.current_processor_has_lock());
ASSERT(m_write_lock.is_locked_by_current_thread());
ASSERT(x < m_width && y < m_height);
const auto& cell = m_buffer[y * m_width + x];
m_terminal_driver->putchar_at(cell.codepoint, x, y, cell.foreground, cell.background);
@@ -454,7 +454,7 @@ namespace Kernel
void VirtualTTY::putchar_at(uint32_t codepoint, uint32_t x, uint32_t y)
{
ASSERT(m_write_lock.current_processor_has_lock());
ASSERT(m_write_lock.is_locked_by_current_thread());
ASSERT(x < m_width && y < m_height);
auto& cell = m_buffer[y * m_width + x];
cell.codepoint = codepoint;
@@ -488,7 +488,7 @@ namespace Kernel
void VirtualTTY::putcodepoint(uint32_t codepoint)
{
ASSERT(m_write_lock.current_processor_has_lock());
ASSERT(m_write_lock.is_locked_by_current_thread());
switch (codepoint)
{
@@ -534,7 +534,7 @@ namespace Kernel
bool VirtualTTY::putchar_impl(uint8_t ch)
{
ASSERT(m_write_lock.current_processor_has_lock());
ASSERT(m_write_lock.is_locked_by_current_thread());
uint32_t codepoint = ch;

View File

@@ -0,0 +1,14 @@
#ifndef _BITS_TYPES_PTHREAD_KEY_H
#define _BITS_TYPES_PTHREAD_KEY_H 1
// https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/pthread.h.html
#include <sys/cdefs.h>
__BEGIN_DECLS
typedef unsigned pthread_key_t;
__END_DECLS
#endif

View File

@@ -8,14 +8,13 @@
__BEGIN_DECLS
#include <bits/types/pthread_attr_t.h>
#include <bits/types/pthread_key_t.h>
#include <bits/types/pthread_t.h>
#include <stdint.h>
typedef int pthread_once_t;
typedef unsigned pthread_key_t;
typedef pthread_t pthread_spinlock_t;
typedef struct

View File

@@ -8,7 +8,9 @@ __BEGIN_DECLS
#define __need_size_t
#include <sys/types.h>
#include <bits/types/pthread_key_t.h>
#include <bits/types/pthread_t.h>
#include <limits.h>
#include <stdint.h>
typedef struct _pthread_cleanup_t
@@ -44,6 +46,8 @@ struct uthread
int cancel_type;
int cancel_state;
volatile int canceled;
pthread_key_t specific_keys[PTHREAD_KEYS_MAX];
void* specific_values[PTHREAD_KEYS_MAX];
// FIXME: make this dynamic
uintptr_t dtv[1 + 256];
};

View File

@@ -52,7 +52,8 @@ __BEGIN_DECLS
O(SYS_TTY_CTRL, tty_ctrl) \
O(SYS_POWEROFF, poweroff) \
O(SYS_FCHMODAT, fchmodat) \
O(SYS_CREATE_DIR, create_dir) \
O(SYS_MKDIRAT, mkdirat) \
O(SYS_MKFIFOAT, mkfifoat) \
O(SYS_UNLINKAT, unlinkat) \
O(SYS_READLINKAT, readlinkat) \
O(SYS_MSYNC, msync) \
@@ -105,6 +106,7 @@ __BEGIN_DECLS
O(SYS_PTHREAD_JOIN, pthread_join) \
O(SYS_PTHREAD_SELF, pthread_self) \
O(SYS_PTHREAD_KILL, pthread_kill) \
O(SYS_PTHREAD_DETACH, pthread_detach) \
O(SYS_EPOLL_CREATE1, epoll_create1) \
O(SYS_EPOLL_CTL, epoll_ctl) \
O(SYS_EPOLL_PWAIT2, epoll_pwait2) \

View File

@@ -612,16 +612,17 @@ long syscall(long syscall, ...);
#include <kernel/API/Syscall.h>
#include <errno.h>
#define _syscall(...) ({ \
long _ret = -ERESTART; \
while (_ret == -ERESTART) \
long _ret; \
do { \
_ret = _kas_syscall(__VA_ARGS__); \
if (_ret < 0) { \
} while (__builtin_expect(_ret == -ERESTART, 0)); \
if (__builtin_expect(_ret < 0, 0)) { \
errno = -_ret; \
_ret = -1; \
} \
_ret; \
})
#define syscall _syscall
#define syscall(...) _syscall(__VA_ARGS__)
#endif
extern char** environ;

View File

@@ -18,6 +18,7 @@
struct pthread_trampoline_info_t
{
struct uthread* uthread;
bool detached;
void* (*start_routine)(void*);
void* arg;
};
@@ -71,6 +72,8 @@ extern "C" void _pthread_trampoline_cpp(void* arg)
#endif
free(arg);
signal(SIGCANCEL, &_pthread_cancel_handler);
if (info.detached)
pthread_detach(info.uthread->id);
pthread_exit(info.start_routine(info.arg));
ASSERT_NOT_REACHED();
}
@@ -137,12 +140,6 @@ void pthread_cleanup_push(void (*routine)(void*), void* arg)
uthread->cleanup_stack = cleanup;
}
static thread_local struct
{
void* value;
pthread_key_t key;
} s_pthread_key_values[PTHREAD_KEYS_MAX] {};
static pthread_key_t s_pthread_key_current = 1;
static pthread_key_t s_pthread_key_map[PTHREAD_KEYS_MAX] {};
static void (*s_pthread_key_destructors[PTHREAD_KEYS_MAX])(void*) {};
@@ -190,17 +187,19 @@ void* pthread_getspecific(pthread_key_t key)
{
void* ret = nullptr;
auto* uthread = _get_uthread();
pthread_spin_lock(&s_pthread_key_lock);
for (size_t i = 0; i < PTHREAD_KEYS_MAX; i++)
{
if (s_pthread_key_map[i] != key)
continue;
if (s_pthread_key_values[i].key != key)
if (uthread->specific_keys[i] != key)
{
s_pthread_key_values[i].key = key;
s_pthread_key_values[i].value = nullptr;
uthread->specific_keys[i] = key;
uthread->specific_values[i] = nullptr;
}
ret = s_pthread_key_values[i].value;
ret = uthread->specific_values[i];
break;
}
pthread_spin_unlock(&s_pthread_key_lock);
@@ -212,14 +211,16 @@ int pthread_setspecific(pthread_key_t key, const void* value)
{
int ret = EINVAL;
auto* uthread = _get_uthread();
pthread_spin_lock(&s_pthread_key_lock);
for (size_t i = 0; i < PTHREAD_KEYS_MAX; i++)
{
if (s_pthread_key_map[i] != key)
continue;
if (s_pthread_key_values[i].key != key)
s_pthread_key_values[i].key = key;
s_pthread_key_values[i].value = const_cast<void*>(value);
if (uthread->specific_keys[i] != key)
uthread->specific_keys[i] = key;
uthread->specific_values[i] = const_cast<void*>(value);
ret = 0;
break;
}
@@ -259,8 +260,6 @@ int pthread_attr_setdetachstate(pthread_attr_t* attr, int detachstate)
switch (detachstate)
{
case PTHREAD_CREATE_DETACHED:
dwarnln("TODO: pthread_attr_setdetachstate");
return ENOTSUP;
case PTHREAD_CREATE_JOINABLE:
attr->detachstate = detachstate;
return 0;
@@ -388,6 +387,7 @@ int pthread_create(pthread_t* __restrict thread_id, const pthread_attr_t* __rest
*info = {
.uthread = nullptr,
.detached = attr ? (attr->detachstate == PTHREAD_CREATE_DETACHED) : false,
.start_routine = start_routine,
.arg = arg,
};
@@ -419,6 +419,8 @@ int pthread_create(pthread_t* __restrict thread_id, const pthread_attr_t* __rest
.cancel_type = PTHREAD_CANCEL_DEFERRED,
.cancel_state = PTHREAD_CANCEL_ENABLE,
.canceled = 0,
.specific_keys = {},
.specific_values = {},
.dtv = { self->dtv[0] }
};
@@ -448,9 +450,7 @@ pthread_create_error:
int pthread_detach(pthread_t thread)
{
(void)thread;
dwarnln("TODO: pthread_detach");
return ENOTSUP;
return syscall(SYS_PTHREAD_DETACH, thread);
}
void pthread_exit(void* value_ptr)
@@ -468,10 +468,11 @@ void pthread_exit(void* value_ptr)
void* value = nullptr;
pthread_spin_lock(&s_pthread_key_lock);
if (s_pthread_key_map[i] && s_pthread_key_values[i].key == s_pthread_key_map[i])
if (s_pthread_key_map[i] && uthread->specific_keys[i] == s_pthread_key_map[i])
{
destructor = s_pthread_key_destructors[i];
value = s_pthread_key_values[i].value;
value = uthread->specific_values[i];
uthread->specific_values[i] = nullptr;
}
pthread_spin_unlock(&s_pthread_key_lock);

View File

@@ -52,28 +52,27 @@ mode_t umask(mode_t cmask)
int mkdir(const char* path, mode_t mode)
{
return syscall(SYS_CREATE_DIR, path, __UMASKED_MODE(mode));
return mkdirat(AT_FDCWD, path, mode);
}
int mkdirat(int fd, const char* path, mode_t mode)
{
return syscall(SYS_MKDIRAT, fd, path, __UMASKED_MODE(mode));
}
int mkfifo(const char* path, mode_t mode)
{
(void)path; (void)mode;
dwarnln("TODO: mkfifo");
return -1;
return mkfifoat(AT_FDCWD, path, mode);
}
int mkfifoat(int fd, const char* path, mode_t mode)
{
(void)fd; (void)path; (void)mode;
dwarnln("TODO: mkfifoat");
return -1;
return syscall(SYS_MKFIFOAT, fd, path, __UMASKED_MODE(mode));
}
int mknod(const char* path, mode_t mode, dev_t dev)
{
(void)path; (void)mode; (void)dev;
dwarnln("TODO: mknod");
return -1;
return mknodat(AT_FDCWD, path, mode, dev);
}
int mknodat(int fd, const char* path, mode_t mode, dev_t dev)

View File

@@ -112,6 +112,8 @@ extern "C" void _init_libc(char** environ, init_funcs_t init_funcs, init_funcs_t
.cancel_type = PTHREAD_CANCEL_DEFERRED,
.cancel_state = PTHREAD_CANCEL_ENABLE,
.canceled = false,
.specific_keys = {},
.specific_values = {},
.dtv = { 0 },
};
@@ -270,7 +272,7 @@ long syscall(long syscall, ...)
return _syscall(syscall, arg1, arg2, arg3, arg4, arg5);
}
#define syscall _syscall
#define syscall(...) _syscall(__VA_ARGS__)
int close(int fd)
{

View File

@@ -47,6 +47,7 @@ set(USERSPACE_PROGRAMS
sync
tee
test
tr
true
TaskBar
Terminal

View File

@@ -1398,6 +1398,8 @@ static void initialize_tls(MasterTLS master_tls)
.cancel_type = PTHREAD_CANCEL_DEFERRED,
.cancel_state = PTHREAD_CANCEL_ENABLE,
.canceled = false,
.specific_keys = {},
.specific_values = {},
.dtv = {},
};

View File

@@ -117,6 +117,7 @@ int main(int argc, char** argv)
{ "pin", required_argument, nullptr, 'p' },
{ "volume", required_argument, nullptr, 'v' },
{ "help", no_argument, nullptr, 'h' },
{}
};
int ch = getopt_long(argc, argv, "ld:p:v:h", long_options, nullptr);
@@ -125,16 +126,6 @@ int main(int argc, char** argv)
switch (ch)
{
case 'h':
fprintf(stderr, "usage: %s [OPTIONS]...\n", argv[0]);
fprintf(stderr, " control the audio server\n");
fprintf(stderr, "OPTIONS:\n");
fprintf(stderr, " -l, --list list devices and their pins\n");
fprintf(stderr, " -d, --device N set device index N as the current one\n");
fprintf(stderr, " -p, --pin N set pin N as the current one\n");
fprintf(stderr, " -v, --volume N set volume to N%%. if + or - is given, volume is relative to the current volume\n");
fprintf(stderr, " -h, --help show this message and exit\n");
return 0;
case 'l':
list = true;
break;
@@ -149,8 +140,17 @@ int main(int argc, char** argv)
volume_rel = (optarg[0] == '-');
volume = parse_u32_or_exit(optarg + volume_rel.has_value());
break;
case '?':
fprintf(stderr, "invalid option %c\n", optopt);
case 'h':
fprintf(stderr, "usage: %s [OPTIONS]...\n", argv[0]);
fprintf(stderr, " control the audio server\n");
fprintf(stderr, "OPTIONS:\n");
fprintf(stderr, " -l, --list list devices and their pins\n");
fprintf(stderr, " -d, --device N set device index N as the current one\n");
fprintf(stderr, " -p, --pin N set pin N as the current one\n");
fprintf(stderr, " -v, --volume N set volume to N%%. if + or - is given, volume is relative to the current volume\n");
fprintf(stderr, " -h, --help show this message and exit\n");
return 0;
case ':': case '?':
fprintf(stderr, "see '%s --help' for usage\n", argv[0]);
return 1;
}

View File

@@ -165,6 +165,7 @@ int main(int argc, char** argv)
static option long_options[] {
{ "help", no_argument, nullptr, 'h' },
{ "recursive", no_argument, nullptr, 'r' },
{}
};
int ch = getopt_long(argc, argv, "hr", long_options, nullptr);
@@ -173,17 +174,16 @@ int main(int argc, char** argv)
switch (ch)
{
case 'r':
recursive = true;
break;
case 'h':
printf("usage: %s [OPTIONS]... SOURCE... DEST\n", argv[0]);
printf("Copies files SOURCE... to DEST\n");
printf("OPTIONS:\n");
printf(" -h, --help Show this message and exit\n");
return 0;
case 'r':
recursive = true;
break;
case '?':
fprintf(stderr, "invalid option %c\n", optopt);
case ':': case '?':
fprintf(stderr, "see '%s --help' for usage\n", argv[0]);
return 1;
}

View File

@@ -11,6 +11,7 @@ int main(int argc, char* argv[])
static option long_options[] {
{ "zero", no_argument, nullptr, 'z' },
{ "help", no_argument, nullptr, 'h' },
{}
};
int ch = getopt_long(argc, argv, "zh", long_options, nullptr);
@@ -23,17 +24,13 @@ int main(int argc, char* argv[])
zero = true;
break;
case 'h':
fprintf(stderr, "usage: %s [OPTIONS]...\n", argv[0]);
fprintf(stderr, " control the audio server\n");
fprintf(stderr, "usage: %s [OPTION] NAME...\n", argv[0]);
fprintf(stderr, " output the directory containing each NAME\n");
fprintf(stderr, "OPTIONS:\n");
fprintf(stderr, " -l, --list list devices and their pins\n");
fprintf(stderr, " -d, --device N set device index N as the current one\n");
fprintf(stderr, " -p, --pin N set pin N as the current one\n");
fprintf(stderr, " -v, --volume N set volume to N%%. if + or - is given, volume is relative to the current volume\n");
fprintf(stderr, " -h, --help show this message and exit\n");
fprintf(stderr, " -z, --zero end each output with NUL instead of a newline\n");
fprintf(stderr, " -h, --help show this message and exit\n");
return 0;
case '?':
fprintf(stderr, "invalid option %c\n", optopt);
case ':': case '?':
fprintf(stderr, "see '%s --help' for usage\n", argv[0]);
return 1;
}
@@ -41,7 +38,7 @@ int main(int argc, char* argv[])
if (optind >= argc)
{
fprintf(stderr, "missing operand\n");
fprintf(stderr, "%s: missing operand\n", argv[0]);
fprintf(stderr, "see '%s --help' for usage\n", argv[0]);
return 1;
}

View File

@@ -5,17 +5,24 @@
#include <dirent.h>
#include <fcntl.h>
#include <getopt.h>
#include <grp.h>
#include <pwd.h>
#include <stdio.h>
#include <sys/stat.h>
#include <termios.h>
struct config_t
struct Config
{
bool list = false;
bool all = false;
bool directory = false;
enum class Visibility {
Normal,
AlmostAll,
All
};
Visibility visibility { Visibility::Normal };
bool show_as_list { false };
bool human_readable { false };
bool directory { false };
};
struct simple_entry_t
@@ -58,7 +65,7 @@ const char* entry_color(mode_t mode)
return "\e[0m";
}
BAN::String build_access_string(mode_t mode)
BAN::String build_access_string(mode_t mode, const Config&)
{
BAN::String access;
MUST(access.resize(10));
@@ -75,12 +82,12 @@ BAN::String build_access_string(mode_t mode)
return access;
}
BAN::String build_hard_links_string(nlink_t links)
BAN::String build_hard_links_string(nlink_t links, const Config&)
{
return MUST(BAN::String::formatted("{}", links));
}
BAN::String build_owner_name_string(uid_t uid)
BAN::String build_owner_name_string(uid_t uid, const Config&)
{
struct passwd* passwd = getpwuid(uid);
if (passwd == nullptr)
@@ -88,7 +95,7 @@ BAN::String build_owner_name_string(uid_t uid)
return BAN::String(BAN::StringView(passwd->pw_name));
}
BAN::String build_owner_group_string(gid_t gid)
BAN::String build_owner_group_string(gid_t gid, const Config&)
{
struct group* grp = getgrgid(gid);
if (grp == nullptr)
@@ -96,23 +103,35 @@ BAN::String build_owner_group_string(gid_t gid)
return BAN::String(BAN::StringView(grp->gr_name));
}
BAN::String build_size_string(off_t size)
BAN::String build_size_string(off_t size, const Config& config)
{
return MUST(BAN::String::formatted("{}", size));
if (!config.human_readable || size < 1024)
return MUST(BAN::String::formatted("{}", size));
size = size / 1024 * 10;
size_t suffix_idx = 0;
for (; size >= 10240; size /= 1024)
suffix_idx++;
constexpr char suffix[] { 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y', 'R', 'Q' };
if (size >= 100)
return MUST(BAN::String::formatted("{}{}", size / 10, suffix[suffix_idx]));
return MUST(BAN::String::formatted("{}.{}{}", size / 10, size % 10, suffix[suffix_idx]));
}
BAN::String build_month_string(BAN::Time time)
BAN::String build_month_string(BAN::Time time, const Config&)
{
static const char* months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
return BAN::String(BAN::StringView(months[(time.month - 1) % 12]));
}
BAN::String build_day_string(BAN::Time time)
BAN::String build_day_string(BAN::Time time, const Config&)
{
return MUST(BAN::String::formatted("{}", time.day));
}
BAN::String build_time_string(BAN::Time time)
BAN::String build_time_string(BAN::Time time, const Config&)
{
static uint32_t current_year = ({ timespec real_time; clock_gettime(CLOCK_REALTIME, &real_time); BAN::from_unix_time(real_time.tv_sec).year; });
if (time.year != current_year)
@@ -151,7 +170,7 @@ BAN::Vector<size_t> resolve_layout(const BAN::Vector<simple_entry_t>& entries)
return {};
}
int list_directory(const BAN::String& path, config_t config)
int list_directory(const BAN::String& path, Config config)
{
static char link_buffer[PATH_MAX];
@@ -166,9 +185,14 @@ int list_directory(const BAN::String& path, config_t config)
return 2;
}
const bool is_directory = S_ISDIR(st.st_mode);
const size_t block_size = st.st_blksize;
size_t blocks_used = 0;
int ret = 0;
if (!S_ISDIR(st.st_mode))
if (!is_directory)
{
MUST(entries.emplace_back(path, st, BAN::String()));
if (S_ISLNK(st.st_mode))
@@ -191,8 +215,19 @@ int list_directory(const BAN::String& path, config_t config)
struct dirent* dirent;
while ((dirent = readdir(dirp)))
{
if (!config.all && dirent->d_name[0] == '.')
continue;
switch (config.visibility)
{
case Config::Visibility::Normal:
if (dirent->d_name[0] == '.')
continue;
break;
case Config::Visibility::AlmostAll:
if (strcmp(dirent->d_name, ".") == 0 || strcmp(dirent->d_name, "..") == 0)
continue;
break;
case Config::Visibility::All:
break;
}
if (fstatat(dirfd(dirp), dirent->d_name, &st, AT_SYMLINK_NOFOLLOW) == -1)
{
@@ -201,6 +236,8 @@ int list_directory(const BAN::String& path, config_t config)
continue;
}
blocks_used += st.st_blocks;
MUST(entries.emplace_back(BAN::StringView(dirent->d_name), st, BAN::String()));
if (S_ISLNK(st.st_mode))
{
@@ -218,8 +255,8 @@ int list_directory(const BAN::String& path, config_t config)
[](const simple_entry_t& lhs, const simple_entry_t& rhs)
{
// sort directories first
bool lhs_isdir = S_ISDIR(lhs.st.st_mode);
bool rhs_isdir = S_ISDIR(rhs.st.st_mode);
const bool lhs_isdir = S_ISDIR(lhs.st.st_mode);
const bool rhs_isdir = S_ISDIR(rhs.st.st_mode);
if (lhs_isdir != rhs_isdir)
return lhs_isdir;
@@ -231,7 +268,7 @@ int list_directory(const BAN::String& path, config_t config)
}
);
if (!config.list)
if (!config.show_as_list)
{
if (!g_stdout_terminal)
{
@@ -297,9 +334,9 @@ int list_directory(const BAN::String& path, config_t config)
{
full_entry_t full_entry;
#define GET_ENTRY_STRING(property, input) \
full_entry.property = build_ ## property ## _string(input); \
if (full_entry.property.size() > max_entry.property.size()) \
#define GET_ENTRY_STRING(property, input) \
full_entry.property = build_ ## property ## _string(input, config); \
if (full_entry.property.size() > max_entry.property.size()) \
max_entry.property = full_entry.property;
GET_ENTRY_STRING(access, entry.st.st_mode);
@@ -323,6 +360,14 @@ int list_directory(const BAN::String& path, config_t config)
MUST(full_entries.push_back(BAN::move(full_entry)));
}
if (is_directory)
{
if (config.human_readable)
printf("total: %s\n", build_size_string(blocks_used * block_size, config).data());
else
printf("total: %zu\n", blocks_used);
}
for (const auto& full_entry : full_entries)
printf("%*s %*s %*s %*s %*s %*s %*s %*s %s\n",
(int)max_entry.access.size(), full_entry.access.data(),
@@ -350,60 +395,65 @@ int usage(const char* argv0, int ret)
return ret;
}
int main(int argc, const char* argv[])
int main(int argc, char* argv[])
{
config_t config;
Config config;
int i = 1;
for (; i < argc; i++)
for (;;)
{
if (argv[i][0] != '-')
break;
if (argv[i][1] == '\0')
static option long_options[] {
{ "all", no_argument, nullptr, 'a' },
{ "almost-all", no_argument, nullptr, 'A' },
{ "directory" , no_argument, nullptr, 'd' },
{ "human-readable", no_argument, nullptr, 'h' },
{ "list", no_argument, nullptr, 'l' },
{ "help", no_argument, nullptr, 0 },
{}
};
int ch = getopt_long(argc, argv, "aAlh", long_options, nullptr);
if (ch == -1)
break;
if (argv[i][1] == '-')
switch (ch)
{
if (strcmp(argv[i], "--help") == 0)
return usage(argv[0], 0);
else if (strcmp(argv[i], "--all") == 0)
config.all = true;
else if (strcmp(argv[i], "--list") == 0)
config.list = true;
else if (strcmp(argv[i], "--directory") == 0)
case 'a':
config.visibility = Config::Visibility::All;
break;
case 'A':
config.visibility = Config::Visibility::AlmostAll;
break;
case 'd':
config.directory = true;
else
{
fprintf(stderr, "unrecognized option '%s'\n", argv[i]);
return usage(argv[0], 2);
}
}
else
{
for (size_t j = 1; argv[i][j]; j++)
{
if (argv[i][j] == 'h')
return usage(argv[0], 0);
else if (argv[i][j] == 'a')
config.all = true;
else if (argv[i][j] == 'l')
config.list = true;
else if (argv[i][j] == 'd')
config.directory = true;
else
{
fprintf(stderr, "unrecognized option '%c'\n", argv[i][j]);
return usage(argv[0], 2);
}
}
break;
case 'h':
config.human_readable = true;
break;
case 'l':
config.show_as_list = true;
break;
case 0:
fprintf(stderr, "usage: %s [OPTION]... [FILE]...\n", argv[0]);
fprintf(stderr, " list information about FILEs\n");
fprintf(stderr, "OPTIONS:\n");
fprintf(stderr, " -a, --all do not ignore entries starting with .\n");
fprintf(stderr, " -A, --almost-all do not list . and ..\n");
fprintf(stderr, " -d, --directory list directories and not their contents\n");
fprintf(stderr, " -h, --human-readable print sizes in human readable form\n");
fprintf(stderr, " -l, --list use long listing format\n");
fprintf(stderr, " --help show this message and exit\n");
return 0;
case ':': case '?':
fprintf(stderr, "see '%s --help' for usage\n", argv[0]);
return 1;
}
}
BAN::Vector<BAN::String> files;
if (i == argc)
if (optind == argc)
MUST(files.emplace_back("."_sv));
else for (; i < argc; i++)
else for (int i = optind; i < argc; i++)
MUST(files.emplace_back(BAN::StringView(argv[i])));
g_stdout_terminal = isatty(STDOUT_FILENO) && tcgetwinsize(STDOUT_FILENO, &g_terminal_size) == 0;

View File

@@ -197,6 +197,7 @@ int main(int argc, char** argv)
{
static option long_options[] {
{ "help", no_argument, nullptr, 'h' },
{}
};
int ch = getopt_long(argc, argv, "h", long_options, nullptr);
@@ -211,8 +212,7 @@ int main(int argc, char** argv)
printf("OPTIONS:\n");
printf(" -h, --help Show this message and exit\n");
return 0;
case '?':
fprintf(stderr, "invalid option %c\n", optopt);
case ':': case '?':
fprintf(stderr, "see '%s --help' for usage\n", argv[0]);
return 1;
}

View File

@@ -102,6 +102,7 @@ int main(int argc, char** argv)
{ "interactive", no_argument, nullptr, 'i' },
{ "force", no_argument, nullptr, 'f' },
{ "help", no_argument, nullptr, 'h' },
{}
};
int ch = getopt_long(argc, argv, "rRifh", long_options, nullptr);
@@ -110,15 +111,6 @@ int main(int argc, char** argv)
switch (ch)
{
case 'h':
fprintf(stderr, "usage: %s [OPTIONS]... FILE...\n", argv[0]);
fprintf(stderr, " remove each FILE\n");
fprintf(stderr, "OPTIONS:\n");
fprintf(stderr, " -r, -R, --recursive remove directories and their contents recursively\n");
fprintf(stderr, " -i, --interactive prompt removal for all files\n");
fprintf(stderr, " -f, --force ignore nonexistent files and never prompt\n");
fprintf(stderr, " -h, --help show this message and exit\n");
return 0;
case 'r': case 'R':
recursive = true;
break;
@@ -130,8 +122,16 @@ int main(int argc, char** argv)
force = false;
interactive = true;
break;
case '?':
fprintf(stderr, "invalid option %c\n", optopt);
case 'h':
fprintf(stderr, "usage: %s [OPTIONS]... FILE...\n", argv[0]);
fprintf(stderr, " remove each FILE\n");
fprintf(stderr, "OPTIONS:\n");
fprintf(stderr, " -r, -R, --recursive remove directories and their contents recursively\n");
fprintf(stderr, " -i, --interactive prompt removal for all files\n");
fprintf(stderr, " -f, --force ignore nonexistent files and never prompt\n");
fprintf(stderr, " -h, --help show this message and exit\n");
return 0;
case ':': case '?':
fprintf(stderr, "see '%s --help' for usage\n", argv[0]);
return 1;
}
@@ -139,7 +139,8 @@ int main(int argc, char** argv)
if (optind >= argc && !force)
{
fprintf(stderr, "missing operand. use --help for more information\n");
fprintf(stderr, "%s: missing operand\n", argv[0]);
fprintf(stderr, "see '%s --help' for usage\n", argv[0]);
return 1;
}

View File

@@ -0,0 +1,287 @@
#include <BAN/String.h>
#include <assert.h>
#include <ctype.h>
#include <getopt.h>
#include <stdio.h>
static const char* s_argv0 { nullptr };
static char parse_char(const char*& array)
{
assert(array[0]);
if (array[0] != '\\')
return *array++;
switch (array[1])
{
case '\\': array += 2; return '\\';
case 'a': array += 2; return '\a';
case 'b': array += 2; return '\b';
case 'f': array += 2; return '\f';
case 'n': array += 2; return '\n';
case 'r': array += 2; return '\r';
case 't': array += 2; return '\t';
case 'v': array += 2; return '\v';
}
size_t octal_count = 0;
while (octal_count < 3 && '0' <= array[1 + octal_count] && array[1 + octal_count] <= '7')
octal_count++;
if (octal_count == 0)
return *array++;
int value = 0;
for (size_t i = 1; i <= octal_count; i++)
value = (value * 8) + (array[i] - '0');
array += 1 + octal_count;
return value;
}
static BAN::String expand_array(const char* array, size_t expand_len)
{
BAN::String result;
while (*array)
{
if (array[0] == '[')
{
const char* end = strchr(array + 1, ']');
if (end == nullptr)
goto normal_character;
const size_t len = end - array - 1;
if (len < 2)
goto normal_character;
if (array[1] == ':' && end[-1] == ':')
{
int (*class_test)(int) = nullptr;
#define CHECK_CHAR_CLASS(name) \
else if (len == sizeof(#name) + 1 && memcmp(array + 2, #name, len - 2) == 0) \
class_test = is##name
if (false);
CHECK_CHAR_CLASS(alnum);
CHECK_CHAR_CLASS(alpha);
CHECK_CHAR_CLASS(blank);
CHECK_CHAR_CLASS(cntrl);
CHECK_CHAR_CLASS(digit);
CHECK_CHAR_CLASS(graph);
CHECK_CHAR_CLASS(lower);
CHECK_CHAR_CLASS(print);
CHECK_CHAR_CLASS(punct);
CHECK_CHAR_CLASS(space);
CHECK_CHAR_CLASS(upper);
CHECK_CHAR_CLASS(xdigit);
#undef CHECK_CHAR_CLASS
if (class_test == nullptr)
{
fprintf(stderr, "%s: invalid character class '%.*s'\n", s_argv0, (int)len - 2, array + 2);
exit(1);
}
for (int ch = 0; ch < 0x100; ch++)
if (class_test(ch))
MUST(result.push_back(ch));
}
else if (array[1] == '=' && end[-1] == '=')
{
if (len - 2 != 1)
{
fprintf(stderr, "%s: %.*s: equivalence class must be a single character\n", s_argv0, (int)len - 2, array + 2);
exit(1);
}
// TODO: actually support collating elements
MUST(result.push_back(array[2]));
}
else
{
const char* temp = array + 1;
const char ch = parse_char(temp);
if (temp[0] != '*')
goto normal_character;
temp++;
const int base = (temp[0] == '0') ? 8 : 10;
bool valid_count = true;
for (size_t i = 0; valid_count && temp[i] != ']'; i++)
valid_count = ('0' <= temp[i] && temp[i] <= '0' + base - 1);
if (!valid_count)
{
fprintf(stderr, "%s: invalid repeat count '%.*s'\n", s_argv0, (int)len - 2, array + 2);
exit(1);
}
size_t count = 0;
for (size_t i = 0; temp[i] != ']'; i++)
count = (count * base) + (temp[i] - '0');
if (count == 0 && result.size() < expand_len)
count = expand_len - result.size();
for (size_t i = 0; i < count; i++)
MUST(result.push_back(ch));
}
array = end + 1;
continue;
}
normal_character:
const char ch1 = parse_char(array);
if (array[0] == '-' && array[1])
{
array++;
const char ch2 = parse_char(array);
for (int ch = ch1; ch <= ch2; ch++)
MUST(result.push_back(ch));
continue;
}
MUST(result.push_back(ch1));
}
return result;
}
int main(int argc, char* argv[])
{
s_argv0 = argv[0];
bool complement { false };
bool delete_ { false };
bool squeeze { false };
bool truncate { false };
for (;;)
{
static option long_options[] {
{ "complement", no_argument, nullptr, 'c' },
{ "delete", no_argument, nullptr, 'd' },
{ "squeeze-repeats", no_argument, nullptr, 's' },
{ "truncate-set1", no_argument, nullptr, 't' },
{ "help", no_argument, nullptr, 0 },
{}
};
int ch = getopt_long(argc, argv, "cCdst", long_options, nullptr);
if (ch == -1)
break;
switch (ch)
{
case 'c': case 'C':
complement = true;
break;
case 'd':
delete_ = true;
break;
case 's':
squeeze = true;
break;
case 't':
truncate = true;
break;
case 0:
fprintf(stderr, "usage: %s [OPTION]... STRING1 [STRING2]\n", argv[0]);
fprintf(stderr, " translate and/or delete characters from standard input\n");
fprintf(stderr, "OPTIONS:\n");
fprintf(stderr, " -c, -C, --complement do not ignore entries starting with .\n");
fprintf(stderr, " -d, --delete do not list . and ..\n");
fprintf(stderr, " -s, --squeeze-repeats list directories and not their contents\n");
fprintf(stderr, " -t, --truncate-set1 print sizes in human readable form\n");
fprintf(stderr, " --help show this message and exit\n");
return 0;
case ':' : case '?':
fprintf(stderr, "see '%s --help' for usage\n", argv[0]);
return 1;
}
}
const int needed_args = (delete_ == squeeze) ? 2 : 1;
if (optind + needed_args > argc)
{
fprintf(stderr, "%s: missing operand\n", argv[0]);
fprintf(stderr, "see '%s --help' for usage\n", argv[0]);
return 1;
}
if (optind + 2 < argc)
{
fprintf(stderr, "%s: extra operand '%s'\n", argv[0], argv[optind + 2]);
fprintf(stderr, "see '%s --help' for usage\n", argv[0]);
return 1;
}
BAN::String array1 = expand_array(argv[optind], 0);
if (complement)
{
bool contains[0x100] {};
for (int ch : array1)
contains[ch] = true;
array1.clear();
for (int ch = 0; ch < 0x100; ch++)
if (!contains[ch])
MUST(array1.push_back(ch));
}
BAN::Optional<BAN::String> array2;
if (optind + 1 < argc)
{
array2 = expand_array(argv[optind + 1], array1.size());;
if (truncate && array1.size() > array2->size())
MUST(array1.resize(array2->size()));
if (!array1.empty() && array2->empty())
{
fprintf(stderr, "%s: STRING2 must not be empty\n", argv[0]);
return 1;
}
while (array2->size() < array1.size())
MUST(array2->push_back(array2->back()));
}
char translate_map[0x100] {};
for (int ch = 0; ch < 0x100; ch++)
translate_map[ch] = ch;
if (!delete_ && !squeeze)
for (size_t i = 0; i < array1.size(); i++)
translate_map[static_cast<int>(array1[i])] = array2.value()[i];
bool delete_set[0x100] {};
if (delete_)
{
for (int ch : array1)
delete_set[ch] = true;
}
bool squeeze_set[0x100] {};
if (squeeze)
{
const auto& array = array2.has_value() ? array2.value() : array1;
for (int ch : array)
squeeze_set[ch] = true;
}
int prev_char = -1;
for (;;)
{
int ch = getchar();
if (ch == EOF)
break;
ch = translate_map[ch];
if (delete_set[ch])
continue;
if (squeeze_set[ch] && prev_char == ch)
continue;
prev_char = ch;
putchar(ch);
}
return 0;
}

View File

@@ -20,6 +20,7 @@ int main(int argc, char** argv)
{ "kernel-name", no_argument, nullptr, 's' },
{ "kernel-version", no_argument, nullptr, 'v' },
{ "help", no_argument, nullptr, 0 },
{}
};
int ch = getopt_long(argc, argv, "amnrsv", long_options, nullptr);
@@ -28,15 +29,6 @@ int main(int argc, char** argv)
switch (ch)
{
case 0:
printf("usage: %s [OPTION]...\n", argv[0]);
printf(" -a, --all print all information\n");
printf(" -m, --machine print machine name\n");
printf(" -r, --release print release\n");
printf(" -n, --nodename print node name\n");
printf(" -s, --system print system name\n");
printf(" -v, --version print version\n");
return 0;
case 'a':
machine = true;
nodename = true;
@@ -59,8 +51,17 @@ int main(int argc, char** argv)
case 'v':
version = true;
break;
case '?':
fprintf(stderr, "invalid option %c\n", optopt);
case 0:
printf("usage: %s [OPTION]...\n", argv[0]);
printf(" -a, --all print all information\n");
printf(" -m, --machine print machine name\n");
printf(" -r, --release print release\n");
printf(" -n, --nodename print node name\n");
printf(" -s, --system print system name\n");
printf(" -v, --version print version\n");
printf(" --help show this message and exit\n");
return 0;
case ':': case '?':
fprintf(stderr, "see '%s --help' for usage\n", argv[0]);
return 1;
}