VirtualBox

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

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

r=bird: Don't create more RuntimeLnx32* legacy. No VBox in IPRT, so check for RT_MINI instead of VBOX_SHARED_RUNTIME. Call it mini iprt instead of abigious 'shared'. Added todo for correcting template usage. Crossing fingers this works - I'm on windows atm.

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