]> Projects (at) Tadryanom (dot) Me - AdrOS.git/commitdiff
posix: add fcntl(F_SETFL) O_NONBLOCK for pipes and tty/pty
authorTulio A M Mendes <[email protected]>
Tue, 10 Feb 2026 01:12:20 +0000 (22:12 -0300)
committerTulio A M Mendes <[email protected]>
Tue, 10 Feb 2026 01:12:20 +0000 (22:12 -0300)
include/errno.h
include/syscall.h
src/kernel/syscall.c
user/init.c

index c6e968e596067f839fe4f1cc11710f6373d3683c..0ed8eeaf476ec16714a6a993268c43edc7f2b0ef 100644 (file)
@@ -17,6 +17,7 @@
 #define E2BIG 7
 #define EBADF 9
 #define ECHILD 10
+#define EAGAIN 11
 #define EFAULT 14
 #define EEXIST 17
 #define ENOMEM 12
index 5aad4b85557daac2bd77c5def2abecb11f197919..e2b2c017b1b5bc8a43030d08c1b6c3cc91b845ea 100644 (file)
@@ -51,6 +51,8 @@ enum {
     SYSCALL_UNLINK = 29,
 
     SYSCALL_GETDENTS = 30,
+
+    SYSCALL_FCNTL = 31,
 };
 
 #endif
index 05761a2eb16c58632c02a9af2e17c75af50a49f1..f43baa567c99159002262e7c1e264f067ef05d5f 100644 (file)
 
 #include <stddef.h>
 
+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 {
@@ -793,7 +802,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);
@@ -804,6 +813,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;
 
@@ -895,6 +921,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;
@@ -939,6 +988,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;
@@ -951,6 +1002,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;
 
@@ -1291,6 +1356,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);
index dfc4871fec42de66ba685716bb79681500cf162e..a0422450ce2f2b04202b5856ae9f5201f5667f1e 100644 (file)
@@ -76,6 +76,13 @@ enum {
     SYSCALL_UNLINK = 29,
 
     SYSCALL_GETDENTS = 30,
+
+    SYSCALL_FCNTL = 31,
+};
+
+enum {
+    F_GETFL = 3,
+    F_SETFL = 4,
 };
 
 enum {
@@ -130,6 +137,11 @@ enum {
 enum {
     O_CREAT = 0x40,
     O_TRUNC = 0x200,
+    O_NONBLOCK = 0x800,
+};
+
+enum {
+    EAGAIN = 11,
 };
 
 #define S_IFMT  0170000
@@ -153,6 +165,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) {
@@ -1720,6 +1743,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++) {