VirtualBox

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

Last change on this file since 4512 was 4071, checked in by vboxsync, 17 years ago

Biggest check-in ever. New source code headers for all (C) innotek files.

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