1 /* $Id: path-posix.cpp 26050 2007-11-12 12:11:12Z lelik $ */
3 * innotek Portable Runtime - Path Manipulation, POSIX.
7 * Copyright (C) 2006-2007 innotek GmbH
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License as published by the Free Software Foundation,
13 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
14 * distribution. VirtualBox OSE is distributed in the hope that it will
15 * be useful, but WITHOUT ANY WARRANTY of any kind.
19 /*******************************************************************************
21 *******************************************************************************/
22 #define LOG_GROUP RTLOGGROUP_PATH
30 #include <sys/types.h>
39 #include "const_defines.h"
40 #include "RTErrConvertFromErrno.h"
41 #include "func_defines.h"
45 /*******************************************************************************
47 *******************************************************************************/
48 char g_szrtProgramPath
[RTPATH_MAX
];
50 /*******************************************************************************
51 * Internal Functions *
52 *******************************************************************************/
53 static int rtstrConvert(const void *pvInput
, size_t cbInput
, const char *pszInputCS
, void **ppvOutput
, size_t cbOutput
, const char *pszOutputCS
, unsigned cFactor
);
56 int RTPathReal(const char *pszPath
, char *pszRealPath
, unsigned cchRealPath
)
62 int rc
= rtPathToNative(&pszNativePath
, pszPath
);
66 * On POSIX platforms the API doesn't take a length parameter, which makes it
67 * a little bit more work.
69 char szTmpPath
[PATH_MAX
+ 1];
70 const char *psz
= realpath(pszNativePath
, szTmpPath
);
74 * Convert result and copy it to the return buffer.
76 char *pszUtf8RealPath
;
77 rc
= rtPathFromNative(&pszUtf8RealPath
, szTmpPath
);
80 size_t cch
= strlen(pszUtf8RealPath
) + 1;
81 if (cch
<= cchRealPath
)
82 memcpy(pszRealPath
, pszUtf8RealPath
, cch
);
84 rc
= VERR_BUFFER_OVERFLOW
;
85 RTStrFree(pszUtf8RealPath
);
89 rc
= RTErrConvertFromErrno(errno
);
90 RTStrFree(pszNativePath
);
93 printf("RTPathReal(%p:{%s}, %p:{%s}, %u): returns %Rrc\n", pszPath
, pszPath
,
94 pszRealPath
, RT_SUCCESS(rc
) ? pszRealPath
: "<failed>", cchRealPath
);
98 int rtPathToNative(char **ppszNativePath
, const char *pszPath
)
100 return RTStrUtf8ToCurrentCP(ppszNativePath
, pszPath
);
103 int rtPathToNativeEx(char **ppszNativePath
, const char *pszPath
, const char *pszBasePath
)
106 return RTStrUtf8ToCurrentCP(ppszNativePath
, pszPath
);
109 void rtPathFreeNative(char *pszNativePath
)
112 RTStrFree(pszNativePath
);
116 int rtPathFromNative(char **pszPath
, const char *pszNativePath
)
118 return RTStrCurrentCPToUtf8(pszPath
, pszNativePath
);
122 int rtPathFromNativeEx(char **pszPath
, const char *pszNativePath
, const char *pszBasePath
)
125 return RTStrCurrentCPToUtf8(pszPath
, pszNativePath
);
130 * Cleans up a path specifier a little bit.
131 * This includes removing duplicate slashes, uncessary single dots, and
132 * trailing slashes. Also, replaces all RTPATH_SLASH characters with '/'.
134 * @returns Number of bytes in the clean path.
135 * @param pszPath The path to cleanup.
136 * @remark Borrowed from innotek libc.
138 static int fsCleanPath(char *pszPath
)
141 * Change to '/' and remove duplicates.
143 char *pszSrc
= pszPath
;
144 char *pszTrg
= pszPath
;
147 if ( RTPATH_IS_SLASH(pszPath
[0])
148 && RTPATH_IS_SLASH(pszPath
[1]))
149 { /* Skip first slash in a unc path. */
159 if (RTPATH_IS_SLASH(ch
))
165 while (RTPATH_IS_SLASH(ch
));
167 /* Remove '/./' and '/.'. */
168 if (ch
!= '.' || (*pszSrc
&& !RTPATH_IS_SLASH(*pszSrc
)))
179 * Remove trailing slash if the path may be pointing to a directory.
181 int cch
= pszTrg
- pszPath
;
183 && RTPATH_IS_SLASH(pszTrg
[-1])
185 && !RTPATH_IS_VOLSEP(pszTrg
[-2])
187 && !RTPATH_IS_SLASH(pszTrg
[-2]))
188 pszPath
[--cch
] = '\0';
194 int RTPathAbs(const char *pszPath
, char *pszAbsPath
, unsigned cchAbsPath
)
200 int rc
= rtPathToNative(&pszNativePath
, pszPath
);
203 printf("RTPathAbs(%p:{%s}, %p, %d): returns %Rrc\n", pszPath
,
204 pszPath
, pszAbsPath
, cchAbsPath
, rc
);
209 * On POSIX platforms the API doesn't take a length parameter, which makes it
210 * a little bit more work.
212 char szTmpPath
[PATH_MAX
+ 1];
213 char *psz
= realpath(pszNativePath
, szTmpPath
);
216 if (errno
== ENOENT
|| errno
== ENOTDIR
218 /// @todo realpath() returns EIO for non-existent UNC paths like
219 // //server/share/subdir (i.e. when a subdir is specified within
220 // a share). We should either fix realpath() in libc or remove
226 if (strlen(pszNativePath
) <= PATH_MAX
)
229 * Iterate the path bit by bit an apply realpath to it.
232 char szTmpSrc
[PATH_MAX
+ 1];
233 strcpy(szTmpSrc
, pszNativePath
);
234 fsCleanPath(szTmpSrc
);
236 size_t cch
= 0; // current resolved path length
237 char *pszCur
= szTmpSrc
;
240 if (pszCur
[0] && RTPATH_IS_VOLSEP(pszCur
[1]) && pszCur
[2] == '/')
248 if (pszCur
[0] == '/' && pszCur
[1] == '/')
251 char *pszSlash
= strchr(pszCur
, '/');
252 size_t cchElement
= pszSlash
? pszSlash
- pszCur
: strlen(pszCur
);
253 if (cchElement
&& pszCur
[cchElement
])
256 cch
= cchElement
+ 2;
257 pszCur
+= cchElement
+ 1;
260 /* we've got just "//server" or "//" */
261 /// @todo (r=dmik) not 100% sure we should fail, but the
262 // above cases are just invalid (incomplete) paths,
263 // no matter that Win32 returns these paths as is.
264 rc
= VERR_INVALID_NAME
;
277 psz
= getcwd(szTmpPath
, sizeof(szTmpPath
));
278 AssertMsg(psz
, ("Couldn't get cwd!\n"));
292 rc
= RTErrConvertFromErrno(errno
);
297 bool fResolveSymlinks
= true;
298 char szTmpPath2
[PATH_MAX
+ 1];
300 /* make sure strrchr() will work correctly */
305 char *pszSlash
= strchr(pszCur
, '/');
306 size_t cchElement
= pszSlash
? pszSlash
- pszCur
: strlen(pszCur
);
307 if (cch
+ cchElement
+ 1 > PATH_MAX
)
309 rc
= VERR_FILENAME_TOO_LONG
;
313 if (!strncmp(pszCur
, "..", cchElement
))
315 char *pszLastSlash
= strrchr(psz
, '/');
317 if (pszLastSlash
&& pszLastSlash
> psz
&&
318 pszLastSlash
[-1] != '/')
323 cch
= pszLastSlash
- psz
;
326 /* else: We've reached the root and the parent of
327 * the root is the root. */
332 memcpy(psz
+ cch
, pszCur
, cchElement
);
336 if (fResolveSymlinks
)
338 /* resolve possible symlinks */
339 char *psz2
= realpath(psz
, psz
== szTmpPath
349 if (errno
!= ENOENT
&& errno
!= ENOTDIR
356 rc
= RTErrConvertFromErrno(errno
);
360 /* no more need to resolve symlinks */
361 fResolveSymlinks
= false;
366 pszCur
+= cchElement
;
373 /* check if we're at the root */
374 if (cch
== 2 && RTPATH_IS_VOLSEP(psz
[1]))
376 /* if the length is zero here, then we're at the root */
386 rc
= VERR_FILENAME_TOO_LONG
;
389 rc
= RTErrConvertFromErrno(errno
);
392 RTStrFree(pszNativePath
);
394 if (psz
&& RT_SUCCESS(rc
))
397 * Convert result and copy it to the return buffer.
399 char *pszUtf8AbsPath
;
400 rc
= rtPathFromNative(&pszUtf8AbsPath
, psz
);
403 printf("RTPathAbs(%p:{%s}, %p, %d): returns %Rrc\n", pszPath
,
404 pszPath
, pszAbsPath
, cchAbsPath
, rc
);
408 /* replace '/' back with native RTPATH_SLASH */
409 psz
= pszUtf8AbsPath
;
414 unsigned cch
= strlen(pszUtf8AbsPath
) + 1;
415 if (cch
<= cchAbsPath
)
416 memcpy(pszAbsPath
, pszUtf8AbsPath
, cch
);
418 rc
= VERR_BUFFER_OVERFLOW
;
419 RTStrFree(pszUtf8AbsPath
);
422 printf("RTPathAbs(%p:{%s}, %p:{%s}, %d): returns %Rrc\n", pszPath
,
423 pszPath
, pszAbsPath
, RT_SUCCESS(rc
) ? pszAbsPath
: "<failed>",
429 int RTPathProgram(char *pszPath
, unsigned cchPath
)
434 if (!g_szrtProgramPath
[0])
437 * Linux have no API for obtaining the executable path, but provides a symbolic link
438 * in the proc file system. Note that readlink is one of the weirdest Unix apis around.
440 * OS/2 have an api for getting the program file name.
442 /** @todo use RTProcGetExecutableName() */
443 #if defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD) || defined(RT_OS_SOLARIS)
445 int cchLink
= readlink("/proc/self/exe", &g_szrtProgramPath
[0], sizeof(g_szrtProgramPath
) - 1);
446 # elif defined(RT_OS_SOLARIS)
447 pid_t curProcId
= getpid();
448 char szFileBuf
[PATH_MAX
+ 1];
449 sprintf(szFileBuf
, "/proc/%ld/path/a.out", curProcId
);
450 int cchLink
= readlink(szFileBuf
, &g_szrtProgramPath
[0], sizeof(g_szrtProgramPath
) - 1);
451 # else /* RT_OS_FREEBSD: */
452 int cchLink
= readlink("/proc/curproc/file", &g_szrtProgramPath
[0], sizeof(g_szrtProgramPath
) - 1);
454 if (cchLink
< 0 || cchLink
== sizeof(g_szrtProgramPath
) - 1)
456 int rc
= RTErrConvertFromErrno(errno
);
457 AssertMsgFailed(("couldn't read /proc/self/exe. errno=%d cchLink=%d\n", errno
, cchLink
));
458 printf("RTPathProgram(%p, %u): returns %Rrc\n", pszPath
, cchPath
, rc
);
461 g_szrtProgramPath
[cchLink
] = '\0';
463 #elif defined(RT_OS_OS2) || defined(RT_OS_L4)
464 _execname(g_szrtProgramPath
, sizeof(g_szrtProgramPath
));
466 #elif defined(RT_OS_DARWIN)
467 const char *pszImageName
= _dyld_get_image_name(0);
468 AssertReturn(pszImageName
, VERR_INTERNAL_ERROR
);
469 size_t cchImageName
= strlen(pszImageName
);
470 if (cchImageName
>= sizeof(g_szrtProgramPath
))
471 AssertReturn(pszImageName
, VERR_INTERNAL_ERROR
);
472 memcpy(g_szrtProgramPath
, pszImageName
, cchImageName
+ 1);
475 # error needs porting.
479 * Convert to UTF-8 and strip of the filename.
482 int rc
= rtPathFromNative(&pszTmp
, &g_szrtProgramPath
[0]);
485 printf("RTPathProgram(%p, %u): returns %Rrc\n", pszPath
, cchPath
, rc
);
488 size_t cch
= strlen(pszTmp
);
489 if (cch
>= sizeof(g_szrtProgramPath
))
492 printf("RTPathProgram(%p, %u): returns %Rrc\n", pszPath
, cchPath
, VERR_BUFFER_OVERFLOW
);
493 return VERR_BUFFER_OVERFLOW
;
495 memcpy(g_szrtProgramPath
, pszTmp
, cch
+ 1);
496 RTPathStripFilename(g_szrtProgramPath
);
501 * Calc the length and check if there is space before copying.
503 unsigned cch
= strlen(g_szrtProgramPath
) + 1;
506 memcpy(pszPath
, g_szrtProgramPath
, cch
+ 1);
507 printf("RTPathProgram(%p:{%s}, %u): returns %Rrc\n", pszPath
, pszPath
, cchPath
, VINF_SUCCESS
);
511 AssertMsgFailed(("Buffer too small (%d < %d)\n", cchPath
, cch
));
512 printf("RTPathProgram(%p, %u): returns %Rrc\n", pszPath
, cchPath
, VERR_BUFFER_OVERFLOW
);
513 return VERR_BUFFER_OVERFLOW
;
519 * Worker for RTPathUserHome that looks up the home directory
520 * using the getpwuid_r api.
522 * @returns IPRT status code.
523 * @param pszPath The path buffer.
524 * @param cchPath The size of the buffer.
525 * @param uid The User ID to query the home directory of.
527 static int rtPathUserHomeByPasswd(char *pszPath
, size_t cchPath
, uid_t uid
)
530 * The getpwuid_r function uses the passed in buffer to "allocate" any
531 * extra memory it needs. On some systems we should probably use the
532 * sysconf function to find the appropriate buffer size, but since it won't
533 * work everywhere we'll settle with a 5KB buffer and ASSUME that it'll
534 * suffice for even the lengthiest user descriptions...
536 char achBuffer
[5120];
537 struct passwd Passwd
;
538 struct passwd
*pPasswd
;
539 memset(&Passwd
, 0, sizeof(Passwd
));
540 int rc
= getpwuid_r(uid
, &Passwd
, &achBuffer
[0], sizeof(achBuffer
), &pPasswd
);
542 return RTErrConvertFromErrno(rc
);
543 if (!pPasswd
) /* uid not found in /etc/passwd */
544 return VERR_PATH_NOT_FOUND
;
547 * Check that it isn't empty and that it exists.
550 if ( !pPasswd
->pw_dir
552 || stat(pPasswd
->pw_dir
, &st
)
553 || !S_ISDIR(st
.st_mode
))
554 return VERR_PATH_NOT_FOUND
;
557 * Convert it to UTF-8 and copy it to the return buffer.
560 rc
= rtPathFromNative(&pszUtf8Path
, pPasswd
->pw_dir
);
563 size_t cchHome
= strlen(pszUtf8Path
);
564 if (cchHome
< cchPath
)
565 memcpy(pszPath
, pszUtf8Path
, cchHome
+ 1);
567 rc
= VERR_BUFFER_OVERFLOW
;
568 RTStrFree(pszUtf8Path
);
576 * Worker for RTPathUserHome that looks up the home directory
577 * using the HOME environment variable.
579 * @returns IPRT status code.
580 * @param pszPath The path buffer.
581 * @param cchPath The size of the buffer.
583 static int rtPathUserHomeByEnv(char *pszPath
, size_t cchPath
)
586 * Get HOME env. var it and validate it's existance.
588 int rc
= VERR_PATH_NOT_FOUND
;
589 const char *pszHome
= getenv("HOME");
594 if ( !stat(pszHome
, &st
)
595 && S_ISDIR(st
.st_mode
))
598 * Convert it to UTF-8 and copy it to the return buffer.
601 rc
= rtPathFromNative(&pszUtf8Path
, pszHome
);
604 size_t cchHome
= strlen(pszUtf8Path
);
605 if (cchHome
< cchPath
)
606 memcpy(pszPath
, pszUtf8Path
, cchHome
+ 1);
608 rc
= VERR_BUFFER_OVERFLOW
;
609 RTStrFree(pszUtf8Path
);
617 int RTPathUserHome(char *pszPath
, unsigned cchPath
)
622 * We make an exception for the root user and use the system call
623 * getpwuid_r to determine their initial home path instead of
624 * reading it from the $HOME variable. This is because the $HOME
625 * variable does not get changed by sudo (and possibly su and others)
626 * which can cause root-owned files to appear in user's home folders.
628 uid_t uid
= geteuid();
630 rc
= rtPathUserHomeByPasswd(pszPath
, cchPath
, uid
);
632 rc
= rtPathUserHomeByEnv(pszPath
, cchPath
);
635 * On failure, retry using the alternative method.
636 * (Should perhaps restrict the retry cases a bit more here...)
639 && rc
!= VERR_BUFFER_OVERFLOW
)
642 rc
= rtPathUserHomeByEnv(pszPath
, cchPath
);
644 rc
= rtPathUserHomeByPasswd(pszPath
, cchPath
, uid
);
647 rc
= rtPathUserHomeByEnv(pszPath
, cchPath
);
648 #endif /* RT_OS_L4 */
650 printf("RTPathUserHome(%p:{%s}, %u): returns %Rrc\n", pszPath
,
651 RT_SUCCESS(rc
) ? pszPath
: "<failed>", cchPath
, rc
);
656 int RTPathQueryInfo(const char *pszPath
, PRTFSOBJINFO pObjInfo
, RTFSOBJATTRADD enmAdditionalAttribs
)
661 AssertMsgReturn(VALID_PTR(pszPath
), ("%p\n", pszPath
), VERR_INVALID_POINTER
);
662 AssertReturn(*pszPath
, VERR_INVALID_PARAMETER
);
663 AssertMsgReturn(VALID_PTR(pObjInfo
), ("%p\n", pszPath
), VERR_INVALID_POINTER
);
664 AssertMsgReturn( enmAdditionalAttribs
>= RTFSOBJATTRADD_NOTHING
665 && enmAdditionalAttribs
<= RTFSOBJATTRADD_LAST
,
666 ("Invalid enmAdditionalAttribs=%p\n", enmAdditionalAttribs
),
667 VERR_INVALID_PARAMETER
);
670 * Convert the filename.
673 int rc
= rtPathToNative(&pszNativePath
, pszPath
);
677 if (!stat(pszNativePath
, &Stat
))
679 rtFsConvertStatToObjInfo(pObjInfo
, &Stat
, pszPath
, 0);
680 switch (enmAdditionalAttribs
)
682 case RTFSOBJATTRADD_EASIZE
:
683 /** @todo Use SGI extended attribute interface to query EA info. */
684 pObjInfo
->Attr
.enmAdditional
= RTFSOBJATTRADD_EASIZE
;
685 pObjInfo
->Attr
.u
.EASize
.cb
= 0;
688 case RTFSOBJATTRADD_NOTHING
:
689 case RTFSOBJATTRADD_UNIX
:
690 Assert(pObjInfo
->Attr
.enmAdditional
== RTFSOBJATTRADD_UNIX
);
694 AssertMsgFailed(("Impossible!\n"));
695 return VERR_INTERNAL_ERROR
;
699 rc
= RTErrConvertFromErrno(errno
);
700 rtPathFreeNative(pszNativePath
);
703 printf("RTPathQueryInfo(%p:{%s}, pObjInfo=%p, %d): returns %Rrc\n",
704 pszPath
, pszPath
, pObjInfo
, enmAdditionalAttribs
, rc
);
709 int RTPathSetTimes(const char *pszPath
, PCRTTIMESPEC pAccessTime
, PCRTTIMESPEC pModificationTime
,
710 PCRTTIMESPEC pChangeTime
, PCRTTIMESPEC pBirthTime
)
715 AssertMsgReturn(VALID_PTR(pszPath
), ("%p\n", pszPath
), VERR_INVALID_POINTER
);
716 AssertMsgReturn(*pszPath
, ("%p\n", pszPath
), VERR_INVALID_PARAMETER
);
717 AssertMsgReturn(!pAccessTime
|| VALID_PTR(pAccessTime
), ("%p\n", pAccessTime
), VERR_INVALID_POINTER
);
718 AssertMsgReturn(!pModificationTime
|| VALID_PTR(pModificationTime
), ("%p\n", pModificationTime
), VERR_INVALID_POINTER
);
719 AssertMsgReturn(!pChangeTime
|| VALID_PTR(pChangeTime
), ("%p\n", pChangeTime
), VERR_INVALID_POINTER
);
720 AssertMsgReturn(!pBirthTime
|| VALID_PTR(pBirthTime
), ("%p\n", pBirthTime
), VERR_INVALID_POINTER
);
726 int rc
= rtPathToNative(&pszNativePath
, pszPath
);
730 * If it's a no-op, we'll only verify the existance of the file.
732 if (!pAccessTime
&& !pModificationTime
)
735 if (!stat(pszNativePath
, &Stat
))
739 rc
= RTErrConvertFromErrno(errno
);
740 printf("RTPathSetTimes('%s',,,,): failed with %Rrc and errno=%d\n", pszPath
, rc
, errno
);
746 * Convert the input to timeval, getting the missing one if necessary,
747 * and call the API which does the change.
749 struct timeval aTimevals
[2];
750 if (pAccessTime
&& pModificationTime
)
752 RTTimeSpecGetTimeval(pAccessTime
, &aTimevals
[0]);
753 RTTimeSpecGetTimeval(pModificationTime
, &aTimevals
[1]);
758 int rc
= RTPathQueryInfo(pszPath
, &ObjInfo
, RTFSOBJATTRADD_UNIX
);
761 RTTimeSpecGetTimeval(pAccessTime
? pAccessTime
: &ObjInfo
.AccessTime
, &aTimevals
[0]);
762 RTTimeSpecGetTimeval(pModificationTime
? pModificationTime
: &ObjInfo
.ModificationTime
, &aTimevals
[1]);
765 printf("RTPathSetTimes('%s',%p,%p,,): RTPathQueryInfo failed with %Rrc\n",
766 pszPath
, pAccessTime
, pModificationTime
, rc
);
770 if (utimes(pszNativePath
, aTimevals
))
772 rc
= RTErrConvertFromErrno(errno
);
773 printf("RTPathSetTimes('%s',%p,%p,,): failed with %Rrc and errno=%d\n",
774 pszPath
, pAccessTime
, pModificationTime
, rc
, errno
);
778 rtPathFreeNative(pszNativePath
);
781 printf("RTPathSetTimes(%p:{%s}, %p:{%RDtimespec}, %p:{%RDtimespec}, %p:{%RDtimespec}, %p:{%RDtimespec}): return %Rrc\n",
782 pszPath
, pszPath
, pAccessTime
, pAccessTime
, pModificationTime
, pModificationTime
,
783 pChangeTime
, pChangeTime
, pBirthTime
, pBirthTime
);
789 * Checks if two files are the one and same file.
791 static bool rtPathSame(const char *pszNativeSrc
, const char *pszNativeDst
)
794 if (stat(pszNativeSrc
, &SrcStat
))
797 if (stat(pszNativeDst
, &DstStat
))
799 Assert(SrcStat
.st_dev
&& DstStat
.st_dev
);
800 Assert(SrcStat
.st_ino
&& DstStat
.st_ino
);
801 if ( SrcStat
.st_dev
== DstStat
.st_dev
802 && SrcStat
.st_ino
== DstStat
.st_ino
803 && (SrcStat
.st_mode
& S_IFMT
) == (DstStat
.st_mode
& S_IFMT
))
810 * Worker for RTPathRename, RTDirRename, RTFileRename.
812 * @returns IPRT status code.
813 * @param pszSrc The source path.
814 * @param pszDst The destintation path.
815 * @param fRename The rename flags.
816 * @param fFileType The filetype. We use the RTFMODE filetypes here. If it's 0,
817 * anything goes. If it's RTFS_TYPE_DIRECTORY we'll check that the
818 * source is a directory. If Its RTFS_TYPE_FILE we'll check that it's
819 * not a directory (we are NOT checking whether it's a file).
821 int rtPathPosixRename(const char *pszSrc
, const char *pszDst
, unsigned fRename
, RTFMODE fFileType
)
827 int rc
= rtPathToNative(&pszNativeSrc
, pszSrc
);
831 rc
= rtPathToNative(&pszNativeDst
, pszDst
);
835 * Check that the source exists and that any types that's specified matches.
836 * We have to check this first to avoid getting errnous VERR_ALREADY_EXISTS
837 * errors from the next step.
839 * There are race conditions here (perhaps unlikly ones but still), but I'm
840 * afraid there is little with can do to fix that.
843 if (stat(pszNativeSrc
, &SrcStat
))
844 rc
= RTErrConvertFromErrno(errno
);
847 else if (RTFS_IS_DIRECTORY(fFileType
))
848 rc
= S_ISDIR(SrcStat
.st_mode
) ? VINF_SUCCESS
: VERR_NOT_A_DIRECTORY
;
850 rc
= S_ISDIR(SrcStat
.st_mode
) ? VERR_IS_A_DIRECTORY
: VINF_SUCCESS
;
853 bool fSameFile
= false;
856 * Check if the target exists, rename is rather destructive.
857 * We'll have to make sure we don't overwrite the source!
858 * Another race condition btw.
861 if (stat(pszNativeDst
, &DstStat
))
862 rc
= errno
== ENOENT
? VINF_SUCCESS
: RTErrConvertFromErrno(errno
);
865 Assert(SrcStat
.st_dev
&& DstStat
.st_dev
);
866 Assert(SrcStat
.st_ino
&& DstStat
.st_ino
);
867 if ( SrcStat
.st_dev
== DstStat
.st_dev
868 && SrcStat
.st_ino
== DstStat
.st_ino
869 && (SrcStat
.st_mode
& S_IFMT
) == (SrcStat
.st_mode
& S_IFMT
))
872 * It's likely that we're talking about the same file here.
873 * We should probably check paths or whatever, but for now this'll have to be enough.
879 else if (S_ISDIR(DstStat
.st_mode
) || !(fRename
& RTPATHRENAME_FLAGS_REPLACE
))
880 rc
= VERR_ALREADY_EXISTS
;
887 if (!rename(pszNativeSrc
, pszNativeDst
))
889 else if ( (fRename
& RTPATHRENAME_FLAGS_REPLACE
)
890 && (errno
== ENOTDIR
|| errno
== EEXIST
))
893 * Check that the destination isn't a directory.
894 * Yet another race condition.
896 if (rtPathSame(pszNativeSrc
, pszNativeDst
))
899 printf("rtPathRename('%s', '%s', %#x ,%RTfmode): appears to be the same file... (errno=%d)\n",
900 pszSrc
, pszDst
, fRename
, fFileType
, errno
);
904 if (stat(pszNativeDst
, &DstStat
))
905 rc
= errno
!= ENOENT
? RTErrConvertFromErrno(errno
) : VINF_SUCCESS
;
906 else if (S_ISDIR(DstStat
.st_mode
))
907 rc
= VERR_ALREADY_EXISTS
;
912 if (!unlink(pszNativeDst
))
914 if (!rename(pszNativeSrc
, pszNativeDst
))
918 rc
= RTErrConvertFromErrno(errno
);
919 printf("rtPathRename('%s', '%s', %#x ,%RTfmode): rename failed rc=%Rrc errno=%d\n",
920 pszSrc
, pszDst
, fRename
, fFileType
, rc
, errno
);
925 rc
= RTErrConvertFromErrno(errno
);
926 printf("rtPathRename('%s', '%s', %#x ,%RTfmode): failed to unlink dst rc=%Rrc errno=%d\n",
927 pszSrc
, pszDst
, fRename
, fFileType
, rc
, errno
);
931 printf("rtPathRename('%s', '%s', %#x ,%RTfmode): dst !dir check failed rc=%Rrc\n",
932 pszSrc
, pszDst
, fRename
, fFileType
, rc
);
937 rc
= RTErrConvertFromErrno(errno
);
938 if (errno
== ENOTDIR
)
939 rc
= VERR_ALREADY_EXISTS
; /* unless somebody is racing us, this is the right interpretation */
940 printf("rtPathRename('%s', '%s', %#x ,%RTfmode): rename failed rc=%Rrc errno=%d\n",
941 pszSrc
, pszDst
, fRename
, fFileType
, rc
, errno
);
945 printf("rtPathRename('%s', '%s', %#x ,%RTfmode): destination check failed rc=%Rrc errno=%d\n",
946 pszSrc
, pszDst
, fRename
, fFileType
, rc
, errno
);
949 printf("rtPathRename('%s', '%s', %#x ,%RTfmode): source type check failed rc=%Rrc errno=%d\n",
950 pszSrc
, pszDst
, fRename
, fFileType
, rc
, errno
);
952 rtPathFreeNative(pszNativeDst
);
954 rtPathFreeNative(pszNativeSrc
);
960 int RTPathRename(const char *pszSrc
, const char *pszDst
, unsigned fRename
)
965 AssertMsgReturn(VALID_PTR(pszSrc
), ("%p\n", pszSrc
), VERR_INVALID_POINTER
);
966 AssertMsgReturn(VALID_PTR(pszDst
), ("%p\n", pszDst
), VERR_INVALID_POINTER
);
967 AssertMsgReturn(*pszSrc
, ("%p\n", pszSrc
), VERR_INVALID_PARAMETER
);
968 AssertMsgReturn(*pszDst
, ("%p\n", pszDst
), VERR_INVALID_PARAMETER
);
969 AssertMsgReturn(!(fRename
& ~RTPATHRENAME_FLAGS_REPLACE
), ("%#x\n", fRename
), VERR_INVALID_PARAMETER
);
972 * Hand it to the worker.
974 int rc
= rtPathPosixRename(pszSrc
, pszDst
, fRename
, 0);
976 printf("RTPathRename(%p:{%s}, %p:{%s}, %#x): returns %Rrc\n", pszSrc
, pszSrc
, pszDst
, pszDst
, fRename
, rc
);
981 bool RTPathExists(const char *pszPath
)
986 AssertPtrReturn(pszPath
, false);
987 AssertReturn(*pszPath
, false);
990 * Convert the path and check if it exists using stat().
993 int rc
= rtPathToNative(&pszNativePath
, pszPath
);
997 if (!stat(pszNativePath
, &Stat
))
1000 rc
= VERR_GENERAL_FAILURE
;
1001 RTStrFree(pszNativePath
);
1003 return RT_SUCCESS(rc
);
1007 * Finds the filename in a path.
1009 * @returns Pointer to filename within pszPath.
1010 * @returns NULL if no filename (i.e. empty string or ends with a slash).
1011 * @param pszPath Path to find filename in.
1013 char* RTPathFilename(const char *pszPath
)
1015 const char *psz
= pszPath
;
1016 const char *pszLastComp
= pszPath
;
1022 /* handle separators. */
1023 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
1025 pszLastComp
= psz
+ 1;
1031 pszLastComp
= psz
+ 1;
1037 return (char *)(void *)pszLastComp
;
1042 /* will never get here */
1047 * Strips the filename from a path.
1049 * @param pszPath Path which filename should be extracted from.
1052 void RTPathStripFilename(char *pszPath
)
1054 char *psz
= pszPath
;
1055 char *pszLastSep
= pszPath
;
1061 /* handle separators. */
1062 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
1064 pszLastSep
= psz
+ 1;
1075 if (pszLastSep
== pszPath
)
1076 *pszLastSep
++ = '.';
1081 /* will never get here */
1085 * Free string allocated by any of the non-UCS-2 string functions.
1087 * @returns iprt status code.
1088 * @param pszString Pointer to buffer with string to free.
1091 void RTStrFree(char *pszString
)
1094 RTMemTmpFree(pszString
);
1098 * Allocates tmp buffer, translates pszString from UTF8 to current codepage.
1100 * @returns iprt status code.
1101 * @param ppszString Receives pointer of allocated native CP string.
1102 * The returned pointer must be freed using RTStrFree().
1103 * @param pszString UTF-8 string to convert.
1105 int RTStrUtf8ToCurrentCP(char **ppszString
, const char *pszString
)
1112 * Assume result string length is not longer than UTF-8 string.
1114 size_t cch
= strlen(pszString
);
1117 /* zero length string passed. */
1118 *ppszString
= (char *)RTMemTmpAllocZ(sizeof(char));
1120 return VINF_SUCCESS
;
1121 return VERR_NO_TMP_MEMORY
;
1123 return rtstrConvert(pszString
, cch
, "UTF-8", (void **)ppszString
, 0, "", 1);
1128 * Allocates tmp buffer, translates pszString from current codepage to UTF-8.
1130 * @returns iprt status code.
1131 * @param ppszString Receives pointer of allocated UTF-8 string.
1132 * The returned pointer must be freed using RTStrFree().
1133 * @param pszString Native string to convert.
1135 int RTStrCurrentCPToUtf8(char **ppszString
, const char *pszString
)
1142 * Attempt with UTF-8 length of 2x the native lenght.
1144 size_t cch
= strlen(pszString
);
1147 /* zero length string passed. */
1148 *ppszString
= (char *)RTMemTmpAllocZ(sizeof(char));
1150 return VINF_SUCCESS
;
1151 return VERR_NO_TMP_MEMORY
;
1153 return rtstrConvert(pszString
, cch
, "", (void **)ppszString
, 0, "UTF-8", 2);
1157 * Converts a string from one charset to another.
1159 * @returns iprt status code.
1160 * @param pvInput Pointer to intput string.
1161 * @param cbInput Size (in bytes) of input string. Excludes any terminators.
1162 * @param pszInputCS Codeset of the input string.
1163 * @param ppvOutput Pointer to pointer to output buffer if cbOutput > 0.
1164 * If cbOutput is 0 this is where the pointer to the allocated
1166 * @param cbOutput Size of the passed in buffer.
1167 * @param pszOutputCS Codeset of the input string.
1168 * @param cFactor Input vs. output size factor.
1170 static int rtstrConvert(const void *pvInput
, size_t cbInput
, const char *pszInputCS
, void **ppvOutput
, size_t cbOutput
, const char *pszOutputCS
, unsigned cFactor
)
1179 cbOutput2
= cbInput
* cFactor
;
1180 pvOutput
= RTMemTmpAlloc(cbOutput2
+ sizeof(RTUCS2
));
1182 return VERR_NO_TMP_MEMORY
;
1186 pvOutput
= *ppvOutput
;
1187 cbOutput2
= cbOutput
- (!strcmp(pszOutputCS
, "UCS-2") ? sizeof(RTUCS2
) : 1);
1188 if (cbOutput2
> cbOutput
)
1189 return VERR_BUFFER_OVERFLOW
;
1193 * Use a loop here to retry with bigger buffers.
1195 for (unsigned cTries
= 10; cTries
> 0; cTries
--)
1198 * Create conversion object.
1200 #ifdef RT_OS_SOLARIS
1201 /* Solaris doesn't grok empty codeset strings, so help it find the current codeset. */
1203 pszInputCS
= nl_langinfo(CODESET
);
1205 pszOutputCS
= nl_langinfo(CODESET
);
1207 iconv_t icHandle
= iconv_open(pszOutputCS
, pszInputCS
);
1208 if (icHandle
!= (iconv_t
)-1)
1211 * Do the conversion.
1213 size_t cbInLeft
= cbInput
;
1214 size_t cbOutLeft
= cbOutput2
;
1215 const void *pvInputLeft
= pvInput
;
1216 void *pvOutputLeft
= pvOutput
;
1217 #ifdef RT_OS_LINUX /* glibc has an incorrect declaration of the api. */
1218 if (iconv(icHandle
, (char **)&pvInputLeft
, &cbInLeft
, (char **)&pvOutputLeft
, &cbOutLeft
) != (size_t)-1)
1220 if (iconv(icHandle
, (const char **)&pvInputLeft
, &cbInLeft
, (char **)&pvOutputLeft
, &cbOutLeft
) != (size_t)-1)
1226 * We're done, just add the terminator and return.
1227 * (Two terminators to support UCS-2 output, too.)
1229 iconv_close(icHandle
);
1230 if (!cbOutput
|| !strcmp(pszOutputCS
, "UCS-2"))
1231 *(PRTUCS2
)pvOutputLeft
= '\0';
1233 *(char *)pvOutputLeft
= '\0';
1234 *ppvOutput
= pvOutput
;
1235 return VINF_SUCCESS
;
1240 iconv_close(icHandle
);
1243 * If we failed because of output buffer space we'll
1244 * increase the output buffer size and retry.
1250 RTMemTmpFree(pvOutput
);
1252 pvOutput
= RTMemTmpAlloc(cbOutput2
);
1254 return VERR_NO_TMP_MEMORY
;
1257 return VERR_BUFFER_OVERFLOW
;
1265 RTMemTmpFree(pvOutput
);
1266 return VERR_NO_TRANSLATION
;