VirtualBox

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

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

IPRT: pathhost changes.

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