VirtualBox

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

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

iprt: try split out some of the RTPathQueryInfo* dependent bits into separate files in the posix world.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 18.8 KB
Line 
1/* $Id: path-posix.cpp 34015 2010-11-12 00:15:05Z vboxsync $ */
2/** @file
3 * IPRT - Path Manipulation, POSIX, Part 1.
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, unnecessary 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
305RTR3DECL(int) RTPathSetMode(const char *pszPath, RTFMODE fMode)
306{
307 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
308 AssertReturn(*pszPath, VERR_INVALID_PARAMETER);
309
310 int rc;
311 fMode = rtFsModeNormalize(fMode, pszPath, 0);
312 if (rtFsModeIsValidPermissions(fMode))
313 {
314 char const *pszNativePath;
315 rc = rtPathToNative(&pszNativePath, pszPath, NULL);
316 if (RT_SUCCESS(rc))
317 {
318 if (chmod(pszNativePath, fMode & RTFS_UNIX_MASK) != 0)
319 rc = RTErrConvertFromErrno(errno);
320 rtPathFreeNative(pszNativePath, pszPath);
321 }
322 }
323 else
324 {
325 AssertMsgFailed(("Invalid file mode! %RTfmode\n", fMode));
326 rc = VERR_INVALID_FMODE;
327 }
328 return rc;
329}
330
331
332/**
333 * Checks if two files are the one and same file.
334 */
335static bool rtPathSame(const char *pszNativeSrc, const char *pszNativeDst)
336{
337 struct stat SrcStat;
338 if (stat(pszNativeSrc, &SrcStat))
339 return false;
340 struct stat DstStat;
341 if (stat(pszNativeDst, &DstStat))
342 return false;
343 Assert(SrcStat.st_dev && DstStat.st_dev);
344 Assert(SrcStat.st_ino && DstStat.st_ino);
345 if ( SrcStat.st_dev == DstStat.st_dev
346 && SrcStat.st_ino == DstStat.st_ino
347 && (SrcStat.st_mode & S_IFMT) == (DstStat.st_mode & S_IFMT))
348 return true;
349 return false;
350}
351
352
353/**
354 * Worker for RTPathRename, RTDirRename, RTFileRename.
355 *
356 * @returns IPRT status code.
357 * @param pszSrc The source path.
358 * @param pszDst The destination path.
359 * @param fRename The rename flags.
360 * @param fFileType The filetype. We use the RTFMODE filetypes here. If it's 0,
361 * anything goes. If it's RTFS_TYPE_DIRECTORY we'll check that the
362 * source is a directory. If Its RTFS_TYPE_FILE we'll check that it's
363 * not a directory (we are NOT checking whether it's a file).
364 */
365DECLHIDDEN(int) rtPathPosixRename(const char *pszSrc, const char *pszDst, unsigned fRename, RTFMODE fFileType)
366{
367 /*
368 * Convert the paths.
369 */
370 char const *pszNativeSrc;
371 int rc = rtPathToNative(&pszNativeSrc, pszSrc, NULL);
372 if (RT_SUCCESS(rc))
373 {
374 char const *pszNativeDst;
375 rc = rtPathToNative(&pszNativeDst, pszDst, NULL);
376 if (RT_SUCCESS(rc))
377 {
378 /*
379 * Check that the source exists and that any types that's specified matches.
380 * We have to check this first to avoid getting errnous VERR_ALREADY_EXISTS
381 * errors from the next step.
382 *
383 * There are race conditions here (perhaps unlikely ones but still), but I'm
384 * afraid there is little with can do to fix that.
385 */
386 struct stat SrcStat;
387 if (stat(pszNativeSrc, &SrcStat))
388 rc = RTErrConvertFromErrno(errno);
389 else if (!fFileType)
390 rc = VINF_SUCCESS;
391 else if (RTFS_IS_DIRECTORY(fFileType))
392 rc = S_ISDIR(SrcStat.st_mode) ? VINF_SUCCESS : VERR_NOT_A_DIRECTORY;
393 else
394 rc = S_ISDIR(SrcStat.st_mode) ? VERR_IS_A_DIRECTORY : VINF_SUCCESS;
395 if (RT_SUCCESS(rc))
396 {
397 bool fSameFile = false;
398
399 /*
400 * Check if the target exists, rename is rather destructive.
401 * We'll have to make sure we don't overwrite the source!
402 * Another race condition btw.
403 */
404 struct stat DstStat;
405 if (stat(pszNativeDst, &DstStat))
406 rc = errno == ENOENT ? VINF_SUCCESS : RTErrConvertFromErrno(errno);
407 else
408 {
409 Assert(SrcStat.st_dev && DstStat.st_dev);
410 Assert(SrcStat.st_ino && DstStat.st_ino);
411 if ( SrcStat.st_dev == DstStat.st_dev
412 && SrcStat.st_ino == DstStat.st_ino
413 && (SrcStat.st_mode & S_IFMT) == (DstStat.st_mode & S_IFMT))
414 {
415 /*
416 * It's likely that we're talking about the same file here.
417 * We should probably check paths or whatever, but for now this'll have to be enough.
418 */
419 fSameFile = true;
420 }
421 if (fSameFile)
422 rc = VINF_SUCCESS;
423 else if (S_ISDIR(DstStat.st_mode) || !(fRename & RTPATHRENAME_FLAGS_REPLACE))
424 rc = VERR_ALREADY_EXISTS;
425 else
426 rc = VINF_SUCCESS;
427
428 }
429 if (RT_SUCCESS(rc))
430 {
431 if (!rename(pszNativeSrc, pszNativeDst))
432 rc = VINF_SUCCESS;
433 else if ( (fRename & RTPATHRENAME_FLAGS_REPLACE)
434 && (errno == ENOTDIR || errno == EEXIST))
435 {
436 /*
437 * Check that the destination isn't a directory.
438 * Yet another race condition.
439 */
440 if (rtPathSame(pszNativeSrc, pszNativeDst))
441 {
442 rc = VINF_SUCCESS;
443 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): appears to be the same file... (errno=%d)\n",
444 pszSrc, pszDst, fRename, fFileType, errno));
445 }
446 else
447 {
448 if (stat(pszNativeDst, &DstStat))
449 rc = errno != ENOENT ? RTErrConvertFromErrno(errno) : VINF_SUCCESS;
450 else if (S_ISDIR(DstStat.st_mode))
451 rc = VERR_ALREADY_EXISTS;
452 else
453 rc = VINF_SUCCESS;
454 if (RT_SUCCESS(rc))
455 {
456 if (!unlink(pszNativeDst))
457 {
458 if (!rename(pszNativeSrc, pszNativeDst))
459 rc = VINF_SUCCESS;
460 else
461 {
462 rc = RTErrConvertFromErrno(errno);
463 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): rename failed rc=%Rrc errno=%d\n",
464 pszSrc, pszDst, fRename, fFileType, rc, errno));
465 }
466 }
467 else
468 {
469 rc = RTErrConvertFromErrno(errno);
470 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): failed to unlink dst rc=%Rrc errno=%d\n",
471 pszSrc, pszDst, fRename, fFileType, rc, errno));
472 }
473 }
474 else
475 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): dst !dir check failed rc=%Rrc\n",
476 pszSrc, pszDst, fRename, fFileType, rc));
477 }
478 }
479 else
480 {
481 rc = RTErrConvertFromErrno(errno);
482 if (errno == ENOTDIR)
483 rc = VERR_ALREADY_EXISTS; /* unless somebody is racing us, this is the right interpretation */
484 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): rename failed rc=%Rrc errno=%d\n",
485 pszSrc, pszDst, fRename, fFileType, rc, errno));
486 }
487 }
488 else
489 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): destination check failed rc=%Rrc errno=%d\n",
490 pszSrc, pszDst, fRename, fFileType, rc, errno));
491 }
492 else
493 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): source type check failed rc=%Rrc errno=%d\n",
494 pszSrc, pszDst, fRename, fFileType, rc, errno));
495
496 rtPathFreeNative(pszNativeDst, pszDst);
497 }
498 rtPathFreeNative(pszNativeSrc, pszSrc);
499 }
500 return rc;
501}
502
503
504RTR3DECL(int) RTPathRename(const char *pszSrc, const char *pszDst, unsigned fRename)
505{
506 /*
507 * Validate input.
508 */
509 AssertMsgReturn(VALID_PTR(pszSrc), ("%p\n", pszSrc), VERR_INVALID_POINTER);
510 AssertMsgReturn(VALID_PTR(pszDst), ("%p\n", pszDst), VERR_INVALID_POINTER);
511 AssertMsgReturn(*pszSrc, ("%p\n", pszSrc), VERR_INVALID_PARAMETER);
512 AssertMsgReturn(*pszDst, ("%p\n", pszDst), VERR_INVALID_PARAMETER);
513 AssertMsgReturn(!(fRename & ~RTPATHRENAME_FLAGS_REPLACE), ("%#x\n", fRename), VERR_INVALID_PARAMETER);
514
515 /*
516 * Hand it to the worker.
517 */
518 int rc = rtPathPosixRename(pszSrc, pszDst, fRename, 0);
519
520 Log(("RTPathRename(%p:{%s}, %p:{%s}, %#x): returns %Rrc\n", pszSrc, pszSrc, pszDst, pszDst, fRename, rc));
521 return rc;
522}
523
524
525RTDECL(bool) RTPathExists(const char *pszPath)
526{
527 return RTPathExistsEx(pszPath, RTPATH_F_FOLLOW_LINK);
528}
529
530
531RTDECL(bool) RTPathExistsEx(const char *pszPath, uint32_t fFlags)
532{
533 /*
534 * Validate input.
535 */
536 AssertPtrReturn(pszPath, false);
537 AssertReturn(*pszPath, false);
538 Assert(RTPATH_F_IS_VALID(fFlags, 0));
539
540 /*
541 * Convert the path and check if it exists using stat().
542 */
543 char const *pszNativePath;
544 int rc = rtPathToNative(&pszNativePath, pszPath, NULL);
545 if (RT_SUCCESS(rc))
546 {
547 struct stat Stat;
548 if (fFlags & RTPATH_F_FOLLOW_LINK)
549 rc = stat(pszNativePath, &Stat);
550 else
551 rc = lstat(pszNativePath, &Stat);
552 if (!rc)
553 rc = VINF_SUCCESS;
554 else
555 rc = VERR_GENERAL_FAILURE;
556 rtPathFreeNative(pszNativePath, pszPath);
557 }
558 return RT_SUCCESS(rc);
559}
560
561
562RTDECL(int) RTPathGetCurrent(char *pszPath, size_t cchPath)
563{
564 int rc;
565 char szNativeCurDir[RTPATH_MAX];
566 if (getcwd(szNativeCurDir, sizeof(szNativeCurDir)) != NULL)
567 rc = rtPathFromNativeCopy(pszPath, cchPath, szNativeCurDir, NULL);
568 else
569 rc = RTErrConvertFromErrno(errno);
570 return rc;
571}
572
573
574RTDECL(int) RTPathSetCurrent(const char *pszPath)
575{
576 /*
577 * Validate input.
578 */
579 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
580 AssertReturn(*pszPath, VERR_INVALID_PARAMETER);
581
582 /*
583 * Change the directory.
584 */
585 char const *pszNativePath;
586 int rc = rtPathToNative(&pszNativePath, pszPath, NULL);
587 if (RT_SUCCESS(rc))
588 {
589 if (chdir(pszNativePath))
590 rc = RTErrConvertFromErrno(errno);
591 rtPathFreeNative(pszNativePath, pszPath);
592 }
593 return rc;
594}
595
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