4c14f009371317eb16a5a4a93b07d9569765a54c
[reactos.git] / posix / lib / psxdll / misc / spawn.c
1 /* $Id: spawn.c,v 1.2 2002/03/07 06:08:00 hyperion Exp $
2 */
3 /*
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>
9 * UPDATE HISTORY:
10 * 25/02/2002: Created
11 */
12
13 /*
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
20 */
21
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>
27 #include <stddef.h>
28 #include <string.h>
29 #include <inttypes.h>
30 #include <pthread.h>
31 #include <psx/debug.h>
32 #include <psx/pdata.h>
33 #include <psx/stdlib.h>
34
35 VOID
36 __PdxSerializeProcessData
37 (
38 IN __PPDX_PDATA ProcessData,
39 OUT __PPDX_SERIALIZED_PDATA *SerializedProcessData
40 )
41 {
42 __PPDX_SERIALIZED_PDATA pspdProcessData = 0;
43 NTSTATUS nErrCode;
44 PBYTE pBufferTail;
45 ULONG ulAllocSize = sizeof(__PDX_SERIALIZED_PDATA) - 1;
46 size_t *pnArgLengths;
47 size_t *pnEnvVarsLengths;
48 int nEnvVarsCount;
49 int i;
50
51 /* calculate buffer length */
52 /* FIXME please! this is the most inefficient way to do it */
53
54 /* argv */
55 pnArgLengths = __malloc(ProcessData->ArgCount * sizeof(size_t));
56
57 for(i = 0; i < ProcessData->ArgCount; i ++)
58 {
59 int nStrLen = strlen(ProcessData->ArgVect[i]) + 1;
60 ulAllocSize += nStrLen;
61 pnArgLengths[i] = nStrLen;
62
63 INFO
64 (
65 "argument %d: \"%s\", length %d\n",
66 i,
67 ProcessData->ArgVect[i],
68 nStrLen
69 );
70 }
71
72 /* environ */
73 pnEnvVarsLengths = 0;
74 nEnvVarsCount = 0;
75
76 for(i = 0; *(ProcessData->Environment)[i] != 0; i++)
77 {
78 int nStrLen = strlen(*(ProcessData->Environment)[i]) + 1;
79 ulAllocSize += nStrLen;
80
81 nEnvVarsCount ++;
82 __realloc(pnEnvVarsLengths, nEnvVarsCount * sizeof(size_t));
83 pnEnvVarsLengths[i] = nStrLen;
84
85 INFO
86 (
87 "environment variable %d: \"%s\", length %d\n",
88 i,
89 *(ProcessData->Environment)[i],
90 nStrLen
91 );
92 }
93
94 INFO("(%d environment variables were found)\n", nEnvVarsCount);
95
96 /* current directory */
97 ulAllocSize += ProcessData->CurDir.Length;
98 INFO
99 (
100 "current directory: \"%Z\", length %d\n",
101 &ProcessData->CurDir,
102 ProcessData->CurDir.Length
103 );
104
105 /* root directory */
106 ulAllocSize += ProcessData->RootPath.Length;
107 INFO
108 (
109 "root directory: \"%Z\", length %d\n",
110 &ProcessData->RootPath,
111 ProcessData->RootPath.Length
112 );
113
114 /* file descriptors table */
115 ulAllocSize += sizeof(__fildes_t) * ProcessData->FdTable.AllocatedDescriptors;
116 INFO
117 (
118 "descriptors table contains %d allocated descriptors, combined length %d\n",
119 ProcessData->FdTable.AllocatedDescriptors,
120 sizeof(__fildes_t) * ProcessData->FdTable.AllocatedDescriptors
121 );
122
123 /* extra descriptors data */
124 for(i = 0; ProcessData->FdTable.AllocatedDescriptors; i ++)
125 if(ProcessData->FdTable.Descriptors[i].ExtraData != NULL)
126 {
127 ulAllocSize += ProcessData->FdTable.Descriptors[i].ExtraDataSize;
128
129 INFO
130 (
131 "descriptor %d has %d bytes of associated data\n",
132 i,
133 ProcessData->FdTable.Descriptors[i].ExtraDataSize
134 );
135
136 }
137
138 /* allocate return block */
139 INFO("about to allocate %d bytes\n", ulAllocSize);
140
141 nErrCode = NtAllocateVirtualMemory
142 (
143 NtCurrentProcess(),
144 (PVOID *)&pspdProcessData,
145 0,
146 &ulAllocSize,
147 MEM_COMMIT,
148 PAGE_READWRITE
149 );
150
151 /* failure */
152 if(!NT_SUCCESS(nErrCode))
153 {
154 ERR("NtAllocateVirtualMemory() failed with status 0x%08X\n", nErrCode);
155 __free(pnArgLengths);
156 __free(pnEnvVarsLengths);
157 *SerializedProcessData = 0;
158 return;
159 }
160
161 INFO("%d bytes actually allocated\n", ulAllocSize);
162 pspdProcessData->AllocSize = ulAllocSize;
163
164 /* copy data */
165 /* static data */
166 memcpy(&pspdProcessData->ProcessData, ProcessData, sizeof(__PDX_PDATA));
167
168 /* buffers */
169 pBufferTail = &pspdProcessData->Buffer[0];
170 INFO("buffer tail begins at 0x%08X\n", pBufferTail);
171
172 /* argv */
173 pspdProcessData->ProcessData.ArgVect = 0;
174
175 for(i = 0; i < ProcessData->ArgCount; i ++)
176 {
177 INFO
178 (
179 "copying %d bytes of argument %d (\"%s\") to 0x%08X\n",
180 pnArgLengths[i],
181 i,
182 ProcessData->ArgVect[i],
183 pBufferTail
184 );
185
186 strncpy(pBufferTail, ProcessData->ArgVect[i], pnArgLengths[i]);
187 pBufferTail += pnArgLengths[i];
188
189 INFO
190 (
191 "buffer tail increased by %d bytes, new tail at 0x%08X\n",
192 pnArgLengths[i],
193 pBufferTail
194 );
195
196 }
197
198 __free(pnArgLengths);
199
200 /* environ */
201 pspdProcessData->ProcessData.Environment = (char ***)nEnvVarsCount;
202
203 for(i = 0; i < nEnvVarsCount; i ++)
204 {
205 INFO
206 (
207 "copying %d bytes of environment variable %d (\"%s\") to 0x%08X\n",
208 pnEnvVarsLengths[i],
209 i,
210 ProcessData->Environment[i],
211 pBufferTail
212 );
213
214 strncpy(pBufferTail, *ProcessData->Environment[i], pnEnvVarsLengths[i]);
215 pBufferTail += pnEnvVarsLengths[i];
216
217 INFO
218 (
219 "buffer tail increased by %d bytes, new tail at 0x%08X\n",
220 pnEnvVarsLengths[i],
221 pBufferTail
222 );
223 }
224
225 __free(pnEnvVarsLengths);
226
227 /* current directory */
228 INFO
229 (
230 "copying %d bytes of current directory (\"%Z\") to 0x%08X\n",
231 ProcessData->CurDir.Length,
232 &ProcessData->CurDir,
233 pBufferTail
234 );
235
236 memcpy(pBufferTail, ProcessData->CurDir.Buffer, ProcessData->CurDir.Length);
237 pBufferTail += ProcessData->CurDir.Length;
238
239 INFO
240 (
241 "buffer tail increased by %d bytes, new tail at 0x%08X\n",
242 ProcessData->CurDir.Length,
243 pBufferTail
244 );
245
246 /* root directory */
247 INFO
248 (
249 "copying %d bytes of root directory (\"%Z\") to 0x%08X\n",
250 ProcessData->RootPath.Length,
251 &ProcessData->RootPath,
252 pBufferTail
253 );
254
255 memcpy
256 (
257 pBufferTail,
258 ProcessData->RootPath.Buffer,
259 ProcessData->RootPath.Length
260 );
261
262 pBufferTail += ProcessData->RootPath.Length;
263
264 INFO
265 (
266 "buffer tail increased by %d bytes, new tail at 0x%08X\n",
267 ProcessData->RootPath.Length,
268 pBufferTail
269 );
270
271 /* file descriptors table */
272 /* save the offset to the descriptors array */
273 pspdProcessData->ProcessData.FdTable.Descriptors =
274 (PVOID)((ULONG)pBufferTail - (ULONG)pspdProcessData);
275
276 INFO
277 (
278 "descriptors table contains %d allocated descriptors, combined length %d\n",
279 ProcessData->FdTable.AllocatedDescriptors,
280 sizeof(__fildes_t) * ProcessData->FdTable.AllocatedDescriptors
281 );
282
283 memcpy
284 (
285 pBufferTail,
286 ProcessData->FdTable.Descriptors,
287 sizeof(__fildes_t) * ProcessData->FdTable.AllocatedDescriptors
288 );
289
290 pBufferTail +=
291 sizeof(__fildes_t) * ProcessData->FdTable.AllocatedDescriptors;
292
293 INFO
294 (
295 "buffer tail increased by %d bytes, new tail at 0x%08X\n",
296 sizeof(__fildes_t) * ProcessData->FdTable.AllocatedDescriptors,
297 pBufferTail
298 );
299
300 /* extra descriptors data */
301 for(i = 0; ProcessData->FdTable.AllocatedDescriptors; i ++)
302 if(ProcessData->FdTable.Descriptors[i].ExtraData != 0)
303 {
304 INFO
305 (
306 "descriptor %d has %d bytes of associated data\n",
307 i,
308 ProcessData->FdTable.Descriptors[i].ExtraDataSize
309 );
310
311 memcpy
312 (
313 pBufferTail,
314 ProcessData->FdTable.Descriptors[i].ExtraData,
315 ProcessData->FdTable.Descriptors[i].ExtraDataSize
316 );
317
318 pBufferTail += ProcessData->FdTable.Descriptors[i].ExtraDataSize;
319
320 INFO
321 (
322 "buffer tail increased by %d bytes, new tail at 0x%08X\n",
323 ProcessData->FdTable.Descriptors[i].ExtraDataSize,
324 pBufferTail
325 );
326 }
327
328 /* success */
329 *SerializedProcessData = pspdProcessData;
330 }
331
332 NTSYSAPI
333 NTSTATUS
334 NTAPI
335 __PdxSpawnPosixProcess
336 (
337 OUT PHANDLE ProcessHandle,
338 OUT PHANDLE ThreadHandle,
339 IN POBJECT_ATTRIBUTES FileObjectAttributes,
340 IN POBJECT_ATTRIBUTES ProcessObjectAttributes,
341 IN HANDLE InheritFromProcessHandle,
342 IN __PPDX_PDATA ProcessData
343 )
344 {
345 __PPDX_SERIALIZED_PDATA pspdProcessData;
346 IO_STATUS_BLOCK isbStatus;
347 PROCESS_BASIC_INFORMATION pbiProcessInfo;
348 ANSI_STRING strStartEntry;
349 PVOID pStartAddress;
350 INITIAL_TEB itInitialTeb;
351 CONTEXT ctxThreadContext;
352 CLIENT_ID ciClientId;
353 NTSTATUS nErrCode;
354 HANDLE hExeFile;
355 HANDLE hExeImage;
356 HANDLE hProcess;
357 HANDLE hThread;
358 PVOID pDestBuffer;
359 ULONG nDestBufferSize;
360 ULONG nCurFilDesOffset;
361 int i;
362
363 /* STEP 1: map executable image in memory */
364 /* 1.1: open the file for execution */
365 nErrCode = NtOpenFile
366 (
367 &hExeFile,
368 SYNCHRONIZE | FILE_EXECUTE,
369 FileObjectAttributes,
370 &isbStatus,
371 FILE_SHARE_READ | FILE_SHARE_DELETE,
372 FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE
373 );
374
375 /* failure */
376 if(!NT_SUCCESS(nErrCode))
377 {
378 ERR("NtOpenFile() failed with status 0x%08X\n", nErrCode);
379 return (nErrCode);
380 }
381
382 /* 1.2: create a memory section for the file */
383 nErrCode = NtCreateSection
384 (
385 &hExeImage,
386 SECTION_ALL_ACCESS,
387 NULL,
388 0,
389 PAGE_EXECUTE,
390 SEC_IMAGE,
391 hExeFile
392 );
393
394 /* close file handle (not needed anymore) */
395 NtClose(hExeFile);
396
397 /* failure */
398 if(!NT_SUCCESS(nErrCode))
399 {
400 ERR("NtCreateSection() failed with status 0x%08X\n", nErrCode);
401 return (nErrCode);
402 }
403
404 /* STEP 2: create process */
405 nErrCode = NtCreateProcess
406 (
407 &hProcess,
408 PROCESS_ALL_ACCESS,
409 ProcessObjectAttributes,
410 InheritFromProcessHandle,
411 FALSE,
412 hExeImage,
413 NULL,
414 NULL
415 );
416
417 /* close image handle (not needed anymore) */
418 NtClose(hExeImage);
419
420 /* failure */
421 if(!NT_SUCCESS(nErrCode))
422 {
423 ERR("NtCreateProcess() failed with status 0x%08X\n", nErrCode);
424 return (nErrCode);
425 }
426
427 /* STEP 3: write process environment */
428 /* 3.1: serialize the process data for transfer */
429 /* FIXME: the serialized data can be allocated and written directly in the
430 destination process */
431 __PdxSerializeProcessData(ProcessData, &pspdProcessData);
432
433 /* 3.1.1: adjust some fields */
434 pspdProcessData->ProcessData.Spawned = TRUE;
435
436 /* 3.2: allocate memory in the destination process */
437 nDestBufferSize = pspdProcessData->AllocSize;
438
439 nErrCode = NtAllocateVirtualMemory
440 (
441 hProcess,
442 &pDestBuffer,
443 0,
444 &nDestBufferSize,
445 MEM_COMMIT,
446 PAGE_READWRITE
447 );
448
449 /* failure */
450 if(!NT_SUCCESS(nErrCode))
451 {
452 ERR("NtAllocateVirtualMemory() failed with status 0x%08X\n", nErrCode);
453 goto undoPData;
454 }
455
456 /* 3.3: get pointer to the PEB */
457 nErrCode = NtQueryInformationProcess
458 (
459 hProcess,
460 ProcessBasicInformation,
461 &pbiProcessInfo,
462 sizeof(pbiProcessInfo),
463 NULL
464 );
465
466 /* failure */
467 if(!NT_SUCCESS(nErrCode))
468 {
469 ERR("NtQueryInformationProcess() failed with status 0x%08X\n", nErrCode);
470 goto undoPData;
471 }
472
473 /* 3.4: write pointer to process data in the SubSystemData field of the PEB */
474 nErrCode = NtWriteVirtualMemory
475 (
476 hProcess,
477 (PVOID)((ULONG)pbiProcessInfo.PebBaseAddress + offsetof(PEB, SubSystemData)),
478 &pDestBuffer,
479 sizeof(PVOID),
480 NULL
481 );
482
483 /* failure */
484 if(!NT_SUCCESS(nErrCode))
485 {
486 ERR("NtWriteVirtualMemory() failed with status 0x%08X\n", nErrCode);
487 goto undoPData;
488 }
489
490 /* 3.5: write the process data */
491 nErrCode = NtWriteVirtualMemory
492 (
493 hProcess,
494 pDestBuffer,
495 pspdProcessData,
496 pspdProcessData->AllocSize,
497 NULL
498 );
499
500 undoPData:
501 /* deallocate the temporary data block in the current process */
502 NtFreeVirtualMemory
503 (
504 NtCurrentProcess(),
505 (PVOID *)&pspdProcessData,
506 0,
507 MEM_RELEASE
508 );
509
510 /* failure */
511 if(!NT_SUCCESS(nErrCode))
512 return (nErrCode);
513
514 /* STEP 4: duplicate handles */
515 /* 4.1: handles in the structure itself */
516 /* 4.1.1: root directory */
517 nErrCode = NtDuplicateObject
518 (
519 NtCurrentProcess(),
520 ProcessData->RootHandle,
521 hProcess,
522 (PHANDLE)((ULONG)pDestBuffer + offsetof(__PDX_PDATA, RootHandle)),
523 0,
524 0,
525 DUPLICATE_SAME_ACCESS | 4 /* | DUPLICATE_SAME_ATTRIBUTES */ /* FIXME */
526 );
527
528 /* failure */
529 if(!NT_SUCCESS(nErrCode))
530 {
531 ERR("NtDuplicateObject() failed with status 0x%08X\n", nErrCode);
532 goto failProcess;
533 }
534
535 /* 4.2: file descriptors table */
536 for
537 (
538 /* pspdProcessData->ProcessData.FdTable.Descriptors contains the offset to
539 the descriptors array inside pspdProcessData->Buffer[], that is to the
540 first element of the array */
541 i = 0,
542 nCurFilDesOffset = (ULONG)pspdProcessData->ProcessData.FdTable.Descriptors;
543 /* iterate through all allocated descriptors */
544 i < ProcessData->FdTable.AllocatedDescriptors;
545 /* at every step, go on to next input descriptor, and increase the offset to
546 the next output descriptor */
547 i ++, nCurFilDesOffset += sizeof(__fildes_t)
548 )
549 /* FIXME? check the table's bitmap instead? */
550 if(ProcessData->FdTable.Descriptors[i].FileHandle != NULL)
551 {
552 /* duplicate the source handle, ProcessData->FdTable.Descriptors[i], from
553 the current process into the process identified by hProcess, at an
554 address calculated by adding to the serialized data block base address:
555 - the offset to the current descriptor
556 - the offset to the handle field of the descriptor */
557 nErrCode = NtDuplicateObject
558 (
559 NtCurrentProcess(),
560 ProcessData->FdTable.Descriptors[i].FileHandle,
561 hProcess,
562 (PHANDLE)(
563 (ULONG)pDestBuffer + nCurFilDesOffset + offsetof(__fildes_t, FileHandle)
564 ),
565 0,
566 0,
567 DUPLICATE_SAME_ACCESS | 4 /* | DUPLICATE_SAME_ATTRIBUTES */ /* FIXME */
568 );
569
570 /* failure */
571 if(!NT_SUCCESS(nErrCode))
572 {
573 ERR("NtDuplicateObject() failed with status 0x%08X\n", nErrCode);
574 goto failProcess;
575 }
576 }
577
578 /* STEP 5: create first thread */
579 /* 5.1: get thunk routine's address */
580 RtlInitAnsiString(&strStartEntry, "LdrInitializeThunk");
581
582 nErrCode = LdrGetProcedureAddress
583 (
584 (PVOID)NTDLL_BASE,
585 &strStartEntry,
586 0,
587 &pStartAddress
588 );
589
590 /* failure */
591 if(!NT_SUCCESS(nErrCode))
592 {
593 ERR("LdrGetProcedureAddress() failed with status 0x%08X\n", nErrCode);
594 goto failProcess;
595 }
596
597 /* 5.2: set up the initial TEB */
598 itInitialTeb.StackAllocate = NULL;
599
600 /* FIXME: allow the caller to specify these values */
601 itInitialTeb.StackReserve = 0x100000;
602 itInitialTeb.StackCommit = itInitialTeb.StackReserve - PAGESIZE;
603
604 /* guard page */
605 itInitialTeb.StackCommit += PAGESIZE;
606
607 /* 5.2.1: set up the stack */
608 /* 5.2.1.1: reserve the stack */
609 nErrCode = NtAllocateVirtualMemory
610 (
611 hProcess,
612 &itInitialTeb.StackAllocate,
613 0,
614 &itInitialTeb.StackReserve,
615 MEM_RESERVE,
616 PAGE_READWRITE
617 );
618
619 /* failure */
620 if(!NT_SUCCESS(nErrCode))
621 {
622 ERR("NtAllocateVirtualMemory() failed with status 0x%08X\n", nErrCode);
623 goto failProcess;
624 }
625
626 itInitialTeb.StackBase =
627 (PVOID)((ULONG)itInitialTeb.StackAllocate + itInitialTeb.StackReserve);
628
629 itInitialTeb.StackLimit =
630 (PVOID)((ULONG)itInitialTeb.StackBase - itInitialTeb.StackCommit);
631
632 /* 5.2.1.2: commit the stack */
633 nErrCode = NtAllocateVirtualMemory
634 (
635 hProcess,
636 &itInitialTeb.StackLimit,
637 0,
638 &itInitialTeb.StackCommit,
639 MEM_COMMIT,
640 PAGE_READWRITE
641 );
642
643 /* failure */
644 if(!NT_SUCCESS(nErrCode))
645 {
646 ERR("NtAllocateVirtualMemory() failed with status 0x%08X\n", nErrCode);
647 goto failProcess;
648 }
649
650 /* 5.2.1.3: set up the guard page */
651 nErrCode = NtProtectVirtualMemory
652 (
653 hProcess,
654 itInitialTeb.StackLimit,
655 PAGESIZE,
656 PAGE_GUARD | PAGE_READWRITE,
657 NULL
658 );
659
660 /* failure */
661 if(!NT_SUCCESS(nErrCode))
662 {
663 ERR("NtProtectVirtualMemory() failed with status 0x%08X\n", nErrCode);
664 goto failProcess;
665 }
666
667 /* 5.2.1.4: initialize the thread context */
668 memset(&ctxThreadContext, 0, sizeof(ctxThreadContext));
669
670 ctxThreadContext.Eip = (ULONG)pStartAddress;
671 ctxThreadContext.SegGs = USER_DS;
672 ctxThreadContext.SegFs = USER_DS;
673 ctxThreadContext.SegEs = USER_DS;
674 ctxThreadContext.SegDs = USER_DS;
675 ctxThreadContext.SegCs = USER_CS;
676 ctxThreadContext.SegSs = USER_DS;
677 /* skip five doublewords (four - unknown - parameters for LdrInitializeThunk,
678 and the return address) */
679 ctxThreadContext.Esp = (ULONG)itInitialTeb.StackBase - 5 * 4;
680 ctxThreadContext.EFlags = (1 << 1) + (1 << 9);
681
682 /* 5.3: create the thread object */
683 nErrCode = NtCreateThread
684 (
685 NULL,
686 THREAD_ALL_ACCESS,
687 NULL,
688 hProcess,
689 &ciClientId,
690 &ctxThreadContext,
691 &itInitialTeb,
692 FALSE
693 );
694
695 /* failure */
696 if(!NT_SUCCESS(nErrCode))
697 {
698 ERR("NtCreateThread() failed with status 0x%08X\n", nErrCode);
699 goto failProcess;
700 }
701
702 /* success */
703 return (STATUS_SUCCESS);
704
705 /* failure */
706 failProcess:
707 NtTerminateProcess
708 (
709 hProcess,
710 nErrCode
711 );
712
713 return (nErrCode);
714 }
715
716 /* EOF */
717