VirtualBox

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

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

(C) year

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

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