Preliminary code for fork()
[reactos.git] / posix / lib / psxdll / unistd / fork.c
1 /* $Id: fork.c,v 1.1 2002/05/17 02:12:55 hyperion Exp $
2 */
3 /*
4 * COPYRIGHT: See COPYING in the top level directory
5 * PROJECT: ReactOS POSIX+ Subsystem
6 * FILE: subsys/psx/lib/psxdll/unistd/fork.c
7 * PURPOSE: create a new process
8 * PROGRAMMER: KJK::Hyperion <noog@libero.it>
9 * UPDATE HISTORY:
10 * 14/05/2002: Created
11 */
12
13 #include <ddk/ntddk.h>
14 #include <napi/teb.h>
15 #include <sys/types.h>
16 #include <stddef.h>
17 #include <unistd.h>
18 #include <errno.h>
19 #include <psx/debug.h>
20 #include <psx/errno.h>
21
22 #include <windows.h>
23
24 typedef struct _PORT_MESSAGE {
25 USHORT DataSize;
26 USHORT MessageSize;
27 USHORT MessageType;
28 USHORT VirtualRangesOffset;
29 CLIENT_ID ClientId;
30 ULONG MessageId;
31 ULONG SectionSize;
32 /* UCHAR Data[]; */
33 } PORT_MESSAGE, *PPORT_MESSAGE;
34
35 struct CSRSS_MESSAGE {
36 ULONG Unknown1;
37 ULONG Opcode;
38 ULONG Status;
39 ULONG Unknown2;
40 };
41
42 NTSTATUS STDCALL CsrClientCallServer(
43 IN PVOID Message,
44 IN PVOID Unknown,
45 IN ULONG Opcode,
46 IN ULONG Size
47 );
48
49 pid_t fork(void)
50 {
51 NTSTATUS nErrCode;
52 CONTEXT ctxThreadContext;
53 HANDLE hProcess;
54 HANDLE hThread;
55 INITIAL_TEB itInitialTeb;
56 CLIENT_ID ciClientId;
57 MEMORY_BASIC_INFORMATION mbiStackInfo;
58 THREAD_BASIC_INFORMATION tbiThreadInfo;
59
60 struct __tagcsrmsg{
61 PORT_MESSAGE PortMessage;
62 struct CSRSS_MESSAGE CsrssMessage;
63 PROCESS_INFORMATION ProcessInformation;
64 CLIENT_ID Debugger;
65 ULONG CreationFlags;
66 ULONG VdmInfo[2];
67 } csrmsg;
68
69 /* STEP 1: Duplicate current process */
70 nErrCode = NtCreateProcess
71 (
72 &hProcess,
73 PROCESS_ALL_ACCESS,
74 NULL,
75 NtCurrentProcess(),
76 TRUE,
77 0,
78 0,
79 0
80 );
81
82 /* failure */
83 if(!NT_SUCCESS(nErrCode))
84 {
85 ERR("NtCreateProcess() failed with status 0x%08X\n", nErrCode);
86 goto fail;
87 }
88
89 /* STEP 2: Duplicate current thread */
90 /* 2.1: duplicate registers */
91 ctxThreadContext.ContextFlags =
92 CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS | CONTEXT_FLOATING_POINT;
93
94 /* get the current thread's registers */
95 nErrCode = NtGetContextThread(NtCurrentThread(), &ctxThreadContext);
96
97 /* failure */
98 if(!NT_SUCCESS(nErrCode))
99 {
100 ERR("NtGetContextThread() failed with status 0x%08X\n", nErrCode);
101 goto cleanup_and_fail;
102 }
103
104 /* redirect the child process to the child_branch label (see 4.3 below) */
105 ctxThreadContext.Eip = (ULONG)&&child_branch;
106
107 /* 2.2: duplicate stack */
108 /* get stack base and size */
109 nErrCode = NtQueryVirtualMemory
110 (
111 NtCurrentProcess(),
112 (PVOID)ctxThreadContext.Esp,
113 MemoryBasicInformation,
114 &mbiStackInfo,
115 sizeof(mbiStackInfo),
116 0
117 );
118
119 /* failure */
120 if(!NT_SUCCESS(nErrCode))
121 {
122 ERR("NtQueryVirtualMemory() failed with status 0x%08X\n", nErrCode);
123 goto cleanup_and_fail;
124 }
125
126 itInitialTeb.StackCommit = 0;
127 itInitialTeb.StackReserve = 0;
128 itInitialTeb.StackBase = (PVOID)((ULONG)(mbiStackInfo.BaseAddress) + mbiStackInfo.RegionSize);
129 itInitialTeb.StackLimit = mbiStackInfo.BaseAddress;
130 itInitialTeb.StackAllocate = mbiStackInfo.AllocationBase;
131
132 /* 2.3: create duplicate thread */
133 nErrCode = NtCreateThread
134 (
135 &hThread,
136 THREAD_ALL_ACCESS,
137 NULL,
138 hProcess,
139 (CLIENT_ID *)&ciClientId,
140 &ctxThreadContext,
141 &itInitialTeb,
142 TRUE
143 );
144
145 /* failure */
146 if(!NT_SUCCESS(nErrCode))
147 {
148 ERR("NtCreateThread() failed with status 0x%08X\n", nErrCode);
149 goto cleanup_and_fail;
150 }
151
152 /* 2.4: duplicate the TEB */
153 /* store the client id in the child thread's stack (see 4.3b) */
154 nErrCode = NtWriteVirtualMemory
155 (
156 hProcess,
157 &ciClientId,
158 &ciClientId,
159 sizeof(ciClientId),
160 0
161 );
162
163 /* failure */
164 if(!NT_SUCCESS(nErrCode))
165 {
166 ERR("NtWriteVirtualMemory() failed with status 0x%08X\n", nErrCode);
167 goto cleanup_and_fail;
168 }
169
170 /* get the child thread's TEB base */
171 nErrCode = NtQueryInformationThread
172 (
173 hThread,
174 ThreadBasicInformation,
175 &tbiThreadInfo,
176 sizeof(tbiThreadInfo),
177 0
178 );
179
180 /* failure */
181 if(!NT_SUCCESS(nErrCode))
182 {
183 ERR("NtQueryInformationThread() failed with status 0x%08X\n", nErrCode);
184 goto cleanup_and_fail;
185 }
186
187 /* copy the TEB */
188 nErrCode = NtWriteVirtualMemory
189 (
190 hProcess,
191 tbiThreadInfo.TebBaseAddress,
192 NtCurrentTeb(),
193 sizeof(TEB),
194 0
195 );
196
197 /* failure */
198 if(!NT_SUCCESS(nErrCode))
199 {
200 ERR("NtWriteVirtualMemory() failed with status 0x%08X\n", nErrCode);
201 goto cleanup_and_fail;
202 }
203
204 /* STEP 3: Call Win32 subsystem */
205 memset(&csrmsg, 0, sizeof(csrmsg));
206
207 csrmsg.ProcessInformation.hProcess = hProcess;
208 csrmsg.ProcessInformation.hThread = hThread;
209 csrmsg.ProcessInformation.dwProcessId = (DWORD)ciClientId.UniqueProcess;
210 csrmsg.ProcessInformation.dwThreadId = (DWORD)ciClientId.UniqueThread;
211
212 nErrCode = CsrClientCallServer(&csrmsg, 0, 0x10000, 0x24);
213
214 /* failure */
215 if(!NT_SUCCESS(nErrCode))
216 {
217 ERR("CsrClientCallServer() failed with status 0x%08X\n", nErrCode);
218 goto cleanup_and_fail;
219 }
220
221 /* STEP 4: Finalization */
222 /* 4.1: resume thread */
223 nErrCode = NtResumeThread(hThread, 0);
224
225 /* 4.2: close superfluous handles */
226 NtClose(hProcess);
227 NtClose(hThread);
228
229 /* 4.3: (parent) return the child process id */
230 return ((pid_t)(ciClientId.UniqueProcess));
231
232 /* 4.3b: (child) cleanup and return 0 */
233 child_branch:
234 /* restore the thread and process id in the TEB */
235 memcpy(&NtCurrentTeb()->Cid, &ciClientId, sizeof(ciClientId));
236
237 /* return 0 */
238 return (0);
239
240 cleanup_and_fail:
241 NtTerminateProcess(hProcess, nErrCode);
242
243 fail:
244 errno = __status_to_errno(nErrCode);
245 return (-1);
246
247 }
248
249 /* EOF */
250