b4e53475f0bdf1b62d6f6c17491d622d5136e6d7
[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 BaseInitializeVDM(VOID)
370 {
371 /* Initialize the list head */
372 InitializeListHead(&VDMConsoleListHead);
373
374 /* Initialize the critical section */
375 RtlInitializeCriticalSection(&DosCriticalSection);
376 RtlInitializeCriticalSection(&WowCriticalSection);
377 }
378
379 /* PUBLIC SERVER APIS *********************************************************/
380
381 CSR_API(BaseSrvCheckVDM)
382 {
383 NTSTATUS Status;
384 PBASE_CHECK_VDM CheckVdmRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.CheckVDMRequest;
385 PCSR_PROCESS ClientProcess;
386 PRTL_CRITICAL_SECTION CriticalSection = NULL;
387 PVDM_CONSOLE_RECORD ConsoleRecord = NULL;
388 PVDM_DOS_RECORD DosRecord = NULL;
389 BOOLEAN NewConsoleRecord = FALSE;
390
391 /* Don't do anything if the VDM has been disabled in the registry */
392 if (!BaseSrvIsVdmAllowed()) return STATUS_ACCESS_DENIED;
393
394 /* Validate the message buffers */
395 if (!CsrValidateMessageBuffer(ApiMessage,
396 (PVOID*)&CheckVdmRequest->CmdLine,
397 CheckVdmRequest->CmdLen,
398 sizeof(*CheckVdmRequest->CmdLine))
399 || !CsrValidateMessageBuffer(ApiMessage,
400 (PVOID*)&CheckVdmRequest->AppName,
401 CheckVdmRequest->AppLen,
402 sizeof(*CheckVdmRequest->AppName))
403 || !CsrValidateMessageBuffer(ApiMessage,
404 (PVOID*)&CheckVdmRequest->PifFile,
405 CheckVdmRequest->PifLen,
406 sizeof(*CheckVdmRequest->PifFile))
407 || !CsrValidateMessageBuffer(ApiMessage,
408 (PVOID*)&CheckVdmRequest->CurDirectory,
409 CheckVdmRequest->CurDirectoryLen,
410 sizeof(*CheckVdmRequest->CurDirectory))
411 || !CsrValidateMessageBuffer(ApiMessage,
412 (PVOID*)&CheckVdmRequest->Desktop,
413 CheckVdmRequest->DesktopLen,
414 sizeof(*CheckVdmRequest->Desktop))
415 || !CsrValidateMessageBuffer(ApiMessage,
416 (PVOID*)&CheckVdmRequest->Title,
417 CheckVdmRequest->TitleLen,
418 sizeof(*CheckVdmRequest->Title))
419 || !CsrValidateMessageBuffer(ApiMessage,
420 (PVOID*)&CheckVdmRequest->Reserved,
421 CheckVdmRequest->ReservedLen,
422 sizeof(*CheckVdmRequest->Reserved)))
423 {
424 return STATUS_INVALID_PARAMETER;
425 }
426
427 /* Lock the process */
428 Status = CsrLockProcessByClientId(ApiMessage->Header.ClientId.UniqueProcess,
429 &ClientProcess);
430 if (!NT_SUCCESS(Status)) return Status;
431
432 CriticalSection = (CheckVdmRequest->BinaryType != BINARY_TYPE_SEPARATE_WOW)
433 ? &DosCriticalSection
434 : &WowCriticalSection;
435
436 /* Enter the critical section */
437 RtlEnterCriticalSection(CriticalSection);
438
439 /* Check if this is a DOS or WOW VDM */
440 if (CheckVdmRequest->BinaryType != BINARY_TYPE_SEPARATE_WOW)
441 {
442 /* Get the console record */
443 Status = BaseSrvGetConsoleRecord(CheckVdmRequest->ConsoleHandle,
444 &ConsoleRecord);
445
446 if (!NT_SUCCESS(Status))
447 {
448 /* Allocate a new console record */
449 ConsoleRecord = (PVDM_CONSOLE_RECORD)RtlAllocateHeap(BaseSrvHeap,
450 HEAP_ZERO_MEMORY,
451 sizeof(VDM_CONSOLE_RECORD));
452 if (ConsoleRecord == NULL)
453 {
454 Status = STATUS_NO_MEMORY;
455 goto Cleanup;
456 }
457
458 /* Initialize the console record */
459 ConsoleRecord->ConsoleHandle = CheckVdmRequest->ConsoleHandle;
460 ConsoleRecord->ProcessHandle = ClientProcess->ProcessHandle;
461 ConsoleRecord->ServerEvent = ConsoleRecord->ClientEvent = NULL;
462 ConsoleRecord->ReenterCount = 0;
463 ConsoleRecord->CurrentDirs = NULL;
464 ConsoleRecord->CurDirsLength = 0;
465 ConsoleRecord->SessionId = GetNextDosSesId();
466 InitializeListHead(&ConsoleRecord->DosListHead);
467 // TODO: The console record structure is incomplete
468
469 /* Remember that the console record was allocated here */
470 NewConsoleRecord = TRUE;
471 }
472
473 /* Allocate a new DOS record */
474 DosRecord = (PVDM_DOS_RECORD)RtlAllocateHeap(BaseSrvHeap,
475 HEAP_ZERO_MEMORY,
476 sizeof(VDM_DOS_RECORD));
477 if (DosRecord == NULL)
478 {
479 Status = STATUS_NO_MEMORY;
480 goto Cleanup;
481 }
482
483 /* Initialize the DOS record */
484 DosRecord->State = NewConsoleRecord ? VDM_NOT_LOADED : VDM_READY;
485 DosRecord->ExitCode = 0;
486 // TODO: The DOS record structure is incomplete
487
488 Status = BaseSrvCreatePairWaitHandles(&DosRecord->ServerEvent, &DosRecord->ClientEvent);
489 if (!NT_SUCCESS(Status)) goto Cleanup;
490
491 /* Return the client event handle */
492 CheckVdmRequest->WaitObjectForParent = DosRecord->ClientEvent;
493
494 /* Translate the input structure into a VDM command structure and set it in the DOS record */
495 if (!BaseSrvCopyCommand(CheckVdmRequest, DosRecord))
496 {
497 /* The only possibility is that an allocation failure occurred */
498 Status = STATUS_NO_MEMORY;
499 goto Cleanup;
500 }
501
502 /* Add the DOS record */
503 InsertHeadList(&ConsoleRecord->DosListHead, &DosRecord->Entry);
504
505 if (NewConsoleRecord)
506 {
507 /* Add the console record */
508 InsertTailList(&VDMConsoleListHead, &ConsoleRecord->Entry);
509 }
510
511 CheckVdmRequest->VDMState = DosRecord->State;
512 Status = STATUS_SUCCESS;
513 }
514 else
515 {
516 // TODO: NOT IMPLEMENTED
517 UNIMPLEMENTED;
518 Status = STATUS_NOT_IMPLEMENTED;
519 }
520
521 Cleanup:
522 /* Check if it failed */
523 if (!NT_SUCCESS(Status))
524 {
525 /* Free the DOS record */
526 if (DosRecord != NULL)
527 {
528 if (DosRecord->ServerEvent) NtClose(DosRecord->ServerEvent);
529 if (DosRecord->ClientEvent)
530 {
531 /* Close the remote handle */
532 NtDuplicateObject(CsrGetClientThread()->Process->ProcessHandle,
533 DosRecord->ClientEvent,
534 NULL,
535 NULL,
536 0,
537 0,
538 DUPLICATE_CLOSE_SOURCE);
539 }
540
541 RtlFreeHeap(BaseSrvHeap, 0, DosRecord);
542 DosRecord = NULL;
543 }
544
545 /* Free the console record if it was allocated here */
546 if (NewConsoleRecord)
547 {
548 RtlFreeHeap(BaseSrvHeap, 0, ConsoleRecord);
549 ConsoleRecord = NULL;
550 }
551 }
552
553 /* Leave the critical section */
554 RtlLeaveCriticalSection(CriticalSection);
555
556 /* Unlock the process */
557 CsrUnlockProcess(ClientProcess);
558
559 return Status;
560 }
561
562 CSR_API(BaseSrvUpdateVDMEntry)
563 {
564 NTSTATUS Status;
565 PBASE_UPDATE_VDM_ENTRY UpdateVdmEntryRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.UpdateVDMEntryRequest;
566 PRTL_CRITICAL_SECTION CriticalSection = NULL;
567 PVDM_CONSOLE_RECORD ConsoleRecord = NULL;
568 PVDM_DOS_RECORD DosRecord = NULL;
569
570 CriticalSection = (UpdateVdmEntryRequest->BinaryType != BINARY_TYPE_SEPARATE_WOW)
571 ? &DosCriticalSection
572 : &WowCriticalSection;
573
574 /* Enter the critical section */
575 RtlEnterCriticalSection(CriticalSection);
576
577 /* Check if this is a DOS or WOW VDM */
578 if (UpdateVdmEntryRequest->BinaryType != BINARY_TYPE_SEPARATE_WOW)
579 {
580 if (UpdateVdmEntryRequest->iTask != 0)
581 {
582 /* Get the console record using the task ID */
583 Status = GetConsoleRecordBySessionId(UpdateVdmEntryRequest->iTask,
584 &ConsoleRecord);
585 }
586 else
587 {
588 /* Get the console record using the console handle */
589 Status = BaseSrvGetConsoleRecord(UpdateVdmEntryRequest->ConsoleHandle,
590 &ConsoleRecord);
591 }
592
593 if (!NT_SUCCESS(Status)) goto Cleanup;
594
595 /* Get the primary DOS record */
596 DosRecord = (PVDM_DOS_RECORD)CONTAINING_RECORD(ConsoleRecord->DosListHead.Flink,
597 VDM_DOS_RECORD,
598 Entry);
599
600 switch (UpdateVdmEntryRequest->EntryIndex)
601 {
602 case VdmEntryUndo:
603 {
604 /* Close the server event handle, the client will close the client handle */
605 NtClose(DosRecord->ServerEvent);
606 DosRecord->ServerEvent = DosRecord->ClientEvent = NULL;
607
608 if (UpdateVdmEntryRequest->VDMCreationState & (VDM_UNDO_PARTIAL | VDM_UNDO_FULL))
609 {
610 /* Remove the DOS record */
611 if (DosRecord->CommandInfo) BaseSrvFreeVDMInfo(DosRecord->CommandInfo);
612 RemoveEntryList(&DosRecord->Entry);
613 RtlFreeHeap(BaseSrvHeap, 0, DosRecord);
614
615 /*
616 * Since this is an undo, if that was the only DOS record the VDM
617 * won't even start, so the console record should be removed too.
618 */
619 if (ConsoleRecord->DosListHead.Flink == &ConsoleRecord->DosListHead)
620 {
621 RemoveEntryList(&ConsoleRecord->Entry);
622 RtlFreeHeap(BaseSrvHeap, 0, ConsoleRecord);
623 }
624 }
625
626 /* It was successful */
627 Status = STATUS_SUCCESS;
628
629 break;
630 }
631
632 case VdmEntryUpdateProcess:
633 {
634 /* Duplicate the VDM process handle */
635 Status = NtDuplicateObject(CsrGetClientThread()->Process->ProcessHandle,
636 UpdateVdmEntryRequest->VDMProcessHandle,
637 NtCurrentProcess(),
638 &ConsoleRecord->ProcessHandle,
639 0,
640 0,
641 DUPLICATE_SAME_ATTRIBUTES | DUPLICATE_SAME_ACCESS);
642 if (!NT_SUCCESS(Status)) goto Cleanup;
643
644 /* Create a pair of handles to one event object */
645 Status = BaseSrvCreatePairWaitHandles(&DosRecord->ServerEvent,
646 &DosRecord->ClientEvent);
647 if (!NT_SUCCESS(Status)) goto Cleanup;
648
649 /* Return the client event handle */
650 UpdateVdmEntryRequest->WaitObjectForParent = DosRecord->ClientEvent;
651
652 break;
653 }
654
655 case VdmEntryUpdateControlCHandler:
656 {
657 // TODO: NOT IMPLEMENTED
658 DPRINT1("BaseSrvUpdateVDMEntry: VdmEntryUpdateControlCHandler not implemented!");
659 Status = STATUS_NOT_IMPLEMENTED;
660
661 break;
662 }
663
664 default:
665 {
666 /* Invalid */
667 Status = STATUS_INVALID_PARAMETER;
668 }
669 }
670 }
671 else
672 {
673 // TODO: NOT IMPLEMENTED
674 UNIMPLEMENTED;
675 Status = STATUS_NOT_IMPLEMENTED;
676 }
677
678 Cleanup:
679 /* Leave the critical section */
680 RtlLeaveCriticalSection(CriticalSection);
681
682 return Status;
683 }
684
685 CSR_API(BaseSrvGetNextVDMCommand)
686 {
687 DPRINT1("%s not yet implemented\n", __FUNCTION__);
688 return STATUS_NOT_IMPLEMENTED;
689 }
690
691 CSR_API(BaseSrvExitVDM)
692 {
693 NTSTATUS Status;
694 PBASE_EXIT_VDM ExitVdmRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.ExitVDMRequest;
695 PRTL_CRITICAL_SECTION CriticalSection = NULL;
696 PVDM_CONSOLE_RECORD ConsoleRecord = NULL;
697 PVDM_DOS_RECORD DosRecord;
698
699 CriticalSection = (ExitVdmRequest->iWowTask == 0)
700 ? &DosCriticalSection
701 : &WowCriticalSection;
702
703 /* Enter the critical section */
704 RtlEnterCriticalSection(CriticalSection);
705
706 if (ExitVdmRequest->iWowTask == 0)
707 {
708 /* Get the console record */
709 Status = BaseSrvGetConsoleRecord(ExitVdmRequest->ConsoleHandle, &ConsoleRecord);
710 if (!NT_SUCCESS(Status)) goto Cleanup;
711
712 /* Cleanup the DOS records */
713 while (ConsoleRecord->DosListHead.Flink != &ConsoleRecord->DosListHead)
714 {
715 DosRecord = CONTAINING_RECORD(ConsoleRecord->DosListHead.Flink,
716 VDM_DOS_RECORD,
717 Entry);
718
719 /* Set the event and close it */
720 NtSetEvent(DosRecord->ServerEvent, NULL);
721 NtClose(DosRecord->ServerEvent);
722
723 /* Remove the DOS entry */
724 if (DosRecord->CommandInfo) BaseSrvFreeVDMInfo(DosRecord->CommandInfo);
725 RemoveEntryList(&DosRecord->Entry);
726 RtlFreeHeap(BaseSrvHeap, 0, DosRecord);
727 }
728
729 if (ConsoleRecord->CurrentDirs != NULL)
730 {
731 /* Free the current directories */
732 RtlFreeHeap(BaseSrvHeap, 0, ConsoleRecord->CurrentDirs);
733 ConsoleRecord->CurrentDirs = NULL;
734 ConsoleRecord->CurDirsLength = 0;
735 }
736
737 /* Remove the console record */
738 RemoveEntryList(&ConsoleRecord->Entry);
739 RtlFreeHeap(BaseSrvHeap, 0, ConsoleRecord);
740 }
741 else
742 {
743 // TODO: NOT IMPLEMENTED
744 UNIMPLEMENTED;
745 Status = STATUS_NOT_IMPLEMENTED;
746 }
747
748 Cleanup:
749 /* Leave the critical section */
750 RtlLeaveCriticalSection(CriticalSection);
751
752 return Status;
753 }
754
755 CSR_API(BaseSrvIsFirstVDM)
756 {
757 PBASE_IS_FIRST_VDM IsFirstVDMRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.IsFirstVDMRequest;
758
759 /* Return the result */
760 IsFirstVDMRequest->FirstVDM = FirstVDM;
761
762 /* Clear the first VDM flag */
763 FirstVDM = FALSE;
764
765 return STATUS_SUCCESS;
766 }
767
768 CSR_API(BaseSrvGetVDMExitCode)
769 {
770 NTSTATUS Status;
771 PBASE_GET_VDM_EXIT_CODE GetVDMExitCodeRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.GetVDMExitCodeRequest;
772 PLIST_ENTRY i = NULL;
773 PVDM_CONSOLE_RECORD ConsoleRecord = NULL;
774 PVDM_DOS_RECORD DosRecord = NULL;
775
776 /* Enter the critical section */
777 RtlEnterCriticalSection(&DosCriticalSection);
778
779 /* Get the console record */
780 Status = BaseSrvGetConsoleRecord(GetVDMExitCodeRequest->ConsoleHandle, &ConsoleRecord);
781 if (!NT_SUCCESS(Status)) goto Cleanup;
782
783 /* Search for a DOS record that has the same parent process handle */
784 for (i = ConsoleRecord->DosListHead.Flink; i != &ConsoleRecord->DosListHead; i = i->Flink)
785 {
786 DosRecord = CONTAINING_RECORD(i, VDM_DOS_RECORD, Entry);
787 if (DosRecord->ClientEvent == GetVDMExitCodeRequest->hParent) break;
788 }
789
790 /* Check if no DOS record was found */
791 if (i == &ConsoleRecord->DosListHead)
792 {
793 Status = STATUS_NOT_FOUND;
794 goto Cleanup;
795 }
796
797 /* Check if this task is still running */
798 if (DosRecord->State == VDM_READY)
799 {
800 GetVDMExitCodeRequest->ExitCode = STATUS_PENDING;
801 goto Cleanup;
802 }
803
804 /* Return the exit code */
805 GetVDMExitCodeRequest->ExitCode = DosRecord->ExitCode;
806
807 /* Since this is a zombie task record, remove it */
808 if (DosRecord->CommandInfo) BaseSrvFreeVDMInfo(DosRecord->CommandInfo);
809 RemoveEntryList(&DosRecord->Entry);
810 RtlFreeHeap(BaseSrvHeap, 0, DosRecord);
811
812 Cleanup:
813 /* Leave the critical section */
814 RtlLeaveCriticalSection(&DosCriticalSection);
815
816 return Status;
817 }
818
819 CSR_API(BaseSrvSetReenterCount)
820 {
821 NTSTATUS Status = STATUS_SUCCESS;
822 PBASE_SET_REENTER_COUNT SetReenterCountRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.SetReenterCountRequest;
823 PVDM_CONSOLE_RECORD ConsoleRecord;
824
825 /* Enter the critical section */
826 RtlEnterCriticalSection(&DosCriticalSection);
827
828 /* Get the console record */
829 Status = BaseSrvGetConsoleRecord(SetReenterCountRequest->ConsoleHandle, &ConsoleRecord);
830 if (!NT_SUCCESS(Status)) goto Cleanup;
831
832 if (SetReenterCountRequest->fIncDec == VDM_INC_REENTER_COUNT) ConsoleRecord->ReenterCount++;
833 else if (SetReenterCountRequest->fIncDec == VDM_DEC_REENTER_COUNT)
834 {
835 ConsoleRecord->ReenterCount--;
836 if (ConsoleRecord->ServerEvent != NULL) NtSetEvent(ConsoleRecord->ServerEvent, NULL);
837 }
838 else Status = STATUS_INVALID_PARAMETER;
839
840 Cleanup:
841 /* Leave the critical section */
842 RtlLeaveCriticalSection(&DosCriticalSection);
843
844 return Status;
845 }
846
847 CSR_API(BaseSrvSetVDMCurDirs)
848 {
849 NTSTATUS Status;
850 PBASE_GETSET_VDM_CURDIRS VDMCurrentDirsRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.VDMCurrentDirsRequest;
851 PVDM_CONSOLE_RECORD ConsoleRecord;
852 PCHAR Buffer = NULL;
853
854 /* Validate the input buffer */
855 if (!CsrValidateMessageBuffer(ApiMessage,
856 (PVOID*)&VDMCurrentDirsRequest->lpszzCurDirs,
857 VDMCurrentDirsRequest->cchCurDirs,
858 sizeof(*VDMCurrentDirsRequest->lpszzCurDirs)))
859 {
860 return STATUS_INVALID_PARAMETER;
861 }
862
863 /* Enter the critical section */
864 RtlEnterCriticalSection(&DosCriticalSection);
865
866 /* Find the console record */
867 Status = BaseSrvGetConsoleRecord(VDMCurrentDirsRequest->ConsoleHandle, &ConsoleRecord);
868 if (!NT_SUCCESS(Status)) goto Cleanup;
869
870 if (ConsoleRecord->CurrentDirs == NULL)
871 {
872 /* Allocate memory for the current directory information */
873 Buffer = RtlAllocateHeap(BaseSrvHeap,
874 HEAP_ZERO_MEMORY,
875 VDMCurrentDirsRequest->cchCurDirs);
876 }
877 else
878 {
879 /* Resize the amount of allocated memory */
880 Buffer = RtlReAllocateHeap(BaseSrvHeap,
881 HEAP_ZERO_MEMORY,
882 ConsoleRecord->CurrentDirs,
883 VDMCurrentDirsRequest->cchCurDirs);
884 }
885
886 if (Buffer == NULL)
887 {
888 /* Allocation failed */
889 Status = STATUS_NO_MEMORY;
890 goto Cleanup;
891 }
892
893 /* Update the console record */
894 ConsoleRecord->CurrentDirs = Buffer;
895 ConsoleRecord->CurDirsLength = VDMCurrentDirsRequest->cchCurDirs;
896
897 /* Copy the data */
898 RtlMoveMemory(ConsoleRecord->CurrentDirs,
899 VDMCurrentDirsRequest->lpszzCurDirs,
900 VDMCurrentDirsRequest->cchCurDirs);
901
902 Cleanup:
903 /* Leave the critical section */
904 RtlLeaveCriticalSection(&DosCriticalSection);
905
906 return Status;
907 }
908
909 CSR_API(BaseSrvGetVDMCurDirs)
910 {
911 NTSTATUS Status;
912 PBASE_GETSET_VDM_CURDIRS VDMCurrentDirsRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.VDMCurrentDirsRequest;
913 PVDM_CONSOLE_RECORD ConsoleRecord;
914
915 /* Validate the output buffer */
916 if (!CsrValidateMessageBuffer(ApiMessage,
917 (PVOID*)&VDMCurrentDirsRequest->lpszzCurDirs,
918 VDMCurrentDirsRequest->cchCurDirs,
919 sizeof(*VDMCurrentDirsRequest->lpszzCurDirs)))
920 {
921 return STATUS_INVALID_PARAMETER;
922 }
923
924 /* Enter the critical section */
925 RtlEnterCriticalSection(&DosCriticalSection);
926
927 /* Find the console record */
928 Status = BaseSrvGetConsoleRecord(VDMCurrentDirsRequest->ConsoleHandle, &ConsoleRecord);
929 if (!NT_SUCCESS(Status)) goto Cleanup;
930
931 /* Return the actual size of the current directory information */
932 VDMCurrentDirsRequest->cchCurDirs = ConsoleRecord->CurDirsLength;
933
934 /* Check if the buffer is large enough */
935 if (VDMCurrentDirsRequest->cchCurDirs < ConsoleRecord->CurDirsLength)
936 {
937 Status = STATUS_BUFFER_TOO_SMALL;
938 goto Cleanup;
939 }
940
941 /* Copy the data */
942 RtlMoveMemory(VDMCurrentDirsRequest->lpszzCurDirs,
943 ConsoleRecord->CurrentDirs,
944 ConsoleRecord->CurDirsLength);
945
946 Cleanup:
947 /* Leave the critical section */
948 RtlLeaveCriticalSection(&DosCriticalSection);
949
950 return Status;
951 }
952
953 CSR_API(BaseSrvBatNotification)
954 {
955 DPRINT1("%s not yet implemented\n", __FUNCTION__);
956 return STATUS_NOT_IMPLEMENTED;
957 }
958
959 CSR_API(BaseSrvRegisterWowExec)
960 {
961 DPRINT1("%s not yet implemented\n", __FUNCTION__);
962 return STATUS_NOT_IMPLEMENTED;
963 }
964
965 CSR_API(BaseSrvRefreshIniFileMapping)
966 {
967 DPRINT1("%s not yet implemented\n", __FUNCTION__);
968 return STATUS_NOT_IMPLEMENTED;
969 }
970
971 /* EOF */