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