Files
nt-lite/code/user/personalities/posix/posix_fd.c
T

975 lines
37 KiB
C

// POSIX descriptor table and shared open-description implementation.
#include <ntlite/atomic.h>
#include <stdbool.h>
#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(KSTATUS 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 one logical process descriptor table.
static int PosixFdLock(POSIX_LOGICAL_PROCESS_STATE* process) {
if (!process || process->FdLock == SYS_HANDLE_INVALID) {
errno = EIO;
return -1;
}
KSTATUS status = SysWait(process->FdLock, SYS_WAIT_FLAG_NONE);
if (status != NTLSTATUS_SUCCESS) {
errno = PosixStatusToErrno(status);
return -1;
}
return 0;
}
// Unlocks one logical process descriptor table.
static void PosixFdUnlock(POSIX_LOGICAL_PROCESS_STATE* process) {
KSTATUS status = SysReleaseMutex(process->FdLock);
if (status != NTLSTATUS_SUCCESS)
errno = PosixStatusToErrno(status);
}
// Returns whether one open-description type requires a retained native capability.
static bool 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 logical-process 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_LOGICAL_PROCESS_STATE* process = PosixRuntimeLogicalProcessState();
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;
}
// 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)
NtlAtomicIncrement32(&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)NtlAtomicLoadU32(&description->Shared->StatusFlags);
}
// 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 without changing server holder ownership.
static void PosixFdReleaseLocal(POSIX_OPEN_DESCRIPTION* description) {
if (!description) return;
if (NtlAtomicDecrement32(&description->RefCount) != 0) return;
if (PosixOpenDescriptionNeedsHandle(description->Type) &&
description->KHandle != SYS_HANDLE_INVALID)
(void)SysCloseHandle(description->KHandle);
free(description);
}
// Releases one stable local wrapper and its server holder at the final reference.
void PosixFdRelease(POSIX_OPEN_DESCRIPTION* description) {
if (!description) return;
if (NtlAtomicDecrement32(&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_LOGICAL_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_LOGICAL_PROCESS_STATE* process = PosixRuntimeLogicalProcessState();
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) {
bool needsHandle = PosixOpenDescriptionNeedsHandle(type);
bool 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_LOGICAL_PROCESS_STATE* process = PosixRuntimeLogicalProcessState();
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_LOGICAL_PROCESS_STATE* process = PosixRuntimeLogicalProcessState();
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_LOGICAL_PROCESS_STATE* process = PosixRuntimeLogicalProcessState();
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 KSTATUS PosixFdInstallStandard(POSIX_LOGICAL_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 KSTATUS PosixFdImportInheritance(POSIX_LOGICAL_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;
KSTATUS 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;
bool 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 one logical process descriptor table and standard descriptors.
KSTATUS PosixFdInitializeLogicalProcess(POSIX_LOGICAL_PROCESS_STATE* process, uint64_t inheritanceToken) {
if (!process)
return NTLSTATUS_INVALID_PARAMETER;
uint32_t lock = SYS_HANDLE_INVALID;
KSTATUS 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) PosixFdDestroyLogicalProcess(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)
PosixFdDestroyLogicalProcess(process);
return status;
}
// Releases descriptor-table resources during failed process startup.
void PosixFdDestroyLogicalProcess(POSIX_LOGICAL_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;
}
}
// Drops inherited local aliases after borrowed exec without releasing committed server holders.
void PosixFdDestroyLogicalProcessLocal(POSIX_LOGICAL_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;
PosixFdReleaseLocal(description);
}
if (process->FdLock != SYS_HANDLE_INVALID) {
(void)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(PosixRuntimeLogicalProcessState(), 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_LOGICAL_PROCESS_STATE* process = PosixRuntimeLogicalProcessState();
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;
KSTATUS 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;
KSTATUS 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;
KSTATUS 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;
KSTATUS 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_LOGICAL_PROCESS_STATE* process = PosixRuntimeLogicalProcessState();
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;
}