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 ULONG NTAPI
GetNextDosSesId(VOID
)
47 PVDM_CONSOLE_RECORD CurrentRecord
= NULL
;
50 /* Search for an available session ID */
51 for (SessionId
= 1; SessionId
!= 0; SessionId
++)
55 /* Check if the ID is already in use */
56 for (i
= VDMConsoleListHead
.Flink
; i
!= &VDMConsoleListHead
; i
= i
->Flink
)
58 CurrentRecord
= CONTAINING_RECORD(i
, VDM_CONSOLE_RECORD
, Entry
);
59 if (CurrentRecord
->SessionId
== SessionId
) Found
= TRUE
;
62 /* If not, we found one */
66 ASSERT(SessionId
!= 0);
68 /* Return the session ID */
72 BOOLEAN NTAPI
BaseSrvIsVdmAllowed(VOID
)
75 BOOLEAN VdmAllowed
= TRUE
;
76 HANDLE RootKey
, KeyHandle
;
77 UNICODE_STRING KeyName
, ValueName
, MachineKeyName
;
78 OBJECT_ATTRIBUTES Attributes
;
79 UCHAR ValueBuffer
[sizeof(KEY_VALUE_PARTIAL_INFORMATION
) + sizeof(ULONG
)];
80 PKEY_VALUE_PARTIAL_INFORMATION ValueInfo
= (PKEY_VALUE_PARTIAL_INFORMATION
)ValueBuffer
;
83 /* Initialize the unicode strings */
84 RtlInitUnicodeString(&MachineKeyName
, L
"\\Registry\\Machine");
85 RtlInitUnicodeString(&KeyName
, VDM_POLICY_KEY_NAME
);
86 RtlInitUnicodeString(&ValueName
, VDM_DISALLOWED_VALUE_NAME
);
88 InitializeObjectAttributes(&Attributes
,
94 /* Open the local machine key */
95 Status
= NtOpenKey(&RootKey
, KEY_READ
, &Attributes
);
96 if (!NT_SUCCESS(Status
)) return FALSE
;
98 InitializeObjectAttributes(&Attributes
,
100 OBJ_CASE_INSENSITIVE
,
104 /* Open the policy key in the local machine hive, if it exists */
105 if (NT_SUCCESS(NtOpenKey(&KeyHandle
, KEY_READ
, &Attributes
)))
107 /* Read the value, if it's set */
108 if (NT_SUCCESS(NtQueryValueKey(KeyHandle
,
110 KeyValuePartialInformation
,
115 if (*((PULONG
)ValueInfo
->Data
))
117 /* The VDM has been disabled in the registry */
125 /* Close the local machine key */
128 /* If it's disabled system-wide, there's no need to check the user key */
129 if (!VdmAllowed
) return FALSE
;
131 /* Open the current user key of the client */
132 if (!CsrImpersonateClient(NULL
)) return VdmAllowed
;
133 Status
= RtlOpenCurrentUser(KEY_READ
, &RootKey
);
136 /* If that fails, return the system-wide setting */
137 if (!NT_SUCCESS(Status
)) return VdmAllowed
;
139 InitializeObjectAttributes(&Attributes
,
141 OBJ_CASE_INSENSITIVE
,
145 /* Open the policy key in the current user hive, if it exists */
146 if (NT_SUCCESS(NtOpenKey(&KeyHandle
, KEY_READ
, &Attributes
)))
148 /* Read the value, if it's set */
149 if (NT_SUCCESS(NtQueryValueKey(KeyHandle
,
151 KeyValuePartialInformation
,
156 if (*((PULONG
)ValueInfo
->Data
))
158 /* The VDM has been disabled in the registry */
169 NTSTATUS NTAPI
BaseSrvCreatePairWaitHandles(PHANDLE ServerEvent
, PHANDLE ClientEvent
)
173 /* Create the event */
174 Status
= NtCreateEvent(ServerEvent
, EVENT_ALL_ACCESS
, NULL
, NotificationEvent
, FALSE
);
175 if (!NT_SUCCESS(Status
)) return Status
;
177 /* Duplicate the event into the client process */
178 Status
= NtDuplicateObject(NtCurrentProcess(),
180 CsrGetClientThread()->Process
->ProcessHandle
,
184 DUPLICATE_SAME_ATTRIBUTES
| DUPLICATE_SAME_ACCESS
);
186 if (!NT_SUCCESS(Status
)) NtClose(*ServerEvent
);
190 VOID NTAPI
BaseInitializeVDM(VOID
)
192 /* Initialize the list head */
193 InitializeListHead(&VDMConsoleListHead
);
195 /* Initialize the critical section */
196 RtlInitializeCriticalSection(&DosCriticalSection
);
197 RtlInitializeCriticalSection(&WowCriticalSection
);
200 /* PUBLIC SERVER APIS *********************************************************/
202 CSR_API(BaseSrvCheckVDM
)
205 PBASE_CHECK_VDM CheckVdmRequest
= &((PBASE_API_MESSAGE
)ApiMessage
)->Data
.CheckVDMRequest
;
206 PRTL_CRITICAL_SECTION CriticalSection
= NULL
;
207 PVDM_CONSOLE_RECORD ConsoleRecord
= NULL
;
208 PVDM_DOS_RECORD DosRecord
= NULL
;
209 BOOLEAN NewConsoleRecord
= FALSE
;
211 /* Don't do anything if the VDM has been disabled in the registry */
212 if (!BaseSrvIsVdmAllowed()) return STATUS_ACCESS_DENIED
;
214 /* Validate the message buffers */
215 if (!CsrValidateMessageBuffer(ApiMessage
,
216 (PVOID
*)&CheckVdmRequest
->CmdLine
,
217 CheckVdmRequest
->CmdLen
,
218 sizeof(*CheckVdmRequest
->CmdLine
))
219 || !CsrValidateMessageBuffer(ApiMessage
,
220 (PVOID
*)&CheckVdmRequest
->AppName
,
221 CheckVdmRequest
->AppLen
,
222 sizeof(*CheckVdmRequest
->AppName
))
223 || !CsrValidateMessageBuffer(ApiMessage
,
224 (PVOID
*)&CheckVdmRequest
->PifFile
,
225 CheckVdmRequest
->PifLen
,
226 sizeof(*CheckVdmRequest
->PifFile
))
227 || !CsrValidateMessageBuffer(ApiMessage
,
228 (PVOID
*)&CheckVdmRequest
->CurDirectory
,
229 CheckVdmRequest
->CurDirectoryLen
,
230 sizeof(*CheckVdmRequest
->CurDirectory
))
231 || !CsrValidateMessageBuffer(ApiMessage
,
232 (PVOID
*)&CheckVdmRequest
->Desktop
,
233 CheckVdmRequest
->DesktopLen
,
234 sizeof(*CheckVdmRequest
->Desktop
))
235 || !CsrValidateMessageBuffer(ApiMessage
,
236 (PVOID
*)&CheckVdmRequest
->Title
,
237 CheckVdmRequest
->TitleLen
,
238 sizeof(*CheckVdmRequest
->Title
))
239 || !CsrValidateMessageBuffer(ApiMessage
,
240 (PVOID
*)&CheckVdmRequest
->Reserved
,
241 CheckVdmRequest
->ReservedLen
,
242 sizeof(*CheckVdmRequest
->Reserved
)))
244 return STATUS_INVALID_PARAMETER
;
247 CriticalSection
= (CheckVdmRequest
->BinaryType
!= BINARY_TYPE_SEPARATE_WOW
)
248 ? &DosCriticalSection
249 : &WowCriticalSection
;
251 /* Enter the critical section */
252 RtlEnterCriticalSection(CriticalSection
);
254 /* Check if this is a DOS or WOW VDM */
255 if (CheckVdmRequest
->BinaryType
!= BINARY_TYPE_SEPARATE_WOW
)
257 /* Get the console record */
258 Status
= BaseSrvGetConsoleRecord(CheckVdmRequest
->ConsoleHandle
,
261 if (!NT_SUCCESS(Status
))
263 /* Allocate a new console record */
264 ConsoleRecord
= (PVDM_CONSOLE_RECORD
)RtlAllocateHeap(BaseSrvHeap
,
266 sizeof(VDM_CONSOLE_RECORD
));
267 if (ConsoleRecord
== NULL
)
269 Status
= STATUS_NO_MEMORY
;
273 /* Initialize the console record */
274 ConsoleRecord
->ConsoleHandle
= CheckVdmRequest
->ConsoleHandle
;
275 ConsoleRecord
->CurrentDirs
= NULL
;
276 ConsoleRecord
->CurDirsLength
= 0;
277 ConsoleRecord
->SessionId
= GetNextDosSesId();
278 InitializeListHead(&ConsoleRecord
->DosListHead
);
279 // TODO: The console record structure is incomplete
281 /* Remember that the console record was allocated here */
282 NewConsoleRecord
= TRUE
;
285 /* Allocate a new DOS record */
286 DosRecord
= (PVDM_DOS_RECORD
)RtlAllocateHeap(BaseSrvHeap
,
288 sizeof(VDM_DOS_RECORD
));
289 if (DosRecord
== NULL
)
291 Status
= STATUS_NO_MEMORY
;
295 /* Initialize the DOS record */
296 DosRecord
->State
= NewConsoleRecord
? VDM_NOT_LOADED
: VDM_READY
;
297 DosRecord
->ExitCode
= 0;
298 // TODO: The DOS record structure is incomplete
300 Status
= BaseSrvCreatePairWaitHandles(&DosRecord
->ServerEvent
, &DosRecord
->ClientEvent
);
301 if (!NT_SUCCESS(Status
)) goto Cleanup
;
303 /* Add the DOS record */
304 InsertHeadList(&ConsoleRecord
->DosListHead
, &DosRecord
->Entry
);
306 if (NewConsoleRecord
)
308 /* Add the console record */
309 InsertTailList(&VDMConsoleListHead
, &ConsoleRecord
->Entry
);
312 CheckVdmRequest
->VDMState
= DosRecord
->State
;
313 Status
= STATUS_SUCCESS
;
317 // TODO: NOT IMPLEMENTED
319 return STATUS_NOT_IMPLEMENTED
;
323 /* Check if it failed */
324 if (!NT_SUCCESS(Status
))
326 /* Free the DOS record */
327 if (DosRecord
!= NULL
)
329 RtlFreeHeap(BaseSrvHeap
, 0, DosRecord
);
333 /* Free the console record if it was allocated here */
334 if (NewConsoleRecord
)
336 RtlFreeHeap(BaseSrvHeap
, 0, ConsoleRecord
);
337 ConsoleRecord
= NULL
;
341 /* Leave the critical section */
342 RtlLeaveCriticalSection(CriticalSection
);
347 CSR_API(BaseSrvUpdateVDMEntry
)
349 DPRINT1("%s not yet implemented\n", __FUNCTION__
);
350 return STATUS_NOT_IMPLEMENTED
;
353 CSR_API(BaseSrvGetNextVDMCommand
)
355 DPRINT1("%s not yet implemented\n", __FUNCTION__
);
356 return STATUS_NOT_IMPLEMENTED
;
359 CSR_API(BaseSrvExitVDM
)
362 PBASE_EXIT_VDM ExitVdmRequest
= &((PBASE_API_MESSAGE
)ApiMessage
)->Data
.ExitVDMRequest
;
363 PRTL_CRITICAL_SECTION CriticalSection
= NULL
;
364 PVDM_CONSOLE_RECORD ConsoleRecord
= NULL
;
365 PVDM_DOS_RECORD DosRecord
;
367 CriticalSection
= (ExitVdmRequest
->iWowTask
== 0)
368 ? &DosCriticalSection
369 : &WowCriticalSection
;
371 /* Enter the critical section */
372 RtlEnterCriticalSection(CriticalSection
);
374 if (ExitVdmRequest
->iWowTask
== 0)
376 /* Get the console record */
377 Status
= BaseSrvGetConsoleRecord(ExitVdmRequest
->ConsoleHandle
, &ConsoleRecord
);
378 if (!NT_SUCCESS(Status
)) goto Cleanup
;
380 /* Cleanup the DOS records */
381 while (ConsoleRecord
->DosListHead
.Flink
!= &ConsoleRecord
->DosListHead
)
383 DosRecord
= CONTAINING_RECORD(ConsoleRecord
->DosListHead
.Flink
,
387 /* Remove the DOS entry */
388 RemoveEntryList(&DosRecord
->Entry
);
389 RtlFreeHeap(BaseSrvHeap
, 0, DosRecord
);
392 if (ConsoleRecord
->CurrentDirs
!= NULL
)
394 /* Free the current directories */
395 RtlFreeHeap(BaseSrvHeap
, 0, ConsoleRecord
->CurrentDirs
);
396 ConsoleRecord
->CurrentDirs
= NULL
;
397 ConsoleRecord
->CurDirsLength
= 0;
400 /* Remove the console record */
401 RemoveEntryList(&ConsoleRecord
->Entry
);
402 RtlFreeHeap(BaseSrvHeap
, 0, ConsoleRecord
);
406 // TODO: NOT IMPLEMENTED
408 return STATUS_NOT_IMPLEMENTED
;
412 /* Leave the critical section */
413 RtlLeaveCriticalSection(CriticalSection
);
418 CSR_API(BaseSrvIsFirstVDM
)
420 PBASE_IS_FIRST_VDM IsFirstVDMRequest
= &((PBASE_API_MESSAGE
)ApiMessage
)->Data
.IsFirstVDMRequest
;
422 /* Return the result */
423 IsFirstVDMRequest
->FirstVDM
= FirstVDM
;
425 /* Clear the first VDM flag */
428 return STATUS_SUCCESS
;
431 CSR_API(BaseSrvGetVDMExitCode
)
434 PBASE_GET_VDM_EXIT_CODE GetVDMExitCodeRequest
= &((PBASE_API_MESSAGE
)ApiMessage
)->Data
.GetVDMExitCodeRequest
;
435 PLIST_ENTRY i
= NULL
;
436 PVDM_CONSOLE_RECORD ConsoleRecord
= NULL
;
437 PVDM_DOS_RECORD DosRecord
= NULL
;
439 /* Enter the critical section */
440 RtlEnterCriticalSection(&DosCriticalSection
);
442 /* Get the console record */
443 Status
= BaseSrvGetConsoleRecord(GetVDMExitCodeRequest
->ConsoleHandle
, &ConsoleRecord
);
444 if (!NT_SUCCESS(Status
)) goto Cleanup
;
446 /* Search for a DOS record that has the same parent process handle */
447 for (i
= ConsoleRecord
->DosListHead
.Flink
; i
!= &ConsoleRecord
->DosListHead
; i
= i
->Flink
)
449 DosRecord
= CONTAINING_RECORD(i
, VDM_DOS_RECORD
, Entry
);
450 if (DosRecord
->ClientEvent
== GetVDMExitCodeRequest
->hParent
) break;
453 /* Check if no DOS record was found */
454 if (i
== &ConsoleRecord
->DosListHead
)
456 Status
= STATUS_NOT_FOUND
;
460 /* Check if this task is still running */
461 if (DosRecord
->State
== VDM_READY
)
463 GetVDMExitCodeRequest
->ExitCode
= STATUS_PENDING
;
467 /* Return the exit code */
468 GetVDMExitCodeRequest
->ExitCode
= DosRecord
->ExitCode
;
470 /* Since this is a zombie task record, remove it */
471 RemoveEntryList(&DosRecord
->Entry
);
472 RtlFreeHeap(BaseSrvHeap
, 0, DosRecord
);
475 /* Leave the critical section */
476 RtlLeaveCriticalSection(&DosCriticalSection
);
481 CSR_API(BaseSrvSetReenterCount
)
483 DPRINT1("%s not yet implemented\n", __FUNCTION__
);
484 return STATUS_NOT_IMPLEMENTED
;
487 CSR_API(BaseSrvSetVDMCurDirs
)
490 PBASE_GETSET_VDM_CURDIRS VDMCurrentDirsRequest
= &((PBASE_API_MESSAGE
)ApiMessage
)->Data
.VDMCurrentDirsRequest
;
491 PVDM_CONSOLE_RECORD ConsoleRecord
;
494 /* Validate the input buffer */
495 if (!CsrValidateMessageBuffer(ApiMessage
,
496 (PVOID
*)&VDMCurrentDirsRequest
->lpszzCurDirs
,
497 VDMCurrentDirsRequest
->cchCurDirs
,
498 sizeof(*VDMCurrentDirsRequest
->lpszzCurDirs
)))
500 return STATUS_INVALID_PARAMETER
;
503 /* Enter the critical section */
504 RtlEnterCriticalSection(&DosCriticalSection
);
506 /* Find the console record */
507 Status
= BaseSrvGetConsoleRecord(VDMCurrentDirsRequest
->ConsoleHandle
, &ConsoleRecord
);
508 if (!NT_SUCCESS(Status
)) goto Cleanup
;
510 if (ConsoleRecord
->CurrentDirs
== NULL
)
512 /* Allocate memory for the current directory information */
513 Buffer
= RtlAllocateHeap(BaseSrvHeap
,
515 VDMCurrentDirsRequest
->cchCurDirs
);
519 /* Resize the amount of allocated memory */
520 Buffer
= RtlReAllocateHeap(BaseSrvHeap
,
522 ConsoleRecord
->CurrentDirs
,
523 VDMCurrentDirsRequest
->cchCurDirs
);
528 /* Allocation failed */
529 Status
= STATUS_NO_MEMORY
;
533 /* Update the console record */
534 ConsoleRecord
->CurrentDirs
= Buffer
;
535 ConsoleRecord
->CurDirsLength
= VDMCurrentDirsRequest
->cchCurDirs
;
538 RtlMoveMemory(ConsoleRecord
->CurrentDirs
,
539 VDMCurrentDirsRequest
->lpszzCurDirs
,
540 VDMCurrentDirsRequest
->cchCurDirs
);
543 /* Leave the critical section */
544 RtlLeaveCriticalSection(&DosCriticalSection
);
549 CSR_API(BaseSrvGetVDMCurDirs
)
552 PBASE_GETSET_VDM_CURDIRS VDMCurrentDirsRequest
= &((PBASE_API_MESSAGE
)ApiMessage
)->Data
.VDMCurrentDirsRequest
;
553 PVDM_CONSOLE_RECORD ConsoleRecord
;
555 /* Validate the output buffer */
556 if (!CsrValidateMessageBuffer(ApiMessage
,
557 (PVOID
*)&VDMCurrentDirsRequest
->lpszzCurDirs
,
558 VDMCurrentDirsRequest
->cchCurDirs
,
559 sizeof(*VDMCurrentDirsRequest
->lpszzCurDirs
)))
561 return STATUS_INVALID_PARAMETER
;
564 /* Enter the critical section */
565 RtlEnterCriticalSection(&DosCriticalSection
);
567 /* Find the console record */
568 Status
= BaseSrvGetConsoleRecord(VDMCurrentDirsRequest
->ConsoleHandle
, &ConsoleRecord
);
569 if (!NT_SUCCESS(Status
)) goto Cleanup
;
571 /* Return the actual size of the current directory information */
572 VDMCurrentDirsRequest
->cchCurDirs
= ConsoleRecord
->CurDirsLength
;
574 /* Check if the buffer is large enough */
575 if (VDMCurrentDirsRequest
->cchCurDirs
< ConsoleRecord
->CurDirsLength
)
577 Status
= STATUS_BUFFER_TOO_SMALL
;
582 RtlMoveMemory(VDMCurrentDirsRequest
->lpszzCurDirs
,
583 ConsoleRecord
->CurrentDirs
,
584 ConsoleRecord
->CurDirsLength
);
587 /* Leave the critical section */
588 RtlLeaveCriticalSection(&DosCriticalSection
);
593 CSR_API(BaseSrvBatNotification
)
595 DPRINT1("%s not yet implemented\n", __FUNCTION__
);
596 return STATUS_NOT_IMPLEMENTED
;
599 CSR_API(BaseSrvRegisterWowExec
)
601 DPRINT1("%s not yet implemented\n", __FUNCTION__
);
602 return STATUS_NOT_IMPLEMENTED
;
605 CSR_API(BaseSrvRefreshIniFileMapping
)
607 DPRINT1("%s not yet implemented\n", __FUNCTION__
);
608 return STATUS_NOT_IMPLEMENTED
;