Source code of Windows XP (NT5)
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.

638 lines
18 KiB

  1. /* cmdexec.c - Misc SCS routines for non-dos exec and re-entering
  2. * the DOS.
  3. *
  4. *
  5. * Modification History:
  6. *
  7. * Sudeepb 22-Apr-1992 Created
  8. */
  9. #include "cmd.h"
  10. #include <cmdsvc.h>
  11. #include <softpc.h>
  12. #include <mvdm.h>
  13. #include <ctype.h>
  14. #include <oemuni.h>
  15. #include <wowcmpat.h>
  16. //*****************************************************************************
  17. // IsWowAppRunnable
  18. //
  19. // Returns FALSE if the WOW-specific compatibility flags for the specified
  20. // task include the bit WOWCF_NOTDOSSPAWNABLE. This is done mostly for
  21. // "dual mode" executables, e.g., Windows apps that have a real program
  22. // as a DOS stub. Certain apps that are started via a DOS command shell,
  23. // for example PWB, really do expect to be started as a DOS app, not a WOW
  24. // app. For these apps, the compatibility bit should be set in the
  25. // registry.
  26. //
  27. //*****************************************************************************
  28. BOOL IsWowAppRunnable(LPSTR lpAppName)
  29. {
  30. BOOL Result = TRUE;
  31. LONG lError;
  32. HKEY hKey = 0;
  33. char szModName[9];
  34. char szHexAsciiFlags[12];
  35. DWORD dwType = REG_SZ;
  36. DWORD cbData = sizeof(szHexAsciiFlags);
  37. ULONG ul = 0;
  38. LPSTR pStrt, pEnd;
  39. SHORT len;
  40. lError = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  41. "Software\\Microsoft\\Windows NT\\CurrentVersion\\WOW\\Compatibility",
  42. 0,
  43. KEY_QUERY_VALUE,
  44. &hKey
  45. );
  46. if (ERROR_SUCCESS != lError) {
  47. goto Cleanup;
  48. }
  49. //
  50. // The following code strips the file name (<9 chars) out of a dos
  51. // path name.
  52. //
  53. pStrt = strrchr (lpAppName, '\\');
  54. if (pStrt==NULL)
  55. pStrt = lpAppName;
  56. else
  57. pStrt++;
  58. if ( (pEnd = strchr (pStrt, '.')) == NULL)
  59. strncpy (szModName, pStrt, 9);
  60. else {
  61. len = (SHORT) (pEnd - pStrt);
  62. if (len>8) goto Cleanup;
  63. strncpy (szModName, pStrt, len);
  64. szModName[len] = '\0';
  65. }
  66. //
  67. // Look for the file name in the registry
  68. //
  69. lError = RegQueryValueEx( hKey,
  70. szModName,
  71. 0,
  72. &dwType,
  73. szHexAsciiFlags,
  74. &cbData
  75. );
  76. if (ERROR_SUCCESS != lError) {
  77. goto Cleanup;
  78. }
  79. if (REG_SZ != dwType) {
  80. goto Cleanup;
  81. }
  82. //
  83. // Force the string to lowercase for the convenience of sscanf.
  84. //
  85. _strlwr(szHexAsciiFlags);
  86. //
  87. // sscanf() returns the number of fields converted.
  88. //
  89. if (1 != sscanf(szHexAsciiFlags, "0x%lx", &ul)) {
  90. goto Cleanup;
  91. }
  92. if ((ul & WOWCF_NOTDOSSPAWNABLE) != 0)
  93. Result = FALSE;
  94. Cleanup:
  95. if (hKey) {
  96. RegCloseKey(hKey);
  97. }
  98. return Result;
  99. }
  100. /* cmdCheckBinary - check that the supplied binary name is a 32bit binary
  101. *
  102. *
  103. * Entry - Client (DS:DX) - pointer to pathname for the executable to be tested
  104. * Client (ES:BX) - pointer to parameter block
  105. *
  106. * EXIT - SUCCESS Client (CY) clear
  107. * FAILURE Client (CY) set
  108. * Client (AX) - error_not_enough_memory if command tail
  109. * cannot accomodate /z
  110. * - error_file_not_found if patname not found
  111. */
  112. VOID cmdCheckBinary (VOID)
  113. {
  114. LPSTR lpAppName;
  115. ULONG BinaryType;
  116. PPARAMBLOCK lpParamBlock;
  117. PCHAR lpCommandTail,lpTemp;
  118. ULONG AppNameLen,CommandTailLen = 0;
  119. USHORT CommandTailOff,CommandTailSeg,usTemp;
  120. NTSTATUS Status;
  121. UNICODE_STRING Unicode;
  122. OEM_STRING OemString;
  123. ANSI_STRING AnsiString;
  124. if(DontCheckDosBinaryType){
  125. setCF(0);
  126. return; // DOS Exe
  127. }
  128. lpAppName = (LPSTR) GetVDMAddr (getDS(),getDX());
  129. Unicode.Buffer = NULL;
  130. AnsiString.Buffer = NULL;
  131. RtlInitString((PSTRING)&OemString, lpAppName);
  132. Status = RtlOemStringToUnicodeString(&Unicode,&OemString,TRUE);
  133. if ( NT_SUCCESS(Status) ) {
  134. Status = RtlUnicodeStringToAnsiString(&AnsiString, &Unicode, TRUE);
  135. }
  136. if ( !NT_SUCCESS(Status) ) {
  137. Status = RtlNtStatusToDosError(Status);
  138. }
  139. else if (GetBinaryType (AnsiString.Buffer,(LPLONG)&BinaryType) == FALSE)
  140. {
  141. Status = GetLastError();
  142. }
  143. if (Unicode.Buffer != NULL) {
  144. RtlFreeUnicodeString( &Unicode );
  145. }
  146. if (AnsiString.Buffer != NULL) {
  147. RtlFreeAnsiString( &AnsiString);
  148. }
  149. if (Status){
  150. setCF(1);
  151. setAX((USHORT)Status);
  152. return; // Invalid path
  153. }
  154. if (BinaryType == SCS_DOS_BINARY) {
  155. setCF(0);
  156. return; // DOS Exe
  157. }
  158. // Prevent certain WOW apps from being spawned by DOS exe's
  159. // This is for win31 compatibility
  160. else if (BinaryType == SCS_WOW_BINARY) {
  161. if (!IsWowAppRunnable(lpAppName)) {
  162. setCF(0);
  163. return; // Run as DOS Exe
  164. }
  165. }
  166. if (VDMForWOW && BinaryType == SCS_WOW_BINARY && IsFirstWOWCheckBinary) {
  167. IsFirstWOWCheckBinary = FALSE;
  168. setCF(0);
  169. return; // Special Hack for krnl286.exe
  170. }
  171. // dont allow running 32bit binaries from autoexec.nt. Reason is that
  172. // running non-dos binary requires that we should have read the actual
  173. // command from GetNextVDMCommand. Otherwise the whole design gets into
  174. // synchronization problems.
  175. if (IsFirstCall) {
  176. setCF(1);
  177. setAX((USHORT)ERROR_FILE_NOT_FOUND);
  178. return;
  179. }
  180. // Its a 32bit exe, replace the command with "command.com /z" and add the
  181. // original binary name to command tail.
  182. AppNameLen = strlen (lpAppName);
  183. lpParamBlock = (PPARAMBLOCK) GetVDMAddr (getES(),getBX());
  184. if (lpParamBlock) {
  185. CommandTailOff = FETCHWORD(lpParamBlock->OffCmdTail);
  186. CommandTailSeg = FETCHWORD(lpParamBlock->SegCmdTail);
  187. lpCommandTail = (PCHAR) GetVDMAddr (CommandTailSeg,CommandTailOff);
  188. if (lpCommandTail){
  189. CommandTailLen = *(PCHAR)lpCommandTail;
  190. lpCommandTail++; // point to the actual command tail
  191. if (CommandTailLen)
  192. CommandTailLen++; // For CR
  193. }
  194. // We are adding 3 below for "/z<space>" and anothre space between
  195. // AppName and CommandTail.
  196. if ((3 + AppNameLen + CommandTailLen ) > 128){
  197. setCF(1);
  198. setAX((USHORT)ERROR_NOT_ENOUGH_MEMORY);
  199. return;
  200. }
  201. }
  202. // copy the stub command.com name
  203. strcpy ((PCHAR)&pSCSInfo->SCS_ComSpec,lpszComSpec+8);
  204. lpTemp = (PCHAR) &pSCSInfo->SCS_ComSpec;
  205. lpTemp = (PCHAR)((ULONG)lpTemp - (ULONG)GetVDMAddr(0,0));
  206. usTemp = (USHORT)((ULONG)lpTemp >> 4);
  207. setDS(usTemp);
  208. usTemp = (USHORT)((ULONG)lpTemp & 0x0f);
  209. setDX((usTemp));
  210. // Form the command tail, first "3" is for "/z "
  211. pSCSInfo->SCS_CmdTail [0] = (UCHAR)(3 +
  212. AppNameLen +
  213. CommandTailLen);
  214. RtlCopyMemory ((PCHAR)&pSCSInfo->SCS_CmdTail[1],"/z ",3);
  215. strcpy ((PCHAR)&pSCSInfo->SCS_CmdTail[4],lpAppName);
  216. if (CommandTailLen) {
  217. pSCSInfo->SCS_CmdTail[4+AppNameLen] = ' ';
  218. RtlCopyMemory ((PCHAR)((ULONG)&pSCSInfo->SCS_CmdTail[4]+AppNameLen+1),
  219. lpCommandTail,
  220. CommandTailLen);
  221. }
  222. else {
  223. pSCSInfo->SCS_CmdTail[4+AppNameLen] = 0xd;
  224. }
  225. // Set the parameter Block
  226. if (lpParamBlock) {
  227. STOREWORD(pSCSInfo->SCS_ParamBlock.SegEnv,lpParamBlock->SegEnv);
  228. STOREDWORD(pSCSInfo->SCS_ParamBlock.pFCB1,lpParamBlock->pFCB1);
  229. STOREDWORD(pSCSInfo->SCS_ParamBlock.pFCB2,lpParamBlock->pFCB2);
  230. }
  231. else {
  232. STOREWORD(pSCSInfo->SCS_ParamBlock.SegEnv,0);
  233. STOREDWORD(pSCSInfo->SCS_ParamBlock.pFCB1,0);
  234. STOREDWORD(pSCSInfo->SCS_ParamBlock.pFCB2,0);
  235. }
  236. lpTemp = (PCHAR) &pSCSInfo->SCS_CmdTail;
  237. lpTemp = (PCHAR)((ULONG)lpTemp - (ULONG)GetVDMAddr(0,0));
  238. usTemp = (USHORT)((ULONG)lpTemp & 0x0f);
  239. STOREWORD(pSCSInfo->SCS_ParamBlock.OffCmdTail,usTemp);
  240. usTemp = (USHORT)((ULONG)lpTemp >> 4);
  241. STOREWORD(pSCSInfo->SCS_ParamBlock.SegCmdTail,usTemp);
  242. lpTemp = (PCHAR) &pSCSInfo->SCS_ParamBlock;
  243. lpTemp = (PCHAR)((ULONG)lpTemp - (ULONG)GetVDMAddr(0,0));
  244. usTemp = (USHORT)((ULONG)lpTemp >> 4);
  245. setES (usTemp);
  246. usTemp = (USHORT)((ULONG)lpTemp & 0x0f);
  247. setBX (usTemp);
  248. setCF(0);
  249. return;
  250. }
  251. #define MAX_DIR 68
  252. VOID cmdCreateProcess ( PSTD_HANDLES pStdHandles )
  253. {
  254. VDMINFO VDMInfoForCount;
  255. STARTUPINFO StartupInfo;
  256. PROCESS_INFORMATION ProcessInformation;
  257. HANDLE hStd16In,hStd16Out,hStd16Err;
  258. CHAR CurDirVar [] = "=?:";
  259. CHAR Buffer [MAX_DIR];
  260. CHAR *CurDir = Buffer;
  261. DWORD dwRet;
  262. BOOL Status;
  263. NTSTATUS NtStatus;
  264. UNICODE_STRING Unicode;
  265. OEM_STRING OemString;
  266. LPVOID lpNewEnv=NULL;
  267. ANSI_STRING Env_A;
  268. // we have one more 32 executable active
  269. Exe32ActiveCount++;
  270. // Increment the Re-enterancy count for the VDM
  271. VDMInfoForCount.VDMState = INCREMENT_REENTER_COUNT;
  272. GetNextVDMCommand (&VDMInfoForCount);
  273. RtlZeroMemory((PVOID)&StartupInfo,sizeof(STARTUPINFO));
  274. StartupInfo.cb = sizeof(STARTUPINFO);
  275. CurDirVar [1] = chDefaultDrive;
  276. dwRet = GetEnvironmentVariable (CurDirVar,Buffer,MAX_DIR);
  277. if (dwRet == 0 || dwRet == MAX_DIR)
  278. CurDir = NULL;
  279. if ((hStd16In = (HANDLE) FETCHDWORD(pStdHandles->hStdIn)) != (HANDLE)-1)
  280. SetStdHandle (STD_INPUT_HANDLE, hStd16In);
  281. if ((hStd16Out = (HANDLE) FETCHDWORD(pStdHandles->hStdOut)) != (HANDLE)-1)
  282. SetStdHandle (STD_OUTPUT_HANDLE, hStd16Out);
  283. if ((hStd16Err = (HANDLE) FETCHDWORD(pStdHandles->hStdErr)) != (HANDLE)-1)
  284. SetStdHandle (STD_ERROR_HANDLE, hStd16Err);
  285. /*
  286. * Warning, pEnv32 currently points to an ansi environment.
  287. * The DOS is using an ANSI env which isn't quite correct.
  288. * If the DOS is changed to use an OEM env then we will
  289. * have to convert the env back to ansi before spawning
  290. * non-dos exes ?!?
  291. * 16-Jan-1993 Jonle
  292. */
  293. Env_A.Buffer = NULL;
  294. RtlInitString((PSTRING)&OemString, pCommand32);
  295. NtStatus = RtlOemStringToUnicodeString(&Unicode,&OemString,TRUE);
  296. if (NT_SUCCESS(NtStatus)) {
  297. NtStatus = RtlUnicodeStringToAnsiString((PANSI_STRING)&OemString, &Unicode, FALSE);
  298. RtlFreeUnicodeString( &Unicode );
  299. }
  300. if (!NT_SUCCESS(NtStatus)) {
  301. SetLastError(RtlNtStatusToDosError(NtStatus));
  302. Status = FALSE;
  303. }
  304. else {
  305. if (pEnv32 != NULL && !cmdXformEnvironment (pEnv32, &Env_A)) {
  306. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  307. Status = FALSE;
  308. }
  309. else {
  310. Status = CreateProcess (
  311. NULL,
  312. (LPTSTR)pCommand32,
  313. NULL,
  314. NULL,
  315. TRUE,
  316. CREATE_SUSPENDED | CREATE_DEFAULT_ERROR_MODE,
  317. Env_A.Buffer,
  318. (LPTSTR)CurDir,
  319. &StartupInfo,
  320. &ProcessInformation);
  321. }
  322. }
  323. if (Status == FALSE)
  324. dwExitCode32 = GetLastError ();
  325. if (hStd16In != (HANDLE)-1)
  326. SetStdHandle (STD_INPUT_HANDLE, SCS_hStdIn);
  327. if (hStd16Out != (HANDLE)-1)
  328. SetStdHandle (STD_OUTPUT_HANDLE, SCS_hStdOut);
  329. if (hStd16Err != (HANDLE)-1)
  330. SetStdHandle (STD_ERROR_HANDLE, SCS_hStdErr);
  331. if (Status) {
  332. ResumeThread (ProcessInformation.hThread);
  333. WaitForSingleObject(ProcessInformation.hProcess, (DWORD)-1);
  334. GetExitCodeProcess (ProcessInformation.hProcess, &dwExitCode32);
  335. CloseHandle (ProcessInformation.hProcess);
  336. CloseHandle (ProcessInformation.hThread);
  337. }
  338. if (Env_A.Buffer)
  339. RtlFreeAnsiString(&Env_A);
  340. // Decrement the Re-enterancy count for the VDM
  341. VDMInfoForCount.VDMState = DECREMENT_REENTER_COUNT;
  342. GetNextVDMCommand (&VDMInfoForCount);
  343. // one less 32 executable active
  344. Exe32ActiveCount--;
  345. // Kill this thread
  346. ExitThread (0);
  347. }
  348. VOID cmdExec32 (PCHAR pCmd32, PCHAR pEnv)
  349. {
  350. DWORD dwThreadId;
  351. HANDLE hThread;
  352. PSTD_HANDLES pStdHandles;
  353. pCommand32 = pCmd32;
  354. pEnv32 = pEnv;
  355. CntrlHandlerState = (CntrlHandlerState & ~CNTRL_SHELLCOUNT) |
  356. (((WORD)(CntrlHandlerState & CNTRL_SHELLCOUNT))+1);
  357. nt_block_event_thread(0);
  358. fSoftpcRedirectionOnShellOut = fSoftpcRedirection;
  359. fBlock = TRUE;
  360. pStdHandles = (PSTD_HANDLES) GetVDMAddr (getSS(), getBP());
  361. if((hThread = CreateThread (NULL,
  362. 0,
  363. (LPTHREAD_START_ROUTINE)cmdCreateProcess,
  364. pStdHandles,
  365. 0,
  366. &dwThreadId)) == FALSE) {
  367. setCF(0);
  368. setAL((UCHAR)GetLastError());
  369. nt_resume_event_thread();
  370. nt_std_handle_notification(fSoftpcRedirectionOnShellOut);
  371. fBlock = FALSE;
  372. CntrlHandlerState = (CntrlHandlerState & ~CNTRL_SHELLCOUNT) |
  373. (((WORD)(CntrlHandlerState & CNTRL_SHELLCOUNT))-1);
  374. return;
  375. }
  376. else
  377. CloseHandle (hThread);
  378. // Wait for next command to be re-entered
  379. RtlZeroMemory(&VDMInfo, sizeof(VDMINFO));
  380. VDMInfo.VDMState = NO_PARENT_TO_WAKE | RETURN_ON_NO_COMMAND;
  381. GetNextVDMCommand (&VDMInfo);
  382. if (VDMInfo.CmdSize > 0){
  383. setCF(1);
  384. IsRepeatCall = TRUE;
  385. }
  386. else {
  387. setCF(0);
  388. setAL((UCHAR)dwExitCode32);
  389. nt_resume_event_thread();
  390. nt_std_handle_notification(fSoftpcRedirectionOnShellOut);
  391. fBlock = FALSE;
  392. }
  393. CntrlHandlerState = (CntrlHandlerState & ~CNTRL_SHELLCOUNT) |
  394. (((WORD)(CntrlHandlerState & CNTRL_SHELLCOUNT))-1);
  395. return;
  396. }
  397. /* cmdExecComspec32 - Exec 32bit COMSPEC
  398. *
  399. *
  400. * Entry - Client (ES) - environment segment
  401. * Client (AL) - default drive
  402. *
  403. * EXIT - SUCCESS Client (CY) Clear - AL has return error code
  404. * FAILURE Client (CY) set - means DOS is being re-entered
  405. */
  406. VOID cmdExecComspec32 (VOID)
  407. {
  408. CHAR Buffer[MAX_PATH];
  409. DWORD dwRet;
  410. PCHAR pEnv;
  411. dwRet = GetEnvironmentVariable ("COMSPEC",Buffer,MAX_PATH);
  412. if (dwRet == 0 || dwRet >= MAX_PATH){
  413. setCF(0);
  414. setAL((UCHAR)ERROR_BAD_ENVIRONMENT);
  415. return;
  416. }
  417. pEnv = (PCHAR) GetVDMAddr ((USHORT)getES(),0);
  418. chDefaultDrive = (CHAR)(getAL() + 'A');
  419. cmdExec32 (Buffer,pEnv);
  420. return;
  421. }
  422. /* cmdExec - Exec a non-dos binary
  423. *
  424. *
  425. * Entry - Client (DS:SI) - command to execute
  426. * Client (AL) - default drive
  427. * Client (ES) - environment segment
  428. * Client (SS:BP) - Pointer to STD_HANDLES
  429. * Client (AH) - if 1 means do "cmd /c command" else "command"
  430. *
  431. * EXIT - SUCCESS Client (CY) Clear - AL has return error code
  432. * FAILURE Client (CY) set - means DOS is being re-entered
  433. */
  434. VOID cmdExec (VOID)
  435. {
  436. DWORD i;
  437. DWORD dwRet;
  438. PCHAR pCommandTail;
  439. PCHAR pEnv;
  440. CHAR Buffer[MAX_PATH];
  441. pCommandTail = (PCHAR) GetVDMAddr ((USHORT)getDS(),(USHORT)getSI());
  442. pEnv = (PCHAR) GetVDMAddr ((USHORT)getES(),0);
  443. for (i=0 ; i<124 ; i++) {
  444. if (pCommandTail[i] == 0x0d){
  445. pCommandTail[i] = 0;
  446. break;
  447. }
  448. }
  449. if (i == 124){
  450. setCF(0);
  451. setAL((UCHAR)ERROR_BAD_FORMAT);
  452. return;
  453. }
  454. chDefaultDrive = (CHAR)(getAL() + 'A');
  455. if (getAH() == 0) {
  456. cmdExec32 (pCommandTail,pEnv);
  457. }
  458. else {
  459. dwRet = GetEnvironmentVariable ("COMSPEC",Buffer,MAX_PATH);
  460. if (dwRet == 0 || dwRet >= MAX_PATH){
  461. setCF(0);
  462. setAL((UCHAR)ERROR_BAD_ENVIRONMENT);
  463. return;
  464. }
  465. if ((dwRet + 4 + strlen(pCommandTail)) > MAX_PATH) {
  466. setCF(0);
  467. setAL((UCHAR)ERROR_BAD_ENVIRONMENT);
  468. return;
  469. }
  470. strcat (Buffer, " /c ");
  471. strcat (Buffer, pCommandTail);
  472. cmdExec32 (Buffer,pEnv);
  473. }
  474. return;
  475. }
  476. /* cmdReturnExitCode - command.com has run a dos binary and returing
  477. * the exit code.
  478. *
  479. * Entry - Client (DX) - exit code
  480. * Client (AL) - current drive
  481. * Client (BX:CX) - RdrInfo address
  482. *
  483. * Exit
  484. * Client Carry Set - Reenter i.e. a new DOS binary to execute.
  485. * Client Carry Clear - This shelled out session is over.
  486. */
  487. VOID cmdReturnExitCode (VOID)
  488. {
  489. VDMINFO VDMInfo;
  490. PREDIRCOMPLETE_INFO pRdrInfo;
  491. RtlZeroMemory(&VDMInfo, sizeof(VDMINFO));
  492. VDMInfo.VDMState = RETURN_ON_NO_COMMAND;
  493. VDMInfo.ErrorCode = (ULONG)getDX();
  494. CntrlHandlerState = (CntrlHandlerState & ~CNTRL_SHELLCOUNT) |
  495. (((WORD)(CntrlHandlerState & CNTRL_SHELLCOUNT))+1);
  496. nt_block_event_thread(0);
  497. fBlock = TRUE;
  498. // a dos program just terminate, inherit its current directories
  499. // and tell base too.
  500. cmdUpdateCurrentDirectories((BYTE)getAL());
  501. // Check for any copying needed for redirection
  502. pRdrInfo = (PREDIRCOMPLETE_INFO) (((ULONG)getBX() << 16) + (ULONG)getCX());
  503. if (!cmdCheckCopyForRedirection (pRdrInfo, FALSE))
  504. VDMInfo.ErrorCode = ERROR_NOT_ENOUGH_MEMORY;
  505. GetNextVDMCommand (&VDMInfo);
  506. if (VDMInfo.CmdSize > 0){
  507. setCF(1);
  508. IsRepeatCall = TRUE;
  509. }
  510. else {
  511. setCF(0);
  512. setAL((UCHAR)dwExitCode32);
  513. nt_resume_event_thread();
  514. nt_std_handle_notification(fSoftpcRedirectionOnShellOut);
  515. fBlock = FALSE;
  516. }
  517. CntrlHandlerState = (CntrlHandlerState & ~CNTRL_SHELLCOUNT) |
  518. (((WORD)(CntrlHandlerState & CNTRL_SHELLCOUNT))-1);
  519. return;
  520. }