VirtualBox

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

Last change on this file since 31453 was 31406, checked in by vboxsync, 14 years ago

RTPathSetMode/posix: No need to use realpath.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 30.8 KB
Line 
1/* $Id: path-posix.cpp 31406 2010-08-05 13:07:40Z 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
481
482RTR3DECL(int) RTPathSetTimes(const char *pszPath, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
483 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
484{
485 return RTPathSetTimesEx(pszPath, pAccessTime, pModificationTime, pChangeTime, pBirthTime, RTPATH_F_ON_LINK);
486}
487
488
489RTR3DECL(int) RTPathSetTimesEx(const char *pszPath, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
490 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime, uint32_t fFlags)
491{
492 /*
493 * Validate input.
494 */
495 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
496 AssertReturn(*pszPath, VERR_INVALID_PARAMETER);
497 AssertPtrNullReturn(pAccessTime, VERR_INVALID_POINTER);
498 AssertPtrNullReturn(pModificationTime, VERR_INVALID_POINTER);
499 AssertPtrNullReturn(pChangeTime, VERR_INVALID_POINTER);
500 AssertPtrNullReturn(pBirthTime, VERR_INVALID_POINTER);
501 AssertMsgReturn(RTPATH_F_IS_VALID(fFlags, 0), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
502
503 /*
504 * Convert the paths.
505 */
506 char const *pszNativePath;
507 int rc = rtPathToNative(&pszNativePath, pszPath, NULL);
508 if (RT_SUCCESS(rc))
509 {
510 RTFSOBJINFO ObjInfo;
511
512 /*
513 * If it's a no-op, we'll only verify the existance of the file.
514 */
515 if (!pAccessTime && !pModificationTime)
516 rc = RTPathQueryInfoEx(pszPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, fFlags);
517 else
518 {
519 /*
520 * Convert the input to timeval, getting the missing one if necessary,
521 * and call the API which does the change.
522 */
523 struct timeval aTimevals[2];
524 if (pAccessTime && pModificationTime)
525 {
526 RTTimeSpecGetTimeval(pAccessTime, &aTimevals[0]);
527 RTTimeSpecGetTimeval(pModificationTime, &aTimevals[1]);
528 }
529 else
530 {
531 rc = RTPathQueryInfoEx(pszPath, &ObjInfo, RTFSOBJATTRADD_UNIX, fFlags);
532 if (RT_SUCCESS(rc))
533 {
534 RTTimeSpecGetTimeval(pAccessTime ? pAccessTime : &ObjInfo.AccessTime, &aTimevals[0]);
535 RTTimeSpecGetTimeval(pModificationTime ? pModificationTime : &ObjInfo.ModificationTime, &aTimevals[1]);
536 }
537 else
538 Log(("RTPathSetTimes('%s',%p,%p,,): RTPathQueryInfo failed with %Rrc\n",
539 pszPath, pAccessTime, pModificationTime, rc));
540 }
541 if (RT_SUCCESS(rc))
542 {
543 if (fFlags & RTPATH_F_FOLLOW_LINK)
544 {
545 if (utimes(pszNativePath, aTimevals))
546 rc = RTErrConvertFromErrno(errno);
547 }
548#if (defined(RT_OS_DARWIN) && MAC_OS_X_VERSION_MIN_REQUIRED >= 1050) \
549 || defined(RT_OS_FREEBSD) \
550 || defined(RT_OS_LINUX) \
551 || defined(RT_OS_OS2) /** @todo who really has lutimes? */
552 else
553 {
554 if (lutimes(pszNativePath, aTimevals))
555 rc = RTErrConvertFromErrno(errno);
556 }
557#else
558 else
559 {
560 if (pAccessTime && pModificationTime)
561 rc = RTPathQueryInfoEx(pszPath, &ObjInfo, RTFSOBJATTRADD_UNIX, fFlags);
562 if (RT_SUCCESS(rc) && RTFS_IS_SYMLINK(ObjInfo.Attr.fMode))
563 rc = VERR_NS_SYMLINK_SET_TIME;
564 else if (RT_SUCCESS(rc))
565 {
566 if (utimes(pszNativePath, aTimevals))
567 rc = RTErrConvertFromErrno(errno);
568 }
569 }
570#endif
571 if (RT_FAILURE(rc))
572 Log(("RTPathSetTimes('%s',%p,%p,,): failed with %Rrc and errno=%d\n",
573 pszPath, pAccessTime, pModificationTime, rc, errno));
574 }
575 }
576 rtPathFreeNative(pszNativePath, pszPath);
577 }
578
579 LogFlow(("RTPathSetTimes(%p:{%s}, %p:{%RDtimespec}, %p:{%RDtimespec}, %p:{%RDtimespec}, %p:{%RDtimespec}): return %Rrc\n",
580 pszPath, pszPath, pAccessTime, pAccessTime, pModificationTime, pModificationTime,
581 pChangeTime, pChangeTime, pBirthTime, pBirthTime, rc));
582 return rc;
583}
584
585
586RTR3DECL(int) RTPathSetOwner(const char *pszPath, uint32_t uid, uint32_t gid)
587{
588 return RTPathSetOwnerEx(pszPath, uid, gid, RTPATH_F_ON_LINK);
589}
590
591
592RTR3DECL(int) RTPathSetOwnerEx(const char *pszPath, uint32_t uid, uint32_t gid, uint32_t fFlags)
593{
594 /*
595 * Validate input.
596 */
597 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
598 AssertReturn(*pszPath, VERR_INVALID_PARAMETER);
599 AssertMsgReturn(RTPATH_F_IS_VALID(fFlags, 0), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
600 uid_t uidNative = uid != UINT32_MAX ? (uid_t)uid : (uid_t)-1;
601 AssertReturn(uid == uidNative, VERR_INVALID_PARAMETER);
602 gid_t gidNative = gid != UINT32_MAX ? (gid_t)gid : (uid_t)-1;
603 AssertReturn(gid == gidNative, VERR_INVALID_PARAMETER);
604
605 /*
606 * Convert the path.
607 */
608 char const *pszNativePath;
609 int rc = rtPathToNative(&pszNativePath, pszPath, NULL);
610 if (RT_SUCCESS(rc))
611 {
612 if (fFlags & RTPATH_F_FOLLOW_LINK)
613 {
614 if (chown(pszNativePath, uidNative, gidNative))
615 rc = RTErrConvertFromErrno(errno);
616 }
617#if 1
618 else
619 {
620 if (lchown(pszNativePath, uidNative, gidNative))
621 rc = RTErrConvertFromErrno(errno);
622 }
623#else
624 else
625 {
626 RTFSOBJINFO ObjInfo;
627 rc = RTPathQueryInfoEx(pszPath, &ObjInfo, RTFSOBJATTRADD_UNIX, fFlags);
628 if (RT_SUCCESS(rc) && RTFS_IS_SYMLINK(ObjInfo.Attr.fMode))
629 rc = VERR_NS_SYMLINK_CHANGE_OWNER;
630 else if (RT_SUCCESS(rc))
631 {
632 if (lchown(pszNativePath, uidNative, gidNative))
633 rc = RTErrConvertFromErrno(errno);
634 }
635 }
636#endif
637 if (RT_FAILURE(rc))
638 Log(("RTPathSetOwnerEx('%s',%d,%d): failed with %Rrc and errno=%d\n",
639 pszPath, uid, gid, rc, errno));
640
641 rtPathFreeNative(pszNativePath, pszPath);
642 }
643
644 LogFlow(("RTPathSetOwnerEx(%p:{%s}, uid, gid): return %Rrc\n",
645 pszPath, pszPath, uid, gid, rc));
646 return rc;
647}
648
649
650RTR3DECL(int) RTPathSetMode(const char *pszPath, RTFMODE fMode)
651{
652 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
653 AssertReturn(*pszPath, VERR_INVALID_PARAMETER);
654
655 int rc;
656 fMode = rtFsModeNormalize(fMode, pszPath, 0);
657 if (rtFsModeIsValidPermissions(fMode))
658 {
659 char const *pszNativePath;
660 rc = rtPathToNative(&pszNativePath, pszPath, NULL);
661 if (RT_SUCCESS(rc))
662 {
663 if (chmod(pszNativePath, fMode & RTFS_UNIX_MASK) != 0)
664 rc = RTErrConvertFromErrno(errno);
665 rtPathFreeNative(pszNativePath, pszPath);
666 }
667 }
668 else
669 {
670 AssertMsgFailed(("Invalid file mode! %RTfmode\n", fMode));
671 rc = VERR_INVALID_FMODE;
672 }
673 return rc;
674}
675
676
677/**
678 * Checks if two files are the one and same file.
679 */
680static bool rtPathSame(const char *pszNativeSrc, const char *pszNativeDst)
681{
682 struct stat SrcStat;
683 if (stat(pszNativeSrc, &SrcStat))
684 return false;
685 struct stat DstStat;
686 if (stat(pszNativeDst, &DstStat))
687 return false;
688 Assert(SrcStat.st_dev && DstStat.st_dev);
689 Assert(SrcStat.st_ino && DstStat.st_ino);
690 if ( SrcStat.st_dev == DstStat.st_dev
691 && SrcStat.st_ino == DstStat.st_ino
692 && (SrcStat.st_mode & S_IFMT) == (DstStat.st_mode & S_IFMT))
693 return true;
694 return false;
695}
696
697
698/**
699 * Worker for RTPathRename, RTDirRename, RTFileRename.
700 *
701 * @returns IPRT status code.
702 * @param pszSrc The source path.
703 * @param pszDst The destintation path.
704 * @param fRename The rename flags.
705 * @param fFileType The filetype. We use the RTFMODE filetypes here. If it's 0,
706 * anything goes. If it's RTFS_TYPE_DIRECTORY we'll check that the
707 * source is a directory. If Its RTFS_TYPE_FILE we'll check that it's
708 * not a directory (we are NOT checking whether it's a file).
709 */
710DECLHIDDEN(int) rtPathPosixRename(const char *pszSrc, const char *pszDst, unsigned fRename, RTFMODE fFileType)
711{
712 /*
713 * Convert the paths.
714 */
715 char const *pszNativeSrc;
716 int rc = rtPathToNative(&pszNativeSrc, pszSrc, NULL);
717 if (RT_SUCCESS(rc))
718 {
719 char const *pszNativeDst;
720 rc = rtPathToNative(&pszNativeDst, pszDst, NULL);
721 if (RT_SUCCESS(rc))
722 {
723 /*
724 * Check that the source exists and that any types that's specified matches.
725 * We have to check this first to avoid getting errnous VERR_ALREADY_EXISTS
726 * errors from the next step.
727 *
728 * There are race conditions here (perhaps unlikly ones but still), but I'm
729 * afraid there is little with can do to fix that.
730 */
731 struct stat SrcStat;
732 if (stat(pszNativeSrc, &SrcStat))
733 rc = RTErrConvertFromErrno(errno);
734 else if (!fFileType)
735 rc = VINF_SUCCESS;
736 else if (RTFS_IS_DIRECTORY(fFileType))
737 rc = S_ISDIR(SrcStat.st_mode) ? VINF_SUCCESS : VERR_NOT_A_DIRECTORY;
738 else
739 rc = S_ISDIR(SrcStat.st_mode) ? VERR_IS_A_DIRECTORY : VINF_SUCCESS;
740 if (RT_SUCCESS(rc))
741 {
742 bool fSameFile = false;
743
744 /*
745 * Check if the target exists, rename is rather destructive.
746 * We'll have to make sure we don't overwrite the source!
747 * Another race condition btw.
748 */
749 struct stat DstStat;
750 if (stat(pszNativeDst, &DstStat))
751 rc = errno == ENOENT ? VINF_SUCCESS : RTErrConvertFromErrno(errno);
752 else
753 {
754 Assert(SrcStat.st_dev && DstStat.st_dev);
755 Assert(SrcStat.st_ino && DstStat.st_ino);
756 if ( SrcStat.st_dev == DstStat.st_dev
757 && SrcStat.st_ino == DstStat.st_ino
758 && (SrcStat.st_mode & S_IFMT) == (DstStat.st_mode & S_IFMT))
759 {
760 /*
761 * It's likely that we're talking about the same file here.
762 * We should probably check paths or whatever, but for now this'll have to be enough.
763 */
764 fSameFile = true;
765 }
766 if (fSameFile)
767 rc = VINF_SUCCESS;
768 else if (S_ISDIR(DstStat.st_mode) || !(fRename & RTPATHRENAME_FLAGS_REPLACE))
769 rc = VERR_ALREADY_EXISTS;
770 else
771 rc = VINF_SUCCESS;
772
773 }
774 if (RT_SUCCESS(rc))
775 {
776 if (!rename(pszNativeSrc, pszNativeDst))
777 rc = VINF_SUCCESS;
778 else if ( (fRename & RTPATHRENAME_FLAGS_REPLACE)
779 && (errno == ENOTDIR || errno == EEXIST))
780 {
781 /*
782 * Check that the destination isn't a directory.
783 * Yet another race condition.
784 */
785 if (rtPathSame(pszNativeSrc, pszNativeDst))
786 {
787 rc = VINF_SUCCESS;
788 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): appears to be the same file... (errno=%d)\n",
789 pszSrc, pszDst, fRename, fFileType, errno));
790 }
791 else
792 {
793 if (stat(pszNativeDst, &DstStat))
794 rc = errno != ENOENT ? RTErrConvertFromErrno(errno) : VINF_SUCCESS;
795 else if (S_ISDIR(DstStat.st_mode))
796 rc = VERR_ALREADY_EXISTS;
797 else
798 rc = VINF_SUCCESS;
799 if (RT_SUCCESS(rc))
800 {
801 if (!unlink(pszNativeDst))
802 {
803 if (!rename(pszNativeSrc, pszNativeDst))
804 rc = VINF_SUCCESS;
805 else
806 {
807 rc = RTErrConvertFromErrno(errno);
808 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): rename failed rc=%Rrc errno=%d\n",
809 pszSrc, pszDst, fRename, fFileType, rc, errno));
810 }
811 }
812 else
813 {
814 rc = RTErrConvertFromErrno(errno);
815 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): failed to unlink dst rc=%Rrc errno=%d\n",
816 pszSrc, pszDst, fRename, fFileType, rc, errno));
817 }
818 }
819 else
820 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): dst !dir check failed rc=%Rrc\n",
821 pszSrc, pszDst, fRename, fFileType, rc));
822 }
823 }
824 else
825 {
826 rc = RTErrConvertFromErrno(errno);
827 if (errno == ENOTDIR)
828 rc = VERR_ALREADY_EXISTS; /* unless somebody is racing us, this is the right interpretation */
829 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): rename failed rc=%Rrc errno=%d\n",
830 pszSrc, pszDst, fRename, fFileType, rc, errno));
831 }
832 }
833 else
834 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): destination check failed rc=%Rrc errno=%d\n",
835 pszSrc, pszDst, fRename, fFileType, rc, errno));
836 }
837 else
838 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): source type check failed rc=%Rrc errno=%d\n",
839 pszSrc, pszDst, fRename, fFileType, rc, errno));
840
841 rtPathFreeNative(pszNativeDst, pszDst);
842 }
843 rtPathFreeNative(pszNativeSrc, pszSrc);
844 }
845 return rc;
846}
847
848
849RTR3DECL(int) RTPathRename(const char *pszSrc, const char *pszDst, unsigned fRename)
850{
851 /*
852 * Validate input.
853 */
854 AssertMsgReturn(VALID_PTR(pszSrc), ("%p\n", pszSrc), VERR_INVALID_POINTER);
855 AssertMsgReturn(VALID_PTR(pszDst), ("%p\n", pszDst), VERR_INVALID_POINTER);
856 AssertMsgReturn(*pszSrc, ("%p\n", pszSrc), VERR_INVALID_PARAMETER);
857 AssertMsgReturn(*pszDst, ("%p\n", pszDst), VERR_INVALID_PARAMETER);
858 AssertMsgReturn(!(fRename & ~RTPATHRENAME_FLAGS_REPLACE), ("%#x\n", fRename), VERR_INVALID_PARAMETER);
859
860 /*
861 * Hand it to the worker.
862 */
863 int rc = rtPathPosixRename(pszSrc, pszDst, fRename, 0);
864
865 Log(("RTPathRename(%p:{%s}, %p:{%s}, %#x): returns %Rrc\n", pszSrc, pszSrc, pszDst, pszDst, fRename, rc));
866 return rc;
867}
868
869
870RTDECL(bool) RTPathExists(const char *pszPath)
871{
872 return RTPathExistsEx(pszPath, RTPATH_F_FOLLOW_LINK);
873}
874
875
876RTDECL(bool) RTPathExistsEx(const char *pszPath, uint32_t fFlags)
877{
878 /*
879 * Validate input.
880 */
881 AssertPtrReturn(pszPath, false);
882 AssertReturn(*pszPath, false);
883 Assert(RTPATH_F_IS_VALID(fFlags, 0));
884
885 /*
886 * Convert the path and check if it exists using stat().
887 */
888 char const *pszNativePath;
889 int rc = rtPathToNative(&pszNativePath, pszPath, NULL);
890 if (RT_SUCCESS(rc))
891 {
892 struct stat Stat;
893 if (fFlags & RTPATH_F_FOLLOW_LINK)
894 rc = stat(pszNativePath, &Stat);
895 else
896 rc = lstat(pszNativePath, &Stat);
897 if (!rc)
898 rc = VINF_SUCCESS;
899 else
900 rc = VERR_GENERAL_FAILURE;
901 rtPathFreeNative(pszNativePath, pszPath);
902 }
903 return RT_SUCCESS(rc);
904}
905
906
907RTDECL(int) RTPathGetCurrent(char *pszPath, size_t cchPath)
908{
909 int rc;
910 char szNativeCurDir[RTPATH_MAX];
911 if (getcwd(szNativeCurDir, sizeof(szNativeCurDir)) != NULL)
912 rc = rtPathFromNativeCopy(pszPath, cchPath, szNativeCurDir, NULL);
913 else
914 rc = RTErrConvertFromErrno(errno);
915 return rc;
916}
917
918
919RTDECL(int) RTPathSetCurrent(const char *pszPath)
920{
921 /*
922 * Validate input.
923 */
924 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
925 AssertReturn(*pszPath, VERR_INVALID_PARAMETER);
926
927 /*
928 * Change the directory.
929 */
930 char const *pszNativePath;
931 int rc = rtPathToNative(&pszNativePath, pszPath, NULL);
932 if (RT_SUCCESS(rc))
933 {
934 if (chdir(pszNativePath))
935 rc = RTErrConvertFromErrno(errno);
936 rtPathFreeNative(pszNativePath, pszPath);
937 }
938 return rc;
939}
940
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