1c2c66affSColin Finck /*
2c2c66affSColin Finck * COPYRIGHT: See COPYING in the top level directory
3c2c66affSColin Finck * PROJECT: ReactOS Base API Server DLL
4c2c66affSColin Finck * FILE: subsystems/win/basesrv/vdm.c
5c2c66affSColin Finck * PURPOSE: Virtual DOS Machines (VDM) Support
6c2c66affSColin Finck * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr)
7c2c66affSColin Finck * Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
8c2c66affSColin Finck */
9c2c66affSColin Finck
10c2c66affSColin Finck /* INCLUDES *******************************************************************/
11c2c66affSColin Finck
12c2c66affSColin Finck #include "basesrv.h"
13c2c66affSColin Finck #include "vdm.h"
14c2c66affSColin Finck
15c2c66affSColin Finck #define NDEBUG
16c2c66affSColin Finck #include <debug.h>
17c2c66affSColin Finck
18c2c66affSColin Finck /* GLOBALS ********************************************************************/
19c2c66affSColin Finck
20c2c66affSColin Finck BOOLEAN FirstVDM = TRUE;
21c2c66affSColin Finck LIST_ENTRY VDMConsoleListHead;
22c2c66affSColin Finck RTL_CRITICAL_SECTION DosCriticalSection;
23c2c66affSColin Finck RTL_CRITICAL_SECTION WowCriticalSection;
24c2c66affSColin Finck
25c2c66affSColin Finck /* HELPER FUNCTIONS ***********************************************************/
26c2c66affSColin Finck
BaseSrvCreateConsoleRecord(VOID)27c2c66affSColin Finck PVDM_CONSOLE_RECORD BaseSrvCreateConsoleRecord(VOID)
28c2c66affSColin Finck {
29c2c66affSColin Finck PVDM_CONSOLE_RECORD ConsoleRecord;
30c2c66affSColin Finck
31c2c66affSColin Finck ConsoleRecord = RtlAllocateHeap(BaseSrvHeap, HEAP_ZERO_MEMORY,
32c2c66affSColin Finck sizeof(VDM_CONSOLE_RECORD));
33c2c66affSColin Finck if (ConsoleRecord == NULL)
34c2c66affSColin Finck return NULL;
35c2c66affSColin Finck
36c2c66affSColin Finck /* Initialize the console record */
37c2c66affSColin Finck ConsoleRecord->ConsoleHandle = NULL;
38c2c66affSColin Finck ConsoleRecord->ProcessHandle = NULL;
39c2c66affSColin Finck ConsoleRecord->ServerEvent = ConsoleRecord->ClientEvent = NULL;
40c2c66affSColin Finck ConsoleRecord->ReenterCount = 0;
41c2c66affSColin Finck ConsoleRecord->CurrentDirs = NULL;
42c2c66affSColin Finck ConsoleRecord->CurDirsLength = 0;
43c2c66affSColin Finck ConsoleRecord->SessionId = 0;
44c2c66affSColin Finck InitializeListHead(&ConsoleRecord->DosListHead);
45c2c66affSColin Finck
46c2c66affSColin Finck return ConsoleRecord;
47c2c66affSColin Finck }
48c2c66affSColin Finck
BaseSrvGetConsoleRecord(HANDLE ConsoleHandle,PVDM_CONSOLE_RECORD * Record)49c2c66affSColin Finck NTSTATUS BaseSrvGetConsoleRecord(HANDLE ConsoleHandle, PVDM_CONSOLE_RECORD *Record)
50c2c66affSColin Finck {
51c2c66affSColin Finck PLIST_ENTRY i;
52c2c66affSColin Finck PVDM_CONSOLE_RECORD CurrentRecord = NULL;
53c2c66affSColin Finck
54c2c66affSColin Finck /* NULL is not a valid console handle */
55c2c66affSColin Finck if (ConsoleHandle == NULL) return STATUS_INVALID_PARAMETER;
56c2c66affSColin Finck
57c2c66affSColin Finck /* Search for a record that has the same console handle */
58c2c66affSColin Finck for (i = VDMConsoleListHead.Flink; i != &VDMConsoleListHead; i = i->Flink)
59c2c66affSColin Finck {
60c2c66affSColin Finck CurrentRecord = CONTAINING_RECORD(i, VDM_CONSOLE_RECORD, Entry);
61c2c66affSColin Finck if (CurrentRecord->ConsoleHandle == ConsoleHandle) break;
62c2c66affSColin Finck }
63c2c66affSColin Finck
64c2c66affSColin Finck /* Check if nothing was found */
65c2c66affSColin Finck if (i == &VDMConsoleListHead) CurrentRecord = NULL;
66c2c66affSColin Finck
67c2c66affSColin Finck *Record = CurrentRecord;
68c2c66affSColin Finck return CurrentRecord ? STATUS_SUCCESS : STATUS_NOT_FOUND;
69c2c66affSColin Finck }
70c2c66affSColin Finck
BaseSrvDestroyConsoleRecord(PVDM_CONSOLE_RECORD ConsoleRecord)71c2c66affSColin Finck VOID BaseSrvDestroyConsoleRecord(PVDM_CONSOLE_RECORD ConsoleRecord)
72c2c66affSColin Finck {
73c2c66affSColin Finck if (ConsoleRecord->CurrentDirs != NULL)
74c2c66affSColin Finck {
75c2c66affSColin Finck /* Free the current directories */
76c2c66affSColin Finck RtlFreeHeap(BaseSrvHeap, 0, ConsoleRecord->CurrentDirs);
77c2c66affSColin Finck ConsoleRecord->CurrentDirs = NULL;
78c2c66affSColin Finck ConsoleRecord->CurDirsLength = 0;
79c2c66affSColin Finck }
80c2c66affSColin Finck
81c2c66affSColin Finck /* Close the process handle */
82c2c66affSColin Finck if (ConsoleRecord->ProcessHandle)
83c2c66affSColin Finck NtClose(ConsoleRecord->ProcessHandle);
84c2c66affSColin Finck
85c2c66affSColin Finck /* Close the event handle */
86c2c66affSColin Finck if (ConsoleRecord->ServerEvent)
87c2c66affSColin Finck NtClose(ConsoleRecord->ServerEvent);
88c2c66affSColin Finck
89c2c66affSColin Finck /* Remove the console record */
90c2c66affSColin Finck // RemoveEntryList(&ConsoleRecord->Entry);
91c2c66affSColin Finck RtlFreeHeap(BaseSrvHeap, 0, ConsoleRecord);
92c2c66affSColin Finck }
93c2c66affSColin Finck
GetConsoleRecordBySessionId(ULONG TaskId,PVDM_CONSOLE_RECORD * Record)94c2c66affSColin Finck NTSTATUS GetConsoleRecordBySessionId(ULONG TaskId, PVDM_CONSOLE_RECORD *Record)
95c2c66affSColin Finck {
96c2c66affSColin Finck PLIST_ENTRY i;
97c2c66affSColin Finck PVDM_CONSOLE_RECORD CurrentRecord = NULL;
98c2c66affSColin Finck
99c2c66affSColin Finck /* Search for a record that has the same console handle */
100c2c66affSColin Finck for (i = VDMConsoleListHead.Flink; i != &VDMConsoleListHead; i = i->Flink)
101c2c66affSColin Finck {
102c2c66affSColin Finck CurrentRecord = CONTAINING_RECORD(i, VDM_CONSOLE_RECORD, Entry);
103c2c66affSColin Finck if (CurrentRecord->SessionId == TaskId) break;
104c2c66affSColin Finck }
105c2c66affSColin Finck
106c2c66affSColin Finck /* Check if nothing was found */
107c2c66affSColin Finck if (i == &VDMConsoleListHead) CurrentRecord = NULL;
108c2c66affSColin Finck
109c2c66affSColin Finck *Record = CurrentRecord;
110c2c66affSColin Finck return CurrentRecord ? STATUS_SUCCESS : STATUS_NOT_FOUND;
111c2c66affSColin Finck }
112c2c66affSColin Finck
GetNextDosSesId(VOID)113c2c66affSColin Finck ULONG GetNextDosSesId(VOID)
114c2c66affSColin Finck {
115c2c66affSColin Finck ULONG SessionId;
116c2c66affSColin Finck PLIST_ENTRY i;
117c2c66affSColin Finck PVDM_CONSOLE_RECORD CurrentRecord = NULL;
118c2c66affSColin Finck BOOLEAN Found;
119c2c66affSColin Finck
120c2c66affSColin Finck /* Search for an available session ID */
121c2c66affSColin Finck for (SessionId = 1; SessionId != 0; SessionId++)
122c2c66affSColin Finck {
123c2c66affSColin Finck Found = FALSE;
124c2c66affSColin Finck
125c2c66affSColin Finck /* Check if the ID is already in use */
126c2c66affSColin Finck for (i = VDMConsoleListHead.Flink; i != &VDMConsoleListHead; i = i->Flink)
127c2c66affSColin Finck {
128c2c66affSColin Finck CurrentRecord = CONTAINING_RECORD(i, VDM_CONSOLE_RECORD, Entry);
129c2c66affSColin Finck if (CurrentRecord->SessionId == SessionId) Found = TRUE;
130c2c66affSColin Finck }
131c2c66affSColin Finck
132c2c66affSColin Finck /* If not, we found one */
133c2c66affSColin Finck if (!Found) break;
134c2c66affSColin Finck }
135c2c66affSColin Finck
136c2c66affSColin Finck ASSERT(SessionId != 0);
137c2c66affSColin Finck
138c2c66affSColin Finck /* Return the session ID */
139c2c66affSColin Finck return SessionId;
140c2c66affSColin Finck }
141c2c66affSColin Finck
BaseSrvIsVdmAllowed(VOID)142c2c66affSColin Finck BOOLEAN BaseSrvIsVdmAllowed(VOID)
143c2c66affSColin Finck {
144c2c66affSColin Finck NTSTATUS Status;
145c2c66affSColin Finck BOOLEAN VdmAllowed = TRUE;
146c2c66affSColin Finck HANDLE RootKey, KeyHandle;
147c2c66affSColin Finck UNICODE_STRING KeyName, ValueName, MachineKeyName;
148c2c66affSColin Finck OBJECT_ATTRIBUTES Attributes;
149c2c66affSColin Finck UCHAR ValueBuffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(ULONG)];
150c2c66affSColin Finck PKEY_VALUE_PARTIAL_INFORMATION ValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)ValueBuffer;
151c2c66affSColin Finck ULONG ActualSize;
152c2c66affSColin Finck
153c2c66affSColin Finck /* Initialize the unicode strings */
154c2c66affSColin Finck RtlInitUnicodeString(&MachineKeyName, L"\\Registry\\Machine");
155c2c66affSColin Finck RtlInitUnicodeString(&KeyName, VDM_POLICY_KEY_NAME);
156c2c66affSColin Finck RtlInitUnicodeString(&ValueName, VDM_DISALLOWED_VALUE_NAME);
157c2c66affSColin Finck
158c2c66affSColin Finck InitializeObjectAttributes(&Attributes,
159c2c66affSColin Finck &MachineKeyName,
160c2c66affSColin Finck OBJ_CASE_INSENSITIVE,
161c2c66affSColin Finck NULL,
162c2c66affSColin Finck NULL);
163c2c66affSColin Finck
164c2c66affSColin Finck /* Open the local machine key */
165c2c66affSColin Finck Status = NtOpenKey(&RootKey, KEY_READ, &Attributes);
166c2c66affSColin Finck if (!NT_SUCCESS(Status)) return FALSE;
167c2c66affSColin Finck
168c2c66affSColin Finck InitializeObjectAttributes(&Attributes,
169c2c66affSColin Finck &KeyName,
170c2c66affSColin Finck OBJ_CASE_INSENSITIVE,
171c2c66affSColin Finck RootKey,
172c2c66affSColin Finck NULL);
173c2c66affSColin Finck
174c2c66affSColin Finck /* Open the policy key in the local machine hive, if it exists */
175c2c66affSColin Finck if (NT_SUCCESS(NtOpenKey(&KeyHandle, KEY_READ, &Attributes)))
176c2c66affSColin Finck {
177c2c66affSColin Finck /* Read the value, if it's set */
178c2c66affSColin Finck if (NT_SUCCESS(NtQueryValueKey(KeyHandle,
179c2c66affSColin Finck &ValueName,
180c2c66affSColin Finck KeyValuePartialInformation,
181c2c66affSColin Finck ValueInfo,
182c2c66affSColin Finck sizeof(ValueBuffer),
183c2c66affSColin Finck &ActualSize)))
184c2c66affSColin Finck {
185c2c66affSColin Finck if (*((PULONG)ValueInfo->Data))
186c2c66affSColin Finck {
187c2c66affSColin Finck /* The VDM has been disabled in the registry */
188c2c66affSColin Finck VdmAllowed = FALSE;
189c2c66affSColin Finck }
190c2c66affSColin Finck }
191c2c66affSColin Finck
192c2c66affSColin Finck NtClose(KeyHandle);
193c2c66affSColin Finck }
194c2c66affSColin Finck
195c2c66affSColin Finck /* Close the local machine key */
196c2c66affSColin Finck NtClose(RootKey);
197c2c66affSColin Finck
198c2c66affSColin Finck /* If it's disabled system-wide, there's no need to check the user key */
199c2c66affSColin Finck if (!VdmAllowed) return FALSE;
200c2c66affSColin Finck
201c2c66affSColin Finck /* Open the current user key of the client */
202c2c66affSColin Finck if (!CsrImpersonateClient(NULL)) return VdmAllowed;
203c2c66affSColin Finck Status = RtlOpenCurrentUser(KEY_READ, &RootKey);
204c2c66affSColin Finck CsrRevertToSelf();
205c2c66affSColin Finck
206c2c66affSColin Finck /* If that fails, return the system-wide setting */
207c2c66affSColin Finck if (!NT_SUCCESS(Status)) return VdmAllowed;
208c2c66affSColin Finck
209c2c66affSColin Finck InitializeObjectAttributes(&Attributes,
210c2c66affSColin Finck &KeyName,
211c2c66affSColin Finck OBJ_CASE_INSENSITIVE,
212c2c66affSColin Finck RootKey,
213c2c66affSColin Finck NULL);
214c2c66affSColin Finck
215c2c66affSColin Finck /* Open the policy key in the current user hive, if it exists */
216c2c66affSColin Finck if (NT_SUCCESS(NtOpenKey(&KeyHandle, KEY_READ, &Attributes)))
217c2c66affSColin Finck {
218c2c66affSColin Finck /* Read the value, if it's set */
219c2c66affSColin Finck if (NT_SUCCESS(NtQueryValueKey(KeyHandle,
220c2c66affSColin Finck &ValueName,
221c2c66affSColin Finck KeyValuePartialInformation,
222c2c66affSColin Finck ValueInfo,
223c2c66affSColin Finck sizeof(ValueBuffer),
224c2c66affSColin Finck &ActualSize)))
225c2c66affSColin Finck {
226c2c66affSColin Finck if (*((PULONG)ValueInfo->Data))
227c2c66affSColin Finck {
228c2c66affSColin Finck /* The VDM has been disabled in the registry */
229c2c66affSColin Finck VdmAllowed = FALSE;
230c2c66affSColin Finck }
231c2c66affSColin Finck }
232c2c66affSColin Finck
233c2c66affSColin Finck NtClose(KeyHandle);
234c2c66affSColin Finck }
235c2c66affSColin Finck
236c2c66affSColin Finck return VdmAllowed;
237c2c66affSColin Finck }
238c2c66affSColin Finck
BaseSrvCreatePairWaitHandles(PHANDLE ServerEvent,PHANDLE ClientEvent)239c2c66affSColin Finck NTSTATUS BaseSrvCreatePairWaitHandles(PHANDLE ServerEvent, PHANDLE ClientEvent)
240c2c66affSColin Finck {
241c2c66affSColin Finck NTSTATUS Status;
242c2c66affSColin Finck
243c2c66affSColin Finck /* Create the event */
244c2c66affSColin Finck Status = NtCreateEvent(ServerEvent, EVENT_ALL_ACCESS, NULL, NotificationEvent, FALSE);
245c2c66affSColin Finck if (!NT_SUCCESS(Status)) return Status;
246c2c66affSColin Finck
247c2c66affSColin Finck /* Duplicate the event into the client process */
248c2c66affSColin Finck Status = NtDuplicateObject(NtCurrentProcess(),
249c2c66affSColin Finck *ServerEvent,
250c2c66affSColin Finck CsrGetClientThread()->Process->ProcessHandle,
251c2c66affSColin Finck ClientEvent,
252c2c66affSColin Finck 0,
253c2c66affSColin Finck 0,
254c2c66affSColin Finck DUPLICATE_SAME_ATTRIBUTES | DUPLICATE_SAME_ACCESS);
255c2c66affSColin Finck
256c2c66affSColin Finck if (!NT_SUCCESS(Status)) NtClose(*ServerEvent);
257c2c66affSColin Finck return Status;
258c2c66affSColin Finck }
259c2c66affSColin Finck
BaseSrvDestroyPairWaitHandles(HANDLE ServerEvent,HANDLE ClientEvent)260c2c66affSColin Finck VOID BaseSrvDestroyPairWaitHandles(HANDLE ServerEvent, HANDLE ClientEvent)
261c2c66affSColin Finck {
262c2c66affSColin Finck if (ServerEvent) NtClose(ServerEvent);
263c2c66affSColin Finck if (ClientEvent)
264c2c66affSColin Finck {
265c2c66affSColin Finck /* Close the remote handle */
266c2c66affSColin Finck NtDuplicateObject(CsrGetClientThread()->Process->ProcessHandle,
267c2c66affSColin Finck ClientEvent,
268c2c66affSColin Finck NULL,
269c2c66affSColin Finck NULL,
270c2c66affSColin Finck 0,
271c2c66affSColin Finck 0,
272c2c66affSColin Finck DUPLICATE_CLOSE_SOURCE);
273c2c66affSColin Finck }
274c2c66affSColin Finck }
275c2c66affSColin Finck
276c2c66affSColin Finck /* WOW SUPPORT FUNCTIONS ******************************************************/
277c2c66affSColin Finck
278c2c66affSColin Finck /* DOS SUPPORT FUNCTIONS ******************************************************/
279c2c66affSColin Finck
BaseSrvFreeVDMInfo(PVDM_COMMAND_INFO CommandInfo)280c2c66affSColin Finck VOID BaseSrvFreeVDMInfo(PVDM_COMMAND_INFO CommandInfo)
281c2c66affSColin Finck {
282c2c66affSColin Finck /* Free the allocated structure members */
283c2c66affSColin Finck if (CommandInfo->CmdLine != NULL) RtlFreeHeap(BaseSrvHeap, 0, CommandInfo->CmdLine);
284c2c66affSColin Finck if (CommandInfo->AppName != NULL) RtlFreeHeap(BaseSrvHeap, 0, CommandInfo->AppName);
285c2c66affSColin Finck if (CommandInfo->PifFile != NULL) RtlFreeHeap(BaseSrvHeap, 0, CommandInfo->PifFile);
286c2c66affSColin Finck if (CommandInfo->CurDirectory != NULL) RtlFreeHeap(BaseSrvHeap, 0, CommandInfo->CurDirectory);
287c2c66affSColin Finck if (CommandInfo->Env != NULL) RtlFreeHeap(BaseSrvHeap, 0, CommandInfo->Env);
288c2c66affSColin Finck if (CommandInfo->Desktop != NULL) RtlFreeHeap(BaseSrvHeap, 0, CommandInfo->Desktop);
289c2c66affSColin Finck if (CommandInfo->Title != NULL) RtlFreeHeap(BaseSrvHeap, 0, CommandInfo->Title);
290c2c66affSColin Finck if (CommandInfo->Reserved != NULL) RtlFreeHeap(BaseSrvHeap, 0, CommandInfo->Reserved);
291c2c66affSColin Finck
292c2c66affSColin Finck /* Free the structure itself */
293c2c66affSColin Finck RtlFreeHeap(BaseSrvHeap, 0, CommandInfo);
294c2c66affSColin Finck }
295c2c66affSColin Finck
BaseSrvCleanupVDMResources(IN PCSR_PROCESS CsrProcess)296c2c66affSColin Finck VOID BaseSrvCleanupVDMResources(IN PCSR_PROCESS CsrProcess)
297c2c66affSColin Finck {
298c2c66affSColin Finck ULONG ProcessId = HandleToUlong(CsrProcess->ClientId.UniqueProcess);
299c2c66affSColin Finck PVDM_CONSOLE_RECORD ConsoleRecord = NULL;
300c2c66affSColin Finck PVDM_DOS_RECORD DosRecord;
301c2c66affSColin Finck PLIST_ENTRY i;
302c2c66affSColin Finck
303c2c66affSColin Finck /* Enter the critical section */
304c2c66affSColin Finck RtlEnterCriticalSection(&DosCriticalSection);
305c2c66affSColin Finck
306c2c66affSColin Finck /* Search for a record that has the same process handle */
307c2c66affSColin Finck i = VDMConsoleListHead.Flink;
308c2c66affSColin Finck while (i != &VDMConsoleListHead)
309c2c66affSColin Finck {
310c2c66affSColin Finck ConsoleRecord = CONTAINING_RECORD(i, VDM_CONSOLE_RECORD, Entry);
311c2c66affSColin Finck i = i->Flink;
312c2c66affSColin Finck
313c2c66affSColin Finck if (ConsoleRecord->ProcessId == ProcessId)
314c2c66affSColin Finck {
315c2c66affSColin Finck if (ConsoleRecord->ServerEvent)
316c2c66affSColin Finck {
317c2c66affSColin Finck NtClose(ConsoleRecord->ServerEvent);
318c2c66affSColin Finck ConsoleRecord->ServerEvent = NULL;
319c2c66affSColin Finck }
320c2c66affSColin Finck
321c2c66affSColin Finck /* Cleanup the DOS records */
322c2c66affSColin Finck while (!IsListEmpty(&ConsoleRecord->DosListHead))
323c2c66affSColin Finck {
324c2c66affSColin Finck DosRecord = CONTAINING_RECORD(ConsoleRecord->DosListHead.Flink,
325c2c66affSColin Finck VDM_DOS_RECORD, Entry);
326c2c66affSColin Finck
327c2c66affSColin Finck /* Set the event and close it */
328c2c66affSColin Finck if (DosRecord->ServerEvent)
329c2c66affSColin Finck {
330c2c66affSColin Finck NtSetEvent(DosRecord->ServerEvent, NULL);
331c2c66affSColin Finck NtClose(DosRecord->ServerEvent);
332c2c66affSColin Finck DosRecord->ServerEvent = NULL;
333c2c66affSColin Finck }
334c2c66affSColin Finck
335c2c66affSColin Finck /* Remove the DOS entry */
336c2c66affSColin Finck if (DosRecord->CommandInfo) BaseSrvFreeVDMInfo(DosRecord->CommandInfo);
337c2c66affSColin Finck RemoveEntryList(&DosRecord->Entry);
338c2c66affSColin Finck RtlFreeHeap(BaseSrvHeap, 0, DosRecord);
339c2c66affSColin Finck }
340c2c66affSColin Finck
341c2c66affSColin Finck /* Remove the console record */
342c2c66affSColin Finck RemoveEntryList(&ConsoleRecord->Entry);
343c2c66affSColin Finck BaseSrvDestroyConsoleRecord(ConsoleRecord);
344c2c66affSColin Finck }
345c2c66affSColin Finck }
346c2c66affSColin Finck
347c2c66affSColin Finck /* Leave the critical section */
348c2c66affSColin Finck RtlLeaveCriticalSection(&DosCriticalSection);
349c2c66affSColin Finck }
350c2c66affSColin Finck
BaseSrvCopyCommand(PBASE_CHECK_VDM CheckVdmRequest,PVDM_DOS_RECORD DosRecord)351c2c66affSColin Finck BOOLEAN BaseSrvCopyCommand(PBASE_CHECK_VDM CheckVdmRequest, PVDM_DOS_RECORD DosRecord)
352c2c66affSColin Finck {
353c2c66affSColin Finck BOOLEAN Success = FALSE;
354c2c66affSColin Finck PVDM_COMMAND_INFO CommandInfo = NULL;
355c2c66affSColin Finck
356c2c66affSColin Finck /* Allocate the command information structure */
357c2c66affSColin Finck CommandInfo = (PVDM_COMMAND_INFO)RtlAllocateHeap(BaseSrvHeap,
358c2c66affSColin Finck HEAP_ZERO_MEMORY,
359c2c66affSColin Finck sizeof(VDM_COMMAND_INFO));
360c2c66affSColin Finck if (CommandInfo == NULL) return FALSE;
361c2c66affSColin Finck
362c2c66affSColin Finck /* Fill the structure */
363c2c66affSColin Finck CommandInfo->TaskId = CheckVdmRequest->iTask;
364c2c66affSColin Finck CommandInfo->ExitCode = DosRecord->ExitCode;
365c2c66affSColin Finck CommandInfo->CodePage = CheckVdmRequest->CodePage;
366c2c66affSColin Finck CommandInfo->StdIn = CheckVdmRequest->StdIn;
367c2c66affSColin Finck CommandInfo->StdOut = CheckVdmRequest->StdOut;
368c2c66affSColin Finck CommandInfo->StdErr = CheckVdmRequest->StdErr;
369c2c66affSColin Finck
370c2c66affSColin Finck /* Allocate memory for the command line */
371c2c66affSColin Finck CommandInfo->CmdLine = RtlAllocateHeap(BaseSrvHeap,
372c2c66affSColin Finck HEAP_ZERO_MEMORY,
373c2c66affSColin Finck CheckVdmRequest->CmdLen);
374c2c66affSColin Finck if (CommandInfo->CmdLine == NULL) goto Cleanup;
375c2c66affSColin Finck
376c2c66affSColin Finck /* Copy the command line */
377c2c66affSColin Finck RtlMoveMemory(CommandInfo->CmdLine, CheckVdmRequest->CmdLine, CheckVdmRequest->CmdLen);
378c2c66affSColin Finck
379c2c66affSColin Finck /* Allocate memory for the application name */
380c2c66affSColin Finck CommandInfo->AppName = RtlAllocateHeap(BaseSrvHeap,
381c2c66affSColin Finck HEAP_ZERO_MEMORY,
382c2c66affSColin Finck CheckVdmRequest->AppLen);
383c2c66affSColin Finck if (CommandInfo->AppName == NULL) goto Cleanup;
384c2c66affSColin Finck
385c2c66affSColin Finck /* Copy the application name */
386c2c66affSColin Finck RtlMoveMemory(CommandInfo->AppName, CheckVdmRequest->AppName, CheckVdmRequest->AppLen);
387c2c66affSColin Finck
388c2c66affSColin Finck /* Allocate memory for the PIF file name */
389c2c66affSColin Finck if (CheckVdmRequest->PifLen != 0)
390c2c66affSColin Finck {
391c2c66affSColin Finck CommandInfo->PifFile = RtlAllocateHeap(BaseSrvHeap,
392c2c66affSColin Finck HEAP_ZERO_MEMORY,
393c2c66affSColin Finck CheckVdmRequest->PifLen);
394c2c66affSColin Finck if (CommandInfo->PifFile == NULL) goto Cleanup;
395c2c66affSColin Finck
396c2c66affSColin Finck /* Copy the PIF file name */
397c2c66affSColin Finck RtlMoveMemory(CommandInfo->PifFile, CheckVdmRequest->PifFile, CheckVdmRequest->PifLen);
398c2c66affSColin Finck }
399c2c66affSColin Finck else CommandInfo->PifFile = NULL;
400c2c66affSColin Finck
401c2c66affSColin Finck /* Allocate memory for the current directory */
402c2c66affSColin Finck if (CheckVdmRequest->CurDirectoryLen != 0)
403c2c66affSColin Finck {
404c2c66affSColin Finck CommandInfo->CurDirectory = RtlAllocateHeap(BaseSrvHeap,
405c2c66affSColin Finck HEAP_ZERO_MEMORY,
406c2c66affSColin Finck CheckVdmRequest->CurDirectoryLen);
407c2c66affSColin Finck if (CommandInfo->CurDirectory == NULL) goto Cleanup;
408c2c66affSColin Finck
409c2c66affSColin Finck /* Copy the current directory */
410c2c66affSColin Finck RtlMoveMemory(CommandInfo->CurDirectory,
411c2c66affSColin Finck CheckVdmRequest->CurDirectory,
412c2c66affSColin Finck CheckVdmRequest->CurDirectoryLen);
413c2c66affSColin Finck }
414c2c66affSColin Finck else CommandInfo->CurDirectory = NULL;
415c2c66affSColin Finck
416c2c66affSColin Finck /* Allocate memory for the environment block */
417c2c66affSColin Finck CommandInfo->Env = RtlAllocateHeap(BaseSrvHeap,
418c2c66affSColin Finck HEAP_ZERO_MEMORY,
419c2c66affSColin Finck CheckVdmRequest->EnvLen);
420c2c66affSColin Finck if (CommandInfo->Env == NULL) goto Cleanup;
421c2c66affSColin Finck
422c2c66affSColin Finck /* Copy the environment block */
423c2c66affSColin Finck RtlMoveMemory(CommandInfo->Env, CheckVdmRequest->Env, CheckVdmRequest->EnvLen);
424c2c66affSColin Finck
425c2c66affSColin Finck CommandInfo->EnvLen = CheckVdmRequest->EnvLen;
426c2c66affSColin Finck RtlMoveMemory(&CommandInfo->StartupInfo,
427c2c66affSColin Finck CheckVdmRequest->StartupInfo,
428c2c66affSColin Finck sizeof(STARTUPINFOA));
429c2c66affSColin Finck
430c2c66affSColin Finck /* Allocate memory for the desktop */
431c2c66affSColin Finck if (CheckVdmRequest->DesktopLen != 0)
432c2c66affSColin Finck {
433c2c66affSColin Finck CommandInfo->Desktop = RtlAllocateHeap(BaseSrvHeap,
434c2c66affSColin Finck HEAP_ZERO_MEMORY,
435c2c66affSColin Finck CheckVdmRequest->DesktopLen);
436c2c66affSColin Finck if (CommandInfo->Desktop == NULL) goto Cleanup;
437c2c66affSColin Finck
438c2c66affSColin Finck /* Copy the desktop name */
439c2c66affSColin Finck RtlMoveMemory(CommandInfo->Desktop, CheckVdmRequest->Desktop, CheckVdmRequest->DesktopLen);
440c2c66affSColin Finck }
441c2c66affSColin Finck else CommandInfo->Desktop = NULL;
442c2c66affSColin Finck
443c2c66affSColin Finck CommandInfo->DesktopLen = CheckVdmRequest->DesktopLen;
444c2c66affSColin Finck
445c2c66affSColin Finck /* Allocate memory for the title */
446c2c66affSColin Finck if (CheckVdmRequest->TitleLen != 0)
447c2c66affSColin Finck {
448c2c66affSColin Finck CommandInfo->Title = RtlAllocateHeap(BaseSrvHeap,
449c2c66affSColin Finck HEAP_ZERO_MEMORY,
450c2c66affSColin Finck CheckVdmRequest->TitleLen);
451c2c66affSColin Finck if (CommandInfo->Title == NULL) goto Cleanup;
452c2c66affSColin Finck
453c2c66affSColin Finck /* Copy the title */
454c2c66affSColin Finck RtlMoveMemory(CommandInfo->Title, CheckVdmRequest->Title, CheckVdmRequest->TitleLen);
455c2c66affSColin Finck }
456c2c66affSColin Finck else CommandInfo->Title = NULL;
457c2c66affSColin Finck
458c2c66affSColin Finck CommandInfo->TitleLen = CheckVdmRequest->TitleLen;
459c2c66affSColin Finck
460c2c66affSColin Finck /* Allocate memory for the reserved field */
461c2c66affSColin Finck if (CheckVdmRequest->ReservedLen != 0)
462c2c66affSColin Finck {
463c2c66affSColin Finck CommandInfo->Reserved = RtlAllocateHeap(BaseSrvHeap,
464c2c66affSColin Finck HEAP_ZERO_MEMORY,
465c2c66affSColin Finck CheckVdmRequest->ReservedLen);
466c2c66affSColin Finck if (CommandInfo->Reserved == NULL) goto Cleanup;
467c2c66affSColin Finck
468c2c66affSColin Finck /* Copy the reserved field */
469c2c66affSColin Finck RtlMoveMemory(CommandInfo->Reserved,
470c2c66affSColin Finck CheckVdmRequest->Reserved,
471c2c66affSColin Finck CheckVdmRequest->ReservedLen);
472c2c66affSColin Finck }
473c2c66affSColin Finck else CommandInfo->Reserved = NULL;
474c2c66affSColin Finck
475c2c66affSColin Finck CommandInfo->ReservedLen = CheckVdmRequest->ReservedLen;
476c2c66affSColin Finck
477c2c66affSColin Finck CommandInfo->CmdLen = CheckVdmRequest->CmdLen;
478c2c66affSColin Finck CommandInfo->AppLen = CheckVdmRequest->AppLen;
479c2c66affSColin Finck CommandInfo->PifLen = CheckVdmRequest->PifLen;
480c2c66affSColin Finck CommandInfo->CurDirectoryLen = CheckVdmRequest->CurDirectoryLen;
481c2c66affSColin Finck CommandInfo->VDMState = DosRecord->State;
482c2c66affSColin Finck // TODO: Set CommandInfo->CurrentDrive
483c2c66affSColin Finck // TODO: Set CommandInfo->ComingFromBat
484c2c66affSColin Finck
485c2c66affSColin Finck /* Set the DOS record's command structure */
486c2c66affSColin Finck DosRecord->CommandInfo = CommandInfo;
487c2c66affSColin Finck
488c2c66affSColin Finck /* The operation was successful */
489c2c66affSColin Finck Success = TRUE;
490c2c66affSColin Finck
491c2c66affSColin Finck Cleanup:
492c2c66affSColin Finck /* If it wasn't successful, free the memory */
493c2c66affSColin Finck if (!Success) BaseSrvFreeVDMInfo(CommandInfo);
494c2c66affSColin Finck
495c2c66affSColin Finck return Success;
496c2c66affSColin Finck }
497c2c66affSColin Finck
BaseSrvFillCommandInfo(PVDM_COMMAND_INFO CommandInfo,PBASE_GET_NEXT_VDM_COMMAND Message)498c2c66affSColin Finck NTSTATUS BaseSrvFillCommandInfo(PVDM_COMMAND_INFO CommandInfo,
499c2c66affSColin Finck PBASE_GET_NEXT_VDM_COMMAND Message)
500c2c66affSColin Finck {
501c2c66affSColin Finck NTSTATUS Status = STATUS_SUCCESS;
502c2c66affSColin Finck
503c2c66affSColin Finck /* Copy the data */
504c2c66affSColin Finck Message->iTask = CommandInfo->TaskId;
505c2c66affSColin Finck Message->StdIn = CommandInfo->StdIn;
506c2c66affSColin Finck Message->StdOut = CommandInfo->StdOut;
507c2c66affSColin Finck Message->StdErr = CommandInfo->StdErr;
508c2c66affSColin Finck Message->CodePage = CommandInfo->CodePage;
509c2c66affSColin Finck Message->dwCreationFlags = CommandInfo->CreationFlags;
510c2c66affSColin Finck Message->ExitCode = CommandInfo->ExitCode;
511c2c66affSColin Finck Message->CurrentDrive = CommandInfo->CurrentDrive;
512c2c66affSColin Finck Message->VDMState = CommandInfo->VDMState;
513c2c66affSColin Finck Message->fComingFromBat = CommandInfo->ComingFromBat;
514c2c66affSColin Finck
515c2c66affSColin Finck if (Message->CmdLen >= CommandInfo->CmdLen)
516c2c66affSColin Finck {
517c2c66affSColin Finck /* Copy the command line */
518c2c66affSColin Finck RtlMoveMemory(Message->CmdLine, CommandInfo->CmdLine, CommandInfo->CmdLen);
519c2c66affSColin Finck }
520c2c66affSColin Finck else Status = STATUS_INVALID_PARAMETER;
521c2c66affSColin Finck Message->CmdLen = CommandInfo->CmdLen;
522c2c66affSColin Finck
523c2c66affSColin Finck if (Message->AppLen >= CommandInfo->AppLen)
524c2c66affSColin Finck {
525c2c66affSColin Finck /* Copy the application name */
526c2c66affSColin Finck RtlMoveMemory(Message->AppName, CommandInfo->AppName, CommandInfo->AppLen);
527c2c66affSColin Finck }
528c2c66affSColin Finck else Status = STATUS_INVALID_PARAMETER;
529c2c66affSColin Finck Message->AppLen = CommandInfo->AppLen;
530c2c66affSColin Finck
531c2c66affSColin Finck if (Message->PifLen >= CommandInfo->PifLen)
532c2c66affSColin Finck {
533c2c66affSColin Finck /* Copy the PIF file name */
534c2c66affSColin Finck RtlMoveMemory(Message->PifFile, CommandInfo->PifFile, CommandInfo->PifLen);
535c2c66affSColin Finck }
536c2c66affSColin Finck else Status = STATUS_INVALID_PARAMETER;
537c2c66affSColin Finck Message->PifLen = CommandInfo->PifLen;
538c2c66affSColin Finck
539c2c66affSColin Finck if (Message->CurDirectoryLen >= CommandInfo->CurDirectoryLen)
540c2c66affSColin Finck {
541c2c66affSColin Finck /* Copy the current directory */
542c2c66affSColin Finck RtlMoveMemory(Message->CurDirectory, CommandInfo->CurDirectory, CommandInfo->CurDirectoryLen);
543c2c66affSColin Finck }
544c2c66affSColin Finck else Status = STATUS_INVALID_PARAMETER;
545c2c66affSColin Finck Message->CurDirectoryLen = CommandInfo->CurDirectoryLen;
546c2c66affSColin Finck
547c2c66affSColin Finck if (Message->EnvLen >= CommandInfo->EnvLen)
548c2c66affSColin Finck {
549c2c66affSColin Finck /* Copy the environment */
550c2c66affSColin Finck RtlMoveMemory(Message->Env, CommandInfo->Env, CommandInfo->EnvLen);
551c2c66affSColin Finck }
552c2c66affSColin Finck else Status = STATUS_INVALID_PARAMETER;
553c2c66affSColin Finck Message->EnvLen = CommandInfo->EnvLen;
554c2c66affSColin Finck
555c2c66affSColin Finck /* Copy the startup info */
556c2c66affSColin Finck RtlMoveMemory(Message->StartupInfo,
557c2c66affSColin Finck &CommandInfo->StartupInfo,
558c2c66affSColin Finck sizeof(STARTUPINFOA));
559c2c66affSColin Finck
560c2c66affSColin Finck if (Message->DesktopLen >= CommandInfo->DesktopLen)
561c2c66affSColin Finck {
562c2c66affSColin Finck /* Copy the desktop name */
563c2c66affSColin Finck RtlMoveMemory(Message->Desktop, CommandInfo->Desktop, CommandInfo->DesktopLen);
564c2c66affSColin Finck }
565c2c66affSColin Finck else Status = STATUS_INVALID_PARAMETER;
566c2c66affSColin Finck Message->DesktopLen = CommandInfo->DesktopLen;
567c2c66affSColin Finck
568c2c66affSColin Finck if (Message->TitleLen >= CommandInfo->TitleLen)
569c2c66affSColin Finck {
570c2c66affSColin Finck /* Copy the title */
571c2c66affSColin Finck RtlMoveMemory(Message->Title, CommandInfo->Title, CommandInfo->TitleLen);
572c2c66affSColin Finck }
573c2c66affSColin Finck else Status = STATUS_INVALID_PARAMETER;
574c2c66affSColin Finck Message->TitleLen = CommandInfo->TitleLen;
575c2c66affSColin Finck
576c2c66affSColin Finck if (Message->ReservedLen >= CommandInfo->ReservedLen)
577c2c66affSColin Finck {
578c2c66affSColin Finck /* Copy the reserved parameter */
579c2c66affSColin Finck RtlMoveMemory(Message->Reserved, CommandInfo->Reserved, CommandInfo->ReservedLen);
580c2c66affSColin Finck }
581c2c66affSColin Finck else Status = STATUS_INVALID_PARAMETER;
582c2c66affSColin Finck Message->ReservedLen = CommandInfo->ReservedLen;
583c2c66affSColin Finck
584c2c66affSColin Finck return Status;
585c2c66affSColin Finck }
586c2c66affSColin Finck
BaseInitializeVDM(VOID)587c2c66affSColin Finck VOID BaseInitializeVDM(VOID)
588c2c66affSColin Finck {
589c2c66affSColin Finck /* Initialize the list head */
590c2c66affSColin Finck InitializeListHead(&VDMConsoleListHead);
591c2c66affSColin Finck
592c2c66affSColin Finck /* Initialize the critical sections */
593c2c66affSColin Finck RtlInitializeCriticalSection(&DosCriticalSection);
594c2c66affSColin Finck RtlInitializeCriticalSection(&WowCriticalSection);
595c2c66affSColin Finck }
596c2c66affSColin Finck
597c2c66affSColin Finck /* PUBLIC SERVER APIS *********************************************************/
598c2c66affSColin Finck
CSR_API(BaseSrvCheckVDM)599c2c66affSColin Finck CSR_API(BaseSrvCheckVDM)
600c2c66affSColin Finck {
601c2c66affSColin Finck NTSTATUS Status;
602c2c66affSColin Finck PBASE_CHECK_VDM CheckVdmRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.CheckVDMRequest;
603c2c66affSColin Finck PRTL_CRITICAL_SECTION CriticalSection = NULL;
604c2c66affSColin Finck PVDM_CONSOLE_RECORD ConsoleRecord = NULL;
605c2c66affSColin Finck PVDM_DOS_RECORD DosRecord = NULL;
606c2c66affSColin Finck BOOLEAN NewConsoleRecord = FALSE;
607c2c66affSColin Finck BOOLEAN NewDosRecord = FALSE;
608c2c66affSColin Finck
609c2c66affSColin Finck /* Don't do anything if the VDM has been disabled in the registry */
610c2c66affSColin Finck if (!BaseSrvIsVdmAllowed()) return STATUS_VDM_DISALLOWED;
611c2c66affSColin Finck
612c2c66affSColin Finck /* Validate the message buffers */
613c2c66affSColin Finck if (!CsrValidateMessageBuffer(ApiMessage,
614c2c66affSColin Finck (PVOID*)&CheckVdmRequest->CmdLine,
615c2c66affSColin Finck CheckVdmRequest->CmdLen,
616c2c66affSColin Finck sizeof(*CheckVdmRequest->CmdLine))
617c2c66affSColin Finck || !CsrValidateMessageBuffer(ApiMessage,
618c2c66affSColin Finck (PVOID*)&CheckVdmRequest->AppName,
619c2c66affSColin Finck CheckVdmRequest->AppLen,
620c2c66affSColin Finck sizeof(*CheckVdmRequest->AppName))
621c2c66affSColin Finck || !CsrValidateMessageBuffer(ApiMessage,
622c2c66affSColin Finck (PVOID*)&CheckVdmRequest->PifFile,
623c2c66affSColin Finck CheckVdmRequest->PifLen,
624c2c66affSColin Finck sizeof(*CheckVdmRequest->PifFile))
625c2c66affSColin Finck || !CsrValidateMessageBuffer(ApiMessage,
626c2c66affSColin Finck (PVOID*)&CheckVdmRequest->CurDirectory,
627c2c66affSColin Finck CheckVdmRequest->CurDirectoryLen,
628c2c66affSColin Finck sizeof(*CheckVdmRequest->CurDirectory))
629c2c66affSColin Finck || !CsrValidateMessageBuffer(ApiMessage,
630c2c66affSColin Finck (PVOID*)&CheckVdmRequest->Desktop,
631c2c66affSColin Finck CheckVdmRequest->DesktopLen,
632c2c66affSColin Finck sizeof(*CheckVdmRequest->Desktop))
633c2c66affSColin Finck || !CsrValidateMessageBuffer(ApiMessage,
634c2c66affSColin Finck (PVOID*)&CheckVdmRequest->Title,
635c2c66affSColin Finck CheckVdmRequest->TitleLen,
636c2c66affSColin Finck sizeof(*CheckVdmRequest->Title))
637c2c66affSColin Finck || !CsrValidateMessageBuffer(ApiMessage,
638c2c66affSColin Finck (PVOID*)&CheckVdmRequest->Reserved,
639c2c66affSColin Finck CheckVdmRequest->ReservedLen,
640c2c66affSColin Finck sizeof(*CheckVdmRequest->Reserved)))
641c2c66affSColin Finck {
642c2c66affSColin Finck return STATUS_INVALID_PARAMETER;
643c2c66affSColin Finck }
644c2c66affSColin Finck
645c2c66affSColin Finck CriticalSection = (CheckVdmRequest->BinaryType != BINARY_TYPE_SEPARATE_WOW)
646c2c66affSColin Finck ? &DosCriticalSection
647c2c66affSColin Finck : &WowCriticalSection;
648c2c66affSColin Finck
649c2c66affSColin Finck /* Enter the critical section */
650c2c66affSColin Finck RtlEnterCriticalSection(CriticalSection);
651c2c66affSColin Finck
652c2c66affSColin Finck /* Check if this is a DOS or WOW VDM */
653c2c66affSColin Finck if (CheckVdmRequest->BinaryType != BINARY_TYPE_SEPARATE_WOW)
654c2c66affSColin Finck {
655c2c66affSColin Finck /* Get the console record */
656c2c66affSColin Finck Status = BaseSrvGetConsoleRecord(CheckVdmRequest->ConsoleHandle,
657c2c66affSColin Finck &ConsoleRecord);
658c2c66affSColin Finck if (!NT_SUCCESS(Status))
659c2c66affSColin Finck {
660c2c66affSColin Finck /* Allocate a new console record */
661c2c66affSColin Finck ConsoleRecord = BaseSrvCreateConsoleRecord();
662c2c66affSColin Finck if (ConsoleRecord == NULL)
663c2c66affSColin Finck {
664c2c66affSColin Finck Status = STATUS_NO_MEMORY;
665c2c66affSColin Finck goto Cleanup;
666c2c66affSColin Finck }
667c2c66affSColin Finck
668c2c66affSColin Finck /* Initialize the console record */
669c2c66affSColin Finck ConsoleRecord->ConsoleHandle = CheckVdmRequest->ConsoleHandle;
670c2c66affSColin Finck if (ConsoleRecord->ConsoleHandle == NULL)
671c2c66affSColin Finck {
672c2c66affSColin Finck /* The parent doesn't have a console, get a new session ID */
673c2c66affSColin Finck ConsoleRecord->SessionId = GetNextDosSesId();
674c2c66affSColin Finck }
675c2c66affSColin Finck else
676c2c66affSColin Finck {
677c2c66affSColin Finck /* No session ID is needed */
678c2c66affSColin Finck ConsoleRecord->SessionId = 0;
679c2c66affSColin Finck }
680c2c66affSColin Finck
681c2c66affSColin Finck /* Remember that the console record was allocated here */
682c2c66affSColin Finck NewConsoleRecord = TRUE;
683c2c66affSColin Finck }
684c2c66affSColin Finck
685c2c66affSColin Finck if (!NewConsoleRecord)
686c2c66affSColin Finck {
687c2c66affSColin Finck /* Get the primary DOS record */
688c2c66affSColin Finck DosRecord = (PVDM_DOS_RECORD)CONTAINING_RECORD(ConsoleRecord->DosListHead.Flink,
689c2c66affSColin Finck VDM_DOS_RECORD, Entry);
690c2c66affSColin Finck
691c2c66affSColin Finck if (DosRecord->State != VDM_READY) // == VDM_NOT_READY
692c2c66affSColin Finck {
693c2c66affSColin Finck /* Allocate a new DOS record */
694c2c66affSColin Finck DosRecord = (PVDM_DOS_RECORD)RtlAllocateHeap(BaseSrvHeap,
695c2c66affSColin Finck HEAP_ZERO_MEMORY,
696c2c66affSColin Finck sizeof(VDM_DOS_RECORD));
697c2c66affSColin Finck if (DosRecord == NULL)
698c2c66affSColin Finck {
699c2c66affSColin Finck Status = STATUS_NO_MEMORY;
700c2c66affSColin Finck goto Cleanup;
701c2c66affSColin Finck }
702c2c66affSColin Finck
703c2c66affSColin Finck /* Remember that the DOS record was allocated here */
704c2c66affSColin Finck NewDosRecord = TRUE;
705c2c66affSColin Finck }
706c2c66affSColin Finck }
707c2c66affSColin Finck else
708c2c66affSColin Finck {
709c2c66affSColin Finck /* Allocate a new DOS record */
710c2c66affSColin Finck DosRecord = (PVDM_DOS_RECORD)RtlAllocateHeap(BaseSrvHeap,
711c2c66affSColin Finck HEAP_ZERO_MEMORY,
712c2c66affSColin Finck sizeof(VDM_DOS_RECORD));
713c2c66affSColin Finck if (DosRecord == NULL)
714c2c66affSColin Finck {
715c2c66affSColin Finck Status = STATUS_NO_MEMORY;
716c2c66affSColin Finck goto Cleanup;
717c2c66affSColin Finck }
718c2c66affSColin Finck
719c2c66affSColin Finck /* Remember that the DOS record was allocated here */
720c2c66affSColin Finck NewDosRecord = TRUE;
721c2c66affSColin Finck }
722c2c66affSColin Finck
723c2c66affSColin Finck /* Initialize the DOS record */
724c2c66affSColin Finck DosRecord->State = VDM_NOT_READY;
725c2c66affSColin Finck DosRecord->ExitCode = 0;
726c2c66affSColin Finck
727c2c66affSColin Finck /* Translate the input structure into a VDM command structure and set it in the DOS record */
728c2c66affSColin Finck if (!BaseSrvCopyCommand(CheckVdmRequest, DosRecord))
729c2c66affSColin Finck {
730c2c66affSColin Finck /* The only possibility is that an allocation failure occurred */
731c2c66affSColin Finck Status = STATUS_NO_MEMORY;
732c2c66affSColin Finck goto Cleanup;
733c2c66affSColin Finck }
734c2c66affSColin Finck
735c2c66affSColin Finck if (NewDosRecord)
736c2c66affSColin Finck {
737c2c66affSColin Finck /* Add the DOS record */
738c2c66affSColin Finck InsertHeadList(&ConsoleRecord->DosListHead, &DosRecord->Entry);
739c2c66affSColin Finck }
740c2c66affSColin Finck
741c2c66affSColin Finck if (!NewConsoleRecord)
742c2c66affSColin Finck {
743c2c66affSColin Finck Status = BaseSrvCreatePairWaitHandles(&DosRecord->ServerEvent, &DosRecord->ClientEvent);
744c2c66affSColin Finck if (!NT_SUCCESS(Status)) goto Cleanup;
745c2c66affSColin Finck
746c2c66affSColin Finck /* Return the client event handle */
747c2c66affSColin Finck CheckVdmRequest->WaitObjectForParent = DosRecord->ClientEvent;
748c2c66affSColin Finck }
749c2c66affSColin Finck
750c2c66affSColin Finck // FIXME: We may notify ONLY if ConsoleRecord->nReEntrancy is > 0
751c2c66affSColin Finck // in case NewConsoleRecord == FALSE AND NewDosRecord == TRUE.
752c2c66affSColin Finck if (ConsoleRecord->ServerEvent)
753c2c66affSColin Finck {
754c2c66affSColin Finck /* Signal the session event */
755c2c66affSColin Finck NtSetEvent(ConsoleRecord->ServerEvent, NULL);
756c2c66affSColin Finck }
757c2c66affSColin Finck
758c2c66affSColin Finck if (NewConsoleRecord)
759c2c66affSColin Finck {
760c2c66affSColin Finck /* Add the console record */
761c2c66affSColin Finck InsertTailList(&VDMConsoleListHead, &ConsoleRecord->Entry);
762c2c66affSColin Finck }
763c2c66affSColin Finck
764c2c66affSColin Finck CheckVdmRequest->iTask = ConsoleRecord->SessionId;
765c2c66affSColin Finck CheckVdmRequest->VDMState = NewConsoleRecord ? VDM_NOT_LOADED : VDM_READY;
766c2c66affSColin Finck Status = STATUS_SUCCESS;
767c2c66affSColin Finck }
768c2c66affSColin Finck else
769c2c66affSColin Finck {
770c2c66affSColin Finck // TODO: NOT IMPLEMENTED
771c2c66affSColin Finck UNIMPLEMENTED;
772c2c66affSColin Finck Status = STATUS_NOT_IMPLEMENTED;
773c2c66affSColin Finck }
774c2c66affSColin Finck
775c2c66affSColin Finck Cleanup:
776c2c66affSColin Finck /* Check if it failed */
777c2c66affSColin Finck if (!NT_SUCCESS(Status))
778c2c66affSColin Finck {
779c2c66affSColin Finck /* Free the DOS record if it was allocated here */
780c2c66affSColin Finck if (NewDosRecord)
781c2c66affSColin Finck {
782c2c66affSColin Finck ASSERT(DosRecord != NULL);
783c2c66affSColin Finck
784c2c66affSColin Finck BaseSrvDestroyPairWaitHandles(DosRecord->ServerEvent,
785c2c66affSColin Finck DosRecord->ClientEvent);
786c2c66affSColin Finck
787c2c66affSColin Finck RtlFreeHeap(BaseSrvHeap, 0, DosRecord);
788c2c66affSColin Finck DosRecord = NULL;
789c2c66affSColin Finck }
790c2c66affSColin Finck
791c2c66affSColin Finck /* Free the console record if it was allocated here */
792c2c66affSColin Finck if (NewConsoleRecord)
793c2c66affSColin Finck {
794c2c66affSColin Finck ASSERT(ConsoleRecord != NULL);
795c2c66affSColin Finck
796c2c66affSColin Finck RtlFreeHeap(BaseSrvHeap, 0, ConsoleRecord);
797c2c66affSColin Finck ConsoleRecord = NULL;
798c2c66affSColin Finck }
799c2c66affSColin Finck }
800c2c66affSColin Finck
801c2c66affSColin Finck /* Leave the critical section */
802c2c66affSColin Finck RtlLeaveCriticalSection(CriticalSection);
803c2c66affSColin Finck
804c2c66affSColin Finck return Status;
805c2c66affSColin Finck }
806c2c66affSColin Finck
CSR_API(BaseSrvUpdateVDMEntry)807c2c66affSColin Finck CSR_API(BaseSrvUpdateVDMEntry)
808c2c66affSColin Finck {
809c2c66affSColin Finck NTSTATUS Status;
810c2c66affSColin Finck PBASE_UPDATE_VDM_ENTRY UpdateVdmEntryRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.UpdateVDMEntryRequest;
811c2c66affSColin Finck PRTL_CRITICAL_SECTION CriticalSection = NULL;
812c2c66affSColin Finck PVDM_CONSOLE_RECORD ConsoleRecord = NULL;
813c2c66affSColin Finck PVDM_DOS_RECORD DosRecord = NULL;
814c2c66affSColin Finck
815c2c66affSColin Finck CriticalSection = (UpdateVdmEntryRequest->BinaryType != BINARY_TYPE_SEPARATE_WOW)
816c2c66affSColin Finck ? &DosCriticalSection
817c2c66affSColin Finck : &WowCriticalSection;
818c2c66affSColin Finck
819c2c66affSColin Finck /* Enter the critical section */
820c2c66affSColin Finck RtlEnterCriticalSection(CriticalSection);
821c2c66affSColin Finck
822c2c66affSColin Finck /* Check if this is a DOS or WOW VDM */
823c2c66affSColin Finck if (UpdateVdmEntryRequest->BinaryType != BINARY_TYPE_SEPARATE_WOW)
824c2c66affSColin Finck {
825c2c66affSColin Finck if (UpdateVdmEntryRequest->iTask != 0)
826c2c66affSColin Finck {
827c2c66affSColin Finck /* Get the console record using the task ID */
828c2c66affSColin Finck Status = GetConsoleRecordBySessionId(UpdateVdmEntryRequest->iTask,
829c2c66affSColin Finck &ConsoleRecord);
830c2c66affSColin Finck }
831c2c66affSColin Finck else
832c2c66affSColin Finck {
833c2c66affSColin Finck /* Get the console record using the console handle */
834c2c66affSColin Finck Status = BaseSrvGetConsoleRecord(UpdateVdmEntryRequest->ConsoleHandle,
835c2c66affSColin Finck &ConsoleRecord);
836c2c66affSColin Finck }
837c2c66affSColin Finck
838c2c66affSColin Finck if (!NT_SUCCESS(Status)) goto Cleanup;
839c2c66affSColin Finck
840c2c66affSColin Finck /* Get the primary DOS record */
841c2c66affSColin Finck DosRecord = (PVDM_DOS_RECORD)CONTAINING_RECORD(ConsoleRecord->DosListHead.Flink,
842c2c66affSColin Finck VDM_DOS_RECORD, Entry);
843c2c66affSColin Finck
844c2c66affSColin Finck switch (UpdateVdmEntryRequest->EntryIndex)
845c2c66affSColin Finck {
846c2c66affSColin Finck case VdmEntryUndo:
847c2c66affSColin Finck {
848c2c66affSColin Finck /* Close the server event handle, the client will close the client handle */
849c2c66affSColin Finck NtClose(DosRecord->ServerEvent);
850c2c66affSColin Finck DosRecord->ServerEvent = DosRecord->ClientEvent = NULL;
851c2c66affSColin Finck
852c2c66affSColin Finck if (UpdateVdmEntryRequest->VDMCreationState & (VDM_UNDO_PARTIAL | VDM_UNDO_FULL))
853c2c66affSColin Finck {
854c2c66affSColin Finck /* Remove the DOS record */
855c2c66affSColin Finck if (DosRecord->CommandInfo) BaseSrvFreeVDMInfo(DosRecord->CommandInfo);
856c2c66affSColin Finck RemoveEntryList(&DosRecord->Entry);
857c2c66affSColin Finck RtlFreeHeap(BaseSrvHeap, 0, DosRecord);
858c2c66affSColin Finck
859c2c66affSColin Finck /*
860c2c66affSColin Finck * Since this is an undo, if that was the only DOS record the VDM
861c2c66affSColin Finck * won't even start, so the console record should be removed too.
862c2c66affSColin Finck */
863c2c66affSColin Finck if (ConsoleRecord->DosListHead.Flink == &ConsoleRecord->DosListHead)
864c2c66affSColin Finck {
865c2c66affSColin Finck RemoveEntryList(&ConsoleRecord->Entry);
866c2c66affSColin Finck BaseSrvDestroyConsoleRecord(ConsoleRecord);
867c2c66affSColin Finck }
868c2c66affSColin Finck }
869c2c66affSColin Finck
870c2c66affSColin Finck /* It was successful */
871c2c66affSColin Finck Status = STATUS_SUCCESS;
872c2c66affSColin Finck
873c2c66affSColin Finck break;
874c2c66affSColin Finck }
875c2c66affSColin Finck
876c2c66affSColin Finck case VdmEntryUpdateProcess:
877c2c66affSColin Finck {
878c2c66affSColin Finck /* Duplicate the VDM process handle */
879c2c66affSColin Finck Status = NtDuplicateObject(CsrGetClientThread()->Process->ProcessHandle,
880c2c66affSColin Finck UpdateVdmEntryRequest->VDMProcessHandle,
881c2c66affSColin Finck NtCurrentProcess(),
882c2c66affSColin Finck &ConsoleRecord->ProcessHandle,
883c2c66affSColin Finck 0,
884c2c66affSColin Finck 0,
885c2c66affSColin Finck DUPLICATE_SAME_ATTRIBUTES | DUPLICATE_SAME_ACCESS);
886c2c66affSColin Finck if (!NT_SUCCESS(Status)) goto Cleanup;
887c2c66affSColin Finck
888c2c66affSColin Finck //
889c2c66affSColin Finck // FIXME! Should we always do the following??
890c2c66affSColin Finck //
891c2c66affSColin Finck
892c2c66affSColin Finck /* Create a pair of handles to one event object */
893c2c66affSColin Finck Status = BaseSrvCreatePairWaitHandles(&DosRecord->ServerEvent,
894c2c66affSColin Finck &DosRecord->ClientEvent);
895c2c66affSColin Finck if (!NT_SUCCESS(Status)) goto Cleanup;
896c2c66affSColin Finck
897c2c66affSColin Finck /* Return the client event handle */
898c2c66affSColin Finck UpdateVdmEntryRequest->WaitObjectForParent = DosRecord->ClientEvent;
899c2c66affSColin Finck
900c2c66affSColin Finck break;
901c2c66affSColin Finck }
902c2c66affSColin Finck
903c2c66affSColin Finck case VdmEntryUpdateControlCHandler:
904c2c66affSColin Finck {
905c2c66affSColin Finck // TODO: NOT IMPLEMENTED
906*f7d612f3SSerge Gautherie DPRINT1("BaseSrvUpdateVDMEntry: VdmEntryUpdateControlCHandler not implemented\n");
907c2c66affSColin Finck Status = STATUS_NOT_IMPLEMENTED;
908c2c66affSColin Finck
909c2c66affSColin Finck break;
910c2c66affSColin Finck }
911c2c66affSColin Finck
912c2c66affSColin Finck default:
913c2c66affSColin Finck {
914c2c66affSColin Finck /* Invalid */
915c2c66affSColin Finck Status = STATUS_INVALID_PARAMETER;
916c2c66affSColin Finck }
917c2c66affSColin Finck }
918c2c66affSColin Finck }
919c2c66affSColin Finck else
920c2c66affSColin Finck {
921c2c66affSColin Finck // TODO: NOT IMPLEMENTED
922c2c66affSColin Finck UNIMPLEMENTED;
923c2c66affSColin Finck Status = STATUS_NOT_IMPLEMENTED;
924c2c66affSColin Finck }
925c2c66affSColin Finck
926c2c66affSColin Finck Cleanup:
927c2c66affSColin Finck /* Leave the critical section */
928c2c66affSColin Finck RtlLeaveCriticalSection(CriticalSection);
929c2c66affSColin Finck
930c2c66affSColin Finck return Status;
931c2c66affSColin Finck }
932c2c66affSColin Finck
CSR_API(BaseSrvGetNextVDMCommand)933c2c66affSColin Finck CSR_API(BaseSrvGetNextVDMCommand)
934c2c66affSColin Finck {
935c2c66affSColin Finck NTSTATUS Status;
936c2c66affSColin Finck PBASE_GET_NEXT_VDM_COMMAND GetNextVdmCommandRequest =
937c2c66affSColin Finck &((PBASE_API_MESSAGE)ApiMessage)->Data.GetNextVDMCommandRequest;
938c2c66affSColin Finck PRTL_CRITICAL_SECTION CriticalSection;
939c2c66affSColin Finck PLIST_ENTRY i = NULL;
940c2c66affSColin Finck PVDM_CONSOLE_RECORD ConsoleRecord = NULL;
941c2c66affSColin Finck PVDM_DOS_RECORD DosRecord = NULL;
942c2c66affSColin Finck
943c2c66affSColin Finck /* Validate the message buffers */
944c2c66affSColin Finck if (!CsrValidateMessageBuffer(ApiMessage,
945c2c66affSColin Finck (PVOID*)&GetNextVdmCommandRequest->CmdLine,
946c2c66affSColin Finck GetNextVdmCommandRequest->CmdLen,
947c2c66affSColin Finck sizeof(*GetNextVdmCommandRequest->CmdLine))
948c2c66affSColin Finck || !CsrValidateMessageBuffer(ApiMessage,
949c2c66affSColin Finck (PVOID*)&GetNextVdmCommandRequest->AppName,
950c2c66affSColin Finck GetNextVdmCommandRequest->AppLen,
951c2c66affSColin Finck sizeof(*GetNextVdmCommandRequest->AppName))
952c2c66affSColin Finck || !CsrValidateMessageBuffer(ApiMessage,
953c2c66affSColin Finck (PVOID*)&GetNextVdmCommandRequest->PifFile,
954c2c66affSColin Finck GetNextVdmCommandRequest->PifLen,
955c2c66affSColin Finck sizeof(*GetNextVdmCommandRequest->PifFile))
956c2c66affSColin Finck || !CsrValidateMessageBuffer(ApiMessage,
957c2c66affSColin Finck (PVOID*)&GetNextVdmCommandRequest->CurDirectory,
958c2c66affSColin Finck GetNextVdmCommandRequest->CurDirectoryLen,
959c2c66affSColin Finck sizeof(*GetNextVdmCommandRequest->CurDirectory))
960c2c66affSColin Finck || !CsrValidateMessageBuffer(ApiMessage,
961c2c66affSColin Finck (PVOID*)&GetNextVdmCommandRequest->Env,
962c2c66affSColin Finck GetNextVdmCommandRequest->EnvLen,
963c2c66affSColin Finck sizeof(*GetNextVdmCommandRequest->Env))
964c2c66affSColin Finck || !CsrValidateMessageBuffer(ApiMessage,
965c2c66affSColin Finck (PVOID*)&GetNextVdmCommandRequest->Desktop,
966c2c66affSColin Finck GetNextVdmCommandRequest->DesktopLen,
967c2c66affSColin Finck sizeof(*GetNextVdmCommandRequest->Desktop))
968c2c66affSColin Finck || !CsrValidateMessageBuffer(ApiMessage,
969c2c66affSColin Finck (PVOID*)&GetNextVdmCommandRequest->Title,
970c2c66affSColin Finck GetNextVdmCommandRequest->TitleLen,
971c2c66affSColin Finck sizeof(*GetNextVdmCommandRequest->Title))
972c2c66affSColin Finck || !CsrValidateMessageBuffer(ApiMessage,
973c2c66affSColin Finck (PVOID*)&GetNextVdmCommandRequest->Reserved,
974c2c66affSColin Finck GetNextVdmCommandRequest->ReservedLen,
975c2c66affSColin Finck sizeof(*GetNextVdmCommandRequest->Reserved))
976c2c66affSColin Finck || !CsrValidateMessageBuffer(ApiMessage,
977c2c66affSColin Finck (PVOID*)&GetNextVdmCommandRequest->StartupInfo,
978c2c66affSColin Finck 1,
979c2c66affSColin Finck sizeof(STARTUPINFOA)))
980c2c66affSColin Finck {
981c2c66affSColin Finck return STATUS_INVALID_PARAMETER;
982c2c66affSColin Finck }
983c2c66affSColin Finck
984c2c66affSColin Finck CriticalSection = (GetNextVdmCommandRequest->VDMState & VDM_FLAG_WOW)
985c2c66affSColin Finck ? &WowCriticalSection
986c2c66affSColin Finck : &DosCriticalSection;
987c2c66affSColin Finck
988c2c66affSColin Finck /* Enter the critical section */
989c2c66affSColin Finck RtlEnterCriticalSection(CriticalSection);
990c2c66affSColin Finck
991c2c66affSColin Finck if (GetNextVdmCommandRequest->VDMState & VDM_FLAG_WOW)
992c2c66affSColin Finck {
993c2c66affSColin Finck // TODO: WOW SUPPORT NOT IMPLEMENTED
994c2c66affSColin Finck UNIMPLEMENTED;
995c2c66affSColin Finck Status = STATUS_NOT_IMPLEMENTED;
996c2c66affSColin Finck goto Cleanup;
997c2c66affSColin Finck }
998c2c66affSColin Finck // else if (!(GetNextVdmCommandRequest->VDMState & VDM_FLAG_WOW))
999c2c66affSColin Finck {
1000c2c66affSColin Finck if (GetNextVdmCommandRequest->iTask != 0)
1001c2c66affSColin Finck {
1002c2c66affSColin Finck /* Get the console record using the task ID */
1003c2c66affSColin Finck Status = GetConsoleRecordBySessionId(GetNextVdmCommandRequest->iTask,
1004c2c66affSColin Finck &ConsoleRecord);
1005c2c66affSColin Finck }
1006c2c66affSColin Finck else
1007c2c66affSColin Finck {
1008c2c66affSColin Finck /* Get the console record using the console handle */
1009c2c66affSColin Finck Status = BaseSrvGetConsoleRecord(GetNextVdmCommandRequest->ConsoleHandle,
1010c2c66affSColin Finck &ConsoleRecord);
1011c2c66affSColin Finck }
1012c2c66affSColin Finck
1013c2c66affSColin Finck /* Make sure we found the console record */
1014c2c66affSColin Finck if (!NT_SUCCESS(Status)) goto Cleanup;
1015c2c66affSColin Finck
1016c2c66affSColin Finck /* Return the session ID */
1017c2c66affSColin Finck GetNextVdmCommandRequest->iTask = ConsoleRecord->SessionId;
1018c2c66affSColin Finck GetNextVdmCommandRequest->WaitObjectForVDM = NULL;
1019c2c66affSColin Finck
1020c2c66affSColin Finck if (GetNextVdmCommandRequest->VDMState & VDM_GET_FIRST_COMMAND)
1021c2c66affSColin Finck {
1022c2c66affSColin Finck /* Check if the DOS record list is empty */
1023c2c66affSColin Finck if (ConsoleRecord->DosListHead.Flink == &ConsoleRecord->DosListHead)
1024c2c66affSColin Finck {
1025c2c66affSColin Finck Status = STATUS_INVALID_PARAMETER;
1026c2c66affSColin Finck goto Cleanup;
1027c2c66affSColin Finck }
1028c2c66affSColin Finck
1029c2c66affSColin Finck /* Get the first DOS record */
1030c2c66affSColin Finck DosRecord = CONTAINING_RECORD(ConsoleRecord->DosListHead.Flink, VDM_DOS_RECORD, Entry);
1031c2c66affSColin Finck
1032c2c66affSColin Finck /* Make sure its command information is still there */
1033c2c66affSColin Finck if (DosRecord->CommandInfo == NULL)
1034c2c66affSColin Finck {
1035c2c66affSColin Finck Status = STATUS_INVALID_PARAMETER;
1036c2c66affSColin Finck goto Cleanup;
1037c2c66affSColin Finck }
1038c2c66affSColin Finck
1039c2c66affSColin Finck /* Check if the console handle hasn't been set yet */
1040c2c66affSColin Finck if (ConsoleRecord->ConsoleHandle == NULL)
1041c2c66affSColin Finck {
1042c2c66affSColin Finck /* Set it now */
1043c2c66affSColin Finck ConsoleRecord->ConsoleHandle = GetNextVdmCommandRequest->ConsoleHandle;
1044c2c66affSColin Finck }
1045c2c66affSColin Finck
1046c2c66affSColin Finck /* Fill the command information */
1047c2c66affSColin Finck Status = BaseSrvFillCommandInfo(DosRecord->CommandInfo, GetNextVdmCommandRequest);
1048c2c66affSColin Finck goto Cleanup;
1049c2c66affSColin Finck }
1050c2c66affSColin Finck
1051c2c66affSColin Finck /* Check if we should set the state of a running DOS record to ready */
1052c2c66affSColin Finck if (!(GetNextVdmCommandRequest->VDMState
1053c2c66affSColin Finck & (VDM_FLAG_FIRST_TASK | VDM_FLAG_RETRY | VDM_FLAG_NESTED_TASK)))
1054c2c66affSColin Finck {
1055c2c66affSColin Finck /* Search for a DOS record that is currently running */
1056c2c66affSColin Finck for (i = ConsoleRecord->DosListHead.Flink; i != &ConsoleRecord->DosListHead; i = i->Flink)
1057c2c66affSColin Finck {
1058c2c66affSColin Finck DosRecord = CONTAINING_RECORD(i, VDM_DOS_RECORD, Entry);
1059c2c66affSColin Finck if (DosRecord->State == VDM_NOT_READY) break;
1060c2c66affSColin Finck }
1061c2c66affSColin Finck
1062c2c66affSColin Finck /* Check if we found any */
1063c2c66affSColin Finck if (i == &ConsoleRecord->DosListHead)
1064c2c66affSColin Finck {
1065c2c66affSColin Finck Status = STATUS_INVALID_PARAMETER;
1066c2c66affSColin Finck goto Cleanup;
1067c2c66affSColin Finck }
1068c2c66affSColin Finck
1069c2c66affSColin Finck /* Set the exit code */
1070c2c66affSColin Finck DosRecord->ExitCode = GetNextVdmCommandRequest->ExitCode;
1071c2c66affSColin Finck
1072c2c66affSColin Finck /* Update the VDM state */
1073c2c66affSColin Finck DosRecord->State = VDM_READY;
1074c2c66affSColin Finck
1075c2c66affSColin Finck /* Notify all waiting threads that the task is finished */
1076c2c66affSColin Finck NtSetEvent(DosRecord->ServerEvent, NULL);
1077c2c66affSColin Finck NtClose(DosRecord->ServerEvent);
1078c2c66affSColin Finck DosRecord->ServerEvent = NULL;
1079c2c66affSColin Finck }
1080c2c66affSColin Finck
1081c2c66affSColin Finck /* Search for a DOS record that is currently running and has command information */
1082c2c66affSColin Finck for (i = ConsoleRecord->DosListHead.Flink; i != &ConsoleRecord->DosListHead; i = i->Flink)
1083c2c66affSColin Finck {
1084c2c66affSColin Finck DosRecord = CONTAINING_RECORD(i, VDM_DOS_RECORD, Entry);
1085c2c66affSColin Finck if ((DosRecord->State == VDM_NOT_READY) && (DosRecord->CommandInfo != NULL)) break;
1086c2c66affSColin Finck }
1087c2c66affSColin Finck
1088c2c66affSColin Finck /* Check if we found any */
1089c2c66affSColin Finck if (i != &ConsoleRecord->DosListHead)
1090c2c66affSColin Finck {
1091c2c66affSColin Finck ASSERT(DosRecord->CommandInfo != NULL);
1092c2c66affSColin Finck
1093c2c66affSColin Finck /* Check if the caller only wants environment data */
1094c2c66affSColin Finck if (GetNextVdmCommandRequest->VDMState & VDM_GET_ENVIRONMENT)
1095c2c66affSColin Finck {
1096c2c66affSColin Finck if (GetNextVdmCommandRequest->EnvLen < DosRecord->CommandInfo->EnvLen)
1097c2c66affSColin Finck {
1098c2c66affSColin Finck /* Not enough space was reserved */
1099c2c66affSColin Finck GetNextVdmCommandRequest->EnvLen = DosRecord->CommandInfo->EnvLen;
1100c2c66affSColin Finck Status = STATUS_BUFFER_OVERFLOW;
1101c2c66affSColin Finck goto Cleanup;
1102c2c66affSColin Finck }
1103c2c66affSColin Finck
1104c2c66affSColin Finck /* Copy the environment data */
1105c2c66affSColin Finck RtlMoveMemory(GetNextVdmCommandRequest->Env,
1106c2c66affSColin Finck DosRecord->CommandInfo->Env,
1107c2c66affSColin Finck DosRecord->CommandInfo->EnvLen);
1108c2c66affSColin Finck
1109c2c66affSColin Finck /* Return the actual size to the caller */
1110c2c66affSColin Finck GetNextVdmCommandRequest->EnvLen = DosRecord->CommandInfo->EnvLen;
1111c2c66affSColin Finck }
1112c2c66affSColin Finck else
1113c2c66affSColin Finck {
1114c2c66affSColin Finck /* Fill the command information */
1115c2c66affSColin Finck Status = BaseSrvFillCommandInfo(DosRecord->CommandInfo, GetNextVdmCommandRequest);
1116c2c66affSColin Finck if (!NT_SUCCESS(Status)) goto Cleanup;
1117c2c66affSColin Finck
1118c2c66affSColin Finck /* Free the command information, it's no longer needed */
1119c2c66affSColin Finck BaseSrvFreeVDMInfo(DosRecord->CommandInfo);
1120c2c66affSColin Finck DosRecord->CommandInfo = NULL;
1121c2c66affSColin Finck
1122c2c66affSColin Finck /* Update the VDM state */
1123c2c66affSColin Finck DosRecord->State = VDM_NOT_READY;
1124c2c66affSColin Finck }
1125c2c66affSColin Finck
1126c2c66affSColin Finck Status = STATUS_SUCCESS;
1127c2c66affSColin Finck goto Cleanup;
1128c2c66affSColin Finck }
1129c2c66affSColin Finck }
1130c2c66affSColin Finck
1131c2c66affSColin Finck GetNextVdmCommandRequest->WaitObjectForVDM = NULL;
1132c2c66affSColin Finck
1133c2c66affSColin Finck /*
1134c2c66affSColin Finck * There is no command yet. Prepare for waiting if we asked so,
1135c2c66affSColin Finck * and if we were not retrying a request.
1136c2c66affSColin Finck */
1137c2c66affSColin Finck if (!(GetNextVdmCommandRequest->VDMState & VDM_FLAG_DONT_WAIT) ||
1138c2c66affSColin Finck !(GetNextVdmCommandRequest->VDMState & VDM_FLAG_RETRY))
1139c2c66affSColin Finck {
1140c2c66affSColin Finck if (ConsoleRecord->ServerEvent)
1141c2c66affSColin Finck {
1142c2c66affSColin Finck /* Reset the event */
1143c2c66affSColin Finck NtResetEvent(ConsoleRecord->ServerEvent, NULL);
1144c2c66affSColin Finck }
1145c2c66affSColin Finck else
1146c2c66affSColin Finck {
1147c2c66affSColin Finck /* Create a pair of wait handles */
1148c2c66affSColin Finck Status = BaseSrvCreatePairWaitHandles(&ConsoleRecord->ServerEvent,
1149c2c66affSColin Finck &ConsoleRecord->ClientEvent);
1150c2c66affSColin Finck if (!NT_SUCCESS(Status)) goto Cleanup;
1151c2c66affSColin Finck }
1152c2c66affSColin Finck
1153c2c66affSColin Finck /* Return the client event handle */
1154c2c66affSColin Finck GetNextVdmCommandRequest->WaitObjectForVDM = ConsoleRecord->ClientEvent;
1155c2c66affSColin Finck }
1156c2c66affSColin Finck
1157c2c66affSColin Finck Cleanup:
1158c2c66affSColin Finck /* Leave the critical section */
1159c2c66affSColin Finck RtlLeaveCriticalSection(CriticalSection);
1160c2c66affSColin Finck
1161c2c66affSColin Finck return Status;
1162c2c66affSColin Finck }
1163c2c66affSColin Finck
CSR_API(BaseSrvExitVDM)1164c2c66affSColin Finck CSR_API(BaseSrvExitVDM)
1165c2c66affSColin Finck {
1166c2c66affSColin Finck NTSTATUS Status;
1167c2c66affSColin Finck PBASE_EXIT_VDM ExitVdmRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.ExitVDMRequest;
1168c2c66affSColin Finck PRTL_CRITICAL_SECTION CriticalSection = NULL;
1169c2c66affSColin Finck PVDM_CONSOLE_RECORD ConsoleRecord = NULL;
1170c2c66affSColin Finck PVDM_DOS_RECORD DosRecord;
1171c2c66affSColin Finck
1172c2c66affSColin Finck CriticalSection = (ExitVdmRequest->iWowTask == 0)
1173c2c66affSColin Finck ? &DosCriticalSection
1174c2c66affSColin Finck : &WowCriticalSection;
1175c2c66affSColin Finck
1176c2c66affSColin Finck /* Enter the critical section */
1177c2c66affSColin Finck RtlEnterCriticalSection(CriticalSection);
1178c2c66affSColin Finck
1179c2c66affSColin Finck if (ExitVdmRequest->iWowTask == 0)
1180c2c66affSColin Finck {
1181c2c66affSColin Finck /* Get the console record */
1182c2c66affSColin Finck Status = BaseSrvGetConsoleRecord(ExitVdmRequest->ConsoleHandle, &ConsoleRecord);
1183c2c66affSColin Finck if (!NT_SUCCESS(Status)) goto Cleanup;
1184c2c66affSColin Finck
1185c2c66affSColin Finck if (ConsoleRecord->ServerEvent)
1186c2c66affSColin Finck ExitVdmRequest->WaitObjectForVDM = ConsoleRecord->ClientEvent;
1187c2c66affSColin Finck
1188c2c66affSColin Finck // NOTE: The following is the same as in BaseSrvCleanupVDMResources.
1189c2c66affSColin Finck
1190c2c66affSColin Finck if (ConsoleRecord->ServerEvent)
1191c2c66affSColin Finck {
1192c2c66affSColin Finck NtClose(ConsoleRecord->ServerEvent);
1193c2c66affSColin Finck ConsoleRecord->ServerEvent = NULL;
1194c2c66affSColin Finck }
1195c2c66affSColin Finck
1196c2c66affSColin Finck /* Cleanup the DOS records */
1197c2c66affSColin Finck while (!IsListEmpty(&ConsoleRecord->DosListHead))
1198c2c66affSColin Finck {
1199c2c66affSColin Finck DosRecord = CONTAINING_RECORD(ConsoleRecord->DosListHead.Flink,
1200c2c66affSColin Finck VDM_DOS_RECORD, Entry);
1201c2c66affSColin Finck
1202c2c66affSColin Finck /* Set the event and close it */
1203c2c66affSColin Finck if (DosRecord->ServerEvent)
1204c2c66affSColin Finck {
1205c2c66affSColin Finck NtSetEvent(DosRecord->ServerEvent, NULL);
1206c2c66affSColin Finck NtClose(DosRecord->ServerEvent);
1207c2c66affSColin Finck DosRecord->ServerEvent = NULL;
1208c2c66affSColin Finck }
1209c2c66affSColin Finck
1210c2c66affSColin Finck /* Remove the DOS entry */
1211c2c66affSColin Finck if (DosRecord->CommandInfo) BaseSrvFreeVDMInfo(DosRecord->CommandInfo);
1212c2c66affSColin Finck RemoveEntryList(&DosRecord->Entry);
1213c2c66affSColin Finck RtlFreeHeap(BaseSrvHeap, 0, DosRecord);
1214c2c66affSColin Finck }
1215c2c66affSColin Finck
1216c2c66affSColin Finck /* Remove the console record */
1217c2c66affSColin Finck RemoveEntryList(&ConsoleRecord->Entry);
1218c2c66affSColin Finck BaseSrvDestroyConsoleRecord(ConsoleRecord);
1219c2c66affSColin Finck }
1220c2c66affSColin Finck else
1221c2c66affSColin Finck {
1222c2c66affSColin Finck // TODO: NOT IMPLEMENTED
1223c2c66affSColin Finck UNIMPLEMENTED;
1224c2c66affSColin Finck Status = STATUS_NOT_IMPLEMENTED;
1225c2c66affSColin Finck }
1226c2c66affSColin Finck
1227c2c66affSColin Finck Cleanup:
1228c2c66affSColin Finck /* Leave the critical section */
1229c2c66affSColin Finck RtlLeaveCriticalSection(CriticalSection);
1230c2c66affSColin Finck
1231c2c66affSColin Finck return Status;
1232c2c66affSColin Finck }
1233c2c66affSColin Finck
CSR_API(BaseSrvIsFirstVDM)1234c2c66affSColin Finck CSR_API(BaseSrvIsFirstVDM)
1235c2c66affSColin Finck {
1236c2c66affSColin Finck PBASE_IS_FIRST_VDM IsFirstVDMRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.IsFirstVDMRequest;
1237c2c66affSColin Finck
1238c2c66affSColin Finck /* Return the result */
1239c2c66affSColin Finck IsFirstVDMRequest->FirstVDM = FirstVDM;
1240c2c66affSColin Finck
1241c2c66affSColin Finck /* Clear the first VDM flag */
1242c2c66affSColin Finck FirstVDM = FALSE;
1243c2c66affSColin Finck
1244c2c66affSColin Finck return STATUS_SUCCESS;
1245c2c66affSColin Finck }
1246c2c66affSColin Finck
CSR_API(BaseSrvGetVDMExitCode)1247c2c66affSColin Finck CSR_API(BaseSrvGetVDMExitCode)
1248c2c66affSColin Finck {
1249c2c66affSColin Finck NTSTATUS Status;
1250c2c66affSColin Finck PBASE_GET_VDM_EXIT_CODE GetVDMExitCodeRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.GetVDMExitCodeRequest;
1251c2c66affSColin Finck PLIST_ENTRY i = NULL;
1252c2c66affSColin Finck PVDM_CONSOLE_RECORD ConsoleRecord = NULL;
1253c2c66affSColin Finck PVDM_DOS_RECORD DosRecord = NULL;
1254c2c66affSColin Finck
1255c2c66affSColin Finck /* Enter the critical section */
1256c2c66affSColin Finck RtlEnterCriticalSection(&DosCriticalSection);
1257c2c66affSColin Finck
1258c2c66affSColin Finck /* Get the console record */
1259c2c66affSColin Finck Status = BaseSrvGetConsoleRecord(GetVDMExitCodeRequest->ConsoleHandle, &ConsoleRecord);
1260c2c66affSColin Finck if (!NT_SUCCESS(Status)) goto Cleanup;
1261c2c66affSColin Finck
1262c2c66affSColin Finck /* Search for a DOS record that has the same parent process handle */
1263c2c66affSColin Finck for (i = ConsoleRecord->DosListHead.Flink; i != &ConsoleRecord->DosListHead; i = i->Flink)
1264c2c66affSColin Finck {
1265c2c66affSColin Finck DosRecord = CONTAINING_RECORD(i, VDM_DOS_RECORD, Entry);
1266c2c66affSColin Finck if (DosRecord->ClientEvent == GetVDMExitCodeRequest->hParent) break;
1267c2c66affSColin Finck }
1268c2c66affSColin Finck
1269c2c66affSColin Finck /* Check if no DOS record was found */
1270c2c66affSColin Finck if (i == &ConsoleRecord->DosListHead)
1271c2c66affSColin Finck {
1272c2c66affSColin Finck Status = STATUS_NOT_FOUND;
1273c2c66affSColin Finck goto Cleanup;
1274c2c66affSColin Finck }
1275c2c66affSColin Finck
1276c2c66affSColin Finck /* Check if this task is still running */
1277c2c66affSColin Finck if (DosRecord->State != VDM_READY)
1278c2c66affSColin Finck {
1279c2c66affSColin Finck GetVDMExitCodeRequest->ExitCode = STATUS_PENDING;
1280c2c66affSColin Finck goto Cleanup;
1281c2c66affSColin Finck }
1282c2c66affSColin Finck
1283c2c66affSColin Finck /* Return the exit code */
1284c2c66affSColin Finck GetVDMExitCodeRequest->ExitCode = DosRecord->ExitCode;
1285c2c66affSColin Finck
1286c2c66affSColin Finck // FIXME: We may just change DosRecord->State to VDM_READY in some cases...
1287c2c66affSColin Finck
1288c2c66affSColin Finck /* Since this is a zombie task record, remove it */
1289c2c66affSColin Finck if (DosRecord->CommandInfo) BaseSrvFreeVDMInfo(DosRecord->CommandInfo);
1290c2c66affSColin Finck RemoveEntryList(&DosRecord->Entry);
1291c2c66affSColin Finck RtlFreeHeap(BaseSrvHeap, 0, DosRecord);
1292c2c66affSColin Finck
1293c2c66affSColin Finck Cleanup:
1294c2c66affSColin Finck /* Leave the critical section */
1295c2c66affSColin Finck RtlLeaveCriticalSection(&DosCriticalSection);
1296c2c66affSColin Finck
1297c2c66affSColin Finck return Status;
1298c2c66affSColin Finck }
1299c2c66affSColin Finck
CSR_API(BaseSrvSetReenterCount)1300c2c66affSColin Finck CSR_API(BaseSrvSetReenterCount)
1301c2c66affSColin Finck {
1302c2c66affSColin Finck NTSTATUS Status = STATUS_SUCCESS;
1303c2c66affSColin Finck PBASE_SET_REENTER_COUNT SetReenterCountRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.SetReenterCountRequest;
1304c2c66affSColin Finck PVDM_CONSOLE_RECORD ConsoleRecord;
1305c2c66affSColin Finck
1306c2c66affSColin Finck /* Enter the critical section */
1307c2c66affSColin Finck RtlEnterCriticalSection(&DosCriticalSection);
1308c2c66affSColin Finck
1309c2c66affSColin Finck /* Get the console record */
1310c2c66affSColin Finck Status = BaseSrvGetConsoleRecord(SetReenterCountRequest->ConsoleHandle, &ConsoleRecord);
1311c2c66affSColin Finck if (!NT_SUCCESS(Status)) goto Cleanup;
1312c2c66affSColin Finck
1313c2c66affSColin Finck if (SetReenterCountRequest->fIncDec == VDM_INC_REENTER_COUNT)
1314c2c66affSColin Finck {
1315c2c66affSColin Finck ConsoleRecord->ReenterCount++;
1316c2c66affSColin Finck }
1317c2c66affSColin Finck else if (SetReenterCountRequest->fIncDec == VDM_DEC_REENTER_COUNT)
1318c2c66affSColin Finck {
1319c2c66affSColin Finck ConsoleRecord->ReenterCount--;
1320c2c66affSColin Finck if (ConsoleRecord->ServerEvent)
1321c2c66affSColin Finck NtSetEvent(ConsoleRecord->ServerEvent, NULL);
1322c2c66affSColin Finck }
1323c2c66affSColin Finck else
1324c2c66affSColin Finck {
1325c2c66affSColin Finck Status = STATUS_INVALID_PARAMETER;
1326c2c66affSColin Finck }
1327c2c66affSColin Finck
1328c2c66affSColin Finck Cleanup:
1329c2c66affSColin Finck /* Leave the critical section */
1330c2c66affSColin Finck RtlLeaveCriticalSection(&DosCriticalSection);
1331c2c66affSColin Finck
1332c2c66affSColin Finck return Status;
1333c2c66affSColin Finck }
1334c2c66affSColin Finck
CSR_API(BaseSrvSetVDMCurDirs)1335c2c66affSColin Finck CSR_API(BaseSrvSetVDMCurDirs)
1336c2c66affSColin Finck {
1337c2c66affSColin Finck NTSTATUS Status;
1338c2c66affSColin Finck PBASE_GETSET_VDM_CURDIRS VDMCurrentDirsRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.VDMCurrentDirsRequest;
1339c2c66affSColin Finck PVDM_CONSOLE_RECORD ConsoleRecord;
1340c2c66affSColin Finck PCHAR Buffer = NULL;
1341c2c66affSColin Finck
1342c2c66affSColin Finck /* Validate the input buffer */
1343c2c66affSColin Finck if (!CsrValidateMessageBuffer(ApiMessage,
1344c2c66affSColin Finck (PVOID*)&VDMCurrentDirsRequest->lpszzCurDirs,
1345c2c66affSColin Finck VDMCurrentDirsRequest->cchCurDirs,
1346c2c66affSColin Finck sizeof(*VDMCurrentDirsRequest->lpszzCurDirs)))
1347c2c66affSColin Finck {
1348c2c66affSColin Finck return STATUS_INVALID_PARAMETER;
1349c2c66affSColin Finck }
1350c2c66affSColin Finck
1351c2c66affSColin Finck /* Enter the critical section */
1352c2c66affSColin Finck RtlEnterCriticalSection(&DosCriticalSection);
1353c2c66affSColin Finck
1354c2c66affSColin Finck /* Find the console record */
1355c2c66affSColin Finck Status = BaseSrvGetConsoleRecord(VDMCurrentDirsRequest->ConsoleHandle, &ConsoleRecord);
1356c2c66affSColin Finck if (!NT_SUCCESS(Status)) goto Cleanup;
1357c2c66affSColin Finck
1358c2c66affSColin Finck if (ConsoleRecord->CurrentDirs == NULL)
1359c2c66affSColin Finck {
1360c2c66affSColin Finck /* Allocate memory for the current directory information */
1361c2c66affSColin Finck Buffer = RtlAllocateHeap(BaseSrvHeap,
1362c2c66affSColin Finck HEAP_ZERO_MEMORY,
1363c2c66affSColin Finck VDMCurrentDirsRequest->cchCurDirs);
1364c2c66affSColin Finck }
1365c2c66affSColin Finck else
1366c2c66affSColin Finck {
1367c2c66affSColin Finck /* Resize the amount of allocated memory */
1368c2c66affSColin Finck Buffer = RtlReAllocateHeap(BaseSrvHeap,
1369c2c66affSColin Finck HEAP_ZERO_MEMORY,
1370c2c66affSColin Finck ConsoleRecord->CurrentDirs,
1371c2c66affSColin Finck VDMCurrentDirsRequest->cchCurDirs);
1372c2c66affSColin Finck }
1373c2c66affSColin Finck
1374c2c66affSColin Finck if (Buffer == NULL)
1375c2c66affSColin Finck {
1376c2c66affSColin Finck /* Allocation failed */
1377c2c66affSColin Finck Status = STATUS_NO_MEMORY;
1378c2c66affSColin Finck goto Cleanup;
1379c2c66affSColin Finck }
1380c2c66affSColin Finck
1381c2c66affSColin Finck /* Update the console record */
1382c2c66affSColin Finck ConsoleRecord->CurrentDirs = Buffer;
1383c2c66affSColin Finck ConsoleRecord->CurDirsLength = VDMCurrentDirsRequest->cchCurDirs;
1384c2c66affSColin Finck
1385c2c66affSColin Finck /* Copy the data */
1386c2c66affSColin Finck RtlMoveMemory(ConsoleRecord->CurrentDirs,
1387c2c66affSColin Finck VDMCurrentDirsRequest->lpszzCurDirs,
1388c2c66affSColin Finck VDMCurrentDirsRequest->cchCurDirs);
1389c2c66affSColin Finck
1390c2c66affSColin Finck Cleanup:
1391c2c66affSColin Finck /* Leave the critical section */
1392c2c66affSColin Finck RtlLeaveCriticalSection(&DosCriticalSection);
1393c2c66affSColin Finck
1394c2c66affSColin Finck return Status;
1395c2c66affSColin Finck }
1396c2c66affSColin Finck
CSR_API(BaseSrvGetVDMCurDirs)1397c2c66affSColin Finck CSR_API(BaseSrvGetVDMCurDirs)
1398c2c66affSColin Finck {
1399c2c66affSColin Finck NTSTATUS Status;
1400c2c66affSColin Finck PBASE_GETSET_VDM_CURDIRS VDMCurrentDirsRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.VDMCurrentDirsRequest;
1401c2c66affSColin Finck PVDM_CONSOLE_RECORD ConsoleRecord;
1402c2c66affSColin Finck
1403c2c66affSColin Finck /* Validate the output buffer */
1404c2c66affSColin Finck if (!CsrValidateMessageBuffer(ApiMessage,
1405c2c66affSColin Finck (PVOID*)&VDMCurrentDirsRequest->lpszzCurDirs,
1406c2c66affSColin Finck VDMCurrentDirsRequest->cchCurDirs,
1407c2c66affSColin Finck sizeof(*VDMCurrentDirsRequest->lpszzCurDirs)))
1408c2c66affSColin Finck {
1409c2c66affSColin Finck return STATUS_INVALID_PARAMETER;
1410c2c66affSColin Finck }
1411c2c66affSColin Finck
1412c2c66affSColin Finck /* Enter the critical section */
1413c2c66affSColin Finck RtlEnterCriticalSection(&DosCriticalSection);
1414c2c66affSColin Finck
1415c2c66affSColin Finck /* Find the console record */
1416c2c66affSColin Finck Status = BaseSrvGetConsoleRecord(VDMCurrentDirsRequest->ConsoleHandle, &ConsoleRecord);
1417c2c66affSColin Finck if (!NT_SUCCESS(Status)) goto Cleanup;
1418c2c66affSColin Finck
1419c2c66affSColin Finck /* Return the actual size of the current directory information */
1420c2c66affSColin Finck VDMCurrentDirsRequest->cchCurDirs = ConsoleRecord->CurDirsLength;
1421c2c66affSColin Finck
1422c2c66affSColin Finck /* Check if the buffer is large enough */
1423c2c66affSColin Finck if (VDMCurrentDirsRequest->cchCurDirs < ConsoleRecord->CurDirsLength)
1424c2c66affSColin Finck {
1425c2c66affSColin Finck Status = STATUS_BUFFER_TOO_SMALL;
1426c2c66affSColin Finck goto Cleanup;
1427c2c66affSColin Finck }
1428c2c66affSColin Finck
1429c2c66affSColin Finck /* Copy the data */
1430c2c66affSColin Finck RtlMoveMemory(VDMCurrentDirsRequest->lpszzCurDirs,
1431c2c66affSColin Finck ConsoleRecord->CurrentDirs,
1432c2c66affSColin Finck ConsoleRecord->CurDirsLength);
1433c2c66affSColin Finck
1434c2c66affSColin Finck Cleanup:
1435c2c66affSColin Finck /* Leave the critical section */
1436c2c66affSColin Finck RtlLeaveCriticalSection(&DosCriticalSection);
1437c2c66affSColin Finck
1438c2c66affSColin Finck return Status;
1439c2c66affSColin Finck }
1440c2c66affSColin Finck
CSR_API(BaseSrvBatNotification)1441c2c66affSColin Finck CSR_API(BaseSrvBatNotification)
1442c2c66affSColin Finck {
1443c2c66affSColin Finck DPRINT1("%s not yet implemented\n", __FUNCTION__);
1444c2c66affSColin Finck return STATUS_NOT_IMPLEMENTED;
1445c2c66affSColin Finck }
1446c2c66affSColin Finck
CSR_API(BaseSrvRegisterWowExec)1447c2c66affSColin Finck CSR_API(BaseSrvRegisterWowExec)
1448c2c66affSColin Finck {
1449c2c66affSColin Finck DPRINT1("%s not yet implemented\n", __FUNCTION__);
1450c2c66affSColin Finck return STATUS_NOT_IMPLEMENTED;
1451c2c66affSColin Finck }
1452c2c66affSColin Finck
CSR_API(BaseSrvRefreshIniFileMapping)1453c2c66affSColin Finck CSR_API(BaseSrvRefreshIniFileMapping)
1454c2c66affSColin Finck {
1455c2c66affSColin Finck DPRINT1("%s not yet implemented\n", __FUNCTION__);
1456c2c66affSColin Finck return STATUS_NOT_IMPLEMENTED;
1457c2c66affSColin Finck }
1458c2c66affSColin Finck
1459c2c66affSColin Finck /* EOF */
1460