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.
3837 lines
125 KiB
3837 lines
125 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"
|
|
#include "apcompat.h"
|
|
#define STRSAFE_NO_DEPRECATE
|
|
#include <strsafe.h>
|
|
#pragma hdrstop
|
|
|
|
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_64BIT_BINARY - Win64 Binary
|
|
SCS_32BIT_BINARY - Win32 Binary
|
|
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 = (BOOLEAN)GetBinaryTypeW(
|
|
DynamicCommandLine.Buffer ? DynamicCommandLine.Buffer : CommandLine->Buffer,
|
|
lpBinaryType);
|
|
|
|
RtlFreeUnicodeString(&DynamicCommandLine);
|
|
|
|
return((BOOL)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_64BIT_BINARY - Win64 Binary
|
|
SCS_32BIT_BINARY - Win32 Binary
|
|
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_U RelativeName;
|
|
BOOLEAN TranslationStatus;
|
|
OBJECT_ATTRIBUTES Obja;
|
|
PVOID FreeBuffer = NULL;
|
|
HANDLE FileHandle, SectionHandle=NULL;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
LONG fBinaryType = SCS_THIS_PLATFORM_BINARY;
|
|
BOOLEAN bReturn = FALSE;
|
|
SECTION_IMAGE_INFORMATION ImageInformation;
|
|
|
|
|
|
try {
|
|
//
|
|
// Translate to an NT name.
|
|
//
|
|
|
|
TranslationStatus = RtlDosPathNameToRelativeNtPathName_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 = 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
|
|
);
|
|
|
|
RtlReleaseRelativeName(&RelativeName);
|
|
|
|
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:
|
|
#if defined(i386) && defined(OS2_SUPPORT_ENABLED)
|
|
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;
|
|
|
|
case STATUS_INVALID_IMAGE_WIN_32:
|
|
fBinaryType = SCS_32BIT_BINARY;
|
|
break;
|
|
|
|
case STATUS_INVALID_IMAGE_WIN_64:
|
|
fBinaryType = SCS_64BIT_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
|
|
switch ( ImageInformation.Machine ) {
|
|
case IMAGE_FILE_MACHINE_I386:
|
|
fBinaryType = SCS_32BIT_BINARY;
|
|
break;
|
|
|
|
#if defined(BUILD_WOW6432)
|
|
//
|
|
// GetBinaryType (64-bit image) from an application running on win64
|
|
// will fall to here since the 64-bit kernel allows creation of 32-bit/64-bit
|
|
// image sections.
|
|
//
|
|
case IMAGE_FILE_MACHINE_IA64:
|
|
case IMAGE_FILE_MACHINE_AMD64:
|
|
fBinaryType = SCS_64BIT_BINARY;
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
SetLastError(ERROR_BAD_EXE_FORMAT);
|
|
goto GBTtryexit;
|
|
}
|
|
#endif // MIPS
|
|
}
|
|
else 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, Status2;
|
|
BASE_API_MSG m;
|
|
PBASE_GET_NEXT_VDM_COMMAND_MSG a = &m.u.GetNextVDMCommand;
|
|
PBASE_EXIT_VDM_MSG c = &m.u.ExitVDM;
|
|
PBASE_IS_FIRST_VDM_MSG d = &m.u.IsFirstVDM;
|
|
PBASE_SET_REENTER_COUNT_MSG e = &m.u.SetReenterCount;
|
|
PCSR_CAPTURE_HEADER CaptureBuffer;
|
|
ULONG Len,nPointers;
|
|
USHORT VDMStateSave;
|
|
|
|
if (lpVDMInfo->VDMState &
|
|
(ASKING_FOR_WOWPROCLIST | ASKING_FOR_WOWTASKLIST | ASKING_TO_ADD_WOWTASK))
|
|
{
|
|
RtlZeroMemory(a,sizeof(BASE_GET_NEXT_VDM_COMMAND_MSG));
|
|
a->iTask = lpVDMInfo->iTask;
|
|
a->EnvLen = lpVDMInfo->EnviornmentSize;
|
|
a->Reserved = lpVDMInfo->Reserved;
|
|
a->VDMState = lpVDMInfo->VDMState;
|
|
CaptureBuffer = NULL;
|
|
|
|
if (lpVDMInfo->Enviornment) {
|
|
Len= (lpVDMInfo->EnviornmentSize) ?
|
|
ROUND_UP(lpVDMInfo->EnviornmentSize, 4) : 4;
|
|
CaptureBuffer = CsrAllocateCaptureBuffer(1, Len);
|
|
if (CaptureBuffer == NULL) {
|
|
BaseSetLastNTError( STATUS_NO_MEMORY );
|
|
return FALSE;
|
|
}
|
|
CsrAllocateMessagePointer( CaptureBuffer,
|
|
lpVDMInfo->EnviornmentSize,
|
|
(PVOID *)&a->Env
|
|
);
|
|
RtlMoveMemory(a->Env,
|
|
lpVDMInfo->Enviornment,
|
|
lpVDMInfo->EnviornmentSize);
|
|
|
|
}
|
|
Status = CsrClientCallServer(
|
|
(PCSR_API_MSG)&m,
|
|
CaptureBuffer,
|
|
CSR_MAKE_API_NUMBER( BASESRV_SERVERDLL_INDEX,
|
|
BasepGetNextVDMCommand
|
|
),
|
|
sizeof( *a )
|
|
);
|
|
if (NT_SUCCESS(Status)) {
|
|
Status = (NTSTATUS)m.ReturnValue;
|
|
}
|
|
if(!NT_SUCCESS(Status)) {
|
|
if(CaptureBuffer) {
|
|
CsrFreeCaptureBuffer( CaptureBuffer );
|
|
}
|
|
BaseSetLastNTError(Status);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Copy back info from the server side
|
|
//
|
|
try{
|
|
if (lpVDMInfo->Enviornment){
|
|
RtlMoveMemory(lpVDMInfo->Enviornment,
|
|
a->Env,
|
|
lpVDMInfo->EnviornmentSize);
|
|
}
|
|
}
|
|
except ( EXCEPTION_EXECUTE_HANDLER ) {
|
|
BaseSetLastNTError(GetExceptionCode());
|
|
CsrFreeCaptureBuffer( CaptureBuffer );
|
|
return FALSE;
|
|
}
|
|
lpVDMInfo->iTask = a->iTask;
|
|
lpVDMInfo->EnviornmentSize = a->EnvLen;
|
|
|
|
|
|
if(CaptureBuffer) {
|
|
CsrFreeCaptureBuffer( CaptureBuffer );
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
// 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;
|
|
|
|
// console handle is always passed on in this case
|
|
// wow is differentiated by a parameter a->VDMState
|
|
// a->VDMState & ASKING_FOR_WOW_BINARY indicates wow
|
|
|
|
a->ConsoleHandle = NtCurrentPeb()->ProcessParameters->ConsoleHandle;
|
|
|
|
if (lpVDMInfo->VDMState & ASKING_FOR_PIF) {
|
|
a->iTask = lpVDMInfo->iTask;
|
|
}
|
|
else {
|
|
a->iTask = 0;
|
|
}
|
|
|
|
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, 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) {
|
|
Status2 = NtWaitForSingleObject(a->WaitObjectForVDM,FALSE,NULL);
|
|
if (Status2 != STATUS_SUCCESS){
|
|
CsrFreeCaptureBuffer(CaptureBuffer);
|
|
BaseSetLastNTError(Status2);
|
|
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
|
|
This parameter is obsolete as basesrv knows about the kind
|
|
of vdm that is calling us
|
|
|
|
|
|
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 = &m.u.ExitVDM;
|
|
|
|
c->ConsoleHandle = NtCurrentPeb()->ProcessParameters->ConsoleHandle;
|
|
|
|
if (IsWowCaller) {
|
|
c->iWowTask = iWowTask;
|
|
}
|
|
else {
|
|
c->iWowTask = 0;
|
|
}
|
|
|
|
// this parameter means
|
|
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 = &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, 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 = &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, 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
|
|
--*/
|
|
|
|
{
|
|
#if defined(BUILD_WOW6432)
|
|
|
|
// 32-bit cmd.exe calls this in WOW64, but there is no VDM support, so
|
|
// no need for a WOW64 thunk for it.
|
|
UNREFERENCED_PARAMETER(fBeginEnd);
|
|
|
|
#else
|
|
BASE_API_MSG m;
|
|
PBASE_BAT_NOTIFICATION_MSG a = &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 )
|
|
);
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
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.
|
|
NULL is passed to de-register any given wowexec
|
|
|
|
Return Value:
|
|
If hwndWowExec != NULL then returns success if wow had been registered successfully
|
|
if hwndWowExec == NULL then returns success if no tasks are pending to be executed
|
|
--*/
|
|
|
|
{
|
|
BASE_API_MSG m;
|
|
PBASE_REGISTER_WOWEXEC_MSG a = &m.u.RegisterWowExec;
|
|
NTSTATUS Status;
|
|
|
|
a->hwndWowExec = hwndWowExec;
|
|
a->ConsoleHandle = NtCurrentPeb()->ProcessParameters->ConsoleHandle;
|
|
|
|
Status = CsrClientCallServer((PCSR_API_MSG)&m,
|
|
NULL,
|
|
CSR_MAKE_API_NUMBER(BASESRV_SERVERDLL_INDEX,
|
|
BasepRegisterWowExec
|
|
),
|
|
sizeof( *a )
|
|
);
|
|
return Status;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
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;
|
|
}
|
|
|
|
NTSTATUS BaseGetVdmLuid(
|
|
HANDLE Token,
|
|
PLUID pluidCaller
|
|
)
|
|
{
|
|
PTOKEN_STATISTICS pStats;
|
|
ULONG BytesRequired;
|
|
NTSTATUS Status;
|
|
|
|
/*
|
|
* Get the session id of the caller.
|
|
*/
|
|
Status = NtQueryInformationToken(
|
|
Token, // Handle
|
|
TokenStatistics, // TokenInformationClass
|
|
NULL, // TokenInformation
|
|
0, // TokenInformationLength
|
|
&BytesRequired // ReturnLength
|
|
);
|
|
|
|
if (Status != STATUS_BUFFER_TOO_SMALL) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Allocate space for the user info
|
|
//
|
|
|
|
pStats = (PTOKEN_STATISTICS)RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG( VDM_TAG ), BytesRequired);
|
|
if (pStats == NULL) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Read in the user info
|
|
//
|
|
|
|
Status = NtQueryInformationToken(
|
|
Token, // Handle
|
|
TokenStatistics, // TokenInformationClass
|
|
pStats, // TokenInformation
|
|
BytesRequired, // TokenInformationLength
|
|
&BytesRequired // ReturnLength
|
|
);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
if (pluidCaller != NULL)
|
|
*pluidCaller = pStats->AuthenticationId;
|
|
}
|
|
|
|
RtlFreeHeap(RtlProcessHeap(), 0, pStats);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
BaseCheckVDMp(
|
|
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,
|
|
IN HANDLE hUserToken
|
|
)
|
|
/*++
|
|
|
|
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.
|
|
|
|
returns nt status code of the last operation
|
|
STATUS_ACCESS_DENIED -- Operation failed (desktop access denied)
|
|
STATUS_SUCCESS -- Operation sucseeded
|
|
|
|
--*/
|
|
{
|
|
|
|
NTSTATUS Status = STATUS_UNSUCCESSFUL;
|
|
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, Buffer = NULL;
|
|
ULONG Len;
|
|
ULONG bufPointers;
|
|
LPWSTR wsBuffer;
|
|
LPWSTR wsAppName;
|
|
LPWSTR wsPifName;
|
|
LPWSTR wsCmdLine;
|
|
LPWSTR wsPif=L".pif";
|
|
LPWSTR wsSharedWowPif=L"wowexec.pif";
|
|
PWCHAR pwch;
|
|
BOOLEAN bNewConsole;
|
|
DWORD dw, dwTotal, Length;
|
|
WCHAR wchBuffer[MAX_PATH + 1];
|
|
ULONG BinarySubType;
|
|
LPWSTR lpAllocatedReserved = NULL;
|
|
DWORD HandleFlags;
|
|
LUID VdmUserLuid;
|
|
|
|
// does a trivial test of the environment
|
|
if (!ARGUMENT_PRESENT(pAnsiStringEnv) ||
|
|
pAnsiStringEnv->Length > MAXIMUM_VDM_ENVIORNMENT) {
|
|
BaseSetLastNTError(STATUS_INVALID_PARAMETER);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
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) {
|
|
|
|
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) {
|
|
Status = STATUS_NO_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) {
|
|
Status = STATUS_OBJECT_PATH_INVALID;
|
|
goto BCVTryExit;
|
|
}
|
|
|
|
RtlInitUnicodeString(&UnicodeString, wsAppName);
|
|
Status = RtlUnicodeStringToOemString(&OemStringAppName,
|
|
&UnicodeString,
|
|
TRUE
|
|
);
|
|
if (!NT_SUCCESS(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) ){
|
|
goto BCVTryExit;
|
|
}
|
|
|
|
//
|
|
// check len of command line for dos compatibility
|
|
//
|
|
if (OemStringCmd.Length >= MAXIMUM_VDM_COMMAND_LENGTH - Length) {
|
|
Status = STATUS_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) {
|
|
Status = STATUS_NO_MEMORY;
|
|
goto BCVTryExit;
|
|
}
|
|
|
|
wsPifName = RtlAllocateHeap(RtlProcessHeap(),MAKE_TAG( VDM_TAG ),MAX_PATH*sizeof(WCHAR));
|
|
if (!wsPifName) {
|
|
Status = STATUS_NO_MEMORY;
|
|
goto BCVTryExit;
|
|
}
|
|
|
|
if (BinaryType == BINARY_TYPE_WIN16) {
|
|
wcscpy(wsBuffer, wsSharedWowPif);
|
|
Len = 0;
|
|
}
|
|
else {
|
|
// dos application path should be less than MAX_PATH
|
|
if(Len >= MAX_PATH) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto BCVTryExit;
|
|
}
|
|
|
|
// 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 || wsBuffer - pwch + MAX_PATH < sizeof(wsPif)/sizeof(WCHAR)) {
|
|
Status = STATUS_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) {
|
|
Status = STATUS_NO_MEMORY;
|
|
goto BCVTryExit;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!Len)
|
|
*wsPifName = UNICODE_NULL;
|
|
|
|
|
|
|
|
if (!ARGUMENT_PRESENT( lpCurrentDirectory )) {
|
|
#pragma prefast(suppress: 209, "RtlGetCurrentDirectory_U takes a byte count")
|
|
|
|
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 {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
goto BCVTryExit;
|
|
}
|
|
}
|
|
else {
|
|
|
|
// first get a full path name
|
|
dw = GetFullPathNameW(lpCurrentDirectory,
|
|
sizeof(wchBuffer) / sizeof(WCHAR),
|
|
wchBuffer,
|
|
NULL);
|
|
if (0 != dw && dw <= sizeof(wchBuffer) / sizeof(WCHAR)) {
|
|
dw = GetShortPathNameW(wchBuffer,
|
|
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) ){
|
|
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) {
|
|
Status = STATUS_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 ????????????????????????????????
|
|
//
|
|
// 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 specified 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",
|
|
HandleToUlong(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) ){
|
|
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) ){
|
|
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) ){
|
|
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) ){
|
|
goto BCVTryExit;
|
|
}
|
|
Len += ROUND_UP((AnsiStringReserved.Length+1),4);
|
|
}
|
|
}
|
|
|
|
//
|
|
// luid space
|
|
//
|
|
// Allocate UserLuid pointer
|
|
if(hUserToken) {
|
|
bufPointers++;
|
|
|
|
if (!NT_SUCCESS(BaseGetVdmLuid(hUserToken,&VdmUserLuid))) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto BCVTryExit;
|
|
}
|
|
Len += ROUND_UP(sizeof(LUID),4);
|
|
}
|
|
|
|
|
|
// capture message buffer
|
|
CaptureBuffer = CsrAllocateCaptureBuffer(bufPointers, Len);
|
|
if (CaptureBuffer == NULL) {
|
|
Status = 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;
|
|
}
|
|
|
|
// Allocate UserLuid pointer
|
|
if(hUserToken) {
|
|
CsrAllocateMessagePointer( CaptureBuffer,
|
|
ROUND_UP(sizeof(LUID),4),
|
|
(PVOID *)&b->UserLuid
|
|
);
|
|
RtlCopyLuid(b->UserLuid,&VdmUserLuid);
|
|
}
|
|
else {
|
|
b->UserLuid = NULL;
|
|
}
|
|
|
|
|
|
// VadimB: this code is of no consequence to our marvelous new
|
|
// architecture for tracking shared wows.
|
|
// Reason: the checkvdm command is executed within the context of
|
|
// a parent process thus at this point ConsoleHandle is of any
|
|
// interest only to DOS apps.
|
|
|
|
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 )
|
|
);
|
|
|
|
// if desktop access is denied, then we try again with the
|
|
// current default desktop
|
|
//
|
|
|
|
if ((STATUS_ACCESS_DENIED == Status) && (0 == b->DesktopLen)) {
|
|
CsrFreeCaptureBuffer(CaptureBuffer);
|
|
goto BCVTryExit;
|
|
}
|
|
|
|
|
|
CsrFreeCaptureBuffer(CaptureBuffer);
|
|
|
|
if (!NT_SUCCESS(Status) || !NT_SUCCESS((NTSTATUS)m->ReturnValue)) {
|
|
Status = (NTSTATUS)m->ReturnValue;
|
|
goto BCVTryExit;
|
|
}
|
|
|
|
|
|
// VadimB: This iTask could be :
|
|
// (*) If not wow task - then dos task id (items below are not
|
|
// relevant for this case)
|
|
// (*) Shared wow exists and ready - this is a wow task id
|
|
// that is unique across all the shared wows
|
|
|
|
*iTask = b->iTask;
|
|
Status = STATUS_SUCCESS;
|
|
|
|
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 Status;
|
|
}
|
|
|
|
|
|
/*
|
|
jarbats
|
|
Some apps send startupinfo with bad desktop name
|
|
as a result, basecheckvdm will fail with because
|
|
access to the desktop can't be obtained
|
|
in that case we attempt again with the parents desktop
|
|
|
|
*/
|
|
|
|
|
|
NTSTATUS
|
|
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,
|
|
IN HANDLE hUserToken
|
|
) {
|
|
|
|
NTSTATUS Status;
|
|
LPWSTR lpDesktopOld;
|
|
|
|
Status = BaseCheckVDMp(
|
|
BinaryType,
|
|
lpApplicationName,
|
|
lpCommandLine,
|
|
lpCurrentDirectory,
|
|
pAnsiStringEnv,
|
|
m,
|
|
iTask,
|
|
dwCreationFlags,
|
|
lpStartupInfo,
|
|
hUserToken
|
|
);
|
|
|
|
if ( Status == STATUS_ACCESS_DENIED ) {
|
|
|
|
lpDesktopOld = lpStartupInfo->lpDesktop;
|
|
|
|
lpStartupInfo->lpDesktop =
|
|
(LPWSTR)((PRTL_USER_PROCESS_PARAMETERS)NtCurrentPeb()->
|
|
ProcessParameters)->DesktopInfo.Buffer;
|
|
|
|
Status = BaseCheckVDMp(
|
|
BinaryType,
|
|
lpApplicationName,
|
|
lpCommandLine,
|
|
lpCurrentDirectory,
|
|
pAnsiStringEnv,
|
|
m,
|
|
iTask,
|
|
dwCreationFlags,
|
|
lpStartupInfo,
|
|
hUserToken
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
lpStartupInfo->lpDesktop = lpDesktopOld;
|
|
}
|
|
} else if (Status == STATUS_VDM_DISALLOWED) {
|
|
UNICODE_STRING UnicodeString;
|
|
ULONG_PTR ErrorParameters[2];
|
|
ULONG ErrorResponse;
|
|
|
|
RtlInitUnicodeString(&UnicodeString, lpApplicationName);
|
|
ErrorParameters[0] = (ULONG_PTR)&UnicodeString;
|
|
NtRaiseHardError(
|
|
STATUS_VDM_DISALLOWED,
|
|
1, // 1 parameter
|
|
1, // ParameterStringMask
|
|
ErrorParameters,
|
|
OptionOk,
|
|
&ErrorResponse
|
|
);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
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 = &m.u.UpdateVDMEntry;
|
|
|
|
switch (UpdateIndex) {
|
|
case UPDATE_VDM_UNDO_CREATION:
|
|
c->iTask = HandleToUlong(*WaitHandle);
|
|
c->VDMCreationState = (USHORT)IndexInfo;
|
|
break;
|
|
case UPDATE_VDM_PROCESS_HANDLE:
|
|
c->VDMProcessHandle = *WaitHandle; // Actually this is VDM handle
|
|
c->iTask = IndexInfo;
|
|
break;
|
|
}
|
|
|
|
// VadimB: this ConsoleHandle is of no consequence to the
|
|
// shared wow tracking mechanism
|
|
|
|
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,
|
|
IN 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. This parameter is also valid for
|
|
shared wow as it is passed to ntvdm as -i parameter. Another
|
|
parameter to identify shared wow is passed to ntvdm as
|
|
'-ws' where 'w' stands for wow app, 's' stands for separate
|
|
In response to this 's' parameter ntvdm launches a
|
|
separate wow (one-time shot). By default, ntvdm starts a shared
|
|
wow.
|
|
|
|
VdmSize --entry:
|
|
return: 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;
|
|
ANSI_STRING AnsiString;
|
|
PCH pDst;
|
|
WCHAR CmdLine[2*MAX_PATH];
|
|
WCHAR szSystemDirectory[MAX_PATH];
|
|
DWORD dwLen;
|
|
|
|
CmdLineString->Buffer = NULL;
|
|
*VdmSize = 16L*1024L*1024L;
|
|
|
|
dwLen = GetSystemDirectoryW(szSystemDirectory,MAX_PATH);
|
|
if(!dwLen || dwLen >= MAX_PATH) {
|
|
SetLastError(ERROR_INVALID_NAME);
|
|
return FALSE;
|
|
}
|
|
|
|
if (DosSeqId) {
|
|
#pragma prefast(suppress:53, the buffer is big enough (PREfast bug 775))
|
|
_snwprintf(CmdLine,
|
|
sizeof(CmdLine)/sizeof(WCHAR),
|
|
L"\"%s\\ntvdm.exe\" -i%lx %s%c",
|
|
szSystemDirectory,
|
|
DosSeqId,
|
|
BinaryType != BINARY_TYPE_DOS? L"-w":L"",
|
|
BinaryType == BINARY_TYPE_SEPWOW? L's':L' '
|
|
);
|
|
}
|
|
else {
|
|
#pragma prefast(suppress:53, the buffer is big enough (PREfast bug 775))
|
|
_snwprintf(CmdLine,
|
|
sizeof(CmdLine)/sizeof(WCHAR),
|
|
L"\"%s\\ntvdm.exe\" %s%c",
|
|
szSystemDirectory,
|
|
BinaryType != BINARY_TYPE_DOS? L"-w":L"",
|
|
BinaryType == BINARY_TYPE_SEPWOW? L's':L'\0'
|
|
);
|
|
}
|
|
|
|
return RtlCreateUnicodeString(CmdLineString, CmdLine);
|
|
}
|
|
|
|
|
|
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 = &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;
|
|
WCHAR TempPathW[MAX_PATH];
|
|
LPWSTR lpShortPathW = NULL;
|
|
DWORD ReturnValue;
|
|
DWORD ReturnValueW;
|
|
|
|
if (lpszLongPath == NULL) {
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return 0;
|
|
}
|
|
// We have to initialize it before the "try" statement
|
|
AString.Buffer = NULL;
|
|
UString.Buffer = NULL;
|
|
|
|
ReturnValue = 0;
|
|
ReturnValueW = 0;
|
|
|
|
try {
|
|
if (!Basep8BitStringToDynamicUnicodeString(&UString, lpszLongPath )) {
|
|
goto gspTryExit;
|
|
}
|
|
|
|
// we have to get the real converted path in order to find out
|
|
// the required length. An UNICODE char does not necessarily convert
|
|
// to one ANSI char(A DBCS is basically TWO ANSI char!!!!!).
|
|
// First, we use the buffer allocated from the stack. If the buffer
|
|
// is too small, we then allocate it from heap.
|
|
// A check of (lpShortPathW && TempPathW != lpShortPathW) will reveal
|
|
// if we have allocated a buffer from heap and need to release it.
|
|
lpShortPathW = TempPathW;
|
|
ReturnValueW = GetShortPathNameW(UString.Buffer, lpShortPathW, sizeof(TempPathW) / sizeof(WCHAR));
|
|
if (ReturnValueW >= sizeof(TempPathW) / sizeof(WCHAR))
|
|
{
|
|
// the stack-based buffer is too small. Allocate a new buffer
|
|
// from heap.
|
|
lpShortPathW = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG( VDM_TAG ),
|
|
ReturnValueW * sizeof(WCHAR)
|
|
);
|
|
if (lpShortPathW) {
|
|
ReturnValueW = GetShortPathNameW(UString.Buffer, lpShortPathW, ReturnValueW);
|
|
}
|
|
else {
|
|
ReturnValueW = 0;
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
}
|
|
|
|
if (ReturnValueW)
|
|
{
|
|
// we are here because we have something interesting left to do.
|
|
// Convert the UNICODE path name to ANSI(or OEM).
|
|
UString.MaximumLength = (USHORT)((ReturnValueW + 1) * sizeof(WCHAR));
|
|
UStringRet.Buffer = lpShortPathW;
|
|
UStringRet.Length = (USHORT)(ReturnValueW * sizeof(WCHAR));
|
|
Status = BasepUnicodeStringTo8BitString(&AString,
|
|
&UStringRet,
|
|
TRUE
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
BaseSetLastNTError(Status);
|
|
ReturnValue=0;
|
|
goto gspTryExit;
|
|
}
|
|
// now AString.Length contains the size of the converted path
|
|
// name. If the caller provides enough buffer, copy the
|
|
// path name.
|
|
ReturnValue = AString.Length;
|
|
if (ARGUMENT_PRESENT(lpShortPath) && cchBuffer > ReturnValue)
|
|
{
|
|
RtlMoveMemory(lpShortPath, AString.Buffer, ReturnValue);
|
|
// terminate the string with NULL char
|
|
lpShortPath[ReturnValue] = '\0';
|
|
}
|
|
else
|
|
{
|
|
// either the caller does not provide a buffer or
|
|
// the provided buffer is too small return the required size,
|
|
// including the terminated null char
|
|
ReturnValue++;
|
|
}
|
|
}
|
|
gspTryExit:;
|
|
}
|
|
|
|
finally {
|
|
if (UString.Buffer)
|
|
RtlFreeUnicodeString(&UString);
|
|
if (AString.Buffer)
|
|
RtlFreeAnsiString(&AString);
|
|
if (lpShortPathW && lpShortPathW != TempPathW)
|
|
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
|
|
)
|
|
{
|
|
|
|
LPCWSTR pcs;
|
|
LPWSTR pSrcCopy, pSrc, pFirst, pLast, pDst;
|
|
WCHAR wch;
|
|
HANDLE FindHandle;
|
|
WIN32_FIND_DATAW FindData;
|
|
LPWSTR Buffer;
|
|
DWORD ReturnLen=0, Length;
|
|
UINT PrevErrorMode;
|
|
if (!ARGUMENT_PRESENT(lpszLongPath)) {
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// override the error mode since we will be touching the media.
|
|
// This is to prevent file system's pop-up when the given path does not
|
|
// exist or the media is not available.
|
|
// we are doing this because we can not depend on the caller's current
|
|
// error mode. NOTE: the old error mode must be restored.
|
|
PrevErrorMode = SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
|
|
|
|
try {
|
|
Buffer = NULL;
|
|
pSrcCopy = NULL;
|
|
// first, make sure the given path exist
|
|
if (0xFFFFFFFF == GetFileAttributesW(lpszLongPath))
|
|
{
|
|
//
|
|
// (bjm - 3/17/99)
|
|
// This behavior (failing if the file does not exist) is new (to NT) with NT 5.
|
|
// (It's the Win9x behavior.)
|
|
// Give an exception to Norton AntiVirus Uninstall.
|
|
// If we fail this call, there will be a registry value left behind in VDD that'll cause
|
|
// undeserved ugly messages for a user. Norton AV Uninstall counts on NT 4 behavior
|
|
// which did not care if the file existed to do this conversion. This was changed in
|
|
// NT 5.0 to match Win9x behavior.
|
|
//
|
|
if ( !NtCurrentPeb() || !APPCOMPATFLAG(KACF_OLDGETSHORTPATHNAME) )
|
|
{
|
|
// last error has been set by GetFileAttributes
|
|
ReturnLen = 0;
|
|
goto gsnTryExit;
|
|
}
|
|
}
|
|
|
|
pcs = SkipPathTypeIndicator_U(lpszLongPath);
|
|
if (!pcs || *pcs == UNICODE_NULL || !FindLFNorSFN_U((LPWSTR)pcs, &pFirst, &pLast, TRUE))
|
|
{
|
|
// nothing to convert, copy down the source string
|
|
// to the buffer if necessary
|
|
|
|
ReturnLen = wcslen(lpszLongPath);
|
|
if (cchBuffer > ReturnLen && ARGUMENT_PRESENT(lpszShortPath))
|
|
{
|
|
if (lpszShortPath != lpszLongPath)
|
|
RtlMoveMemory(lpszShortPath, lpszLongPath,
|
|
(ReturnLen + 1) * sizeof(WCHAR)
|
|
);
|
|
}
|
|
else {
|
|
// the caller does not provide enough buffer, return
|
|
// necessary string length plus the terminated null char
|
|
ReturnLen++;
|
|
}
|
|
goto gsnTryExit;
|
|
}
|
|
|
|
// conversions are necessary, make a local copy of the string
|
|
// because we have to party on it.
|
|
|
|
ASSERT(!pSrcCopy);
|
|
|
|
// get the source string length
|
|
Length = wcslen(lpszLongPath) + 1;
|
|
|
|
pSrcCopy = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG( VDM_TAG ),
|
|
Length * sizeof(WCHAR)
|
|
);
|
|
if (!pSrcCopy) {
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
goto gsnTryExit;
|
|
}
|
|
wcsncpy(pSrcCopy, lpszLongPath, Length);
|
|
// pFirst points to the first char of the very first LFN in the path
|
|
// pLast points to the char right after the last char of the very
|
|
// first LFN in the path. *pLast could be UNICODE_NULL
|
|
pFirst = pSrcCopy + (pFirst - lpszLongPath);
|
|
pLast = pSrcCopy + (pLast - lpszLongPath);
|
|
//
|
|
// We allow lpszShortPath be overlapped with lpszLongPath so
|
|
// allocate a local buffer.
|
|
|
|
pDst = lpszShortPath;
|
|
if (cchBuffer > 0 && ARGUMENT_PRESENT(lpszShortPath) &&
|
|
(lpszShortPath >= lpszLongPath &&lpszShortPath < lpszLongPath + Length ||
|
|
lpszShortPath < lpszLongPath && lpszShortPath + cchBuffer >= lpszLongPath))
|
|
{
|
|
ASSERT(!Buffer);
|
|
|
|
Buffer = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG( VDM_TAG ),
|
|
cchBuffer * sizeof(WCHAR));
|
|
if (!Buffer){
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
goto gsnTryExit;
|
|
}
|
|
pDst = Buffer;
|
|
}
|
|
|
|
pSrc = pSrcCopy;
|
|
|
|
ReturnLen = 0;
|
|
do {
|
|
// there are three pointers involve in the conversion loop:
|
|
// pSrc, pFirst and pLast. Their relationship
|
|
// is:
|
|
//
|
|
// "c:\long~1.1\\foo.bar\\long~2.2\\bar"
|
|
// ^ ^ ^ ^
|
|
// | | | |
|
|
// | pSrc pFirst pLast
|
|
// pSrcCopy
|
|
//
|
|
// pSrcCopy always points to the very first char of the entire
|
|
// path.
|
|
//
|
|
// chars between pSrc(included) and pFirst(not included)
|
|
// do not need conversion so we simply copy them.
|
|
// chars between pFirst(included) and pLast(not included)
|
|
// need conversion.
|
|
//
|
|
Length = (ULONG)(pFirst - pSrc);
|
|
if (Length) {
|
|
ReturnLen += Length;
|
|
if (cchBuffer > ReturnLen && ARGUMENT_PRESENT(lpszShortPath)) {
|
|
RtlMoveMemory(pDst, pSrc, Length * sizeof(WCHAR));
|
|
pDst += Length;
|
|
}
|
|
}
|
|
wch = *pLast;
|
|
*pLast = UNICODE_NULL;
|
|
FindHandle = FindFirstFileW(pSrcCopy, &FindData);
|
|
*pLast = wch;
|
|
if (INVALID_HANDLE_VALUE != FindHandle) {
|
|
FindClose(FindHandle);
|
|
// if no short name could be found, copy the original name.
|
|
// the origian name starts with pFirst(included) and ends
|
|
// with pLast(excluded).
|
|
if (!(Length = wcslen(FindData.cAlternateFileName)))
|
|
Length = (ULONG)(pLast - pFirst);
|
|
else
|
|
pFirst = FindData.cAlternateFileName;
|
|
ReturnLen += Length;
|
|
if (cchBuffer > ReturnLen && ARGUMENT_PRESENT(lpszShortPath))
|
|
{
|
|
RtlMoveMemory(pDst, pFirst, Length * sizeof(WCHAR));
|
|
pDst += Length;
|
|
}
|
|
}
|
|
else {
|
|
// part of the path does not exist, fail the function
|
|
//
|
|
ReturnLen = 0;
|
|
break;
|
|
}
|
|
// move to next path name
|
|
pSrc = pLast;
|
|
if (*pLast == UNICODE_NULL)
|
|
break;
|
|
}while (FindLFNorSFN_U(pSrc, &pFirst, &pLast, TRUE));
|
|
|
|
// if ReturnLen == 0, we fail somewhere inside while loop.
|
|
if (ReturnLen) {
|
|
// (*pSrc == UNICODE_NULL) means the last pathname is a LFN which
|
|
// has been dealt with. otherwise, the substring pointed by
|
|
// pSrc is a legal short path name and we have to copy it
|
|
//Length could be zero
|
|
Length = wcslen(pSrc);
|
|
ReturnLen += Length;
|
|
if (cchBuffer > ReturnLen && ARGUMENT_PRESENT(lpszShortPath))
|
|
{
|
|
//include the terminated null char
|
|
RtlMoveMemory(pDst, pSrc, (Length + 1)* sizeof(WCHAR));
|
|
if (Buffer)
|
|
RtlMoveMemory(lpszShortPath, Buffer, (ReturnLen + 1) * sizeof(WCHAR));
|
|
}
|
|
else
|
|
// not enough buffer, the return value counts the terminated NULL
|
|
ReturnLen++;
|
|
}
|
|
gsnTryExit:;
|
|
}
|
|
finally {
|
|
if (Buffer)
|
|
RtlFreeHeap(RtlProcessHeap(), 0, Buffer);
|
|
if (pSrcCopy)
|
|
RtlFreeHeap(RtlProcessHeap(), 0, pSrcCopy);
|
|
|
|
// restore the error mode
|
|
SetErrorMode(PrevErrorMode);
|
|
}
|
|
|
|
return ReturnLen;
|
|
}
|
|
|
|
/**
|
|
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=NULL,* pTmp, *pNewEnv=NULL;
|
|
DWORD cchEnv, dw, Length, dwRemain;
|
|
NTSTATUS Status;
|
|
UINT NameType;
|
|
BOOL bRet = FALSE;
|
|
SIZE_T EnvSize;
|
|
|
|
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.
|
|
EnvSize = (cchEnv + MAX_PATH) * sizeof(WCHAR);
|
|
Status = NtAllocateVirtualMemory( NtCurrentProcess(),
|
|
&pNewEnv,
|
|
0,
|
|
&EnvSize,
|
|
MEM_COMMIT,
|
|
PAGE_READWRITE
|
|
);
|
|
if (!NT_SUCCESS(Status) ) {
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
pNewEnv = NULL;
|
|
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) {
|
|
// current directory environment has the form as:
|
|
// "=d:=d:\pathname" where d: is the drive designator.
|
|
if (pEnv[0] == L'=')
|
|
{
|
|
if ((pEnv[1] >= L'A' && pEnv[1] <= L'Z' || pEnv[1] >= L'a' && pEnv[1] <= L'z') &&
|
|
pEnv[2] == L':' && pEnv[3] == L'=' && wcslen(pEnv) >= 7)
|
|
{
|
|
// hack hack!!!!
|
|
// if the path points to the root directory,
|
|
// bypass the conversion. Dos or Wow keeps current directory
|
|
// for every valid drive. If we do the conversion for
|
|
// every current directory, it could take several
|
|
// seconds on removable drives, especially, on
|
|
// floppy drives.
|
|
if (pEnv[7] == UNICODE_NULL &&
|
|
(pEnv[6] == L'\\' || pEnv[6] == L'/') &&
|
|
pEnv[5] == L':' &&
|
|
(pEnv[4] >= L'A' && pEnv[4] <= L'Z' ||
|
|
pEnv[4] >= L'a' && pEnv[4] <= L'z'))
|
|
{
|
|
NameType = ENV_NAME_TYPE_NO_PATH;
|
|
}
|
|
else
|
|
{
|
|
// copy "=N:=", where N is the drive letter
|
|
*pDst++ = *pEnv++;*pDst++ = *pEnv++;
|
|
*pDst++ = *pEnv++;*pDst++ = *pEnv++;
|
|
NameType = ENV_NAME_TYPE_SINGLE_PATH;
|
|
}
|
|
}
|
|
else {
|
|
// a weird environment was detected.
|
|
// treat it as no path
|
|
NameType = ENV_NAME_TYPE_NO_PATH;
|
|
}
|
|
}
|
|
else {
|
|
pTmp = pEnv;
|
|
// copy down the name and the '='
|
|
while (*pEnv != UNICODE_NULL && (*pDst++ = *pEnv++) != L'=')
|
|
;
|
|
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 == L';') {
|
|
// 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++ = L';';
|
|
if (dw > Length)
|
|
dwRemain -= dw - Length;
|
|
}
|
|
// skip all consecutive ';'
|
|
while (*pEnv == L';')
|
|
*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 = (ULONG)((ULONG_PTR)pDst - (ULONG_PTR)pNewEnv);
|
|
pUStringEnv->MaximumLength = pUStringEnv->Length = (USHORT)cchEnv;
|
|
pUStringEnv->Buffer = pNewEnv;
|
|
Status = RtlUnicodeStringToAnsiString(pAStringEnv,
|
|
pUStringEnv,
|
|
TRUE
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
BaseSetLastNTError(Status);
|
|
} else {
|
|
pNewEnv = NULL;
|
|
bRet = TRUE;
|
|
}
|
|
bveTryExit:;
|
|
}
|
|
finally {
|
|
if (lpEnvironment == NULL && EnvStrings != NULL) {
|
|
RtlDestroyEnvironment(EnvStrings);
|
|
}
|
|
if (pNewEnv != NULL) {
|
|
pUStringEnv->Length = pUStringEnv->MaximumLength = 0;
|
|
pUStringEnv->Buffer = NULL;
|
|
pAStringEnv->Length = pAStringEnv->MaximumLength = 0;
|
|
pAStringEnv->Buffer = NULL;
|
|
EnvSize = 0;
|
|
Status = NtFreeVirtualMemory (NtCurrentProcess(),
|
|
&pNewEnv,
|
|
&EnvSize,
|
|
MEM_RELEASE);
|
|
ASSERT (NT_SUCCESS (Status));
|
|
}
|
|
}
|
|
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) {
|
|
NTSTATUS Status;
|
|
SIZE_T RegionSize;
|
|
|
|
//
|
|
// Free the specified environment variable block.
|
|
//
|
|
|
|
RegionSize = 0;
|
|
Status = NtFreeVirtualMemory( NtCurrentProcess(),
|
|
&pUStringEnv->Buffer,
|
|
&RegionSize,
|
|
MEM_RELEASE
|
|
);
|
|
}
|
|
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 five predefined names:
|
|
// PATH
|
|
// WINDIR and
|
|
// SYSTEMROOT.
|
|
// TEMP
|
|
// TMP
|
|
//
|
|
static ENV_INFO EnvInfoTable[STD_ENV_NAME_COUNT] = {
|
|
{ENV_NAME_TYPE_MULTIPLE_PATH, ENV_NAME_PATH_LEN, ENV_NAME_PATH},
|
|
{ENV_NAME_TYPE_SINGLE_PATH, ENV_NAME_WINDIR_LEN, ENV_NAME_WINDIR},
|
|
{ENV_NAME_TYPE_SINGLE_PATH, ENV_NAME_SYSTEMROOT_LEN, ENV_NAME_SYSTEMROOT},
|
|
{ENV_NAME_TYPE_MULTIPLE_PATH, ENV_NAME_TEMP_LEN, ENV_NAME_TEMP},
|
|
{ENV_NAME_TYPE_MULTIPLE_PATH, ENV_NAME_TMP_LEN, ENV_NAME_TMP}
|
|
};
|
|
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
DWORD
|
|
APIENTRY
|
|
GetLongPathNameA(
|
|
IN LPCSTR lpszShortPath,
|
|
IN LPSTR lpLongPath,
|
|
IN DWORD cchBuffer
|
|
)
|
|
{
|
|
UNICODE_STRING UString, UStringRet;
|
|
ANSI_STRING AString;
|
|
NTSTATUS Status;
|
|
LPWSTR lpLongPathW = NULL;
|
|
WCHAR TempPathW[MAX_PATH];
|
|
DWORD ReturnValue, ReturnValueW;
|
|
|
|
|
|
if (lpszShortPath == NULL) {
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return 0;
|
|
}
|
|
|
|
AString.Buffer = NULL;
|
|
UString.Buffer = NULL;
|
|
|
|
ReturnValue = 0;
|
|
ReturnValueW = 0;
|
|
|
|
try {
|
|
if (!Basep8BitStringToDynamicUnicodeString(&UString, lpszShortPath )) {
|
|
goto glpTryExit;
|
|
}
|
|
|
|
// we have to get the real converted path in order to find out
|
|
// the required length. An UNICODE char does not necessarily convert
|
|
// to one ANSI char(A DBCS is basically TWO ANSI char!!!!!).
|
|
// First, we use the buffer allocated from the stack. If the buffer
|
|
// is too small, we then allocate it from heap.
|
|
// A check of (lpLongPathW && TempPathW != lpLongPathW) will reveal
|
|
// if we have allocated a buffer from heap and need to release it.
|
|
lpLongPathW = TempPathW;
|
|
ReturnValueW = GetLongPathNameW(UString.Buffer, lpLongPathW, sizeof(TempPathW) / sizeof(WCHAR));
|
|
if (ReturnValueW >= sizeof(TempPathW) / sizeof(WCHAR))
|
|
{
|
|
// the stack-based buffer is too small. Allocate a new buffer
|
|
// from heap.
|
|
lpLongPathW = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG( VDM_TAG ),
|
|
ReturnValueW * sizeof(WCHAR)
|
|
);
|
|
if (lpLongPathW) {
|
|
ReturnValueW = GetLongPathNameW(UString.Buffer, lpLongPathW, ReturnValueW);
|
|
}
|
|
else {
|
|
ReturnValueW = 0;
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
}
|
|
|
|
if (ReturnValueW)
|
|
{
|
|
// we are here because we have something interesting left to do.
|
|
// Convert the UNICODE path name to ANSI(or OEM).
|
|
UString.MaximumLength = (USHORT)((ReturnValueW + 1) * sizeof(WCHAR));
|
|
UStringRet.Buffer = lpLongPathW;
|
|
UStringRet.Length = (USHORT)(ReturnValueW * sizeof(WCHAR));
|
|
Status = BasepUnicodeStringTo8BitString(&AString,
|
|
&UStringRet,
|
|
TRUE
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
BaseSetLastNTError(Status);
|
|
ReturnValue=0;
|
|
goto glpTryExit;
|
|
}
|
|
// now AString.Length contains the size of the converted path
|
|
// name. If the caller provides enough buffer, copy the
|
|
// path name.
|
|
ReturnValue = AString.Length;
|
|
if (ARGUMENT_PRESENT(lpLongPath) && cchBuffer > ReturnValue)
|
|
{
|
|
RtlMoveMemory(lpLongPath, AString.Buffer, ReturnValue);
|
|
// terminate the buffer with NULL char.
|
|
lpLongPath[ReturnValue] = '\0';
|
|
}
|
|
else
|
|
{
|
|
// either the caller does not provide a buffer or
|
|
// the provided buffer is too small, return the required size,
|
|
// including the terminated null char.
|
|
ReturnValue++;
|
|
}
|
|
}
|
|
glpTryExit:;
|
|
}
|
|
|
|
finally {
|
|
if (UString.Buffer)
|
|
RtlFreeUnicodeString(&UString);
|
|
if (AString.Buffer)
|
|
RtlFreeAnsiString(&AString);
|
|
if (lpLongPathW && lpLongPathW != TempPathW)
|
|
RtlFreeHeap(RtlProcessHeap(), 0, lpLongPathW);
|
|
}
|
|
return ReturnValue;
|
|
}
|
|
|
|
DWORD
|
|
APIENTRY
|
|
GetLongPathNameW(
|
|
IN LPCWSTR lpszShortPath,
|
|
IN LPWSTR lpszLongPath,
|
|
IN DWORD cchBuffer
|
|
)
|
|
{
|
|
|
|
LPCWSTR pcs;
|
|
DWORD ReturnLen, Length;
|
|
LPWSTR pSrc, pSrcCopy, pFirst, pLast, Buffer, pDst;
|
|
WCHAR wch;
|
|
HANDLE FindHandle;
|
|
WIN32_FIND_DATAW FindData;
|
|
UINT PrevErrorMode;
|
|
|
|
if (!ARGUMENT_PRESENT(lpszShortPath)) {
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return 0;
|
|
}
|
|
//
|
|
// override the error mode since we will be touching the media.
|
|
// This is to prevent file system's pop-up when the given path does not
|
|
// exist or the media is not available.
|
|
// we are doing this because we can not depend on the caller's current
|
|
// error mode. NOTE: the old error mode must be restored.
|
|
PrevErrorMode = SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
|
|
|
|
try {
|
|
|
|
Buffer = NULL;
|
|
pSrcCopy = NULL;
|
|
// first make sure the given path exist.
|
|
//
|
|
if (0xFFFFFFFF == GetFileAttributesW(lpszShortPath))
|
|
{
|
|
// last error has been set by GetFileAttributes
|
|
ReturnLen = 0;
|
|
goto glnTryExit;
|
|
}
|
|
pcs = SkipPathTypeIndicator_U(lpszShortPath);
|
|
if (!pcs || *pcs == UNICODE_NULL || !FindLFNorSFN_U((LPWSTR)pcs, &pFirst, &pLast, FALSE))
|
|
{
|
|
// The path is ok and does not need conversion at all.
|
|
// Check if we need to do copy
|
|
ReturnLen = wcslen(lpszShortPath);
|
|
if (cchBuffer > ReturnLen && ARGUMENT_PRESENT(lpszLongPath))
|
|
{
|
|
if (lpszLongPath != lpszShortPath)
|
|
RtlMoveMemory(lpszLongPath, lpszShortPath,
|
|
(ReturnLen + 1)* sizeof(WCHAR)
|
|
);
|
|
}
|
|
else {
|
|
// No buffer or buffer too small, the return size
|
|
// has to count the terminated NULL char
|
|
ReturnLen++;
|
|
}
|
|
goto glnTryExit;
|
|
}
|
|
|
|
|
|
// conversions are necessary, make a local copy of the string
|
|
// because we have to party on it.
|
|
|
|
ASSERT(!pSrcCopy);
|
|
|
|
Length = wcslen(lpszShortPath) + 1;
|
|
pSrcCopy = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG( VDM_TAG ),
|
|
Length * sizeof(WCHAR)
|
|
);
|
|
if (!pSrcCopy) {
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
goto glnTryExit;
|
|
}
|
|
RtlMoveMemory(pSrcCopy, lpszShortPath, Length * sizeof(WCHAR));
|
|
// pFirst points to the first char of the very first SFN in the path
|
|
// pLast points to the char right after the last char of the very
|
|
// first SFN in the path. *pLast could be UNICODE_NULL
|
|
pFirst = pSrcCopy + (pFirst - lpszShortPath);
|
|
pLast = pSrcCopy + (pLast - lpszShortPath);
|
|
//
|
|
// We allow lpszShortPath be overlapped with lpszLongPath so
|
|
// allocate a local buffer if necessary:
|
|
// (1) the caller does provide a legitimate buffer and
|
|
// (2) the buffer overlaps with lpszShortName
|
|
|
|
pDst = lpszLongPath;
|
|
if (cchBuffer && ARGUMENT_PRESENT(lpszLongPath) &&
|
|
(lpszLongPath >= lpszShortPath && lpszLongPath < lpszShortPath + Length ||
|
|
lpszLongPath < lpszShortPath && lpszLongPath + cchBuffer >= lpszShortPath))
|
|
{
|
|
ASSERT(!Buffer);
|
|
|
|
Buffer = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG( VDM_TAG ),
|
|
cchBuffer * sizeof(WCHAR));
|
|
if (!Buffer){
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
goto glnTryExit;
|
|
}
|
|
pDst = Buffer;
|
|
}
|
|
|
|
pSrc = pSrcCopy;
|
|
ReturnLen = 0;
|
|
do {
|
|
// there are three pointers involve in the conversion loop:
|
|
// pSrc, pFirst and pLast. Their relationship
|
|
// is:
|
|
//
|
|
// "c:\long~1.1\\foo.bar\\long~2.2\\bar"
|
|
// ^ ^ ^ ^
|
|
// | | | |
|
|
// | pSrc pFirst pLast
|
|
// pSrcCopy
|
|
//
|
|
// pSrcCopy always points to the very first char of the entire
|
|
// path.
|
|
//
|
|
// chars between pSrc(included) and pFirst(not included)
|
|
// do not need conversion so we simply copy them.
|
|
// chars between pFirst(included) and pLast(not included)
|
|
// need conversion.
|
|
//
|
|
Length = (ULONG)(pFirst - pSrc);
|
|
ReturnLen += Length;
|
|
if (Length && cchBuffer > ReturnLen && ARGUMENT_PRESENT(lpszShortPath))
|
|
{
|
|
RtlMoveMemory(pDst, pSrc, Length * sizeof(WCHAR));
|
|
pDst += Length;
|
|
}
|
|
// now try to convert the name, chars between pFirst and (pLast - 1)
|
|
wch = *pLast;
|
|
*pLast = UNICODE_NULL;
|
|
FindHandle = FindFirstFileW(pSrcCopy, &FindData);
|
|
*pLast = wch;
|
|
if (FindHandle != INVALID_HANDLE_VALUE){
|
|
FindClose(FindHandle);
|
|
// if no long name, copy the original name
|
|
// starts with pFirst(included) and ends with pLast(excluded)
|
|
if (!(Length = wcslen(FindData.cFileName)))
|
|
Length = (ULONG)(pLast - pFirst);
|
|
else
|
|
pFirst = FindData.cFileName;
|
|
ReturnLen += Length;
|
|
if (cchBuffer > ReturnLen && ARGUMENT_PRESENT(lpszLongPath))
|
|
{
|
|
RtlMoveMemory(pDst, pFirst, Length * sizeof(WCHAR));
|
|
pDst += Length;
|
|
}
|
|
}
|
|
else {
|
|
// invalid path, reset the length, mark the error and
|
|
// bail out of the loop. We will be copying the source
|
|
// to destination later.
|
|
//
|
|
ReturnLen = 0;
|
|
break;
|
|
}
|
|
pSrc = pLast;
|
|
if (*pSrc == UNICODE_NULL)
|
|
break;
|
|
} while (FindLFNorSFN_U(pSrc, &pFirst, &pLast, FALSE));
|
|
|
|
if (ReturnLen) {
|
|
//copy the rest of the path from pSrc. This may only contain
|
|
//a single NULL char
|
|
Length = wcslen(pSrc);
|
|
ReturnLen += Length;
|
|
if (cchBuffer > ReturnLen && ARGUMENT_PRESENT(lpszLongPath))
|
|
{
|
|
RtlMoveMemory(pDst, pSrc, (Length + 1) * sizeof(WCHAR));
|
|
if (Buffer)
|
|
RtlMoveMemory(lpszLongPath, Buffer, (ReturnLen + 1) * sizeof(WCHAR));
|
|
}
|
|
else
|
|
ReturnLen++;
|
|
}
|
|
|
|
glnTryExit:
|
|
;
|
|
}
|
|
finally {
|
|
if (pSrcCopy)
|
|
RtlFreeHeap(RtlProcessHeap(), 0, pSrcCopy);
|
|
if (Buffer)
|
|
RtlFreeHeap(RtlProcessHeap(), 0, Buffer);
|
|
}
|
|
|
|
// restore error mode.
|
|
SetErrorMode(PrevErrorMode);
|
|
return ReturnLen;
|
|
}
|
|
|
|
/**
|
|
Search for SFN(Short File Name) or LFN(Long File Name) in the
|
|
given path depends on FindLFN.
|
|
|
|
Input: LPWSTR Path
|
|
The given path name. Does not have to be fully qualified.
|
|
However, path type separaters are not allowed.
|
|
LPWSTR* ppFirst
|
|
To return the pointer points to the first char
|
|
of the name found.
|
|
LPWSTR* ppLast
|
|
To return the pointer points the char right after
|
|
the last char of the name found.
|
|
BOOL FindLFN
|
|
TRUE to search for LFN, otherwise, search for SFN
|
|
|
|
Output:
|
|
TRUE
|
|
if the target file name type is found, ppFirst and
|
|
ppLast are filled with pointers.
|
|
FALSE
|
|
if the target file name type not found.
|
|
|
|
Remark: "\\." and "\\.." are special cases. When encountered, they
|
|
are ignored and the function continue to search
|
|
|
|
|
|
**/
|
|
BOOL
|
|
FindLFNorSFN_U(
|
|
LPWSTR Path,
|
|
LPWSTR* ppFirst,
|
|
LPWSTR* ppLast,
|
|
BOOL FindLFN
|
|
)
|
|
{
|
|
LPWSTR pFirst, pLast;
|
|
BOOL TargetFound;
|
|
|
|
ASSERT(Path);
|
|
|
|
pFirst = Path;
|
|
|
|
TargetFound = FALSE;
|
|
|
|
while(TRUE) {
|
|
//skip over leading path separator
|
|
// it is legal to have multiple path separators in between
|
|
// name such as "foobar\\\\\\multiplepathchar"
|
|
while (*pFirst != UNICODE_NULL && (*pFirst == L'\\' || *pFirst == L'/'))
|
|
pFirst++;
|
|
if (*pFirst == UNICODE_NULL)
|
|
break;
|
|
pLast = pFirst + 1;
|
|
while (*pLast != UNICODE_NULL && *pLast != L'\\' && *pLast != L'/')
|
|
pLast++;
|
|
if (FindLFN)
|
|
TargetFound = !IsShortName_U(pFirst, (int)(pLast - pFirst));
|
|
else
|
|
TargetFound = !IsLongName_U(pFirst, (int)(pLast - pFirst));
|
|
if (TargetFound) {
|
|
if(ppFirst && ppLast) {
|
|
*ppFirst = pFirst;
|
|
// pLast point to the last char of the path/file name
|
|
*ppLast = pLast;
|
|
}
|
|
break;
|
|
}
|
|
if (*pLast == UNICODE_NULL)
|
|
break;
|
|
pFirst = pLast + 1;
|
|
}
|
|
return TargetFound;
|
|
}
|
|
LPCWSTR
|
|
SkipPathTypeIndicator_U(
|
|
LPCWSTR Path
|
|
)
|
|
{
|
|
RTL_PATH_TYPE RtlPathType;
|
|
LPCWSTR pFirst;
|
|
DWORD Count;
|
|
|
|
RtlPathType = RtlDetermineDosPathNameType_U(Path);
|
|
switch (RtlPathType) {
|
|
// form: "\\server_name\share_name\rest_of_the_path"
|
|
case RtlPathTypeUncAbsolute:
|
|
case RtlPathTypeLocalDevice:
|
|
pFirst = Path + 2;
|
|
Count = 2;
|
|
// guard for UNICODE_NULL is necessary because
|
|
// RtlDetermineDosPathNameType_U doesn't really
|
|
// verify an UNC name.
|
|
while (Count && *pFirst != UNICODE_NULL) {
|
|
if (*pFirst == L'\\' || *pFirst == L'/')
|
|
Count--;
|
|
pFirst++;
|
|
}
|
|
break;
|
|
|
|
// form: "\\."
|
|
case RtlPathTypeRootLocalDevice:
|
|
pFirst = NULL;
|
|
break;
|
|
|
|
// form: "D:\rest_of_the_path"
|
|
case RtlPathTypeDriveAbsolute:
|
|
pFirst = Path + 3;
|
|
break;
|
|
|
|
// form: "D:rest_of_the_path"
|
|
case RtlPathTypeDriveRelative:
|
|
pFirst = Path + 2;
|
|
break;
|
|
|
|
// form: "\rest_of_the_path"
|
|
case RtlPathTypeRooted:
|
|
pFirst = Path + 1;
|
|
break;
|
|
|
|
// form: "rest_of_the_path"
|
|
case RtlPathTypeRelative:
|
|
pFirst = Path;
|
|
break;
|
|
|
|
default:
|
|
pFirst = NULL;
|
|
break;
|
|
}
|
|
return pFirst;
|
|
}
|
|
|
|
/**
|
|
This function determines if the given name is a valid short name.
|
|
This function only does "obvious" testing since there are not precise
|
|
ways to cover all the file systems(each file system has its own
|
|
file name domain(for example, FAT allows all extended chars and space char
|
|
while NTFS **may** not).
|
|
The main purpose is to help the caller decide if a long to short name
|
|
conversion is necessary. When in doubt, this function simply tells the
|
|
caller that the given name is NOT a short name so that caller would
|
|
do whatever it takes to convert the name.
|
|
This function applies strict rules in deciding if the given name
|
|
is a valid short name. For example, a name containing any extended chars
|
|
is treated as invalid; a name with embedded space chars is also treated
|
|
as invalid.
|
|
A name is a valid short name if ALL the following conditions are met:
|
|
(1). total length <= 13.
|
|
(2). 0 < base name length <= 8.
|
|
(3). extention name length <= 3.
|
|
(4). only one '.' is allowed and must not be the first char.
|
|
(5). every char must be legal defined by the IllegalMask array.
|
|
|
|
null path, "." and ".." are treated valid.
|
|
|
|
Input: LPCWSTR Name - points to the name to be checked. It does not
|
|
have to be NULL terminated.
|
|
|
|
int Length - Length of the name, not including teminated NULL char.
|
|
|
|
output: TRUE - if the given name is a short file name.
|
|
FALSE - if the given name is not a short file name
|
|
**/
|
|
|
|
// bit set -> char is illegal
|
|
DWORD IllegalMask[] =
|
|
|
|
{
|
|
// code 0x00 - 0x1F --> all illegal
|
|
0xFFFFFFFF,
|
|
// code 0x20 - 0x3f --> 0x20,0x22,0x2A-0x2C,0x2F and 0x3A-0x3F are illegal
|
|
0xFC009C05,
|
|
// code 0x40 - 0x5F --> 0x5B-0x5D are illegal
|
|
0x38000000,
|
|
// code 0x60 - 0x7F --> 0x7C is illegal
|
|
0x10000000
|
|
};
|
|
|
|
BOOL
|
|
IsShortName_U(
|
|
LPCWSTR Name,
|
|
int Length
|
|
)
|
|
{
|
|
int Index;
|
|
BOOL ExtensionFound;
|
|
DWORD dwStatus;
|
|
UNICODE_STRING UnicodeName;
|
|
ANSI_STRING AnsiString;
|
|
UCHAR AnsiBuffer[MAX_PATH];
|
|
UCHAR Char;
|
|
|
|
ASSERT(Name);
|
|
|
|
// total length must less than 13(8.3 = 8 + 1 + 3 = 12)
|
|
if (Length > 12)
|
|
return FALSE;
|
|
// "" or "." or ".."
|
|
if (!Length)
|
|
return TRUE;
|
|
if (L'.' == *Name)
|
|
{
|
|
// "." or ".."
|
|
if (1 == Length || (2 == Length && L'.' == Name[1]))
|
|
return TRUE;
|
|
else
|
|
// '.' can not be the first char(base name length is 0)
|
|
return FALSE;
|
|
}
|
|
|
|
UnicodeName.Buffer = (LPWSTR)Name;
|
|
UnicodeName.Length =
|
|
UnicodeName.MaximumLength = (USHORT)(Length * sizeof(WCHAR));
|
|
|
|
AnsiString.Buffer = AnsiBuffer;
|
|
AnsiString.Length = 0;
|
|
AnsiString.MaximumLength = MAX_PATH; // make a dangerous assumption
|
|
|
|
dwStatus = BasepUnicodeStringTo8BitString(&AnsiString,
|
|
&UnicodeName,
|
|
FALSE);
|
|
if (! NT_SUCCESS(dwStatus)) {
|
|
return(FALSE);
|
|
}
|
|
|
|
// all trivial cases are tested, now we have to walk through the name
|
|
ExtensionFound = FALSE;
|
|
for (Index = 0; Index < AnsiString.Length; Index++)
|
|
{
|
|
Char = AnsiString.Buffer[Index];
|
|
|
|
// Skip over and Dbcs characters
|
|
if (IsDBCSLeadByte(Char)) {
|
|
//
|
|
// 1) if we're looking at base part ( !ExtensionPresent ) and the 8th byte
|
|
// is in the dbcs leading byte range, it's error ( Index == 7 ). If the
|
|
// length of base part is more than 8 ( Index > 7 ), it's definitely error.
|
|
//
|
|
// 2) if the last byte ( Index == DbcsName.Length - 1 ) is in the dbcs leading
|
|
// byte range, it's error
|
|
//
|
|
if ((!ExtensionFound && (Index >= 7)) ||
|
|
(Index == AnsiString.Length - 1)) {
|
|
return FALSE;
|
|
}
|
|
Index += 1;
|
|
continue;
|
|
}
|
|
|
|
// make sure the char is legal
|
|
if (Char > 0x7F || IllegalMask[Char / 32] & (1 << (Char % 32)))
|
|
return FALSE;
|
|
|
|
if ('.' == Char)
|
|
{
|
|
// (1) can have only one '.'
|
|
// (2) can not have more than 3 chars following.
|
|
if (ExtensionFound || Length - (Index + 1) > 3)
|
|
{
|
|
return FALSE;
|
|
}
|
|
ExtensionFound = TRUE;
|
|
}
|
|
// base length > 8 chars
|
|
if (Index >= 8 && !ExtensionFound)
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
/**
|
|
This function determines if the given name is a valid long name.
|
|
This function only does "obvious" testing since there are not precise
|
|
ways to cover all the file systems(each file system has its own
|
|
file name domain(for example, FAT allows all extended chars and space char
|
|
while NTFS **may** not)
|
|
This function helps the caller to determine if a short to long name
|
|
conversion is necessary. When in doubt, this function simply tells the
|
|
caller that the given name is NOT a long name so that caller would
|
|
do whatever it takes to convert the name.
|
|
A name is a valid long name if one of the following conditions is met:
|
|
(1). total length >= 13.
|
|
(2). 0 == base name length || base name length > 8.
|
|
(3). extention name length > 3.
|
|
(4). '.' is the first char.
|
|
(5). muitlple '.'
|
|
|
|
|
|
null path, "." and ".." are treat as valid long name.
|
|
|
|
Input: LPCWSTR Name - points to the name to be checked. It does not
|
|
have to be NULL terminated.
|
|
|
|
int Length - Length of the name, not including teminated NULL char.
|
|
|
|
output: TRUE - if the given name is a long file name.
|
|
FALSE - if the given name is not a long file name
|
|
**/
|
|
|
|
|
|
BOOL
|
|
IsLongName_U(
|
|
LPCWSTR Name,
|
|
int Length
|
|
)
|
|
{
|
|
int Index;
|
|
BOOL ExtensionFound;
|
|
// (1) NULL path
|
|
// (2) total length > 12
|
|
// (3) . is the first char (cover "." and "..")
|
|
if (!Length || Length > 12 || L'.' == *Name)
|
|
return TRUE;
|
|
ExtensionFound = FALSE;
|
|
for (Index = 0; Index < Length; Index++)
|
|
{
|
|
if (L'.' == Name[Index])
|
|
{
|
|
// multiple . or extension longer than 3
|
|
if (ExtensionFound || Length - (Index + 1) > 3)
|
|
return TRUE;
|
|
ExtensionFound = TRUE;
|
|
}
|
|
// base length longer than 8
|
|
if (Index >= 8 && !ExtensionFound)
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|