VirtualBox

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

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

IPRT: RTPathQueryInfo and RTPathSetTimes should work on symbolic links when specified. Introduced Ex versions of these to work around this issue. Should also address the lack of symlinks in the RTDirReadEx output as well VWRN_NO_DIRENT_INFO on broken links.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 29.5 KB
Line 
1/* $Id: path-posix.cpp 23291 2009-09-24 16:08:19Z 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) || defined(RT_OS_FREEBSD) || defined(RT_OS_LINUX) || defined(RT_OS_OS2) /** @todo who really has lutimes? */
594 else
595 {
596 if (lutimes(pszNativePath, aTimevals))
597 rc = RTErrConvertFromErrno(errno);
598 }
599#else
600 else
601 {
602 if (pAccessTime && pModificationTime)
603 rc = RTPathQueryInfoEx(pszPath, &ObjInfo, RTFSOBJATTRADD_UNIX, fFlags);
604 if (RT_SUCCESS(rc) && RTFS_IS_SYMLINK(ObjInfo.Attr.fMode))
605 rc = VERR_NS_SYMLINK_SET_TIME;
606 else if (RT_SUCCESS(rc))
607 {
608 if (utimes(pszNativePath, aTimevals))
609 rc = RTErrConvertFromErrno(errno);
610 }
611 }
612#endif
613 if (RT_FAILURE(rc))
614 Log(("RTPathSetTimes('%s',%p,%p,,): failed with %Rrc and errno=%d\n",
615 pszPath, pAccessTime, pModificationTime, rc, errno));
616 }
617 }
618 rtPathFreeNative(pszNativePath);
619 }
620
621 LogFlow(("RTPathSetTimes(%p:{%s}, %p:{%RDtimespec}, %p:{%RDtimespec}, %p:{%RDtimespec}, %p:{%RDtimespec}): return %Rrc\n",
622 pszPath, pszPath, pAccessTime, pAccessTime, pModificationTime, pModificationTime,
623 pChangeTime, pChangeTime, pBirthTime, pBirthTime));
624 return rc;
625}
626
627
628/**
629 * Checks if two files are the one and same file.
630 */
631static bool rtPathSame(const char *pszNativeSrc, const char *pszNativeDst)
632{
633 struct stat SrcStat;
634 if (stat(pszNativeSrc, &SrcStat))
635 return false;
636 struct stat DstStat;
637 if (stat(pszNativeDst, &DstStat))
638 return false;
639 Assert(SrcStat.st_dev && DstStat.st_dev);
640 Assert(SrcStat.st_ino && DstStat.st_ino);
641 if ( SrcStat.st_dev == DstStat.st_dev
642 && SrcStat.st_ino == DstStat.st_ino
643 && (SrcStat.st_mode & S_IFMT) == (DstStat.st_mode & S_IFMT))
644 return true;
645 return false;
646}
647
648
649/**
650 * Worker for RTPathRename, RTDirRename, RTFileRename.
651 *
652 * @returns IPRT status code.
653 * @param pszSrc The source path.
654 * @param pszDst The destintation path.
655 * @param fRename The rename flags.
656 * @param fFileType The filetype. We use the RTFMODE filetypes here. If it's 0,
657 * anything goes. If it's RTFS_TYPE_DIRECTORY we'll check that the
658 * source is a directory. If Its RTFS_TYPE_FILE we'll check that it's
659 * not a directory (we are NOT checking whether it's a file).
660 */
661int rtPathPosixRename(const char *pszSrc, const char *pszDst, unsigned fRename, RTFMODE fFileType)
662{
663 /*
664 * Convert the paths.
665 */
666 char *pszNativeSrc;
667 int rc = rtPathToNative(&pszNativeSrc, pszSrc);
668 if (RT_SUCCESS(rc))
669 {
670 char *pszNativeDst;
671 rc = rtPathToNative(&pszNativeDst, pszDst);
672 if (RT_SUCCESS(rc))
673 {
674 /*
675 * Check that the source exists and that any types that's specified matches.
676 * We have to check this first to avoid getting errnous VERR_ALREADY_EXISTS
677 * errors from the next step.
678 *
679 * There are race conditions here (perhaps unlikly ones but still), but I'm
680 * afraid there is little with can do to fix that.
681 */
682 struct stat SrcStat;
683 if (stat(pszNativeSrc, &SrcStat))
684 rc = RTErrConvertFromErrno(errno);
685 else if (!fFileType)
686 rc = VINF_SUCCESS;
687 else if (RTFS_IS_DIRECTORY(fFileType))
688 rc = S_ISDIR(SrcStat.st_mode) ? VINF_SUCCESS : VERR_NOT_A_DIRECTORY;
689 else
690 rc = S_ISDIR(SrcStat.st_mode) ? VERR_IS_A_DIRECTORY : VINF_SUCCESS;
691 if (RT_SUCCESS(rc))
692 {
693 bool fSameFile = false;
694
695 /*
696 * Check if the target exists, rename is rather destructive.
697 * We'll have to make sure we don't overwrite the source!
698 * Another race condition btw.
699 */
700 struct stat DstStat;
701 if (stat(pszNativeDst, &DstStat))
702 rc = errno == ENOENT ? VINF_SUCCESS : RTErrConvertFromErrno(errno);
703 else
704 {
705 Assert(SrcStat.st_dev && DstStat.st_dev);
706 Assert(SrcStat.st_ino && DstStat.st_ino);
707 if ( SrcStat.st_dev == DstStat.st_dev
708 && SrcStat.st_ino == DstStat.st_ino
709 && (SrcStat.st_mode & S_IFMT) == (SrcStat.st_mode & S_IFMT))
710 {
711 /*
712 * It's likely that we're talking about the same file here.
713 * We should probably check paths or whatever, but for now this'll have to be enough.
714 */
715 fSameFile = true;
716 }
717 if (fSameFile)
718 rc = VINF_SUCCESS;
719 else if (S_ISDIR(DstStat.st_mode) || !(fRename & RTPATHRENAME_FLAGS_REPLACE))
720 rc = VERR_ALREADY_EXISTS;
721 else
722 rc = VINF_SUCCESS;
723
724 }
725 if (RT_SUCCESS(rc))
726 {
727 if (!rename(pszNativeSrc, pszNativeDst))
728 rc = VINF_SUCCESS;
729 else if ( (fRename & RTPATHRENAME_FLAGS_REPLACE)
730 && (errno == ENOTDIR || errno == EEXIST))
731 {
732 /*
733 * Check that the destination isn't a directory.
734 * Yet another race condition.
735 */
736 if (rtPathSame(pszNativeSrc, pszNativeDst))
737 {
738 rc = VINF_SUCCESS;
739 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): appears to be the same file... (errno=%d)\n",
740 pszSrc, pszDst, fRename, fFileType, errno));
741 }
742 else
743 {
744 if (stat(pszNativeDst, &DstStat))
745 rc = errno != ENOENT ? RTErrConvertFromErrno(errno) : VINF_SUCCESS;
746 else if (S_ISDIR(DstStat.st_mode))
747 rc = VERR_ALREADY_EXISTS;
748 else
749 rc = VINF_SUCCESS;
750 if (RT_SUCCESS(rc))
751 {
752 if (!unlink(pszNativeDst))
753 {
754 if (!rename(pszNativeSrc, pszNativeDst))
755 rc = VINF_SUCCESS;
756 else
757 {
758 rc = RTErrConvertFromErrno(errno);
759 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): rename failed rc=%Rrc errno=%d\n",
760 pszSrc, pszDst, fRename, fFileType, rc, errno));
761 }
762 }
763 else
764 {
765 rc = RTErrConvertFromErrno(errno);
766 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): failed to unlink dst rc=%Rrc errno=%d\n",
767 pszSrc, pszDst, fRename, fFileType, rc, errno));
768 }
769 }
770 else
771 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): dst !dir check failed rc=%Rrc\n",
772 pszSrc, pszDst, fRename, fFileType, rc));
773 }
774 }
775 else
776 {
777 rc = RTErrConvertFromErrno(errno);
778 if (errno == ENOTDIR)
779 rc = VERR_ALREADY_EXISTS; /* unless somebody is racing us, this is the right interpretation */
780 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): rename failed rc=%Rrc errno=%d\n",
781 pszSrc, pszDst, fRename, fFileType, rc, errno));
782 }
783 }
784 else
785 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): destination check failed rc=%Rrc errno=%d\n",
786 pszSrc, pszDst, fRename, fFileType, rc, errno));
787 }
788 else
789 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): source type check failed rc=%Rrc errno=%d\n",
790 pszSrc, pszDst, fRename, fFileType, rc, errno));
791
792 rtPathFreeNative(pszNativeDst);
793 }
794 rtPathFreeNative(pszNativeSrc);
795 }
796 return rc;
797}
798
799
800RTR3DECL(int) RTPathRename(const char *pszSrc, const char *pszDst, unsigned fRename)
801{
802 /*
803 * Validate input.
804 */
805 AssertMsgReturn(VALID_PTR(pszSrc), ("%p\n", pszSrc), VERR_INVALID_POINTER);
806 AssertMsgReturn(VALID_PTR(pszDst), ("%p\n", pszDst), VERR_INVALID_POINTER);
807 AssertMsgReturn(*pszSrc, ("%p\n", pszSrc), VERR_INVALID_PARAMETER);
808 AssertMsgReturn(*pszDst, ("%p\n", pszDst), VERR_INVALID_PARAMETER);
809 AssertMsgReturn(!(fRename & ~RTPATHRENAME_FLAGS_REPLACE), ("%#x\n", fRename), VERR_INVALID_PARAMETER);
810
811 /*
812 * Hand it to the worker.
813 */
814 int rc = rtPathPosixRename(pszSrc, pszDst, fRename, 0);
815
816 Log(("RTPathRename(%p:{%s}, %p:{%s}, %#x): returns %Rrc\n", pszSrc, pszSrc, pszDst, pszDst, fRename, rc));
817 return rc;
818}
819
820
821RTDECL(bool) RTPathExists(const char *pszPath)
822{
823 return RTPathExistsEx(pszPath, RTPATH_F_FOLLOW_LINK);
824}
825
826
827RTDECL(bool) RTPathExistsEx(const char *pszPath, uint32_t fFlags)
828{
829 /*
830 * Validate input.
831 */
832 AssertPtrReturn(pszPath, false);
833 AssertReturn(*pszPath, false);
834 Assert(RTPATH_F_IS_VALID(fFlags, 0));
835
836 /*
837 * Convert the path and check if it exists using stat().
838 */
839 char *pszNativePath;
840 int rc = rtPathToNative(&pszNativePath, pszPath);
841 if (RT_SUCCESS(rc))
842 {
843 struct stat Stat;
844 if (fFlags & RTPATH_F_FOLLOW_LINK)
845 rc = stat(pszNativePath, &Stat);
846 else
847 rc = lstat(pszNativePath, &Stat);
848 if (!rc)
849 rc = VINF_SUCCESS;
850 else
851 rc = VERR_GENERAL_FAILURE;
852 RTStrFree(pszNativePath);
853 }
854 return RT_SUCCESS(rc);
855}
856
857
858RTDECL(int) RTPathGetCurrent(char *pszPath, size_t cchPath)
859{
860 int rc;
861 char szNativeCurDir[RTPATH_MAX];
862 if (getcwd(szNativeCurDir, sizeof(szNativeCurDir)) != NULL)
863 {
864 char *pszCurDir;
865 rc = rtPathFromNative(&pszCurDir, szNativeCurDir);
866 if (RT_SUCCESS(rc))
867 {
868 size_t cchCurDir = strlen(pszCurDir);
869 if (cchCurDir < cchPath)
870 {
871 memcpy(pszPath, pszCurDir, cchCurDir + 1);
872 RTStrFree(pszCurDir);
873 return VINF_SUCCESS;
874 }
875
876 rc = VERR_BUFFER_OVERFLOW;
877 RTStrFree(pszCurDir);
878 }
879 }
880 else
881 rc = RTErrConvertFromErrno(errno);
882 return rc;
883}
884
885
886RTDECL(int) RTPathSetCurrent(const char *pszPath)
887{
888 /*
889 * Validate input.
890 */
891 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
892 AssertReturn(*pszPath, VERR_INVALID_PARAMETER);
893
894 /*
895 * Change the directory.
896 */
897 char *pszNativePath;
898 int rc = rtPathToNative(&pszNativePath, pszPath);
899 if (RT_SUCCESS(rc))
900 {
901 if (chdir(pszNativePath))
902 rc = RTErrConvertFromErrno(errno);
903 RTStrFree(pszNativePath);
904 }
905 return rc;
906}
907
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