VirtualBox

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

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

rtVfsChainSpecDupStrN: unescaping bugfix.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 38.5 KB
Line 
1/* $Id: vfschain.cpp 66675 2017-04-26 10:54:22Z 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 if (RT_SUCCESS(rc))
221 {
222 *phVfsObj = RTVfsObjFromFile(hVfsFile);
223 RTVfsFileRelease(hVfsFile);
224 if (*phVfsObj != NIL_RTVFSOBJ)
225 return VINF_SUCCESS;
226 rc = VERR_VFS_CHAIN_CAST_FAILED;
227 }
228 return rc;
229 }
230 if (pElement->enmType == RTVFSOBJTYPE_DIR)
231 {
232 RTVFSDIR hVfsDir = NIL_RTVFSDIR;
233 int rc = RTVfsDirOpen(hVfsIn, pElement->paArgs[0].psz, (uint32_t)pElement->uProvider, &hVfsDir);
234 if (RT_SUCCESS(rc))
235 {
236 *phVfsObj = RTVfsObjFromDir(hVfsDir);
237 RTVfsDirRelease(hVfsDir);
238 if (*phVfsObj != NIL_RTVFSOBJ)
239 return VINF_SUCCESS;
240 rc = VERR_VFS_CHAIN_CAST_FAILED;
241 }
242 return rc;
243 }
244 return VERR_VFS_CHAIN_IPE;
245 }
246
247 /*
248 * Directory: Similar to above, just relative to a directory.
249 */
250 RTVFSDIR hVfsDirIn = RTVfsObjToDir(hPrevVfsObj);
251 if (hVfsDirIn != NIL_RTVFSDIR)
252 {
253 if ( pElement->enmType == RTVFSOBJTYPE_FILE
254 || pElement->enmType == RTVFSOBJTYPE_IO_STREAM)
255 {
256 RTVFSFILE hVfsFile = NIL_RTVFSFILE;
257 int rc = RTVfsDirOpenFile(hVfsDirIn, pElement->paArgs[0].psz, pElement->uProvider, &hVfsFile);
258 if (RT_SUCCESS(rc))
259 {
260 *phVfsObj = RTVfsObjFromFile(hVfsFile);
261 RTVfsFileRelease(hVfsFile);
262 if (*phVfsObj != NIL_RTVFSOBJ)
263 return VINF_SUCCESS;
264 rc = VERR_VFS_CHAIN_CAST_FAILED;
265 }
266 return rc;
267 }
268 if (pElement->enmType == RTVFSOBJTYPE_DIR)
269 {
270 RTVFSDIR hVfsDir = NIL_RTVFSDIR;
271 int rc = RTVfsDirOpenDir(hVfsDirIn, pElement->paArgs[0].psz, pElement->uProvider, &hVfsDir);
272 if (RT_SUCCESS(rc))
273 {
274 *phVfsObj = RTVfsObjFromDir(hVfsDir);
275 RTVfsDirRelease(hVfsDir);
276 if (*phVfsObj != NIL_RTVFSOBJ)
277 return VINF_SUCCESS;
278 rc = VERR_VFS_CHAIN_CAST_FAILED;
279 }
280 return rc;
281 }
282 return VERR_VFS_CHAIN_IPE;
283 }
284
285 AssertFailed();
286 return VERR_VFS_CHAIN_CAST_FAILED;
287}
288
289
290/**
291 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnCanReuseElement}
292 */
293static DECLCALLBACK(bool) rtVfsChainOpen_CanReuseElement(PCRTVFSCHAINELEMENTREG pProviderReg,
294 PCRTVFSCHAINSPEC pSpec, PCRTVFSCHAINELEMSPEC pElement,
295 PCRTVFSCHAINSPEC pReuseSpec, PCRTVFSCHAINELEMSPEC pReuseElement)
296{
297 RT_NOREF(pProviderReg, pSpec, pElement, pReuseSpec, pReuseElement);
298 return false;
299}
300
301
302/** VFS chain element 'gunzip'. */
303static RTVFSCHAINELEMENTREG g_rtVfsChainGunzipReg =
304{
305 /* uVersion = */ RTVFSCHAINELEMENTREG_VERSION,
306 /* fReserved = */ 0,
307 /* pszName = */ "open",
308 /* ListEntry = */ { NULL, NULL },
309 /* pszHelp = */ "Generic VFS open, that can open files (or I/O stream) and directories in a VFS, directory or file system stream.\n"
310 "If used as the first element in a chain, it will work like 'stdfile' or 'stddir' and work on the real file system.\n"
311 "First argument is the filename or directory path.\n"
312 "Second argument is access mode, files only, optional: r, w, rw.\n"
313 "Third argument is open disposition, files only, optional: create, create-replace, open, open-create, open-append, open-truncate.\n"
314 "Forth argument is file sharing, files only, optional: nr, nw, nrw, d.",
315 /* pfnValidate = */ rtVfsChainOpen_Validate,
316 /* pfnInstantiate = */ rtVfsChainOpen_Instantiate,
317 /* pfnCanReuseElement = */ rtVfsChainOpen_CanReuseElement,
318 /* uEndMarker = */ RTVFSCHAINELEMENTREG_VERSION
319};
320
321RTVFSCHAIN_AUTO_REGISTER_ELEMENT_PROVIDER(&g_rtVfsChainGunzipReg, rtVfsChainGunzipReg);
322
323
324
325
326/**
327 * Initializes the globals via RTOnce.
328 *
329 * @returns IPRT status code
330 * @param pvUser Unused, ignored.
331 */
332static DECLCALLBACK(int) rtVfsChainElementRegisterInit(void *pvUser)
333{
334 NOREF(pvUser);
335 if (!g_rtVfsChainElementProviderList.pNext)
336 RTListInit(&g_rtVfsChainElementProviderList);
337 int rc = RTCritSectRwInit(&g_rtVfsChainElementCritSect);
338 if (RT_SUCCESS(rc))
339 {
340 }
341 return rc;
342}
343
344
345RTDECL(int) RTVfsChainElementRegisterProvider(PRTVFSCHAINELEMENTREG pRegRec, bool fFromCtor)
346{
347 int rc;
348
349 /*
350 * Input validation.
351 */
352 AssertPtrReturn(pRegRec, VERR_INVALID_POINTER);
353 AssertMsgReturn(pRegRec->uVersion == RTVFSCHAINELEMENTREG_VERSION, ("%#x", pRegRec->uVersion), VERR_INVALID_POINTER);
354 AssertMsgReturn(pRegRec->uEndMarker == RTVFSCHAINELEMENTREG_VERSION, ("%#zx", pRegRec->uEndMarker), VERR_INVALID_POINTER);
355 AssertReturn(pRegRec->fReserved == 0, VERR_INVALID_POINTER);
356 AssertPtrReturn(pRegRec->pszName, VERR_INVALID_POINTER);
357 AssertPtrReturn(pRegRec->pfnValidate, VERR_INVALID_POINTER);
358 AssertPtrReturn(pRegRec->pfnInstantiate, VERR_INVALID_POINTER);
359 AssertPtrReturn(pRegRec->pfnCanReuseElement, VERR_INVALID_POINTER);
360
361 /*
362 * Init and take the lock.
363 */
364 if (!fFromCtor)
365 {
366 rc = RTOnce(&g_rtVfsChainElementInitOnce, rtVfsChainElementRegisterInit, NULL);
367 if (RT_FAILURE(rc))
368 return rc;
369 rc = RTCritSectRwEnterExcl(&g_rtVfsChainElementCritSect);
370 if (RT_FAILURE(rc))
371 return rc;
372 }
373 else if (!g_rtVfsChainElementProviderList.pNext)
374 RTListInit(&g_rtVfsChainElementProviderList);
375
376 /*
377 * Duplicate name?
378 */
379 rc = VINF_SUCCESS;
380 PRTVFSCHAINELEMENTREG pIterator, pIterNext;
381 RTListForEachSafe(&g_rtVfsChainElementProviderList, pIterator, pIterNext, RTVFSCHAINELEMENTREG, ListEntry)
382 {
383 if (!strcmp(pIterator->pszName, pRegRec->pszName))
384 {
385 AssertMsgFailed(("duplicate name '%s' old=%p new=%p\n", pIterator->pszName, pIterator, pRegRec));
386 rc = VERR_ALREADY_EXISTS;
387 break;
388 }
389 }
390
391 /*
392 * If not, append the record to the list.
393 */
394 if (RT_SUCCESS(rc))
395 RTListAppend(&g_rtVfsChainElementProviderList, &pRegRec->ListEntry);
396
397 /*
398 * Leave the lock and return.
399 */
400 if (!fFromCtor)
401 RTCritSectRwLeaveExcl(&g_rtVfsChainElementCritSect);
402 return rc;
403}
404
405
406/**
407 * Allocates and initializes an empty spec
408 *
409 * @returns Pointer to the spec on success, NULL on failure.
410 */
411static PRTVFSCHAINSPEC rtVfsChainSpecAlloc(void)
412{
413 PRTVFSCHAINSPEC pSpec = (PRTVFSCHAINSPEC)RTMemTmpAlloc(sizeof(*pSpec));
414 if (pSpec)
415 {
416 pSpec->fOpenFile = 0;
417 pSpec->fOpenDir = 0;
418 pSpec->cElements = 0;
419 pSpec->paElements = NULL;
420 }
421 return pSpec;
422}
423
424
425/**
426 * Duplicate a spec string.
427 *
428 * This differs from RTStrDupN in that it uses RTMemTmpAlloc instead of
429 * RTMemAlloc.
430 *
431 * @returns String copy on success, NULL on failure.
432 * @param psz The string to duplicate.
433 * @param cch The number of bytes to duplicate.
434 * @param prc The status code variable to set on failure. (Leeps the
435 * code shorter. -lazy bird)
436 */
437DECLINLINE(char *) rtVfsChainSpecDupStrN(const char *psz, size_t cch, int *prc)
438{
439 char *pszCopy = (char *)RTMemTmpAlloc(cch + 1);
440 if (pszCopy)
441 {
442 if (!memchr(psz, '\\', cch))
443 {
444 /* Plain string, copy it raw. */
445 memcpy(pszCopy, psz, cch);
446 pszCopy[cch] = '\0';
447 }
448 else
449 {
450 /* Has escape sequences, must unescape it. */
451 char *pszDst = pszCopy;
452 while (cch-- > 0)
453 {
454 char ch = *psz++;
455 if (ch == '\\' && cch > 0)
456 {
457 char ch2 = psz[2];
458 if (ch2 == '(' || ch2 == ')' || ch2 == '\\' || ch2 == ',')
459 {
460 psz++;
461 cch--;
462 ch = ch2;
463 }
464 }
465 *pszDst++ = ch;
466 }
467 *pszDst = '\0';
468 }
469 }
470 else
471 *prc = VERR_NO_TMP_MEMORY;
472 return pszCopy;
473}
474
475
476/**
477 * Adds an empty element to the chain specification.
478 *
479 * The caller is responsible for filling it the element attributes.
480 *
481 * @returns Pointer to the new element on success, NULL on failure. The
482 * pointer is only valid till the next call to this function.
483 * @param pSpec The chain specification.
484 * @param prc The status code variable to set on failure. (Leeps the
485 * code shorter. -lazy bird)
486 */
487static PRTVFSCHAINELEMSPEC rtVfsChainSpecAddElement(PRTVFSCHAINSPEC pSpec, uint16_t offSpec, int *prc)
488{
489 AssertPtr(pSpec);
490
491 /*
492 * Resize the element table if necessary.
493 */
494 uint32_t const iElement = pSpec->cElements;
495 if ((iElement % 32) == 0)
496 {
497 PRTVFSCHAINELEMSPEC paNew = (PRTVFSCHAINELEMSPEC)RTMemTmpAlloc((iElement + 32) * sizeof(paNew[0]));
498 if (!paNew)
499 {
500 *prc = VERR_NO_TMP_MEMORY;
501 return NULL;
502 }
503
504 memcpy(paNew, pSpec->paElements, iElement * sizeof(paNew[0]));
505 RTMemTmpFree(pSpec->paElements);
506 pSpec->paElements = paNew;
507 }
508
509 /*
510 * Initialize and add the new element.
511 */
512 PRTVFSCHAINELEMSPEC pElement = &pSpec->paElements[iElement];
513 pElement->pszProvider = NULL;
514 pElement->enmTypeIn = iElement ? pSpec->paElements[iElement - 1].enmType : RTVFSOBJTYPE_INVALID;
515 pElement->enmType = RTVFSOBJTYPE_INVALID;
516 pElement->offSpec = offSpec;
517 pElement->cchSpec = 0;
518 pElement->cArgs = 0;
519 pElement->paArgs = NULL;
520 pElement->pProvider = NULL;
521 pElement->hVfsObj = NIL_RTVFSOBJ;
522
523 pSpec->cElements = iElement + 1;
524 return pElement;
525}
526
527
528/**
529 * Adds an argument to the element spec.
530 *
531 * @returns IPRT status code.
532 * @param pElement The element.
533 * @param psz The start of the argument string.
534 * @param cch The length of the argument string, escape
535 * sequences counted twice.
536 */
537static int rtVfsChainSpecElementAddArg(PRTVFSCHAINELEMSPEC pElement, const char *psz, size_t cch, uint16_t offSpec)
538{
539 uint32_t iArg = pElement->cArgs;
540 if ((iArg % 32) == 0)
541 {
542 PRTVFSCHAINELEMENTARG paNew = (PRTVFSCHAINELEMENTARG)RTMemTmpAlloc((iArg + 32) * sizeof(paNew[0]));
543 if (!paNew)
544 return VERR_NO_TMP_MEMORY;
545 memcpy(paNew, pElement->paArgs, iArg * sizeof(paNew[0]));
546 RTMemTmpFree(pElement->paArgs);
547 pElement->paArgs = paNew;
548 }
549
550 int rc = VINF_SUCCESS;
551 pElement->paArgs[iArg].psz = rtVfsChainSpecDupStrN(psz, cch, &rc);
552 pElement->paArgs[iArg].offSpec = offSpec;
553 pElement->cArgs = iArg + 1;
554 return rc;
555}
556
557
558RTDECL(void) RTVfsChainSpecFree(PRTVFSCHAINSPEC pSpec)
559{
560 if (!pSpec)
561 return;
562
563 uint32_t i = pSpec->cElements;
564 while (i-- > 0)
565 {
566 uint32_t iArg = pSpec->paElements[i].cArgs;
567 while (iArg-- > 0)
568 RTMemTmpFree(pSpec->paElements[i].paArgs[iArg].psz);
569 RTMemTmpFree(pSpec->paElements[i].paArgs);
570 RTMemTmpFree(pSpec->paElements[i].pszProvider);
571 if (pSpec->paElements[i].hVfsObj != NIL_RTVFSOBJ)
572 {
573 RTVfsObjRelease(pSpec->paElements[i].hVfsObj);
574 pSpec->paElements[i].hVfsObj = NIL_RTVFSOBJ;
575 }
576 }
577
578 RTMemTmpFree(pSpec->paElements);
579 pSpec->paElements = NULL;
580 RTMemTmpFree(pSpec);
581}
582
583
584/**
585 * Finds the end of the argument string.
586 *
587 * @returns The offset of the end character relative to @a psz.
588 * @param psz The argument string.
589 */
590static size_t rtVfsChainSpecFindArgEnd(const char *psz)
591{
592 char ch;
593 size_t off = 0;
594 while ( (ch = psz[off]) != '\0'
595 && ch != ','
596 && ch != ')'
597 && ch != '(')
598 {
599 /* check for escape sequences. */
600 if ( ch == '\\'
601 && (psz[off+1] == '(' || psz[off+1] == ')' || psz[off+1] == '\\' || psz[off+1] == ','))
602 off++;
603 off++;
604 }
605 return off;
606}
607
608
609RTDECL(int) RTVfsChainSpecParse(const char *pszSpec, uint32_t fFlags, RTVFSOBJTYPE enmDesiredType,
610 PRTVFSCHAINSPEC *ppSpec, uint32_t *poffError)
611{
612 if (poffError)
613 {
614 AssertPtrReturn(poffError, VERR_INVALID_POINTER);
615 *poffError = 0;
616 }
617 AssertPtrReturn(ppSpec, VERR_INVALID_POINTER);
618 *ppSpec = NULL;
619 AssertPtrReturn(pszSpec, VERR_INVALID_POINTER);
620 AssertReturn(!(fFlags & ~RTVFSCHAIN_PF_VALID_MASK), VERR_INVALID_PARAMETER);
621 AssertReturn(enmDesiredType > RTVFSOBJTYPE_INVALID && enmDesiredType < RTVFSOBJTYPE_END, VERR_INVALID_PARAMETER);
622
623 /*
624 * Check the start of the specification and allocate an empty return spec.
625 */
626 if (strncmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX, sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1))
627 return VERR_VFS_CHAIN_NO_PREFIX;
628 const char *pszSrc = RTStrStripL(pszSpec + sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1);
629 if (!*pszSrc)
630 return VERR_VFS_CHAIN_EMPTY;
631
632 PRTVFSCHAINSPEC pSpec = rtVfsChainSpecAlloc();
633 if (!pSpec)
634 return VERR_NO_TMP_MEMORY;
635 pSpec->enmDesiredType = enmDesiredType;
636
637 /*
638 * Parse the spec one element at a time.
639 */
640 int rc = VINF_SUCCESS;
641 while (*pszSrc && RT_SUCCESS(rc))
642 {
643 /*
644 * Digest element separator, except for the first element.
645 */
646 if (*pszSrc == '|' || *pszSrc == ':')
647 {
648 if (pSpec->cElements != 0)
649 pszSrc = RTStrStripL(pszSrc + 1);
650 else
651 {
652 rc = VERR_VFS_CHAIN_LEADING_SEPARATOR;
653 break;
654 }
655 }
656 else if (pSpec->cElements != 0)
657 {
658 rc = VERR_VFS_CHAIN_EXPECTED_SEPARATOR;
659 break;
660 }
661
662 /*
663 * Ok, there should be an element here so add one to the return struct.
664 */
665 PRTVFSCHAINELEMSPEC pElement = rtVfsChainSpecAddElement(pSpec, (uint16_t)(pszSrc - pszSpec), &rc);
666 if (!pElement)
667 break;
668
669 /*
670 * First up is the VFS object type followed by a parentheses,
671 * or this could be the trailing action.
672 */
673 size_t cch;
674 if (strncmp(pszSrc, "base", cch = 4) == 0)
675 pElement->enmType = RTVFSOBJTYPE_BASE;
676 else if (strncmp(pszSrc, "vfs", cch = 3) == 0)
677 pElement->enmType = RTVFSOBJTYPE_VFS;
678 else if (strncmp(pszSrc, "fss", cch = 3) == 0)
679 pElement->enmType = RTVFSOBJTYPE_FS_STREAM;
680 else if (strncmp(pszSrc, "ios", cch = 3) == 0)
681 pElement->enmType = RTVFSOBJTYPE_IO_STREAM;
682 else if (strncmp(pszSrc, "dir", cch = 3) == 0)
683 pElement->enmType = RTVFSOBJTYPE_DIR;
684 else if (strncmp(pszSrc, "file", cch = 4) == 0)
685 pElement->enmType = RTVFSOBJTYPE_FILE;
686 else if (strncmp(pszSrc, "sym", cch = 3) == 0)
687 pElement->enmType = RTVFSOBJTYPE_SYMLINK;
688 else
689 {
690 if (*pszSrc == '\0')
691 rc = VERR_VFS_CHAIN_TRAILING_SEPARATOR;
692 else
693 rc = VERR_VFS_CHAIN_UNKNOWN_TYPE;
694 break;
695 }
696 pszSrc += cch;
697
698 /* Check and skip the parentheses. */
699 if (*pszSrc != '(')
700 {
701 rc = VERR_VFS_CHAIN_EXPECTED_LEFT_PARENTHESES;
702 break;
703 }
704 pszSrc = RTStrStripL(pszSrc + 1);
705
706 /*
707 * The name of the element provider.
708 */
709 cch = rtVfsChainSpecFindArgEnd(pszSrc);
710 if (!cch)
711 {
712 rc = VERR_VFS_CHAIN_EXPECTED_PROVIDER_NAME;
713 break;
714 }
715 pElement->pszProvider = rtVfsChainSpecDupStrN(pszSrc, cch, &rc);
716 if (!pElement->pszProvider)
717 break;
718 pszSrc += cch;
719
720 /*
721 * The arguments.
722 */
723 while (*pszSrc == ',')
724 {
725 pszSrc = RTStrStripL(pszSrc + 1);
726 cch = rtVfsChainSpecFindArgEnd(pszSrc);
727 rc = rtVfsChainSpecElementAddArg(pElement, pszSrc, cch, (uint16_t)(pszSrc - pszSpec));
728 if (RT_FAILURE(rc))
729 break;
730 pszSrc += cch;
731 }
732 if (RT_FAILURE(rc))
733 break;
734
735 /* Must end with a right parentheses. */
736 if (*pszSrc != ')')
737 {
738 rc = VERR_VFS_CHAIN_EXPECTED_RIGHT_PARENTHESES;
739 break;
740 }
741 pElement->cchSpec = (uint16_t)(pszSrc - pszSpec) - pElement->offSpec + 1;
742
743 pszSrc = RTStrStripL(pszSrc + 1);
744 }
745
746#if 0
747 /*
748 * Dump the chain. Useful for debugging the above code.
749 */
750 RTAssertMsg2("dbg: cElements=%d rc=%Rrc\n", pSpec->cElements, rc);
751 for (uint32_t i = 0; i < pSpec->cElements; i++)
752 {
753 uint32_t const cArgs = pSpec->paElements[i].cArgs;
754 RTAssertMsg2("dbg: #%u: enmTypeIn=%d enmType=%d cArgs=%d",
755 i, pSpec->paElements[i].enmTypeIn, pSpec->paElements[i].enmType, cArgs);
756 for (uint32_t j = 0; j < cArgs; j++)
757 RTAssertMsg2(j == 0 ? (cArgs > 1 ? " [%s" : " [%s]") : j + 1 < cArgs ? ", %s" : ", %s]",
758 pSpec->paElements[i].paArgs[j].psz);
759 RTAssertMsg2(" offSpec=%d cchSpec=%d", pSpec->paElements[i].offSpec, pSpec->paElements[i].cchSpec);
760 RTAssertMsg2(" spec: %.*s\n", pSpec->paElements[i].cchSpec, &pszSpec[pSpec->paElements[i].offSpec]);
761 }
762#endif
763
764 /*
765 * Return the chain on success; Cleanup and set the error indicator on
766 * failure.
767 */
768 if (RT_SUCCESS(rc))
769 *ppSpec = pSpec;
770 else
771 {
772 if (poffError)
773 *poffError = (uint32_t)(pszSrc - pszSpec);
774 RTVfsChainSpecFree(pSpec);
775 }
776 return rc;
777}
778
779
780/**
781 * Looks up @a pszProvider among the registered providers.
782 *
783 * @returns Pointer to registration record if found, NULL if not.
784 * @param pszProvider The provider.
785 */
786static PCRTVFSCHAINELEMENTREG rtVfsChainFindProviderLocked(const char *pszProvider)
787{
788 PCRTVFSCHAINELEMENTREG pIterator;
789 RTListForEach(&g_rtVfsChainElementProviderList, pIterator, RTVFSCHAINELEMENTREG, ListEntry)
790 {
791 if (strcmp(pIterator->pszName, pszProvider) == 0)
792 return pIterator;
793 }
794 return NULL;
795}
796
797
798/**
799 * Does reusable object type matching.
800 *
801 * @returns true if the types matches, false if not.
802 * @param pElement The target element specification.
803 * @param pReuseElement The source element specification.
804 */
805static bool rtVfsChainMatchReusableType(PRTVFSCHAINELEMSPEC pElement, PRTVFSCHAINELEMSPEC pReuseElement)
806{
807 if (pElement->enmType == pReuseElement->enmType)
808 return true;
809
810 /* File objects can always be cast to I/O streams. */
811 if ( pElement->enmType == RTVFSOBJTYPE_IO_STREAM
812 && pReuseElement->enmType == RTVFSOBJTYPE_FILE)
813 return true;
814
815 /* I/O stream objects may be file objects. */
816 if ( pElement->enmType == RTVFSOBJTYPE_FILE
817 && pReuseElement->enmType == RTVFSOBJTYPE_IO_STREAM)
818 {
819 RTVFSFILE hVfsFile = RTVfsObjToFile(pReuseElement->hVfsObj);
820 if (hVfsFile != NIL_RTVFSFILE)
821 {
822 RTVfsFileRelease(hVfsFile);
823 return true;
824 }
825 }
826 return false;
827}
828
829
830RTDECL(int) RTVfsChainSpecCheckAndSetup(PRTVFSCHAINSPEC pSpec, PCRTVFSCHAINSPEC pReuseSpec,
831 PRTVFSOBJ phVfsObj, uint32_t *poffError, PRTERRINFO pErrInfo)
832{
833 AssertPtrReturn(poffError, VERR_INVALID_POINTER);
834 *poffError = 0;
835 AssertPtrReturn(phVfsObj, VERR_INVALID_POINTER);
836 *phVfsObj = NIL_RTVFSOBJ;
837 AssertPtrReturn(pSpec, VERR_INVALID_POINTER);
838 AssertPtrNullReturn(pErrInfo, VERR_INVALID_POINTER);
839
840 /*
841 * Enter the critical section after making sure it has been initialized.
842 */
843 int rc = RTOnce(&g_rtVfsChainElementInitOnce, rtVfsChainElementRegisterInit, NULL);
844 if (RT_SUCCESS(rc))
845 rc = RTCritSectRwEnterShared(&g_rtVfsChainElementCritSect);
846 if (RT_SUCCESS(rc))
847 {
848 /*
849 * Resolve and check each element first.
850 */
851 for (uint32_t i = 0; i < pSpec->cElements; i++)
852 {
853 PRTVFSCHAINELEMSPEC const pElement = &pSpec->paElements[i];
854 *poffError = pElement->offSpec;
855 pElement->pProvider = rtVfsChainFindProviderLocked(pElement->pszProvider);
856 if (pElement->pProvider)
857 {
858 rc = pElement->pProvider->pfnValidate(pElement->pProvider, pSpec, pElement, poffError, pErrInfo);
859 if (RT_SUCCESS(rc))
860 continue;
861 }
862 else
863 rc = VERR_VFS_CHAIN_PROVIDER_NOT_FOUND;
864 break;
865 }
866
867 /*
868 * Check that the desired type is compatible with the last element.
869 */
870 if (RT_SUCCESS(rc))
871 {
872 if (pSpec->cElements > 0) /* paranoia */
873 {
874 PRTVFSCHAINELEMSPEC const pLast = &pSpec->paElements[pSpec->cElements - 1];
875 if ( pLast->enmType == pSpec->enmDesiredType
876 || ( pLast->enmType == RTVFSOBJTYPE_FILE
877 && pSpec->enmDesiredType == RTVFSOBJTYPE_IO_STREAM) )
878 rc = VINF_SUCCESS;
879 else
880 {
881 *poffError = pLast->offSpec;
882 rc = VERR_VFS_CHAIN_FINAL_TYPE_MISMATCH;
883 }
884 }
885 else
886 rc = VERR_VFS_CHAIN_EMPTY;
887 }
888
889 if (RT_SUCCESS(rc))
890 {
891 /*
892 * Try construct the chain.
893 */
894 RTVFSOBJ hPrevVfsObj = NIL_RTVFSOBJ; /* No extra reference, kept in chain structure. */
895 for (uint32_t i = 0; i < pSpec->cElements; i++)
896 {
897 PRTVFSCHAINELEMSPEC const pElement = &pSpec->paElements[i];
898 *poffError = pElement->offSpec;
899
900 /*
901 * Try reuse the VFS objects at the start of the passed in reuse chain.
902 */
903 if (!pReuseSpec)
904 { /* likely */ }
905 else
906 {
907 if (i < pReuseSpec->cElements)
908 {
909 PRTVFSCHAINELEMSPEC const pReuseElement = &pReuseSpec->paElements[i];
910 if (pReuseElement->hVfsObj != NIL_RTVFSOBJ)
911 {
912 if (strcmp(pElement->pszProvider, pReuseElement->pszProvider) == 0)
913 {
914 if (rtVfsChainMatchReusableType(pElement, pReuseElement))
915 {
916 if (pElement->pProvider->pfnCanReuseElement(pElement->pProvider, pSpec, pElement,
917 pReuseSpec, pReuseElement))
918 {
919 uint32_t cRefs = RTVfsObjRetain(pReuseElement->hVfsObj);
920 if (cRefs != UINT32_MAX)
921 {
922 pElement->hVfsObj = hPrevVfsObj = pReuseElement->hVfsObj;
923 continue;
924 }
925 }
926 }
927 }
928 }
929 }
930 pReuseSpec = NULL;
931 }
932
933 /*
934 * Instantiate a new VFS object.
935 */
936 RTVFSOBJ hVfsObj = NIL_RTVFSOBJ;
937 rc = pElement->pProvider->pfnInstantiate(pElement->pProvider, pSpec, pElement, hPrevVfsObj,
938 &hVfsObj, poffError, pErrInfo);
939 if (RT_FAILURE(rc))
940 break;
941 pElement->hVfsObj = hVfsObj;
942 hPrevVfsObj = hVfsObj;
943 }
944
945 /*
946 * Add another reference to the final object and return.
947 */
948 if (RT_SUCCESS(rc))
949 {
950 uint32_t cRefs = RTVfsObjRetain(hPrevVfsObj);
951 AssertStmt(cRefs != UINT32_MAX, rc = VERR_VFS_CHAIN_IPE);
952 *phVfsObj = hPrevVfsObj;
953 }
954 }
955
956 int rc2 = RTCritSectRwLeaveShared(&g_rtVfsChainElementCritSect);
957 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
958 rc = rc2;
959 }
960 return rc;
961}
962
963
964RTDECL(int) RTVfsChainElementDeregisterProvider(PRTVFSCHAINELEMENTREG pRegRec, bool fFromDtor)
965{
966 /*
967 * Fend off wildlife.
968 */
969 if (pRegRec == NULL)
970 return VINF_SUCCESS;
971 AssertPtrReturn(pRegRec, VERR_INVALID_POINTER);
972 AssertMsgReturn(pRegRec->uVersion == RTVFSCHAINELEMENTREG_VERSION, ("%#x", pRegRec->uVersion), VERR_INVALID_POINTER);
973 AssertMsgReturn(pRegRec->uEndMarker == RTVFSCHAINELEMENTREG_VERSION, ("%#zx", pRegRec->uEndMarker), VERR_INVALID_POINTER);
974 AssertPtrReturn(pRegRec->pszName, VERR_INVALID_POINTER);
975
976 /*
977 * Take the lock if that's safe.
978 */
979 if (!fFromDtor)
980 RTCritSectRwEnterExcl(&g_rtVfsChainElementCritSect);
981 else if (!g_rtVfsChainElementProviderList.pNext)
982 RTListInit(&g_rtVfsChainElementProviderList);
983
984 /*
985 * Ok, remove it.
986 */
987 int rc = VERR_NOT_FOUND;
988 PRTVFSCHAINELEMENTREG pIterator, pIterNext;
989 RTListForEachSafe(&g_rtVfsChainElementProviderList, pIterator, pIterNext, RTVFSCHAINELEMENTREG, ListEntry)
990 {
991 if (pIterator == pRegRec)
992 {
993 RTListNodeRemove(&pRegRec->ListEntry);
994 rc = VINF_SUCCESS;
995 break;
996 }
997 }
998
999 /*
1000 * Leave the lock and return.
1001 */
1002 if (!fFromDtor)
1003 RTCritSectRwLeaveExcl(&g_rtVfsChainElementCritSect);
1004 return rc;
1005}
1006
1007
1008RTDECL(int) RTVfsChainOpenFile(const char *pszSpec, uint64_t fOpen,
1009 PRTVFSFILE phVfsFile, uint32_t *poffError, PRTERRINFO pErrInfo)
1010{
1011 uint32_t offErrorIgn;
1012 if (!poffError)
1013 poffError = &offErrorIgn;
1014 *poffError = 0;
1015 AssertPtrReturn(pszSpec, VERR_INVALID_POINTER);
1016 AssertReturn(*pszSpec != '\0', VERR_INVALID_PARAMETER);
1017 AssertPtrReturn(phVfsFile, VERR_INVALID_POINTER);
1018 AssertPtrNullReturn(pErrInfo, VERR_INVALID_POINTER);
1019
1020 /*
1021 * If it's not a VFS chain spec, treat it as a file.
1022 */
1023 int rc;
1024 if (strncmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX, sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1))
1025 {
1026 RTFILE hFile;
1027 rc = RTFileOpen(&hFile, pszSpec, fOpen);
1028 if (RT_SUCCESS(rc))
1029 {
1030 RTVFSFILE hVfsFile;
1031 rc = RTVfsFileFromRTFile(hFile, fOpen, false /*fLeaveOpen*/, &hVfsFile);
1032 if (RT_SUCCESS(rc))
1033 *phVfsFile = hVfsFile;
1034 else
1035 RTFileClose(hFile);
1036 }
1037 }
1038 else
1039 {
1040 PRTVFSCHAINSPEC pSpec;
1041 rc = RTVfsChainSpecParse(pszSpec, 0 /*fFlags*/, RTVFSOBJTYPE_FILE, &pSpec, poffError);
1042 if (RT_SUCCESS(rc))
1043 {
1044 pSpec->fOpenFile = fOpen;
1045
1046 RTVFSOBJ hVfsObj = NIL_RTVFSOBJ;
1047 rc = RTVfsChainSpecCheckAndSetup(pSpec, NULL /*pReuseSpec*/, &hVfsObj, poffError, pErrInfo);
1048 if (RT_SUCCESS(rc))
1049 {
1050 *phVfsFile = RTVfsObjToFile(hVfsObj);
1051 if (*phVfsFile)
1052 rc = VINF_SUCCESS;
1053 else
1054 rc = VERR_VFS_CHAIN_CAST_FAILED;
1055 RTVfsObjRelease(hVfsObj);
1056 }
1057
1058 RTVfsChainSpecFree(pSpec);
1059 }
1060 }
1061 return rc;
1062}
1063
1064
1065RTDECL(int) RTVfsChainOpenIoStream(const char *pszSpec, uint64_t fOpen,
1066 PRTVFSIOSTREAM phVfsIos, uint32_t *poffError, PRTERRINFO pErrInfo)
1067{
1068 uint32_t offErrorIgn;
1069 if (!poffError)
1070 poffError = &offErrorIgn;
1071 *poffError = 0;
1072 AssertPtrReturn(pszSpec, VERR_INVALID_POINTER);
1073 AssertReturn(*pszSpec != '\0', VERR_INVALID_PARAMETER);
1074 AssertPtrReturn(phVfsIos, VERR_INVALID_POINTER);
1075 AssertPtrNullReturn(pErrInfo, VERR_INVALID_POINTER);
1076
1077 /*
1078 * If it's not a VFS chain spec, treat it as a file.
1079 */
1080 int rc;
1081 if (strncmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX, sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1))
1082 {
1083 RTFILE hFile;
1084 rc = RTFileOpen(&hFile, pszSpec, fOpen);
1085 if (RT_SUCCESS(rc))
1086 {
1087 RTVFSFILE hVfsFile;
1088 rc = RTVfsFileFromRTFile(hFile, fOpen, false /*fLeaveOpen*/, &hVfsFile);
1089 if (RT_SUCCESS(rc))
1090 {
1091 *phVfsIos = RTVfsFileToIoStream(hVfsFile);
1092 RTVfsFileRelease(hVfsFile);
1093 }
1094 else
1095 RTFileClose(hFile);
1096 }
1097 }
1098 /*
1099 * Do the chain thing.
1100 */
1101 else
1102 {
1103 PRTVFSCHAINSPEC pSpec;
1104 rc = RTVfsChainSpecParse(pszSpec, 0 /*fFlags*/, RTVFSOBJTYPE_IO_STREAM, &pSpec, poffError);
1105 if (RT_SUCCESS(rc))
1106 {
1107 pSpec->fOpenFile = fOpen;
1108
1109 RTVFSOBJ hVfsObj = NIL_RTVFSOBJ;
1110 rc = RTVfsChainSpecCheckAndSetup(pSpec, NULL /*pReuseSpec*/, &hVfsObj, poffError, pErrInfo);
1111 if (RT_SUCCESS(rc))
1112 {
1113 *phVfsIos = RTVfsObjToIoStream(hVfsObj);
1114 if (*phVfsIos)
1115 rc = VINF_SUCCESS;
1116 else
1117 rc = VERR_VFS_CHAIN_CAST_FAILED;
1118 RTVfsObjRelease(hVfsObj);
1119 }
1120 RTVfsChainSpecFree(pSpec);
1121 }
1122 }
1123 return rc;
1124}
1125
1126
1127
1128RTDECL(bool) RTVfsChainIsSpec(const char *pszSpec)
1129{
1130 return pszSpec
1131 && strcmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX) == 0;
1132}
1133
1134
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