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