[REACTOS]
[reactos.git] / reactos / dll / win32 / kernel32 / client / proc.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS system libraries
4 * FILE: lib/kernel32/proc/proc.c
5 * PURPOSE: Process functions
6 * PROGRAMMERS: Ariadne (ariadne@xs4all.nl)
7 * UPDATE HISTORY:
8 * Created 01/11/98
9 */
10
11 /* INCLUDES ****************************************************************/
12
13 #include <k32.h>
14
15 #define NDEBUG
16 #include <debug.h>
17
18 /* GLOBALS *******************************************************************/
19
20 WaitForInputIdleType UserWaitForInputIdleRoutine;
21 UNICODE_STRING BaseUnicodeCommandLine;
22 ANSI_STRING BaseAnsiCommandLine;
23 UNICODE_STRING BasePathVariableName = RTL_CONSTANT_STRING(L"PATH");
24 LPSTARTUPINFOA BaseAnsiStartupInfo = NULL;
25 PLDR_DATA_TABLE_ENTRY BasepExeLdrEntry;
26 BOOLEAN g_AppCertInitialized;
27 BOOLEAN g_HaveAppCerts;
28 LIST_ENTRY BasepAppCertDllsList;
29 RTL_CRITICAL_SECTION gcsAppCert;
30 PBASEP_APPCERT_EMBEDDED_FUNC fEmbeddedCertFunc;
31 NTSTATUS g_AppCertStatus;
32 RTL_QUERY_REGISTRY_TABLE BasepAppCertTable[2] =
33 {
34 {
35 BasepConfigureAppCertDlls,
36 1,
37 L"AppCertDlls",
38 &BasepAppCertDllsList,
39 0,
40 NULL,
41 0
42 }
43 };
44
45 PSAFER_REPLACE_PROCESS_THREAD_TOKENS g_SaferReplaceProcessThreadTokens;
46 HMODULE gSaferHandle = (HMODULE)-1;
47
48 VOID WINAPI
49 RegisterWaitForInputIdle(WaitForInputIdleType lpfnRegisterWaitForInputIdle);
50
51 #define CMD_STRING L"cmd /c "
52
53 /* FUNCTIONS ****************************************************************/
54
55 VOID
56 WINAPI
57 StuffStdHandle(IN HANDLE ProcessHandle,
58 IN HANDLE StandardHandle,
59 IN PHANDLE Address)
60 {
61 NTSTATUS Status;
62 HANDLE DuplicatedHandle;
63 SIZE_T NumberOfBytesWritten;
64
65 /* If there is no handle to duplicate, return immediately */
66 if (!StandardHandle) return;
67
68 /* Duplicate the handle */
69 Status = NtDuplicateObject(NtCurrentProcess(),
70 StandardHandle,
71 ProcessHandle,
72 &DuplicatedHandle,
73 0,
74 0,
75 DUPLICATE_SAME_ACCESS |
76 DUPLICATE_SAME_ATTRIBUTES);
77 if (!NT_SUCCESS(Status)) return;
78
79 /* Write it */
80 NtWriteVirtualMemory(ProcessHandle,
81 Address,
82 &DuplicatedHandle,
83 sizeof(HANDLE),
84 &NumberOfBytesWritten);
85 }
86
87 BOOLEAN
88 WINAPI
89 BuildSubSysCommandLine(IN LPCWSTR SubsystemName,
90 IN LPCWSTR ApplicationName,
91 IN LPCWSTR CommandLine,
92 OUT PUNICODE_STRING SubsysCommandLine)
93 {
94 UNICODE_STRING CommandLineString, ApplicationNameString;
95 PWCHAR Buffer;
96 ULONG Length;
97
98 /* Convert to unicode strings */
99 RtlInitUnicodeString(&CommandLineString, ApplicationName);
100 RtlInitUnicodeString(&ApplicationNameString, CommandLine);
101
102 /* Allocate buffer for the output string */
103 Length = CommandLineString.MaximumLength + ApplicationNameString.MaximumLength + 32;
104 Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, Length);
105 RtlInitEmptyUnicodeString(SubsysCommandLine, Buffer, (USHORT)Length);
106 if (!Buffer)
107 {
108 /* Fail, no memory */
109 BaseSetLastNTError(STATUS_NO_MEMORY);
110 return FALSE;
111 }
112
113 /* Build the final subsystem command line */
114 RtlAppendUnicodeToString(SubsysCommandLine, SubsystemName);
115 RtlAppendUnicodeStringToString(SubsysCommandLine, &CommandLineString);
116 RtlAppendUnicodeToString(SubsysCommandLine, L" /C ");
117 RtlAppendUnicodeStringToString(SubsysCommandLine, &ApplicationNameString);
118 return TRUE;
119 }
120
121 BOOLEAN
122 WINAPI
123 BasepIsImageVersionOk(IN ULONG ImageMajorVersion,
124 IN ULONG ImageMinorVersion)
125 {
126 /* Accept images for NT 3.1 or higher, as long as they're not newer than us */
127 return ((ImageMajorVersion >= 3) &&
128 ((ImageMajorVersion != 3) ||
129 (ImageMinorVersion >= 10)) &&
130 (ImageMajorVersion <= SharedUserData->NtMajorVersion) &&
131 ((ImageMajorVersion != SharedUserData->NtMajorVersion) ||
132 (ImageMinorVersion <= SharedUserData->NtMinorVersion)));
133 }
134
135 NTSTATUS
136 WINAPI
137 BasepCheckWebBladeHashes(IN HANDLE FileHandle)
138 {
139 NTSTATUS Status;
140 CHAR Hash[16];
141
142 /* Get all the MD5 hashes */
143 Status = RtlComputeImportTableHash(FileHandle, Hash, 1);
144 if (!NT_SUCCESS(Status)) return Status;
145
146 /* Depending on which suite this is, run a bsearch and block the appropriate ones */
147 if (SharedUserData->SuiteMask & VER_SUITE_COMPUTE_SERVER)
148 {
149 DPRINT1("Egad! This is a ReactOS Compute Server and we should prevent you from using certain APIs...but we won't.");
150 }
151 else if (SharedUserData->SuiteMask & VER_SUITE_STORAGE_SERVER)
152 {
153 DPRINT1("Gasp! This is a ReactOS Storage Server and we should prevent you from using certain APIs...but we won't.");
154 }
155 else if (SharedUserData->SuiteMask & VER_SUITE_BLADE)
156 {
157 DPRINT1("Golly! This is a ReactOS Web Blade Server and we should prevent you from using certain APIs...but we won't.");
158 }
159
160 /* Actually, fuck it, don't block anything, we're open source */
161 return STATUS_SUCCESS;
162 }
163
164 NTSTATUS
165 NTAPI
166 BasepSaveAppCertRegistryValue(IN PLIST_ENTRY List,
167 IN PWCHAR ComponentName,
168 IN PWCHAR DllName)
169 {
170 /* Pretty much the only thing this key is used for, is malware */
171 UNIMPLEMENTED;
172 return STATUS_NOT_IMPLEMENTED;
173 }
174
175 NTSTATUS
176 NTAPI
177 BasepConfigureAppCertDlls(IN PWSTR ValueName,
178 IN ULONG ValueType,
179 IN PVOID ValueData,
180 IN ULONG ValueLength,
181 IN PVOID Context,
182 IN PVOID EntryContext)
183 {
184 /* Add this to the certification list */
185 return BasepSaveAppCertRegistryValue(Context, ValueName, ValueData);
186 }
187
188 NTSTATUS
189 WINAPI
190 BasepIsProcessAllowed(IN LPWSTR ApplicationName)
191 {
192 NTSTATUS Status, Status1;
193 PWCHAR Buffer;
194 UINT Length;
195 HMODULE TrustLibrary;
196 PBASEP_APPCERT_ENTRY Entry;
197 ULONG CertFlag;
198 PLIST_ENTRY NextEntry;
199 HANDLE KeyHandle;
200 UNICODE_STRING CertKey = RTL_CONSTANT_STRING(L"\\Registry\\MACHINE\\System\\CurrentControlSet\\Control\\Session Manager\\AppCertDlls");
201 OBJECT_ATTRIBUTES KeyAttributes = RTL_CONSTANT_OBJECT_ATTRIBUTES(&CertKey, OBJ_CASE_INSENSITIVE);
202
203 /* Try to initialize the certification subsystem */
204 while (!g_AppCertInitialized)
205 {
206 /* Defaults */
207 Status = STATUS_SUCCESS;
208 Buffer = NULL;
209
210 /* Acquire the lock while initializing and see if we lost a race */
211 RtlEnterCriticalSection(&gcsAppCert);
212 if (g_AppCertInitialized) break;
213
214 /* On embedded, there is a special DLL */
215 if (SharedUserData->SuiteMask & VER_SUITE_EMBEDDEDNT)
216 {
217 /* Allocate a buffer for the name */
218 Buffer = RtlAllocateHeap(RtlGetProcessHeap(),
219 0,
220 MAX_PATH * sizeof(WCHAR) +
221 sizeof(UNICODE_NULL));
222 if (!Buffer)
223 {
224 /* Fail if no memory */
225 Status = STATUS_NO_MEMORY;
226 }
227 else
228 {
229 /* Now get the system32 directory in our buffer, make sure it fits */
230 Length = GetSystemDirectoryW(Buffer, MAX_PATH - sizeof("EmbdTrst.DLL"));
231 if ((Length) && (Length <= MAX_PATH - sizeof("EmbdTrst.DLL")))
232 {
233 /* Add a slash if needed, and add the embedded cert DLL name */
234 if (Buffer[Length - 1] != '\\') Buffer[Length++] = '\\';
235 RtlCopyMemory(&Buffer[Length],
236 L"EmbdTrst.DLL",
237 sizeof(L"EmbdTrst.DLL"));
238
239 /* Try to load it */
240 TrustLibrary = LoadLibraryW(Buffer);
241 if (TrustLibrary)
242 {
243 /* And extract the special function out of it */
244 fEmbeddedCertFunc = (PVOID)GetProcAddress(TrustLibrary,
245 "ImageOkToRunOnEmbeddedNT");
246 }
247 }
248
249 /* If we didn't get this far, set a failure code */
250 if (!fEmbeddedCertFunc) Status = STATUS_UNSUCCESSFUL;
251 }
252 }
253 else
254 {
255 /* Other systems have a registry entry for this */
256 Status1 = NtOpenKey(&KeyHandle, KEY_READ, &KeyAttributes);
257 if (NT_SUCCESS(Status1))
258 {
259 /* Close it, we'll query it through Rtl */
260 NtClose(KeyHandle);
261
262 /* Do the query, which will call a special callback */
263 Status = RtlQueryRegistryValues(RTL_REGISTRY_CONTROL,
264 L"Session Manager",
265 BasepAppCertTable,
266 NULL,
267 NULL);
268 if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
269 {
270 Status = STATUS_SUCCESS;
271 }
272 }
273 }
274
275 /* Free any buffer if we had one */
276 if (Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
277
278 /* Check for errors, or a missing embedded/custom certification DLL */
279 if (!NT_SUCCESS(Status) ||
280 (!(fEmbeddedCertFunc) && (IsListEmpty(&BasepAppCertDllsList))))
281 {
282 /* The subsystem is not active on this machine, so give up */
283 g_HaveAppCerts = FALSE;
284 g_AppCertStatus = Status;
285 }
286 else
287 {
288 /* We have certification DLLs active, remember this */
289 g_HaveAppCerts = TRUE;
290 }
291
292 /* We are done the initialization phase, release the lock */
293 g_AppCertInitialized = TRUE;
294 RtlLeaveCriticalSection(&gcsAppCert);
295 }
296
297 /* If there's no certification DLLs present, return the failure code */
298 if (!g_HaveAppCerts) return g_AppCertStatus;
299
300 /* Otherwise, assume success and make sure we have *something* */
301 ASSERT(fEmbeddedCertFunc || !IsListEmpty(&BasepAppCertDllsList));
302 Status = STATUS_SUCCESS;
303
304 /* If the something is an embedded certification DLL, call it and return */
305 if (fEmbeddedCertFunc) return fEmbeddedCertFunc(ApplicationName);
306
307 /* Otherwise we have custom certification DLLs, parse them */
308 NextEntry = BasepAppCertDllsList.Flink;
309 CertFlag = 2;
310 while (NextEntry != &BasepAppCertDllsList)
311 {
312 /* Make sure the entry has a callback */
313 Entry = CONTAINING_RECORD(NextEntry, BASEP_APPCERT_ENTRY, Entry);
314 ASSERT(Entry->fPluginCertFunc != NULL);
315
316 /* Call it and check if it failed */
317 Status = Entry->fPluginCertFunc(ApplicationName, 1);
318 if (!NT_SUCCESS(Status)) CertFlag = 3;
319
320 /* Move on */
321 NextEntry = NextEntry->Flink;
322 }
323
324 /* Now loop them again */
325 NextEntry = BasepAppCertDllsList.Flink;
326 while (NextEntry != &BasepAppCertDllsList)
327 {
328 /* Make sure the entry has a callback */
329 Entry = CONTAINING_RECORD(NextEntry, BASEP_APPCERT_ENTRY, Entry);
330 ASSERT(Entry->fPluginCertFunc != NULL);
331
332 /* Call it, this time with the flag from the loop above */
333 Status = Entry->fPluginCertFunc(ApplicationName, CertFlag);
334 }
335
336 /* All done, return the status */
337 return Status;
338 }
339
340 NTSTATUS
341 WINAPI
342 BasepReplaceProcessThreadTokens(IN HANDLE TokenHandle,
343 IN HANDLE ProcessHandle,
344 IN HANDLE ThreadHandle)
345 {
346 NTSTATUS Status;
347 ANSI_STRING SaferiReplaceProcessThreadTokens = RTL_CONSTANT_STRING("SaferiReplaceProcessThreadTokens");
348
349 /* Enter the application certification lock */
350 RtlEnterCriticalSection(&gcsAppCert);
351
352 /* Check if we already know the function */
353 if (g_SaferReplaceProcessThreadTokens)
354 {
355 /* Call it */
356 Status = g_SaferReplaceProcessThreadTokens(TokenHandle,
357 ProcessHandle,
358 ThreadHandle) ?
359 STATUS_SUCCESS :
360 STATUS_UNSUCCESSFUL;
361 }
362 else
363 {
364 /* Check if the app certification DLL isn't loaded */
365 if (!(gSaferHandle) ||
366 (gSaferHandle == (HMODULE)-1) ||
367 (gSaferHandle == (HMODULE)-2))
368 {
369 /* Then we can't call the function */
370 Status = STATUS_ENTRYPOINT_NOT_FOUND;
371 }
372 else
373 {
374 /* We have the DLL, find the address of the Safer function */
375 Status = LdrGetProcedureAddress(gSaferHandle,
376 &SaferiReplaceProcessThreadTokens,
377 0,
378 (PVOID*)&g_SaferReplaceProcessThreadTokens);
379 if (NT_SUCCESS(Status))
380 {
381 /* Found it, now call it */
382 Status = g_SaferReplaceProcessThreadTokens(TokenHandle,
383 ProcessHandle,
384 ThreadHandle) ?
385 STATUS_SUCCESS :
386 STATUS_UNSUCCESSFUL;
387 }
388 else
389 {
390 /* We couldn't find it, so this must be an unsupported DLL */
391 LdrUnloadDll(gSaferHandle);
392 gSaferHandle = NULL;
393 Status = STATUS_ENTRYPOINT_NOT_FOUND;
394 }
395 }
396 }
397
398 /* Release the lock and return the result */
399 RtlLeaveCriticalSection(&gcsAppCert);
400 return Status;
401 }
402
403 VOID
404 WINAPI
405 BasepSxsCloseHandles(IN PBASE_MSG_SXS_HANDLES Handles)
406 {
407 NTSTATUS Status;
408
409 /* Sanity checks */
410 ASSERT(Handles != NULL);
411 ASSERT(Handles->Process == NULL || Handles->Process == NtCurrentProcess());
412
413 /* Close the file handle */
414 if (Handles->File)
415 {
416 Status = NtClose(Handles->File);
417 ASSERT(NT_SUCCESS(Status));
418 }
419
420 /* Close the section handle */
421 if (Handles->Section)
422 {
423 Status = NtClose(Handles->Section);
424 ASSERT(NT_SUCCESS(Status));
425 }
426
427 /* Unmap the section view */
428 if (Handles->ViewBase.QuadPart)
429 {
430 Status = NtUnmapViewOfSection(NtCurrentProcess(),
431 (PVOID)Handles->ViewBase.LowPart);
432 ASSERT(NT_SUCCESS(Status));
433 }
434 }
435
436 static
437 LONG BaseExceptionFilter(EXCEPTION_POINTERS *ExceptionInfo)
438 {
439 LONG ExceptionDisposition = EXCEPTION_EXECUTE_HANDLER;
440 LPTOP_LEVEL_EXCEPTION_FILTER RealFilter;
441 RealFilter = RtlDecodePointer(GlobalTopLevelExceptionFilter);
442
443 if (RealFilter != NULL)
444 {
445 _SEH2_TRY
446 {
447 ExceptionDisposition = RealFilter(ExceptionInfo);
448 }
449 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
450 {
451 }
452 _SEH2_END;
453 }
454 if ((ExceptionDisposition == EXCEPTION_CONTINUE_SEARCH || ExceptionDisposition == EXCEPTION_EXECUTE_HANDLER) &&
455 RealFilter != UnhandledExceptionFilter)
456 {
457 ExceptionDisposition = UnhandledExceptionFilter(ExceptionInfo);
458 }
459
460 return ExceptionDisposition;
461 }
462
463 VOID
464 WINAPI
465 BaseProcessStartup(PPROCESS_START_ROUTINE lpStartAddress)
466 {
467 DPRINT("BaseProcessStartup(..) - setting up exception frame.\n");
468
469 _SEH2_TRY
470 {
471 /* Set our Start Address */
472 NtSetInformationThread(NtCurrentThread(),
473 ThreadQuerySetWin32StartAddress,
474 &lpStartAddress,
475 sizeof(PPROCESS_START_ROUTINE));
476
477 /* Call the Start Routine */
478 ExitThread(lpStartAddress());
479 }
480 _SEH2_EXCEPT(BaseExceptionFilter(_SEH2_GetExceptionInformation()))
481 {
482 /* Get the Exit code from the SEH Handler */
483 if (!BaseRunningInServerProcess)
484 {
485 /* Kill the whole process, usually */
486 ExitProcess(_SEH2_GetExceptionCode());
487 }
488 else
489 {
490 /* If running inside CSRSS, kill just this thread */
491 ExitThread(_SEH2_GetExceptionCode());
492 }
493 }
494 _SEH2_END;
495 }
496
497 NTSTATUS
498 WINAPI
499 BasepNotifyCsrOfThread(IN HANDLE ThreadHandle,
500 IN PCLIENT_ID ClientId)
501 {
502 NTSTATUS Status;
503 BASE_API_MESSAGE ApiMessage;
504 PBASE_CREATE_THREAD CreateThreadRequest = &ApiMessage.Data.CreateThreadRequest;
505
506 DPRINT("BasepNotifyCsrOfThread: Thread: %p, Handle %p\n",
507 ClientId->UniqueThread, ThreadHandle);
508
509 /* Fill out the request */
510 CreateThreadRequest->ClientId = *ClientId;
511 CreateThreadRequest->ThreadHandle = ThreadHandle;
512
513 /* Call CSR */
514 Status = CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
515 NULL,
516 CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepCreateThread),
517 sizeof(BASE_CREATE_THREAD));
518 if (!NT_SUCCESS(Status))
519 {
520 DPRINT1("Failed to tell CSRSS about new thread: %lx\n", Status);
521 return Status;
522 }
523
524 /* Return Success */
525 return STATUS_SUCCESS;
526 }
527
528 BOOLEAN
529 WINAPI
530 BasePushProcessParameters(IN ULONG ParameterFlags,
531 IN HANDLE ProcessHandle,
532 IN PPEB RemotePeb,
533 IN LPCWSTR ApplicationPathName,
534 IN LPWSTR lpCurrentDirectory,
535 IN LPWSTR lpCommandLine,
536 IN LPVOID lpEnvironment,
537 IN LPSTARTUPINFOW StartupInfo,
538 IN DWORD CreationFlags,
539 IN BOOL InheritHandles,
540 IN ULONG ImageSubsystem,
541 IN PVOID AppCompatData,
542 IN ULONG AppCompatDataSize)
543 {
544 WCHAR FullPath[MAX_PATH + 5];
545 PWCHAR Remaining, DllPathString, ScanChar;
546 PRTL_USER_PROCESS_PARAMETERS ProcessParameters, RemoteParameters;
547 PVOID RemoteAppCompatData;
548 UNICODE_STRING DllPath, ImageName, CommandLine, CurrentDirectory;
549 UNICODE_STRING Desktop, Shell, Runtime, Title;
550 NTSTATUS Status;
551 ULONG EnviroSize;
552 SIZE_T Size;
553 BOOLEAN HavePebLock = FALSE, Result;
554 PPEB Peb = NtCurrentPeb();
555
556 /* Get the full path name */
557 Size = GetFullPathNameW(ApplicationPathName,
558 MAX_PATH + 4,
559 FullPath,
560 &Remaining);
561 if ((Size) && (Size <= (MAX_PATH + 4)))
562 {
563 /* Get the DLL Path */
564 DllPathString = BaseComputeProcessDllPath(FullPath, lpEnvironment);
565 if (!DllPathString)
566 {
567 /* Fail */
568 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
569 return FALSE;
570 }
571
572 /* Initialize Strings */
573 RtlInitUnicodeString(&DllPath, DllPathString);
574 RtlInitUnicodeString(&ImageName, FullPath);
575 }
576 else
577 {
578 /* Couldn't get the path name. Just take the original path */
579 DllPathString = BaseComputeProcessDllPath((LPWSTR)ApplicationPathName,
580 lpEnvironment);
581 if (!DllPathString)
582 {
583 /* Fail */
584 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
585 return FALSE;
586 }
587
588 /* Initialize Strings */
589 RtlInitUnicodeString(&DllPath, DllPathString);
590 RtlInitUnicodeString(&ImageName, ApplicationPathName);
591 }
592
593 /* Initialize Strings */
594 RtlInitUnicodeString(&CommandLine, lpCommandLine);
595 RtlInitUnicodeString(&CurrentDirectory, lpCurrentDirectory);
596
597 /* Initialize more Strings from the Startup Info */
598 if (StartupInfo->lpDesktop)
599 {
600 RtlInitUnicodeString(&Desktop, StartupInfo->lpDesktop);
601 }
602 else
603 {
604 RtlInitUnicodeString(&Desktop, L"");
605 }
606 if (StartupInfo->lpReserved)
607 {
608 RtlInitUnicodeString(&Shell, StartupInfo->lpReserved);
609 }
610 else
611 {
612 RtlInitUnicodeString(&Shell, L"");
613 }
614 if (StartupInfo->lpTitle)
615 {
616 RtlInitUnicodeString(&Title, StartupInfo->lpTitle);
617 }
618 else
619 {
620 RtlInitUnicodeString(&Title, ApplicationPathName);
621 }
622
623 /* This one is special because the length can differ */
624 Runtime.Buffer = (LPWSTR)StartupInfo->lpReserved2;
625 Runtime.MaximumLength = Runtime.Length = StartupInfo->cbReserved2;
626
627 /* Enforce no app compat data if the pointer was NULL */
628 if (!AppCompatData) AppCompatDataSize = 0;
629
630 /* Create the Parameter Block */
631 ProcessParameters = NULL;
632 DPRINT("ImageName: '%wZ'\n", &ImageName);
633 DPRINT("DllPath : '%wZ'\n", &DllPath);
634 DPRINT("CurDir : '%wZ'\n", &CurrentDirectory);
635 DPRINT("CmdLine : '%wZ'\n", &CommandLine);
636 DPRINT("Title : '%wZ'\n", &Title);
637 DPRINT("Desktop : '%wZ'\n", &Desktop);
638 DPRINT("Shell : '%wZ'\n", &Shell);
639 DPRINT("Runtime : '%wZ'\n", &Runtime);
640 Status = RtlCreateProcessParameters(&ProcessParameters,
641 &ImageName,
642 &DllPath,
643 lpCurrentDirectory ?
644 &CurrentDirectory : NULL,
645 &CommandLine,
646 lpEnvironment,
647 &Title,
648 &Desktop,
649 &Shell,
650 &Runtime);
651 if (!NT_SUCCESS(Status)) goto FailPath;
652
653 /* Clear the current directory handle if not inheriting */
654 if (!InheritHandles) ProcessParameters->CurrentDirectory.Handle = NULL;
655
656 /* Check if the user passed in an environment */
657 if (lpEnvironment)
658 {
659 /* We should've made it part of the parameters block, enforce this */
660 ASSERT(ProcessParameters->Environment == lpEnvironment);
661 lpEnvironment = ProcessParameters->Environment;
662 }
663 else
664 {
665 /* The user did not, so use the one from the current PEB */
666 HavePebLock = TRUE;
667 RtlAcquirePebLock();
668 lpEnvironment = Peb->ProcessParameters->Environment;
669 }
670
671 /* Save pointer and start lookup */
672 ScanChar = lpEnvironment;
673 if (lpEnvironment)
674 {
675 /* Find the environment size */
676 while (*ScanChar++) while (*ScanChar++);
677 EnviroSize = (ULONG)((ULONG_PTR)ScanChar - (ULONG_PTR)lpEnvironment);
678
679 /* Allocate and Initialize new Environment Block */
680 Size = EnviroSize;
681 ProcessParameters->Environment = NULL;
682 Status = NtAllocateVirtualMemory(ProcessHandle,
683 (PVOID*)&ProcessParameters->Environment,
684 0,
685 &Size,
686 MEM_COMMIT,
687 PAGE_READWRITE);
688 if (!NT_SUCCESS(Status)) goto FailPath;
689
690 /* Write the Environment Block */
691 Status = NtWriteVirtualMemory(ProcessHandle,
692 ProcessParameters->Environment,
693 lpEnvironment,
694 EnviroSize,
695 NULL);
696
697 /* No longer need the PEB lock anymore */
698 if (HavePebLock)
699 {
700 /* Release it */
701 RtlReleasePebLock();
702 HavePebLock = FALSE;
703 }
704
705 /* Check if the write failed */
706 if (!NT_SUCCESS(Status)) goto FailPath;
707 }
708
709 /* Write new parameters */
710 ProcessParameters->StartingX = StartupInfo->dwX;
711 ProcessParameters->StartingY = StartupInfo->dwY;
712 ProcessParameters->CountX = StartupInfo->dwXSize;
713 ProcessParameters->CountY = StartupInfo->dwYSize;
714 ProcessParameters->CountCharsX = StartupInfo->dwXCountChars;
715 ProcessParameters->CountCharsY = StartupInfo->dwYCountChars;
716 ProcessParameters->FillAttribute = StartupInfo->dwFillAttribute;
717 ProcessParameters->WindowFlags = StartupInfo->dwFlags;
718 ProcessParameters->ShowWindowFlags = StartupInfo->wShowWindow;
719
720 /* Write the handles only if we have to */
721 if (StartupInfo->dwFlags &
722 (STARTF_USESTDHANDLES | STARTF_USEHOTKEY | STARTF_SHELLPRIVATE))
723 {
724 ProcessParameters->StandardInput = StartupInfo->hStdInput;
725 ProcessParameters->StandardOutput = StartupInfo->hStdOutput;
726 ProcessParameters->StandardError = StartupInfo->hStdError;
727 }
728
729 /* Use Special Flags for ConDllInitialize in Kernel32 */
730 if (CreationFlags & DETACHED_PROCESS)
731 {
732 ProcessParameters->ConsoleHandle = HANDLE_DETACHED_PROCESS;
733 }
734 else if (CreationFlags & CREATE_NEW_CONSOLE)
735 {
736 ProcessParameters->ConsoleHandle = HANDLE_CREATE_NEW_CONSOLE;
737 }
738 else if (CreationFlags & CREATE_NO_WINDOW)
739 {
740 ProcessParameters->ConsoleHandle = HANDLE_CREATE_NO_WINDOW;
741 }
742 else
743 {
744 /* Inherit our Console Handle */
745 ProcessParameters->ConsoleHandle = Peb->ProcessParameters->ConsoleHandle;
746
747 /* Make sure that the shell isn't trampling on our handles first */
748 if (!(StartupInfo->dwFlags &
749 (STARTF_USESTDHANDLES | STARTF_USEHOTKEY | STARTF_SHELLPRIVATE)))
750 {
751 /* Copy the handle if we are inheriting or if it's a console handle */
752 if ((InheritHandles) ||
753 (IsConsoleHandle(Peb->ProcessParameters->StandardInput)))
754 {
755 ProcessParameters->StandardInput = Peb->ProcessParameters->StandardInput;
756 }
757 if ((InheritHandles) ||
758 (IsConsoleHandle(Peb->ProcessParameters->StandardOutput)))
759 {
760 ProcessParameters->StandardOutput = Peb->ProcessParameters->StandardOutput;
761 }
762 if ((InheritHandles) ||
763 (IsConsoleHandle(Peb->ProcessParameters->StandardError)))
764 {
765 ProcessParameters->StandardError = Peb->ProcessParameters->StandardError;
766 }
767 }
768 }
769
770 /* Also set the Console Flag */
771 if ((CreationFlags & CREATE_NEW_PROCESS_GROUP) &&
772 (!(CreationFlags & CREATE_NEW_CONSOLE)))
773 {
774 ProcessParameters->ConsoleFlags = 1;
775 }
776
777 /* Check if there's a .local file present */
778 if (ParameterFlags & 1)
779 {
780 ProcessParameters->Flags |= RTL_USER_PROCESS_PARAMETERS_LOCAL_DLL_PATH;
781 }
782
783 /* Check if we failed to open the IFEO key */
784 if (ParameterFlags & 2)
785 {
786 ProcessParameters->Flags |= RTL_USER_PROCESS_PARAMETERS_IMAGE_KEY_MISSING;
787 }
788
789 /* Allocate memory for the parameter block */
790 Size = ProcessParameters->Length;
791 RemoteParameters = NULL;
792 Status = NtAllocateVirtualMemory(ProcessHandle,
793 (PVOID*)&RemoteParameters,
794 0,
795 &Size,
796 MEM_COMMIT,
797 PAGE_READWRITE);
798 if (!NT_SUCCESS(Status)) goto FailPath;
799
800 /* Set the allocated size */
801 ProcessParameters->MaximumLength = Size;
802
803 /* Handle some Parameter Flags */
804 ProcessParameters->Flags |= (CreationFlags & PROFILE_USER) ?
805 RTL_USER_PROCESS_PARAMETERS_PROFILE_USER : 0;
806 ProcessParameters->Flags |= (CreationFlags & PROFILE_KERNEL) ?
807 RTL_USER_PROCESS_PARAMETERS_PROFILE_KERNEL : 0;
808 ProcessParameters->Flags |= (CreationFlags & PROFILE_SERVER) ?
809 RTL_USER_PROCESS_PARAMETERS_PROFILE_SERVER : 0;
810 ProcessParameters->Flags |= (Peb->ProcessParameters->Flags &
811 RTL_USER_PROCESS_PARAMETERS_DISABLE_HEAP_CHECKS);
812
813 /* Write the Parameter Block */
814 Status = NtWriteVirtualMemory(ProcessHandle,
815 RemoteParameters,
816 ProcessParameters,
817 ProcessParameters->Length,
818 NULL);
819 if (!NT_SUCCESS(Status)) goto FailPath;
820
821 /* Write the PEB Pointer */
822 Status = NtWriteVirtualMemory(ProcessHandle,
823 &RemotePeb->ProcessParameters,
824 &RemoteParameters,
825 sizeof(PVOID),
826 NULL);
827 if (!NT_SUCCESS(Status)) goto FailPath;
828
829 /* Check if there's any app compat data to write */
830 RemoteAppCompatData = NULL;
831 if (AppCompatData)
832 {
833 /* Allocate some space for the application compatibility data */
834 Size = AppCompatDataSize;
835 Status = NtAllocateVirtualMemory(ProcessHandle,
836 &RemoteAppCompatData,
837 0,
838 &Size,
839 MEM_COMMIT,
840 PAGE_READWRITE);
841 if (!NT_SUCCESS(Status)) goto FailPath;
842
843 /* Write the application compatibility data */
844 Status = NtWriteVirtualMemory(ProcessHandle,
845 RemoteAppCompatData,
846 AppCompatData,
847 AppCompatDataSize,
848 NULL);
849 if (!NT_SUCCESS(Status)) goto FailPath;
850 }
851
852 /* Write the PEB Pointer to the app compat data (might be NULL) */
853 Status = NtWriteVirtualMemory(ProcessHandle,
854 &RemotePeb->pShimData,
855 &RemoteAppCompatData,
856 sizeof(PVOID),
857 NULL);
858 if (!NT_SUCCESS(Status)) goto FailPath;
859
860 /* Now write Peb->ImageSubSystem */
861 if (ImageSubsystem)
862 {
863 NtWriteVirtualMemory(ProcessHandle,
864 &RemotePeb->ImageSubsystem,
865 &ImageSubsystem,
866 sizeof(ImageSubsystem),
867 NULL);
868 }
869
870 /* Success path */
871 Result = TRUE;
872
873 Quickie:
874 /* Cleanup */
875 if (HavePebLock) RtlReleasePebLock();
876 RtlFreeHeap(RtlGetProcessHeap(), 0, DllPath.Buffer);
877 if (ProcessParameters) RtlDestroyProcessParameters(ProcessParameters);
878 return Result;
879 FailPath:
880 DPRINT1("Failure to create process parameters: %lx\n", Status);
881 BaseSetLastNTError(Status);
882 Result = FALSE;
883 goto Quickie;
884 }
885
886 VOID
887 WINAPI
888 InitCommandLines(VOID)
889 {
890 NTSTATUS Status;
891
892 /* Read the UNICODE_STRING from the PEB */
893 BaseUnicodeCommandLine = NtCurrentPeb()->ProcessParameters->CommandLine;
894
895 /* Convert to ANSI_STRING for the *A callers */
896 Status = RtlUnicodeStringToAnsiString(&BaseAnsiCommandLine,
897 &BaseUnicodeCommandLine,
898 TRUE);
899 if (!NT_SUCCESS(Status)) RtlInitEmptyAnsiString(&BaseAnsiCommandLine, 0, 0);
900 }
901
902 /* PUBLIC FUNCTIONS ***********************************************************/
903
904 /*
905 * @implemented
906 */
907 BOOL
908 WINAPI
909 GetProcessAffinityMask(IN HANDLE hProcess,
910 OUT PDWORD_PTR lpProcessAffinityMask,
911 OUT PDWORD_PTR lpSystemAffinityMask)
912 {
913 PROCESS_BASIC_INFORMATION ProcessInfo;
914 NTSTATUS Status;
915
916 /* Query information on the process from the kernel */
917 Status = NtQueryInformationProcess(hProcess,
918 ProcessBasicInformation,
919 (PVOID)&ProcessInfo,
920 sizeof(PROCESS_BASIC_INFORMATION),
921 NULL);
922 if (!NT_SUCCESS(Status))
923 {
924 /* Fail */
925 BaseSetLastNTError(Status);
926 return FALSE;
927 }
928
929 /* Copy the affinity mask, and get the system one from our shared data */
930 *lpProcessAffinityMask = (DWORD)ProcessInfo.AffinityMask;
931 *lpSystemAffinityMask = (DWORD)BaseStaticServerData->SysInfo.ActiveProcessorsAffinityMask;
932 return TRUE;
933 }
934
935 /*
936 * @implemented
937 */
938 BOOL
939 WINAPI
940 SetProcessAffinityMask(IN HANDLE hProcess,
941 IN DWORD_PTR dwProcessAffinityMask)
942 {
943 NTSTATUS Status;
944
945 /* Directly set the affinity mask */
946 Status = NtSetInformationProcess(hProcess,
947 ProcessAffinityMask,
948 (PVOID)&dwProcessAffinityMask,
949 sizeof(DWORD));
950 if (!NT_SUCCESS(Status))
951 {
952 /* Handle failure */
953 BaseSetLastNTError(Status);
954 return FALSE;
955 }
956
957 /* Everything was ok */
958 return TRUE;
959 }
960
961 /*
962 * @implemented
963 */
964 BOOL
965 WINAPI
966 GetProcessShutdownParameters(OUT LPDWORD lpdwLevel,
967 OUT LPDWORD lpdwFlags)
968 {
969 NTSTATUS Status;
970 BASE_API_MESSAGE ApiMessage;
971 PBASE_GETSET_PROCESS_SHUTDOWN_PARAMS ShutdownParametersRequest = &ApiMessage.Data.ShutdownParametersRequest;
972
973 /* Ask CSRSS for shutdown information */
974 Status = CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
975 NULL,
976 CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepGetProcessShutdownParam),
977 sizeof(BASE_GETSET_PROCESS_SHUTDOWN_PARAMS));
978 if (!NT_SUCCESS(Status))
979 {
980 /* Return the failure from CSRSS */
981 BaseSetLastNTError(Status);
982 return FALSE;
983 }
984
985 /* Get the data back */
986 *lpdwLevel = ShutdownParametersRequest->ShutdownLevel;
987 *lpdwFlags = ShutdownParametersRequest->ShutdownFlags;
988 return TRUE;
989 }
990
991 /*
992 * @implemented
993 */
994 BOOL
995 WINAPI
996 SetProcessShutdownParameters(IN DWORD dwLevel,
997 IN DWORD dwFlags)
998 {
999 NTSTATUS Status;
1000 BASE_API_MESSAGE ApiMessage;
1001 PBASE_GETSET_PROCESS_SHUTDOWN_PARAMS ShutdownParametersRequest = &ApiMessage.Data.ShutdownParametersRequest;
1002
1003 /* Write the data into the CSRSS request and send it */
1004 ShutdownParametersRequest->ShutdownLevel = dwLevel;
1005 ShutdownParametersRequest->ShutdownFlags = dwFlags;
1006 Status = CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
1007 NULL,
1008 CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepSetProcessShutdownParam),
1009 sizeof(BASE_GETSET_PROCESS_SHUTDOWN_PARAMS));
1010 if (!NT_SUCCESS(Status))
1011 {
1012 /* Return the failure from CSRSS */
1013 BaseSetLastNTError(Status);
1014 return FALSE;
1015 }
1016
1017 /* All went well */
1018 return TRUE;
1019 }
1020
1021 /*
1022 * @implemented
1023 */
1024 BOOL
1025 WINAPI
1026 GetProcessWorkingSetSizeEx(IN HANDLE hProcess,
1027 OUT PSIZE_T lpMinimumWorkingSetSize,
1028 OUT PSIZE_T lpMaximumWorkingSetSize,
1029 OUT PDWORD Flags)
1030 {
1031 QUOTA_LIMITS_EX QuotaLimits;
1032 NTSTATUS Status;
1033
1034 /* Query the kernel about this */
1035 Status = NtQueryInformationProcess(hProcess,
1036 ProcessQuotaLimits,
1037 &QuotaLimits,
1038 sizeof(QUOTA_LIMITS_EX),
1039 NULL);
1040 if (!NT_SUCCESS(Status))
1041 {
1042 /* Return error */
1043 BaseSetLastNTError(Status);
1044 return FALSE;
1045 }
1046
1047 /* Copy the quota information out */
1048 *lpMinimumWorkingSetSize = QuotaLimits.MinimumWorkingSetSize;
1049 *lpMaximumWorkingSetSize = QuotaLimits.MaximumWorkingSetSize;
1050 *Flags = QuotaLimits.Flags;
1051 return TRUE;
1052 }
1053
1054 /*
1055 * @implemented
1056 */
1057 BOOL
1058 WINAPI
1059 GetProcessWorkingSetSize(IN HANDLE hProcess,
1060 OUT PSIZE_T lpMinimumWorkingSetSize,
1061 OUT PSIZE_T lpMaximumWorkingSetSize)
1062 {
1063 DWORD Dummy;
1064 return GetProcessWorkingSetSizeEx(hProcess,
1065 lpMinimumWorkingSetSize,
1066 lpMaximumWorkingSetSize,
1067 &Dummy);
1068 }
1069
1070 /*
1071 * @implemented
1072 */
1073 BOOL
1074 WINAPI
1075 SetProcessWorkingSetSizeEx(IN HANDLE hProcess,
1076 IN SIZE_T dwMinimumWorkingSetSize,
1077 IN SIZE_T dwMaximumWorkingSetSize,
1078 IN DWORD Flags)
1079 {
1080 QUOTA_LIMITS_EX QuotaLimits;
1081 NTSTATUS Status, ReturnStatus;
1082 BOOL Result;
1083 PVOID State;
1084 ULONG Privilege = SE_INC_BASE_PRIORITY_PRIVILEGE;
1085
1086 /* Zero out the input structure */
1087 RtlZeroMemory(&QuotaLimits, sizeof(QuotaLimits));
1088
1089 /* Check if the caller sent any limits */
1090 if ((dwMinimumWorkingSetSize) && (dwMaximumWorkingSetSize))
1091 {
1092 /* Write the quota information */
1093 QuotaLimits.MinimumWorkingSetSize = dwMinimumWorkingSetSize;
1094 QuotaLimits.MaximumWorkingSetSize = dwMaximumWorkingSetSize;
1095 QuotaLimits.Flags = Flags;
1096
1097 /* Acquire the required privilege */
1098 Status = RtlAcquirePrivilege(&Privilege, 1, 0, &State);
1099
1100 /* Request the new quotas */
1101 ReturnStatus = NtSetInformationProcess(hProcess,
1102 ProcessQuotaLimits,
1103 &QuotaLimits,
1104 sizeof(QuotaLimits));
1105 Result = NT_SUCCESS(ReturnStatus);
1106 if (NT_SUCCESS(Status))
1107 {
1108 /* Release the privilege and set succes code */
1109 ASSERT(State != NULL);
1110 RtlReleasePrivilege(State);
1111 State = NULL;
1112 }
1113 }
1114 else
1115 {
1116 /* No limits, fail the call */
1117 ReturnStatus = STATUS_INVALID_PARAMETER;
1118 Result = FALSE;
1119 }
1120
1121 /* Return result code, set error code if this was a failure */
1122 if (!Result) BaseSetLastNTError(ReturnStatus);
1123 return Result;
1124 }
1125
1126 /*
1127 * @implemented
1128 */
1129 BOOL
1130 WINAPI
1131 SetProcessWorkingSetSize(IN HANDLE hProcess,
1132 IN SIZE_T dwMinimumWorkingSetSize,
1133 IN SIZE_T dwMaximumWorkingSetSize)
1134 {
1135 /* Call the newer API */
1136 return SetProcessWorkingSetSizeEx(hProcess,
1137 dwMinimumWorkingSetSize,
1138 dwMaximumWorkingSetSize,
1139 0);
1140 }
1141
1142 /*
1143 * @implemented
1144 */
1145 BOOL
1146 WINAPI
1147 GetProcessTimes(IN HANDLE hProcess,
1148 IN LPFILETIME lpCreationTime,
1149 IN LPFILETIME lpExitTime,
1150 IN LPFILETIME lpKernelTime,
1151 IN LPFILETIME lpUserTime)
1152 {
1153 KERNEL_USER_TIMES Kut;
1154 NTSTATUS Status;
1155
1156 /* Query the times */
1157 Status = NtQueryInformationProcess(hProcess,
1158 ProcessTimes,
1159 &Kut,
1160 sizeof(Kut),
1161 NULL);
1162 if (!NT_SUCCESS(Status))
1163 {
1164 /* Handle failure */
1165 BaseSetLastNTError(Status);
1166 return FALSE;
1167 }
1168
1169 /* Copy all the times and return success */
1170 lpCreationTime->dwLowDateTime = Kut.CreateTime.u.LowPart;
1171 lpCreationTime->dwHighDateTime = Kut.CreateTime.u.HighPart;
1172 lpExitTime->dwLowDateTime = Kut.ExitTime.u.LowPart;
1173 lpExitTime->dwHighDateTime = Kut.ExitTime.u.HighPart;
1174 lpKernelTime->dwLowDateTime = Kut.KernelTime.u.LowPart;
1175 lpKernelTime->dwHighDateTime = Kut.KernelTime.u.HighPart;
1176 lpUserTime->dwLowDateTime = Kut.UserTime.u.LowPart;
1177 lpUserTime->dwHighDateTime = Kut.UserTime.u.HighPart;
1178 return TRUE;
1179 }
1180
1181 /*
1182 * @implemented
1183 */
1184 HANDLE
1185 WINAPI
1186 GetCurrentProcess(VOID)
1187 {
1188 return (HANDLE)NtCurrentProcess();
1189 }
1190
1191 /*
1192 * @implemented
1193 */
1194 HANDLE
1195 WINAPI
1196 GetCurrentThread(VOID)
1197 {
1198 return (HANDLE)NtCurrentThread();
1199 }
1200
1201 /*
1202 * @implemented
1203 */
1204 DWORD
1205 WINAPI
1206 GetCurrentProcessId(VOID)
1207 {
1208 return HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess);
1209 }
1210
1211 /*
1212 * @implemented
1213 */
1214 BOOL
1215 WINAPI
1216 GetExitCodeProcess(IN HANDLE hProcess,
1217 IN LPDWORD lpExitCode)
1218 {
1219 PROCESS_BASIC_INFORMATION ProcessBasic;
1220 NTSTATUS Status;
1221
1222 /* Ask the kernel */
1223 Status = NtQueryInformationProcess(hProcess,
1224 ProcessBasicInformation,
1225 &ProcessBasic,
1226 sizeof(PROCESS_BASIC_INFORMATION),
1227 NULL);
1228 if (!NT_SUCCESS(Status))
1229 {
1230 /* We failed, was this because this is a VDM process? */
1231 if (BaseCheckForVDM(hProcess, lpExitCode) == TRUE) return TRUE;
1232
1233 /* Not a VDM process, fail the call */
1234 BaseSetLastNTError(Status);
1235 return FALSE;
1236 }
1237
1238 /* Succes case, return the exit code */
1239 *lpExitCode = (DWORD)ProcessBasic.ExitStatus;
1240 return TRUE;
1241 }
1242
1243 /*
1244 * @implemented
1245 */
1246 DWORD
1247 WINAPI
1248 GetProcessId(IN HANDLE Process)
1249 {
1250 PROCESS_BASIC_INFORMATION ProcessBasic;
1251 NTSTATUS Status;
1252
1253 /* Query the kernel */
1254 Status = NtQueryInformationProcess(Process,
1255 ProcessBasicInformation,
1256 &ProcessBasic,
1257 sizeof(PROCESS_BASIC_INFORMATION),
1258 NULL);
1259 if (!NT_SUCCESS(Status))
1260 {
1261 /* Handle failure */
1262 BaseSetLastNTError(Status);
1263 return 0;
1264 }
1265
1266 /* Return the PID */
1267 return (DWORD)ProcessBasic.UniqueProcessId;
1268 }
1269
1270 /*
1271 * @implemented
1272 */
1273 HANDLE
1274 WINAPI
1275 OpenProcess(IN DWORD dwDesiredAccess,
1276 IN BOOL bInheritHandle,
1277 IN DWORD dwProcessId)
1278 {
1279 NTSTATUS Status;
1280 HANDLE ProcessHandle;
1281 OBJECT_ATTRIBUTES ObjectAttributes;
1282 CLIENT_ID ClientId;
1283
1284 /* Setup the input client ID structure */
1285 ClientId.UniqueProcess = UlongToHandle(dwProcessId);
1286 ClientId.UniqueThread = 0;
1287
1288 /* This is needed just to define the inheritance flags */
1289 InitializeObjectAttributes(&ObjectAttributes,
1290 NULL,
1291 (bInheritHandle ? OBJ_INHERIT : 0),
1292 NULL,
1293 NULL);
1294
1295 /* Now try to open the process */
1296 Status = NtOpenProcess(&ProcessHandle,
1297 dwDesiredAccess,
1298 &ObjectAttributes,
1299 &ClientId);
1300 if (!NT_SUCCESS(Status))
1301 {
1302 /* Handle failure */
1303 BaseSetLastNTError(Status);
1304 return NULL;
1305 }
1306
1307 /* Otherwise return a handle to the process */
1308 return ProcessHandle;
1309 }
1310
1311 /*
1312 * @implemented
1313 */
1314 VOID
1315 WINAPI
1316 RegisterWaitForInputIdle(IN WaitForInputIdleType lpfnRegisterWaitForInputIdle)
1317 {
1318 /* Write the global function pointer */
1319 UserWaitForInputIdleRoutine = lpfnRegisterWaitForInputIdle;
1320 }
1321
1322 /*
1323 * @implemented
1324 */
1325 VOID
1326 WINAPI
1327 GetStartupInfoW(IN LPSTARTUPINFOW lpStartupInfo)
1328 {
1329 PRTL_USER_PROCESS_PARAMETERS Params;
1330
1331 /* Get the process parameters */
1332 Params = NtCurrentPeb()->ProcessParameters;
1333
1334 /* Copy the data out of there */
1335 lpStartupInfo->cb = sizeof(STARTUPINFOW);
1336 lpStartupInfo->lpReserved = Params->ShellInfo.Buffer;
1337 lpStartupInfo->lpDesktop = Params->DesktopInfo.Buffer;
1338 lpStartupInfo->lpTitle = Params->WindowTitle.Buffer;
1339 lpStartupInfo->dwX = Params->StartingX;
1340 lpStartupInfo->dwY = Params->StartingY;
1341 lpStartupInfo->dwXSize = Params->CountX;
1342 lpStartupInfo->dwYSize = Params->CountY;
1343 lpStartupInfo->dwXCountChars = Params->CountCharsX;
1344 lpStartupInfo->dwYCountChars = Params->CountCharsY;
1345 lpStartupInfo->dwFillAttribute = Params->FillAttribute;
1346 lpStartupInfo->dwFlags = Params->WindowFlags;
1347 lpStartupInfo->wShowWindow = (WORD)Params->ShowWindowFlags;
1348 lpStartupInfo->cbReserved2 = Params->RuntimeData.Length;
1349 lpStartupInfo->lpReserved2 = (LPBYTE)Params->RuntimeData.Buffer;
1350
1351 /* Check if the standard handles are being used for other features */
1352 if (lpStartupInfo->dwFlags & (STARTF_USESTDHANDLES |
1353 STARTF_USEHOTKEY |
1354 STARTF_SHELLPRIVATE))
1355 {
1356 /* These are, so copy the standard handles too */
1357 lpStartupInfo->hStdInput = Params->StandardInput;
1358 lpStartupInfo->hStdOutput = Params->StandardOutput;
1359 lpStartupInfo->hStdError = Params->StandardError;
1360 }
1361 }
1362
1363 /*
1364 * @implemented
1365 */
1366 VOID
1367 WINAPI
1368 GetStartupInfoA(IN LPSTARTUPINFOA lpStartupInfo)
1369 {
1370 PRTL_USER_PROCESS_PARAMETERS Params;
1371 ANSI_STRING TitleString, ShellString, DesktopString;
1372 LPSTARTUPINFOA StartupInfo;
1373 NTSTATUS Status;
1374
1375 /* Get the cached information as well as the PEB parameters */
1376 StartupInfo = BaseAnsiStartupInfo;
1377 Params = NtCurrentPeb()->ProcessParameters;
1378
1379 /* Check if this is the first time we have to get the cached version */
1380 while (!StartupInfo)
1381 {
1382 /* Create new ANSI startup info */
1383 StartupInfo = RtlAllocateHeap(RtlGetProcessHeap(),
1384 0,
1385 sizeof(*StartupInfo));
1386 if (StartupInfo)
1387 {
1388 /* Zero out string pointers in case we fail to create them */
1389 StartupInfo->lpReserved = 0;
1390 StartupInfo->lpDesktop = 0;
1391 StartupInfo->lpTitle = 0;
1392
1393 /* Set the size */
1394 StartupInfo->cb = sizeof(*StartupInfo);
1395
1396 /* Copy what's already stored in the PEB */
1397 StartupInfo->dwX = Params->StartingX;
1398 StartupInfo->dwY = Params->StartingY;
1399 StartupInfo->dwXSize = Params->CountX;
1400 StartupInfo->dwYSize = Params->CountY;
1401 StartupInfo->dwXCountChars = Params->CountCharsX;
1402 StartupInfo->dwYCountChars = Params->CountCharsY;
1403 StartupInfo->dwFillAttribute = Params->FillAttribute;
1404 StartupInfo->dwFlags = Params->WindowFlags;
1405 StartupInfo->wShowWindow = (WORD)Params->ShowWindowFlags;
1406 StartupInfo->cbReserved2 = Params->RuntimeData.Length;
1407 StartupInfo->lpReserved2 = (LPBYTE)Params->RuntimeData.Buffer;
1408 StartupInfo->hStdInput = Params->StandardInput;
1409 StartupInfo->hStdOutput = Params->StandardOutput;
1410 StartupInfo->hStdError = Params->StandardError;
1411
1412 /* Copy shell info string */
1413 Status = RtlUnicodeStringToAnsiString(&ShellString,
1414 &Params->ShellInfo,
1415 TRUE);
1416 if (NT_SUCCESS(Status))
1417 {
1418 /* Save it */
1419 StartupInfo->lpReserved = ShellString.Buffer;
1420
1421 /* Copy desktop info string */
1422 Status = RtlUnicodeStringToAnsiString(&DesktopString,
1423 &Params->DesktopInfo,
1424 TRUE);
1425 if (NT_SUCCESS(Status))
1426 {
1427 /* Save it */
1428 StartupInfo->lpDesktop = DesktopString.Buffer;
1429
1430 /* Copy window title string */
1431 Status = RtlUnicodeStringToAnsiString(&TitleString,
1432 &Params->WindowTitle,
1433 TRUE);
1434 if (NT_SUCCESS(Status))
1435 {
1436 /* Save it */
1437 StartupInfo->lpTitle = TitleString.Buffer;
1438
1439 /* We finished with the ANSI version, try to cache it */
1440 if (!InterlockedCompareExchangePointer(&BaseAnsiStartupInfo,
1441 StartupInfo,
1442 NULL))
1443 {
1444 /* We were the first thread through, use the data */
1445 break;
1446 }
1447
1448 /* Someone beat us to it, use their data instead */
1449 StartupInfo = BaseAnsiStartupInfo;
1450 Status = STATUS_SUCCESS;
1451
1452 /* We're going to free our own stuff, but not raise */
1453 RtlFreeAnsiString(&TitleString);
1454 }
1455 RtlFreeAnsiString(&DesktopString);
1456 }
1457 RtlFreeAnsiString(&ShellString);
1458 }
1459 RtlFreeHeap(RtlGetProcessHeap(), 0, StartupInfo);
1460 }
1461 else
1462 {
1463 /* No memory, fail */
1464 Status = STATUS_NO_MEMORY;
1465 }
1466
1467 /* Raise an error unless we got here due to the race condition */
1468 if (!NT_SUCCESS(Status)) RtlRaiseStatus(Status);
1469 }
1470
1471 /* Now copy from the cached ANSI version */
1472 lpStartupInfo->cb = StartupInfo->cb;
1473 lpStartupInfo->lpReserved = StartupInfo->lpReserved;
1474 lpStartupInfo->lpDesktop = StartupInfo->lpDesktop;
1475 lpStartupInfo->lpTitle = StartupInfo->lpTitle;
1476 lpStartupInfo->dwX = StartupInfo->dwX;
1477 lpStartupInfo->dwY = StartupInfo->dwY;
1478 lpStartupInfo->dwXSize = StartupInfo->dwXSize;
1479 lpStartupInfo->dwYSize = StartupInfo->dwYSize;
1480 lpStartupInfo->dwXCountChars = StartupInfo->dwXCountChars;
1481 lpStartupInfo->dwYCountChars = StartupInfo->dwYCountChars;
1482 lpStartupInfo->dwFillAttribute = StartupInfo->dwFillAttribute;
1483 lpStartupInfo->dwFlags = StartupInfo->dwFlags;
1484 lpStartupInfo->wShowWindow = StartupInfo->wShowWindow;
1485 lpStartupInfo->cbReserved2 = StartupInfo->cbReserved2;
1486 lpStartupInfo->lpReserved2 = StartupInfo->lpReserved2;
1487
1488 /* Check if the shell is hijacking the handles for other features */
1489 if (lpStartupInfo->dwFlags &
1490 (STARTF_USESTDHANDLES | STARTF_USEHOTKEY | STARTF_SHELLPRIVATE))
1491 {
1492 /* It isn't, so we can return the raw values */
1493 lpStartupInfo->hStdInput = StartupInfo->hStdInput;
1494 lpStartupInfo->hStdOutput = StartupInfo->hStdOutput;
1495 lpStartupInfo->hStdError = StartupInfo->hStdError;
1496 }
1497 else
1498 {
1499 /* It is, so make sure nobody uses these as console handles */
1500 lpStartupInfo->hStdInput = INVALID_HANDLE_VALUE;
1501 lpStartupInfo->hStdOutput = INVALID_HANDLE_VALUE;
1502 lpStartupInfo->hStdError = INVALID_HANDLE_VALUE;
1503 }
1504 }
1505
1506 /*
1507 * @implemented
1508 */
1509 BOOL
1510 WINAPI
1511 FlushInstructionCache(IN HANDLE hProcess,
1512 IN LPCVOID lpBaseAddress,
1513 IN SIZE_T dwSize)
1514 {
1515 NTSTATUS Status;
1516
1517 /* Call the native function */
1518 Status = NtFlushInstructionCache(hProcess, (PVOID)lpBaseAddress, dwSize);
1519 if (!NT_SUCCESS(Status))
1520 {
1521 /* Handle failure case */
1522 BaseSetLastNTError(Status);
1523 return FALSE;
1524 }
1525
1526 /* All good */
1527 return TRUE;
1528 }
1529
1530 /*
1531 * @implemented
1532 */
1533 VOID
1534 WINAPI
1535 ExitProcess(IN UINT uExitCode)
1536 {
1537 BASE_API_MESSAGE ApiMessage;
1538 PBASE_EXIT_PROCESS ExitProcessRequest = &ApiMessage.Data.ExitProcessRequest;
1539
1540 ASSERT(!BaseRunningInServerProcess);
1541
1542 _SEH2_TRY
1543 {
1544 /* Acquire the PEB lock */
1545 RtlAcquirePebLock();
1546
1547 /* Kill all the threads */
1548 NtTerminateProcess(NULL, 0);
1549
1550 /* Unload all DLLs */
1551 LdrShutdownProcess();
1552
1553 /* Notify Base Server of process termination */
1554 ExitProcessRequest->uExitCode = uExitCode;
1555 CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
1556 NULL,
1557 CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepExitProcess),
1558 sizeof(BASE_EXIT_PROCESS));
1559
1560 /* Now do it again */
1561 NtTerminateProcess(NtCurrentProcess(), uExitCode);
1562 }
1563 _SEH2_FINALLY
1564 {
1565 /* Release the PEB lock */
1566 RtlReleasePebLock();
1567 }
1568 _SEH2_END;
1569
1570 /* should never get here */
1571 ASSERT(0);
1572 while(1);
1573 }
1574
1575 /*
1576 * @implemented
1577 */
1578 BOOL
1579 WINAPI
1580 TerminateProcess(IN HANDLE hProcess,
1581 IN UINT uExitCode)
1582 {
1583 NTSTATUS Status;
1584
1585 /* Check if no handle was passed in */
1586 if (!hProcess)
1587 {
1588 /* Set error code */
1589 SetLastError(ERROR_INVALID_HANDLE);
1590 }
1591 else
1592 {
1593 /* Otherwise, try to terminate the process */
1594 Status = NtTerminateProcess(hProcess, uExitCode);
1595 if (NT_SUCCESS(Status)) return TRUE;
1596
1597 /* It failed, convert error code */
1598 BaseSetLastNTError(Status);
1599 }
1600
1601 /* This is the failure path */
1602 return FALSE;
1603 }
1604
1605 /*
1606 * @implemented
1607 */
1608 VOID
1609 WINAPI
1610 FatalAppExitA(UINT uAction,
1611 LPCSTR lpMessageText)
1612 {
1613 PUNICODE_STRING MessageTextU;
1614 ANSI_STRING MessageText;
1615 NTSTATUS Status;
1616
1617 /* Initialize the string using the static TEB pointer */
1618 MessageTextU = &NtCurrentTeb()->StaticUnicodeString;
1619 RtlInitAnsiString(&MessageText, (LPSTR)lpMessageText);
1620
1621 /* Convert to unicode and just exit normally if this failed */
1622 Status = RtlAnsiStringToUnicodeString(MessageTextU, &MessageText, FALSE);
1623 if (!NT_SUCCESS(Status)) ExitProcess(0);
1624
1625 /* Call the Wide function */
1626 FatalAppExitW(uAction, MessageTextU->Buffer);
1627 }
1628
1629 /*
1630 * @implemented
1631 */
1632 VOID
1633 WINAPI
1634 FatalAppExitW(IN UINT uAction,
1635 IN LPCWSTR lpMessageText)
1636 {
1637 UNICODE_STRING UnicodeString;
1638 ULONG Response;
1639 NTSTATUS Status;
1640
1641 /* Setup the string to print out */
1642 RtlInitUnicodeString(&UnicodeString, lpMessageText);
1643
1644 /* Display the hard error no matter what */
1645 Status = NtRaiseHardError(STATUS_FATAL_APP_EXIT | HARDERROR_OVERRIDE_ERRORMODE,
1646 1,
1647 1,
1648 (PULONG_PTR)&UnicodeString,
1649 OptionOkCancel,
1650 &Response);
1651
1652 /* Give the user a chance to abort */
1653 if ((NT_SUCCESS(Status)) && (Response == ResponseCancel)) return;
1654
1655 /* Otherwise kill the process */
1656 ExitProcess(0);
1657 }
1658
1659 /*
1660 * @implemented
1661 */
1662 VOID
1663 WINAPI
1664 FatalExit(IN int ExitCode)
1665 {
1666 #if DBG
1667 /* On Checked builds, Windows gives you a nice little debugger UI */
1668 CHAR ch[2];
1669 DbgPrint("FatalExit...\n");
1670 DbgPrint("\n");
1671
1672 while (TRUE)
1673 {
1674 DbgPrompt( "A (Abort), B (Break), I (Ignore)? ", ch, sizeof(ch));
1675 switch (ch[0])
1676 {
1677 case 'B': case 'b':
1678 DbgBreakPoint();
1679 break;
1680
1681 case 'A': case 'a':
1682 ExitProcess(ExitCode);
1683
1684 case 'I': case 'i':
1685 return;
1686 }
1687 }
1688 #endif
1689 /* On other builds, just kill the process */
1690 ExitProcess(ExitCode);
1691 }
1692
1693 /*
1694 * @implemented
1695 */
1696 DWORD
1697 WINAPI
1698 GetPriorityClass(IN HANDLE hProcess)
1699 {
1700 NTSTATUS Status;
1701 PROCESS_PRIORITY_CLASS PriorityClass;
1702
1703 /* Query the kernel */
1704 Status = NtQueryInformationProcess(hProcess,
1705 ProcessPriorityClass,
1706 &PriorityClass,
1707 sizeof(PROCESS_PRIORITY_CLASS),
1708 NULL);
1709 if (NT_SUCCESS(Status))
1710 {
1711 /* Handle the conversion from NT to Win32 classes */
1712 switch (PriorityClass.PriorityClass)
1713 {
1714 case PROCESS_PRIORITY_CLASS_IDLE: return IDLE_PRIORITY_CLASS;
1715 case PROCESS_PRIORITY_CLASS_BELOW_NORMAL: return BELOW_NORMAL_PRIORITY_CLASS;
1716 case PROCESS_PRIORITY_CLASS_ABOVE_NORMAL: return ABOVE_NORMAL_PRIORITY_CLASS;
1717 case PROCESS_PRIORITY_CLASS_HIGH: return HIGH_PRIORITY_CLASS;
1718 case PROCESS_PRIORITY_CLASS_REALTIME: return REALTIME_PRIORITY_CLASS;
1719 case PROCESS_PRIORITY_CLASS_NORMAL: default: return NORMAL_PRIORITY_CLASS;
1720 }
1721 }
1722
1723 /* Failure path */
1724 BaseSetLastNTError(Status);
1725 return FALSE;
1726 }
1727
1728 /*
1729 * @implemented
1730 */
1731 BOOL
1732 WINAPI
1733 SetPriorityClass(IN HANDLE hProcess,
1734 IN DWORD dwPriorityClass)
1735 {
1736 NTSTATUS Status;
1737 PVOID State = NULL;
1738 PROCESS_PRIORITY_CLASS PriorityClass;
1739
1740 /* Handle conversion from Win32 to NT priority classes */
1741 switch (dwPriorityClass)
1742 {
1743 case IDLE_PRIORITY_CLASS:
1744 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_IDLE;
1745 break;
1746
1747 case BELOW_NORMAL_PRIORITY_CLASS:
1748 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_BELOW_NORMAL;
1749 break;
1750
1751 case NORMAL_PRIORITY_CLASS:
1752 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_NORMAL;
1753 break;
1754
1755 case ABOVE_NORMAL_PRIORITY_CLASS:
1756 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_ABOVE_NORMAL;
1757 break;
1758
1759 case HIGH_PRIORITY_CLASS:
1760 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_HIGH;
1761 break;
1762
1763 case REALTIME_PRIORITY_CLASS:
1764 /* Try to acquire the privilege. If it fails, just use HIGH */
1765 State = BasepIsRealtimeAllowed(TRUE);
1766 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_HIGH;
1767 PriorityClass.PriorityClass += (State != NULL);
1768 break;
1769
1770 default:
1771 /* Unrecognized priority classes don't make it to the kernel */
1772 SetLastError(ERROR_INVALID_PARAMETER);
1773 return FALSE;
1774 }
1775
1776 /* Send the request to the kernel, and don't touch the foreground flag */
1777 PriorityClass.Foreground = FALSE;
1778 Status = NtSetInformationProcess(hProcess,
1779 ProcessPriorityClass,
1780 &PriorityClass,
1781 sizeof(PROCESS_PRIORITY_CLASS));
1782
1783 /* Release the privilege if we had it */
1784 if (State) RtlReleasePrivilege(State);
1785 if (!NT_SUCCESS(Status))
1786 {
1787 /* Handle error path */
1788 BaseSetLastNTError(Status);
1789 return FALSE;
1790 }
1791
1792 /* All done */
1793 return TRUE;
1794 }
1795
1796 /*
1797 * @implemented
1798 */
1799 DWORD
1800 WINAPI
1801 GetProcessVersion(IN DWORD ProcessId)
1802 {
1803 DWORD Version = 0;
1804 PIMAGE_NT_HEADERS NtHeader;
1805 PIMAGE_DOS_HEADER DosHeader;
1806 PPEB Peb;
1807 PROCESS_BASIC_INFORMATION ProcessBasicInfo;
1808 PVOID BaseAddress;
1809 ULONG e_lfanew;
1810 HANDLE ProcessHandle = NULL;
1811 NTSTATUS Status;
1812 USHORT VersionData[2];
1813 BOOLEAN Result;
1814
1815 /* We'll be accessing stuff that can fault, so protect everything with SEH */
1816 _SEH2_TRY
1817 {
1818 /* It this an in-process or out-of-process request? */
1819 if (!(ProcessId) || (GetCurrentProcessId() == ProcessId))
1820 {
1821 /* It's in-process, so just read our own header */
1822 NtHeader = RtlImageNtHeader(NtCurrentPeb()->ImageBaseAddress);
1823 if (!NtHeader)
1824 {
1825 /* Unable to read the NT header, something is wrong here... */
1826 Status = STATUS_INVALID_IMAGE_FORMAT;
1827 goto Error;
1828 }
1829
1830 /* Get the version straight out of the NT header */
1831 Version = MAKELONG(NtHeader->OptionalHeader.MinorSubsystemVersion,
1832 NtHeader->OptionalHeader.MajorSubsystemVersion);
1833 }
1834 else
1835 {
1836 /* Out-of-process, so open it */
1837 ProcessHandle = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION,
1838 FALSE,
1839 ProcessId);
1840 if (!ProcessHandle) _SEH2_YIELD(return 0);
1841
1842 /* Try to find out where its PEB lives */
1843 Status = NtQueryInformationProcess(ProcessHandle,
1844 ProcessBasicInformation,
1845 &ProcessBasicInfo,
1846 sizeof(ProcessBasicInfo),
1847 NULL);
1848
1849 if (!NT_SUCCESS(Status)) goto Error;
1850 Peb = ProcessBasicInfo.PebBaseAddress;
1851
1852 /* Now that we have the PEB, read the image base address out of it */
1853 Result = ReadProcessMemory(ProcessHandle,
1854 &Peb->ImageBaseAddress,
1855 &BaseAddress,
1856 sizeof(BaseAddress),
1857 NULL);
1858 if (!Result) goto Error;
1859
1860 /* Now read the e_lfanew (offset to NT header) from the base */
1861 DosHeader = BaseAddress;
1862 Result = ReadProcessMemory(ProcessHandle,
1863 &DosHeader->e_lfanew,
1864 &e_lfanew,
1865 sizeof(e_lfanew),
1866 NULL);
1867 if (!Result) goto Error;
1868
1869 /* And finally, read the NT header itself by adding the offset */
1870 NtHeader = (PVOID)((ULONG_PTR)BaseAddress + e_lfanew);
1871 Result = ReadProcessMemory(ProcessHandle,
1872 &NtHeader->OptionalHeader.MajorSubsystemVersion,
1873 &VersionData,
1874 sizeof(VersionData),
1875 NULL);
1876 if (!Result) goto Error;
1877
1878 /* Get the version straight out of the NT header */
1879 Version = MAKELONG(VersionData[0], VersionData[1]);
1880
1881 Error:
1882 /* If there was an error anywhere, set the last error */
1883 if (!NT_SUCCESS(Status)) BaseSetLastNTError(Status);
1884 }
1885 }
1886 _SEH2_FINALLY
1887 {
1888 /* Close the process handle */
1889 if (ProcessHandle) CloseHandle(ProcessHandle);
1890 }
1891 _SEH2_END;
1892
1893 /* And return the version data */
1894 return Version;
1895 }
1896
1897 /*
1898 * @implemented
1899 */
1900 BOOL
1901 WINAPI
1902 GetProcessIoCounters(IN HANDLE hProcess,
1903 OUT PIO_COUNTERS lpIoCounters)
1904 {
1905 NTSTATUS Status;
1906
1907 /* Query the kernel. Structures are identical, so let it do the copy too. */
1908 Status = NtQueryInformationProcess(hProcess,
1909 ProcessIoCounters,
1910 lpIoCounters,
1911 sizeof(IO_COUNTERS),
1912 NULL);
1913 if (!NT_SUCCESS(Status))
1914 {
1915 /* Handle error path */
1916 BaseSetLastNTError(Status);
1917 return FALSE;
1918 }
1919
1920 /* All done */
1921 return TRUE;
1922 }
1923
1924 /*
1925 * @implemented
1926 */
1927 BOOL
1928 WINAPI
1929 GetProcessPriorityBoost(IN HANDLE hProcess,
1930 OUT PBOOL pDisablePriorityBoost)
1931 {
1932 NTSTATUS Status;
1933 ULONG PriorityBoost;
1934
1935 /* Query the kernel */
1936 Status = NtQueryInformationProcess(hProcess,
1937 ProcessPriorityBoost,
1938 &PriorityBoost,
1939 sizeof(ULONG),
1940 NULL);
1941 if (NT_SUCCESS(Status))
1942 {
1943 /* Convert from ULONG to a BOOL */
1944 *pDisablePriorityBoost = PriorityBoost ? TRUE : FALSE;
1945 return TRUE;
1946 }
1947
1948 /* Handle error path */
1949 BaseSetLastNTError(Status);
1950 return FALSE;
1951 }
1952
1953 /*
1954 * @implemented
1955 */
1956 BOOL
1957 WINAPI
1958 SetProcessPriorityBoost(IN HANDLE hProcess,
1959 IN BOOL bDisablePriorityBoost)
1960 {
1961 NTSTATUS Status;
1962 ULONG PriorityBoost;
1963
1964 /* Enforce that this is a BOOL, and send it to the kernel as a ULONG */
1965 PriorityBoost = (bDisablePriorityBoost ? TRUE : FALSE);
1966 Status = NtSetInformationProcess(hProcess,
1967 ProcessPriorityBoost,
1968 &PriorityBoost,
1969 sizeof(ULONG));
1970 if (!NT_SUCCESS(Status))
1971 {
1972 /* Handle error path */
1973 BaseSetLastNTError(Status);
1974 return FALSE;
1975 }
1976
1977 /* All done */
1978 return TRUE;
1979 }
1980
1981 /*
1982 * @implemented
1983 */
1984 BOOL
1985 WINAPI
1986 GetProcessHandleCount(IN HANDLE hProcess,
1987 OUT PDWORD pdwHandleCount)
1988 {
1989 ULONG phc;
1990 NTSTATUS Status;
1991
1992 /* Query the kernel */
1993 Status = NtQueryInformationProcess(hProcess,
1994 ProcessHandleCount,
1995 &phc,
1996 sizeof(ULONG),
1997 NULL);
1998 if (NT_SUCCESS(Status))
1999 {
2000 /* Copy the count and return sucecss */
2001 *pdwHandleCount = phc;
2002 return TRUE;
2003 }
2004
2005 /* Handle error path */
2006 BaseSetLastNTError(Status);
2007 return FALSE;
2008 }
2009
2010 /*
2011 * @implemented
2012 */
2013 BOOL
2014 WINAPI
2015 IsWow64Process(IN HANDLE hProcess,
2016 OUT PBOOL Wow64Process)
2017 {
2018 ULONG_PTR pbi;
2019 NTSTATUS Status;
2020
2021 /* Query the kernel */
2022 Status = NtQueryInformationProcess(hProcess,
2023 ProcessWow64Information,
2024 &pbi,
2025 sizeof(pbi),
2026 NULL);
2027 if (!NT_SUCCESS(Status))
2028 {
2029 /* Handle error path */
2030 BaseSetLastNTError(Status);
2031 return FALSE;
2032 }
2033
2034 /* Enforce this is a BOOL, and return success */
2035 *Wow64Process = (pbi != 0);
2036 return TRUE;
2037 }
2038
2039 /*
2040 * @implemented
2041 */
2042 LPSTR
2043 WINAPI
2044 GetCommandLineA(VOID)
2045 {
2046 return BaseAnsiCommandLine.Buffer;
2047 }
2048
2049 /*
2050 * @implemented
2051 */
2052 LPWSTR
2053 WINAPI
2054 GetCommandLineW(VOID)
2055 {
2056 return BaseUnicodeCommandLine.Buffer;
2057 }
2058
2059 /*
2060 * @implemented
2061 */
2062 BOOL
2063 NTAPI
2064 ReadProcessMemory(IN HANDLE hProcess,
2065 IN LPCVOID lpBaseAddress,
2066 IN LPVOID lpBuffer,
2067 IN SIZE_T nSize,
2068 OUT SIZE_T* lpNumberOfBytesRead)
2069 {
2070 NTSTATUS Status;
2071
2072 /* Do the read */
2073 Status = NtReadVirtualMemory(hProcess,
2074 (PVOID)lpBaseAddress,
2075 lpBuffer,
2076 nSize,
2077 &nSize);
2078
2079 /* In user-mode, this parameter is optional */
2080 if (lpNumberOfBytesRead) *lpNumberOfBytesRead = nSize;
2081 if (!NT_SUCCESS(Status))
2082 {
2083 /* We failed */
2084 BaseSetLastNTError(Status);
2085 return FALSE;
2086 }
2087
2088 /* Return success */
2089 return TRUE;
2090 }
2091
2092 /*
2093 * @implemented
2094 */
2095 BOOL
2096 NTAPI
2097 WriteProcessMemory(IN HANDLE hProcess,
2098 IN LPVOID lpBaseAddress,
2099 IN LPCVOID lpBuffer,
2100 IN SIZE_T nSize,
2101 OUT SIZE_T *lpNumberOfBytesWritten)
2102 {
2103 NTSTATUS Status;
2104 ULONG OldValue;
2105 SIZE_T RegionSize;
2106 PVOID Base;
2107 BOOLEAN UnProtect;
2108
2109 /* Set parameters for protect call */
2110 RegionSize = nSize;
2111 Base = lpBaseAddress;
2112
2113 /* Check the current status */
2114 Status = NtProtectVirtualMemory(hProcess,
2115 &Base,
2116 &RegionSize,
2117 PAGE_EXECUTE_READWRITE,
2118 &OldValue);
2119 if (NT_SUCCESS(Status))
2120 {
2121 /* Check if we are unprotecting */
2122 UnProtect = OldValue & (PAGE_READWRITE |
2123 PAGE_WRITECOPY |
2124 PAGE_EXECUTE_READWRITE |
2125 PAGE_EXECUTE_WRITECOPY) ? FALSE : TRUE;
2126 if (!UnProtect)
2127 {
2128 /* Set the new protection */
2129 Status = NtProtectVirtualMemory(hProcess,
2130 &Base,
2131 &RegionSize,
2132 OldValue,
2133 &OldValue);
2134
2135 /* Write the memory */
2136 Status = NtWriteVirtualMemory(hProcess,
2137 lpBaseAddress,
2138 (LPVOID)lpBuffer,
2139 nSize,
2140 &nSize);
2141
2142 /* In Win32, the parameter is optional, so handle this case */
2143 if (lpNumberOfBytesWritten) *lpNumberOfBytesWritten = nSize;
2144
2145 if (!NT_SUCCESS(Status))
2146 {
2147 /* We failed */
2148 BaseSetLastNTError(Status);
2149 return FALSE;
2150 }
2151
2152 /* Flush the ITLB */
2153 NtFlushInstructionCache(hProcess, lpBaseAddress, nSize);
2154 return TRUE;
2155 }
2156 else
2157 {
2158 /* Check if we were read only */
2159 if (OldValue & (PAGE_NOACCESS | PAGE_READONLY))
2160 {
2161 /* Restore protection and fail */
2162 NtProtectVirtualMemory(hProcess,
2163 &Base,
2164 &RegionSize,
2165 OldValue,
2166 &OldValue);
2167 BaseSetLastNTError(STATUS_ACCESS_VIOLATION);
2168
2169 /* Note: This is what Windows returns and code depends on it */
2170 return STATUS_ACCESS_VIOLATION;
2171 }
2172
2173 /* Otherwise, do the write */
2174 Status = NtWriteVirtualMemory(hProcess,
2175 lpBaseAddress,
2176 (LPVOID)lpBuffer,
2177 nSize,
2178 &nSize);
2179
2180 /* In Win32, the parameter is optional, so handle this case */
2181 if (lpNumberOfBytesWritten) *lpNumberOfBytesWritten = nSize;
2182
2183 /* And restore the protection */
2184 NtProtectVirtualMemory(hProcess,
2185 &Base,
2186 &RegionSize,
2187 OldValue,
2188 &OldValue);
2189 if (!NT_SUCCESS(Status))
2190 {
2191 /* We failed */
2192 BaseSetLastNTError(STATUS_ACCESS_VIOLATION);
2193
2194 /* Note: This is what Windows returns and code depends on it */
2195 return STATUS_ACCESS_VIOLATION;
2196 }
2197
2198 /* Flush the ITLB */
2199 NtFlushInstructionCache(hProcess, lpBaseAddress, nSize);
2200 return TRUE;
2201 }
2202 }
2203 else
2204 {
2205 /* We failed */
2206 BaseSetLastNTError(Status);
2207 return FALSE;
2208 }
2209 }
2210
2211 /*
2212 * @implemented
2213 */
2214 BOOL
2215 WINAPI
2216 ProcessIdToSessionId(IN DWORD dwProcessId,
2217 OUT PDWORD pSessionId)
2218 {
2219 PROCESS_SESSION_INFORMATION SessionInformation;
2220 OBJECT_ATTRIBUTES ObjectAttributes;
2221 CLIENT_ID ClientId;
2222 HANDLE ProcessHandle;
2223 NTSTATUS Status;
2224
2225 /* Do a quick check if the pointer is not writable */
2226 if (IsBadWritePtr(pSessionId, sizeof(DWORD)))
2227 {
2228 /* Fail fast */
2229 SetLastError(ERROR_INVALID_PARAMETER);
2230 return FALSE;
2231 }
2232
2233 /* Open the process passed in by ID */
2234 ClientId.UniqueProcess = UlongToHandle(dwProcessId);
2235 ClientId.UniqueThread = 0;
2236 InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
2237 Status = NtOpenProcess(&ProcessHandle,
2238 PROCESS_QUERY_INFORMATION,
2239 &ObjectAttributes,
2240 &ClientId);
2241 if (NT_SUCCESS(Status))
2242 {
2243 /* Query the session ID from the kernel */
2244 Status = NtQueryInformationProcess(ProcessHandle,
2245 ProcessSessionInformation,
2246 &SessionInformation,
2247 sizeof(SessionInformation),
2248 NULL);
2249
2250 /* Close the handle and check if we suceeded */
2251 NtClose(ProcessHandle);
2252 if (NT_SUCCESS(Status))
2253 {
2254 /* Return the session ID */
2255 *pSessionId = SessionInformation.SessionId;
2256 return TRUE;
2257 }
2258 }
2259
2260 /* Set error code and fail */
2261 BaseSetLastNTError(Status);
2262 return FALSE;
2263 }
2264
2265
2266 #define AddToHandle(x,y) (x) = (HANDLE)((ULONG_PTR)(x) | (y));
2267 #define RemoveFromHandle(x,y) (x) = (HANDLE)((ULONG_PTR)(x) & ~(y));
2268 C_ASSERT(PROCESS_PRIORITY_CLASS_REALTIME == (PROCESS_PRIORITY_CLASS_HIGH + 1));
2269
2270 /*
2271 * @implemented
2272 */
2273 BOOL
2274 WINAPI
2275 CreateProcessInternalW(IN HANDLE hUserToken,
2276 IN LPCWSTR lpApplicationName,
2277 IN LPWSTR lpCommandLine,
2278 IN LPSECURITY_ATTRIBUTES lpProcessAttributes,
2279 IN LPSECURITY_ATTRIBUTES lpThreadAttributes,
2280 IN BOOL bInheritHandles,
2281 IN DWORD dwCreationFlags,
2282 IN LPVOID lpEnvironment,
2283 IN LPCWSTR lpCurrentDirectory,
2284 IN LPSTARTUPINFOW lpStartupInfo,
2285 IN LPPROCESS_INFORMATION lpProcessInformation,
2286 OUT PHANDLE hNewToken)
2287 {
2288 //
2289 // Core variables used for creating the initial process and thread
2290 //
2291 SECURITY_ATTRIBUTES LocalThreadAttributes, LocalProcessAttributes;
2292 OBJECT_ATTRIBUTES LocalObjectAttributes;
2293 POBJECT_ATTRIBUTES ObjectAttributes;
2294 SECTION_IMAGE_INFORMATION ImageInformation;
2295 IO_STATUS_BLOCK IoStatusBlock;
2296 CLIENT_ID ClientId;
2297 ULONG NoWindow, RegionSize, StackSize, ErrorCode, Flags;
2298 USHORT ImageMachine;
2299 ULONG ParameterFlags, PrivilegeValue, HardErrorMode, ErrorResponse;
2300 ULONG_PTR ErrorParameters[2];
2301 BOOLEAN InJob, SaferNeeded, UseLargePages, HavePrivilege;
2302 BOOLEAN QuerySection, SkipSaferAndAppCompat;
2303 CONTEXT Context;
2304 BASE_API_MESSAGE CsrMsg[2];
2305 PBASE_CREATE_PROCESS CreateProcessMsg;
2306 PCSR_CAPTURE_BUFFER CaptureBuffer;
2307 PVOID BaseAddress, PrivilegeState, RealTimePrivilegeState;
2308 HANDLE DebugHandle, TokenHandle, JobHandle, KeyHandle, ThreadHandle;
2309 HANDLE FileHandle, SectionHandle, ProcessHandle;
2310 ULONG ResumeCount;
2311 PROCESS_PRIORITY_CLASS PriorityClass;
2312 NTSTATUS Status, Status1, ImageDbgStatus;
2313 PPEB Peb, RemotePeb;
2314 PTEB Teb;
2315 INITIAL_TEB InitialTeb;
2316 PVOID TibValue;
2317 PIMAGE_NT_HEADERS NtHeaders;
2318 STARTUPINFOW StartupInfo;
2319 PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
2320 UNICODE_STRING DebuggerString;
2321 BOOL Result;
2322 //
2323 // Variables used for command-line and argument parsing
2324 //
2325 PCHAR pcScan;
2326 SIZE_T n;
2327 WCHAR SaveChar;
2328 ULONG Length, CurdirLength, CmdQuoteLength;
2329 ULONG CmdLineLength, ResultSize;
2330 PWCHAR QuotedCmdLine, AnsiCmdCommand, ExtBuffer, CurrentDirectory;
2331 PWCHAR NullBuffer, ScanString, NameBuffer, SearchPath, DebuggerCmdLine;
2332 ANSI_STRING AnsiEnv;
2333 UNICODE_STRING UnicodeEnv, PathName;
2334 BOOLEAN SearchRetry, QuotesNeeded, CmdLineIsAppName, HasQuotes;
2335
2336 //
2337 // Variables used for Fusion/SxS (Side-by-Side Assemblies)
2338 //
2339 RTL_PATH_TYPE SxsPathType, PathType;
2340 #if _SXS_SUPPORT_ENABLED_
2341 PRTL_BUFFER ByteBuffer;
2342 PRTL_UNICODE_STRING_BUFFER ThisBuffer, Buffer, SxsStaticBuffers[5];
2343 PRTL_UNICODE_STRING_BUFFER* BufferHead, SxsStringBuffer;
2344 RTL_UNICODE_STRING_BUFFER SxsWin32ManifestPath, SxsNtManifestPath;
2345 RTL_UNICODE_STRING_BUFFER SxsWin32PolicyPath, SxsNtPolicyPath;
2346 RTL_UNICODE_STRING_BUFFER SxsWin32AssemblyDirectory;
2347 BASE_MSG_SXS_HANDLES MappedHandles, Handles, FileHandles;
2348 PVOID CapturedStrings[3];
2349 SXS_WIN32_NT_PATH_PAIR ExePathPair, ManifestPathPair, PolicyPathPair;
2350 SXS_OVERRIDE_MANIFEST OverrideMannifest;
2351 UNICODE_STRING FreeString, SxsNtExePath;
2352 PWCHAR SxsConglomeratedBuffer, StaticBuffer;
2353 ULONG ConglomeratedBufferSizeBytes, StaticBufferSize, i;
2354 #endif
2355 ULONG FusionFlags;
2356
2357 //
2358 // Variables used for path conversion (and partially Fusion/SxS)
2359 //
2360 PWCHAR FilePart, PathBuffer, FreeBuffer;
2361 BOOLEAN TranslationStatus;
2362 RTL_RELATIVE_NAME_U SxsWin32RelativePath;
2363 UNICODE_STRING PathBufferString, SxsWin32ExePath;
2364
2365 //
2366 // Variables used by Application Compatibility (and partially Fusion/SxS)
2367 //
2368 PVOID AppCompatSxsData, AppCompatData;
2369 ULONG AppCompatSxsDataSize, AppCompatDataSize;
2370 //
2371 // Variables used by VDM (Virtual Dos Machine) and WOW32 (16-bit Support)
2372 //
2373 ULONG BinarySubType, VdmBinaryType, VdmTask, VdmReserve;
2374 ULONG VdmUndoLevel;
2375 BOOLEAN UseVdmReserve;
2376 HANDLE VdmWaitObject;
2377 ANSI_STRING VdmAnsiEnv;
2378 UNICODE_STRING VdmString, VdmUnicodeEnv;
2379 BOOLEAN IsWowApp;
2380 PBASE_CHECK_VDM CheckVdmMsg;
2381
2382 /* Zero out the initial core variables and handles */
2383 QuerySection = FALSE;
2384 InJob = FALSE;
2385 SkipSaferAndAppCompat = TRUE; // HACK for making .bat/.cmd launch working again.
2386 ParameterFlags = 0;
2387 Flags = 0;
2388 DebugHandle = NULL;
2389 JobHandle = NULL;
2390 TokenHandle = NULL;
2391 FileHandle = NULL;
2392 SectionHandle = NULL;
2393 ProcessHandle = NULL;
2394 ThreadHandle = NULL;
2395 BaseAddress = (PVOID)1;
2396
2397 /* Zero out initial SxS and Application Compatibility state */
2398 AppCompatData = NULL;
2399 AppCompatDataSize = 0;
2400 AppCompatSxsData = NULL;
2401 AppCompatSxsDataSize = 0;
2402 CaptureBuffer = NULL;
2403 #if _SXS_SUPPORT_ENABLED_
2404 SxsConglomeratedBuffer = NULL;
2405 #endif
2406 FusionFlags = 0;
2407
2408 /* Zero out initial parsing variables -- others are initialized later */
2409 DebuggerCmdLine = NULL;
2410 PathBuffer = NULL;
2411 SearchPath = NULL;
2412 NullBuffer = 0;
2413 FreeBuffer = NULL;
2414 NameBuffer = NULL;
2415 CurrentDirectory = NULL;
2416 FilePart = NULL;
2417 DebuggerString.Buffer = NULL;
2418 HasQuotes = FALSE;
2419 QuotedCmdLine = NULL;
2420
2421 /* Zero out initial VDM state */
2422 VdmAnsiEnv.Buffer = NULL;
2423 VdmUnicodeEnv.Buffer = NULL;
2424 VdmString.Buffer = NULL;
2425 VdmTask = 0;
2426 VdmUndoLevel = 0;
2427 VdmBinaryType = 0;
2428 VdmReserve = 0;
2429 VdmWaitObject = NULL;
2430 UseVdmReserve = FALSE;
2431 IsWowApp = FALSE;
2432
2433 /* Set message structures */
2434 CreateProcessMsg = &CsrMsg[0].Data.CreateProcessRequest;
2435 CheckVdmMsg = &CsrMsg[1].Data.CheckVDMRequest;
2436
2437 /* Clear the more complex structures by zeroing out their entire memory */
2438 RtlZeroMemory(&Context, sizeof(Context));
2439 #if _SXS_SUPPORT_ENABLED_
2440 RtlZeroMemory(&FileHandles, sizeof(FileHandles));
2441 RtlZeroMemory(&MappedHandles, sizeof(MappedHandles));
2442 RtlZeroMemory(&Handles, sizeof(Handles));
2443 #endif
2444 RtlZeroMemory(&CreateProcessMsg->Sxs, sizeof(CreateProcessMsg->Sxs));
2445 RtlZeroMemory(&LocalProcessAttributes, sizeof(LocalProcessAttributes));
2446 RtlZeroMemory(&LocalThreadAttributes, sizeof(LocalThreadAttributes));
2447
2448 /* Zero out output arguments as well */
2449 RtlZeroMemory(lpProcessInformation, sizeof(*lpProcessInformation));
2450 if (hNewToken) *hNewToken = NULL;
2451
2452 /* Capture the special window flag */
2453 NoWindow = dwCreationFlags & CREATE_NO_WINDOW;
2454 dwCreationFlags &= ~CREATE_NO_WINDOW;
2455
2456 #if _SXS_SUPPORT_ENABLED_
2457 /* Setup the SxS static string arrays and buffers */
2458 SxsStaticBuffers[0] = &SxsWin32ManifestPath;
2459 SxsStaticBuffers[1] = &SxsWin32PolicyPath;
2460 SxsStaticBuffers[2] = &SxsWin32AssemblyDirectory;
2461 SxsStaticBuffers[3] = &SxsNtManifestPath;
2462 SxsStaticBuffers[4] = &SxsNtPolicyPath;
2463 ExePathPair.Win32 = &SxsWin32ExePath;
2464 ExePathPair.Nt = &SxsNtExePath;
2465 ManifestPathPair.Win32 = &SxsWin32ManifestPath.String;
2466 ManifestPathPair.Nt = &SxsNtManifestPath.String;
2467 PolicyPathPair.Win32 = &SxsWin32PolicyPath.String;
2468 PolicyPathPair.Nt = &SxsNtPolicyPath.String;
2469 #endif
2470
2471 DPRINT("CreateProcessInternalW: '%S' '%S' %lx\n", lpApplicationName, lpCommandLine, dwCreationFlags);
2472
2473 /* Finally, set our TEB and PEB */
2474 Teb = NtCurrentTeb();
2475 Peb = NtCurrentPeb();
2476
2477 /* This combination is illegal (see MSDN) */
2478 if ((dwCreationFlags & (DETACHED_PROCESS | CREATE_NEW_CONSOLE)) ==
2479 (DETACHED_PROCESS | CREATE_NEW_CONSOLE))
2480 {
2481 DPRINT1("Invalid flag combo used\n");
2482 SetLastError(ERROR_INVALID_PARAMETER);
2483 return FALSE;
2484 }
2485
2486 /* Convert the priority class */
2487 if (dwCreationFlags & IDLE_PRIORITY_CLASS)
2488 {
2489 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_IDLE;
2490 }
2491 else if (dwCreationFlags & BELOW_NORMAL_PRIORITY_CLASS)
2492 {
2493 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_BELOW_NORMAL;
2494 }
2495 else if (dwCreationFlags & NORMAL_PRIORITY_CLASS)
2496 {
2497 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_NORMAL;
2498 }
2499 else if (dwCreationFlags & ABOVE_NORMAL_PRIORITY_CLASS)
2500 {
2501 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_ABOVE_NORMAL;
2502 }
2503 else if (dwCreationFlags & HIGH_PRIORITY_CLASS)
2504 {
2505 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_HIGH;
2506 }
2507 else if (dwCreationFlags & REALTIME_PRIORITY_CLASS)
2508 {
2509 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_HIGH;
2510 PriorityClass.PriorityClass += (BasepIsRealtimeAllowed(FALSE) != NULL);
2511 }
2512 else
2513 {
2514 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_INVALID;
2515 }
2516
2517 /* Done with the priority masks, so get rid of them */
2518 PriorityClass.Foreground = FALSE;
2519 dwCreationFlags &= ~(NORMAL_PRIORITY_CLASS |
2520 IDLE_PRIORITY_CLASS |
2521 HIGH_PRIORITY_CLASS |
2522 REALTIME_PRIORITY_CLASS |
2523 BELOW_NORMAL_PRIORITY_CLASS |
2524 ABOVE_NORMAL_PRIORITY_CLASS);
2525
2526 /* You cannot request both a shared and a separate WoW VDM */
2527 if ((dwCreationFlags & CREATE_SEPARATE_WOW_VDM) &&
2528 (dwCreationFlags & CREATE_SHARED_WOW_VDM))
2529 {
2530 /* Fail such nonsensical attempts */
2531 DPRINT1("Invalid WOW flags\n");
2532 SetLastError(ERROR_INVALID_PARAMETER);
2533 return FALSE;
2534 }
2535 else if (!(dwCreationFlags & CREATE_SHARED_WOW_VDM) &&
2536 (BaseStaticServerData->DefaultSeparateVDM))
2537 {
2538 /* A shared WoW VDM was not requested but system enforces separation */
2539 dwCreationFlags |= CREATE_SEPARATE_WOW_VDM;
2540 }
2541
2542 /* If a shared WoW VDM is used, make sure the process isn't in a job */
2543 if (!(dwCreationFlags & CREATE_SEPARATE_WOW_VDM) &&
2544 (NtIsProcessInJob(NtCurrentProcess(), NULL)))
2545 {
2546 /* Remove the shared flag and add the separate flag */
2547 dwCreationFlags = (dwCreationFlags &~ CREATE_SHARED_WOW_VDM) |
2548 CREATE_SEPARATE_WOW_VDM;
2549 }
2550
2551 /* Convert the environment */
2552 if ((lpEnvironment) && !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT))
2553 {
2554 /* Scan the environment to calculate its Unicode size */
2555 AnsiEnv.Buffer = pcScan = (PCHAR)lpEnvironment;
2556 while ((*pcScan) || (*(pcScan + 1))) ++pcScan;
2557
2558 /* Create our ANSI String */
2559 AnsiEnv.Length = pcScan - (PCHAR)lpEnvironment + sizeof(ANSI_NULL);
2560 AnsiEnv.MaximumLength = AnsiEnv.Length + sizeof(ANSI_NULL);
2561
2562 /* Allocate memory for the Unicode Environment */
2563 UnicodeEnv.Buffer = NULL;
2564 RegionSize = AnsiEnv.MaximumLength * sizeof(WCHAR);
2565 Status = NtAllocateVirtualMemory(NtCurrentProcess(),
2566 (PVOID)&UnicodeEnv.Buffer,
2567 0,
2568 &RegionSize,
2569 MEM_COMMIT,
2570 PAGE_READWRITE);
2571 if (!NT_SUCCESS(Status))
2572 {
2573 /* Fail */
2574 BaseSetLastNTError(Status);
2575 return FALSE;
2576 }
2577
2578 /* Use the allocated size and convert */
2579 UnicodeEnv.MaximumLength = (USHORT)RegionSize;
2580 Status = RtlAnsiStringToUnicodeString(&UnicodeEnv, &AnsiEnv, FALSE);
2581 if (!NT_SUCCESS(Status))
2582 {
2583 /* Fail */
2584 NtFreeVirtualMemory(NtCurrentProcess(),
2585 (PVOID)&UnicodeEnv.Buffer,
2586 &RegionSize,
2587 MEM_RELEASE);
2588 BaseSetLastNTError(Status);
2589 return FALSE;
2590 }
2591
2592 /* Now set the Unicode environment as the environment string pointer */
2593 lpEnvironment = UnicodeEnv.Buffer;
2594 }
2595
2596 /* Make a copy of the caller's startup info since we'll modify it */
2597 StartupInfo = *lpStartupInfo;
2598
2599 /* Check if private data is being sent on the same channel as std handles */
2600 if ((StartupInfo.dwFlags & STARTF_USESTDHANDLES) &&
2601 (StartupInfo.dwFlags & (STARTF_USEHOTKEY | STARTF_SHELLPRIVATE)))
2602 {
2603 /* Cannot use the std handles since we have monitor/hotkey values */
2604 StartupInfo.dwFlags &= ~STARTF_USESTDHANDLES;
2605 }
2606
2607 /* If there's a debugger, or we have to launch cmd.exe, we go back here */
2608 AppNameRetry:
2609 /* New iteration -- free any existing name buffer */
2610 if (NameBuffer)
2611 {
2612 RtlFreeHeap(RtlGetProcessHeap(), 0, NameBuffer);
2613 NameBuffer = NULL;
2614 }
2615
2616 /* New iteration -- free any existing free buffer */
2617 if (FreeBuffer)
2618 {
2619 RtlFreeHeap(RtlGetProcessHeap(), 0, FreeBuffer);
2620 FreeBuffer = NULL;
2621 }
2622
2623 /* New iteration -- close any existing file handle */
2624 if (FileHandle)
2625 {
2626 NtClose(FileHandle);
2627 FileHandle = NULL;
2628 }
2629
2630 /* Set the initial parsing state. This code can loop -- don't move this! */
2631 ErrorCode = 0;
2632 SearchRetry = TRUE;
2633 QuotesNeeded = FALSE;
2634 CmdLineIsAppName = FALSE;
2635
2636 /* First check if we don't have an application name */
2637 if (!lpApplicationName)
2638 {
2639 /* This should be the first time we attempt creating one */
2640 ASSERT(NameBuffer == NULL);
2641
2642 /* Allocate a buffer to hold it */
2643 NameBuffer = RtlAllocateHeap(RtlGetProcessHeap(),
2644 0,
2645 MAX_PATH * sizeof(WCHAR));
2646 if (!NameBuffer)
2647 {
2648 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2649 Result = FALSE;
2650 goto Quickie;
2651 }
2652
2653 /* Initialize the application name and our parsing parameters */
2654 lpApplicationName = NullBuffer = ScanString = lpCommandLine;
2655
2656 /* Check for an initial quote*/
2657 if (*lpCommandLine == L'\"')
2658 {
2659 /* We found a quote, keep searching for another one */
2660 SearchRetry = FALSE;
2661 ScanString++;
2662 lpApplicationName = ScanString;
2663 while (*ScanString)
2664 {
2665 /* Have we found the terminating quote? */
2666 if (*ScanString == L'\"')
2667 {
2668 /* We're done, get out of here */
2669 NullBuffer = ScanString;
2670 HasQuotes = TRUE;
2671 break;
2672 }
2673
2674 /* Keep searching for the quote */
2675 ScanString++;
2676 NullBuffer = ScanString;
2677 }
2678 }
2679 else
2680 {
2681 StartScan:
2682 /* We simply make the application name be the command line*/
2683 lpApplicationName = lpCommandLine;
2684 while (*ScanString)
2685 {
2686 /* Check if it starts with a space or tab */
2687 if ((*ScanString == L' ') || (*ScanString == L'\t'))
2688 {
2689 /* Break out of the search loop */
2690 NullBuffer = ScanString;
2691 break;
2692 }
2693
2694 /* Keep searching for a space or tab */
2695 ScanString++;
2696 NullBuffer = ScanString;
2697 }
2698 }
2699
2700 /* We have found the end of the application name, terminate it */
2701 SaveChar = *NullBuffer;
2702 *NullBuffer = UNICODE_NULL;
2703
2704 /* New iteration -- free any existing saved path */
2705 if (SearchPath)
2706 {
2707 RtlFreeHeap(RtlGetProcessHeap(), 0, SearchPath);
2708 SearchPath = NULL;
2709 }
2710
2711 /* Now compute the final EXE path based on the name */
2712 SearchPath = BaseComputeProcessExePath((LPWSTR)lpApplicationName);
2713 DPRINT("Search Path: %S\n", SearchPath);
2714 if (!SearchPath)
2715 {
2716 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2717 Result = FALSE;
2718 goto Quickie;
2719 }
2720
2721 /* And search for the executable in the search path */
2722 Length = SearchPathW(SearchPath,
2723 lpApplicationName,
2724 L".exe",
2725 MAX_PATH,
2726 NameBuffer,
2727 NULL);
2728
2729 /* Did we find it? */
2730 if ((Length) && (Length < MAX_PATH))
2731 {
2732 /* Get file attributes */
2733 CurdirLength = GetFileAttributesW(NameBuffer);
2734 if ((CurdirLength != 0xFFFFFFFF) &&
2735 (CurdirLength & FILE_ATTRIBUTE_DIRECTORY))
2736 {
2737 /* This was a directory, fail later on */
2738 Length = 0;
2739 }
2740 else
2741 {
2742 /* It's a file! */
2743 Length++;
2744 }
2745 }
2746
2747 DPRINT("Length: %lu Buffer: %S\n", Length, NameBuffer);
2748
2749 /* Check if there was a failure in SearchPathW */
2750 if ((Length) && (Length < MAX_PATH))
2751 {
2752 /* Everything looks good, restore the name */
2753 *NullBuffer = SaveChar;
2754 lpApplicationName = NameBuffer;
2755 }
2756 else
2757 {
2758 /* Check if this was a relative path, which would explain it */
2759 PathType = RtlDetermineDosPathNameType_U(lpApplicationName);
2760 if (PathType != RtlPathTypeRelative)
2761 {
2762 /* This should fail, and give us a detailed LastError */
2763 FileHandle = CreateFileW(lpApplicationName,
2764 GENERIC_READ,
2765 FILE_SHARE_READ |
2766 FILE_SHARE_WRITE,
2767 NULL,
2768 OPEN_EXISTING,
2769 FILE_ATTRIBUTE_NORMAL,
2770 NULL);
2771 if (FileHandle != INVALID_HANDLE_VALUE)
2772 {
2773 /* It worked? Return a generic error */
2774 CloseHandle(FileHandle);
2775 FileHandle = NULL;
2776 BaseSetLastNTError(STATUS_OBJECT_NAME_NOT_FOUND);
2777 }
2778 }
2779 else
2780 {
2781 /* Path was absolute, which means it doesn't exist */
2782 BaseSetLastNTError(STATUS_OBJECT_NAME_NOT_FOUND);
2783 }
2784
2785 /* Did we already fail once? */
2786 if (ErrorCode)
2787 {
2788 /* Set the error code */
2789 SetLastError(ErrorCode);
2790 }
2791 else
2792 {
2793 /* Not yet, cache it */
2794 ErrorCode = GetLastError();
2795 }
2796
2797 /* Put back the command line */
2798 *NullBuffer = SaveChar;
2799 lpApplicationName = NameBuffer;
2800
2801 /* It's possible there's whitespace in the directory name */
2802 if (!(*ScanString) || !(SearchRetry))
2803 {
2804 /* Not the case, give up completely */
2805 Result = FALSE;
2806 goto Quickie;
2807 }
2808
2809 /* There are spaces, so keep trying the next possibility */
2810 ScanString++;
2811 NullBuffer = ScanString;
2812
2813 /* We will have to add a quote, since there is a space */
2814 QuotesNeeded = TRUE;
2815 HasQuotes = TRUE;
2816 goto StartScan;
2817 }
2818 }
2819 else if (!(lpCommandLine) || !(*lpCommandLine))
2820 {
2821 /* We don't have a command line, so just use the application name */
2822 CmdLineIsAppName = TRUE;
2823 lpCommandLine = (LPWSTR)lpApplicationName;
2824 }
2825
2826 /* Convert the application name to its NT path */
2827 TranslationStatus = RtlDosPathNameToRelativeNtPathName_U(lpApplicationName,
2828 &PathName,
2829 NULL,
2830 &SxsWin32RelativePath);
2831 if (!TranslationStatus)
2832 {
2833 /* Path must be invaild somehow, bail out */
2834 DPRINT1("Path translation for SxS failed\n");
2835 SetLastError(ERROR_PATH_NOT_FOUND);
2836 Result = FALSE;
2837 goto Quickie;
2838 }
2839
2840 /* Setup the buffer that needs to be freed at the end */
2841 ASSERT(FreeBuffer == NULL);
2842 FreeBuffer = PathName.Buffer;
2843
2844 /* Check what kind of path the application is, for SxS (Fusion) purposes */
2845 RtlInitUnicodeString(&SxsWin32ExePath, lpApplicationName);
2846 SxsPathType = RtlDetermineDosPathNameType_U(lpApplicationName);
2847 if ((SxsPathType != RtlPathTypeDriveAbsolute) &&
2848 (SxsPathType != RtlPathTypeLocalDevice) &&
2849 (SxsPathType != RtlPathTypeRootLocalDevice) &&
2850 (SxsPathType != RtlPathTypeUncAbsolute))
2851 {
2852 /* Relative-type path, get the full path */
2853 RtlInitEmptyUnicodeString(&PathBufferString, NULL, 0);
2854 Status = RtlGetFullPathName_UstrEx(&SxsWin32ExePath,
2855 NULL,
2856 &PathBufferString,
2857 NULL,
2858 NULL,
2859 NULL,
2860 &SxsPathType,
2861 NULL);
2862 if (!NT_SUCCESS(Status))
2863 {
2864 /* Fail the rest of the create */
2865 RtlReleaseRelativeName(&SxsWin32RelativePath);
2866 BaseSetLastNTError(Status);
2867 Result = FALSE;
2868 goto Quickie;
2869 }
2870
2871 /* Use this full path as the SxS path */
2872 SxsWin32ExePath = PathBufferString;
2873 PathBuffer = PathBufferString.Buffer;
2874 PathBufferString.Buffer = NULL;
2875 DPRINT("SxS Path: %S\n", PathBuffer);
2876 }
2877
2878 /* Also set the .EXE path based on the path name */
2879 #if _SXS_SUPPORT_ENABLED_
2880 SxsNtExePath = PathName;
2881 #endif
2882 if (SxsWin32RelativePath.RelativeName.Length)
2883 {
2884 /* If it's relative, capture the relative name */
2885 PathName = SxsWin32RelativePath.RelativeName;
2886 }
2887 else
2888 {
2889 /* Otherwise, it's absolute, make sure no relative dir is used */
2890 SxsWin32RelativePath.ContainingDirectory = NULL;
2891 }
2892
2893 /* Now use the path name, and the root path, to try opening the app */
2894 DPRINT("Path: %wZ. Dir: %p\n", &PathName, SxsWin32RelativePath.ContainingDirectory);
2895 InitializeObjectAttributes(&LocalObjectAttributes,
2896 &PathName,
2897 OBJ_CASE_INSENSITIVE,
2898 SxsWin32RelativePath.ContainingDirectory,
2899 NULL);
2900 Status = NtOpenFile(&FileHandle,
2901 SYNCHRONIZE |
2902 FILE_READ_ATTRIBUTES |
2903 FILE_READ_DATA |
2904 FILE_EXECUTE,
2905 &LocalObjectAttributes,
2906 &IoStatusBlock,
2907 FILE_SHARE_READ | FILE_SHARE_DELETE,
2908 FILE_SYNCHRONOUS_IO_NONALERT |
2909 FILE_NON_DIRECTORY_FILE);
2910 if (!NT_SUCCESS(Status))
2911 {
2912 /* Try to open the app just for execute purposes instead */
2913 Status = NtOpenFile(&FileHandle,
2914 SYNCHRONIZE | FILE_EXECUTE,
2915 &LocalObjectAttributes,
2916 &IoStatusBlock,
2917 FILE_SHARE_READ | FILE_SHARE_DELETE,
2918 FILE_SYNCHRONOUS_IO_NONALERT |
2919 FILE_NON_DIRECTORY_FILE);
2920 }
2921
2922 /* Cleanup in preparation for failure or success */
2923 RtlReleaseRelativeName(&SxsWin32RelativePath);
2924 if (!NT_SUCCESS(Status))
2925 {
2926 /* Failure path, try to understand why */
2927 DPRINT1("Open file failed: %lx\n", Status);
2928 if (RtlIsDosDeviceName_U(lpApplicationName))
2929 {
2930 /* If a device is being executed, return this special error code */
2931 SetLastError(ERROR_BAD_DEVICE);
2932 Result = FALSE;
2933 goto Quickie;
2934 }
2935 else
2936 {
2937 /* Otherwise return the converted NT error code */
2938 BaseSetLastNTError(Status);
2939 Result = FALSE;
2940 goto Quickie;
2941 }
2942 }
2943
2944 /* Did the caller specify a desktop? */
2945 if (!StartupInfo.lpDesktop)
2946 {
2947 /* Use the one from the current process */
2948 StartupInfo.lpDesktop = Peb->ProcessParameters->DesktopInfo.Buffer;
2949 }
2950
2951 /* Create a section for this file */
2952 Status = NtCreateSection(&SectionHandle,
2953 SECTION_ALL_ACCESS,
2954 NULL,
2955 NULL,
2956 PAGE_EXECUTE,
2957 SEC_IMAGE,
2958 FileHandle);
2959 DPRINT("Section status: %lx\n", Status);
2960 if (NT_SUCCESS(Status))
2961 {
2962 /* Are we running on Windows Embedded, Datacenter, Blade or Starter? */
2963 if (SharedUserData->SuiteMask & (VER_SUITE_EMBEDDEDNT |
2964 VER_SUITE_DATACENTER |
2965 VER_SUITE_PERSONAL |
2966 VER_SUITE_BLADE))
2967 {
2968 /* These SKUs do not allow running certain applications */
2969 Status = BasepCheckWebBladeHashes(FileHandle);
2970 if (Status == STATUS_ACCESS_DENIED)
2971 {
2972 /* And this is one of them! */
2973 DPRINT1("Invalid Blade hashes!\n");
2974 SetLastError(ERROR_ACCESS_DISABLED_WEBBLADE);
2975 Result = FALSE;
2976 goto Quickie;
2977 }
2978
2979 /* Did we get some other failure? */
2980 if (!NT_SUCCESS(Status))
2981 {
2982 /* If we couldn't check the hashes, assume nefariousness */
2983 DPRINT1("Tampered Blade hashes!\n");
2984 SetLastError(ERROR_ACCESS_DISABLED_WEBBLADE_TAMPER);
2985 Result = FALSE;
2986 goto Quickie;
2987 }
2988 }
2989
2990 /* Now do Winsafer, etc, checks */
2991 Status = BasepIsProcessAllowed((LPWSTR)lpApplicationName);
2992 if (!NT_SUCCESS(Status))
2993 {
2994 /* Fail if we're not allowed to launch the process */
2995 DPRINT1("Process not allowed to launch: %lx\n", Status);
2996 BaseSetLastNTError(Status);
2997 if (SectionHandle)
2998 {
2999 NtClose(SectionHandle);
3000 SectionHandle = NULL;
3001 }
3002 Result = FALSE;
3003 goto Quickie;
3004 }
3005
3006 /* Is a DOS VDM being forced, but we already have a WOW32 instance ready? */
3007 if ((dwCreationFlags & CREATE_FORCEDOS) &&
3008 (BaseStaticServerData->IsWowTaskReady))
3009 {
3010 /* This request can't be satisfied, instead, a separate VDM is needed */
3011 dwCreationFlags &= ~(CREATE_FORCEDOS | CREATE_SHARED_WOW_VDM);
3012 dwCreationFlags |= CREATE_SEPARATE_WOW_VDM;
3013
3014 /* Set a failure code, ask for VDM reservation */
3015 Status = STATUS_INVALID_IMAGE_WIN_16;
3016 UseVdmReserve = TRUE;
3017
3018 /* Close the current handle */
3019 NtClose(SectionHandle);
3020 SectionHandle = NULL;
3021
3022 /* Don't query the section later */
3023 QuerySection = FALSE;
3024 }
3025 }
3026
3027 /* Did we already do these checks? */
3028 if (!SkipSaferAndAppCompat)
3029 {
3030 /* Is everything OK so far, OR do we have an non-MZ, non-DOS app? */
3031 if ((NT_SUCCESS(Status)) ||
3032 ((Status == STATUS_INVALID_IMAGE_NOT_MZ) &&
3033 !(BaseIsDosApplication(&PathName, Status))))
3034 {
3035 /* Clear the machine type in case of failure */
3036 ImageMachine = 0;
3037
3038 /* Clean any app compat data that may have accumulated */
3039 BasepFreeAppCompatData(AppCompatData, AppCompatSxsData);
3040 AppCompatData = NULL;
3041 AppCompatSxsData = NULL;
3042
3043 /* Do we have a section? */
3044 if (SectionHandle)
3045 {
3046 /* Have we already queried it? */
3047 if (QuerySection)
3048 {
3049 /* Nothing to do */
3050 Status = STATUS_SUCCESS;
3051 }
3052 else
3053 {
3054 /* Get some information about the executable */
3055 Status = NtQuerySection(SectionHandle,
3056 SectionImageInformation,
3057 &ImageInformation,
3058 sizeof(ImageInformation),
3059 NULL);
3060 }
3061
3062 /* Do we have section information now? */
3063 if (NT_SUCCESS(Status))
3064 {
3065 /* Don't ask for it again, save the machine type */
3066 QuerySection = TRUE;
3067 ImageMachine = ImageInformation.Machine;
3068 }
3069 }
3070
3071 /* Is there a reason/Shim we shouldn't run this application? */
3072 Status = BasepCheckBadapp(FileHandle,
3073 FreeBuffer,
3074 lpEnvironment,
3075 ImageMachine,
3076 &AppCompatData,
3077 &AppCompatDataSize,
3078 &AppCompatSxsData,
3079 &AppCompatSxsDataSize,
3080 &FusionFlags);
3081 if (!NT_SUCCESS(Status))
3082 {
3083 /* This is usually the status we get back */
3084 DPRINT1("App compat launch failure: %lx\n", Status);
3085 if (Status == STATUS_ACCESS_DENIED)
3086 {
3087 /* Convert it to something more Win32-specific */
3088 SetLastError(ERROR_CANCELLED);
3089 }
3090 else
3091 {
3092 /* Some other error */
3093 BaseSetLastNTError(Status);
3094 }
3095
3096 /* Did we have a section? */
3097 if (SectionHandle)
3098 {
3099 /* Clean it up */
3100 NtClose(SectionHandle);
3101 SectionHandle = NULL;
3102 }
3103
3104 /* Fail the call */
3105 Result = FALSE;
3106 goto Quickie;
3107 }
3108 }
3109 }
3110
3111 //ASSERT((dwFusionFlags & ~SXS_APPCOMPACT_FLAG_APP_RUNNING_SAFEMODE) == 0);
3112
3113 /* Have we already done, and do we need to do, SRP (WinSafer) checks? */
3114 if (!(SkipSaferAndAppCompat) &&
3115 ~(dwCreationFlags & CREATE_PRESERVE_CODE_AUTHZ_LEVEL))
3116 {
3117 /* Assume yes */
3118 SaferNeeded = TRUE;
3119 switch (Status)
3120 {
3121 case STATUS_INVALID_IMAGE_NE_FORMAT:
3122 case STATUS_INVALID_IMAGE_PROTECT:
3123 case STATUS_INVALID_IMAGE_WIN_16:
3124 case STATUS_FILE_IS_OFFLINE:
3125 /* For all DOS, 16-bit, OS/2 images, we do*/
3126 break;
3127
3128 case STATUS_INVALID_IMAGE_NOT_MZ:
3129 /* For invalid files, we don't, unless it's a .BAT file */
3130 if (BaseIsDosApplication(&PathName, Status)) break;
3131
3132 default:
3133 /* Any other error codes we also don't */
3134 if (!NT_SUCCESS(Status))
3135 {
3136 SaferNeeded = FALSE;
3137 }
3138
3139 /* But for success, we do */
3140 break;
3141 }
3142
3143 /* Okay, so what did the checks above result in? */
3144 if (SaferNeeded)
3145 {
3146 /* We have to call into the WinSafer library and actually check */
3147 Status = BasepCheckWinSaferRestrictions(hUserToken,
3148 (LPWSTR)lpApplicationName,
3149 FileHandle,
3150 &InJob,
3151 &TokenHandle,
3152 &JobHandle);
3153 if (Status == 0xFFFFFFFF)
3154 {
3155 /* Back in 2003, they didn't have an NTSTATUS for this... */
3156 DPRINT1("WinSafer blocking process launch\n");
3157 SetLastError(ERROR_ACCESS_DISABLED_BY_POLICY);
3158 Result = FALSE;
3159 goto Quickie;
3160 }
3161
3162 /* Other status codes are not-Safer related, just convert them */
3163 if (!NT_SUCCESS(Status))
3164 {
3165 DPRINT1("Error checking WinSafer: %lx\n", Status);
3166 BaseSetLastNTError(Status);
3167 Result = FALSE;
3168 goto Quickie;
3169 }
3170 }
3171 }
3172
3173 /* The last step is to figure out why the section object was not created */
3174 switch (Status)
3175 {
3176 case STATUS_INVALID_IMAGE_WIN_16:
3177 {
3178 /* 16-bit binary. Should we use WOW or does the caller force VDM? */
3179 if (!(dwCreationFlags & CREATE_FORCEDOS))
3180 {
3181 /* Remember that we're launching WOW */
3182 IsWowApp = TRUE;
3183
3184 /* Create the VDM environment, it's valid for WOW too */
3185 Result = BaseCreateVDMEnvironment(lpEnvironment,
3186 &VdmAnsiEnv,
3187 &VdmUnicodeEnv);
3188 if (!Result)
3189 {
3190 DPRINT1("VDM environment for WOW app failed\n");
3191 goto Quickie;
3192 }
3193
3194 /* We're going to try this twice, so do a loop */
3195 while (TRUE)
3196 {
3197 /* Pick which kind of WOW mode we want to run in */
3198 VdmBinaryType = (dwCreationFlags &
3199 CREATE_SEPARATE_WOW_VDM) ?
3200 BINARY_TYPE_SEPARATE_WOW : BINARY_TYPE_WOW;
3201
3202 /* Get all the VDM settings and current status */
3203 Status = BaseCheckVDM(VdmBinaryType,
3204 lpApplicationName,
3205 lpCommandLine,
3206 lpCurrentDirectory,
3207 &VdmAnsiEnv,
3208 &CsrMsg[1],
3209 &VdmTask,
3210 dwCreationFlags,
3211 &StartupInfo,
3212 hUserToken);
3213
3214 /* If it worked, no need to try again */
3215 if (NT_SUCCESS(Status)) break;
3216
3217 /* Check if it's disallowed or if it's our second time */
3218 BaseSetLastNTError(Status);
3219 if ((Status == STATUS_VDM_DISALLOWED) ||
3220 (VdmBinaryType == BINARY_TYPE_SEPARATE_WOW) ||
3221 (GetLastError() == ERROR_ACCESS_DENIED))
3222 {
3223 /* Fail the call -- we won't try again */
3224 DPRINT1("VDM message failure for WOW: %lx\n", Status);
3225 Result = FALSE;
3226 goto Quickie;
3227 }
3228
3229 /* Try one more time, but with a separate WOW instance */
3230 dwCreationFlags |= CREATE_SEPARATE_WOW_VDM;
3231 }
3232
3233 /* Check which VDM state we're currently in */
3234 switch (CheckVdmMsg->VDMState & (VDM_NOT_LOADED |
3235 VDM_NOT_READY |
3236 VDM_READY))
3237 {
3238 case VDM_NOT_LOADED:
3239 /* VDM is not fully loaded, so not that much to undo */
3240 VdmUndoLevel = VDM_UNDO_PARTIAL;
3241
3242 /* Reset VDM reserve if needed */
3243 if (UseVdmReserve) VdmReserve = 1;
3244
3245 /* Get the required parameters and names for launch */
3246 Result = BaseGetVdmConfigInfo(lpCommandLine,
3247 VdmTask,
3248 VdmBinaryType,
3249 &VdmString,
3250 &VdmReserve);
3251 if (!Result)
3252 {
3253 DPRINT1("VDM Configuration failed for WOW\n");
3254 BaseSetLastNTError(Status);
3255 goto Quickie;
3256 }
3257
3258 /* Update the command-line with the VDM one instead */
3259 lpCommandLine = VdmString.Buffer;
3260 lpApplicationName = NULL;
3261
3262 /* We don't want a console, detachment, nor a window */
3263 dwCreationFlags |= CREATE_NO_WINDOW;
3264 dwCreationFlags &= ~(CREATE_NEW_CONSOLE | DETACHED_PROCESS);
3265
3266 /* Force feedback on */
3267 StartupInfo.dwFlags |= STARTF_FORCEONFEEDBACK;
3268 break;
3269
3270
3271 case VDM_READY:
3272 /* VDM is ready, so we have to undo everything */
3273 VdmUndoLevel = VDM_UNDO_REUSE;
3274
3275 /* Check if CSRSS wants us to wait on VDM */
3276 VdmWaitObject = CheckVdmMsg->WaitObjectForParent;
3277 break;
3278
3279 case VDM_NOT_READY:
3280 /* Something is wrong with VDM, we'll fail the call */
3281 DPRINT1("VDM is not ready for WOW\n");
3282 SetLastError(ERROR_NOT_READY);
3283 Result = FALSE;
3284 goto Quickie;
3285
3286 default:
3287 break;
3288 }
3289
3290 /* Since to get NULL, we allocate from 0x1, account for this */
3291 VdmReserve--;
3292
3293 /* This implies VDM is ready, so skip everything else */
3294 if (VdmWaitObject) goto VdmShortCircuit;
3295
3296 /* Don't inherit handles since we're doing VDM now */
3297 bInheritHandles = FALSE;
3298
3299 /* Had the user passed in environment? If so, destroy it */
3300 if ((lpEnvironment) &&
3301 !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT))
3302 {
3303 RtlDestroyEnvironment(lpEnvironment);
3304 }
3305
3306 /* We've already done all these checks, don't do them again */
3307 SkipSaferAndAppCompat = TRUE;
3308 goto AppNameRetry;
3309 }
3310
3311 // There is no break here on purpose, so FORCEDOS drops down!
3312 }
3313
3314 case STATUS_INVALID_IMAGE_PROTECT:
3315 case STATUS_INVALID_IMAGE_NOT_MZ:
3316 case STATUS_INVALID_IMAGE_NE_FORMAT:
3317 {
3318 /* We're launching an executable application */
3319 BinarySubType = BINARY_TYPE_EXE;
3320
3321 /* We can drop here from other "cases" above too, so check */
3322 if ((Status == STATUS_INVALID_IMAGE_PROTECT) ||
3323 (Status == STATUS_INVALID_IMAGE_NE_FORMAT) ||
3324 (BinarySubType = BaseIsDosApplication(&PathName, Status)))
3325 {
3326 /* We're launching a DOS application */
3327 VdmBinaryType = BINARY_TYPE_DOS;
3328
3329 /* Based on the caller environment, create a VDM one */
3330 Result = BaseCreateVDMEnvironment(lpEnvironment,
3331 &VdmAnsiEnv,
3332 &VdmUnicodeEnv);
3333 if (!Result)
3334 {
3335 DPRINT1("VDM environment for DOS failed\n");
3336 goto Quickie;
3337 }
3338
3339 /* Check the current state of the VDM subsystem */
3340 Status = BaseCheckVDM(VdmBinaryType | BinarySubType,
3341 lpApplicationName,
3342 lpCommandLine,
3343 lpCurrentDirectory,
3344 &VdmAnsiEnv,
3345 &CsrMsg[1],
3346 &VdmTask,
3347 dwCreationFlags,
3348 &StartupInfo,
3349 NULL);
3350 if (!NT_SUCCESS(Status))
3351 {
3352 /* Failed to inquire about VDM, fail the call */
3353 DPRINT1("VDM message failure for DOS: %lx\n", Status);
3354 BaseSetLastNTError(Status);
3355 Result = FALSE;
3356 goto Quickie;
3357 };
3358
3359 /* Handle possible VDM states */
3360 switch (CheckVdmMsg->VDMState & (VDM_NOT_LOADED |
3361 VDM_NOT_READY |
3362 VDM_READY))
3363 {
3364 case VDM_NOT_LOADED:
3365 /* If VDM is not loaded, we'll do a partial undo */
3366 VdmUndoLevel = VDM_UNDO_PARTIAL;
3367
3368 /* A VDM process can't also be detached, so fail */
3369 if (dwCreationFlags & DETACHED_PROCESS)
3370 {
3371 DPRINT1("Detached process but no VDM, not allowed\n");
3372 SetLastError(ERROR_ACCESS_DENIED);
3373 return FALSE;
3374 }
3375
3376 /* Get the required parameters and names for launch */
3377 Result = BaseGetVdmConfigInfo(lpCommandLine,
3378 VdmTask,
3379 VdmBinaryType,
3380 &VdmString,
3381 &VdmReserve);
3382 if (!Result)
3383 {
3384 DPRINT1("VDM Configuration failed for DOS\n");
3385 BaseSetLastNTError(Status);
3386 goto Quickie;
3387 }
3388
3389 /* Update the command-line to launch VDM instead */
3390 lpCommandLine = VdmString.Buffer;
3391 lpApplicationName = NULL;
3392 break;
3393
3394 case VDM_READY:
3395 /* VDM is ready, so we have to undo everything */
3396 VdmUndoLevel = VDM_UNDO_REUSE;
3397
3398 /* Check if CSRSS wants us to wait on VDM */
3399 VdmWaitObject = CheckVdmMsg->WaitObjectForParent;
3400 break;
3401
3402 case VDM_NOT_READY:
3403 /* Something is wrong with VDM, we'll fail the call */
3404 DPRINT1("VDM is not ready for DOS\n");
3405 SetLastError(ERROR_NOT_READY);
3406 Result = FALSE;
3407 goto Quickie;
3408
3409 default:
3410 break;
3411 }
3412
3413 /* Since to get NULL, we allocate from 0x1, account for this */
3414 VdmReserve--;
3415
3416 /* This implies VDM is ready, so skip everything else */
3417 if (VdmWaitObject) goto VdmShortCircuit;
3418
3419 /* Don't inherit handles since we're doing VDM now */
3420 bInheritHandles = FALSE;
3421
3422 /* Had the user passed in environment? If so, destroy it */
3423 if ((lpEnvironment) &&
3424 !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT))
3425 {
3426 RtlDestroyEnvironment(lpEnvironment);
3427 }
3428
3429 /* Use our VDM Unicode environment instead */
3430 lpEnvironment = VdmUnicodeEnv.Buffer;
3431 }
3432 else
3433 {
3434 /* It's a batch file, get the extension */
3435 ExtBuffer = &PathName.Buffer[PathName.Length / sizeof(WCHAR) - 4];
3436
3437 /* Make sure the extensions are correct */
3438 if ((PathName.Length < (4 * sizeof(WCHAR))) ||
3439 ((_wcsnicmp(ExtBuffer, L".bat", 4)) &&
3440 (_wcsnicmp(ExtBuffer, L".cmd", 4))))
3441 {
3442 DPRINT1("'%wZ': Invalid EXE, and not a batch or script file\n", &PathName);
3443 SetLastError(ERROR_BAD_EXE_FORMAT);
3444 Result = FALSE;
3445 goto Quickie;
3446 }
3447
3448 /* Check if we need to account for quotes around the path */
3449 CmdQuoteLength = CmdLineIsAppName || HasQuotes;
3450 if (!CmdLineIsAppName)
3451 {
3452 if (HasQuotes) CmdQuoteLength++;
3453 }
3454 else
3455 {
3456 CmdQuoteLength++;
3457 }
3458
3459 /* Calculate the length of the command line */
3460 CmdLineLength = wcslen(lpCommandLine);
3461 CmdLineLength += wcslen(CMD_STRING);
3462 CmdLineLength += CmdQuoteLength + sizeof(ANSI_NULL);
3463 CmdLineLength *= sizeof(WCHAR);
3464
3465 /* Allocate space for the new command line */
3466 AnsiCmdCommand = RtlAllocateHeap(RtlGetProcessHeap(),
3467 0,
3468 CmdLineLength);
3469 if (!AnsiCmdCommand)
3470 {
3471 BaseSetLastNTError(STATUS_NO_MEMORY);
3472 Result = FALSE;
3473 goto Quickie;
3474 }
3475
3476 /* Build it */
3477 wcscpy(AnsiCmdCommand, CMD_STRING);
3478 if ((CmdLineIsAppName) || (HasQuotes))
3479 {
3480 wcscat(AnsiCmdCommand, L"\"");
3481 }
3482 wcscat(AnsiCmdCommand, lpCommandLine);
3483 if ((CmdLineIsAppName) || (HasQuotes))
3484 {
3485 wcscat(AnsiCmdCommand, L"\"");
3486 }
3487
3488 /* Create it as a Unicode String */
3489 RtlInitUnicodeString(&DebuggerString, AnsiCmdCommand);
3490
3491 /* Set the command line to this */
3492 lpCommandLine = DebuggerString.Buffer;
3493 lpApplicationName = NULL;
3494 DPRINT1("Retrying with: %S\n", lpCommandLine);
3495 }
3496
3497 /* We've already done all these checks, don't do them again */
3498 SkipSaferAndAppCompat = TRUE;
3499 goto AppNameRetry;
3500 }
3501
3502 case STATUS_INVALID_IMAGE_WIN_64:
3503 {
3504 /* 64-bit binaries are not allowed to run on 32-bit ReactOS */
3505 DPRINT1("64-bit binary, failing\n");
3506 SetLastError(ERROR_EXE_MACHINE_TYPE_MISMATCH);
3507 Result = FALSE;
3508 goto Quickie;
3509 }
3510
3511 case STATUS_FILE_IS_OFFLINE:
3512 {
3513 /* Set the correct last error for this */
3514 DPRINT1("File is offline, failing\n");
3515 SetLastError(ERROR_FILE_OFFLINE);
3516 break;
3517 }
3518
3519 default:
3520 {
3521 /* Any other error, convert it to a generic Win32 error */
3522 if (!NT_SUCCESS(Status))
3523 {
3524 DPRINT1("Failed to create section: %lx\n", Status);
3525 SetLastError(ERROR_BAD_EXE_FORMAT);
3526 Result = FALSE;
3527 goto Quickie;
3528 }
3529
3530 /* Otherwise, this must be success */
3531 ASSERT(Status == STATUS_SUCCESS);
3532 break;
3533 }
3534 }
3535
3536 /* Is this not a WOW application, but a WOW32 VDM was requested for it? */
3537 if (!(IsWowApp) && (dwCreationFlags & CREATE_SEPARATE_WOW_VDM))
3538 {
3539 /* Ignore the nonsensical request */
3540 dwCreationFlags &= ~CREATE_SEPARATE_WOW_VDM;
3541 }
3542
3543 /* Did we already check information for the section? */
3544 if (!QuerySection)
3545 {
3546 /* Get some information about the executable */
3547 Status = NtQuerySection(SectionHandle,
3548 SectionImageInformation,
3549 &ImageInformation,
3550 sizeof(ImageInformation),
3551 NULL);
3552 if (!NT_SUCCESS(Status))
3553 {
3554 /* We failed, bail out */
3555 DPRINT1("Section query failed\n");
3556 BaseSetLastNTError(Status);
3557 Result = FALSE;
3558 goto Quickie;
3559 }
3560
3561 /* Don't check this later */
3562 QuerySection = TRUE;
3563 }
3564
3565 /* Check if this was linked as a DLL */
3566 if (ImageInformation.ImageCharacteristics & IMAGE_FILE_DLL)
3567 {
3568 /* These aren't valid images to try to execute! */
3569 DPRINT1("Trying to launch a DLL, failing\n");
3570 SetLastError(ERROR_BAD_EXE_FORMAT);
3571 Result = FALSE;
3572 goto Quickie;
3573 }
3574
3575 /* Don't let callers pass in this flag -- we'll only get it from IFRO */
3576 Flags &= ~PROCESS_CREATE_FLAGS_LARGE_PAGES;
3577
3578 /* Clear the IFEO-missing flag, before we know for sure... */
3579 ParameterFlags &= ~2;
3580
3581 /* If the process is being debugged, only read IFEO if the PEB says so */
3582 if (!(dwCreationFlags & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS)) ||
3583 (NtCurrentPeb()->ReadImageFileExecOptions))
3584 {
3585 /* Let's do this! Attempt to open IFEO */
3586 Status1 = LdrOpenImageFileOptionsKey(&PathName, 0, &KeyHandle);
3587 if (!NT_SUCCESS(Status1))
3588 {
3589 /* We failed, set the flag so we store this in the parameters */
3590 if (Status1 == STATUS_OBJECT_NAME_NOT_FOUND) ParameterFlags |= 2;
3591 }
3592 else
3593 {
3594 /* Was this our first time going through this path? */
3595 if (!DebuggerCmdLine)
3596 {
3597 /* Allocate a buffer for the debugger path */
3598 DebuggerCmdLine = RtlAllocateHeap(RtlGetProcessHeap(),
3599 0,
3600 MAX_PATH * sizeof(WCHAR));
3601 if (!DebuggerCmdLine)
3602 {
3603 /* Close IFEO on failure */
3604 Status1 = NtClose(KeyHandle);
3605 ASSERT(NT_SUCCESS(Status1));
3606
3607 /* Fail the call */
3608 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3609 Result = FALSE;
3610 goto Quickie;
3611 }
3612 }
3613
3614 /* Now query for the debugger */
3615 Status1 = LdrQueryImageFileKeyOption(KeyHandle,
3616 L"Debugger",
3617 REG_SZ,
3618 DebuggerCmdLine,
3619 MAX_PATH * sizeof(WCHAR),
3620 &ResultSize);
3621 if (!(NT_SUCCESS(Status1)) ||
3622 (ResultSize < sizeof(WCHAR)) ||
3623 (DebuggerCmdLine[0] == UNICODE_NULL))
3624 {
3625 /* If it's not there, or too small, or invalid, ignore it */
3626 RtlFreeHeap(RtlGetProcessHeap(), 0, DebuggerCmdLine);
3627 DebuggerCmdLine = NULL;
3628 }
3629
3630 /* Also query if we should map with large pages */
3631 Status1 = LdrQueryImageFileKeyOption(KeyHandle,
3632 L"UseLargePages",
3633 REG_DWORD,
3634 &UseLargePages,
3635 sizeof(UseLargePages),
3636 NULL);
3637 if ((NT_SUCCESS(Status1)) && (UseLargePages))
3638 {
3639 /* Do it! This is the only way this flag can be set */
3640 Flags |= PROCESS_CREATE_FLAGS_LARGE_PAGES;
3641 }
3642
3643 /* We're done with IFEO, can close it now */
3644 Status1 = NtClose(KeyHandle);
3645 ASSERT(NT_SUCCESS(Status1));
3646 }
3647 }
3648
3649 /* Make sure the image was compiled for this processor */
3650 if ((ImageInformation.Machine < SharedUserData->ImageNumberLow) ||
3651 (ImageInformation.Machine > SharedUserData->ImageNumberHigh))
3652 {
3653 /* It was not -- raise a hard error */
3654 ErrorResponse = ResponseOk;
3655 ErrorParameters[0] = (ULONG_PTR)&PathName;
3656 NtRaiseHardError(STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE,
3657 1,
3658 1,
3659 ErrorParameters,
3660 OptionOk,
3661 &ErrorResponse);
3662 if (Peb->ImageSubsystemMajorVersion <= 3)
3663 {
3664 /* If it's really old, return this error */
3665 SetLastError(ERROR_BAD_EXE_FORMAT);
3666 }
3667 else
3668 {
3669 /* Otherwise, return a more modern error */
3670 SetLastError(ERROR_EXE_MACHINE_TYPE_MISMATCH);
3671 }
3672
3673 /* Go to the failure path */
3674 DPRINT1("Invalid image architecture: %lx\n", ImageInformation.Machine);
3675 Result = FALSE;
3676 goto Quickie;
3677 }
3678
3679 /* Check if this isn't a Windows image */
3680 if ((ImageInformation.SubSystemType != IMAGE_SUBSYSTEM_WINDOWS_GUI) &&
3681 (ImageInformation.SubSystemType != IMAGE_SUBSYSTEM_WINDOWS_CUI))
3682 {
3683 /* Get rid of section-related information since we'll retry */
3684 NtClose(SectionHandle);
3685 SectionHandle = NULL;
3686 QuerySection = FALSE;
3687
3688 /* The only other non-Windows image type we support here is POSIX */
3689 if (ImageInformation.SubSystemType != IMAGE_SUBSYSTEM_POSIX_CUI)
3690 {
3691 /* Bail out if it's something else */
3692 SetLastError(ERROR_CHILD_NOT_COMPLETE);
3693 Result = FALSE;
3694 goto Quickie;
3695 }
3696
3697 /* Now build the command-line to have posix launch this image */
3698 Result = BuildSubSysCommandLine(L"POSIX /P ",
3699 lpApplicationName,
3700 lpCommandLine,
3701 &DebuggerString);
3702 if (!Result)
3703 {
3704 /* Bail out if that failed */
3705 DPRINT1("Subsystem command line failed\n");
3706 goto Quickie;
3707 }
3708
3709 /* And re-try launching the process, with the new command-line now */
3710 lpCommandLine = DebuggerString.Buffer;
3711 lpApplicationName = NULL;
3712
3713 /* We've already done all these checks, don't do them again */
3714 SkipSaferAndAppCompat = TRUE;
3715 DPRINT1("Retrying with: %S\n", lpCommandLine);
3716 goto AppNameRetry;
3717 }
3718
3719 /* Was this image built for a version of Windows whose images we can run? */
3720 Result = BasepIsImageVersionOk(ImageInformation.SubSystemMajorVersion,
3721 ImageInformation.SubSystemMinorVersion);
3722 if (!Result)
3723 {
3724 /* It was not, bail out */
3725 DPRINT1("Invalid subsystem version: %hu.%hu\n",
3726 ImageInformation.SubSystemMajorVersion,
3727 ImageInformation.SubSystemMinorVersion);
3728 SetLastError(ERROR_BAD_EXE_FORMAT);
3729 goto Quickie;
3730 }
3731
3732 /* Check if there is a debugger associated with the application */
3733 if (DebuggerCmdLine)
3734 {
3735 /* Get the length of the command line */
3736 n = wcslen(lpCommandLine);
3737 if (!n)
3738 {
3739 /* There's no command line, use the application name instead */
3740 lpCommandLine = (LPWSTR)lpApplicationName;
3741 n = wcslen(lpCommandLine);
3742 }
3743
3744 /* Protect against overflow */
3745 if (n > UNICODE_STRING_MAX_CHARS)
3746 {
3747 BaseSetLastNTError(STATUS_NAME_TOO_LONG);
3748 Result = FALSE;
3749 goto Quickie;
3750 }
3751
3752 /* Now add the length of the debugger command-line */
3753 n += wcslen(DebuggerCmdLine);
3754
3755 /* Again make sure we don't overflow */
3756 if (n > UNICODE_STRING_MAX_CHARS)
3757 {
3758 BaseSetLastNTError(STATUS_NAME_TOO_LONG);
3759 Result = FALSE;
3760 goto Quickie;
3761 }
3762
3763 /* Account for the quotes and space between the two */
3764 n += sizeof("\" \"") - sizeof(ANSI_NULL);
3765
3766 /* Convert to bytes, and make sure we don't overflow */
3767 n *= sizeof(WCHAR);
3768 if (n > UNICODE_STRING_MAX_BYTES)
3769 {
3770 BaseSetLastNTError(STATUS_NAME_TOO_LONG);
3771 Result = FALSE;
3772 goto Quickie;
3773 }
3774
3775 /* Allocate space for the string */
3776 DebuggerString.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, n);
3777 if (!DebuggerString.Buffer)
3778 {
3779 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3780 Result = FALSE;
3781 goto Quickie;
3782 }
3783
3784 /* Set the length */
3785 RtlInitEmptyUnicodeString(&DebuggerString,
3786 DebuggerString.Buffer,
3787 (USHORT)n);
3788
3789 /* Now perform the command line creation */
3790 ImageDbgStatus = RtlAppendUnicodeToString(&DebuggerString,
3791 DebuggerCmdLine);
3792 ASSERT(NT_SUCCESS(ImageDbgStatus));
3793 ImageDbgStatus = RtlAppendUnicodeToString(&DebuggerString, L" ");
3794 ASSERT(NT_SUCCESS(ImageDbgStatus));
3795 ImageDbgStatus = RtlAppendUnicodeToString(&DebuggerString, lpCommandLine);
3796 ASSERT(NT_SUCCESS(ImageDbgStatus));
3797
3798 /* Make sure it all looks nice */
3799 DbgPrint("BASE: Calling debugger with '%wZ'\n", &DebuggerString);
3800
3801 /* Update the command line and application name */
3802 lpCommandLine = DebuggerString.Buffer;
3803 lpApplicationName = NULL;
3804
3805 /* Close all temporary state */
3806 NtClose(SectionHandle);
3807 SectionHandle = NULL;
3808 QuerySection = FALSE;
3809
3810 /* Free all temporary memory */
3811 RtlFreeHeap(RtlGetProcessHeap(), 0, NameBuffer);
3812 NameBuffer = NULL;
3813 RtlFreeHeap(RtlGetProcessHeap(), 0, FreeBuffer);
3814 FreeBuffer = NULL;
3815 RtlFreeHeap(RtlGetProcessHeap(), 0, DebuggerCmdLine);
3816 DebuggerCmdLine = NULL;
3817 DPRINT1("Retrying with: %S\n", lpCommandLine);
3818 goto AppNameRetry;
3819 }
3820
3821 /* Initialize the process object attributes */
3822 ObjectAttributes = BaseFormatObjectAttributes(&LocalObjectAttributes,
3823 lpProcessAttributes,
3824 NULL);
3825 if ((hUserToken) && (lpProcessAttributes))
3826 {
3827 /* Auggment them with information from the user */
3828
3829 LocalProcessAttributes = *lpProcessAttributes;
3830 LocalProcessAttributes.lpSecurityDescriptor = NULL;
3831 ObjectAttributes = BaseFormatObjectAttributes(&LocalObjectAttributes,
3832 &LocalProcessAttributes,
3833 NULL);
3834 }
3835
3836 /* Check if we're going to be debugged */
3837 if (dwCreationFlags & DEBUG_PROCESS)
3838 {
3839 /* Set process flag */
3840 Flags |= PROCESS_CREATE_FLAGS_BREAKAWAY;
3841 }
3842
3843 /* Check if we're going to be debugged */
3844 if (dwCreationFlags & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS))
3845 {
3846 /* Connect to DbgUi */
3847 Status = DbgUiConnectToDbg();
3848 if (!NT_SUCCESS(Status))
3849 {
3850 DPRINT1("Failed to connect to DbgUI!\n");
3851 BaseSetLastNTError(Status);
3852 Result = FALSE;
3853 goto Quickie;
3854 }
3855
3856 /* Get the debug object */
3857 DebugHandle = DbgUiGetThreadDebugObject();
3858
3859 /* Check if only this process will be debugged */
3860 if (dwCreationFlags & DEBUG_ONLY_THIS_PROCESS)
3861 {
3862 /* Set process flag */
3863 Flags |= PROCESS_CREATE_FLAGS_NO_DEBUG_INHERIT;
3864 }
3865 }
3866
3867 /* Set inherit flag */
3868 if (bInheritHandles) Flags |= PROCESS_CREATE_FLAGS_INHERIT_HANDLES;
3869
3870 /* Check if the process should be created with large pages */
3871 HavePrivilege = FALSE;
3872 PrivilegeState = NULL;
3873 if (Flags & PROCESS_CREATE_FLAGS_LARGE_PAGES)
3874 {
3875 /* Acquire the required privilege so that the kernel won't fail the call */
3876 PrivilegeValue = SE_LOCK_MEMORY_PRIVILEGE;
3877 Status = RtlAcquirePrivilege(&PrivilegeValue, 1, 0, &PrivilegeState);
3878 if (NT_SUCCESS(Status))
3879 {
3880 /* Remember to release it later */
3881 HavePrivilege = TRUE;
3882 }
3883 }
3884
3885 /* Save the current TIB value since kernel overwrites it to store PEB */
3886 TibValue = Teb->NtTib.ArbitraryUserPointer;
3887
3888 /* Tell the kernel to create the process */
3889 Status = NtCreateProcessEx(&ProcessHandle,
3890 PROCESS_ALL_ACCESS,
3891 ObjectAttributes,
3892 NtCurrentProcess(),
3893 Flags,
3894 SectionHandle,
3895 DebugHandle,
3896 NULL,
3897 InJob);
3898
3899 /* Load the PEB address from the hacky location where the kernel stores it */
3900 RemotePeb = Teb->NtTib.ArbitraryUserPointer;
3901
3902 /* And restore the old TIB value */
3903 Teb->NtTib.ArbitraryUserPointer = TibValue;
3904
3905 /* Release the large page privilege if we had acquired it */
3906 if (HavePrivilege) RtlReleasePrivilege(PrivilegeState);
3907
3908 /* And now check if the kernel failed to create the process */
3909 if (!NT_SUCCESS(Status))
3910 {
3911 /* Go to failure path */
3912 DPRINT1("Failed to create process: %lx\n", Status);
3913 BaseSetLastNTError(Status);
3914 Result = FALSE;
3915 goto Quickie;
3916 }
3917
3918 /* Check if there is a priority class to set */
3919 if (PriorityClass.PriorityClass)
3920 {
3921 /* Reset current privilege state */
3922 RealTimePrivilegeState = NULL;
3923
3924 /* Is realtime priority being requested? */
3925 if (PriorityClass.PriorityClass == PROCESS_PRIORITY_CLASS_REALTIME)
3926 {
3927 /* Check if the caller has real-time access, and enable it if so */
3928 RealTimePrivilegeState = BasepIsRealtimeAllowed(TRUE);
3929 }
3930
3931 /* Set the new priority class and release the privilege */
3932 Status = NtSetInformationProcess(ProcessHandle,
3933 ProcessPriorityClass,
3934 &PriorityClass,
3935 sizeof(PROCESS_PRIORITY_CLASS));
3936 if (RealTimePrivilegeState) RtlReleasePrivilege(RealTimePrivilegeState);
3937
3938 /* Check if we failed to set the priority class */
3939 if (!NT_SUCCESS(Status))
3940 {
3941 /* Bail out on failure */
3942 DPRINT1("Failed to set priority class: %lx\n", Status);
3943 BaseSetLastNTError(Status);
3944 Result = FALSE;
3945 goto Quickie;
3946 }
3947 }
3948
3949 /* Check if the caller wants the default error mode */
3950 if (dwCreationFlags & CREATE_DEFAULT_ERROR_MODE)
3951 {
3952 /* Set Error Mode to only fail on critical errors */
3953 HardErrorMode = SEM_FAILCRITICALERRORS;
3954 NtSetInformationProcess(ProcessHandle,
3955 ProcessDefaultHardErrorMode,
3956 &HardErrorMode,
3957 sizeof(ULONG));
3958 }
3959
3960 /* Check if this was a VDM binary */
3961 if (VdmBinaryType)
3962 {
3963 /* Update VDM by telling it the process has now been created */
3964 VdmWaitObject = ProcessHandle;
3965 Result = BaseUpdateVDMEntry(VdmEntryUpdateProcess,
3966 &VdmWaitObject,
3967 VdmTask,
3968 VdmBinaryType);
3969
3970 if (!Result)
3971 {
3972 /* Bail out on failure */
3973 DPRINT1("Failed to update VDM with wait object\n");
3974 VdmWaitObject = NULL;
3975 goto Quickie;
3976 }
3977
3978 /* At this point, a failure means VDM has to undo all the state */
3979 VdmUndoLevel |= VDM_UNDO_FULL;
3980 }
3981
3982 /* Check if VDM needed reserved low-memory */
3983 if (VdmReserve)
3984 {
3985 /* Reserve the requested allocation */
3986 Status = NtAllocateVirtualMemory(ProcessHandle,
3987 &BaseAddress,
3988 0,
3989 &VdmReserve,
3990 MEM_RESERVE,
3991 PAGE_EXECUTE_READWRITE);
3992 if (!NT_SUCCESS(Status))
3993 {
3994 /* Bail out on failure */
3995 DPRINT1("Failed to reserved memory for VDM: %lx\n", Status);
3996 BaseSetLastNTError(Status);
3997 Result = FALSE;
3998 goto Quickie;
3999 }
4000 }
4001
4002 /* Check if we've already queried information on the section */
4003 if (!QuerySection)
4004 {
4005 /* We haven't, so get some information about the executable */
4006 Status = NtQuerySection(SectionHandle,
4007 SectionImageInformation,
4008 &ImageInformation,
4009 sizeof(ImageInformation),
4010 NULL);
4011 if (!NT_SUCCESS(Status))
4012 {
4013 /* Bail out on failure */
4014 DPRINT1("Failed to query section: %lx\n", Status);
4015 BaseSetLastNTError(Status);
4016 Result = FALSE;
4017 goto Quickie;
4018 }
4019
4020 /* If we encounter a restart, don't re-query this information again */
4021 QuerySection = TRUE;
4022 }
4023
4024 /* Do we need to apply SxS to this image? */
4025 if (!(ImageInformation.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_NO_ISOLATION))
4026 {
4027 /* Too bad, we don't support this yet */
4028 DPRINT1("Image should receive SxS Fusion Isolation\n");
4029 }
4030
4031 /* There's some SxS flag that we need to set if fusion flags have 1 set */
4032 if (FusionFlags & 1) CreateProcessMsg->Sxs.Flags |= 0x10;
4033
4034 /* Check if we have a current directory */
4035 if (lpCurrentDirectory)
4036 {
4037 /* Allocate a buffer so we can keep a Unicode copy */
4038 DPRINT1("Current directory: %S\n", lpCurrentDirectory);
4039 CurrentDirectory = RtlAllocateHeap(RtlGetProcessHeap(),
4040 0,
4041 (MAX_PATH * sizeof(WCHAR)) +
4042 sizeof(UNICODE_NULL));
4043 if (!CurrentDirectory)
4044 {
4045 /* Bail out if this failed */
4046 BaseSetLastNTError(STATUS_NO_MEMORY);
4047 Result = FALSE;
4048 goto Quickie;
4049 }
4050
4051 /* Get the length in Unicode */
4052 Length = GetFullPathNameW(lpCurrentDirectory,
4053 MAX_PATH,
4054 CurrentDirectory,
4055 &FilePart);
4056 if (Length > MAX_PATH)
4057 {
4058 /* The directory is too long, so bail out */
4059 SetLastError(ERROR_DIRECTORY);
4060 Result = FALSE;
4061 goto Quickie;
4062 }
4063
4064 /* Make sure the directory is actually valid */
4065 CurdirLength = GetFileAttributesW(CurrentDirectory);
4066 if ((CurdirLength == 0xffffffff) ||
4067 !(CurdirLength & FILE_ATTRIBUTE_DIRECTORY))
4068 {
4069 /* It isn't, so bail out */
4070 DPRINT1("Current directory is invalid\n");
4071 SetLastError(ERROR_DIRECTORY);
4072 Result = FALSE;
4073 goto Quickie;
4074 }
4075 }
4076
4077 /* Insert quotes if needed */
4078 if ((QuotesNeeded) || (CmdLineIsAppName))
4079 {
4080 /* Allocate our buffer, plus enough space for quotes and a NULL */
4081 QuotedCmdLine = RtlAllocateHeap(RtlGetProcessHeap(),
4082 0,
4083 (wcslen(lpCommandLine) * sizeof(WCHAR)) +
4084 (2 * sizeof(L'\"') + sizeof(UNICODE_NULL)));
4085 if (QuotedCmdLine)
4086 {
4087 /* Copy the first quote */
4088 wcscpy(QuotedCmdLine, L"\"");
4089
4090 /* Save the current null-character */
4091 if (QuotesNeeded)
4092 {
4093 SaveChar = *NullBuffer;
4094 *NullBuffer = UNICODE_NULL;
4095 }
4096
4097 /* Copy the command line and the final quote */
4098 wcscat(QuotedCmdLine, lpCommandLine);
4099 wcscat(QuotedCmdLine, L"\"");
4100
4101 /* Copy the null-char back */
4102 if (QuotesNeeded)
4103 {
4104 *NullBuffer = SaveChar;
4105 wcscat(QuotedCmdLine, NullBuffer);
4106 }
4107 }
4108 else
4109 {
4110 /* We can't put quotes around the thing, so try it anyway */
4111 if (QuotesNeeded) QuotesNeeded = FALSE;
4112 if (CmdLineIsAppName) CmdLineIsAppName = FALSE;
4113 }
4114 }
4115
4116 /* Use isolation if needed */
4117 if (CreateProcessMsg->Sxs.Flags & 1) ParameterFlags |= 1;
4118
4119 /* Set the new command-line if needed */
4120 if ((QuotesNeeded) || (CmdLineIsAppName)) lpCommandLine = QuotedCmdLine;
4121
4122 /* Call the helper function in charge of RTL_USER_PROCESS_PARAMETERS */
4123 Result = BasePushProcessParameters(ParameterFlags,
4124 ProcessHandle,
4125 RemotePeb,
4126 lpApplicationName,
4127 CurrentDirectory,
4128 lpCommandLine,
4129 lpEnvironment,
4130 &StartupInfo,
4131 dwCreationFlags | NoWindow,
4132 bInheritHandles,
4133 IsWowApp ? IMAGE_SUBSYSTEM_WINDOWS_GUI: 0,
4134 AppCompatData,
4135 AppCompatDataSize);
4136 if (!Result)
4137 {
4138 /* The remote process would have an undefined state, so fail the call */
4139 DPRINT1("BasePushProcessParameters failed\n");
4140 goto Quickie;
4141 }
4142
4143 /* Free the VDM command line string as it's no longer needed */
4144 RtlFreeUnicodeString(&VdmString);
4145 VdmString.Buffer = NULL;
4146
4147 /* Non-VDM console applications usually inherit handles unless specified */
4148 if (!(VdmBinaryType) &&
4149 !(bInheritHandles) &&
4150 !(StartupInfo.dwFlags & STARTF_USESTDHANDLES) &&
4151 !(dwCreationFlags & (CREATE_NO_WINDOW |
4152 CREATE_NEW_CONSOLE |
4153 DETACHED_PROCESS)) &&
4154 (ImageInformation.SubSystemType == IMAGE_SUBSYSTEM_WINDOWS_CUI))
4155 {
4156 /* Get the remote parameters */
4157 Status = NtReadVirtualMemory(ProcessHandle,
4158 &RemotePeb->ProcessParameters,
4159 &ProcessParameters,
4160 sizeof(PRTL_USER_PROCESS_PARAMETERS),
4161 NULL);
4162 if (NT_SUCCESS(Status))
4163 {
4164 /* Duplicate standard input unless it's a console handle */
4165 if (!IsConsoleHandle(Peb->ProcessParameters->StandardInput))
4166 {
4167 StuffStdHandle(ProcessHandle,
4168 Peb->ProcessParameters->StandardInput,
4169 &ProcessParameters->StandardInput);
4170 }
4171
4172 /* Duplicate standard output unless it's a console handle */
4173 if (!IsConsoleHandle(Peb->ProcessParameters->StandardOutput))
4174 {
4175 StuffStdHandle(ProcessHandle,
4176 Peb->ProcessParameters->StandardOutput,
4177 &ProcessParameters->StandardOutput);
4178 }
4179
4180 /* Duplicate standard error unless it's a console handle */
4181 if (!IsConsoleHandle(Peb->ProcessParameters->StandardError))
4182 {
4183 StuffStdHandle(ProcessHandle,
4184 Peb->ProcessParameters->StandardError,
4185 &ProcessParameters->StandardError);
4186 }
4187 }
4188 }
4189
4190 /* Create the Thread's Stack */
4191 StackSize = max(256 * 1024, ImageInformation.MaximumStackSize);
4192 Status = BaseCreateStack(ProcessHandle,
4193 ImageInformation.CommittedStackSize,
4194 StackSize,
4195 &InitialTeb);
4196 if (!NT_SUCCESS(Status))
4197 {
4198 DPRINT1("Creating the thread stack failed: %lx\n", Status);
4199 BaseSetLastNTError(Status);
4200 Result = FALSE;
4201 goto Quickie;
4202 }
4203
4204 /* Create the Thread's Context */
4205 BaseInitializeContext(&Context,
4206 Peb,
4207 ImageInformation.TransferAddress,
4208 InitialTeb.StackBase,
4209 0);
4210
4211 /* Convert the thread attributes */
4212 ObjectAttributes = BaseFormatObjectAttributes(&LocalObjectAttributes,
4213 lpThreadAttributes,
4214 NULL);
4215 if ((hUserToken) && (lpThreadAttributes))
4216 {
4217 /* If the caller specified a user token, zero the security descriptor */
4218 LocalThreadAttributes = *lpThreadAttributes;
4219 LocalThreadAttributes.lpSecurityDescriptor = NULL;
4220 ObjectAttributes = BaseFormatObjectAttributes(&LocalObjectAttributes,
4221 &LocalThreadAttributes,
4222 NULL);
4223 }
4224
4225 /* Create the Kernel Thread Object */
4226 Status = NtCreateThread(&ThreadHandle,
4227 THREAD_ALL_ACCESS,
4228 ObjectAttributes,
4229 ProcessHandle,
4230 &ClientId,
4231 &Context,
4232 &InitialTeb,
4233 TRUE);
4234 if (!NT_SUCCESS(Status))
4235 {
4236 /* A process is not allowed to exist without a main thread, so fail */
4237 DPRINT1("Creating the main thread failed: %lx\n", Status);
4238 BaseSetLastNTError(Status);
4239 Result = FALSE;
4240 goto Quickie;
4241 }
4242
4243 /* Begin filling out the CSRSS message, first with our IDs and handles */
4244 CreateProcessMsg->ProcessHandle = ProcessHandle;
4245 CreateProcessMsg->ThreadHandle = ThreadHandle;
4246 CreateProcessMsg->ClientId = ClientId;
4247
4248 /* Write the remote PEB address and clear it locally, we no longer use it */
4249 CreateProcessMsg->PebAddressNative = RemotePeb;
4250 CreateProcessMsg->PebAddressWow64 = (ULONG)RemotePeb;
4251 RemotePeb = NULL;
4252
4253 /* Now check what kind of architecture this image was made for */
4254 switch (ImageInformation.Machine)
4255 {
4256 /* IA32, IA64 and AMD64 are supported in Server 2003 */
4257 case IMAGE_FILE_MACHINE_I386:
4258 CreateProcessMsg->ProcessorArchitecture = PROCESSOR_ARCHITECTURE_INTEL;
4259 break;
4260 case IMAGE_FILE_MACHINE_IA64:
4261 CreateProcessMsg->ProcessorArchitecture = PROCESSOR_ARCHITECTURE_IA64;
4262 break;
4263 case IMAGE_FILE_MACHINE_AMD64:
4264 CreateProcessMsg->ProcessorArchitecture = PROCESSOR_ARCHITECTURE_AMD64;
4265 break;
4266
4267 /* Anything else results in image unknown -- but no failure */
4268 default:
4269 DbgPrint("kernel32: No mapping for ImageInformation.Machine == %04x\n",
4270 ImageInformation.Machine);
4271 CreateProcessMsg->ProcessorArchitecture = PROCESSOR_ARCHITECTURE_UNKNOWN;
4272 break;
4273 }
4274
4275 /* Write the input creation flags except any debugger-related flags */
4276 CreateProcessMsg->CreationFlags = dwCreationFlags &
4277 ~(DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS);
4278
4279 /* CSRSS needs to know if this is a GUI app or not */
4280 if ((ImageInformation.SubSystemType == IMAGE_SUBSYSTEM_WINDOWS_GUI) ||
4281 (IsWowApp))
4282 {
4283 /*
4284 * For GUI apps we turn on the 2nd bit. This allow CSRSS server dlls
4285 * (basesrv in particular) to know whether or not this is a GUI or a
4286 * TUI application.
4287 */
4288 AddToHandle(CreateProcessMsg->ProcessHandle, 2);
4289
4290 /* Also check if the parent is also a GUI process */
4291 NtHeaders = RtlImageNtHeader(GetModuleHandle(NULL));
4292 if ((NtHeaders) &&
4293 (NtHeaders->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI))
4294 {
4295 /* Let it know that it should display the hourglass mouse cursor */
4296 AddToHandle(CreateProcessMsg->ProcessHandle, 1);
4297 }
4298 }
4299
4300 /* For all apps, if this flag is on, the hourglass mouse cursor is shown */
4301 if (StartupInfo.dwFlags & STARTF_FORCEONFEEDBACK)
4302 {
4303 AddToHandle(CreateProcessMsg->ProcessHandle, 1);
4304 }
4305
4306 /* Likewise, the opposite holds as well */
4307 if (StartupInfo.dwFlags & STARTF_FORCEOFFFEEDBACK)
4308 {
4309 RemoveFromHandle(CreateProcessMsg->ProcessHandle, 1);
4310 }
4311
4312 /* Also store which kind of VDM app (if any) this is */
4313 CreateProcessMsg->VdmBinaryType = VdmBinaryType;
4314
4315 /* And if it really is a VDM app... */
4316 if (VdmBinaryType)
4317 {
4318 /* Store the task ID and VDM console handle */
4319 CreateProcessMsg->hVDM = VdmTask ? 0 : Peb->ProcessParameters->ConsoleHandle;
4320 CreateProcessMsg->VdmTask = VdmTask;
4321 }
4322 else if (VdmReserve)
4323 {
4324 /* Extended VDM, set a flag */
4325 CreateProcessMsg->VdmBinaryType |= BINARY_TYPE_WOW_EX;
4326 }
4327
4328 /* Check if there's side-by-side assembly data associated with the process */
4329 if (CreateProcessMsg->Sxs.Flags)
4330 {
4331 /* This should not happen in ReactOS yet */
4332 DPRINT1("This is an SxS Message -- should not happen yet\n");
4333 BaseSetLastNTError(STATUS_NOT_IMPLEMENTED);
4334 NtTerminateProcess(ProcessHandle, STATUS_NOT_IMPLEMENTED);
4335 Result = FALSE;
4336 goto Quickie;
4337 }
4338
4339 /* We are finally ready to call CSRSS to tell it about our new process! */
4340 CsrClientCallServer((PCSR_API_MESSAGE)&CsrMsg[0],
4341 CaptureBuffer,
4342 CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX,
4343 BasepCreateProcess),
4344 sizeof(*CreateProcessMsg));
4345
4346 /* CSRSS has returned, free the capture buffer now if we had one */
4347 if (CaptureBuffer)
4348 {
4349 CsrFreeCaptureBuffer(CaptureBuffer);
4350 CaptureBuffer = NULL;
4351 }
4352
4353 /* Check if CSRSS failed to accept ownership of the new Windows process */
4354 if (!NT_SUCCESS(CsrMsg[0].Status))
4355 {
4356 /* Terminate the process and enter failure path with the CSRSS status */
4357 DPRINT1("Failed to tell csrss about new process\n");
4358 BaseSetLastNTError(CsrMsg[0].Status);
4359 NtTerminateProcess(ProcessHandle, CsrMsg[0].Status);
4360 Result = FALSE;
4361 goto Quickie;
4362 }
4363
4364 /* Check if we have a token due to Authz/Safer, not passed by the user */
4365 if ((TokenHandle) && !(hUserToken))
4366 {
4367 /* Replace the process and/or thread token with the one from Safer */
4368 Status = BasepReplaceProcessThreadTokens(TokenHandle,
4369 ProcessHandle,
4370 ThreadHandle);
4371 if (!NT_SUCCESS(Status))
4372 {
4373 /* If this failed, kill the process and enter the failure path */
4374 DPRINT1("Failed to update process token: %lx\n", Status);
4375 NtTerminateProcess(ProcessHandle, Status);
4376 BaseSetLastNTError(Status);
4377 Result = FALSE;
4378 goto Quickie;
4379 }
4380 }
4381
4382 /* Check if a job was associated with this process */
4383 if (JobHandle)
4384 {
4385 /* Bind the process and job together now */
4386 Status = NtAssignProcessToJobObject(JobHandle, ProcessHandle);
4387 if (!NT_SUCCESS(Status))
4388 {
4389 /* Kill the process and enter the failure path if binding failed */
4390 DPRINT1("Failed to assign process to job: %lx\n", Status);
4391 NtTerminateProcess(ProcessHandle, STATUS_ACCESS_DENIED);
4392 BaseSetLastNTError(Status);
4393 Result = FALSE;
4394 goto Quickie;
4395 }
4396 }
4397
4398 /* Finally, resume the thread to actually get the process started */
4399 if (!(dwCreationFlags & CREATE_SUSPENDED))
4400 {
4401 NtResumeThread(ThreadHandle, &ResumeCount);
4402 }
4403
4404 VdmShortCircuit:
4405 /* We made it this far, meaning we have a fully created process and thread */
4406 Result = TRUE;
4407
4408 /* Anyone doing a VDM undo should now undo everything, since we are done */
4409 if (VdmUndoLevel) VdmUndoLevel |= VDM_UNDO_COMPLETED;
4410
4411 /* Having a VDM wait object implies this must be a VDM process */
4412 if (VdmWaitObject)
4413 {
4414 /* Check if it's a 16-bit separate WOW process */
4415 if (VdmBinaryType == BINARY_TYPE_SEPARATE_WOW)
4416 {
4417 /* OR-in the special flag to indicate this, and return to caller */
4418 AddToHandle(VdmWaitObject, 2);
4419 lpProcessInformation->hProcess = VdmWaitObject;
4420
4421 /* Check if this was a re-used VDM */
4422 if (VdmUndoLevel & VDM_UNDO_REUSE)
4423 {
4424 /* No Client ID should be returned in this case */
4425 ClientId.UniqueProcess = 0;
4426 ClientId.UniqueThread = 0;
4427 }
4428 }
4429 else
4430 {
4431 /* OR-in the special flag to indicate this is not a separate VDM */
4432 AddToHandle(VdmWaitObject, 1);
4433
4434 /* Return handle to the caller */
4435 lpProcessInformation->hProcess = VdmWaitObject;
4436 }
4437
4438 /* Close the original process handle, since it's not needed for VDM */
4439 if (ProcessHandle) NtClose(ProcessHandle);
4440 }
4441 else
4442 {
4443 /* This is a regular process, so return the real process handle */
4444 lpProcessInformation->hProcess = ProcessHandle;
4445 }
4446
4447 /* Return the rest of the process information based on what we have so far */
4448 lpProcessInformation->hThread = ThreadHandle;
4449 lpProcessInformation->dwProcessId = HandleToUlong(ClientId.UniqueProcess);
4450 lpProcessInformation->dwThreadId = HandleToUlong(ClientId.UniqueThread);
4451
4452 /* NULL these out here so we know to treat this as a success scenario */
4453 ProcessHandle = NULL;
4454 ThreadHandle = NULL;
4455
4456 Quickie:
4457 /* Free the debugger command line if one was allocated */
4458 if (DebuggerCmdLine) RtlFreeHeap(RtlGetProcessHeap(), 0, DebuggerCmdLine);
4459
4460 /* Check if an SxS full path as queried */
4461 if (PathBuffer)
4462 {
4463 /* Reinitialize the executable path */
4464 RtlInitEmptyUnicodeString(&SxsWin32ExePath, NULL, 0);
4465 SxsWin32ExePath.Length = 0;
4466
4467 /* Free the path buffer */
4468 RtlFreeHeap(RtlGetProcessHeap(), 0, PathBuffer);
4469 }
4470
4471 #if _SXS_SUPPORT_ENABLED_
4472 /* Check if this was a non-VDM process */
4473 if (!VdmBinaryType)
4474 {
4475 /* Then it must've had SxS data, so close the handles used for it */
4476 BasepSxsCloseHandles(&Handles);
4477 BasepSxsCloseHandles(&FileHandles);
4478
4479 /* Check if we built SxS byte buffers for this create process request */
4480 if (SxsConglomeratedBuffer)
4481 {
4482 /* Loop all of them */
4483 for (i = 0; i < 5; i++)
4484 {
4485 /* Check if this one was allocated */
4486 ThisBuffer = SxsStaticBuffers[i];
4487 if (ThisBuffer)
4488 {
4489 /* Get the underlying RTL_BUFFER structure */
4490 ByteBuffer = &ThisBuffer->ByteBuffer;
4491 if ((ThisBuffer != (PVOID)-8) && (ByteBuffer->Buffer))
4492 {
4493 /* Check if it was dynamic */
4494 if (ByteBuffer->Buffer != ByteBuffer->StaticBuffer)
4495 {
4496 /* Free it from the heap */
4497 FreeString.Buffer = (PWCHAR)ByteBuffer->Buffer;
4498 RtlFreeUnicodeString(&FreeString);
4499 }
4500
4501 /* Reset the buffer to its static data */
4502 ByteBuffer->Buffer = ByteBuffer->StaticBuffer;
4503 ByteBuffer->Size = ByteBuffer->StaticSize;
4504 }
4505
4506 /* Reset the string to the static buffer */
4507 RtlInitEmptyUnicodeString(&ThisBuffer->String,
4508 (PWCHAR)ByteBuffer->StaticBuffer,
4509 ByteBuffer->StaticSize);
4510 if (ThisBuffer->String.Buffer)
4511 {
4512 /* Also NULL-terminate it */
4513 *ThisBuffer->String.Buffer = UNICODE_NULL;
4514 }
4515 }
4516 }
4517 }
4518 }
4519 #endif
4520 /* Check if an environment was passed in */
4521 if ((lpEnvironment) && !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT))
4522 {
4523 /* Destroy it */
4524 RtlDestroyEnvironment(lpEnvironment);
4525
4526 /* If this was the VDM environment too, clear that as well */
4527 if (VdmUnicodeEnv.Buffer == lpEnvironment) VdmUnicodeEnv.Buffer = NULL;
4528 lpEnvironment = NULL;
4529 }
4530
4531 /* Unconditionally free all the name parsing buffers we always allocate */
4532 RtlFreeHeap(RtlGetProcessHeap(), 0, QuotedCmdLine);
4533 RtlFreeHeap(RtlGetProcessHeap(), 0, NameBuffer);
4534 RtlFreeHeap(RtlGetProcessHeap(), 0, CurrentDirectory);
4535 RtlFreeHeap(RtlGetProcessHeap(), 0, FreeBuffer);
4536
4537 /* Close open file/section handles */
4538 if (FileHandle) NtClose(FileHandle);
4539 if (SectionHandle) NtClose(SectionHandle);
4540
4541 /* If we have a thread handle, this was a failure path */
4542 if (ThreadHandle)
4543 {
4544 /* So kill the process and close the thread handle */
4545 NtTerminateProcess(ProcessHandle, 0);
4546 NtClose(ThreadHandle);
4547 }
4548
4549 /* If we have a process handle, this was a failure path, so close it */
4550 if (ProcessHandle) NtClose(ProcessHandle);
4551
4552 /* Thread/process handles, if any, are now processed. Now close this one. */
4553 if (JobHandle) NtClose(JobHandle);
4554
4555 /* Check if we had created a token */
4556 if (TokenHandle)
4557 {
4558 /* And if the user asked for one */
4559 if (hUserToken)
4560 {
4561 /* Then return it */
4562 *hNewToken = TokenHandle;
4563 }
4564 else
4565 {
4566 /* User didn't want it, so we used it temporarily -- close it */
4567 NtClose(TokenHandle);
4568 }
4569 }
4570
4571 /* Free any temporary app compatibility data, it's no longer needed */
4572 BasepFreeAppCompatData(AppCompatData, AppCompatSxsData);
4573
4574 /* Free a few strings. The API takes care of these possibly being NULL */
4575 RtlFreeUnicodeString(&VdmString);
4576 RtlFreeUnicodeString(&DebuggerString);
4577
4578 /* Check if we had built any sort of VDM environment */
4579 if ((VdmAnsiEnv.Buffer) || (VdmUnicodeEnv.Buffer))
4580 {
4581 /* Free it */
4582 BaseDestroyVDMEnvironment(&VdmAnsiEnv, &VdmUnicodeEnv);
4583 }
4584
4585 /* Check if this was any kind of VDM application that we ended up creating */
4586 if ((VdmUndoLevel) && (!(VdmUndoLevel & VDM_UNDO_COMPLETED)))
4587 {
4588 /* Send an undo */
4589 BaseUpdateVDMEntry(VdmEntryUndo,
4590 (PHANDLE)&VdmTask,
4591 VdmUndoLevel,
4592 VdmBinaryType);
4593
4594 /* And close whatever VDM handle we were using for notifications */
4595 if (VdmWaitObject) NtClose(VdmWaitObject);
4596 }
4597
4598 /* Check if we ended up here with an allocated search path, and free it */
4599 if (SearchPath) RtlFreeHeap(RtlGetProcessHeap(), 0, SearchPath);
4600
4601 /* Finally, return the API's result */
4602 return Result;
4603 }
4604
4605 /*
4606 * @implemented
4607 */
4608 BOOL
4609 WINAPI
4610 DECLSPEC_HOTPATCH
4611 CreateProcessW(LPCWSTR lpApplicationName,
4612 LPWSTR lpCommandLine,
4613 LPSECURITY_ATTRIBUTES lpProcessAttributes,
4614 LPSECURITY_ATTRIBUTES lpThreadAttributes,
4615 BOOL bInheritHandles,
4616 DWORD dwCreationFlags,
4617 LPVOID lpEnvironment,
4618 LPCWSTR lpCurrentDirectory,
4619 LPSTARTUPINFOW lpStartupInfo,
4620 LPPROCESS_INFORMATION lpProcessInformation)
4621 {
4622 /* Call the internal (but exported) version */
4623 return CreateProcessInternalW(NULL,
4624 lpApplicationName,
4625 lpCommandLine,
4626 lpProcessAttributes,
4627 lpThreadAttributes,
4628 bInheritHandles,
4629 dwCreationFlags,
4630 lpEnvironment,
4631 lpCurrentDirectory,
4632 lpStartupInfo,
4633 lpProcessInformation,
4634 NULL);
4635 }
4636
4637 /*
4638 * @implemented
4639 */
4640 BOOL
4641 WINAPI
4642 CreateProcessInternalA(HANDLE hToken,
4643 LPCSTR lpApplicationName,
4644 LPSTR lpCommandLine,
4645 LPSECURITY_ATTRIBUTES lpProcessAttributes,
4646 LPSECURITY_ATTRIBUTES lpThreadAttributes,
4647 BOOL bInheritHandles,
4648 DWORD dwCreationFlags,
4649 LPVOID lpEnvironment,
4650 LPCSTR lpCurrentDirectory,
4651 LPSTARTUPINFOA lpStartupInfo,
4652 LPPROCESS_INFORMATION lpProcessInformation,
4653 PHANDLE hNewToken)
4654 {
4655 PUNICODE_STRING CommandLine = NULL;
4656 UNICODE_STRING DummyString;
4657 UNICODE_STRING LiveCommandLine;
4658 UNICODE_STRING ApplicationName;
4659 UNICODE_STRING CurrentDirectory;
4660 BOOL bRetVal;
4661 STARTUPINFOW StartupInfo;
4662
4663 DPRINT("dwCreationFlags %x, lpEnvironment %p, lpCurrentDirectory %p, "
4664 "lpStartupInfo %p, lpProcessInformation %p\n",
4665 dwCreationFlags, lpEnvironment, lpCurrentDirectory,
4666 lpStartupInfo, lpProcessInformation);
4667
4668 /* Copy Startup Info */
4669 RtlMoveMemory(&StartupInfo, lpStartupInfo, sizeof(*lpStartupInfo));
4670
4671 /* Initialize all strings to nothing */
4672 LiveCommandLine.Buffer = NULL;
4673 DummyString.Buffer = NULL;
4674 ApplicationName.Buffer = NULL;
4675 CurrentDirectory.Buffer = NULL;
4676 StartupInfo.lpDesktop = NULL;
4677 StartupInfo.lpReserved = NULL;
4678 StartupInfo.lpTitle = NULL;
4679
4680 /* Convert the Command line */
4681 if (lpCommandLine)
4682 {
4683 /* If it's too long, then we'll have a problem */
4684 if ((strlen(lpCommandLine) + 1) * sizeof(WCHAR) <
4685 NtCurrentTeb()->StaticUnicodeString.MaximumLength)
4686 {
4687 /* Cache it in the TEB */
4688 CommandLine = Basep8BitStringToStaticUnicodeString(lpCommandLine);
4689 }
4690 else
4691 {
4692 /* Use a dynamic version */
4693 Basep8BitStringToDynamicUnicodeString(&LiveCommandLine,
4694 lpCommandLine);
4695 }
4696 }
4697 else
4698 {
4699 /* The logic below will use CommandLine, so we must make it valid */
4700 CommandLine = &DummyString;
4701 }
4702
4703 /* Convert the Name and Directory */
4704 if (lpApplicationName)
4705 {
4706 Basep8BitStringToDynamicUnicodeString(&ApplicationName,
4707 lpApplicationName);
4708 }
4709 if (lpCurrentDirectory)
4710 {
4711 Basep8BitStringToDynamicUnicodeString(&CurrentDirectory,
4712 lpCurrentDirectory);
4713 }
4714
4715 /* Now convert Startup Strings */
4716 if (lpStartupInfo->lpReserved)
4717 {
4718 BasepAnsiStringToHeapUnicodeString(lpStartupInfo->lpReserved,
4719 &StartupInfo.lpReserved);
4720 }
4721 if (lpStartupInfo->lpDesktop)
4722 {
4723 BasepAnsiStringToHeapUnicodeString(lpStartupInfo->lpDesktop,
4724 &StartupInfo.lpDesktop);
4725 }
4726 if (lpStartupInfo->lpTitle)
4727 {
4728 BasepAnsiStringToHeapUnicodeString(lpStartupInfo->lpTitle,
4729 &StartupInfo.lpTitle);
4730 }
4731
4732 /* Call the Unicode function */
4733 bRetVal = CreateProcessInternalW(hToken,
4734 ApplicationName.Buffer,
4735 LiveCommandLine.Buffer ?
4736 LiveCommandLine.Buffer : CommandLine->Buffer,
4737 lpProcessAttributes,
4738 lpThreadAttributes,
4739 bInheritHandles,
4740 dwCreationFlags,
4741 lpEnvironment,
4742 CurrentDirectory.Buffer,
4743 &StartupInfo,
4744 lpProcessInformation,
4745 hNewToken);
4746
4747 /* Clean up */
4748 RtlFreeUnicodeString(&ApplicationName);
4749 RtlFreeUnicodeString(&LiveCommandLine);
4750 RtlFreeUnicodeString(&CurrentDirectory);
4751 RtlFreeHeap(RtlGetProcessHeap(), 0, StartupInfo.lpDesktop);
4752 RtlFreeHeap(RtlGetProcessHeap(), 0, StartupInfo.lpReserved);
4753 RtlFreeHeap(RtlGetProcessHeap(), 0, StartupInfo.lpTitle);
4754
4755 /* Return what Unicode did */
4756 return bRetVal;
4757 }
4758
4759 /*
4760 * FUNCTION: The CreateProcess function creates a new process and its
4761 * primary thread. The new process executes the specified executable file
4762 * ARGUMENTS:
4763 *
4764 * lpApplicationName = Pointer to name of executable module
4765 * lpCommandLine = Pointer to command line string
4766 * lpProcessAttributes = Process security attributes
4767 * lpThreadAttributes = Thread security attributes
4768 * bInheritHandles = Handle inheritance flag
4769 * dwCreationFlags = Creation flags
4770 * lpEnvironment = Pointer to new environment block
4771 * lpCurrentDirectory = Pointer to current directory name
4772 * lpStartupInfo = Pointer to startup info
4773 * lpProcessInformation = Pointer to process information
4774 *
4775 * @implemented
4776 */
4777 BOOL
4778 WINAPI
4779 DECLSPEC_HOTPATCH
4780 CreateProcessA(LPCSTR lpApplicationName,
4781 LPSTR lpCommandLine,
4782 LPSECURITY_ATTRIBUTES lpProcessAttributes,
4783 LPSECURITY_ATTRIBUTES lpThreadAttributes,
4784 BOOL bInheritHandles,
4785 DWORD dwCreationFlags,
4786 LPVOID lpEnvironment,
4787 LPCSTR lpCurrentDirectory,
4788 LPSTARTUPINFOA lpStartupInfo,
4789 LPPROCESS_INFORMATION lpProcessInformation)
4790 {
4791 /* Call the internal (but exported) version */
4792 return CreateProcessInternalA(NULL,
4793 lpApplicationName,
4794 lpCommandLine,
4795 lpProcessAttributes,
4796 lpThreadAttributes,
4797 bInheritHandles,
4798 dwCreationFlags,
4799 lpEnvironment,
4800 lpCurrentDirectory,
4801 lpStartupInfo,
4802 lpProcessInformation,
4803 NULL);
4804 }
4805
4806 /*
4807 * @implemented
4808 */
4809 UINT
4810 WINAPI
4811 WinExec(LPCSTR lpCmdLine,
4812 UINT uCmdShow)
4813 {
4814 STARTUPINFOA StartupInfo;
4815 PROCESS_INFORMATION ProcessInformation;
4816 DWORD dosErr;
4817
4818 RtlZeroMemory(&StartupInfo, sizeof(StartupInfo));
4819 StartupInfo.cb = sizeof(STARTUPINFOA);
4820 StartupInfo.wShowWindow = (WORD)uCmdShow;
4821 StartupInfo.dwFlags = 0;
4822
4823 if (!CreateProcessA(NULL,
4824 (PVOID)lpCmdLine,
4825 NULL,
4826 NULL,
4827 FALSE,
4828 0,
4829 NULL,
4830 NULL,
4831 &StartupInfo,
4832 &ProcessInformation))
4833 {
4834 dosErr = GetLastError();
4835 return dosErr < 32 ? dosErr : ERROR_BAD_FORMAT;
4836 }
4837
4838 if (NULL != UserWaitForInputIdleRoutine)
4839 {
4840 UserWaitForInputIdleRoutine(ProcessInformation.hProcess,
4841 10000);
4842 }
4843
4844 NtClose(ProcessInformation.hProcess);
4845 NtClose(ProcessInformation.hThread);
4846
4847 return 33; /* Something bigger than 31 means success. */
4848 }
4849
4850 /* EOF */