[BASESRV]
[reactos.git] / subsystems / win / basesrv / vdm.c
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 /* FUNCTIONS ******************************************************************/
26
27 NTSTATUS NTAPI BaseSrvGetConsoleRecord(HANDLE ConsoleHandle, PVDM_CONSOLE_RECORD *Record)
28 {
29 PLIST_ENTRY i;
30 PVDM_CONSOLE_RECORD CurrentRecord = NULL;
31
32 /* Search for a record that has the same console handle */
33 for (i = VDMConsoleListHead.Flink; i != &VDMConsoleListHead; i = i->Flink)
34 {
35 CurrentRecord = CONTAINING_RECORD(i, VDM_CONSOLE_RECORD, Entry);
36 if (CurrentRecord->ConsoleHandle == ConsoleHandle) break;
37 }
38
39 *Record = CurrentRecord;
40 return CurrentRecord ? STATUS_SUCCESS : STATUS_NOT_FOUND;
41 }
42
43 NTSTATUS NTAPI GetConsoleRecordBySessionId(ULONG TaskId, PVDM_CONSOLE_RECORD *Record)
44 {
45 PLIST_ENTRY i;
46 PVDM_CONSOLE_RECORD CurrentRecord = NULL;
47
48 /* Search for a record that has the same console handle */
49 for (i = VDMConsoleListHead.Flink; i != &VDMConsoleListHead; i = i->Flink)
50 {
51 CurrentRecord = CONTAINING_RECORD(i, VDM_CONSOLE_RECORD, Entry);
52 if (CurrentRecord->SessionId == TaskId) break;
53 }
54
55 *Record = CurrentRecord;
56 return CurrentRecord ? STATUS_SUCCESS : STATUS_NOT_FOUND;
57 }
58
59 ULONG NTAPI GetNextDosSesId(VOID)
60 {
61 ULONG SessionId;
62 PLIST_ENTRY i;
63 PVDM_CONSOLE_RECORD CurrentRecord = NULL;
64 BOOLEAN Found;
65
66 /* Search for an available session ID */
67 for (SessionId = 1; SessionId != 0; SessionId++)
68 {
69 Found = FALSE;
70
71 /* Check if the ID is already in use */
72 for (i = VDMConsoleListHead.Flink; i != &VDMConsoleListHead; i = i->Flink)
73 {
74 CurrentRecord = CONTAINING_RECORD(i, VDM_CONSOLE_RECORD, Entry);
75 if (CurrentRecord->SessionId == SessionId) Found = TRUE;
76 }
77
78 /* If not, we found one */
79 if (!Found) break;
80 }
81
82 ASSERT(SessionId != 0);
83
84 /* Return the session ID */
85 return SessionId;
86 }
87
88 BOOLEAN NTAPI BaseSrvIsVdmAllowed(VOID)
89 {
90 NTSTATUS Status;
91 BOOLEAN VdmAllowed = TRUE;
92 HANDLE RootKey, KeyHandle;
93 UNICODE_STRING KeyName, ValueName, MachineKeyName;
94 OBJECT_ATTRIBUTES Attributes;
95 UCHAR ValueBuffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(ULONG)];
96 PKEY_VALUE_PARTIAL_INFORMATION ValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)ValueBuffer;
97 ULONG ActualSize;
98
99 /* Initialize the unicode strings */
100 RtlInitUnicodeString(&MachineKeyName, L"\\Registry\\Machine");
101 RtlInitUnicodeString(&KeyName, VDM_POLICY_KEY_NAME);
102 RtlInitUnicodeString(&ValueName, VDM_DISALLOWED_VALUE_NAME);
103
104 InitializeObjectAttributes(&Attributes,
105 &MachineKeyName,
106 OBJ_CASE_INSENSITIVE,
107 NULL,
108 NULL);
109
110 /* Open the local machine key */
111 Status = NtOpenKey(&RootKey, KEY_READ, &Attributes);
112 if (!NT_SUCCESS(Status)) return FALSE;
113
114 InitializeObjectAttributes(&Attributes,
115 &KeyName,
116 OBJ_CASE_INSENSITIVE,
117 RootKey,
118 NULL);
119
120 /* Open the policy key in the local machine hive, if it exists */
121 if (NT_SUCCESS(NtOpenKey(&KeyHandle, KEY_READ, &Attributes)))
122 {
123 /* Read the value, if it's set */
124 if (NT_SUCCESS(NtQueryValueKey(KeyHandle,
125 &ValueName,
126 KeyValuePartialInformation,
127 ValueInfo,
128 sizeof(ValueBuffer),
129 &ActualSize)))
130 {
131 if (*((PULONG)ValueInfo->Data))
132 {
133 /* The VDM has been disabled in the registry */
134 VdmAllowed = FALSE;
135 }
136 }
137
138 NtClose(KeyHandle);
139 }
140
141 /* Close the local machine key */
142 NtClose(RootKey);
143
144 /* If it's disabled system-wide, there's no need to check the user key */
145 if (!VdmAllowed) return FALSE;
146
147 /* Open the current user key of the client */
148 if (!CsrImpersonateClient(NULL)) return VdmAllowed;
149 Status = RtlOpenCurrentUser(KEY_READ, &RootKey);
150 CsrRevertToSelf();
151
152 /* If that fails, return the system-wide setting */
153 if (!NT_SUCCESS(Status)) return VdmAllowed;
154
155 InitializeObjectAttributes(&Attributes,
156 &KeyName,
157 OBJ_CASE_INSENSITIVE,
158 RootKey,
159 NULL);
160
161 /* Open the policy key in the current user hive, if it exists */
162 if (NT_SUCCESS(NtOpenKey(&KeyHandle, KEY_READ, &Attributes)))
163 {
164 /* Read the value, if it's set */
165 if (NT_SUCCESS(NtQueryValueKey(KeyHandle,
166 &ValueName,
167 KeyValuePartialInformation,
168 ValueInfo,
169 sizeof(ValueBuffer),
170 &ActualSize)))
171 {
172 if (*((PULONG)ValueInfo->Data))
173 {
174 /* The VDM has been disabled in the registry */
175 VdmAllowed = FALSE;
176 }
177 }
178
179 NtClose(KeyHandle);
180 }
181
182 return VdmAllowed;
183 }
184
185 NTSTATUS NTAPI BaseSrvCreatePairWaitHandles(PHANDLE ServerEvent, PHANDLE ClientEvent)
186 {
187 NTSTATUS Status;
188
189 /* Create the event */
190 Status = NtCreateEvent(ServerEvent, EVENT_ALL_ACCESS, NULL, NotificationEvent, FALSE);
191 if (!NT_SUCCESS(Status)) return Status;
192
193 /* Duplicate the event into the client process */
194 Status = NtDuplicateObject(NtCurrentProcess(),
195 *ServerEvent,
196 CsrGetClientThread()->Process->ProcessHandle,
197 ClientEvent,
198 0,
199 0,
200 DUPLICATE_SAME_ATTRIBUTES | DUPLICATE_SAME_ACCESS);
201
202 if (!NT_SUCCESS(Status)) NtClose(*ServerEvent);
203 return Status;
204 }
205
206 VOID BaseSrvFreeVDMInfo(PVDM_COMMAND_INFO CommandInfo)
207 {
208 /* Free the allocated structure members */
209 if (CommandInfo->CmdLine != NULL) RtlFreeHeap(BaseSrvHeap, 0, CommandInfo->CmdLine);
210 if (CommandInfo->AppName != NULL) RtlFreeHeap(BaseSrvHeap, 0, CommandInfo->AppName);
211 if (CommandInfo->PifFile != NULL) RtlFreeHeap(BaseSrvHeap, 0, CommandInfo->PifFile);
212 if (CommandInfo->CurDirectory != NULL) RtlFreeHeap(BaseSrvHeap, 0, CommandInfo->CurDirectory);
213 if (CommandInfo->Env != NULL) RtlFreeHeap(BaseSrvHeap, 0, CommandInfo->Env);
214 if (CommandInfo->Desktop != NULL) RtlFreeHeap(BaseSrvHeap, 0, CommandInfo->Desktop);
215 if (CommandInfo->Title != NULL) RtlFreeHeap(BaseSrvHeap, 0, CommandInfo->Title);
216 if (CommandInfo->Reserved != NULL) RtlFreeHeap(BaseSrvHeap, 0, CommandInfo->Reserved);
217
218 /* Free the structure itself */
219 RtlFreeHeap(BaseSrvHeap, 0, CommandInfo);
220 }
221
222 BOOLEAN NTAPI BaseSrvCopyCommand(PBASE_CHECK_VDM CheckVdmRequest, PVDM_DOS_RECORD DosRecord)
223 {
224 BOOLEAN Success = FALSE;
225 PVDM_COMMAND_INFO CommandInfo = NULL;
226
227 /* Allocate the command information structure */
228 CommandInfo = (PVDM_COMMAND_INFO)RtlAllocateHeap(BaseSrvHeap,
229 HEAP_ZERO_MEMORY,
230 sizeof(VDM_COMMAND_INFO));
231 if (CommandInfo == NULL) return FALSE;
232
233 /* Fill the structure */
234 CommandInfo->TaskId = CheckVdmRequest->iTask;
235 CommandInfo->ExitCode = DosRecord->ExitCode;
236 CommandInfo->CodePage = CheckVdmRequest->CodePage;
237 CommandInfo->StdIn = CheckVdmRequest->StdIn;
238 CommandInfo->StdOut = CheckVdmRequest->StdOut;
239 CommandInfo->StdErr = CheckVdmRequest->StdErr;
240
241 /* Allocate memory for the command line */
242 CommandInfo->CmdLine = RtlAllocateHeap(BaseSrvHeap,
243 HEAP_ZERO_MEMORY,
244 CheckVdmRequest->CmdLen);
245 if (CommandInfo->CmdLine == NULL) goto Cleanup;
246
247 /* Copy the command line */
248 RtlMoveMemory(CommandInfo->CmdLine, CheckVdmRequest->CmdLine, CheckVdmRequest->CmdLen);
249
250 /* Allocate memory for the application name */
251 CommandInfo->AppName = RtlAllocateHeap(BaseSrvHeap,
252 HEAP_ZERO_MEMORY,
253 CheckVdmRequest->AppLen);
254 if (CommandInfo->AppName == NULL) goto Cleanup;
255
256 /* Copy the application name */
257 RtlMoveMemory(CommandInfo->AppName, CheckVdmRequest->AppName, CheckVdmRequest->AppLen);
258
259 /* Allocate memory for the PIF file name */
260 if (CheckVdmRequest->PifLen != 0)
261 {
262 CommandInfo->PifFile = RtlAllocateHeap(BaseSrvHeap,
263 HEAP_ZERO_MEMORY,
264 CheckVdmRequest->PifLen);
265 if (CommandInfo->PifFile == NULL) goto Cleanup;
266
267 /* Copy the PIF file name */
268 RtlMoveMemory(CommandInfo->PifFile, CheckVdmRequest->PifFile, CheckVdmRequest->PifLen);
269 }
270 else CommandInfo->PifFile = NULL;
271
272 /* Allocate memory for the current directory */
273 if (CheckVdmRequest->CurDirectoryLen != 0)
274 {
275 CommandInfo->CurDirectory = RtlAllocateHeap(BaseSrvHeap,
276 HEAP_ZERO_MEMORY,
277 CheckVdmRequest->CurDirectoryLen);
278 if (CommandInfo->CurDirectory == NULL) goto Cleanup;
279
280 /* Copy the current directory */
281 RtlMoveMemory(CommandInfo->CurDirectory,
282 CheckVdmRequest->CurDirectory,
283 CheckVdmRequest->CurDirectoryLen);
284 }
285 else CommandInfo->CurDirectory = NULL;
286
287 /* Allocate memory for the environment block */
288 CommandInfo->Env = RtlAllocateHeap(BaseSrvHeap,
289 HEAP_ZERO_MEMORY,
290 CheckVdmRequest->EnvLen);
291 if (CommandInfo->Env == NULL) goto Cleanup;
292
293 /* Copy the environment block */
294 RtlMoveMemory(CommandInfo->Env, CheckVdmRequest->Env, CheckVdmRequest->EnvLen);
295
296 CommandInfo->EnvLen = CheckVdmRequest->EnvLen;
297 RtlMoveMemory(&CommandInfo->StartupInfo,
298 CheckVdmRequest->StartupInfo,
299 sizeof(STARTUPINFOA));
300
301 /* Allocate memory for the desktop */
302 if (CheckVdmRequest->DesktopLen != 0)
303 {
304 CommandInfo->Desktop = RtlAllocateHeap(BaseSrvHeap,
305 HEAP_ZERO_MEMORY,
306 CheckVdmRequest->DesktopLen);
307 if (CommandInfo->Desktop == NULL) goto Cleanup;
308
309 /* Copy the desktop name */
310 RtlMoveMemory(CommandInfo->Desktop, CheckVdmRequest->Desktop, CheckVdmRequest->DesktopLen);
311 }
312 else CommandInfo->Desktop = NULL;
313
314 CommandInfo->DesktopLen = CheckVdmRequest->DesktopLen;
315
316 /* Allocate memory for the title */
317 if (CheckVdmRequest->TitleLen != 0)
318 {
319 CommandInfo->Title = RtlAllocateHeap(BaseSrvHeap,
320 HEAP_ZERO_MEMORY,
321 CheckVdmRequest->TitleLen);
322 if (CommandInfo->Title == NULL) goto Cleanup;
323
324 /* Copy the title */
325 RtlMoveMemory(CommandInfo->Title, CheckVdmRequest->Title, CheckVdmRequest->TitleLen);
326 }
327 else CommandInfo->Title = NULL;
328
329 CommandInfo->TitleLen = CheckVdmRequest->TitleLen;
330
331 /* Allocate memory for the reserved field */
332 if (CheckVdmRequest->ReservedLen != 0)
333 {
334 CommandInfo->Reserved = RtlAllocateHeap(BaseSrvHeap,
335 HEAP_ZERO_MEMORY,
336 CheckVdmRequest->ReservedLen);
337 if (CommandInfo->Reserved == NULL) goto Cleanup;
338
339 /* Copy the reserved field */
340 RtlMoveMemory(CommandInfo->Reserved,
341 CheckVdmRequest->Reserved,
342 CheckVdmRequest->ReservedLen);
343 }
344 else CommandInfo->Reserved = NULL;
345
346 CommandInfo->ReservedLen = CheckVdmRequest->ReservedLen;
347
348 CommandInfo->CmdLen = CheckVdmRequest->CmdLen;
349 CommandInfo->AppLen = CheckVdmRequest->AppLen;
350 CommandInfo->PifLen = CheckVdmRequest->PifLen;
351 CommandInfo->CurDirectoryLen = CheckVdmRequest->CurDirectoryLen;
352 CommandInfo->VDMState = DosRecord->State;
353 // TODO: Set CommandInfo->CurrentDrive
354 // TODO: Set CommandInfo->ComingFromBat
355
356 /* Set the DOS record's command structure */
357 DosRecord->CommandInfo = CommandInfo;
358
359 /* The operation was successful */
360 Success = TRUE;
361
362 Cleanup:
363 /* If it wasn't successful, free the memory */
364 if (!Success) BaseSrvFreeVDMInfo(CommandInfo);
365
366 return Success;
367 }
368
369 VOID NTAPI BaseSrvFillCommandInfo(PVDM_COMMAND_INFO CommandInfo,
370 PBASE_GET_NEXT_VDM_COMMAND Message)
371 {
372 /* Copy the data */
373 Message->iTask = CommandInfo->TaskId;
374 Message->StdIn = CommandInfo->StdIn;
375 Message->StdOut = CommandInfo->StdOut;
376 Message->StdErr = CommandInfo->StdErr;
377 Message->CodePage = CommandInfo->CodePage;
378 Message->dwCreationFlags = CommandInfo->CreationFlags;
379 Message->ExitCode = CommandInfo->ExitCode;
380 Message->CurrentDrive = CommandInfo->CurrentDrive;
381 Message->VDMState = CommandInfo->VDMState;
382 Message->fComingFromBat = CommandInfo->ComingFromBat;
383
384 if (CommandInfo->CmdLen)
385 {
386 /* Copy the command line */
387 RtlMoveMemory(Message->CmdLine, CommandInfo->CmdLine, CommandInfo->CmdLen);
388 Message->CmdLen = CommandInfo->CmdLen;
389 }
390
391 if (CommandInfo->AppLen)
392 {
393 /* Copy the application name */
394 RtlMoveMemory(Message->AppName, CommandInfo->AppName, CommandInfo->AppLen);
395 Message->AppLen = CommandInfo->AppLen;
396 }
397
398 if (CommandInfo->PifLen)
399 {
400 /* Copy the PIF file name */
401 RtlMoveMemory(Message->PifFile, CommandInfo->PifFile, CommandInfo->PifLen);
402 Message->PifLen = CommandInfo->PifLen;
403 }
404
405 if (CommandInfo->CurDirectoryLen)
406 {
407 /* Copy the current directory */
408 RtlMoveMemory(Message->CurDirectory, CommandInfo->CurDirectory, CommandInfo->CurDirectoryLen);
409 Message->CurDirectoryLen = CommandInfo->CurDirectoryLen;
410 }
411
412 if (CommandInfo->EnvLen)
413 {
414 /* Copy the environment */
415 RtlMoveMemory(Message->Env, CommandInfo->Env, CommandInfo->EnvLen);
416 Message->EnvLen = CommandInfo->EnvLen;
417 }
418
419 /* Copy the startup info */
420 RtlMoveMemory(Message->StartupInfo,
421 &CommandInfo->StartupInfo,
422 sizeof(STARTUPINFOA));
423
424 if (CommandInfo->DesktopLen)
425 {
426 /* Copy the desktop name */
427 RtlMoveMemory(Message->Desktop, CommandInfo->Desktop, CommandInfo->DesktopLen);
428 Message->DesktopLen = CommandInfo->DesktopLen;
429 }
430
431 if (CommandInfo->TitleLen)
432 {
433 /* Copy the title */
434 RtlMoveMemory(Message->Title, CommandInfo->Title, CommandInfo->TitleLen);
435 Message->TitleLen = CommandInfo->TitleLen;
436 }
437
438 if (CommandInfo->ReservedLen)
439 {
440 /* Copy the reserved parameter */
441 RtlMoveMemory(Message->Reserved, CommandInfo->Reserved, CommandInfo->ReservedLen);
442 Message->ReservedLen = CommandInfo->ReservedLen;
443 }
444 }
445
446 VOID NTAPI BaseInitializeVDM(VOID)
447 {
448 /* Initialize the list head */
449 InitializeListHead(&VDMConsoleListHead);
450
451 /* Initialize the critical section */
452 RtlInitializeCriticalSection(&DosCriticalSection);
453 RtlInitializeCriticalSection(&WowCriticalSection);
454 }
455
456 /* PUBLIC SERVER APIS *********************************************************/
457
458 CSR_API(BaseSrvCheckVDM)
459 {
460 NTSTATUS Status;
461 PBASE_CHECK_VDM CheckVdmRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.CheckVDMRequest;
462 PCSR_PROCESS ClientProcess;
463 PRTL_CRITICAL_SECTION CriticalSection = NULL;
464 PVDM_CONSOLE_RECORD ConsoleRecord = NULL;
465 PVDM_DOS_RECORD DosRecord = NULL;
466 BOOLEAN NewConsoleRecord = FALSE;
467
468 /* Don't do anything if the VDM has been disabled in the registry */
469 if (!BaseSrvIsVdmAllowed()) return STATUS_ACCESS_DENIED;
470
471 /* Validate the message buffers */
472 if (!CsrValidateMessageBuffer(ApiMessage,
473 (PVOID*)&CheckVdmRequest->CmdLine,
474 CheckVdmRequest->CmdLen,
475 sizeof(*CheckVdmRequest->CmdLine))
476 || !CsrValidateMessageBuffer(ApiMessage,
477 (PVOID*)&CheckVdmRequest->AppName,
478 CheckVdmRequest->AppLen,
479 sizeof(*CheckVdmRequest->AppName))
480 || !CsrValidateMessageBuffer(ApiMessage,
481 (PVOID*)&CheckVdmRequest->PifFile,
482 CheckVdmRequest->PifLen,
483 sizeof(*CheckVdmRequest->PifFile))
484 || !CsrValidateMessageBuffer(ApiMessage,
485 (PVOID*)&CheckVdmRequest->CurDirectory,
486 CheckVdmRequest->CurDirectoryLen,
487 sizeof(*CheckVdmRequest->CurDirectory))
488 || !CsrValidateMessageBuffer(ApiMessage,
489 (PVOID*)&CheckVdmRequest->Desktop,
490 CheckVdmRequest->DesktopLen,
491 sizeof(*CheckVdmRequest->Desktop))
492 || !CsrValidateMessageBuffer(ApiMessage,
493 (PVOID*)&CheckVdmRequest->Title,
494 CheckVdmRequest->TitleLen,
495 sizeof(*CheckVdmRequest->Title))
496 || !CsrValidateMessageBuffer(ApiMessage,
497 (PVOID*)&CheckVdmRequest->Reserved,
498 CheckVdmRequest->ReservedLen,
499 sizeof(*CheckVdmRequest->Reserved)))
500 {
501 return STATUS_INVALID_PARAMETER;
502 }
503
504 /* Lock the process */
505 Status = CsrLockProcessByClientId(ApiMessage->Header.ClientId.UniqueProcess,
506 &ClientProcess);
507 if (!NT_SUCCESS(Status)) return Status;
508
509 CriticalSection = (CheckVdmRequest->BinaryType != BINARY_TYPE_SEPARATE_WOW)
510 ? &DosCriticalSection
511 : &WowCriticalSection;
512
513 /* Enter the critical section */
514 RtlEnterCriticalSection(CriticalSection);
515
516 /* Check if this is a DOS or WOW VDM */
517 if (CheckVdmRequest->BinaryType != BINARY_TYPE_SEPARATE_WOW)
518 {
519 /* Get the console record */
520 Status = BaseSrvGetConsoleRecord(CheckVdmRequest->ConsoleHandle,
521 &ConsoleRecord);
522
523 if (!NT_SUCCESS(Status))
524 {
525 /* Allocate a new console record */
526 ConsoleRecord = (PVDM_CONSOLE_RECORD)RtlAllocateHeap(BaseSrvHeap,
527 HEAP_ZERO_MEMORY,
528 sizeof(VDM_CONSOLE_RECORD));
529 if (ConsoleRecord == NULL)
530 {
531 Status = STATUS_NO_MEMORY;
532 goto Cleanup;
533 }
534
535 /* Initialize the console record */
536 ConsoleRecord->ConsoleHandle = CheckVdmRequest->ConsoleHandle;
537 ConsoleRecord->ProcessHandle = ClientProcess->ProcessHandle;
538 ConsoleRecord->ServerEvent = ConsoleRecord->ClientEvent = NULL;
539 ConsoleRecord->ReenterCount = 0;
540 ConsoleRecord->CurrentDirs = NULL;
541 ConsoleRecord->CurDirsLength = 0;
542 ConsoleRecord->SessionId = GetNextDosSesId();
543 InitializeListHead(&ConsoleRecord->DosListHead);
544 // TODO: The console record structure is incomplete
545
546 /* Remember that the console record was allocated here */
547 NewConsoleRecord = TRUE;
548 }
549
550 /* Allocate a new DOS record */
551 DosRecord = (PVDM_DOS_RECORD)RtlAllocateHeap(BaseSrvHeap,
552 HEAP_ZERO_MEMORY,
553 sizeof(VDM_DOS_RECORD));
554 if (DosRecord == NULL)
555 {
556 Status = STATUS_NO_MEMORY;
557 goto Cleanup;
558 }
559
560 /* Initialize the DOS record */
561 DosRecord->State = NewConsoleRecord ? VDM_NOT_LOADED : VDM_READY;
562 DosRecord->ExitCode = 0;
563 // TODO: The DOS record structure is incomplete
564
565 Status = BaseSrvCreatePairWaitHandles(&DosRecord->ServerEvent, &DosRecord->ClientEvent);
566 if (!NT_SUCCESS(Status)) goto Cleanup;
567
568 /* Return the client event handle */
569 CheckVdmRequest->WaitObjectForParent = DosRecord->ClientEvent;
570
571 /* Translate the input structure into a VDM command structure and set it in the DOS record */
572 if (!BaseSrvCopyCommand(CheckVdmRequest, DosRecord))
573 {
574 /* The only possibility is that an allocation failure occurred */
575 Status = STATUS_NO_MEMORY;
576 goto Cleanup;
577 }
578
579 /* Add the DOS record */
580 InsertHeadList(&ConsoleRecord->DosListHead, &DosRecord->Entry);
581
582 if (NewConsoleRecord)
583 {
584 /* Add the console record */
585 InsertTailList(&VDMConsoleListHead, &ConsoleRecord->Entry);
586 }
587
588 CheckVdmRequest->VDMState = DosRecord->State;
589 Status = STATUS_SUCCESS;
590 }
591 else
592 {
593 // TODO: NOT IMPLEMENTED
594 UNIMPLEMENTED;
595 Status = STATUS_NOT_IMPLEMENTED;
596 }
597
598 Cleanup:
599 /* Check if it failed */
600 if (!NT_SUCCESS(Status))
601 {
602 /* Free the DOS record */
603 if (DosRecord != NULL)
604 {
605 if (DosRecord->ServerEvent) NtClose(DosRecord->ServerEvent);
606 if (DosRecord->ClientEvent)
607 {
608 /* Close the remote handle */
609 NtDuplicateObject(CsrGetClientThread()->Process->ProcessHandle,
610 DosRecord->ClientEvent,
611 NULL,
612 NULL,
613 0,
614 0,
615 DUPLICATE_CLOSE_SOURCE);
616 }
617
618 RtlFreeHeap(BaseSrvHeap, 0, DosRecord);
619 DosRecord = NULL;
620 }
621
622 /* Free the console record if it was allocated here */
623 if (NewConsoleRecord)
624 {
625 RtlFreeHeap(BaseSrvHeap, 0, ConsoleRecord);
626 ConsoleRecord = NULL;
627 }
628 }
629
630 /* Leave the critical section */
631 RtlLeaveCriticalSection(CriticalSection);
632
633 /* Unlock the process */
634 CsrUnlockProcess(ClientProcess);
635
636 return Status;
637 }
638
639 CSR_API(BaseSrvUpdateVDMEntry)
640 {
641 NTSTATUS Status;
642 PBASE_UPDATE_VDM_ENTRY UpdateVdmEntryRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.UpdateVDMEntryRequest;
643 PRTL_CRITICAL_SECTION CriticalSection = NULL;
644 PVDM_CONSOLE_RECORD ConsoleRecord = NULL;
645 PVDM_DOS_RECORD DosRecord = NULL;
646
647 CriticalSection = (UpdateVdmEntryRequest->BinaryType != BINARY_TYPE_SEPARATE_WOW)
648 ? &DosCriticalSection
649 : &WowCriticalSection;
650
651 /* Enter the critical section */
652 RtlEnterCriticalSection(CriticalSection);
653
654 /* Check if this is a DOS or WOW VDM */
655 if (UpdateVdmEntryRequest->BinaryType != BINARY_TYPE_SEPARATE_WOW)
656 {
657 if (UpdateVdmEntryRequest->iTask != 0)
658 {
659 /* Get the console record using the task ID */
660 Status = GetConsoleRecordBySessionId(UpdateVdmEntryRequest->iTask,
661 &ConsoleRecord);
662 }
663 else
664 {
665 /* Get the console record using the console handle */
666 Status = BaseSrvGetConsoleRecord(UpdateVdmEntryRequest->ConsoleHandle,
667 &ConsoleRecord);
668 }
669
670 if (!NT_SUCCESS(Status)) goto Cleanup;
671
672 /* Get the primary DOS record */
673 DosRecord = (PVDM_DOS_RECORD)CONTAINING_RECORD(ConsoleRecord->DosListHead.Flink,
674 VDM_DOS_RECORD,
675 Entry);
676
677 switch (UpdateVdmEntryRequest->EntryIndex)
678 {
679 case VdmEntryUndo:
680 {
681 /* Close the server event handle, the client will close the client handle */
682 NtClose(DosRecord->ServerEvent);
683 DosRecord->ServerEvent = DosRecord->ClientEvent = NULL;
684
685 if (UpdateVdmEntryRequest->VDMCreationState & (VDM_UNDO_PARTIAL | VDM_UNDO_FULL))
686 {
687 /* Remove the DOS record */
688 if (DosRecord->CommandInfo) BaseSrvFreeVDMInfo(DosRecord->CommandInfo);
689 RemoveEntryList(&DosRecord->Entry);
690 RtlFreeHeap(BaseSrvHeap, 0, DosRecord);
691
692 /*
693 * Since this is an undo, if that was the only DOS record the VDM
694 * won't even start, so the console record should be removed too.
695 */
696 if (ConsoleRecord->DosListHead.Flink == &ConsoleRecord->DosListHead)
697 {
698 RemoveEntryList(&ConsoleRecord->Entry);
699 RtlFreeHeap(BaseSrvHeap, 0, ConsoleRecord);
700 }
701 }
702
703 /* It was successful */
704 Status = STATUS_SUCCESS;
705
706 break;
707 }
708
709 case VdmEntryUpdateProcess:
710 {
711 /* Duplicate the VDM process handle */
712 Status = NtDuplicateObject(CsrGetClientThread()->Process->ProcessHandle,
713 UpdateVdmEntryRequest->VDMProcessHandle,
714 NtCurrentProcess(),
715 &ConsoleRecord->ProcessHandle,
716 0,
717 0,
718 DUPLICATE_SAME_ATTRIBUTES | DUPLICATE_SAME_ACCESS);
719 if (!NT_SUCCESS(Status)) goto Cleanup;
720
721 /* Create a pair of handles to one event object */
722 Status = BaseSrvCreatePairWaitHandles(&DosRecord->ServerEvent,
723 &DosRecord->ClientEvent);
724 if (!NT_SUCCESS(Status)) goto Cleanup;
725
726 /* Return the client event handle */
727 UpdateVdmEntryRequest->WaitObjectForParent = DosRecord->ClientEvent;
728
729 break;
730 }
731
732 case VdmEntryUpdateControlCHandler:
733 {
734 // TODO: NOT IMPLEMENTED
735 DPRINT1("BaseSrvUpdateVDMEntry: VdmEntryUpdateControlCHandler not implemented!");
736 Status = STATUS_NOT_IMPLEMENTED;
737
738 break;
739 }
740
741 default:
742 {
743 /* Invalid */
744 Status = STATUS_INVALID_PARAMETER;
745 }
746 }
747 }
748 else
749 {
750 // TODO: NOT IMPLEMENTED
751 UNIMPLEMENTED;
752 Status = STATUS_NOT_IMPLEMENTED;
753 }
754
755 Cleanup:
756 /* Leave the critical section */
757 RtlLeaveCriticalSection(CriticalSection);
758
759 return Status;
760 }
761
762 CSR_API(BaseSrvGetNextVDMCommand)
763 {
764 DPRINT1("%s not yet implemented\n", __FUNCTION__);
765 return STATUS_NOT_IMPLEMENTED;
766 }
767
768 CSR_API(BaseSrvExitVDM)
769 {
770 NTSTATUS Status;
771 PBASE_EXIT_VDM ExitVdmRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.ExitVDMRequest;
772 PRTL_CRITICAL_SECTION CriticalSection = NULL;
773 PVDM_CONSOLE_RECORD ConsoleRecord = NULL;
774 PVDM_DOS_RECORD DosRecord;
775
776 CriticalSection = (ExitVdmRequest->iWowTask == 0)
777 ? &DosCriticalSection
778 : &WowCriticalSection;
779
780 /* Enter the critical section */
781 RtlEnterCriticalSection(CriticalSection);
782
783 if (ExitVdmRequest->iWowTask == 0)
784 {
785 /* Get the console record */
786 Status = BaseSrvGetConsoleRecord(ExitVdmRequest->ConsoleHandle, &ConsoleRecord);
787 if (!NT_SUCCESS(Status)) goto Cleanup;
788
789 /* Cleanup the DOS records */
790 while (ConsoleRecord->DosListHead.Flink != &ConsoleRecord->DosListHead)
791 {
792 DosRecord = CONTAINING_RECORD(ConsoleRecord->DosListHead.Flink,
793 VDM_DOS_RECORD,
794 Entry);
795
796 /* Set the event and close it */
797 NtSetEvent(DosRecord->ServerEvent, NULL);
798 NtClose(DosRecord->ServerEvent);
799
800 /* Remove the DOS entry */
801 if (DosRecord->CommandInfo) BaseSrvFreeVDMInfo(DosRecord->CommandInfo);
802 RemoveEntryList(&DosRecord->Entry);
803 RtlFreeHeap(BaseSrvHeap, 0, DosRecord);
804 }
805
806 if (ConsoleRecord->CurrentDirs != NULL)
807 {
808 /* Free the current directories */
809 RtlFreeHeap(BaseSrvHeap, 0, ConsoleRecord->CurrentDirs);
810 ConsoleRecord->CurrentDirs = NULL;
811 ConsoleRecord->CurDirsLength = 0;
812 }
813
814 /* Remove the console record */
815 RemoveEntryList(&ConsoleRecord->Entry);
816 RtlFreeHeap(BaseSrvHeap, 0, ConsoleRecord);
817 }
818 else
819 {
820 // TODO: NOT IMPLEMENTED
821 UNIMPLEMENTED;
822 Status = STATUS_NOT_IMPLEMENTED;
823 }
824
825 Cleanup:
826 /* Leave the critical section */
827 RtlLeaveCriticalSection(CriticalSection);
828
829 return Status;
830 }
831
832 CSR_API(BaseSrvIsFirstVDM)
833 {
834 PBASE_IS_FIRST_VDM IsFirstVDMRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.IsFirstVDMRequest;
835
836 /* Return the result */
837 IsFirstVDMRequest->FirstVDM = FirstVDM;
838
839 /* Clear the first VDM flag */
840 FirstVDM = FALSE;
841
842 return STATUS_SUCCESS;
843 }
844
845 CSR_API(BaseSrvGetVDMExitCode)
846 {
847 NTSTATUS Status;
848 PBASE_GET_VDM_EXIT_CODE GetVDMExitCodeRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.GetVDMExitCodeRequest;
849 PLIST_ENTRY i = NULL;
850 PVDM_CONSOLE_RECORD ConsoleRecord = NULL;
851 PVDM_DOS_RECORD DosRecord = NULL;
852
853 /* Enter the critical section */
854 RtlEnterCriticalSection(&DosCriticalSection);
855
856 /* Get the console record */
857 Status = BaseSrvGetConsoleRecord(GetVDMExitCodeRequest->ConsoleHandle, &ConsoleRecord);
858 if (!NT_SUCCESS(Status)) goto Cleanup;
859
860 /* Search for a DOS record that has the same parent process handle */
861 for (i = ConsoleRecord->DosListHead.Flink; i != &ConsoleRecord->DosListHead; i = i->Flink)
862 {
863 DosRecord = CONTAINING_RECORD(i, VDM_DOS_RECORD, Entry);
864 if (DosRecord->ClientEvent == GetVDMExitCodeRequest->hParent) break;
865 }
866
867 /* Check if no DOS record was found */
868 if (i == &ConsoleRecord->DosListHead)
869 {
870 Status = STATUS_NOT_FOUND;
871 goto Cleanup;
872 }
873
874 /* Check if this task is still running */
875 if (DosRecord->State == VDM_READY)
876 {
877 GetVDMExitCodeRequest->ExitCode = STATUS_PENDING;
878 goto Cleanup;
879 }
880
881 /* Return the exit code */
882 GetVDMExitCodeRequest->ExitCode = DosRecord->ExitCode;
883
884 /* Since this is a zombie task record, remove it */
885 if (DosRecord->CommandInfo) BaseSrvFreeVDMInfo(DosRecord->CommandInfo);
886 RemoveEntryList(&DosRecord->Entry);
887 RtlFreeHeap(BaseSrvHeap, 0, DosRecord);
888
889 Cleanup:
890 /* Leave the critical section */
891 RtlLeaveCriticalSection(&DosCriticalSection);
892
893 return Status;
894 }
895
896 CSR_API(BaseSrvSetReenterCount)
897 {
898 NTSTATUS Status = STATUS_SUCCESS;
899 PBASE_SET_REENTER_COUNT SetReenterCountRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.SetReenterCountRequest;
900 PVDM_CONSOLE_RECORD ConsoleRecord;
901
902 /* Enter the critical section */
903 RtlEnterCriticalSection(&DosCriticalSection);
904
905 /* Get the console record */
906 Status = BaseSrvGetConsoleRecord(SetReenterCountRequest->ConsoleHandle, &ConsoleRecord);
907 if (!NT_SUCCESS(Status)) goto Cleanup;
908
909 if (SetReenterCountRequest->fIncDec == VDM_INC_REENTER_COUNT) ConsoleRecord->ReenterCount++;
910 else if (SetReenterCountRequest->fIncDec == VDM_DEC_REENTER_COUNT)
911 {
912 ConsoleRecord->ReenterCount--;
913 if (ConsoleRecord->ServerEvent != NULL) NtSetEvent(ConsoleRecord->ServerEvent, NULL);
914 }
915 else Status = STATUS_INVALID_PARAMETER;
916
917 Cleanup:
918 /* Leave the critical section */
919 RtlLeaveCriticalSection(&DosCriticalSection);
920
921 return Status;
922 }
923
924 CSR_API(BaseSrvSetVDMCurDirs)
925 {
926 NTSTATUS Status;
927 PBASE_GETSET_VDM_CURDIRS VDMCurrentDirsRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.VDMCurrentDirsRequest;
928 PVDM_CONSOLE_RECORD ConsoleRecord;
929 PCHAR Buffer = NULL;
930
931 /* Validate the input buffer */
932 if (!CsrValidateMessageBuffer(ApiMessage,
933 (PVOID*)&VDMCurrentDirsRequest->lpszzCurDirs,
934 VDMCurrentDirsRequest->cchCurDirs,
935 sizeof(*VDMCurrentDirsRequest->lpszzCurDirs)))
936 {
937 return STATUS_INVALID_PARAMETER;
938 }
939
940 /* Enter the critical section */
941 RtlEnterCriticalSection(&DosCriticalSection);
942
943 /* Find the console record */
944 Status = BaseSrvGetConsoleRecord(VDMCurrentDirsRequest->ConsoleHandle, &ConsoleRecord);
945 if (!NT_SUCCESS(Status)) goto Cleanup;
946
947 if (ConsoleRecord->CurrentDirs == NULL)
948 {
949 /* Allocate memory for the current directory information */
950 Buffer = RtlAllocateHeap(BaseSrvHeap,
951 HEAP_ZERO_MEMORY,
952 VDMCurrentDirsRequest->cchCurDirs);
953 }
954 else
955 {
956 /* Resize the amount of allocated memory */
957 Buffer = RtlReAllocateHeap(BaseSrvHeap,
958 HEAP_ZERO_MEMORY,
959 ConsoleRecord->CurrentDirs,
960 VDMCurrentDirsRequest->cchCurDirs);
961 }
962
963 if (Buffer == NULL)
964 {
965 /* Allocation failed */
966 Status = STATUS_NO_MEMORY;
967 goto Cleanup;
968 }
969
970 /* Update the console record */
971 ConsoleRecord->CurrentDirs = Buffer;
972 ConsoleRecord->CurDirsLength = VDMCurrentDirsRequest->cchCurDirs;
973
974 /* Copy the data */
975 RtlMoveMemory(ConsoleRecord->CurrentDirs,
976 VDMCurrentDirsRequest->lpszzCurDirs,
977 VDMCurrentDirsRequest->cchCurDirs);
978
979 Cleanup:
980 /* Leave the critical section */
981 RtlLeaveCriticalSection(&DosCriticalSection);
982
983 return Status;
984 }
985
986 CSR_API(BaseSrvGetVDMCurDirs)
987 {
988 NTSTATUS Status;
989 PBASE_GETSET_VDM_CURDIRS VDMCurrentDirsRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.VDMCurrentDirsRequest;
990 PVDM_CONSOLE_RECORD ConsoleRecord;
991
992 /* Validate the output buffer */
993 if (!CsrValidateMessageBuffer(ApiMessage,
994 (PVOID*)&VDMCurrentDirsRequest->lpszzCurDirs,
995 VDMCurrentDirsRequest->cchCurDirs,
996 sizeof(*VDMCurrentDirsRequest->lpszzCurDirs)))
997 {
998 return STATUS_INVALID_PARAMETER;
999 }
1000
1001 /* Enter the critical section */
1002 RtlEnterCriticalSection(&DosCriticalSection);
1003
1004 /* Find the console record */
1005 Status = BaseSrvGetConsoleRecord(VDMCurrentDirsRequest->ConsoleHandle, &ConsoleRecord);
1006 if (!NT_SUCCESS(Status)) goto Cleanup;
1007
1008 /* Return the actual size of the current directory information */
1009 VDMCurrentDirsRequest->cchCurDirs = ConsoleRecord->CurDirsLength;
1010
1011 /* Check if the buffer is large enough */
1012 if (VDMCurrentDirsRequest->cchCurDirs < ConsoleRecord->CurDirsLength)
1013 {
1014 Status = STATUS_BUFFER_TOO_SMALL;
1015 goto Cleanup;
1016 }
1017
1018 /* Copy the data */
1019 RtlMoveMemory(VDMCurrentDirsRequest->lpszzCurDirs,
1020 ConsoleRecord->CurrentDirs,
1021 ConsoleRecord->CurDirsLength);
1022
1023 Cleanup:
1024 /* Leave the critical section */
1025 RtlLeaveCriticalSection(&DosCriticalSection);
1026
1027 return Status;
1028 }
1029
1030 CSR_API(BaseSrvBatNotification)
1031 {
1032 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1033 return STATUS_NOT_IMPLEMENTED;
1034 }
1035
1036 CSR_API(BaseSrvRegisterWowExec)
1037 {
1038 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1039 return STATUS_NOT_IMPLEMENTED;
1040 }
1041
1042 CSR_API(BaseSrvRefreshIniFileMapping)
1043 {
1044 DPRINT1("%s not yet implemented\n", __FUNCTION__);
1045 return STATUS_NOT_IMPLEMENTED;
1046 }
1047
1048 /* EOF */