VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/posix/path-posix.cpp@ 5841

Last change on this file since 5841 was 5702, checked in by vboxsync, 17 years ago

Free a previously allocated memory.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 33.3 KB
Line 
1/* $Id: path-posix.cpp 5702 2007-11-12 11:29:04Z vboxsync $ */
2/** @file
3 * innotek Portable Runtime - Path Manipulation, POSIX.
4 */
5
6/*
7 * Copyright (C) 2006-2007 innotek GmbH
8 *
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.
16 */
17
18
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#ifdef RT_OS_DARWIN
33# include <mach-o/dyld.h>
34#endif
35
36#include <iprt/path.h>
37#include <iprt/assert.h>
38#include <iprt/string.h>
39#include <iprt/err.h>
40#include <iprt/log.h>
41#include "internal/path.h"
42#include "internal/fs.h"
43
44#ifdef RT_OS_L4
45# include <l4/vboxserver/vboxserver.h>
46#endif
47
48
49
50
51RTDECL(int) RTPathReal(const char *pszPath, char *pszRealPath, unsigned cchRealPath)
52{
53 /*
54 * Convert input.
55 */
56 char *pszNativePath;
57 int rc = rtPathToNative(&pszNativePath, pszPath);
58 if (RT_SUCCESS(rc))
59 {
60 /*
61 * On POSIX platforms the API doesn't take a length parameter, which makes it
62 * a little bit more work.
63 */
64 char szTmpPath[PATH_MAX + 1];
65 const char *psz = realpath(pszNativePath, szTmpPath);
66 if (psz)
67 {
68 /*
69 * Convert result and copy it to the return buffer.
70 */
71 char *pszUtf8RealPath;
72 rc = rtPathFromNative(&pszUtf8RealPath, szTmpPath);
73 if (RT_SUCCESS(rc))
74 {
75 size_t cch = strlen(pszUtf8RealPath) + 1;
76 if (cch <= cchRealPath)
77 memcpy(pszRealPath, pszUtf8RealPath, cch);
78 else
79 rc = VERR_BUFFER_OVERFLOW;
80 RTStrFree(pszUtf8RealPath);
81 }
82 }
83 else
84 rc = RTErrConvertFromErrno(errno);
85 RTStrFree(pszNativePath);
86 }
87
88 LogFlow(("RTPathReal(%p:{%s}, %p:{%s}, %u): returns %Rrc\n", pszPath, pszPath,
89 pszRealPath, RT_SUCCESS(rc) ? pszRealPath : "<failed>", cchRealPath));
90 return rc;
91}
92
93
94/**
95 * Cleans up a path specifier a little bit.
96 * This includes removing duplicate slashes, uncessary single dots, and
97 * trailing slashes. Also, replaces all RTPATH_SLASH characters with '/'.
98 *
99 * @returns Number of bytes in the clean path.
100 * @param pszPath The path to cleanup.
101 * @remark Borrowed from innotek libc.
102 */
103static int fsCleanPath(char *pszPath)
104{
105 /*
106 * Change to '/' and remove duplicates.
107 */
108 char *pszSrc = pszPath;
109 char *pszTrg = pszPath;
110#ifdef HAVE_UNC
111 int fUnc = 0;
112 if ( RTPATH_IS_SLASH(pszPath[0])
113 && RTPATH_IS_SLASH(pszPath[1]))
114 { /* Skip first slash in a unc path. */
115 pszSrc++;
116 *pszTrg++ = '/';
117 fUnc = 1;
118 }
119#endif
120
121 for (;;)
122 {
123 char ch = *pszSrc++;
124 if (RTPATH_IS_SLASH(ch))
125 {
126 *pszTrg++ = '/';
127 for (;;)
128 {
129 do ch = *pszSrc++;
130 while (RTPATH_IS_SLASH(ch));
131
132 /* Remove '/./' and '/.'. */
133 if (ch != '.' || (*pszSrc && !RTPATH_IS_SLASH(*pszSrc)))
134 break;
135 }
136 }
137 *pszTrg = ch;
138 if (!ch)
139 break;
140 pszTrg++;
141 }
142
143 /*
144 * Remove trailing slash if the path may be pointing to a directory.
145 */
146 int cch = pszTrg - pszPath;
147 if ( cch > 1
148 && RTPATH_IS_SLASH(pszTrg[-1])
149#ifdef HAVE_DRIVE
150 && !RTPATH_IS_VOLSEP(pszTrg[-2])
151#endif
152 && !RTPATH_IS_SLASH(pszTrg[-2]))
153 pszPath[--cch] = '\0';
154
155 return cch;
156}
157
158
159RTDECL(int) RTPathAbs(const char *pszPath, char *pszAbsPath, unsigned cchAbsPath)
160{
161 /*
162 * Convert input.
163 */
164 char *pszNativePath;
165 int rc = rtPathToNative(&pszNativePath, pszPath);
166 if (RT_FAILURE(rc))
167 {
168 LogFlow(("RTPathAbs(%p:{%s}, %p, %d): returns %Rrc\n", pszPath,
169 pszPath, pszAbsPath, cchAbsPath, rc));
170 return rc;
171 }
172
173 /*
174 * On POSIX platforms the API doesn't take a length parameter, which makes it
175 * a little bit more work.
176 */
177 char szTmpPath[PATH_MAX + 1];
178 char *psz = realpath(pszNativePath, szTmpPath);
179 if (!psz)
180 {
181 if (errno == ENOENT || errno == ENOTDIR
182#ifdef RT_OS_OS2
183 /// @todo realpath() returns EIO for non-existent UNC paths like
184 // //server/share/subdir (i.e. when a subdir is specified within
185 // a share). We should either fix realpath() in libc or remove
186 // this todo.
187 || errno == EIO
188#endif
189 )
190 {
191 if (strlen(pszNativePath) <= PATH_MAX)
192 {
193 /*
194 * Iterate the path bit by bit an apply realpath to it.
195 */
196
197 char szTmpSrc[PATH_MAX + 1];
198 strcpy(szTmpSrc, pszNativePath);
199 fsCleanPath(szTmpSrc);
200
201 size_t cch = 0; // current resolved path length
202 char *pszCur = szTmpSrc;
203
204#ifdef HAVE_DRIVE
205 if (pszCur[0] && RTPATH_IS_VOLSEP(pszCur[1]) && pszCur[2] == '/')
206 {
207 psz = szTmpPath;
208 cch = 2;
209 pszCur += 3;
210 }
211#ifdef HAVE_UNC
212 else
213 if (pszCur[0] == '/' && pszCur[1] == '/')
214 {
215 pszCur += 2;
216 char *pszSlash = strchr(pszCur, '/');
217 size_t cchElement = pszSlash ? pszSlash - pszCur : strlen(pszCur);
218 if (cchElement && pszCur[cchElement])
219 {
220 psz = szTmpPath;
221 cch = cchElement + 2;
222 pszCur += cchElement + 1;
223 }
224 else
225 /* we've got just "//server" or "//" */
226 /// @todo (r=dmik) not 100% sure we should fail, but the
227 // above cases are just invalid (incomplete) paths,
228 // no matter that Win32 returns these paths as is.
229 rc = VERR_INVALID_NAME;
230 }
231#endif
232#else
233 if (*pszCur == '/')
234 {
235 psz = szTmpPath;
236 pszCur++;
237 }
238#endif
239 else
240 {
241 /* get the cwd */
242 psz = getcwd(szTmpPath, sizeof(szTmpPath));
243 AssertMsg(psz, ("Couldn't get cwd!\n"));
244 if (psz)
245 {
246#ifdef HAVE_DRIVE
247 if (*pszCur == '/')
248 {
249 cch = 2;
250 pszCur++;
251 }
252 else
253#endif
254 cch = strlen(psz);
255 }
256 else
257 rc = RTErrConvertFromErrno(errno);
258 }
259
260 if (psz)
261 {
262 bool fResolveSymlinks = true;
263 char szTmpPath2[PATH_MAX + 1];
264
265 /* make sure strrchr() will work correctly */
266 psz[cch] = '\0';
267
268 while (*pszCur)
269 {
270 char *pszSlash = strchr(pszCur, '/');
271 size_t cchElement = pszSlash ? pszSlash - pszCur : strlen(pszCur);
272 if (cch + cchElement + 1 > PATH_MAX)
273 {
274 rc = VERR_FILENAME_TOO_LONG;
275 break;
276 }
277
278 if (!strncmp(pszCur, "..", cchElement))
279 {
280 char *pszLastSlash = strrchr(psz, '/');
281#ifdef HAVE_UNC
282 if (pszLastSlash && pszLastSlash > psz &&
283 pszLastSlash[-1] != '/')
284#else
285 if (pszLastSlash)
286#endif
287 {
288 cch = pszLastSlash - psz;
289 psz[cch] = '\0';
290 }
291 /* else: We've reached the root and the parent of
292 * the root is the root. */
293 }
294 else
295 {
296 psz[cch++] = '/';
297 memcpy(psz + cch, pszCur, cchElement);
298 cch += cchElement;
299 psz[cch] = '\0';
300
301 if (fResolveSymlinks)
302 {
303 /* resolve possible symlinks */
304 char *psz2 = realpath(psz, psz == szTmpPath
305 ? szTmpPath2
306 : szTmpPath);
307 if (psz2)
308 {
309 psz = psz2;
310 cch = strlen(psz);
311 }
312 else
313 {
314 if (errno != ENOENT && errno != ENOTDIR
315#ifdef RT_OS_OS2
316 /// @todo see above
317 && errno != EIO
318#endif
319 )
320 {
321 rc = RTErrConvertFromErrno(errno);
322 break;
323 }
324
325 /* no more need to resolve symlinks */
326 fResolveSymlinks = false;
327 }
328 }
329 }
330
331 pszCur += cchElement;
332 /* skip the slash */
333 if (*pszCur)
334 ++pszCur;
335 }
336
337#ifdef HAVE_DRIVE
338 /* check if we're at the root */
339 if (cch == 2 && RTPATH_IS_VOLSEP(psz[1]))
340#else
341 /* if the length is zero here, then we're at the root */
342 if (!cch)
343#endif
344 {
345 psz[cch++] = '/';
346 psz[cch] = '\0';
347 }
348 }
349 }
350 else
351 rc = VERR_FILENAME_TOO_LONG;
352 }
353 else
354 rc = RTErrConvertFromErrno(errno);
355 }
356
357 RTStrFree(pszNativePath);
358
359 if (psz && RT_SUCCESS(rc))
360 {
361 /*
362 * Convert result and copy it to the return buffer.
363 */
364 char *pszUtf8AbsPath;
365 rc = rtPathFromNative(&pszUtf8AbsPath, psz);
366 if (RT_FAILURE(rc))
367 {
368 LogFlow(("RTPathAbs(%p:{%s}, %p, %d): returns %Rrc\n", pszPath,
369 pszPath, pszAbsPath, cchAbsPath, rc));
370 return rc;
371 }
372
373 /* replace '/' back with native RTPATH_SLASH */
374 psz = pszUtf8AbsPath;
375 for (; *psz; psz++)
376 if (*psz == '/')
377 *psz = RTPATH_SLASH;
378
379 unsigned cch = strlen(pszUtf8AbsPath) + 1;
380 if (cch <= cchAbsPath)
381 memcpy(pszAbsPath, pszUtf8AbsPath, cch);
382 else
383 rc = VERR_BUFFER_OVERFLOW;
384 RTStrFree(pszUtf8AbsPath);
385 }
386
387 LogFlow(("RTPathAbs(%p:{%s}, %p:{%s}, %d): returns %Rrc\n", pszPath,
388 pszPath, pszAbsPath, RT_SUCCESS(rc) ? pszAbsPath : "<failed>",
389 cchAbsPath, rc));
390 return rc;
391}
392
393
394RTDECL(int) RTPathProgram(char *pszPath, unsigned cchPath)
395{
396 /*
397 * First time only.
398 */
399 if (!g_szrtProgramPath[0])
400 {
401 /*
402 * Linux have no API for obtaining the executable path, but provides a symbolic link
403 * in the proc file system. Note that readlink is one of the weirdest Unix apis around.
404 *
405 * OS/2 have an api for getting the program file name.
406 */
407/** @todo use RTProcGetExecutableName() */
408#if defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD) || defined(RT_OS_SOLARIS)
409# ifdef RT_OS_LINUX
410 int cchLink = readlink("/proc/self/exe", &g_szrtProgramPath[0], sizeof(g_szrtProgramPath) - 1);
411# elif defined(RT_OS_SOLARIS)
412 char szFileBuf[PATH_MAX + 1];
413 sprintf(szFileBuf, "/proc/%ld/path/a.out", (long)getpid());
414 int cchLink = readlink(szFileBuf, &g_szrtProgramPath[0], sizeof(g_szrtProgramPath) - 1);
415# else /* RT_OS_FREEBSD: */
416 int cchLink = readlink("/proc/curproc/file", &g_szrtProgramPath[0], sizeof(g_szrtProgramPath) - 1);
417# endif
418 if (cchLink < 0 || cchLink == sizeof(g_szrtProgramPath) - 1)
419 {
420 int rc = RTErrConvertFromErrno(errno);
421 AssertMsgFailed(("couldn't read /proc/self/exe. errno=%d cchLink=%d\n", errno, cchLink));
422 LogFlow(("RTPathProgram(%p, %u): returns %Rrc\n", pszPath, cchPath, rc));
423 return rc;
424 }
425 g_szrtProgramPath[cchLink] = '\0';
426
427#elif defined(RT_OS_OS2) || defined(RT_OS_L4)
428 _execname(g_szrtProgramPath, sizeof(g_szrtProgramPath));
429
430#elif defined(RT_OS_DARWIN)
431 const char *pszImageName = _dyld_get_image_name(0);
432 AssertReturn(pszImageName, VERR_INTERNAL_ERROR);
433 size_t cchImageName = strlen(pszImageName);
434 if (cchImageName >= sizeof(g_szrtProgramPath))
435 AssertReturn(pszImageName, VERR_INTERNAL_ERROR);
436 memcpy(g_szrtProgramPath, pszImageName, cchImageName + 1);
437
438#else
439# error needs porting.
440#endif
441
442 /*
443 * Convert to UTF-8 and strip of the filename.
444 */
445 char *pszTmp = NULL;
446 int rc = rtPathFromNative(&pszTmp, &g_szrtProgramPath[0]);
447 if (RT_FAILURE(rc))
448 {
449 LogFlow(("RTPathProgram(%p, %u): returns %Rrc\n", pszPath, cchPath, rc));
450 return rc;
451 }
452 size_t cch = strlen(pszTmp);
453 if (cch >= sizeof(g_szrtProgramPath))
454 {
455 RTStrFree(pszTmp);
456 LogFlow(("RTPathProgram(%p, %u): returns %Rrc\n", pszPath, cchPath, VERR_BUFFER_OVERFLOW));
457 return VERR_BUFFER_OVERFLOW;
458 }
459 memcpy(g_szrtProgramPath, pszTmp, cch + 1);
460 RTPathStripFilename(g_szrtProgramPath);
461 RTStrFree(pszTmp);
462 }
463
464 /*
465 * Calc the length and check if there is space before copying.
466 */
467 unsigned cch = strlen(g_szrtProgramPath) + 1;
468 if (cch <= cchPath)
469 {
470 memcpy(pszPath, g_szrtProgramPath, cch + 1);
471 LogFlow(("RTPathProgram(%p:{%s}, %u): returns %Rrc\n", pszPath, pszPath, cchPath, VINF_SUCCESS));
472 return VINF_SUCCESS;
473 }
474
475 AssertMsgFailed(("Buffer too small (%d < %d)\n", cchPath, cch));
476 LogFlow(("RTPathProgram(%p, %u): returns %Rrc\n", pszPath, cchPath, VERR_BUFFER_OVERFLOW));
477 return VERR_BUFFER_OVERFLOW;
478}
479
480
481#ifndef RT_OS_L4
482/**
483 * Worker for RTPathUserHome that looks up the home directory
484 * using the getpwuid_r api.
485 *
486 * @returns IPRT status code.
487 * @param pszPath The path buffer.
488 * @param cchPath The size of the buffer.
489 * @param uid The User ID to query the home directory of.
490 */
491static int rtPathUserHomeByPasswd(char *pszPath, size_t cchPath, uid_t uid)
492{
493 /*
494 * The getpwuid_r function uses the passed in buffer to "allocate" any
495 * extra memory it needs. On some systems we should probably use the
496 * sysconf function to find the appropriate buffer size, but since it won't
497 * work everywhere we'll settle with a 5KB buffer and ASSUME that it'll
498 * suffice for even the lengthiest user descriptions...
499 */
500 char achBuffer[5120];
501 struct passwd Passwd;
502 struct passwd *pPasswd;
503 memset(&Passwd, 0, sizeof(Passwd));
504 int rc = getpwuid_r(uid, &Passwd, &achBuffer[0], sizeof(achBuffer), &pPasswd);
505 if (rc != 0)
506 return RTErrConvertFromErrno(rc);
507 if (!pPasswd) /* uid not found in /etc/passwd */
508 return VERR_PATH_NOT_FOUND;
509
510 /*
511 * Check that it isn't empty and that it exists.
512 */
513 struct stat st;
514 if ( !pPasswd->pw_dir
515 || !*pPasswd->pw_dir
516 || stat(pPasswd->pw_dir, &st)
517 || !S_ISDIR(st.st_mode))
518 return VERR_PATH_NOT_FOUND;
519
520 /*
521 * Convert it to UTF-8 and copy it to the return buffer.
522 */
523 char *pszUtf8Path;
524 rc = rtPathFromNative(&pszUtf8Path, pPasswd->pw_dir);
525 if (RT_SUCCESS(rc))
526 {
527 size_t cchHome = strlen(pszUtf8Path);
528 if (cchHome < cchPath)
529 memcpy(pszPath, pszUtf8Path, cchHome + 1);
530 else
531 rc = VERR_BUFFER_OVERFLOW;
532 RTStrFree(pszUtf8Path);
533 }
534 return rc;
535}
536#endif
537
538
539/**
540 * Worker for RTPathUserHome that looks up the home directory
541 * using the HOME environment variable.
542 *
543 * @returns IPRT status code.
544 * @param pszPath The path buffer.
545 * @param cchPath The size of the buffer.
546 */
547static int rtPathUserHomeByEnv(char *pszPath, size_t cchPath)
548{
549 /*
550 * Get HOME env. var it and validate it's existance.
551 */
552 int rc = VERR_PATH_NOT_FOUND;
553 const char *pszHome = getenv("HOME");
554 if (pszHome)
555
556 {
557 struct stat st;
558 if ( !stat(pszHome, &st)
559 && S_ISDIR(st.st_mode))
560 {
561 /*
562 * Convert it to UTF-8 and copy it to the return buffer.
563 */
564 char *pszUtf8Path;
565 rc = rtPathFromNative(&pszUtf8Path, pszHome);
566 if (RT_SUCCESS(rc))
567 {
568 size_t cchHome = strlen(pszUtf8Path);
569 if (cchHome < cchPath)
570 memcpy(pszPath, pszUtf8Path, cchHome + 1);
571 else
572 rc = VERR_BUFFER_OVERFLOW;
573 RTStrFree(pszUtf8Path);
574 }
575 }
576 }
577 return rc;
578}
579
580
581RTDECL(int) RTPathUserHome(char *pszPath, unsigned cchPath)
582{
583 int rc;
584#ifndef RT_OS_L4
585 /*
586 * We make an exception for the root user and use the system call
587 * getpwuid_r to determine their initial home path instead of
588 * reading it from the $HOME variable. This is because the $HOME
589 * variable does not get changed by sudo (and possibly su and others)
590 * which can cause root-owned files to appear in user's home folders.
591 */
592 uid_t uid = geteuid();
593 if (!uid)
594 rc = rtPathUserHomeByPasswd(pszPath, cchPath, uid);
595 else
596 rc = rtPathUserHomeByEnv(pszPath, cchPath);
597
598 /*
599 * On failure, retry using the alternative method.
600 * (Should perhaps restrict the retry cases a bit more here...)
601 */
602 if ( RT_FAILURE(rc)
603 && rc != VERR_BUFFER_OVERFLOW)
604 {
605 if (!uid)
606 rc = rtPathUserHomeByEnv(pszPath, cchPath);
607 else
608 rc = rtPathUserHomeByPasswd(pszPath, cchPath, uid);
609 }
610#else /* RT_OS_L4 */
611 rc = rtPathUserHomeByEnv(pszPath, cchPath);
612#endif /* RT_OS_L4 */
613
614 LogFlow(("RTPathUserHome(%p:{%s}, %u): returns %Rrc\n", pszPath,
615 RT_SUCCESS(rc) ? pszPath : "<failed>", cchPath, rc));
616 return rc;
617}
618
619
620RTR3DECL(int) RTPathQueryInfo(const char *pszPath, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs)
621{
622 /*
623 * Validate input.
624 */
625 AssertMsgReturn(VALID_PTR(pszPath), ("%p\n", pszPath), VERR_INVALID_POINTER);
626 AssertReturn(*pszPath, VERR_INVALID_PARAMETER);
627 AssertMsgReturn(VALID_PTR(pObjInfo), ("%p\n", pszPath), VERR_INVALID_POINTER);
628 AssertMsgReturn( enmAdditionalAttribs >= RTFSOBJATTRADD_NOTHING
629 && enmAdditionalAttribs <= RTFSOBJATTRADD_LAST,
630 ("Invalid enmAdditionalAttribs=%p\n", enmAdditionalAttribs),
631 VERR_INVALID_PARAMETER);
632
633 /*
634 * Convert the filename.
635 */
636 char *pszNativePath;
637 int rc = rtPathToNative(&pszNativePath, pszPath);
638 if (RT_SUCCESS(rc))
639 {
640 struct stat Stat;
641 if (!stat(pszNativePath, &Stat))
642 {
643 rtFsConvertStatToObjInfo(pObjInfo, &Stat, pszPath, 0);
644 switch (enmAdditionalAttribs)
645 {
646 case RTFSOBJATTRADD_EASIZE:
647 /** @todo Use SGI extended attribute interface to query EA info. */
648 pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_EASIZE;
649 pObjInfo->Attr.u.EASize.cb = 0;
650 break;
651
652 case RTFSOBJATTRADD_NOTHING:
653 case RTFSOBJATTRADD_UNIX:
654 Assert(pObjInfo->Attr.enmAdditional == RTFSOBJATTRADD_UNIX);
655 break;
656
657 default:
658 AssertMsgFailed(("Impossible!\n"));
659 return VERR_INTERNAL_ERROR;
660 }
661 }
662 else
663 rc = RTErrConvertFromErrno(errno);
664 rtPathFreeNative(pszNativePath);
665 }
666
667 LogFlow(("RTPathQueryInfo(%p:{%s}, pObjInfo=%p, %d): returns %Rrc\n",
668 pszPath, pszPath, pObjInfo, enmAdditionalAttribs, rc));
669 return rc;
670}
671
672
673RTR3DECL(int) RTPathSetTimes(const char *pszPath, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
674 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
675{
676 /*
677 * Validate input.
678 */
679 AssertMsgReturn(VALID_PTR(pszPath), ("%p\n", pszPath), VERR_INVALID_POINTER);
680 AssertMsgReturn(*pszPath, ("%p\n", pszPath), VERR_INVALID_PARAMETER);
681 AssertMsgReturn(!pAccessTime || VALID_PTR(pAccessTime), ("%p\n", pAccessTime), VERR_INVALID_POINTER);
682 AssertMsgReturn(!pModificationTime || VALID_PTR(pModificationTime), ("%p\n", pModificationTime), VERR_INVALID_POINTER);
683 AssertMsgReturn(!pChangeTime || VALID_PTR(pChangeTime), ("%p\n", pChangeTime), VERR_INVALID_POINTER);
684 AssertMsgReturn(!pBirthTime || VALID_PTR(pBirthTime), ("%p\n", pBirthTime), VERR_INVALID_POINTER);
685
686 /*
687 * Convert the paths.
688 */
689 char *pszNativePath;
690 int rc = rtPathToNative(&pszNativePath, pszPath);
691 if (RT_SUCCESS(rc))
692 {
693 /*
694 * If it's a no-op, we'll only verify the existance of the file.
695 */
696 if (!pAccessTime && !pModificationTime)
697 {
698 struct stat Stat;
699 if (!stat(pszNativePath, &Stat))
700 rc = VINF_SUCCESS;
701 else
702 {
703 rc = RTErrConvertFromErrno(errno);
704 Log(("RTPathSetTimes('%s',,,,): failed with %Rrc and errno=%d\n", pszPath, rc, errno));
705 }
706 }
707 else
708 {
709 /*
710 * Convert the input to timeval, getting the missing one if necessary,
711 * and call the API which does the change.
712 */
713 struct timeval aTimevals[2];
714 if (pAccessTime && pModificationTime)
715 {
716 RTTimeSpecGetTimeval(pAccessTime, &aTimevals[0]);
717 RTTimeSpecGetTimeval(pModificationTime, &aTimevals[1]);
718 }
719 else
720 {
721 RTFSOBJINFO ObjInfo;
722 int rc = RTPathQueryInfo(pszPath, &ObjInfo, RTFSOBJATTRADD_UNIX);
723 if (RT_SUCCESS(rc))
724 {
725 RTTimeSpecGetTimeval(pAccessTime ? pAccessTime : &ObjInfo.AccessTime, &aTimevals[0]);
726 RTTimeSpecGetTimeval(pModificationTime ? pModificationTime : &ObjInfo.ModificationTime, &aTimevals[1]);
727 }
728 else
729 Log(("RTPathSetTimes('%s',%p,%p,,): RTPathQueryInfo failed with %Rrc\n",
730 pszPath, pAccessTime, pModificationTime, rc));
731 }
732 if (RT_SUCCESS(rc))
733 {
734 if (utimes(pszNativePath, aTimevals))
735 {
736 rc = RTErrConvertFromErrno(errno);
737 Log(("RTPathSetTimes('%s',%p,%p,,): failed with %Rrc and errno=%d\n",
738 pszPath, pAccessTime, pModificationTime, rc, errno));
739 }
740 }
741 }
742 rtPathFreeNative(pszNativePath);
743 }
744
745 LogFlow(("RTPathSetTimes(%p:{%s}, %p:{%RDtimespec}, %p:{%RDtimespec}, %p:{%RDtimespec}, %p:{%RDtimespec}): return %Rrc\n",
746 pszPath, pszPath, pAccessTime, pAccessTime, pModificationTime, pModificationTime,
747 pChangeTime, pChangeTime, pBirthTime, pBirthTime));
748 return rc;
749}
750
751
752/**
753 * Checks if two files are the one and same file.
754 */
755static bool rtPathSame(const char *pszNativeSrc, const char *pszNativeDst)
756{
757 struct stat SrcStat;
758 if (stat(pszNativeSrc, &SrcStat))
759 return false;
760 struct stat DstStat;
761 if (stat(pszNativeDst, &DstStat))
762 return false;
763 Assert(SrcStat.st_dev && DstStat.st_dev);
764 Assert(SrcStat.st_ino && DstStat.st_ino);
765 if ( SrcStat.st_dev == DstStat.st_dev
766 && SrcStat.st_ino == DstStat.st_ino
767 && (SrcStat.st_mode & S_IFMT) == (DstStat.st_mode & S_IFMT))
768 return true;
769 return false;
770}
771
772
773/**
774 * Worker for RTPathRename, RTDirRename, RTFileRename.
775 *
776 * @returns IPRT status code.
777 * @param pszSrc The source path.
778 * @param pszDst The destintation path.
779 * @param fRename The rename flags.
780 * @param fFileType The filetype. We use the RTFMODE filetypes here. If it's 0,
781 * anything goes. If it's RTFS_TYPE_DIRECTORY we'll check that the
782 * source is a directory. If Its RTFS_TYPE_FILE we'll check that it's
783 * not a directory (we are NOT checking whether it's a file).
784 */
785int rtPathPosixRename(const char *pszSrc, const char *pszDst, unsigned fRename, RTFMODE fFileType)
786{
787 /*
788 * Convert the paths.
789 */
790 char *pszNativeSrc;
791 int rc = rtPathToNative(&pszNativeSrc, pszSrc);
792 if (RT_SUCCESS(rc))
793 {
794 char *pszNativeDst;
795 rc = rtPathToNative(&pszNativeDst, pszDst);
796 if (RT_SUCCESS(rc))
797 {
798 /*
799 * Check that the source exists and that any types that's specified matches.
800 * We have to check this first to avoid getting errnous VERR_ALREADY_EXISTS
801 * errors from the next step.
802 *
803 * There are race conditions here (perhaps unlikly ones but still), but I'm
804 * afraid there is little with can do to fix that.
805 */
806 struct stat SrcStat;
807 if (stat(pszNativeSrc, &SrcStat))
808 rc = RTErrConvertFromErrno(errno);
809 else if (!fFileType)
810 rc = VINF_SUCCESS;
811 else if (RTFS_IS_DIRECTORY(fFileType))
812 rc = S_ISDIR(SrcStat.st_mode) ? VINF_SUCCESS : VERR_NOT_A_DIRECTORY;
813 else
814 rc = S_ISDIR(SrcStat.st_mode) ? VERR_IS_A_DIRECTORY : VINF_SUCCESS;
815 if (RT_SUCCESS(rc))
816 {
817 bool fSameFile = false;
818
819 /*
820 * Check if the target exists, rename is rather destructive.
821 * We'll have to make sure we don't overwrite the source!
822 * Another race condition btw.
823 */
824 struct stat DstStat;
825 if (stat(pszNativeDst, &DstStat))
826 rc = errno == ENOENT ? VINF_SUCCESS : RTErrConvertFromErrno(errno);
827 else
828 {
829 Assert(SrcStat.st_dev && DstStat.st_dev);
830 Assert(SrcStat.st_ino && DstStat.st_ino);
831 if ( SrcStat.st_dev == DstStat.st_dev
832 && SrcStat.st_ino == DstStat.st_ino
833 && (SrcStat.st_mode & S_IFMT) == (SrcStat.st_mode & S_IFMT))
834 {
835 /*
836 * It's likely that we're talking about the same file here.
837 * We should probably check paths or whatever, but for now this'll have to be enough.
838 */
839 fSameFile = true;
840 }
841 if (fSameFile)
842 rc = VINF_SUCCESS;
843 else if (S_ISDIR(DstStat.st_mode) || !(fRename & RTPATHRENAME_FLAGS_REPLACE))
844 rc = VERR_ALREADY_EXISTS;
845 else
846 rc = VINF_SUCCESS;
847
848 }
849 if (RT_SUCCESS(rc))
850 {
851 if (!rename(pszNativeSrc, pszNativeDst))
852 rc = VINF_SUCCESS;
853 else if ( (fRename & RTPATHRENAME_FLAGS_REPLACE)
854 && (errno == ENOTDIR || errno == EEXIST))
855 {
856 /*
857 * Check that the destination isn't a directory.
858 * Yet another race condition.
859 */
860 if (rtPathSame(pszNativeSrc, pszNativeDst))
861 {
862 rc = VINF_SUCCESS;
863 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): appears to be the same file... (errno=%d)\n",
864 pszSrc, pszDst, fRename, fFileType, errno));
865 }
866 else
867 {
868 if (stat(pszNativeDst, &DstStat))
869 rc = errno != ENOENT ? RTErrConvertFromErrno(errno) : VINF_SUCCESS;
870 else if (S_ISDIR(DstStat.st_mode))
871 rc = VERR_ALREADY_EXISTS;
872 else
873 rc = VINF_SUCCESS;
874 if (RT_SUCCESS(rc))
875 {
876 if (!unlink(pszNativeDst))
877 {
878 if (!rename(pszNativeSrc, pszNativeDst))
879 rc = VINF_SUCCESS;
880 else
881 {
882 rc = RTErrConvertFromErrno(errno);
883 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): rename failed rc=%Rrc errno=%d\n",
884 pszSrc, pszDst, fRename, fFileType, rc, errno));
885 }
886 }
887 else
888 {
889 rc = RTErrConvertFromErrno(errno);
890 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): failed to unlink dst rc=%Rrc errno=%d\n",
891 pszSrc, pszDst, fRename, fFileType, rc, errno));
892 }
893 }
894 else
895 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): dst !dir check failed rc=%Rrc\n",
896 pszSrc, pszDst, fRename, fFileType, rc));
897 }
898 }
899 else
900 {
901 rc = RTErrConvertFromErrno(errno);
902 if (errno == ENOTDIR)
903 rc = VERR_ALREADY_EXISTS; /* unless somebody is racing us, this is the right interpretation */
904 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): rename failed rc=%Rrc errno=%d\n",
905 pszSrc, pszDst, fRename, fFileType, rc, errno));
906 }
907 }
908 else
909 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): destination check failed rc=%Rrc errno=%d\n",
910 pszSrc, pszDst, fRename, fFileType, rc, errno));
911 }
912 else
913 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): source type check failed rc=%Rrc errno=%d\n",
914 pszSrc, pszDst, fRename, fFileType, rc, errno));
915
916 rtPathFreeNative(pszNativeDst);
917 }
918 rtPathFreeNative(pszNativeSrc);
919 }
920 return rc;
921}
922
923
924RTR3DECL(int) RTPathRename(const char *pszSrc, const char *pszDst, unsigned fRename)
925{
926 /*
927 * Validate input.
928 */
929 AssertMsgReturn(VALID_PTR(pszSrc), ("%p\n", pszSrc), VERR_INVALID_POINTER);
930 AssertMsgReturn(VALID_PTR(pszDst), ("%p\n", pszDst), VERR_INVALID_POINTER);
931 AssertMsgReturn(*pszSrc, ("%p\n", pszSrc), VERR_INVALID_PARAMETER);
932 AssertMsgReturn(*pszDst, ("%p\n", pszDst), VERR_INVALID_PARAMETER);
933 AssertMsgReturn(!(fRename & ~RTPATHRENAME_FLAGS_REPLACE), ("%#x\n", fRename), VERR_INVALID_PARAMETER);
934
935 /*
936 * Hand it to the worker.
937 */
938 int rc = rtPathPosixRename(pszSrc, pszDst, fRename, 0);
939
940 Log(("RTPathRename(%p:{%s}, %p:{%s}, %#x): returns %Rrc\n", pszSrc, pszSrc, pszDst, pszDst, fRename, rc));
941 return rc;
942}
943
944
945RTDECL(bool) RTPathExists(const char *pszPath)
946{
947 /*
948 * Validate input.
949 */
950 AssertPtrReturn(pszPath, false);
951 AssertReturn(*pszPath, false);
952
953 /*
954 * Convert the path and check if it exists using stat().
955 */
956 char *pszNativePath;
957 int rc = rtPathToNative(&pszNativePath, pszPath);
958 if (RT_SUCCESS(rc))
959 {
960 struct stat Stat;
961 if (!stat(pszNativePath, &Stat))
962 rc = VINF_SUCCESS;
963 else
964 rc = VERR_GENERAL_FAILURE;
965 RTStrFree(pszNativePath);
966 }
967 return RT_SUCCESS(rc);
968}
969
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette