#include <k32.h>
-// #define NDEBUG
+#define NDEBUG
#include <debug.h>
/* GLOBALS *******************************************************************/
{
NTSTATUS Status;
HANDLE DuplicatedHandle;
- SIZE_T 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,
- DUPLICATE_SAME_ACCESS | DUPLICATE_SAME_ATTRIBUTES,
0,
- 0);
- if (NT_SUCCESS(Status))
- {
- /* Write it */
- NtWriteVirtualMemory(ProcessHandle,
- Address,
- &DuplicatedHandle,
- sizeof(HANDLE),
- &Dummy);
- }
+ 0,
+ DUPLICATE_SAME_ACCESS |
+ DUPLICATE_SAME_ATTRIBUTES);
+ if (!NT_SUCCESS(Status)) return;
+
+ /* Write it */
+ NtWriteVirtualMemory(ProcessHandle,
+ Address,
+ &DuplicatedHandle,
+ sizeof(HANDLE),
+ &NumberOfBytesWritten);
}
BOOLEAN
WINAPI
-BuildSubSysCommandLine(IN LPWSTR SubsystemName,
- IN LPWSTR ApplicationName,
- IN LPWSTR CommandLine,
+BuildSubSysCommandLine(IN LPCWSTR SubsystemName,
+ IN LPCWSTR ApplicationName,
+ IN LPCWSTR CommandLine,
OUT PUNICODE_STRING SubsysCommandLine)
{
UNICODE_STRING CommandLineString, ApplicationNameString;
NTSTATUS
WINAPI
-BasepIsProcessAllowed(IN PCHAR ApplicationName)
+BasepIsProcessAllowed(IN LPWSTR ApplicationName)
{
- NTSTATUS Status;
+ NTSTATUS Status, Status1;
PWCHAR Buffer;
UINT Length;
HMODULE TrustLibrary;
else
{
/* Other systems have a registry entry for this */
- Status = NtOpenKey(&KeyHandle, KEY_READ, &KeyAttributes);
- if (NT_SUCCESS(Status))
+ 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 */
- Status = RtlQueryRegistryValues(2,
+ Status = RtlQueryRegistryValues(RTL_REGISTRY_CONTROL,
L"Session Manager",
BasepAppCertTable,
- 0,
- 0);
- if (Status == 0xC0000034) Status = STATUS_SUCCESS;
+ NULL,
+ NULL);
+ if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
+ {
+ Status = STATUS_SUCCESS;
+ }
}
}
NULL,
CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepCreateThread),
sizeof(BASE_CREATE_THREAD));
- if (!NT_SUCCESS(Status) || !NT_SUCCESS(ApiMessage.Status))
+ if (!NT_SUCCESS(Status))
{
- DPRINT1("Failed to tell csrss about new thread: %lx %lx\n", Status, ApiMessage.Status);
- return ApiMessage.Status;
+ DPRINT1("Failed to tell CSRSS about new thread: %lx\n", Status);
+ return Status;
}
/* Return Success */
return STATUS_SUCCESS;
}
-/*
- * Creates the first Thread in a Proces
- */
-HANDLE
-WINAPI
-BasepCreateFirstThread(HANDLE ProcessHandle,
- LPSECURITY_ATTRIBUTES lpThreadAttributes,
- PSECTION_IMAGE_INFORMATION SectionImageInfo,
- PCLIENT_ID ClientId,
- BOOLEAN InheritHandles,
- 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;
- CreateProcessRequest->bInheritHandles = InheritHandles;
-
- /* Call CSR */
- DPRINT1("Calling CsrClientCallServer from BasepCreateFirstThread...\n");
- Status = CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
- NULL,
- CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepCreateProcess),
- sizeof(BASE_CREATE_PROCESS));
- if (!NT_SUCCESS(Status) || !NT_SUCCESS(ApiMessage.Status))
- {
- DPRINT1("Failed to tell csrss about new process: %lx %lx\n", Status, ApiMessage.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.
- */
-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,
- DUPLICATE_SAME_ACCESS | DUPLICATE_SAME_ATTRIBUTES,
- 0,
- 0);
- if (NT_SUCCESS(Status))
- {
- /* Write it */
- NtWriteVirtualMemory(ProcessHandle,
- Address,
- &DuplicatedHandle,
- sizeof(HANDLE),
- &Dummy);
- }
-}
-
BOOLEAN
WINAPI
BasePushProcessParameters(IN ULONG ParameterFlags,
/* 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,
ProcessParameters->StandardError = StartupInfo->hStdError;
}
- /* Use Special Flags for ConDllInitialize in Kernel32 */
+ /* Use Special Flags for BasepInitConsole in Kernel32 */
if (CreationFlags & DETACHED_PROCESS)
{
ProcessParameters->ConsoleHandle = HANDLE_DETACHED_PROCESS;
}
- else if (CreationFlags & CREATE_NO_WINDOW)
- {
- ProcessParameters->ConsoleHandle = HANDLE_CREATE_NO_WINDOW;
- }
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->ConsoleFlags = 1;
}
- /* See if the first 1MB should be reserved */
+ /* Check if there's a .local file present */
if (ParameterFlags & 1)
{
- ProcessParameters->Flags |= RTL_USER_PROCESS_PARAMETERS_RESERVE_1MB;
+ ProcessParameters->Flags |= RTL_USER_PROCESS_PARAMETERS_LOCAL_DLL_PATH;
}
- /* See if the first 16MB should be reserved */
+ /* Check if we failed to open the IFEO key */
if (ParameterFlags & 2)
{
- ProcessParameters->Flags |= RTL_USER_PROCESS_PARAMETERS_RESERVE_16MB;
+ ProcessParameters->Flags |= RTL_USER_PROCESS_PARAMETERS_IMAGE_KEY_MISSING;
}
/* Allocate memory for the parameter block */
if (ProcessParameters) RtlDestroyProcessParameters(ProcessParameters);
return Result;
FailPath:
- DPRINT1("Failure to create proecss parameters: %lx\n", Status);
+ DPRINT1("Failure to create process parameters: %lx\n", Status);
BaseSetLastNTError(Status);
Result = FALSE;
goto Quickie;
{
NTSTATUS Status;
BASE_API_MESSAGE ApiMessage;
- PBASE_GET_PROCESS_SHUTDOWN_PARAMS GetShutdownParametersRequest = &ApiMessage.Data.GetShutdownParametersRequest;
+ PBASE_GETSET_PROCESS_SHUTDOWN_PARAMS ShutdownParametersRequest = &ApiMessage.Data.ShutdownParametersRequest;
/* 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)) || !(NT_SUCCESS(ApiMessage.Status)))
+ sizeof(BASE_GETSET_PROCESS_SHUTDOWN_PARAMS));
+ if (!NT_SUCCESS(Status))
{
/* Return the failure from CSRSS */
- BaseSetLastNTError(ApiMessage.Status);
+ BaseSetLastNTError(Status);
return FALSE;
}
- /* Get the data out of the LCP reply */
- *lpdwLevel = GetShutdownParametersRequest->Level;
- *lpdwFlags = GetShutdownParametersRequest->Flags;
+ /* Get the data back */
+ *lpdwLevel = ShutdownParametersRequest->ShutdownLevel;
+ *lpdwFlags = ShutdownParametersRequest->ShutdownFlags;
return TRUE;
}
{
NTSTATUS Status;
BASE_API_MESSAGE ApiMessage;
- PBASE_SET_PROCESS_SHUTDOWN_PARAMS SetShutdownParametersRequest = &ApiMessage.Data.SetShutdownParametersRequest;
+ PBASE_GETSET_PROCESS_SHUTDOWN_PARAMS ShutdownParametersRequest = &ApiMessage.Data.ShutdownParametersRequest;
/* Write the data into the CSRSS request and send it */
- SetShutdownParametersRequest->Level = dwLevel;
- SetShutdownParametersRequest->Flags = dwFlags;
+ ShutdownParametersRequest->ShutdownLevel = dwLevel;
+ ShutdownParametersRequest->ShutdownFlags = 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) || !NT_SUCCESS(ApiMessage.Status))
+ sizeof(BASE_GETSET_PROCESS_SHUTDOWN_PARAMS));
+ if (!NT_SUCCESS(Status))
{
/* Return the failure from CSRSS */
- BaseSetLastNTError(ApiMessage.Status);
+ BaseSetLastNTError(Status);
return FALSE;
}
ULONG Response;
NTSTATUS Status;
- /* Setup the stirng to print out */
+ /* Setup the string to print out */
RtlInitUnicodeString(&UnicodeString, lpMessageText);
/* Display the hard error no matter what */
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
-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)
+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)
{
- NTSTATUS Status;
- PROCESS_PRIORITY_CLASS PriorityClass;
- BOOLEAN FoundQuotes = FALSE;
- BOOLEAN QuotesNeeded = FALSE;
- BOOLEAN CmdLineIsAppName = FALSE;
- UNICODE_STRING ApplicationName = { 0, 0, NULL };
+ //
+ // Core variables used for creating the initial process and thread
+ //
+ SECURITY_ATTRIBUTES LocalThreadAttributes, LocalProcessAttributes;
OBJECT_ATTRIBUTES LocalObjectAttributes;
POBJECT_ATTRIBUTES ObjectAttributes;
- 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;
- BOOLEAN SearchDone = FALSE;
- BOOLEAN Escape = FALSE;
+ SECTION_IMAGE_INFORMATION ImageInformation;
+ IO_STATUS_BLOCK IoStatusBlock;
CLIENT_ID ClientId;
- PPEB OurPeb = NtCurrentPeb();
- PPEB RemotePeb;
- SIZE_T EnvSize = 0;
- BOOL Ret = FALSE;
+ 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;
+
+ //
+ // 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.CheckVDMRequest;
+
+ /* 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);
- /* 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)
- */
+ /* Finally, set our TEB and PEB */
+ Teb = NtCurrentTeb();
+ Peb = NtCurrentPeb();
- DPRINT("CreateProcessW: lpApplicationName: %S lpCommandLine: %S"
- " lpEnvironment: %p lpCurrentDirectory: %S dwCreationFlags: %lx\n",
- lpApplicationName, lpCommandLine, lpEnvironment, lpCurrentDirectory,
- dwCreationFlags);
+ /* This combination is illegal (see MSDN) */
+ if ((dwCreationFlags & (DETACHED_PROCESS | CREATE_NEW_CONSOLE)) ==
+ (DETACHED_PROCESS | CREATE_NEW_CONSOLE))
+ {
+ DPRINT1("Invalid flag combo used\n");
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
- /* Flags we don't handle yet */
- if (dwCreationFlags & CREATE_SEPARATE_WOW_VDM)
+ /* Convert the priority class */
+ if (dwCreationFlags & IDLE_PRIORITY_CLASS)
{
- DPRINT1("CREATE_SEPARATE_WOW_VDM not handled\n");
+ PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_IDLE;
}
- if (dwCreationFlags & CREATE_SHARED_WOW_VDM)
+ else if (dwCreationFlags & BELOW_NORMAL_PRIORITY_CLASS)
{
- DPRINT1("CREATE_SHARED_WOW_VDM not handled\n");
+ PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_BELOW_NORMAL;
}
- if (dwCreationFlags & CREATE_FORCEDOS)
+ else if (dwCreationFlags & NORMAL_PRIORITY_CLASS)
{
- DPRINT1("CREATE_FORCEDOS not handled\n");
+ PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_NORMAL;
}
-
- /* Fail on this flag, it's only valid with the WithLogonW function */
- if (dwCreationFlags & CREATE_PRESERVE_CODE_AUTHZ_LEVEL)
+ else if (dwCreationFlags & ABOVE_NORMAL_PRIORITY_CLASS)
{
- DPRINT1("Invalid flag used\n");
- SetLastError(ERROR_INVALID_PARAMETER);
- return FALSE;
+ PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_ABOVE_NORMAL;
}
-
- /* This combination is illegal (see MSDN) */
- if ((dwCreationFlags & (DETACHED_PROCESS | CREATE_NEW_CONSOLE)) ==
- (DETACHED_PROCESS | CREATE_NEW_CONSOLE))
+ else if (dwCreationFlags & HIGH_PRIORITY_CLASS)
{
- DPRINT1("Invalid flag combo used\n");
+ 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;
+ }
- /* Another illegal combo */
- if ((dwCreationFlags & (CREATE_SEPARATE_WOW_VDM | CREATE_SHARED_WOW_VDM)) ==
- (CREATE_SEPARATE_WOW_VDM | CREATE_SHARED_WOW_VDM))
+ /* 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)))
{
- DPRINT1("Invalid flag combo used\n");
- SetLastError(ERROR_INVALID_PARAMETER);
- return FALSE;
+ /* Remove the shared flag and add the separate flag */
+ dwCreationFlags = (dwCreationFlags &~ CREATE_SHARED_WOW_VDM) |
+ CREATE_SEPARATE_WOW_VDM;
}
- if (lpCurrentDirectory)
+ /* Convert the environment */
+ if ((lpEnvironment) && !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT))
{
- if ((GetFileAttributesW(lpCurrentDirectory) == INVALID_FILE_ATTRIBUTES) ||
- !(GetFileAttributesW(lpCurrentDirectory) & FILE_ATTRIBUTE_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))
{
- SetLastError(ERROR_DIRECTORY);
+ /* 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))
+ /* Use the allocated size and convert */
+ UnicodeEnv.MaximumLength = RegionSize;
+ Status = RtlAnsiStringToUnicodeString(&UnicodeEnv, &AnsiEnv, FALSE);
+ if (!NT_SUCCESS(Status))
{
- /* Remove the shared flag and add the separate flag. */
- dwCreationFlags = (dwCreationFlags &~ CREATE_SHARED_WOW_VDM) |
- CREATE_SEPARATE_WOW_VDM;
+ /* Fail */
+ NtFreeVirtualMemory(NtCurrentProcess(),
+ (PVOID)&UnicodeEnv.Buffer,
+ &RegionSize,
+ MEM_RELEASE);
+ BaseSetLastNTError(Status);
+ return FALSE;
}
+
+ /* 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
- */
+ /* 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;
}
- /* Start by zeroing out the fields */
- RtlZeroMemory(lpProcessInformation, sizeof(PROCESS_INFORMATION));
+ /* 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;
+ }
- /* Easy stuff first, convert the process priority class */
- PriorityClass.Foreground = FALSE;
- PriorityClass.PriorityClass = (UCHAR)BasepConvertPriorityClass(dwCreationFlags);
+ /* New iteration -- free any existing free buffer */
+ if (FreeBuffer)
+ {
+ RtlFreeHeap(RtlGetProcessHeap(), 0, FreeBuffer);
+ FreeBuffer = NULL;
+ }
- if (lpCommandLine)
+ /* New iteration -- close any existing file handle */
+ if (FileHandle)
{
- /* Search for escape sequences */
- ScanString = lpCommandLine;
- while (NULL != (ScanString = wcschr(ScanString, L'^')))
- {
- ScanString++;
- if (*ScanString == L'\"' || *ScanString == L'^' || *ScanString == L'\"')
- {
- Escape = TRUE;
- break;
- }
- }
+ NtClose(FileHandle);
+ FileHandle = NULL;
}
- /* 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!) */
+ /* 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)
{
- /* The fun begins */
+ /* 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));
- if (NameBuffer == NULL)
+ if (!NameBuffer)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
- goto Cleanup;
+ Result = FALSE;
+ goto Quickie;
}
- /* This is all we have to work with :( */
- lpApplicationName = lpCommandLine;
+ /* Initialize the application name and our parsing parameters */
+ lpApplicationName = NullBuffer = ScanString = lpCommandLine;
- /* Initialize our friends at the beginning */
- NullBuffer = (LPWSTR)lpApplicationName;
- ScanString = (LPWSTR)lpApplicationName;
-
- /* We will start by looking for a quote */
- if (*ScanString == L'\"')
+ /* Check for an initial quote*/
+ if (*lpCommandLine == L'\"')
{
- /* 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;
- }
+ /* 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;
+ }
- /* Keep looking */
- ScanString++;
- NullBuffer = ScanString;
- }
+ /* Keep searching for the quote */
+ ScanString++;
+ NullBuffer = ScanString;
+ }
}
else
{
- /* No quotes, so we'll be looking for white space */
- WhiteScan:
- /* Reset the pointer */
+StartScan:
+ /* We simply make the application name be the command line*/
lpApplicationName = lpCommandLine;
-
- /* Find whitespace of Tab */
while (*ScanString)
{
- if (*ScanString == ' ' || *ScanString == '\t')
+ /* Check if it starts with a space or tab */
+ if ((*ScanString == L' ') || (*ScanString == L'\t'))
{
- /* Found it */
+ /* Break out of the search loop */
NullBuffer = ScanString;
break;
}
- /* Keep looking */
+ /* Keep searching for a space or tab */
ScanString++;
NullBuffer = ScanString;
}
}
- /* Set the Null Buffer */
+ /* We have found the end of the application name, terminate it */
SaveChar = *NullBuffer;
*NullBuffer = UNICODE_NULL;
- /* Do a search for the file */
- DPRINT("Ready for SearchPathW: %S\n", lpApplicationName);
- RetVal = SearchPathW(NULL,
- lpApplicationName,
- L".exe",
- MAX_PATH,
- NameBuffer,
- 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,
+ NULL);
- /* Did it find something? */
- if (RetVal)
+ /* Did we find it? */
+ if ((Length) && (Length < MAX_PATH))
{
/* Get file attributes */
- ULONG Attributes = GetFileAttributesW(NameBuffer);
- if (Attributes & FILE_ATTRIBUTE_DIRECTORY)
+ CurdirLength = GetFileAttributesW(NameBuffer);
+ if ((CurdirLength != 0xFFFFFFFF) &&
+ (CurdirLength & FILE_ATTRIBUTE_DIRECTORY))
{
- /* Give it a length of 0 to fail, this was a directory. */
- RetVal = 0;
+ /* This was a directory, fail later on */
+ Length = 0;
}
else
{
/* It's a file! */
- RetVal += sizeof(WCHAR);
+ Length++;
}
}
- /* 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;
+ DPRINT1("Length: %lx Buffer: %S\n", Length, NameBuffer);
- /* We failed, try to get the Path Type */
- DPRINT("SearchPathW failed. Retval: %ld\n", RetVal);
+ /* 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 it's not relative, try to get the error */
if (PathType != RtlPathTypeRelative)
{
/* This should fail, and give us a detailed LastError */
- 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)
+ FileHandle = CreateFileW(lpApplicationName,
+ GENERIC_READ,
+ FILE_SHARE_READ |
+ FILE_SHARE_WRITE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+ if (FileHandle != INVALID_HANDLE_VALUE)
{
- /* Fake the error */
- CloseHandle(hFile);
+ /* It worked? Return a generic error */
+ CloseHandle(FileHandle);
+ FileHandle = NULL;
BaseSetLastNTError(STATUS_OBJECT_NAME_NOT_FOUND);
}
}
else
{
- /* Immediately set the error */
+ /* Path was absolute, which means it doesn't exist */
BaseSetLastNTError(STATUS_OBJECT_NAME_NOT_FOUND);
}
/* Did we already fail once? */
- if (Error)
+ if (ErrorCode)
{
- SetLastError(Error);
+ /* Set the error code */
+ SetLastError(ErrorCode);
}
else
{
/* Not yet, cache it */
- Error = GetLastError();
+ ErrorCode = GetLastError();
}
/* Put back the command line */
*NullBuffer = SaveChar;
lpApplicationName = NameBuffer;
- /*
- * If the search isn't done and we still have cmdline
- * then start over. Ex: c:\ha ha ha\haha.exe
- */
- if (*ScanString && !SearchDone)
+ /* It's possible there's whitespace in the directory name */
+ if (!(*ScanString) || !(SearchRetry))
{
- /* Move in the buffer */
- ScanString++;
- NullBuffer = ScanString;
+ /* Not the case, give up completely */
+ Result = FALSE;
+ goto Quickie;
+ }
- /* We will have to add a quote, since there is a space*/
- QuotesNeeded = TRUE;
+ /* There are spaces, so keep trying the next possibility */
+ ScanString++;
+ NullBuffer = ScanString;
- /* And we will also fake the fact we found one */
- FoundQuotes = TRUE;
+ /* 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;
+ }
- /* Start over */
- goto WhiteScan;
+ /* 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;
+ }
+ }
+
+ /* Did the caller specify a desktop? */
+ if (!StartupInfo.lpDesktop)
+ {
+ /* Use the one from the current process */
+ StartupInfo.lpDesktop = Peb->ProcessParameters->DesktopInfo.Buffer;
+ }
+
+ /* 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;
+ }
+
+ /* 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;
+ }
}
- else if (!lpCommandLine || *lpCommandLine == UNICODE_NULL)
+
+ /* Did we already do these checks? */
+ if (!SkipSaferAndAppCompat)
{
- /* We have an app name (good!) but no command line */
- CmdLineIsAppName = TRUE;
- lpCommandLine = (LPWSTR)lpApplicationName;
+ /* 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;
+ }
+ }
}
- /* At this point the name has been toyed with enough to be openable */
- Status = BasepMapFile(lpApplicationName, &hSection, &ApplicationName);
+ //ASSERT((dwFusionFlags & ~SXS_APPCOMPACT_FLAG_APP_RUNNING_SAFEMODE) == 0);
- /* Check for failure */
- if (!NT_SUCCESS(Status))
+ /* Have we already done, and do we need to do, SRP (WinSafer) checks? */
+ if (!(SkipSaferAndAppCompat) &&
+ ~(dwCreationFlags & CREATE_PRESERVE_CODE_AUTHZ_LEVEL))
{
- /* Could be a non-PE File */
+ /* Assume yes */
+ SaferNeeded = TRUE;
switch (Status)
{
- /* Check if the Kernel tells us it's not even valid MZ */
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;
+ }
-#if 0
- /* If it's a DOS app, use VDM */
- if ((BasepCheckDosApp(&ApplicationName)))
+ /* But for success, we do */
+ break;
+ }
+
+ /* 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)
{
- DPRINT1("Launching VDM...\n");
- RtlFreeHeap(RtlGetProcessHeap(), 0, NameBuffer);
- RtlFreeHeap(RtlGetProcessHeap(), 0, ApplicationName.Buffer);
- return CreateProcessW(L"ntvdm.exe",
- (LPWSTR)((ULONG_PTR)lpApplicationName), /* FIXME: Buffer must be writable!!! */
- lpProcessAttributes,
- lpThreadAttributes,
- bInheritHandles,
- dwCreationFlags,
- lpEnvironment,
- lpCurrentDirectory,
- &StartupInfo,
- lpProcessInformation);
+ /* 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;
}
-#endif
- /* It's a batch file */
- Extension = &ApplicationName.Buffer[ApplicationName.Length /
- sizeof(WCHAR) - 4];
- /* Make sure the extensions are correct */
- if (_wcsnicmp(Extension, L".bat", 4) && _wcsnicmp(Extension, L".cmd", 4))
+ /* Other status codes are not-Safer related, just convert them */
+ if (!NT_SUCCESS(Status))
{
- SetLastError(ERROR_BAD_EXE_FORMAT);
- return FALSE;
+ DPRINT1("Error checking WinSafer: %lx\n", Status);
+ BaseSetLastNTError(Status);
+ Result = FALSE;
+ goto Quickie;
}
+ }
+ }
- /* Calculate the length of the command line */
- CmdLineLength = wcslen(CMD_STRING) + wcslen(lpCommandLine) + 1;
+ /* 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;
+ }
- /* If we found quotes, then add them into the length size */
- if (CmdLineIsAppName || FoundQuotes) CmdLineLength += 2;
- CmdLineLength *= sizeof(WCHAR);
+ /* 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;
+ }
- /* Allocate space for the new command line */
- BatchCommandLine = RtlAllocateHeap(RtlGetProcessHeap(),
- 0,
- CmdLineLength);
- if (BatchCommandLine == NULL)
- {
- SetLastError(ERROR_NOT_ENOUGH_MEMORY);
- goto Cleanup;
+ /* 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;
}
- /* Build it */
- wcscpy(BatchCommandLine, CMD_STRING);
- if (CmdLineIsAppName || FoundQuotes)
+ // 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, L"\"");
+ /* 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, lpCommandLine);
- if (CmdLineIsAppName || FoundQuotes)
+ else
{
- wcscat(BatchCommandLine, L"\"");
- }
+ /* It's a batch file, get the extension */
+ ExtBuffer = &PathName.Buffer[PathName.Length / sizeof(WCHAR) - 4];
- /* Create it as a Unicode String */
- RtlInitUnicodeString(&CommandLineString, BatchCommandLine);
+ /* 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;
+ }
- /* Set the command line to this */
- lpCommandLine = CommandLineString.Buffer;
- lpApplicationName = NULL;
+ /* Check if we need to account for quotes around the path */
+ CmdQuoteLength = CmdLineIsAppName || HasQuotes;
+ if (!CmdLineIsAppName)
+ {
+ if (HasQuotes) CmdQuoteLength++;
+ }
+ else
+ {
+ CmdQuoteLength++;
+ }
- /* Free memory */
- RtlFreeHeap(RtlGetProcessHeap(), 0, ApplicationName.Buffer);
- ApplicationName.Buffer = NULL;
- goto GetAppName;
- break;
+ /* 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;
+ }
- case STATUS_INVALID_IMAGE_WIN_16:
+ /* Build it */
+ wcscpy(AnsiCmdCommand, CMD_STRING);
+ if ((CmdLineIsAppName) || (HasQuotes))
+ {
+ wcscat(AnsiCmdCommand, L"\"");
+ }
+ wcscat(AnsiCmdCommand, lpCommandLine);
+ if ((CmdLineIsAppName) || (HasQuotes))
+ {
+ wcscat(AnsiCmdCommand, L"\"");
+ }
- /* It's a Win16 Image, use VDM */
- DPRINT1("Launching VDM...\n");
- RtlFreeHeap(RtlGetProcessHeap(), 0, NameBuffer);
- RtlFreeHeap(RtlGetProcessHeap(), 0, ApplicationName.Buffer);
- return CreateProcessW(L"ntvdm.exe",
- (LPWSTR)((ULONG_PTR)lpApplicationName), /* FIXME: Buffer must be writable!!! */
- lpProcessAttributes,
- lpThreadAttributes,
- bInheritHandles,
- dwCreationFlags,
- lpEnvironment,
- lpCurrentDirectory,
- &StartupInfo,
- lpProcessInformation);
+ /* Create it as a Unicode String */
+ RtlInitUnicodeString(&DebuggerString, AnsiCmdCommand);
- case STATUS_OBJECT_NAME_NOT_FOUND:
- case STATUS_OBJECT_PATH_NOT_FOUND:
- BaseSetLastNTError(Status);
- goto Cleanup;
+ /* Set the command line to this */
+ lpCommandLine = DebuggerString.Buffer;
+ lpApplicationName = NULL;
+ DPRINT1("Retrying with: %S\n", lpCommandLine);
+ }
- default:
- /* Invalid Image Type */
+ /* 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);
- goto Cleanup;
+ Result = FALSE;
+ goto Quickie;
+ }
+
+ /* Otherwise, this must be success */
+ ASSERT(Status == STATUS_SUCCESS);
+ break;
+ }
+
+ /* Is this not a WOW application, but a WOW32 VDM was requested for it? */
+ if (!(IsWowApp) && (dwCreationFlags & CREATE_SEPARATE_WOW_VDM))
+ {
+ /* Ignore the nonsensical request */
+ dwCreationFlags &= ~CREATE_SEPARATE_WOW_VDM;
+ }
+
+ /* 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;
}
+
+ /* Don't check this later */
+ QuerySection = TRUE;
}
- /* Use our desktop if we didn't get any */
- if (!StartupInfo.lpDesktop)
+ /* Check if this was linked as a DLL */
+ if (ImageInformation.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;
+ }
+
+ /* 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))
{
- StartupInfo.lpDesktop = OurPeb->ProcessParameters->DesktopInfo.Buffer;
+ /* 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 Application is allowed to run */
+ /* 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);
+ }
+
+ /* Go to the failure path */
+ DPRINT1("Invalid image architecture: %lx\n", ImageInformation.Machine);
+ Result = FALSE;
+ goto Quickie;
+ }
+
+ /* Check if this isn't a Windows image */
+ if ((ImageInformation.SubSystemType != IMAGE_SUBSYSTEM_WINDOWS_GUI) &&
+ (ImageInformation.SubSystemType != IMAGE_SUBSYSTEM_WINDOWS_CUI))
+ {
+ /* 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;
+ }
- /* FIXME: Allow CREATE_SEPARATE only for WOW Apps, once we have that. */
+ /* And re-try launching the process, with the new command-line now */
+ lpCommandLine = DebuggerString.Buffer;
+ lpApplicationName = NULL;
- /* Get some information about the executable */
- Status = NtQuerySection(hSection,
- SectionImageInformation,
- &SectionImageInfo,
- sizeof(SectionImageInfo),
- NULL);
- if(!NT_SUCCESS(Status))
- {
- DPRINT1("Unable to get SectionImageInformation, status 0x%x\n", Status);
- BaseSetLastNTError(Status);
- goto Cleanup;
+ /* We've already done all these checks, don't do them again */
+ SkipSaferAndAppCompat = TRUE;
+ DPRINT1("Retrying with: %S\n", lpCommandLine);
+ goto AppNameRetry;
}
- /* Don't execute DLLs */
- if (SectionImageInfo.ImageCharacteristics & IMAGE_FILE_DLL)
+ /* Was this image built for a version of Windows whose images we can run? */
+ Result = BasepIsImageVersionOk(ImageInformation.SubSystemMajorVersion,
+ ImageInformation.SubSystemMinorVersion);
+ if (!Result)
{
- DPRINT1("Can't execute a DLL\n");
+ /* It was not, bail out */
+ DPRINT1("Invalid subsystem version: %d.%d\n",
+ ImageInformation.SubSystemMajorVersion,
+ ImageInformation.SubSystemMinorVersion);
SetLastError(ERROR_BAD_EXE_FORMAT);
- goto Cleanup;
+ goto Quickie;
}
- /* FIXME: Check for Debugger */
+ /* Check if there is a debugger associated with the application */
+ if (DebuggerCmdLine)
+ {
+ /* 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);
+ }
- /* FIXME: Check if Machine Type and SubSys Version Match */
+ /* Protect against overflow */
+ if (n > UNICODE_STRING_MAX_CHARS)
+ {
+ BaseSetLastNTError(STATUS_NAME_TOO_LONG);
+ Result = FALSE;
+ goto Quickie;
+ }
- /* We don't support POSIX or anything else for now */
- if (IMAGE_SUBSYSTEM_WINDOWS_GUI != SectionImageInfo.SubSystemType &&
- IMAGE_SUBSYSTEM_WINDOWS_CUI != SectionImageInfo.SubSystemType)
- {
- 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;
- }
+ /* Now add the length of the debugger command-line */
+ n += wcslen(DebuggerCmdLine);
- if (IMAGE_SUBSYSTEM_WINDOWS_GUI == SectionImageInfo.SubSystemType)
- {
- /* Do not create a console for GUI applications */
- dwCreationFlags &= ~CREATE_NEW_CONSOLE;
- dwCreationFlags |= DETACHED_PROCESS;
+ /* 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,
- lpProcessAttributes,
- NULL);
+ 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)
{
- /* FIXME: Set process flag */
+ /* Set process flag */
+ Flags |= PROCESS_CREATE_FLAGS_BREAKAWAY;
}
/* Check if we're going to be debugged */
{
DPRINT1("Failed to connect to DbgUI!\n");
BaseSetLastNTError(Status);
- goto Cleanup;
+ Result = FALSE;
+ goto Quickie;
}
/* Get the debug object */
- hDebug = DbgUiGetThreadDebugObject();
+ DebugHandle = DbgUiGetThreadDebugObject();
/* Check if only this process will be debugged */
if (dwCreationFlags & DEBUG_ONLY_THIS_PROCESS)
{
/* Set process flag */
- hDebug = (HANDLE)((ULONG_PTR)hDebug | 0x1);
+ Flags |= PROCESS_CREATE_FLAGS_NO_DEBUG_INHERIT;
}
}
- /* Create the Process */
- Status = NtCreateProcess(&hProcess,
- PROCESS_ALL_ACCESS,
- ObjectAttributes,
- NtCurrentProcess(),
- (BOOLEAN)bInheritHandles,
- hSection,
- hDebug,
- NULL);
+ /* 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))
{
- DPRINT1("Unable to create process, status 0x%x\n", Status);
+ /* Go to failure path */
+ DPRINT1("Failed to create process: %lx\n", Status);
BaseSetLastNTError(Status);
- goto Cleanup;
+ Result = FALSE;
+ goto Quickie;
}
- if (PriorityClass.PriorityClass != PROCESS_PRIORITY_CLASS_INVALID)
+ /* Check if there is a priority class to set */
+ if (PriorityClass.PriorityClass)
{
- /* Set new class */
- Status = NtSetInformationProcess(hProcess,
+ /* 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));
- if(!NT_SUCCESS(Status))
+ if (RealTimePrivilegeState) RtlReleasePrivilege(RealTimePrivilegeState);
+
+ /* Check if we failed to set the priority class */
+ if (!NT_SUCCESS(Status))
{
- DPRINT1("Unable to set new process priority, status 0x%x\n", Status);
+ /* Bail out on failure */
+ DPRINT1("Failed to set priority class: %lx\n", Status);
BaseSetLastNTError(Status);
- goto Cleanup;
+ Result = FALSE;
+ goto Quickie;
}
}
- /* Set Error Mode */
+ /* Check if the caller wants the default error mode */
if (dwCreationFlags & CREATE_DEFAULT_ERROR_MODE)
{
- ULONG ErrorMode = SEM_FAILCRITICALERRORS;
- NtSetInformationProcess(hProcess,
+ /* Set Error Mode to only fail on critical errors */
+ HardErrorMode = SEM_FAILCRITICALERRORS;
+ NtSetInformationProcess(ProcessHandle,
ProcessDefaultHardErrorMode,
- &ErrorMode,
+ &HardErrorMode,
sizeof(ULONG));
}
- /* Convert the directory to a full path */
+ /* 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)
{
- /* Allocate a buffer */
+ /* Allocate a buffer so we can keep a Unicode copy */
+ DPRINT1("Current directory: %S\n", lpCurrentDirectory);
CurrentDirectory = RtlAllocateHeap(RtlGetProcessHeap(),
0,
- (MAX_PATH + 1) * sizeof(WCHAR));
- if (CurrentDirectory == NULL)
+ (MAX_PATH * sizeof(WCHAR)) +
+ sizeof(UNICODE_NULL));
+ if (!CurrentDirectory)
{
- DPRINT1("Cannot allocate memory for directory name\n");
- SetLastError(ERROR_NOT_ENOUGH_MEMORY);
- goto Cleanup;
+ /* Bail out if this failed */
+ BaseSetLastNTError(STATUS_NO_MEMORY);
+ Result = FALSE;
+ goto Quickie;
}
- /* Get the length */
- if (GetFullPathNameW(lpCurrentDirectory,
- MAX_PATH,
- CurrentDirectory,
- &CurrentDirectoryPart) > MAX_PATH)
+ /* 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))
{
- DPRINT1("Directory name too long\n");
+ /* It isn't, so bail out */
+ DPRINT1("Current directory is invalid\n");
SetLastError(ERROR_DIRECTORY);
- goto Cleanup;
+ Result = FALSE;
+ goto Quickie;
}
}
/* Insert quotes if needed */
- if (QuotesNeeded || CmdLineIsAppName)
+ if ((QuotesNeeded) || (CmdLineIsAppName))
{
- /* Allocate a buffer */
+ /* Allocate our buffer, plus enough space for quotes and a NULL */
QuotedCmdLine = RtlAllocateHeap(RtlGetProcessHeap(),
0,
- (wcslen(lpCommandLine) + 2 + 1) *
- sizeof(WCHAR));
- if (QuotedCmdLine == NULL)
+ (wcslen(lpCommandLine) * sizeof(WCHAR)) +
+ (2 * sizeof(L'\"') + sizeof(UNICODE_NULL)));
+ if (QuotedCmdLine)
{
- DPRINT1("Cannot allocate memory for quoted command line\n");
- SetLastError(ERROR_NOT_ENOUGH_MEMORY);
- goto Cleanup;
- }
+ /* Copy the first quote */
+ wcscpy(QuotedCmdLine, L"\"");
+
+ /* Save the current null-character */
+ if (QuotesNeeded)
+ {
+ SaveChar = *NullBuffer;
+ *NullBuffer = UNICODE_NULL;
+ }
- /* Copy the first quote */
- wcscpy(QuotedCmdLine, L"\"");
+ /* 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;
+ }
+ }
- /* Save a null char */
- if (QuotesNeeded)
+ /* 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))
{
- SaveChar = *NullBuffer;
- *NullBuffer = UNICODE_NULL;
+ /* 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);
+
+ /* 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);
+ }
+
+ /* 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);
- /* Add the command line and the finishing quote */
- wcscat(QuotedCmdLine, lpCommandLine);
- wcscat(QuotedCmdLine, L"\"");
+ /* 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);
- /* Add the null char */
- if (QuotesNeeded)
+ /* Also check if the parent is also a GUI process */
+ NtHeaders = RtlImageNtHeader(GetModuleHandle(NULL));
+ if ((NtHeaders) &&
+ (NtHeaders->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI))
{
- *NullBuffer = SaveChar;
- wcscat(QuotedCmdLine, NullBuffer);
+ /* 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));
- DPRINT("Quoted CmdLine: %S\n", QuotedCmdLine);
+ /* 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 (Escape)
+ /* Check if we have a token due to Authz/Safer, not passed by the user */
+ if ((TokenHandle) && !(hUserToken))
{
- if (QuotedCmdLine == NULL)
+ /* Replace the process and/or thread token with the one from Safer */
+ Status = BasepReplaceProcessThreadTokens(TokenHandle,
+ ProcessHandle,
+ ThreadHandle);
+ if (!NT_SUCCESS(Status))
{
- QuotedCmdLine = RtlAllocateHeap(RtlGetProcessHeap(),
- 0,
- (wcslen(lpCommandLine) + 1) * sizeof(WCHAR));
- if (QuotedCmdLine == NULL)
- {
- SetLastError(ERROR_NOT_ENOUGH_MEMORY);
- goto Cleanup;
- }
- wcscpy(QuotedCmdLine, lpCommandLine);
+ /* 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 = QuotedCmdLine;
- while (NULL != (ScanString = wcschr(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))
{
- ScanString++;
- if (*ScanString == L'\"' || *ScanString == L'^' || *ScanString == L'\\')
+ /* 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)
{
- memmove(ScanString-1, ScanString, wcslen(ScanString) * sizeof(WCHAR) + sizeof(WCHAR));
+ /* No Client ID should be returned in this case */
+ ClientId.UniqueProcess = 0;
+ ClientId.UniqueThread = 0;
}
}
- }
+ else
+ {
+ /* OR-in the special flag to indicate this is not a separate VDM */
+ AddToHandle(VdmWaitObject, 1);
- /* Get the Process Information */
- Status = NtQueryInformationProcess(hProcess,
- ProcessBasicInformation,
- &ProcessBasicInfo,
- sizeof(ProcessBasicInfo),
- NULL);
+ /* Return handle to the caller */
+ lpProcessInformation->hProcess = VdmWaitObject;
+ }
- /* 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))
+ /* Close the original process handle, since it's not needed for VDM */
+ if (ProcessHandle) NtClose(ProcessHandle);
+ }
+ else
{
- RtlDestroyEnvironment(lpEnvironment);
+ /* This is a regular process, so return the real process handle */
+ lpProcessInformation->hProcess = ProcessHandle;
}
- /* Close the section */
- NtClose(hSection);
- hSection = NULL;
+ /* 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;
- /* Duplicate the handles if needed */
- if (!bInheritHandles && !(StartupInfo.dwFlags & STARTF_USESTDHANDLES) &&
- SectionImageInfo.SubSystemType == IMAGE_SUBSYSTEM_WINDOWS_CUI)
+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)
{
- PRTL_USER_PROCESS_PARAMETERS RemoteParameters;
+ /* Reinitialize the executable path */
+ RtlInitEmptyUnicodeString(&SxsWin32ExePath, NULL, 0);
+ SxsWin32ExePath.Length = 0;
- /* Get the remote parameters */
- Status = NtReadVirtualMemory(hProcess,
- &RemotePeb->ProcessParameters,
- &RemoteParameters,
- sizeof(PVOID),
- NULL);
- if (!NT_SUCCESS(Status))
+ /* 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)
{
- DPRINT1("Failed to read memory\n");
- goto Cleanup;
+ /* 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);
- /* 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);
+ /* If this was the VDM environment too, clear that as well */
+ if (VdmUnicodeEnv.Buffer == lpEnvironment) VdmUnicodeEnv.Buffer = NULL;
+ lpEnvironment = NULL;
}
- /* Create the first thread */
- DPRINT("Creating thread for process (EntryPoint = 0x%p)\n",
- SectionImageInfo.TransferAddress);
- hThread = BasepCreateFirstThread(hProcess,
- lpThreadAttributes,
- &SectionImageInfo,
- &ClientId,
- bInheritHandles,
- dwCreationFlags);
+ /* 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);
- if (hThread == NULL)
+ /* Close open file/section handles */
+ if (FileHandle) NtClose(FileHandle);
+ if (SectionHandle) NtClose(SectionHandle);
+
+ /* If we have a thread handle, this was a failure path */
+ if (ThreadHandle)
{
- DPRINT1("Could not create Initial Thread\n");
- /* FIXME - set last error code */
- goto Cleanup;
+ /* So kill the process and close the thread handle */
+ NtTerminateProcess(ProcessHandle, 0);
+ NtClose(ThreadHandle);
}
- if (!(dwCreationFlags & CREATE_SUSPENDED))
+ /* 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)
{
- NtResumeThread(hThread, &Dummy);
+ /* 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);
+ }
}
- /* 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;
+ /* Free any temporary app compatibility data, it's no longer needed */
+ BasepFreeAppCompatData(AppCompatData, AppCompatSxsData);
-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 a few strings. The API takes care of these possibly being NULL */
+ RtlFreeUnicodeString(&VdmString);
+ RtlFreeUnicodeString(&DebuggerString);
- /* Kill any handles still alive */
- if (hSection) NtClose(hSection);
- if (hThread)
+ /* Check if we had built any sort of VDM environment */
+ if ((VdmAnsiEnv.Buffer) || (VdmUnicodeEnv.Buffer))
{
- /* We don't know any more details than this */
- NtTerminateProcess(hProcess, STATUS_UNSUCCESSFUL);
- NtClose(hThread);
+ /* Free it */
+ BaseDestroyVDMEnvironment(&VdmAnsiEnv, &VdmUnicodeEnv);
}
- if (hProcess) NtClose(hProcess);
- /* Return Success */
- return Ret;
+ /* 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;
}
/*
LPPROCESS_INFORMATION lpProcessInformation)
{
/* Call the internal (but exported) version */
- return CreateProcessInternalW(0,
+ return CreateProcessInternalW(NULL,
lpApplicationName,
lpCommandLine,
lpProcessAttributes,
LPPROCESS_INFORMATION lpProcessInformation)
{
/* Call the internal (but exported) version */
- return CreateProcessInternalA(0,
+ return CreateProcessInternalA(NULL,
lpApplicationName,
lpCommandLine,
lpProcessAttributes,