VirtualBox

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

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

getenv -> RTEnvGet.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 27.9 KB
Line 
1/* $Id: path-posix.cpp 20819 2009-06-23 12:15:07Z 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 /*
466 * Validate input.
467 */
468 AssertMsgReturn(VALID_PTR(pszPath), ("%p\n", pszPath), VERR_INVALID_POINTER);
469 AssertReturn(*pszPath, VERR_INVALID_PARAMETER);
470 AssertMsgReturn(VALID_PTR(pObjInfo), ("%p\n", pszPath), VERR_INVALID_POINTER);
471 AssertMsgReturn( enmAdditionalAttribs >= RTFSOBJATTRADD_NOTHING
472 && enmAdditionalAttribs <= RTFSOBJATTRADD_LAST,
473 ("Invalid enmAdditionalAttribs=%p\n", enmAdditionalAttribs),
474 VERR_INVALID_PARAMETER);
475
476 /*
477 * Convert the filename.
478 */
479 char *pszNativePath;
480 int rc = rtPathToNative(&pszNativePath, pszPath);
481 if (RT_SUCCESS(rc))
482 {
483 struct stat Stat;
484 if (!stat(pszNativePath, &Stat))
485 {
486 rtFsConvertStatToObjInfo(pObjInfo, &Stat, pszPath, 0);
487 switch (enmAdditionalAttribs)
488 {
489 case RTFSOBJATTRADD_EASIZE:
490 /** @todo Use SGI extended attribute interface to query EA info. */
491 pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_EASIZE;
492 pObjInfo->Attr.u.EASize.cb = 0;
493 break;
494
495 case RTFSOBJATTRADD_NOTHING:
496 case RTFSOBJATTRADD_UNIX:
497 Assert(pObjInfo->Attr.enmAdditional == RTFSOBJATTRADD_UNIX);
498 break;
499
500 default:
501 AssertMsgFailed(("Impossible!\n"));
502 return VERR_INTERNAL_ERROR;
503 }
504 }
505 else
506 rc = RTErrConvertFromErrno(errno);
507 rtPathFreeNative(pszNativePath);
508 }
509
510 LogFlow(("RTPathQueryInfo(%p:{%s}, pObjInfo=%p, %d): returns %Rrc\n",
511 pszPath, pszPath, pObjInfo, enmAdditionalAttribs, rc));
512 return rc;
513}
514
515
516RTR3DECL(int) RTPathSetTimes(const char *pszPath, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
517 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
518{
519 /*
520 * Validate input.
521 */
522 AssertMsgReturn(VALID_PTR(pszPath), ("%p\n", pszPath), VERR_INVALID_POINTER);
523 AssertMsgReturn(*pszPath, ("%p\n", pszPath), VERR_INVALID_PARAMETER);
524 AssertMsgReturn(!pAccessTime || VALID_PTR(pAccessTime), ("%p\n", pAccessTime), VERR_INVALID_POINTER);
525 AssertMsgReturn(!pModificationTime || VALID_PTR(pModificationTime), ("%p\n", pModificationTime), VERR_INVALID_POINTER);
526 AssertMsgReturn(!pChangeTime || VALID_PTR(pChangeTime), ("%p\n", pChangeTime), VERR_INVALID_POINTER);
527 AssertMsgReturn(!pBirthTime || VALID_PTR(pBirthTime), ("%p\n", pBirthTime), VERR_INVALID_POINTER);
528
529 /*
530 * Convert the paths.
531 */
532 char *pszNativePath;
533 int rc = rtPathToNative(&pszNativePath, pszPath);
534 if (RT_SUCCESS(rc))
535 {
536 /*
537 * If it's a no-op, we'll only verify the existance of the file.
538 */
539 if (!pAccessTime && !pModificationTime)
540 {
541 struct stat Stat;
542 if (!stat(pszNativePath, &Stat))
543 rc = VINF_SUCCESS;
544 else
545 {
546 rc = RTErrConvertFromErrno(errno);
547 Log(("RTPathSetTimes('%s',,,,): failed with %Rrc and errno=%d\n", pszPath, rc, errno));
548 }
549 }
550 else
551 {
552 /*
553 * Convert the input to timeval, getting the missing one if necessary,
554 * and call the API which does the change.
555 */
556 struct timeval aTimevals[2];
557 if (pAccessTime && pModificationTime)
558 {
559 RTTimeSpecGetTimeval(pAccessTime, &aTimevals[0]);
560 RTTimeSpecGetTimeval(pModificationTime, &aTimevals[1]);
561 }
562 else
563 {
564 RTFSOBJINFO ObjInfo;
565 int rc = RTPathQueryInfo(pszPath, &ObjInfo, RTFSOBJATTRADD_UNIX);
566 if (RT_SUCCESS(rc))
567 {
568 RTTimeSpecGetTimeval(pAccessTime ? pAccessTime : &ObjInfo.AccessTime, &aTimevals[0]);
569 RTTimeSpecGetTimeval(pModificationTime ? pModificationTime : &ObjInfo.ModificationTime, &aTimevals[1]);
570 }
571 else
572 Log(("RTPathSetTimes('%s',%p,%p,,): RTPathQueryInfo failed with %Rrc\n",
573 pszPath, pAccessTime, pModificationTime, rc));
574 }
575 if (RT_SUCCESS(rc))
576 {
577 if (utimes(pszNativePath, aTimevals))
578 {
579 rc = RTErrConvertFromErrno(errno);
580 Log(("RTPathSetTimes('%s',%p,%p,,): failed with %Rrc and errno=%d\n",
581 pszPath, pAccessTime, pModificationTime, rc, errno));
582 }
583 }
584 }
585 rtPathFreeNative(pszNativePath);
586 }
587
588 LogFlow(("RTPathSetTimes(%p:{%s}, %p:{%RDtimespec}, %p:{%RDtimespec}, %p:{%RDtimespec}, %p:{%RDtimespec}): return %Rrc\n",
589 pszPath, pszPath, pAccessTime, pAccessTime, pModificationTime, pModificationTime,
590 pChangeTime, pChangeTime, pBirthTime, pBirthTime));
591 return rc;
592}
593
594
595/**
596 * Checks if two files are the one and same file.
597 */
598static bool rtPathSame(const char *pszNativeSrc, const char *pszNativeDst)
599{
600 struct stat SrcStat;
601 if (stat(pszNativeSrc, &SrcStat))
602 return false;
603 struct stat DstStat;
604 if (stat(pszNativeDst, &DstStat))
605 return false;
606 Assert(SrcStat.st_dev && DstStat.st_dev);
607 Assert(SrcStat.st_ino && DstStat.st_ino);
608 if ( SrcStat.st_dev == DstStat.st_dev
609 && SrcStat.st_ino == DstStat.st_ino
610 && (SrcStat.st_mode & S_IFMT) == (DstStat.st_mode & S_IFMT))
611 return true;
612 return false;
613}
614
615
616/**
617 * Worker for RTPathRename, RTDirRename, RTFileRename.
618 *
619 * @returns IPRT status code.
620 * @param pszSrc The source path.
621 * @param pszDst The destintation path.
622 * @param fRename The rename flags.
623 * @param fFileType The filetype. We use the RTFMODE filetypes here. If it's 0,
624 * anything goes. If it's RTFS_TYPE_DIRECTORY we'll check that the
625 * source is a directory. If Its RTFS_TYPE_FILE we'll check that it's
626 * not a directory (we are NOT checking whether it's a file).
627 */
628int rtPathPosixRename(const char *pszSrc, const char *pszDst, unsigned fRename, RTFMODE fFileType)
629{
630 /*
631 * Convert the paths.
632 */
633 char *pszNativeSrc;
634 int rc = rtPathToNative(&pszNativeSrc, pszSrc);
635 if (RT_SUCCESS(rc))
636 {
637 char *pszNativeDst;
638 rc = rtPathToNative(&pszNativeDst, pszDst);
639 if (RT_SUCCESS(rc))
640 {
641 /*
642 * Check that the source exists and that any types that's specified matches.
643 * We have to check this first to avoid getting errnous VERR_ALREADY_EXISTS
644 * errors from the next step.
645 *
646 * There are race conditions here (perhaps unlikly ones but still), but I'm
647 * afraid there is little with can do to fix that.
648 */
649 struct stat SrcStat;
650 if (stat(pszNativeSrc, &SrcStat))
651 rc = RTErrConvertFromErrno(errno);
652 else if (!fFileType)
653 rc = VINF_SUCCESS;
654 else if (RTFS_IS_DIRECTORY(fFileType))
655 rc = S_ISDIR(SrcStat.st_mode) ? VINF_SUCCESS : VERR_NOT_A_DIRECTORY;
656 else
657 rc = S_ISDIR(SrcStat.st_mode) ? VERR_IS_A_DIRECTORY : VINF_SUCCESS;
658 if (RT_SUCCESS(rc))
659 {
660 bool fSameFile = false;
661
662 /*
663 * Check if the target exists, rename is rather destructive.
664 * We'll have to make sure we don't overwrite the source!
665 * Another race condition btw.
666 */
667 struct stat DstStat;
668 if (stat(pszNativeDst, &DstStat))
669 rc = errno == ENOENT ? VINF_SUCCESS : RTErrConvertFromErrno(errno);
670 else
671 {
672 Assert(SrcStat.st_dev && DstStat.st_dev);
673 Assert(SrcStat.st_ino && DstStat.st_ino);
674 if ( SrcStat.st_dev == DstStat.st_dev
675 && SrcStat.st_ino == DstStat.st_ino
676 && (SrcStat.st_mode & S_IFMT) == (SrcStat.st_mode & S_IFMT))
677 {
678 /*
679 * It's likely that we're talking about the same file here.
680 * We should probably check paths or whatever, but for now this'll have to be enough.
681 */
682 fSameFile = true;
683 }
684 if (fSameFile)
685 rc = VINF_SUCCESS;
686 else if (S_ISDIR(DstStat.st_mode) || !(fRename & RTPATHRENAME_FLAGS_REPLACE))
687 rc = VERR_ALREADY_EXISTS;
688 else
689 rc = VINF_SUCCESS;
690
691 }
692 if (RT_SUCCESS(rc))
693 {
694 if (!rename(pszNativeSrc, pszNativeDst))
695 rc = VINF_SUCCESS;
696 else if ( (fRename & RTPATHRENAME_FLAGS_REPLACE)
697 && (errno == ENOTDIR || errno == EEXIST))
698 {
699 /*
700 * Check that the destination isn't a directory.
701 * Yet another race condition.
702 */
703 if (rtPathSame(pszNativeSrc, pszNativeDst))
704 {
705 rc = VINF_SUCCESS;
706 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): appears to be the same file... (errno=%d)\n",
707 pszSrc, pszDst, fRename, fFileType, errno));
708 }
709 else
710 {
711 if (stat(pszNativeDst, &DstStat))
712 rc = errno != ENOENT ? RTErrConvertFromErrno(errno) : VINF_SUCCESS;
713 else if (S_ISDIR(DstStat.st_mode))
714 rc = VERR_ALREADY_EXISTS;
715 else
716 rc = VINF_SUCCESS;
717 if (RT_SUCCESS(rc))
718 {
719 if (!unlink(pszNativeDst))
720 {
721 if (!rename(pszNativeSrc, pszNativeDst))
722 rc = VINF_SUCCESS;
723 else
724 {
725 rc = RTErrConvertFromErrno(errno);
726 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): rename failed rc=%Rrc errno=%d\n",
727 pszSrc, pszDst, fRename, fFileType, rc, errno));
728 }
729 }
730 else
731 {
732 rc = RTErrConvertFromErrno(errno);
733 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): failed to unlink dst rc=%Rrc errno=%d\n",
734 pszSrc, pszDst, fRename, fFileType, rc, errno));
735 }
736 }
737 else
738 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): dst !dir check failed rc=%Rrc\n",
739 pszSrc, pszDst, fRename, fFileType, rc));
740 }
741 }
742 else
743 {
744 rc = RTErrConvertFromErrno(errno);
745 if (errno == ENOTDIR)
746 rc = VERR_ALREADY_EXISTS; /* unless somebody is racing us, this is the right interpretation */
747 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): rename failed rc=%Rrc errno=%d\n",
748 pszSrc, pszDst, fRename, fFileType, rc, errno));
749 }
750 }
751 else
752 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): destination check failed rc=%Rrc errno=%d\n",
753 pszSrc, pszDst, fRename, fFileType, rc, errno));
754 }
755 else
756 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): source type check failed rc=%Rrc errno=%d\n",
757 pszSrc, pszDst, fRename, fFileType, rc, errno));
758
759 rtPathFreeNative(pszNativeDst);
760 }
761 rtPathFreeNative(pszNativeSrc);
762 }
763 return rc;
764}
765
766
767RTR3DECL(int) RTPathRename(const char *pszSrc, const char *pszDst, unsigned fRename)
768{
769 /*
770 * Validate input.
771 */
772 AssertMsgReturn(VALID_PTR(pszSrc), ("%p\n", pszSrc), VERR_INVALID_POINTER);
773 AssertMsgReturn(VALID_PTR(pszDst), ("%p\n", pszDst), VERR_INVALID_POINTER);
774 AssertMsgReturn(*pszSrc, ("%p\n", pszSrc), VERR_INVALID_PARAMETER);
775 AssertMsgReturn(*pszDst, ("%p\n", pszDst), VERR_INVALID_PARAMETER);
776 AssertMsgReturn(!(fRename & ~RTPATHRENAME_FLAGS_REPLACE), ("%#x\n", fRename), VERR_INVALID_PARAMETER);
777
778 /*
779 * Hand it to the worker.
780 */
781 int rc = rtPathPosixRename(pszSrc, pszDst, fRename, 0);
782
783 Log(("RTPathRename(%p:{%s}, %p:{%s}, %#x): returns %Rrc\n", pszSrc, pszSrc, pszDst, pszDst, fRename, rc));
784 return rc;
785}
786
787
788RTDECL(bool) RTPathExists(const char *pszPath)
789{
790 /*
791 * Validate input.
792 */
793 AssertPtrReturn(pszPath, false);
794 AssertReturn(*pszPath, false);
795
796 /*
797 * Convert the path and check if it exists using stat().
798 */
799 char *pszNativePath;
800 int rc = rtPathToNative(&pszNativePath, pszPath);
801 if (RT_SUCCESS(rc))
802 {
803 struct stat Stat;
804 if (!stat(pszNativePath, &Stat))
805 rc = VINF_SUCCESS;
806 else
807 rc = VERR_GENERAL_FAILURE;
808 RTStrFree(pszNativePath);
809 }
810 return RT_SUCCESS(rc);
811}
812
813
814RTDECL(int) RTPathGetCurrent(char *pszPath, size_t cchPath)
815{
816 int rc;
817 char szNativeCurDir[RTPATH_MAX];
818 if (getcwd(szNativeCurDir, sizeof(szNativeCurDir)) != NULL)
819 {
820 char *pszCurDir;
821 rc = rtPathFromNative(&pszCurDir, szNativeCurDir);
822 if (RT_SUCCESS(rc))
823 {
824 size_t cchCurDir = strlen(pszCurDir);
825 if (cchCurDir < cchPath)
826 {
827 memcpy(pszPath, pszCurDir, cchCurDir + 1);
828 RTStrFree(pszCurDir);
829 return VINF_SUCCESS;
830 }
831
832 rc = VERR_BUFFER_OVERFLOW;
833 RTStrFree(pszCurDir);
834 }
835 }
836 else
837 rc = RTErrConvertFromErrno(errno);
838 return rc;
839}
840
841
842RTDECL(int) RTPathSetCurrent(const char *pszPath)
843{
844 /*
845 * Validate input.
846 */
847 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
848 AssertReturn(*pszPath, VERR_INVALID_PARAMETER);
849
850 /*
851 * Change the directory.
852 */
853 char *pszNativePath;
854 int rc = rtPathToNative(&pszNativePath, pszPath);
855 if (RT_SUCCESS(rc))
856 {
857 if (chdir(pszNativePath))
858 rc = RTErrConvertFromErrno(errno);
859 RTStrFree(pszNativePath);
860 }
861 return rc;
862}
863
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