VirtualBox

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

Last change on this file since 4026 was 4026, checked in by vboxsync, 18 years ago

Modified RTPathUserHome to return the value from the passwd file for user root to solve problems with the use of sudo

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 31.9 KB
Line 
1/* $Id: path-posix.cpp 4026 2007-08-03 17:29:19Z vboxsync $ */
2/** @file
3 * innotek Portable Runtime - Path Manipulation, POSIX.
4 */
5
6/*
7 * Copyright (C) 2006-2007 innotek GmbH
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License as published by the Free Software Foundation,
13 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
14 * distribution. VirtualBox OSE is distributed in the hope that it will
15 * be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * If you received this file as part of a commercial VirtualBox
18 * distribution, then only the terms of your commercial VirtualBox
19 * license agreement apply instead of the previous paragraph.
20 */
21
22
23/*******************************************************************************
24* Header Files *
25*******************************************************************************/
26#define LOG_GROUP RTLOGGROUP_PATH
27#include <stdlib.h>
28#include <limits.h>
29#include <errno.h>
30#include <unistd.h>
31#include <sys/stat.h>
32#include <sys/time.h>
33#include <stdio.h>
34#include <sys/types.h>
35#include <pwd.h>
36#ifdef RT_OS_DARWIN
37# include <mach-o/dyld.h>
38#endif
39
40#include <iprt/path.h>
41#include <iprt/assert.h>
42#include <iprt/string.h>
43#include <iprt/err.h>
44#include <iprt/log.h>
45#include "internal/path.h"
46#include "internal/fs.h"
47
48#ifdef RT_OS_L4
49# include <l4/vboxserver/vboxserver.h>
50#endif
51
52
53
54
55RTDECL(int) RTPathReal(const char *pszPath, char *pszRealPath, unsigned cchRealPath)
56{
57 /*
58 * Convert input.
59 */
60 char *pszNativePath;
61 int rc = rtPathToNative(&pszNativePath, pszPath);
62 if (RT_SUCCESS(rc))
63 {
64 /*
65 * On POSIX platforms the API doesn't take a length parameter, which makes it
66 * a little bit more work.
67 */
68 char szTmpPath[PATH_MAX + 1];
69 const char *psz = realpath(pszNativePath, szTmpPath);
70 if (psz)
71 {
72 /*
73 * Convert result and copy it to the return buffer.
74 */
75 char *pszUtf8RealPath;
76 rc = rtPathFromNative(&pszUtf8RealPath, szTmpPath);
77 if (RT_SUCCESS(rc))
78 {
79 size_t cch = strlen(pszUtf8RealPath) + 1;
80 if (cch <= cchRealPath)
81 memcpy(pszRealPath, pszUtf8RealPath, cch);
82 else
83 rc = VERR_BUFFER_OVERFLOW;
84 RTStrFree(pszUtf8RealPath);
85 }
86 }
87 else
88 rc = RTErrConvertFromErrno(errno);
89 RTStrFree(pszNativePath);
90 }
91
92 LogFlow(("RTPathReal(%p:{%s}, %p:{%s}, %u): returns %Rrc\n", pszPath, pszPath,
93 pszRealPath, RT_SUCCESS(rc) ? pszRealPath : "<failed>", cchRealPath));
94 return rc;
95}
96
97
98/**
99 * Cleans up a path specifier a little bit.
100 * This includes removing duplicate slashes, uncessary single dots, and
101 * trailing slashes. Also, replaces all RTPATH_SLASH characters with '/'.
102 *
103 * @returns Number of bytes in the clean path.
104 * @param pszPath The path to cleanup.
105 * @remark Borrowed from innotek libc.
106 */
107static int fsCleanPath(char *pszPath)
108{
109 /*
110 * Change to '/' and remove duplicates.
111 */
112 char *pszSrc = pszPath;
113 char *pszTrg = pszPath;
114#ifdef HAVE_UNC
115 int fUnc = 0;
116 if ( RTPATH_IS_SLASH(pszPath[0])
117 && RTPATH_IS_SLASH(pszPath[1]))
118 { /* Skip first slash in a unc path. */
119 pszSrc++;
120 *pszTrg++ = '/';
121 fUnc = 1;
122 }
123#endif
124
125 for (;;)
126 {
127 char ch = *pszSrc++;
128 if (RTPATH_IS_SLASH(ch))
129 {
130 *pszTrg++ = '/';
131 for (;;)
132 {
133 do ch = *pszSrc++;
134 while (RTPATH_IS_SLASH(ch));
135
136 /* Remove '/./' and '/.'. */
137 if (ch != '.' || (*pszSrc && !RTPATH_IS_SLASH(*pszSrc)))
138 break;
139 }
140 }
141 *pszTrg = ch;
142 if (!ch)
143 break;
144 pszTrg++;
145 }
146
147 /*
148 * Remove trailing slash if the path may be pointing to a directory.
149 */
150 int cch = pszTrg - pszPath;
151 if ( cch > 1
152 && RTPATH_IS_SLASH(pszTrg[-1])
153#ifdef HAVE_DRIVE
154 && !RTPATH_IS_VOLSEP(pszTrg[-2])
155#endif
156 && !RTPATH_IS_SLASH(pszTrg[-2]))
157 pszPath[--cch] = '\0';
158
159 return cch;
160}
161
162
163RTDECL(int) RTPathAbs(const char *pszPath, char *pszAbsPath, unsigned cchAbsPath)
164{
165 /*
166 * Convert input.
167 */
168 char *pszNativePath;
169 int rc = rtPathToNative(&pszNativePath, pszPath);
170 if (RT_FAILURE(rc))
171 {
172 LogFlow(("RTPathAbs(%p:{%s}, %p, %d): returns %Rrc\n", pszPath,
173 pszPath, pszAbsPath, cchAbsPath, rc));
174 return rc;
175 }
176
177 /*
178 * On POSIX platforms the API doesn't take a length parameter, which makes it
179 * a little bit more work.
180 */
181 char szTmpPath[PATH_MAX + 1];
182 char *psz = realpath(pszNativePath, szTmpPath);
183 if (!psz)
184 {
185 if (errno == ENOENT || errno == ENOTDIR
186#ifdef RT_OS_OS2
187 /// @todo realpath() returns EIO for non-existent UNC paths like
188 // //server/share/subdir (i.e. when a subdir is specified within
189 // a share). We should either fix realpath() in libc or remove
190 // this todo.
191 || errno == EIO
192#endif
193 )
194 {
195 if (strlen(pszNativePath) <= PATH_MAX)
196 {
197 /*
198 * Iterate the path bit by bit an apply realpath to it.
199 */
200
201 char szTmpSrc[PATH_MAX + 1];
202 strcpy(szTmpSrc, pszNativePath);
203 fsCleanPath(szTmpSrc);
204
205 size_t cch = 0; // current resolved path length
206 char *pszCur = szTmpSrc;
207
208#ifdef HAVE_DRIVE
209 if (pszCur[0] && RTPATH_IS_VOLSEP(pszCur[1]) && pszCur[2] == '/')
210 {
211 psz = szTmpPath;
212 cch = 2;
213 pszCur += 3;
214 }
215#ifdef HAVE_UNC
216 else
217 if (pszCur[0] == '/' && pszCur[1] == '/')
218 {
219 pszCur += 2;
220 char *pszSlash = strchr(pszCur, '/');
221 size_t cchElement = pszSlash ? pszSlash - pszCur : strlen(pszCur);
222 if (cchElement && pszCur[cchElement])
223 {
224 psz = szTmpPath;
225 cch = cchElement + 2;
226 pszCur += cchElement + 1;
227 }
228 else
229 /* we've got just "//server" or "//" */
230 /// @todo (r=dmik) not 100% sure we should fail, but the
231 // above cases are just invalid (incomplete) paths,
232 // no matter that Win32 returns these paths as is.
233 rc = VERR_INVALID_NAME;
234 }
235#endif
236#else
237 if (*pszCur == '/')
238 {
239 psz = szTmpPath;
240 pszCur++;
241 }
242#endif
243 else
244 {
245 /* get the cwd */
246 psz = getcwd(szTmpPath, sizeof(szTmpPath));
247 AssertMsg(psz, ("Couldn't get cwd!\n"));
248 if (psz)
249 {
250#ifdef HAVE_DRIVE
251 if (*pszCur == '/')
252 {
253 cch = 2;
254 pszCur++;
255 }
256 else
257#endif
258 cch = strlen(psz);
259 }
260 else
261 rc = RTErrConvertFromErrno(errno);
262 }
263
264 if (psz)
265 {
266 bool fResolveSymlinks = true;
267 char szTmpPath2[PATH_MAX + 1];
268
269 /* make sure strrchr() will work correctly */
270 psz[cch] = '\0';
271
272 while (*pszCur)
273 {
274 char *pszSlash = strchr(pszCur, '/');
275 size_t cchElement = pszSlash ? pszSlash - pszCur : strlen(pszCur);
276 if (cch + cchElement + 1 > PATH_MAX)
277 {
278 rc = VERR_FILENAME_TOO_LONG;
279 break;
280 }
281
282 if (!strncmp(pszCur, "..", cchElement))
283 {
284 char *pszLastSlash = strrchr(psz, '/');
285#ifdef HAVE_UNC
286 if (pszLastSlash && pszLastSlash > psz &&
287 pszLastSlash[-1] != '/')
288#else
289 if (pszLastSlash)
290#endif
291 {
292 cch = pszLastSlash - psz;
293 psz[cch] = '\0';
294 }
295 /* else: We've reached the root and the parent of
296 * the root is the root. */
297 }
298 else
299 {
300 psz[cch++] = '/';
301 memcpy(psz + cch, pszCur, cchElement);
302 cch += cchElement;
303 psz[cch] = '\0';
304
305 if (fResolveSymlinks)
306 {
307 /* resolve possible symlinks */
308 char *psz2 = realpath(psz, psz == szTmpPath
309 ? szTmpPath2
310 : szTmpPath);
311 if (psz2)
312 {
313 psz = psz2;
314 cch = strlen(psz);
315 }
316 else
317 {
318 if (errno != ENOENT && errno != ENOTDIR
319#ifdef RT_OS_OS2
320 /// @todo see above
321 && errno != EIO
322#endif
323 )
324 {
325 rc = RTErrConvertFromErrno(errno);
326 break;
327 }
328
329 /* no more need to resolve symlinks */
330 fResolveSymlinks = false;
331 }
332 }
333 }
334
335 pszCur += cchElement;
336 /* skip the slash */
337 if (*pszCur)
338 ++pszCur;
339 }
340
341#ifdef HAVE_DRIVE
342 /* check if we're at the root */
343 if (cch == 2 && RTPATH_IS_VOLSEP(psz[1]))
344#else
345 /* if the length is zero here, then we're at the root */
346 if (!cch)
347#endif
348 {
349 psz[cch++] = '/';
350 psz[cch] = '\0';
351 }
352 }
353 }
354 else
355 rc = VERR_FILENAME_TOO_LONG;
356 }
357 else
358 rc = RTErrConvertFromErrno(errno);
359 }
360
361 RTStrFree(pszNativePath);
362
363 if (psz && RT_SUCCESS(rc))
364 {
365 /*
366 * Convert result and copy it to the return buffer.
367 */
368 char *pszUtf8AbsPath;
369 rc = rtPathFromNative(&pszUtf8AbsPath, psz);
370 if (RT_FAILURE(rc))
371 {
372 LogFlow(("RTPathAbs(%p:{%s}, %p, %d): returns %Rrc\n", pszPath,
373 pszPath, pszAbsPath, cchAbsPath, rc));
374 return rc;
375 }
376
377 /* replace '/' back with native RTPATH_SLASH */
378 psz = pszUtf8AbsPath;
379 for (; *psz; psz++)
380 if (*psz == '/')
381 *psz = RTPATH_SLASH;
382
383 unsigned cch = strlen(pszUtf8AbsPath) + 1;
384 if (cch <= cchAbsPath)
385 memcpy(pszAbsPath, pszUtf8AbsPath, cch);
386 else
387 rc = VERR_BUFFER_OVERFLOW;
388 RTStrFree(pszUtf8AbsPath);
389 }
390
391 LogFlow(("RTPathAbs(%p:{%s}, %p:{%s}, %d): returns %Rrc\n", pszPath,
392 pszPath, pszAbsPath, RT_SUCCESS(rc) ? pszAbsPath : "<failed>",
393 cchAbsPath, rc));
394 return rc;
395}
396
397
398RTDECL(int) RTPathProgram(char *pszPath, unsigned cchPath)
399{
400 /*
401 * First time only.
402 */
403 if (!g_szrtProgramPath[0])
404 {
405 /*
406 * Linux have no API for obtaining the executable path, but provides a symbolic link
407 * in the proc file system. Note that readlink is one of the weirdest Unix apis around.
408 *
409 * OS/2 have an api for getting the program file name.
410 */
411/** @todo use RTProcGetExecutableName() */
412#if defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD) || defined(RT_OS_SOLARIS)
413# ifdef RT_OS_LINUX
414 int cchLink = readlink("/proc/self/exe", &g_szrtProgramPath[0], sizeof(g_szrtProgramPath) - 1);
415# elif defined(RT_OS_SOLARIS)
416 pid_t curProcId = getpid();
417 char szFileBuf[PATH_MAX + 1];
418 sprintf(szFileBuf, "/proc/%ld/path/a.out", curProcId);
419 int cchLink = readlink(szFileBuf, &g_szrtProgramPath[0], sizeof(g_szrtProgramPath) - 1);
420# else /* RT_OS_FREEBSD: */
421 int cchLink = readlink("/proc/curproc/file", &g_szrtProgramPath[0], sizeof(g_szrtProgramPath) - 1);
422# endif
423 if (cchLink < 0 || cchLink == sizeof(g_szrtProgramPath) - 1)
424 {
425 int rc = RTErrConvertFromErrno(errno);
426 AssertMsgFailed(("couldn't read /proc/self/exe. errno=%d cchLink=%d\n", errno, cchLink));
427 LogFlow(("RTPathProgram(%p, %u): returns %Rrc\n", pszPath, cchPath, rc));
428 return rc;
429 }
430 g_szrtProgramPath[cchLink] = '\0';
431
432#elif defined(RT_OS_OS2) || defined(RT_OS_L4)
433 _execname(g_szrtProgramPath, sizeof(g_szrtProgramPath));
434
435#elif defined(RT_OS_DARWIN)
436 const char *pszImageName = _dyld_get_image_name(0);
437 AssertReturn(pszImageName, VERR_INTERNAL_ERROR);
438 size_t cchImageName = strlen(pszImageName);
439 if (cchImageName >= sizeof(g_szrtProgramPath))
440 AssertReturn(pszImageName, VERR_INTERNAL_ERROR);
441 memcpy(g_szrtProgramPath, pszImageName, cchImageName + 1);
442
443#else
444# error needs porting.
445#endif
446
447 /*
448 * Convert to UTF-8 and strip of the filename.
449 */
450 char *pszTmp = NULL;
451 int rc = rtPathFromNative(&pszTmp, &g_szrtProgramPath[0]);
452 if (RT_FAILURE(rc))
453 {
454 LogFlow(("RTPathProgram(%p, %u): returns %Rrc\n", pszPath, cchPath, rc));
455 return rc;
456 }
457 size_t cch = strlen(pszTmp);
458 if (cch >= sizeof(g_szrtProgramPath))
459 {
460 RTStrFree(pszTmp);
461 LogFlow(("RTPathProgram(%p, %u): returns %Rrc\n", pszPath, cchPath, VERR_BUFFER_OVERFLOW));
462 return VERR_BUFFER_OVERFLOW;
463 }
464 memcpy(g_szrtProgramPath, pszTmp, cch + 1);
465 RTPathStripFilename(g_szrtProgramPath);
466 RTStrFree(pszTmp);
467 }
468
469 /*
470 * Calc the length and check if there is space before copying.
471 */
472 unsigned cch = strlen(g_szrtProgramPath) + 1;
473 if (cch <= cchPath)
474 {
475 memcpy(pszPath, g_szrtProgramPath, cch + 1);
476 LogFlow(("RTPathProgram(%p:{%s}, %u): returns %Rrc\n", pszPath, pszPath, cchPath, VINF_SUCCESS));
477 return VINF_SUCCESS;
478 }
479
480 AssertMsgFailed(("Buffer too small (%d < %d)\n", cchPath, cch));
481 LogFlow(("RTPathProgram(%p, %u): returns %Rrc\n", pszPath, cchPath, VERR_BUFFER_OVERFLOW));
482 return VERR_BUFFER_OVERFLOW;
483}
484
485
486RTDECL(int) RTPathUserHome(char *pszPath, unsigned cchPath)
487{
488 int rc;
489#ifndef RT_OS_L4
490 /*
491 * We make an exception for the root user and use the system call
492 * getpwuid_r to determine their initial home path instead of
493 * reading it from the $HOME variable. This is because the $HOME
494 * variable does not get changed by sudo (and possibly su and others)
495 * which can cause root-owned files to appear in user's home folders.
496 */
497 uid_t uid = geteuid();
498 if (uid == 0)
499 {
500 /* The getpwuid_r function "allocates" any pointer memory it
501 needs from here. In theory one should use the sysconf
502 function to find the appropriate size, but sysconf is
503 unreliable by design. This should definitely be enough. */
504 char buffer[5120];
505 struct passwd sPasswd, *psPasswd;
506 rc = getpwuid_r(0, &sPasswd, buffer, sizeof(buffer), &psPasswd);
507 if (rc != 0)
508 return RTErrConvertFromErrno(rc);
509 /*
510 * Convert it to UTF-8 and copy it to the return buffer.
511 */
512 char *pszUtf8Path;
513 rc = rtPathFromNative(&pszUtf8Path, psPasswd->pw_dir);
514 if (RT_SUCCESS(rc))
515 {
516 size_t cchHome = strlen(pszUtf8Path);
517 if (cchHome < cchPath)
518 memcpy(pszPath, pszUtf8Path, cchHome + 1);
519 else
520 rc = VERR_BUFFER_OVERFLOW;
521 RTStrFree(pszUtf8Path);
522 }
523 LogFlow(("RTPathUserHome(%p:{%s}, %u): returns %Rrc\n", pszPath,
524 RT_SUCCESS(rc) ? pszPath : "<failed>", cchPath, rc));
525 return rc;
526 }
527#endif
528 /*
529 * Get HOME env. var it and validate it's existance.
530 */
531 struct stat s;
532 const char *pszHome = getenv("HOME");
533 if (pszHome)
534 {
535 if ( !stat(pszHome, &s)
536 && S_ISDIR(s.st_mode))
537 {
538 /*
539 * Convert it to UTF-8 and copy it to the return buffer.
540 */
541 char *pszUtf8Path;
542 rc = rtPathFromNative(&pszUtf8Path, pszHome);
543 if (RT_SUCCESS(rc))
544 {
545 size_t cchHome = strlen(pszUtf8Path);
546 if (cchHome < cchPath)
547 memcpy(pszPath, pszUtf8Path, cchHome + 1);
548 else
549 rc = VERR_BUFFER_OVERFLOW;
550 RTStrFree(pszUtf8Path);
551 }
552 }
553 else
554 rc = VERR_PATH_NOT_FOUND;
555
556 }
557 else
558 rc = VERR_PATH_NOT_FOUND;
559
560 LogFlow(("RTPathUserHome(%p:{%s}, %u): returns %Rrc\n", pszPath,
561 RT_SUCCESS(rc) ? pszPath : "<failed>", cchPath, rc));
562 return rc;
563}
564
565
566RTR3DECL(int) RTPathQueryInfo(const char *pszPath, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs)
567{
568 /*
569 * Validate input.
570 */
571 AssertMsgReturn(VALID_PTR(pszPath), ("%p\n", pszPath), VERR_INVALID_POINTER);
572 AssertReturn(*pszPath, VERR_INVALID_PARAMETER);
573 AssertMsgReturn(VALID_PTR(pObjInfo), ("%p\n", pszPath), VERR_INVALID_POINTER);
574 AssertMsgReturn( enmAdditionalAttribs >= RTFSOBJATTRADD_NOTHING
575 && enmAdditionalAttribs <= RTFSOBJATTRADD_LAST,
576 ("Invalid enmAdditionalAttribs=%p\n", enmAdditionalAttribs),
577 VERR_INVALID_PARAMETER);
578
579 /*
580 * Convert the filename.
581 */
582 char *pszNativePath;
583 int rc = rtPathToNative(&pszNativePath, pszPath);
584 if (RT_SUCCESS(rc))
585 {
586 struct stat Stat;
587 if (!stat(pszNativePath, &Stat))
588 {
589 rtFsConvertStatToObjInfo(pObjInfo, &Stat, pszPath, 0);
590 switch (enmAdditionalAttribs)
591 {
592 case RTFSOBJATTRADD_EASIZE:
593 /** @todo Use SGI extended attribute interface to query EA info. */
594 pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_EASIZE;
595 pObjInfo->Attr.u.EASize.cb = 0;
596 break;
597
598 case RTFSOBJATTRADD_NOTHING:
599 case RTFSOBJATTRADD_UNIX:
600 Assert(pObjInfo->Attr.enmAdditional == RTFSOBJATTRADD_UNIX);
601 break;
602
603 default:
604 AssertMsgFailed(("Impossible!\n"));
605 return VERR_INTERNAL_ERROR;
606 }
607 }
608 else
609 rc = RTErrConvertFromErrno(errno);
610 }
611
612 LogFlow(("RTPathQueryInfo(%p:{%s}, pObjInfo=%p, %d): returns %Rrc\n",
613 pszPath, pszPath, pObjInfo, enmAdditionalAttribs, rc));
614 return rc;
615}
616
617
618RTR3DECL(int) RTPathSetTimes(const char *pszPath, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
619 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
620{
621 /*
622 * Validate input.
623 */
624 AssertMsgReturn(VALID_PTR(pszPath), ("%p\n", pszPath), VERR_INVALID_POINTER);
625 AssertMsgReturn(*pszPath, ("%p\n", pszPath), VERR_INVALID_PARAMETER);
626 AssertMsgReturn(!pAccessTime || VALID_PTR(pAccessTime), ("%p\n", pAccessTime), VERR_INVALID_POINTER);
627 AssertMsgReturn(!pModificationTime || VALID_PTR(pModificationTime), ("%p\n", pModificationTime), VERR_INVALID_POINTER);
628 AssertMsgReturn(!pChangeTime || VALID_PTR(pChangeTime), ("%p\n", pChangeTime), VERR_INVALID_POINTER);
629 AssertMsgReturn(!pBirthTime || VALID_PTR(pBirthTime), ("%p\n", pBirthTime), VERR_INVALID_POINTER);
630
631 /*
632 * Convert the paths.
633 */
634 char *pszNativePath;
635 int rc = rtPathToNative(&pszNativePath, pszPath);
636 if (RT_SUCCESS(rc))
637 {
638 /*
639 * If it's a no-op, we'll only verify the existance of the file.
640 */
641 if (!pAccessTime && !pModificationTime)
642 {
643 struct stat Stat;
644 if (!stat(pszNativePath, &Stat))
645 rc = VINF_SUCCESS;
646 else
647 {
648 rc = RTErrConvertFromErrno(errno);
649 Log(("RTPathSetTimes('%s',,,,): failed with %Rrc and errno=%d\n", pszPath, rc, errno));
650 }
651 }
652 else
653 {
654 /*
655 * Convert the input to timeval, getting the missing one if necessary,
656 * and call the API which does the change.
657 */
658 struct timeval aTimevals[2];
659 if (pAccessTime && pModificationTime)
660 {
661 RTTimeSpecGetTimeval(pAccessTime, &aTimevals[0]);
662 RTTimeSpecGetTimeval(pModificationTime, &aTimevals[1]);
663 }
664 else
665 {
666 RTFSOBJINFO ObjInfo;
667 int rc = RTPathQueryInfo(pszPath, &ObjInfo, RTFSOBJATTRADD_UNIX);
668 if (RT_SUCCESS(rc))
669 {
670 RTTimeSpecGetTimeval(pAccessTime ? pAccessTime : &ObjInfo.AccessTime, &aTimevals[0]);
671 RTTimeSpecGetTimeval(pModificationTime ? pModificationTime : &ObjInfo.ModificationTime, &aTimevals[1]);
672 }
673 else
674 Log(("RTPathSetTimes('%s',%p,%p,,): RTPathQueryInfo failed with %Rrc\n",
675 pszPath, pAccessTime, pModificationTime, rc));
676 }
677 if (RT_SUCCESS(rc))
678 {
679 if (utimes(pszNativePath, aTimevals))
680 {
681 rc = RTErrConvertFromErrno(errno);
682 Log(("RTPathSetTimes('%s',%p,%p,,): failed with %Rrc and errno=%d\n",
683 pszPath, pAccessTime, pModificationTime, rc, errno));
684 }
685 }
686 }
687 }
688
689 LogFlow(("RTPathSetTimes(%p:{%s}, %p:{%RDtimespec}, %p:{%RDtimespec}, %p:{%RDtimespec}, %p:{%RDtimespec}): return %Rrc\n",
690 pszPath, pszPath, pAccessTime, pAccessTime, pModificationTime, pModificationTime,
691 pChangeTime, pChangeTime, pBirthTime, pBirthTime));
692 return rc;
693}
694
695
696/**
697 * Checks if two files are the one and same file.
698 */
699static bool rtPathSame(const char *pszNativeSrc, const char *pszNativeDst)
700{
701 struct stat SrcStat;
702 if (stat(pszNativeSrc, &SrcStat))
703 return false;
704 struct stat DstStat;
705 if (stat(pszNativeDst, &DstStat))
706 return false;
707 Assert(SrcStat.st_dev && DstStat.st_dev);
708 Assert(SrcStat.st_ino && DstStat.st_ino);
709 if ( SrcStat.st_dev == DstStat.st_dev
710 && SrcStat.st_ino == DstStat.st_ino
711 && (SrcStat.st_mode & S_IFMT) == (DstStat.st_mode & S_IFMT))
712 return true;
713 return false;
714}
715
716
717/**
718 * Worker for RTPathRename, RTDirRename, RTFileRename.
719 *
720 * @returns IPRT status code.
721 * @param pszSrc The source path.
722 * @param pszDst The destintation path.
723 * @param fRename The rename flags.
724 * @param fFileType The filetype. We use the RTFMODE filetypes here. If it's 0,
725 * anything goes. If it's RTFS_TYPE_DIRECTORY we'll check that the
726 * source is a directory. If Its RTFS_TYPE_FILE we'll check that it's
727 * not a directory (we are NOT checking whether it's a file).
728 */
729int rtPathPosixRename(const char *pszSrc, const char *pszDst, unsigned fRename, RTFMODE fFileType)
730{
731 /*
732 * Convert the paths.
733 */
734 char *pszNativeSrc;
735 int rc = rtPathToNative(&pszNativeSrc, pszSrc);
736 if (RT_SUCCESS(rc))
737 {
738 char *pszNativeDst;
739 rc = rtPathToNative(&pszNativeDst, pszDst);
740 if (RT_SUCCESS(rc))
741 {
742 /*
743 * Check that the source exists and that any types that's specified matches.
744 * We have to check this first to avoid getting errnous VERR_ALREADY_EXISTS
745 * errors from the next step.
746 *
747 * There are race conditions here (perhaps unlikly ones but still), but I'm
748 * afraid there is little with can do to fix that.
749 */
750 struct stat SrcStat;
751 if (stat(pszNativeSrc, &SrcStat))
752 rc = RTErrConvertFromErrno(errno);
753 else if (!fFileType)
754 rc = VINF_SUCCESS;
755 else if (RTFS_IS_DIRECTORY(fFileType))
756 rc = S_ISDIR(SrcStat.st_mode) ? VINF_SUCCESS : VERR_NOT_A_DIRECTORY;
757 else
758 rc = S_ISDIR(SrcStat.st_mode) ? VERR_IS_A_DIRECTORY : VINF_SUCCESS;
759 if (RT_SUCCESS(rc))
760 {
761 bool fSameFile = false;
762
763 /*
764 * Check if the target exists, rename is rather destructive.
765 * We'll have to make sure we don't overwrite the source!
766 * Another race condition btw.
767 */
768 struct stat DstStat;
769 if (stat(pszNativeDst, &DstStat))
770 rc = errno == ENOENT ? VINF_SUCCESS : RTErrConvertFromErrno(errno);
771 else
772 {
773 Assert(SrcStat.st_dev && DstStat.st_dev);
774 Assert(SrcStat.st_ino && DstStat.st_ino);
775 if ( SrcStat.st_dev == DstStat.st_dev
776 && SrcStat.st_ino == DstStat.st_ino
777 && (SrcStat.st_mode & S_IFMT) == (SrcStat.st_mode & S_IFMT))
778 {
779 /*
780 * It's likely that we're talking about the same file here.
781 * We should probably check paths or whatever, but for now this'll have to be enough.
782 */
783 fSameFile = true;
784 }
785 if (fSameFile)
786 rc = VINF_SUCCESS;
787 else if (S_ISDIR(DstStat.st_mode) || !(fRename & RTPATHRENAME_FLAGS_REPLACE))
788 rc = VERR_ALREADY_EXISTS;
789 else
790 rc = VINF_SUCCESS;
791
792 }
793 if (RT_SUCCESS(rc))
794 {
795 if (!rename(pszNativeSrc, pszNativeDst))
796 rc = VINF_SUCCESS;
797 else if ( (fRename & RTPATHRENAME_FLAGS_REPLACE)
798 && (errno == ENOTDIR || errno == EEXIST))
799 {
800 /*
801 * Check that the destination isn't a directory.
802 * Yet another race condition.
803 */
804 if (rtPathSame(pszNativeSrc, pszNativeDst))
805 {
806 rc = VINF_SUCCESS;
807 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): appears to be the same file... (errno=%d)\n",
808 pszSrc, pszDst, fRename, fFileType, errno));
809 }
810 else
811 {
812 if (stat(pszNativeDst, &DstStat))
813 rc = errno != ENOENT ? RTErrConvertFromErrno(errno) : VINF_SUCCESS;
814 else if (S_ISDIR(DstStat.st_mode))
815 rc = VERR_ALREADY_EXISTS;
816 else
817 rc = VINF_SUCCESS;
818 if (RT_SUCCESS(rc))
819 {
820 if (!unlink(pszNativeDst))
821 {
822 if (!rename(pszNativeSrc, pszNativeDst))
823 rc = VINF_SUCCESS;
824 else
825 {
826 rc = RTErrConvertFromErrno(errno);
827 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): rename failed rc=%Rrc errno=%d\n",
828 pszSrc, pszDst, fRename, fFileType, rc, errno));
829 }
830 }
831 else
832 {
833 rc = RTErrConvertFromErrno(errno);
834 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): failed to unlink dst rc=%Rrc errno=%d\n",
835 pszSrc, pszDst, fRename, fFileType, rc, errno));
836 }
837 }
838 else
839 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): dst !dir check failed rc=%Rrc\n",
840 pszSrc, pszDst, fRename, fFileType, rc));
841 }
842 }
843 else
844 {
845 rc = RTErrConvertFromErrno(errno);
846 if (errno == ENOTDIR)
847 rc = VERR_ALREADY_EXISTS; /* unless somebody is racing us, this is the right interpretation */
848 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): rename failed rc=%Rrc errno=%d\n",
849 pszSrc, pszDst, fRename, fFileType, rc, errno));
850 }
851 }
852 else
853 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): destination check failed rc=%Rrc errno=%d\n",
854 pszSrc, pszDst, fRename, fFileType, rc, errno));
855 }
856 else
857 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): source type check failed rc=%Rrc errno=%d\n",
858 pszSrc, pszDst, fRename, fFileType, rc, errno));
859
860 rtPathFreeNative(pszNativeDst);
861 }
862 rtPathFreeNative(pszNativeSrc);
863 }
864 return rc;
865}
866
867
868RTR3DECL(int) RTPathRename(const char *pszSrc, const char *pszDst, unsigned fRename)
869{
870 /*
871 * Validate input.
872 */
873 AssertMsgReturn(VALID_PTR(pszSrc), ("%p\n", pszSrc), VERR_INVALID_POINTER);
874 AssertMsgReturn(VALID_PTR(pszDst), ("%p\n", pszDst), VERR_INVALID_POINTER);
875 AssertMsgReturn(*pszSrc, ("%p\n", pszSrc), VERR_INVALID_PARAMETER);
876 AssertMsgReturn(*pszDst, ("%p\n", pszDst), VERR_INVALID_PARAMETER);
877 AssertMsgReturn(!(fRename & ~RTPATHRENAME_FLAGS_REPLACE), ("%#x\n", fRename), VERR_INVALID_PARAMETER);
878
879 /*
880 * Hand it to the worker.
881 */
882 int rc = rtPathPosixRename(pszSrc, pszDst, fRename, 0);
883
884 Log(("RTPathRename(%p:{%s}, %p:{%s}, %#x): returns %Rrc\n", pszSrc, pszSrc, pszDst, pszDst, fRename, rc));
885 return rc;
886}
887
888
889RTDECL(bool) RTPathExists(const char *pszPath)
890{
891 /*
892 * Validate input.
893 */
894 AssertPtrReturn(pszPath, false);
895 AssertReturn(*pszPath, false);
896
897 /*
898 * Convert the path and check if it exists using stat().
899 */
900 char *pszNativePath;
901 int rc = rtPathToNative(&pszNativePath, pszPath);
902 if (RT_SUCCESS(rc))
903 {
904 struct stat Stat;
905 if (!stat(pszNativePath, &Stat))
906 rc = VINF_SUCCESS;
907 else
908 rc = VERR_GENERAL_FAILURE;
909 RTStrFree(pszNativePath);
910 }
911 return RT_SUCCESS(rc);
912}
913
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette