/* $Id: spawn.c,v 1.2 2002/03/07 06:08:00 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 #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 */ RtlInitAnsiString(&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 */