Index: /trunk/include/iprt/vfs.h
===================================================================
--- /trunk/include/iprt/vfs.h	(revision 33858)
+++ /trunk/include/iprt/vfs.h	(revision 33859)
@@ -120,11 +120,48 @@
                                        char *pszMountPoint, size_t cbMountPoint);
 
+
 /** @defgroup grp_vfs_dir           VFS Directory API
  * @{
  */
+
+/**
+ * Retains a reference to the VFS directory handle.
+ *
+ * @returns New reference count on success, UINT32_MAX on failure.
+ * @param   hVfsDir         The VFS directory handle.
+ */
+RTDECL(uint32_t)    RTVfsDirRetain(RTVFSDIR hVfsDir);
+
+/**
+ * Releases a reference to the VFS directory handle.
+ *
+ * @returns New reference count on success (0 if closed), UINT32_MAX on failure.
+ * @param   hVfsIos         The VFS directory handle.
+ */
+RTDECL(uint32_t)    RTVfsDirRelease(RTVFSDIR hVfsDir);
+
 /** @}  */
 
 
-/** @defgroup grp_vfs_iostream      VFS I/O Stream
+/** @defgroup grp_vfs_iostream      VFS Symbolic Link API
+ * @{
+ */
+
+/**
+ * Read the symbolic link target.
+ *
+ * @returns IPRT status code.
+ * @param   hVfsSym         The VFS symbolic link handle.
+ * @param   pszTarget       The target buffer.
+ * @param   cbTarget        The size of the target buffer.
+ * @sa      RTSymlinkRead
+ */
+RTDECL(int)         RTVfsSymlinkRead(RTVFSSYMLINK hVfsSym, char *pszTarget, size_t cbTarget);
+
+/** @}  */
+
+
+
+/** @defgroup grp_vfs_iostream      VFS I/O Stream API
  * @{
  */
@@ -264,4 +301,22 @@
  */
 RTDECL(RTFOFF)      RTVfsIoStrmTell(RTVFSIOSTREAM hVfsIos);
+
+/**
+ * Skips @a cb ahead in the stream.
+ *
+ * @returns IPRT status code.
+ * @param   hVfsIos         The VFS I/O stream handle.
+ * @param   cb              The number bytes to skip.
+ */
+RTDECL(int)         RTVfsIoStrmSkip(RTVFSIOSTREAM hVfsIos, RTFOFF cb);
+
+/**
+ * Fills the stream with @a cb zeros.
+ *
+ * @returns IPRT status code.
+ * @param   hVfsIos         The VFS I/O stream handle.
+ * @param   cb              The number of zero bytes to insert.
+ */
+RTDECL(int)         RTVfsIoStrmZeroFill(RTVFSIOSTREAM hVfsIos, RTFOFF cb);
 /** @} */
 
Index: /trunk/include/iprt/vfslowlevel.h
===================================================================
--- /trunk/include/iprt/vfslowlevel.h	(revision 33858)
+++ /trunk/include/iprt/vfslowlevel.h	(revision 33859)
@@ -28,4 +28,5 @@
 
 #include <iprt/vfs.h>
+#include <iprt/param.h>
 
 
@@ -87,5 +88,5 @@
 } RTVFSOPS;
 /** Pointer to constant VFS operations. */
-typedef RTVFSOPS const PCRTVFSOPS;
+typedef RTVFSOPS const *PCRTVFSOPS;
 
 /** The RTVFSOPS structure version. */
@@ -112,4 +113,6 @@
     /** File. */
     RTVFSOBJTYPE_FILE,
+    /** Symbolic link. */
+    RTVFSOBJTYPE_SYMLINK,
     /** End of valid object types. */
     RTVFSOBJTYPE_END,
@@ -242,4 +245,27 @@
 
     /**
+     * Opens a directory entry for traversal purposes.
+     *
+     * Method which sole purpose is helping the path traversal.  Only one of
+     * the three output variables will be set, the others will left untouched
+     * (caller sets them to NIL).
+     *
+     * @returns IPRT status code.
+     * @retval  VERR_PATH_NOT_FOUND if @a pszEntry was not found.
+     * @param   pvThis          The implementation specific directory data.
+     * @param   pszEntry        The name of the directory entry to remove.
+     * @param   phVfsDir        If not NULL and it is a directory, open it and
+     *                          return the handle here.
+     * @param   phVfsSymlink    If not NULL and it is a symbolic link, open it
+     *                          and return the handle here.
+     * @param   phVfsMounted    If not NULL and it is a mounted VFS directory,
+     *                          reference it and return the handle here.
+     * @todo    Should com dir, symlinks and mount points using some common
+     *          ancestor "class".
+     */
+    DECLCALLBACKMEMBER(int, pfnTraversalOpen)(void *pvThis, const char *pszEntry, PRTVFSDIR phVfsDir,
+                                              PRTVFSSYMLINK phVfsSymlink, PRTVFS phVfsMounted);
+
+    /**
      * Open or create a file.
      *
@@ -347,4 +373,41 @@
 /** The RTVFSDIROPS structure version. */
 #define RTVFSDIROPS_VERSION         RT_MAKE_U32_FROM_U8(0xff,0x3f,1,0)
+
+
+/**
+ * The symbolic link operations.
+ *
+ * @extends RTVFSOBJOPS
+ * @extends RTVFSOBJSETOPS
+ */
+typedef struct RTVFSSYMLINKOPS
+{
+    /** The basic object operation.  */
+    RTVFSOBJOPS             Obj;
+    /** The structure version (RTVFSSYMLINKOPS_VERSION). */
+    uint32_t                uVersion;
+    /** Reserved field, MBZ. */
+    uint32_t                fReserved;
+    /** The object setter operations. */
+    RTVFSOBJSETOPS          ObjSet;
+
+    /**
+     * Read the symbolic link target.
+     *
+     * @returns IPRT status code.
+     * @param   pvThis      The implementation specific symbolic link data.
+     * @param   pszTarget   The target buffer.
+     * @param   cbTarget    The size of the target buffer.
+     * @sa      RTSymlinkRead
+     */
+    DECLCALLBACKMEMBER(int, pfnRead)(void *pvThis, char *pszTarget, size_t cbTarget);
+
+    /** Marks the end of the structure (RTVFSSYMLINKOPS_VERSION). */
+    uintptr_t               uEndMarker;
+} RTVFSSYMLINKOPS;
+/** Pointer to const symbolic link operations. */
+typedef RTVFSSYMLINKOPS const *PCRTVFSSYMLINKOPS;
+/** The RTVFSSYMLINKOPS structure version. */
+#define RTVFSSYMLINKOPS_VERSION     RT_MAKE_U32_FROM_U8(0xff,0x4f,1,0)
 
 
@@ -432,4 +495,24 @@
     DECLCALLBACKMEMBER(int, pfnTell)(void *pvThis, PRTFOFF poffActual);
 
+    /**
+     * Skips @a cb ahead in the stream.
+     *
+     * @returns IPRT status code.
+     * @param   pvThis      The implementation specific file data.
+     * @param   cb          The number bytes to skip.
+     * @remarks This is optional and can be NULL.
+     */
+    DECLCALLBACKMEMBER(int, pfnSkip)(void *pvThis, RTFOFF cb);
+
+    /**
+     * Fills the stream with @a cb zeros.
+     *
+     * @returns IPRT status code.
+     * @param   pvThis      The implementation specific file data.
+     * @param   cb          The number of zero bytes to insert.
+     * @remarks This is optional and can be NULL.
+     */
+    DECLCALLBACKMEMBER(int, pfnZeroFill)(void *pvThis, RTFOFF cb);
+
     /** Marks the end of the structure (RTVFSIOSTREAMOPS_VERSION). */
     uintptr_t               uEndMarker;
@@ -439,5 +522,5 @@
 
 /** The RTVFSIOSTREAMOPS structure version. */
-#define RTVFSIOSTREAMOPS_VERSION    RT_MAKE_U32_FROM_U8(0xff,0x4f,1,0)
+#define RTVFSIOSTREAMOPS_VERSION    RT_MAKE_U32_FROM_U8(0xff,0x5f,1,0)
 
 
@@ -490,5 +573,5 @@
 
 /** The RTVFSFILEOPS structure version. */
-#define RTVFSFILEOPS_VERSION        RT_MAKE_U32_FROM_U8(0xff,0x5f,1,0)
+#define RTVFSFILEOPS_VERSION        RT_MAKE_U32_FROM_U8(0xff,0x6f,1,0)
 
 /**
@@ -509,4 +592,92 @@
 
 
+/** @defgroup grp_rt_vfs_ll_util        VFS Utility APIs
+ * @{ */
+
+/**
+ * Parsed path.
+ */
+typedef struct RTVFSPARSEDPATH
+{
+    /** The length of the path in szCopy. */
+    uint16_t        cch;
+    /** The number of path components. */
+    uint16_t        cComponents;
+    /** Set if the path ends with slash, indicating that it's a directory
+     * reference and not a file reference.  The slash has been removed from
+     * the copy. */
+    bool            fDirSlash;
+    /** The offset where each path component starts, i.e. the char after the
+     * slash.  The array has cComponents + 1 entries, where the final one is
+     * cch + 1 so that one can always terminate the current component by
+     * szPath[aoffComponent[i] - 1] = '\0'. */
+    uint16_t        aoffComponents[RTPATH_MAX / 2 + 1];
+    /** A normalized copy of the path.
+     * Reserve some extra space so we can be more relaxed about overflow
+     * checks and terminator paddings, especially when recursing. */
+    char            szPath[RTPATH_MAX];
+} RTVFSPARSEDPATH;
+/** Pointer to a parsed path. */
+typedef RTVFSPARSEDPATH *PRTVFSPARSEDPATH;
+
+/** The max accepted path length.
+ * This must be a few chars shorter than RTVFSPARSEDPATH::szPath because we
+ * use two terminators and wish be a little bit lazy with checking. */
+#define RTVFSPARSEDPATH_MAX     (RTPATH_MAX - 4U)
+
+/**
+ * Appends @a pszPath (relative) to the already parsed path @a pPath.
+ *
+ * @retval  VINF_SUCCESS
+ * @retval  VERR_FILENAME_TOO_LONG
+ * @retval  VERR_INTERNAL_ERROR_4
+ * @param   pPath               The parsed path to append @a pszPath onto.
+ *                              This is both input and output.
+ * @param   pszPath             The path to append.  This must be relative.
+ * @param   piRestartComp       The component to restart parsing at.  This is
+ *                              input/output.  The input does not have to be
+ *                              within the valid range.  Optional.
+ */
+RTDECL(int) RTVfsParsePathAppend(PRTVFSPARSEDPATH pPath, const char *pszPath, uint16_t *piRestartComp);
+
+/**
+ * Parses a path.
+ *
+ * @retval  VINF_SUCCESS
+ * @retval  VERR_FILENAME_TOO_LONG
+ * @param   pPath               Where to store the parsed path.
+ * @param   pszPath             The path to parse.  Absolute or relative to @a
+ *                              pszCwd.
+ * @param   pszCwd              The current working directory.  Must be
+ *                              absolute.
+ */
+RTDECL(int) RTVfsParsePath(PRTVFSPARSEDPATH pPath, const char *pszPath, const char *pszCwd);
+
+/**
+ * Same as RTVfsParsePath except that it allocates a temporary buffer.
+ *
+ * @retval  VINF_SUCCESS
+ * @retval  VERR_NO_TMP_MEMORY
+ * @retval  VERR_FILENAME_TOO_LONG
+ * @param   pszPath             The path to parse.  Absolute or relative to @a
+ *                              pszCwd.
+ * @param   pszCwd              The current working directory.  Must be
+ *                              absolute.
+ * @param   ppPath              Where to store the pointer to the allocated
+ *                              buffer containing the parsed path.  This must
+ *                              be freed by calling RTVfsParsePathFree.  NULL
+ *                              will be stored on failured.
+ */
+RTDECL(int) RTVfsParsePathA(const char *pszPath, const char *pszCwd, PRTVFSPARSEDPATH *ppPath);
+
+/**
+ * Frees a buffer returned by RTVfsParsePathA.
+ *
+ * @param   pPath               The parsed path buffer to free.  NULL is fine.
+ */
+RTDECL(void) RTVfsParsePathFree(PRTVFSPARSEDPATH pPath);
+
+/** @}  */
+
 /** @} */
 
Index: /trunk/src/VBox/HostDrivers/Support/SUPR3HardenedVerify.cpp
===================================================================
--- /trunk/src/VBox/HostDrivers/Support/SUPR3HardenedVerify.cpp	(revision 33858)
+++ /trunk/src/VBox/HostDrivers/Support/SUPR3HardenedVerify.cpp	(revision 33859)
@@ -917,5 +917,6 @@
      * Check each component.  No parent references or double slashes.
      */
-    pInfo->fDirSlash = false;
+    pInfo->cComponents = 0;
+    pInfo->fDirSlash   = false;
     while (pszSrc[0])
     {
Index: /trunk/src/VBox/Runtime/common/vfs/vfsbase.cpp
===================================================================
--- /trunk/src/VBox/Runtime/common/vfs/vfsbase.cpp	(revision 33858)
+++ /trunk/src/VBox/Runtime/common/vfs/vfsbase.cpp	(revision 33859)
@@ -36,4 +36,11 @@
 #include <iprt/file.h>
 #include <iprt/mem.h>
+#include <iprt/param.h>
+#include <iprt/path.h>
+#include <iprt/semaphore.h>
+
+#include "internal/file.h"
+#include "internal/magics.h"
+//#include "internal/vfs.h"
 
 
@@ -43,11 +50,59 @@
 #define RTVFS_MAGIC                 UINT32_C(0x11112222)
 #define RTVFS_MAGIC_DEAD            (~RTVFS_MAGIC)
+#define RTVFSDIR_MAGIC              UINT32_C(0x77778888)
+#define RTVFSDIR_MAGIC_DEAD         (~RTVFSDIR_MAGIC)
+#define RTVFSFILE_MAGIC             UINT32_C(0x55556666)
+#define RTVFSFILE_MAGIC_DEAD        (~RTVFSFILE_MAGIC)
 #define RTVFSIOSTREAM_MAGIC         UINT32_C(0x33334444)
 #define RTVFSIOSTREAM_MAGIC_DEAD    (~RTVFSIOSTREAM_MAGIC)
-#define RTVFSFILE_MAGIC             UINT32_C(0x55556666)
-#define RTVFSFILE_MAGIC_DEAD        (~RTVFSFILE_MAGIC)
+#define RTVFSSYMLINK_MAGIC          UINT32_C(0x9999aaaa)
+#define RTVFSSYMLINK_MAGIC_DEAD     (~RTVFSSYMLINK_MAGIC)
 
 /** The instance data alignment. */
 #define RTVFS_INST_ALIGNMENT        16U
+
+/** The max number of symbolic links to resolve in a path. */
+#define RTVFS_MAX_LINKS             20U
+
+
+/** Takes a write lock. */
+#define RTVFS_WRITE_LOCK(hSemRW)  \
+    do { \
+        if ((hSemRW) != NIL_RTSEMRW) \
+        { \
+            int rcSemEnter = RTSemRWRequestWrite(hSemRW, RT_INDEFINITE_WAIT); \
+            AssertRC(rcSemEnter); \
+        } \
+    } while (0)
+
+/** Releases a write lock. */
+#define RTVFS_WRITE_UNLOCK(hSemRW)  \
+    do { \
+        if ((hSemRW) != NIL_RTSEMRW) \
+        { \
+            int rcSemLeave = RTSemRWReleaseWrite(hSemRW); \
+            AssertRC(rcSemLeave); \
+        } \
+    } while (0)
+
+/** Takes a read lock. */
+#define RTVFS_READ_LOCK(hSemRW)  \
+    do { \
+        if ((hSemRW) != NIL_RTSEMRW) \
+        { \
+            int rcSemEnter = RTSemRWRequestRead(hSemRW, RT_INDEFINITE_WAIT); \
+            AssertRC(rcSemEnter); \
+        } \
+    } while (0)
+
+/** Releases a read lock. */
+#define RTVFS_READ_UNLOCK(hSemRW)  \
+    do { \
+        if ((hSemRW) != NIL_RTSEMRW) \
+        { \
+            int rcSemLeave = RTSemRWReleaseRead(hSemRW); \
+            AssertRC(rcSemLeave); \
+        } \
+    } while (0)
 
 
@@ -104,4 +159,26 @@
 
 /**
+ * The VFS symbolic link handle data.
+ */
+typedef struct RTVFSSYMLINKINTERNAL
+{
+    /** The VFS magic (RTVFSSYMLINK_MAGIC). */
+    uint32_t                uMagic;
+    /** Reserved for flags or something. */
+    uint32_t                fReserved;
+    /** Pointer to the instance data. */
+    void                   *pvThis;
+    /** The vtable. */
+    PCRTVFSSYMLINKOPS       pOps;
+    /** The VFS RW sem if serialized. */
+    RTSEMRW                 hSemRW;
+    /** Reference back to the VFS containing this symbolic link. */
+    RTVFS                   hVfs;
+    /** The number of references to this symbolic link handle. */
+    uint32_t volatile       cRefs;
+} RTVFSSYMLINKINTERNAL;
+
+
+/**
  * The VFS I/O stream handle data.
  *
@@ -145,4 +222,5 @@
 
 
+
 /**
  * Internal object retainer that asserts sanity in strict builds.
@@ -183,4 +261,518 @@
 }
 
+
+/*
+ *
+ *  U T I L   U T I L   U T I L
+ *  U T I L   U T I L   U T I L
+ *  U T I L   U T I L   U T I L
+ *
+ */
+
+
+
+/**
+ * Removes dots from the path.
+ *
+ * @returns The new @a pszDst value.
+ * @param   pPath               The path parsing buffer.
+ * @param   pszDst              The current szPath position.  This will be
+ *                              updated and returned.
+ * @param   fTheEnd             Indicates whether we're at the end of the path
+ *                              or not.
+ * @param   piRestartComp       The component to restart parsing at.
+ */
+static char *rtVfsParsePathHandleDots(PRTVFSPARSEDPATH pPath, char *pszDst, bool fTheEnd, uint16_t *piRestartComp)
+{
+    if (pszDst[-1] != '.')
+        return pszDst;
+
+    if (pszDst[-2] == '/')
+    {
+        pPath->cComponents--;
+        pszDst = &pPath->szPath[pPath->aoffComponents[pPath->cComponents]];
+    }
+    else if (pszDst[-2] == '.' && pszDst[-3] == '/')
+    {
+        pPath->cComponents -= pPath->cComponents > 1 ? 2 : 1;
+        pszDst = &pPath->szPath[pPath->aoffComponents[pPath->cComponents]];
+        if (piRestartComp && *piRestartComp + 1 >= pPath->cComponents)
+            *piRestartComp = pPath->cComponents > 0 ? pPath->cComponents - 1 : 0;
+    }
+    else
+        return pszDst;
+
+    /*
+     * Drop the trailing slash if we're at the end of the source path.
+     */
+    if (fTheEnd && pPath->cComponents == 0)
+        pszDst--;
+    return pszDst;
+}
+
+
+RTDECL(int) RTVfsParsePathAppend(PRTVFSPARSEDPATH pPath, const char *pszPath, uint16_t *piRestartComp)
+{
+    AssertReturn(*pszPath != '/', VERR_INTERNAL_ERROR_4);
+
+    /* In case *piRestartComp was set higher than the number of components
+       before making the call to this function. */
+    if (piRestartComp && *piRestartComp + 1 >= pPath->cComponents)
+        *piRestartComp = pPath->cComponents > 0 ? pPath->cComponents - 1 : 0;
+
+    /*
+     * Append a slash to the destination path if necessary.
+     */
+    char *pszDst = &pPath->szPath[pPath->cch];
+    if (pPath->cComponents > 0)
+    {
+        *pszDst++ = '/';
+        if (pszDst - &pPath->szPath[0] >= RTVFSPARSEDPATH_MAX)
+            return VERR_FILENAME_TOO_LONG;
+    }
+    Assert(pszDst[-1] == '/');
+
+    /*
+     * Parse and append the relative path.
+     */
+    const char *pszSrc = pszPath;
+    pPath->fDirSlash   = false;
+    while (pszSrc[0])
+    {
+        /* Skip unncessary slashes. */
+        while (pszSrc[0] == '/')
+            pszSrc++;
+
+        /* Copy until we encounter the next slash. */
+        pPath->aoffComponents[pPath->cComponents++] = pszDst - &pPath->szPath[0];
+        while (pszSrc[0])
+        {
+            if (pszSrc[0] == '/')
+            {
+                pszSrc++;
+                if (pszSrc[0])
+                    *pszDst++ = '/';
+                else
+                    pPath->fDirSlash = true;
+                pszDst = rtVfsParsePathHandleDots(pPath, pszDst, pszSrc[0] == '\0', piRestartComp);
+                break;
+            }
+
+            *pszDst++ = *pszSrc++;
+            if (pszDst - &pPath->szPath[0] >= RTVFSPARSEDPATH_MAX)
+                return VERR_FILENAME_TOO_LONG;
+        }
+    }
+    pszDst = rtVfsParsePathHandleDots(pPath, pszDst, true /*fTheEnd*/, piRestartComp);
+
+    /* Terminate the string and enter its length. */
+    pszDst[0] = '\0';
+    pszDst[1] = '\0';                   /* for aoffComponents */
+    pPath->cch = (uint16_t)(pszDst - &pPath->szPath[0]);
+    pPath->aoffComponents[pPath->cComponents] = pPath->cch + 1;
+
+    return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTVfsParsePath(PRTVFSPARSEDPATH pPath, const char *pszPath, const char *pszCwd)
+{
+    if (*pszPath != '/')
+    {
+        /*
+         * Relative, recurse and parse pszCwd first.
+         */
+        int rc = RTVfsParsePath(pPath, pszCwd, NULL /*crash if pszCwd is not absolute*/);
+        if (RT_FAILURE(rc))
+            return rc;
+    }
+    else
+    {
+        /*
+         * Make pszPath relative, i.e. set up pPath for the root and skip
+         * leading slashes in pszPath before appending it.
+         */
+        pPath->cch               = 1;
+        pPath->cComponents       = 0;
+        pPath->fDirSlash         = false;
+        pPath->aoffComponents[0] = 1;
+        pPath->aoffComponents[1] = 2;
+        pPath->szPath[0]         = '/';
+        pPath->szPath[1]         = '\0';
+        pPath->szPath[2]         = '\0';
+        while (pszPath[0] == '/')
+            pszPath++;
+        if (!pszPath[0])
+            return VINF_SUCCESS;
+    }
+    return RTVfsParsePathAppend(pPath, pszPath, NULL);
+}
+
+
+
+RTDECL(int) RTVfsParsePathA(const char *pszPath, const char *pszCwd, PRTVFSPARSEDPATH *ppPath)
+{
+    /*
+     * Allocate the output buffer and hand the problem to rtVfsParsePath.
+     */
+    int rc;
+    PRTVFSPARSEDPATH pPath = (PRTVFSPARSEDPATH)RTMemTmpAlloc(sizeof(RTVFSPARSEDPATH));
+    if (pPath)
+    {
+        rc = RTVfsParsePath(pPath, pszPath, pszCwd);
+        if (RT_FAILURE(rc))
+        {
+            RTMemTmpFree(pPath);
+            pPath = NULL;
+        }
+    }
+    else
+        rc = VERR_NO_TMP_MEMORY;
+    *ppPath = pPath;                    /* always set it */
+    return rc;
+}
+
+
+RTDECL(void) RTVfsParsePathFree(PRTVFSPARSEDPATH pPath)
+{
+    if (pPath)
+    {
+        pPath->cch               = UINT16_MAX;
+        pPath->cComponents       = UINT16_MAX;
+        pPath->aoffComponents[0] = UINT16_MAX;
+        pPath->aoffComponents[1] = UINT16_MAX;
+        RTMemTmpFree(pPath);
+    }
+}
+
+
+/**
+ * Handles a symbolic link, adding it to
+ *
+ * @returns IPRT status code.
+ * @param   pPath               The parsed path to update.
+ * @param   piComponent         The component iterator to update.
+ * @param   hSymlink            The symbolic link to process.
+ */
+static int rtVfsTraverseHandleSymlink(PRTVFSPARSEDPATH pPath, uint16_t *piComponent, RTVFSSYMLINK hSymlink)
+{
+    /*
+     * Read the link.
+     */
+    char szPath[RTPATH_MAX];
+    int rc = RTVfsSymlinkRead(hSymlink, szPath, sizeof(szPath) - 1);
+    if (RT_SUCCESS(rc))
+    {
+        szPath[sizeof(szPath) - 1] = '\0';
+        if (szPath[0] == '/')
+        {
+            /*
+             * Absolute symlink.
+             */
+            rc = RTVfsParsePath(pPath, szPath, NULL);
+            if (RT_SUCCESS(rc))
+            {
+                *piComponent = 0;
+                return VINF_SUCCESS;
+            }
+        }
+        else
+        {
+            /*
+             * Relative symlink, must replace the current component with the
+             * link value.  We do that by using the remainder of the symlink
+             * buffer as temporary storage.
+             */
+            uint16_t iComponent = *piComponent;
+            if (iComponent + 1 < pPath->cComponents)
+                rc = RTPathAppend(szPath, sizeof(szPath), &pPath->szPath[pPath->aoffComponents[iComponent + 1]]);
+            if (RT_SUCCESS(rc))
+            {
+                pPath->cch = pPath->aoffComponents[iComponent] - (iComponent > 0);
+                pPath->aoffComponents[iComponent + 1] = pPath->cch + 1;
+                pPath->szPath[pPath->cch]     = '\0';
+                pPath->szPath[pPath->cch + 1] = '\0';
+
+                rc = RTVfsParsePathAppend(pPath, szPath, &iComponent);
+                if (RT_SUCCESS(rc))
+                {
+                    *piComponent = iComponent;
+                    return VINF_SUCCESS;
+                }
+            }
+        }
+    }
+    return rc == VERR_BUFFER_OVERFLOW ? VERR_FILENAME_TOO_LONG : rc;
+}
+
+
+/**
+ * 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->cRefs > 0);
+    AssertPtr(pPath);
+    AssertPtr(ppVfsParentDir);
+    AssertReturn(pPath->cComponents > 0, VERR_INTERNAL_ERROR_3);
+
+    /*
+     * Open the root directory.
+     */
+    /** @todo Union mounts, traversal optimization methods, races, ++ */
+    RTVFSDIRINTERNAL *pCurDir;
+    RTVFS_READ_LOCK(pThis->hSemRW);
+    int rc = pThis->pOps->pfnOpenRoot(pThis->pvThis, &pCurDir);
+    RTVFS_READ_UNLOCK(pThis->hSemRW);
+    if (RT_FAILURE(rc))
+        return rc;
+    Assert(pCurDir->uMagic == RTVFSDIR_MAGIC);
+
+    /*
+     * The traversal loop.
+     */
+    unsigned cLinks     = 0;
+    uint16_t iComponent = 0;
+    for (;;)
+    {
+        /*
+         * Are we done yet?
+         */
+        bool fFinal = iComponent + 1 >= pPath->cComponents;
+        if (fFinal && !fFollowSymlink)
+        {
+            *ppVfsParentDir = pCurDir;
+            return VINF_SUCCESS;
+        }
+
+        /*
+         * Try open the next entry.
+         */
+        const char     *pszEntry    = &pPath->szPath[pPath->aoffComponents[iComponent]];
+        char           *pszEntryEnd = &pPath->szPath[pPath->aoffComponents[iComponent + 1] - 1];
+        *pszEntryEnd = '\0';
+        RTVFSDIR        hDir     = NIL_RTVFSDIR;
+        RTVFSSYMLINK    hSymlink = NIL_RTVFSSYMLINK;
+        RTVFS           hVfsMnt  = NIL_RTVFS;
+        if (fFinal)
+        {
+            RTVFS_READ_LOCK(pCurDir->hSemRW);
+            rc = pCurDir->pOps->pfnTraversalOpen(pCurDir->pvThis, pszEntry, NULL, &hSymlink, NULL);
+            RTVFS_READ_UNLOCK(pCurDir->hSemRW);
+            *pszEntryEnd = '\0';
+            if (rc == VERR_PATH_NOT_FOUND)
+                rc = VINF_SUCCESS;
+            if (RT_FAILURE(rc))
+                break;
+
+            if (hSymlink == NIL_RTVFSSYMLINK)
+            {
+                *ppVfsParentDir = pCurDir;
+                return VINF_SUCCESS;
+            }
+        }
+        else
+        {
+            RTVFS_READ_LOCK(pCurDir->hSemRW);
+            rc = pCurDir->pOps->pfnTraversalOpen(pCurDir->pvThis, pszEntry, &hDir, &hSymlink, &hVfsMnt);
+            RTVFS_READ_UNLOCK(pCurDir->hSemRW);
+            *pszEntryEnd = '/';
+            if (RT_FAILURE(rc))
+                break;
+
+            if (   hDir     == NIL_RTVFSDIR
+                && hSymlink == NIL_RTVFSSYMLINK
+                && hVfsMnt  == NIL_RTVFS)
+            {
+                rc = VERR_NOT_A_DIRECTORY;
+                break;
+            }
+        }
+        Assert(   (hDir != NIL_RTVFSDIR && hSymlink == NIL_RTVFSSYMLINK && hVfsMnt == NIL_RTVFS)
+               || (hDir == NIL_RTVFSDIR && hSymlink != NIL_RTVFSSYMLINK && hVfsMnt == NIL_RTVFS)
+               || (hDir == NIL_RTVFSDIR && hSymlink == NIL_RTVFSSYMLINK && hVfsMnt != NIL_RTVFS));
+
+        if (hDir != NIL_RTVFSDIR)
+        {
+            /*
+             * Directory - advance down the path.
+             */
+            AssertPtr(hDir);
+            Assert(hDir->uMagic == RTVFSDIR_MAGIC);
+            RTVfsDirRelease(pCurDir);
+            pCurDir = hDir;
+            iComponent++;
+        }
+        else if (hSymlink != NIL_RTVFSSYMLINK)
+        {
+            /*
+             * Symbolic link - deal with it and retry the current component.
+             */
+            AssertPtr(hSymlink);
+            Assert(hSymlink->uMagic == RTVFSSYMLINK_MAGIC);
+            cLinks++;
+            if (cLinks >= RTVFS_MAX_LINKS)
+            {
+                rc = VERR_TOO_MANY_SYMLINKS;
+                break;
+            }
+            uint16_t iRestartComp = iComponent;
+            rc = rtVfsTraverseHandleSymlink(pPath, &iRestartComp, hSymlink);
+            if (RT_FAILURE(rc))
+                break;
+            if (iRestartComp != iComponent)
+            {
+                /* Must restart from the root (optimize this). */
+                RTVfsDirRelease(pCurDir);
+                RTVFS_READ_LOCK(pThis->hSemRW);
+                rc = pThis->pOps->pfnOpenRoot(pThis->pvThis, &pCurDir);
+                RTVFS_READ_UNLOCK(pThis->hSemRW);
+                if (RT_FAILURE(rc))
+                {
+                    pCurDir = NULL;
+                    break;
+                }
+                iComponent = 0;
+            }
+        }
+        else
+        {
+            /*
+             * Mount point - deal with it and retry the current component.
+             */
+            RTVfsDirRelease(pCurDir);
+            RTVFS_READ_LOCK(hVfsMnt->hSemRW);
+            rc = pThis->pOps->pfnOpenRoot(hVfsMnt->pvThis, &pCurDir);
+            RTVFS_READ_UNLOCK(hVfsMnt->hSemRW);
+            if (RT_FAILURE(rc))
+            {
+                pCurDir = NULL;
+                break;
+            }
+            iComponent = 0;
+            /** @todo union mounts. */
+        }
+    }
+
+    if (pCurDir)
+        RTVfsDirRelease(pCurDir);
+
+    return rc;
+}
+
+
+
+/*
+ *
+ *  D I R   D I R   D I R
+ *  D I R   D I R   D I R
+ *  D I R   D I R   D I R
+ *
+ */
+
+RTDECL(uint32_t)    RTVfsDirRetain(RTVFSDIR hVfsDir)
+{
+    RTVFSDIRINTERNAL *pThis = hVfsDir;
+    AssertPtrReturn(pThis, UINT32_MAX);
+    AssertReturn(pThis->uMagic == RTVFSDIR_MAGIC, UINT32_MAX);
+    return rtVfsRetain(&pThis->cRefs);
+}
+
+
+RTDECL(uint32_t)    RTVfsDirRelease(RTVFSDIR hVfsDir)
+{
+    RTVFSDIRINTERNAL *pThis = hVfsDir;
+    AssertPtrReturn(pThis, UINT32_MAX);
+    AssertReturn(pThis->uMagic == RTVFSDIR_MAGIC, UINT32_MAX);
+
+    uint32_t cRefs = rtVfsRelease(&pThis->cRefs);
+    if (!cRefs)
+    {
+        RTVFS_WRITE_LOCK(pThis->hSemRW);
+        ASMAtomicWriteU32(&pThis->uMagic, RTVFSDIR_MAGIC_DEAD);
+        RTVFS_WRITE_UNLOCK(pThis->hSemRW);
+        pThis->pOps->Obj.pfnClose(pThis->pvThis);
+        RTMemFree(pThis);
+    }
+
+    return cRefs;
+}
+
+
+
+/*
+ *
+ *  S Y M B O L I C   L I N K
+ *  S Y M B O L I C   L I N K
+ *  S Y M B O L I C   L I N K
+ *
+ */
+
+RTDECL(uint32_t)    RTVfsSymlinkRetain(RTVFSSYMLINK hVfsSym)
+{
+    RTVFSSYMLINKINTERNAL *pThis = hVfsSym;
+    AssertPtrReturn(pThis, UINT32_MAX);
+    AssertReturn(pThis->uMagic == RTVFSSYMLINK_MAGIC, UINT32_MAX);
+    return rtVfsRetain(&pThis->cRefs);
+}
+
+
+RTDECL(uint32_t)    RTVfsSymlinkRelease(RTVFSSYMLINK hVfsSym)
+{
+    RTVFSSYMLINKINTERNAL *pThis = hVfsSym;
+    AssertPtrReturn(pThis, UINT32_MAX);
+    AssertReturn(pThis->uMagic == RTVFSSYMLINK_MAGIC, UINT32_MAX);
+
+    uint32_t cRefs = rtVfsRelease(&pThis->cRefs);
+    if (!cRefs)
+    {
+        RTVFS_WRITE_LOCK(pThis->hSemRW);
+        ASMAtomicWriteU32(&pThis->uMagic, RTVFSSYMLINK_MAGIC_DEAD);
+        RTVFS_WRITE_UNLOCK(pThis->hSemRW);
+        pThis->pOps->Obj.pfnClose(pThis->pvThis);
+        RTMemFree(pThis);
+    }
+
+    return cRefs;
+}
+
+
+RTDECL(int)         RTVfsSymlinkRead(RTVFSSYMLINK hVfsSym, char *pszTarget, size_t cbTarget)
+{
+    RTVFSSYMLINKINTERNAL *pThis = hVfsSym;
+    AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+    AssertReturn(pThis->uMagic == RTVFSSYMLINK_MAGIC, VERR_INVALID_HANDLE);
+
+    RTVFS_WRITE_LOCK(pThis->hSemRW);
+    int rc = pThis->pOps->pfnRead(pThis->pvThis, pszTarget, cbTarget);
+    RTVFS_WRITE_UNLOCK(pThis->hSemRW);
+
+    return rc;
+}
+
+
+
+/*
+ *
+ *  I / O   S T R E A M     I / O   S T R E A M     I / O   S T R E A M
+ *  I / O   S T R E A M     I / O   S T R E A M     I / O   S T R E A M
+ *  I / O   S T R E A M     I / O   S T R E A M     I / O   S T R E A M
+ *
+ */
 
 RTDECL(uint32_t)    RTVfsIoStrmRetain(RTVFSIOSTREAM hVfsIos)
@@ -209,5 +801,7 @@
          * need to get to the real one before handing it to RTMemFree.
          */
+        RTVFS_WRITE_LOCK(pThis->hSemRW);
         ASMAtomicWriteU32(&pThis->uMagic, RTVFSIOSTREAM_MAGIC_DEAD);
+        RTVFS_WRITE_UNLOCK(pThis->hSemRW);
         pThis->pOps->Obj.pfnClose(pThis->pvThis);
 
@@ -262,5 +856,8 @@
     AssertReturn(pThis->uMagic == RTVFSIOSTREAM_MAGIC, VERR_INVALID_HANDLE);
 
-    return pThis->pOps->Obj.pfnQueryInfo(pThis->pvThis, pObjInfo, enmAddAttr);
+    RTVFS_READ_LOCK(pThis->hSemRW);
+    int rc = pThis->pOps->Obj.pfnQueryInfo(pThis->pvThis, pObjInfo, enmAddAttr);
+    RTVFS_READ_UNLOCK(pThis->hSemRW);
+    return rc;
 }
 
@@ -275,5 +872,9 @@
     RTSGBUF SgBuf;
     RTSgBufInit(&SgBuf, &Seg, 1);
-    return pThis->pOps->pfnRead(pThis->pvThis, -1 /*off*/, &SgBuf, pcbRead == NULL /*fBlocking*/, pcbRead);
+
+    RTVFS_WRITE_LOCK(pThis->hSemRW);
+    int rc = pThis->pOps->pfnRead(pThis->pvThis, -1 /*off*/, &SgBuf, pcbRead == NULL /*fBlocking*/, pcbRead);
+    RTVFS_WRITE_UNLOCK(pThis->hSemRW);
+    return rc;
 }
 
@@ -288,5 +889,9 @@
     RTSGBUF SgBuf;
     RTSgBufInit(&SgBuf, &Seg, 1);
-    return pThis->pOps->pfnWrite(pThis->pvThis, -1 /*off*/, &SgBuf, pcbWritten == NULL /*fBlocking*/, pcbWritten);
+
+    RTVFS_WRITE_LOCK(pThis->hSemRW);
+    int rc = pThis->pOps->pfnWrite(pThis->pvThis, -1 /*off*/, &SgBuf, pcbWritten == NULL /*fBlocking*/, pcbWritten);
+    RTVFS_WRITE_UNLOCK(pThis->hSemRW);
+    return rc;
 }
 
@@ -300,5 +905,8 @@
     AssertReturn(fBlocking || VALID_PTR(pcbRead), VERR_INVALID_PARAMETER);
 
-    return pThis->pOps->pfnRead(pThis->pvThis, -1 /*off*/, pSgBuf, fBlocking, pcbRead);
+    RTVFS_WRITE_LOCK(pThis->hSemRW);
+    int rc = pThis->pOps->pfnRead(pThis->pvThis, -1 /*off*/, pSgBuf, fBlocking, pcbRead);
+    RTVFS_WRITE_UNLOCK(pThis->hSemRW);
+    return rc;
 }
 
@@ -312,5 +920,8 @@
     AssertReturn(fBlocking || VALID_PTR(pcbWritten), VERR_INVALID_PARAMETER);
 
-    return pThis->pOps->pfnWrite(pThis->pvThis, -1 /*off*/, pSgBuf, fBlocking, pcbWritten);
+    RTVFS_WRITE_LOCK(pThis->hSemRW);
+    int rc = pThis->pOps->pfnWrite(pThis->pvThis, -1 /*off*/, pSgBuf, fBlocking, pcbWritten);
+    RTVFS_WRITE_UNLOCK(pThis->hSemRW);
+    return rc;
 }
 
@@ -322,5 +933,8 @@
     AssertReturn(pThis->uMagic == RTVFSIOSTREAM_MAGIC, VERR_INVALID_HANDLE);
 
-    return pThis->pOps->pfnFlush(pThis->pvThis);
+    RTVFS_WRITE_LOCK(pThis->hSemRW);
+    int rc = pThis->pOps->pfnFlush(pThis->pvThis);
+    RTVFS_WRITE_UNLOCK(pThis->hSemRW);
+    return rc;
 }
 
@@ -333,5 +947,8 @@
     AssertReturn(pThis->uMagic == RTVFSIOSTREAM_MAGIC, VERR_INVALID_HANDLE);
 
-    return pThis->pOps->pfnPollOne(pThis->pvThis, fEvents, cMillies, fIntr, pfRetEvents);
+    RTVFS_WRITE_LOCK(pThis->hSemRW);
+    int rc = pThis->pOps->pfnPollOne(pThis->pvThis, fEvents, cMillies, fIntr, pfRetEvents);
+    RTVFS_WRITE_UNLOCK(pThis->hSemRW);
+    return rc;
 }
 
@@ -344,5 +961,7 @@
 
     RTFOFF off;
+    RTVFS_READ_LOCK(pThis->hSemRW);
     int rc = pThis->pOps->pfnTell(pThis->pvThis, &off);
+    RTVFS_READ_UNLOCK(pThis->hSemRW);
     if (RT_FAILURE(rc))
         off = rc;
@@ -351,4 +970,94 @@
 
 
+RTDECL(int)         RTVfsIoStrmSkip(RTVFSIOSTREAM hVfsIos, RTFOFF cb)
+{
+    RTVFSIOSTREAMINTERNAL *pThis = hVfsIos;
+    AssertPtrReturn(pThis, -1);
+    AssertReturn(pThis->uMagic == RTVFSIOSTREAM_MAGIC, -1);
+    AssertReturn(cb >= 0, VERR_INVALID_PARAMETER);
+
+    int rc;
+    if (pThis->pOps->pfnSkip)
+    {
+        RTVFS_WRITE_LOCK(pThis->hSemRW);
+        rc = pThis->pOps->pfnSkip(pThis->pvThis, cb);
+        RTVFS_WRITE_UNLOCK(pThis->hSemRW);
+    }
+    else
+    {
+        void *pvBuf = RTMemTmpAlloc(_64K);
+        if (pvBuf)
+        {
+            rc = VINF_SUCCESS;
+            while (cb > 0)
+            {
+                size_t cbToRead = RT_MIN(cb, _64K);
+                RTVFS_WRITE_LOCK(pThis->hSemRW);
+                rc = RTVfsIoStrmRead(hVfsIos, pvBuf, cbToRead, NULL);
+                RTVFS_WRITE_UNLOCK(pThis->hSemRW);
+                if (RT_FAILURE(rc))
+                    break;
+                cb -= cbToRead;
+            }
+
+            RTMemTmpFree(pvBuf);
+        }
+        else
+            rc = VERR_NO_TMP_MEMORY;
+    }
+    return rc;
+}
+
+
+RTDECL(int)         RTVfsIoStrmZeroFill(RTVFSIOSTREAM hVfsIos, RTFOFF cb)
+{
+    RTVFSIOSTREAMINTERNAL *pThis = hVfsIos;
+    AssertPtrReturn(pThis, -1);
+    AssertReturn(pThis->uMagic == RTVFSIOSTREAM_MAGIC, -1);
+
+    int rc;
+    if (pThis->pOps->pfnSkip)
+    {
+        RTVFS_WRITE_LOCK(pThis->hSemRW);
+        rc = pThis->pOps->pfnZeroFill(pThis->pvThis, cb);
+        RTVFS_WRITE_UNLOCK(pThis->hSemRW);
+    }
+    else
+    {
+        void *pvBuf = RTMemTmpAllocZ(_64K);
+        if (pvBuf)
+        {
+            rc = VINF_SUCCESS;
+            while (cb > 0)
+            {
+                size_t cbToWrite = RT_MIN(cb, _64K);
+                RTVFS_WRITE_LOCK(pThis->hSemRW);
+                rc = RTVfsIoStrmWrite(hVfsIos, pvBuf, cbToWrite, NULL);
+                RTVFS_WRITE_UNLOCK(pThis->hSemRW);
+                if (RT_FAILURE(rc))
+                    break;
+                cb -= cbToWrite;
+            }
+
+            RTMemTmpFree(pvBuf);
+        }
+        else
+            rc = VERR_NO_TMP_MEMORY;
+    }
+    return rc;
+}
+
+
+
+
+
+
+/*
+ *
+ *  F I L E   F I L E   F I L E
+ *  F I L E   F I L E   F I L E
+ *  F I L E   F I L E   F I L E
+ *
+ */
 
 RTDECL(int) RTVfsNewFile(PCRTVFSFILEOPS pFileOps, size_t cbInstance, uint32_t fOpen, RTVFS hVfs,
@@ -403,4 +1112,61 @@
 
 
+RTDECL(int)         RTVfsFileOpen(RTVFS hVfs, const char *pszFilename, uint32_t fOpen, PRTVFSFILE phVfsFile)
+{
+    /*
+     * Validate input.
+     */
+    RTVFSINTERNAL *pThis = hVfs;
+    AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+    AssertReturn(pThis->uMagic == RTVFS_MAGIC, VERR_INVALID_HANDLE);
+    AssertPtrReturn(pszFilename, 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(pszFilename, "/", &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 = 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. */
+                RTVFS_WRITE_LOCK(pVfsParentDir->hSemRW);
+                rc = pVfsParentDir->pOps->pfnOpenFile(pVfsParentDir->pvThis, pszEntryName, fOpen, phVfsFile);
+                RTVFS_WRITE_UNLOCK(pVfsParentDir->hSemRW);
+
+                RTVfsDirRelease(pVfsParentDir);
+
+                if (RT_SUCCESS(rc))
+                {
+                    AssertPtr(*phVfsFile);
+                    Assert((*phVfsFile)->uMagic == RTVFSFILE_MAGIC);
+                }
+            }
+        }
+        else
+            rc = VERR_INVALID_PARAMETER;
+        RTVfsParsePathFree(pPath);
+    }
+    return rc;
+}
+
+
 RTDECL(uint32_t)    RTVfsFileRetain(RTVFSFILE hVfsFile)
 {
@@ -421,6 +1187,9 @@
     if (!cRefs)
     {
+        RTVFS_WRITE_LOCK(pThis->Stream.hSemRW);
         ASMAtomicWriteU32(&pThis->uMagic, RTVFSFILE_MAGIC_DEAD);
         ASMAtomicWriteU32(&pThis->Stream.uMagic, RTVFSIOSTREAM_MAGIC_DEAD);
+        RTVFS_WRITE_UNLOCK(pThis->Stream.hSemRW);
+
         pThis->pOps->Stream.Obj.pfnClose(pThis->Stream.pvThis);
         RTMemFree(pThis);
Index: /trunk/src/VBox/Runtime/common/vfs/vfsstdfile.cpp
===================================================================
--- /trunk/src/VBox/Runtime/common/vfs/vfsstdfile.cpp	(revision 33858)
+++ /trunk/src/VBox/Runtime/common/vfs/vfsstdfile.cpp	(revision 33859)
@@ -243,4 +243,15 @@
 
 /**
+ * @interface_method_impl{RTVFSIOSTREAMOPS,pfnSkip}
+ */
+static DECLCALLBACK(int) rtVfsStdFile_Skip(void *pvThis, RTFOFF cb)
+{
+    PRTVFSSTDFILE pThis = (PRTVFSSTDFILE)pvThis;
+    uint64_t offIgnore;
+    return RTFileSeek(pThis->hFile, cb, RTFILE_SEEK_CURRENT, &offIgnore);
+}
+
+
+/**
  * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
  */
@@ -339,4 +350,6 @@
         rtVfsStdFile_PollOne,
         rtVfsStdFile_Tell,
+        rtVfsStdFile_Skip,
+        NULL /*ZeroFill*/,
         RTVFSIOSTREAMOPS_VERSION,
     },
