path-posix.cpp revision aebd6922da709979189b47974e57a99e799a1a3a
/* $Id$ */
/** @file
* IPRT - Path Manipulation, POSIX.
*/
/*
* Copyright (C) 2006-2007 Sun Microsystems, Inc.
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*
* The contents of this file may alternatively be used under the terms
* of the Common Development and Distribution License Version 1.0
* (CDDL) only, as it comes in the "COPYING.CDDL" file of the
* VirtualBox OSE distribution, in which case the provisions of the
* CDDL are applicable instead of those of the GPL.
*
* You may elect to license modified versions of this file under the
* terms and conditions of either the GPL or the CDDL or both.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 USA or visit http://www.sun.com if you need
* additional information or have any questions.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP RTLOGGROUP_PATH
#include <stdlib.h>
#include <limits.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <pwd.h>
#ifdef RT_OS_L4
#endif
{
/*
* Convert input.
*/
char *pszNativePath;
if (RT_SUCCESS(rc))
{
/*
* On POSIX platforms the API doesn't take a length parameter, which makes it
* a little bit more work.
*/
if (psz)
{
/*
* Convert result and copy it to the return buffer.
*/
char *pszUtf8RealPath;
if (RT_SUCCESS(rc))
{
if (cch <= cchRealPath)
else
}
}
else
}
return rc;
}
/**
* Cleans up a path specifier a little bit.
* This includes removing duplicate slashes, uncessary single dots, and
* trailing slashes. Also, replaces all RTPATH_SLASH characters with '/'.
*
* @returns Number of bytes in the clean path.
* @param pszPath The path to cleanup.
*/
static int fsCleanPath(char *pszPath)
{
/*
* Change to '/' and remove duplicates.
*/
#ifdef HAVE_UNC
int fUnc = 0;
if ( RTPATH_IS_SLASH(pszPath[0])
{ /* Skip first slash in a unc path. */
pszSrc++;
*pszTrg++ = '/';
fUnc = 1;
}
#endif
for (;;)
{
if (RTPATH_IS_SLASH(ch))
{
*pszTrg++ = '/';
for (;;)
{
while (RTPATH_IS_SLASH(ch));
/* Remove '/./' and '/.'. */
break;
}
}
if (!ch)
break;
pszTrg++;
}
/*
* Remove trailing slash if the path may be pointing to a directory.
*/
if ( cch > 1
#ifdef HAVE_DRIVE
#endif
return cch;
}
{
int rc;
/*
* Validation.
*/
if (RT_UNLIKELY(!*pszPath))
return VERR_INVALID_PARAMETER;
/*
* Make a clean working copy of the input.
*/
{
LogFlow(("RTPathAbs(%p:{%s}, %p, %d): returns %Rrc\n", pszPath, pszPath, pszAbsPath, cchAbsPath, VERR_FILENAME_TOO_LONG));
return VERR_FILENAME_TOO_LONG;
}
/*
* Handle "." specially (fsCleanPath does).
*/
/*
* Do we have a root slash?
*/
#ifdef HAVE_DRIVE
pszCur += 3;
# ifdef HAVE_UNC
pszCur += 2;
# endif
#else /* !HAVE_DRIVE */
if (pszCur[0] == '/')
pszCur += 1;
#endif /* !HAVE_DRIVE */
else
{
/*
* No, prepend the current directory to the relative path.
*/
char szCurDir[RTPATH_MAX];
{
LogFlow(("RTPathAbs(%p:{%s}, %p, %d): returns %Rrc\n", pszPath, pszPath, pszAbsPath, cchAbsPath, VERR_FILENAME_TOO_LONG));
return VERR_FILENAME_TOO_LONG;
}
#ifdef HAVE_DRIVE
pszCur += 3;
# ifdef HAVE_UNC
pszCur += 2;
# endif
#else
if (pszCur[0] == '/')
pszCur += 1;
#endif
else
}
/*
* Get rid of double dot path components by evaluating them.
*/
for (;;)
{
if ( pszCur[0] == '.'
{
/* rewind to the previous component if any */
while (*--pszPrev != '/')
;
}
else
{
/* advance to end of component. */
pszCur++;
}
if (!*pszCur)
break;
/* skip the slash */
++pszCur;
}
{
/*
* We overwrote the root slash with '\0', restore it.
*/
*pszCur++ = '/';
*pszCur = '\0';
}
{
/*
* Extra trailing slash in a non-root path, remove it.
* (A bit questionable...)
*/
*--pszCur = '\0';
}
/*
* Copy the result to the user buffer.
*/
if (cchTmpPath < cchAbsPath)
{
rc = VINF_SUCCESS;
}
else
return rc;
}
#ifndef RT_OS_L4
/**
* Worker for RTPathUserHome that looks up the home directory
* using the getpwuid_r api.
*
* @returns IPRT status code.
* @param pszPath The path buffer.
* @param cchPath The size of the buffer.
* @param uid The User ID to query the home directory of.
*/
{
/*
* The getpwuid_r function uses the passed in buffer to "allocate" any
* extra memory it needs. On some systems we should probably use the
* sysconf function to find the appropriate buffer size, but since it won't
* work everywhere we'll settle with a 5KB buffer and ASSUME that it'll
* suffice for even the lengthiest user descriptions...
*/
char achBuffer[5120];
if (rc != 0)
return RTErrConvertFromErrno(rc);
return VERR_PATH_NOT_FOUND;
/*
* Check that it isn't empty and that it exists.
*/
return VERR_PATH_NOT_FOUND;
/*
* Convert it to UTF-8 and copy it to the return buffer.
*/
char *pszUtf8Path;
if (RT_SUCCESS(rc))
{
else
}
return rc;
}
#endif
/**
* Worker for RTPathUserHome that looks up the home directory
* using the HOME environment variable.
*
* @returns IPRT status code.
* @param pszPath The path buffer.
* @param cchPath The size of the buffer.
*/
{
/*
* Get HOME env. var it and validate it's existance.
*/
int rc = VERR_PATH_NOT_FOUND;
if (pszHome)
{
{
/*
* Convert it to UTF-8 and copy it to the return buffer.
*/
char *pszUtf8Path;
if (RT_SUCCESS(rc))
{
else
}
}
}
return rc;
}
{
int rc;
#ifndef RT_OS_L4
/*
* We make an exception for the root user and use the system call
* getpwuid_r to determine their initial home path instead of
* reading it from the $HOME variable. This is because the $HOME
* variable does not get changed by sudo (and possibly su and others)
* which can cause root-owned files to appear in user's home folders.
*/
if (!uid)
else
/*
* On failure, retry using the alternative method.
* (Should perhaps restrict the retry cases a bit more here...)
*/
if ( RT_FAILURE(rc)
&& rc != VERR_BUFFER_OVERFLOW)
{
if (!uid)
else
}
#else /* RT_OS_L4 */
#endif /* RT_OS_L4 */
return rc;
}
RTR3DECL(int) RTPathQueryInfo(const char *pszPath, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs)
{
/*
* Validate input.
*/
("Invalid enmAdditionalAttribs=%p\n", enmAdditionalAttribs),
/*
* Convert the filename.
*/
char *pszNativePath;
if (RT_SUCCESS(rc))
{
{
switch (enmAdditionalAttribs)
{
case RTFSOBJATTRADD_EASIZE:
/** @todo Use SGI extended attribute interface to query EA info. */
break;
case RTFSOBJATTRADD_NOTHING:
case RTFSOBJATTRADD_UNIX:
break;
default:
AssertMsgFailed(("Impossible!\n"));
return VERR_INTERNAL_ERROR;
}
}
else
}
LogFlow(("RTPathQueryInfo(%p:{%s}, pObjInfo=%p, %d): returns %Rrc\n",
return rc;
}
RTR3DECL(int) RTPathSetTimes(const char *pszPath, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
{
/*
* Validate input.
*/
AssertMsgReturn(!pAccessTime || VALID_PTR(pAccessTime), ("%p\n", pAccessTime), VERR_INVALID_POINTER);
AssertMsgReturn(!pModificationTime || VALID_PTR(pModificationTime), ("%p\n", pModificationTime), VERR_INVALID_POINTER);
AssertMsgReturn(!pChangeTime || VALID_PTR(pChangeTime), ("%p\n", pChangeTime), VERR_INVALID_POINTER);
/*
* Convert the paths.
*/
char *pszNativePath;
if (RT_SUCCESS(rc))
{
/*
* If it's a no-op, we'll only verify the existance of the file.
*/
if (!pAccessTime && !pModificationTime)
{
rc = VINF_SUCCESS;
else
{
}
}
else
{
/*
* Convert the input to timeval, getting the missing one if necessary,
* and call the API which does the change.
*/
if (pAccessTime && pModificationTime)
{
}
else
{
if (RT_SUCCESS(rc))
{
RTTimeSpecGetTimeval(pModificationTime ? pModificationTime : &ObjInfo.ModificationTime, &aTimevals[1]);
}
else
Log(("RTPathSetTimes('%s',%p,%p,,): RTPathQueryInfo failed with %Rrc\n",
}
if (RT_SUCCESS(rc))
{
{
Log(("RTPathSetTimes('%s',%p,%p,,): failed with %Rrc and errno=%d\n",
}
}
}
}
LogFlow(("RTPathSetTimes(%p:{%s}, %p:{%RDtimespec}, %p:{%RDtimespec}, %p:{%RDtimespec}, %p:{%RDtimespec}): return %Rrc\n",
return rc;
}
/**
* Checks if two files are the one and same file.
*/
{
return false;
return false;
return true;
return false;
}
/**
* Worker for RTPathRename, RTDirRename, RTFileRename.
*
* @returns IPRT status code.
* @param pszSrc The source path.
* @param pszDst The destintation path.
* @param fRename The rename flags.
* @param fFileType The filetype. We use the RTFMODE filetypes here. If it's 0,
* anything goes. If it's RTFS_TYPE_DIRECTORY we'll check that the
* source is a directory. If Its RTFS_TYPE_FILE we'll check that it's
* not a directory (we are NOT checking whether it's a file).
*/
{
/*
* Convert the paths.
*/
char *pszNativeSrc;
if (RT_SUCCESS(rc))
{
char *pszNativeDst;
if (RT_SUCCESS(rc))
{
/*
* Check that the source exists and that any types that's specified matches.
* We have to check this first to avoid getting errnous VERR_ALREADY_EXISTS
* errors from the next step.
*
* There are race conditions here (perhaps unlikly ones but still), but I'm
* afraid there is little with can do to fix that.
*/
else if (!fFileType)
rc = VINF_SUCCESS;
else if (RTFS_IS_DIRECTORY(fFileType))
else
if (RT_SUCCESS(rc))
{
bool fSameFile = false;
/*
* Check if the target exists, rename is rather destructive.
* We'll have to make sure we don't overwrite the source!
* Another race condition btw.
*/
else
{
{
/*
* It's likely that we're talking about the same file here.
* We should probably check paths or whatever, but for now this'll have to be enough.
*/
fSameFile = true;
}
if (fSameFile)
rc = VINF_SUCCESS;
else
rc = VINF_SUCCESS;
}
if (RT_SUCCESS(rc))
{
rc = VINF_SUCCESS;
else if ( (fRename & RTPATHRENAME_FLAGS_REPLACE)
{
/*
* Check that the destination isn't a directory.
* Yet another race condition.
*/
{
rc = VINF_SUCCESS;
Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): appears to be the same file... (errno=%d)\n",
}
else
{
else
rc = VINF_SUCCESS;
if (RT_SUCCESS(rc))
{
if (!unlink(pszNativeDst))
{
rc = VINF_SUCCESS;
else
{
Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): rename failed rc=%Rrc errno=%d\n",
}
}
else
{
Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): failed to unlink dst rc=%Rrc errno=%d\n",
}
}
else
Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): dst !dir check failed rc=%Rrc\n",
}
}
else
{
Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): rename failed rc=%Rrc errno=%d\n",
}
}
else
Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): destination check failed rc=%Rrc errno=%d\n",
}
else
Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): source type check failed rc=%Rrc errno=%d\n",
}
}
return rc;
}
{
/*
* Validate input.
*/
AssertMsgReturn(!(fRename & ~RTPATHRENAME_FLAGS_REPLACE), ("%#x\n", fRename), VERR_INVALID_PARAMETER);
/*
* Hand it to the worker.
*/
Log(("RTPathRename(%p:{%s}, %p:{%s}, %#x): returns %Rrc\n", pszSrc, pszSrc, pszDst, pszDst, fRename, rc));
return rc;
}
{
/*
* Validate input.
*/
AssertPtrReturn(pszPath, false);
AssertReturn(*pszPath, false);
/*
* Convert the path and check if it exists using stat().
*/
char *pszNativePath;
if (RT_SUCCESS(rc))
{
rc = VINF_SUCCESS;
else
}
return RT_SUCCESS(rc);
}
{
int rc;
char szNativeCurDir[RTPATH_MAX];
{
char *pszCurDir;
if (RT_SUCCESS(rc))
{
{
return VINF_SUCCESS;
}
}
}
else
return rc;
}
{
/*
* Validate input.
*/
/*
* Change the directory.
*/
char *pszNativePath;
if (RT_SUCCESS(rc))
{
if (chdir(pszNativePath))
}
return rc;
}