VirtualBox

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

Last change on this file since 42072 was 39515, checked in by vboxsync, 13 years ago

*: Use RTLISTANCHOR.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 20.3 KB
Line 
1/* $Id: vfschain.cpp 39515 2011-12-02 13:41:07Z vboxsync $ */
2/** @file
3 * IPRT - Virtual File System, Chains.
4 */
5
6/*
7 * Copyright (C) 2010 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;
56/** Critical section protecting g_rtVfsChainElementProviderList. */
57static RTCRITSECT g_rtVfsChainElementCritSect;
58/** List of VFS chain element providers (RTVFSCHAINELEMENTREG). */
59static RTLISTANCHOR g_rtVfsChainElementProviderList;
60
61
62/**
63 * Initializes the globals via RTOnce.
64 *
65 * @returns IPRT status code
66 * @param pvUser1 Unused, ignored.
67 * @param pvUser2 Unused, ignored.
68 */
69static DECLCALLBACK(int) rtVfsChainElementRegisterInit(void *pvUser1, void *pvUser2)
70{
71 NOREF(pvUser1);
72 NOREF(pvUser2);
73 return RTCritSectInit(&g_rtVfsChainElementCritSect);
74}
75
76
77RTDECL(int) RTVfsChainElementRegisterProvider(PRTVFSCHAINELEMENTREG pRegRec, bool fFromCtor)
78{
79 int rc;
80
81 /*
82 * Input validation.
83 */
84 AssertPtrReturn(pRegRec, VERR_INVALID_POINTER);
85 AssertMsgReturn(pRegRec->uVersion == RTVFSCHAINELEMENTREG_VERSION, ("%#x", pRegRec->uVersion), VERR_INVALID_POINTER);
86 AssertMsgReturn(pRegRec->uEndMarker == RTVFSCHAINELEMENTREG_VERSION, ("%#zx", pRegRec->uEndMarker), VERR_INVALID_POINTER);
87 AssertReturn(pRegRec->fReserved == 0, VERR_INVALID_POINTER);
88 AssertPtrReturn(pRegRec->pszName, VERR_INVALID_POINTER);
89 AssertPtrNullReturn(pRegRec->pfnOpenVfs, VERR_INVALID_POINTER);
90 AssertPtrNullReturn(pRegRec->pfnOpenDir, VERR_INVALID_POINTER);
91 AssertPtrNullReturn(pRegRec->pfnOpenFile, VERR_INVALID_POINTER);
92 AssertPtrNullReturn(pRegRec->pfnOpenIoStream, VERR_INVALID_POINTER);
93 AssertPtrNullReturn(pRegRec->pfnOpenFsStream, VERR_INVALID_POINTER);
94
95 /*
96 * Init and take the lock.
97 */
98 if (!fFromCtor)
99 {
100 rc = RTOnce(&g_rtVfsChainElementInitOnce, rtVfsChainElementRegisterInit, NULL, NULL);
101 if (RT_FAILURE(rc))
102 return rc;
103 rc = RTCritSectEnter(&g_rtVfsChainElementCritSect);
104 if (RT_FAILURE(rc))
105 return rc;
106 }
107
108 /*
109 * Duplicate name?
110 */
111 rc = VINF_SUCCESS;
112 PRTVFSCHAINELEMENTREG pIterator, pIterNext;
113 RTListForEachSafe(&g_rtVfsChainElementProviderList, pIterator, pIterNext, RTVFSCHAINELEMENTREG, ListEntry)
114 {
115 if (!strcmp(pIterator->pszName, pRegRec->pszName))
116 {
117 AssertMsgFailed(("duplicate name '%s' old=%p new=%p\n", pIterator->pszName, pIterator, pRegRec));
118 rc = VERR_ALREADY_EXISTS;
119 break;
120 }
121 }
122
123 /*
124 * If not, append the record to the list.
125 */
126 if (RT_SUCCESS(rc))
127 RTListAppend(&g_rtVfsChainElementProviderList, &pRegRec->ListEntry);
128
129 /*
130 * Leave the lock and return.
131 */
132 if (!fFromCtor)
133 RTCritSectLeave(&g_rtVfsChainElementCritSect);
134 return rc;
135}
136
137
138/**
139 * Allocates and initializes an empty spec
140 *
141 * @returns Pointer to the spec on success, NULL on failure.
142 */
143static PRTVFSCHAINSPEC rtVfsChainSpecAlloc(void)
144{
145 PRTVFSCHAINSPEC pSpec = (PRTVFSCHAINSPEC)RTMemTmpAlloc(sizeof(*pSpec));
146 if (pSpec)
147 {
148 pSpec->iActionElement = UINT32_MAX;
149 pSpec->cElements = 0;
150 pSpec->paElements = NULL;
151 }
152 return pSpec;
153}
154
155
156/**
157 * Duplicate a spec string.
158 *
159 * This differs from RTStrDupN in that it uses RTMemTmpAlloc instead of
160 * RTMemAlloc.
161 *
162 * @returns String copy on success, NULL on failure.
163 * @param psz The string to duplicate.
164 * @param cch The number of bytes to duplicate.
165 * @param prc The status code variable to set on failure. (Leeps the
166 * code shorter. -lazy bird)
167 */
168DECLINLINE(char *) rtVfsChainSpecDupStrN(const char *psz, size_t cch, int *prc)
169{
170 char *pszCopy = (char *)RTMemTmpAlloc(cch + 1);
171 if (pszCopy)
172 {
173 if (!memchr(psz, '\\', cch))
174 {
175 /* Plain string, copy it raw. */
176 memcpy(pszCopy, psz, cch);
177 pszCopy[cch] = '\0';
178 }
179 else
180 {
181 /* Has escape sequences, must unescape it. */
182 char *pszDst = pszCopy;
183 while (cch)
184 {
185 char ch = *psz++;
186 if (ch == '\\')
187 {
188 char ch2 = psz[2];
189 if (ch2 == '(' || ch2 == ')' || ch2 == '\\' || ch2 == ',')
190 {
191 psz++;
192 ch = ch2;
193 }
194 }
195 *pszDst++ = ch;
196 }
197 *pszDst = '\0';
198 }
199 }
200 else
201 *prc = VERR_NO_TMP_MEMORY;
202 return pszCopy;
203}
204
205
206/**
207 * Adds an empty element to the chain specification.
208 *
209 * The caller is responsible for filling it the element attributes.
210 *
211 * @returns Pointer to the new element on success, NULL on failure. The
212 * pointer is only valid till the next call to this function.
213 * @param pSpec The chain specification.
214 * @param prc The status code variable to set on failure. (Leeps the
215 * code shorter. -lazy bird)
216 */
217static PRTVFSCHAINELEMSPEC rtVfsChainSpecAddElement(PRTVFSCHAINSPEC pSpec, int *prc)
218{
219 AssertPtr(pSpec);
220
221 /*
222 * Resize the element table if necessary.
223 */
224 uint32_t const iElement = pSpec->cElements;
225 if ((iElement % 32) == 0)
226 {
227 PRTVFSCHAINELEMSPEC paNew = (PRTVFSCHAINELEMSPEC)RTMemTmpAlloc((iElement + 32) * sizeof(paNew[0]));
228 if (!paNew)
229 {
230 *prc = VERR_NO_TMP_MEMORY;
231 return NULL;
232 }
233
234 memcpy(paNew, pSpec->paElements, iElement * sizeof(paNew[0]));
235 RTMemTmpFree(pSpec->paElements);
236 pSpec->paElements = paNew;
237 }
238
239 /*
240 * Initialize and add the new element.
241 */
242 PRTVFSCHAINELEMSPEC pElement = &pSpec->paElements[iElement];
243 pElement->pszProvider = NULL;
244 pElement->enmTypeIn = iElement ? pSpec->paElements[iElement - 1].enmTypeOut : RTVFSOBJTYPE_INVALID;
245 pElement->enmTypeOut = RTVFSOBJTYPE_INVALID;
246 pElement->enmAction = RTVFSCHAINACTION_INVALID;
247 pElement->cArgs = 0;
248 pElement->papszArgs = 0;
249
250 pSpec->cElements = iElement + 1;
251 return pElement;
252}
253
254
255/**
256 * Adds an argument to the element spec.
257 *
258 * @returns IPRT status code.
259 * @param pElement The element.
260 * @param psz The start of the argument string.
261 * @param cch The length of the argument string, escape
262 * sequences counted twice.
263 */
264static int rtVfsChainSpecElementAddArg(PRTVFSCHAINELEMSPEC pElement, const char *psz, size_t cch)
265{
266 uint32_t iArg = pElement->cArgs;
267 if ((iArg % 32) == 0)
268 {
269 char **papszNew = (char **)RTMemTmpAlloc((iArg + 32 + 1) * sizeof(papszNew[0]));
270 if (!papszNew)
271 return VERR_NO_TMP_MEMORY;
272 memcpy(papszNew, pElement->papszArgs, iArg * sizeof(papszNew[0]));
273 RTMemTmpFree(pElement->papszArgs);
274 pElement->papszArgs = papszNew;
275 }
276
277 int rc = VINF_SUCCESS;
278 pElement->papszArgs[iArg] = rtVfsChainSpecDupStrN(psz, cch, &rc);
279 pElement->papszArgs[iArg + 1] = NULL;
280 pElement->cArgs = iArg + 1;
281 return rc;
282}
283
284
285RTDECL(void) RTVfsChainSpecFree(PRTVFSCHAINSPEC pSpec)
286{
287 if (!pSpec)
288 return;
289
290 uint32_t i = pSpec->cElements;
291 while (i-- > 0)
292 {
293 uint32_t iArg = pSpec->paElements[i].cArgs;
294 while (iArg-- > 0)
295 RTMemTmpFree(pSpec->paElements[i].papszArgs[iArg]);
296 RTMemTmpFree(pSpec->paElements[i].papszArgs);
297 RTMemTmpFree(pSpec->paElements[i].pszProvider);
298 }
299
300 RTMemTmpFree(pSpec->paElements);
301 pSpec->paElements = NULL;
302 RTMemTmpFree(pSpec);
303}
304
305
306/**
307 * Finds the end of the argument string.
308 *
309 * @returns The offset of the end character relative to @a psz.
310 * @param psz The argument string.
311 */
312static size_t rtVfsChainSpecFindArgEnd(const char *psz)
313{
314 char ch;
315 size_t off = 0;
316 while ( (ch = psz[off]) != '\0'
317 && ch != ','
318 && ch != ')'
319 && ch != '(')
320 {
321 /* check for escape sequences. */
322 if ( ch == '\\'
323 && (psz[off+1] == '(' || psz[off+1] == ')' || psz[off+1] == '\\' || psz[off+1] == ','))
324 off++;
325 off++;
326 }
327 return off;
328}
329
330/**
331 * Look for action.
332 *
333 * @returns Action.
334 * @param pszSpec The current spec position.
335 * @param pcchAction Where to return the length of the action
336 * string.
337 */
338static RTVFSCHAINACTION rtVfsChainSpecEatAction(const char *pszSpec, size_t *pcchAction)
339{
340 switch (*pszSpec)
341 {
342 case '|':
343 *pcchAction = 1;
344 return RTVFSCHAINACTION_PASSIVE;
345 case '>':
346 *pcchAction = 1;
347 return RTVFSCHAINACTION_PUSH;
348 default:
349 *pcchAction = 0;
350 return RTVFSCHAINACTION_NONE;
351 }
352}
353
354
355RTDECL(int) RTVfsChainSpecParse(const char *pszSpec, uint32_t fFlags, RTVFSCHAINACTION enmLeadingAction,
356 RTVFSCHAINACTION enmTrailingAction,
357 PRTVFSCHAINSPEC *ppSpec, const char **ppszError)
358{
359 AssertPtrNullReturn(ppszError, VERR_INVALID_POINTER);
360 if (ppszError)
361 *ppszError = NULL;
362 AssertPtrReturn(ppSpec, VERR_INVALID_POINTER);
363 *ppSpec = NULL;
364 AssertPtrReturn(pszSpec, VERR_INVALID_POINTER);
365 AssertReturn(!(fFlags & ~RTVFSCHAIN_PF_VALID_MASK), VERR_INVALID_PARAMETER);
366 AssertReturn(enmLeadingAction > RTVFSCHAINACTION_INVALID && enmLeadingAction < RTVFSCHAINACTION_END, VERR_INVALID_PARAMETER);
367
368 /*
369 * Check the start of the specification and allocate an empty return spec.
370 */
371 if (strncmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX, sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1))
372 return VERR_VFS_CHAIN_NO_PREFIX;
373 pszSpec = RTStrStripL(pszSpec + sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1);
374 if (!*pszSpec)
375 return VERR_VFS_CHAIN_EMPTY;
376
377 PRTVFSCHAINSPEC pSpec = rtVfsChainSpecAlloc();
378 if (!pSpec)
379 return VERR_NO_TMP_MEMORY;
380
381 /*
382 * Parse the spec one element at a time.
383 */
384 int rc = VINF_SUCCESS;
385 const char *pszSrc = pszSpec;
386 while (*pszSrc && RT_SUCCESS(rc))
387 {
388 /*
389 * Pipe or redirection action symbol, except maybe the first time.
390 * The pipe symbol may occur at the end of the spec.
391 */
392 size_t cch;
393 RTVFSCHAINACTION enmAction = rtVfsChainSpecEatAction(pszSpec, &cch);
394 if (enmAction != RTVFSCHAINACTION_NONE)
395 {
396 pszSrc = RTStrStripL(pszSrc + cch);
397 if (!*pszSrc)
398 {
399 /* Fail if the caller does not approve of a trailing pipe (all
400 other actions non-trailing). */
401 if ( enmAction != enmTrailingAction
402 && !(fFlags & RTVFSCHAIN_PF_TRAILING_ACTION_OPTIONAL))
403 rc = VERR_VFS_CHAIN_EXPECTED_ELEMENT;
404 break;
405 }
406
407 /* There can only be one real action atm. */
408 if (enmAction != RTVFSCHAINACTION_PASSIVE)
409 {
410 if (pSpec->iActionElement != UINT32_MAX)
411 {
412 rc = VERR_VFS_CHAIN_MULTIPLE_ACTIONS;
413 break;
414 }
415 pSpec->iActionElement = pSpec->cElements;
416 }
417 }
418 else if (pSpec->cElements > 0)
419 {
420 rc = VERR_VFS_CHAIN_EXPECTED_ACTION;
421 break;
422 }
423
424 /* Check the leading action. */
425 if ( pSpec->cElements == 0
426 && enmAction != enmLeadingAction
427 && !(fFlags & RTVFSCHAIN_PF_LEADING_ACTION_OPTIONAL))
428 {
429 rc = VERR_VFS_CHAIN_UNEXPECTED_ACTION_TYPE;
430 break;
431 }
432
433 /*
434 * Ok, there should be an element here so add one to the return struct.
435 */
436 PRTVFSCHAINELEMSPEC pElement = rtVfsChainSpecAddElement(pSpec, &rc);
437 if (!pElement)
438 break;
439 pElement->enmAction = enmAction;
440
441 /*
442 * First up is the VFS object type followed by a parentheses.
443 */
444 if (strncmp(pszSrc, "base", cch = 4) == 0)
445 pElement->enmTypeOut = RTVFSOBJTYPE_BASE;
446 else if (strncmp(pszSrc, "vfs", cch = 3) == 0)
447 pElement->enmTypeOut = RTVFSOBJTYPE_VFS;
448 else if (strncmp(pszSrc, "fss", cch = 3) == 0)
449 pElement->enmTypeOut = RTVFSOBJTYPE_FS_STREAM;
450 else if (strncmp(pszSrc, "ios", cch = 3) == 0)
451 pElement->enmTypeOut = RTVFSOBJTYPE_IO_STREAM;
452 else if (strncmp(pszSrc, "dir", cch = 3) == 0)
453 pElement->enmTypeOut = RTVFSOBJTYPE_DIR;
454 else if (strncmp(pszSrc, "file", cch = 4) == 0)
455 pElement->enmTypeOut = RTVFSOBJTYPE_FILE;
456 else if (strncmp(pszSrc, "sym", cch = 3) == 0)
457 pElement->enmTypeOut = RTVFSOBJTYPE_SYMLINK;
458 else
459 {
460 rc = VERR_VFS_CHAIN_UNKNOWN_TYPE;
461 break;
462 }
463 pszSrc += cch;
464
465 /* Check and skip the parentheses. */
466 if (*pszSrc != '(')
467 {
468 rc = VERR_VFS_CHAIN_EXPECTED_LEFT_PARENTHESES;
469 break;
470 }
471 pszSrc = RTStrStripL(pszSrc + 1);
472
473 /*
474 * The name of the element provider.
475 */
476 cch = rtVfsChainSpecFindArgEnd(pszSrc);
477 if (!cch)
478 {
479 rc = VERR_VFS_CHAIN_EXPECTED_PROVIDER_NAME;
480 break;
481 }
482 pElement->pszProvider = rtVfsChainSpecDupStrN(pszSrc, cch, &rc);
483 if (!pElement->pszProvider)
484 break;
485 pszSrc += cch;
486
487 /*
488 * The arguments.
489 */
490 while (*pszSrc == ',')
491 {
492 pszSrc = RTStrStripL(pszSrc + 1);
493 cch = rtVfsChainSpecFindArgEnd(pszSrc);
494 rc = rtVfsChainSpecElementAddArg(pElement, pszSrc, cch);
495 pszSrc += cch;
496 }
497
498 /* Must end with a right parentheses. */
499 if (*pszSrc != ')')
500 {
501 rc = VERR_VFS_CHAIN_EXPECTED_RIGHT_PARENTHESES;
502 break;
503 }
504 pszSrc = RTStrStripL(pszSrc + 1);
505 }
506
507 /*
508 * Return the chain on success; Cleanup and set the error indicator on
509 * failure.
510 */
511 if (RT_SUCCESS(rc))
512 *ppSpec = pSpec;
513 else
514 {
515 if (ppszError)
516 *ppszError = pszSrc;
517 RTVfsChainSpecFree(pSpec);
518 }
519 return rc;
520}
521
522
523
524
525
526RTDECL(int) RTVfsChainElementDeregisterProvider(PRTVFSCHAINELEMENTREG pRegRec, bool fFromDtor)
527{
528 /*
529 * Fend off wildlife.
530 */
531 if (pRegRec == NULL)
532 return VINF_SUCCESS;
533 AssertPtrReturn(pRegRec, VERR_INVALID_POINTER);
534 AssertMsgReturn(pRegRec->uVersion == RTVFSCHAINELEMENTREG_VERSION, ("%#x", pRegRec->uVersion), VERR_INVALID_POINTER);
535 AssertMsgReturn(pRegRec->uEndMarker == RTVFSCHAINELEMENTREG_VERSION, ("%#zx", pRegRec->uEndMarker), VERR_INVALID_POINTER);
536 AssertPtrReturn(pRegRec->pszName, VERR_INVALID_POINTER);
537
538 /*
539 * Take the lock if that's safe.
540 */
541 if (!fFromDtor)
542 RTCritSectEnter(&g_rtVfsChainElementCritSect);
543
544 /*
545 * Ok, remove it.
546 */
547 int rc = VERR_NOT_FOUND;
548 PRTVFSCHAINELEMENTREG pIterator, pIterNext;
549 RTListForEachSafe(&g_rtVfsChainElementProviderList, pIterator, pIterNext, RTVFSCHAINELEMENTREG, ListEntry)
550 {
551 if (pIterator == pRegRec)
552 {
553 RTListNodeRemove(&pRegRec->ListEntry);
554 rc = VINF_SUCCESS;
555 break;
556 }
557 }
558
559 /*
560 * Leave the lock and return.
561 */
562 if (!fFromDtor)
563 RTCritSectLeave(&g_rtVfsChainElementCritSect);
564 return rc;
565}
566
567
568RTDECL(int) RTVfsChainOpenFile(const char *pszSpec, uint64_t fOpen, PRTVFSFILE phVfsFile, const char **ppszError)
569{
570 AssertPtrReturn(pszSpec, VERR_INVALID_POINTER);
571 AssertReturn(*pszSpec != '\0', VERR_INVALID_PARAMETER);
572 AssertPtrReturn(phVfsFile, VERR_INVALID_POINTER);
573 if (ppszError)
574 *ppszError = NULL;
575
576 /*
577 * If it's not a VFS chain spec, treat it as a file.
578 */
579 int rc;
580 if (strncmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX, sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1))
581 {
582 RTFILE hFile;
583 rc = RTFileOpen(&hFile, pszSpec, fOpen);
584 if (RT_SUCCESS(rc))
585 {
586 RTVFSFILE hVfsFile;
587 rc = RTVfsFileFromRTFile(hFile, fOpen, false /*fLeaveOpen*/, &hVfsFile);
588 if (RT_SUCCESS(rc))
589 *phVfsFile = hVfsFile;
590 else
591 RTFileClose(hFile);
592 }
593 }
594 else
595 {
596 PRTVFSCHAINSPEC pSpec;
597 rc = RTVfsChainSpecParse(pszSpec,
598 RTVFSCHAIN_PF_NO_REAL_ACTION
599 | RTVFSCHAIN_PF_LEADING_ACTION_OPTIONAL,
600 RTVFSCHAINACTION_PASSIVE,
601 RTVFSCHAINACTION_NONE,
602 &pSpec,
603 ppszError);
604 if (RT_SUCCESS(rc))
605 {
606 /** @todo implement this when needed. */
607 rc = VERR_NOT_IMPLEMENTED;
608 RTVfsChainSpecFree(pSpec);
609 }
610 }
611 return rc;
612}
613
614
615RTDECL(int) RTVfsChainOpenIoStream(const char *pszSpec, uint64_t fOpen, PRTVFSIOSTREAM phVfsIos, const char **ppszError)
616{
617 AssertPtrReturn(pszSpec, VERR_INVALID_POINTER);
618 AssertReturn(*pszSpec != '\0', VERR_INVALID_PARAMETER);
619 AssertPtrReturn(phVfsIos, VERR_INVALID_POINTER);
620 if (ppszError)
621 *ppszError = NULL;
622
623 /*
624 * If it's not a VFS chain spec, treat it as a file.
625 */
626 int rc;
627 if (strncmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX, sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1))
628 {
629 RTFILE hFile;
630 rc = RTFileOpen(&hFile, pszSpec, fOpen);
631 if (RT_SUCCESS(rc))
632 {
633 RTVFSFILE hVfsFile;
634 rc = RTVfsFileFromRTFile(hFile, fOpen, false /*fLeaveOpen*/, &hVfsFile);
635 if (RT_SUCCESS(rc))
636 {
637 *phVfsIos = RTVfsFileToIoStream(hVfsFile);
638 RTVfsFileRelease(hVfsFile);
639 }
640 else
641 RTFileClose(hFile);
642 }
643 }
644 else
645 {
646 PRTVFSCHAINSPEC pSpec;
647 rc = RTVfsChainSpecParse(pszSpec,
648 RTVFSCHAIN_PF_NO_REAL_ACTION
649 | RTVFSCHAIN_PF_LEADING_ACTION_OPTIONAL,
650 RTVFSCHAINACTION_PASSIVE,
651 RTVFSCHAINACTION_NONE,
652 &pSpec,
653 ppszError);
654 if (RT_SUCCESS(rc))
655 {
656
657
658 rc = VERR_NOT_IMPLEMENTED;
659 RTVfsChainSpecFree(pSpec);
660 }
661 }
662 return rc;
663}
664
665
666
667RTDECL(bool) RTVfsChainIsSpec(const char *pszSpec)
668{
669 return pszSpec
670 && strcmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX) == 0;
671}
672
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