// POSIX compatibility support for posix proc. #include #include #include #include #include #include #include #include "posix_internal.h" #include "posix_path.h" // Returns the native PEB retained by the process-local POSIX runtime state. static SYS_PEB* PosixPeb(void) { POSIX_PROCESS_STATE* process = PosixRuntimeProcessState(); return process ? process->Peb : (SYS_PEB*)0; } // Returns the POSIX argv-derived command-line compatibility view when available. __declspec(dllexport) const char* posix_get_command_line(void) { POSIX_PROCESS_STATE* process = PosixRuntimeProcessState(); if (process && process->CommandLine) return process->CommandLine; SYS_PEB* peb = PosixPeb(); if (!peb || peb->Magic != SYS_PEB_MAGIC || !peb->CommandLine.Data) return ""; return peb->CommandLine.Data; } // Returns the stable POSIX PID selected for the calling logical thread. __declspec(dllexport) pid_t getpid(void) { POSIX_THREAD_STATE* thread = PosixRuntimeThreadState(); if (!thread || !thread->LogicalProcess || thread->LogicalProcess->PosixPid == 0) return (pid_t)-1; return (pid_t)thread->LogicalProcess->PosixPid; } #define POSIX_SPAWN_ACTIONS_MAGIC 0x53464131u #define POSIX_SPAWN_ATTRIBUTES_MAGIC 0x53415431u // Converts one posixsrv transport/result pair into a POSIX construction error. static int PosixConstructionCall(NTL_POSIX_REQUEST* request, NTL_POSIX_REPLY* reply) { KSTATUS status = PosixRuntimeCallServer(request, reply); if (!KSUCCESS(status)) return PosixStatusToErrno(status); if (!KSUCCESS(reply->Status)) return PosixStatusToErrno(reply->Status); return 0; } // Cancels one process-owned construction transaction after a client-side failure. static void PosixConstructionCancelTransaction(uint32_t transactionId) { if (!transactionId) 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_TRANSACTION; request.TransactionId = transactionId; NTL_POSIX_REPLY reply; (void)PosixRuntimeCallServer(&request, &reply); } // Begins one server-owned process-construction transaction. static int PosixConstructionBegin(uint32_t kind, uint32_t* transactionIdOut) { if (!transactionIdOut || (kind != NTL_POSIX_TRANSACTION_SPAWN && kind != NTL_POSIX_TRANSACTION_EXEC)) return EINVAL; *transactionIdOut = 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_BEGIN_TRANSACTION; request.TransactionKind = kind; NTL_POSIX_REPLY reply; int error = PosixConstructionCall(&request, &reply); if (error) return error; if (!reply.TransactionId) return EIO; *transactionIdOut = reply.TransactionId; return 0; } // Adds one bounded string length to the aggregate construction payload size. static int PosixConstructionMeasureString(const char* text, uint32_t* totalBytes) { if (!text || !totalBytes) return EINVAL; size_t length = strlen(text); if (length > NTL_POSIX_CONSTRUCT_DATA_MAX || (uint32_t)length > NTL_POSIX_CONSTRUCT_DATA_MAX - *totalBytes) return E2BIG; *totalBytes += (uint32_t)length; return 0; } // Validates one argv/envp vector against the bounded server transaction format. static int PosixConstructionMeasureVector(char* const vector[], uint32_t* totalBytes) { if (!vector) return 0; for (uint32_t index = 0; vector[index]; ++index) { if (index >= NTL_POSIX_CONSTRUCT_VECTOR_MAX) return E2BIG; int error = PosixConstructionMeasureString(vector[index], totalBytes); if (error) return error; } return 0; } // Validates all bounded spawn text before allocating a server transaction. static int PosixSpawnValidateInput(const char* path, const posix_spawn_file_actions_t* actions, char* const argv[], char* const envp[]) { if (!path || !path[0]) return EINVAL; size_t pathLength = strlen(path); if (pathLength > SYS_PATH_MAX) return ENAMETOOLONG; uint32_t totalBytes = 0; int error = PosixConstructionMeasureString(path, &totalBytes); if (error) return error; error = PosixConstructionMeasureVector(argv, &totalBytes); if (error) return error; error = PosixConstructionMeasureVector(envp, &totalBytes); if (error) return error; if (!actions) return 0; if (actions->Count > POSIX_SPAWN_FILE_ACTION_CAPACITY || actions->Count > NTL_POSIX_SPAWN_ACTION_MAX) return EINVAL; for (uint32_t i = 0; i < actions->Count; ++i) { const posix_spawn_file_action_entry* action = &actions->Entries[i]; if (action->Type != NTL_POSIX_SPAWN_FILE_ACTION_OPEN) continue; if (!action->Path) return EINVAL; size_t length = strlen(action->Path); if (length > SYS_PATH_MAX) return ENAMETOOLONG; error = PosixConstructionMeasureString(action->Path, &totalBytes); if (error) return error; } return 0; } // Sends one construction executable pathname to posixsrv. static int PosixConstructionSendExecutable(uint32_t transactionId, const char* path) { if (!path || !path[0]) return EINVAL; size_t length = strlen(path); if (length > SYS_PATH_MAX) return ENAMETOOLONG; NTL_POSIX_REQUEST request; memset(&request, 0, sizeof(request)); request.Size = sizeof(request); request.Version = NTL_POSIX_PROTOCOL_VERSION; request.Operation = NTL_POSIX_OP_CONSTRUCT_SET_EXECUTABLE; request.TransactionId = transactionId; request.PathLength = (uint32_t)length; memcpy(request.Path, path, length + 1u); NTL_POSIX_REPLY reply; return PosixConstructionCall(&request, &reply); } // Streams one argv/envp vector through bounded Port messages. static int PosixConstructionSendVector(uint32_t transactionId, uint32_t kind, char* const vector[]) { if (!vector) return 0; for (uint32_t index = 0; vector[index]; ++index) { if (index >= NTL_POSIX_CONSTRUCT_VECTOR_MAX) return E2BIG; size_t length = strlen(vector[index]); if (length > UINT32_MAX) return E2BIG; uint32_t offset = 0; do { uint32_t remaining = (uint32_t)length - offset; uint32_t chunk = remaining > SYS_PATH_MAX ? SYS_PATH_MAX : remaining; NTL_POSIX_REQUEST request; memset(&request, 0, sizeof(request)); request.Size = sizeof(request); request.Version = NTL_POSIX_PROTOCOL_VERSION; request.Operation = NTL_POSIX_OP_CONSTRUCT_WRITE_VECTOR; request.TransactionId = transactionId; request.ConstructionVectorKind = kind; request.ConstructionVectorIndex = index; request.ConstructionVectorOffset = offset; request.PathLength = chunk; if (chunk) memcpy(request.Path, vector[index] + offset, chunk); request.Path[chunk] = '\0'; if (offset + chunk == (uint32_t)length) request.ConstructionVectorFlags = NTL_POSIX_CONSTRUCT_VECTOR_CHUNK_FINAL; NTL_POSIX_REPLY reply; int error = PosixConstructionCall(&request, &reply); if (error) return error; offset += chunk; } while (offset < (uint32_t)length || (length == 0 && offset == 0)); } return 0; } // Sends ordered file actions to the server-owned logical descriptor manifest. static int PosixSpawnSendFileActions(uint32_t transactionId, const posix_spawn_file_actions_t* actions) { if (!actions) return 0; for (uint32_t i = 0; i < actions->Count; ++i) { const posix_spawn_file_action_entry* action = &actions->Entries[i]; NTL_POSIX_REQUEST request; memset(&request, 0, sizeof(request)); request.Size = sizeof(request); request.Version = NTL_POSIX_PROTOCOL_VERSION; request.Operation = NTL_POSIX_OP_SPAWN_ADD_FILE_ACTION; request.TransactionId = transactionId; request.SpawnActionKind = action->Type; request.SpawnFileDescriptor = action->FileDescriptor; request.SpawnNewFileDescriptor = action->NewFileDescriptor; request.SpawnOpenFlags = (uint32_t)action->OpenFlags; request.SpawnOpenMode = (uint32_t)action->Mode; if (action->Type == NTL_POSIX_SPAWN_FILE_ACTION_OPEN) { if (!action->Path) return EINVAL; size_t length = strlen(action->Path); if (length > SYS_PATH_MAX) return ENAMETOOLONG; request.PathLength = (uint32_t)length; memcpy(request.Path, action->Path, length + 1u); } NTL_POSIX_REPLY reply; int error = PosixConstructionCall(&request, &reply); if (error) return error; } return 0; } // Sends process-group and signal inheritance policy for one server-owned spawn. static int PosixSpawnSendAttributes(uint32_t transactionId, const posix_spawnattr_t* attributes) { POSIX_THREAD_STATE* thread = PosixRuntimeThreadState(); if (!thread) return EIO; NTL_POSIX_REQUEST request; memset(&request, 0, sizeof(request)); request.Size = sizeof(request); request.Version = NTL_POSIX_PROTOCOL_VERSION; request.Operation = NTL_POSIX_OP_SPAWN_SET_ATTRIBUTES; request.TransactionId = transactionId; request.SpawnSignalMask = thread->SignalBlocked; if (attributes) { request.SpawnAttributeFlags = attributes->Flags; request.ProcessGroupId = (uint32_t)attributes->ProcessGroup; if (attributes->Flags & POSIX_SPAWN_SETSIGMASK) request.SpawnSignalMask = attributes->SignalMask; if (attributes->Flags & POSIX_SPAWN_SETSIGDEF) request.SpawnSignalDefaultMask = attributes->SignalDefault; } NTL_POSIX_REPLY reply; return PosixConstructionCall(&request, &reply); } // Delegates the caller's native security authority so posixsrv does not spawn as itself. static KSTATUS PosixProcessDeriveSecurityContext(SYS_HANDLE* contextOut) { if (!contextOut) return NTLSTATUS_INVALID_PARAMETER; *contextOut = SYS_HANDLE_INVALID; SYS_SECURITY_CONTEXT_INFO info; memset(&info, 0, sizeof(info)); info.Size = sizeof(info); KSTATUS status = SysQuerySecurityContext(SYS_SECURITY_CONTEXT_CURRENT, &info); if (!KSUCCESS(status)) return status; if ((info.EffectivePrivileges & ~info.DelegablePrivileges) != 0 || (info.ServiceClaims & ~info.DelegableServiceClaims) != 0) return NTLSTATUS_ACCESS_DENIED; SYS_DERIVE_SECURITY_CONTEXT_REQUEST derive; memset(&derive, 0, sizeof(derive)); derive.Size = sizeof(derive); derive.SourceContext = SYS_SECURITY_CONTEXT_CURRENT; derive.EffectivePrivileges = info.EffectivePrivileges; derive.DelegablePrivileges = info.DelegablePrivileges; derive.ServiceClaims = info.ServiceClaims; derive.DelegableServiceClaims = info.DelegableServiceClaims; derive.Flags = SYS_SECURITY_CONTEXT_FLAG_NONE; derive.ContextHandleOut = contextOut; return SysDeriveSecurityContext(&derive); } // Initializes an empty ordered spawn file-action set. __declspec(dllexport) int posix_spawn_file_actions_init(posix_spawn_file_actions_t* actions) { if (!actions) return EINVAL; memset(actions, 0, sizeof(*actions)); actions->Initialized = POSIX_SPAWN_ACTIONS_MAGIC; return 0; } // Releases pathname storage owned by one file-action set. __declspec(dllexport) int posix_spawn_file_actions_destroy(posix_spawn_file_actions_t* actions) { if (!actions || actions->Initialized != POSIX_SPAWN_ACTIONS_MAGIC) return EINVAL; for (uint32_t i = 0; i < actions->Count; ++i) free(actions->Entries[i].Path); memset(actions, 0, sizeof(*actions)); return 0; } // Appends one descriptor close action. __declspec(dllexport) int posix_spawn_file_actions_addclose(posix_spawn_file_actions_t* actions, int fd) { if (!actions || actions->Initialized != POSIX_SPAWN_ACTIONS_MAGIC || fd < 0) return EBADF; if (actions->Count >= POSIX_SPAWN_FILE_ACTION_CAPACITY) return ENOMEM; posix_spawn_file_action_entry* action = &actions->Entries[actions->Count++]; memset(action, 0, sizeof(*action)); action->Type = NTL_POSIX_SPAWN_FILE_ACTION_CLOSE; action->FileDescriptor = fd; return 0; } // Appends one dup2 action; the destination loses FD_CLOEXEC during server application. __declspec(dllexport) int posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t* actions, int fd, int newFd) { if (!actions || actions->Initialized != POSIX_SPAWN_ACTIONS_MAGIC || fd < 0 || newFd < 0) return EBADF; if (actions->Count >= POSIX_SPAWN_FILE_ACTION_CAPACITY) return ENOMEM; posix_spawn_file_action_entry* action = &actions->Entries[actions->Count++]; memset(action, 0, sizeof(*action)); action->Type = NTL_POSIX_SPAWN_FILE_ACTION_DUP2; action->FileDescriptor = fd; action->NewFileDescriptor = newFd; return 0; } // Appends one open-at-descriptor action and owns a private copy of its pathname. __declspec(dllexport) int posix_spawn_file_actions_addopen(posix_spawn_file_actions_t* actions, int fd, const char* path, int openFlags, mode_t mode) { if (!actions || actions->Initialized != POSIX_SPAWN_ACTIONS_MAGIC || fd < 0 || !path) return EINVAL; if (actions->Count >= POSIX_SPAWN_FILE_ACTION_CAPACITY) return ENOMEM; size_t length = strlen(path); char* copy = (char*)malloc(length + 1u); if (!copy) return ENOMEM; memcpy(copy, path, length + 1u); posix_spawn_file_action_entry* action = &actions->Entries[actions->Count++]; memset(action, 0, sizeof(*action)); action->Type = NTL_POSIX_SPAWN_FILE_ACTION_OPEN; action->FileDescriptor = fd; action->OpenFlags = openFlags; action->Mode = mode; action->Path = copy; return 0; } // Initializes one POSIX spawn attribute object. __declspec(dllexport) int posix_spawnattr_init(posix_spawnattr_t* attributes) { if (!attributes) return EINVAL; memset(attributes, 0, sizeof(*attributes)); attributes->Initialized = POSIX_SPAWN_ATTRIBUTES_MAGIC; return 0; } // Invalidates one spawn attribute object. __declspec(dllexport) int posix_spawnattr_destroy(posix_spawnattr_t* attributes) { if (!attributes || attributes->Initialized != POSIX_SPAWN_ATTRIBUTES_MAGIC) return EINVAL; memset(attributes, 0, sizeof(*attributes)); return 0; } // Returns active spawn attribute flags. __declspec(dllexport) int posix_spawnattr_getflags(const posix_spawnattr_t* attributes, short* flags) { if (!attributes || attributes->Initialized != POSIX_SPAWN_ATTRIBUTES_MAGIC || !flags) return EINVAL; *flags = (short)attributes->Flags; return 0; } // Selects supported process-group and signal spawn attributes. __declspec(dllexport) int posix_spawnattr_setflags(posix_spawnattr_t* attributes, short flags) { const uint32_t valid = POSIX_SPAWN_SETPGROUP | POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF; if (!attributes || attributes->Initialized != POSIX_SPAWN_ATTRIBUTES_MAGIC || ((uint32_t)(unsigned short)flags & ~valid) != 0) return EINVAL; attributes->Flags = (uint32_t)(unsigned short)flags; return 0; } // Returns the requested process group for POSIX_SPAWN_SETPGROUP. __declspec(dllexport) int posix_spawnattr_getpgroup(const posix_spawnattr_t* attributes, pid_t* pgroup) { if (!attributes || attributes->Initialized != POSIX_SPAWN_ATTRIBUTES_MAGIC || !pgroup) return EINVAL; *pgroup = attributes->ProcessGroup; return 0; } // Sets the requested child process group; zero means the child's own stable POSIX PID. __declspec(dllexport) int posix_spawnattr_setpgroup(posix_spawnattr_t* attributes, pid_t pgroup) { if (!attributes || attributes->Initialized != POSIX_SPAWN_ATTRIBUTES_MAGIC || pgroup < 0) return EINVAL; attributes->ProcessGroup = pgroup; return 0; } // Returns the explicit child signal mask stored in one spawn attribute object. __declspec(dllexport) int posix_spawnattr_getsigmask(const posix_spawnattr_t* attributes, sigset_t* mask) { if (!attributes || attributes->Initialized != POSIX_SPAWN_ATTRIBUTES_MAGIC || !mask) return EINVAL; *mask = attributes->SignalMask; return 0; } // Stores the child signal mask used with POSIX_SPAWN_SETSIGMASK. __declspec(dllexport) int posix_spawnattr_setsigmask(posix_spawnattr_t* attributes, const sigset_t* mask) { if (!attributes || attributes->Initialized != POSIX_SPAWN_ATTRIBUTES_MAGIC || !mask || (*mask & ~NTL_POSIX_SIGNAL_VALID_MASK) != 0) return EINVAL; attributes->SignalMask = *mask & ~NTL_POSIX_SIGNAL_UNBLOCKABLE_MASK; return 0; } // Returns the set forced to default disposition in the spawned image. __declspec(dllexport) int posix_spawnattr_getsigdefault(const posix_spawnattr_t* attributes, sigset_t* set) { if (!attributes || attributes->Initialized != POSIX_SPAWN_ATTRIBUTES_MAGIC || !set) return EINVAL; *set = attributes->SignalDefault; return 0; } // Stores the signals forced to default disposition in the spawned image. __declspec(dllexport) int posix_spawnattr_setsigdefault(posix_spawnattr_t* attributes, const sigset_t* set) { if (!attributes || attributes->Initialized != POSIX_SPAWN_ATTRIBUTES_MAGIC || !set || (*set & ~NTL_POSIX_SIGNAL_VALID_MASK) != 0) return EINVAL; attributes->SignalDefault = *set; return 0; } // Constructs one POSIX child through the authoritative posixsrv transaction. __declspec(dllexport) int posix_spawn(pid_t* pidOut, const char* path, const posix_spawn_file_actions_t* fileActions, const posix_spawnattr_t* attributes, char* const argv[], char* const envp[]) { if (!pidOut || !path || !path[0]) return EINVAL; if (fileActions && fileActions->Initialized != POSIX_SPAWN_ACTIONS_MAGIC) return EINVAL; if (attributes && attributes->Initialized != POSIX_SPAWN_ATTRIBUTES_MAGIC) return EINVAL; char* const* environment = envp ? envp : environ; int error = PosixSpawnValidateInput(path, fileActions, argv, (char* const*)environment); if (error) return error; *pidOut = (pid_t)-1; uint32_t transactionId = 0; error = PosixConstructionBegin(NTL_POSIX_TRANSACTION_SPAWN, &transactionId); if (error) return error; error = PosixConstructionSendExecutable(transactionId, path); if (!error) error = PosixConstructionSendVector(transactionId, NTL_POSIX_CONSTRUCT_VECTOR_ARGUMENT, argv); if (!error) error = PosixConstructionSendVector(transactionId, NTL_POSIX_CONSTRUCT_VECTOR_ENVIRONMENT, (char* const*)environment); if (!error) error = PosixSpawnSendFileActions(transactionId, fileActions); if (!error) error = PosixSpawnSendAttributes(transactionId, attributes); if (error) { PosixConstructionCancelTransaction(transactionId); return error; } uint64_t inheritanceToken = 0; KSTATUS status = PosixFdCreateInheritanceSnapshot(0, &inheritanceToken); if (!KSUCCESS(status)) { PosixConstructionCancelTransaction(transactionId); return PosixStatusToErrno(status); } SYS_HANDLE securityContext = SYS_HANDLE_INVALID; status = PosixProcessDeriveSecurityContext(&securityContext); if (!KSUCCESS(status)) { PosixFdCancelInheritanceSnapshot(inheritanceToken); PosixConstructionCancelTransaction(transactionId); return PosixStatusToErrno(status); } NTL_POSIX_REQUEST request; memset(&request, 0, sizeof(request)); request.Size = sizeof(request); request.Version = NTL_POSIX_PROTOCOL_VERSION; request.Operation = NTL_POSIX_OP_SPAWN_COMMIT; request.TransactionId = transactionId; request.FdInheritanceToken = inheritanceToken; SYS_PORT_HANDLE_TRANSFER transfer; memset(&transfer, 0, sizeof(transfer)); transfer.Handle = securityContext; transfer.DesiredRights = SYS_RIGHT_QUERY | SYS_SECURITY_CONTEXT_RIGHT_ASSIGN; transfer.Flags = SYS_PORT_TRANSFER_FLAG_NONE; NTL_POSIX_REPLY reply; status = PosixRuntimeCallServerWithHandles(&request, &transfer, 1u, &reply); (void)SysCloseHandle(securityContext); if (!KSUCCESS(status)) { PosixFdCancelInheritanceSnapshot(inheritanceToken); PosixConstructionCancelTransaction(transactionId); return PosixStatusToErrno(status); } if (!KSUCCESS(reply.Status)) { PosixFdCancelInheritanceSnapshot(inheritanceToken); PosixConstructionCancelTransaction(transactionId); return PosixStatusToErrno(reply.Status); } if (reply.SpawnedPosixPid == 0 || reply.SpawnedPosixPid > INT32_MAX) return EIO; *pidOut = (pid_t)reply.SpawnedPosixPid; return 0; } // Validates one exec image and its replacement argv/envp payload. static int PosixExecValidateInput(const char* path, char* const argv[], char* const envp[]) { if (!path || !path[0]) return EINVAL; size_t pathLength = strlen(path); if (pathLength > SYS_PATH_MAX) return ENAMETOOLONG; uint32_t totalBytes = 0; int error = PosixConstructionMeasureString(path, &totalBytes); if (!error) error = PosixConstructionMeasureVector(argv, &totalBytes); if (!error) error = PosixConstructionMeasureVector(envp, &totalBytes); return error; } // Replaces the current native generation while preserving its stable POSIX process identity. __declspec(dllexport) int execve(const char* path, char* const argv[], char* const envp[]) { int error = PosixExecValidateInput(path, argv, envp); if (error) { errno = error; return -1; } POSIX_THREAD_STATE* thread = PosixRuntimeThreadState(); if (!thread || !thread->LogicalProcess) { errno = EIO; return -1; } POSIX_VFORK_CHILD_STATE* borrowedChild = PosixVforkCurrentBorrowedChild(); uint32_t transactionId = 0; error = PosixConstructionBegin(NTL_POSIX_TRANSACTION_EXEC, &transactionId); if (error) { errno = error; return -1; } error = PosixConstructionSendExecutable(transactionId, path); if (!error) error = PosixConstructionSendVector(transactionId, NTL_POSIX_CONSTRUCT_VECTOR_ARGUMENT, argv); if (!error) error = PosixConstructionSendVector(transactionId, NTL_POSIX_CONSTRUCT_VECTOR_ENVIRONMENT, envp); if (error) { PosixConstructionCancelTransaction(transactionId); errno = error; return -1; } uint64_t inheritanceToken = 0; KSTATUS status = PosixFdCreateInheritanceSnapshot(POSIX_FD_INHERIT_SKIP_CLOEXEC, &inheritanceToken); if (!KSUCCESS(status)) { PosixConstructionCancelTransaction(transactionId); errno = PosixStatusToErrno(status); 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_EXEC_COMMIT; request.TransactionId = transactionId; request.FdInheritanceToken = inheritanceToken; request.SignalMask = (thread->SignalBlocked | thread->SignalHandlerBlocked) & ~NTL_POSIX_SIGNAL_UNBLOCKABLE_MASK; NTL_POSIX_REPLY reply; status = PosixRuntimeCallServerBlockingWithHandles( &request, (const SYS_PORT_HANDLE_TRANSFER*)0, 0, &reply); if (!KSUCCESS(status)) { PosixFdCancelInheritanceSnapshot(inheritanceToken); PosixConstructionCancelTransaction(transactionId); errno = PosixStatusToErrno(status); return -1; } if (!KSUCCESS(reply.Status)) { PosixFdCancelInheritanceSnapshot(inheritanceToken); PosixConstructionCancelTransaction(transactionId); errno = PosixStatusToErrno(reply.Status); return -1; } if (borrowedChild) { // The server has a READY replacement but will release it only after this thread is dead. PosixVforkDetachBorrowedChildLocal(borrowedChild, false); for (;;) SysExitThread(0); } // Ordinary successful exec has no reply path; reaching here violates that contract. errno = EIO; return -1; } // Returns the server-owned parent launch association for the current POSIX process. __declspec(dllexport) pid_t getppid(void) { NTL_POSIX_REPLY reply; KSTATUS status = PosixRuntimeGetProcessAttributes(&reply); if (!KSUCCESS(status)) { errno = PosixStatusToErrno(status); return (pid_t)-1; } return (pid_t)reply.ParentPosixPid; } // Waits for one matching POSIX child record and collects it exactly once. __declspec(dllexport) pid_t waitpid(pid_t pid, int* statusOut, int options) { if ((options & ~WNOHANG) != 0) { errno = EINVAL; return (pid_t)-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_WAIT_PROCESS; request.WaitPid = (int32_t)pid; request.WaitOptions = (options & WNOHANG) ? NTL_POSIX_WAIT_NOHANG : 0u; NTL_POSIX_REPLY reply; KSTATUS status = PosixRuntimeCallServerBlocking(&request, &reply); if (!KSUCCESS(status)) { errno = PosixStatusToErrno(status); return (pid_t)-1; } if (!KSUCCESS(reply.Status)) { errno = PosixStatusToErrno(reply.Status); return (pid_t)-1; } if (reply.WaitedPosixPid == 0) return 0; if (reply.WaitedPosixPid < 0) { errno = EIO; return (pid_t)-1; } if (statusOut) *statusOut = (reply.ExitCode & 0xff) << 8; return (pid_t)reply.WaitedPosixPid; } // Waits for any POSIX child. __declspec(dllexport) pid_t wait(int* status) { return waitpid((pid_t)-1, status, 0); } // Terminates either an ordinary POSIX native generation or one borrowed vfork child. __declspec(dllexport) void _exit(int status) { POSIX_VFORK_CHILD_STATE* borrowedChild = PosixVforkCurrentBorrowedChild(); if (borrowedChild) { (void)PosixVforkRecordExit(borrowedChild, status); PosixVforkDetachBorrowedChildLocal(borrowedChild, true); for (;;) SysExitThread(status); } for (;;) SysExitProcess(status); } // Terminates the process; stdio/atexit policy can layer above this when those facilities exist. __declspec(dllexport) void exit(int status) { _exit(status); } // Initializes one ordinary shared-attribute request. static void PosixProcessRequest(NTL_POSIX_REQUEST* request, uint32_t operation) { memset(request, 0, sizeof(*request)); request->Size = sizeof(*request); request->Version = NTL_POSIX_PROTOCOL_VERSION; request->Operation = operation; request->Flags = NTL_POSIX_REQUEST_FLAG_NONE; } // Executes one shared-attribute request and converts transport/server failure to errno. static int PosixProcessCall(NTL_POSIX_REQUEST* request, NTL_POSIX_REPLY* reply) { KSTATUS status = PosixRuntimeCallServer(request, reply); if (!KSUCCESS(status)) { errno = PosixStatusToErrno(status); return -1; } if (!KSUCCESS(reply->Status)) { errno = PosixStatusToErrno(reply->Status); return -1; } return 0; } // Executes one operation whose permission failures are POSIX EPERM rather than EACCES. static int PosixProcessCallPermission(NTL_POSIX_REQUEST* request, NTL_POSIX_REPLY* reply) { KSTATUS status = PosixRuntimeCallServer(request, reply); if (!KSUCCESS(status)) { errno = PosixStatusToErrno(status); return -1; } if (!KSUCCESS(reply->Status)) { errno = reply->Status == NTLSTATUS_ACCESS_DENIED ? EPERM : PosixStatusToErrno(reply->Status); return -1; } return 0; } // Returns the real POSIX user id owned by posixsrv. __declspec(dllexport) uid_t getuid(void) { NTL_POSIX_REPLY reply; if (!KSUCCESS(PosixRuntimeGetProcessAttributes(&reply))) return (uid_t)-1; return (uid_t)reply.UserId; } // Returns the effective POSIX user id owned by posixsrv. __declspec(dllexport) uid_t geteuid(void) { NTL_POSIX_REPLY reply; if (!KSUCCESS(PosixRuntimeGetProcessAttributes(&reply))) return (uid_t)-1; return (uid_t)reply.EffectiveUserId; } // Returns the real POSIX group id owned by posixsrv. __declspec(dllexport) gid_t getgid(void) { NTL_POSIX_REPLY reply; if (!KSUCCESS(PosixRuntimeGetProcessAttributes(&reply))) return (gid_t)-1; return (gid_t)reply.GroupId; } // Returns the effective POSIX group id owned by posixsrv. __declspec(dllexport) gid_t getegid(void) { NTL_POSIX_REPLY reply; if (!KSUCCESS(PosixRuntimeGetProcessAttributes(&reply))) return (gid_t)-1; return (gid_t)reply.EffectiveGroupId; } // Changes synthetic user credentials; uid zero is the only credential-changing authority. __declspec(dllexport) int setuid(uid_t uid) { NTL_POSIX_REQUEST request; NTL_POSIX_REPLY reply; PosixProcessRequest(&request, NTL_POSIX_OP_SET_UID); request.UserId = (uint32_t)uid; return PosixProcessCallPermission(&request, &reply); } // Changes synthetic group credentials; effective uid zero is the authority. __declspec(dllexport) int setgid(gid_t gid) { NTL_POSIX_REQUEST request; NTL_POSIX_REPLY reply; PosixProcessRequest(&request, NTL_POSIX_OP_SET_GID); request.GroupId = (uint32_t)gid; return PosixProcessCallPermission(&request, &reply); } // Returns the supplementary group list attached to the current POSIX process. __declspec(dllexport) int getgroups(int size, gid_t list[]) { if (size < 0 || (size != 0 && !list)) { errno = EINVAL; return -1; } NTL_POSIX_REPLY reply; KSTATUS status = PosixRuntimeGetProcessAttributes(&reply); if (!KSUCCESS(status)) { errno = PosixStatusToErrno(status); return -1; } if (reply.SupplementaryGroupCount > NTL_POSIX_SUPPLEMENTARY_GROUP_MAX) { errno = EIO; return -1; } if (size == 0) return (int)reply.SupplementaryGroupCount; if ((uint32_t)size < reply.SupplementaryGroupCount) { errno = EINVAL; return -1; } for (uint32_t i = 0; i < reply.SupplementaryGroupCount; ++i) list[i] = (gid_t)reply.SupplementaryGroups[i]; return (int)reply.SupplementaryGroupCount; } // Returns the login identity fixed when the POSIX process record is created. __declspec(dllexport) char* getlogin(void) { POSIX_THREAD_STATE* thread = PosixRuntimeThreadState(); if (!thread) return (char*)0; NTL_POSIX_REPLY reply; KSTATUS status = PosixRuntimeGetProcessAttributes(&reply); if (!KSUCCESS(status)) { errno = PosixStatusToErrno(status); return (char*)0; } if (reply.NameLength == 0 || reply.NameLength > NTL_POSIX_IDENTITY_NAME_MAX || reply.Name[reply.NameLength] != '\0') { errno = ENOENT; return (char*)0; } memcpy(thread->LoginName, reply.Name, reply.NameLength + 1u); return thread->LoginName; } // Returns the current POSIX process-group id. __declspec(dllexport) pid_t getpgrp(void) { NTL_POSIX_REPLY reply; KSTATUS status = PosixRuntimeGetProcessAttributes(&reply); if (!KSUCCESS(status)) { errno = PosixStatusToErrno(status); return (pid_t)-1; } return (pid_t)reply.ProcessGroupId; } // Changes process-group membership under posixsrv's parent/session invariants. __declspec(dllexport) int setpgid(pid_t pid, pid_t pgid) { if (pid < 0 || pgid < 0) { errno = EINVAL; return -1; } NTL_POSIX_REQUEST request; NTL_POSIX_REPLY reply; PosixProcessRequest(&request, NTL_POSIX_OP_SET_PROCESS_GROUP); request.TargetPosixPid = (uint32_t)pid; request.ProcessGroupId = (uint32_t)pgid; return PosixProcessCallPermission(&request, &reply); } // Creates a new POSIX session and process group when the caller is not a group leader. __declspec(dllexport) pid_t setsid(void) { NTL_POSIX_REQUEST request; NTL_POSIX_REPLY reply; PosixProcessRequest(&request, NTL_POSIX_OP_CREATE_SESSION); if (PosixProcessCallPermission(&request, &reply) != 0) return (pid_t)-1; return (pid_t)reply.SessionId; } // Performs one passwd lookup and builds a thread-local POSIX result view. static struct passwd* PosixLookupUser(uint32_t lookupKind, uid_t uid, const char* name) { POSIX_THREAD_STATE* thread = PosixRuntimeThreadState(); if (!thread) return (struct passwd*)0; NTL_POSIX_REQUEST request; NTL_POSIX_REPLY reply; PosixProcessRequest(&request, NTL_POSIX_OP_LOOKUP_USER); request.LookupKind = lookupKind; request.UserId = (uint32_t)uid; if (lookupKind == NTL_POSIX_LOOKUP_BY_NAME) { if (!name) { errno = EINVAL; return (struct passwd*)0; } size_t length = strlen(name); if (length == 0 || length > NTL_POSIX_IDENTITY_NAME_MAX) { errno = ENOENT; return (struct passwd*)0; } memcpy(request.Name, name, length + 1u); request.NameLength = (uint32_t)length; } if (PosixProcessCall(&request, &reply) != 0) return (struct passwd*)0; if (reply.NameLength == 0 || reply.NameLength > NTL_POSIX_IDENTITY_NAME_MAX || reply.HomeLength > NTL_POSIX_IDENTITY_HOME_MAX || reply.ShellLength > NTL_POSIX_IDENTITY_SHELL_MAX || reply.Name[reply.NameLength] != '\0' || reply.Home[reply.HomeLength] != '\0' || reply.Shell[reply.ShellLength] != '\0') { errno = EIO; return (struct passwd*)0; } memcpy(thread->PasswordName, reply.Name, reply.NameLength + 1u); memcpy(thread->PasswordHome, reply.Home, reply.HomeLength + 1u); memcpy(thread->PasswordShell, reply.Shell, reply.ShellLength + 1u); thread->PasswordResult.pw_name = thread->PasswordName; thread->PasswordResult.pw_uid = (uid_t)reply.UserId; thread->PasswordResult.pw_gid = (gid_t)reply.GroupId; thread->PasswordResult.pw_dir = thread->PasswordHome; thread->PasswordResult.pw_shell = thread->PasswordShell; return &thread->PasswordResult; } // Looks up one synthesized passwd entry by name. __declspec(dllexport) struct passwd* getpwnam(const char* name) { return PosixLookupUser(NTL_POSIX_LOOKUP_BY_NAME, 0, name); } // Looks up one synthesized passwd entry by uid. __declspec(dllexport) struct passwd* getpwuid(uid_t uid) { return PosixLookupUser(NTL_POSIX_LOOKUP_BY_ID, uid, (const char*)0); } // Performs one group lookup and builds a thread-local POSIX result view. static struct group* PosixLookupGroup(uint32_t lookupKind, gid_t gid, const char* name) { POSIX_THREAD_STATE* thread = PosixRuntimeThreadState(); if (!thread) return (struct group*)0; NTL_POSIX_REQUEST request; NTL_POSIX_REPLY reply; PosixProcessRequest(&request, NTL_POSIX_OP_LOOKUP_GROUP); request.LookupKind = lookupKind; request.GroupId = (uint32_t)gid; if (lookupKind == NTL_POSIX_LOOKUP_BY_NAME) { if (!name) { errno = EINVAL; return (struct group*)0; } size_t length = strlen(name); if (length == 0 || length > NTL_POSIX_IDENTITY_NAME_MAX) { errno = ENOENT; return (struct group*)0; } memcpy(request.Name, name, length + 1u); request.NameLength = (uint32_t)length; } if (PosixProcessCall(&request, &reply) != 0) return (struct group*)0; if (reply.NameLength == 0 || reply.NameLength > NTL_POSIX_IDENTITY_NAME_MAX || reply.Name[reply.NameLength] != '\0') { errno = EIO; return (struct group*)0; } memcpy(thread->GroupName, reply.Name, reply.NameLength + 1u); thread->GroupMembers[0] = (char*)0; thread->GroupResult.gr_name = thread->GroupName; thread->GroupResult.gr_gid = (gid_t)reply.GroupId; thread->GroupResult.gr_mem = thread->GroupMembers; return &thread->GroupResult; } // Looks up one synthesized group entry by name. __declspec(dllexport) struct group* getgrnam(const char* name) { return PosixLookupGroup(NTL_POSIX_LOOKUP_BY_NAME, 0, name); } // Looks up one synthesized group entry by gid. __declspec(dllexport) struct group* getgrgid(gid_t gid) { return PosixLookupGroup(NTL_POSIX_LOOKUP_BY_ID, gid, (const char*)0); }