VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/vfs/vfschain.cpp@ 69716

最後變更 在這個檔案從69716是 69716,由 vboxsync 提交於 7 年 前

IPRT: More work on directory relative APIs (NT mainly) and VFS; introducing RTMkDir (test) tool.

  • Added RTVfsDirCreateDir
  • Added RTVfsChainOpenParentDir and RTVfsChainSplitOffFinalPath.
  • Added new tool for testing this called RTMkDir.
  • Fixed directory traversal problem with stddir by making it okay to return VERR_FILE_NOT_FOUND as well.
  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 60.0 KB
 
1/* $Id: vfschain.cpp 69716 2017-11-16 14:31:25Z vboxsync $ */
2/** @file
3 * IPRT - Virtual File System, Chains.
4 */
5
6/*
7 * Copyright (C) 2010-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.alldomusa.eu.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#include <iprt/vfs.h>
32#include <iprt/vfslowlevel.h>
33
34#include <iprt/asm.h>
35#include <iprt/critsect.h>
36#include <iprt/ctype.h>
37#include <iprt/err.h>
38#include <iprt/file.h>
39#include <iprt/mem.h>
40#include <iprt/once.h>
41#include <iprt/param.h>
42#include <iprt/path.h>
43#include <iprt/semaphore.h>
44#include <iprt/string.h>
45
46#include "internal/file.h"
47#include "internal/magics.h"
48//#include "internal/vfs.h"
49
50
51/*********************************************************************************************************************************
52* Internal Functions *
53*********************************************************************************************************************************/
54static PCRTVFSCHAINELEMENTREG rtVfsChainFindProviderLocked(const char *pszProvider);
55
56
57/*********************************************************************************************************************************
58* Global Variables *
59*********************************************************************************************************************************/
60/** Init the critical section once. */
61static RTONCE g_rtVfsChainElementInitOnce = RTONCE_INITIALIZER;
62/** Critical section protecting g_rtVfsChainElementProviderList. */
63static RTCRITSECTRW g_rtVfsChainElementCritSect;
64/** List of VFS chain element providers (RTVFSCHAINELEMENTREG). */
65static RTLISTANCHOR g_rtVfsChainElementProviderList;
66
67
68
69RTDECL(int) RTVfsChainValidateOpenFileOrIoStream(PRTVFSCHAINSPEC pSpec, PRTVFSCHAINELEMSPEC pElement,
70 uint32_t *poffError, PRTERRINFO pErrInfo)
71{
72 if (pElement->cArgs < 1)
73 return VERR_VFS_CHAIN_AT_LEAST_ONE_ARG;
74 if (pElement->cArgs > 4)
75 return VERR_VFS_CHAIN_AT_MOST_FOUR_ARGS;
76 if (!*pElement->paArgs[0].psz)
77 return VERR_VFS_CHAIN_EMPTY_ARG;
78
79 /*
80 * Calculate the flags, storing them in the first argument.
81 */
82 const char *pszAccess = pElement->cArgs >= 2 ? pElement->paArgs[1].psz : "";
83 if (!*pszAccess)
84 pszAccess = (pSpec->fOpenFile & RTFILE_O_ACCESS_MASK) == RTFILE_O_READWRITE ? "rw"
85 : (pSpec->fOpenFile & RTFILE_O_ACCESS_MASK) == RTFILE_O_READ ? "r"
86 : (pSpec->fOpenFile & RTFILE_O_ACCESS_MASK) == RTFILE_O_WRITE ? "w"
87 : "rw";
88
89 const char *pszDisp = pElement->cArgs >= 3 ? pElement->paArgs[2].psz : "";
90 if (!*pszDisp)
91 pszDisp = strchr(pszAccess, 'w') != NULL ? "open-create" : "open";
92
93 const char *pszSharing = pElement->cArgs >= 4 ? pElement->paArgs[3].psz : "";
94
95 int rc = RTFileModeToFlagsEx(pszAccess, pszDisp, pszSharing, &pElement->uProvider);
96 if (RT_SUCCESS(rc))
97 return VINF_SUCCESS;
98
99 /*
100 * Now try figure out which argument offended us.
101 */
102 AssertReturn(pElement->cArgs > 1, VERR_VFS_CHAIN_IPE);
103 if ( pElement->cArgs == 2
104 || RT_FAILURE(RTFileModeToFlagsEx(pszAccess, "open-create", "", &pElement->uProvider)))
105 {
106 *poffError = pElement->paArgs[1].offSpec;
107 rc = RTErrInfoSet(pErrInfo, VERR_VFS_CHAIN_INVALID_ARGUMENT, "Expected valid access flags: 'r', 'rw', or 'w'");
108 }
109 else if ( pElement->cArgs == 3
110 || RT_FAILURE(RTFileModeToFlagsEx(pszAccess, pszDisp, "", &pElement->uProvider)))
111 {
112 *poffError = pElement->paArgs[2].offSpec;
113 rc = RTErrInfoSet(pErrInfo, VERR_VFS_CHAIN_INVALID_ARGUMENT,
114 "Expected valid open disposition: create, create-replace, open, open-create, open-append, open-truncate");
115 }
116 else
117 {
118 *poffError = pElement->paArgs[3].offSpec;
119 rc = RTErrInfoSet(pErrInfo, VERR_VFS_CHAIN_INVALID_ARGUMENT, "Expected valid sharing flags: nr, nw, nrw, d");
120
121 }
122 return rc;
123}
124
125
126/**
127 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnValidate}
128 */
129static DECLCALLBACK(int) rtVfsChainOpen_Validate(PCRTVFSCHAINELEMENTREG pProviderReg, PRTVFSCHAINSPEC pSpec,
130 PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError, PRTERRINFO pErrInfo)
131{
132 RT_NOREF(pProviderReg);
133
134 /*
135 * Basic checks.
136 */
137 if ( pElement->enmType != RTVFSOBJTYPE_DIR
138 && pElement->enmType != RTVFSOBJTYPE_FILE
139 && pElement->enmType != RTVFSOBJTYPE_IO_STREAM)
140 return VERR_VFS_CHAIN_ONLY_FILE_OR_IOS_OR_DIR;
141 if ( pElement->enmTypeIn != RTVFSOBJTYPE_DIR
142 && pElement->enmTypeIn != RTVFSOBJTYPE_FS_STREAM
143 && pElement->enmTypeIn != RTVFSOBJTYPE_VFS)
144 {
145 if (pElement->enmTypeIn == RTVFSOBJTYPE_INVALID)
146 {
147 /*
148 * First element: Transform into 'stdfile' or 'stddir' if registered.
149 */
150 const char *pszNewProvider = pElement->enmType == RTVFSOBJTYPE_DIR ? "stddir" : "stdfile";
151 PCRTVFSCHAINELEMENTREG pNewProvider = rtVfsChainFindProviderLocked(pszNewProvider);
152 if (pNewProvider)
153 {
154 pElement->pProvider = pNewProvider;
155 return pNewProvider->pfnValidate(pNewProvider, pSpec, pElement, poffError, pErrInfo);
156 }
157 return VERR_VFS_CHAIN_CANNOT_BE_FIRST_ELEMENT;
158 }
159 return VERR_VFS_CHAIN_TAKES_DIR_OR_FSS_OR_VFS;
160 }
161
162 /*
163 * Make common cause with 'stdfile' if we're opening a file or I/O stream.
164 * If the input is a FSS, we have to make sure it's a read-only operation.
165 */
166 if ( pElement->enmType == RTVFSOBJTYPE_FILE
167 || pElement->enmType == RTVFSOBJTYPE_IO_STREAM)
168 {
169 int rc = RTVfsChainValidateOpenFileOrIoStream(pSpec, pElement, poffError, pErrInfo);
170 if (RT_SUCCESS(rc))
171 {
172 if (pElement->enmTypeIn != RTVFSOBJTYPE_FS_STREAM)
173 return VINF_SUCCESS;
174 if ( !(pElement->uProvider & RTFILE_O_WRITE)
175 && (pElement->uProvider & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN)
176 return VINF_SUCCESS;
177 *poffError = pElement->cArgs > 1 ? pElement->paArgs[1].offSpec : pElement->offSpec;
178 return VERR_VFS_CHAIN_INVALID_ARGUMENT;
179 }
180 return rc;
181 }
182
183
184 /*
185 * Directory checks. Path argument only, optional. If not given the root directory of a VFS or the
186 */
187 if (pElement->cArgs > 1)
188 return VERR_VFS_CHAIN_AT_MOST_ONE_ARG;
189 pElement->uProvider = 0;
190 return VINF_SUCCESS;
191}
192
193
194/**
195 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnInstantiate}
196 */
197static DECLCALLBACK(int) rtVfsChainOpen_Instantiate(PCRTVFSCHAINELEMENTREG pProviderReg, PCRTVFSCHAINSPEC pSpec,
198 PCRTVFSCHAINELEMSPEC pElement, RTVFSOBJ hPrevVfsObj,
199 PRTVFSOBJ phVfsObj, uint32_t *poffError, PRTERRINFO pErrInfo)
200{
201 RT_NOREF(pProviderReg, pSpec, pElement, poffError, pErrInfo);
202 AssertReturn(hPrevVfsObj != NIL_RTVFSOBJ, VERR_VFS_CHAIN_IPE);
203
204 /*
205 * File system stream: Seek thru the stream looking for the object to open.
206 */
207 RTVFSFSSTREAM hVfsFssIn = RTVfsObjToFsStream(hPrevVfsObj);
208 if (hVfsFssIn != NIL_RTVFSFSSTREAM)
209 {
210 return VERR_NOT_IMPLEMENTED;
211 }
212
213 /*
214 * VFS: Use RTVfsFileOpen or RTVfsDirOpen.
215 */
216 RTVFS hVfsIn = RTVfsObjToVfs(hPrevVfsObj);
217 if (hVfsIn != NIL_RTVFS)
218 {
219 if ( pElement->enmType == RTVFSOBJTYPE_FILE
220 || pElement->enmType == RTVFSOBJTYPE_IO_STREAM)
221 {
222 RTVFSFILE hVfsFile = NIL_RTVFSFILE;
223 int rc = RTVfsFileOpen(hVfsIn, pElement->paArgs[0].psz, pElement->uProvider, &hVfsFile);
224 RTVfsRelease(hVfsIn);
225 if (RT_SUCCESS(rc))
226 {
227 *phVfsObj = RTVfsObjFromFile(hVfsFile);
228 RTVfsFileRelease(hVfsFile);
229 if (*phVfsObj != NIL_RTVFSOBJ)
230 return VINF_SUCCESS;
231 rc = VERR_VFS_CHAIN_CAST_FAILED;
232 }
233 return rc;
234 }
235 if (pElement->enmType == RTVFSOBJTYPE_DIR)
236 {
237 RTVFSDIR hVfsDir = NIL_RTVFSDIR;
238 int rc = RTVfsDirOpen(hVfsIn, pElement->paArgs[0].psz, (uint32_t)pElement->uProvider, &hVfsDir);
239 RTVfsRelease(hVfsIn);
240 if (RT_SUCCESS(rc))
241 {
242 *phVfsObj = RTVfsObjFromDir(hVfsDir);
243 RTVfsDirRelease(hVfsDir);
244 if (*phVfsObj != NIL_RTVFSOBJ)
245 return VINF_SUCCESS;
246 rc = VERR_VFS_CHAIN_CAST_FAILED;
247 }
248 return rc;
249 }
250 RTVfsRelease(hVfsIn);
251 return VERR_VFS_CHAIN_IPE;
252 }
253
254 /*
255 * Directory: Similar to above, just relative to a directory.
256 */
257 RTVFSDIR hVfsDirIn = RTVfsObjToDir(hPrevVfsObj);
258 if (hVfsDirIn != NIL_RTVFSDIR)
259 {
260 if ( pElement->enmType == RTVFSOBJTYPE_FILE
261 || pElement->enmType == RTVFSOBJTYPE_IO_STREAM)
262 {
263 RTVFSFILE hVfsFile = NIL_RTVFSFILE;
264 int rc = RTVfsDirOpenFile(hVfsDirIn, pElement->paArgs[0].psz, pElement->uProvider, &hVfsFile);
265 RTVfsDirRelease(hVfsDirIn);
266 if (RT_SUCCESS(rc))
267 {
268 *phVfsObj = RTVfsObjFromFile(hVfsFile);
269 RTVfsFileRelease(hVfsFile);
270 if (*phVfsObj != NIL_RTVFSOBJ)
271 return VINF_SUCCESS;
272 rc = VERR_VFS_CHAIN_CAST_FAILED;
273 }
274 return rc;
275 }
276 if (pElement->enmType == RTVFSOBJTYPE_DIR)
277 {
278 RTVFSDIR hVfsDir = NIL_RTVFSDIR;
279 int rc = RTVfsDirOpenDir(hVfsDirIn, pElement->paArgs[0].psz, pElement->uProvider, &hVfsDir);
280 RTVfsDirRelease(hVfsDirIn);
281 if (RT_SUCCESS(rc))
282 {
283 *phVfsObj = RTVfsObjFromDir(hVfsDir);
284 RTVfsDirRelease(hVfsDir);
285 if (*phVfsObj != NIL_RTVFSOBJ)
286 return VINF_SUCCESS;
287 rc = VERR_VFS_CHAIN_CAST_FAILED;
288 }
289 return rc;
290 }
291 RTVfsDirRelease(hVfsDirIn);
292 return VERR_VFS_CHAIN_IPE;
293 }
294
295 AssertFailed();
296 return VERR_VFS_CHAIN_CAST_FAILED;
297}
298
299
300/**
301 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnCanReuseElement}
302 */
303static DECLCALLBACK(bool) rtVfsChainOpen_CanReuseElement(PCRTVFSCHAINELEMENTREG pProviderReg,
304 PCRTVFSCHAINSPEC pSpec, PCRTVFSCHAINELEMSPEC pElement,
305 PCRTVFSCHAINSPEC pReuseSpec, PCRTVFSCHAINELEMSPEC pReuseElement)
306{
307 RT_NOREF(pProviderReg, pSpec, pElement, pReuseSpec, pReuseElement);
308 return false;
309}
310
311
312/** VFS chain element 'gunzip'. */
313static RTVFSCHAINELEMENTREG g_rtVfsChainGunzipReg =
314{
315 /* uVersion = */ RTVFSCHAINELEMENTREG_VERSION,
316 /* fReserved = */ 0,
317 /* pszName = */ "open",
318 /* ListEntry = */ { NULL, NULL },
319 /* pszHelp = */ "Generic VFS open, that can open files (or I/O stream) and directories in a VFS, directory or file system stream.\n"
320 "If used as the first element in a chain, it will work like 'stdfile' or 'stddir' and work on the real file system.\n"
321 "First argument is the filename or directory path.\n"
322 "Second argument is access mode, files only, optional: r, w, rw.\n"
323 "Third argument is open disposition, files only, optional: create, create-replace, open, open-create, open-append, open-truncate.\n"
324 "Forth argument is file sharing, files only, optional: nr, nw, nrw, d.",
325 /* pfnValidate = */ rtVfsChainOpen_Validate,
326 /* pfnInstantiate = */ rtVfsChainOpen_Instantiate,
327 /* pfnCanReuseElement = */ rtVfsChainOpen_CanReuseElement,
328 /* uEndMarker = */ RTVFSCHAINELEMENTREG_VERSION
329};
330
331RTVFSCHAIN_AUTO_REGISTER_ELEMENT_PROVIDER(&g_rtVfsChainGunzipReg, rtVfsChainGunzipReg);
332
333
334
335
336/**
337 * Initializes the globals via RTOnce.
338 *
339 * @returns IPRT status code
340 * @param pvUser Unused, ignored.
341 */
342static DECLCALLBACK(int) rtVfsChainElementRegisterInit(void *pvUser)
343{
344 NOREF(pvUser);
345 if (!g_rtVfsChainElementProviderList.pNext)
346 RTListInit(&g_rtVfsChainElementProviderList);
347 int rc = RTCritSectRwInit(&g_rtVfsChainElementCritSect);
348 if (RT_SUCCESS(rc))
349 {
350 }
351 return rc;
352}
353
354
355RTDECL(int) RTVfsChainElementRegisterProvider(PRTVFSCHAINELEMENTREG pRegRec, bool fFromCtor)
356{
357 int rc;
358
359 /*
360 * Input validation.
361 */
362 AssertPtrReturn(pRegRec, VERR_INVALID_POINTER);
363 AssertMsgReturn(pRegRec->uVersion == RTVFSCHAINELEMENTREG_VERSION, ("%#x", pRegRec->uVersion), VERR_INVALID_POINTER);
364 AssertMsgReturn(pRegRec->uEndMarker == RTVFSCHAINELEMENTREG_VERSION, ("%#zx", pRegRec->uEndMarker), VERR_INVALID_POINTER);
365 AssertReturn(pRegRec->fReserved == 0, VERR_INVALID_POINTER);
366 AssertPtrReturn(pRegRec->pszName, VERR_INVALID_POINTER);
367 AssertPtrReturn(pRegRec->pfnValidate, VERR_INVALID_POINTER);
368 AssertPtrReturn(pRegRec->pfnInstantiate, VERR_INVALID_POINTER);
369 AssertPtrReturn(pRegRec->pfnCanReuseElement, VERR_INVALID_POINTER);
370
371 /*
372 * Init and take the lock.
373 */
374 if (!fFromCtor)
375 {
376 rc = RTOnce(&g_rtVfsChainElementInitOnce, rtVfsChainElementRegisterInit, NULL);
377 if (RT_FAILURE(rc))
378 return rc;
379 rc = RTCritSectRwEnterExcl(&g_rtVfsChainElementCritSect);
380 if (RT_FAILURE(rc))
381 return rc;
382 }
383 else if (!g_rtVfsChainElementProviderList.pNext)
384 RTListInit(&g_rtVfsChainElementProviderList);
385
386 /*
387 * Duplicate name?
388 */
389 rc = VINF_SUCCESS;
390 PRTVFSCHAINELEMENTREG pIterator, pIterNext;
391 RTListForEachSafe(&g_rtVfsChainElementProviderList, pIterator, pIterNext, RTVFSCHAINELEMENTREG, ListEntry)
392 {
393 if (!strcmp(pIterator->pszName, pRegRec->pszName))
394 {
395 AssertMsgFailed(("duplicate name '%s' old=%p new=%p\n", pIterator->pszName, pIterator, pRegRec));
396 rc = VERR_ALREADY_EXISTS;
397 break;
398 }
399 }
400
401 /*
402 * If not, append the record to the list.
403 */
404 if (RT_SUCCESS(rc))
405 RTListAppend(&g_rtVfsChainElementProviderList, &pRegRec->ListEntry);
406
407 /*
408 * Leave the lock and return.
409 */
410 if (!fFromCtor)
411 RTCritSectRwLeaveExcl(&g_rtVfsChainElementCritSect);
412 return rc;
413}
414
415
416/**
417 * Allocates and initializes an empty spec
418 *
419 * @returns Pointer to the spec on success, NULL on failure.
420 */
421static PRTVFSCHAINSPEC rtVfsChainSpecAlloc(void)
422{
423 PRTVFSCHAINSPEC pSpec = (PRTVFSCHAINSPEC)RTMemTmpAlloc(sizeof(*pSpec));
424 if (pSpec)
425 {
426 pSpec->fOpenFile = 0;
427 pSpec->fOpenDir = 0;
428 pSpec->cElements = 0;
429 pSpec->paElements = NULL;
430 }
431 return pSpec;
432}
433
434
435/**
436 * Checks if @a ch is a character that can be escaped.
437 *
438 * @returns true / false.
439 * @param ch The character to consider.
440 */
441DECLINLINE(bool) rtVfsChainSpecIsEscapableChar(char ch)
442{
443 return ch == '('
444 || ch == ')'
445 || ch == '{'
446 || ch == '}'
447 || ch == '\\'
448 || ch == ','
449 || ch == '|'
450 || ch == ':';
451}
452
453
454/**
455 * Duplicate a spec string after unescaping it.
456 *
457 * This differs from RTStrDupN in that it uses RTMemTmpAlloc instead of
458 * RTMemAlloc.
459 *
460 * @returns String copy on success, NULL on failure.
461 * @param psz The string to duplicate.
462 * @param cch The number of bytes to duplicate.
463 * @param prc The status code variable to set on failure. (Leeps the
464 * code shorter. -lazy bird)
465 */
466DECLINLINE(char *) rtVfsChainSpecDupStrN(const char *psz, size_t cch, int *prc)
467{
468 char *pszCopy = (char *)RTMemTmpAlloc(cch + 1);
469 if (pszCopy)
470 {
471 if (!memchr(psz, '\\', cch))
472 {
473 /* Plain string, copy it raw. */
474 memcpy(pszCopy, psz, cch);
475 pszCopy[cch] = '\0';
476 }
477 else
478 {
479 /* Has escape sequences, must unescape it. */
480 char *pszDst = pszCopy;
481 while (cch-- > 0)
482 {
483 char ch = *psz++;
484 if (ch == '\\' && cch > 0)
485 {
486 char ch2 = *psz;
487 if (rtVfsChainSpecIsEscapableChar(ch2))
488 {
489 psz++;
490 cch--;
491 ch = ch2;
492 }
493 }
494 *pszDst++ = ch;
495 }
496 *pszDst = '\0';
497 }
498 }
499 else
500 *prc = VERR_NO_TMP_MEMORY;
501 return pszCopy;
502}
503
504
505/**
506 * Adds an empty element to the chain specification.
507 *
508 * The caller is responsible for filling it the element attributes.
509 *
510 * @returns Pointer to the new element on success, NULL on failure. The
511 * pointer is only valid till the next call to this function.
512 * @param pSpec The chain specification.
513 * @param prc The status code variable to set on failure. (Leeps the
514 * code shorter. -lazy bird)
515 */
516static PRTVFSCHAINELEMSPEC rtVfsChainSpecAddElement(PRTVFSCHAINSPEC pSpec, uint16_t offSpec, int *prc)
517{
518 AssertPtr(pSpec);
519
520 /*
521 * Resize the element table if necessary.
522 */
523 uint32_t const iElement = pSpec->cElements;
524 if ((iElement % 32) == 0)
525 {
526 PRTVFSCHAINELEMSPEC paNew = (PRTVFSCHAINELEMSPEC)RTMemTmpAlloc((iElement + 32) * sizeof(paNew[0]));
527 if (!paNew)
528 {
529 *prc = VERR_NO_TMP_MEMORY;
530 return NULL;
531 }
532
533 memcpy(paNew, pSpec->paElements, iElement * sizeof(paNew[0]));
534 RTMemTmpFree(pSpec->paElements);
535 pSpec->paElements = paNew;
536 }
537
538 /*
539 * Initialize and add the new element.
540 */
541 PRTVFSCHAINELEMSPEC pElement = &pSpec->paElements[iElement];
542 pElement->pszProvider = NULL;
543 pElement->enmTypeIn = iElement ? pSpec->paElements[iElement - 1].enmType : RTVFSOBJTYPE_INVALID;
544 pElement->enmType = RTVFSOBJTYPE_INVALID;
545 pElement->offSpec = offSpec;
546 pElement->cchSpec = 0;
547 pElement->cArgs = 0;
548 pElement->paArgs = NULL;
549 pElement->pProvider = NULL;
550 pElement->hVfsObj = NIL_RTVFSOBJ;
551
552 pSpec->cElements = iElement + 1;
553 return pElement;
554}
555
556
557/**
558 * Adds an argument to the element spec.
559 *
560 * @returns IPRT status code.
561 * @param pElement The element.
562 * @param psz The start of the argument string.
563 * @param cch The length of the argument string, escape
564 * sequences counted twice.
565 */
566static int rtVfsChainSpecElementAddArg(PRTVFSCHAINELEMSPEC pElement, const char *psz, size_t cch, uint16_t offSpec)
567{
568 uint32_t iArg = pElement->cArgs;
569 if ((iArg % 32) == 0)
570 {
571 PRTVFSCHAINELEMENTARG paNew = (PRTVFSCHAINELEMENTARG)RTMemTmpAlloc((iArg + 32) * sizeof(paNew[0]));
572 if (!paNew)
573 return VERR_NO_TMP_MEMORY;
574 memcpy(paNew, pElement->paArgs, iArg * sizeof(paNew[0]));
575 RTMemTmpFree(pElement->paArgs);
576 pElement->paArgs = paNew;
577 }
578
579 int rc = VINF_SUCCESS;
580 pElement->paArgs[iArg].psz = rtVfsChainSpecDupStrN(psz, cch, &rc);
581 pElement->paArgs[iArg].offSpec = offSpec;
582 pElement->cArgs = iArg + 1;
583 return rc;
584}
585
586
587RTDECL(void) RTVfsChainSpecFree(PRTVFSCHAINSPEC pSpec)
588{
589 if (!pSpec)
590 return;
591
592 uint32_t i = pSpec->cElements;
593 while (i-- > 0)
594 {
595 uint32_t iArg = pSpec->paElements[i].cArgs;
596 while (iArg-- > 0)
597 RTMemTmpFree(pSpec->paElements[i].paArgs[iArg].psz);
598 RTMemTmpFree(pSpec->paElements[i].paArgs);
599 RTMemTmpFree(pSpec->paElements[i].pszProvider);
600 if (pSpec->paElements[i].hVfsObj != NIL_RTVFSOBJ)
601 {
602 RTVfsObjRelease(pSpec->paElements[i].hVfsObj);
603 pSpec->paElements[i].hVfsObj = NIL_RTVFSOBJ;
604 }
605 }
606
607 RTMemTmpFree(pSpec->paElements);
608 pSpec->paElements = NULL;
609 RTMemTmpFree(pSpec);
610}
611
612
613/**
614 * Checks if @a psz is pointing to the final element specification.
615 *
616 * @returns true / false.
617 * @param psz Start of an element or path.
618 * @param pcch Where to return the length.
619 */
620static bool rtVfsChainSpecIsFinalElement(const char *psz, size_t *pcch)
621{
622 size_t off = 0;
623 char ch;
624 while ((ch = psz[off]) != '\0')
625 {
626 if (ch == '|' || ch == ':')
627 return false;
628 if ( ch == '\\'
629 && rtVfsChainSpecIsEscapableChar(psz[off + 1]))
630 off++;
631 off++;
632 }
633 *pcch = off;
634 return off > 0;
635}
636
637
638/**
639 * Makes the final path element.
640 * @returns IPRT status code
641 * @param pElement The element.
642 * @param pszPath The path.
643 * @param cchPath The path length.
644 */
645static int rtVfsChainSpecMakeFinalPathElement(PRTVFSCHAINELEMSPEC pElement, const char *pszPath, size_t cchPath)
646{
647 pElement->pszProvider = NULL;
648 pElement->enmType = RTVFSOBJTYPE_END;
649 pElement->cchSpec = (uint16_t)cchPath;
650 return rtVfsChainSpecElementAddArg(pElement, pszPath, cchPath, pElement->offSpec);
651}
652
653
654/**
655 * Finds the end of the argument string.
656 *
657 * @returns The offset of the end character relative to @a psz.
658 * @param psz The argument string.
659 */
660static size_t rtVfsChainSpecFindArgEnd(const char *psz)
661{
662 size_t off = 0;
663 char ch;
664 while ( (ch = psz[off]) != '\0'
665 && ch != ','
666 && ch != ')'
667 && ch != '}')
668 {
669 if ( ch == '\\'
670 && rtVfsChainSpecIsEscapableChar(psz[off+1]))
671 off++;
672 off++;
673 }
674 return off;
675}
676
677
678RTDECL(int) RTVfsChainSpecParse(const char *pszSpec, uint32_t fFlags, RTVFSOBJTYPE enmDesiredType,
679 PRTVFSCHAINSPEC *ppSpec, uint32_t *poffError)
680{
681 if (poffError)
682 {
683 AssertPtrReturn(poffError, VERR_INVALID_POINTER);
684 *poffError = 0;
685 }
686 AssertPtrReturn(ppSpec, VERR_INVALID_POINTER);
687 *ppSpec = NULL;
688 AssertPtrReturn(pszSpec, VERR_INVALID_POINTER);
689 AssertReturn(!(fFlags & ~RTVFSCHAIN_PF_VALID_MASK), VERR_INVALID_PARAMETER);
690 AssertReturn(enmDesiredType > RTVFSOBJTYPE_INVALID && enmDesiredType < RTVFSOBJTYPE_END, VERR_INVALID_PARAMETER);
691
692 /*
693 * Check the start of the specification and allocate an empty return spec.
694 */
695 if (strncmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX, sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1))
696 return VERR_VFS_CHAIN_NO_PREFIX;
697 const char *pszSrc = RTStrStripL(pszSpec + sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1);
698 if (!*pszSrc)
699 return VERR_VFS_CHAIN_EMPTY;
700
701 PRTVFSCHAINSPEC pSpec = rtVfsChainSpecAlloc();
702 if (!pSpec)
703 return VERR_NO_TMP_MEMORY;
704 pSpec->enmDesiredType = enmDesiredType;
705
706 /*
707 * Parse the spec one element at a time.
708 */
709 int rc = VINF_SUCCESS;
710 while (*pszSrc && RT_SUCCESS(rc))
711 {
712 /*
713 * Digest element separator, except for the first element.
714 */
715 if (*pszSrc == '|' || *pszSrc == ':')
716 {
717 if (pSpec->cElements != 0)
718 pszSrc = RTStrStripL(pszSrc + 1);
719 else
720 {
721 rc = VERR_VFS_CHAIN_LEADING_SEPARATOR;
722 break;
723 }
724 }
725 else if (pSpec->cElements != 0)
726 {
727 rc = VERR_VFS_CHAIN_EXPECTED_SEPARATOR;
728 break;
729 }
730
731 /*
732 * Ok, there should be an element here so add one to the return struct.
733 */
734 PRTVFSCHAINELEMSPEC pElement = rtVfsChainSpecAddElement(pSpec, (uint16_t)(pszSrc - pszSpec), &rc);
735 if (!pElement)
736 break;
737
738 /*
739 * First up is the VFS object type followed by a parenthesis/curly, or
740 * this could be the trailing action. Alternatively, we could have a
741 * final path-only element here.
742 */
743 size_t cch;
744 if (strncmp(pszSrc, "base", cch = 4) == 0)
745 pElement->enmType = RTVFSOBJTYPE_BASE;
746 else if (strncmp(pszSrc, "vfs", cch = 3) == 0)
747 pElement->enmType = RTVFSOBJTYPE_VFS;
748 else if (strncmp(pszSrc, "fss", cch = 3) == 0)
749 pElement->enmType = RTVFSOBJTYPE_FS_STREAM;
750 else if (strncmp(pszSrc, "ios", cch = 3) == 0)
751 pElement->enmType = RTVFSOBJTYPE_IO_STREAM;
752 else if (strncmp(pszSrc, "dir", cch = 3) == 0)
753 pElement->enmType = RTVFSOBJTYPE_DIR;
754 else if (strncmp(pszSrc, "file", cch = 4) == 0)
755 pElement->enmType = RTVFSOBJTYPE_FILE;
756 else if (strncmp(pszSrc, "sym", cch = 3) == 0)
757 pElement->enmType = RTVFSOBJTYPE_SYMLINK;
758 else
759 {
760 if (rtVfsChainSpecIsFinalElement(pszSrc, &cch))
761 rc = rtVfsChainSpecMakeFinalPathElement(pElement, pszSrc, cch);
762 else if (*pszSrc == '\0')
763 rc = VERR_VFS_CHAIN_TRAILING_SEPARATOR;
764 else
765 rc = VERR_VFS_CHAIN_UNKNOWN_TYPE;
766 break;
767 }
768
769 /* Check and skip past the parenthesis/curly. If not there, we might
770 have a final path element at our hands. */
771 char const chOpenParen = pszSrc[cch];
772 if (chOpenParen != '(' && chOpenParen != '{')
773 {
774 if (rtVfsChainSpecIsFinalElement(pszSrc, &cch))
775 rc = rtVfsChainSpecMakeFinalPathElement(pElement, pszSrc, cch);
776 else
777 rc = VERR_VFS_CHAIN_EXPECTED_LEFT_PARENTHESES;
778 break;
779 }
780 pszSrc = RTStrStripL(pszSrc + cch + 1);
781
782 /*
783 * The name of the element provider.
784 */
785 cch = rtVfsChainSpecFindArgEnd(pszSrc);
786 if (!cch)
787 {
788 rc = VERR_VFS_CHAIN_EXPECTED_PROVIDER_NAME;
789 break;
790 }
791 pElement->pszProvider = rtVfsChainSpecDupStrN(pszSrc, cch, &rc);
792 if (!pElement->pszProvider)
793 break;
794 pszSrc += cch;
795
796 /*
797 * The arguments.
798 */
799 while (*pszSrc == ',')
800 {
801 pszSrc = RTStrStripL(pszSrc + 1);
802 cch = rtVfsChainSpecFindArgEnd(pszSrc);
803 rc = rtVfsChainSpecElementAddArg(pElement, pszSrc, cch, (uint16_t)(pszSrc - pszSpec));
804 if (RT_FAILURE(rc))
805 break;
806 pszSrc += cch;
807 }
808 if (RT_FAILURE(rc))
809 break;
810
811 /* Must end with a right parentheses/curly. */
812 if (*pszSrc != (chOpenParen == '(' ? ')' : '}'))
813 {
814 rc = VERR_VFS_CHAIN_EXPECTED_RIGHT_PARENTHESES;
815 break;
816 }
817 pElement->cchSpec = (uint16_t)(pszSrc - pszSpec) - pElement->offSpec + 1;
818
819 pszSrc = RTStrStripL(pszSrc + 1);
820 }
821
822#if 0
823 /*
824 * Dump the chain. Useful for debugging the above code.
825 */
826 RTAssertMsg2("dbg: cElements=%d rc=%Rrc\n", pSpec->cElements, rc);
827 for (uint32_t i = 0; i < pSpec->cElements; i++)
828 {
829 uint32_t const cArgs = pSpec->paElements[i].cArgs;
830 RTAssertMsg2("dbg: #%u: enmTypeIn=%d enmType=%d cArgs=%d",
831 i, pSpec->paElements[i].enmTypeIn, pSpec->paElements[i].enmType, cArgs);
832 for (uint32_t j = 0; j < cArgs; j++)
833 RTAssertMsg2(j == 0 ? (cArgs > 1 ? " [%s" : " [%s]") : j + 1 < cArgs ? ", %s" : ", %s]",
834 pSpec->paElements[i].paArgs[j].psz);
835 RTAssertMsg2(" offSpec=%d cchSpec=%d", pSpec->paElements[i].offSpec, pSpec->paElements[i].cchSpec);
836 RTAssertMsg2(" spec: %.*s\n", pSpec->paElements[i].cchSpec, &pszSpec[pSpec->paElements[i].offSpec]);
837 }
838#endif
839
840 /*
841 * Return the chain on success; Cleanup and set the error indicator on
842 * failure.
843 */
844 if (RT_SUCCESS(rc))
845 *ppSpec = pSpec;
846 else
847 {
848 if (poffError)
849 *poffError = (uint32_t)(pszSrc - pszSpec);
850 RTVfsChainSpecFree(pSpec);
851 }
852 return rc;
853}
854
855
856/**
857 * Looks up @a pszProvider among the registered providers.
858 *
859 * @returns Pointer to registration record if found, NULL if not.
860 * @param pszProvider The provider.
861 */
862static PCRTVFSCHAINELEMENTREG rtVfsChainFindProviderLocked(const char *pszProvider)
863{
864 PCRTVFSCHAINELEMENTREG pIterator;
865 RTListForEach(&g_rtVfsChainElementProviderList, pIterator, RTVFSCHAINELEMENTREG, ListEntry)
866 {
867 if (strcmp(pIterator->pszName, pszProvider) == 0)
868 return pIterator;
869 }
870 return NULL;
871}
872
873
874/**
875 * Does reusable object type matching.
876 *
877 * @returns true if the types matches, false if not.
878 * @param pElement The target element specification.
879 * @param pReuseElement The source element specification.
880 */
881static bool rtVfsChainMatchReusableType(PRTVFSCHAINELEMSPEC pElement, PRTVFSCHAINELEMSPEC pReuseElement)
882{
883 if (pElement->enmType == pReuseElement->enmType)
884 return true;
885
886 /* File objects can always be cast to I/O streams. */
887 if ( pElement->enmType == RTVFSOBJTYPE_IO_STREAM
888 && pReuseElement->enmType == RTVFSOBJTYPE_FILE)
889 return true;
890
891 /* I/O stream objects may be file objects. */
892 if ( pElement->enmType == RTVFSOBJTYPE_FILE
893 && pReuseElement->enmType == RTVFSOBJTYPE_IO_STREAM)
894 {
895 RTVFSFILE hVfsFile = RTVfsObjToFile(pReuseElement->hVfsObj);
896 if (hVfsFile != NIL_RTVFSFILE)
897 {
898 RTVfsFileRelease(hVfsFile);
899 return true;
900 }
901 }
902 return false;
903}
904
905
906RTDECL(int) RTVfsChainSpecCheckAndSetup(PRTVFSCHAINSPEC pSpec, PCRTVFSCHAINSPEC pReuseSpec,
907 PRTVFSOBJ phVfsObj, const char **ppszFinalPath, uint32_t *poffError, PRTERRINFO pErrInfo)
908{
909 AssertPtrReturn(poffError, VERR_INVALID_POINTER);
910 *poffError = 0;
911 AssertPtrReturn(phVfsObj, VERR_INVALID_POINTER);
912 *phVfsObj = NIL_RTVFSOBJ;
913 AssertPtrReturn(ppszFinalPath, VERR_INVALID_POINTER);
914 *ppszFinalPath = NULL;
915 AssertPtrReturn(pSpec, VERR_INVALID_POINTER);
916 AssertPtrNullReturn(pErrInfo, VERR_INVALID_POINTER);
917
918 /*
919 * Check for final path-only component as we will not touch it yet.
920 */
921 uint32_t cElements = pSpec->cElements;
922 if (cElements > 0)
923 {
924 if (pSpec->paElements[pSpec->cElements - 1].enmType == RTVFSOBJTYPE_END)
925 {
926 if (cElements > 1)
927 cElements--;
928 else
929 {
930 *ppszFinalPath = pSpec->paElements[0].paArgs[0].psz;
931 return VERR_VFS_CHAIN_PATH_ONLY;
932 }
933 }
934 }
935 else
936 return VERR_VFS_CHAIN_EMPTY;
937
938 /*
939 * Enter the critical section after making sure it has been initialized.
940 */
941 int rc = RTOnce(&g_rtVfsChainElementInitOnce, rtVfsChainElementRegisterInit, NULL);
942 if (RT_SUCCESS(rc))
943 rc = RTCritSectRwEnterShared(&g_rtVfsChainElementCritSect);
944 if (RT_SUCCESS(rc))
945 {
946 /*
947 * Resolve and check each element first.
948 */
949 for (uint32_t i = 0; i < cElements; i++)
950 {
951 PRTVFSCHAINELEMSPEC const pElement = &pSpec->paElements[i];
952 *poffError = pElement->offSpec;
953 pElement->pProvider = rtVfsChainFindProviderLocked(pElement->pszProvider);
954 if (pElement->pProvider)
955 {
956 rc = pElement->pProvider->pfnValidate(pElement->pProvider, pSpec, pElement, poffError, pErrInfo);
957 if (RT_SUCCESS(rc))
958 continue;
959 }
960 else
961 rc = VERR_VFS_CHAIN_PROVIDER_NOT_FOUND;
962 break;
963 }
964
965 /*
966 * Check that the desired type is compatible with the last element.
967 */
968 if (RT_SUCCESS(rc))
969 {
970 PRTVFSCHAINELEMSPEC const pLast = &pSpec->paElements[cElements - 1];
971 if (cElements == pSpec->cElements)
972 {
973 if ( pLast->enmType == pSpec->enmDesiredType
974 || pSpec->enmDesiredType == RTVFSOBJTYPE_BASE
975 || ( pLast->enmType == RTVFSOBJTYPE_FILE
976 && pSpec->enmDesiredType == RTVFSOBJTYPE_IO_STREAM) )
977 rc = VINF_SUCCESS;
978 else
979 {
980 *poffError = pLast->offSpec;
981 rc = VERR_VFS_CHAIN_FINAL_TYPE_MISMATCH;
982 }
983 }
984 /* Ends with a path-only element, so check the type of the element preceding it. */
985 else if ( pLast->enmType == RTVFSOBJTYPE_DIR
986 || pLast->enmType == RTVFSOBJTYPE_VFS
987 || pLast->enmType == RTVFSOBJTYPE_FS_STREAM)
988 rc = VINF_SUCCESS;
989 else
990 {
991 *poffError = pLast->offSpec;
992 rc = VERR_VFS_CHAIN_TYPE_MISMATCH_PATH_ONLY;
993 }
994 }
995
996 if (RT_SUCCESS(rc))
997 {
998 /*
999 * Try construct the chain.
1000 */
1001 RTVFSOBJ hPrevVfsObj = NIL_RTVFSOBJ; /* No extra reference, kept in chain structure. */
1002 for (uint32_t i = 0; i < cElements; i++)
1003 {
1004 PRTVFSCHAINELEMSPEC const pElement = &pSpec->paElements[i];
1005 *poffError = pElement->offSpec;
1006
1007 /*
1008 * Try reuse the VFS objects at the start of the passed in reuse chain.
1009 */
1010 if (!pReuseSpec)
1011 { /* likely */ }
1012 else
1013 {
1014 if (i < pReuseSpec->cElements)
1015 {
1016 PRTVFSCHAINELEMSPEC const pReuseElement = &pReuseSpec->paElements[i];
1017 if (pReuseElement->hVfsObj != NIL_RTVFSOBJ)
1018 {
1019 if (strcmp(pElement->pszProvider, pReuseElement->pszProvider) == 0)
1020 {
1021 if (rtVfsChainMatchReusableType(pElement, pReuseElement))
1022 {
1023 if (pElement->pProvider->pfnCanReuseElement(pElement->pProvider, pSpec, pElement,
1024 pReuseSpec, pReuseElement))
1025 {
1026 uint32_t cRefs = RTVfsObjRetain(pReuseElement->hVfsObj);
1027 if (cRefs != UINT32_MAX)
1028 {
1029 pElement->hVfsObj = hPrevVfsObj = pReuseElement->hVfsObj;
1030 continue;
1031 }
1032 }
1033 }
1034 }
1035 }
1036 }
1037 pReuseSpec = NULL;
1038 }
1039
1040 /*
1041 * Instantiate a new VFS object.
1042 */
1043 RTVFSOBJ hVfsObj = NIL_RTVFSOBJ;
1044 rc = pElement->pProvider->pfnInstantiate(pElement->pProvider, pSpec, pElement, hPrevVfsObj,
1045 &hVfsObj, poffError, pErrInfo);
1046 if (RT_FAILURE(rc))
1047 break;
1048 pElement->hVfsObj = hVfsObj;
1049 hPrevVfsObj = hVfsObj;
1050 }
1051
1052 /*
1053 * Add another reference to the final object and return.
1054 */
1055 if (RT_SUCCESS(rc))
1056 {
1057 uint32_t cRefs = RTVfsObjRetain(hPrevVfsObj);
1058 AssertStmt(cRefs != UINT32_MAX, rc = VERR_VFS_CHAIN_IPE);
1059 *phVfsObj = hPrevVfsObj;
1060 *ppszFinalPath = cElements == pSpec->cElements ? NULL : pSpec->paElements[cElements].paArgs[0].psz;
1061 }
1062 }
1063
1064 int rc2 = RTCritSectRwLeaveShared(&g_rtVfsChainElementCritSect);
1065 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
1066 rc = rc2;
1067 }
1068 return rc;
1069}
1070
1071
1072RTDECL(int) RTVfsChainElementDeregisterProvider(PRTVFSCHAINELEMENTREG pRegRec, bool fFromDtor)
1073{
1074 /*
1075 * Fend off wildlife.
1076 */
1077 if (pRegRec == NULL)
1078 return VINF_SUCCESS;
1079 AssertPtrReturn(pRegRec, VERR_INVALID_POINTER);
1080 AssertMsgReturn(pRegRec->uVersion == RTVFSCHAINELEMENTREG_VERSION, ("%#x", pRegRec->uVersion), VERR_INVALID_POINTER);
1081 AssertMsgReturn(pRegRec->uEndMarker == RTVFSCHAINELEMENTREG_VERSION, ("%#zx", pRegRec->uEndMarker), VERR_INVALID_POINTER);
1082 AssertPtrReturn(pRegRec->pszName, VERR_INVALID_POINTER);
1083
1084 /*
1085 * Take the lock if that's safe.
1086 */
1087 if (!fFromDtor)
1088 RTCritSectRwEnterExcl(&g_rtVfsChainElementCritSect);
1089 else if (!g_rtVfsChainElementProviderList.pNext)
1090 RTListInit(&g_rtVfsChainElementProviderList);
1091
1092 /*
1093 * Ok, remove it.
1094 */
1095 int rc = VERR_NOT_FOUND;
1096 PRTVFSCHAINELEMENTREG pIterator, pIterNext;
1097 RTListForEachSafe(&g_rtVfsChainElementProviderList, pIterator, pIterNext, RTVFSCHAINELEMENTREG, ListEntry)
1098 {
1099 if (pIterator == pRegRec)
1100 {
1101 RTListNodeRemove(&pRegRec->ListEntry);
1102 rc = VINF_SUCCESS;
1103 break;
1104 }
1105 }
1106
1107 /*
1108 * Leave the lock and return.
1109 */
1110 if (!fFromDtor)
1111 RTCritSectRwLeaveExcl(&g_rtVfsChainElementCritSect);
1112 return rc;
1113}
1114
1115
1116RTDECL(int) RTVfsChainOpenDir(const char *pszSpec, uint32_t fOpen,
1117 PRTVFSDIR phVfsDir, uint32_t *poffError, PRTERRINFO pErrInfo)
1118{
1119 uint32_t offErrorIgn;
1120 if (!poffError)
1121 poffError = &offErrorIgn;
1122 *poffError = 0;
1123 AssertPtrReturn(pszSpec, VERR_INVALID_POINTER);
1124 AssertReturn(*pszSpec != '\0', VERR_INVALID_PARAMETER);
1125 AssertPtrReturn(phVfsDir, VERR_INVALID_POINTER);
1126 AssertPtrNullReturn(pErrInfo, VERR_INVALID_POINTER);
1127
1128 /*
1129 * Try for a VFS chain first, falling back on regular file system stuff if it's just a path.
1130 */
1131 int rc;
1132 PRTVFSCHAINSPEC pSpec = NULL;
1133 if (strncmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX, sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1) == 0)
1134 {
1135 rc = RTVfsChainSpecParse(pszSpec, 0 /*fFlags*/, RTVFSOBJTYPE_DIR, &pSpec, poffError);
1136 if (RT_FAILURE(rc))
1137 return rc;
1138
1139 Assert(pSpec->cElements > 0);
1140 if ( pSpec->cElements > 1
1141 || pSpec->paElements[0].enmType != RTVFSOBJTYPE_END)
1142 {
1143
1144 const char *pszFinal = NULL;
1145 RTVFSOBJ hVfsObj = NIL_RTVFSOBJ;
1146 pSpec->fOpenFile = fOpen;
1147 rc = RTVfsChainSpecCheckAndSetup(pSpec, NULL /*pReuseSpec*/, &hVfsObj, &pszFinal, poffError, pErrInfo);
1148 if (RT_SUCCESS(rc))
1149 {
1150 if (!pszFinal)
1151 {
1152 /* Try convert it to a file object and we're done. */
1153 *phVfsDir = RTVfsObjToDir(hVfsObj);
1154 if (*phVfsDir)
1155 rc = VINF_SUCCESS;
1156 else
1157 rc = VERR_VFS_CHAIN_CAST_FAILED;
1158 }
1159 else
1160 {
1161 /*
1162 * Do a file open with the final path on the returned object.
1163 */
1164 RTVFS hVfs = RTVfsObjToVfs(hVfsObj);
1165 RTVFSDIR hVfsDir = RTVfsObjToDir(hVfsObj);
1166 RTVFSFSSTREAM hVfsFss = RTVfsObjToFsStream(hVfsObj);
1167 if (hVfs != NIL_RTVFS)
1168 rc = RTVfsDirOpen(hVfs, pszFinal, fOpen, phVfsDir);
1169 else if (hVfsDir != NIL_RTVFSDIR)
1170 rc = RTVfsDirOpenDir(hVfsDir, pszFinal, fOpen, phVfsDir);
1171 else if (hVfsFss != NIL_RTVFSFSSTREAM)
1172 rc = VERR_NOT_IMPLEMENTED;
1173 else
1174 rc = VERR_VFS_CHAIN_TYPE_MISMATCH_PATH_ONLY;
1175 RTVfsRelease(hVfs);
1176 RTVfsDirRelease(hVfsDir);
1177 RTVfsFsStrmRelease(hVfsFss);
1178 }
1179 RTVfsObjRelease(hVfsObj);
1180 }
1181
1182 RTVfsChainSpecFree(pSpec);
1183 return rc;
1184 }
1185
1186 /* Only a path element. */
1187 pszSpec = pSpec->paElements[0].paArgs[0].psz;
1188 }
1189
1190 /*
1191 * Path to regular file system.
1192 */
1193 rc = RTVfsDirOpenNormal(pszSpec, fOpen, phVfsDir);
1194
1195 RTVfsChainSpecFree(pSpec);
1196 return rc;
1197
1198}
1199
1200
1201RTDECL(int) RTVfsChainOpenParentDir(const char *pszSpec, uint32_t fOpen, PRTVFSDIR phVfsDir, const char **ppszChild,
1202 uint32_t *poffError, PRTERRINFO pErrInfo)
1203{
1204 uint32_t offErrorIgn;
1205 if (!poffError)
1206 poffError = &offErrorIgn;
1207 *poffError = 0;
1208 AssertPtrReturn(pszSpec, VERR_INVALID_POINTER);
1209 AssertReturn(*pszSpec != '\0', VERR_INVALID_PARAMETER);
1210 AssertPtrReturn(phVfsDir, VERR_INVALID_POINTER);
1211 AssertPtrReturn(ppszChild, VERR_INVALID_POINTER);
1212 *ppszChild = NULL;
1213 AssertPtrNullReturn(pErrInfo, VERR_INVALID_POINTER);
1214
1215 /*
1216 * Process the spec from the end, trying to find the child part of it.
1217 * We cannot use RTPathFilename here because we must ignore trailing slashes.
1218 */
1219 const char * const pszEnd = RTStrEnd(pszSpec, RTSTR_MAX);
1220 const char *pszChild = pszEnd;
1221 while ( pszChild != pszSpec
1222 && RTPATH_IS_SLASH(pszChild[-1]))
1223 pszChild--;
1224 while ( pszChild != pszSpec
1225 && !RTPATH_IS_SLASH(pszChild[-1])
1226 && !RTPATH_IS_VOLSEP(pszChild[-1]))
1227 pszChild--;
1228 size_t const cchChild = pszEnd - pszChild;
1229 *ppszChild = pszChild;
1230
1231 /*
1232 * Try for a VFS chain first, falling back on regular file system stuff if it's just a path.
1233 */
1234 int rc;
1235 PRTVFSCHAINSPEC pSpec = NULL;
1236 if (strncmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX, sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1) == 0)
1237 {
1238 rc = RTVfsChainSpecParse(pszSpec, 0 /*fFlags*/, RTVFSOBJTYPE_DIR, &pSpec, poffError);
1239 if (RT_FAILURE(rc))
1240 return rc;
1241
1242 Assert(pSpec->cElements > 0);
1243 if ( pSpec->cElements > 1
1244 || pSpec->paElements[0].enmType != RTVFSOBJTYPE_END)
1245 {
1246 /*
1247 * Check that it ends with a path-only element and that this in turn ends with
1248 * what pszChild points to. (We cannot easiy figure out the parent part of
1249 * an element that isn't path-only, so we don't bother trying try.)
1250 */
1251 PRTVFSCHAINELEMSPEC pLast = &pSpec->paElements[pSpec->cElements - 1];
1252 if (pLast->pszProvider == NULL)
1253 {
1254 size_t cchFinal = strlen(pLast->paArgs[0].psz);
1255 if ( cchFinal >= cchChild
1256 && memcmp(&pLast->paArgs[0].psz[cchFinal - cchChild], pszChild, cchChild + 1) == 0)
1257 {
1258 /*
1259 * Drop the child part so we have a path to the parent, then setup the chain.
1260 */
1261 if (cchFinal > cchChild)
1262 pLast->paArgs[0].psz[cchFinal - cchChild] = '\0';
1263 else
1264 pSpec->cElements--;
1265
1266 const char *pszFinal = NULL;
1267 RTVFSOBJ hVfsObj = NIL_RTVFSOBJ;
1268 pSpec->fOpenFile = fOpen;
1269 rc = RTVfsChainSpecCheckAndSetup(pSpec, NULL /*pReuseSpec*/, &hVfsObj, &pszFinal, poffError, pErrInfo);
1270 if (RT_SUCCESS(rc))
1271 {
1272 if (!pszFinal)
1273 {
1274 Assert(cchFinal == cchChild);
1275
1276 /* Try convert it to a file object and we're done. */
1277 *phVfsDir = RTVfsObjToDir(hVfsObj);
1278 if (*phVfsDir)
1279 rc = VINF_SUCCESS;
1280 else
1281 rc = VERR_VFS_CHAIN_CAST_FAILED;
1282 }
1283 else
1284 {
1285 /*
1286 * Do a file open with the final path on the returned object.
1287 */
1288 RTVFS hVfs = RTVfsObjToVfs(hVfsObj);
1289 RTVFSDIR hVfsDir = RTVfsObjToDir(hVfsObj);
1290 RTVFSFSSTREAM hVfsFss = RTVfsObjToFsStream(hVfsObj);
1291 if (hVfs != NIL_RTVFS)
1292 rc = RTVfsDirOpen(hVfs, pszFinal, fOpen, phVfsDir);
1293 else if (hVfsDir != NIL_RTVFSDIR)
1294 rc = RTVfsDirOpenDir(hVfsDir, pszFinal, fOpen, phVfsDir);
1295 else if (hVfsFss != NIL_RTVFSFSSTREAM)
1296 rc = VERR_NOT_IMPLEMENTED;
1297 else
1298 rc = VERR_VFS_CHAIN_TYPE_MISMATCH_PATH_ONLY;
1299 RTVfsRelease(hVfs);
1300 RTVfsDirRelease(hVfsDir);
1301 RTVfsFsStrmRelease(hVfsFss);
1302 }
1303 RTVfsObjRelease(hVfsObj);
1304 }
1305 }
1306 else
1307 rc = VERR_VFS_CHAIN_TOO_SHORT_FOR_PARENT;
1308 }
1309 else
1310 rc = VERR_VFS_CHAIN_NOT_PATH_ONLY;
1311
1312 RTVfsChainSpecFree(pSpec);
1313 return rc;
1314 }
1315
1316 /* Only a path element. */
1317 pszSpec = pSpec->paElements[0].paArgs[0].psz;
1318 }
1319
1320 /*
1321 * Path to regular file system.
1322 */
1323 if (RTPathHasPath(pszSpec))
1324 {
1325 char *pszCopy = RTStrDup(pszSpec);
1326 if (pszCopy)
1327 {
1328 RTPathStripFilename(pszCopy);
1329 rc = RTVfsDirOpenNormal(pszCopy, fOpen, phVfsDir);
1330 RTStrFree(pszCopy);
1331 }
1332 else
1333 rc = VERR_NO_STR_MEMORY;
1334 }
1335 else
1336 rc = RTVfsDirOpenNormal(".", fOpen, phVfsDir);
1337
1338 RTVfsChainSpecFree(pSpec);
1339 return rc;
1340
1341}
1342
1343
1344RTDECL(int) RTVfsChainOpenFile(const char *pszSpec, uint64_t fOpen,
1345 PRTVFSFILE phVfsFile, uint32_t *poffError, PRTERRINFO pErrInfo)
1346{
1347 uint32_t offErrorIgn;
1348 if (!poffError)
1349 poffError = &offErrorIgn;
1350 *poffError = 0;
1351 AssertPtrReturn(pszSpec, VERR_INVALID_POINTER);
1352 AssertReturn(*pszSpec != '\0', VERR_INVALID_PARAMETER);
1353 AssertPtrReturn(phVfsFile, VERR_INVALID_POINTER);
1354 AssertPtrNullReturn(pErrInfo, VERR_INVALID_POINTER);
1355
1356 /*
1357 * Try for a VFS chain first, falling back on regular file system stuff if it's just a path.
1358 */
1359 int rc;
1360 PRTVFSCHAINSPEC pSpec = NULL;
1361 if (strncmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX, sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1) == 0)
1362 {
1363 rc = RTVfsChainSpecParse(pszSpec, 0 /*fFlags*/, RTVFSOBJTYPE_FILE, &pSpec, poffError);
1364 if (RT_FAILURE(rc))
1365 return rc;
1366
1367 Assert(pSpec->cElements > 0);
1368 if ( pSpec->cElements > 1
1369 || pSpec->paElements[0].enmType != RTVFSOBJTYPE_END)
1370 {
1371 const char *pszFinal = NULL;
1372 RTVFSOBJ hVfsObj = NIL_RTVFSOBJ;
1373 pSpec->fOpenFile = fOpen;
1374 rc = RTVfsChainSpecCheckAndSetup(pSpec, NULL /*pReuseSpec*/, &hVfsObj, &pszFinal, poffError, pErrInfo);
1375 if (RT_SUCCESS(rc))
1376 {
1377 if (!pszFinal)
1378 {
1379 /* Try convert it to a file object and we're done. */
1380 *phVfsFile = RTVfsObjToFile(hVfsObj);
1381 if (*phVfsFile)
1382 rc = VINF_SUCCESS;
1383 else
1384 rc = VERR_VFS_CHAIN_CAST_FAILED;
1385 }
1386 else
1387 {
1388 /*
1389 * Do a file open with the final path on the returned object.
1390 */
1391 RTVFS hVfs = RTVfsObjToVfs(hVfsObj);
1392 RTVFSDIR hVfsDir = RTVfsObjToDir(hVfsObj);
1393 RTVFSFSSTREAM hVfsFss = RTVfsObjToFsStream(hVfsObj);
1394 if (hVfs != NIL_RTVFS)
1395 rc = RTVfsFileOpen(hVfs, pszFinal, fOpen, phVfsFile);
1396 else if (hVfsDir != NIL_RTVFSDIR)
1397 rc = RTVfsDirOpenFile(hVfsDir, pszFinal, fOpen, phVfsFile);
1398 else if (hVfsFss != NIL_RTVFSFSSTREAM)
1399 rc = VERR_NOT_IMPLEMENTED;
1400 else
1401 rc = VERR_VFS_CHAIN_TYPE_MISMATCH_PATH_ONLY;
1402 RTVfsRelease(hVfs);
1403 RTVfsDirRelease(hVfsDir);
1404 RTVfsFsStrmRelease(hVfsFss);
1405 }
1406 RTVfsObjRelease(hVfsObj);
1407 }
1408
1409 RTVfsChainSpecFree(pSpec);
1410 return rc;
1411 }
1412
1413 /* Only a path element. */
1414 pszSpec = pSpec->paElements[0].paArgs[0].psz;
1415 }
1416
1417 /*
1418 * Path to regular file system.
1419 */
1420 RTFILE hFile;
1421 rc = RTFileOpen(&hFile, pszSpec, fOpen);
1422 if (RT_SUCCESS(rc))
1423 {
1424 RTVFSFILE hVfsFile;
1425 rc = RTVfsFileFromRTFile(hFile, fOpen, false /*fLeaveOpen*/, &hVfsFile);
1426 if (RT_SUCCESS(rc))
1427 *phVfsFile = hVfsFile;
1428 else
1429 RTFileClose(hFile);
1430 }
1431
1432 RTVfsChainSpecFree(pSpec);
1433 return rc;
1434}
1435
1436
1437RTDECL(int) RTVfsChainOpenIoStream(const char *pszSpec, uint64_t fOpen,
1438 PRTVFSIOSTREAM phVfsIos, uint32_t *poffError, PRTERRINFO pErrInfo)
1439{
1440 uint32_t offErrorIgn;
1441 if (!poffError)
1442 poffError = &offErrorIgn;
1443 *poffError = 0;
1444 AssertPtrReturn(pszSpec, VERR_INVALID_POINTER);
1445 AssertReturn(*pszSpec != '\0', VERR_INVALID_PARAMETER);
1446 AssertPtrReturn(phVfsIos, VERR_INVALID_POINTER);
1447 AssertPtrNullReturn(pErrInfo, VERR_INVALID_POINTER);
1448
1449 /*
1450 * Try for a VFS chain first, falling back on regular file system stuff if it's just a path.
1451 */
1452 int rc;
1453 PRTVFSCHAINSPEC pSpec = NULL;
1454 if (strncmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX, sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1) == 0)
1455 {
1456 rc = RTVfsChainSpecParse(pszSpec, 0 /*fFlags*/, RTVFSOBJTYPE_IO_STREAM, &pSpec, poffError);
1457 if (RT_FAILURE(rc))
1458 return rc;
1459
1460 Assert(pSpec->cElements > 0);
1461 if ( pSpec->cElements > 1
1462 || pSpec->paElements[0].enmType != RTVFSOBJTYPE_END)
1463 {
1464 const char *pszFinal = NULL;
1465 RTVFSOBJ hVfsObj = NIL_RTVFSOBJ;
1466 pSpec->fOpenFile = fOpen;
1467 rc = RTVfsChainSpecCheckAndSetup(pSpec, NULL /*pReuseSpec*/, &hVfsObj, &pszFinal, poffError, pErrInfo);
1468 if (RT_SUCCESS(rc))
1469 {
1470 if (!pszFinal)
1471 {
1472 /* Try convert it to an I/O object and we're done. */
1473 *phVfsIos = RTVfsObjToIoStream(hVfsObj);
1474 if (*phVfsIos)
1475 rc = VINF_SUCCESS;
1476 else
1477 rc = VERR_VFS_CHAIN_CAST_FAILED;
1478 }
1479 else
1480 {
1481 /*
1482 * Do a file open with the final path on the returned object.
1483 */
1484 RTVFS hVfs = RTVfsObjToVfs(hVfsObj);
1485 RTVFSDIR hVfsDir = RTVfsObjToDir(hVfsObj);
1486 RTVFSFSSTREAM hVfsFss = RTVfsObjToFsStream(hVfsObj);
1487 RTVFSFILE hVfsFile = NIL_RTVFSFILE;
1488 if (hVfs != NIL_RTVFS)
1489 rc = RTVfsFileOpen(hVfs, pszFinal, fOpen, &hVfsFile);
1490 else if (hVfsDir != NIL_RTVFSDIR)
1491 rc = RTVfsDirOpenFile(hVfsDir, pszFinal, fOpen, &hVfsFile);
1492 else if (hVfsFss != NIL_RTVFSFSSTREAM)
1493 rc = VERR_NOT_IMPLEMENTED;
1494 else
1495 rc = VERR_VFS_CHAIN_TYPE_MISMATCH_PATH_ONLY;
1496 if (RT_SUCCESS(rc))
1497 {
1498 *phVfsIos = RTVfsFileToIoStream(hVfsFile);
1499 if (*phVfsIos)
1500 rc = VINF_SUCCESS;
1501 else
1502 rc = VERR_VFS_CHAIN_CAST_FAILED;
1503 RTVfsFileRelease(hVfsFile);
1504 }
1505 RTVfsRelease(hVfs);
1506 RTVfsDirRelease(hVfsDir);
1507 RTVfsFsStrmRelease(hVfsFss);
1508 }
1509 RTVfsObjRelease(hVfsObj);
1510 }
1511
1512 RTVfsChainSpecFree(pSpec);
1513 return rc;
1514 }
1515
1516 /* Only a path element. */
1517 pszSpec = pSpec->paElements[0].paArgs[0].psz;
1518 }
1519
1520 /*
1521 * Path to regular file system.
1522 */
1523 RTFILE hFile;
1524 rc = RTFileOpen(&hFile, pszSpec, fOpen);
1525 if (RT_SUCCESS(rc))
1526 {
1527 RTVFSFILE hVfsFile;
1528 rc = RTVfsFileFromRTFile(hFile, fOpen, false /*fLeaveOpen*/, &hVfsFile);
1529 if (RT_SUCCESS(rc))
1530 {
1531 *phVfsIos = RTVfsFileToIoStream(hVfsFile);
1532 RTVfsFileRelease(hVfsFile);
1533 }
1534 else
1535 RTFileClose(hFile);
1536 }
1537
1538 RTVfsChainSpecFree(pSpec);
1539 return rc;
1540}
1541
1542
1543/**
1544 * The equivalent of RTPathQueryInfoEx
1545 */
1546RTDECL(int) RTVfsChainQueryInfo(const char *pszSpec, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs,
1547 uint32_t fFlags, uint32_t *poffError, PRTERRINFO pErrInfo)
1548{
1549 uint32_t offErrorIgn;
1550 if (!poffError)
1551 poffError = &offErrorIgn;
1552 *poffError = 0;
1553 AssertPtrReturn(pszSpec, VERR_INVALID_POINTER);
1554 AssertReturn(*pszSpec != '\0', VERR_INVALID_PARAMETER);
1555 AssertPtrReturn(pObjInfo, VERR_INVALID_POINTER);
1556 AssertReturn(enmAdditionalAttribs >= RTFSOBJATTRADD_NOTHING && enmAdditionalAttribs <= RTFSOBJATTRADD_LAST,
1557 VERR_INVALID_PARAMETER);
1558 AssertPtrNullReturn(pErrInfo, VERR_INVALID_POINTER);
1559
1560 /*
1561 * Try for a VFS chain first, falling back on regular file system stuff if it's just a path.
1562 */
1563 int rc;
1564 PRTVFSCHAINSPEC pSpec = NULL;
1565 if (strncmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX, sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1) == 0)
1566 {
1567 rc = RTVfsChainSpecParse(pszSpec, 0 /*fFlags*/, RTVFSOBJTYPE_BASE, &pSpec, poffError);
1568 if (RT_FAILURE(rc))
1569 return rc;
1570
1571 Assert(pSpec->cElements > 0);
1572 if ( pSpec->cElements > 1
1573 || pSpec->paElements[0].enmType != RTVFSOBJTYPE_END)
1574 {
1575 const char *pszFinal = NULL;
1576 RTVFSOBJ hVfsObj = NIL_RTVFSOBJ;
1577 pSpec->fOpenFile = RTFILE_O_READ | RTFILE_O_OPEN;
1578 rc = RTVfsChainSpecCheckAndSetup(pSpec, NULL /*pReuseSpec*/, &hVfsObj, &pszFinal, poffError, pErrInfo);
1579 if (RT_SUCCESS(rc))
1580 {
1581 if (!pszFinal)
1582 {
1583 /*
1584 * Do the job on the final object.
1585 */
1586 rc = RTVfsObjQueryInfo(hVfsObj, pObjInfo, enmAdditionalAttribs);
1587 }
1588 else
1589 {
1590 /*
1591 * Do a path query operation on the penultimate object.
1592 */
1593 RTVFS hVfs = RTVfsObjToVfs(hVfsObj);
1594 RTVFSDIR hVfsDir = RTVfsObjToDir(hVfsObj);
1595 RTVFSFSSTREAM hVfsFss = RTVfsObjToFsStream(hVfsObj);
1596 if (hVfs != NIL_RTVFS)
1597 rc = RTVfsQueryPathInfo(hVfs, pszFinal, pObjInfo, enmAdditionalAttribs, fFlags);
1598 else if (hVfsDir != NIL_RTVFSDIR)
1599 rc = RTVfsDirQueryPathInfo(hVfsDir, pszFinal, pObjInfo, enmAdditionalAttribs, fFlags);
1600 else if (hVfsFss != NIL_RTVFSFSSTREAM)
1601 rc = VERR_NOT_SUPPORTED;
1602 else
1603 rc = VERR_VFS_CHAIN_TYPE_MISMATCH_PATH_ONLY;
1604 RTVfsRelease(hVfs);
1605 RTVfsDirRelease(hVfsDir);
1606 RTVfsFsStrmRelease(hVfsFss);
1607 }
1608 RTVfsObjRelease(hVfsObj);
1609 }
1610
1611 RTVfsChainSpecFree(pSpec);
1612 return rc;
1613 }
1614
1615 /* Only a path element. */
1616 pszSpec = pSpec->paElements[0].paArgs[0].psz;
1617 }
1618
1619 /*
1620 * Path to regular file system.
1621 */
1622 rc = RTPathQueryInfoEx(pszSpec, pObjInfo, enmAdditionalAttribs, fFlags);
1623
1624 RTVfsChainSpecFree(pSpec);
1625 return rc;
1626}
1627
1628
1629RTDECL(bool) RTVfsChainIsSpec(const char *pszSpec)
1630{
1631 return pszSpec
1632 && strcmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX) == 0;
1633}
1634
1635
1636RTDECL(int) RTVfsChainQueryFinalPath(const char *pszSpec, char **ppszFinalPath, uint32_t *poffError)
1637{
1638 /* Make sure we've got an error info variable. */
1639 uint32_t offErrorIgn;
1640 if (!poffError)
1641 poffError = &offErrorIgn;
1642 *poffError = 0;
1643
1644 /*
1645 * If not chain specifier, just duplicate the input and return.
1646 */
1647 if (strncmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX, sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1) != 0)
1648 return RTStrDupEx(ppszFinalPath, pszSpec);
1649
1650 /*
1651 * Parse it and check out the last element.
1652 */
1653 PRTVFSCHAINSPEC pSpec = NULL;
1654 int rc = RTVfsChainSpecParse(pszSpec, 0 /*fFlags*/, RTVFSOBJTYPE_BASE, &pSpec, poffError);
1655 if (RT_SUCCESS(rc))
1656 {
1657 PCRTVFSCHAINELEMSPEC pLast = &pSpec->paElements[pSpec->cElements - 1];
1658 if (pLast->pszProvider == NULL)
1659 rc = RTStrDupEx(ppszFinalPath, pLast->paArgs[0].psz);
1660 else
1661 {
1662 rc = VERR_VFS_CHAIN_NOT_PATH_ONLY;
1663 *poffError = pLast->offSpec;
1664 }
1665 RTVfsChainSpecFree(pSpec);
1666 }
1667 return rc;
1668}
1669
1670
1671RTDECL(int) RTVfsChainSplitOffFinalPath(char *pszSpec, char **ppszSpec, char **ppszFinalPath, uint32_t *poffError)
1672{
1673 /* Make sure we've got an error info variable. */
1674 uint32_t offErrorIgn;
1675 if (!poffError)
1676 poffError = &offErrorIgn;
1677 *poffError = 0;
1678
1679 /*
1680 * If not chain specifier, just duplicate the input and return.
1681 */
1682 if (strncmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX, sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1) != 0)
1683 {
1684 *ppszSpec = NULL;
1685 *ppszFinalPath = pszSpec;
1686 return VINF_SUCCESS;
1687 }
1688
1689 /*
1690 * Parse it and check out the last element.
1691 */
1692 PRTVFSCHAINSPEC pSpec = NULL;
1693 int rc = RTVfsChainSpecParse(pszSpec, 0 /*fFlags*/, RTVFSOBJTYPE_BASE, &pSpec, poffError);
1694 if (RT_SUCCESS(rc))
1695 {
1696 Assert(pSpec->cElements > 0);
1697 PCRTVFSCHAINELEMSPEC pLast = &pSpec->paElements[pSpec->cElements - 1];
1698 if (pLast->pszProvider == NULL)
1699 {
1700 char *psz = &pszSpec[pLast->offSpec];
1701 *ppszFinalPath = psz;
1702 if (pSpec->cElements > 1)
1703 {
1704 *ppszSpec = pszSpec;
1705
1706 /* Remove the separator and any whitespace around it. */
1707 while ( psz != pszSpec
1708 && RT_C_IS_SPACE(psz[-1]))
1709 psz--;
1710 if ( psz != pszSpec
1711 && ( psz[-1] == ':'
1712 || psz[-1] == '|'))
1713 psz--;
1714 while ( psz != pszSpec
1715 && RT_C_IS_SPACE(psz[-1]))
1716 psz--;
1717 *psz = '\0';
1718 }
1719 else
1720 *ppszSpec = NULL;
1721 }
1722 else
1723 {
1724 *ppszFinalPath = NULL;
1725 *ppszSpec = pszSpec;
1726 }
1727 RTVfsChainSpecFree(pSpec);
1728 }
1729 else
1730 {
1731 *ppszSpec = NULL;
1732 *ppszFinalPath = NULL;
1733 }
1734 return rc;
1735}
1736
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette