[BASESRV]
[reactos.git] / subsystems / win / basesrv / vdm.c
index 82e2d24..ba6866c 100644 (file)
@@ -36,6 +36,28 @@ NTSTATUS NTAPI BaseSrvGetConsoleRecord(HANDLE ConsoleHandle, PVDM_CONSOLE_RECORD
         if (CurrentRecord->ConsoleHandle == ConsoleHandle) break;
     }
 
+    /* Check if nothing was found */
+    if (i == &VDMConsoleListHead) CurrentRecord = NULL;
+
+    *Record = CurrentRecord;
+    return CurrentRecord ? STATUS_SUCCESS : STATUS_NOT_FOUND;
+}
+
+NTSTATUS NTAPI GetConsoleRecordBySessionId(ULONG TaskId, PVDM_CONSOLE_RECORD *Record)
+{
+    PLIST_ENTRY i;
+    PVDM_CONSOLE_RECORD CurrentRecord = NULL;
+
+    /* Search for a record that has the same console handle */
+    for (i = VDMConsoleListHead.Flink; i != &VDMConsoleListHead; i = i->Flink)
+    {
+        CurrentRecord = CONTAINING_RECORD(i, VDM_CONSOLE_RECORD, Entry);
+        if (CurrentRecord->SessionId == TaskId) break;
+    }
+
+    /* Check if nothing was found */
+    if (i == &VDMConsoleListHead) CurrentRecord = NULL;
+
     *Record = CurrentRecord;
     return CurrentRecord ? STATUS_SUCCESS : STATUS_NOT_FOUND;
 }
@@ -69,6 +91,437 @@ ULONG NTAPI GetNextDosSesId(VOID)
     return SessionId;
 }
 
+BOOLEAN NTAPI BaseSrvIsVdmAllowed(VOID)
+{
+    NTSTATUS Status;
+    BOOLEAN VdmAllowed = TRUE;
+    HANDLE RootKey, KeyHandle;
+    UNICODE_STRING KeyName, ValueName, MachineKeyName;
+    OBJECT_ATTRIBUTES Attributes;
+    UCHAR ValueBuffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(ULONG)];
+    PKEY_VALUE_PARTIAL_INFORMATION ValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)ValueBuffer;
+    ULONG ActualSize;
+
+    /* Initialize the unicode strings */
+    RtlInitUnicodeString(&MachineKeyName, L"\\Registry\\Machine");
+    RtlInitUnicodeString(&KeyName, VDM_POLICY_KEY_NAME);
+    RtlInitUnicodeString(&ValueName, VDM_DISALLOWED_VALUE_NAME);
+
+    InitializeObjectAttributes(&Attributes,
+                               &MachineKeyName,
+                               OBJ_CASE_INSENSITIVE,
+                               NULL,
+                               NULL);
+
+    /* Open the local machine key */
+    Status = NtOpenKey(&RootKey, KEY_READ, &Attributes);
+    if (!NT_SUCCESS(Status)) return FALSE;
+
+    InitializeObjectAttributes(&Attributes,
+                               &KeyName,
+                               OBJ_CASE_INSENSITIVE,
+                               RootKey,
+                               NULL);
+
+    /* Open the policy key in the local machine hive, if it exists */
+    if (NT_SUCCESS(NtOpenKey(&KeyHandle, KEY_READ, &Attributes)))
+    {
+        /* Read the value, if it's set */
+        if (NT_SUCCESS(NtQueryValueKey(KeyHandle,
+                                       &ValueName,
+                                       KeyValuePartialInformation,
+                                       ValueInfo,
+                                       sizeof(ValueBuffer),
+                                       &ActualSize)))
+        {
+            if (*((PULONG)ValueInfo->Data))
+            {
+                /* The VDM has been disabled in the registry */
+                VdmAllowed = FALSE;
+            }
+        }
+
+        NtClose(KeyHandle);
+    }
+
+    /* Close the local machine key */
+    NtClose(RootKey);
+
+    /* If it's disabled system-wide, there's no need to check the user key */
+    if (!VdmAllowed) return FALSE;
+
+    /* Open the current user key of the client */
+    if (!CsrImpersonateClient(NULL)) return VdmAllowed;
+    Status = RtlOpenCurrentUser(KEY_READ, &RootKey);
+    CsrRevertToSelf();
+
+    /* If that fails, return the system-wide setting */
+    if (!NT_SUCCESS(Status)) return VdmAllowed;
+
+    InitializeObjectAttributes(&Attributes,
+                               &KeyName,
+                               OBJ_CASE_INSENSITIVE,
+                               RootKey,
+                               NULL);
+
+    /* Open the policy key in the current user hive, if it exists */
+    if (NT_SUCCESS(NtOpenKey(&KeyHandle, KEY_READ, &Attributes)))
+    {
+        /* Read the value, if it's set */
+        if (NT_SUCCESS(NtQueryValueKey(KeyHandle,
+                                       &ValueName,
+                                       KeyValuePartialInformation,
+                                       ValueInfo,
+                                       sizeof(ValueBuffer),
+                                       &ActualSize)))
+        {
+            if (*((PULONG)ValueInfo->Data))
+            {
+                /* The VDM has been disabled in the registry */
+                VdmAllowed = FALSE;
+            }
+        }
+
+        NtClose(KeyHandle);
+    }
+
+    return VdmAllowed;
+}
+
+NTSTATUS NTAPI BaseSrvCreatePairWaitHandles(PHANDLE ServerEvent, PHANDLE ClientEvent)
+{
+    NTSTATUS Status;
+
+    /* Create the event */
+    Status = NtCreateEvent(ServerEvent, EVENT_ALL_ACCESS, NULL, NotificationEvent, FALSE);
+    if (!NT_SUCCESS(Status)) return Status;
+
+    /* Duplicate the event into the client process */
+    Status = NtDuplicateObject(NtCurrentProcess(),
+                               *ServerEvent,
+                               CsrGetClientThread()->Process->ProcessHandle,
+                               ClientEvent,
+                               0,
+                               0,
+                               DUPLICATE_SAME_ATTRIBUTES | DUPLICATE_SAME_ACCESS);
+
+    if (!NT_SUCCESS(Status)) NtClose(*ServerEvent);
+    return Status;
+}
+
+VOID NTAPI BaseSrvFreeVDMInfo(PVDM_COMMAND_INFO CommandInfo)
+{
+    /* Free the allocated structure members */
+    if (CommandInfo->CmdLine != NULL) RtlFreeHeap(BaseSrvHeap, 0, CommandInfo->CmdLine);
+    if (CommandInfo->AppName != NULL) RtlFreeHeap(BaseSrvHeap, 0, CommandInfo->AppName);
+    if (CommandInfo->PifFile != NULL) RtlFreeHeap(BaseSrvHeap, 0, CommandInfo->PifFile);
+    if (CommandInfo->CurDirectory != NULL) RtlFreeHeap(BaseSrvHeap, 0, CommandInfo->CurDirectory);
+    if (CommandInfo->Env != NULL) RtlFreeHeap(BaseSrvHeap, 0, CommandInfo->Env);
+    if (CommandInfo->Desktop != NULL) RtlFreeHeap(BaseSrvHeap, 0, CommandInfo->Desktop);
+    if (CommandInfo->Title != NULL) RtlFreeHeap(BaseSrvHeap, 0, CommandInfo->Title);
+    if (CommandInfo->Reserved != NULL) RtlFreeHeap(BaseSrvHeap, 0, CommandInfo->Reserved);
+
+    /* Free the structure itself */
+    RtlFreeHeap(BaseSrvHeap, 0, CommandInfo);
+}
+
+VOID NTAPI BaseSrvCleanupVdmRecords(ULONG ProcessId)
+{
+    PLIST_ENTRY i;
+    PVDM_CONSOLE_RECORD ConsoleRecord = NULL;
+    PVDM_DOS_RECORD DosRecord;
+
+    /* Enter the critical section */
+    RtlEnterCriticalSection(&DosCriticalSection);
+
+    /* Search for a record that has the same process handle */
+    for (i = VDMConsoleListHead.Flink; i != &VDMConsoleListHead; i = i->Flink)
+    {
+        ConsoleRecord = CONTAINING_RECORD(i, VDM_CONSOLE_RECORD, Entry);
+
+        if (ConsoleRecord->ProcessId == ProcessId)
+        {
+            /* Cleanup the DOS records */
+            while (ConsoleRecord->DosListHead.Flink != &ConsoleRecord->DosListHead)
+            {
+                DosRecord = CONTAINING_RECORD(ConsoleRecord->DosListHead.Flink,
+                                              VDM_DOS_RECORD,
+                                              Entry);
+
+                /* Set the event and close it */
+                NtSetEvent(DosRecord->ServerEvent, NULL);
+                NtClose(DosRecord->ServerEvent);
+
+                /* Remove the DOS entry */
+                if (DosRecord->CommandInfo) BaseSrvFreeVDMInfo(DosRecord->CommandInfo);
+                RemoveEntryList(&DosRecord->Entry);
+                RtlFreeHeap(BaseSrvHeap, 0, DosRecord);
+            }
+
+            if (ConsoleRecord->CurrentDirs != NULL)
+            {
+                /* Free the current directories */
+                RtlFreeHeap(BaseSrvHeap, 0, ConsoleRecord->CurrentDirs);
+                ConsoleRecord->CurrentDirs = NULL;
+                ConsoleRecord->CurDirsLength = 0;
+            }
+
+            /* Close the event handle */
+            if (ConsoleRecord->ServerEvent) NtClose(ConsoleRecord->ServerEvent);
+
+            /* Remove the console record */
+            i = i->Blink;
+            RemoveEntryList(&ConsoleRecord->Entry);
+            RtlFreeHeap(BaseSrvHeap, 0, ConsoleRecord);
+        }
+    }
+
+    /* Leave the critical section */
+    RtlLeaveCriticalSection(&DosCriticalSection);
+}
+
+BOOLEAN NTAPI BaseSrvCopyCommand(PBASE_CHECK_VDM CheckVdmRequest, PVDM_DOS_RECORD DosRecord)
+{
+    BOOLEAN Success = FALSE;
+    PVDM_COMMAND_INFO CommandInfo = NULL;
+
+    /* Allocate the command information structure */
+    CommandInfo = (PVDM_COMMAND_INFO)RtlAllocateHeap(BaseSrvHeap,
+                                                     HEAP_ZERO_MEMORY,
+                                                     sizeof(VDM_COMMAND_INFO));
+    if (CommandInfo == NULL) return FALSE;
+
+    /* Fill the structure */
+    CommandInfo->TaskId = CheckVdmRequest->iTask;
+    CommandInfo->ExitCode = DosRecord->ExitCode;
+    CommandInfo->CodePage = CheckVdmRequest->CodePage;
+    CommandInfo->StdIn = CheckVdmRequest->StdIn;
+    CommandInfo->StdOut = CheckVdmRequest->StdOut;
+    CommandInfo->StdErr = CheckVdmRequest->StdErr;
+
+    /* Allocate memory for the command line */
+    CommandInfo->CmdLine = RtlAllocateHeap(BaseSrvHeap,
+                                           HEAP_ZERO_MEMORY,
+                                           CheckVdmRequest->CmdLen);
+    if (CommandInfo->CmdLine == NULL) goto Cleanup;
+
+    /* Copy the command line */
+    RtlMoveMemory(CommandInfo->CmdLine, CheckVdmRequest->CmdLine, CheckVdmRequest->CmdLen);
+
+    /* Allocate memory for the application name */
+    CommandInfo->AppName = RtlAllocateHeap(BaseSrvHeap,
+                                           HEAP_ZERO_MEMORY,
+                                           CheckVdmRequest->AppLen);
+    if (CommandInfo->AppName == NULL) goto Cleanup;
+
+    /* Copy the application name */
+    RtlMoveMemory(CommandInfo->AppName, CheckVdmRequest->AppName, CheckVdmRequest->AppLen);
+
+    /* Allocate memory for the PIF file name */
+    if (CheckVdmRequest->PifLen != 0)
+    {
+        CommandInfo->PifFile = RtlAllocateHeap(BaseSrvHeap,
+                                               HEAP_ZERO_MEMORY,
+                                               CheckVdmRequest->PifLen);
+        if (CommandInfo->PifFile == NULL) goto Cleanup;
+
+        /* Copy the PIF file name */
+        RtlMoveMemory(CommandInfo->PifFile, CheckVdmRequest->PifFile, CheckVdmRequest->PifLen);
+    }
+    else CommandInfo->PifFile = NULL;
+
+    /* Allocate memory for the current directory */
+    if (CheckVdmRequest->CurDirectoryLen != 0)
+    {
+        CommandInfo->CurDirectory = RtlAllocateHeap(BaseSrvHeap,
+                                                    HEAP_ZERO_MEMORY,
+                                                    CheckVdmRequest->CurDirectoryLen);
+        if (CommandInfo->CurDirectory == NULL) goto Cleanup;
+
+        /* Copy the current directory */
+        RtlMoveMemory(CommandInfo->CurDirectory,
+                      CheckVdmRequest->CurDirectory,
+                      CheckVdmRequest->CurDirectoryLen);
+    }
+    else CommandInfo->CurDirectory = NULL;
+
+    /* Allocate memory for the environment block */
+    CommandInfo->Env = RtlAllocateHeap(BaseSrvHeap,
+                                       HEAP_ZERO_MEMORY,
+                                       CheckVdmRequest->EnvLen);
+    if (CommandInfo->Env == NULL) goto Cleanup;
+
+    /* Copy the environment block */
+    RtlMoveMemory(CommandInfo->Env, CheckVdmRequest->Env, CheckVdmRequest->EnvLen);
+
+    CommandInfo->EnvLen = CheckVdmRequest->EnvLen;
+    RtlMoveMemory(&CommandInfo->StartupInfo,
+                  CheckVdmRequest->StartupInfo,
+                  sizeof(STARTUPINFOA));
+    /* Allocate memory for the desktop */
+    if (CheckVdmRequest->DesktopLen != 0)
+    {
+        CommandInfo->Desktop = RtlAllocateHeap(BaseSrvHeap,
+                                               HEAP_ZERO_MEMORY,
+                                               CheckVdmRequest->DesktopLen);
+        if (CommandInfo->Desktop == NULL) goto Cleanup;
+
+        /* Copy the desktop name */
+        RtlMoveMemory(CommandInfo->Desktop, CheckVdmRequest->Desktop, CheckVdmRequest->DesktopLen);
+    }
+    else CommandInfo->Desktop = NULL;
+
+    CommandInfo->DesktopLen = CheckVdmRequest->DesktopLen;
+
+    /* Allocate memory for the title */
+    if (CheckVdmRequest->TitleLen != 0)
+    {
+        CommandInfo->Title = RtlAllocateHeap(BaseSrvHeap,
+                                             HEAP_ZERO_MEMORY,
+                                             CheckVdmRequest->TitleLen);
+        if (CommandInfo->Title == NULL) goto Cleanup;
+
+        /* Copy the title */
+        RtlMoveMemory(CommandInfo->Title, CheckVdmRequest->Title, CheckVdmRequest->TitleLen);
+    }
+    else CommandInfo->Title = NULL;
+
+    CommandInfo->TitleLen = CheckVdmRequest->TitleLen;
+
+    /* Allocate memory for the reserved field */
+    if (CheckVdmRequest->ReservedLen != 0)
+    {
+        CommandInfo->Reserved = RtlAllocateHeap(BaseSrvHeap,
+                                                HEAP_ZERO_MEMORY,
+                                                CheckVdmRequest->ReservedLen);
+        if (CommandInfo->Reserved == NULL) goto Cleanup;
+
+        /* Copy the reserved field */
+        RtlMoveMemory(CommandInfo->Reserved,
+                      CheckVdmRequest->Reserved,
+                      CheckVdmRequest->ReservedLen);
+    }
+    else CommandInfo->Reserved = NULL;
+
+    CommandInfo->ReservedLen = CheckVdmRequest->ReservedLen;
+
+    CommandInfo->CmdLen = CheckVdmRequest->CmdLen;
+    CommandInfo->AppLen = CheckVdmRequest->AppLen;
+    CommandInfo->PifLen = CheckVdmRequest->PifLen;
+    CommandInfo->CurDirectoryLen = CheckVdmRequest->CurDirectoryLen;
+    CommandInfo->VDMState = DosRecord->State;
+    // TODO: Set CommandInfo->CurrentDrive
+    // TODO: Set CommandInfo->ComingFromBat
+
+    /* Set the DOS record's command structure */
+    DosRecord->CommandInfo = CommandInfo;
+
+    /* The operation was successful */
+    Success = TRUE;
+
+Cleanup:
+    /* If it wasn't successful, free the memory */
+    if (!Success) BaseSrvFreeVDMInfo(CommandInfo);
+
+    return Success;
+}
+
+NTSTATUS NTAPI BaseSrvFillCommandInfo(PVDM_COMMAND_INFO CommandInfo,
+                                      PBASE_GET_NEXT_VDM_COMMAND Message)
+{
+    /* Copy the data */
+    Message->iTask = CommandInfo->TaskId;
+    Message->StdIn = CommandInfo->StdIn;
+    Message->StdOut = CommandInfo->StdOut;
+    Message->StdErr = CommandInfo->StdErr;
+    Message->CodePage = CommandInfo->CodePage;
+    Message->dwCreationFlags = CommandInfo->CreationFlags;
+    Message->ExitCode = CommandInfo->ExitCode;
+    Message->CurrentDrive = CommandInfo->CurrentDrive;
+    Message->VDMState = CommandInfo->VDMState;
+    Message->fComingFromBat = CommandInfo->ComingFromBat;
+
+    if (CommandInfo->CmdLen && Message->CmdLen)
+    {
+        if (Message->CmdLen < CommandInfo->CmdLen) return STATUS_BUFFER_TOO_SMALL;
+
+        /* Copy the command line */
+        RtlMoveMemory(Message->CmdLine, CommandInfo->CmdLine, CommandInfo->CmdLen);
+        Message->CmdLen = CommandInfo->CmdLen;
+    }
+
+    if (CommandInfo->AppLen && Message->AppLen)
+    {
+        if (Message->AppLen < CommandInfo->CmdLen) return STATUS_BUFFER_TOO_SMALL;
+
+        /* Copy the application name */
+        RtlMoveMemory(Message->AppName, CommandInfo->AppName, CommandInfo->AppLen);
+        Message->AppLen = CommandInfo->AppLen;
+    }
+
+    if (CommandInfo->PifLen && Message->PifLen)
+    {
+        if (Message->PifLen < CommandInfo->PifLen) return STATUS_BUFFER_TOO_SMALL;
+
+        /* Copy the PIF file name */
+        RtlMoveMemory(Message->PifFile, CommandInfo->PifFile, CommandInfo->PifLen);
+        Message->PifLen = CommandInfo->PifLen;
+    }
+
+    if (CommandInfo->CurDirectoryLen && Message->CurDirectoryLen)
+    {
+        if (Message->CurDirectoryLen < CommandInfo->CurDirectoryLen) return STATUS_BUFFER_TOO_SMALL;
+
+        /* Copy the current directory */
+        RtlMoveMemory(Message->CurDirectory, CommandInfo->CurDirectory, CommandInfo->CurDirectoryLen);
+        Message->CurDirectoryLen = CommandInfo->CurDirectoryLen;
+    }
+
+    if (CommandInfo->EnvLen && Message->EnvLen)
+    {
+        if (Message->EnvLen < CommandInfo->EnvLen) return STATUS_BUFFER_TOO_SMALL;
+
+        /* Copy the environment */
+        RtlMoveMemory(Message->Env, CommandInfo->Env, CommandInfo->EnvLen);
+        Message->EnvLen = CommandInfo->EnvLen;
+    }
+
+    /* Copy the startup info */
+    RtlMoveMemory(Message->StartupInfo,
+                  &CommandInfo->StartupInfo,
+                  sizeof(STARTUPINFOA));
+
+    if (CommandInfo->DesktopLen && Message->DesktopLen)
+    {
+        if (Message->DesktopLen < CommandInfo->DesktopLen) return STATUS_BUFFER_TOO_SMALL;
+
+        /* Copy the desktop name */
+        RtlMoveMemory(Message->Desktop, CommandInfo->Desktop, CommandInfo->DesktopLen);
+        Message->DesktopLen = CommandInfo->DesktopLen;
+    }
+
+    if (CommandInfo->TitleLen && Message->TitleLen)
+    {
+        if (Message->TitleLen < CommandInfo->TitleLen) return STATUS_BUFFER_TOO_SMALL;
+
+        /* Copy the title */
+        RtlMoveMemory(Message->Title, CommandInfo->Title, CommandInfo->TitleLen);
+        Message->TitleLen = CommandInfo->TitleLen;
+    }
+
+    if (CommandInfo->ReservedLen && Message->ReservedLen)
+    {
+        if (Message->ReservedLen < CommandInfo->ReservedLen) return STATUS_BUFFER_TOO_SMALL;
+
+        /* Copy the reserved parameter */
+        RtlMoveMemory(Message->Reserved, CommandInfo->Reserved, CommandInfo->ReservedLen);
+        Message->ReservedLen = CommandInfo->ReservedLen;
+    }
+
+    return STATUS_SUCCESS;
+}
+
 VOID NTAPI BaseInitializeVDM(VOID)
 {
     /* Initialize the list head */
@@ -87,6 +540,11 @@ CSR_API(BaseSrvCheckVDM)
     PBASE_CHECK_VDM CheckVdmRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.CheckVDMRequest;
     PRTL_CRITICAL_SECTION CriticalSection = NULL;
     PVDM_CONSOLE_RECORD ConsoleRecord = NULL;
+    PVDM_DOS_RECORD DosRecord = NULL;
+    BOOLEAN NewConsoleRecord = FALSE;
+
+    /* Don't do anything if the VDM has been disabled in the registry */
+    if (!BaseSrvIsVdmAllowed()) return STATUS_ACCESS_DENIED;
 
     /* Validate the message buffers */
     if (!CsrValidateMessageBuffer(ApiMessage,
@@ -147,29 +605,106 @@ CSR_API(BaseSrvCheckVDM)
                 goto Cleanup;
             }
 
+            /* Remember that the console record was allocated here */
+            NewConsoleRecord = TRUE;
+
             /* Initialize the console record */
             ConsoleRecord->ConsoleHandle = CheckVdmRequest->ConsoleHandle;
+            ConsoleRecord->ProcessHandle = CsrGetClientThread()->Process->ProcessHandle;
+            ConsoleRecord->ServerEvent = ConsoleRecord->ClientEvent = NULL;
+            ConsoleRecord->ReenterCount = 0;
             ConsoleRecord->CurrentDirs = NULL;
             ConsoleRecord->CurDirsLength = 0;
             ConsoleRecord->SessionId = GetNextDosSesId();
             InitializeListHead(&ConsoleRecord->DosListHead);
+        }
+
+        /* Allocate a new DOS record */
+        DosRecord = (PVDM_DOS_RECORD)RtlAllocateHeap(BaseSrvHeap,
+                                                     HEAP_ZERO_MEMORY,
+                                                     sizeof(VDM_DOS_RECORD));
+        if (DosRecord == NULL)
+        {
+            Status = STATUS_NO_MEMORY;
+            goto Cleanup;
+        }
+
+        /* Initialize the DOS record */
+        DosRecord->State = VDM_NOT_LOADED;
+        DosRecord->ExitCode = 0;
 
+        Status = BaseSrvCreatePairWaitHandles(&DosRecord->ServerEvent, &DosRecord->ClientEvent);
+        if (!NT_SUCCESS(Status)) goto Cleanup;
+
+        /* Return the client event handle */
+        CheckVdmRequest->WaitObjectForParent = DosRecord->ClientEvent;
+
+        /* Translate the input structure into a VDM command structure and set it in the DOS record */
+        if (!BaseSrvCopyCommand(CheckVdmRequest, DosRecord))
+        {
+            /* The only possibility is that an allocation failure occurred */
+            Status = STATUS_NO_MEMORY;
+            goto Cleanup;
+        }
+
+        /* Add the DOS record */
+        InsertHeadList(&ConsoleRecord->DosListHead, &DosRecord->Entry);
+
+        if (ConsoleRecord->ServerEvent)
+        {
+            /* Signal the session event */
+            NtSetEvent(ConsoleRecord->ServerEvent, NULL);
+        }
+
+        if (NewConsoleRecord)
+        {
             /* Add the console record */
             InsertTailList(&VDMConsoleListHead, &ConsoleRecord->Entry);
         }
 
-        // TODO: NOT IMPLEMENTED
-        UNIMPLEMENTED;
-        return STATUS_NOT_IMPLEMENTED;
+        CheckVdmRequest->iTask = ConsoleRecord->SessionId;
+        CheckVdmRequest->VDMState = NewConsoleRecord ? VDM_NOT_LOADED : VDM_READY;
+        Status = STATUS_SUCCESS;
     }
     else
     {
         // TODO: NOT IMPLEMENTED
         UNIMPLEMENTED;
-        return STATUS_NOT_IMPLEMENTED;
+        Status = STATUS_NOT_IMPLEMENTED;
     }
 
 Cleanup:
+    /* Check if it failed */
+    if (!NT_SUCCESS(Status))
+    {
+        /* Free the DOS record */
+        if (DosRecord != NULL)
+        {
+            if (DosRecord->ServerEvent) NtClose(DosRecord->ServerEvent);
+            if (DosRecord->ClientEvent)
+            {
+                /* Close the remote handle */
+                NtDuplicateObject(CsrGetClientThread()->Process->ProcessHandle,
+                                  DosRecord->ClientEvent,
+                                  NULL,
+                                  NULL,
+                                  0,
+                                  0,
+                                  DUPLICATE_CLOSE_SOURCE);
+            }
+
+            RtlFreeHeap(BaseSrvHeap, 0, DosRecord);
+            DosRecord = NULL;
+        }
+
+        /* Free the console record if it was allocated here */
+        if (NewConsoleRecord)
+        {
+            RtlFreeHeap(BaseSrvHeap, 0, ConsoleRecord);
+            ConsoleRecord = NULL;
+        }
+    }
+
     /* Leave the critical section */
     RtlLeaveCriticalSection(CriticalSection);
 
@@ -178,20 +713,410 @@ Cleanup:
 
 CSR_API(BaseSrvUpdateVDMEntry)
 {
-    DPRINT1("%s not yet implemented\n", __FUNCTION__);
-    return STATUS_NOT_IMPLEMENTED;
+    NTSTATUS Status;
+    PBASE_UPDATE_VDM_ENTRY UpdateVdmEntryRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.UpdateVDMEntryRequest;
+    PRTL_CRITICAL_SECTION CriticalSection = NULL;
+    PVDM_CONSOLE_RECORD ConsoleRecord = NULL;
+    PVDM_DOS_RECORD DosRecord = NULL;
+
+    CriticalSection = (UpdateVdmEntryRequest->BinaryType != BINARY_TYPE_SEPARATE_WOW)
+                      ? &DosCriticalSection
+                      : &WowCriticalSection;
+
+    /* Enter the critical section */
+    RtlEnterCriticalSection(CriticalSection);
+
+    /* Check if this is a DOS or WOW VDM */
+    if (UpdateVdmEntryRequest->BinaryType != BINARY_TYPE_SEPARATE_WOW)
+    {
+        if (UpdateVdmEntryRequest->iTask != 0)
+        {
+            /* Get the console record using the task ID */
+            Status = GetConsoleRecordBySessionId(UpdateVdmEntryRequest->iTask,
+                                                 &ConsoleRecord);
+        }
+        else
+        {
+            /* Get the console record using the console handle */
+            Status = BaseSrvGetConsoleRecord(UpdateVdmEntryRequest->ConsoleHandle,
+                                             &ConsoleRecord);
+        }
+
+        if (!NT_SUCCESS(Status)) goto Cleanup;
+
+        /* Get the primary DOS record */
+        DosRecord = (PVDM_DOS_RECORD)CONTAINING_RECORD(ConsoleRecord->DosListHead.Flink,
+                                                       VDM_DOS_RECORD,
+                                                       Entry);
+
+        switch (UpdateVdmEntryRequest->EntryIndex)
+        {
+            case VdmEntryUndo:
+            {
+                /* Close the server event handle, the client will close the client handle */
+                NtClose(DosRecord->ServerEvent);
+                DosRecord->ServerEvent = DosRecord->ClientEvent = NULL;
+
+                if (UpdateVdmEntryRequest->VDMCreationState & (VDM_UNDO_PARTIAL | VDM_UNDO_FULL))
+                {
+                    /* Remove the DOS record */
+                    if (DosRecord->CommandInfo) BaseSrvFreeVDMInfo(DosRecord->CommandInfo);
+                    RemoveEntryList(&DosRecord->Entry);
+                    RtlFreeHeap(BaseSrvHeap, 0, DosRecord);
+
+                    /*
+                     * Since this is an undo, if that was the only DOS record the VDM
+                     * won't even start, so the console record should be removed too.
+                     */
+                    if (ConsoleRecord->DosListHead.Flink == &ConsoleRecord->DosListHead)
+                    {
+                        if (ConsoleRecord->ServerEvent) NtClose(ConsoleRecord->ServerEvent);
+                        RemoveEntryList(&ConsoleRecord->Entry);
+                        RtlFreeHeap(BaseSrvHeap, 0, ConsoleRecord);
+                    }
+                }
+
+                /* It was successful */
+                Status = STATUS_SUCCESS;
+
+                break;
+            }
+
+            case VdmEntryUpdateProcess:
+            {
+                /* Duplicate the VDM process handle */
+                Status = NtDuplicateObject(CsrGetClientThread()->Process->ProcessHandle,
+                                           UpdateVdmEntryRequest->VDMProcessHandle,
+                                           NtCurrentProcess(),
+                                           &ConsoleRecord->ProcessHandle,
+                                           0,
+                                           0,
+                                           DUPLICATE_SAME_ATTRIBUTES | DUPLICATE_SAME_ACCESS);
+                if (!NT_SUCCESS(Status)) goto Cleanup;
+
+                /* Create a pair of handles to one event object */
+                Status = BaseSrvCreatePairWaitHandles(&DosRecord->ServerEvent,
+                                                      &DosRecord->ClientEvent);
+                if (!NT_SUCCESS(Status)) goto Cleanup;
+
+                /* Return the client event handle */
+                UpdateVdmEntryRequest->WaitObjectForParent = DosRecord->ClientEvent;
+
+                break;
+            }
+
+            case VdmEntryUpdateControlCHandler:
+            {
+                // TODO: NOT IMPLEMENTED
+                DPRINT1("BaseSrvUpdateVDMEntry: VdmEntryUpdateControlCHandler not implemented!");
+                Status = STATUS_NOT_IMPLEMENTED;
+
+                break;
+            }
+
+            default:
+            {
+                /* Invalid */
+                Status = STATUS_INVALID_PARAMETER;
+            }
+        }
+    }
+    else
+    {
+        // TODO: NOT IMPLEMENTED
+        UNIMPLEMENTED;
+        Status = STATUS_NOT_IMPLEMENTED;
+    }
+
+Cleanup:
+    /* Leave the critical section */
+    RtlLeaveCriticalSection(CriticalSection);
+
+    return Status;
 }
 
 CSR_API(BaseSrvGetNextVDMCommand)
 {
-    DPRINT1("%s not yet implemented\n", __FUNCTION__);
-    return STATUS_NOT_IMPLEMENTED;
+    NTSTATUS Status;
+    PBASE_GET_NEXT_VDM_COMMAND GetNextVdmCommandRequest =
+    &((PBASE_API_MESSAGE)ApiMessage)->Data.GetNextVDMCommandRequest;
+    PRTL_CRITICAL_SECTION CriticalSection;
+    PLIST_ENTRY i = NULL;
+    PVDM_CONSOLE_RECORD ConsoleRecord = NULL;
+    PVDM_DOS_RECORD DosRecord = NULL;
+
+    /* Validate the message buffers */
+    if (!CsrValidateMessageBuffer(ApiMessage,
+                                  (PVOID*)&GetNextVdmCommandRequest->CmdLine,
+                                  GetNextVdmCommandRequest->CmdLen,
+                                  sizeof(*GetNextVdmCommandRequest->CmdLine))
+        || !CsrValidateMessageBuffer(ApiMessage,
+                                     (PVOID*)&GetNextVdmCommandRequest->AppName,
+                                     GetNextVdmCommandRequest->AppLen,
+                                     sizeof(*GetNextVdmCommandRequest->AppName))
+        || !CsrValidateMessageBuffer(ApiMessage,
+                                     (PVOID*)&GetNextVdmCommandRequest->PifFile,
+                                     GetNextVdmCommandRequest->PifLen,
+                                     sizeof(*GetNextVdmCommandRequest->PifFile))
+        || !CsrValidateMessageBuffer(ApiMessage,
+                                     (PVOID*)&GetNextVdmCommandRequest->CurDirectory,
+                                     GetNextVdmCommandRequest->CurDirectoryLen,
+                                     sizeof(*GetNextVdmCommandRequest->CurDirectory))
+        || !CsrValidateMessageBuffer(ApiMessage,
+                                     (PVOID*)&GetNextVdmCommandRequest->Env,
+                                     GetNextVdmCommandRequest->EnvLen,
+                                     sizeof(*GetNextVdmCommandRequest->Env))
+        || !CsrValidateMessageBuffer(ApiMessage,
+                                     (PVOID*)&GetNextVdmCommandRequest->Desktop,
+                                     GetNextVdmCommandRequest->DesktopLen,
+                                     sizeof(*GetNextVdmCommandRequest->Desktop))
+        || !CsrValidateMessageBuffer(ApiMessage,
+                                     (PVOID*)&GetNextVdmCommandRequest->Title,
+                                     GetNextVdmCommandRequest->TitleLen,
+                                     sizeof(*GetNextVdmCommandRequest->Title))
+        || !CsrValidateMessageBuffer(ApiMessage,
+                                     (PVOID*)&GetNextVdmCommandRequest->Reserved,
+                                     GetNextVdmCommandRequest->ReservedLen,
+                                     sizeof(*GetNextVdmCommandRequest->Reserved))
+        || !CsrValidateMessageBuffer(ApiMessage,
+                                     (PVOID*)&GetNextVdmCommandRequest->StartupInfo,
+                                     1,
+                                     sizeof(STARTUPINFOA)))
+    {
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    CriticalSection = (GetNextVdmCommandRequest->VDMState & VDM_FLAG_WOW)
+                      ? &WowCriticalSection
+                      : &DosCriticalSection;
+
+    /* Enter the critical section */
+    RtlEnterCriticalSection(CriticalSection);
+
+    if (!(GetNextVdmCommandRequest->VDMState & VDM_FLAG_WOW))
+    {
+        if (GetNextVdmCommandRequest->iTask != 0)
+        {
+            /* Get the console record using the task ID */
+            Status = GetConsoleRecordBySessionId(GetNextVdmCommandRequest->iTask,
+                                                 &ConsoleRecord);
+        }
+        else
+        {
+            /* Get the console record using the console handle */
+            Status = BaseSrvGetConsoleRecord(GetNextVdmCommandRequest->ConsoleHandle,
+                                             &ConsoleRecord);
+        }
+
+        /* Make sure we found the console record */
+        if (!NT_SUCCESS(Status)) goto Cleanup;
+
+        /* Return the session ID */
+        GetNextVdmCommandRequest->iTask = ConsoleRecord->SessionId;
+        GetNextVdmCommandRequest->WaitObjectForVDM = NULL;
+
+        if (GetNextVdmCommandRequest->VDMState & VDM_GET_FIRST_COMMAND)
+        {
+            /* Check if the DOS record list is empty */
+            if (ConsoleRecord->DosListHead.Flink == &ConsoleRecord->DosListHead)
+            {
+                Status = STATUS_INVALID_PARAMETER;
+                goto Cleanup;
+            }
+
+            /* Get the first DOS record */
+            DosRecord = CONTAINING_RECORD(ConsoleRecord->DosListHead.Flink, VDM_DOS_RECORD, Entry);
+
+            /* Make sure its command information is still there */
+            if (DosRecord->CommandInfo == NULL)
+            {
+                Status = STATUS_INVALID_PARAMETER;
+                goto Cleanup;
+            }
+
+            /* Fill the command information */
+            Status = BaseSrvFillCommandInfo(DosRecord->CommandInfo, GetNextVdmCommandRequest);
+            goto Cleanup;
+        }
+
+        /* Check if we should set the state of a running DOS record to ready */
+        if (!(GetNextVdmCommandRequest->VDMState
+            & (VDM_FLAG_FIRST_TASK | VDM_FLAG_RETRY | VDM_FLAG_NESTED_TASK)))
+        {
+            /* Search for a DOS record that is currently running */
+            for (i = ConsoleRecord->DosListHead.Flink; i != &ConsoleRecord->DosListHead; i = i->Flink)
+            {
+                DosRecord = CONTAINING_RECORD(i, VDM_DOS_RECORD, Entry);
+                if (DosRecord->State == VDM_NOT_READY) break;
+            }
+
+            /* Check if we found any */
+            if (i == &ConsoleRecord->DosListHead)
+            {
+                Status = STATUS_INVALID_PARAMETER;
+                goto Cleanup;
+            }
+
+            /* Set the exit code */
+            DosRecord->ExitCode = GetNextVdmCommandRequest->ExitCode;
+
+            /* Update the VDM state */
+            DosRecord->State = VDM_READY; 
+
+            /* Notify all waiting threads that the task is finished */
+            NtSetEvent(DosRecord->ServerEvent, NULL);
+            NtClose(DosRecord->ServerEvent);
+            DosRecord->ServerEvent = NULL;
+        }
+
+        /* Search for a DOS record that isn't loaded yet */
+        for (i = ConsoleRecord->DosListHead.Flink; i != &ConsoleRecord->DosListHead; i = i->Flink)
+        {
+            DosRecord = CONTAINING_RECORD(i, VDM_DOS_RECORD, Entry);
+            if (DosRecord->State == VDM_NOT_LOADED) break;
+        }
+
+        if (i != &ConsoleRecord->DosListHead)
+        {
+            /* DOS tasks which haven't been loaded yet should have a command info structure */
+            ASSERT(DosRecord->CommandInfo != NULL);
+
+            /* Check if the caller only wants environment data */
+            if (GetNextVdmCommandRequest->VDMState & VDM_GET_ENVIRONMENT)
+            {
+                if (GetNextVdmCommandRequest->EnvLen < DosRecord->CommandInfo->EnvLen)
+                {
+                    /* Not enough space was reserved */
+                    GetNextVdmCommandRequest->EnvLen = DosRecord->CommandInfo->EnvLen;
+                    Status = STATUS_BUFFER_OVERFLOW;
+                    goto Cleanup;
+                }
+
+                /* Copy the environment data */
+                RtlMoveMemory(GetNextVdmCommandRequest->Env,
+                              DosRecord->CommandInfo->Env,
+                              DosRecord->CommandInfo->EnvLen);
+
+                /* Return the actual size to the caller */
+                GetNextVdmCommandRequest->EnvLen = DosRecord->CommandInfo->EnvLen;
+            }
+            else
+            {
+                /* Fill the command information */
+                Status = BaseSrvFillCommandInfo(DosRecord->CommandInfo, GetNextVdmCommandRequest);
+
+                /* Free the command information, it's no longer needed */
+                BaseSrvFreeVDMInfo(DosRecord->CommandInfo);
+                DosRecord->CommandInfo = NULL;
+
+                /* Update the VDM state */
+                GetNextVdmCommandRequest->VDMState = DosRecord->State = VDM_NOT_READY;
+            }
+
+            Status = STATUS_SUCCESS;
+            goto Cleanup;
+        }
+    }
+    else
+    {
+        // TODO: WOW SUPPORT NOT IMPLEMENTED
+        Status = STATUS_NOT_IMPLEMENTED;
+        goto Cleanup;
+    }
+
+    /* There is no command yet */
+    if ((GetNextVdmCommandRequest->VDMState & (VDM_FLAG_DONT_WAIT | VDM_FLAG_RETRY))
+        != (VDM_FLAG_DONT_WAIT | VDM_FLAG_RETRY))
+    {
+        if (ConsoleRecord->ServerEvent)
+        {
+            /* Reset the event */
+            NtResetEvent(ConsoleRecord->ServerEvent, NULL);
+        }
+        else
+        {
+            /* Create a pair of wait handles */
+            Status = BaseSrvCreatePairWaitHandles(&ConsoleRecord->ServerEvent,
+                                                  &ConsoleRecord->ClientEvent);
+            if (!NT_SUCCESS(Status)) goto Cleanup;
+        }
+
+        /* Return the client event handle */
+        GetNextVdmCommandRequest->WaitObjectForVDM = ConsoleRecord->ClientEvent;
+    }
+
+Cleanup:
+    /* Leave the critical section */
+    RtlLeaveCriticalSection(CriticalSection);
+
+    return Status;
 }
 
 CSR_API(BaseSrvExitVDM)
 {
-    DPRINT1("%s not yet implemented\n", __FUNCTION__);
-    return STATUS_NOT_IMPLEMENTED;
+    NTSTATUS Status;
+    PBASE_EXIT_VDM ExitVdmRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.ExitVDMRequest;
+    PRTL_CRITICAL_SECTION CriticalSection = NULL;
+    PVDM_CONSOLE_RECORD ConsoleRecord = NULL;
+    PVDM_DOS_RECORD DosRecord;
+
+    CriticalSection = (ExitVdmRequest->iWowTask == 0)
+                      ? &DosCriticalSection
+                      : &WowCriticalSection;
+
+    /* Enter the critical section */
+    RtlEnterCriticalSection(CriticalSection);
+
+    if (ExitVdmRequest->iWowTask == 0)
+    {
+        /* Get the console record */
+        Status = BaseSrvGetConsoleRecord(ExitVdmRequest->ConsoleHandle, &ConsoleRecord);
+        if (!NT_SUCCESS(Status)) goto Cleanup;
+
+        /* Cleanup the DOS records */
+        while (ConsoleRecord->DosListHead.Flink != &ConsoleRecord->DosListHead)
+        {
+            DosRecord = CONTAINING_RECORD(ConsoleRecord->DosListHead.Flink,
+                                          VDM_DOS_RECORD,
+                                          Entry);
+
+            /* Set the event and close it */
+            NtSetEvent(DosRecord->ServerEvent, NULL);
+            NtClose(DosRecord->ServerEvent);
+
+            /* Remove the DOS entry */
+            if (DosRecord->CommandInfo) BaseSrvFreeVDMInfo(DosRecord->CommandInfo);
+            RemoveEntryList(&DosRecord->Entry);
+            RtlFreeHeap(BaseSrvHeap, 0, DosRecord);
+        }
+
+        if (ConsoleRecord->CurrentDirs != NULL)
+        {
+            /* Free the current directories */
+            RtlFreeHeap(BaseSrvHeap, 0, ConsoleRecord->CurrentDirs);
+            ConsoleRecord->CurrentDirs = NULL;
+            ConsoleRecord->CurDirsLength = 0;
+        }
+
+        /* Close the event handle */
+        if (ConsoleRecord->ServerEvent) NtClose(ConsoleRecord->ServerEvent);
+
+        /* Remove the console record */
+        RemoveEntryList(&ConsoleRecord->Entry);
+        RtlFreeHeap(BaseSrvHeap, 0, ConsoleRecord);
+    }
+    else
+    {
+        // TODO: NOT IMPLEMENTED
+        UNIMPLEMENTED;
+        Status = STATUS_NOT_IMPLEMENTED;
+    }
+
+Cleanup:
+    /* Leave the critical section */
+    RtlLeaveCriticalSection(CriticalSection);
+
+    return Status;
 }
 
 CSR_API(BaseSrvIsFirstVDM)
@@ -209,14 +1134,81 @@ CSR_API(BaseSrvIsFirstVDM)
 
 CSR_API(BaseSrvGetVDMExitCode)
 {
-    DPRINT1("%s not yet implemented\n", __FUNCTION__);
-    return STATUS_NOT_IMPLEMENTED;
+    NTSTATUS Status;
+    PBASE_GET_VDM_EXIT_CODE GetVDMExitCodeRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.GetVDMExitCodeRequest;
+    PLIST_ENTRY i = NULL;
+    PVDM_CONSOLE_RECORD ConsoleRecord = NULL;
+    PVDM_DOS_RECORD DosRecord = NULL;
+
+    /* Enter the critical section */
+    RtlEnterCriticalSection(&DosCriticalSection);
+
+    /* Get the console record */
+    Status = BaseSrvGetConsoleRecord(GetVDMExitCodeRequest->ConsoleHandle, &ConsoleRecord);
+    if (!NT_SUCCESS(Status)) goto Cleanup;
+
+    /* Search for a DOS record that has the same parent process handle */
+    for (i = ConsoleRecord->DosListHead.Flink; i != &ConsoleRecord->DosListHead; i = i->Flink)
+    {
+        DosRecord = CONTAINING_RECORD(i, VDM_DOS_RECORD, Entry);
+        if (DosRecord->ClientEvent == GetVDMExitCodeRequest->hParent) break;
+    }
+
+    /* Check if no DOS record was found */
+    if (i == &ConsoleRecord->DosListHead)
+    {
+        Status = STATUS_NOT_FOUND;
+        goto Cleanup;
+    }
+
+    /* Check if this task is still running */
+    if (DosRecord->State == VDM_READY)
+    {
+        GetVDMExitCodeRequest->ExitCode = STATUS_PENDING;
+        goto Cleanup;
+    }
+
+    /* Return the exit code */
+    GetVDMExitCodeRequest->ExitCode = DosRecord->ExitCode;
+
+    /* Since this is a zombie task record, remove it */
+    if (DosRecord->CommandInfo) BaseSrvFreeVDMInfo(DosRecord->CommandInfo);
+    RemoveEntryList(&DosRecord->Entry);
+    RtlFreeHeap(BaseSrvHeap, 0, DosRecord);
+
+Cleanup:
+    /* Leave the critical section */
+    RtlLeaveCriticalSection(&DosCriticalSection);
+
+    return Status;
 }
 
 CSR_API(BaseSrvSetReenterCount)
 {
-    DPRINT1("%s not yet implemented\n", __FUNCTION__);
-    return STATUS_NOT_IMPLEMENTED;
+    NTSTATUS Status = STATUS_SUCCESS;
+    PBASE_SET_REENTER_COUNT SetReenterCountRequest = &((PBASE_API_MESSAGE)ApiMessage)->Data.SetReenterCountRequest;
+    PVDM_CONSOLE_RECORD ConsoleRecord;
+
+    /* Enter the critical section */
+    RtlEnterCriticalSection(&DosCriticalSection);
+
+    /* Get the console record */
+    Status = BaseSrvGetConsoleRecord(SetReenterCountRequest->ConsoleHandle, &ConsoleRecord);
+    if (!NT_SUCCESS(Status)) goto Cleanup;
+
+    if (SetReenterCountRequest->fIncDec == VDM_INC_REENTER_COUNT) ConsoleRecord->ReenterCount++;
+    else if (SetReenterCountRequest->fIncDec == VDM_DEC_REENTER_COUNT)
+    {
+        ConsoleRecord->ReenterCount--;
+        if (ConsoleRecord->ServerEvent != NULL) NtSetEvent(ConsoleRecord->ServerEvent, NULL);
+    }
+    else Status = STATUS_INVALID_PARAMETER;
+
+Cleanup:
+    /* Leave the critical section */
+    RtlLeaveCriticalSection(&DosCriticalSection);
+
+    return Status;
 }
 
 CSR_API(BaseSrvSetVDMCurDirs)