VirtualBox

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

Last change on this file since 44417 was 43879, checked in by vboxsync, 12 years ago

Extended RTOnce with termination cleanups. (Changes existing structures and functions.)

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