VirtualBox

source: vbox/trunk/src/VBox/Runtime/generic/RTPathAbs-generic.cpp@ 78052

Last change on this file since 78052 was 78048, checked in by vboxsync, 6 years ago

IPRT: Working on new RTPathAbsEx implementation, temporarily called RTPathAbsExEx. This should fix most of the bugs in the current RTPathAbs/Ex code and do away with the fixed path buffer limitations. Also introduces a RTPATH_BIG_MAX, given that RTPATH_MAX is just a reasonable and not absolute MAX value. The new one is more or less absolute and must never be used for stack buffers. bugref:9172

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 11.1 KB
Line 
1/* $Id: RTPathAbs-generic.cpp 78048 2019-04-09 01:21:09Z vboxsync $ */
2/** @file
3 * IPRT - RTPathAbs, generic implementation.
4 */
5
6/*
7 * Copyright (C) 2006-2019 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 <iprt/path.h>
33
34#include <iprt/assert.h>
35#include <iprt/ctype.h>
36#include <iprt/err.h>
37#include <iprt/log.h>
38#include <iprt/string.h>
39#include "internal/path.h"
40#include "internal/fs.h"
41
42#if 1
43
44static char *rtPathSkipRootSpec(char *pszCur)
45{
46#ifdef HAVE_DRIVE
47 if (pszCur[0] && RTPATH_IS_VOLSEP(pszCur[1]) && pszCur[2] == RTPATH_SLASH)
48 pszCur += 3;
49# ifdef HAVE_UNC
50 else if (pszCur[0] == RTPATH_SLASH && pszCur[1] == RTPATH_SLASH)
51 {
52 pszCur += 2;
53 while (*pszCur == RTPATH_SLASH)
54 pszCur++;
55 if (*pszCur)
56 {
57 while (*pszCur != RTPATH_SLASH && *pszCur)
58 pszCur++;
59 if (*pszCur == RTPATH_SLASH)
60 {
61 pszCur++;
62 while (*pszCur != RTPATH_SLASH && *pszCur)
63 pszCur++;
64 if (*pszCur == RTPATH_SLASH)
65 pszCur++;
66 }
67 }
68 }
69# endif
70#else
71 if (pszCur[0] == RTPATH_SLASH)
72 pszCur += 1;
73#endif
74 return pszCur;
75}
76
77
78/**
79 * Cleans up a path specifier a little bit.
80 *
81 * This includes removing duplicate slashes, unnecessary single dots, and
82 * trailing slashes. Also, replaces all slash characters with RTPATH_SLASH.
83 *
84 * @returns Length of the cleaned path (in chars).
85 * @param pszPath The path to cleanup.
86 */
87static int fsCleanPath(char *pszPath)
88{
89 char *pszSrc = pszPath;
90 char *pszTrg = pszPath;
91
92 /*
93 * On windows, you either use on or two slashes at the head of a path,
94 * seems like it treats additional slashes as part of the UNC server name.
95 * Just change slashes to RTPATH_SLASH and skip them.
96 */
97 /** @todo check how OS/2 treats unnecessary leading slashes */
98 /*int cchIgnoreLeading = 0;*/
99#ifdef HAVE_UNC
100 if ( RTPATH_IS_SLASH(pszSrc[0])
101 && RTPATH_IS_SLASH(pszSrc[1]))
102 {
103 pszTrg[0] = RTPATH_SLASH;
104 pszTrg[1] = RTPATH_SLASH;
105 pszTrg += 2;
106 pszSrc += 2;
107 /*cchIgnoreLeading = 1;*/
108 while (RTPATH_IS_SLASH(*pszSrc))
109 {
110 /*cchIgnoreLeading++;*/
111 pszSrc++;
112 *pszTrg++ = RTPATH_SLASH;
113 }
114 }
115#endif
116
117 /*
118 * Change slashes to RTPATH_SLASH and remove duplicates.
119 */
120 for (;;)
121 {
122 char ch = *pszSrc++;
123 if (RTPATH_IS_SLASH(ch))
124 {
125 *pszTrg++ = RTPATH_SLASH;
126 for (;;)
127 {
128 do
129 ch = *pszSrc++;
130 while (RTPATH_IS_SLASH(ch));
131
132 /* Remove '/./' and '/.'. */
133 if ( ch != '.'
134 || (*pszSrc && !RTPATH_IS_SLASH(*pszSrc)))
135 break;
136 }
137 }
138 *pszTrg = ch;
139 if (!ch)
140 break;
141 pszTrg++;
142 }
143
144 return pszTrg - pszPath;
145}
146
147
148RTDECL(int) RTPathAbs(const char *pszPath, char *pszAbsPath, size_t cbAbsPath)
149{
150 int rc;
151
152 /*
153 * Validation.
154 */
155 AssertPtr(pszAbsPath);
156 AssertPtr(pszPath);
157 if (RT_UNLIKELY(!*pszPath))
158 return VERR_INVALID_PARAMETER; //VERR_INVALID_NAME;
159
160 /*
161 * Make a clean working copy of the input.
162 */
163 size_t cchPath = strlen(pszPath);
164 if (cchPath >= RTPATH_MAX)
165 {
166 LogFlow(("RTPathAbs(%p:{%s}, %p, %d): returns %Rrc\n", pszPath, pszPath, pszAbsPath, cbAbsPath, VERR_FILENAME_TOO_LONG));
167 return VERR_FILENAME_TOO_LONG;
168 }
169
170 char szTmpPath[RTPATH_MAX];
171 memcpy(szTmpPath, pszPath, cchPath + 1);
172 size_t cchTmpPath = fsCleanPath(szTmpPath);
173
174 /*
175 * Handle "." specially (fsCleanPath does).
176 */
177 if (szTmpPath[0] == '.')
178 {
179 if ( cchTmpPath == 1
180 || (cchTmpPath == 2 && szTmpPath[1] == RTPATH_SLASH))
181 {
182 rc = RTPathGetCurrent(pszAbsPath, cbAbsPath);
183 if (RT_SUCCESS(rc))
184 {
185 size_t cch = fsCleanPath(pszAbsPath);
186 char *pszTop = rtPathSkipRootSpec(pszAbsPath);
187#if 1
188 if ((uintptr_t)&pszAbsPath[cch] > (uintptr_t)pszTop && pszAbsPath[cch - 1] == RTPATH_SLASH)
189 pszAbsPath[cch - 1] = '\0';
190#else
191 if ( cchTmpPath == 2
192 && (uintptr_t)&pszAbsPath[cch - 1] > (uintptr_t)pszTop && pszAbsPath[cch - 1] != RTPATH_SLASH)
193 {
194 if (cch + 1 < cbAbsPath)
195 {
196 pszAbsPath[cch++] = RTPATH_SLASH;
197 pszAbsPath[cch] = '\0';
198 }
199 else
200 rc = VERR_BUFFER_OVERFLOW;
201 }
202#endif
203 }
204 return rc;
205 }
206 }
207
208 /*
209 * Do we have an incomplete root spec? Supply the missing bits.
210 */
211#ifdef HAVE_DRIVE
212 if ( !(szTmpPath[0] && RTPATH_IS_VOLSEP(szTmpPath[1]) && szTmpPath[2] == RTPATH_SLASH)
213# ifdef HAVE_UNC
214 && !(szTmpPath[0] == RTPATH_SLASH && szTmpPath[1] == RTPATH_SLASH)
215# endif
216 )
217#else
218 if (szTmpPath[0] != RTPATH_SLASH)
219#endif
220 {
221 char szCurDir[RTPATH_MAX];
222 size_t cchCurDir;
223 int offApplyAt;
224 bool fNeedSlash;
225#ifdef HAVE_DRIVE
226 if (szTmpPath[0] && RTPATH_IS_VOLSEP(szTmpPath[1]) && szTmpPath[2] != RTPATH_SLASH)
227 {
228 /*
229 * Relative to drive specific current directory.
230 */
231 rc = RTPathGetCurrentOnDrive(szTmpPath[0], szCurDir, sizeof(szCurDir));
232 fNeedSlash = true;
233 offApplyAt = 2;
234 }
235# ifdef HAVE_UNC
236 else if (szTmpPath[0] == RTPATH_SLASH && szTmpPath[1] != RTPATH_SLASH)
237# else
238 else if (szTmpPath[0] == RTPATH_SLASH)
239# endif
240 {
241 /*
242 * Root of current drive. This may return a UNC root if we're not
243 * standing on a drive but on a UNC share.
244 */
245 rc = RTPathGetCurrentDrive(szCurDir, sizeof(szCurDir));
246 fNeedSlash = false;
247 offApplyAt = 0;
248 }
249 else
250#endif
251 {
252 /*
253 * Relative to current directory.
254 */
255 rc = RTPathGetCurrent(szCurDir, sizeof(szCurDir));
256 fNeedSlash = true;
257 offApplyAt = 0;
258 }
259 if (RT_SUCCESS(rc))
260 {
261 cchCurDir = fsCleanPath(szCurDir);
262 if (fNeedSlash && cchCurDir > 0 && szCurDir[cchCurDir - 1] == RTPATH_SLASH)
263 fNeedSlash = false;
264
265 if (cchCurDir + fNeedSlash + cchTmpPath - offApplyAt <= RTPATH_MAX)
266 {
267 memmove(szTmpPath + cchCurDir + fNeedSlash, szTmpPath + offApplyAt, cchTmpPath + 1 - offApplyAt);
268 memcpy(szTmpPath, szCurDir, cchCurDir);
269 if (fNeedSlash)
270 szTmpPath[cchCurDir] = RTPATH_SLASH;
271 }
272 else
273 rc = VERR_FILENAME_TOO_LONG;
274 }
275 if (RT_FAILURE(rc))
276 {
277 LogFlow(("RTPathAbs(%p:{%s}, %p, %d): returns %Rrc\n", pszPath, pszPath, pszAbsPath, cbAbsPath, rc));
278 return rc;
279 }
280 }
281
282 /*
283 * Skip past the root spec.
284 */
285 char *pszCur = rtPathSkipRootSpec(szTmpPath);
286 AssertMsgReturn(pszCur != &szTmpPath[0], ("pszCur=%s\n", pszCur), VERR_INTERNAL_ERROR);
287 char * const pszTop = pszCur;
288
289 /*
290 * Get rid of double dot path components by evaluating them.
291 */
292 for (;;)
293 {
294 char const chFirst = pszCur[0];
295 if ( chFirst == '.'
296 && pszCur[1] == '.'
297 && (!pszCur[2] || pszCur[2] == RTPATH_SLASH))
298 {
299 /* rewind to the previous component if any */
300 char *pszPrev = pszCur;
301 if ((uintptr_t)pszPrev > (uintptr_t)pszTop)
302 {
303 pszPrev--;
304 while ( (uintptr_t)pszPrev > (uintptr_t)pszTop
305 && pszPrev[-1] != RTPATH_SLASH)
306 pszPrev--;
307 }
308 if (!pszCur[2])
309 {
310 if (pszPrev != pszTop)
311 pszPrev[-1] = '\0';
312 else
313 *pszPrev = '\0';
314 break;
315 }
316 Assert(pszPrev[-1] == RTPATH_SLASH);
317 memmove(pszPrev, pszCur + 3, strlen(pszCur + 3) + 1);
318 pszCur = pszPrev - 1;
319 }
320 else if ( chFirst == '.'
321 && (!pszCur[1] || pszCur[1] == RTPATH_SLASH))
322 {
323 /* remove unnecessary '.' */
324 if (!pszCur[1])
325 {
326 if (pszCur != pszTop)
327 pszCur[-1] = '\0';
328 else
329 *pszCur = '\0';
330 break;
331 }
332 memmove(pszCur, pszCur + 2, strlen(pszCur + 2) + 1);
333 continue;
334 }
335 else
336 {
337 /* advance to end of component. */
338 while (*pszCur && *pszCur != RTPATH_SLASH)
339 pszCur++;
340 }
341
342 if (!*pszCur)
343 break;
344
345 /* skip the slash */
346 ++pszCur;
347 }
348
349 cchTmpPath = pszCur - szTmpPath;
350
351#if 1
352 /*
353 * Strip trailing slash if that's what's desired.
354 */
355
356 if ((uintptr_t)&szTmpPath[cchTmpPath] > (uintptr_t)pszTop && szTmpPath[cchTmpPath - 1] == RTPATH_SLASH)
357 szTmpPath[--cchTmpPath] = '\0';
358#endif
359
360 /*
361 * Copy the result to the user buffer.
362 */
363 if (cchTmpPath < cbAbsPath)
364 {
365 memcpy(pszAbsPath, szTmpPath, cchTmpPath + 1);
366 rc = VINF_SUCCESS;
367 }
368 else
369 rc = VERR_BUFFER_OVERFLOW;
370
371 LogFlow(("RTPathAbs(%p:{%s}, %p:{%s}, %d): returns %Rrc\n", pszPath, pszPath, pszAbsPath,
372 RT_SUCCESS(rc) ? pszAbsPath : "<failed>", cbAbsPath, rc));
373 return rc;
374}
375
376#else
377
378RTDECL(int) RTPathAbs(const char *pszPath, char *pszAbsPath, size_t cbAbsPath)
379{
380 return RTPathAbsExEx(NULL, pszPath, RTPATH_STR_F_STYLE_HOST, pszAbsPath, &cbAbsPath);
381}
382
383#endif
384
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