[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 ULONG NTAPI GetNextDosSesId(VOID)
44 {
45 ULONG SessionId;
46 PLIST_ENTRY i;
47 PVDM_CONSOLE_RECORD CurrentRecord = NULL;
48 BOOLEAN Found;
49
50 /* Search for an available session ID */
51 for (SessionId = 1; SessionId != 0; SessionId++)
52 {
53 Found = FALSE;
54
55 /* Check if the ID is already in use */
56 for (i = VDMConsoleListHead.Flink; i != &VDMConsoleListHead; i = i->Flink)
57 {
58 CurrentRecord = CONTAINING_RECORD(i, VDM_CONSOLE_RECORD, Entry);
59 if (CurrentRecord->SessionId == SessionId) Found = TRUE;
60 }
61
62 /* If not, we found one */
63 if (!Found) break;
64 }
65
66 ASSERT(SessionId != 0);
67
68 /* Return the session ID */
69 return SessionId;
70 }
71
72 BOOLEAN NTAPI BaseSrvIsVdmAllowed(VOID)
73 {
74 NTSTATUS Status;
75 BOOLEAN VdmAllowed = TRUE;
76 HANDLE RootKey, KeyHandle;
77 UNICODE_STRING KeyName, ValueName, MachineKeyName;
78 OBJECT_ATTRIBUTES Attributes;
79 UCHAR ValueBuffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(ULONG)];
80 PKEY_VALUE_PARTIAL_INFORMATION ValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)ValueBuffer;
81 ULONG ActualSize;
82
83 /* Initialize the unicode strings */
84 RtlInitUnicodeString(&MachineKeyName, L"\\Registry\\Machine");
85 RtlInitUnicodeString(&KeyName, VDM_POLICY_KEY_NAME);
86 RtlInitUnicodeString(&ValueName, VDM_DISALLOWED_VALUE_NAME);
87
88 InitializeObjectAttributes(&Attributes,
89 &MachineKeyName,
90 OBJ_CASE_INSENSITIVE,
91 NULL,
92 NULL);
93
94 /* Open the local machine key */
95 Status = NtOpenKey(&RootKey, KEY_READ, &Attributes);
96 if (!NT_SUCCESS(Status)) return FALSE;
97
98 InitializeObjectAttributes(&Attributes,
99 &KeyName,
100 OBJ_CASE_INSENSITIVE,
101 RootKey,
102 NULL);
103
104 /* Open the policy key in the local machine hive, if it exists */
105 if (NT_SUCCESS(NtOpenKey(&KeyHandle, KEY_READ, &Attributes)))
106 {
107 /* Read the value, if it's set */
108 if (NT_SUCCESS(NtQueryValueKey(KeyHandle,
109 &ValueName,
110 KeyValuePartialInformation,
111 ValueInfo,
112 sizeof(ValueBuffer),
113 &ActualSize)))
114 {
115 if (*((PULONG)ValueInfo->Data))
116 {
117 /* The VDM has been disabled in the registry */
118 VdmAllowed = FALSE;
119 }
120 }
121
122 NtClose(KeyHandle);
123 }
124
125 /* Close the local machine key */
126 NtClose(RootKey);
127
128 /* If it's disabled system-wide, there's no need to check the user key */
129 if (!VdmAllowed) return FALSE;
130
131 /* Open the current user key of the client */
132 if (!CsrImpersonateClient(NULL)) return VdmAllowed;
133 Status = RtlOpenCurrentUser(KEY_READ, &RootKey);
134 CsrRevertToSelf();
135
136 /* If that fails, return the system-wide setting */
137 if (!NT_SUCCESS(Status)) return VdmAllowed;
138
139 InitializeObjectAttributes(&Attributes,
140 &KeyName,
141 OBJ_CASE_INSENSITIVE,
142 RootKey,
143 NULL);
144
145 /* Open the policy key in the current user hive, if it exists */
146 if (NT_SUCCESS(NtOpenKey(&KeyHandle, KEY_READ, &Attributes)))
147 {
148 /* Read the value, if it's set */
149 if (NT_SUCCESS(NtQueryValueKey(KeyHandle,
150 &ValueName,
151 KeyValuePartialInformation,
152 ValueInfo,
153 sizeof(ValueBuffer),
154 &ActualSize)))
155 {
156 if (*((PULONG)ValueInfo->Data))
157 {
158 /* The VDM has been disabled in the registry */
159 VdmAllowed = FALSE;
160 }
161 }
162
163 NtClose(KeyHandle);
164 }
165
166 return VdmAllowed;
167 }
168
169 NTSTATUS NTAPI BaseSrvCreatePairWaitHandles(PHANDLE ServerEvent, PHANDLE ClientEvent)
170 {
171 NTSTATUS Status;
172
173 /* Create the event */
174 Status = NtCreateEvent(ServerEvent, EVENT_ALL_ACCESS, NULL, NotificationEvent, FALSE);
175 if (!NT_SUCCESS(Status)) return Status;
176
177 /* Duplicate the event into the client process */
178 Status = NtDuplicateObject(NtCurrentProcess(),
179 *ServerEvent,
180 CsrGetClientThread()->Process->ProcessHandle,
181 ClientEvent,
182 0,
183 0,
184 DUPLICATE_SAME_ATTRIBUTES | DUPLICATE_SAME_ACCESS);
185
186 if (!NT_SUCCESS(Status)) NtClose(*ServerEvent);
187 return Status;
188 }
189
190 VOID NTAPI BaseInitializeVDM(VOID)
191 {
192 /* Initialize the list head */
193 InitializeListHead(&VDMConsoleListHead);
194
195 /* Initialize the critical section */
196 RtlInitializeCriticalSection(&DosCriticalSection);
197 RtlInitializeCriticalSection(&WowCriticalSection);
198 }
199
200 /* PUBLIC SERVER APIS *********************************************************/
201
202 CSR_API(BaseSrvCheckVDM)
203 {
204 NTSTATUS Status;
205 PBASE_CHECK_VDM CheckVdmRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.CheckVDMRequest;
206 PRTL_CRITICAL_SECTION CriticalSection = NULL;
207 PVDM_CONSOLE_RECORD ConsoleRecord = NULL;
208 PVDM_DOS_RECORD DosRecord = NULL;
209 BOOLEAN NewConsoleRecord = FALSE;
210
211 /* Don't do anything if the VDM has been disabled in the registry */
212 if (!BaseSrvIsVdmAllowed()) return STATUS_ACCESS_DENIED;
213
214 /* Validate the message buffers */
215 if (!CsrValidateMessageBuffer(ApiMessage,
216 (PVOID*)&CheckVdmRequest->CmdLine,
217 CheckVdmRequest->CmdLen,
218 sizeof(*CheckVdmRequest->CmdLine))
219 || !CsrValidateMessageBuffer(ApiMessage,
220 (PVOID*)&CheckVdmRequest->AppName,
221 CheckVdmRequest->AppLen,
222 sizeof(*CheckVdmRequest->AppName))
223 || !CsrValidateMessageBuffer(ApiMessage,
224 (PVOID*)&CheckVdmRequest->PifFile,
225 CheckVdmRequest->PifLen,
226 sizeof(*CheckVdmRequest->PifFile))
227 || !CsrValidateMessageBuffer(ApiMessage,
228 (PVOID*)&CheckVdmRequest->CurDirectory,
229 CheckVdmRequest->CurDirectoryLen,
230 sizeof(*CheckVdmRequest->CurDirectory))
231 || !CsrValidateMessageBuffer(ApiMessage,
232 (PVOID*)&CheckVdmRequest->Desktop,
233 CheckVdmRequest->DesktopLen,
234 sizeof(*CheckVdmRequest->Desktop))
235 || !CsrValidateMessageBuffer(ApiMessage,
236 (PVOID*)&CheckVdmRequest->Title,
237 CheckVdmRequest->TitleLen,
238 sizeof(*CheckVdmRequest->Title))
239 || !CsrValidateMessageBuffer(ApiMessage,
240 (PVOID*)&CheckVdmRequest->Reserved,
241 CheckVdmRequest->ReservedLen,
242 sizeof(*CheckVdmRequest->Reserved)))
243 {
244 return STATUS_INVALID_PARAMETER;
245 }
246
247 CriticalSection = (CheckVdmRequest->BinaryType != BINARY_TYPE_SEPARATE_WOW)
248 ? &DosCriticalSection
249 : &WowCriticalSection;
250
251 /* Enter the critical section */
252 RtlEnterCriticalSection(CriticalSection);
253
254 /* Check if this is a DOS or WOW VDM */
255 if (CheckVdmRequest->BinaryType != BINARY_TYPE_SEPARATE_WOW)
256 {
257 /* Get the console record */
258 Status = BaseSrvGetConsoleRecord(CheckVdmRequest->ConsoleHandle,
259 &ConsoleRecord);
260
261 if (!NT_SUCCESS(Status))
262 {
263 /* Allocate a new console record */
264 ConsoleRecord = (PVDM_CONSOLE_RECORD)RtlAllocateHeap(BaseSrvHeap,
265 HEAP_ZERO_MEMORY,
266 sizeof(VDM_CONSOLE_RECORD));
267 if (ConsoleRecord == NULL)
268 {
269 Status = STATUS_NO_MEMORY;
270 goto Cleanup;
271 }
272
273 /* Initialize the console record */
274 ConsoleRecord->ConsoleHandle = CheckVdmRequest->ConsoleHandle;
275 ConsoleRecord->CurrentDirs = NULL;
276 ConsoleRecord->CurDirsLength = 0;
277 ConsoleRecord->SessionId = GetNextDosSesId();
278 InitializeListHead(&ConsoleRecord->DosListHead);
279 // TODO: The console record structure is incomplete
280
281 /* Remember that the console record was allocated here */
282 NewConsoleRecord = TRUE;
283 }
284
285 /* Allocate a new DOS record */
286 DosRecord = (PVDM_DOS_RECORD)RtlAllocateHeap(BaseSrvHeap,
287 HEAP_ZERO_MEMORY,
288 sizeof(VDM_DOS_RECORD));
289 if (DosRecord == NULL)
290 {
291 Status = STATUS_NO_MEMORY;
292 goto Cleanup;
293 }
294
295 /* Initialize the DOS record */
296 DosRecord->State = NewConsoleRecord ? VDM_NOT_LOADED : VDM_READY;
297 DosRecord->ExitCode = 0;
298 // TODO: The DOS record structure is incomplete
299
300 Status = BaseSrvCreatePairWaitHandles(&DosRecord->ServerEvent, &DosRecord->ClientEvent);
301 if (!NT_SUCCESS(Status)) goto Cleanup;
302
303 /* Add the DOS record */
304 InsertHeadList(&ConsoleRecord->DosListHead, &DosRecord->Entry);
305
306 if (NewConsoleRecord)
307 {
308 /* Add the console record */
309 InsertTailList(&VDMConsoleListHead, &ConsoleRecord->Entry);
310 }
311
312 CheckVdmRequest->VDMState = DosRecord->State;
313 Status = STATUS_SUCCESS;
314 }
315 else
316 {
317 // TODO: NOT IMPLEMENTED
318 UNIMPLEMENTED;
319 return STATUS_NOT_IMPLEMENTED;
320 }
321
322 Cleanup:
323 /* Check if it failed */
324 if (!NT_SUCCESS(Status))
325 {
326 /* Free the DOS record */
327 if (DosRecord != NULL)
328 {
329 RtlFreeHeap(BaseSrvHeap, 0, DosRecord);
330 DosRecord = NULL;
331 }
332
333 /* Free the console record if it was allocated here */
334 if (NewConsoleRecord)
335 {
336 RtlFreeHeap(BaseSrvHeap, 0, ConsoleRecord);
337 ConsoleRecord = NULL;
338 }
339 }
340
341 /* Leave the critical section */
342 RtlLeaveCriticalSection(CriticalSection);
343
344 return Status;
345 }
346
347 CSR_API(BaseSrvUpdateVDMEntry)
348 {
349 DPRINT1("%s not yet implemented\n", __FUNCTION__);
350 return STATUS_NOT_IMPLEMENTED;
351 }
352
353 CSR_API(BaseSrvGetNextVDMCommand)
354 {
355 DPRINT1("%s not yet implemented\n", __FUNCTION__);
356 return STATUS_NOT_IMPLEMENTED;
357 }
358
359 CSR_API(BaseSrvExitVDM)
360 {
361 NTSTATUS Status;
362 PBASE_EXIT_VDM ExitVdmRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.ExitVDMRequest;
363 PRTL_CRITICAL_SECTION CriticalSection = NULL;
364 PVDM_CONSOLE_RECORD ConsoleRecord = NULL;
365 PVDM_DOS_RECORD DosRecord;
366
367 CriticalSection = (ExitVdmRequest->iWowTask == 0)
368 ? &DosCriticalSection
369 : &WowCriticalSection;
370
371 /* Enter the critical section */
372 RtlEnterCriticalSection(CriticalSection);
373
374 if (ExitVdmRequest->iWowTask == 0)
375 {
376 /* Get the console record */
377 Status = BaseSrvGetConsoleRecord(ExitVdmRequest->ConsoleHandle, &ConsoleRecord);
378 if (!NT_SUCCESS(Status)) goto Cleanup;
379
380 /* Cleanup the DOS records */
381 while (ConsoleRecord->DosListHead.Flink != &ConsoleRecord->DosListHead)
382 {
383 DosRecord = CONTAINING_RECORD(ConsoleRecord->DosListHead.Flink,
384 VDM_DOS_RECORD,
385 Entry);
386
387 /* Remove the DOS entry */
388 RemoveEntryList(&DosRecord->Entry);
389 RtlFreeHeap(BaseSrvHeap, 0, DosRecord);
390 }
391
392 if (ConsoleRecord->CurrentDirs != NULL)
393 {
394 /* Free the current directories */
395 RtlFreeHeap(BaseSrvHeap, 0, ConsoleRecord->CurrentDirs);
396 ConsoleRecord->CurrentDirs = NULL;
397 ConsoleRecord->CurDirsLength = 0;
398 }
399
400 /* Remove the console record */
401 RemoveEntryList(&ConsoleRecord->Entry);
402 RtlFreeHeap(BaseSrvHeap, 0, ConsoleRecord);
403 }
404 else
405 {
406 // TODO: NOT IMPLEMENTED
407 UNIMPLEMENTED;
408 return STATUS_NOT_IMPLEMENTED;
409 }
410
411 Cleanup:
412 /* Leave the critical section */
413 RtlLeaveCriticalSection(CriticalSection);
414
415 return Status;
416 }
417
418 CSR_API(BaseSrvIsFirstVDM)
419 {
420 PBASE_IS_FIRST_VDM IsFirstVDMRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.IsFirstVDMRequest;
421
422 /* Return the result */
423 IsFirstVDMRequest->FirstVDM = FirstVDM;
424
425 /* Clear the first VDM flag */
426 FirstVDM = FALSE;
427
428 return STATUS_SUCCESS;
429 }
430
431 CSR_API(BaseSrvGetVDMExitCode)
432 {
433 NTSTATUS Status;
434 PBASE_GET_VDM_EXIT_CODE GetVDMExitCodeRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.GetVDMExitCodeRequest;
435 PLIST_ENTRY i = NULL;
436 PVDM_CONSOLE_RECORD ConsoleRecord = NULL;
437 PVDM_DOS_RECORD DosRecord = NULL;
438
439 /* Enter the critical section */
440 RtlEnterCriticalSection(&DosCriticalSection);
441
442 /* Get the console record */
443 Status = BaseSrvGetConsoleRecord(GetVDMExitCodeRequest->ConsoleHandle, &ConsoleRecord);
444 if (!NT_SUCCESS(Status)) goto Cleanup;
445
446 /* Search for a DOS record that has the same parent process handle */
447 for (i = ConsoleRecord->DosListHead.Flink; i != &ConsoleRecord->DosListHead; i = i->Flink)
448 {
449 DosRecord = CONTAINING_RECORD(i, VDM_DOS_RECORD, Entry);
450 if (DosRecord->ClientEvent == GetVDMExitCodeRequest->hParent) break;
451 }
452
453 /* Check if no DOS record was found */
454 if (i == &ConsoleRecord->DosListHead)
455 {
456 Status = STATUS_NOT_FOUND;
457 goto Cleanup;
458 }
459
460 /* Check if this task is still running */
461 if (DosRecord->State == VDM_READY)
462 {
463 GetVDMExitCodeRequest->ExitCode = STATUS_PENDING;
464 goto Cleanup;
465 }
466
467 /* Return the exit code */
468 GetVDMExitCodeRequest->ExitCode = DosRecord->ExitCode;
469
470 /* Since this is a zombie task record, remove it */
471 RemoveEntryList(&DosRecord->Entry);
472 RtlFreeHeap(BaseSrvHeap, 0, DosRecord);
473
474 Cleanup:
475 /* Leave the critical section */
476 RtlLeaveCriticalSection(&DosCriticalSection);
477
478 return Status;
479 }
480
481 CSR_API(BaseSrvSetReenterCount)
482 {
483 DPRINT1("%s not yet implemented\n", __FUNCTION__);
484 return STATUS_NOT_IMPLEMENTED;
485 }
486
487 CSR_API(BaseSrvSetVDMCurDirs)
488 {
489 NTSTATUS Status;
490 PBASE_GETSET_VDM_CURDIRS VDMCurrentDirsRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.VDMCurrentDirsRequest;
491 PVDM_CONSOLE_RECORD ConsoleRecord;
492 PCHAR Buffer = NULL;
493
494 /* Validate the input buffer */
495 if (!CsrValidateMessageBuffer(ApiMessage,
496 (PVOID*)&VDMCurrentDirsRequest->lpszzCurDirs,
497 VDMCurrentDirsRequest->cchCurDirs,
498 sizeof(*VDMCurrentDirsRequest->lpszzCurDirs)))
499 {
500 return STATUS_INVALID_PARAMETER;
501 }
502
503 /* Enter the critical section */
504 RtlEnterCriticalSection(&DosCriticalSection);
505
506 /* Find the console record */
507 Status = BaseSrvGetConsoleRecord(VDMCurrentDirsRequest->ConsoleHandle, &ConsoleRecord);
508 if (!NT_SUCCESS(Status)) goto Cleanup;
509
510 if (ConsoleRecord->CurrentDirs == NULL)
511 {
512 /* Allocate memory for the current directory information */
513 Buffer = RtlAllocateHeap(BaseSrvHeap,
514 HEAP_ZERO_MEMORY,
515 VDMCurrentDirsRequest->cchCurDirs);
516 }
517 else
518 {
519 /* Resize the amount of allocated memory */
520 Buffer = RtlReAllocateHeap(BaseSrvHeap,
521 HEAP_ZERO_MEMORY,
522 ConsoleRecord->CurrentDirs,
523 VDMCurrentDirsRequest->cchCurDirs);
524 }
525
526 if (Buffer == NULL)
527 {
528 /* Allocation failed */
529 Status = STATUS_NO_MEMORY;
530 goto Cleanup;
531 }
532
533 /* Update the console record */
534 ConsoleRecord->CurrentDirs = Buffer;
535 ConsoleRecord->CurDirsLength = VDMCurrentDirsRequest->cchCurDirs;
536
537 /* Copy the data */
538 RtlMoveMemory(ConsoleRecord->CurrentDirs,
539 VDMCurrentDirsRequest->lpszzCurDirs,
540 VDMCurrentDirsRequest->cchCurDirs);
541
542 Cleanup:
543 /* Leave the critical section */
544 RtlLeaveCriticalSection(&DosCriticalSection);
545
546 return Status;
547 }
548
549 CSR_API(BaseSrvGetVDMCurDirs)
550 {
551 NTSTATUS Status;
552 PBASE_GETSET_VDM_CURDIRS VDMCurrentDirsRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.VDMCurrentDirsRequest;
553 PVDM_CONSOLE_RECORD ConsoleRecord;
554
555 /* Validate the output buffer */
556 if (!CsrValidateMessageBuffer(ApiMessage,
557 (PVOID*)&VDMCurrentDirsRequest->lpszzCurDirs,
558 VDMCurrentDirsRequest->cchCurDirs,
559 sizeof(*VDMCurrentDirsRequest->lpszzCurDirs)))
560 {
561 return STATUS_INVALID_PARAMETER;
562 }
563
564 /* Enter the critical section */
565 RtlEnterCriticalSection(&DosCriticalSection);
566
567 /* Find the console record */
568 Status = BaseSrvGetConsoleRecord(VDMCurrentDirsRequest->ConsoleHandle, &ConsoleRecord);
569 if (!NT_SUCCESS(Status)) goto Cleanup;
570
571 /* Return the actual size of the current directory information */
572 VDMCurrentDirsRequest->cchCurDirs = ConsoleRecord->CurDirsLength;
573
574 /* Check if the buffer is large enough */
575 if (VDMCurrentDirsRequest->cchCurDirs < ConsoleRecord->CurDirsLength)
576 {
577 Status = STATUS_BUFFER_TOO_SMALL;
578 goto Cleanup;
579 }
580
581 /* Copy the data */
582 RtlMoveMemory(VDMCurrentDirsRequest->lpszzCurDirs,
583 ConsoleRecord->CurrentDirs,
584 ConsoleRecord->CurDirsLength);
585
586 Cleanup:
587 /* Leave the critical section */
588 RtlLeaveCriticalSection(&DosCriticalSection);
589
590 return Status;
591 }
592
593 CSR_API(BaseSrvBatNotification)
594 {
595 DPRINT1("%s not yet implemented\n", __FUNCTION__);
596 return STATUS_NOT_IMPLEMENTED;
597 }
598
599 CSR_API(BaseSrvRegisterWowExec)
600 {
601 DPRINT1("%s not yet implemented\n", __FUNCTION__);
602 return STATUS_NOT_IMPLEMENTED;
603 }
604
605 CSR_API(BaseSrvRefreshIniFileMapping)
606 {
607 DPRINT1("%s not yet implemented\n", __FUNCTION__);
608 return STATUS_NOT_IMPLEMENTED;
609 }
610
611 /* EOF */