path-posix.cpp revision 11e38fb5c81f04067f310efc4cdeb604bb462585
4efd24e631e5312d1fb78ae7ccaf9de912ff0e9fvboxsync * IPRT - Path Manipulation, POSIX.
e64031e20c39650a7bc902a3e1aba613b9415deevboxsync * Copyright (C) 2006-2007 Sun Microsystems, Inc.
637712559132d2b3617aea0103fb0385978da14evboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
637712559132d2b3617aea0103fb0385978da14evboxsync * available from http://www.virtualbox.org. This file is free software;
637712559132d2b3617aea0103fb0385978da14evboxsync * you can redistribute it and/or modify it under the terms of the GNU
637712559132d2b3617aea0103fb0385978da14evboxsync * General Public License (GPL) as published by the Free Software
637712559132d2b3617aea0103fb0385978da14evboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
637712559132d2b3617aea0103fb0385978da14evboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
637712559132d2b3617aea0103fb0385978da14evboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
637712559132d2b3617aea0103fb0385978da14evboxsync * The contents of this file may alternatively be used under the terms
637712559132d2b3617aea0103fb0385978da14evboxsync * of the Common Development and Distribution License Version 1.0
637712559132d2b3617aea0103fb0385978da14evboxsync * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
637712559132d2b3617aea0103fb0385978da14evboxsync * VirtualBox OSE distribution, in which case the provisions of the
637712559132d2b3617aea0103fb0385978da14evboxsync * CDDL are applicable instead of those of the GPL.
637712559132d2b3617aea0103fb0385978da14evboxsync * You may elect to license modified versions of this file under the
637712559132d2b3617aea0103fb0385978da14evboxsync * terms and conditions of either the GPL or the CDDL or both.
637712559132d2b3617aea0103fb0385978da14evboxsync * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
637712559132d2b3617aea0103fb0385978da14evboxsync * Clara, CA 95054 USA or visit http://www.sun.com if you need
637712559132d2b3617aea0103fb0385978da14evboxsync * additional information or have any questions.
637712559132d2b3617aea0103fb0385978da14evboxsync/*******************************************************************************
637712559132d2b3617aea0103fb0385978da14evboxsync* Header Files *
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync*******************************************************************************/
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsyncRTDECL(int) RTPathReal(const char *pszPath, char *pszRealPath, size_t cchRealPath)
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync * Convert input.
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync * On POSIX platforms the API doesn't take a length parameter, which makes it
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync * a little bit more work.
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync const char *psz = realpath(pszNativePath, szTmpPath);
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync * Convert result and copy it to the return buffer.
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync rc = rtPathFromNative(&pszUtf8RealPath, szTmpPath);
b2e185d7c946ed90402d1dd86b24c6d82d8e591bvboxsync LogFlow(("RTPathReal(%p:{%s}, %p:{%s}, %u): returns %Rrc\n", pszPath, pszPath,
b2e185d7c946ed90402d1dd86b24c6d82d8e591bvboxsync pszRealPath, RT_SUCCESS(rc) ? pszRealPath : "<failed>", cchRealPath));
b2e185d7c946ed90402d1dd86b24c6d82d8e591bvboxsync * Cleans up a path specifier a little bit.
b2e185d7c946ed90402d1dd86b24c6d82d8e591bvboxsync * This includes removing duplicate slashes, uncessary single dots, and
b2e185d7c946ed90402d1dd86b24c6d82d8e591bvboxsync * trailing slashes. Also, replaces all RTPATH_SLASH characters with '/'.
b2e185d7c946ed90402d1dd86b24c6d82d8e591bvboxsync * @returns Number of bytes in the clean path.
b2e185d7c946ed90402d1dd86b24c6d82d8e591bvboxsync * @param pszPath The path to cleanup.
b2e185d7c946ed90402d1dd86b24c6d82d8e591bvboxsync * Change to '/' and remove duplicates.
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync { /* Skip first slash in a unc path. */
b2e185d7c946ed90402d1dd86b24c6d82d8e591bvboxsync /* Remove '/./' and '/.'. */
b2e185d7c946ed90402d1dd86b24c6d82d8e591bvboxsync if (ch != '.' || (*pszSrc && !RTPATH_IS_SLASH(*pszSrc)))
b2e185d7c946ed90402d1dd86b24c6d82d8e591bvboxsync * Remove trailing slash if the path may be pointing to a directory.
d142cd5e90598dd8d14744cb6bb7dd467cec446cvboxsyncRTDECL(int) RTPathAbs(const char *pszPath, char *pszAbsPath, size_t cchAbsPath)
d142cd5e90598dd8d14744cb6bb7dd467cec446cvboxsync * Validation.
d142cd5e90598dd8d14744cb6bb7dd467cec446cvboxsync * Make a clean working copy of the input.
d142cd5e90598dd8d14744cb6bb7dd467cec446cvboxsync LogFlow(("RTPathAbs(%p:{%s}, %p, %d): returns %Rrc\n", pszPath, pszPath, pszAbsPath, cchAbsPath, VERR_FILENAME_TOO_LONG));
d142cd5e90598dd8d14744cb6bb7dd467cec446cvboxsync * Handle "." specially (fsCleanPath does).
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync * Do we have a root slash?
637712559132d2b3617aea0103fb0385978da14evboxsync if (pszCur[0] && RTPATH_IS_VOLSEP(pszCur[1]) && pszCur[2] == '/')
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync#else /* !HAVE_DRIVE */
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync#endif /* !HAVE_DRIVE */
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync * No, prepend the current directory to the relative path.
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync size_t cchCurDir = fsCleanPath(szCurDir); /* paranoia */
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync LogFlow(("RTPathAbs(%p:{%s}, %p, %d): returns %Rrc\n", pszPath, pszPath, pszAbsPath, cchAbsPath, VERR_FILENAME_TOO_LONG));
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync memmove(szTmpPath + cchCurDir + 1, szTmpPath, cchTmpPath + 1);
eeee785f36554f67d8737506fb0fadbf683cf9dfvboxsync if (pszCur[0] && RTPATH_IS_VOLSEP(pszCur[1]) && pszCur[2] == '/')
eeee785f36554f67d8737506fb0fadbf683cf9dfvboxsync AssertMsgFailedReturn(("pszCur=%s\n", pszCur), VERR_INTERNAL_ERROR);
eeee785f36554f67d8737506fb0fadbf683cf9dfvboxsync * Get rid of double dot path components by evaluating them.
eeee785f36554f67d8737506fb0fadbf683cf9dfvboxsync /* rewind to the previous component if any */
eeee785f36554f67d8737506fb0fadbf683cf9dfvboxsync AssertMsg(*pszPrev == '/', ("szTmpPath={%s}, pszPrev=+%u\n", szTmpPath, pszPrev - szTmpPath));
eeee785f36554f67d8737506fb0fadbf683cf9dfvboxsync memmove(pszPrev, pszCur + 2, strlen(pszCur + 2) + 1);
eeee785f36554f67d8737506fb0fadbf683cf9dfvboxsync /* advance to end of component. */
637712559132d2b3617aea0103fb0385978da14evboxsync /* skip the slash */
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync * We overwrote the root slash with '\0', restore it.
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync * Extra trailing slash in a non-root path, remove it.
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync * (A bit questionable...)
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync * Copy the result to the user buffer.
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync LogFlow(("RTPathAbs(%p:{%s}, %p:{%s}, %d): returns %Rrc\n", pszPath, pszPath, pszAbsPath,
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync RT_SUCCESS(rc) ? pszAbsPath : "<failed>", cchAbsPath, rc));
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync * Worker for RTPathUserHome that looks up the home directory
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync * using the getpwuid_r api.
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync * @returns IPRT status code.
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync * @param pszPath The path buffer.
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync * @param cchPath The size of the buffer.
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync * @param uid The User ID to query the home directory of.
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsyncstatic int rtPathUserHomeByPasswd(char *pszPath, size_t cchPath, uid_t uid)
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync * The getpwuid_r function uses the passed in buffer to "allocate" any
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync * extra memory it needs. On some systems we should probably use the
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync * sysconf function to find the appropriate buffer size, but since it won't
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync * work everywhere we'll settle with a 5KB buffer and ASSUME that it'll
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync * suffice for even the lengthiest user descriptions...
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync int rc = getpwuid_r(uid, &Passwd, &achBuffer[0], sizeof(achBuffer), &pPasswd);
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync * Check that it isn't empty and that it exists.
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync * Convert it to UTF-8 and copy it to the return buffer.
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync rc = rtPathFromNative(&pszUtf8Path, pPasswd->pw_dir);
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync * Worker for RTPathUserHome that looks up the home directory
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync * using the HOME environment variable.
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync * @returns IPRT status code.
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync * @param pszPath The path buffer.
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync * @param cchPath The size of the buffer.
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsyncstatic int rtPathUserHomeByEnv(char *pszPath, size_t cchPath)
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync * Get HOME env. var it and validate it's existance.
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync * Convert it to UTF-8 and copy it to the return buffer.
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsyncRTDECL(int) RTPathUserHome(char *pszPath, size_t cchPath)
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync * We make an exception for the root user and use the system call
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync * getpwuid_r to determine their initial home path instead of
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync * reading it from the $HOME variable. This is because the $HOME
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync * variable does not get changed by sudo (and possibly su and others)
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync * which can cause root-owned files to appear in user's home folders.
637712559132d2b3617aea0103fb0385978da14evboxsync rc = rtPathUserHomeByPasswd(pszPath, cchPath, uid);
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync * On failure, retry using the alternative method.
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync * (Should perhaps restrict the retry cases a bit more here...)
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync rc = rtPathUserHomeByPasswd(pszPath, cchPath, uid);
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync#else /* RT_OS_L4 */
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync#endif /* RT_OS_L4 */
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync LogFlow(("RTPathUserHome(%p:{%s}, %u): returns %Rrc\n", pszPath,
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync RT_SUCCESS(rc) ? pszPath : "<failed>", cchPath, rc));
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsyncRTR3DECL(int) RTPathQueryInfo(const char *pszPath, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs)
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync return RTPathQueryInfoEx(pszPath, pObjInfo, enmAdditionalAttribs, RTPATH_F_ON_LINK);
637712559132d2b3617aea0103fb0385978da14evboxsyncRTR3DECL(int) RTPathQueryInfoEx(const char *pszPath, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs, uint32_t fFlags)
637712559132d2b3617aea0103fb0385978da14evboxsync * Validate input.
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync AssertMsgReturn( enmAdditionalAttribs >= RTFSOBJATTRADD_NOTHING
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync ("Invalid enmAdditionalAttribs=%p\n", enmAdditionalAttribs),
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync AssertMsgReturn(RTPATH_F_IS_VALID(fFlags, 0), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync * Convert the filename.
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync rc = lstat(pszNativePath, &Stat); /** @todo how doesn't have lstat again? */
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync rtFsConvertStatToObjInfo(pObjInfo, &Stat, pszPath, 0);
b1d7d513c459787311cd09c440524044fa7ff8a9vboxsync /** @todo Use SGI extended attribute interface to query EA info. */
b1d7d513c459787311cd09c440524044fa7ff8a9vboxsync pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_EASIZE;
b1d7d513c459787311cd09c440524044fa7ff8a9vboxsync Assert(pObjInfo->Attr.enmAdditional == RTFSOBJATTRADD_UNIX);
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync LogFlow(("RTPathQueryInfo(%p:{%s}, pObjInfo=%p, %d): returns %Rrc\n",
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync pszPath, pszPath, pObjInfo, enmAdditionalAttribs, rc));
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsyncRTR3DECL(int) RTPathSetTimes(const char *pszPath, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
45c4c3d2db1a166b7b1e92960adb823960821386vboxsync return RTPathSetTimesEx(pszPath, pAccessTime, pModificationTime, pChangeTime, pBirthTime, RTPATH_F_ON_LINK);
45c4c3d2db1a166b7b1e92960adb823960821386vboxsyncRTR3DECL(int) RTPathSetTimesEx(const char *pszPath, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
45c4c3d2db1a166b7b1e92960adb823960821386vboxsync PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime, uint32_t fFlags)
45c4c3d2db1a166b7b1e92960adb823960821386vboxsync * Validate input.
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync AssertPtrNullReturn(pAccessTime, VERR_INVALID_POINTER);
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync AssertPtrNullReturn(pModificationTime, VERR_INVALID_POINTER);
45c4c3d2db1a166b7b1e92960adb823960821386vboxsync AssertPtrNullReturn(pChangeTime, VERR_INVALID_POINTER);
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync AssertPtrNullReturn(pBirthTime, VERR_INVALID_POINTER);
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync AssertMsgReturn(RTPATH_F_IS_VALID(fFlags, 0), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
637712559132d2b3617aea0103fb0385978da14evboxsync * Convert the paths.
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync * If it's a no-op, we'll only verify the existance of the file.
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync rc = RTPathQueryInfoEx(pszPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, fFlags);
b1d7d513c459787311cd09c440524044fa7ff8a9vboxsync * Convert the input to timeval, getting the missing one if necessary,
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync * and call the API which does the change.
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync RTTimeSpecGetTimeval(pModificationTime, &aTimevals[1]);
b1d7d513c459787311cd09c440524044fa7ff8a9vboxsync rc = RTPathQueryInfoEx(pszPath, &ObjInfo, RTFSOBJATTRADD_UNIX, fFlags);
b1d7d513c459787311cd09c440524044fa7ff8a9vboxsync RTTimeSpecGetTimeval(pAccessTime ? pAccessTime : &ObjInfo.AccessTime, &aTimevals[0]);
b1d7d513c459787311cd09c440524044fa7ff8a9vboxsync RTTimeSpecGetTimeval(pModificationTime ? pModificationTime : &ObjInfo.ModificationTime, &aTimevals[1]);
b1d7d513c459787311cd09c440524044fa7ff8a9vboxsync Log(("RTPathSetTimes('%s',%p,%p,,): RTPathQueryInfo failed with %Rrc\n",
637712559132d2b3617aea0103fb0385978da14evboxsync#if (defined(RT_OS_DARWIN) && MAC_OS_X_VERSION_MIN_REQUIRED >= 1050) \
637712559132d2b3617aea0103fb0385978da14evboxsync || defined(RT_OS_LINUX) \
637712559132d2b3617aea0103fb0385978da14evboxsync || defined(RT_OS_OS2) /** @todo who really has lutimes? */
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync rc = RTPathQueryInfoEx(pszPath, &ObjInfo, RTFSOBJATTRADD_UNIX, fFlags);
45c4c3d2db1a166b7b1e92960adb823960821386vboxsync if (RT_SUCCESS(rc) && RTFS_IS_SYMLINK(ObjInfo.Attr.fMode))
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync Log(("RTPathSetTimes('%s',%p,%p,,): failed with %Rrc and errno=%d\n",
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync pszPath, pAccessTime, pModificationTime, rc, errno));
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync LogFlow(("RTPathSetTimes(%p:{%s}, %p:{%RDtimespec}, %p:{%RDtimespec}, %p:{%RDtimespec}, %p:{%RDtimespec}): return %Rrc\n",
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync pszPath, pszPath, pAccessTime, pAccessTime, pModificationTime, pModificationTime,
637712559132d2b3617aea0103fb0385978da14evboxsync pChangeTime, pChangeTime, pBirthTime, pBirthTime));
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync * Checks if two files are the one and same file.
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsyncstatic bool rtPathSame(const char *pszNativeSrc, const char *pszNativeDst)
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync return false;
char *pszNativeSrc;
char *pszNativeDst;
else if (!fFileType)
bool fSameFile = false;
fSameFile = true;
if (fSameFile)
return rc;
AssertMsgReturn(!(fRename & ~RTPATHRENAME_FLAGS_REPLACE), ("%#x\n", fRename), VERR_INVALID_PARAMETER);
Log(("RTPathRename(%p:{%s}, %p:{%s}, %#x): returns %Rrc\n", pszSrc, pszSrc, pszDst, pszDst, fRename, rc));
return rc;
char *pszNativePath;
if (!rc)
int rc;
char *pszCurDir;
return VINF_SUCCESS;
return rc;
char *pszNativePath;
return rc;