VirtualBox

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

Last change on this file since 48935 was 48935, checked in by vboxsync, 11 years ago

Runtime: Whitespace and svn:keyword cleanups by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 18.9 KB
Line 
1/* $Id: path-posix.cpp 48935 2013-10-07 21:19:37Z vboxsync $ */
2/** @file
3 * IPRT - Path Manipulation, POSIX, Part 1.
4 */
5
6/*
7 * Copyright (C) 2006-2012 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
53
54RTDECL(int) RTPathReal(const char *pszPath, char *pszRealPath, size_t cchRealPath)
55{
56 /*
57 * Convert input.
58 */
59 char const *pszNativePath;
60 int rc = rtPathToNative(&pszNativePath, pszPath, NULL);
61 if (RT_SUCCESS(rc))
62 {
63 /*
64 * On POSIX platforms the API doesn't take a length parameter, which makes it
65 * a little bit more work.
66 */
67 char szTmpPath[PATH_MAX + 1];
68 const char *psz = realpath(pszNativePath, szTmpPath);
69 if (psz)
70 rc = rtPathFromNativeCopy(pszRealPath, cchRealPath, szTmpPath, NULL);
71 else
72 rc = RTErrConvertFromErrno(errno);
73 rtPathFreeNative(pszNativePath, pszPath);
74 }
75
76 LogFlow(("RTPathReal(%p:{%s}, %p:{%s}, %u): returns %Rrc\n", pszPath, pszPath,
77 pszRealPath, RT_SUCCESS(rc) ? pszRealPath : "<failed>", cchRealPath, rc));
78 return rc;
79}
80
81
82/**
83 * Cleans up a path specifier a little bit.
84 * This includes removing duplicate slashes, unnecessary single dots, and
85 * trailing slashes. Also, replaces all RTPATH_SLASH characters with '/'.
86 *
87 * @returns Number of bytes in the clean path.
88 * @param pszPath The path to cleanup.
89 */
90static int fsCleanPath(char *pszPath)
91{
92 /*
93 * Change to '/' and remove duplicates.
94 */
95 char *pszSrc = pszPath;
96 char *pszTrg = pszPath;
97#ifdef HAVE_UNC
98 int fUnc = 0;
99 if ( RTPATH_IS_SLASH(pszPath[0])
100 && RTPATH_IS_SLASH(pszPath[1]))
101 { /* Skip first slash in a unc path. */
102 pszSrc++;
103 *pszTrg++ = '/';
104 fUnc = 1;
105 }
106#endif
107
108 for (;;)
109 {
110 char ch = *pszSrc++;
111 if (RTPATH_IS_SLASH(ch))
112 {
113 *pszTrg++ = '/';
114 for (;;)
115 {
116 do ch = *pszSrc++;
117 while (RTPATH_IS_SLASH(ch));
118
119 /* Remove '/./' and '/.'. */
120 if (ch != '.' || (*pszSrc && !RTPATH_IS_SLASH(*pszSrc)))
121 break;
122 }
123 }
124 *pszTrg = ch;
125 if (!ch)
126 break;
127 pszTrg++;
128 }
129
130 /*
131 * Remove trailing slash if the path may be pointing to a directory.
132 */
133 int cch = pszTrg - pszPath;
134 if ( cch > 1
135 && RTPATH_IS_SLASH(pszTrg[-1])
136#ifdef HAVE_DRIVE
137 && !RTPATH_IS_VOLSEP(pszTrg[-2])
138#endif
139 && !RTPATH_IS_SLASH(pszTrg[-2]))
140 pszPath[--cch] = '\0';
141
142 return cch;
143}
144
145
146RTDECL(int) RTPathAbs(const char *pszPath, char *pszAbsPath, size_t cchAbsPath)
147{
148 int rc;
149
150 /*
151 * Validation.
152 */
153 AssertPtr(pszAbsPath);
154 AssertPtr(pszPath);
155 if (RT_UNLIKELY(!*pszPath))
156 return VERR_INVALID_PARAMETER;
157
158 /*
159 * Make a clean working copy of the input.
160 */
161 size_t cchPath = strlen(pszPath);
162 if (cchPath > PATH_MAX)
163 {
164 LogFlow(("RTPathAbs(%p:{%s}, %p, %d): returns %Rrc\n", pszPath, pszPath, pszAbsPath, cchAbsPath, VERR_FILENAME_TOO_LONG));
165 return VERR_FILENAME_TOO_LONG;
166 }
167
168 char szTmpPath[PATH_MAX + 1];
169 memcpy(szTmpPath, pszPath, cchPath + 1);
170 size_t cchTmpPath = fsCleanPath(szTmpPath);
171
172 /*
173 * Handle "." specially (fsCleanPath does).
174 */
175 if (szTmpPath[0] == '.' && !szTmpPath[1])
176 return RTPathGetCurrent(pszAbsPath, cchAbsPath);
177
178 /*
179 * Do we have a root slash?
180 */
181 char *pszCur = szTmpPath;
182#ifdef HAVE_DRIVE
183 if (pszCur[0] && RTPATH_IS_VOLSEP(pszCur[1]) && pszCur[2] == '/')
184 pszCur += 3;
185# ifdef HAVE_UNC
186 else if (pszCur[0] == '/' && pszCur[1] == '/')
187 pszCur += 2;
188# endif
189#else /* !HAVE_DRIVE */
190 if (pszCur[0] == '/')
191 pszCur += 1;
192#endif /* !HAVE_DRIVE */
193 else
194 {
195 /*
196 * No, prepend the current directory to the relative path.
197 */
198 char szCurDir[RTPATH_MAX];
199 rc = RTPathGetCurrent(szCurDir, sizeof(szCurDir));
200 AssertRCReturn(rc, rc);
201
202 size_t cchCurDir = fsCleanPath(szCurDir); /* paranoia */
203 if (cchCurDir + cchTmpPath + 1 > PATH_MAX)
204 {
205 LogFlow(("RTPathAbs(%p:{%s}, %p, %d): returns %Rrc\n", pszPath, pszPath, pszAbsPath, cchAbsPath, VERR_FILENAME_TOO_LONG));
206 return VERR_FILENAME_TOO_LONG;
207 }
208
209 memmove(szTmpPath + cchCurDir + 1, szTmpPath, cchTmpPath + 1);
210 memcpy(szTmpPath, szCurDir, cchCurDir);
211 szTmpPath[cchCurDir] = '/';
212
213
214#ifdef HAVE_DRIVE
215 if (pszCur[0] && RTPATH_IS_VOLSEP(pszCur[1]) && pszCur[2] == '/')
216 pszCur += 3;
217# ifdef HAVE_UNC
218 else if (pszCur[0] == '/' && pszCur[1] == '/')
219 pszCur += 2;
220# endif
221#else
222 if (pszCur[0] == '/')
223 pszCur += 1;
224#endif
225 else
226 AssertMsgFailedReturn(("pszCur=%s\n", pszCur), VERR_INTERNAL_ERROR);
227 }
228
229 char *pszTop = pszCur;
230
231 /*
232 * Get rid of double dot path components by evaluating them.
233 */
234 for (;;)
235 {
236 if ( pszCur[0] == '.'
237 && pszCur[1] == '.'
238 && (!pszCur[2] || pszCur[2] == '/'))
239 {
240 /* rewind to the previous component if any */
241 char *pszPrev = pszCur - 1;
242 if (pszPrev > pszTop)
243 while (*--pszPrev != '/')
244 ;
245
246 AssertMsg(*pszPrev == '/', ("szTmpPath={%s}, pszPrev=+%u\n", szTmpPath, pszPrev - szTmpPath));
247 memmove(pszPrev, pszCur + 2, strlen(pszCur + 2) + 1);
248
249 pszCur = pszPrev;
250 }
251 else
252 {
253 /* advance to end of component. */
254 while (*pszCur && *pszCur != '/')
255 pszCur++;
256 }
257
258 if (!*pszCur)
259 break;
260
261 /* skip the slash */
262 ++pszCur;
263 }
264
265 if (pszCur < pszTop)
266 {
267 /*
268 * We overwrote the root slash with '\0', restore it.
269 */
270 *pszCur++ = '/';
271 *pszCur = '\0';
272 }
273 else if (pszCur > pszTop && pszCur[-1] == '/')
274 {
275 /*
276 * Extra trailing slash in a non-root path, remove it.
277 * (A bit questionable...)
278 */
279 *--pszCur = '\0';
280 }
281
282 /*
283 * Copy the result to the user buffer.
284 */
285 cchTmpPath = pszCur - szTmpPath;
286 if (cchTmpPath < cchAbsPath)
287 {
288 memcpy(pszAbsPath, szTmpPath, cchTmpPath + 1);
289 rc = VINF_SUCCESS;
290 }
291 else
292 rc = VERR_BUFFER_OVERFLOW;
293
294 LogFlow(("RTPathAbs(%p:{%s}, %p:{%s}, %d): returns %Rrc\n", pszPath, pszPath, pszAbsPath,
295 RT_SUCCESS(rc) ? pszAbsPath : "<failed>", cchAbsPath, rc));
296 return rc;
297}
298
299
300RTR3DECL(int) RTPathSetMode(const char *pszPath, RTFMODE fMode)
301{
302 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
303 AssertReturn(*pszPath, VERR_INVALID_PARAMETER);
304
305 int rc;
306 fMode = rtFsModeNormalize(fMode, pszPath, 0);
307 if (rtFsModeIsValidPermissions(fMode))
308 {
309 char const *pszNativePath;
310 rc = rtPathToNative(&pszNativePath, pszPath, NULL);
311 if (RT_SUCCESS(rc))
312 {
313 if (chmod(pszNativePath, fMode & RTFS_UNIX_MASK) != 0)
314 rc = RTErrConvertFromErrno(errno);
315 rtPathFreeNative(pszNativePath, pszPath);
316 }
317 }
318 else
319 {
320 AssertMsgFailed(("Invalid file mode! %RTfmode\n", fMode));
321 rc = VERR_INVALID_FMODE;
322 }
323 return rc;
324}
325
326
327/**
328 * Checks if two files are the one and same file.
329 */
330static bool rtPathSame(const char *pszNativeSrc, const char *pszNativeDst)
331{
332 struct stat SrcStat;
333 if (lstat(pszNativeSrc, &SrcStat))
334 return false;
335 struct stat DstStat;
336 if (lstat(pszNativeDst, &DstStat))
337 return false;
338 Assert(SrcStat.st_dev && DstStat.st_dev);
339 Assert(SrcStat.st_ino && DstStat.st_ino);
340 if ( SrcStat.st_dev == DstStat.st_dev
341 && SrcStat.st_ino == DstStat.st_ino
342 && (SrcStat.st_mode & S_IFMT) == (DstStat.st_mode & S_IFMT))
343 return true;
344 return false;
345}
346
347
348/**
349 * Worker for RTPathRename, RTDirRename, RTFileRename.
350 *
351 * @returns IPRT status code.
352 * @param pszSrc The source path.
353 * @param pszDst The destination path.
354 * @param fRename The rename flags.
355 * @param fFileType The filetype. We use the RTFMODE filetypes here. If it's 0,
356 * anything goes. If it's RTFS_TYPE_DIRECTORY we'll check that the
357 * source is a directory. If Its RTFS_TYPE_FILE we'll check that it's
358 * not a directory (we are NOT checking whether it's a file).
359 */
360DECLHIDDEN(int) rtPathPosixRename(const char *pszSrc, const char *pszDst, unsigned fRename, RTFMODE fFileType)
361{
362 /*
363 * Convert the paths.
364 */
365 char const *pszNativeSrc;
366 int rc = rtPathToNative(&pszNativeSrc, pszSrc, NULL);
367 if (RT_SUCCESS(rc))
368 {
369 char const *pszNativeDst;
370 rc = rtPathToNative(&pszNativeDst, pszDst, NULL);
371 if (RT_SUCCESS(rc))
372 {
373 /*
374 * Check that the source exists and that any types that's specified matches.
375 * We have to check this first to avoid getting errnous VERR_ALREADY_EXISTS
376 * errors from the next step.
377 *
378 * There are race conditions here (perhaps unlikely ones, but still), but I'm
379 * afraid there is little with can do to fix that.
380 */
381 struct stat SrcStat;
382 if (lstat(pszNativeSrc, &SrcStat))
383 rc = RTErrConvertFromErrno(errno);
384 else if (!fFileType)
385 rc = VINF_SUCCESS;
386 else if (RTFS_IS_DIRECTORY(fFileType))
387 rc = S_ISDIR(SrcStat.st_mode) ? VINF_SUCCESS : VERR_NOT_A_DIRECTORY;
388 else
389 rc = S_ISDIR(SrcStat.st_mode) ? VERR_IS_A_DIRECTORY : VINF_SUCCESS;
390 if (RT_SUCCESS(rc))
391 {
392 bool fSameFile = false;
393
394 /*
395 * Check if the target exists, rename is rather destructive.
396 * We'll have to make sure we don't overwrite the source!
397 * Another race condition btw.
398 */
399 struct stat DstStat;
400 if (lstat(pszNativeDst, &DstStat))
401 rc = errno == ENOENT ? VINF_SUCCESS : RTErrConvertFromErrno(errno);
402 else
403 {
404 Assert(SrcStat.st_dev && DstStat.st_dev);
405 Assert(SrcStat.st_ino && DstStat.st_ino);
406 if ( SrcStat.st_dev == DstStat.st_dev
407 && SrcStat.st_ino == DstStat.st_ino
408 && (SrcStat.st_mode & S_IFMT) == (DstStat.st_mode & S_IFMT))
409 {
410 /*
411 * It's likely that we're talking about the same file here.
412 * We should probably check paths or whatever, but for now this'll have to be enough.
413 */
414 fSameFile = true;
415 }
416 if (fSameFile)
417 rc = VINF_SUCCESS;
418 else if (S_ISDIR(DstStat.st_mode) || !(fRename & RTPATHRENAME_FLAGS_REPLACE))
419 rc = VERR_ALREADY_EXISTS;
420 else
421 rc = VINF_SUCCESS;
422
423 }
424 if (RT_SUCCESS(rc))
425 {
426 if (!rename(pszNativeSrc, pszNativeDst))
427 rc = VINF_SUCCESS;
428 else if ( (fRename & RTPATHRENAME_FLAGS_REPLACE)
429 && (errno == ENOTDIR || errno == EEXIST))
430 {
431 /*
432 * Check that the destination isn't a directory.
433 * Yet another race condition.
434 */
435 if (rtPathSame(pszNativeSrc, pszNativeDst))
436 {
437 rc = VINF_SUCCESS;
438 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): appears to be the same file... (errno=%d)\n",
439 pszSrc, pszDst, fRename, fFileType, errno));
440 }
441 else
442 {
443 if (lstat(pszNativeDst, &DstStat))
444 rc = errno != ENOENT ? RTErrConvertFromErrno(errno) : VINF_SUCCESS;
445 else if (S_ISDIR(DstStat.st_mode))
446 rc = VERR_ALREADY_EXISTS;
447 else
448 rc = VINF_SUCCESS;
449 if (RT_SUCCESS(rc))
450 {
451 if (!unlink(pszNativeDst))
452 {
453 if (!rename(pszNativeSrc, pszNativeDst))
454 rc = VINF_SUCCESS;
455 else
456 {
457 rc = RTErrConvertFromErrno(errno);
458 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): rename failed rc=%Rrc errno=%d\n",
459 pszSrc, pszDst, fRename, fFileType, rc, errno));
460 }
461 }
462 else
463 {
464 rc = RTErrConvertFromErrno(errno);
465 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): failed to unlink dst rc=%Rrc errno=%d\n",
466 pszSrc, pszDst, fRename, fFileType, rc, errno));
467 }
468 }
469 else
470 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): dst !dir check failed rc=%Rrc\n",
471 pszSrc, pszDst, fRename, fFileType, rc));
472 }
473 }
474 else
475 {
476 rc = RTErrConvertFromErrno(errno);
477 if (errno == ENOTDIR)
478 rc = VERR_ALREADY_EXISTS; /* unless somebody is racing us, this is the right interpretation */
479 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): rename failed rc=%Rrc errno=%d\n",
480 pszSrc, pszDst, fRename, fFileType, rc, errno));
481 }
482 }
483 else
484 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): destination check failed rc=%Rrc errno=%d\n",
485 pszSrc, pszDst, fRename, fFileType, rc, errno));
486 }
487 else
488 Log(("rtPathRename('%s', '%s', %#x ,%RTfmode): source type check failed rc=%Rrc errno=%d\n",
489 pszSrc, pszDst, fRename, fFileType, rc, errno));
490
491 rtPathFreeNative(pszNativeDst, pszDst);
492 }
493 rtPathFreeNative(pszNativeSrc, pszSrc);
494 }
495 return rc;
496}
497
498
499RTR3DECL(int) RTPathRename(const char *pszSrc, const char *pszDst, unsigned fRename)
500{
501 /*
502 * Validate input.
503 */
504 AssertMsgReturn(VALID_PTR(pszSrc), ("%p\n", pszSrc), VERR_INVALID_POINTER);
505 AssertMsgReturn(VALID_PTR(pszDst), ("%p\n", pszDst), VERR_INVALID_POINTER);
506 AssertMsgReturn(*pszSrc, ("%p\n", pszSrc), VERR_INVALID_PARAMETER);
507 AssertMsgReturn(*pszDst, ("%p\n", pszDst), VERR_INVALID_PARAMETER);
508 AssertMsgReturn(!(fRename & ~RTPATHRENAME_FLAGS_REPLACE), ("%#x\n", fRename), VERR_INVALID_PARAMETER);
509
510 /*
511 * Hand it to the worker.
512 */
513 int rc = rtPathPosixRename(pszSrc, pszDst, fRename, 0);
514
515 Log(("RTPathRename(%p:{%s}, %p:{%s}, %#x): returns %Rrc\n", pszSrc, pszSrc, pszDst, pszDst, fRename, rc));
516 return rc;
517}
518
519
520RTR3DECL(int) RTPathUnlink(const char *pszPath, uint32_t fUnlink)
521{
522 return VERR_NOT_IMPLEMENTED;
523}
524
525
526RTDECL(bool) RTPathExists(const char *pszPath)
527{
528 return RTPathExistsEx(pszPath, RTPATH_F_FOLLOW_LINK);
529}
530
531
532RTDECL(bool) RTPathExistsEx(const char *pszPath, uint32_t fFlags)
533{
534 /*
535 * Validate input.
536 */
537 AssertPtrReturn(pszPath, false);
538 AssertReturn(*pszPath, false);
539 Assert(RTPATH_F_IS_VALID(fFlags, 0));
540
541 /*
542 * Convert the path and check if it exists using stat().
543 */
544 char const *pszNativePath;
545 int rc = rtPathToNative(&pszNativePath, pszPath, NULL);
546 if (RT_SUCCESS(rc))
547 {
548 struct stat Stat;
549 if (fFlags & RTPATH_F_FOLLOW_LINK)
550 rc = stat(pszNativePath, &Stat);
551 else
552 rc = lstat(pszNativePath, &Stat);
553 if (!rc)
554 rc = VINF_SUCCESS;
555 else
556 rc = VERR_GENERAL_FAILURE;
557 rtPathFreeNative(pszNativePath, pszPath);
558 }
559 return RT_SUCCESS(rc);
560}
561
562
563RTDECL(int) RTPathGetCurrent(char *pszPath, size_t cchPath)
564{
565 int rc;
566 char szNativeCurDir[RTPATH_MAX];
567 if (getcwd(szNativeCurDir, sizeof(szNativeCurDir)) != NULL)
568 rc = rtPathFromNativeCopy(pszPath, cchPath, szNativeCurDir, NULL);
569 else
570 rc = RTErrConvertFromErrno(errno);
571 return rc;
572}
573
574
575RTDECL(int) RTPathSetCurrent(const char *pszPath)
576{
577 /*
578 * Validate input.
579 */
580 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
581 AssertReturn(*pszPath, VERR_INVALID_PARAMETER);
582
583 /*
584 * Change the directory.
585 */
586 char const *pszNativePath;
587 int rc = rtPathToNative(&pszNativePath, pszPath, NULL);
588 if (RT_SUCCESS(rc))
589 {
590 if (chdir(pszNativePath))
591 rc = RTErrConvertFromErrno(errno);
592 rtPathFreeNative(pszNativePath, pszPath);
593 }
594 return rc;
595}
596
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