--- /dev/null
- // NOTE: Code duplicated from BasepDuplicateAndWriteHandle
+/*
+ * COPYRIGHT: See COPYING in the top level directory
+ * PROJECT: ReactOS system libraries
+ * FILE: lib/kernel32/proc/proc.c
+ * PURPOSE: Process functions
+ * PROGRAMMERS: Ariadne (ariadne@xs4all.nl)
+ * UPDATE HISTORY:
+ * Created 01/11/98
+ */
+
+/* INCLUDES ****************************************************************/
+
+#include <k32.h>
+
+#define NDEBUG
+#include <debug.h>
+
+/* GLOBALS *******************************************************************/
+
+WaitForInputIdleType UserWaitForInputIdleRoutine;
+UNICODE_STRING BaseUnicodeCommandLine;
+ANSI_STRING BaseAnsiCommandLine;
+UNICODE_STRING BasePathVariableName = RTL_CONSTANT_STRING(L"PATH");
+LPSTARTUPINFOA BaseAnsiStartupInfo = NULL;
+PLDR_DATA_TABLE_ENTRY BasepExeLdrEntry;
+BOOLEAN g_AppCertInitialized;
+BOOLEAN g_HaveAppCerts;
+LIST_ENTRY BasepAppCertDllsList;
+RTL_CRITICAL_SECTION gcsAppCert;
+PBASEP_APPCERT_EMBEDDED_FUNC fEmbeddedCertFunc;
+NTSTATUS g_AppCertStatus;
+RTL_QUERY_REGISTRY_TABLE BasepAppCertTable[2] =
+{
+ {
+ BasepConfigureAppCertDlls,
+ 1,
+ L"AppCertDlls",
+ &BasepAppCertDllsList,
+ 0,
+ NULL,
+ 0
+ }
+};
+
+PSAFER_REPLACE_PROCESS_THREAD_TOKENS g_SaferReplaceProcessThreadTokens;
+HMODULE gSaferHandle = (HMODULE)-1;
+
+VOID WINAPI
+RegisterWaitForInputIdle(WaitForInputIdleType lpfnRegisterWaitForInputIdle);
+
+#define CMD_STRING L"cmd /c "
+#define NTVDM_STRING L"\\ntvdm.exe"
+
+/* FUNCTIONS ****************************************************************/
+
- SIZE_T Dummy;
+VOID
+WINAPI
+StuffStdHandle(IN HANDLE ProcessHandle,
+ IN HANDLE StandardHandle,
+ IN PHANDLE Address)
+{
+ NTSTATUS Status;
+ HANDLE DuplicatedHandle;
- 0, 0,
- DUPLICATE_SAME_ACCESS | DUPLICATE_SAME_ATTRIBUTES);
- if (NT_SUCCESS(Status))
- {
- /* Write it */
- NtWriteVirtualMemory(ProcessHandle,
- Address,
- &DuplicatedHandle,
- sizeof(HANDLE),
- &Dummy);
- }
++ SIZE_T NumberOfBytesWritten;
++
++ /* If there is no handle to duplicate, return immediately */
++ if (!StandardHandle) return;
+
+ /* Duplicate the handle */
+ Status = NtDuplicateObject(NtCurrentProcess(),
+ StandardHandle,
+ ProcessHandle,
+ &DuplicatedHandle,
- BuildSubSysCommandLine(IN LPWSTR SubsystemName,
- IN LPWSTR ApplicationName,
- IN LPWSTR CommandLine,
++ 0,
++ 0,
++ DUPLICATE_SAME_ACCESS |
++ DUPLICATE_SAME_ATTRIBUTES);
++ if (!NT_SUCCESS(Status)) return;
++
++ /* Write it */
++ NtWriteVirtualMemory(ProcessHandle,
++ Address,
++ &DuplicatedHandle,
++ sizeof(HANDLE),
++ &NumberOfBytesWritten);
+}
+
+BOOLEAN
+WINAPI
- BasepIsProcessAllowed(IN PCHAR ApplicationName)
++BuildSubSysCommandLine(IN LPCWSTR SubsystemName,
++ IN LPCWSTR ApplicationName,
++ IN LPCWSTR CommandLine,
+ OUT PUNICODE_STRING SubsysCommandLine)
+{
+ UNICODE_STRING CommandLineString, ApplicationNameString;
+ PWCHAR Buffer;
+ ULONG Length;
+
+ /* Convert to unicode strings */
+ RtlInitUnicodeString(&CommandLineString, ApplicationName);
+ RtlInitUnicodeString(&ApplicationNameString, CommandLine);
+
+ /* Allocate buffer for the output string */
+ Length = CommandLineString.MaximumLength + ApplicationNameString.MaximumLength + 32;
+ Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, Length);
+ RtlInitEmptyUnicodeString(SubsysCommandLine, Buffer, Length);
+ if (!Buffer)
+ {
+ /* Fail, no memory */
+ BaseSetLastNTError(STATUS_NO_MEMORY);
+ return FALSE;
+ }
+
+ /* Build the final subsystem command line */
+ RtlAppendUnicodeToString(SubsysCommandLine, SubsystemName);
+ RtlAppendUnicodeStringToString(SubsysCommandLine, &CommandLineString);
+ RtlAppendUnicodeToString(SubsysCommandLine, L" /C ");
+ RtlAppendUnicodeStringToString(SubsysCommandLine, &ApplicationNameString);
+ return TRUE;
+}
+
+BOOLEAN
+WINAPI
+BasepIsImageVersionOk(IN ULONG ImageMajorVersion,
+ IN ULONG ImageMinorVersion)
+{
+ /* Accept images for NT 3.1 or higher, as long as they're not newer than us */
+ return ((ImageMajorVersion >= 3) &&
+ ((ImageMajorVersion != 3) ||
+ (ImageMinorVersion >= 10)) &&
+ (ImageMajorVersion <= SharedUserData->NtMajorVersion) &&
+ ((ImageMajorVersion != SharedUserData->NtMajorVersion) ||
+ (ImageMinorVersion <= SharedUserData->NtMinorVersion)));
+}
+
+NTSTATUS
+WINAPI
+BasepCheckWebBladeHashes(IN HANDLE FileHandle)
+{
+ NTSTATUS Status;
+ CHAR Hash[16];
+
+ /* Get all the MD5 hashes */
+ Status = RtlComputeImportTableHash(FileHandle, Hash, 1);
+ if (!NT_SUCCESS(Status)) return Status;
+
+ /* Depending on which suite this is, run a bsearch and block the appropriate ones */
+ if (SharedUserData->SuiteMask & VER_SUITE_COMPUTE_SERVER)
+ {
+ DPRINT1("Egad! This is a ReactOS Compute Server and we should prevent you from using certain APIs...but we won't.");
+ }
+ else if (SharedUserData->SuiteMask & VER_SUITE_STORAGE_SERVER)
+ {
+ DPRINT1("Gasp! This is a ReactOS Storage Server and we should prevent you from using certain APIs...but we won't.");
+ }
+ else if (SharedUserData->SuiteMask & VER_SUITE_BLADE)
+ {
+ DPRINT1("Golly! This is a ReactOS Web Blade Server and we should prevent you from using certain APIs...but we won't.");
+ }
+
+ /* Actually, fuck it, don't block anything, we're open source */
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+NTAPI
+BasepSaveAppCertRegistryValue(IN PLIST_ENTRY List,
+ IN PWCHAR ComponentName,
+ IN PWCHAR DllName)
+{
+ /* Pretty much the only thing this key is used for, is malware */
+ UNIMPLEMENTED;
+ return STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS
+NTAPI
+BasepConfigureAppCertDlls(IN PWSTR ValueName,
+ IN ULONG ValueType,
+ IN PVOID ValueData,
+ IN ULONG ValueLength,
+ IN PVOID Context,
+ IN PVOID EntryContext)
+{
+ /* Add this to the certification list */
+ return BasepSaveAppCertRegistryValue(Context, ValueName, ValueData);
+}
+
+NTSTATUS
+WINAPI
- NTSTATUS Status;
++BasepIsProcessAllowed(IN LPWSTR ApplicationName)
+{
- Status = NtOpenKey(&KeyHandle, KEY_READ, &KeyAttributes);
- if (NT_SUCCESS(Status))
++ NTSTATUS Status, Status1;
+ PWCHAR Buffer;
+ UINT Length;
+ HMODULE TrustLibrary;
+ PBASEP_APPCERT_ENTRY Entry;
+ ULONG CertFlag;
+ PLIST_ENTRY NextEntry;
+ HANDLE KeyHandle;
+ UNICODE_STRING CertKey = RTL_CONSTANT_STRING(L"\\Registry\\MACHINE\\System\\CurrentControlSet\\Control\\Session Manager\\AppCertDlls");
+ OBJECT_ATTRIBUTES KeyAttributes = RTL_CONSTANT_OBJECT_ATTRIBUTES(&CertKey, OBJ_CASE_INSENSITIVE);
+
+ /* Try to initialize the certification subsystem */
+ while (!g_AppCertInitialized)
+ {
+ /* Defaults */
+ Status = STATUS_SUCCESS;
+ Buffer = NULL;
+
+ /* Acquire the lock while initializing and see if we lost a race */
+ RtlEnterCriticalSection(&gcsAppCert);
+ if (g_AppCertInitialized) break;
+
+ /* On embedded, there is a special DLL */
+ if (SharedUserData->SuiteMask & VER_SUITE_EMBEDDEDNT)
+ {
+ /* Allocate a buffer for the name */
+ Buffer = RtlAllocateHeap(RtlGetProcessHeap(),
+ 0,
+ MAX_PATH * sizeof(WCHAR) +
+ sizeof(UNICODE_NULL));
+ if (!Buffer)
+ {
+ /* Fail if no memory */
+ Status = STATUS_NO_MEMORY;
+ }
+ else
+ {
+ /* Now get the system32 directory in our buffer, make sure it fits */
+ Length = GetSystemDirectoryW(Buffer, MAX_PATH - sizeof("EmbdTrst.DLL"));
+ if ((Length) && (Length <= MAX_PATH - sizeof("EmbdTrst.DLL")))
+ {
+ /* Add a slash if needed, and add the embedded cert DLL name */
+ if (Buffer[Length - 1] != '\\') Buffer[Length++] = '\\';
+ RtlCopyMemory(&Buffer[Length],
+ L"EmbdTrst.DLL",
+ sizeof(L"EmbdTrst.DLL"));
+
+ /* Try to load it */
+ TrustLibrary = LoadLibraryW(Buffer);
+ if (TrustLibrary)
+ {
+ /* And extract the special function out of it */
+ fEmbeddedCertFunc = (PVOID)GetProcAddress(TrustLibrary,
+ "ImageOkToRunOnEmbeddedNT");
+ }
+ }
+
+ /* If we didn't get this far, set a failure code */
+ if (!fEmbeddedCertFunc) Status = STATUS_UNSUCCESSFUL;
+ }
+ }
+ else
+ {
+ /* Other systems have a registry entry for this */
- Status = RtlQueryRegistryValues(2,
++ Status1 = NtOpenKey(&KeyHandle, KEY_READ, &KeyAttributes);
++ if (NT_SUCCESS(Status1))
+ {
+ /* Close it, we'll query it through Rtl */
+ NtClose(KeyHandle);
+
+ /* Do the query, which will call a special callback */
- 0,
- 0);
- if (Status == 0xC0000034) Status = STATUS_SUCCESS;
++ Status = RtlQueryRegistryValues(RTL_REGISTRY_CONTROL,
+ L"Session Manager",
+ BasepAppCertTable,
- /*
- * Creates the first Thread in a Proces
- */
- HANDLE
- WINAPI
- BasepCreateFirstThread(HANDLE ProcessHandle,
- LPSECURITY_ATTRIBUTES lpThreadAttributes,
- PSECTION_IMAGE_INFORMATION SectionImageInfo,
- PCLIENT_ID ClientId,
- DWORD dwCreationFlags)
- {
- NTSTATUS Status;
- OBJECT_ATTRIBUTES LocalObjectAttributes;
- POBJECT_ATTRIBUTES ObjectAttributes;
- CONTEXT Context;
- INITIAL_TEB InitialTeb;
- HANDLE hThread;
- BASE_API_MESSAGE ApiMessage;
- PBASE_CREATE_PROCESS CreateProcessRequest = &ApiMessage.Data.CreateProcessRequest;
-
- DPRINT("BasepCreateFirstThread. hProcess: %lx\n", ProcessHandle);
-
- /* Create the Thread's Stack */
- BaseCreateStack(ProcessHandle,
- SectionImageInfo->MaximumStackSize,
- SectionImageInfo->CommittedStackSize,
- &InitialTeb);
-
- /* Create the Thread's Context */
- BaseInitializeContext(&Context,
- NtCurrentPeb(),
- SectionImageInfo->TransferAddress,
- InitialTeb.StackBase,
- 0);
-
- /* Convert the thread attributes */
- ObjectAttributes = BaseFormatObjectAttributes(&LocalObjectAttributes,
- lpThreadAttributes,
- NULL);
-
- /* Create the Kernel Thread Object */
- Status = NtCreateThread(&hThread,
- THREAD_ALL_ACCESS,
- ObjectAttributes,
- ProcessHandle,
- ClientId,
- &Context,
- &InitialTeb,
- TRUE);
- if (!NT_SUCCESS(Status))
- {
- return NULL;
- }
-
- /* Fill out the request to notify CSRSS */
- CreateProcessRequest->ClientId = *ClientId;
- CreateProcessRequest->ProcessHandle = ProcessHandle;
- CreateProcessRequest->ThreadHandle = hThread;
- CreateProcessRequest->CreationFlags = dwCreationFlags;
-
- /*
- * For GUI applications we turn on the 2nd bit. This also allows
- * us to know whether or not this is a GUI or a TUI application.
- */
- if (IMAGE_SUBSYSTEM_WINDOWS_GUI == SectionImageInfo->SubSystemType)
- {
- CreateProcessRequest->ProcessHandle = (HANDLE)
- ((ULONG_PTR)CreateProcessRequest->ProcessHandle | 2);
- }
-
- /* Call CSR */
- Status = CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
- NULL,
- CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepCreateProcess),
- sizeof(BASE_CREATE_PROCESS));
- if (!NT_SUCCESS(Status))
- {
- DPRINT1("Failed to tell CSRSS about new process: %lx\n", Status);
- return NULL;
- }
-
- /* Success */
- return hThread;
- }
-
- /*
- * Converts ANSI to Unicode Environment
- */
- PVOID
- WINAPI
- BasepConvertUnicodeEnvironment(OUT SIZE_T* EnvSize,
- IN PVOID lpEnvironment)
- {
- PCHAR pcScan;
- ANSI_STRING AnsiEnv;
- UNICODE_STRING UnicodeEnv;
- NTSTATUS Status;
-
- DPRINT("BasepConvertUnicodeEnvironment\n");
-
- /* Scan the environment to calculate its Unicode size */
- AnsiEnv.Buffer = pcScan = (PCHAR)lpEnvironment;
- while (*pcScan)
- {
- pcScan += strlen(pcScan) + 1;
- }
-
- /* Create our ANSI String */
- if (pcScan == (PCHAR)lpEnvironment)
- {
- AnsiEnv.Length = 2 * sizeof(CHAR);
- }
- else
- {
-
- AnsiEnv.Length = (USHORT)((ULONG_PTR)pcScan - (ULONG_PTR)lpEnvironment + sizeof(CHAR));
- }
- AnsiEnv.MaximumLength = AnsiEnv.Length + 1;
-
- /* Allocate memory for the Unicode Environment */
- UnicodeEnv.Buffer = NULL;
- *EnvSize = AnsiEnv.MaximumLength * sizeof(WCHAR);
- Status = NtAllocateVirtualMemory(NtCurrentProcess(),
- (PVOID)&UnicodeEnv.Buffer,
- 0,
- EnvSize,
- MEM_COMMIT,
- PAGE_READWRITE);
- /* Failure */
- if (!NT_SUCCESS(Status))
- {
- SetLastError(Status);
- *EnvSize = 0;
- return NULL;
- }
-
- /* Use the allocated size */
- UnicodeEnv.MaximumLength = (USHORT)*EnvSize;
-
- /* Convert */
- RtlAnsiStringToUnicodeString(&UnicodeEnv, &AnsiEnv, FALSE);
- return UnicodeEnv.Buffer;
- }
-
- /*
- * Converts a Win32 Priority Class to NT
- */
- ULONG
- WINAPI
- BasepConvertPriorityClass(IN ULONG dwCreationFlags)
- {
- ULONG ReturnClass;
-
- if(dwCreationFlags & IDLE_PRIORITY_CLASS)
- {
- ReturnClass = PROCESS_PRIORITY_CLASS_IDLE;
- }
- else if(dwCreationFlags & BELOW_NORMAL_PRIORITY_CLASS)
- {
- ReturnClass = PROCESS_PRIORITY_CLASS_BELOW_NORMAL;
- }
- else if(dwCreationFlags & NORMAL_PRIORITY_CLASS)
- {
- ReturnClass = PROCESS_PRIORITY_CLASS_NORMAL;
- }
- else if(dwCreationFlags & ABOVE_NORMAL_PRIORITY_CLASS)
- {
- ReturnClass = PROCESS_PRIORITY_CLASS_ABOVE_NORMAL;
- }
- else if(dwCreationFlags & HIGH_PRIORITY_CLASS)
- {
- ReturnClass = PROCESS_PRIORITY_CLASS_HIGH;
- }
- else if(dwCreationFlags & REALTIME_PRIORITY_CLASS)
- {
- /* Check for Privilege First */
- if (BasepIsRealtimeAllowed(TRUE))
- {
- ReturnClass = PROCESS_PRIORITY_CLASS_REALTIME;
- }
- else
- {
- ReturnClass = PROCESS_PRIORITY_CLASS_HIGH;
- }
- }
- else
- {
- ReturnClass = PROCESS_PRIORITY_CLASS_INVALID;
- }
-
- return ReturnClass;
- }
-
- /*
- * Duplicates a standard handle and writes it where requested.
- */
- // NOTE: Code duplicated from StuffStdHandle
- VOID
- WINAPI
- BasepDuplicateAndWriteHandle(IN HANDLE ProcessHandle,
- IN HANDLE StandardHandle,
- IN PHANDLE Address)
- {
- NTSTATUS Status;
- HANDLE DuplicatedHandle;
- SIZE_T Dummy;
-
- DPRINT("BasepDuplicateAndWriteHandle. hProcess: %lx, Handle: %lx,"
- "Address: %p\n", ProcessHandle, StandardHandle, Address);
-
- /* Don't touch Console Handles */
- if (IsConsoleHandle(StandardHandle)) return;
-
- /* Duplicate the handle */
- Status = NtDuplicateObject(NtCurrentProcess(),
- StandardHandle,
- ProcessHandle,
- &DuplicatedHandle,
- 0, 0,
- DUPLICATE_SAME_ACCESS | DUPLICATE_SAME_ATTRIBUTES);
- if (NT_SUCCESS(Status))
- {
- /* Write it */
- NtWriteVirtualMemory(ProcessHandle,
- Address,
- &DuplicatedHandle,
- sizeof(HANDLE),
- &Dummy);
- }
- }
-
++ NULL,
++ NULL);
++ if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
++ {
++ Status = STATUS_SUCCESS;
++ }
+ }
+ }
+
+ /* Free any buffer if we had one */
+ if (Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
+
+ /* Check for errors, or a missing embedded/custom certification DLL */
+ if (!NT_SUCCESS(Status) ||
+ (!(fEmbeddedCertFunc) && (IsListEmpty(&BasepAppCertDllsList))))
+ {
+ /* The subsystem is not active on this machine, so give up */
+ g_HaveAppCerts = FALSE;
+ g_AppCertStatus = Status;
+ }
+ else
+ {
+ /* We have certification DLLs active, remember this */
+ g_HaveAppCerts = TRUE;
+ }
+
+ /* We are done the initialization phase, release the lock */
+ g_AppCertInitialized = TRUE;
+ RtlLeaveCriticalSection(&gcsAppCert);
+ }
+
+ /* If there's no certification DLLs present, return the failure code */
+ if (!g_HaveAppCerts) return g_AppCertStatus;
+
+ /* Otherwise, assume success and make sure we have *something* */
+ ASSERT(fEmbeddedCertFunc || !IsListEmpty(&BasepAppCertDllsList));
+ Status = STATUS_SUCCESS;
+
+ /* If the something is an embedded certification DLL, call it and return */
+ if (fEmbeddedCertFunc) return fEmbeddedCertFunc(ApplicationName);
+
+ /* Otherwise we have custom certification DLLs, parse them */
+ NextEntry = BasepAppCertDllsList.Flink;
+ CertFlag = 2;
+ while (NextEntry != &BasepAppCertDllsList)
+ {
+ /* Make sure the entry has a callback */
+ Entry = CONTAINING_RECORD(NextEntry, BASEP_APPCERT_ENTRY, Entry);
+ ASSERT(Entry->fPluginCertFunc != NULL);
+
+ /* Call it and check if it failed */
+ Status = Entry->fPluginCertFunc(ApplicationName, 1);
+ if (!NT_SUCCESS(Status)) CertFlag = 3;
+
+ /* Move on */
+ NextEntry = NextEntry->Flink;
+ }
+
+ /* Now loop them again */
+ NextEntry = BasepAppCertDllsList.Flink;
+ while (NextEntry != &BasepAppCertDllsList)
+ {
+ /* Make sure the entry has a callback */
+ Entry = CONTAINING_RECORD(NextEntry, BASEP_APPCERT_ENTRY, Entry);
+ ASSERT(Entry->fPluginCertFunc != NULL);
+
+ /* Call it, this time with the flag from the loop above */
+ Status = Entry->fPluginCertFunc(ApplicationName, CertFlag);
+ }
+
+ /* All done, return the status */
+ return Status;
+}
+
+NTSTATUS
+WINAPI
+BasepReplaceProcessThreadTokens(IN HANDLE TokenHandle,
+ IN HANDLE ProcessHandle,
+ IN HANDLE ThreadHandle)
+{
+ NTSTATUS Status;
+ ANSI_STRING SaferiReplaceProcessThreadTokens = RTL_CONSTANT_STRING("SaferiReplaceProcessThreadTokens");
+
+ /* Enter the application certification lock */
+ RtlEnterCriticalSection(&gcsAppCert);
+
+ /* Check if we already know the function */
+ if (g_SaferReplaceProcessThreadTokens)
+ {
+ /* Call it */
+ Status = g_SaferReplaceProcessThreadTokens(TokenHandle,
+ ProcessHandle,
+ ThreadHandle) ?
+ STATUS_SUCCESS :
+ STATUS_UNSUCCESSFUL;
+ }
+ else
+ {
+ /* Check if the app certification DLL isn't loaded */
+ if (!(gSaferHandle) ||
+ (gSaferHandle == (HMODULE)-1) ||
+ (gSaferHandle == (HMODULE)-2))
+ {
+ /* Then we can't call the function */
+ Status = STATUS_ENTRYPOINT_NOT_FOUND;
+ }
+ else
+ {
+ /* We have the DLL, find the address of the Safer function */
+ Status = LdrGetProcedureAddress(gSaferHandle,
+ &SaferiReplaceProcessThreadTokens,
+ 0,
+ (PVOID*)&g_SaferReplaceProcessThreadTokens);
+ if (NT_SUCCESS(Status))
+ {
+ /* Found it, now call it */
+ Status = g_SaferReplaceProcessThreadTokens(TokenHandle,
+ ProcessHandle,
+ ThreadHandle) ?
+ STATUS_SUCCESS :
+ STATUS_UNSUCCESSFUL;
+ }
+ else
+ {
+ /* We couldn't find it, so this must be an unsupported DLL */
+ LdrUnloadDll(gSaferHandle);
+ gSaferHandle = NULL;
+ Status = STATUS_ENTRYPOINT_NOT_FOUND;
+ }
+ }
+ }
+
+ /* Release the lock and return the result */
+ RtlLeaveCriticalSection(&gcsAppCert);
+ return Status;
+}
+
+VOID
+WINAPI
+BasepSxsCloseHandles(IN PBASE_MSG_SXS_HANDLES Handles)
+{
+ NTSTATUS Status;
+
+ /* Sanity checks */
+ ASSERT(Handles != NULL);
+ ASSERT(Handles->Process == NULL || Handles->Process == NtCurrentProcess());
+
+ /* Close the file handle */
+ if (Handles->File)
+ {
+ Status = NtClose(Handles->File);
+ ASSERT(NT_SUCCESS(Status));
+ }
+
+ /* Close the section handle */
+ if (Handles->Section)
+ {
+ Status = NtClose(Handles->Section);
+ ASSERT(NT_SUCCESS(Status));
+ }
+
+ /* Unmap the section view */
+ if (Handles->ViewBase.QuadPart)
+ {
+ Status = NtUnmapViewOfSection(NtCurrentProcess(),
+ (PVOID)Handles->ViewBase.LowPart);
+ ASSERT(NT_SUCCESS(Status));
+ }
+}
+
+static
+LONG BaseExceptionFilter(EXCEPTION_POINTERS *ExceptionInfo)
+{
+ LONG ExceptionDisposition = EXCEPTION_EXECUTE_HANDLER;
+ LPTOP_LEVEL_EXCEPTION_FILTER RealFilter;
+ RealFilter = RtlDecodePointer(GlobalTopLevelExceptionFilter);
+
+ if (RealFilter != NULL)
+ {
+ _SEH2_TRY
+ {
+ ExceptionDisposition = RealFilter(ExceptionInfo);
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ }
+ _SEH2_END;
+ }
+ if ((ExceptionDisposition == EXCEPTION_CONTINUE_SEARCH || ExceptionDisposition == EXCEPTION_EXECUTE_HANDLER) &&
+ RealFilter != UnhandledExceptionFilter)
+ {
+ ExceptionDisposition = UnhandledExceptionFilter(ExceptionInfo);
+ }
+
+ return ExceptionDisposition;
+}
+
+VOID
+WINAPI
+BaseProcessStartup(PPROCESS_START_ROUTINE lpStartAddress)
+{
+ DPRINT("BaseProcessStartup(..) - setting up exception frame.\n");
+
+ _SEH2_TRY
+ {
+ /* Set our Start Address */
+ NtSetInformationThread(NtCurrentThread(),
+ ThreadQuerySetWin32StartAddress,
+ &lpStartAddress,
+ sizeof(PPROCESS_START_ROUTINE));
+
+ /* Call the Start Routine */
+ ExitThread(lpStartAddress());
+ }
+ _SEH2_EXCEPT(BaseExceptionFilter(_SEH2_GetExceptionInformation()))
+ {
+ /* Get the Exit code from the SEH Handler */
+ if (!BaseRunningInServerProcess)
+ {
+ /* Kill the whole process, usually */
+ ExitProcess(_SEH2_GetExceptionCode());
+ }
+ else
+ {
+ /* If running inside CSRSS, kill just this thread */
+ ExitThread(_SEH2_GetExceptionCode());
+ }
+ }
+ _SEH2_END;
+}
+
+NTSTATUS
+WINAPI
+BasepNotifyCsrOfThread(IN HANDLE ThreadHandle,
+ IN PCLIENT_ID ClientId)
+{
+ NTSTATUS Status;
+ BASE_API_MESSAGE ApiMessage;
+ PBASE_CREATE_THREAD CreateThreadRequest = &ApiMessage.Data.CreateThreadRequest;
+
+ DPRINT("BasepNotifyCsrOfThread: Thread: %lx, Handle %lx\n",
+ ClientId->UniqueThread, ThreadHandle);
+
+ /* Fill out the request */
+ CreateThreadRequest->ClientId = *ClientId;
+ CreateThreadRequest->ThreadHandle = ThreadHandle;
+
+ /* Call CSR */
+ Status = CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
+ NULL,
+ CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepCreateThread),
+ sizeof(BASE_CREATE_THREAD));
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT1("Failed to tell CSRSS about new thread: %lx\n", Status);
+ return Status;
+ }
+
+ /* Return Success */
+ return STATUS_SUCCESS;
+}
+
- /* See if the first 1MB should be reserved */
+BOOLEAN
+WINAPI
+BasePushProcessParameters(IN ULONG ParameterFlags,
+ IN HANDLE ProcessHandle,
+ IN PPEB RemotePeb,
+ IN LPCWSTR ApplicationPathName,
+ IN LPWSTR lpCurrentDirectory,
+ IN LPWSTR lpCommandLine,
+ IN LPVOID lpEnvironment,
+ IN LPSTARTUPINFOW StartupInfo,
+ IN DWORD CreationFlags,
+ IN BOOL InheritHandles,
+ IN ULONG ImageSubsystem,
+ IN PVOID AppCompatData,
+ IN ULONG AppCompatDataSize)
+{
+ WCHAR FullPath[MAX_PATH + 5];
+ PWCHAR Remaining, DllPathString, ScanChar;
+ PRTL_USER_PROCESS_PARAMETERS ProcessParameters, RemoteParameters;
+ PVOID RemoteAppCompatData;
+ UNICODE_STRING DllPath, ImageName, CommandLine, CurrentDirectory;
+ UNICODE_STRING Desktop, Shell, Runtime, Title;
+ NTSTATUS Status;
+ ULONG EnviroSize;
+ SIZE_T Size;
+ BOOLEAN HavePebLock = FALSE, Result;
+ PPEB Peb = NtCurrentPeb();
+
+ /* Get the full path name */
+ Size = GetFullPathNameW(ApplicationPathName,
+ MAX_PATH + 4,
+ FullPath,
+ &Remaining);
+ if ((Size) && (Size <= (MAX_PATH + 4)))
+ {
+ /* Get the DLL Path */
+ DllPathString = BaseComputeProcessDllPath(FullPath, lpEnvironment);
+ if (!DllPathString)
+ {
+ /* Fail */
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ return FALSE;
+ }
+
+ /* Initialize Strings */
+ RtlInitUnicodeString(&DllPath, DllPathString);
+ RtlInitUnicodeString(&ImageName, FullPath);
+ }
+ else
+ {
+ /* Couldn't get the path name. Just take the original path */
+ DllPathString = BaseComputeProcessDllPath((LPWSTR)ApplicationPathName,
+ lpEnvironment);
+ if (!DllPathString)
+ {
+ /* Fail */
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ return FALSE;
+ }
+
+ /* Initialize Strings */
+ RtlInitUnicodeString(&DllPath, DllPathString);
+ RtlInitUnicodeString(&ImageName, ApplicationPathName);
+ }
+
+ /* Initialize Strings */
+ RtlInitUnicodeString(&CommandLine, lpCommandLine);
+ RtlInitUnicodeString(&CurrentDirectory, lpCurrentDirectory);
+
+ /* Initialize more Strings from the Startup Info */
+ if (StartupInfo->lpDesktop)
+ {
+ RtlInitUnicodeString(&Desktop, StartupInfo->lpDesktop);
+ }
+ else
+ {
+ RtlInitUnicodeString(&Desktop, L"");
+ }
+ if (StartupInfo->lpReserved)
+ {
+ RtlInitUnicodeString(&Shell, StartupInfo->lpReserved);
+ }
+ else
+ {
+ RtlInitUnicodeString(&Shell, L"");
+ }
+ if (StartupInfo->lpTitle)
+ {
+ RtlInitUnicodeString(&Title, StartupInfo->lpTitle);
+ }
+ else
+ {
+ RtlInitUnicodeString(&Title, ApplicationPathName);
+ }
+
+ /* This one is special because the length can differ */
+ Runtime.Buffer = (LPWSTR)StartupInfo->lpReserved2;
+ Runtime.MaximumLength = Runtime.Length = StartupInfo->cbReserved2;
+
+ /* Enforce no app compat data if the pointer was NULL */
+ if (!AppCompatData) AppCompatDataSize = 0;
+
+ /* Create the Parameter Block */
+ ProcessParameters = NULL;
++ DPRINT1("Image Name: %wZ Dll Path: %wZ current directory: %wZ, CmdLine: %wZ, Title: %wZ, Desktop: %wZ, Shell: %wZ, Runtime: %wZ\n",
++ &ImageName, &DllPath, &CurrentDirectory, &CommandLine, &Title, &Desktop, &Shell, &Runtime);
+ Status = RtlCreateProcessParameters(&ProcessParameters,
+ &ImageName,
+ &DllPath,
+ lpCurrentDirectory ?
+ &CurrentDirectory : NULL,
+ &CommandLine,
+ lpEnvironment,
+ &Title,
+ &Desktop,
+ &Shell,
+ &Runtime);
+ if (!NT_SUCCESS(Status)) goto FailPath;
+
+ /* Clear the current directory handle if not inheriting */
+ if (!InheritHandles) ProcessParameters->CurrentDirectory.Handle = NULL;
+
+ /* Check if the user passed in an environment */
+ if (lpEnvironment)
+ {
+ /* We should've made it part of the parameters block, enforce this */
+ ASSERT(ProcessParameters->Environment == lpEnvironment);
+ lpEnvironment = ProcessParameters->Environment;
+ }
+ else
+ {
+ /* The user did not, so use the one from the current PEB */
+ HavePebLock = TRUE;
+ RtlAcquirePebLock();
+ lpEnvironment = Peb->ProcessParameters->Environment;
+ }
+
+ /* Save pointer and start lookup */
+ ScanChar = lpEnvironment;
+ if (lpEnvironment)
+ {
+ /* Find the environment size */
+ while ((ScanChar[0]) || (ScanChar[1])) ++ScanChar;
+ ScanChar += (2 * sizeof(UNICODE_NULL));
+ EnviroSize = (ULONG_PTR)ScanChar - (ULONG_PTR)lpEnvironment;
+
+ /* Allocate and Initialize new Environment Block */
+ Size = EnviroSize;
+ ProcessParameters->Environment = NULL;
+ Status = NtAllocateVirtualMemory(ProcessHandle,
+ (PVOID*)&ProcessParameters->Environment,
+ 0,
+ &Size,
+ MEM_COMMIT,
+ PAGE_READWRITE);
+ if (!NT_SUCCESS(Status)) goto FailPath;
+
+ /* Write the Environment Block */
+ Status = NtWriteVirtualMemory(ProcessHandle,
+ ProcessParameters->Environment,
+ lpEnvironment,
+ EnviroSize,
+ NULL);
+
+ /* No longer need the PEB lock anymore */
+ if (HavePebLock)
+ {
+ /* Release it */
+ RtlReleasePebLock();
+ HavePebLock = FALSE;
+ }
+
+ /* Check if the write failed */
+ if (!NT_SUCCESS(Status)) goto FailPath;
+ }
+
+ /* Write new parameters */
+ ProcessParameters->StartingX = StartupInfo->dwX;
+ ProcessParameters->StartingY = StartupInfo->dwY;
+ ProcessParameters->CountX = StartupInfo->dwXSize;
+ ProcessParameters->CountY = StartupInfo->dwYSize;
+ ProcessParameters->CountCharsX = StartupInfo->dwXCountChars;
+ ProcessParameters->CountCharsY = StartupInfo->dwYCountChars;
+ ProcessParameters->FillAttribute = StartupInfo->dwFillAttribute;
+ ProcessParameters->WindowFlags = StartupInfo->dwFlags;
+ ProcessParameters->ShowWindowFlags = StartupInfo->wShowWindow;
+
+ /* Write the handles only if we have to */
+ if (StartupInfo->dwFlags &
+ (STARTF_USESTDHANDLES | STARTF_USEHOTKEY | STARTF_SHELLPRIVATE))
+ {
+ ProcessParameters->StandardInput = StartupInfo->hStdInput;
+ ProcessParameters->StandardOutput = StartupInfo->hStdOutput;
+ ProcessParameters->StandardError = StartupInfo->hStdError;
+ }
+
+ /* Use Special Flags for BasepInitConsole in Kernel32 */
+ if (CreationFlags & DETACHED_PROCESS)
+ {
+ ProcessParameters->ConsoleHandle = HANDLE_DETACHED_PROCESS;
+ }
+ else if (CreationFlags & CREATE_NEW_CONSOLE)
+ {
+ ProcessParameters->ConsoleHandle = HANDLE_CREATE_NEW_CONSOLE;
+ }
+ else if (CreationFlags & CREATE_NO_WINDOW)
+ {
+ ProcessParameters->ConsoleHandle = HANDLE_CREATE_NO_WINDOW;
+ }
+ else
+ {
+ /* Inherit our Console Handle */
+ ProcessParameters->ConsoleHandle = Peb->ProcessParameters->ConsoleHandle;
+
+ /* Make sure that the shell isn't trampling on our handles first */
+ if (!(StartupInfo->dwFlags &
+ (STARTF_USESTDHANDLES | STARTF_USEHOTKEY | STARTF_SHELLPRIVATE)))
+ {
+ /* Copy the handle if we are inheriting or if it's a console handle */
+ if ((InheritHandles) ||
+ (IsConsoleHandle(Peb->ProcessParameters->StandardInput)))
+ {
+ ProcessParameters->StandardInput = Peb->ProcessParameters->StandardInput;
+ }
+ if ((InheritHandles) ||
+ (IsConsoleHandle(Peb->ProcessParameters->StandardOutput)))
+ {
+ ProcessParameters->StandardOutput = Peb->ProcessParameters->StandardOutput;
+ }
+ if ((InheritHandles) ||
+ (IsConsoleHandle(Peb->ProcessParameters->StandardError)))
+ {
+ ProcessParameters->StandardError = Peb->ProcessParameters->StandardError;
+ }
+ }
+ }
+
+ /* Also set the Console Flag */
+ if ((CreationFlags & CREATE_NEW_PROCESS_GROUP) &&
+ (!(CreationFlags & CREATE_NEW_CONSOLE)))
+ {
+ ProcessParameters->ConsoleFlags = 1;
+ }
+
- ProcessParameters->Flags |= RTL_USER_PROCESS_PARAMETERS_RESERVE_1MB;
++ /* Check if there's a .local file present */
+ if (ParameterFlags & 1)
+ {
- /* See if the first 16MB should be reserved */
++ ProcessParameters->Flags |= RTL_USER_PROCESS_PARAMETERS_LOCAL_DLL_PATH;
+ }
+
- ProcessParameters->Flags |= RTL_USER_PROCESS_PARAMETERS_RESERVE_16MB;
++ /* Check if we failed to open the IFEO key */
+ if (ParameterFlags & 2)
+ {
- DPRINT1("Failure to create proecss parameters: %lx\n", Status);
++ ProcessParameters->Flags |= RTL_USER_PROCESS_PARAMETERS_IMAGE_KEY_MISSING;
+ }
+
+ /* Allocate memory for the parameter block */
+ Size = ProcessParameters->Length;
+ RemoteParameters = NULL;
+ Status = NtAllocateVirtualMemory(ProcessHandle,
+ (PVOID*)&RemoteParameters,
+ 0,
+ &Size,
+ MEM_COMMIT,
+ PAGE_READWRITE);
+ if (!NT_SUCCESS(Status)) goto FailPath;
+
+ /* Set the allocated size */
+ ProcessParameters->MaximumLength = Size;
+
+ /* Handle some Parameter Flags */
+ ProcessParameters->Flags |= (CreationFlags & PROFILE_USER) ?
+ RTL_USER_PROCESS_PARAMETERS_PROFILE_USER : 0;
+ ProcessParameters->Flags |= (CreationFlags & PROFILE_KERNEL) ?
+ RTL_USER_PROCESS_PARAMETERS_PROFILE_KERNEL : 0;
+ ProcessParameters->Flags |= (CreationFlags & PROFILE_SERVER) ?
+ RTL_USER_PROCESS_PARAMETERS_PROFILE_SERVER : 0;
+ ProcessParameters->Flags |= (Peb->ProcessParameters->Flags &
+ RTL_USER_PROCESS_PARAMETERS_DISABLE_HEAP_CHECKS);
+
+ /* Write the Parameter Block */
+ Status = NtWriteVirtualMemory(ProcessHandle,
+ RemoteParameters,
+ ProcessParameters,
+ ProcessParameters->Length,
+ NULL);
+ if (!NT_SUCCESS(Status)) goto FailPath;
+
+ /* Write the PEB Pointer */
+ Status = NtWriteVirtualMemory(ProcessHandle,
+ &RemotePeb->ProcessParameters,
+ &RemoteParameters,
+ sizeof(PVOID),
+ NULL);
+ if (!NT_SUCCESS(Status)) goto FailPath;
+
+ /* Check if there's any app compat data to write */
+ RemoteAppCompatData = NULL;
+ if (AppCompatData)
+ {
+ /* Allocate some space for the application compatibility data */
+ Size = AppCompatDataSize;
+ Status = NtAllocateVirtualMemory(ProcessHandle,
+ &RemoteAppCompatData,
+ 0,
+ &Size,
+ MEM_COMMIT,
+ PAGE_READWRITE);
+ if (!NT_SUCCESS(Status)) goto FailPath;
+
+ /* Write the application compatibility data */
+ Status = NtWriteVirtualMemory(ProcessHandle,
+ RemoteAppCompatData,
+ AppCompatData,
+ AppCompatDataSize,
+ NULL);
+ if (!NT_SUCCESS(Status)) goto FailPath;
+ }
+
+ /* Write the PEB Pointer to the app compat data (might be NULL) */
+ Status = NtWriteVirtualMemory(ProcessHandle,
+ &RemotePeb->pShimData,
+ &RemoteAppCompatData,
+ sizeof(PVOID),
+ NULL);
+ if (!NT_SUCCESS(Status)) goto FailPath;
+
+ /* Now write Peb->ImageSubSystem */
+ if (ImageSubsystem)
+ {
+ NtWriteVirtualMemory(ProcessHandle,
+ &RemotePeb->ImageSubsystem,
+ &ImageSubsystem,
+ sizeof(ImageSubsystem),
+ NULL);
+ }
+
+ /* Success path */
+ Result = TRUE;
+
+Quickie:
+ /* Cleanup */
+ if (HavePebLock) RtlReleasePebLock();
+ RtlFreeHeap(RtlGetProcessHeap(), 0, DllPath.Buffer);
+ if (ProcessParameters) RtlDestroyProcessParameters(ProcessParameters);
+ return Result;
+FailPath:
- CreateProcessInternalW(HANDLE hToken,
- LPCWSTR lpApplicationName,
- LPWSTR lpCommandLine,
- LPSECURITY_ATTRIBUTES lpProcessAttributes,
- LPSECURITY_ATTRIBUTES lpThreadAttributes,
- BOOL bInheritHandles,
- DWORD dwCreationFlags,
- LPVOID lpEnvironment,
- LPCWSTR lpCurrentDirectory,
- LPSTARTUPINFOW lpStartupInfo,
- LPPROCESS_INFORMATION lpProcessInformation,
- PHANDLE hNewToken)
++ DPRINT1("Failure to create process parameters: %lx\n", Status);
+ BaseSetLastNTError(Status);
+ Result = FALSE;
+ goto Quickie;
+}
+
+VOID
+WINAPI
+InitCommandLines(VOID)
+{
+ NTSTATUS Status;
+
+ /* Read the UNICODE_STRING from the PEB */
+ BaseUnicodeCommandLine = NtCurrentPeb()->ProcessParameters->CommandLine;
+
+ /* Convert to ANSI_STRING for the *A callers */
+ Status = RtlUnicodeStringToAnsiString(&BaseAnsiCommandLine,
+ &BaseUnicodeCommandLine,
+ TRUE);
+ if (!NT_SUCCESS(Status)) RtlInitEmptyAnsiString(&BaseAnsiCommandLine, 0, 0);
+}
+
+/* PUBLIC FUNCTIONS ***********************************************************/
+
+/*
+ * @implemented
+ */
+BOOL
+WINAPI
+GetProcessAffinityMask(IN HANDLE hProcess,
+ OUT PDWORD_PTR lpProcessAffinityMask,
+ OUT PDWORD_PTR lpSystemAffinityMask)
+{
+ PROCESS_BASIC_INFORMATION ProcessInfo;
+ NTSTATUS Status;
+
+ /* Query information on the process from the kernel */
+ Status = NtQueryInformationProcess(hProcess,
+ ProcessBasicInformation,
+ (PVOID)&ProcessInfo,
+ sizeof(PROCESS_BASIC_INFORMATION),
+ NULL);
+ if (!NT_SUCCESS(Status))
+ {
+ /* Fail */
+ BaseSetLastNTError(Status);
+ return FALSE;
+ }
+
+ /* Copy the affinity mask, and get the system one from our shared data */
+ *lpProcessAffinityMask = (DWORD)ProcessInfo.AffinityMask;
+ *lpSystemAffinityMask = (DWORD)BaseStaticServerData->SysInfo.ActiveProcessorsAffinityMask;
+ return TRUE;
+}
+
+/*
+ * @implemented
+ */
+BOOL
+WINAPI
+SetProcessAffinityMask(IN HANDLE hProcess,
+ IN DWORD_PTR dwProcessAffinityMask)
+{
+ NTSTATUS Status;
+
+ /* Directly set the affinity mask */
+ Status = NtSetInformationProcess(hProcess,
+ ProcessAffinityMask,
+ (PVOID)&dwProcessAffinityMask,
+ sizeof(DWORD));
+ if (!NT_SUCCESS(Status))
+ {
+ /* Handle failure */
+ BaseSetLastNTError(Status);
+ return FALSE;
+ }
+
+ /* Everything was ok */
+ return TRUE;
+}
+
+/*
+ * @implemented
+ */
+BOOL
+WINAPI
+GetProcessShutdownParameters(OUT LPDWORD lpdwLevel,
+ OUT LPDWORD lpdwFlags)
+{
+ NTSTATUS Status;
+ BASE_API_MESSAGE ApiMessage;
+ PBASE_GET_PROCESS_SHUTDOWN_PARAMS GetShutdownParametersRequest = &ApiMessage.Data.GetShutdownParametersRequest;
+
+ /* Ask CSRSS for shutdown information */
+ Status = CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
+ NULL,
+ CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepGetProcessShutdownParam),
+ sizeof(BASE_GET_PROCESS_SHUTDOWN_PARAMS));
+ if (!NT_SUCCESS(Status))
+ {
+ /* Return the failure from CSRSS */
+ BaseSetLastNTError(Status);
+ return FALSE;
+ }
+
+ /* Get the data back */
+ *lpdwLevel = GetShutdownParametersRequest->Level;
+ *lpdwFlags = GetShutdownParametersRequest->Flags;
+ return TRUE;
+}
+
+/*
+ * @implemented
+ */
+BOOL
+WINAPI
+SetProcessShutdownParameters(IN DWORD dwLevel,
+ IN DWORD dwFlags)
+{
+ NTSTATUS Status;
+ BASE_API_MESSAGE ApiMessage;
+ PBASE_SET_PROCESS_SHUTDOWN_PARAMS SetShutdownParametersRequest = &ApiMessage.Data.SetShutdownParametersRequest;
+
+ /* Write the data into the CSRSS request and send it */
+ SetShutdownParametersRequest->Level = dwLevel;
+ SetShutdownParametersRequest->Flags = dwFlags;
+ Status = CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
+ NULL,
+ CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepSetProcessShutdownParam),
+ sizeof(BASE_SET_PROCESS_SHUTDOWN_PARAMS));
+ if (!NT_SUCCESS(Status))
+ {
+ /* Return the failure from CSRSS */
+ BaseSetLastNTError(Status);
+ return FALSE;
+ }
+
+ /* All went well */
+ return TRUE;
+}
+
+/*
+ * @implemented
+ */
+BOOL
+WINAPI
+GetProcessWorkingSetSizeEx(IN HANDLE hProcess,
+ OUT PSIZE_T lpMinimumWorkingSetSize,
+ OUT PSIZE_T lpMaximumWorkingSetSize,
+ OUT PDWORD Flags)
+{
+ QUOTA_LIMITS_EX QuotaLimits;
+ NTSTATUS Status;
+
+ /* Query the kernel about this */
+ Status = NtQueryInformationProcess(hProcess,
+ ProcessQuotaLimits,
+ &QuotaLimits,
+ sizeof(QUOTA_LIMITS_EX),
+ NULL);
+ if (!NT_SUCCESS(Status))
+ {
+ /* Return error */
+ BaseSetLastNTError(Status);
+ return FALSE;
+ }
+
+ /* Copy the quota information out */
+ *lpMinimumWorkingSetSize = QuotaLimits.MinimumWorkingSetSize;
+ *lpMaximumWorkingSetSize = QuotaLimits.MaximumWorkingSetSize;
+ *Flags = QuotaLimits.Flags;
+ return TRUE;
+}
+
+/*
+ * @implemented
+ */
+BOOL
+WINAPI
+GetProcessWorkingSetSize(IN HANDLE hProcess,
+ OUT PSIZE_T lpMinimumWorkingSetSize,
+ OUT PSIZE_T lpMaximumWorkingSetSize)
+{
+ DWORD Dummy;
+ return GetProcessWorkingSetSizeEx(hProcess,
+ lpMinimumWorkingSetSize,
+ lpMaximumWorkingSetSize,
+ &Dummy);
+}
+
+/*
+ * @implemented
+ */
+BOOL
+WINAPI
+SetProcessWorkingSetSizeEx(IN HANDLE hProcess,
+ IN SIZE_T dwMinimumWorkingSetSize,
+ IN SIZE_T dwMaximumWorkingSetSize,
+ IN DWORD Flags)
+{
+ QUOTA_LIMITS_EX QuotaLimits;
+ NTSTATUS Status, ReturnStatus;
+ BOOL Result;
+ PVOID State;
+ ULONG Privilege = SE_INC_BASE_PRIORITY_PRIVILEGE;
+
+ /* Zero out the input structure */
+ RtlZeroMemory(&QuotaLimits, sizeof(QuotaLimits));
+
+ /* Check if the caller sent any limits */
+ if ((dwMinimumWorkingSetSize) && (dwMaximumWorkingSetSize))
+ {
+ /* Write the quota information */
+ QuotaLimits.MinimumWorkingSetSize = dwMinimumWorkingSetSize;
+ QuotaLimits.MaximumWorkingSetSize = dwMaximumWorkingSetSize;
+ QuotaLimits.Flags = Flags;
+
+ /* Acquire the required privilege */
+ Status = RtlAcquirePrivilege(&Privilege, 1, 0, &State);
+
+ /* Request the new quotas */
+ ReturnStatus = NtSetInformationProcess(hProcess,
+ ProcessQuotaLimits,
+ &QuotaLimits,
+ sizeof(QuotaLimits));
+ Result = NT_SUCCESS(ReturnStatus);
+ if (NT_SUCCESS(Status))
+ {
+ /* Release the privilege and set succes code */
+ ASSERT(State != NULL);
+ RtlReleasePrivilege(State);
+ State = NULL;
+ }
+ }
+ else
+ {
+ /* No limits, fail the call */
+ ReturnStatus = STATUS_INVALID_PARAMETER;
+ Result = FALSE;
+ }
+
+ /* Return result code, set error code if this was a failure */
+ if (!Result) BaseSetLastNTError(ReturnStatus);
+ return Result;
+}
+
+/*
+ * @implemented
+ */
+BOOL
+WINAPI
+SetProcessWorkingSetSize(IN HANDLE hProcess,
+ IN SIZE_T dwMinimumWorkingSetSize,
+ IN SIZE_T dwMaximumWorkingSetSize)
+{
+ /* Call the newer API */
+ return SetProcessWorkingSetSizeEx(hProcess,
+ dwMinimumWorkingSetSize,
+ dwMaximumWorkingSetSize,
+ 0);
+}
+
+/*
+ * @implemented
+ */
+BOOL
+WINAPI
+GetProcessTimes(IN HANDLE hProcess,
+ IN LPFILETIME lpCreationTime,
+ IN LPFILETIME lpExitTime,
+ IN LPFILETIME lpKernelTime,
+ IN LPFILETIME lpUserTime)
+{
+ KERNEL_USER_TIMES Kut;
+ NTSTATUS Status;
+
+ /* Query the times */
+ Status = NtQueryInformationProcess(hProcess,
+ ProcessTimes,
+ &Kut,
+ sizeof(Kut),
+ NULL);
+ if (!NT_SUCCESS(Status))
+ {
+ /* Handle failure */
+ BaseSetLastNTError(Status);
+ return FALSE;
+ }
+
+ /* Copy all the times and return success */
+ lpCreationTime->dwLowDateTime = Kut.CreateTime.u.LowPart;
+ lpCreationTime->dwHighDateTime = Kut.CreateTime.u.HighPart;
+ lpExitTime->dwLowDateTime = Kut.ExitTime.u.LowPart;
+ lpExitTime->dwHighDateTime = Kut.ExitTime.u.HighPart;
+ lpKernelTime->dwLowDateTime = Kut.KernelTime.u.LowPart;
+ lpKernelTime->dwHighDateTime = Kut.KernelTime.u.HighPart;
+ lpUserTime->dwLowDateTime = Kut.UserTime.u.LowPart;
+ lpUserTime->dwHighDateTime = Kut.UserTime.u.HighPart;
+ return TRUE;
+}
+
+/*
+ * @implemented
+ */
+HANDLE
+WINAPI
+GetCurrentProcess(VOID)
+{
+ return (HANDLE)NtCurrentProcess();
+}
+
+/*
+ * @implemented
+ */
+HANDLE
+WINAPI
+GetCurrentThread(VOID)
+{
+ return (HANDLE)NtCurrentThread();
+}
+
+/*
+ * @implemented
+ */
+DWORD
+WINAPI
+GetCurrentProcessId(VOID)
+{
+ return HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess);
+}
+
+/*
+ * @implemented
+ */
+BOOL
+WINAPI
+GetExitCodeProcess(IN HANDLE hProcess,
+ IN LPDWORD lpExitCode)
+{
+ PROCESS_BASIC_INFORMATION ProcessBasic;
+ NTSTATUS Status;
+
+ /* Ask the kernel */
+ Status = NtQueryInformationProcess(hProcess,
+ ProcessBasicInformation,
+ &ProcessBasic,
+ sizeof(PROCESS_BASIC_INFORMATION),
+ NULL);
+ if (!NT_SUCCESS(Status))
+ {
+ /* We failed, was this because this is a VDM process? */
+ if (BaseCheckForVDM(hProcess, lpExitCode) == TRUE) return TRUE;
+
+ /* Not a VDM process, fail the call */
+ BaseSetLastNTError(Status);
+ return FALSE;
+ }
+
+ /* Succes case, return the exit code */
+ *lpExitCode = (DWORD)ProcessBasic.ExitStatus;
+ return TRUE;
+}
+
+/*
+ * @implemented
+ */
+DWORD
+WINAPI
+GetProcessId(IN HANDLE Process)
+{
+ PROCESS_BASIC_INFORMATION ProcessBasic;
+ NTSTATUS Status;
+
+ /* Query the kernel */
+ Status = NtQueryInformationProcess(Process,
+ ProcessBasicInformation,
+ &ProcessBasic,
+ sizeof(PROCESS_BASIC_INFORMATION),
+ NULL);
+ if (!NT_SUCCESS(Status))
+ {
+ /* Handle failure */
+ BaseSetLastNTError(Status);
+ return 0;
+ }
+
+ /* Return the PID */
+ return (DWORD)ProcessBasic.UniqueProcessId;
+}
+
+/*
+ * @implemented
+ */
+HANDLE
+WINAPI
+OpenProcess(IN DWORD dwDesiredAccess,
+ IN BOOL bInheritHandle,
+ IN DWORD dwProcessId)
+{
+ NTSTATUS Status;
+ HANDLE ProcessHandle;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ CLIENT_ID ClientId;
+
+ /* Setup the input client ID structure */
+ ClientId.UniqueProcess = UlongToHandle(dwProcessId);
+ ClientId.UniqueThread = 0;
+
+ /* This is needed just to define the inheritance flags */
+ InitializeObjectAttributes(&ObjectAttributes,
+ NULL,
+ (bInheritHandle ? OBJ_INHERIT : 0),
+ NULL,
+ NULL);
+
+ /* Now try to open the process */
+ Status = NtOpenProcess(&ProcessHandle,
+ dwDesiredAccess,
+ &ObjectAttributes,
+ &ClientId);
+ if (!NT_SUCCESS(Status))
+ {
+ /* Handle failure */
+ BaseSetLastNTError(Status);
+ return NULL;
+ }
+
+ /* Otherwise return a handle to the process */
+ return ProcessHandle;
+}
+
+/*
+ * @implemented
+ */
+VOID
+WINAPI
+RegisterWaitForInputIdle(IN WaitForInputIdleType lpfnRegisterWaitForInputIdle)
+{
+ /* Write the global function pointer */
+ UserWaitForInputIdleRoutine = lpfnRegisterWaitForInputIdle;
+}
+
+/*
+ * @implemented
+ */
+VOID
+WINAPI
+GetStartupInfoW(IN LPSTARTUPINFOW lpStartupInfo)
+{
+ PRTL_USER_PROCESS_PARAMETERS Params;
+
+ /* Get the process parameters */
+ Params = NtCurrentPeb()->ProcessParameters;
+
+ /* Copy the data out of there */
+ lpStartupInfo->cb = sizeof(STARTUPINFOW);
+ lpStartupInfo->lpReserved = Params->ShellInfo.Buffer;
+ lpStartupInfo->lpDesktop = Params->DesktopInfo.Buffer;
+ lpStartupInfo->lpTitle = Params->WindowTitle.Buffer;
+ lpStartupInfo->dwX = Params->StartingX;
+ lpStartupInfo->dwY = Params->StartingY;
+ lpStartupInfo->dwXSize = Params->CountX;
+ lpStartupInfo->dwYSize = Params->CountY;
+ lpStartupInfo->dwXCountChars = Params->CountCharsX;
+ lpStartupInfo->dwYCountChars = Params->CountCharsY;
+ lpStartupInfo->dwFillAttribute = Params->FillAttribute;
+ lpStartupInfo->dwFlags = Params->WindowFlags;
+ lpStartupInfo->wShowWindow = (WORD)Params->ShowWindowFlags;
+ lpStartupInfo->cbReserved2 = Params->RuntimeData.Length;
+ lpStartupInfo->lpReserved2 = (LPBYTE)Params->RuntimeData.Buffer;
+
+ /* Check if the standard handles are being used for other features */
+ if (lpStartupInfo->dwFlags & (STARTF_USESTDHANDLES |
+ STARTF_USEHOTKEY |
+ STARTF_SHELLPRIVATE))
+ {
+ /* These are, so copy the standard handles too */
+ lpStartupInfo->hStdInput = Params->StandardInput;
+ lpStartupInfo->hStdOutput = Params->StandardOutput;
+ lpStartupInfo->hStdError = Params->StandardError;
+ }
+}
+
+/*
+ * @implemented
+ */
+VOID
+WINAPI
+GetStartupInfoA(IN LPSTARTUPINFOA lpStartupInfo)
+{
+ PRTL_USER_PROCESS_PARAMETERS Params;
+ ANSI_STRING TitleString, ShellString, DesktopString;
+ LPSTARTUPINFOA StartupInfo;
+ NTSTATUS Status;
+
+ /* Get the cached information as well as the PEB parameters */
+ StartupInfo = BaseAnsiStartupInfo;
+ Params = NtCurrentPeb()->ProcessParameters;
+
+ /* Check if this is the first time we have to get the cached version */
+ while (!StartupInfo)
+ {
+ /* Create new ANSI startup info */
+ StartupInfo = RtlAllocateHeap(RtlGetProcessHeap(),
+ 0,
+ sizeof(*StartupInfo));
+ if (StartupInfo)
+ {
+ /* Zero out string pointers in case we fail to create them */
+ StartupInfo->lpReserved = 0;
+ StartupInfo->lpDesktop = 0;
+ StartupInfo->lpTitle = 0;
+
+ /* Set the size */
+ StartupInfo->cb = sizeof(*StartupInfo);
+
+ /* Copy what's already stored in the PEB */
+ StartupInfo->dwX = Params->StartingX;
+ StartupInfo->dwY = Params->StartingY;
+ StartupInfo->dwXSize = Params->CountX;
+ StartupInfo->dwYSize = Params->CountY;
+ StartupInfo->dwXCountChars = Params->CountCharsX;
+ StartupInfo->dwYCountChars = Params->CountCharsY;
+ StartupInfo->dwFillAttribute = Params->FillAttribute;
+ StartupInfo->dwFlags = Params->WindowFlags;
+ StartupInfo->wShowWindow = (WORD)Params->ShowWindowFlags;
+ StartupInfo->cbReserved2 = Params->RuntimeData.Length;
+ StartupInfo->lpReserved2 = (LPBYTE)Params->RuntimeData.Buffer;
+ StartupInfo->hStdInput = Params->StandardInput;
+ StartupInfo->hStdOutput = Params->StandardOutput;
+ StartupInfo->hStdError = Params->StandardError;
+
+ /* Copy shell info string */
+ Status = RtlUnicodeStringToAnsiString(&ShellString,
+ &Params->ShellInfo,
+ TRUE);
+ if (NT_SUCCESS(Status))
+ {
+ /* Save it */
+ StartupInfo->lpReserved = ShellString.Buffer;
+
+ /* Copy desktop info string */
+ Status = RtlUnicodeStringToAnsiString(&DesktopString,
+ &Params->DesktopInfo,
+ TRUE);
+ if (NT_SUCCESS(Status))
+ {
+ /* Save it */
+ StartupInfo->lpDesktop = DesktopString.Buffer;
+
+ /* Copy window title string */
+ Status = RtlUnicodeStringToAnsiString(&TitleString,
+ &Params->WindowTitle,
+ TRUE);
+ if (NT_SUCCESS(Status))
+ {
+ /* Save it */
+ StartupInfo->lpTitle = TitleString.Buffer;
+
+ /* We finished with the ANSI version, try to cache it */
+ if (!InterlockedCompareExchangePointer(&BaseAnsiStartupInfo,
+ StartupInfo,
+ NULL))
+ {
+ /* We were the first thread through, use the data */
+ break;
+ }
+
+ /* Someone beat us to it, use their data instead */
+ StartupInfo = BaseAnsiStartupInfo;
+ Status = STATUS_SUCCESS;
+
+ /* We're going to free our own stuff, but not raise */
+ RtlFreeAnsiString(&TitleString);
+ }
+ RtlFreeAnsiString(&DesktopString);
+ }
+ RtlFreeAnsiString(&ShellString);
+ }
+ RtlFreeHeap(RtlGetProcessHeap(), 0, StartupInfo);
+ }
+ else
+ {
+ /* No memory, fail */
+ Status = STATUS_NO_MEMORY;
+ }
+
+ /* Raise an error unless we got here due to the race condition */
+ if (!NT_SUCCESS(Status)) RtlRaiseStatus(Status);
+ }
+
+ /* Now copy from the cached ANSI version */
+ lpStartupInfo->cb = StartupInfo->cb;
+ lpStartupInfo->lpReserved = StartupInfo->lpReserved;
+ lpStartupInfo->lpDesktop = StartupInfo->lpDesktop;
+ lpStartupInfo->lpTitle = StartupInfo->lpTitle;
+ lpStartupInfo->dwX = StartupInfo->dwX;
+ lpStartupInfo->dwY = StartupInfo->dwY;
+ lpStartupInfo->dwXSize = StartupInfo->dwXSize;
+ lpStartupInfo->dwYSize = StartupInfo->dwYSize;
+ lpStartupInfo->dwXCountChars = StartupInfo->dwXCountChars;
+ lpStartupInfo->dwYCountChars = StartupInfo->dwYCountChars;
+ lpStartupInfo->dwFillAttribute = StartupInfo->dwFillAttribute;
+ lpStartupInfo->dwFlags = StartupInfo->dwFlags;
+ lpStartupInfo->wShowWindow = StartupInfo->wShowWindow;
+ lpStartupInfo->cbReserved2 = StartupInfo->cbReserved2;
+ lpStartupInfo->lpReserved2 = StartupInfo->lpReserved2;
+
+ /* Check if the shell is hijacking the handles for other features */
+ if (lpStartupInfo->dwFlags &
+ (STARTF_USESTDHANDLES | STARTF_USEHOTKEY | STARTF_SHELLPRIVATE))
+ {
+ /* It isn't, so we can return the raw values */
+ lpStartupInfo->hStdInput = StartupInfo->hStdInput;
+ lpStartupInfo->hStdOutput = StartupInfo->hStdOutput;
+ lpStartupInfo->hStdError = StartupInfo->hStdError;
+ }
+ else
+ {
+ /* It is, so make sure nobody uses these as console handles */
+ lpStartupInfo->hStdInput = INVALID_HANDLE_VALUE;
+ lpStartupInfo->hStdOutput = INVALID_HANDLE_VALUE;
+ lpStartupInfo->hStdError = INVALID_HANDLE_VALUE;
+ }
+}
+
+/*
+ * @implemented
+ */
+BOOL
+WINAPI
+FlushInstructionCache(IN HANDLE hProcess,
+ IN LPCVOID lpBaseAddress,
+ IN SIZE_T dwSize)
+{
+ NTSTATUS Status;
+
+ /* Call the native function */
+ Status = NtFlushInstructionCache(hProcess, (PVOID)lpBaseAddress, dwSize);
+ if (!NT_SUCCESS(Status))
+ {
+ /* Handle failure case */
+ BaseSetLastNTError(Status);
+ return FALSE;
+ }
+
+ /* All good */
+ return TRUE;
+}
+
+/*
+ * @implemented
+ */
+VOID
+WINAPI
+ExitProcess(IN UINT uExitCode)
+{
+ BASE_API_MESSAGE ApiMessage;
+ PBASE_EXIT_PROCESS ExitProcessRequest = &ApiMessage.Data.ExitProcessRequest;
+
+ ASSERT(!BaseRunningInServerProcess);
+
+ _SEH2_TRY
+ {
+ /* Acquire the PEB lock */
+ RtlAcquirePebLock();
+
+ /* Kill all the threads */
+ NtTerminateProcess(NULL, 0);
+
+ /* Unload all DLLs */
+ LdrShutdownProcess();
+
+ /* Notify Base Server of process termination */
+ ExitProcessRequest->uExitCode = uExitCode;
+ CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
+ NULL,
+ CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepExitProcess),
+ sizeof(BASE_EXIT_PROCESS));
+
+ /* Now do it again */
+ NtTerminateProcess(NtCurrentProcess(), uExitCode);
+ }
+ _SEH2_FINALLY
+ {
+ /* Release the PEB lock */
+ RtlReleasePebLock();
+ }
+ _SEH2_END;
+
+ /* should never get here */
+ ASSERT(0);
+ while(1);
+}
+
+/*
+ * @implemented
+ */
+BOOL
+WINAPI
+TerminateProcess(IN HANDLE hProcess,
+ IN UINT uExitCode)
+{
+ NTSTATUS Status;
+
+ /* Check if no handle was passed in */
+ if (!hProcess)
+ {
+ /* Set error code */
+ SetLastError(ERROR_INVALID_HANDLE);
+ }
+ else
+ {
+ /* Otherwise, try to terminate the process */
+ Status = NtTerminateProcess(hProcess, uExitCode);
+ if (NT_SUCCESS(Status)) return TRUE;
+
+ /* It failed, convert error code */
+ BaseSetLastNTError(Status);
+ }
+
+ /* This is the failure path */
+ return FALSE;
+}
+
+/*
+ * @implemented
+ */
+VOID
+WINAPI
+FatalAppExitA(UINT uAction,
+ LPCSTR lpMessageText)
+{
+ PUNICODE_STRING MessageTextU;
+ ANSI_STRING MessageText;
+ NTSTATUS Status;
+
+ /* Initialize the string using the static TEB pointer */
+ MessageTextU = &NtCurrentTeb()->StaticUnicodeString;
+ RtlInitAnsiString(&MessageText, (LPSTR)lpMessageText);
+
+ /* Convert to unicode and just exit normally if this failed */
+ Status = RtlAnsiStringToUnicodeString(MessageTextU, &MessageText, FALSE);
+ if (!NT_SUCCESS(Status)) ExitProcess(0);
+
+ /* Call the Wide function */
+ FatalAppExitW(uAction, MessageTextU->Buffer);
+}
+
+/*
+ * @implemented
+ */
+VOID
+WINAPI
+FatalAppExitW(IN UINT uAction,
+ IN LPCWSTR lpMessageText)
+{
+ UNICODE_STRING UnicodeString;
+ ULONG Response;
+ NTSTATUS Status;
+
+ /* Setup the string to print out */
+ RtlInitUnicodeString(&UnicodeString, lpMessageText);
+
+ /* Display the hard error no matter what */
+ Status = NtRaiseHardError(STATUS_FATAL_APP_EXIT | HARDERROR_OVERRIDE_ERRORMODE,
+ 1,
+ 1,
+ (PULONG_PTR)&UnicodeString,
+ OptionOkCancel,
+ &Response);
+
+ /* Give the user a chance to abort */
+ if ((NT_SUCCESS(Status)) && (Response == ResponseCancel)) return;
+
+ /* Otherwise kill the process */
+ ExitProcess(0);
+}
+
+/*
+ * @implemented
+ */
+VOID
+WINAPI
+FatalExit(IN int ExitCode)
+{
+#if DBG
+ /* On Checked builds, Windows gives you a nice little debugger UI */
+ CHAR ch[2];
+ DbgPrint("FatalExit...\n");
+ DbgPrint("\n");
+
+ while (TRUE)
+ {
+ DbgPrompt( "A (Abort), B (Break), I (Ignore)? ", ch, sizeof(ch));
+ switch (ch[0])
+ {
+ case 'B': case 'b':
+ DbgBreakPoint();
+ break;
+
+ case 'A': case 'a':
+ ExitProcess(ExitCode);
+
+ case 'I': case 'i':
+ return;
+ }
+ }
+#endif
+ /* On other builds, just kill the process */
+ ExitProcess(ExitCode);
+}
+
+/*
+ * @implemented
+ */
+DWORD
+WINAPI
+GetPriorityClass(IN HANDLE hProcess)
+{
+ NTSTATUS Status;
+ PROCESS_PRIORITY_CLASS PriorityClass;
+
+ /* Query the kernel */
+ Status = NtQueryInformationProcess(hProcess,
+ ProcessPriorityClass,
+ &PriorityClass,
+ sizeof(PROCESS_PRIORITY_CLASS),
+ NULL);
+ if (NT_SUCCESS(Status))
+ {
+ /* Handle the conversion from NT to Win32 classes */
+ switch (PriorityClass.PriorityClass)
+ {
+ case PROCESS_PRIORITY_CLASS_IDLE: return IDLE_PRIORITY_CLASS;
+ case PROCESS_PRIORITY_CLASS_BELOW_NORMAL: return BELOW_NORMAL_PRIORITY_CLASS;
+ case PROCESS_PRIORITY_CLASS_ABOVE_NORMAL: return ABOVE_NORMAL_PRIORITY_CLASS;
+ case PROCESS_PRIORITY_CLASS_HIGH: return HIGH_PRIORITY_CLASS;
+ case PROCESS_PRIORITY_CLASS_REALTIME: return REALTIME_PRIORITY_CLASS;
+ case PROCESS_PRIORITY_CLASS_NORMAL: default: return NORMAL_PRIORITY_CLASS;
+ }
+ }
+
+ /* Failure path */
+ BaseSetLastNTError(Status);
+ return FALSE;
+}
+
+/*
+ * @implemented
+ */
+BOOL
+WINAPI
+SetPriorityClass(IN HANDLE hProcess,
+ IN DWORD dwPriorityClass)
+{
+ NTSTATUS Status;
+ PVOID State = NULL;
+ PROCESS_PRIORITY_CLASS PriorityClass;
+
+ /* Handle conversion from Win32 to NT priority classes */
+ switch (dwPriorityClass)
+ {
+ case IDLE_PRIORITY_CLASS:
+ PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_IDLE;
+ break;
+
+ case BELOW_NORMAL_PRIORITY_CLASS:
+ PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_BELOW_NORMAL;
+ break;
+
+ case NORMAL_PRIORITY_CLASS:
+ PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_NORMAL;
+ break;
+
+ case ABOVE_NORMAL_PRIORITY_CLASS:
+ PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_ABOVE_NORMAL;
+ break;
+
+ case HIGH_PRIORITY_CLASS:
+ PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_HIGH;
+ break;
+
+ case REALTIME_PRIORITY_CLASS:
+ /* Try to acquire the privilege. If it fails, just use HIGH */
+ State = BasepIsRealtimeAllowed(TRUE);
+ PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_HIGH;
+ PriorityClass.PriorityClass += (State != NULL);
+ break;
+
+ default:
+ /* Unrecognized priority classes don't make it to the kernel */
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+
+ /* Send the request to the kernel, and don't touch the foreground flag */
+ PriorityClass.Foreground = FALSE;
+ Status = NtSetInformationProcess(hProcess,
+ ProcessPriorityClass,
+ &PriorityClass,
+ sizeof(PROCESS_PRIORITY_CLASS));
+
+ /* Release the privilege if we had it */
+ if (State) RtlReleasePrivilege(State);
+ if (!NT_SUCCESS(Status))
+ {
+ /* Handle error path */
+ BaseSetLastNTError(Status);
+ return FALSE;
+ }
+
+ /* All done */
+ return TRUE;
+}
+
+/*
+ * @implemented
+ */
+DWORD
+WINAPI
+GetProcessVersion(IN DWORD ProcessId)
+{
+ DWORD Version = 0;
+ PIMAGE_NT_HEADERS NtHeader;
+ PIMAGE_DOS_HEADER DosHeader;
+ PPEB Peb;
+ PROCESS_BASIC_INFORMATION ProcessBasicInfo;
+ PVOID BaseAddress;
+ ULONG e_lfanew;
+ HANDLE ProcessHandle = NULL;
+ NTSTATUS Status;
+ USHORT VersionData[2];
+ BOOLEAN Result;
+
+ /* We'll be accessing stuff that can fault, so protect everything with SEH */
+ _SEH2_TRY
+ {
+ /* It this an in-process or out-of-process request? */
+ if (!(ProcessId) || (GetCurrentProcessId() == ProcessId))
+ {
+ /* It's in-process, so just read our own header */
+ NtHeader = RtlImageNtHeader(NtCurrentPeb()->ImageBaseAddress);
+ if (!NtHeader)
+ {
+ /* Unable to read the NT header, something is wrong here... */
+ Status = STATUS_INVALID_IMAGE_FORMAT;
+ goto Error;
+ }
+
+ /* Get the version straight out of the NT header */
+ Version = MAKELONG(NtHeader->OptionalHeader.MinorSubsystemVersion,
+ NtHeader->OptionalHeader.MajorSubsystemVersion);
+ }
+ else
+ {
+ /* Out-of-process, so open it */
+ ProcessHandle = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION,
+ FALSE,
+ ProcessId);
+ if (!ProcessHandle) return 0;
+
+ /* Try to find out where its PEB lives */
+ Status = NtQueryInformationProcess(ProcessHandle,
+ ProcessBasicInformation,
+ &ProcessBasicInfo,
+ sizeof(ProcessBasicInfo),
+ NULL);
+
+ if (!NT_SUCCESS(Status)) goto Error;
+ Peb = ProcessBasicInfo.PebBaseAddress;
+
+ /* Now that we have the PEB, read the image base address out of it */
+ Result = ReadProcessMemory(ProcessHandle,
+ &Peb->ImageBaseAddress,
+ &BaseAddress,
+ sizeof(BaseAddress),
+ NULL);
+ if (!Result) goto Error;
+
+ /* Now read the e_lfanew (offset to NT header) from the base */
+ DosHeader = BaseAddress;
+ Result = ReadProcessMemory(ProcessHandle,
+ &DosHeader->e_lfanew,
+ &e_lfanew,
+ sizeof(e_lfanew),
+ NULL);
+ if (!Result) goto Error;
+
+ /* And finally, read the NT header itself by adding the offset */
+ NtHeader = (PVOID)((ULONG_PTR)BaseAddress + e_lfanew);
+ Result = ReadProcessMemory(ProcessHandle,
+ &NtHeader->OptionalHeader.MajorSubsystemVersion,
+ &VersionData,
+ sizeof(VersionData),
+ NULL);
+ if (!Result) goto Error;
+
+ /* Get the version straight out of the NT header */
+ Version = MAKELONG(VersionData[0], VersionData[1]);
+
+Error:
+ /* If there was an error anywhere, set the last error */
+ if (!NT_SUCCESS(Status)) BaseSetLastNTError(Status);
+ }
+ }
+ _SEH2_FINALLY
+ {
+ /* Close the process handle */
+ if (ProcessHandle) CloseHandle(ProcessHandle);
+ }
+ _SEH2_END;
+
+ /* And return the version data */
+ return Version;
+}
+
+/*
+ * @implemented
+ */
+BOOL
+WINAPI
+GetProcessIoCounters(IN HANDLE hProcess,
+ OUT PIO_COUNTERS lpIoCounters)
+{
+ NTSTATUS Status;
+
+ /* Query the kernel. Structures are identical, so let it do the copy too. */
+ Status = NtQueryInformationProcess(hProcess,
+ ProcessIoCounters,
+ lpIoCounters,
+ sizeof(IO_COUNTERS),
+ NULL);
+ if (!NT_SUCCESS(Status))
+ {
+ /* Handle error path */
+ BaseSetLastNTError(Status);
+ return FALSE;
+ }
+
+ /* All done */
+ return TRUE;
+}
+
+/*
+ * @implemented
+ */
+BOOL
+WINAPI
+GetProcessPriorityBoost(IN HANDLE hProcess,
+ OUT PBOOL pDisablePriorityBoost)
+{
+ NTSTATUS Status;
+ ULONG PriorityBoost;
+
+ /* Query the kernel */
+ Status = NtQueryInformationProcess(hProcess,
+ ProcessPriorityBoost,
+ &PriorityBoost,
+ sizeof(ULONG),
+ NULL);
+ if (NT_SUCCESS(Status))
+ {
+ /* Convert from ULONG to a BOOL */
+ *pDisablePriorityBoost = PriorityBoost ? TRUE : FALSE;
+ return TRUE;
+ }
+
+ /* Handle error path */
+ BaseSetLastNTError(Status);
+ return FALSE;
+}
+
+/*
+ * @implemented
+ */
+BOOL
+WINAPI
+SetProcessPriorityBoost(IN HANDLE hProcess,
+ IN BOOL bDisablePriorityBoost)
+{
+ NTSTATUS Status;
+ ULONG PriorityBoost;
+
+ /* Enforce that this is a BOOL, and send it to the kernel as a ULONG */
+ PriorityBoost = (bDisablePriorityBoost ? TRUE : FALSE);
+ Status = NtSetInformationProcess(hProcess,
+ ProcessPriorityBoost,
+ &PriorityBoost,
+ sizeof(ULONG));
+ if (!NT_SUCCESS(Status))
+ {
+ /* Handle error path */
+ BaseSetLastNTError(Status);
+ return FALSE;
+ }
+
+ /* All done */
+ return TRUE;
+}
+
+/*
+ * @implemented
+ */
+BOOL
+WINAPI
+GetProcessHandleCount(IN HANDLE hProcess,
+ OUT PDWORD pdwHandleCount)
+{
+ ULONG phc;
+ NTSTATUS Status;
+
+ /* Query the kernel */
+ Status = NtQueryInformationProcess(hProcess,
+ ProcessHandleCount,
+ &phc,
+ sizeof(ULONG),
+ NULL);
+ if (NT_SUCCESS(Status))
+ {
+ /* Copy the count and return sucecss */
+ *pdwHandleCount = phc;
+ return TRUE;
+ }
+
+ /* Handle error path */
+ BaseSetLastNTError(Status);
+ return FALSE;
+}
+
+/*
+ * @implemented
+ */
+BOOL
+WINAPI
+IsWow64Process(IN HANDLE hProcess,
+ OUT PBOOL Wow64Process)
+{
+ ULONG_PTR pbi;
+ NTSTATUS Status;
+
+ /* Query the kernel */
+ Status = NtQueryInformationProcess(hProcess,
+ ProcessWow64Information,
+ &pbi,
+ sizeof(pbi),
+ NULL);
+ if (!NT_SUCCESS(Status))
+ {
+ /* Handle error path */
+ BaseSetLastNTError(Status);
+ return FALSE;
+ }
+
+ /* Enforce this is a BOOL, and return success */
+ *Wow64Process = (pbi != 0);
+ return TRUE;
+}
+
+/*
+ * @implemented
+ */
+LPSTR
+WINAPI
+GetCommandLineA(VOID)
+{
+ return BaseAnsiCommandLine.Buffer;
+}
+
+/*
+ * @implemented
+ */
+LPWSTR
+WINAPI
+GetCommandLineW(VOID)
+{
+ return BaseUnicodeCommandLine.Buffer;
+}
+
+/*
+ * @implemented
+ */
+BOOL
+NTAPI
+ReadProcessMemory(IN HANDLE hProcess,
+ IN LPCVOID lpBaseAddress,
+ IN LPVOID lpBuffer,
+ IN SIZE_T nSize,
+ OUT SIZE_T* lpNumberOfBytesRead)
+{
+ NTSTATUS Status;
+
+ /* Do the read */
+ Status = NtReadVirtualMemory(hProcess,
+ (PVOID)lpBaseAddress,
+ lpBuffer,
+ nSize,
+ &nSize);
+
+ /* In user-mode, this parameter is optional */
+ if (lpNumberOfBytesRead) *lpNumberOfBytesRead = nSize;
+ if (!NT_SUCCESS(Status))
+ {
+ /* We failed */
+ BaseSetLastNTError(Status);
+ return FALSE;
+ }
+
+ /* Return success */
+ return TRUE;
+}
+
+/*
+ * @implemented
+ */
+BOOL
+NTAPI
+WriteProcessMemory(IN HANDLE hProcess,
+ IN LPVOID lpBaseAddress,
+ IN LPCVOID lpBuffer,
+ IN SIZE_T nSize,
+ OUT SIZE_T *lpNumberOfBytesWritten)
+{
+ NTSTATUS Status;
+ ULONG OldValue;
+ SIZE_T RegionSize;
+ PVOID Base;
+ BOOLEAN UnProtect;
+
+ /* Set parameters for protect call */
+ RegionSize = nSize;
+ Base = lpBaseAddress;
+
+ /* Check the current status */
+ Status = NtProtectVirtualMemory(hProcess,
+ &Base,
+ &RegionSize,
+ PAGE_EXECUTE_READWRITE,
+ &OldValue);
+ if (NT_SUCCESS(Status))
+ {
+ /* Check if we are unprotecting */
+ UnProtect = OldValue & (PAGE_READWRITE |
+ PAGE_WRITECOPY |
+ PAGE_EXECUTE_READWRITE |
+ PAGE_EXECUTE_WRITECOPY) ? FALSE : TRUE;
+ if (!UnProtect)
+ {
+ /* Set the new protection */
+ Status = NtProtectVirtualMemory(hProcess,
+ &Base,
+ &RegionSize,
+ OldValue,
+ &OldValue);
+
+ /* Write the memory */
+ Status = NtWriteVirtualMemory(hProcess,
+ lpBaseAddress,
+ (LPVOID)lpBuffer,
+ nSize,
+ &nSize);
+
+ /* In Win32, the parameter is optional, so handle this case */
+ if (lpNumberOfBytesWritten) *lpNumberOfBytesWritten = nSize;
+
+ if (!NT_SUCCESS(Status))
+ {
+ /* We failed */
+ BaseSetLastNTError(Status);
+ return FALSE;
+ }
+
+ /* Flush the ITLB */
+ NtFlushInstructionCache(hProcess, lpBaseAddress, nSize);
+ return TRUE;
+ }
+ else
+ {
+ /* Check if we were read only */
+ if (OldValue & (PAGE_NOACCESS | PAGE_READONLY))
+ {
+ /* Restore protection and fail */
+ NtProtectVirtualMemory(hProcess,
+ &Base,
+ &RegionSize,
+ OldValue,
+ &OldValue);
+ BaseSetLastNTError(STATUS_ACCESS_VIOLATION);
+
+ /* Note: This is what Windows returns and code depends on it */
+ return STATUS_ACCESS_VIOLATION;
+ }
+
+ /* Otherwise, do the write */
+ Status = NtWriteVirtualMemory(hProcess,
+ lpBaseAddress,
+ (LPVOID)lpBuffer,
+ nSize,
+ &nSize);
+
+ /* In Win32, the parameter is optional, so handle this case */
+ if (lpNumberOfBytesWritten) *lpNumberOfBytesWritten = nSize;
+
+ /* And restore the protection */
+ NtProtectVirtualMemory(hProcess,
+ &Base,
+ &RegionSize,
+ OldValue,
+ &OldValue);
+ if (!NT_SUCCESS(Status))
+ {
+ /* We failed */
+ BaseSetLastNTError(STATUS_ACCESS_VIOLATION);
+
+ /* Note: This is what Windows returns and code depends on it */
+ return STATUS_ACCESS_VIOLATION;
+ }
+
+ /* Flush the ITLB */
+ NtFlushInstructionCache(hProcess, lpBaseAddress, nSize);
+ return TRUE;
+ }
+ }
+ else
+ {
+ /* We failed */
+ BaseSetLastNTError(Status);
+ return FALSE;
+ }
+}
+
+/*
+ * @implemented
+ */
+BOOL
+WINAPI
+ProcessIdToSessionId(IN DWORD dwProcessId,
+ OUT PDWORD pSessionId)
+{
+ PROCESS_SESSION_INFORMATION SessionInformation;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ CLIENT_ID ClientId;
+ HANDLE ProcessHandle;
+ NTSTATUS Status;
+
+ /* Do a quick check if the pointer is not writable */
+ if (IsBadWritePtr(pSessionId, sizeof(DWORD)))
+ {
+ /* Fail fast */
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+
+ /* Open the process passed in by ID */
+ ClientId.UniqueProcess = UlongToHandle(dwProcessId);
+ ClientId.UniqueThread = 0;
+ InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
+ Status = NtOpenProcess(&ProcessHandle,
+ PROCESS_QUERY_INFORMATION,
+ &ObjectAttributes,
+ &ClientId);
+ if (NT_SUCCESS(Status))
+ {
+ /* Query the session ID from the kernel */
+ Status = NtQueryInformationProcess(ProcessHandle,
+ ProcessSessionInformation,
+ &SessionInformation,
+ sizeof(SessionInformation),
+ NULL);
+
+ /* Close the handle and check if we suceeded */
+ NtClose(ProcessHandle);
+ if (NT_SUCCESS(Status))
+ {
+ /* Return the session ID */
+ *pSessionId = SessionInformation.SessionId;
+ return TRUE;
+ }
+ }
+
+ /* Set error code and fail */
+ BaseSetLastNTError(Status);
+ return FALSE;
+}
+
++
++#define AddToHandle(x,y) (x) = (HANDLE)((ULONG_PTR)(x) | (y));
++#define RemoveFromHandle(x,y) (x) = (HANDLE)((ULONG_PTR)(x) & ~(y));
++C_ASSERT(PROCESS_PRIORITY_CLASS_REALTIME == (PROCESS_PRIORITY_CLASS_HIGH + 1));
++
+/*
+ * @implemented
+ */
+BOOL
+WINAPI
- NTSTATUS Status;
- PROCESS_PRIORITY_CLASS PriorityClass;
- BOOLEAN FoundQuotes = FALSE;
- BOOLEAN QuotesNeeded = FALSE;
- BOOLEAN CmdLineIsAppName = FALSE;
- UNICODE_STRING ApplicationName = { 0, 0, NULL };
++CreateProcessInternalW(IN HANDLE hUserToken,
++ IN LPCWSTR lpApplicationName,
++ IN LPWSTR lpCommandLine,
++ IN LPSECURITY_ATTRIBUTES lpProcessAttributes,
++ IN LPSECURITY_ATTRIBUTES lpThreadAttributes,
++ IN BOOL bInheritHandles,
++ IN DWORD dwCreationFlags,
++ IN LPVOID lpEnvironment,
++ IN LPCWSTR lpCurrentDirectory,
++ IN LPSTARTUPINFOW lpStartupInfo,
++ IN LPPROCESS_INFORMATION lpProcessInformation,
++ OUT PHANDLE hNewToken)
+{
- HANDLE hSection = NULL, hProcess = NULL, hThread = NULL, hDebug = NULL;
- SECTION_IMAGE_INFORMATION SectionImageInfo;
- LPWSTR CurrentDirectory = NULL;
- LPWSTR CurrentDirectoryPart;
- PROCESS_BASIC_INFORMATION ProcessBasicInfo;
- STARTUPINFOW StartupInfo;
- ULONG Dummy;
- LPWSTR BatchCommandLine;
- ULONG CmdLineLength;
- UNICODE_STRING CommandLineString;
- PWCHAR Extension;
- LPWSTR QuotedCmdLine = NULL;
- LPWSTR ScanString;
- LPWSTR NullBuffer = NULL;
- LPWSTR NameBuffer = NULL;
- WCHAR SaveChar = 0;
- ULONG RetVal;
- UINT Error = 0;
- UINT Length;
- BOOLEAN SearchDone = FALSE;
- BOOLEAN Escape = FALSE;
++ //
++ // Core variables used for creating the initial process and thread
++ //
++ SECURITY_ATTRIBUTES LocalThreadAttributes, LocalProcessAttributes;
+ OBJECT_ATTRIBUTES LocalObjectAttributes;
+ POBJECT_ATTRIBUTES ObjectAttributes;
- PPEB OurPeb = NtCurrentPeb();
- PPEB RemotePeb;
- SIZE_T EnvSize = 0;
- BOOL Ret = FALSE;
++ SECTION_IMAGE_INFORMATION ImageInformation;
++ IO_STATUS_BLOCK IoStatusBlock;
+ CLIENT_ID ClientId;
- /* FIXME should process
- * HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options
- * key (see http://blogs.msdn.com/oldnewthing/archive/2005/12/19/505449.aspx)
- */
++ ULONG NoWindow, RegionSize, StackSize, ImageMachine, ErrorCode, Flags;
++ ULONG ParameterFlags, PrivilegeValue, HardErrorMode, ErrorResponse;
++ ULONG_PTR ErrorParameters[2];
++ BOOLEAN InJob, SaferNeeded, UseLargePages, HavePrivilege;
++ BOOLEAN QuerySection, SkipSaferAndAppCompat;
++ CONTEXT Context;
++ BASE_API_MESSAGE CsrMsg;
++ PBASE_CREATE_PROCESS CreateProcessMsg;
++ PCSR_CAPTURE_BUFFER CaptureBuffer;
++ PVOID BaseAddress, PrivilegeState, RealTimePrivilegeState;
++ HANDLE DebugHandle, TokenHandle, JobHandle, KeyHandle, ThreadHandle;
++ HANDLE FileHandle, SectionHandle, ProcessHandle;
++ ULONG ResumeCount;
++ PROCESS_PRIORITY_CLASS PriorityClass;
++ NTSTATUS Status, Status1, ImageDbgStatus;
++ PPEB Peb, RemotePeb;
++ PTEB Teb;
++ INITIAL_TEB InitialTeb;
++ PVOID TibValue;
++ PIMAGE_NT_HEADERS NtHeaders;
++ STARTUPINFOW StartupInfo;
++ PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
++ UNICODE_STRING DebuggerString;
++ BOOL Result;
++ //
++ // Variables used for command-line and argument parsing
++ //
++ PCHAR pcScan;
++ SIZE_T n;
++ WCHAR SaveChar;
++ ULONG Length, CurdirLength, CmdQuoteLength;
++ ULONG CmdLineLength, ResultSize;
++ PWCHAR QuotedCmdLine, AnsiCmdCommand, ExtBuffer, CurrentDirectory;
++ PWCHAR NullBuffer, ScanString, NameBuffer, SearchPath, DebuggerCmdLine;
++ ANSI_STRING AnsiEnv;
++ UNICODE_STRING UnicodeEnv, PathName;
++ BOOLEAN SearchRetry, QuotesNeeded, CmdLineIsAppName, HasQuotes;
+ WCHAR VdmPath[MAX_PATH];
+
- DPRINT("CreateProcessW: lpApplicationName: %S lpCommandLine: %S"
- " lpEnvironment: %p lpCurrentDirectory: %S dwCreationFlags: %lx\n",
- lpApplicationName, lpCommandLine, lpEnvironment, lpCurrentDirectory,
- dwCreationFlags);
++ //
++ // Variables used for Fusion/SxS (Side-by-Side Assemblies)
++ //
++ RTL_PATH_TYPE SxsPathType, PathType;
++#if _SXS_SUPPORT_ENABLED_
++ PRTL_BUFFER ByteBuffer;
++ PRTL_UNICODE_STRING_BUFFER ThisBuffer, Buffer, SxsStaticBuffers[5];
++ PRTL_UNICODE_STRING_BUFFER* BufferHead, SxsStringBuffer;
++ RTL_UNICODE_STRING_BUFFER SxsWin32ManifestPath, SxsNtManifestPath;
++ RTL_UNICODE_STRING_BUFFER SxsWin32PolicyPath, SxsNtPolicyPath;
++ RTL_UNICODE_STRING_BUFFER SxsWin32AssemblyDirectory;
++ BASE_MSG_SXS_HANDLES MappedHandles, Handles, FileHandles;
++ PVOID CapturedStrings[3];
++ SXS_WIN32_NT_PATH_PAIR ExePathPair, ManifestPathPair, PolicyPathPair;
++ SXS_OVERRIDE_MANIFEST OverrideMannifest;
++ UNICODE_STRING FreeString, SxsNtExePath;
++ PWCHAR SxsConglomeratedBuffer, StaticBuffer;
++ ULONG ConglomeratedBufferSizeBytes, StaticBufferSize, i;
++#endif
++ ULONG FusionFlags;
++
++ //
++ // Variables used for path conversion (and partially Fusion/SxS)
++ //
++ PWCHAR FilePart, PathBuffer, FreeBuffer;
++ BOOLEAN TranslationStatus;
++ RTL_RELATIVE_NAME_U SxsWin32RelativePath;
++ UNICODE_STRING PathBufferString, SxsWin32ExePath;
++
++ //
++ // Variables used by Application Compatibility (and partially Fusion/SxS)
++ //
++ PVOID AppCompatSxsData, AppCompatData;
++ ULONG AppCompatSxsDataSize, AppCompatDataSize;
++ //
++ // Variables used by VDM (Virtual Dos Machine) and WOW32 (16-bit Support)
++ //
++ ULONG BinarySubType, VdmBinaryType, VdmTask, VdmReserve;
++ ULONG VdmUndoLevel;
++ BOOLEAN UseVdmReserve;
++ HANDLE VdmWaitObject;
++ ANSI_STRING VdmAnsiEnv;
++ UNICODE_STRING VdmString, VdmUnicodeEnv;
++ BOOLEAN IsWowApp;
++ PBASE_CHECK_VDM VdmMsg;
++
++ /* Zero out the initial core variables and handles */
++ QuerySection = FALSE;
++ InJob = FALSE;
++ SkipSaferAndAppCompat = FALSE;
++ ParameterFlags = 0;
++ Flags = 0;
++ DebugHandle = NULL;
++ JobHandle = NULL;
++ TokenHandle = NULL;
++ FileHandle = NULL;
++ SectionHandle = NULL;
++ ProcessHandle = NULL;
++ ThreadHandle = NULL;
++ BaseAddress = (PVOID)1;
++
++ /* Zero out initial SxS and Application Compatibility state */
++ AppCompatData = NULL;
++ AppCompatDataSize = 0;
++ AppCompatSxsData = NULL;
++ AppCompatSxsDataSize = 0;
++ CaptureBuffer = NULL;
++#if _SXS_SUPPORT_ENABLED_
++ SxsConglomeratedBuffer = NULL;
++#endif
++ FusionFlags = 0;
++
++ /* Zero out initial parsing variables -- others are initialized later */
++ DebuggerCmdLine = NULL;
++ PathBuffer = NULL;
++ SearchPath = NULL;
++ NullBuffer = 0;
++ FreeBuffer = NULL;
++ NameBuffer = NULL;
++ CurrentDirectory = NULL;
++ FilePart = NULL;
++ DebuggerString.Buffer = NULL;
++ HasQuotes = FALSE;
++ QuotedCmdLine = NULL;
++
++ /* Zero out initial VDM state */
++ VdmAnsiEnv.Buffer = NULL;
++ VdmUnicodeEnv.Buffer = NULL;
++ VdmString.Buffer = NULL;
++ VdmTask = 0;
++ VdmUndoLevel = 0;
++ VdmBinaryType = 0;
++ VdmReserve = 0;
++ VdmWaitObject = NULL;
++ UseVdmReserve = FALSE;
++ IsWowApp = FALSE;
++
++ /* Set message structures */
++ CreateProcessMsg = &CsrMsg.Data.CreateProcessRequest;
++ VdmMsg = &CsrMsg.Data.CheckVdm;
++
++ /* Clear the more complex structures by zeroing out their entire memory */
++ RtlZeroMemory(&Context, sizeof(Context));
++#if _SXS_SUPPORT_ENABLED_
++ RtlZeroMemory(&FileHandles, sizeof(FileHandles));
++ RtlZeroMemory(&MappedHandles, sizeof(MappedHandles));
++ RtlZeroMemory(&Handles, sizeof(Handles));
++#endif
++ RtlZeroMemory(&CreateProcessMsg->Sxs, sizeof(CreateProcessMsg->Sxs));
++ RtlZeroMemory(&LocalProcessAttributes, sizeof(LocalProcessAttributes));
++ RtlZeroMemory(&LocalThreadAttributes, sizeof(LocalThreadAttributes));
++
++ /* Zero out output arguments as well */
++ RtlZeroMemory(lpProcessInformation, sizeof(*lpProcessInformation));
++ if (hNewToken) *hNewToken = NULL;
++
++ /* Capture the special window flag */
++ NoWindow = dwCreationFlags & CREATE_NO_WINDOW;
++ dwCreationFlags &= ~CREATE_NO_WINDOW;
++
++#if _SXS_SUPPORT_ENABLED_
++ /* Setup the SxS static string arrays and buffers */
++ SxsStaticBuffers[0] = &SxsWin32ManifestPath;
++ SxsStaticBuffers[1] = &SxsWin32PolicyPath;
++ SxsStaticBuffers[2] = &SxsWin32AssemblyDirectory;
++ SxsStaticBuffers[3] = &SxsNtManifestPath;
++ SxsStaticBuffers[4] = &SxsNtPolicyPath;
++ ExePathPair.Win32 = &SxsWin32ExePath;
++ ExePathPair.Nt = &SxsNtExePath;
++ ManifestPathPair.Win32 = &SxsWin32ManifestPath.String;
++ ManifestPathPair.Nt = &SxsNtManifestPath.String;
++ PolicyPathPair.Win32 = &SxsWin32PolicyPath.String;
++ PolicyPathPair.Nt = &SxsNtPolicyPath.String;
++#endif
++
++ DPRINT1("CreateProcessInternalW: %S %S %lx\n", lpApplicationName, lpCommandLine, dwCreationFlags);
+
- /* Flags we don't handle yet */
- if (dwCreationFlags & CREATE_SEPARATE_WOW_VDM)
++ /* Finally, set our TEB and PEB */
++ Teb = NtCurrentTeb();
++ Peb = NtCurrentPeb();
+
- DPRINT1("CREATE_SEPARATE_WOW_VDM not handled\n");
++ /* This combination is illegal (see MSDN) */
++ if ((dwCreationFlags & (DETACHED_PROCESS | CREATE_NEW_CONSOLE)) ==
++ (DETACHED_PROCESS | CREATE_NEW_CONSOLE))
+ {
- if (dwCreationFlags & CREATE_SHARED_WOW_VDM)
++ DPRINT1("Invalid flag combo used\n");
++ SetLastError(ERROR_INVALID_PARAMETER);
++ return FALSE;
+ }
- DPRINT1("CREATE_SHARED_WOW_VDM not handled\n");
++
++ /* Convert the priority class */
++ if (dwCreationFlags & IDLE_PRIORITY_CLASS)
+ {
- if (dwCreationFlags & CREATE_FORCEDOS)
++ PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_IDLE;
+ }
- DPRINT1("CREATE_FORCEDOS not handled\n");
++ else if (dwCreationFlags & BELOW_NORMAL_PRIORITY_CLASS)
+ {
-
- /* Fail on this flag, it's only valid with the WithLogonW function */
- if (dwCreationFlags & CREATE_PRESERVE_CODE_AUTHZ_LEVEL)
++ PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_BELOW_NORMAL;
+ }
- DPRINT1("Invalid flag used\n");
- SetLastError(ERROR_INVALID_PARAMETER);
- return FALSE;
++ else if (dwCreationFlags & NORMAL_PRIORITY_CLASS)
+ {
-
- /* This combination is illegal (see MSDN) */
- if ((dwCreationFlags & (DETACHED_PROCESS | CREATE_NEW_CONSOLE)) ==
- (DETACHED_PROCESS | CREATE_NEW_CONSOLE))
++ PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_NORMAL;
+ }
- DPRINT1("Invalid flag combo used\n");
++ else if (dwCreationFlags & ABOVE_NORMAL_PRIORITY_CLASS)
+ {
- /* Another illegal combo */
- if ((dwCreationFlags & (CREATE_SEPARATE_WOW_VDM | CREATE_SHARED_WOW_VDM)) ==
- (CREATE_SEPARATE_WOW_VDM | CREATE_SHARED_WOW_VDM))
++ PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_ABOVE_NORMAL;
++ }
++ else if (dwCreationFlags & HIGH_PRIORITY_CLASS)
++ {
++ PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_HIGH;
++ }
++ else if (dwCreationFlags & REALTIME_PRIORITY_CLASS)
++ {
++ PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_HIGH;
++ PriorityClass.PriorityClass += (BasepIsRealtimeAllowed(FALSE) != NULL);
++ }
++ else
++ {
++ PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_INVALID;
++ }
++
++ /* Done with the priority masks, so get rid of them */
++ PriorityClass.Foreground = FALSE;
++ dwCreationFlags &= ~(NORMAL_PRIORITY_CLASS |
++ IDLE_PRIORITY_CLASS |
++ HIGH_PRIORITY_CLASS |
++ REALTIME_PRIORITY_CLASS |
++ BELOW_NORMAL_PRIORITY_CLASS |
++ ABOVE_NORMAL_PRIORITY_CLASS);
++
++ /* You cannot request both a shared and a separate WoW VDM */
++ if ((dwCreationFlags & CREATE_SEPARATE_WOW_VDM) &&
++ (dwCreationFlags & CREATE_SHARED_WOW_VDM))
++ {
++ /* Fail such nonsensical attempts */
++ DPRINT1("Invalid WOW flags\n");
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
++ else if (!(dwCreationFlags & CREATE_SHARED_WOW_VDM) &&
++ (BaseStaticServerData->DefaultSeparateVDM))
++ {
++ /* A shared WoW VDM was not requested but system enforces separation */
++ dwCreationFlags |= CREATE_SEPARATE_WOW_VDM;
++ }
+
- DPRINT1("Invalid flag combo used\n");
- SetLastError(ERROR_INVALID_PARAMETER);
- return FALSE;
++ /* If a shared WoW VDM is used, make sure the process isn't in a job */
++ if (!(dwCreationFlags & CREATE_SEPARATE_WOW_VDM) &&
++ (NtIsProcessInJob(NtCurrentProcess(), NULL)))
+ {
- if (lpCurrentDirectory)
++ /* Remove the shared flag and add the separate flag */
++ dwCreationFlags = (dwCreationFlags &~ CREATE_SHARED_WOW_VDM) |
++ CREATE_SEPARATE_WOW_VDM;
+ }
+
- if ((GetFileAttributesW(lpCurrentDirectory) == INVALID_FILE_ATTRIBUTES) ||
- !(GetFileAttributesW(lpCurrentDirectory) & FILE_ATTRIBUTE_DIRECTORY))
++ /* Convert the environment */
++ if ((lpEnvironment) && !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT))
+ {
- SetLastError(ERROR_DIRECTORY);
++ /* Scan the environment to calculate its Unicode size */
++ AnsiEnv.Buffer = pcScan = (PCHAR)lpEnvironment;
++ while ((*pcScan) || (*(pcScan + 1))) ++pcScan;
++
++ /* Create our ANSI String */
++ AnsiEnv.Length = pcScan - (PCHAR)lpEnvironment + sizeof(ANSI_NULL);
++ AnsiEnv.MaximumLength = AnsiEnv.Length + sizeof(ANSI_NULL);
++
++ /* Allocate memory for the Unicode Environment */
++ UnicodeEnv.Buffer = NULL;
++ RegionSize = AnsiEnv.MaximumLength * sizeof(WCHAR);
++ Status = NtAllocateVirtualMemory(NtCurrentProcess(),
++ (PVOID)&UnicodeEnv.Buffer,
++ 0,
++ &RegionSize,
++ MEM_COMMIT,
++ PAGE_READWRITE);
++ if (!NT_SUCCESS(Status))
+ {
- }
++ /* Fail */
++ BaseSetLastNTError(Status);
+ return FALSE;
+ }
- /*
- * We're going to modify and mask out flags and stuff in lpStartupInfo,
- * so we'll use our own local copy for that.
- */
- StartupInfo = *lpStartupInfo;
-
- /* FIXME: Use default Separate/Shared VDM Flag */
-
- /* If we are inside a Job, use Separate VDM so it won't escape the Job */
- if (!(dwCreationFlags & CREATE_SEPARATE_WOW_VDM))
- {
- if (NtIsProcessInJob(NtCurrentProcess(), NULL))
+
- /* Remove the shared flag and add the separate flag. */
- dwCreationFlags = (dwCreationFlags &~ CREATE_SHARED_WOW_VDM) |
- CREATE_SEPARATE_WOW_VDM;
++ /* Use the allocated size and convert */
++ UnicodeEnv.MaximumLength = RegionSize;
++ Status = RtlAnsiStringToUnicodeString(&UnicodeEnv, &AnsiEnv, FALSE);
++ if (!NT_SUCCESS(Status))
+ {
- }
++ /* Fail */
++ NtFreeVirtualMemory(NtCurrentProcess(),
++ (PVOID)&UnicodeEnv.Buffer,
++ &RegionSize,
++ MEM_RELEASE);
++ BaseSetLastNTError(Status);
++ return FALSE;
+ }
- /* Get the path to the VDM host */
- Length = GetSystemDirectoryW(VdmPath, MAX_PATH - wcslen(NTVDM_STRING));
- if ((Length == 0) || (Length >= MAX_PATH - wcslen(NTVDM_STRING)))
- {
- /* System path not found for some reason, fail */
- SetLastError(ERROR_INVALID_NAME);
- return FALSE;
+
- wcscat(VdmPath, NTVDM_STRING);
++ /* Now set the Unicode environment as the environment string pointer */
++ lpEnvironment = UnicodeEnv.Buffer;
+ }
- /*
- * According to some sites, ShellExecuteEx uses an undocumented flag to
- * send private handle data (such as HMONITOR or HICON). See:
- * www.catch22.net/tuts/undoc01.asp. This implies that we can't use the
- * standard handles anymore since we'd be overwriting this private data
- */
+
- /* Start by zeroing out the fields */
- RtlZeroMemory(lpProcessInformation, sizeof(PROCESS_INFORMATION));
++ /* Make a copy of the caller's startup info since we'll modify it */
++ StartupInfo = *lpStartupInfo;
++
++ /* Check if private data is being sent on the same channel as std handles */
+ if ((StartupInfo.dwFlags & STARTF_USESTDHANDLES) &&
+ (StartupInfo.dwFlags & (STARTF_USEHOTKEY | STARTF_SHELLPRIVATE)))
+ {
++ /* Cannot use the std handles since we have monitor/hotkey values */
+ StartupInfo.dwFlags &= ~STARTF_USESTDHANDLES;
+ }
+
- /* Easy stuff first, convert the process priority class */
- PriorityClass.Foreground = FALSE;
- PriorityClass.PriorityClass = (UCHAR)BasepConvertPriorityClass(dwCreationFlags);
++ /* If there's a debugger, or we have to launch cmd.exe, we go back here */
++AppNameRetry:
++ /* New iteration -- free any existing name buffer */
++ if (NameBuffer)
++ {
++ RtlFreeHeap(RtlGetProcessHeap(), 0, NameBuffer);
++ NameBuffer = NULL;
++ }
+
- if (lpCommandLine)
++ /* New iteration -- free any existing free buffer */
++ if (FreeBuffer)
++ {
++ RtlFreeHeap(RtlGetProcessHeap(), 0, FreeBuffer);
++ FreeBuffer = NULL;
++ }
+
- /* Search for escape sequences */
- ScanString = lpCommandLine;
- while (NULL != (ScanString = wcschr(ScanString, L'^')))
- {
- ScanString++;
- if (*ScanString == L'\"' || *ScanString == L'^' || *ScanString == L'\\')
- {
- Escape = TRUE;
- break;
- }
- }
++ /* New iteration -- close any existing file handle */
++ if (FileHandle)
+ {
- /* Get the application name and do all the proper formating necessary */
- GetAppName:
- /* See if we have an application name (oh please let us have one!) */
++ NtClose(FileHandle);
++ FileHandle = NULL;
+ }
+
- /* The fun begins */
++ /* Set the initial parsing state. This code can loop -- don't move this! */
++ ErrorCode = 0;
++ SearchRetry = TRUE;
++ QuotesNeeded = FALSE;
++ CmdLineIsAppName = FALSE;
++
++ /* First check if we don't have an application name */
+ if (!lpApplicationName)
+ {
- if (NameBuffer == NULL)
++ /* This should be the first time we attempt creating one */
++ ASSERT(NameBuffer == NULL);
++
++ /* Allocate a buffer to hold it */
+ NameBuffer = RtlAllocateHeap(RtlGetProcessHeap(),
+ 0,
+ MAX_PATH * sizeof(WCHAR));
- goto Cleanup;
++ if (!NameBuffer)
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
- /* This is all we have to work with :( */
- lpApplicationName = lpCommandLine;
-
- /* Initialize our friends at the beginning */
- NullBuffer = (LPWSTR)lpApplicationName;
- ScanString = (LPWSTR)lpApplicationName;
++ Result = FALSE;
++ goto Quickie;
+ }
+
- /* We will start by looking for a quote */
- if (*ScanString == L'\"')
++ /* Initialize the application name and our parsing parameters */
++ lpApplicationName = NullBuffer = ScanString = lpCommandLine;
+
- /* That was quick */
- SearchDone = TRUE;
-
- /* Advance past quote */
- ScanString++;
- lpApplicationName = ScanString;
-
- /* Find the closing quote */
- while (*ScanString)
- {
- if (*ScanString == L'\"' && *(ScanString - 1) != L'^')
- {
- /* Found it */
- NullBuffer = ScanString;
- FoundQuotes = TRUE;
- break;
- }
++ /* Check for an initial quote*/
++ if (*lpCommandLine == L'\"')
+ {
- /* Keep looking */
- ScanString++;
- NullBuffer = ScanString;
- }
++ /* We found a quote, keep searching for another one */
++ SearchRetry = FALSE;
++ ScanString++;
++ lpApplicationName = ScanString;
++ while (*ScanString)
++ {
++ /* Have we found the terminating quote? */
++ if (*ScanString == L'\"')
++ {
++ /* We're done, get out of here */
++ NullBuffer = ScanString;
++ HasQuotes = TRUE;
++ break;
++ }
+
- /* No quotes, so we'll be looking for white space */
- WhiteScan:
- /* Reset the pointer */
++ /* Keep searching for the quote */
++ ScanString++;
++ NullBuffer = ScanString;
++ }
+ }
+ else
+ {
-
- /* Find whitespace of Tab */
++StartScan:
++ /* We simply make the application name be the command line*/
+ lpApplicationName = lpCommandLine;
- if (*ScanString == ' ' || *ScanString == '\t')
+ while (*ScanString)
+ {
- /* Found it */
++ /* Check if it starts with a space or tab */
++ if ((*ScanString == L' ') || (*ScanString == L'\t'))
+ {
- /* Keep looking */
++ /* Break out of the search loop */
+ NullBuffer = ScanString;
+ break;
+ }
+
- /* Set the Null Buffer */
++ /* Keep searching for a space or tab */
+ ScanString++;
+ NullBuffer = ScanString;
+ }
+ }
+
- /* Do a search for the file */
- DPRINT("Ready for SearchPathW: %S\n", lpApplicationName);
- RetVal = SearchPathW(NULL,
++ /* We have found the end of the application name, terminate it */
+ SaveChar = *NullBuffer;
+ *NullBuffer = UNICODE_NULL;
+
- NULL) * sizeof(WCHAR);
++ /* New iteration -- free any existing saved path */
++ if (SearchPath)
++ {
++ RtlFreeHeap(RtlGetProcessHeap(), 0, SearchPath);
++ SearchPath = NULL;
++ }
++
++ /* Now compute the final EXE path based on the name */
++ SearchPath = BaseComputeProcessExePath((LPWSTR)lpApplicationName);
++ DPRINT1("Search Path: %S\n", SearchPath);
++ if (!SearchPath)
++ {
++ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
++ Result = FALSE;
++ goto Quickie;
++ }
++
++ /* And search for the executable in the search path */
++ Length = SearchPathW(SearchPath,
+ lpApplicationName,
+ L".exe",
+ MAX_PATH,
+ NameBuffer,
- /* Did it find something? */
- if (RetVal)
++ NULL);
+
- ULONG Attributes = GetFileAttributesW(NameBuffer);
- if (Attributes & FILE_ATTRIBUTE_DIRECTORY)
++ /* Did we find it? */
++ if ((Length) && (Length < MAX_PATH))
+ {
+ /* Get file attributes */
- /* Give it a length of 0 to fail, this was a directory. */
- RetVal = 0;
++ CurdirLength = GetFileAttributesW(NameBuffer);
++ if ((CurdirLength != 0xFFFFFFFF) &&
++ (CurdirLength & FILE_ATTRIBUTE_DIRECTORY))
+ {
- RetVal += sizeof(WCHAR);
++ /* This was a directory, fail later on */
++ Length = 0;
+ }
+ else
+ {
+ /* It's a file! */
- /* Now check if we have a file, and if the path size is OK */
- if (!RetVal || RetVal >= (MAX_PATH * sizeof(WCHAR)))
- {
- ULONG PathType;
- HANDLE hFile;
-
- /* We failed, try to get the Path Type */
- DPRINT("SearchPathW failed. Retval: %ld\n", RetVal);
- PathType = RtlDetermineDosPathNameType_U(lpApplicationName);
++ Length++;
+ }
+ }
+
- /* If it's not relative, try to get the error */
++ DPRINT1("Length: %lx Buffer: %S\n", Length, NameBuffer);
+
- hFile = CreateFileW(lpApplicationName,
- GENERIC_READ,
- FILE_SHARE_READ | FILE_SHARE_WRITE,
- NULL,
- OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL,
- NULL);
-
- /* Did it actually NOT fail? */
- if (hFile != INVALID_HANDLE_VALUE)
++ /* Check if there was a failure in SearchPathW */
++ if ((Length) && (Length < MAX_PATH))
++ {
++ /* Everything looks good, restore the name */
++ *NullBuffer = SaveChar;
++ lpApplicationName = NameBuffer;
++ }
++ else
++ {
++ /* Check if this was a relative path, which would explain it */
++ PathType = RtlDetermineDosPathNameType_U(lpApplicationName);
+ if (PathType != RtlPathTypeRelative)
+ {
+ /* This should fail, and give us a detailed LastError */
- /* Fake the error */
- CloseHandle(hFile);
++ FileHandle = CreateFileW(lpApplicationName,
++ GENERIC_READ,
++ FILE_SHARE_READ |
++ FILE_SHARE_WRITE,
++ NULL,
++ OPEN_EXISTING,
++ FILE_ATTRIBUTE_NORMAL,
++ NULL);
++ if (FileHandle != INVALID_HANDLE_VALUE)
+ {
- /* Immediately set the error */
++ /* It worked? Return a generic error */
++ CloseHandle(FileHandle);
++ FileHandle = NULL;
+ BaseSetLastNTError(STATUS_OBJECT_NAME_NOT_FOUND);
+ }
+ }
+ else
+ {
- if (Error)
++ /* Path was absolute, which means it doesn't exist */
+ BaseSetLastNTError(STATUS_OBJECT_NAME_NOT_FOUND);
+ }
+
+ /* Did we already fail once? */
- SetLastError(Error);
++ if (ErrorCode)
+ {
- Error = GetLastError();
++ /* Set the error code */
++ SetLastError(ErrorCode);
+ }
+ else
+ {
+ /* Not yet, cache it */
- /*
- * If the search isn't done and we still have cmdline
- * then start over. Ex: c:\ha ha ha\haha.exe
- */
- if (*ScanString && !SearchDone)
++ ErrorCode = GetLastError();
+ }
+
+ /* Put back the command line */
+ *NullBuffer = SaveChar;
+ lpApplicationName = NameBuffer;
+
- /* Move in the buffer */
- ScanString++;
- NullBuffer = ScanString;
++ /* It's possible there's whitespace in the directory name */
++ if (!(*ScanString) || !(SearchRetry))
+ {
- /* We will have to add a quote, since there is a space*/
- QuotesNeeded = TRUE;
++ /* Not the case, give up completely */
++ Result = FALSE;
++ goto Quickie;
++ }
++
++ /* There are spaces, so keep trying the next possibility */
++ ScanString++;
++ NullBuffer = ScanString;
++
++ /* We will have to add a quote, since there is a space */
++ QuotesNeeded = TRUE;
++ HasQuotes = TRUE;
++ goto StartScan;
++ }
++ }
++ else if (!(lpCommandLine) || !(*lpCommandLine))
++ {
++ /* We don't have a command line, so just use the application name */
++ CmdLineIsAppName = TRUE;
++ lpCommandLine = (LPWSTR)lpApplicationName;
++ }
++
++ /* Convert the application name to its NT path */
++ TranslationStatus = RtlDosPathNameToRelativeNtPathName_U(lpApplicationName,
++ &PathName,
++ NULL,
++ &SxsWin32RelativePath);
++ if (!TranslationStatus)
++ {
++ /* Path must be invaild somehow, bail out */
++ DPRINT1("Path translation for SxS failed\n");
++ SetLastError(ERROR_PATH_NOT_FOUND);
++ Result = FALSE;
++ goto Quickie;
++ }
++
++ /* Setup the buffer that needs to be freed at the end */
++ ASSERT(FreeBuffer == NULL);
++ FreeBuffer = PathName.Buffer;
++
++ /* Check what kind of path the application is, for SxS (Fusion) purposes */
++ RtlInitUnicodeString(&SxsWin32ExePath, lpApplicationName);
++ SxsPathType = RtlDetermineDosPathNameType_U(lpApplicationName);
++ if ((SxsPathType != RtlPathTypeDriveAbsolute) &&
++ (SxsPathType != RtlPathTypeLocalDevice) &&
++ (SxsPathType != RtlPathTypeRootLocalDevice) &&
++ (SxsPathType != RtlPathTypeUncAbsolute))
++ {
++ /* Relative-type path, get the full path */
++ RtlInitEmptyUnicodeString(&PathBufferString, NULL, 0);
++ Status = RtlGetFullPathName_UstrEx(&SxsWin32ExePath,
++ NULL,
++ &PathBufferString,
++ NULL,
++ NULL,
++ NULL,
++ &SxsPathType,
++ NULL);
++ if (!NT_SUCCESS(Status))
++ {
++ /* Fail the rest of the create */
++ RtlReleaseRelativeName(&SxsWin32RelativePath);
++ BaseSetLastNTError(Status);
++ Result = FALSE;
++ goto Quickie;
++ }
++
++ /* Use this full path as the SxS path */
++ SxsWin32ExePath = PathBufferString;
++ PathBuffer = PathBufferString.Buffer;
++ PathBufferString.Buffer = NULL;
++ DPRINT1("SxS Path: %S\n", PathBuffer);
++ }
++
++ /* Also set the .EXE path based on the path name */
++#if _SXS_SUPPORT_ENABLED_
++ SxsNtExePath = PathName;
++#endif
++ if (SxsWin32RelativePath.RelativeName.Length)
++ {
++ /* If it's relative, capture the relative name */
++ PathName = SxsWin32RelativePath.RelativeName;
++ }
++ else
++ {
++ /* Otherwise, it's absolute, make sure no relative dir is used */
++ SxsWin32RelativePath.ContainingDirectory = NULL;
++ }
++
++ /* Now use the path name, and the root path, to try opening the app */
++ DPRINT1("Path: %wZ. Dir: %lx\n", &PathName, SxsWin32RelativePath.ContainingDirectory);
++ InitializeObjectAttributes(&LocalObjectAttributes,
++ &PathName,
++ OBJ_CASE_INSENSITIVE,
++ SxsWin32RelativePath.ContainingDirectory,
++ NULL);
++ Status = NtOpenFile(&FileHandle,
++ SYNCHRONIZE |
++ FILE_READ_ATTRIBUTES |
++ FILE_READ_DATA |
++ FILE_EXECUTE,
++ &LocalObjectAttributes,
++ &IoStatusBlock,
++ FILE_SHARE_READ | FILE_SHARE_DELETE,
++ FILE_SYNCHRONOUS_IO_NONALERT |
++ FILE_NON_DIRECTORY_FILE);
++ if (!NT_SUCCESS(Status))
++ {
++ /* Try to open the app just for execute purposes instead */
++ Status = NtOpenFile(&FileHandle,
++ SYNCHRONIZE | FILE_EXECUTE,
++ &LocalObjectAttributes,
++ &IoStatusBlock,
++ FILE_SHARE_READ | FILE_SHARE_DELETE,
++ FILE_SYNCHRONOUS_IO_NONALERT |
++ FILE_NON_DIRECTORY_FILE);
++ }
++
++ /* Cleanup in preparation for failure or success */
++ RtlReleaseRelativeName(&SxsWin32RelativePath);
++ if (!NT_SUCCESS(Status))
++ {
++ /* Failure path, try to understand why */
++ DPRINT1("Open file failed: %lx\n", Status);
++ if (RtlIsDosDeviceName_U(lpApplicationName))
++ {
++ /* If a device is being executed, return this special error code */
++ SetLastError(ERROR_BAD_DEVICE);
++ Result = FALSE;
++ goto Quickie;
++ }
++ else
++ {
++ /* Otherwise return the converted NT error code */
++ BaseSetLastNTError(Status);
++ Result = FALSE;
++ goto Quickie;
++ }
++ }
+
- /* And we will also fake the fact we found one */
- FoundQuotes = TRUE;
++ /* Did the caller specify a desktop? */
++ if (!StartupInfo.lpDesktop)
++ {
++ /* Use the one from the current process */
++ StartupInfo.lpDesktop = Peb->ProcessParameters->DesktopInfo.Buffer;
++ }
+
- /* Start over */
- goto WhiteScan;
++ /* Create a section for this file */
++ Status = NtCreateSection(&SectionHandle,
++ SECTION_ALL_ACCESS,
++ NULL,
++ NULL,
++ PAGE_EXECUTE,
++ SEC_IMAGE,
++ FileHandle);
++ DPRINT1("Section status: %lx\n", Status);
++ if (NT_SUCCESS(Status))
++ {
++ /* Are we running on Windows Embedded, Datacenter, Blade or Starter? */
++ if (SharedUserData->SuiteMask & (VER_SUITE_EMBEDDEDNT |
++ VER_SUITE_DATACENTER |
++ VER_SUITE_PERSONAL |
++ VER_SUITE_BLADE))
++ {
++ /* These SKUs do not allow running certain applications */
++ Status = BasepCheckWebBladeHashes(FileHandle);
++ if (Status == STATUS_ACCESS_DENIED)
++ {
++ /* And this is one of them! */
++ DPRINT1("Invalid Blade hashes!\n");
++ SetLastError(ERROR_ACCESS_DISABLED_WEBBLADE);
++ Result = FALSE;
++ goto Quickie;
++ }
+
- /* We totally failed */
- goto Cleanup;
++ /* Did we get some other failure? */
++ if (!NT_SUCCESS(Status))
++ {
++ /* If we couldn't check the hashes, assume nefariousness */
++ DPRINT1("Tampered Blade hashes!\n");
++ SetLastError(ERROR_ACCESS_DISABLED_WEBBLADE_TAMPER);
++ Result = FALSE;
++ goto Quickie;
+ }
++ }
+
- /* Put back the command line */
- *NullBuffer = SaveChar;
- lpApplicationName = NameBuffer;
- DPRINT("SearchPathW suceeded (%ld): %S\n", RetVal, NameBuffer);
++ /* Now do Winsafer, etc, checks */
++ Status = BasepIsProcessAllowed((LPWSTR)lpApplicationName);
++ if (!NT_SUCCESS(Status))
++ {
++ /* Fail if we're not allowed to launch the process */
++ DPRINT1("Process not allowed to launch: %lx\n", Status);
++ BaseSetLastNTError(Status);
++ if (SectionHandle)
++ {
++ NtClose(SectionHandle);
++ SectionHandle = NULL;
++ }
++ Result = FALSE;
++ goto Quickie;
+ }
+
- else if (!lpCommandLine || *lpCommandLine == UNICODE_NULL)
++ /* Is a DOS VDM being forced, but we already have a WOW32 instance ready? */
++ if ((dwCreationFlags & CREATE_FORCEDOS) &&
++ (BaseStaticServerData->IsWowTaskReady))
++ {
++ /* This request can't be satisfied, instead, a separate VDM is needed */
++ dwCreationFlags &= ~(CREATE_FORCEDOS | CREATE_SHARED_WOW_VDM);
++ dwCreationFlags |= CREATE_SEPARATE_WOW_VDM;
++
++ /* Set a failure code, ask for VDM reservation */
++ Status = STATUS_INVALID_IMAGE_WIN_16;
++ UseVdmReserve = TRUE;
++
++ /* Close the current handle */
++ NtClose(SectionHandle);
++ SectionHandle = NULL;
++
++ /* Don't query the section later */
++ QuerySection = FALSE;
++ }
+ }
- /* We have an app name (good!) but no command line */
- CmdLineIsAppName = TRUE;
- lpCommandLine = (LPWSTR)lpApplicationName;
++
++ /* Did we already do these checks? */
++ if (!SkipSaferAndAppCompat)
+ {
- /* At this point the name has been toyed with enough to be openable */
- Status = BasepMapFile(lpApplicationName, &hSection, &ApplicationName);
++ /* Is everything OK so far, OR do we have an non-MZ, non-DOS app? */
++ if ((NT_SUCCESS(Status)) ||
++ ((Status == STATUS_INVALID_IMAGE_NOT_MZ) &&
++ !(BaseIsDosApplication(&PathName, Status))))
++ {
++ /* Clear the machine type in case of failure */
++ ImageMachine = 0;
++
++ /* Clean any app compat data that may have accumulated */
++ BasepFreeAppCompatData(AppCompatData, AppCompatSxsData);
++ AppCompatData = NULL;
++ AppCompatSxsData = NULL;
++
++ /* Do we have a section? */
++ if (SectionHandle)
++ {
++ /* Have we already queried it? */
++ if (QuerySection)
++ {
++ /* Nothing to do */
++ Status = STATUS_SUCCESS;
++ }
++ else
++ {
++ /* Get some information about the executable */
++ Status = NtQuerySection(SectionHandle,
++ SectionImageInformation,
++ &ImageInformation,
++ sizeof(ImageInformation),
++ NULL);
++ }
++
++ /* Do we have section information now? */
++ if (NT_SUCCESS(Status))
++ {
++ /* Don't ask for it again, save the machine type */
++ QuerySection = TRUE;
++ ImageMachine = ImageInformation.Machine;
++ }
++ }
++
++ /* Is there a reason/Shim we shouldn't run this application? */
++ Status = BasepCheckBadapp(FileHandle,
++ FreeBuffer,
++ lpEnvironment,
++ ImageMachine,
++ &AppCompatData,
++ &AppCompatDataSize,
++ &AppCompatSxsData,
++ &AppCompatSxsDataSize,
++ &FusionFlags);
++ if (!NT_SUCCESS(Status))
++ {
++ /* This is usually the status we get back */
++ DPRINT1("App compat launch failure: %lx\n", Status);
++ if (Status == STATUS_ACCESS_DENIED)
++ {
++ /* Convert it to something more Win32-specific */
++ SetLastError(ERROR_CANCELLED);
++ }
++ else
++ {
++ /* Some other error */
++ BaseSetLastNTError(Status);
++ }
++
++ /* Did we have a section? */
++ if (SectionHandle)
++ {
++ /* Clean it up */
++ NtClose(SectionHandle);
++ SectionHandle = NULL;
++ }
++
++ /* Fail the call */
++ Result = FALSE;
++ goto Quickie;
++ }
++ }
+ }
+
- /* Check for failure */
- if (!NT_SUCCESS(Status))
++ //ASSERT((dwFusionFlags & ~SXS_APPCOMPACT_FLAG_APP_RUNNING_SAFEMODE) == 0);
+
- /* Could be a non-PE File */
++ /* Have we already done, and do we need to do, SRP (WinSafer) checks? */
++ if (!(SkipSaferAndAppCompat) &&
++ ~(dwCreationFlags & CREATE_PRESERVE_CODE_AUTHZ_LEVEL))
+ {
- /* Check if the Kernel tells us it's not even valid MZ */
++ /* Assume yes */
++ SaferNeeded = TRUE;
+ switch (Status)
+ {
- /* If it's a DOS app, use VDM */
- if (BaseIsDosApplication(&ApplicationName, Status))
+ case STATUS_INVALID_IMAGE_NE_FORMAT:
+ case STATUS_INVALID_IMAGE_PROTECT:
++ case STATUS_INVALID_IMAGE_WIN_16:
++ case STATUS_FILE_IS_OFFLINE:
++ /* For all DOS, 16-bit, OS/2 images, we do*/
++ break;
++
+ case STATUS_INVALID_IMAGE_NOT_MZ:
++ /* For invalid files, we don't, unless it's a .BAT file */
++ if (BaseIsDosApplication(&PathName, Status)) break;
++
++ default:
++ /* Any other error codes we also don't */
++ if (!NT_SUCCESS(Status))
++ {
++ SaferNeeded = FALSE;
++ }
++
++ /* But for success, we do */
++ break;
++ }
+
- DPRINT1("Launching VDM...\n");
- RtlFreeHeap(RtlGetProcessHeap(), 0, NameBuffer);
- RtlFreeHeap(RtlGetProcessHeap(), 0, ApplicationName.Buffer);
- return CreateProcessW(VdmPath,
- (LPWSTR)((ULONG_PTR)lpApplicationName), /* FIXME: Buffer must be writable!!! */
- lpProcessAttributes,
- lpThreadAttributes,
- bInheritHandles,
- dwCreationFlags,
- lpEnvironment,
- lpCurrentDirectory,
- &StartupInfo,
- lpProcessInformation);
++ /* Okay, so what did the checks above result in? */
++ if (SaferNeeded)
++ {
++ /* We have to call into the WinSafer library and actually check */
++ Status = BasepCheckWinSaferRestrictions(hUserToken,
++ (LPWSTR)lpApplicationName,
++ FileHandle,
++ &InJob,
++ &TokenHandle,
++ &JobHandle);
++ if (Status == 0xFFFFFFFF)
+ {
- /* It's a batch file */
- Extension = &ApplicationName.Buffer[ApplicationName.Length /
- sizeof(WCHAR) - 4];
++ /* Back in 2003, they didn't have an NTSTATUS for this... */
++ DPRINT1("WinSafer blocking process launch\n");
++ SetLastError(ERROR_ACCESS_DISABLED_BY_POLICY);
++ Result = FALSE;
++ goto Quickie;
+ }
- /* Make sure the extensions are correct */
- if (_wcsnicmp(Extension, L".bat", 4) && _wcsnicmp(Extension, L".cmd", 4))
+
- SetLastError(ERROR_BAD_EXE_FORMAT);
- return FALSE;
++ /* Other status codes are not-Safer related, just convert them */
++ if (!NT_SUCCESS(Status))
+ {
- /* Calculate the length of the command line */
- CmdLineLength = wcslen(CMD_STRING) + wcslen(lpCommandLine) + 1;
++ DPRINT1("Error checking WinSafer: %lx\n", Status);
++ BaseSetLastNTError(Status);
++ Result = FALSE;
++ goto Quickie;
+ }
++ }
++ }
+
- /* If we found quotes, then add them into the length size */
- if (CmdLineIsAppName || FoundQuotes) CmdLineLength += 2;
- CmdLineLength *= sizeof(WCHAR);
++ /* The last step is to figure out why the section object was not created */
++ switch (Status)
++ {
++ case STATUS_INVALID_IMAGE_WIN_16:
++ /* 16-bit binary. Should we use WOW or does the caller force VDM? */
++ if (!(dwCreationFlags & CREATE_FORCEDOS))
++ {
++ /* Remember that we're launching WOW */
++ IsWowApp = TRUE;
++
++ /* Create the VDM environment, it's valid for WOW too */
++ Result = BaseCreateVDMEnvironment(lpEnvironment,
++ &VdmAnsiEnv,
++ &VdmUnicodeEnv);
++ if (!Result)
++ {
++ DPRINT1("VDM environment for WOW app failed\n");
++ goto Quickie;
++ }
+
- /* Allocate space for the new command line */
- BatchCommandLine = RtlAllocateHeap(RtlGetProcessHeap(),
- 0,
- CmdLineLength);
- if (BatchCommandLine == NULL)
- {
- SetLastError(ERROR_NOT_ENOUGH_MEMORY);
- goto Cleanup;
++ /* We're going to try this twice, so do a loop */
++ while (TRUE)
++ {
++ /* Pick which kind of WOW mode we want to run in */
++ VdmBinaryType = (dwCreationFlags &
++ CREATE_SEPARATE_WOW_VDM) ?
++ BINARY_TYPE_WOW : BINARY_TYPE_SEPARATE_WOW;
++
++ /* Get all the VDM settings and current status */
++ Status = BaseCheckVDM(VdmBinaryType,
++ lpApplicationName,
++ lpCommandLine,
++ lpCurrentDirectory,
++ &VdmAnsiEnv,
++ (PCSR_API_MESSAGE)VdmMsg,
++ &VdmTask,
++ dwCreationFlags,
++ &StartupInfo,
++ hUserToken);
++
++ /* If it worked, no need to try again */
++ if (NT_SUCCESS(Status)) break;
++
++ /* Check if it's disallowed or if it's our second time */
++ BaseSetLastNTError(Status);
++ if ((Status == STATUS_VDM_DISALLOWED) ||
++ (VdmBinaryType == BINARY_TYPE_SEPARATE_WOW) ||
++ (GetLastError() == ERROR_ACCESS_DENIED))
++ {
++ /* Fail the call -- we won't try again */
++ DPRINT1("VDM message failure for WOW: %lx\n", Status);
++ Result = FALSE;
++ goto Quickie;
++ }
+
- /* Build it */
- wcscpy(BatchCommandLine, CMD_STRING);
- if (CmdLineIsAppName || FoundQuotes)
++ /* Try one more time, but with a separate WOW instance */
++ dwCreationFlags |= CREATE_SEPARATE_WOW_VDM;
++ }
++
++ /* Check which VDM state we're currently in */
++ switch (VdmMsg->VDMState & (VDM_NOT_LOADED |
++ VDM_NOT_READY |
++ VDM_READY))
++ {
++ case VDM_NOT_LOADED:
++ /* VDM is not fully loaded, so not that much to undo */
++ VdmUndoLevel = VDM_UNDO_PARTIAL;
++
++ /* Reset VDM reserve if needed */
++ if (UseVdmReserve) VdmReserve = 1;
++
++ /* Get the required parameters and names for launch */
++ Result = BaseGetVdmConfigInfo(lpCommandLine,
++ VdmTask,
++ VdmBinaryType,
++ &VdmString,
++ &VdmReserve);
++ if (!Result)
++ {
++ DPRINT1("VDM Configuration failed for WOW\n");
++ BaseSetLastNTError(Status);
++ goto Quickie;
++ }
++
++ /* Update the command-line with the VDM one instead */
++ lpCommandLine = VdmString.Buffer;
++ lpApplicationName = NULL;
++
++ /* We don't want a console, detachment, nor a window */
++ dwCreationFlags |= CREATE_NO_WINDOW;
++ dwCreationFlags &= ~(CREATE_NEW_CONSOLE | DETACHED_PROCESS);
++
++ /* Force feedback on */
++ StartupInfo.dwFlags |= STARTF_FORCEONFEEDBACK;
++ break;
++
++
++ case VDM_READY:
++ /* VDM is ready, so we have to undo everything */
++ VdmUndoLevel = VDM_UNDO_REUSE;
++
++ /* Check if CSRSS wants us to wait on VDM */
++ VdmWaitObject = VdmMsg->WaitObjectForParent;
++ break;
++
++ case VDM_NOT_READY:
++ /* Something is wrong with VDM, we'll fail the call */
++ DPRINT1("VDM is not ready for WOW\n");
++ SetLastError(ERROR_NOT_READY);
++ Result = FALSE;
++ goto Quickie;
++
++ default:
++ break;
++ }
++
++ /* Since to get NULL, we allocate from 0x1, account for this */
++ VdmReserve--;
++
++ /* This implies VDM is ready, so skip everything else */
++ if (VdmWaitObject) goto VdmShortCircuit;
++
++ /* Don't inherit handles since we're doing VDM now */
++ bInheritHandles = FALSE;
++
++ /* Had the user passed in environment? If so, destroy it */
++ if ((lpEnvironment) &&
++ !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT))
++ {
++ RtlDestroyEnvironment(lpEnvironment);
++ }
++
++ /* We've already done all these checks, don't do them again */
++ SkipSaferAndAppCompat = TRUE;
++ goto AppNameRetry;
+ }
+
- wcscat(BatchCommandLine, L"\"");
++ // There is no break here on purpose, so FORCEDOS drops down!
++ case STATUS_INVALID_IMAGE_PROTECT:
++ case STATUS_INVALID_IMAGE_NOT_MZ:
++ case STATUS_INVALID_IMAGE_NE_FORMAT:
++ /* We're launching an executable application */
++ BinarySubType = BINARY_TYPE_EXE;
++
++ /* We can drop here from other "cases" above too, so check */
++ if ((Status == STATUS_INVALID_IMAGE_PROTECT) ||
++ (Status == STATUS_INVALID_IMAGE_NE_FORMAT) ||
++ (BinarySubType = BaseIsDosApplication(&PathName, Status)))
+ {
- wcscat(BatchCommandLine, lpCommandLine);
- if (CmdLineIsAppName || FoundQuotes)
++ /* We're launching a DOS application */
++ VdmBinaryType = BINARY_TYPE_DOS;
++
++ /* Based on the caller environment, create a VDM one */
++ Result = BaseCreateVDMEnvironment(lpEnvironment,
++ &VdmAnsiEnv,
++ &VdmUnicodeEnv);
++ if (!Result)
++ {
++ DPRINT1("VDM environment for DOS failed\n");
++ goto Quickie;
++ }
++
++ /* Check the current state of the VDM subsystem */
++ Status = BaseCheckVDM(VdmBinaryType | BinarySubType,
++ lpApplicationName,
++ lpCommandLine,
++ lpCurrentDirectory,
++ &VdmAnsiEnv,
++ (PCSR_API_MESSAGE)VdmMsg,
++ &VdmTask,
++ dwCreationFlags,
++ &StartupInfo,
++ NULL);
++ if (!NT_SUCCESS(Status))
++ {
++ /* Failed to inquire about VDM, fail the call */
++ DPRINT1("VDM message failure for DOS: %lx\n", Status);
++ BaseSetLastNTError(Status);
++ Result = FALSE;
++ goto Quickie;
++ };
++
++ /* Handle possible VDM states */
++ switch (VdmMsg->VDMState & (VDM_NOT_LOADED |
++ VDM_NOT_READY |
++ VDM_READY))
++ {
++ case VDM_NOT_LOADED:
++ /* If VDM is not loaded, we'll do a partial undo */
++ VdmUndoLevel = VDM_UNDO_PARTIAL;
++
++ /* A VDM process can't also be detached, so fail */
++ if (dwCreationFlags & DETACHED_PROCESS)
++ {
++ DPRINT1("Detached process but no VDM, not allowed\n");
++ SetLastError(ERROR_ACCESS_DENIED);
++ return FALSE;
++ }
++
++ /* Get the required parameters and names for launch */
++ Result = BaseGetVdmConfigInfo(lpCommandLine,
++ VdmTask,
++ VdmBinaryType,
++ &VdmString,
++ &VdmReserve);
++ if (!Result)
++ {
++ DPRINT1("VDM Configuration failed for DOS\n");
++ BaseSetLastNTError(Status);
++ goto Quickie;
++ }
++
++ /* Update the command-line to launch VDM instead */
++ lpCommandLine = VdmString.Buffer;
++ lpApplicationName = NULL;
++ break;
++
++ case VDM_READY:
++ /* VDM is ready, so we have to undo everything */
++ VdmUndoLevel = VDM_UNDO_REUSE;
++
++ /* Check if CSRSS wants us to wait on VDM */
++ VdmWaitObject = VdmMsg->WaitObjectForParent;
++ break;
++
++ case VDM_NOT_READY:
++ /* Something is wrong with VDM, we'll fail the call */
++ DPRINT1("VDM is not ready for DOS\n");
++ SetLastError(ERROR_NOT_READY);
++ Result = FALSE;
++ goto Quickie;
++
++ default:
++ break;
++ }
++
++ /* Since to get NULL, we allocate from 0x1, account for this */
++ VdmReserve--;
++
++ /* This implies VDM is ready, so skip everything else */
++ if (VdmWaitObject) goto VdmShortCircuit;
++
++ /* Don't inherit handles since we're doing VDM now */
++ bInheritHandles = FALSE;
++
++ /* Had the user passed in environment? If so, destroy it */
++ if ((lpEnvironment) &&
++ !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT))
++ {
++ RtlDestroyEnvironment(lpEnvironment);
++ }
++
++ /* Use our VDM Unicode environment instead */
++ lpEnvironment = VdmUnicodeEnv.Buffer;
+ }
- wcscat(BatchCommandLine, L"\"");
- }
++ else
+ {
- /* Create it as a Unicode String */
- RtlInitUnicodeString(&CommandLineString, BatchCommandLine);
++ /* It's a batch file, get the extension */
++ ExtBuffer = &PathName.Buffer[PathName.Length / sizeof(WCHAR) - 4];
+
- /* Set the command line to this */
- lpCommandLine = CommandLineString.Buffer;
- lpApplicationName = NULL;
++ /* Make sure the extensions are correct */
++ if ((PathName.Length < (4 * sizeof(WCHAR))) ||
++ ((_wcsnicmp(ExtBuffer, L".bat", 4)) &&
++ (_wcsnicmp(ExtBuffer, L".cmd", 4))))
++ {
++ DPRINT1("Invalid EXE, and not a batch or script file\n");
++ SetLastError(ERROR_BAD_EXE_FORMAT);
++ Result = FALSE;
++ goto Quickie;
++ }
+
- /* Free memory */
- RtlFreeHeap(RtlGetProcessHeap(), 0, ApplicationName.Buffer);
- ApplicationName.Buffer = NULL;
- goto GetAppName;
- break;
++ /* Check if we need to account for quotes around the path */
++ CmdQuoteLength = CmdLineIsAppName || HasQuotes;
++ if (!CmdLineIsAppName)
++ {
++ if (HasQuotes) CmdQuoteLength++;
++ }
++ else
++ {
++ CmdQuoteLength++;
++ }
+
- case STATUS_INVALID_IMAGE_WIN_16:
++ /* Calculate the length of the command line */
++ CmdLineLength = wcslen(lpCommandLine);
++ CmdLineLength += wcslen(CMD_STRING);
++ CmdLineLength += CmdQuoteLength + sizeof(ANSI_NULL);
++ CmdLineLength *= sizeof(WCHAR);
++
++ /* Allocate space for the new command line */
++ AnsiCmdCommand = RtlAllocateHeap(RtlGetProcessHeap(),
++ 0,
++ CmdLineLength);
++ if (!AnsiCmdCommand)
++ {
++ BaseSetLastNTError(STATUS_NO_MEMORY);
++ Result = FALSE;
++ goto Quickie;
++ }
+
- /* It's a Win16 Image, use VDM */
- DPRINT1("Launching VDM...\n");
- RtlFreeHeap(RtlGetProcessHeap(), 0, NameBuffer);
- RtlFreeHeap(RtlGetProcessHeap(), 0, ApplicationName.Buffer);
- return CreateProcessW(VdmPath,
- (LPWSTR)((ULONG_PTR)lpApplicationName), /* FIXME: Buffer must be writable!!! */
- lpProcessAttributes,
- lpThreadAttributes,
- bInheritHandles,
- dwCreationFlags,
- lpEnvironment,
- lpCurrentDirectory,
- &StartupInfo,
- lpProcessInformation);
++ /* Build it */
++ wcscpy(AnsiCmdCommand, CMD_STRING);
++ if ((CmdLineIsAppName) || (HasQuotes))
++ {
++ wcscat(AnsiCmdCommand, L"\"");
++ }
++ wcscat(AnsiCmdCommand, lpCommandLine);
++ if ((CmdLineIsAppName) || (HasQuotes))
++ {
++ wcscat(AnsiCmdCommand, L"\"");
++ }
+
- case STATUS_OBJECT_NAME_NOT_FOUND:
- case STATUS_OBJECT_PATH_NOT_FOUND:
- BaseSetLastNTError(Status);
- goto Cleanup;
++ /* Create it as a Unicode String */
++ RtlInitUnicodeString(&DebuggerString, AnsiCmdCommand);
+
- default:
- /* Invalid Image Type */
++ /* Set the command line to this */
++ lpCommandLine = DebuggerString.Buffer;
++ lpApplicationName = NULL;
++ DPRINT1("Retrying with: %S\n", lpCommandLine);
++ }
+
- goto Cleanup;
- }
++ /* We've already done all these checks, don't do them again */
++ SkipSaferAndAppCompat = TRUE;
++ goto AppNameRetry;
++
++ case STATUS_INVALID_IMAGE_WIN_64:
++ /* 64-bit binaries are not allowed to run on 32-bit ReactOS */
++ DPRINT1("64-bit binary, failing\n");
++ SetLastError(ERROR_EXE_MACHINE_TYPE_MISMATCH);
++ Result = FALSE;
++ goto Quickie;
++
++ case STATUS_FILE_IS_OFFLINE:
++ /* Set the correct last error for this */
++ DPRINT1("File is offline, failing\n");
++ SetLastError(ERROR_FILE_OFFLINE);
++ break;
++
++ default:
++ /* Any other error, convert it to a generic Win32 error */
++ if (!NT_SUCCESS(Status))
++ {
++ DPRINT1("Failed to create section: %lx\n", Status);
+ SetLastError(ERROR_BAD_EXE_FORMAT);
- /* Use our desktop if we didn't get any */
- if (!StartupInfo.lpDesktop)
++ Result = FALSE;
++ goto Quickie;
++ }
++
++ /* Otherwise, this must be success */
++ ASSERT(Status == STATUS_SUCCESS);
++ break;
+ }
+
- StartupInfo.lpDesktop = OurPeb->ProcessParameters->DesktopInfo.Buffer;
++ /* Is this not a WOW application, but a WOW32 VDM was requested for it? */
++ if (!(IsWowApp) && (dwCreationFlags & CREATE_SEPARATE_WOW_VDM))
+ {
- /* FIXME: Check if Application is allowed to run */
++ /* Ignore the nonsensical request */
++ dwCreationFlags &= ~CREATE_SEPARATE_WOW_VDM;
+ }
+
- /* FIXME: Allow CREATE_SEPARATE only for WOW Apps, once we have that. */
++ /* Did we already check information for the section? */
++ if (!QuerySection)
++ {
++ /* Get some information about the executable */
++ Status = NtQuerySection(SectionHandle,
++ SectionImageInformation,
++ &ImageInformation,
++ sizeof(ImageInformation),
++ NULL);
++ if (!NT_SUCCESS(Status))
++ {
++ /* We failed, bail out */
++ DPRINT1("Section query failed\n");
++ BaseSetLastNTError(Status);
++ Result = FALSE;
++ goto Quickie;
++ }
+
- /* Get some information about the executable */
- Status = NtQuerySection(hSection,
- SectionImageInformation,
- &SectionImageInfo,
- sizeof(SectionImageInfo),
- NULL);
- if(!NT_SUCCESS(Status))
++ /* Don't check this later */
++ QuerySection = TRUE;
++ }
+
- DPRINT1("Unable to get SectionImageInformation, status 0x%x\n", Status);
- BaseSetLastNTError(Status);
- goto Cleanup;
++ /* Check if this was linked as a DLL */
++ if (ImageInformation.ImageCharacteristics & IMAGE_FILE_DLL)
+ {
- /* Don't execute DLLs */
- if (SectionImageInfo.ImageCharacteristics & IMAGE_FILE_DLL)
++ /* These aren't valid images to try to execute! */
++ DPRINT1("Trying to launch a DLL, failing\n");
++ SetLastError(ERROR_BAD_EXE_FORMAT);
++ Result = FALSE;
++ goto Quickie;
+ }
+
- DPRINT1("Can't execute a DLL\n");
- SetLastError(ERROR_BAD_EXE_FORMAT);
- goto Cleanup;
++ /* Don't let callers pass in this flag -- we'll only get it from IFRO */
++ Flags &= ~PROCESS_CREATE_FLAGS_LARGE_PAGES;
++
++ /* Clear the IFEO-missing flag, before we know for sure... */
++ ParameterFlags &= ~2;
++
++ /* If the process is being debugged, only read IFEO if the PEB says so */
++ if (!(dwCreationFlags & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS)) ||
++ (NtCurrentPeb()->ReadImageFileExecOptions))
+ {
- /* FIXME: Check for Debugger */
++ /* Let's do this! Attempt to open IFEO */
++ Status1 = LdrOpenImageFileOptionsKey(&PathName, 0, &KeyHandle);
++ if (!NT_SUCCESS(Status1))
++ {
++ /* We failed, set the flag so we store this in the parameters */
++ if (Status1 == STATUS_OBJECT_NAME_NOT_FOUND) ParameterFlags |= 2;
++ }
++ else
++ {
++ /* Was this our first time going through this path? */
++ if (!DebuggerCmdLine)
++ {
++ /* Allocate a buffer for the debugger path */
++ DebuggerCmdLine = RtlAllocateHeap(RtlGetProcessHeap(),
++ 0,
++ MAX_PATH * sizeof(WCHAR));
++ if (!DebuggerCmdLine)
++ {
++ /* Close IFEO on failure */
++ Status1 = NtClose(KeyHandle);
++ ASSERT(NT_SUCCESS(Status1));
++
++ /* Fail the call */
++ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
++ Result = FALSE;
++ goto Quickie;
++ }
++ }
++
++ /* Now query for the debugger */
++ Status1 = LdrQueryImageFileKeyOption(KeyHandle,
++ L"Debugger",
++ REG_SZ,
++ DebuggerCmdLine,
++ MAX_PATH * sizeof(WCHAR),
++ &ResultSize);
++ if (!(NT_SUCCESS(Status1)) ||
++ (ResultSize < sizeof(WCHAR)) ||
++ (DebuggerCmdLine[0] == UNICODE_NULL))
++ {
++ /* If it's not there, or too small, or invalid, ignore it */
++ RtlFreeHeap(RtlGetProcessHeap(), 0, DebuggerCmdLine);
++ DebuggerCmdLine = NULL;
++ }
++
++ /* Also query if we should map with large pages */
++ Status1 = LdrQueryImageFileKeyOption(KeyHandle,
++ L"UseLargePages",
++ REG_DWORD,
++ &UseLargePages,
++ sizeof(UseLargePages),
++ NULL);
++ if ((NT_SUCCESS(Status1)) && (UseLargePages))
++ {
++ /* Do it! This is the only way this flag can be set */
++ Flags |= PROCESS_CREATE_FLAGS_LARGE_PAGES;
++ }
++
++ /* We're done with IFEO, can close it now */
++ Status1 = NtClose(KeyHandle);
++ ASSERT(NT_SUCCESS(Status1));
++ }
+ }
+
- /* FIXME: Check if Machine Type and SubSys Version Match */
++ /* Make sure the image was compiled for this processor */
++ if ((ImageInformation.Machine < SharedUserData->ImageNumberLow) ||
++ (ImageInformation.Machine > SharedUserData->ImageNumberHigh))
++ {
++ /* It was not -- raise a hard error */
++ ErrorResponse = ResponseOk;
++ ErrorParameters[0] = (ULONG_PTR)&PathName;
++ NtRaiseHardError(STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE,
++ 1,
++ 1,
++ ErrorParameters,
++ OptionOk,
++ &ErrorResponse);
++ if (Peb->ImageSubsystemMajorVersion <= 3)
++ {
++ /* If it's really old, return this error */
++ SetLastError(ERROR_BAD_EXE_FORMAT);
++ }
++ else
++ {
++ /* Otherwise, return a more modern error */
++ SetLastError(ERROR_EXE_MACHINE_TYPE_MISMATCH);
++ }
+
- /* We don't support POSIX or anything else for now */
- if (IMAGE_SUBSYSTEM_WINDOWS_GUI != SectionImageInfo.SubSystemType &&
- IMAGE_SUBSYSTEM_WINDOWS_CUI != SectionImageInfo.SubSystemType)
++ /* Go to the failure path */
++ DPRINT1("Invalid image architecture: %lx\n", ImageInformation.Machine);
++ Result = FALSE;
++ goto Quickie;
++ }
+
- DPRINT1("Invalid subsystem %d\n", SectionImageInfo.SubSystemType);
- /*
- * Despite the name of the error code suggests, it corresponds to the
- * well-known "The %1 application cannot be run in Win32 mode" message.
- */
- SetLastError(ERROR_CHILD_NOT_COMPLETE);
- goto Cleanup;
++ /* Check if this isn't a Windows image */
++ if ((ImageInformation.SubSystemType != IMAGE_SUBSYSTEM_WINDOWS_GUI) &&
++ (ImageInformation.SubSystemType != IMAGE_SUBSYSTEM_WINDOWS_CUI))
+ {
- if (IMAGE_SUBSYSTEM_WINDOWS_GUI == SectionImageInfo.SubSystemType)
++ /* Get rid of section-related information since we'll retry */
++ NtClose(SectionHandle);
++ SectionHandle = NULL;
++ QuerySection = FALSE;
++
++ /* The only other non-Windows image type we support here is POSIX */
++ if (ImageInformation.SubSystemType != IMAGE_SUBSYSTEM_POSIX_CUI)
++ {
++ /* Bail out if it's something else */
++ SetLastError(ERROR_CHILD_NOT_COMPLETE);
++ Result = FALSE;
++ goto Quickie;
++ }
++
++ /* Now build the command-line to have posix launch this image */
++ Result = BuildSubSysCommandLine(L"POSIX /P ",
++ lpApplicationName,
++ lpCommandLine,
++ &DebuggerString);
++ if (!Result)
++ {
++ /* Bail out if that failed */
++ DPRINT1("Subsystem command line failed\n");
++ goto Quickie;
++ }
++
++ /* And re-try launching the process, with the new command-line now */
++ lpCommandLine = DebuggerString.Buffer;
++ lpApplicationName = NULL;
++
++ /* We've already done all these checks, don't do them again */
++ SkipSaferAndAppCompat = TRUE;
++ DPRINT1("Retrying with: %S\n", lpCommandLine);
++ goto AppNameRetry;
++ }
++
++ /* Was this image built for a version of Windows whose images we can run? */
++ Result = BasepIsImageVersionOk(ImageInformation.SubSystemMajorVersion,
++ ImageInformation.SubSystemMinorVersion);
++ if (!Result)
++ {
++ /* It was not, bail out */
++ DPRINT1("Invalid subsystem version: %d.%d\n",
++ ImageInformation.SubSystemMajorVersion,
++ ImageInformation.SubSystemMinorVersion);
++ SetLastError(ERROR_BAD_EXE_FORMAT);
++ goto Quickie;
+ }
+
- /* Do not create a console for GUI applications */
- dwCreationFlags &= ~CREATE_NEW_CONSOLE;
- dwCreationFlags |= DETACHED_PROCESS;
++ /* Check if there is a debugger associated with the application */
++ if (DebuggerCmdLine)
+ {
- lpProcessAttributes,
- NULL);
++ /* Get the length of the command line */
++ n = wcslen(lpCommandLine);
++ if (!n)
++ {
++ /* There's no command line, use the application name instead */
++ lpCommandLine = (LPWSTR)lpApplicationName;
++ n = wcslen(lpCommandLine);
++ }
++
++ /* Protect against overflow */
++ if (n > UNICODE_STRING_MAX_CHARS)
++ {
++ BaseSetLastNTError(STATUS_NAME_TOO_LONG);
++ Result = FALSE;
++ goto Quickie;
++ }
++
++ /* Now add the length of the debugger command-line */
++ n += wcslen(DebuggerCmdLine);
++
++ /* Again make sure we don't overflow */
++ if (n > UNICODE_STRING_MAX_CHARS)
++ {
++ BaseSetLastNTError(STATUS_NAME_TOO_LONG);
++ Result = FALSE;
++ goto Quickie;
++ }
++
++ /* Account for the quotes and space between the two */
++ n += ((sizeof('""') * 2) + sizeof(' '));
++
++ /* Convert to bytes, and make sure we don't overflow */
++ n *= sizeof(WCHAR);
++ if (n > UNICODE_STRING_MAX_BYTES)
++ {
++ BaseSetLastNTError(STATUS_NAME_TOO_LONG);
++ Result = FALSE;
++ goto Quickie;
++ }
++
++ /* Allocate space for the string */
++ DebuggerString.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, n);
++ if (!DebuggerString.Buffer)
++ {
++ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
++ Result = FALSE;
++ goto Quickie;
++ }
++
++ /* Set the length */
++ RtlInitEmptyUnicodeString(&DebuggerString,
++ DebuggerString.Buffer,
++ n);
++
++ /* Now perform the command line creation */
++ ImageDbgStatus = RtlAppendUnicodeToString(&DebuggerString,
++ DebuggerCmdLine);
++ ASSERT(NT_SUCCESS(ImageDbgStatus));
++ ImageDbgStatus = RtlAppendUnicodeToString(&DebuggerString, L" ");
++ ASSERT(NT_SUCCESS(ImageDbgStatus));
++ ImageDbgStatus = RtlAppendUnicodeToString(&DebuggerString, lpCommandLine);
++ ASSERT(NT_SUCCESS(ImageDbgStatus));
++
++ /* Make sure it all looks nice */
++ DbgPrint("BASE: Calling debugger with '%wZ'\n", &DebuggerString);
++
++ /* Update the command line and application name */
++ lpCommandLine = DebuggerString.Buffer;
++ lpApplicationName = NULL;
++
++ /* Close all temporary state */
++ NtClose(SectionHandle);
++ SectionHandle = NULL;
++ QuerySection = FALSE;
++
++ /* Free all temporary memory */
++ RtlFreeHeap(RtlGetProcessHeap(), 0, NameBuffer);
++ NameBuffer = NULL;
++ RtlFreeHeap(RtlGetProcessHeap(), 0, FreeBuffer);
++ FreeBuffer = NULL;
++ RtlFreeHeap(RtlGetProcessHeap(), 0, DebuggerCmdLine);
++ DebuggerCmdLine = NULL;
++ DPRINT1("Retrying with: %S\n", lpCommandLine);
++ goto AppNameRetry;
+ }
+
+ /* Initialize the process object attributes */
+ ObjectAttributes = BaseFormatObjectAttributes(&LocalObjectAttributes,
- /* FIXME: Set process flag */
++ lpProcessAttributes,
++ NULL);
++ if ((hUserToken) && (lpProcessAttributes))
++ {
++ /* Auggment them with information from the user */
++
++ LocalProcessAttributes = *lpProcessAttributes;
++ LocalProcessAttributes.lpSecurityDescriptor = NULL;
++ ObjectAttributes = BaseFormatObjectAttributes(&LocalObjectAttributes,
++ &LocalProcessAttributes,
++ NULL);
++ }
+
+ /* Check if we're going to be debugged */
+ if (dwCreationFlags & DEBUG_PROCESS)
+ {
- goto Cleanup;
++ /* Set process flag */
++ Flags |= PROCESS_CREATE_FLAGS_BREAKAWAY;
+ }
+
+ /* Check if we're going to be debugged */
+ if (dwCreationFlags & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS))
+ {
+ /* Connect to DbgUi */
+ Status = DbgUiConnectToDbg();
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT1("Failed to connect to DbgUI!\n");
+ BaseSetLastNTError(Status);
- hDebug = DbgUiGetThreadDebugObject();
++ Result = FALSE;
++ goto Quickie;
+ }
+
+ /* Get the debug object */
- hDebug = (HANDLE)((ULONG_PTR)hDebug | 0x1);
++ DebugHandle = DbgUiGetThreadDebugObject();
+
+ /* Check if only this process will be debugged */
+ if (dwCreationFlags & DEBUG_ONLY_THIS_PROCESS)
+ {
+ /* Set process flag */
- /* Create the Process */
- Status = NtCreateProcess(&hProcess,
- PROCESS_ALL_ACCESS,
- ObjectAttributes,
- NtCurrentProcess(),
- (BOOLEAN)bInheritHandles,
- hSection,
- hDebug,
- NULL);
++ Flags |= PROCESS_CREATE_FLAGS_NO_DEBUG_INHERIT;
+ }
+ }
+
- DPRINT1("Unable to create process, status 0x%x\n", Status);
++ /* Set inherit flag */
++ if (bInheritHandles) Flags |= PROCESS_CREATE_FLAGS_INHERIT_HANDLES;
++
++ /* Check if the process should be created with large pages */
++ HavePrivilege = FALSE;
++ PrivilegeState = NULL;
++ if (Flags & PROCESS_CREATE_FLAGS_LARGE_PAGES)
++ {
++ /* Acquire the required privilege so that the kernel won't fail the call */
++ PrivilegeValue = SE_LOCK_MEMORY_PRIVILEGE;
++ Status = RtlAcquirePrivilege(&PrivilegeValue, TRUE, FALSE, &PrivilegeState);
++ if (NT_SUCCESS(Status))
++ {
++ /* Remember to release it later */
++ HavePrivilege = TRUE;
++ }
++ }
++
++ /* Save the current TIB value since kernel overwrites it to store PEB */
++ TibValue = Teb->NtTib.ArbitraryUserPointer;
++
++ /* Tell the kernel to create the process */
++ Status = NtCreateProcessEx(&ProcessHandle,
++ PROCESS_ALL_ACCESS,
++ ObjectAttributes,
++ NtCurrentProcess(),
++ Flags,
++ SectionHandle,
++ DebugHandle,
++ NULL,
++ InJob);
++
++ /* Load the PEB address from the hacky location where the kernel stores it */
++ RemotePeb = Teb->NtTib.ArbitraryUserPointer;
++
++ /* And restore the old TIB value */
++ Teb->NtTib.ArbitraryUserPointer = TibValue;
++
++ /* Release the large page privilege if we had acquired it */
++ if (HavePrivilege) RtlReleasePrivilege(PrivilegeState);
++
++ /* And now check if the kernel failed to create the process */
+ if (!NT_SUCCESS(Status))
+ {
- goto Cleanup;
++ /* Go to failure path */
++ DPRINT1("Failed to create process: %lx\n", Status);
+ BaseSetLastNTError(Status);
- if (PriorityClass.PriorityClass != PROCESS_PRIORITY_CLASS_INVALID)
++ Result = FALSE;
++ goto Quickie;
+ }
+
- /* Set new class */
- Status = NtSetInformationProcess(hProcess,
++ /* Check if there is a priority class to set */
++ if (PriorityClass.PriorityClass)
+ {
- if(!NT_SUCCESS(Status))
++ /* Reset current privilege state */
++ RealTimePrivilegeState = NULL;
++
++ /* Is realtime priority being requested? */
++ if (PriorityClass.PriorityClass == REALTIME_PRIORITY_CLASS)
++ {
++ /* Check if the caller has real-time access, and enable it if so */
++ RealTimePrivilegeState = BasepIsRealtimeAllowed(TRUE);
++ }
++
++ /* Set the new priority class and release the privilege */
++ Status = NtSetInformationProcess(ProcessHandle,
+ ProcessPriorityClass,
+ &PriorityClass,
+ sizeof(PROCESS_PRIORITY_CLASS));
- DPRINT1("Unable to set new process priority, status 0x%x\n", Status);
++ if (RealTimePrivilegeState) RtlReleasePrivilege(RealTimePrivilegeState);
++
++ /* Check if we failed to set the priority class */
++ if (!NT_SUCCESS(Status))
+ {
- goto Cleanup;
++ /* Bail out on failure */
++ DPRINT1("Failed to set priority class: %lx\n", Status);
+ BaseSetLastNTError(Status);
- /* Set Error Mode */
++ Result = FALSE;
++ goto Quickie;
+ }
+ }
+
- ULONG ErrorMode = SEM_FAILCRITICALERRORS;
- NtSetInformationProcess(hProcess,
++ /* Check if the caller wants the default error mode */
+ if (dwCreationFlags & CREATE_DEFAULT_ERROR_MODE)
+ {
- &ErrorMode,
++ /* Set Error Mode to only fail on critical errors */
++ HardErrorMode = SEM_FAILCRITICALERRORS;
++ NtSetInformationProcess(ProcessHandle,
+ ProcessDefaultHardErrorMode,
- /* Convert the directory to a full path */
++ &HardErrorMode,
+ sizeof(ULONG));
+ }
+
- /* Allocate a buffer */
++ /* Check if this was a VDM binary */
++ if (VdmBinaryType)
++ {
++ /* Update VDM by telling it the process has now been created */
++ VdmWaitObject = ProcessHandle;
++ Result = BaseUpdateVDMEntry(VdmEntryUpdateProcess,
++ &VdmWaitObject,
++ VdmTask,
++ VdmBinaryType);
++ {
++ /* Bail out on failure */
++ DPRINT1("Failed to update VDM with wait object\n");
++ VdmWaitObject = NULL;
++ goto Quickie;
++ }
++
++ /* At this point, a failure means VDM has to undo all the state */
++ VdmUndoLevel |= VDM_UNDO_FULL;
++ }
++
++ /* Check if VDM needed reserved low-memory */
++ if (VdmReserve)
++ {
++ /* Reserve the requested allocation */
++ Status = NtAllocateVirtualMemory(ProcessHandle,
++ &BaseAddress,
++ 0,
++ &VdmReserve,
++ MEM_RESERVE,
++ PAGE_EXECUTE_READWRITE);
++ if (!NT_SUCCESS(Status))
++ {
++ /* Bail out on failure */
++ DPRINT1("Failed to reserved memory for VDM: %lx\n", Status);
++ BaseSetLastNTError(Status);
++ Result = FALSE;
++ goto Quickie;
++ }
++ }
++
++ /* Check if we've already queried information on the section */
++ if (!QuerySection)
++ {
++ /* We haven't, so get some information about the executable */
++ Status = NtQuerySection(SectionHandle,
++ SectionImageInformation,
++ &ImageInformation,
++ sizeof(ImageInformation),
++ NULL);
++ if (!NT_SUCCESS(Status))
++ {
++ /* Bail out on failure */
++ DPRINT1("Failed to query section: %lx\n", Status);
++ BaseSetLastNTError(Status);
++ Result = FALSE;
++ goto Quickie;
++ }
++
++ /* If we encounter a restart, don't re-query this information again */
++ QuerySection = TRUE;
++ }
++
++ /* Do we need to apply SxS to this image? */
++ if (!(ImageInformation.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_NO_ISOLATION))
++ {
++ /* Too bad, we don't support this yet */
++ DPRINT1("Image should receive SxS Fusion Isolation\n");
++ }
++
++ /* There's some SxS flag that we need to set if fusion flags have 1 set */
++ if (FusionFlags & 1) CreateProcessMsg->Sxs.Flags |= 0x10;
++
++ /* Check if we have a current directory */
+ if (lpCurrentDirectory)
+ {
- (MAX_PATH + 1) * sizeof(WCHAR));
- if (CurrentDirectory == NULL)
++ /* Allocate a buffer so we can keep a Unicode copy */
++ DPRINT1("Current directory: %S\n", lpCurrentDirectory);
+ CurrentDirectory = RtlAllocateHeap(RtlGetProcessHeap(),
+ 0,
- DPRINT1("Cannot allocate memory for directory name\n");
- SetLastError(ERROR_NOT_ENOUGH_MEMORY);
- goto Cleanup;
++ (MAX_PATH * sizeof(WCHAR)) +
++ sizeof(UNICODE_NULL));
++ if (!CurrentDirectory)
+ {
- /* Get the length */
- if (GetFullPathNameW(lpCurrentDirectory,
- MAX_PATH,
- CurrentDirectory,
- &CurrentDirectoryPart) > MAX_PATH)
++ /* Bail out if this failed */
++ BaseSetLastNTError(STATUS_NO_MEMORY);
++ Result = FALSE;
++ goto Quickie;
+ }
+
- DPRINT1("Directory name too long\n");
++ /* Get the length in Unicode */
++ Length = GetFullPathNameW(lpCurrentDirectory,
++ MAX_PATH,
++ CurrentDirectory,
++ &FilePart);
++ if (Length > MAX_PATH)
++ {
++ /* The directory is too long, so bail out */
++ SetLastError(ERROR_DIRECTORY);
++ Result = FALSE;
++ goto Quickie;
++ }
++
++ /* Make sure the directory is actually valid */
++ CurdirLength = GetFileAttributesW(CurrentDirectory);
++ if ((CurdirLength == 0xffffffff) ||
++ !(CurdirLength & FILE_ATTRIBUTE_DIRECTORY))
+ {
- goto Cleanup;
++ /* It isn't, so bail out */
++ DPRINT1("Current directory is invalid\n");
+ SetLastError(ERROR_DIRECTORY);
- if (QuotesNeeded || CmdLineIsAppName)
++ Result = FALSE;
++ goto Quickie;
+ }
+ }
+
+ /* Insert quotes if needed */
- /* Allocate a buffer */
++ if ((QuotesNeeded) || (CmdLineIsAppName))
+ {
- (wcslen(lpCommandLine) + 2 + 1) *
- sizeof(WCHAR));
- if (QuotedCmdLine == NULL)
++ /* Allocate our buffer, plus enough space for quotes and a NULL */
+ QuotedCmdLine = RtlAllocateHeap(RtlGetProcessHeap(),
+ 0,
- DPRINT1("Cannot allocate memory for quoted command line\n");
- SetLastError(ERROR_NOT_ENOUGH_MEMORY);
- goto Cleanup;
- }
++ (wcslen(lpCommandLine) * sizeof(WCHAR)) +
++ (2 * sizeof(L'\"') + sizeof(UNICODE_NULL)));
++ if (QuotedCmdLine)
+ {
- /* Copy the first quote */
- wcscpy(QuotedCmdLine, L"\"");
++ /* Copy the first quote */
++ wcscpy(QuotedCmdLine, L"\"");
++
++ /* Save the current null-character */
++ if (QuotesNeeded)
++ {
++ SaveChar = *NullBuffer;
++ *NullBuffer = UNICODE_NULL;
++ }
+
- /* Save a null char */
- if (QuotesNeeded)
++ /* Copy the command line and the final quote */
++ wcscat(QuotedCmdLine, lpCommandLine);
++ wcscat(QuotedCmdLine, L"\"");
++
++ /* Copy the null-char back */
++ if (QuotesNeeded)
++ {
++ *NullBuffer = SaveChar;
++ wcscat(QuotedCmdLine, NullBuffer);
++ }
++ }
++ else
++ {
++ /* We can't put quotes around the thing, so try it anyway */
++ if (QuotesNeeded) QuotesNeeded = FALSE;
++ if (CmdLineIsAppName) CmdLineIsAppName = FALSE;
++ }
++ }
+
- SaveChar = *NullBuffer;
- *NullBuffer = UNICODE_NULL;
++ /* Use isolation if needed */
++ if (CreateProcessMsg->Sxs.Flags & 1) ParameterFlags |= 1;
++
++ /* Set the new command-line if needed */
++ if ((QuotesNeeded) || (CmdLineIsAppName)) lpCommandLine = QuotedCmdLine;
++
++ /* Call the helper function in charge of RTL_USER_PROCESS_PARAMETERS */
++ Result = BasePushProcessParameters(ParameterFlags,
++ ProcessHandle,
++ RemotePeb,
++ lpApplicationName,
++ CurrentDirectory,
++ lpCommandLine,
++ lpEnvironment,
++ &StartupInfo,
++ dwCreationFlags | NoWindow,
++ bInheritHandles,
++ IsWowApp ? IMAGE_SUBSYSTEM_WINDOWS_GUI: 0,
++ AppCompatData,
++ AppCompatDataSize);
++ if (!Result)
++ {
++ /* The remote process would have an undefined state, so fail the call */
++ DPRINT1("BasePushProcessParameters failed\n");
++ goto Quickie;
++ }
++
++ /* Free the VDM command line string as it's no longer needed */
++ RtlFreeUnicodeString(&VdmString);
++ VdmString.Buffer = NULL;
++
++ /* Non-VDM console applications usually inherit handles unless specified */
++ if (!(VdmBinaryType) &&
++ !(bInheritHandles) &&
++ !(StartupInfo.dwFlags & STARTF_USESTDHANDLES) &&
++ !(dwCreationFlags & (CREATE_NO_WINDOW |
++ CREATE_NEW_CONSOLE |
++ DETACHED_PROCESS)) &&
++ (ImageInformation.SubSystemType == IMAGE_SUBSYSTEM_WINDOWS_CUI))
++ {
++ /* Get the remote parameters */
++ Status = NtReadVirtualMemory(ProcessHandle,
++ &RemotePeb->ProcessParameters,
++ &ProcessParameters,
++ sizeof(PRTL_USER_PROCESS_PARAMETERS),
++ NULL);
++ if (NT_SUCCESS(Status))
+ {
- /* Add the command line and the finishing quote */
- wcscat(QuotedCmdLine, lpCommandLine);
- wcscat(QuotedCmdLine, L"\"");
++ /* Duplicate standard input unless it's a console handle */
++ if (!IsConsoleHandle(Peb->ProcessParameters->StandardInput))
++ {
++ StuffStdHandle(ProcessHandle,
++ Peb->ProcessParameters->StandardInput,
++ &ProcessParameters->StandardInput);
++ }
++
++ /* Duplicate standard output unless it's a console handle */
++ if (!IsConsoleHandle(Peb->ProcessParameters->StandardOutput))
++ {
++ StuffStdHandle(ProcessHandle,
++ Peb->ProcessParameters->StandardOutput,
++ &ProcessParameters->StandardOutput);
++ }
++
++ /* Duplicate standard error unless it's a console handle */
++ if (!IsConsoleHandle(Peb->ProcessParameters->StandardError))
++ {
++ StuffStdHandle(ProcessHandle,
++ Peb->ProcessParameters->StandardError,
++ &ProcessParameters->StandardError);
++ }
+ }
++ }
++
++ /* Create the Thread's Stack */
++ StackSize = max(256 * 1024, ImageInformation.MaximumStackSize);
++ Status = BaseCreateStack(ProcessHandle,
++ ImageInformation.CommittedStackSize,
++ StackSize,
++ &InitialTeb);
++ if (!NT_SUCCESS(Status))
++ {
++ DPRINT1("Creating the thread stack failed: %lx\n", Status);
++ BaseSetLastNTError(Status);
++ Result = FALSE;
++ goto Quickie;
++ }
++
++ /* Create the Thread's Context */
++ BaseInitializeContext(&Context,
++ Peb,
++ ImageInformation.TransferAddress,
++ InitialTeb.StackBase,
++ 0);
+
- /* Add the null char */
- if (QuotesNeeded)
++ /* Convert the thread attributes */
++ ObjectAttributes = BaseFormatObjectAttributes(&LocalObjectAttributes,
++ lpThreadAttributes,
++ NULL);
++ if ((hUserToken) && (lpThreadAttributes))
++ {
++ /* If the caller specified a user token, zero the security descriptor */
++ LocalThreadAttributes = *lpThreadAttributes;
++ LocalThreadAttributes.lpSecurityDescriptor = NULL;
++ ObjectAttributes = BaseFormatObjectAttributes(&LocalObjectAttributes,
++ &LocalThreadAttributes,
++ NULL);
++ }
+
- *NullBuffer = SaveChar;
- wcscat(QuotedCmdLine, NullBuffer);
++ /* Create the Kernel Thread Object */
++ Status = NtCreateThread(&ThreadHandle,
++ THREAD_ALL_ACCESS,
++ ObjectAttributes,
++ ProcessHandle,
++ &ClientId,
++ &Context,
++ &InitialTeb,
++ TRUE);
++ if (!NT_SUCCESS(Status))
++ {
++ /* A process is not allowed to exist without a main thread, so fail */
++ DPRINT1("Creating the main thread failed: %lx\n", Status);
++ BaseSetLastNTError(Status);
++ Result = FALSE;
++ goto Quickie;
++ }
++
++ /* Begin filling out the CSRSS message, first with our IDs and handles */
++ CreateProcessMsg->ProcessHandle = ProcessHandle;
++ CreateProcessMsg->ThreadHandle = ThreadHandle;
++ CreateProcessMsg->ClientId = ClientId;
++
++ /* Write the remote PEB address and clear it locally, we no longer use it */
++ CreateProcessMsg->PebAddressNative = RemotePeb;
++ CreateProcessMsg->PebAddressWow64 = (ULONG)RemotePeb;
++ RemotePeb = NULL;
++
++ /* Now check what kind of architecture this image was made for */
++ switch (ImageInformation.Machine)
++ {
++ /* IA32, IA64 and AMD64 are supported in Server 2003 */
++ case IMAGE_FILE_MACHINE_I386:
++ CreateProcessMsg->ProcessorArchitecture = PROCESSOR_ARCHITECTURE_INTEL;
++ break;
++ case IMAGE_FILE_MACHINE_IA64:
++ CreateProcessMsg->ProcessorArchitecture = PROCESSOR_ARCHITECTURE_IA64;
++ break;
++ case IMAGE_FILE_MACHINE_AMD64:
++ CreateProcessMsg->ProcessorArchitecture = PROCESSOR_ARCHITECTURE_AMD64;
++ break;
++
++ /* Anything else results in image unknown -- but no failure */
++ default:
++ DbgPrint("kernel32: No mapping for ImageInformation.Machine == %04x\n",
++ ImageInformation.Machine);
++ CreateProcessMsg->ProcessorArchitecture = PROCESSOR_ARCHITECTURE_UNKNOWN;
++ break;
++ }
++
++ /* Write the input creation flags except any debugger-related flags */
++ CreateProcessMsg->CreationFlags = dwCreationFlags &
++ ~(DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS);
++
++ /* CSRSS needs to know if this is a GUI app or not */
++ if ((ImageInformation.SubSystemType == IMAGE_SUBSYSTEM_WINDOWS_GUI) ||
++ (IsWowApp))
++ {
++ /*
++ * For GUI apps we turn on the 2nd bit. This allow CSRSS server dlls
++ * (basesrv in particular) to know whether or not this is a GUI or a
++ * TUI application.
++ */
++ AddToHandle(CreateProcessMsg->ProcessHandle, 2);
++
++ /* Also check if the parent is also a GUI process */
++ NtHeaders = RtlImageNtHeader(GetModuleHandle(NULL));
++ if ((NtHeaders) &&
++ (NtHeaders->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI))
+ {
- DPRINT("Quoted CmdLine: %S\n", QuotedCmdLine);
++ /* Let it know that it should display the hourglass mouse cursor */
++ AddToHandle(CreateProcessMsg->ProcessHandle, 1);
+ }
++ }
++
++ /* For all apps, if this flag is on, the hourglass mouse cursor is shown */
++ if (StartupInfo.dwFlags & STARTF_FORCEONFEEDBACK)
++ {
++ AddToHandle(CreateProcessMsg->ProcessHandle, 1);
++ }
++
++ /* Likewise, the opposite holds as well */
++ if (StartupInfo.dwFlags & STARTF_FORCEOFFFEEDBACK)
++ {
++ RemoveFromHandle(CreateProcessMsg->ProcessHandle, 1);
++ }
++
++ /* Also store which kind of VDM app (if any) this is */
++ CreateProcessMsg->VdmBinaryType = VdmBinaryType;
++
++ /* And if it really is a VDM app... */
++ if (VdmBinaryType)
++ {
++ /* Store the task ID and VDM console handle */
++ CreateProcessMsg->hVDM = VdmTask ? 0 : Peb->ProcessParameters->ConsoleHandle;
++ CreateProcessMsg->VdmTask = VdmTask;
++ }
++ else if (VdmReserve)
++ {
++ /* Extended VDM, set a flag */
++ CreateProcessMsg->VdmBinaryType |= BINARY_TYPE_WOW_EX;
++ }
++
++ /* Check if there's side-by-side assembly data associated with the process */
++ if (CreateProcessMsg->Sxs.Flags)
++ {
++ /* This should not happen in ReactOS yet */
++ DPRINT1("This is an SxS Message -- should not happen yet\n");
++ BaseSetLastNTError(STATUS_NOT_IMPLEMENTED);
++ NtTerminateProcess(ProcessHandle, STATUS_NOT_IMPLEMENTED);
++ Result = FALSE;
++ goto Quickie;
++ }
++
++ /* We are finally ready to call CSRSS to tell it about our new process! */
++ CsrClientCallServer((PCSR_API_MESSAGE)&CsrMsg,
++ CaptureBuffer,
++ CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX,
++ BasepCreateProcess),
++ sizeof(*CreateProcessMsg));
+
- if (Escape)
++ /* CSRSS has returned, free the capture buffer now if we had one */
++ if (CaptureBuffer)
++ {
++ CsrFreeCaptureBuffer(CaptureBuffer);
++ CaptureBuffer = NULL;
++ }
++
++ /* Check if CSRSS failed to accept ownership of the new Windows process */
++ if (!NT_SUCCESS(CsrMsg.Status))
++ {
++ /* Terminate the process and enter failure path with the CSRSS status */
++ DPRINT1("Failed to tell csrss about new process\n");
++ BaseSetLastNTError(CsrMsg.Status);
++ NtTerminateProcess(ProcessHandle, CsrMsg.Status);
++ Result = FALSE;
++ goto Quickie;
+ }
+
- if (QuotedCmdLine == NULL)
++ /* Check if we have a token due to Authz/Safer, not passed by the user */
++ if ((TokenHandle) && !(hUserToken))
+ {
- QuotedCmdLine = RtlAllocateHeap(RtlGetProcessHeap(),
- 0,
- (wcslen(lpCommandLine) + 1) * sizeof(WCHAR));
- if (QuotedCmdLine == NULL)
- {
- SetLastError(ERROR_NOT_ENOUGH_MEMORY);
- goto Cleanup;
- }
- wcscpy(QuotedCmdLine, lpCommandLine);
++ /* Replace the process and/or thread token with the one from Safer */
++ Status = BasepReplaceProcessThreadTokens(TokenHandle,
++ ProcessHandle,
++ ThreadHandle);
++ if (!NT_SUCCESS(Status))
+ {
- ScanString = QuotedCmdLine;
- while (NULL != (ScanString = wcschr(ScanString, L'^')))
++ /* If this failed, kill the process and enter the failure path */
++ DPRINT1("Failed to update process token: %lx\n", Status);
++ NtTerminateProcess(ProcessHandle, Status);
++ BaseSetLastNTError(Status);
++ Result = FALSE;
++ goto Quickie;
+ }
++ }
+
- ScanString++;
- if (*ScanString == L'\"' || *ScanString == L'^' || *ScanString == L'\\')
++ /* Check if a job was associated with this process */
++ if (JobHandle)
++ {
++ /* Bind the process and job together now */
++ Status = NtAssignProcessToJobObject(JobHandle, ProcessHandle);
++ if (!NT_SUCCESS(Status))
+ {
- memmove(ScanString-1, ScanString, wcslen(ScanString) * sizeof(WCHAR) + sizeof(WCHAR));
++ /* Kill the process and enter the failure path if binding failed */
++ DPRINT1("Failed to assign process to job: %lx\n", Status);
++ NtTerminateProcess(ProcessHandle, STATUS_ACCESS_DENIED);
++ BaseSetLastNTError(Status);
++ Result = FALSE;
++ goto Quickie;
++ }
++ }
++
++ /* Finally, resume the thread to actually get the process started */
++ if (!(dwCreationFlags & CREATE_SUSPENDED))
++ {
++ NtResumeThread(ThreadHandle, &ResumeCount);
++ }
++
++VdmShortCircuit:
++ /* We made it this far, meaning we have a fully created process and thread */
++ Result = TRUE;
++
++ /* Anyone doing a VDM undo should now undo everything, since we are done */
++ if (VdmUndoLevel) VdmUndoLevel |= VDM_UNDO_COMPLETED;
++
++ /* Having a VDM wait object implies this must be a VDM process */
++ if (VdmWaitObject)
++ {
++ /* Check if it's a 16-bit separate WOW process */
++ if (VdmBinaryType == BINARY_TYPE_SEPARATE_WOW)
++ {
++ /* OR-in the special flag to indicate this, and return to caller */
++ AddToHandle(VdmWaitObject, 2);
++ lpProcessInformation->hProcess = VdmWaitObject;
++
++ /* Check if this was a re-used VDM */
++ if (VdmUndoLevel & VDM_UNDO_REUSE)
+ {
- }
++ /* No Client ID should be returned in this case */
++ ClientId.UniqueProcess = 0;
++ ClientId.UniqueThread = 0;
+ }
+ }
- /* Get the Process Information */
- Status = NtQueryInformationProcess(hProcess,
- ProcessBasicInformation,
- &ProcessBasicInfo,
- sizeof(ProcessBasicInfo),
- NULL);
++ else
++ {
++ /* OR-in the special flag to indicate this is not a separate VDM */
++ AddToHandle(VdmWaitObject, 1);
+
- /* Convert the environment */
- if(lpEnvironment && !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT))
- {
- lpEnvironment = BasepConvertUnicodeEnvironment(&EnvSize, lpEnvironment);
- if (!lpEnvironment) goto Cleanup;
- }
-
- /* Create Process Environment */
- RemotePeb = ProcessBasicInfo.PebBaseAddress;
- Ret = BasePushProcessParameters(0,
- hProcess,
- RemotePeb,
- (LPWSTR)lpApplicationName,
- CurrentDirectory,
- (QuotesNeeded || CmdLineIsAppName || Escape) ?
- QuotedCmdLine : lpCommandLine,
- lpEnvironment,
- &StartupInfo,
- dwCreationFlags,
- bInheritHandles,
- 0,
- NULL,
- 0);
- if (!Ret) goto Cleanup;
-
- /* Cleanup Environment */
- if (lpEnvironment && !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT))
++ /* Return handle to the caller */
++ lpProcessInformation->hProcess = VdmWaitObject;
++ }
+
- RtlDestroyEnvironment(lpEnvironment);
++ /* Close the original process handle, since it's not needed for VDM */
++ if (ProcessHandle) NtClose(ProcessHandle);
++ }
++ else
+ {
- /* Close the section */
- NtClose(hSection);
- hSection = NULL;
++ /* This is a regular process, so return the real process handle */
++ lpProcessInformation->hProcess = ProcessHandle;
+ }
+
- /* Duplicate the handles if needed */
- if (!bInheritHandles && !(StartupInfo.dwFlags & STARTF_USESTDHANDLES) &&
- SectionImageInfo.SubSystemType == IMAGE_SUBSYSTEM_WINDOWS_CUI)
++ /* Return the rest of the process information based on what we have so far */
++ lpProcessInformation->hThread = ThreadHandle;
++ lpProcessInformation->dwProcessId = HandleToUlong(ClientId.UniqueProcess);
++ lpProcessInformation->dwThreadId = HandleToUlong(ClientId.UniqueThread);
++
++ /* NULL these out here so we know to treat this as a success scenario */
++ ProcessHandle = NULL;
++ ThreadHandle = NULL;
+
- PRTL_USER_PROCESS_PARAMETERS RemoteParameters;
++Quickie:
++ /* Free the debugger command line if one was allocated */
++ if (DebuggerCmdLine) RtlFreeHeap(RtlGetProcessHeap(), 0, DebuggerCmdLine);
++
++ /* Check if an SxS full path as queried */
++ if (PathBuffer)
+ {
- /* Get the remote parameters */
- Status = NtReadVirtualMemory(hProcess,
- &RemotePeb->ProcessParameters,
- &RemoteParameters,
- sizeof(PVOID),
- NULL);
- if (!NT_SUCCESS(Status))
++ /* Reinitialize the executable path */
++ RtlInitEmptyUnicodeString(&SxsWin32ExePath, NULL, 0);
++ SxsWin32ExePath.Length = 0;
+
- DPRINT1("Failed to read memory\n");
- goto Cleanup;
++ /* Free the path buffer */
++ RtlFreeHeap(RtlGetProcessHeap(), 0, PathBuffer);
++ }
++
++#if _SXS_SUPPORT_ENABLED_
++ /* Check if this was a non-VDM process */
++ if (!VdmBinaryType)
++ {
++ /* Then it must've had SxS data, so close the handles used for it */
++ BasepSxsCloseHandles(&Handles);
++ BasepSxsCloseHandles(&FileHandles);
++
++ /* Check if we built SxS byte buffers for this create process request */
++ if (SxsConglomeratedBuffer)
+ {
- /* Duplicate and write the handles */
- BasepDuplicateAndWriteHandle(hProcess,
- OurPeb->ProcessParameters->StandardInput,
- &RemoteParameters->StandardInput);
- BasepDuplicateAndWriteHandle(hProcess,
- OurPeb->ProcessParameters->StandardOutput,
- &RemoteParameters->StandardOutput);
- BasepDuplicateAndWriteHandle(hProcess,
- OurPeb->ProcessParameters->StandardError,
- &RemoteParameters->StandardError);
++ /* Loop all of them */
++ for (i = 0; i < 5; i++)
++ {
++ /* Check if this one was allocated */
++ ThisBuffer = SxsStaticBuffers[i];
++ if (ThisBuffer)
++ {
++ /* Get the underlying RTL_BUFFER structure */
++ ByteBuffer = &ThisBuffer->ByteBuffer;
++ if ((ThisBuffer != (PVOID)-8) && (ByteBuffer->Buffer))
++ {
++ /* Check if it was dynamic */
++ if (ByteBuffer->Buffer != ByteBuffer->StaticBuffer)
++ {
++ /* Free it from the heap */
++ FreeString.Buffer = (PWCHAR)ByteBuffer->Buffer;
++ RtlFreeUnicodeString(&FreeString);
++ }
++
++ /* Reset the buffer to its static data */
++ ByteBuffer->Buffer = ByteBuffer->StaticBuffer;
++ ByteBuffer->Size = ByteBuffer->StaticSize;
++ }
++
++ /* Reset the string to the static buffer */
++ RtlInitEmptyUnicodeString(&ThisBuffer->String,
++ (PWCHAR)ByteBuffer->StaticBuffer,
++ ByteBuffer->StaticSize);
++ if (ThisBuffer->String.Buffer)
++ {
++ /* Also NULL-terminate it */
++ *ThisBuffer->String.Buffer = UNICODE_NULL;
++ }
++ }
++ }
+ }
++ }
++#endif
++ /* Check if an environment was passed in */
++ if ((lpEnvironment) && !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT))
++ {
++ /* Destroy it */
++ RtlDestroyEnvironment(lpEnvironment);
+
- /* Create the first thread */
- DPRINT("Creating thread for process (EntryPoint = 0x%p)\n",
- SectionImageInfo.TransferAddress);
- hThread = BasepCreateFirstThread(hProcess,
- lpThreadAttributes,
- &SectionImageInfo,
- &ClientId,
- dwCreationFlags);
++ /* If this was the VDM environment too, clear that as well */
++ if (VdmUnicodeEnv.Buffer == lpEnvironment) VdmUnicodeEnv.Buffer = NULL;
++ lpEnvironment = NULL;
+ }
+
- if (hThread == NULL)
++ /* Unconditionally free all the name parsing buffers we always allocate */
++ RtlFreeHeap(RtlGetProcessHeap(), 0, QuotedCmdLine);
++ RtlFreeHeap(RtlGetProcessHeap(), 0, NameBuffer);
++ RtlFreeHeap(RtlGetProcessHeap(), 0, CurrentDirectory);
++ RtlFreeHeap(RtlGetProcessHeap(), 0, FreeBuffer);
++
++ /* Close open file/section handles */
++ if (FileHandle) NtClose(FileHandle);
++ if (SectionHandle) NtClose(SectionHandle);
+
- DPRINT1("Could not create Initial Thread\n");
- /* FIXME - set last error code */
- goto Cleanup;
++ /* If we have a thread handle, this was a failure path */
++ if (ThreadHandle)
+ {
- if (!(dwCreationFlags & CREATE_SUSPENDED))
++ /* So kill the process and close the thread handle */
++ NtTerminateProcess(ProcessHandle, 0);
++ NtClose(ThreadHandle);
+ }
+
- NtResumeThread(hThread, &Dummy);
++ /* If we have a process handle, this was a failure path, so close it */
++ if (ProcessHandle) NtClose(ProcessHandle);
++
++ /* Thread/process handles, if any, are now processed. Now close this one. */
++ if (JobHandle) NtClose(JobHandle);
++
++ /* Check if we had created a token */
++ if (TokenHandle)
+ {
- /* Return Data */
- lpProcessInformation->dwProcessId = (DWORD)ClientId.UniqueProcess;
- lpProcessInformation->dwThreadId = (DWORD)ClientId.UniqueThread;
- lpProcessInformation->hProcess = hProcess;
- lpProcessInformation->hThread = hThread;
- DPRINT("hThread[%p]: %p inside hProcess[%p]: %p\n", hThread,
- ClientId.UniqueThread, ClientId.UniqueProcess, hProcess);
- hProcess = hThread = NULL;
- Ret = TRUE;
++ /* And if the user asked for one */
++ if (hUserToken)
++ {
++ /* Then return it */
++ *hNewToken = TokenHandle;
++ }
++ else
++ {
++ /* User didn't want it, so we used it temporarily -- close it */
++ NtClose(TokenHandle);
++ }
+ }
+
- Cleanup:
- /* De-allocate heap strings */
- if (NameBuffer) RtlFreeHeap(RtlGetProcessHeap(), 0, NameBuffer);
- if (ApplicationName.Buffer)
- RtlFreeHeap(RtlGetProcessHeap(), 0, ApplicationName.Buffer);
- if (CurrentDirectory) RtlFreeHeap(RtlGetProcessHeap(), 0, CurrentDirectory);
- if (QuotedCmdLine) RtlFreeHeap(RtlGetProcessHeap(), 0, QuotedCmdLine);
++ /* Free any temporary app compatibility data, it's no longer needed */
++ BasepFreeAppCompatData(AppCompatData, AppCompatSxsData);
+
- /* Kill any handles still alive */
- if (hSection) NtClose(hSection);
- if (hThread)
++ /* Free a few strings. The API takes care of these possibly being NULL */
++ RtlFreeUnicodeString(&VdmString);
++ RtlFreeUnicodeString(&DebuggerString);
+
- /* We don't know any more details than this */
- NtTerminateProcess(hProcess, STATUS_UNSUCCESSFUL);
- NtClose(hThread);
++ /* Check if we had built any sort of VDM environment */
++ if ((VdmAnsiEnv.Buffer) || (VdmUnicodeEnv.Buffer))
+ {
- if (hProcess) NtClose(hProcess);
++ /* Free it */
++ BaseDestroyVDMEnvironment(&VdmAnsiEnv, &VdmUnicodeEnv);
+ }
- /* Return Success */
- return Ret;
+
- return CreateProcessInternalW(0,
++ /* Check if this was any kind of VDM application that we ended up creating */
++ if ((VdmUndoLevel) && (!(VdmUndoLevel & VDM_UNDO_COMPLETED)))
++ {
++ /* Send an undo */
++ BaseUpdateVDMEntry(VdmEntryUndo,
++ (PHANDLE)&VdmTask,
++ VdmUndoLevel,
++ VdmBinaryType);
++
++ /* And close whatever VDM handle we were using for notifications */
++ if (VdmWaitObject) NtClose(VdmWaitObject);
++ }
++
++ /* Check if we ended up here with an allocated search path, and free it */
++ if (SearchPath) RtlFreeHeap(RtlGetProcessHeap(), 0, SearchPath);
++
++ /* Finally, return the API's result */
++ return Result;
+}
+
+/*
+ * @implemented
+ */
+BOOL
+WINAPI
+CreateProcessW(LPCWSTR lpApplicationName,
+ LPWSTR lpCommandLine,
+ LPSECURITY_ATTRIBUTES lpProcessAttributes,
+ LPSECURITY_ATTRIBUTES lpThreadAttributes,
+ BOOL bInheritHandles,
+ DWORD dwCreationFlags,
+ LPVOID lpEnvironment,
+ LPCWSTR lpCurrentDirectory,
+ LPSTARTUPINFOW lpStartupInfo,
+ LPPROCESS_INFORMATION lpProcessInformation)
+{
+ /* Call the internal (but exported) version */
- return CreateProcessInternalA(0,
++ return CreateProcessInternalW(NULL,
+ lpApplicationName,
+ lpCommandLine,
+ lpProcessAttributes,
+ lpThreadAttributes,
+ bInheritHandles,
+ dwCreationFlags,
+ lpEnvironment,
+ lpCurrentDirectory,
+ lpStartupInfo,
+ lpProcessInformation,
+ NULL);
+}
+
+/*
+ * @implemented
+ */
+BOOL
+WINAPI
+CreateProcessInternalA(HANDLE hToken,
+ LPCSTR lpApplicationName,
+ LPSTR lpCommandLine,
+ LPSECURITY_ATTRIBUTES lpProcessAttributes,
+ LPSECURITY_ATTRIBUTES lpThreadAttributes,
+ BOOL bInheritHandles,
+ DWORD dwCreationFlags,
+ LPVOID lpEnvironment,
+ LPCSTR lpCurrentDirectory,
+ LPSTARTUPINFOA lpStartupInfo,
+ LPPROCESS_INFORMATION lpProcessInformation,
+ PHANDLE hNewToken)
+{
+ PUNICODE_STRING CommandLine = NULL;
+ UNICODE_STRING DummyString;
+ UNICODE_STRING LiveCommandLine;
+ UNICODE_STRING ApplicationName;
+ UNICODE_STRING CurrentDirectory;
+ BOOL bRetVal;
+ STARTUPINFOW StartupInfo;
+
+ DPRINT("dwCreationFlags %x, lpEnvironment %x, lpCurrentDirectory %x, "
+ "lpStartupInfo %x, lpProcessInformation %x\n",
+ dwCreationFlags, lpEnvironment, lpCurrentDirectory,
+ lpStartupInfo, lpProcessInformation);
+
+ /* Copy Startup Info */
+ RtlMoveMemory(&StartupInfo, lpStartupInfo, sizeof(*lpStartupInfo));
+
+ /* Initialize all strings to nothing */
+ LiveCommandLine.Buffer = NULL;
+ DummyString.Buffer = NULL;
+ ApplicationName.Buffer = NULL;
+ CurrentDirectory.Buffer = NULL;
+ StartupInfo.lpDesktop = NULL;
+ StartupInfo.lpReserved = NULL;
+ StartupInfo.lpTitle = NULL;
+
+ /* Convert the Command line */
+ if (lpCommandLine)
+ {
+ /* If it's too long, then we'll have a problem */
+ if ((strlen(lpCommandLine) + 1) * sizeof(WCHAR) <
+ NtCurrentTeb()->StaticUnicodeString.MaximumLength)
+ {
+ /* Cache it in the TEB */
+ CommandLine = Basep8BitStringToStaticUnicodeString(lpCommandLine);
+ }
+ else
+ {
+ /* Use a dynamic version */
+ Basep8BitStringToDynamicUnicodeString(&LiveCommandLine,
+ lpCommandLine);
+ }
+ }
+ else
+ {
+ /* The logic below will use CommandLine, so we must make it valid */
+ CommandLine = &DummyString;
+ }
+
+ /* Convert the Name and Directory */
+ if (lpApplicationName)
+ {
+ Basep8BitStringToDynamicUnicodeString(&ApplicationName,
+ lpApplicationName);
+ }
+ if (lpCurrentDirectory)
+ {
+ Basep8BitStringToDynamicUnicodeString(&CurrentDirectory,
+ lpCurrentDirectory);
+ }
+
+ /* Now convert Startup Strings */
+ if (lpStartupInfo->lpReserved)
+ {
+ BasepAnsiStringToHeapUnicodeString(lpStartupInfo->lpReserved,
+ &StartupInfo.lpReserved);
+ }
+ if (lpStartupInfo->lpDesktop)
+ {
+ BasepAnsiStringToHeapUnicodeString(lpStartupInfo->lpDesktop,
+ &StartupInfo.lpDesktop);
+ }
+ if (lpStartupInfo->lpTitle)
+ {
+ BasepAnsiStringToHeapUnicodeString(lpStartupInfo->lpTitle,
+ &StartupInfo.lpTitle);
+ }
+
+ /* Call the Unicode function */
+ bRetVal = CreateProcessInternalW(hToken,
+ ApplicationName.Buffer,
+ LiveCommandLine.Buffer ?
+ LiveCommandLine.Buffer : CommandLine->Buffer,
+ lpProcessAttributes,
+ lpThreadAttributes,
+ bInheritHandles,
+ dwCreationFlags,
+ lpEnvironment,
+ CurrentDirectory.Buffer,
+ &StartupInfo,
+ lpProcessInformation,
+ hNewToken);
+
+ /* Clean up */
+ RtlFreeUnicodeString(&ApplicationName);
+ RtlFreeUnicodeString(&LiveCommandLine);
+ RtlFreeUnicodeString(&CurrentDirectory);
+ RtlFreeHeap(RtlGetProcessHeap(), 0, StartupInfo.lpDesktop);
+ RtlFreeHeap(RtlGetProcessHeap(), 0, StartupInfo.lpReserved);
+ RtlFreeHeap(RtlGetProcessHeap(), 0, StartupInfo.lpTitle);
+
+ /* Return what Unicode did */
+ return bRetVal;
+}
+
+/*
+ * FUNCTION: The CreateProcess function creates a new process and its
+ * primary thread. The new process executes the specified executable file
+ * ARGUMENTS:
+ *
+ * lpApplicationName = Pointer to name of executable module
+ * lpCommandLine = Pointer to command line string
+ * lpProcessAttributes = Process security attributes
+ * lpThreadAttributes = Thread security attributes
+ * bInheritHandles = Handle inheritance flag
+ * dwCreationFlags = Creation flags
+ * lpEnvironment = Pointer to new environment block
+ * lpCurrentDirectory = Pointer to current directory name
+ * lpStartupInfo = Pointer to startup info
+ * lpProcessInformation = Pointer to process information
+ *
+ * @implemented
+ */
+BOOL
+WINAPI
+CreateProcessA(LPCSTR lpApplicationName,
+ LPSTR lpCommandLine,
+ LPSECURITY_ATTRIBUTES lpProcessAttributes,
+ LPSECURITY_ATTRIBUTES lpThreadAttributes,
+ BOOL bInheritHandles,
+ DWORD dwCreationFlags,
+ LPVOID lpEnvironment,
+ LPCSTR lpCurrentDirectory,
+ LPSTARTUPINFOA lpStartupInfo,
+ LPPROCESS_INFORMATION lpProcessInformation)
+{
+ /* Call the internal (but exported) version */
++ return CreateProcessInternalA(NULL,
+ lpApplicationName,
+ lpCommandLine,
+ lpProcessAttributes,
+ lpThreadAttributes,
+ bInheritHandles,
+ dwCreationFlags,
+ lpEnvironment,
+ lpCurrentDirectory,
+ lpStartupInfo,
+ lpProcessInformation,
+ NULL);
+}
+
+/*
+ * @implemented
+ */
+UINT
+WINAPI
+WinExec(LPCSTR lpCmdLine,
+ UINT uCmdShow)
+{
+ STARTUPINFOA StartupInfo;
+ PROCESS_INFORMATION ProcessInformation;
+ DWORD dosErr;
+
+ RtlZeroMemory(&StartupInfo, sizeof(StartupInfo));
+ StartupInfo.cb = sizeof(STARTUPINFOA);
+ StartupInfo.wShowWindow = (WORD)uCmdShow;
+ StartupInfo.dwFlags = 0;
+
+ if (!CreateProcessA(NULL,
+ (PVOID)lpCmdLine,
+ NULL,
+ NULL,
+ FALSE,
+ 0,
+ NULL,
+ NULL,
+ &StartupInfo,
+ &ProcessInformation))
+ {
+ dosErr = GetLastError();
+ return dosErr < 32 ? dosErr : ERROR_BAD_FORMAT;
+ }
+
+ if (NULL != UserWaitForInputIdleRoutine)
+ {
+ UserWaitForInputIdleRoutine(ProcessInformation.hProcess,
+ 10000);
+ }
+
+ NtClose(ProcessInformation.hProcess);
+ NtClose(ProcessInformation.hThread);
+
+ return 33; /* Something bigger than 31 means success. */
+}
+
+/* EOF */