VirtualBox

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

Last change on this file since 3916 was 3916, checked in by vboxsync, 18 years ago

Solaris.

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