VirtualBox

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

Last change on this file since 14298 was 14054, checked in by vboxsync, 17 years ago

iprt: RTPath - lenths are size_t not unsigned.

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