VirtualBox

Changeset 15754 in vbox for trunk/src/VBox


Ignore:
Timestamp:
Dec 25, 2008 10:52:24 AM (16 years ago)
Author:
vboxsync
Message:

Runtime/POSIX: RTPathAbs: Don't access file system when calculating the absolute path.

Location:
trunk/src/VBox/Runtime
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Runtime/r3/posix/path-posix.cpp

    r14054 r15754  
    169169RTDECL(int) RTPathAbs(const char *pszPath, char *pszAbsPath, size_t cchAbsPath)
    170170{
    171     /*
    172      * Convert input.
    173      */
    174     char *pszNativePath;
    175     int rc = rtPathToNative(&pszNativePath, pszPath);
    176     if (RT_FAILURE(rc))
     171    if (strlen(pszPath) > PATH_MAX)
    177172    {
    178173        LogFlow(("RTPathAbs(%p:{%s}, %p, %d): returns %Rrc\n", pszPath,
    179                  pszPath, pszAbsPath, cchAbsPath, rc));
    180         return rc;
    181     }
    182 
    183     /*
    184      * On POSIX platforms the API doesn't take a length parameter, which makes it
    185      * a little bit more work.
    186      */
     174                 pszPath, pszAbsPath, cchAbsPath, VERR_FILENAME_TOO_LONG));
     175        return VERR_FILENAME_TOO_LONG;
     176    }
     177
    187178    char szTmpPath[PATH_MAX + 1];
    188     char *psz = realpath(pszNativePath, szTmpPath);
    189     if (!psz)
    190     {
    191         if (errno == ENOENT || errno == ENOTDIR
    192 #ifdef RT_OS_OS2
    193             /// @todo realpath() returns EIO for non-existent UNC paths like
    194             //  //server/share/subdir (i.e. when a subdir is specified within
    195             //  a share). We should either fix realpath() in libc or remove
    196             //  this todo.
    197             || errno == EIO
    198 #endif
    199             )
    200         {
    201             if (strlen(pszNativePath) <= PATH_MAX)
    202             {
    203                 /*
    204                  * Iterate the path bit by bit an apply realpath to it.
    205                  */
    206 
    207                 char szTmpSrc[PATH_MAX + 1];
    208                 strcpy(szTmpSrc, pszNativePath);
    209                 fsCleanPath(szTmpSrc);
    210 
    211                 size_t cch = 0; // current resolved path length
    212                 char *pszCur = szTmpSrc;
     179    strcpy(szTmpPath, pszPath);
     180    fsCleanPath(szTmpPath);
     181
     182    char *pszCur = szTmpPath;
    213183
    214184#ifdef HAVE_DRIVE
    215                 if (pszCur[0] && RTPATH_IS_VOLSEP(pszCur[1]) && pszCur[2] == '/')
    216                 {
    217                     psz = szTmpPath;
    218                     cch = 2;
    219                     pszCur += 3;
    220                 }
     185    if (pszCur[0] && RTPATH_IS_VOLSEP(pszCur[1]) && pszCur[2] == '/')
     186        pszCur += 3;
    221187#ifdef HAVE_UNC
    222                 else
    223                 if (pszCur[0] == '/' && pszCur[1] == '/')
    224                 {
    225                     pszCur += 2;
    226                     char *pszSlash = strchr(pszCur, '/');
    227                     size_t cchElement = pszSlash ? pszSlash - pszCur : strlen(pszCur);
    228                     if (cchElement && pszCur[cchElement])
    229                     {
    230                         psz = szTmpPath;
    231                         cch = cchElement + 2;
    232                         pszCur += cchElement + 1;
    233                     }
    234                     else
    235                         /* we've got just "//server" or "//" */
    236                         /// @todo (r=dmik) not 100% sure we should fail, but the
    237                         //  above cases are just invalid (incomplete) paths,
    238                         //  no matter that Win32 returns these paths as is.
    239                         rc = VERR_INVALID_NAME;
    240                 }
     188    else
     189    if (pszCur[0] == '/' && pszCur[1] == '/')
     190        pszCur += 2;
    241191#endif
    242192#else
    243                 if (*pszCur == '/')
    244                 {
    245                     psz = szTmpPath;
    246                     pszCur++;
    247                 }
     193    if (pszCur[0] == '/')
     194        pszCur += 1;
    248195#endif
    249                 else
    250                 {
    251                     /* get the cwd */
    252                     psz = getcwd(szTmpPath,  sizeof(szTmpPath));
    253                     AssertMsg(psz, ("Couldn't get cwd!\n"));
    254                     if (psz)
    255                     {
    256 #ifdef HAVE_DRIVE
    257                         if (*pszCur == '/')
    258                         {
    259                             cch = 2;
    260                             pszCur++;
    261                         }
    262                         else
    263 #endif
    264                             cch = strlen(psz);
    265                     }
    266                     else
    267                         rc = RTErrConvertFromErrno(errno);
    268                 }
    269 
    270                 if (psz)
    271                 {
    272                     bool fResolveSymlinks = true;
    273                     char szTmpPath2[PATH_MAX + 1];
    274 
    275                     /* make sure strrchr() will work correctly */
    276                     psz[cch] = '\0';
    277 
    278                     while (*pszCur)
    279                     {
    280                         char *pszSlash = strchr(pszCur, '/');
    281                         size_t cchElement = pszSlash ? pszSlash - pszCur : strlen(pszCur);
    282                         if (cch + cchElement + 1 > PATH_MAX)
    283                         {
    284                             rc = VERR_FILENAME_TOO_LONG;
    285                             break;
    286                         }
    287 
    288                         if (!strncmp(pszCur, "..", cchElement))
    289                         {
    290                             char *pszLastSlash = strrchr(psz, '/');
    291 #ifdef HAVE_UNC
    292                             if (pszLastSlash && pszLastSlash > psz &&
    293                                 pszLastSlash[-1] != '/')
    294 #else
    295                             if (pszLastSlash)
    296 #endif
    297                             {
    298                                 cch = pszLastSlash - psz;
    299                                 psz[cch] = '\0';
    300                             }
    301                             /* else: We've reached the root and the parent of
    302                              * the root is the root. */
    303                         }
    304                         else
    305                         {
    306                             psz[cch++] = '/';
    307                             memcpy(psz + cch, pszCur, cchElement);
    308                             cch += cchElement;
    309                             psz[cch] = '\0';
    310 
    311                             if (fResolveSymlinks)
    312                             {
    313                                 /* resolve possible symlinks */
    314                                 char *psz2 = realpath(psz, psz == szTmpPath
    315                                                            ? szTmpPath2
    316                                                            : szTmpPath);
    317                                 if (psz2)
    318                                 {
    319                                     psz = psz2;
    320                                     cch = strlen(psz);
    321                                 }
    322                                 else
    323                                 {
    324                                     if (errno != ENOENT && errno != ENOTDIR
    325 #ifdef RT_OS_OS2
    326                                         /// @todo see above
    327                                         && errno != EIO
    328 #endif
    329                                         )
    330                                     {
    331                                         rc = RTErrConvertFromErrno(errno);
    332                                         break;
    333                                     }
    334 
    335                                     /* no more need to resolve symlinks */
    336                                     fResolveSymlinks = false;
    337                                 }
    338                             }
    339                         }
    340 
    341                         pszCur += cchElement;
    342                         /* skip the slash */
    343                         if (*pszCur)
    344                             ++pszCur;
    345                     }
    346 
    347 #ifdef HAVE_DRIVE
    348                     /* check if we're at the root */
    349                     if (cch == 2 && RTPATH_IS_VOLSEP(psz[1]))
    350 #else
    351                     /* if the length is zero here, then we're at the root */
    352                     if (!cch)
    353 #endif
    354                     {
    355                         psz[cch++] = '/';
    356                         psz[cch] = '\0';
    357                     }
    358                 }
    359             }
    360             else
    361                 rc = VERR_FILENAME_TOO_LONG;
    362         }
    363         else
    364             rc = RTErrConvertFromErrno(errno);
    365     }
    366 
    367     RTStrFree(pszNativePath);
    368 
    369     if (psz && RT_SUCCESS(rc))
     196    else
    370197    {
    371198        /*
    372          * Convert result and copy it to the return buffer.
     199         * Prepend the current directory to the relative path.
    373200         */
    374         char *pszUtf8AbsPath;
    375         rc = rtPathFromNative(&pszUtf8AbsPath, psz);
    376         if (RT_FAILURE(rc))
    377         {
     201
     202        char szCurDir[PATH_MAX + 1];
     203        if (getcwd(szCurDir, sizeof(szCurDir)) == NULL)
     204        {
     205            AssertMsgFailed(("Couldn't get cwd!\n"));
     206
     207            int rc = RTErrConvertFromErrno(errno);
    378208            LogFlow(("RTPathAbs(%p:{%s}, %p, %d): returns %Rrc\n", pszPath,
    379209                     pszPath, pszAbsPath, cchAbsPath, rc));
     
    381211        }
    382212
    383         /* replace '/' back with native RTPATH_SLASH */
    384         psz = pszUtf8AbsPath;
    385         for (; *psz; psz++)
    386             if (*psz == '/')
    387                 *psz = RTPATH_SLASH;
    388 
    389         unsigned cch = strlen(pszUtf8AbsPath) + 1;
    390         if (cch <= cchAbsPath)
    391             memcpy(pszAbsPath, pszUtf8AbsPath, cch);
     213        fsCleanPath(szCurDir);
     214
     215        {
     216            /*
     217             * Convert result and copy it to the return buffer.
     218             */
     219            char *pszUtf8CurDir;
     220            int rc = rtPathFromNative(&pszUtf8CurDir, szCurDir);
     221            if (RT_FAILURE(rc))
     222            {
     223                LogFlow(("RTPathAbs(%p:{%s}, %p, %d): returns %Rrc\n", pszPath,
     224                         pszPath, pszAbsPath, cchAbsPath, rc));
     225                return rc;
     226            }
     227
     228            size_t cchUtf8CurDir = strlen(pszUtf8CurDir);
     229            if (cchUtf8CurDir + strlen(szTmpPath) + 1 > PATH_MAX)
     230            {
     231                RTStrFree(pszUtf8CurDir);
     232                LogFlow(("RTPathAbs(%p:{%s}, %p, %d): returns %Rrc\n", pszPath,
     233                         pszPath, pszAbsPath, cchAbsPath, VERR_FILENAME_TOO_LONG));
     234                return VERR_FILENAME_TOO_LONG;
     235            }
     236
     237            strcpy(szTmpPath + cchUtf8CurDir + 1, szTmpPath);
     238            strcpy(szTmpPath, pszUtf8CurDir);
     239            szTmpPath[cchUtf8CurDir] = '/';
     240
     241            RTStrFree(pszUtf8CurDir);
     242        }
     243
     244#ifdef HAVE_DRIVE
     245        if (pszCur[0] && RTPATH_IS_VOLSEP(pszCur[1]) && pszCur[2] == '/')
     246            pszCur += 3;
     247#ifdef HAVE_UNC
    392248        else
    393             rc = VERR_BUFFER_OVERFLOW;
    394         RTStrFree(pszUtf8AbsPath);
    395     }
     249        if (pszCur[0] == '/' && pszCur[1] == '/')
     250            pszCur += 2;
     251#endif
     252#else
     253        if (pszCur[0] == '/')
     254            pszCur += 1;
     255#endif
     256        else
     257        {
     258            AssertFailed();
     259            LogFlow(("RTPathAbs(%p:{%s}, %p, %d): returns %Rrc\n", pszPath,
     260                     pszPath, pszAbsPath, cchAbsPath, VERR_GENERAL_FAILURE));
     261            return VERR_GENERAL_FAILURE;
     262        }
     263    }
     264
     265    char *pszTop = pszCur;
     266
     267    /*
     268     * Get rid of double dot path components by evaluating them.
     269     */
     270
     271    for (;;)
     272    {
     273        if (pszCur[0] == '.' && pszCur[1] == '.' &&
     274            (!pszCur[2] || pszCur[2] == '/'))
     275        {
     276            /* rewind to the previous component if any */
     277            char *pszPrev = pszCur - 1;
     278            if (pszPrev > pszTop)
     279                while (*--pszPrev != '/')
     280                    ;
     281
     282            AssertMsg(*pszPrev == '/', ("szTmpPath={%s}, pszPrev=+%u\n",
     283                                        szTmpPath, pszPrev - szTmpPath));
     284            strcpy(pszPrev, pszCur + 2);
     285
     286            pszCur = pszPrev;
     287        }
     288        else
     289        {
     290            while (*pszCur && *pszCur != '/')
     291                ++pszCur;
     292        }
     293
     294        if (!*pszCur)
     295            break;
     296
     297        /* skip the slash */
     298        ++pszCur;
     299    }
     300
     301    if (pszCur < pszTop)
     302    {
     303        /*
     304         * We overwrote the trailing slash of the root path with zero, restore
     305         * it.
     306         */
     307        *pszCur++ = '/';
     308        *pszCur = '\0';
     309    }
     310    else if (pszCur > pszTop && *(pszCur - 1) == '/')
     311    {
     312        /*
     313         * Extra trailing slash in a non-root path, remove it.
     314         */
     315        *--pszCur = '\0';
     316    }
     317
     318    int rc = VINF_SUCCESS;
     319
     320    size_t cch = pszCur - szTmpPath + 1;
     321    if (cch <= cchAbsPath)
     322        memcpy(pszAbsPath, szTmpPath, cch);
     323    else
     324        rc = VERR_BUFFER_OVERFLOW;
    396325
    397326    LogFlow(("RTPathAbs(%p:{%s}, %p:{%s}, %d): returns %Rrc\n", pszPath,
  • trunk/src/VBox/Runtime/testcase/tstPath.cpp

    r14324 r15754  
    4040#include <iprt/param.h>
    4141
     42#if defined (RT_OS_WINDOWS)
     43# include <direct.h> // for getcwd
     44#else
     45# include <unistd.h> // for getcwd
     46#endif
     47#include <errno.h> // for getcwd
    4248
    4349#define CHECK_RC(method) \
     
    8591     */
    8692    RTPrintf("tstPath: TESTING RTPathAbsEx()\n");
    87     static const char *aInput[] =
    88     {
    89         // NULL, NULL, -- assertion in RTStrToUtf16
    90         NULL,                           "/absolute/..",
    91         NULL,                           "/absolute\\\\../..",
    92         NULL,                           "/absolute//../path",
    93         NULL,                           "/absolute/../../path",
    94         NULL,                           "relative/../dir\\.\\.\\.\\file.txt",
    95         NULL,                           "\\",
    96         "relative_base/dir\\",          "\\from_root",
    97         "relative_base/dir/",           "relative_also",
    98 #if defined (RT_OS_OS2) || defined (RT_OS_WINDOWS)
    99         NULL,                           "C:\\",
    100         "C:\\",                         "..",
    101         "C:\\temp",                     "..",
    102         "C:\\VirtualBox/Machines",      "..\\VirtualBox.xml",
    103         "C:\\MustDie",                  "\\from_root/dir/..",
    104         "C:\\temp",                     "D:\\data",
    105         NULL,                           "\\\\server\\../share", // -- on Win32, GetFullPathName doesn't remove .. here
    106         /* the three below use cases should fail with VERR_INVALID_NAME */
    107         //NULL,                           "\\\\server",
    108         //NULL,                           "\\\\",
    109         //NULL,                           "\\\\\\something",
    110         "\\\\server\\share_as_base",    "/from_root",
    111         "\\\\just_server",              "/from_root",
    112         "\\\\server\\share_as_base",    "relative\\data",
    113         "base",                         "\\\\?\\UNC\\relative/edwef/..",
    114         "base",                         "\\\\?\\UNC\\relative/edwef/..",
    115         /* this is not (and I guess should not be) supported, should fail */
    116         ///@todo "\\\\?\\UNC\\base",             "/from_root",
     93    static const struct
     94    {
     95        const char *pcszInputBase;
     96        const char *pcszInputPath;
     97        int rc;
     98        const char *pcszOutput;
     99    }
     100    aRTPathAbsExTests[] =
     101    {
     102#if defined (RT_OS_OS2) || defined (RT_OS_WINDOWS)
     103//  { NULL, "", VINF_SUCCESS, "%p" },
     104//  { NULL, ".", VINF_SUCCESS, "%p" },
     105    { NULL, "\\", VINF_SUCCESS, "%d\\" },
     106    { NULL, "\\..", VINF_SUCCESS, "%d\\" },
     107    { NULL, "/absolute/..", VINF_SUCCESS, "%d\\" },
     108    { NULL, "/absolute\\\\../..", VINF_SUCCESS, "%d\\" },
     109    { NULL, "/absolute//../path\\", VINF_SUCCESS, "%d\\path" },
     110    { NULL, "/absolute/../../path", VINF_SUCCESS, "%d\\path" },
     111    { NULL, "relative/../dir\\.\\.\\.\\file.txt", VINF_SUCCESS, "%p\\dir\\file.txt" },
     112    { NULL, "\\data\\", VINF_SUCCESS, "%d\\data" },
     113    { "relative_base/dir\\", "\\from_root", VINF_SUCCESS, "%d\\from_root" },
     114    { "relative_base/dir/", "relative_also", VINF_SUCCESS, "%p\\relative_base\\dir\\relative_also" },
    117115#else
    118         "\\temp",                       "..",
    119         "\\VirtualBox/Machines",        "..\\VirtualBox.xml",
    120         "\\MustDie",                    "\\from_root/dir/..",
    121         "\\temp",                       "\\data",
     116//  { NULL, "", VINF_SUCCESS, "/" },
     117//  { NULL, ".", VINF_SUCCESS, "%p" },
     118    { NULL, "/", VINF_SUCCESS, "/" },
     119    { NULL, "/..", VINF_SUCCESS, "/" },
     120    { NULL, "/absolute/..", VINF_SUCCESS, "/" },
     121    { NULL, "/absolute\\\\../..", VINF_SUCCESS, "/" },
     122    { NULL, "/absolute//../path/", VINF_SUCCESS, "/path" },
     123    { NULL, "/absolute/../../path", VINF_SUCCESS, "/path" },
     124    { NULL, "relative/../dir/./././file.txt", VINF_SUCCESS, "%p/dir/file.txt" },
     125    { NULL, "relative/../dir\\.\\.\\.\\file.txt", VINF_SUCCESS, "%p/dir\\.\\.\\.\\file.txt" },  /* linux-specific */
     126    { NULL, "/data/", VINF_SUCCESS, "/data" },
     127    { "relative_base/dir/", "/from_root", VINF_SUCCESS, "/from_root" },
     128    { "relative_base/dir/", "relative_also", VINF_SUCCESS, "%p/relative_base/dir/relative_also" },
     129#endif
     130#if defined (RT_OS_OS2) || defined (RT_OS_WINDOWS)
     131    { NULL, "C:\\", VINF_SUCCESS, "C:\\" },
     132    { "C:\\", "..", VINF_SUCCESS, "C:\\" },
     133    { "C:\\temp", "..", VINF_SUCCESS, "C:\\" },
     134    { "C:\\VirtualBox/Machines", "..\\VirtualBox.xml", VINF_SUCCESS, "C:\\VirtualBox\\VirtualBox.xml" },
     135    { "C:\\MustDie", "\\from_root/dir/..", VINF_SUCCESS, "C:\\from_root" },
     136    { "C:\\temp", "D:\\data", VINF_SUCCESS, "D:\\data" },
     137    { NULL, "\\\\server\\..\\share", VINF_SUCCESS, "\\\\server\\..\\share" /* kind of strange */ },
     138    { NULL, "\\\\server/", VINF_SUCCESS, "\\\\server" },
     139    { NULL, "\\\\", VINF_SUCCESS, "\\\\" },
     140    { NULL, "\\\\\\something", VINF_SUCCESS, "\\\\\\something" /* kind of strange */ },
     141    { "\\\\server\\share_as_base", "/from_root", VINF_SUCCESS, "\\\\server\\from_root" },
     142    { "\\\\just_server", "/from_root", VINF_SUCCESS, "\\\\just_server\\from_root" },
     143    { "\\\\server\\share_as_base", "relative\\data", VINF_SUCCESS, "\\\\server\\share_as_base\\relative\\data" },
     144    { "base", "\\\\?\\UNC\\relative/edwef/..", VINF_SUCCESS, "\\\\?\\UNC\\relative" },
     145    { "\\\\?\\UNC\\base", "/from_root", VERR_INVALID_NAME, NULL },
     146#else
     147    { "/temp", "..", VINF_SUCCESS, "/" },
     148    { "/VirtualBox/Machines", "../VirtualBox.xml", VINF_SUCCESS, "/VirtualBox/VirtualBox.xml" },
     149    { "/MustDie", "/from_root/dir/..", VINF_SUCCESS, "/from_root" },
     150    { "\\temp", "\\data", VINF_SUCCESS, "%p/\\temp/\\data" },
    122151#endif
    123152    };
    124153
    125     for (unsigned i = 0; i < RT_ELEMENTS(aInput); i += 2)
    126     {
    127         RTPrintf("tstPath: base={%s}, path={%s}, ", aInput[i], aInput[i + 1]);
    128         CHECK_RC(RTPathAbsEx(aInput[i], aInput[i + 1], szPath, sizeof(szPath)));
    129         if (RT_SUCCESS(rc))
    130             RTPrintf("abs={%s}\n", szPath);
     154    for (unsigned i = 0; i < RT_ELEMENTS(aRTPathAbsExTests); ++ i)
     155    {
     156        rc = RTPathAbsEx(aRTPathAbsExTests[i].pcszInputBase,
     157                         aRTPathAbsExTests[i].pcszInputPath,
     158                         szPath, sizeof(szPath));
     159        if (rc != aRTPathAbsExTests[i].rc)
     160        {
     161            RTPrintf("tstPath: RTPathAbsEx unexpected result code!\n"
     162                     "   input base: '%s'\n"
     163                     "   input path: '%s'\n"
     164                     "       output: '%s'\n"
     165                     "           rc: %Rrc\n"
     166                     "  expected rc: %Rrc\n",
     167                     aRTPathAbsExTests[i].pcszInputBase,
     168                     aRTPathAbsExTests[i].pcszInputPath,
     169                     szPath, rc,
     170                     aRTPathAbsExTests[i].rc);
     171            cErrors++;
     172            continue;
     173        }
     174
     175        char szTmp[RTPATH_MAX];
     176        char *pszExpected = NULL;
     177        if (aRTPathAbsExTests[i].pcszOutput != NULL)
     178        {
     179            if (aRTPathAbsExTests[i].pcszOutput[0] == '%')
     180            {
     181                if (getcwd(szTmp, sizeof(szTmp)) == NULL)
     182                {
     183                    RTPrintf("tstPath: getcwd failed with errno=%d!\n", errno);
     184                    cErrors++;
     185                    break;
     186                }
     187
     188                pszExpected = szTmp;
     189
     190                if (aRTPathAbsExTests[i].pcszOutput[1] == 'p')
     191                {
     192                    size_t cch = strlen(szTmp);
     193                    if (cch + strlen (aRTPathAbsExTests[i].pcszOutput) - 2 <= sizeof(szTmp))
     194                        strcpy (szTmp + cch, aRTPathAbsExTests[i].pcszOutput + 2);
     195                }
     196#if defined (RT_OS_OS2) || defined (RT_OS_WINDOWS)
     197                else if (aRTPathAbsExTests[i].pcszOutput[1] == 'd')
     198                {
     199                    if (2 + strlen (aRTPathAbsExTests[i].pcszOutput) - 2 <= sizeof(szTmp))
     200                        strcpy (szTmp + 2, aRTPathAbsExTests[i].pcszOutput + 2);
     201                }
     202#endif
     203            }
     204            else
     205            {
     206                strcpy(szTmp, aRTPathAbsExTests[i].pcszOutput);
     207                pszExpected = szTmp;
     208            }
     209
     210            if (strcmp(szPath, pszExpected))
     211            {
     212                RTPrintf("tstPath: RTPathAbsEx failed!\n"
     213                         "   input base: '%s'\n"
     214                         "   input path: '%s'\n"
     215                         "       output: '%s'\n"
     216                         "     expected: '%s'\n",
     217                         aRTPathAbsExTests[i].pcszInputBase,
     218                         aRTPathAbsExTests[i].pcszInputPath,
     219                         szPath,
     220                         aRTPathAbsExTests[i].pcszOutput);
     221                cErrors++;
     222            }
     223        }
    131224    }
    132225
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