1 /* $Id: spawn.c,v 1.5 2002/03/21 22:43:27 hyperion Exp $
4 * COPYRIGHT: See COPYING in the top level directory
5 * PROJECT: ReactOS POSIX+ Subsystem
6 * FILE: subsys/psx/lib/psxdll/misc/spawn.c
7 * PURPOSE: Create the first POSIX+ process
8 * PROGRAMMER: KJK::Hyperion <noog@libero.it>
14 * NOTE by KJK::Hyperion:
15 * The __PdxSpawnPosixProcess() call solves the chicken-egg dilemma of
16 * creating the first POSIX+ process in a group without the ability to
17 * fork+exec (for example from inside a Win32 process). Processes created by
18 * __PdxSpawnPosixProcess() will *not* inherit anything from the parent, not
19 * even handles: all creation parameters have to be specified explicitely
22 #include <ddk/ntddk.h>
23 #include <ntdll/base.h>
24 #include <napi/i386/segment.h>
25 #include <ntdll/rtl.h>
26 #include <ntdll/ldr.h>
31 #include <psx/debug.h>
32 #include <psx/pdata.h>
33 #include <psx/spawn.h>
34 #include <psx/stdlib.h>
38 typedef struct _PORT_MESSAGE
{
42 USHORT VirtualRangesOffset
;
47 } PORT_MESSAGE
, *PPORT_MESSAGE
;
49 NTSTATUS STDCALL
CsrClientCallServer(
56 NTSTATUS STDCALL __PdxSpawnPosixProcess
58 OUT PHANDLE ProcessHandle
,
59 OUT PHANDLE ThreadHandle
,
60 IN POBJECT_ATTRIBUTES FileObjectAttributes
,
61 IN POBJECT_ATTRIBUTES ProcessObjectAttributes
,
62 IN HANDLE InheritFromProcessHandle
,
63 IN __PPDX_PDATA ProcessData
66 struct CSRSS_MESSAGE
{
74 PORT_MESSAGE PortMessage
;
75 struct CSRSS_MESSAGE CsrssMessage
;
76 PROCESS_INFORMATION ProcessInformation
;
82 __PPDX_SERIALIZED_PDATA pspdProcessData
;
83 IO_STATUS_BLOCK isbStatus
;
84 PROCESS_BASIC_INFORMATION pbiProcessInfo
;
85 INITIAL_TEB itInitialTeb
;
86 PRTL_USER_PROCESS_PARAMETERS pppProcessParameters
;
87 SECTION_IMAGE_INFORMATION siiInfo
;
88 CONTEXT ctxThreadContext
;
94 PVOID pPdataBuffer
= 0;
95 PVOID pParamsBuffer
= 0;
96 ULONG nDestBufferSize
;
97 ULONG nCurFilDesOffset
;
104 /* STEP 1: map executable image in memory */
105 /* 1.1: open the file for execution */
106 nErrCode
= NtOpenFile
109 SYNCHRONIZE
| FILE_EXECUTE
,
110 FileObjectAttributes
,
112 FILE_SHARE_READ
| FILE_SHARE_DELETE
,
113 FILE_SYNCHRONOUS_IO_NONALERT
| FILE_NON_DIRECTORY_FILE
117 if(!NT_SUCCESS(nErrCode
))
119 ERR("NtOpenFile() failed with status 0x%08X\n", nErrCode
);
123 /* 1.2: create an image section for the file */
124 nErrCode
= NtCreateSection
135 /* close file handle (not needed anymore) */
139 if(!NT_SUCCESS(nErrCode
))
141 ERR("NtCreateSection() failed with status 0x%08X\n", nErrCode
);
145 /* 1.3: get section image information */
146 nErrCode
= NtQuerySection
149 SectionImageInformation
,
156 if(!NT_SUCCESS(nErrCode
))
158 ERR("NtCreateSection() failed with status 0x%08X\n", nErrCode
);
163 /* STEP 2: create process */
164 nErrCode
= NtCreateProcess
168 ProcessObjectAttributes
,
169 InheritFromProcessHandle
,
176 /* close image handle (not needed anymore) */
180 if(!NT_SUCCESS(nErrCode
))
182 ERR("NtCreateProcess() failed with status 0x%08X\n", nErrCode
);
186 /* STEP 3: write process environment and process parameters */
187 /* 3.1: convert process data into process parameters */
188 __PdxProcessDataToProcessParameters
190 &pppProcessParameters
,
192 FileObjectAttributes
->ObjectName
195 /* 3.2: serialize the process data for transfer */
196 /* FIXME: the serialized data can be allocated and written directly in the
197 destination process */
198 __PdxSerializeProcessData(ProcessData
, &pspdProcessData
);
200 /* 3.2.1: adjust some fields */
201 pspdProcessData
->ProcessData
.Spawned
= TRUE
;
203 /* 3.3: allocate memory in the destination process */
204 /* 3.3.1: process data */
205 nDestBufferSize
= pspdProcessData
->AllocSize
;
207 nErrCode
= NtAllocateVirtualMemory
218 if(!NT_SUCCESS(nErrCode
))
220 ERR("NtAllocateVirtualMemory() failed with status 0x%08X\n", nErrCode
);
224 /* 3.3.2: process parameters */
225 nDestBufferSize
= pppProcessParameters
->Length
;
227 nErrCode
= NtAllocateVirtualMemory
238 if(!NT_SUCCESS(nErrCode
))
240 ERR("NtAllocateVirtualMemory() failed with status 0x%08X\n", nErrCode
);
244 /* 3.4: get pointer to the PEB */
245 nErrCode
= NtQueryInformationProcess
248 ProcessBasicInformation
,
250 sizeof(pbiProcessInfo
),
255 if(!NT_SUCCESS(nErrCode
))
257 ERR("NtQueryInformationProcess() failed with status 0x%08X\n", nErrCode
);
261 /* 3.5: write pointers in the PEB */
262 /* 3.5.1: process data */
263 nErrCode
= NtWriteVirtualMemory
266 (PVOID
)((ULONG
)pbiProcessInfo
.PebBaseAddress
+ offsetof(PEB
, SubSystemData
)),
273 if(!NT_SUCCESS(nErrCode
))
275 ERR("NtWriteVirtualMemory() failed with status 0x%08X\n", nErrCode
);
279 /* 3.5.2: process parameters */
280 nErrCode
= NtWriteVirtualMemory
283 (PVOID
)((ULONG
)pbiProcessInfo
.PebBaseAddress
+ offsetof(PEB
, ProcessParameters
)),
290 if(!NT_SUCCESS(nErrCode
))
292 ERR("NtWriteVirtualMemory() failed with status 0x%08X\n", nErrCode
);
296 /* 3.6: write data */
297 /* 3.6.1: process data */
298 nErrCode
= NtWriteVirtualMemory
303 pspdProcessData
->AllocSize
,
308 if(!NT_SUCCESS(nErrCode
))
310 ERR("NtWriteVirtualMemory() failed with status 0x%08X\n", nErrCode
);
314 /* 3.6.2 process parameters */
315 nErrCode
= NtWriteVirtualMemory
319 pppProcessParameters
,
320 pppProcessParameters
->Length
,
325 if(!NT_SUCCESS(nErrCode
))
327 ERR("NtWriteVirtualMemory() failed with status 0x%08X\n", nErrCode
);
332 /* deallocate the temporary data block in the current process */
336 (PVOID
*)&pspdProcessData
,
341 /* destroy process parameters */
342 RtlDestroyProcessParameters(pppProcessParameters
);
345 if(!NT_SUCCESS(nErrCode
))
348 /* STEP 4: duplicate handles */
349 /* 4.1: handles in the structure itself */
350 /* 4.1.1: root directory */
351 nErrCode
= NtDuplicateObject
354 ProcessData
->RootHandle
,
356 (PHANDLE
)((ULONG
)pPdataBuffer
+ offsetof(__PDX_PDATA
, RootHandle
)),
359 DUPLICATE_SAME_ACCESS
| 4 /* | DUPLICATE_SAME_ATTRIBUTES */ /* FIXME */
363 if(!NT_SUCCESS(nErrCode
))
365 ERR("NtDuplicateObject() failed with status 0x%08X\n", nErrCode
);
369 /* 4.2: file descriptors table */
372 /* pspdProcessData->ProcessData.FdTable.Descriptors contains the offset to
373 the descriptors array inside pspdProcessData->Buffer[], that is to the
374 first element of the array */
376 nCurFilDesOffset
= (ULONG
)pspdProcessData
->ProcessData
.FdTable
.Descriptors
;
377 /* iterate through all allocated descriptors */
378 i
< ProcessData
->FdTable
.AllocatedDescriptors
;
379 /* at every step, go on to next input descriptor, and increase the offset to
380 the next output descriptor */
381 i
++, nCurFilDesOffset
+= sizeof(__fildes_t
)
383 /* FIXME? check the table's bitmap instead? */
384 if(ProcessData
->FdTable
.Descriptors
[i
].FileHandle
!= NULL
)
386 /* duplicate the source handle, ProcessData->FdTable.Descriptors[i], from
387 the current process into the process identified by hProcess, at an
388 address calculated by adding to the serialized data block base address:
389 - the offset to the current descriptor
390 - the offset to the handle field of the descriptor */
391 nErrCode
= NtDuplicateObject
394 ProcessData
->FdTable
.Descriptors
[i
].FileHandle
,
397 (ULONG
)pPdataBuffer
+ nCurFilDesOffset
+ offsetof(__fildes_t
, FileHandle
)
401 DUPLICATE_SAME_ACCESS
| 4 /* | DUPLICATE_SAME_ATTRIBUTES */ /* FIXME */
405 if(!NT_SUCCESS(nErrCode
))
407 ERR("NtDuplicateObject() failed with status 0x%08X\n", nErrCode
);
411 /* duplicate standard handles */
413 if(i
== STDIN_FILENO
)
415 nErrCode
= NtDuplicateObject
418 ProcessData
->FdTable
.Descriptors
[i
].FileHandle
,
420 (PHANDLE
)((ULONG
)pParamsBuffer
+ offsetof(RTL_USER_PROCESS_PARAMETERS
, InputHandle
)),
423 DUPLICATE_SAME_ACCESS
| 4 /* | DUPLICATE_SAME_ATTRIBUTES */ /* FIXME */
427 if(!NT_SUCCESS(nErrCode
))
429 ERR("NtDuplicateObject() failed with status 0x%08X\n", nErrCode
);
433 /* standard output */
434 else if(i
== STDOUT_FILENO
)
436 nErrCode
= NtDuplicateObject
439 ProcessData
->FdTable
.Descriptors
[i
].FileHandle
,
441 (PHANDLE
)((ULONG
)pParamsBuffer
+ offsetof(RTL_USER_PROCESS_PARAMETERS
, OutputHandle
)),
444 DUPLICATE_SAME_ACCESS
| 4 /* | DUPLICATE_SAME_ATTRIBUTES */ /* FIXME */
448 if(!NT_SUCCESS(nErrCode
))
450 ERR("NtDuplicateObject() failed with status 0x%08X\n", nErrCode
);
455 else if(i
== STDERR_FILENO
)
457 nErrCode
= NtDuplicateObject
460 ProcessData
->FdTable
.Descriptors
[i
].FileHandle
,
462 (PHANDLE
)((ULONG
)pParamsBuffer
+ offsetof(RTL_USER_PROCESS_PARAMETERS
, ErrorHandle
)),
465 DUPLICATE_SAME_ACCESS
| 4 /* | DUPLICATE_SAME_ATTRIBUTES */ /* FIXME */
469 if(!NT_SUCCESS(nErrCode
))
471 ERR("NtDuplicateObject() failed with status 0x%08X\n", nErrCode
);
477 /* STEP 5: create first thread */
478 /* 5.1: set up the stack */
479 itInitialTeb
.StackAllocate
= NULL
;
480 nVirtualSize
= 0x100000;
481 nCommitSize
= 0x100000 - PAGESIZE
;
483 /* 5.1.1: reserve the stack */
484 nErrCode
= NtAllocateVirtualMemory
487 &itInitialTeb
.StackAllocate
,
495 if(!NT_SUCCESS(nErrCode
))
497 ERR("NtAllocateVirtualMemory() failed with status 0x%08X\n", nErrCode
);
501 itInitialTeb
.StackBase
=
502 (PVOID
)((ULONG
)itInitialTeb
.StackAllocate
+ nVirtualSize
);
504 itInitialTeb
.StackLimit
=
505 (PVOID
)((ULONG
)itInitialTeb
.StackBase
- nCommitSize
);
507 /* 5.1.2: commit the stack */
508 nVirtualSize
= nCommitSize
+ PAGESIZE
;
510 (PVOID
)((ULONG
)itInitialTeb
.StackBase
- nVirtualSize
);
512 nErrCode
= NtAllocateVirtualMemory
523 if(!NT_SUCCESS(nErrCode
))
525 ERR("NtAllocateVirtualMemory() failed with status 0x%08X\n", nErrCode
);
529 /* 5.1.3: set up the guard page */
530 nVirtualSize
= PAGESIZE
;
532 nErrCode
= NtProtectVirtualMemory
537 PAGE_GUARD
| PAGE_READWRITE
,
542 if(!NT_SUCCESS(nErrCode
))
544 ERR("NtProtectVirtualMemory() failed with status 0x%08X\n", nErrCode
);
548 /* 5.2: initialize the thread context */
549 memset(&ctxThreadContext
, 0, sizeof(ctxThreadContext
));
551 ctxThreadContext
.Eip
= (ULONG
)siiInfo
.EntryPoint
;
552 ctxThreadContext
.SegGs
= USER_DS
;
553 ctxThreadContext
.SegFs
= USER_DS
;
554 ctxThreadContext
.SegEs
= USER_DS
;
555 ctxThreadContext
.SegDs
= USER_DS
;
556 ctxThreadContext
.SegCs
= USER_CS
;
557 ctxThreadContext
.SegSs
= USER_DS
;
558 ctxThreadContext
.Esp
= (ULONG
)itInitialTeb
.StackBase
- 4;
559 ctxThreadContext
.EFlags
= (1 << 1) + (1 << 9);
561 /* 5.3: create the thread object */
562 nErrCode
= NtCreateThread
571 TRUE
/* FIXME: the thread is only created in suspended state for easier
572 debugging. This behavior is subject to future changes */
576 if(!NT_SUCCESS(nErrCode
))
578 ERR("NtCreateThread() failed with status 0x%08X\n", nErrCode
);
582 /* 6: register the process with the Win32 subsystem (temporary code for
583 debugging purposes) */
585 memset(&csrmsg
, 0, sizeof(csrmsg
));
587 //csrmsg.PortMessage = {0};
588 //csrmsg.CsrssMessage = {0};
589 csrmsg
.ProcessInformation
.hProcess
= hProcess
;
590 csrmsg
.ProcessInformation
.hThread
= *ThreadHandle
;
591 csrmsg
.ProcessInformation
.dwProcessId
= (DWORD
)ciClientId
.UniqueProcess
;
592 csrmsg
.ProcessInformation
.dwThreadId
= (DWORD
)ciClientId
.UniqueThread
;
593 //csrmsg.Debugger = {0};
594 //csrmsg.CreationFlags = 0;
595 //csrmsg.VdmInfo = {0};
597 nErrCode
= CsrClientCallServer(&csrmsg
, 0, 0x10000, 0x24);
600 if(!NT_SUCCESS(nErrCode
))
602 ERR("CsrClientCallServer() failed with status 0x%08X\n", nErrCode
);
606 nErrCode
= NtResumeThread(*ThreadHandle
, NULL
);
609 if(!NT_SUCCESS(nErrCode
))
611 ERR("NtResumeThread() failed with status 0x%08X\n", nErrCode
);
616 return (STATUS_SUCCESS
);