Files
nt-lite/code/user/personalities/posix/posix_fd.c
T
maksw 73abd6f90d isolate POSIX builds from the host UCRT, wire lwext4 and ntfs-3g as ext4.sys and ntfs.sys, split the FAT driver into directory/file/async/driver/mount units, add posixsrv and dwm plus input/console/desktop syscall clients and a pile of new smoke targets, rename serial.sys to cereal.sys, name HDA/USB/lwIP/AHCI workers, harden ATA probing/reset/removal around actual hardware deadlines, absent-channel detection, diagnostics and explicit worker lifetime, redo ACPI S5 shutdown around GAS/MMIO and HW-reduced firmware, scan SSDTs for _S5 and deal with Intel SMI sleep trapping, make serial and SMP timeouts use real monotonic deadlines, add thread names and fix terminal thread teardown, scheduler and APC prepare/commit races, make advisory file locks queryable and preserve split ranges and add mutable file append state, add shared-memory pipes with kernel-assisted wait/wake, introduce capability-scoped input and display managers, split surface map rights into read/write, add explicit unmap and replace present with commit/generation/damage tracking while keeping physical presentation under the display manager, decouple native exception classes from Win32 exception numbers and translate them in kernel32, add opaque process startup data, bump the resource protocol to v3 with namespace directories, symlinks and enumeration and service control to v2 with generic READY handle exports, add the new POSIX errno/status surface and rebuild fds around posixsrv-owned shared open descriptions, descriptor-local flags, exact inheritance snapshots, dup/fcntl/record locks and shared-ring pipes, add waitpid, POSIX credentials/groups/sessions/passwd/group lookups and move sockets onto stable open descriptions, port the shells and apps to the new console/input services and surface commit API, teach init to load services.ini and start/supervise RSSRV, DWM and generic services under restricted capabilities while publishing their endpoints through RSSRV, split RSSRV into namespace, volume, display, session, input, console and resource components and make /SystemRoot a real symlink to the independently published system volume, shift the syscall table again, expand lock/resource/surface/POSIX/service/thread smoke coverage, make docs scream ASK QUESTIONS and DISCUSS at LLMs, replace MIT No Attribution with WTFPL+.
2026-09-16 10:33:20 +02:00

978 lines
37 KiB
C

// POSIX descriptor table and shared open-description implementation.
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <intrin.h>
#include <ntlite/syscall.h>
#include "posix_internal.h"
#include "posix_fd.h"
// Maps one native status to the POSIX personality errno space.
int PosixStatusToErrno(uint32_t s) {
switch (s) {
case NTLSTATUS_SUCCESS: return 0;
case NTLSTATUS_TIMEOUT: return ETIMEDOUT;
case NTLSTATUS_PENDING: return EINPROGRESS;
case NTLSTATUS_ALERTED:
case NTLSTATUS_KERNEL_APC:
case NTLSTATUS_USER_APC: return EINTR;
case NTLSTATUS_ABANDONED: return EIO;
case NTLSTATUS_FAILURE: return EIO;
case NTLSTATUS_NOT_SUPPORTED: return ENOSYS;
case NTLSTATUS_INVALID_PARAMETER: return EINVAL;
case NTLSTATUS_INVALID_HANDLE: return EBADF;
case NTLSTATUS_BUFFER_TOO_SMALL: return ERANGE;
case NTLSTATUS_CHECKSUM_MISMATCH: return EIO;
case NTLSTATUS_NO_MEMORY: return ENOMEM;
case NTLSTATUS_ACCESS_DENIED: return EACCES;
case NTLSTATUS_NOT_FOUND: return ENOENT;
case NTLSTATUS_ALREADY_EXISTS:
case NTLSTATUS_OBJECT_NAME_COLLISION: return EEXIST;
case NTLSTATUS_OBJECT_DEAD: return ECONNRESET;
case NTLSTATUS_QUEUE_FULL:
case NTLSTATUS_QUEUE_EMPTY:
case NTLSTATUS_WOULD_BLOCK: return EAGAIN;
case NTLSTATUS_CANCELLED: return ECANCELED;
case NTLSTATUS_DEVICE_ERROR: return EIO;
case NTLSTATUS_NO_MORE_FILES: return ENOENT;
case NTLSTATUS_INVALID_IRQL: return EIO;
case NTLSTATUS_DEADLOCK: return EDEADLK;
case NTLSTATUS_DEVICE_REMOVED: return ENODEV;
case NTLSTATUS_ID_EXHAUSTED:
case NTLSTATUS_LIMIT_REACHED: return EAGAIN;
case NTLSTATUS_CAPABILITY_REVOKED: return EBADF;
case NTLSTATUS_PORT_DISCONNECTED: return ECONNRESET;
case NTLSTATUS_BROKEN_PIPE: return EPIPE;
case NTLSTATUS_NOT_A_DIRECTORY: return ENOTDIR;
case NTLSTATUS_IS_A_DIRECTORY: return EISDIR;
case NTLSTATUS_DIRECTORY_NOT_EMPTY: return ENOTEMPTY;
case NTLSTATUS_CROSS_DEVICE: return EXDEV;
case NTLSTATUS_NO_CHILD: return ECHILD;
case NTLSTATUS_INVALID_IMAGE_FORMAT: return ENOEXEC;
case NTLSTATUS_NAME_TOO_LONG: return ENAMETOOLONG;
case NTLSTATUS_TOO_MANY_LINKS: return EMLINK;
case NTLSTATUS_READ_ONLY_FILESYSTEM: return EROFS;
case NTLSTATUS_FILE_TOO_LARGE: return EFBIG;
case NTLSTATUS_PROCESS_KILLED: return EINTR;
case NTLSTATUS_PROCESS_PAGE_FAULT: return EFAULT;
case NTLSTATUS_PROCESS_GENERAL_PROTECTION:
case NTLSTATUS_PROCESS_ILLEGAL_OPCODE:
case NTLSTATUS_PROCESS_DIVIDE_BY_ZERO:
case NTLSTATUS_PROCESS_BREAKPOINT:
case NTLSTATUS_PROCESS_ALIGNMENT_FAULT:
case NTLSTATUS_PROCESS_USER_EXCEPTION:
case NTLSTATUS_PROCESS_UNKNOWN_FAULT: return EIO;
default: return EIO;
}
}
// Locks the process descriptor table.
static int PosixFdLock(POSIX_PROCESS_STATE* process) {
if (!process || process->FdLock == SYS_HANDLE_INVALID) {
errno = EIO;
return -1;
}
uint32_t status = SysWait(process->FdLock, SYS_WAIT_FLAG_NONE);
if (status != NTLSTATUS_SUCCESS) {
errno = PosixStatusToErrno(status);
return -1;
}
return 0;
}
// Unlocks the process descriptor table.
static void PosixFdUnlock(POSIX_PROCESS_STATE* process) {
uint32_t status = SysReleaseMutex(process->FdLock);
if (status != NTLSTATUS_SUCCESS)
errno = PosixStatusToErrno(status);
}
// Returns whether one open-description type requires a retained native capability.
static int PosixOpenDescriptionNeedsHandle(POSIX_FD_TYPE type) {
return type == PosixFdFile || type == PosixFdSocket ||
type == PosixFdPipeRead || type == PosixFdPipeWrite;
}
// Derives the rights retained by posixsrv's master capability for one description.
static SYS_RIGHTS PosixOpenDescriptionMasterRights(POSIX_FD_TYPE type, int statusFlags) {
SYS_RIGHTS rights = SYS_RIGHT_TRANSFER | SYS_RIGHT_QUERY;
if (type == PosixFdFile) {
rights |= SYS_FILE_RIGHT_LOCK;
int access = statusFlags & O_ACCMODE;
if (access == O_RDONLY || access == O_RDWR)
rights |= SYS_FILE_RIGHT_READ_DATA | SYS_FILE_RIGHT_MAP_READ;
if (access == O_WRONLY || access == O_RDWR)
rights |= SYS_FILE_RIGHT_WRITE_DATA | SYS_FILE_RIGHT_MAP_WRITE;
} else if (type == PosixFdSocket) {
rights |= SYS_SOCKET_RIGHT_BIND | SYS_SOCKET_RIGHT_CONNECT | SYS_SOCKET_RIGHT_LISTEN |
SYS_SOCKET_RIGHT_ACCEPT | SYS_SOCKET_RIGHT_SEND | SYS_SOCKET_RIGHT_RECEIVE |
SYS_SOCKET_RIGHT_SHUTDOWN | SYS_SOCKET_RIGHT_POLL |
SYS_SOCKET_RIGHT_SET_OPTION;
} else if (type == PosixFdPipeRead) {
rights |= SYS_RIGHT_WAIT | SYS_PIPE_RIGHT_READ | SYS_PIPE_RIGHT_POLL;
} else if (type == PosixFdPipeWrite) {
rights |= SYS_RIGHT_WAIT | SYS_PIPE_RIGHT_WRITE | SYS_PIPE_RIGHT_POLL;
}
return rights;
}
// Best-effort release of one server-owned open description after local teardown/failure.
void PosixFdReleaseServerDescription(uint64_t descriptionId) {
if (descriptionId == 0) return;
NTL_POSIX_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Version = NTL_POSIX_PROTOCOL_VERSION;
request.Operation = NTL_POSIX_OP_RELEASE_OPEN_DESCRIPTION;
request.Flags = NTL_POSIX_REQUEST_FLAG_NONE;
request.OpenDescriptionId = descriptionId;
NTL_POSIX_REPLY reply;
(void)PosixRuntimeCallServer(&request, &reply);
}
// Captures process-local descriptor numbers/flags while posixsrv pins unique descriptions.
KSTATUS PosixFdCreateInheritanceSnapshot(uint32_t flags, uint64_t* tokenOut) {
if (!tokenOut || (flags & ~POSIX_FD_INHERIT_SKIP_CLOEXEC) != 0)
return NTLSTATUS_INVALID_PARAMETER;
*tokenOut = 0;
POSIX_PROCESS_STATE* process = PosixRuntimeProcessState();
if (!process) return NTLSTATUS_NOT_FOUND;
if (PosixFdLock(process) != 0) return NTLSTATUS_FAILURE;
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_FD_INHERITANCE;
request.Flags = NTL_POSIX_REQUEST_FLAG_NONE;
request.FdInheritanceFlags = NTL_POSIX_FD_INHERIT_FLAG_NONE;
for (int fd = 0; fd < POSIX_FD_MAX; ++fd) {
POSIX_OPEN_DESCRIPTION* description = process->FdTable[fd].Description;
if (!description) continue;
int descriptorFlags = process->FdTable[fd].DescriptorFlags & FD_CLOEXEC;
if ((flags & POSIX_FD_INHERIT_SKIP_CLOEXEC) && (descriptorFlags & FD_CLOEXEC))
continue;
uint32_t index = request.FdInheritanceCount++;
request.FdManifest[index].Descriptor = fd;
request.FdManifest[index].DescriptorFlags = (uint32_t)descriptorFlags;
request.FdManifest[index].OpenDescriptionId = description->DescriptionId;
}
NTL_POSIX_REPLY reply;
KSTATUS status = PosixRuntimeCallServer(&request, &reply);
PosixFdUnlock(process);
if (!KSUCCESS(status)) return status;
if (!KSUCCESS(reply.Status)) return reply.Status;
if (reply.FdInheritanceToken == 0 ||
reply.FdInheritanceCount != request.FdInheritanceCount) {
if (reply.FdInheritanceToken != 0)
PosixFdCancelInheritanceSnapshot(reply.FdInheritanceToken);
return NTLSTATUS_FAILURE;
}
*tokenOut = reply.FdInheritanceToken;
return NTLSTATUS_SUCCESS;
}
// Arms one snapshot to a concrete native child lifetime before the parent returns from spawn.
KSTATUS PosixFdArmInheritanceSnapshot(uint64_t token, SYS_HANDLE processHandle) {
if (token == 0 || processHandle == SYS_HANDLE_INVALID) return NTLSTATUS_INVALID_PARAMETER;
NTL_POSIX_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Version = NTL_POSIX_PROTOCOL_VERSION;
request.Operation = NTL_POSIX_OP_ARM_FD_INHERITANCE;
request.Flags = NTL_POSIX_REQUEST_FLAG_NONE;
request.FdInheritanceToken = token;
SYS_PORT_HANDLE_TRANSFER transfer;
memset(&transfer, 0, sizeof(transfer));
transfer.Handle = processHandle;
transfer.DesiredRights = SYS_RIGHT_QUERY | SYS_RIGHT_WAIT;
transfer.Flags = SYS_PORT_TRANSFER_FLAG_NONE;
NTL_POSIX_REPLY reply;
KSTATUS status = PosixRuntimeCallServerWithHandles(&request, &transfer, 1u, &reply);
if (!KSUCCESS(status)) return status;
return reply.Status;
}
// Cancels one snapshot when native process construction failed before a child could claim it.
void PosixFdCancelInheritanceSnapshot(uint64_t token) {
if (token == 0) return;
NTL_POSIX_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Version = NTL_POSIX_PROTOCOL_VERSION;
request.Operation = NTL_POSIX_OP_CANCEL_FD_INHERITANCE;
request.Flags = NTL_POSIX_REQUEST_FLAG_NONE;
request.FdInheritanceToken = token;
NTL_POSIX_REPLY reply;
(void)PosixRuntimeCallServer(&request, &reply);
}
// Allocates one local wrapper backed by one posixsrv-owned shared open description.
static POSIX_OPEN_DESCRIPTION* PosixOpenDescriptionCreate(POSIX_FD_TYPE type,
uint32_t khandle,
int statusFlags) {
if ((PosixOpenDescriptionNeedsHandle(type) && khandle == SYS_HANDLE_INVALID) ||
(!PosixOpenDescriptionNeedsHandle(type) && khandle != SYS_HANDLE_INVALID)) {
errno = EINVAL;
return (POSIX_OPEN_DESCRIPTION*)0;
}
POSIX_OPEN_DESCRIPTION* description =
(POSIX_OPEN_DESCRIPTION*)calloc(1, sizeof(*description));
if (!description) {
errno = ENOMEM;
return (POSIX_OPEN_DESCRIPTION*)0;
}
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_OPEN_DESCRIPTION;
request.Flags = NTL_POSIX_REQUEST_FLAG_NONE;
request.OpenDescriptionType = (uint32_t)type;
request.OpenStatusFlags = (uint32_t)statusFlags;
SYS_PORT_HANDLE_TRANSFER transfer;
memset(&transfer, 0, sizeof(transfer));
const SYS_PORT_HANDLE_TRANSFER* transfers = (const SYS_PORT_HANDLE_TRANSFER*)0;
uint32_t transferCount = 0;
if (PosixOpenDescriptionNeedsHandle(type)) {
transfer.Handle = khandle;
transfer.DesiredRights = PosixOpenDescriptionMasterRights(type, statusFlags);
transfer.Flags = SYS_PORT_TRANSFER_FLAG_NONE;
transfers = &transfer;
transferCount = 1;
}
NTL_POSIX_REPLY reply;
KSTATUS status = PosixRuntimeCallServerWithHandles(&request, transfers, transferCount, &reply);
if (!KSUCCESS(status) || !KSUCCESS(reply.Status)) {
errno = PosixStatusToErrno(KSUCCESS(status) ? reply.Status : status);
free(description);
return (POSIX_OPEN_DESCRIPTION*)0;
}
NTL_POSIX_SHARED_OPEN_DESCRIPTION* shared =
(NTL_POSIX_SHARED_OPEN_DESCRIPTION*)(uintptr_t)reply.SharedOpenDescription;
if (!shared || reply.OpenDescriptionId == 0 || reply.OpenDescriptionType != (uint32_t)type ||
shared->Magic != NTL_POSIX_SHARED_OPEN_MAGIC ||
shared->Version != NTL_POSIX_SHARED_OPEN_VERSION || shared->Reserved != 0 ||
shared->Reserved2 != 0 || shared->SharedObjectData != 0 ||
shared->DescriptionId != reply.OpenDescriptionId ||
shared->Type != (uint32_t)type) {
PosixFdReleaseServerDescription(reply.OpenDescriptionId);
errno = EIO;
free(description);
return (POSIX_OPEN_DESCRIPTION*)0;
}
description->RefCount = 1;
description->Type = type;
description->KHandle = khandle;
description->DescriptionId = reply.OpenDescriptionId;
description->Shared = shared;
return description;
}
// Adds one reference to an open description.
static void PosixFdReference(POSIX_OPEN_DESCRIPTION* description) {
if (description)
_InterlockedIncrement(&description->RefCount);
}
// Returns one atomic snapshot of server-owned shared open-description status flags.
int PosixFdGetStatusFlags(const POSIX_OPEN_DESCRIPTION* description) {
if (!description || !description->Shared)
return 0;
return (int)_InterlockedCompareExchange(
(volatile long*)&description->Shared->StatusFlags, 0, 0);
}
// Applies mutable status flags through posixsrv so native backing and shared state stay serialized.
int PosixFdSetStatusFlags(POSIX_OPEN_DESCRIPTION* description, int statusFlags) {
if (!description || !description->Shared || description->DescriptionId == 0) {
errno = EBADF;
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_SET_OPEN_STATUS;
request.Flags = NTL_POSIX_REQUEST_FLAG_NONE;
request.OpenDescriptionId = description->DescriptionId;
request.OpenStatusFlags = (uint32_t)statusFlags;
NTL_POSIX_REPLY reply;
KSTATUS status = PosixRuntimeCallServer(&request, &reply);
if (!KSUCCESS(status) || !KSUCCESS(reply.Status)) {
errno = PosixStatusToErrno(KSUCCESS(status) ? reply.Status : status);
return -1;
}
if (reply.OpenDescriptionId != description->DescriptionId ||
reply.OpenDescriptionType != (uint32_t)description->Type) {
errno = EIO;
return -1;
}
return 0;
}
// Releases one stable local wrapper and its server holder at the final reference.
void PosixFdRelease(POSIX_OPEN_DESCRIPTION* description) {
if (!description)
return;
if (_InterlockedDecrement(&description->RefCount) != 0)
return;
if (PosixOpenDescriptionNeedsHandle(description->Type) &&
description->KHandle != SYS_HANDLE_INVALID)
(void)SysCloseHandle(description->KHandle);
PosixFdReleaseServerDescription(description->DescriptionId);
free(description);
}
// Installs a description at the lowest free descriptor at or above minFd.
static int PosixFdInstall(POSIX_PROCESS_STATE* process,
POSIX_OPEN_DESCRIPTION* description,
int minFd,
int descriptorFlags) {
if (!process || !description || minFd < 0 || minFd >= POSIX_FD_MAX) {
errno = EINVAL;
return -1;
}
if (PosixFdLock(process) != 0)
return -1;
int fd = -1;
for (int i = minFd; i < POSIX_FD_MAX; i++) {
if (!process->FdTable[i].Description) {
PosixFdReference(description);
process->FdTable[i].Description = description;
process->FdTable[i].DescriptorFlags = descriptorFlags & FD_CLOEXEC;
fd = i;
break;
}
}
PosixFdUnlock(process);
if (fd < 0)
errno = EMFILE;
return fd;
}
// Creates and installs one new open description.
int PosixFdCreate(POSIX_FD_TYPE type, uint32_t khandle, int statusFlags) {
POSIX_PROCESS_STATE* process = PosixRuntimeProcessState();
if (!process) {
errno = EIO;
return -1;
}
POSIX_OPEN_DESCRIPTION* description =
PosixOpenDescriptionCreate(type, khandle, statusFlags);
if (!description)
return -1;
int fd = PosixFdInstall(process, description, 3, 0);
PosixFdRelease(description);
return fd;
}
// Adopts one server-owned description into a process-local wrapper.
static POSIX_OPEN_DESCRIPTION* PosixOpenDescriptionImport(POSIX_FD_TYPE type,
uint32_t khandle,
uint64_t descriptionId,
NTL_POSIX_SHARED_OPEN_DESCRIPTION* shared) {
int needsHandle = PosixOpenDescriptionNeedsHandle(type);
int isPipe = type == PosixFdPipeRead || type == PosixFdPipeWrite;
if (descriptionId == 0 || !shared ||
(needsHandle && khandle == SYS_HANDLE_INVALID) ||
(!needsHandle && khandle != SYS_HANDLE_INVALID) ||
shared->Magic != NTL_POSIX_SHARED_OPEN_MAGIC ||
shared->Version != NTL_POSIX_SHARED_OPEN_VERSION || shared->Reserved != 0 ||
shared->Reserved2 != 0 || shared->DescriptionId != descriptionId ||
shared->Type != (uint32_t)type ||
(isPipe && shared->SharedObjectData == 0) ||
(!isPipe && shared->SharedObjectData != 0)) {
if (needsHandle && khandle != SYS_HANDLE_INVALID) (void)SysCloseHandle(khandle);
PosixFdReleaseServerDescription(descriptionId);
errno = EIO;
return (POSIX_OPEN_DESCRIPTION*)0;
}
POSIX_OPEN_DESCRIPTION* description =
(POSIX_OPEN_DESCRIPTION*)calloc(1, sizeof(*description));
if (!description) {
if (needsHandle) (void)SysCloseHandle(khandle);
PosixFdReleaseServerDescription(descriptionId);
errno = ENOMEM;
return (POSIX_OPEN_DESCRIPTION*)0;
}
description->RefCount = 1;
description->Type = type;
description->KHandle = khandle;
description->DescriptionId = descriptionId;
description->Shared = shared;
return description;
}
// Installs one process-local wrapper for a server-created shared open description.
int PosixFdImport(POSIX_FD_TYPE type, uint32_t khandle, uint64_t descriptionId,
NTL_POSIX_SHARED_OPEN_DESCRIPTION* shared, int descriptorFlags) {
POSIX_PROCESS_STATE* process = PosixRuntimeProcessState();
if (!process) { errno = EIO; return -1; }
POSIX_OPEN_DESCRIPTION* description =
PosixOpenDescriptionImport(type, khandle, descriptionId, shared);
if (!description) return -1;
int fd = PosixFdInstall(process, description, 3, descriptorFlags);
PosixFdRelease(description);
return fd;
}
// Acquires a stable reference to the open description backing fd.
POSIX_OPEN_DESCRIPTION* PosixFdAcquire(int fd) {
POSIX_PROCESS_STATE* process = PosixRuntimeProcessState();
if (!process || fd < 0 || fd >= POSIX_FD_MAX) {
errno = EBADF;
return (POSIX_OPEN_DESCRIPTION*)0;
}
if (PosixFdLock(process) != 0)
return (POSIX_OPEN_DESCRIPTION*)0;
POSIX_OPEN_DESCRIPTION* description = process->FdTable[fd].Description;
if (description)
PosixFdReference(description);
PosixFdUnlock(process);
if (!description)
errno = EBADF;
return description;
}
// Removes one descriptor and releases its table-owned reference.
int PosixFdClose(int fd) {
POSIX_PROCESS_STATE* process = PosixRuntimeProcessState();
if (!process || fd < 0 || fd >= POSIX_FD_MAX) {
errno = EBADF;
return -1;
}
if (PosixFdLock(process) != 0)
return -1;
POSIX_OPEN_DESCRIPTION* description = process->FdTable[fd].Description;
if (!description) {
PosixFdUnlock(process);
errno = EBADF;
return -1;
}
process->FdTable[fd].Description = (POSIX_OPEN_DESCRIPTION*)0;
process->FdTable[fd].DescriptorFlags = 0;
PosixFdUnlock(process);
PosixFdRelease(description);
return 0;
}
// Installs one fixed standard descriptor during process initialization.
static uint32_t PosixFdInstallStandard(POSIX_PROCESS_STATE* process,
int fd,
POSIX_FD_TYPE type,
int statusFlags) {
POSIX_OPEN_DESCRIPTION* description =
PosixOpenDescriptionCreate(type, SYS_HANDLE_INVALID, statusFlags);
if (!description)
return NTLSTATUS_NO_MEMORY;
PosixFdReference(description);
process->FdTable[fd].Description = description;
process->FdTable[fd].DescriptorFlags = 0;
PosixFdRelease(description);
return NTLSTATUS_SUCCESS;
}
// Reconstructs one exact descriptor table from a server-owned inheritance snapshot.
static uint32_t PosixFdImportInheritance(POSIX_PROCESS_STATE* process, uint64_t token) {
if (!process || token == 0) return NTLSTATUS_INVALID_PARAMETER;
NTL_POSIX_FD_MANIFEST_ENTRY manifest[NTL_POSIX_FD_INHERIT_MAX];
memset(manifest, 0, sizeof(manifest));
uint32_t manifestCount = UINT32_MAX;
POSIX_OPEN_DESCRIPTION* descriptions[NTL_POSIX_FD_INHERIT_MAX];
uint64_t descriptionIds[NTL_POSIX_FD_INHERIT_MAX];
memset(descriptions, 0, sizeof(descriptions));
memset(descriptionIds, 0, sizeof(descriptionIds));
uint32_t descriptionCount = 0;
uint32_t descriptionIndex = 0;
uint32_t descriptionTotal = UINT32_MAX;
uint32_t result = NTLSTATUS_SUCCESS;
for (;;) {
NTL_POSIX_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Version = NTL_POSIX_PROTOCOL_VERSION;
request.Operation = NTL_POSIX_OP_IMPORT_FD_INHERITANCE;
request.Flags = NTL_POSIX_REQUEST_FLAG_NONE;
request.FdInheritanceToken = token;
request.FdInheritanceIndex = descriptionIndex;
SYS_HANDLE handles[NTL_POSIX_FD_IMPORT_BATCH_MAX];
for (uint32_t i = 0; i < NTL_POSIX_FD_IMPORT_BATCH_MAX; ++i)
handles[i] = SYS_HANDLE_INVALID;
uint32_t handleCount = 0;
NTL_POSIX_REPLY reply;
KSTATUS status = PosixRuntimeCallServerExchangeVariable(
&request, handles, NTL_POSIX_FD_IMPORT_BATCH_MAX, &handleCount, &reply);
if (!KSUCCESS(status) || !KSUCCESS(reply.Status)) {
result = KSUCCESS(status) ? reply.Status : status;
for (uint32_t i = 0; i < handleCount; ++i)
if (handles[i] != SYS_HANDLE_INVALID) (void)SysCloseHandle(handles[i]);
break;
}
if (reply.FdInheritanceToken != token || reply.FdInheritanceIndex != descriptionIndex ||
reply.FdInheritanceCount > NTL_POSIX_FD_INHERIT_MAX ||
reply.FdDescriptionIndex != descriptionIndex ||
reply.FdDescriptionCount > NTL_POSIX_FD_IMPORT_BATCH_MAX ||
reply.FdDescriptionTotal > NTL_POSIX_FD_INHERIT_MAX ||
descriptionIndex + reply.FdDescriptionCount > reply.FdDescriptionTotal) {
result = NTLSTATUS_FAILURE;
for (uint32_t i = 0; i < handleCount; ++i)
if (handles[i] != SYS_HANDLE_INVALID) (void)SysCloseHandle(handles[i]);
break;
}
if (manifestCount == UINT32_MAX) {
manifestCount = reply.FdInheritanceCount;
memcpy(manifest, reply.FdManifest, sizeof(manifest));
descriptionTotal = reply.FdDescriptionTotal;
} else if (manifestCount != reply.FdInheritanceCount ||
descriptionTotal != reply.FdDescriptionTotal ||
memcmp(manifest, reply.FdManifest, sizeof(manifest)) != 0) {
result = NTLSTATUS_FAILURE;
for (uint32_t i = 0; i < handleCount; ++i)
if (handles[i] != SYS_HANDLE_INVALID) (void)SysCloseHandle(handles[i]);
break;
}
uint8_t handleUsed[NTL_POSIX_FD_IMPORT_BATCH_MAX];
memset(handleUsed, 0, sizeof(handleUsed));
for (uint32_t i = 0; i < reply.FdDescriptionCount; ++i) {
NTL_POSIX_FD_DESCRIPTION_ENTRY* entry = &reply.FdDescriptions[i];
if (entry->Reserved != 0 || entry->OpenDescriptionId == 0 ||
entry->OpenDescriptionType < NTL_POSIX_OPEN_TYPE_FILE ||
entry->OpenDescriptionType > NTL_POSIX_OPEN_TYPE_PIPE_WRITE ||
descriptionCount >= NTL_POSIX_FD_INHERIT_MAX) {
result = NTLSTATUS_FAILURE;
break;
}
for (uint32_t j = 0; j < descriptionCount; ++j) {
if (descriptionIds[j] == entry->OpenDescriptionId) { result = NTLSTATUS_FAILURE; break; }
}
if (!KSUCCESS(result)) break;
POSIX_FD_TYPE type = (POSIX_FD_TYPE)entry->OpenDescriptionType;
int needsHandle = PosixOpenDescriptionNeedsHandle(type);
SYS_HANDLE handle = SYS_HANDLE_INVALID;
if (needsHandle) {
if (entry->HandleIndex >= handleCount || handleUsed[entry->HandleIndex]) {
result = NTLSTATUS_FAILURE;
break;
}
handleUsed[entry->HandleIndex] = 1u;
handle = handles[entry->HandleIndex];
handles[entry->HandleIndex] = SYS_HANDLE_INVALID;
} else if (entry->HandleIndex != NTL_POSIX_FD_HANDLE_NONE) {
result = NTLSTATUS_FAILURE;
break;
}
POSIX_OPEN_DESCRIPTION* description = PosixOpenDescriptionImport(
type, handle, entry->OpenDescriptionId,
(NTL_POSIX_SHARED_OPEN_DESCRIPTION*)(uintptr_t)entry->SharedOpenDescription);
if (!description) { result = NTLSTATUS_FAILURE; break; }
descriptions[descriptionCount] = description;
descriptionIds[descriptionCount] = entry->OpenDescriptionId;
++descriptionCount;
}
for (uint32_t i = 0; i < handleCount; ++i) {
if (handles[i] != SYS_HANDLE_INVALID) {
(void)SysCloseHandle(handles[i]);
if (KSUCCESS(result)) result = NTLSTATUS_FAILURE;
}
}
if (!KSUCCESS(result)) break;
descriptionIndex += reply.FdDescriptionCount;
if (descriptionIndex == descriptionTotal) break;
if (reply.FdDescriptionCount == 0) { result = NTLSTATUS_FAILURE; break; }
}
if (KSUCCESS(result)) {
if (manifestCount == UINT32_MAX || descriptionTotal == UINT32_MAX ||
descriptionCount != descriptionTotal)
result = NTLSTATUS_FAILURE;
}
if (KSUCCESS(result)) {
uint8_t fdUsed[POSIX_FD_MAX];
memset(fdUsed, 0, sizeof(fdUsed));
for (uint32_t i = 0; i < manifestCount; ++i) {
NTL_POSIX_FD_MANIFEST_ENTRY* entry = &manifest[i];
if (entry->Descriptor < 0 || entry->Descriptor >= POSIX_FD_MAX ||
fdUsed[entry->Descriptor] ||
(entry->DescriptorFlags & ~NTL_POSIX_FD_DESCRIPTOR_CLOEXEC) != 0) {
result = NTLSTATUS_FAILURE;
break;
}
POSIX_OPEN_DESCRIPTION* description = (POSIX_OPEN_DESCRIPTION*)0;
for (uint32_t j = 0; j < descriptionCount; ++j) {
if (descriptionIds[j] == entry->OpenDescriptionId) {
description = descriptions[j];
break;
}
}
if (!description) { result = NTLSTATUS_FAILURE; break; }
fdUsed[entry->Descriptor] = 1u;
PosixFdReference(description);
process->FdTable[entry->Descriptor].Description = description;
process->FdTable[entry->Descriptor].DescriptorFlags =
(int)(entry->DescriptorFlags & NTL_POSIX_FD_DESCRIPTOR_CLOEXEC);
}
}
for (uint32_t i = 0; i < descriptionCount; ++i)
PosixFdRelease(descriptions[i]);
return result;
}
// Initializes the process descriptor table and standard descriptors.
uint32_t PosixFdInitializeProcess(POSIX_PROCESS_STATE* process, uint64_t inheritanceToken) {
if (!process)
return NTLSTATUS_INVALID_PARAMETER;
uint32_t lock = SYS_HANDLE_INVALID;
uint32_t status = SysCreateMutex(&lock);
if (status != NTLSTATUS_SUCCESS)
return status;
process->FdLock = lock;
memset(process->FdTable, 0, sizeof(process->FdTable));
if (inheritanceToken != 0) {
status = PosixFdImportInheritance(process, inheritanceToken);
if (status != NTLSTATUS_SUCCESS) PosixFdDestroyProcess(process);
return status;
}
status = PosixFdInstallStandard(process, 0, PosixFdConsoleRead, O_RDONLY);
if (status == NTLSTATUS_SUCCESS)
status = PosixFdInstallStandard(process, 1, PosixFdConsoleWrite, O_WRONLY);
if (status == NTLSTATUS_SUCCESS)
status = PosixFdInstallStandard(process, 2, PosixFdConsoleWrite, O_WRONLY);
if (status != NTLSTATUS_SUCCESS)
PosixFdDestroyProcess(process);
return status;
}
// Releases descriptor-table resources during failed process startup.
void PosixFdDestroyProcess(POSIX_PROCESS_STATE* process) {
if (!process)
return;
for (int fd = 0; fd < POSIX_FD_MAX; fd++) {
POSIX_OPEN_DESCRIPTION* description = process->FdTable[fd].Description;
process->FdTable[fd].Description = (POSIX_OPEN_DESCRIPTION*)0;
process->FdTable[fd].DescriptorFlags = 0;
PosixFdRelease(description);
}
if (process->FdLock != SYS_HANDLE_INVALID) {
SysCloseHandle(process->FdLock);
process->FdLock = SYS_HANDLE_INVALID;
}
}
// Duplicates oldfd into the lowest free descriptor at or above minFd.
static int PosixFdDuplicateFrom(int oldfd, int minFd) {
if (minFd < 0 || minFd >= POSIX_FD_MAX) {
errno = EINVAL;
return -1;
}
POSIX_OPEN_DESCRIPTION* description = PosixFdAcquire(oldfd);
if (!description)
return -1;
int result = PosixFdInstall(PosixRuntimeProcessState(), description, minFd, 0);
PosixFdRelease(description);
return result;
}
// Duplicates one descriptor to the lowest available descriptor.
NTL_POSIX_API int dup(int oldfd) {
return PosixFdDuplicateFrom(oldfd, 0);
}
// Duplicates oldfd onto newfd, atomically replacing newfd when necessary.
NTL_POSIX_API int dup2(int oldfd, int newfd) {
if (newfd < 0 || newfd >= POSIX_FD_MAX) {
errno = EBADF;
return -1;
}
POSIX_OPEN_DESCRIPTION* description = PosixFdAcquire(oldfd);
if (!description)
return -1;
if (oldfd == newfd) {
PosixFdRelease(description);
return newfd;
}
POSIX_PROCESS_STATE* process = PosixRuntimeProcessState();
if (PosixFdLock(process) != 0) {
PosixFdRelease(description);
return -1;
}
POSIX_OPEN_DESCRIPTION* replaced = process->FdTable[newfd].Description;
PosixFdReference(description);
process->FdTable[newfd].Description = description;
process->FdTable[newfd].DescriptorFlags = 0;
PosixFdUnlock(process);
PosixFdRelease(replaced);
PosixFdRelease(description);
return newfd;
}
// Resolves one flock range to the native absolute byte-range form.
static int PosixFlockRange(POSIX_OPEN_DESCRIPTION* description,
const struct flock* lock,
uint64_t* offsetOut,
uint64_t* lengthOut) {
if (!description || !lock || !offsetOut || !lengthOut) {
errno = EINVAL;
return -1;
}
if (description->Type != PosixFdFile) {
errno = EBADF;
return -1;
}
int64_t base = 0;
uint32_t status = NTLSTATUS_SUCCESS;
if (lock->l_whence == SEEK_CUR) {
uint64_t current = 0;
status = SysGetFilePos(description->KHandle, &current);
if (status == NTLSTATUS_SUCCESS) {
if (current > 0x7FFFFFFFULL) status = NTLSTATUS_INVALID_PARAMETER;
else base = (int64_t)current;
}
} else if (lock->l_whence == SEEK_END) {
uint64_t size = 0;
status = SysGetFileSize(description->KHandle, &size);
if (status == NTLSTATUS_SUCCESS) {
if (size > 0x7FFFFFFFULL) status = NTLSTATUS_INVALID_PARAMETER;
else base = (int64_t)size;
}
} else if (lock->l_whence != SEEK_SET) {
status = NTLSTATUS_INVALID_PARAMETER;
}
if (status != NTLSTATUS_SUCCESS) {
errno = PosixStatusToErrno(status);
return -1;
}
int64_t start = base + (int64_t)lock->l_start;
int64_t length = (int64_t)lock->l_len;
if (length < 0) {
start += length;
length = -length;
}
if (start < 0 || start > 0x7FFFFFFFLL) {
errno = EINVAL;
return -1;
}
*offsetOut = (uint64_t)start;
*lengthOut = length == 0 ? SYS_FILE_LOCK_TO_EOF : (uint64_t)length;
return 0;
}
// Implements F_GETLK/F_SETLK/F_SETLKW on the native process-owned lock table.
static int PosixFdControlLock(POSIX_OPEN_DESCRIPTION* description, int command, struct flock* lock) {
if (!description || !lock) {
errno = EINVAL;
return -1;
}
if (description->Type != PosixFdFile) {
errno = EBADF;
return -1;
}
uint64_t offset = 0;
uint64_t length = 0;
if (PosixFlockRange(description, lock, &offset, &length) != 0)
return -1;
if (command == F_GETLK) {
if (lock->l_type != F_RDLCK && lock->l_type != F_WRLCK) {
errno = EINVAL;
return -1;
}
SYS_FILE_LOCK_INFO info;
memset(&info, 0, sizeof(info));
SYS_QUERY_FILE_LOCK_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.FileHandle = description->KHandle;
request.Mode = lock->l_type == F_RDLCK ? SYS_FILE_LOCK_SHARED : SYS_FILE_LOCK_EXCLUSIVE;
request.Flags = SYS_FILE_LOCK_FLAG_NONE;
request.Offset = offset;
request.Length = length;
request.InfoOut = &info;
uint32_t status = SysQueryFileLock(&request);
if (status != NTLSTATUS_SUCCESS) {
errno = PosixStatusToErrno(status);
return -1;
}
if (info.Mode == SYS_FILE_LOCK_NONE) {
lock->l_type = F_UNLCK;
lock->l_pid = 0;
return 0;
}
lock->l_type = info.Mode == SYS_FILE_LOCK_SHARED ? F_RDLCK : F_WRLCK;
lock->l_whence = SEEK_SET;
lock->l_start = (off_t)info.Offset;
lock->l_len = info.Length == SYS_FILE_LOCK_TO_EOF ? 0 : (off_t)info.Length;
lock->l_pid = (pid_t)info.OwnerProcessId;
return 0;
}
if (lock->l_type == F_UNLCK) {
SYS_UNLOCK_FILE_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.FileHandle = description->KHandle;
request.Flags = SYS_FILE_LOCK_FLAG_NONE;
request.Offset = offset;
request.Length = length;
uint32_t status = SysUnlockFile(&request);
if (status == NTLSTATUS_SUCCESS || status == NTLSTATUS_NOT_FOUND)
return 0;
errno = PosixStatusToErrno(status);
return -1;
}
int statusFlags = PosixFdGetStatusFlags(description);
uint32_t mode = 0;
if (lock->l_type == F_RDLCK) {
if ((statusFlags & O_ACCMODE) == O_WRONLY) {
errno = EBADF;
return -1;
}
mode = SYS_FILE_LOCK_SHARED;
} else if (lock->l_type == F_WRLCK) {
if ((statusFlags & O_ACCMODE) == O_RDONLY) {
errno = EBADF;
return -1;
}
mode = SYS_FILE_LOCK_EXCLUSIVE;
} else {
errno = EINVAL;
return -1;
}
SYS_LOCK_FILE_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.FileHandle = description->KHandle;
request.Mode = mode;
request.Flags = SYS_FILE_LOCK_FLAG_NONE;
request.Offset = offset;
request.Length = length;
request.TimeoutMilliseconds = command == F_SETLKW ? SYS_WAIT_TIMEOUT_INFINITE : 0;
uint32_t status = SysLockFile(&request);
if (status != NTLSTATUS_SUCCESS) {
errno = PosixStatusToErrno(status);
return -1;
}
return 0;
}
// Implements descriptor and open-description file-control operations.
NTL_POSIX_API int fcntl(int fd, int command, ...) {
POSIX_PROCESS_STATE* process = PosixRuntimeProcessState();
if (!process || fd < 0 || fd >= POSIX_FD_MAX) {
errno = EBADF;
return -1;
}
va_list args;
va_start(args, command);
if (command == F_DUPFD) {
int minFd = va_arg(args, int);
va_end(args);
return PosixFdDuplicateFrom(fd, minFd);
}
if (command == F_GETFL) {
va_end(args);
POSIX_OPEN_DESCRIPTION* description = PosixFdAcquire(fd);
if (!description)
return -1;
int flags = PosixFdGetStatusFlags(description);
PosixFdRelease(description);
return flags;
}
if (command == F_SETFL) {
int flags = va_arg(args, int);
va_end(args);
POSIX_OPEN_DESCRIPTION* description = PosixFdAcquire(fd);
if (!description)
return -1;
int result = PosixFdSetStatusFlags(description, flags);
PosixFdRelease(description);
return result;
}
if (command == F_GETLK || command == F_SETLK || command == F_SETLKW) {
struct flock* lock = va_arg(args, struct flock*);
va_end(args);
POSIX_OPEN_DESCRIPTION* description = PosixFdAcquire(fd);
if (!description)
return -1;
int result = PosixFdControlLock(description, command, lock);
PosixFdRelease(description);
return result;
}
if (PosixFdLock(process) != 0) {
va_end(args);
return -1;
}
if (!process->FdTable[fd].Description) {
PosixFdUnlock(process);
va_end(args);
errno = EBADF;
return -1;
}
int result = -1;
if (command == F_GETFD) {
result = process->FdTable[fd].DescriptorFlags;
} else if (command == F_SETFD) {
int flags = va_arg(args, int);
process->FdTable[fd].DescriptorFlags = flags & FD_CLOEXEC;
result = 0;
} else {
errno = EINVAL;
}
PosixFdUnlock(process);
va_end(args);
return result;
}