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