// POSIX compatibility support for byte-stream file I/O. #include #include #include #include #include #include #include "posix_internal.h" #include "posix_fd.h" #include "posix_path.h" // Returns whether one open description permits reading. static bool PosixDescriptionReadable(const POSIX_OPEN_DESCRIPTION* description) { return description && (PosixFdGetStatusFlags(description) & O_ACCMODE) != O_WRONLY; } // Returns whether one open description permits writing. static bool PosixDescriptionWritable(const POSIX_OPEN_DESCRIPTION* description) { return description && (PosixFdGetStatusFlags(description) & O_ACCMODE) != O_RDONLY; } // Returns the server-owned shared ring behind one pipe open description. static SYS_PIPE_SHARED_STATE* PosixPipeShared(POSIX_OPEN_DESCRIPTION* description) { if (!description || !description->Shared || description->Shared->SharedObjectData == 0) { errno = EIO; return (SYS_PIPE_SHARED_STATE*)0; } SYS_PIPE_SHARED_STATE* shared = (SYS_PIPE_SHARED_STATE*)(uintptr_t)description->Shared->SharedObjectData; if (shared->Magic != SYS_PIPE_SHARED_MAGIC || shared->Version != SYS_PIPE_SHARED_VERSION || shared->Capacity != SYS_PIPE_BUFFER_CAPACITY || shared->HeaderBytes != SYS_PIPE_SHARED_HEADER_BYTES) { errno = EIO; return (SYS_PIPE_SHARED_STATE*)0; } return shared; } // Acquires one shared pipe-side serialization lock without entering the kernel. static void PosixPipeLock(volatile long* lock) { while (_InterlockedCompareExchange(lock, 1, 0) != 0) _mm_pause(); _ReadWriteBarrier(); } // Releases one shared pipe-side serialization lock. static void PosixPipeUnlock(volatile long* lock) { _ReadWriteBarrier(); _InterlockedExchange(lock, 0); } // Returns buffered bytes, rejecting corrupted shared counters. static int PosixPipeUsed(SYS_PIPE_SHARED_STATE* shared, uint64_t* usedOut) { uint64_t readPosition = shared->ReadPosition; _ReadWriteBarrier(); uint64_t writePosition = shared->WritePosition; uint64_t used = writePosition - readPosition; if (used > SYS_PIPE_BUFFER_CAPACITY) { errno = EIO; return -1; } *usedOut = used; return 0; } // Copies bytes out of the shared circular pipe buffer. static void PosixPipeCopyOut(SYS_PIPE_SHARED_STATE* shared, uint64_t readPosition, void* buffer, size_t length) { size_t offset = (size_t)(readPosition % SYS_PIPE_BUFFER_CAPACITY); size_t first = SYS_PIPE_BUFFER_CAPACITY - offset; if (first > length) first = length; memcpy(buffer, shared->Buffer + offset, first); if (first < length) memcpy((uint8_t*)buffer + first, shared->Buffer, length - first); } // Copies bytes into the shared circular pipe buffer. static void PosixPipeCopyIn(SYS_PIPE_SHARED_STATE* shared, uint64_t writePosition, const void* buffer, size_t length) { size_t offset = (size_t)(writePosition % SYS_PIPE_BUFFER_CAPACITY); size_t first = SYS_PIPE_BUFFER_CAPACITY - offset; if (first > length) first = length; memcpy(shared->Buffer + offset, buffer, first); if (first < length) memcpy(shared->Buffer, (const uint8_t*)buffer + first, length - first); } // Reads one pipe entirely through the shared user mapping except when blocking. static ssize_t PosixPipeRead(POSIX_OPEN_DESCRIPTION* description, void* buffer, size_t length) { SYS_PIPE_SHARED_STATE* shared = PosixPipeShared(description); if (!shared) return -1; for (;;) { PosixPipeLock(&shared->ReadLock); uint64_t used = 0; if (PosixPipeUsed(shared, &used) != 0) { PosixPipeUnlock(&shared->ReadLock); return -1; } if (used != 0) { size_t transfer = length < used ? length : (size_t)used; uint64_t readPosition = shared->ReadPosition; PosixPipeCopyOut(shared, readPosition, buffer, transfer); _ReadWriteBarrier(); shared->ReadPosition = readPosition + transfer; _ReadWriteBarrier(); long writeWaiters = shared->WriteWaiters; PosixPipeUnlock(&shared->ReadLock); if (writeWaiters != 0) (void)SysNotifyPipe(description->KHandle); return (ssize_t)transfer; } if (shared->WriterOpen == 0) { PosixPipeUnlock(&shared->ReadLock); return 0; } if (PosixFdGetStatusFlags(description) & O_NONBLOCK) { PosixPipeUnlock(&shared->ReadLock); errno = EAGAIN; return -1; } PosixPipeUnlock(&shared->ReadLock); uint32_t status = SysWait(description->KHandle, SYS_WAIT_FLAG_NONE); if (status != NTLSTATUS_SUCCESS) { errno = PosixStatusToErrno(status); return -1; } } } // Writes one pipe entirely through the shared user mapping except when blocking/waking. static ssize_t PosixPipeWrite(POSIX_OPEN_DESCRIPTION* description, const void* buffer, size_t length) { SYS_PIPE_SHARED_STATE* shared = PosixPipeShared(description); if (!shared) return -1; for (;;) { PosixPipeLock(&shared->WriteLock); if (shared->ReaderOpen == 0) { PosixPipeUnlock(&shared->WriteLock); errno = EPIPE; return -1; } uint64_t used = 0; if (PosixPipeUsed(shared, &used) != 0) { PosixPipeUnlock(&shared->WriteLock); return -1; } size_t available = SYS_PIPE_BUFFER_CAPACITY - (size_t)used; size_t transfer = 0; if (length <= SYS_PIPE_ATOMIC_WRITE_MAX) { if (available >= length) transfer = length; } else if (available != 0) { transfer = length < available ? length : available; } if (transfer != 0) { uint64_t writePosition = shared->WritePosition; PosixPipeCopyIn(shared, writePosition, buffer, transfer); _ReadWriteBarrier(); shared->WritePosition = writePosition + transfer; _ReadWriteBarrier(); long readWaiters = shared->ReadWaiters; PosixPipeUnlock(&shared->WriteLock); if (readWaiters != 0) (void)SysNotifyPipe(description->KHandle); return (ssize_t)transfer; } if (PosixFdGetStatusFlags(description) & O_NONBLOCK) { PosixPipeUnlock(&shared->WriteLock); errno = EAGAIN; return -1; } PosixPipeUnlock(&shared->WriteLock); uint32_t status = SysWait(description->KHandle, SYS_WAIT_FLAG_NONE); if (status != NTLSTATUS_SUCCESS) { errno = PosixStatusToErrno(status); return -1; } } } // Clears the native console display. __declspec(dllexport) void clearscreen(void) { (void)NtlConsoleClear(SYS_HANDLE_INVALID, 0x012456); } // Flushes pending native console keyboard input. __declspec(dllexport) void clearkeyboard(void) { (void)NtlConsoleFlushInput(SYS_HANDLE_INVALID); } // Opens one native file or synthetic POSIX device and returns a process-local descriptor. __declspec(dllexport) int open(const char* path, int flags, ...) { if (!path || (flags & O_ACCMODE) == O_ACCMODE) { errno = EINVAL; return -1; } char native[512]; const char* p = PosixTranslatePath(path, native, sizeof(native)); int statusFlags = flags & (O_ACCMODE | O_APPEND | O_NONBLOCK); if (strcmp(p, SYNTH_DEVNULL) == 0) return PosixFdCreate(PosixFdDevNull, SYS_HANDLE_INVALID, statusFlags); if (strcmp(p, SYNTH_DEVZERO) == 0) return PosixFdCreate(PosixFdDevZero, SYS_HANDLE_INVALID, statusFlags); uint32_t kh = SYS_HANDLE_INVALID; uint32_t access = SYS_FILE_ACCESS_QUERY | SYS_FILE_ACCESS_LOCK; uint32_t disposition = SYS_FILE_OPEN_EXISTING; if ((flags & O_ACCMODE) == O_RDWR) access |= SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_WRITE; else if ((flags & O_ACCMODE) == O_WRONLY) access |= SYS_FILE_ACCESS_WRITE; else access |= SYS_FILE_ACCESS_READ; if (flags & O_APPEND) access |= SYS_FILE_ACCESS_APPEND; if ((flags & O_CREAT) && (flags & O_TRUNC)) disposition = SYS_FILE_CREATE_ALWAYS; else if (flags & O_CREAT) disposition = SYS_FILE_OPEN_ALWAYS; else if (flags & O_TRUNC) disposition = SYS_FILE_TRUNCATE_EXISTING; uint32_t status = NtlOpenFile(SysSvFromCstr(p), access, disposition, SYS_FILE_FLAG_NONE, &kh); if (status != NTLSTATUS_SUCCESS) { errno = PosixStatusToErrno(status); return -1; } int fd = PosixFdCreate(PosixFdFile, kh, statusFlags); if (fd < 0) SysCloseHandle(kh); return fd; } // Reads bytes from one descriptor through its stable open-description reference. __declspec(dllexport) ssize_t read(int fd, void* buf, size_t count) { if (count == 0) return 0; if (!buf) { errno = EINVAL; return -1; } POSIX_OPEN_DESCRIPTION* description = PosixFdAcquire(fd); if (!description) return -1; if (!PosixDescriptionReadable(description)) { PosixFdRelease(description); errno = EBADF; return -1; } ssize_t result = -1; switch (description->Type) { case PosixFdConsoleRead: { char* dst = (char*)buf; result = (ssize_t)count; for (size_t i = 0; i < count; i++) { uint32_t status = NtlConsoleReadCharacter(SYS_HANDLE_INVALID, &dst[i]); if (status != NTLSTATUS_SUCCESS) { errno = PosixStatusToErrno(status); result = -1; break; } if (dst[i] == '\n' || dst[i] == '\r') { result = (ssize_t)(i + 1u); break; } } break; } case PosixFdDevNull: result = 0; break; case PosixFdDevZero: memset(buf, 0, count); result = (ssize_t)count; break; case PosixFdFile: { size_t got = 0; SYS_FILE_IO_REQUEST request = SysMakeFileIoRequest(description->KHandle, buf, count, &got); uint32_t status = SysReadFile(&request); if (status != NTLSTATUS_SUCCESS && got == 0) errno = PosixStatusToErrno(status); else result = (ssize_t)got; break; } case PosixFdPipeRead: result = PosixPipeRead(description, buf, count); break; case PosixFdSocket: { size_t got = 0; SYS_SOCKET_RECV_REQUEST request = { sizeof(SYS_SOCKET_RECV_REQUEST), description->KHandle, 0, buf, count, &got }; uint32_t status = SysRecvSocket(&request); if (status != NTLSTATUS_SUCCESS) errno = PosixStatusToErrno(status); else result = (ssize_t)got; break; } default: errno = EBADF; break; } PosixFdRelease(description); return result; } // Writes bytes through one descriptor's shared open description. __declspec(dllexport) ssize_t write(int fd, const void* buf, size_t count) { if (count == 0) return 0; if (!buf) { errno = EINVAL; return -1; } POSIX_OPEN_DESCRIPTION* description = PosixFdAcquire(fd); if (!description) return -1; if (!PosixDescriptionWritable(description)) { PosixFdRelease(description); errno = EBADF; return -1; } ssize_t result = -1; switch (description->Type) { case PosixFdConsoleWrite: { size_t written = 0; uint32_t status = NtlConsoleWrite(SYS_HANDLE_INVALID, buf, count, &written); if (status != NTLSTATUS_SUCCESS && written == 0) errno = PosixStatusToErrno(status); else result = (ssize_t)written; break; } case PosixFdDevNull: case PosixFdDevZero: result = (ssize_t)count; break; case PosixFdFile: { size_t written = 0; SYS_FILE_IO_REQUEST request = SysMakeFileIoRequest(description->KHandle, (void*)buf, count, &written); uint32_t status = SysWriteFile(&request); if (status != NTLSTATUS_SUCCESS && written == 0) errno = PosixStatusToErrno(status); else result = (ssize_t)written; break; } case PosixFdPipeWrite: result = PosixPipeWrite(description, buf, count); break; case PosixFdSocket: { size_t sent = 0; SYS_SOCKET_SEND_REQUEST request = { sizeof(SYS_SOCKET_SEND_REQUEST), description->KHandle, 0, buf, count, &sent }; uint32_t status = SysSendSocket(&request); if (status != NTLSTATUS_SUCCESS) errno = PosixStatusToErrno(status); else result = (ssize_t)sent; break; } default: errno = EBADF; break; } PosixFdRelease(description); return result; } // Closes one descriptor without invalidating concurrent open-description users. __declspec(dllexport) int close(int fd) { return PosixFdClose(fd); } // Creates one anonymous pipe whose backing and master endpoint capabilities are owned by posixsrv. __declspec(dllexport) int pipe(int pipefd[2]) { if (!pipefd) { errno = EINVAL; return -1; } NTL_POSIX_REQUEST request; memset(&request, 0, sizeof(request)); request.Size = sizeof(request); request.Version = NTL_POSIX_PROTOCOL_VERSION; request.Operation = NTL_POSIX_OP_CREATE_PIPE; request.Flags = NTL_POSIX_REQUEST_FLAG_NONE; SYS_HANDLE handles[2] = { SYS_HANDLE_INVALID, SYS_HANDLE_INVALID }; NTL_POSIX_REPLY reply; KSTATUS status = PosixRuntimeCallServerExchange( &request, (const SYS_PORT_HANDLE_TRANSFER*)0, 0, handles, 2u, 2u, &reply); if (!KSUCCESS(status) || !KSUCCESS(reply.Status)) { for (uint32_t i = 0; i < 2u; ++i) { if (handles[i] != SYS_HANDLE_INVALID) (void)SysCloseHandle(handles[i]); } errno = PosixStatusToErrno(KSUCCESS(status) ? reply.Status : status); return -1; } NTL_POSIX_SHARED_OPEN_DESCRIPTION* readShared = (NTL_POSIX_SHARED_OPEN_DESCRIPTION*)(uintptr_t)reply.SharedOpenDescription; NTL_POSIX_SHARED_OPEN_DESCRIPTION* writeShared = (NTL_POSIX_SHARED_OPEN_DESCRIPTION*)(uintptr_t)reply.SharedOpenDescription2; SYS_PIPE_SHARED_STATE* pipeShared = readShared ? (SYS_PIPE_SHARED_STATE*)(uintptr_t)readShared->SharedObjectData : (SYS_PIPE_SHARED_STATE*)0; int valid = handles[0] != SYS_HANDLE_INVALID && handles[1] != SYS_HANDLE_INVALID && reply.OpenDescriptionId != 0 && reply.OpenDescriptionId2 != 0 && reply.OpenDescriptionId != reply.OpenDescriptionId2 && reply.OpenDescriptionType == NTL_POSIX_OPEN_TYPE_PIPE_READ && reply.OpenDescriptionType2 == NTL_POSIX_OPEN_TYPE_PIPE_WRITE && reply.OpenStatusFlags == NTL_POSIX_OPEN_STATUS_RDONLY && reply.OpenStatusFlags2 == NTL_POSIX_OPEN_STATUS_WRONLY && readShared && writeShared && readShared->Magic == NTL_POSIX_SHARED_OPEN_MAGIC && writeShared->Magic == NTL_POSIX_SHARED_OPEN_MAGIC && readShared->Version == NTL_POSIX_SHARED_OPEN_VERSION && writeShared->Version == NTL_POSIX_SHARED_OPEN_VERSION && readShared->Type == NTL_POSIX_OPEN_TYPE_PIPE_READ && writeShared->Type == NTL_POSIX_OPEN_TYPE_PIPE_WRITE && readShared->DescriptionId == reply.OpenDescriptionId && writeShared->DescriptionId == reply.OpenDescriptionId2 && readShared->Reserved == 0 && writeShared->Reserved == 0 && readShared->Reserved2 == 0 && writeShared->Reserved2 == 0 && readShared->SharedObjectData != 0 && readShared->SharedObjectData == writeShared->SharedObjectData && pipeShared && pipeShared->Magic == SYS_PIPE_SHARED_MAGIC && pipeShared->Version == SYS_PIPE_SHARED_VERSION && pipeShared->Capacity == SYS_PIPE_BUFFER_CAPACITY && pipeShared->HeaderBytes == SYS_PIPE_SHARED_HEADER_BYTES; if (!valid) { (void)SysCloseHandle(handles[0]); (void)SysCloseHandle(handles[1]); PosixFdReleaseServerDescription(reply.OpenDescriptionId); PosixFdReleaseServerDescription(reply.OpenDescriptionId2); errno = EIO; return -1; } int readFd = PosixFdImport(PosixFdPipeRead, handles[0], reply.OpenDescriptionId, readShared, 0); handles[0] = SYS_HANDLE_INVALID; if (readFd < 0) { (void)SysCloseHandle(handles[1]); PosixFdReleaseServerDescription(reply.OpenDescriptionId2); return -1; } int writeFd = PosixFdImport(PosixFdPipeWrite, handles[1], reply.OpenDescriptionId2, writeShared, 0); handles[1] = SYS_HANDLE_INVALID; if (writeFd < 0) { (void)PosixFdClose(readFd); return -1; } pipefd[0] = readFd; pipefd[1] = writeFd; return 0; } // Deletes one translated native path. __declspec(dllexport) int unlink(const char* path) { if (!path) { errno = EINVAL; return -1; } char native[512]; const char* p = PosixTranslatePath(path, native, sizeof(native)); if (strcmp(p, SYNTH_DEVNULL) == 0 || strcmp(p, SYNTH_DEVZERO) == 0) { errno = EINVAL; return -1; } uint32_t status = NtlDeleteFile(SysSvFromCstr(p)); if (status != NTLSTATUS_SUCCESS) { errno = PosixStatusToErrno(status); return -1; } return 0; } // Repositions the shared file offset owned by an open description. __declspec(dllexport) off_t lseek(int fd, off_t offset, int whence) { POSIX_OPEN_DESCRIPTION* description = PosixFdAcquire(fd); if (!description) return -1; if (description->Type != PosixFdFile) { PosixFdRelease(description); errno = ESPIPE; return -1; } int64_t target = 0; uint32_t status = NTLSTATUS_SUCCESS; if (whence == SEEK_SET) { target = (int64_t)offset; } else if (whence == SEEK_CUR) { uint64_t current = 0; status = SysGetFilePos(description->KHandle, ¤t); if (status == NTLSTATUS_SUCCESS && current <= 0x7FFFFFFFULL) target = (int64_t)current + (int64_t)offset; else if (status == NTLSTATUS_SUCCESS) status = NTLSTATUS_INVALID_PARAMETER; } else if (whence == SEEK_END) { uint64_t size = 0; status = SysGetFileSize(description->KHandle, &size); if (status == NTLSTATUS_SUCCESS && size <= 0x7FFFFFFFULL) target = (int64_t)size + (int64_t)offset; else if (status == NTLSTATUS_SUCCESS) status = NTLSTATUS_INVALID_PARAMETER; } else { status = NTLSTATUS_INVALID_PARAMETER; } if (status == NTLSTATUS_SUCCESS && (target < 0 || target > 0x7FFFFFFFLL)) status = NTLSTATUS_INVALID_PARAMETER; if (status == NTLSTATUS_SUCCESS) status = SysSeekFile(description->KHandle, (uint64_t)target); PosixFdRelease(description); if (status != NTLSTATUS_SUCCESS) { errno = PosixStatusToErrno(status); return -1; } return (off_t)target; }