// POSIX compatibility support for posix proc. #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; } // Implements the posix get command line operation. __declspec(dllexport) const char* posix_get_command_line(void) { SYS_PEB* peb = PosixPeb(); if (!peb || peb->Magic != SYS_PEB_MAGIC || !peb->CommandLine.Data) return ""; return peb->CommandLine.Data; } // Records one spawn association in process-local POSIX state. static void ProcTableInsert(pid_t pid, SYS_HANDLE handle, uint64_t inheritanceToken) { POSIX_PROCESS_STATE* process = PosixRuntimeProcessState(); if (!process) { SysCloseHandle(handle); return; } for (uint32_t i = 0; i < POSIX_SPAWN_TABLE_SIZE; i++) { if (!process->SpawnTable[i].Used) { process->SpawnTable[i] = (POSIX_SPAWN_ENTRY){ pid, handle, inheritanceToken, true }; return; } } SysCloseHandle(handle); if (inheritanceToken != 0) PosixFdCancelInheritanceSnapshot(inheritanceToken); } // Finds a spawned process handle retained by the current POSIX process. static SYS_HANDLE ProcTableFind(pid_t pid) { POSIX_PROCESS_STATE* process = PosixRuntimeProcessState(); if (!process) return SYS_HANDLE_INVALID; for (uint32_t i = 0; i < POSIX_SPAWN_TABLE_SIZE; i++) { if (process->SpawnTable[i].Used && process->SpawnTable[i].Pid == pid) return process->SpawnTable[i].Handle; } return SYS_HANDLE_INVALID; } // Removes one spawn association from process-local POSIX state. static void ProcTableRemove(pid_t pid) { POSIX_PROCESS_STATE* process = PosixRuntimeProcessState(); if (!process) return; for (uint32_t i = 0; i < POSIX_SPAWN_TABLE_SIZE; i++) { if (process->SpawnTable[i].Used && process->SpawnTable[i].Pid == pid) { uint64_t inheritanceToken = process->SpawnTable[i].FdInheritanceToken; memset(&process->SpawnTable[i], 0, sizeof(process->SpawnTable[i])); if (inheritanceToken != 0) PosixFdCancelInheritanceSnapshot(inheritanceToken); return; } } } // Build a flat Windows-style cmdline from a NULL-terminated argv array. // Each token that contains a space is wrapped in double-quotes. // argv[0] is included (it becomes the executable token visible to _start). static void BuildCmdline(char* const argv[], char* out, size_t outSize) { size_t pos = 0; for (int i = 0; argv && argv[i]; i++) { if (i > 0 && pos < outSize - 1) out[pos++] = ' '; const char* tok = argv[i]; bool needQuote = false; for (const char* c = tok; *c; c++) if (*c == ' ' || *c == '\t') { needQuote = true; break; } if (needQuote && pos < outSize - 1) out[pos++] = '"'; for (; *tok && pos < outSize - 2; tok++) { if (*tok == '"') { out[pos++] = '\\'; } out[pos++] = *tok; } if (needQuote && pos < outSize - 1) out[pos++] = '"'; } if (pos < outSize) out[pos] = '\0'; } // POSIX API. __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; } __declspec(dllexport) int posix_spawn(pid_t* pidOut, const char* path, const posix_spawn_file_actions_t* file_actions, // ignored v1 const posix_spawnattr_t* attrp, // ignored v1 char* const argv[], char* const envp[]) // ignored v1 { (void)file_actions; (void)attrp; (void)envp; char native[512]; uint32_t pathStatus = PosixProcessPath(path, native, sizeof(native)); if (pathStatus != NTLSTATUS_SUCCESS) { errno = PosixStatusToErrno(pathStatus); return -1; } SYS_HANDLE imageFile = SYS_HANDLE_INVALID; pathStatus = NtlOpenFile(SysSvFromCstr(native), SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_QUERY, SYS_FILE_OPEN_EXISTING, SYS_FILE_FLAG_NONE, &imageFile); if (pathStatus != NTLSTATUS_SUCCESS) { errno = PosixStatusToErrno(pathStatus); return -1; } char cmdline[512]; BuildCmdline(argv, cmdline, sizeof(cmdline)); // If argv was NULL or empty, fall back to just the path so _start still // gets a meaningful argv[0]. if (cmdline[0] == '\0') { size_t len = strlen(path); if (len >= sizeof(cmdline)) len = sizeof(cmdline) - 1; memcpy(cmdline, path, len); cmdline[len] = '\0'; } uint64_t inheritanceToken = 0; uint32_t snapshotStatus = PosixFdCreateInheritanceSnapshot(POSIX_FD_INHERIT_SKIP_CLOEXEC, &inheritanceToken); if (snapshotStatus != NTLSTATUS_SUCCESS) { (void)SysCloseHandle(imageFile); errno = PosixStatusToErrno(snapshotStatus); return -1; } NTL_POSIX_BOOTSTRAP_DATA bootstrap; memset(&bootstrap, 0, sizeof(bootstrap)); bootstrap.Magic = NTL_POSIX_BOOTSTRAP_MAGIC; bootstrap.Version = NTL_POSIX_BOOTSTRAP_VERSION; bootstrap.Size = sizeof(bootstrap); bootstrap.FdInheritanceToken = inheritanceToken; uint32_t procHandle = 0; NTL_CREATE_SYSTEM_PROCESS_REQUEST req; memset(&req, 0, sizeof(req)); req.Size = sizeof(req); req.Flags = NTL_PROCESS_LOAD_FLAG_NONE; req.ImageFile = imageFile; req.Path = SysSvFromCstr(native); req.CommandLine = SysSvFromCstr(cmdline); req.SecurityContext = SYS_SECURITY_CONTEXT_CURRENT; req.ResolveModuleName = PosixResolveModuleName; req.StartupData = &bootstrap; req.StartupDataSize = sizeof(bootstrap); req.ProcessHandleOut = &procHandle; uint32_t s = NtlCreateSystemProcess(&req); if (imageFile != SYS_HANDLE_INVALID) (void)SysCloseHandle(imageFile); if (s != NTLSTATUS_SUCCESS) { PosixFdCancelInheritanceSnapshot(inheritanceToken); errno = PosixStatusToErrno(s); return -1; } KSTATUS armStatus = PosixFdArmInheritanceSnapshot(inheritanceToken, procHandle); if (!KSUCCESS(armStatus) && armStatus != NTLSTATUS_NOT_FOUND) { (void)SysKillProcess(procHandle, (int32_t)NTLSTATUS_PROCESS_KILLED); (void)SysCloseHandle(procHandle); PosixFdCancelInheritanceSnapshot(inheritanceToken); errno = PosixStatusToErrno(armStatus); return -1; } uint32_t kpid = 0; SysGetProcessPid(procHandle, &kpid); ProcTableInsert((pid_t)kpid, procHandle, inheritanceToken); if (pidOut) *pidOut = (pid_t)kpid; return 0; } // options: WNOHANG not supported in v1 (always blocks). __declspec(dllexport) pid_t waitpid(pid_t pid, int* status, int options) { (void)options; SYS_HANDLE handle = ProcTableFind(pid); if (handle == SYS_HANDLE_INVALID) { errno = ESRCH; return -1; } SysWait(handle, SYS_WAIT_FLAG_NONE); int32_t exitCode = 0; SysGetProcessExitCode(handle, &exitCode); if (status) *status = exitCode << 8; // WEXITSTATUS convention SysCloseHandle(handle); ProcTableRemove(pid); return pid; } // Implements the exit operation. __declspec(dllexport) void exit(int status) { SysExitProcess(status); while (true) {} } // 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); }