VirtualBox

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

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

fatvfs,iso9660vfs,vfsbase: Sorted volume reference fun. Volumes will be freed now.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 38.7 KB
Line 
1/* $Id: vfschain.cpp 66742 2017-05-02 09:37:06Z 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.215389.xyz. 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 * Duplicate a spec string.
433 *
434 * This differs from RTStrDupN in that it uses RTMemTmpAlloc instead of
435 * RTMemAlloc.
436 *
437 * @returns String copy on success, NULL on failure.
438 * @param psz The string to duplicate.
439 * @param cch The number of bytes to duplicate.
440 * @param prc The status code variable to set on failure. (Leeps the
441 * code shorter. -lazy bird)
442 */
443DECLINLINE(char *) rtVfsChainSpecDupStrN(const char *psz, size_t cch, int *prc)
444{
445 char *pszCopy = (char *)RTMemTmpAlloc(cch + 1);
446 if (pszCopy)
447 {
448 if (!memchr(psz, '\\', cch))
449 {
450 /* Plain string, copy it raw. */
451 memcpy(pszCopy, psz, cch);
452 pszCopy[cch] = '\0';
453 }
454 else
455 {
456 /* Has escape sequences, must unescape it. */
457 char *pszDst = pszCopy;
458 while (cch-- > 0)
459 {
460 char ch = *psz++;
461 if (ch == '\\' && cch > 0)
462 {
463 char ch2 = psz[2];
464 if (ch2 == '(' || ch2 == ')' || ch2 == '\\' || ch2 == ',')
465 {
466 psz++;
467 cch--;
468 ch = ch2;
469 }
470 }
471 *pszDst++ = ch;
472 }
473 *pszDst = '\0';
474 }
475 }
476 else
477 *prc = VERR_NO_TMP_MEMORY;
478 return pszCopy;
479}
480
481
482/**
483 * Adds an empty element to the chain specification.
484 *
485 * The caller is responsible for filling it the element attributes.
486 *
487 * @returns Pointer to the new element on success, NULL on failure. The
488 * pointer is only valid till the next call to this function.
489 * @param pSpec The chain specification.
490 * @param prc The status code variable to set on failure. (Leeps the
491 * code shorter. -lazy bird)
492 */
493static PRTVFSCHAINELEMSPEC rtVfsChainSpecAddElement(PRTVFSCHAINSPEC pSpec, uint16_t offSpec, int *prc)
494{
495 AssertPtr(pSpec);
496
497 /*
498 * Resize the element table if necessary.
499 */
500 uint32_t const iElement = pSpec->cElements;
501 if ((iElement % 32) == 0)
502 {
503 PRTVFSCHAINELEMSPEC paNew = (PRTVFSCHAINELEMSPEC)RTMemTmpAlloc((iElement + 32) * sizeof(paNew[0]));
504 if (!paNew)
505 {
506 *prc = VERR_NO_TMP_MEMORY;
507 return NULL;
508 }
509
510 memcpy(paNew, pSpec->paElements, iElement * sizeof(paNew[0]));
511 RTMemTmpFree(pSpec->paElements);
512 pSpec->paElements = paNew;
513 }
514
515 /*
516 * Initialize and add the new element.
517 */
518 PRTVFSCHAINELEMSPEC pElement = &pSpec->paElements[iElement];
519 pElement->pszProvider = NULL;
520 pElement->enmTypeIn = iElement ? pSpec->paElements[iElement - 1].enmType : RTVFSOBJTYPE_INVALID;
521 pElement->enmType = RTVFSOBJTYPE_INVALID;
522 pElement->offSpec = offSpec;
523 pElement->cchSpec = 0;
524 pElement->cArgs = 0;
525 pElement->paArgs = NULL;
526 pElement->pProvider = NULL;
527 pElement->hVfsObj = NIL_RTVFSOBJ;
528
529 pSpec->cElements = iElement + 1;
530 return pElement;
531}
532
533
534/**
535 * Adds an argument to the element spec.
536 *
537 * @returns IPRT status code.
538 * @param pElement The element.
539 * @param psz The start of the argument string.
540 * @param cch The length of the argument string, escape
541 * sequences counted twice.
542 */
543static int rtVfsChainSpecElementAddArg(PRTVFSCHAINELEMSPEC pElement, const char *psz, size_t cch, uint16_t offSpec)
544{
545 uint32_t iArg = pElement->cArgs;
546 if ((iArg % 32) == 0)
547 {
548 PRTVFSCHAINELEMENTARG paNew = (PRTVFSCHAINELEMENTARG)RTMemTmpAlloc((iArg + 32) * sizeof(paNew[0]));
549 if (!paNew)
550 return VERR_NO_TMP_MEMORY;
551 memcpy(paNew, pElement->paArgs, iArg * sizeof(paNew[0]));
552 RTMemTmpFree(pElement->paArgs);
553 pElement->paArgs = paNew;
554 }
555
556 int rc = VINF_SUCCESS;
557 pElement->paArgs[iArg].psz = rtVfsChainSpecDupStrN(psz, cch, &rc);
558 pElement->paArgs[iArg].offSpec = offSpec;
559 pElement->cArgs = iArg + 1;
560 return rc;
561}
562
563
564RTDECL(void) RTVfsChainSpecFree(PRTVFSCHAINSPEC pSpec)
565{
566 if (!pSpec)
567 return;
568
569 uint32_t i = pSpec->cElements;
570 while (i-- > 0)
571 {
572 uint32_t iArg = pSpec->paElements[i].cArgs;
573 while (iArg-- > 0)
574 RTMemTmpFree(pSpec->paElements[i].paArgs[iArg].psz);
575 RTMemTmpFree(pSpec->paElements[i].paArgs);
576 RTMemTmpFree(pSpec->paElements[i].pszProvider);
577 if (pSpec->paElements[i].hVfsObj != NIL_RTVFSOBJ)
578 {
579 RTVfsObjRelease(pSpec->paElements[i].hVfsObj);
580 pSpec->paElements[i].hVfsObj = NIL_RTVFSOBJ;
581 }
582 }
583
584 RTMemTmpFree(pSpec->paElements);
585 pSpec->paElements = NULL;
586 RTMemTmpFree(pSpec);
587}
588
589
590/**
591 * Finds the end of the argument string.
592 *
593 * @returns The offset of the end character relative to @a psz.
594 * @param psz The argument string.
595 */
596static size_t rtVfsChainSpecFindArgEnd(const char *psz)
597{
598 char ch;
599 size_t off = 0;
600 while ( (ch = psz[off]) != '\0'
601 && ch != ','
602 && ch != ')'
603 && ch != '(')
604 {
605 /* check for escape sequences. */
606 if ( ch == '\\'
607 && (psz[off+1] == '(' || psz[off+1] == ')' || psz[off+1] == '\\' || psz[off+1] == ','))
608 off++;
609 off++;
610 }
611 return off;
612}
613
614
615RTDECL(int) RTVfsChainSpecParse(const char *pszSpec, uint32_t fFlags, RTVFSOBJTYPE enmDesiredType,
616 PRTVFSCHAINSPEC *ppSpec, uint32_t *poffError)
617{
618 if (poffError)
619 {
620 AssertPtrReturn(poffError, VERR_INVALID_POINTER);
621 *poffError = 0;
622 }
623 AssertPtrReturn(ppSpec, VERR_INVALID_POINTER);
624 *ppSpec = NULL;
625 AssertPtrReturn(pszSpec, VERR_INVALID_POINTER);
626 AssertReturn(!(fFlags & ~RTVFSCHAIN_PF_VALID_MASK), VERR_INVALID_PARAMETER);
627 AssertReturn(enmDesiredType > RTVFSOBJTYPE_INVALID && enmDesiredType < RTVFSOBJTYPE_END, VERR_INVALID_PARAMETER);
628
629 /*
630 * Check the start of the specification and allocate an empty return spec.
631 */
632 if (strncmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX, sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1))
633 return VERR_VFS_CHAIN_NO_PREFIX;
634 const char *pszSrc = RTStrStripL(pszSpec + sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1);
635 if (!*pszSrc)
636 return VERR_VFS_CHAIN_EMPTY;
637
638 PRTVFSCHAINSPEC pSpec = rtVfsChainSpecAlloc();
639 if (!pSpec)
640 return VERR_NO_TMP_MEMORY;
641 pSpec->enmDesiredType = enmDesiredType;
642
643 /*
644 * Parse the spec one element at a time.
645 */
646 int rc = VINF_SUCCESS;
647 while (*pszSrc && RT_SUCCESS(rc))
648 {
649 /*
650 * Digest element separator, except for the first element.
651 */
652 if (*pszSrc == '|' || *pszSrc == ':')
653 {
654 if (pSpec->cElements != 0)
655 pszSrc = RTStrStripL(pszSrc + 1);
656 else
657 {
658 rc = VERR_VFS_CHAIN_LEADING_SEPARATOR;
659 break;
660 }
661 }
662 else if (pSpec->cElements != 0)
663 {
664 rc = VERR_VFS_CHAIN_EXPECTED_SEPARATOR;
665 break;
666 }
667
668 /*
669 * Ok, there should be an element here so add one to the return struct.
670 */
671 PRTVFSCHAINELEMSPEC pElement = rtVfsChainSpecAddElement(pSpec, (uint16_t)(pszSrc - pszSpec), &rc);
672 if (!pElement)
673 break;
674
675 /*
676 * First up is the VFS object type followed by a parentheses,
677 * or this could be the trailing action.
678 */
679 size_t cch;
680 if (strncmp(pszSrc, "base", cch = 4) == 0)
681 pElement->enmType = RTVFSOBJTYPE_BASE;
682 else if (strncmp(pszSrc, "vfs", cch = 3) == 0)
683 pElement->enmType = RTVFSOBJTYPE_VFS;
684 else if (strncmp(pszSrc, "fss", cch = 3) == 0)
685 pElement->enmType = RTVFSOBJTYPE_FS_STREAM;
686 else if (strncmp(pszSrc, "ios", cch = 3) == 0)
687 pElement->enmType = RTVFSOBJTYPE_IO_STREAM;
688 else if (strncmp(pszSrc, "dir", cch = 3) == 0)
689 pElement->enmType = RTVFSOBJTYPE_DIR;
690 else if (strncmp(pszSrc, "file", cch = 4) == 0)
691 pElement->enmType = RTVFSOBJTYPE_FILE;
692 else if (strncmp(pszSrc, "sym", cch = 3) == 0)
693 pElement->enmType = RTVFSOBJTYPE_SYMLINK;
694 else
695 {
696 if (*pszSrc == '\0')
697 rc = VERR_VFS_CHAIN_TRAILING_SEPARATOR;
698 else
699 rc = VERR_VFS_CHAIN_UNKNOWN_TYPE;
700 break;
701 }
702 pszSrc += cch;
703
704 /* Check and skip the parentheses. */
705 if (*pszSrc != '(')
706 {
707 rc = VERR_VFS_CHAIN_EXPECTED_LEFT_PARENTHESES;
708 break;
709 }
710 pszSrc = RTStrStripL(pszSrc + 1);
711
712 /*
713 * The name of the element provider.
714 */
715 cch = rtVfsChainSpecFindArgEnd(pszSrc);
716 if (!cch)
717 {
718 rc = VERR_VFS_CHAIN_EXPECTED_PROVIDER_NAME;
719 break;
720 }
721 pElement->pszProvider = rtVfsChainSpecDupStrN(pszSrc, cch, &rc);
722 if (!pElement->pszProvider)
723 break;
724 pszSrc += cch;
725
726 /*
727 * The arguments.
728 */
729 while (*pszSrc == ',')
730 {
731 pszSrc = RTStrStripL(pszSrc + 1);
732 cch = rtVfsChainSpecFindArgEnd(pszSrc);
733 rc = rtVfsChainSpecElementAddArg(pElement, pszSrc, cch, (uint16_t)(pszSrc - pszSpec));
734 if (RT_FAILURE(rc))
735 break;
736 pszSrc += cch;
737 }
738 if (RT_FAILURE(rc))
739 break;
740
741 /* Must end with a right parentheses. */
742 if (*pszSrc != ')')
743 {
744 rc = VERR_VFS_CHAIN_EXPECTED_RIGHT_PARENTHESES;
745 break;
746 }
747 pElement->cchSpec = (uint16_t)(pszSrc - pszSpec) - pElement->offSpec + 1;
748
749 pszSrc = RTStrStripL(pszSrc + 1);
750 }
751
752#if 0
753 /*
754 * Dump the chain. Useful for debugging the above code.
755 */
756 RTAssertMsg2("dbg: cElements=%d rc=%Rrc\n", pSpec->cElements, rc);
757 for (uint32_t i = 0; i < pSpec->cElements; i++)
758 {
759 uint32_t const cArgs = pSpec->paElements[i].cArgs;
760 RTAssertMsg2("dbg: #%u: enmTypeIn=%d enmType=%d cArgs=%d",
761 i, pSpec->paElements[i].enmTypeIn, pSpec->paElements[i].enmType, cArgs);
762 for (uint32_t j = 0; j < cArgs; j++)
763 RTAssertMsg2(j == 0 ? (cArgs > 1 ? " [%s" : " [%s]") : j + 1 < cArgs ? ", %s" : ", %s]",
764 pSpec->paElements[i].paArgs[j].psz);
765 RTAssertMsg2(" offSpec=%d cchSpec=%d", pSpec->paElements[i].offSpec, pSpec->paElements[i].cchSpec);
766 RTAssertMsg2(" spec: %.*s\n", pSpec->paElements[i].cchSpec, &pszSpec[pSpec->paElements[i].offSpec]);
767 }
768#endif
769
770 /*
771 * Return the chain on success; Cleanup and set the error indicator on
772 * failure.
773 */
774 if (RT_SUCCESS(rc))
775 *ppSpec = pSpec;
776 else
777 {
778 if (poffError)
779 *poffError = (uint32_t)(pszSrc - pszSpec);
780 RTVfsChainSpecFree(pSpec);
781 }
782 return rc;
783}
784
785
786/**
787 * Looks up @a pszProvider among the registered providers.
788 *
789 * @returns Pointer to registration record if found, NULL if not.
790 * @param pszProvider The provider.
791 */
792static PCRTVFSCHAINELEMENTREG rtVfsChainFindProviderLocked(const char *pszProvider)
793{
794 PCRTVFSCHAINELEMENTREG pIterator;
795 RTListForEach(&g_rtVfsChainElementProviderList, pIterator, RTVFSCHAINELEMENTREG, ListEntry)
796 {
797 if (strcmp(pIterator->pszName, pszProvider) == 0)
798 return pIterator;
799 }
800 return NULL;
801}
802
803
804/**
805 * Does reusable object type matching.
806 *
807 * @returns true if the types matches, false if not.
808 * @param pElement The target element specification.
809 * @param pReuseElement The source element specification.
810 */
811static bool rtVfsChainMatchReusableType(PRTVFSCHAINELEMSPEC pElement, PRTVFSCHAINELEMSPEC pReuseElement)
812{
813 if (pElement->enmType == pReuseElement->enmType)
814 return true;
815
816 /* File objects can always be cast to I/O streams. */
817 if ( pElement->enmType == RTVFSOBJTYPE_IO_STREAM
818 && pReuseElement->enmType == RTVFSOBJTYPE_FILE)
819 return true;
820
821 /* I/O stream objects may be file objects. */
822 if ( pElement->enmType == RTVFSOBJTYPE_FILE
823 && pReuseElement->enmType == RTVFSOBJTYPE_IO_STREAM)
824 {
825 RTVFSFILE hVfsFile = RTVfsObjToFile(pReuseElement->hVfsObj);
826 if (hVfsFile != NIL_RTVFSFILE)
827 {
828 RTVfsFileRelease(hVfsFile);
829 return true;
830 }
831 }
832 return false;
833}
834
835
836RTDECL(int) RTVfsChainSpecCheckAndSetup(PRTVFSCHAINSPEC pSpec, PCRTVFSCHAINSPEC pReuseSpec,
837 PRTVFSOBJ phVfsObj, uint32_t *poffError, PRTERRINFO pErrInfo)
838{
839 AssertPtrReturn(poffError, VERR_INVALID_POINTER);
840 *poffError = 0;
841 AssertPtrReturn(phVfsObj, VERR_INVALID_POINTER);
842 *phVfsObj = NIL_RTVFSOBJ;
843 AssertPtrReturn(pSpec, VERR_INVALID_POINTER);
844 AssertPtrNullReturn(pErrInfo, VERR_INVALID_POINTER);
845
846 /*
847 * Enter the critical section after making sure it has been initialized.
848 */
849 int rc = RTOnce(&g_rtVfsChainElementInitOnce, rtVfsChainElementRegisterInit, NULL);
850 if (RT_SUCCESS(rc))
851 rc = RTCritSectRwEnterShared(&g_rtVfsChainElementCritSect);
852 if (RT_SUCCESS(rc))
853 {
854 /*
855 * Resolve and check each element first.
856 */
857 for (uint32_t i = 0; i < pSpec->cElements; i++)
858 {
859 PRTVFSCHAINELEMSPEC const pElement = &pSpec->paElements[i];
860 *poffError = pElement->offSpec;
861 pElement->pProvider = rtVfsChainFindProviderLocked(pElement->pszProvider);
862 if (pElement->pProvider)
863 {
864 rc = pElement->pProvider->pfnValidate(pElement->pProvider, pSpec, pElement, poffError, pErrInfo);
865 if (RT_SUCCESS(rc))
866 continue;
867 }
868 else
869 rc = VERR_VFS_CHAIN_PROVIDER_NOT_FOUND;
870 break;
871 }
872
873 /*
874 * Check that the desired type is compatible with the last element.
875 */
876 if (RT_SUCCESS(rc))
877 {
878 if (pSpec->cElements > 0) /* paranoia */
879 {
880 PRTVFSCHAINELEMSPEC const pLast = &pSpec->paElements[pSpec->cElements - 1];
881 if ( pLast->enmType == pSpec->enmDesiredType
882 || ( pLast->enmType == RTVFSOBJTYPE_FILE
883 && pSpec->enmDesiredType == RTVFSOBJTYPE_IO_STREAM) )
884 rc = VINF_SUCCESS;
885 else
886 {
887 *poffError = pLast->offSpec;
888 rc = VERR_VFS_CHAIN_FINAL_TYPE_MISMATCH;
889 }
890 }
891 else
892 rc = VERR_VFS_CHAIN_EMPTY;
893 }
894
895 if (RT_SUCCESS(rc))
896 {
897 /*
898 * Try construct the chain.
899 */
900 RTVFSOBJ hPrevVfsObj = NIL_RTVFSOBJ; /* No extra reference, kept in chain structure. */
901 for (uint32_t i = 0; i < pSpec->cElements; i++)
902 {
903 PRTVFSCHAINELEMSPEC const pElement = &pSpec->paElements[i];
904 *poffError = pElement->offSpec;
905
906 /*
907 * Try reuse the VFS objects at the start of the passed in reuse chain.
908 */
909 if (!pReuseSpec)
910 { /* likely */ }
911 else
912 {
913 if (i < pReuseSpec->cElements)
914 {
915 PRTVFSCHAINELEMSPEC const pReuseElement = &pReuseSpec->paElements[i];
916 if (pReuseElement->hVfsObj != NIL_RTVFSOBJ)
917 {
918 if (strcmp(pElement->pszProvider, pReuseElement->pszProvider) == 0)
919 {
920 if (rtVfsChainMatchReusableType(pElement, pReuseElement))
921 {
922 if (pElement->pProvider->pfnCanReuseElement(pElement->pProvider, pSpec, pElement,
923 pReuseSpec, pReuseElement))
924 {
925 uint32_t cRefs = RTVfsObjRetain(pReuseElement->hVfsObj);
926 if (cRefs != UINT32_MAX)
927 {
928 pElement->hVfsObj = hPrevVfsObj = pReuseElement->hVfsObj;
929 continue;
930 }
931 }
932 }
933 }
934 }
935 }
936 pReuseSpec = NULL;
937 }
938
939 /*
940 * Instantiate a new VFS object.
941 */
942 RTVFSOBJ hVfsObj = NIL_RTVFSOBJ;
943 rc = pElement->pProvider->pfnInstantiate(pElement->pProvider, pSpec, pElement, hPrevVfsObj,
944 &hVfsObj, poffError, pErrInfo);
945 if (RT_FAILURE(rc))
946 break;
947 pElement->hVfsObj = hVfsObj;
948 hPrevVfsObj = hVfsObj;
949 }
950
951 /*
952 * Add another reference to the final object and return.
953 */
954 if (RT_SUCCESS(rc))
955 {
956 uint32_t cRefs = RTVfsObjRetain(hPrevVfsObj);
957 AssertStmt(cRefs != UINT32_MAX, rc = VERR_VFS_CHAIN_IPE);
958 *phVfsObj = hPrevVfsObj;
959 }
960 }
961
962 int rc2 = RTCritSectRwLeaveShared(&g_rtVfsChainElementCritSect);
963 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
964 rc = rc2;
965 }
966 return rc;
967}
968
969
970RTDECL(int) RTVfsChainElementDeregisterProvider(PRTVFSCHAINELEMENTREG pRegRec, bool fFromDtor)
971{
972 /*
973 * Fend off wildlife.
974 */
975 if (pRegRec == NULL)
976 return VINF_SUCCESS;
977 AssertPtrReturn(pRegRec, VERR_INVALID_POINTER);
978 AssertMsgReturn(pRegRec->uVersion == RTVFSCHAINELEMENTREG_VERSION, ("%#x", pRegRec->uVersion), VERR_INVALID_POINTER);
979 AssertMsgReturn(pRegRec->uEndMarker == RTVFSCHAINELEMENTREG_VERSION, ("%#zx", pRegRec->uEndMarker), VERR_INVALID_POINTER);
980 AssertPtrReturn(pRegRec->pszName, VERR_INVALID_POINTER);
981
982 /*
983 * Take the lock if that's safe.
984 */
985 if (!fFromDtor)
986 RTCritSectRwEnterExcl(&g_rtVfsChainElementCritSect);
987 else if (!g_rtVfsChainElementProviderList.pNext)
988 RTListInit(&g_rtVfsChainElementProviderList);
989
990 /*
991 * Ok, remove it.
992 */
993 int rc = VERR_NOT_FOUND;
994 PRTVFSCHAINELEMENTREG pIterator, pIterNext;
995 RTListForEachSafe(&g_rtVfsChainElementProviderList, pIterator, pIterNext, RTVFSCHAINELEMENTREG, ListEntry)
996 {
997 if (pIterator == pRegRec)
998 {
999 RTListNodeRemove(&pRegRec->ListEntry);
1000 rc = VINF_SUCCESS;
1001 break;
1002 }
1003 }
1004
1005 /*
1006 * Leave the lock and return.
1007 */
1008 if (!fFromDtor)
1009 RTCritSectRwLeaveExcl(&g_rtVfsChainElementCritSect);
1010 return rc;
1011}
1012
1013
1014RTDECL(int) RTVfsChainOpenFile(const char *pszSpec, uint64_t fOpen,
1015 PRTVFSFILE phVfsFile, uint32_t *poffError, PRTERRINFO pErrInfo)
1016{
1017 uint32_t offErrorIgn;
1018 if (!poffError)
1019 poffError = &offErrorIgn;
1020 *poffError = 0;
1021 AssertPtrReturn(pszSpec, VERR_INVALID_POINTER);
1022 AssertReturn(*pszSpec != '\0', VERR_INVALID_PARAMETER);
1023 AssertPtrReturn(phVfsFile, VERR_INVALID_POINTER);
1024 AssertPtrNullReturn(pErrInfo, VERR_INVALID_POINTER);
1025
1026 /*
1027 * If it's not a VFS chain spec, treat it as a file.
1028 */
1029 int rc;
1030 if (strncmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX, sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1))
1031 {
1032 RTFILE hFile;
1033 rc = RTFileOpen(&hFile, pszSpec, fOpen);
1034 if (RT_SUCCESS(rc))
1035 {
1036 RTVFSFILE hVfsFile;
1037 rc = RTVfsFileFromRTFile(hFile, fOpen, false /*fLeaveOpen*/, &hVfsFile);
1038 if (RT_SUCCESS(rc))
1039 *phVfsFile = hVfsFile;
1040 else
1041 RTFileClose(hFile);
1042 }
1043 }
1044 else
1045 {
1046 PRTVFSCHAINSPEC pSpec;
1047 rc = RTVfsChainSpecParse(pszSpec, 0 /*fFlags*/, RTVFSOBJTYPE_FILE, &pSpec, poffError);
1048 if (RT_SUCCESS(rc))
1049 {
1050 pSpec->fOpenFile = fOpen;
1051
1052 RTVFSOBJ hVfsObj = NIL_RTVFSOBJ;
1053 rc = RTVfsChainSpecCheckAndSetup(pSpec, NULL /*pReuseSpec*/, &hVfsObj, poffError, pErrInfo);
1054 if (RT_SUCCESS(rc))
1055 {
1056 *phVfsFile = RTVfsObjToFile(hVfsObj);
1057 if (*phVfsFile)
1058 rc = VINF_SUCCESS;
1059 else
1060 rc = VERR_VFS_CHAIN_CAST_FAILED;
1061 RTVfsObjRelease(hVfsObj);
1062 }
1063
1064 RTVfsChainSpecFree(pSpec);
1065 }
1066 }
1067 return rc;
1068}
1069
1070
1071RTDECL(int) RTVfsChainOpenIoStream(const char *pszSpec, uint64_t fOpen,
1072 PRTVFSIOSTREAM phVfsIos, uint32_t *poffError, PRTERRINFO pErrInfo)
1073{
1074 uint32_t offErrorIgn;
1075 if (!poffError)
1076 poffError = &offErrorIgn;
1077 *poffError = 0;
1078 AssertPtrReturn(pszSpec, VERR_INVALID_POINTER);
1079 AssertReturn(*pszSpec != '\0', VERR_INVALID_PARAMETER);
1080 AssertPtrReturn(phVfsIos, VERR_INVALID_POINTER);
1081 AssertPtrNullReturn(pErrInfo, VERR_INVALID_POINTER);
1082
1083 /*
1084 * If it's not a VFS chain spec, treat it as a file.
1085 */
1086 int rc;
1087 if (strncmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX, sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1))
1088 {
1089 RTFILE hFile;
1090 rc = RTFileOpen(&hFile, pszSpec, fOpen);
1091 if (RT_SUCCESS(rc))
1092 {
1093 RTVFSFILE hVfsFile;
1094 rc = RTVfsFileFromRTFile(hFile, fOpen, false /*fLeaveOpen*/, &hVfsFile);
1095 if (RT_SUCCESS(rc))
1096 {
1097 *phVfsIos = RTVfsFileToIoStream(hVfsFile);
1098 RTVfsFileRelease(hVfsFile);
1099 }
1100 else
1101 RTFileClose(hFile);
1102 }
1103 }
1104 /*
1105 * Do the chain thing.
1106 */
1107 else
1108 {
1109 PRTVFSCHAINSPEC pSpec;
1110 rc = RTVfsChainSpecParse(pszSpec, 0 /*fFlags*/, RTVFSOBJTYPE_IO_STREAM, &pSpec, poffError);
1111 if (RT_SUCCESS(rc))
1112 {
1113 pSpec->fOpenFile = fOpen;
1114
1115 RTVFSOBJ hVfsObj = NIL_RTVFSOBJ;
1116 rc = RTVfsChainSpecCheckAndSetup(pSpec, NULL /*pReuseSpec*/, &hVfsObj, poffError, pErrInfo);
1117 if (RT_SUCCESS(rc))
1118 {
1119 *phVfsIos = RTVfsObjToIoStream(hVfsObj);
1120 if (*phVfsIos)
1121 rc = VINF_SUCCESS;
1122 else
1123 rc = VERR_VFS_CHAIN_CAST_FAILED;
1124 RTVfsObjRelease(hVfsObj);
1125 }
1126 RTVfsChainSpecFree(pSpec);
1127 }
1128 }
1129 return rc;
1130}
1131
1132
1133
1134RTDECL(bool) RTVfsChainIsSpec(const char *pszSpec)
1135{
1136 return pszSpec
1137 && strcmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX) == 0;
1138}
1139
1140
Note: See TracBrowser for help on using the repository browser.

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