VirtualBox

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

Last change on this file since 66594 was 66594, checked in by vboxsync, 8 years ago

iprt: Reworked and implemented VFS chains, adding stdfile, gzip and gunzip element provider/factories.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 27.5 KB
Line 
1/* $Id: vfschain.cpp 66594 2017-04-17 15:29:05Z vboxsync $ */
2/** @file
3 * IPRT - Virtual File System, Chains.
4 */
5
6/*
7 * Copyright (C) 2010-2016 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#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/*********************************************************************************************************************************
52* Global Variables *
53*********************************************************************************************************************************/
54/** Init the critical section once. */
55static RTONCE g_rtVfsChainElementInitOnce = RTONCE_INITIALIZER;
56/** Critical section protecting g_rtVfsChainElementProviderList. */
57static RTCRITSECTRW g_rtVfsChainElementCritSect;
58/** List of VFS chain element providers (RTVFSCHAINELEMENTREG). */
59static RTLISTANCHOR g_rtVfsChainElementProviderList;
60
61
62
63
64/**
65 * Initializes the globals via RTOnce.
66 *
67 * @returns IPRT status code
68 * @param pvUser Unused, ignored.
69 */
70static DECLCALLBACK(int) rtVfsChainElementRegisterInit(void *pvUser)
71{
72 NOREF(pvUser);
73 if (!g_rtVfsChainElementProviderList.pNext)
74 RTListInit(&g_rtVfsChainElementProviderList);
75 int rc = RTCritSectRwInit(&g_rtVfsChainElementCritSect);
76 if (RT_SUCCESS(rc))
77 {
78 }
79 return rc;
80}
81
82
83RTDECL(int) RTVfsChainElementRegisterProvider(PRTVFSCHAINELEMENTREG pRegRec, bool fFromCtor)
84{
85 int rc;
86
87 /*
88 * Input validation.
89 */
90 AssertPtrReturn(pRegRec, VERR_INVALID_POINTER);
91 AssertMsgReturn(pRegRec->uVersion == RTVFSCHAINELEMENTREG_VERSION, ("%#x", pRegRec->uVersion), VERR_INVALID_POINTER);
92 AssertMsgReturn(pRegRec->uEndMarker == RTVFSCHAINELEMENTREG_VERSION, ("%#zx", pRegRec->uEndMarker), VERR_INVALID_POINTER);
93 AssertReturn(pRegRec->fReserved == 0, VERR_INVALID_POINTER);
94 AssertPtrReturn(pRegRec->pszName, VERR_INVALID_POINTER);
95 AssertPtrReturn(pRegRec->pfnValidate, VERR_INVALID_POINTER);
96 AssertPtrReturn(pRegRec->pfnInstantiate, VERR_INVALID_POINTER);
97 AssertPtrReturn(pRegRec->pfnCanReuseElement, VERR_INVALID_POINTER);
98
99 /*
100 * Init and take the lock.
101 */
102 if (!fFromCtor)
103 {
104 rc = RTOnce(&g_rtVfsChainElementInitOnce, rtVfsChainElementRegisterInit, NULL);
105 if (RT_FAILURE(rc))
106 return rc;
107 rc = RTCritSectRwEnterExcl(&g_rtVfsChainElementCritSect);
108 if (RT_FAILURE(rc))
109 return rc;
110 }
111 else if (!g_rtVfsChainElementProviderList.pNext)
112 RTListInit(&g_rtVfsChainElementProviderList);
113
114 /*
115 * Duplicate name?
116 */
117 rc = VINF_SUCCESS;
118 PRTVFSCHAINELEMENTREG pIterator, pIterNext;
119 RTListForEachSafe(&g_rtVfsChainElementProviderList, pIterator, pIterNext, RTVFSCHAINELEMENTREG, ListEntry)
120 {
121 if (!strcmp(pIterator->pszName, pRegRec->pszName))
122 {
123 AssertMsgFailed(("duplicate name '%s' old=%p new=%p\n", pIterator->pszName, pIterator, pRegRec));
124 rc = VERR_ALREADY_EXISTS;
125 break;
126 }
127 }
128
129 /*
130 * If not, append the record to the list.
131 */
132 if (RT_SUCCESS(rc))
133 RTListAppend(&g_rtVfsChainElementProviderList, &pRegRec->ListEntry);
134
135 /*
136 * Leave the lock and return.
137 */
138 if (!fFromCtor)
139 RTCritSectRwLeaveExcl(&g_rtVfsChainElementCritSect);
140 return rc;
141}
142
143
144/**
145 * Allocates and initializes an empty spec
146 *
147 * @returns Pointer to the spec on success, NULL on failure.
148 */
149static PRTVFSCHAINSPEC rtVfsChainSpecAlloc(void)
150{
151 PRTVFSCHAINSPEC pSpec = (PRTVFSCHAINSPEC)RTMemTmpAlloc(sizeof(*pSpec));
152 if (pSpec)
153 {
154 pSpec->fOpenFile = 0;
155 pSpec->fOpenDir = 0;
156 pSpec->cElements = 0;
157 pSpec->uProvider = 0;
158 pSpec->paElements = NULL;
159 }
160 return pSpec;
161}
162
163
164/**
165 * Duplicate a spec string.
166 *
167 * This differs from RTStrDupN in that it uses RTMemTmpAlloc instead of
168 * RTMemAlloc.
169 *
170 * @returns String copy on success, NULL on failure.
171 * @param psz The string to duplicate.
172 * @param cch The number of bytes to duplicate.
173 * @param prc The status code variable to set on failure. (Leeps the
174 * code shorter. -lazy bird)
175 */
176DECLINLINE(char *) rtVfsChainSpecDupStrN(const char *psz, size_t cch, int *prc)
177{
178 char *pszCopy = (char *)RTMemTmpAlloc(cch + 1);
179 if (pszCopy)
180 {
181 if (!memchr(psz, '\\', cch))
182 {
183 /* Plain string, copy it raw. */
184 memcpy(pszCopy, psz, cch);
185 pszCopy[cch] = '\0';
186 }
187 else
188 {
189 /* Has escape sequences, must unescape it. */
190 char *pszDst = pszCopy;
191 while (cch)
192 {
193 char ch = *psz++;
194 if (ch == '\\')
195 {
196 char ch2 = psz[2];
197 if (ch2 == '(' || ch2 == ')' || ch2 == '\\' || ch2 == ',')
198 {
199 psz++;
200 ch = ch2;
201 }
202 }
203 *pszDst++ = ch;
204 }
205 *pszDst = '\0';
206 }
207 }
208 else
209 *prc = VERR_NO_TMP_MEMORY;
210 return pszCopy;
211}
212
213
214/**
215 * Adds an empty element to the chain specification.
216 *
217 * The caller is responsible for filling it the element attributes.
218 *
219 * @returns Pointer to the new element on success, NULL on failure. The
220 * pointer is only valid till the next call to this function.
221 * @param pSpec The chain specification.
222 * @param prc The status code variable to set on failure. (Leeps the
223 * code shorter. -lazy bird)
224 */
225static PRTVFSCHAINELEMSPEC rtVfsChainSpecAddElement(PRTVFSCHAINSPEC pSpec, uint16_t offSpec, int *prc)
226{
227 AssertPtr(pSpec);
228
229 /*
230 * Resize the element table if necessary.
231 */
232 uint32_t const iElement = pSpec->cElements;
233 if ((iElement % 32) == 0)
234 {
235 PRTVFSCHAINELEMSPEC paNew = (PRTVFSCHAINELEMSPEC)RTMemTmpAlloc((iElement + 32) * sizeof(paNew[0]));
236 if (!paNew)
237 {
238 *prc = VERR_NO_TMP_MEMORY;
239 return NULL;
240 }
241
242 memcpy(paNew, pSpec->paElements, iElement * sizeof(paNew[0]));
243 RTMemTmpFree(pSpec->paElements);
244 pSpec->paElements = paNew;
245 }
246
247 /*
248 * Initialize and add the new element.
249 */
250 PRTVFSCHAINELEMSPEC pElement = &pSpec->paElements[iElement];
251 pElement->pszProvider = NULL;
252 pElement->enmTypeIn = iElement ? pSpec->paElements[iElement - 1].enmType : RTVFSOBJTYPE_INVALID;
253 pElement->enmType = RTVFSOBJTYPE_INVALID;
254 pElement->offSpec = offSpec;
255 pElement->cchSpec = 0;
256 pElement->cArgs = 0;
257 pElement->paArgs = NULL;
258 pElement->pProvider = NULL;
259 pElement->hVfsObj = NIL_RTVFSOBJ;
260
261 pSpec->cElements = iElement + 1;
262 return pElement;
263}
264
265
266/**
267 * Adds an argument to the element spec.
268 *
269 * @returns IPRT status code.
270 * @param pElement The element.
271 * @param psz The start of the argument string.
272 * @param cch The length of the argument string, escape
273 * sequences counted twice.
274 */
275static int rtVfsChainSpecElementAddArg(PRTVFSCHAINELEMSPEC pElement, const char *psz, size_t cch, uint16_t offSpec)
276{
277 uint32_t iArg = pElement->cArgs;
278 if ((iArg % 32) == 0)
279 {
280 PRTVFSCHAINELEMENTARG paNew = (PRTVFSCHAINELEMENTARG)RTMemTmpAlloc((iArg + 32) * sizeof(paNew[0]));
281 if (!paNew)
282 return VERR_NO_TMP_MEMORY;
283 memcpy(paNew, pElement->paArgs, iArg * sizeof(paNew[0]));
284 RTMemTmpFree(pElement->paArgs);
285 pElement->paArgs = paNew;
286 }
287
288 int rc = VINF_SUCCESS;
289 pElement->paArgs[iArg].psz = rtVfsChainSpecDupStrN(psz, cch, &rc);
290 pElement->paArgs[iArg].offSpec = offSpec;
291 pElement->cArgs = iArg + 1;
292 return rc;
293}
294
295
296RTDECL(void) RTVfsChainSpecFree(PRTVFSCHAINSPEC pSpec)
297{
298 if (!pSpec)
299 return;
300
301 uint32_t i = pSpec->cElements;
302 while (i-- > 0)
303 {
304 uint32_t iArg = pSpec->paElements[i].cArgs;
305 while (iArg-- > 0)
306 RTMemTmpFree(pSpec->paElements[i].paArgs[iArg].psz);
307 RTMemTmpFree(pSpec->paElements[i].paArgs);
308 RTMemTmpFree(pSpec->paElements[i].pszProvider);
309 if (pSpec->paElements[i].hVfsObj != NIL_RTVFSOBJ)
310 {
311 RTVfsObjRelease(pSpec->paElements[i].hVfsObj);
312 pSpec->paElements[i].hVfsObj = NIL_RTVFSOBJ;
313 }
314 }
315
316 RTMemTmpFree(pSpec->paElements);
317 pSpec->paElements = NULL;
318 RTMemTmpFree(pSpec);
319}
320
321
322/**
323 * Finds the end of the argument string.
324 *
325 * @returns The offset of the end character relative to @a psz.
326 * @param psz The argument string.
327 */
328static size_t rtVfsChainSpecFindArgEnd(const char *psz)
329{
330 char ch;
331 size_t off = 0;
332 while ( (ch = psz[off]) != '\0'
333 && ch != ','
334 && ch != ')'
335 && ch != '(')
336 {
337 /* check for escape sequences. */
338 if ( ch == '\\'
339 && (psz[off+1] == '(' || psz[off+1] == ')' || psz[off+1] == '\\' || psz[off+1] == ','))
340 off++;
341 off++;
342 }
343 return off;
344}
345
346
347RTDECL(int) RTVfsChainSpecParse(const char *pszSpec, uint32_t fFlags, RTVFSOBJTYPE enmDesiredType,
348 PRTVFSCHAINSPEC *ppSpec, const char **ppszError)
349{
350 AssertPtrNullReturn(ppszError, VERR_INVALID_POINTER);
351 if (ppszError)
352 *ppszError = NULL;
353 AssertPtrReturn(ppSpec, VERR_INVALID_POINTER);
354 *ppSpec = NULL;
355 AssertPtrReturn(pszSpec, VERR_INVALID_POINTER);
356 AssertReturn(!(fFlags & ~RTVFSCHAIN_PF_VALID_MASK), VERR_INVALID_PARAMETER);
357 AssertReturn(enmDesiredType > RTVFSOBJTYPE_INVALID && enmDesiredType < RTVFSOBJTYPE_END, VERR_INVALID_PARAMETER);
358
359 /*
360 * Check the start of the specification and allocate an empty return spec.
361 */
362 if (strncmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX, sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1))
363 return VERR_VFS_CHAIN_NO_PREFIX;
364 const char *pszSrc = RTStrStripL(pszSpec + sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1);
365 if (!*pszSrc)
366 return VERR_VFS_CHAIN_EMPTY;
367
368 PRTVFSCHAINSPEC pSpec = rtVfsChainSpecAlloc();
369 if (!pSpec)
370 return VERR_NO_TMP_MEMORY;
371 pSpec->enmDesiredType = enmDesiredType;
372
373 /*
374 * Parse the spec one element at a time.
375 */
376 int rc = VINF_SUCCESS;
377 while (*pszSrc && RT_SUCCESS(rc))
378 {
379 /*
380 * Digest element separator, except for the first element.
381 */
382 if (*pszSrc == '|' || *pszSrc == ':')
383 {
384 if (pSpec->cElements != 0)
385 pszSrc = RTStrStripL(pszSrc + 1);
386 else
387 {
388 rc = VERR_VFS_CHAIN_LEADING_SEPARATOR;
389 break;
390 }
391 }
392 else if (pSpec->cElements != 0)
393 {
394 rc = VERR_VFS_CHAIN_EXPECTED_SEPARATOR;
395 break;
396 }
397
398 /*
399 * Ok, there should be an element here so add one to the return struct.
400 */
401 PRTVFSCHAINELEMSPEC pElement = rtVfsChainSpecAddElement(pSpec, (uint16_t)(pszSrc - pszSpec), &rc);
402 if (!pElement)
403 break;
404
405 /*
406 * First up is the VFS object type followed by a parentheses,
407 * or this could be the trailing action.
408 */
409 size_t cch;
410 if (strncmp(pszSrc, "base", cch = 4) == 0)
411 pElement->enmType = RTVFSOBJTYPE_BASE;
412 else if (strncmp(pszSrc, "vfs", cch = 3) == 0)
413 pElement->enmType = RTVFSOBJTYPE_VFS;
414 else if (strncmp(pszSrc, "fss", cch = 3) == 0)
415 pElement->enmType = RTVFSOBJTYPE_FS_STREAM;
416 else if (strncmp(pszSrc, "ios", cch = 3) == 0)
417 pElement->enmType = RTVFSOBJTYPE_IO_STREAM;
418 else if (strncmp(pszSrc, "dir", cch = 3) == 0)
419 pElement->enmType = RTVFSOBJTYPE_DIR;
420 else if (strncmp(pszSrc, "file", cch = 4) == 0)
421 pElement->enmType = RTVFSOBJTYPE_FILE;
422 else if (strncmp(pszSrc, "sym", cch = 3) == 0)
423 pElement->enmType = RTVFSOBJTYPE_SYMLINK;
424 else
425 {
426 if (*pszSrc == '\0')
427 rc = VERR_VFS_CHAIN_TRAILING_SEPARATOR;
428 else
429 rc = VERR_VFS_CHAIN_UNKNOWN_TYPE;
430 break;
431 }
432 pszSrc += cch;
433
434 /* Check and skip the parentheses. */
435 if (*pszSrc != '(')
436 {
437 rc = VERR_VFS_CHAIN_EXPECTED_LEFT_PARENTHESES;
438 break;
439 }
440 pszSrc = RTStrStripL(pszSrc + 1);
441
442 /*
443 * The name of the element provider.
444 */
445 cch = rtVfsChainSpecFindArgEnd(pszSrc);
446 if (!cch)
447 {
448 rc = VERR_VFS_CHAIN_EXPECTED_PROVIDER_NAME;
449 break;
450 }
451 pElement->pszProvider = rtVfsChainSpecDupStrN(pszSrc, cch, &rc);
452 if (!pElement->pszProvider)
453 break;
454 pszSrc += cch;
455
456 /*
457 * The arguments.
458 */
459 while (*pszSrc == ',')
460 {
461 pszSrc = RTStrStripL(pszSrc + 1);
462 cch = rtVfsChainSpecFindArgEnd(pszSrc);
463 rc = rtVfsChainSpecElementAddArg(pElement, pszSrc, cch, (uint16_t)(pszSrc - pszSpec));
464 if (RT_FAILURE(rc))
465 break;
466 pszSrc += cch;
467 }
468 if (RT_FAILURE(rc))
469 break;
470
471 /* Must end with a right parentheses. */
472 if (*pszSrc != ')')
473 {
474 rc = VERR_VFS_CHAIN_EXPECTED_RIGHT_PARENTHESES;
475 break;
476 }
477 pElement->cchSpec = (uint16_t)(pszSrc - pszSpec) - pElement->offSpec + 1;
478
479 pszSrc = RTStrStripL(pszSrc + 1);
480 }
481
482#if 1
483 RTAssertMsg2("dbg: cElements=%d rc=%Rrc\n", pSpec->cElements, rc);
484 for (uint32_t i = 0; i < pSpec->cElements; i++)
485 {
486 uint32_t const cArgs = pSpec->paElements[i].cArgs;
487 RTAssertMsg2("dbg: #%u: enmTypeIn=%d enmType=%d cArgs=%d",
488 i, pSpec->paElements[i].enmTypeIn, pSpec->paElements[i].enmType, cArgs);
489 for (uint32_t j = 0; j < cArgs; j++)
490 RTAssertMsg2(j == 0 ? (cArgs > 1 ? " [%s" : " [%s]") : j + 1 < cArgs ? ", %s" : ", %s]",
491 pSpec->paElements[i].paArgs[j].psz);
492 //RTAssertMsg2(" offSpec=%d cchSpec=%d", pSpec->paElements[i].offSpec, pSpec->paElements[i].cchSpec);
493 RTAssertMsg2(" spec: %.*s\n", pSpec->paElements[i].cchSpec, &pszSpec[pSpec->paElements[i].offSpec]);
494 }
495#endif
496
497 /*
498 * Return the chain on success; Cleanup and set the error indicator on
499 * failure.
500 */
501 if (RT_SUCCESS(rc))
502 *ppSpec = pSpec;
503 else
504 {
505 if (ppszError)
506 *ppszError = pszSrc;
507 RTVfsChainSpecFree(pSpec);
508 }
509 return rc;
510}
511
512
513/**
514 * Looks up @a pszProvider among the registered providers.
515 *
516 * @returns Pointer to registration record if found, NULL if not.
517 * @param pszProvider The provider.
518 */
519static PCRTVFSCHAINELEMENTREG rtVfsChainFindProviderLocked(const char *pszProvider)
520{
521 PCRTVFSCHAINELEMENTREG pIterator;
522 RTListForEach(&g_rtVfsChainElementProviderList, pIterator, RTVFSCHAINELEMENTREG, ListEntry)
523 {
524 if (strcmp(pIterator->pszName, pszProvider) == 0)
525 return pIterator;
526 }
527 return NULL;
528}
529
530
531/**
532 * Does reusable object type matching.
533 *
534 * @returns true if the types matches, false if not.
535 * @param pElement The target element specification.
536 * @param pReuseElement The source element specification.
537 */
538static bool rtVfsChainMatchReusableType(PRTVFSCHAINELEMSPEC pElement, PRTVFSCHAINELEMSPEC pReuseElement)
539{
540 if (pElement->enmType == pReuseElement->enmType)
541 return true;
542
543 /* File objects can always be cast to I/O streams. */
544 if ( pElement->enmType == RTVFSOBJTYPE_IO_STREAM
545 && pReuseElement->enmType == RTVFSOBJTYPE_FILE)
546 return true;
547
548 /* I/O stream objects may be file objects. */
549 if ( pElement->enmType == RTVFSOBJTYPE_FILE
550 && pReuseElement->enmType == RTVFSOBJTYPE_IO_STREAM)
551 {
552 RTVFSFILE hVfsFile = RTVfsObjToFile(pReuseElement->hVfsObj);
553 if (hVfsFile != NIL_RTVFSFILE)
554 {
555 RTVfsFileRelease(hVfsFile);
556 return true;
557 }
558 }
559 return false;
560}
561
562
563RTDECL(int) RTVfsChainSpecCheckAndSetup(PRTVFSCHAINSPEC pSpec, PCRTVFSCHAINSPEC pReuseSpec,
564 PRTVFSOBJ phVfsObj, uint32_t *poffError)
565{
566 AssertPtrReturn(poffError, VERR_INVALID_POINTER);
567 *poffError = 0;
568 AssertPtrReturn(phVfsObj, VERR_INVALID_POINTER);
569 *phVfsObj = NIL_RTVFSOBJ;
570 AssertPtrReturn(pSpec, VERR_INVALID_POINTER);
571
572 /*
573 * Enter the critical section after making sure it has been initialized.
574 */
575 int rc = RTOnce(&g_rtVfsChainElementInitOnce, rtVfsChainElementRegisterInit, NULL);
576 if (RT_SUCCESS(rc))
577 rc = RTCritSectRwEnterShared(&g_rtVfsChainElementCritSect);
578 if (RT_SUCCESS(rc))
579 {
580 /*
581 * Resolve and check each element first.
582 */
583 for (uint32_t i = 0; i < pSpec->cElements; i++)
584 {
585 PRTVFSCHAINELEMSPEC const pElement = &pSpec->paElements[i];
586 *poffError = pElement->offSpec;
587 pElement->pProvider = rtVfsChainFindProviderLocked(pElement->pszProvider);
588 if (pElement->pProvider)
589 {
590 rc = pElement->pProvider->pfnValidate(pElement->pProvider, pSpec, pElement, poffError);
591 if (RT_SUCCESS(rc))
592 continue;
593 }
594 else
595 rc = VERR_VFS_CHAIN_PROVIDER_NOT_FOUND;
596 break;
597 }
598
599 /*
600 * Check that the desired type is compatible with the last element.
601 */
602 if (RT_SUCCESS(rc))
603 {
604 if (pSpec->cElements > 0) /* paranoia */
605 {
606 PRTVFSCHAINELEMSPEC const pLast = &pSpec->paElements[pSpec->cElements - 1];
607 if ( pLast->enmType == pSpec->enmDesiredType
608 || ( pLast->enmType == RTVFSOBJTYPE_FILE
609 && pSpec->enmDesiredType == RTVFSOBJTYPE_IO_STREAM) )
610 rc = VINF_SUCCESS;
611 else
612 {
613 *poffError = pLast->offSpec;
614 rc = VERR_VFS_CHAIN_FINAL_TYPE_MISMATCH;
615 }
616 }
617 else
618 rc = VERR_VFS_CHAIN_EMPTY;
619 }
620
621 if (RT_SUCCESS(rc))
622 {
623 /*
624 * Try construct the chain.
625 */
626 RTVFSOBJ hPrevVfsObj = NIL_RTVFSOBJ; /* No extra reference, kept in chain structure. */
627 for (uint32_t i = 0; i < pSpec->cElements; i++)
628 {
629 PRTVFSCHAINELEMSPEC const pElement = &pSpec->paElements[i];
630
631 /*
632 * Try reuse the VFS objects at the start of the passed in reuse chain.
633 */
634 if (!pReuseSpec)
635 { /* likely */ }
636 else
637 {
638 if (i < pReuseSpec->cElements)
639 {
640 PRTVFSCHAINELEMSPEC const pReuseElement = &pReuseSpec->paElements[i];
641 if (pReuseElement->hVfsObj != NIL_RTVFSOBJ)
642 {
643 if (strcmp(pElement->pszProvider, pReuseElement->pszProvider) == 0)
644 {
645 if (rtVfsChainMatchReusableType(pElement, pReuseElement))
646 {
647 if (pElement->pProvider->pfnCanReuseElement(pElement->pProvider, pSpec, pElement,
648 pReuseSpec, pReuseElement))
649 {
650 uint32_t cRefs = RTVfsObjRetain(pReuseElement->hVfsObj);
651 if (cRefs != UINT32_MAX)
652 {
653 pElement->hVfsObj = hPrevVfsObj = pReuseElement->hVfsObj;
654 continue;
655 }
656 }
657 }
658 }
659 }
660 }
661 pReuseSpec = NULL;
662 }
663
664 /*
665 * Instantiate a new VFS object.
666 */
667 RTVFSOBJ hVfsObj = NIL_RTVFSOBJ;
668 rc = pElement->pProvider->pfnInstantiate(pElement->pProvider, pSpec, pElement, hPrevVfsObj, &hVfsObj, poffError);
669 if (RT_FAILURE(rc))
670 break;
671 pElement->hVfsObj = hVfsObj;
672 hPrevVfsObj = hVfsObj;
673 }
674
675 /*
676 * Add another reference to the final object and return.
677 */
678 if (RT_SUCCESS(rc))
679 {
680 uint32_t cRefs = RTVfsObjRetain(hPrevVfsObj);
681 AssertStmt(cRefs != UINT32_MAX, rc = VERR_VFS_CHAIN_IPE);
682 *phVfsObj = hPrevVfsObj;
683 }
684 }
685
686 int rc2 = RTCritSectRwLeaveShared(&g_rtVfsChainElementCritSect);
687 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
688 rc = rc2;
689 }
690 return rc;
691}
692
693
694RTDECL(int) RTVfsChainElementDeregisterProvider(PRTVFSCHAINELEMENTREG pRegRec, bool fFromDtor)
695{
696 /*
697 * Fend off wildlife.
698 */
699 if (pRegRec == NULL)
700 return VINF_SUCCESS;
701 AssertPtrReturn(pRegRec, VERR_INVALID_POINTER);
702 AssertMsgReturn(pRegRec->uVersion == RTVFSCHAINELEMENTREG_VERSION, ("%#x", pRegRec->uVersion), VERR_INVALID_POINTER);
703 AssertMsgReturn(pRegRec->uEndMarker == RTVFSCHAINELEMENTREG_VERSION, ("%#zx", pRegRec->uEndMarker), VERR_INVALID_POINTER);
704 AssertPtrReturn(pRegRec->pszName, VERR_INVALID_POINTER);
705
706 /*
707 * Take the lock if that's safe.
708 */
709 if (!fFromDtor)
710 RTCritSectRwEnterExcl(&g_rtVfsChainElementCritSect);
711 else if (!g_rtVfsChainElementProviderList.pNext)
712 RTListInit(&g_rtVfsChainElementProviderList);
713
714 /*
715 * Ok, remove it.
716 */
717 int rc = VERR_NOT_FOUND;
718 PRTVFSCHAINELEMENTREG pIterator, pIterNext;
719 RTListForEachSafe(&g_rtVfsChainElementProviderList, pIterator, pIterNext, RTVFSCHAINELEMENTREG, ListEntry)
720 {
721 if (pIterator == pRegRec)
722 {
723 RTListNodeRemove(&pRegRec->ListEntry);
724 rc = VINF_SUCCESS;
725 break;
726 }
727 }
728
729 /*
730 * Leave the lock and return.
731 */
732 if (!fFromDtor)
733 RTCritSectRwLeaveExcl(&g_rtVfsChainElementCritSect);
734 return rc;
735}
736
737
738RTDECL(int) RTVfsChainOpenFile(const char *pszSpec, uint64_t fOpen, PRTVFSFILE phVfsFile, const char **ppszError)
739{
740 AssertPtrReturn(pszSpec, VERR_INVALID_POINTER);
741 AssertReturn(*pszSpec != '\0', VERR_INVALID_PARAMETER);
742 AssertPtrReturn(phVfsFile, VERR_INVALID_POINTER);
743 if (ppszError)
744 *ppszError = NULL;
745
746 /*
747 * If it's not a VFS chain spec, treat it as a file.
748 */
749 int rc;
750 if (strncmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX, sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1))
751 {
752 RTFILE hFile;
753 rc = RTFileOpen(&hFile, pszSpec, fOpen);
754 if (RT_SUCCESS(rc))
755 {
756 RTVFSFILE hVfsFile;
757 rc = RTVfsFileFromRTFile(hFile, fOpen, false /*fLeaveOpen*/, &hVfsFile);
758 if (RT_SUCCESS(rc))
759 *phVfsFile = hVfsFile;
760 else
761 RTFileClose(hFile);
762 }
763 }
764 else
765 {
766 PRTVFSCHAINSPEC pSpec;
767 rc = RTVfsChainSpecParse(pszSpec, 0 /*fFlags*/, RTVFSOBJTYPE_FILE, &pSpec, ppszError);
768 if (RT_SUCCESS(rc))
769 {
770 pSpec->fOpenFile = fOpen;
771
772 uint32_t offError = 0;
773 RTVFSOBJ hVfsObj = NIL_RTVFSOBJ;
774 rc = RTVfsChainSpecCheckAndSetup(pSpec, NULL /*pReuseSpec*/, &hVfsObj, &offError);
775 if (RT_SUCCESS(rc))
776 {
777 *phVfsFile = RTVfsObjToFile(hVfsObj);
778 if (*phVfsFile)
779 rc = VINF_SUCCESS;
780 else
781 rc = VERR_VFS_CHAIN_CAST_FAILED;
782 RTVfsObjRelease(hVfsObj);
783 }
784 else if (ppszError)
785 *ppszError = &pszSpec[offError];
786
787 RTVfsChainSpecFree(pSpec);
788 }
789 }
790 return rc;
791}
792
793
794RTDECL(int) RTVfsChainOpenIoStream(const char *pszSpec, uint64_t fOpen, PRTVFSIOSTREAM phVfsIos, const char **ppszError)
795{
796 if (ppszError)
797 *ppszError = NULL;
798
799 AssertPtrReturn(pszSpec, VERR_INVALID_POINTER);
800 AssertReturn(*pszSpec != '\0', VERR_INVALID_PARAMETER);
801 AssertPtrReturn(phVfsIos, VERR_INVALID_POINTER);
802
803 /*
804 * If it's not a VFS chain spec, treat it as a file.
805 */
806 int rc;
807 if (strncmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX, sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1))
808 {
809 RTFILE hFile;
810 rc = RTFileOpen(&hFile, pszSpec, fOpen);
811 if (RT_SUCCESS(rc))
812 {
813 RTVFSFILE hVfsFile;
814 rc = RTVfsFileFromRTFile(hFile, fOpen, false /*fLeaveOpen*/, &hVfsFile);
815 if (RT_SUCCESS(rc))
816 {
817 *phVfsIos = RTVfsFileToIoStream(hVfsFile);
818 RTVfsFileRelease(hVfsFile);
819 }
820 else
821 RTFileClose(hFile);
822 }
823 }
824 /*
825 * Do the chain thing.
826 */
827 else
828 {
829 PRTVFSCHAINSPEC pSpec;
830 rc = RTVfsChainSpecParse(pszSpec, 0 /*fFlags*/, RTVFSOBJTYPE_IO_STREAM, &pSpec, ppszError);
831 if (RT_SUCCESS(rc))
832 {
833 pSpec->fOpenFile = fOpen;
834
835 uint32_t offError = 0;
836 RTVFSOBJ hVfsObj = NIL_RTVFSOBJ;
837 rc = RTVfsChainSpecCheckAndSetup(pSpec, NULL /*pReuseSpec*/, &hVfsObj, &offError);
838 if (RT_SUCCESS(rc))
839 {
840 *phVfsIos = RTVfsObjToIoStream(hVfsObj);
841 if (*phVfsIos)
842 rc = VINF_SUCCESS;
843 else
844 rc = VERR_VFS_CHAIN_CAST_FAILED;
845 RTVfsObjRelease(hVfsObj);
846 }
847 else if (ppszError)
848 *ppszError = &pszSpec[offError];
849
850 RTVfsChainSpecFree(pSpec);
851 }
852 }
853 return rc;
854}
855
856
857
858RTDECL(bool) RTVfsChainIsSpec(const char *pszSpec)
859{
860 return pszSpec
861 && strcmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX) == 0;
862}
863
Note: See TracBrowser for help on using the repository browser.

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