[KERNEL32]
[reactos.git] / dll / win32 / kernel32 / client / vdm.c
1 /*
2 * PROJECT: ReactOS Win32 Base API
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: dll/win32/kernel32/client/vdm.c
5 * PURPOSE: Virtual DOS Machines (VDM) Support
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #include <k32.h>
12
13 #define NDEBUG
14 #include <debug.h>
15
16 /* TYPES **********************************************************************/
17
18 typedef struct _ENV_INFO
19 {
20 ULONG NameType;
21 ULONG NameLength;
22 PWCHAR Name;
23 } ENV_INFO, *PENV_INFO;
24
25 /* GLOBALS ********************************************************************/
26
27 ENV_INFO BasepEnvNameType[] =
28 {
29 {3, sizeof(L"PATH") , L"PATH" },
30 {2, sizeof(L"WINDIR") , L"WINDIR" },
31 {2, sizeof(L"SYSTEMROOT"), L"SYSTEMROOT"},
32 {3, sizeof(L"TEMP") , L"TEMP" },
33 {3, sizeof(L"TMP") , L"TMP" },
34 };
35
36 UNICODE_STRING BaseDotComSuffixName = RTL_CONSTANT_STRING(L".com");
37 UNICODE_STRING BaseDotPifSuffixName = RTL_CONSTANT_STRING(L".pif");
38 UNICODE_STRING BaseDotExeSuffixName = RTL_CONSTANT_STRING(L".exe");
39
40 /* FUNCTIONS ******************************************************************/
41
42 ULONG
43 WINAPI
44 BaseIsDosApplication(IN PUNICODE_STRING PathName,
45 IN NTSTATUS Status)
46 {
47 UNICODE_STRING String;
48
49 /* Is it a .com? */
50 String.Length = BaseDotComSuffixName.Length;
51 String.Buffer = &PathName->Buffer[(PathName->Length - String.Length) / sizeof(WCHAR)];
52 if (RtlEqualUnicodeString(&String, &BaseDotComSuffixName, TRUE)) return BINARY_TYPE_COM;
53
54 /* Is it a .pif? */
55 String.Length = BaseDotPifSuffixName.Length;
56 String.Buffer = &PathName->Buffer[(PathName->Length - String.Length) / sizeof(WCHAR)];
57 if (RtlEqualUnicodeString(&String, &BaseDotPifSuffixName, TRUE)) return BINARY_TYPE_PIF;
58
59 /* Is it an exe? */
60 String.Length = BaseDotExeSuffixName.Length;
61 String.Buffer = &PathName->Buffer[(PathName->Length - String.Length) / sizeof(WCHAR)];
62 if (RtlEqualUnicodeString(&String, &BaseDotExeSuffixName, TRUE)) return BINARY_TYPE_EXE;
63
64 return 0;
65 }
66
67 NTSTATUS
68 WINAPI
69 BaseCheckVDM(IN ULONG BinaryType,
70 IN PCWCH ApplicationName,
71 IN PCWCH CommandLine,
72 IN PCWCH CurrentDirectory,
73 IN PANSI_STRING AnsiEnvironment,
74 IN PBASE_API_MESSAGE ApiMessage,
75 IN OUT PULONG iTask,
76 IN DWORD CreationFlags,
77 IN LPSTARTUPINFOW StartupInfo,
78 IN HANDLE hUserToken OPTIONAL)
79 {
80 NTSTATUS Status;
81 PBASE_CHECK_VDM CheckVdm = &ApiMessage->Data.CheckVDMRequest;
82 PCSR_CAPTURE_BUFFER CaptureBuffer;
83 PWCHAR CurrentDir = NULL;
84 PWCHAR ShortAppName = NULL;
85 PWCHAR ShortCurrentDir = NULL;
86 ULONG Length;
87 PCHAR AnsiCmdLine = NULL;
88 PCHAR AnsiAppName = NULL;
89 PCHAR AnsiCurDirectory = NULL;
90 PCHAR AnsiDesktop = NULL;
91 PCHAR AnsiTitle = NULL;
92 PCHAR AnsiReserved = NULL;
93 STARTUPINFOA AnsiStartupInfo;
94 ULONG NumStrings = 5;
95
96 if (CurrentDirectory == NULL)
97 {
98 /* Allocate memory for the current directory path */
99 Length = GetCurrentDirectoryW(0, NULL);
100 CurrentDir = (PWCHAR)RtlAllocateHeap(RtlGetProcessHeap(),
101 HEAP_ZERO_MEMORY,
102 Length * sizeof(WCHAR));
103 if (CurrentDir == NULL)
104 {
105 Status = STATUS_NO_MEMORY;
106 goto Cleanup;
107 }
108
109 /* Get the current directory */
110 GetCurrentDirectoryW(Length, CurrentDir);
111 CurrentDirectory = CurrentDir;
112 }
113
114 /* Calculate the size of the short application name */
115 Length = GetShortPathNameW(ApplicationName, NULL, 0);
116
117 /* Allocate memory for the short application name */
118 ShortAppName = (PWCHAR)RtlAllocateHeap(RtlGetProcessHeap(),
119 HEAP_ZERO_MEMORY,
120 Length * sizeof(WCHAR));
121 if (!ShortAppName)
122 {
123 Status = STATUS_NO_MEMORY;
124 goto Cleanup;
125 }
126
127 /* Get the short application name */
128 if (!GetShortPathNameW(ApplicationName, ShortAppName, Length))
129 {
130 /* Try to determine which error occurred */
131 switch (GetLastError())
132 {
133 case ERROR_NOT_ENOUGH_MEMORY:
134 {
135 Status = STATUS_NO_MEMORY;
136 break;
137 }
138
139 case ERROR_INVALID_PARAMETER:
140 {
141 Status = STATUS_INVALID_PARAMETER;
142 break;
143 }
144
145 default:
146 {
147 Status = STATUS_OBJECT_PATH_INVALID;
148 }
149 }
150
151 goto Cleanup;
152 }
153
154 /* Calculate the size of the short current directory path */
155 Length = GetShortPathNameW(CurrentDirectory, NULL, 0);
156
157 /* Allocate memory for the short current directory path */
158 ShortCurrentDir = (PWCHAR)RtlAllocateHeap(RtlGetProcessHeap(),
159 HEAP_ZERO_MEMORY,
160 Length * sizeof(WCHAR));
161 if (!ShortCurrentDir)
162 {
163 Status = STATUS_NO_MEMORY;
164 goto Cleanup;
165 }
166
167 /* Get the short current directory path */
168 if (!GetShortPathNameW(CurrentDirectory, ShortCurrentDir, Length))
169 {
170 /* Try to determine which error occurred */
171 switch (GetLastError())
172 {
173 case ERROR_NOT_ENOUGH_MEMORY:
174 {
175 Status = STATUS_NO_MEMORY;
176 break;
177 }
178
179 case ERROR_INVALID_PARAMETER:
180 {
181 Status = STATUS_INVALID_PARAMETER;
182 break;
183 }
184
185 default:
186 {
187 Status = STATUS_OBJECT_PATH_INVALID;
188 }
189 }
190 goto Cleanup;
191 }
192
193 /* Setup the input parameters */
194 CheckVdm->ConsoleHandle = NtCurrentPeb()->ProcessParameters->ConsoleHandle;
195 CheckVdm->BinaryType = BinaryType;
196 CheckVdm->CodePage = CP_ACP;
197 CheckVdm->dwCreationFlags = CreationFlags;
198 CheckVdm->CurDrive = CurrentDirectory[0] - L'A';
199 CheckVdm->CmdLen = wcslen(CommandLine) + 1;
200 CheckVdm->AppLen = wcslen(ShortAppName) + 1;
201 CheckVdm->PifLen = 0; // TODO: PIF file support!
202 CheckVdm->CurDirectoryLen = wcslen(ShortCurrentDir) + 1;
203 CheckVdm->EnvLen = AnsiEnvironment->Length;
204 CheckVdm->DesktopLen = (StartupInfo->lpDesktop != NULL) ? (wcslen(StartupInfo->lpDesktop) + 1) : 0;
205 CheckVdm->TitleLen = (StartupInfo->lpTitle != NULL) ? (wcslen(StartupInfo->lpTitle) + 1) : 0;
206 CheckVdm->ReservedLen = (StartupInfo->lpReserved != NULL) ? (wcslen(StartupInfo->lpReserved) + 1) : 0;
207
208 if (StartupInfo->dwFlags & STARTF_USESTDHANDLES)
209 {
210 /* Set the standard handles */
211 CheckVdm->StdIn = StartupInfo->hStdInput;
212 CheckVdm->StdOut = StartupInfo->hStdOutput;
213 CheckVdm->StdErr = StartupInfo->hStdError;
214 }
215
216 /* Allocate memory for the ANSI strings */
217 AnsiCmdLine = (PCHAR)RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, CheckVdm->CmdLen);
218 AnsiAppName = (PCHAR)RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, CheckVdm->AppLen);
219 AnsiCurDirectory = (PCHAR)RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, CheckVdm->CurDirectoryLen);
220 if (StartupInfo->lpDesktop) AnsiDesktop = (PCHAR)RtlAllocateHeap(RtlGetProcessHeap(),
221 HEAP_ZERO_MEMORY,
222 CheckVdm->DesktopLen);
223 if (StartupInfo->lpTitle) AnsiTitle = (PCHAR)RtlAllocateHeap(RtlGetProcessHeap(),
224 HEAP_ZERO_MEMORY,
225 CheckVdm->TitleLen);
226 if (StartupInfo->lpReserved) AnsiReserved = (PCHAR)RtlAllocateHeap(RtlGetProcessHeap(),
227 HEAP_ZERO_MEMORY,
228 CheckVdm->ReservedLen);
229
230 if (!AnsiCmdLine
231 || !AnsiAppName
232 || !AnsiCurDirectory
233 || (StartupInfo->lpDesktop && !AnsiDesktop)
234 || (StartupInfo->lpTitle && !AnsiTitle)
235 || (StartupInfo->lpReserved && !AnsiReserved))
236 {
237 Status = STATUS_NO_MEMORY;
238 goto Cleanup;
239 }
240
241 /* Convert the command line into an ANSI string */
242 WideCharToMultiByte(CP_ACP,
243 0,
244 CommandLine,
245 CheckVdm->CmdLen,
246 AnsiCmdLine,
247 CheckVdm->CmdLen,
248 NULL,
249 NULL);
250
251 /* Convert the short application name into an ANSI string */
252 WideCharToMultiByte(CP_ACP,
253 0,
254 ShortAppName,
255 CheckVdm->AppLen,
256 AnsiAppName,
257 CheckVdm->AppLen,
258 NULL,
259 NULL);
260
261 /* Convert the short current directory path into an ANSI string */
262 WideCharToMultiByte(CP_ACP,
263 0,
264 ShortCurrentDir,
265 CheckVdm->CurDirectoryLen,
266 AnsiCurDirectory,
267 CheckVdm->CurDirectoryLen,
268 NULL,
269 NULL);
270
271 if (StartupInfo->lpDesktop)
272 {
273 /* Convert the desktop name into an ANSI string */
274 WideCharToMultiByte(CP_ACP,
275 0,
276 StartupInfo->lpDesktop,
277 CheckVdm->DesktopLen,
278 AnsiDesktop,
279 CheckVdm->DesktopLen,
280 NULL,
281 NULL);
282 NumStrings++;
283 }
284
285 if (StartupInfo->lpTitle)
286 {
287 /* Convert the title into an ANSI string */
288 WideCharToMultiByte(CP_ACP,
289 0,
290 StartupInfo->lpTitle,
291 CheckVdm->TitleLen,
292 AnsiTitle,
293 CheckVdm->TitleLen,
294 NULL,
295 NULL);
296 NumStrings++;
297 }
298
299 if (StartupInfo->lpReserved)
300 {
301 /* Convert the reserved value into an ANSI string */
302 WideCharToMultiByte(CP_ACP,
303 0,
304 StartupInfo->lpReserved,
305 CheckVdm->ReservedLen,
306 AnsiReserved,
307 CheckVdm->ReservedLen,
308 NULL,
309 NULL);
310 NumStrings++;
311 }
312
313 /* Fill the ANSI startup info structure */
314 RtlCopyMemory(&AnsiStartupInfo, StartupInfo, sizeof(STARTUPINFO));
315 AnsiStartupInfo.lpReserved = AnsiReserved;
316 AnsiStartupInfo.lpDesktop = AnsiDesktop;
317 AnsiStartupInfo.lpTitle = AnsiTitle;
318
319 /* Allocate the capture buffer */
320 CaptureBuffer = CsrAllocateCaptureBuffer(NumStrings,
321 CheckVdm->CmdLen
322 + CheckVdm->AppLen
323 + CheckVdm->PifLen
324 + CheckVdm->CurDirectoryLen
325 + CheckVdm->DesktopLen
326 + CheckVdm->TitleLen
327 + CheckVdm->ReservedLen
328 + CheckVdm->EnvLen
329 + sizeof(STARTUPINFOA));
330 if (CaptureBuffer == NULL)
331 {
332 Status = STATUS_NO_MEMORY;
333 goto Cleanup;
334 }
335
336 /* Capture the command line */
337 CsrCaptureMessageBuffer(CaptureBuffer,
338 AnsiCmdLine,
339 CheckVdm->CmdLen,
340 (PVOID*)&CheckVdm->CmdLine);
341
342 /* Capture the application name */
343 CsrCaptureMessageBuffer(CaptureBuffer,
344 AnsiAppName,
345 CheckVdm->AppLen,
346 (PVOID*)&CheckVdm->AppName);
347
348 CheckVdm->PifFile = NULL; // TODO: PIF file support!
349
350 /* Capture the current directory */
351 CsrCaptureMessageBuffer(CaptureBuffer,
352 AnsiCurDirectory,
353 CheckVdm->CurDirectoryLen,
354 (PVOID*)&CheckVdm->CurDirectory);
355
356 /* Capture the environment */
357 CsrCaptureMessageBuffer(CaptureBuffer,
358 AnsiEnvironment->Buffer,
359 CheckVdm->EnvLen,
360 (PVOID*)&CheckVdm->Env);
361
362 /* Capture the startup info structure */
363 CsrCaptureMessageBuffer(CaptureBuffer,
364 &AnsiStartupInfo,
365 sizeof(STARTUPINFOA),
366 (PVOID*)&CheckVdm->StartupInfo);
367
368 if (StartupInfo->lpDesktop)
369 {
370 /* Capture the desktop name */
371 CsrCaptureMessageBuffer(CaptureBuffer,
372 AnsiDesktop,
373 CheckVdm->DesktopLen,
374 (PVOID*)&CheckVdm->Desktop);
375 }
376 else CheckVdm->Desktop = NULL;
377
378 if (StartupInfo->lpTitle)
379 {
380 /* Capture the title */
381 CsrCaptureMessageBuffer(CaptureBuffer,
382 AnsiTitle,
383 CheckVdm->TitleLen,
384 (PVOID*)&CheckVdm->Title);
385 }
386 else CheckVdm->Title = NULL;
387
388 if (StartupInfo->lpReserved)
389 {
390 /* Capture the reserved parameter */
391 CsrCaptureMessageBuffer(CaptureBuffer,
392 AnsiReserved,
393 CheckVdm->ReservedLen,
394 (PVOID*)&CheckVdm->Reserved);
395 }
396 else CheckVdm->Reserved = NULL;
397
398 /* Send the message to CSRSS */
399 Status = CsrClientCallServer((PCSR_API_MESSAGE)ApiMessage,
400 CaptureBuffer,
401 CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepCheckVDM),
402 sizeof(BASE_CHECK_VDM));
403
404 /* Write back the task ID */
405 *iTask = CheckVdm->iTask;
406
407 Cleanup:
408
409 /* Free the ANSI strings */
410 if (AnsiCmdLine) RtlFreeHeap(RtlGetProcessHeap(), 0, AnsiCmdLine);
411 if (AnsiAppName) RtlFreeHeap(RtlGetProcessHeap(), 0, AnsiAppName);
412 if (AnsiCurDirectory) RtlFreeHeap(RtlGetProcessHeap(), 0, AnsiCurDirectory);
413 if (AnsiDesktop) RtlFreeHeap(RtlGetProcessHeap(), 0, AnsiDesktop);
414 if (AnsiTitle) RtlFreeHeap(RtlGetProcessHeap(), 0, AnsiTitle);
415 if (AnsiReserved) RtlFreeHeap(RtlGetProcessHeap(), 0, AnsiReserved);
416
417 /* Free the capture buffer */
418 CsrFreeCaptureBuffer(CaptureBuffer);
419
420 /* Free the short paths */
421 if (ShortAppName) RtlFreeHeap(RtlGetProcessHeap(), 0, ShortAppName);
422 if (ShortCurrentDir) RtlFreeHeap(RtlGetProcessHeap(), 0, ShortCurrentDir);
423
424 /* Free the current directory, if it was allocated here */
425 if (CurrentDir) RtlFreeHeap(RtlGetProcessHeap(), 0, CurrentDir);
426
427 return Status;
428 }
429
430 BOOL
431 WINAPI
432 BaseUpdateVDMEntry(IN ULONG UpdateIndex,
433 IN OUT PHANDLE WaitHandle,
434 IN ULONG IndexInfo,
435 IN ULONG BinaryType)
436 {
437 NTSTATUS Status;
438 BASE_API_MESSAGE ApiMessage;
439 PBASE_UPDATE_VDM_ENTRY UpdateVdmEntry = &ApiMessage.Data.UpdateVDMEntryRequest;
440
441 /* Check what update is being sent */
442 switch (UpdateIndex)
443 {
444 /* VDM is being undone */
445 case VdmEntryUndo:
446 {
447 /* Tell the server how far we had gotten along */
448 UpdateVdmEntry->iTask = HandleToUlong(*WaitHandle);
449 UpdateVdmEntry->VDMCreationState = IndexInfo;
450 break;
451 }
452
453 /* VDM is ready with a new process handle */
454 case VdmEntryUpdateProcess:
455 {
456 /* Send it the process handle */
457 UpdateVdmEntry->VDMProcessHandle = *WaitHandle;
458 UpdateVdmEntry->iTask = IndexInfo;
459 break;
460 }
461 }
462
463 /* Also check what kind of binary this is for the console handle */
464 if (BinaryType == BINARY_TYPE_WOW)
465 {
466 /* Magic value for 16-bit apps */
467 UpdateVdmEntry->ConsoleHandle = (HANDLE)-1;
468 }
469 else if (UpdateVdmEntry->iTask)
470 {
471 /* No handle for true VDM */
472 UpdateVdmEntry->ConsoleHandle = NULL;
473 }
474 else
475 {
476 /* Otherwise, use the regular console handle */
477 UpdateVdmEntry->ConsoleHandle = NtCurrentPeb()->ProcessParameters->ConsoleHandle;
478 }
479
480 /* Finally write the index and binary type */
481 UpdateVdmEntry->EntryIndex = UpdateIndex;
482 UpdateVdmEntry->BinaryType = BinaryType;
483
484 /* Send the message to CSRSS */
485 Status = CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
486 NULL,
487 CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepUpdateVDMEntry),
488 sizeof(BASE_UPDATE_VDM_ENTRY));
489 if (!NT_SUCCESS(Status))
490 {
491 /* Handle failure */
492 BaseSetLastNTError(Status);
493 return FALSE;
494 }
495
496 /* If this was an update, CSRSS returns a new wait handle */
497 if (UpdateIndex == VdmEntryUpdateProcess)
498 {
499 /* Return it to the caller */
500 *WaitHandle = UpdateVdmEntry->WaitObjectForParent;
501 }
502
503 /* We made it */
504 return TRUE;
505 }
506
507 BOOL
508 WINAPI
509 BaseCheckForVDM(IN HANDLE ProcessHandle,
510 OUT LPDWORD ExitCode)
511 {
512 NTSTATUS Status;
513 EVENT_BASIC_INFORMATION EventBasicInfo;
514 BASE_API_MESSAGE ApiMessage;
515 PBASE_GET_VDM_EXIT_CODE GetVdmExitCode = &ApiMessage.Data.GetVDMExitCodeRequest;
516
517 /* It's VDM if the process is actually a wait handle (an event) */
518 Status = NtQueryEvent(ProcessHandle,
519 EventBasicInformation,
520 &EventBasicInfo,
521 sizeof(EventBasicInfo),
522 NULL);
523 if (!NT_SUCCESS(Status)) return FALSE;
524
525 /* Setup the input parameters */
526 GetVdmExitCode->ConsoleHandle = NtCurrentPeb()->ProcessParameters->ConsoleHandle;
527 GetVdmExitCode->hParent = ProcessHandle;
528
529 /* Call CSRSS */
530 Status = CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
531 NULL,
532 CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepGetVDMExitCode),
533 sizeof(BASE_GET_VDM_EXIT_CODE));
534 if (!NT_SUCCESS(Status)) return FALSE;
535
536 /* Get the exit code from the reply */
537 *ExitCode = GetVdmExitCode->ExitCode;
538 return TRUE;
539 }
540
541 BOOL
542 WINAPI
543 BaseGetVdmConfigInfo(IN LPCWSTR CommandLineReserved,
544 IN ULONG DosSeqId,
545 IN ULONG BinaryType,
546 IN PUNICODE_STRING CmdLineString,
547 OUT PULONG VdmSize)
548 {
549 WCHAR Buffer[MAX_PATH];
550 WCHAR CommandLine[MAX_PATH * 2];
551 ULONG Length;
552
553 /* Clear the buffer in case we fail */
554 CmdLineString->Buffer = 0;
555
556 /* Always return the same size: 16 Mb */
557 *VdmSize = 0x1000000;
558
559 /* Get the system directory */
560 Length = GetSystemDirectoryW(Buffer, MAX_PATH);
561 if (!(Length) || (Length >= MAX_PATH))
562 {
563 /* Eliminate no path or path too big */
564 SetLastError(ERROR_INVALID_NAME);
565 return FALSE;
566 }
567
568 /* Check if this is VDM with a DOS Sequence ID */
569 if (DosSeqId)
570 {
571 /*
572 * Build the VDM string for it:
573 * -i%lx : Gives the DOS Sequence ID;
574 * %s%c : Nothing if DOS VDM, -w if WoW VDM, -ws if separate WoW VDM.
575 */
576 _snwprintf(CommandLine,
577 sizeof(CommandLine),
578 L"\"%s\\ntvdm.exe\" -i%lx %s%c",
579 Buffer,
580 DosSeqId,
581 (BinaryType == BINARY_TYPE_DOS) ? L" " : L"-w",
582 (BinaryType == BINARY_TYPE_SEPARATE_WOW) ? L's' : L' ');
583 }
584 else
585 {
586 /*
587 * Build the string for it without the DOS Sequence ID:
588 * %s%c : Nothing if DOS VDM, -w if WoW VDM, -ws if separate WoW VDM.
589 */
590 _snwprintf(CommandLine,
591 sizeof(CommandLine),
592 L"\"%s\\ntvdm.exe\" %s%c",
593 Buffer,
594 (BinaryType == BINARY_TYPE_DOS) ? L" " : L"-w",
595 (BinaryType == BINARY_TYPE_SEPARATE_WOW) ? L's' : L' ');
596 }
597
598 /* Create the actual string */
599 return RtlCreateUnicodeString(CmdLineString, CommandLine);
600 }
601
602 UINT
603 WINAPI
604 BaseGetEnvNameType_U(IN PWCHAR Name,
605 IN ULONG NameLength)
606 {
607 PENV_INFO EnvInfo;
608 ULONG NameType, i;
609
610 /* Start by assuming unknown type */
611 NameType = 1;
612
613 /* Loop all the environment names */
614 for (i = 0; i < (sizeof(BasepEnvNameType) / sizeof(ENV_INFO)); i++)
615 {
616 /* Get this entry */
617 EnvInfo = &BasepEnvNameType[i];
618
619 /* Check if it matches the name */
620 if ((EnvInfo->NameLength == NameLength) &&
621 !(_wcsnicmp(EnvInfo->Name, Name, NameLength)))
622 {
623 /* It does, return the type */
624 NameType = EnvInfo->NameType;
625 break;
626 }
627 }
628
629 /* Return what we found, or unknown if nothing */
630 return NameType;
631 }
632
633 BOOL
634 NTAPI
635 BaseDestroyVDMEnvironment(IN PANSI_STRING AnsiEnv,
636 IN PUNICODE_STRING UnicodeEnv)
637 {
638 ULONG Dummy = 0;
639
640 /* Clear the ASCII buffer since Rtl creates this for us */
641 if (AnsiEnv->Buffer) RtlFreeAnsiString(AnsiEnv);
642
643 /* The Unicode buffer is build by hand, though */
644 if (UnicodeEnv->Buffer)
645 {
646 /* So clear it through the API */
647 NtFreeVirtualMemory(NtCurrentProcess(),
648 (PVOID*)&UnicodeEnv->Buffer,
649 &Dummy,
650 MEM_RELEASE);
651 }
652
653 /* All done */
654 return TRUE;
655 }
656
657 BOOL
658 NTAPI
659 BaseCreateVDMEnvironment(IN PWCHAR lpEnvironment,
660 IN PANSI_STRING AnsiEnv,
661 IN PUNICODE_STRING UnicodeEnv)
662 {
663 BOOL Result;
664 ULONG RegionSize, EnvironmentSize = 0;
665 PWCHAR p, Environment, NewEnvironment = NULL;
666 NTSTATUS Status;
667
668 /* Make sure we have both strings */
669 if (!(AnsiEnv) || !(UnicodeEnv))
670 {
671 /* Fail */
672 SetLastError(ERROR_INVALID_PARAMETER);
673 return FALSE;
674 }
675
676 /* Check if an environment was passed in */
677 if (!lpEnvironment)
678 {
679 /* Nope, create one */
680 Status = RtlCreateEnvironment(TRUE, (PWCHAR*)&Environment);
681 if (!NT_SUCCESS(Status)) goto Quickie;
682 }
683 else
684 {
685 /* Use the one we got */
686 Environment = lpEnvironment;
687 }
688
689 /* Do we have something now ? */
690 if (!Environment)
691 {
692 /* Still not, fail out */
693 SetLastError(ERROR_BAD_ENVIRONMENT);
694 goto Quickie;
695 }
696
697 /* Count how much space the whole environment takes */
698 p = Environment;
699 while ((*p++ != UNICODE_NULL) && (*p != UNICODE_NULL)) EnvironmentSize++;
700 EnvironmentSize += sizeof(UNICODE_NULL);
701
702 /* Allocate a new copy */
703 RegionSize = (EnvironmentSize + MAX_PATH) * sizeof(WCHAR);
704 if (!NT_SUCCESS(NtAllocateVirtualMemory(NtCurrentProcess(),
705 (PVOID*)&NewEnvironment,
706 0,
707 &RegionSize,
708 MEM_COMMIT,
709 PAGE_READWRITE)))
710 {
711 /* We failed, bail out */
712 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
713 NewEnvironment = NULL;
714 goto Quickie;
715 }
716
717 /* Begin parsing the new environment */
718 p = NewEnvironment;
719
720 /* FIXME: Code here */
721
722 /* Terminate it */
723 *p++ = UNICODE_NULL;
724
725 /* Initialize the unicode string to hold it */
726 EnvironmentSize = (p - NewEnvironment) * sizeof(WCHAR);
727 RtlInitEmptyUnicodeString(UnicodeEnv, NewEnvironment, (USHORT)EnvironmentSize);
728 UnicodeEnv->Length = (USHORT)EnvironmentSize;
729
730 /* Create the ASCII version of it */
731 Status = RtlUnicodeStringToAnsiString(AnsiEnv, UnicodeEnv, TRUE);
732 if (!NT_SUCCESS(Status))
733 {
734 /* Set last error if conversion failure */
735 BaseSetLastNTError(Status);
736 }
737 else
738 {
739 /* Everything went okay, so return success */
740 Result = TRUE;
741 NewEnvironment = NULL;
742 }
743
744 Quickie:
745 /* Cleanup path starts here, start by destroying the envrionment copy */
746 if (!(lpEnvironment) && (Environment)) RtlDestroyEnvironment(Environment);
747
748 /* See if we are here due to failure */
749 if (NewEnvironment)
750 {
751 /* Initialize the paths to be empty */
752 RtlInitEmptyUnicodeString(UnicodeEnv, NULL, 0);
753 RtlInitEmptyAnsiString(AnsiEnv, NULL, 0);
754
755 /* Free the environment copy */
756 RegionSize = 0;
757 Status = NtFreeVirtualMemory(NtCurrentProcess(),
758 (PVOID*)&NewEnvironment,
759 &RegionSize,
760 MEM_RELEASE);
761 ASSERT(NT_SUCCESS(Status));
762 }
763
764 /* Return the result */
765 return Result;
766 }
767
768
769 /* Check whether a file is an OS/2 or a very old Windows executable
770 * by testing on import of KERNEL.
771 *
772 * FIXME: is reading the module imports the only way of discerning
773 * old Windows binaries from OS/2 ones ? At least it seems so...
774 */
775 static DWORD WINAPI
776 InternalIsOS2OrOldWin(HANDLE hFile, IMAGE_DOS_HEADER *mz, IMAGE_OS2_HEADER *ne)
777 {
778 DWORD CurPos;
779 LPWORD modtab = NULL;
780 LPSTR nametab = NULL;
781 DWORD Read, Ret;
782 int i;
783
784 Ret = BINARY_OS216;
785 CurPos = SetFilePointer(hFile, 0, NULL, FILE_CURRENT);
786
787 /* read modref table */
788 if((SetFilePointer(hFile, mz->e_lfanew + ne->ne_modtab, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) ||
789 (!(modtab = HeapAlloc(GetProcessHeap(), 0, ne->ne_cmod * sizeof(WORD)))) ||
790 (!(ReadFile(hFile, modtab, ne->ne_cmod * sizeof(WORD), &Read, NULL))) ||
791 (Read != (DWORD)ne->ne_cmod * sizeof(WORD)))
792 {
793 goto broken;
794 }
795
796 /* read imported names table */
797 if((SetFilePointer(hFile, mz->e_lfanew + ne->ne_imptab, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) ||
798 (!(nametab = HeapAlloc(GetProcessHeap(), 0, ne->ne_enttab - ne->ne_imptab))) ||
799 (!(ReadFile(hFile, nametab, ne->ne_enttab - ne->ne_imptab, &Read, NULL))) ||
800 (Read != (DWORD)ne->ne_enttab - ne->ne_imptab))
801 {
802 goto broken;
803 }
804
805 for(i = 0; i < ne->ne_cmod; i++)
806 {
807 LPSTR module;
808 module = &nametab[modtab[i]];
809 if(!strncmp(&module[1], "KERNEL", module[0]))
810 {
811 /* very old windows file */
812 Ret = BINARY_WIN16;
813 goto done;
814 }
815 }
816
817 broken:
818 DPRINT1("InternalIsOS2OrOldWin(): Binary file seems to be broken\n");
819
820 done:
821 HeapFree(GetProcessHeap(), 0, modtab);
822 HeapFree(GetProcessHeap(), 0, nametab);
823 SetFilePointer(hFile, CurPos, NULL, FILE_BEGIN);
824 return Ret;
825 }
826
827 static DWORD WINAPI
828 InternalGetBinaryType(HANDLE hFile)
829 {
830 union
831 {
832 struct
833 {
834 unsigned char magic[4];
835 unsigned char ignored[12];
836 unsigned short type;
837 } elf;
838 struct
839 {
840 unsigned long magic;
841 unsigned long cputype;
842 unsigned long cpusubtype;
843 unsigned long filetype;
844 } macho;
845 IMAGE_DOS_HEADER mz;
846 } Header;
847 char magic[4];
848 DWORD Read;
849
850 if((SetFilePointer(hFile, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) ||
851 (!ReadFile(hFile, &Header, sizeof(Header), &Read, NULL) ||
852 (Read != sizeof(Header))))
853 {
854 return BINARY_UNKNOWN;
855 }
856
857 if(!memcmp(Header.elf.magic, "\177ELF", sizeof(Header.elf.magic)))
858 {
859 /* FIXME: we don't bother to check byte order, architecture, etc. */
860 switch(Header.elf.type)
861 {
862 case 2:
863 return BINARY_UNIX_EXE;
864 case 3:
865 return BINARY_UNIX_LIB;
866 }
867 return BINARY_UNKNOWN;
868 }
869
870 /* Mach-o File with Endian set to Big Endian or Little Endian*/
871 if(Header.macho.magic == 0xFEEDFACE ||
872 Header.macho.magic == 0xCEFAEDFE)
873 {
874 switch(Header.macho.filetype)
875 {
876 case 0x8:
877 /* MH_BUNDLE */
878 return BINARY_UNIX_LIB;
879 }
880 return BINARY_UNKNOWN;
881 }
882
883 /* Not ELF, try DOS */
884 if(Header.mz.e_magic == IMAGE_DOS_SIGNATURE)
885 {
886 /* We do have a DOS image so we will now try to seek into
887 * the file by the amount indicated by the field
888 * "Offset to extended header" and read in the
889 * "magic" field information at that location.
890 * This will tell us if there is more header information
891 * to read or not.
892 */
893 if((SetFilePointer(hFile, Header.mz.e_lfanew, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) ||
894 (!ReadFile(hFile, magic, sizeof(magic), &Read, NULL) ||
895 (Read != sizeof(magic))))
896 {
897 return BINARY_DOS;
898 }
899
900 /* Reading the magic field succeeded so
901 * we will try to determine what type it is.
902 */
903 if(!memcmp(magic, "PE\0\0", sizeof(magic)))
904 {
905 IMAGE_FILE_HEADER FileHeader;
906 if(!ReadFile(hFile, &FileHeader, sizeof(IMAGE_FILE_HEADER), &Read, NULL) ||
907 (Read != sizeof(IMAGE_FILE_HEADER)))
908 {
909 return BINARY_DOS;
910 }
911
912 /* FIXME - detect 32/64 bit */
913
914 if(FileHeader.Characteristics & IMAGE_FILE_DLL)
915 return BINARY_PE_DLL32;
916 return BINARY_PE_EXE32;
917 }
918
919 if(!memcmp(magic, "NE", 1))
920 {
921 /* This is a Windows executable (NE) header. This can
922 * mean either a 16-bit OS/2 or a 16-bit Windows or even a
923 * DOS program (running under a DOS extender). To decide
924 * which, we'll have to read the NE header.
925 */
926 IMAGE_OS2_HEADER ne;
927 if((SetFilePointer(hFile, Header.mz.e_lfanew, NULL, FILE_BEGIN) == 1) ||
928 !ReadFile(hFile, &ne, sizeof(IMAGE_OS2_HEADER), &Read, NULL) ||
929 (Read != sizeof(IMAGE_OS2_HEADER)))
930 {
931 /* Couldn't read header, so abort. */
932 return BINARY_DOS;
933 }
934
935 switch(ne.ne_exetyp)
936 {
937 case 2:
938 return BINARY_WIN16;
939 case 5:
940 return BINARY_DOS;
941 default:
942 return InternalIsOS2OrOldWin(hFile, &Header.mz, &ne);
943 }
944 }
945 return BINARY_DOS;
946 }
947 return BINARY_UNKNOWN;
948 }
949
950 /*
951 * @implemented
952 */
953 BOOL
954 WINAPI
955 GetBinaryTypeW (
956 LPCWSTR lpApplicationName,
957 LPDWORD lpBinaryType
958 )
959 {
960 HANDLE hFile;
961 DWORD BinType;
962
963 if(!lpApplicationName || !lpBinaryType)
964 {
965 SetLastError(ERROR_INVALID_PARAMETER);
966 return FALSE;
967 }
968
969 hFile = CreateFileW(lpApplicationName, GENERIC_READ, FILE_SHARE_READ, NULL,
970 OPEN_EXISTING, 0, 0);
971 if(hFile == INVALID_HANDLE_VALUE)
972 {
973 return FALSE;
974 }
975
976 BinType = InternalGetBinaryType(hFile);
977 CloseHandle(hFile);
978
979 switch(BinType)
980 {
981 case BINARY_UNKNOWN:
982 {
983 WCHAR *dot;
984
985 /*
986 * guess from filename
987 */
988 if(!(dot = wcsrchr(lpApplicationName, L'.')))
989 {
990 return FALSE;
991 }
992 if(!lstrcmpiW(dot, L".COM"))
993 {
994 *lpBinaryType = SCS_DOS_BINARY;
995 return TRUE;
996 }
997 if(!lstrcmpiW(dot, L".PIF"))
998 {
999 *lpBinaryType = SCS_PIF_BINARY;
1000 return TRUE;
1001 }
1002 return FALSE;
1003 }
1004 case BINARY_PE_EXE32:
1005 case BINARY_PE_DLL32:
1006 {
1007 *lpBinaryType = SCS_32BIT_BINARY;
1008 return TRUE;
1009 }
1010 case BINARY_PE_EXE64:
1011 case BINARY_PE_DLL64:
1012 {
1013 *lpBinaryType = SCS_64BIT_BINARY;
1014 return TRUE;
1015 }
1016 case BINARY_WIN16:
1017 {
1018 *lpBinaryType = SCS_WOW_BINARY;
1019 return TRUE;
1020 }
1021 case BINARY_OS216:
1022 {
1023 *lpBinaryType = SCS_OS216_BINARY;
1024 return TRUE;
1025 }
1026 case BINARY_DOS:
1027 {
1028 *lpBinaryType = SCS_DOS_BINARY;
1029 return TRUE;
1030 }
1031 case BINARY_UNIX_EXE:
1032 case BINARY_UNIX_LIB:
1033 {
1034 return FALSE;
1035 }
1036 }
1037
1038 DPRINT1("Invalid binary type %lu returned!\n", BinType);
1039 return FALSE;
1040 }
1041
1042 /*
1043 * @implemented
1044 */
1045 BOOL
1046 WINAPI
1047 GetBinaryTypeA(IN LPCSTR lpApplicationName,
1048 OUT LPDWORD lpBinaryType)
1049 {
1050 ANSI_STRING ApplicationNameString;
1051 UNICODE_STRING ApplicationNameW;
1052 BOOL StringAllocated = FALSE, Result;
1053 NTSTATUS Status;
1054
1055 RtlInitAnsiString(&ApplicationNameString, lpApplicationName);
1056
1057 if (ApplicationNameString.Length * sizeof(WCHAR) >= NtCurrentTeb()->StaticUnicodeString.MaximumLength)
1058 {
1059 StringAllocated = TRUE;
1060 Status = RtlAnsiStringToUnicodeString(&ApplicationNameW, &ApplicationNameString, TRUE);
1061 }
1062 else
1063 {
1064 Status = RtlAnsiStringToUnicodeString(&(NtCurrentTeb()->StaticUnicodeString), &ApplicationNameString, FALSE);
1065 }
1066
1067 if (!NT_SUCCESS(Status))
1068 {
1069 BaseSetLastNTError(Status);
1070 return FALSE;
1071 }
1072
1073 if (StringAllocated)
1074 {
1075 Result = GetBinaryTypeW(ApplicationNameW.Buffer, lpBinaryType);
1076 RtlFreeUnicodeString(&ApplicationNameW);
1077 }
1078 else
1079 {
1080 Result = GetBinaryTypeW(NtCurrentTeb()->StaticUnicodeString.Buffer, lpBinaryType);
1081 }
1082
1083 return Result;
1084 }
1085
1086 /*
1087 * @unimplemented
1088 */
1089 BOOL
1090 WINAPI
1091 CmdBatNotification (
1092 DWORD Unknown
1093 )
1094 {
1095 STUB;
1096 return FALSE;
1097 }
1098
1099 /*
1100 * @implemented
1101 */
1102 VOID
1103 WINAPI
1104 ExitVDM(BOOL IsWow, ULONG iWowTask)
1105 {
1106 BASE_API_MESSAGE ApiMessage;
1107 PBASE_EXIT_VDM ExitVdm = &ApiMessage.Data.ExitVDMRequest;
1108
1109 /* Setup the input parameters */
1110 ExitVdm->ConsoleHandle = NtCurrentPeb()->ProcessParameters->ConsoleHandle;
1111 ExitVdm->iWowTask = IsWow ? iWowTask : 0; /* Always zero for DOS tasks */
1112 ExitVdm->WaitObjectForVDM = NULL;
1113
1114 /* Call CSRSS */
1115 CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
1116 NULL,
1117 CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepExitVDM),
1118 sizeof(BASE_EXIT_VDM));
1119
1120 /* Close the returned wait object handle, if any */
1121 if (NT_SUCCESS(ApiMessage.Status) && (ExitVdm->WaitObjectForVDM != NULL))
1122 {
1123 CloseHandle(ExitVdm->WaitObjectForVDM);
1124 }
1125 }
1126
1127
1128 /*
1129 * @unimplemented
1130 */
1131 DWORD
1132 WINAPI
1133 GetNextVDMCommand(PGET_NEXT_VDM_COMMAND_DATA CommandData)
1134 {
1135 STUB;
1136 return 0;
1137 }
1138
1139
1140 /*
1141 * @implemented
1142 */
1143 DWORD
1144 WINAPI
1145 GetVDMCurrentDirectories(DWORD cchCurDirs, PCHAR lpszzCurDirs)
1146 {
1147 BASE_API_MESSAGE ApiMessage;
1148 PBASE_GETSET_VDM_CURDIRS VDMCurrentDirsRequest = &ApiMessage.Data.VDMCurrentDirsRequest;
1149 PCSR_CAPTURE_BUFFER CaptureBuffer;
1150
1151 /* Allocate the capture buffer */
1152 CaptureBuffer = CsrAllocateCaptureBuffer(1, cchCurDirs);
1153 if (CaptureBuffer == NULL)
1154 {
1155 BaseSetLastNTError(STATUS_NO_MEMORY);
1156 return 0;
1157 }
1158
1159 /* Setup the input parameters */
1160 VDMCurrentDirsRequest->cchCurDirs = cchCurDirs;
1161 CsrAllocateMessagePointer(CaptureBuffer,
1162 cchCurDirs,
1163 (PVOID*)&VDMCurrentDirsRequest->lpszzCurDirs);
1164
1165 /* Call CSRSS */
1166 CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
1167 CaptureBuffer,
1168 CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepGetVDMCurDirs),
1169 sizeof(BASE_GETSET_VDM_CURDIRS));
1170
1171 /* Set the last error */
1172 BaseSetLastNTError(ApiMessage.Status);
1173
1174 if (NT_SUCCESS(ApiMessage.Status))
1175 {
1176 /* Copy the result */
1177 RtlMoveMemory(lpszzCurDirs, VDMCurrentDirsRequest->lpszzCurDirs, cchCurDirs);
1178 }
1179
1180 /* Free the capture buffer */
1181 CsrFreeCaptureBuffer(CaptureBuffer);
1182
1183 /* Return the size if it was successful, or if the buffer was too small */
1184 return (NT_SUCCESS(ApiMessage.Status) || (ApiMessage.Status == STATUS_BUFFER_TOO_SMALL))
1185 ? VDMCurrentDirsRequest->cchCurDirs : 0;
1186 }
1187
1188
1189 /*
1190 * @unimplemented
1191 */
1192 BOOL
1193 WINAPI
1194 RegisterConsoleVDM (
1195 DWORD Unknown0,
1196 DWORD Unknown1,
1197 DWORD Unknown2,
1198 DWORD Unknown3,
1199 DWORD Unknown4,
1200 DWORD Unknown5,
1201 DWORD Unknown6,
1202 DWORD Unknown7,
1203 DWORD Unknown8,
1204 DWORD Unknown9,
1205 DWORD Unknown10
1206 )
1207 {
1208 STUB;
1209 return FALSE;
1210 }
1211
1212
1213 /*
1214 * @unimplemented
1215 */
1216 BOOL
1217 WINAPI
1218 RegisterWowBaseHandlers (
1219 DWORD Unknown0
1220 )
1221 {
1222 STUB;
1223 return FALSE;
1224 }
1225
1226
1227 /*
1228 * @unimplemented
1229 */
1230 BOOL
1231 WINAPI
1232 RegisterWowExec (
1233 DWORD Unknown0
1234 )
1235 {
1236 STUB;
1237 return FALSE;
1238 }
1239
1240
1241 /*
1242 * @implemented
1243 */
1244 BOOL
1245 WINAPI
1246 SetVDMCurrentDirectories(DWORD cchCurDirs, PCHAR lpszzCurDirs)
1247 {
1248 BASE_API_MESSAGE ApiMessage;
1249 PBASE_GETSET_VDM_CURDIRS VDMCurrentDirsRequest = &ApiMessage.Data.VDMCurrentDirsRequest;
1250 PCSR_CAPTURE_BUFFER CaptureBuffer;
1251
1252 /* Allocate the capture buffer */
1253 CaptureBuffer = CsrAllocateCaptureBuffer(1, cchCurDirs);
1254 if (CaptureBuffer == NULL)
1255 {
1256 BaseSetLastNTError(STATUS_NO_MEMORY);
1257 return FALSE;
1258 }
1259
1260 /* Setup the input parameters */
1261 VDMCurrentDirsRequest->cchCurDirs = cchCurDirs;
1262 CsrCaptureMessageBuffer(CaptureBuffer,
1263 lpszzCurDirs,
1264 cchCurDirs,
1265 (PVOID*)&VDMCurrentDirsRequest->lpszzCurDirs);
1266
1267 /* Call CSRSS */
1268 CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
1269 CaptureBuffer,
1270 CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepGetVDMCurDirs),
1271 sizeof(BASE_GETSET_VDM_CURDIRS));
1272
1273 /* Free the capture buffer */
1274 CsrFreeCaptureBuffer(CaptureBuffer);
1275
1276 /* Set the last error */
1277 BaseSetLastNTError(ApiMessage.Status);
1278
1279 return NT_SUCCESS(ApiMessage.Status) ? TRUE : FALSE;
1280 }
1281
1282 /*
1283 * @unimplemented
1284 */
1285 DWORD
1286 WINAPI
1287 VDMConsoleOperation (
1288 DWORD Unknown0,
1289 DWORD Unknown1
1290 )
1291 {
1292 STUB;
1293 return 0;
1294 }
1295
1296
1297 /*
1298 * @unimplemented
1299 */
1300 DWORD
1301 WINAPI
1302 VDMOperationStarted (
1303 DWORD Unknown0
1304 )
1305 {
1306 STUB;
1307 return 0;
1308 }