diff --git a/src/syscall.c b/src/syscall.c index a1388cc..1939b83 100644 --- a/src/syscall.c +++ b/src/syscall.c @@ -298,9 +298,25 @@ uint64 syscallDispatch(Registers* regs) } regs->rax = (uint64)result; return (uint64)regs; - case sysRead: - regs->rax = (uint64)doRead(regs->rdi, regs->rsi, regs->rdx); + } + case sysRead: { + int64 result = doRead(regs->rdi, regs->rsi, regs->rdx); + if (result == -1) { + /* Covers a fdFlagBlocking pipe/fifo end (see sysWrite above) and + fd 0's console reader queue (keyboard.h) alike -- both report + "would block" as -1 from doRead, and vfsBlockingChannel picks + the right wait channel for whichever kind fd actually is. */ + const void* channel = vfsBlockingChannel((int)regs->rdi, false); + if (channel) { + currentThread()->sigInRetryableSyscall = true; + schedBlockCurrent(channel); + regs->rip -= 2; + return schedule((uint64)regs); + } + } + regs->rax = (uint64)result; return (uint64)regs; + } case sysGetpid: regs->rax = currentThreadId(); return (uint64)regs; @@ -358,6 +374,68 @@ uint64 syscallDispatch(Registers* regs) regs->rax = (uint64)(int64)vfsMkdir(path); return (uint64)regs; } + case sysChdir: { + char path[256]; + if (!copyUserPath(regs->rdi, path, sizeof(path))) + regs->rax = (uint64)-1; + else + regs->rax = (uint64)(int64)vfsChdir(path); + return (uint64)regs; + } + case sysGetcwd: + if (!userRangeOk(regs->rdi, regs->rsi)) + regs->rax = (uint64)-1; + else + regs->rax = (uint64)vfsGetcwd((char*)regs->rdi, regs->rsi); + return (uint64)regs; + case sysGetEnv: { + char name[envEntryMax]; + if (!copyUserPath(regs->rdi, name, sizeof(name)) || !userRangeOk(regs->rsi, regs->rdx)) { + regs->rax = (uint64)-1; + return (uint64)regs; + } + const char* value = currentThreadGetEnv(name); + if (!value) { + regs->rax = (uint64)-1; + return (uint64)regs; + } + uint64 len = strlen(value); + if (len >= regs->rdx) { + regs->rax = (uint64)-1; + return (uint64)regs; + } + memcpy((void*)regs->rsi, value, len + 1); + regs->rax = (uint64)len; + return (uint64)regs; + } + case sysSetEnv: { + char name[envEntryMax]; + char value[envEntryMax]; + if (!copyUserPath(regs->rdi, name, sizeof(name)) || + !copyUserPath(regs->rsi, value, sizeof(value))) { + regs->rax = (uint64)-1; + return (uint64)regs; + } + regs->rax = currentThreadSetEnv(name, value) ? 0 : (uint64)-1; + return (uint64)regs; + } + case sysUnsetEnv: { + /* Real unsetenv(3): a no-op success whether or not `name` actually + had an entry -- ONLY a bad user pointer/path is a real failure + here, unlike sysSetEnv above where "table full"/"too long" are + genuine failures. currentThreadUnsetEnv's own bool return (which + DOES distinguish "removed" from "was not there", for a kernel- + side caller like shell.c that might want to) is deliberately + discarded at this syscall boundary. */ + char name[envEntryMax]; + if (!copyUserPath(regs->rdi, name, sizeof(name))) { + regs->rax = (uint64)-1; + return (uint64)regs; + } + currentThreadUnsetEnv(name); + regs->rax = 0; + return (uint64)regs; + } case sysUnlink: { char path[256]; if (!copyUserPath(regs->rdi, path, sizeof(path)))