VirtualBox

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

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

RTPathAbs/posix: man strcpy 'The strings may not overlap, ...' -> memmove. style

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