VirtualBox

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

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

IPRT: VFS and NT path handling fixes.

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

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