VirtualBox

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

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

RT_OS_* and RT_ARCH_* for Runtime/ and Support/

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 30.1 KB
Line 
1/* $Id: path-posix.cpp 3672 2007-07-17 12:39:30Z 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)
411# ifdef RT_OS_LINUX
412 int cchLink = readlink("/proc/self/exe", &g_szrtProgramPath[0], sizeof(g_szrtProgramPath) - 1);
413# else
414 int cchLink = readlink("/proc/curproc/file", &g_szrtProgramPath[0], sizeof(g_szrtProgramPath) - 1);
415# endif
416 if (cchLink < 0 || cchLink == sizeof(g_szrtProgramPath) - 1)
417 {
418 int rc = RTErrConvertFromErrno(errno);
419 AssertMsgFailed(("couldn't read /proc/self/exe. errno=%d cchLink=%d\n", errno, cchLink));
420 LogFlow(("RTPathProgram(%p, %u): returns %Rrc\n", pszPath, cchPath, rc));
421 return rc;
422 }
423 g_szrtProgramPath[cchLink] = '\0';
424
425#elif defined(RT_OS_OS2) || defined(RT_OS_L4)
426 _execname(g_szrtProgramPath, sizeof(g_szrtProgramPath));
427
428#elif defined(RT_OS_DARWIN)
429 const char *pszImageName = _dyld_get_image_name(0);
430 AssertReturn(pszImageName, VERR_INTERNAL_ERROR);
431 size_t cchImageName = strlen(pszImageName);
432 if (cchImageName >= sizeof(g_szrtProgramPath))
433 AssertReturn(pszImageName, VERR_INTERNAL_ERROR);
434 memcpy(g_szrtProgramPath, pszImageName, cchImageName + 1);
435
436#else
437# error needs porting.
438#endif
439
440 /*
441 * Convert to UTF-8 and strip of the filename.
442 */
443 char *pszTmp = NULL;
444 int rc = rtPathFromNative(&pszTmp, &g_szrtProgramPath[0]);
445 if (RT_FAILURE(rc))
446 {
447 LogFlow(("RTPathProgram(%p, %u): returns %Rrc\n", pszPath, cchPath, rc));
448 return rc;
449 }
450 size_t cch = strlen(pszTmp);
451 if (cch >= sizeof(g_szrtProgramPath))
452 {
453 RTStrFree(pszTmp);
454 LogFlow(("RTPathProgram(%p, %u): returns %Rrc\n", pszPath, cchPath, VERR_BUFFER_OVERFLOW));
455 return VERR_BUFFER_OVERFLOW;
456 }
457 memcpy(g_szrtProgramPath, pszTmp, cch + 1);
458 RTPathStripFilename(g_szrtProgramPath);
459 RTStrFree(pszTmp);
460 }
461
462 /*
463 * Calc the length and check if there is space before copying.
464 */
465 unsigned cch = strlen(g_szrtProgramPath) + 1;
466 if (cch <= cchPath)
467 {
468 memcpy(pszPath, g_szrtProgramPath, cch + 1);
469 LogFlow(("RTPathProgram(%p:{%s}, %u): returns %Rrc\n", pszPath, pszPath, cchPath, VINF_SUCCESS));
470 return VINF_SUCCESS;
471 }
472
473 AssertMsgFailed(("Buffer too small (%d < %d)\n", cchPath, cch));
474 LogFlow(("RTPathProgram(%p, %u): returns %Rrc\n", pszPath, cchPath, VERR_BUFFER_OVERFLOW));
475 return VERR_BUFFER_OVERFLOW;
476}
477
478
479RTDECL(int) RTPathUserHome(char *pszPath, unsigned cchPath)
480{
481 /*
482 * Get HOME env. var it and validate it's existance.
483 */
484 int rc;
485 struct stat s;
486 const char *pszHome = getenv("HOME");
487 if (pszHome)
488 {
489 if ( !stat(pszHome, &s)
490 && S_ISDIR(s.st_mode))
491 {
492 /*
493 * Convert it to UTF-8 and copy it to the return buffer.
494 */
495 char *pszUtf8Path;
496 rc = rtPathFromNative(&pszUtf8Path, pszHome);
497 if (RT_SUCCESS(rc))
498 {
499 size_t cchHome = strlen(pszUtf8Path);
500 if (cchHome < cchPath)
501 memcpy(pszPath, pszUtf8Path, cchHome + 1);
502 else
503 rc = VERR_BUFFER_OVERFLOW;
504 RTStrFree(pszUtf8Path);
505 }
506 }
507 else
508 rc = VERR_PATH_NOT_FOUND;
509
510 }
511 else
512 rc = VERR_PATH_NOT_FOUND;
513
514 LogFlow(("RTPathUserHome(%p:{%s}, %u): returns %Rrc\n", pszPath,
515 RT_SUCCESS(rc) ? pszPath : "<failed>", cchPath, rc));
516 return rc;
517}
518
519
520RTR3DECL(int) RTPathQueryInfo(const char *pszPath, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs)
521{
522 /*
523 * Validate input.
524 */
525 AssertMsgReturn(VALID_PTR(pszPath), ("%p\n", pszPath), VERR_INVALID_POINTER);
526 AssertReturn(*pszPath, VERR_INVALID_PARAMETER);
527 AssertMsgReturn(VALID_PTR(pObjInfo), ("%p\n", pszPath), VERR_INVALID_POINTER);
528 AssertMsgReturn( enmAdditionalAttribs >= RTFSOBJATTRADD_NOTHING
529 && enmAdditionalAttribs <= RTFSOBJATTRADD_LAST,
530 ("Invalid enmAdditionalAttribs=%p\n", enmAdditionalAttribs),
531 VERR_INVALID_PARAMETER);
532
533 /*
534 * Convert the filename.
535 */
536 char *pszNativePath;
537 int rc = rtPathToNative(&pszNativePath, pszPath);
538 if (RT_SUCCESS(rc))
539 {
540 struct stat Stat;
541 if (!stat(pszNativePath, &Stat))
542 {
543 rtFsConvertStatToObjInfo(pObjInfo, &Stat, pszPath, 0);
544 switch (enmAdditionalAttribs)
545 {
546 case RTFSOBJATTRADD_EASIZE:
547 /** @todo Use SGI extended attribute interface to query EA info. */
548 pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_EASIZE;
549 pObjInfo->Attr.u.EASize.cb = 0;
550 break;
551
552 case RTFSOBJATTRADD_NOTHING:
553 case RTFSOBJATTRADD_UNIX:
554 Assert(pObjInfo->Attr.enmAdditional == RTFSOBJATTRADD_UNIX);
555 break;
556
557 default:
558 AssertMsgFailed(("Impossible!\n"));
559 return VERR_INTERNAL_ERROR;
560 }
561 }
562 else
563 rc = RTErrConvertFromErrno(errno);
564 }
565
566 LogFlow(("RTPathQueryInfo(%p:{%s}, pObjInfo=%p, %d): returns %Rrc\n",
567 pszPath, pszPath, pObjInfo, enmAdditionalAttribs, rc));
568 return rc;
569}
570
571
572RTR3DECL(int) RTPathSetTimes(const char *pszPath, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
573 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
574{
575 /*
576 * Validate input.
577 */
578 AssertMsgReturn(VALID_PTR(pszPath), ("%p\n", pszPath), VERR_INVALID_POINTER);
579 AssertMsgReturn(*pszPath, ("%p\n", pszPath), VERR_INVALID_PARAMETER);
580 AssertMsgReturn(!pAccessTime || VALID_PTR(pAccessTime), ("%p\n", pAccessTime), VERR_INVALID_POINTER);
581 AssertMsgReturn(!pModificationTime || VALID_PTR(pModificationTime), ("%p\n", pModificationTime), VERR_INVALID_POINTER);
582 AssertMsgReturn(!pChangeTime || VALID_PTR(pChangeTime), ("%p\n", pChangeTime), VERR_INVALID_POINTER);
583 AssertMsgReturn(!pBirthTime || VALID_PTR(pBirthTime), ("%p\n", pBirthTime), VERR_INVALID_POINTER);
584
585 /*
586 * Convert the paths.
587 */
588 char *pszNativePath;
589 int rc = rtPathToNative(&pszNativePath, pszPath);
590 if (RT_SUCCESS(rc))
591 {
592 /*
593 * If it's a no-op, we'll only verify the existance of the file.
594 */
595 if (!pAccessTime && !pModificationTime)
596 {
597 struct stat Stat;
598 if (!stat(pszNativePath, &Stat))
599 rc = VINF_SUCCESS;
600 else
601 {
602 rc = RTErrConvertFromErrno(errno);
603 Log(("RTPathSetTimes('%s',,,,): failed with %Rrc and errno=%d\n", pszPath, rc, errno));
604 }
605 }
606 else
607 {
608 /*
609 * Convert the input to timeval, getting the missing one if necessary,
610 * and call the API which does the change.
611 */
612 struct timeval aTimevals[2];
613 if (pAccessTime && pModificationTime)
614 {
615 RTTimeSpecGetTimeval(pAccessTime, &aTimevals[0]);
616 RTTimeSpecGetTimeval(pModificationTime, &aTimevals[1]);
617 }
618 else
619 {
620 RTFSOBJINFO ObjInfo;
621 int rc = RTPathQueryInfo(pszPath, &ObjInfo, RTFSOBJATTRADD_UNIX);
622 if (RT_SUCCESS(rc))
623 {
624 RTTimeSpecGetTimeval(pAccessTime ? pAccessTime : &ObjInfo.AccessTime, &aTimevals[0]);
625 RTTimeSpecGetTimeval(pModificationTime ? pModificationTime : &ObjInfo.ModificationTime, &aTimevals[1]);
626 }
627 else
628 Log(("RTPathSetTimes('%s',%p,%p,,): RTPathQueryInfo failed with %Rrc\n",
629 pszPath, pAccessTime, pModificationTime, rc));
630 }
631 if (RT_SUCCESS(rc))
632 {
633 if (utimes(pszNativePath, aTimevals))
634 {
635 rc = RTErrConvertFromErrno(errno);
636 Log(("RTPathSetTimes('%s',%p,%p,,): failed with %Rrc and errno=%d\n",
637 pszPath, pAccessTime, pModificationTime, rc, errno));
638 }
639 }
640 }
641 }
642
643 LogFlow(("RTPathSetTimes(%p:{%s}, %p:{%RDtimespec}, %p:{%RDtimespec}, %p:{%RDtimespec}, %p:{%RDtimespec}): return %Rrc\n",
644 pszPath, pszPath, pAccessTime, pAccessTime, pModificationTime, pModificationTime,
645 pChangeTime, pChangeTime, pBirthTime, pBirthTime));
646 return rc;
647}
648
649
650/**
651 * Checks if two files are the one and same file.
652 */
653static bool rtPathSame(const char *pszNativeSrc, const char *pszNativeDst)
654{
655 struct stat SrcStat;
656 if (stat(pszNativeSrc, &SrcStat))
657 return false;
658 struct stat DstStat;
659 if (stat(pszNativeDst, &DstStat))
660 return false;
661 Assert(SrcStat.st_dev && DstStat.st_dev);
662 Assert(SrcStat.st_ino && DstStat.st_ino);
663 if ( SrcStat.st_dev == DstStat.st_dev
664 && SrcStat.st_ino == DstStat.st_ino
665 && (SrcStat.st_mode & S_IFMT) == (DstStat.st_mode & S_IFMT))
666 return true;
667 return false;
668}
669
670
671/**
672 * Worker for RTPathRename, RTDirRename, RTFileRename.
673 *
674 * @returns IPRT status code.
675 * @param pszSrc The source path.
676 * @param pszDst The destintation path.
677 * @param fRename The rename flags.
678 * @param fFileType The filetype. We use the RTFMODE filetypes here. If it's 0,
679 * anything goes. If it's RTFS_TYPE_DIRECTORY we'll check that the
680 * source is a directory. If Its RTFS_TYPE_FILE we'll check that it's
681 * not a directory (we are NOT checking whether it's a file).
682 */
683int rtPathPosixRename(const char *pszSrc, const char *pszDst, unsigned fRename, RTFMODE fFileType)
684{
685 /*
686 * Convert the paths.
687 */
688 char *pszNativeSrc;
689 int rc = rtPathToNative(&pszNativeSrc, pszSrc);
690 if (RT_SUCCESS(rc))
691 {
692 char *pszNativeDst;
693 rc = rtPathToNative(&pszNativeDst, pszDst);
694 if (RT_SUCCESS(rc))
695 {
696 /*
697 * Check that the source exists and that any types that's specified matches.
698 * We have to check this first to avoid getting errnous VERR_ALREADY_EXISTS
699 * errors from the next step.
700 *
701 * There are race conditions here (perhaps unlikly ones but still), but I'm
702 * afraid there is little with can do to fix that.
703 */
704 struct stat SrcStat;
705 if (stat(pszNativeSrc, &SrcStat))
706 rc = RTErrConvertFromErrno(errno);
707 else if (!fFileType)
708 rc = VINF_SUCCESS;
709 else if (RTFS_IS_DIRECTORY(fFileType))
710 rc = S_ISDIR(SrcStat.st_mode) ? VINF_SUCCESS : VERR_NOT_A_DIRECTORY;
711 else
712 rc = S_ISDIR(SrcStat.st_mode) ? VERR_IS_A_DIRECTORY : VINF_SUCCESS;
713 if (RT_SUCCESS(rc))
714 {
715 bool fSameFile = false;
716
717 /*
718 * Check if the target exists, rename is rather destructive.
719 * We'll have to make sure we don't overwrite the source!
720 * Another race condition btw.
721 */
722 struct stat DstStat;
723 if (stat(pszNativeDst, &DstStat))
724 rc = errno == ENOENT ? VINF_SUCCESS : RTErrConvertFromErrno(errno);
725 else
726 {
727 Assert(SrcStat.st_dev && DstStat.st_dev);
728 Assert(SrcStat.st_ino && DstStat.st_ino);
729 if ( SrcStat.st_dev == DstStat.st_dev
730 && SrcStat.st_ino == DstStat.st_ino
731 && (SrcStat.st_mode & S_IFMT) == (SrcStat.st_mode & S_IFMT))
732 {
733 /*
734 * It's likely that we're talking about the same file here.
735 * We should probably check paths or whatever, but for now this'll have to be enough.
736 */
737 fSameFile = true;
738 }
739 if (fSameFile)
740 rc = VINF_SUCCESS;
741 else if (S_ISDIR(DstStat.st_mode) || !(fRename & RTPATHRENAME_FLAGS_REPLACE))
742 rc = VERR_ALREADY_EXISTS;
743 else
744 rc = VINF_SUCCESS;
745
746 }
747 if (RT_SUCCESS(rc))
748 {
749 if (!rename(pszNativeSrc, pszNativeDst))
750 rc = VINF_SUCCESS;
751 else if ( (fRename & RTPATHRENAME_FLAGS_REPLACE)
752 && (errno == ENOTDIR || errno == EEXIST))
753 {
754 /*
755 * Check that the destination isn't a directory.
756 * Yet another race condition.
757 */
758 if (rtPathSame(pszNativeSrc, pszNativeDst))
759 {
760 rc = VINF_SUCCESS;
761 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): appears to be the same file... (errno=%d)\n",
762 pszSrc, pszDst, fRename, fFileType, errno));
763 }
764 else
765 {
766 if (stat(pszNativeDst, &DstStat))
767 rc = errno != ENOENT ? RTErrConvertFromErrno(errno) : VINF_SUCCESS;
768 else if (S_ISDIR(DstStat.st_mode))
769 rc = VERR_ALREADY_EXISTS;
770 else
771 rc = VINF_SUCCESS;
772 if (RT_SUCCESS(rc))
773 {
774 if (!unlink(pszNativeDst))
775 {
776 if (!rename(pszNativeSrc, pszNativeDst))
777 rc = VINF_SUCCESS;
778 else
779 {
780 rc = RTErrConvertFromErrno(errno);
781 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): rename failed rc=%Rrc errno=%d\n",
782 pszSrc, pszDst, fRename, fFileType, rc, errno));
783 }
784 }
785 else
786 {
787 rc = RTErrConvertFromErrno(errno);
788 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): failed to unlink dst rc=%Rrc errno=%d\n",
789 pszSrc, pszDst, fRename, fFileType, rc, errno));
790 }
791 }
792 else
793 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): dst !dir check failed rc=%Rrc\n",
794 pszSrc, pszDst, fRename, fFileType, rc));
795 }
796 }
797 else
798 {
799 rc = RTErrConvertFromErrno(errno);
800 if (errno == ENOTDIR)
801 rc = VERR_ALREADY_EXISTS; /* unless somebody is racing us, this is the right interpretation */
802 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): rename failed rc=%Rrc errno=%d\n",
803 pszSrc, pszDst, fRename, fFileType, rc, errno));
804 }
805 }
806 else
807 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): destination check failed rc=%Rrc errno=%d\n",
808 pszSrc, pszDst, fRename, fFileType, rc, errno));
809 }
810 else
811 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): source type check failed rc=%Rrc errno=%d\n",
812 pszSrc, pszDst, fRename, fFileType, rc, errno));
813
814 rtPathFreeNative(pszNativeDst);
815 }
816 rtPathFreeNative(pszNativeSrc);
817 }
818 return rc;
819}
820
821
822RTR3DECL(int) RTPathRename(const char *pszSrc, const char *pszDst, unsigned fRename)
823{
824 /*
825 * Validate input.
826 */
827 AssertMsgReturn(VALID_PTR(pszSrc), ("%p\n", pszSrc), VERR_INVALID_POINTER);
828 AssertMsgReturn(VALID_PTR(pszDst), ("%p\n", pszDst), VERR_INVALID_POINTER);
829 AssertMsgReturn(*pszSrc, ("%p\n", pszSrc), VERR_INVALID_PARAMETER);
830 AssertMsgReturn(*pszDst, ("%p\n", pszDst), VERR_INVALID_PARAMETER);
831 AssertMsgReturn(!(fRename & ~RTPATHRENAME_FLAGS_REPLACE), ("%#x\n", fRename), VERR_INVALID_PARAMETER);
832
833 /*
834 * Hand it to the worker.
835 */
836 int rc = rtPathPosixRename(pszSrc, pszDst, fRename, 0);
837
838 Log(("RTPathRename(%p:{%s}, %p:{%s}, %#x): returns %Rrc\n", pszSrc, pszSrc, pszDst, pszDst, fRename, rc));
839 return rc;
840}
841
842
843RTDECL(bool) RTPathExists(const char *pszPath)
844{
845 /*
846 * Validate input.
847 */
848 AssertPtrReturn(pszPath, false);
849 AssertReturn(*pszPath, false);
850
851 /*
852 * Convert the path and check if it exists using stat().
853 */
854 char *pszNativePath;
855 int rc = rtPathToNative(&pszNativePath, pszPath);
856 if (RT_SUCCESS(rc))
857 {
858 struct stat Stat;
859 if (!stat(pszNativePath, &Stat))
860 rc = VINF_SUCCESS;
861 else
862 rc = VERR_GENERAL_FAILURE;
863 RTStrFree(pszNativePath);
864 }
865 return RT_SUCCESS(rc);
866}
867
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