reactos/posix/lib/psxdll/misc/spawn.c
2002-12-26 18:14:38 +00:00

631 lines
14 KiB
C

/* $Id: spawn.c,v 1.9 2002/12/26 18:14:36 robd 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 <noog@libero.it>
* 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 <ddk/ntddk.h>
#include <ntdll/base.h>
#include <napi/i386/segment.h>
#include <ntdll/rtl.h>
#include <ntdll/ldr.h>
#include <stddef.h>
#include <string.h>
#include <inttypes.h>
#include <unistd.h>
#include <psx/debug.h>
#include <psx/pdata.h>
#include <psx/spawn.h>
#include <psx/stdlib.h>
#include <windows.h>
typedef struct _PORT_MESSAGE {
USHORT DataSize;
USHORT MessageSize;
USHORT MessageType;
USHORT VirtualRangesOffset;
CLIENT_ID ClientId;
ULONG MessageId;
ULONG SectionSize;
// UCHAR Data[];
} PORT_MESSAGE, *PPORT_MESSAGE;
NTSTATUS STDCALL CsrClientCallServer(
IN PVOID Message,
IN PVOID Unknown,
IN ULONG Opcode,
IN ULONG Size
);
NTSTATUS STDCALL __PdxSpawnPosixProcess
(
OUT PHANDLE ProcessHandle,
OUT PHANDLE ThreadHandle,
IN POBJECT_ATTRIBUTES FileObjectAttributes,
IN POBJECT_ATTRIBUTES ProcessObjectAttributes,
IN HANDLE InheritFromProcessHandle,
IN __PPDX_PDATA ProcessData
)
{
struct CSRSS_MESSAGE {
ULONG Unknown1;
ULONG Opcode;
ULONG Status;
ULONG Unknown2;
};
struct __tagcsrmsg{
PORT_MESSAGE PortMessage;
struct CSRSS_MESSAGE CsrssMessage;
PROCESS_INFORMATION ProcessInformation;
CLIENT_ID Debugger;
ULONG CreationFlags;
ULONG VdmInfo[2];
} csrmsg;
__PPDX_SERIALIZED_PDATA pspdProcessData;
IO_STATUS_BLOCK isbStatus;
PROCESS_BASIC_INFORMATION pbiProcessInfo;
INITIAL_TEB itInitialTeb;
PRTL_USER_PROCESS_PARAMETERS pppProcessParameters;
SECTION_IMAGE_INFORMATION siiInfo;
CONTEXT ctxThreadContext;
CLIENT_ID ciClientId;
NTSTATUS nErrCode;
HANDLE hExeFile;
HANDLE hExeImage;
HANDLE hProcess;
PVOID pPdataBuffer = 0;
PVOID pParamsBuffer = 0;
ULONG nDestBufferSize;
ULONG nCurFilDesOffset;
ULONG nVirtualSize;
ULONG nCommitSize;
PVOID pCommitBottom;
ULONG nOldProtect;
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 an image 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);
}
/* 1.3: get section image information */
nErrCode = NtQuerySection
(
hExeImage,
SectionImageInformation,
&siiInfo,
sizeof(siiInfo),
NULL
);
/* failure */
if(!NT_SUCCESS(nErrCode))
{
ERR("NtCreateSection() failed with status 0x%08X\n", nErrCode);
NtClose(hExeImage);
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 and process parameters */
/* 3.1: convert process data into process parameters */
__PdxProcessDataToProcessParameters
(
&pppProcessParameters,
ProcessData,
FileObjectAttributes->ObjectName
);
/* 3.2: serialize the process data for transfer */
/* FIXME: the serialized data can be allocated and written directly in the
destination process */
__PdxSerializeProcessData(ProcessData, &pspdProcessData);
/* 3.2.1: adjust some fields */
pspdProcessData->ProcessData.Spawned = TRUE;
/* 3.3: allocate memory in the destination process */
/* 3.3.1: process data */
nDestBufferSize = pspdProcessData->AllocSize;
nErrCode = NtAllocateVirtualMemory
(
hProcess,
&pPdataBuffer,
0,
&nDestBufferSize,
MEM_COMMIT,
PAGE_READWRITE
);
/* failure */
if(!NT_SUCCESS(nErrCode))
{
ERR("NtAllocateVirtualMemory() failed with status 0x%08X\n", nErrCode);
goto undoPData;
}
/* 3.3.2: process parameters */
nDestBufferSize = pppProcessParameters->Size;
nErrCode = NtAllocateVirtualMemory
(
hProcess,
&pParamsBuffer,
0,
&nDestBufferSize,
MEM_COMMIT,
PAGE_READWRITE
);
/* failure */
if(!NT_SUCCESS(nErrCode))
{
ERR("NtAllocateVirtualMemory() failed with status 0x%08X\n", nErrCode);
goto undoPData;
}
/* 3.4: 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.5: write pointers in the PEB */
/* 3.5.1: process data */
nErrCode = NtWriteVirtualMemory
(
hProcess,
(PVOID)((ULONG)pbiProcessInfo.PebBaseAddress + offsetof(PEB, SubSystemData)),
&pPdataBuffer,
sizeof(PVOID),
NULL
);
/* failure */
if(!NT_SUCCESS(nErrCode))
{
ERR("NtWriteVirtualMemory() failed with status 0x%08X\n", nErrCode);
goto undoPData;
}
/* 3.5.2: process parameters */
nErrCode = NtWriteVirtualMemory
(
hProcess,
(PVOID)((ULONG)pbiProcessInfo.PebBaseAddress + offsetof(PEB, ProcessParameters)),
&pParamsBuffer,
sizeof(PVOID),
NULL
);
/* failure */
if(!NT_SUCCESS(nErrCode))
{
ERR("NtWriteVirtualMemory() failed with status 0x%08X\n", nErrCode);
goto undoPData;
}
/* 3.6: write data */
/* 3.6.1: process data */
nErrCode = NtWriteVirtualMemory
(
hProcess,
pPdataBuffer,
pspdProcessData,
pspdProcessData->AllocSize,
NULL
);
/* failure */
if(!NT_SUCCESS(nErrCode))
{
ERR("NtWriteVirtualMemory() failed with status 0x%08X\n", nErrCode);
goto undoPData;
}
/* 3.6.2 process parameters */
nErrCode = NtWriteVirtualMemory
(
hProcess,
pParamsBuffer,
pppProcessParameters,
pppProcessParameters->Size,
NULL
);
/* failure */
if(!NT_SUCCESS(nErrCode))
{
ERR("NtWriteVirtualMemory() failed with status 0x%08X\n", nErrCode);
goto undoPData;
}
undoPData:
/* deallocate the temporary data block in the current process */
NtFreeVirtualMemory
(
NtCurrentProcess(),
(PVOID *)&pspdProcessData,
0,
MEM_RELEASE
);
/* destroy process parameters */
RtlDestroyProcessParameters(pppProcessParameters);
/* failure */
if(!NT_SUCCESS(nErrCode))
goto failProcess;
/* STEP 4: duplicate handles */
/* 4.1: handles in the structure itself */
/* 4.1.1: root directory */
nErrCode = NtDuplicateObject
(
NtCurrentProcess(),
ProcessData->RootHandle,
hProcess,
(PHANDLE)((ULONG)pPdataBuffer + 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)pPdataBuffer + 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;
}
/* duplicate standard handles */
/* standard input */
if(i == STDIN_FILENO)
{
nErrCode = NtDuplicateObject
(
NtCurrentProcess(),
ProcessData->FdTable.Descriptors[i].FileHandle,
hProcess,
(PHANDLE)((ULONG)pParamsBuffer + offsetof(RTL_USER_PROCESS_PARAMETERS, hStdInput)),
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;
}
}
/* standard output */
else if(i == STDOUT_FILENO)
{
nErrCode = NtDuplicateObject
(
NtCurrentProcess(),
ProcessData->FdTable.Descriptors[i].FileHandle,
hProcess,
(PHANDLE)((ULONG)pParamsBuffer + offsetof(RTL_USER_PROCESS_PARAMETERS, hStdOutput)),
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;
}
}
/* standard error */
else if(i == STDERR_FILENO)
{
nErrCode = NtDuplicateObject
(
NtCurrentProcess(),
ProcessData->FdTable.Descriptors[i].FileHandle,
hProcess,
(PHANDLE)((ULONG)pParamsBuffer + offsetof(RTL_USER_PROCESS_PARAMETERS, hStdError)),
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: set up the stack */
itInitialTeb.StackAllocate = NULL;
nVirtualSize = 0x100000;
nCommitSize = 0x100000 - PAGE_SIZE;
/* 5.1.1: reserve the stack */
nErrCode = NtAllocateVirtualMemory
(
hProcess,
&itInitialTeb.StackAllocate,
0,
&nVirtualSize,
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 + nVirtualSize);
itInitialTeb.StackLimit =
(PVOID)((ULONG)itInitialTeb.StackBase - nCommitSize);
/* 5.1.2: commit the stack */
nVirtualSize = nCommitSize + PAGE_SIZE;
pCommitBottom =
(PVOID)((ULONG)itInitialTeb.StackBase - nVirtualSize);
nErrCode = NtAllocateVirtualMemory
(
hProcess,
&pCommitBottom,
0,
&nVirtualSize,
MEM_COMMIT,
PAGE_READWRITE
);
/* failure */
if(!NT_SUCCESS(nErrCode))
{
ERR("NtAllocateVirtualMemory() failed with status 0x%08X\n", nErrCode);
goto failProcess;
}
/* 5.1.3: set up the guard page */
nVirtualSize = PAGE_SIZE;
nErrCode = NtProtectVirtualMemory
(
hProcess,
&pCommitBottom,
&nVirtualSize,
PAGE_GUARD | PAGE_READWRITE,
&nOldProtect
);
/* failure */
if(!NT_SUCCESS(nErrCode))
{
ERR("NtProtectVirtualMemory() failed with status 0x%08X\n", nErrCode);
goto failProcess;
}
/* 5.2: initialize the thread context */
memset(&ctxThreadContext, 0, sizeof(ctxThreadContext));
ctxThreadContext.Eip = (ULONG)siiInfo.EntryPoint;
ctxThreadContext.SegGs = USER_DS;
ctxThreadContext.SegFs = USER_DS;
ctxThreadContext.SegEs = USER_DS;
ctxThreadContext.SegDs = USER_DS;
ctxThreadContext.SegCs = USER_CS;
ctxThreadContext.SegSs = USER_DS;
ctxThreadContext.Esp = (ULONG)itInitialTeb.StackBase - 4;
ctxThreadContext.EFlags = (1 << 1) + (1 << 9);
/* 5.3: create the thread object */
nErrCode = NtCreateThread
(
ThreadHandle,
THREAD_ALL_ACCESS,
NULL,
hProcess,
&ciClientId,
&ctxThreadContext,
&itInitialTeb,
TRUE /* FIXME: the thread is only created in suspended state for easier
debugging. This behavior is subject to future changes */
);
/* failure */
if(!NT_SUCCESS(nErrCode))
{
ERR("NtCreateThread() failed with status 0x%08X\n", nErrCode);
goto failProcess;
}
/* 6: register the process with the Win32 subsystem (temporary code for
debugging purposes) */
memset(&csrmsg, 0, sizeof(csrmsg));
//csrmsg.PortMessage = {0};
//csrmsg.CsrssMessage = {0};
csrmsg.ProcessInformation.hProcess = hProcess;
csrmsg.ProcessInformation.hThread = *ThreadHandle;
csrmsg.ProcessInformation.dwProcessId = (DWORD)ciClientId.UniqueProcess;
csrmsg.ProcessInformation.dwThreadId = (DWORD)ciClientId.UniqueThread;
//csrmsg.Debugger = {0};
//csrmsg.CreationFlags = 0;
//csrmsg.VdmInfo = {0};
nErrCode = CsrClientCallServer(&csrmsg, 0, 0x10000, 0x24);
/* failure */
if(!NT_SUCCESS(nErrCode))
{
ERR("CsrClientCallServer() failed with status 0x%08X\n", nErrCode);
goto failProcess;
}
nErrCode = NtResumeThread(*ThreadHandle, NULL);
/* failure */
if(!NT_SUCCESS(nErrCode))
{
ERR("NtResumeThread() failed with status 0x%08X\n", nErrCode);
goto failProcess;
}
/* success */
return (STATUS_SUCCESS);
/* failure */
failProcess:
NtTerminateProcess
(
hProcess,
nErrCode
);
return (nErrCode);
}
/* EOF */