diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index d82a477aa..b76892c42 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -20,6 +20,7 @@ set(KERNEL_SOURCES kernel/Font.cpp kernel/FS/Ext2.cpp kernel/FS/Inode.cpp + kernel/FS/Pipe.cpp kernel/FS/VirtualFileSystem.cpp kernel/Input/PS2Controller.cpp kernel/Input/PS2Keyboard.cpp diff --git a/kernel/include/kernel/FS/Inode.h b/kernel/include/kernel/FS/Inode.h index 376d1b01d..e31b23e0c 100644 --- a/kernel/include/kernel/FS/Inode.h +++ b/kernel/include/kernel/FS/Inode.h @@ -76,6 +76,7 @@ namespace Kernel virtual dev_t rdev() const = 0; virtual bool is_device() const { return false; } + virtual bool is_pipe() const { return false; } virtual BAN::StringView name() const = 0; diff --git a/kernel/include/kernel/FS/Pipe.h b/kernel/include/kernel/FS/Pipe.h new file mode 100644 index 000000000..6dfa880da --- /dev/null +++ b/kernel/include/kernel/FS/Pipe.h @@ -0,0 +1,54 @@ +#pragma once + +#include +#include +#include + +namespace Kernel +{ + + class Pipe : public Inode + { + public: + static BAN::ErrorOr> create(const Credentials&); + + virtual bool is_pipe() const override { return true; } + void clone_writing(); + void close_writing(); + + virtual ino_t ino() const override { return 0; } // FIXME + virtual Mode mode() const override { return { Mode::IFIFO | Mode::IRUSR | Mode::IWUSR }; } + virtual nlink_t nlink() const override { return 1; } + virtual uid_t uid() const override { return m_uid; } + virtual gid_t gid() const override { return m_gid; } + virtual off_t size() const override { return 0; } + virtual timespec atime() const override { return m_atime; } + virtual timespec mtime() const override { return m_mtime; } + virtual timespec ctime() const override { return m_ctime; } + virtual blksize_t blksize() const override { return 4096; } + virtual blkcnt_t blocks() const override { return 0; } + virtual dev_t dev() const override { return 0; } // FIXME + virtual dev_t rdev() const override { return 0; } // FIXME + + virtual BAN::StringView name() const override { return ""sv; } + + virtual BAN::ErrorOr read(size_t, void*, size_t) override; + virtual BAN::ErrorOr write(size_t, const void*, size_t) override; + + private: + Pipe(const Credentials&); + + private: + const uid_t m_uid; + const gid_t m_gid; + timespec m_atime {}; + timespec m_mtime {}; + timespec m_ctime {}; + BAN::Vector m_buffer; + SpinLock m_lock; + Semaphore m_semaphore; + + uint32_t m_writing_count { 1 }; + }; + +} \ No newline at end of file diff --git a/kernel/include/kernel/Process.h b/kernel/include/kernel/Process.h index 6a5220a9a..99e1b66d7 100644 --- a/kernel/include/kernel/Process.h +++ b/kernel/include/kernel/Process.h @@ -84,6 +84,8 @@ namespace Kernel BAN::ErrorOr sys_write(int fd, const void* buffer, size_t count); BAN::ErrorOr sys_creat(BAN::StringView name, mode_t); + BAN::ErrorOr sys_pipe(int fildes[2]); + BAN::ErrorOr sys_seek(int fd, off_t offset, int whence); BAN::ErrorOr sys_tell(int fd); @@ -136,6 +138,7 @@ namespace Kernel BAN::ErrorOr validate_fd(int); OpenFileDescription& open_file_description(int); BAN::ErrorOr get_free_fd(); + BAN::ErrorOr get_free_fd_pair(int fds[2]); struct ExitStatus diff --git a/kernel/kernel/FS/Pipe.cpp b/kernel/kernel/FS/Pipe.cpp new file mode 100644 index 000000000..c0159d01c --- /dev/null +++ b/kernel/kernel/FS/Pipe.cpp @@ -0,0 +1,88 @@ +#include +#include +#include + +namespace Kernel +{ + + BAN::ErrorOr> Pipe::create(const Credentials& credentials) + { + Pipe* pipe = new Pipe(credentials); + if (pipe == nullptr) + return BAN::Error::from_errno(ENOMEM); + return BAN::RefPtr::adopt(pipe); + } + + Pipe::Pipe(const Credentials& credentials) + : m_uid(credentials.euid()) + , m_gid(credentials.egid()) + { + uint64_t current_time = BAN::to_unix_time(RTC::get_current_time()); + m_atime = { .tv_sec = current_time, .tv_nsec = 0 }; + m_mtime = { .tv_sec = current_time, .tv_nsec = 0 }; + m_ctime = { .tv_sec = current_time, .tv_nsec = 0 }; + } + + void Pipe::clone_writing() + { + LockGuard _(m_lock); + ASSERT(m_writing_count > 0); + m_writing_count++; + } + + void Pipe::close_writing() + { + LockGuard _(m_lock); + ASSERT(m_writing_count > 0); + m_writing_count--; + if (m_writing_count == 0) + m_semaphore.unblock(); + } + + BAN::ErrorOr Pipe::read(size_t, void* buffer, size_t count) + { + m_lock.lock(); + while (m_buffer.empty()) + { + if (m_writing_count == 0) + return 0; + m_lock.unlock(); + m_semaphore.block(); + m_lock.lock(); + } + + size_t to_copy = BAN::Math::min(count, m_buffer.size()); + memcpy(buffer, m_buffer.data(), to_copy); + + memmove(m_buffer.data(), m_buffer.data() + to_copy, m_buffer.size() - to_copy); + MUST(m_buffer.resize(m_buffer.size() - to_copy)); + + uint64_t current_time = BAN::to_unix_time(RTC::get_current_time()); + m_atime = { .tv_sec = current_time, .tv_nsec = 0 }; + + m_semaphore.unblock(); + + m_lock.unlock(); + + return to_copy; + } + + BAN::ErrorOr Pipe::write(size_t, const void* buffer, size_t count) + { + LockGuard _(m_lock); + + size_t old_size = m_buffer.size(); + + TRY(m_buffer.resize(old_size + count)); + memcpy(m_buffer.data() + old_size, buffer, count); + + uint64_t current_time = BAN::to_unix_time(RTC::get_current_time()); + m_mtime = { .tv_sec = current_time, .tv_nsec = 0 }; + m_ctime = { .tv_sec = current_time, .tv_nsec = 0 }; + + m_semaphore.unblock(); + + return count; + } + +} \ No newline at end of file diff --git a/kernel/kernel/Process.cpp b/kernel/kernel/Process.cpp index e3b4064e0..0eede365d 100644 --- a/kernel/kernel/Process.cpp +++ b/kernel/kernel/Process.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -277,6 +278,14 @@ namespace Kernel forked->m_working_directory = m_working_directory; forked->m_open_files = m_open_files; + for (int fd = 0; fd < (int)m_open_files.size(); fd++) + { + if (validate_fd(fd).is_error()) + continue; + auto& openfd = open_file_description(fd); + if (openfd.flags & O_WRONLY && openfd.inode->is_pipe()) + ((Pipe*)openfd.inode.ptr())->clone_writing(); + } forked->m_userspace_info = m_userspace_info; @@ -325,7 +334,7 @@ namespace Kernel LockGuard lock_guard(m_lock); - for (int fd = 0; fd < m_open_files.size(); fd++) + for (int fd = 0; fd < (int)m_open_files.size(); fd++) { if (validate_fd(fd).is_error()) continue; @@ -538,6 +547,8 @@ namespace Kernel LockGuard _(m_lock); TRY(validate_fd(fd)); auto& open_file_description = this->open_file_description(fd); + if (open_file_description.flags & O_WRONLY && open_file_description.inode->is_pipe()) + ((Pipe*)open_file_description.inode.ptr())->close_writing(); open_file_description.inode = nullptr; return 0; } @@ -590,6 +601,29 @@ namespace Kernel return nwrite; } + BAN::ErrorOr Process::sys_pipe(int fildes[2]) + { + LockGuard _(m_lock); + + auto pipe = TRY(Pipe::create(m_credentials)); + + TRY(get_free_fd_pair(fildes)); + + auto& openfd_read = m_open_files[fildes[0]]; + openfd_read.inode = pipe; + openfd_read.flags = O_RDONLY; + openfd_read.offset = 0; + openfd_read.path.clear(); + + auto& openfd_write = m_open_files[fildes[1]]; + openfd_write.inode = pipe; + openfd_write.flags = O_WRONLY; + openfd_write.offset = 0; + openfd_write.path.clear(); + + return 0; + } + BAN::ErrorOr Process::sys_seek(int fd, off_t offset, int whence) { LockGuard _(m_lock); @@ -1089,4 +1123,27 @@ namespace Kernel return m_open_files.size() - 1; } + BAN::ErrorOr Process::get_free_fd_pair(int fds[2]) + { + ASSERT(m_lock.is_locked()); + int found = 0; + for (size_t fd = 0; fd < m_open_files.size(); fd++) + { + if (!m_open_files[fd].inode) + { + fds[found++] = fd; + if (found >= 2) + return {}; + } + } + + while (found < 2) + { + TRY(m_open_files.push_back({})); + fds[found++] = m_open_files.size() - 1; + } + + return {}; + } + } \ No newline at end of file diff --git a/kernel/kernel/Syscall.cpp b/kernel/kernel/Syscall.cpp index 9fe10b62e..bfedb0edf 100644 --- a/kernel/kernel/Syscall.cpp +++ b/kernel/kernel/Syscall.cpp @@ -132,6 +132,9 @@ namespace Kernel case SYS_CLOCK_GETTIME: ret = Process::current().sys_clock_gettime((clockid_t)arg1, (timespec*)arg2); break; + case SYS_PIPE: + ret = Process::current().sys_pipe((int*)arg1); + break; default: dwarnln("Unknown syscall {}", syscall); break; diff --git a/libc/include/sys/syscall.h b/libc/include/sys/syscall.h index cab0cf040..6b2545a63 100644 --- a/libc/include/sys/syscall.h +++ b/libc/include/sys/syscall.h @@ -39,6 +39,7 @@ __BEGIN_DECLS #define SYS_GET_PWD 32 #define SYS_SET_PWD 33 #define SYS_CLOCK_GETTIME 34 +#define SYS_PIPE 35 __END_DECLS diff --git a/libc/unistd.cpp b/libc/unistd.cpp index f2b4dfa03..ff7a4ff08 100644 --- a/libc/unistd.cpp +++ b/libc/unistd.cpp @@ -251,6 +251,12 @@ long syscall(long syscall, ...) ret = Kernel::syscall(SYS_CLOCK_GETTIME, clock_id, (uintptr_t)tp); break; } + case SYS_PIPE: + { + int* fildes = va_arg(args, int*); + ret = Kernel::syscall(SYS_PIPE, (uintptr_t)fildes); + break; + } default: puts("LibC: Unhandeled syscall"); ret = -ENOSYS; @@ -365,6 +371,11 @@ pid_t fork(void) return syscall(SYS_FORK); } +int pipe(int fildes[2]) +{ + return syscall(SYS_PIPE, fildes); +} + unsigned int sleep(unsigned int seconds) { return syscall(SYS_SLEEP, seconds);