VirtualBox

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

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

IPRT/vfs-chains: Pass around an pErrInfo buffer too.

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

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