From: Tulio A M Mendes Date: Tue, 10 Feb 2026 01:12:20 +0000 (-0300) Subject: posix: add fcntl(F_SETFL) O_NONBLOCK for pipes and tty/pty X-Git-Url: https://projects.tadryanom.me/sitemap.xml?a=commitdiff_plain;h=533780256806cabe297e366ef7fde1b9589416c1;p=AdrOS.git posix: add fcntl(F_SETFL) O_NONBLOCK for pipes and tty/pty --- diff --git a/include/errno.h b/include/errno.h index e19670c..603b20d 100644 --- a/include/errno.h +++ b/include/errno.h @@ -8,6 +8,7 @@ #define E2BIG 7 #define EBADF 9 #define ECHILD 10 +#define EAGAIN 11 #define EFAULT 14 #define EEXIST 17 #define ENOMEM 12 diff --git a/include/syscall.h b/include/syscall.h index a31dd0b..8026b6d 100644 --- a/include/syscall.h +++ b/include/syscall.h @@ -42,6 +42,8 @@ enum { SYSCALL_UNLINK = 29, SYSCALL_GETDENTS = 30, + + SYSCALL_FCNTL = 31, }; #endif diff --git a/src/kernel/syscall.c b/src/kernel/syscall.c index 61b14c7..fcdd248 100644 --- a/src/kernel/syscall.c +++ b/src/kernel/syscall.c @@ -21,6 +21,15 @@ #include +enum { + O_NONBLOCK = 0x800, +}; + +enum { + FCNTL_F_GETFL = 3, + FCNTL_F_SETFL = 4, +}; + #if defined(__i386__) static const uint32_t SIGFRAME_MAGIC = 0x53494746U; // 'SIGF' struct sigframe { @@ -784,7 +793,7 @@ static int syscall_open_impl(const char* user_path, uint32_t flags) { if (!f) return -ENOMEM; f->node = node; f->offset = 0; - f->flags = 0; + f->flags = flags; f->refcount = 1; int fd = fd_alloc(f); @@ -795,6 +804,23 @@ static int syscall_open_impl(const char* user_path, uint32_t flags) { return fd; } +static int syscall_fcntl_impl(int fd, int cmd, uint32_t arg) { + struct file* f = fd_get(fd); + if (!f) return -EBADF; + + if (cmd == FCNTL_F_GETFL) { + return (int)f->flags; + } + if (cmd == FCNTL_F_SETFL) { + // Minimal: allow toggling O_NONBLOCK only. + uint32_t keep = f->flags & ~O_NONBLOCK; + uint32_t set = arg & O_NONBLOCK; + f->flags = keep | set; + return 0; + } + return -EINVAL; +} + static int syscall_mkdir_impl(const char* user_path) { if (!user_path) return -EFAULT; @@ -886,6 +912,29 @@ static int syscall_read_impl(int fd, void* user_buf, uint32_t len) { struct file* f = fd_get(fd); if (!f || !f->node) return -EBADF; + int nonblock = (f->flags & O_NONBLOCK) ? 1 : 0; + if (nonblock) { + // Non-blocking pipes: if empty but writers exist, return -EAGAIN. + if (f->node->name[0] == 'p' && f->node->name[1] == 'i' && f->node->name[2] == 'p' && f->node->name[3] == 'e' && f->node->name[4] == ':') { + struct pipe_node* pn = (struct pipe_node*)f->node; + struct pipe_state* ps = pn ? pn->ps : 0; + if (pn && ps && pn->is_read_end && ps->count == 0 && ps->writers != 0) { + return -EAGAIN; + } + } + + // Non-blocking char devices (tty/pty) need special handling, since devfs read blocks. + if (f->node->flags == FS_CHARDEVICE) { + if (f->node->inode == 3) { + if (!tty_can_read()) return -EAGAIN; + } else if (f->node->inode == 4) { + if (!pty_master_can_read()) return -EAGAIN; + } else if (f->node->inode == 6) { + if (!pty_slave_can_read()) return -EAGAIN; + } + } + } + if (f->node->flags == FS_CHARDEVICE) { uint8_t kbuf[256]; uint32_t total = 0; @@ -930,6 +979,8 @@ static int syscall_read_impl(int fd, void* user_buf, uint32_t len) { return (int)total; } +static int syscall_write_impl(int fd, const void* user_buf, uint32_t len); + static int syscall_write_impl(int fd, const void* user_buf, uint32_t len) { if (len > 1024 * 1024) return -EINVAL; if (user_range_ok(user_buf, (size_t)len) == 0) return -EFAULT; @@ -942,6 +993,20 @@ static int syscall_write_impl(int fd, const void* user_buf, uint32_t len) { struct file* f = fd_get(fd); if (!f || !f->node) return -EBADF; + + int nonblock = (f->flags & O_NONBLOCK) ? 1 : 0; + if (nonblock) { + // Non-blocking pipe write: if full but readers exist, return -EAGAIN. + if (f->node->name[0] == 'p' && f->node->name[1] == 'i' && f->node->name[2] == 'p' && f->node->name[3] == 'e' && f->node->name[4] == ':') { + struct pipe_node* pn = (struct pipe_node*)f->node; + struct pipe_state* ps = pn ? pn->ps : 0; + if (pn && ps && !pn->is_read_end) { + if (ps->readers != 0 && (ps->cap - ps->count) == 0) { + return -EAGAIN; + } + } + } + } if (!f->node->write) return -ESPIPE; if (((f->node->flags & FS_FILE) == 0) && f->node->flags != FS_CHARDEVICE) return -ESPIPE; @@ -1282,6 +1347,14 @@ static void syscall_handler(struct registers* regs) { return; } + if (syscall_no == SYSCALL_FCNTL) { + int fd = (int)regs->ebx; + int cmd = (int)regs->ecx; + uint32_t arg = (uint32_t)regs->edx; + regs->eax = (uint32_t)syscall_fcntl_impl(fd, cmd, arg); + return; + } + if (syscall_no == SYSCALL_MKDIR) { const char* path = (const char*)regs->ebx; regs->eax = (uint32_t)syscall_mkdir_impl(path); diff --git a/user/init.c b/user/init.c index 277faab..9082795 100644 --- a/user/init.c +++ b/user/init.c @@ -67,6 +67,13 @@ enum { SYSCALL_UNLINK = 29, SYSCALL_GETDENTS = 30, + + SYSCALL_FCNTL = 31, +}; + +enum { + F_GETFL = 3, + F_SETFL = 4, }; enum { @@ -121,6 +128,11 @@ enum { enum { O_CREAT = 0x40, O_TRUNC = 0x200, + O_NONBLOCK = 0x800, +}; + +enum { + EAGAIN = 11, }; #define S_IFMT 0170000 @@ -144,6 +156,17 @@ static int sys_write(int fd, const void* buf, uint32_t len) { return __syscall_fix(ret); } +static int sys_fcntl(int fd, int cmd, uint32_t arg) { + int ret; + __asm__ volatile( + "int $0x80" + : "=a"(ret) + : "a"(SYSCALL_FCNTL), "b"(fd), "c"(cmd), "d"(arg) + : "memory" + ); + return __syscall_fix(ret); +} + static int sys_ioctl(int fd, uint32_t cmd, void* arg); static int isatty_fd(int fd) { @@ -1711,6 +1734,68 @@ void _start(void) { sys_write(1, "[init] isatty OK\n", (uint32_t)(sizeof("[init] isatty OK\n") - 1)); } + // B6: O_NONBLOCK smoke (pipe + pty) + { + int fds[2]; + if (sys_pipe(fds) < 0) { + sys_write(1, "[init] pipe for nonblock failed\n", + (uint32_t)(sizeof("[init] pipe for nonblock failed\n") - 1)); + sys_exit(1); + } + + if (sys_fcntl(fds[0], F_SETFL, O_NONBLOCK) < 0) { + sys_write(1, "[init] fcntl nonblock pipe failed\n", + (uint32_t)(sizeof("[init] fcntl nonblock pipe failed\n") - 1)); + sys_exit(1); + } + + char b; + int r = sys_read(fds[0], &b, 1); + if (r != -1 || errno != EAGAIN) { + sys_write(1, "[init] nonblock pipe read expected EAGAIN\n", + (uint32_t)(sizeof("[init] nonblock pipe read expected EAGAIN\n") - 1)); + sys_exit(1); + } + + if (sys_write(fds[1], "x", 1) != 1) { + sys_write(1, "[init] pipe write failed\n", + (uint32_t)(sizeof("[init] pipe write failed\n") - 1)); + sys_exit(1); + } + r = sys_read(fds[0], &b, 1); + if (r != 1 || b != 'x') { + sys_write(1, "[init] nonblock pipe read after write failed\n", + (uint32_t)(sizeof("[init] nonblock pipe read after write failed\n") - 1)); + sys_exit(1); + } + + (void)sys_close(fds[0]); + (void)sys_close(fds[1]); + + int p = sys_open("/dev/ptmx", 0); + if (p < 0) { + sys_write(1, "[init] open /dev/ptmx failed\n", + (uint32_t)(sizeof("[init] open /dev/ptmx failed\n") - 1)); + sys_exit(1); + } + if (sys_fcntl(p, F_SETFL, O_NONBLOCK) < 0) { + sys_write(1, "[init] fcntl nonblock ptmx failed\n", + (uint32_t)(sizeof("[init] fcntl nonblock ptmx failed\n") - 1)); + sys_exit(1); + } + char pch; + r = sys_read(p, &pch, 1); + if (r != -1 || errno != EAGAIN) { + sys_write(1, "[init] nonblock ptmx read expected EAGAIN\n", + (uint32_t)(sizeof("[init] nonblock ptmx read expected EAGAIN\n") - 1)); + sys_exit(1); + } + (void)sys_close(p); + + sys_write(1, "[init] O_NONBLOCK OK\n", + (uint32_t)(sizeof("[init] O_NONBLOCK OK\n") - 1)); + } + enum { NCHILD = 100 }; int children[NCHILD]; for (int i = 0; i < NCHILD; i++) {