VirtualBox

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

Last change on this file since 34002 was 34002, checked in by vboxsync, 14 years ago

iprt: Working on tar vfs.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 27.6 KB
Line 
1/* $Id: path-posix.cpp 34002 2010-11-11 17:16:37Z vboxsync $ */
2/** @file
3 * IPRT - Path Manipulation, POSIX.
4 */
5
6/*
7 * Copyright (C) 2006-2007 Oracle Corporation
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
27
28/*******************************************************************************
29* Header Files *
30*******************************************************************************/
31#define LOG_GROUP RTLOGGROUP_PATH
32#include <stdlib.h>
33#include <limits.h>
34#include <errno.h>
35#include <unistd.h>
36#include <sys/stat.h>
37#include <sys/time.h>
38#include <stdio.h>
39#include <sys/types.h>
40#include <pwd.h>
41#include <grp.h>
42
43#include <iprt/path.h>
44#include <iprt/env.h>
45#include <iprt/assert.h>
46#include <iprt/string.h>
47#include <iprt/err.h>
48#include <iprt/log.h>
49#include "internal/path.h"
50#include "internal/process.h"
51#include "internal/fs.h"
52
53#ifdef RT_OS_L4
54# include <l4/vboxserver/vboxserver.h>
55#endif
56
57
58
59
60RTDECL(int) RTPathReal(const char *pszPath, char *pszRealPath, size_t cchRealPath)
61{
62 /*
63 * Convert input.
64 */
65 char const *pszNativePath;
66 int rc = rtPathToNative(&pszNativePath, pszPath, NULL);
67 if (RT_SUCCESS(rc))
68 {
69 /*
70 * On POSIX platforms the API doesn't take a length parameter, which makes it
71 * a little bit more work.
72 */
73 char szTmpPath[PATH_MAX + 1];
74 const char *psz = realpath(pszNativePath, szTmpPath);
75 if (psz)
76 rc = rtPathFromNativeCopy(pszRealPath, cchRealPath, szTmpPath, NULL);
77 else
78 rc = RTErrConvertFromErrno(errno);
79 rtPathFreeNative(pszNativePath, pszPath);
80 }
81
82 LogFlow(("RTPathReal(%p:{%s}, %p:{%s}, %u): returns %Rrc\n", pszPath, pszPath,
83 pszRealPath, RT_SUCCESS(rc) ? pszRealPath : "<failed>", cchRealPath));
84 return rc;
85}
86
87
88/**
89 * Cleans up a path specifier a little bit.
90 * This includes removing duplicate slashes, unnecessary single dots, and
91 * trailing slashes. Also, replaces all RTPATH_SLASH characters with '/'.
92 *
93 * @returns Number of bytes in the clean path.
94 * @param pszPath The path to cleanup.
95 */
96static int fsCleanPath(char *pszPath)
97{
98 /*
99 * Change to '/' and remove duplicates.
100 */
101 char *pszSrc = pszPath;
102 char *pszTrg = pszPath;
103#ifdef HAVE_UNC
104 int fUnc = 0;
105 if ( RTPATH_IS_SLASH(pszPath[0])
106 && RTPATH_IS_SLASH(pszPath[1]))
107 { /* Skip first slash in a unc path. */
108 pszSrc++;
109 *pszTrg++ = '/';
110 fUnc = 1;
111 }
112#endif
113
114 for (;;)
115 {
116 char ch = *pszSrc++;
117 if (RTPATH_IS_SLASH(ch))
118 {
119 *pszTrg++ = '/';
120 for (;;)
121 {
122 do ch = *pszSrc++;
123 while (RTPATH_IS_SLASH(ch));
124
125 /* Remove '/./' and '/.'. */
126 if (ch != '.' || (*pszSrc && !RTPATH_IS_SLASH(*pszSrc)))
127 break;
128 }
129 }
130 *pszTrg = ch;
131 if (!ch)
132 break;
133 pszTrg++;
134 }
135
136 /*
137 * Remove trailing slash if the path may be pointing to a directory.
138 */
139 int cch = pszTrg - pszPath;
140 if ( cch > 1
141 && RTPATH_IS_SLASH(pszTrg[-1])
142#ifdef HAVE_DRIVE
143 && !RTPATH_IS_VOLSEP(pszTrg[-2])
144#endif
145 && !RTPATH_IS_SLASH(pszTrg[-2]))
146 pszPath[--cch] = '\0';
147
148 return cch;
149}
150
151
152RTDECL(int) RTPathAbs(const char *pszPath, char *pszAbsPath, size_t cchAbsPath)
153{
154 int rc;
155
156 /*
157 * Validation.
158 */
159 AssertPtr(pszAbsPath);
160 AssertPtr(pszPath);
161 if (RT_UNLIKELY(!*pszPath))
162 return VERR_INVALID_PARAMETER;
163
164 /*
165 * Make a clean working copy of the input.
166 */
167 size_t cchPath = strlen(pszPath);
168 if (cchPath > PATH_MAX)
169 {
170 LogFlow(("RTPathAbs(%p:{%s}, %p, %d): returns %Rrc\n", pszPath, pszPath, pszAbsPath, cchAbsPath, VERR_FILENAME_TOO_LONG));
171 return VERR_FILENAME_TOO_LONG;
172 }
173
174 char szTmpPath[PATH_MAX + 1];
175 memcpy(szTmpPath, pszPath, cchPath + 1);
176 size_t cchTmpPath = fsCleanPath(szTmpPath);
177
178 /*
179 * Handle "." specially (fsCleanPath does).
180 */
181 if (szTmpPath[0] == '.' && !szTmpPath[1])
182 return RTPathGetCurrent(pszAbsPath, cchAbsPath);
183
184 /*
185 * Do we have a root slash?
186 */
187 char *pszCur = szTmpPath;
188#ifdef HAVE_DRIVE
189 if (pszCur[0] && RTPATH_IS_VOLSEP(pszCur[1]) && pszCur[2] == '/')
190 pszCur += 3;
191# ifdef HAVE_UNC
192 else if (pszCur[0] == '/' && pszCur[1] == '/')
193 pszCur += 2;
194# endif
195#else /* !HAVE_DRIVE */
196 if (pszCur[0] == '/')
197 pszCur += 1;
198#endif /* !HAVE_DRIVE */
199 else
200 {
201 /*
202 * No, prepend the current directory to the relative path.
203 */
204 char szCurDir[RTPATH_MAX];
205 rc = RTPathGetCurrent(szCurDir, sizeof(szCurDir));
206 AssertRCReturn(rc, rc);
207
208 size_t cchCurDir = fsCleanPath(szCurDir); /* paranoia */
209 if (cchCurDir + cchTmpPath + 1 > PATH_MAX)
210 {
211 LogFlow(("RTPathAbs(%p:{%s}, %p, %d): returns %Rrc\n", pszPath, pszPath, pszAbsPath, cchAbsPath, VERR_FILENAME_TOO_LONG));
212 return VERR_FILENAME_TOO_LONG;
213 }
214
215 memmove(szTmpPath + cchCurDir + 1, szTmpPath, cchTmpPath + 1);
216 memcpy(szTmpPath, szCurDir, cchCurDir);
217 szTmpPath[cchCurDir] = '/';
218
219
220#ifdef HAVE_DRIVE
221 if (pszCur[0] && RTPATH_IS_VOLSEP(pszCur[1]) && pszCur[2] == '/')
222 pszCur += 3;
223# ifdef HAVE_UNC
224 else if (pszCur[0] == '/' && pszCur[1] == '/')
225 pszCur += 2;
226# endif
227#else
228 if (pszCur[0] == '/')
229 pszCur += 1;
230#endif
231 else
232 AssertMsgFailedReturn(("pszCur=%s\n", pszCur), VERR_INTERNAL_ERROR);
233 }
234
235 char *pszTop = pszCur;
236
237 /*
238 * Get rid of double dot path components by evaluating them.
239 */
240 for (;;)
241 {
242 if ( pszCur[0] == '.'
243 && pszCur[1] == '.'
244 && (!pszCur[2] || pszCur[2] == '/'))
245 {
246 /* rewind to the previous component if any */
247 char *pszPrev = pszCur - 1;
248 if (pszPrev > pszTop)
249 while (*--pszPrev != '/')
250 ;
251
252 AssertMsg(*pszPrev == '/', ("szTmpPath={%s}, pszPrev=+%u\n", szTmpPath, pszPrev - szTmpPath));
253 memmove(pszPrev, pszCur + 2, strlen(pszCur + 2) + 1);
254
255 pszCur = pszPrev;
256 }
257 else
258 {
259 /* advance to end of component. */
260 while (*pszCur && *pszCur != '/')
261 pszCur++;
262 }
263
264 if (!*pszCur)
265 break;
266
267 /* skip the slash */
268 ++pszCur;
269 }
270
271 if (pszCur < pszTop)
272 {
273 /*
274 * We overwrote the root slash with '\0', restore it.
275 */
276 *pszCur++ = '/';
277 *pszCur = '\0';
278 }
279 else if (pszCur > pszTop && pszCur[-1] == '/')
280 {
281 /*
282 * Extra trailing slash in a non-root path, remove it.
283 * (A bit questionable...)
284 */
285 *--pszCur = '\0';
286 }
287
288 /*
289 * Copy the result to the user buffer.
290 */
291 cchTmpPath = pszCur - szTmpPath;
292 if (cchTmpPath < cchAbsPath)
293 {
294 memcpy(pszAbsPath, szTmpPath, cchTmpPath + 1);
295 rc = VINF_SUCCESS;
296 }
297 else
298 rc = VERR_BUFFER_OVERFLOW;
299
300 LogFlow(("RTPathAbs(%p:{%s}, %p:{%s}, %d): returns %Rrc\n", pszPath, pszPath, pszAbsPath,
301 RT_SUCCESS(rc) ? pszAbsPath : "<failed>", cchAbsPath, rc));
302 return rc;
303}
304
305
306RTR3DECL(int) RTPathQueryInfo(const char *pszPath, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs)
307{
308 return RTPathQueryInfoEx(pszPath, pObjInfo, enmAdditionalAttribs, RTPATH_F_ON_LINK);
309}
310
311
312RTR3DECL(int) RTPathQueryInfoEx(const char *pszPath, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs, uint32_t fFlags)
313{
314 /*
315 * Validate input.
316 */
317 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
318 AssertReturn(*pszPath, VERR_INVALID_PARAMETER);
319 AssertPtrReturn(pObjInfo, VERR_INVALID_POINTER);
320 AssertMsgReturn( enmAdditionalAttribs >= RTFSOBJATTRADD_NOTHING
321 && enmAdditionalAttribs <= RTFSOBJATTRADD_LAST,
322 ("Invalid enmAdditionalAttribs=%p\n", enmAdditionalAttribs),
323 VERR_INVALID_PARAMETER);
324 AssertMsgReturn(RTPATH_F_IS_VALID(fFlags, 0), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
325
326 /*
327 * Convert the filename.
328 */
329 char const *pszNativePath;
330 int rc = rtPathToNative(&pszNativePath, pszPath, NULL);
331 if (RT_SUCCESS(rc))
332 {
333 struct stat Stat;
334 if (fFlags & RTPATH_F_FOLLOW_LINK)
335 rc = stat(pszNativePath, &Stat);
336 else
337 rc = lstat(pszNativePath, &Stat); /** @todo how doesn't have lstat again? */
338 if (!rc)
339 {
340 rtFsConvertStatToObjInfo(pObjInfo, &Stat, pszPath, 0);
341 switch (enmAdditionalAttribs)
342 {
343 case RTFSOBJATTRADD_NOTHING:
344 case RTFSOBJATTRADD_UNIX:
345 Assert(pObjInfo->Attr.enmAdditional == RTFSOBJATTRADD_UNIX);
346 break;
347
348 case RTFSOBJATTRADD_UNIX_OWNER:
349 rtFsObjInfoAttrSetUnixOwner(pObjInfo, Stat.st_uid);
350 break;
351
352 case RTFSOBJATTRADD_UNIX_GROUP:
353 rtFsObjInfoAttrSetUnixGroup(pObjInfo, Stat.st_gid);
354 break;
355
356 case RTFSOBJATTRADD_EASIZE:
357 /** @todo Use SGI extended attribute interface to query EA info. */
358 pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_EASIZE;
359 pObjInfo->Attr.u.EASize.cb = 0;
360 break;
361
362 default:
363 AssertMsgFailed(("Impossible!\n"));
364 return VERR_INTERNAL_ERROR;
365 }
366 }
367 else
368 rc = RTErrConvertFromErrno(errno);
369 rtPathFreeNative(pszNativePath, pszPath);
370 }
371
372 LogFlow(("RTPathQueryInfo(%p:{%s}, pObjInfo=%p, %d): returns %Rrc\n",
373 pszPath, pszPath, pObjInfo, enmAdditionalAttribs, rc));
374 return rc;
375}
376
377
378RTR3DECL(int) RTPathSetTimes(const char *pszPath, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
379 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
380{
381 return RTPathSetTimesEx(pszPath, pAccessTime, pModificationTime, pChangeTime, pBirthTime, RTPATH_F_ON_LINK);
382}
383
384
385RTR3DECL(int) RTPathSetTimesEx(const char *pszPath, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
386 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime, uint32_t fFlags)
387{
388 /*
389 * Validate input.
390 */
391 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
392 AssertReturn(*pszPath, VERR_INVALID_PARAMETER);
393 AssertPtrNullReturn(pAccessTime, VERR_INVALID_POINTER);
394 AssertPtrNullReturn(pModificationTime, VERR_INVALID_POINTER);
395 AssertPtrNullReturn(pChangeTime, VERR_INVALID_POINTER);
396 AssertPtrNullReturn(pBirthTime, VERR_INVALID_POINTER);
397 AssertMsgReturn(RTPATH_F_IS_VALID(fFlags, 0), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
398
399 /*
400 * Convert the paths.
401 */
402 char const *pszNativePath;
403 int rc = rtPathToNative(&pszNativePath, pszPath, NULL);
404 if (RT_SUCCESS(rc))
405 {
406 RTFSOBJINFO ObjInfo;
407
408 /*
409 * If it's a no-op, we'll only verify the existance of the file.
410 */
411 if (!pAccessTime && !pModificationTime)
412 rc = RTPathQueryInfoEx(pszPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, fFlags);
413 else
414 {
415 /*
416 * Convert the input to timeval, getting the missing one if necessary,
417 * and call the API which does the change.
418 */
419 struct timeval aTimevals[2];
420 if (pAccessTime && pModificationTime)
421 {
422 RTTimeSpecGetTimeval(pAccessTime, &aTimevals[0]);
423 RTTimeSpecGetTimeval(pModificationTime, &aTimevals[1]);
424 }
425 else
426 {
427 rc = RTPathQueryInfoEx(pszPath, &ObjInfo, RTFSOBJATTRADD_UNIX, fFlags);
428 if (RT_SUCCESS(rc))
429 {
430 RTTimeSpecGetTimeval(pAccessTime ? pAccessTime : &ObjInfo.AccessTime, &aTimevals[0]);
431 RTTimeSpecGetTimeval(pModificationTime ? pModificationTime : &ObjInfo.ModificationTime, &aTimevals[1]);
432 }
433 else
434 Log(("RTPathSetTimes('%s',%p,%p,,): RTPathQueryInfo failed with %Rrc\n",
435 pszPath, pAccessTime, pModificationTime, rc));
436 }
437 if (RT_SUCCESS(rc))
438 {
439 if (fFlags & RTPATH_F_FOLLOW_LINK)
440 {
441 if (utimes(pszNativePath, aTimevals))
442 rc = RTErrConvertFromErrno(errno);
443 }
444#if (defined(RT_OS_DARWIN) && MAC_OS_X_VERSION_MIN_REQUIRED >= 1050) \
445 || defined(RT_OS_FREEBSD) \
446 || defined(RT_OS_LINUX) \
447 || defined(RT_OS_OS2) /** @todo who really has lutimes? */
448 else
449 {
450 if (lutimes(pszNativePath, aTimevals))
451 rc = RTErrConvertFromErrno(errno);
452 }
453#else
454 else
455 {
456 if (pAccessTime && pModificationTime)
457 rc = RTPathQueryInfoEx(pszPath, &ObjInfo, RTFSOBJATTRADD_UNIX, fFlags);
458 if (RT_SUCCESS(rc) && RTFS_IS_SYMLINK(ObjInfo.Attr.fMode))
459 rc = VERR_NS_SYMLINK_SET_TIME;
460 else if (RT_SUCCESS(rc))
461 {
462 if (utimes(pszNativePath, aTimevals))
463 rc = RTErrConvertFromErrno(errno);
464 }
465 }
466#endif
467 if (RT_FAILURE(rc))
468 Log(("RTPathSetTimes('%s',%p,%p,,): failed with %Rrc and errno=%d\n",
469 pszPath, pAccessTime, pModificationTime, rc, errno));
470 }
471 }
472 rtPathFreeNative(pszNativePath, pszPath);
473 }
474
475 LogFlow(("RTPathSetTimes(%p:{%s}, %p:{%RDtimespec}, %p:{%RDtimespec}, %p:{%RDtimespec}, %p:{%RDtimespec}): return %Rrc\n",
476 pszPath, pszPath, pAccessTime, pAccessTime, pModificationTime, pModificationTime,
477 pChangeTime, pChangeTime, pBirthTime, pBirthTime, rc));
478 return rc;
479}
480
481
482RTR3DECL(int) RTPathSetOwner(const char *pszPath, uint32_t uid, uint32_t gid)
483{
484 return RTPathSetOwnerEx(pszPath, uid, gid, RTPATH_F_ON_LINK);
485}
486
487
488RTR3DECL(int) RTPathSetOwnerEx(const char *pszPath, uint32_t uid, uint32_t gid, uint32_t fFlags)
489{
490 /*
491 * Validate input.
492 */
493 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
494 AssertReturn(*pszPath, VERR_INVALID_PARAMETER);
495 AssertMsgReturn(RTPATH_F_IS_VALID(fFlags, 0), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
496 uid_t uidNative = uid != UINT32_MAX ? (uid_t)uid : (uid_t)-1;
497 AssertReturn(uid == uidNative, VERR_INVALID_PARAMETER);
498 gid_t gidNative = gid != UINT32_MAX ? (gid_t)gid : (uid_t)-1;
499 AssertReturn(gid == gidNative, VERR_INVALID_PARAMETER);
500
501 /*
502 * Convert the path.
503 */
504 char const *pszNativePath;
505 int rc = rtPathToNative(&pszNativePath, pszPath, NULL);
506 if (RT_SUCCESS(rc))
507 {
508 if (fFlags & RTPATH_F_FOLLOW_LINK)
509 {
510 if (chown(pszNativePath, uidNative, gidNative))
511 rc = RTErrConvertFromErrno(errno);
512 }
513#if 1
514 else
515 {
516 if (lchown(pszNativePath, uidNative, gidNative))
517 rc = RTErrConvertFromErrno(errno);
518 }
519#else
520 else
521 {
522 RTFSOBJINFO ObjInfo;
523 rc = RTPathQueryInfoEx(pszPath, &ObjInfo, RTFSOBJATTRADD_UNIX, fFlags);
524 if (RT_SUCCESS(rc) && RTFS_IS_SYMLINK(ObjInfo.Attr.fMode))
525 rc = VERR_NS_SYMLINK_CHANGE_OWNER;
526 else if (RT_SUCCESS(rc))
527 {
528 if (lchown(pszNativePath, uidNative, gidNative))
529 rc = RTErrConvertFromErrno(errno);
530 }
531 }
532#endif
533 if (RT_FAILURE(rc))
534 Log(("RTPathSetOwnerEx('%s',%d,%d): failed with %Rrc and errno=%d\n",
535 pszPath, uid, gid, rc, errno));
536
537 rtPathFreeNative(pszNativePath, pszPath);
538 }
539
540 LogFlow(("RTPathSetOwnerEx(%p:{%s}, uid, gid): return %Rrc\n",
541 pszPath, pszPath, uid, gid, rc));
542 return rc;
543}
544
545
546RTR3DECL(int) RTPathSetMode(const char *pszPath, RTFMODE fMode)
547{
548 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
549 AssertReturn(*pszPath, VERR_INVALID_PARAMETER);
550
551 int rc;
552 fMode = rtFsModeNormalize(fMode, pszPath, 0);
553 if (rtFsModeIsValidPermissions(fMode))
554 {
555 char const *pszNativePath;
556 rc = rtPathToNative(&pszNativePath, pszPath, NULL);
557 if (RT_SUCCESS(rc))
558 {
559 if (chmod(pszNativePath, fMode & RTFS_UNIX_MASK) != 0)
560 rc = RTErrConvertFromErrno(errno);
561 rtPathFreeNative(pszNativePath, pszPath);
562 }
563 }
564 else
565 {
566 AssertMsgFailed(("Invalid file mode! %RTfmode\n", fMode));
567 rc = VERR_INVALID_FMODE;
568 }
569 return rc;
570}
571
572
573/**
574 * Checks if two files are the one and same file.
575 */
576static bool rtPathSame(const char *pszNativeSrc, const char *pszNativeDst)
577{
578 struct stat SrcStat;
579 if (stat(pszNativeSrc, &SrcStat))
580 return false;
581 struct stat DstStat;
582 if (stat(pszNativeDst, &DstStat))
583 return false;
584 Assert(SrcStat.st_dev && DstStat.st_dev);
585 Assert(SrcStat.st_ino && DstStat.st_ino);
586 if ( SrcStat.st_dev == DstStat.st_dev
587 && SrcStat.st_ino == DstStat.st_ino
588 && (SrcStat.st_mode & S_IFMT) == (DstStat.st_mode & S_IFMT))
589 return true;
590 return false;
591}
592
593
594/**
595 * Worker for RTPathRename, RTDirRename, RTFileRename.
596 *
597 * @returns IPRT status code.
598 * @param pszSrc The source path.
599 * @param pszDst The destination path.
600 * @param fRename The rename flags.
601 * @param fFileType The filetype. We use the RTFMODE filetypes here. If it's 0,
602 * anything goes. If it's RTFS_TYPE_DIRECTORY we'll check that the
603 * source is a directory. If Its RTFS_TYPE_FILE we'll check that it's
604 * not a directory (we are NOT checking whether it's a file).
605 */
606DECLHIDDEN(int) rtPathPosixRename(const char *pszSrc, const char *pszDst, unsigned fRename, RTFMODE fFileType)
607{
608 /*
609 * Convert the paths.
610 */
611 char const *pszNativeSrc;
612 int rc = rtPathToNative(&pszNativeSrc, pszSrc, NULL);
613 if (RT_SUCCESS(rc))
614 {
615 char const *pszNativeDst;
616 rc = rtPathToNative(&pszNativeDst, pszDst, NULL);
617 if (RT_SUCCESS(rc))
618 {
619 /*
620 * Check that the source exists and that any types that's specified matches.
621 * We have to check this first to avoid getting errnous VERR_ALREADY_EXISTS
622 * errors from the next step.
623 *
624 * There are race conditions here (perhaps unlikely ones but still), but I'm
625 * afraid there is little with can do to fix that.
626 */
627 struct stat SrcStat;
628 if (stat(pszNativeSrc, &SrcStat))
629 rc = RTErrConvertFromErrno(errno);
630 else if (!fFileType)
631 rc = VINF_SUCCESS;
632 else if (RTFS_IS_DIRECTORY(fFileType))
633 rc = S_ISDIR(SrcStat.st_mode) ? VINF_SUCCESS : VERR_NOT_A_DIRECTORY;
634 else
635 rc = S_ISDIR(SrcStat.st_mode) ? VERR_IS_A_DIRECTORY : VINF_SUCCESS;
636 if (RT_SUCCESS(rc))
637 {
638 bool fSameFile = false;
639
640 /*
641 * Check if the target exists, rename is rather destructive.
642 * We'll have to make sure we don't overwrite the source!
643 * Another race condition btw.
644 */
645 struct stat DstStat;
646 if (stat(pszNativeDst, &DstStat))
647 rc = errno == ENOENT ? VINF_SUCCESS : RTErrConvertFromErrno(errno);
648 else
649 {
650 Assert(SrcStat.st_dev && DstStat.st_dev);
651 Assert(SrcStat.st_ino && DstStat.st_ino);
652 if ( SrcStat.st_dev == DstStat.st_dev
653 && SrcStat.st_ino == DstStat.st_ino
654 && (SrcStat.st_mode & S_IFMT) == (DstStat.st_mode & S_IFMT))
655 {
656 /*
657 * It's likely that we're talking about the same file here.
658 * We should probably check paths or whatever, but for now this'll have to be enough.
659 */
660 fSameFile = true;
661 }
662 if (fSameFile)
663 rc = VINF_SUCCESS;
664 else if (S_ISDIR(DstStat.st_mode) || !(fRename & RTPATHRENAME_FLAGS_REPLACE))
665 rc = VERR_ALREADY_EXISTS;
666 else
667 rc = VINF_SUCCESS;
668
669 }
670 if (RT_SUCCESS(rc))
671 {
672 if (!rename(pszNativeSrc, pszNativeDst))
673 rc = VINF_SUCCESS;
674 else if ( (fRename & RTPATHRENAME_FLAGS_REPLACE)
675 && (errno == ENOTDIR || errno == EEXIST))
676 {
677 /*
678 * Check that the destination isn't a directory.
679 * Yet another race condition.
680 */
681 if (rtPathSame(pszNativeSrc, pszNativeDst))
682 {
683 rc = VINF_SUCCESS;
684 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): appears to be the same file... (errno=%d)\n",
685 pszSrc, pszDst, fRename, fFileType, errno));
686 }
687 else
688 {
689 if (stat(pszNativeDst, &DstStat))
690 rc = errno != ENOENT ? RTErrConvertFromErrno(errno) : VINF_SUCCESS;
691 else if (S_ISDIR(DstStat.st_mode))
692 rc = VERR_ALREADY_EXISTS;
693 else
694 rc = VINF_SUCCESS;
695 if (RT_SUCCESS(rc))
696 {
697 if (!unlink(pszNativeDst))
698 {
699 if (!rename(pszNativeSrc, pszNativeDst))
700 rc = VINF_SUCCESS;
701 else
702 {
703 rc = RTErrConvertFromErrno(errno);
704 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): rename failed rc=%Rrc errno=%d\n",
705 pszSrc, pszDst, fRename, fFileType, rc, errno));
706 }
707 }
708 else
709 {
710 rc = RTErrConvertFromErrno(errno);
711 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): failed to unlink dst rc=%Rrc errno=%d\n",
712 pszSrc, pszDst, fRename, fFileType, rc, errno));
713 }
714 }
715 else
716 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): dst !dir check failed rc=%Rrc\n",
717 pszSrc, pszDst, fRename, fFileType, rc));
718 }
719 }
720 else
721 {
722 rc = RTErrConvertFromErrno(errno);
723 if (errno == ENOTDIR)
724 rc = VERR_ALREADY_EXISTS; /* unless somebody is racing us, this is the right interpretation */
725 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): rename failed rc=%Rrc errno=%d\n",
726 pszSrc, pszDst, fRename, fFileType, rc, errno));
727 }
728 }
729 else
730 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): destination check failed rc=%Rrc errno=%d\n",
731 pszSrc, pszDst, fRename, fFileType, rc, errno));
732 }
733 else
734 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): source type check failed rc=%Rrc errno=%d\n",
735 pszSrc, pszDst, fRename, fFileType, rc, errno));
736
737 rtPathFreeNative(pszNativeDst, pszDst);
738 }
739 rtPathFreeNative(pszNativeSrc, pszSrc);
740 }
741 return rc;
742}
743
744
745RTR3DECL(int) RTPathRename(const char *pszSrc, const char *pszDst, unsigned fRename)
746{
747 /*
748 * Validate input.
749 */
750 AssertMsgReturn(VALID_PTR(pszSrc), ("%p\n", pszSrc), VERR_INVALID_POINTER);
751 AssertMsgReturn(VALID_PTR(pszDst), ("%p\n", pszDst), VERR_INVALID_POINTER);
752 AssertMsgReturn(*pszSrc, ("%p\n", pszSrc), VERR_INVALID_PARAMETER);
753 AssertMsgReturn(*pszDst, ("%p\n", pszDst), VERR_INVALID_PARAMETER);
754 AssertMsgReturn(!(fRename & ~RTPATHRENAME_FLAGS_REPLACE), ("%#x\n", fRename), VERR_INVALID_PARAMETER);
755
756 /*
757 * Hand it to the worker.
758 */
759 int rc = rtPathPosixRename(pszSrc, pszDst, fRename, 0);
760
761 Log(("RTPathRename(%p:{%s}, %p:{%s}, %#x): returns %Rrc\n", pszSrc, pszSrc, pszDst, pszDst, fRename, rc));
762 return rc;
763}
764
765
766RTDECL(bool) RTPathExists(const char *pszPath)
767{
768 return RTPathExistsEx(pszPath, RTPATH_F_FOLLOW_LINK);
769}
770
771
772RTDECL(bool) RTPathExistsEx(const char *pszPath, uint32_t fFlags)
773{
774 /*
775 * Validate input.
776 */
777 AssertPtrReturn(pszPath, false);
778 AssertReturn(*pszPath, false);
779 Assert(RTPATH_F_IS_VALID(fFlags, 0));
780
781 /*
782 * Convert the path and check if it exists using stat().
783 */
784 char const *pszNativePath;
785 int rc = rtPathToNative(&pszNativePath, pszPath, NULL);
786 if (RT_SUCCESS(rc))
787 {
788 struct stat Stat;
789 if (fFlags & RTPATH_F_FOLLOW_LINK)
790 rc = stat(pszNativePath, &Stat);
791 else
792 rc = lstat(pszNativePath, &Stat);
793 if (!rc)
794 rc = VINF_SUCCESS;
795 else
796 rc = VERR_GENERAL_FAILURE;
797 rtPathFreeNative(pszNativePath, pszPath);
798 }
799 return RT_SUCCESS(rc);
800}
801
802
803RTDECL(int) RTPathGetCurrent(char *pszPath, size_t cchPath)
804{
805 int rc;
806 char szNativeCurDir[RTPATH_MAX];
807 if (getcwd(szNativeCurDir, sizeof(szNativeCurDir)) != NULL)
808 rc = rtPathFromNativeCopy(pszPath, cchPath, szNativeCurDir, NULL);
809 else
810 rc = RTErrConvertFromErrno(errno);
811 return rc;
812}
813
814
815RTDECL(int) RTPathSetCurrent(const char *pszPath)
816{
817 /*
818 * Validate input.
819 */
820 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
821 AssertReturn(*pszPath, VERR_INVALID_PARAMETER);
822
823 /*
824 * Change the directory.
825 */
826 char const *pszNativePath;
827 int rc = rtPathToNative(&pszNativePath, pszPath, NULL);
828 if (RT_SUCCESS(rc))
829 {
830 if (chdir(pszNativePath))
831 rc = RTErrConvertFromErrno(errno);
832 rtPathFreeNative(pszNativePath, pszPath);
833 }
834 return rc;
835}
836
Note: See TracBrowser for help on using the repository browser.

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