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