Index: trunk/src/VBox/Runtime/common/fs/fatvfs.cpp
===================================================================
--- trunk/src/VBox/Runtime/common/fs/fatvfs.cpp	(revision 66742)
+++ trunk/src/VBox/Runtime/common/fs/fatvfs.cpp	(revision 66762)
@@ -51,4 +51,6 @@
 #include <iprt/zero.h>
 #include <iprt/formats/fat.h>
+
+#include "internal/fs.h"
 
 
@@ -1458,4 +1460,5 @@
     pObj->offEntryInDir     = offEntryInDir;
     pObj->fAttrib           = ((RTFMODE)pDirEntry->fAttrib << RTFS_DOS_SHIFT) & RTFS_DOS_MASK_OS2;
+    pObj->fAttrib           = rtFsModeFromDos(pObj->fAttrib, (char *)&pDirEntry->achName[0], sizeof(pDirEntry->achName), 0);
     pObj->cbObject          = pDirEntry->cbFile;
     pObj->fMaybeDirtyFat    = false;
@@ -3481,4 +3484,34 @@
 
 /**
+ * @interface_method_impl{RTVFSDIROPS,pfnQueryEntryInfo}
+ */
+static DECLCALLBACK(int) rtFsFatDir_QueryEntryInfo(void *pvThis, const char *pszEntry,
+                                                   PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
+{
+    /*
+     * Try locate the entry.
+     */
+    PRTFSFATDIR pThis = (PRTFSFATDIR)pvThis;
+    uint32_t    offEntryInDir;
+    bool        fLong;
+    FATDIRENTRY DirEntry;
+    int rc = rtFsFatDir_FindEntry(pThis, pszEntry, &offEntryInDir, &fLong, &DirEntry);
+    Log2(("rtFsFatDir_QueryEntryInfo: FindEntry(,%s,) -> %Rrc\n", pszEntry, rc));
+    if (RT_SUCCESS(rc))
+    {
+        /*
+         * To avoid duplicating code in rtFsFatObj_InitFromDirRec and
+         * rtFsFatObj_QueryInfo, we create a dummy RTFSFATOBJ on the stack.
+         */
+        RTFSFATOBJ TmpObj;
+        RT_ZERO(TmpObj);
+        rtFsFatObj_InitFromDirEntry(&TmpObj, &DirEntry, offEntryInDir, pThis->Core.pVol);
+        rc = rtFsFatObj_QueryInfo(&TmpObj, pObjInfo, enmAddAttr);
+    }
+    return rc;
+}
+
+
+/**
  * @interface_method_impl{RTVFSDIROPS,pfnUnlinkEntry}
  */
@@ -3486,4 +3519,15 @@
 {
     RT_NOREF(pvThis, pszEntry, fType);
+RTAssertMsg2("%s: %s\n", __FUNCTION__, pszEntry);
+    return VERR_NOT_IMPLEMENTED;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSDIROPS,pfnRenameEntry}
+ */
+static DECLCALLBACK(int) rtFsFatDir_RenameEntry(void *pvThis, const char *pszEntry, RTFMODE fType, const char *pszNewName)
+{
+    RT_NOREF(pvThis, pszEntry, fType, pszNewName);
 RTAssertMsg2("%s: %s\n", __FUNCTION__, pszEntry);
     return VERR_NOT_IMPLEMENTED;
@@ -3543,5 +3587,7 @@
     rtFsFatDir_OpenSymlink,
     rtFsFatDir_CreateSymlink,
+    rtFsFatDir_QueryEntryInfo,
     rtFsFatDir_UnlinkEntry,
+    rtFsFatDir_RenameEntry,
     rtFsFatDir_RewindDir,
     rtFsFatDir_ReadDir,
@@ -3664,5 +3710,5 @@
             rtFsFatObj_InitFromDirEntry(&pNewDir->Core, pDirEntry, offEntryInDir, pThis);
         else
-            rtFsFatObj_InitDummy(&pNewDir->Core, cbDir, RTFS_DOS_DIRECTORY, pThis);
+            rtFsFatObj_InitDummy(&pNewDir->Core, cbDir, RTFS_TYPE_DIRECTORY | RTFS_DOS_DIRECTORY | RTFS_UNIX_ALL_PERMS, pThis);
 
         pNewDir->hVfsSelf           = *phVfsDir;
Index: trunk/src/VBox/Runtime/common/fs/iso9660vfs.cpp
===================================================================
--- trunk/src/VBox/Runtime/common/fs/iso9660vfs.cpp	(revision 66742)
+++ trunk/src/VBox/Runtime/common/fs/iso9660vfs.cpp	(revision 66762)
@@ -937,5 +937,5 @@
  */
 static DECLCALLBACK(int) rtFsIso9660Dir_CreateSymlink(void *pvThis, const char *pszSymlink, const char *pszTarget,
-                                                  RTSYMLINKTYPE enmType, PRTVFSSYMLINK phVfsSymlink)
+                                                      RTSYMLINKTYPE enmType, PRTVFSSYMLINK phVfsSymlink)
 {
     RT_NOREF(pvThis, pszSymlink, pszTarget, enmType, phVfsSymlink);
@@ -945,4 +945,35 @@
 
 /**
+ * @interface_method_impl{RTVFSDIROPS,pfnQueryEntryInfo}
+ */
+static DECLCALLBACK(int) rtFsIso9660Dir_QueryEntryInfo(void *pvThis, const char *pszEntry,
+                                                       PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
+{
+    /*
+     * Try locate the entry.
+     */
+    PRTFSISO9660DIR pThis = (PRTFSISO9660DIR)pvThis;
+    PCISO9660DIRREC pDirRec;
+    uint64_t        offDirRec;
+    uint32_t        cDirRecs;
+    RTFMODE         fMode;
+    int rc = rtFsIso9660Dir_FindEntry(pThis, pszEntry, &offDirRec, &pDirRec, &cDirRecs, &fMode);
+    Log2(("rtFsIso9660Dir_QueryEntryInfo: FindEntry(,%s,) -> %Rrc\n", pszEntry, rc));
+    if (RT_SUCCESS(rc))
+    {
+        /*
+         * To avoid duplicating code in rtFsIso9660Obj_InitFromDirRec and
+         * rtFsIso9660Obj_QueryInfo, we create a dummy RTFSISO9660OBJ on the stack.
+         */
+        RTFSISO9660OBJ TmpObj;
+        RT_ZERO(TmpObj);
+        rtFsIso9660Obj_InitFromDirRec(&TmpObj, pDirRec, cDirRecs, offDirRec, pThis->Core.pVol);
+        rc = rtFsIso9660Obj_QueryInfo(&TmpObj, pObjInfo, enmAddAttr);
+    }
+    return rc;
+}
+
+
+/**
  * @interface_method_impl{RTVFSDIROPS,pfnUnlinkEntry}
  */
@@ -950,4 +981,14 @@
 {
     RT_NOREF(pvThis, pszEntry, fType);
+    return VERR_WRITE_PROTECT;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSDIROPS,pfnRenameEntry}
+ */
+static DECLCALLBACK(int) rtFsIso9660Dir_RenameEntry(void *pvThis, const char *pszEntry, RTFMODE fType, const char *pszNewName)
+{
+    RT_NOREF(pvThis, pszEntry, fType, pszNewName);
     return VERR_WRITE_PROTECT;
 }
@@ -1006,5 +1047,7 @@
     rtFsIso9660Dir_OpenSymlink,
     rtFsIso9660Dir_CreateSymlink,
+    rtFsIso9660Dir_QueryEntryInfo,
     rtFsIso9660Dir_UnlinkEntry,
+    rtFsIso9660Dir_RenameEntry,
     rtFsIso9660Dir_RewindDir,
     rtFsIso9660Dir_ReadDir,
Index: trunk/src/VBox/Runtime/common/vfs/vfsbase.cpp
===================================================================
--- trunk/src/VBox/Runtime/common/vfs/vfsbase.cpp	(revision 66742)
+++ trunk/src/VBox/Runtime/common/vfs/vfsbase.cpp	(revision 66762)
@@ -1429,10 +1429,9 @@
  * @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   fFlags          RTPATH_F_XXX.
  * @param   ppVfsParentDir  Where to return the parent directory handle
  *                          (referenced).
  */
-static int rtVfsDirTraverseToParent(RTVFSDIRINTERNAL *pThis, PRTVFSPARSEDPATH pPath, bool fFollowSymlink,
+static int rtVfsDirTraverseToParent(RTVFSDIRINTERNAL *pThis, PRTVFSPARSEDPATH pPath, uint32_t fFlags,
                                     RTVFSDIRINTERNAL **ppVfsParentDir)
 {
@@ -1447,4 +1446,5 @@
     *ppVfsParentDir = NULL;
     AssertReturn(pPath->cComponents > 0, VERR_INTERNAL_ERROR_3);
+    Assert(RTPATH_F_IS_VALID(fFlags, 0));
 
     /*
@@ -1467,5 +1467,5 @@
          */
         bool fFinal = iComponent + 1 >= pPath->cComponents;
-        if (fFinal && !fFollowSymlink)
+        if (fFinal && (fFlags & RTPATH_F_ON_LINK))
         {
             *ppVfsParentDir = pCurDir;
@@ -1539,4 +1539,9 @@
             AssertPtr(hSymlink);
             Assert(hSymlink->uMagic == RTVFSSYMLINK_MAGIC);
+            if (fFlags & RTPATH_F_NO_SYMLINKS)
+            {
+                rc = VERR_SYMLINK_NOT_ALLOWED;
+                break;
+            }
             cLinks++;
             if (cLinks >= RTVFS_MAX_LINKS)
@@ -1596,11 +1601,9 @@
  * @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   fFlags          RTPATH_F_XXX.
  * @param   ppVfsParentDir  Where to return the parent directory handle
  *                          (referenced).
  */
-static int rtVfsTraverseToParent(RTVFSINTERNAL *pThis, PRTVFSPARSEDPATH pPath, bool fFollowSymlink,
-                                 RTVFSDIRINTERNAL **ppVfsParentDir)
+static int rtVfsTraverseToParent(RTVFSINTERNAL *pThis, PRTVFSPARSEDPATH pPath, uint32_t fFlags, RTVFSDIRINTERNAL **ppVfsParentDir)
 {
     /*
@@ -1614,4 +1617,5 @@
     *ppVfsParentDir = NULL;
     AssertReturn(pPath->cComponents > 0, VERR_INTERNAL_ERROR_3);
+    Assert(RTPATH_F_IS_VALID(fFlags, 0));
 
     /*
@@ -1625,5 +1629,5 @@
     if (RT_SUCCESS(rc))
     {
-        rc = rtVfsDirTraverseToParent(pRootDir, pPath, fFollowSymlink, ppVfsParentDir);
+        rc = rtVfsDirTraverseToParent(pRootDir, pPath, fFlags, ppVfsParentDir);
         RTVfsDirRelease(pRootDir);
     }
@@ -1820,4 +1824,65 @@
 
 
+RTDECL(int) RTVfsQueryPathInfo(RTVFS hVfs, const char *pszPath, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr, uint32_t fFlags)
+{
+    RTVFSINTERNAL *pThis = hVfs;
+    AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+    AssertReturn(pThis->uMagic == RTVFS_MAGIC, VERR_INVALID_HANDLE);
+    AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
+    AssertReturn(*pszPath, VERR_INVALID_PARAMETER);
+    AssertPtrReturn(pObjInfo, VERR_INVALID_POINTER);
+    AssertReturn(enmAddAttr >= RTFSOBJATTRADD_NOTHING &&  enmAddAttr <= RTFSOBJATTRADD_LAST, VERR_INVALID_PARAMETER);
+    AssertMsgReturn(RTPATH_F_IS_VALID(fFlags, 0), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
+
+    /*
+     * Parse the path, assume current directory is root since we've got no
+     * caller context here.  Then traverse to the parent directory.
+     */
+    PRTVFSPARSEDPATH pPath;
+    int rc = RTVfsParsePathA(pszPath, "/", &pPath);
+    if (RT_SUCCESS(rc))
+    {
+        RTVFSDIRINTERNAL *pVfsParentDir;
+        if (pPath->cComponents > 0)
+        {
+            rc = rtVfsTraverseToParent(pThis, pPath, fFlags, &pVfsParentDir);
+            if (RT_SUCCESS(rc))
+            {
+                /*
+                 * Call the query method on the parent directory.
+                 */
+                /** @todo race condition here :/ */
+                const char *pszEntryName = &pPath->szPath[pPath->aoffComponents[pPath->cComponents - 1]];
+                RTVfsLockAcquireRead(pVfsParentDir->Base.hLock);
+                rc = pVfsParentDir->pOps->pfnQueryEntryInfo(pVfsParentDir->Base.pvThis, pszEntryName, pObjInfo, enmAddAttr);
+                RTVfsLockReleaseRead(pVfsParentDir->Base.hLock);
+
+                RTVfsDirRelease(pVfsParentDir);
+            }
+        }
+        /*
+         * The path boils down to '.', open the root dir and query its info.
+         */
+        else
+        {
+            RTVfsLockAcquireRead(pThis->Base.hLock);
+            RTVFSDIR hRootDir = NIL_RTVFSDIR;
+            rc = pThis->pOps->pfnOpenRoot(pThis->Base.pvThis, &hRootDir);
+            RTVfsLockReleaseRead(pThis->Base.hLock);
+            if (RT_SUCCESS(rc))
+            {
+                RTVfsLockAcquireRead(hRootDir->Base.hLock);
+                rc = hRootDir->Base.pOps->pfnQueryInfo(hRootDir->Base.pvThis, pObjInfo, enmAddAttr);
+                RTVfsLockReleaseRead(hRootDir->Base.hLock);
+                RTVfsDirRelease(hRootDir);
+            }
+        }
+
+        RTVfsParsePathFree(pPath);
+    }
+    return rc;
+}
+
+
 
 RTDECL(int) RTVfsIsRangeInUse(RTVFS hVfs, uint64_t off, size_t cb, bool *pfUsed)
@@ -2073,26 +2138,38 @@
     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 (pPath->cComponents > 0)
+        {
+            /*
+             * 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, RTPATH_F_FOLLOW_LINK, &pVfsParentDir);
             if (RT_SUCCESS(rc))
             {
-                AssertPtr(*phVfsDir);
-                Assert((*phVfsDir)->uMagic == RTVFSDIR_MAGIC);
+                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);
+                }
             }
+        }
+        /*
+         * If the path boils down to '.' return the root directory.
+         */
+        else
+        {
+            RTVfsLockAcquireRead(pThis->Base.hLock);
+            rc = pThis->pOps->pfnOpenRoot(pThis->Base.pvThis, phVfsDir);
+            RTVfsLockReleaseRead(pThis->Base.hLock);
         }
         RTVfsParsePathFree(pPath);
@@ -2121,26 +2198,40 @@
     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 (pPath->cComponents > 0)
+        {
+            /*
+             * 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, RTPATH_F_FOLLOW_LINK, &pVfsParentDir);
             if (RT_SUCCESS(rc))
             {
-                AssertPtr(*phVfsDir);
-                Assert((*phVfsDir)->uMagic == RTVFSDIR_MAGIC);
+                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);
+                }
             }
+        }
+        /*
+         * The path boils down to '.', call pfnOpenDir on pThis with '.' as input.
+         * The caller may wish for a new directory instance to enumerate the entries
+         * in parallel or some such thing.
+         */
+        else
+        {
+            RTVfsLockAcquireWrite(pThis->Base.hLock);
+            rc = pThis->pOps->pfnOpenDir(pThis->Base.pvThis, ".", fFlags, phVfsDir);
+            RTVfsLockReleaseWrite(pThis->Base.hLock);
         }
         RTVfsParsePathFree(pPath);
@@ -2173,5 +2264,6 @@
     if (RT_SUCCESS(rc))
     {
-        if (!pPath->fDirSlash)
+        if (   !pPath->fDirSlash
+            && pPath->cComponents > 0)
         {
             /*
@@ -2180,5 +2272,5 @@
              */
             RTVFSDIRINTERNAL *pVfsParentDir;
-            rc = rtVfsDirTraverseToParent(pThis, pPath, true /*fFollowSymlink*/, &pVfsParentDir);
+            rc = rtVfsDirTraverseToParent(pThis, pPath, RTPATH_F_FOLLOW_LINK, &pVfsParentDir);
             if (RT_SUCCESS(rc))
             {
@@ -2200,7 +2292,97 @@
         }
         else
-            rc = VERR_INVALID_PARAMETER;
+            rc = VERR_NOT_A_FILE;
         RTVfsParsePathFree(pPath);
     }
+    return rc;
+}
+
+
+RTDECL(int) RTVfsDirQueryPathInfo(RTVFSDIR hVfsDir, const char *pszPath, PRTFSOBJINFO pObjInfo,
+                                  RTFSOBJATTRADD enmAddAttr, uint32_t fFlags)
+{
+    /*
+     * Validate input.
+     */
+    RTVFSDIRINTERNAL *pThis = hVfsDir;
+    AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+    AssertReturn(pThis->uMagic == RTVFSDIR_MAGIC, VERR_INVALID_HANDLE);
+    AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
+    AssertReturn(*pszPath, VERR_INVALID_PARAMETER);
+    AssertPtrReturn(pObjInfo, VERR_INVALID_POINTER);
+    AssertReturn(enmAddAttr >= RTFSOBJATTRADD_NOTHING &&  enmAddAttr <= RTFSOBJATTRADD_LAST, VERR_INVALID_PARAMETER);
+    AssertMsgReturn(RTPATH_F_IS_VALID(fFlags, 0), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
+
+    /*
+     * Parse the path, assume current directory is root since we've got no
+     * caller context here.  Then traverse to the parent directory.
+     */
+    PRTVFSPARSEDPATH pPath;
+    int rc = RTVfsParsePathA(pszPath, "/", &pPath);
+    if (RT_SUCCESS(rc))
+    {
+        if (pPath->cComponents > 0)
+        {
+            RTVFSDIRINTERNAL *pVfsParentDir;
+            rc = rtVfsDirTraverseToParent(pThis, pPath, fFlags, &pVfsParentDir);
+            if (RT_SUCCESS(rc))
+            {
+                /*
+                 * Call the query method on the parent directory.
+                 */
+                /** @todo symlink race condition here :/ */
+                const char *pszEntryName = &pPath->szPath[pPath->aoffComponents[pPath->cComponents - 1]];
+                RTVfsLockAcquireRead(pVfsParentDir->Base.hLock);
+                rc = pVfsParentDir->pOps->pfnQueryEntryInfo(pVfsParentDir->Base.pvThis, pszEntryName, pObjInfo, enmAddAttr);
+                RTVfsLockReleaseRead(pVfsParentDir->Base.hLock);
+
+                RTVfsDirRelease(pVfsParentDir);
+            }
+            else
+                rc = VERR_INVALID_PARAMETER;
+        }
+        /*
+         * The path boils down to '.' so just query the directory.
+         */
+        else
+        {
+            RTVfsLockAcquireRead(pThis->Base.hLock);
+            rc = pThis->Base.pOps->pfnQueryInfo(pThis->Base.pvThis, pObjInfo, enmAddAttr);
+            RTVfsLockReleaseRead(pThis->Base.hLock);
+        }
+        RTVfsParsePathFree(pPath);
+    }
+    return rc;
+}
+
+
+RTDECL(int) RTVfsDirReadEx(RTVFSDIR hVfsDir, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry, RTFSOBJATTRADD enmAddAttr)
+{
+    /*
+     * Validate input.
+     */
+    RTVFSDIRINTERNAL *pThis = hVfsDir;
+    AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+    AssertReturn(pThis->uMagic == RTVFSDIR_MAGIC, VERR_INVALID_HANDLE);
+    AssertPtrReturn(pDirEntry, VERR_INVALID_POINTER);
+    AssertReturn(enmAddAttr >= RTFSOBJATTRADD_NOTHING && enmAddAttr <= RTFSOBJATTRADD_LAST, VERR_INVALID_PARAMETER);
+
+    size_t cbDirEntry = sizeof(*pDirEntry);
+    if (!pcbDirEntry)
+        pcbDirEntry = &cbDirEntry;
+    else
+    {
+        cbDirEntry = *pcbDirEntry;
+        AssertMsgReturn(cbDirEntry >= RT_UOFFSETOF(RTDIRENTRYEX, szName[2]),
+                        ("Invalid *pcbDirEntry=%d (min %d)\n", *pcbDirEntry, RT_OFFSETOF(RTDIRENTRYEX, szName[2])),
+                        VERR_INVALID_PARAMETER);
+    }
+
+    /*
+     * Call the directory method.
+     */
+    RTVfsLockAcquireRead(pThis->Base.hLock);
+    int rc = pThis->pOps->pfnReadDir(pThis->Base.pvThis, pDirEntry, pcbDirEntry, enmAddAttr);
+    RTVfsLockReleaseRead(pThis->Base.hLock);
     return rc;
 }
@@ -2893,5 +3075,6 @@
     if (RT_SUCCESS(rc))
     {
-        if (!pPath->fDirSlash)
+        if (   !pPath->fDirSlash
+            && pPath->cComponents > 0)
         {
             /*
@@ -2900,5 +3083,5 @@
              */
             RTVFSDIRINTERNAL *pVfsParentDir;
-            rc = rtVfsTraverseToParent(pThis, pPath, true /*fFollowSymlink*/, &pVfsParentDir);
+            rc = rtVfsTraverseToParent(pThis, pPath, RTPATH_F_FOLLOW_LINK, &pVfsParentDir);
             if (RT_SUCCESS(rc))
             {
@@ -2920,5 +3103,5 @@
         }
         else
-            rc = VERR_INVALID_PARAMETER;
+            rc = VERR_NOT_A_FILE;
         RTVfsParsePathFree(pPath);
     }
Index: trunk/src/VBox/Runtime/common/vfs/vfschain.cpp
===================================================================
--- trunk/src/VBox/Runtime/common/vfs/vfschain.cpp	(revision 66742)
+++ trunk/src/VBox/Runtime/common/vfs/vfschain.cpp	(revision 66762)
@@ -430,5 +430,24 @@
 
 /**
- * Duplicate a spec string.
+ * Checks if @a ch is a character that can be escaped.
+ *
+ * @returns true / false.
+ * @param   ch          The character to consider.
+ */
+DECLINLINE(bool) rtVfsChainSpecIsEscapableChar(char ch)
+{
+    return ch == '('
+        || ch == ')'
+        || ch == '{'
+        || ch == '}'
+        || ch == '\\'
+        || ch == ','
+        || ch == '|'
+        || ch == ':';
+}
+
+
+/**
+ * Duplicate a spec string after unescaping it.
  *
  * This differs from RTStrDupN in that it uses RTMemTmpAlloc instead of
@@ -462,5 +481,5 @@
                 {
                     char ch2 = psz[2];
-                    if (ch2 == '(' || ch2 == ')' || ch2 == '\\' || ch2 == ',')
+                    if (rtVfsChainSpecIsEscapableChar(ch2))
                     {
                         psz++;
@@ -589,4 +608,45 @@
 
 /**
+ * Checks if @a psz is pointing to the final element specification.
+ *
+ * @returns true / false.
+ * @param   psz         Start of an element or path.
+ * @param   pcch        Where to return the length.
+ */
+static bool rtVfsChainSpecIsFinalElement(const char *psz, size_t *pcch)
+{
+    size_t off = 0;
+    char   ch;
+    while ((ch = psz[off]) != '\0')
+    {
+        if (ch == '|' || ch == ':')
+            return false;
+        if (   ch == '\\'
+            && rtVfsChainSpecIsEscapableChar(psz[off + 1]))
+            off++;
+        off++;
+    }
+    *pcch = off;
+    return off > 0;
+}
+
+
+/**
+ * Makes the final path element.
+ * @returns IPRT status code
+ * @param   pElement    The element.
+ * @param   pszPath     The path.
+ * @param   cchPath     The path length.
+ */
+static int rtVfsChainSpecMakeFinalPathElement(PRTVFSCHAINELEMSPEC pElement, const char *pszPath, size_t cchPath)
+{
+    pElement->pszProvider = NULL;
+    pElement->enmType     = RTVFSOBJTYPE_END;
+    pElement->cchSpec     = (uint16_t)cchPath;
+    return rtVfsChainSpecElementAddArg(pElement, pszPath, cchPath, pElement->offSpec);
+}
+
+
+/**
  * Finds the end of the argument string.
  *
@@ -596,14 +656,13 @@
 static size_t rtVfsChainSpecFindArgEnd(const char *psz)
 {
-    char ch;
     size_t off = 0;
+    char   ch;
     while (  (ch = psz[off]) != '\0'
            && ch != ','
            && ch != ')'
-           && ch != '(')
-    {
-        /* check for escape sequences. */
+           && ch != '}')
+    {
         if (   ch == '\\'
-            && (psz[off+1] == '(' || psz[off+1] == ')' || psz[off+1] == '\\' || psz[off+1] == ','))
+            && rtVfsChainSpecIsEscapableChar(psz[off+1]))
             off++;
         off++;
@@ -674,6 +733,7 @@
 
         /*
-         * First up is the VFS object type followed by a parentheses,
-         * or this could be the trailing action.
+         * First up is the VFS object type followed by a parenthesis/curly, or
+         * this could be the trailing action.  Alternatively, we could have a
+         * final path-only element here.
          */
         size_t cch;
@@ -694,5 +754,7 @@
         else
         {
-            if (*pszSrc == '\0')
+            if (rtVfsChainSpecIsFinalElement(pszSrc, &cch))
+                rc = rtVfsChainSpecMakeFinalPathElement(pElement, pszSrc, cch);
+            else if (*pszSrc == '\0')
                 rc = VERR_VFS_CHAIN_TRAILING_SEPARATOR;
             else
@@ -700,13 +762,17 @@
             break;
         }
-        pszSrc += cch;
-
-        /* Check and skip the parentheses. */
-        if (*pszSrc != '(')
-        {
-            rc = VERR_VFS_CHAIN_EXPECTED_LEFT_PARENTHESES;
+
+        /* Check and skip past the parenthesis/curly.  If not there, we might
+           have a final path element at our hands. */
+        char const chOpenParen = pszSrc[cch];
+        if (chOpenParen != '(' && chOpenParen != '{')
+        {
+            if (rtVfsChainSpecIsFinalElement(pszSrc, &cch))
+                rc = rtVfsChainSpecMakeFinalPathElement(pElement, pszSrc, cch);
+            else
+                rc = VERR_VFS_CHAIN_EXPECTED_LEFT_PARENTHESES;
             break;
         }
-        pszSrc = RTStrStripL(pszSrc + 1);
+        pszSrc = RTStrStripL(pszSrc + cch + 1);
 
         /*
@@ -739,6 +805,6 @@
             break;
 
-        /* Must end with a right parentheses. */
-        if (*pszSrc != ')')
+        /* Must end with a right parentheses/curly. */
+        if (*pszSrc != (chOpenParen == '(' ? ')' : '}'))
         {
             rc = VERR_VFS_CHAIN_EXPECTED_RIGHT_PARENTHESES;
@@ -835,5 +901,5 @@
 
 RTDECL(int) RTVfsChainSpecCheckAndSetup(PRTVFSCHAINSPEC pSpec, PCRTVFSCHAINSPEC pReuseSpec,
-                                        PRTVFSOBJ phVfsObj, uint32_t *poffError, PRTERRINFO pErrInfo)
+                                        PRTVFSOBJ phVfsObj, const char **ppszFinalPath, uint32_t *poffError, PRTERRINFO pErrInfo)
 {
     AssertPtrReturn(poffError, VERR_INVALID_POINTER);
@@ -841,6 +907,28 @@
     AssertPtrReturn(phVfsObj, VERR_INVALID_POINTER);
     *phVfsObj = NIL_RTVFSOBJ;
+    AssertPtrReturn(ppszFinalPath, VERR_INVALID_POINTER);
+    *ppszFinalPath = NULL;
     AssertPtrReturn(pSpec, VERR_INVALID_POINTER);
     AssertPtrNullReturn(pErrInfo, VERR_INVALID_POINTER);
+
+    /*
+     * Check for final path-only component as we will not touch it yet.
+     */
+    uint32_t cElements = pSpec->cElements;
+    if (cElements > 0)
+    {
+        if (pSpec->paElements[pSpec->cElements - 1].enmType == RTVFSOBJTYPE_END)
+        {
+            if (cElements > 1)
+                cElements--;
+            else
+            {
+                *ppszFinalPath = pSpec->paElements[0].paArgs[0].psz;
+                return VERR_VFS_CHAIN_PATH_ONLY;
+            }
+        }
+    }
+    else
+        return VERR_VFS_CHAIN_EMPTY;
 
     /*
@@ -855,5 +943,5 @@
          * Resolve and check each element first.
          */
-        for (uint32_t i = 0; i < pSpec->cElements; i++)
+        for (uint32_t i = 0; i < cElements; i++)
         {
             PRTVFSCHAINELEMSPEC const pElement = &pSpec->paElements[i];
@@ -876,8 +964,9 @@
         if (RT_SUCCESS(rc))
         {
-            if (pSpec->cElements > 0) /* paranoia */
-            {
-                PRTVFSCHAINELEMSPEC const pLast = &pSpec->paElements[pSpec->cElements - 1];
+            PRTVFSCHAINELEMSPEC const pLast = &pSpec->paElements[cElements - 1];
+            if (cElements == pSpec->cElements)
+            {
                 if (   pLast->enmType == pSpec->enmDesiredType
+                    || pSpec->enmDesiredType == RTVFSOBJTYPE_BASE
                     || (   pLast->enmType == RTVFSOBJTYPE_FILE
                         && pSpec->enmDesiredType == RTVFSOBJTYPE_IO_STREAM) )
@@ -889,6 +978,14 @@
                 }
             }
+            /* Ends with a path-only element, so check the type of the element preceding it. */
+            else if (   pLast->enmType == RTVFSOBJTYPE_DIR
+                     || pLast->enmType == RTVFSOBJTYPE_VFS
+                     || pLast->enmType == RTVFSOBJTYPE_FS_STREAM)
+                rc = VINF_SUCCESS;
             else
-                rc = VERR_VFS_CHAIN_EMPTY;
+            {
+                *poffError = pLast->offSpec;
+                rc = VERR_VFS_CHAIN_TYPE_MISMATCH_PATH_ONLY;
+            }
         }
 
@@ -899,5 +996,5 @@
              */
             RTVFSOBJ hPrevVfsObj = NIL_RTVFSOBJ; /* No extra reference, kept in chain structure. */
-            for (uint32_t i = 0; i < pSpec->cElements; i++)
+            for (uint32_t i = 0; i < cElements; i++)
             {
                 PRTVFSCHAINELEMSPEC const pElement = &pSpec->paElements[i];
@@ -956,5 +1053,6 @@
                 uint32_t cRefs = RTVfsObjRetain(hPrevVfsObj);
                 AssertStmt(cRefs != UINT32_MAX, rc = VERR_VFS_CHAIN_IPE);
-                *phVfsObj = hPrevVfsObj;
+                *phVfsObj      = hPrevVfsObj;
+                *ppszFinalPath = cElements == pSpec->cElements ? NULL : pSpec->paElements[cElements].paArgs[0].psz;
             }
         }
@@ -1009,4 +1107,90 @@
         RTCritSectRwLeaveExcl(&g_rtVfsChainElementCritSect);
     return rc;
+}
+
+
+RTDECL(int) RTVfsChainOpenDir(const char *pszSpec, uint32_t fOpen,
+                              PRTVFSDIR phVfsDir, uint32_t *poffError, PRTERRINFO pErrInfo)
+{
+    uint32_t offErrorIgn;
+    if (!poffError)
+        poffError = &offErrorIgn;
+    *poffError = 0;
+    AssertPtrReturn(pszSpec, VERR_INVALID_POINTER);
+    AssertReturn(*pszSpec != '\0', VERR_INVALID_PARAMETER);
+    AssertPtrReturn(phVfsDir, VERR_INVALID_POINTER);
+    AssertPtrNullReturn(pErrInfo, VERR_INVALID_POINTER);
+
+    /*
+     * Try for a VFS chain first, falling back on regular file system stuff if it's just a path.
+     */
+    int rc;
+    PRTVFSCHAINSPEC pSpec = NULL;
+    if (strncmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX, sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1) == 0)
+    {
+        rc = RTVfsChainSpecParse(pszSpec,  0 /*fFlags*/, RTVFSOBJTYPE_DIR, &pSpec, poffError);
+        if (RT_FAILURE(rc))
+            return rc;
+
+        Assert(pSpec->cElements > 0);
+        if (   pSpec->cElements > 1
+            || pSpec->paElements[0].enmType != RTVFSOBJTYPE_END)
+        {
+
+            const char *pszFinal = NULL;
+            RTVFSOBJ    hVfsObj  = NIL_RTVFSOBJ;
+            pSpec->fOpenFile = fOpen;
+            rc = RTVfsChainSpecCheckAndSetup(pSpec, NULL /*pReuseSpec*/, &hVfsObj, &pszFinal, poffError, pErrInfo);
+            if (RT_SUCCESS(rc))
+            {
+                if (!pszFinal)
+                {
+                    /* Try convert it to a file object and we're done. */
+                    *phVfsDir = RTVfsObjToDir(hVfsObj);
+                    if (*phVfsDir)
+                        rc = VINF_SUCCESS;
+                    else
+                        rc = VERR_VFS_CHAIN_CAST_FAILED;
+                }
+                else
+                {
+                    /*
+                     * Do a file open with the final path on the returned object.
+                     */
+                    RTVFS           hVfs    = RTVfsObjToVfs(hVfsObj);
+                    RTVFSDIR        hVfsDir = RTVfsObjToDir(hVfsObj);
+                    RTVFSFSSTREAM   hVfsFss = RTVfsObjToFsStream(hVfsObj);
+                    if (hVfs != NIL_RTVFS)
+                        rc = RTVfsDirOpen(hVfs, pszFinal, fOpen, phVfsDir);
+                    else if (hVfsDir != NIL_RTVFSDIR)
+                        rc = RTVfsDirOpenDir(hVfsDir, pszFinal, fOpen, phVfsDir);
+                    else if (hVfsFss != NIL_RTVFSFSSTREAM)
+                        rc = VERR_NOT_IMPLEMENTED;
+                    else
+                        rc = VERR_VFS_CHAIN_TYPE_MISMATCH_PATH_ONLY;
+                    RTVfsRelease(hVfs);
+                    RTVfsDirRelease(hVfsDir);
+                    RTVfsFsStrmRelease(hVfsFss);
+                }
+                RTVfsObjRelease(hVfsObj);
+            }
+
+            RTVfsChainSpecFree(pSpec);
+            return rc;
+        }
+
+        /* Only a path element. */
+        pszSpec = pSpec->paElements[0].paArgs[0].psz;
+    }
+
+    /*
+     * Path to regular file system.
+     */
+    /** @todo implement system specific standard VFS directory class. */
+    rc = VERR_NOT_IMPLEMENTED;
+
+    RTVfsChainSpecFree(pSpec);
+    return rc;
+
 }
 
@@ -1025,44 +1209,81 @@
 
     /*
-     * If it's not a VFS chain spec, treat it as a file.
+     * Try for a VFS chain first, falling back on regular file system stuff if it's just a path.
      */
     int rc;
-    if (strncmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX, sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1))
-    {
-        RTFILE hFile;
-        rc = RTFileOpen(&hFile, pszSpec, fOpen);
+    PRTVFSCHAINSPEC pSpec = NULL;
+    if (strncmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX, sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1) == 0)
+    {
+        rc = RTVfsChainSpecParse(pszSpec,  0 /*fFlags*/, RTVFSOBJTYPE_FILE, &pSpec, poffError);
+        if (RT_FAILURE(rc))
+            return rc;
+
+        Assert(pSpec->cElements > 0);
+        if (   pSpec->cElements > 1
+            || pSpec->paElements[0].enmType != RTVFSOBJTYPE_END)
+        {
+
+            const char *pszFinal = NULL;
+            RTVFSOBJ    hVfsObj  = NIL_RTVFSOBJ;
+            pSpec->fOpenFile = fOpen;
+            rc = RTVfsChainSpecCheckAndSetup(pSpec, NULL /*pReuseSpec*/, &hVfsObj, &pszFinal, poffError, pErrInfo);
+            if (RT_SUCCESS(rc))
+            {
+                if (!pszFinal)
+                {
+                    /* Try convert it to a file object and we're done. */
+                    *phVfsFile = RTVfsObjToFile(hVfsObj);
+                    if (*phVfsFile)
+                        rc = VINF_SUCCESS;
+                    else
+                        rc = VERR_VFS_CHAIN_CAST_FAILED;
+                }
+                else
+                {
+                    /*
+                     * Do a file open with the final path on the returned object.
+                     */
+                    RTVFS           hVfs    = RTVfsObjToVfs(hVfsObj);
+                    RTVFSDIR        hVfsDir = RTVfsObjToDir(hVfsObj);
+                    RTVFSFSSTREAM   hVfsFss = RTVfsObjToFsStream(hVfsObj);
+                    if (hVfs != NIL_RTVFS)
+                        rc = RTVfsFileOpen(hVfs, pszFinal, fOpen, phVfsFile);
+                    else if (hVfsDir != NIL_RTVFSDIR)
+                        rc = RTVfsDirOpenFile(hVfsDir, pszFinal, fOpen, phVfsFile);
+                    else if (hVfsFss != NIL_RTVFSFSSTREAM)
+                        rc = VERR_NOT_IMPLEMENTED;
+                    else
+                        rc = VERR_VFS_CHAIN_TYPE_MISMATCH_PATH_ONLY;
+                    RTVfsRelease(hVfs);
+                    RTVfsDirRelease(hVfsDir);
+                    RTVfsFsStrmRelease(hVfsFss);
+                }
+                RTVfsObjRelease(hVfsObj);
+            }
+
+            RTVfsChainSpecFree(pSpec);
+            return rc;
+        }
+
+        /* Only a path element. */
+        pszSpec = pSpec->paElements[0].paArgs[0].psz;
+    }
+
+    /*
+     * Path to regular file system.
+     */
+    RTFILE hFile;
+    rc = RTFileOpen(&hFile, pszSpec, fOpen);
+    if (RT_SUCCESS(rc))
+    {
+        RTVFSFILE hVfsFile;
+        rc = RTVfsFileFromRTFile(hFile, fOpen, false /*fLeaveOpen*/, &hVfsFile);
         if (RT_SUCCESS(rc))
-        {
-            RTVFSFILE hVfsFile;
-            rc = RTVfsFileFromRTFile(hFile, fOpen, false /*fLeaveOpen*/, &hVfsFile);
-            if (RT_SUCCESS(rc))
-                *phVfsFile = hVfsFile;
-            else
-                RTFileClose(hFile);
-        }
-    }
-    else
-    {
-        PRTVFSCHAINSPEC pSpec;
-        rc = RTVfsChainSpecParse(pszSpec,  0 /*fFlags*/, RTVFSOBJTYPE_FILE, &pSpec, poffError);
-        if (RT_SUCCESS(rc))
-        {
-            pSpec->fOpenFile = fOpen;
-
-            RTVFSOBJ hVfsObj  = NIL_RTVFSOBJ;
-            rc = RTVfsChainSpecCheckAndSetup(pSpec, NULL /*pReuseSpec*/, &hVfsObj, poffError, pErrInfo);
-            if (RT_SUCCESS(rc))
-            {
-                *phVfsFile = RTVfsObjToFile(hVfsObj);
-                if (*phVfsFile)
-                    rc = VINF_SUCCESS;
-                else
-                    rc = VERR_VFS_CHAIN_CAST_FAILED;
-                RTVfsObjRelease(hVfsObj);
-            }
-
-            RTVfsChainSpecFree(pSpec);
-        }
-    }
+            *phVfsFile = hVfsFile;
+        else
+            RTFileClose(hFile);
+    }
+
+    RTVfsChainSpecFree(pSpec);
     return rc;
 }
@@ -1082,52 +1303,183 @@
 
     /*
-     * If it's not a VFS chain spec, treat it as a file.
+     * Try for a VFS chain first, falling back on regular file system stuff if it's just a path.
      */
     int rc;
-    if (strncmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX, sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1))
-    {
-        RTFILE hFile;
-        rc = RTFileOpen(&hFile, pszSpec, fOpen);
+    PRTVFSCHAINSPEC pSpec = NULL;
+    if (strncmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX, sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1) == 0)
+    {
+        rc = RTVfsChainSpecParse(pszSpec,  0 /*fFlags*/, RTVFSOBJTYPE_IO_STREAM, &pSpec, poffError);
+        if (RT_FAILURE(rc))
+            return rc;
+
+        Assert(pSpec->cElements > 0);
+        if (   pSpec->cElements > 1
+            || pSpec->paElements[0].enmType != RTVFSOBJTYPE_END)
+        {
+
+            const char *pszFinal = NULL;
+            RTVFSOBJ    hVfsObj  = NIL_RTVFSOBJ;
+            pSpec->fOpenFile = fOpen;
+            rc = RTVfsChainSpecCheckAndSetup(pSpec, NULL /*pReuseSpec*/, &hVfsObj, &pszFinal, poffError, pErrInfo);
+            if (RT_SUCCESS(rc))
+            {
+                if (!pszFinal)
+                {
+                    /* Try convert it to an I/O object and we're done. */
+                    *phVfsIos = RTVfsObjToIoStream(hVfsObj);
+                    if (*phVfsIos)
+                        rc = VINF_SUCCESS;
+                    else
+                        rc = VERR_VFS_CHAIN_CAST_FAILED;
+                }
+                else
+                {
+                    /*
+                     * Do a file open with the final path on the returned object.
+                     */
+                    RTVFS           hVfs    = RTVfsObjToVfs(hVfsObj);
+                    RTVFSDIR        hVfsDir = RTVfsObjToDir(hVfsObj);
+                    RTVFSFSSTREAM   hVfsFss = RTVfsObjToFsStream(hVfsObj);
+                    RTVFSFILE       hVfsFile = NIL_RTVFSFILE;
+                    if (hVfs != NIL_RTVFS)
+                        rc = RTVfsFileOpen(hVfs, pszFinal, fOpen, &hVfsFile);
+                    else if (hVfsDir != NIL_RTVFSDIR)
+                        rc = RTVfsDirOpenFile(hVfsDir, pszFinal, fOpen, &hVfsFile);
+                    else if (hVfsFss != NIL_RTVFSFSSTREAM)
+                        rc = VERR_NOT_IMPLEMENTED;
+                    else
+                        rc = VERR_VFS_CHAIN_TYPE_MISMATCH_PATH_ONLY;
+                    if (RT_SUCCESS(rc))
+                    {
+                        *phVfsIos = RTVfsFileToIoStream(hVfsFile);
+                        if (*phVfsIos)
+                            rc = VINF_SUCCESS;
+                        else
+                            rc = VERR_VFS_CHAIN_CAST_FAILED;
+                        RTVfsFileRelease(hVfsFile);
+                    }
+                    RTVfsRelease(hVfs);
+                    RTVfsDirRelease(hVfsDir);
+                    RTVfsFsStrmRelease(hVfsFss);
+                }
+                RTVfsObjRelease(hVfsObj);
+            }
+
+            RTVfsChainSpecFree(pSpec);
+            return rc;
+        }
+
+        /* Only a path element. */
+        pszSpec = pSpec->paElements[0].paArgs[0].psz;
+    }
+
+    /*
+     * Path to regular file system.
+     */
+    RTFILE hFile;
+    rc = RTFileOpen(&hFile, pszSpec, fOpen);
+    if (RT_SUCCESS(rc))
+    {
+        RTVFSFILE hVfsFile;
+        rc = RTVfsFileFromRTFile(hFile, fOpen, false /*fLeaveOpen*/, &hVfsFile);
         if (RT_SUCCESS(rc))
         {
-            RTVFSFILE hVfsFile;
-            rc = RTVfsFileFromRTFile(hFile, fOpen, false /*fLeaveOpen*/, &hVfsFile);
+            *phVfsIos = RTVfsFileToIoStream(hVfsFile);
+            RTVfsFileRelease(hVfsFile);
+        }
+        else
+            RTFileClose(hFile);
+    }
+
+    RTVfsChainSpecFree(pSpec);
+    return rc;
+}
+
+
+/**
+ * The equivalent of RTPathQueryInfoEx
+ */
+RTDECL(int) RTVfsChainQueryInfo(const char *pszSpec, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs,
+                                uint32_t fFlags, uint32_t *poffError, PRTERRINFO pErrInfo)
+{
+    uint32_t offErrorIgn;
+    if (!poffError)
+        poffError = &offErrorIgn;
+    *poffError = 0;
+    AssertPtrReturn(pszSpec, VERR_INVALID_POINTER);
+    AssertReturn(*pszSpec != '\0', VERR_INVALID_PARAMETER);
+    AssertPtrReturn(pObjInfo, VERR_INVALID_POINTER);
+    AssertReturn(enmAdditionalAttribs >= RTFSOBJATTRADD_NOTHING && enmAdditionalAttribs <= RTFSOBJATTRADD_LAST,
+                 VERR_INVALID_PARAMETER);
+    AssertPtrNullReturn(pErrInfo, VERR_INVALID_POINTER);
+
+    /*
+     * Try for a VFS chain first, falling back on regular file system stuff if it's just a path.
+     */
+    int rc;
+    PRTVFSCHAINSPEC pSpec = NULL;
+    if (strncmp(pszSpec, RTVFSCHAIN_SPEC_PREFIX, sizeof(RTVFSCHAIN_SPEC_PREFIX) - 1) == 0)
+    {
+        rc = RTVfsChainSpecParse(pszSpec,  0 /*fFlags*/, RTVFSOBJTYPE_BASE, &pSpec, poffError);
+        if (RT_FAILURE(rc))
+            return rc;
+
+        Assert(pSpec->cElements > 0);
+        if (   pSpec->cElements > 1
+            || pSpec->paElements[0].enmType != RTVFSOBJTYPE_END)
+        {
+
+            const char *pszFinal = NULL;
+            RTVFSOBJ    hVfsObj  = NIL_RTVFSOBJ;
+            pSpec->fOpenFile = RTFILE_O_READ | RTFILE_O_OPEN;
+            rc = RTVfsChainSpecCheckAndSetup(pSpec, NULL /*pReuseSpec*/, &hVfsObj, &pszFinal, poffError, pErrInfo);
             if (RT_SUCCESS(rc))
             {
-                *phVfsIos = RTVfsFileToIoStream(hVfsFile);
-                RTVfsFileRelease(hVfsFile);
-            }
-            else
-                RTFileClose(hFile);
-        }
-    }
-    /*
-     * Do the chain thing.
-     */
-    else
-    {
-        PRTVFSCHAINSPEC pSpec;
-        rc = RTVfsChainSpecParse(pszSpec, 0 /*fFlags*/, RTVFSOBJTYPE_IO_STREAM, &pSpec, poffError);
-        if (RT_SUCCESS(rc))
-        {
-            pSpec->fOpenFile = fOpen;
-
-            RTVFSOBJ hVfsObj  = NIL_RTVFSOBJ;
-            rc = RTVfsChainSpecCheckAndSetup(pSpec, NULL /*pReuseSpec*/, &hVfsObj, poffError, pErrInfo);
-            if (RT_SUCCESS(rc))
-            {
-                *phVfsIos = RTVfsObjToIoStream(hVfsObj);
-                if (*phVfsIos)
-                    rc = VINF_SUCCESS;
+                if (!pszFinal)
+                {
+                    /*
+                     * Do the job on the final object.
+                     */
+                    rc = RTVfsObjQueryInfo(hVfsObj, pObjInfo, enmAdditionalAttribs);
+                }
                 else
-                    rc = VERR_VFS_CHAIN_CAST_FAILED;
+                {
+                    /*
+                     * Do a path query operation on the penultimate object.
+                     */
+                    RTVFS           hVfs    = RTVfsObjToVfs(hVfsObj);
+                    RTVFSDIR        hVfsDir = RTVfsObjToDir(hVfsObj);
+                    RTVFSFSSTREAM   hVfsFss = RTVfsObjToFsStream(hVfsObj);
+                    if (hVfs != NIL_RTVFS)
+                        rc = RTVfsQueryPathInfo(hVfs, pszFinal, pObjInfo, enmAdditionalAttribs, fFlags);
+                    else if (hVfsDir != NIL_RTVFSDIR)
+                        rc = RTVfsDirQueryPathInfo(hVfsDir, pszFinal, pObjInfo, enmAdditionalAttribs, fFlags);
+                    else if (hVfsFss != NIL_RTVFSFSSTREAM)
+                        rc = VERR_NOT_SUPPORTED;
+                    else
+                        rc = VERR_VFS_CHAIN_TYPE_MISMATCH_PATH_ONLY;
+                    RTVfsRelease(hVfs);
+                    RTVfsDirRelease(hVfsDir);
+                    RTVfsFsStrmRelease(hVfsFss);
+                }
                 RTVfsObjRelease(hVfsObj);
             }
+
             RTVfsChainSpecFree(pSpec);
-        }
-    }
+            return rc;
+        }
+
+        /* Only a path element. */
+        pszSpec = pSpec->paElements[0].paArgs[0].psz;
+    }
+
+    /*
+     * Path to regular file system.
+     */
+    rc = RTPathQueryInfoEx(pszSpec, pObjInfo, enmAdditionalAttribs, fFlags);
+
+    RTVfsChainSpecFree(pSpec);
     return rc;
 }
-
 
 
