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