From b9dbbc8eb824ccd978c9364cb65143a533af64b2 Mon Sep 17 00:00:00 2001 From: "KJK::Hyperion" Date: Thu, 7 Mar 2002 05:46:03 +0000 Subject: [PATCH] preliminary spawn code (untested yet) svn path=/trunk/; revision=2678 --- posix/lib/psxdll/misc/spawn.c | 715 ++++++++++++++++++++++++++++++++++ 1 file changed, 715 insertions(+) create mode 100644 posix/lib/psxdll/misc/spawn.c diff --git a/posix/lib/psxdll/misc/spawn.c b/posix/lib/psxdll/misc/spawn.c new file mode 100644 index 00000000000..2b8572d8974 --- /dev/null +++ b/posix/lib/psxdll/misc/spawn.c @@ -0,0 +1,715 @@ +/* $Id: spawn.c,v 1.1 2002/03/07 05:46:03 hyperion Exp $ + */ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS POSIX+ Subsystem + * FILE: subsys/psx/lib/psxdll/misc/spawn.c + * PURPOSE: Create the first POSIX+ process + * PROGRAMMER: KJK::Hyperion + * UPDATE HISTORY: + * 25/02/2002: Created + */ + +/* + * NOTE by KJK::Hyperion: + * The __PdxSpawnPosixProcess() call solves the chicken-egg dilemma of + * creating the first POSIX+ process in a group without the ability to + * fork+exec (for example from inside a Win32 process). Processes created by + * __PdxSpawnPosixProcess() will *not* inherit anything from the parent, not + * even handles: all creation parameters have to be specified explicitely + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +VOID +__PdxSerializeProcessData +( + IN __PPDX_PDATA ProcessData, + OUT __PPDX_SERIALIZED_PDATA *SerializedProcessData +) +{ + __PPDX_SERIALIZED_PDATA pspdProcessData = 0; + NTSTATUS nErrCode; + PBYTE pBufferTail; + ULONG ulAllocSize = sizeof(__PDX_SERIALIZED_PDATA) - 1; + size_t *pnArgLengths; + size_t *pnEnvVarsLengths; + int nEnvVarsCount; + int i; + + /* calculate buffer length */ + /* FIXME please! this is the most inefficient way to do it */ + + /* argv */ + pnArgLengths = __malloc(ProcessData->ArgCount * sizeof(size_t)); + + for(i = 0; i < ProcessData->ArgCount; i ++) + { + int nStrLen = strlen(ProcessData->ArgVect[i]) + 1; + ulAllocSize += nStrLen; + pnArgLengths[i] = nStrLen; + + INFO + ( + "argument %d: \"%s\", length %d\n", + i, + ProcessData->ArgVect[i], + nStrLen + ); + } + + /* environ */ + pnEnvVarsLengths = 0; + nEnvVarsCount = 0; + + for(i = 0; *(ProcessData->Environment)[i] != 0; i++) + { + int nStrLen = strlen(*(ProcessData->Environment)[i]) + 1; + ulAllocSize += nStrLen; + + nEnvVarsCount ++; + __realloc(pnEnvVarsLengths, nEnvVarsCount * sizeof(size_t)); + pnEnvVarsLengths[i] = nStrLen; + + INFO + ( + "environment variable %d: \"%s\", length %d\n", + i, + *(ProcessData->Environment)[i], + nStrLen + ); + } + + INFO("(%d environment variables were found)\n", nEnvVarsCount); + + /* current directory */ + ulAllocSize += ProcessData->CurDir.Length; + INFO + ( + "current directory: \"%Z\", length %d\n", + &ProcessData->CurDir, + ProcessData->CurDir.Length + ); + + /* root directory */ + ulAllocSize += ProcessData->RootPath.Length; + INFO + ( + "root directory: \"%Z\", length %d\n", + &ProcessData->RootPath, + ProcessData->RootPath.Length + ); + + /* file descriptors table */ + ulAllocSize += sizeof(__fildes_t) * ProcessData->FdTable.AllocatedDescriptors; + INFO + ( + "descriptors table contains %d allocated descriptors, combined length %d\n", + ProcessData->FdTable.AllocatedDescriptors, + sizeof(__fildes_t) * ProcessData->FdTable.AllocatedDescriptors + ); + + /* extra descriptors data */ + for(i = 0; ProcessData->FdTable.AllocatedDescriptors; i ++) + if(ProcessData->FdTable.Descriptors[i].ExtraData != NULL) + { + ulAllocSize += ProcessData->FdTable.Descriptors[i].ExtraDataSize; + + INFO + ( + "descriptor %d has %d bytes of associated data\n", + i, + ProcessData->FdTable.Descriptors[i].ExtraDataSize + ); + + } + + /* allocate return block */ + INFO("about to allocate %d bytes\n", ulAllocSize); + + nErrCode = NtAllocateVirtualMemory + ( + NtCurrentProcess(), + (PVOID *)&pspdProcessData, + 0, + &ulAllocSize, + MEM_COMMIT, + PAGE_READWRITE + ); + + /* failure */ + if(!NT_SUCCESS(nErrCode)) + { + ERR("NtAllocateVirtualMemory() failed with status 0x%08X\n", nErrCode); + __free(pnArgLengths); + __free(pnEnvVarsLengths); + *SerializedProcessData = 0; + return; + } + + INFO("%d bytes actually allocated\n", ulAllocSize); + pspdProcessData->AllocSize = ulAllocSize; + + /* copy data */ + /* static data */ + memcpy(&pspdProcessData->ProcessData, ProcessData, sizeof(__PDX_PDATA)); + + /* buffers */ + pBufferTail = &pspdProcessData->Buffer[0]; + INFO("buffer tail begins at 0x%08X\n", pBufferTail); + + /* argv */ + pspdProcessData->ProcessData.ArgVect = 0; + + for(i = 0; i < ProcessData->ArgCount; i ++) + { + INFO + ( + "copying %d bytes of argument %d (\"%s\") to 0x%08X\n", + pnArgLengths[i], + i, + ProcessData->ArgVect[i], + pBufferTail + ); + + strncpy(pBufferTail, ProcessData->ArgVect[i], pnArgLengths[i]); + pBufferTail += pnArgLengths[i]; + + INFO + ( + "buffer tail increased by %d bytes, new tail at 0x%08X\n", + pnArgLengths[i], + pBufferTail + ); + + } + + __free(pnArgLengths); + + /* environ */ + pspdProcessData->ProcessData.Environment = (char ***)nEnvVarsCount; + + for(i = 0; i < nEnvVarsCount; i ++) + { + INFO + ( + "copying %d bytes of environment variable %d (\"%s\") to 0x%08X\n", + pnEnvVarsLengths[i], + i, + ProcessData->Environment[i], + pBufferTail + ); + + strncpy(pBufferTail, *ProcessData->Environment[i], pnEnvVarsLengths[i]); + pBufferTail += pnEnvVarsLengths[i]; + + INFO + ( + "buffer tail increased by %d bytes, new tail at 0x%08X\n", + pnEnvVarsLengths[i], + pBufferTail + ); + } + + __free(pnEnvVarsLengths); + + /* current directory */ + INFO + ( + "copying %d bytes of current directory (\"%Z\") to 0x%08X\n", + ProcessData->CurDir.Length, + &ProcessData->CurDir, + pBufferTail + ); + + memcpy(pBufferTail, ProcessData->CurDir.Buffer, ProcessData->CurDir.Length); + pBufferTail += ProcessData->CurDir.Length; + + INFO + ( + "buffer tail increased by %d bytes, new tail at 0x%08X\n", + ProcessData->CurDir.Length, + pBufferTail + ); + + /* root directory */ + INFO + ( + "copying %d bytes of root directory (\"%Z\") to 0x%08X\n", + ProcessData->RootPath.Length, + &ProcessData->RootPath, + pBufferTail + ); + + memcpy + ( + pBufferTail, + ProcessData->RootPath.Buffer, + ProcessData->RootPath.Length + ); + + pBufferTail += ProcessData->RootPath.Length; + + INFO + ( + "buffer tail increased by %d bytes, new tail at 0x%08X\n", + ProcessData->RootPath.Length, + pBufferTail + ); + + /* file descriptors table */ + /* save the offset to the descriptors array */ + pspdProcessData->ProcessData.FdTable.Descriptors = + (PVOID)((ULONG)pBufferTail - (ULONG)pspdProcessData); + + INFO + ( + "descriptors table contains %d allocated descriptors, combined length %d\n", + ProcessData->FdTable.AllocatedDescriptors, + sizeof(__fildes_t) * ProcessData->FdTable.AllocatedDescriptors + ); + + memcpy + ( + pBufferTail, + ProcessData->FdTable.Descriptors, + sizeof(__fildes_t) * ProcessData->FdTable.AllocatedDescriptors + ); + + pBufferTail += + sizeof(__fildes_t) * ProcessData->FdTable.AllocatedDescriptors; + + INFO + ( + "buffer tail increased by %d bytes, new tail at 0x%08X\n", + sizeof(__fildes_t) * ProcessData->FdTable.AllocatedDescriptors, + pBufferTail + ); + + /* extra descriptors data */ + for(i = 0; ProcessData->FdTable.AllocatedDescriptors; i ++) + if(ProcessData->FdTable.Descriptors[i].ExtraData != 0) + { + INFO + ( + "descriptor %d has %d bytes of associated data\n", + i, + ProcessData->FdTable.Descriptors[i].ExtraDataSize + ); + + memcpy + ( + pBufferTail, + ProcessData->FdTable.Descriptors[i].ExtraData, + ProcessData->FdTable.Descriptors[i].ExtraDataSize + ); + + pBufferTail += ProcessData->FdTable.Descriptors[i].ExtraDataSize; + + INFO + ( + "buffer tail increased by %d bytes, new tail at 0x%08X\n", + ProcessData->FdTable.Descriptors[i].ExtraDataSize, + pBufferTail + ); + } + + /* success */ + *SerializedProcessData = pspdProcessData; +} + +NTSYSAPI +NTSTATUS +NTAPI +__PdxSpawnPosixProcess +( + OUT PHANDLE ProcessHandle, + OUT PHANDLE ThreadHandle, + IN POBJECT_ATTRIBUTES FileObjectAttributes, + IN POBJECT_ATTRIBUTES ProcessObjectAttributes, + IN HANDLE InheritFromProcessHandle, + IN __PPDX_PDATA ProcessData +) +{ + __PPDX_SERIALIZED_PDATA pspdProcessData; + IO_STATUS_BLOCK isbStatus; + PROCESS_BASIC_INFORMATION pbiProcessInfo; + ANSI_STRING strStartEntry; + PVOID pStartAddress; + INITIAL_TEB itInitialTeb; + CONTEXT ctxThreadContext; + CLIENT_ID ciClientId; + NTSTATUS nErrCode; + HANDLE hExeFile; + HANDLE hExeImage; + HANDLE hProcess; + HANDLE hThread; + PVOID pDestBuffer; + ULONG nDestBufferSize; + ULONG nCurFilDesOffset; + int i; + + /* STEP 1: map executable image in memory */ + /* 1.1: open the file for execution */ + nErrCode = NtOpenFile + ( + &hExeFile, + SYNCHRONIZE | FILE_EXECUTE, + FileObjectAttributes, + &isbStatus, + FILE_SHARE_READ | FILE_SHARE_DELETE, + FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE + ); + + /* failure */ + if(!NT_SUCCESS(nErrCode)) + { + ERR("NtOpenFile() failed with status 0x%08X\n", nErrCode); + return (nErrCode); + } + + /* 1.2: create a memory section for the file */ + nErrCode = NtCreateSection + ( + &hExeImage, + SECTION_ALL_ACCESS, + NULL, + 0, + PAGE_EXECUTE, + SEC_IMAGE, + hExeFile + ); + + /* close file handle (not needed anymore) */ + NtClose(hExeFile); + + /* failure */ + if(!NT_SUCCESS(nErrCode)) + { + ERR("NtCreateSection() failed with status 0x%08X\n", nErrCode); + return (nErrCode); + } + + /* STEP 2: create process */ + nErrCode = NtCreateProcess + ( + &hProcess, + PROCESS_ALL_ACCESS, + ProcessObjectAttributes, + InheritFromProcessHandle, + FALSE, + hExeImage, + NULL, + NULL + ); + + /* close image handle (not needed anymore) */ + NtClose(hExeImage); + + /* failure */ + if(!NT_SUCCESS(nErrCode)) + { + ERR("NtCreateProcess() failed with status 0x%08X\n", nErrCode); + return (nErrCode); + } + + /* STEP 3: write process environment */ + /* 3.1: serialize the process data for transfer */ + /* FIXME: the serialized data can be allocated and written directly in the + destination process */ + __PdxSerializeProcessData(ProcessData, &pspdProcessData); + + /* 3.1.1: adjust some fields */ + pspdProcessData->ProcessData.Spawned = TRUE; + + /* 3.2: allocate memory in the destination process */ + nDestBufferSize = pspdProcessData->AllocSize; + + nErrCode = NtAllocateVirtualMemory + ( + hProcess, + &pDestBuffer, + 0, + &nDestBufferSize, + MEM_COMMIT, + PAGE_READWRITE + ); + + /* failure */ + if(!NT_SUCCESS(nErrCode)) + { + ERR("NtAllocateVirtualMemory() failed with status 0x%08X\n", nErrCode); + goto undoPData; + } + + /* 3.3: get pointer to the PEB */ + nErrCode = NtQueryInformationProcess + ( + hProcess, + ProcessBasicInformation, + &pbiProcessInfo, + sizeof(pbiProcessInfo), + NULL + ); + + /* failure */ + if(!NT_SUCCESS(nErrCode)) + { + ERR("NtQueryInformationProcess() failed with status 0x%08X\n", nErrCode); + goto undoPData; + } + + /* 3.4: write pointer to process data in the SubSystemData field of the PEB */ + nErrCode = NtWriteVirtualMemory + ( + hProcess, + (PVOID)((ULONG)pbiProcessInfo.PebBaseAddress + offsetof(PEB, SubSystemData)), + &pDestBuffer, + sizeof(PVOID), + NULL + ); + + /* failure */ + if(!NT_SUCCESS(nErrCode)) + { + ERR("NtWriteVirtualMemory() failed with status 0x%08X\n", nErrCode); + goto undoPData; + } + + /* 3.5: write the process data */ + nErrCode = NtWriteVirtualMemory + ( + hProcess, + pDestBuffer, + pspdProcessData, + pspdProcessData->AllocSize, + NULL + ); + +undoPData: + /* deallocate the temporary data block in the current process */ + NtFreeVirtualMemory + ( + NtCurrentProcess(), + (PVOID *)&pspdProcessData, + 0, + MEM_RELEASE + ); + + /* failure */ + if(!NT_SUCCESS(nErrCode)) + return (nErrCode); + + /* STEP 4: duplicate handles */ + /* 4.1: handles in the structure itself */ + /* 4.1.1: root directory */ + nErrCode = NtDuplicateObject + ( + NtCurrentProcess(), + ProcessData->RootHandle, + hProcess, + (PHANDLE)((ULONG)pDestBuffer + offsetof(__PDX_PDATA, RootHandle)), + 0, + 0, + DUPLICATE_SAME_ACCESS | 4 /* | DUPLICATE_SAME_ATTRIBUTES */ /* FIXME */ + ); + + /* failure */ + if(!NT_SUCCESS(nErrCode)) + { + ERR("NtDuplicateObject() failed with status 0x%08X\n", nErrCode); + goto failProcess; + } + + /* 4.2: file descriptors table */ + for + ( + /* pspdProcessData->ProcessData.FdTable.Descriptors contains the offset to + the descriptors array inside pspdProcessData->Buffer[], that is to the + first element of the array */ + i = 0, + nCurFilDesOffset = (ULONG)pspdProcessData->ProcessData.FdTable.Descriptors; + /* iterate through all allocated descriptors */ + i < ProcessData->FdTable.AllocatedDescriptors; + /* at every step, go on to next input descriptor, and increase the offset to + the next output descriptor */ + i ++, nCurFilDesOffset += sizeof(__fildes_t) + ) + /* FIXME? check the table's bitmap instead? */ + if(ProcessData->FdTable.Descriptors[i].FileHandle != NULL) + { + /* duplicate the source handle, ProcessData->FdTable.Descriptors[i], from + the current process into the process identified by hProcess, at an + address calculated by adding to the serialized data block base address: + - the offset to the current descriptor + - the offset to the handle field of the descriptor */ + nErrCode = NtDuplicateObject + ( + NtCurrentProcess(), + ProcessData->FdTable.Descriptors[i].FileHandle, + hProcess, + (PHANDLE)( + (ULONG)pDestBuffer + nCurFilDesOffset + offsetof(__fildes_t, FileHandle) + ), + 0, + 0, + DUPLICATE_SAME_ACCESS | 4 /* | DUPLICATE_SAME_ATTRIBUTES */ /* FIXME */ + ); + + /* failure */ + if(!NT_SUCCESS(nErrCode)) + { + ERR("NtDuplicateObject() failed with status 0x%08X\n", nErrCode); + goto failProcess; + } + } + + /* STEP 5: create first thread */ + /* 5.1: get thunk routine's address */ + RtlInitializeAnsiString(&strStartEntry, "LdrInitializeThunk"); + + nErrCode = LdrGetProcedureAddress + ( + (PVOID)NTDLL_BASE, + &strStartEntry, + 0, + &pStartAddress + ); + + /* failure */ + if(!NT_SUCCESS(nErrCode)) + { + ERR("LdrGetProcedureAddress() failed with status 0x%08X\n", nErrCode); + goto failProcess; + } + + /* 5.2: set up the initial TEB */ + itInitialTeb.StackAllocate = NULL; + + /* FIXME: allow the caller to specify these values */ + itInitialTeb.StackReserve = 0x100000; + itInitialTeb.StackCommit = itInitialTeb.StackReserve - PAGESIZE; + + /* guard page */ + itInitialTeb.StackCommit += PAGESIZE; + + /* 5.2.1: set up the stack */ + /* 5.2.1.1: reserve the stack */ + nErrCode = NtAllocateVirtualMemory + ( + hProcess, + &itInitialTeb.StackAllocate, + 0, + &itInitialTeb.StackReserve, + MEM_RESERVE, + PAGE_READWRITE + ); + + /* failure */ + if(!NT_SUCCESS(nErrCode)) + { + ERR("NtAllocateVirtualMemory() failed with status 0x%08X\n", nErrCode); + goto failProcess; + } + + itInitialTeb.StackBase = + (PVOID)((ULONG)itInitialTeb.StackAllocate + itInitialTeb.StackReserve); + + itInitialTeb.StackLimit = + (PVOID)((ULONG)itInitialTeb.StackBase - itInitialTeb.StackCommit); + + /* 5.2.1.2: commit the stack */ + nErrCode = NtAllocateVirtualMemory + ( + hProcess, + &itInitialTeb.StackLimit, + 0, + &itInitialTeb.StackCommit, + MEM_COMMIT, + PAGE_READWRITE + ); + + /* failure */ + if(!NT_SUCCESS(nErrCode)) + { + ERR("NtAllocateVirtualMemory() failed with status 0x%08X\n", nErrCode); + goto failProcess; + } + + /* 5.2.1.3: set up the guard page */ + nErrCode = NtProtectVirtualMemory + ( + hProcess, + itInitialTeb.StackLimit, + PAGESIZE, + PAGE_GUARD | PAGE_READWRITE, + NULL + ); + + /* failure */ + if(!NT_SUCCESS(nErrCode)) + { + ERR("NtProtectVirtualMemory() failed with status 0x%08X\n", nErrCode); + goto failProcess; + } + + /* 5.2.1.4: initialize the thread context */ + memset(&ctxThreadContext, 0, sizeof(ctxThreadContext)); + + ctxThreadContext.Eip = (ULONG)pStartAddress; + ctxThreadContext.SegGs = USER_DS; + ctxThreadContext.SegFs = USER_DS; + ctxThreadContext.SegEs = USER_DS; + ctxThreadContext.SegDs = USER_DS; + ctxThreadContext.SegCs = USER_CS; + ctxThreadContext.SegSs = USER_DS; + /* skip five doublewords (four - unknown - parameters for LdrInitializeThunk, + and the return address) */ + ctxThreadContext.Esp = (ULONG)itInitialTeb.StackBase - 5 * 4; + ctxThreadContext.EFlags = (1 << 1) + (1 << 9); + + /* 5.3: create the thread object */ + nErrCode = NtCreateThread + ( + NULL, + THREAD_ALL_ACCESS, + NULL, + hProcess, + &ciClientId, + &ctxThreadContext, + &itInitialTeb, + FALSE + ); + + /* failure */ + if(!NT_SUCCESS(nErrCode)) + { + ERR("NtCreateThread() failed with status 0x%08X\n", nErrCode); + goto failProcess; + } + + /* success */ + return (STATUS_SUCCESS); + + /* failure */ +failProcess: + NtTerminateProcess + ( + hProcess, + nErrCode + ); + + return (nErrCode); +} + +/* EOF */ +