VirtualBox

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

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

RTPathAbs: When the path is empty we return VERR_INVALID_PARAMETER like elsewhere, not the current directory.

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