windows-nt/Source/XPSP1/NT/base/mvdm/dos/command/cmdexec.c
2020-09-26 16:20:57 +08:00

639 lines
18 KiB
C

/* cmdexec.c - Misc SCS routines for non-dos exec and re-entering
* the DOS.
*
*
* Modification History:
*
* Sudeepb 22-Apr-1992 Created
*/
#include "cmd.h"
#include <cmdsvc.h>
#include <softpc.h>
#include <mvdm.h>
#include <ctype.h>
#include <oemuni.h>
#include <wowcmpat.h>
//*****************************************************************************
// IsWowAppRunnable
//
// Returns FALSE if the WOW-specific compatibility flags for the specified
// task include the bit WOWCF_NOTDOSSPAWNABLE. This is done mostly for
// "dual mode" executables, e.g., Windows apps that have a real program
// as a DOS stub. Certain apps that are started via a DOS command shell,
// for example PWB, really do expect to be started as a DOS app, not a WOW
// app. For these apps, the compatibility bit should be set in the
// registry.
//
//*****************************************************************************
BOOL IsWowAppRunnable(LPSTR lpAppName)
{
BOOL Result = TRUE;
LONG lError;
HKEY hKey = 0;
char szModName[9];
char szHexAsciiFlags[12];
DWORD dwType = REG_SZ;
DWORD cbData = sizeof(szHexAsciiFlags);
ULONG ul = 0;
LPSTR pStrt, pEnd;
SHORT len;
lError = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
"Software\\Microsoft\\Windows NT\\CurrentVersion\\WOW\\Compatibility",
0,
KEY_QUERY_VALUE,
&hKey
);
if (ERROR_SUCCESS != lError) {
goto Cleanup;
}
//
// The following code strips the file name (<9 chars) out of a dos
// path name.
//
pStrt = strrchr (lpAppName, '\\');
if (pStrt==NULL)
pStrt = lpAppName;
else
pStrt++;
if ( (pEnd = strchr (pStrt, '.')) == NULL)
strncpy (szModName, pStrt, 9);
else {
len = (SHORT) (pEnd - pStrt);
if (len>8) goto Cleanup;
strncpy (szModName, pStrt, len);
szModName[len] = '\0';
}
//
// Look for the file name in the registry
//
lError = RegQueryValueEx( hKey,
szModName,
0,
&dwType,
szHexAsciiFlags,
&cbData
);
if (ERROR_SUCCESS != lError) {
goto Cleanup;
}
if (REG_SZ != dwType) {
goto Cleanup;
}
//
// Force the string to lowercase for the convenience of sscanf.
//
_strlwr(szHexAsciiFlags);
//
// sscanf() returns the number of fields converted.
//
if (1 != sscanf(szHexAsciiFlags, "0x%lx", &ul)) {
goto Cleanup;
}
if ((ul & WOWCF_NOTDOSSPAWNABLE) != 0)
Result = FALSE;
Cleanup:
if (hKey) {
RegCloseKey(hKey);
}
return Result;
}
/* cmdCheckBinary - check that the supplied binary name is a 32bit binary
*
*
* Entry - Client (DS:DX) - pointer to pathname for the executable to be tested
* Client (ES:BX) - pointer to parameter block
*
* EXIT - SUCCESS Client (CY) clear
* FAILURE Client (CY) set
* Client (AX) - error_not_enough_memory if command tail
* cannot accomodate /z
* - error_file_not_found if patname not found
*/
VOID cmdCheckBinary (VOID)
{
LPSTR lpAppName;
ULONG BinaryType;
PPARAMBLOCK lpParamBlock;
PCHAR lpCommandTail,lpTemp;
ULONG AppNameLen,CommandTailLen = 0;
USHORT CommandTailOff,CommandTailSeg,usTemp;
NTSTATUS Status;
UNICODE_STRING Unicode;
OEM_STRING OemString;
ANSI_STRING AnsiString;
if(DontCheckDosBinaryType){
setCF(0);
return; // DOS Exe
}
lpAppName = (LPSTR) GetVDMAddr (getDS(),getDX());
Unicode.Buffer = NULL;
AnsiString.Buffer = NULL;
RtlInitString((PSTRING)&OemString, lpAppName);
Status = RtlOemStringToUnicodeString(&Unicode,&OemString,TRUE);
if ( NT_SUCCESS(Status) ) {
Status = RtlUnicodeStringToAnsiString(&AnsiString, &Unicode, TRUE);
}
if ( !NT_SUCCESS(Status) ) {
Status = RtlNtStatusToDosError(Status);
}
else if (GetBinaryType (AnsiString.Buffer,(LPLONG)&BinaryType) == FALSE)
{
Status = GetLastError();
}
if (Unicode.Buffer != NULL) {
RtlFreeUnicodeString( &Unicode );
}
if (AnsiString.Buffer != NULL) {
RtlFreeAnsiString( &AnsiString);
}
if (Status){
setCF(1);
setAX((USHORT)Status);
return; // Invalid path
}
if (BinaryType == SCS_DOS_BINARY) {
setCF(0);
return; // DOS Exe
}
// Prevent certain WOW apps from being spawned by DOS exe's
// This is for win31 compatibility
else if (BinaryType == SCS_WOW_BINARY) {
if (!IsWowAppRunnable(lpAppName)) {
setCF(0);
return; // Run as DOS Exe
}
}
if (VDMForWOW && BinaryType == SCS_WOW_BINARY && IsFirstWOWCheckBinary) {
IsFirstWOWCheckBinary = FALSE;
setCF(0);
return; // Special Hack for krnl286.exe
}
// dont allow running 32bit binaries from autoexec.nt. Reason is that
// running non-dos binary requires that we should have read the actual
// command from GetNextVDMCommand. Otherwise the whole design gets into
// synchronization problems.
if (IsFirstCall) {
setCF(1);
setAX((USHORT)ERROR_FILE_NOT_FOUND);
return;
}
// Its a 32bit exe, replace the command with "command.com /z" and add the
// original binary name to command tail.
AppNameLen = strlen (lpAppName);
lpParamBlock = (PPARAMBLOCK) GetVDMAddr (getES(),getBX());
if (lpParamBlock) {
CommandTailOff = FETCHWORD(lpParamBlock->OffCmdTail);
CommandTailSeg = FETCHWORD(lpParamBlock->SegCmdTail);
lpCommandTail = (PCHAR) GetVDMAddr (CommandTailSeg,CommandTailOff);
if (lpCommandTail){
CommandTailLen = *(PCHAR)lpCommandTail;
lpCommandTail++; // point to the actual command tail
if (CommandTailLen)
CommandTailLen++; // For CR
}
// We are adding 3 below for "/z<space>" and anothre space between
// AppName and CommandTail.
if ((3 + AppNameLen + CommandTailLen ) > 128){
setCF(1);
setAX((USHORT)ERROR_NOT_ENOUGH_MEMORY);
return;
}
}
// copy the stub command.com name
strcpy ((PCHAR)&pSCSInfo->SCS_ComSpec,lpszComSpec+8);
lpTemp = (PCHAR) &pSCSInfo->SCS_ComSpec;
lpTemp = (PCHAR)((ULONG)lpTemp - (ULONG)GetVDMAddr(0,0));
usTemp = (USHORT)((ULONG)lpTemp >> 4);
setDS(usTemp);
usTemp = (USHORT)((ULONG)lpTemp & 0x0f);
setDX((usTemp));
// Form the command tail, first "3" is for "/z "
pSCSInfo->SCS_CmdTail [0] = (UCHAR)(3 +
AppNameLen +
CommandTailLen);
RtlCopyMemory ((PCHAR)&pSCSInfo->SCS_CmdTail[1],"/z ",3);
strcpy ((PCHAR)&pSCSInfo->SCS_CmdTail[4],lpAppName);
if (CommandTailLen) {
pSCSInfo->SCS_CmdTail[4+AppNameLen] = ' ';
RtlCopyMemory ((PCHAR)((ULONG)&pSCSInfo->SCS_CmdTail[4]+AppNameLen+1),
lpCommandTail,
CommandTailLen);
}
else {
pSCSInfo->SCS_CmdTail[4+AppNameLen] = 0xd;
}
// Set the parameter Block
if (lpParamBlock) {
STOREWORD(pSCSInfo->SCS_ParamBlock.SegEnv,lpParamBlock->SegEnv);
STOREDWORD(pSCSInfo->SCS_ParamBlock.pFCB1,lpParamBlock->pFCB1);
STOREDWORD(pSCSInfo->SCS_ParamBlock.pFCB2,lpParamBlock->pFCB2);
}
else {
STOREWORD(pSCSInfo->SCS_ParamBlock.SegEnv,0);
STOREDWORD(pSCSInfo->SCS_ParamBlock.pFCB1,0);
STOREDWORD(pSCSInfo->SCS_ParamBlock.pFCB2,0);
}
lpTemp = (PCHAR) &pSCSInfo->SCS_CmdTail;
lpTemp = (PCHAR)((ULONG)lpTemp - (ULONG)GetVDMAddr(0,0));
usTemp = (USHORT)((ULONG)lpTemp & 0x0f);
STOREWORD(pSCSInfo->SCS_ParamBlock.OffCmdTail,usTemp);
usTemp = (USHORT)((ULONG)lpTemp >> 4);
STOREWORD(pSCSInfo->SCS_ParamBlock.SegCmdTail,usTemp);
lpTemp = (PCHAR) &pSCSInfo->SCS_ParamBlock;
lpTemp = (PCHAR)((ULONG)lpTemp - (ULONG)GetVDMAddr(0,0));
usTemp = (USHORT)((ULONG)lpTemp >> 4);
setES (usTemp);
usTemp = (USHORT)((ULONG)lpTemp & 0x0f);
setBX (usTemp);
setCF(0);
return;
}
#define MAX_DIR 68
VOID cmdCreateProcess ( PSTD_HANDLES pStdHandles )
{
VDMINFO VDMInfoForCount;
STARTUPINFO StartupInfo;
PROCESS_INFORMATION ProcessInformation;
HANDLE hStd16In,hStd16Out,hStd16Err;
CHAR CurDirVar [] = "=?:";
CHAR Buffer [MAX_DIR];
CHAR *CurDir = Buffer;
DWORD dwRet;
BOOL Status;
NTSTATUS NtStatus;
UNICODE_STRING Unicode;
OEM_STRING OemString;
LPVOID lpNewEnv=NULL;
ANSI_STRING Env_A;
// we have one more 32 executable active
Exe32ActiveCount++;
// Increment the Re-enterancy count for the VDM
VDMInfoForCount.VDMState = INCREMENT_REENTER_COUNT;
GetNextVDMCommand (&VDMInfoForCount);
RtlZeroMemory((PVOID)&StartupInfo,sizeof(STARTUPINFO));
StartupInfo.cb = sizeof(STARTUPINFO);
CurDirVar [1] = chDefaultDrive;
dwRet = GetEnvironmentVariable (CurDirVar,Buffer,MAX_DIR);
if (dwRet == 0 || dwRet == MAX_DIR)
CurDir = NULL;
if ((hStd16In = (HANDLE) FETCHDWORD(pStdHandles->hStdIn)) != (HANDLE)-1)
SetStdHandle (STD_INPUT_HANDLE, hStd16In);
if ((hStd16Out = (HANDLE) FETCHDWORD(pStdHandles->hStdOut)) != (HANDLE)-1)
SetStdHandle (STD_OUTPUT_HANDLE, hStd16Out);
if ((hStd16Err = (HANDLE) FETCHDWORD(pStdHandles->hStdErr)) != (HANDLE)-1)
SetStdHandle (STD_ERROR_HANDLE, hStd16Err);
/*
* Warning, pEnv32 currently points to an ansi environment.
* The DOS is using an ANSI env which isn't quite correct.
* If the DOS is changed to use an OEM env then we will
* have to convert the env back to ansi before spawning
* non-dos exes ?!?
* 16-Jan-1993 Jonle
*/
Env_A.Buffer = NULL;
RtlInitString((PSTRING)&OemString, pCommand32);
NtStatus = RtlOemStringToUnicodeString(&Unicode,&OemString,TRUE);
if (NT_SUCCESS(NtStatus)) {
NtStatus = RtlUnicodeStringToAnsiString((PANSI_STRING)&OemString, &Unicode, FALSE);
RtlFreeUnicodeString( &Unicode );
}
if (!NT_SUCCESS(NtStatus)) {
SetLastError(RtlNtStatusToDosError(NtStatus));
Status = FALSE;
}
else {
if (pEnv32 != NULL && !cmdXformEnvironment (pEnv32, &Env_A)) {
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
Status = FALSE;
}
else {
Status = CreateProcess (
NULL,
(LPTSTR)pCommand32,
NULL,
NULL,
TRUE,
CREATE_SUSPENDED | CREATE_DEFAULT_ERROR_MODE,
Env_A.Buffer,
(LPTSTR)CurDir,
&StartupInfo,
&ProcessInformation);
}
}
if (Status == FALSE)
dwExitCode32 = GetLastError ();
if (hStd16In != (HANDLE)-1)
SetStdHandle (STD_INPUT_HANDLE, SCS_hStdIn);
if (hStd16Out != (HANDLE)-1)
SetStdHandle (STD_OUTPUT_HANDLE, SCS_hStdOut);
if (hStd16Err != (HANDLE)-1)
SetStdHandle (STD_ERROR_HANDLE, SCS_hStdErr);
if (Status) {
ResumeThread (ProcessInformation.hThread);
WaitForSingleObject(ProcessInformation.hProcess, (DWORD)-1);
GetExitCodeProcess (ProcessInformation.hProcess, &dwExitCode32);
CloseHandle (ProcessInformation.hProcess);
CloseHandle (ProcessInformation.hThread);
}
if (Env_A.Buffer)
RtlFreeAnsiString(&Env_A);
// Decrement the Re-enterancy count for the VDM
VDMInfoForCount.VDMState = DECREMENT_REENTER_COUNT;
GetNextVDMCommand (&VDMInfoForCount);
// one less 32 executable active
Exe32ActiveCount--;
// Kill this thread
ExitThread (0);
}
VOID cmdExec32 (PCHAR pCmd32, PCHAR pEnv)
{
DWORD dwThreadId;
HANDLE hThread;
PSTD_HANDLES pStdHandles;
pCommand32 = pCmd32;
pEnv32 = pEnv;
CntrlHandlerState = (CntrlHandlerState & ~CNTRL_SHELLCOUNT) |
(((WORD)(CntrlHandlerState & CNTRL_SHELLCOUNT))+1);
nt_block_event_thread(0);
fSoftpcRedirectionOnShellOut = fSoftpcRedirection;
fBlock = TRUE;
pStdHandles = (PSTD_HANDLES) GetVDMAddr (getSS(), getBP());
if((hThread = CreateThread (NULL,
0,
(LPTHREAD_START_ROUTINE)cmdCreateProcess,
pStdHandles,
0,
&dwThreadId)) == FALSE) {
setCF(0);
setAL((UCHAR)GetLastError());
nt_resume_event_thread();
nt_std_handle_notification(fSoftpcRedirectionOnShellOut);
fBlock = FALSE;
CntrlHandlerState = (CntrlHandlerState & ~CNTRL_SHELLCOUNT) |
(((WORD)(CntrlHandlerState & CNTRL_SHELLCOUNT))-1);
return;
}
else
CloseHandle (hThread);
// Wait for next command to be re-entered
RtlZeroMemory(&VDMInfo, sizeof(VDMINFO));
VDMInfo.VDMState = NO_PARENT_TO_WAKE | RETURN_ON_NO_COMMAND;
GetNextVDMCommand (&VDMInfo);
if (VDMInfo.CmdSize > 0){
setCF(1);
IsRepeatCall = TRUE;
}
else {
setCF(0);
setAL((UCHAR)dwExitCode32);
nt_resume_event_thread();
nt_std_handle_notification(fSoftpcRedirectionOnShellOut);
fBlock = FALSE;
}
CntrlHandlerState = (CntrlHandlerState & ~CNTRL_SHELLCOUNT) |
(((WORD)(CntrlHandlerState & CNTRL_SHELLCOUNT))-1);
return;
}
/* cmdExecComspec32 - Exec 32bit COMSPEC
*
*
* Entry - Client (ES) - environment segment
* Client (AL) - default drive
*
* EXIT - SUCCESS Client (CY) Clear - AL has return error code
* FAILURE Client (CY) set - means DOS is being re-entered
*/
VOID cmdExecComspec32 (VOID)
{
CHAR Buffer[MAX_PATH];
DWORD dwRet;
PCHAR pEnv;
dwRet = GetEnvironmentVariable ("COMSPEC",Buffer,MAX_PATH);
if (dwRet == 0 || dwRet >= MAX_PATH){
setCF(0);
setAL((UCHAR)ERROR_BAD_ENVIRONMENT);
return;
}
pEnv = (PCHAR) GetVDMAddr ((USHORT)getES(),0);
chDefaultDrive = (CHAR)(getAL() + 'A');
cmdExec32 (Buffer,pEnv);
return;
}
/* cmdExec - Exec a non-dos binary
*
*
* Entry - Client (DS:SI) - command to execute
* Client (AL) - default drive
* Client (ES) - environment segment
* Client (SS:BP) - Pointer to STD_HANDLES
* Client (AH) - if 1 means do "cmd /c command" else "command"
*
* EXIT - SUCCESS Client (CY) Clear - AL has return error code
* FAILURE Client (CY) set - means DOS is being re-entered
*/
VOID cmdExec (VOID)
{
DWORD i;
DWORD dwRet;
PCHAR pCommandTail;
PCHAR pEnv;
CHAR Buffer[MAX_PATH];
pCommandTail = (PCHAR) GetVDMAddr ((USHORT)getDS(),(USHORT)getSI());
pEnv = (PCHAR) GetVDMAddr ((USHORT)getES(),0);
for (i=0 ; i<124 ; i++) {
if (pCommandTail[i] == 0x0d){
pCommandTail[i] = 0;
break;
}
}
if (i == 124){
setCF(0);
setAL((UCHAR)ERROR_BAD_FORMAT);
return;
}
chDefaultDrive = (CHAR)(getAL() + 'A');
if (getAH() == 0) {
cmdExec32 (pCommandTail,pEnv);
}
else {
dwRet = GetEnvironmentVariable ("COMSPEC",Buffer,MAX_PATH);
if (dwRet == 0 || dwRet >= MAX_PATH){
setCF(0);
setAL((UCHAR)ERROR_BAD_ENVIRONMENT);
return;
}
if ((dwRet + 4 + strlen(pCommandTail)) > MAX_PATH) {
setCF(0);
setAL((UCHAR)ERROR_BAD_ENVIRONMENT);
return;
}
strcat (Buffer, " /c ");
strcat (Buffer, pCommandTail);
cmdExec32 (Buffer,pEnv);
}
return;
}
/* cmdReturnExitCode - command.com has run a dos binary and returing
* the exit code.
*
* Entry - Client (DX) - exit code
* Client (AL) - current drive
* Client (BX:CX) - RdrInfo address
*
* Exit
* Client Carry Set - Reenter i.e. a new DOS binary to execute.
* Client Carry Clear - This shelled out session is over.
*/
VOID cmdReturnExitCode (VOID)
{
VDMINFO VDMInfo;
PREDIRCOMPLETE_INFO pRdrInfo;
RtlZeroMemory(&VDMInfo, sizeof(VDMINFO));
VDMInfo.VDMState = RETURN_ON_NO_COMMAND;
VDMInfo.ErrorCode = (ULONG)getDX();
CntrlHandlerState = (CntrlHandlerState & ~CNTRL_SHELLCOUNT) |
(((WORD)(CntrlHandlerState & CNTRL_SHELLCOUNT))+1);
nt_block_event_thread(0);
fBlock = TRUE;
// a dos program just terminate, inherit its current directories
// and tell base too.
cmdUpdateCurrentDirectories((BYTE)getAL());
// Check for any copying needed for redirection
pRdrInfo = (PREDIRCOMPLETE_INFO) (((ULONG)getBX() << 16) + (ULONG)getCX());
if (!cmdCheckCopyForRedirection (pRdrInfo, FALSE))
VDMInfo.ErrorCode = ERROR_NOT_ENOUGH_MEMORY;
GetNextVDMCommand (&VDMInfo);
if (VDMInfo.CmdSize > 0){
setCF(1);
IsRepeatCall = TRUE;
}
else {
setCF(0);
setAL((UCHAR)dwExitCode32);
nt_resume_event_thread();
nt_std_handle_notification(fSoftpcRedirectionOnShellOut);
fBlock = FALSE;
}
CntrlHandlerState = (CntrlHandlerState & ~CNTRL_SHELLCOUNT) |
(((WORD)(CntrlHandlerState & CNTRL_SHELLCOUNT))-1);
return;
}