VirtualBox

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

Last change on this file since 74525 was 70997, checked in by vboxsync, 7 years ago

RTVfsChainOpenDir: Default to READ access.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 64.1 KB
Line 
1/* $Id: vfschain.cpp 70997 2018-02-13 12:42:11Z vboxsync $ */
2/** @file
3 * IPRT - Virtual File System, Chains.
4 */
5
6/*
7 * Copyright (C) 2010-2017 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/ctype.h>
37#include <iprt/err.h>
38#include <iprt/file.h>
39#include <iprt/mem.h>
40#include <iprt/once.h>
41#include <iprt/param.h>
42#include <iprt/path.h>
43#include <iprt/semaphore.h>
44#include <iprt/string.h>
45
46#include "internal/file.h"
47#include "internal/magics.h"
48//#include "internal/vfs.h"
49
50
51/*********************************************************************************************************************************
52* Internal Functions *
53*********************************************************************************************************************************/
54static PCRTVFSCHAINELEMENTREG rtVfsChainFindProviderLocked(const char *pszProvider);
55
56
57/*********************************************************************************************************************************
58* Global Variables *
59*********************************************************************************************************************************/
60/** Init the critical section once. */
61static RTONCE g_rtVfsChainElementInitOnce = RTONCE_INITIALIZER;
62/** Critical section protecting g_rtVfsChainElementProviderList. */
63static RTCRITSECTRW g_rtVfsChainElementCritSect;
64/** List of VFS chain element providers (RTVFSCHAINELEMENTREG). */
65static RTLISTANCHOR g_rtVfsChainElementProviderList;
66
67
68
69RTDECL(int) RTVfsChainValidateOpenFileOrIoStream(PRTVFSCHAINSPEC pSpec, PRTVFSCHAINELEMSPEC pElement,
70 uint32_t *poffError, PRTERRINFO pErrInfo)
71{
72 if (pElement->cArgs < 1)
73 return VERR_VFS_CHAIN_AT_LEAST_ONE_ARG;
74 if (pElement->cArgs > 4)
75 return VERR_VFS_CHAIN_AT_MOST_FOUR_ARGS;
76 if (!*pElement->paArgs[0].psz)
77 return VERR_VFS_CHAIN_EMPTY_ARG;
78
79 /*
80 * Calculate the flags, storing them in the first argument.
81 */
82 const char *pszAccess = pElement->cArgs >= 2 ? pElement->paArgs[1].psz : "";
83 if (!*pszAccess)
84 pszAccess = (pSpec->fOpenFile & RTFILE_O_ACCESS_MASK) == RTFILE_O_READWRITE ? "rw"
85 : (pSpec->fOpenFile & RTFILE_O_ACCESS_MASK) == RTFILE_O_READ ? "r"
86 : (pSpec->fOpenFile & RTFILE_O_ACCESS_MASK) == RTFILE_O_WRITE ? "w"
87 : "rw";
88
89 const char *pszDisp = pElement->cArgs >= 3 ? pElement->paArgs[2].psz : "";
90 if (!*pszDisp)
91 pszDisp = strchr(pszAccess, 'w') != NULL ? "open-create" : "open";
92
93 const char *pszSharing = pElement->cArgs >= 4 ? pElement->paArgs[3].psz : "";
94
95 int rc = RTFileModeToFlagsEx(pszAccess, pszDisp, pszSharing, &pElement->uProvider);
96 if (RT_SUCCESS(rc))
97 return VINF_SUCCESS;
98
99 /*
100 * Now try figure out which argument offended us.
101 */
102 AssertReturn(pElement->cArgs > 1, VERR_VFS_CHAIN_IPE);
103 if ( pElement->cArgs == 2
104 || RT_FAILURE(RTFileModeToFlagsEx(pszAccess, "open-create", "", &pElement->uProvider)))
105 {
106 *poffError = pElement->paArgs[1].offSpec;
107 rc = RTErrInfoSet(pErrInfo, VERR_VFS_CHAIN_INVALID_ARGUMENT, "Expected valid access flags: 'r', 'rw', or 'w'");
108 }
109 else if ( pElement->cArgs == 3
110 || RT_FAILURE(RTFileModeToFlagsEx(pszAccess, pszDisp, "", &pElement->uProvider)))
111 {
112 *poffError = pElement->paArgs[2].offSpec;
113 rc = RTErrInfoSet(pErrInfo, VERR_VFS_CHAIN_INVALID_ARGUMENT,
114 "Expected valid open disposition: create, create-replace, open, open-create, open-append, open-truncate");
115 }
116 else
117 {
118 *poffError = pElement->paArgs[3].offSpec;
119 rc = RTErrInfoSet(pErrInfo, VERR_VFS_CHAIN_INVALID_ARGUMENT, "Expected valid sharing flags: nr, nw, nrw, d");
120
121 }
122 return rc;
123}
124
125
126/**
127 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnValidate}
128 */
129static DECLCALLBACK(int) rtVfsChainOpen_Validate(PCRTVFSCHAINELEMENTREG pProviderReg, PRTVFSCHAINSPEC pSpec,
130 PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError, PRTERRINFO pErrInfo)
131{
132 RT_NOREF(pProviderReg);
133
134 /*
135 * Basic checks.
136 */
137 if ( pElement->enmType != RTVFSOBJTYPE_DIR
138 && pElement->enmType != RTVFSOBJTYPE_FILE
139 && pElement->enmType != RTVFSOBJTYPE_IO_STREAM)
140 return VERR_VFS_CHAIN_ONLY_FILE_OR_IOS_OR_DIR;
141 if ( pElement->enmTypeIn != RTVFSOBJTYPE_DIR
142 && pElement->enmTypeIn != RTVFSOBJTYPE_FS_STREAM
143 && pElement->enmTypeIn != RTVFSOBJTYPE_VFS)
144 {
145 if (pElement->enmTypeIn == RTVFSOBJTYPE_INVALID)
146 {
147 /*
148 * First element: Transform into 'stdfile' or 'stddir' if registered.
149 */
150 const char *pszNewProvider = pElement->enmType == RTVFSOBJTYPE_DIR ? "stddir" : "stdfile";
151 PCRTVFSCHAINELEMENTREG pNewProvider = rtVfsChainFindProviderLocked(pszNewProvider);
152 if (pNewProvider)
153 {
154 pElement->pProvider = pNewProvider;
155 return pNewProvider->pfnValidate(pNewProvider, pSpec, pElement, poffError, pErrInfo);
156 }
157 return VERR_VFS_CHAIN_CANNOT_BE_FIRST_ELEMENT;
158 }
159 return VERR_VFS_CHAIN_TAKES_DIR_OR_FSS_OR_VFS;
160 }
161
162 /*
163 * Make common cause with 'stdfile' if we're opening a file or I/O stream.
164 * If the input is a FSS, we have to make sure it's a read-only operation.
165 */
166 if ( pElement->enmType == RTVFSOBJTYPE_FILE
167 || pElement->enmType == RTVFSOBJTYPE_IO_STREAM)
168 {
169 int rc = RTVfsChainValidateOpenFileOrIoStream(pSpec, pElement, poffError, pErrInfo);
170 if (RT_SUCCESS(rc))
171 {
172 if (pElement->enmTypeIn != RTVFSOBJTYPE_FS_STREAM)
173 return VINF_SUCCESS;
174 if ( !(pElement->uProvider & RTFILE_O_WRITE)
175 && (pElement->uProvider & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN)
176 return VINF_SUCCESS;
177 *poffError = pElement->cArgs > 1 ? pElement->paArgs[1].offSpec : pElement->offSpec;
178 return VERR_VFS_CHAIN_INVALID_ARGUMENT;
179 }
180 return rc;
181 }
182
183
184 /*
185 * Directory checks. Path argument only, optional. If not given the root directory of a VFS or the
186 */
187 if (pElement->cArgs > 1)
188 return VERR_VFS_CHAIN_AT_MOST_ONE_ARG;
189 pElement->uProvider = 0;
190 return VINF_SUCCESS;
191}
192
193
194/**
195 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnInstantiate}
196 */
197static DECLCALLBACK(int) rtVfsChainOpen_Instantiate(PCRTVFSCHAINELEMENTREG pProviderReg, PCRTVFSCHAINSPEC pSpec,
198 PCRTVFSCHAINELEMSPEC pElement, RTVFSOBJ hPrevVfsObj,
199 PRTVFSOBJ phVfsObj, uint32_t *poffError, PRTERRINFO pErrInfo)
200{
201 RT_NOREF(pProviderReg, pSpec, pElement, poffError, pErrInfo);
202 AssertReturn(hPrevVfsObj != NIL_RTVFSOBJ, VERR_VFS_CHAIN_IPE);
203
204 /*
205 * File system stream: Seek thru the stream looking for the object to open.
206 */
207 RTVFSFSSTREAM hVfsFssIn = RTVfsObjToFsStream(hPrevVfsObj);
208 if (hVfsFssIn != NIL_RTVFSFSSTREAM)
209 {
210 return VERR_NOT_IMPLEMENTED;
211 }
212
213 /*
214 * VFS: Use RTVfsFileOpen or RTVfsDirOpen.
215 */
216 RTVFS hVfsIn = RTVfsObjToVfs(hPrevVfsObj);
217 if (hVfsIn != NIL_RTVFS)
218 {
219 if ( pElement->enmType == RTVFSOBJTYPE_FILE
220 || pElement->enmType == RTVFSOBJTYPE_IO_STREAM)
221 {
222 RTVFSFILE hVfsFile = NIL_RTVFSFILE;
223 int rc = RTVfsFileOpen(hVfsIn, pElement->paArgs[0].psz, pElement->uProvider, &hVfsFile);
224 RTVfsRelease(hVfsIn);
225 if (RT_SUCCESS(rc))
226 {
227 *phVfsObj = RTVfsObjFromFile(hVfsFile);
228 RTVfsFileRelease(hVfsFile);
229 if (*phVfsObj != NIL_RTVFSOBJ)
230 return VINF_SUCCESS;
231 rc = VERR_VFS_CHAIN_CAST_FAILED;
232 }
233 return rc;
234 }
235 if (pElement->enmType == RTVFSOBJTYPE_DIR)
236 {
237 RTVFSDIR hVfsDir = NIL_RTVFSDIR;
238 int rc = RTVfsDirOpen(hVfsIn, pElement->paArgs[0].psz, (uint32_t)pElement->uProvider, &hVfsDir);
239 RTVfsRelease(hVfsIn);
240 if (RT_SUCCESS(rc))
241 {
242 *phVfsObj = RTVfsObjFromDir(hVfsDir);
243 RTVfsDirRelease(hVfsDir);
244 if (*phVfsObj != NIL_RTVFSOBJ)
245 return VINF_SUCCESS;
246 rc = VERR_VFS_CHAIN_CAST_FAILED;
247 }
248 return rc;
249 }
250 RTVfsRelease(hVfsIn);
251 return VERR_VFS_CHAIN_IPE;
252 }
253
254 /*
255 * Directory: Similar to above, just relative to a directory.
256 */
257 RTVFSDIR hVfsDirIn = RTVfsObjToDir(hPrevVfsObj);
258 if (hVfsDirIn != NIL_RTVFSDIR)
259 {
260 if ( pElement->enmType == RTVFSOBJTYPE_FILE
261 || pElement->enmType == RTVFSOBJTYPE_IO_STREAM)
262 {
263 RTVFSFILE hVfsFile = NIL_RTVFSFILE;
264 int rc = RTVfsDirOpenFile(hVfsDirIn, pElement->paArgs[0].psz, pElement->uProvider, &hVfsFile);
265 RTVfsDirRelease(hVfsDirIn);
266 if (RT_SUCCESS(rc))
267 {
268 *phVfsObj = RTVfsObjFromFile(hVfsFile);
269 RTVfsFileRelease(hVfsFile);
270 if (*phVfsObj != NIL_RTVFSOBJ)
271 return VINF_SUCCESS;
272 rc = VERR_VFS_CHAIN_CAST_FAILED;
273 }
274 return rc;
275 }
276 if (pElement->enmType == RTVFSOBJTYPE_DIR)
277 {
278 RTVFSDIR hVfsDir = NIL_RTVFSDIR;
279 int rc = RTVfsDirOpenDir(hVfsDirIn, pElement->paArgs[0].psz, pElement->uProvider, &hVfsDir);
280 RTVfsDirRelease(hVfsDirIn);
281 if (RT_SUCCESS(rc))
282 {
283 *phVfsObj = RTVfsObjFromDir(hVfsDir);
284 RTVfsDirRelease(hVfsDir);
285 if (*phVfsObj != NIL_RTVFSOBJ)
286 return VINF_SUCCESS;
287 rc = VERR_VFS_CHAIN_CAST_FAILED;
288 }
289 return rc;
290 }
291 RTVfsDirRelease(hVfsDirIn);
292 return VERR_VFS_CHAIN_IPE;
293 }
294
295 AssertFailed();
296 return VERR_VFS_CHAIN_CAST_FAILED;
297}
298
299
300/**
301 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnCanReuseElement}
302 */
303static DECLCALLBACK(bool) rtVfsChainOpen_CanReuseElement(PCRTVFSCHAINELEMENTREG pProviderReg,
304 PCRTVFSCHAINSPEC pSpec, PCRTVFSCHAINELEMSPEC pElement,
305 PCRTVFSCHAINSPEC pReuseSpec, PCRTVFSCHAINELEMSPEC pReuseElement)
306{
307 RT_NOREF(pProviderReg, pSpec, pElement, pReuseSpec, pReuseElement);
308 return false;
309}
310
311
312/** VFS chain element 'gunzip'. */
313static RTVFSCHAINELEMENTREG g_rtVfsChainGunzipReg =
314{
315 /* uVersion = */ RTVFSCHAINELEMENTREG_VERSION,
316 /* fReserved = */ 0,
317 /* pszName = */ "open",
318 /* ListEntry = */ { NULL, NULL },
319 /* pszHelp = */ "Generic VFS open, that can open files (or I/O stream) and directories in a VFS, directory or file system stream.\n"
320 "If used as the first element in a chain, it will work like 'stdfile' or 'stddir' and work on the real file system.\n"
321 "First argument is the filename or directory path.\n"
322 "Second argument is access mode, files only, optional: r, w, rw.\n"
323 "Third argument is open disposition, files only, optional: create, create-replace, open, open-create, open-append, open-truncate.\n"
324 "Forth argument is file sharing, files only, optional: nr, nw, nrw, d.",
325 /* pfnValidate = */ rtVfsChainOpen_Validate,
326 /* pfnInstantiate = */ rtVfsChainOpen_Instantiate,
327 /* pfnCanReuseElement = */ rtVfsChainOpen_CanReuseElement,
328 /* uEndMarker = */ RTVFSCHAINELEMENTREG_VERSION
329};
330
331RTVFSCHAIN_AUTO_REGISTER_ELEMENT_PROVIDER(&g_rtVfsChainGunzipReg, rtVfsChainGunzipReg);
332
333
334
335
336/**
337 * Initializes the globals via RTOnce.
338 *
339 * @returns IPRT status code
340 * @param pvUser Unused, ignored.
341 */
342static DECLCALLBACK(int) rtVfsChainElementRegisterInit(void *pvUser)
343{
344 NOREF(pvUser);
345 if (!g_rtVfsChainElementProviderList.pNext)
346 RTListInit(&g_rtVfsChainElementProviderList);
347 int rc = RTCritSectRwInit(&g_rtVfsChainElementCritSect);
348 if (RT_SUCCESS(rc))
349 {
350 }
351 return rc;
352}
353
354
355RTDECL(int) RTVfsChainElementRegisterProvider(PRTVFSCHAINELEMENTREG pRegRec, bool fFromCtor)
356{
357 int rc;
358
359 /*
360 * Input validation.
361 */
362 AssertPtrReturn(pRegRec, VERR_INVALID_POINTER);
363 AssertMsgReturn(pRegRec->uVersion == RTVFSCHAINELEMENTREG_VERSION, ("%#x", pRegRec->uVersion), VERR_INVALID_POINTER);
364 AssertMsgReturn(pRegRec->uEndMarker == RTVFSCHAINELEMENTREG_VERSION, ("%#zx", pRegRec->uEndMarker), VERR_INVALID_POINTER);
365 AssertReturn(pRegRec->fReserved == 0, VERR_INVALID_POINTER);
366 AssertPtrReturn(pRegRec->pszName, VERR_INVALID_POINTER);
367 AssertPtrReturn(pRegRec->pfnValidate, VERR_INVALID_POINTER);
368 AssertPtrReturn(pRegRec->pfnInstantiate, VERR_INVALID_POINTER);
369 AssertPtrReturn(pRegRec->pfnCanReuseElement, VERR_INVALID_POINTER);
370
371 /*
372 * Init and take the lock.
373 */
374 if (!fFromCtor)
375 {
376 rc = RTOnce(&g_rtVfsChainElementInitOnce, rtVfsChainElementRegisterInit, NULL);
377 if (RT_FAILURE(rc))
378 return rc;
379 rc = RTCritSectRwEnterExcl(&g_rtVfsChainElementCritSect);
380 if (RT_FAILURE(rc))
381 return rc;
382 }
383 else if (!g_rtVfsChainElementProviderList.pNext)
384 RTListInit(&g_rtVfsChainElementProviderList);
385
386 /*
387 * Duplicate name?
388 */
389 rc = VINF_SUCCESS;
390 PRTVFSCHAINELEMENTREG pIterator, pIterNext;
391 RTListForEachSafe(&g_rtVfsChainElementProviderList, pIterator, pIterNext, RTVFSCHAINELEMENTREG, ListEntry)
392 {
393 if (!strcmp(pIterator->pszName, pRegRec->pszName))
394 {
395 AssertMsgFailed(("duplicate name '%s' old=%p new=%p\n", pIterator->pszName, pIterator, pRegRec));
396 rc = VERR_ALREADY_EXISTS;
397 break;
398 }
399 }
400
401 /*
402 * If not, append the record to the list.
403 */
404 if (RT_SUCCESS(rc))
405 RTListAppend(&g_rtVfsChainElementProviderList, &pRegRec->ListEntry);
406
407 /*
408 * Leave the lock and return.
409 */
410 if (!fFromCtor)
411 RTCritSectRwLeaveExcl(&g_rtVfsChainElementCritSect);
412 return rc;
413}
414
415
416/**
417 * Allocates and initializes an empty spec
418 *
419 * @returns Pointer to the spec on success, NULL on failure.
420 */
421static PRTVFSCHAINSPEC rtVfsChainSpecAlloc(void)
422{
423 PRTVFSCHAINSPEC pSpec = (PRTVFSCHAINSPEC)RTMemTmpAlloc(sizeof(*pSpec));
424 if (pSpec)
425 {
426 pSpec->fOpenFile = 0;
427 pSpec->fOpenDir = 0;
428 pSpec->cElements = 0;
429 pSpec->paElements = NULL;
430 }
431 return pSpec;
432}
433
434
435/**
436 * Checks if @a ch is a character that can be escaped.
437 *
438 * @returns true / false.
439 * @param ch The character to consider.
440 */
441DECLINLINE(bool) rtVfsChainSpecIsEscapableChar(char ch)
442{
443 return ch == '('
444 || ch == ')'
445 || ch == '{'
446 || ch == '}'
447 || ch == '\\'
448 || ch == ','
449 || ch == '|'
450 || ch == ':';
451}
452
453
454/**
455 * Duplicate a spec string after unescaping it.
456 *
457 * This differs from RTStrDupN in that it uses RTMemTmpAlloc instead of
458 * RTMemAlloc.
459 *
460 * @returns String copy on success, NULL on failure.
461 * @param psz The string to duplicate.
462 * @param cch The number of bytes to duplicate.
463 * @param prc The status code variable to set on failure. (Leeps the
464 * code shorter. -lazy bird)
465 */
466DECLINLINE(char *) rtVfsChainSpecDupStrN(const char *psz, size_t cch, int *prc)
467{
468 char *pszCopy = (char *)RTMemTmpAlloc(cch + 1);
469 if (pszCopy)
470 {
471 if (!memchr(psz, '\\', cch))
472 {
473 /* Plain string, copy it raw. */
474 memcpy(pszCopy, psz, cch);
475 pszCopy[cch] = '\0';
476 }
477 else
478 {
479 /* Has escape sequences, must unescape it. */
480 char *pszDst = pszCopy;
481 while (cch-- > 0)
482 {
483 char ch = *psz++;
484 if (ch == '\\' && cch > 0)
485 {
486 char ch2 = *psz;
487 if (rtVfsChainSpecIsEscapableChar(ch2))
488 {
489 psz++;
490 cch--;
491 ch = ch2;
492 }
493 }
494 *pszDst++ = ch;
495 }
496 *pszDst = '\0';
497 }
498 }
499 else
500 *prc = VERR_NO_TMP_MEMORY;
501 return pszCopy;
502}
503
504
505/**
506 * Adds an empty element to the chain specification.
507 *
508 * The caller is responsible for filling it the element attributes.
509 *
510 * @returns Pointer to the new element on success, NULL on failure. The
511 * pointer is only valid till the next call to this function.
512 * @param pSpec The chain specification.
513 * @param prc The status code variable to set on failure. (Leeps the
514 * code shorter. -lazy bird)
515 */
516static PRTVFSCHAINELEMSPEC rtVfsChainSpecAddElement(PRTVFSCHAINSPEC pSpec, uint16_t offSpec, int *prc)
517{
518 AssertPtr(pSpec);
519
520 /*
521 * Resize the element table if necessary.
522 */
523 uint32_t const iElement = pSpec->cElements;
524 if ((iElement % 32) == 0)
525 {
526 PRTVFSCHAINELEMSPEC paNew = (PRTVFSCHAINELEMSPEC)RTMemTmpAlloc((iElement + 32) * sizeof(paNew[0]));
527 if (!paNew)
528 {
529 *prc = VERR_NO_TMP_MEMORY;
530 return NULL;
531 }
532
533 memcpy(paNew, pSpec->paElements, iElement * sizeof(paNew[0]));
534 RTMemTmpFree(pSpec->paElements);
535 pSpec->paElements = paNew;
536 }
537
538 /*
539 * Initialize and add the new element.
540 */
541 PRTVFSCHAINELEMSPEC pElement = &pSpec->paElements[iElement];
542 pElement->pszProvider = NULL;
543 pElement->enmTypeIn = iElement ? pSpec->paElements[iElement - 1].enmType : RTVFSOBJTYPE_INVALID;
544 pElement->enmType = RTVFSOBJTYPE_INVALID;
545 pElement->offSpec = offSpec;
546 pElement->cchSpec = 0;
547 pElement->cArgs = 0;
548 pElement->paArgs = NULL;
549 pElement->pProvider = NULL;
550 pElement->hVfsObj = NIL_RTVFSOBJ;
551
552 pSpec->cElements = iElement + 1;
553 return pElement;
554}
555
556
557/**
558 * Adds an argument to the element spec.
559 *
560 * @returns IPRT status code.
561 * @param pElement The element.
562 * @param psz The start of the argument string.
563 * @param cch The length of the argument string, escape
564 * sequences counted twice.
565 */
566static int rtVfsChainSpecElementAddArg(PRTVFSCHAINELEMSPEC pElement, const char *psz, size_t cch, uint16_t offSpec)
567{
568 uint32_t iArg = pElement->cArgs;
569 if ((iArg % 32) == 0)
570 {
571 PRTVFSCHAINELEMENTARG paNew = (PRTVFSCHAINELEMENTARG)RTMemTmpAlloc((iArg + 32) * sizeof(paNew[0]));
572 if (!paNew)
573 return VERR_NO_TMP_MEMORY;
574 memcpy(paNew, pElement->paArgs, iArg * sizeof(paNew[0]));
575 RTMemTmpFree(pElement->paArgs);
576 pElement->paArgs = paNew;
577 }
578
579 int rc = VINF_SUCCESS;
580 pElement->paArgs[iArg].psz = rtVfsChainSpecDupStrN(psz, cch, &rc);
581 pElement->paArgs[iArg].offSpec = offSpec;
582 pElement->cArgs = iArg + 1;
583 return rc;
584}
585
586
587RTDECL(void) RTVfsChainSpecFree(PRTVFSCHAINSPEC pSpec)
588{
589 if (!pSpec)
590 return;
591
592 uint32_t i = pSpec->cElements;
593 while (i-- > 0)
594 {
595 uint32_t iArg = pSpec->paElements[i].cArgs;
596 while (iArg-- > 0)
597 RTMemTmpFree(pSpec->paElements[i].paArgs[iArg].psz);
598 RTMemTmpFree(pSpec->paElements[i].paArgs);
599 RTMemTmpFree(pSpec->paElements[i].pszProvider);
600 if (pSpec->paElements[i].hVfsObj != NIL_RTVFSOBJ)
601 {
602 RTVfsObjRelease(pSpec->paElements[i].hVfsObj);
603 pSpec->paElements[i].hVfsObj = NIL_RTVFSOBJ;
604 }
605 }
606
607 RTMemTmpFree(pSpec->paElements);
608 pSpec->paElements = NULL;
609 RTMemTmpFree(pSpec);
610}
611
612
613/**
614 * Checks if @a psz is pointing to the final element specification.
615 *
616 * @returns true / false.
617 * @param psz Start of an element or path.
618 * @param pcch Where to return the length.
619 */
620static bool rtVfsChainSpecIsFinalElement(const char *psz, size_t *pcch)
621{
622 size_t off = 0;
623 char ch;
624 while ((ch = psz[off]) != '\0')
625 {
626 if (ch == '|' || ch == ':')
627 return false;
628 if ( ch == '\\'
629 && rtVfsChainSpecIsEscapableChar(psz[off + 1]))
630 off++;
631 off++;
632 }
633 *pcch = off;
634 return off > 0;
635}
636
637
638/**
639 * Makes the final path element.
640 * @returns IPRT status code
641 * @param pElement The element.
642 * @param pszPath The path.
643 * @param cchPath The path length.
644 */
645static int rtVfsChainSpecMakeFinalPathElement(PRTVFSCHAINELEMSPEC pElement, const char *pszPath, size_t cchPath)
646{
647 pElement->pszProvider = NULL;
648 pElement->enmType = RTVFSOBJTYPE_END;
649 pElement->cchSpec = (uint16_t)cchPath;
650 return rtVfsChainSpecElementAddArg(pElement, pszPath, cchPath, pElement->offSpec);
651}
652
653
654/**
655 * Finds the end of the argument string.
656 *
657 * @returns The offset of the end character relative to @a psz.
658 * @param psz The argument string.
659 * @param chCloseParen The closing parenthesis.
660 */
661static size_t rtVfsChainSpecFindArgEnd(const char *psz, char const chCloseParen)
662{
663 size_t off = 0;
664 char ch;
665 while ( (ch = psz[off]) != '\0'
666 && ch != ','
667 && ch != chCloseParen)
668 {
669 if ( ch == '\\'
670 && rtVfsChainSpecIsEscapableChar(psz[off+1]))
671 off++;
672 off++;
673 }
674 return off;
675}
676
677
678RTDECL(int) RTVfsChainSpecParse(const char *pszSpec, uint32_t fFlags, RTVFSOBJTYPE enmDesiredType,
679 PRTVFSCHAINSPEC *ppSpec, uint32_t *poffError)
680{
681 if (poffError)
682 {
683 AssertPtrReturn(poffError, VERR_INVALID_POINTER);
684 *poffError = 0;
685 }
686 AssertPtrReturn(ppSpec, VERR_INVALID_POINTER);
687 *ppSpec = NULL;
688 AssertPtrReturn(pszSpec, VERR_INVALID_POINTER);
689 AssertReturn(!(fFlags & ~RTVFSCHAIN_PF_VALID_MASK), VERR_INVALID_PARAMETER);
690 AssertReturn(enmDesiredType > RTVFSOBJTYPE_INVALID && enmDesiredType < RTVFSOBJTYPE_END, VERR_INVALID_PARAMETER);
691
692 /*
693 * Check the start of the specification and allocate an empty return spec.
694 */
695 if (strncmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX, sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1))
696 return VERR_VFS_CHAIN_NO_PREFIX;
697 const char *pszSrc = RTStrStripL(pszSpec + sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1);
698 if (!*pszSrc)
699 return VERR_VFS_CHAIN_EMPTY;
700
701 PRTVFSCHAINSPEC pSpec = rtVfsChainSpecAlloc();
702 if (!pSpec)
703 return VERR_NO_TMP_MEMORY;
704 pSpec->enmDesiredType = enmDesiredType;
705
706 /*
707 * Parse the spec one element at a time.
708 */
709 int rc = VINF_SUCCESS;
710 while (*pszSrc && RT_SUCCESS(rc))
711 {
712 /*
713 * Digest element separator, except for the first element.
714 */
715 if (*pszSrc == '|' || *pszSrc == ':')
716 {
717 if (pSpec->cElements != 0)
718 pszSrc = RTStrStripL(pszSrc + 1);
719 else
720 {
721 rc = VERR_VFS_CHAIN_LEADING_SEPARATOR;
722 break;
723 }
724 }
725 else if (pSpec->cElements != 0)
726 {
727 rc = VERR_VFS_CHAIN_EXPECTED_SEPARATOR;
728 break;
729 }
730
731 /*
732 * Ok, there should be an element here so add one to the return struct.
733 */
734 PRTVFSCHAINELEMSPEC pElement = rtVfsChainSpecAddElement(pSpec, (uint16_t)(pszSrc - pszSpec), &rc);
735 if (!pElement)
736 break;
737
738 /*
739 * First up is the VFS object type followed by a parenthesis/curly, or
740 * this could be the trailing action. Alternatively, we could have a
741 * final path-only element here.
742 */
743 size_t cch;
744 if (strncmp(pszSrc, "base", cch = 4) == 0)
745 pElement->enmType = RTVFSOBJTYPE_BASE;
746 else if (strncmp(pszSrc, "vfs", cch = 3) == 0)
747 pElement->enmType = RTVFSOBJTYPE_VFS;
748 else if (strncmp(pszSrc, "fss", cch = 3) == 0)
749 pElement->enmType = RTVFSOBJTYPE_FS_STREAM;
750 else if (strncmp(pszSrc, "ios", cch = 3) == 0)
751 pElement->enmType = RTVFSOBJTYPE_IO_STREAM;
752 else if (strncmp(pszSrc, "dir", cch = 3) == 0)
753 pElement->enmType = RTVFSOBJTYPE_DIR;
754 else if (strncmp(pszSrc, "file", cch = 4) == 0)
755 pElement->enmType = RTVFSOBJTYPE_FILE;
756 else if (strncmp(pszSrc, "sym", cch = 3) == 0)
757 pElement->enmType = RTVFSOBJTYPE_SYMLINK;
758 else
759 {
760 if (rtVfsChainSpecIsFinalElement(pszSrc, &cch))
761 rc = rtVfsChainSpecMakeFinalPathElement(pElement, pszSrc, cch);
762 else if (*pszSrc == '\0')
763 rc = VERR_VFS_CHAIN_TRAILING_SEPARATOR;
764 else
765 rc = VERR_VFS_CHAIN_UNKNOWN_TYPE;
766 break;
767 }
768
769 /* Check and skip past the parenthesis/curly. If not there, we might
770 have a final path element at our hands. */
771 char const chOpenParen = pszSrc[cch];
772 if (chOpenParen != '(' && chOpenParen != '{')
773 {
774 if (rtVfsChainSpecIsFinalElement(pszSrc, &cch))
775 rc = rtVfsChainSpecMakeFinalPathElement(pElement, pszSrc, cch);
776 else
777 rc = VERR_VFS_CHAIN_EXPECTED_LEFT_PARENTHESES;
778 break;
779 }
780 char const chCloseParen = (chOpenParen == '(' ? ')' : '}');
781 pszSrc = RTStrStripL(pszSrc + cch + 1);
782
783 /*
784 * The name of the element provider.
785 */
786 cch = rtVfsChainSpecFindArgEnd(pszSrc, chCloseParen);
787 if (!cch)
788 {
789 rc = VERR_VFS_CHAIN_EXPECTED_PROVIDER_NAME;
790 break;
791 }
792 pElement->pszProvider = rtVfsChainSpecDupStrN(pszSrc, cch, &rc);
793 if (!pElement->pszProvider)
794 break;
795 pszSrc += cch;
796
797 /*
798 * The arguments.
799 */
800 while (*pszSrc == ',')
801 {
802 pszSrc = RTStrStripL(pszSrc + 1);
803 cch = rtVfsChainSpecFindArgEnd(pszSrc, chCloseParen);
804 rc = rtVfsChainSpecElementAddArg(pElement, pszSrc, cch, (uint16_t)(pszSrc - pszSpec));
805 if (RT_FAILURE(rc))
806 break;
807 pszSrc += cch;
808 }
809 if (RT_FAILURE(rc))
810 break;
811
812 /* Must end with a right parentheses/curly. */
813 if (*pszSrc != chCloseParen)
814 {
815 rc = VERR_VFS_CHAIN_EXPECTED_RIGHT_PARENTHESES;
816 break;
817 }
818 pElement->cchSpec = (uint16_t)(pszSrc - pszSpec) - pElement->offSpec + 1;
819
820 pszSrc = RTStrStripL(pszSrc + 1);
821 }
822
823#if 0
824 /*
825 * Dump the chain. Useful for debugging the above code.
826 */
827 RTAssertMsg2("dbg: cElements=%d rc=%Rrc\n", pSpec->cElements, rc);
828 for (uint32_t i = 0; i < pSpec->cElements; i++)
829 {
830 uint32_t const cArgs = pSpec->paElements[i].cArgs;
831 RTAssertMsg2("dbg: #%u: enmTypeIn=%d enmType=%d cArgs=%d",
832 i, pSpec->paElements[i].enmTypeIn, pSpec->paElements[i].enmType, cArgs);
833 for (uint32_t j = 0; j < cArgs; j++)
834 RTAssertMsg2(j == 0 ? (cArgs > 1 ? " [%s" : " [%s]") : j + 1 < cArgs ? ", %s" : ", %s]",
835 pSpec->paElements[i].paArgs[j].psz);
836 RTAssertMsg2(" offSpec=%d cchSpec=%d", pSpec->paElements[i].offSpec, pSpec->paElements[i].cchSpec);
837 RTAssertMsg2(" spec: %.*s\n", pSpec->paElements[i].cchSpec, &pszSpec[pSpec->paElements[i].offSpec]);
838 }
839#endif
840
841 /*
842 * Return the chain on success; Cleanup and set the error indicator on
843 * failure.
844 */
845 if (RT_SUCCESS(rc))
846 *ppSpec = pSpec;
847 else
848 {
849 if (poffError)
850 *poffError = (uint32_t)(pszSrc - pszSpec);
851 RTVfsChainSpecFree(pSpec);
852 }
853 return rc;
854}
855
856
857/**
858 * Looks up @a pszProvider among the registered providers.
859 *
860 * @returns Pointer to registration record if found, NULL if not.
861 * @param pszProvider The provider.
862 */
863static PCRTVFSCHAINELEMENTREG rtVfsChainFindProviderLocked(const char *pszProvider)
864{
865 PCRTVFSCHAINELEMENTREG pIterator;
866 RTListForEach(&g_rtVfsChainElementProviderList, pIterator, RTVFSCHAINELEMENTREG, ListEntry)
867 {
868 if (strcmp(pIterator->pszName, pszProvider) == 0)
869 return pIterator;
870 }
871 return NULL;
872}
873
874
875/**
876 * Does reusable object type matching.
877 *
878 * @returns true if the types matches, false if not.
879 * @param pElement The target element specification.
880 * @param pReuseElement The source element specification.
881 */
882static bool rtVfsChainMatchReusableType(PRTVFSCHAINELEMSPEC pElement, PRTVFSCHAINELEMSPEC pReuseElement)
883{
884 if (pElement->enmType == pReuseElement->enmType)
885 return true;
886
887 /* File objects can always be cast to I/O streams. */
888 if ( pElement->enmType == RTVFSOBJTYPE_IO_STREAM
889 && pReuseElement->enmType == RTVFSOBJTYPE_FILE)
890 return true;
891
892 /* I/O stream objects may be file objects. */
893 if ( pElement->enmType == RTVFSOBJTYPE_FILE
894 && pReuseElement->enmType == RTVFSOBJTYPE_IO_STREAM)
895 {
896 RTVFSFILE hVfsFile = RTVfsObjToFile(pReuseElement->hVfsObj);
897 if (hVfsFile != NIL_RTVFSFILE)
898 {
899 RTVfsFileRelease(hVfsFile);
900 return true;
901 }
902 }
903 return false;
904}
905
906
907RTDECL(int) RTVfsChainSpecCheckAndSetup(PRTVFSCHAINSPEC pSpec, PCRTVFSCHAINSPEC pReuseSpec,
908 PRTVFSOBJ phVfsObj, const char **ppszFinalPath, uint32_t *poffError, PRTERRINFO pErrInfo)
909{
910 AssertPtrReturn(poffError, VERR_INVALID_POINTER);
911 *poffError = 0;
912 AssertPtrReturn(phVfsObj, VERR_INVALID_POINTER);
913 *phVfsObj = NIL_RTVFSOBJ;
914 AssertPtrReturn(ppszFinalPath, VERR_INVALID_POINTER);
915 *ppszFinalPath = NULL;
916 AssertPtrReturn(pSpec, VERR_INVALID_POINTER);
917 AssertPtrNullReturn(pErrInfo, VERR_INVALID_POINTER);
918
919 /*
920 * Check for final path-only component as we will not touch it yet.
921 */
922 uint32_t cElements = pSpec->cElements;
923 if (cElements > 0)
924 {
925 if (pSpec->paElements[pSpec->cElements - 1].enmType == RTVFSOBJTYPE_END)
926 {
927 if (cElements > 1)
928 cElements--;
929 else
930 {
931 *ppszFinalPath = pSpec->paElements[0].paArgs[0].psz;
932 return VERR_VFS_CHAIN_PATH_ONLY;
933 }
934 }
935 }
936 else
937 return VERR_VFS_CHAIN_EMPTY;
938
939 /*
940 * Enter the critical section after making sure it has been initialized.
941 */
942 int rc = RTOnce(&g_rtVfsChainElementInitOnce, rtVfsChainElementRegisterInit, NULL);
943 if (RT_SUCCESS(rc))
944 rc = RTCritSectRwEnterShared(&g_rtVfsChainElementCritSect);
945 if (RT_SUCCESS(rc))
946 {
947 /*
948 * Resolve and check each element first.
949 */
950 for (uint32_t i = 0; i < cElements; i++)
951 {
952 PRTVFSCHAINELEMSPEC const pElement = &pSpec->paElements[i];
953 *poffError = pElement->offSpec;
954 pElement->pProvider = rtVfsChainFindProviderLocked(pElement->pszProvider);
955 if (pElement->pProvider)
956 {
957 rc = pElement->pProvider->pfnValidate(pElement->pProvider, pSpec, pElement, poffError, pErrInfo);
958 if (RT_SUCCESS(rc))
959 continue;
960 }
961 else
962 rc = VERR_VFS_CHAIN_PROVIDER_NOT_FOUND;
963 break;
964 }
965
966 /*
967 * Check that the desired type is compatible with the last element.
968 */
969 if (RT_SUCCESS(rc))
970 {
971 PRTVFSCHAINELEMSPEC const pLast = &pSpec->paElements[cElements - 1];
972 if (cElements == pSpec->cElements)
973 {
974 if ( pLast->enmType == pSpec->enmDesiredType
975 || pSpec->enmDesiredType == RTVFSOBJTYPE_BASE
976 || ( pLast->enmType == RTVFSOBJTYPE_FILE
977 && pSpec->enmDesiredType == RTVFSOBJTYPE_IO_STREAM) )
978 rc = VINF_SUCCESS;
979 else
980 {
981 *poffError = pLast->offSpec;
982 rc = VERR_VFS_CHAIN_FINAL_TYPE_MISMATCH;
983 }
984 }
985 /* Ends with a path-only element, so check the type of the element preceding it. */
986 else if ( pLast->enmType == RTVFSOBJTYPE_DIR
987 || pLast->enmType == RTVFSOBJTYPE_VFS
988 || pLast->enmType == RTVFSOBJTYPE_FS_STREAM)
989 rc = VINF_SUCCESS;
990 else
991 {
992 *poffError = pLast->offSpec;
993 rc = VERR_VFS_CHAIN_TYPE_MISMATCH_PATH_ONLY;
994 }
995 }
996
997 if (RT_SUCCESS(rc))
998 {
999 /*
1000 * Try construct the chain.
1001 */
1002 RTVFSOBJ hPrevVfsObj = NIL_RTVFSOBJ; /* No extra reference, kept in chain structure. */
1003 for (uint32_t i = 0; i < cElements; i++)
1004 {
1005 PRTVFSCHAINELEMSPEC const pElement = &pSpec->paElements[i];
1006 *poffError = pElement->offSpec;
1007
1008 /*
1009 * Try reuse the VFS objects at the start of the passed in reuse chain.
1010 */
1011 if (!pReuseSpec)
1012 { /* likely */ }
1013 else
1014 {
1015 if (i < pReuseSpec->cElements)
1016 {
1017 PRTVFSCHAINELEMSPEC const pReuseElement = &pReuseSpec->paElements[i];
1018 if (pReuseElement->hVfsObj != NIL_RTVFSOBJ)
1019 {
1020 if (strcmp(pElement->pszProvider, pReuseElement->pszProvider) == 0)
1021 {
1022 if (rtVfsChainMatchReusableType(pElement, pReuseElement))
1023 {
1024 if (pElement->pProvider->pfnCanReuseElement(pElement->pProvider, pSpec, pElement,
1025 pReuseSpec, pReuseElement))
1026 {
1027 uint32_t cRefs = RTVfsObjRetain(pReuseElement->hVfsObj);
1028 if (cRefs != UINT32_MAX)
1029 {
1030 pElement->hVfsObj = hPrevVfsObj = pReuseElement->hVfsObj;
1031 continue;
1032 }
1033 }
1034 }
1035 }
1036 }
1037 }
1038 pReuseSpec = NULL;
1039 }
1040
1041 /*
1042 * Instantiate a new VFS object.
1043 */
1044 RTVFSOBJ hVfsObj = NIL_RTVFSOBJ;
1045 rc = pElement->pProvider->pfnInstantiate(pElement->pProvider, pSpec, pElement, hPrevVfsObj,
1046 &hVfsObj, poffError, pErrInfo);
1047 if (RT_FAILURE(rc))
1048 break;
1049 pElement->hVfsObj = hVfsObj;
1050 hPrevVfsObj = hVfsObj;
1051 }
1052
1053 /*
1054 * Add another reference to the final object and return.
1055 */
1056 if (RT_SUCCESS(rc))
1057 {
1058 uint32_t cRefs = RTVfsObjRetain(hPrevVfsObj);
1059 AssertStmt(cRefs != UINT32_MAX, rc = VERR_VFS_CHAIN_IPE);
1060 *phVfsObj = hPrevVfsObj;
1061 *ppszFinalPath = cElements == pSpec->cElements ? NULL : pSpec->paElements[cElements].paArgs[0].psz;
1062 }
1063 }
1064
1065 int rc2 = RTCritSectRwLeaveShared(&g_rtVfsChainElementCritSect);
1066 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
1067 rc = rc2;
1068 }
1069 return rc;
1070}
1071
1072
1073RTDECL(int) RTVfsChainElementDeregisterProvider(PRTVFSCHAINELEMENTREG pRegRec, bool fFromDtor)
1074{
1075 /*
1076 * Fend off wildlife.
1077 */
1078 if (pRegRec == NULL)
1079 return VINF_SUCCESS;
1080 AssertPtrReturn(pRegRec, VERR_INVALID_POINTER);
1081 AssertMsgReturn(pRegRec->uVersion == RTVFSCHAINELEMENTREG_VERSION, ("%#x", pRegRec->uVersion), VERR_INVALID_POINTER);
1082 AssertMsgReturn(pRegRec->uEndMarker == RTVFSCHAINELEMENTREG_VERSION, ("%#zx", pRegRec->uEndMarker), VERR_INVALID_POINTER);
1083 AssertPtrReturn(pRegRec->pszName, VERR_INVALID_POINTER);
1084
1085 /*
1086 * Take the lock if that's safe.
1087 */
1088 if (!fFromDtor)
1089 RTCritSectRwEnterExcl(&g_rtVfsChainElementCritSect);
1090 else if (!g_rtVfsChainElementProviderList.pNext)
1091 RTListInit(&g_rtVfsChainElementProviderList);
1092
1093 /*
1094 * Ok, remove it.
1095 */
1096 int rc = VERR_NOT_FOUND;
1097 PRTVFSCHAINELEMENTREG pIterator, pIterNext;
1098 RTListForEachSafe(&g_rtVfsChainElementProviderList, pIterator, pIterNext, RTVFSCHAINELEMENTREG, ListEntry)
1099 {
1100 if (pIterator == pRegRec)
1101 {
1102 RTListNodeRemove(&pRegRec->ListEntry);
1103 rc = VINF_SUCCESS;
1104 break;
1105 }
1106 }
1107
1108 /*
1109 * Leave the lock and return.
1110 */
1111 if (!fFromDtor)
1112 RTCritSectRwLeaveExcl(&g_rtVfsChainElementCritSect);
1113 return rc;
1114}
1115
1116
1117RTDECL(int) RTVfsChainOpenObj(const char *pszSpec, uint64_t fFileOpen, uint32_t fObjFlags,
1118 PRTVFSOBJ phVfsObj, uint32_t *poffError, PRTERRINFO pErrInfo)
1119{
1120 /*
1121 * Validate input.
1122 */
1123 uint32_t offErrorIgn;
1124 if (!poffError)
1125 poffError = &offErrorIgn;
1126 *poffError = 0;
1127 AssertPtrReturn(pszSpec, VERR_INVALID_POINTER);
1128 AssertReturn(*pszSpec != '\0', VERR_INVALID_PARAMETER);
1129 AssertPtrReturn(phVfsObj, VERR_INVALID_POINTER);
1130 AssertPtrNullReturn(pErrInfo, VERR_INVALID_POINTER);
1131
1132 int rc = rtFileRecalcAndValidateFlags(&fFileOpen);
1133 if (RT_FAILURE(rc))
1134 return rc;
1135 AssertMsgReturn( RTPATH_F_IS_VALID(fObjFlags, RTVFSOBJ_F_VALID_MASK)
1136 && (fObjFlags & RTVFSOBJ_F_CREATE_MASK) <= RTVFSOBJ_F_CREATE_DIRECTORY,
1137 ("fObjFlags=%#x\n", fObjFlags),
1138 VERR_INVALID_FLAGS);
1139
1140 /*
1141 * Try for a VFS chain first, falling back on regular file system stuff if it's just a path.
1142 */
1143 PRTVFSCHAINSPEC pSpec = NULL;
1144 if (strncmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX, sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1) == 0)
1145 {
1146 rc = RTVfsChainSpecParse(pszSpec, 0 /*fFlags*/, RTVFSOBJTYPE_DIR, &pSpec, poffError);
1147 if (RT_FAILURE(rc))
1148 return rc;
1149
1150 Assert(pSpec->cElements > 0);
1151 if ( pSpec->cElements > 1
1152 || pSpec->paElements[0].enmType != RTVFSOBJTYPE_END)
1153 {
1154 const char *pszFinal = NULL;
1155 RTVFSOBJ hVfsObj = NIL_RTVFSOBJ;
1156 pSpec->fOpenFile = fFileOpen;
1157 rc = RTVfsChainSpecCheckAndSetup(pSpec, NULL /*pReuseSpec*/, &hVfsObj, &pszFinal, poffError, pErrInfo);
1158 if (RT_SUCCESS(rc))
1159 {
1160 if (!pszFinal)
1161 {
1162 *phVfsObj = hVfsObj;
1163 rc = VINF_SUCCESS;
1164 }
1165 else
1166 {
1167 /*
1168 * Do a file open with the final path on the returned object.
1169 */
1170 RTVFS hVfs = RTVfsObjToVfs(hVfsObj);
1171 RTVFSDIR hVfsDir = RTVfsObjToDir(hVfsObj);
1172 RTVFSFSSTREAM hVfsFss = RTVfsObjToFsStream(hVfsObj);
1173 if (hVfs != NIL_RTVFS)
1174 rc = RTVfsObjOpen(hVfs, pszFinal, fFileOpen, fObjFlags, phVfsObj);
1175 else if (hVfsDir != NIL_RTVFSDIR)
1176 rc = RTVfsDirOpenObj(hVfsDir, pszFinal, fFileOpen, fObjFlags, phVfsObj);
1177 else if (hVfsFss != NIL_RTVFSFSSTREAM)
1178 rc = VERR_NOT_IMPLEMENTED;
1179 else
1180 rc = VERR_VFS_CHAIN_TYPE_MISMATCH_PATH_ONLY;
1181 RTVfsRelease(hVfs);
1182 RTVfsDirRelease(hVfsDir);
1183 RTVfsFsStrmRelease(hVfsFss);
1184 RTVfsObjRelease(hVfsObj);
1185 }
1186 }
1187
1188 RTVfsChainSpecFree(pSpec);
1189 return rc;
1190 }
1191
1192 /* Only a path element. */
1193 pszSpec = pSpec->paElements[0].paArgs[0].psz;
1194 }
1195
1196 /*
1197 * Path to regular file system.
1198 * Go via the directory VFS wrapper to avoid duplicating code.
1199 */
1200 RTVFSDIR hVfsParentDir = NIL_RTVFSDIR;
1201 const char *pszFilename;
1202 if (RTPathHasPath(pszSpec))
1203 {
1204 char *pszCopy = RTStrDup(pszSpec);
1205 if (pszCopy)
1206 {
1207 RTPathStripFilename(pszCopy);
1208 rc = RTVfsDirOpenNormal(pszCopy, 0 /*fOpen*/, &hVfsParentDir);
1209 RTStrFree(pszCopy);
1210 }
1211 else
1212 rc = VERR_NO_STR_MEMORY;
1213 pszFilename = RTPathFilename(pszSpec);
1214 }
1215 else
1216 {
1217 pszFilename = pszSpec;
1218 rc = RTVfsDirOpenNormal(".", 0 /*fOpen*/, &hVfsParentDir);
1219 }
1220 if (RT_SUCCESS(rc))
1221 {
1222 rc = RTVfsDirOpenObj(hVfsParentDir, pszFilename, fFileOpen, fObjFlags, phVfsObj);
1223 RTVfsDirRelease(hVfsParentDir);
1224 }
1225
1226 RTVfsChainSpecFree(pSpec);
1227 return rc;
1228}
1229
1230
1231RTDECL(int) RTVfsChainOpenDir(const char *pszSpec, uint32_t fOpen,
1232 PRTVFSDIR phVfsDir, uint32_t *poffError, PRTERRINFO pErrInfo)
1233{
1234 uint32_t offErrorIgn;
1235 if (!poffError)
1236 poffError = &offErrorIgn;
1237 *poffError = 0;
1238 AssertPtrReturn(pszSpec, VERR_INVALID_POINTER);
1239 AssertReturn(*pszSpec != '\0', VERR_INVALID_PARAMETER);
1240 AssertPtrReturn(phVfsDir, VERR_INVALID_POINTER);
1241 AssertPtrNullReturn(pErrInfo, VERR_INVALID_POINTER);
1242
1243 /*
1244 * Try for a VFS chain first, falling back on regular file system stuff if it's just a path.
1245 */
1246 int rc;
1247 PRTVFSCHAINSPEC pSpec = NULL;
1248 if (strncmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX, sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1) == 0)
1249 {
1250 rc = RTVfsChainSpecParse(pszSpec, 0 /*fFlags*/, RTVFSOBJTYPE_DIR, &pSpec, poffError);
1251 if (RT_FAILURE(rc))
1252 return rc;
1253
1254 Assert(pSpec->cElements > 0);
1255 if ( pSpec->cElements > 1
1256 || pSpec->paElements[0].enmType != RTVFSOBJTYPE_END)
1257 {
1258 const char *pszFinal = NULL;
1259 RTVFSOBJ hVfsObj = NIL_RTVFSOBJ;
1260 pSpec->fOpenFile = RTFILE_O_READ;
1261 rc = RTVfsChainSpecCheckAndSetup(pSpec, NULL /*pReuseSpec*/, &hVfsObj, &pszFinal, poffError, pErrInfo);
1262 if (RT_SUCCESS(rc))
1263 {
1264 if (!pszFinal)
1265 {
1266 /* Try convert it to a directory object and we're done. */
1267 *phVfsDir = RTVfsObjToDir(hVfsObj);
1268 if (*phVfsDir)
1269 rc = VINF_SUCCESS;
1270 else
1271 rc = VERR_VFS_CHAIN_CAST_FAILED;
1272 }
1273 else
1274 {
1275 /*
1276 * Do a file open with the final path on the returned object.
1277 */
1278 RTVFS hVfs = RTVfsObjToVfs(hVfsObj);
1279 RTVFSDIR hVfsDir = RTVfsObjToDir(hVfsObj);
1280 RTVFSFSSTREAM hVfsFss = RTVfsObjToFsStream(hVfsObj);
1281 if (hVfs != NIL_RTVFS)
1282 rc = RTVfsDirOpen(hVfs, pszFinal, fOpen, phVfsDir);
1283 else if (hVfsDir != NIL_RTVFSDIR)
1284 rc = RTVfsDirOpenDir(hVfsDir, pszFinal, fOpen, phVfsDir);
1285 else if (hVfsFss != NIL_RTVFSFSSTREAM)
1286 rc = VERR_NOT_IMPLEMENTED;
1287 else
1288 rc = VERR_VFS_CHAIN_TYPE_MISMATCH_PATH_ONLY;
1289 RTVfsRelease(hVfs);
1290 RTVfsDirRelease(hVfsDir);
1291 RTVfsFsStrmRelease(hVfsFss);
1292 }
1293 RTVfsObjRelease(hVfsObj);
1294 }
1295
1296 RTVfsChainSpecFree(pSpec);
1297 return rc;
1298 }
1299
1300 /* Only a path element. */
1301 pszSpec = pSpec->paElements[0].paArgs[0].psz;
1302 }
1303
1304 /*
1305 * Path to regular file system.
1306 */
1307 rc = RTVfsDirOpenNormal(pszSpec, fOpen, phVfsDir);
1308
1309 RTVfsChainSpecFree(pSpec);
1310 return rc;
1311}
1312
1313
1314RTDECL(int) RTVfsChainOpenParentDir(const char *pszSpec, uint32_t fOpen, PRTVFSDIR phVfsDir, const char **ppszChild,
1315 uint32_t *poffError, PRTERRINFO pErrInfo)
1316{
1317 uint32_t offErrorIgn;
1318 if (!poffError)
1319 poffError = &offErrorIgn;
1320 *poffError = 0;
1321 AssertPtrReturn(pszSpec, VERR_INVALID_POINTER);
1322 AssertReturn(*pszSpec != '\0', VERR_INVALID_PARAMETER);
1323 AssertPtrReturn(phVfsDir, VERR_INVALID_POINTER);
1324 AssertPtrReturn(ppszChild, VERR_INVALID_POINTER);
1325 *ppszChild = NULL;
1326 AssertPtrNullReturn(pErrInfo, VERR_INVALID_POINTER);
1327
1328 /*
1329 * Process the spec from the end, trying to find the child part of it.
1330 * We cannot use RTPathFilename here because we must ignore trailing slashes.
1331 */
1332 const char * const pszEnd = RTStrEnd(pszSpec, RTSTR_MAX);
1333 const char *pszChild = pszEnd;
1334 while ( pszChild != pszSpec
1335 && RTPATH_IS_SLASH(pszChild[-1]))
1336 pszChild--;
1337 while ( pszChild != pszSpec
1338 && !RTPATH_IS_SLASH(pszChild[-1])
1339 && !RTPATH_IS_VOLSEP(pszChild[-1]))
1340 pszChild--;
1341 size_t const cchChild = pszEnd - pszChild;
1342 *ppszChild = pszChild;
1343
1344 /*
1345 * Try for a VFS chain first, falling back on regular file system stuff if it's just a path.
1346 */
1347 int rc;
1348 PRTVFSCHAINSPEC pSpec = NULL;
1349 if (strncmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX, sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1) == 0)
1350 {
1351 rc = RTVfsChainSpecParse(pszSpec, 0 /*fFlags*/, RTVFSOBJTYPE_DIR, &pSpec, poffError);
1352 if (RT_FAILURE(rc))
1353 return rc;
1354
1355 Assert(pSpec->cElements > 0);
1356 if ( pSpec->cElements > 1
1357 || pSpec->paElements[0].enmType != RTVFSOBJTYPE_END)
1358 {
1359 /*
1360 * Check that it ends with a path-only element and that this in turn ends with
1361 * what pszChild points to. (We cannot easiy figure out the parent part of
1362 * an element that isn't path-only, so we don't bother trying try.)
1363 */
1364 PRTVFSCHAINELEMSPEC pLast = &pSpec->paElements[pSpec->cElements - 1];
1365 if (pLast->pszProvider == NULL)
1366 {
1367 size_t cchFinal = strlen(pLast->paArgs[0].psz);
1368 if ( cchFinal >= cchChild
1369 && memcmp(&pLast->paArgs[0].psz[cchFinal - cchChild], pszChild, cchChild + 1) == 0)
1370 {
1371 /*
1372 * Drop the child part so we have a path to the parent, then setup the chain.
1373 */
1374 if (cchFinal > cchChild)
1375 pLast->paArgs[0].psz[cchFinal - cchChild] = '\0';
1376 else
1377 pSpec->cElements--;
1378
1379 const char *pszFinal = NULL;
1380 RTVFSOBJ hVfsObj = NIL_RTVFSOBJ;
1381 pSpec->fOpenFile = fOpen;
1382 rc = RTVfsChainSpecCheckAndSetup(pSpec, NULL /*pReuseSpec*/, &hVfsObj, &pszFinal, poffError, pErrInfo);
1383 if (RT_SUCCESS(rc))
1384 {
1385 if (!pszFinal)
1386 {
1387 Assert(cchFinal == cchChild);
1388
1389 /* Try convert it to a file object and we're done. */
1390 *phVfsDir = RTVfsObjToDir(hVfsObj);
1391 if (*phVfsDir)
1392 rc = VINF_SUCCESS;
1393 else
1394 rc = VERR_VFS_CHAIN_CAST_FAILED;
1395 }
1396 else
1397 {
1398 /*
1399 * Do a file open with the final path on the returned object.
1400 */
1401 RTVFS hVfs = RTVfsObjToVfs(hVfsObj);
1402 RTVFSDIR hVfsDir = RTVfsObjToDir(hVfsObj);
1403 RTVFSFSSTREAM hVfsFss = RTVfsObjToFsStream(hVfsObj);
1404 if (hVfs != NIL_RTVFS)
1405 rc = RTVfsDirOpen(hVfs, pszFinal, fOpen, phVfsDir);
1406 else if (hVfsDir != NIL_RTVFSDIR)
1407 rc = RTVfsDirOpenDir(hVfsDir, pszFinal, fOpen, phVfsDir);
1408 else if (hVfsFss != NIL_RTVFSFSSTREAM)
1409 rc = VERR_NOT_IMPLEMENTED;
1410 else
1411 rc = VERR_VFS_CHAIN_TYPE_MISMATCH_PATH_ONLY;
1412 RTVfsRelease(hVfs);
1413 RTVfsDirRelease(hVfsDir);
1414 RTVfsFsStrmRelease(hVfsFss);
1415 }
1416 RTVfsObjRelease(hVfsObj);
1417 }
1418 }
1419 else
1420 rc = VERR_VFS_CHAIN_TOO_SHORT_FOR_PARENT;
1421 }
1422 else
1423 rc = VERR_VFS_CHAIN_NOT_PATH_ONLY;
1424
1425 RTVfsChainSpecFree(pSpec);
1426 return rc;
1427 }
1428
1429 /* Only a path element. */
1430 pszSpec = pSpec->paElements[0].paArgs[0].psz;
1431 }
1432
1433 /*
1434 * Path to regular file system.
1435 */
1436 if (RTPathHasPath(pszSpec))
1437 {
1438 char *pszCopy = RTStrDup(pszSpec);
1439 if (pszCopy)
1440 {
1441 RTPathStripFilename(pszCopy);
1442 rc = RTVfsDirOpenNormal(pszCopy, fOpen, phVfsDir);
1443 RTStrFree(pszCopy);
1444 }
1445 else
1446 rc = VERR_NO_STR_MEMORY;
1447 }
1448 else
1449 rc = RTVfsDirOpenNormal(".", fOpen, phVfsDir);
1450
1451 RTVfsChainSpecFree(pSpec);
1452 return rc;
1453
1454}
1455
1456
1457RTDECL(int) RTVfsChainOpenFile(const char *pszSpec, uint64_t fOpen,
1458 PRTVFSFILE phVfsFile, uint32_t *poffError, PRTERRINFO pErrInfo)
1459{
1460 uint32_t offErrorIgn;
1461 if (!poffError)
1462 poffError = &offErrorIgn;
1463 *poffError = 0;
1464 AssertPtrReturn(pszSpec, VERR_INVALID_POINTER);
1465 AssertReturn(*pszSpec != '\0', VERR_INVALID_PARAMETER);
1466 AssertPtrReturn(phVfsFile, VERR_INVALID_POINTER);
1467 AssertPtrNullReturn(pErrInfo, VERR_INVALID_POINTER);
1468
1469 /*
1470 * Try for a VFS chain first, falling back on regular file system stuff if it's just a path.
1471 */
1472 int rc;
1473 PRTVFSCHAINSPEC pSpec = NULL;
1474 if (strncmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX, sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1) == 0)
1475 {
1476 rc = RTVfsChainSpecParse(pszSpec, 0 /*fFlags*/, RTVFSOBJTYPE_FILE, &pSpec, poffError);
1477 if (RT_FAILURE(rc))
1478 return rc;
1479
1480 Assert(pSpec->cElements > 0);
1481 if ( pSpec->cElements > 1
1482 || pSpec->paElements[0].enmType != RTVFSOBJTYPE_END)
1483 {
1484 const char *pszFinal = NULL;
1485 RTVFSOBJ hVfsObj = NIL_RTVFSOBJ;
1486 pSpec->fOpenFile = fOpen;
1487 rc = RTVfsChainSpecCheckAndSetup(pSpec, NULL /*pReuseSpec*/, &hVfsObj, &pszFinal, poffError, pErrInfo);
1488 if (RT_SUCCESS(rc))
1489 {
1490 if (!pszFinal)
1491 {
1492 /* Try convert it to a file object and we're done. */
1493 *phVfsFile = RTVfsObjToFile(hVfsObj);
1494 if (*phVfsFile)
1495 rc = VINF_SUCCESS;
1496 else
1497 rc = VERR_VFS_CHAIN_CAST_FAILED;
1498 }
1499 else
1500 {
1501 /*
1502 * Do a file open with the final path on the returned object.
1503 */
1504 RTVFS hVfs = RTVfsObjToVfs(hVfsObj);
1505 RTVFSDIR hVfsDir = RTVfsObjToDir(hVfsObj);
1506 RTVFSFSSTREAM hVfsFss = RTVfsObjToFsStream(hVfsObj);
1507 if (hVfs != NIL_RTVFS)
1508 rc = RTVfsFileOpen(hVfs, pszFinal, fOpen, phVfsFile);
1509 else if (hVfsDir != NIL_RTVFSDIR)
1510 rc = RTVfsDirOpenFile(hVfsDir, pszFinal, fOpen, phVfsFile);
1511 else if (hVfsFss != NIL_RTVFSFSSTREAM)
1512 rc = VERR_NOT_IMPLEMENTED;
1513 else
1514 rc = VERR_VFS_CHAIN_TYPE_MISMATCH_PATH_ONLY;
1515 RTVfsRelease(hVfs);
1516 RTVfsDirRelease(hVfsDir);
1517 RTVfsFsStrmRelease(hVfsFss);
1518 }
1519 RTVfsObjRelease(hVfsObj);
1520 }
1521
1522 RTVfsChainSpecFree(pSpec);
1523 return rc;
1524 }
1525
1526 /* Only a path element. */
1527 pszSpec = pSpec->paElements[0].paArgs[0].psz;
1528 }
1529
1530 /*
1531 * Path to regular file system.
1532 */
1533 RTFILE hFile;
1534 rc = RTFileOpen(&hFile, pszSpec, fOpen);
1535 if (RT_SUCCESS(rc))
1536 {
1537 RTVFSFILE hVfsFile;
1538 rc = RTVfsFileFromRTFile(hFile, fOpen, false /*fLeaveOpen*/, &hVfsFile);
1539 if (RT_SUCCESS(rc))
1540 *phVfsFile = hVfsFile;
1541 else
1542 RTFileClose(hFile);
1543 }
1544
1545 RTVfsChainSpecFree(pSpec);
1546 return rc;
1547}
1548
1549
1550RTDECL(int) RTVfsChainOpenIoStream(const char *pszSpec, uint64_t fOpen,
1551 PRTVFSIOSTREAM phVfsIos, uint32_t *poffError, PRTERRINFO pErrInfo)
1552{
1553 uint32_t offErrorIgn;
1554 if (!poffError)
1555 poffError = &offErrorIgn;
1556 *poffError = 0;
1557 AssertPtrReturn(pszSpec, VERR_INVALID_POINTER);
1558 AssertReturn(*pszSpec != '\0', VERR_INVALID_PARAMETER);
1559 AssertPtrReturn(phVfsIos, VERR_INVALID_POINTER);
1560 AssertPtrNullReturn(pErrInfo, VERR_INVALID_POINTER);
1561
1562 /*
1563 * Try for a VFS chain first, falling back on regular file system stuff if it's just a path.
1564 */
1565 int rc;
1566 PRTVFSCHAINSPEC pSpec = NULL;
1567 if (strncmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX, sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1) == 0)
1568 {
1569 rc = RTVfsChainSpecParse(pszSpec, 0 /*fFlags*/, RTVFSOBJTYPE_IO_STREAM, &pSpec, poffError);
1570 if (RT_FAILURE(rc))
1571 return rc;
1572
1573 Assert(pSpec->cElements > 0);
1574 if ( pSpec->cElements > 1
1575 || pSpec->paElements[0].enmType != RTVFSOBJTYPE_END)
1576 {
1577 const char *pszFinal = NULL;
1578 RTVFSOBJ hVfsObj = NIL_RTVFSOBJ;
1579 pSpec->fOpenFile = fOpen;
1580 rc = RTVfsChainSpecCheckAndSetup(pSpec, NULL /*pReuseSpec*/, &hVfsObj, &pszFinal, poffError, pErrInfo);
1581 if (RT_SUCCESS(rc))
1582 {
1583 if (!pszFinal)
1584 {
1585 /* Try convert it to an I/O object and we're done. */
1586 *phVfsIos = RTVfsObjToIoStream(hVfsObj);
1587 if (*phVfsIos)
1588 rc = VINF_SUCCESS;
1589 else
1590 rc = VERR_VFS_CHAIN_CAST_FAILED;
1591 }
1592 else
1593 {
1594 /*
1595 * Do a file open with the final path on the returned object.
1596 */
1597 RTVFS hVfs = RTVfsObjToVfs(hVfsObj);
1598 RTVFSDIR hVfsDir = RTVfsObjToDir(hVfsObj);
1599 RTVFSFSSTREAM hVfsFss = RTVfsObjToFsStream(hVfsObj);
1600 RTVFSFILE hVfsFile = NIL_RTVFSFILE;
1601 if (hVfs != NIL_RTVFS)
1602 rc = RTVfsFileOpen(hVfs, pszFinal, fOpen, &hVfsFile);
1603 else if (hVfsDir != NIL_RTVFSDIR)
1604 rc = RTVfsDirOpenFile(hVfsDir, pszFinal, fOpen, &hVfsFile);
1605 else if (hVfsFss != NIL_RTVFSFSSTREAM)
1606 rc = VERR_NOT_IMPLEMENTED;
1607 else
1608 rc = VERR_VFS_CHAIN_TYPE_MISMATCH_PATH_ONLY;
1609 if (RT_SUCCESS(rc))
1610 {
1611 *phVfsIos = RTVfsFileToIoStream(hVfsFile);
1612 if (*phVfsIos)
1613 rc = VINF_SUCCESS;
1614 else
1615 rc = VERR_VFS_CHAIN_CAST_FAILED;
1616 RTVfsFileRelease(hVfsFile);
1617 }
1618 RTVfsRelease(hVfs);
1619 RTVfsDirRelease(hVfsDir);
1620 RTVfsFsStrmRelease(hVfsFss);
1621 }
1622 RTVfsObjRelease(hVfsObj);
1623 }
1624
1625 RTVfsChainSpecFree(pSpec);
1626 return rc;
1627 }
1628
1629 /* Only a path element. */
1630 pszSpec = pSpec->paElements[0].paArgs[0].psz;
1631 }
1632
1633 /*
1634 * Path to regular file system.
1635 */
1636 RTFILE hFile;
1637 rc = RTFileOpen(&hFile, pszSpec, fOpen);
1638 if (RT_SUCCESS(rc))
1639 {
1640 RTVFSFILE hVfsFile;
1641 rc = RTVfsFileFromRTFile(hFile, fOpen, false /*fLeaveOpen*/, &hVfsFile);
1642 if (RT_SUCCESS(rc))
1643 {
1644 *phVfsIos = RTVfsFileToIoStream(hVfsFile);
1645 RTVfsFileRelease(hVfsFile);
1646 }
1647 else
1648 RTFileClose(hFile);
1649 }
1650
1651 RTVfsChainSpecFree(pSpec);
1652 return rc;
1653}
1654
1655
1656/**
1657 * The equivalent of RTPathQueryInfoEx
1658 */
1659RTDECL(int) RTVfsChainQueryInfo(const char *pszSpec, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs,
1660 uint32_t fFlags, uint32_t *poffError, PRTERRINFO pErrInfo)
1661{
1662 uint32_t offErrorIgn;
1663 if (!poffError)
1664 poffError = &offErrorIgn;
1665 *poffError = 0;
1666 AssertPtrReturn(pszSpec, VERR_INVALID_POINTER);
1667 AssertReturn(*pszSpec != '\0', VERR_INVALID_PARAMETER);
1668 AssertPtrReturn(pObjInfo, VERR_INVALID_POINTER);
1669 AssertReturn(enmAdditionalAttribs >= RTFSOBJATTRADD_NOTHING && enmAdditionalAttribs <= RTFSOBJATTRADD_LAST,
1670 VERR_INVALID_PARAMETER);
1671 AssertPtrNullReturn(pErrInfo, VERR_INVALID_POINTER);
1672
1673 /*
1674 * Try for a VFS chain first, falling back on regular file system stuff if it's just a path.
1675 */
1676 int rc;
1677 PRTVFSCHAINSPEC pSpec = NULL;
1678 if (strncmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX, sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1) == 0)
1679 {
1680 rc = RTVfsChainSpecParse(pszSpec, 0 /*fFlags*/, RTVFSOBJTYPE_BASE, &pSpec, poffError);
1681 if (RT_FAILURE(rc))
1682 return rc;
1683
1684 Assert(pSpec->cElements > 0);
1685 if ( pSpec->cElements > 1
1686 || pSpec->paElements[0].enmType != RTVFSOBJTYPE_END)
1687 {
1688 const char *pszFinal = NULL;
1689 RTVFSOBJ hVfsObj = NIL_RTVFSOBJ;
1690 pSpec->fOpenFile = RTFILE_O_READ | RTFILE_O_OPEN;
1691 rc = RTVfsChainSpecCheckAndSetup(pSpec, NULL /*pReuseSpec*/, &hVfsObj, &pszFinal, poffError, pErrInfo);
1692 if (RT_SUCCESS(rc))
1693 {
1694 if (!pszFinal)
1695 {
1696 /*
1697 * Do the job on the final object.
1698 */
1699 rc = RTVfsObjQueryInfo(hVfsObj, pObjInfo, enmAdditionalAttribs);
1700 }
1701 else
1702 {
1703 /*
1704 * Do a path query operation on the penultimate object.
1705 */
1706 RTVFS hVfs = RTVfsObjToVfs(hVfsObj);
1707 RTVFSDIR hVfsDir = RTVfsObjToDir(hVfsObj);
1708 RTVFSFSSTREAM hVfsFss = RTVfsObjToFsStream(hVfsObj);
1709 if (hVfs != NIL_RTVFS)
1710 rc = RTVfsQueryPathInfo(hVfs, pszFinal, pObjInfo, enmAdditionalAttribs, fFlags);
1711 else if (hVfsDir != NIL_RTVFSDIR)
1712 rc = RTVfsDirQueryPathInfo(hVfsDir, pszFinal, pObjInfo, enmAdditionalAttribs, fFlags);
1713 else if (hVfsFss != NIL_RTVFSFSSTREAM)
1714 rc = VERR_NOT_SUPPORTED;
1715 else
1716 rc = VERR_VFS_CHAIN_TYPE_MISMATCH_PATH_ONLY;
1717 RTVfsRelease(hVfs);
1718 RTVfsDirRelease(hVfsDir);
1719 RTVfsFsStrmRelease(hVfsFss);
1720 }
1721 RTVfsObjRelease(hVfsObj);
1722 }
1723
1724 RTVfsChainSpecFree(pSpec);
1725 return rc;
1726 }
1727
1728 /* Only a path element. */
1729 pszSpec = pSpec->paElements[0].paArgs[0].psz;
1730 }
1731
1732 /*
1733 * Path to regular file system.
1734 */
1735 rc = RTPathQueryInfoEx(pszSpec, pObjInfo, enmAdditionalAttribs, fFlags);
1736
1737 RTVfsChainSpecFree(pSpec);
1738 return rc;
1739}
1740
1741
1742RTDECL(bool) RTVfsChainIsSpec(const char *pszSpec)
1743{
1744 return pszSpec
1745 && strncmp(pszSpec, RT_STR_TUPLE(RTVFSCHAIN_SPEC_PREFIX)) == 0;
1746}
1747
1748
1749RTDECL(int) RTVfsChainQueryFinalPath(const char *pszSpec, char **ppszFinalPath, uint32_t *poffError)
1750{
1751 /* Make sure we've got an error info variable. */
1752 uint32_t offErrorIgn;
1753 if (!poffError)
1754 poffError = &offErrorIgn;
1755 *poffError = 0;
1756
1757 /*
1758 * If not chain specifier, just duplicate the input and return.
1759 */
1760 if (strncmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX, sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1) != 0)
1761 return RTStrDupEx(ppszFinalPath, pszSpec);
1762
1763 /*
1764 * Parse it and check out the last element.
1765 */
1766 PRTVFSCHAINSPEC pSpec = NULL;
1767 int rc = RTVfsChainSpecParse(pszSpec, 0 /*fFlags*/, RTVFSOBJTYPE_BASE, &pSpec, poffError);
1768 if (RT_SUCCESS(rc))
1769 {
1770 PCRTVFSCHAINELEMSPEC pLast = &pSpec->paElements[pSpec->cElements - 1];
1771 if (pLast->pszProvider == NULL)
1772 rc = RTStrDupEx(ppszFinalPath, pLast->paArgs[0].psz);
1773 else
1774 {
1775 rc = VERR_VFS_CHAIN_NOT_PATH_ONLY;
1776 *poffError = pLast->offSpec;
1777 }
1778 RTVfsChainSpecFree(pSpec);
1779 }
1780 return rc;
1781}
1782
1783
1784RTDECL(int) RTVfsChainSplitOffFinalPath(char *pszSpec, char **ppszSpec, char **ppszFinalPath, uint32_t *poffError)
1785{
1786 /* Make sure we've got an error info variable. */
1787 uint32_t offErrorIgn;
1788 if (!poffError)
1789 poffError = &offErrorIgn;
1790 *poffError = 0;
1791
1792 /*
1793 * If not chain specifier, just duplicate the input and return.
1794 */
1795 if (strncmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX, sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1) != 0)
1796 {
1797 *ppszSpec = NULL;
1798 *ppszFinalPath = pszSpec;
1799 return VINF_SUCCESS;
1800 }
1801
1802 /*
1803 * Parse it and check out the last element.
1804 */
1805 PRTVFSCHAINSPEC pSpec = NULL;
1806 int rc = RTVfsChainSpecParse(pszSpec, 0 /*fFlags*/, RTVFSOBJTYPE_BASE, &pSpec, poffError);
1807 if (RT_SUCCESS(rc))
1808 {
1809 Assert(pSpec->cElements > 0);
1810 PCRTVFSCHAINELEMSPEC pLast = &pSpec->paElements[pSpec->cElements - 1];
1811 if (pLast->pszProvider == NULL)
1812 {
1813 char *psz = &pszSpec[pLast->offSpec];
1814 *ppszFinalPath = psz;
1815 if (pSpec->cElements > 1)
1816 {
1817 *ppszSpec = pszSpec;
1818
1819 /* Remove the separator and any whitespace around it. */
1820 while ( psz != pszSpec
1821 && RT_C_IS_SPACE(psz[-1]))
1822 psz--;
1823 if ( psz != pszSpec
1824 && ( psz[-1] == ':'
1825 || psz[-1] == '|'))
1826 psz--;
1827 while ( psz != pszSpec
1828 && RT_C_IS_SPACE(psz[-1]))
1829 psz--;
1830 *psz = '\0';
1831 }
1832 else
1833 *ppszSpec = NULL;
1834 }
1835 else
1836 {
1837 *ppszFinalPath = NULL;
1838 *ppszSpec = pszSpec;
1839 }
1840 RTVfsChainSpecFree(pSpec);
1841 }
1842 else
1843 {
1844 *ppszSpec = NULL;
1845 *ppszFinalPath = NULL;
1846 }
1847 return rc;
1848}
1849
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