reactos/posix/lib/psxdll/unistd/fork.c
2002-10-29 04:45:58 +00:00

251 lines
5.2 KiB
C

/* $Id: fork.c,v 1.3 2002/10/29 04:45:46 rex Exp $
*/
/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS POSIX+ Subsystem
* FILE: subsys/psx/lib/psxdll/unistd/fork.c
* PURPOSE: create a new process
* PROGRAMMER: KJK::Hyperion <noog@libero.it>
* UPDATE HISTORY:
* 14/05/2002: Created
*/
#include <ddk/ntddk.h>
#include <napi/teb.h>
#include <sys/types.h>
#include <stddef.h>
#include <unistd.h>
#include <errno.h>
#include <psx/debug.h>
#include <psx/errno.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;
struct CSRSS_MESSAGE {
ULONG Unknown1;
ULONG Opcode;
ULONG Status;
ULONG Unknown2;
};
NTSTATUS STDCALL CsrClientCallServer(
IN PVOID Message,
IN PVOID Unknown,
IN ULONG Opcode,
IN ULONG Size
);
pid_t fork(void)
{
NTSTATUS nErrCode;
CONTEXT ctxThreadContext;
HANDLE hProcess;
HANDLE hThread;
INITIAL_TEB itInitialTeb;
CLIENT_ID ciClientId;
MEMORY_BASIC_INFORMATION mbiStackInfo;
THREAD_BASIC_INFORMATION tbiThreadInfo;
struct __tagcsrmsg{
PORT_MESSAGE PortMessage;
struct CSRSS_MESSAGE CsrssMessage;
PROCESS_INFORMATION ProcessInformation;
CLIENT_ID Debugger;
ULONG CreationFlags;
ULONG VdmInfo[2];
} csrmsg;
/* STEP 1: Duplicate current process */
nErrCode = NtCreateProcess
(
&hProcess,
PROCESS_ALL_ACCESS,
NULL,
NtCurrentProcess(),
TRUE,
0,
0,
0
);
/* failure */
if(!NT_SUCCESS(nErrCode))
{
ERR("NtCreateProcess() failed with status 0x%08X\n", nErrCode);
goto fail;
}
/* STEP 2: Duplicate current thread */
/* 2.1: duplicate registers */
ctxThreadContext.ContextFlags =
CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS | CONTEXT_FLOATING_POINT;
/* get the current thread's registers */
nErrCode = NtGetContextThread(NtCurrentThread(), &ctxThreadContext);
/* failure */
if(!NT_SUCCESS(nErrCode))
{
ERR("NtGetContextThread() failed with status 0x%08X\n", nErrCode);
goto cleanup_and_fail;
}
/* redirect the child process to the child_branch label (see 4.3 below) */
ctxThreadContext.Eip = (ULONG)&&child_branch;
/* 2.2: duplicate stack */
/* get stack base and size */
nErrCode = NtQueryVirtualMemory
(
NtCurrentProcess(),
(PVOID)ctxThreadContext.Esp,
MemoryBasicInformation,
&mbiStackInfo,
sizeof(mbiStackInfo),
0
);
/* failure */
if(!NT_SUCCESS(nErrCode))
{
ERR("NtQueryVirtualMemory() failed with status 0x%08X\n", nErrCode);
goto cleanup_and_fail;
}
itInitialTeb.StackCommit = 0;
itInitialTeb.StackReserve = 0;
itInitialTeb.StackBase = (PVOID)((ULONG)(mbiStackInfo.BaseAddress) + mbiStackInfo.RegionSize);
itInitialTeb.StackLimit = mbiStackInfo.BaseAddress;
itInitialTeb.StackAllocate = mbiStackInfo.AllocationBase;
/* 2.3: create duplicate thread */
nErrCode = NtCreateThread
(
&hThread,
THREAD_ALL_ACCESS,
NULL,
hProcess,
(CLIENT_ID *)&ciClientId,
&ctxThreadContext,
&itInitialTeb,
TRUE
);
/* failure */
if(!NT_SUCCESS(nErrCode))
{
ERR("NtCreateThread() failed with status 0x%08X\n", nErrCode);
goto cleanup_and_fail;
}
/* 2.4: duplicate the TEB */
/* store the client id in the child thread's stack (see 4.3b) */
nErrCode = NtWriteVirtualMemory
(
hProcess,
&ciClientId,
&ciClientId,
sizeof(ciClientId),
0
);
/* failure */
if(!NT_SUCCESS(nErrCode))
{
ERR("NtWriteVirtualMemory() failed with status 0x%08X\n", nErrCode);
goto cleanup_and_fail;
}
/* get the child thread's TEB base */
nErrCode = NtQueryInformationThread
(
hThread,
ThreadBasicInformation,
&tbiThreadInfo,
sizeof(tbiThreadInfo),
0
);
/* failure */
if(!NT_SUCCESS(nErrCode))
{
ERR("NtQueryInformationThread() failed with status 0x%08X\n", nErrCode);
goto cleanup_and_fail;
}
/* copy the TEB */
nErrCode = NtWriteVirtualMemory
(
hProcess,
tbiThreadInfo.TebBaseAddress,
NtCurrentTeb(),
sizeof(TEB),
0
);
/* failure */
if(!NT_SUCCESS(nErrCode))
{
ERR("NtWriteVirtualMemory() failed with status 0x%08X\n", nErrCode);
goto cleanup_and_fail;
}
/* STEP 3: Call Win32 subsystem */
memset(&csrmsg, 0, sizeof(csrmsg));
csrmsg.ProcessInformation.hProcess = hProcess;
csrmsg.ProcessInformation.hThread = hThread;
csrmsg.ProcessInformation.dwProcessId = (DWORD)ciClientId.UniqueProcess;
csrmsg.ProcessInformation.dwThreadId = (DWORD)ciClientId.UniqueThread;
nErrCode = CsrClientCallServer(&csrmsg, 0, 0x10000, 0x24);
/* failure */
if(!NT_SUCCESS(nErrCode))
{
ERR("CsrClientCallServer() failed with status 0x%08X\n", nErrCode);
goto cleanup_and_fail;
}
/* STEP 4: Finalization */
/* 4.1: resume thread */
nErrCode = NtResumeThread(hThread, 0);
/* 4.2: close superfluous handles */
NtClose(hProcess);
NtClose(hThread);
/* 4.3: (parent) return the child process id */
return ((pid_t)(ciClientId.UniqueProcess));
/* 4.3b: (child) cleanup and return 0 */
child_branch:
/* restore the thread and process id in the TEB */
memcpy(&NtCurrentTeb()->Cid, &ciClientId, sizeof(ciClientId));
/* return 0 */
return (0);
cleanup_and_fail:
NtTerminateProcess(hProcess, nErrCode);
fail:
errno = __status_to_errno(nErrCode);
return (-1);
}
/* EOF */