VirtualBox

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

Last change on this file since 24006 was 23312, checked in by vboxsync, 15 years ago

IPRT: Fixed mac-ose build break caused by 10.4 not having lutimes().

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