VirtualBox

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

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

IPRT: VFS opening reworking in progress.

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 63.9 KB
 
1/* $Id: vfschain.cpp 69813 2017-11-23 18:33:30Z 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) RTVfsChainOpenObj(const char *pszSpec, uint64_t fFileOpen, uint32_t fObjFlags,
1117 PRTVFSOBJ phVfsObj, uint32_t *poffError, PRTERRINFO pErrInfo)
1118{
1119 /*
1120 * Validate input.
1121 */
1122 uint32_t offErrorIgn;
1123 if (!poffError)
1124 poffError = &offErrorIgn;
1125 *poffError = 0;
1126 AssertPtrReturn(pszSpec, VERR_INVALID_POINTER);
1127 AssertReturn(*pszSpec != '\0', VERR_INVALID_PARAMETER);
1128 AssertPtrReturn(phVfsObj, VERR_INVALID_POINTER);
1129 AssertPtrNullReturn(pErrInfo, VERR_INVALID_POINTER);
1130
1131 int rc = rtFileRecalcAndValidateFlags(&fFileOpen);
1132 if (RT_FAILURE(rc))
1133 return rc;
1134 AssertMsgReturn( RTPATH_F_IS_VALID(fObjFlags, RTVFSOBJ_F_VALID_MASK)
1135 && (fObjFlags & RTVFSOBJ_F_CREATE_MASK) <= RTVFSOBJ_F_CREATE_DIRECTORY,
1136 ("fObjFlags=%#x\n", fObjFlags),
1137 VERR_INVALID_FLAGS);
1138
1139 /*
1140 * Try for a VFS chain first, falling back on regular file system stuff if it's just a path.
1141 */
1142 PRTVFSCHAINSPEC pSpec = NULL;
1143 if (strncmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX, sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1) == 0)
1144 {
1145 rc = RTVfsChainSpecParse(pszSpec, 0 /*fFlags*/, RTVFSOBJTYPE_DIR, &pSpec, poffError);
1146 if (RT_FAILURE(rc))
1147 return rc;
1148
1149 Assert(pSpec->cElements > 0);
1150 if ( pSpec->cElements > 1
1151 || pSpec->paElements[0].enmType != RTVFSOBJTYPE_END)
1152 {
1153 const char *pszFinal = NULL;
1154 RTVFSOBJ hVfsObj = NIL_RTVFSOBJ;
1155 pSpec->fOpenFile = fFileOpen;
1156 rc = RTVfsChainSpecCheckAndSetup(pSpec, NULL /*pReuseSpec*/, &hVfsObj, &pszFinal, poffError, pErrInfo);
1157 if (RT_SUCCESS(rc))
1158 {
1159 if (!pszFinal)
1160 {
1161 *phVfsObj = hVfsObj;
1162 rc = VINF_SUCCESS;
1163 }
1164 else
1165 {
1166 /*
1167 * Do a file open with the final path on the returned object.
1168 */
1169 RTVFS hVfs = RTVfsObjToVfs(hVfsObj);
1170 RTVFSDIR hVfsDir = RTVfsObjToDir(hVfsObj);
1171 RTVFSFSSTREAM hVfsFss = RTVfsObjToFsStream(hVfsObj);
1172 if (hVfs != NIL_RTVFS)
1173 rc = RTVfsObjOpen(hVfs, pszFinal, fFileOpen, fObjFlags, phVfsObj);
1174 else if (hVfsDir != NIL_RTVFSDIR)
1175 rc = RTVfsDirOpenObj(hVfsDir, pszFinal, fFileOpen, fObjFlags, phVfsObj);
1176 else if (hVfsFss != NIL_RTVFSFSSTREAM)
1177 rc = VERR_NOT_IMPLEMENTED;
1178 else
1179 rc = VERR_VFS_CHAIN_TYPE_MISMATCH_PATH_ONLY;
1180 RTVfsRelease(hVfs);
1181 RTVfsDirRelease(hVfsDir);
1182 RTVfsFsStrmRelease(hVfsFss);
1183 RTVfsObjRelease(hVfsObj);
1184 }
1185 }
1186
1187 RTVfsChainSpecFree(pSpec);
1188 return rc;
1189 }
1190
1191 /* Only a path element. */
1192 pszSpec = pSpec->paElements[0].paArgs[0].psz;
1193 }
1194
1195 /*
1196 * Path to regular file system.
1197 * Go via the directory VFS wrapper to avoid duplicating code.
1198 */
1199 RTVFSDIR hVfsParentDir = NIL_RTVFSDIR;
1200 const char *pszFilename;
1201 if (RTPathHasPath(pszSpec))
1202 {
1203 char *pszCopy = RTStrDup(pszSpec);
1204 if (pszCopy)
1205 {
1206 RTPathStripFilename(pszCopy);
1207 rc = RTVfsDirOpenNormal(pszCopy, 0 /*fOpen*/, &hVfsParentDir);
1208 RTStrFree(pszCopy);
1209 }
1210 else
1211 rc = VERR_NO_STR_MEMORY;
1212 pszFilename = RTPathFilename(pszSpec);
1213 }
1214 else
1215 {
1216 pszFilename = pszSpec;
1217 rc = RTVfsDirOpenNormal(".", 0 /*fOpen*/, &hVfsParentDir);
1218 }
1219 if (RT_SUCCESS(rc))
1220 {
1221 rc = RTVfsDirOpenObj(hVfsParentDir, pszFilename, fFileOpen, fObjFlags, phVfsObj);
1222 RTVfsDirRelease(hVfsParentDir);
1223 }
1224
1225 RTVfsChainSpecFree(pSpec);
1226 return rc;
1227}
1228
1229
1230RTDECL(int) RTVfsChainOpenDir(const char *pszSpec, uint32_t fOpen,
1231 PRTVFSDIR phVfsDir, uint32_t *poffError, PRTERRINFO pErrInfo)
1232{
1233 uint32_t offErrorIgn;
1234 if (!poffError)
1235 poffError = &offErrorIgn;
1236 *poffError = 0;
1237 AssertPtrReturn(pszSpec, VERR_INVALID_POINTER);
1238 AssertReturn(*pszSpec != '\0', VERR_INVALID_PARAMETER);
1239 AssertPtrReturn(phVfsDir, VERR_INVALID_POINTER);
1240 AssertPtrNullReturn(pErrInfo, VERR_INVALID_POINTER);
1241
1242 /*
1243 * Try for a VFS chain first, falling back on regular file system stuff if it's just a path.
1244 */
1245 int rc;
1246 PRTVFSCHAINSPEC pSpec = NULL;
1247 if (strncmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX, sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1) == 0)
1248 {
1249 rc = RTVfsChainSpecParse(pszSpec, 0 /*fFlags*/, RTVFSOBJTYPE_DIR, &pSpec, poffError);
1250 if (RT_FAILURE(rc))
1251 return rc;
1252
1253 Assert(pSpec->cElements > 0);
1254 if ( pSpec->cElements > 1
1255 || pSpec->paElements[0].enmType != RTVFSOBJTYPE_END)
1256 {
1257 const char *pszFinal = NULL;
1258 RTVFSOBJ hVfsObj = NIL_RTVFSOBJ;
1259 pSpec->fOpenFile = fOpen;
1260 rc = RTVfsChainSpecCheckAndSetup(pSpec, NULL /*pReuseSpec*/, &hVfsObj, &pszFinal, poffError, pErrInfo);
1261 if (RT_SUCCESS(rc))
1262 {
1263 if (!pszFinal)
1264 {
1265 /* Try convert it to a directory object and we're done. */
1266 *phVfsDir = RTVfsObjToDir(hVfsObj);
1267 if (*phVfsDir)
1268 rc = VINF_SUCCESS;
1269 else
1270 rc = VERR_VFS_CHAIN_CAST_FAILED;
1271 }
1272 else
1273 {
1274 /*
1275 * Do a file open with the final path on the returned object.
1276 */
1277 RTVFS hVfs = RTVfsObjToVfs(hVfsObj);
1278 RTVFSDIR hVfsDir = RTVfsObjToDir(hVfsObj);
1279 RTVFSFSSTREAM hVfsFss = RTVfsObjToFsStream(hVfsObj);
1280 if (hVfs != NIL_RTVFS)
1281 rc = RTVfsDirOpen(hVfs, pszFinal, fOpen, phVfsDir);
1282 else if (hVfsDir != NIL_RTVFSDIR)
1283 rc = RTVfsDirOpenDir(hVfsDir, pszFinal, fOpen, phVfsDir);
1284 else if (hVfsFss != NIL_RTVFSFSSTREAM)
1285 rc = VERR_NOT_IMPLEMENTED;
1286 else
1287 rc = VERR_VFS_CHAIN_TYPE_MISMATCH_PATH_ONLY;
1288 RTVfsRelease(hVfs);
1289 RTVfsDirRelease(hVfsDir);
1290 RTVfsFsStrmRelease(hVfsFss);
1291 }
1292 RTVfsObjRelease(hVfsObj);
1293 }
1294
1295 RTVfsChainSpecFree(pSpec);
1296 return rc;
1297 }
1298
1299 /* Only a path element. */
1300 pszSpec = pSpec->paElements[0].paArgs[0].psz;
1301 }
1302
1303 /*
1304 * Path to regular file system.
1305 */
1306 rc = RTVfsDirOpenNormal(pszSpec, fOpen, phVfsDir);
1307
1308 RTVfsChainSpecFree(pSpec);
1309 return rc;
1310}
1311
1312
1313RTDECL(int) RTVfsChainOpenParentDir(const char *pszSpec, uint32_t fOpen, PRTVFSDIR phVfsDir, const char **ppszChild,
1314 uint32_t *poffError, PRTERRINFO pErrInfo)
1315{
1316 uint32_t offErrorIgn;
1317 if (!poffError)
1318 poffError = &offErrorIgn;
1319 *poffError = 0;
1320 AssertPtrReturn(pszSpec, VERR_INVALID_POINTER);
1321 AssertReturn(*pszSpec != '\0', VERR_INVALID_PARAMETER);
1322 AssertPtrReturn(phVfsDir, VERR_INVALID_POINTER);
1323 AssertPtrReturn(ppszChild, VERR_INVALID_POINTER);
1324 *ppszChild = NULL;
1325 AssertPtrNullReturn(pErrInfo, VERR_INVALID_POINTER);
1326
1327 /*
1328 * Process the spec from the end, trying to find the child part of it.
1329 * We cannot use RTPathFilename here because we must ignore trailing slashes.
1330 */
1331 const char * const pszEnd = RTStrEnd(pszSpec, RTSTR_MAX);
1332 const char *pszChild = pszEnd;
1333 while ( pszChild != pszSpec
1334 && RTPATH_IS_SLASH(pszChild[-1]))
1335 pszChild--;
1336 while ( pszChild != pszSpec
1337 && !RTPATH_IS_SLASH(pszChild[-1])
1338 && !RTPATH_IS_VOLSEP(pszChild[-1]))
1339 pszChild--;
1340 size_t const cchChild = pszEnd - pszChild;
1341 *ppszChild = pszChild;
1342
1343 /*
1344 * Try for a VFS chain first, falling back on regular file system stuff if it's just a path.
1345 */
1346 int rc;
1347 PRTVFSCHAINSPEC pSpec = NULL;
1348 if (strncmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX, sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1) == 0)
1349 {
1350 rc = RTVfsChainSpecParse(pszSpec, 0 /*fFlags*/, RTVFSOBJTYPE_DIR, &pSpec, poffError);
1351 if (RT_FAILURE(rc))
1352 return rc;
1353
1354 Assert(pSpec->cElements > 0);
1355 if ( pSpec->cElements > 1
1356 || pSpec->paElements[0].enmType != RTVFSOBJTYPE_END)
1357 {
1358 /*
1359 * Check that it ends with a path-only element and that this in turn ends with
1360 * what pszChild points to. (We cannot easiy figure out the parent part of
1361 * an element that isn't path-only, so we don't bother trying try.)
1362 */
1363 PRTVFSCHAINELEMSPEC pLast = &pSpec->paElements[pSpec->cElements - 1];
1364 if (pLast->pszProvider == NULL)
1365 {
1366 size_t cchFinal = strlen(pLast->paArgs[0].psz);
1367 if ( cchFinal >= cchChild
1368 && memcmp(&pLast->paArgs[0].psz[cchFinal - cchChild], pszChild, cchChild + 1) == 0)
1369 {
1370 /*
1371 * Drop the child part so we have a path to the parent, then setup the chain.
1372 */
1373 if (cchFinal > cchChild)
1374 pLast->paArgs[0].psz[cchFinal - cchChild] = '\0';
1375 else
1376 pSpec->cElements--;
1377
1378 const char *pszFinal = NULL;
1379 RTVFSOBJ hVfsObj = NIL_RTVFSOBJ;
1380 pSpec->fOpenFile = fOpen;
1381 rc = RTVfsChainSpecCheckAndSetup(pSpec, NULL /*pReuseSpec*/, &hVfsObj, &pszFinal, poffError, pErrInfo);
1382 if (RT_SUCCESS(rc))
1383 {
1384 if (!pszFinal)
1385 {
1386 Assert(cchFinal == cchChild);
1387
1388 /* Try convert it to a file object and we're done. */
1389 *phVfsDir = RTVfsObjToDir(hVfsObj);
1390 if (*phVfsDir)
1391 rc = VINF_SUCCESS;
1392 else
1393 rc = VERR_VFS_CHAIN_CAST_FAILED;
1394 }
1395 else
1396 {
1397 /*
1398 * Do a file open with the final path on the returned object.
1399 */
1400 RTVFS hVfs = RTVfsObjToVfs(hVfsObj);
1401 RTVFSDIR hVfsDir = RTVfsObjToDir(hVfsObj);
1402 RTVFSFSSTREAM hVfsFss = RTVfsObjToFsStream(hVfsObj);
1403 if (hVfs != NIL_RTVFS)
1404 rc = RTVfsDirOpen(hVfs, pszFinal, fOpen, phVfsDir);
1405 else if (hVfsDir != NIL_RTVFSDIR)
1406 rc = RTVfsDirOpenDir(hVfsDir, pszFinal, fOpen, phVfsDir);
1407 else if (hVfsFss != NIL_RTVFSFSSTREAM)
1408 rc = VERR_NOT_IMPLEMENTED;
1409 else
1410 rc = VERR_VFS_CHAIN_TYPE_MISMATCH_PATH_ONLY;
1411 RTVfsRelease(hVfs);
1412 RTVfsDirRelease(hVfsDir);
1413 RTVfsFsStrmRelease(hVfsFss);
1414 }
1415 RTVfsObjRelease(hVfsObj);
1416 }
1417 }
1418 else
1419 rc = VERR_VFS_CHAIN_TOO_SHORT_FOR_PARENT;
1420 }
1421 else
1422 rc = VERR_VFS_CHAIN_NOT_PATH_ONLY;
1423
1424 RTVfsChainSpecFree(pSpec);
1425 return rc;
1426 }
1427
1428 /* Only a path element. */
1429 pszSpec = pSpec->paElements[0].paArgs[0].psz;
1430 }
1431
1432 /*
1433 * Path to regular file system.
1434 */
1435 if (RTPathHasPath(pszSpec))
1436 {
1437 char *pszCopy = RTStrDup(pszSpec);
1438 if (pszCopy)
1439 {
1440 RTPathStripFilename(pszCopy);
1441 rc = RTVfsDirOpenNormal(pszCopy, fOpen, phVfsDir);
1442 RTStrFree(pszCopy);
1443 }
1444 else
1445 rc = VERR_NO_STR_MEMORY;
1446 }
1447 else
1448 rc = RTVfsDirOpenNormal(".", fOpen, phVfsDir);
1449
1450 RTVfsChainSpecFree(pSpec);
1451 return rc;
1452
1453}
1454
1455
1456RTDECL(int) RTVfsChainOpenFile(const char *pszSpec, uint64_t fOpen,
1457 PRTVFSFILE phVfsFile, uint32_t *poffError, PRTERRINFO pErrInfo)
1458{
1459 uint32_t offErrorIgn;
1460 if (!poffError)
1461 poffError = &offErrorIgn;
1462 *poffError = 0;
1463 AssertPtrReturn(pszSpec, VERR_INVALID_POINTER);
1464 AssertReturn(*pszSpec != '\0', VERR_INVALID_PARAMETER);
1465 AssertPtrReturn(phVfsFile, VERR_INVALID_POINTER);
1466 AssertPtrNullReturn(pErrInfo, VERR_INVALID_POINTER);
1467
1468 /*
1469 * Try for a VFS chain first, falling back on regular file system stuff if it's just a path.
1470 */
1471 int rc;
1472 PRTVFSCHAINSPEC pSpec = NULL;
1473 if (strncmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX, sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1) == 0)
1474 {
1475 rc = RTVfsChainSpecParse(pszSpec, 0 /*fFlags*/, RTVFSOBJTYPE_FILE, &pSpec, poffError);
1476 if (RT_FAILURE(rc))
1477 return rc;
1478
1479 Assert(pSpec->cElements > 0);
1480 if ( pSpec->cElements > 1
1481 || pSpec->paElements[0].enmType != RTVFSOBJTYPE_END)
1482 {
1483 const char *pszFinal = NULL;
1484 RTVFSOBJ hVfsObj = NIL_RTVFSOBJ;
1485 pSpec->fOpenFile = fOpen;
1486 rc = RTVfsChainSpecCheckAndSetup(pSpec, NULL /*pReuseSpec*/, &hVfsObj, &pszFinal, poffError, pErrInfo);
1487 if (RT_SUCCESS(rc))
1488 {
1489 if (!pszFinal)
1490 {
1491 /* Try convert it to a file object and we're done. */
1492 *phVfsFile = RTVfsObjToFile(hVfsObj);
1493 if (*phVfsFile)
1494 rc = VINF_SUCCESS;
1495 else
1496 rc = VERR_VFS_CHAIN_CAST_FAILED;
1497 }
1498 else
1499 {
1500 /*
1501 * Do a file open with the final path on the returned object.
1502 */
1503 RTVFS hVfs = RTVfsObjToVfs(hVfsObj);
1504 RTVFSDIR hVfsDir = RTVfsObjToDir(hVfsObj);
1505 RTVFSFSSTREAM hVfsFss = RTVfsObjToFsStream(hVfsObj);
1506 if (hVfs != NIL_RTVFS)
1507 rc = RTVfsFileOpen(hVfs, pszFinal, fOpen, phVfsFile);
1508 else if (hVfsDir != NIL_RTVFSDIR)
1509 rc = RTVfsDirOpenFile(hVfsDir, pszFinal, fOpen, phVfsFile);
1510 else if (hVfsFss != NIL_RTVFSFSSTREAM)
1511 rc = VERR_NOT_IMPLEMENTED;
1512 else
1513 rc = VERR_VFS_CHAIN_TYPE_MISMATCH_PATH_ONLY;
1514 RTVfsRelease(hVfs);
1515 RTVfsDirRelease(hVfsDir);
1516 RTVfsFsStrmRelease(hVfsFss);
1517 }
1518 RTVfsObjRelease(hVfsObj);
1519 }
1520
1521 RTVfsChainSpecFree(pSpec);
1522 return rc;
1523 }
1524
1525 /* Only a path element. */
1526 pszSpec = pSpec->paElements[0].paArgs[0].psz;
1527 }
1528
1529 /*
1530 * Path to regular file system.
1531 */
1532 RTFILE hFile;
1533 rc = RTFileOpen(&hFile, pszSpec, fOpen);
1534 if (RT_SUCCESS(rc))
1535 {
1536 RTVFSFILE hVfsFile;
1537 rc = RTVfsFileFromRTFile(hFile, fOpen, false /*fLeaveOpen*/, &hVfsFile);
1538 if (RT_SUCCESS(rc))
1539 *phVfsFile = hVfsFile;
1540 else
1541 RTFileClose(hFile);
1542 }
1543
1544 RTVfsChainSpecFree(pSpec);
1545 return rc;
1546}
1547
1548
1549RTDECL(int) RTVfsChainOpenIoStream(const char *pszSpec, uint64_t fOpen,
1550 PRTVFSIOSTREAM phVfsIos, uint32_t *poffError, PRTERRINFO pErrInfo)
1551{
1552 uint32_t offErrorIgn;
1553 if (!poffError)
1554 poffError = &offErrorIgn;
1555 *poffError = 0;
1556 AssertPtrReturn(pszSpec, VERR_INVALID_POINTER);
1557 AssertReturn(*pszSpec != '\0', VERR_INVALID_PARAMETER);
1558 AssertPtrReturn(phVfsIos, VERR_INVALID_POINTER);
1559 AssertPtrNullReturn(pErrInfo, VERR_INVALID_POINTER);
1560
1561 /*
1562 * Try for a VFS chain first, falling back on regular file system stuff if it's just a path.
1563 */
1564 int rc;
1565 PRTVFSCHAINSPEC pSpec = NULL;
1566 if (strncmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX, sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1) == 0)
1567 {
1568 rc = RTVfsChainSpecParse(pszSpec, 0 /*fFlags*/, RTVFSOBJTYPE_IO_STREAM, &pSpec, poffError);
1569 if (RT_FAILURE(rc))
1570 return rc;
1571
1572 Assert(pSpec->cElements > 0);
1573 if ( pSpec->cElements > 1
1574 || pSpec->paElements[0].enmType != RTVFSOBJTYPE_END)
1575 {
1576 const char *pszFinal = NULL;
1577 RTVFSOBJ hVfsObj = NIL_RTVFSOBJ;
1578 pSpec->fOpenFile = fOpen;
1579 rc = RTVfsChainSpecCheckAndSetup(pSpec, NULL /*pReuseSpec*/, &hVfsObj, &pszFinal, poffError, pErrInfo);
1580 if (RT_SUCCESS(rc))
1581 {
1582 if (!pszFinal)
1583 {
1584 /* Try convert it to an I/O object and we're done. */
1585 *phVfsIos = RTVfsObjToIoStream(hVfsObj);
1586 if (*phVfsIos)
1587 rc = VINF_SUCCESS;
1588 else
1589 rc = VERR_VFS_CHAIN_CAST_FAILED;
1590 }
1591 else
1592 {
1593 /*
1594 * Do a file open with the final path on the returned object.
1595 */
1596 RTVFS hVfs = RTVfsObjToVfs(hVfsObj);
1597 RTVFSDIR hVfsDir = RTVfsObjToDir(hVfsObj);
1598 RTVFSFSSTREAM hVfsFss = RTVfsObjToFsStream(hVfsObj);
1599 RTVFSFILE hVfsFile = NIL_RTVFSFILE;
1600 if (hVfs != NIL_RTVFS)
1601 rc = RTVfsFileOpen(hVfs, pszFinal, fOpen, &hVfsFile);
1602 else if (hVfsDir != NIL_RTVFSDIR)
1603 rc = RTVfsDirOpenFile(hVfsDir, pszFinal, fOpen, &hVfsFile);
1604 else if (hVfsFss != NIL_RTVFSFSSTREAM)
1605 rc = VERR_NOT_IMPLEMENTED;
1606 else
1607 rc = VERR_VFS_CHAIN_TYPE_MISMATCH_PATH_ONLY;
1608 if (RT_SUCCESS(rc))
1609 {
1610 *phVfsIos = RTVfsFileToIoStream(hVfsFile);
1611 if (*phVfsIos)
1612 rc = VINF_SUCCESS;
1613 else
1614 rc = VERR_VFS_CHAIN_CAST_FAILED;
1615 RTVfsFileRelease(hVfsFile);
1616 }
1617 RTVfsRelease(hVfs);
1618 RTVfsDirRelease(hVfsDir);
1619 RTVfsFsStrmRelease(hVfsFss);
1620 }
1621 RTVfsObjRelease(hVfsObj);
1622 }
1623
1624 RTVfsChainSpecFree(pSpec);
1625 return rc;
1626 }
1627
1628 /* Only a path element. */
1629 pszSpec = pSpec->paElements[0].paArgs[0].psz;
1630 }
1631
1632 /*
1633 * Path to regular file system.
1634 */
1635 RTFILE hFile;
1636 rc = RTFileOpen(&hFile, pszSpec, fOpen);
1637 if (RT_SUCCESS(rc))
1638 {
1639 RTVFSFILE hVfsFile;
1640 rc = RTVfsFileFromRTFile(hFile, fOpen, false /*fLeaveOpen*/, &hVfsFile);
1641 if (RT_SUCCESS(rc))
1642 {
1643 *phVfsIos = RTVfsFileToIoStream(hVfsFile);
1644 RTVfsFileRelease(hVfsFile);
1645 }
1646 else
1647 RTFileClose(hFile);
1648 }
1649
1650 RTVfsChainSpecFree(pSpec);
1651 return rc;
1652}
1653
1654
1655/**
1656 * The equivalent of RTPathQueryInfoEx
1657 */
1658RTDECL(int) RTVfsChainQueryInfo(const char *pszSpec, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs,
1659 uint32_t fFlags, uint32_t *poffError, PRTERRINFO pErrInfo)
1660{
1661 uint32_t offErrorIgn;
1662 if (!poffError)
1663 poffError = &offErrorIgn;
1664 *poffError = 0;
1665 AssertPtrReturn(pszSpec, VERR_INVALID_POINTER);
1666 AssertReturn(*pszSpec != '\0', VERR_INVALID_PARAMETER);
1667 AssertPtrReturn(pObjInfo, VERR_INVALID_POINTER);
1668 AssertReturn(enmAdditionalAttribs >= RTFSOBJATTRADD_NOTHING && enmAdditionalAttribs <= RTFSOBJATTRADD_LAST,
1669 VERR_INVALID_PARAMETER);
1670 AssertPtrNullReturn(pErrInfo, VERR_INVALID_POINTER);
1671
1672 /*
1673 * Try for a VFS chain first, falling back on regular file system stuff if it's just a path.
1674 */
1675 int rc;
1676 PRTVFSCHAINSPEC pSpec = NULL;
1677 if (strncmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX, sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1) == 0)
1678 {
1679 rc = RTVfsChainSpecParse(pszSpec, 0 /*fFlags*/, RTVFSOBJTYPE_BASE, &pSpec, poffError);
1680 if (RT_FAILURE(rc))
1681 return rc;
1682
1683 Assert(pSpec->cElements > 0);
1684 if ( pSpec->cElements > 1
1685 || pSpec->paElements[0].enmType != RTVFSOBJTYPE_END)
1686 {
1687 const char *pszFinal = NULL;
1688 RTVFSOBJ hVfsObj = NIL_RTVFSOBJ;
1689 pSpec->fOpenFile = RTFILE_O_READ | RTFILE_O_OPEN;
1690 rc = RTVfsChainSpecCheckAndSetup(pSpec, NULL /*pReuseSpec*/, &hVfsObj, &pszFinal, poffError, pErrInfo);
1691 if (RT_SUCCESS(rc))
1692 {
1693 if (!pszFinal)
1694 {
1695 /*
1696 * Do the job on the final object.
1697 */
1698 rc = RTVfsObjQueryInfo(hVfsObj, pObjInfo, enmAdditionalAttribs);
1699 }
1700 else
1701 {
1702 /*
1703 * Do a path query operation on the penultimate object.
1704 */
1705 RTVFS hVfs = RTVfsObjToVfs(hVfsObj);
1706 RTVFSDIR hVfsDir = RTVfsObjToDir(hVfsObj);
1707 RTVFSFSSTREAM hVfsFss = RTVfsObjToFsStream(hVfsObj);
1708 if (hVfs != NIL_RTVFS)
1709 rc = RTVfsQueryPathInfo(hVfs, pszFinal, pObjInfo, enmAdditionalAttribs, fFlags);
1710 else if (hVfsDir != NIL_RTVFSDIR)
1711 rc = RTVfsDirQueryPathInfo(hVfsDir, pszFinal, pObjInfo, enmAdditionalAttribs, fFlags);
1712 else if (hVfsFss != NIL_RTVFSFSSTREAM)
1713 rc = VERR_NOT_SUPPORTED;
1714 else
1715 rc = VERR_VFS_CHAIN_TYPE_MISMATCH_PATH_ONLY;
1716 RTVfsRelease(hVfs);
1717 RTVfsDirRelease(hVfsDir);
1718 RTVfsFsStrmRelease(hVfsFss);
1719 }
1720 RTVfsObjRelease(hVfsObj);
1721 }
1722
1723 RTVfsChainSpecFree(pSpec);
1724 return rc;
1725 }
1726
1727 /* Only a path element. */
1728 pszSpec = pSpec->paElements[0].paArgs[0].psz;
1729 }
1730
1731 /*
1732 * Path to regular file system.
1733 */
1734 rc = RTPathQueryInfoEx(pszSpec, pObjInfo, enmAdditionalAttribs, fFlags);
1735
1736 RTVfsChainSpecFree(pSpec);
1737 return rc;
1738}
1739
1740
1741RTDECL(bool) RTVfsChainIsSpec(const char *pszSpec)
1742{
1743 return pszSpec
1744 && strncmp(pszSpec, RT_STR_TUPLE(RTVFSCHAIN_SPEC_PREFIX)) == 0;
1745}
1746
1747
1748RTDECL(int) RTVfsChainQueryFinalPath(const char *pszSpec, char **ppszFinalPath, uint32_t *poffError)
1749{
1750 /* Make sure we've got an error info variable. */
1751 uint32_t offErrorIgn;
1752 if (!poffError)
1753 poffError = &offErrorIgn;
1754 *poffError = 0;
1755
1756 /*
1757 * If not chain specifier, just duplicate the input and return.
1758 */
1759 if (strncmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX, sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1) != 0)
1760 return RTStrDupEx(ppszFinalPath, pszSpec);
1761
1762 /*
1763 * Parse it and check out the last element.
1764 */
1765 PRTVFSCHAINSPEC pSpec = NULL;
1766 int rc = RTVfsChainSpecParse(pszSpec, 0 /*fFlags*/, RTVFSOBJTYPE_BASE, &pSpec, poffError);
1767 if (RT_SUCCESS(rc))
1768 {
1769 PCRTVFSCHAINELEMSPEC pLast = &pSpec->paElements[pSpec->cElements - 1];
1770 if (pLast->pszProvider == NULL)
1771 rc = RTStrDupEx(ppszFinalPath, pLast->paArgs[0].psz);
1772 else
1773 {
1774 rc = VERR_VFS_CHAIN_NOT_PATH_ONLY;
1775 *poffError = pLast->offSpec;
1776 }
1777 RTVfsChainSpecFree(pSpec);
1778 }
1779 return rc;
1780}
1781
1782
1783RTDECL(int) RTVfsChainSplitOffFinalPath(char *pszSpec, char **ppszSpec, char **ppszFinalPath, uint32_t *poffError)
1784{
1785 /* Make sure we've got an error info variable. */
1786 uint32_t offErrorIgn;
1787 if (!poffError)
1788 poffError = &offErrorIgn;
1789 *poffError = 0;
1790
1791 /*
1792 * If not chain specifier, just duplicate the input and return.
1793 */
1794 if (strncmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX, sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1) != 0)
1795 {
1796 *ppszSpec = NULL;
1797 *ppszFinalPath = pszSpec;
1798 return VINF_SUCCESS;
1799 }
1800
1801 /*
1802 * Parse it and check out the last element.
1803 */
1804 PRTVFSCHAINSPEC pSpec = NULL;
1805 int rc = RTVfsChainSpecParse(pszSpec, 0 /*fFlags*/, RTVFSOBJTYPE_BASE, &pSpec, poffError);
1806 if (RT_SUCCESS(rc))
1807 {
1808 Assert(pSpec->cElements > 0);
1809 PCRTVFSCHAINELEMSPEC pLast = &pSpec->paElements[pSpec->cElements - 1];
1810 if (pLast->pszProvider == NULL)
1811 {
1812 char *psz = &pszSpec[pLast->offSpec];
1813 *ppszFinalPath = psz;
1814 if (pSpec->cElements > 1)
1815 {
1816 *ppszSpec = pszSpec;
1817
1818 /* Remove the separator and any whitespace around it. */
1819 while ( psz != pszSpec
1820 && RT_C_IS_SPACE(psz[-1]))
1821 psz--;
1822 if ( psz != pszSpec
1823 && ( psz[-1] == ':'
1824 || psz[-1] == '|'))
1825 psz--;
1826 while ( psz != pszSpec
1827 && RT_C_IS_SPACE(psz[-1]))
1828 psz--;
1829 *psz = '\0';
1830 }
1831 else
1832 *ppszSpec = NULL;
1833 }
1834 else
1835 {
1836 *ppszFinalPath = NULL;
1837 *ppszSpec = pszSpec;
1838 }
1839 RTVfsChainSpecFree(pSpec);
1840 }
1841 else
1842 {
1843 *ppszSpec = NULL;
1844 *ppszFinalPath = NULL;
1845 }
1846 return rc;
1847}
1848
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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