VirtualBox

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

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

IPRT/posix: Added RTPathSetMode().

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 30.9 KB
Line 
1/* $Id: path-posix.cpp 31309 2010-08-02 14:58:35Z 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 char szRealPath[RTPATH_MAX];
664 rc = RTPathReal(pszPath, szRealPath, sizeof(szRealPath));
665 if (RT_SUCCESS(rc))
666 {
667 if (chmod(szRealPath, fMode & RTFS_UNIX_MASK) < 0)
668 rc = RTErrConvertFromErrno(errno);
669 }
670 rtPathFreeNative(pszNativePath, pszPath);
671 }
672 }
673 else
674 {
675 AssertMsgFailed(("Invalid file mode! %RTfmode\n", fMode));
676 rc = VERR_INVALID_FMODE;
677 }
678 return rc;
679}
680
681
682/**
683 * Checks if two files are the one and same file.
684 */
685static bool rtPathSame(const char *pszNativeSrc, const char *pszNativeDst)
686{
687 struct stat SrcStat;
688 if (stat(pszNativeSrc, &SrcStat))
689 return false;
690 struct stat DstStat;
691 if (stat(pszNativeDst, &DstStat))
692 return false;
693 Assert(SrcStat.st_dev && DstStat.st_dev);
694 Assert(SrcStat.st_ino && DstStat.st_ino);
695 if ( SrcStat.st_dev == DstStat.st_dev
696 && SrcStat.st_ino == DstStat.st_ino
697 && (SrcStat.st_mode & S_IFMT) == (DstStat.st_mode & S_IFMT))
698 return true;
699 return false;
700}
701
702
703/**
704 * Worker for RTPathRename, RTDirRename, RTFileRename.
705 *
706 * @returns IPRT status code.
707 * @param pszSrc The source path.
708 * @param pszDst The destintation path.
709 * @param fRename The rename flags.
710 * @param fFileType The filetype. We use the RTFMODE filetypes here. If it's 0,
711 * anything goes. If it's RTFS_TYPE_DIRECTORY we'll check that the
712 * source is a directory. If Its RTFS_TYPE_FILE we'll check that it's
713 * not a directory (we are NOT checking whether it's a file).
714 */
715DECLHIDDEN(int) rtPathPosixRename(const char *pszSrc, const char *pszDst, unsigned fRename, RTFMODE fFileType)
716{
717 /*
718 * Convert the paths.
719 */
720 char const *pszNativeSrc;
721 int rc = rtPathToNative(&pszNativeSrc, pszSrc, NULL);
722 if (RT_SUCCESS(rc))
723 {
724 char const *pszNativeDst;
725 rc = rtPathToNative(&pszNativeDst, pszDst, NULL);
726 if (RT_SUCCESS(rc))
727 {
728 /*
729 * Check that the source exists and that any types that's specified matches.
730 * We have to check this first to avoid getting errnous VERR_ALREADY_EXISTS
731 * errors from the next step.
732 *
733 * There are race conditions here (perhaps unlikly ones but still), but I'm
734 * afraid there is little with can do to fix that.
735 */
736 struct stat SrcStat;
737 if (stat(pszNativeSrc, &SrcStat))
738 rc = RTErrConvertFromErrno(errno);
739 else if (!fFileType)
740 rc = VINF_SUCCESS;
741 else if (RTFS_IS_DIRECTORY(fFileType))
742 rc = S_ISDIR(SrcStat.st_mode) ? VINF_SUCCESS : VERR_NOT_A_DIRECTORY;
743 else
744 rc = S_ISDIR(SrcStat.st_mode) ? VERR_IS_A_DIRECTORY : VINF_SUCCESS;
745 if (RT_SUCCESS(rc))
746 {
747 bool fSameFile = false;
748
749 /*
750 * Check if the target exists, rename is rather destructive.
751 * We'll have to make sure we don't overwrite the source!
752 * Another race condition btw.
753 */
754 struct stat DstStat;
755 if (stat(pszNativeDst, &DstStat))
756 rc = errno == ENOENT ? VINF_SUCCESS : RTErrConvertFromErrno(errno);
757 else
758 {
759 Assert(SrcStat.st_dev && DstStat.st_dev);
760 Assert(SrcStat.st_ino && DstStat.st_ino);
761 if ( SrcStat.st_dev == DstStat.st_dev
762 && SrcStat.st_ino == DstStat.st_ino
763 && (SrcStat.st_mode & S_IFMT) == (DstStat.st_mode & S_IFMT))
764 {
765 /*
766 * It's likely that we're talking about the same file here.
767 * We should probably check paths or whatever, but for now this'll have to be enough.
768 */
769 fSameFile = true;
770 }
771 if (fSameFile)
772 rc = VINF_SUCCESS;
773 else if (S_ISDIR(DstStat.st_mode) || !(fRename & RTPATHRENAME_FLAGS_REPLACE))
774 rc = VERR_ALREADY_EXISTS;
775 else
776 rc = VINF_SUCCESS;
777
778 }
779 if (RT_SUCCESS(rc))
780 {
781 if (!rename(pszNativeSrc, pszNativeDst))
782 rc = VINF_SUCCESS;
783 else if ( (fRename & RTPATHRENAME_FLAGS_REPLACE)
784 && (errno == ENOTDIR || errno == EEXIST))
785 {
786 /*
787 * Check that the destination isn't a directory.
788 * Yet another race condition.
789 */
790 if (rtPathSame(pszNativeSrc, pszNativeDst))
791 {
792 rc = VINF_SUCCESS;
793 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): appears to be the same file... (errno=%d)\n",
794 pszSrc, pszDst, fRename, fFileType, errno));
795 }
796 else
797 {
798 if (stat(pszNativeDst, &DstStat))
799 rc = errno != ENOENT ? RTErrConvertFromErrno(errno) : VINF_SUCCESS;
800 else if (S_ISDIR(DstStat.st_mode))
801 rc = VERR_ALREADY_EXISTS;
802 else
803 rc = VINF_SUCCESS;
804 if (RT_SUCCESS(rc))
805 {
806 if (!unlink(pszNativeDst))
807 {
808 if (!rename(pszNativeSrc, pszNativeDst))
809 rc = VINF_SUCCESS;
810 else
811 {
812 rc = RTErrConvertFromErrno(errno);
813 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): rename failed rc=%Rrc errno=%d\n",
814 pszSrc, pszDst, fRename, fFileType, rc, errno));
815 }
816 }
817 else
818 {
819 rc = RTErrConvertFromErrno(errno);
820 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): failed to unlink dst rc=%Rrc errno=%d\n",
821 pszSrc, pszDst, fRename, fFileType, rc, errno));
822 }
823 }
824 else
825 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): dst !dir check failed rc=%Rrc\n",
826 pszSrc, pszDst, fRename, fFileType, rc));
827 }
828 }
829 else
830 {
831 rc = RTErrConvertFromErrno(errno);
832 if (errno == ENOTDIR)
833 rc = VERR_ALREADY_EXISTS; /* unless somebody is racing us, this is the right interpretation */
834 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): rename failed rc=%Rrc errno=%d\n",
835 pszSrc, pszDst, fRename, fFileType, rc, errno));
836 }
837 }
838 else
839 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): destination check failed rc=%Rrc errno=%d\n",
840 pszSrc, pszDst, fRename, fFileType, rc, errno));
841 }
842 else
843 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): source type check failed rc=%Rrc errno=%d\n",
844 pszSrc, pszDst, fRename, fFileType, rc, errno));
845
846 rtPathFreeNative(pszNativeDst, pszDst);
847 }
848 rtPathFreeNative(pszNativeSrc, pszSrc);
849 }
850 return rc;
851}
852
853
854RTR3DECL(int) RTPathRename(const char *pszSrc, const char *pszDst, unsigned fRename)
855{
856 /*
857 * Validate input.
858 */
859 AssertMsgReturn(VALID_PTR(pszSrc), ("%p\n", pszSrc), VERR_INVALID_POINTER);
860 AssertMsgReturn(VALID_PTR(pszDst), ("%p\n", pszDst), VERR_INVALID_POINTER);
861 AssertMsgReturn(*pszSrc, ("%p\n", pszSrc), VERR_INVALID_PARAMETER);
862 AssertMsgReturn(*pszDst, ("%p\n", pszDst), VERR_INVALID_PARAMETER);
863 AssertMsgReturn(!(fRename & ~RTPATHRENAME_FLAGS_REPLACE), ("%#x\n", fRename), VERR_INVALID_PARAMETER);
864
865 /*
866 * Hand it to the worker.
867 */
868 int rc = rtPathPosixRename(pszSrc, pszDst, fRename, 0);
869
870 Log(("RTPathRename(%p:{%s}, %p:{%s}, %#x): returns %Rrc\n", pszSrc, pszSrc, pszDst, pszDst, fRename, rc));
871 return rc;
872}
873
874
875RTDECL(bool) RTPathExists(const char *pszPath)
876{
877 return RTPathExistsEx(pszPath, RTPATH_F_FOLLOW_LINK);
878}
879
880
881RTDECL(bool) RTPathExistsEx(const char *pszPath, uint32_t fFlags)
882{
883 /*
884 * Validate input.
885 */
886 AssertPtrReturn(pszPath, false);
887 AssertReturn(*pszPath, false);
888 Assert(RTPATH_F_IS_VALID(fFlags, 0));
889
890 /*
891 * Convert the path and check if it exists using stat().
892 */
893 char const *pszNativePath;
894 int rc = rtPathToNative(&pszNativePath, pszPath, NULL);
895 if (RT_SUCCESS(rc))
896 {
897 struct stat Stat;
898 if (fFlags & RTPATH_F_FOLLOW_LINK)
899 rc = stat(pszNativePath, &Stat);
900 else
901 rc = lstat(pszNativePath, &Stat);
902 if (!rc)
903 rc = VINF_SUCCESS;
904 else
905 rc = VERR_GENERAL_FAILURE;
906 rtPathFreeNative(pszNativePath, pszPath);
907 }
908 return RT_SUCCESS(rc);
909}
910
911
912RTDECL(int) RTPathGetCurrent(char *pszPath, size_t cchPath)
913{
914 int rc;
915 char szNativeCurDir[RTPATH_MAX];
916 if (getcwd(szNativeCurDir, sizeof(szNativeCurDir)) != NULL)
917 rc = rtPathFromNativeCopy(pszPath, cchPath, szNativeCurDir, NULL);
918 else
919 rc = RTErrConvertFromErrno(errno);
920 return rc;
921}
922
923
924RTDECL(int) RTPathSetCurrent(const char *pszPath)
925{
926 /*
927 * Validate input.
928 */
929 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
930 AssertReturn(*pszPath, VERR_INVALID_PARAMETER);
931
932 /*
933 * Change the directory.
934 */
935 char const *pszNativePath;
936 int rc = rtPathToNative(&pszNativePath, pszPath, NULL);
937 if (RT_SUCCESS(rc))
938 {
939 if (chdir(pszNativePath))
940 rc = RTErrConvertFromErrno(errno);
941 rtPathFreeNative(pszNativePath, pszPath);
942 }
943 return rc;
944}
945
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