VirtualBox

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

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

rebranding, eol

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