VirtualBox

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

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

iprt: More path conversion avoidance.

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