VirtualBox

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

Last change on this file since 36596 was 34179, checked in by vboxsync, 14 years ago

iprt/tarvfs: Rewrote the tar parser to deal with header sequences used by solaris, gnu and pax while being offline yesterday. Only GNU long link and long names are supported, but adding pax and solaris support is possible. This also resolves some of the ustar<space><space> vs. ustart<null>00 problems (the latter was not readable previously).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 20.2 KB
Line 
1/* $Id: vfschain.cpp 34179 2010-11-18 15:49:17Z 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. */
59static RTLISTNODE 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 * Cleanup and set the error indicator on failure.
509 */
510 if (RT_FAILURE(rc))
511 {
512 if (ppszError)
513 *ppszError = pszSrc;
514 RTVfsChainSpecFree(pSpec);
515 }
516 return rc;
517}
518
519
520
521
522
523RTDECL(int) RTVfsChainElementDeregisterProvider(PRTVFSCHAINELEMENTREG pRegRec, bool fFromDtor)
524{
525 /*
526 * Fend off wildlife.
527 */
528 if (pRegRec == NULL)
529 return VINF_SUCCESS;
530 AssertPtrReturn(pRegRec, VERR_INVALID_POINTER);
531 AssertMsgReturn(pRegRec->uVersion == RTVFSCHAINELEMENTREG_VERSION, ("%#x", pRegRec->uVersion), VERR_INVALID_POINTER);
532 AssertMsgReturn(pRegRec->uEndMarker == RTVFSCHAINELEMENTREG_VERSION, ("%#zx", pRegRec->uEndMarker), VERR_INVALID_POINTER);
533 AssertPtrReturn(pRegRec->pszName, VERR_INVALID_POINTER);
534
535 /*
536 * Take the lock if that's safe.
537 */
538 if (!fFromDtor)
539 RTCritSectEnter(&g_rtVfsChainElementCritSect);
540
541 /*
542 * Ok, remove it.
543 */
544 int rc = VERR_NOT_FOUND;
545 PRTVFSCHAINELEMENTREG pIterator, pIterNext;
546 RTListForEachSafe(&g_rtVfsChainElementProviderList, pIterator, pIterNext, RTVFSCHAINELEMENTREG, ListEntry)
547 {
548 if (pIterator == pRegRec)
549 {
550 RTListNodeRemove(&pRegRec->ListEntry);
551 rc = VINF_SUCCESS;
552 break;
553 }
554 }
555
556 /*
557 * Leave the lock and return.
558 */
559 if (!fFromDtor)
560 RTCritSectLeave(&g_rtVfsChainElementCritSect);
561 return rc;
562}
563
564
565RTDECL(int) RTVfsChainOpenFile(const char *pszSpec, uint32_t fOpen, PRTVFSFILE phVfsFile, const char **ppszError)
566{
567 AssertPtrReturn(pszSpec, VERR_INVALID_POINTER);
568 AssertReturn(*pszSpec != '\0', VERR_INVALID_PARAMETER);
569 AssertPtrReturn(phVfsFile, VERR_INVALID_POINTER);
570 if (ppszError)
571 *ppszError = NULL;
572
573 /*
574 * If it's not a VFS chain spec, treat it as a file.
575 */
576 int rc;
577 if (strncmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX, sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1))
578 {
579 RTFILE hFile;
580 rc = RTFileOpen(&hFile, pszSpec, fOpen);
581 if (RT_SUCCESS(rc))
582 {
583 RTVFSFILE hVfsFile;
584 rc = RTVfsFileFromRTFile(hFile, fOpen, false /*fLeaveOpen*/, &hVfsFile);
585 if (RT_SUCCESS(rc))
586 *phVfsFile = hVfsFile;
587 else
588 RTFileClose(hFile);
589 }
590 }
591 else
592 {
593 PRTVFSCHAINSPEC pSpec;
594 rc = RTVfsChainSpecParse(pszSpec,
595 RTVFSCHAIN_PF_NO_REAL_ACTION
596 | RTVFSCHAIN_PF_LEADING_ACTION_OPTIONAL,
597 RTVFSCHAINACTION_PASSIVE,
598 RTVFSCHAINACTION_NONE,
599 &pSpec,
600 ppszError);
601 if (RT_SUCCESS(rc))
602 {
603 /** @todo implement this when needed. */
604 rc = VERR_NOT_IMPLEMENTED;
605 RTVfsChainSpecFree(pSpec);
606 }
607 }
608 return rc;
609}
610
611
612RTDECL(int) RTVfsChainOpenIoStream(const char *pszSpec, uint32_t fOpen, PRTVFSIOSTREAM phVfsIos, const char **ppszError)
613{
614 AssertPtrReturn(pszSpec, VERR_INVALID_POINTER);
615 AssertReturn(*pszSpec != '\0', VERR_INVALID_PARAMETER);
616 AssertPtrReturn(phVfsIos, VERR_INVALID_POINTER);
617 if (ppszError)
618 *ppszError = NULL;
619
620 /*
621 * If it's not a VFS chain spec, treat it as a file.
622 */
623 int rc;
624 if (strncmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX, sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1))
625 {
626 RTFILE hFile;
627 rc = RTFileOpen(&hFile, pszSpec, fOpen);
628 if (RT_SUCCESS(rc))
629 {
630 RTVFSFILE hVfsFile;
631 rc = RTVfsFileFromRTFile(hFile, fOpen, false /*fLeaveOpen*/, &hVfsFile);
632 if (RT_SUCCESS(rc))
633 {
634 *phVfsIos = RTVfsFileToIoStream(hVfsFile);
635 RTVfsFileRelease(hVfsFile);
636 }
637 else
638 RTFileClose(hFile);
639 }
640 }
641 else
642 {
643 PRTVFSCHAINSPEC pSpec;
644 rc = RTVfsChainSpecParse(pszSpec,
645 RTVFSCHAIN_PF_NO_REAL_ACTION
646 | RTVFSCHAIN_PF_LEADING_ACTION_OPTIONAL,
647 RTVFSCHAINACTION_PASSIVE,
648 RTVFSCHAINACTION_NONE,
649 &pSpec,
650 ppszError);
651 if (RT_SUCCESS(rc))
652 {
653
654
655 rc = VERR_NOT_IMPLEMENTED;
656 RTVfsChainSpecFree(pSpec);
657 }
658 }
659 return rc;
660}
661
662
663
664RTDECL(bool) RTVfsChainIsSpec(const char *pszSpec)
665{
666 return pszSpec
667 && strcmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX) == 0;
668}
669
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