a8991c31e4f7ccef9a85f52f9dc0ff19bbe79d77
[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 VOID NTAPI BaseInitializeVDM(VOID)
170 {
171 /* Initialize the list head */
172 InitializeListHead(&VDMConsoleListHead);
173
174 /* Initialize the critical section */
175 RtlInitializeCriticalSection(&DosCriticalSection);
176 RtlInitializeCriticalSection(&WowCriticalSection);
177 }
178
179 /* PUBLIC SERVER APIS *********************************************************/
180
181 CSR_API(BaseSrvCheckVDM)
182 {
183 NTSTATUS Status;
184 PBASE_CHECK_VDM CheckVdmRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.CheckVDMRequest;
185 PRTL_CRITICAL_SECTION CriticalSection = NULL;
186 PVDM_CONSOLE_RECORD ConsoleRecord = NULL;
187 PVDM_DOS_RECORD DosRecord = NULL;
188 BOOLEAN NewConsoleRecord = FALSE;
189
190 /* Don't do anything if the VDM has been disabled in the registry */
191 if (!BaseSrvIsVdmAllowed()) return STATUS_ACCESS_DENIED;
192
193 /* Validate the message buffers */
194 if (!CsrValidateMessageBuffer(ApiMessage,
195 (PVOID*)&CheckVdmRequest->CmdLine,
196 CheckVdmRequest->CmdLen,
197 sizeof(*CheckVdmRequest->CmdLine))
198 || !CsrValidateMessageBuffer(ApiMessage,
199 (PVOID*)&CheckVdmRequest->AppName,
200 CheckVdmRequest->AppLen,
201 sizeof(*CheckVdmRequest->AppName))
202 || !CsrValidateMessageBuffer(ApiMessage,
203 (PVOID*)&CheckVdmRequest->PifFile,
204 CheckVdmRequest->PifLen,
205 sizeof(*CheckVdmRequest->PifFile))
206 || !CsrValidateMessageBuffer(ApiMessage,
207 (PVOID*)&CheckVdmRequest->CurDirectory,
208 CheckVdmRequest->CurDirectoryLen,
209 sizeof(*CheckVdmRequest->CurDirectory))
210 || !CsrValidateMessageBuffer(ApiMessage,
211 (PVOID*)&CheckVdmRequest->Desktop,
212 CheckVdmRequest->DesktopLen,
213 sizeof(*CheckVdmRequest->Desktop))
214 || !CsrValidateMessageBuffer(ApiMessage,
215 (PVOID*)&CheckVdmRequest->Title,
216 CheckVdmRequest->TitleLen,
217 sizeof(*CheckVdmRequest->Title))
218 || !CsrValidateMessageBuffer(ApiMessage,
219 (PVOID*)&CheckVdmRequest->Reserved,
220 CheckVdmRequest->ReservedLen,
221 sizeof(*CheckVdmRequest->Reserved)))
222 {
223 return STATUS_INVALID_PARAMETER;
224 }
225
226 CriticalSection = (CheckVdmRequest->BinaryType != BINARY_TYPE_SEPARATE_WOW)
227 ? &DosCriticalSection
228 : &WowCriticalSection;
229
230 /* Enter the critical section */
231 RtlEnterCriticalSection(CriticalSection);
232
233 /* Check if this is a DOS or WOW VDM */
234 if (CheckVdmRequest->BinaryType != BINARY_TYPE_SEPARATE_WOW)
235 {
236 /* Get the console record */
237 Status = BaseSrvGetConsoleRecord(CheckVdmRequest->ConsoleHandle,
238 &ConsoleRecord);
239
240 if (!NT_SUCCESS(Status))
241 {
242 /* Allocate a new console record */
243 ConsoleRecord = (PVDM_CONSOLE_RECORD)RtlAllocateHeap(BaseSrvHeap,
244 HEAP_ZERO_MEMORY,
245 sizeof(VDM_CONSOLE_RECORD));
246 if (ConsoleRecord == NULL)
247 {
248 Status = STATUS_NO_MEMORY;
249 goto Cleanup;
250 }
251
252 /* Initialize the console record */
253 ConsoleRecord->ConsoleHandle = CheckVdmRequest->ConsoleHandle;
254 ConsoleRecord->CurrentDirs = NULL;
255 ConsoleRecord->CurDirsLength = 0;
256 ConsoleRecord->SessionId = GetNextDosSesId();
257 InitializeListHead(&ConsoleRecord->DosListHead);
258 // TODO: The console record structure is incomplete
259
260 /* Remember that the console record was allocated here */
261 NewConsoleRecord = TRUE;
262 }
263
264 /* Allocate a new DOS record */
265 DosRecord = (PVDM_DOS_RECORD)RtlAllocateHeap(BaseSrvHeap,
266 HEAP_ZERO_MEMORY,
267 sizeof(VDM_DOS_RECORD));
268 if (DosRecord == NULL)
269 {
270 Status = STATUS_NO_MEMORY;
271 goto Cleanup;
272 }
273
274 /* Initialize the DOS record */
275 DosRecord->State = NewConsoleRecord ? VDM_NOT_LOADED : VDM_READY;
276 DosRecord->ExitCode = 0;
277 // TODO: The DOS record structure is incomplete
278
279 /* Add the DOS record */
280 InsertHeadList(&ConsoleRecord->DosListHead, &DosRecord->Entry);
281
282 if (NewConsoleRecord)
283 {
284 /* Add the console record */
285 InsertTailList(&VDMConsoleListHead, &ConsoleRecord->Entry);
286 }
287
288 CheckVdmRequest->VDMState = DosRecord->State;
289 Status = STATUS_SUCCESS;
290 }
291 else
292 {
293 // TODO: NOT IMPLEMENTED
294 UNIMPLEMENTED;
295 return STATUS_NOT_IMPLEMENTED;
296 }
297
298 Cleanup:
299 /* Check if it failed */
300 if (!NT_SUCCESS(Status))
301 {
302 /* Free the DOS record */
303 if (DosRecord != NULL)
304 {
305 RtlFreeHeap(BaseSrvHeap, 0, DosRecord);
306 DosRecord = NULL;
307 }
308
309 /* Free the console record if it was allocated here */
310 if (NewConsoleRecord)
311 {
312 RtlFreeHeap(BaseSrvHeap, 0, ConsoleRecord);
313 ConsoleRecord = NULL;
314 }
315 }
316
317 /* Leave the critical section */
318 RtlLeaveCriticalSection(CriticalSection);
319
320 return Status;
321 }
322
323 CSR_API(BaseSrvUpdateVDMEntry)
324 {
325 DPRINT1("%s not yet implemented\n", __FUNCTION__);
326 return STATUS_NOT_IMPLEMENTED;
327 }
328
329 CSR_API(BaseSrvGetNextVDMCommand)
330 {
331 DPRINT1("%s not yet implemented\n", __FUNCTION__);
332 return STATUS_NOT_IMPLEMENTED;
333 }
334
335 CSR_API(BaseSrvExitVDM)
336 {
337 NTSTATUS Status;
338 PBASE_EXIT_VDM ExitVdmRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.ExitVDMRequest;
339 PRTL_CRITICAL_SECTION CriticalSection = NULL;
340 PVDM_CONSOLE_RECORD ConsoleRecord = NULL;
341 PVDM_DOS_RECORD DosRecord;
342
343 CriticalSection = (ExitVdmRequest->iWowTask == 0)
344 ? &DosCriticalSection
345 : &WowCriticalSection;
346
347 /* Enter the critical section */
348 RtlEnterCriticalSection(CriticalSection);
349
350 if (ExitVdmRequest->iWowTask == 0)
351 {
352 /* Get the console record */
353 Status = BaseSrvGetConsoleRecord(ExitVdmRequest->ConsoleHandle, &ConsoleRecord);
354 if (!NT_SUCCESS(Status)) goto Cleanup;
355
356 /* Cleanup the DOS records */
357 while (ConsoleRecord->DosListHead.Flink != &ConsoleRecord->DosListHead)
358 {
359 DosRecord = CONTAINING_RECORD(ConsoleRecord->DosListHead.Flink,
360 VDM_DOS_RECORD,
361 Entry);
362
363 /* Remove the DOS entry */
364 RemoveEntryList(&DosRecord->Entry);
365 RtlFreeHeap(BaseSrvHeap, 0, DosRecord);
366 }
367
368 if (ConsoleRecord->CurrentDirs != NULL)
369 {
370 /* Free the current directories */
371 RtlFreeHeap(BaseSrvHeap, 0, ConsoleRecord->CurrentDirs);
372 ConsoleRecord->CurrentDirs = NULL;
373 ConsoleRecord->CurDirsLength = 0;
374 }
375
376 /* Remove the console record */
377 RemoveEntryList(&ConsoleRecord->Entry);
378 RtlFreeHeap(BaseSrvHeap, 0, ConsoleRecord);
379 }
380 else
381 {
382 // TODO: NOT IMPLEMENTED
383 UNIMPLEMENTED;
384 return STATUS_NOT_IMPLEMENTED;
385 }
386
387 Cleanup:
388 /* Leave the critical section */
389 RtlLeaveCriticalSection(CriticalSection);
390
391 return Status;
392 }
393
394 CSR_API(BaseSrvIsFirstVDM)
395 {
396 PBASE_IS_FIRST_VDM IsFirstVDMRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.IsFirstVDMRequest;
397
398 /* Return the result */
399 IsFirstVDMRequest->FirstVDM = FirstVDM;
400
401 /* Clear the first VDM flag */
402 FirstVDM = FALSE;
403
404 return STATUS_SUCCESS;
405 }
406
407 CSR_API(BaseSrvGetVDMExitCode)
408 {
409 NTSTATUS Status;
410 PBASE_GET_VDM_EXIT_CODE GetVDMExitCodeRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.GetVDMExitCodeRequest;
411 PLIST_ENTRY i = NULL;
412 PVDM_CONSOLE_RECORD ConsoleRecord = NULL;
413 PVDM_DOS_RECORD DosRecord = NULL;
414
415 /* Enter the critical section */
416 RtlEnterCriticalSection(&DosCriticalSection);
417
418 /* Get the console record */
419 Status = BaseSrvGetConsoleRecord(GetVDMExitCodeRequest->ConsoleHandle, &ConsoleRecord);
420 if (!NT_SUCCESS(Status)) goto Cleanup;
421
422 /* Search for a DOS record that has the same parent process handle */
423 for (i = ConsoleRecord->DosListHead.Flink; i != &ConsoleRecord->DosListHead; i = i->Flink)
424 {
425 DosRecord = CONTAINING_RECORD(i, VDM_DOS_RECORD, Entry);
426 if (DosRecord->ParentProcess == GetVDMExitCodeRequest->hParent) break;
427 }
428
429 /* Check if no DOS record was found */
430 if (i == &ConsoleRecord->DosListHead)
431 {
432 Status = STATUS_NOT_FOUND;
433 goto Cleanup;
434 }
435
436 /* Check if this task is still running */
437 if (DosRecord->State == VDM_READY)
438 {
439 GetVDMExitCodeRequest->ExitCode = STATUS_PENDING;
440 goto Cleanup;
441 }
442
443 /* Return the exit code */
444 GetVDMExitCodeRequest->ExitCode = DosRecord->ExitCode;
445
446 /* Since this is a zombie task record, remove it */
447 RemoveEntryList(&DosRecord->Entry);
448 RtlFreeHeap(BaseSrvHeap, 0, DosRecord);
449
450 Cleanup:
451 /* Leave the critical section */
452 RtlLeaveCriticalSection(&DosCriticalSection);
453
454 return Status;
455 }
456
457 CSR_API(BaseSrvSetReenterCount)
458 {
459 DPRINT1("%s not yet implemented\n", __FUNCTION__);
460 return STATUS_NOT_IMPLEMENTED;
461 }
462
463 CSR_API(BaseSrvSetVDMCurDirs)
464 {
465 NTSTATUS Status;
466 PBASE_GETSET_VDM_CURDIRS VDMCurrentDirsRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.VDMCurrentDirsRequest;
467 PVDM_CONSOLE_RECORD ConsoleRecord;
468 PCHAR Buffer = NULL;
469
470 /* Validate the input buffer */
471 if (!CsrValidateMessageBuffer(ApiMessage,
472 (PVOID*)&VDMCurrentDirsRequest->lpszzCurDirs,
473 VDMCurrentDirsRequest->cchCurDirs,
474 sizeof(*VDMCurrentDirsRequest->lpszzCurDirs)))
475 {
476 return STATUS_INVALID_PARAMETER;
477 }
478
479 /* Enter the critical section */
480 RtlEnterCriticalSection(&DosCriticalSection);
481
482 /* Find the console record */
483 Status = BaseSrvGetConsoleRecord(VDMCurrentDirsRequest->ConsoleHandle, &ConsoleRecord);
484 if (!NT_SUCCESS(Status)) goto Cleanup;
485
486 if (ConsoleRecord->CurrentDirs == NULL)
487 {
488 /* Allocate memory for the current directory information */
489 Buffer = RtlAllocateHeap(BaseSrvHeap,
490 HEAP_ZERO_MEMORY,
491 VDMCurrentDirsRequest->cchCurDirs);
492 }
493 else
494 {
495 /* Resize the amount of allocated memory */
496 Buffer = RtlReAllocateHeap(BaseSrvHeap,
497 HEAP_ZERO_MEMORY,
498 ConsoleRecord->CurrentDirs,
499 VDMCurrentDirsRequest->cchCurDirs);
500 }
501
502 if (Buffer == NULL)
503 {
504 /* Allocation failed */
505 Status = STATUS_NO_MEMORY;
506 goto Cleanup;
507 }
508
509 /* Update the console record */
510 ConsoleRecord->CurrentDirs = Buffer;
511 ConsoleRecord->CurDirsLength = VDMCurrentDirsRequest->cchCurDirs;
512
513 /* Copy the data */
514 RtlMoveMemory(ConsoleRecord->CurrentDirs,
515 VDMCurrentDirsRequest->lpszzCurDirs,
516 VDMCurrentDirsRequest->cchCurDirs);
517
518 Cleanup:
519 /* Leave the critical section */
520 RtlLeaveCriticalSection(&DosCriticalSection);
521
522 return Status;
523 }
524
525 CSR_API(BaseSrvGetVDMCurDirs)
526 {
527 NTSTATUS Status;
528 PBASE_GETSET_VDM_CURDIRS VDMCurrentDirsRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.VDMCurrentDirsRequest;
529 PVDM_CONSOLE_RECORD ConsoleRecord;
530
531 /* Validate the output buffer */
532 if (!CsrValidateMessageBuffer(ApiMessage,
533 (PVOID*)&VDMCurrentDirsRequest->lpszzCurDirs,
534 VDMCurrentDirsRequest->cchCurDirs,
535 sizeof(*VDMCurrentDirsRequest->lpszzCurDirs)))
536 {
537 return STATUS_INVALID_PARAMETER;
538 }
539
540 /* Enter the critical section */
541 RtlEnterCriticalSection(&DosCriticalSection);
542
543 /* Find the console record */
544 Status = BaseSrvGetConsoleRecord(VDMCurrentDirsRequest->ConsoleHandle, &ConsoleRecord);
545 if (!NT_SUCCESS(Status)) goto Cleanup;
546
547 /* Return the actual size of the current directory information */
548 VDMCurrentDirsRequest->cchCurDirs = ConsoleRecord->CurDirsLength;
549
550 /* Check if the buffer is large enough */
551 if (VDMCurrentDirsRequest->cchCurDirs < ConsoleRecord->CurDirsLength)
552 {
553 Status = STATUS_BUFFER_TOO_SMALL;
554 goto Cleanup;
555 }
556
557 /* Copy the data */
558 RtlMoveMemory(VDMCurrentDirsRequest->lpszzCurDirs,
559 ConsoleRecord->CurrentDirs,
560 ConsoleRecord->CurDirsLength);
561
562 Cleanup:
563 /* Leave the critical section */
564 RtlLeaveCriticalSection(&DosCriticalSection);
565
566 return Status;
567 }
568
569 CSR_API(BaseSrvBatNotification)
570 {
571 DPRINT1("%s not yet implemented\n", __FUNCTION__);
572 return STATUS_NOT_IMPLEMENTED;
573 }
574
575 CSR_API(BaseSrvRegisterWowExec)
576 {
577 DPRINT1("%s not yet implemented\n", __FUNCTION__);
578 return STATUS_NOT_IMPLEMENTED;
579 }
580
581 CSR_API(BaseSrvRefreshIniFileMapping)
582 {
583 DPRINT1("%s not yet implemented\n", __FUNCTION__);
584 return STATUS_NOT_IMPLEMENTED;
585 }
586
587 /* EOF */