diff --git a/libc/calls/fcntl-nt.c b/libc/calls/fcntl-nt.c index 09483e6e1..830a50830 100644 --- a/libc/calls/fcntl-nt.c +++ b/libc/calls/fcntl-nt.c @@ -332,7 +332,8 @@ textwindows int sys_fcntl_nt(int fd, int cmd, uintptr_t arg) { if (__isfdkind(fd, kFdFile) || // __isfdkind(fd, kFdSocket) || // __isfdkind(fd, kFdConsole) || // - __isfdkind(fd, kFdDevNull)) { + __isfdkind(fd, kFdDevNull) || // + __isfdkind(fd, kFdDevRandom)) { if (cmd == F_GETFL) { rc = g_fds.p[fd].flags & (O_ACCMODE | _O_APPEND | _O_DIRECT | _O_NONBLOCK | _O_RANDOM | _O_SEQUENTIAL); diff --git a/libc/calls/fstat-nt.c b/libc/calls/fstat-nt.c index 9fd4874cf..97a941b1c 100644 --- a/libc/calls/fstat-nt.c +++ b/libc/calls/fstat-nt.c @@ -104,6 +104,7 @@ textwindows int sys_fstat_nt(int fd, struct stat *st) { return ebadf(); case kFdConsole: case kFdDevNull: + case kFdDevRandom: return sys_fstat_nt_special(g_fds.p[fd].kind, st); case kFdSocket: return sys_fstat_nt_socket(g_fds.p[fd].kind, st); diff --git a/libc/calls/fstatat-nt.c b/libc/calls/fstatat-nt.c index 63e24b9b4..b8b75e8f8 100644 --- a/libc/calls/fstatat-nt.c +++ b/libc/calls/fstatat-nt.c @@ -59,6 +59,8 @@ textwindows int sys_fstatat_nt(int dirfd, const char *path, struct stat *st, return sys_fstat_nt_special(kFdConsole, st); } else if (!strcmp(path + 5, "null")) { return sys_fstat_nt_special(kFdDevNull, st); + } else if (!strcmp(path + 5, "random") || !strcmp(path + 5, "urandom")) { + return sys_fstat_nt_special(kFdDevRandom, st); } else if (!strcmp(path + 5, "stdin")) { return sys_fstat_nt(STDIN_FILENO, st); } else if (!strcmp(path + 5, "stdout")) { diff --git a/libc/calls/ioctl.c b/libc/calls/ioctl.c index 4d96e6d61..21434d00e 100644 --- a/libc/calls/ioctl.c +++ b/libc/calls/ioctl.c @@ -24,7 +24,6 @@ #include "libc/calls/termios.h" #include "libc/dce.h" #include "libc/errno.h" -#include "libc/serialize.h" #include "libc/intrin/cmpxchg.h" #include "libc/intrin/strace.internal.h" #include "libc/intrin/weaken.h" @@ -42,6 +41,7 @@ #include "libc/nt/winsock.h" #include "libc/runtime/runtime.h" #include "libc/runtime/stack.h" +#include "libc/serialize.h" #include "libc/sock/internal.h" #include "libc/sock/struct/ifconf.h" #include "libc/sock/struct/ifreq.h" @@ -66,7 +66,7 @@ static struct HostAdapterInfoNode { struct sockaddr netmask; struct sockaddr broadcast; short flags; -} *__hostInfo; +} * __hostInfo; static int ioctl_default(int fd, unsigned long request, void *arg) { int rc; @@ -107,8 +107,9 @@ static int ioctl_fionread(int fd, uint32_t *arg) { *arg = MAX(0, bytes); return 0; } else if (g_fds.p[fd].kind == kFdDevNull) { - *arg = 1; - return 0; + return enotty(); + } else if (g_fds.p[fd].kind == kFdDevRandom) { + return einval(); } else if (GetFileType(handle) == kNtFileTypePipe) { uint32_t avail; if (PeekNamedPipe(handle, 0, 0, 0, &avail, 0)) { diff --git a/libc/calls/ischardev.c b/libc/calls/ischardev.c index a323b908d..615c068c8 100644 --- a/libc/calls/ischardev.c +++ b/libc/calls/ischardev.c @@ -55,6 +55,7 @@ bool32 ischardev(int fd) { } } else { return __isfdkind(fd, kFdConsole) || __isfdkind(fd, kFdDevNull) || + __isfdkind(fd, kFdDevRandom) || (__isfdkind(fd, kFdFile) && GetFileType(g_fds.p[fd].handle) == kNtFileTypeChar); } diff --git a/libc/calls/lseek-nt.c b/libc/calls/lseek-nt.c index 28ec23daa..11ef3471f 100644 --- a/libc/calls/lseek-nt.c +++ b/libc/calls/lseek-nt.c @@ -62,7 +62,7 @@ static textwindows int64_t Seek(struct Fd *f, int64_t offset, int whence) { } textwindows int64_t sys_lseek_nt(int fd, int64_t offset, int whence) { - if (__isfdkind(fd, kFdDevNull)) { + if (__isfdkind(fd, kFdDevNull) || __isfdkind(fd, kFdDevRandom)) { return offset; } else if (__isfdkind(fd, kFdFile)) { struct Fd *f = g_fds.p + fd; diff --git a/libc/calls/open-nt.c b/libc/calls/open-nt.c index 663164c53..c7339171b 100644 --- a/libc/calls/open-nt.c +++ b/libc/calls/open-nt.c @@ -159,6 +159,15 @@ static textwindows int sys_open_nt_special(int fd, int flags, int mode, return fd; } +static textwindows int sys_open_nt_no_handle(int fd, int flags, int mode, + int kind) { + g_fds.p[fd].kind = kind; + g_fds.p[fd].mode = mode; + g_fds.p[fd].flags = flags; + g_fds.p[fd].handle = -1; + return fd; +} + static textwindows int sys_open_nt_dup(int fd, int flags, int mode, int oldfd) { int64_t handle; if (!__isfdopen(oldfd)) { @@ -211,6 +220,8 @@ textwindows int sys_open_nt(int dirfd, const char *file, uint32_t flags, rc = sys_open_nt_special(fd, flags, mode, kFdConsole, u"CONIN$"); } else if (!strcmp(file + 5, "null")) { rc = sys_open_nt_special(fd, flags, mode, kFdDevNull, u"NUL"); + } else if (!strcmp(file + 5, "urandom") || !strcmp(file + 5, "random")) { + rc = sys_open_nt_no_handle(fd, flags, mode, kFdDevRandom); } else if (!strcmp(file + 5, "stdin")) { rc = sys_open_nt_dup(fd, flags, mode, STDIN_FILENO); } else if (!strcmp(file + 5, "stdout")) { diff --git a/libc/calls/pread.c b/libc/calls/pread.c index c13dcab36..c90fe6108 100644 --- a/libc/calls/pread.c +++ b/libc/calls/pread.c @@ -72,7 +72,8 @@ ssize_t pread(int fd, void *buf, size_t size, int64_t offset) { rc = sys_pread(fd, buf, size, offset, offset); } else if (__isfdkind(fd, kFdSocket)) { rc = espipe(); - } else if (__isfdkind(fd, kFdFile) || __isfdkind(fd, kFdDevNull)) { + } else if (__isfdkind(fd, kFdFile) || __isfdkind(fd, kFdDevNull) || + __isfdkind(fd, kFdDevRandom)) { rc = sys_read_nt(fd, (struct iovec[]){{buf, size}}, 1, offset); } else { rc = ebadf(); diff --git a/libc/calls/printfds.c b/libc/calls/printfds.c index 9b81e1e00..9e3ad0e55 100644 --- a/libc/calls/printfds.c +++ b/libc/calls/printfds.c @@ -40,6 +40,8 @@ static const char *__fdkind2str(int x) { return "kFdZip"; case kFdEpoll: return "kFdEpoll"; + case kFdDevRandom: + return "kFdRandom"; default: return "kFdWut"; } diff --git a/libc/calls/pwrite.c b/libc/calls/pwrite.c index b777e06ff..472472c6d 100644 --- a/libc/calls/pwrite.c +++ b/libc/calls/pwrite.c @@ -65,7 +65,8 @@ ssize_t pwrite(int fd, const void *buf, size_t size, int64_t offset) { rc = sys_pwrite(fd, buf, size, offset, offset); } else if (__isfdkind(fd, kFdSocket)) { rc = espipe(); - } else if (__isfdkind(fd, kFdFile) || __isfdkind(fd, kFdDevNull)) { + } else if (__isfdkind(fd, kFdFile) || __isfdkind(fd, kFdDevNull) || + __isfdkind(fd, kFdDevRandom)) { rc = sys_write_nt(fd, (struct iovec[]){{(void *)buf, size}}, 1, offset); } else { return ebadf(); diff --git a/libc/calls/read-nt.c b/libc/calls/read-nt.c index 699a7e2d8..931e9b1f9 100644 --- a/libc/calls/read-nt.c +++ b/libc/calls/read-nt.c @@ -786,6 +786,10 @@ textwindows ssize_t ReadBuffer(int fd, void *data, size_t size, int64_t offset, return 0; } + if (f->kind == kFdDevRandom) { + return ProcessPrng(data, size) ? size : __winerr(); + } + if (f->kind == kFdConsole) { return ReadFromConsole(f, data, size, waitmask); } diff --git a/libc/calls/readv-nt.c b/libc/calls/readv-nt.c index 3bc2f57d4..af4de5323 100644 --- a/libc/calls/readv-nt.c +++ b/libc/calls/readv-nt.c @@ -29,6 +29,7 @@ textwindows ssize_t sys_readv_nt(int fd, const struct iovec *iov, int iovlen) { case kFdFile: case kFdConsole: case kFdDevNull: + case kFdDevRandom: return sys_read_nt(fd, iov, iovlen, -1); case kFdSocket: return _weaken(sys_recv_nt)(fd, iov, iovlen, 0); diff --git a/libc/calls/readwrite-nt.c b/libc/calls/readwrite-nt.c index 3643cd9dc..530880f4b 100644 --- a/libc/calls/readwrite-nt.c +++ b/libc/calls/readwrite-nt.c @@ -61,7 +61,7 @@ sys_readwrite_nt(int fd, void *data, size_t size, ssize_t offset, bool pwriting = offset != -1; bool seekable = (f->kind == kFdFile && GetFileType(handle) == kNtFileTypeDisk) || - f->kind == kFdDevNull; + f->kind == kFdDevNull || f->kind == kFdDevRandom; if (pwriting && !seekable) { return espipe(); } diff --git a/libc/calls/struct/fd.internal.h b/libc/calls/struct/fd.internal.h index 235ee7ff5..e3ff36fc5 100644 --- a/libc/calls/struct/fd.internal.h +++ b/libc/calls/struct/fd.internal.h @@ -2,15 +2,16 @@ #define COSMOPOLITAN_LIBC_CALLS_STRUCT_FD_INTERNAL_H_ COSMOPOLITAN_C_START_ -#define kFdEmpty 0 -#define kFdFile 1 -#define kFdSocket 2 -#define kFdConsole 4 -#define kFdSerial 5 -#define kFdZip 6 -#define kFdEpoll 7 -#define kFdReserved 8 -#define kFdDevNull 9 +#define kFdEmpty 0 +#define kFdFile 1 +#define kFdSocket 2 +#define kFdConsole 4 +#define kFdSerial 5 +#define kFdZip 6 +#define kFdEpoll 7 +#define kFdReserved 8 +#define kFdDevNull 9 +#define kFdDevRandom 10 struct Fd { char kind; diff --git a/libc/calls/write-nt.c b/libc/calls/write-nt.c index c5ae7cb02..edea79f9f 100644 --- a/libc/calls/write-nt.c +++ b/libc/calls/write-nt.c @@ -52,6 +52,11 @@ static textwindows ssize_t sys_write_nt_impl(int fd, void *data, size_t size, struct Fd *f = g_fds.p + fd; bool isconsole = f->kind == kFdConsole; + // not implemented, XNU returns eperm(); + if (f->kind == kFdDevRandom) { + return eperm(); + } + // determine win32 handle for writing int64_t handle = f->handle; if (isconsole && _weaken(GetConsoleOutputHandle)) { diff --git a/libc/calls/writev-nt.c b/libc/calls/writev-nt.c index 367b22d27..edf7cdd2f 100644 --- a/libc/calls/writev-nt.c +++ b/libc/calls/writev-nt.c @@ -28,6 +28,7 @@ textwindows ssize_t sys_writev_nt(int fd, const struct iovec *iov, int iovlen) { case kFdFile: case kFdConsole: case kFdDevNull: + case kFdDevRandom: return sys_write_nt(fd, iov, iovlen, -1); case kFdSocket: return _weaken(sys_send_nt)(fd, iov, iovlen, 0); diff --git a/test/libc/calls/specialfile_test.c b/test/libc/calls/specialfile_test.c index 169ac50ee..f96cea944 100644 --- a/test/libc/calls/specialfile_test.c +++ b/test/libc/calls/specialfile_test.c @@ -20,15 +20,19 @@ #include "libc/dce.h" #include "libc/errno.h" #include "libc/nt/files.h" +#include "libc/str/str.h" #include "libc/sysv/consts/f.h" #include "libc/sysv/consts/o.h" #include "libc/testlib/testlib.h" int pipefd[2]; int stdoutBack; +int allowMask; void SetUpOnce(void) { testlib_enable_tmp_setup_teardown(); + // qemu-aarch64 defines o_largefile wrong + allowMask = ~(O_LARGEFILE | 0x00008000); } void CaptureStdout(void) { @@ -46,8 +50,7 @@ void RestoreStdout(void) { TEST(specialfile, devNull) { ASSERT_SYS(0, 3, creat("/dev/null", 0644)); - // qemu-aarch64 defines o_largefile wrong - ASSERT_EQ(O_WRONLY, fcntl(3, F_GETFL) & ~(O_LARGEFILE | 0x00008000)); + ASSERT_EQ(O_WRONLY, fcntl(3, F_GETFL) & allowMask); ASSERT_SYS(0, 2, write(3, "hi", 2)); ASSERT_SYS(0, 2, pwrite(3, "hi", 2, 0)); ASSERT_SYS(0, 2, pwrite(3, "hi", 2, 2)); @@ -64,12 +67,51 @@ TEST(specialfile, devNull) { TEST(specialfile, devNullRead) { char buf[8] = {0}; ASSERT_SYS(0, 3, open("/dev/null", O_RDONLY)); - // qemu-aarch64 defines o_largefile wrong - ASSERT_EQ(O_RDONLY, fcntl(3, F_GETFL) & ~(O_LARGEFILE | 0x00008000)); + ASSERT_EQ(O_RDONLY, fcntl(3, F_GETFL) & allowMask); ASSERT_SYS(0, 0, read(3, buf, 8)); ASSERT_SYS(0, 0, close(3)); } +TEST(specialfile, devRandomRead) { + char buf[8] = {0}; + ASSERT_SYS(0, 3, open("/dev/random", O_RDONLY)); + ASSERT_EQ(O_RDONLY, fcntl(3, F_GETFL) & allowMask); + ASSERT_SYS(0, 8, read(3, buf, 8)); + ASSERT_NE(0, memcmp(buf, " ", 8)); + ASSERT_SYS(0, 0, close(3)); +} + +TEST(specialfile, devUrandomRead) { + char buf[8] = {0}; + ASSERT_SYS(0, 3, open("/dev/urandom", O_RDONLY)); + ASSERT_EQ(O_RDONLY, fcntl(3, F_GETFL) & allowMask); + ASSERT_SYS(0, 8, read(3, buf, 8)); + ASSERT_NE(0, memcmp(buf, " ", 8)); + ASSERT_SYS(0, 0, close(3)); +} + +TEST(specialfile, devRandomWrite_fails_on_nt) { + if (!IsWindows()) { + return; + } + char buf[8] = {0}; + ASSERT_SYS(0, 3, creat("/dev/random", 0644)); + ASSERT_EQ(O_WRONLY, fcntl(3, F_GETFL) & allowMask); + ASSERT_SYS(EPERM, -1, write(3, buf, 8)); + ASSERT_SYS(0, 0, close(3)); +} + +TEST(specialfile, devUrandomWrite_fails_on_nt) { + if (!IsWindows()) { + return; + } + char buf[8] = {0}; + ASSERT_SYS(0, 3, creat("/dev/urandom", 0644)); + ASSERT_EQ(O_WRONLY, fcntl(3, F_GETFL) & allowMask); + ASSERT_SYS(EPERM, -1, write(3, buf, 8)); + ASSERT_SYS(0, 0, close(3)); +} + TEST(specialfile, devStdout) { char buf[8] = {8}; CaptureStdout();