Index: /trunk/doc/manual/en_US/user_VBoxManage.xml
===================================================================
--- /trunk/doc/manual/en_US/user_VBoxManage.xml	(revision 38084)
+++ /trunk/doc/manual/en_US/user_VBoxManage.xml	(revision 38085)
@@ -2880,4 +2880,62 @@
 
         <listitem>
+          <para><computeroutput>stat</computeroutput>, which displays file
+          or file system status on the guest.</para>
+
+          <screen>VBoxManage guestcontrol &lt;vmname&gt;|&lt;uuid&gt; stat
+            &lt;file element(s) to check on guest&gt;
+            [--username "&lt;name&gt;"] [--password "&lt;password&gt;"]
+            [--verbose]</screen>
+
+          <para>where the parameters mean: <glosslist>
+              <glossentry>
+                <glossterm>uuid|vmname</glossterm>
+
+                <glossdef>
+                  <para>The VM UUID or VM name. Mandatory.</para>
+                </glossdef>
+              </glossentry>
+
+              <glossentry>
+                <glossterm>file element(s) to check on guest</glossterm>
+
+                <glossdef>
+                  <para>Absolute path of directory/directories to check on
+                  guest, e.g. <computeroutput>/home/foo/a.out</computeroutput>.
+                  The specified user must have appropriate rights to access
+                  the given file element(s).</para>
+                </glossdef>
+              </glossentry>
+
+              <glossentry>
+                <glossterm>--username &lt;name&gt;</glossterm>
+
+                <glossdef>
+                  <para>Name of the user the copy process should run under.
+                  This user must exist on the guest OS.</para>
+                </glossdef>
+              </glossentry>
+
+              <glossentry>
+                <glossterm>--password &lt;password&gt;</glossterm>
+
+                <glossdef>
+                  <para>Password of the user account specified with
+                  <computeroutput>--username</computeroutput>. If not given,
+                  an empty password is assumed.</para>
+                </glossdef>
+              </glossentry>
+
+              <glossentry>
+                <glossterm>--verbose</glossterm>
+
+                <glossdef>
+                  <para>Tells VBoxManage to be more verbose.</para>
+                </glossdef>
+              </glossentry>
+            </glosslist></para>
+        </listitem>
+
+        <listitem>
           <para><computeroutput>updateadditions</computeroutput>, which allows
           for updating an already installed Guest Additions version on the
Index: /trunk/include/VBox/HostServices/GuestControlSvc.h
===================================================================
--- /trunk/include/VBox/HostServices/GuestControlSvc.h	(revision 38084)
+++ /trunk/include/VBox/HostServices/GuestControlSvc.h	(revision 38085)
@@ -88,5 +88,7 @@
  */
 #define VBOXSERVICE_TOOL_CAT        "vbox_cat"
+#define VBOXSERVICE_TOOL_LS         "vbox_ls"
 #define VBOXSERVICE_TOOL_MKDIR      "vbox_mkdir"
+#define VBOXSERVICE_TOOL_STAT       "vbox_stat"
 /** @} */
 
Index: /trunk/src/VBox/Additions/common/VBoxService/VBoxServiceControlExec.cpp
===================================================================
--- /trunk/src/VBox/Additions/common/VBoxService/VBoxServiceControlExec.cpp	(revision 38084)
+++ /trunk/src/VBox/Additions/common/VBoxService/VBoxServiceControlExec.cpp	(revision 38085)
@@ -1273,5 +1273,6 @@
         {
             fPendingClose = true;
-            VBoxServiceVerbose(4, "ControlExec: Got last input block (PID %u) of size %u ...\n", uPID, cbSize);
+            VBoxServiceVerbose(4, "ControlExec: Got last input block (PID %u) of size %u ...\n",
+                               uPID, cbSize);
         }
 
@@ -1319,5 +1320,6 @@
                                            uStatus, uFlags, (uint32_t)cbWritten);
 
-    VBoxServiceVerbose(3, "ControlExec: VBoxServiceControlExecHandleCmdSetInput returned with %Rrc\n", rc);
+    if (RT_FAILURE(rc))
+        VBoxServiceError("ControlExec: Failed to report input status! Error: %Rrc\n", rc);
     return rc;
 }
@@ -1351,4 +1353,7 @@
             if (RT_SUCCESS(rc))
             {
+                VBoxServiceVerbose(3, "ControlExec: Got output (PID %u), read=%u, handle=%u, flags=%u\n",
+                                   uPID, cbRead, uHandleID, uFlags);
+
                 /* Note: Since the context ID is unique the request *has* to be completed here,
                  *       regardless whether we got data or not! Otherwise the progress object
@@ -1358,4 +1363,7 @@
                                                 pBuf, cbRead);
             }
+            else
+                VBoxServiceError("ControlExec: Failed to retrieve output (PID %u), rc=%Rrc\n",
+                                 uPID, rc);
             RTMemFree(pBuf);
         }
@@ -1365,6 +1373,5 @@
     else
         VBoxServiceError("ControlExec: Failed to retrieve exec output command! Error: %Rrc\n", rc);
-    VBoxServiceVerbose(3, "ControlExec: VBoxServiceControlExecHandleCmdGetOutput returned with %Rrc\n", rc);
-    return rc;
-}
-
+    return rc;
+}
+
Index: /trunk/src/VBox/Additions/common/VBoxService/VBoxServiceControlExecThread.cpp
===================================================================
--- /trunk/src/VBox/Additions/common/VBoxService/VBoxServiceControlExecThread.cpp	(revision 38084)
+++ /trunk/src/VBox/Additions/common/VBoxService/VBoxServiceControlExecThread.cpp	(revision 38085)
@@ -273,5 +273,6 @@
                                           uint8_t *pBuf, uint32_t cbSize, uint32_t *pcbRead)
 {
-    AssertPtrReturn(pBuf, VERR_INVALID_PARAMETER);
+    AssertPtrReturn(pBuf, VERR_INVALID_POINTER);
+    AssertReturn(cbSize, VERR_INVALID_PARAMETER);
 
     int rc = RTCritSectEnter(&g_GuestControlExecThreadsCritSect);
Index: /trunk/src/VBox/Additions/common/VBoxService/VBoxServicePipeBuf.cpp
===================================================================
--- /trunk/src/VBox/Additions/common/VBoxService/VBoxServicePipeBuf.cpp	(revision 38084)
+++ /trunk/src/VBox/Additions/common/VBoxService/VBoxServicePipeBuf.cpp	(revision 38085)
@@ -112,4 +112,9 @@
                 AssertRC(rc);
             }
+
+#ifdef DEBUG_andy
+            VBoxServiceVerbose(4, "PipeBuf[0x%p]: read=%u, size=%u, alloc=%u, off=%u\n",
+                                   pBuf, *pcbToRead, pBuf->cbSize, pBuf->cbAllocated, pBuf->cbOffset);
+#endif
         }
         else
@@ -269,4 +274,9 @@
                     AssertRC(rc);
                 }
+
+#ifdef DEBUG_andy
+                VBoxServiceVerbose(4, "PipeBuf[0x%p]: written=%u, size=%u, alloc=%u, off=%u\n",
+                                   pBuf, cbData, pBuf->cbSize, pBuf->cbAllocated, pBuf->cbOffset);
+#endif
             }
         }
Index: /trunk/src/VBox/Frontends/VBoxManage/VBoxManageGuestCtrl.cpp
===================================================================
--- /trunk/src/VBox/Frontends/VBoxManage/VBoxManageGuestCtrl.cpp	(revision 38084)
+++ /trunk/src/VBox/Frontends/VBoxManage/VBoxManageGuestCtrl.cpp	(revision 38085)
@@ -69,4 +69,13 @@
 static volatile bool    g_fGuestCtrlCanceled = false;
 
+typedef struct COPYCONTEXT
+{
+    IGuest *pGuest;
+    bool fVerbose;
+    bool fHostToGuest;
+    char *pszUsername;
+    char *pszPassword;
+} COPYCONTEXT, *PCOPYCONTEXT;
+
 /**
  * An entry for a source element, including an optional filter.
@@ -100,5 +109,5 @@
 
 /**
- * An entry for an element which needs to be copied to the guest.
+ * An entry for an element which needs to be copied/created to/on the guest.
  */
 typedef struct DESTFILEENTRY
@@ -187,11 +196,8 @@
                  "                            [--timeout <msec>] [--unix2dos] [--verbose]\n"
                  "                            [--wait-exit] [--wait-stdout] [--wait-stderr]\n"
-                 //"                          [--output-format=<dos>|<unix>]\n"
-                 "                            [--output-type=<binary>|<text>]\n"
                  "                            [-- [<argument1>] ... [<argumentN>]]\n"
                  /** @todo Add a "--" parameter (has to be last parameter) to directly execute
                   *        stuff, e.g. "VBoxManage guestcontrol execute <VMName> --username <> ... -- /bin/rm -Rf /foo". */
                  "\n"
-#if 0
                  "                            copyfrom\n"
                  "                            <source on guest> <destination on host>\n"
@@ -199,5 +205,4 @@
                  "                            [--dryrun] [--follow] [--recursive] [--verbose]\n"
                  "\n"
-#endif
                  "                            copyto|cp\n"
                  "                            <source on host> <destination on guest>\n"
@@ -206,7 +211,12 @@
                  "\n"
                  "                            createdir[ectory]|mkdir|md\n"
-                 "                            <directory to create on guest>\n"
+                 "                            <director[y|ies] to create on guest>\n"
                  "                            --username <name> --password <password>\n"
                  "                            [--parents] [--mode <mode>] [--verbose]\n"
+                 "\n"
+                 "                            stat\n"
+                 "                            <file element(s) to check on guest>\n"
+                 "                            --username <name> --password <password>\n"
+                 "                            [--verbose]\n"
                  "\n"
                  "                            updateadditions\n"
@@ -572,4 +582,8 @@
     if (Utf8UserName.isEmpty())
         return errorSyntax(USAGE_GUESTCONTROL, "No user name specified!");
+
+    /* Any output conversion not supported yet! */
+    if (eOutputType != OUTPUTTYPE_UNDEFINED)
+        return errorSyntax(USAGE_GUESTCONTROL, "Output conversion not implemented yet!");
 
     /*
@@ -685,30 +699,26 @@
                         *        generic problem and the new VFS APIs will handle it more
                         *        transparently. (requires writing dos2unix/unix2dos filters ofc) */
-                        if (eOutputType != OUTPUTTYPE_UNDEFINED)
+
+                        /*
+                         * If aOutputData is text data from the guest process' stdout or stderr,
+                         * it has a platform dependent line ending. So standardize on
+                         * Unix style, as RTStrmWrite does the LF -> CR/LF replacement on
+                         * Windows. Otherwise we end up with CR/CR/LF on Windows.
+                         */
+                        ULONG cbOutputDataPrint = cbOutputData;
+                        for (BYTE *s = aOutputData.raw(), *d = s;
+                             s - aOutputData.raw() < (ssize_t)cbOutputData;
+                             s++, d++)
                         {
-                            /*
-                             * If aOutputData is text data from the guest process' stdout or stderr,
-                             * it has a platform dependent line ending. So standardize on
-                             * Unix style, as RTStrmWrite does the LF -> CR/LF replacement on
-                             * Windows. Otherwise we end up with CR/CR/LF on Windows.
-                             */
-                            ULONG cbOutputDataPrint = cbOutputData;
-                            for (BYTE *s = aOutputData.raw(), *d = s;
-                                 s - aOutputData.raw() < (ssize_t)cbOutputData;
-                                 s++, d++)
+                            if (*s == '\r')
                             {
-                                if (*s == '\r')
-                                {
-                                    /* skip over CR, adjust destination */
-                                    d--;
-                                    cbOutputDataPrint--;
-                                }
-                                else if (s != d)
-                                    *d = *s;
+                                /* skip over CR, adjust destination */
+                                d--;
+                                cbOutputDataPrint--;
                             }
-                            RTStrmWrite(g_pStdOut, aOutputData.raw(), cbOutputDataPrint);
+                            else if (s != d)
+                                *d = *s;
                         }
-                        else /* Just dump all data as we got it ... */
-                            RTStrmWrite(g_pStdOut, aOutputData.raw(), cbOutputData);
+                        RTStrmWrite(g_pStdOut, aOutputData.raw(), cbOutputDataPrint);
                     }
                 }
@@ -790,16 +800,246 @@
 }
 
-/** @todo Clean up too long parameter list -> move guest specific stuff into own struct etc! */
-static int ctrlCopyDirectoryReadGuest(IGuest *pGuest,
-                                      const char *pszUsername, const char *pszPassword,
-                                      const char *pszRootDir, const char *pszSubDir,
-                                      const char *pszFilter, const char *pszDest,
-                                      uint32_t fFlags, uint32_t *pcObjects, DESTDIRMAP &dirMap)
-{
-    AssertPtrReturn(pszRootDir, VERR_INVALID_POINTER);
+static int ctrlCopyContextCreate(IGuest *pGuest, bool fVerbose, bool fHostToGuest,
+                                 const char *pszUsername, const char *pszPassword,
+                                 PCOPYCONTEXT *ppContext)
+{
+    AssertPtrReturn(pGuest, VERR_INVALID_POINTER);
+    AssertPtrReturn(pszUsername, VERR_INVALID_POINTER);
+    AssertPtrReturn(pszPassword, VERR_INVALID_POINTER);
+
+    PCOPYCONTEXT pContext = (PCOPYCONTEXT)RTMemAlloc(sizeof(COPYCONTEXT));
+    AssertPtrReturn(pContext, VERR_NO_MEMORY);
+    pContext->pGuest = pGuest;
+    pContext->fVerbose = fVerbose;
+    pContext->fHostToGuest = fHostToGuest;
+
+    pContext->pszUsername = RTStrDup(pszUsername);
+    if (!pContext->pszUsername)
+    {
+        RTMemFree(pContext);
+        return VERR_NO_MEMORY;
+    }
+
+    pContext->pszPassword = RTStrDup(pszPassword);
+    if (!pContext->pszPassword)
+    {
+        RTStrFree(pContext->pszUsername);
+        RTMemFree(pContext);
+        return VERR_NO_MEMORY;
+    }
+
+    *ppContext = pContext;
+
+    return VINF_SUCCESS;
+}
+
+static void ctrlCopyContextFree(PCOPYCONTEXT pContext)
+{
+    if (pContext)
+    {
+        RTStrFree(pContext->pszUsername);
+        RTStrFree(pContext->pszPassword);
+        RTMemFree(pContext);
+    }
+}
+
+/*
+ * Source          Dest                 Translated
+ * c:\foo.txt      c:\from_host         c:\from_host\foo.txt
+ * c:\asdf\foo     c:\qwer              c:\qwer\foo
+ * c:\bar\baz.txt  d:\users\            d:\users\baz.txt
+ * c:\*.dll        e:\baz               e:\baz
+ */
+static int ctrlCopyTranslatePath(PCOPYCONTEXT pContext,
+                                 const char *pszSourceRoot, const char *pszSource,
+                                 const char *pszDest, char **ppszTranslated)
+{
+    AssertPtrReturn(pContext, VERR_INVALID_POINTER);
+    AssertPtrReturn(pszSourceRoot, VERR_INVALID_POINTER);
+    AssertPtrReturn(pszSource, VERR_INVALID_POINTER);
+    AssertPtrReturn(pszDest, VERR_INVALID_POINTER);
+    AssertPtrReturn(ppszTranslated, VERR_INVALID_POINTER);
+
+    /* Source path must contain the source root! */
+    if (!RTPathStartsWith(pszSource, pszSourceRoot))
+        return VERR_INVALID_PARAMETER;
+
+    /* Construct the relative dest destination path by "subtracting" the
+     * source from the source root, e.g.
+     *
+     * source root path = "e:\foo\", source = "e:\foo\bar"
+     * dest = "d:\baz\"
+     * translated = "d:\baz\bar\"
+     */
+
+    size_t lenRoot = strlen(pszSourceRoot);
+    AssertReturn(lenRoot, VERR_INVALID_PARAMETER);
+    char *pszTranslated = RTStrDup(pszDest);
+    AssertReturn(pszTranslated, VERR_NO_MEMORY);
+    int vrc = RTStrAAppend(&pszTranslated, &pszSource[lenRoot]);
+    if (RT_FAILURE(vrc))
+        return vrc;
+
+    *ppszTranslated = pszTranslated;
+
+#ifdef DEBUG
+    if (pContext->fVerbose)
+        RTPrintf("Translating root=%s, source=%s, dest=%s -> %s\n",
+                 pszSourceRoot, pszSource, pszDest, *ppszTranslated);
+#endif
+
+    return vrc;
+}
+
+static int ctrlCopyDirCreate(PCOPYCONTEXT pContext, const char *pszDir)
+{
+    AssertPtrReturn(pContext, VERR_INVALID_POINTER);
+    AssertPtrReturn(pszDir, VERR_INVALID_POINTER);
+
+    int rc = VINF_SUCCESS;
+    if (pContext->fHostToGuest) /* We want to create directories on the guest. */
+    {
+        HRESULT hrc = pContext->pGuest->DirectoryCreate(Bstr(pszDir).raw(),
+                                                        Bstr(pContext->pszUsername).raw(), Bstr(pContext->pszPassword).raw(),
+                                                        700, DirectoryCreateFlag_Parents);
+        if (FAILED(hrc))
+            rc = ctrlPrintError(pContext->pGuest, COM_IIDOF(IGuest));
+    }
+    else /* ... or on the host. */
+    {
+        rc = RTDirCreate(pszDir, 700);
+        if (rc == VERR_ALREADY_EXISTS)
+            rc = VINF_SUCCESS;
+    }
+    return rc;
+}
+
+static int ctrlCopyDirExists(PCOPYCONTEXT pContext, bool bGuest,
+                             const char *pszDir, bool *fExists)
+{
+    AssertPtrReturn(pContext, false);
+    AssertPtrReturn(pszDir, false);
+    AssertPtrReturn(fExists, false);
+
+    int rc = VINF_SUCCESS;
+    if (bGuest)
+    {
+        BOOL fDirExists = FALSE;
+        HRESULT hr = pContext->pGuest->FileExists(Bstr(pszDir).raw(),
+                                                  Bstr(pContext->pszUsername).raw(),
+                                                  Bstr(pContext->pszPassword).raw(), &fDirExists);
+        if (FAILED(hr))
+            rc = ctrlPrintError(pContext->pGuest, COM_IIDOF(IGuest));
+        else
+            *fExists = fDirExists ? true : false;
+    }
+    else
+        *fExists = RTDirExists(pszDir);
+    return rc;
+}
+
+static int ctrlCopyDirExistsOnDest(PCOPYCONTEXT pContext, const char *pszDir,
+                                   bool *fExists)
+{
+    return ctrlCopyDirExists(pContext, pContext->fHostToGuest,
+                             pszDir, fExists);
+}
+
+static int ctrlCopyDirExistsOnSource(PCOPYCONTEXT pContext, const char *pszDir,
+                                     bool *fExists)
+{
+    return ctrlCopyDirExists(pContext, !pContext->fHostToGuest,
+                             pszDir, fExists);
+}
+
+static int ctrlCopyFileExists(PCOPYCONTEXT pContext, bool bOnGuest,
+                              const char *pszFile, bool *fExists)
+{
+    AssertPtrReturn(pContext, false);
+    AssertPtrReturn(pszFile, false);
+    AssertPtrReturn(fExists, false);
+
+    int rc = VINF_SUCCESS;
+    if (bOnGuest)
+    {
+        BOOL fFileExists = FALSE;
+        HRESULT hr = pContext->pGuest->FileExists(Bstr(pszFile).raw(),
+                                                  Bstr(pContext->pszUsername).raw(),
+                                                  Bstr(pContext->pszPassword).raw(), &fFileExists);
+        if (FAILED(hr))
+            rc = ctrlPrintError(pContext->pGuest, COM_IIDOF(IGuest));
+        else
+            *fExists = fFileExists ? true : false;
+    }
+    else
+        *fExists = RTFileExists(pszFile);
+    return rc;
+}
+
+static int ctrlCopyFileExistsOnDest(PCOPYCONTEXT pContext, const char *pszFile,
+                                    bool *fExists)
+{
+    return ctrlCopyFileExists(pContext, pContext->fHostToGuest,
+                              pszFile, fExists);
+}
+
+static int ctrlCopyFileExistsOnSource(PCOPYCONTEXT pContext, const char *pszFile,
+                                      bool *fExists)
+{
+    return ctrlCopyFileExists(pContext, !pContext->fHostToGuest,
+                              pszFile, fExists);
+}
+
+static int ctrlCopyFileToTarget(PCOPYCONTEXT pContext, const char *pszFileSource,
+                                const char *pszFileDest, uint32_t fFlags)
+{
+    AssertPtrReturn(pContext, VERR_INVALID_POINTER);
+    AssertPtrReturn(pszFileSource, VERR_INVALID_POINTER);
+    AssertPtrReturn(pszFileDest, VERR_INVALID_POINTER);
+
+    if (pContext->fVerbose)
+    {
+        RTPrintf("Copying \"%s\" to \"%s\" ...\n",
+                 pszFileSource, pszFileDest);
+    }
+
+    int vrc = VINF_SUCCESS;
+    ComPtr<IProgress> progress;
+    HRESULT hr;
+    if (pContext->fHostToGuest)
+    {
+        hr = pContext->pGuest->CopyToGuest(Bstr(pszFileSource).raw(), Bstr(pszFileDest).raw(),
+                                           Bstr(pContext->pszUsername).raw(), Bstr(pContext->pszPassword).raw(),
+                                           fFlags, progress.asOutParam());
+    }
+    else
+    {
+        hr = pContext->pGuest->CopyFromGuest(Bstr(pszFileSource).raw(), Bstr(pszFileDest).raw(),
+                                             Bstr(pContext->pszUsername).raw(), Bstr(pContext->pszPassword).raw(),
+                                             fFlags, progress.asOutParam());
+    }
+
+    if (FAILED(hr))
+        vrc = ctrlPrintError(pContext->pGuest, COM_IIDOF(IGuest));
+    else
+    {
+        hr = showProgress(progress);
+        if (FAILED(hr))
+            vrc = ctrlPrintProgressError(progress);
+    }
+
+    return vrc;
+}
+
+static int ctrlCopyDirToGuest(PCOPYCONTEXT pContext,
+                              const char *pszSource, const char *pszFilter,
+                              const char *pszDest, uint32_t fFlags,
+                              const char *pszSubDir /* For recursion */)
+{
+    AssertPtrReturn(pContext, VERR_INVALID_POINTER);
+    AssertPtrReturn(pszSource, VERR_INVALID_POINTER);
+    /* Filter is optional. */
+    AssertPtrReturn(pszDest, VERR_INVALID_POINTER);
     /* Sub directory is optional. */
-    /* Filter directory is optional. */
-    AssertPtrReturn(pszDest, VERR_INVALID_POINTER);
-    AssertPtrReturn(pcObjects, VERR_INVALID_POINTER);
 
     /*
@@ -807,128 +1047,11 @@
      */
     char szCurDir[RTPATH_MAX];
-    int rc = RTStrCopy(szCurDir, sizeof(szCurDir), pszRootDir);
-    if (RT_SUCCESS(rc) && pszSubDir != NULL)
+    int rc = RTStrCopy(szCurDir, sizeof(szCurDir), pszSource);
+    if (RT_SUCCESS(rc) && pszSubDir)
         rc = RTPathAppend(szCurDir, sizeof(szCurDir), pszSubDir);
 
-    if (RT_SUCCESS(rc))
-    {
-        ULONG uDirHandle;
-        HRESULT hr = pGuest->DirectoryOpen(Bstr(szCurDir).raw(), Bstr(pszFilter).raw(), fFlags,
-                                           Bstr(pszUsername).raw(), Bstr(pszPassword).raw(), &uDirHandle);
-        if (FAILED(hr))
-            rc = ctrlPrintError(pGuest, COM_IIDOF(IGuest));
-        else
-        {
-            ComPtr <IGuestDirEntry> dirEntry;
-            while (SUCCEEDED(hr = pGuest->DirectoryRead(uDirHandle, dirEntry.asOutParam())))
-            {
-                GuestDirEntryType_T enmType;
-                dirEntry->COMGETTER(Type)(&enmType);
-
-                Bstr strName;
-                dirEntry->COMGETTER(Name)(strName.asOutParam());
-
-                switch (enmType)
-                {
-                    case GuestDirEntryType_Directory:
-                    {
-                        /* Skip "." and ".." entries. */
-                        if (   !strName.compare(Bstr("."))
-                            || !strName.compare(Bstr("..")))
-                            break;
-
-                        const char *pszName = Utf8Str(strName).c_str();
-                        if (fFlags & CopyFileFlag_Recursive)
-                        {
-                            char *pszNewSub = NULL;
-                            if (pszSubDir)
-                                RTStrAPrintf(&pszNewSub, "%s/%s", pszSubDir, pszName);
-                            else
-                                RTStrAPrintf(&pszNewSub, "%s", pszName);
-
-                            if (pszNewSub)
-                            {
-                                dirMap[pszNewSub];
-
-                                rc = ctrlCopyDirectoryReadGuest(pGuest, pszUsername, pszPassword,
-                                                                pszRootDir, pszNewSub,
-                                                                pszFilter, pszDest,
-                                                                fFlags, pcObjects, dirMap);
-                                RTStrFree(pszNewSub);
-                            }
-                            else
-                                rc = VERR_NO_MEMORY;
-                        }
-                        break;
-                    }
-
-                    case GuestDirEntryType_Symlink:
-                        if (   (fFlags & CopyFileFlag_Recursive)
-                            && (fFlags & CopyFileFlag_FollowLinks))
-                        {
-                            /* Fall through to next case is intentional. */
-                        }
-                        else
-                            break;
-
-                    case GuestDirEntryType_File:
-                    {
-                        const char *pszName = Utf8Str(strName).c_str();
-                        if (   !pszFilter
-                            || RTStrSimplePatternMatch(pszFilter, pszName))
-                        {
-                            dirMap[pszSubDir].push_back(DESTFILEENTRY(pszName));
-                            *pcObjects += 1;
-                        }
-                        break;
-                    }
-
-                    default:
-                        break;
-                }
-            }
-
-            hr = pGuest->DirectoryClose(uDirHandle);
-            if (FAILED(rc))
-                rc = ctrlPrintError(pGuest, COM_IIDOF(IGuest));
-        }
-    }
-    return rc;
-}
-
-/**
- * Reads a specified directory (recursively) based on the copy flags
- * and appends all matching entries to the supplied list.
- *
- * @return  IPRT status code.
- * @param   pszRootDir          Directory to start with. Must end with
- *                              a trailing slash and must be absolute.
- * @param   pszSubDir           Sub directory part relative to the root
- *                              directory; needed for recursion.
- * @param   pszFilter           Search filter (e.g. *.pdf).
- * @param   pszDest             Destination directory.
- * @param   fFlags              Copy flags.
- * @param   pcObjects           Where to store the overall objects to
- *                              copy found.
- * @param   dirMap              Reference to destination directory map to store found
- *                              directories (primary key) + files (secondary key, vector).
- */
-static int ctrlCopyDirectoryReadHost(const char *pszRootDir, const char *pszSubDir,
-                                     const char *pszFilter, const char *pszDest,
-                                     uint32_t fFlags, uint32_t *pcObjects, DESTDIRMAP &dirMap)
-{
-    AssertPtrReturn(pszRootDir, VERR_INVALID_POINTER);
-    /* Sub directory is optional. */
-    /* Filter directory is optional. */
-    AssertPtrReturn(pszDest, VERR_INVALID_POINTER);
-    AssertPtrReturn(pcObjects, VERR_INVALID_POINTER);
-
-    /*
-     * Construct current path.
-     */
-    char szCurDir[RTPATH_MAX];
-    int rc = RTStrCopy(szCurDir, sizeof(szCurDir), pszRootDir);
-    if (RT_SUCCESS(rc) && pszSubDir != NULL)
-        rc = RTPathAppend(szCurDir, sizeof(szCurDir), pszSubDir);
+    /* Flag indicating whether the current directory was created on the
+     * target or not. */
+    bool fDirCreated = false;
 
     /*
@@ -977,9 +1100,7 @@
                         if (pszNewSub)
                         {
-                            dirMap[pszNewSub];
-
-                            rc = ctrlCopyDirectoryReadHost(pszRootDir, pszNewSub,
-                                                           pszFilter, pszDest,
-                                                           fFlags, pcObjects, dirMap);
+                            rc = ctrlCopyDirToGuest(pContext,
+                                                    pszSource, pszFilter,
+                                                    pszDest, fFlags, pszNewSub);
                             RTStrFree(pszNewSub);
                         }
@@ -1004,6 +1125,36 @@
                         || RTStrSimplePatternMatch(pszFilter, DirEntry.szName))
                     {
-                        dirMap[pszSubDir].push_back(DESTFILEENTRY(Utf8Str(DirEntry.szName)));
-                        *pcObjects += 1;
+                        if (!fDirCreated)
+                        {
+                            char *pszDestDir;
+                            rc = ctrlCopyTranslatePath(pContext, pszSource, szCurDir,
+                                                       pszDest, &pszDestDir);
+                            if (RT_SUCCESS(rc))
+                            {
+                                rc = ctrlCopyDirCreate(pContext, pszDestDir);
+                                RTStrFree(pszDestDir);
+
+                                fDirCreated = true;
+                            }
+                        }
+
+                        if (RT_SUCCESS(rc))
+                        {
+                            char *pszFileSource;
+                            if (RTStrAPrintf(&pszFileSource, "%s/%s",
+                                             szCurDir, DirEntry.szName))
+                            {
+                                char *pszFileDest;
+                                rc = ctrlCopyTranslatePath(pContext, pszSource, pszFileSource,
+                                                           pszDest, &pszFileDest);
+                                if (RT_SUCCESS(rc))
+                                {
+                                    rc = ctrlCopyFileToTarget(pContext, pszFileSource,
+                                                              pszFileDest, 0 /* Flags? */);
+                                    RTStrFree(pszFileDest);
+                                }
+                                RTStrFree(pszFileSource);
+                            }
+                        }
                     }
                     break;
@@ -1022,265 +1173,188 @@
 }
 
-/**
- * Constructs a destinations map from a source entry and a destination root.
- *
- * @return  IPRT status code.
- * @param   fHostToGuest
- * @param   sourceEntry             Reference to a specified source entry to use.
- * @param   fFlags                  Copy file flags. Needed for recursive directory parsing.
- * @param   pszDestRoot             Pointer to destination root. This can be used to add one or
- *                                  more directories to the actual destination path.
- * @param   mapDest                 Reference to the destination map for storing the actual result.
- * @param   pcObjects               Pointer to a total object (file) count to copy.
- */
-static int ctrlCopyConstructDestinationsForGuest(SOURCEFILEENTRY &sourceEntry, uint32_t fFlags,
-                                                 const char *pszDestRoot, DESTDIRMAP &mapDest,
-                                                 uint32_t *pcObjects)
-{
-    int rc = VINF_SUCCESS;
-    const char *pszSource = sourceEntry.mSource.c_str();
-
-    if (   RTPathFilename(pszSource)
-        && RTFileExists(pszSource))
-    {
-        /* Source is a single file. */
-        char *pszFileName = RTPathFilename(pszSource);
-        mapDest[Utf8Str("")].push_back(DESTFILEENTRY(pszFileName));
-
-        *pcObjects += 1;
-    }
+static int ctrlCopyDirToHost(PCOPYCONTEXT pContext,
+                             const char *pszSource, const char *pszFilter,
+                             const char *pszDest, uint32_t fFlags,
+                             const char *pszSubDir /* For recursion */)
+{
+    AssertPtrReturn(pContext, VERR_INVALID_POINTER);
+    AssertPtrReturn(pszSource, VERR_INVALID_POINTER);
+    /* Filter is optional. */
+    AssertPtrReturn(pszDest, VERR_INVALID_POINTER);
+    /* Sub directory is optional. */
+
+    /*
+     * Construct current path.
+     */
+    char szCurDir[RTPATH_MAX];
+    int rc = RTStrCopy(szCurDir, sizeof(szCurDir), pszSource);
+    if (RT_SUCCESS(rc) && pszSubDir)
+        rc = RTPathAppend(szCurDir, sizeof(szCurDir), pszSubDir);
+
+    /* Flag indicating whether the current directory was created on the
+     * target or not. */
+    bool fDirCreated = false;
+
+    ULONG uDirHandle;
+    HRESULT hr = pContext->pGuest->DirectoryOpen(Bstr(szCurDir).raw(), Bstr(pszFilter).raw(),
+                                                 fFlags,
+                                                 Bstr(pContext->pszUsername).raw(), Bstr(pContext->pszPassword).raw(),
+                                                 &uDirHandle);
+    if (FAILED(hr))
+        rc = ctrlPrintError(pContext->pGuest, COM_IIDOF(IGuest));
     else
     {
-        /* Source is either a directory or a filter (e.g. *.dll). */
-        rc = ctrlCopyDirectoryReadHost(pszSource,
-                                       NULL /* pszSubDir */,
-                                       sourceEntry.mFilter.isEmpty() ? NULL : sourceEntry.mFilter.c_str(),
-                                       pszDestRoot, fFlags, pcObjects, mapDest);
-    }
+        ComPtr <IGuestDirEntry> dirEntry;
+        while (SUCCEEDED(hr = pContext->pGuest->DirectoryRead(uDirHandle, dirEntry.asOutParam())))
+        {
+            GuestDirEntryType_T enmType;
+            dirEntry->COMGETTER(Type)(&enmType);
+
+            Bstr strName;
+            dirEntry->COMGETTER(Name)(strName.asOutParam());
+
+            switch (enmType)
+            {
+                case GuestDirEntryType_Directory:
+                {
+                    /* Skip "." and ".." entries. */
+                    if (   !strName.compare(Bstr("."))
+                        || !strName.compare(Bstr("..")))
+                        break;
+
+                    if (fFlags & CopyFileFlag_Recursive)
+                    {
+                        Utf8Str strDir(strName);
+                        char *pszNewSub = NULL;
+                        if (pszSubDir)
+                            RTStrAPrintf(&pszNewSub, "%s/%s", pszSubDir, strDir.c_str());
+                        else
+                            RTStrAPrintf(&pszNewSub, "%s", strDir.c_str());
+
+                        if (pszNewSub)
+                        {
+                            rc = ctrlCopyDirToHost(pContext,
+                                                   pszSource, pszFilter,
+                                                   pszDest, fFlags, pszNewSub);
+                            RTStrFree(pszNewSub);
+                        }
+                        else
+                            rc = VERR_NO_MEMORY;
+                    }
+                    break;
+                }
+
+                case GuestDirEntryType_Symlink:
+                    if (   (fFlags & CopyFileFlag_Recursive)
+                        && (fFlags & CopyFileFlag_FollowLinks))
+                    {
+                        /* Fall through to next case is intentional. */
+                    }
+                    else
+                        break;
+
+                case GuestDirEntryType_File:
+                {
+                    const char *pszName = Utf8Str(strName).c_str();
+                    if (   !pszFilter
+                        || RTStrSimplePatternMatch(pszFilter, pszName))
+                    {
+                        if (!fDirCreated)
+                        {
+                            char *pszDestDir;
+                            rc = ctrlCopyTranslatePath(pContext, pszSource, szCurDir,
+                                                       pszDest, &pszDestDir);
+                            if (RT_SUCCESS(rc))
+                            {
+                                rc = ctrlCopyDirCreate(pContext, pszDestDir);
+                                RTStrFree(pszDestDir);
+
+                                fDirCreated = true;
+                            }
+                        }
+
+                        if (RT_SUCCESS(rc))
+                        {
+                            Utf8Str strDir(strName);
+                            char *pszFileSource;
+                            if (RTStrAPrintf(&pszFileSource, "%s/%s",
+                                             szCurDir, strDir.c_str()))
+                            {
+                                char *pszFileDest;
+                                rc = ctrlCopyTranslatePath(pContext, pszSource, pszFileSource,
+                                                           pszDest, &pszFileDest);
+                                if (RT_SUCCESS(rc))
+                                {
+                                    rc = ctrlCopyFileToTarget(pContext, pszFileSource,
+                                                              pszFileDest, 0 /* Flags? */);
+                                    RTStrFree(pszFileDest);
+                                }
+                                RTStrFree(pszFileSource);
+                            }
+                        }
+                    }
+                    break;
+                }
+
+                default:
+                    break;
+            }
+        }
+
+        hr = pContext->pGuest->DirectoryClose(uDirHandle);
+        if (FAILED(rc))
+            rc = ctrlPrintError(pContext->pGuest, COM_IIDOF(IGuest));
+    }
+
     return rc;
 }
 
-static int ctrlCopyConstructDestinationsForHost(IGuest *pGuest,
-                                                const char *pszUsername, const char *pszPassword,
-                                                SOURCEFILEENTRY &sourceEntry, uint32_t fFlags,
-                                                const char *pszDestRoot, DESTDIRMAP &mapDest,
-                                                uint32_t *pcObjects)
-{
-    int rc = VINF_SUCCESS;
-    const char *pszSource = sourceEntry.mSource.c_str();
-
-    BOOL fExists = FALSE;
-    HRESULT hr = pGuest->FileExists(Bstr(pszSource).raw(),
-                                    Bstr(pszUsername).raw(), Bstr(pszPassword).raw(), &fExists);
-    if (FAILED(rc))
-        rc = ctrlPrintError(pGuest, COM_IIDOF(IGuest));
+static int ctrlCopyDirToTarget(PCOPYCONTEXT pContext,
+                               const char *pszSource, const char *pszFilter,
+                               const char *pszDest, uint32_t fFlags,
+                               const char *pszSubDir /* For recursion */)
+{
+    if (pContext->fHostToGuest)
+        return ctrlCopyDirToGuest(pContext, pszSource, pszFilter,
+                                  pszDest, fFlags, pszSubDir);
+    return ctrlCopyDirToHost(pContext, pszSource, pszFilter,
+                             pszDest, fFlags, pszSubDir);
+}
+
+static int ctrlCopyCreateSourceRoot(const char *pszSource, char **ppszSourceRoot)
+{
+    AssertPtrReturn(pszSource, VERR_INVALID_POINTER);
+    AssertPtrReturn(ppszSourceRoot, VERR_INVALID_POINTER);
+
+    char *pszNewRoot = RTStrDup(pszSource);
+    AssertPtrReturn(pszNewRoot, VERR_NO_MEMORY);
+
+    size_t lenRoot = strlen(pszNewRoot);
+    if (   lenRoot
+        && pszNewRoot[lenRoot - 1] == '/'
+        && pszNewRoot[lenRoot - 1] == '\\'
+        && lenRoot > 1
+        && pszNewRoot[lenRoot - 2] == '/'
+        && pszNewRoot[lenRoot - 2] == '\\')
+    {
+        *ppszSourceRoot = pszNewRoot;
+        if (lenRoot > 1)
+            *ppszSourceRoot[lenRoot - 2] = '\0';
+        *ppszSourceRoot[lenRoot - 1] = '\0';
+    }
     else
     {
-        if (fExists)
-        {
-            /* Source is a single file. */
-            char *pszFileName = RTPathFilename(pszSource);
-            mapDest[Utf8Str(pszDestRoot)].push_back(DESTFILEENTRY(pszFileName));
-
-            *pcObjects++;
-        }
-        else
-        {
-            /* Source is either a directory or a filter (e.g. *.dll). */
-            rc = ctrlCopyDirectoryReadGuest(pGuest, pszUsername, pszPassword,
-                                            pszSource, NULL /* pszSubDir */,
-                                            sourceEntry.mFilter.isEmpty() ? NULL : sourceEntry.mFilter.c_str(),
-                                            pszDestRoot, fFlags, pcObjects, mapDest);
-        }
-    }
-    return rc;
-}
-
-/**
- * Prepares the destination directory hirarchy on the guest side by creating the directories
- * and sets the appropriate access rights.
- *
- * @return  IPRT status code.
- * @param   pGuest                  IGuest interface pointer.
- * @param   fHostToGuest
- * @param   itDest                  Destination map iterator to process.
- * @param   pszDestRoot             Destination root to use.
- * @param   pszUsername             Username to use.
- * @param   pszPassword             Password to use.
- */
-static int ctrlCopyPrepareDestDirectory(IGuest *pGuest, bool fHostToGuest,
-                                        const char *pszDestRoot, const char *pszDestSub,
-                                        const char *pszUsername, const char *pszPassword)
-{
-    AssertPtrReturn(pGuest, VERR_INVALID_POINTER);
-    AssertPtrReturn(pszDestRoot, VERR_INVALID_POINTER);
-    AssertPtrReturn(pszDestSub, VERR_INVALID_POINTER);
-    AssertPtrReturn(pszUsername, VERR_INVALID_POINTER);
-    AssertPtrReturn(pszPassword, VERR_INVALID_POINTER);
-
-    char *pszDestFinal = NULL;
-    int rc = VINF_SUCCESS;
-
-    /* Create root directory (= empty name) and skip the rest for
-     * this round. */
-    if (!strlen(pszDestSub))
-    {
-        pszDestFinal = RTStrDup(pszDestRoot);
-        if (!pszDestFinal)
-            rc = VERR_NO_MEMORY;
-    }
-    else /* Create sub-directories, also empty ones. */
-    {
-        if (!RTStrAPrintf(&pszDestFinal, "%s/%s", pszDestRoot, pszDestSub))
-            rc = VERR_NO_MEMORY;
-    }
-
-    if (RT_SUCCESS(rc) && pszDestFinal)
-    {
-        if (fHostToGuest) /* We want to create directories on the guest. */
-        {
-            HRESULT hrc = pGuest->DirectoryCreate(Bstr(pszDestFinal).raw(),
-                                                  Bstr(pszUsername).raw(), Bstr(pszPassword).raw(),
-                                                  700, DirectoryCreateFlag_Parents);
-            if (FAILED(hrc))
-                rc = ctrlPrintError(pGuest, COM_IIDOF(IGuest));
-        }
-        else /* ... or on the host. */
-        {
-            rc = RTDirCreate(pszDestFinal, 700);
-        }
-        RTStrFree(pszDestFinal);
-    }
-    return rc;
-}
-
-/**
- * Copys a file from host to the guest.
- *
- * @return  IPRT status code.
- * @param   pGuest                  IGuest interface pointer.
- * @param   pszSource               Source path of existing host file to copy to the guest.
- * @param   pszDest                 Destination path on guest to copy the file to.
- * @param   pszUserName             User name on guest to use for the copy operation.
- * @param   pszPassword             Password of user account.
- * @param   fFlags                  Copy flags.
- */
-static int ctrlCopyFileToGuest(IGuest *pGuest, const char *pszSource, const char *pszDest,
-                               const char *pszUserName, const char *pszPassword,
-                               uint32_t fFlags)
-{
-    AssertPtrReturn(pGuest, VERR_INVALID_POINTER);
-    AssertPtrReturn(pszSource, VERR_INVALID_POINTER);
-    AssertPtrReturn(pszDest, VERR_INVALID_POINTER);
-    AssertPtrReturn(pszUserName, VERR_INVALID_POINTER);
-    AssertPtrReturn(pszPassword, VERR_INVALID_POINTER);
-
-    int vrc = VINF_SUCCESS;
-    ComPtr<IProgress> progress;
-    HRESULT rc = pGuest->CopyToGuest(Bstr(pszSource).raw(), Bstr(pszDest).raw(),
-                                     Bstr(pszUserName).raw(), Bstr(pszPassword).raw(),
-                                     fFlags, progress.asOutParam());
-    if (FAILED(rc))
-        vrc = ctrlPrintError(pGuest, COM_IIDOF(IGuest));
-    else
-    {
-        rc = showProgress(progress);
-        if (FAILED(rc))
-            vrc = ctrlPrintProgressError(progress);
-    }
-    return vrc;
-}
-
-/**
- * Copys a file from guest to the host.
- *
- * @return  IPRT status code.
- * @param   pGuest                  IGuest interface pointer.
- * @param   pszSource               Source path of existing guest file to copy to the host.
- * @param   pszDest                 Destination path/file on host to copy the file to.
- * @param   pszUserName             User name on guest to use for the copy operation.
- * @param   pszPassword             Password of user account.
- * @param   fFlags                  Copy flags.
- */
-static int ctrlCopyFileToHost(IGuest *pGuest, const char *pszSource, const char *pszDest,
-                              const char *pszUserName, const char *pszPassword,
-                              uint32_t fFlags)
-{
-    AssertPtrReturn(pGuest, VERR_INVALID_POINTER);
-    AssertPtrReturn(pszSource, VERR_INVALID_POINTER);
-    AssertPtrReturn(pszDest, VERR_INVALID_POINTER);
-    AssertPtrReturn(pszUserName, VERR_INVALID_POINTER);
-    AssertPtrReturn(pszPassword, VERR_INVALID_POINTER);
-
-    int vrc = VINF_SUCCESS;
-    ComPtr<IProgress> progress;
-    HRESULT rc = pGuest->CopyFromGuest(Bstr(pszSource).raw(), Bstr(pszDest).raw(),
-                                       Bstr(pszUserName).raw(), Bstr(pszPassword).raw(),
-                                       fFlags, progress.asOutParam());
-    if (FAILED(rc))
-        vrc = ctrlPrintError(pGuest, COM_IIDOF(IGuest));
-    else
-    {
-        rc = showProgress(progress);
-        if (FAILED(rc))
-            vrc = ctrlPrintProgressError(progress);
-    }
-    return vrc;
-}
-
-static int ctrlCopyToDestDirectory(IGuest *pGuest, bool fVerbose, bool fDryRun, bool fHostToGuest,
-                                   const char *pszSourceDir,
-                                   const char *pszDestRoot, const char *pszDestSub, const char *pszFileName,
-                                   uint32_t uFlags, const char *pszUsername, const char *pszPassword)
-{
-    AssertPtrReturn(pGuest, VERR_INVALID_POINTER);
-    AssertPtrReturn(pszDestRoot, VERR_INVALID_POINTER);
-    AssertPtrReturn(pszDestSub, VERR_INVALID_POINTER);
-    AssertPtrReturn(pszFileName, VERR_INVALID_POINTER);
-    AssertPtrReturn(pszSourceDir, VERR_INVALID_POINTER);
-    AssertPtrReturn(pszUsername, VERR_INVALID_POINTER);
-    AssertPtrReturn(pszPassword, VERR_INVALID_POINTER);
-
-    int iLen;
-    char *pszSource;
-    if (!strlen(pszDestSub))
-        iLen = RTStrAPrintf(&pszSource, "%s/%s", pszSourceDir, pszFileName);
-    else
-        iLen = RTStrAPrintf(&pszSource, "%s/%s/%s",
-                            pszSourceDir, pszDestSub, pszFileName);
-    if (!iLen)
-        return VERR_NO_MEMORY;
-
-    char *pszDest;
-    if (!strlen(pszDestSub))
-        iLen = RTStrAPrintf(&pszDest, "%s/%s", pszDestRoot, pszFileName);
-    else
-        iLen = RTStrAPrintf(&pszDest, "%s/%s/%s", pszDestRoot, pszDestSub,
-                            pszFileName);
-    if (!iLen)
-    {
-        RTStrFree(pszSource);
-        return VERR_NO_MEMORY;
-    }
-
-    if (fVerbose)
-        RTPrintf("\"%s\" -> \"%s\"\n", pszSource, pszDest);
-
-    int rc = VINF_SUCCESS;
-
-    /* Finally copy the desired file (if no dry run selected). */
-    if (!fDryRun)
-    {
-        if (fHostToGuest)
-            rc = ctrlCopyFileToGuest(pGuest, pszSource, pszDest,
-                                     pszUsername, pszPassword, uFlags);
-        else
-            rc = ctrlCopyFileToHost(pGuest, pszSource, pszDest,
-                                    pszUsername, pszPassword, uFlags);
-    }
-    RTStrFree(pszSource);
-    RTStrFree(pszDest);
-
-    return rc;
+        /* If there's anything (like a file name or a filter),
+         * strip it! */
+        RTPathStripFilename(pszNewRoot);
+        *ppszSourceRoot = pszNewRoot;
+    }
+
+    return VINF_SUCCESS;
+}
+
+static void ctrlCopyFreeSourceRoot(char *pszSourceRoot)
+{
+    RTStrFree(pszSourceRoot);
 }
 
@@ -1415,116 +1489,108 @@
     }
 
-    /* Strip traling slash from destination path. */
-    RTPathStripTrailingSlash(Utf8Dest.mutableRaw());
-    Utf8Dest.jolt();
-
-    /*
-     * Here starts the actual fun!
-     */
-    for (unsigned long s = 0; s < vecSources.size(); s++)
-    {
-        char *pszSourceDir;
-        if (RTDirExists(vecSources[s].mSource.c_str()))
-            pszSourceDir = RTStrDup(vecSources[s].mSource.c_str());
-        else
-        {
-            pszSourceDir = RTStrDup(vecSources[s].mSource.c_str());
-            RTPathStripFilename(pszSourceDir);
-        }
-
-        uint32_t cObjects = 0;
-        DESTDIRMAP mapDest;
-        const char *pszDestRoot = Utf8Dest.c_str();
-
-        if (fHostToGuest)
-            vrc = ctrlCopyConstructDestinationsForGuest(vecSources[s], fFlags, pszDestRoot,
-                                                        mapDest, &cObjects);
-        else
-            vrc = ctrlCopyConstructDestinationsForHost(guest, Utf8UserName.c_str(), Utf8Password.c_str(),
-                                                       vecSources[s], fFlags, pszDestRoot,
-                                                       mapDest, &cObjects);
-        if (RT_FAILURE(vrc))
-        {
-            if (   fVerbose
-                && vrc == VERR_FILE_NOT_FOUND)
-            {
-                RTPrintf("Warning: Source \"%s\" does not exist, skipping!\n",
-                         vecSources[s].mSource.c_str());
-            }
-        }
-        else
-        {
-            /*
-             * Prepare directory structure of each destination directory.
-             */
-            DESTDIRMAPITER itDest;
-            for (itDest = mapDest.begin(); itDest != mapDest.end(); itDest++)
-            {
-                if (fVerbose)
+    /* Create the copy context -- it contains all information
+     * the routines need to know when handling the actual copying. */
+    PCOPYCONTEXT pContext;
+    vrc = ctrlCopyContextCreate(guest, fVerbose, fHostToGuest,
+                                Utf8UserName.c_str(), Utf8Password.c_str(),
+                                &pContext);
+    if (RT_FAILURE(vrc))
+    {
+        RTMsgError("Unable to create copy context, rc=%Rrc\n", vrc);
+        return RTEXITCODE_FAILURE;
+    }
+
+    /* If the destination is a path, (try to) create it. */
+    const char *pszDest = Utf8Dest.c_str();
+    AssertPtr(pszDest);
+    size_t lenDest = strlen(pszDest);
+    if (   lenDest
+         ||pszDest[lenDest - 1] == '/'
+        || pszDest[lenDest - 1] == '\\')
+    {
+        vrc = ctrlCopyDirCreate(pContext, pszDest);
+    }
+
+    if (RT_SUCCESS(vrc))
+    {
+        /*
+         * Here starts the actual fun!
+         * Handle all given sources one by one.
+         */
+        for (unsigned long s = 0; s < vecSources.size(); s++)
+        {
+            const char *pszSource = vecSources[s].mSource.c_str();
+            const char *pszFilter = vecSources[s].mFilter.c_str();
+
+            char *pszSourceRoot;
+            vrc = ctrlCopyCreateSourceRoot(pszSource, &pszSourceRoot);
+            if (RT_FAILURE(vrc))
+            {
+                RTMsgError("Unable to create source root, rc=%Rrc\n", vrc);
+                break;
+            }
+
+            if (fVerbose)
+                RTPrintf("Source: %s\n", pszSource);
+
+            /* @todo Files with filter?? */
+            bool fExists;
+            vrc = ctrlCopyFileExistsOnSource(pContext, pszSource, &fExists);
+            if (RT_SUCCESS(vrc))
+            {
+                if (fExists)
                 {
-                    const char *pszSubDir = itDest->first.c_str();
-                    AssertPtr(pszSubDir);
-                    if (!strlen(pszSubDir))
-                        RTPrintf("Preparing directory \"%s\" ...\n", pszDestRoot);
+                    /* Single file. */
+                    char *pszDest;
+                    vrc = ctrlCopyTranslatePath(pContext, pszSourceRoot, pszSource,
+                                                Utf8Dest.c_str(), &pszDest);
+                    if (RT_SUCCESS(vrc))
+                    {
+                        vrc = ctrlCopyFileToTarget(pContext, pszSource,
+                                                   pszDest, fFlags);
+                        RTStrFree(pszDest);
+                    }
                     else
-                        RTPrintf("Preparing directory \"%s/%s\" ...\n", pszDestRoot,
-                                 itDest->first.c_str());
+                    {
+                        RTMsgError("Unable to translate path for \"%s\", rc=%Rrc\n",
+                                   pszSource, vrc);
+                    }
                 }
-                if (!fDryRun)
-                    vrc = ctrlCopyPrepareDestDirectory(guest, fHostToGuest,
-                                                       pszDestRoot, itDest->first.c_str(),
-                                                       Utf8UserName.c_str(), Utf8Password.c_str());
-                if (RT_FAILURE(vrc))
-                    break;
+                else
+                {
+                    if (   (RT_SUCCESS(ctrlCopyDirExistsOnSource(pContext, pszSource, &fExists))
+                            && fExists)
+                        || (   RT_SUCCESS(ctrlCopyDirExistsOnSource(pContext, pszSourceRoot, &fExists))
+                            && fExists
+                            && pszFilter)
+                       )
+                    {
+                        /* Directory (with filter?). */
+                        vrc = ctrlCopyDirToTarget(pContext, pszSource, pszFilter,
+                                                  Utf8Dest.c_str(), fFlags, NULL /* Subdir */);
+                    }
+                }
+            }
+
+            ctrlCopyFreeSourceRoot(pszSourceRoot);
+
+            if (   RT_SUCCESS(vrc)
+                && !fExists)
+            {
+                RTMsgError("Warning: Source \"%s\" does not exist, skipping!\n",
+                           pszSource);
+                continue;
             }
 
             if (RT_FAILURE(vrc))
-                break;
-
-            if (fVerbose)
-            {
-                if (!cObjects)
-                    RTPrintf("Warning: Source \"%s\" has no (matching) files to copy, skipping!\n",
-                             vecSources[s].mSource.c_str());
-                else
-                    RTPrintf("Copying \"%s\" (%u files) ...\n",
-                             vecSources[s].mSource.c_str(), cObjects);
-            }
-
-            /*
-             * Copy files of each destination root directory to the guest.
-             */
-            for (itDest = mapDest.begin(); itDest != mapDest.end(); itDest++)
-            {
-                if (fVerbose && itDest->second.size())
-                {
-                    if (itDest->first.isEmpty())
-                        RTPrintf("Copying %u files ...\n", itDest->second.size());
-                    else
-                        RTPrintf("Copying directory \"%s\" (%u files) ...\n",
-                                 itDest->first.c_str(), itDest->second.size());
-                }
-
-                for (unsigned long l = 0; l < itDest->second.size(); l++)
-                {
-                    vrc = ctrlCopyToDestDirectory(guest, fVerbose, fDryRun, fHostToGuest,
-                                                  pszSourceDir,
-                                                  pszDestRoot, itDest->first.c_str() /* Sub directory */,
-                                                  itDest->second[l].mFileName.c_str() /* Filename */,
-                                                  fFlags, Utf8UserName.c_str(), Utf8Password.c_str());
-                    if (RT_FAILURE(vrc))
-                        break;
-                }
-
-                if (RT_FAILURE(vrc))
-                    break;
-            }
-
-            if (RT_FAILURE(vrc))
-                break;
-        }
-
-        RTStrFree(pszSourceDir);
-    }
+            {
+                RTMsgError("Error processing \"%s\", rc=%Rrc\n",
+                           pszSource, vrc);
+                break;
+            }
+        }
+    }
+
+    ctrlCopyContextFree(pContext);
 
     return RT_SUCCESS(vrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
@@ -1632,4 +1698,119 @@
                 ctrlPrintError(guest, COM_IIDOF(IGuest)); /* Return code ignored, save original rc. */
                 break;
+            }
+
+            it++;
+        }
+
+        if (FAILED(hrc))
+            rcExit = RTEXITCODE_FAILURE;
+    }
+
+    return rcExit;
+}
+
+static int handleCtrlStat(ComPtr<IGuest> guest, HandlerArg *pArg)
+{
+    AssertPtrReturn(pArg, VERR_INVALID_PARAMETER);
+
+    /*
+     * Parse arguments.
+     *
+     * Note! No direct returns here, everyone must go thru the cleanup at the
+     *       end of this function.
+     */
+    static const RTGETOPTDEF s_aOptions[] =
+    {
+        /** @todo Implement "--dereference/-L", and "--file-system/-f" later! */
+        { "--password",            'p',         RTGETOPT_REQ_STRING  },
+        { "--username",            'u',         RTGETOPT_REQ_STRING  },
+        { "--verbose",             'v',         RTGETOPT_REQ_NOTHING }
+    };
+
+    int ch;
+    RTGETOPTUNION ValueUnion;
+    RTGETOPTSTATE GetState;
+    RTGetOptInit(&GetState, pArg->argc, pArg->argv,
+                 s_aOptions, RT_ELEMENTS(s_aOptions), 0, RTGETOPTINIT_FLAGS_OPTS_FIRST);
+
+    Utf8Str Utf8UserName;
+    Utf8Str Utf8Password;
+
+    bool fVerbose = false;
+    DESTDIRMAP mapDirs;
+
+    RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
+    while (   (ch = RTGetOpt(&GetState, &ValueUnion))
+           && rcExit == RTEXITCODE_SUCCESS)
+    {
+        /* For options that require an argument, ValueUnion has received the value. */
+        switch (ch)
+        {
+            case 'p': /* Password */
+                Utf8Password = ValueUnion.psz;
+                break;
+
+            case 'u': /* User name */
+                Utf8UserName = ValueUnion.psz;
+                break;
+
+            case 'v': /* Verbose */
+                fVerbose = true;
+                break;
+
+            case VINF_GETOPT_NOT_OPTION:
+            {
+                mapDirs[ValueUnion.psz]; /* Add element to check to map. */
+                break;
+            }
+
+            default:
+                rcExit = RTGetOptPrintError(ch, &ValueUnion);
+                break;
+        }
+    }
+
+    uint32_t cDirs = mapDirs.size();
+    if (rcExit == RTEXITCODE_SUCCESS && !cDirs)
+        rcExit = errorSyntax(USAGE_GUESTCONTROL, "No element to check specified!");
+
+    if (rcExit == RTEXITCODE_SUCCESS && Utf8UserName.isEmpty())
+        rcExit = errorSyntax(USAGE_GUESTCONTROL, "No user name specified!");
+
+    if (rcExit == RTEXITCODE_SUCCESS)
+    {
+        /*
+         * Create the directories.
+         */
+        HRESULT hrc = S_OK;
+
+        DESTDIRMAPITER it = mapDirs.begin();
+        while (it != mapDirs.end())
+        {
+            if (fVerbose)
+                RTPrintf("Checking for element \"%s\" ...\n", it->first.c_str());
+
+            BOOL fExists;
+            hrc = guest->FileExists(Bstr(it->first).raw(),
+                                    Bstr(Utf8UserName).raw(), Bstr(Utf8Password).raw(),
+                                    &fExists);
+            if (FAILED(hrc))
+            {
+                ctrlPrintError(guest, COM_IIDOF(IGuest)); /* Return code ignored, save original rc. */
+                break;
+            }
+            else
+            {
+                /** @todo: Output vbox_stat's stdout output to get more information about
+                 *         what happened. */
+
+                /* If there's at least one element which does not exist on the guest,
+                 * drop out with exitcode 1. */
+                if (!fExists)
+                {
+                    RTPrintf("Cannot stat for element \"%s\": No such file or directory.\n",
+                             it->first.c_str());
+                    rcExit = RTEXITCODE_FAILURE;
+                }
             }
 
@@ -1772,5 +1953,4 @@
             rcExit = handleCtrlExecProgram(guest, &arg);
         }
-#if 0
         else if (!strcmp(pArg->argv[1], "copyfrom"))
         {
@@ -1778,5 +1958,4 @@
                                       false /* Guest to host */);
         }
-#endif
         else if (   !strcmp(pArg->argv[1], "copyto")
                  || !strcmp(pArg->argv[1], "cp"))
@@ -1792,4 +1971,8 @@
             rcExit = handleCtrlCreateDirectory(guest, &arg);
         }
+        else if (   !strcmp(pArg->argv[1], "stat"))
+        {
+            rcExit = handleCtrlStat(guest, &arg);
+        }
         else if (   !strcmp(pArg->argv[1], "updateadditions")
                  || !strcmp(pArg->argv[1], "updateadds"))
Index: /trunk/src/VBox/Main/Makefile.kmk
===================================================================
--- /trunk/src/VBox/Main/Makefile.kmk	(revision 38084)
+++ /trunk/src/VBox/Main/Makefile.kmk	(revision 38085)
@@ -649,4 +649,5 @@
 	src-client/DisplayImpl.cpp \
 	src-client/GuestCtrlImpl.cpp \
+	src-client/GuestCtrlIO.cpp \
 	src-client/GuestImpl.cpp \
 	src-client/KeyboardImpl.cpp \
Index: /trunk/src/VBox/Main/include/GuestCtrlImplPrivate.h
===================================================================
--- /trunk/src/VBox/Main/include/GuestCtrlImplPrivate.h	(revision 38085)
+++ /trunk/src/VBox/Main/include/GuestCtrlImplPrivate.h	(revision 38085)
@@ -0,0 +1,89 @@
+/** @file
+ *
+ * VirtualBox guest execution control private data definitions
+ */
+
+/*
+ * Copyright (C) 2011 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#ifndef ____H_GUESTIMPLPRIVATE
+#define ____H_GUESTIMPLPRIVATE
+
+#include "VirtualBoxBase.h"
+
+#include <map>
+#include <vector>
+
+#ifdef VBOX_WITH_GUEST_CONTROL
+# include <VBox/HostServices/GuestControlSvc.h>
+using namespace guestControl;
+#endif
+
+/** Structure representing the "value" side of a "key=value" pair. */
+typedef struct VBOXGUESTCTRL_STREAM_PAIR
+{
+    char *pszValue;
+} VBOXGUESTCTRL_STREAM_PAIR, *PVBOXGUESTCTRL_STREAM_PAIR;
+
+/** Map containing "key=value" pairs of a stream object. */
+typedef std::map< Utf8Str, VBOXGUESTCTRL_STREAM_PAIR > GuestCtrlStreamPairs;
+typedef std::map< Utf8Str, VBOXGUESTCTRL_STREAM_PAIR >::iterator GuestCtrlStreamPairsIter;
+typedef std::map< Utf8Str, VBOXGUESTCTRL_STREAM_PAIR >::const_iterator GuestCtrlStreamPairsIterConst;
+
+class GuestProcessStream
+{
+
+public:
+
+    GuestProcessStream();
+
+    virtual ~GuestProcessStream();
+
+public:
+
+    void Destroy();
+
+    void ClearPairs();
+
+    const char* GetString(const char *pszKey);
+
+    int GetUInt32Ex(const char *pszKey, uint32_t *puVal);
+
+    uint32_t GetUInt32(const char *pszKey);
+
+    int GetInt64Ex(const char *pszKey, int64_t *piVal);
+
+    int64_t GetInt64(const char *pszKey);
+
+    size_t GetNumPairs();
+
+    int AddData(const BYTE *pbData, size_t cbData);
+
+    int Parse();
+
+protected:
+
+    /** The map containing one more more stream pairs. */
+    GuestCtrlStreamPairs m_mapPairs;
+    /** Currently allocated size of internal stream buffer. */
+    uint32_t m_cbAllocated;
+    /** Currently used size of allocated internal stream buffer. */
+    uint32_t m_cbSize;
+    /** Current offset within the internal stream buffer. */
+    uint32_t m_cbOffset;
+    /** Current parser offset. */
+    uint32_t m_cbParserOffset;
+    /** Internal stream buffer. */
+    BYTE *m_pbBuffer;
+};
+#endif // ____H_GUESTIMPLPRIVATE
+
Index: /trunk/src/VBox/Main/include/GuestImpl.h
===================================================================
--- /trunk/src/VBox/Main/include/GuestImpl.h	(revision 38084)
+++ /trunk/src/VBox/Main/include/GuestImpl.h	(revision 38085)
@@ -115,10 +115,16 @@
 
     // Public methods that are not in IDL (only called internally).
+    HRESULT directoryCreateInternal(IN_BSTR aDirectory, IN_BSTR aUserName, IN_BSTR aPassword,
+                                    ULONG aMode, ULONG aFlags, int *pRC);
+    HRESULT directoryOpenInternal(IN_BSTR aDirectory, IN_BSTR aFilter,
+                                  ULONG aFlags,
+                                  IN_BSTR aUserName, IN_BSTR aPassword,
+                                  ULONG *aHandle, int *pRC);
     HRESULT executeProcessInternal(IN_BSTR aCommand, ULONG aFlags,
                                    ComSafeArrayIn(IN_BSTR, aArguments), ComSafeArrayIn(IN_BSTR, aEnvironment),
                                    IN_BSTR aUserName, IN_BSTR aPassword,
                                    ULONG aTimeoutMS, ULONG *aPID, IProgress **aProgress, int *pRC);
-    HRESULT directoryCreateInternal(IN_BSTR aDirectory, IN_BSTR aUserName, IN_BSTR aPassword,
-                                    ULONG aMode, ULONG aFlags, int *pRC);
+    HRESULT fileExistsInternal(IN_BSTR aFile, IN_BSTR aUserName, IN_BSTR aPassword, BOOL *aExists, int *pRC);
+    HRESULT fileQuerySizeInternal(IN_BSTR aFile, IN_BSTR aUserName, IN_BSTR aPassword, LONG64 *aSize, int *pRC);
     void setAdditionsInfo(Bstr aInterfaceVersion, VBOXOSTYPE aOsType);
     void setAdditionsInfo2(Bstr aAdditionsVersion, Bstr aVersionName, Bstr aRevision);
@@ -196,7 +202,25 @@
     int processSetStatus(uint32_t u32PID, ExecuteProcessStatus_T enmStatus, uint32_t uExitCode, uint32_t uFlags);
 
+    // Internal guest directory representation.
+    typedef struct VBOXGUESTCTRL_DIRECTORY
+    {
+        char    *mpszDirectory;
+        char    *mpszFilter;
+        ULONG    uFlags;
+        /** Associated PID of started vbox_ls tool. */
+        uint32_t mPID;
+        /** Offset within the current retrieved stdout buffer. */
+        uint64_t mOffset;
+    } VBOXGUESTCTRL_DIRECTORY, *PVBOXGUESTCTRL_DIRECTORY;
+    typedef std::map< uint32_t, VBOXGUESTCTRL_DIRECTORY > GuestDirectoryMap;
+    typedef std::map< uint32_t, VBOXGUESTCTRL_DIRECTORY >::iterator GuestDirectoryMapIter;
+    typedef std::map< uint32_t, VBOXGUESTCTRL_DIRECTORY >::const_iterator GuestDirectoryMapIterConst;
+
+    int directoryCreateHandle(ULONG *puHandle, const char *pszDirectory, const char *pszFilter, ULONG uFlags);
+    void directoryDestroyHandle(uint32_t uHandle);
+    uint32_t directoryGetPID(uint32_t uHandle);
+    bool directoryHandleExists(uint32_t uHandle);
+
     // Utility functions.
-    int directoryEntryAppend(const char *pszPath, PRTLISTNODE pList);
-    int directoryRead(const char *pszDirectory, const char *pszFilter, ULONG uFlags, ULONG *pcObjects, PRTLISTNODE pList);
     int prepareExecuteEnv(const char *pszEnv, void **ppvList, uint32_t *pcbList, uint32_t *pcEnv);
 
@@ -239,5 +263,8 @@
     /** Next upcoming context ID. */
     volatile uint32_t mNextContextID;
+    /** Next upcoming directory handle ID. */
+    volatile uint32_t mNextDirectoryID;
     CallbackMap       mCallbackMap;
+    GuestDirectoryMap mGuestDirectoryMap;
     GuestProcessMap   mGuestProcessMap;
 # endif
Index: /trunk/src/VBox/Main/src-client/GuestCtrlIO.cpp
===================================================================
--- /trunk/src/VBox/Main/src-client/GuestCtrlIO.cpp	(revision 38085)
+++ /trunk/src/VBox/Main/src-client/GuestCtrlIO.cpp	(revision 38085)
@@ -0,0 +1,320 @@
+/* $Id$ */
+/** @file
+ *
+ * IO helper for IGuest COM class implementations.
+ */
+
+/*
+ * Copyright (C) 2011 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+/******************************************************************************
+ *   Header Files                                                             *
+ ******************************************************************************/
+#include "GuestCtrlImplPrivate.h"
+
+
+/******************************************************************************
+ *   Structures and Typedefs                                                  *
+ ******************************************************************************/
+
+/** @todo *NOT* thread safe yet! */
+
+GuestProcessStream::GuestProcessStream()
+    : m_cbAllocated(0),
+      m_cbSize(0),
+      m_cbOffset(0),
+      m_cbParserOffset(0),
+      m_pbBuffer(NULL)
+{
+
+}
+
+GuestProcessStream::~GuestProcessStream()
+{
+    Destroy();
+}
+
+/**
+ * Destroys the stored stream pairs.
+ */
+void GuestProcessStream::Destroy()
+{
+    ClearPairs();
+
+    if (m_pbBuffer)
+        RTMemFree(m_pbBuffer);
+}
+
+void GuestProcessStream::ClearPairs()
+{
+    for (GuestCtrlStreamPairsIter it = m_mapPairs.begin(); it != m_mapPairs.end(); it++)
+    {
+        if (it->second.pszValue)
+            RTMemFree(it->second.pszValue);
+    }
+
+    m_mapPairs.clear();
+}
+
+/**
+ * Returns a 32-bit unsigned integer of a specified key.
+ *
+ * @return  uint32_t            Value to return, 0 if not found / on failure.
+ * @param   pszKey              Name of key to get the value for.
+ */
+const char* GuestProcessStream::GetString(const char *pszKey)
+{
+    AssertPtrReturn(pszKey, NULL);
+
+    try
+    {
+        GuestCtrlStreamPairsIterConst itPairs = m_mapPairs.find(Utf8Str(pszKey));
+        if (itPairs != m_mapPairs.end())
+            return itPairs->second.pszValue;
+    }
+    catch (const std::exception &ex)
+    {
+        NOREF(ex);
+    }
+    return NULL;
+}
+
+int GuestProcessStream::GetUInt32Ex(const char *pszKey, uint32_t *puVal)
+{
+    AssertPtrReturn(pszKey, VERR_INVALID_POINTER);
+    AssertPtrReturn(puVal, VERR_INVALID_POINTER);
+    const char *pszValue = GetString(pszKey);
+    if (pszValue)
+    {
+        *puVal = RTStrToUInt32(pszValue);
+        return VINF_SUCCESS;
+    }
+    return VERR_NOT_FOUND;
+}
+
+/**
+ * Returns a 32-bit unsigned integer of a specified key.
+ *
+ * @return  uint32_t            Value to return, 0 if not found / on failure.
+ * @param   pszKey              Name of key to get the value for.
+ */
+uint32_t GuestProcessStream::GetUInt32(const char *pszKey)
+{
+    uint32_t uVal;
+    if (RT_SUCCESS(GetUInt32Ex(pszKey, &uVal)))
+        return uVal;
+    return 0;
+}
+
+int GuestProcessStream::GetInt64Ex(const char *pszKey, int64_t *piVal)
+{
+    AssertPtrReturn(pszKey, VERR_INVALID_POINTER);
+    AssertPtrReturn(piVal, VERR_INVALID_POINTER);
+    const char *pszValue = GetString(pszKey);
+    if (pszValue)
+    {
+        *piVal = RTStrToInt64(pszValue);
+        return VINF_SUCCESS;
+    }
+    return VERR_NOT_FOUND;
+}
+
+/**
+ * Returns a 64-bit integer of a specified key.
+ *
+ * @return  int64_t             Value to return, 0 if not found / on failure.
+ * @param   pszKey              Name of key to get the value for.
+ */
+int64_t GuestProcessStream::GetInt64(const char *pszKey)
+{
+    int64_t iVal;
+    if (RT_SUCCESS(GetInt64Ex(pszKey, &iVal)))
+        return iVal;
+    return 0;
+}
+
+/**
+ * Returns the current number of stream pairs.
+ *
+ * @return  uint32_t            Current number of stream pairs.
+ */
+size_t GuestProcessStream::GetNumPairs()
+{
+    return m_mapPairs.size();
+}
+
+int GuestProcessStream::AddData(const BYTE *pbData, size_t cbData)
+{
+    AssertPtrReturn(pbData, VERR_INVALID_POINTER);
+    AssertReturn(cbData, VERR_INVALID_PARAMETER);
+
+    int rc = VINF_SUCCESS;
+
+    /* Rewind the buffer if it's empty. */
+    size_t     cbInBuf   = m_cbSize - m_cbOffset;
+    bool const fAddToSet = cbInBuf == 0;
+    if (fAddToSet)
+        m_cbSize = m_cbOffset = 0;
+
+    /* Try and see if we can simply append the data. */
+    if (cbData + m_cbSize <= m_cbAllocated)
+    {
+        memcpy(&m_pbBuffer[m_cbSize], pbData, cbData);
+        m_cbSize += cbData;
+    }
+    else
+    {
+        /* Move any buffered data to the front. */
+        cbInBuf = m_cbSize - m_cbOffset;
+        if (cbInBuf == 0)
+            m_cbSize = m_cbOffset = 0;
+        else if (m_cbOffset) /* Do we have something to move? */
+        {
+            memmove(m_pbBuffer, &m_pbBuffer[m_cbOffset], cbInBuf);
+            m_cbSize = cbInBuf;
+            m_cbOffset = 0;
+        }
+
+        /* Do we need to grow the buffer? */
+        if (cbData + m_cbSize > m_cbAllocated)
+        {
+            size_t cbAlloc = m_cbSize + cbData;
+            cbAlloc = RT_ALIGN_Z(cbAlloc, _64K);
+            void *pvNew = RTMemRealloc(m_pbBuffer, cbAlloc);
+            if (pvNew)
+            {
+                m_pbBuffer = (uint8_t *)pvNew;
+                m_cbAllocated = cbAlloc;
+            }
+            else
+                rc = VERR_NO_MEMORY;
+        }
+
+        /* Finally, copy the data. */
+        if (RT_SUCCESS(rc))
+        {
+            if (cbData + m_cbSize <= m_cbAllocated)
+            {
+                memcpy(&m_pbBuffer[m_cbSize], pbData, cbData);
+                m_cbSize += cbData;
+            }
+            else
+                rc = VERR_BUFFER_OVERFLOW;
+        }
+    }
+
+    return rc;
+}
+
+int GuestProcessStream::Parse()
+{
+    AssertPtrReturn(m_pbBuffer, VERR_INVALID_POINTER);
+    AssertReturn(m_cbSize, VERR_INVALID_PARAMETER);
+    AssertReturn(m_cbParserOffset < m_cbSize, VERR_INVALID_PARAMETER);
+
+    int rc = VINF_SUCCESS;
+
+    size_t uCur = m_cbParserOffset;
+    for (;uCur < m_cbSize;)
+    {
+        const char *pszStart = (char*)&m_pbBuffer[uCur];
+        const char *pszEnd = pszStart;
+
+        /* Search end of current pair (key=value\0). */
+        while (uCur++ < m_cbSize)
+        {
+            if (*pszEnd == '\0')
+                break;
+            pszEnd++;
+        }
+
+        size_t uPairLen = pszEnd - pszStart;
+        if (uPairLen)
+        {
+            const char *pszSep = pszStart;
+            while (   *pszSep != '='
+                   &&  pszSep != pszEnd)
+            {
+                pszSep++;
+            }
+
+            /* No separator found (or incomplete key=value pair)? */
+            if (   pszSep == pszStart
+                || pszSep == pszEnd)
+            {
+                m_cbParserOffset =  uCur - uPairLen - 1;
+                rc = VERR_MORE_DATA;
+            }
+
+            if (RT_FAILURE(rc))
+                break;
+
+            size_t uKeyLen = pszSep - pszStart;
+            size_t uValLen = pszEnd - (pszSep + 1);
+
+            /* Get key (if present). */
+            if (uKeyLen)
+            {
+                Assert(pszSep > pszStart);
+                char *pszKey = (char*)RTMemAllocZ(uKeyLen + 1);
+                if (!pszKey)
+                {
+                    rc = VERR_NO_MEMORY;
+                    break;
+                }
+                memcpy(pszKey, pszStart, uKeyLen);
+
+                m_mapPairs[Utf8Str(pszKey)].pszValue = NULL;
+
+                /* Get value (if present). */
+                if (uValLen)
+                {
+                    Assert(pszEnd > pszSep);
+                    char *pszVal = (char*)RTMemAllocZ(uValLen + 1);
+                    if (!pszVal)
+                    {
+                        rc = VERR_NO_MEMORY;
+                        break;
+                    }
+                    memcpy(pszVal, pszSep + 1, uValLen);
+
+                    m_mapPairs[Utf8Str(pszKey)].pszValue = pszVal;
+                }
+
+                RTMemFree(pszKey);
+
+                m_cbParserOffset += uCur - m_cbParserOffset;
+            }
+        }
+        else /* No pair detected, check for a new block. */
+        {
+            do
+            {
+                if (*pszEnd == '\0')
+                {
+                    m_cbParserOffset = uCur;
+                    rc = VERR_MORE_DATA;
+                    break;
+                }
+                pszEnd++;
+            } while (++uCur < m_cbSize);
+        }
+
+        if (RT_FAILURE(rc))
+            break;
+    }
+
+    RT_CLAMP(m_cbParserOffset, 0, m_cbSize);
+
+    return rc;
+}
+
Index: /trunk/src/VBox/Main/src-client/GuestCtrlImpl.cpp
===================================================================
--- /trunk/src/VBox/Main/src-client/GuestCtrlImpl.cpp	(revision 38084)
+++ /trunk/src/VBox/Main/src-client/GuestCtrlImpl.cpp	(revision 38085)
@@ -17,4 +17,5 @@
 
 #include "GuestImpl.h"
+#include "GuestCtrlImplPrivate.h"
 
 #include "Global.h"
@@ -485,249 +486,144 @@
         AssertPtr(pGuest);
 
-
-
-#if 0
         /* Does our source file exist? */
-        if (!RTFileExists(aTask->strSource.c_str()))
-        {
-            rc = TaskGuest::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
-                                                 Guest::tr("Source file \"%s\" does not exist, or is not a file"),
-                                                 aTask->strSource.c_str());
-        }
-        else
-        {
-            RTFILE fileSource;
-            int vrc = RTFileOpen(&fileSource, aTask->strSource.c_str(),
-                                 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE);
-            if (RT_FAILURE(vrc))
+        BOOL fFileExists;
+        rc = pGuest->FileExists(Bstr(aTask->strSource).raw(),
+                                Bstr(aTask->strUserName).raw(), Bstr(aTask->strPassword).raw(),
+                                &fFileExists);
+        if (SUCCEEDED(rc))
+        {
+            if (!fFileExists)
+                rc = TaskGuest::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
+                                                     Guest::tr("Source file \"%s\" does not exist, or is not a file"),
+                                                     aTask->strSource.c_str());
+        }
+
+        /* Query file size to make an estimate for our progress object. */
+        if (SUCCEEDED(rc))
+        {
+            LONG64 lFileSize;
+            rc = pGuest->FileQuerySize(Bstr(aTask->strSource).raw(),
+                                       Bstr(aTask->strUserName).raw(), Bstr(aTask->strPassword).raw(),
+                                       &lFileSize);
+            if (FAILED(rc))
+                rc = TaskGuest::setProgressErrorInfo(rc, aTask->progress, pGuest);
+
+            com::SafeArray<IN_BSTR> args;
+            com::SafeArray<IN_BSTR> env;
+
+            if (SUCCEEDED(rc))
             {
-                rc = TaskGuest::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
-                                                     Guest::tr("Could not open source file \"%s\" for reading (%Rrc)"),
-                                                     aTask->strSource.c_str(),  vrc);
-            }
-            else
-            {
-                uint64_t cbSize;
-                vrc = RTFileGetSize(fileSource, &cbSize);
-                if (RT_FAILURE(vrc))
+                /*
+                 * Prepare tool command line.
+                 */
+                char szSource[RTPATH_MAX];
+                if (RTStrPrintf(szSource, sizeof(szSource), "%s", aTask->strSource.c_str()) <= sizeof(szSource) - 1)
                 {
-                    rc = TaskGuest::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
-                                                         Guest::tr("Could not query file size of \"%s\" (%Rrc)"),
-                                                         aTask->strSource.c_str(), vrc);
+                    /*
+                     * Normalize path slashes, based on the detected guest.
+                     */
+                    Utf8Str osType = mData.mOSTypeId;
+                    if (   osType.contains("Microsoft", Utf8Str::CaseInsensitive)
+                        || osType.contains("Windows", Utf8Str::CaseInsensitive))
+                    {
+                        /* We have a Windows guest. */
+                        RTPathChangeToDosSlashes(szSource, true /* Force conversion. */);
+                    }
+                    else /* ... or something which isn't from Redmond ... */
+                    {
+                        RTPathChangeToUnixSlashes(szSource, true /* Force conversion. */);
+                    }
+
+                    args.push_back(Bstr(szSource).raw()); /* Tell our cat tool which file to output. */
                 }
                 else
+                    rc = TaskGuest::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
+                                                         Guest::tr("Error preparing command line"));
+            }
+
+            ComPtr<IProgress> execProgress;
+            ULONG uPID;
+            if (SUCCEEDED(rc))
+            {
+                LogRel(("Copying file \"%s\" to host \"%s\" (%u bytes) ...\n",
+                        aTask->strSource.c_str(), aTask->strDest.c_str(), lFileSize));
+
+                /*
+                 * Okay, since we gathered all stuff we need until now to start the
+                 * 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());
+                if (FAILED(rc))
+                    rc = TaskGuest::setProgressErrorInfo(rc, aTask->progress, pGuest);
+            }
+
+            if (SUCCEEDED(rc))
+            {
+                BOOL fCompleted = FALSE;
+                BOOL fCanceled = FALSE;
+
+                RTFILE hFileDest;
+                int vrc = RTFileOpen(&hFileDest, aTask->strDest.c_str(),
+                                     RTFILE_O_READWRITE | RTFILE_O_OPEN_CREATE | RTFILE_O_DENY_WRITE);
+                if (RT_FAILURE(vrc))
+                    rc = TaskGuest::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
+                                                         Guest::tr("Unable to create/open destination file \"%s\", rc=%Rrc"),
+                                                         aTask->strDest.c_str(), vrc);
+                else
                 {
-                    com::SafeArray<IN_BSTR> args;
-                    com::SafeArray<IN_BSTR> env;
-
-                    /*
-                     * Prepare tool command line.
-                     */
-                    char szOutput[RTPATH_MAX];
-                    if (RTStrPrintf(szOutput, sizeof(szOutput), "--output=%s", aTask->strDest.c_str()) <= sizeof(szOutput) - 1)
+                    size_t cbToRead = lFileSize;
+                    size_t cbTransfered = 0;
+                    SafeArray<BYTE> aOutputData(_64K);
+                    while (SUCCEEDED(execProgress->COMGETTER(Completed(&fCompleted))))
                     {
-                        /*
-                         * Normalize path slashes, based on the detected guest.
-                         */
-                        Utf8Str osType = mData.mOSTypeId;
-                        if (   osType.contains("Microsoft", Utf8Str::CaseInsensitive)
-                            || osType.contains("Windows", Utf8Str::CaseInsensitive))
+                        rc = this->GetProcessOutput(uPID, ProcessOutputFlag_None,
+                                                    10 * 1000 /* Timeout in ms */,
+                                                    _64K, ComSafeArrayAsOutParam(aOutputData));
+                        if (SUCCEEDED(rc))
                         {
-                            /* We have a Windows guest. */
-                            RTPathChangeToDosSlashes(szOutput, true /* Force conversion. */);
-                        }
-                        else /* ... or something which isn't from Redmond ... */
-                        {
-                            RTPathChangeToUnixSlashes(szOutput, true /* Force conversion. */);
-                        }
-
-                        args.push_back(Bstr(szOutput).raw());             /* We want to write a file ... */
-                    }
-                    else
-                    {
-                        rc = TaskGuest::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
-                                                             Guest::tr("Error preparing command line"));
-                    }
-
-                    ComPtr<IProgress> execProgress;
-                    ULONG uPID;
-                    if (SUCCEEDED(rc))
-                    {
-                        LogRel(("Copying file \"%s\" to guest \"%s\" (%u bytes) ...\n",
-                                aTask->strSource.c_str(), aTask->strDest.c_str(), cbSize));
-                        /*
-                         * Okay, since we gathered all stuff we need until now to start the
-                         * actual copying, start the guest part now.
-                         */
-                        rc = pGuest->ExecuteProcess(Bstr(VBOXSERVICE_TOOL_CAT).raw(),
-                                                      ExecuteProcessFlag_Hidden
-                                                    | ExecuteProcessFlag_WaitForProcessStartOnly,
-                                                    ComSafeArrayAsInParam(args),
-                                                    ComSafeArrayAsInParam(env),
-                                                    Bstr(aTask->strUserName).raw(),
-                                                    Bstr(aTask->strPassword).raw(),
-                                                    5 * 1000 /* Wait 5s for getting the process started. */,
-                                                    &uPID, execProgress.asOutParam());
-                        if (FAILED(rc))
-                            rc = TaskGuest::setProgressErrorInfo(rc, aTask->progress, pGuest);
-                    }
-
-                    if (SUCCEEDED(rc))
-                    {
-                        BOOL fCompleted = FALSE;
-                        BOOL fCanceled = FALSE;
-
-                        size_t cbToRead = cbSize;
-                        size_t cbTransfered = 0;
-                        size_t cbRead;
-                        SafeArray<BYTE> aInputData(_64K);
-                        while (   SUCCEEDED(execProgress->COMGETTER(Completed(&fCompleted)))
-                               && !fCompleted)
-                        {
-                            if (!cbToRead)
-                                cbRead = 0;
-                            else
+                            if (!aOutputData.size())
                             {
-                                vrc = RTFileRead(fileSource, (uint8_t*)aInputData.raw(),
-                                                 RT_MIN(cbToRead, _64K), &cbRead);
-                                /*
-                                 * Some other error occured? There might be a chance that RTFileRead
-                                 * could not resolve/map the native error code to an IPRT code, so just
-                                 * print a generic error.
-                                 */
-                                if (RT_FAILURE(vrc))
-                                {
+                                if (cbToRead)
                                     rc = TaskGuest::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
-                                                                         Guest::tr("Could not read from file \"%s\" (%Rrc)"),
-                                                                         aTask->strSource.c_str(), vrc);
-                                    break;
-                                }
-                            }
-
-                            /* Resize buffer to reflect amount we just have read.
-                             * Size 0 is allowed! */
-                            aInputData.resize(cbRead);
-
-                            ULONG uFlags = ProcessInputFlag_None;
-                            /* Did we reach the end of the content we want to transfer (last chunk)? */
-                            if (   (cbRead < _64K)
-                                /* Did we reach the last block which is exactly _64K? */
-                                || (cbToRead - cbRead == 0)
-                                /* ... or does the user want to cancel? */
-                                || (   SUCCEEDED(aTask->progress->COMGETTER(Canceled(&fCanceled)))
-                                    && fCanceled)
-                               )
-                            {
-                                uFlags |= ProcessInputFlag_EndOfFile;
-                            }
-
-                            /* Transfer the current chunk ... */
-                            ULONG uBytesWritten;
-                            rc = pGuest->SetProcessInput(uPID, uFlags,
-                                                         10 * 1000 /* Wait 10s for getting the input data transfered. */,
-                                                         ComSafeArrayAsInParam(aInputData), &uBytesWritten);
-                            if (FAILED(rc))
-                            {
-                                rc = TaskGuest::setProgressErrorInfo(rc, aTask->progress, pGuest);
+                                                                         Guest::tr("Unexpected end of file \"%s\" (%u bytes left)"),
+                                                                         aTask->strSource.c_str(), cbToRead);
                                 break;
                             }
 
-                            Assert(cbRead <= cbToRead);
-                            Assert(cbToRead >= cbRead);
-                            cbToRead -= cbRead;
-
-                            cbTransfered += uBytesWritten;
-                            Assert(cbTransfered <= cbSize);
-                            aTask->progress->SetCurrentOperationProgress(cbTransfered / (cbSize / 100.0));
-
-                            /* End of file reached? */
-                            if (cbToRead == 0)
-                                break;
-
-                            /* Did the user cancel the operation above? */
-                            if (fCanceled)
-                                break;
-
-                            /* Progress canceled by Main API? */
-                            if (   SUCCEEDED(execProgress->COMGETTER(Canceled(&fCanceled)))
-                                && fCanceled)
+                            vrc = RTFileWrite(hFileDest, aOutputData.raw(), aOutputData.size(), NULL /* No partial writes */);
+                            if (RT_FAILURE(vrc))
                             {
                                 rc = TaskGuest::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
-                                                                     Guest::tr("Copy operation of file \"%s\" was canceled on guest side"),
-                                                                     aTask->strSource.c_str());
+                                                                     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));
                         }
-
-                        if (SUCCEEDED(rc))
+                        else
                         {
-                            /*
-                             * If we got here this means the started process either was completed,
-                             * canceled or we simply got all stuff transferred.
-                             */
-                            ExecuteProcessStatus_T retStatus;
-                            ULONG uRetExitCode;
-                            rc = pGuest->waitForProcessStatusChange(uPID, &retStatus, &uRetExitCode, 10 * 1000 /* 10s timeout. */);
-                            if (FAILED(rc))
-                            {
-                                rc = TaskGuest::setProgressErrorInfo(rc, aTask->progress, pGuest);
-                            }
-                            else
-                            {
-                                if (   uRetExitCode != 0
-                                    || retStatus    != ExecuteProcessStatus_TerminatedNormally)
-                                {
-                                    rc = TaskGuest::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
-                                                                         Guest::tr("Guest reported error %u while copying file \"%s\" to \"%s\""),
-                                                                         uRetExitCode, aTask->strSource.c_str(), aTask->strDest.c_str());
-                                }
-                            }
-                        }
-
-                        if (SUCCEEDED(rc))
-                        {
-                            if (fCanceled)
-                            {
-                                /*
-                                 * In order to make the progress object to behave nicely, we also have to
-                                 * notify the object with a complete event when it's canceled.
-                                 */
-                                aTask->progress->notifyComplete(VBOX_E_IPRT_ERROR,
-                                                                COM_IIDOF(IGuest),
-                                                                Guest::getStaticComponentName(),
-                                                                Guest::tr("Copying file \"%s\" canceled"), aTask->strSource.c_str());
-                            }
-                            else
-                            {
-                                /*
-                                 * Even if we succeeded until here make sure to check whether we really transfered
-                                 * everything.
-                                 */
-                                if (   cbSize > 0
-                                    && cbTransfered == 0)
-                                {
-                                    /* If nothing was transfered but the file size was > 0 then "vbox_cat" wasn't able to write
-                                     * to the destination -> access denied. */
-                                    rc = TaskGuest::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
-                                                                         Guest::tr("Access denied when copying file \"%s\" to \"%s\""),
-                                                                         aTask->strSource.c_str(), aTask->strDest.c_str());
-                                }
-                                else if (cbTransfered < cbSize)
-                                {
-                                    /* If we did not copy all let the user know. */
-                                    rc = TaskGuest::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
-                                                                         Guest::tr("Copying file \"%s\" failed (%u/%u bytes transfered)"),
-                                                                         aTask->strSource.c_str(), cbTransfered, cbSize);
-                                }
-                                else /* Yay, all went fine! */
-                                    aTask->progress->notifyComplete(S_OK);
-                            }
+                            rc = TaskGuest::setProgressErrorInfo(rc, aTask->progress, pGuest);
+                            break;
                         }
                     }
+
+                    if (SUCCEEDED(rc))
+                        aTask->progress->notifyComplete(S_OK);
+
+                    RTFileClose(hFileDest);
                 }
-                RTFileClose(fileSource);
             }
         }
-#endif
     }
     catch (HRESULT aRC)
@@ -2874,11 +2770,4 @@
                                   ULONG aFlags, IProgress **aProgress)
 {
-    ReturnComNotImplemented();
-}
-
-STDMETHODIMP Guest::CopyToGuest(IN_BSTR aSource, IN_BSTR aDest,
-                                IN_BSTR aUserName, IN_BSTR aPassword,
-                                ULONG aFlags, IProgress **aProgress)
-{
 #ifndef VBOX_WITH_GUEST_CONTROL
     ReturnComNotImplemented();
@@ -2915,10 +2804,10 @@
 
         rc = progress->init(static_cast<IGuest*>(this),
-                            Bstr(tr("Copying file from host to guest")).raw(),
+                            Bstr(tr("Copying file from guest to host")).raw(),
                             TRUE /* aCancelable */);
         if (FAILED(rc)) throw rc;
 
         /* Initialize our worker task. */
-        TaskGuest *pTask = new TaskGuest(TaskGuest::CopyFileToGuest, this, progress);
+        TaskGuest *pTask = new TaskGuest(TaskGuest::CopyFileFromGuest, this, progress);
         AssertPtr(pTask);
         std::auto_ptr<TaskGuest> task(pTask);
@@ -2952,7 +2841,95 @@
 }
 
+STDMETHODIMP Guest::CopyToGuest(IN_BSTR aSource, IN_BSTR aDest,
+                                IN_BSTR aUserName, IN_BSTR aPassword,
+                                ULONG aFlags, IProgress **aProgress)
+{
+#ifndef VBOX_WITH_GUEST_CONTROL
+    ReturnComNotImplemented();
+#else /* VBOX_WITH_GUEST_CONTROL */
+    CheckComArgStrNotEmptyOrNull(aSource);
+    CheckComArgStrNotEmptyOrNull(aDest);
+    CheckComArgStrNotEmptyOrNull(aUserName);
+    CheckComArgStrNotEmptyOrNull(aPassword);
+    CheckComArgOutPointerValid(aProgress);
+
+    AutoCaller autoCaller(this);
+    if (FAILED(autoCaller.rc())) return autoCaller.rc();
+
+    /* Validate flags. */
+    if (aFlags != CopyFileFlag_None)
+    {
+        if (   !(aFlags & CopyFileFlag_Recursive)
+            && !(aFlags & CopyFileFlag_Update)
+            && !(aFlags & CopyFileFlag_FollowLinks))
+        {
+            return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);
+        }
+    }
+
+    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+    HRESULT rc = S_OK;
+
+    ComObjPtr<Progress> progress;
+    try
+    {
+        /* Create the progress object. */
+        progress.createObject();
+
+        rc = progress->init(static_cast<IGuest*>(this),
+                            Bstr(tr("Copying file from host to guest")).raw(),
+                            TRUE /* aCancelable */);
+        if (FAILED(rc)) throw rc;
+
+        /* Initialize our worker task. */
+        TaskGuest *pTask = new TaskGuest(TaskGuest::CopyFileToGuest, this, progress);
+        AssertPtr(pTask);
+        std::auto_ptr<TaskGuest> task(pTask);
+
+        /* Assign data - aSource is the source file on the host,
+         * aDest reflects the full path on the guest. */
+        task->strSource   = (Utf8Str(aSource));
+        task->strDest     = (Utf8Str(aDest));
+        task->strUserName = (Utf8Str(aUserName));
+        task->strPassword = (Utf8Str(aPassword));
+        task->uFlags      = aFlags;
+
+        rc = task->startThread();
+        if (FAILED(rc)) throw rc;
+
+        /* Don't destruct on success. */
+        task.release();
+    }
+    catch (HRESULT aRC)
+    {
+        rc = aRC;
+    }
+
+    if (SUCCEEDED(rc))
+    {
+        /* Return progress to the caller. */
+        progress.queryInterfaceTo(aProgress);
+    }
+    return rc;
+#endif /* VBOX_WITH_GUEST_CONTROL */
+}
+
 STDMETHODIMP Guest::DirectoryClose(ULONG aHandle)
 {
+#ifndef VBOX_WITH_GUEST_CONTROL
     ReturnComNotImplemented();
+#else /* VBOX_WITH_GUEST_CONTROL */
+    using namespace guestControl;
+
+    if (directoryHandleExists(aHandle))
+    {
+        directoryDestroyHandle(aHandle);
+        return S_OK;
+    }
+
+    return setError(VBOX_E_IPRT_ERROR,
+                    Guest::tr("Directory handle is invalid"));
+#endif
 }
 
@@ -3087,17 +3064,101 @@
 }
 
+/**
+ * Creates a new directory handle ID and returns it.
+ *
+ * @return IPRT status code.
+ * @param puHandle             Pointer where the handle gets stored to.
+ * @param pszDirectory         Directory the handle is assigned to.
+ * @param pszFilter            Directory filter.  Optional.
+ * @param uFlags               Directory open flags.
+ *
+ */
+int Guest::directoryCreateHandle(ULONG *puHandle, const char *pszDirectory, const char *pszFilter, ULONG uFlags)
+{
+    AssertPtrReturn(puHandle, VERR_INVALID_POINTER);
+    AssertPtrReturn(pszDirectory, VERR_INVALID_POINTER);
+    /* pszFilter is optional. */
+
+    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+    int rc = VERR_TOO_MUCH_DATA;
+    for (uint32_t i = 0; i < UINT32_MAX; i++)
+    {
+        /* Create a new context ID ... */
+        uint32_t uHandleTry = ASMAtomicIncU32(&mNextDirectoryID);
+        GuestDirectoryMapIter it = mGuestDirectoryMap.find(uHandleTry);
+        if (mGuestDirectoryMap.end() == it)
+        {
+            rc = VINF_SUCCESS;
+            if (!RTStrAPrintf(&mGuestDirectoryMap[uHandleTry].mpszDirectory, pszDirectory))
+                rc = VERR_NO_MEMORY;
+            else
+            {
+                /* Filter is optional. */
+                if (pszFilter)
+                {
+                    if (!RTStrAPrintf(&mGuestDirectoryMap[uHandleTry].mpszFilter, pszFilter))
+                        rc = VERR_NO_MEMORY;
+                }
+
+                if (RT_SUCCESS(rc))
+                {
+                    mGuestDirectoryMap[uHandleTry].uFlags = uFlags;
+                    *puHandle = uHandleTry;
+
+                    break;
+                }
+            }
+
+            if (RT_FAILURE(rc))
+                break;
+
+            Assert(mGuestDirectoryMap.size());
+        }
+    }
+
+    return rc;
+}
+
+void Guest::directoryDestroyHandle(uint32_t uHandle)
+{
+    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+    GuestDirectoryMapIter it = mGuestDirectoryMap.find(uHandle);
+    if (it != mGuestDirectoryMap.end())
+    {
+        RTStrFree(it->second.mpszDirectory);
+        RTStrFree(it->second.mpszFilter);
+
+        /* Remove callback context (not used anymore). */
+        mGuestDirectoryMap.erase(it);
+    }
+}
+
+uint32_t Guest::directoryGetPID(uint32_t uHandle)
+{
+    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+    GuestDirectoryMapIter it = mGuestDirectoryMap.find(uHandle);
+    if (it != mGuestDirectoryMap.end())
+        return it->second.mPID;
+
+    return 0;
+}
+
+bool Guest::directoryHandleExists(uint32_t uHandle)
+{
+    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+    GuestDirectoryMapIter it = mGuestDirectoryMap.find(uHandle);
+    if (it != mGuestDirectoryMap.end())
+        return true;
+
+    return false;
+}
+
 STDMETHODIMP Guest::DirectoryOpen(IN_BSTR aDirectory, IN_BSTR aFilter,
                                   ULONG aFlags, IN_BSTR aUserName, IN_BSTR aPassword,
                                   ULONG *aHandle)
-{
-    ReturnComNotImplemented();
-}
-
-STDMETHODIMP Guest::DirectoryRead(ULONG aHandle, IGuestDirEntry **aDirEntry)
-{
-    ReturnComNotImplemented();
-}
-
-STDMETHODIMP Guest::FileExists(IN_BSTR aFile, IN_BSTR aUserName, IN_BSTR aPassword, BOOL *aExists)
 {
 #ifndef VBOX_WITH_GUEST_CONTROL
@@ -3106,9 +3167,22 @@
     using namespace guestControl;
 
-    return VBOX_E_NOT_SUPPORTED;
+    CheckComArgStrNotEmptyOrNull(aDirectory);
+    CheckComArgNotNull(aHandle);
+
+    /* Do not allow anonymous executions (with system rights). */
+    if (RT_UNLIKELY((aUserName) == NULL || *(aUserName) == '\0'))
+        return setError(E_INVALIDARG, tr("No user name specified"));
+
+    return directoryOpenInternal(aDirectory, aFilter,
+                                 aFlags,
+                                 aUserName, aPassword,
+                                 aHandle, NULL /* rc */);
 #endif
 }
 
-STDMETHODIMP Guest::FileQuerySize(IN_BSTR aFile, IN_BSTR aUserName, IN_BSTR aPassword, LONG64 *aSize)
+HRESULT Guest::directoryOpenInternal(IN_BSTR aDirectory, IN_BSTR aFilter,
+                                     ULONG aFlags,
+                                     IN_BSTR aUserName, IN_BSTR aPassword,
+                                     ULONG *aHandle, int *pRC)
 {
 #ifndef VBOX_WITH_GUEST_CONTROL
@@ -3117,5 +3191,414 @@
     using namespace guestControl;
 
-    return VBOX_E_NOT_SUPPORTED;
+    CheckComArgStrNotEmptyOrNull(aDirectory);
+    CheckComArgNotNull(aHandle);
+
+    AutoCaller autoCaller(this);
+    if (FAILED(autoCaller.rc())) return autoCaller.rc();
+
+    /* Validate flags. No flags supported yet. */
+    if (aFlags != DirectoryOpenFlag_None)
+        return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);
+
+    HRESULT rc = S_OK;
+    try
+    {
+        Utf8Str Utf8Directory(aDirectory);
+        Utf8Str Utf8Filter(aFilter);
+        Utf8Str Utf8UserName(aUserName);
+        Utf8Str Utf8Password(aPassword);
+
+        com::SafeArray<IN_BSTR> args;
+        com::SafeArray<IN_BSTR> env;
+
+        /*
+         * Prepare tool command line.
+         */
+
+        /* We need to get output which is machine-readable in form
+         * of "key=value\0..key=value\0\0". */
+        args.push_back(Bstr("--machinereadable").raw());
+
+        /* We want the long output format. Handy for getting a lot of
+         * details we could (should?) use (later). */
+        args.push_back(Bstr("-l").raw());
+
+        /* As we want to keep this stuff simple we don't do recursive (-R)
+         * or dereferencing (--dereference) lookups here. This has to be done by
+         * the user. */
+
+        /* Construct and hand in actual directory name + filter we want to open. */
+        char *pszDirectoryFinal;
+        int cbRet;
+        if (Utf8Filter.isEmpty())
+            cbRet = RTStrAPrintf(&pszDirectoryFinal, "%s", Utf8Directory.c_str());
+        else
+            cbRet = RTStrAPrintf(&pszDirectoryFinal, "%s/%s",
+                                 Utf8Directory.c_str(), Utf8Filter.c_str());
+        if (!cbRet)
+            return setError(E_OUTOFMEMORY, tr("Out of memory while allocating final directory"));
+
+        args.push_back(Bstr(pszDirectoryFinal).raw());  /* The directory we want to open. */
+
+        /*
+         * Execute guest process.
+         */
+        ComPtr<IProgress> progressExec;
+        ULONG uPID;
+
+        rc = ExecuteProcess(Bstr(VBOXSERVICE_TOOL_LS).raw(),
+                            ExecuteProcessFlag_Hidden,
+                            ComSafeArrayAsInParam(args),
+                            ComSafeArrayAsInParam(env),
+                            Bstr(Utf8UserName).raw(),
+                            Bstr(Utf8Password).raw(),
+                            30 * 1000 /* Wait 30s for getting the process started. */,
+                            &uPID, progressExec.asOutParam());
+
+        RTStrFree(pszDirectoryFinal);
+
+        if (SUCCEEDED(rc))
+        {
+            /* Wait for process to exit ... */
+            rc = progressExec->WaitForCompletion(-1);
+            if (FAILED(rc)) return rc;
+
+            BOOL fCompleted = FALSE;
+            BOOL fCanceled = FALSE;
+            progressExec->COMGETTER(Completed)(&fCompleted);
+            if (!fCompleted)
+                progressExec->COMGETTER(Canceled)(&fCanceled);
+
+            if (fCompleted)
+            {
+                ExecuteProcessStatus_T retStatus;
+                ULONG uRetExitCode, uRetFlags;
+                if (SUCCEEDED(rc))
+                {
+                    rc = GetProcessStatus(uPID, &uRetExitCode, &uRetFlags, &retStatus);
+                    if (SUCCEEDED(rc) && uRetExitCode != 0)
+                    {
+                        rc = setError(VBOX_E_IPRT_ERROR,
+                                      tr("Error %u while opening guest directory"), uRetExitCode);
+                    }
+                }
+            }
+            else if (fCanceled)
+                rc = setError(VBOX_E_IPRT_ERROR,
+                              tr("Guest directory opening was aborted"));
+            else
+                AssertReleaseMsgFailed(("Guest directory opening neither completed nor canceled!?\n"));
+
+            if (SUCCEEDED(rc))
+            {
+                /* Assign new directory handle ID. */
+                int vrc = directoryCreateHandle(aHandle,
+                                                Utf8Directory.c_str(),
+                                                Utf8Filter.isEmpty() ? NULL : Utf8Filter.c_str(),
+                                                aFlags);
+                if (RT_FAILURE(vrc))
+                {
+                    rc = setError(VBOX_E_IPRT_ERROR,
+                                  tr("Unable to create guest directory handle (%Rrc)"), vrc);
+                }
+            }
+        }
+    }
+    catch (std::bad_alloc &)
+    {
+        rc = E_OUTOFMEMORY;
+    }
+    return rc;
+#endif /* VBOX_WITH_GUEST_CONTROL */
+}
+
+STDMETHODIMP Guest::DirectoryRead(ULONG aHandle, IGuestDirEntry **aDirEntry)
+{
+#ifndef VBOX_WITH_GUEST_CONTROL
+    ReturnComNotImplemented();
+#else /* VBOX_WITH_GUEST_CONTROL */
+    using namespace guestControl;
+
+    uint32_t uPID = directoryGetPID(aHandle);
+    if (uPID)
+    {
+        SafeArray<BYTE> aOutputData;
+        ULONG cbOutputData = 0;
+
+        HRESULT rc = this->GetProcessOutput(uPID, ProcessOutputFlag_None,
+                                            30 * 1000 /* Timeout in ms */,
+                                            _64K, ComSafeArrayAsOutParam(aOutputData));
+        if (SUCCEEDED(rc))
+        {
+
+        }
+
+        return rc;
+    }
+
+    return setError(VBOX_E_IPRT_ERROR,
+                    Guest::tr("Directory handle is invalid"));
+#endif
+}
+
+STDMETHODIMP Guest::FileExists(IN_BSTR aFile, IN_BSTR aUserName, IN_BSTR aPassword, BOOL *aExists)
+{
+#ifndef VBOX_WITH_GUEST_CONTROL
+    ReturnComNotImplemented();
+#else /* VBOX_WITH_GUEST_CONTROL */
+    using namespace guestControl;
+
+    CheckComArgStrNotEmptyOrNull(aFile);
+
+    /* Do not allow anonymous executions (with system rights). */
+    if (RT_UNLIKELY((aUserName) == NULL || *(aUserName) == '\0'))
+        return setError(E_INVALIDARG, tr("No user name specified"));
+
+    return fileExistsInternal(aFile,
+                              aUserName, aPassword, aExists,
+                              NULL /* rc */);
+#endif
+}
+
+HRESULT Guest::fileExistsInternal(IN_BSTR aFile, IN_BSTR aUserName, IN_BSTR aPassword, BOOL *aExists, int *pRC)
+{
+#ifndef VBOX_WITH_GUEST_CONTROL
+    ReturnComNotImplemented();
+#else /* VBOX_WITH_GUEST_CONTROL */
+    using namespace guestControl;
+
+    CheckComArgStrNotEmptyOrNull(aFile);
+
+    AutoCaller autoCaller(this);
+    if (FAILED(autoCaller.rc())) return autoCaller.rc();
+
+    HRESULT rc = S_OK;
+    try
+    {
+        Utf8Str Utf8File(aFile);
+        Utf8Str Utf8UserName(aUserName);
+        Utf8Str Utf8Password(aPassword);
+
+        com::SafeArray<IN_BSTR> args;
+        com::SafeArray<IN_BSTR> env;
+
+        /*
+         * Prepare tool command line.
+         */
+
+        /* Only the actual file name to chekc is needed for now. */
+        args.push_back(Bstr(Utf8File).raw());
+
+        /*
+         * Execute guest process.
+         */
+        ComPtr<IProgress> progressExec;
+        ULONG uPID;
+
+        rc = ExecuteProcess(Bstr(VBOXSERVICE_TOOL_STAT).raw(),
+                            ExecuteProcessFlag_Hidden,
+                            ComSafeArrayAsInParam(args),
+                            ComSafeArrayAsInParam(env),
+                            Bstr(Utf8UserName).raw(),
+                            Bstr(Utf8Password).raw(),
+                            30 * 1000 /* Wait 30s for getting the process started. */,
+                            &uPID, progressExec.asOutParam());
+
+        if (SUCCEEDED(rc))
+        {
+            /* Wait for process to exit ... */
+            rc = progressExec->WaitForCompletion(-1);
+            if (FAILED(rc)) return rc;
+
+            BOOL fCompleted = FALSE;
+            BOOL fCanceled = FALSE;
+            progressExec->COMGETTER(Completed)(&fCompleted);
+            if (!fCompleted)
+                progressExec->COMGETTER(Canceled)(&fCanceled);
+
+            if (fCompleted)
+            {
+                ExecuteProcessStatus_T retStatus;
+                ULONG uRetExitCode, uRetFlags;
+                if (SUCCEEDED(rc))
+                {
+                    rc = GetProcessStatus(uPID, &uRetExitCode, &uRetFlags, &retStatus);
+                    if (SUCCEEDED(rc))
+                    {
+                        *aExists = uRetExitCode == 0 ? TRUE : FALSE;
+                    }
+                    else
+                        rc = setError(VBOX_E_IPRT_ERROR,
+                                      tr("Error %u while checking for existence of file \"%s\""),
+                                      uRetExitCode, Utf8File.c_str());
+                }
+            }
+            else if (fCanceled)
+                rc = setError(VBOX_E_IPRT_ERROR,
+                              tr("Checking for file existence was aborted"));
+            else
+                AssertReleaseMsgFailed(("Checking for file existence neither completed nor canceled!?\n"));
+        }
+    }
+    catch (std::bad_alloc &)
+    {
+        rc = E_OUTOFMEMORY;
+    }
+    return rc;
+#endif
+}
+
+STDMETHODIMP Guest::FileQuerySize(IN_BSTR aFile, IN_BSTR aUserName, IN_BSTR aPassword, LONG64 *aSize)
+{
+#ifndef VBOX_WITH_GUEST_CONTROL
+    ReturnComNotImplemented();
+#else /* VBOX_WITH_GUEST_CONTROL */
+    using namespace guestControl;
+
+    CheckComArgStrNotEmptyOrNull(aFile);
+
+    /* Do not allow anonymous executions (with system rights). */
+    if (RT_UNLIKELY((aUserName) == NULL || *(aUserName) == '\0'))
+        return setError(E_INVALIDARG, tr("No user name specified"));
+
+    return fileQuerySizeInternal(aFile,
+                                 aUserName, aPassword, aSize,
+                                 NULL /* rc */);
+#endif
+}
+
+HRESULT Guest::fileQuerySizeInternal(IN_BSTR aFile, IN_BSTR aUserName, IN_BSTR aPassword, LONG64 *aSize, int *pRC)
+{
+#ifndef VBOX_WITH_GUEST_CONTROL
+    ReturnComNotImplemented();
+#else /* VBOX_WITH_GUEST_CONTROL */
+    using namespace guestControl;
+
+    CheckComArgStrNotEmptyOrNull(aFile);
+
+    AutoCaller autoCaller(this);
+    if (FAILED(autoCaller.rc())) return autoCaller.rc();
+
+    HRESULT rc = S_OK;
+    try
+    {
+        Utf8Str Utf8File(aFile);
+        Utf8Str Utf8UserName(aUserName);
+        Utf8Str Utf8Password(aPassword);
+
+        com::SafeArray<IN_BSTR> args;
+        com::SafeArray<IN_BSTR> env;
+
+        /*
+         * Prepare tool command line.
+         */
+
+        /* We need to get output which is machine-readable in form
+         * of "key=value\0..key=value\0\0". */
+        args.push_back(Bstr("--machinereadable").raw());
+
+        /* Only the actual file name to chekc is needed for now. */
+        args.push_back(Bstr(Utf8File).raw());
+
+        /*
+         * Execute guest process.
+         */
+        ComPtr<IProgress> progressExec;
+        ULONG uPID;
+
+        rc = ExecuteProcess(Bstr(VBOXSERVICE_TOOL_STAT).raw(),
+                            ExecuteProcessFlag_Hidden,
+                            ComSafeArrayAsInParam(args),
+                            ComSafeArrayAsInParam(env),
+                            Bstr(Utf8UserName).raw(),
+                            Bstr(Utf8Password).raw(),
+                            30 * 1000 /* Wait 30s for getting the process started. */,
+                            &uPID, progressExec.asOutParam());
+
+        if (SUCCEEDED(rc))
+        {
+            /* Wait for process to exit ... */
+            rc = progressExec->WaitForCompletion(-1);
+            if (FAILED(rc)) return rc;
+
+            BOOL fCompleted = FALSE;
+            BOOL fCanceled = FALSE;
+            progressExec->COMGETTER(Completed)(&fCompleted);
+            if (!fCompleted)
+                progressExec->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)
+                        {
+                            /* Get file size from output stream. */
+                            SafeArray<BYTE> aOutputData;
+                            ULONG cbOutputData = 0;
+
+                            GuestProcessStream guestStream;
+                            int vrc = VINF_SUCCESS;
+                            for (;;)
+                            {
+                                rc = this->GetProcessOutput(uPID, ProcessOutputFlag_None,
+                                                            30 * 1000 /* Timeout in ms */,
+                                                            _64K, ComSafeArrayAsOutParam(aOutputData));
+                                /** @todo Do stream header validation! */
+                                if (   SUCCEEDED(rc)
+                                    && aOutputData.size())
+                                {
+                                    vrc = guestStream.AddData(aOutputData.raw(), aOutputData.size());
+                                    if (RT_UNLIKELY(RT_FAILURE(vrc)))
+                                        rc = setError(VBOX_E_IPRT_ERROR,
+                                                      tr("Error while adding guest output to stream buffer (%Rrc)"), vrc);
+                                }
+                                else
+                                    break;
+                            }
+
+                            if (SUCCEEDED(rc))
+                            {
+                                vrc = guestStream.Parse();
+                                if (   RT_SUCCESS(vrc)
+                                    || vrc == VERR_MORE_DATA)
+                                {
+                                    int64_t iVal;
+                                    vrc = guestStream.GetInt64Ex("st_size", &iVal);
+                                    if (RT_SUCCESS(vrc))
+                                        *aSize = iVal;
+                                    else
+                                        rc = setError(VBOX_E_IPRT_ERROR,
+                                                      tr("Unable to retrieve file size (%Rrc)"), vrc);
+                                }
+                                else
+                                    rc = setError(VBOX_E_IPRT_ERROR,
+                                                  tr("Error while parsing guest output (%Rrc)"), vrc);
+                            }
+                        }
+                        else
+                            rc = setError(VBOX_E_IPRT_ERROR,
+                                          tr("Error querying file size for file \"%s\" (exit code %u)"),
+                                          Utf8File.c_str(), uRetExitCode);
+                    }
+                }
+            }
+            else if (fCanceled)
+                rc = setError(VBOX_E_IPRT_ERROR,
+                              tr("Checking for file existence was aborted"));
+            else
+                AssertReleaseMsgFailed(("Checking for file existence neither completed nor canceled!?\n"));
+        }
+    }
+    catch (std::bad_alloc &)
+    {
+        rc = E_OUTOFMEMORY;
+    }
+    return rc;
 #endif
 }
Index: /trunk/src/VBox/Main/testcase/tstGuestCtrlParseBuffer.cpp
===================================================================
--- /trunk/src/VBox/Main/testcase/tstGuestCtrlParseBuffer.cpp	(revision 38084)
+++ /trunk/src/VBox/Main/testcase/tstGuestCtrlParseBuffer.cpp	(revision 38085)
@@ -54,27 +54,31 @@
 {
     /* Invalid stuff. */
-    { NULL,                             0,                                                 0,  0,       0, VERR_INVALID_POINTER },
-    { NULL,                             512,                                               0,  0,       0, VERR_INVALID_POINTER },
-    { "",                               0,                                                 0,  0,       0, VERR_INVALID_PARAMETER },
-    { "",                               0,                                                 0,  0,       0, VERR_INVALID_PARAMETER },
-    { "foo=bar1",                       0,                                                 0,  0,       0, VERR_INVALID_PARAMETER },
-    { "foo=bar2",                       0,                                                 50, 50,      0, VERR_INVALID_PARAMETER },
-    /* Incomplete buffer (missing \0 termination). */
-    { "",                               1,                                                 0, 0,       0, VERR_MORE_DATA },
-    { "\0",                             1,                                                 0, 0,       0, VERR_MORE_DATA },
-    { szUnterm1,                        5,                                                 0, 0,       0, VERR_MORE_DATA },
-    { "foo1",                           sizeof("foo1"),                                    0, 0,       0, VERR_MORE_DATA },
-    { szUnterm2,                        8,                                                 0, 0,       0, VERR_MORE_DATA },
+    { NULL,                             0,                                                 0,  0,                                         0, VERR_INVALID_POINTER },
+    { NULL,                             512,                                               0,  0,                                         0, VERR_INVALID_POINTER },
+    { "",                               0,                                                 0,  0,                                         0, VERR_INVALID_PARAMETER },
+    { "",                               0,                                                 0,  0,                                         0, VERR_INVALID_PARAMETER },
+    { "foo=bar1",                       0,                                                 0,  0,                                         0, VERR_INVALID_PARAMETER },
+    { "foo=bar2",                       0,                                                 50, 50,                                        0, VERR_INVALID_PARAMETER },
+    /* Empty buffers. */
+    { "",                               1,                                                 0, 1,                                          0, VERR_MORE_DATA },
+    { "\0",                             1,                                                 0, 1,                                          0, VERR_MORE_DATA },
     /* Incomplete buffer (missing components). */
-    { "=bar\0",                         sizeof("=bar"),                                    0,  0,                                         0, VERR_MORE_DATA },
+    { szUnterm1,                        5,                                                 0, 0,                                          0, VERR_MORE_DATA },
+    { "foo1",                           sizeof("foo1"),                                    0, 0,                                          0, VERR_MORE_DATA },
+    { "=bar\0",                         sizeof("=bar"),                                    0, 0 ,                                         0, VERR_MORE_DATA },
     /* Last sequence is incomplete -- new offset should point to it. */
     { "hug=sub\0incomplete",            sizeof("hug=sub\0incomplete"),                     0,  sizeof("hug=sub"),                         1, VERR_MORE_DATA },
     { "boo=hoo\0baz=boo\0qwer",         sizeof("boo=hoo\0baz=boo\0qwer"),                  0,  sizeof("boo=hoo\0baz=boo"),                2, VERR_MORE_DATA },
     /* Parsing good stuff. */
+    { "novalue=",                       sizeof("novalue="),                                0,  sizeof("novalue="),                        1, VINF_SUCCESS },
+    { szUnterm2,                        8,                                                 0,  sizeof(szUnterm2),                         1, VINF_SUCCESS },
     { "foo2=",                          sizeof("foo2="),                                   0,  sizeof("foo2="),                           1, VINF_SUCCESS },
     { "har=hor",                        sizeof("har=hor"),                                 0,  sizeof("har=hor"),                         1, VINF_SUCCESS },
     { "foo=bar\0baz=boo",               sizeof("foo=bar\0baz=boo"),                        0,  sizeof("foo=bar\0baz=boo"),                2, VINF_SUCCESS },
     /* Parsing until a different block (two terminations, returning offset to next block). */
-    { "off=rab\0\0zab=oob",             sizeof("off=rab\0\0zab=oob"),                      0,  sizeof("zab=oob"),                         1, VERR_MORE_DATA }
+    { "off=rab\0a=b\0\0\0\0",           sizeof("off=rab\0a=b\0\0\0"),                      0,  13,                                        2, VERR_MORE_DATA },
+    { "off=rab\0\0zab=oob",             sizeof("off=rab\0\0zab=oob"),                      0,  9,                                         1, VERR_MORE_DATA },
+    { "\0\0\0\0off=rab\0zab=oob\0\0",   sizeof("\0\0\0\0off=rab\0zab=oob\0\0"),            0,  1,                                         0, VERR_MORE_DATA },
+    { "o2=r2\0z3=o3\0\0f3=g3",          sizeof("o2=r2\0z3=o3\0\0f3=g3"),                   0,  13,                                        2, VERR_MORE_DATA }
 };
 
@@ -89,8 +93,10 @@
 } aTests2[] =
 {
+    { "\0\0\0\0",                                      sizeof("\0\0\0\0"),                                0, VERR_MORE_DATA },
     { "off=rab\0\0zab=oob",                            sizeof("off=rab\0\0zab=oob"),                      2, VINF_SUCCESS },
     { "\0\0\0soo=foo\0goo=loo\0\0zab=oob",             sizeof("\0\0\0soo=foo\0goo=loo\0\0zab=oob"),       2, VINF_SUCCESS },
-    { "qoo=uoo\0\0\0\0asdf=\0\0",                      sizeof("qoo=uoo\0\0\0\0asdf=\0\0"),                2, VINF_SUCCESS },
-    { "foo=bar\0\0\0\0\0\0",                           sizeof("foo=bar\0\0\0\0\0\0"),                     1, VINF_SUCCESS }
+    { "qoo=uoo\0\0\0\0asdf=\0\0",                      sizeof("qoo=uoo\0\0\0\0asdf=\0\0"),                2, VERR_MORE_DATA },
+    { "foo=bar\0\0\0\0\0\0",                           sizeof("foo=bar\0\0\0\0\0\0"),                     1, VERR_MORE_DATA },
+    { "qwer=cvbnr\0\0\0gui=uig\0\0\0",                 sizeof("qwer=cvbnr\0\0\0gui=uig\0\0\0"),           2, VERR_MORE_DATA }
 };
 
@@ -110,5 +116,5 @@
         const char *pszEnd = pszStart;
 
-        /* Search and of current pair (key=value\0). */
+        /* Search end of current pair (key=value\0). */
         while (uCur++ < cbData)
         {
@@ -119,62 +125,80 @@
 
         size_t uPairLen = pszEnd - pszStart;
-        if (   *pszEnd != '\0'
-            || !uPairLen)
-        {
-            rc = VERR_MORE_DATA;
-            break;
-        }
-
-        const char *pszSep = pszStart;
-        while (   *pszSep != '='
-               &&  pszSep != pszEnd)
-        {
-            pszSep++;
-        }
-
-        if (   pszSep == pszStart
-            || pszSep == pszEnd)
-        {
-            rc = VERR_MORE_DATA;
-            break;
-        }
-
-        size_t uKeyLen = pszSep - pszStart;
-        size_t uValLen = pszEnd - (pszSep + 1);
-
-        /* Get key (if present). */
-        if (uKeyLen)
-        {
-            Assert(pszSep > pszStart);
-            char *pszKey = (char*)RTMemAllocZ(uKeyLen + 1);
-            if (!pszKey)
-            {
-                rc = VERR_NO_MEMORY;
+        if (uPairLen)
+        {
+            const char *pszSep = pszStart;
+            while (   *pszSep != '='
+                   &&  pszSep != pszEnd)
+            {
+                pszSep++;
+            }
+
+            /* No separator found (or incomplete key=value pair)? */
+            if (   pszSep == pszStart
+                || pszSep == pszEnd)
+            {
+                *puOffset =  uCur - uPairLen - 1;
+                rc = VERR_MORE_DATA;
+            }
+
+            if (RT_FAILURE(rc))
                 break;
-            }
-            memcpy(pszKey, pszStart, uKeyLen);
-
-            mapBuf[RTCString(pszKey)].pszValue = NULL;
-
-            /* Get value (if present). */
-            if (uValLen)
-            {
-                Assert(pszEnd > pszSep);
-                char *pszVal = (char*)RTMemAllocZ(uValLen + 1);
-                if (!pszVal)
+
+            size_t uKeyLen = pszSep - pszStart;
+            size_t uValLen = pszEnd - (pszSep + 1);
+
+            /* Get key (if present). */
+            if (uKeyLen)
+            {
+                Assert(pszSep > pszStart);
+                char *pszKey = (char*)RTMemAllocZ(uKeyLen + 1);
+                if (!pszKey)
                 {
                     rc = VERR_NO_MEMORY;
                     break;
                 }
-                memcpy(pszVal, pszSep + 1, uValLen);
-
-                mapBuf[RTCString(pszKey)].pszValue = pszVal;
-            }
-
-            RTMemFree(pszKey);
-
-            *puOffset += uCur - *puOffset;
-        }
+                memcpy(pszKey, pszStart, uKeyLen);
+
+                mapBuf[RTCString(pszKey)].pszValue = NULL;
+
+                /* Get value (if present). */
+                if (uValLen)
+                {
+                    Assert(pszEnd > pszSep);
+                    char *pszVal = (char*)RTMemAllocZ(uValLen + 1);
+                    if (!pszVal)
+                    {
+                        rc = VERR_NO_MEMORY;
+                        break;
+                    }
+                    memcpy(pszVal, pszSep + 1, uValLen);
+
+                    mapBuf[RTCString(pszKey)].pszValue = pszVal;
+                }
+
+                RTMemFree(pszKey);
+
+                *puOffset += uCur - *puOffset;
+            }
+        }
+        else /* No pair detected, check for a new block. */
+        {
+            do
+            {
+                if (*pszEnd == '\0')
+                {
+                    *puOffset = uCur;
+                    rc = VERR_MORE_DATA;
+                    break;
+                }
+                pszEnd++;
+            } while (++uCur < cbData);
+        }
+
+        if (RT_FAILURE(rc))
+            break;
     }
+
+    RT_CLAMP(*puOffset, 0, cbData);
 
     return rc;
@@ -206,14 +230,20 @@
 
     if (sizeof("sizecheck") != 10)
-        RTTestFailed(hTest, "Basic size test failed (%u <-> 10)", sizeof("sizecheck"));
+        RTTestFailed(hTest, "Basic size test #1 failed (%u <-> 10)", sizeof("sizecheck"));
+    if (sizeof("off=rab") != 8)
+        RTTestFailed(hTest, "Basic size test #2 failed (%u <-> 7)", sizeof("off=rab"));
+    if (sizeof("off=rab\0\0") != 10)
+        RTTestFailed(hTest, "Basic size test #3 failed (%u <-> 10)", sizeof("off=rab\0\0"));
 
     RTTestIPrintf(RTTESTLVL_INFO, "Doing line tests ...\n");
 
-    for (unsigned iTest = 0; iTest < RT_ELEMENTS(aTests); iTest++)
+    unsigned iTest = 0;
+    for (iTest; iTest < RT_ELEMENTS(aTests); iTest++)
     {
         GuestBufferMap bufMap;
+        uint32_t uOffset = aTests[iTest].uOffsetStart;
 
         int iResult = outputBufferParse((BYTE*)aTests[iTest].pbData, aTests[iTest].cbData,
-                                        &aTests[iTest].uOffsetStart, bufMap);
+                                        &uOffset, bufMap);
 
         RTTestIPrintf(RTTESTLVL_DEBUG, "=> Test #%u\n", iTest);
@@ -229,20 +259,21 @@
                          bufMap.size(), aTests[iTest].uMapElements);
         }
-        else if (aTests[iTest].uOffsetStart != aTests[iTest].uOffsetAfter)
+        else if (uOffset != aTests[iTest].uOffsetAfter)
         {
             RTTestFailed(hTest, "\tOffset %u wrong, expected %u",
-                         aTests[iTest].uOffsetStart, aTests[iTest].uOffsetAfter);
+                         uOffset, aTests[iTest].uOffsetAfter);
         }
         else if (iResult == VERR_MORE_DATA)
         {
+            RTTestIPrintf(RTTESTLVL_DEBUG, "\tMore data (Offset: %u)\n", uOffset);
+
             /* There is remaining data left in the buffer (which needs to be merged
              * with a following buffer) -- print it. */
-            const char *pszRemaining = aTests[iTest].pbData;
-            size_t uOffsetNew = aTests[iTest].uOffsetStart;
-            size_t uToWrite = aTests[iTest].cbData - uOffsetNew;
-            if (pszRemaining && uOffsetNew)
-            {
+            size_t uToWrite = aTests[iTest].cbData - uOffset;
+            if (uToWrite)
+            {
+                const char *pszRemaining = aTests[iTest].pbData;
                 RTTestIPrintf(RTTESTLVL_DEBUG, "\tRemaining (%u):\n", uToWrite);
-                RTStrmWriteEx(g_pStdOut, &aTests[iTest].pbData[uOffsetNew], uToWrite - 1, NULL);
+                RTStrmWriteEx(g_pStdOut, &aTests[iTest].pbData[uOffset], uToWrite - 1, NULL);
                 RTTestIPrintf(RTTESTLVL_DEBUG, "\n");
             }
@@ -264,5 +295,5 @@
         uint32_t uNumBlocks = 0;
 
-        while (uOffset < aTests2[iTest].cbData)
+        while (uOffset < aTests2[iTest].cbData - 1)
         {
             iResult = outputBufferParse((BYTE*)aTests2[iTest].pbData, aTests2[iTest].cbData,
@@ -279,5 +310,4 @@
                 RTTestIPrintf(RTTESTLVL_DEBUG, "\tNext offset %u (total: %u)\n",
                               uOffset, aTests2[iTest].cbData);
-                uOffset++;
             }
             else
@@ -288,5 +318,10 @@
         }
 
-        if (uNumBlocks != aTests2[iTest].uNumBlocks)
+        if (iResult != aTests2[iTest].iResult)
+        {
+            RTTestFailed(hTest, "\tReturned %Rrc, expected %Rrc",
+                         iResult, aTests2[iTest].iResult);
+        }
+        else if (uNumBlocks != aTests2[iTest].uNumBlocks)
         {
             RTTestFailed(hTest, "\tReturned %u blocks, expected %u\n",
