Close all vdi's before exit, by Daniel.
[vdi_driver.git] / src / path.cpp
blob6beafdbecb01b408b2458fb1d7b5ea18c68a2eb3
1 /* $Id: path-posix.cpp 26050 2007-11-12 12:11:12Z lelik $ */
2 /** @file
3 * innotek Portable Runtime - Path Manipulation, POSIX.
4 */
6 /*
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 /*******************************************************************************
20 * Header Files *
21 *******************************************************************************/
22 #define LOG_GROUP RTLOGGROUP_PATH
23 #include <stdlib.h>
24 #include <limits.h>
25 #include <errno.h>
26 #include <unistd.h>
27 #include <sys/stat.h>
28 #include <sys/time.h>
29 #include <stdio.h>
30 #include <sys/types.h>
31 #include <pwd.h>
32 #include <cstring>
33 #include <cstdio>
34 #include <iconv.h>
36 #include "path.h"
37 #include "fs.h"
38 #include "config.h"
39 #include "const_defines.h"
40 #include "RTErrConvertFromErrno.h"
41 #include "func_defines.h"
42 #include "alloc.h"
43 #include "types.h"
45 /*******************************************************************************
46 * Global Variables *
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)
59 * Convert input.
61 char *pszNativePath;
62 int rc = rtPathToNative(&pszNativePath, pszPath);
63 if (RT_SUCCESS(rc))
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);
71 if (psz)
74 * Convert result and copy it to the return buffer.
76 char *pszUtf8RealPath;
77 rc = rtPathFromNative(&pszUtf8RealPath, szTmpPath);
78 if (RT_SUCCESS(rc))
80 size_t cch = strlen(pszUtf8RealPath) + 1;
81 if (cch <= cchRealPath)
82 memcpy(pszRealPath, pszUtf8RealPath, cch);
83 else
84 rc = VERR_BUFFER_OVERFLOW;
85 RTStrFree(pszUtf8RealPath);
88 else
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);
95 return rc;
98 int rtPathToNative(char **ppszNativePath, const char *pszPath)
100 return RTStrUtf8ToCurrentCP(ppszNativePath, pszPath);
103 int rtPathToNativeEx(char **ppszNativePath, const char *pszPath, const char *pszBasePath)
105 NOREF(pszBasePath);
106 return RTStrUtf8ToCurrentCP(ppszNativePath, pszPath);
109 void rtPathFreeNative(char *pszNativePath)
111 if (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)
124 NOREF(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;
145 #ifdef HAVE_UNC
146 int fUnc = 0;
147 if ( RTPATH_IS_SLASH(pszPath[0])
148 && RTPATH_IS_SLASH(pszPath[1]))
149 { /* Skip first slash in a unc path. */
150 pszSrc++;
151 *pszTrg++ = '/';
152 fUnc = 1;
154 #endif
156 for (;;)
158 char ch = *pszSrc++;
159 if (RTPATH_IS_SLASH(ch))
161 *pszTrg++ = '/';
162 for (;;)
164 do ch = *pszSrc++;
165 while (RTPATH_IS_SLASH(ch));
167 /* Remove '/./' and '/.'. */
168 if (ch != '.' || (*pszSrc && !RTPATH_IS_SLASH(*pszSrc)))
169 break;
172 *pszTrg = ch;
173 if (!ch)
174 break;
175 pszTrg++;
179 * Remove trailing slash if the path may be pointing to a directory.
181 int cch = pszTrg - pszPath;
182 if ( cch > 1
183 && RTPATH_IS_SLASH(pszTrg[-1])
184 #ifdef HAVE_DRIVE
185 && !RTPATH_IS_VOLSEP(pszTrg[-2])
186 #endif
187 && !RTPATH_IS_SLASH(pszTrg[-2]))
188 pszPath[--cch] = '\0';
190 return cch;
194 int RTPathAbs(const char *pszPath, char *pszAbsPath, unsigned cchAbsPath)
197 * Convert input.
199 char *pszNativePath;
200 int rc = rtPathToNative(&pszNativePath, pszPath);
201 if (RT_FAILURE(rc))
203 printf("RTPathAbs(%p:{%s}, %p, %d): returns %Rrc\n", pszPath,
204 pszPath, pszAbsPath, cchAbsPath, rc);
205 return 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);
214 if (!psz)
216 if (errno == ENOENT || errno == ENOTDIR
217 #ifdef RT_OS_OS2
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
221 // this todo.
222 || errno == EIO
223 #endif
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;
239 #ifdef HAVE_DRIVE
240 if (pszCur[0] && RTPATH_IS_VOLSEP(pszCur[1]) && pszCur[2] == '/')
242 psz = szTmpPath;
243 cch = 2;
244 pszCur += 3;
246 #ifdef HAVE_UNC
247 else
248 if (pszCur[0] == '/' && pszCur[1] == '/')
250 pszCur += 2;
251 char *pszSlash = strchr(pszCur, '/');
252 size_t cchElement = pszSlash ? pszSlash - pszCur : strlen(pszCur);
253 if (cchElement && pszCur[cchElement])
255 psz = szTmpPath;
256 cch = cchElement + 2;
257 pszCur += cchElement + 1;
259 else
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;
266 #endif
267 #else
268 if (*pszCur == '/')
270 psz = szTmpPath;
271 pszCur++;
273 #endif
274 else
276 /* get the cwd */
277 psz = getcwd(szTmpPath, sizeof(szTmpPath));
278 AssertMsg(psz, ("Couldn't get cwd!\n"));
279 if (psz)
281 #ifdef HAVE_DRIVE
282 if (*pszCur == '/')
284 cch = 2;
285 pszCur++;
287 else
288 #endif
289 cch = strlen(psz);
291 else
292 rc = RTErrConvertFromErrno(errno);
295 if (psz)
297 bool fResolveSymlinks = true;
298 char szTmpPath2[PATH_MAX + 1];
300 /* make sure strrchr() will work correctly */
301 psz[cch] = '\0';
303 while (*pszCur)
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;
310 break;
313 if (!strncmp(pszCur, "..", cchElement))
315 char *pszLastSlash = strrchr(psz, '/');
316 #ifdef HAVE_UNC
317 if (pszLastSlash && pszLastSlash > psz &&
318 pszLastSlash[-1] != '/')
319 #else
320 if (pszLastSlash)
321 #endif
323 cch = pszLastSlash - psz;
324 psz[cch] = '\0';
326 /* else: We've reached the root and the parent of
327 * the root is the root. */
329 else
331 psz[cch++] = '/';
332 memcpy(psz + cch, pszCur, cchElement);
333 cch += cchElement;
334 psz[cch] = '\0';
336 if (fResolveSymlinks)
338 /* resolve possible symlinks */
339 char *psz2 = realpath(psz, psz == szTmpPath
340 ? szTmpPath2
341 : szTmpPath);
342 if (psz2)
344 psz = psz2;
345 cch = strlen(psz);
347 else
349 if (errno != ENOENT && errno != ENOTDIR
350 #ifdef RT_OS_OS2
351 /// @todo see above
352 && errno != EIO
353 #endif
356 rc = RTErrConvertFromErrno(errno);
357 break;
360 /* no more need to resolve symlinks */
361 fResolveSymlinks = false;
366 pszCur += cchElement;
367 /* skip the slash */
368 if (*pszCur)
369 ++pszCur;
372 #ifdef HAVE_DRIVE
373 /* check if we're at the root */
374 if (cch == 2 && RTPATH_IS_VOLSEP(psz[1]))
375 #else
376 /* if the length is zero here, then we're at the root */
377 if (!cch)
378 #endif
380 psz[cch++] = '/';
381 psz[cch] = '\0';
385 else
386 rc = VERR_FILENAME_TOO_LONG;
388 else
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);
401 if (RT_FAILURE(rc))
403 printf("RTPathAbs(%p:{%s}, %p, %d): returns %Rrc\n", pszPath,
404 pszPath, pszAbsPath, cchAbsPath, rc);
405 return rc;
408 /* replace '/' back with native RTPATH_SLASH */
409 psz = pszUtf8AbsPath;
410 for (; *psz; psz++)
411 if (*psz == '/')
412 *psz = RTPATH_SLASH;
414 unsigned cch = strlen(pszUtf8AbsPath) + 1;
415 if (cch <= cchAbsPath)
416 memcpy(pszAbsPath, pszUtf8AbsPath, cch);
417 else
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>",
424 cchAbsPath, rc);
425 return rc;
429 int RTPathProgram(char *pszPath, unsigned cchPath)
432 * First time only.
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)
444 # ifdef RT_OS_LINUX
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);
453 # endif
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);
459 return 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);
474 #else
475 # error needs porting.
476 #endif
479 * Convert to UTF-8 and strip of the filename.
481 char *pszTmp = NULL;
482 int rc = rtPathFromNative(&pszTmp, &g_szrtProgramPath[0]);
483 if (RT_FAILURE(rc))
485 printf("RTPathProgram(%p, %u): returns %Rrc\n", pszPath, cchPath, rc);
486 return rc;
488 size_t cch = strlen(pszTmp);
489 if (cch >= sizeof(g_szrtProgramPath))
491 RTStrFree(pszTmp);
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);
497 RTStrFree(pszTmp);
501 * Calc the length and check if there is space before copying.
503 unsigned cch = strlen(g_szrtProgramPath) + 1;
504 if (cch <= cchPath)
506 memcpy(pszPath, g_szrtProgramPath, cch + 1);
507 printf("RTPathProgram(%p:{%s}, %u): returns %Rrc\n", pszPath, pszPath, cchPath, VINF_SUCCESS);
508 return 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;
517 #ifndef RT_OS_L4
518 /**
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);
541 if (rc != 0)
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.
549 struct stat st;
550 if ( !pPasswd->pw_dir
551 || !*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.
559 char *pszUtf8Path;
560 rc = rtPathFromNative(&pszUtf8Path, pPasswd->pw_dir);
561 if (RT_SUCCESS(rc))
563 size_t cchHome = strlen(pszUtf8Path);
564 if (cchHome < cchPath)
565 memcpy(pszPath, pszUtf8Path, cchHome + 1);
566 else
567 rc = VERR_BUFFER_OVERFLOW;
568 RTStrFree(pszUtf8Path);
570 return rc;
572 #endif
575 /**
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");
590 if (!pszHome)
593 struct stat st;
594 if ( !stat(pszHome, &st)
595 && S_ISDIR(st.st_mode))
598 * Convert it to UTF-8 and copy it to the return buffer.
600 char *pszUtf8Path;
601 rc = rtPathFromNative(&pszUtf8Path, pszHome);
602 if (RT_SUCCESS(rc))
604 size_t cchHome = strlen(pszUtf8Path);
605 if (cchHome < cchPath)
606 memcpy(pszPath, pszUtf8Path, cchHome + 1);
607 else
608 rc = VERR_BUFFER_OVERFLOW;
609 RTStrFree(pszUtf8Path);
613 return rc;
617 int RTPathUserHome(char *pszPath, unsigned cchPath)
619 int rc;
620 #ifndef RT_OS_L4
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();
629 if (!uid)
630 rc = rtPathUserHomeByPasswd(pszPath, cchPath, uid);
631 else
632 rc = rtPathUserHomeByEnv(pszPath, cchPath);
635 * On failure, retry using the alternative method.
636 * (Should perhaps restrict the retry cases a bit more here...)
638 if ( RT_FAILURE(rc)
639 && rc != VERR_BUFFER_OVERFLOW)
641 if (!uid)
642 rc = rtPathUserHomeByEnv(pszPath, cchPath);
643 else
644 rc = rtPathUserHomeByPasswd(pszPath, cchPath, uid);
646 #else /* RT_OS_L4 */
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);
652 return rc;
656 int RTPathQueryInfo(const char *pszPath, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs)
659 * Validate input.
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.
672 char *pszNativePath;
673 int rc = rtPathToNative(&pszNativePath, pszPath);
674 if (RT_SUCCESS(rc))
676 struct stat Stat;
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;
686 break;
688 case RTFSOBJATTRADD_NOTHING:
689 case RTFSOBJATTRADD_UNIX:
690 Assert(pObjInfo->Attr.enmAdditional == RTFSOBJATTRADD_UNIX);
691 break;
693 default:
694 AssertMsgFailed(("Impossible!\n"));
695 return VERR_INTERNAL_ERROR;
698 else
699 rc = RTErrConvertFromErrno(errno);
700 rtPathFreeNative(pszNativePath);
703 printf("RTPathQueryInfo(%p:{%s}, pObjInfo=%p, %d): returns %Rrc\n",
704 pszPath, pszPath, pObjInfo, enmAdditionalAttribs, rc);
705 return rc;
709 int RTPathSetTimes(const char *pszPath, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
710 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
713 * Validate input.
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);
723 * Convert the paths.
725 char *pszNativePath;
726 int rc = rtPathToNative(&pszNativePath, pszPath);
727 if (RT_SUCCESS(rc))
730 * If it's a no-op, we'll only verify the existance of the file.
732 if (!pAccessTime && !pModificationTime)
734 struct stat Stat;
735 if (!stat(pszNativePath, &Stat))
736 rc = VINF_SUCCESS;
737 else
739 rc = RTErrConvertFromErrno(errno);
740 printf("RTPathSetTimes('%s',,,,): failed with %Rrc and errno=%d\n", pszPath, rc, errno);
743 else
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]);
755 else
757 RTFSOBJINFO ObjInfo;
758 int rc = RTPathQueryInfo(pszPath, &ObjInfo, RTFSOBJATTRADD_UNIX);
759 if (RT_SUCCESS(rc))
761 RTTimeSpecGetTimeval(pAccessTime ? pAccessTime : &ObjInfo.AccessTime, &aTimevals[0]);
762 RTTimeSpecGetTimeval(pModificationTime ? pModificationTime : &ObjInfo.ModificationTime, &aTimevals[1]);
764 else
765 printf("RTPathSetTimes('%s',%p,%p,,): RTPathQueryInfo failed with %Rrc\n",
766 pszPath, pAccessTime, pModificationTime, rc);
768 if (RT_SUCCESS(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);
784 return rc;
789 * Checks if two files are the one and same file.
791 static bool rtPathSame(const char *pszNativeSrc, const char *pszNativeDst)
793 struct stat SrcStat;
794 if (stat(pszNativeSrc, &SrcStat))
795 return false;
796 struct stat DstStat;
797 if (stat(pszNativeDst, &DstStat))
798 return false;
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))
804 return true;
805 return false;
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)
824 * Convert the paths.
826 char *pszNativeSrc;
827 int rc = rtPathToNative(&pszNativeSrc, pszSrc);
828 if (RT_SUCCESS(rc))
830 char *pszNativeDst;
831 rc = rtPathToNative(&pszNativeDst, pszDst);
832 if (RT_SUCCESS(rc))
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.
842 struct stat SrcStat;
843 if (stat(pszNativeSrc, &SrcStat))
844 rc = RTErrConvertFromErrno(errno);
845 else if (!fFileType)
846 rc = VINF_SUCCESS;
847 else if (RTFS_IS_DIRECTORY(fFileType))
848 rc = S_ISDIR(SrcStat.st_mode) ? VINF_SUCCESS : VERR_NOT_A_DIRECTORY;
849 else
850 rc = S_ISDIR(SrcStat.st_mode) ? VERR_IS_A_DIRECTORY : VINF_SUCCESS;
851 if (RT_SUCCESS(rc))
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.
860 struct stat DstStat;
861 if (stat(pszNativeDst, &DstStat))
862 rc = errno == ENOENT ? VINF_SUCCESS : RTErrConvertFromErrno(errno);
863 else
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.
875 fSameFile = true;
877 if (fSameFile)
878 rc = VINF_SUCCESS;
879 else if (S_ISDIR(DstStat.st_mode) || !(fRename & RTPATHRENAME_FLAGS_REPLACE))
880 rc = VERR_ALREADY_EXISTS;
881 else
882 rc = VINF_SUCCESS;
885 if (RT_SUCCESS(rc))
887 if (!rename(pszNativeSrc, pszNativeDst))
888 rc = VINF_SUCCESS;
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))
898 rc = VINF_SUCCESS;
899 printf("rtPathRename('%s', '%s', %#x ,%RTfmode): appears to be the same file... (errno=%d)\n",
900 pszSrc, pszDst, fRename, fFileType, errno);
902 else
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;
908 else
909 rc = VINF_SUCCESS;
910 if (RT_SUCCESS(rc))
912 if (!unlink(pszNativeDst))
914 if (!rename(pszNativeSrc, pszNativeDst))
915 rc = VINF_SUCCESS;
916 else
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);
923 else
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);
930 else
931 printf("rtPathRename('%s', '%s', %#x ,%RTfmode): dst !dir check failed rc=%Rrc\n",
932 pszSrc, pszDst, fRename, fFileType, rc);
935 else
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);
944 else
945 printf("rtPathRename('%s', '%s', %#x ,%RTfmode): destination check failed rc=%Rrc errno=%d\n",
946 pszSrc, pszDst, fRename, fFileType, rc, errno);
948 else
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);
956 return rc;
960 int RTPathRename(const char *pszSrc, const char *pszDst, unsigned fRename)
963 * Validate input.
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);
977 return rc;
981 bool RTPathExists(const char *pszPath)
984 * Validate input.
986 AssertPtrReturn(pszPath, false);
987 AssertReturn(*pszPath, false);
990 * Convert the path and check if it exists using stat().
992 char *pszNativePath;
993 int rc = rtPathToNative(&pszNativePath, pszPath);
994 if (RT_SUCCESS(rc))
996 struct stat Stat;
997 if (!stat(pszNativePath, &Stat))
998 rc = VINF_SUCCESS;
999 else
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;
1018 for (;; psz++)
1020 switch (*psz)
1022 /* handle separators. */
1023 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
1024 case ':':
1025 pszLastComp = psz + 1;
1026 break;
1028 case '\\':
1029 #endif
1030 case '/':
1031 pszLastComp = psz + 1;
1032 break;
1034 /* the end */
1035 case '\0':
1036 if (*pszLastComp)
1037 return (char *)(void *)pszLastComp;
1038 return NULL;
1042 /* will never get here */
1043 return NULL;
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;
1057 for (;; psz++)
1059 switch (*psz)
1061 /* handle separators. */
1062 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
1063 case ':':
1064 pszLastSep = psz + 1;
1065 break;
1067 case '\\':
1068 #endif
1069 case '/':
1070 pszLastSep = psz;
1071 break;
1073 /* the end */
1074 case '\0':
1075 if (pszLastSep == pszPath)
1076 *pszLastSep++ = '.';
1077 *pszLastSep = '\0';
1078 return;
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.
1089 * NULL is accepted.
1091 void RTStrFree(char *pszString)
1093 if (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)
1107 Assert(ppszString);
1108 Assert(pszString);
1109 *ppszString = NULL;
1112 * Assume result string length is not longer than UTF-8 string.
1114 size_t cch = strlen(pszString);
1115 if (cch <= 0)
1117 /* zero length string passed. */
1118 *ppszString = (char *)RTMemTmpAllocZ(sizeof(char));
1119 if (*ppszString)
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)
1137 Assert(ppszString);
1138 Assert(pszString);
1139 *ppszString = NULL;
1142 * Attempt with UTF-8 length of 2x the native lenght.
1144 size_t cch = strlen(pszString);
1145 if (cch <= 0)
1147 /* zero length string passed. */
1148 *ppszString = (char *)RTMemTmpAllocZ(sizeof(char));
1149 if (*ppszString)
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
1165 * buffer is stored.
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)
1173 * Allocate buffer
1175 void *pvOutput;
1176 size_t cbOutput2;
1177 if (!cbOutput)
1179 cbOutput2 = cbInput * cFactor;
1180 pvOutput = RTMemTmpAlloc(cbOutput2 + sizeof(RTUCS2));
1181 if (!pvOutput)
1182 return VERR_NO_TMP_MEMORY;
1184 else
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. */
1202 if (!*pszInputCS)
1203 pszInputCS = nl_langinfo(CODESET);
1204 if (!*pszOutputCS)
1205 pszOutputCS = nl_langinfo(CODESET);
1206 #endif
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)
1219 #else
1220 if (iconv(icHandle, (const char **)&pvInputLeft, &cbInLeft, (char **)&pvOutputLeft, &cbOutLeft) != (size_t)-1)
1221 #endif
1223 if (!cbInLeft)
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';
1232 else
1233 *(char *)pvOutputLeft = '\0';
1234 *ppvOutput = pvOutput;
1235 return VINF_SUCCESS;
1237 else
1238 errno = E2BIG;
1240 iconv_close(icHandle);
1243 * If we failed because of output buffer space we'll
1244 * increase the output buffer size and retry.
1246 if (errno == E2BIG)
1248 if (!cbOutput)
1250 RTMemTmpFree(pvOutput);
1251 cbOutput2 *= 2;
1252 pvOutput = RTMemTmpAlloc(cbOutput2);
1253 if (!pvOutput)
1254 return VERR_NO_TMP_MEMORY;
1255 continue;
1257 return VERR_BUFFER_OVERFLOW;
1260 break;
1263 /* failure */
1264 if (!cbOutput)
1265 RTMemTmpFree(pvOutput);
1266 return VERR_NO_TRANSLATION;