Index: trunk/src/VBox/Main/src-client/GuestCtrlIO.cpp
===================================================================
--- trunk/src/VBox/Main/src-client/GuestCtrlIO.cpp	(revision 39411)
+++ trunk/src/VBox/Main/src-client/GuestCtrlIO.cpp	(revision 39418)
@@ -318,4 +318,9 @@
 }
 
+uint32_t GuestProcessStream::GetSize()
+{
+    return m_cbSize;
+}
+
 /**
  * Tries to parse the next upcoming pair block within the internal
Index: trunk/src/VBox/Main/src-client/GuestCtrlImpl.cpp
===================================================================
--- trunk/src/VBox/Main/src-client/GuestCtrlImpl.cpp	(revision 39411)
+++ trunk/src/VBox/Main/src-client/GuestCtrlImpl.cpp	(revision 39418)
@@ -162,16 +162,12 @@
     AssertReturnVoid(uContextID);
 
-    LogFlowFunc(("Destroying callback with CID=%u ...\n", uContextID));
-
-    /* Notify callback (if necessary). */
-    int rc = callbackNotifyEx(uContextID, VERR_CANCELLED,
-                              Guest::tr("VM is shutting down, canceling uncompleted guest requests ..."));
-    AssertRC(rc);
-
     CallbackMapIter it = mCallbackMap.find(uContextID);
     if (it != mCallbackMap.end())
     {
+        LogFlowFunc(("Callback with CID=%u found\n", uContextID));
         if (it->second.pvData)
         {
+            LogFlowFunc(("Destroying callback with CID=%u ...\n", uContextID));
+
             callbackFreeUserData(it->second.pvData);
             it->second.cbData = 0;
@@ -385,5 +381,5 @@
 
 /**
- * TODO
+ * Notifies a specified callback about its final status.
  *
  * @return  IPRT status code.
@@ -395,6 +391,8 @@
 {
     AssertReturn(uContextID, VERR_INVALID_PARAMETER);
-
-    LogFlowFunc(("Notifying callback with CID=%u, iRC=%d, pszMsg=%s ...\n",
+    if (RT_FAILURE(iRC))
+        AssertReturn(pszMessage, VERR_INVALID_PARAMETER);
+
+    LogFlowFunc(("Checking whether callback (CID=%u) needs notification iRC=%Rrc, pszMsg=%s\n",
                  uContextID, iRC, pszMessage ? pszMessage : "<No message given>"));
 
@@ -430,4 +428,7 @@
             && !fCompleted)
         {
+            LogFlowFunc(("Notifying callback with CID=%u, iRC=%Rrc, pszMsg=%s\n",
+                         uContextID, iRC, pszMessage ? pszMessage : "<No message given>"));
+
             /*
              * To get waitForCompletion completed (unblocked) we have to notify it if necessary (only
@@ -437,6 +438,5 @@
              * progress object to become signalled.
              */
-            if (   RT_SUCCESS(iRC)
-                && !pszMessage)
+            if (RT_SUCCESS(iRC))
             {
                 hRC = pProgress->notifyComplete(S_OK);
@@ -444,5 +444,5 @@
             else
             {
-                AssertPtrReturn(pszMessage, VERR_INVALID_PARAMETER);
+
                 hRC = pProgress->notifyComplete(VBOX_E_IPRT_ERROR /* Must not be S_OK. */,
                                                 COM_IIDOF(IGuest),
@@ -450,6 +450,10 @@
                                                 pszMessage);
             }
-        }
-        ComAssertComRC(hRC);
+
+            LogFlowFunc(("Notified callback with CID=%u returned %Rhrc (0x%x)\n",
+                         uContextID, hRC, hRC));
+        }
+        else
+            LogFlowFunc(("Callback with CID=%u already notified\n", uContextID));
 
         /*
@@ -564,4 +568,6 @@
     if (RT_SUCCESS(vrc))
     {
+        LogFlowFunc(("Waiting for callback completion (CID=%u, Stage=%RI32, timeout=%RI32ms) ...\n",
+                     uContextID, lStage, lTimeout));
         HRESULT rc;
         if (lStage < 0)
@@ -579,4 +585,6 @@
     }
 
+    LogFlowFunc(("Callback (CID=%u) completed with rc=%Rrc\n",
+                 uContextID, vrc));
     return vrc;
 }
@@ -603,5 +611,5 @@
      */
 #ifdef DEBUG_andy
-    LogFlowFunc(("pvExtension = %p, u32Function = %d, pvParms = %p, cbParms = %d\n",
+    LogFlowFunc(("pvExtension=%p, u32Function=%d, pvParms=%p, cbParms=%d\n",
                  pvExtension, u32Function, pvParms, cbParms));
 #endif
@@ -686,4 +694,7 @@
         AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
+        LogFlowFunc(("Execution status (CID=%u, pData=0x%p)\n",
+                     uContextID, pData));
+
         PCALLBACKDATAEXECSTATUS pCallbackData =
             (PCALLBACKDATAEXECSTATUS)callbackGetUserDataMutableRaw(uContextID, NULL /* cbData */);
@@ -695,16 +706,43 @@
             /** @todo Copy void* buffer contents? */
         }
-        else
-            AssertReleaseMsgFailed(("Process status (PID=%u, CID=%u) does not have allocated callback data!\n",
-                                    pData->u32PID, uContextID));
-    }
-
-    int vrc = VINF_SUCCESS;
+        /* If pCallbackData is NULL this might be an old request for which no user data
+         * might exist anymore. */
+    }
+
+    int vrc = VINF_SUCCESS; /* Function result. */
+    int rcCallback = VINF_SUCCESS; /* Callback result. */
     Utf8Str errMsg;
 
     /* Was progress canceled before? */
-    bool fCbCanceled = callbackIsCanceled(uContextID);
-    if (!fCbCanceled)
-    {
+    bool fCanceled = callbackIsCanceled(uContextID);
+    if (!fCanceled)
+    {
+        /* Handle process map. This needs to be done first in order to have a valid
+         * map in case some callback gets notified a bit below. */
+
+        /* Note: PIDs never get removed here in case the guest process signalled its
+         *       end; instead the next call of GetProcessStatus() will remove the PID
+         *       from the process map after we got the process' final (exit) status.
+         *       See waitpid() for an example. */
+        if (pData->u32PID) /* Only add/change a process if it has a valid PID (>0). */
+        {
+            switch (pData->u32Status)
+            {
+                /* Interprete u32Flags as the guest process' exit code. */
+                case PROC_STS_TES:
+                case PROC_STS_TOK:
+                    vrc = processSetStatus(pData->u32PID,
+                                           (ExecuteProcessStatus_T)pData->u32Status,
+                                           pData->u32Flags /* Exit code. */, 0 /* Flags. */);
+                    break;
+                /* Just reach through flags. */
+                default:
+                    vrc = processSetStatus(pData->u32PID,
+                                           (ExecuteProcessStatus_T)pData->u32Status,
+                                           0 /* Exit code. */, pData->u32Flags);
+                    break;
+            }
+        }
+
         /* Do progress handling. */
         switch (pData->u32Status)
@@ -725,4 +763,5 @@
                 errMsg = Utf8StrFmt(Guest::tr("Process terminated abnormally with status '%u'"),
                                     pData->u32Flags);
+                rcCallback = VERR_GENERAL_FAILURE; /** @todo */
                 break;
 
@@ -732,4 +771,5 @@
                 errMsg = Utf8StrFmt(Guest::tr("Process terminated via signal with status '%u'"),
                                     pData->u32Flags);
+                rcCallback = VERR_GENERAL_FAILURE; /** @todo */
                 break;
 
@@ -737,4 +777,5 @@
                 LogRel(("Guest process (PID %u) timed out and was killed\n", pData->u32PID)); /** @todo Add process name */
                 errMsg = Utf8StrFmt(Guest::tr("Process timed out and was killed"));
+                rcCallback = VERR_TIMEOUT;
                 break;
 
@@ -742,4 +783,5 @@
                 LogRel(("Guest process (PID %u) timed out and could not be killed\n", pData->u32PID)); /** @todo Add process name */
                 errMsg = Utf8StrFmt(Guest::tr("Process timed out and could not be killed"));
+                rcCallback = VERR_TIMEOUT;
                 break;
 
@@ -758,11 +800,32 @@
                 }
                 else
+                {
                     errMsg = Utf8StrFmt(Guest::tr("Process killed because system is shutting down"));
+                    rcCallback = VERR_CANCELLED;
+                }
                 break;
 
             case PROC_STS_ERROR:
-                LogRel(("Guest process (PID %u) could not be started because of rc=%Rrc\n",
-                        pData->u32PID, pData->u32Flags)); /** @todo Add process name */
+                if (pData->u32PID)
+                {
+                    LogRel(("Guest process (PID %u) could not be started because of rc=%Rrc\n",
+                            pData->u32PID, pData->u32Flags)); /** @todo Add process name */
+                }
+                else
+                {
+                    switch (pData->u32Flags)
+                    {
+                        case VERR_MAX_PROCS_REACHED:
+                            LogRel(("Guest process could not be started because maximum number of parallel guest processes has been reached\n"));
+                            break;
+
+                        default:
+                            LogRel(("Guest process could not be started because of rc=%Rrc\n",
+                                    pData->u32Flags));
+                    }
+
+                }
                 errMsg = Utf8StrFmt(Guest::tr("Process execution failed with rc=%Rrc"), pData->u32Flags);
+                rcCallback = pData->u32Flags; /* Report back rc. */
                 break;
 
@@ -771,67 +834,44 @@
                 break;
         }
-
-        /* Handle process map. */
-        /** @todo What happens on/deal with PID reuse? */
-        /** @todo How to deal with multiple updates at once? */
+    }
+    else
+    {
+        errMsg = Utf8StrFmt(Guest::tr("Process execution canceled"));
+        rcCallback = VERR_CANCELLED;
+    }
+
+    /* Do we need to handle the callback error? */
+    if (RT_FAILURE(rcCallback))
+    {
+        AssertMsg(!errMsg.isEmpty(), ("Error message must not be empty!\n"));
+
+        /* Notify all callbacks which are still waiting on something
+         * which is related to the current PID. */
         if (pData->u32PID)
         {
-            VBOXGUESTCTRL_PROCESS process;
-            vrc = processGetByPID(pData->u32PID, &process);
-            if (vrc == VERR_NOT_FOUND)
-            {
-                /* Not found, add to map. */
-                vrc = processAdd(pData->u32PID,
-                                 (ExecuteProcessStatus_T)pData->u32Status,
-                                 pData->u32Flags /* Contains exit code. */,
-                                 0 /*Flags. */);
-                AssertRC(vrc);
-            }
-            else if (RT_SUCCESS(vrc))
-            {
-                /* Process found, update process map. */
-                vrc = processSetStatus(pData->u32PID,
-                                       (ExecuteProcessStatus_T)pData->u32Status,
-                                       pData->u32Flags /* Contains exit code. */,
-                                       0 /*Flags. */);
-                AssertRC(vrc);
-            }
-            else
-                AssertReleaseMsgFailed(("Process was neither found nor absent!?\n"));
-        }
-    }
-    else
-        errMsg = Utf8StrFmt(Guest::tr("Process execution canceled"));
-
-    if (!callbackIsComplete(uContextID))
-    {
-        if (   errMsg.length()
-            || fCbCanceled) /* If canceled we have to report E_FAIL! */
-        {
-            /* Notify all callbacks which are still waiting on something
-             * which is related to the current PID. */
-            if (pData->u32PID)
-            {
-                vrc = callbackNotifyAllForPID(pData->u32PID, VERR_GENERAL_FAILURE, errMsg.c_str());
-                if (RT_FAILURE(vrc))
-                    LogFlowFunc(("Failed to notify other callbacks for PID=%u\n",
-                                 pData->u32PID));
-            }
-
-            /* Let the caller know what went wrong ... */
-            int rc2 = callbackNotifyEx(uContextID, VERR_GENERAL_FAILURE, errMsg.c_str());
+            int rc2 = callbackNotifyAllForPID(pData->u32PID, rcCallback, errMsg.c_str());
             if (RT_FAILURE(rc2))
             {
-                LogFlowFunc(("Failed to notify callback CID=%u for PID=%u\n",
-                             uContextID, pData->u32PID));
-
+                LogFlowFunc(("Failed to notify other callbacks for PID=%u\n",
+                             pData->u32PID));
                 if (RT_SUCCESS(vrc))
                     vrc = rc2;
             }
-            LogFlowFunc(("Process (CID=%u, status=%u) reported error: %s\n",
-                         uContextID, pData->u32Status, errMsg.c_str()));
-        }
-    }
-    LogFlowFunc(("Returned with rc=%Rrc\n", vrc));
+        }
+
+        /* Let the caller know what went wrong ... */
+        int rc2 = callbackNotifyEx(uContextID, rcCallback, errMsg.c_str());
+        if (RT_FAILURE(rc2))
+        {
+            LogFlowFunc(("Failed to notify callback CID=%u for PID=%u\n",
+                         uContextID, pData->u32PID));
+            if (RT_SUCCESS(vrc))
+                vrc = rc2;
+        }
+        LogFlowFunc(("Process (CID=%u, status=%u) reported: %s\n",
+                     uContextID, pData->u32Status, errMsg.c_str()));
+    }
+    LogFlowFunc(("Returned with rc=%Rrc, rcCallback=%Rrc\n",
+                 vrc, rcCallback));
     return vrc;
 }
@@ -851,4 +891,7 @@
         AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
+        LogFlowFunc(("Output status (CID=%u, pData=0x%p)\n",
+                     uContextID, pData));
+
         PCALLBACKDATAEXECOUT pCallbackData =
             (PCALLBACKDATAEXECOUT)callbackGetUserDataMutableRaw(uContextID, NULL /* cbData */);
@@ -878,7 +921,6 @@
             }
         }
-        else
-            AssertReleaseMsgFailed(("Process output status (PID=%u, CID=%u) does not have allocated callback data!\n",
-                                    pData->u32PID, uContextID));
+        /* If pCallbackData is NULL this might be an old request for which no user data
+         * might exist anymore. */
     }
 
@@ -909,4 +951,7 @@
         AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
+        LogFlowFunc(("Input status (CID=%u, pData=0x%p)\n",
+                     uContextID, pData));
+
         PCALLBACKDATAEXECINSTATUS pCallbackData =
             (PCALLBACKDATAEXECINSTATUS)callbackGetUserDataMutableRaw(uContextID, NULL /* cbData */);
@@ -915,11 +960,10 @@
             /* Save bytes processed. */
             pCallbackData->cbProcessed = pData->cbProcessed;
-            pCallbackData->u32Status = pData->u32Status;
-            pCallbackData->u32Flags = pData->u32Flags;
-            pCallbackData->u32PID = pData->u32PID;
-        }
-        else
-            AssertReleaseMsgFailed(("Process input status (PID=%u, CID=%u) does not have allocated callback data!\n",
-                                    pData->u32PID, uContextID));
+            pCallbackData->u32Status   = pData->u32Status;
+            pCallbackData->u32Flags    = pData->u32Flags;
+            pCallbackData->u32PID      = pData->u32PID;
+        }
+        /* If pCallbackData is NULL this might be an old request for which no user data
+         * might exist anymore. */
     }
 
@@ -936,17 +980,59 @@
     Assert(uContextID);
 
-    return callbackNotifyEx(uContextID, S_OK,
+    LogFlowFunc(("Client disconnected (CID=%u)\n,", uContextID));
+
+    return callbackNotifyEx(uContextID, VERR_CANCELLED,
                             Guest::tr("Client disconnected"));
 }
 
-int Guest::processAdd(uint32_t u32PID, ExecuteProcessStatus_T enmStatus,
-                      uint32_t uExitCode, uint32_t uFlags)
+/**
+ * Gets guest process information. Removes the process from the map
+ * after the process was marked as exited/terminated.
+ *
+ * @return  IPRT status code.
+ * @param   u32PID
+ * @param   pProcess
+ */
+int Guest::processGetStatus(uint32_t u32PID, PVBOXGUESTCTRL_PROCESS pProcess)
 {
     AssertReturn(u32PID, VERR_INVALID_PARAMETER);
 
+    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+    GuestProcessMapIter it = mGuestProcessMap.find(u32PID);
+    if (it != mGuestProcessMap.end())
+    {
+        if (pProcess)
+        {
+            pProcess->mStatus   = it->second.mStatus;
+            pProcess->mExitCode = it->second.mExitCode;
+            pProcess->mFlags    = it->second.mFlags;
+        }
+
+        /* If the is marked as stopped/terminated
+         * remove it from the map. */
+        if (it->second.mStatus != ExecuteProcessStatus_Started)
+            mGuestProcessMap.erase(it);
+
+        return VINF_SUCCESS;
+    }
+
+    return VERR_NOT_FOUND;
+}
+
+int Guest::processSetStatus(uint32_t u32PID, ExecuteProcessStatus_T enmStatus, uint32_t uExitCode, uint32_t uFlags)
+{
+    AssertReturn(u32PID, VERR_INVALID_PARAMETER);
+
     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
-    GuestProcessMapIterConst it = mGuestProcessMap.find(u32PID);
-    if (it == mGuestProcessMap.end())
+    GuestProcessMapIter it = mGuestProcessMap.find(u32PID);
+    if (it != mGuestProcessMap.end())
+    {
+        it->second.mStatus = enmStatus;
+        it->second.mExitCode = uExitCode;
+        it->second.mFlags = uFlags;
+    }
+    else
     {
         VBOXGUESTCTRL_PROCESS process;
@@ -957,48 +1043,7 @@
 
         mGuestProcessMap[u32PID] = process;
-
-        return VINF_SUCCESS;
-    }
-
-    return VERR_ALREADY_EXISTS;
-}
-
-int Guest::processGetByPID(uint32_t u32PID, PVBOXGUESTCTRL_PROCESS pProcess)
-{
-    AssertReturn(u32PID, VERR_INVALID_PARAMETER);
-    AssertPtrReturn(pProcess, VERR_INVALID_PARAMETER);
-
-    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
-
-    GuestProcessMapIterConst it = mGuestProcessMap.find(u32PID);
-    if (it != mGuestProcessMap.end())
-    {
-        pProcess->mStatus = it->second.mStatus;
-        pProcess->mExitCode = it->second.mExitCode;
-        pProcess->mFlags = it->second.mFlags;
-
-        return VINF_SUCCESS;
-    }
-
-    return VERR_NOT_FOUND;
-}
-
-int Guest::processSetStatus(uint32_t u32PID, ExecuteProcessStatus_T enmStatus, uint32_t uExitCode, uint32_t uFlags)
-{
-    AssertReturn(u32PID, VERR_INVALID_PARAMETER);
-
-    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
-
-    GuestProcessMapIter it = mGuestProcessMap.find(u32PID);
-    if (it != mGuestProcessMap.end())
-    {
-        it->second.mStatus = enmStatus;
-        it->second.mExitCode = uExitCode;
-        it->second.mFlags = uFlags;
-
-        return VINF_SUCCESS;
-    }
-
-    return VERR_NOT_FOUND;
+    }
+
+    return VINF_SUCCESS;
 }
 
@@ -1080,4 +1125,5 @@
  * @param   aUsername               Username to execute tool under.
  * @param   aPassword               The user's password.
+ * @param   uFlagsToAdd             ExecuteProcessFlag flags to add to the execution operation.
  * @param   aProgress               Pointer which receives the tool's progress object. Optional.
  * @param   aPID                    Pointer which receives the tool's PID. Optional.
@@ -1086,4 +1132,6 @@
                                      ComSafeArrayIn(IN_BSTR, aArguments), ComSafeArrayIn(IN_BSTR, aEnvironment),
                                      IN_BSTR aUsername, IN_BSTR aPassword,
+                                     ULONG uFlagsToAdd,
+                                     GuestCtrlStreamObjects *pObjStdOut, GuestCtrlStreamObjects *pObjStdErr,
                                      IProgress **aProgress, ULONG *aPID)
 {
@@ -1091,60 +1139,69 @@
     ULONG uPID;
 
+    ULONG uFlags =   ExecuteProcessFlag_Hidden
+                   | ExecuteProcessFlag_WaitForProcessStartOnly;
+    if (uFlagsToAdd)
+        uFlags |= uFlagsToAdd;
+
+    bool fWaitForOutput = false;
+    if (   (   (uFlags & ExecuteProcessFlag_WaitForStdOut)
+            && pObjStdOut)
+        || (   (uFlags & ExecuteProcessFlag_WaitForStdErr)
+            && pObjStdErr))
+    {
+        fWaitForOutput = true;
+    }
+
     HRESULT rc = ExecuteProcess(aTool,
-                                ExecuteProcessFlag_Hidden,
+                                uFlags,
                                 ComSafeArrayInArg(aArguments),
                                 ComSafeArrayInArg(aEnvironment),
                                 aUsername, aPassword,
-                                5 * 1000 /* Wait 5s for getting the process started. */,
+                                0 /* No timeout. */,
                                 &uPID, progressTool.asOutParam());
+    if (   SUCCEEDED(rc)
+        && fWaitForOutput)
+    {
+        BOOL fCompleted;
+        while (   SUCCEEDED(progressTool->COMGETTER(Completed)(&fCompleted))
+               && !fCompleted)
+        {
+            BOOL fCanceled;
+            rc = progressTool->COMGETTER(Canceled)(&fCanceled);
+            ComAssertRC(rc);
+            if (fCanceled)
+            {
+                rc = setError(VBOX_E_IPRT_ERROR,
+                              tr("%s was cancelled"), Utf8Str(aDescription));
+                break;
+            }
+
+            if (   (uFlags & ExecuteProcessFlag_WaitForStdOut)
+                && pObjStdOut)
+            {
+                rc = executeStreamParse(uPID, ProcessOutputFlag_None /* StdOut */, *pObjStdOut);
+            }
+
+            if (   (uFlags & ExecuteProcessFlag_WaitForStdErr)
+                && pObjStdErr)
+            {
+                rc = executeStreamParse(uPID, ProcessOutputFlag_StdErr, *pObjStdErr);
+            }
+
+            if (FAILED(rc))
+                break;
+        }
+    }
+
     if (SUCCEEDED(rc))
     {
-        /* Wait for process to exit ... */
-        rc = progressTool->WaitForCompletion(-1);
-        if (FAILED(rc)) return rc;
-
-        BOOL fCompleted = FALSE;
-        BOOL fCanceled = FALSE;
-        progressTool->COMGETTER(Completed)(&fCompleted);
-        if (!fCompleted)
-            progressTool->COMGETTER(Canceled)(&fCanceled);
-
-        if (fCompleted)
-        {
-            ExecuteProcessStatus_T retStatus;
-            ULONG uRetExitCode, uRetFlags;
-            if (SUCCEEDED(rc))
-            {
-                rc = GetProcessStatus(uPID, &uRetExitCode, &uRetFlags, &retStatus);
-                if (SUCCEEDED(rc))
-                {
-                    if (uRetExitCode != 0) /* Not equal 0 means some error occured. */
-                    {
-                        /** @todo IPRT exit code to string! */
-                        rc = setError(VBOX_E_IPRT_ERROR,
-                                      tr("%s: Error %u occured"),
-                                      Utf8Str(aDescription).c_str(), uRetExitCode);
-                    }
-                    else /* Return code 0, success. */
-                    {
-                        if (aProgress)
-                        {
-                            /* Return the progress to the caller. */
-                            progressTool.queryInterfaceTo(aProgress);
-                        }
-                        if (aPID)
-                            *aPID = uPID;
-                    }
-                }
-            }
-        }
-        else if (fCanceled)
-        {
-            rc = setError(VBOX_E_IPRT_ERROR,
-                          tr("%s was aborted"), aDescription);
-        }
-        else
-            AssertReleaseMsgFailed(("%s: Operation neither completed nor canceled!?\n",
-                                    Utf8Str(aDescription).c_str()));
+        if (aProgress)
+        {
+            /* Return the progress to the caller. */
+            progressTool.queryInterfaceTo(aProgress);
+        }
+
+        if (aPID)
+            *aPID = uPID;
     }
 
@@ -1267,34 +1324,112 @@
 
 /**
- * Tries to drain the guest's output (from stdout) and fill it into
+ * Tries to drain the guest's output and fill it into
  * a guest process stream object for later usage.
  *
+ * @todo    What's about specifying stderr?
  * @return  IPRT status code.
  * @param   aPID                    PID of process to get the output from.
+ * @param   aFlags                  Which stream to drain (stdout or stderr).
  * @param   stream                  Reference to guest process stream to fill.
  */
-int Guest::executeStreamDrain(ULONG aPID, GuestProcessStream &stream)
+int Guest::executeStreamDrain(ULONG aPID, ULONG aFlags, GuestProcessStream &stream)
 {
     AssertReturn(aPID, VERR_INVALID_PARAMETER);
-
-    /** @todo Should we try to drain the stream harder? */
 
     int rc = VINF_SUCCESS;
     for (;;)
     {
-        SafeArray<BYTE> aOutputData;
-        HRESULT hr = GetProcessOutput(aPID, ProcessOutputFlag_None /* Stdout */,
-                                      10 * 1000 /* Timeout in ms */,
-                                      _64K, ComSafeArrayAsOutParam(aOutputData));
-        if (   SUCCEEDED(hr)
-            && aOutputData.size())
-        {
-            rc = stream.AddData(aOutputData.raw(), aOutputData.size());
-            if (RT_UNLIKELY(RT_FAILURE(rc)))
+        SafeArray<BYTE> aData;
+        HRESULT hr = getProcessOutputInternal(aPID, aFlags,
+                                              0 /* Infinite timeout */,
+                                              _64K, ComSafeArrayAsOutParam(aData), &rc);
+        if (SUCCEEDED(hr))
+        {
+            if (aData.size())
+            {
+                rc = stream.AddData(aData.raw(), aData.size());
+                if (RT_UNLIKELY(RT_FAILURE(rc)))
+                    break;
+            }
+
+            continue; /* Try one more time. */
+        }
+        else /* No more output and/or error! */
+        {
+            if (rc == VERR_NOT_FOUND)
+                rc = VINF_SUCCESS;
+            break;
+        }
+    }
+
+    return rc;
+}
+
+/**
+ * Tries to retrieve the next stream block of a given stream and
+ * drains the process output only as much as needed to get this next
+ * stream block.
+ *
+ * @return  IPRT status code.
+ * @param   ulPID
+ * @param   ulFlags
+ * @param   stream
+ * @param   streamBlock
+ */
+int Guest::executeStreamGetNextBlock(ULONG ulPID,
+                                     ULONG ulFlags,
+                                     GuestProcessStream &stream,
+                                     GuestProcessStreamBlock &streamBlock)
+{
+    AssertReturn(!streamBlock.GetCount(), VERR_INVALID_PARAMETER);
+
+    bool fDrainStream = true;
+    int rc;
+    do
+    {
+        rc = stream.ParseBlock(streamBlock);
+        if (RT_FAILURE(rc)) /* More data needed or empty buffer? */
+        {
+            if (fDrainStream)
+            {
+                SafeArray<BYTE> aData;
+                HRESULT hr = getProcessOutputInternal(ulPID, ulFlags,
+                                                      0 /* Infinite timeout */,
+                                                      _64K, ComSafeArrayAsOutParam(aData), &rc);
+                if (SUCCEEDED(hr))
+                {
+                    if (aData.size())
+                    {
+                        rc = stream.AddData(aData.raw(), aData.size());
+                        if (RT_UNLIKELY(RT_FAILURE(rc)))
+                            break;
+                    }
+
+                    continue; /* Try one more time. */
+                }
+                else
+                {
+                    /* No more output to drain from stream. */
+                    if (rc == VERR_NOT_FOUND)
+                    {
+                        fDrainStream = false;
+                        continue;
+                    }
+
+                    break;
+                }
+            }
+            else
+            {
+                /* We haved drained the stream as much as we can and reached the
+                 * end of our stream buffer -- that means that there simply is no
+                 * stream block anymore, which is ok. */
+                if (rc == VERR_NO_DATA)
+                    rc = VINF_SUCCESS;
                 break;
-        }
-        else /* No more output and/or error! */
-            break;
-    }
+            }
+        }
+    }
+    while (!streamBlock.GetCount());
 
     return rc;
@@ -1306,30 +1441,25 @@
  *
  * @return  IPRT status code.
- * @param   aPID                    PID of process to get/parse the output from.
+ * @param   ulPID                   PID of process to get/parse the output from.
+ * @param   ulFlags                 ?
  * @param   stream                  Reference to process stream object to use.
  * @param   streamBlock             Reference that receives the next stream block data.
  *
  */
-int Guest::executeStreamGetNextBlock(ULONG aPID, GuestProcessStream &stream,
-                                     GuestProcessStreamBlock &streamBlock)
-{
-    int rc = executeStreamDrain(aPID, stream);
-    if (RT_SUCCESS(rc))
-    {
-        do
-        {
-            rc = stream.ParseBlock(streamBlock);
-            if (streamBlock.GetCount())
-                break; /* We got a block, bail out! */
-        } while (RT_SUCCESS(rc));
-
-        /* In case this was the last block, VERR_NO_DATA is returned.
-         * Overwrite this to get a proper return value for the last block. */
-        if(    streamBlock.GetCount()
-            && rc == VERR_NO_DATA)
-        {
-            rc = VINF_SUCCESS;
-        }
-    }
+int Guest::executeStreamParseNextBlock(ULONG ulPID,
+                                       ULONG ulFlags,
+                                       GuestProcessStream &stream,
+                                       GuestProcessStreamBlock &streamBlock)
+{
+    AssertReturn(!streamBlock.GetCount(), VERR_INVALID_PARAMETER);
+
+    int rc;
+    do
+    {
+        rc = stream.ParseBlock(streamBlock);
+        if (RT_FAILURE(rc))
+            break;
+    }
+    while (!streamBlock.GetCount());
 
     return rc;
@@ -1342,38 +1472,33 @@
  *
  * @return  HRESULT
- * @param   aPID                    PID of process to get/parse the output from.
+ * @param   ulPID                   PID of process to get/parse the output from.
+ * @param   ulFlags                 ?
  * @param   streamObjects           Reference to a guest stream object structure for
  *                                  storing the parsed data.
  */
-HRESULT Guest::executeStreamParse(ULONG aPID, GuestCtrlStreamObjects &streamObjects)
-{
-    GuestProcessStream guestStream;
-    HRESULT hr = executeStreamDrain(aPID, guestStream);
-    if (SUCCEEDED(hr))
-    {
-        for (;;)
+HRESULT Guest::executeStreamParse(ULONG ulPID, ULONG ulFlags, GuestCtrlStreamObjects &streamObjects)
+{
+    GuestProcessStream stream;
+    int rc = executeStreamDrain(ulPID, ulFlags, stream);
+    if (RT_SUCCESS(rc))
+    {
+        do
         {
             /* Try to parse the stream output we gathered until now. If we still need more
              * data the parsing routine will tell us and we just do another poll round. */
             GuestProcessStreamBlock curBlock;
-            int vrc = guestStream.ParseBlock(curBlock);
-            if (RT_SUCCESS(vrc))
-            {
-                if (curBlock.GetCount())
-                {
-                    streamObjects.push_back(curBlock);
-                }
-                else
-                    break; /* No more data. */
-            }
-            else /* Everything else would be an error! */
-                hr = setError(VBOX_E_IPRT_ERROR,
-                              tr("Error while parsing guest output (%Rrc)"), vrc);
-        }
-    }
-
-    /** @todo Add check if there now are any sream objects at all! */
-
-    return hr;
+            rc = executeStreamParseNextBlock(ulPID, ulFlags, stream, curBlock);
+            if (RT_SUCCESS(rc))
+                streamObjects.push_back(curBlock);
+        } while (RT_SUCCESS(rc));
+
+        if (rc == VERR_NO_DATA) /* End of data reached. */
+            rc = VINF_SUCCESS;
+    }
+
+    if (RT_FAILURE(rc))
+        return setError(VBOX_E_IPRT_ERROR,
+                        tr("Error while parsing guest output (%Rrc)"), rc);
+    return rc;
 }
 
@@ -1461,5 +1586,7 @@
             && !(aFlags & ExecuteProcessFlag_WaitForProcessStartOnly)
             && !(aFlags & ExecuteProcessFlag_Hidden)
-            && !(aFlags & ExecuteProcessFlag_NoProfile))
+            && !(aFlags & ExecuteProcessFlag_NoProfile)
+            && !(aFlags & ExecuteProcessFlag_WaitForStdOut)
+            && !(aFlags & ExecuteProcessFlag_WaitForStdErr))
         {
             if (pRC)
@@ -1619,5 +1746,5 @@
             if (RT_SUCCESS(vrc))
             {
-                LogFlowFunc(("Waiting for HGCM callback (timeout=%dms) ...\n", aTimeoutMS));
+                LogFlowFunc(("Waiting for HGCM callback (timeout=%RI32ms) ...\n", aTimeoutMS));
 
                 /*
@@ -1629,5 +1756,5 @@
                 PCALLBACKDATAEXECSTATUS pExecStatus = NULL;
 
-                 /*
+                /*
                  * Wait for the first stage (=0) to complete (that is starting the process).
                  */
@@ -1713,5 +1840,5 @@
     {
         VBOXGUESTCTRL_PROCESS process;
-        int vrc = processGetByPID(aPID, &process);
+        int vrc = processGetStatus(aPID, &process);
         if (RT_SUCCESS(vrc))
         {
@@ -1871,4 +1998,17 @@
 STDMETHODIMP Guest::GetProcessOutput(ULONG aPID, ULONG aFlags, ULONG aTimeoutMS, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
 {
+#ifndef VBOX_WITH_GUEST_CONTROL
+    ReturnComNotImplemented();
+#else  /* VBOX_WITH_GUEST_CONTROL */
+    using namespace guestControl;
+
+    return getProcessOutputInternal(aPID, aFlags, aTimeoutMS,
+                                    aSize, ComSafeArrayOutArg(aData), NULL /* rc */);
+#endif
+}
+
+HRESULT Guest::getProcessOutputInternal(ULONG aPID, ULONG aFlags, ULONG aTimeoutMS,
+                                        LONG64 aSize, ComSafeArrayOut(BYTE, aData), int *pRC)
+{
 /** @todo r=bird: Eventually we should clean up all the timeout parameters
  *        in the API and have the same way of specifying infinite waits!  */
@@ -1898,11 +2038,12 @@
     try
     {
-        VBOXGUESTCTRL_PROCESS process;
-        int vrc = processGetByPID(aPID, &process);
+        VBOXGUESTCTRL_PROCESS proc;
+        int vrc = processGetStatus(aPID, &proc);
         if (RT_FAILURE(vrc))
+        {
             rc = setError(VBOX_E_IPRT_ERROR,
-                          Guest::tr("Cannot get output from non-existent guest process (PID %u)"), aPID);
-
-        if (SUCCEEDED(rc))
+                          Guest::tr("Guest process (PID %u) does not exist"), aPID);
+        }
+        else
         {
             uint32_t uContextID = 0;
@@ -1979,5 +2120,5 @@
             if (RT_SUCCESS(vrc))
             {
-                LogFlowFunc(("Waiting for HGCM callback (timeout=%dms) ...\n", aTimeoutMS));
+                LogFlowFunc(("Waiting for HGCM callback (timeout=%RI32ms) ...\n", aTimeoutMS));
 
                 /*
@@ -1987,12 +2128,11 @@
                  */
 
-                PCALLBACKDATAEXECOUT pExecOut = NULL;
-
                 /*
                  * Wait for the first output callback notification to arrive.
                  */
-                vrc = callbackWaitForCompletion(uContextID, -1 /* No staging */, aTimeoutMS);
+                vrc = callbackWaitForCompletion(uContextID, -1 /* No staging required */, aTimeoutMS);
                 if (RT_SUCCESS(vrc))
                 {
+                    PCALLBACKDATAEXECOUT pExecOut = NULL;
                     vrc = callbackGetUserData(uContextID, NULL /* We know the type. */,
                                               (void**)&pExecOut, NULL /* Don't need the size. */);
@@ -2042,4 +2182,7 @@
             pProgress.setNull();
         }
+
+        if (pRC)
+            *pRC = vrc;
     }
     catch (std::bad_alloc &)
@@ -2068,5 +2211,5 @@
     {
         VBOXGUESTCTRL_PROCESS process;
-        int vrc = processGetByPID(aPID, &process);
+        int vrc = processGetStatus(aPID, &process);
         if (RT_SUCCESS(vrc))
         {
Index: trunk/src/VBox/Main/src-client/GuestCtrlImplDir.cpp
===================================================================
--- trunk/src/VBox/Main/src-client/GuestCtrlImplDir.cpp	(revision 39411)
+++ trunk/src/VBox/Main/src-client/GuestCtrlImplDir.cpp	(revision 39418)
@@ -126,4 +126,6 @@
                                    ComSafeArrayAsInParam(env),
                                    aUsername, aPassword,
+                                   ExecuteProcessFlag_None,
+                                   NULL, NULL,
                                    NULL /* Progress */, NULL /* PID */);
     }
@@ -301,4 +303,5 @@
     {
         return executeStreamGetNextBlock(it->second.mPID,
+                                         ProcessOutputFlag_None /* StdOut */,
                                          it->second.mStream, streamBlock);
     }
@@ -405,9 +408,12 @@
 
         ULONG uPID;
-        /** @todo Don't wait for tool to finish! Might take a lot of time! */
+        /* We only start the directory listing and requesting stdout data but don't get its
+         * data here; this is done in sequential IGuest::DirectoryRead calls then. */
         hr = executeAndWaitForTool(Bstr(VBOXSERVICE_TOOL_LS).raw(), Bstr("Opening directory").raw(),
                                    ComSafeArrayAsInParam(args),
                                    ComSafeArrayAsInParam(env),
                                    aUsername, aPassword,
+                                   ExecuteProcessFlag_WaitForStdOut,
+                                   NULL, NULL,
                                    NULL /* Progress */, &uPID);
         if (SUCCEEDED(hr))
@@ -477,38 +483,36 @@
          */
         ULONG uPID;
+        GuestCtrlStreamObjects stdOut;
         hr = executeAndWaitForTool(Bstr(VBOXSERVICE_TOOL_STAT).raw(), Bstr("Querying directory information").raw(),
                                    ComSafeArrayAsInParam(args),
                                    ComSafeArrayAsInParam(env),
                                    aUsername, aPassword,
+                                   ExecuteProcessFlag_WaitForStdOut,
+                                   &stdOut, NULL /* stdErr */,
                                    NULL /* Progress */, &uPID);
         if (SUCCEEDED(hr))
         {
-            GuestCtrlStreamObjects streamObjs;
-            hr = executeStreamParse(uPID, streamObjs);
-            if (SUCCEEDED(hr))
+            int rc = VINF_SUCCESS;
+
+            Assert(stdOut.size());
+            const char *pszFsType = stdOut[0].GetString("ftype");
+            if (!pszFsType) /* Attribute missing? */
+                 rc = VERR_NOT_FOUND;
+            if (   RT_SUCCESS(rc)
+                && strcmp(pszFsType, "d")) /* Directory? */
             {
-                int rc = VINF_SUCCESS;
-
-                Assert(streamObjs.size());
-                const char *pszFsType = streamObjs[0].GetString("ftype");
-                if (!pszFsType) /* Attribute missing? */
-                     rc = VERR_NOT_FOUND;
-                if (   RT_SUCCESS(rc)
-                    && strcmp(pszFsType, "d")) /* Directory? */
-                {
-                     rc = VERR_FILE_NOT_FOUND;
-                     /* This is not critical for Main, so don't set hr --
-                      * we will take care of rc then. */
-                }
-                if (   RT_SUCCESS(rc)
-                    && aObjInfo) /* Do we want object details? */
-                {
-                    hr = executeStreamQueryFsObjInfo(aDirectory, streamObjs[0],
-                                                     aObjInfo, enmAddAttribs);
-                }
-
-                if (pRC)
-                    *pRC = rc;
+                 rc = VERR_FILE_NOT_FOUND;
+                 /* This is not critical for Main, so don't set hr --
+                  * we will take care of rc then. */
             }
+            if (   RT_SUCCESS(rc)
+                && aObjInfo) /* Do we want object details? */
+            {
+                hr = executeStreamQueryFsObjInfo(aDirectory, stdOut[0],
+                                                 aObjInfo, enmAddAttribs);
+            }
+
+            if (pRC)
+                *pRC = rc;
         }
     }
@@ -540,22 +544,25 @@
         if (RT_SUCCESS(rc))
         {
-            ComObjPtr <GuestDirEntry> pDirEntry;
-            hr = pDirEntry.createObject();
-            ComAssertComRC(hr);
-
-            Assert(streamBlock.GetCount());
-            hr = pDirEntry->init(this, streamBlock);
-            if (SUCCEEDED(hr))
+            if (streamBlock.GetCount())
             {
-                pDirEntry.queryInterfaceTo(aDirEntry);
+                ComObjPtr <GuestDirEntry> pDirEntry;
+                hr = pDirEntry.createObject();
+                ComAssertComRC(hr);
+
+                Assert(streamBlock.GetCount());
+                hr = pDirEntry->init(this, streamBlock);
+                if (SUCCEEDED(hr))
+                {
+                    pDirEntry.queryInterfaceTo(aDirEntry);
+                }
+                else
+                    hr = setError(VBOX_E_IPRT_ERROR,
+                                  Guest::tr("Failed to init guest directory entry"));
             }
             else
-                hr = setError(VBOX_E_IPRT_ERROR,
-                              Guest::tr("Failed to init guest directory entry"));
-        }
-        else if (rc == VERR_NO_DATA)
-        {
-            /* No more directory entries to read. That's fine. */
-            hr = E_ABORT; /** @todo Find/define a better rc! */
+            {
+                /* No more directory entries to read. That's fine. */
+                hr = E_ABORT; /** @todo Find/define a better rc! */
+            }
         }
         else
Index: trunk/src/VBox/Main/src-client/GuestCtrlImplFile.cpp
===================================================================
--- trunk/src/VBox/Main/src-client/GuestCtrlImplFile.cpp	(revision 39411)
+++ trunk/src/VBox/Main/src-client/GuestCtrlImplFile.cpp	(revision 39418)
@@ -135,36 +135,34 @@
          */
         ULONG uPID;
+        GuestCtrlStreamObjects stdOut;
         hr = executeAndWaitForTool(Bstr(VBOXSERVICE_TOOL_STAT).raw(), Bstr("Querying file information").raw(),
                                    ComSafeArrayAsInParam(args),
                                    ComSafeArrayAsInParam(env),
                                    aUsername, aPassword,
+                                   ExecuteProcessFlag_WaitForStdOut,
+                                   &stdOut, NULL,
                                    NULL /* Progress */, &uPID);
         if (SUCCEEDED(hr))
         {
-            GuestCtrlStreamObjects streamObjs;
-            hr = executeStreamParse(uPID, streamObjs);
-            if (SUCCEEDED(hr))
+            int rc = VINF_SUCCESS;
+            const char *pszFsType = stdOut[0].GetString("ftype");
+            if (!pszFsType) /* Attribute missing? */
+                rc = VERR_NOT_FOUND;
+            if (   RT_SUCCESS(rc)
+                && strcmp(pszFsType, "-")) /* Regular file? */
             {
-                int rc = VINF_SUCCESS;
-                const char *pszFsType = streamObjs[0].GetString("ftype");
-                if (!pszFsType) /* Attribute missing? */
-                     rc = VERR_NOT_FOUND;
-                if (   RT_SUCCESS(rc)
-                    && strcmp(pszFsType, "-")) /* Regular file? */
-                {
-                     rc = VERR_FILE_NOT_FOUND;
-                     /* This is not critical for Main, so don't set hr --
-                      * we will take care of rc then. */
-                }
-                if (   RT_SUCCESS(rc)
-                    && aObjInfo) /* Do we want object details? */
-                {
-                    hr = executeStreamQueryFsObjInfo(aFile, streamObjs[0],
-                                                     aObjInfo, enmAddAttribs);
-                }
-
-                if (pRC)
-                    *pRC = rc;
+                rc = VERR_FILE_NOT_FOUND;
+                /* This is not critical for Main, so don't set hr --
+                 * we will take care of rc then. */
             }
+            if (   RT_SUCCESS(rc)
+                && aObjInfo) /* Do we want object details? */
+            {
+                hr = executeStreamQueryFsObjInfo(aFile, stdOut[0],
+                                                 aObjInfo, enmAddAttribs);
+            }
+
+            if (pRC)
+                *pRC = rc;
         }
     }
Index: trunk/src/VBox/Main/src-client/GuestCtrlImplTasks.cpp
===================================================================
--- trunk/src/VBox/Main/src-client/GuestCtrlImplTasks.cpp	(revision 39411)
+++ trunk/src/VBox/Main/src-client/GuestCtrlImplTasks.cpp	(revision 39418)
@@ -522,12 +522,13 @@
                  * actual copying, start the guest part now.
                  */
-                rc = pGuest->ExecuteProcess(Bstr(VBOXSERVICE_TOOL_CAT).raw(),
-                                            ExecuteProcessFlag_Hidden,
-                                            ComSafeArrayAsInParam(args),
-                                            ComSafeArrayAsInParam(env),
-                                            Bstr(aTask->strUserName).raw(),
-                                            Bstr(aTask->strPassword).raw(),
-                                            5 * 1000 /* Wait 5s for getting the process started. */,
-                                            &uPID, execProgress.asOutParam());
+                rc = pGuest->executeAndWaitForTool(Bstr(VBOXSERVICE_TOOL_CAT).raw(),
+                                                   Bstr("Copying file to host").raw(),
+                                                   ComSafeArrayAsInParam(args),
+                                                   ComSafeArrayAsInParam(env),
+                                                   Bstr(aTask->strUserName).raw(),
+                                                   Bstr(aTask->strPassword).raw(),
+                                                   ExecuteProcessFlag_WaitForStdOut,
+                                                   NULL, NULL,
+                                                   execProgress.asOutParam(), &uPID);
                 if (FAILED(rc))
                     rc = GuestTask::setProgressErrorInfo(rc, aTask->progress, pGuest);
@@ -551,41 +552,31 @@
                     size_t cbTransfered = 0;
                     SafeArray<BYTE> aOutputData(_64K);
-                    while (SUCCEEDED(execProgress->COMGETTER(Completed(&fCompleted))))
-                    {
-                        rc = this->GetProcessOutput(uPID, ProcessOutputFlag_None,
-                                                    10 * 1000 /* Timeout in ms */,
-                                                    _64K, ComSafeArrayAsOutParam(aOutputData));
+                    while (   SUCCEEDED(execProgress->COMGETTER(Completed(&fCompleted)))
+                           && !fCompleted)
+                    {
+                        rc = pGuest->GetProcessOutput(uPID, ProcessOutputFlag_None /* StdOut */,
+                                                      0 /* No timeout. */,
+                                                      _64K, ComSafeArrayAsOutParam(aOutputData));
                         if (SUCCEEDED(rc))
                         {
-                            if (!aOutputData.size())
-                            {
-                                /*
-                                 * Only bitch about an unexpected end of a file when there already
-                                 * was data read from that file. If this was the very first read we can
-                                 * be (almost) sure that this file is not meant to be read by the specified user.
-                                 */
-                                if (   cbTransfered
-                                    && cbToRead)
+                            if (aOutputData.size())
+                            {
+                                vrc = RTFileWrite(hFileDest, aOutputData.raw(), aOutputData.size(), NULL /* No partial writes */);
+                                if (RT_FAILURE(vrc))
                                 {
                                     rc = GuestTask::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
-                                                                         Guest::tr("Unexpected end of file \"%s\" (%u bytes left, %u bytes written)"),
-                                                                         aTask->strSource.c_str(), cbToRead, cbTransfered);
+                                                                         Guest::tr("Error writing to file \"%s\" (%u bytes left), rc=%Rrc"),
+                                                                         aTask->strSource.c_str(), cbToRead, vrc);
+                                    break;
                                 }
-                                break;
-                            }
-
-                            vrc = RTFileWrite(hFileDest, aOutputData.raw(), aOutputData.size(), NULL /* No partial writes */);
-                            if (RT_FAILURE(vrc))
-                            {
-                                rc = GuestTask::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
-                                                                     Guest::tr("Error writing to file \"%s\" (%u bytes left), rc=%Rrc"),
-                                                                     aTask->strSource.c_str(), cbToRead, vrc);
-                                break;
-                            }
-
-                            cbToRead -= aOutputData.size();
-                            cbTransfered += aOutputData.size();
-
-                            aTask->progress->SetCurrentOperationProgress(cbTransfered / (lFileSize / 100.0));
+
+                                cbToRead -= aOutputData.size();
+                                Assert(cbToRead >= 0);
+                                cbTransfered += aOutputData.size();
+
+                                aTask->progress->SetCurrentOperationProgress(cbTransfered / (lFileSize / 100.0));
+                            }
+
+                            /* Nothing read this time; try next round. */
                         }
                         else
@@ -596,8 +587,21 @@
                     }
 
+                    RTFileClose(hFileDest);
+
+                    if (   cbTransfered
+                        && (cbTransfered != lFileSize))
+                    {
+                        /*
+                         * Only bitch about an unexpected end of a file when there already
+                         * was data read from that file. If this was the very first read we can
+                         * be (almost) sure that this file is not meant to be read by the specified user.
+                         */
+                        rc = GuestTask::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
+                                                             Guest::tr("Unexpected end of file \"%s\" (%u bytes total, %u bytes transferred)"),
+                                                             aTask->strSource.c_str(), lFileSize, cbTransfered);
+                    }
+
                     if (SUCCEEDED(rc))
                         aTask->progress->notifyComplete(S_OK);
-
-                    RTFileClose(hFileDest);
                 }
             }
Index: trunk/src/VBox/Main/src-client/GuestImpl.cpp
===================================================================
--- trunk/src/VBox/Main/src-client/GuestImpl.cpp	(revision 39411)
+++ trunk/src/VBox/Main/src-client/GuestImpl.cpp	(revision 39418)
@@ -120,6 +120,14 @@
         AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
-        /* Clean up callback data. */
+        /* Notify left over callbacks that we are about to shutdown ... */
         CallbackMapIter it;
+        for (it = mCallbackMap.begin(); it != mCallbackMap.end(); it++)
+        {
+            int rc2 = callbackNotifyEx(it->first, VERR_CANCELLED,
+                                       Guest::tr("VM is shutting down, canceling uncompleted guest requests ..."));
+            AssertRC(rc2);
+        }
+
+        /* Destroy left over callback data. */
         for (it = mCallbackMap.begin(); it != mCallbackMap.end(); it++)
             callbackDestroy(it->first);
