VirtualBox

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

Last change on this file since 15756 was 15756, checked in by vboxsync, 16 years ago

Runtime: RTPathAbs: Fixed the "." case on POSIX, fixed the "" case on Windows.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 27.6 KB
Line 
1/* $Id: path-posix.cpp 15756 2008-12-25 11:12:42Z 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.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 (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 if (strlen(pszPath) > PATH_MAX)
172 {
173 LogFlow(("RTPathAbs(%p:{%s}, %p, %d): returns %Rrc\n", pszPath,
174 pszPath, pszAbsPath, cchAbsPath, VERR_FILENAME_TOO_LONG));
175 return VERR_FILENAME_TOO_LONG;
176 }
177
178 char szTmpPath[PATH_MAX + 1];
179 strcpy(szTmpPath, pszPath);
180 fsCleanPath(szTmpPath);
181
182 /*
183 * fsCleanPath will leave the single dot alone, we don't need it here.
184 */
185 if (szTmpPath[0] == '.' && !szTmpPath[1])
186 szTmpPath[0] = '\0';
187
188 char *pszCur = szTmpPath;
189
190#ifdef HAVE_DRIVE
191 if (pszCur[0] && RTPATH_IS_VOLSEP(pszCur[1]) && pszCur[2] == '/')
192 pszCur += 3;
193#ifdef HAVE_UNC
194 else
195 if (pszCur[0] == '/' && pszCur[1] == '/')
196 pszCur += 2;
197#endif
198#else
199 if (pszCur[0] == '/')
200 pszCur += 1;
201#endif
202 else
203 {
204 /*
205 * Prepend the current directory to the relative path.
206 */
207
208 char szCurDir[PATH_MAX + 1];
209 if (getcwd(szCurDir, sizeof(szCurDir)) == NULL)
210 {
211 AssertMsgFailed(("Couldn't get cwd!\n"));
212
213 int rc = RTErrConvertFromErrno(errno);
214 LogFlow(("RTPathAbs(%p:{%s}, %p, %d): returns %Rrc\n", pszPath,
215 pszPath, pszAbsPath, cchAbsPath, rc));
216 return rc;
217 }
218
219 fsCleanPath(szCurDir);
220
221 {
222 char *pszUtf8CurDir;
223 int rc = rtPathFromNative(&pszUtf8CurDir, szCurDir);
224 if (RT_FAILURE(rc))
225 {
226 LogFlow(("RTPathAbs(%p:{%s}, %p, %d): returns %Rrc\n", pszPath,
227 pszPath, pszAbsPath, cchAbsPath, rc));
228 return rc;
229 }
230
231 size_t cchUtf8CurDir = strlen(pszUtf8CurDir);
232 if (cchUtf8CurDir + strlen(szTmpPath) + 1 > PATH_MAX)
233 {
234 RTStrFree(pszUtf8CurDir);
235 LogFlow(("RTPathAbs(%p:{%s}, %p, %d): returns %Rrc\n", pszPath,
236 pszPath, pszAbsPath, cchAbsPath, VERR_FILENAME_TOO_LONG));
237 return VERR_FILENAME_TOO_LONG;
238 }
239
240 strcpy(szTmpPath + cchUtf8CurDir + 1, szTmpPath);
241 strcpy(szTmpPath, pszUtf8CurDir);
242 szTmpPath[cchUtf8CurDir] = '/';
243
244 RTStrFree(pszUtf8CurDir);
245 }
246
247#ifdef HAVE_DRIVE
248 if (pszCur[0] && RTPATH_IS_VOLSEP(pszCur[1]) && pszCur[2] == '/')
249 pszCur += 3;
250#ifdef HAVE_UNC
251 else
252 if (pszCur[0] == '/' && pszCur[1] == '/')
253 pszCur += 2;
254#endif
255#else
256 if (pszCur[0] == '/')
257 pszCur += 1;
258#endif
259 else
260 {
261 AssertFailed();
262 LogFlow(("RTPathAbs(%p:{%s}, %p, %d): returns %Rrc\n", pszPath,
263 pszPath, pszAbsPath, cchAbsPath, VERR_GENERAL_FAILURE));
264 return VERR_GENERAL_FAILURE;
265 }
266 }
267
268 char *pszTop = pszCur;
269
270 /*
271 * Get rid of double dot path components by evaluating them.
272 */
273
274 for (;;)
275 {
276 if (pszCur[0] == '.' && pszCur[1] == '.' &&
277 (!pszCur[2] || pszCur[2] == '/'))
278 {
279 /* rewind to the previous component if any */
280 char *pszPrev = pszCur - 1;
281 if (pszPrev > pszTop)
282 while (*--pszPrev != '/')
283 ;
284
285 AssertMsg(*pszPrev == '/', ("szTmpPath={%s}, pszPrev=+%u\n",
286 szTmpPath, pszPrev - szTmpPath));
287 strcpy(pszPrev, pszCur + 2);
288
289 pszCur = pszPrev;
290 }
291 else
292 {
293 while (*pszCur && *pszCur != '/')
294 ++pszCur;
295 }
296
297 if (!*pszCur)
298 break;
299
300 /* skip the slash */
301 ++pszCur;
302 }
303
304 if (pszCur < pszTop)
305 {
306 /*
307 * We overwrote the trailing slash of the root path with zero, restore
308 * it.
309 */
310 *pszCur++ = '/';
311 *pszCur = '\0';
312 }
313 else if (pszCur > pszTop && *(pszCur - 1) == '/')
314 {
315 /*
316 * Extra trailing slash in a non-root path, remove it.
317 */
318 *--pszCur = '\0';
319 }
320
321 int rc = VINF_SUCCESS;
322
323 size_t cch = pszCur - szTmpPath + 1;
324 if (cch <= cchAbsPath)
325 memcpy(pszAbsPath, szTmpPath, cch);
326 else
327 rc = VERR_BUFFER_OVERFLOW;
328
329 LogFlow(("RTPathAbs(%p:{%s}, %p:{%s}, %d): returns %Rrc\n", pszPath,
330 pszPath, pszAbsPath, RT_SUCCESS(rc) ? pszAbsPath : "<failed>",
331 cchAbsPath, rc));
332 return rc;
333}
334
335
336#ifndef RT_OS_L4
337/**
338 * Worker for RTPathUserHome that looks up the home directory
339 * using the getpwuid_r api.
340 *
341 * @returns IPRT status code.
342 * @param pszPath The path buffer.
343 * @param cchPath The size of the buffer.
344 * @param uid The User ID to query the home directory of.
345 */
346static int rtPathUserHomeByPasswd(char *pszPath, size_t cchPath, uid_t uid)
347{
348 /*
349 * The getpwuid_r function uses the passed in buffer to "allocate" any
350 * extra memory it needs. On some systems we should probably use the
351 * sysconf function to find the appropriate buffer size, but since it won't
352 * work everywhere we'll settle with a 5KB buffer and ASSUME that it'll
353 * suffice for even the lengthiest user descriptions...
354 */
355 char achBuffer[5120];
356 struct passwd Passwd;
357 struct passwd *pPasswd;
358 memset(&Passwd, 0, sizeof(Passwd));
359 int rc = getpwuid_r(uid, &Passwd, &achBuffer[0], sizeof(achBuffer), &pPasswd);
360 if (rc != 0)
361 return RTErrConvertFromErrno(rc);
362 if (!pPasswd) /* uid not found in /etc/passwd */
363 return VERR_PATH_NOT_FOUND;
364
365 /*
366 * Check that it isn't empty and that it exists.
367 */
368 struct stat st;
369 if ( !pPasswd->pw_dir
370 || !*pPasswd->pw_dir
371 || stat(pPasswd->pw_dir, &st)
372 || !S_ISDIR(st.st_mode))
373 return VERR_PATH_NOT_FOUND;
374
375 /*
376 * Convert it to UTF-8 and copy it to the return buffer.
377 */
378 char *pszUtf8Path;
379 rc = rtPathFromNative(&pszUtf8Path, pPasswd->pw_dir);
380 if (RT_SUCCESS(rc))
381 {
382 size_t cchHome = strlen(pszUtf8Path);
383 if (cchHome < cchPath)
384 memcpy(pszPath, pszUtf8Path, cchHome + 1);
385 else
386 rc = VERR_BUFFER_OVERFLOW;
387 RTStrFree(pszUtf8Path);
388 }
389 return rc;
390}
391#endif
392
393
394/**
395 * Worker for RTPathUserHome that looks up the home directory
396 * using the HOME environment variable.
397 *
398 * @returns IPRT status code.
399 * @param pszPath The path buffer.
400 * @param cchPath The size of the buffer.
401 */
402static int rtPathUserHomeByEnv(char *pszPath, size_t cchPath)
403{
404 /*
405 * Get HOME env. var it and validate it's existance.
406 */
407 int rc = VERR_PATH_NOT_FOUND;
408 const char *pszHome = getenv("HOME");
409 if (pszHome)
410
411 {
412 struct stat st;
413 if ( !stat(pszHome, &st)
414 && S_ISDIR(st.st_mode))
415 {
416 /*
417 * Convert it to UTF-8 and copy it to the return buffer.
418 */
419 char *pszUtf8Path;
420 rc = rtPathFromNative(&pszUtf8Path, pszHome);
421 if (RT_SUCCESS(rc))
422 {
423 size_t cchHome = strlen(pszUtf8Path);
424 if (cchHome < cchPath)
425 memcpy(pszPath, pszUtf8Path, cchHome + 1);
426 else
427 rc = VERR_BUFFER_OVERFLOW;
428 RTStrFree(pszUtf8Path);
429 }
430 }
431 }
432 return rc;
433}
434
435
436RTDECL(int) RTPathUserHome(char *pszPath, size_t cchPath)
437{
438 int rc;
439#ifndef RT_OS_L4
440 /*
441 * We make an exception for the root user and use the system call
442 * getpwuid_r to determine their initial home path instead of
443 * reading it from the $HOME variable. This is because the $HOME
444 * variable does not get changed by sudo (and possibly su and others)
445 * which can cause root-owned files to appear in user's home folders.
446 */
447 uid_t uid = geteuid();
448 if (!uid)
449 rc = rtPathUserHomeByPasswd(pszPath, cchPath, uid);
450 else
451 rc = rtPathUserHomeByEnv(pszPath, cchPath);
452
453 /*
454 * On failure, retry using the alternative method.
455 * (Should perhaps restrict the retry cases a bit more here...)
456 */
457 if ( RT_FAILURE(rc)
458 && rc != VERR_BUFFER_OVERFLOW)
459 {
460 if (!uid)
461 rc = rtPathUserHomeByEnv(pszPath, cchPath);
462 else
463 rc = rtPathUserHomeByPasswd(pszPath, cchPath, uid);
464 }
465#else /* RT_OS_L4 */
466 rc = rtPathUserHomeByEnv(pszPath, cchPath);
467#endif /* RT_OS_L4 */
468
469 LogFlow(("RTPathUserHome(%p:{%s}, %u): returns %Rrc\n", pszPath,
470 RT_SUCCESS(rc) ? pszPath : "<failed>", cchPath, rc));
471 return rc;
472}
473
474
475RTR3DECL(int) RTPathQueryInfo(const char *pszPath, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs)
476{
477 /*
478 * Validate input.
479 */
480 AssertMsgReturn(VALID_PTR(pszPath), ("%p\n", pszPath), VERR_INVALID_POINTER);
481 AssertReturn(*pszPath, VERR_INVALID_PARAMETER);
482 AssertMsgReturn(VALID_PTR(pObjInfo), ("%p\n", pszPath), VERR_INVALID_POINTER);
483 AssertMsgReturn( enmAdditionalAttribs >= RTFSOBJATTRADD_NOTHING
484 && enmAdditionalAttribs <= RTFSOBJATTRADD_LAST,
485 ("Invalid enmAdditionalAttribs=%p\n", enmAdditionalAttribs),
486 VERR_INVALID_PARAMETER);
487
488 /*
489 * Convert the filename.
490 */
491 char *pszNativePath;
492 int rc = rtPathToNative(&pszNativePath, pszPath);
493 if (RT_SUCCESS(rc))
494 {
495 struct stat Stat;
496 if (!stat(pszNativePath, &Stat))
497 {
498 rtFsConvertStatToObjInfo(pObjInfo, &Stat, pszPath, 0);
499 switch (enmAdditionalAttribs)
500 {
501 case RTFSOBJATTRADD_EASIZE:
502 /** @todo Use SGI extended attribute interface to query EA info. */
503 pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_EASIZE;
504 pObjInfo->Attr.u.EASize.cb = 0;
505 break;
506
507 case RTFSOBJATTRADD_NOTHING:
508 case RTFSOBJATTRADD_UNIX:
509 Assert(pObjInfo->Attr.enmAdditional == RTFSOBJATTRADD_UNIX);
510 break;
511
512 default:
513 AssertMsgFailed(("Impossible!\n"));
514 return VERR_INTERNAL_ERROR;
515 }
516 }
517 else
518 rc = RTErrConvertFromErrno(errno);
519 rtPathFreeNative(pszNativePath);
520 }
521
522 LogFlow(("RTPathQueryInfo(%p:{%s}, pObjInfo=%p, %d): returns %Rrc\n",
523 pszPath, pszPath, pObjInfo, enmAdditionalAttribs, rc));
524 return rc;
525}
526
527
528RTR3DECL(int) RTPathSetTimes(const char *pszPath, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
529 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
530{
531 /*
532 * Validate input.
533 */
534 AssertMsgReturn(VALID_PTR(pszPath), ("%p\n", pszPath), VERR_INVALID_POINTER);
535 AssertMsgReturn(*pszPath, ("%p\n", pszPath), VERR_INVALID_PARAMETER);
536 AssertMsgReturn(!pAccessTime || VALID_PTR(pAccessTime), ("%p\n", pAccessTime), VERR_INVALID_POINTER);
537 AssertMsgReturn(!pModificationTime || VALID_PTR(pModificationTime), ("%p\n", pModificationTime), VERR_INVALID_POINTER);
538 AssertMsgReturn(!pChangeTime || VALID_PTR(pChangeTime), ("%p\n", pChangeTime), VERR_INVALID_POINTER);
539 AssertMsgReturn(!pBirthTime || VALID_PTR(pBirthTime), ("%p\n", pBirthTime), VERR_INVALID_POINTER);
540
541 /*
542 * Convert the paths.
543 */
544 char *pszNativePath;
545 int rc = rtPathToNative(&pszNativePath, pszPath);
546 if (RT_SUCCESS(rc))
547 {
548 /*
549 * If it's a no-op, we'll only verify the existance of the file.
550 */
551 if (!pAccessTime && !pModificationTime)
552 {
553 struct stat Stat;
554 if (!stat(pszNativePath, &Stat))
555 rc = VINF_SUCCESS;
556 else
557 {
558 rc = RTErrConvertFromErrno(errno);
559 Log(("RTPathSetTimes('%s',,,,): failed with %Rrc and errno=%d\n", pszPath, rc, errno));
560 }
561 }
562 else
563 {
564 /*
565 * Convert the input to timeval, getting the missing one if necessary,
566 * and call the API which does the change.
567 */
568 struct timeval aTimevals[2];
569 if (pAccessTime && pModificationTime)
570 {
571 RTTimeSpecGetTimeval(pAccessTime, &aTimevals[0]);
572 RTTimeSpecGetTimeval(pModificationTime, &aTimevals[1]);
573 }
574 else
575 {
576 RTFSOBJINFO ObjInfo;
577 int rc = RTPathQueryInfo(pszPath, &ObjInfo, RTFSOBJATTRADD_UNIX);
578 if (RT_SUCCESS(rc))
579 {
580 RTTimeSpecGetTimeval(pAccessTime ? pAccessTime : &ObjInfo.AccessTime, &aTimevals[0]);
581 RTTimeSpecGetTimeval(pModificationTime ? pModificationTime : &ObjInfo.ModificationTime, &aTimevals[1]);
582 }
583 else
584 Log(("RTPathSetTimes('%s',%p,%p,,): RTPathQueryInfo failed with %Rrc\n",
585 pszPath, pAccessTime, pModificationTime, rc));
586 }
587 if (RT_SUCCESS(rc))
588 {
589 if (utimes(pszNativePath, aTimevals))
590 {
591 rc = RTErrConvertFromErrno(errno);
592 Log(("RTPathSetTimes('%s',%p,%p,,): failed with %Rrc and errno=%d\n",
593 pszPath, pAccessTime, pModificationTime, rc, errno));
594 }
595 }
596 }
597 rtPathFreeNative(pszNativePath);
598 }
599
600 LogFlow(("RTPathSetTimes(%p:{%s}, %p:{%RDtimespec}, %p:{%RDtimespec}, %p:{%RDtimespec}, %p:{%RDtimespec}): return %Rrc\n",
601 pszPath, pszPath, pAccessTime, pAccessTime, pModificationTime, pModificationTime,
602 pChangeTime, pChangeTime, pBirthTime, pBirthTime));
603 return rc;
604}
605
606
607/**
608 * Checks if two files are the one and same file.
609 */
610static bool rtPathSame(const char *pszNativeSrc, const char *pszNativeDst)
611{
612 struct stat SrcStat;
613 if (stat(pszNativeSrc, &SrcStat))
614 return false;
615 struct stat DstStat;
616 if (stat(pszNativeDst, &DstStat))
617 return false;
618 Assert(SrcStat.st_dev && DstStat.st_dev);
619 Assert(SrcStat.st_ino && DstStat.st_ino);
620 if ( SrcStat.st_dev == DstStat.st_dev
621 && SrcStat.st_ino == DstStat.st_ino
622 && (SrcStat.st_mode & S_IFMT) == (DstStat.st_mode & S_IFMT))
623 return true;
624 return false;
625}
626
627
628/**
629 * Worker for RTPathRename, RTDirRename, RTFileRename.
630 *
631 * @returns IPRT status code.
632 * @param pszSrc The source path.
633 * @param pszDst The destintation path.
634 * @param fRename The rename flags.
635 * @param fFileType The filetype. We use the RTFMODE filetypes here. If it's 0,
636 * anything goes. If it's RTFS_TYPE_DIRECTORY we'll check that the
637 * source is a directory. If Its RTFS_TYPE_FILE we'll check that it's
638 * not a directory (we are NOT checking whether it's a file).
639 */
640int rtPathPosixRename(const char *pszSrc, const char *pszDst, unsigned fRename, RTFMODE fFileType)
641{
642 /*
643 * Convert the paths.
644 */
645 char *pszNativeSrc;
646 int rc = rtPathToNative(&pszNativeSrc, pszSrc);
647 if (RT_SUCCESS(rc))
648 {
649 char *pszNativeDst;
650 rc = rtPathToNative(&pszNativeDst, pszDst);
651 if (RT_SUCCESS(rc))
652 {
653 /*
654 * Check that the source exists and that any types that's specified matches.
655 * We have to check this first to avoid getting errnous VERR_ALREADY_EXISTS
656 * errors from the next step.
657 *
658 * There are race conditions here (perhaps unlikly ones but still), but I'm
659 * afraid there is little with can do to fix that.
660 */
661 struct stat SrcStat;
662 if (stat(pszNativeSrc, &SrcStat))
663 rc = RTErrConvertFromErrno(errno);
664 else if (!fFileType)
665 rc = VINF_SUCCESS;
666 else if (RTFS_IS_DIRECTORY(fFileType))
667 rc = S_ISDIR(SrcStat.st_mode) ? VINF_SUCCESS : VERR_NOT_A_DIRECTORY;
668 else
669 rc = S_ISDIR(SrcStat.st_mode) ? VERR_IS_A_DIRECTORY : VINF_SUCCESS;
670 if (RT_SUCCESS(rc))
671 {
672 bool fSameFile = false;
673
674 /*
675 * Check if the target exists, rename is rather destructive.
676 * We'll have to make sure we don't overwrite the source!
677 * Another race condition btw.
678 */
679 struct stat DstStat;
680 if (stat(pszNativeDst, &DstStat))
681 rc = errno == ENOENT ? VINF_SUCCESS : RTErrConvertFromErrno(errno);
682 else
683 {
684 Assert(SrcStat.st_dev && DstStat.st_dev);
685 Assert(SrcStat.st_ino && DstStat.st_ino);
686 if ( SrcStat.st_dev == DstStat.st_dev
687 && SrcStat.st_ino == DstStat.st_ino
688 && (SrcStat.st_mode & S_IFMT) == (SrcStat.st_mode & S_IFMT))
689 {
690 /*
691 * It's likely that we're talking about the same file here.
692 * We should probably check paths or whatever, but for now this'll have to be enough.
693 */
694 fSameFile = true;
695 }
696 if (fSameFile)
697 rc = VINF_SUCCESS;
698 else if (S_ISDIR(DstStat.st_mode) || !(fRename & RTPATHRENAME_FLAGS_REPLACE))
699 rc = VERR_ALREADY_EXISTS;
700 else
701 rc = VINF_SUCCESS;
702
703 }
704 if (RT_SUCCESS(rc))
705 {
706 if (!rename(pszNativeSrc, pszNativeDst))
707 rc = VINF_SUCCESS;
708 else if ( (fRename & RTPATHRENAME_FLAGS_REPLACE)
709 && (errno == ENOTDIR || errno == EEXIST))
710 {
711 /*
712 * Check that the destination isn't a directory.
713 * Yet another race condition.
714 */
715 if (rtPathSame(pszNativeSrc, pszNativeDst))
716 {
717 rc = VINF_SUCCESS;
718 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): appears to be the same file... (errno=%d)\n",
719 pszSrc, pszDst, fRename, fFileType, errno));
720 }
721 else
722 {
723 if (stat(pszNativeDst, &DstStat))
724 rc = errno != ENOENT ? RTErrConvertFromErrno(errno) : VINF_SUCCESS;
725 else if (S_ISDIR(DstStat.st_mode))
726 rc = VERR_ALREADY_EXISTS;
727 else
728 rc = VINF_SUCCESS;
729 if (RT_SUCCESS(rc))
730 {
731 if (!unlink(pszNativeDst))
732 {
733 if (!rename(pszNativeSrc, pszNativeDst))
734 rc = VINF_SUCCESS;
735 else
736 {
737 rc = RTErrConvertFromErrno(errno);
738 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): rename failed rc=%Rrc errno=%d\n",
739 pszSrc, pszDst, fRename, fFileType, rc, errno));
740 }
741 }
742 else
743 {
744 rc = RTErrConvertFromErrno(errno);
745 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): failed to unlink dst rc=%Rrc errno=%d\n",
746 pszSrc, pszDst, fRename, fFileType, rc, errno));
747 }
748 }
749 else
750 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): dst !dir check failed rc=%Rrc\n",
751 pszSrc, pszDst, fRename, fFileType, rc));
752 }
753 }
754 else
755 {
756 rc = RTErrConvertFromErrno(errno);
757 if (errno == ENOTDIR)
758 rc = VERR_ALREADY_EXISTS; /* unless somebody is racing us, this is the right interpretation */
759 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): rename failed rc=%Rrc errno=%d\n",
760 pszSrc, pszDst, fRename, fFileType, rc, errno));
761 }
762 }
763 else
764 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): destination check failed rc=%Rrc errno=%d\n",
765 pszSrc, pszDst, fRename, fFileType, rc, errno));
766 }
767 else
768 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): source type check failed rc=%Rrc errno=%d\n",
769 pszSrc, pszDst, fRename, fFileType, rc, errno));
770
771 rtPathFreeNative(pszNativeDst);
772 }
773 rtPathFreeNative(pszNativeSrc);
774 }
775 return rc;
776}
777
778
779RTR3DECL(int) RTPathRename(const char *pszSrc, const char *pszDst, unsigned fRename)
780{
781 /*
782 * Validate input.
783 */
784 AssertMsgReturn(VALID_PTR(pszSrc), ("%p\n", pszSrc), VERR_INVALID_POINTER);
785 AssertMsgReturn(VALID_PTR(pszDst), ("%p\n", pszDst), VERR_INVALID_POINTER);
786 AssertMsgReturn(*pszSrc, ("%p\n", pszSrc), VERR_INVALID_PARAMETER);
787 AssertMsgReturn(*pszDst, ("%p\n", pszDst), VERR_INVALID_PARAMETER);
788 AssertMsgReturn(!(fRename & ~RTPATHRENAME_FLAGS_REPLACE), ("%#x\n", fRename), VERR_INVALID_PARAMETER);
789
790 /*
791 * Hand it to the worker.
792 */
793 int rc = rtPathPosixRename(pszSrc, pszDst, fRename, 0);
794
795 Log(("RTPathRename(%p:{%s}, %p:{%s}, %#x): returns %Rrc\n", pszSrc, pszSrc, pszDst, pszDst, fRename, rc));
796 return rc;
797}
798
799
800RTDECL(bool) RTPathExists(const char *pszPath)
801{
802 /*
803 * Validate input.
804 */
805 AssertPtrReturn(pszPath, false);
806 AssertReturn(*pszPath, false);
807
808 /*
809 * Convert the path and check if it exists using stat().
810 */
811 char *pszNativePath;
812 int rc = rtPathToNative(&pszNativePath, pszPath);
813 if (RT_SUCCESS(rc))
814 {
815 struct stat Stat;
816 if (!stat(pszNativePath, &Stat))
817 rc = VINF_SUCCESS;
818 else
819 rc = VERR_GENERAL_FAILURE;
820 RTStrFree(pszNativePath);
821 }
822 return RT_SUCCESS(rc);
823}
824
825
826RTDECL(int) RTPathSetCurrent(const char *pszPath)
827{
828 /*
829 * Validate input.
830 */
831 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
832 AssertReturn(*pszPath, VERR_INVALID_PARAMETER);
833
834 /*
835 * Change the directory.
836 */
837 char *pszNativePath;
838 int rc = rtPathToNative(&pszNativePath, pszPath);
839 if (RT_SUCCESS(rc))
840 {
841 if (chdir(pszNativePath))
842 rc = RTErrConvertFromErrno(errno);
843 RTStrFree(pszNativePath);
844 }
845 return rc;
846}
847
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