VirtualBox

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

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

Rebranding: replacing more innotek strings.

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