xref: /reactos/subsystems/win/basesrv/vdm.c (revision 0c2cdcae)
1 /*
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>
8  */
9 
10 /* INCLUDES *******************************************************************/
11 
12 #include "basesrv.h"
13 #include "vdm.h"
14 
15 #define NDEBUG
16 #include <debug.h>
17 
18 /* GLOBALS ********************************************************************/
19 
20 BOOLEAN FirstVDM = TRUE;
21 LIST_ENTRY VDMConsoleListHead;
22 RTL_CRITICAL_SECTION DosCriticalSection;
23 RTL_CRITICAL_SECTION WowCriticalSection;
24 
25 /* HELPER FUNCTIONS ***********************************************************/
26 
27 PVDM_CONSOLE_RECORD BaseSrvCreateConsoleRecord(VOID)
28 {
29     PVDM_CONSOLE_RECORD ConsoleRecord;
30 
31     ConsoleRecord = RtlAllocateHeap(BaseSrvHeap, HEAP_ZERO_MEMORY,
32                                     sizeof(VDM_CONSOLE_RECORD));
33     if (ConsoleRecord == NULL)
34         return NULL;
35 
36     /* Initialize the console record */
37     ConsoleRecord->ConsoleHandle = NULL;
38     ConsoleRecord->ProcessHandle = NULL;
39     ConsoleRecord->ServerEvent = ConsoleRecord->ClientEvent = NULL;
40     ConsoleRecord->ReenterCount = 0;
41     ConsoleRecord->CurrentDirs = NULL;
42     ConsoleRecord->CurDirsLength = 0;
43     ConsoleRecord->SessionId = 0;
44     InitializeListHead(&ConsoleRecord->DosListHead);
45 
46     return ConsoleRecord;
47 }
48 
49 NTSTATUS BaseSrvGetConsoleRecord(HANDLE ConsoleHandle, PVDM_CONSOLE_RECORD *Record)
50 {
51     PLIST_ENTRY i;
52     PVDM_CONSOLE_RECORD CurrentRecord = NULL;
53 
54     /* NULL is not a valid console handle */
55     if (ConsoleHandle == NULL) return STATUS_INVALID_PARAMETER;
56 
57     /* Search for a record that has the same console handle */
58     for (i = VDMConsoleListHead.Flink; i != &VDMConsoleListHead; i = i->Flink)
59     {
60         CurrentRecord = CONTAINING_RECORD(i, VDM_CONSOLE_RECORD, Entry);
61         if (CurrentRecord->ConsoleHandle == ConsoleHandle) break;
62     }
63 
64     /* Check if nothing was found */
65     if (i == &VDMConsoleListHead) CurrentRecord = NULL;
66 
67     *Record = CurrentRecord;
68     return CurrentRecord ? STATUS_SUCCESS : STATUS_NOT_FOUND;
69 }
70 
71 VOID BaseSrvDestroyConsoleRecord(PVDM_CONSOLE_RECORD ConsoleRecord)
72 {
73     if (ConsoleRecord->CurrentDirs != NULL)
74     {
75         /* Free the current directories */
76         RtlFreeHeap(BaseSrvHeap, 0, ConsoleRecord->CurrentDirs);
77         ConsoleRecord->CurrentDirs = NULL;
78         ConsoleRecord->CurDirsLength = 0;
79     }
80 
81     /* Close the process handle */
82     if (ConsoleRecord->ProcessHandle)
83         NtClose(ConsoleRecord->ProcessHandle);
84 
85     /* Close the event handle */
86     if (ConsoleRecord->ServerEvent)
87         NtClose(ConsoleRecord->ServerEvent);
88 
89     /* Remove the console record */
90     // RemoveEntryList(&ConsoleRecord->Entry);
91     RtlFreeHeap(BaseSrvHeap, 0, ConsoleRecord);
92 }
93 
94 NTSTATUS GetConsoleRecordBySessionId(ULONG TaskId, PVDM_CONSOLE_RECORD *Record)
95 {
96     PLIST_ENTRY i;
97     PVDM_CONSOLE_RECORD CurrentRecord = NULL;
98 
99     /* Search for a record that has the same console handle */
100     for (i = VDMConsoleListHead.Flink; i != &VDMConsoleListHead; i = i->Flink)
101     {
102         CurrentRecord = CONTAINING_RECORD(i, VDM_CONSOLE_RECORD, Entry);
103         if (CurrentRecord->SessionId == TaskId) break;
104     }
105 
106     /* Check if nothing was found */
107     if (i == &VDMConsoleListHead) CurrentRecord = NULL;
108 
109     *Record = CurrentRecord;
110     return CurrentRecord ? STATUS_SUCCESS : STATUS_NOT_FOUND;
111 }
112 
113 ULONG GetNextDosSesId(VOID)
114 {
115     ULONG SessionId;
116     PLIST_ENTRY i;
117     PVDM_CONSOLE_RECORD CurrentRecord = NULL;
118     BOOLEAN Found;
119 
120     /* Search for an available session ID */
121     for (SessionId = 1; SessionId != 0; SessionId++)
122     {
123         Found = FALSE;
124 
125         /* Check if the ID is already in use */
126         for (i = VDMConsoleListHead.Flink; i != &VDMConsoleListHead; i = i->Flink)
127         {
128             CurrentRecord = CONTAINING_RECORD(i, VDM_CONSOLE_RECORD, Entry);
129             if (CurrentRecord->SessionId == SessionId) Found = TRUE;
130         }
131 
132         /* If not, we found one */
133         if (!Found) break;
134     }
135 
136     ASSERT(SessionId != 0);
137 
138     /* Return the session ID */
139     return SessionId;
140 }
141 
142 BOOLEAN BaseSrvIsVdmAllowed(VOID)
143 {
144     NTSTATUS Status;
145     BOOLEAN VdmAllowed = TRUE;
146     HANDLE RootKey, KeyHandle;
147     UNICODE_STRING KeyName, ValueName, MachineKeyName;
148     OBJECT_ATTRIBUTES Attributes;
149     UCHAR ValueBuffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(ULONG)];
150     PKEY_VALUE_PARTIAL_INFORMATION ValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)ValueBuffer;
151     ULONG ActualSize;
152 
153     /* Initialize the unicode strings */
154     RtlInitUnicodeString(&MachineKeyName, L"\\Registry\\Machine");
155     RtlInitUnicodeString(&KeyName, VDM_POLICY_KEY_NAME);
156     RtlInitUnicodeString(&ValueName, VDM_DISALLOWED_VALUE_NAME);
157 
158     InitializeObjectAttributes(&Attributes,
159                                &MachineKeyName,
160                                OBJ_CASE_INSENSITIVE,
161                                NULL,
162                                NULL);
163 
164     /* Open the local machine key */
165     Status = NtOpenKey(&RootKey, KEY_READ, &Attributes);
166     if (!NT_SUCCESS(Status)) return FALSE;
167 
168     InitializeObjectAttributes(&Attributes,
169                                &KeyName,
170                                OBJ_CASE_INSENSITIVE,
171                                RootKey,
172                                NULL);
173 
174     /* Open the policy key in the local machine hive, if it exists */
175     if (NT_SUCCESS(NtOpenKey(&KeyHandle, KEY_READ, &Attributes)))
176     {
177         /* Read the value, if it's set */
178         if (NT_SUCCESS(NtQueryValueKey(KeyHandle,
179                                        &ValueName,
180                                        KeyValuePartialInformation,
181                                        ValueInfo,
182                                        sizeof(ValueBuffer),
183                                        &ActualSize)))
184         {
185             if (*((PULONG)ValueInfo->Data))
186             {
187                 /* The VDM has been disabled in the registry */
188                 VdmAllowed = FALSE;
189             }
190         }
191 
192         NtClose(KeyHandle);
193     }
194 
195     /* Close the local machine key */
196     NtClose(RootKey);
197 
198     /* If it's disabled system-wide, there's no need to check the user key */
199     if (!VdmAllowed) return FALSE;
200 
201     /* Open the current user key of the client */
202     if (!CsrImpersonateClient(NULL)) return VdmAllowed;
203     Status = RtlOpenCurrentUser(KEY_READ, &RootKey);
204     CsrRevertToSelf();
205 
206     /* If that fails, return the system-wide setting */
207     if (!NT_SUCCESS(Status)) return VdmAllowed;
208 
209     InitializeObjectAttributes(&Attributes,
210                                &KeyName,
211                                OBJ_CASE_INSENSITIVE,
212                                RootKey,
213                                NULL);
214 
215     /* Open the policy key in the current user hive, if it exists */
216     if (NT_SUCCESS(NtOpenKey(&KeyHandle, KEY_READ, &Attributes)))
217     {
218         /* Read the value, if it's set */
219         if (NT_SUCCESS(NtQueryValueKey(KeyHandle,
220                                        &ValueName,
221                                        KeyValuePartialInformation,
222                                        ValueInfo,
223                                        sizeof(ValueBuffer),
224                                        &ActualSize)))
225         {
226             if (*((PULONG)ValueInfo->Data))
227             {
228                 /* The VDM has been disabled in the registry */
229                 VdmAllowed = FALSE;
230             }
231         }
232 
233         NtClose(KeyHandle);
234     }
235 
236     return VdmAllowed;
237 }
238 
239 NTSTATUS BaseSrvCreatePairWaitHandles(PHANDLE ServerEvent, PHANDLE ClientEvent)
240 {
241     NTSTATUS Status;
242 
243     /* Create the event */
244     Status = NtCreateEvent(ServerEvent, EVENT_ALL_ACCESS, NULL, NotificationEvent, FALSE);
245     if (!NT_SUCCESS(Status)) return Status;
246 
247     /* Duplicate the event into the client process */
248     Status = NtDuplicateObject(NtCurrentProcess(),
249                                *ServerEvent,
250                                CsrGetClientThread()->Process->ProcessHandle,
251                                ClientEvent,
252                                0,
253                                0,
254                                DUPLICATE_SAME_ATTRIBUTES | DUPLICATE_SAME_ACCESS);
255 
256     if (!NT_SUCCESS(Status)) NtClose(*ServerEvent);
257     return Status;
258 }
259 
260 VOID BaseSrvDestroyPairWaitHandles(HANDLE ServerEvent, HANDLE ClientEvent)
261 {
262     if (ServerEvent) NtClose(ServerEvent);
263     if (ClientEvent)
264     {
265         /* Close the remote handle */
266         NtDuplicateObject(CsrGetClientThread()->Process->ProcessHandle,
267                           ClientEvent,
268                           NULL,
269                           NULL,
270                           0,
271                           0,
272                           DUPLICATE_CLOSE_SOURCE);
273     }
274 }
275 
276 /* WOW SUPPORT FUNCTIONS ******************************************************/
277 
278 /* DOS SUPPORT FUNCTIONS ******************************************************/
279 
280 VOID BaseSrvFreeVDMInfo(PVDM_COMMAND_INFO CommandInfo)
281 {
282     /* Free the allocated structure members */
283     if (CommandInfo->CmdLine != NULL) RtlFreeHeap(BaseSrvHeap, 0, CommandInfo->CmdLine);
284     if (CommandInfo->AppName != NULL) RtlFreeHeap(BaseSrvHeap, 0, CommandInfo->AppName);
285     if (CommandInfo->PifFile != NULL) RtlFreeHeap(BaseSrvHeap, 0, CommandInfo->PifFile);
286     if (CommandInfo->CurDirectory != NULL) RtlFreeHeap(BaseSrvHeap, 0, CommandInfo->CurDirectory);
287     if (CommandInfo->Env != NULL) RtlFreeHeap(BaseSrvHeap, 0, CommandInfo->Env);
288     if (CommandInfo->Desktop != NULL) RtlFreeHeap(BaseSrvHeap, 0, CommandInfo->Desktop);
289     if (CommandInfo->Title != NULL) RtlFreeHeap(BaseSrvHeap, 0, CommandInfo->Title);
290     if (CommandInfo->Reserved != NULL) RtlFreeHeap(BaseSrvHeap, 0, CommandInfo->Reserved);
291 
292     /* Free the structure itself */
293     RtlFreeHeap(BaseSrvHeap, 0, CommandInfo);
294 }
295 
296 VOID BaseSrvCleanupVDMResources(IN PCSR_PROCESS CsrProcess)
297 {
298     ULONG ProcessId = HandleToUlong(CsrProcess->ClientId.UniqueProcess);
299     PVDM_CONSOLE_RECORD ConsoleRecord = NULL;
300     PVDM_DOS_RECORD DosRecord;
301     PLIST_ENTRY i;
302 
303     /* Enter the critical section */
304     RtlEnterCriticalSection(&DosCriticalSection);
305 
306     /* Search for a record that has the same process handle */
307     i = VDMConsoleListHead.Flink;
308     while (i != &VDMConsoleListHead)
309     {
310         ConsoleRecord = CONTAINING_RECORD(i, VDM_CONSOLE_RECORD, Entry);
311         i = i->Flink;
312 
313         if (ConsoleRecord->ProcessId == ProcessId)
314         {
315             if (ConsoleRecord->ServerEvent)
316             {
317                 NtClose(ConsoleRecord->ServerEvent);
318                 ConsoleRecord->ServerEvent = NULL;
319             }
320 
321             /* Cleanup the DOS records */
322             while (!IsListEmpty(&ConsoleRecord->DosListHead))
323             {
324                 DosRecord = CONTAINING_RECORD(ConsoleRecord->DosListHead.Flink,
325                                               VDM_DOS_RECORD, Entry);
326 
327                 /* Set the event and close it */
328                 if (DosRecord->ServerEvent)
329                 {
330                     NtSetEvent(DosRecord->ServerEvent, NULL);
331                     NtClose(DosRecord->ServerEvent);
332                     DosRecord->ServerEvent = NULL;
333                 }
334 
335                 /* Remove the DOS entry */
336                 if (DosRecord->CommandInfo) BaseSrvFreeVDMInfo(DosRecord->CommandInfo);
337                 RemoveEntryList(&DosRecord->Entry);
338                 RtlFreeHeap(BaseSrvHeap, 0, DosRecord);
339             }
340 
341             /* Remove the console record */
342             RemoveEntryList(&ConsoleRecord->Entry);
343             BaseSrvDestroyConsoleRecord(ConsoleRecord);
344         }
345     }
346 
347     /* Leave the critical section */
348     RtlLeaveCriticalSection(&DosCriticalSection);
349 }
350 
351 BOOLEAN BaseSrvCopyCommand(PBASE_CHECK_VDM CheckVdmRequest, PVDM_DOS_RECORD DosRecord)
352 {
353     BOOLEAN Success = FALSE;
354     PVDM_COMMAND_INFO CommandInfo = NULL;
355 
356     /* Allocate the command information structure */
357     CommandInfo = (PVDM_COMMAND_INFO)RtlAllocateHeap(BaseSrvHeap,
358                                                      HEAP_ZERO_MEMORY,
359                                                      sizeof(VDM_COMMAND_INFO));
360     if (CommandInfo == NULL) return FALSE;
361 
362     /* Fill the structure */
363     CommandInfo->TaskId = CheckVdmRequest->iTask;
364     CommandInfo->ExitCode = DosRecord->ExitCode;
365     CommandInfo->CodePage = CheckVdmRequest->CodePage;
366     CommandInfo->StdIn = CheckVdmRequest->StdIn;
367     CommandInfo->StdOut = CheckVdmRequest->StdOut;
368     CommandInfo->StdErr = CheckVdmRequest->StdErr;
369 
370     /* Allocate memory for the command line */
371     CommandInfo->CmdLine = RtlAllocateHeap(BaseSrvHeap,
372                                            HEAP_ZERO_MEMORY,
373                                            CheckVdmRequest->CmdLen);
374     if (CommandInfo->CmdLine == NULL) goto Cleanup;
375 
376     /* Copy the command line */
377     RtlMoveMemory(CommandInfo->CmdLine, CheckVdmRequest->CmdLine, CheckVdmRequest->CmdLen);
378 
379     /* Allocate memory for the application name */
380     CommandInfo->AppName = RtlAllocateHeap(BaseSrvHeap,
381                                            HEAP_ZERO_MEMORY,
382                                            CheckVdmRequest->AppLen);
383     if (CommandInfo->AppName == NULL) goto Cleanup;
384 
385     /* Copy the application name */
386     RtlMoveMemory(CommandInfo->AppName, CheckVdmRequest->AppName, CheckVdmRequest->AppLen);
387 
388     /* Allocate memory for the PIF file name */
389     if (CheckVdmRequest->PifLen != 0)
390     {
391         CommandInfo->PifFile = RtlAllocateHeap(BaseSrvHeap,
392                                                HEAP_ZERO_MEMORY,
393                                                CheckVdmRequest->PifLen);
394         if (CommandInfo->PifFile == NULL) goto Cleanup;
395 
396         /* Copy the PIF file name */
397         RtlMoveMemory(CommandInfo->PifFile, CheckVdmRequest->PifFile, CheckVdmRequest->PifLen);
398     }
399     else CommandInfo->PifFile = NULL;
400 
401     /* Allocate memory for the current directory */
402     if (CheckVdmRequest->CurDirectoryLen != 0)
403     {
404         CommandInfo->CurDirectory = RtlAllocateHeap(BaseSrvHeap,
405                                                     HEAP_ZERO_MEMORY,
406                                                     CheckVdmRequest->CurDirectoryLen);
407         if (CommandInfo->CurDirectory == NULL) goto Cleanup;
408 
409         /* Copy the current directory */
410         RtlMoveMemory(CommandInfo->CurDirectory,
411                       CheckVdmRequest->CurDirectory,
412                       CheckVdmRequest->CurDirectoryLen);
413     }
414     else CommandInfo->CurDirectory = NULL;
415 
416     /* Allocate memory for the environment block */
417     CommandInfo->Env = RtlAllocateHeap(BaseSrvHeap,
418                                        HEAP_ZERO_MEMORY,
419                                        CheckVdmRequest->EnvLen);
420     if (CommandInfo->Env == NULL) goto Cleanup;
421 
422     /* Copy the environment block */
423     RtlMoveMemory(CommandInfo->Env, CheckVdmRequest->Env, CheckVdmRequest->EnvLen);
424 
425     CommandInfo->EnvLen = CheckVdmRequest->EnvLen;
426     RtlMoveMemory(&CommandInfo->StartupInfo,
427                   CheckVdmRequest->StartupInfo,
428                   sizeof(STARTUPINFOA));
429 
430     /* Allocate memory for the desktop */
431     if (CheckVdmRequest->DesktopLen != 0)
432     {
433         CommandInfo->Desktop = RtlAllocateHeap(BaseSrvHeap,
434                                                HEAP_ZERO_MEMORY,
435                                                CheckVdmRequest->DesktopLen);
436         if (CommandInfo->Desktop == NULL) goto Cleanup;
437 
438         /* Copy the desktop name */
439         RtlMoveMemory(CommandInfo->Desktop, CheckVdmRequest->Desktop, CheckVdmRequest->DesktopLen);
440     }
441     else CommandInfo->Desktop = NULL;
442 
443     CommandInfo->DesktopLen = CheckVdmRequest->DesktopLen;
444 
445     /* Allocate memory for the title */
446     if (CheckVdmRequest->TitleLen != 0)
447     {
448         CommandInfo->Title = RtlAllocateHeap(BaseSrvHeap,
449                                              HEAP_ZERO_MEMORY,
450                                              CheckVdmRequest->TitleLen);
451         if (CommandInfo->Title == NULL) goto Cleanup;
452 
453         /* Copy the title */
454         RtlMoveMemory(CommandInfo->Title, CheckVdmRequest->Title, CheckVdmRequest->TitleLen);
455     }
456     else CommandInfo->Title = NULL;
457 
458     CommandInfo->TitleLen = CheckVdmRequest->TitleLen;
459 
460     /* Allocate memory for the reserved field */
461     if (CheckVdmRequest->ReservedLen != 0)
462     {
463         CommandInfo->Reserved = RtlAllocateHeap(BaseSrvHeap,
464                                                 HEAP_ZERO_MEMORY,
465                                                 CheckVdmRequest->ReservedLen);
466         if (CommandInfo->Reserved == NULL) goto Cleanup;
467 
468         /* Copy the reserved field */
469         RtlMoveMemory(CommandInfo->Reserved,
470                       CheckVdmRequest->Reserved,
471                       CheckVdmRequest->ReservedLen);
472     }
473     else CommandInfo->Reserved = NULL;
474 
475     CommandInfo->ReservedLen = CheckVdmRequest->ReservedLen;
476 
477     CommandInfo->CmdLen = CheckVdmRequest->CmdLen;
478     CommandInfo->AppLen = CheckVdmRequest->AppLen;
479     CommandInfo->PifLen = CheckVdmRequest->PifLen;
480     CommandInfo->CurDirectoryLen = CheckVdmRequest->CurDirectoryLen;
481     CommandInfo->VDMState = DosRecord->State;
482     // TODO: Set CommandInfo->CurrentDrive
483     // TODO: Set CommandInfo->ComingFromBat
484 
485     /* Set the DOS record's command structure */
486     DosRecord->CommandInfo = CommandInfo;
487 
488     /* The operation was successful */
489     Success = TRUE;
490 
491 Cleanup:
492     /* If it wasn't successful, free the memory */
493     if (!Success) BaseSrvFreeVDMInfo(CommandInfo);
494 
495     return Success;
496 }
497 
498 NTSTATUS BaseSrvFillCommandInfo(PVDM_COMMAND_INFO CommandInfo,
499                                 PBASE_GET_NEXT_VDM_COMMAND Message)
500 {
501     NTSTATUS Status = STATUS_SUCCESS;
502 
503     /* Copy the data */
504     Message->iTask = CommandInfo->TaskId;
505     Message->StdIn = CommandInfo->StdIn;
506     Message->StdOut = CommandInfo->StdOut;
507     Message->StdErr = CommandInfo->StdErr;
508     Message->CodePage = CommandInfo->CodePage;
509     Message->dwCreationFlags = CommandInfo->CreationFlags;
510     Message->ExitCode = CommandInfo->ExitCode;
511     Message->CurrentDrive = CommandInfo->CurrentDrive;
512     Message->VDMState = CommandInfo->VDMState;
513     Message->fComingFromBat = CommandInfo->ComingFromBat;
514 
515     if (Message->CmdLen >= CommandInfo->CmdLen)
516     {
517         /* Copy the command line */
518         RtlMoveMemory(Message->CmdLine, CommandInfo->CmdLine, CommandInfo->CmdLen);
519     }
520     else Status = STATUS_INVALID_PARAMETER;
521     Message->CmdLen = CommandInfo->CmdLen;
522 
523     if (Message->AppLen >= CommandInfo->AppLen)
524     {
525         /* Copy the application name */
526         RtlMoveMemory(Message->AppName, CommandInfo->AppName, CommandInfo->AppLen);
527     }
528     else Status = STATUS_INVALID_PARAMETER;
529     Message->AppLen = CommandInfo->AppLen;
530 
531     if (Message->PifLen >= CommandInfo->PifLen)
532     {
533         /* Copy the PIF file name */
534         RtlMoveMemory(Message->PifFile, CommandInfo->PifFile, CommandInfo->PifLen);
535     }
536     else Status = STATUS_INVALID_PARAMETER;
537     Message->PifLen = CommandInfo->PifLen;
538 
539     if (Message->CurDirectoryLen >= CommandInfo->CurDirectoryLen)
540     {
541         /* Copy the current directory */
542         RtlMoveMemory(Message->CurDirectory, CommandInfo->CurDirectory, CommandInfo->CurDirectoryLen);
543     }
544     else Status = STATUS_INVALID_PARAMETER;
545     Message->CurDirectoryLen = CommandInfo->CurDirectoryLen;
546 
547     if (Message->EnvLen >= CommandInfo->EnvLen)
548     {
549         /* Copy the environment */
550         RtlMoveMemory(Message->Env, CommandInfo->Env, CommandInfo->EnvLen);
551     }
552     else Status = STATUS_INVALID_PARAMETER;
553     Message->EnvLen = CommandInfo->EnvLen;
554 
555     /* Copy the startup info */
556     RtlMoveMemory(Message->StartupInfo,
557                   &CommandInfo->StartupInfo,
558                   sizeof(STARTUPINFOA));
559 
560     if (Message->DesktopLen >= CommandInfo->DesktopLen)
561     {
562         /* Copy the desktop name */
563         RtlMoveMemory(Message->Desktop, CommandInfo->Desktop, CommandInfo->DesktopLen);
564     }
565     else Status = STATUS_INVALID_PARAMETER;
566     Message->DesktopLen = CommandInfo->DesktopLen;
567 
568     if (Message->TitleLen >= CommandInfo->TitleLen)
569     {
570         /* Copy the title */
571         RtlMoveMemory(Message->Title, CommandInfo->Title, CommandInfo->TitleLen);
572     }
573     else Status = STATUS_INVALID_PARAMETER;
574     Message->TitleLen = CommandInfo->TitleLen;
575 
576     if (Message->ReservedLen >= CommandInfo->ReservedLen)
577     {
578         /* Copy the reserved parameter */
579         RtlMoveMemory(Message->Reserved, CommandInfo->Reserved, CommandInfo->ReservedLen);
580     }
581     else Status = STATUS_INVALID_PARAMETER;
582     Message->ReservedLen = CommandInfo->ReservedLen;
583 
584     return Status;
585 }
586 
587 VOID BaseInitializeVDM(VOID)
588 {
589     /* Initialize the list head */
590     InitializeListHead(&VDMConsoleListHead);
591 
592     /* Initialize the critical sections */
593     RtlInitializeCriticalSection(&DosCriticalSection);
594     RtlInitializeCriticalSection(&WowCriticalSection);
595 }
596 
597 /* PUBLIC SERVER APIS *********************************************************/
598 
599 CSR_API(BaseSrvCheckVDM)
600 {
601     NTSTATUS Status;
602     PBASE_CHECK_VDM CheckVdmRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.CheckVDMRequest;
603     PRTL_CRITICAL_SECTION CriticalSection = NULL;
604     PVDM_CONSOLE_RECORD ConsoleRecord = NULL;
605     PVDM_DOS_RECORD DosRecord = NULL;
606     BOOLEAN NewConsoleRecord = FALSE;
607     BOOLEAN NewDosRecord = FALSE;
608 
609     /* Don't do anything if the VDM has been disabled in the registry */
610     if (!BaseSrvIsVdmAllowed()) return STATUS_VDM_DISALLOWED;
611 
612     /* Validate the message buffers */
613     if (!CsrValidateMessageBuffer(ApiMessage,
614                                   (PVOID*)&CheckVdmRequest->CmdLine,
615                                   CheckVdmRequest->CmdLen,
616                                   sizeof(*CheckVdmRequest->CmdLine))
617         || !CsrValidateMessageBuffer(ApiMessage,
618                                      (PVOID*)&CheckVdmRequest->AppName,
619                                      CheckVdmRequest->AppLen,
620                                      sizeof(*CheckVdmRequest->AppName))
621         || !CsrValidateMessageBuffer(ApiMessage,
622                                      (PVOID*)&CheckVdmRequest->PifFile,
623                                      CheckVdmRequest->PifLen,
624                                      sizeof(*CheckVdmRequest->PifFile))
625         || !CsrValidateMessageBuffer(ApiMessage,
626                                      (PVOID*)&CheckVdmRequest->CurDirectory,
627                                      CheckVdmRequest->CurDirectoryLen,
628                                      sizeof(*CheckVdmRequest->CurDirectory))
629         || !CsrValidateMessageBuffer(ApiMessage,
630                                      (PVOID*)&CheckVdmRequest->Desktop,
631                                      CheckVdmRequest->DesktopLen,
632                                      sizeof(*CheckVdmRequest->Desktop))
633         || !CsrValidateMessageBuffer(ApiMessage,
634                                      (PVOID*)&CheckVdmRequest->Title,
635                                      CheckVdmRequest->TitleLen,
636                                      sizeof(*CheckVdmRequest->Title))
637         || !CsrValidateMessageBuffer(ApiMessage,
638                                      (PVOID*)&CheckVdmRequest->Reserved,
639                                      CheckVdmRequest->ReservedLen,
640                                      sizeof(*CheckVdmRequest->Reserved)))
641     {
642         return STATUS_INVALID_PARAMETER;
643     }
644 
645     CriticalSection = (CheckVdmRequest->BinaryType != BINARY_TYPE_SEPARATE_WOW)
646                       ? &DosCriticalSection
647                       : &WowCriticalSection;
648 
649     /* Enter the critical section */
650     RtlEnterCriticalSection(CriticalSection);
651 
652     /* Check if this is a DOS or WOW VDM */
653     if (CheckVdmRequest->BinaryType != BINARY_TYPE_SEPARATE_WOW)
654     {
655         /* Get the console record */
656         Status = BaseSrvGetConsoleRecord(CheckVdmRequest->ConsoleHandle,
657                                          &ConsoleRecord);
658         if (!NT_SUCCESS(Status))
659         {
660             /* Allocate a new console record */
661             ConsoleRecord = BaseSrvCreateConsoleRecord();
662             if (ConsoleRecord == NULL)
663             {
664                 Status = STATUS_NO_MEMORY;
665                 goto Cleanup;
666             }
667 
668             /* Initialize the console record */
669             ConsoleRecord->ConsoleHandle = CheckVdmRequest->ConsoleHandle;
670             if (ConsoleRecord->ConsoleHandle == NULL)
671             {
672                 /* The parent doesn't have a console, get a new session ID */
673                 ConsoleRecord->SessionId = GetNextDosSesId();
674             }
675             else
676             {
677                 /* No session ID is needed */
678                 ConsoleRecord->SessionId = 0;
679             }
680 
681             /* Remember that the console record was allocated here */
682             NewConsoleRecord = TRUE;
683         }
684 
685         if (!NewConsoleRecord)
686         {
687             /* Get the primary DOS record */
688             DosRecord = (PVDM_DOS_RECORD)CONTAINING_RECORD(ConsoleRecord->DosListHead.Flink,
689                                                            VDM_DOS_RECORD, Entry);
690 
691             if (DosRecord->State != VDM_READY) // == VDM_NOT_READY
692             {
693                 /* Allocate a new DOS record */
694                 DosRecord = (PVDM_DOS_RECORD)RtlAllocateHeap(BaseSrvHeap,
695                                                              HEAP_ZERO_MEMORY,
696                                                              sizeof(VDM_DOS_RECORD));
697                 if (DosRecord == NULL)
698                 {
699                     Status = STATUS_NO_MEMORY;
700                     goto Cleanup;
701                 }
702 
703                 /* Remember that the DOS record was allocated here */
704                 NewDosRecord = TRUE;
705             }
706         }
707         else
708         {
709             /* Allocate a new DOS record */
710             DosRecord = (PVDM_DOS_RECORD)RtlAllocateHeap(BaseSrvHeap,
711                                                          HEAP_ZERO_MEMORY,
712                                                          sizeof(VDM_DOS_RECORD));
713             if (DosRecord == NULL)
714             {
715                 Status = STATUS_NO_MEMORY;
716                 goto Cleanup;
717             }
718 
719             /* Remember that the DOS record was allocated here */
720             NewDosRecord = TRUE;
721         }
722 
723         /* Initialize the DOS record */
724         DosRecord->State = VDM_NOT_READY;
725         DosRecord->ExitCode = 0;
726 
727         /* Translate the input structure into a VDM command structure and set it in the DOS record */
728         if (!BaseSrvCopyCommand(CheckVdmRequest, DosRecord))
729         {
730             /* The only possibility is that an allocation failure occurred */
731             Status = STATUS_NO_MEMORY;
732             goto Cleanup;
733         }
734 
735         if (NewDosRecord)
736         {
737             /* Add the DOS record */
738             InsertHeadList(&ConsoleRecord->DosListHead, &DosRecord->Entry);
739         }
740 
741         if (!NewConsoleRecord)
742         {
743             Status = BaseSrvCreatePairWaitHandles(&DosRecord->ServerEvent, &DosRecord->ClientEvent);
744             if (!NT_SUCCESS(Status)) goto Cleanup;
745 
746             /* Return the client event handle */
747             CheckVdmRequest->WaitObjectForParent = DosRecord->ClientEvent;
748         }
749 
750         // FIXME: We may notify ONLY if ConsoleRecord->nReEntrancy is > 0
751         // in case NewConsoleRecord == FALSE AND NewDosRecord == TRUE.
752         if (ConsoleRecord->ServerEvent)
753         {
754             /* Signal the session event */
755             NtSetEvent(ConsoleRecord->ServerEvent, NULL);
756         }
757 
758         if (NewConsoleRecord)
759         {
760             /* Add the console record */
761             InsertTailList(&VDMConsoleListHead, &ConsoleRecord->Entry);
762         }
763 
764         CheckVdmRequest->iTask = ConsoleRecord->SessionId;
765         CheckVdmRequest->VDMState = NewConsoleRecord ? VDM_NOT_LOADED : VDM_READY;
766         Status = STATUS_SUCCESS;
767     }
768     else
769     {
770         // TODO: NOT IMPLEMENTED
771         UNIMPLEMENTED;
772         Status = STATUS_NOT_IMPLEMENTED;
773     }
774 
775 Cleanup:
776     /* Check if it failed */
777     if (!NT_SUCCESS(Status))
778     {
779         /* Free the DOS record if it was allocated here */
780         if (NewDosRecord)
781         {
782             ASSERT(DosRecord != NULL);
783 
784             BaseSrvDestroyPairWaitHandles(DosRecord->ServerEvent,
785                                           DosRecord->ClientEvent);
786 
787             RtlFreeHeap(BaseSrvHeap, 0, DosRecord);
788             DosRecord = NULL;
789         }
790 
791         /* Free the console record if it was allocated here */
792         if (NewConsoleRecord)
793         {
794             ASSERT(ConsoleRecord != NULL);
795 
796             RtlFreeHeap(BaseSrvHeap, 0, ConsoleRecord);
797             ConsoleRecord = NULL;
798         }
799     }
800 
801     /* Leave the critical section */
802     RtlLeaveCriticalSection(CriticalSection);
803 
804     return Status;
805 }
806 
807 CSR_API(BaseSrvUpdateVDMEntry)
808 {
809     NTSTATUS Status;
810     PBASE_UPDATE_VDM_ENTRY UpdateVdmEntryRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.UpdateVDMEntryRequest;
811     PRTL_CRITICAL_SECTION CriticalSection = NULL;
812     PVDM_CONSOLE_RECORD ConsoleRecord = NULL;
813     PVDM_DOS_RECORD DosRecord = NULL;
814 
815     CriticalSection = (UpdateVdmEntryRequest->BinaryType != BINARY_TYPE_SEPARATE_WOW)
816                       ? &DosCriticalSection
817                       : &WowCriticalSection;
818 
819     /* Enter the critical section */
820     RtlEnterCriticalSection(CriticalSection);
821 
822     /* Check if this is a DOS or WOW VDM */
823     if (UpdateVdmEntryRequest->BinaryType != BINARY_TYPE_SEPARATE_WOW)
824     {
825         if (UpdateVdmEntryRequest->iTask != 0)
826         {
827             /* Get the console record using the task ID */
828             Status = GetConsoleRecordBySessionId(UpdateVdmEntryRequest->iTask,
829                                                  &ConsoleRecord);
830         }
831         else
832         {
833             /* Get the console record using the console handle */
834             Status = BaseSrvGetConsoleRecord(UpdateVdmEntryRequest->ConsoleHandle,
835                                              &ConsoleRecord);
836         }
837 
838         if (!NT_SUCCESS(Status)) goto Cleanup;
839 
840         /* Get the primary DOS record */
841         DosRecord = (PVDM_DOS_RECORD)CONTAINING_RECORD(ConsoleRecord->DosListHead.Flink,
842                                                        VDM_DOS_RECORD, Entry);
843 
844         switch (UpdateVdmEntryRequest->EntryIndex)
845         {
846             case VdmEntryUndo:
847             {
848                 /* Close the server event handle, the client will close the client handle */
849                 NtClose(DosRecord->ServerEvent);
850                 DosRecord->ServerEvent = DosRecord->ClientEvent = NULL;
851 
852                 if (UpdateVdmEntryRequest->VDMCreationState & (VDM_UNDO_PARTIAL | VDM_UNDO_FULL))
853                 {
854                     /* Remove the DOS record */
855                     if (DosRecord->CommandInfo) BaseSrvFreeVDMInfo(DosRecord->CommandInfo);
856                     RemoveEntryList(&DosRecord->Entry);
857                     RtlFreeHeap(BaseSrvHeap, 0, DosRecord);
858 
859                     /*
860                      * Since this is an undo, if that was the only DOS record the VDM
861                      * won't even start, so the console record should be removed too.
862                      */
863                     if (ConsoleRecord->DosListHead.Flink == &ConsoleRecord->DosListHead)
864                     {
865                         RemoveEntryList(&ConsoleRecord->Entry);
866                         BaseSrvDestroyConsoleRecord(ConsoleRecord);
867                     }
868                 }
869 
870                 /* It was successful */
871                 Status = STATUS_SUCCESS;
872 
873                 break;
874             }
875 
876             case VdmEntryUpdateProcess:
877             {
878                 /* Duplicate the VDM process handle */
879                 Status = NtDuplicateObject(CsrGetClientThread()->Process->ProcessHandle,
880                                            UpdateVdmEntryRequest->VDMProcessHandle,
881                                            NtCurrentProcess(),
882                                            &ConsoleRecord->ProcessHandle,
883                                            0,
884                                            0,
885                                            DUPLICATE_SAME_ATTRIBUTES | DUPLICATE_SAME_ACCESS);
886                 if (!NT_SUCCESS(Status)) goto Cleanup;
887 
888                 //
889                 // FIXME! Should we always do the following??
890                 //
891 
892                 /* Create a pair of handles to one event object */
893                 Status = BaseSrvCreatePairWaitHandles(&DosRecord->ServerEvent,
894                                                       &DosRecord->ClientEvent);
895                 if (!NT_SUCCESS(Status)) goto Cleanup;
896 
897                 /* Return the client event handle */
898                 UpdateVdmEntryRequest->WaitObjectForParent = DosRecord->ClientEvent;
899 
900                 break;
901             }
902 
903             case VdmEntryUpdateControlCHandler:
904             {
905                 // TODO: NOT IMPLEMENTED
906                 DPRINT1("BaseSrvUpdateVDMEntry: VdmEntryUpdateControlCHandler not implemented\n");
907                 Status = STATUS_NOT_IMPLEMENTED;
908 
909                 break;
910             }
911 
912             default:
913             {
914                 /* Invalid */
915                 Status = STATUS_INVALID_PARAMETER;
916             }
917         }
918     }
919     else
920     {
921         // TODO: NOT IMPLEMENTED
922         UNIMPLEMENTED;
923         Status = STATUS_NOT_IMPLEMENTED;
924     }
925 
926 Cleanup:
927     /* Leave the critical section */
928     RtlLeaveCriticalSection(CriticalSection);
929 
930     return Status;
931 }
932 
933 CSR_API(BaseSrvGetNextVDMCommand)
934 {
935     NTSTATUS Status;
936     PBASE_GET_NEXT_VDM_COMMAND GetNextVdmCommandRequest =
937         &((PBASE_API_MESSAGE)ApiMessage)->Data.GetNextVDMCommandRequest;
938     PRTL_CRITICAL_SECTION CriticalSection;
939     PLIST_ENTRY i = NULL;
940     PVDM_CONSOLE_RECORD ConsoleRecord = NULL;
941     PVDM_DOS_RECORD DosRecord = NULL;
942 
943     /* Validate the message buffers */
944     if (!CsrValidateMessageBuffer(ApiMessage,
945                                   (PVOID*)&GetNextVdmCommandRequest->CmdLine,
946                                   GetNextVdmCommandRequest->CmdLen,
947                                   sizeof(*GetNextVdmCommandRequest->CmdLine))
948         || !CsrValidateMessageBuffer(ApiMessage,
949                                      (PVOID*)&GetNextVdmCommandRequest->AppName,
950                                      GetNextVdmCommandRequest->AppLen,
951                                      sizeof(*GetNextVdmCommandRequest->AppName))
952         || !CsrValidateMessageBuffer(ApiMessage,
953                                      (PVOID*)&GetNextVdmCommandRequest->PifFile,
954                                      GetNextVdmCommandRequest->PifLen,
955                                      sizeof(*GetNextVdmCommandRequest->PifFile))
956         || !CsrValidateMessageBuffer(ApiMessage,
957                                      (PVOID*)&GetNextVdmCommandRequest->CurDirectory,
958                                      GetNextVdmCommandRequest->CurDirectoryLen,
959                                      sizeof(*GetNextVdmCommandRequest->CurDirectory))
960         || !CsrValidateMessageBuffer(ApiMessage,
961                                      (PVOID*)&GetNextVdmCommandRequest->Env,
962                                      GetNextVdmCommandRequest->EnvLen,
963                                      sizeof(*GetNextVdmCommandRequest->Env))
964         || !CsrValidateMessageBuffer(ApiMessage,
965                                      (PVOID*)&GetNextVdmCommandRequest->Desktop,
966                                      GetNextVdmCommandRequest->DesktopLen,
967                                      sizeof(*GetNextVdmCommandRequest->Desktop))
968         || !CsrValidateMessageBuffer(ApiMessage,
969                                      (PVOID*)&GetNextVdmCommandRequest->Title,
970                                      GetNextVdmCommandRequest->TitleLen,
971                                      sizeof(*GetNextVdmCommandRequest->Title))
972         || !CsrValidateMessageBuffer(ApiMessage,
973                                      (PVOID*)&GetNextVdmCommandRequest->Reserved,
974                                      GetNextVdmCommandRequest->ReservedLen,
975                                      sizeof(*GetNextVdmCommandRequest->Reserved))
976         || !CsrValidateMessageBuffer(ApiMessage,
977                                      (PVOID*)&GetNextVdmCommandRequest->StartupInfo,
978                                      1,
979                                      sizeof(STARTUPINFOA)))
980     {
981         return STATUS_INVALID_PARAMETER;
982     }
983 
984     CriticalSection = (GetNextVdmCommandRequest->VDMState & VDM_FLAG_WOW)
985                       ? &WowCriticalSection
986                       : &DosCriticalSection;
987 
988     /* Enter the critical section */
989     RtlEnterCriticalSection(CriticalSection);
990 
991     if (GetNextVdmCommandRequest->VDMState & VDM_FLAG_WOW)
992     {
993         // TODO: WOW SUPPORT NOT IMPLEMENTED
994         UNIMPLEMENTED;
995         Status = STATUS_NOT_IMPLEMENTED;
996         goto Cleanup;
997     }
998     // else if (!(GetNextVdmCommandRequest->VDMState & VDM_FLAG_WOW))
999     {
1000         if (GetNextVdmCommandRequest->iTask != 0)
1001         {
1002             /* Get the console record using the task ID */
1003             Status = GetConsoleRecordBySessionId(GetNextVdmCommandRequest->iTask,
1004                                                  &ConsoleRecord);
1005         }
1006         else
1007         {
1008             /* Get the console record using the console handle */
1009             Status = BaseSrvGetConsoleRecord(GetNextVdmCommandRequest->ConsoleHandle,
1010                                              &ConsoleRecord);
1011         }
1012 
1013         /* Make sure we found the console record */
1014         if (!NT_SUCCESS(Status)) goto Cleanup;
1015 
1016         /* Return the session ID */
1017         GetNextVdmCommandRequest->iTask = ConsoleRecord->SessionId;
1018         GetNextVdmCommandRequest->WaitObjectForVDM = NULL;
1019 
1020         if (GetNextVdmCommandRequest->VDMState & VDM_GET_FIRST_COMMAND)
1021         {
1022             /* Check if the DOS record list is empty */
1023             if (ConsoleRecord->DosListHead.Flink == &ConsoleRecord->DosListHead)
1024             {
1025                 Status = STATUS_INVALID_PARAMETER;
1026                 goto Cleanup;
1027             }
1028 
1029             /* Get the first DOS record */
1030             DosRecord = CONTAINING_RECORD(ConsoleRecord->DosListHead.Flink, VDM_DOS_RECORD, Entry);
1031 
1032             /* Make sure its command information is still there */
1033             if (DosRecord->CommandInfo == NULL)
1034             {
1035                 Status = STATUS_INVALID_PARAMETER;
1036                 goto Cleanup;
1037             }
1038 
1039             /* Check if the console handle hasn't been set yet */
1040             if (ConsoleRecord->ConsoleHandle == NULL)
1041             {
1042                 /* Set it now */
1043                 ConsoleRecord->ConsoleHandle = GetNextVdmCommandRequest->ConsoleHandle;
1044             }
1045 
1046             /* Fill the command information */
1047             Status = BaseSrvFillCommandInfo(DosRecord->CommandInfo, GetNextVdmCommandRequest);
1048             goto Cleanup;
1049         }
1050 
1051         /* Check if we should set the state of a running DOS record to ready */
1052         if (!(GetNextVdmCommandRequest->VDMState
1053             & (VDM_FLAG_FIRST_TASK | VDM_FLAG_RETRY | VDM_FLAG_NESTED_TASK)))
1054         {
1055             /* Search for a DOS record that is currently running */
1056             for (i = ConsoleRecord->DosListHead.Flink; i != &ConsoleRecord->DosListHead; i = i->Flink)
1057             {
1058                 DosRecord = CONTAINING_RECORD(i, VDM_DOS_RECORD, Entry);
1059                 if (DosRecord->State == VDM_NOT_READY) break;
1060             }
1061 
1062             /* Check if we found any */
1063             if (i == &ConsoleRecord->DosListHead)
1064             {
1065                 Status = STATUS_INVALID_PARAMETER;
1066                 goto Cleanup;
1067             }
1068 
1069             /* Set the exit code */
1070             DosRecord->ExitCode = GetNextVdmCommandRequest->ExitCode;
1071 
1072             /* Update the VDM state */
1073             DosRecord->State = VDM_READY;
1074 
1075             /* Notify all waiting threads that the task is finished */
1076             NtSetEvent(DosRecord->ServerEvent, NULL);
1077             NtClose(DosRecord->ServerEvent);
1078             DosRecord->ServerEvent = NULL;
1079         }
1080 
1081         /* Search for a DOS record that is currently running and has command information */
1082         for (i = ConsoleRecord->DosListHead.Flink; i != &ConsoleRecord->DosListHead; i = i->Flink)
1083         {
1084             DosRecord = CONTAINING_RECORD(i, VDM_DOS_RECORD, Entry);
1085             if ((DosRecord->State == VDM_NOT_READY) && (DosRecord->CommandInfo != NULL)) break;
1086         }
1087 
1088         /* Check if we found any */
1089         if (i != &ConsoleRecord->DosListHead)
1090         {
1091             ASSERT(DosRecord->CommandInfo != NULL);
1092 
1093             /* Check if the caller only wants environment data */
1094             if (GetNextVdmCommandRequest->VDMState & VDM_GET_ENVIRONMENT)
1095             {
1096                 if (GetNextVdmCommandRequest->EnvLen < DosRecord->CommandInfo->EnvLen)
1097                 {
1098                     /* Not enough space was reserved */
1099                     GetNextVdmCommandRequest->EnvLen = DosRecord->CommandInfo->EnvLen;
1100                     Status = STATUS_BUFFER_OVERFLOW;
1101                     goto Cleanup;
1102                 }
1103 
1104                 /* Copy the environment data */
1105                 RtlMoveMemory(GetNextVdmCommandRequest->Env,
1106                               DosRecord->CommandInfo->Env,
1107                               DosRecord->CommandInfo->EnvLen);
1108 
1109                 /* Return the actual size to the caller */
1110                 GetNextVdmCommandRequest->EnvLen = DosRecord->CommandInfo->EnvLen;
1111             }
1112             else
1113             {
1114                 /* Fill the command information */
1115                 Status = BaseSrvFillCommandInfo(DosRecord->CommandInfo, GetNextVdmCommandRequest);
1116                 if (!NT_SUCCESS(Status)) goto Cleanup;
1117 
1118                 /* Free the command information, it's no longer needed */
1119                 BaseSrvFreeVDMInfo(DosRecord->CommandInfo);
1120                 DosRecord->CommandInfo = NULL;
1121 
1122                 /* Update the VDM state */
1123                 DosRecord->State = VDM_NOT_READY;
1124             }
1125 
1126             Status = STATUS_SUCCESS;
1127             goto Cleanup;
1128         }
1129     }
1130 
1131     GetNextVdmCommandRequest->WaitObjectForVDM = NULL;
1132 
1133     /*
1134      * There is no command yet. Prepare for waiting if we asked so,
1135      * and if we were not retrying a request.
1136      */
1137     if (!(GetNextVdmCommandRequest->VDMState & VDM_FLAG_DONT_WAIT) ||
1138         !(GetNextVdmCommandRequest->VDMState & VDM_FLAG_RETRY))
1139     {
1140         if (ConsoleRecord->ServerEvent)
1141         {
1142             /* Reset the event */
1143             NtResetEvent(ConsoleRecord->ServerEvent, NULL);
1144         }
1145         else
1146         {
1147             /* Create a pair of wait handles */
1148             Status = BaseSrvCreatePairWaitHandles(&ConsoleRecord->ServerEvent,
1149                                                   &ConsoleRecord->ClientEvent);
1150             if (!NT_SUCCESS(Status)) goto Cleanup;
1151         }
1152 
1153         /* Return the client event handle */
1154         GetNextVdmCommandRequest->WaitObjectForVDM = ConsoleRecord->ClientEvent;
1155     }
1156 
1157 Cleanup:
1158     /* Leave the critical section */
1159     RtlLeaveCriticalSection(CriticalSection);
1160 
1161     return Status;
1162 }
1163 
1164 CSR_API(BaseSrvExitVDM)
1165 {
1166     NTSTATUS Status;
1167     PBASE_EXIT_VDM ExitVdmRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.ExitVDMRequest;
1168     PRTL_CRITICAL_SECTION CriticalSection = NULL;
1169     PVDM_CONSOLE_RECORD ConsoleRecord = NULL;
1170     PVDM_DOS_RECORD DosRecord;
1171 
1172     CriticalSection = (ExitVdmRequest->iWowTask == 0)
1173                       ? &DosCriticalSection
1174                       : &WowCriticalSection;
1175 
1176     /* Enter the critical section */
1177     RtlEnterCriticalSection(CriticalSection);
1178 
1179     if (ExitVdmRequest->iWowTask == 0)
1180     {
1181         /* Get the console record */
1182         Status = BaseSrvGetConsoleRecord(ExitVdmRequest->ConsoleHandle, &ConsoleRecord);
1183         if (!NT_SUCCESS(Status)) goto Cleanup;
1184 
1185         if (ConsoleRecord->ServerEvent)
1186             ExitVdmRequest->WaitObjectForVDM = ConsoleRecord->ClientEvent;
1187 
1188         // NOTE: The following is the same as in BaseSrvCleanupVDMResources.
1189 
1190         if (ConsoleRecord->ServerEvent)
1191         {
1192             NtClose(ConsoleRecord->ServerEvent);
1193             ConsoleRecord->ServerEvent = NULL;
1194         }
1195 
1196         /* Cleanup the DOS records */
1197         while (!IsListEmpty(&ConsoleRecord->DosListHead))
1198         {
1199             DosRecord = CONTAINING_RECORD(ConsoleRecord->DosListHead.Flink,
1200                                           VDM_DOS_RECORD, Entry);
1201 
1202             /* Set the event and close it */
1203             if (DosRecord->ServerEvent)
1204             {
1205                 NtSetEvent(DosRecord->ServerEvent, NULL);
1206                 NtClose(DosRecord->ServerEvent);
1207                 DosRecord->ServerEvent = NULL;
1208             }
1209 
1210             /* Remove the DOS entry */
1211             if (DosRecord->CommandInfo) BaseSrvFreeVDMInfo(DosRecord->CommandInfo);
1212             RemoveEntryList(&DosRecord->Entry);
1213             RtlFreeHeap(BaseSrvHeap, 0, DosRecord);
1214         }
1215 
1216         /* Remove the console record */
1217         RemoveEntryList(&ConsoleRecord->Entry);
1218         BaseSrvDestroyConsoleRecord(ConsoleRecord);
1219     }
1220     else
1221     {
1222         // TODO: NOT IMPLEMENTED
1223         UNIMPLEMENTED;
1224         Status = STATUS_NOT_IMPLEMENTED;
1225     }
1226 
1227 Cleanup:
1228     /* Leave the critical section */
1229     RtlLeaveCriticalSection(CriticalSection);
1230 
1231     return Status;
1232 }
1233 
1234 CSR_API(BaseSrvIsFirstVDM)
1235 {
1236     PBASE_IS_FIRST_VDM IsFirstVDMRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.IsFirstVDMRequest;
1237 
1238     /* Return the result */
1239     IsFirstVDMRequest->FirstVDM = FirstVDM;
1240 
1241     /* Clear the first VDM flag */
1242     FirstVDM = FALSE;
1243 
1244     return STATUS_SUCCESS;
1245 }
1246 
1247 CSR_API(BaseSrvGetVDMExitCode)
1248 {
1249     NTSTATUS Status;
1250     PBASE_GET_VDM_EXIT_CODE GetVDMExitCodeRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.GetVDMExitCodeRequest;
1251     PLIST_ENTRY i = NULL;
1252     PVDM_CONSOLE_RECORD ConsoleRecord = NULL;
1253     PVDM_DOS_RECORD DosRecord = NULL;
1254 
1255     /* Enter the critical section */
1256     RtlEnterCriticalSection(&DosCriticalSection);
1257 
1258     /* Get the console record */
1259     Status = BaseSrvGetConsoleRecord(GetVDMExitCodeRequest->ConsoleHandle, &ConsoleRecord);
1260     if (!NT_SUCCESS(Status)) goto Cleanup;
1261 
1262     /* Search for a DOS record that has the same parent process handle */
1263     for (i = ConsoleRecord->DosListHead.Flink; i != &ConsoleRecord->DosListHead; i = i->Flink)
1264     {
1265         DosRecord = CONTAINING_RECORD(i, VDM_DOS_RECORD, Entry);
1266         if (DosRecord->ClientEvent == GetVDMExitCodeRequest->hParent) break;
1267     }
1268 
1269     /* Check if no DOS record was found */
1270     if (i == &ConsoleRecord->DosListHead)
1271     {
1272         Status = STATUS_NOT_FOUND;
1273         goto Cleanup;
1274     }
1275 
1276     /* Check if this task is still running */
1277     if (DosRecord->State != VDM_READY)
1278     {
1279         GetVDMExitCodeRequest->ExitCode = STATUS_PENDING;
1280         goto Cleanup;
1281     }
1282 
1283     /* Return the exit code */
1284     GetVDMExitCodeRequest->ExitCode = DosRecord->ExitCode;
1285 
1286     // FIXME: We may just change DosRecord->State to VDM_READY in some cases...
1287 
1288     /* Since this is a zombie task record, remove it */
1289     if (DosRecord->CommandInfo) BaseSrvFreeVDMInfo(DosRecord->CommandInfo);
1290     RemoveEntryList(&DosRecord->Entry);
1291     RtlFreeHeap(BaseSrvHeap, 0, DosRecord);
1292 
1293 Cleanup:
1294     /* Leave the critical section */
1295     RtlLeaveCriticalSection(&DosCriticalSection);
1296 
1297     return Status;
1298 }
1299 
1300 CSR_API(BaseSrvSetReenterCount)
1301 {
1302     NTSTATUS Status = STATUS_SUCCESS;
1303     PBASE_SET_REENTER_COUNT SetReenterCountRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.SetReenterCountRequest;
1304     PVDM_CONSOLE_RECORD ConsoleRecord;
1305 
1306     /* Enter the critical section */
1307     RtlEnterCriticalSection(&DosCriticalSection);
1308 
1309     /* Get the console record */
1310     Status = BaseSrvGetConsoleRecord(SetReenterCountRequest->ConsoleHandle, &ConsoleRecord);
1311     if (!NT_SUCCESS(Status)) goto Cleanup;
1312 
1313     if (SetReenterCountRequest->fIncDec == VDM_INC_REENTER_COUNT)
1314     {
1315         ConsoleRecord->ReenterCount++;
1316     }
1317     else if (SetReenterCountRequest->fIncDec == VDM_DEC_REENTER_COUNT)
1318     {
1319         ConsoleRecord->ReenterCount--;
1320         if (ConsoleRecord->ServerEvent)
1321             NtSetEvent(ConsoleRecord->ServerEvent, NULL);
1322     }
1323     else
1324     {
1325         Status = STATUS_INVALID_PARAMETER;
1326     }
1327 
1328 Cleanup:
1329     /* Leave the critical section */
1330     RtlLeaveCriticalSection(&DosCriticalSection);
1331 
1332     return Status;
1333 }
1334 
1335 CSR_API(BaseSrvSetVDMCurDirs)
1336 {
1337     NTSTATUS Status;
1338     PBASE_GETSET_VDM_CURDIRS VDMCurrentDirsRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.VDMCurrentDirsRequest;
1339     PVDM_CONSOLE_RECORD ConsoleRecord;
1340     PCHAR Buffer = NULL;
1341 
1342     /* Validate the input buffer */
1343     if (!CsrValidateMessageBuffer(ApiMessage,
1344                                   (PVOID*)&VDMCurrentDirsRequest->lpszzCurDirs,
1345                                   VDMCurrentDirsRequest->cchCurDirs,
1346                                   sizeof(*VDMCurrentDirsRequest->lpszzCurDirs)))
1347     {
1348         return STATUS_INVALID_PARAMETER;
1349     }
1350 
1351     /* Enter the critical section */
1352     RtlEnterCriticalSection(&DosCriticalSection);
1353 
1354     /* Find the console record */
1355     Status = BaseSrvGetConsoleRecord(VDMCurrentDirsRequest->ConsoleHandle, &ConsoleRecord);
1356     if (!NT_SUCCESS(Status)) goto Cleanup;
1357 
1358     if (ConsoleRecord->CurrentDirs == NULL)
1359     {
1360         /* Allocate memory for the current directory information */
1361         Buffer = RtlAllocateHeap(BaseSrvHeap,
1362                                  HEAP_ZERO_MEMORY,
1363                                  VDMCurrentDirsRequest->cchCurDirs);
1364     }
1365     else
1366     {
1367         /* Resize the amount of allocated memory */
1368         Buffer = RtlReAllocateHeap(BaseSrvHeap,
1369                                    HEAP_ZERO_MEMORY,
1370                                    ConsoleRecord->CurrentDirs,
1371                                    VDMCurrentDirsRequest->cchCurDirs);
1372     }
1373 
1374     if (Buffer == NULL)
1375     {
1376         /* Allocation failed */
1377         Status = STATUS_NO_MEMORY;
1378         goto Cleanup;
1379     }
1380 
1381     /* Update the console record */
1382     ConsoleRecord->CurrentDirs = Buffer;
1383     ConsoleRecord->CurDirsLength = VDMCurrentDirsRequest->cchCurDirs;
1384 
1385     /* Copy the data */
1386     RtlMoveMemory(ConsoleRecord->CurrentDirs,
1387                   VDMCurrentDirsRequest->lpszzCurDirs,
1388                   VDMCurrentDirsRequest->cchCurDirs);
1389 
1390 Cleanup:
1391     /* Leave the critical section */
1392     RtlLeaveCriticalSection(&DosCriticalSection);
1393 
1394     return Status;
1395 }
1396 
1397 CSR_API(BaseSrvGetVDMCurDirs)
1398 {
1399     NTSTATUS Status;
1400     PBASE_GETSET_VDM_CURDIRS VDMCurrentDirsRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.VDMCurrentDirsRequest;
1401     PVDM_CONSOLE_RECORD ConsoleRecord;
1402 
1403     /* Validate the output buffer */
1404     if (!CsrValidateMessageBuffer(ApiMessage,
1405                                   (PVOID*)&VDMCurrentDirsRequest->lpszzCurDirs,
1406                                   VDMCurrentDirsRequest->cchCurDirs,
1407                                   sizeof(*VDMCurrentDirsRequest->lpszzCurDirs)))
1408     {
1409         return STATUS_INVALID_PARAMETER;
1410     }
1411 
1412     /* Enter the critical section */
1413     RtlEnterCriticalSection(&DosCriticalSection);
1414 
1415     /* Find the console record */
1416     Status = BaseSrvGetConsoleRecord(VDMCurrentDirsRequest->ConsoleHandle, &ConsoleRecord);
1417     if (!NT_SUCCESS(Status)) goto Cleanup;
1418 
1419     /* Return the actual size of the current directory information */
1420     VDMCurrentDirsRequest->cchCurDirs = ConsoleRecord->CurDirsLength;
1421 
1422     /* Check if the buffer is large enough */
1423     if (VDMCurrentDirsRequest->cchCurDirs < ConsoleRecord->CurDirsLength)
1424     {
1425         Status = STATUS_BUFFER_TOO_SMALL;
1426         goto Cleanup;
1427     }
1428 
1429     /* Copy the data */
1430     RtlMoveMemory(VDMCurrentDirsRequest->lpszzCurDirs,
1431                   ConsoleRecord->CurrentDirs,
1432                   ConsoleRecord->CurDirsLength);
1433 
1434 Cleanup:
1435     /* Leave the critical section */
1436     RtlLeaveCriticalSection(&DosCriticalSection);
1437 
1438     return Status;
1439 }
1440 
1441 CSR_API(BaseSrvBatNotification)
1442 {
1443     DPRINT1("%s not yet implemented\n", __FUNCTION__);
1444     return STATUS_NOT_IMPLEMENTED;
1445 }
1446 
1447 CSR_API(BaseSrvRegisterWowExec)
1448 {
1449     DPRINT1("%s not yet implemented\n", __FUNCTION__);
1450     return STATUS_NOT_IMPLEMENTED;
1451 }
1452 
1453 CSR_API(BaseSrvRefreshIniFileMapping)
1454 {
1455     DPRINT1("%s not yet implemented\n", __FUNCTION__);
1456     return STATUS_NOT_IMPLEMENTED;
1457 }
1458 
1459 /* EOF */
1460