Index: /trunk/include/iprt/err.h
===================================================================
--- /trunk/include/iprt/err.h	(revision 66600)
+++ /trunk/include/iprt/err.h	(revision 66601)
@@ -1928,10 +1928,29 @@
 /** VFS chain element only provides file system (vfs) objects. */
 #define VERR_VFS_CHAIN_ONLY_VFS                     (-22143)
+/** VFS chain element only provides file, I/O stream (ios), or
+ * directory (dir) objects. */
+#define VERR_VFS_CHAIN_ONLY_FILE_OR_IOS_OR_DIR      (-22144)
+/** VFS chain element takes a file object as input. */
+#define VERR_VFS_CHAIN_TAKES_FILE                   (-22145)
+/** VFS chain element takes a file or I/O stream (ios) object as input. */
+#define VERR_VFS_CHAIN_TAKES_FILE_OR_IOS            (-22146)
+/** VFS chain element takes a directory (dir) object as input. */
+#define VERR_VFS_CHAIN_TAKES_DIR                    (-22147)
+/** VFS chain element takes a file system stream (fss) object as input. */
+#define VERR_VFS_CHAIN_TAKES_FSS                    (-22148)
+/** VFS chain element takes a file system (vfs) object as input. */
+#define VERR_VFS_CHAIN_TAKES_VFS                    (-22149)
+/** VFS chain element takes a directory (dir) or file system (vfs)
+ * object as input. */
+#define VERR_VFS_CHAIN_TAKES_DIR_OR_VFS             (-22150)
+/** VFS chain element takes a directory (dir), file system stream (fss),
+ * or file system (vfs) object as input. */
+#define VERR_VFS_CHAIN_TAKES_DIR_OR_FSS_OR_VFS      (-22151)
 /** VFS chain element only provides a read-only I/O stream, while the chain
  * requires write access. */
-#define VERR_VFS_CHAIN_READ_ONLY_IOS                (-22144)
+#define VERR_VFS_CHAIN_READ_ONLY_IOS                (-22152)
 /** VFS chain element only provides a read-only I/O stream, while the chain
  * read access. */
-#define VERR_VFS_CHAIN_WRITE_ONLY_IOS               (-22145)
+#define VERR_VFS_CHAIN_WRITE_ONLY_IOS               (-22153)
 /** @} */
 
Index: /trunk/include/iprt/mangling.h
===================================================================
--- /trunk/include/iprt/mangling.h	(revision 66600)
+++ /trunk/include/iprt/mangling.h	(revision 66601)
@@ -2286,6 +2286,10 @@
 # define RTVfsChainSpecFree                             RT_MANGLER(RTVfsChainSpecFree)
 # define RTVfsChainSpecParse                            RT_MANGLER(RTVfsChainSpecParse)
+# define RTVfsChainValidateOpenFileOrIoStream           RT_MANGLER(RTVfsChainValidateOpenFileOrIoStream)
 # define RTVfsDirRelease                                RT_MANGLER(RTVfsDirRelease)
 # define RTVfsDirRetain                                 RT_MANGLER(RTVfsDirRetain)
+# define RTVfsDirOpen                                   RT_MANGLER(RTVfsDirOpen)
+# define RTVfsDirOpenDir                                RT_MANGLER(RTVfsDirOpenDir)
+# define RTVfsDirOpenFile                               RT_MANGLER(RTVfsDirOpenFile)
 # define RTVfsFileFlush                                 RT_MANGLER(RTVfsFileFlush)
 # define RTVfsFileFromBuffer                            RT_MANGLER(RTVfsFileFromBuffer)
@@ -2369,4 +2373,5 @@
 # define RTVfsParsePathFree                             RT_MANGLER(RTVfsParsePathFree)
 # define RTVfsRelease                                   RT_MANGLER(RTVfsRelease)
+# define RTVfsOpenRoot                                  RT_MANGLER(RTVfsOpenRoot)
 # define RTVfsRetain                                    RT_MANGLER(RTVfsRetain)
 # define RTVfsSymlinkQueryInfo                          RT_MANGLER(RTVfsSymlinkQueryInfo)
Index: /trunk/include/iprt/vfs.h
===================================================================
--- /trunk/include/iprt/vfs.h	(revision 66600)
+++ /trunk/include/iprt/vfs.h	(revision 66601)
@@ -330,4 +330,39 @@
 RTDECL(uint32_t)    RTVfsDirRelease(RTVFSDIR hVfsDir);
 
+/**
+ * Opens a directory in the specified file system.
+ *
+ * @returns IPRT status code.
+ * @param   hVfs            The VFS to open the directory within.
+ * @param   pszPath         Path to the directory, relative to the root.
+ * @param   fFlags          Reserved, MBZ.
+ * @param   phVfsDir        Where to return the directory.
+ */
+RTDECL(int) RTVfsDirOpen(RTVFS hVfs, const char *pszPath, uint32_t fFlags, PRTVFSDIR phVfsDir);
+
+/**
+ * Opens a file in or under the given directory.
+ *
+ * @returns IPRT status code.
+ * @param   hVfsDir         The VFS directory start walking the @a pszPath
+ *                          relative to.
+ * @param   pszPath         Path to the file.
+ * @param   fOpen           RTFILE_O_XXX flags.
+ * @param   phVfsFile       Where to return the file.
+ */
+RTDECL(int) RTVfsDirOpenFile(RTVFSDIR hVfsDir, const char *pszPath, uint64_t fOpen, PRTVFSFILE phVfsFile);
+
+/**
+ * Opens a directory in or under the given directory.
+ *
+ * @returns IPRT status code.
+ * @param   hVfsDir         The VFS directory start walking the @a pszPath
+ *                          relative to.
+ * @param   pszPath         Path to the file.
+ * @param   fFlags          Reserved, MBZ.
+ * @param   phVfsDir        Where to return the directory.
+ */
+RTDECL(int) RTVfsDirOpenDir(RTVFSDIR hVfsDir, const char *pszPath, uint32_t fFlags, PRTVFSDIR phVfsDir);
+
 /** @}  */
 
Index: /trunk/include/iprt/vfslowlevel.h
===================================================================
--- /trunk/include/iprt/vfslowlevel.h	(revision 66600)
+++ /trunk/include/iprt/vfslowlevel.h	(revision 66601)
@@ -495,5 +495,5 @@
      * @sa      RTDirOpen.
      */
-    DECLCALLBACKMEMBER(int, pfnOpenDir)(void *pvThis, const char *pszSubDir, PRTVFSDIR phVfsDir);
+    DECLCALLBACKMEMBER(int, pfnOpenDir)(void *pvThis, const char *pszSubDir, uint32_t fFlags, PRTVFSDIR phVfsDir);
 
     /**
@@ -1016,4 +1016,6 @@
     /** The provider. */
     PCRTVFSCHAINELEMENTREG  pProvider;
+    /** Provider specific value. */
+    uint64_t                uProvider;
     /** The object (with reference). */
     RTVFSOBJ                hVfsObj;
@@ -1031,15 +1033,13 @@
 {
     /** Open directory flags (RTFILE_O_XXX). */
-    uint32_t            fOpenFile;
+    uint32_t                fOpenFile;
     /** To be defined. */
-    uint32_t            fOpenDir;
+    uint32_t                fOpenDir;
     /** The type desired by the caller. */
-    RTVFSOBJTYPE        enmDesiredType;
+    RTVFSOBJTYPE            enmDesiredType;
     /** The number of elements. */
-    uint32_t            cElements;
-    /** Provider specific value. */
-    uint64_t            uProvider;
+    uint32_t                cElements;
     /** The elements. */
-    PRTVFSCHAINELEMSPEC paElements;
+    PRTVFSCHAINELEMSPEC     paElements;
 } RTVFSCHAINSPEC;
 /** Pointer to a parsed VFS chain specification. */
@@ -1238,4 +1238,20 @@
 
 
+/**
+ * Common worker for the 'stdfile' and 'open' providers for implementing
+ * RTVFSCHAINELEMENTREG::pfnValidate.
+ *
+ * Stores the RTFILE_O_XXX flags in pSpec->uProvider.
+ *
+ * @returns IPRT status code.
+ * @param   pSpec           The chain specification.
+ * @param   pElement        The chain element specification to validate.
+ * @param   poffError       Where to return error offset on failure.  This is
+ *                          set to the pElement->offSpec on input, so it only
+ *                          needs to be adjusted if an argument is at fault.
+ */
+RTDECL(int) RTVfsChainValidateOpenFileOrIoStream(PRTVFSCHAINSPEC pSpec, PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError);
+
+
 /** @}  */
 
Index: /trunk/src/VBox/Runtime/common/vfs/vfsbase.cpp
===================================================================
--- /trunk/src/VBox/Runtime/common/vfs/vfsbase.cpp	(revision 66600)
+++ /trunk/src/VBox/Runtime/common/vfs/vfsbase.cpp	(revision 66601)
@@ -1198,4 +1198,5 @@
 
 
+/** @todo Replace RTVfsParsePath with RTPathParse and friends?  */
 RTDECL(int) RTVfsParsePath(PRTVFSPARSEDPATH pPath, const char *pszPath, const char *pszCwd)
 {
@@ -1341,6 +1342,6 @@
  *                          (referenced).
  */
-static int rtVfsTraverseToParent(RTVFSINTERNAL *pThis, PRTVFSPARSEDPATH pPath, bool fFollowSymlink,
-                                 RTVFSDIRINTERNAL **ppVfsParentDir)
+static int rtVfsDirTraverseToParent(RTVFSDIRINTERNAL *pThis, PRTVFSPARSEDPATH pPath, bool fFollowSymlink,
+                                    RTVFSDIRINTERNAL **ppVfsParentDir)
 {
     /*
@@ -1348,5 +1349,5 @@
      */
     AssertPtr(pThis);
-    Assert(pThis->uMagic == RTVFS_MAGIC);
+    Assert(pThis->uMagic == RTVFSDIR_MAGIC);
     Assert(pThis->Base.cRefs > 0);
     AssertPtr(pPath);
@@ -1356,18 +1357,14 @@
 
     /*
-     * Open the root directory.
-     */
-    /** @todo Union mounts, traversal optimization methods, races, ++ */
-    RTVFSDIRINTERNAL *pCurDir;
-    RTVfsLockAcquireRead(pThis->Base.hLock);
-    int rc = pThis->pOps->pfnOpenRoot(pThis->Base.pvThis, &pCurDir);
-    RTVfsLockReleaseRead(pThis->Base.hLock);
-    if (RT_FAILURE(rc))
-        return rc;
-    Assert(pCurDir->uMagic == RTVFSDIR_MAGIC);
+     * Start with the pThis directory.
+     */
+    if (RTVfsDirRetain(pThis) == UINT32_MAX)
+        return VERR_INVALID_HANDLE;
+    RTVFSDIRINTERNAL *pCurDir = pThis;
 
     /*
      * The traversal loop.
      */
+    int      rc         = VINF_SUCCESS;
     unsigned cLinks     = 0;
     uint16_t iComponent = 0;
@@ -1461,14 +1458,13 @@
             if (iRestartComp != iComponent)
             {
-                /* Must restart from the root (optimize this). */
+                /* Must restart from the root. */
                 RTVfsDirRelease(pCurDir);
-                RTVfsLockAcquireRead(pThis->Base.hLock);
-                rc = pThis->pOps->pfnOpenRoot(pThis->Base.pvThis, &pCurDir);
-                RTVfsLockReleaseRead(pThis->Base.hLock);
-                if (RT_FAILURE(rc))
+                if (RTVfsDirRetain(pThis) == UINT32_MAX)
                 {
+                    rc = VERR_INVALID_HANDLE;
                     pCurDir = NULL;
                     break;
                 }
+                pCurDir = pThis;
                 iComponent = 0;
             }
@@ -1481,5 +1477,5 @@
             RTVfsDirRelease(pCurDir);
             RTVfsLockAcquireRead(hVfsMnt->Base.hLock);
-            rc = pThis->pOps->pfnOpenRoot(hVfsMnt->Base.pvThis, &pCurDir);
+            rc = hVfsMnt->pOps->pfnOpenRoot(hVfsMnt->Base.pvThis, &pCurDir);
             RTVfsLockReleaseRead(hVfsMnt->Base.hLock);
             if (RT_FAILURE(rc))
@@ -1500,4 +1496,47 @@
 
 
+/**
+ * Internal worker for various open functions as well as RTVfsTraverseToParent.
+ *
+ * @returns IPRT status code.
+ * @param   pThis           The VFS.
+ * @param   pPath           The parsed path.  This may be changed as symbolic
+ *                          links are processed during the path traversal.
+ * @param   fFollowSymlink  Whether to follow the final component if it is a
+ *                          symbolic link.
+ * @param   ppVfsParentDir  Where to return the parent directory handle
+ *                          (referenced).
+ */
+static int rtVfsTraverseToParent(RTVFSINTERNAL *pThis, PRTVFSPARSEDPATH pPath, bool fFollowSymlink,
+                                 RTVFSDIRINTERNAL **ppVfsParentDir)
+{
+    /*
+     * Assert sanity.
+     */
+    AssertPtr(pThis);
+    Assert(pThis->uMagic == RTVFS_MAGIC);
+    Assert(pThis->Base.cRefs > 0);
+    AssertPtr(pPath);
+    AssertPtr(ppVfsParentDir);
+    *ppVfsParentDir = NULL;
+    AssertReturn(pPath->cComponents > 0, VERR_INTERNAL_ERROR_3);
+
+    /*
+     * Open the root directory and join paths with the directory traversal.
+     */
+    /** @todo Union mounts, traversal optimization methods, races, ++ */
+    RTVFSDIRINTERNAL *pRootDir;
+    RTVfsLockAcquireRead(pThis->Base.hLock);
+    int rc = pThis->pOps->pfnOpenRoot(pThis->Base.pvThis, &pRootDir);
+    RTVfsLockReleaseRead(pThis->Base.hLock);
+    if (RT_SUCCESS(rc))
+    {
+        rc = rtVfsDirTraverseToParent(pRootDir, pPath, fFollowSymlink, ppVfsParentDir);
+        RTVfsDirRelease(pRootDir);
+    }
+    return rc;
+}
+
+
 RTDECL(int) RTVfsUtilDummyPollOne(uint32_t fEvents, RTMSINTERVAL cMillies, bool fIntr, uint32_t *pfRetEvents)
 {
@@ -1625,5 +1664,5 @@
 
 
-RTDECL(uint32_t)    RTVfsRetain(RTVFS hVfs)
+RTDECL(uint32_t) RTVfsRetain(RTVFS hVfs)
 {
     RTVFSINTERNAL *pThis = hVfs;
@@ -1634,5 +1673,5 @@
 
 
-RTDECL(uint32_t)    RTVfsRelease(RTVFS hVfs)
+RTDECL(uint32_t) RTVfsRelease(RTVFS hVfs)
 {
     RTVFSINTERNAL *pThis = hVfs;
@@ -1645,18 +1684,39 @@
 
 
-RTDECL(int)         RTVfsIsRangeInUse(RTVFS hVfs, uint64_t off, size_t cb, bool *pfUsed)
+RTDECL(int) RTVfsOpenRoot(RTVFS hVfs, PRTVFSDIR phDir)
 {
     RTVFSINTERNAL *pThis = hVfs;
     AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
     AssertReturn(pThis->uMagic == RTVFS_MAGIC, VERR_INVALID_HANDLE);
+    AssertPtrReturn(phDir, VERR_INVALID_POINTER);
+    *phDir = NIL_RTVFSDIR;
 
     if (!pThis->pOps->pfnIsRangeInUse)
         return VERR_NOT_SUPPORTED;
-    RTVfsLockAcquireWrite(pThis->Base.hLock);
+    RTVfsLockAcquireRead(pThis->Base.hLock);
+    int rc = pThis->pOps->pfnOpenRoot(pThis->Base.pvThis, phDir);
+    RTVfsLockReleaseRead(pThis->Base.hLock);
+
+    return rc;
+}
+
+
+
+RTDECL(int) RTVfsIsRangeInUse(RTVFS hVfs, uint64_t off, size_t cb, bool *pfUsed)
+{
+    RTVFSINTERNAL *pThis = hVfs;
+    AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+    AssertReturn(pThis->uMagic == RTVFS_MAGIC, VERR_INVALID_HANDLE);
+
+    if (!pThis->pOps->pfnIsRangeInUse)
+        return VERR_NOT_SUPPORTED;
+    RTVfsLockAcquireRead(pThis->Base.hLock);
     int rc = pThis->pOps->pfnIsRangeInUse(pThis->Base.pvThis, off, cb, pfUsed);
-    RTVfsLockReleaseWrite(pThis->Base.hLock);
-
-    return rc;
-}
+    RTVfsLockReleaseRead(pThis->Base.hLock);
+
+    return rc;
+}
+
+
 
 
@@ -1773,5 +1833,5 @@
  */
 
-RTDECL(uint32_t)    RTVfsDirRetain(RTVFSDIR hVfsDir)
+RTDECL(uint32_t) RTVfsDirRetain(RTVFSDIR hVfsDir)
 {
     RTVFSDIRINTERNAL *pThis = hVfsDir;
@@ -1782,5 +1842,5 @@
 
 
-RTDECL(uint32_t)    RTVfsDirRelease(RTVFSDIR hVfsDir)
+RTDECL(uint32_t) RTVfsDirRelease(RTVFSDIR hVfsDir)
 {
     RTVFSDIRINTERNAL *pThis = hVfsDir;
@@ -1792,4 +1852,157 @@
 }
 
+
+RTDECL(int) RTVfsDirOpen(RTVFS hVfs, const char *pszPath, uint32_t fFlags, PRTVFSDIR phVfsDir)
+{
+    /*
+     * Validate input.
+     */
+    RTVFSINTERNAL *pThis = hVfs;
+    AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+    AssertReturn(pThis->uMagic == RTVFS_MAGIC, VERR_INVALID_HANDLE);
+    AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
+    AssertPtrReturn(phVfsDir, VERR_INVALID_POINTER);
+    AssertReturn(!fFlags, VERR_INVALID_FLAGS);
+
+    /*
+     * Parse the path, assume current directory is root since we've got no
+     * caller context here.
+     */
+    PRTVFSPARSEDPATH pPath;
+    int rc = RTVfsParsePathA(pszPath, "/", &pPath);
+    if (RT_SUCCESS(rc))
+    {
+        /*
+         * Tranverse the path, resolving the parent node and any symlinks
+         * in the final element, and ask the directory to open the subdir.
+         */
+        RTVFSDIRINTERNAL *pVfsParentDir;
+        rc = rtVfsTraverseToParent(pThis, pPath, true /*fFollowSymlink*/, &pVfsParentDir);
+        if (RT_SUCCESS(rc))
+        {
+            const char *pszEntryName = &pPath->szPath[pPath->aoffComponents[pPath->cComponents - 1]];
+
+            /** @todo there is a symlink creation race here. */
+            RTVfsLockAcquireWrite(pVfsParentDir->Base.hLock);
+            rc = pVfsParentDir->pOps->pfnOpenDir(pVfsParentDir->Base.pvThis, pszEntryName, fFlags, phVfsDir);
+            RTVfsLockReleaseWrite(pVfsParentDir->Base.hLock);
+
+            RTVfsDirRelease(pVfsParentDir);
+
+            if (RT_SUCCESS(rc))
+            {
+                AssertPtr(*phVfsDir);
+                Assert((*phVfsDir)->uMagic == RTVFSDIR_MAGIC);
+            }
+        }
+        RTVfsParsePathFree(pPath);
+    }
+    return rc;
+}
+
+
+RTDECL(int) RTVfsDirOpenDir(RTVFSDIR hVfsDir, const char *pszPath, uint32_t fFlags, PRTVFSDIR phVfsDir)
+{
+    /*
+     * Validate input.
+     */
+    RTVFSDIRINTERNAL *pThis = hVfsDir;
+    AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+    AssertReturn(pThis->uMagic == RTVFSDIR_MAGIC, VERR_INVALID_HANDLE);
+    AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
+    AssertPtrReturn(phVfsDir, VERR_INVALID_POINTER);
+    AssertReturn(!fFlags, VERR_INVALID_FLAGS);
+
+    /*
+     * Parse the path, it's always relative to the given directory.
+     */
+    PRTVFSPARSEDPATH pPath;
+    int rc = RTVfsParsePathA(pszPath, "/", &pPath);
+    if (RT_SUCCESS(rc))
+    {
+        /*
+         * Tranverse the path, resolving the parent node and any symlinks
+         * in the final element, and ask the directory to open the subdir.
+         */
+        RTVFSDIRINTERNAL *pVfsParentDir;
+        rc = rtVfsDirTraverseToParent(pThis, pPath, true /*fFollowSymlink*/, &pVfsParentDir);
+        if (RT_SUCCESS(rc))
+        {
+            const char *pszEntryName = &pPath->szPath[pPath->aoffComponents[pPath->cComponents - 1]];
+
+            /** @todo there is a symlink creation race here. */
+            RTVfsLockAcquireWrite(pVfsParentDir->Base.hLock);
+            rc = pVfsParentDir->pOps->pfnOpenDir(pVfsParentDir->Base.pvThis, pszEntryName, fFlags, phVfsDir);
+            RTVfsLockReleaseWrite(pVfsParentDir->Base.hLock);
+
+            RTVfsDirRelease(pVfsParentDir);
+
+            if (RT_SUCCESS(rc))
+            {
+                AssertPtr(*phVfsDir);
+                Assert((*phVfsDir)->uMagic == RTVFSDIR_MAGIC);
+            }
+        }
+        RTVfsParsePathFree(pPath);
+    }
+    return rc;
+}
+
+
+RTDECL(int) RTVfsDirOpenFile(RTVFSDIR hVfsDir, const char *pszPath, uint64_t fOpen, PRTVFSFILE phVfsFile)
+{
+    /*
+     * Validate input.
+     */
+    RTVFSDIRINTERNAL *pThis = hVfsDir;
+    AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+    AssertReturn(pThis->uMagic == RTVFSDIR_MAGIC, VERR_INVALID_HANDLE);
+    AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
+    AssertPtrReturn(phVfsFile, VERR_INVALID_POINTER);
+
+    int rc = rtFileRecalcAndValidateFlags(&fOpen);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    /*
+     * Parse the path, assume current directory is root since we've got no
+     * caller context here.
+     */
+    PRTVFSPARSEDPATH pPath;
+    rc = RTVfsParsePathA(pszPath, "/", &pPath);
+    if (RT_SUCCESS(rc))
+    {
+        if (!pPath->fDirSlash)
+        {
+            /*
+             * Tranverse the path, resolving the parent node and any symlinks
+             * in the final element, and ask the directory to open the file.
+             */
+            RTVFSDIRINTERNAL *pVfsParentDir;
+            rc = rtVfsDirTraverseToParent(pThis, pPath, true /*fFollowSymlink*/, &pVfsParentDir);
+            if (RT_SUCCESS(rc))
+            {
+                const char *pszEntryName = &pPath->szPath[pPath->aoffComponents[pPath->cComponents - 1]];
+
+                /** @todo there is a symlink creation race here. */
+                RTVfsLockAcquireWrite(pVfsParentDir->Base.hLock);
+                rc = pVfsParentDir->pOps->pfnOpenFile(pVfsParentDir->Base.pvThis, pszEntryName, fOpen, phVfsFile);
+                RTVfsLockReleaseWrite(pVfsParentDir->Base.hLock);
+
+                RTVfsDirRelease(pVfsParentDir);
+
+                if (RT_SUCCESS(rc))
+                {
+                    AssertPtr(*phVfsFile);
+                    Assert((*phVfsFile)->uMagic == RTVFSFILE_MAGIC);
+                }
+            }
+        }
+        else
+            rc = VERR_INVALID_PARAMETER;
+        RTVfsParsePathFree(pPath);
+    }
+    return rc;
+}
 
 
@@ -2433,5 +2646,5 @@
 
 
-RTDECL(int)         RTVfsFileOpen(RTVFS hVfs, const char *pszFilename, uint64_t fOpen, PRTVFSFILE phVfsFile)
+RTDECL(int) RTVfsFileOpen(RTVFS hVfs, const char *pszFilename, uint64_t fOpen, PRTVFSFILE phVfsFile)
 {
     /*
Index: /trunk/src/VBox/Runtime/common/vfs/vfschain.cpp
===================================================================
--- /trunk/src/VBox/Runtime/common/vfs/vfschain.cpp	(revision 66600)
+++ /trunk/src/VBox/Runtime/common/vfs/vfschain.cpp	(revision 66601)
@@ -48,4 +48,9 @@
 
 
+/*********************************************************************************************************************************
+*   Internal Functions                                                                                                           *
+*********************************************************************************************************************************/
+static PCRTVFSCHAINELEMENTREG rtVfsChainFindProviderLocked(const char *pszProvider);
+
 
 /*********************************************************************************************************************************
@@ -61,4 +66,249 @@
 
 
+RTDECL(int) RTVfsChainValidateOpenFileOrIoStream(PRTVFSCHAINSPEC pSpec, PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError)
+{
+    if (pElement->cArgs < 1)
+        return VERR_VFS_CHAIN_AT_LEAST_ONE_ARG;
+    if (pElement->cArgs > 4)
+        return VERR_VFS_CHAIN_AT_MOST_FOUR_ARGS;
+    if (!*pElement->paArgs[0].psz)
+        return VERR_VFS_CHAIN_EMPTY_ARG;
+
+    /*
+     * Calculate the flags, storing them in the first argument.
+     */
+    const char *pszAccess = pElement->cArgs >= 2 ? pElement->paArgs[1].psz : "";
+    if (!*pszAccess)
+        pszAccess = (pSpec->fOpenFile & RTFILE_O_ACCESS_MASK) == RTFILE_O_READWRITE ? "rw"
+                  : (pSpec->fOpenFile & RTFILE_O_ACCESS_MASK) == RTFILE_O_READ      ? "r"
+                  : (pSpec->fOpenFile & RTFILE_O_ACCESS_MASK) == RTFILE_O_WRITE     ? "w"
+                  :                                                                   "rw";
+
+    const char *pszDisp = pElement->cArgs >= 3 ? pElement->paArgs[2].psz : "";
+    if (!*pszDisp)
+        pszDisp = strchr(pszAccess, 'w') != NULL ? "open-create" : "open";
+
+    const char *pszSharing = pElement->cArgs >= 4 ? pElement->paArgs[3].psz : "";
+
+    int rc = RTFileModeToFlagsEx(pszAccess, pszDisp, pszSharing, &pElement->uProvider);
+    if (RT_SUCCESS(rc))
+        return VINF_SUCCESS;
+
+    /*
+     * Now try figure out which argument offended us.
+     */
+    AssertReturn(pElement->cArgs > 1, VERR_VFS_CHAIN_IPE);
+    if (   pElement->cArgs == 2
+        || RT_FAILURE(RTFileModeToFlagsEx(pszAccess, "open-create", "", &pElement->uProvider)))
+        *poffError = pElement->paArgs[1].offSpec;
+    else if (   pElement->cArgs == 3
+             || RT_FAILURE(RTFileModeToFlagsEx(pszAccess, pszDisp, "", &pElement->uProvider)))
+        *poffError = pElement->paArgs[2].offSpec;
+    else
+        *poffError = pElement->paArgs[3].offSpec;
+    return VERR_VFS_CHAIN_INVALID_ARGUMENT;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnValidate}
+ */
+static DECLCALLBACK(int) rtVfsChainOpen_Validate(PCRTVFSCHAINELEMENTREG pProviderReg, PRTVFSCHAINSPEC pSpec,
+                                                 PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError)
+{
+    RT_NOREF(pProviderReg);
+
+    /*
+     * Basic checks.
+     */
+    if (   pElement->enmType != RTVFSOBJTYPE_DIR
+        && pElement->enmType != RTVFSOBJTYPE_FILE
+        && pElement->enmType != RTVFSOBJTYPE_IO_STREAM)
+        return VERR_VFS_CHAIN_ONLY_FILE_OR_IOS_OR_DIR;
+    if (   pElement->enmTypeIn != RTVFSOBJTYPE_DIR
+        && pElement->enmTypeIn != RTVFSOBJTYPE_FS_STREAM
+        && pElement->enmTypeIn != RTVFSOBJTYPE_VFS)
+    {
+        if (pElement->enmTypeIn == RTVFSOBJTYPE_INVALID)
+        {
+            /*
+             * First element: Ttransform into 'stdfile' or 'stddir' if registered.
+             */
+            const char            *pszNewProvider = pElement->enmType == RTVFSOBJTYPE_DIR ? "stddir" : "stdfile";
+            PCRTVFSCHAINELEMENTREG pNewProvider   = rtVfsChainFindProviderLocked(pszNewProvider);
+            if (pNewProvider)
+            {
+                pElement->pProvider = pNewProvider;
+                return pNewProvider->pfnValidate(pNewProvider, pSpec, pElement, poffError);
+            }
+            return VERR_VFS_CHAIN_CANNOT_BE_FIRST_ELEMENT;
+        }
+        return VERR_VFS_CHAIN_TAKES_DIR_OR_FSS_OR_VFS;
+    }
+
+    /*
+     * Make common cause with 'stdfile' if we're opening a file or I/O stream.
+     * If the input is a FSS, we have to make sure it's a read-only operation.
+     */
+    if (   pElement->enmType != RTVFSOBJTYPE_FILE
+        && pElement->enmType != RTVFSOBJTYPE_IO_STREAM)
+    {
+        int rc = RTVfsChainValidateOpenFileOrIoStream(pSpec, pElement, poffError);
+        if (RT_SUCCESS(rc))
+        {
+            if (pElement->enmTypeIn != RTVFSOBJTYPE_FS_STREAM)
+                return VINF_SUCCESS;
+            if (   !(pElement->uProvider & RTFILE_O_WRITE)
+                &&  (pElement->uProvider & RTFILE_O_ACTION_MASK) ==  RTFILE_O_OPEN)
+                return VINF_SUCCESS;
+            *poffError = pElement->cArgs > 1 ? pElement->paArgs[1].offSpec : pElement->offSpec;
+            return VERR_VFS_CHAIN_INVALID_ARGUMENT;
+        }
+    }
+
+    /*
+     * Directory checks.  Path argument only, optional. If not given the root directory of a VFS or the
+     */
+    if (pElement->cArgs > 1)
+        return VERR_VFS_CHAIN_AT_MOST_ONE_ARG;
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnInstantiate}
+ */
+static DECLCALLBACK(int) rtVfsChainOpen_Instantiate(PCRTVFSCHAINELEMENTREG pProviderReg, PCRTVFSCHAINSPEC pSpec,
+                                                    PCRTVFSCHAINELEMSPEC pElement, RTVFSOBJ hPrevVfsObj,
+                                                    PRTVFSOBJ phVfsObj, uint32_t *poffError)
+{
+    RT_NOREF(pProviderReg, pSpec, pElement, poffError);
+    AssertReturn(hPrevVfsObj != NIL_RTVFSOBJ, VERR_VFS_CHAIN_IPE);
+
+    /*
+     * File system stream: Seek thru the stream looking for the object to open.
+     */
+    RTVFSFSSTREAM hVfsFssIn = RTVfsObjToFsStream(hPrevVfsObj);
+    if (hVfsFssIn != NIL_RTVFSFSSTREAM)
+    {
+        return VERR_NOT_IMPLEMENTED;
+    }
+
+    /*
+     * VFS: Use RTVfsFileOpen or RTVfsDirOpen.
+     */
+    RTVFS hVfsIn = RTVfsObjToVfs(hPrevVfsObj);
+    if (hVfsIn != NIL_RTVFS)
+    {
+        if (   pElement->enmType == RTVFSOBJTYPE_FILE
+            || pElement->enmType == RTVFSOBJTYPE_IO_STREAM)
+        {
+            RTVFSFILE hVfsFile = NIL_RTVFSFILE;
+            int rc = RTVfsFileOpen(hVfsIn, pElement->paArgs[0].psz, pElement->uProvider, &hVfsFile);
+            if (RT_SUCCESS(rc))
+            {
+                *phVfsObj = RTVfsObjFromFile(hVfsFile);
+                RTVfsFileRelease(hVfsFile);
+                if (*phVfsObj != NIL_RTVFSOBJ)
+                    return VINF_SUCCESS;
+                rc = VERR_VFS_CHAIN_CAST_FAILED;
+            }
+            return rc;
+        }
+        if (pElement->enmType == RTVFSOBJTYPE_DIR)
+        {
+            RTVFSDIR hVfsDir = NIL_RTVFSDIR;
+            int rc = RTVfsDirOpen(hVfsIn, pElement->paArgs[0].psz, (uint32_t)pElement->uProvider, &hVfsDir);
+            if (RT_SUCCESS(rc))
+            {
+                *phVfsObj = RTVfsObjFromDir(hVfsDir);
+                RTVfsDirRelease(hVfsDir);
+                if (*phVfsObj != NIL_RTVFSOBJ)
+                    return VINF_SUCCESS;
+                rc = VERR_VFS_CHAIN_CAST_FAILED;
+            }
+            return rc;
+        }
+        return VERR_VFS_CHAIN_IPE;
+    }
+
+    /*
+     * Directory: Similar to above, just relative to a directory.
+     */
+    RTVFSDIR hVfsDirIn = RTVfsObjToDir(hPrevVfsObj);
+    if (hVfsDirIn != NIL_RTVFSDIR)
+    {
+        if (   pElement->enmType == RTVFSOBJTYPE_FILE
+            || pElement->enmType == RTVFSOBJTYPE_IO_STREAM)
+        {
+            RTVFSFILE hVfsFile = NIL_RTVFSFILE;
+            int rc = RTVfsDirOpenFile(hVfsDirIn, pElement->paArgs[0].psz, pElement->uProvider, &hVfsFile);
+            if (RT_SUCCESS(rc))
+            {
+                *phVfsObj = RTVfsObjFromFile(hVfsFile);
+                RTVfsFileRelease(hVfsFile);
+                if (*phVfsObj != NIL_RTVFSOBJ)
+                    return VINF_SUCCESS;
+                rc = VERR_VFS_CHAIN_CAST_FAILED;
+            }
+            return rc;
+        }
+        if (pElement->enmType == RTVFSOBJTYPE_DIR)
+        {
+            RTVFSDIR hVfsDir = NIL_RTVFSDIR;
+            int rc = RTVfsDirOpenDir(hVfsDirIn, pElement->paArgs[0].psz, pElement->uProvider, &hVfsDir);
+            if (RT_SUCCESS(rc))
+            {
+                *phVfsObj = RTVfsObjFromDir(hVfsDir);
+                RTVfsDirRelease(hVfsDir);
+                if (*phVfsObj != NIL_RTVFSOBJ)
+                    return VINF_SUCCESS;
+                rc = VERR_VFS_CHAIN_CAST_FAILED;
+            }
+            return rc;
+        }
+        return VERR_VFS_CHAIN_IPE;
+    }
+
+    AssertFailed();
+    return VERR_VFS_CHAIN_CAST_FAILED;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnCanReuseElement}
+ */
+static DECLCALLBACK(bool) rtVfsChainOpen_CanReuseElement(PCRTVFSCHAINELEMENTREG pProviderReg,
+                                                         PCRTVFSCHAINSPEC pSpec, PCRTVFSCHAINELEMSPEC pElement,
+                                                         PCRTVFSCHAINSPEC pReuseSpec, PCRTVFSCHAINELEMSPEC pReuseElement)
+{
+    RT_NOREF(pProviderReg, pSpec, pElement, pReuseSpec, pReuseElement);
+    return false;
+}
+
+
+/** VFS chain element 'gunzip'. */
+static RTVFSCHAINELEMENTREG g_rtVfsChainGunzipReg =
+{
+    /* uVersion = */            RTVFSCHAINELEMENTREG_VERSION,
+    /* fReserved = */           0,
+    /* pszName = */             "open",
+    /* ListEntry = */           { NULL, NULL },
+    /* pszHelp = */             "Generic VFS open, that can open files (or I/O stream) and directories in a VFS, directory or file system stream.\n"
+                                "If used as the first element in a chain, it will work like 'stdfile' or 'stddir' and work on the real file system.\n"
+                                "First argument is the filename or directory path.\n"
+                                "Second argument is access mode, files only, optional: r, w, rw.\n"
+                                "Third argument is open disposition, files only, optional: create, create-replace, open, open-create, open-append, open-truncate.\n"
+                                "Forth argument is file sharing, files only, optional: nr, nw, nrw, d.",
+    /* pfnValidate = */         rtVfsChainOpen_Validate,
+    /* pfnInstantiate = */      rtVfsChainOpen_Instantiate,
+    /* pfnCanReuseElement = */  rtVfsChainOpen_CanReuseElement,
+    /* uEndMarker = */          RTVFSCHAINELEMENTREG_VERSION
+};
+
+RTVFSCHAIN_AUTO_REGISTER_ELEMENT_PROVIDER(&g_rtVfsChainGunzipReg, rtVfsChainGunzipReg);
+
+
+
 
 /**
@@ -155,5 +405,4 @@
         pSpec->fOpenDir       = 0;
         pSpec->cElements      = 0;
-        pSpec->uProvider      = 0;
         pSpec->paElements     = NULL;
     }
@@ -628,4 +877,5 @@
             {
                 PRTVFSCHAINELEMSPEC const pElement = &pSpec->paElements[i];
+                *poffError = pElement->offSpec;
 
                 /*
@@ -862,2 +1112,3 @@
 }
 
+
Index: /trunk/src/VBox/Runtime/common/vfs/vfsreadahead.cpp
===================================================================
--- /trunk/src/VBox/Runtime/common/vfs/vfsreadahead.cpp	(revision 66600)
+++ /trunk/src/VBox/Runtime/common/vfs/vfsreadahead.cpp	(revision 66601)
@@ -826,13 +826,16 @@
      * Basics.
      */
-    if (pElement->enmTypeIn == RTVFSOBJTYPE_INVALID)
-        return VERR_VFS_CHAIN_CANNOT_BE_FIRST_ELEMENT;
-    if (pElement->cArgs > 2)
-        return VERR_VFS_CHAIN_AT_MOST_TWO_ARGS;
     if (   pElement->enmType != RTVFSOBJTYPE_FILE
         && pElement->enmType != RTVFSOBJTYPE_IO_STREAM)
         return VERR_VFS_CHAIN_ONLY_FILE_OR_IOS;
+    if (pElement->enmTypeIn == RTVFSOBJTYPE_INVALID)
+        return VERR_VFS_CHAIN_CANNOT_BE_FIRST_ELEMENT;
+    if (   pElement->enmTypeIn != RTVFSOBJTYPE_FILE
+        && pElement->enmTypeIn != RTVFSOBJTYPE_IO_STREAM)
+        return VERR_VFS_CHAIN_TAKES_FILE_OR_IOS;
     if (pSpec->fOpenFile & RTFILE_O_WRITE)
         return VERR_VFS_CHAIN_READ_ONLY_IOS;
+    if (pElement->cArgs > 2)
+        return VERR_VFS_CHAIN_AT_MOST_TWO_ARGS;
 
     /*
@@ -872,5 +875,5 @@
      * Save the parsed arguments in the spec since their both optional.
      */
-    pSpec->uProvider = RT_MAKE_U64(cBuffers, cbBuffer);
+    pElement->uProvider = RT_MAKE_U64(cBuffers, cbBuffer);
 
     return VINF_SUCCESS;
@@ -894,6 +897,6 @@
     {
         RTVFSFILE hVfsFile = NIL_RTVFSFILE;
-        rc = RTVfsCreateReadAheadForFile(hVfsFileIn, 0 /*fFlags*/, RT_LO_U32(pSpec->uProvider),
-                                         RT_HI_U32(pSpec->uProvider), &hVfsFile);
+        rc = RTVfsCreateReadAheadForFile(hVfsFileIn, 0 /*fFlags*/, RT_LO_U32(pElement->uProvider),
+                                         RT_HI_U32(pElement->uProvider), &hVfsFile);
         RTVfsFileRelease(hVfsFileIn);
         if (RT_SUCCESS(rc))
@@ -912,6 +915,6 @@
         {
             RTVFSIOSTREAM hVfsIos = NIL_RTVFSIOSTREAM;
-            rc = RTVfsCreateReadAheadForIoStream(hVfsIosIn, 0 /*fFlags*/, RT_LO_U32(pSpec->uProvider),
-                                                 RT_HI_U32(pSpec->uProvider), &hVfsIos);
+            rc = RTVfsCreateReadAheadForIoStream(hVfsIosIn, 0 /*fFlags*/, RT_LO_U32(pElement->uProvider),
+                                                 RT_HI_U32(pElement->uProvider), &hVfsIos);
             RTVfsIoStrmRelease(hVfsIosIn);
             if (RT_SUCCESS(rc))
Index: /trunk/src/VBox/Runtime/common/vfs/vfsstdfile.cpp
===================================================================
--- /trunk/src/VBox/Runtime/common/vfs/vfsstdfile.cpp	(revision 66600)
+++ /trunk/src/VBox/Runtime/common/vfs/vfsstdfile.cpp	(revision 66601)
@@ -526,47 +526,13 @@
     if (pElement->enmTypeIn != RTVFSOBJTYPE_INVALID)
         return VERR_VFS_CHAIN_MUST_BE_FIRST_ELEMENT;
-    if (pElement->cArgs < 1)
-        return VERR_VFS_CHAIN_AT_LEAST_ONE_ARG;
-    if (pElement->cArgs > 4)
-        return VERR_VFS_CHAIN_AT_MOST_FOUR_ARGS;
-    if (!*pElement->paArgs[0].psz)
-        return VERR_VFS_CHAIN_EMPTY_ARG;
     if (   pElement->enmType != RTVFSOBJTYPE_FILE
         && pElement->enmType != RTVFSOBJTYPE_IO_STREAM)
         return VERR_VFS_CHAIN_ONLY_FILE_OR_IOS;
 
+
     /*
-     * Calculate the flags, storing them in the first argument.
+     * Join common cause with the 'open' provider.
      */
-    const char *pszAccess = pElement->cArgs >= 2 ? pElement->paArgs[1].psz : "";
-    if (!*pszAccess)
-        pszAccess = (pSpec->fOpenFile & RTFILE_O_ACCESS_MASK) == RTFILE_O_READWRITE ? "rw"
-                  : (pSpec->fOpenFile & RTFILE_O_ACCESS_MASK) == RTFILE_O_READ      ? "r"
-                  : (pSpec->fOpenFile & RTFILE_O_ACCESS_MASK) == RTFILE_O_WRITE     ? "w"
-                  :                                                                   "rw";
-
-    const char *pszDisp = pElement->cArgs >= 3 ? pElement->paArgs[2].psz : "";
-    if (!*pszDisp)
-        pszDisp = strchr(pszAccess, 'w') != NULL ? "open-create" : "open";
-
-    const char *pszSharing = pElement->cArgs >= 4 ? pElement->paArgs[3].psz : "";
-
-    int rc = RTFileModeToFlagsEx(pszAccess, pszDisp, pszSharing, &pElement->paArgs[0].uProvider);
-    if (RT_SUCCESS(rc))
-        return VINF_SUCCESS;
-
-    /*
-     * Now try figure out which argument offended us.
-     */
-    AssertReturn(pElement->cArgs > 1, VERR_VFS_CHAIN_IPE);
-    if (   pElement->cArgs == 2
-        || RT_FAILURE(RTFileModeToFlagsEx(pszAccess, "open-create", "", &pElement->paArgs[0].uProvider)))
-        *poffError = pElement->paArgs[1].offSpec;
-    else if (   pElement->cArgs == 3
-             || RT_FAILURE(RTFileModeToFlagsEx(pszAccess, pszDisp, "", &pElement->paArgs[0].uProvider)))
-        *poffError = pElement->paArgs[2].offSpec;
-    else
-        *poffError = pElement->paArgs[3].offSpec;
-    return VERR_VFS_CHAIN_INVALID_ARGUMENT;
+    return RTVfsChainValidateOpenFileOrIoStream(pSpec, pElement, poffError);
 }
 
@@ -583,5 +549,5 @@
 
     RTVFSFILE hVfsFile;
-    int rc = RTVfsFileOpenNormal(pElement->paArgs[0].psz, pElement->paArgs[0].uProvider, &hVfsFile);
+    int rc = RTVfsFileOpenNormal(pElement->paArgs[0].psz, pElement->uProvider, &hVfsFile);
     if (RT_SUCCESS(rc))
     {
Index: /trunk/src/VBox/Runtime/common/zip/gzipvfs.cpp
===================================================================
--- /trunk/src/VBox/Runtime/common/zip/gzipvfs.cpp	(revision 66600)
+++ /trunk/src/VBox/Runtime/common/zip/gzipvfs.cpp	(revision 66601)
@@ -839,12 +839,15 @@
     RT_NOREF(pProviderReg, poffError);
 
+    if (pElement->enmType != RTVFSOBJTYPE_IO_STREAM)
+        return VERR_VFS_CHAIN_ONLY_IOS;
     if (pElement->enmTypeIn == RTVFSOBJTYPE_INVALID)
         return VERR_VFS_CHAIN_CANNOT_BE_FIRST_ELEMENT;
+    if (   pElement->enmTypeIn != RTVFSOBJTYPE_FILE
+        && pElement->enmTypeIn != RTVFSOBJTYPE_IO_STREAM)
+        return VERR_VFS_CHAIN_TAKES_FILE_OR_IOS;
+    if (pSpec->fOpenFile & RTFILE_O_WRITE)
+        return VERR_VFS_CHAIN_READ_ONLY_IOS;
     if (pElement->cArgs != 0)
         return VERR_VFS_CHAIN_NO_ARGS;
-    if (pElement->enmType != RTVFSOBJTYPE_IO_STREAM)
-        return VERR_VFS_CHAIN_ONLY_IOS;
-    if (pSpec->fOpenFile & RTFILE_O_WRITE)
-        return VERR_VFS_CHAIN_READ_ONLY_IOS;
 
     return VINF_SUCCESS;
@@ -922,12 +925,15 @@
      * Basics.
      */
+    if (pElement->enmType != RTVFSOBJTYPE_IO_STREAM)
+        return VERR_VFS_CHAIN_ONLY_IOS;
     if (pElement->enmTypeIn == RTVFSOBJTYPE_INVALID)
         return VERR_VFS_CHAIN_CANNOT_BE_FIRST_ELEMENT;
+    if (   pElement->enmTypeIn != RTVFSOBJTYPE_FILE
+        && pElement->enmTypeIn != RTVFSOBJTYPE_IO_STREAM)
+        return VERR_VFS_CHAIN_TAKES_FILE_OR_IOS;
+    if (pSpec->fOpenFile & RTFILE_O_READ)
+        return VERR_VFS_CHAIN_WRITE_ONLY_IOS;
     if (pElement->cArgs > 1)
         return VERR_VFS_CHAIN_AT_MOST_ONE_ARG;
-    if (pElement->enmType != RTVFSOBJTYPE_IO_STREAM)
-        return VERR_VFS_CHAIN_ONLY_IOS;
-    if (pSpec->fOpenFile & RTFILE_O_READ)
-        return VERR_VFS_CHAIN_WRITE_ONLY_IOS;
 
     /*
@@ -939,11 +945,11 @@
         const char *psz = pElement->paArgs[0].psz;
         if (!*psz || !strcmp(psz, "default"))
-            pSpec->uProvider = 6;
+            pElement->uProvider = 6;
         else if (!strcmp(psz, "fast"))
-            pSpec->uProvider = 3;
+            pElement->uProvider = 3;
         else if (   RT_C_IS_DIGIT(*psz)
                  && *psz != '0'
                  && *RTStrStripL(psz + 1) == '\0')
-            pSpec->uProvider = *psz - '0';
+            pElement->uProvider = *psz - '0';
         else
         {
@@ -953,5 +959,5 @@
     }
     else
-        pSpec->uProvider = 6;
+        pElement->uProvider = 6;
 
     return VINF_SUCCESS;
@@ -974,5 +980,5 @@
 
     RTVFSIOSTREAM hVfsIos = NIL_RTVFSIOSTREAM;
-    int rc = RTZipGzipCompressIoStream(hVfsIosOut, 0 /*fFlags*/, pSpec->uProvider, &hVfsIos);
+    int rc = RTZipGzipCompressIoStream(hVfsIosOut, 0 /*fFlags*/, pElement->uProvider, &hVfsIos);
     RTVfsObjFromIoStream(hVfsIosOut);
     if (RT_SUCCESS(rc))
