mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3275 lines
88 KiB
3275 lines
88 KiB
/*++
|
|
|
|
Copyright (c) 1990 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
vdm.c
|
|
|
|
Abstract:
|
|
|
|
This module implements Win32 APIs for VDMs
|
|
|
|
Author:
|
|
|
|
Sudeepb Bharati (sudeepb) 04-Sep-1991
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "basedll.h"
|
|
#pragma hdrstop
|
|
|
|
#include "ntdbg.h"
|
|
|
|
|
|
BOOL
|
|
APIENTRY
|
|
GetBinaryTypeA(
|
|
IN LPCSTR lpApplicationName,
|
|
OUT LPDWORD lpBinaryType
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description: ANSI version of GetBinaryTypeW.
|
|
This API returns the binary type of lpApplicationName.
|
|
|
|
Arguments:
|
|
lpApplicationName - Full pathname of the binary
|
|
lpBinaryType - pointer where binary type will be returned.
|
|
|
|
Return Value:
|
|
TRUE - if SUCCESS; lpBinaryType has following
|
|
SCS_32BIT_BINARY - Win32 Binary (NT or Chicago)
|
|
SCS_DOS_BINARY - DOS Binary
|
|
SCS_WOW_BINARY - Windows 3.X Binary
|
|
SCS_PIF_BINARY - PIF file
|
|
SCS_POSIX_BINARY - POSIX Binary
|
|
SCS_OS216_BINARY - OS/2 Binary
|
|
FALSE - if file not found or of unknown type. More info with GetLastError
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
PUNICODE_STRING CommandLine;
|
|
ANSI_STRING AnsiString;
|
|
UNICODE_STRING DynamicCommandLine;
|
|
BOOLEAN bReturn = FALSE;
|
|
|
|
CommandLine = &NtCurrentTeb()->StaticUnicodeString;
|
|
RtlInitAnsiString(&AnsiString,lpApplicationName);
|
|
if ( (ULONG)AnsiString.Length<<1 < (ULONG)NtCurrentTeb()->StaticUnicodeString.MaximumLength ) {
|
|
DynamicCommandLine.Buffer = NULL;
|
|
Status = RtlAnsiStringToUnicodeString(CommandLine,&AnsiString,FALSE);
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
BaseSetLastNTError(Status);
|
|
return FALSE;
|
|
}
|
|
}
|
|
else {
|
|
Status = RtlAnsiStringToUnicodeString(&DynamicCommandLine,&AnsiString,TRUE);
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
BaseSetLastNTError(Status);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
bReturn = GetBinaryTypeW(
|
|
DynamicCommandLine.Buffer ? DynamicCommandLine.Buffer : CommandLine->Buffer,
|
|
lpBinaryType);
|
|
|
|
if (DynamicCommandLine.Buffer) {
|
|
RtlFreeUnicodeString(&DynamicCommandLine);
|
|
DynamicCommandLine.Buffer = NULL;
|
|
}
|
|
return(bReturn);
|
|
|
|
}
|
|
|
|
|
|
BOOL
|
|
WINAPI
|
|
GetBinaryTypeW(
|
|
IN LPCWSTR lpApplicationName,
|
|
OUT LPDWORD lpBinaryType
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description: Unicode version.
|
|
This API returns the binary type of lpApplicationName.
|
|
|
|
Arguments:
|
|
lpApplicationName - Full pathname of the binary
|
|
lpBinaryType - pointer where binary type will be returned.
|
|
|
|
Return Value:
|
|
TRUE - if SUCCESS; lpBinaryType has following
|
|
SCS_32BIT_BINARY - Win32 Binary (NT or Chicago)
|
|
SCS_DOS_BINARY - DOS Binary
|
|
SCS_WOW_BINARY - Windows 3.X Binary
|
|
SCS_PIF_BINARY - PIF file
|
|
SCS_POSIX_BINARY - POSIX Binary
|
|
SCS_OS216_BINARY - OS/2 Binary
|
|
FALSE - if file not found or of unknown type. More info with GetLastError
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
UNICODE_STRING PathName;
|
|
RTL_RELATIVE_NAME RelativeName;
|
|
BOOLEAN TranslationStatus;
|
|
OBJECT_ATTRIBUTES Obja;
|
|
PVOID FreeBuffer = NULL;
|
|
HANDLE FileHandle, SectionHandle=NULL;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
LONG fBinaryType = SCS_32BIT_BINARY;
|
|
BOOLEAN bReturn = FALSE;
|
|
SECTION_IMAGE_INFORMATION ImageInformation;
|
|
|
|
|
|
try {
|
|
//
|
|
// Translate to an NT name.
|
|
//
|
|
|
|
TranslationStatus = RtlDosPathNameToNtPathName_U(
|
|
// DynamicCommandLine.Buffer ? DynamicCommandLine.Buffer : CommandLine->Buffer,
|
|
lpApplicationName,
|
|
&PathName,
|
|
NULL,
|
|
&RelativeName
|
|
);
|
|
|
|
if ( !TranslationStatus ) {
|
|
BaseSetLastNTError(STATUS_OBJECT_NAME_INVALID);
|
|
goto GBTtryexit;
|
|
}
|
|
|
|
FreeBuffer = PathName.Buffer;
|
|
|
|
if ( RelativeName.RelativeName.Length ) {
|
|
PathName = *(PUNICODE_STRING)&RelativeName.RelativeName;
|
|
}
|
|
else {
|
|
RelativeName.ContainingDirectory = NULL;
|
|
}
|
|
|
|
InitializeObjectAttributes(
|
|
&Obja,
|
|
&PathName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
RelativeName.ContainingDirectory,
|
|
NULL
|
|
);
|
|
|
|
//
|
|
// Open the file for execute access
|
|
//
|
|
|
|
Status = NtOpenFile(
|
|
&FileHandle,
|
|
SYNCHRONIZE | FILE_EXECUTE,
|
|
&Obja,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_READ | FILE_SHARE_DELETE,
|
|
FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE
|
|
);
|
|
if (!NT_SUCCESS(Status) ) {
|
|
BaseSetLastNTError(Status);
|
|
goto GBTtryexit;
|
|
}
|
|
|
|
//
|
|
// Create a section object backed by the file
|
|
//
|
|
|
|
Status = NtCreateSection(
|
|
&SectionHandle,
|
|
SECTION_ALL_ACCESS,
|
|
NULL,
|
|
NULL,
|
|
PAGE_EXECUTE,
|
|
SEC_IMAGE,
|
|
FileHandle
|
|
);
|
|
NtClose(FileHandle);
|
|
|
|
if (!NT_SUCCESS(Status) ) {
|
|
|
|
SectionHandle = NULL;
|
|
|
|
switch (Status) {
|
|
case STATUS_INVALID_IMAGE_NE_FORMAT:
|
|
#ifdef i386
|
|
fBinaryType = SCS_OS216_BINARY;
|
|
break;
|
|
#endif
|
|
|
|
case STATUS_INVALID_IMAGE_PROTECT:
|
|
fBinaryType = SCS_DOS_BINARY;
|
|
break;
|
|
|
|
case STATUS_INVALID_IMAGE_WIN_16:
|
|
fBinaryType = SCS_WOW_BINARY;
|
|
break;
|
|
|
|
case STATUS_INVALID_IMAGE_NOT_MZ:
|
|
fBinaryType = BaseIsDosApplication(&PathName, Status);
|
|
if (!fBinaryType){
|
|
BaseSetLastNTError(Status);
|
|
goto GBTtryexit;
|
|
}
|
|
fBinaryType = (fBinaryType == BINARY_TYPE_DOS_PIF) ?
|
|
SCS_PIF_BINARY : SCS_DOS_BINARY;
|
|
break;
|
|
|
|
default:
|
|
BaseSetLastNTError(Status);
|
|
goto GBTtryexit;
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// Query the section
|
|
//
|
|
|
|
Status = NtQuerySection(
|
|
SectionHandle,
|
|
SectionImageInformation,
|
|
&ImageInformation,
|
|
sizeof( ImageInformation ),
|
|
NULL
|
|
);
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
BaseSetLastNTError(Status);
|
|
goto GBTtryexit;
|
|
}
|
|
|
|
if (ImageInformation.ImageCharacteristics & IMAGE_FILE_DLL) {
|
|
SetLastError(ERROR_BAD_EXE_FORMAT);
|
|
goto GBTtryexit;
|
|
}
|
|
|
|
if (ImageInformation.Machine !=
|
|
RtlImageNtHeader(NtCurrentPeb()->ImageBaseAddress)->FileHeader.Machine) {
|
|
|
|
#ifdef MIPS
|
|
if ( ImageInformation.Machine == IMAGE_FILE_MACHINE_R3000 ||
|
|
ImageInformation.Machine == IMAGE_FILE_MACHINE_R4000 ) {
|
|
;
|
|
}
|
|
else {
|
|
SetLastError(ERROR_BAD_EXE_FORMAT);
|
|
goto GBTtryexit;
|
|
}
|
|
#else
|
|
SetLastError(ERROR_BAD_EXE_FORMAT);
|
|
goto GBTtryexit;
|
|
#endif // MIPS
|
|
}
|
|
|
|
if ( ImageInformation.SubSystemType != IMAGE_SUBSYSTEM_WINDOWS_GUI &&
|
|
ImageInformation.SubSystemType != IMAGE_SUBSYSTEM_WINDOWS_CUI ) {
|
|
|
|
|
|
if ( ImageInformation.SubSystemType == IMAGE_SUBSYSTEM_POSIX_CUI ) {
|
|
fBinaryType = SCS_POSIX_BINARY;
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
*lpBinaryType = fBinaryType;
|
|
|
|
bReturn = TRUE;
|
|
|
|
GBTtryexit:;
|
|
}
|
|
finally {
|
|
|
|
if (SectionHandle)
|
|
NtClose(SectionHandle);
|
|
|
|
if (FreeBuffer)
|
|
RtlFreeHeap(RtlProcessHeap(), 0,FreeBuffer);
|
|
}
|
|
return bReturn;
|
|
}
|
|
|
|
|
|
VOID
|
|
APIENTRY
|
|
VDMOperationStarted
|
|
(
|
|
BOOL IsWowCaller
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
This routine is used by MVDM to tell base that it has hooked
|
|
ctrl-c handler with console. If the cmd window is killed
|
|
before VDM could hook ctrl-c, then we wont get a chance to
|
|
cleanup our data structures. The absence of this call tells
|
|
base that it has to clean up the resources next time a
|
|
call is made to create a VDM.
|
|
|
|
Arguments:
|
|
IsWowCaller - TRUE if the caller is WOWVDM
|
|
|
|
Return Value:
|
|
|
|
None
|
|
--*/
|
|
|
|
{
|
|
BaseUpdateVDMEntry(UPDATE_VDM_HOOKED_CTRLC,
|
|
NULL,
|
|
0,
|
|
IsWowCaller);
|
|
return;
|
|
}
|
|
|
|
|
|
BOOL
|
|
APIENTRY
|
|
GetNextVDMCommand(
|
|
PVDMINFO lpVDMInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
This routine is used by MVDM to get a new command to execute. The
|
|
VDM is blocked untill a DOS/WOW binary is encountered.
|
|
|
|
|
|
Arguments:
|
|
lpVDMInfo - pointer to VDMINFO where new DOS command and other
|
|
enviornment information is returned.
|
|
|
|
if lpVDMInfo is NULL, then the caller is
|
|
asking whether its the first VDM in the system.
|
|
|
|
Return Value:
|
|
|
|
TRUE - The operation was successful. lpVDMInfo is filled in.
|
|
|
|
FALSE/NULL - The operation failed.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
NTSTATUS Status;
|
|
BASE_API_MSG m;
|
|
PBASE_GET_NEXT_VDM_COMMAND_MSG a = (PBASE_GET_NEXT_VDM_COMMAND_MSG)&m.u.GetNextVDMCommand;
|
|
PBASE_EXIT_VDM_MSG c= (PBASE_EXIT_VDM_MSG)&m.u.ExitVDM;
|
|
PBASE_IS_FIRST_VDM_MSG d= (PBASE_IS_FIRST_VDM_MSG)&m.u.IsFirstVDM;
|
|
PBASE_SET_REENTER_COUNT_MSG e = (PBASE_SET_REENTER_COUNT_MSG)&m.u.SetReenterCount;
|
|
PCSR_CAPTURE_HEADER CaptureBuffer;
|
|
ULONG Len,nPointers;
|
|
USHORT VDMStateSave;
|
|
|
|
// Special case to query the first VDM In the system.
|
|
if(lpVDMInfo == NULL){
|
|
Status = CsrClientCallServer(
|
|
(PCSR_API_MSG)&m,
|
|
NULL,
|
|
CSR_MAKE_API_NUMBER(BASESRV_SERVERDLL_INDEX,
|
|
BasepIsFirstVDM
|
|
),
|
|
sizeof( *d )
|
|
);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
return(d->FirstVDM);
|
|
}
|
|
else {
|
|
BaseSetLastNTError(Status);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// Special case to increment/decrement the re-enterancy count
|
|
|
|
if (lpVDMInfo->VDMState == INCREMENT_REENTER_COUNT ||
|
|
lpVDMInfo->VDMState == DECREMENT_REENTER_COUNT) {
|
|
|
|
e->ConsoleHandle = NtCurrentPeb()->ProcessParameters->ConsoleHandle;
|
|
e->fIncDec = lpVDMInfo->VDMState;
|
|
Status = CsrClientCallServer(
|
|
(PCSR_API_MSG)&m,
|
|
NULL,
|
|
CSR_MAKE_API_NUMBER( BASESRV_SERVERDLL_INDEX,
|
|
BasepSetReenterCount
|
|
),
|
|
sizeof( *e )
|
|
);
|
|
if (NT_SUCCESS(Status)) {
|
|
return TRUE;
|
|
}
|
|
else {
|
|
BaseSetLastNTError(Status);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
VDMStateSave = lpVDMInfo->VDMState;
|
|
|
|
if(VDMStateSave & ASKING_FOR_WOW_BINARY)
|
|
a->ConsoleHandle = (HANDLE)-1;
|
|
else
|
|
a->ConsoleHandle = NtCurrentPeb()->ProcessParameters->ConsoleHandle;
|
|
|
|
if (lpVDMInfo->VDMState & ASKING_FOR_PIF)
|
|
a->iTask = lpVDMInfo->iTask;
|
|
|
|
a->AppLen = lpVDMInfo->AppLen;
|
|
a->PifLen = lpVDMInfo->PifLen;
|
|
a->CmdLen = lpVDMInfo->CmdSize;
|
|
a->EnvLen = lpVDMInfo->EnviornmentSize;
|
|
a->ExitCode = lpVDMInfo->ErrorCode;
|
|
a->VDMState = VDMStateSave;
|
|
a->WaitObjectForVDM = 0;
|
|
a->DesktopLen = lpVDMInfo->DesktopLen;
|
|
a->TitleLen = lpVDMInfo->TitleLen;
|
|
a->ReservedLen = lpVDMInfo->ReservedLen;
|
|
a->CurDirectoryLen = lpVDMInfo->CurDirectoryLen;
|
|
|
|
// Find the total space for capture buffer
|
|
|
|
// startup info
|
|
Len = ROUND_UP(sizeof(STARTUPINFOA),4);
|
|
nPointers = 1;
|
|
|
|
if (lpVDMInfo->CmdSize) {
|
|
Len += ROUND_UP(a->CmdLen,4);
|
|
nPointers++;
|
|
}
|
|
|
|
if (lpVDMInfo->AppLen) {
|
|
Len +=ROUND_UP(a->AppLen,4);
|
|
nPointers++;
|
|
}
|
|
|
|
if (lpVDMInfo->PifLen) {
|
|
Len +=ROUND_UP(a->PifLen,4);
|
|
nPointers++;
|
|
}
|
|
|
|
if (lpVDMInfo->Enviornment) {
|
|
nPointers++;
|
|
Len+= (lpVDMInfo->EnviornmentSize) ?
|
|
ROUND_UP(lpVDMInfo->EnviornmentSize, 4) : 4;
|
|
}
|
|
|
|
if (lpVDMInfo->CurDirectoryLen == 0)
|
|
a->CurDirectory = NULL;
|
|
else{
|
|
Len += ROUND_UP(lpVDMInfo->CurDirectoryLen,4);
|
|
nPointers++;
|
|
}
|
|
|
|
if (lpVDMInfo->DesktopLen == 0)
|
|
a->Desktop = NULL;
|
|
else {
|
|
Len += ROUND_UP(lpVDMInfo->DesktopLen,4);
|
|
nPointers++;
|
|
}
|
|
|
|
if (lpVDMInfo->TitleLen == 0)
|
|
a->Title = NULL;
|
|
else {
|
|
Len += ROUND_UP(lpVDMInfo->TitleLen,4);
|
|
nPointers++;
|
|
}
|
|
|
|
if (lpVDMInfo->ReservedLen == 0)
|
|
a->Reserved = NULL;
|
|
else {
|
|
Len += ROUND_UP(lpVDMInfo->ReservedLen,4);
|
|
nPointers++;
|
|
}
|
|
|
|
CaptureBuffer = CsrAllocateCaptureBuffer(nPointers, 0, Len);
|
|
if (CaptureBuffer == NULL) {
|
|
BaseSetLastNTError( STATUS_NO_MEMORY );
|
|
return FALSE;
|
|
}
|
|
|
|
if (lpVDMInfo->CmdLine) {
|
|
CsrAllocateMessagePointer( CaptureBuffer,
|
|
lpVDMInfo->CmdSize,
|
|
(PVOID *)&a->CmdLine
|
|
);
|
|
}
|
|
else {
|
|
a->CmdLine = NULL;
|
|
}
|
|
|
|
|
|
if (lpVDMInfo->AppLen) {
|
|
CsrAllocateMessagePointer( CaptureBuffer,
|
|
lpVDMInfo->AppLen,
|
|
(PVOID *)&a->AppName
|
|
);
|
|
}
|
|
else {
|
|
a->AppName = NULL;
|
|
}
|
|
|
|
if (lpVDMInfo->PifLen) {
|
|
CsrAllocateMessagePointer( CaptureBuffer,
|
|
lpVDMInfo->PifLen,
|
|
(PVOID *)&a->PifFile
|
|
);
|
|
}
|
|
else {
|
|
a->PifFile = NULL;
|
|
}
|
|
|
|
|
|
if (lpVDMInfo->EnviornmentSize) {
|
|
CsrAllocateMessagePointer( CaptureBuffer,
|
|
lpVDMInfo->EnviornmentSize,
|
|
(PVOID *)&a->Env
|
|
);
|
|
}
|
|
else {
|
|
a->Env = NULL;
|
|
}
|
|
|
|
if (lpVDMInfo->CurDirectoryLen)
|
|
CsrAllocateMessagePointer( CaptureBuffer,
|
|
lpVDMInfo->CurDirectoryLen,
|
|
(PVOID *)&a->CurDirectory
|
|
);
|
|
else
|
|
a->CurDirectory = NULL;
|
|
|
|
|
|
CsrAllocateMessagePointer( CaptureBuffer,
|
|
sizeof(STARTUPINFOA),
|
|
(PVOID *)&a->StartupInfo
|
|
);
|
|
|
|
if (lpVDMInfo->DesktopLen)
|
|
CsrAllocateMessagePointer( CaptureBuffer,
|
|
lpVDMInfo->DesktopLen,
|
|
(PVOID *)&a->Desktop
|
|
);
|
|
else
|
|
a->Desktop = NULL;
|
|
|
|
if (lpVDMInfo->TitleLen)
|
|
CsrAllocateMessagePointer( CaptureBuffer,
|
|
lpVDMInfo->TitleLen,
|
|
(PVOID *)&a->Title
|
|
);
|
|
else
|
|
a->Title = NULL;
|
|
|
|
if (lpVDMInfo->ReservedLen)
|
|
CsrAllocateMessagePointer( CaptureBuffer,
|
|
lpVDMInfo->ReservedLen,
|
|
(PVOID *)&a->Reserved
|
|
);
|
|
else
|
|
a->Reserved = NULL;
|
|
|
|
retry:
|
|
Status = CsrClientCallServer(
|
|
(PCSR_API_MSG)&m,
|
|
CaptureBuffer,
|
|
CSR_MAKE_API_NUMBER( BASESRV_SERVERDLL_INDEX,
|
|
BasepGetNextVDMCommand
|
|
),
|
|
sizeof( *a )
|
|
);
|
|
|
|
if (a->WaitObjectForVDM) {
|
|
Status = NtWaitForSingleObject(a->WaitObjectForVDM,FALSE,NULL);
|
|
if (Status != STATUS_SUCCESS){
|
|
BaseSetLastNTError(Status);
|
|
return FALSE;
|
|
}
|
|
else {
|
|
a->VDMState |= ASKING_FOR_SECOND_TIME;
|
|
a->ExitCode = 0;
|
|
goto retry;
|
|
}
|
|
}
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
Status = (NTSTATUS)m.ReturnValue;
|
|
}
|
|
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
if (Status == STATUS_INVALID_PARAMETER) {
|
|
//This means one of the buffer size is less than required.
|
|
lpVDMInfo->CmdSize = a->CmdLen;
|
|
lpVDMInfo->AppLen = a->AppLen;
|
|
lpVDMInfo->PifLen = a->PifLen;
|
|
lpVDMInfo->EnviornmentSize = a->EnvLen;
|
|
lpVDMInfo->CurDirectoryLen = a->CurDirectoryLen;
|
|
lpVDMInfo->DesktopLen = a->DesktopLen;
|
|
lpVDMInfo->TitleLen = a->TitleLen;
|
|
lpVDMInfo->ReservedLen = a->ReservedLen;
|
|
}
|
|
else {
|
|
lpVDMInfo->CmdSize = 0;
|
|
lpVDMInfo->AppLen = 0;
|
|
lpVDMInfo->PifLen = 0;
|
|
lpVDMInfo->EnviornmentSize = 0;
|
|
lpVDMInfo->CurDirectoryLen = 0;
|
|
lpVDMInfo->DesktopLen = 0;
|
|
lpVDMInfo->TitleLen = 0;
|
|
lpVDMInfo->ReservedLen = 0;
|
|
}
|
|
CsrFreeCaptureBuffer( CaptureBuffer );
|
|
BaseSetLastNTError(Status);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
try {
|
|
|
|
if (lpVDMInfo->CmdSize)
|
|
RtlMoveMemory(lpVDMInfo->CmdLine,
|
|
a->CmdLine,
|
|
a->CmdLen);
|
|
|
|
|
|
if (lpVDMInfo->AppLen)
|
|
RtlMoveMemory(lpVDMInfo->AppName,
|
|
a->AppName,
|
|
a->AppLen);
|
|
|
|
if (lpVDMInfo->PifLen)
|
|
RtlMoveMemory(lpVDMInfo->PifFile,
|
|
a->PifFile,
|
|
a->PifLen);
|
|
|
|
|
|
if (lpVDMInfo->Enviornment)
|
|
RtlMoveMemory(lpVDMInfo->Enviornment,
|
|
a->Env,
|
|
a->EnvLen);
|
|
|
|
|
|
if (lpVDMInfo->CurDirectoryLen)
|
|
RtlMoveMemory(lpVDMInfo->CurDirectory,
|
|
a->CurDirectory,
|
|
a->CurDirectoryLen);
|
|
|
|
if (a->VDMState & STARTUP_INFO_RETURNED)
|
|
RtlMoveMemory(&lpVDMInfo->StartupInfo,
|
|
a->StartupInfo,
|
|
sizeof(STARTUPINFOA));
|
|
|
|
if (lpVDMInfo->DesktopLen){
|
|
RtlMoveMemory(lpVDMInfo->Desktop,
|
|
a->Desktop,
|
|
a->DesktopLen);
|
|
lpVDMInfo->StartupInfo.lpDesktop = lpVDMInfo->Desktop;
|
|
}
|
|
|
|
|
|
if (lpVDMInfo->TitleLen){
|
|
RtlMoveMemory(lpVDMInfo->Title,
|
|
a->Title,
|
|
a->TitleLen);
|
|
lpVDMInfo->StartupInfo.lpTitle = lpVDMInfo->Title;
|
|
}
|
|
|
|
if (lpVDMInfo->ReservedLen){
|
|
RtlMoveMemory(lpVDMInfo->Reserved,
|
|
a->Reserved,
|
|
a->ReservedLen);
|
|
lpVDMInfo->StartupInfo.lpReserved = lpVDMInfo->Reserved;
|
|
}
|
|
|
|
lpVDMInfo->CmdSize = a->CmdLen;
|
|
lpVDMInfo->AppLen = a->AppLen;
|
|
lpVDMInfo->PifLen = a->PifLen;
|
|
lpVDMInfo->EnviornmentSize = a->EnvLen;
|
|
if (a->VDMState & STARTUP_INFO_RETURNED)
|
|
lpVDMInfo->VDMState = STARTUP_INFO_RETURNED;
|
|
else
|
|
lpVDMInfo->VDMState = 0;
|
|
lpVDMInfo->CurDrive = a->CurrentDrive;
|
|
lpVDMInfo->StdIn = a->StdIn;
|
|
lpVDMInfo->StdOut = a->StdOut;
|
|
lpVDMInfo->StdErr = a->StdErr;
|
|
lpVDMInfo->iTask = a->iTask;
|
|
lpVDMInfo->CodePage = a->CodePage;
|
|
lpVDMInfo->CurDirectoryLen = a->CurDirectoryLen;
|
|
lpVDMInfo->DesktopLen = a->DesktopLen;
|
|
lpVDMInfo->TitleLen = a->TitleLen;
|
|
lpVDMInfo->ReservedLen = a->ReservedLen;
|
|
lpVDMInfo->dwCreationFlags = a->dwCreationFlags;
|
|
lpVDMInfo->fComingFromBat = a->fComingFromBat;
|
|
|
|
CsrFreeCaptureBuffer( CaptureBuffer );
|
|
return TRUE;
|
|
}
|
|
except ( EXCEPTION_EXECUTE_HANDLER ) {
|
|
BaseSetLastNTError(GetExceptionCode());
|
|
CsrFreeCaptureBuffer( CaptureBuffer );
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
APIENTRY
|
|
ExitVDM(
|
|
BOOL IsWowCaller,
|
|
ULONG iWowTask
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
This routine is used by MVDM to exit.
|
|
|
|
|
|
Arguments:
|
|
IsWowCaller - TRUE if the caller is WOWVDM.
|
|
FALSE if the caller is DOSVDM
|
|
|
|
iWowTask - if IsWowCaller == FALSE then Dont Care
|
|
- if IsWowCaller == TRUE && iWowTask != -1 kill iWowTask task
|
|
- if IsWowCaller == TRUE && iWowTask == -1 kill all wow task
|
|
|
|
Return Value:
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
NTSTATUS Status;
|
|
BASE_API_MSG m;
|
|
PBASE_EXIT_VDM_MSG c= (PBASE_EXIT_VDM_MSG)&m.u.ExitVDM;
|
|
|
|
|
|
if(IsWowCaller){
|
|
c->ConsoleHandle = (HANDLE)-1;
|
|
c->iWowTask = iWowTask;
|
|
}
|
|
else {
|
|
c->ConsoleHandle = NtCurrentPeb()->ProcessParameters->ConsoleHandle;
|
|
}
|
|
|
|
c->WaitObjectForVDM =0;
|
|
|
|
Status = CsrClientCallServer(
|
|
(PCSR_API_MSG)&m,
|
|
NULL,
|
|
CSR_MAKE_API_NUMBER( BASESRV_SERVERDLL_INDEX,
|
|
BasepExitVDM
|
|
),
|
|
sizeof( *c )
|
|
);
|
|
if (NT_SUCCESS(Status) && c->WaitObjectForVDM) {
|
|
NtClose (c->WaitObjectForVDM);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
Set new VDM current directories
|
|
|
|
Arguments:
|
|
cchCurDir - length of buffer in bytes
|
|
lpszCurDir - buffer to return the current director of NTVDM
|
|
|
|
Return Value:
|
|
TRUE if function succeed
|
|
FALSE if function failed, GetLastError() has the error code
|
|
--*/
|
|
|
|
|
|
BOOL
|
|
APIENTRY
|
|
SetVDMCurrentDirectories(
|
|
IN ULONG cchCurDirs,
|
|
IN LPSTR lpszzCurDirs
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PCSR_CAPTURE_HEADER CaptureBuffer;
|
|
BASE_API_MSG m;
|
|
PBASE_GET_SET_VDM_CUR_DIRS_MSG a = (PBASE_GET_SET_VDM_CUR_DIRS_MSG)&m.u.GetSetVDMCurDirs;
|
|
|
|
a->ConsoleHandle = NtCurrentPeb()->ProcessParameters->ConsoleHandle;
|
|
// caller must have a valid console(WOW will fail)
|
|
if (a->ConsoleHandle == (HANDLE) -1) {
|
|
BaseSetLastNTError(STATUS_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
if (cchCurDirs && lpszzCurDirs) {
|
|
// get capture buffer, one pointer in the message
|
|
|
|
CaptureBuffer = CsrAllocateCaptureBuffer(1, 0, cchCurDirs);
|
|
if (CaptureBuffer == NULL) {
|
|
BaseSetLastNTError( STATUS_NO_MEMORY );
|
|
return FALSE;
|
|
}
|
|
|
|
CsrAllocateMessagePointer( CaptureBuffer,
|
|
cchCurDirs,
|
|
(PVOID *)&a->lpszzCurDirs
|
|
);
|
|
|
|
a->cchCurDirs = cchCurDirs;
|
|
try {
|
|
RtlMoveMemory(a->lpszzCurDirs, lpszzCurDirs, cchCurDirs);
|
|
}
|
|
except (EXCEPTION_EXECUTE_HANDLER) {
|
|
BaseSetLastNTError(GetExceptionCode());
|
|
CsrFreeCaptureBuffer(CaptureBuffer);
|
|
return FALSE;
|
|
}
|
|
Status = CsrClientCallServer(
|
|
(PCSR_API_MSG)&m,
|
|
CaptureBuffer,
|
|
CSR_MAKE_API_NUMBER(BASESRV_SERVERDLL_INDEX,
|
|
BasepSetVDMCurDirs
|
|
),
|
|
sizeof( *a )
|
|
);
|
|
CsrFreeCaptureBuffer(CaptureBuffer);
|
|
|
|
if (!NT_SUCCESS(Status) || !NT_SUCCESS((NTSTATUS)m.ReturnValue)) {
|
|
BaseSetLastNTError(Status);
|
|
return FALSE;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
To return current directory of NTVDM.
|
|
This allows the parent process(CMD.EXE in most cases) to keep track the
|
|
current directory after each VDM execution.
|
|
NOTE: this function doesn't apply to wow
|
|
|
|
Arguments:
|
|
cchCurDir - length of buffer in bytes
|
|
lpszCurDir - buffer to return the current director of NTVDM
|
|
|
|
Note: We don't require the process id to the running VDM because
|
|
current directories are global to every VDMs under a single NTVDM
|
|
control -- each console handle has its own current directories
|
|
Return Value:
|
|
ULONG - (1). number of bytes written to the given buffer if succeed
|
|
(2). lentgh of the current directory including NULL
|
|
if the provided buffer is not large enough
|
|
(3). 0 then GetLastError() has the error code
|
|
--*/
|
|
|
|
|
|
ULONG
|
|
APIENTRY
|
|
GetVDMCurrentDirectories(
|
|
IN ULONG cchCurDirs,
|
|
IN LPSTR lpszzCurDirs
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PCSR_CAPTURE_HEADER CaptureBuffer;
|
|
BASE_API_MSG m;
|
|
PBASE_GET_SET_VDM_CUR_DIRS_MSG a = (PBASE_GET_SET_VDM_CUR_DIRS_MSG)&m.u.GetSetVDMCurDirs;
|
|
|
|
|
|
a->ConsoleHandle = NtCurrentPeb()->ProcessParameters->ConsoleHandle;
|
|
if (a->ConsoleHandle == (HANDLE) -1) {
|
|
BaseSetLastNTError(STATUS_INVALID_PARAMETER);
|
|
return 0L;
|
|
}
|
|
if (cchCurDirs && lpszzCurDirs) {
|
|
CaptureBuffer = CsrAllocateCaptureBuffer(1, 0, cchCurDirs);
|
|
if (CaptureBuffer == NULL) {
|
|
BaseSetLastNTError( STATUS_NO_MEMORY );
|
|
return FALSE;
|
|
}
|
|
|
|
CsrAllocateMessagePointer( CaptureBuffer,
|
|
cchCurDirs,
|
|
(PVOID *)&a->lpszzCurDirs
|
|
);
|
|
|
|
a->cchCurDirs = cchCurDirs;
|
|
}
|
|
else {
|
|
a->cchCurDirs = 0;
|
|
a->lpszzCurDirs = NULL;
|
|
CaptureBuffer = NULL;
|
|
}
|
|
|
|
m.ReturnValue = 0xffffffff;
|
|
|
|
Status = CsrClientCallServer(
|
|
(PCSR_API_MSG)&m,
|
|
CaptureBuffer,
|
|
CSR_MAKE_API_NUMBER(BASESRV_SERVERDLL_INDEX,
|
|
BasepGetVDMCurDirs
|
|
),
|
|
sizeof( *a )
|
|
);
|
|
|
|
if (m.ReturnValue == 0xffffffff) {
|
|
a->cchCurDirs = 0;
|
|
}
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
Status = m.ReturnValue;
|
|
}
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
try {
|
|
RtlMoveMemory(lpszzCurDirs, a->lpszzCurDirs, a->cchCurDirs);
|
|
}
|
|
except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = GetExceptionCode();
|
|
a->cchCurDirs = 0;
|
|
}
|
|
}
|
|
else {
|
|
BaseSetLastNTError(Status);
|
|
}
|
|
|
|
if (CaptureBuffer) {
|
|
CsrFreeCaptureBuffer(CaptureBuffer);
|
|
}
|
|
|
|
return a->cchCurDirs;
|
|
}
|
|
|
|
|
|
VOID
|
|
APIENTRY
|
|
CmdBatNotification(
|
|
IN ULONG fBeginEnd
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
This API lets base know about .bat processing from cmd. This is
|
|
required by VDM, so that it can decided correctly when to put
|
|
command.com prompt on TSRs. If the command came from .bat file
|
|
then VDM should'nt put its prompt. This is important for
|
|
ventura publisher and civilization apps.
|
|
|
|
Arguments:
|
|
fBeginEnd - CMD_BAT_OPERATION_STARTING -> .BAT processing is starting
|
|
CMD_BAT_OPERATION_TERMINATING -> .BAT processing is ending
|
|
|
|
Return Value:
|
|
None
|
|
--*/
|
|
|
|
{
|
|
BASE_API_MSG m;
|
|
PBASE_BAT_NOTIFICATION_MSG a = (PBASE_BAT_NOTIFICATION_MSG)&m.u.BatNotification;
|
|
|
|
a->ConsoleHandle = NtCurrentPeb()->ProcessParameters->ConsoleHandle;
|
|
|
|
if (a->ConsoleHandle == (HANDLE) -1)
|
|
return;
|
|
|
|
a->fBeginEnd = fBeginEnd;
|
|
|
|
CsrClientCallServer((PCSR_API_MSG)&m,
|
|
NULL,
|
|
CSR_MAKE_API_NUMBER(BASESRV_SERVERDLL_INDEX,
|
|
BasepBatNotification
|
|
),
|
|
sizeof( *a )
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
APIENTRY
|
|
RegisterWowExec(
|
|
IN HANDLE hwndWowExec
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
This API gives basesrv the window handle for the shared WowExec so
|
|
it can send WM_WOWEXECSTARTAPP messages to WowExec. This
|
|
saves having a thread in WOW dedicated to GetNextVDMCommand.
|
|
|
|
Arguments:
|
|
hwndWowExec - Win32 window handle for WowExec in shared WOW VDM.
|
|
Separate WOW VDMs don't register their WowExec handle
|
|
because they never get commands from base.
|
|
|
|
Return Value:
|
|
None.
|
|
--*/
|
|
|
|
{
|
|
BASE_API_MSG m;
|
|
PBASE_REGISTER_WOWEXEC_MSG a = &m.u.RegisterWowExec;
|
|
|
|
a->hwndWowExec = hwndWowExec;
|
|
|
|
CsrClientCallServer((PCSR_API_MSG)&m,
|
|
NULL,
|
|
CSR_MAKE_API_NUMBER(BASESRV_SERVERDLL_INDEX,
|
|
BasepRegisterWowExec
|
|
),
|
|
sizeof( *a )
|
|
);
|
|
return;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
This routine is used to close standard IO handles before returning to the
|
|
caller
|
|
|
|
|
|
Arguments:
|
|
pVDMInfo - VDM Info record containing stdio handles
|
|
|
|
Return Value:
|
|
None
|
|
|
|
--*/
|
|
VOID
|
|
BaseCloseStandardHandle(
|
|
IN PVDMINFO pVDMInfo
|
|
)
|
|
{
|
|
if (pVDMInfo->StdIn)
|
|
NtClose (pVDMInfo->StdIn);
|
|
|
|
if (pVDMInfo->StdOut)
|
|
NtClose (pVDMInfo->StdOut);
|
|
|
|
if (pVDMInfo->StdErr)
|
|
NtClose (pVDMInfo->StdErr);
|
|
|
|
pVDMInfo->StdIn = 0;
|
|
pVDMInfo->StdOut = 0;
|
|
pVDMInfo->StdErr = 0;
|
|
}
|
|
|
|
#ifdef OLD_CFG_BASED
|
|
|
|
BOOL
|
|
BaseGetVDMKeyword(
|
|
PCHAR KeywordLine,
|
|
PCONFIG_KEYWORD *pKeywordLine,
|
|
PCHAR KeywordSize,
|
|
PULONG VdmSize
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PCONFIG_FILE ConfigFile;
|
|
PCONFIG_SECTION Section;
|
|
STRING SectionName, KeywordName;
|
|
PCONFIG_KEYWORD pKeywordSize;
|
|
|
|
//
|
|
// Retrieve the VDM configuration information from the config file
|
|
//
|
|
|
|
Status = RtlOpenConfigFile( NULL, &ConfigFile );
|
|
if (!NT_SUCCESS( Status )) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Find WOW section of config file
|
|
//
|
|
|
|
RtlInitString( &SectionName, "WOW" );
|
|
Section = RtlLocateSectionConfigFile( ConfigFile, &SectionName );
|
|
if (Section == NULL) {
|
|
RtlCloseConfigFile( ConfigFile );
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Get command line
|
|
//
|
|
|
|
RtlInitString( &KeywordName, KeywordLine );
|
|
*pKeywordLine = RtlLocateKeywordConfigFile( Section, &KeywordName );
|
|
if (*pKeywordLine == NULL) {
|
|
RtlCloseConfigFile( ConfigFile );
|
|
return FALSE;
|
|
}
|
|
//
|
|
// Get Vdm size
|
|
//
|
|
|
|
RtlInitString( &KeywordName, KeywordSize );
|
|
pKeywordSize = RtlLocateKeywordConfigFile( Section, &KeywordName );
|
|
if (pKeywordSize == NULL) {
|
|
*VdmSize = 1024L * 1024L * 16L;
|
|
} else {
|
|
Status = RtlCharToInteger( pKeywordSize->Value.Buffer, 0, VdmSize );
|
|
if (!NT_SUCCESS( Status )) {
|
|
*VdmSize = 1024L * 1024L * 16L;
|
|
} else {
|
|
*VdmSize *= 1024L * 1024L; // convert to MB
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
#endif
|
|
|
|
BOOL
|
|
BaseGetVDMKeyword(
|
|
LPWSTR KeywordLine,
|
|
LPSTR KeywordLineValue,
|
|
LPDWORD KeywordLineSize,
|
|
LPWSTR KeywordSize,
|
|
LPDWORD VdmSize
|
|
)
|
|
{
|
|
NTSTATUS NtStatus;
|
|
UNICODE_STRING UnicodeString,UnicodeTemp;
|
|
UNICODE_STRING KeyName;
|
|
ANSI_STRING AnsiString;
|
|
LPWSTR UnicodeBuffer,Temp;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
HANDLE hKey = NULL;
|
|
PKEY_VALUE_FULL_INFORMATION pKeyValueInformation;
|
|
|
|
//
|
|
// Allocate Work buffer
|
|
//
|
|
|
|
UnicodeBuffer = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG( VDM_TAG ), FULL_INFO_BUFFER_SIZE);
|
|
if (!UnicodeBuffer) {
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return(FALSE);
|
|
}
|
|
|
|
// Open the WOW key
|
|
|
|
RtlInitUnicodeString (&KeyName, WOW_ROOT);
|
|
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&KeyName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
NtStatus = NtOpenKey(&hKey, KEY_READ, &ObjectAttributes);
|
|
|
|
if (NtStatus == STATUS_OBJECT_NAME_NOT_FOUND) {
|
|
BaseSetLastNTError(NtStatus);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!GetVDMConfigValue(hKey,KeywordLine,UnicodeBuffer)) {
|
|
NtClose (hKey);
|
|
RtlFreeHeap(RtlProcessHeap(), 0, UnicodeBuffer);
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// Now convert back to ANSI for the caller after doing all the substitution
|
|
//
|
|
pKeyValueInformation = (PVOID)UnicodeBuffer;
|
|
Temp = (LPWSTR)((PBYTE) pKeyValueInformation + pKeyValueInformation->DataOffset);
|
|
RtlInitUnicodeString( &UnicodeString, Temp );
|
|
UnicodeTemp.Buffer = (LPWSTR)KeywordLineValue;
|
|
UnicodeTemp.Length = 0;
|
|
UnicodeTemp.MaximumLength = MAX_VDM_CFG_LINE;
|
|
NtStatus = RtlExpandEnvironmentStrings_U (NULL,&UnicodeString, &UnicodeTemp, NULL);
|
|
if (!NT_SUCCESS( NtStatus )){
|
|
NtClose (hKey);
|
|
RtlFreeHeap(RtlProcessHeap(), 0, UnicodeBuffer);
|
|
return FALSE;
|
|
}
|
|
wcscpy(UnicodeString.Buffer,UnicodeTemp.Buffer);
|
|
UnicodeString.Length = UnicodeTemp.Length;
|
|
|
|
//
|
|
// Set up an ANSI_STRING that points to the user's buffer
|
|
//
|
|
|
|
AnsiString.MaximumLength = (USHORT) *KeywordLineSize;
|
|
AnsiString.Length = 0;
|
|
AnsiString.Buffer = KeywordLineValue;
|
|
|
|
|
|
RtlUnicodeStringToAnsiString(&AnsiString, &UnicodeString, FALSE);
|
|
|
|
*KeywordLineSize = AnsiString.Length;
|
|
|
|
// Always set the VDMSize to 16Mb. (This is for reservation only)
|
|
// Actual commit is done by SAS_INIT.
|
|
|
|
*VdmSize = 16L; //default value is 16
|
|
*VdmSize *= 1024L * 1024L; // convert From MB
|
|
|
|
NtClose (hKey);
|
|
RtlFreeHeap(RtlProcessHeap(), 0, UnicodeBuffer);
|
|
return(TRUE);
|
|
|
|
}
|
|
|
|
BOOL
|
|
GetVDMConfigValue(
|
|
HANDLE hKey,
|
|
LPWSTR Keyword,
|
|
LPWSTR UnicodeBuffer
|
|
)
|
|
{
|
|
NTSTATUS NtStatus;
|
|
UNICODE_STRING ValueName;
|
|
PKEY_VALUE_FULL_INFORMATION pKeyValueInformation = (PVOID) UnicodeBuffer;
|
|
ULONG ValueLength;
|
|
|
|
RtlInitUnicodeString(&ValueName, Keyword);
|
|
NtStatus = NtQueryValueKey(hKey,
|
|
&ValueName,
|
|
KeyValueFullInformation,
|
|
pKeyValueInformation,
|
|
FULL_INFO_BUFFER_SIZE,
|
|
&ValueLength);
|
|
|
|
if (NT_SUCCESS(NtStatus))
|
|
return TRUE;
|
|
else {
|
|
BaseSetLastNTError (NtStatus);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
BaseCheckVDM(
|
|
IN ULONG BinaryType,
|
|
IN PCWCH lpApplicationName,
|
|
IN PCWCH lpCommandLine,
|
|
IN PCWCH lpCurrentDirectory,
|
|
IN ANSI_STRING *pAnsiStringEnv,
|
|
IN PBASE_API_MSG m,
|
|
IN OUT PULONG iTask,
|
|
IN DWORD dwCreationFlags,
|
|
LPSTARTUPINFOW lpStartupInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine calls the windows server to find out if the VDM for the
|
|
current session is already present. If so, a new process is'nt created
|
|
instead the DOS binary is dispatched to the existing VDM. Otherwise,
|
|
a new VDM process is created. This routine also passes the app name
|
|
and command line to the server in DOS int21/0ah style which is later
|
|
passed by the server to the VDM.
|
|
|
|
Arguments:
|
|
|
|
BinaryType - DOS/WOW binary
|
|
lpApplicationName -- pointer to the full path name of the executable.
|
|
lpCommandLine -- command line
|
|
lpCurrentDirectory - Current directory
|
|
lpEnvironment, - Envirinment strings
|
|
m - pointer to the base api message.
|
|
iTask - taskid for win16 apps, and no-console dos apps
|
|
dwCreationFlags - creation flags as passed to createprocess
|
|
lpStartupInfo =- pointer to startupinfo as passed to createprocess
|
|
|
|
|
|
Return Value:
|
|
|
|
OEM vs. ANSI:
|
|
The command line, Application Name, title are converted to OEM strings,
|
|
suitable for the VDM. All other strings are returned as ANSI.
|
|
|
|
TRUE -- Operation successful, VDM state and other relevant information
|
|
is in base api message.
|
|
FALSE -- Operation failed.
|
|
|
|
--*/
|
|
{
|
|
|
|
NTSTATUS Status;
|
|
PPEB Peb;
|
|
PBASE_CHECKVDM_MSG b= (PBASE_CHECKVDM_MSG)&m->u.CheckVDM;
|
|
PCSR_CAPTURE_HEADER CaptureBuffer;
|
|
ANSI_STRING AnsiStringCurrentDir,AnsiStringDesktop;
|
|
ANSI_STRING AnsiStringReserved, AnsiStringPif;
|
|
OEM_STRING OemStringCmd, OemStringAppName, OemStringTitle;
|
|
UNICODE_STRING UnicodeString;
|
|
PCHAR pch, pSlash, Buffer = NULL;
|
|
ULONG Len;
|
|
ULONG bufPointers;
|
|
LPWSTR wsBuffer;
|
|
LPWSTR wsAppName;
|
|
LPWSTR wsPifName;
|
|
LPWSTR wsCmdLine;
|
|
LPWSTR wsPif=(PWSTR)".\0p\0i\0f\0\0"; // L".pif"
|
|
LPWSTR wsSharedWowPif=L"wowexec.pif";
|
|
PWCHAR pwch;
|
|
BOOLEAN bNewConsole;
|
|
BOOLEAN bReturn = FALSE;
|
|
DWORD dw, dwTotal, Length;
|
|
WCHAR *pSrc, *pDot, *pTmp;
|
|
UNICODE_STRING * pUnicodeStringExtName;
|
|
WCHAR wchBuffer[MAX_PATH + 1];
|
|
ULONG BinarySubType;
|
|
LPWSTR lpAllocatedReserved = NULL;
|
|
DWORD HandleFlags;
|
|
|
|
// does a trivial test of the environment
|
|
if (!ARGUMENT_PRESENT(pAnsiStringEnv) ||
|
|
pAnsiStringEnv->Length > MAXIMUM_VDM_ENVIORNMENT) {
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
wsCmdLine = wsAppName = NULL;
|
|
OemStringCmd.Buffer = NULL;
|
|
OemStringAppName.Buffer = NULL;
|
|
AnsiStringCurrentDir.Buffer = NULL;
|
|
AnsiStringDesktop.Buffer = NULL;
|
|
AnsiStringPif.Buffer = NULL;
|
|
OemStringTitle.Buffer = NULL;
|
|
AnsiStringReserved.Buffer = NULL;
|
|
wsBuffer = NULL;
|
|
wsPifName = NULL;
|
|
|
|
BinarySubType = BinaryType & BINARY_SUBTYPE_MASK;
|
|
BinaryType = BinaryType & ~BINARY_SUBTYPE_MASK;
|
|
bNewConsole = !NtCurrentPeb()->ProcessParameters->ConsoleHandle ||
|
|
(dwCreationFlags & CREATE_NEW_CONSOLE);
|
|
|
|
try {
|
|
|
|
if (BinaryType == BINARY_TYPE_DOS) {
|
|
|
|
//
|
|
// if the command line is a pif file we must have a new
|
|
// console since a pif file defines its own settings. This
|
|
// could be forced into a new console, but forcedos isn't
|
|
// cooperative.
|
|
//
|
|
|
|
if (BinarySubType == BINARY_TYPE_DOS_PIF && !bNewConsole) {
|
|
BaseSetLastNTError(STATUS_INVALID_IMAGE_NOT_MZ);
|
|
goto BCVTryExit;
|
|
}
|
|
|
|
Peb = NtCurrentPeb();
|
|
if (lpStartupInfo && lpStartupInfo->dwFlags & STARTF_USESTDHANDLES) {
|
|
b->StdIn = lpStartupInfo->hStdInput;
|
|
b->StdOut = lpStartupInfo->hStdOutput;
|
|
b->StdErr = lpStartupInfo->hStdError;
|
|
|
|
}
|
|
else {
|
|
b->StdIn = Peb->ProcessParameters->StandardInput;
|
|
b->StdOut = Peb->ProcessParameters->StandardOutput;
|
|
b->StdErr = Peb->ProcessParameters->StandardError;
|
|
|
|
//
|
|
// Verify that the standard handles ntvdm process will inherit
|
|
// from the calling process are real handles. They are not
|
|
// handles if the calling process was created with
|
|
// STARTF_USEHOTKEY | STARTF_HASSHELLDATA.
|
|
// Note that CreateProcess clears STARTF_USESTANDHANDLES
|
|
// if either STARTF_USEHOTKEY or STARTF_HASSHELLDATA is set.
|
|
//
|
|
if (Peb->ProcessParameters->WindowFlags &
|
|
(STARTF_USEHOTKEY | STARTF_HASSHELLDATA)) {
|
|
|
|
if (b->StdIn && !CONSOLE_HANDLE(b->StdIn) &&
|
|
!GetHandleInformation(b->StdIn, &HandleFlags))
|
|
b->StdIn = 0;
|
|
if (b->StdOut && !CONSOLE_HANDLE(b->StdOut) &&
|
|
!GetHandleInformation(b->StdOut, &HandleFlags)) {
|
|
if (b->StdErr == b->StdOut)
|
|
b->StdErr = 0;
|
|
b->StdOut = 0;
|
|
}
|
|
if (b->StdErr && b->StdErr != b->StdOut &&
|
|
!CONSOLE_HANDLE(b->StdErr) &&
|
|
!GetHandleInformation(b->StdErr, &HandleFlags))
|
|
b->StdErr = 0;
|
|
}
|
|
}
|
|
if (CONSOLE_HANDLE((b->StdIn)))
|
|
b->StdIn = 0;
|
|
|
|
if (CONSOLE_HANDLE((b->StdOut)))
|
|
b->StdOut = 0;
|
|
|
|
if (CONSOLE_HANDLE((b->StdErr)))
|
|
b->StdErr = 0;
|
|
}
|
|
|
|
|
|
if (BinaryType == BINARY_TYPE_SEPWOW) {
|
|
bNewConsole = TRUE;
|
|
}
|
|
|
|
//
|
|
// Convert Unicode Application Name to Oem short name
|
|
//
|
|
// skiping leading white space
|
|
while(*lpApplicationName == (WCHAR)' ' || *lpApplicationName == (WCHAR)'\t' ) {
|
|
lpApplicationName++;
|
|
}
|
|
|
|
// space for short AppName
|
|
Len = wcslen(lpApplicationName);
|
|
dwTotal = Len + 1 + MAX_PATH;
|
|
wsAppName = RtlAllocateHeap(RtlProcessHeap(),
|
|
MAKE_TAG(VDM_TAG),
|
|
dwTotal * sizeof(WCHAR)
|
|
);
|
|
if (wsAppName == NULL) {
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
goto BCVTryExit;
|
|
}
|
|
|
|
dw = GetShortPathNameW(lpApplicationName, wsAppName, dwTotal);
|
|
// If getting the short name is impossible, stop right here.
|
|
// We can not execute a 16bits biranry if we can not find
|
|
// its appropriate short name alias. Sorry HPFS, Sorry NFS
|
|
|
|
if (0 == dw || dw > dwTotal) {
|
|
SetLastError(ERROR_BAD_PATHNAME);
|
|
goto BCVTryExit;
|
|
}
|
|
|
|
RtlInitUnicodeString(&UnicodeString, wsAppName);
|
|
Status = RtlUnicodeStringToOemString(&OemStringAppName,
|
|
&UnicodeString,
|
|
TRUE
|
|
);
|
|
if (!NT_SUCCESS(Status) ){
|
|
BaseSetLastNTError(Status);
|
|
goto BCVTryExit;
|
|
}
|
|
|
|
|
|
//
|
|
// Find len of basename excluding extension,
|
|
// for CommandTail max len check.
|
|
//
|
|
dw = OemStringAppName.Length;
|
|
pch = OemStringAppName.Buffer;
|
|
Length = 1; // start at one for space between cmdname & cmdtail
|
|
while (dw-- && *pch != '.') {
|
|
if (*pch == '\\') {
|
|
Length = 1;
|
|
}
|
|
else {
|
|
Length++;
|
|
}
|
|
pch++;
|
|
}
|
|
|
|
|
|
//
|
|
// Find the beg of the command tail to pass as the CmdLine
|
|
//
|
|
|
|
Len = wcslen(lpApplicationName);
|
|
|
|
if (L'"' == lpCommandLine[0]) {
|
|
|
|
//
|
|
// Application name is quoted, skip the quoted text
|
|
// to get command tail.
|
|
//
|
|
|
|
pwch = (LPWSTR)&lpCommandLine[1];
|
|
while (*pwch && L'"' != *pwch++) {
|
|
;
|
|
}
|
|
|
|
} else if (Len < wcslen(lpCommandLine) &&
|
|
0 == _wcsnicmp(lpApplicationName, lpCommandLine, Len)) {
|
|
|
|
//
|
|
// Application path is also on the command line, skip past
|
|
// that to reach the command tail instead of looking for
|
|
// the first white space.
|
|
//
|
|
|
|
pwch = (LPWSTR)lpCommandLine + Len;
|
|
|
|
} else {
|
|
|
|
//
|
|
// We assume first token is exename (argv[0]).
|
|
//
|
|
|
|
pwch = (LPWSTR)lpCommandLine;
|
|
|
|
// skip leading white characters
|
|
while (*pwch != UNICODE_NULL &&
|
|
(*pwch == (WCHAR) ' ' || *pwch == (WCHAR) '\t')) {
|
|
pwch++;
|
|
}
|
|
|
|
// skip first token
|
|
if (*pwch == (WCHAR) '\"') { // quotes as delimiter
|
|
pwch++;
|
|
while (*pwch && *pwch++ != '\"') {
|
|
;
|
|
}
|
|
}
|
|
else { // white space as delimiter
|
|
while (*pwch && *pwch != ' ' && *pwch != '\t') {
|
|
pwch++;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// pwch points past the application name, now skip any trailing
|
|
// whitespace.
|
|
//
|
|
|
|
while (*pwch && (L' ' == *pwch || L'\t' == *pwch)) {
|
|
pwch++;
|
|
}
|
|
|
|
wsCmdLine = pwch;
|
|
dw = wcslen(wsCmdLine);
|
|
|
|
// convert to oem
|
|
UnicodeString.Length = (USHORT)(dw * sizeof(WCHAR));
|
|
UnicodeString.MaximumLength = UnicodeString.Length + sizeof(WCHAR);
|
|
UnicodeString.Buffer = wsCmdLine;
|
|
Status = RtlUnicodeStringToOemString(
|
|
&OemStringCmd,
|
|
&UnicodeString,
|
|
TRUE);
|
|
|
|
if (!NT_SUCCESS(Status) ){
|
|
BaseSetLastNTError(Status);
|
|
goto BCVTryExit;
|
|
}
|
|
|
|
//
|
|
// check len of command line for dos compatibility
|
|
//
|
|
if (OemStringCmd.Length >= MAXIMUM_VDM_COMMAND_LENGTH - Length) {
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
goto BCVTryExit;
|
|
}
|
|
|
|
|
|
//
|
|
// Search for matching pif file. Search order is AppName dir,
|
|
// followed by win32 default search path. For the shared wow, pif
|
|
// is wowexec.pif if it exists.
|
|
//
|
|
wsBuffer = RtlAllocateHeap(RtlProcessHeap(),MAKE_TAG( VDM_TAG ),MAX_PATH*sizeof(WCHAR));
|
|
if (!wsBuffer) {
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
goto BCVTryExit;
|
|
}
|
|
|
|
wsPifName = RtlAllocateHeap(RtlProcessHeap(),MAKE_TAG( VDM_TAG ),MAX_PATH*sizeof(WCHAR));
|
|
if (!wsPifName) {
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
goto BCVTryExit;
|
|
}
|
|
|
|
if (BinaryType == BINARY_TYPE_WIN16) {
|
|
wcscpy(wsBuffer, wsSharedWowPif);
|
|
Len = 0;
|
|
}
|
|
else {
|
|
// start with fully qualified app name
|
|
wcscpy(wsBuffer, lpApplicationName);
|
|
|
|
// strip extension if any
|
|
pwch = wcsrchr(wsBuffer, (WCHAR)'.');
|
|
// dos application must have an extention
|
|
if (pwch == NULL) {
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
goto BCVTryExit;
|
|
}
|
|
wcscpy(pwch, wsPif);
|
|
Len = GetFileAttributesW(wsBuffer);
|
|
if (Len == (DWORD)(-1) || (Len & FILE_ATTRIBUTE_DIRECTORY)) {
|
|
Len = 0;
|
|
}
|
|
else {
|
|
Len = wcslen(wsBuffer) + 1;
|
|
wcsncpy(wsPifName, wsBuffer, Len);
|
|
}
|
|
}
|
|
|
|
if (!Len) { // try basename
|
|
|
|
// find beg of basename
|
|
pwch = wcsrchr(wsBuffer, (WCHAR)'\\');
|
|
if (!pwch ) {
|
|
pwch = wcsrchr(wsBuffer, (WCHAR)':');
|
|
}
|
|
|
|
// move basename to beg of wsBuffer
|
|
if (pwch++) {
|
|
while (*pwch != UNICODE_NULL &&
|
|
*pwch != (WCHAR)' ' && *pwch != (WCHAR)'\t' )
|
|
{
|
|
wsBuffer[Len++] = *pwch++;
|
|
}
|
|
wsBuffer[Len] = UNICODE_NULL;
|
|
}
|
|
|
|
if (Len) {
|
|
Len = SearchPathW(
|
|
NULL,
|
|
wsBuffer,
|
|
wsPif, // L".pif"
|
|
MAX_PATH,
|
|
wsPifName,
|
|
NULL
|
|
);
|
|
if (Len >= MAX_PATH) {
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
goto BCVTryExit;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!Len)
|
|
*wsPifName = UNICODE_NULL;
|
|
|
|
|
|
|
|
if (!ARGUMENT_PRESENT( lpCurrentDirectory )) {
|
|
|
|
dw = RtlGetCurrentDirectory_U(sizeof (wchBuffer), wchBuffer);
|
|
|
|
wchBuffer[dw / sizeof(WCHAR)] = UNICODE_NULL;
|
|
dw = GetShortPathNameW(wchBuffer,
|
|
wchBuffer,
|
|
sizeof(wchBuffer) / sizeof(WCHAR)
|
|
);
|
|
if (dw > sizeof(wchBuffer) / sizeof(WCHAR))
|
|
goto BCVTryExit;
|
|
|
|
else if (dw == 0) {
|
|
RtlInitUnicodeString(&UnicodeString, wchBuffer);
|
|
dw = UnicodeString.Length / sizeof(WCHAR);
|
|
}
|
|
else {
|
|
UnicodeString.Length = (USHORT)(dw * sizeof(WCHAR));
|
|
UnicodeString.Buffer = wchBuffer;
|
|
UnicodeString.MaximumLength = (USHORT)sizeof(wchBuffer);
|
|
}
|
|
// DOS limit of 64 includes the final NULL but not the leading
|
|
// drive and slash. So here we should be checking the ansi length
|
|
// of current directory + 1 (for NULL) - 3 (for c:\).
|
|
if ( dw - 2 <= MAXIMUM_VDM_CURRENT_DIR ) {
|
|
Status = RtlUnicodeStringToAnsiString(
|
|
&AnsiStringCurrentDir,
|
|
&UnicodeString,
|
|
TRUE
|
|
);
|
|
}
|
|
else {
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
goto BCVTryExit;
|
|
}
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
BaseSetLastNTError(Status);
|
|
goto BCVTryExit;
|
|
}
|
|
}
|
|
else {
|
|
|
|
|
|
dw = GetShortPathNameW(lpCurrentDirectory, wchBuffer,
|
|
sizeof(wchBuffer) / sizeof(WCHAR)
|
|
);
|
|
if (dw > sizeof(wchBuffer) / sizeof(WCHAR))
|
|
goto BCVTryExit;
|
|
|
|
if (dw != 0) {
|
|
UnicodeString.Buffer = wchBuffer;
|
|
UnicodeString.Length = (USHORT)(dw * sizeof(WCHAR));
|
|
UnicodeString.MaximumLength = sizeof(wchBuffer);
|
|
}
|
|
else
|
|
RtlInitUnicodeString(&UnicodeString, lpCurrentDirectory);
|
|
|
|
Status = RtlUnicodeStringToAnsiString(
|
|
&AnsiStringCurrentDir,
|
|
&UnicodeString,
|
|
TRUE);
|
|
|
|
if ( !NT_SUCCESS(Status) ){
|
|
BaseSetLastNTError(Status);
|
|
goto BCVTryExit;
|
|
}
|
|
|
|
// DOS limit of 64 includes the final NULL but not the leading
|
|
// drive and slash. So here we should be checking the ansi length
|
|
// of current directory + 1 (for NULL) - 3 (for c:\).
|
|
if((AnsiStringCurrentDir.Length - 2) > MAXIMUM_VDM_CURRENT_DIR) {
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
goto BCVTryExit;
|
|
}
|
|
}
|
|
|
|
// NT allows applications to use UNC name as their current directory.
|
|
// while NTVDM can't do that. We will end up a weird drive number
|
|
// like '\' - 'a') here ????????????????????????????????
|
|
// BUGBUG
|
|
// Place Current Drive
|
|
if(AnsiStringCurrentDir.Buffer[0] <= 'Z')
|
|
b->CurDrive = AnsiStringCurrentDir.Buffer[0] - 'A';
|
|
else
|
|
b->CurDrive = AnsiStringCurrentDir.Buffer[0] - 'a';
|
|
|
|
//
|
|
// Hotkey info in NT traditionally is specified in the
|
|
// startupinfo.lpReserved field, but Win95 added a
|
|
// duplicate mechanism. If the Win95 method was used,
|
|
// map it to the NT method here so the rest of the
|
|
// VDM code only has to deal with one method.
|
|
//
|
|
// If the caller was stupid enough to specify a hotkey
|
|
// in lpReserved as well as using STARTF_USEHOTKEY,
|
|
// the STARTF_USEHOTKEY hotkey will take precedence.
|
|
//
|
|
|
|
if (lpStartupInfo && lpStartupInfo->dwFlags & STARTF_USEHOTKEY) {
|
|
|
|
DWORD cbAlloc = sizeof(WCHAR) *
|
|
(20 + // "hotkey.4294967295 " (MAXULONG)
|
|
(lpStartupInfo->lpReserved // length of prev lpReserved
|
|
? wcslen(lpStartupInfo->lpReserved)
|
|
: 0
|
|
) +
|
|
1 // NULL terminator
|
|
);
|
|
|
|
|
|
lpAllocatedReserved = RtlAllocateHeap(RtlProcessHeap(),
|
|
MAKE_TAG( VDM_TAG ),
|
|
cbAlloc
|
|
);
|
|
if (lpAllocatedReserved) {
|
|
|
|
swprintf(lpAllocatedReserved,
|
|
L"hotkey.%u %s",
|
|
(DWORD) lpStartupInfo->hStdInput,
|
|
lpStartupInfo->lpReserved ? lpStartupInfo->lpReserved : L""
|
|
);
|
|
|
|
lpStartupInfo->dwFlags &= ~STARTF_USEHOTKEY;
|
|
lpStartupInfo->hStdInput = 0;
|
|
lpStartupInfo->lpReserved = lpAllocatedReserved;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Allocate Capture Buffer
|
|
//
|
|
//
|
|
bufPointers = 2; // CmdLine, AppName
|
|
|
|
//
|
|
// CmdLine for capture buffer, 3 for 0xd,0xa and NULL
|
|
//
|
|
Len = ROUND_UP((OemStringCmd.Length + 3),4);
|
|
|
|
// AppName, 1 for NULL
|
|
Len += ROUND_UP((OemStringAppName.Length + 1),4);
|
|
|
|
// Env
|
|
if (pAnsiStringEnv->Length) {
|
|
bufPointers++;
|
|
Len += ROUND_UP(pAnsiStringEnv->Length, 4);
|
|
}
|
|
|
|
// CurrentDir
|
|
if (AnsiStringCurrentDir.Length){
|
|
bufPointers++;
|
|
Len += ROUND_UP((AnsiStringCurrentDir.Length +1),4); // 1 for NULL
|
|
}
|
|
|
|
|
|
// pif file name, 1 for NULL
|
|
if (wsPifName && *wsPifName != UNICODE_NULL) {
|
|
bufPointers++;
|
|
RtlInitUnicodeString(&UnicodeString,wsPifName);
|
|
Status = RtlUnicodeStringToAnsiString(&AnsiStringPif,
|
|
&UnicodeString,
|
|
TRUE
|
|
);
|
|
if ( !NT_SUCCESS(Status) ){
|
|
BaseSetLastNTError(Status);
|
|
goto BCVTryExit;
|
|
}
|
|
|
|
Len += ROUND_UP((AnsiStringPif.Length+1),4);
|
|
}
|
|
|
|
//
|
|
// startupinfo space
|
|
//
|
|
if (lpStartupInfo) {
|
|
Len += ROUND_UP(sizeof(STARTUPINFOA),4);
|
|
bufPointers++;
|
|
if (lpStartupInfo->lpDesktop) {
|
|
bufPointers++;
|
|
RtlInitUnicodeString(&UnicodeString,lpStartupInfo->lpDesktop);
|
|
Status = RtlUnicodeStringToAnsiString(
|
|
&AnsiStringDesktop,
|
|
&UnicodeString,
|
|
TRUE);
|
|
|
|
if ( !NT_SUCCESS(Status) ){
|
|
BaseSetLastNTError(Status);
|
|
goto BCVTryExit;
|
|
}
|
|
Len += ROUND_UP((AnsiStringDesktop.Length+1),4);
|
|
}
|
|
|
|
if (lpStartupInfo->lpTitle) {
|
|
bufPointers++;
|
|
RtlInitUnicodeString(&UnicodeString,lpStartupInfo->lpTitle);
|
|
Status = RtlUnicodeStringToOemString(
|
|
&OemStringTitle,
|
|
&UnicodeString,
|
|
TRUE);
|
|
|
|
if ( !NT_SUCCESS(Status) ){
|
|
BaseSetLastNTError(Status);
|
|
goto BCVTryExit;
|
|
}
|
|
Len += ROUND_UP((OemStringTitle.Length+1),4);
|
|
}
|
|
|
|
if (lpStartupInfo->lpReserved) {
|
|
bufPointers++;
|
|
RtlInitUnicodeString(&UnicodeString,lpStartupInfo->lpReserved);
|
|
Status = RtlUnicodeStringToAnsiString(
|
|
&AnsiStringReserved,
|
|
&UnicodeString,
|
|
TRUE);
|
|
|
|
if ( !NT_SUCCESS(Status) ){
|
|
BaseSetLastNTError(Status);
|
|
goto BCVTryExit;
|
|
}
|
|
Len += ROUND_UP((AnsiStringReserved.Length+1),4);
|
|
}
|
|
}
|
|
|
|
|
|
// capture message buffer
|
|
CaptureBuffer = CsrAllocateCaptureBuffer(bufPointers, 0, Len);
|
|
if (CaptureBuffer == NULL) {
|
|
BaseSetLastNTError( STATUS_NO_MEMORY );
|
|
goto BCVTryExit;
|
|
}
|
|
|
|
// Allocate CmdLine pointer
|
|
CsrAllocateMessagePointer( CaptureBuffer,
|
|
ROUND_UP((OemStringCmd.Length + 3),4),
|
|
(PVOID *)&b->CmdLine
|
|
);
|
|
|
|
// Copy Command Line
|
|
RtlMoveMemory (b->CmdLine, OemStringCmd.Buffer, OemStringCmd.Length);
|
|
b->CmdLine[OemStringCmd.Length] = 0xd;
|
|
b->CmdLine[OemStringCmd.Length+1] = 0xa;
|
|
b->CmdLine[OemStringCmd.Length+2] = 0;
|
|
b->CmdLen = (USHORT)(OemStringCmd.Length + 3);
|
|
|
|
// Allocate AppName pointer
|
|
CsrAllocateMessagePointer( CaptureBuffer,
|
|
ROUND_UP((OemStringAppName.Length + 1),4),
|
|
(PVOID *)&b->AppName
|
|
);
|
|
|
|
// Copy AppName
|
|
RtlMoveMemory (b->AppName,
|
|
OemStringAppName.Buffer,
|
|
OemStringAppName.Length
|
|
);
|
|
b->AppName[OemStringAppName.Length] = 0;
|
|
b->AppLen = OemStringAppName.Length + 1;
|
|
|
|
|
|
|
|
|
|
// Allocate PifFile pointer, Copy PifFile name
|
|
if(AnsiStringPif.Buffer) {
|
|
CsrAllocateMessagePointer( CaptureBuffer,
|
|
ROUND_UP((AnsiStringPif.Length + 1),4),
|
|
(PVOID *)&b->PifFile
|
|
);
|
|
|
|
RtlMoveMemory(b->PifFile,
|
|
AnsiStringPif.Buffer,
|
|
AnsiStringPif.Length);
|
|
|
|
b->PifFile[AnsiStringPif.Length] = 0;
|
|
b->PifLen = AnsiStringPif.Length + 1;
|
|
|
|
}
|
|
else {
|
|
b->PifLen = 0;
|
|
b->PifFile = NULL;
|
|
}
|
|
|
|
|
|
|
|
// Allocate Env pointer, Copy Env strings
|
|
if(pAnsiStringEnv->Length) {
|
|
CsrAllocateMessagePointer( CaptureBuffer,
|
|
ROUND_UP((pAnsiStringEnv->Length),4),
|
|
(PVOID *)&b->Env
|
|
);
|
|
|
|
RtlMoveMemory(b->Env,
|
|
pAnsiStringEnv->Buffer,
|
|
pAnsiStringEnv->Length);
|
|
|
|
b->EnvLen = pAnsiStringEnv->Length;
|
|
|
|
}
|
|
else {
|
|
b->EnvLen = 0;
|
|
b->Env = NULL;
|
|
}
|
|
|
|
|
|
if(AnsiStringCurrentDir.Length) {
|
|
// Allocate Curdir pointer
|
|
CsrAllocateMessagePointer( CaptureBuffer,
|
|
ROUND_UP((AnsiStringCurrentDir.Length + 1),4),
|
|
(PVOID *)&b->CurDirectory
|
|
);
|
|
// copy cur directory
|
|
RtlMoveMemory (b->CurDirectory,
|
|
AnsiStringCurrentDir.Buffer,
|
|
AnsiStringCurrentDir.Length+1);
|
|
|
|
b->CurDirectoryLen = AnsiStringCurrentDir.Length+1;
|
|
}
|
|
else {
|
|
b->CurDirectory = NULL;
|
|
b->CurDirectoryLen = 0;
|
|
}
|
|
|
|
// Allocate startupinfo pointer
|
|
if (lpStartupInfo) {
|
|
CsrAllocateMessagePointer( CaptureBuffer,
|
|
ROUND_UP(sizeof(STARTUPINFOA),4),
|
|
(PVOID *)&b->StartupInfo
|
|
);
|
|
// Copy startupinfo
|
|
b->StartupInfo->dwX = lpStartupInfo->dwX;
|
|
b->StartupInfo->dwY = lpStartupInfo->dwY;
|
|
b->StartupInfo->dwXSize = lpStartupInfo->dwXSize;
|
|
b->StartupInfo->dwYSize = lpStartupInfo->dwYSize;
|
|
b->StartupInfo->dwXCountChars= lpStartupInfo->dwXCountChars;
|
|
b->StartupInfo->dwYCountChars= lpStartupInfo->dwYCountChars;
|
|
b->StartupInfo->dwFillAttribute=lpStartupInfo->dwFillAttribute;
|
|
b->StartupInfo->dwFlags = lpStartupInfo->dwFlags;
|
|
b->StartupInfo->wShowWindow = lpStartupInfo->wShowWindow;
|
|
b->StartupInfo->cb = sizeof(STARTUPINFOA);
|
|
}
|
|
else {
|
|
b->StartupInfo = NULL;
|
|
}
|
|
|
|
// Allocate pointer for Desktop info if needed
|
|
if (AnsiStringDesktop.Buffer) {
|
|
CsrAllocateMessagePointer( CaptureBuffer,
|
|
ROUND_UP((AnsiStringDesktop.Length + 1),4),
|
|
(PVOID *)&b->Desktop
|
|
);
|
|
// Copy desktop string
|
|
RtlMoveMemory (b->Desktop,
|
|
AnsiStringDesktop.Buffer,
|
|
AnsiStringDesktop.Length+1);
|
|
b->DesktopLen =AnsiStringDesktop.Length+1;
|
|
}
|
|
else {
|
|
b->Desktop = NULL;
|
|
b->DesktopLen =0;
|
|
}
|
|
|
|
// Allocate pointer for Title info if needed
|
|
if (OemStringTitle.Buffer) {
|
|
CsrAllocateMessagePointer( CaptureBuffer,
|
|
ROUND_UP((OemStringTitle.Length + 1),4),
|
|
(PVOID *)&b->Title
|
|
);
|
|
// Copy title string
|
|
RtlMoveMemory (b->Title,
|
|
OemStringTitle.Buffer,
|
|
OemStringTitle.Length+1);
|
|
b->TitleLen = OemStringTitle.Length+1;
|
|
}
|
|
else {
|
|
b->Title = NULL;
|
|
b->TitleLen = 0;
|
|
}
|
|
|
|
// Allocate pointer for Reserved field if needed
|
|
if (AnsiStringReserved.Buffer) {
|
|
CsrAllocateMessagePointer( CaptureBuffer,
|
|
ROUND_UP((AnsiStringReserved.Length + 1),4),
|
|
(PVOID *)&b->Reserved
|
|
);
|
|
// Copy reserved string
|
|
RtlMoveMemory (b->Reserved,
|
|
AnsiStringReserved.Buffer,
|
|
AnsiStringReserved.Length+1);
|
|
b->ReservedLen = AnsiStringReserved.Length+1;
|
|
}
|
|
else {
|
|
b->Reserved = NULL;
|
|
b->ReservedLen = 0;
|
|
}
|
|
|
|
|
|
if (BinaryType == BINARY_TYPE_WIN16)
|
|
b->ConsoleHandle = (HANDLE)-1;
|
|
else if (bNewConsole)
|
|
b->ConsoleHandle = 0;
|
|
else
|
|
b->ConsoleHandle = NtCurrentPeb()->ProcessParameters->ConsoleHandle;
|
|
|
|
b->VDMState = FALSE;
|
|
b->BinaryType = BinaryType;
|
|
b->CodePage = (ULONG) GetConsoleCP ();
|
|
b->dwCreationFlags = dwCreationFlags;
|
|
|
|
|
|
Status = CsrClientCallServer(
|
|
(PCSR_API_MSG)m,
|
|
CaptureBuffer,
|
|
CSR_MAKE_API_NUMBER( BASESRV_SERVERDLL_INDEX,
|
|
BasepCheckVDM
|
|
),
|
|
sizeof( *b )
|
|
);
|
|
|
|
CsrFreeCaptureBuffer(CaptureBuffer);
|
|
|
|
if (!NT_SUCCESS(Status) || !NT_SUCCESS((NTSTATUS)m->ReturnValue)) {
|
|
BaseSetLastNTError((NTSTATUS)m->ReturnValue);
|
|
goto BCVTryExit;
|
|
}
|
|
|
|
*iTask = b->iTask;
|
|
bReturn = TRUE;
|
|
BCVTryExit:;
|
|
}
|
|
|
|
finally {
|
|
if(Buffer != NULL)
|
|
RtlFreeHeap(RtlProcessHeap(), 0, (PVOID)Buffer);
|
|
|
|
if(wsBuffer != NULL)
|
|
RtlFreeHeap(RtlProcessHeap(), 0, (PVOID)wsBuffer);
|
|
|
|
if(wsPifName != NULL)
|
|
RtlFreeHeap(RtlProcessHeap(), 0, (PVOID)wsPifName);
|
|
|
|
if(OemStringCmd.Buffer != NULL)
|
|
RtlFreeOemString(&OemStringCmd);
|
|
|
|
if(OemStringAppName.Buffer != NULL)
|
|
RtlFreeOemString(&OemStringAppName);
|
|
|
|
if(AnsiStringPif.Buffer != NULL)
|
|
RtlFreeAnsiString(&AnsiStringPif);
|
|
|
|
if(AnsiStringCurrentDir.Buffer != NULL)
|
|
RtlFreeAnsiString(&AnsiStringCurrentDir);
|
|
|
|
if(AnsiStringDesktop.Buffer != NULL)
|
|
RtlFreeAnsiString(&AnsiStringDesktop);
|
|
|
|
if(OemStringTitle.Buffer != NULL)
|
|
RtlFreeAnsiString(&OemStringTitle);
|
|
|
|
if(AnsiStringReserved.Buffer != NULL)
|
|
RtlFreeAnsiString(&AnsiStringReserved);
|
|
|
|
if (wsAppName != NULL)
|
|
RtlFreeHeap(RtlProcessHeap(), 0, wsAppName);
|
|
|
|
if (lpAllocatedReserved != NULL)
|
|
RtlFreeHeap(RtlProcessHeap(), 0, lpAllocatedReserved);
|
|
|
|
}
|
|
|
|
return bReturn;
|
|
}
|
|
|
|
BOOL
|
|
BaseUpdateVDMEntry(
|
|
IN ULONG UpdateIndex,
|
|
IN OUT HANDLE *WaitHandle,
|
|
IN ULONG IndexInfo,
|
|
IN ULONG BinaryType
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
BASE_API_MSG m;
|
|
PBASE_UPDATE_VDM_ENTRY_MSG c= (PBASE_UPDATE_VDM_ENTRY_MSG)&m.u.UpdateVDMEntry;
|
|
|
|
switch (UpdateIndex) {
|
|
case UPDATE_VDM_UNDO_CREATION:
|
|
c->iTask = (ULONG)*WaitHandle;
|
|
c->VDMCreationState = (USHORT)IndexInfo;
|
|
break;
|
|
case UPDATE_VDM_PROCESS_HANDLE:
|
|
c->VDMProcessHandle = *WaitHandle; // Actually this is VDM handle
|
|
c->iTask = IndexInfo;
|
|
break;
|
|
}
|
|
|
|
if(BinaryType == BINARY_TYPE_WIN16)
|
|
c->ConsoleHandle = (HANDLE)-1;
|
|
else if (c->iTask)
|
|
c->ConsoleHandle = 0;
|
|
else
|
|
c->ConsoleHandle = NtCurrentPeb()->ProcessParameters->ConsoleHandle;
|
|
|
|
c->EntryIndex = (WORD)UpdateIndex;
|
|
c->BinaryType = BinaryType;
|
|
|
|
|
|
Status = CsrClientCallServer(
|
|
(PCSR_API_MSG)&m,
|
|
NULL,
|
|
CSR_MAKE_API_NUMBER( BASESRV_SERVERDLL_INDEX,
|
|
BasepUpdateVDMEntry
|
|
),
|
|
sizeof( *c )
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status) || !NT_SUCCESS((NTSTATUS)m.ReturnValue)) {
|
|
BaseSetLastNTError((NTSTATUS)m.ReturnValue);
|
|
return FALSE;
|
|
}
|
|
|
|
switch (UpdateIndex) {
|
|
case UPDATE_VDM_UNDO_CREATION:
|
|
break;
|
|
case UPDATE_VDM_PROCESS_HANDLE:
|
|
*WaitHandle = c->WaitObjectForParent;
|
|
break;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
ULONG
|
|
BaseIsDosApplication(
|
|
IN PUNICODE_STRING PathName,
|
|
IN NTSTATUS Status
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determines if app is a ".com" or a ".pif" type of app
|
|
by looking at the extension, and the Status from NtCreateSection
|
|
for PAGE_EXECUTE.
|
|
|
|
Arguments:
|
|
|
|
PathName -- Supplies a pointer to the path string
|
|
Status -- Status code from CreateSection call
|
|
bNewConsole -- Pif can exec only from a new console
|
|
|
|
Return Value:
|
|
|
|
file is a com\pif dos application
|
|
SCS_DOS_BINARY - ".com", may also be a .exe extension
|
|
SCS_PIF_BINARY - ".pif"
|
|
|
|
|
|
0 -- file is not a dos application, may be a .bat or .cmd file
|
|
|
|
--*/
|
|
{
|
|
UNICODE_STRING String;
|
|
|
|
// check for .com extension
|
|
String.Length = BaseDotComSuffixName.Length;
|
|
String.Buffer = &(PathName->Buffer[(PathName->Length - String.Length) /
|
|
sizeof(WCHAR)]);
|
|
|
|
if (RtlEqualUnicodeString(&String, &BaseDotComSuffixName, TRUE))
|
|
return BINARY_TYPE_DOS_COM;
|
|
|
|
|
|
// check for .pif extension
|
|
String.Length = BaseDotPifSuffixName.Length;
|
|
String.Buffer = &(PathName->Buffer[(PathName->Length - String.Length) /
|
|
sizeof(WCHAR)]);
|
|
|
|
if (RtlEqualUnicodeString(&String, &BaseDotPifSuffixName, TRUE))
|
|
return BINARY_TYPE_DOS_PIF;
|
|
|
|
|
|
// check for .exe extension
|
|
String.Length = BaseDotExeSuffixName.Length;
|
|
String.Buffer = &(PathName->Buffer[(PathName->Length - String.Length) /
|
|
sizeof(WCHAR)]);
|
|
|
|
if (RtlEqualUnicodeString(&String, &BaseDotExeSuffixName, TRUE))
|
|
return BINARY_TYPE_DOS_EXE;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
BaseGetVdmConfigInfo(
|
|
IN LPCWSTR CommandLine,
|
|
IN ULONG DosSeqId,
|
|
IN ULONG BinaryType,
|
|
IN PUNICODE_STRING CmdLineString,
|
|
OUT PULONG VdmSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine locates the VDM configuration information for Wow vdms in
|
|
the system configuration file. It also reconstructs the commandline so
|
|
that we can start the VDM. The new command line is composed from the
|
|
information in the configuration file + the old command line.
|
|
|
|
Arguments:
|
|
|
|
CommandLine -- pointer to a string pointer that is used to pass the
|
|
command line string
|
|
|
|
DosSeqId - new console session id.
|
|
|
|
VdmSize -- Returns the size in bytes of the VDM to be created
|
|
|
|
BinaryType - dos, sharedwow, sepwow
|
|
|
|
|
|
Return Value:
|
|
|
|
TRUE -- VDM configuration information was available
|
|
FALSE -- VDM configuration information was not available
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
BOOL bRet;
|
|
DWORD dw;
|
|
ANSI_STRING AnsiString;
|
|
LPSTR NewCmdLine=NULL;
|
|
PCH pSrc, pDst, pch;
|
|
ULONG Len;
|
|
char CmdLine[MAX_VDM_CFG_LINE];
|
|
|
|
CmdLineString->Buffer = NULL;
|
|
|
|
Len = MAX_VDM_CFG_LINE;
|
|
|
|
|
|
if (BinaryType == BINARY_TYPE_DOS) {
|
|
bRet = BaseGetVDMKeyword(CMDLINE, CmdLine, &Len, DOSSIZE, VdmSize);
|
|
}
|
|
else {
|
|
bRet = BaseGetVDMKeyword(WOWCMDLINE, CmdLine, &Len, WOWSIZE, VdmSize);
|
|
}
|
|
|
|
if (!bRet) {
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Allocate memory to replace the CommandLine
|
|
// extra space is needed for long->short name conversion,
|
|
// separate wow, and extension.
|
|
//
|
|
|
|
NewCmdLine = RtlAllocateHeap(RtlProcessHeap(),
|
|
MAKE_TAG( VDM_TAG ),
|
|
MAX_PATH + MAX_VDM_CFG_LINE
|
|
);
|
|
if (!NewCmdLine) {
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
//
|
|
// Copy over the cmdline checking for special args
|
|
// and locating the beg of wowkernel
|
|
//
|
|
pSrc = CmdLine;
|
|
pDst = NewCmdLine;
|
|
|
|
|
|
//
|
|
// first token must be "\\%SystemRoot%\\system32\\ntvdm", search
|
|
// for the tail of the pathname to traverse possible long file name
|
|
// safely.
|
|
//
|
|
pch = strstr(pSrc, "\\system32\\ntvdm");
|
|
if (!pch) {
|
|
BaseSetLastNTError(STATUS_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
// mov pch to trailing space in "ntvdm "
|
|
while (*pch && *pch != ' ') {
|
|
pch++;
|
|
}
|
|
|
|
//
|
|
// copy first token (ntvdm path name), surrounded by quotes for
|
|
// possible long file name
|
|
//
|
|
*pDst++ = '\"';
|
|
while (pSrc < pch) {
|
|
*pDst++ = *pSrc++;
|
|
}
|
|
*pDst++ = '\"';
|
|
|
|
|
|
//
|
|
// Add -f arg, so ntvdm knows it wasn't invoked directly
|
|
//
|
|
*pDst++ = ' ';
|
|
*pDst++ = '-';
|
|
*pDst++ = 'f';
|
|
|
|
//
|
|
// Add DosSeqId for new console
|
|
//
|
|
if (DosSeqId) {
|
|
sprintf(pDst, " -i%lx", DosSeqId);
|
|
pDst += strlen(pDst);
|
|
}
|
|
|
|
//
|
|
// Copy over everything up to the " -a " (exclusive)
|
|
// CAVEAT: we assume -a is last
|
|
//
|
|
pch = strstr(pSrc, " -a ");
|
|
if (pch) {
|
|
while (pSrc < pch) {
|
|
*pDst++ = *pSrc++;
|
|
}
|
|
}
|
|
else {
|
|
while (*pSrc) {
|
|
*pDst++ = *pSrc++;
|
|
}
|
|
}
|
|
|
|
*pDst = '\0';
|
|
|
|
|
|
//
|
|
// for wow -a is mandatory to specify win16 krnl, and is expected
|
|
// to be the last cmdline parameter
|
|
//
|
|
if (BinaryType != BINARY_TYPE_DOS) { // shared wow, sep wow
|
|
PCH pWowKernel;
|
|
|
|
if (!*pSrc) {
|
|
BaseSetLastNTError(STATUS_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Add -w to tell ntvdm its wow (mandatory)
|
|
//
|
|
*pDst++ = ' ';
|
|
*pDst++ = '-';
|
|
*pDst++ = 'w';
|
|
|
|
//
|
|
// copy over the " -a WowKernelPathname" argument
|
|
// and locate beg of WowKernelPathname in destination
|
|
//
|
|
pWowKernel = pDst;
|
|
while (*pSrc) {
|
|
*pDst++ = *pSrc++;
|
|
}
|
|
pWowKernel += 4; // find beg of WowKernelPathaname
|
|
while (*pWowKernel == ' ') {
|
|
pWowKernel++;
|
|
}
|
|
|
|
//
|
|
// Append file extension to destination
|
|
//
|
|
strcpy(pDst, ".exe");
|
|
|
|
//
|
|
// convert wowkernel to short name
|
|
//
|
|
Len = MAX_PATH + MAX_VDM_CFG_LINE - ((ULONG)pWowKernel - (ULONG)NewCmdLine) -1;
|
|
dw = GetShortPathNameA(pWowKernel, pWowKernel, Len);
|
|
if (dw > Len) {
|
|
RtlFreeHeap(RtlProcessHeap(), 0, NewCmdLine);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
RtlInitAnsiString(&AnsiString, NewCmdLine);
|
|
Status = RtlAnsiStringToUnicodeString(CmdLineString, &AnsiString, TRUE);
|
|
if (!NT_SUCCESS(Status)) {
|
|
BaseSetLastNTError(Status);
|
|
RtlFreeHeap(RtlProcessHeap(), 0, NewCmdLine);
|
|
CmdLineString->Buffer = NULL;
|
|
return FALSE;
|
|
}
|
|
|
|
RtlFreeHeap(RtlProcessHeap(), 0, NewCmdLine);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
BOOL
|
|
BaseCheckForVDM(
|
|
IN HANDLE hProcess,
|
|
OUT LPDWORD lpExitCode
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
EVENT_BASIC_INFORMATION ebi;
|
|
BASE_API_MSG m;
|
|
PBASE_GET_VDM_EXIT_CODE_MSG a = (PBASE_GET_VDM_EXIT_CODE_MSG)&m.u.GetVDMExitCode;
|
|
|
|
Status = NtQueryEvent (
|
|
hProcess,
|
|
EventBasicInformation,
|
|
&ebi,
|
|
sizeof(ebi),
|
|
NULL);
|
|
|
|
if(!NT_SUCCESS(Status))
|
|
return FALSE;
|
|
|
|
a->ConsoleHandle = NtCurrentPeb()->ProcessParameters->ConsoleHandle;
|
|
a->hParent = hProcess;
|
|
Status = CsrClientCallServer(
|
|
(PCSR_API_MSG)&m,
|
|
NULL,
|
|
CSR_MAKE_API_NUMBER( BASESRV_SERVERDLL_INDEX,
|
|
BasepGetVDMExitCode),
|
|
sizeof( *a )
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
return FALSE;
|
|
}
|
|
|
|
*lpExitCode = (DWORD)a->ExitCode;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
DWORD
|
|
APIENTRY
|
|
GetShortPathNameA(
|
|
IN LPCSTR lpszLongPath,
|
|
IN LPSTR lpShortPath,
|
|
IN DWORD cchBuffer
|
|
)
|
|
{
|
|
UNICODE_STRING UString, UStringRet;
|
|
ANSI_STRING AString;
|
|
NTSTATUS Status;
|
|
LPWSTR lpShortPathW;
|
|
DWORD ReturnValue=0;
|
|
|
|
if (lpszLongPath == NULL) {
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return 0;
|
|
}
|
|
try {
|
|
RtlInitAnsiString(&AString, lpszLongPath);
|
|
Status = Basep8BitStringToUnicodeString(&UString,
|
|
&AString,
|
|
TRUE
|
|
);
|
|
if (!NT_SUCCESS(Status)){
|
|
BaseSetLastNTError(Status);
|
|
goto gspTryExit;
|
|
}
|
|
if (ARGUMENT_PRESENT(lpShortPath) && cchBuffer > 0) {
|
|
lpShortPathW = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG( VDM_TAG ),
|
|
cchBuffer * sizeof(WCHAR)
|
|
);
|
|
if (lpShortPathW == NULL) {
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
goto gspTryExit;
|
|
}
|
|
}
|
|
else {
|
|
lpShortPathW = NULL;
|
|
cchBuffer = 0;
|
|
}
|
|
ReturnValue = GetShortPathNameW(UString.Buffer,
|
|
lpShortPathW,
|
|
cchBuffer
|
|
);
|
|
if (ReturnValue != 0 && ReturnValue <= cchBuffer) {
|
|
if (ARGUMENT_PRESENT(lpShortPath)) {
|
|
AString.Buffer = lpShortPath;
|
|
AString.MaximumLength = (USHORT) cchBuffer;
|
|
UString.MaximumLength = (USHORT)(cchBuffer * sizeof(WCHAR));
|
|
UStringRet.Buffer = lpShortPathW;
|
|
UStringRet.Length = (USHORT)(ReturnValue * sizeof(WCHAR));
|
|
Status = BasepUnicodeStringTo8BitString(&AString,
|
|
&UStringRet,
|
|
FALSE
|
|
);
|
|
if (!NT_SUCCESS(Status)) {
|
|
BaseSetLastNTError(Status);
|
|
ReturnValue=0;
|
|
goto gspTryExit;
|
|
}
|
|
}
|
|
}
|
|
gspTryExit:;
|
|
}
|
|
|
|
finally {
|
|
RtlFreeUnicodeString(&UString);
|
|
RtlFreeHeap(RtlProcessHeap(), 0, lpShortPathW);
|
|
}
|
|
|
|
return ReturnValue;
|
|
}
|
|
/****
|
|
GetShortPathName
|
|
|
|
Description:
|
|
This function converts the given path name to its short form if
|
|
needed. The conversion may not be necessary and in that case,
|
|
this function simply copies down the given name to the return buffer.
|
|
The caller can have the return buffer set equal to the given path name
|
|
address.
|
|
|
|
Parameters:
|
|
lpszLongPath - Points to a NULL terminated string.
|
|
lpszShortPath - Buffer address to return the short name.
|
|
cchBuffer - Buffer size in char of lpszShortPath.
|
|
|
|
Return Value
|
|
If the GetShortPathName function succeeds, the return value is the length,
|
|
in characters, of the string copied to lpszShortPath,
|
|
not including the terminating
|
|
null character.
|
|
|
|
If the lpszShortPath is too small, the return value is
|
|
the size of the buffer, in
|
|
characters, required to hold the path.
|
|
|
|
If the function fails, the return value is zero. To get
|
|
extended error information, use
|
|
the GetLastError function.
|
|
|
|
Remarks:
|
|
The "short name" can be longer than its "long name". lpszLongPath doesn't
|
|
have to be a fully qualified path name or a long path name.
|
|
|
|
****/
|
|
|
|
DWORD
|
|
APIENTRY
|
|
GetShortPathNameW(
|
|
IN LPCWSTR lpszLongPath,
|
|
IN LPWSTR lpszShortPath,
|
|
IN DWORD cchBuffer
|
|
)
|
|
{
|
|
|
|
RTL_PATH_TYPE RtlPathType;
|
|
LPWSTR p, p1, p2, pLast, pDst;
|
|
DWORD wchTotal, Length;
|
|
WCHAR wch, BufferForFileNameInfo[4 + 14];
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
OBJECT_ATTRIBUTES Obja;
|
|
NTSTATUS Status;
|
|
PFILE_NAME_INFORMATION pFileNameInfo;
|
|
HANDLE Handle;
|
|
PWCHAR pLocalBuffer;
|
|
UINT uReturnVal;
|
|
BOOLEAN TranslationStatus;
|
|
UINT Count;
|
|
|
|
|
|
UNICODE_STRING UStringNtName;
|
|
|
|
uReturnVal = 0;
|
|
UStringNtName.Buffer = NULL;
|
|
pLocalBuffer = NULL;
|
|
|
|
if (!ARGUMENT_PRESENT(lpszLongPath)) {
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return 0;
|
|
}
|
|
try {
|
|
// decide the path type, we want to find out the position of
|
|
// the first character of the first name
|
|
RtlPathType = RtlDetermineDosPathNameType_U(lpszLongPath);
|
|
switch (RtlPathType) {
|
|
// form: "\\server_name\share_name\rest_of_the_path"
|
|
case RtlPathTypeUncAbsolute:
|
|
|
|
p = (LPWSTR)lpszLongPath + 2;
|
|
Count = 2;
|
|
// guard for UNICODE_NULL is necessary because
|
|
// RtlDetermineDosPathNameType_U doesn't really
|
|
// verify an UNC name.
|
|
while (Count && *p != UNICODE_NULL) {
|
|
if (*p == L'\\' || *p == L'/')
|
|
Count--;
|
|
p++;
|
|
}
|
|
break;
|
|
|
|
// form: "\\.\rest_of_the_path"
|
|
case RtlPathTypeLocalDevice:
|
|
p = (LPWSTR)lpszLongPath + 4;
|
|
break;
|
|
|
|
// form: "\\."
|
|
case RtlPathTypeRootLocalDevice:
|
|
p = NULL;
|
|
break;
|
|
|
|
// form: "D:\rest_of_the_path"
|
|
case RtlPathTypeDriveAbsolute:
|
|
p = (LPWSTR)lpszLongPath + 3;
|
|
break;
|
|
|
|
// form: "D:rest_of_the_path"
|
|
case RtlPathTypeDriveRelative:
|
|
p = (LPWSTR)lpszLongPath + 2;
|
|
break;
|
|
|
|
// form: "\rest_of_the_path"
|
|
case RtlPathTypeRooted:
|
|
p = (LPWSTR)lpszLongPath + 1;
|
|
break;
|
|
|
|
// form: "rest_of_the_path"
|
|
case RtlPathTypeRelative:
|
|
p = (LPWSTR) lpszLongPath;
|
|
break;
|
|
|
|
default:
|
|
p = NULL;
|
|
break;
|
|
}
|
|
|
|
|
|
if (p == NULL || *p == UNICODE_NULL ||
|
|
!BaseSearchLongName_U(p, &p1)) {
|
|
|
|
// nothing to convert, copy down the source string
|
|
// to the buffer if necessary
|
|
|
|
if (p == NULL)
|
|
Length = wcslen(lpszLongPath) + 1;
|
|
else if (*p == UNICODE_NULL)
|
|
Length = (DWORD)(p - lpszLongPath + 1);
|
|
else
|
|
Length = (DWORD)(p1 - lpszLongPath + 1);
|
|
if (cchBuffer >= Length) {
|
|
if (ARGUMENT_PRESENT(lpszShortPath) && lpszShortPath != lpszLongPath) {
|
|
RtlMoveMemory(lpszShortPath, lpszLongPath, Length * sizeof(WCHAR));
|
|
}
|
|
uReturnVal = Length - 1;
|
|
goto gsnTryExit;
|
|
}
|
|
else {
|
|
uReturnVal = Length;
|
|
goto gsnTryExit;
|
|
}
|
|
}
|
|
|
|
// Make a local buffer so that we won't overlap the
|
|
// source pathname in case the short name is longer than the
|
|
// long name.
|
|
if (cchBuffer > 0 && ARGUMENT_PRESENT(lpszShortPath)) {
|
|
pLocalBuffer = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG( VDM_TAG ),
|
|
cchBuffer * sizeof(WCHAR));
|
|
if (pLocalBuffer == NULL){
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
goto gsnTryExit;
|
|
}
|
|
}
|
|
|
|
pDst = pLocalBuffer;
|
|
pLast = (LPWSTR)lpszLongPath;
|
|
UStringNtName.Buffer = NULL;
|
|
wchTotal = 0;
|
|
|
|
while (TRUE) {
|
|
// p1 points to the first LFN character
|
|
// copy the short name in the source first
|
|
Length = (DWORD)(p1 - pLast);
|
|
if (Length > 0) {
|
|
wchTotal += Length;
|
|
if (cchBuffer > wchTotal && ARGUMENT_PRESENT(lpszShortPath)) {
|
|
RtlMoveMemory(pDst, pLast, Length * sizeof(WCHAR));
|
|
pDst += Length;
|
|
}
|
|
}
|
|
p2 = p1;
|
|
while (*p2 != UNICODE_NULL) {
|
|
if (*p2 == L'\\' || *p2 == L'/')
|
|
break;
|
|
p2++;
|
|
}
|
|
wch = *p2;
|
|
*p2 = UNICODE_NULL;
|
|
|
|
// forgot to free the buffer while going to the loop???
|
|
ASSERT(UStringNtName.Buffer == NULL);
|
|
|
|
// convert to nt path name. This will take care of
|
|
// \. and \..
|
|
TranslationStatus = RtlDosPathNameToNtPathName_U(lpszLongPath,
|
|
&UStringNtName,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
*p2 = wch;
|
|
if (!TranslationStatus) {
|
|
SetLastError(ERROR_PATH_NOT_FOUND);
|
|
goto gsnTryExit;
|
|
}
|
|
|
|
pFileNameInfo = (PFILE_NAME_INFORMATION)BufferForFileNameInfo;
|
|
InitializeObjectAttributes(&Obja,
|
|
&UStringNtName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
// use FILE_READ_ATTRIBUTES so we don't have to worry about
|
|
// sharing violation.
|
|
|
|
Status = NtOpenFile(&Handle,
|
|
FILE_READ_ATTRIBUTES | SYNCHRONIZE,
|
|
&Obja,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
FILE_SYNCHRONOUS_IO_NONALERT
|
|
);
|
|
// free the buffer because we don't need it any more
|
|
RtlFreeUnicodeString(&UStringNtName);
|
|
UStringNtName.Buffer = NULL;
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
BaseSetLastNTError(Status);
|
|
goto gsnTryExit;
|
|
}
|
|
|
|
// get the short name
|
|
Status = NtQueryInformationFile(Handle,
|
|
&IoStatusBlock,
|
|
pFileNameInfo,
|
|
sizeof(BufferForFileNameInfo),
|
|
FileAlternateNameInformation
|
|
);
|
|
NtClose(Handle);
|
|
if (!NT_SUCCESS(Status)) {
|
|
BaseSetLastNTError(Status);
|
|
goto gsnTryExit;
|
|
}
|
|
// the returned length is in bytes!!
|
|
wchTotal += pFileNameInfo->FileNameLength / sizeof(WCHAR);
|
|
if (cchBuffer > wchTotal && ARGUMENT_PRESENT(lpszShortPath)) {
|
|
RtlMoveMemory(pDst,
|
|
pFileNameInfo->FileName,
|
|
pFileNameInfo->FileNameLength
|
|
);
|
|
pDst += pFileNameInfo->FileNameLength / sizeof(WCHAR);
|
|
}
|
|
// if nothing left, we are done
|
|
if (*p2 == UNICODE_NULL)
|
|
break;
|
|
else {
|
|
pLast = p2;
|
|
p1 = p2 + 1;
|
|
}
|
|
// get next name
|
|
p2 = p1;
|
|
if (!BaseSearchLongName_U(p2, &p1) ) {
|
|
Length = (DWORD)(p1 - pLast);
|
|
wchTotal += Length;
|
|
if (cchBuffer > wchTotal && ARGUMENT_PRESENT(lpszShortPath)) {
|
|
RtlMoveMemory(pDst, pLast, Length * sizeof(WCHAR));
|
|
pDst += Length;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (cchBuffer > wchTotal && ARGUMENT_PRESENT(lpszShortPath)) {
|
|
RtlMoveMemory(lpszShortPath, pLocalBuffer, wchTotal * sizeof(WCHAR));
|
|
lpszShortPath[wchTotal] = UNICODE_NULL;
|
|
uReturnVal = wchTotal;
|
|
}
|
|
else
|
|
uReturnVal = wchTotal + 1;
|
|
gsnTryExit:;
|
|
}
|
|
finally {
|
|
if (UStringNtName.Buffer != NULL)
|
|
RtlFreeUnicodeString(&UStringNtName);
|
|
if (pLocalBuffer != NULL)
|
|
RtlFreeHeap(RtlProcessHeap(), 0, pLocalBuffer);
|
|
}
|
|
|
|
return uReturnVal;
|
|
}
|
|
|
|
/**
|
|
This function search a long name(invalid dos name) in the given string.
|
|
The following characters are not valid in file name domain:
|
|
* + , : ; < = > ? [ ] |
|
|
Input: lpPathName
|
|
ppLongName
|
|
Output: FALSE if no LFN was found in the given pathname.
|
|
*ppLongName will point to the last character(should be UNICODE_NULL)
|
|
in the given pathname.
|
|
TRUE if a LFN was found. *ppLongName points to the first
|
|
character in the LFN
|
|
NOTE: space characters are valid for FAT file names. This function
|
|
will return TRUE(long name found) if space characters are found
|
|
in the given pathname.
|
|
|
|
**/
|
|
|
|
// (1). base name longer than 8 chars
|
|
// (2). extention longer than 3 chars
|
|
// (3). more than 2 dots.
|
|
// (4). two dots but not ".."
|
|
// (5). "basename."
|
|
// (6). ".ext"
|
|
// note that "basename." would never happen on win32 because
|
|
// win32 api strips the trailing '.'
|
|
#define IsInvalidDosFileName(BaseNameLen, ExtentionLen, Dots) \
|
|
(BaseNameLen > 8 || ExtentionLen > 3 || Dots > 2 || \
|
|
(Dots == 2 && (ExtentionLen + BaseNameLen)) || \
|
|
(Dots == 1 && (!BaseNameLen && ExtentionLen)) || \
|
|
(Dots == 1 && (BaseNameLen && !ExtentionLen)))
|
|
|
|
BOOL BaseSearchLongName_U(
|
|
LPCWSTR lpPathName,
|
|
LPWSTR *ppLongName
|
|
)
|
|
|
|
{
|
|
BOOL fLongNameFound, Done;
|
|
UINT BaseNameLen, ExtentionLen, Dots;
|
|
LPWSTR p;
|
|
|
|
ASSERT(lpPathName && ppLongName);
|
|
|
|
p = (LPWSTR)lpPathName;
|
|
fLongNameFound = FALSE;
|
|
*ppLongName = p;
|
|
Dots = 0;
|
|
BaseNameLen = 0;
|
|
ExtentionLen = 0;
|
|
Done = FALSE;
|
|
|
|
while (!Done && !fLongNameFound) {
|
|
switch (*p) {
|
|
case L'.':
|
|
Dots++;
|
|
p++;
|
|
break;
|
|
case L'\\':
|
|
case L'/':
|
|
|
|
if (IsInvalidDosFileName(BaseNameLen, ExtentionLen, Dots)) {
|
|
fLongNameFound = TRUE;
|
|
}
|
|
else {
|
|
*ppLongName = ++p;
|
|
// reset all counters for the next file/dir name
|
|
Dots = 0;
|
|
BaseNameLen = 0;
|
|
ExtentionLen = 0;
|
|
}
|
|
break;
|
|
case UNICODE_NULL:
|
|
if (IsInvalidDosFileName(BaseNameLen, ExtentionLen, Dots)) {
|
|
fLongNameFound = TRUE;
|
|
}
|
|
else
|
|
*ppLongName = p;
|
|
Done = TRUE;
|
|
break;
|
|
|
|
// space is a valid char on FAT volume but not on NTFS.
|
|
// we treated it like invalid one so the caller will be
|
|
// aware of it.
|
|
case L' ':
|
|
case L'*':
|
|
case L'+':
|
|
case L',':
|
|
case L':':
|
|
case L';':
|
|
case L'<':
|
|
case L'=':
|
|
case L'>':
|
|
case L'?':
|
|
case L'[':
|
|
case L']':
|
|
case L'|':
|
|
fLongNameFound = TRUE;
|
|
break;
|
|
default:
|
|
if (!Dots)
|
|
BaseNameLen++;
|
|
else
|
|
ExtentionLen++;
|
|
p++;
|
|
break;
|
|
}
|
|
}
|
|
return (fLongNameFound);
|
|
}
|
|
|
|
/**
|
|
function to create VDM environment for the new executable.
|
|
Input: lpEnvironmen = optinal environment strings prototype in UNICODE.
|
|
If it is NULL, this function use the environment
|
|
block attached to the process
|
|
pAStringEnv = pointer to a ANSI_STRING to receive the
|
|
new environment strings.
|
|
pUStringEnv = pointer to a UNICODE_STRING to receive the
|
|
new environment strings.
|
|
Output: FALSE if the creattion failed.
|
|
TRUE creation successful, pAStringEnv has been setup.
|
|
|
|
This function was provided so that BaseCheckVdm can have correct
|
|
environment(includes the newly create NTVDM process). This was done
|
|
because before command.com gets the next command, users can have
|
|
tons of things specified in config.sys and autoexec.bat which
|
|
may rely on current directory of each drive.
|
|
**/
|
|
BOOL BaseCreateVDMEnvironment(
|
|
PWCHAR lpEnvironment,
|
|
ANSI_STRING * pAStringEnv,
|
|
UNICODE_STRING *pUStringEnv
|
|
)
|
|
{
|
|
WCHAR *pEnv, *pDst, *EnvStrings,* pTmp, *pNewEnv;
|
|
DWORD cchEnv, dw, Length, dwRemain;
|
|
NTSTATUS Status;
|
|
UINT NameType;
|
|
BOOL bRet = FALSE;
|
|
|
|
if (!ARGUMENT_PRESENT(pAStringEnv) || !ARGUMENT_PRESENT(pUStringEnv)){
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
try {
|
|
// the environment strings are shared by every thread of the same
|
|
// process. Since we have no idea of what the caller process
|
|
// is, we have to grab the entire environment to our local buffer in one
|
|
// shot then we can walk through the strings.
|
|
// Note that if another thread makes call to RtlSetEnvironmentVariable
|
|
// then we are out of sync. It is a problem of process structure and
|
|
// I don't want to think about it now.
|
|
// The funny thing is that we have to assume the environment
|
|
// is a block of strings(otherwise, how can we do it?)t, nothing more and
|
|
// nothing less. If someday and somebody dares to change it, he will be
|
|
// the one to blame. If the caller(CreateProcess)
|
|
// provides the environment, we assume it is safe to walk through it.
|
|
//
|
|
|
|
if (lpEnvironment == NULL) {
|
|
// create a new environment and inherit the current process env
|
|
Status = RtlCreateEnvironment(TRUE, (PVOID *)&EnvStrings);
|
|
if (!NT_SUCCESS(Status))
|
|
goto bveTryExit;
|
|
}
|
|
else
|
|
EnvStrings = lpEnvironment;
|
|
|
|
if (EnvStrings == NULL) {
|
|
SetLastError(ERROR_BAD_ENVIRONMENT);
|
|
goto bveTryExit;
|
|
}
|
|
// figure out how long the environment is
|
|
// why can Rtl just provides such a function for us?
|
|
//
|
|
cchEnv = 0;
|
|
pEnv = EnvStrings;
|
|
// environment is double-null terminated
|
|
while (!(*pEnv++ == UNICODE_NULL && *pEnv == UNICODE_NULL))
|
|
cchEnv++;
|
|
// count the last two NULLs
|
|
cchEnv += 2;
|
|
// we don't want to change the original environment, so
|
|
// make a local buffer for it.
|
|
pNewEnv = (LPWSTR)RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG( VDM_TAG ),
|
|
(cchEnv + MAX_PATH) * sizeof(WCHAR));
|
|
if (pNewEnv == NULL) {
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
goto bveTryExit;
|
|
}
|
|
// give the last two for null
|
|
dwRemain = MAX_PATH - 2;
|
|
// now walk through the environment string
|
|
pEnv = EnvStrings;
|
|
// the new environmet will be
|
|
pDst = pNewEnv;
|
|
while (*pEnv != UNICODE_NULL) {
|
|
pTmp = pEnv;
|
|
// current directory environment has the form as:
|
|
// "=d:=d:\pathname" where d: is the drive designator.
|
|
if (pEnv[0] == (WCHAR) '=' ) {
|
|
if (((pEnv[1] >= (WCHAR) 'A' && pEnv[1] <= (WCHAR) 'Z') ||
|
|
(pEnv[1] >= (WCHAR) 'a' && pEnv[1] <= (WCHAR) 'z')) &&
|
|
pEnv[2] == (WCHAR) ':' &&
|
|
pEnv[3] == (WCHAR) '=') {
|
|
// copy the name and the '='
|
|
*pDst++ = *pEnv++;*pDst++ = *pEnv++;
|
|
*pDst++ = *pEnv++;*pDst++ = *pEnv++;
|
|
// current dir is single path
|
|
NameType = ENV_NAME_TYPE_SINGLE_PATH;
|
|
}
|
|
else {
|
|
// a weird environment was detected.
|
|
// treat it as no path
|
|
NameType = ENV_NAME_TYPE_NO_PATH;
|
|
}
|
|
}
|
|
else {
|
|
// copy down the name and the '='
|
|
while (*pEnv && (*pDst++ = *pEnv++) != (WCHAR) '=')
|
|
;
|
|
|
|
// and check the type
|
|
NameType = BaseGetEnvNameType_U(pTmp, (DWORD)(pEnv - pTmp) - 1);
|
|
}
|
|
if (NameType == ENV_NAME_TYPE_NO_PATH) {
|
|
while ((*pDst++ = *pEnv++) != UNICODE_NULL)
|
|
;
|
|
}
|
|
else if (NameType == ENV_NAME_TYPE_SINGLE_PATH) {
|
|
Length = wcslen(pEnv) + 1;
|
|
dw = GetShortPathNameW(pEnv, pDst, Length + dwRemain);
|
|
// if the conversion failed, we simply pass down the original
|
|
// one no matter what the reason is. This is done because we
|
|
// are doing the environment strings.
|
|
if (dw == 0 || dw >= Length + dwRemain){
|
|
RtlMoveMemory(pDst, pEnv, Length * sizeof(WCHAR));
|
|
dw = Length - 1;
|
|
}
|
|
pDst += dw + 1;
|
|
pEnv += Length;
|
|
if (dw > Length)
|
|
dwRemain -= dw - Length;
|
|
}
|
|
else {
|
|
// multiple path name found.
|
|
// the character ';' is used for seperator
|
|
pTmp = pEnv;
|
|
while(*pEnv != UNICODE_NULL) {
|
|
if (*pEnv == (WCHAR) ';') {
|
|
// length not include the ';'
|
|
Length = (DWORD)(pEnv - pTmp);
|
|
if (Length > 0) {
|
|
*pEnv = UNICODE_NULL;
|
|
dw = GetShortPathNameW(pTmp, pDst, Length + 1 + dwRemain);
|
|
// again, if the conversion failed, use the original one
|
|
if (dw == 0 || dw > Length + dwRemain) {
|
|
RtlMoveMemory(pDst, pTmp, Length * sizeof(WCHAR));
|
|
dw = Length;
|
|
}
|
|
pDst += dw;
|
|
*pDst++ = *pEnv++ = (WCHAR)';';
|
|
if (dw > Length)
|
|
dwRemain -= dw - Length;
|
|
}
|
|
// skip all consecutive ';'
|
|
while (*pEnv == (WCHAR) ';')
|
|
*pDst++ = *pEnv++;
|
|
pTmp = pEnv;
|
|
}
|
|
else
|
|
pEnv++;
|
|
}
|
|
// convert the last one
|
|
if ((Length = (DWORD)(pEnv - pTmp)) != 0) {
|
|
dw = GetShortPathNameW(pTmp, pDst, Length+1 + dwRemain);
|
|
if (dw == 0 || dw > Length) {
|
|
RtlMoveMemory(pDst, pTmp, Length * sizeof(WCHAR));
|
|
dw = Length;
|
|
}
|
|
pDst += dw;
|
|
if (dw > Length)
|
|
dwRemain -= dw - Length;
|
|
}
|
|
*pDst++ = *pEnv++;
|
|
}
|
|
}
|
|
*pDst++ = UNICODE_NULL;
|
|
cchEnv = (DWORD) pDst - (DWORD)pNewEnv;
|
|
pUStringEnv->MaximumLength = pUStringEnv->Length = (USHORT)cchEnv;
|
|
pUStringEnv->Buffer = pNewEnv;
|
|
Status = RtlUnicodeStringToAnsiString(pAStringEnv,
|
|
pUStringEnv,
|
|
TRUE
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
BaseSetLastNTError(Status);
|
|
}
|
|
else
|
|
bRet = TRUE;
|
|
bveTryExit:;
|
|
}
|
|
finally {
|
|
if (lpEnvironment == NULL && EnvStrings != NULL)
|
|
RtlDestroyEnvironment(EnvStrings);
|
|
}
|
|
return bRet;
|
|
}
|
|
/**
|
|
Destroy the environment block created by BaseCreateVDMEnvironment
|
|
Input: ANSI_STRING * pAnsiStringVDMEnv
|
|
Environment block in ANSI, should be freed via
|
|
RtlFreeAnsiString
|
|
UNICODE_STRING * pUnicodeStringEnv
|
|
Environment block in UNICODE. The Buffer should
|
|
be freed with RtlFreeHeap.
|
|
Output: should always be TRUE.
|
|
|
|
**/
|
|
|
|
BOOL
|
|
BaseDestroyVDMEnvironment(
|
|
ANSI_STRING *pAStringEnv,
|
|
UNICODE_STRING *pUStringEnv
|
|
)
|
|
{
|
|
if (pAStringEnv->Buffer)
|
|
RtlFreeAnsiString(pAStringEnv);
|
|
if (pUStringEnv->Buffer)
|
|
RtlFreeHeap(RtlProcessHeap(), 0, pUStringEnv->Buffer);
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
/**
|
|
This function returns the name type of the given environment variable name
|
|
The name type has three possibilities. Each one represents if the
|
|
given name can have pathnames as its value.
|
|
ENV_NAME_TYPE_NO_PATH: no pathname can be its value
|
|
ENV_NAME_TYPE_SINGLE_PATH: single pathname
|
|
ENV_NAME_MULTIPLE_PATH: multiple path
|
|
|
|
|
|
SIDE NOTE:
|
|
Currently, nt can not installed on a long path and it seems
|
|
that systemroot and windir are never be in long path.
|
|
|
|
**/
|
|
UINT
|
|
BaseGetEnvNameType_U(WCHAR * Name, DWORD NameLength)
|
|
{
|
|
|
|
|
|
// so far we only take care of three predefined names:
|
|
// PATH
|
|
// WINDIR and
|
|
// SYSTEMROOT.
|
|
//
|
|
static ENV_INFO EnvInfoTable[STD_ENV_NAME_COUNT] = {
|
|
{ENV_NAME_TYPE_MULTIPLE_PATH, 4, ENV_NAME_PATH},
|
|
{ENV_NAME_TYPE_SINGLE_PATH, 6, ENV_NAME_WINDIR},
|
|
{ENV_NAME_TYPE_SINGLE_PATH, 10, ENV_NAME_SYSTEMROOT}
|
|
};
|
|
|
|
|
|
|
|
UINT NameType;
|
|
int i;
|
|
|
|
|
|
NameType = ENV_NAME_TYPE_NO_PATH;
|
|
|
|
for (i = 0; i < STD_ENV_NAME_COUNT; i++) {
|
|
if (EnvInfoTable[i].NameLength == NameLength &&
|
|
!_wcsnicmp(EnvInfoTable[i].Name, Name, NameLength)) {
|
|
NameType = EnvInfoTable[i].NameType;
|
|
break;
|
|
}
|
|
}
|
|
return NameType;
|
|
}
|