VirtualBox

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

Last change on this file was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use