2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Base API Server DLL
4 * FILE: subsystems/win/basesrv/vdm.c
5 * PURPOSE: Virtual DOS Machines (VDM) Support
6 * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr)
7 * Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
10 /* INCLUDES *******************************************************************/
18 /* GLOBALS ********************************************************************/
20 BOOLEAN FirstVDM
= TRUE
;
21 LIST_ENTRY VDMConsoleListHead
;
22 RTL_CRITICAL_SECTION DosCriticalSection
;
23 RTL_CRITICAL_SECTION WowCriticalSection
;
25 /* FUNCTIONS ******************************************************************/
27 NTSTATUS NTAPI
BaseSrvGetConsoleRecord(HANDLE ConsoleHandle
, PVDM_CONSOLE_RECORD
*Record
)
30 PVDM_CONSOLE_RECORD CurrentRecord
= NULL
;
32 /* Search for a record that has the same console handle */
33 for (i
= VDMConsoleListHead
.Flink
; i
!= &VDMConsoleListHead
; i
= i
->Flink
)
35 CurrentRecord
= CONTAINING_RECORD(i
, VDM_CONSOLE_RECORD
, Entry
);
36 if (CurrentRecord
->ConsoleHandle
== ConsoleHandle
) break;
39 *Record
= CurrentRecord
;
40 return CurrentRecord
? STATUS_SUCCESS
: STATUS_NOT_FOUND
;
43 NTSTATUS NTAPI
GetConsoleRecordBySessionId(ULONG TaskId
, PVDM_CONSOLE_RECORD
*Record
)
46 PVDM_CONSOLE_RECORD CurrentRecord
= NULL
;
48 /* Search for a record that has the same console handle */
49 for (i
= VDMConsoleListHead
.Flink
; i
!= &VDMConsoleListHead
; i
= i
->Flink
)
51 CurrentRecord
= CONTAINING_RECORD(i
, VDM_CONSOLE_RECORD
, Entry
);
52 if (CurrentRecord
->SessionId
== TaskId
) break;
55 *Record
= CurrentRecord
;
56 return CurrentRecord
? STATUS_SUCCESS
: STATUS_NOT_FOUND
;
59 ULONG NTAPI
GetNextDosSesId(VOID
)
63 PVDM_CONSOLE_RECORD CurrentRecord
= NULL
;
66 /* Search for an available session ID */
67 for (SessionId
= 1; SessionId
!= 0; SessionId
++)
71 /* Check if the ID is already in use */
72 for (i
= VDMConsoleListHead
.Flink
; i
!= &VDMConsoleListHead
; i
= i
->Flink
)
74 CurrentRecord
= CONTAINING_RECORD(i
, VDM_CONSOLE_RECORD
, Entry
);
75 if (CurrentRecord
->SessionId
== SessionId
) Found
= TRUE
;
78 /* If not, we found one */
82 ASSERT(SessionId
!= 0);
84 /* Return the session ID */
88 BOOLEAN NTAPI
BaseSrvIsVdmAllowed(VOID
)
91 BOOLEAN VdmAllowed
= TRUE
;
92 HANDLE RootKey
, KeyHandle
;
93 UNICODE_STRING KeyName
, ValueName
, MachineKeyName
;
94 OBJECT_ATTRIBUTES Attributes
;
95 UCHAR ValueBuffer
[sizeof(KEY_VALUE_PARTIAL_INFORMATION
) + sizeof(ULONG
)];
96 PKEY_VALUE_PARTIAL_INFORMATION ValueInfo
= (PKEY_VALUE_PARTIAL_INFORMATION
)ValueBuffer
;
99 /* Initialize the unicode strings */
100 RtlInitUnicodeString(&MachineKeyName
, L
"\\Registry\\Machine");
101 RtlInitUnicodeString(&KeyName
, VDM_POLICY_KEY_NAME
);
102 RtlInitUnicodeString(&ValueName
, VDM_DISALLOWED_VALUE_NAME
);
104 InitializeObjectAttributes(&Attributes
,
106 OBJ_CASE_INSENSITIVE
,
110 /* Open the local machine key */
111 Status
= NtOpenKey(&RootKey
, KEY_READ
, &Attributes
);
112 if (!NT_SUCCESS(Status
)) return FALSE
;
114 InitializeObjectAttributes(&Attributes
,
116 OBJ_CASE_INSENSITIVE
,
120 /* Open the policy key in the local machine hive, if it exists */
121 if (NT_SUCCESS(NtOpenKey(&KeyHandle
, KEY_READ
, &Attributes
)))
123 /* Read the value, if it's set */
124 if (NT_SUCCESS(NtQueryValueKey(KeyHandle
,
126 KeyValuePartialInformation
,
131 if (*((PULONG
)ValueInfo
->Data
))
133 /* The VDM has been disabled in the registry */
141 /* Close the local machine key */
144 /* If it's disabled system-wide, there's no need to check the user key */
145 if (!VdmAllowed
) return FALSE
;
147 /* Open the current user key of the client */
148 if (!CsrImpersonateClient(NULL
)) return VdmAllowed
;
149 Status
= RtlOpenCurrentUser(KEY_READ
, &RootKey
);
152 /* If that fails, return the system-wide setting */
153 if (!NT_SUCCESS(Status
)) return VdmAllowed
;
155 InitializeObjectAttributes(&Attributes
,
157 OBJ_CASE_INSENSITIVE
,
161 /* Open the policy key in the current user hive, if it exists */
162 if (NT_SUCCESS(NtOpenKey(&KeyHandle
, KEY_READ
, &Attributes
)))
164 /* Read the value, if it's set */
165 if (NT_SUCCESS(NtQueryValueKey(KeyHandle
,
167 KeyValuePartialInformation
,
172 if (*((PULONG
)ValueInfo
->Data
))
174 /* The VDM has been disabled in the registry */
185 NTSTATUS NTAPI
BaseSrvCreatePairWaitHandles(PHANDLE ServerEvent
, PHANDLE ClientEvent
)
189 /* Create the event */
190 Status
= NtCreateEvent(ServerEvent
, EVENT_ALL_ACCESS
, NULL
, NotificationEvent
, FALSE
);
191 if (!NT_SUCCESS(Status
)) return Status
;
193 /* Duplicate the event into the client process */
194 Status
= NtDuplicateObject(NtCurrentProcess(),
196 CsrGetClientThread()->Process
->ProcessHandle
,
200 DUPLICATE_SAME_ATTRIBUTES
| DUPLICATE_SAME_ACCESS
);
202 if (!NT_SUCCESS(Status
)) NtClose(*ServerEvent
);
206 VOID
BaseSrvFreeVDMInfo(PVDM_COMMAND_INFO CommandInfo
)
208 /* Free the allocated structure members */
209 if (CommandInfo
->CmdLine
!= NULL
) RtlFreeHeap(BaseSrvHeap
, 0, CommandInfo
->CmdLine
);
210 if (CommandInfo
->AppName
!= NULL
) RtlFreeHeap(BaseSrvHeap
, 0, CommandInfo
->AppName
);
211 if (CommandInfo
->PifFile
!= NULL
) RtlFreeHeap(BaseSrvHeap
, 0, CommandInfo
->PifFile
);
212 if (CommandInfo
->CurDirectory
!= NULL
) RtlFreeHeap(BaseSrvHeap
, 0, CommandInfo
->CurDirectory
);
213 if (CommandInfo
->Env
!= NULL
) RtlFreeHeap(BaseSrvHeap
, 0, CommandInfo
->Env
);
214 if (CommandInfo
->Desktop
!= NULL
) RtlFreeHeap(BaseSrvHeap
, 0, CommandInfo
->Desktop
);
215 if (CommandInfo
->Title
!= NULL
) RtlFreeHeap(BaseSrvHeap
, 0, CommandInfo
->Title
);
216 if (CommandInfo
->Reserved
!= NULL
) RtlFreeHeap(BaseSrvHeap
, 0, CommandInfo
->Reserved
);
218 /* Free the structure itself */
219 RtlFreeHeap(BaseSrvHeap
, 0, CommandInfo
);
222 BOOLEAN NTAPI
BaseSrvCopyCommand(PBASE_CHECK_VDM CheckVdmRequest
, PVDM_DOS_RECORD DosRecord
)
224 BOOLEAN Success
= FALSE
;
225 PVDM_COMMAND_INFO CommandInfo
= NULL
;
227 /* Allocate the command information structure */
228 CommandInfo
= (PVDM_COMMAND_INFO
)RtlAllocateHeap(BaseSrvHeap
,
230 sizeof(VDM_COMMAND_INFO
));
231 if (CommandInfo
== NULL
) return FALSE
;
233 /* Fill the structure */
234 CommandInfo
->TaskId
= CheckVdmRequest
->iTask
;
235 CommandInfo
->ExitCode
= DosRecord
->ExitCode
;
236 CommandInfo
->CodePage
= CheckVdmRequest
->CodePage
;
237 CommandInfo
->StdIn
= CheckVdmRequest
->StdIn
;
238 CommandInfo
->StdOut
= CheckVdmRequest
->StdOut
;
239 CommandInfo
->StdErr
= CheckVdmRequest
->StdErr
;
241 /* Allocate memory for the command line */
242 CommandInfo
->CmdLine
= RtlAllocateHeap(BaseSrvHeap
,
244 CheckVdmRequest
->CmdLen
);
245 if (CommandInfo
->CmdLine
== NULL
) goto Cleanup
;
247 /* Copy the command line */
248 RtlMoveMemory(CommandInfo
->CmdLine
, CheckVdmRequest
->CmdLine
, CheckVdmRequest
->CmdLen
);
250 /* Allocate memory for the application name */
251 CommandInfo
->AppName
= RtlAllocateHeap(BaseSrvHeap
,
253 CheckVdmRequest
->AppLen
);
254 if (CommandInfo
->AppName
== NULL
) goto Cleanup
;
256 /* Copy the application name */
257 RtlMoveMemory(CommandInfo
->AppName
, CheckVdmRequest
->AppName
, CheckVdmRequest
->AppLen
);
259 /* Allocate memory for the PIF file name */
260 if (CheckVdmRequest
->PifLen
!= 0)
262 CommandInfo
->PifFile
= RtlAllocateHeap(BaseSrvHeap
,
264 CheckVdmRequest
->PifLen
);
265 if (CommandInfo
->PifFile
== NULL
) goto Cleanup
;
267 /* Copy the PIF file name */
268 RtlMoveMemory(CommandInfo
->PifFile
, CheckVdmRequest
->PifFile
, CheckVdmRequest
->PifLen
);
270 else CommandInfo
->PifFile
= NULL
;
272 /* Allocate memory for the current directory */
273 if (CheckVdmRequest
->CurDirectoryLen
!= 0)
275 CommandInfo
->CurDirectory
= RtlAllocateHeap(BaseSrvHeap
,
277 CheckVdmRequest
->CurDirectoryLen
);
278 if (CommandInfo
->CurDirectory
== NULL
) goto Cleanup
;
280 /* Copy the current directory */
281 RtlMoveMemory(CommandInfo
->CurDirectory
,
282 CheckVdmRequest
->CurDirectory
,
283 CheckVdmRequest
->CurDirectoryLen
);
285 else CommandInfo
->CurDirectory
= NULL
;
287 /* Allocate memory for the environment block */
288 CommandInfo
->Env
= RtlAllocateHeap(BaseSrvHeap
,
290 CheckVdmRequest
->EnvLen
);
291 if (CommandInfo
->Env
== NULL
) goto Cleanup
;
293 /* Copy the environment block */
294 RtlMoveMemory(CommandInfo
->Env
, CheckVdmRequest
->Env
, CheckVdmRequest
->EnvLen
);
296 CommandInfo
->EnvLen
= CheckVdmRequest
->EnvLen
;
297 RtlMoveMemory(&CommandInfo
->StartupInfo
,
298 CheckVdmRequest
->StartupInfo
,
299 sizeof(STARTUPINFOA
));
301 /* Allocate memory for the desktop */
302 if (CheckVdmRequest
->DesktopLen
!= 0)
304 CommandInfo
->Desktop
= RtlAllocateHeap(BaseSrvHeap
,
306 CheckVdmRequest
->DesktopLen
);
307 if (CommandInfo
->Desktop
== NULL
) goto Cleanup
;
309 /* Copy the desktop name */
310 RtlMoveMemory(CommandInfo
->Desktop
, CheckVdmRequest
->Desktop
, CheckVdmRequest
->DesktopLen
);
312 else CommandInfo
->Desktop
= NULL
;
314 CommandInfo
->DesktopLen
= CheckVdmRequest
->DesktopLen
;
316 /* Allocate memory for the title */
317 if (CheckVdmRequest
->TitleLen
!= 0)
319 CommandInfo
->Title
= RtlAllocateHeap(BaseSrvHeap
,
321 CheckVdmRequest
->TitleLen
);
322 if (CommandInfo
->Title
== NULL
) goto Cleanup
;
325 RtlMoveMemory(CommandInfo
->Title
, CheckVdmRequest
->Title
, CheckVdmRequest
->TitleLen
);
327 else CommandInfo
->Title
= NULL
;
329 CommandInfo
->TitleLen
= CheckVdmRequest
->TitleLen
;
331 /* Allocate memory for the reserved field */
332 if (CheckVdmRequest
->ReservedLen
!= 0)
334 CommandInfo
->Reserved
= RtlAllocateHeap(BaseSrvHeap
,
336 CheckVdmRequest
->ReservedLen
);
337 if (CommandInfo
->Reserved
== NULL
) goto Cleanup
;
339 /* Copy the reserved field */
340 RtlMoveMemory(CommandInfo
->Reserved
,
341 CheckVdmRequest
->Reserved
,
342 CheckVdmRequest
->ReservedLen
);
344 else CommandInfo
->Reserved
= NULL
;
346 CommandInfo
->ReservedLen
= CheckVdmRequest
->ReservedLen
;
348 CommandInfo
->CmdLen
= CheckVdmRequest
->CmdLen
;
349 CommandInfo
->AppLen
= CheckVdmRequest
->AppLen
;
350 CommandInfo
->PifLen
= CheckVdmRequest
->PifLen
;
351 CommandInfo
->CurDirectoryLen
= CheckVdmRequest
->CurDirectoryLen
;
352 CommandInfo
->VDMState
= DosRecord
->State
;
353 // TODO: Set CommandInfo->CurrentDrive
354 // TODO: Set CommandInfo->ComingFromBat
356 /* Set the DOS record's command structure */
357 DosRecord
->CommandInfo
= CommandInfo
;
359 /* The operation was successful */
363 /* If it wasn't successful, free the memory */
364 if (!Success
) BaseSrvFreeVDMInfo(CommandInfo
);
369 NTSTATUS NTAPI
BaseSrvFillCommandInfo(PVDM_COMMAND_INFO CommandInfo
,
370 PBASE_GET_NEXT_VDM_COMMAND Message
)
373 Message
->iTask
= CommandInfo
->TaskId
;
374 Message
->StdIn
= CommandInfo
->StdIn
;
375 Message
->StdOut
= CommandInfo
->StdOut
;
376 Message
->StdErr
= CommandInfo
->StdErr
;
377 Message
->CodePage
= CommandInfo
->CodePage
;
378 Message
->dwCreationFlags
= CommandInfo
->CreationFlags
;
379 Message
->ExitCode
= CommandInfo
->ExitCode
;
380 Message
->CurrentDrive
= CommandInfo
->CurrentDrive
;
381 Message
->VDMState
= CommandInfo
->VDMState
;
382 Message
->fComingFromBat
= CommandInfo
->ComingFromBat
;
384 if (CommandInfo
->CmdLen
&& Message
->CmdLen
)
386 if (Message
->CmdLen
< CommandInfo
->CmdLen
) return STATUS_BUFFER_TOO_SMALL
;
388 /* Copy the command line */
389 RtlMoveMemory(Message
->CmdLine
, CommandInfo
->CmdLine
, CommandInfo
->CmdLen
);
390 Message
->CmdLen
= CommandInfo
->CmdLen
;
393 if (CommandInfo
->AppLen
&& Message
->AppLen
)
395 if (Message
->AppLen
< CommandInfo
->CmdLen
) return STATUS_BUFFER_TOO_SMALL
;
397 /* Copy the application name */
398 RtlMoveMemory(Message
->AppName
, CommandInfo
->AppName
, CommandInfo
->AppLen
);
399 Message
->AppLen
= CommandInfo
->AppLen
;
402 if (CommandInfo
->PifLen
&& Message
->PifLen
)
404 if (Message
->PifLen
< CommandInfo
->PifLen
) return STATUS_BUFFER_TOO_SMALL
;
406 /* Copy the PIF file name */
407 RtlMoveMemory(Message
->PifFile
, CommandInfo
->PifFile
, CommandInfo
->PifLen
);
408 Message
->PifLen
= CommandInfo
->PifLen
;
411 if (CommandInfo
->CurDirectoryLen
&& Message
->CurDirectoryLen
)
413 if (Message
->CurDirectoryLen
< CommandInfo
->CurDirectoryLen
) return STATUS_BUFFER_TOO_SMALL
;
415 /* Copy the current directory */
416 RtlMoveMemory(Message
->CurDirectory
, CommandInfo
->CurDirectory
, CommandInfo
->CurDirectoryLen
);
417 Message
->CurDirectoryLen
= CommandInfo
->CurDirectoryLen
;
420 if (CommandInfo
->EnvLen
&& Message
->EnvLen
)
422 if (Message
->EnvLen
< CommandInfo
->EnvLen
) return STATUS_BUFFER_TOO_SMALL
;
424 /* Copy the environment */
425 RtlMoveMemory(Message
->Env
, CommandInfo
->Env
, CommandInfo
->EnvLen
);
426 Message
->EnvLen
= CommandInfo
->EnvLen
;
429 /* Copy the startup info */
430 RtlMoveMemory(Message
->StartupInfo
,
431 &CommandInfo
->StartupInfo
,
432 sizeof(STARTUPINFOA
));
434 if (CommandInfo
->DesktopLen
&& Message
->DesktopLen
)
436 if (Message
->DesktopLen
< CommandInfo
->DesktopLen
) return STATUS_BUFFER_TOO_SMALL
;
438 /* Copy the desktop name */
439 RtlMoveMemory(Message
->Desktop
, CommandInfo
->Desktop
, CommandInfo
->DesktopLen
);
440 Message
->DesktopLen
= CommandInfo
->DesktopLen
;
443 if (CommandInfo
->TitleLen
&& Message
->TitleLen
)
445 if (Message
->TitleLen
< CommandInfo
->TitleLen
) return STATUS_BUFFER_TOO_SMALL
;
448 RtlMoveMemory(Message
->Title
, CommandInfo
->Title
, CommandInfo
->TitleLen
);
449 Message
->TitleLen
= CommandInfo
->TitleLen
;
452 if (CommandInfo
->ReservedLen
&& Message
->ReservedLen
)
454 if (Message
->ReservedLen
< CommandInfo
->ReservedLen
) return STATUS_BUFFER_TOO_SMALL
;
456 /* Copy the reserved parameter */
457 RtlMoveMemory(Message
->Reserved
, CommandInfo
->Reserved
, CommandInfo
->ReservedLen
);
458 Message
->ReservedLen
= CommandInfo
->ReservedLen
;
461 return STATUS_SUCCESS
;
464 VOID NTAPI
BaseInitializeVDM(VOID
)
466 /* Initialize the list head */
467 InitializeListHead(&VDMConsoleListHead
);
469 /* Initialize the critical section */
470 RtlInitializeCriticalSection(&DosCriticalSection
);
471 RtlInitializeCriticalSection(&WowCriticalSection
);
474 /* PUBLIC SERVER APIS *********************************************************/
476 CSR_API(BaseSrvCheckVDM
)
479 PBASE_CHECK_VDM CheckVdmRequest
= &((PBASE_API_MESSAGE
)ApiMessage
)->Data
.CheckVDMRequest
;
480 PRTL_CRITICAL_SECTION CriticalSection
= NULL
;
481 PVDM_CONSOLE_RECORD ConsoleRecord
= NULL
;
482 PVDM_DOS_RECORD DosRecord
= NULL
;
483 BOOLEAN NewConsoleRecord
= FALSE
;
485 /* Don't do anything if the VDM has been disabled in the registry */
486 if (!BaseSrvIsVdmAllowed()) return STATUS_ACCESS_DENIED
;
488 /* Validate the message buffers */
489 if (!CsrValidateMessageBuffer(ApiMessage
,
490 (PVOID
*)&CheckVdmRequest
->CmdLine
,
491 CheckVdmRequest
->CmdLen
,
492 sizeof(*CheckVdmRequest
->CmdLine
))
493 || !CsrValidateMessageBuffer(ApiMessage
,
494 (PVOID
*)&CheckVdmRequest
->AppName
,
495 CheckVdmRequest
->AppLen
,
496 sizeof(*CheckVdmRequest
->AppName
))
497 || !CsrValidateMessageBuffer(ApiMessage
,
498 (PVOID
*)&CheckVdmRequest
->PifFile
,
499 CheckVdmRequest
->PifLen
,
500 sizeof(*CheckVdmRequest
->PifFile
))
501 || !CsrValidateMessageBuffer(ApiMessage
,
502 (PVOID
*)&CheckVdmRequest
->CurDirectory
,
503 CheckVdmRequest
->CurDirectoryLen
,
504 sizeof(*CheckVdmRequest
->CurDirectory
))
505 || !CsrValidateMessageBuffer(ApiMessage
,
506 (PVOID
*)&CheckVdmRequest
->Desktop
,
507 CheckVdmRequest
->DesktopLen
,
508 sizeof(*CheckVdmRequest
->Desktop
))
509 || !CsrValidateMessageBuffer(ApiMessage
,
510 (PVOID
*)&CheckVdmRequest
->Title
,
511 CheckVdmRequest
->TitleLen
,
512 sizeof(*CheckVdmRequest
->Title
))
513 || !CsrValidateMessageBuffer(ApiMessage
,
514 (PVOID
*)&CheckVdmRequest
->Reserved
,
515 CheckVdmRequest
->ReservedLen
,
516 sizeof(*CheckVdmRequest
->Reserved
)))
518 return STATUS_INVALID_PARAMETER
;
521 CriticalSection
= (CheckVdmRequest
->BinaryType
!= BINARY_TYPE_SEPARATE_WOW
)
522 ? &DosCriticalSection
523 : &WowCriticalSection
;
525 /* Enter the critical section */
526 RtlEnterCriticalSection(CriticalSection
);
528 /* Check if this is a DOS or WOW VDM */
529 if (CheckVdmRequest
->BinaryType
!= BINARY_TYPE_SEPARATE_WOW
)
531 /* Get the console record */
532 Status
= BaseSrvGetConsoleRecord(CheckVdmRequest
->ConsoleHandle
,
535 if (!NT_SUCCESS(Status
))
537 /* Allocate a new console record */
538 ConsoleRecord
= (PVDM_CONSOLE_RECORD
)RtlAllocateHeap(BaseSrvHeap
,
540 sizeof(VDM_CONSOLE_RECORD
));
541 if (ConsoleRecord
== NULL
)
543 Status
= STATUS_NO_MEMORY
;
547 /* Remember that the console record was allocated here */
548 NewConsoleRecord
= TRUE
;
550 /* Initialize the console record */
551 ConsoleRecord
->ConsoleHandle
= CheckVdmRequest
->ConsoleHandle
;
552 ConsoleRecord
->ProcessHandle
= CsrGetClientThread()->Process
->ProcessHandle
;
553 ConsoleRecord
->ServerEvent
= ConsoleRecord
->ClientEvent
= NULL
;
554 ConsoleRecord
->ReenterCount
= 0;
555 ConsoleRecord
->CurrentDirs
= NULL
;
556 ConsoleRecord
->CurDirsLength
= 0;
557 ConsoleRecord
->SessionId
= GetNextDosSesId();
558 InitializeListHead(&ConsoleRecord
->DosListHead
);
561 /* Allocate a new DOS record */
562 DosRecord
= (PVDM_DOS_RECORD
)RtlAllocateHeap(BaseSrvHeap
,
564 sizeof(VDM_DOS_RECORD
));
565 if (DosRecord
== NULL
)
567 Status
= STATUS_NO_MEMORY
;
571 /* Initialize the DOS record */
572 DosRecord
->State
= VDM_NOT_LOADED
;
573 DosRecord
->ExitCode
= 0;
575 Status
= BaseSrvCreatePairWaitHandles(&DosRecord
->ServerEvent
, &DosRecord
->ClientEvent
);
576 if (!NT_SUCCESS(Status
)) goto Cleanup
;
578 /* Return the client event handle */
579 CheckVdmRequest
->WaitObjectForParent
= DosRecord
->ClientEvent
;
581 /* Translate the input structure into a VDM command structure and set it in the DOS record */
582 if (!BaseSrvCopyCommand(CheckVdmRequest
, DosRecord
))
584 /* The only possibility is that an allocation failure occurred */
585 Status
= STATUS_NO_MEMORY
;
589 /* Add the DOS record */
590 InsertHeadList(&ConsoleRecord
->DosListHead
, &DosRecord
->Entry
);
592 if (ConsoleRecord
->ServerEvent
)
594 /* Signal the session event */
595 NtSetEvent(ConsoleRecord
->ServerEvent
, NULL
);
598 if (NewConsoleRecord
)
600 /* Add the console record */
601 InsertTailList(&VDMConsoleListHead
, &ConsoleRecord
->Entry
);
604 CheckVdmRequest
->VDMState
= NewConsoleRecord
? VDM_NOT_LOADED
: VDM_READY
;
605 Status
= STATUS_SUCCESS
;
609 // TODO: NOT IMPLEMENTED
611 Status
= STATUS_NOT_IMPLEMENTED
;
615 /* Check if it failed */
616 if (!NT_SUCCESS(Status
))
618 /* Free the DOS record */
619 if (DosRecord
!= NULL
)
621 if (DosRecord
->ServerEvent
) NtClose(DosRecord
->ServerEvent
);
622 if (DosRecord
->ClientEvent
)
624 /* Close the remote handle */
625 NtDuplicateObject(CsrGetClientThread()->Process
->ProcessHandle
,
626 DosRecord
->ClientEvent
,
631 DUPLICATE_CLOSE_SOURCE
);
634 RtlFreeHeap(BaseSrvHeap
, 0, DosRecord
);
638 /* Free the console record if it was allocated here */
639 if (NewConsoleRecord
)
641 RtlFreeHeap(BaseSrvHeap
, 0, ConsoleRecord
);
642 ConsoleRecord
= NULL
;
646 /* Leave the critical section */
647 RtlLeaveCriticalSection(CriticalSection
);
652 CSR_API(BaseSrvUpdateVDMEntry
)
655 PBASE_UPDATE_VDM_ENTRY UpdateVdmEntryRequest
= &((PBASE_API_MESSAGE
)ApiMessage
)->Data
.UpdateVDMEntryRequest
;
656 PRTL_CRITICAL_SECTION CriticalSection
= NULL
;
657 PVDM_CONSOLE_RECORD ConsoleRecord
= NULL
;
658 PVDM_DOS_RECORD DosRecord
= NULL
;
660 CriticalSection
= (UpdateVdmEntryRequest
->BinaryType
!= BINARY_TYPE_SEPARATE_WOW
)
661 ? &DosCriticalSection
662 : &WowCriticalSection
;
664 /* Enter the critical section */
665 RtlEnterCriticalSection(CriticalSection
);
667 /* Check if this is a DOS or WOW VDM */
668 if (UpdateVdmEntryRequest
->BinaryType
!= BINARY_TYPE_SEPARATE_WOW
)
670 if (UpdateVdmEntryRequest
->iTask
!= 0)
672 /* Get the console record using the task ID */
673 Status
= GetConsoleRecordBySessionId(UpdateVdmEntryRequest
->iTask
,
678 /* Get the console record using the console handle */
679 Status
= BaseSrvGetConsoleRecord(UpdateVdmEntryRequest
->ConsoleHandle
,
683 if (!NT_SUCCESS(Status
)) goto Cleanup
;
685 /* Get the primary DOS record */
686 DosRecord
= (PVDM_DOS_RECORD
)CONTAINING_RECORD(ConsoleRecord
->DosListHead
.Flink
,
690 switch (UpdateVdmEntryRequest
->EntryIndex
)
694 /* Close the server event handle, the client will close the client handle */
695 NtClose(DosRecord
->ServerEvent
);
696 DosRecord
->ServerEvent
= DosRecord
->ClientEvent
= NULL
;
698 if (UpdateVdmEntryRequest
->VDMCreationState
& (VDM_UNDO_PARTIAL
| VDM_UNDO_FULL
))
700 /* Remove the DOS record */
701 if (DosRecord
->CommandInfo
) BaseSrvFreeVDMInfo(DosRecord
->CommandInfo
);
702 RemoveEntryList(&DosRecord
->Entry
);
703 RtlFreeHeap(BaseSrvHeap
, 0, DosRecord
);
706 * Since this is an undo, if that was the only DOS record the VDM
707 * won't even start, so the console record should be removed too.
709 if (ConsoleRecord
->DosListHead
.Flink
== &ConsoleRecord
->DosListHead
)
711 if (ConsoleRecord
->ServerEvent
) NtClose(ConsoleRecord
->ServerEvent
);
712 RemoveEntryList(&ConsoleRecord
->Entry
);
713 RtlFreeHeap(BaseSrvHeap
, 0, ConsoleRecord
);
717 /* It was successful */
718 Status
= STATUS_SUCCESS
;
723 case VdmEntryUpdateProcess
:
725 /* Duplicate the VDM process handle */
726 Status
= NtDuplicateObject(CsrGetClientThread()->Process
->ProcessHandle
,
727 UpdateVdmEntryRequest
->VDMProcessHandle
,
729 &ConsoleRecord
->ProcessHandle
,
732 DUPLICATE_SAME_ATTRIBUTES
| DUPLICATE_SAME_ACCESS
);
733 if (!NT_SUCCESS(Status
)) goto Cleanup
;
735 /* Create a pair of handles to one event object */
736 Status
= BaseSrvCreatePairWaitHandles(&DosRecord
->ServerEvent
,
737 &DosRecord
->ClientEvent
);
738 if (!NT_SUCCESS(Status
)) goto Cleanup
;
740 /* Return the client event handle */
741 UpdateVdmEntryRequest
->WaitObjectForParent
= DosRecord
->ClientEvent
;
746 case VdmEntryUpdateControlCHandler
:
748 // TODO: NOT IMPLEMENTED
749 DPRINT1("BaseSrvUpdateVDMEntry: VdmEntryUpdateControlCHandler not implemented!");
750 Status
= STATUS_NOT_IMPLEMENTED
;
758 Status
= STATUS_INVALID_PARAMETER
;
764 // TODO: NOT IMPLEMENTED
766 Status
= STATUS_NOT_IMPLEMENTED
;
770 /* Leave the critical section */
771 RtlLeaveCriticalSection(CriticalSection
);
776 CSR_API(BaseSrvGetNextVDMCommand
)
779 PBASE_GET_NEXT_VDM_COMMAND GetNextVdmCommandRequest
=
780 &((PBASE_API_MESSAGE
)ApiMessage
)->Data
.GetNextVDMCommandRequest
;
781 PRTL_CRITICAL_SECTION CriticalSection
;
782 PLIST_ENTRY i
= NULL
;
783 PVDM_CONSOLE_RECORD ConsoleRecord
= NULL
;
784 PVDM_DOS_RECORD DosRecord
= NULL
;
786 /* Validate the message buffers */
787 if (!CsrValidateMessageBuffer(ApiMessage
,
788 (PVOID
*)&GetNextVdmCommandRequest
->CmdLine
,
789 GetNextVdmCommandRequest
->CmdLen
,
790 sizeof(*GetNextVdmCommandRequest
->CmdLine
))
791 || !CsrValidateMessageBuffer(ApiMessage
,
792 (PVOID
*)&GetNextVdmCommandRequest
->AppName
,
793 GetNextVdmCommandRequest
->AppLen
,
794 sizeof(*GetNextVdmCommandRequest
->AppName
))
795 || !CsrValidateMessageBuffer(ApiMessage
,
796 (PVOID
*)&GetNextVdmCommandRequest
->PifFile
,
797 GetNextVdmCommandRequest
->PifLen
,
798 sizeof(*GetNextVdmCommandRequest
->PifFile
))
799 || !CsrValidateMessageBuffer(ApiMessage
,
800 (PVOID
*)&GetNextVdmCommandRequest
->CurDirectory
,
801 GetNextVdmCommandRequest
->CurDirectoryLen
,
802 sizeof(*GetNextVdmCommandRequest
->CurDirectory
))
803 || !CsrValidateMessageBuffer(ApiMessage
,
804 (PVOID
*)&GetNextVdmCommandRequest
->Env
,
805 GetNextVdmCommandRequest
->EnvLen
,
806 sizeof(*GetNextVdmCommandRequest
->Env
))
807 || !CsrValidateMessageBuffer(ApiMessage
,
808 (PVOID
*)&GetNextVdmCommandRequest
->Desktop
,
809 GetNextVdmCommandRequest
->DesktopLen
,
810 sizeof(*GetNextVdmCommandRequest
->Desktop
))
811 || !CsrValidateMessageBuffer(ApiMessage
,
812 (PVOID
*)&GetNextVdmCommandRequest
->Title
,
813 GetNextVdmCommandRequest
->TitleLen
,
814 sizeof(*GetNextVdmCommandRequest
->Title
))
815 || !CsrValidateMessageBuffer(ApiMessage
,
816 (PVOID
*)&GetNextVdmCommandRequest
->Reserved
,
817 GetNextVdmCommandRequest
->ReservedLen
,
818 sizeof(*GetNextVdmCommandRequest
->Reserved
))
819 || !CsrValidateMessageBuffer(ApiMessage
,
820 (PVOID
*)&GetNextVdmCommandRequest
->StartupInfo
,
822 sizeof(STARTUPINFOA
)))
824 return STATUS_INVALID_PARAMETER
;
827 CriticalSection
= (GetNextVdmCommandRequest
->VDMState
& VDM_FLAG_WOW
)
828 ? &WowCriticalSection
829 : &DosCriticalSection
;
831 /* Enter the critical section */
832 RtlEnterCriticalSection(CriticalSection
);
834 if (!(GetNextVdmCommandRequest
->VDMState
& VDM_FLAG_WOW
))
836 if (GetNextVdmCommandRequest
->iTask
!= 0)
838 /* Get the console record using the task ID */
839 Status
= GetConsoleRecordBySessionId(GetNextVdmCommandRequest
->iTask
,
844 /* Get the console record using the console handle */
845 Status
= BaseSrvGetConsoleRecord(GetNextVdmCommandRequest
->ConsoleHandle
,
849 /* Return the session ID */
850 GetNextVdmCommandRequest
->iTask
= ConsoleRecord
->SessionId
;
851 GetNextVdmCommandRequest
->WaitObjectForVDM
= NULL
;
853 // HACK: I'm not sure if this should happen...
854 for (i
= ConsoleRecord
->DosListHead
.Flink
; i
!= &ConsoleRecord
->DosListHead
; i
= i
->Flink
)
856 DosRecord
= CONTAINING_RECORD(i
, VDM_DOS_RECORD
, Entry
);
857 if (DosRecord
->State
== VDM_NOT_READY
)
859 /* If NTVDM is asking for a new command, it means these are done */
860 DosRecord
->State
= VDM_READY
;
862 NtSetEvent(DosRecord
->ServerEvent
, NULL
);
863 NtClose(DosRecord
->ServerEvent
);
864 DosRecord
->ServerEvent
= NULL
;
868 /* Search for a DOS record that isn't loaded yet */
869 for (i
= ConsoleRecord
->DosListHead
.Flink
; i
!= &ConsoleRecord
->DosListHead
; i
= i
->Flink
)
871 DosRecord
= CONTAINING_RECORD(i
, VDM_DOS_RECORD
, Entry
);
872 if (DosRecord
->State
== VDM_NOT_LOADED
) break;
875 if (i
!= &ConsoleRecord
->DosListHead
)
877 /* Fill the command information */
878 Status
= BaseSrvFillCommandInfo(DosRecord
->CommandInfo
, GetNextVdmCommandRequest
);
879 if (!NT_SUCCESS(Status
)) goto Cleanup
;
881 /* Free the command information, it's no longer needed */
882 BaseSrvFreeVDMInfo(DosRecord
->CommandInfo
);
883 DosRecord
->CommandInfo
= NULL
;
885 /* Update the VDM state */
886 DosRecord
->State
= VDM_NOT_READY
;
888 Status
= STATUS_SUCCESS
;
894 // TODO: WOW SUPPORT NOT IMPLEMENTED
895 Status
= STATUS_NOT_IMPLEMENTED
;
899 /* There is no command yet */
900 if (ConsoleRecord
->ServerEvent
)
902 /* Reset the event */
903 NtResetEvent(ConsoleRecord
->ServerEvent
, NULL
);
907 /* Create a pair of wait handles */
908 Status
= BaseSrvCreatePairWaitHandles(&ConsoleRecord
->ServerEvent
,
909 &ConsoleRecord
->ClientEvent
);
910 if (!NT_SUCCESS(Status
)) goto Cleanup
;
913 /* Return the client event handle */
914 GetNextVdmCommandRequest
->WaitObjectForVDM
= ConsoleRecord
->ClientEvent
;
917 /* Leave the critical section */
918 RtlLeaveCriticalSection(CriticalSection
);
923 CSR_API(BaseSrvExitVDM
)
926 PBASE_EXIT_VDM ExitVdmRequest
= &((PBASE_API_MESSAGE
)ApiMessage
)->Data
.ExitVDMRequest
;
927 PRTL_CRITICAL_SECTION CriticalSection
= NULL
;
928 PVDM_CONSOLE_RECORD ConsoleRecord
= NULL
;
929 PVDM_DOS_RECORD DosRecord
;
931 CriticalSection
= (ExitVdmRequest
->iWowTask
== 0)
932 ? &DosCriticalSection
933 : &WowCriticalSection
;
935 /* Enter the critical section */
936 RtlEnterCriticalSection(CriticalSection
);
938 if (ExitVdmRequest
->iWowTask
== 0)
940 /* Get the console record */
941 Status
= BaseSrvGetConsoleRecord(ExitVdmRequest
->ConsoleHandle
, &ConsoleRecord
);
942 if (!NT_SUCCESS(Status
)) goto Cleanup
;
944 /* Cleanup the DOS records */
945 while (ConsoleRecord
->DosListHead
.Flink
!= &ConsoleRecord
->DosListHead
)
947 DosRecord
= CONTAINING_RECORD(ConsoleRecord
->DosListHead
.Flink
,
951 /* Set the event and close it */
952 NtSetEvent(DosRecord
->ServerEvent
, NULL
);
953 NtClose(DosRecord
->ServerEvent
);
955 /* Remove the DOS entry */
956 if (DosRecord
->CommandInfo
) BaseSrvFreeVDMInfo(DosRecord
->CommandInfo
);
957 RemoveEntryList(&DosRecord
->Entry
);
958 RtlFreeHeap(BaseSrvHeap
, 0, DosRecord
);
961 if (ConsoleRecord
->CurrentDirs
!= NULL
)
963 /* Free the current directories */
964 RtlFreeHeap(BaseSrvHeap
, 0, ConsoleRecord
->CurrentDirs
);
965 ConsoleRecord
->CurrentDirs
= NULL
;
966 ConsoleRecord
->CurDirsLength
= 0;
969 /* Close the event handle */
970 if (ConsoleRecord
->ServerEvent
) NtClose(ConsoleRecord
->ServerEvent
);
972 /* Remove the console record */
973 RemoveEntryList(&ConsoleRecord
->Entry
);
974 RtlFreeHeap(BaseSrvHeap
, 0, ConsoleRecord
);
978 // TODO: NOT IMPLEMENTED
980 Status
= STATUS_NOT_IMPLEMENTED
;
984 /* Leave the critical section */
985 RtlLeaveCriticalSection(CriticalSection
);
990 CSR_API(BaseSrvIsFirstVDM
)
992 PBASE_IS_FIRST_VDM IsFirstVDMRequest
= &((PBASE_API_MESSAGE
)ApiMessage
)->Data
.IsFirstVDMRequest
;
994 /* Return the result */
995 IsFirstVDMRequest
->FirstVDM
= FirstVDM
;
997 /* Clear the first VDM flag */
1000 return STATUS_SUCCESS
;
1003 CSR_API(BaseSrvGetVDMExitCode
)
1006 PBASE_GET_VDM_EXIT_CODE GetVDMExitCodeRequest
= &((PBASE_API_MESSAGE
)ApiMessage
)->Data
.GetVDMExitCodeRequest
;
1007 PLIST_ENTRY i
= NULL
;
1008 PVDM_CONSOLE_RECORD ConsoleRecord
= NULL
;
1009 PVDM_DOS_RECORD DosRecord
= NULL
;
1011 /* Enter the critical section */
1012 RtlEnterCriticalSection(&DosCriticalSection
);
1014 /* Get the console record */
1015 Status
= BaseSrvGetConsoleRecord(GetVDMExitCodeRequest
->ConsoleHandle
, &ConsoleRecord
);
1016 if (!NT_SUCCESS(Status
)) goto Cleanup
;
1018 /* Search for a DOS record that has the same parent process handle */
1019 for (i
= ConsoleRecord
->DosListHead
.Flink
; i
!= &ConsoleRecord
->DosListHead
; i
= i
->Flink
)
1021 DosRecord
= CONTAINING_RECORD(i
, VDM_DOS_RECORD
, Entry
);
1022 if (DosRecord
->ClientEvent
== GetVDMExitCodeRequest
->hParent
) break;
1025 /* Check if no DOS record was found */
1026 if (i
== &ConsoleRecord
->DosListHead
)
1028 Status
= STATUS_NOT_FOUND
;
1032 /* Check if this task is still running */
1033 if (DosRecord
->State
== VDM_READY
)
1035 GetVDMExitCodeRequest
->ExitCode
= STATUS_PENDING
;
1039 /* Return the exit code */
1040 GetVDMExitCodeRequest
->ExitCode
= DosRecord
->ExitCode
;
1042 /* Since this is a zombie task record, remove it */
1043 if (DosRecord
->CommandInfo
) BaseSrvFreeVDMInfo(DosRecord
->CommandInfo
);
1044 RemoveEntryList(&DosRecord
->Entry
);
1045 RtlFreeHeap(BaseSrvHeap
, 0, DosRecord
);
1048 /* Leave the critical section */
1049 RtlLeaveCriticalSection(&DosCriticalSection
);
1054 CSR_API(BaseSrvSetReenterCount
)
1056 NTSTATUS Status
= STATUS_SUCCESS
;
1057 PBASE_SET_REENTER_COUNT SetReenterCountRequest
= &((PBASE_API_MESSAGE
)ApiMessage
)->Data
.SetReenterCountRequest
;
1058 PVDM_CONSOLE_RECORD ConsoleRecord
;
1060 /* Enter the critical section */
1061 RtlEnterCriticalSection(&DosCriticalSection
);
1063 /* Get the console record */
1064 Status
= BaseSrvGetConsoleRecord(SetReenterCountRequest
->ConsoleHandle
, &ConsoleRecord
);
1065 if (!NT_SUCCESS(Status
)) goto Cleanup
;
1067 if (SetReenterCountRequest
->fIncDec
== VDM_INC_REENTER_COUNT
) ConsoleRecord
->ReenterCount
++;
1068 else if (SetReenterCountRequest
->fIncDec
== VDM_DEC_REENTER_COUNT
)
1070 ConsoleRecord
->ReenterCount
--;
1071 if (ConsoleRecord
->ServerEvent
!= NULL
) NtSetEvent(ConsoleRecord
->ServerEvent
, NULL
);
1073 else Status
= STATUS_INVALID_PARAMETER
;
1076 /* Leave the critical section */
1077 RtlLeaveCriticalSection(&DosCriticalSection
);
1082 CSR_API(BaseSrvSetVDMCurDirs
)
1085 PBASE_GETSET_VDM_CURDIRS VDMCurrentDirsRequest
= &((PBASE_API_MESSAGE
)ApiMessage
)->Data
.VDMCurrentDirsRequest
;
1086 PVDM_CONSOLE_RECORD ConsoleRecord
;
1087 PCHAR Buffer
= NULL
;
1089 /* Validate the input buffer */
1090 if (!CsrValidateMessageBuffer(ApiMessage
,
1091 (PVOID
*)&VDMCurrentDirsRequest
->lpszzCurDirs
,
1092 VDMCurrentDirsRequest
->cchCurDirs
,
1093 sizeof(*VDMCurrentDirsRequest
->lpszzCurDirs
)))
1095 return STATUS_INVALID_PARAMETER
;
1098 /* Enter the critical section */
1099 RtlEnterCriticalSection(&DosCriticalSection
);
1101 /* Find the console record */
1102 Status
= BaseSrvGetConsoleRecord(VDMCurrentDirsRequest
->ConsoleHandle
, &ConsoleRecord
);
1103 if (!NT_SUCCESS(Status
)) goto Cleanup
;
1105 if (ConsoleRecord
->CurrentDirs
== NULL
)
1107 /* Allocate memory for the current directory information */
1108 Buffer
= RtlAllocateHeap(BaseSrvHeap
,
1110 VDMCurrentDirsRequest
->cchCurDirs
);
1114 /* Resize the amount of allocated memory */
1115 Buffer
= RtlReAllocateHeap(BaseSrvHeap
,
1117 ConsoleRecord
->CurrentDirs
,
1118 VDMCurrentDirsRequest
->cchCurDirs
);
1123 /* Allocation failed */
1124 Status
= STATUS_NO_MEMORY
;
1128 /* Update the console record */
1129 ConsoleRecord
->CurrentDirs
= Buffer
;
1130 ConsoleRecord
->CurDirsLength
= VDMCurrentDirsRequest
->cchCurDirs
;
1133 RtlMoveMemory(ConsoleRecord
->CurrentDirs
,
1134 VDMCurrentDirsRequest
->lpszzCurDirs
,
1135 VDMCurrentDirsRequest
->cchCurDirs
);
1138 /* Leave the critical section */
1139 RtlLeaveCriticalSection(&DosCriticalSection
);
1144 CSR_API(BaseSrvGetVDMCurDirs
)
1147 PBASE_GETSET_VDM_CURDIRS VDMCurrentDirsRequest
= &((PBASE_API_MESSAGE
)ApiMessage
)->Data
.VDMCurrentDirsRequest
;
1148 PVDM_CONSOLE_RECORD ConsoleRecord
;
1150 /* Validate the output buffer */
1151 if (!CsrValidateMessageBuffer(ApiMessage
,
1152 (PVOID
*)&VDMCurrentDirsRequest
->lpszzCurDirs
,
1153 VDMCurrentDirsRequest
->cchCurDirs
,
1154 sizeof(*VDMCurrentDirsRequest
->lpszzCurDirs
)))
1156 return STATUS_INVALID_PARAMETER
;
1159 /* Enter the critical section */
1160 RtlEnterCriticalSection(&DosCriticalSection
);
1162 /* Find the console record */
1163 Status
= BaseSrvGetConsoleRecord(VDMCurrentDirsRequest
->ConsoleHandle
, &ConsoleRecord
);
1164 if (!NT_SUCCESS(Status
)) goto Cleanup
;
1166 /* Return the actual size of the current directory information */
1167 VDMCurrentDirsRequest
->cchCurDirs
= ConsoleRecord
->CurDirsLength
;
1169 /* Check if the buffer is large enough */
1170 if (VDMCurrentDirsRequest
->cchCurDirs
< ConsoleRecord
->CurDirsLength
)
1172 Status
= STATUS_BUFFER_TOO_SMALL
;
1177 RtlMoveMemory(VDMCurrentDirsRequest
->lpszzCurDirs
,
1178 ConsoleRecord
->CurrentDirs
,
1179 ConsoleRecord
->CurDirsLength
);
1182 /* Leave the critical section */
1183 RtlLeaveCriticalSection(&DosCriticalSection
);
1188 CSR_API(BaseSrvBatNotification
)
1190 DPRINT1("%s not yet implemented\n", __FUNCTION__
);
1191 return STATUS_NOT_IMPLEMENTED
;
1194 CSR_API(BaseSrvRegisterWowExec
)
1196 DPRINT1("%s not yet implemented\n", __FUNCTION__
);
1197 return STATUS_NOT_IMPLEMENTED
;
1200 CSR_API(BaseSrvRefreshIniFileMapping
)
1202 DPRINT1("%s not yet implemented\n", __FUNCTION__
);
1203 return STATUS_NOT_IMPLEMENTED
;