VirtualBox

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

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

iprt: added RTPathGetCurrent.

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