VirtualBox

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

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

iprt,vfs: Added a generic 'open' provider and sketched out a few directory related APIs.

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