#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 {
if (!f) return -ENOMEM;
f->node = node;
f->offset = 0;
- f->flags = 0;
+ f->flags = flags;
f->refcount = 1;
int fd = fd_alloc(f);
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;
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;
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;
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;
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);
SYSCALL_UNLINK = 29,
SYSCALL_GETDENTS = 30,
+
+ SYSCALL_FCNTL = 31,
+};
+
+enum {
+ F_GETFL = 3,
+ F_SETFL = 4,
};
enum {
enum {
O_CREAT = 0x40,
O_TRUNC = 0x200,
+ O_NONBLOCK = 0x800,
+};
+
+enum {
+ EAGAIN = 11,
};
#define S_IFMT 0170000
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) {
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++) {