VirtualBox

Changeset 38085 in vbox


Ignore:
Timestamp:
Jul 21, 2011 7:29:54 AM (14 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
73032
Message:

GuestCtrl: Update of copy from guest (work in progress).

Location:
trunk
Files:
2 added
10 edited

Legend:

Unmodified
Added
Removed
  • trunk/doc/manual/en_US/user_VBoxManage.xml

    r38055 r38085  
    28802880
    28812881        <listitem>
     2882          <para><computeroutput>stat</computeroutput>, which displays file
     2883          or file system status on the guest.</para>
     2884
     2885          <screen>VBoxManage guestcontrol &lt;vmname&gt;|&lt;uuid&gt; stat
     2886            &lt;file element(s) to check on guest&gt;
     2887            [--username "&lt;name&gt;"] [--password "&lt;password&gt;"]
     2888            [--verbose]</screen>
     2889
     2890          <para>where the parameters mean: <glosslist>
     2891              <glossentry>
     2892                <glossterm>uuid|vmname</glossterm>
     2893
     2894                <glossdef>
     2895                  <para>The VM UUID or VM name. Mandatory.</para>
     2896                </glossdef>
     2897              </glossentry>
     2898
     2899              <glossentry>
     2900                <glossterm>file element(s) to check on guest</glossterm>
     2901
     2902                <glossdef>
     2903                  <para>Absolute path of directory/directories to check on
     2904                  guest, e.g. <computeroutput>/home/foo/a.out</computeroutput>.
     2905                  The specified user must have appropriate rights to access
     2906                  the given file element(s).</para>
     2907                </glossdef>
     2908              </glossentry>
     2909
     2910              <glossentry>
     2911                <glossterm>--username &lt;name&gt;</glossterm>
     2912
     2913                <glossdef>
     2914                  <para>Name of the user the copy process should run under.
     2915                  This user must exist on the guest OS.</para>
     2916                </glossdef>
     2917              </glossentry>
     2918
     2919              <glossentry>
     2920                <glossterm>--password &lt;password&gt;</glossterm>
     2921
     2922                <glossdef>
     2923                  <para>Password of the user account specified with
     2924                  <computeroutput>--username</computeroutput>. If not given,
     2925                  an empty password is assumed.</para>
     2926                </glossdef>
     2927              </glossentry>
     2928
     2929              <glossentry>
     2930                <glossterm>--verbose</glossterm>
     2931
     2932                <glossdef>
     2933                  <para>Tells VBoxManage to be more verbose.</para>
     2934                </glossdef>
     2935              </glossentry>
     2936            </glosslist></para>
     2937        </listitem>
     2938
     2939        <listitem>
    28822940          <para><computeroutput>updateadditions</computeroutput>, which allows
    28832941          for updating an already installed Guest Additions version on the
  • trunk/include/VBox/HostServices/GuestControlSvc.h

    r37375 r38085  
    8888 */
    8989#define VBOXSERVICE_TOOL_CAT        "vbox_cat"
     90#define VBOXSERVICE_TOOL_LS         "vbox_ls"
    9091#define VBOXSERVICE_TOOL_MKDIR      "vbox_mkdir"
     92#define VBOXSERVICE_TOOL_STAT       "vbox_stat"
    9193/** @} */
    9294
  • trunk/src/VBox/Additions/common/VBoxService/VBoxServiceControlExec.cpp

    r37816 r38085  
    12731273        {
    12741274            fPendingClose = true;
    1275             VBoxServiceVerbose(4, "ControlExec: Got last input block (PID %u) of size %u ...\n", uPID, cbSize);
     1275            VBoxServiceVerbose(4, "ControlExec: Got last input block (PID %u) of size %u ...\n",
     1276                               uPID, cbSize);
    12761277        }
    12771278
     
    13191320                                           uStatus, uFlags, (uint32_t)cbWritten);
    13201321
    1321     VBoxServiceVerbose(3, "ControlExec: VBoxServiceControlExecHandleCmdSetInput returned with %Rrc\n", rc);
     1322    if (RT_FAILURE(rc))
     1323        VBoxServiceError("ControlExec: Failed to report input status! Error: %Rrc\n", rc);
    13221324    return rc;
    13231325}
     
    13511353            if (RT_SUCCESS(rc))
    13521354            {
     1355                VBoxServiceVerbose(3, "ControlExec: Got output (PID %u), read=%u, handle=%u, flags=%u\n",
     1356                                   uPID, cbRead, uHandleID, uFlags);
     1357
    13531358                /* Note: Since the context ID is unique the request *has* to be completed here,
    13541359                 *       regardless whether we got data or not! Otherwise the progress object
     
    13581363                                                pBuf, cbRead);
    13591364            }
     1365            else
     1366                VBoxServiceError("ControlExec: Failed to retrieve output (PID %u), rc=%Rrc\n",
     1367                                 uPID, rc);
    13601368            RTMemFree(pBuf);
    13611369        }
     
    13651373    else
    13661374        VBoxServiceError("ControlExec: Failed to retrieve exec output command! Error: %Rrc\n", rc);
    1367     VBoxServiceVerbose(3, "ControlExec: VBoxServiceControlExecHandleCmdGetOutput returned with %Rrc\n", rc);
    1368     return rc;
    1369 }
    1370 
     1375    return rc;
     1376}
     1377
  • trunk/src/VBox/Additions/common/VBoxService/VBoxServiceControlExecThread.cpp

    r36887 r38085  
    273273                                          uint8_t *pBuf, uint32_t cbSize, uint32_t *pcbRead)
    274274{
    275     AssertPtrReturn(pBuf, VERR_INVALID_PARAMETER);
     275    AssertPtrReturn(pBuf, VERR_INVALID_POINTER);
     276    AssertReturn(cbSize, VERR_INVALID_PARAMETER);
    276277
    277278    int rc = RTCritSectEnter(&g_GuestControlExecThreadsCritSect);
  • trunk/src/VBox/Additions/common/VBoxService/VBoxServicePipeBuf.cpp

    r36880 r38085  
    112112                AssertRC(rc);
    113113            }
     114
     115#ifdef DEBUG_andy
     116            VBoxServiceVerbose(4, "PipeBuf[0x%p]: read=%u, size=%u, alloc=%u, off=%u\n",
     117                                   pBuf, *pcbToRead, pBuf->cbSize, pBuf->cbAllocated, pBuf->cbOffset);
     118#endif
    114119        }
    115120        else
     
    269274                    AssertRC(rc);
    270275                }
     276
     277#ifdef DEBUG_andy
     278                VBoxServiceVerbose(4, "PipeBuf[0x%p]: written=%u, size=%u, alloc=%u, off=%u\n",
     279                                   pBuf, cbData, pBuf->cbSize, pBuf->cbAllocated, pBuf->cbOffset);
     280#endif
    271281            }
    272282        }
  • trunk/src/VBox/Frontends/VBoxManage/VBoxManageGuestCtrl.cpp

    r37761 r38085  
    6969static volatile bool    g_fGuestCtrlCanceled = false;
    7070
     71typedef struct COPYCONTEXT
     72{
     73    IGuest *pGuest;
     74    bool fVerbose;
     75    bool fHostToGuest;
     76    char *pszUsername;
     77    char *pszPassword;
     78} COPYCONTEXT, *PCOPYCONTEXT;
     79
    7180/**
    7281 * An entry for a source element, including an optional filter.
     
    100109
    101110/**
    102  * An entry for an element which needs to be copied to the guest.
     111 * An entry for an element which needs to be copied/created to/on the guest.
    103112 */
    104113typedef struct DESTFILEENTRY
     
    187196                 "                            [--timeout <msec>] [--unix2dos] [--verbose]\n"
    188197                 "                            [--wait-exit] [--wait-stdout] [--wait-stderr]\n"
    189                  //"                          [--output-format=<dos>|<unix>]\n"
    190                  "                            [--output-type=<binary>|<text>]\n"
    191198                 "                            [-- [<argument1>] ... [<argumentN>]]\n"
    192199                 /** @todo Add a "--" parameter (has to be last parameter) to directly execute
    193200                  *        stuff, e.g. "VBoxManage guestcontrol execute <VMName> --username <> ... -- /bin/rm -Rf /foo". */
    194201                 "\n"
    195 #if 0
    196202                 "                            copyfrom\n"
    197203                 "                            <source on guest> <destination on host>\n"
     
    199205                 "                            [--dryrun] [--follow] [--recursive] [--verbose]\n"
    200206                 "\n"
    201 #endif
    202207                 "                            copyto|cp\n"
    203208                 "                            <source on host> <destination on guest>\n"
     
    206211                 "\n"
    207212                 "                            createdir[ectory]|mkdir|md\n"
    208                  "                            <directory to create on guest>\n"
     213                 "                            <director[y|ies] to create on guest>\n"
    209214                 "                            --username <name> --password <password>\n"
    210215                 "                            [--parents] [--mode <mode>] [--verbose]\n"
     216                 "\n"
     217                 "                            stat\n"
     218                 "                            <file element(s) to check on guest>\n"
     219                 "                            --username <name> --password <password>\n"
     220                 "                            [--verbose]\n"
    211221                 "\n"
    212222                 "                            updateadditions\n"
     
    572582    if (Utf8UserName.isEmpty())
    573583        return errorSyntax(USAGE_GUESTCONTROL, "No user name specified!");
     584
     585    /* Any output conversion not supported yet! */
     586    if (eOutputType != OUTPUTTYPE_UNDEFINED)
     587        return errorSyntax(USAGE_GUESTCONTROL, "Output conversion not implemented yet!");
    574588
    575589    /*
     
    685699                        *        generic problem and the new VFS APIs will handle it more
    686700                        *        transparently. (requires writing dos2unix/unix2dos filters ofc) */
    687                         if (eOutputType != OUTPUTTYPE_UNDEFINED)
     701
     702                        /*
     703                         * If aOutputData is text data from the guest process' stdout or stderr,
     704                         * it has a platform dependent line ending. So standardize on
     705                         * Unix style, as RTStrmWrite does the LF -> CR/LF replacement on
     706                         * Windows. Otherwise we end up with CR/CR/LF on Windows.
     707                         */
     708                        ULONG cbOutputDataPrint = cbOutputData;
     709                        for (BYTE *s = aOutputData.raw(), *d = s;
     710                             s - aOutputData.raw() < (ssize_t)cbOutputData;
     711                             s++, d++)
    688712                        {
    689                             /*
    690                              * If aOutputData is text data from the guest process' stdout or stderr,
    691                              * it has a platform dependent line ending. So standardize on
    692                              * Unix style, as RTStrmWrite does the LF -> CR/LF replacement on
    693                              * Windows. Otherwise we end up with CR/CR/LF on Windows.
    694                              */
    695                             ULONG cbOutputDataPrint = cbOutputData;
    696                             for (BYTE *s = aOutputData.raw(), *d = s;
    697                                  s - aOutputData.raw() < (ssize_t)cbOutputData;
    698                                  s++, d++)
     713                            if (*s == '\r')
    699714                            {
    700                                 if (*s == '\r')
    701                                 {
    702                                     /* skip over CR, adjust destination */
    703                                     d--;
    704                                     cbOutputDataPrint--;
    705                                 }
    706                                 else if (s != d)
    707                                     *d = *s;
     715                                /* skip over CR, adjust destination */
     716                                d--;
     717                                cbOutputDataPrint--;
    708718                            }
    709                             RTStrmWrite(g_pStdOut, aOutputData.raw(), cbOutputDataPrint);
     719                            else if (s != d)
     720                                *d = *s;
    710721                        }
    711                         else /* Just dump all data as we got it ... */
    712                             RTStrmWrite(g_pStdOut, aOutputData.raw(), cbOutputData);
     722                        RTStrmWrite(g_pStdOut, aOutputData.raw(), cbOutputDataPrint);
    713723                    }
    714724                }
     
    790800}
    791801
    792 /** @todo Clean up too long parameter list -> move guest specific stuff into own struct etc! */
    793 static int ctrlCopyDirectoryReadGuest(IGuest *pGuest,
    794                                       const char *pszUsername, const char *pszPassword,
    795                                       const char *pszRootDir, const char *pszSubDir,
    796                                       const char *pszFilter, const char *pszDest,
    797                                       uint32_t fFlags, uint32_t *pcObjects, DESTDIRMAP &dirMap)
    798 {
    799     AssertPtrReturn(pszRootDir, VERR_INVALID_POINTER);
     802static int ctrlCopyContextCreate(IGuest *pGuest, bool fVerbose, bool fHostToGuest,
     803                                 const char *pszUsername, const char *pszPassword,
     804                                 PCOPYCONTEXT *ppContext)
     805{
     806    AssertPtrReturn(pGuest, VERR_INVALID_POINTER);
     807    AssertPtrReturn(pszUsername, VERR_INVALID_POINTER);
     808    AssertPtrReturn(pszPassword, VERR_INVALID_POINTER);
     809
     810    PCOPYCONTEXT pContext = (PCOPYCONTEXT)RTMemAlloc(sizeof(COPYCONTEXT));
     811    AssertPtrReturn(pContext, VERR_NO_MEMORY);
     812    pContext->pGuest = pGuest;
     813    pContext->fVerbose = fVerbose;
     814    pContext->fHostToGuest = fHostToGuest;
     815
     816    pContext->pszUsername = RTStrDup(pszUsername);
     817    if (!pContext->pszUsername)
     818    {
     819        RTMemFree(pContext);
     820        return VERR_NO_MEMORY;
     821    }
     822
     823    pContext->pszPassword = RTStrDup(pszPassword);
     824    if (!pContext->pszPassword)
     825    {
     826        RTStrFree(pContext->pszUsername);
     827        RTMemFree(pContext);
     828        return VERR_NO_MEMORY;
     829    }
     830
     831    *ppContext = pContext;
     832
     833    return VINF_SUCCESS;
     834}
     835
     836static void ctrlCopyContextFree(PCOPYCONTEXT pContext)
     837{
     838    if (pContext)
     839    {
     840        RTStrFree(pContext->pszUsername);
     841        RTStrFree(pContext->pszPassword);
     842        RTMemFree(pContext);
     843    }
     844}
     845
     846/*
     847 * Source          Dest                 Translated
     848 * c:\foo.txt      c:\from_host         c:\from_host\foo.txt
     849 * c:\asdf\foo     c:\qwer              c:\qwer\foo
     850 * c:\bar\baz.txt  d:\users\            d:\users\baz.txt
     851 * c:\*.dll        e:\baz               e:\baz
     852 */
     853static int ctrlCopyTranslatePath(PCOPYCONTEXT pContext,
     854                                 const char *pszSourceRoot, const char *pszSource,
     855                                 const char *pszDest, char **ppszTranslated)
     856{
     857    AssertPtrReturn(pContext, VERR_INVALID_POINTER);
     858    AssertPtrReturn(pszSourceRoot, VERR_INVALID_POINTER);
     859    AssertPtrReturn(pszSource, VERR_INVALID_POINTER);
     860    AssertPtrReturn(pszDest, VERR_INVALID_POINTER);
     861    AssertPtrReturn(ppszTranslated, VERR_INVALID_POINTER);
     862
     863    /* Source path must contain the source root! */
     864    if (!RTPathStartsWith(pszSource, pszSourceRoot))
     865        return VERR_INVALID_PARAMETER;
     866
     867    /* Construct the relative dest destination path by "subtracting" the
     868     * source from the source root, e.g.
     869     *
     870     * source root path = "e:\foo\", source = "e:\foo\bar"
     871     * dest = "d:\baz\"
     872     * translated = "d:\baz\bar\"
     873     */
     874
     875    size_t lenRoot = strlen(pszSourceRoot);
     876    AssertReturn(lenRoot, VERR_INVALID_PARAMETER);
     877    char *pszTranslated = RTStrDup(pszDest);
     878    AssertReturn(pszTranslated, VERR_NO_MEMORY);
     879    int vrc = RTStrAAppend(&pszTranslated, &pszSource[lenRoot]);
     880    if (RT_FAILURE(vrc))
     881        return vrc;
     882
     883    *ppszTranslated = pszTranslated;
     884
     885#ifdef DEBUG
     886    if (pContext->fVerbose)
     887        RTPrintf("Translating root=%s, source=%s, dest=%s -> %s\n",
     888                 pszSourceRoot, pszSource, pszDest, *ppszTranslated);
     889#endif
     890
     891    return vrc;
     892}
     893
     894static int ctrlCopyDirCreate(PCOPYCONTEXT pContext, const char *pszDir)
     895{
     896    AssertPtrReturn(pContext, VERR_INVALID_POINTER);
     897    AssertPtrReturn(pszDir, VERR_INVALID_POINTER);
     898
     899    int rc = VINF_SUCCESS;
     900    if (pContext->fHostToGuest) /* We want to create directories on the guest. */
     901    {
     902        HRESULT hrc = pContext->pGuest->DirectoryCreate(Bstr(pszDir).raw(),
     903                                                        Bstr(pContext->pszUsername).raw(), Bstr(pContext->pszPassword).raw(),
     904                                                        700, DirectoryCreateFlag_Parents);
     905        if (FAILED(hrc))
     906            rc = ctrlPrintError(pContext->pGuest, COM_IIDOF(IGuest));
     907    }
     908    else /* ... or on the host. */
     909    {
     910        rc = RTDirCreate(pszDir, 700);
     911        if (rc == VERR_ALREADY_EXISTS)
     912            rc = VINF_SUCCESS;
     913    }
     914    return rc;
     915}
     916
     917static int ctrlCopyDirExists(PCOPYCONTEXT pContext, bool bGuest,
     918                             const char *pszDir, bool *fExists)
     919{
     920    AssertPtrReturn(pContext, false);
     921    AssertPtrReturn(pszDir, false);
     922    AssertPtrReturn(fExists, false);
     923
     924    int rc = VINF_SUCCESS;
     925    if (bGuest)
     926    {
     927        BOOL fDirExists = FALSE;
     928        HRESULT hr = pContext->pGuest->FileExists(Bstr(pszDir).raw(),
     929                                                  Bstr(pContext->pszUsername).raw(),
     930                                                  Bstr(pContext->pszPassword).raw(), &fDirExists);
     931        if (FAILED(hr))
     932            rc = ctrlPrintError(pContext->pGuest, COM_IIDOF(IGuest));
     933        else
     934            *fExists = fDirExists ? true : false;
     935    }
     936    else
     937        *fExists = RTDirExists(pszDir);
     938    return rc;
     939}
     940
     941static int ctrlCopyDirExistsOnDest(PCOPYCONTEXT pContext, const char *pszDir,
     942                                   bool *fExists)
     943{
     944    return ctrlCopyDirExists(pContext, pContext->fHostToGuest,
     945                             pszDir, fExists);
     946}
     947
     948static int ctrlCopyDirExistsOnSource(PCOPYCONTEXT pContext, const char *pszDir,
     949                                     bool *fExists)
     950{
     951    return ctrlCopyDirExists(pContext, !pContext->fHostToGuest,
     952                             pszDir, fExists);
     953}
     954
     955static int ctrlCopyFileExists(PCOPYCONTEXT pContext, bool bOnGuest,
     956                              const char *pszFile, bool *fExists)
     957{
     958    AssertPtrReturn(pContext, false);
     959    AssertPtrReturn(pszFile, false);
     960    AssertPtrReturn(fExists, false);
     961
     962    int rc = VINF_SUCCESS;
     963    if (bOnGuest)
     964    {
     965        BOOL fFileExists = FALSE;
     966        HRESULT hr = pContext->pGuest->FileExists(Bstr(pszFile).raw(),
     967                                                  Bstr(pContext->pszUsername).raw(),
     968                                                  Bstr(pContext->pszPassword).raw(), &fFileExists);
     969        if (FAILED(hr))
     970            rc = ctrlPrintError(pContext->pGuest, COM_IIDOF(IGuest));
     971        else
     972            *fExists = fFileExists ? true : false;
     973    }
     974    else
     975        *fExists = RTFileExists(pszFile);
     976    return rc;
     977}
     978
     979static int ctrlCopyFileExistsOnDest(PCOPYCONTEXT pContext, const char *pszFile,
     980                                    bool *fExists)
     981{
     982    return ctrlCopyFileExists(pContext, pContext->fHostToGuest,
     983                              pszFile, fExists);
     984}
     985
     986static int ctrlCopyFileExistsOnSource(PCOPYCONTEXT pContext, const char *pszFile,
     987                                      bool *fExists)
     988{
     989    return ctrlCopyFileExists(pContext, !pContext->fHostToGuest,
     990                              pszFile, fExists);
     991}
     992
     993static int ctrlCopyFileToTarget(PCOPYCONTEXT pContext, const char *pszFileSource,
     994                                const char *pszFileDest, uint32_t fFlags)
     995{
     996    AssertPtrReturn(pContext, VERR_INVALID_POINTER);
     997    AssertPtrReturn(pszFileSource, VERR_INVALID_POINTER);
     998    AssertPtrReturn(pszFileDest, VERR_INVALID_POINTER);
     999
     1000    if (pContext->fVerbose)
     1001    {
     1002        RTPrintf("Copying \"%s\" to \"%s\" ...\n",
     1003                 pszFileSource, pszFileDest);
     1004    }
     1005
     1006    int vrc = VINF_SUCCESS;
     1007    ComPtr<IProgress> progress;
     1008    HRESULT hr;
     1009    if (pContext->fHostToGuest)
     1010    {
     1011        hr = pContext->pGuest->CopyToGuest(Bstr(pszFileSource).raw(), Bstr(pszFileDest).raw(),
     1012                                           Bstr(pContext->pszUsername).raw(), Bstr(pContext->pszPassword).raw(),
     1013                                           fFlags, progress.asOutParam());
     1014    }
     1015    else
     1016    {
     1017        hr = pContext->pGuest->CopyFromGuest(Bstr(pszFileSource).raw(), Bstr(pszFileDest).raw(),
     1018                                             Bstr(pContext->pszUsername).raw(), Bstr(pContext->pszPassword).raw(),
     1019                                             fFlags, progress.asOutParam());
     1020    }
     1021
     1022    if (FAILED(hr))
     1023        vrc = ctrlPrintError(pContext->pGuest, COM_IIDOF(IGuest));
     1024    else
     1025    {
     1026        hr = showProgress(progress);
     1027        if (FAILED(hr))
     1028            vrc = ctrlPrintProgressError(progress);
     1029    }
     1030
     1031    return vrc;
     1032}
     1033
     1034static int ctrlCopyDirToGuest(PCOPYCONTEXT pContext,
     1035                              const char *pszSource, const char *pszFilter,
     1036                              const char *pszDest, uint32_t fFlags,
     1037                              const char *pszSubDir /* For recursion */)
     1038{
     1039    AssertPtrReturn(pContext, VERR_INVALID_POINTER);
     1040    AssertPtrReturn(pszSource, VERR_INVALID_POINTER);
     1041    /* Filter is optional. */
     1042    AssertPtrReturn(pszDest, VERR_INVALID_POINTER);
    8001043    /* Sub directory is optional. */
    801     /* Filter directory is optional. */
    802     AssertPtrReturn(pszDest, VERR_INVALID_POINTER);
    803     AssertPtrReturn(pcObjects, VERR_INVALID_POINTER);
    8041044
    8051045    /*
     
    8071047     */
    8081048    char szCurDir[RTPATH_MAX];
    809     int rc = RTStrCopy(szCurDir, sizeof(szCurDir), pszRootDir);
    810     if (RT_SUCCESS(rc) && pszSubDir != NULL)
     1049    int rc = RTStrCopy(szCurDir, sizeof(szCurDir), pszSource);
     1050    if (RT_SUCCESS(rc) && pszSubDir)
    8111051        rc = RTPathAppend(szCurDir, sizeof(szCurDir), pszSubDir);
    8121052
    813     if (RT_SUCCESS(rc))
    814     {
    815         ULONG uDirHandle;
    816         HRESULT hr = pGuest->DirectoryOpen(Bstr(szCurDir).raw(), Bstr(pszFilter).raw(), fFlags,
    817                                            Bstr(pszUsername).raw(), Bstr(pszPassword).raw(), &uDirHandle);
    818         if (FAILED(hr))
    819             rc = ctrlPrintError(pGuest, COM_IIDOF(IGuest));
    820         else
    821         {
    822             ComPtr <IGuestDirEntry> dirEntry;
    823             while (SUCCEEDED(hr = pGuest->DirectoryRead(uDirHandle, dirEntry.asOutParam())))
    824             {
    825                 GuestDirEntryType_T enmType;
    826                 dirEntry->COMGETTER(Type)(&enmType);
    827 
    828                 Bstr strName;
    829                 dirEntry->COMGETTER(Name)(strName.asOutParam());
    830 
    831                 switch (enmType)
    832                 {
    833                     case GuestDirEntryType_Directory:
    834                     {
    835                         /* Skip "." and ".." entries. */
    836                         if (   !strName.compare(Bstr("."))
    837                             || !strName.compare(Bstr("..")))
    838                             break;
    839 
    840                         const char *pszName = Utf8Str(strName).c_str();
    841                         if (fFlags & CopyFileFlag_Recursive)
    842                         {
    843                             char *pszNewSub = NULL;
    844                             if (pszSubDir)
    845                                 RTStrAPrintf(&pszNewSub, "%s/%s", pszSubDir, pszName);
    846                             else
    847                                 RTStrAPrintf(&pszNewSub, "%s", pszName);
    848 
    849                             if (pszNewSub)
    850                             {
    851                                 dirMap[pszNewSub];
    852 
    853                                 rc = ctrlCopyDirectoryReadGuest(pGuest, pszUsername, pszPassword,
    854                                                                 pszRootDir, pszNewSub,
    855                                                                 pszFilter, pszDest,
    856                                                                 fFlags, pcObjects, dirMap);
    857                                 RTStrFree(pszNewSub);
    858                             }
    859                             else
    860                                 rc = VERR_NO_MEMORY;
    861                         }
    862                         break;
    863                     }
    864 
    865                     case GuestDirEntryType_Symlink:
    866                         if (   (fFlags & CopyFileFlag_Recursive)
    867                             && (fFlags & CopyFileFlag_FollowLinks))
    868                         {
    869                             /* Fall through to next case is intentional. */
    870                         }
    871                         else
    872                             break;
    873 
    874                     case GuestDirEntryType_File:
    875                     {
    876                         const char *pszName = Utf8Str(strName).c_str();
    877                         if (   !pszFilter
    878                             || RTStrSimplePatternMatch(pszFilter, pszName))
    879                         {
    880                             dirMap[pszSubDir].push_back(DESTFILEENTRY(pszName));
    881                             *pcObjects += 1;
    882                         }
    883                         break;
    884                     }
    885 
    886                     default:
    887                         break;
    888                 }
    889             }
    890 
    891             hr = pGuest->DirectoryClose(uDirHandle);
    892             if (FAILED(rc))
    893                 rc = ctrlPrintError(pGuest, COM_IIDOF(IGuest));
    894         }
    895     }
    896     return rc;
    897 }
    898 
    899 /**
    900  * Reads a specified directory (recursively) based on the copy flags
    901  * and appends all matching entries to the supplied list.
    902  *
    903  * @return  IPRT status code.
    904  * @param   pszRootDir          Directory to start with. Must end with
    905  *                              a trailing slash and must be absolute.
    906  * @param   pszSubDir           Sub directory part relative to the root
    907  *                              directory; needed for recursion.
    908  * @param   pszFilter           Search filter (e.g. *.pdf).
    909  * @param   pszDest             Destination directory.
    910  * @param   fFlags              Copy flags.
    911  * @param   pcObjects           Where to store the overall objects to
    912  *                              copy found.
    913  * @param   dirMap              Reference to destination directory map to store found
    914  *                              directories (primary key) + files (secondary key, vector).
    915  */
    916 static int ctrlCopyDirectoryReadHost(const char *pszRootDir, const char *pszSubDir,
    917                                      const char *pszFilter, const char *pszDest,
    918                                      uint32_t fFlags, uint32_t *pcObjects, DESTDIRMAP &dirMap)
    919 {
    920     AssertPtrReturn(pszRootDir, VERR_INVALID_POINTER);
    921     /* Sub directory is optional. */
    922     /* Filter directory is optional. */
    923     AssertPtrReturn(pszDest, VERR_INVALID_POINTER);
    924     AssertPtrReturn(pcObjects, VERR_INVALID_POINTER);
    925 
    926     /*
    927      * Construct current path.
    928      */
    929     char szCurDir[RTPATH_MAX];
    930     int rc = RTStrCopy(szCurDir, sizeof(szCurDir), pszRootDir);
    931     if (RT_SUCCESS(rc) && pszSubDir != NULL)
    932         rc = RTPathAppend(szCurDir, sizeof(szCurDir), pszSubDir);
     1053    /* Flag indicating whether the current directory was created on the
     1054     * target or not. */
     1055    bool fDirCreated = false;
    9331056
    9341057    /*
     
    9771100                        if (pszNewSub)
    9781101                        {
    979                             dirMap[pszNewSub];
    980 
    981                             rc = ctrlCopyDirectoryReadHost(pszRootDir, pszNewSub,
    982                                                            pszFilter, pszDest,
    983                                                            fFlags, pcObjects, dirMap);
     1102                            rc = ctrlCopyDirToGuest(pContext,
     1103                                                    pszSource, pszFilter,
     1104                                                    pszDest, fFlags, pszNewSub);
    9841105                            RTStrFree(pszNewSub);
    9851106                        }
     
    10041125                        || RTStrSimplePatternMatch(pszFilter, DirEntry.szName))
    10051126                    {
    1006                         dirMap[pszSubDir].push_back(DESTFILEENTRY(Utf8Str(DirEntry.szName)));
    1007                         *pcObjects += 1;
     1127                        if (!fDirCreated)
     1128                        {
     1129                            char *pszDestDir;
     1130                            rc = ctrlCopyTranslatePath(pContext, pszSource, szCurDir,
     1131                                                       pszDest, &pszDestDir);
     1132                            if (RT_SUCCESS(rc))
     1133                            {
     1134                                rc = ctrlCopyDirCreate(pContext, pszDestDir);
     1135                                RTStrFree(pszDestDir);
     1136
     1137                                fDirCreated = true;
     1138                            }
     1139                        }
     1140
     1141                        if (RT_SUCCESS(rc))
     1142                        {
     1143                            char *pszFileSource;
     1144                            if (RTStrAPrintf(&pszFileSource, "%s/%s",
     1145                                             szCurDir, DirEntry.szName))
     1146                            {
     1147                                char *pszFileDest;
     1148                                rc = ctrlCopyTranslatePath(pContext, pszSource, pszFileSource,
     1149                                                           pszDest, &pszFileDest);
     1150                                if (RT_SUCCESS(rc))
     1151                                {
     1152                                    rc = ctrlCopyFileToTarget(pContext, pszFileSource,
     1153                                                              pszFileDest, 0 /* Flags? */);
     1154                                    RTStrFree(pszFileDest);
     1155                                }
     1156                                RTStrFree(pszFileSource);
     1157                            }
     1158                        }
    10081159                    }
    10091160                    break;
     
    10221173}
    10231174
    1024 /**
    1025  * Constructs a destinations map from a source entry and a destination root.
    1026  *
    1027  * @return  IPRT status code.
    1028  * @param   fHostToGuest
    1029  * @param   sourceEntry             Reference to a specified source entry to use.
    1030  * @param   fFlags                  Copy file flags. Needed for recursive directory parsing.
    1031  * @param   pszDestRoot             Pointer to destination root. This can be used to add one or
    1032  *                                  more directories to the actual destination path.
    1033  * @param   mapDest                 Reference to the destination map for storing the actual result.
    1034  * @param   pcObjects               Pointer to a total object (file) count to copy.
    1035  */
    1036 static int ctrlCopyConstructDestinationsForGuest(SOURCEFILEENTRY &sourceEntry, uint32_t fFlags,
    1037                                                  const char *pszDestRoot, DESTDIRMAP &mapDest,
    1038                                                  uint32_t *pcObjects)
    1039 {
    1040     int rc = VINF_SUCCESS;
    1041     const char *pszSource = sourceEntry.mSource.c_str();
    1042 
    1043     if (   RTPathFilename(pszSource)
    1044         && RTFileExists(pszSource))
    1045     {
    1046         /* Source is a single file. */
    1047         char *pszFileName = RTPathFilename(pszSource);
    1048         mapDest[Utf8Str("")].push_back(DESTFILEENTRY(pszFileName));
    1049 
    1050         *pcObjects += 1;
    1051     }
     1175static int ctrlCopyDirToHost(PCOPYCONTEXT pContext,
     1176                             const char *pszSource, const char *pszFilter,
     1177                             const char *pszDest, uint32_t fFlags,
     1178                             const char *pszSubDir /* For recursion */)
     1179{
     1180    AssertPtrReturn(pContext, VERR_INVALID_POINTER);
     1181    AssertPtrReturn(pszSource, VERR_INVALID_POINTER);
     1182    /* Filter is optional. */
     1183    AssertPtrReturn(pszDest, VERR_INVALID_POINTER);
     1184    /* Sub directory is optional. */
     1185
     1186    /*
     1187     * Construct current path.
     1188     */
     1189    char szCurDir[RTPATH_MAX];
     1190    int rc = RTStrCopy(szCurDir, sizeof(szCurDir), pszSource);
     1191    if (RT_SUCCESS(rc) && pszSubDir)
     1192        rc = RTPathAppend(szCurDir, sizeof(szCurDir), pszSubDir);
     1193
     1194    /* Flag indicating whether the current directory was created on the
     1195     * target or not. */
     1196    bool fDirCreated = false;
     1197
     1198    ULONG uDirHandle;
     1199    HRESULT hr = pContext->pGuest->DirectoryOpen(Bstr(szCurDir).raw(), Bstr(pszFilter).raw(),
     1200                                                 fFlags,
     1201                                                 Bstr(pContext->pszUsername).raw(), Bstr(pContext->pszPassword).raw(),
     1202                                                 &uDirHandle);
     1203    if (FAILED(hr))
     1204        rc = ctrlPrintError(pContext->pGuest, COM_IIDOF(IGuest));
    10521205    else
    10531206    {
    1054         /* Source is either a directory or a filter (e.g. *.dll). */
    1055         rc = ctrlCopyDirectoryReadHost(pszSource,
    1056                                        NULL /* pszSubDir */,
    1057                                        sourceEntry.mFilter.isEmpty() ? NULL : sourceEntry.mFilter.c_str(),
    1058                                        pszDestRoot, fFlags, pcObjects, mapDest);
    1059     }
     1207        ComPtr <IGuestDirEntry> dirEntry;
     1208        while (SUCCEEDED(hr = pContext->pGuest->DirectoryRead(uDirHandle, dirEntry.asOutParam())))
     1209        {
     1210            GuestDirEntryType_T enmType;
     1211            dirEntry->COMGETTER(Type)(&enmType);
     1212
     1213            Bstr strName;
     1214            dirEntry->COMGETTER(Name)(strName.asOutParam());
     1215
     1216            switch (enmType)
     1217            {
     1218                case GuestDirEntryType_Directory:
     1219                {
     1220                    /* Skip "." and ".." entries. */
     1221                    if (   !strName.compare(Bstr("."))
     1222                        || !strName.compare(Bstr("..")))
     1223                        break;
     1224
     1225                    if (fFlags & CopyFileFlag_Recursive)
     1226                    {
     1227                        Utf8Str strDir(strName);
     1228                        char *pszNewSub = NULL;
     1229                        if (pszSubDir)
     1230                            RTStrAPrintf(&pszNewSub, "%s/%s", pszSubDir, strDir.c_str());
     1231                        else
     1232                            RTStrAPrintf(&pszNewSub, "%s", strDir.c_str());
     1233
     1234                        if (pszNewSub)
     1235                        {
     1236                            rc = ctrlCopyDirToHost(pContext,
     1237                                                   pszSource, pszFilter,
     1238                                                   pszDest, fFlags, pszNewSub);
     1239                            RTStrFree(pszNewSub);
     1240                        }
     1241                        else
     1242                            rc = VERR_NO_MEMORY;
     1243                    }
     1244                    break;
     1245                }
     1246
     1247                case GuestDirEntryType_Symlink:
     1248                    if (   (fFlags & CopyFileFlag_Recursive)
     1249                        && (fFlags & CopyFileFlag_FollowLinks))
     1250                    {
     1251                        /* Fall through to next case is intentional. */
     1252                    }
     1253                    else
     1254                        break;
     1255
     1256                case GuestDirEntryType_File:
     1257                {
     1258                    const char *pszName = Utf8Str(strName).c_str();
     1259                    if (   !pszFilter
     1260                        || RTStrSimplePatternMatch(pszFilter, pszName))
     1261                    {
     1262                        if (!fDirCreated)
     1263                        {
     1264                            char *pszDestDir;
     1265                            rc = ctrlCopyTranslatePath(pContext, pszSource, szCurDir,
     1266                                                       pszDest, &pszDestDir);
     1267                            if (RT_SUCCESS(rc))
     1268                            {
     1269                                rc = ctrlCopyDirCreate(pContext, pszDestDir);
     1270                                RTStrFree(pszDestDir);
     1271
     1272                                fDirCreated = true;
     1273                            }
     1274                        }
     1275
     1276                        if (RT_SUCCESS(rc))
     1277                        {
     1278                            Utf8Str strDir(strName);
     1279                            char *pszFileSource;
     1280                            if (RTStrAPrintf(&pszFileSource, "%s/%s",
     1281                                             szCurDir, strDir.c_str()))
     1282                            {
     1283                                char *pszFileDest;
     1284                                rc = ctrlCopyTranslatePath(pContext, pszSource, pszFileSource,
     1285                                                           pszDest, &pszFileDest);
     1286                                if (RT_SUCCESS(rc))
     1287                                {
     1288                                    rc = ctrlCopyFileToTarget(pContext, pszFileSource,
     1289                                                              pszFileDest, 0 /* Flags? */);
     1290                                    RTStrFree(pszFileDest);
     1291                                }
     1292                                RTStrFree(pszFileSource);
     1293                            }
     1294                        }
     1295                    }
     1296                    break;
     1297                }
     1298
     1299                default:
     1300                    break;
     1301            }
     1302        }
     1303
     1304        hr = pContext->pGuest->DirectoryClose(uDirHandle);
     1305        if (FAILED(rc))
     1306            rc = ctrlPrintError(pContext->pGuest, COM_IIDOF(IGuest));
     1307    }
     1308
    10601309    return rc;
    10611310}
    10621311
    1063 static int ctrlCopyConstructDestinationsForHost(IGuest *pGuest,
    1064                                                 const char *pszUsername, const char *pszPassword,
    1065                                                 SOURCEFILEENTRY &sourceEntry, uint32_t fFlags,
    1066                                                 const char *pszDestRoot, DESTDIRMAP &mapDest,
    1067                                                 uint32_t *pcObjects)
    1068 {
    1069     int rc = VINF_SUCCESS;
    1070     const char *pszSource = sourceEntry.mSource.c_str();
    1071 
    1072     BOOL fExists = FALSE;
    1073     HRESULT hr = pGuest->FileExists(Bstr(pszSource).raw(),
    1074                                     Bstr(pszUsername).raw(), Bstr(pszPassword).raw(), &fExists);
    1075     if (FAILED(rc))
    1076         rc = ctrlPrintError(pGuest, COM_IIDOF(IGuest));
     1312static int ctrlCopyDirToTarget(PCOPYCONTEXT pContext,
     1313                               const char *pszSource, const char *pszFilter,
     1314                               const char *pszDest, uint32_t fFlags,
     1315                               const char *pszSubDir /* For recursion */)
     1316{
     1317    if (pContext->fHostToGuest)
     1318        return ctrlCopyDirToGuest(pContext, pszSource, pszFilter,
     1319                                  pszDest, fFlags, pszSubDir);
     1320    return ctrlCopyDirToHost(pContext, pszSource, pszFilter,
     1321                             pszDest, fFlags, pszSubDir);
     1322}
     1323
     1324static int ctrlCopyCreateSourceRoot(const char *pszSource, char **ppszSourceRoot)
     1325{
     1326    AssertPtrReturn(pszSource, VERR_INVALID_POINTER);
     1327    AssertPtrReturn(ppszSourceRoot, VERR_INVALID_POINTER);
     1328
     1329    char *pszNewRoot = RTStrDup(pszSource);
     1330    AssertPtrReturn(pszNewRoot, VERR_NO_MEMORY);
     1331
     1332    size_t lenRoot = strlen(pszNewRoot);
     1333    if (   lenRoot
     1334        && pszNewRoot[lenRoot - 1] == '/'
     1335        && pszNewRoot[lenRoot - 1] == '\\'
     1336        && lenRoot > 1
     1337        && pszNewRoot[lenRoot - 2] == '/'
     1338        && pszNewRoot[lenRoot - 2] == '\\')
     1339    {
     1340        *ppszSourceRoot = pszNewRoot;
     1341        if (lenRoot > 1)
     1342            *ppszSourceRoot[lenRoot - 2] = '\0';
     1343        *ppszSourceRoot[lenRoot - 1] = '\0';
     1344    }
    10771345    else
    10781346    {
    1079         if (fExists)
    1080         {
    1081             /* Source is a single file. */
    1082             char *pszFileName = RTPathFilename(pszSource);
    1083             mapDest[Utf8Str(pszDestRoot)].push_back(DESTFILEENTRY(pszFileName));
    1084 
    1085             *pcObjects++;
    1086         }
    1087         else
    1088         {
    1089             /* Source is either a directory or a filter (e.g. *.dll). */
    1090             rc = ctrlCopyDirectoryReadGuest(pGuest, pszUsername, pszPassword,
    1091                                             pszSource, NULL /* pszSubDir */,
    1092                                             sourceEntry.mFilter.isEmpty() ? NULL : sourceEntry.mFilter.c_str(),
    1093                                             pszDestRoot, fFlags, pcObjects, mapDest);
    1094         }
    1095     }
    1096     return rc;
    1097 }
    1098 
    1099 /**
    1100  * Prepares the destination directory hirarchy on the guest side by creating the directories
    1101  * and sets the appropriate access rights.
    1102  *
    1103  * @return  IPRT status code.
    1104  * @param   pGuest                  IGuest interface pointer.
    1105  * @param   fHostToGuest
    1106  * @param   itDest                  Destination map iterator to process.
    1107  * @param   pszDestRoot             Destination root to use.
    1108  * @param   pszUsername             Username to use.
    1109  * @param   pszPassword             Password to use.
    1110  */
    1111 static int ctrlCopyPrepareDestDirectory(IGuest *pGuest, bool fHostToGuest,
    1112                                         const char *pszDestRoot, const char *pszDestSub,
    1113                                         const char *pszUsername, const char *pszPassword)
    1114 {
    1115     AssertPtrReturn(pGuest, VERR_INVALID_POINTER);
    1116     AssertPtrReturn(pszDestRoot, VERR_INVALID_POINTER);
    1117     AssertPtrReturn(pszDestSub, VERR_INVALID_POINTER);
    1118     AssertPtrReturn(pszUsername, VERR_INVALID_POINTER);
    1119     AssertPtrReturn(pszPassword, VERR_INVALID_POINTER);
    1120 
    1121     char *pszDestFinal = NULL;
    1122     int rc = VINF_SUCCESS;
    1123 
    1124     /* Create root directory (= empty name) and skip the rest for
    1125      * this round. */
    1126     if (!strlen(pszDestSub))
    1127     {
    1128         pszDestFinal = RTStrDup(pszDestRoot);
    1129         if (!pszDestFinal)
    1130             rc = VERR_NO_MEMORY;
    1131     }
    1132     else /* Create sub-directories, also empty ones. */
    1133     {
    1134         if (!RTStrAPrintf(&pszDestFinal, "%s/%s", pszDestRoot, pszDestSub))
    1135             rc = VERR_NO_MEMORY;
    1136     }
    1137 
    1138     if (RT_SUCCESS(rc) && pszDestFinal)
    1139     {
    1140         if (fHostToGuest) /* We want to create directories on the guest. */
    1141         {
    1142             HRESULT hrc = pGuest->DirectoryCreate(Bstr(pszDestFinal).raw(),
    1143                                                   Bstr(pszUsername).raw(), Bstr(pszPassword).raw(),
    1144                                                   700, DirectoryCreateFlag_Parents);
    1145             if (FAILED(hrc))
    1146                 rc = ctrlPrintError(pGuest, COM_IIDOF(IGuest));
    1147         }
    1148         else /* ... or on the host. */
    1149         {
    1150             rc = RTDirCreate(pszDestFinal, 700);
    1151         }
    1152         RTStrFree(pszDestFinal);
    1153     }
    1154     return rc;
    1155 }
    1156 
    1157 /**
    1158  * Copys a file from host to the guest.
    1159  *
    1160  * @return  IPRT status code.
    1161  * @param   pGuest                  IGuest interface pointer.
    1162  * @param   pszSource               Source path of existing host file to copy to the guest.
    1163  * @param   pszDest                 Destination path on guest to copy the file to.
    1164  * @param   pszUserName             User name on guest to use for the copy operation.
    1165  * @param   pszPassword             Password of user account.
    1166  * @param   fFlags                  Copy flags.
    1167  */
    1168 static int ctrlCopyFileToGuest(IGuest *pGuest, const char *pszSource, const char *pszDest,
    1169                                const char *pszUserName, const char *pszPassword,
    1170                                uint32_t fFlags)
    1171 {
    1172     AssertPtrReturn(pGuest, VERR_INVALID_POINTER);
    1173     AssertPtrReturn(pszSource, VERR_INVALID_POINTER);
    1174     AssertPtrReturn(pszDest, VERR_INVALID_POINTER);
    1175     AssertPtrReturn(pszUserName, VERR_INVALID_POINTER);
    1176     AssertPtrReturn(pszPassword, VERR_INVALID_POINTER);
    1177 
    1178     int vrc = VINF_SUCCESS;
    1179     ComPtr<IProgress> progress;
    1180     HRESULT rc = pGuest->CopyToGuest(Bstr(pszSource).raw(), Bstr(pszDest).raw(),
    1181                                      Bstr(pszUserName).raw(), Bstr(pszPassword).raw(),
    1182                                      fFlags, progress.asOutParam());
    1183     if (FAILED(rc))
    1184         vrc = ctrlPrintError(pGuest, COM_IIDOF(IGuest));
    1185     else
    1186     {
    1187         rc = showProgress(progress);
    1188         if (FAILED(rc))
    1189             vrc = ctrlPrintProgressError(progress);
    1190     }
    1191     return vrc;
    1192 }
    1193 
    1194 /**
    1195  * Copys a file from guest to the host.
    1196  *
    1197  * @return  IPRT status code.
    1198  * @param   pGuest                  IGuest interface pointer.
    1199  * @param   pszSource               Source path of existing guest file to copy to the host.
    1200  * @param   pszDest                 Destination path/file on host to copy the file to.
    1201  * @param   pszUserName             User name on guest to use for the copy operation.
    1202  * @param   pszPassword             Password of user account.
    1203  * @param   fFlags                  Copy flags.
    1204  */
    1205 static int ctrlCopyFileToHost(IGuest *pGuest, const char *pszSource, const char *pszDest,
    1206                               const char *pszUserName, const char *pszPassword,
    1207                               uint32_t fFlags)
    1208 {
    1209     AssertPtrReturn(pGuest, VERR_INVALID_POINTER);
    1210     AssertPtrReturn(pszSource, VERR_INVALID_POINTER);
    1211     AssertPtrReturn(pszDest, VERR_INVALID_POINTER);
    1212     AssertPtrReturn(pszUserName, VERR_INVALID_POINTER);
    1213     AssertPtrReturn(pszPassword, VERR_INVALID_POINTER);
    1214 
    1215     int vrc = VINF_SUCCESS;
    1216     ComPtr<IProgress> progress;
    1217     HRESULT rc = pGuest->CopyFromGuest(Bstr(pszSource).raw(), Bstr(pszDest).raw(),
    1218                                        Bstr(pszUserName).raw(), Bstr(pszPassword).raw(),
    1219                                        fFlags, progress.asOutParam());
    1220     if (FAILED(rc))
    1221         vrc = ctrlPrintError(pGuest, COM_IIDOF(IGuest));
    1222     else
    1223     {
    1224         rc = showProgress(progress);
    1225         if (FAILED(rc))
    1226             vrc = ctrlPrintProgressError(progress);
    1227     }
    1228     return vrc;
    1229 }
    1230 
    1231 static int ctrlCopyToDestDirectory(IGuest *pGuest, bool fVerbose, bool fDryRun, bool fHostToGuest,
    1232                                    const char *pszSourceDir,
    1233                                    const char *pszDestRoot, const char *pszDestSub, const char *pszFileName,
    1234                                    uint32_t uFlags, const char *pszUsername, const char *pszPassword)
    1235 {
    1236     AssertPtrReturn(pGuest, VERR_INVALID_POINTER);
    1237     AssertPtrReturn(pszDestRoot, VERR_INVALID_POINTER);
    1238     AssertPtrReturn(pszDestSub, VERR_INVALID_POINTER);
    1239     AssertPtrReturn(pszFileName, VERR_INVALID_POINTER);
    1240     AssertPtrReturn(pszSourceDir, VERR_INVALID_POINTER);
    1241     AssertPtrReturn(pszUsername, VERR_INVALID_POINTER);
    1242     AssertPtrReturn(pszPassword, VERR_INVALID_POINTER);
    1243 
    1244     int iLen;
    1245     char *pszSource;
    1246     if (!strlen(pszDestSub))
    1247         iLen = RTStrAPrintf(&pszSource, "%s/%s", pszSourceDir, pszFileName);
    1248     else
    1249         iLen = RTStrAPrintf(&pszSource, "%s/%s/%s",
    1250                             pszSourceDir, pszDestSub, pszFileName);
    1251     if (!iLen)
    1252         return VERR_NO_MEMORY;
    1253 
    1254     char *pszDest;
    1255     if (!strlen(pszDestSub))
    1256         iLen = RTStrAPrintf(&pszDest, "%s/%s", pszDestRoot, pszFileName);
    1257     else
    1258         iLen = RTStrAPrintf(&pszDest, "%s/%s/%s", pszDestRoot, pszDestSub,
    1259                             pszFileName);
    1260     if (!iLen)
    1261     {
    1262         RTStrFree(pszSource);
    1263         return VERR_NO_MEMORY;
    1264     }
    1265 
    1266     if (fVerbose)
    1267         RTPrintf("\"%s\" -> \"%s\"\n", pszSource, pszDest);
    1268 
    1269     int rc = VINF_SUCCESS;
    1270 
    1271     /* Finally copy the desired file (if no dry run selected). */
    1272     if (!fDryRun)
    1273     {
    1274         if (fHostToGuest)
    1275             rc = ctrlCopyFileToGuest(pGuest, pszSource, pszDest,
    1276                                      pszUsername, pszPassword, uFlags);
    1277         else
    1278             rc = ctrlCopyFileToHost(pGuest, pszSource, pszDest,
    1279                                     pszUsername, pszPassword, uFlags);
    1280     }
    1281     RTStrFree(pszSource);
    1282     RTStrFree(pszDest);
    1283 
    1284     return rc;
     1347        /* If there's anything (like a file name or a filter),
     1348         * strip it! */
     1349        RTPathStripFilename(pszNewRoot);
     1350        *ppszSourceRoot = pszNewRoot;
     1351    }
     1352
     1353    return VINF_SUCCESS;
     1354}
     1355
     1356static void ctrlCopyFreeSourceRoot(char *pszSourceRoot)
     1357{
     1358    RTStrFree(pszSourceRoot);
    12851359}
    12861360
     
    14151489    }
    14161490
    1417     /* Strip traling slash from destination path. */
    1418     RTPathStripTrailingSlash(Utf8Dest.mutableRaw());
    1419     Utf8Dest.jolt();
    1420 
    1421     /*
    1422      * Here starts the actual fun!
    1423      */
    1424     for (unsigned long s = 0; s < vecSources.size(); s++)
    1425     {
    1426         char *pszSourceDir;
    1427         if (RTDirExists(vecSources[s].mSource.c_str()))
    1428             pszSourceDir = RTStrDup(vecSources[s].mSource.c_str());
    1429         else
    1430         {
    1431             pszSourceDir = RTStrDup(vecSources[s].mSource.c_str());
    1432             RTPathStripFilename(pszSourceDir);
    1433         }
    1434 
    1435         uint32_t cObjects = 0;
    1436         DESTDIRMAP mapDest;
    1437         const char *pszDestRoot = Utf8Dest.c_str();
    1438 
    1439         if (fHostToGuest)
    1440             vrc = ctrlCopyConstructDestinationsForGuest(vecSources[s], fFlags, pszDestRoot,
    1441                                                         mapDest, &cObjects);
    1442         else
    1443             vrc = ctrlCopyConstructDestinationsForHost(guest, Utf8UserName.c_str(), Utf8Password.c_str(),
    1444                                                        vecSources[s], fFlags, pszDestRoot,
    1445                                                        mapDest, &cObjects);
    1446         if (RT_FAILURE(vrc))
    1447         {
    1448             if (   fVerbose
    1449                 && vrc == VERR_FILE_NOT_FOUND)
    1450             {
    1451                 RTPrintf("Warning: Source \"%s\" does not exist, skipping!\n",
    1452                          vecSources[s].mSource.c_str());
    1453             }
    1454         }
    1455         else
    1456         {
    1457             /*
    1458              * Prepare directory structure of each destination directory.
    1459              */
    1460             DESTDIRMAPITER itDest;
    1461             for (itDest = mapDest.begin(); itDest != mapDest.end(); itDest++)
    1462             {
    1463                 if (fVerbose)
     1491    /* Create the copy context -- it contains all information
     1492     * the routines need to know when handling the actual copying. */
     1493    PCOPYCONTEXT pContext;
     1494    vrc = ctrlCopyContextCreate(guest, fVerbose, fHostToGuest,
     1495                                Utf8UserName.c_str(), Utf8Password.c_str(),
     1496                                &pContext);
     1497    if (RT_FAILURE(vrc))
     1498    {
     1499        RTMsgError("Unable to create copy context, rc=%Rrc\n", vrc);
     1500        return RTEXITCODE_FAILURE;
     1501    }
     1502
     1503    /* If the destination is a path, (try to) create it. */
     1504    const char *pszDest = Utf8Dest.c_str();
     1505    AssertPtr(pszDest);
     1506    size_t lenDest = strlen(pszDest);
     1507    if (   lenDest
     1508         ||pszDest[lenDest - 1] == '/'
     1509        || pszDest[lenDest - 1] == '\\')
     1510    {
     1511        vrc = ctrlCopyDirCreate(pContext, pszDest);
     1512    }
     1513
     1514    if (RT_SUCCESS(vrc))
     1515    {
     1516        /*
     1517         * Here starts the actual fun!
     1518         * Handle all given sources one by one.
     1519         */
     1520        for (unsigned long s = 0; s < vecSources.size(); s++)
     1521        {
     1522            const char *pszSource = vecSources[s].mSource.c_str();
     1523            const char *pszFilter = vecSources[s].mFilter.c_str();
     1524
     1525            char *pszSourceRoot;
     1526            vrc = ctrlCopyCreateSourceRoot(pszSource, &pszSourceRoot);
     1527            if (RT_FAILURE(vrc))
     1528            {
     1529                RTMsgError("Unable to create source root, rc=%Rrc\n", vrc);
     1530                break;
     1531            }
     1532
     1533            if (fVerbose)
     1534                RTPrintf("Source: %s\n", pszSource);
     1535
     1536            /* @todo Files with filter?? */
     1537            bool fExists;
     1538            vrc = ctrlCopyFileExistsOnSource(pContext, pszSource, &fExists);
     1539            if (RT_SUCCESS(vrc))
     1540            {
     1541                if (fExists)
    14641542                {
    1465                     const char *pszSubDir = itDest->first.c_str();
    1466                     AssertPtr(pszSubDir);
    1467                     if (!strlen(pszSubDir))
    1468                         RTPrintf("Preparing directory \"%s\" ...\n", pszDestRoot);
     1543                    /* Single file. */
     1544                    char *pszDest;
     1545                    vrc = ctrlCopyTranslatePath(pContext, pszSourceRoot, pszSource,
     1546                                                Utf8Dest.c_str(), &pszDest);
     1547                    if (RT_SUCCESS(vrc))
     1548                    {
     1549                        vrc = ctrlCopyFileToTarget(pContext, pszSource,
     1550                                                   pszDest, fFlags);
     1551                        RTStrFree(pszDest);
     1552                    }
    14691553                    else
    1470                         RTPrintf("Preparing directory \"%s/%s\" ...\n", pszDestRoot,
    1471                                  itDest->first.c_str());
     1554                    {
     1555                        RTMsgError("Unable to translate path for \"%s\", rc=%Rrc\n",
     1556                                   pszSource, vrc);
     1557                    }
    14721558                }
    1473                 if (!fDryRun)
    1474                     vrc = ctrlCopyPrepareDestDirectory(guest, fHostToGuest,
    1475                                                        pszDestRoot, itDest->first.c_str(),
    1476                                                        Utf8UserName.c_str(), Utf8Password.c_str());
    1477                 if (RT_FAILURE(vrc))
    1478                     break;
     1559                else
     1560                {
     1561                    if (   (RT_SUCCESS(ctrlCopyDirExistsOnSource(pContext, pszSource, &fExists))
     1562                            && fExists)
     1563                        || (   RT_SUCCESS(ctrlCopyDirExistsOnSource(pContext, pszSourceRoot, &fExists))
     1564                            && fExists
     1565                            && pszFilter)
     1566                       )
     1567                    {
     1568                        /* Directory (with filter?). */
     1569                        vrc = ctrlCopyDirToTarget(pContext, pszSource, pszFilter,
     1570                                                  Utf8Dest.c_str(), fFlags, NULL /* Subdir */);
     1571                    }
     1572                }
     1573            }
     1574
     1575            ctrlCopyFreeSourceRoot(pszSourceRoot);
     1576
     1577            if (   RT_SUCCESS(vrc)
     1578                && !fExists)
     1579            {
     1580                RTMsgError("Warning: Source \"%s\" does not exist, skipping!\n",
     1581                           pszSource);
     1582                continue;
    14791583            }
    14801584
    14811585            if (RT_FAILURE(vrc))
    1482                 break;
    1483 
    1484             if (fVerbose)
    1485             {
    1486                 if (!cObjects)
    1487                     RTPrintf("Warning: Source \"%s\" has no (matching) files to copy, skipping!\n",
    1488                              vecSources[s].mSource.c_str());
    1489                 else
    1490                     RTPrintf("Copying \"%s\" (%u files) ...\n",
    1491                              vecSources[s].mSource.c_str(), cObjects);
    1492             }
    1493 
    1494             /*
    1495              * Copy files of each destination root directory to the guest.
    1496              */
    1497             for (itDest = mapDest.begin(); itDest != mapDest.end(); itDest++)
    1498             {
    1499                 if (fVerbose && itDest->second.size())
    1500                 {
    1501                     if (itDest->first.isEmpty())
    1502                         RTPrintf("Copying %u files ...\n", itDest->second.size());
    1503                     else
    1504                         RTPrintf("Copying directory \"%s\" (%u files) ...\n",
    1505                                  itDest->first.c_str(), itDest->second.size());
    1506                 }
    1507 
    1508                 for (unsigned long l = 0; l < itDest->second.size(); l++)
    1509                 {
    1510                     vrc = ctrlCopyToDestDirectory(guest, fVerbose, fDryRun, fHostToGuest,
    1511                                                   pszSourceDir,
    1512                                                   pszDestRoot, itDest->first.c_str() /* Sub directory */,
    1513                                                   itDest->second[l].mFileName.c_str() /* Filename */,
    1514                                                   fFlags, Utf8UserName.c_str(), Utf8Password.c_str());
    1515                     if (RT_FAILURE(vrc))
    1516                         break;
    1517                 }
    1518 
    1519                 if (RT_FAILURE(vrc))
    1520                     break;
    1521             }
    1522 
    1523             if (RT_FAILURE(vrc))
    1524                 break;
    1525         }
    1526 
    1527         RTStrFree(pszSourceDir);
    1528     }
     1586            {
     1587                RTMsgError("Error processing \"%s\", rc=%Rrc\n",
     1588                           pszSource, vrc);
     1589                break;
     1590            }
     1591        }
     1592    }
     1593
     1594    ctrlCopyContextFree(pContext);
    15291595
    15301596    return RT_SUCCESS(vrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
     
    16321698                ctrlPrintError(guest, COM_IIDOF(IGuest)); /* Return code ignored, save original rc. */
    16331699                break;
     1700            }
     1701
     1702            it++;
     1703        }
     1704
     1705        if (FAILED(hrc))
     1706            rcExit = RTEXITCODE_FAILURE;
     1707    }
     1708
     1709    return rcExit;
     1710}
     1711
     1712static int handleCtrlStat(ComPtr<IGuest> guest, HandlerArg *pArg)
     1713{
     1714    AssertPtrReturn(pArg, VERR_INVALID_PARAMETER);
     1715
     1716    /*
     1717     * Parse arguments.
     1718     *
     1719     * Note! No direct returns here, everyone must go thru the cleanup at the
     1720     *       end of this function.
     1721     */
     1722    static const RTGETOPTDEF s_aOptions[] =
     1723    {
     1724        /** @todo Implement "--dereference/-L", and "--file-system/-f" later! */
     1725        { "--password",            'p',         RTGETOPT_REQ_STRING  },
     1726        { "--username",            'u',         RTGETOPT_REQ_STRING  },
     1727        { "--verbose",             'v',         RTGETOPT_REQ_NOTHING }
     1728    };
     1729
     1730    int ch;
     1731    RTGETOPTUNION ValueUnion;
     1732    RTGETOPTSTATE GetState;
     1733    RTGetOptInit(&GetState, pArg->argc, pArg->argv,
     1734                 s_aOptions, RT_ELEMENTS(s_aOptions), 0, RTGETOPTINIT_FLAGS_OPTS_FIRST);
     1735
     1736    Utf8Str Utf8UserName;
     1737    Utf8Str Utf8Password;
     1738
     1739    bool fVerbose = false;
     1740    DESTDIRMAP mapDirs;
     1741
     1742    RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
     1743    while (   (ch = RTGetOpt(&GetState, &ValueUnion))
     1744           && rcExit == RTEXITCODE_SUCCESS)
     1745    {
     1746        /* For options that require an argument, ValueUnion has received the value. */
     1747        switch (ch)
     1748        {
     1749            case 'p': /* Password */
     1750                Utf8Password = ValueUnion.psz;
     1751                break;
     1752
     1753            case 'u': /* User name */
     1754                Utf8UserName = ValueUnion.psz;
     1755                break;
     1756
     1757            case 'v': /* Verbose */
     1758                fVerbose = true;
     1759                break;
     1760
     1761            case VINF_GETOPT_NOT_OPTION:
     1762            {
     1763                mapDirs[ValueUnion.psz]; /* Add element to check to map. */
     1764                break;
     1765            }
     1766
     1767            default:
     1768                rcExit = RTGetOptPrintError(ch, &ValueUnion);
     1769                break;
     1770        }
     1771    }
     1772
     1773    uint32_t cDirs = mapDirs.size();
     1774    if (rcExit == RTEXITCODE_SUCCESS && !cDirs)
     1775        rcExit = errorSyntax(USAGE_GUESTCONTROL, "No element to check specified!");
     1776
     1777    if (rcExit == RTEXITCODE_SUCCESS && Utf8UserName.isEmpty())
     1778        rcExit = errorSyntax(USAGE_GUESTCONTROL, "No user name specified!");
     1779
     1780    if (rcExit == RTEXITCODE_SUCCESS)
     1781    {
     1782        /*
     1783         * Create the directories.
     1784         */
     1785        HRESULT hrc = S_OK;
     1786
     1787        DESTDIRMAPITER it = mapDirs.begin();
     1788        while (it != mapDirs.end())
     1789        {
     1790            if (fVerbose)
     1791                RTPrintf("Checking for element \"%s\" ...\n", it->first.c_str());
     1792
     1793            BOOL fExists;
     1794            hrc = guest->FileExists(Bstr(it->first).raw(),
     1795                                    Bstr(Utf8UserName).raw(), Bstr(Utf8Password).raw(),
     1796                                    &fExists);
     1797            if (FAILED(hrc))
     1798            {
     1799                ctrlPrintError(guest, COM_IIDOF(IGuest)); /* Return code ignored, save original rc. */
     1800                break;
     1801            }
     1802            else
     1803            {
     1804                /** @todo: Output vbox_stat's stdout output to get more information about
     1805                 *         what happened. */
     1806
     1807                /* If there's at least one element which does not exist on the guest,
     1808                 * drop out with exitcode 1. */
     1809                if (!fExists)
     1810                {
     1811                    RTPrintf("Cannot stat for element \"%s\": No such file or directory.\n",
     1812                             it->first.c_str());
     1813                    rcExit = RTEXITCODE_FAILURE;
     1814                }
    16341815            }
    16351816
     
    17721953            rcExit = handleCtrlExecProgram(guest, &arg);
    17731954        }
    1774 #if 0
    17751955        else if (!strcmp(pArg->argv[1], "copyfrom"))
    17761956        {
     
    17781958                                      false /* Guest to host */);
    17791959        }
    1780 #endif
    17811960        else if (   !strcmp(pArg->argv[1], "copyto")
    17821961                 || !strcmp(pArg->argv[1], "cp"))
     
    17921971            rcExit = handleCtrlCreateDirectory(guest, &arg);
    17931972        }
     1973        else if (   !strcmp(pArg->argv[1], "stat"))
     1974        {
     1975            rcExit = handleCtrlStat(guest, &arg);
     1976        }
    17941977        else if (   !strcmp(pArg->argv[1], "updateadditions")
    17951978                 || !strcmp(pArg->argv[1], "updateadds"))
  • trunk/src/VBox/Main/Makefile.kmk

    r38082 r38085  
    649649        src-client/DisplayImpl.cpp \
    650650        src-client/GuestCtrlImpl.cpp \
     651        src-client/GuestCtrlIO.cpp \
    651652        src-client/GuestImpl.cpp \
    652653        src-client/KeyboardImpl.cpp \
  • trunk/src/VBox/Main/include/GuestImpl.h

    r37863 r38085  
    115115
    116116    // Public methods that are not in IDL (only called internally).
     117    HRESULT directoryCreateInternal(IN_BSTR aDirectory, IN_BSTR aUserName, IN_BSTR aPassword,
     118                                    ULONG aMode, ULONG aFlags, int *pRC);
     119    HRESULT directoryOpenInternal(IN_BSTR aDirectory, IN_BSTR aFilter,
     120                                  ULONG aFlags,
     121                                  IN_BSTR aUserName, IN_BSTR aPassword,
     122                                  ULONG *aHandle, int *pRC);
    117123    HRESULT executeProcessInternal(IN_BSTR aCommand, ULONG aFlags,
    118124                                   ComSafeArrayIn(IN_BSTR, aArguments), ComSafeArrayIn(IN_BSTR, aEnvironment),
    119125                                   IN_BSTR aUserName, IN_BSTR aPassword,
    120126                                   ULONG aTimeoutMS, ULONG *aPID, IProgress **aProgress, int *pRC);
    121     HRESULT directoryCreateInternal(IN_BSTR aDirectory, IN_BSTR aUserName, IN_BSTR aPassword,
    122                                     ULONG aMode, ULONG aFlags, int *pRC);
     127    HRESULT fileExistsInternal(IN_BSTR aFile, IN_BSTR aUserName, IN_BSTR aPassword, BOOL *aExists, int *pRC);
     128    HRESULT fileQuerySizeInternal(IN_BSTR aFile, IN_BSTR aUserName, IN_BSTR aPassword, LONG64 *aSize, int *pRC);
    123129    void setAdditionsInfo(Bstr aInterfaceVersion, VBOXOSTYPE aOsType);
    124130    void setAdditionsInfo2(Bstr aAdditionsVersion, Bstr aVersionName, Bstr aRevision);
     
    196202    int processSetStatus(uint32_t u32PID, ExecuteProcessStatus_T enmStatus, uint32_t uExitCode, uint32_t uFlags);
    197203
     204    // Internal guest directory representation.
     205    typedef struct VBOXGUESTCTRL_DIRECTORY
     206    {
     207        char    *mpszDirectory;
     208        char    *mpszFilter;
     209        ULONG    uFlags;
     210        /** Associated PID of started vbox_ls tool. */
     211        uint32_t mPID;
     212        /** Offset within the current retrieved stdout buffer. */
     213        uint64_t mOffset;
     214    } VBOXGUESTCTRL_DIRECTORY, *PVBOXGUESTCTRL_DIRECTORY;
     215    typedef std::map< uint32_t, VBOXGUESTCTRL_DIRECTORY > GuestDirectoryMap;
     216    typedef std::map< uint32_t, VBOXGUESTCTRL_DIRECTORY >::iterator GuestDirectoryMapIter;
     217    typedef std::map< uint32_t, VBOXGUESTCTRL_DIRECTORY >::const_iterator GuestDirectoryMapIterConst;
     218
     219    int directoryCreateHandle(ULONG *puHandle, const char *pszDirectory, const char *pszFilter, ULONG uFlags);
     220    void directoryDestroyHandle(uint32_t uHandle);
     221    uint32_t directoryGetPID(uint32_t uHandle);
     222    bool directoryHandleExists(uint32_t uHandle);
     223
    198224    // Utility functions.
    199     int directoryEntryAppend(const char *pszPath, PRTLISTNODE pList);
    200     int directoryRead(const char *pszDirectory, const char *pszFilter, ULONG uFlags, ULONG *pcObjects, PRTLISTNODE pList);
    201225    int prepareExecuteEnv(const char *pszEnv, void **ppvList, uint32_t *pcbList, uint32_t *pcEnv);
    202226
     
    239263    /** Next upcoming context ID. */
    240264    volatile uint32_t mNextContextID;
     265    /** Next upcoming directory handle ID. */
     266    volatile uint32_t mNextDirectoryID;
    241267    CallbackMap       mCallbackMap;
     268    GuestDirectoryMap mGuestDirectoryMap;
    242269    GuestProcessMap   mGuestProcessMap;
    243270# endif
  • trunk/src/VBox/Main/src-client/GuestCtrlImpl.cpp

    r37930 r38085  
    1717
    1818#include "GuestImpl.h"
     19#include "GuestCtrlImplPrivate.h"
    1920
    2021#include "Global.h"
     
    485486        AssertPtr(pGuest);
    486487
    487 
    488 
    489 #if 0
    490488        /* Does our source file exist? */
    491         if (!RTFileExists(aTask->strSource.c_str()))
    492         {
    493             rc = TaskGuest::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
    494                                                  Guest::tr("Source file \"%s\" does not exist, or is not a file"),
    495                                                  aTask->strSource.c_str());
    496         }
    497         else
    498         {
    499             RTFILE fileSource;
    500             int vrc = RTFileOpen(&fileSource, aTask->strSource.c_str(),
    501                                  RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE);
    502             if (RT_FAILURE(vrc))
     489        BOOL fFileExists;
     490        rc = pGuest->FileExists(Bstr(aTask->strSource).raw(),
     491                                Bstr(aTask->strUserName).raw(), Bstr(aTask->strPassword).raw(),
     492                                &fFileExists);
     493        if (SUCCEEDED(rc))
     494        {
     495            if (!fFileExists)
     496                rc = TaskGuest::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
     497                                                     Guest::tr("Source file \"%s\" does not exist, or is not a file"),
     498                                                     aTask->strSource.c_str());
     499        }
     500
     501        /* Query file size to make an estimate for our progress object. */
     502        if (SUCCEEDED(rc))
     503        {
     504            LONG64 lFileSize;
     505            rc = pGuest->FileQuerySize(Bstr(aTask->strSource).raw(),
     506                                       Bstr(aTask->strUserName).raw(), Bstr(aTask->strPassword).raw(),
     507                                       &lFileSize);
     508            if (FAILED(rc))
     509                rc = TaskGuest::setProgressErrorInfo(rc, aTask->progress, pGuest);
     510
     511            com::SafeArray<IN_BSTR> args;
     512            com::SafeArray<IN_BSTR> env;
     513
     514            if (SUCCEEDED(rc))
    503515            {
    504                 rc = TaskGuest::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
    505                                                      Guest::tr("Could not open source file \"%s\" for reading (%Rrc)"),
    506                                                      aTask->strSource.c_str(),  vrc);
    507             }
    508             else
    509             {
    510                 uint64_t cbSize;
    511                 vrc = RTFileGetSize(fileSource, &cbSize);
    512                 if (RT_FAILURE(vrc))
     516                /*
     517                 * Prepare tool command line.
     518                 */
     519                char szSource[RTPATH_MAX];
     520                if (RTStrPrintf(szSource, sizeof(szSource), "%s", aTask->strSource.c_str()) <= sizeof(szSource) - 1)
    513521                {
    514                     rc = TaskGuest::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
    515                                                          Guest::tr("Could not query file size of \"%s\" (%Rrc)"),
    516                                                          aTask->strSource.c_str(), vrc);
     522                    /*
     523                     * Normalize path slashes, based on the detected guest.
     524                     */
     525                    Utf8Str osType = mData.mOSTypeId;
     526                    if (   osType.contains("Microsoft", Utf8Str::CaseInsensitive)
     527                        || osType.contains("Windows", Utf8Str::CaseInsensitive))
     528                    {
     529                        /* We have a Windows guest. */
     530                        RTPathChangeToDosSlashes(szSource, true /* Force conversion. */);
     531                    }
     532                    else /* ... or something which isn't from Redmond ... */
     533                    {
     534                        RTPathChangeToUnixSlashes(szSource, true /* Force conversion. */);
     535                    }
     536
     537                    args.push_back(Bstr(szSource).raw()); /* Tell our cat tool which file to output. */
    517538                }
    518539                else
     540                    rc = TaskGuest::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
     541                                                         Guest::tr("Error preparing command line"));
     542            }
     543
     544            ComPtr<IProgress> execProgress;
     545            ULONG uPID;
     546            if (SUCCEEDED(rc))
     547            {
     548                LogRel(("Copying file \"%s\" to host \"%s\" (%u bytes) ...\n",
     549                        aTask->strSource.c_str(), aTask->strDest.c_str(), lFileSize));
     550
     551                /*
     552                 * Okay, since we gathered all stuff we need until now to start the
     553                 * actual copying, start the guest part now.
     554                 */
     555                rc = pGuest->ExecuteProcess(Bstr(VBOXSERVICE_TOOL_CAT).raw(),
     556                                            ExecuteProcessFlag_Hidden,
     557                                            ComSafeArrayAsInParam(args),
     558                                            ComSafeArrayAsInParam(env),
     559                                            Bstr(aTask->strUserName).raw(),
     560                                            Bstr(aTask->strPassword).raw(),
     561                                            5 * 1000 /* Wait 5s for getting the process started. */,
     562                                            &uPID, execProgress.asOutParam());
     563                if (FAILED(rc))
     564                    rc = TaskGuest::setProgressErrorInfo(rc, aTask->progress, pGuest);
     565            }
     566
     567            if (SUCCEEDED(rc))
     568            {
     569                BOOL fCompleted = FALSE;
     570                BOOL fCanceled = FALSE;
     571
     572                RTFILE hFileDest;
     573                int vrc = RTFileOpen(&hFileDest, aTask->strDest.c_str(),
     574                                     RTFILE_O_READWRITE | RTFILE_O_OPEN_CREATE | RTFILE_O_DENY_WRITE);
     575                if (RT_FAILURE(vrc))
     576                    rc = TaskGuest::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
     577                                                         Guest::tr("Unable to create/open destination file \"%s\", rc=%Rrc"),
     578                                                         aTask->strDest.c_str(), vrc);
     579                else
    519580                {
    520                     com::SafeArray<IN_BSTR> args;
    521                     com::SafeArray<IN_BSTR> env;
    522 
    523                     /*
    524                      * Prepare tool command line.
    525                      */
    526                     char szOutput[RTPATH_MAX];
    527                     if (RTStrPrintf(szOutput, sizeof(szOutput), "--output=%s", aTask->strDest.c_str()) <= sizeof(szOutput) - 1)
     581                    size_t cbToRead = lFileSize;
     582                    size_t cbTransfered = 0;
     583                    SafeArray<BYTE> aOutputData(_64K);
     584                    while (SUCCEEDED(execProgress->COMGETTER(Completed(&fCompleted))))
    528585                    {
    529                         /*
    530                          * Normalize path slashes, based on the detected guest.
    531                          */
    532                         Utf8Str osType = mData.mOSTypeId;
    533                         if (   osType.contains("Microsoft", Utf8Str::CaseInsensitive)
    534                             || osType.contains("Windows", Utf8Str::CaseInsensitive))
     586                        rc = this->GetProcessOutput(uPID, ProcessOutputFlag_None,
     587                                                    10 * 1000 /* Timeout in ms */,
     588                                                    _64K, ComSafeArrayAsOutParam(aOutputData));
     589                        if (SUCCEEDED(rc))
    535590                        {
    536                             /* We have a Windows guest. */
    537                             RTPathChangeToDosSlashes(szOutput, true /* Force conversion. */);
    538                         }
    539                         else /* ... or something which isn't from Redmond ... */
    540                         {
    541                             RTPathChangeToUnixSlashes(szOutput, true /* Force conversion. */);
    542                         }
    543 
    544                         args.push_back(Bstr(szOutput).raw());             /* We want to write a file ... */
    545                     }
    546                     else
    547                     {
    548                         rc = TaskGuest::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
    549                                                              Guest::tr("Error preparing command line"));
    550                     }
    551 
    552                     ComPtr<IProgress> execProgress;
    553                     ULONG uPID;
    554                     if (SUCCEEDED(rc))
    555                     {
    556                         LogRel(("Copying file \"%s\" to guest \"%s\" (%u bytes) ...\n",
    557                                 aTask->strSource.c_str(), aTask->strDest.c_str(), cbSize));
    558                         /*
    559                          * Okay, since we gathered all stuff we need until now to start the
    560                          * actual copying, start the guest part now.
    561                          */
    562                         rc = pGuest->ExecuteProcess(Bstr(VBOXSERVICE_TOOL_CAT).raw(),
    563                                                       ExecuteProcessFlag_Hidden
    564                                                     | ExecuteProcessFlag_WaitForProcessStartOnly,
    565                                                     ComSafeArrayAsInParam(args),
    566                                                     ComSafeArrayAsInParam(env),
    567                                                     Bstr(aTask->strUserName).raw(),
    568                                                     Bstr(aTask->strPassword).raw(),
    569                                                     5 * 1000 /* Wait 5s for getting the process started. */,
    570                                                     &uPID, execProgress.asOutParam());
    571                         if (FAILED(rc))
    572                             rc = TaskGuest::setProgressErrorInfo(rc, aTask->progress, pGuest);
    573                     }
    574 
    575                     if (SUCCEEDED(rc))
    576                     {
    577                         BOOL fCompleted = FALSE;
    578                         BOOL fCanceled = FALSE;
    579 
    580                         size_t cbToRead = cbSize;
    581                         size_t cbTransfered = 0;
    582                         size_t cbRead;
    583                         SafeArray<BYTE> aInputData(_64K);
    584                         while (   SUCCEEDED(execProgress->COMGETTER(Completed(&fCompleted)))
    585                                && !fCompleted)
    586                         {
    587                             if (!cbToRead)
    588                                 cbRead = 0;
    589                             else
     591                            if (!aOutputData.size())
    590592                            {
    591                                 vrc = RTFileRead(fileSource, (uint8_t*)aInputData.raw(),
    592                                                  RT_MIN(cbToRead, _64K), &cbRead);
    593                                 /*
    594                                  * Some other error occured? There might be a chance that RTFileRead
    595                                  * could not resolve/map the native error code to an IPRT code, so just
    596                                  * print a generic error.
    597                                  */
    598                                 if (RT_FAILURE(vrc))
    599                                 {
     593                                if (cbToRead)
    600594                                    rc = TaskGuest::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
    601                                                                          Guest::tr("Could not read from file \"%s\" (%Rrc)"),
    602                                                                          aTask->strSource.c_str(), vrc);
    603                                     break;
    604                                 }
    605                             }
    606 
    607                             /* Resize buffer to reflect amount we just have read.
    608                              * Size 0 is allowed! */
    609                             aInputData.resize(cbRead);
    610 
    611                             ULONG uFlags = ProcessInputFlag_None;
    612                             /* Did we reach the end of the content we want to transfer (last chunk)? */
    613                             if (   (cbRead < _64K)
    614                                 /* Did we reach the last block which is exactly _64K? */
    615                                 || (cbToRead - cbRead == 0)
    616                                 /* ... or does the user want to cancel? */
    617                                 || (   SUCCEEDED(aTask->progress->COMGETTER(Canceled(&fCanceled)))
    618                                     && fCanceled)
    619                                )
    620                             {
    621                                 uFlags |= ProcessInputFlag_EndOfFile;
    622                             }
    623 
    624                             /* Transfer the current chunk ... */
    625                             ULONG uBytesWritten;
    626                             rc = pGuest->SetProcessInput(uPID, uFlags,
    627                                                          10 * 1000 /* Wait 10s for getting the input data transfered. */,
    628                                                          ComSafeArrayAsInParam(aInputData), &uBytesWritten);
    629                             if (FAILED(rc))
    630                             {
    631                                 rc = TaskGuest::setProgressErrorInfo(rc, aTask->progress, pGuest);
     595                                                                         Guest::tr("Unexpected end of file \"%s\" (%u bytes left)"),
     596                                                                         aTask->strSource.c_str(), cbToRead);
    632597                                break;
    633598                            }
    634599
    635                             Assert(cbRead <= cbToRead);
    636                             Assert(cbToRead >= cbRead);
    637                             cbToRead -= cbRead;
    638 
    639                             cbTransfered += uBytesWritten;
    640                             Assert(cbTransfered <= cbSize);
    641                             aTask->progress->SetCurrentOperationProgress(cbTransfered / (cbSize / 100.0));
    642 
    643                             /* End of file reached? */
    644                             if (cbToRead == 0)
    645                                 break;
    646 
    647                             /* Did the user cancel the operation above? */
    648                             if (fCanceled)
    649                                 break;
    650 
    651                             /* Progress canceled by Main API? */
    652                             if (   SUCCEEDED(execProgress->COMGETTER(Canceled(&fCanceled)))
    653                                 && fCanceled)
     600                            vrc = RTFileWrite(hFileDest, aOutputData.raw(), aOutputData.size(), NULL /* No partial writes */);
     601                            if (RT_FAILURE(vrc))
    654602                            {
    655603                                rc = TaskGuest::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
    656                                                                      Guest::tr("Copy operation of file \"%s\" was canceled on guest side"),
    657                                                                      aTask->strSource.c_str());
     604                                                                     Guest::tr("Error writing to file \"%s\" (%u bytes left), rc=%Rrc"),
     605                                                                     aTask->strSource.c_str(), cbToRead, vrc);
    658606                                break;
    659607                            }
     608
     609                            cbToRead -= aOutputData.size();
     610                            cbTransfered += aOutputData.size();
     611
     612                            aTask->progress->SetCurrentOperationProgress(cbTransfered / (lFileSize / 100.0));
    660613                        }
    661 
    662                         if (SUCCEEDED(rc))
     614                        else
    663615                        {
    664                             /*
    665                              * If we got here this means the started process either was completed,
    666                              * canceled or we simply got all stuff transferred.
    667                              */
    668                             ExecuteProcessStatus_T retStatus;
    669                             ULONG uRetExitCode;
    670                             rc = pGuest->waitForProcessStatusChange(uPID, &retStatus, &uRetExitCode, 10 * 1000 /* 10s timeout. */);
    671                             if (FAILED(rc))
    672                             {
    673                                 rc = TaskGuest::setProgressErrorInfo(rc, aTask->progress, pGuest);
    674                             }
    675                             else
    676                             {
    677                                 if (   uRetExitCode != 0
    678                                     || retStatus    != ExecuteProcessStatus_TerminatedNormally)
    679                                 {
    680                                     rc = TaskGuest::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
    681                                                                          Guest::tr("Guest reported error %u while copying file \"%s\" to \"%s\""),
    682                                                                          uRetExitCode, aTask->strSource.c_str(), aTask->strDest.c_str());
    683                                 }
    684                             }
    685                         }
    686 
    687                         if (SUCCEEDED(rc))
    688                         {
    689                             if (fCanceled)
    690                             {
    691                                 /*
    692                                  * In order to make the progress object to behave nicely, we also have to
    693                                  * notify the object with a complete event when it's canceled.
    694                                  */
    695                                 aTask->progress->notifyComplete(VBOX_E_IPRT_ERROR,
    696                                                                 COM_IIDOF(IGuest),
    697                                                                 Guest::getStaticComponentName(),
    698                                                                 Guest::tr("Copying file \"%s\" canceled"), aTask->strSource.c_str());
    699                             }
    700                             else
    701                             {
    702                                 /*
    703                                  * Even if we succeeded until here make sure to check whether we really transfered
    704                                  * everything.
    705                                  */
    706                                 if (   cbSize > 0
    707                                     && cbTransfered == 0)
    708                                 {
    709                                     /* If nothing was transfered but the file size was > 0 then "vbox_cat" wasn't able to write
    710                                      * to the destination -> access denied. */
    711                                     rc = TaskGuest::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
    712                                                                          Guest::tr("Access denied when copying file \"%s\" to \"%s\""),
    713                                                                          aTask->strSource.c_str(), aTask->strDest.c_str());
    714                                 }
    715                                 else if (cbTransfered < cbSize)
    716                                 {
    717                                     /* If we did not copy all let the user know. */
    718                                     rc = TaskGuest::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
    719                                                                          Guest::tr("Copying file \"%s\" failed (%u/%u bytes transfered)"),
    720                                                                          aTask->strSource.c_str(), cbTransfered, cbSize);
    721                                 }
    722                                 else /* Yay, all went fine! */
    723                                     aTask->progress->notifyComplete(S_OK);
    724                             }
     616                            rc = TaskGuest::setProgressErrorInfo(rc, aTask->progress, pGuest);
     617                            break;
    725618                        }
    726619                    }
     620
     621                    if (SUCCEEDED(rc))
     622                        aTask->progress->notifyComplete(S_OK);
     623
     624                    RTFileClose(hFileDest);
    727625                }
    728                 RTFileClose(fileSource);
    729626            }
    730627        }
    731 #endif
    732628    }
    733629    catch (HRESULT aRC)
     
    28742770                                  ULONG aFlags, IProgress **aProgress)
    28752771{
    2876     ReturnComNotImplemented();
    2877 }
    2878 
    2879 STDMETHODIMP Guest::CopyToGuest(IN_BSTR aSource, IN_BSTR aDest,
    2880                                 IN_BSTR aUserName, IN_BSTR aPassword,
    2881                                 ULONG aFlags, IProgress **aProgress)
    2882 {
    28832772#ifndef VBOX_WITH_GUEST_CONTROL
    28842773    ReturnComNotImplemented();
     
    29152804
    29162805        rc = progress->init(static_cast<IGuest*>(this),
    2917                             Bstr(tr("Copying file from host to guest")).raw(),
     2806                            Bstr(tr("Copying file from guest to host")).raw(),
    29182807                            TRUE /* aCancelable */);
    29192808        if (FAILED(rc)) throw rc;
    29202809
    29212810        /* Initialize our worker task. */
    2922         TaskGuest *pTask = new TaskGuest(TaskGuest::CopyFileToGuest, this, progress);
     2811        TaskGuest *pTask = new TaskGuest(TaskGuest::CopyFileFromGuest, this, progress);
    29232812        AssertPtr(pTask);
    29242813        std::auto_ptr<TaskGuest> task(pTask);
     
    29522841}
    29532842
     2843STDMETHODIMP Guest::CopyToGuest(IN_BSTR aSource, IN_BSTR aDest,
     2844                                IN_BSTR aUserName, IN_BSTR aPassword,
     2845                                ULONG aFlags, IProgress **aProgress)
     2846{
     2847#ifndef VBOX_WITH_GUEST_CONTROL
     2848    ReturnComNotImplemented();
     2849#else /* VBOX_WITH_GUEST_CONTROL */
     2850    CheckComArgStrNotEmptyOrNull(aSource);
     2851    CheckComArgStrNotEmptyOrNull(aDest);
     2852    CheckComArgStrNotEmptyOrNull(aUserName);
     2853    CheckComArgStrNotEmptyOrNull(aPassword);
     2854    CheckComArgOutPointerValid(aProgress);
     2855
     2856    AutoCaller autoCaller(this);
     2857    if (FAILED(autoCaller.rc())) return autoCaller.rc();
     2858
     2859    /* Validate flags. */
     2860    if (aFlags != CopyFileFlag_None)
     2861    {
     2862        if (   !(aFlags & CopyFileFlag_Recursive)
     2863            && !(aFlags & CopyFileFlag_Update)
     2864            && !(aFlags & CopyFileFlag_FollowLinks))
     2865        {
     2866            return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);
     2867        }
     2868    }
     2869
     2870    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     2871
     2872    HRESULT rc = S_OK;
     2873
     2874    ComObjPtr<Progress> progress;
     2875    try
     2876    {
     2877        /* Create the progress object. */
     2878        progress.createObject();
     2879
     2880        rc = progress->init(static_cast<IGuest*>(this),
     2881                            Bstr(tr("Copying file from host to guest")).raw(),
     2882                            TRUE /* aCancelable */);
     2883        if (FAILED(rc)) throw rc;
     2884
     2885        /* Initialize our worker task. */
     2886        TaskGuest *pTask = new TaskGuest(TaskGuest::CopyFileToGuest, this, progress);
     2887        AssertPtr(pTask);
     2888        std::auto_ptr<TaskGuest> task(pTask);
     2889
     2890        /* Assign data - aSource is the source file on the host,
     2891         * aDest reflects the full path on the guest. */
     2892        task->strSource   = (Utf8Str(aSource));
     2893        task->strDest     = (Utf8Str(aDest));
     2894        task->strUserName = (Utf8Str(aUserName));
     2895        task->strPassword = (Utf8Str(aPassword));
     2896        task->uFlags      = aFlags;
     2897
     2898        rc = task->startThread();
     2899        if (FAILED(rc)) throw rc;
     2900
     2901        /* Don't destruct on success. */
     2902        task.release();
     2903    }
     2904    catch (HRESULT aRC)
     2905    {
     2906        rc = aRC;
     2907    }
     2908
     2909    if (SUCCEEDED(rc))
     2910    {
     2911        /* Return progress to the caller. */
     2912        progress.queryInterfaceTo(aProgress);
     2913    }
     2914    return rc;
     2915#endif /* VBOX_WITH_GUEST_CONTROL */
     2916}
     2917
    29542918STDMETHODIMP Guest::DirectoryClose(ULONG aHandle)
    29552919{
     2920#ifndef VBOX_WITH_GUEST_CONTROL
    29562921    ReturnComNotImplemented();
     2922#else /* VBOX_WITH_GUEST_CONTROL */
     2923    using namespace guestControl;
     2924
     2925    if (directoryHandleExists(aHandle))
     2926    {
     2927        directoryDestroyHandle(aHandle);
     2928        return S_OK;
     2929    }
     2930
     2931    return setError(VBOX_E_IPRT_ERROR,
     2932                    Guest::tr("Directory handle is invalid"));
     2933#endif
    29572934}
    29582935
     
    30873064}
    30883065
     3066/**
     3067 * Creates a new directory handle ID and returns it.
     3068 *
     3069 * @return IPRT status code.
     3070 * @param puHandle             Pointer where the handle gets stored to.
     3071 * @param pszDirectory         Directory the handle is assigned to.
     3072 * @param pszFilter            Directory filter.  Optional.
     3073 * @param uFlags               Directory open flags.
     3074 *
     3075 */
     3076int Guest::directoryCreateHandle(ULONG *puHandle, const char *pszDirectory, const char *pszFilter, ULONG uFlags)
     3077{
     3078    AssertPtrReturn(puHandle, VERR_INVALID_POINTER);
     3079    AssertPtrReturn(pszDirectory, VERR_INVALID_POINTER);
     3080    /* pszFilter is optional. */
     3081
     3082    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
     3083
     3084    int rc = VERR_TOO_MUCH_DATA;
     3085    for (uint32_t i = 0; i < UINT32_MAX; i++)
     3086    {
     3087        /* Create a new context ID ... */
     3088        uint32_t uHandleTry = ASMAtomicIncU32(&mNextDirectoryID);
     3089        GuestDirectoryMapIter it = mGuestDirectoryMap.find(uHandleTry);
     3090        if (mGuestDirectoryMap.end() == it)
     3091        {
     3092            rc = VINF_SUCCESS;
     3093            if (!RTStrAPrintf(&mGuestDirectoryMap[uHandleTry].mpszDirectory, pszDirectory))
     3094                rc = VERR_NO_MEMORY;
     3095            else
     3096            {
     3097                /* Filter is optional. */
     3098                if (pszFilter)
     3099                {
     3100                    if (!RTStrAPrintf(&mGuestDirectoryMap[uHandleTry].mpszFilter, pszFilter))
     3101                        rc = VERR_NO_MEMORY;
     3102                }
     3103
     3104                if (RT_SUCCESS(rc))
     3105                {
     3106                    mGuestDirectoryMap[uHandleTry].uFlags = uFlags;
     3107                    *puHandle = uHandleTry;
     3108
     3109                    break;
     3110                }
     3111            }
     3112
     3113            if (RT_FAILURE(rc))
     3114                break;
     3115
     3116            Assert(mGuestDirectoryMap.size());
     3117        }
     3118    }
     3119
     3120    return rc;
     3121}
     3122
     3123void Guest::directoryDestroyHandle(uint32_t uHandle)
     3124{
     3125    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
     3126
     3127    GuestDirectoryMapIter it = mGuestDirectoryMap.find(uHandle);
     3128    if (it != mGuestDirectoryMap.end())
     3129    {
     3130        RTStrFree(it->second.mpszDirectory);
     3131        RTStrFree(it->second.mpszFilter);
     3132
     3133        /* Remove callback context (not used anymore). */
     3134        mGuestDirectoryMap.erase(it);
     3135    }
     3136}
     3137
     3138uint32_t Guest::directoryGetPID(uint32_t uHandle)
     3139{
     3140    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     3141
     3142    GuestDirectoryMapIter it = mGuestDirectoryMap.find(uHandle);
     3143    if (it != mGuestDirectoryMap.end())
     3144        return it->second.mPID;
     3145
     3146    return 0;
     3147}
     3148
     3149bool Guest::directoryHandleExists(uint32_t uHandle)
     3150{
     3151    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     3152
     3153    GuestDirectoryMapIter it = mGuestDirectoryMap.find(uHandle);
     3154    if (it != mGuestDirectoryMap.end())
     3155        return true;
     3156
     3157    return false;
     3158}
     3159
    30893160STDMETHODIMP Guest::DirectoryOpen(IN_BSTR aDirectory, IN_BSTR aFilter,
    30903161                                  ULONG aFlags, IN_BSTR aUserName, IN_BSTR aPassword,
    30913162                                  ULONG *aHandle)
    3092 {
    3093     ReturnComNotImplemented();
    3094 }
    3095 
    3096 STDMETHODIMP Guest::DirectoryRead(ULONG aHandle, IGuestDirEntry **aDirEntry)
    3097 {
    3098     ReturnComNotImplemented();
    3099 }
    3100 
    3101 STDMETHODIMP Guest::FileExists(IN_BSTR aFile, IN_BSTR aUserName, IN_BSTR aPassword, BOOL *aExists)
    31023163{
    31033164#ifndef VBOX_WITH_GUEST_CONTROL
     
    31063167    using namespace guestControl;
    31073168
    3108     return VBOX_E_NOT_SUPPORTED;
     3169    CheckComArgStrNotEmptyOrNull(aDirectory);
     3170    CheckComArgNotNull(aHandle);
     3171
     3172    /* Do not allow anonymous executions (with system rights). */
     3173    if (RT_UNLIKELY((aUserName) == NULL || *(aUserName) == '\0'))
     3174        return setError(E_INVALIDARG, tr("No user name specified"));
     3175
     3176    return directoryOpenInternal(aDirectory, aFilter,
     3177                                 aFlags,
     3178                                 aUserName, aPassword,
     3179                                 aHandle, NULL /* rc */);
    31093180#endif
    31103181}
    31113182
    3112 STDMETHODIMP Guest::FileQuerySize(IN_BSTR aFile, IN_BSTR aUserName, IN_BSTR aPassword, LONG64 *aSize)
     3183HRESULT Guest::directoryOpenInternal(IN_BSTR aDirectory, IN_BSTR aFilter,
     3184                                     ULONG aFlags,
     3185                                     IN_BSTR aUserName, IN_BSTR aPassword,
     3186                                     ULONG *aHandle, int *pRC)
    31133187{
    31143188#ifndef VBOX_WITH_GUEST_CONTROL
     
    31173191    using namespace guestControl;
    31183192
    3119     return VBOX_E_NOT_SUPPORTED;
     3193    CheckComArgStrNotEmptyOrNull(aDirectory);
     3194    CheckComArgNotNull(aHandle);
     3195
     3196    AutoCaller autoCaller(this);
     3197    if (FAILED(autoCaller.rc())) return autoCaller.rc();
     3198
     3199    /* Validate flags. No flags supported yet. */
     3200    if (aFlags != DirectoryOpenFlag_None)
     3201        return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);
     3202
     3203    HRESULT rc = S_OK;
     3204    try
     3205    {
     3206        Utf8Str Utf8Directory(aDirectory);
     3207        Utf8Str Utf8Filter(aFilter);
     3208        Utf8Str Utf8UserName(aUserName);
     3209        Utf8Str Utf8Password(aPassword);
     3210
     3211        com::SafeArray<IN_BSTR> args;
     3212        com::SafeArray<IN_BSTR> env;
     3213
     3214        /*
     3215         * Prepare tool command line.
     3216         */
     3217
     3218        /* We need to get output which is machine-readable in form
     3219         * of "key=value\0..key=value\0\0". */
     3220        args.push_back(Bstr("--machinereadable").raw());
     3221
     3222        /* We want the long output format. Handy for getting a lot of
     3223         * details we could (should?) use (later). */
     3224        args.push_back(Bstr("-l").raw());
     3225
     3226        /* As we want to keep this stuff simple we don't do recursive (-R)
     3227         * or dereferencing (--dereference) lookups here. This has to be done by
     3228         * the user. */
     3229
     3230        /* Construct and hand in actual directory name + filter we want to open. */
     3231        char *pszDirectoryFinal;
     3232        int cbRet;
     3233        if (Utf8Filter.isEmpty())
     3234            cbRet = RTStrAPrintf(&pszDirectoryFinal, "%s", Utf8Directory.c_str());
     3235        else
     3236            cbRet = RTStrAPrintf(&pszDirectoryFinal, "%s/%s",
     3237                                 Utf8Directory.c_str(), Utf8Filter.c_str());
     3238        if (!cbRet)
     3239            return setError(E_OUTOFMEMORY, tr("Out of memory while allocating final directory"));
     3240
     3241        args.push_back(Bstr(pszDirectoryFinal).raw());  /* The directory we want to open. */
     3242
     3243        /*
     3244         * Execute guest process.
     3245         */
     3246        ComPtr<IProgress> progressExec;
     3247        ULONG uPID;
     3248
     3249        rc = ExecuteProcess(Bstr(VBOXSERVICE_TOOL_LS).raw(),
     3250                            ExecuteProcessFlag_Hidden,
     3251                            ComSafeArrayAsInParam(args),
     3252                            ComSafeArrayAsInParam(env),
     3253                            Bstr(Utf8UserName).raw(),
     3254                            Bstr(Utf8Password).raw(),
     3255                            30 * 1000 /* Wait 30s for getting the process started. */,
     3256                            &uPID, progressExec.asOutParam());
     3257
     3258        RTStrFree(pszDirectoryFinal);
     3259
     3260        if (SUCCEEDED(rc))
     3261        {
     3262            /* Wait for process to exit ... */
     3263            rc = progressExec->WaitForCompletion(-1);
     3264            if (FAILED(rc)) return rc;
     3265
     3266            BOOL fCompleted = FALSE;
     3267            BOOL fCanceled = FALSE;
     3268            progressExec->COMGETTER(Completed)(&fCompleted);
     3269            if (!fCompleted)
     3270                progressExec->COMGETTER(Canceled)(&fCanceled);
     3271
     3272            if (fCompleted)
     3273            {
     3274                ExecuteProcessStatus_T retStatus;
     3275                ULONG uRetExitCode, uRetFlags;
     3276                if (SUCCEEDED(rc))
     3277                {
     3278                    rc = GetProcessStatus(uPID, &uRetExitCode, &uRetFlags, &retStatus);
     3279                    if (SUCCEEDED(rc) && uRetExitCode != 0)
     3280                    {
     3281                        rc = setError(VBOX_E_IPRT_ERROR,
     3282                                      tr("Error %u while opening guest directory"), uRetExitCode);
     3283                    }
     3284                }
     3285            }
     3286            else if (fCanceled)
     3287                rc = setError(VBOX_E_IPRT_ERROR,
     3288                              tr("Guest directory opening was aborted"));
     3289            else
     3290                AssertReleaseMsgFailed(("Guest directory opening neither completed nor canceled!?\n"));
     3291
     3292            if (SUCCEEDED(rc))
     3293            {
     3294                /* Assign new directory handle ID. */
     3295                int vrc = directoryCreateHandle(aHandle,
     3296                                                Utf8Directory.c_str(),
     3297                                                Utf8Filter.isEmpty() ? NULL : Utf8Filter.c_str(),
     3298                                                aFlags);
     3299                if (RT_FAILURE(vrc))
     3300                {
     3301                    rc = setError(VBOX_E_IPRT_ERROR,
     3302                                  tr("Unable to create guest directory handle (%Rrc)"), vrc);
     3303                }
     3304            }
     3305        }
     3306    }
     3307    catch (std::bad_alloc &)
     3308    {
     3309        rc = E_OUTOFMEMORY;
     3310    }
     3311    return rc;
     3312#endif /* VBOX_WITH_GUEST_CONTROL */
     3313}
     3314
     3315STDMETHODIMP Guest::DirectoryRead(ULONG aHandle, IGuestDirEntry **aDirEntry)
     3316{
     3317#ifndef VBOX_WITH_GUEST_CONTROL
     3318    ReturnComNotImplemented();
     3319#else /* VBOX_WITH_GUEST_CONTROL */
     3320    using namespace guestControl;
     3321
     3322    uint32_t uPID = directoryGetPID(aHandle);
     3323    if (uPID)
     3324    {
     3325        SafeArray<BYTE> aOutputData;
     3326        ULONG cbOutputData = 0;
     3327
     3328        HRESULT rc = this->GetProcessOutput(uPID, ProcessOutputFlag_None,
     3329                                            30 * 1000 /* Timeout in ms */,
     3330                                            _64K, ComSafeArrayAsOutParam(aOutputData));
     3331        if (SUCCEEDED(rc))
     3332        {
     3333
     3334        }
     3335
     3336        return rc;
     3337    }
     3338
     3339    return setError(VBOX_E_IPRT_ERROR,
     3340                    Guest::tr("Directory handle is invalid"));
     3341#endif
     3342}
     3343
     3344STDMETHODIMP Guest::FileExists(IN_BSTR aFile, IN_BSTR aUserName, IN_BSTR aPassword, BOOL *aExists)
     3345{
     3346#ifndef VBOX_WITH_GUEST_CONTROL
     3347    ReturnComNotImplemented();
     3348#else /* VBOX_WITH_GUEST_CONTROL */
     3349    using namespace guestControl;
     3350
     3351    CheckComArgStrNotEmptyOrNull(aFile);
     3352
     3353    /* Do not allow anonymous executions (with system rights). */
     3354    if (RT_UNLIKELY((aUserName) == NULL || *(aUserName) == '\0'))
     3355        return setError(E_INVALIDARG, tr("No user name specified"));
     3356
     3357    return fileExistsInternal(aFile,
     3358                              aUserName, aPassword, aExists,
     3359                              NULL /* rc */);
     3360#endif
     3361}
     3362
     3363HRESULT Guest::fileExistsInternal(IN_BSTR aFile, IN_BSTR aUserName, IN_BSTR aPassword, BOOL *aExists, int *pRC)
     3364{
     3365#ifndef VBOX_WITH_GUEST_CONTROL
     3366    ReturnComNotImplemented();
     3367#else /* VBOX_WITH_GUEST_CONTROL */
     3368    using namespace guestControl;
     3369
     3370    CheckComArgStrNotEmptyOrNull(aFile);
     3371
     3372    AutoCaller autoCaller(this);
     3373    if (FAILED(autoCaller.rc())) return autoCaller.rc();
     3374
     3375    HRESULT rc = S_OK;
     3376    try
     3377    {
     3378        Utf8Str Utf8File(aFile);
     3379        Utf8Str Utf8UserName(aUserName);
     3380        Utf8Str Utf8Password(aPassword);
     3381
     3382        com::SafeArray<IN_BSTR> args;
     3383        com::SafeArray<IN_BSTR> env;
     3384
     3385        /*
     3386         * Prepare tool command line.
     3387         */
     3388
     3389        /* Only the actual file name to chekc is needed for now. */
     3390        args.push_back(Bstr(Utf8File).raw());
     3391
     3392        /*
     3393         * Execute guest process.
     3394         */
     3395        ComPtr<IProgress> progressExec;
     3396        ULONG uPID;
     3397
     3398        rc = ExecuteProcess(Bstr(VBOXSERVICE_TOOL_STAT).raw(),
     3399                            ExecuteProcessFlag_Hidden,
     3400                            ComSafeArrayAsInParam(args),
     3401                            ComSafeArrayAsInParam(env),
     3402                            Bstr(Utf8UserName).raw(),
     3403                            Bstr(Utf8Password).raw(),
     3404                            30 * 1000 /* Wait 30s for getting the process started. */,
     3405                            &uPID, progressExec.asOutParam());
     3406
     3407        if (SUCCEEDED(rc))
     3408        {
     3409            /* Wait for process to exit ... */
     3410            rc = progressExec->WaitForCompletion(-1);
     3411            if (FAILED(rc)) return rc;
     3412
     3413            BOOL fCompleted = FALSE;
     3414            BOOL fCanceled = FALSE;
     3415            progressExec->COMGETTER(Completed)(&fCompleted);
     3416            if (!fCompleted)
     3417                progressExec->COMGETTER(Canceled)(&fCanceled);
     3418
     3419            if (fCompleted)
     3420            {
     3421                ExecuteProcessStatus_T retStatus;
     3422                ULONG uRetExitCode, uRetFlags;
     3423                if (SUCCEEDED(rc))
     3424                {
     3425                    rc = GetProcessStatus(uPID, &uRetExitCode, &uRetFlags, &retStatus);
     3426                    if (SUCCEEDED(rc))
     3427                    {
     3428                        *aExists = uRetExitCode == 0 ? TRUE : FALSE;
     3429                    }
     3430                    else
     3431                        rc = setError(VBOX_E_IPRT_ERROR,
     3432                                      tr("Error %u while checking for existence of file \"%s\""),
     3433                                      uRetExitCode, Utf8File.c_str());
     3434                }
     3435            }
     3436            else if (fCanceled)
     3437                rc = setError(VBOX_E_IPRT_ERROR,
     3438                              tr("Checking for file existence was aborted"));
     3439            else
     3440                AssertReleaseMsgFailed(("Checking for file existence neither completed nor canceled!?\n"));
     3441        }
     3442    }
     3443    catch (std::bad_alloc &)
     3444    {
     3445        rc = E_OUTOFMEMORY;
     3446    }
     3447    return rc;
     3448#endif
     3449}
     3450
     3451STDMETHODIMP Guest::FileQuerySize(IN_BSTR aFile, IN_BSTR aUserName, IN_BSTR aPassword, LONG64 *aSize)
     3452{
     3453#ifndef VBOX_WITH_GUEST_CONTROL
     3454    ReturnComNotImplemented();
     3455#else /* VBOX_WITH_GUEST_CONTROL */
     3456    using namespace guestControl;
     3457
     3458    CheckComArgStrNotEmptyOrNull(aFile);
     3459
     3460    /* Do not allow anonymous executions (with system rights). */
     3461    if (RT_UNLIKELY((aUserName) == NULL || *(aUserName) == '\0'))
     3462        return setError(E_INVALIDARG, tr("No user name specified"));
     3463
     3464    return fileQuerySizeInternal(aFile,
     3465                                 aUserName, aPassword, aSize,
     3466                                 NULL /* rc */);
     3467#endif
     3468}
     3469
     3470HRESULT Guest::fileQuerySizeInternal(IN_BSTR aFile, IN_BSTR aUserName, IN_BSTR aPassword, LONG64 *aSize, int *pRC)
     3471{
     3472#ifndef VBOX_WITH_GUEST_CONTROL
     3473    ReturnComNotImplemented();
     3474#else /* VBOX_WITH_GUEST_CONTROL */
     3475    using namespace guestControl;
     3476
     3477    CheckComArgStrNotEmptyOrNull(aFile);
     3478
     3479    AutoCaller autoCaller(this);
     3480    if (FAILED(autoCaller.rc())) return autoCaller.rc();
     3481
     3482    HRESULT rc = S_OK;
     3483    try
     3484    {
     3485        Utf8Str Utf8File(aFile);
     3486        Utf8Str Utf8UserName(aUserName);
     3487        Utf8Str Utf8Password(aPassword);
     3488
     3489        com::SafeArray<IN_BSTR> args;
     3490        com::SafeArray<IN_BSTR> env;
     3491
     3492        /*
     3493         * Prepare tool command line.
     3494         */
     3495
     3496        /* We need to get output which is machine-readable in form
     3497         * of "key=value\0..key=value\0\0". */
     3498        args.push_back(Bstr("--machinereadable").raw());
     3499
     3500        /* Only the actual file name to chekc is needed for now. */
     3501        args.push_back(Bstr(Utf8File).raw());
     3502
     3503        /*
     3504         * Execute guest process.
     3505         */
     3506        ComPtr<IProgress> progressExec;
     3507        ULONG uPID;
     3508
     3509        rc = ExecuteProcess(Bstr(VBOXSERVICE_TOOL_STAT).raw(),
     3510                            ExecuteProcessFlag_Hidden,
     3511                            ComSafeArrayAsInParam(args),
     3512                            ComSafeArrayAsInParam(env),
     3513                            Bstr(Utf8UserName).raw(),
     3514                            Bstr(Utf8Password).raw(),
     3515                            30 * 1000 /* Wait 30s for getting the process started. */,
     3516                            &uPID, progressExec.asOutParam());
     3517
     3518        if (SUCCEEDED(rc))
     3519        {
     3520            /* Wait for process to exit ... */
     3521            rc = progressExec->WaitForCompletion(-1);
     3522            if (FAILED(rc)) return rc;
     3523
     3524            BOOL fCompleted = FALSE;
     3525            BOOL fCanceled = FALSE;
     3526            progressExec->COMGETTER(Completed)(&fCompleted);
     3527            if (!fCompleted)
     3528                progressExec->COMGETTER(Canceled)(&fCanceled);
     3529
     3530            if (fCompleted)
     3531            {
     3532                ExecuteProcessStatus_T retStatus;
     3533                ULONG uRetExitCode, uRetFlags;
     3534                if (SUCCEEDED(rc))
     3535                {
     3536                    rc = GetProcessStatus(uPID, &uRetExitCode, &uRetFlags, &retStatus);
     3537                    if (SUCCEEDED(rc))
     3538                    {
     3539                        if (uRetExitCode == 0)
     3540                        {
     3541                            /* Get file size from output stream. */
     3542                            SafeArray<BYTE> aOutputData;
     3543                            ULONG cbOutputData = 0;
     3544
     3545                            GuestProcessStream guestStream;
     3546                            int vrc = VINF_SUCCESS;
     3547                            for (;;)
     3548                            {
     3549                                rc = this->GetProcessOutput(uPID, ProcessOutputFlag_None,
     3550                                                            30 * 1000 /* Timeout in ms */,
     3551                                                            _64K, ComSafeArrayAsOutParam(aOutputData));
     3552                                /** @todo Do stream header validation! */
     3553                                if (   SUCCEEDED(rc)
     3554                                    && aOutputData.size())
     3555                                {
     3556                                    vrc = guestStream.AddData(aOutputData.raw(), aOutputData.size());
     3557                                    if (RT_UNLIKELY(RT_FAILURE(vrc)))
     3558                                        rc = setError(VBOX_E_IPRT_ERROR,
     3559                                                      tr("Error while adding guest output to stream buffer (%Rrc)"), vrc);
     3560                                }
     3561                                else
     3562                                    break;
     3563                            }
     3564
     3565                            if (SUCCEEDED(rc))
     3566                            {
     3567                                vrc = guestStream.Parse();
     3568                                if (   RT_SUCCESS(vrc)
     3569                                    || vrc == VERR_MORE_DATA)
     3570                                {
     3571                                    int64_t iVal;
     3572                                    vrc = guestStream.GetInt64Ex("st_size", &iVal);
     3573                                    if (RT_SUCCESS(vrc))
     3574                                        *aSize = iVal;
     3575                                    else
     3576                                        rc = setError(VBOX_E_IPRT_ERROR,
     3577                                                      tr("Unable to retrieve file size (%Rrc)"), vrc);
     3578                                }
     3579                                else
     3580                                    rc = setError(VBOX_E_IPRT_ERROR,
     3581                                                  tr("Error while parsing guest output (%Rrc)"), vrc);
     3582                            }
     3583                        }
     3584                        else
     3585                            rc = setError(VBOX_E_IPRT_ERROR,
     3586                                          tr("Error querying file size for file \"%s\" (exit code %u)"),
     3587                                          Utf8File.c_str(), uRetExitCode);
     3588                    }
     3589                }
     3590            }
     3591            else if (fCanceled)
     3592                rc = setError(VBOX_E_IPRT_ERROR,
     3593                              tr("Checking for file existence was aborted"));
     3594            else
     3595                AssertReleaseMsgFailed(("Checking for file existence neither completed nor canceled!?\n"));
     3596        }
     3597    }
     3598    catch (std::bad_alloc &)
     3599    {
     3600        rc = E_OUTOFMEMORY;
     3601    }
     3602    return rc;
    31203603#endif
    31213604}
  • trunk/src/VBox/Main/testcase/tstGuestCtrlParseBuffer.cpp

    r37974 r38085  
    5454{
    5555    /* Invalid stuff. */
    56     { NULL,                             0,                                                 0,  0,       0, VERR_INVALID_POINTER },
    57     { NULL,                             512,                                               0,  0,       0, VERR_INVALID_POINTER },
    58     { "",                               0,                                                 0,  0,       0, VERR_INVALID_PARAMETER },
    59     { "",                               0,                                                 0,  0,       0, VERR_INVALID_PARAMETER },
    60     { "foo=bar1",                       0,                                                 0,  0,       0, VERR_INVALID_PARAMETER },
    61     { "foo=bar2",                       0,                                                 50, 50,      0, VERR_INVALID_PARAMETER },
    62     /* Incomplete buffer (missing \0 termination). */
    63     { "",                               1,                                                 0, 0,       0, VERR_MORE_DATA },
    64     { "\0",                             1,                                                 0, 0,       0, VERR_MORE_DATA },
    65     { szUnterm1,                        5,                                                 0, 0,       0, VERR_MORE_DATA },
    66     { "foo1",                           sizeof("foo1"),                                    0, 0,       0, VERR_MORE_DATA },
    67     { szUnterm2,                        8,                                                 0, 0,       0, VERR_MORE_DATA },
     56    { NULL,                             0,                                                 0,  0,                                         0, VERR_INVALID_POINTER },
     57    { NULL,                             512,                                               0,  0,                                         0, VERR_INVALID_POINTER },
     58    { "",                               0,                                                 0,  0,                                         0, VERR_INVALID_PARAMETER },
     59    { "",                               0,                                                 0,  0,                                         0, VERR_INVALID_PARAMETER },
     60    { "foo=bar1",                       0,                                                 0,  0,                                         0, VERR_INVALID_PARAMETER },
     61    { "foo=bar2",                       0,                                                 50, 50,                                        0, VERR_INVALID_PARAMETER },
     62    /* Empty buffers. */
     63    { "",                               1,                                                 0, 1,                                          0, VERR_MORE_DATA },
     64    { "\0",                             1,                                                 0, 1,                                          0, VERR_MORE_DATA },
    6865    /* Incomplete buffer (missing components). */
    69     { "=bar\0",                         sizeof("=bar"),                                    0,  0,                                         0, VERR_MORE_DATA },
     66    { szUnterm1,                        5,                                                 0, 0,                                          0, VERR_MORE_DATA },
     67    { "foo1",                           sizeof("foo1"),                                    0, 0,                                          0, VERR_MORE_DATA },
     68    { "=bar\0",                         sizeof("=bar"),                                    0, 0 ,                                         0, VERR_MORE_DATA },
    7069    /* Last sequence is incomplete -- new offset should point to it. */
    7170    { "hug=sub\0incomplete",            sizeof("hug=sub\0incomplete"),                     0,  sizeof("hug=sub"),                         1, VERR_MORE_DATA },
    7271    { "boo=hoo\0baz=boo\0qwer",         sizeof("boo=hoo\0baz=boo\0qwer"),                  0,  sizeof("boo=hoo\0baz=boo"),                2, VERR_MORE_DATA },
    7372    /* Parsing good stuff. */
     73    { "novalue=",                       sizeof("novalue="),                                0,  sizeof("novalue="),                        1, VINF_SUCCESS },
     74    { szUnterm2,                        8,                                                 0,  sizeof(szUnterm2),                         1, VINF_SUCCESS },
    7475    { "foo2=",                          sizeof("foo2="),                                   0,  sizeof("foo2="),                           1, VINF_SUCCESS },
    7576    { "har=hor",                        sizeof("har=hor"),                                 0,  sizeof("har=hor"),                         1, VINF_SUCCESS },
    7677    { "foo=bar\0baz=boo",               sizeof("foo=bar\0baz=boo"),                        0,  sizeof("foo=bar\0baz=boo"),                2, VINF_SUCCESS },
    7778    /* Parsing until a different block (two terminations, returning offset to next block). */
    78     { "off=rab\0\0zab=oob",             sizeof("off=rab\0\0zab=oob"),                      0,  sizeof("zab=oob"),                         1, VERR_MORE_DATA }
     79    { "off=rab\0a=b\0\0\0\0",           sizeof("off=rab\0a=b\0\0\0"),                      0,  13,                                        2, VERR_MORE_DATA },
     80    { "off=rab\0\0zab=oob",             sizeof("off=rab\0\0zab=oob"),                      0,  9,                                         1, VERR_MORE_DATA },
     81    { "\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 },
     82    { "o2=r2\0z3=o3\0\0f3=g3",          sizeof("o2=r2\0z3=o3\0\0f3=g3"),                   0,  13,                                        2, VERR_MORE_DATA }
    7983};
    8084
     
    8993} aTests2[] =
    9094{
     95    { "\0\0\0\0",                                      sizeof("\0\0\0\0"),                                0, VERR_MORE_DATA },
    9196    { "off=rab\0\0zab=oob",                            sizeof("off=rab\0\0zab=oob"),                      2, VINF_SUCCESS },
    9297    { "\0\0\0soo=foo\0goo=loo\0\0zab=oob",             sizeof("\0\0\0soo=foo\0goo=loo\0\0zab=oob"),       2, VINF_SUCCESS },
    93     { "qoo=uoo\0\0\0\0asdf=\0\0",                      sizeof("qoo=uoo\0\0\0\0asdf=\0\0"),                2, VINF_SUCCESS },
    94     { "foo=bar\0\0\0\0\0\0",                           sizeof("foo=bar\0\0\0\0\0\0"),                     1, VINF_SUCCESS }
     98    { "qoo=uoo\0\0\0\0asdf=\0\0",                      sizeof("qoo=uoo\0\0\0\0asdf=\0\0"),                2, VERR_MORE_DATA },
     99    { "foo=bar\0\0\0\0\0\0",                           sizeof("foo=bar\0\0\0\0\0\0"),                     1, VERR_MORE_DATA },
     100    { "qwer=cvbnr\0\0\0gui=uig\0\0\0",                 sizeof("qwer=cvbnr\0\0\0gui=uig\0\0\0"),           2, VERR_MORE_DATA }
    95101};
    96102
     
    110116        const char *pszEnd = pszStart;
    111117
    112         /* Search and of current pair (key=value\0). */
     118        /* Search end of current pair (key=value\0). */
    113119        while (uCur++ < cbData)
    114120        {
     
    119125
    120126        size_t uPairLen = pszEnd - pszStart;
    121         if (   *pszEnd != '\0'
    122             || !uPairLen)
    123         {
    124             rc = VERR_MORE_DATA;
    125             break;
    126         }
    127 
    128         const char *pszSep = pszStart;
    129         while (   *pszSep != '='
    130                &&  pszSep != pszEnd)
    131         {
    132             pszSep++;
    133         }
    134 
    135         if (   pszSep == pszStart
    136             || pszSep == pszEnd)
    137         {
    138             rc = VERR_MORE_DATA;
    139             break;
    140         }
    141 
    142         size_t uKeyLen = pszSep - pszStart;
    143         size_t uValLen = pszEnd - (pszSep + 1);
    144 
    145         /* Get key (if present). */
    146         if (uKeyLen)
    147         {
    148             Assert(pszSep > pszStart);
    149             char *pszKey = (char*)RTMemAllocZ(uKeyLen + 1);
    150             if (!pszKey)
    151             {
    152                 rc = VERR_NO_MEMORY;
     127        if (uPairLen)
     128        {
     129            const char *pszSep = pszStart;
     130            while (   *pszSep != '='
     131                   &&  pszSep != pszEnd)
     132            {
     133                pszSep++;
     134            }
     135
     136            /* No separator found (or incomplete key=value pair)? */
     137            if (   pszSep == pszStart
     138                || pszSep == pszEnd)
     139            {
     140                *puOffset =  uCur - uPairLen - 1;
     141                rc = VERR_MORE_DATA;
     142            }
     143
     144            if (RT_FAILURE(rc))
    153145                break;
    154             }
    155             memcpy(pszKey, pszStart, uKeyLen);
    156 
    157             mapBuf[RTCString(pszKey)].pszValue = NULL;
    158 
    159             /* Get value (if present). */
    160             if (uValLen)
    161             {
    162                 Assert(pszEnd > pszSep);
    163                 char *pszVal = (char*)RTMemAllocZ(uValLen + 1);
    164                 if (!pszVal)
     146
     147            size_t uKeyLen = pszSep - pszStart;
     148            size_t uValLen = pszEnd - (pszSep + 1);
     149
     150            /* Get key (if present). */
     151            if (uKeyLen)
     152            {
     153                Assert(pszSep > pszStart);
     154                char *pszKey = (char*)RTMemAllocZ(uKeyLen + 1);
     155                if (!pszKey)
    165156                {
    166157                    rc = VERR_NO_MEMORY;
    167158                    break;
    168159                }
    169                 memcpy(pszVal, pszSep + 1, uValLen);
    170 
    171                 mapBuf[RTCString(pszKey)].pszValue = pszVal;
    172             }
    173 
    174             RTMemFree(pszKey);
    175 
    176             *puOffset += uCur - *puOffset;
    177         }
     160                memcpy(pszKey, pszStart, uKeyLen);
     161
     162                mapBuf[RTCString(pszKey)].pszValue = NULL;
     163
     164                /* Get value (if present). */
     165                if (uValLen)
     166                {
     167                    Assert(pszEnd > pszSep);
     168                    char *pszVal = (char*)RTMemAllocZ(uValLen + 1);
     169                    if (!pszVal)
     170                    {
     171                        rc = VERR_NO_MEMORY;
     172                        break;
     173                    }
     174                    memcpy(pszVal, pszSep + 1, uValLen);
     175
     176                    mapBuf[RTCString(pszKey)].pszValue = pszVal;
     177                }
     178
     179                RTMemFree(pszKey);
     180
     181                *puOffset += uCur - *puOffset;
     182            }
     183        }
     184        else /* No pair detected, check for a new block. */
     185        {
     186            do
     187            {
     188                if (*pszEnd == '\0')
     189                {
     190                    *puOffset = uCur;
     191                    rc = VERR_MORE_DATA;
     192                    break;
     193                }
     194                pszEnd++;
     195            } while (++uCur < cbData);
     196        }
     197
     198        if (RT_FAILURE(rc))
     199            break;
    178200    }
     201
     202    RT_CLAMP(*puOffset, 0, cbData);
    179203
    180204    return rc;
     
    206230
    207231    if (sizeof("sizecheck") != 10)
    208         RTTestFailed(hTest, "Basic size test failed (%u <-> 10)", sizeof("sizecheck"));
     232        RTTestFailed(hTest, "Basic size test #1 failed (%u <-> 10)", sizeof("sizecheck"));
     233    if (sizeof("off=rab") != 8)
     234        RTTestFailed(hTest, "Basic size test #2 failed (%u <-> 7)", sizeof("off=rab"));
     235    if (sizeof("off=rab\0\0") != 10)
     236        RTTestFailed(hTest, "Basic size test #3 failed (%u <-> 10)", sizeof("off=rab\0\0"));
    209237
    210238    RTTestIPrintf(RTTESTLVL_INFO, "Doing line tests ...\n");
    211239
    212     for (unsigned iTest = 0; iTest < RT_ELEMENTS(aTests); iTest++)
     240    unsigned iTest = 0;
     241    for (iTest; iTest < RT_ELEMENTS(aTests); iTest++)
    213242    {
    214243        GuestBufferMap bufMap;
     244        uint32_t uOffset = aTests[iTest].uOffsetStart;
    215245
    216246        int iResult = outputBufferParse((BYTE*)aTests[iTest].pbData, aTests[iTest].cbData,
    217                                         &aTests[iTest].uOffsetStart, bufMap);
     247                                        &uOffset, bufMap);
    218248
    219249        RTTestIPrintf(RTTESTLVL_DEBUG, "=> Test #%u\n", iTest);
     
    229259                         bufMap.size(), aTests[iTest].uMapElements);
    230260        }
    231         else if (aTests[iTest].uOffsetStart != aTests[iTest].uOffsetAfter)
     261        else if (uOffset != aTests[iTest].uOffsetAfter)
    232262        {
    233263            RTTestFailed(hTest, "\tOffset %u wrong, expected %u",
    234                          aTests[iTest].uOffsetStart, aTests[iTest].uOffsetAfter);
     264                         uOffset, aTests[iTest].uOffsetAfter);
    235265        }
    236266        else if (iResult == VERR_MORE_DATA)
    237267        {
     268            RTTestIPrintf(RTTESTLVL_DEBUG, "\tMore data (Offset: %u)\n", uOffset);
     269
    238270            /* There is remaining data left in the buffer (which needs to be merged
    239271             * with a following buffer) -- print it. */
    240             const char *pszRemaining = aTests[iTest].pbData;
    241             size_t uOffsetNew = aTests[iTest].uOffsetStart;
    242             size_t uToWrite = aTests[iTest].cbData - uOffsetNew;
    243             if (pszRemaining && uOffsetNew)
    244             {
     272            size_t uToWrite = aTests[iTest].cbData - uOffset;
     273            if (uToWrite)
     274            {
     275                const char *pszRemaining = aTests[iTest].pbData;
    245276                RTTestIPrintf(RTTESTLVL_DEBUG, "\tRemaining (%u):\n", uToWrite);
    246                 RTStrmWriteEx(g_pStdOut, &aTests[iTest].pbData[uOffsetNew], uToWrite - 1, NULL);
     277                RTStrmWriteEx(g_pStdOut, &aTests[iTest].pbData[uOffset], uToWrite - 1, NULL);
    247278                RTTestIPrintf(RTTESTLVL_DEBUG, "\n");
    248279            }
     
    264295        uint32_t uNumBlocks = 0;
    265296
    266         while (uOffset < aTests2[iTest].cbData)
     297        while (uOffset < aTests2[iTest].cbData - 1)
    267298        {
    268299            iResult = outputBufferParse((BYTE*)aTests2[iTest].pbData, aTests2[iTest].cbData,
     
    279310                RTTestIPrintf(RTTESTLVL_DEBUG, "\tNext offset %u (total: %u)\n",
    280311                              uOffset, aTests2[iTest].cbData);
    281                 uOffset++;
    282312            }
    283313            else
     
    288318        }
    289319
    290         if (uNumBlocks != aTests2[iTest].uNumBlocks)
     320        if (iResult != aTests2[iTest].iResult)
     321        {
     322            RTTestFailed(hTest, "\tReturned %Rrc, expected %Rrc",
     323                         iResult, aTests2[iTest].iResult);
     324        }
     325        else if (uNumBlocks != aTests2[iTest].uNumBlocks)
    291326        {
    292327            RTTestFailed(hTest, "\tReturned %u blocks, expected %u\n",
Note: See TracChangeset for help on using the changeset viewer.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette