VirtualBox

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

Last change on this file since 33400 was 33342, checked in by vboxsync, 15 years ago

iprt: typo

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

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