VirtualBox

Changeset 71213 in vbox for trunk


Ignore:
Timestamp:
Mar 5, 2018 8:30:44 PM (7 years ago)
Author:
vboxsync
Message:

Guest Control: Moved the copying host -> guest implementations from VBoxMange into Main, where it actually belongs. Also cleaned up and refactored the internal session task helpers into file / directory primitives, which can be reused for various different tasks.

Location:
trunk/src/VBox
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Frontends/VBoxManage/VBoxManageGuestCtrl.cpp

    r71168 r71213  
    298298    kStreamTransform_Unix2Dos
    299299};
    300 
    301 
    302 /*********************************************************************************************************************************
    303 *   Internal Functions                                                                                                           *
    304 *********************************************************************************************************************************/
    305 static int gctlCopyDirExists(PCOPYCONTEXT pContext, bool bGuest, const char *pszDir, bool *fExists);
    306 
    307300#endif /* VBOX_ONLY_DOCS */
    308 
    309301
    310302
     
    17371729
    17381730
    1739 /** bird: This is just a code conversion tool, flags are better defined by
    1740  *        the preprocessor, in general.  But the code was using obsoleted
    1741  *        main flags for internal purposes (in a uint32_t) without passing them
    1742  *        along, or it seemed that way.  Enum means compiler checks types. */
    1743 enum gctlCopyFlags
    1744 {
    1745     kGctlCopyFlags_None         = 0,
    1746     kGctlCopyFlags_Recursive    = RT_BIT(1),
    1747     kGctlCopyFlags_FollowLinks  = RT_BIT(2)
    1748 };
    1749 
    1750 
    1751 /**
    1752  * Creates a copy context structure which then can be used with various
    1753  * guest control copy functions. Needs to be free'd with gctlCopyContextFree().
    1754  *
    1755  * @return  IPRT status code.
    1756  * @param   pCtx                    Pointer to command context.
    1757  * @param   fDryRun                 Flag indicating if we want to run a dry run only.
    1758  * @param   fHostToGuest            Flag indicating if we want to copy from host to guest
    1759  *                                  or vice versa.
    1760  * @param   strSessionName          Session name (only for identification purposes).
    1761  * @param   ppContext               Pointer which receives the allocated copy context.
    1762  */
    1763 static int gctlCopyContextCreate(PGCTLCMDCTX pCtx, bool fDryRun, bool fHostToGuest,
    1764                                  const Utf8Str &strSessionName,
    1765                                  PCOPYCONTEXT *ppContext)
    1766 {
    1767     RT_NOREF(strSessionName);
    1768     AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
    1769 
    1770     int vrc = VINF_SUCCESS;
    1771     try
    1772     {
    1773         PCOPYCONTEXT pContext = new COPYCONTEXT();
    1774 
    1775         pContext->pCmdCtx = pCtx;
    1776         pContext->fDryRun = fDryRun;
    1777         pContext->fHostToGuest = fHostToGuest;
    1778 
    1779         *ppContext = pContext;
    1780     }
    1781     catch (std::bad_alloc)
    1782     {
    1783         vrc = VERR_NO_MEMORY;
    1784     }
    1785 
    1786     return vrc;
    1787 }
    1788 
    1789 /**
    1790  * Frees are previously allocated copy context structure.
    1791  *
    1792  * @param   pContext                Pointer to copy context to free.
    1793  */
    1794 static void gctlCopyContextFree(PCOPYCONTEXT pContext)
    1795 {
    1796     if (pContext)
    1797         delete pContext;
    1798 }
    1799 
    1800 /**
    1801  * Translates a source path to a destination path (can be both sides,
    1802  * either host or guest). The source root is needed to determine the start
    1803  * of the relative source path which also needs to present in the destination
    1804  * path.
    1805  *
    1806  * @return  IPRT status code.
    1807  * @param   pszSourceRoot           Source root path. No trailing directory slash!
    1808  * @param   pszSource               Actual source to transform. Must begin with
    1809  *                                  the source root path!
    1810  * @param   pszDest                 Destination path.
    1811  * @param   ppszTranslated          Pointer to the allocated, translated destination
    1812  *                                  path. Must be free'd with RTStrFree().
    1813  */
    1814 static int gctlCopyTranslatePath(const char *pszSourceRoot, const char *pszSource,
    1815                                  const char *pszDest, char **ppszTranslated)
    1816 {
    1817     AssertPtrReturn(pszSourceRoot, VERR_INVALID_POINTER);
    1818     AssertPtrReturn(pszSource, VERR_INVALID_POINTER);
    1819     AssertPtrReturn(pszDest, VERR_INVALID_POINTER);
    1820     AssertPtrReturn(ppszTranslated, VERR_INVALID_POINTER);
    1821 #if 0 /** @todo r=bird: It does not make sense to apply host path parsing semantics onto guest paths. I hope this code isn't mixing host/guest paths in the same way anywhere else... @bugref{6344} */
    1822     AssertReturn(RTPathStartsWith(pszSource, pszSourceRoot), VERR_INVALID_PARAMETER);
    1823 #endif
    1824 
    1825     /* Construct the relative dest destination path by "subtracting" the
    1826      * source from the source root, e.g.
    1827      *
    1828      * source root path = "e:\foo\", source = "e:\foo\bar"
    1829      * dest = "d:\baz\"
    1830      * translated = "d:\baz\bar\"
    1831      */
    1832     char szTranslated[RTPATH_MAX];
    1833     size_t srcOff = strlen(pszSourceRoot);
    1834     AssertReturn(srcOff, VERR_INVALID_PARAMETER);
    1835 
    1836     char *pszDestPath = RTStrDup(pszDest);
    1837     AssertPtrReturn(pszDestPath, VERR_NO_MEMORY);
    1838 
    1839     int vrc;
    1840     if (!RTPathFilename(pszDestPath))
    1841     {
    1842         vrc = RTPathJoin(szTranslated, sizeof(szTranslated),
    1843                          pszDestPath, &pszSource[srcOff]);
    1844     }
    1845     else
    1846     {
    1847         char *pszDestFileName = RTStrDup(RTPathFilename(pszDestPath));
    1848         if (pszDestFileName)
    1849         {
    1850             RTPathStripFilename(pszDestPath);
    1851             vrc = RTPathJoin(szTranslated, sizeof(szTranslated),
    1852                             pszDestPath, pszDestFileName);
    1853             RTStrFree(pszDestFileName);
    1854         }
    1855         else
    1856             vrc = VERR_NO_MEMORY;
    1857     }
    1858     RTStrFree(pszDestPath);
    1859 
    1860     if (RT_SUCCESS(vrc))
    1861     {
    1862         *ppszTranslated = RTStrDup(szTranslated);
    1863 #if 0
    1864         RTPrintf("Root: %s, Source: %s, Dest: %s, Translated: %s\n",
    1865                  pszSourceRoot, pszSource, pszDest, *ppszTranslated);
    1866 #endif
    1867     }
    1868     return vrc;
    1869 }
    1870 
    1871 #ifdef DEBUG_andy_disabled
    1872 static int tstTranslatePath()
    1873 {
    1874     RTAssertSetMayPanic(false /* Do not freak out, please. */);
    1875 
    1876     static struct
    1877     {
    1878         const char *pszSourceRoot;
    1879         const char *pszSource;
    1880         const char *pszDest;
    1881         const char *pszTranslated;
    1882         int         iResult;
    1883     } aTests[] =
    1884     {
    1885         /* Invalid stuff. */
    1886         { NULL, NULL, NULL, NULL, VERR_INVALID_POINTER },
    1887 #ifdef RT_OS_WINDOWS
    1888         /* Windows paths. */
    1889         { "c:\\foo", "c:\\foo\\bar.txt", "c:\\test", "c:\\test\\bar.txt", VINF_SUCCESS },
    1890         { "c:\\foo", "c:\\foo\\baz\\bar.txt", "c:\\test", "c:\\test\\baz\\bar.txt", VINF_SUCCESS },
    1891 #else /* RT_OS_WINDOWS */
    1892         { "/home/test/foo", "/home/test/foo/bar.txt", "/opt/test", "/opt/test/bar.txt", VINF_SUCCESS },
    1893         { "/home/test/foo", "/home/test/foo/baz/bar.txt", "/opt/test", "/opt/test/baz/bar.txt", VINF_SUCCESS },
    1894 #endif /* !RT_OS_WINDOWS */
    1895         /* Mixed paths*/
    1896         /** @todo */
    1897         { NULL }
    1898     };
    1899 
    1900     size_t iTest = 0;
    1901     for (iTest; iTest < RT_ELEMENTS(aTests); iTest++)
    1902     {
    1903         RTPrintf("=> Test %d\n", iTest);
    1904         RTPrintf("\tSourceRoot=%s, Source=%s, Dest=%s\n",
    1905                  aTests[iTest].pszSourceRoot, aTests[iTest].pszSource, aTests[iTest].pszDest);
    1906 
    1907         char *pszTranslated = NULL;
    1908         int iResult =  gctlCopyTranslatePath(aTests[iTest].pszSourceRoot, aTests[iTest].pszSource,
    1909                                              aTests[iTest].pszDest, &pszTranslated);
    1910         if (iResult != aTests[iTest].iResult)
    1911         {
    1912             RTPrintf("\tReturned %Rrc, expected %Rrc\n",
    1913                      iResult, aTests[iTest].iResult);
    1914         }
    1915         else if (   pszTranslated
    1916                  && strcmp(pszTranslated, aTests[iTest].pszTranslated))
    1917         {
    1918             RTPrintf("\tReturned translated path %s, expected %s\n",
    1919                      pszTranslated, aTests[iTest].pszTranslated);
    1920         }
    1921 
    1922         if (pszTranslated)
    1923         {
    1924             RTPrintf("\tTranslated=%s\n", pszTranslated);
    1925             RTStrFree(pszTranslated);
    1926         }
    1927     }
    1928 
    1929     return VINF_SUCCESS; /** @todo */
    1930 }
    1931 #endif
    1932 
    1933 /**
    1934  * Creates a directory on the destination, based on the current copy
    1935  * context.
    1936  *
    1937  * @return  IPRT status code.
    1938  * @param   pContext                Pointer to current copy control context.
    1939  * @param   pszDir                  Directory to create.
    1940  */
    1941 static int gctlCopyDirCreate(PCOPYCONTEXT pContext, const char *pszDir)
    1942 {
    1943     AssertPtrReturn(pContext, VERR_INVALID_POINTER);
    1944     AssertPtrReturn(pszDir, VERR_INVALID_POINTER);
    1945 
    1946     bool fDirExists;
    1947     int vrc = gctlCopyDirExists(pContext, pContext->fHostToGuest, pszDir, &fDirExists);
    1948     if (   RT_SUCCESS(vrc)
    1949         && fDirExists)
    1950     {
    1951         if (pContext->pCmdCtx->cVerbose)
    1952             RTPrintf("Directory \"%s\" already exists\n", pszDir);
    1953         return VINF_SUCCESS;
    1954     }
    1955 
    1956     /* If querying for a directory existence fails there's no point of even trying
    1957      * to create such a directory. */
    1958     if (RT_FAILURE(vrc))
    1959         return vrc;
    1960 
    1961     if (pContext->pCmdCtx->cVerbose)
    1962         RTPrintf("Creating directory \"%s\" ...\n", pszDir);
    1963 
    1964     if (pContext->fDryRun)
    1965         return VINF_SUCCESS;
    1966 
    1967     if (pContext->fHostToGuest) /* We want to create directories on the guest. */
    1968     {
    1969         SafeArray<DirectoryCreateFlag_T> dirCreateFlags;
    1970         dirCreateFlags.push_back(DirectoryCreateFlag_Parents);
    1971         HRESULT rc = pContext->pCmdCtx->pGuestSession->DirectoryCreate(Bstr(pszDir).raw(),
    1972                                                                        0700, ComSafeArrayAsInParam(dirCreateFlags));
    1973         if (FAILED(rc))
    1974             vrc = gctlPrintError(pContext->pCmdCtx->pGuestSession, COM_IIDOF(IGuestSession));
    1975     }
    1976     else /* ... or on the host. */
    1977     {
    1978         vrc = RTDirCreateFullPath(pszDir, 0700);
    1979         if (vrc == VERR_ALREADY_EXISTS)
    1980             vrc = VINF_SUCCESS;
    1981     }
    1982     return vrc;
    1983 }
    1984 
    1985 /**
    1986  * Checks whether a specific host/guest directory exists.
    1987  *
    1988  * @return  IPRT status code.
    1989  * @param   pContext                Pointer to current copy control context.
    1990  * @param   fOnGuest                true if directory needs to be checked on the guest
    1991  *                                  or false if on the host.
    1992  * @param   pszDir                  Actual directory to check.
    1993  * @param   fExists                 Pointer which receives the result if the
    1994  *                                  given directory exists or not.
    1995  */
    1996 static int gctlCopyDirExists(PCOPYCONTEXT pContext, bool fOnGuest,
    1997                              const char *pszDir, bool *fExists)
    1998 {
    1999     AssertPtrReturn(pContext, false);
    2000     AssertPtrReturn(pszDir, false);
    2001     AssertPtrReturn(fExists, false);
    2002 
    2003     int vrc = VINF_SUCCESS;
    2004     if (fOnGuest)
    2005     {
    2006         BOOL fDirExists = FALSE;
    2007         HRESULT rc = pContext->pCmdCtx->pGuestSession->DirectoryExists(Bstr(pszDir).raw(), FALSE /*followSymlinks*/, &fDirExists);
    2008         if (SUCCEEDED(rc))
    2009             *fExists = fDirExists != FALSE;
    2010         else
    2011             vrc = gctlPrintError(pContext->pCmdCtx->pGuestSession, COM_IIDOF(IGuestSession));
    2012     }
    2013     else
    2014         *fExists = RTDirExists(pszDir);
    2015     return vrc;
    2016 }
    2017 
    2018 #if 0 /* unused */
    2019 /**
    2020  * Checks whether a specific directory exists on the destination, based
    2021  * on the current copy context.
    2022  *
    2023  * @return  IPRT status code.
    2024  * @param   pContext                Pointer to current copy control context.
    2025  * @param   pszDir                  Actual directory to check.
    2026  * @param   fExists                 Pointer which receives the result if the
    2027  *                                  given directory exists or not.
    2028  */
    2029 static int gctlCopyDirExistsOnDest(PCOPYCONTEXT pContext, const char *pszDir,
    2030                                    bool *fExists)
    2031 {
    2032     return gctlCopyDirExists(pContext, pContext->fHostToGuest,
    2033                              pszDir, fExists);
    2034 }
    2035 #endif /* unused */
    2036 
    2037 /**
    2038  * Checks whether a specific directory exists on the source, based
    2039  * on the current copy context.
    2040  *
    2041  * @return  IPRT status code.
    2042  * @param   pContext                Pointer to current copy control context.
    2043  * @param   pszDir                  Actual directory to check.
    2044  * @param   fExists                 Pointer which receives the result if the
    2045  *                                  given directory exists or not.
    2046  */
    2047 static int gctlCopyDirExistsOnSource(PCOPYCONTEXT pContext, const char *pszDir,
    2048                                      bool *fExists)
    2049 {
    2050     return gctlCopyDirExists(pContext, !pContext->fHostToGuest,
    2051                              pszDir, fExists);
    2052 }
    2053 
    2054 /**
    2055  * Checks whether a specific host/guest file exists.
    2056  *
    2057  * @return  IPRT status code.
    2058  * @param   pContext                Pointer to current copy control context.
    2059  * @param   bGuest                  true if file needs to be checked on the guest
    2060  *                                  or false if on the host.
    2061  * @param   pszFile                 Actual file to check.
    2062  * @param   fExists                 Pointer which receives the result if the
    2063  *                                  given file exists or not.
    2064  */
    2065 static int gctlCopyFileExists(PCOPYCONTEXT pContext, bool bOnGuest,
    2066                               const char *pszFile, bool *fExists)
    2067 {
    2068     AssertPtrReturn(pContext, false);
    2069     AssertPtrReturn(pszFile, false);
    2070     AssertPtrReturn(fExists, false);
    2071 
    2072     int vrc = VINF_SUCCESS;
    2073     if (bOnGuest)
    2074     {
    2075         BOOL fFileExists = FALSE;
    2076         HRESULT rc = pContext->pCmdCtx->pGuestSession->FileExists(Bstr(pszFile).raw(), FALSE /*followSymlinks*/, &fFileExists);
    2077         if (SUCCEEDED(rc))
    2078             *fExists = fFileExists != FALSE;
    2079         else
    2080             vrc = gctlPrintError(pContext->pCmdCtx->pGuestSession, COM_IIDOF(IGuestSession));
    2081     }
    2082     else
    2083         *fExists = RTFileExists(pszFile);
    2084     return vrc;
    2085 }
    2086 
    2087 #if 0 /* unused */
    2088 /**
    2089  * Checks whether a specific file exists on the destination, based on the
    2090  * current copy context.
    2091  *
    2092  * @return  IPRT status code.
    2093  * @param   pContext                Pointer to current copy control context.
    2094  * @param   pszFile                 Actual file to check.
    2095  * @param   fExists                 Pointer which receives the result if the
    2096  *                                  given file exists or not.
    2097  */
    2098 static int gctlCopyFileExistsOnDest(PCOPYCONTEXT pContext, const char *pszFile,
    2099                                     bool *fExists)
    2100 {
    2101     return gctlCopyFileExists(pContext, pContext->fHostToGuest,
    2102                               pszFile, fExists);
    2103 }
    2104 #endif /* unused */
    2105 
    2106 /**
    2107  * Checks whether a specific file exists on the source, based on the
    2108  * current copy context.
    2109  *
    2110  * @return  IPRT status code.
    2111  * @param   pContext                Pointer to current copy control context.
    2112  * @param   pszFile                 Actual file to check.
    2113  * @param   fExists                 Pointer which receives the result if the
    2114  *                                  given file exists or not.
    2115  */
    2116 static int gctlCopyFileExistsOnSource(PCOPYCONTEXT pContext, const char *pszFile,
    2117                                       bool *fExists)
    2118 {
    2119     return gctlCopyFileExists(pContext, !pContext->fHostToGuest,
    2120                               pszFile, fExists);
    2121 }
    2122 
    2123 /**
    2124  * Copies a source file to the destination.
    2125  *
    2126  * @return  IPRT status code.
    2127  * @param   pContext                Pointer to current copy control context.
    2128  * @param   pszFileSource           Source file to copy to the destination.
    2129  * @param   pszFileDest             Name of copied file on the destination.
    2130  * @param   enmFlags                Copy flags. No supported at the moment and
    2131  *                                  needs to be set to 0.
    2132  */
    2133 static int gctlCopyFileToDest(PCOPYCONTEXT pContext, const char *pszFileSource,
    2134                               const char *pszFileDest, gctlCopyFlags enmFlags)
    2135 {
    2136     AssertPtrReturn(pContext, VERR_INVALID_POINTER);
    2137     AssertPtrReturn(pszFileSource, VERR_INVALID_POINTER);
    2138     AssertPtrReturn(pszFileDest, VERR_INVALID_POINTER);
    2139     AssertReturn(enmFlags == kGctlCopyFlags_None, VERR_INVALID_PARAMETER); /* No flags supported yet. */
    2140 
    2141     if (pContext->pCmdCtx->cVerbose)
    2142         RTPrintf("Copying \"%s\" to \"%s\" ...\n", pszFileSource, pszFileDest);
    2143 
    2144     if (pContext->fDryRun)
    2145         return VINF_SUCCESS;
    2146 
    2147     int vrc = VINF_SUCCESS;
    2148     ComPtr<IProgress> pProgress;
    2149     HRESULT rc;
    2150     if (pContext->fHostToGuest)
    2151     {
    2152         SafeArray<FileCopyFlag_T> copyFlags;
    2153         rc = pContext->pCmdCtx->pGuestSession->FileCopyToGuest(Bstr(pszFileSource).raw(), Bstr(pszFileDest).raw(),
    2154                                                                ComSafeArrayAsInParam(copyFlags),
    2155                                                                pProgress.asOutParam());
    2156     }
    2157     else
    2158     {
    2159         SafeArray<FileCopyFlag_T> copyFlags;
    2160         rc = pContext->pCmdCtx->pGuestSession->FileCopyFromGuest(Bstr(pszFileSource).raw(), Bstr(pszFileDest).raw(),
    2161                                                                  ComSafeArrayAsInParam(copyFlags),
    2162                                                                  pProgress.asOutParam());
    2163     }
    2164 
    2165     if (FAILED(rc))
    2166     {
    2167         vrc = gctlPrintError(pContext->pCmdCtx->pGuestSession, COM_IIDOF(IGuestSession));
    2168     }
    2169     else
    2170     {
    2171         if (pContext->pCmdCtx->cVerbose)
    2172             rc = showProgress(pProgress);
    2173         else
    2174             rc = pProgress->WaitForCompletion(-1 /* No timeout */);
    2175         if (SUCCEEDED(rc))
    2176             CHECK_PROGRESS_ERROR(pProgress, ("File copy failed"));
    2177         vrc = gctlPrintProgressError(pProgress);
    2178     }
    2179 
    2180     return vrc;
    2181 }
    2182 
    2183 /**
    2184  * Copys a directory (tree) from host to the guest.
    2185  *
    2186  * @return  IPRT status code.
    2187  * @param   pContext                Pointer to current copy control context.
    2188  * @param   pszSource               Source directory on the host to copy to the guest.
    2189  * @param   pszFilter               DOS-style wildcard filter (?, *).  Optional.
    2190  * @param   pszDest                 Destination directory on the guest.
    2191  * @param   enmFlags                Copy flags, such as recursive copying.
    2192  * @param   pszSubDir               Current sub directory to handle. Needs to NULL and only
    2193  *                                  is needed for recursion.
    2194  */
    2195 static int gctlCopyDirToGuest(PCOPYCONTEXT pContext,
    2196                               const char *pszSource, const char *pszFilter,
    2197                               const char *pszDest, enum gctlCopyFlags enmFlags,
    2198                               const char *pszSubDir /* For recursion. */)
    2199 {
    2200     AssertPtrReturn(pContext, VERR_INVALID_POINTER);
    2201     AssertPtrReturn(pszSource, VERR_INVALID_POINTER);
    2202     /* Filter is optional. */
    2203     AssertPtrReturn(pszDest, VERR_INVALID_POINTER);
    2204     /* Sub directory is optional. */
    2205 
    2206     /*
    2207      * Construct current path.
    2208      */
    2209     char szCurDir[RTPATH_MAX];
    2210     int vrc = RTStrCopy(szCurDir, sizeof(szCurDir), pszSource);
    2211     if (RT_SUCCESS(vrc) && pszSubDir)
    2212         vrc = RTPathAppend(szCurDir, sizeof(szCurDir), pszSubDir);
    2213 
    2214     if (pContext->pCmdCtx->cVerbose)
    2215         RTPrintf("Processing host directory: %s\n", szCurDir);
    2216 
    2217     /* Flag indicating whether the current directory was created on the
    2218      * target or not. */
    2219     bool fDirCreated = false;
    2220 
    2221     /*
    2222      * Open directory without a filter - RTDirOpenFiltered unfortunately
    2223      * cannot handle sub directories so we have to do the filtering ourselves.
    2224      */
    2225     if (RT_SUCCESS(vrc))
    2226     {
    2227         RTDIR hDir;
    2228         vrc = RTDirOpen(&hDir, szCurDir);
    2229         if (RT_SUCCESS(vrc))
    2230         {
    2231             /*
    2232              * Enumerate the directory tree.
    2233              */
    2234             size_t        cbDirEntry = 0;
    2235             PRTDIRENTRYEX pDirEntry  = NULL;
    2236             while (RT_SUCCESS(vrc))
    2237             {
    2238                 vrc = RTDirReadExA(hDir, &pDirEntry, &cbDirEntry, RTFSOBJATTRADD_NOTHING, 0);
    2239                 if (RT_FAILURE(vrc))
    2240                 {
    2241                     if (vrc == VERR_NO_MORE_FILES)
    2242                         vrc = VINF_SUCCESS;
    2243                     break;
    2244                 }
    2245 
    2246                 switch (pDirEntry->Info.Attr.fMode & RTFS_TYPE_MASK)
    2247                 {
    2248                     case RTFS_TYPE_DIRECTORY:
    2249                     {
    2250                         /* Skip "." and ".." entries. */
    2251                         if (RTDirEntryExIsStdDotLink(pDirEntry))
    2252                             break;
    2253 
    2254                         if (pContext->pCmdCtx->cVerbose)
    2255                             RTPrintf("Directory: %s\n", pDirEntry->szName);
    2256 
    2257                         if (enmFlags & kGctlCopyFlags_Recursive)
    2258                         {
    2259                             char *pszNewSub = NULL;
    2260                             if (pszSubDir)
    2261                                 pszNewSub = RTPathJoinA(pszSubDir, pDirEntry->szName);
    2262                             else
    2263                             {
    2264                                 pszNewSub = RTStrDup(pDirEntry->szName);
    2265                                 RTPathStripTrailingSlash(pszNewSub);
    2266                             }
    2267 
    2268                             if (pszNewSub)
    2269                             {
    2270                                 vrc = gctlCopyDirToGuest(pContext,
    2271                                                          pszSource, pszFilter,
    2272                                                          pszDest, enmFlags, pszNewSub);
    2273                                 RTStrFree(pszNewSub);
    2274                             }
    2275                             else
    2276                                 vrc = VERR_NO_MEMORY;
    2277                         }
    2278                         break;
    2279                     }
    2280 
    2281                     case RTFS_TYPE_SYMLINK:
    2282                         if (   (enmFlags & kGctlCopyFlags_Recursive)
    2283                             && (enmFlags & kGctlCopyFlags_FollowLinks))
    2284                         { /* Fall through to next case is intentional. */ }
    2285                         else
    2286                             break;
    2287                         RT_FALL_THRU();
    2288 
    2289                     case RTFS_TYPE_FILE:
    2290                     {
    2291                         if (   pszFilter
    2292                             && !RTStrSimplePatternMatch(pszFilter, pDirEntry->szName))
    2293                         {
    2294                             break; /* Filter does not match. */
    2295                         }
    2296 
    2297                         if (pContext->pCmdCtx->cVerbose)
    2298                             RTPrintf("File: %s\n", pDirEntry->szName);
    2299 
    2300                         if (!fDirCreated)
    2301                         {
    2302                             char *pszDestDir;
    2303                             vrc = gctlCopyTranslatePath(pszSource, szCurDir, pszDest, &pszDestDir);
    2304                             if (RT_SUCCESS(vrc))
    2305                             {
    2306                                 vrc = gctlCopyDirCreate(pContext, pszDestDir);
    2307                                 RTStrFree(pszDestDir);
    2308 
    2309                                 fDirCreated = true;
    2310                             }
    2311                         }
    2312 
    2313                         if (RT_SUCCESS(vrc))
    2314                         {
    2315                             char *pszFileSource = RTPathJoinA(szCurDir, pDirEntry->szName);
    2316                             if (pszFileSource)
    2317                             {
    2318                                 char *pszFileDest;
    2319                                 vrc = gctlCopyTranslatePath(pszSource, pszFileSource, pszDest, &pszFileDest);
    2320                                 if (RT_SUCCESS(vrc))
    2321                                 {
    2322                                     vrc = gctlCopyFileToDest(pContext, pszFileSource,
    2323                                                              pszFileDest, kGctlCopyFlags_None);
    2324                                     RTStrFree(pszFileDest);
    2325                                 }
    2326                                 RTStrFree(pszFileSource);
    2327                             }
    2328                         }
    2329                         break;
    2330                     }
    2331 
    2332                     default:
    2333                         break;
    2334                 }
    2335                 if (RT_FAILURE(vrc))
    2336                     break;
    2337             }
    2338 
    2339             RTDirReadExAFree(&pDirEntry, &cbDirEntry);
    2340             RTDirClose(hDir);
    2341         }
    2342     }
    2343     return vrc;
    2344 }
    2345 
    2346 /**
    2347  * Copys a directory (tree) from guest to the host.
    2348  *
    2349  * @return  IPRT status code.
    2350  * @param   pContext                Pointer to current copy control context.
    2351  * @param   pszSource               Source directory on the guest to copy to the host.
    2352  * @param   pszFilter               DOS-style wildcard filter (?, *).  Optional.
    2353  * @param   pszDest                 Destination directory on the host.
    2354  * @param   enmFlags                Copy flags, such as recursive copying.
    2355  * @param   pszSubDir               Current sub directory to handle. Needs to NULL and only
    2356  *                                  is needed for recursion.
    2357  */
    2358 static int gctlCopyDirToHost(PCOPYCONTEXT pContext,
    2359                              const char *pszSource, const char *pszFilter,
    2360                              const char *pszDest, gctlCopyFlags enmFlags,
    2361                              const char *pszSubDir /* For recursion. */)
    2362 {
    2363     AssertPtrReturn(pContext, VERR_INVALID_POINTER);
    2364     AssertPtrReturn(pszSource, VERR_INVALID_POINTER);
    2365     /* Filter is optional. */
    2366     AssertPtrReturn(pszDest, VERR_INVALID_POINTER);
    2367     /* Sub directory is optional. */
    2368 
    2369     /*
    2370      * Construct current path.
    2371      */
    2372     char szCurDir[RTPATH_MAX];
    2373     int vrc = RTStrCopy(szCurDir, sizeof(szCurDir), pszSource);
    2374     if (RT_SUCCESS(vrc) && pszSubDir)
    2375         vrc = RTPathAppend(szCurDir, sizeof(szCurDir), pszSubDir);
    2376 
    2377     if (RT_FAILURE(vrc))
    2378         return vrc;
    2379 
    2380     if (pContext->pCmdCtx->cVerbose)
    2381         RTPrintf("Processing guest directory: %s\n", szCurDir);
    2382 
    2383     /* Flag indicating whether the current directory was created on the
    2384      * target or not. */
    2385     bool fDirCreated = false;
    2386     SafeArray<DirectoryOpenFlag_T> dirOpenFlags; /* No flags supported yet. */
    2387     ComPtr<IGuestDirectory> pDirectory;
    2388     HRESULT rc = pContext->pCmdCtx->pGuestSession->DirectoryOpen(Bstr(szCurDir).raw(), Bstr(pszFilter).raw(),
    2389                                                         ComSafeArrayAsInParam(dirOpenFlags),
    2390                                                         pDirectory.asOutParam());
    2391     if (FAILED(rc))
    2392         return gctlPrintError(pContext->pCmdCtx->pGuestSession, COM_IIDOF(IGuestSession));
    2393     ComPtr<IFsObjInfo> dirEntry;
    2394     while (true)
    2395     {
    2396         rc = pDirectory->Read(dirEntry.asOutParam());
    2397         if (FAILED(rc))
    2398             break;
    2399 
    2400         FsObjType_T enmType;
    2401         dirEntry->COMGETTER(Type)(&enmType);
    2402 
    2403         Bstr strName;
    2404         dirEntry->COMGETTER(Name)(strName.asOutParam());
    2405 
    2406         switch (enmType)
    2407         {
    2408             case FsObjType_Directory:
    2409             {
    2410                 Assert(!strName.isEmpty());
    2411 
    2412                 /* Skip "." and ".." entries. */
    2413                 if (   !strName.compare(Bstr("."))
    2414                     || !strName.compare(Bstr("..")))
    2415                     break;
    2416 
    2417                 if (pContext->pCmdCtx->cVerbose)
    2418                 {
    2419                     Utf8Str strDir(strName);
    2420                     RTPrintf("Directory: %s\n", strDir.c_str());
    2421                 }
    2422 
    2423                 if (enmFlags & kGctlCopyFlags_Recursive)
    2424                 {
    2425                     Utf8Str strDir(strName);
    2426                     char *pszNewSub = NULL;
    2427                     if (pszSubDir)
    2428                         pszNewSub = RTPathJoinA(pszSubDir, strDir.c_str());
    2429                     else
    2430                     {
    2431                         pszNewSub = RTStrDup(strDir.c_str());
    2432                         RTPathStripTrailingSlash(pszNewSub);
    2433                     }
    2434                     if (pszNewSub)
    2435                     {
    2436                         vrc = gctlCopyDirToHost(pContext,
    2437                                                 pszSource, pszFilter,
    2438                                                 pszDest, enmFlags, pszNewSub);
    2439                         RTStrFree(pszNewSub);
    2440                     }
    2441                     else
    2442                         vrc = VERR_NO_MEMORY;
    2443                 }
    2444                 break;
    2445             }
    2446 
    2447             case FsObjType_Symlink:
    2448                 if (   (enmFlags & kGctlCopyFlags_Recursive)
    2449                     && (enmFlags & kGctlCopyFlags_FollowLinks))
    2450                 {
    2451                     /* Fall through to next case is intentional. */
    2452                 }
    2453                 else
    2454                     break;
    2455 
    2456             case FsObjType_File:
    2457             {
    2458                 Assert(!strName.isEmpty());
    2459 
    2460                 Utf8Str strFile(strName);
    2461                 if (   pszFilter
    2462                     && !RTStrSimplePatternMatch(pszFilter, strFile.c_str()))
    2463                 {
    2464                     break; /* Filter does not match. */
    2465                 }
    2466 
    2467                 if (pContext->pCmdCtx->cVerbose)
    2468                     RTPrintf("File: %s\n", strFile.c_str());
    2469 
    2470                 if (!fDirCreated)
    2471                 {
    2472                     char *pszDestDir;
    2473                     vrc = gctlCopyTranslatePath(pszSource, szCurDir,
    2474                                                 pszDest, &pszDestDir);
    2475                     if (RT_SUCCESS(vrc))
    2476                     {
    2477                         vrc = gctlCopyDirCreate(pContext, pszDestDir);
    2478                         RTStrFree(pszDestDir);
    2479 
    2480                         fDirCreated = true;
    2481                     }
    2482                 }
    2483 
    2484                 if (RT_SUCCESS(vrc))
    2485                 {
    2486                     char *pszFileSource = RTPathJoinA(szCurDir, strFile.c_str());
    2487                     if (pszFileSource)
    2488                     {
    2489                         char *pszFileDest;
    2490                         vrc = gctlCopyTranslatePath(pszSource, pszFileSource,
    2491                                                    pszDest, &pszFileDest);
    2492                         if (RT_SUCCESS(vrc))
    2493                         {
    2494                             vrc = gctlCopyFileToDest(pContext, pszFileSource,
    2495                                                      pszFileDest, kGctlCopyFlags_None);
    2496                             RTStrFree(pszFileDest);
    2497                         }
    2498                         RTStrFree(pszFileSource);
    2499                     }
    2500                     else
    2501                         vrc = VERR_NO_MEMORY;
    2502                 }
    2503                 break;
    2504             }
    2505 
    2506             default:
    2507                 RTPrintf("Warning: Directory entry of type %ld not handled, skipping ...\n",
    2508                          enmType);
    2509                 break;
    2510         }
    2511 
    2512         if (RT_FAILURE(vrc))
    2513             break;
    2514     }
    2515 
    2516     if (RT_UNLIKELY(FAILED(rc)))
    2517     {
    2518         switch (rc)
    2519         {
    2520             case E_ABORT: /* No more directory entries left to process. */
    2521                 break;
    2522 
    2523             case VBOX_E_FILE_ERROR: /* Current entry cannot be accessed to
    2524                                        to missing rights. */
    2525             {
    2526                 RTPrintf("Warning: Cannot access \"%s\", skipping ...\n",
    2527                          szCurDir);
    2528                 break;
    2529             }
    2530 
    2531             default:
    2532                 vrc = gctlPrintError(pDirectory, COM_IIDOF(IGuestDirectory));
    2533                 break;
    2534         }
    2535     }
    2536 
    2537     HRESULT rc2 = pDirectory->Close();
    2538     if (FAILED(rc2))
    2539     {
    2540         int vrc2 = gctlPrintError(pDirectory, COM_IIDOF(IGuestDirectory));
    2541         if (RT_SUCCESS(vrc))
    2542             vrc = vrc2;
    2543     }
    2544     else if (SUCCEEDED(rc))
    2545         rc = rc2;
    2546 
    2547     return vrc;
    2548 }
    2549 
    2550 /**
    2551  * Copys a directory (tree) to the destination, based on the current copy
    2552  * context.
    2553  *
    2554  * @return  IPRT status code.
    2555  * @param   pContext                Pointer to current copy control context.
    2556  * @param   pszSource               Source directory to copy to the destination.
    2557  * @param   pszFilter               DOS-style wildcard filter (?, *).  Optional.
    2558  * @param   pszDest                 Destination directory where to copy in the source
    2559  *                                  source directory.
    2560  * @param   enmFlags                Copy flags, such as recursive copying.
    2561  */
    2562 static int gctlCopyDirToDest(PCOPYCONTEXT pContext,
    2563                              const char *pszSource, const char *pszFilter,
    2564                              const char *pszDest, enum gctlCopyFlags enmFlags)
    2565 {
    2566     if (pContext->fHostToGuest)
    2567         return gctlCopyDirToGuest(pContext, pszSource, pszFilter,
    2568                                   pszDest, enmFlags, NULL /* Sub directory, only for recursion. */);
    2569     return gctlCopyDirToHost(pContext, pszSource, pszFilter,
    2570                              pszDest, enmFlags, NULL /* Sub directory, only for recursion. */);
    2571 }
    2572 
    2573 /**
    2574  * Creates a source root by stripping file names or filters of the specified source.
    2575  *
    2576  * @return  IPRT status code.
    2577  * @param   pszSource               Source to create source root for.
    2578  * @param   ppszSourceRoot          Pointer that receives the allocated source root. Needs
    2579  *                                  to be free'd with gctlCopyFreeSourceRoot().
    2580  */
    2581 static int gctlCopyCreateSourceRoot(const char *pszSource, char **ppszSourceRoot)
    2582 {
    2583     AssertPtrReturn(pszSource, VERR_INVALID_POINTER);
    2584     AssertPtrReturn(ppszSourceRoot, VERR_INVALID_POINTER);
    2585 
    2586     char *pszNewRoot = RTStrDup(pszSource);
    2587     if (!pszNewRoot)
    2588         return VERR_NO_MEMORY;
    2589 
    2590     size_t lenRoot = strlen(pszNewRoot);
    2591     if (   lenRoot
    2592         && (   pszNewRoot[lenRoot - 1] == '/'
    2593             || pszNewRoot[lenRoot - 1] == '\\')
    2594        )
    2595     {
    2596         pszNewRoot[lenRoot - 1] = '\0';
    2597     }
    2598 
    2599     if (   lenRoot > 1
    2600         && (   pszNewRoot[lenRoot - 2] == '/'
    2601             || pszNewRoot[lenRoot - 2] == '\\')
    2602        )
    2603     {
    2604         pszNewRoot[lenRoot - 2] = '\0';
    2605     }
    2606 
    2607     if (!lenRoot)
    2608     {
    2609         /* If there's anything (like a file name or a filter),
    2610          * strip it! */
    2611         RTPathStripFilename(pszNewRoot);
    2612     }
    2613 
    2614     *ppszSourceRoot = pszNewRoot;
    2615 
    2616     return VINF_SUCCESS;
    2617 }
    2618 
    2619 /**
    2620  * Frees a previously allocated source root.
    2621  *
    2622  * @return  IPRT status code.
    2623  * @param   pszSourceRoot           Source root to free.
    2624  */
    2625 static void gctlCopyFreeSourceRoot(char *pszSourceRoot)
    2626 {
    2627     RTStrFree(pszSourceRoot);
    2628 }
    2629 
    26301731static RTEXITCODE gctlHandleCopy(PGCTLCMDCTX pCtx, int argc, char **argv, bool fHostToGuest)
    26311732{
     
    26681769    Utf8Str strSource;
    26691770    const char *pszDst = NULL;
    2670     enum gctlCopyFlags enmFlags = kGctlCopyFlags_None;
    2671     /*bool fCopyRecursive = false; - unused */
    26721771    bool fDryRun = false;
     1772    bool fFollow = false;
     1773    bool fRecursive = false;
    26731774    uint32_t uUsage = fHostToGuest ? USAGE_GSTCTRL_COPYTO : USAGE_GSTCTRL_COPYFROM;
    26741775
     
    26881789
    26891790            case GETOPTDEF_COPY_FOLLOW:
    2690                 enmFlags = (enum gctlCopyFlags)((uint32_t)enmFlags | kGctlCopyFlags_FollowLinks);
     1791                fFollow = true;
    26911792                break;
    26921793
    26931794            case 'R': /* Recursive processing */
    2694                 enmFlags = (enum gctlCopyFlags)((uint32_t)enmFlags | kGctlCopyFlags_Recursive);
     1795                fRecursive = true;
    26951796                break;
    26961797
     
    27471848    }
    27481849
    2749     /* Create the copy context -- it contains all information
    2750      * the routines need to know when handling the actual copying. */
    2751     PCOPYCONTEXT pContext = NULL;
    2752     vrc = gctlCopyContextCreate(pCtx, fDryRun, fHostToGuest,
    2753                                   fHostToGuest
    2754                                 ? "VBoxManage Guest Control - Copy to guest"
    2755                                 : "VBoxManage Guest Control - Copy from guest", &pContext);
    2756     if (RT_FAILURE(vrc))
    2757     {
    2758         RTMsgError("Unable to create copy context, rc=%Rrc\n", vrc);
    2759         return RTEXITCODE_FAILURE;
    2760     }
    2761 
    2762 /** @todo r=bird: RTPathFilename and RTPathStripFilename won't work
    2763  * correctly on non-windows hosts when the guest is from the DOS world (Windows,
    2764  * OS/2, DOS).  The host doesn't know about DOS slashes, only UNIX slashes and
    2765  * will get the wrong idea if some dilligent user does:
    2766  *
    2767  *      copyto myfile.txt 'C:\guestfile.txt'
    2768  * or
    2769  *      copyto myfile.txt 'D:guestfile.txt'
    2770  *
    2771  * @bugref{6344}
    2772  */
    2773     if (!RTPathFilename(pszDst))
    2774     {
    2775         vrc = gctlCopyDirCreate(pContext, pszDst);
     1850    ComPtr<IProgress> pProgress;
     1851    HRESULT rc;
     1852
     1853    for (unsigned long s = 0; s < vecSources.size(); s++)
     1854    {
     1855        char *pszSrc = RTStrDup(vecSources[s].GetSource());
     1856        AssertPtrBreakStmt(pszSrc, vrc = VERR_NO_MEMORY);
     1857        const char *pszFilter = vecSources[s].GetFilter();
     1858        RT_NOREF(pszFilter);
     1859        /* pszFilter can be NULL if not set. */
     1860
     1861        if (fHostToGuest)
     1862        {
     1863            if (RTFileExists(pszSrc))
     1864            {
     1865                SafeArray<FileCopyFlag_T> copyFlags;
     1866                rc = pCtx->pGuestSession->FileCopyToGuest(Bstr(pszSrc).raw(), Bstr(pszDst).raw(),
     1867                                                          ComSafeArrayAsInParam(copyFlags), pProgress.asOutParam());
     1868            }
     1869            else if (RTDirExists(pszSrc))
     1870            {
     1871                SafeArray<DirectoryCopyFlags_T> copyFlags;
     1872                copyFlags.push_back(DirectoryCopyFlags_CopyIntoExisting);
     1873                rc = pCtx->pGuestSession->DirectoryCopyToGuest(Bstr(pszSrc).raw(), Bstr(pszDst).raw(),
     1874                                                               ComSafeArrayAsInParam(copyFlags), pProgress.asOutParam());
     1875            }
     1876            else if (pCtx->cVerbose)
     1877                RTPrintf("Warning: \"%s\" does not exist or is not a file/directory, skipping ...\n", pszSrc);
     1878        }
     1879        else
     1880        {
     1881            SafeArray<FileCopyFlag_T> copyFlags;
     1882            rc = pCtx->pGuestSession->FileCopyFromGuest(Bstr(pszSrc).raw(), Bstr(pszDst).raw(),
     1883                                                        ComSafeArrayAsInParam(copyFlags), pProgress.asOutParam());
     1884        }
     1885    }
     1886
     1887    if (FAILED(rc))
     1888    {
     1889        vrc = gctlPrintError(pCtx->pGuestSession, COM_IIDOF(IGuestSession));
    27761890    }
    27771891    else
    27781892    {
    2779         /* We assume we got a file name as destination -- so strip
    2780          * the actual file name and make sure the appropriate
    2781          * directories get created. */
    2782         char *pszDstDir = RTStrDup(pszDst);
    2783         AssertPtr(pszDstDir);
    2784         RTPathStripFilename(pszDstDir);
    2785         vrc = gctlCopyDirCreate(pContext, pszDstDir);
    2786         RTStrFree(pszDstDir);
    2787     }
    2788 
    2789     if (RT_SUCCESS(vrc))
    2790     {
    2791         /*
    2792          * Here starts the actual fun!
    2793          * Handle all given sources one by one.
    2794          */
    2795         for (unsigned long s = 0; s < vecSources.size(); s++)
    2796         {
    2797             char *pszSource = RTStrDup(vecSources[s].GetSource());
    2798             AssertPtrBreakStmt(pszSource, vrc = VERR_NO_MEMORY);
    2799             const char *pszFilter = vecSources[s].GetFilter();
    2800             if (!strlen(pszFilter))
    2801                 pszFilter = NULL; /* If empty filter then there's no filter :-) */
    2802 
    2803             char *pszSourceRoot;
    2804             vrc = gctlCopyCreateSourceRoot(pszSource, &pszSourceRoot);
    2805             if (RT_FAILURE(vrc))
    2806             {
    2807                 RTMsgError("Unable to create source root, rc=%Rrc\n", vrc);
    2808                 break;
    2809             }
    2810 
    2811             if (pCtx->cVerbose)
    2812                 RTPrintf("Source: %s\n", pszSource);
    2813 
    2814             /** @todo Files with filter?? */
    2815             bool fSourceIsFile = false;
    2816             bool fSourceExists;
    2817 
    2818             size_t cchSource = strlen(pszSource);
    2819             if (   cchSource > 1
    2820                 && RTPATH_IS_SLASH(pszSource[cchSource - 1]))
    2821             {
    2822                 if (pszFilter) /* Directory with filter (so use source root w/o the actual filter). */
    2823                     vrc = gctlCopyDirExistsOnSource(pContext, pszSourceRoot, &fSourceExists);
    2824                 else /* Regular directory without filter. */
    2825                     vrc = gctlCopyDirExistsOnSource(pContext, pszSource, &fSourceExists);
    2826 
    2827                 if (fSourceExists)
    2828                 {
    2829                     /* Strip trailing slash from our source element so that other functions
    2830                      * can use this stuff properly (like RTPathStartsWith). */
    2831                     RTPathStripTrailingSlash(pszSource);
    2832                 }
    2833             }
    2834             else
    2835             {
    2836                 vrc = gctlCopyFileExistsOnSource(pContext, pszSource, &fSourceExists);
    2837                 if (   RT_SUCCESS(vrc)
    2838                     && fSourceExists)
    2839                 {
    2840                     fSourceIsFile = true;
    2841                 }
    2842             }
    2843 
    2844             if (   RT_SUCCESS(vrc)
    2845                 && fSourceExists)
    2846             {
    2847                 if (fSourceIsFile)
    2848                 {
    2849                     /* Single file. */
    2850                     char *pszDstFile;
    2851                     vrc = gctlCopyTranslatePath(pszSourceRoot, pszSource, pszDst, &pszDstFile);
    2852                     if (RT_SUCCESS(vrc))
    2853                     {
    2854                         vrc = gctlCopyFileToDest(pContext, pszSource, pszDstFile, kGctlCopyFlags_None);
    2855                         RTStrFree(pszDstFile);
    2856                     }
    2857                     else
    2858                         RTMsgError("Unable to translate path for \"%s\", rc=%Rrc\n", pszSource, vrc);
    2859                 }
    2860                 else
    2861                 {
    2862                     /* Directory (with filter?). */
    2863                     vrc = gctlCopyDirToDest(pContext, pszSource, pszFilter, pszDst, enmFlags);
    2864                 }
    2865             }
    2866 
    2867             gctlCopyFreeSourceRoot(pszSourceRoot);
    2868 
    2869             if (   RT_SUCCESS(vrc)
    2870                 && !fSourceExists)
    2871             {
    2872                 RTMsgError("Warning: Source \"%s\" does not exist, skipping!\n",
    2873                            pszSource);
    2874                 RTStrFree(pszSource);
    2875                 continue;
    2876             }
    2877             else if (RT_FAILURE(vrc))
    2878             {
    2879                 RTMsgError("Error processing \"%s\", rc=%Rrc\n",
    2880                            pszSource, vrc);
    2881                 RTStrFree(pszSource);
    2882                 break;
    2883             }
    2884 
    2885             RTStrFree(pszSource);
    2886         }
    2887     }
    2888 
    2889     gctlCopyContextFree(pContext);
     1893        if (pCtx->cVerbose)
     1894            rc = showProgress(pProgress);
     1895        else
     1896            rc = pProgress->WaitForCompletion(-1 /* No timeout */);
     1897        if (SUCCEEDED(rc))
     1898            CHECK_PROGRESS_ERROR(pProgress, ("File copy failed"));
     1899        vrc = gctlPrintProgressError(pProgress);
     1900    }
    28901901
    28911902    return RT_SUCCESS(vrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
  • trunk/src/VBox/Main/include/GuestSessionImpl.h

    r69500 r71213  
    55
    66/*
    7  * Copyright (C) 2012-2017 Oracle Corporation
     7 * Copyright (C) 2012-2018 Oracle Corporation
    88 *
    99 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    7070    }
    7171
    72     const ComObjPtr<Progress>& GetProgressObject() const {return mProgress;}
    73 
    74 protected:
    75 
    76     int getGuestProperty(const ComObjPtr<Guest> &pGuest,
    77                            const Utf8Str &strPath, Utf8Str &strValue);
     72    const ComObjPtr<Progress>& GetProgressObject() const { return mProgress; }
     73
     74protected:
     75
     76    /** @name Directory handling primitives.
     77     * @{ */
     78    int directoryCreate(const com::Utf8Str &strPath, DirectoryCreateFlag_T enmDirecotryCreateFlags, uint32_t uMode, bool fFollowSymlinks);
     79    /** @}  */
     80
     81    /** @name File handling primitives.
     82     * @{ */
     83    int fileCopyToGuestEx(const Utf8Str &strSource, const Utf8Str &strDest, FileCopyFlag_T enmFileCopyFlags, PRTFILE pFile, uint64_t cbOffset, uint64_t cbSize);
     84    int fileCopyToGuest(const Utf8Str &strSource, const Utf8Str &strDest, FileCopyFlag_T enmFileCopyFlags);
     85    /** @}  */
     86
     87    /** @name Guest property handling primitives.
     88     * @{ */
     89    int getGuestProperty(const ComObjPtr<Guest> &pGuest, const Utf8Str &strPath, Utf8Str &strValue);
     90    /** @}  */
     91
     92    /** @name Path handling primitives.
     93     * @{ */
     94    int pathConstructOnGuest(const Utf8Str &strSourceRoot, const Utf8Str &strSource, const Utf8Str &strDest, Utf8Str &strOut);
     95    /** @}  */
     96
    7897    int setProgress(ULONG uPercent);
    7998    int setProgressSuccess(void);
    8099    HRESULT setProgressErrorMsg(HRESULT hr, const Utf8Str &strMsg);
     100
    81101    inline void setTaskDesc(const Utf8Str &strTaskDesc) throw()
    82102    {
     
    85105
    86106    HRESULT createAndSetProgressObject();
     107
    87108protected:
    88109
    89110    Utf8Str                 mDesc;
    90     GuestSession           *mSession;
     111    /** The guest session object this task is working on. */
     112    ComObjPtr<GuestSession> mSession;
    91113    /** Progress object for getting updated when running
    92114     *  asynchronously. Optional. */
     
    116138
    117139/**
     140 * Task for copying directories from guest to the host.
     141 */
     142class SessionTaskCopyDirFrom : public GuestSessionTask
     143{
     144public:
     145
     146    SessionTaskCopyDirFrom(GuestSession *pSession,
     147                           const Utf8Str &strSource, const Utf8Str &strDest, const Utf8Str &strFilter, DirectoryCopyFlags_T enmDirCopyFlags);
     148    virtual ~SessionTaskCopyDirFrom(void);
     149    int Run(void);
     150
     151protected:
     152
     153    int directoryCopyToHost(const Utf8Str &strSource, const Utf8Str &strFilter, const Utf8Str &strDest, bool fRecursive, bool fFollowSymlinks,
     154                            const Utf8Str &strSubDir /* For recursion. */);
     155protected:
     156
     157    Utf8Str              mSource;
     158    Utf8Str              mDest;
     159    Utf8Str              mFilter;
     160    DirectoryCopyFlags_T mDirCopyFlags;
     161};
     162
     163/**
     164 * Task for copying directories from host to the guest.
     165 */
     166class SessionTaskCopyDirTo : public GuestSessionTask
     167{
     168public:
     169
     170    SessionTaskCopyDirTo(GuestSession *pSession,
     171                         const Utf8Str &strSource, const Utf8Str &strDest, const Utf8Str &strFilter, DirectoryCopyFlags_T enmDirCopyFlags);
     172    virtual ~SessionTaskCopyDirTo(void);
     173    int Run(void);
     174
     175protected:
     176
     177    int directoryCopyToGuest(const Utf8Str &strSource, const Utf8Str &strFilter, const Utf8Str &strDest, bool fRecursive, bool fFollowSymlinks,
     178                             const Utf8Str &strSubDir /* For recursion. */);
     179protected:
     180
     181    Utf8Str              mSource;
     182    Utf8Str              mDest;
     183    Utf8Str              mFilter;
     184    DirectoryCopyFlags_T mDirCopyFlags;
     185};
     186
     187/**
    118188 * Task for copying files from host to the guest.
    119189 */
    120 class SessionTaskCopyTo : public GuestSessionTask
    121 {
    122 public:
    123 
    124     SessionTaskCopyTo(GuestSession *pSession,
    125                       const Utf8Str &strSource, const Utf8Str &strDest, uint32_t uFlags);
    126     SessionTaskCopyTo(GuestSession *pSession,
    127                       PRTFILE pSourceFile, size_t cbSourceOffset, uint64_t cbSourceSize,
    128                       const Utf8Str &strDest, uint32_t uFlags);
    129     virtual ~SessionTaskCopyTo(void);
     190class SessionTaskCopyFileTo : public GuestSessionTask
     191{
     192public:
     193
     194    SessionTaskCopyFileTo(GuestSession *pSession,
     195                          const Utf8Str &strSource, const Utf8Str &strDest, uint32_t uFlags);
     196    SessionTaskCopyFileTo(GuestSession *pSession,
     197                          PRTFILE pSourceFile, size_t cbSourceOffset, uint64_t cbSourceSize,
     198                          const Utf8Str &strDest, uint32_t uFlags);
     199    virtual ~SessionTaskCopyFileTo(void);
    130200    int Run(void);
    131201
     
    143213 * Task for copying files from guest to the host.
    144214 */
    145 class SessionTaskCopyFrom : public GuestSessionTask
    146 {
    147 public:
    148 
    149     SessionTaskCopyFrom(GuestSession *pSession,
     215class SessionTaskCopyFileFrom : public GuestSessionTask
     216{
     217public:
     218
     219    SessionTaskCopyFileFrom(GuestSession *pSession,
    150220                        const Utf8Str &strSource, const Utf8Str &strDest, uint32_t uFlags);
    151     virtual ~SessionTaskCopyFrom(void);
     221    virtual ~SessionTaskCopyFileFrom(void);
    152222    int Run(void);
    153223
     
    223293    };
    224294
    225     int i_addProcessArguments(ProcessArguments &aArgumentsDest,
    226                               const ProcessArguments &aArgumentsSource);
    227     int i_copyFileToGuest(GuestSession *pSession, PRTISOFSFILE pISO,
    228                           Utf8Str const &strFileSource, const Utf8Str &strFileDest,
    229                           bool fOptional, uint32_t *pcbSize);
    230     int i_runFileOnGuest(GuestSession *pSession, GuestProcessStartupInfo &procInfo);
     295    int addProcessArguments(ProcessArguments &aArgumentsDest, const ProcessArguments &aArgumentsSource);
     296    int copyFileToGuest(GuestSession *pSession, PRTISOFSFILE pISO, Utf8Str const &strFileSource, const Utf8Str &strFileDest, bool fOptional, uint32_t *pcbSize);
     297    int runFileOnGuest(GuestSession *pSession, GuestProcessStartupInfo &procInfo);
    231298
    232299    /** Files to handle. */
     
    423490     * @todo r=bird: Most of these are public for no real reason...
    424491     * @{ */
     492    int                     i_copyToGuestCreateDir(const com::Utf8Str &aDestination, uint32_t fFlags, int *pGuestRc);
    425493    int                     i_closeSession(uint32_t uFlags, uint32_t uTimeoutMS, int *pGuestRc);
    426494    inline bool             i_directoryExists(uint32_t uDirID, ComObjPtr<GuestDirectory> *pDir);
  • trunk/src/VBox/Main/src-client/GuestSessionImpl.cpp

    r69500 r71213  
    55
    66/*
    7  * Copyright (C) 2012-2017 Oracle Corporation
     7 * Copyright (C) 2012-2018 Oracle Corporation
    88 *
    99 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    2020*   Header Files                                                                                                                 *
    2121*********************************************************************************************************************************/
    22 #define LOG_GROUP LOG_GROUP_GUEST_CONTROL //LOG_GROUP_MAIN_GUESTSESSION
     22#define LOG_GROUP LOG_GROUP_MAIN_GUESTSESSION
    2323#include "LoggingNew.h"
    2424
     
    24572457            fFlags |= aFlags[i];
    24582458    }
    2459 /** @todo r=bird: fend off flags we don't implement here!  */
     2459
     2460    if (fFlags)
     2461        return setError(E_NOTIMPL, tr("Flag(s) not yet implemented"));
    24602462
    24612463    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     
    24652467    try
    24662468    {
    2467         SessionTaskCopyFrom *pTask = NULL;
     2469        SessionTaskCopyFileFrom *pTask = NULL;
    24682470        ComObjPtr<Progress> pProgress;
    24692471        try
    24702472        {
    2471             pTask = new SessionTaskCopyFrom(this /* GuestSession */, aSource, aDest, fFlags);
     2473            pTask = new SessionTaskCopyFileFrom(this /* GuestSession */, aSource, aDest, fFlags);
    24722474        }
    24732475        catch(...)
    24742476        {
    2475             hr = setError(VBOX_E_IPRT_ERROR, tr("Failed to create SessionTaskCopyFrom object "));
     2477            hr = setError(VBOX_E_IPRT_ERROR, tr("Failed to create SessionTaskCopyFileFrom object"));
    24762478            throw;
    24772479        }
     
    24832485            delete pTask;
    24842486            hr = setError(VBOX_E_IPRT_ERROR,
    2485                           tr("Creating progress object for SessionTaskCopyFrom object failed"));
     2487                          tr("Creating progress object for SessionTaskCopyFileFrom object failed"));
    24862488            throw hr;
    24872489        }
     
    25302532            fFlags |= aFlags[i];
    25312533    }
    2532 /** @todo r=bird: fend off flags we don't implement here!  */
     2534
     2535    if (fFlags)
     2536        return setError(E_NOTIMPL, tr("Flag(s) not yet implemented"));
    25332537
    25342538    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     
    25382542    try
    25392543    {
    2540         SessionTaskCopyTo *pTask = NULL;
     2544        SessionTaskCopyFileTo *pTask = NULL;
    25412545        ComObjPtr<Progress> pProgress;
    25422546        try
    25432547        {
    2544             pTask = new SessionTaskCopyTo(this /* GuestSession */, aSource, aDest, fFlags);
     2548            pTask = new SessionTaskCopyFileTo(this /* GuestSession */, aSource, aDest, fFlags);
    25452549        }
    25462550        catch(...)
    25472551        {
    2548             hr = setError(VBOX_E_IPRT_ERROR, tr("Failed to create SessionTaskCopyTo object "));
     2552            hr = setError(VBOX_E_IPRT_ERROR, tr("Failed to create SessionTaskCopyFileTo object"));
    25492553            throw;
    25502554        }
    2551 
    25522555
    25532556        hr = pTask->Init(Utf8StrFmt(tr("Copying \"%s\" from host to \"%s\" on the guest"), aSource.c_str(), aDest.c_str()));
     
    25562559            delete pTask;
    25572560            hr = setError(VBOX_E_IPRT_ERROR,
    2558                           tr("Creating progress object for SessionTaskCopyTo object failed"));
     2561                          tr("Creating progress object for SessionTaskCopyFileTo object failed"));
    25592562            throw hr;
    25602563        }
     
    26032606                                           const std::vector<DirectoryCopyFlags_T> &aFlags, ComPtr<IProgress> &aProgress)
    26042607{
    2605     RT_NOREF(aSource, aDestination, aFlags, aProgress);
    2606     ReturnComNotImplemented();
     2608    LogFlowThisFuncEnter();
     2609
     2610    if (RT_UNLIKELY((aSource.c_str()) == NULL || *(aSource.c_str()) == '\0'))
     2611        return setError(E_INVALIDARG, tr("No source directory specified"));
     2612
     2613    if (RT_UNLIKELY((aDestination.c_str()) == NULL || *(aDestination.c_str()) == '\0'))
     2614        return setError(E_INVALIDARG, tr("No destination directory specified"));
     2615
     2616    if (!RTPathExists(aSource.c_str()))
     2617        return setError(E_INVALIDARG, tr("Source directory \"%s\" does not exist"), aSource.c_str());
     2618
     2619    uint32_t fFlags = DirectoryCopyFlags_None;
     2620    if (aFlags.size())
     2621    {
     2622        for (size_t i = 0; i < aFlags.size(); i++)
     2623            fFlags |= aFlags[i];
     2624    }
     2625    /** @todo Validate flags. */
     2626
     2627    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     2628
     2629    HRESULT hr = S_OK;
     2630
     2631    try
     2632    {
     2633        SessionTaskCopyDirTo *pTask = NULL;
     2634        ComObjPtr<Progress> pProgress;
     2635        try
     2636        {
     2637            pTask = new SessionTaskCopyDirTo(this /* GuestSession */, aSource, aDestination, "" /* strFilter */, fFlags);
     2638        }
     2639        catch(...)
     2640        {
     2641            hr = setError(VBOX_E_IPRT_ERROR, tr("Failed to create SessionTaskCopyDirTo object"));
     2642            throw;
     2643        }
     2644
     2645        hr = pTask->Init(Utf8StrFmt(tr("Copying directory \"%s\" from host to \"%s\" on the guest"),
     2646                                    aSource.c_str(), aDestination.c_str()));
     2647        if (FAILED(hr))
     2648        {
     2649            delete pTask;
     2650            hr = setError(VBOX_E_IPRT_ERROR,
     2651                          tr("Creating progress object for SessionTaskCopyDirTo object failed"));
     2652            throw hr;
     2653        }
     2654
     2655        hr = pTask->createThreadWithType(RTTHREADTYPE_MAIN_HEAVY_WORKER);
     2656
     2657        if (SUCCEEDED(hr))
     2658        {
     2659            /* Return progress to the caller. */
     2660            pProgress = pTask->GetProgressObject();
     2661            hr = pProgress.queryInterfaceTo(aProgress.asOutParam());
     2662        }
     2663        else
     2664            hr = setError(VBOX_E_IPRT_ERROR,
     2665                          tr("Starting thread for copying directory \"%s\" from host to \"%s\" on the guest failed"),
     2666                          aSource.c_str(), aDestination.c_str());
     2667    }
     2668    catch(std::bad_alloc &)
     2669    {
     2670        hr = E_OUTOFMEMORY;
     2671    }
     2672    catch(HRESULT eHR)
     2673    {
     2674        hr = eHR;
     2675        LogFlowThisFunc(("Exception was caught in the function\n"));
     2676    }
     2677
     2678    return hr;
    26072679}
    26082680
     
    27062778    else
    27072779    {
    2708         /** @todo r=bird: Looks like this code raises errors if the directory doesn't
    2709          *        exist... That's of course not right. */
    27102780        switch (rc)
    27112781        {
    27122782            case VERR_GSTCTL_GUEST_ERROR:
    2713                 hr = GuestProcess::i_setErrorExternal(this, guestRc);
     2783            {
     2784                switch (guestRc)
     2785                {
     2786                    case VERR_PATH_NOT_FOUND:
     2787                        *aExists = FALSE;
     2788                        break;
     2789                    default:
     2790                        hr = setError(VBOX_E_IPRT_ERROR, tr("Querying directory existence \"%s\" failed: %s"),
     2791                                      aPath.c_str(), GuestProcess::i_guestErrorToString(guestRc).c_str());
     2792                        break;
     2793                }
    27142794                break;
     2795            }
    27152796
    27162797            default:
  • trunk/src/VBox/Main/src-client/GuestSessionImplTasks.cpp

    r69500 r71213  
    55
    66/*
    7  * Copyright (C) 2012-2017 Oracle Corporation
     7 * Copyright (C) 2012-2018 Oracle Corporation
    88 *
    99 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    2020*   Header Files                                                                                                                 *
    2121*********************************************************************************************************************************/
    22 #define LOG_GROUP LOG_GROUP_GUEST_CONTROL //LOG_GROUP_MAIN_GUESTSESSION
     22#define LOG_GROUP LOG_GROUP_MAIN_GUESTSESSION
    2323#include "LoggingNew.h"
    2424
     
    199199}
    200200
    201 SessionTaskOpen::SessionTaskOpen(GuestSession *pSession,
    202                                  uint32_t uFlags,
    203                                  uint32_t uTimeoutMS)
    204                                  : GuestSessionTask(pSession),
    205                                    mFlags(uFlags),
    206                                    mTimeoutMS(uTimeoutMS)
    207 {
    208     m_strTaskName = "gctlSesOpen";
    209 }
    210 
    211 SessionTaskOpen::~SessionTaskOpen(void)
    212 {
    213 
    214 }
    215 
    216 int SessionTaskOpen::Run(void)
    217 {
    218     LogFlowThisFuncEnter();
    219 
    220     ComObjPtr<GuestSession> pSession = mSession;
    221     Assert(!pSession.isNull());
    222 
    223     AutoCaller autoCaller(pSession);
    224     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    225 
    226     int vrc = pSession->i_startSessionInternal(NULL /*pvrcGuest*/);
    227     /* Nothing to do here anymore. */
    228 
    229     LogFlowFuncLeaveRC(vrc);
    230     return vrc;
    231 }
    232 
    233 SessionTaskCopyTo::SessionTaskCopyTo(GuestSession *pSession,
    234                                      const Utf8Str &strSource, const Utf8Str &strDest, uint32_t uFlags)
    235                                      : GuestSessionTask(pSession),
    236                                        mSource(strSource),
    237                                        mSourceFile(NULL),
    238                                        mSourceOffset(0),
    239                                        mSourceSize(0),
    240                                        mDest(strDest)
    241 {
    242     mCopyFileFlags = uFlags;
    243     m_strTaskName = "gctlCpyTo";
    244 }
    245 
    246 /** @todo Merge this and the above call and let the above call do the open/close file handling so that the
    247  *        inner code only has to deal with file handles. No time now ... */
    248 SessionTaskCopyTo::SessionTaskCopyTo(GuestSession *pSession,
    249                                      PRTFILE pSourceFile, size_t cbSourceOffset, uint64_t cbSourceSize,
    250                                      const Utf8Str &strDest, uint32_t uFlags)
    251                                      : GuestSessionTask(pSession)
    252 {
    253     mSourceFile    = pSourceFile;
    254     mSourceOffset  = cbSourceOffset;
    255     mSourceSize    = cbSourceSize;
    256     mDest          = strDest;
    257     mCopyFileFlags = uFlags;
    258     m_strTaskName = "gctlCpyTo";
    259 }
    260 
    261 SessionTaskCopyTo::~SessionTaskCopyTo(void)
    262 {
    263 
    264 }
    265 
    266 int SessionTaskCopyTo::Run(void)
    267 {
    268     LogFlowThisFuncEnter();
    269 
    270     ComObjPtr<GuestSession> pSession = mSession;
    271     Assert(!pSession.isNull());
    272 
    273     AutoCaller autoCaller(pSession);
    274     if (FAILED(autoCaller.rc())) return autoCaller.rc();
    275 
    276     if (mCopyFileFlags)
    277     {
    278         setProgressErrorMsg(VBOX_E_IPRT_ERROR,
    279                             Utf8StrFmt(GuestSession::tr("Copy flags (%#x) not implemented yet"),
    280                             mCopyFileFlags));
    281         return VERR_INVALID_PARAMETER;
    282     }
    283 
    284     int rc;
     201/**
     202 * Creates a directory on the guest.
     203 *
     204 * @return VBox status code. VWRN_ALREADY_EXISTS if directory on the guest already exists.
     205 * @param  strPath                  Absolute path to directory on the guest (guest style path) to create.
     206 * @param  enmDirecotryCreateFlags  Directory creation flags.
     207 * @param  uMode                    Directory mode to use for creation.
     208 * @param  fFollowSymlinks          Whether to follow symlinks on the guest or not.
     209 */
     210int GuestSessionTask::directoryCreate(const com::Utf8Str &strPath,
     211                                      DirectoryCreateFlag_T enmDirecotryCreateFlags, uint32_t uMode, bool fFollowSymlinks)
     212{
     213    LogFlowFunc(("strPath=%s, fFlags=0x%x, uMode=%RU32, fFollowSymlinks=%RTbool\n",
     214                 strPath.c_str(), enmDirecotryCreateFlags, uMode, fFollowSymlinks));
     215
     216    GuestFsObjData objData; int guestRc;
     217
     218    int rc = mSession->i_directoryQueryInfoInternal(strPath, fFollowSymlinks, objData, &guestRc);
     219    if (RT_SUCCESS(rc))
     220    {
     221        return VWRN_ALREADY_EXISTS;
     222    }
     223    else
     224    {
     225        switch (rc)
     226        {
     227            case VERR_GSTCTL_GUEST_ERROR:
     228            {
     229                switch (guestRc)
     230                {
     231                    case VERR_FILE_NOT_FOUND:
     232                    case VERR_PATH_NOT_FOUND:
     233                        rc = mSession->i_directoryCreateInternal(strPath.c_str(), uMode, enmDirecotryCreateFlags, &guestRc);
     234                        break;
     235                    default:
     236                        break;
     237                }
     238                break;
     239            }
     240
     241            default:
     242                break;
     243        }
     244    }
     245
     246    if (RT_FAILURE(rc))
     247    {
     248        if (rc == VERR_GSTCTL_GUEST_ERROR)
     249        {
     250            setProgressErrorMsg(VBOX_E_IPRT_ERROR, GuestProcess::i_guestErrorToString(guestRc));
     251        }
     252        else
     253            setProgressErrorMsg(VBOX_E_IPRT_ERROR,
     254                                Utf8StrFmt(GuestSession::tr("Error creating directory on the guest: %Rrc"), strPath.c_str(), rc));
     255    }
     256
     257    LogFlowFuncLeaveRC(rc);
     258    return rc;
     259}
     260
     261/**
     262 * Copies a file from the host to the guest, extended version.
     263 *
     264 * @return VBox status code.
     265 * @param  strSource            Full path of source file on the host to copy.
     266 * @param  strDest              Full destination path and file name (guest style) to copy file to.
     267 * @param  enmFileCopyFlags     File copy flags.
     268 * @param  pFile                Source file handle to use for accessing the host file.
     269 *                              The caller is responsible of opening / closing the file accordingly.
     270 * @param  cbOffset             Offset (in bytes) where to start copying the source file.
     271 * @param  cbSize               Size (in bytes) to copy from the source file.
     272 */
     273int GuestSessionTask::fileCopyToGuestEx(const Utf8Str &strSource, const Utf8Str &strDest, FileCopyFlag_T enmFileCopyFlags,
     274                                        PRTFILE pFile, uint64_t cbOffset, uint64_t cbSize)
     275{
     276    /** @todo Implement sparse file support? */
     277
     278    if (   enmFileCopyFlags & FileCopyFlag_NoReplace
     279        || enmFileCopyFlags & FileCopyFlag_FollowLinks
     280        || enmFileCopyFlags & FileCopyFlag_Update)
     281    {
     282        return VERR_NOT_IMPLEMENTED;
     283    }
    285284
    286285    RTMSINTERVAL msTimeout = 30 * 1000; /** @todo 30s timeout for all actions. Make this configurable? */
    287 
    288     RTFILE fileLocal;
    289     PRTFILE pFile = &fileLocal;
    290 
    291     if (!mSourceFile)
    292     {
    293         /* Does our source file exist? */
    294         if (!RTFileExists(mSource.c_str()))
    295         {
    296             rc = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
    297                                      Utf8StrFmt(GuestSession::tr("Source file \"%s\" does not exist or is not a file"),
    298                                                 mSource.c_str()));
    299         }
    300         else
    301         {
    302             rc = RTFileOpen(pFile, mSource.c_str(),
    303                             RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE);
    304             if (RT_FAILURE(rc))
    305             {
    306                 rc = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
    307                                          Utf8StrFmt(GuestSession::tr("Could not open source file \"%s\" for reading: %Rrc"),
    308                                                     mSource.c_str(), rc));
    309             }
    310             else
    311             {
    312                 rc = RTFileGetSize(*pFile, &mSourceSize);
    313                 if (RT_FAILURE(rc))
    314                 {
    315                     setProgressErrorMsg(VBOX_E_IPRT_ERROR,
    316                                         Utf8StrFmt(GuestSession::tr("Could not query file size of \"%s\": %Rrc"),
    317                                                    mSource.c_str(), rc));
    318                 }
    319             }
    320         }
    321     }
    322     else
    323     {
    324         rc = VINF_SUCCESS;
    325         pFile = mSourceFile;
    326         /* Size + offset are optional. */
    327     }
    328 
    329     /*
    330      * Query information about our destination first.
    331      */
    332     int guestRc = VERR_IPE_UNINITIALIZED_STATUS;
    333     if (RT_SUCCESS(rc))
    334     {
    335         GuestFsObjData objData;
    336         rc = pSession->i_directoryQueryInfoInternal(mDest, true /* fFollowSymlinks */, objData, &guestRc);
    337         if (RT_SUCCESS(rc))
    338         {
    339             mDest = Utf8StrFmt("%s/%s", mDest.c_str(), RTPathFilename(mSource.c_str()));
    340         }
    341         else if (rc == VERR_NOT_A_DIRECTORY)
    342         {
    343             rc = VINF_SUCCESS;
    344         }
    345     }
    346 
    347     /** @todo Implement sparse file support? */
    348286
    349287    /*
     
    356294
    357295    /* Set arguments.*/
    358     procInfo.mArguments.push_back(procInfo.mExecutable);                     /* Set argv0. */
    359     procInfo.mArguments.push_back(Utf8StrFmt("--output=%s", mDest.c_str())); /** @todo Do we need path conversion? */
     296    procInfo.mArguments.push_back(procInfo.mExecutable);                       /* Set argv0. */
     297    procInfo.mArguments.push_back(Utf8StrFmt("--output=%s", strDest.c_str()));
    360298
    361299    /* Startup process. */
    362300    ComObjPtr<GuestProcess> pProcess;
    363     if (RT_SUCCESS(rc))
    364         rc = pSession->i_processCreateExInternal(procInfo, pProcess);
     301    int rc = mSession->i_processCreateExInternal(procInfo, pProcess);
     302
     303    int guestRc;
    365304    if (RT_SUCCESS(rc))
    366305    {
     
    382321                                    Utf8StrFmt(GuestSession::tr(
    383322                                    "Error while creating guest process for copying file \"%s\" from guest to host: %Rrc"),
    384                                     mSource.c_str(), rc));
     323                                    strSource.c_str(), rc));
    385324                break;
    386325        }
    387326    }
    388327
     328    if (RT_FAILURE(rc))
     329        return rc;
     330
     331    ProcessWaitResult_T waitRes;
     332    BYTE byBuf[_64K];
     333
     334    BOOL fCanceled = FALSE;
     335    uint64_t cbWrittenTotal = 0;
     336    uint64_t cbToRead = cbSize;
     337
     338    for (;;)
     339    {
     340        rc = pProcess->i_waitFor(ProcessWaitForFlag_StdIn, msTimeout, waitRes, &guestRc);
     341        if (   RT_FAILURE(rc)
     342            || (   waitRes != ProcessWaitResult_StdIn
     343                && waitRes != ProcessWaitResult_WaitFlagNotSupported))
     344        {
     345            break;
     346        }
     347
     348        /* If the guest does not support waiting for stdin, we now yield in
     349         * order to reduce the CPU load due to busy waiting. */
     350        if (waitRes == ProcessWaitResult_WaitFlagNotSupported)
     351            RTThreadYield(); /* Optional, don't check rc. */
     352
     353        size_t cbRead = 0;
     354        if (cbSize) /* If we have nothing to write, take a shortcut. */
     355        {
     356            /** @todo Not very efficient, but works for now. */
     357            rc = RTFileSeek(*pFile, cbOffset + cbWrittenTotal,
     358                            RTFILE_SEEK_BEGIN, NULL /* poffActual */);
     359            if (RT_SUCCESS(rc))
     360            {
     361                rc = RTFileRead(*pFile, (uint8_t*)byBuf,
     362                                RT_MIN((size_t)cbToRead, sizeof(byBuf)), &cbRead);
     363                /*
     364                 * Some other error occured? There might be a chance that RTFileRead
     365                 * could not resolve/map the native error code to an IPRT code, so just
     366                 * print a generic error.
     367                 */
     368                if (RT_FAILURE(rc))
     369                {
     370                    setProgressErrorMsg(VBOX_E_IPRT_ERROR,
     371                                        Utf8StrFmt(GuestSession::tr("Could not read from host file \"%s\" (%Rrc)"),
     372                                                   strSource.c_str(), rc));
     373                    break;
     374                }
     375            }
     376            else
     377            {
     378                setProgressErrorMsg(VBOX_E_IPRT_ERROR,
     379                                    Utf8StrFmt(GuestSession::tr("Seeking host file \"%s\" to offset %RU64 failed: %Rrc"),
     380                                               strSource.c_str(), cbWrittenTotal, rc));
     381                break;
     382            }
     383        }
     384
     385        uint32_t fFlags = ProcessInputFlag_None;
     386
     387        /* Did we reach the end of the content we want to transfer (last chunk)? */
     388        if (   (cbRead < sizeof(byBuf))
     389            /* Did we reach the last block which is exactly _64K? */
     390            || (cbToRead - cbRead == 0)
     391            /* ... or does the user want to cancel? */
     392            || (   !mProgress.isNull()
     393                && SUCCEEDED(mProgress->COMGETTER(Canceled(&fCanceled)))
     394                && fCanceled)
     395           )
     396        {
     397            LogFlowThisFunc(("Writing last chunk cbRead=%RU64\n", cbRead));
     398            fFlags |= ProcessInputFlag_EndOfFile;
     399        }
     400
     401        uint32_t cbWritten;
     402        Assert(sizeof(byBuf) >= cbRead);
     403        rc = pProcess->i_writeData(0 /* StdIn */, fFlags,
     404                                   byBuf, cbRead,
     405                                   msTimeout, &cbWritten, &guestRc);
     406        if (RT_FAILURE(rc))
     407        {
     408            switch (rc)
     409            {
     410                case VERR_GSTCTL_GUEST_ERROR:
     411                    setProgressErrorMsg(VBOX_E_IPRT_ERROR,
     412                                        GuestProcess::i_guestErrorToString(guestRc));
     413                    break;
     414
     415                default:
     416                    setProgressErrorMsg(VBOX_E_IPRT_ERROR,
     417                                        Utf8StrFmt(GuestSession::tr("Writing to guest file \"%s\" (offset %RU64) failed: %Rrc"),
     418                                        strDest.c_str(), cbWrittenTotal, rc));
     419                    break;
     420            }
     421
     422            break;
     423        }
     424
     425        /* Only subtract bytes reported written by the guest. */
     426        Assert(cbToRead >= cbWritten);
     427        cbToRead -= cbWritten;
     428
     429        /* Update total bytes written to the guest. */
     430        cbWrittenTotal += cbWritten;
     431        Assert(cbWrittenTotal <= cbSize);
     432
     433        LogFlowThisFunc(("rc=%Rrc, cbWritten=%RU32, cbToRead=%RU64, cbWrittenTotal=%RU64, cbFileSize=%RU64\n",
     434                         rc, cbWritten, cbToRead, cbWrittenTotal, cbSize));
     435
     436        /* Did the user cancel the operation above? */
     437        if (fCanceled)
     438            break;
     439
     440        /* Update the progress.
     441         * Watch out for division by zero. */
     442        cbSize > 0
     443            ? rc = setProgress((ULONG)(cbWrittenTotal * 100 / cbSize))
     444            : rc = setProgress(100);
     445        if (RT_FAILURE(rc))
     446            break;
     447
     448        /* End of file reached? */
     449        if (!cbToRead)
     450            break;
     451    } /* for */
     452
     453    LogFlowThisFunc(("Copy loop ended with rc=%Rrc, cbToRead=%RU64, cbWrittenTotal=%RU64, cbFileSize=%RU64\n",
     454                     rc, cbToRead, cbWrittenTotal, cbSize));
     455
     456    /*
     457     * Wait on termination of guest process until it completed all operations.
     458     */
     459    if (   !fCanceled
     460        || RT_SUCCESS(rc))
     461    {
     462        rc = pProcess->i_waitFor(ProcessWaitForFlag_Terminate, msTimeout, waitRes, &guestRc);
     463        if (   RT_FAILURE(rc)
     464            || waitRes != ProcessWaitResult_Terminate)
     465        {
     466            if (RT_FAILURE(rc))
     467                setProgressErrorMsg(VBOX_E_IPRT_ERROR,
     468                                    Utf8StrFmt(
     469                                    GuestSession::tr("Waiting on termination for copying file \"%s\" to guest failed: %Rrc"),
     470                                    strSource.c_str(), rc));
     471            else
     472            {
     473                setProgressErrorMsg(VBOX_E_IPRT_ERROR,
     474                                    Utf8StrFmt(GuestSession::tr(
     475                                               "Waiting on termination for copying file \"%s\" to guest failed with wait result %ld"),
     476                                               strSource.c_str(), waitRes));
     477                rc = VERR_GENERAL_FAILURE; /* Fudge. */
     478            }
     479        }
     480    }
     481
    389482    if (RT_SUCCESS(rc))
    390483    {
    391         ProcessWaitResult_T waitRes;
    392         BYTE byBuf[_64K];
    393 
    394         BOOL fCanceled = FALSE;
    395         uint64_t cbWrittenTotal = 0;
    396         uint64_t cbToRead = mSourceSize;
    397 
    398         for (;;)
    399         {
    400             rc = pProcess->i_waitFor(ProcessWaitForFlag_StdIn, msTimeout, waitRes, &guestRc);
    401             if (   RT_FAILURE(rc)
    402                 || (   waitRes != ProcessWaitResult_StdIn
    403                     && waitRes != ProcessWaitResult_WaitFlagNotSupported))
    404             {
     484        /*
     485         * Newer VBoxService toolbox versions report what went wrong via exit code.
     486         * So handle this first.
     487         */
     488        /** @todo This code sequence is duplicated in CopyFrom...   */
     489        ProcessStatus_T procStatus = ProcessStatus_TerminatedAbnormally;
     490        HRESULT hrc = pProcess->COMGETTER(Status(&procStatus));
     491        if (!SUCCEEDED(hrc))
     492            procStatus = ProcessStatus_TerminatedAbnormally;
     493
     494        LONG exitCode = 42424242;
     495        hrc = pProcess->COMGETTER(ExitCode(&exitCode));
     496        if (!SUCCEEDED(hrc))
     497            exitCode = 42424242;
     498
     499        if (   procStatus != ProcessStatus_TerminatedNormally
     500            || exitCode != 0)
     501        {
     502            LogFlowThisFunc(("procStatus=%d, exitCode=%d\n", procStatus, exitCode));
     503            if (procStatus == ProcessStatus_TerminatedNormally)
     504                rc = GuestProcessTool::i_exitCodeToRc(procInfo, exitCode);
     505            else
     506                rc = VERR_GENERAL_FAILURE;
     507            setProgressErrorMsg(VBOX_E_IPRT_ERROR,
     508                                Utf8StrFmt(GuestSession::tr("Copying file \"%s\" to guest failed: %Rrc"),
     509                                           strSource.c_str(), rc));
     510        }
     511        /*
     512         * Even if we succeeded until here make sure to check whether we really transfered
     513         * everything.
     514         */
     515        else if (   cbSize > 0
     516                 && cbWrittenTotal == 0)
     517        {
     518            /* If nothing was transfered but the file size was > 0 then "vbox_cat" wasn't able to write
     519             * to the destination -> access denied. */
     520            setProgressErrorMsg(VBOX_E_IPRT_ERROR,
     521                                Utf8StrFmt(GuestSession::tr("Access denied when copying file \"%s\" to guest \"%s\""),
     522                                           strSource.c_str(), strDest.c_str()));
     523            rc = VERR_ACCESS_DENIED;
     524        }
     525        else if (cbWrittenTotal < cbSize)
     526        {
     527            /* If we did not copy all let the user know. */
     528            setProgressErrorMsg(VBOX_E_IPRT_ERROR,
     529                                Utf8StrFmt(GuestSession::tr("Copying file \"%s\" to guest failed (%RU64/%RU64 bytes transfered)"),
     530                                           strSource.c_str(), cbWrittenTotal, cbSize));
     531            rc = VERR_INTERRUPTED;
     532        }
     533    }
     534
     535    return rc;
     536}
     537
     538/**
     539 * Copies a file from the host to the guest.
     540 *
     541 * @return VBox status code.
     542 * @param  strSource            Full path of source file on the host to copy.
     543 * @param  strDest              Full destination path and file name (guest style) to copy file to.
     544 * @param  enmFileCopyFlags     File copy flags.
     545 */
     546int GuestSessionTask::fileCopyToGuest(const Utf8Str &strSource, const Utf8Str &strDest, FileCopyFlag_T enmFileCopyFlags)
     547{
     548    int rc;
     549
     550    /* Does our source file exist? */
     551    if (!RTFileExists(strSource.c_str()))
     552    {
     553        setProgressErrorMsg(VBOX_E_IPRT_ERROR,
     554                            Utf8StrFmt(GuestSession::tr("Host file \"%s\" does not exist or is not a file"),
     555                                       strSource.c_str()));
     556        rc  = VERR_FILE_NOT_FOUND;
     557    }
     558    else
     559    {
     560        RTFILE hFile;
     561        rc = RTFileOpen(&hFile, strSource.c_str(),
     562                        RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE);
     563        if (RT_FAILURE(rc))
     564        {
     565            setProgressErrorMsg(VBOX_E_IPRT_ERROR,
     566                                Utf8StrFmt(GuestSession::tr("Could not open host file \"%s\" for reading: %Rrc"),
     567                                           strSource.c_str(), rc));
     568        }
     569        else
     570        {
     571            uint64_t cbSize;
     572            rc = RTFileGetSize(hFile, &cbSize);
     573            if (RT_FAILURE(rc))
     574            {
     575                setProgressErrorMsg(VBOX_E_IPRT_ERROR,
     576                                    Utf8StrFmt(GuestSession::tr("Could not query host file size of \"%s\": %Rrc"),
     577                                               strSource.c_str(), rc));
     578            }
     579            else
     580                rc = fileCopyToGuestEx(strSource, strDest, enmFileCopyFlags, &hFile, 0 /* Offset */, cbSize);
     581
     582             RTFileClose(hFile);
     583        }
     584    }
     585
     586    return rc;
     587}
     588
     589/**
     590 * Translates a source path to a destination path (can be both sides,
     591 * either host or guest). The source root is needed to determine the start
     592 * of the relative source path which also needs to present in the destination
     593 * path.
     594 *
     595 * @return  IPRT status code.
     596 * @param   strSourceRoot           Source root path. No trailing directory slash!
     597 * @param   strSource               Actual source to transform. Must begin with
     598 *                                  the source root path!
     599 * @param   strDest                 Destination path.
     600 * @param   strOut                  where to store the output path.
     601 */
     602int GuestSessionTask::pathConstructOnGuest(const Utf8Str &strSourceRoot, const Utf8Str &strSource,
     603                                           const Utf8Str &strDest, Utf8Str &strOut)
     604{
     605    RT_NOREF(strSourceRoot, strSource, strDest, strOut);
     606    return VINF_SUCCESS;
     607}
     608
     609SessionTaskOpen::SessionTaskOpen(GuestSession *pSession,
     610                                 uint32_t uFlags,
     611                                 uint32_t uTimeoutMS)
     612                                 : GuestSessionTask(pSession),
     613                                   mFlags(uFlags),
     614                                   mTimeoutMS(uTimeoutMS)
     615{
     616    m_strTaskName = "gctlSesOpen";
     617}
     618
     619SessionTaskOpen::~SessionTaskOpen(void)
     620{
     621
     622}
     623
     624int SessionTaskOpen::Run(void)
     625{
     626    LogFlowThisFuncEnter();
     627
     628    AutoCaller autoCaller(mSession);
     629    if (FAILED(autoCaller.rc())) return autoCaller.rc();
     630
     631    int vrc = mSession->i_startSessionInternal(NULL /*pvrcGuest*/);
     632    /* Nothing to do here anymore. */
     633
     634    LogFlowFuncLeaveRC(vrc);
     635    return vrc;
     636}
     637
     638SessionTaskCopyDirTo::SessionTaskCopyDirTo(GuestSession *pSession,
     639                                           const Utf8Str &strSource, const Utf8Str &strDest, const Utf8Str &strFilter,
     640                                           DirectoryCopyFlags_T enmDirCopyFlags)
     641                                           : GuestSessionTask(pSession)
     642                                           , mSource(strSource)
     643                                           , mDest(strDest)
     644                                           , mFilter(strFilter)
     645                                           , mDirCopyFlags(enmDirCopyFlags)
     646{
     647    m_strTaskName = "gctlCopyDirTo";
     648}
     649
     650SessionTaskCopyDirTo::~SessionTaskCopyDirTo(void)
     651{
     652
     653}
     654
     655/**
     656 * Copys a directory (tree) from host to the guest.
     657 *
     658 * @return  IPRT status code.
     659 * @param   strSource               Source directory on the host to copy to the guest.
     660 * @param   strFilter               DOS-style wildcard filter (?, *).  Optional.
     661 * @param   strDest                 Destination directory on the guest.
     662 * @param   fRecursive,             Whther to recursively copy the directory contents or not.
     663 * @param   fFollowSymlinks         Whether to follow symlinks or not.
     664 * @param   enmDirCopyFlags         Directory copy flags.
     665 * @param   strSubDir               Current sub directory to handle. Needs to NULL and only
     666 *                                  is needed for recursion.
     667 */
     668int SessionTaskCopyDirTo::directoryCopyToGuest(const Utf8Str &strSource, const Utf8Str &strFilter,
     669                                               const Utf8Str &strDest, bool fRecursive, bool fFollowSymlinks,
     670                                               const Utf8Str &strSubDir /* For recursion. */)
     671{
     672    Utf8Str strSrcDir    = strSource;
     673    Utf8Str strDstDir    = strDest;
     674    Utf8Str strSrcSubDir = strSubDir;
     675
     676    /* Validation and sanity. */
     677    if (   !strSrcDir.endsWith("/")
     678        && !strSrcDir.endsWith("\\"))
     679        strSrcDir += "/";
     680
     681    if (   !strDstDir.endsWith("/")
     682        && !strDstDir.endsWith("\\"))
     683        strDstDir+= "/";
     684
     685    if (    strSrcSubDir.isNotEmpty() /* Optional, for recursion. */
     686        && !strSrcSubDir.endsWith("/")
     687        && !strSrcSubDir.endsWith("\\"))
     688        strSrcSubDir += "/";
     689
     690    Utf8Str strSrcCur = strSrcDir + strSrcSubDir;
     691
     692    LogFlowFunc(("Entering '%s'\n", strSrcCur.c_str()));
     693
     694    int rc;
     695
     696    if (strSrcSubDir.isNotEmpty())
     697    {
     698        const uint32_t uMode = 0700; /** @todo */
     699        rc = directoryCreate(strDstDir + strSrcSubDir, DirectoryCreateFlag_Parents, uMode, fFollowSymlinks);
     700        if (RT_FAILURE(rc))
     701            return rc;
     702    }
     703
     704    /*
     705     * Open directory without a filter - RTDirOpenFiltered unfortunately
     706     * cannot handle sub directories so we have to do the filtering ourselves.
     707     */
     708    RTDIR hDir;
     709    rc = RTDirOpen(&hDir, strSrcCur.c_str());
     710    if (RT_SUCCESS(rc))
     711    {
     712        /*
     713         * Enumerate the directory tree.
     714         */
     715        size_t        cbDirEntry = 0;
     716        PRTDIRENTRYEX pDirEntry  = NULL;
     717        while (RT_SUCCESS(rc))
     718        {
     719            rc = RTDirReadExA(hDir, &pDirEntry, &cbDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
     720            if (RT_FAILURE(rc))
     721            {
     722                if (rc == VERR_NO_MORE_FILES)
     723                    rc = VINF_SUCCESS;
    405724                break;
    406725            }
    407726
    408             /* If the guest does not support waiting for stdin, we now yield in
    409              * order to reduce the CPU load due to busy waiting. */
    410             if (waitRes == ProcessWaitResult_WaitFlagNotSupported)
    411                 RTThreadYield(); /* Optional, don't check rc. */
    412 
    413             size_t cbRead = 0;
    414             if (mSourceSize) /* If we have nothing to write, take a shortcut. */
    415             {
    416                 /** @todo Not very efficient, but works for now. */
    417                 rc = RTFileSeek(*pFile, mSourceOffset + cbWrittenTotal,
    418                                 RTFILE_SEEK_BEGIN, NULL /* poffActual */);
    419                 if (RT_SUCCESS(rc))
    420                 {
    421                     rc = RTFileRead(*pFile, (uint8_t*)byBuf,
    422                                     RT_MIN((size_t)cbToRead, sizeof(byBuf)), &cbRead);
    423                     /*
    424                      * Some other error occured? There might be a chance that RTFileRead
    425                      * could not resolve/map the native error code to an IPRT code, so just
    426                      * print a generic error.
    427                      */
    428                     if (RT_FAILURE(rc))
     727#ifdef LOG_ENABLED
     728            Utf8Str strDbgCurEntry = strSrcCur + Utf8Str(pDirEntry->szName);
     729            LogFlowFunc(("Handling '%s' (fMode=0x%x)\n", strDbgCurEntry.c_str(), pDirEntry->Info.Attr.fMode & RTFS_TYPE_MASK));
     730#endif
     731            switch (pDirEntry->Info.Attr.fMode & RTFS_TYPE_MASK)
     732            {
     733                case RTFS_TYPE_DIRECTORY:
     734                {
     735                    /* Skip "." and ".." entries. */
     736                    if (RTDirEntryExIsStdDotLink(pDirEntry))
     737                        break;
     738
     739                    bool fSkip = false;
     740
     741                    if (   strFilter.isNotEmpty()
     742                        && !RTStrSimplePatternMatch(strFilter.c_str(), pDirEntry->szName))
     743                        fSkip = true;
     744
     745                    if (   fRecursive
     746                        && !fSkip)
     747                        rc = directoryCopyToGuest(strSrcDir, strFilter, strDstDir, fRecursive, fFollowSymlinks,
     748                                                  strSrcSubDir + Utf8Str(pDirEntry->szName));
     749                    break;
     750                }
     751
     752                case RTFS_TYPE_SYMLINK:
     753                    if (fFollowSymlinks)
     754                    { /* Fall through to next case is intentional. */ }
     755                    else
     756                        break;
     757                    RT_FALL_THRU();
     758
     759                case RTFS_TYPE_FILE:
     760                {
     761                    if (   strFilter.isNotEmpty()
     762                        && !RTStrSimplePatternMatch(strFilter.c_str(), pDirEntry->szName))
    429763                    {
    430                         setProgressErrorMsg(VBOX_E_IPRT_ERROR,
    431                                             Utf8StrFmt(GuestSession::tr("Could not read from host file \"%s\" (%Rrc)"),
    432                                                        mSource.c_str(), rc));
    433                         break;
     764                        break; /* Filter does not match. */
    434765                    }
    435                 }
    436                 else
    437                 {
    438                     setProgressErrorMsg(VBOX_E_IPRT_ERROR,
    439                                         Utf8StrFmt(GuestSession::tr("Seeking host file \"%s\" to offset %RU64 failed: %Rrc"),
    440                                                    mSource.c_str(), cbWrittenTotal, rc));
     766
     767                    if (RT_SUCCESS(rc))
     768                    {
     769                        Utf8Str strSrcFile = strSrcDir + strSrcSubDir + Utf8Str(pDirEntry->szName);
     770                        Utf8Str strDstFile = strDstDir + strSrcSubDir + Utf8Str(pDirEntry->szName);
     771                        rc = fileCopyToGuest(strSrcFile, strDstFile, FileCopyFlag_None);
     772                    }
    441773                    break;
    442774                }
    443             }
    444 
    445             uint32_t fFlags = ProcessInputFlag_None;
    446 
    447             /* Did we reach the end of the content we want to transfer (last chunk)? */
    448             if (   (cbRead < sizeof(byBuf))
    449                 /* Did we reach the last block which is exactly _64K? */
    450                 || (cbToRead - cbRead == 0)
    451                 /* ... or does the user want to cancel? */
    452                 || (   !mProgress.isNull()
    453                     && SUCCEEDED(mProgress->COMGETTER(Canceled(&fCanceled)))
    454                     && fCanceled)
    455                )
    456             {
    457                 LogFlowThisFunc(("Writing last chunk cbRead=%RU64\n", cbRead));
    458                 fFlags |= ProcessInputFlag_EndOfFile;
    459             }
    460 
    461             uint32_t cbWritten;
    462             Assert(sizeof(byBuf) >= cbRead);
    463             rc = pProcess->i_writeData(0 /* StdIn */, fFlags,
    464                                        byBuf, cbRead,
    465                                        msTimeout, &cbWritten, &guestRc);
    466             if (RT_FAILURE(rc))
    467             {
    468                 switch (rc)
    469                 {
    470                     case VERR_GSTCTL_GUEST_ERROR:
    471                         setProgressErrorMsg(VBOX_E_IPRT_ERROR,
    472                                             GuestProcess::i_guestErrorToString(guestRc));
    473                         break;
    474 
    475                     default:
    476                         setProgressErrorMsg(VBOX_E_IPRT_ERROR,
    477                                             Utf8StrFmt(GuestSession::tr("Writing to guest file \"%s\" (offset %RU64) failed: %Rrc"),
    478                                             mDest.c_str(), cbWrittenTotal, rc));
    479                         break;
    480                 }
    481 
    482                 break;
    483             }
    484 
    485             /* Only subtract bytes reported written by the guest. */
    486             Assert(cbToRead >= cbWritten);
    487             cbToRead -= cbWritten;
    488 
    489             /* Update total bytes written to the guest. */
    490             cbWrittenTotal += cbWritten;
    491             Assert(cbWrittenTotal <= mSourceSize);
    492 
    493             LogFlowThisFunc(("rc=%Rrc, cbWritten=%RU32, cbToRead=%RU64, cbWrittenTotal=%RU64, cbFileSize=%RU64\n",
    494                              rc, cbWritten, cbToRead, cbWrittenTotal, mSourceSize));
    495 
    496             /* Did the user cancel the operation above? */
    497             if (fCanceled)
    498                 break;
    499 
    500             /* Update the progress.
    501              * Watch out for division by zero. */
    502             mSourceSize > 0
    503                 ? rc = setProgress((ULONG)(cbWrittenTotal * 100 / mSourceSize))
    504                 : rc = setProgress(100);
     775
     776                default:
     777                    break;
     778            }
    505779            if (RT_FAILURE(rc))
    506780                break;
    507 
    508             /* End of file reached? */
    509             if (!cbToRead)
    510                 break;
    511         } /* for */
    512 
    513         LogFlowThisFunc(("Copy loop ended with rc=%Rrc, cbToRead=%RU64, cbWrittenTotal=%RU64, cbFileSize=%RU64\n",
    514                          rc, cbToRead, cbWrittenTotal, mSourceSize));
    515 
    516         /*
    517          * Wait on termination of guest process until it completed all operations.
    518          */
    519         if (   !fCanceled
    520             || RT_SUCCESS(rc))
    521         {
    522             rc = pProcess->i_waitFor(ProcessWaitForFlag_Terminate, msTimeout, waitRes, &guestRc);
    523             if (   RT_FAILURE(rc)
    524                 || waitRes != ProcessWaitResult_Terminate)
    525             {
    526                 if (RT_FAILURE(rc))
    527                     setProgressErrorMsg(VBOX_E_IPRT_ERROR,
    528                                         Utf8StrFmt(
    529                                         GuestSession::tr("Waiting on termination for copying file \"%s\" to guest failed: %Rrc"),
    530                                         mSource.c_str(), rc));
    531                 else
    532                 {
    533                     setProgressErrorMsg(VBOX_E_IPRT_ERROR,
    534                                         Utf8StrFmt(GuestSession::tr(
    535                                                    "Waiting on termination for copying file \"%s\" to guest failed with wait result %ld"),
    536                                                    mSource.c_str(), waitRes));
    537                     rc = VERR_GENERAL_FAILURE; /* Fudge. */
    538                 }
    539             }
    540         }
    541 
    542         if (RT_SUCCESS(rc))
    543         {
    544             /*
    545              * Newer VBoxService toolbox versions report what went wrong via exit code.
    546              * So handle this first.
    547              */
    548             /** @todo This code sequence is duplicated in CopyFrom...   */
    549             ProcessStatus_T procStatus = ProcessStatus_TerminatedAbnormally;
    550             HRESULT hrc = pProcess->COMGETTER(Status(&procStatus));
    551             if (!SUCCEEDED(hrc))
    552                 procStatus = ProcessStatus_TerminatedAbnormally;
    553 
    554             LONG exitCode = 42424242;
    555             hrc = pProcess->COMGETTER(ExitCode(&exitCode));
    556             if (!SUCCEEDED(hrc))
    557                 exitCode = 42424242;
    558 
    559             if (   procStatus != ProcessStatus_TerminatedNormally
    560                 || exitCode != 0)
    561             {
    562                 LogFlowThisFunc(("procStatus=%d, exitCode=%d\n", procStatus, exitCode));
    563                 if (procStatus == ProcessStatus_TerminatedNormally)
    564                     rc = GuestProcessTool::i_exitCodeToRc(procInfo, exitCode);
    565                 else
    566                     rc = VERR_GENERAL_FAILURE;
    567                 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
    568                                     Utf8StrFmt(GuestSession::tr("Copying file \"%s\" to guest failed: %Rrc"),
    569                                                mSource.c_str(), rc));
    570             }
    571             /*
    572              * Even if we succeeded until here make sure to check whether we really transfered
    573              * everything.
    574              */
    575             else if (   mSourceSize > 0
    576                      && cbWrittenTotal == 0)
    577             {
    578                 /* If nothing was transfered but the file size was > 0 then "vbox_cat" wasn't able to write
    579                  * to the destination -> access denied. */
    580                 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
    581                                     Utf8StrFmt(GuestSession::tr("Access denied when copying file \"%s\" to guest \"%s\""),
    582                                                mSource.c_str(), mDest.c_str()));
    583                 rc = VERR_ACCESS_DENIED;
    584             }
    585             else if (cbWrittenTotal < mSourceSize)
    586             {
    587                 /* If we did not copy all let the user know. */
    588                 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
    589                                     Utf8StrFmt(GuestSession::tr("Copying file \"%s\" to guest failed (%RU64/%RU64 bytes transfered)"),
    590                                                mSource.c_str(), cbWrittenTotal, mSourceSize));
    591                 rc = VERR_INTERRUPTED;
    592             }
    593             else
    594                 rc = setProgressSuccess();
    595         }
    596     } /* processCreateExInteral */
    597 
    598     if (!mSourceFile) /* Only close locally opened files. */
    599         RTFileClose(*pFile);
     781        }
     782
     783        RTDirReadExAFree(&pDirEntry, &cbDirEntry);
     784        RTDirClose(hDir);
     785    }
     786
     787    LogFlowFunc(("Leaving '%s', rc=%Rrc\n", strSrcCur.c_str(), rc));
     788    return rc;
     789}
     790
     791int SessionTaskCopyDirTo::Run(void)
     792{
     793    LogFlowThisFuncEnter();
     794
     795    AutoCaller autoCaller(mSession);
     796    if (FAILED(autoCaller.rc())) return autoCaller.rc();
     797
     798    const bool fRecursive      = true; /** @todo Make this configurable. */
     799    const bool fFollowSymlinks = true; /** @todo Make this configurable. */
     800    const uint32_t uDirMode    = 0700; /* Play safe by default. */
     801
     802    /* Create the root target directory on the guest.
     803     * The target directory might already exist on the guest (based on mDirCopyFlags). */
     804    int rc = directoryCreate(mDest, DirectoryCreateFlag_None, uDirMode, fFollowSymlinks);
     805    if (   rc == VWRN_ALREADY_EXISTS
     806        && !(mDirCopyFlags & DirectoryCopyFlags_CopyIntoExisting))
     807    {
     808        setProgressErrorMsg(VBOX_E_IPRT_ERROR,
     809                            Utf8StrFmt(GuestSession::tr("Destination directory \"%s\" exists when it must not"), mDest.c_str()));
     810    }
     811
     812    if (RT_FAILURE(rc))
     813        return rc;
     814
     815    /* At this point the directory on the guest was created and (hopefully) is ready
     816     * to receive further content. */
     817    rc = directoryCopyToGuest(mSource, mFilter, mDest, fRecursive, fFollowSymlinks,
     818                              "" /* strSubDir; for recursion */);
     819    if (RT_SUCCESS(rc))
     820        rc = setProgressSuccess();
    600821
    601822    LogFlowFuncLeaveRC(rc);
     
    603824}
    604825
    605 SessionTaskCopyFrom::SessionTaskCopyFrom(GuestSession *pSession,
    606                                          const Utf8Str &strSource, const Utf8Str &strDest, uint32_t uFlags)
    607                                          : GuestSessionTask(pSession)
     826SessionTaskCopyFileTo::SessionTaskCopyFileTo(GuestSession *pSession,
     827                                             const Utf8Str &strSource, const Utf8Str &strDest, uint32_t uFlags)
     828                                             : GuestSessionTask(pSession),
     829                                               mSource(strSource),
     830                                               mSourceFile(NULL),
     831                                               mSourceOffset(0),
     832                                               mSourceSize(0),
     833                                               mDest(strDest)
     834{
     835    mCopyFileFlags = uFlags;
     836    m_strTaskName = "gctlCopyFileTo";
     837}
     838
     839SessionTaskCopyFileTo::SessionTaskCopyFileTo(GuestSession *pSession,
     840                                             PRTFILE pSourceFile, size_t cbSourceOffset, uint64_t cbSourceSize,
     841                                             const Utf8Str &strDest, uint32_t uFlags)
     842                                             : GuestSessionTask(pSession)
     843{
     844    mSourceFile    = pSourceFile;
     845    mSourceOffset  = cbSourceOffset;
     846    mSourceSize    = cbSourceSize;
     847    mDest          = strDest;
     848    mCopyFileFlags = uFlags;
     849    m_strTaskName = "gctlCopyFileToWithHandle";
     850}
     851
     852SessionTaskCopyFileTo::~SessionTaskCopyFileTo(void)
     853{
     854
     855}
     856
     857int SessionTaskCopyFileTo::Run(void)
     858{
     859    LogFlowThisFuncEnter();
     860
     861    AutoCaller autoCaller(mSession);
     862    if (FAILED(autoCaller.rc())) return autoCaller.rc();
     863
     864    if (mSource.isEmpty())
     865    {
     866        setProgressErrorMsg(VBOX_E_IPRT_ERROR, Utf8StrFmt(GuestSession::tr("No source file specified")));
     867        return VERR_INVALID_PARAMETER;
     868    }
     869
     870    if (mDest.isEmpty())
     871    {
     872        setProgressErrorMsg(VBOX_E_IPRT_ERROR, Utf8StrFmt(GuestSession::tr("No destintation file specified")));
     873        return VERR_INVALID_PARAMETER;
     874    }
     875
     876    const char *pszFileName = RTPathFilename(mSource.c_str());
     877
     878    if (   !pszFileName
     879        || !RTFileExists(pszFileName))
     880    {
     881        setProgressErrorMsg(VBOX_E_IPRT_ERROR, Utf8StrFmt(GuestSession::tr("Source file not valid or does not exist")));
     882        return VERR_FILE_NOT_FOUND;
     883    }
     884
     885    if (mCopyFileFlags)
     886    {
     887        setProgressErrorMsg(VBOX_E_IPRT_ERROR,
     888                            Utf8StrFmt(GuestSession::tr("Copy flags (%#x) not implemented yet"),
     889                            mCopyFileFlags));
     890        return VERR_NOT_IMPLEMENTED;
     891    }
     892
     893    /** @todo Try to lock the destination directory on the guest here first? */
     894
     895    int rc = VINF_SUCCESS;
     896
     897    /*
     898     * Query information about our destination first.
     899     */
     900    if (   mDest.endsWith("/")
     901        || mDest.endsWith("\\"))
     902    {
     903        int guestRc;
     904        GuestFsObjData objData;
     905        rc = mSession->i_fsQueryInfoInternal(mDest, true /* fFollowSymlinks */, objData, &guestRc);
     906        if (RT_SUCCESS(rc))
     907        {
     908            if (objData.mType != FsObjType_Directory)
     909            {
     910                setProgressErrorMsg(VBOX_E_IPRT_ERROR,
     911                                    Utf8StrFmt(GuestSession::tr("Path \"%s\" is not a directory on guest"), mDest.c_str()));
     912                rc = VERR_NOT_A_DIRECTORY;
     913            }
     914            else
     915            {
     916                AssertPtr(pszFileName);
     917                mDest += pszFileName;
     918            }
     919        }
     920        else
     921        {
     922            switch (rc)
     923            {
     924                case VERR_GSTCTL_GUEST_ERROR:
     925                    setProgressErrorMsg(VBOX_E_IPRT_ERROR,
     926                                        GuestProcess::i_guestErrorToString(guestRc));
     927                    break;
     928
     929                default:
     930                    setProgressErrorMsg(VBOX_E_IPRT_ERROR,
     931                                        Utf8StrFmt(GuestSession::tr("Unable to query information for directory \"%s\" on the guest: %Rrc"),
     932                                                   mDest.c_str(), rc));
     933                    break;
     934            }
     935        }
     936    }
     937
     938    if (RT_SUCCESS(rc))
     939    {
     940        if (mSourceFile) /* Use existing file handle. */
     941            rc = fileCopyToGuestEx(mSource, mDest, mCopyFileFlags,
     942                                   mSourceFile, mSourceOffset, mSourceSize);
     943        else
     944            rc = fileCopyToGuest(mSource, mDest, mCopyFileFlags);
     945
     946        if (RT_SUCCESS(rc))
     947            rc = setProgressSuccess();
     948    }
     949
     950    LogFlowFuncLeaveRC(rc);
     951    return rc;
     952}
     953
     954SessionTaskCopyFileFrom::SessionTaskCopyFileFrom(GuestSession *pSession,
     955                                                 const Utf8Str &strSource, const Utf8Str &strDest, uint32_t uFlags)
     956                                                 : GuestSessionTask(pSession)
    608957{
    609958    mSource = strSource;
    610959    mDest   = strDest;
    611960    mFlags  = uFlags;
    612     m_strTaskName = "gctlCpyFrom";
    613 }
    614 
    615 SessionTaskCopyFrom::~SessionTaskCopyFrom(void)
    616 {
    617 
    618 }
    619 
    620 int SessionTaskCopyFrom::Run(void)
     961    m_strTaskName = "gctlCopyFileFrom";
     962}
     963
     964SessionTaskCopyFileFrom::~SessionTaskCopyFileFrom(void)
     965{
     966
     967}
     968
     969int SessionTaskCopyFileFrom::Run(void)
    621970{
    622971    LogFlowThisFuncEnter();
    623972
    624     ComObjPtr<GuestSession> pSession = mSession;
    625     Assert(!pSession.isNull());
    626 
    627     AutoCaller autoCaller(pSession);
     973    AutoCaller autoCaller(mSession);
    628974    if (FAILED(autoCaller.rc())) return autoCaller.rc();
    629975
     
    637983     */
    638984    GuestFsObjData objData; int guestRc;
    639     int rc = pSession->i_fileQueryInfoInternal(Utf8Str(mSource), false /*fFollowSymlinks*/, objData, &guestRc);
     985    int rc = mSession->i_fileQueryInfoInternal(Utf8Str(mSource), false /*fFollowSymlinks*/, objData, &guestRc);
    640986    if (RT_FAILURE(rc))
    641987    {
     
    6761022            /* Startup process. */
    6771023            ComObjPtr<GuestProcess> pProcess;
    678             rc = pSession->i_processCreateExInternal(procInfo, pProcess);
     1024            rc = mSession->i_processCreateExInternal(procInfo, pProcess);
    6791025            if (RT_SUCCESS(rc))
    6801026                rc = pProcess->i_startProcess(msTimeout, &guestRc);
     
    8991245}
    9001246
    901 int SessionTaskUpdateAdditions::i_addProcessArguments(ProcessArguments &aArgumentsDest,
    902                                                       const ProcessArguments &aArgumentsSource)
     1247int SessionTaskUpdateAdditions::addProcessArguments(ProcessArguments &aArgumentsDest, const ProcessArguments &aArgumentsSource)
    9031248{
    9041249    int rc = VINF_SUCCESS;
     
    9381283}
    9391284
    940 int SessionTaskUpdateAdditions::i_copyFileToGuest(GuestSession *pSession, PRTISOFSFILE pISO,
    941                                                   Utf8Str const &strFileSource, const Utf8Str &strFileDest,
    942                                                   bool fOptional, uint32_t *pcbSize)
     1285int SessionTaskUpdateAdditions::copyFileToGuest(GuestSession *pSession, PRTISOFSFILE pISO,
     1286                                                Utf8Str const &strFileSource, const Utf8Str &strFileDest,
     1287                                                bool fOptional, uint32_t *pcbSize)
    9431288{
    9441289    AssertPtrReturn(pSession, VERR_INVALID_POINTER);
     
    9711316        if (RT_SUCCESS(rc))
    9721317        {
    973             SessionTaskCopyTo *pTask = NULL;
     1318            SessionTaskCopyFileTo *pTask = NULL;
    9741319            ComObjPtr<Progress> pProgressCopyTo;
    9751320            try
     
    9771322                try
    9781323                {
    979                     pTask = new SessionTaskCopyTo(pSession /* GuestSession */,
    980                                                   &pISO->file, cbOffset, cbSize,
    981                                                   strFileDest, FileCopyFlag_None);
     1324                    pTask = new SessionTaskCopyFileTo(pSession /* GuestSession */,
     1325                                                      &pISO->file, cbOffset, cbSize,
     1326                                                      strFileDest, FileCopyFlag_None);
    9821327                }
    9831328                catch(...)
     
    10911436}
    10921437
    1093 int SessionTaskUpdateAdditions::i_runFileOnGuest(GuestSession *pSession, GuestProcessStartupInfo &procInfo)
     1438int SessionTaskUpdateAdditions::runFileOnGuest(GuestSession *pSession, GuestProcessStartupInfo &procInfo)
    10941439{
    10951440    AssertPtrReturn(pSession, VERR_INVALID_POINTER);
     
    14801825                        /* Add optional installer command line arguments from the API to the
    14811826                         * installer's startup info. */
    1482                         rc = i_addProcessArguments(siInstaller.mArguments, mArguments);
     1827                        rc = addProcessArguments(siInstaller.mArguments, mArguments);
    14831828                        AssertRC(rc);
    14841829                        /* If the caller does not want to wait for out guest update process to end,
     
    15201865                        if (itFiles->fFlags & UPDATEFILE_FLAG_OPTIONAL)
    15211866                            fOptional = true;
    1522                         rc = i_copyFileToGuest(pSession, &iso, itFiles->strSource, itFiles->strDest,
     1867                        rc = copyFileToGuest(pSession, &iso, itFiles->strSource, itFiles->strDest,
    15231868                                               fOptional, NULL /* cbSize */);
    15241869                        if (RT_FAILURE(rc))
     
    15571902                    if (itFiles->fFlags & UPDATEFILE_FLAG_EXECUTE)
    15581903                    {
    1559                         rc = i_runFileOnGuest(pSession, itFiles->mProcInfo);
     1904                        rc = runFileOnGuest(pSession, itFiles->mProcInfo);
    15601905                        if (RT_FAILURE(rc))
    15611906                            break;
Note: See TracChangeset for help on using the changeset viewer.

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