path-posix.cpp revision 677833bc953b6cb418c701facbdcf4aa18d6c44e
/* $Id$ */
/** @file
* InnoTek Portable Runtime - Path Manipulation, POSIX.
*/
/*
* Copyright (C) 2006 InnoTek Systemberatung GmbH
*
* 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 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.
*
* If you received this file as part of a commercial VirtualBox
* distribution, then only the terms of your commercial VirtualBox
* license agreement apply instead of the previous paragraph.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP RTLOGGROUP_PATH
#include <stdlib.h>
#include <limits.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#ifdef __DARWIN__
#endif
#ifdef __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.
*
* @returns Number of bytes in the clean path.
* @param pszPath The path to cleanup.
* @remark Borrowed from InnoTek libc.
*/
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_SEP(ch))
{
*pszTrg++ = RTPATH_SLASH;
for (;;)
{
while (RTPATH_IS_SEP(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;
}
{
/*
* Convert input.
*/
char *pszNativePath;
if (RT_FAILURE(rc))
{
return rc;
}
/*
* On POSIX platforms the API doesn't take a length parameter, which makes it
* a little bit more work.
*/
if (!psz)
{
{
{
/*
* Iterate the path bit by bit an apply realpath to it.
*/
if (*pszCur == RTPATH_SLASH)
{
pszCur++;
}
else
{
/* get the cwd */
if (psz)
else
}
if (psz)
{
bool fResolveSymlinks = true;
while (*pszCur)
{
{
break;
}
{
if (pszLastSlash)
{
}
/* else: We've reached the root and the parent of the root is the root. */
}
else
{
cch += cchElement;
if (fResolveSymlinks)
{
/* resolve possible symlinks */
: szTmpPath);
if (psz2)
{
}
else
{
{
break;
}
/* no more need to resolve symlinks */
fResolveSymlinks = false;
}
}
}
pszCur += cchElement;
/* skip the slash */
if (*pszCur)
++pszCur;
}
/* if the length is zero here, then we're at the root (Not true for half-posixs stuff such as libc!) */
if (!cch)
{
}
}
}
else
}
else
}
{
/*
* Convert result and copy it to the return buffer.
*/
char *pszUtf8AbsPath;
if (RT_FAILURE(rc))
{
return rc;
}
if (cch <= cchAbsPath)
else
}
return rc;
}
{
/*
* First time only.
*/
if (!g_szrtProgramPath[0])
{
/*
* Linux have no API for obtaining the executable path, but provides a symbolic link
* in the proc file system. Note that readlink is one of the weirdest Unix apis around.
*
* OS/2 have an api for getting the program file name.
*/
/** @todo use RTProcGetExecutableName() */
#ifdef __LINUX__
{
return rc;
}
#elif defined(__DARWIN__)
const char *pszImageName = _dyld_get_image_name(0);
if (cchImageName >= sizeof(g_szrtProgramPath))
#else
#endif
/*
* Convert to UTF-8 and strip of the filename.
*/
if (RT_FAILURE(rc))
{
return rc;
}
if (cch >= sizeof(g_szrtProgramPath))
{
return VERR_BUFFER_OVERFLOW;
}
}
/*
* Calc the length and check if there is space before copying.
*/
{
return VINF_SUCCESS;
}
return VERR_BUFFER_OVERFLOW;
}
{
/*
* Get HOME env. var it and validate it's existance.
*/
int rc;
struct stat s;
if (pszHome)
{
{
/*
* Convert it to UTF-8 and copy it to the return buffer.
*/
char *pszUtf8Path;
if (RT_SUCCESS(rc))
{
else
}
}
else
}
else
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;
}