VirtualBox

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

Last change on this file since 82660 was 77772, checked in by vboxsync, 6 years ago

vfschain.cpp: Call memcpy only when there is something to copy to avoid ASAN runtime warnings (the spec says that passing NULL pointers is undefined even though most memcpy implementations shouldn't bother as long as the n argument is 0 as well)

  • 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 77772 2019-03-18 21:59:40Z vboxsync $ */
2/** @file
3 * IPRT - Virtual File System, Chains.
4 */
5
6/*
7 * Copyright (C) 2010-2019 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 if (iElement)
534 memcpy(paNew, pSpec->paElements, iElement * sizeof(paNew[0]));
535 RTMemTmpFree(pSpec->paElements);
536 pSpec->paElements = paNew;
537 }
538
539 /*
540 * Initialize and add the new element.
541 */
542 PRTVFSCHAINELEMSPEC pElement = &pSpec->paElements[iElement];
543 pElement->pszProvider = NULL;
544 pElement->enmTypeIn = iElement ? pSpec->paElements[iElement - 1].enmType : RTVFSOBJTYPE_INVALID;
545 pElement->enmType = RTVFSOBJTYPE_INVALID;
546 pElement->offSpec = offSpec;
547 pElement->cchSpec = 0;
548 pElement->cArgs = 0;
549 pElement->paArgs = NULL;
550 pElement->pProvider = NULL;
551 pElement->hVfsObj = NIL_RTVFSOBJ;
552
553 pSpec->cElements = iElement + 1;
554 return pElement;
555}
556
557
558/**
559 * Adds an argument to the element spec.
560 *
561 * @returns IPRT status code.
562 * @param pElement The element.
563 * @param psz The start of the argument string.
564 * @param cch The length of the argument string, escape
565 * sequences counted twice.
566 */
567static int rtVfsChainSpecElementAddArg(PRTVFSCHAINELEMSPEC pElement, const char *psz, size_t cch, uint16_t offSpec)
568{
569 uint32_t iArg = pElement->cArgs;
570 if ((iArg % 32) == 0)
571 {
572 PRTVFSCHAINELEMENTARG paNew = (PRTVFSCHAINELEMENTARG)RTMemTmpAlloc((iArg + 32) * sizeof(paNew[0]));
573 if (!paNew)
574 return VERR_NO_TMP_MEMORY;
575 if (iArg)
576 memcpy(paNew, pElement->paArgs, iArg * sizeof(paNew[0]));
577 RTMemTmpFree(pElement->paArgs);
578 pElement->paArgs = paNew;
579 }
580
581 int rc = VINF_SUCCESS;
582 pElement->paArgs[iArg].psz = rtVfsChainSpecDupStrN(psz, cch, &rc);
583 pElement->paArgs[iArg].offSpec = offSpec;
584 pElement->cArgs = iArg + 1;
585 return rc;
586}
587
588
589RTDECL(void) RTVfsChainSpecFree(PRTVFSCHAINSPEC pSpec)
590{
591 if (!pSpec)
592 return;
593
594 uint32_t i = pSpec->cElements;
595 while (i-- > 0)
596 {
597 uint32_t iArg = pSpec->paElements[i].cArgs;
598 while (iArg-- > 0)
599 RTMemTmpFree(pSpec->paElements[i].paArgs[iArg].psz);
600 RTMemTmpFree(pSpec->paElements[i].paArgs);
601 RTMemTmpFree(pSpec->paElements[i].pszProvider);
602 if (pSpec->paElements[i].hVfsObj != NIL_RTVFSOBJ)
603 {
604 RTVfsObjRelease(pSpec->paElements[i].hVfsObj);
605 pSpec->paElements[i].hVfsObj = NIL_RTVFSOBJ;
606 }
607 }
608
609 RTMemTmpFree(pSpec->paElements);
610 pSpec->paElements = NULL;
611 RTMemTmpFree(pSpec);
612}
613
614
615/**
616 * Checks if @a psz is pointing to the final element specification.
617 *
618 * @returns true / false.
619 * @param psz Start of an element or path.
620 * @param pcch Where to return the length.
621 */
622static bool rtVfsChainSpecIsFinalElement(const char *psz, size_t *pcch)
623{
624 size_t off = 0;
625 char ch;
626 while ((ch = psz[off]) != '\0')
627 {
628 if (ch == '|' || ch == ':')
629 return false;
630 if ( ch == '\\'
631 && rtVfsChainSpecIsEscapableChar(psz[off + 1]))
632 off++;
633 off++;
634 }
635 *pcch = off;
636 return off > 0;
637}
638
639
640/**
641 * Makes the final path element.
642 * @returns IPRT status code
643 * @param pElement The element.
644 * @param pszPath The path.
645 * @param cchPath The path length.
646 */
647static int rtVfsChainSpecMakeFinalPathElement(PRTVFSCHAINELEMSPEC pElement, const char *pszPath, size_t cchPath)
648{
649 pElement->pszProvider = NULL;
650 pElement->enmType = RTVFSOBJTYPE_END;
651 pElement->cchSpec = (uint16_t)cchPath;
652 return rtVfsChainSpecElementAddArg(pElement, pszPath, cchPath, pElement->offSpec);
653}
654
655
656/**
657 * Finds the end of the argument string.
658 *
659 * @returns The offset of the end character relative to @a psz.
660 * @param psz The argument string.
661 * @param chCloseParen The closing parenthesis.
662 */
663static size_t rtVfsChainSpecFindArgEnd(const char *psz, char const chCloseParen)
664{
665 size_t off = 0;
666 char ch;
667 while ( (ch = psz[off]) != '\0'
668 && ch != ','
669 && ch != chCloseParen)
670 {
671 if ( ch == '\\'
672 && rtVfsChainSpecIsEscapableChar(psz[off+1]))
673 off++;
674 off++;
675 }
676 return off;
677}
678
679
680RTDECL(int) RTVfsChainSpecParse(const char *pszSpec, uint32_t fFlags, RTVFSOBJTYPE enmDesiredType,
681 PRTVFSCHAINSPEC *ppSpec, uint32_t *poffError)
682{
683 if (poffError)
684 {
685 AssertPtrReturn(poffError, VERR_INVALID_POINTER);
686 *poffError = 0;
687 }
688 AssertPtrReturn(ppSpec, VERR_INVALID_POINTER);
689 *ppSpec = NULL;
690 AssertPtrReturn(pszSpec, VERR_INVALID_POINTER);
691 AssertReturn(!(fFlags & ~RTVFSCHAIN_PF_VALID_MASK), VERR_INVALID_PARAMETER);
692 AssertReturn(enmDesiredType > RTVFSOBJTYPE_INVALID && enmDesiredType < RTVFSOBJTYPE_END, VERR_INVALID_PARAMETER);
693
694 /*
695 * Check the start of the specification and allocate an empty return spec.
696 */
697 if (strncmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX, sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1))
698 return VERR_VFS_CHAIN_NO_PREFIX;
699 const char *pszSrc = RTStrStripL(pszSpec + sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1);
700 if (!*pszSrc)
701 return VERR_VFS_CHAIN_EMPTY;
702
703 PRTVFSCHAINSPEC pSpec = rtVfsChainSpecAlloc();
704 if (!pSpec)
705 return VERR_NO_TMP_MEMORY;
706 pSpec->enmDesiredType = enmDesiredType;
707
708 /*
709 * Parse the spec one element at a time.
710 */
711 int rc = VINF_SUCCESS;
712 while (*pszSrc && RT_SUCCESS(rc))
713 {
714 /*
715 * Digest element separator, except for the first element.
716 */
717 if (*pszSrc == '|' || *pszSrc == ':')
718 {
719 if (pSpec->cElements != 0)
720 pszSrc = RTStrStripL(pszSrc + 1);
721 else
722 {
723 rc = VERR_VFS_CHAIN_LEADING_SEPARATOR;
724 break;
725 }
726 }
727 else if (pSpec->cElements != 0)
728 {
729 rc = VERR_VFS_CHAIN_EXPECTED_SEPARATOR;
730 break;
731 }
732
733 /*
734 * Ok, there should be an element here so add one to the return struct.
735 */
736 PRTVFSCHAINELEMSPEC pElement = rtVfsChainSpecAddElement(pSpec, (uint16_t)(pszSrc - pszSpec), &rc);
737 if (!pElement)
738 break;
739
740 /*
741 * First up is the VFS object type followed by a parenthesis/curly, or
742 * this could be the trailing action. Alternatively, we could have a
743 * final path-only element here.
744 */
745 size_t cch;
746 if (strncmp(pszSrc, "base", cch = 4) == 0)
747 pElement->enmType = RTVFSOBJTYPE_BASE;
748 else if (strncmp(pszSrc, "vfs", cch = 3) == 0)
749 pElement->enmType = RTVFSOBJTYPE_VFS;
750 else if (strncmp(pszSrc, "fss", cch = 3) == 0)
751 pElement->enmType = RTVFSOBJTYPE_FS_STREAM;
752 else if (strncmp(pszSrc, "ios", cch = 3) == 0)
753 pElement->enmType = RTVFSOBJTYPE_IO_STREAM;
754 else if (strncmp(pszSrc, "dir", cch = 3) == 0)
755 pElement->enmType = RTVFSOBJTYPE_DIR;
756 else if (strncmp(pszSrc, "file", cch = 4) == 0)
757 pElement->enmType = RTVFSOBJTYPE_FILE;
758 else if (strncmp(pszSrc, "sym", cch = 3) == 0)
759 pElement->enmType = RTVFSOBJTYPE_SYMLINK;
760 else
761 {
762 if (rtVfsChainSpecIsFinalElement(pszSrc, &cch))
763 rc = rtVfsChainSpecMakeFinalPathElement(pElement, pszSrc, cch);
764 else if (*pszSrc == '\0')
765 rc = VERR_VFS_CHAIN_TRAILING_SEPARATOR;
766 else
767 rc = VERR_VFS_CHAIN_UNKNOWN_TYPE;
768 break;
769 }
770
771 /* Check and skip past the parenthesis/curly. If not there, we might
772 have a final path element at our hands. */
773 char const chOpenParen = pszSrc[cch];
774 if (chOpenParen != '(' && chOpenParen != '{')
775 {
776 if (rtVfsChainSpecIsFinalElement(pszSrc, &cch))
777 rc = rtVfsChainSpecMakeFinalPathElement(pElement, pszSrc, cch);
778 else
779 rc = VERR_VFS_CHAIN_EXPECTED_LEFT_PARENTHESES;
780 break;
781 }
782 char const chCloseParen = (chOpenParen == '(' ? ')' : '}');
783 pszSrc = RTStrStripL(pszSrc + cch + 1);
784
785 /*
786 * The name of the element provider.
787 */
788 cch = rtVfsChainSpecFindArgEnd(pszSrc, chCloseParen);
789 if (!cch)
790 {
791 rc = VERR_VFS_CHAIN_EXPECTED_PROVIDER_NAME;
792 break;
793 }
794 pElement->pszProvider = rtVfsChainSpecDupStrN(pszSrc, cch, &rc);
795 if (!pElement->pszProvider)
796 break;
797 pszSrc += cch;
798
799 /*
800 * The arguments.
801 */
802 while (*pszSrc == ',')
803 {
804 pszSrc = RTStrStripL(pszSrc + 1);
805 cch = rtVfsChainSpecFindArgEnd(pszSrc, chCloseParen);
806 rc = rtVfsChainSpecElementAddArg(pElement, pszSrc, cch, (uint16_t)(pszSrc - pszSpec));
807 if (RT_FAILURE(rc))
808 break;
809 pszSrc += cch;
810 }
811 if (RT_FAILURE(rc))
812 break;
813
814 /* Must end with a right parentheses/curly. */
815 if (*pszSrc != chCloseParen)
816 {
817 rc = VERR_VFS_CHAIN_EXPECTED_RIGHT_PARENTHESES;
818 break;
819 }
820 pElement->cchSpec = (uint16_t)(pszSrc - pszSpec) - pElement->offSpec + 1;
821
822 pszSrc = RTStrStripL(pszSrc + 1);
823 }
824
825#if 0
826 /*
827 * Dump the chain. Useful for debugging the above code.
828 */
829 RTAssertMsg2("dbg: cElements=%d rc=%Rrc\n", pSpec->cElements, rc);
830 for (uint32_t i = 0; i < pSpec->cElements; i++)
831 {
832 uint32_t const cArgs = pSpec->paElements[i].cArgs;
833 RTAssertMsg2("dbg: #%u: enmTypeIn=%d enmType=%d cArgs=%d",
834 i, pSpec->paElements[i].enmTypeIn, pSpec->paElements[i].enmType, cArgs);
835 for (uint32_t j = 0; j < cArgs; j++)
836 RTAssertMsg2(j == 0 ? (cArgs > 1 ? " [%s" : " [%s]") : j + 1 < cArgs ? ", %s" : ", %s]",
837 pSpec->paElements[i].paArgs[j].psz);
838 RTAssertMsg2(" offSpec=%d cchSpec=%d", pSpec->paElements[i].offSpec, pSpec->paElements[i].cchSpec);
839 RTAssertMsg2(" spec: %.*s\n", pSpec->paElements[i].cchSpec, &pszSpec[pSpec->paElements[i].offSpec]);
840 }
841#endif
842
843 /*
844 * Return the chain on success; Cleanup and set the error indicator on
845 * failure.
846 */
847 if (RT_SUCCESS(rc))
848 *ppSpec = pSpec;
849 else
850 {
851 if (poffError)
852 *poffError = (uint32_t)(pszSrc - pszSpec);
853 RTVfsChainSpecFree(pSpec);
854 }
855 return rc;
856}
857
858
859/**
860 * Looks up @a pszProvider among the registered providers.
861 *
862 * @returns Pointer to registration record if found, NULL if not.
863 * @param pszProvider The provider.
864 */
865static PCRTVFSCHAINELEMENTREG rtVfsChainFindProviderLocked(const char *pszProvider)
866{
867 PCRTVFSCHAINELEMENTREG pIterator;
868 RTListForEach(&g_rtVfsChainElementProviderList, pIterator, RTVFSCHAINELEMENTREG, ListEntry)
869 {
870 if (strcmp(pIterator->pszName, pszProvider) == 0)
871 return pIterator;
872 }
873 return NULL;
874}
875
876
877/**
878 * Does reusable object type matching.
879 *
880 * @returns true if the types matches, false if not.
881 * @param pElement The target element specification.
882 * @param pReuseElement The source element specification.
883 */
884static bool rtVfsChainMatchReusableType(PRTVFSCHAINELEMSPEC pElement, PRTVFSCHAINELEMSPEC pReuseElement)
885{
886 if (pElement->enmType == pReuseElement->enmType)
887 return true;
888
889 /* File objects can always be cast to I/O streams. */
890 if ( pElement->enmType == RTVFSOBJTYPE_IO_STREAM
891 && pReuseElement->enmType == RTVFSOBJTYPE_FILE)
892 return true;
893
894 /* I/O stream objects may be file objects. */
895 if ( pElement->enmType == RTVFSOBJTYPE_FILE
896 && pReuseElement->enmType == RTVFSOBJTYPE_IO_STREAM)
897 {
898 RTVFSFILE hVfsFile = RTVfsObjToFile(pReuseElement->hVfsObj);
899 if (hVfsFile != NIL_RTVFSFILE)
900 {
901 RTVfsFileRelease(hVfsFile);
902 return true;
903 }
904 }
905 return false;
906}
907
908
909RTDECL(int) RTVfsChainSpecCheckAndSetup(PRTVFSCHAINSPEC pSpec, PCRTVFSCHAINSPEC pReuseSpec,
910 PRTVFSOBJ phVfsObj, const char **ppszFinalPath, uint32_t *poffError, PRTERRINFO pErrInfo)
911{
912 AssertPtrReturn(poffError, VERR_INVALID_POINTER);
913 *poffError = 0;
914 AssertPtrReturn(phVfsObj, VERR_INVALID_POINTER);
915 *phVfsObj = NIL_RTVFSOBJ;
916 AssertPtrReturn(ppszFinalPath, VERR_INVALID_POINTER);
917 *ppszFinalPath = NULL;
918 AssertPtrReturn(pSpec, VERR_INVALID_POINTER);
919 AssertPtrNullReturn(pErrInfo, VERR_INVALID_POINTER);
920
921 /*
922 * Check for final path-only component as we will not touch it yet.
923 */
924 uint32_t cElements = pSpec->cElements;
925 if (cElements > 0)
926 {
927 if (pSpec->paElements[pSpec->cElements - 1].enmType == RTVFSOBJTYPE_END)
928 {
929 if (cElements > 1)
930 cElements--;
931 else
932 {
933 *ppszFinalPath = pSpec->paElements[0].paArgs[0].psz;
934 return VERR_VFS_CHAIN_PATH_ONLY;
935 }
936 }
937 }
938 else
939 return VERR_VFS_CHAIN_EMPTY;
940
941 /*
942 * Enter the critical section after making sure it has been initialized.
943 */
944 int rc = RTOnce(&g_rtVfsChainElementInitOnce, rtVfsChainElementRegisterInit, NULL);
945 if (RT_SUCCESS(rc))
946 rc = RTCritSectRwEnterShared(&g_rtVfsChainElementCritSect);
947 if (RT_SUCCESS(rc))
948 {
949 /*
950 * Resolve and check each element first.
951 */
952 for (uint32_t i = 0; i < cElements; i++)
953 {
954 PRTVFSCHAINELEMSPEC const pElement = &pSpec->paElements[i];
955 *poffError = pElement->offSpec;
956 pElement->pProvider = rtVfsChainFindProviderLocked(pElement->pszProvider);
957 if (pElement->pProvider)
958 {
959 rc = pElement->pProvider->pfnValidate(pElement->pProvider, pSpec, pElement, poffError, pErrInfo);
960 if (RT_SUCCESS(rc))
961 continue;
962 }
963 else
964 rc = VERR_VFS_CHAIN_PROVIDER_NOT_FOUND;
965 break;
966 }
967
968 /*
969 * Check that the desired type is compatible with the last element.
970 */
971 if (RT_SUCCESS(rc))
972 {
973 PRTVFSCHAINELEMSPEC const pLast = &pSpec->paElements[cElements - 1];
974 if (cElements == pSpec->cElements)
975 {
976 if ( pLast->enmType == pSpec->enmDesiredType
977 || pSpec->enmDesiredType == RTVFSOBJTYPE_BASE
978 || ( pLast->enmType == RTVFSOBJTYPE_FILE
979 && pSpec->enmDesiredType == RTVFSOBJTYPE_IO_STREAM) )
980 rc = VINF_SUCCESS;
981 else
982 {
983 *poffError = pLast->offSpec;
984 rc = VERR_VFS_CHAIN_FINAL_TYPE_MISMATCH;
985 }
986 }
987 /* Ends with a path-only element, so check the type of the element preceding it. */
988 else if ( pLast->enmType == RTVFSOBJTYPE_DIR
989 || pLast->enmType == RTVFSOBJTYPE_VFS
990 || pLast->enmType == RTVFSOBJTYPE_FS_STREAM)
991 rc = VINF_SUCCESS;
992 else
993 {
994 *poffError = pLast->offSpec;
995 rc = VERR_VFS_CHAIN_TYPE_MISMATCH_PATH_ONLY;
996 }
997 }
998
999 if (RT_SUCCESS(rc))
1000 {
1001 /*
1002 * Try construct the chain.
1003 */
1004 RTVFSOBJ hPrevVfsObj = NIL_RTVFSOBJ; /* No extra reference, kept in chain structure. */
1005 for (uint32_t i = 0; i < cElements; i++)
1006 {
1007 PRTVFSCHAINELEMSPEC const pElement = &pSpec->paElements[i];
1008 *poffError = pElement->offSpec;
1009
1010 /*
1011 * Try reuse the VFS objects at the start of the passed in reuse chain.
1012 */
1013 if (!pReuseSpec)
1014 { /* likely */ }
1015 else
1016 {
1017 if (i < pReuseSpec->cElements)
1018 {
1019 PRTVFSCHAINELEMSPEC const pReuseElement = &pReuseSpec->paElements[i];
1020 if (pReuseElement->hVfsObj != NIL_RTVFSOBJ)
1021 {
1022 if (strcmp(pElement->pszProvider, pReuseElement->pszProvider) == 0)
1023 {
1024 if (rtVfsChainMatchReusableType(pElement, pReuseElement))
1025 {
1026 if (pElement->pProvider->pfnCanReuseElement(pElement->pProvider, pSpec, pElement,
1027 pReuseSpec, pReuseElement))
1028 {
1029 uint32_t cRefs = RTVfsObjRetain(pReuseElement->hVfsObj);
1030 if (cRefs != UINT32_MAX)
1031 {
1032 pElement->hVfsObj = hPrevVfsObj = pReuseElement->hVfsObj;
1033 continue;
1034 }
1035 }
1036 }
1037 }
1038 }
1039 }
1040 pReuseSpec = NULL;
1041 }
1042
1043 /*
1044 * Instantiate a new VFS object.
1045 */
1046 RTVFSOBJ hVfsObj = NIL_RTVFSOBJ;
1047 rc = pElement->pProvider->pfnInstantiate(pElement->pProvider, pSpec, pElement, hPrevVfsObj,
1048 &hVfsObj, poffError, pErrInfo);
1049 if (RT_FAILURE(rc))
1050 break;
1051 pElement->hVfsObj = hVfsObj;
1052 hPrevVfsObj = hVfsObj;
1053 }
1054
1055 /*
1056 * Add another reference to the final object and return.
1057 */
1058 if (RT_SUCCESS(rc))
1059 {
1060 uint32_t cRefs = RTVfsObjRetain(hPrevVfsObj);
1061 AssertStmt(cRefs != UINT32_MAX, rc = VERR_VFS_CHAIN_IPE);
1062 *phVfsObj = hPrevVfsObj;
1063 *ppszFinalPath = cElements == pSpec->cElements ? NULL : pSpec->paElements[cElements].paArgs[0].psz;
1064 }
1065 }
1066
1067 int rc2 = RTCritSectRwLeaveShared(&g_rtVfsChainElementCritSect);
1068 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
1069 rc = rc2;
1070 }
1071 return rc;
1072}
1073
1074
1075RTDECL(int) RTVfsChainElementDeregisterProvider(PRTVFSCHAINELEMENTREG pRegRec, bool fFromDtor)
1076{
1077 /*
1078 * Fend off wildlife.
1079 */
1080 if (pRegRec == NULL)
1081 return VINF_SUCCESS;
1082 AssertPtrReturn(pRegRec, VERR_INVALID_POINTER);
1083 AssertMsgReturn(pRegRec->uVersion == RTVFSCHAINELEMENTREG_VERSION, ("%#x", pRegRec->uVersion), VERR_INVALID_POINTER);
1084 AssertMsgReturn(pRegRec->uEndMarker == RTVFSCHAINELEMENTREG_VERSION, ("%#zx", pRegRec->uEndMarker), VERR_INVALID_POINTER);
1085 AssertPtrReturn(pRegRec->pszName, VERR_INVALID_POINTER);
1086
1087 /*
1088 * Take the lock if that's safe.
1089 */
1090 if (!fFromDtor)
1091 RTCritSectRwEnterExcl(&g_rtVfsChainElementCritSect);
1092 else if (!g_rtVfsChainElementProviderList.pNext)
1093 RTListInit(&g_rtVfsChainElementProviderList);
1094
1095 /*
1096 * Ok, remove it.
1097 */
1098 int rc = VERR_NOT_FOUND;
1099 PRTVFSCHAINELEMENTREG pIterator, pIterNext;
1100 RTListForEachSafe(&g_rtVfsChainElementProviderList, pIterator, pIterNext, RTVFSCHAINELEMENTREG, ListEntry)
1101 {
1102 if (pIterator == pRegRec)
1103 {
1104 RTListNodeRemove(&pRegRec->ListEntry);
1105 rc = VINF_SUCCESS;
1106 break;
1107 }
1108 }
1109
1110 /*
1111 * Leave the lock and return.
1112 */
1113 if (!fFromDtor)
1114 RTCritSectRwLeaveExcl(&g_rtVfsChainElementCritSect);
1115 return rc;
1116}
1117
1118
1119RTDECL(int) RTVfsChainOpenObj(const char *pszSpec, uint64_t fFileOpen, uint32_t fObjFlags,
1120 PRTVFSOBJ phVfsObj, uint32_t *poffError, PRTERRINFO pErrInfo)
1121{
1122 /*
1123 * Validate input.
1124 */
1125 uint32_t offErrorIgn;
1126 if (!poffError)
1127 poffError = &offErrorIgn;
1128 *poffError = 0;
1129 AssertPtrReturn(pszSpec, VERR_INVALID_POINTER);
1130 AssertReturn(*pszSpec != '\0', VERR_INVALID_PARAMETER);
1131 AssertPtrReturn(phVfsObj, VERR_INVALID_POINTER);
1132 AssertPtrNullReturn(pErrInfo, VERR_INVALID_POINTER);
1133
1134 int rc = rtFileRecalcAndValidateFlags(&fFileOpen);
1135 if (RT_FAILURE(rc))
1136 return rc;
1137 AssertMsgReturn( RTPATH_F_IS_VALID(fObjFlags, RTVFSOBJ_F_VALID_MASK)
1138 && (fObjFlags & RTVFSOBJ_F_CREATE_MASK) <= RTVFSOBJ_F_CREATE_DIRECTORY,
1139 ("fObjFlags=%#x\n", fObjFlags),
1140 VERR_INVALID_FLAGS);
1141
1142 /*
1143 * Try for a VFS chain first, falling back on regular file system stuff if it's just a path.
1144 */
1145 PRTVFSCHAINSPEC pSpec = NULL;
1146 if (strncmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX, sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1) == 0)
1147 {
1148 rc = RTVfsChainSpecParse(pszSpec, 0 /*fFlags*/, RTVFSOBJTYPE_DIR, &pSpec, poffError);
1149 if (RT_FAILURE(rc))
1150 return rc;
1151
1152 Assert(pSpec->cElements > 0);
1153 if ( pSpec->cElements > 1
1154 || pSpec->paElements[0].enmType != RTVFSOBJTYPE_END)
1155 {
1156 const char *pszFinal = NULL;
1157 RTVFSOBJ hVfsObj = NIL_RTVFSOBJ;
1158 pSpec->fOpenFile = fFileOpen;
1159 rc = RTVfsChainSpecCheckAndSetup(pSpec, NULL /*pReuseSpec*/, &hVfsObj, &pszFinal, poffError, pErrInfo);
1160 if (RT_SUCCESS(rc))
1161 {
1162 if (!pszFinal)
1163 {
1164 *phVfsObj = hVfsObj;
1165 rc = VINF_SUCCESS;
1166 }
1167 else
1168 {
1169 /*
1170 * Do a file open with the final path on the returned object.
1171 */
1172 RTVFS hVfs = RTVfsObjToVfs(hVfsObj);
1173 RTVFSDIR hVfsDir = RTVfsObjToDir(hVfsObj);
1174 RTVFSFSSTREAM hVfsFss = RTVfsObjToFsStream(hVfsObj);
1175 if (hVfs != NIL_RTVFS)
1176 rc = RTVfsObjOpen(hVfs, pszFinal, fFileOpen, fObjFlags, phVfsObj);
1177 else if (hVfsDir != NIL_RTVFSDIR)
1178 rc = RTVfsDirOpenObj(hVfsDir, pszFinal, fFileOpen, fObjFlags, phVfsObj);
1179 else if (hVfsFss != NIL_RTVFSFSSTREAM)
1180 rc = VERR_NOT_IMPLEMENTED;
1181 else
1182 rc = VERR_VFS_CHAIN_TYPE_MISMATCH_PATH_ONLY;
1183 RTVfsRelease(hVfs);
1184 RTVfsDirRelease(hVfsDir);
1185 RTVfsFsStrmRelease(hVfsFss);
1186 RTVfsObjRelease(hVfsObj);
1187 }
1188 }
1189
1190 RTVfsChainSpecFree(pSpec);
1191 return rc;
1192 }
1193
1194 /* Only a path element. */
1195 pszSpec = pSpec->paElements[0].paArgs[0].psz;
1196 }
1197
1198 /*
1199 * Path to regular file system.
1200 * Go via the directory VFS wrapper to avoid duplicating code.
1201 */
1202 RTVFSDIR hVfsParentDir = NIL_RTVFSDIR;
1203 const char *pszFilename;
1204 if (RTPathHasPath(pszSpec))
1205 {
1206 char *pszCopy = RTStrDup(pszSpec);
1207 if (pszCopy)
1208 {
1209 RTPathStripFilename(pszCopy);
1210 rc = RTVfsDirOpenNormal(pszCopy, 0 /*fOpen*/, &hVfsParentDir);
1211 RTStrFree(pszCopy);
1212 }
1213 else
1214 rc = VERR_NO_STR_MEMORY;
1215 pszFilename = RTPathFilename(pszSpec);
1216 }
1217 else
1218 {
1219 pszFilename = pszSpec;
1220 rc = RTVfsDirOpenNormal(".", 0 /*fOpen*/, &hVfsParentDir);
1221 }
1222 if (RT_SUCCESS(rc))
1223 {
1224 rc = RTVfsDirOpenObj(hVfsParentDir, pszFilename, fFileOpen, fObjFlags, phVfsObj);
1225 RTVfsDirRelease(hVfsParentDir);
1226 }
1227
1228 RTVfsChainSpecFree(pSpec);
1229 return rc;
1230}
1231
1232
1233RTDECL(int) RTVfsChainOpenDir(const char *pszSpec, uint32_t fOpen,
1234 PRTVFSDIR phVfsDir, uint32_t *poffError, PRTERRINFO pErrInfo)
1235{
1236 uint32_t offErrorIgn;
1237 if (!poffError)
1238 poffError = &offErrorIgn;
1239 *poffError = 0;
1240 AssertPtrReturn(pszSpec, VERR_INVALID_POINTER);
1241 AssertReturn(*pszSpec != '\0', VERR_INVALID_PARAMETER);
1242 AssertPtrReturn(phVfsDir, VERR_INVALID_POINTER);
1243 AssertPtrNullReturn(pErrInfo, VERR_INVALID_POINTER);
1244
1245 /*
1246 * Try for a VFS chain first, falling back on regular file system stuff if it's just a path.
1247 */
1248 int rc;
1249 PRTVFSCHAINSPEC pSpec = NULL;
1250 if (strncmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX, sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1) == 0)
1251 {
1252 rc = RTVfsChainSpecParse(pszSpec, 0 /*fFlags*/, RTVFSOBJTYPE_DIR, &pSpec, poffError);
1253 if (RT_FAILURE(rc))
1254 return rc;
1255
1256 Assert(pSpec->cElements > 0);
1257 if ( pSpec->cElements > 1
1258 || pSpec->paElements[0].enmType != RTVFSOBJTYPE_END)
1259 {
1260 const char *pszFinal = NULL;
1261 RTVFSOBJ hVfsObj = NIL_RTVFSOBJ;
1262 pSpec->fOpenFile = RTFILE_O_READ;
1263 rc = RTVfsChainSpecCheckAndSetup(pSpec, NULL /*pReuseSpec*/, &hVfsObj, &pszFinal, poffError, pErrInfo);
1264 if (RT_SUCCESS(rc))
1265 {
1266 if (!pszFinal)
1267 {
1268 /* Try convert it to a directory object and we're done. */
1269 *phVfsDir = RTVfsObjToDir(hVfsObj);
1270 if (*phVfsDir)
1271 rc = VINF_SUCCESS;
1272 else
1273 rc = VERR_VFS_CHAIN_CAST_FAILED;
1274 }
1275 else
1276 {
1277 /*
1278 * Do a file open with the final path on the returned object.
1279 */
1280 RTVFS hVfs = RTVfsObjToVfs(hVfsObj);
1281 RTVFSDIR hVfsDir = RTVfsObjToDir(hVfsObj);
1282 RTVFSFSSTREAM hVfsFss = RTVfsObjToFsStream(hVfsObj);
1283 if (hVfs != NIL_RTVFS)
1284 rc = RTVfsDirOpen(hVfs, pszFinal, fOpen, phVfsDir);
1285 else if (hVfsDir != NIL_RTVFSDIR)
1286 rc = RTVfsDirOpenDir(hVfsDir, pszFinal, fOpen, phVfsDir);
1287 else if (hVfsFss != NIL_RTVFSFSSTREAM)
1288 rc = VERR_NOT_IMPLEMENTED;
1289 else
1290 rc = VERR_VFS_CHAIN_TYPE_MISMATCH_PATH_ONLY;
1291 RTVfsRelease(hVfs);
1292 RTVfsDirRelease(hVfsDir);
1293 RTVfsFsStrmRelease(hVfsFss);
1294 }
1295 RTVfsObjRelease(hVfsObj);
1296 }
1297
1298 RTVfsChainSpecFree(pSpec);
1299 return rc;
1300 }
1301
1302 /* Only a path element. */
1303 pszSpec = pSpec->paElements[0].paArgs[0].psz;
1304 }
1305
1306 /*
1307 * Path to regular file system.
1308 */
1309 rc = RTVfsDirOpenNormal(pszSpec, fOpen, phVfsDir);
1310
1311 RTVfsChainSpecFree(pSpec);
1312 return rc;
1313}
1314
1315
1316RTDECL(int) RTVfsChainOpenParentDir(const char *pszSpec, uint32_t fOpen, PRTVFSDIR phVfsDir, const char **ppszChild,
1317 uint32_t *poffError, PRTERRINFO pErrInfo)
1318{
1319 uint32_t offErrorIgn;
1320 if (!poffError)
1321 poffError = &offErrorIgn;
1322 *poffError = 0;
1323 AssertPtrReturn(pszSpec, VERR_INVALID_POINTER);
1324 AssertReturn(*pszSpec != '\0', VERR_INVALID_PARAMETER);
1325 AssertPtrReturn(phVfsDir, VERR_INVALID_POINTER);
1326 AssertPtrReturn(ppszChild, VERR_INVALID_POINTER);
1327 *ppszChild = NULL;
1328 AssertPtrNullReturn(pErrInfo, VERR_INVALID_POINTER);
1329
1330 /*
1331 * Process the spec from the end, trying to find the child part of it.
1332 * We cannot use RTPathFilename here because we must ignore trailing slashes.
1333 */
1334 const char * const pszEnd = RTStrEnd(pszSpec, RTSTR_MAX);
1335 const char *pszChild = pszEnd;
1336 while ( pszChild != pszSpec
1337 && RTPATH_IS_SLASH(pszChild[-1]))
1338 pszChild--;
1339 while ( pszChild != pszSpec
1340 && !RTPATH_IS_SLASH(pszChild[-1])
1341 && !RTPATH_IS_VOLSEP(pszChild[-1]))
1342 pszChild--;
1343 size_t const cchChild = pszEnd - pszChild;
1344 *ppszChild = pszChild;
1345
1346 /*
1347 * Try for a VFS chain first, falling back on regular file system stuff if it's just a path.
1348 */
1349 int rc;
1350 PRTVFSCHAINSPEC pSpec = NULL;
1351 if (strncmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX, sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1) == 0)
1352 {
1353 rc = RTVfsChainSpecParse(pszSpec, 0 /*fFlags*/, RTVFSOBJTYPE_DIR, &pSpec, poffError);
1354 if (RT_FAILURE(rc))
1355 return rc;
1356
1357 Assert(pSpec->cElements > 0);
1358 if ( pSpec->cElements > 1
1359 || pSpec->paElements[0].enmType != RTVFSOBJTYPE_END)
1360 {
1361 /*
1362 * Check that it ends with a path-only element and that this in turn ends with
1363 * what pszChild points to. (We cannot easiy figure out the parent part of
1364 * an element that isn't path-only, so we don't bother trying try.)
1365 */
1366 PRTVFSCHAINELEMSPEC pLast = &pSpec->paElements[pSpec->cElements - 1];
1367 if (pLast->pszProvider == NULL)
1368 {
1369 size_t cchFinal = strlen(pLast->paArgs[0].psz);
1370 if ( cchFinal >= cchChild
1371 && memcmp(&pLast->paArgs[0].psz[cchFinal - cchChild], pszChild, cchChild + 1) == 0)
1372 {
1373 /*
1374 * Drop the child part so we have a path to the parent, then setup the chain.
1375 */
1376 if (cchFinal > cchChild)
1377 pLast->paArgs[0].psz[cchFinal - cchChild] = '\0';
1378 else
1379 pSpec->cElements--;
1380
1381 const char *pszFinal = NULL;
1382 RTVFSOBJ hVfsObj = NIL_RTVFSOBJ;
1383 pSpec->fOpenFile = fOpen;
1384 rc = RTVfsChainSpecCheckAndSetup(pSpec, NULL /*pReuseSpec*/, &hVfsObj, &pszFinal, poffError, pErrInfo);
1385 if (RT_SUCCESS(rc))
1386 {
1387 if (!pszFinal)
1388 {
1389 Assert(cchFinal == cchChild);
1390
1391 /* Try convert it to a file object and we're done. */
1392 *phVfsDir = RTVfsObjToDir(hVfsObj);
1393 if (*phVfsDir)
1394 rc = VINF_SUCCESS;
1395 else
1396 rc = VERR_VFS_CHAIN_CAST_FAILED;
1397 }
1398 else
1399 {
1400 /*
1401 * Do a file open with the final path on the returned object.
1402 */
1403 RTVFS hVfs = RTVfsObjToVfs(hVfsObj);
1404 RTVFSDIR hVfsDir = RTVfsObjToDir(hVfsObj);
1405 RTVFSFSSTREAM hVfsFss = RTVfsObjToFsStream(hVfsObj);
1406 if (hVfs != NIL_RTVFS)
1407 rc = RTVfsDirOpen(hVfs, pszFinal, fOpen, phVfsDir);
1408 else if (hVfsDir != NIL_RTVFSDIR)
1409 rc = RTVfsDirOpenDir(hVfsDir, pszFinal, fOpen, phVfsDir);
1410 else if (hVfsFss != NIL_RTVFSFSSTREAM)
1411 rc = VERR_NOT_IMPLEMENTED;
1412 else
1413 rc = VERR_VFS_CHAIN_TYPE_MISMATCH_PATH_ONLY;
1414 RTVfsRelease(hVfs);
1415 RTVfsDirRelease(hVfsDir);
1416 RTVfsFsStrmRelease(hVfsFss);
1417 }
1418 RTVfsObjRelease(hVfsObj);
1419 }
1420 }
1421 else
1422 rc = VERR_VFS_CHAIN_TOO_SHORT_FOR_PARENT;
1423 }
1424 else
1425 rc = VERR_VFS_CHAIN_NOT_PATH_ONLY;
1426
1427 RTVfsChainSpecFree(pSpec);
1428 return rc;
1429 }
1430
1431 /* Only a path element. */
1432 pszSpec = pSpec->paElements[0].paArgs[0].psz;
1433 }
1434
1435 /*
1436 * Path to regular file system.
1437 */
1438 if (RTPathHasPath(pszSpec))
1439 {
1440 char *pszCopy = RTStrDup(pszSpec);
1441 if (pszCopy)
1442 {
1443 RTPathStripFilename(pszCopy);
1444 rc = RTVfsDirOpenNormal(pszCopy, fOpen, phVfsDir);
1445 RTStrFree(pszCopy);
1446 }
1447 else
1448 rc = VERR_NO_STR_MEMORY;
1449 }
1450 else
1451 rc = RTVfsDirOpenNormal(".", fOpen, phVfsDir);
1452
1453 RTVfsChainSpecFree(pSpec);
1454 return rc;
1455
1456}
1457
1458
1459RTDECL(int) RTVfsChainOpenFile(const char *pszSpec, uint64_t fOpen,
1460 PRTVFSFILE phVfsFile, uint32_t *poffError, PRTERRINFO pErrInfo)
1461{
1462 uint32_t offErrorIgn;
1463 if (!poffError)
1464 poffError = &offErrorIgn;
1465 *poffError = 0;
1466 AssertPtrReturn(pszSpec, VERR_INVALID_POINTER);
1467 AssertReturn(*pszSpec != '\0', VERR_INVALID_PARAMETER);
1468 AssertPtrReturn(phVfsFile, VERR_INVALID_POINTER);
1469 AssertPtrNullReturn(pErrInfo, VERR_INVALID_POINTER);
1470
1471 /*
1472 * Try for a VFS chain first, falling back on regular file system stuff if it's just a path.
1473 */
1474 int rc;
1475 PRTVFSCHAINSPEC pSpec = NULL;
1476 if (strncmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX, sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1) == 0)
1477 {
1478 rc = RTVfsChainSpecParse(pszSpec, 0 /*fFlags*/, RTVFSOBJTYPE_FILE, &pSpec, poffError);
1479 if (RT_FAILURE(rc))
1480 return rc;
1481
1482 Assert(pSpec->cElements > 0);
1483 if ( pSpec->cElements > 1
1484 || pSpec->paElements[0].enmType != RTVFSOBJTYPE_END)
1485 {
1486 const char *pszFinal = NULL;
1487 RTVFSOBJ hVfsObj = NIL_RTVFSOBJ;
1488 pSpec->fOpenFile = fOpen;
1489 rc = RTVfsChainSpecCheckAndSetup(pSpec, NULL /*pReuseSpec*/, &hVfsObj, &pszFinal, poffError, pErrInfo);
1490 if (RT_SUCCESS(rc))
1491 {
1492 if (!pszFinal)
1493 {
1494 /* Try convert it to a file object and we're done. */
1495 *phVfsFile = RTVfsObjToFile(hVfsObj);
1496 if (*phVfsFile)
1497 rc = VINF_SUCCESS;
1498 else
1499 rc = VERR_VFS_CHAIN_CAST_FAILED;
1500 }
1501 else
1502 {
1503 /*
1504 * Do a file open with the final path on the returned object.
1505 */
1506 RTVFS hVfs = RTVfsObjToVfs(hVfsObj);
1507 RTVFSDIR hVfsDir = RTVfsObjToDir(hVfsObj);
1508 RTVFSFSSTREAM hVfsFss = RTVfsObjToFsStream(hVfsObj);
1509 if (hVfs != NIL_RTVFS)
1510 rc = RTVfsFileOpen(hVfs, pszFinal, fOpen, phVfsFile);
1511 else if (hVfsDir != NIL_RTVFSDIR)
1512 rc = RTVfsDirOpenFile(hVfsDir, pszFinal, fOpen, phVfsFile);
1513 else if (hVfsFss != NIL_RTVFSFSSTREAM)
1514 rc = VERR_NOT_IMPLEMENTED;
1515 else
1516 rc = VERR_VFS_CHAIN_TYPE_MISMATCH_PATH_ONLY;
1517 RTVfsRelease(hVfs);
1518 RTVfsDirRelease(hVfsDir);
1519 RTVfsFsStrmRelease(hVfsFss);
1520 }
1521 RTVfsObjRelease(hVfsObj);
1522 }
1523
1524 RTVfsChainSpecFree(pSpec);
1525 return rc;
1526 }
1527
1528 /* Only a path element. */
1529 pszSpec = pSpec->paElements[0].paArgs[0].psz;
1530 }
1531
1532 /*
1533 * Path to regular file system.
1534 */
1535 RTFILE hFile;
1536 rc = RTFileOpen(&hFile, pszSpec, fOpen);
1537 if (RT_SUCCESS(rc))
1538 {
1539 RTVFSFILE hVfsFile;
1540 rc = RTVfsFileFromRTFile(hFile, fOpen, false /*fLeaveOpen*/, &hVfsFile);
1541 if (RT_SUCCESS(rc))
1542 *phVfsFile = hVfsFile;
1543 else
1544 RTFileClose(hFile);
1545 }
1546
1547 RTVfsChainSpecFree(pSpec);
1548 return rc;
1549}
1550
1551
1552RTDECL(int) RTVfsChainOpenIoStream(const char *pszSpec, uint64_t fOpen,
1553 PRTVFSIOSTREAM phVfsIos, uint32_t *poffError, PRTERRINFO pErrInfo)
1554{
1555 uint32_t offErrorIgn;
1556 if (!poffError)
1557 poffError = &offErrorIgn;
1558 *poffError = 0;
1559 AssertPtrReturn(pszSpec, VERR_INVALID_POINTER);
1560 AssertReturn(*pszSpec != '\0', VERR_INVALID_PARAMETER);
1561 AssertPtrReturn(phVfsIos, VERR_INVALID_POINTER);
1562 AssertPtrNullReturn(pErrInfo, VERR_INVALID_POINTER);
1563
1564 /*
1565 * Try for a VFS chain first, falling back on regular file system stuff if it's just a path.
1566 */
1567 int rc;
1568 PRTVFSCHAINSPEC pSpec = NULL;
1569 if (strncmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX, sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1) == 0)
1570 {
1571 rc = RTVfsChainSpecParse(pszSpec, 0 /*fFlags*/, RTVFSOBJTYPE_IO_STREAM, &pSpec, poffError);
1572 if (RT_FAILURE(rc))
1573 return rc;
1574
1575 Assert(pSpec->cElements > 0);
1576 if ( pSpec->cElements > 1
1577 || pSpec->paElements[0].enmType != RTVFSOBJTYPE_END)
1578 {
1579 const char *pszFinal = NULL;
1580 RTVFSOBJ hVfsObj = NIL_RTVFSOBJ;
1581 pSpec->fOpenFile = fOpen;
1582 rc = RTVfsChainSpecCheckAndSetup(pSpec, NULL /*pReuseSpec*/, &hVfsObj, &pszFinal, poffError, pErrInfo);
1583 if (RT_SUCCESS(rc))
1584 {
1585 if (!pszFinal)
1586 {
1587 /* Try convert it to an I/O object and we're done. */
1588 *phVfsIos = RTVfsObjToIoStream(hVfsObj);
1589 if (*phVfsIos)
1590 rc = VINF_SUCCESS;
1591 else
1592 rc = VERR_VFS_CHAIN_CAST_FAILED;
1593 }
1594 else
1595 {
1596 /*
1597 * Do a file open with the final path on the returned object.
1598 */
1599 RTVFS hVfs = RTVfsObjToVfs(hVfsObj);
1600 RTVFSDIR hVfsDir = RTVfsObjToDir(hVfsObj);
1601 RTVFSFSSTREAM hVfsFss = RTVfsObjToFsStream(hVfsObj);
1602 RTVFSFILE hVfsFile = NIL_RTVFSFILE;
1603 if (hVfs != NIL_RTVFS)
1604 rc = RTVfsFileOpen(hVfs, pszFinal, fOpen, &hVfsFile);
1605 else if (hVfsDir != NIL_RTVFSDIR)
1606 rc = RTVfsDirOpenFile(hVfsDir, pszFinal, fOpen, &hVfsFile);
1607 else if (hVfsFss != NIL_RTVFSFSSTREAM)
1608 rc = VERR_NOT_IMPLEMENTED;
1609 else
1610 rc = VERR_VFS_CHAIN_TYPE_MISMATCH_PATH_ONLY;
1611 if (RT_SUCCESS(rc))
1612 {
1613 *phVfsIos = RTVfsFileToIoStream(hVfsFile);
1614 if (*phVfsIos)
1615 rc = VINF_SUCCESS;
1616 else
1617 rc = VERR_VFS_CHAIN_CAST_FAILED;
1618 RTVfsFileRelease(hVfsFile);
1619 }
1620 RTVfsRelease(hVfs);
1621 RTVfsDirRelease(hVfsDir);
1622 RTVfsFsStrmRelease(hVfsFss);
1623 }
1624 RTVfsObjRelease(hVfsObj);
1625 }
1626
1627 RTVfsChainSpecFree(pSpec);
1628 return rc;
1629 }
1630
1631 /* Only a path element. */
1632 pszSpec = pSpec->paElements[0].paArgs[0].psz;
1633 }
1634
1635 /*
1636 * Path to regular file system.
1637 */
1638 RTFILE hFile;
1639 rc = RTFileOpen(&hFile, pszSpec, fOpen);
1640 if (RT_SUCCESS(rc))
1641 {
1642 RTVFSFILE hVfsFile;
1643 rc = RTVfsFileFromRTFile(hFile, fOpen, false /*fLeaveOpen*/, &hVfsFile);
1644 if (RT_SUCCESS(rc))
1645 {
1646 *phVfsIos = RTVfsFileToIoStream(hVfsFile);
1647 RTVfsFileRelease(hVfsFile);
1648 }
1649 else
1650 RTFileClose(hFile);
1651 }
1652
1653 RTVfsChainSpecFree(pSpec);
1654 return rc;
1655}
1656
1657
1658/**
1659 * The equivalent of RTPathQueryInfoEx
1660 */
1661RTDECL(int) RTVfsChainQueryInfo(const char *pszSpec, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs,
1662 uint32_t fFlags, uint32_t *poffError, PRTERRINFO pErrInfo)
1663{
1664 uint32_t offErrorIgn;
1665 if (!poffError)
1666 poffError = &offErrorIgn;
1667 *poffError = 0;
1668 AssertPtrReturn(pszSpec, VERR_INVALID_POINTER);
1669 AssertReturn(*pszSpec != '\0', VERR_INVALID_PARAMETER);
1670 AssertPtrReturn(pObjInfo, VERR_INVALID_POINTER);
1671 AssertReturn(enmAdditionalAttribs >= RTFSOBJATTRADD_NOTHING && enmAdditionalAttribs <= RTFSOBJATTRADD_LAST,
1672 VERR_INVALID_PARAMETER);
1673 AssertPtrNullReturn(pErrInfo, VERR_INVALID_POINTER);
1674
1675 /*
1676 * Try for a VFS chain first, falling back on regular file system stuff if it's just a path.
1677 */
1678 int rc;
1679 PRTVFSCHAINSPEC pSpec = NULL;
1680 if (strncmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX, sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1) == 0)
1681 {
1682 rc = RTVfsChainSpecParse(pszSpec, 0 /*fFlags*/, RTVFSOBJTYPE_BASE, &pSpec, poffError);
1683 if (RT_FAILURE(rc))
1684 return rc;
1685
1686 Assert(pSpec->cElements > 0);
1687 if ( pSpec->cElements > 1
1688 || pSpec->paElements[0].enmType != RTVFSOBJTYPE_END)
1689 {
1690 const char *pszFinal = NULL;
1691 RTVFSOBJ hVfsObj = NIL_RTVFSOBJ;
1692 pSpec->fOpenFile = RTFILE_O_READ | RTFILE_O_OPEN;
1693 rc = RTVfsChainSpecCheckAndSetup(pSpec, NULL /*pReuseSpec*/, &hVfsObj, &pszFinal, poffError, pErrInfo);
1694 if (RT_SUCCESS(rc))
1695 {
1696 if (!pszFinal)
1697 {
1698 /*
1699 * Do the job on the final object.
1700 */
1701 rc = RTVfsObjQueryInfo(hVfsObj, pObjInfo, enmAdditionalAttribs);
1702 }
1703 else
1704 {
1705 /*
1706 * Do a path query operation on the penultimate object.
1707 */
1708 RTVFS hVfs = RTVfsObjToVfs(hVfsObj);
1709 RTVFSDIR hVfsDir = RTVfsObjToDir(hVfsObj);
1710 RTVFSFSSTREAM hVfsFss = RTVfsObjToFsStream(hVfsObj);
1711 if (hVfs != NIL_RTVFS)
1712 rc = RTVfsQueryPathInfo(hVfs, pszFinal, pObjInfo, enmAdditionalAttribs, fFlags);
1713 else if (hVfsDir != NIL_RTVFSDIR)
1714 rc = RTVfsDirQueryPathInfo(hVfsDir, pszFinal, pObjInfo, enmAdditionalAttribs, fFlags);
1715 else if (hVfsFss != NIL_RTVFSFSSTREAM)
1716 rc = VERR_NOT_SUPPORTED;
1717 else
1718 rc = VERR_VFS_CHAIN_TYPE_MISMATCH_PATH_ONLY;
1719 RTVfsRelease(hVfs);
1720 RTVfsDirRelease(hVfsDir);
1721 RTVfsFsStrmRelease(hVfsFss);
1722 }
1723 RTVfsObjRelease(hVfsObj);
1724 }
1725
1726 RTVfsChainSpecFree(pSpec);
1727 return rc;
1728 }
1729
1730 /* Only a path element. */
1731 pszSpec = pSpec->paElements[0].paArgs[0].psz;
1732 }
1733
1734 /*
1735 * Path to regular file system.
1736 */
1737 rc = RTPathQueryInfoEx(pszSpec, pObjInfo, enmAdditionalAttribs, fFlags);
1738
1739 RTVfsChainSpecFree(pSpec);
1740 return rc;
1741}
1742
1743
1744RTDECL(bool) RTVfsChainIsSpec(const char *pszSpec)
1745{
1746 return pszSpec
1747 && strncmp(pszSpec, RT_STR_TUPLE(RTVFSCHAIN_SPEC_PREFIX)) == 0;
1748}
1749
1750
1751RTDECL(int) RTVfsChainQueryFinalPath(const char *pszSpec, char **ppszFinalPath, uint32_t *poffError)
1752{
1753 /* Make sure we've got an error info variable. */
1754 uint32_t offErrorIgn;
1755 if (!poffError)
1756 poffError = &offErrorIgn;
1757 *poffError = 0;
1758
1759 /*
1760 * If not chain specifier, just duplicate the input and return.
1761 */
1762 if (strncmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX, sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1) != 0)
1763 return RTStrDupEx(ppszFinalPath, pszSpec);
1764
1765 /*
1766 * Parse it and check out the last element.
1767 */
1768 PRTVFSCHAINSPEC pSpec = NULL;
1769 int rc = RTVfsChainSpecParse(pszSpec, 0 /*fFlags*/, RTVFSOBJTYPE_BASE, &pSpec, poffError);
1770 if (RT_SUCCESS(rc))
1771 {
1772 PCRTVFSCHAINELEMSPEC pLast = &pSpec->paElements[pSpec->cElements - 1];
1773 if (pLast->pszProvider == NULL)
1774 rc = RTStrDupEx(ppszFinalPath, pLast->paArgs[0].psz);
1775 else
1776 {
1777 rc = VERR_VFS_CHAIN_NOT_PATH_ONLY;
1778 *poffError = pLast->offSpec;
1779 }
1780 RTVfsChainSpecFree(pSpec);
1781 }
1782 return rc;
1783}
1784
1785
1786RTDECL(int) RTVfsChainSplitOffFinalPath(char *pszSpec, char **ppszSpec, char **ppszFinalPath, uint32_t *poffError)
1787{
1788 /* Make sure we've got an error info variable. */
1789 uint32_t offErrorIgn;
1790 if (!poffError)
1791 poffError = &offErrorIgn;
1792 *poffError = 0;
1793
1794 /*
1795 * If not chain specifier, just duplicate the input and return.
1796 */
1797 if (strncmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX, sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1) != 0)
1798 {
1799 *ppszSpec = NULL;
1800 *ppszFinalPath = pszSpec;
1801 return VINF_SUCCESS;
1802 }
1803
1804 /*
1805 * Parse it and check out the last element.
1806 */
1807 PRTVFSCHAINSPEC pSpec = NULL;
1808 int rc = RTVfsChainSpecParse(pszSpec, 0 /*fFlags*/, RTVFSOBJTYPE_BASE, &pSpec, poffError);
1809 if (RT_SUCCESS(rc))
1810 {
1811 Assert(pSpec->cElements > 0);
1812 PCRTVFSCHAINELEMSPEC pLast = &pSpec->paElements[pSpec->cElements - 1];
1813 if (pLast->pszProvider == NULL)
1814 {
1815 char *psz = &pszSpec[pLast->offSpec];
1816 *ppszFinalPath = psz;
1817 if (pSpec->cElements > 1)
1818 {
1819 *ppszSpec = pszSpec;
1820
1821 /* Remove the separator and any whitespace around it. */
1822 while ( psz != pszSpec
1823 && RT_C_IS_SPACE(psz[-1]))
1824 psz--;
1825 if ( psz != pszSpec
1826 && ( psz[-1] == ':'
1827 || psz[-1] == '|'))
1828 psz--;
1829 while ( psz != pszSpec
1830 && RT_C_IS_SPACE(psz[-1]))
1831 psz--;
1832 *psz = '\0';
1833 }
1834 else
1835 *ppszSpec = NULL;
1836 }
1837 else
1838 {
1839 *ppszFinalPath = NULL;
1840 *ppszSpec = pszSpec;
1841 }
1842 RTVfsChainSpecFree(pSpec);
1843 }
1844 else
1845 {
1846 *ppszSpec = NULL;
1847 *ppszFinalPath = NULL;
1848 }
1849 return rc;
1850}
1851
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