Index: /trunk/src/VBox/Runtime/Makefile.kmk
===================================================================
--- /trunk/src/VBox/Runtime/Makefile.kmk	(revision 68344)
+++ /trunk/src/VBox/Runtime/Makefile.kmk	(revision 68345)
@@ -429,5 +429,5 @@
 	common/fs/filesystemext.cpp \
 	common/fs/fatvfs.cpp \
-	common/fs/iso9660vfs.cpp \
+	common/fs/isovfs.cpp \
 	common/fs/isomaker.cpp \
 	common/fs/isomakercmd.cpp \
Index: unk/src/VBox/Runtime/common/fs/iso9660vfs.cpp
===================================================================
--- /trunk/src/VBox/Runtime/common/fs/iso9660vfs.cpp	(revision 68344)
+++ 	(revision )
@@ -1,2799 +1,0 @@
-/* $Id$ */
-/** @file
- * IPRT - ISO 9660 Virtual Filesystem.
- */
-
-/*
- * Copyright (C) 2017 Oracle Corporation
- *
- * This file is part of VirtualBox Open Source Edition (OSE), as
- * available from http://www.virtualbox.org. This file is free software;
- * you can redistribute it and/or modify it under the terms of the GNU
- * General Public License (GPL) as published by the Free Software
- * Foundation, in version 2 as it comes in the "COPYING" file of the
- * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
- * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
- *
- * The contents of this file may alternatively be used under the terms
- * of the Common Development and Distribution License Version 1.0
- * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
- * VirtualBox OSE distribution, in which case the provisions of the
- * CDDL are applicable instead of those of the GPL.
- *
- * You may elect to license modified versions of this file under the
- * terms and conditions of either the GPL or the CDDL or both.
- */
-
-
-/*********************************************************************************************************************************
-*   Header Files                                                                                                                 *
-*********************************************************************************************************************************/
-#define LOG_GROUP RTLOGGROUP_FS
-#include "internal/iprt.h"
-#include <iprt/fsvfs.h>
-
-#include <iprt/asm.h>
-#include <iprt/assert.h>
-#include <iprt/err.h>
-#include <iprt/ctype.h>
-#include <iprt/file.h>
-#include <iprt/log.h>
-#include <iprt/mem.h>
-#include <iprt/poll.h>
-#include <iprt/string.h>
-#include <iprt/thread.h>
-#include <iprt/vfs.h>
-#include <iprt/vfslowlevel.h>
-#include <iprt/formats/iso9660.h>
-
-
-
-/*********************************************************************************************************************************
-*   Structures and Typedefs                                                                                                      *
-*********************************************************************************************************************************/
-/** Pointer to an ISO 9660 volume (VFS instance data). */
-typedef struct RTFSISO9660VOL *PRTFSISO9660VOL;
-/** Pointer to a const ISO 9660 volume (VFS instance data). */
-typedef struct RTFSISO9660VOL const *PCRTFSISO9660VOL;
-
-/** Pointer to a ISO 9660 directory instance. */
-typedef struct RTFSISO9660DIRSHRD *PRTFSISO9660DIRSHRD;
-
-
-
-/**
- * ISO 9660 extent (internal to the VFS not a disk structure).
- */
-typedef struct RTFSISO9660EXTENT
-{
-    /** The disk offset. */
-    uint64_t            offDisk;
-    /** The size of the extent in bytes. */
-    uint64_t            cbExtent;
-} RTFSISO9660EXTENT;
-/** Pointer to an ISO 9660 extent. */
-typedef RTFSISO9660EXTENT *PRTFSISO9660EXTENT;
-/** Pointer to a const ISO 9660 extent. */
-typedef RTFSISO9660EXTENT const *PCRTFSISO9660EXTENT;
-
-
-/**
- * ISO 9660 file system object, shared part.
- */
-typedef struct RTFSISO9660CORE
-{
-    /** The parent directory keeps a list of open objects (RTFSISO9660CORE). */
-    RTLISTNODE          Entry;
-    /** Reference counter.   */
-    uint32_t volatile   cRefs;
-    /** The parent directory (not released till all children are close). */
-    PRTFSISO9660DIRSHRD pParentDir;
-    /** The byte offset of the first directory record. */
-    uint64_t            offDirRec;
-    /** Attributes. */
-    RTFMODE             fAttrib;
-    /** The object size. */
-    uint64_t            cbObject;
-    /** The access time. */
-    RTTIMESPEC          AccessTime;
-    /** The modificaton time. */
-    RTTIMESPEC          ModificationTime;
-    /** The change time. */
-    RTTIMESPEC          ChangeTime;
-    /** The birth time. */
-    RTTIMESPEC          BirthTime;
-    /** Pointer to the volume. */
-    PRTFSISO9660VOL     pVol;
-    /** The version number. */
-    uint32_t            uVersion;
-    /** Number of extents. */
-    uint32_t            cExtents;
-    /** The first extent. */
-    RTFSISO9660EXTENT   FirstExtent;
-    /** Array of additional extents. */
-    PRTFSISO9660EXTENT  paExtents;
-} RTFSISO9660CORE;
-typedef RTFSISO9660CORE *PRTFSISO9660CORE;
-
-/**
- * ISO 9660 file, shared data.
- */
-typedef struct RTFSISO9660FILESHRD
-{
-    /** Core ISO9660 object info.  */
-    RTFSISO9660CORE     Core;
-} RTFSISO9660FILESHRD;
-/** Pointer to a ISO 9660 file object. */
-typedef RTFSISO9660FILESHRD *PRTFSISO9660FILESHRD;
-
-
-/**
- * ISO 9660 directory, shared data.
- *
- * We will always read in the whole directory just to keep things really simple.
- */
-typedef struct RTFSISO9660DIRSHRD
-{
-    /** Core ISO 9660 object info.  */
-    RTFSISO9660CORE     Core;
-    /** Open child objects (RTFSISO9660CORE). */
-    RTLISTNODE          OpenChildren;
-
-    /** Pointer to the directory content. */
-    uint8_t            *pbDir;
-    /** The size of the directory content (duplicate of Core.cbObject). */
-    uint32_t            cbDir;
-} RTFSISO9660DIRSHRD;
-/** Pointer to a ISO 9660 directory instance. */
-typedef RTFSISO9660DIRSHRD *PRTFSISO9660DIRSHRD;
-
-
-/**
- * Private data for a VFS file object.
- */
-typedef struct RTFSISO9660FILEOBJ
-{
-    /** Pointer to the shared data. */
-    PRTFSISO9660FILESHRD    pShared;
-    /** The current file offset. */
-    uint64_t                offFile;
-} RTFSISO9660FILEOBJ;
-typedef RTFSISO9660FILEOBJ *PRTFSISO9660FILEOBJ;
-
-/**
- * Private data for a VFS directory object.
- */
-typedef struct RTFSISO9660DIROBJ
-{
-    /** Pointer to the shared data. */
-    PRTFSISO9660DIRSHRD     pShared;
-    /** The current directory offset. */
-    uint32_t                offDir;
-} RTFSISO9660DIROBJ;
-typedef RTFSISO9660DIROBJ *PRTFSISO9660DIROBJ;
-
-
-/**
- * A ISO 9660 volume.
- */
-typedef struct RTFSISO9660VOL
-{
-    /** Handle to itself. */
-    RTVFS                   hVfsSelf;
-    /** The file, partition, or whatever backing the ISO 9660 volume. */
-    RTVFSFILE               hVfsBacking;
-    /** The size of the backing thingy. */
-    uint64_t                cbBacking;
-    /** Flags. */
-    uint32_t                fFlags;
-    /** The sector size (in bytes). */
-    uint32_t                cbSector;
-    /** The size of a logical block in bytes. */
-    uint32_t                cbBlock;
-    /** The primary volume space size in blocks. */
-    uint32_t                cBlocksInPrimaryVolumeSpace;
-    /** The primary volume space size in bytes. */
-    uint64_t                cbPrimaryVolumeSpace;
-    /** The number of volumes in the set. */
-    uint32_t                cVolumesInSet;
-    /** The primary volume sequence ID. */
-    uint32_t                idPrimaryVol;
-    /** Set if using UTF16-2 (joliet). */
-    bool                    fIsUtf16;
-
-    /** The root directory shared data. */
-    PRTFSISO9660DIRSHRD     pRootDir;
-} RTFSISO9660VOL;
-
-
-
-/*********************************************************************************************************************************
-*   Global Variables                                                                                                             *
-*********************************************************************************************************************************/
-
-
-/*********************************************************************************************************************************
-*   Internal Functions                                                                                                           *
-*********************************************************************************************************************************/
-static void rtFsIso9660DirShrd_AddOpenChild(PRTFSISO9660DIRSHRD pDir, PRTFSISO9660CORE pChild);
-static void rtFsIso9660DirShrd_RemoveOpenChild(PRTFSISO9660DIRSHRD pDir, PRTFSISO9660CORE pChild);
-static int  rtFsIso9660Dir_New(PRTFSISO9660VOL pThis, PRTFSISO9660DIRSHRD pParentDir, PCISO9660DIRREC pDirRec,
-                               uint32_t cDirRecs, uint64_t offDirRec, PRTVFSDIR phVfsDir);
-static PRTFSISO9660CORE rtFsIso9660Dir_LookupShared(PRTFSISO9660DIRSHRD pThis, uint64_t offDirRec);
-
-
-/**
- * Returns the length of the version suffix in the given name.
- *
- * @returns Number of UTF16-BE chars in the version suffix.
- * @param   pawcName        The name to examine.
- * @param   cwcName         The length of the name.
- * @param   puValue         Where to return the value.
- */
-static size_t rtFsIso9660GetVersionLengthUtf16Big(PCRTUTF16 pawcName, size_t cwcName, uint32_t *puValue)
-{
-    *puValue = 0;
-
-    /* -1: */
-    if (cwcName <= 2)
-        return 0;
-    RTUTF16 wc1 = RT_BE2H_U16(pawcName[cwcName - 1]);
-    if (!RT_C_IS_DIGIT(wc1))
-        return 0;
-    Assert(wc1 < 0x3a); /* ASSUMES the RT_C_IS_DIGIT macro works just fine on wide chars too. */
-
-    /* -2: */
-    RTUTF16 wc2 = RT_BE2H_U16(pawcName[cwcName - 2]);
-    if (wc2 == ';')
-    {
-        *puValue = wc1 - '0';
-        return 2;
-    }
-    if (!RT_C_IS_DIGIT(wc2) || cwcName <= 3)
-        return 0;
-
-    /* -3: */
-    RTUTF16 wc3 = RT_BE2H_U16(pawcName[cwcName - 3]);
-    if (wc3 == ';')
-    {
-        *puValue = (wc1 - '0')
-                 + (wc2 - '0') * 10;
-        return 3;
-    }
-    if (!RT_C_IS_DIGIT(wc3) || cwcName <= 4)
-        return 0;
-
-    /* -4: */
-    RTUTF16 wc4 = RT_BE2H_U16(pawcName[cwcName - 4]);
-    if (wc4 == ';')
-    {
-        *puValue = (wc1 - '0')
-                 + (wc2 - '0') * 10
-                 + (wc3 - '0') * 100;
-        return 4;
-    }
-    if (!RT_C_IS_DIGIT(wc4) || cwcName <= 5)
-        return 0;
-
-    /* -5: */
-    RTUTF16 wc5 = RT_BE2H_U16(pawcName[cwcName - 5]);
-    if (wc5 == ';')
-    {
-        *puValue = (wc1 - '0')
-                 + (wc2 - '0') * 10
-                 + (wc3 - '0') * 100
-                 + (wc4 - '0') * 1000;
-        return 5;
-    }
-    if (!RT_C_IS_DIGIT(wc5) || cwcName <= 6)
-        return 0;
-
-    /* -6: */
-    RTUTF16 wc6 = RT_BE2H_U16(pawcName[cwcName - 6]);
-    if (wc6 == ';')
-    {
-        *puValue = (wc1 - '0')
-                 + (wc2 - '0') * 10
-                 + (wc3 - '0') * 100
-                 + (wc4 - '0') * 1000
-                 + (wc5 - '0') * 10000;
-        return 6;
-    }
-    return 0;
-}
-
-
-/**
- * Returns the length of the version suffix in the given name.
- *
- * @returns Number of chars in the version suffix.
- * @param   pachName        The name to examine.
- * @param   cchName         The length of the name.
- * @param   puValue         Where to return the value.
- */
-static size_t rtFsIso9660GetVersionLengthAscii(const char *pachName, size_t cchName, uint32_t *puValue)
-{
-    *puValue = 0;
-
-    /* -1: */
-    if (cchName <= 2)
-        return 0;
-    char ch1 = pachName[cchName - 1];
-    if (!RT_C_IS_DIGIT(ch1))
-        return 0;
-
-    /* -2: */
-    char ch2 = pachName[cchName - 2];
-    if (ch2 == ';')
-    {
-        *puValue = ch1 - '0';
-        return 2;
-    }
-    if (!RT_C_IS_DIGIT(ch2) || cchName <= 3)
-        return 0;
-
-    /* -3: */
-    char ch3 = pachName[cchName - 3];
-    if (ch3 == ';')
-    {
-        *puValue = (ch1 - '0')
-                 + (ch2 - '0') * 10;
-        return 3;
-    }
-    if (!RT_C_IS_DIGIT(ch3) || cchName <= 4)
-        return 0;
-
-    /* -4: */
-    char ch4 = pachName[cchName - 4];
-    if (ch4 == ';')
-    {
-        *puValue = (ch1 - '0')
-                 + (ch2 - '0') * 10
-                 + (ch3 - '0') * 100;
-        return 4;
-    }
-    if (!RT_C_IS_DIGIT(ch4) || cchName <= 5)
-        return 0;
-
-    /* -5: */
-    char ch5 = pachName[cchName - 5];
-    if (ch5 == ';')
-    {
-        *puValue = (ch1 - '0')
-                 + (ch2 - '0') * 10
-                 + (ch3 - '0') * 100
-                 + (ch4 - '0') * 1000;
-        return 5;
-    }
-    if (!RT_C_IS_DIGIT(ch5) || cchName <= 6)
-        return 0;
-
-    /* -6: */
-    if (pachName[cchName - 6] == ';')
-    {
-        *puValue = (ch1 - '0')
-                 + (ch2 - '0') * 10
-                 + (ch3 - '0') * 100
-                 + (ch4 - '0') * 1000
-                 + (ch5 - '0') * 10000;
-        return 6;
-    }
-    return 0;
-}
-
-
-/**
- * Converts a ISO 9660 binary timestamp into an IPRT timesspec.
- *
- * @param   pTimeSpec       Where to return the IRPT time.
- * @param   pIso9660        The ISO 9660 binary timestamp.
- */
-static void rtFsIso9660DateTime2TimeSpec(PRTTIMESPEC pTimeSpec, PCISO9660RECTIMESTAMP pIso9660)
-{
-    RTTIME Time;
-    Time.fFlags         = RTTIME_FLAGS_TYPE_UTC;
-    Time.offUTC         = 0;
-    Time.i32Year        = pIso9660->bYear + 1900;
-    Time.u8Month        = RT_MIN(RT_MAX(pIso9660->bMonth, 1), 12);
-    Time.u8MonthDay     = RT_MIN(RT_MAX(pIso9660->bDay, 1), 31);
-    Time.u8WeekDay      = UINT8_MAX;
-    Time.u16YearDay     = 0;
-    Time.u8Hour         = RT_MIN(pIso9660->bHour, 23);
-    Time.u8Minute       = RT_MIN(pIso9660->bMinute, 59);
-    Time.u8Second       = RT_MIN(pIso9660->bSecond, 59);
-    Time.u32Nanosecond  = 0;
-    RTTimeImplode(pTimeSpec, RTTimeNormalize(&Time));
-
-    /* Only apply the UTC offset if it's within reasons. */
-    if (RT_ABS(pIso9660->offUtc) <= 13*4)
-        RTTimeSpecSubSeconds(pTimeSpec, pIso9660->offUtc * 15 * 60 * 60);
-}
-
-
-/**
- * Initialization of a RTFSISO9660CORE structure from a directory record.
- *
- * @note    The RTFSISO9660CORE::pParentDir and RTFSISO9660CORE::Clusters members are
- *          properly initialized elsewhere.
- *
- * @returns IRPT status code.  Either VINF_SUCCESS or VERR_NO_MEMORY, the latter
- *          only if @a cDirRecs is above 1.
- * @param   pCore           The structure to initialize.
- * @param   pDirRec         The primary directory record.
- * @param   cDirRecs        Number of directory records.
- * @param   offDirRec       The offset of the primary directory record.
- * @param   uVersion        The file version number.
- * @param   pVol            The volume.
- */
-static int rtFsIso9660Core_InitFromDirRec(PRTFSISO9660CORE pCore, PCISO9660DIRREC pDirRec, uint32_t cDirRecs,
-                                          uint64_t offDirRec, uint32_t uVersion, PRTFSISO9660VOL pVol)
-{
-    RTListInit(&pCore->Entry);
-    pCore->cRefs                = 1;
-    pCore->pParentDir           = NULL;
-    pCore->pVol                 = pVol;
-    pCore->offDirRec            = offDirRec;
-    pCore->fAttrib              = pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY
-                                ? 0755 | RTFS_TYPE_DIRECTORY | RTFS_DOS_DIRECTORY
-                                : 0644 | RTFS_TYPE_FILE;
-    if (pDirRec->fFileFlags & ISO9660_FILE_FLAGS_HIDDEN)
-        pCore->fAttrib |= RTFS_DOS_HIDDEN;
-    pCore->cbObject             = ISO9660_GET_ENDIAN(&pDirRec->cbData);
-    pCore->uVersion             = uVersion;
-    pCore->cExtents             = 1;
-    pCore->FirstExtent.cbExtent = pCore->cbObject;
-    pCore->FirstExtent.offDisk  = (ISO9660_GET_ENDIAN(&pDirRec->offExtent) + pDirRec->cExtAttrBlocks) * (uint64_t)pVol->cbBlock;
-
-    rtFsIso9660DateTime2TimeSpec(&pCore->ModificationTime, &pDirRec->RecTime);
-    pCore->BirthTime  = pCore->ModificationTime;
-    pCore->AccessTime = pCore->ModificationTime;
-    pCore->ChangeTime = pCore->ModificationTime;
-
-    /*
-     * Deal with multiple extents.
-     */
-    if (RT_LIKELY(cDirRecs == 1))
-    { /* done */ }
-    else
-    {
-        PRTFSISO9660EXTENT pCurExtent = &pCore->FirstExtent;
-        while (cDirRecs > 1)
-        {
-            offDirRec += pDirRec->cbDirRec;
-            pDirRec = (PCISO9660DIRREC)((uintptr_t)pDirRec + pDirRec->cbDirRec);
-            if (pDirRec->cbDirRec != 0)
-            {
-                uint64_t offDisk = ISO9660_GET_ENDIAN(&pDirRec->offExtent) * (uint64_t)pVol->cbBlock;
-                uint32_t cbExtent  = ISO9660_GET_ENDIAN(&pDirRec->cbData);
-                pCore->cbObject += cbExtent;
-
-                if (pCurExtent->offDisk + pCurExtent->cbExtent == offDisk)
-                    pCurExtent->cbExtent += cbExtent;
-                else
-                {
-                    void *pvNew = RTMemRealloc(pCore->paExtents, pCore->cExtents * sizeof(pCore->paExtents[0]));
-                    if (pvNew)
-                        pCore->paExtents = (PRTFSISO9660EXTENT)pvNew;
-                    else
-                    {
-                        RTMemFree(pCore->paExtents);
-                        return VERR_NO_MEMORY;
-                    }
-                    pCurExtent = &pCore->paExtents[pCore->cExtents - 1];
-                    pCurExtent->cbExtent = cbExtent;
-                    pCurExtent->offDisk  = offDisk;
-                    pCore->cExtents++;
-                }
-                cDirRecs--;
-            }
-            else
-            {
-                size_t cbSkip = (offDirRec + pVol->cbSector) & ~(pVol->cbSector - 1U);
-                offDirRec += cbSkip;
-                pDirRec = (PCISO9660DIRREC)((uintptr_t)pDirRec + cbSkip);
-            }
-        }
-    }
-    return VINF_SUCCESS;
-}
-
-
-/**
- * Worker for rtFsIso9660File_QueryInfo and rtFsIso9660Dir_QueryInfo.
- */
-static int rtFsIso9660Core_QueryInfo(PRTFSISO9660CORE pCore, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
-{
-    pObjInfo->cbObject              = pCore->cbObject;
-    pObjInfo->cbAllocated           = RT_ALIGN_64(pCore->cbObject, pCore->pVol->cbBlock);
-    pObjInfo->AccessTime            = pCore->AccessTime;
-    pObjInfo->ModificationTime      = pCore->ModificationTime;
-    pObjInfo->ChangeTime            = pCore->ChangeTime;
-    pObjInfo->BirthTime             = pCore->BirthTime;
-    pObjInfo->Attr.fMode            = pCore->fAttrib;
-    pObjInfo->Attr.enmAdditional    = enmAddAttr;
-
-    switch (enmAddAttr)
-    {
-        case RTFSOBJATTRADD_NOTHING: /* fall thru */
-        case RTFSOBJATTRADD_UNIX:
-            pObjInfo->Attr.u.Unix.uid           = NIL_RTUID;
-            pObjInfo->Attr.u.Unix.gid           = NIL_RTGID;
-            pObjInfo->Attr.u.Unix.cHardlinks    = 1;
-            pObjInfo->Attr.u.Unix.INodeIdDevice = 0;
-            pObjInfo->Attr.u.Unix.INodeId       = 0; /* Could probably use the directory entry offset. */
-            pObjInfo->Attr.u.Unix.fFlags        = 0;
-            pObjInfo->Attr.u.Unix.GenerationId  = pCore->uVersion;
-            pObjInfo->Attr.u.Unix.Device        = 0;
-            break;
-        case RTFSOBJATTRADD_UNIX_OWNER:
-            pObjInfo->Attr.u.UnixOwner.uid       = 0;
-            pObjInfo->Attr.u.UnixOwner.szName[0] = '\0';
-            break;
-        case RTFSOBJATTRADD_UNIX_GROUP:
-            pObjInfo->Attr.u.UnixGroup.gid       = 0;
-            pObjInfo->Attr.u.UnixGroup.szName[0] = '\0';
-            break;
-        case RTFSOBJATTRADD_EASIZE:
-            pObjInfo->Attr.u.EASize.cb = 0;
-            break;
-        default:
-            return VERR_INVALID_PARAMETER;
-    }
-    return VINF_SUCCESS;
-}
-
-
-/**
- * Worker for rtFsIso9660File_Close and rtFsIso9660Dir_Close that does common work.
- *
- * @param   pCore           The common shared structure.
- */
-static void rtFsIso9660Core_Destroy(PRTFSISO9660CORE pCore)
-{
-    if (pCore->pParentDir)
-        rtFsIso9660DirShrd_RemoveOpenChild(pCore->pParentDir, pCore);
-    if (pCore->paExtents)
-    {
-        RTMemFree(pCore->paExtents);
-        pCore->paExtents = NULL;
-    }
-}
-
-
-/**
- * @interface_method_impl{RTVFSOBJOPS,pfnClose}
- */
-static DECLCALLBACK(int) rtFsIso9660File_Close(void *pvThis)
-{
-    PRTFSISO9660FILEOBJ  pThis   = (PRTFSISO9660FILEOBJ)pvThis;
-    LogFlow(("rtFsIso9660File_Close(%p/%p)\n", pThis, pThis->pShared));
-
-    PRTFSISO9660FILESHRD pShared = pThis->pShared;
-    pThis->pShared = NULL;
-    if (pShared)
-    {
-        if (ASMAtomicDecU32(&pShared->Core.cRefs) == 0)
-        {
-            LogFlow(("rtFsIso9660File_Close: Destroying shared structure %p\n", pShared));
-            rtFsIso9660Core_Destroy(&pShared->Core);
-            RTMemFree(pShared);
-        }
-    }
-    return VINF_SUCCESS;
-}
-
-
-/**
- * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
- */
-static DECLCALLBACK(int) rtFsIso9660File_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
-{
-    PRTFSISO9660FILEOBJ pThis = (PRTFSISO9660FILEOBJ)pvThis;
-    return rtFsIso9660Core_QueryInfo(&pThis->pShared->Core, pObjInfo, enmAddAttr);
-}
-
-
-/**
- * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
- */
-static DECLCALLBACK(int) rtFsIso9660File_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
-{
-    PRTFSISO9660FILEOBJ  pThis   = (PRTFSISO9660FILEOBJ)pvThis;
-    PRTFSISO9660FILESHRD pShared = pThis->pShared;
-    AssertReturn(pSgBuf->cSegs != 0, VERR_INTERNAL_ERROR_3);
-    RT_NOREF(fBlocking);
-
-    /*
-     * Check for EOF.
-     */
-    if (off == -1)
-        off = pThis->offFile;
-    if ((uint64_t)off >= pShared->Core.cbObject)
-    {
-        if (pcbRead)
-        {
-            *pcbRead = 0;
-            return VINF_EOF;
-        }
-        return VERR_EOF;
-    }
-
-
-    /*
-     * Simple case: File has a single extent.
-     */
-    int      rc         = VINF_SUCCESS;
-    size_t   cbRead     = 0;
-    uint64_t cbFileLeft = pShared->Core.cbObject - (uint64_t)off;
-    size_t   cbLeft     = pSgBuf->paSegs[0].cbSeg;
-    uint8_t *pbDst      = (uint8_t *)pSgBuf->paSegs[0].pvSeg;
-    if (pShared->Core.cExtents == 1)
-    {
-        if (cbLeft > 0)
-        {
-            size_t cbToRead = cbLeft;
-            if (cbToRead > cbFileLeft)
-                cbToRead = (size_t)cbFileLeft;
-            rc = RTVfsFileReadAt(pShared->Core.pVol->hVfsBacking, pShared->Core.FirstExtent.offDisk + off, pbDst, cbToRead, NULL);
-            if (RT_SUCCESS(rc))
-            {
-                off         += cbToRead;
-                pbDst       += cbToRead;
-                cbRead      += cbToRead;
-                cbFileLeft  -= cbToRead;
-                cbLeft      -= cbToRead;
-            }
-        }
-    }
-    /*
-     * Complicated case: Work the file content extent by extent.
-     */
-    else
-    {
-        return VERR_NOT_IMPLEMENTED; /** @todo multi-extent stuff . */
-    }
-
-    /* Update the offset and return. */
-    pThis->offFile = off;
-    if (pcbRead)
-        *pcbRead = cbRead;
-    return VINF_SUCCESS;
-}
-
-
-/**
- * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite}
- */
-static DECLCALLBACK(int) rtFsIso9660File_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
-{
-    RT_NOREF(pvThis, off, pSgBuf, fBlocking, pcbWritten);
-    return VERR_WRITE_PROTECT;
-}
-
-
-/**
- * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
- */
-static DECLCALLBACK(int) rtFsIso9660File_Flush(void *pvThis)
-{
-    RT_NOREF(pvThis);
-    return VINF_SUCCESS;
-}
-
-
-/**
- * @interface_method_impl{RTVFSIOSTREAMOPS,pfnPollOne}
- */
-static DECLCALLBACK(int) rtFsIso9660File_PollOne(void *pvThis, uint32_t fEvents, RTMSINTERVAL cMillies, bool fIntr,
-                                                 uint32_t *pfRetEvents)
-{
-    NOREF(pvThis);
-    int rc;
-    if (fEvents != RTPOLL_EVT_ERROR)
-    {
-        *pfRetEvents = fEvents & ~RTPOLL_EVT_ERROR;
-        rc = VINF_SUCCESS;
-    }
-    else if (fIntr)
-        rc = RTThreadSleep(cMillies);
-    else
-    {
-        uint64_t uMsStart = RTTimeMilliTS();
-        do
-            rc = RTThreadSleep(cMillies);
-        while (   rc == VERR_INTERRUPTED
-               && !fIntr
-               && RTTimeMilliTS() - uMsStart < cMillies);
-        if (rc == VERR_INTERRUPTED)
-            rc = VERR_TIMEOUT;
-    }
-    return rc;
-}
-
-
-/**
- * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
- */
-static DECLCALLBACK(int) rtFsIso9660File_Tell(void *pvThis, PRTFOFF poffActual)
-{
-    PRTFSISO9660FILEOBJ pThis = (PRTFSISO9660FILEOBJ)pvThis;
-    *poffActual = pThis->offFile;
-    return VINF_SUCCESS;
-}
-
-
-/**
- * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
- */
-static DECLCALLBACK(int) rtFsIso9660File_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
-{
-    RT_NOREF(pvThis, fMode, fMask);
-    return VERR_WRITE_PROTECT;
-}
-
-
-/**
- * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
- */
-static DECLCALLBACK(int) rtFsIso9660File_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
-                                                 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
-{
-    RT_NOREF(pvThis, pAccessTime, pModificationTime, pChangeTime, pBirthTime);
-    return VERR_WRITE_PROTECT;
-}
-
-
-/**
- * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
- */
-static DECLCALLBACK(int) rtFsIso9660File_SetOwner(void *pvThis, RTUID uid, RTGID gid)
-{
-    RT_NOREF(pvThis, uid, gid);
-    return VERR_WRITE_PROTECT;
-}
-
-
-/**
- * @interface_method_impl{RTVFSFILEOPS,pfnSeek}
- */
-static DECLCALLBACK(int) rtFsIso9660File_Seek(void *pvThis, RTFOFF offSeek, unsigned uMethod, PRTFOFF poffActual)
-{
-    PRTFSISO9660FILEOBJ pThis = (PRTFSISO9660FILEOBJ)pvThis;
-    RTFOFF offNew;
-    switch (uMethod)
-    {
-        case RTFILE_SEEK_BEGIN:
-            offNew = offSeek;
-            break;
-        case RTFILE_SEEK_END:
-            offNew = (RTFOFF)pThis->pShared->Core.cbObject + offSeek;
-            break;
-        case RTFILE_SEEK_CURRENT:
-            offNew = (RTFOFF)pThis->offFile + offSeek;
-            break;
-        default:
-            return VERR_INVALID_PARAMETER;
-    }
-    if (offNew >= 0)
-    {
-        if (offNew <= _4G)
-        {
-            pThis->offFile = offNew;
-            *poffActual    = offNew;
-            return VINF_SUCCESS;
-        }
-        return VERR_OUT_OF_RANGE;
-    }
-    return VERR_NEGATIVE_SEEK;
-}
-
-
-/**
- * @interface_method_impl{RTVFSFILEOPS,pfnQuerySize}
- */
-static DECLCALLBACK(int) rtFsIso9660File_QuerySize(void *pvThis, uint64_t *pcbFile)
-{
-    PRTFSISO9660FILEOBJ pThis = (PRTFSISO9660FILEOBJ)pvThis;
-    *pcbFile = pThis->pShared->Core.cbObject;
-    return VINF_SUCCESS;
-}
-
-
-/**
- * ISO FS file operations.
- */
-DECL_HIDDEN_CONST(const RTVFSFILEOPS) g_rtFsIos9660FileOps =
-{
-    { /* Stream */
-        { /* Obj */
-            RTVFSOBJOPS_VERSION,
-            RTVFSOBJTYPE_FILE,
-            "FatFile",
-            rtFsIso9660File_Close,
-            rtFsIso9660File_QueryInfo,
-            RTVFSOBJOPS_VERSION
-        },
-        RTVFSIOSTREAMOPS_VERSION,
-        RTVFSIOSTREAMOPS_FEAT_NO_SG,
-        rtFsIso9660File_Read,
-        rtFsIso9660File_Write,
-        rtFsIso9660File_Flush,
-        rtFsIso9660File_PollOne,
-        rtFsIso9660File_Tell,
-        NULL /*pfnSkip*/,
-        NULL /*pfnZeroFill*/,
-        RTVFSIOSTREAMOPS_VERSION,
-    },
-    RTVFSFILEOPS_VERSION,
-    0,
-    { /* ObjSet */
-        RTVFSOBJSETOPS_VERSION,
-        RT_OFFSETOF(RTVFSFILEOPS, Stream.Obj) - RT_OFFSETOF(RTVFSFILEOPS, ObjSet),
-        rtFsIso9660File_SetMode,
-        rtFsIso9660File_SetTimes,
-        rtFsIso9660File_SetOwner,
-        RTVFSOBJSETOPS_VERSION
-    },
-    rtFsIso9660File_Seek,
-    rtFsIso9660File_QuerySize,
-    RTVFSFILEOPS_VERSION
-};
-
-
-/**
- * Instantiates a new directory.
- *
- * @returns IPRT status code.
- * @param   pThis           The FAT volume instance.
- * @param   pParentDir      The parent directory (shared part).
- * @param   pDirRec         The directory record.
- * @param   cDirRecs        Number of directory records if more than one.
- * @param   offDirRec       The byte offset of the directory record.
- * @param   offEntryInDir   The byte offset of the directory entry in the parent
- *                          directory.
- * @param   fOpen           RTFILE_O_XXX flags.
- * @param   uVersion        The file version number (since the caller already
- *                          parsed the filename, we don't want to repeat the
- *                          effort here).
- * @param   phVfsFile       Where to return the file handle.
- */
-static int rtFsIso9660File_New(PRTFSISO9660VOL pThis, PRTFSISO9660DIRSHRD pParentDir, PCISO9660DIRREC pDirRec, uint32_t cDirRecs,
-                               uint64_t offDirRec, uint64_t fOpen, uint32_t uVersion, PRTVFSFILE phVfsFile)
-{
-    AssertPtr(pParentDir);
-
-    /*
-     * Create a VFS object.
-     */
-    PRTFSISO9660FILEOBJ pNewFile;
-    int rc = RTVfsNewFile(&g_rtFsIos9660FileOps, sizeof(*pNewFile), fOpen, pThis->hVfsSelf, NIL_RTVFSLOCK /*use volume lock*/,
-                          phVfsFile, (void **)&pNewFile);
-    if (RT_SUCCESS(rc))
-    {
-        /*
-         * Look for existing shared object, create a new one if necessary.
-         */
-        PRTFSISO9660FILESHRD pShared = (PRTFSISO9660FILESHRD)rtFsIso9660Dir_LookupShared(pParentDir, offDirRec);
-        if (!pShared)
-        {
-            pShared = (PRTFSISO9660FILESHRD)RTMemAllocZ(sizeof(*pShared));
-            if (pShared)
-            {
-                /*
-                 * Initialize it all so rtFsIso9660File_Close doesn't trip up in anyway.
-                 */
-                rc = rtFsIso9660Core_InitFromDirRec(&pShared->Core, pDirRec, cDirRecs, offDirRec, uVersion, pThis);
-                if (RT_SUCCESS(rc))
-                    rtFsIso9660DirShrd_AddOpenChild(pParentDir, &pShared->Core);
-                else
-                {
-                    RTMemFree(pShared);
-                    pShared = NULL;
-                }
-            }
-        }
-        if (pShared)
-        {
-            LogFlow(("rtFsIso9660File_New: cbObject=%#RX64 First Extent: off=%#RX64 cb=%#RX64\n",
-                     pShared->Core.cbObject, pShared->Core.FirstExtent.offDisk, pShared->Core.FirstExtent.cbExtent));
-            pNewFile->offFile = 0;
-            pNewFile->pShared = pShared;
-            return VINF_SUCCESS;
-        }
-
-        rc = VERR_NO_MEMORY;
-    }
-    *phVfsFile = NIL_RTVFSFILE;
-    return rc;
-}
-
-
-/**
- * Looks up the shared structure for a child.
- *
- * @returns Referenced pointer to the shared structure, NULL if not found.
- * @param   pThis           The directory.
- * @param   offDirRec       The directory record offset of the child.
- */
-static PRTFSISO9660CORE rtFsIso9660Dir_LookupShared(PRTFSISO9660DIRSHRD pThis, uint64_t offDirRec)
-{
-    PRTFSISO9660CORE pCur;
-    RTListForEach(&pThis->OpenChildren, pCur, RTFSISO9660CORE, Entry)
-    {
-        if (pCur->offDirRec == offDirRec)
-        {
-            uint32_t cRefs = ASMAtomicIncU32(&pCur->cRefs);
-            Assert(cRefs > 1); RT_NOREF(cRefs);
-            return pCur;
-        }
-    }
-    return NULL;
-}
-
-
-#ifdef RT_STRICT
-/**
- * Checks if @a pNext is an extent of @a pFirst.
- *
- * @returns true if @a pNext is the next extent, false if not
- * @param   pFirst      The directory record describing the first or the
- *                      previous extent.
- * @param   pNext       The directory record alleged to be the next extent.
- */
-DECLINLINE(bool) rtFsIso9660Dir_IsDirRecNextExtent(PCISO9660DIRREC pFirst, PCISO9660DIRREC pNext)
-{
-    if (RT_LIKELY(pNext->bFileIdLength == pFirst->bFileIdLength))
-    {
-        if (RT_LIKELY((pNext->fFileFlags | ISO9660_FILE_FLAGS_MULTI_EXTENT) == pFirst->fFileFlags))
-        {
-            if (RT_LIKELY(memcmp(pNext->achFileId, pFirst->achFileId, pNext->bFileIdLength) == 0))
-                return true;
-        }
-    }
-    return false;
-}
-#endif /* RT_STRICT */
-
-
-/**
- * Worker for rtFsIso9660Dir_FindEntry that compares a UTF-16BE name with a
- * directory record.
- *
- * @returns true if equal, false if not.
- * @param   pDirRec             The directory record.
- * @param   pwszEntry           The UTF-16BE string to compare with.
- * @param   cbEntry             The compare string length in bytes (sans zero
- *                              terminator).
- * @param   cwcEntry            The compare string length in RTUTF16 units.
- * @param   puVersion           Where to return any file version number.
- */
-DECL_FORCE_INLINE(bool) rtFsIso9660Dir_IsEntryEqualUtf16Big(PCISO9660DIRREC pDirRec, PCRTUTF16 pwszEntry, size_t cbEntry,
-                                                            size_t cwcEntry, uint32_t *puVersion)
-{
-    /* ASSUME directories cannot have any version tags. */
-    if (pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY)
-    {
-        if (RT_LIKELY(pDirRec->bFileIdLength != cbEntry))
-            return false;
-        if (RT_LIKELY(RTUtf16BigNICmp((PCRTUTF16)pDirRec->achFileId, pwszEntry, cwcEntry) != 0))
-            return false;
-    }
-    else
-    {
-        size_t cbNameDelta = (size_t)pDirRec->bFileIdLength - cbEntry;
-        if (RT_LIKELY(cbNameDelta > (size_t)12 /* ;12345 */))
-            return false;
-        if (cbNameDelta == 0)
-        {
-            if (RT_LIKELY(RTUtf16BigNICmp((PCRTUTF16)pDirRec->achFileId, pwszEntry, cwcEntry) != 0))
-                return false;
-            *puVersion = 1;
-        }
-        else
-        {
-            if (RT_LIKELY(RT_MAKE_U16(pDirRec->achFileId[cbEntry + 1], pDirRec->achFileId[cbEntry]) != ';'))
-                return false;
-            if (RT_LIKELY(RTUtf16BigNICmp((PCRTUTF16)pDirRec->achFileId, pwszEntry, cwcEntry) != 0))
-                return false;
-            uint32_t uVersion;
-            size_t  cwcVersion = rtFsIso9660GetVersionLengthUtf16Big((PCRTUTF16)pDirRec->achFileId,
-                                                                     pDirRec->bFileIdLength, &uVersion);
-            if (RT_LIKELY(cwcVersion * sizeof(RTUTF16) == cbNameDelta))
-                *puVersion = uVersion;
-            else
-                return false;
-        }
-    }
-
-    /* (No need to check for dot and dot-dot here, because cbEntry must be a
-       multiple of two.) */
-    Assert(!(cbEntry & 1));
-    return true;
-}
-
-
-/**
- * Worker for rtFsIso9660Dir_FindEntry that compares an ASCII name with a
- * directory record.
- *
- * @returns true if equal, false if not.
- * @param   pDirRec             The directory record.
- * @param   pszEntry            The uppercased ASCII string to compare with.
- * @param   cchEntry            The length of the compare string.
- * @param   puVersion           Where to return any file version number.
- */
-DECL_FORCE_INLINE(bool) rtFsIso9660Dir_IsEntryEqualAscii(PCISO9660DIRREC pDirRec, const char *pszEntry, size_t cchEntry,
-                                                         uint32_t *puVersion)
-{
-    /* ASSUME directories cannot have any version tags. */
-    if (pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY)
-    {
-        if (RT_LIKELY(pDirRec->bFileIdLength != cchEntry))
-            return false;
-        if (RT_LIKELY(memcmp(pDirRec->achFileId, pszEntry, cchEntry) != 0))
-            return false;
-    }
-    else
-    {
-        size_t cchNameDelta = (size_t)pDirRec->bFileIdLength - cchEntry;
-        if (RT_LIKELY(cchNameDelta > (size_t)6 /* ;12345 */))
-            return false;
-        if (cchNameDelta == 0)
-        {
-            if (RT_LIKELY(memcmp(pDirRec->achFileId, pszEntry, cchEntry) != 0))
-                return false;
-            *puVersion = 1;
-        }
-        else
-        {
-            if (RT_LIKELY(pDirRec->achFileId[cchEntry] != ';'))
-                return false;
-            if (RT_LIKELY(memcmp(pDirRec->achFileId, pszEntry, cchEntry) != 0))
-                return false;
-            uint32_t uVersion;
-            size_t  cchVersion = rtFsIso9660GetVersionLengthAscii(pDirRec->achFileId, pDirRec->bFileIdLength, &uVersion);
-            if (RT_LIKELY(cchVersion == cchNameDelta))
-                *puVersion = uVersion;
-            else
-                return false;
-        }
-    }
-
-    /* Don't match the 'dot' and 'dot-dot' directory records. */
-    if (RT_LIKELY(   pDirRec->bFileIdLength != 1
-                  || (uint8_t)pDirRec->achFileId[0] > (uint8_t)0x01))
-        return true;
-    return false;
-}
-
-
-/**
- * Locates a directory entry in a directory.
- *
- * @returns IPRT status code.
- * @retval  VERR_FILE_NOT_FOUND if not found.
- * @param   pThis           The directory to search.
- * @param   pszEntry        The entry to look for.
- * @param   poffDirRec      Where to return the offset of the directory record
- *                          on the disk.
- * @param   ppDirRec        Where to return the pointer to the directory record
- *                          (the whole directory is buffered).
- * @param   pcDirRecs       Where to return the number of directory records
- *                          related to this entry.
- * @param   pfMode          Where to return the file type, rock ridge adjusted.
- * @param   puVersion       Where to return the file version number.
- */
-static int rtFsIso9660Dir_FindEntry(PRTFSISO9660DIRSHRD pThis, const char *pszEntry,  uint64_t *poffDirRec,
-                                    PCISO9660DIRREC *ppDirRec, uint32_t *pcDirRecs, PRTFMODE pfMode, uint32_t *puVersion)
-{
-    /* Set return values. */
-    *poffDirRec = UINT64_MAX;
-    *ppDirRec   = NULL;
-    *pcDirRecs  = 1;
-    *pfMode     = UINT32_MAX;
-    *puVersion  = 0;
-
-    /*
-     * If we're in UTF-16BE mode, convert the input name to UTF-16BE.  Otherwise try
-     * uppercase it into a ISO 9660 compliant name.
-     */
-    int         rc;
-    bool const  fIsUtf16  = pThis->Core.pVol->fIsUtf16;
-    size_t      cwcEntry  = 0;
-    size_t      cbEntry   = 0;
-    size_t      cchUpper  = ~(size_t)0;
-    union
-    {
-        RTUTF16  wszEntry[260 + 1];
-        struct
-        {
-            char  szUpper[255 + 1];
-            char  szRock[260 + 1];
-        } s;
-    } uBuf;
-    if (fIsUtf16)
-    {
-        PRTUTF16 pwszEntry = uBuf.wszEntry;
-        rc = RTStrToUtf16BigEx(pszEntry, RTSTR_MAX, &pwszEntry, RT_ELEMENTS(uBuf.wszEntry), &cwcEntry);
-        if (RT_FAILURE(rc))
-            return rc;
-        cbEntry = cwcEntry * 2;
-    }
-    else
-    {
-        rc = RTStrCopy(uBuf.s.szUpper, sizeof(uBuf.s.szUpper), pszEntry);
-        if (RT_SUCCESS(rc))
-        {
-            RTStrToUpper(uBuf.s.szUpper);
-            cchUpper = strlen(uBuf.s.szUpper);
-        }
-    }
-
-    /*
-     * Scan the directory buffer by buffer.
-     */
-    uint32_t        offEntryInDir   = 0;
-    uint32_t const  cbDir           = pThis->Core.cbObject;
-    while (offEntryInDir + RT_UOFFSETOF(ISO9660DIRREC, achFileId) <= cbDir)
-    {
-        PCISO9660DIRREC pDirRec = (PCISO9660DIRREC)&pThis->pbDir[offEntryInDir];
-
-        /* If null length, skip to the next sector. */
-        if (pDirRec->cbDirRec == 0)
-            offEntryInDir = (offEntryInDir + pThis->Core.pVol->cbSector) & ~(pThis->Core.pVol->cbSector - 1U);
-        else
-        {
-            /* Try match the filename. */
-            if (fIsUtf16)
-            {
-                if (RT_LIKELY(!rtFsIso9660Dir_IsEntryEqualUtf16Big(pDirRec, uBuf.wszEntry, cbEntry, cwcEntry, puVersion)))
-                {
-                    /* Advance */
-                    offEntryInDir += pDirRec->cbDirRec;
-                    continue;
-                }
-            }
-            else
-            {
-                if (RT_LIKELY(!rtFsIso9660Dir_IsEntryEqualAscii(pDirRec, uBuf.s.szUpper, cchUpper, puVersion)))
-                {
-                    /** @todo check rock. */
-                    if (1)
-                    {
-                        /* Advance */
-                        offEntryInDir += pDirRec->cbDirRec;
-                        continue;
-                    }
-                }
-            }
-
-            *poffDirRec = pThis->Core.FirstExtent.offDisk + offEntryInDir;
-            *ppDirRec   = pDirRec;
-            *pfMode     = pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY
-                        ? 0755 | RTFS_TYPE_DIRECTORY | RTFS_DOS_DIRECTORY
-                        : 0644 | RTFS_TYPE_FILE;
-
-            /*
-             * Deal with the unlikely scenario of multi extent records.
-             */
-            if (!(pDirRec->fFileFlags & ISO9660_FILE_FLAGS_MULTI_EXTENT))
-                *pcDirRecs = 1;
-            else
-            {
-                offEntryInDir += pDirRec->cbDirRec;
-
-                uint32_t cDirRecs = 1;
-                while (offEntryInDir + RT_UOFFSETOF(ISO9660DIRREC, achFileId) <= cbDir)
-                {
-                    PCISO9660DIRREC pDirRec2 = (PCISO9660DIRREC)&pThis->pbDir[offEntryInDir];
-                    if (pDirRec2->cbDirRec != 0)
-                    {
-                        Assert(rtFsIso9660Dir_IsDirRecNextExtent(pDirRec, pDirRec2));
-                        cDirRecs++;
-                        if (!(pDirRec2->fFileFlags & ISO9660_FILE_FLAGS_MULTI_EXTENT))
-                            break;
-                        offEntryInDir += pDirRec2->cbDirRec;
-                    }
-                    else
-                        offEntryInDir = (offEntryInDir + pThis->Core.pVol->cbSector) & ~(pThis->Core.pVol->cbSector - 1U);
-                }
-
-                *pcDirRecs = cDirRecs;
-            }
-            return VINF_SUCCESS;
-        }
-    }
-
-    return VERR_FILE_NOT_FOUND;
-}
-
-
-/**
- * Releases a reference to a shared directory structure.
- *
- * @param   pShared             The shared directory structure.
- */
-static void rtFsIso9660DirShrd_Release(PRTFSISO9660DIRSHRD pShared)
-{
-    uint32_t cRefs = ASMAtomicDecU32(&pShared->Core.cRefs);
-    Assert(cRefs < UINT32_MAX / 2);
-    if (cRefs == 0)
-    {
-        LogFlow(("rtFsIso9660DirShrd_Release: Destroying shared structure %p\n", pShared));
-        Assert(pShared->Core.cRefs == 0);
-        if (pShared->pbDir)
-        {
-            RTMemFree(pShared->pbDir);
-            pShared->pbDir = NULL;
-        }
-        rtFsIso9660Core_Destroy(&pShared->Core);
-        RTMemFree(pShared);
-    }
-}
-
-
-/**
- * Retains a reference to a shared directory structure.
- *
- * @param   pShared             The shared directory structure.
- */
-static void rtFsIso9660DirShrd_Retain(PRTFSISO9660DIRSHRD pShared)
-{
-    uint32_t cRefs = ASMAtomicIncU32(&pShared->Core.cRefs);
-    Assert(cRefs > 1); NOREF(cRefs);
-}
-
-
-
-/**
- * @interface_method_impl{RTVFSOBJOPS,pfnClose}
- */
-static DECLCALLBACK(int) rtFsIso9660Dir_Close(void *pvThis)
-{
-    PRTFSISO9660DIROBJ pThis = (PRTFSISO9660DIROBJ)pvThis;
-    LogFlow(("rtFsIso9660Dir_Close(%p/%p)\n", pThis, pThis->pShared));
-
-    PRTFSISO9660DIRSHRD pShared = pThis->pShared;
-    pThis->pShared = NULL;
-    if (pShared)
-        rtFsIso9660DirShrd_Release(pShared);
-    return VINF_SUCCESS;
-}
-
-
-/**
- * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
- */
-static DECLCALLBACK(int) rtFsIso9660Dir_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
-{
-    PRTFSISO9660DIROBJ pThis = (PRTFSISO9660DIROBJ)pvThis;
-    return rtFsIso9660Core_QueryInfo(&pThis->pShared->Core, pObjInfo, enmAddAttr);
-}
-
-
-/**
- * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
- */
-static DECLCALLBACK(int) rtFsIso9660Dir_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
-{
-    RT_NOREF(pvThis, fMode, fMask);
-    return VERR_WRITE_PROTECT;
-}
-
-
-/**
- * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
- */
-static DECLCALLBACK(int) rtFsIso9660Dir_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
-                                                 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
-{
-    RT_NOREF(pvThis, pAccessTime, pModificationTime, pChangeTime, pBirthTime);
-    return VERR_WRITE_PROTECT;
-}
-
-
-/**
- * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
- */
-static DECLCALLBACK(int) rtFsIso9660Dir_SetOwner(void *pvThis, RTUID uid, RTGID gid)
-{
-    RT_NOREF(pvThis, uid, gid);
-    return VERR_WRITE_PROTECT;
-}
-
-
-/**
- * @interface_method_impl{RTVFSOBJOPS,pfnTraversalOpen}
- */
-static DECLCALLBACK(int) rtFsIso9660Dir_TraversalOpen(void *pvThis, const char *pszEntry, PRTVFSDIR phVfsDir,
-                                                      PRTVFSSYMLINK phVfsSymlink, PRTVFS phVfsMounted)
-{
-    /*
-     * We may have symbolic links if rock ridge is being used, though currently
-     * we won't have nested mounts.
-     */
-    int rc;
-    if (phVfsMounted)
-        *phVfsMounted = NIL_RTVFS;
-    if (phVfsDir || phVfsSymlink)
-    {
-        if (phVfsSymlink)
-            *phVfsSymlink = NIL_RTVFSSYMLINK;
-        if (phVfsDir)
-            *phVfsDir = NIL_RTVFSDIR;
-
-        PRTFSISO9660DIROBJ  pThis = (PRTFSISO9660DIROBJ)pvThis;
-        PRTFSISO9660DIRSHRD pShared = pThis->pShared;
-        PCISO9660DIRREC     pDirRec;
-        uint64_t            offDirRec;
-        uint32_t            cDirRecs;
-        RTFMODE             fMode;
-        uint32_t            uVersion;
-        rc = rtFsIso9660Dir_FindEntry(pShared, pszEntry, &offDirRec, &pDirRec, &cDirRecs, &fMode, &uVersion);
-        Log2(("rtFsIso9660Dir_TraversalOpen: FindEntry(,%s,) -> %Rrc\n", pszEntry, rc));
-        if (RT_SUCCESS(rc))
-        {
-            switch (fMode & RTFS_TYPE_MASK)
-            {
-                case RTFS_TYPE_DIRECTORY:
-                    if (phVfsDir)
-                        rc = rtFsIso9660Dir_New(pShared->Core.pVol, pShared, pDirRec, cDirRecs, offDirRec, phVfsDir);
-                    else
-                        rc = VERR_NOT_SYMLINK;
-                    break;
-
-                case RTFS_TYPE_SYMLINK:
-                    rc = VERR_NOT_IMPLEMENTED;
-                    break;
-                case RTFS_TYPE_FILE:
-                case RTFS_TYPE_DEV_BLOCK:
-                case RTFS_TYPE_DEV_CHAR:
-                case RTFS_TYPE_FIFO:
-                case RTFS_TYPE_SOCKET:
-                    rc = VERR_NOT_A_DIRECTORY;
-                    break;
-                default:
-                case RTFS_TYPE_WHITEOUT:
-                    rc = VERR_PATH_NOT_FOUND;
-                    break;
-            }
-        }
-        else if (rc == VERR_FILE_NOT_FOUND)
-            rc = VERR_PATH_NOT_FOUND;
-    }
-    else
-        rc = VERR_PATH_NOT_FOUND;
-    return rc;
-}
-
-
-/**
- * @interface_method_impl{RTVFSDIROPS,pfnOpenFile}
- */
-static DECLCALLBACK(int) rtFsIso9660Dir_OpenFile(void *pvThis, const char *pszFilename, uint32_t fOpen, PRTVFSFILE phVfsFile)
-{
-    PRTFSISO9660DIROBJ  pThis   = (PRTFSISO9660DIROBJ)pvThis;
-    PRTFSISO9660DIRSHRD pShared = pThis->pShared;
-
-    /*
-     * We cannot create or replace anything, just open stuff.
-     */
-    if (   (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_CREATE
-        || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_CREATE_REPLACE)
-        return VERR_WRITE_PROTECT;
-
-    /*
-     * Try open file.
-     */
-    PCISO9660DIRREC pDirRec;
-    uint64_t        offDirRec;
-    uint32_t        cDirRecs;
-    RTFMODE         fMode;
-    uint32_t        uVersion;
-    int rc = rtFsIso9660Dir_FindEntry(pShared, pszFilename, &offDirRec, &pDirRec, &cDirRecs, &fMode, &uVersion);
-    Log2(("rtFsIso9660Dir_OpenFile: FindEntry(,%s,) -> %Rrc\n", pszFilename, rc));
-    if (RT_SUCCESS(rc))
-    {
-        switch (fMode & RTFS_TYPE_MASK)
-        {
-            case RTFS_TYPE_FILE:
-                rc = rtFsIso9660File_New(pShared->Core.pVol, pShared, pDirRec, cDirRecs, offDirRec, fOpen, uVersion, phVfsFile);
-                break;
-
-            case RTFS_TYPE_SYMLINK:
-            case RTFS_TYPE_DEV_BLOCK:
-            case RTFS_TYPE_DEV_CHAR:
-            case RTFS_TYPE_FIFO:
-            case RTFS_TYPE_SOCKET:
-            case RTFS_TYPE_WHITEOUT:
-                rc = VERR_NOT_IMPLEMENTED;
-                break;
-
-            case RTFS_TYPE_DIRECTORY:
-                rc = VERR_NOT_A_FILE;
-                break;
-
-            default:
-                rc = VERR_PATH_NOT_FOUND;
-                break;
-        }
-    }
-    return rc;
-}
-
-
-/**
- * @interface_method_impl{RTVFSDIROPS,pfnOpenDir}
- */
-static DECLCALLBACK(int) rtFsIso9660Dir_OpenDir(void *pvThis, const char *pszSubDir, uint32_t fFlags, PRTVFSDIR phVfsDir)
-{
-    PRTFSISO9660DIROBJ  pThis   = (PRTFSISO9660DIROBJ)pvThis;
-    PRTFSISO9660DIRSHRD pShared = pThis->pShared;
-    AssertReturn(!fFlags, VERR_INVALID_FLAGS);
-
-    /*
-     * Try open file.
-     */
-    PCISO9660DIRREC pDirRec;
-    uint64_t        offDirRec;
-    uint32_t        cDirRecs;
-    RTFMODE         fMode;
-    uint32_t        uVersion;
-    int rc = rtFsIso9660Dir_FindEntry(pShared, pszSubDir, &offDirRec, &pDirRec, &cDirRecs, &fMode, &uVersion);
-    Log2(("rtFsIso9660Dir_OpenDir: FindEntry(,%s,) -> %Rrc\n", pszSubDir, rc));
-    if (RT_SUCCESS(rc))
-    {
-        switch (fMode & RTFS_TYPE_MASK)
-        {
-            case RTFS_TYPE_DIRECTORY:
-                rc = rtFsIso9660Dir_New(pShared->Core.pVol, pShared, pDirRec, cDirRecs, offDirRec, phVfsDir);
-                break;
-
-            case RTFS_TYPE_FILE:
-            case RTFS_TYPE_SYMLINK:
-            case RTFS_TYPE_DEV_BLOCK:
-            case RTFS_TYPE_DEV_CHAR:
-            case RTFS_TYPE_FIFO:
-            case RTFS_TYPE_SOCKET:
-            case RTFS_TYPE_WHITEOUT:
-                rc = VERR_NOT_A_DIRECTORY;
-                break;
-
-            default:
-                rc = VERR_PATH_NOT_FOUND;
-                break;
-        }
-    }
-    return rc;
-}
-
-
-/**
- * @interface_method_impl{RTVFSDIROPS,pfnCreateDir}
- */
-static DECLCALLBACK(int) rtFsIso9660Dir_CreateDir(void *pvThis, const char *pszSubDir, RTFMODE fMode, PRTVFSDIR phVfsDir)
-{
-    RT_NOREF(pvThis, pszSubDir, fMode, phVfsDir);
-    return VERR_WRITE_PROTECT;
-}
-
-
-/**
- * @interface_method_impl{RTVFSDIROPS,pfnOpenSymlink}
- */
-static DECLCALLBACK(int) rtFsIso9660Dir_OpenSymlink(void *pvThis, const char *pszSymlink, PRTVFSSYMLINK phVfsSymlink)
-{
-    RT_NOREF(pvThis, pszSymlink, phVfsSymlink);
-RTAssertMsg2("%s: %s\n", __FUNCTION__, pszSymlink);
-    return VERR_NOT_SUPPORTED;
-}
-
-
-/**
- * @interface_method_impl{RTVFSDIROPS,pfnCreateSymlink}
- */
-static DECLCALLBACK(int) rtFsIso9660Dir_CreateSymlink(void *pvThis, const char *pszSymlink, const char *pszTarget,
-                                                      RTSYMLINKTYPE enmType, PRTVFSSYMLINK phVfsSymlink)
-{
-    RT_NOREF(pvThis, pszSymlink, pszTarget, enmType, phVfsSymlink);
-    return VERR_WRITE_PROTECT;
-}
-
-
-/**
- * @interface_method_impl{RTVFSDIROPS,pfnQueryEntryInfo}
- */
-static DECLCALLBACK(int) rtFsIso9660Dir_QueryEntryInfo(void *pvThis, const char *pszEntry,
-                                                       PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
-{
-    /*
-     * Try locate the entry.
-     */
-    PRTFSISO9660DIROBJ  pThis   = (PRTFSISO9660DIROBJ)pvThis;
-    PRTFSISO9660DIRSHRD pShared = pThis->pShared;
-    PCISO9660DIRREC     pDirRec;
-    uint64_t            offDirRec;
-    uint32_t            cDirRecs;
-    RTFMODE             fMode;
-    uint32_t            uVersion;
-    int rc = rtFsIso9660Dir_FindEntry(pShared, pszEntry, &offDirRec, &pDirRec, &cDirRecs, &fMode, &uVersion);
-    Log2(("rtFsIso9660Dir_QueryEntryInfo: FindEntry(,%s,) -> %Rrc\n", pszEntry, rc));
-    if (RT_SUCCESS(rc))
-    {
-        /*
-         * To avoid duplicating code in rtFsIso9660Core_InitFromDirRec and
-         * rtFsIso9660Core_QueryInfo, we create a dummy RTFSISO9660CORE on the stack.
-         */
-        RTFSISO9660CORE TmpObj;
-        RT_ZERO(TmpObj);
-        rc = rtFsIso9660Core_InitFromDirRec(&TmpObj, pDirRec, cDirRecs, offDirRec, uVersion, pShared->Core.pVol);
-        if (RT_SUCCESS(rc))
-        {
-            rc = rtFsIso9660Core_QueryInfo(&TmpObj, pObjInfo, enmAddAttr);
-            RTMemFree(TmpObj.paExtents);
-        }
-    }
-    return rc;
-}
-
-
-/**
- * @interface_method_impl{RTVFSDIROPS,pfnUnlinkEntry}
- */
-static DECLCALLBACK(int) rtFsIso9660Dir_UnlinkEntry(void *pvThis, const char *pszEntry, RTFMODE fType)
-{
-    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;
-}
-
-
-/**
- * @interface_method_impl{RTVFSDIROPS,pfnRewindDir}
- */
-static DECLCALLBACK(int) rtFsIso9660Dir_RewindDir(void *pvThis)
-{
-    PRTFSISO9660DIROBJ pThis = (PRTFSISO9660DIROBJ)pvThis;
-    pThis->offDir = 0;
-    return VINF_SUCCESS;
-}
-
-
-/**
- * @interface_method_impl{RTVFSDIROPS,pfnReadDir}
- */
-static DECLCALLBACK(int) rtFsIso9660Dir_ReadDir(void *pvThis, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry,
-                                                RTFSOBJATTRADD enmAddAttr)
-{
-    PRTFSISO9660DIROBJ  pThis   = (PRTFSISO9660DIROBJ)pvThis;
-    PRTFSISO9660DIRSHRD pShared = pThis->pShared;
-
-    while (pThis->offDir + RT_UOFFSETOF(ISO9660DIRREC, achFileId) <= pShared->cbDir)
-    {
-        PCISO9660DIRREC pDirRec = (PCISO9660DIRREC)&pShared->pbDir[pThis->offDir];
-
-        /* If null length, skip to the next sector. */
-        if (pDirRec->cbDirRec == 0)
-            pThis->offDir = (pThis->offDir + pShared->Core.pVol->cbSector) & ~(pShared->Core.pVol->cbSector - 1U);
-        else
-        {
-            /*
-             * Do names first as they may cause overflows.
-             */
-            uint32_t uVersion = 0;
-            if (   pDirRec->bFileIdLength == 1
-                && pDirRec->achFileId[0]  == '\0')
-            {
-                if (*pcbDirEntry < RT_UOFFSETOF(RTDIRENTRYEX, szName) + 2)
-                {
-                    *pcbDirEntry = RT_UOFFSETOF(RTDIRENTRYEX, szName) + 2;
-                    Log3(("rtFsIso9660Dir_ReadDir: VERR_BUFFER_OVERFLOW (dot)\n"));
-                    return VERR_BUFFER_OVERFLOW;
-                }
-                pDirEntry->cbName    = 1;
-                pDirEntry->szName[0] = '.';
-                pDirEntry->szName[1] = '\0';
-            }
-            else if (   pDirRec->bFileIdLength == 1
-                     && pDirRec->achFileId[0]  == '\1')
-            {
-                if (*pcbDirEntry < RT_UOFFSETOF(RTDIRENTRYEX, szName) + 3)
-                {
-                    *pcbDirEntry = RT_UOFFSETOF(RTDIRENTRYEX, szName) + 3;
-                    Log3(("rtFsIso9660Dir_ReadDir: VERR_BUFFER_OVERFLOW (dot-dot)\n"));
-                    return VERR_BUFFER_OVERFLOW;
-                }
-                pDirEntry->cbName    = 2;
-                pDirEntry->szName[0] = '.';
-                pDirEntry->szName[1] = '.';
-                pDirEntry->szName[2] = '\0';
-            }
-            else if (pShared->Core.pVol->fIsUtf16)
-            {
-                PCRTUTF16 pawcSrc   = (PCRTUTF16)&pDirRec->achFileId[0];
-                size_t    cwcSrc    = pDirRec->bFileIdLength / sizeof(RTUTF16);
-                size_t    cwcVer    = !(pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY)
-                                    ? rtFsIso9660GetVersionLengthUtf16Big(pawcSrc, cwcSrc, &uVersion) : 0;
-                size_t    cchNeeded = 0;
-                size_t    cbDst     = *pcbDirEntry - RT_UOFFSETOF(RTDIRENTRYEX, szName);
-                char     *pszDst    = pDirEntry->szName;
-
-                int rc = RTUtf16BigToUtf8Ex(pawcSrc, cwcSrc - cwcVer, &pszDst, cbDst, &cchNeeded);
-                if (RT_SUCCESS(rc))
-                    pDirEntry->cbName = (uint16_t)cchNeeded;
-                else if (rc == VERR_BUFFER_OVERFLOW)
-                {
-                    *pcbDirEntry = RT_UOFFSETOF(RTDIRENTRYEX, szName) + cchNeeded + 1;
-                    Log3(("rtFsIso9660Dir_ReadDir: VERR_BUFFER_OVERFLOW - cbDst=%zu cchNeeded=%zu (UTF-16BE)\n", cbDst, cchNeeded));
-                    return VERR_BUFFER_OVERFLOW;
-                }
-                else
-                {
-                    ssize_t cchNeeded2 = RTStrPrintf2(pszDst, cbDst, "bad-name-%#x", pThis->offDir);
-                    if (cchNeeded2 >= 0)
-                        pDirEntry->cbName = (uint16_t)cchNeeded2;
-                    else
-                    {
-                        *pcbDirEntry = RT_UOFFSETOF(RTDIRENTRYEX, szName) + (size_t)-cchNeeded2;
-                        return VERR_BUFFER_OVERFLOW;
-                    }
-                }
-            }
-            else
-            {
-                /* This is supposed to be upper case ASCII, however, purge the encoding anyway. */
-                size_t cchVer   = !(pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY)
-                                ? rtFsIso9660GetVersionLengthAscii(pDirRec->achFileId, pDirRec->bFileIdLength, &uVersion) : 0;
-                size_t cchName  = pDirRec->bFileIdLength - cchVer;
-                size_t cbNeeded = RT_UOFFSETOF(RTDIRENTRYEX, szName) + cchName + 1;
-                if (*pcbDirEntry < cbNeeded)
-                {
-                    Log3(("rtFsIso9660Dir_ReadDir: VERR_BUFFER_OVERFLOW - cbDst=%zu cbNeeded=%zu (ASCII)\n", *pcbDirEntry, cbNeeded));
-                    *pcbDirEntry = cbNeeded;
-                    return VERR_BUFFER_OVERFLOW;
-                }
-                pDirEntry->cbName = (uint16_t)cchName;
-                memcpy(pDirEntry->szName, pDirRec->achFileId, cchName);
-                pDirEntry->szName[cchName] = '\0';
-                RTStrPurgeEncoding(pDirEntry->szName);
-
-                /** @todo check for rock ridge names here.   */
-            }
-            pDirEntry->cwcShortName    = 0;
-            pDirEntry->wszShortName[0] = '\0';
-
-            /*
-             * To avoid duplicating code in rtFsIso9660Core_InitFromDirRec and
-             * rtFsIso9660Core_QueryInfo, we create a dummy RTFSISO9660CORE on the stack.
-             */
-            RTFSISO9660CORE TmpObj;
-            RT_ZERO(TmpObj);
-            rtFsIso9660Core_InitFromDirRec(&TmpObj, pDirRec, 1 /* cDirRecs - see below why 1 */,
-                                           pThis->offDir + pShared->Core.FirstExtent.offDisk, uVersion, pShared->Core.pVol);
-            int rc = rtFsIso9660Core_QueryInfo(&TmpObj, &pDirEntry->Info, enmAddAttr);
-
-            /*
-             * Update the directory location and handle multi extent records.
-             *
-             * Multi extent records only affect the file size and the directory location,
-             * so we deal with it here instead of involving * rtFsIso9660Core_InitFromDirRec
-             * which would potentially require freeing memory and such.
-             */
-            if (!(pDirRec->fFileFlags & ISO9660_FILE_FLAGS_MULTI_EXTENT))
-            {
-                Log3(("rtFsIso9660Dir_ReadDir: offDir=%#07x: %s (rc=%Rrc)\n", pThis->offDir, pDirEntry->szName, rc));
-                pThis->offDir += pDirRec->cbDirRec;
-            }
-            else
-            {
-                uint32_t cExtents = 1;
-                uint32_t offDir   = pThis->offDir + pDirRec->cbDirRec;
-                while (offDir + RT_UOFFSETOF(ISO9660DIRREC, achFileId) <= pShared->cbDir)
-                {
-                    PCISO9660DIRREC pDirRec2 = (PCISO9660DIRREC)&pShared->pbDir[offDir];
-                    if (pDirRec2->cbDirRec != 0)
-                    {
-                        pDirEntry->Info.cbObject += ISO9660_GET_ENDIAN(&pDirRec2->cbData);
-                        offDir += pDirRec2->cbDirRec;
-                        cExtents++;
-                        if (!(pDirRec2->fFileFlags & ISO9660_FILE_FLAGS_MULTI_EXTENT))
-                            break;
-                    }
-                    else
-                        offDir = (offDir + pShared->Core.pVol->cbSector) & ~(pShared->Core.pVol->cbSector - 1U);
-                }
-                Log3(("rtFsIso9660Dir_ReadDir: offDir=%#07x, %u extents ending at %#07x: %s (rc=%Rrc)\n",
-                      pThis->offDir, cExtents, offDir, pDirEntry->szName, rc));
-                pThis->offDir = offDir;
-            }
-
-            return rc;
-        }
-    }
-
-    Log3(("rtFsIso9660Dir_ReadDir: offDir=%#07x: VERR_NO_MORE_FILES\n", pThis->offDir));
-    return VERR_NO_MORE_FILES;
-}
-
-
-/**
- * FAT file operations.
- */
-static const RTVFSDIROPS g_rtFsIso9660DirOps =
-{
-    { /* Obj */
-        RTVFSOBJOPS_VERSION,
-        RTVFSOBJTYPE_DIR,
-        "ISO 9660 Dir",
-        rtFsIso9660Dir_Close,
-        rtFsIso9660Dir_QueryInfo,
-        RTVFSOBJOPS_VERSION
-    },
-    RTVFSDIROPS_VERSION,
-    0,
-    { /* ObjSet */
-        RTVFSOBJSETOPS_VERSION,
-        RT_OFFSETOF(RTVFSDIROPS, Obj) - RT_OFFSETOF(RTVFSDIROPS, ObjSet),
-        rtFsIso9660Dir_SetMode,
-        rtFsIso9660Dir_SetTimes,
-        rtFsIso9660Dir_SetOwner,
-        RTVFSOBJSETOPS_VERSION
-    },
-    rtFsIso9660Dir_TraversalOpen,
-    rtFsIso9660Dir_OpenFile,
-    rtFsIso9660Dir_OpenDir,
-    rtFsIso9660Dir_CreateDir,
-    rtFsIso9660Dir_OpenSymlink,
-    rtFsIso9660Dir_CreateSymlink,
-    rtFsIso9660Dir_QueryEntryInfo,
-    rtFsIso9660Dir_UnlinkEntry,
-    rtFsIso9660Dir_RenameEntry,
-    rtFsIso9660Dir_RewindDir,
-    rtFsIso9660Dir_ReadDir,
-    RTVFSDIROPS_VERSION,
-};
-
-
-/**
- * Adds an open child to the parent directory's shared structure.
- *
- * Maintains an additional reference to the parent dir to prevent it from going
- * away.  If @a pDir is the root directory, it also ensures the volume is
- * referenced and sticks around until the last open object is gone.
- *
- * @param   pDir        The directory.
- * @param   pChild      The child being opened.
- * @sa      rtFsIso9660DirShrd_RemoveOpenChild
- */
-static void rtFsIso9660DirShrd_AddOpenChild(PRTFSISO9660DIRSHRD pDir, PRTFSISO9660CORE pChild)
-{
-    rtFsIso9660DirShrd_Retain(pDir);
-
-    RTListAppend(&pDir->OpenChildren, &pChild->Entry);
-    pChild->pParentDir = pDir;
-}
-
-
-/**
- * Removes an open child to the parent directory.
- *
- * @param   pDir        The directory.
- * @param   pChild      The child being removed.
- *
- * @remarks This is the very last thing you do as it may cause a few other
- *          objects to be released recursively (parent dir and the volume).
- *
- * @sa      rtFsIso9660DirShrd_AddOpenChild
- */
-static void rtFsIso9660DirShrd_RemoveOpenChild(PRTFSISO9660DIRSHRD pDir, PRTFSISO9660CORE pChild)
-{
-    AssertReturnVoid(pChild->pParentDir == pDir);
-    RTListNodeRemove(&pChild->Entry);
-    pChild->pParentDir = NULL;
-
-    rtFsIso9660DirShrd_Release(pDir);
-}
-
-
-#ifdef LOG_ENABLED
-/**
- * Logs the content of a directory.
- */
-static void rtFsIso9660DirShrd_LogContent(PRTFSISO9660DIRSHRD pThis)
-{
-    if (LogIs2Enabled())
-    {
-        uint32_t offRec = 0;
-        while (offRec < pThis->cbDir)
-        {
-            PCISO9660DIRREC pDirRec = (PCISO9660DIRREC)&pThis->pbDir[offRec];
-            if (pDirRec->cbDirRec == 0)
-                break;
-
-            RTUTF16 wszName[128];
-            if (pThis->Core.pVol->fIsUtf16)
-            {
-                PRTUTF16  pwszDst = &wszName[pDirRec->bFileIdLength / sizeof(RTUTF16)];
-                PCRTUTF16 pwszSrc = (PCRTUTF16)&pDirRec->achFileId[pDirRec->bFileIdLength];
-                pwszSrc--;
-                *pwszDst-- = '\0';
-                while ((uintptr_t)pwszDst >= (uintptr_t)&wszName[0])
-                {
-                    *pwszDst = RT_BE2H_U16(*pwszSrc);
-                    pwszDst--;
-                    pwszSrc--;
-                }
-            }
-            else
-            {
-                PRTUTF16 pwszDst = wszName;
-                for (uint32_t off = 0; off < pDirRec->bFileIdLength; off++)
-                    *pwszDst++ = pDirRec->achFileId[off];
-                *pwszDst = '\0';
-            }
-
-            Log2(("ISO9660:  %04x: rec=%#x ea=%#x cb=%#010RX32 off=%#010RX32 fl=%#04x %04u-%02u-%02u %02u:%02u:%02u%+03d unit=%#x igap=%#x idVol=%#x '%ls'\n",
-                  offRec,
-                  pDirRec->cbDirRec,
-                  pDirRec->cExtAttrBlocks,
-                  ISO9660_GET_ENDIAN(&pDirRec->cbData),
-                  ISO9660_GET_ENDIAN(&pDirRec->offExtent),
-                  pDirRec->fFileFlags,
-                  pDirRec->RecTime.bYear + 1900,
-                  pDirRec->RecTime.bMonth,
-                  pDirRec->RecTime.bDay,
-                  pDirRec->RecTime.bHour,
-                  pDirRec->RecTime.bMinute,
-                  pDirRec->RecTime.bSecond,
-                  pDirRec->RecTime.offUtc*4/60,
-                  pDirRec->bFileUnitSize,
-                  pDirRec->bInterleaveGapSize,
-                  ISO9660_GET_ENDIAN(&pDirRec->VolumeSeqNo),
-                  wszName));
-
-            uint32_t offSysUse = RT_OFFSETOF(ISO9660DIRREC, achFileId[pDirRec->bFileIdLength]) + !(pDirRec->bFileIdLength & 1);
-            if (offSysUse < pDirRec->cbDirRec)
-            {
-                Log2(("ISO9660:       system use (%#x bytes):\n%.*Rhxd\n", pDirRec->cbDirRec - offSysUse,
-                      pDirRec->cbDirRec - offSysUse, (uint8_t *)pDirRec + offSysUse));
-            }
-
-            /* advance */
-            offRec += pDirRec->cbDirRec;
-        }
-    }
-}
-#endif /* LOG_ENABLED */
-
-
-/**
- * Instantiates a new shared directory structure.
- *
- * @returns IPRT status code.
- * @param   pThis           The FAT volume instance.
- * @param   pParentDir      The parent directory.  This is NULL for the root
- *                          directory.
- * @param   pDirRec         The directory record.  Will access @a cDirRecs
- *                          records.
- * @param   cDirRecs        Number of directory records if more than one.
- * @param   offDirRec       The byte offset of the directory record.
- * @param   ppShared        Where to return the shared directory structure.
- */
-static int rtFsIso9660DirShrd_New(PRTFSISO9660VOL pThis, PRTFSISO9660DIRSHRD pParentDir, PCISO9660DIRREC pDirRec,
-                                  uint32_t cDirRecs, uint64_t offDirRec, PRTFSISO9660DIRSHRD *ppShared)
-{
-    /*
-     * Allocate a new structure and initialize it.
-     */
-    int rc = VERR_NO_MEMORY;
-    PRTFSISO9660DIRSHRD pShared = (PRTFSISO9660DIRSHRD)RTMemAllocZ(sizeof(*pShared));
-    if (pShared)
-    {
-        rc = rtFsIso9660Core_InitFromDirRec(&pShared->Core, pDirRec, cDirRecs, offDirRec, 0 /*uVersion*/, pThis);
-        if (RT_SUCCESS(rc))
-        {
-            RTListInit(&pShared->OpenChildren);
-            pShared->cbDir = ISO9660_GET_ENDIAN(&pDirRec->cbData);
-            pShared->pbDir = (uint8_t *)RTMemAllocZ(pShared->cbDir + 256);
-            if (pShared->pbDir)
-            {
-                rc = RTVfsFileReadAt(pThis->hVfsBacking, pShared->Core.FirstExtent.offDisk, pShared->pbDir, pShared->cbDir, NULL);
-                if (RT_SUCCESS(rc))
-                {
-#ifdef LOG_ENABLED
-                    rtFsIso9660DirShrd_LogContent(pShared);
-#endif
-
-                    /*
-                     * Link into parent directory so we can use it to update
-                     * our directory entry.
-                     */
-                    if (pParentDir)
-                        rtFsIso9660DirShrd_AddOpenChild(pParentDir, &pShared->Core);
-                    *ppShared = pShared;
-                    return VINF_SUCCESS;
-                }
-            }
-        }
-        RTMemFree(pShared);
-    }
-    *ppShared = NULL;
-    return rc;
-}
-
-
-/**
- * Instantiates a new directory with a shared structure presupplied.
- *
- * @returns IPRT status code.
- * @param   pThis           The FAT volume instance.
- * @param   pShared         Referenced pointer to the shared structure.  The
- *                          reference is always CONSUMED.
- * @param   phVfsDir        Where to return the directory handle.
- */
-static int rtFsIso9660Dir_NewWithShared(PRTFSISO9660VOL pThis, PRTFSISO9660DIRSHRD pShared, PRTVFSDIR phVfsDir)
-{
-    /*
-     * Create VFS object around the shared structure.
-     */
-    PRTFSISO9660DIROBJ pNewDir;
-    int rc = RTVfsNewDir(&g_rtFsIso9660DirOps, sizeof(*pNewDir), 0 /*fFlags*/, pThis->hVfsSelf,
-                         NIL_RTVFSLOCK /*use volume lock*/, phVfsDir, (void **)&pNewDir);
-    if (RT_SUCCESS(rc))
-    {
-        /*
-         * Look for existing shared object, create a new one if necessary.
-         * We CONSUME a reference to pShared here.
-         */
-        pNewDir->offDir  = 0;
-        pNewDir->pShared = pShared;
-        return VINF_SUCCESS;
-    }
-
-    rtFsIso9660DirShrd_Release(pShared);
-    *phVfsDir = NIL_RTVFSDIR;
-    return rc;
-}
-
-
-
-/**
- * Instantiates a new directory VFS instance, creating the shared structure as
- * necessary.
- *
- * @returns IPRT status code.
- * @param   pThis           The FAT volume instance.
- * @param   pParentDir      The parent directory.  This is NULL for the root
- *                          directory.
- * @param   pDirRec         The directory record.
- * @param   cDirRecs        Number of directory records if more than one.
- * @param   offDirRec       The byte offset of the directory record.
- * @param   phVfsDir        Where to return the directory handle.
- */
-static int  rtFsIso9660Dir_New(PRTFSISO9660VOL pThis, PRTFSISO9660DIRSHRD pParentDir, PCISO9660DIRREC pDirRec,
-                               uint32_t cDirRecs, uint64_t offDirRec, PRTVFSDIR phVfsDir)
-{
-    /*
-     * Look for existing shared object, create a new one if necessary.
-     */
-    int                 rc      = VINF_SUCCESS;
-    PRTFSISO9660DIRSHRD pShared = (PRTFSISO9660DIRSHRD)rtFsIso9660Dir_LookupShared(pParentDir, offDirRec);
-    if (!pShared)
-    {
-        rc = rtFsIso9660DirShrd_New(pThis, pParentDir, pDirRec, cDirRecs, offDirRec, &pShared);
-        if (RT_FAILURE(rc))
-        {
-            *phVfsDir = NIL_RTVFSDIR;
-            return rc;
-        }
-    }
-    return rtFsIso9660Dir_NewWithShared(pThis, pShared, phVfsDir);
-}
-
-
-
-/**
- * @interface_method_impl{RTVFSOBJOPS::Obj,pfnClose}
- */
-static DECLCALLBACK(int) rtFsIso9660Vol_Close(void *pvThis)
-{
-    PRTFSISO9660VOL pThis = (PRTFSISO9660VOL)pvThis;
-    Log(("rtFsIso9660Vol_Close(%p)\n", pThis));
-
-    if (pThis->pRootDir)
-    {
-        Assert(RTListIsEmpty(&pThis->pRootDir->OpenChildren));
-        Assert(pThis->pRootDir->Core.cRefs == 1);
-        rtFsIso9660DirShrd_Release(pThis->pRootDir);
-        pThis->pRootDir = NULL;
-    }
-
-    RTVfsFileRelease(pThis->hVfsBacking);
-    pThis->hVfsBacking = NIL_RTVFSFILE;
-
-    return VINF_SUCCESS;
-}
-
-
-/**
- * @interface_method_impl{RTVFSOBJOPS::Obj,pfnQueryInfo}
- */
-static DECLCALLBACK(int) rtFsIso9660Vol_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
-{
-    RT_NOREF(pvThis, pObjInfo, enmAddAttr);
-    return VERR_WRONG_TYPE;
-}
-
-
-/**
- * @interface_method_impl{RTVFSOPS,pfnOpenRoo}
- */
-static DECLCALLBACK(int) rtFsIso9660Vol_OpenRoot(void *pvThis, PRTVFSDIR phVfsDir)
-{
-    PRTFSISO9660VOL pThis = (PRTFSISO9660VOL)pvThis;
-
-    rtFsIso9660DirShrd_Retain(pThis->pRootDir); /* consumed by the next call */
-    return rtFsIso9660Dir_NewWithShared(pThis, pThis->pRootDir, phVfsDir);
-}
-
-
-/**
- * @interface_method_impl{RTVFSOPS,pfnIsRangeInUse}
- */
-static DECLCALLBACK(int) rtFsIso9660Vol_IsRangeInUse(void *pvThis, RTFOFF off, size_t cb, bool *pfUsed)
-{
-    RT_NOREF(pvThis, off, cb, pfUsed);
-    return VERR_NOT_IMPLEMENTED;
-}
-
-
-DECL_HIDDEN_CONST(const RTVFSOPS) g_rtFsIso9660VolOps =
-{
-    { /* Obj */
-        RTVFSOBJOPS_VERSION,
-        RTVFSOBJTYPE_VFS,
-        "ISO 9660",
-        rtFsIso9660Vol_Close,
-        rtFsIso9660Vol_QueryInfo,
-        RTVFSOBJOPS_VERSION
-    },
-    RTVFSOPS_VERSION,
-    0 /* fFeatures */,
-    rtFsIso9660Vol_OpenRoot,
-    rtFsIso9660Vol_IsRangeInUse,
-    RTVFSOPS_VERSION
-};
-
-
-
-#ifdef LOG_ENABLED
-
-/** Logging helper. */
-static size_t rtFsIso9660VolGetStrippedLength(const char *pachField, size_t cchField)
-{
-    while (cchField > 0 && pachField[cchField - 1] == ' ')
-        cchField--;
-    return cchField;
-}
-
-/** Logging helper. */
-static char *rtFsIso9660VolGetMaybeUtf16Be(const char *pachField, size_t cchField, char *pszDst, size_t cbDst)
-{
-    /* Check the format by looking for zero bytes.  ISO-9660 doesn't allow zeros.
-       This doesn't have to be a UTF-16BE string.  */
-    size_t cFirstZeros  = 0;
-    size_t cSecondZeros = 0;
-    for (size_t off = 0; off + 1 < cchField; off += 2)
-    {
-        cFirstZeros  += pachField[off]     == '\0';
-        cSecondZeros += pachField[off + 1] == '\0';
-    }
-
-    int    rc     = VINF_SUCCESS;
-    char  *pszTmp = &pszDst[10];
-    size_t cchRet = 0;
-    if (cFirstZeros > cSecondZeros)
-    {
-        /* UTF-16BE / UTC-2BE: */
-        if (cchField & 1)
-        {
-            if (pachField[cchField - 1] == '\0' || pachField[cchField - 1] == ' ')
-                cchField--;
-            else
-                rc = VERR_INVALID_UTF16_ENCODING;
-        }
-        if (RT_SUCCESS(rc))
-        {
-            while (   cchField >= 2
-                   && pachField[cchField - 1] == ' '
-                   && pachField[cchField - 2] == '\0')
-                cchField -= 2;
-
-            rc = RTUtf16BigToUtf8Ex((PCRTUTF16)pachField, cchField / sizeof(RTUTF16), &pszTmp, cbDst - 10 - 1, &cchRet);
-        }
-        if (RT_SUCCESS(rc))
-        {
-            pszDst[0] = 'U';
-            pszDst[1] = 'T';
-            pszDst[2] = 'F';
-            pszDst[3] = '-';
-            pszDst[4] = '1';
-            pszDst[5] = '6';
-            pszDst[6] = 'B';
-            pszDst[7] = 'E';
-            pszDst[8] = ':';
-            pszDst[9] = '\'';
-            pszDst[10 + cchRet] = '\'';
-            pszDst[10 + cchRet + 1] = '\0';
-        }
-        else
-            RTStrPrintf(pszDst, cbDst, "UTF-16BE: %.*Rhxs", cchField, pachField);
-    }
-    else if (cSecondZeros > 0)
-    {
-        /* Little endian UTF-16 / UCS-2 (ASSUMES host is little endian, sorry) */
-        if (cchField & 1)
-        {
-            if (pachField[cchField - 1] == '\0' || pachField[cchField - 1] == ' ')
-                cchField--;
-            else
-                rc = VERR_INVALID_UTF16_ENCODING;
-        }
-        if (RT_SUCCESS(rc))
-        {
-            while (   cchField >= 2
-                   && pachField[cchField - 1] == '\0'
-                   && pachField[cchField - 2] == ' ')
-                cchField -= 2;
-
-            rc = RTUtf16ToUtf8Ex((PCRTUTF16)pachField, cchField / sizeof(RTUTF16), &pszTmp, cbDst - 10 - 1, &cchRet);
-        }
-        if (RT_SUCCESS(rc))
-        {
-            pszDst[0] = 'U';
-            pszDst[1] = 'T';
-            pszDst[2] = 'F';
-            pszDst[3] = '-';
-            pszDst[4] = '1';
-            pszDst[5] = '6';
-            pszDst[6] = 'L';
-            pszDst[7] = 'E';
-            pszDst[8] = ':';
-            pszDst[9] = '\'';
-            pszDst[10 + cchRet] = '\'';
-            pszDst[10 + cchRet + 1] = '\0';
-        }
-        else
-            RTStrPrintf(pszDst, cbDst, "UTF-16LE: %.*Rhxs", cchField, pachField);
-    }
-    else
-    {
-        /* ASSUME UTF-8/ASCII. */
-        while (   cchField > 0
-               && pachField[cchField - 1] == ' ')
-            cchField--;
-        rc = RTStrValidateEncodingEx(pachField, cchField, RTSTR_VALIDATE_ENCODING_EXACT_LENGTH);
-        if (RT_SUCCESS(rc))
-            RTStrPrintf(pszDst, cbDst, "UTF-8: '%.*s'", cchField, pachField);
-        else
-            RTStrPrintf(pszDst, cbDst, "UNK-8: %.*Rhxs", cchField, pachField);
-    }
-    return pszDst;
-}
-
-
-/**
- * Logs the primary or supplementary volume descriptor
- *
- * @param   pVolDesc            The descriptor.
- */
-static void rtFsIso9660VolLogPrimarySupplementaryVolDesc(PCISO9660SUPVOLDESC pVolDesc)
-{
-    if (LogIs2Enabled())
-    {
-        char szTmp[384];
-        Log2(("ISO9660:  fVolumeFlags:              %#RX8\n", pVolDesc->fVolumeFlags));
-        Log2(("ISO9660:  achSystemId:               %s\n", rtFsIso9660VolGetMaybeUtf16Be(pVolDesc->achSystemId, sizeof(pVolDesc->achSystemId), szTmp, sizeof(szTmp)) ));
-        Log2(("ISO9660:  achVolumeId:               %s\n", rtFsIso9660VolGetMaybeUtf16Be(pVolDesc->achVolumeId, sizeof(pVolDesc->achVolumeId), szTmp, sizeof(szTmp)) ));
-        Log2(("ISO9660:  Unused73:                  {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->Unused73.be), RT_LE2H_U32(pVolDesc->Unused73.le)));
-        Log2(("ISO9660:  VolumeSpaceSize:           {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->VolumeSpaceSize.be), RT_LE2H_U32(pVolDesc->VolumeSpaceSize.le)));
-        Log2(("ISO9660:  abEscapeSequences:         '%.*s'\n", rtFsIso9660VolGetStrippedLength((char *)pVolDesc->abEscapeSequences, sizeof(pVolDesc->abEscapeSequences)), pVolDesc->abEscapeSequences));
-        Log2(("ISO9660:  cVolumesInSet:             {%#RX16,%#RX16}\n", RT_BE2H_U16(pVolDesc->cVolumesInSet.be), RT_LE2H_U16(pVolDesc->cVolumesInSet.le)));
-        Log2(("ISO9660:  VolumeSeqNo:               {%#RX16,%#RX16}\n", RT_BE2H_U16(pVolDesc->VolumeSeqNo.be), RT_LE2H_U16(pVolDesc->VolumeSeqNo.le)));
-        Log2(("ISO9660:  cbLogicalBlock:            {%#RX16,%#RX16}\n", RT_BE2H_U16(pVolDesc->cbLogicalBlock.be), RT_LE2H_U16(pVolDesc->cbLogicalBlock.le)));
-        Log2(("ISO9660:  cbPathTable:               {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->cbPathTable.be), RT_LE2H_U32(pVolDesc->cbPathTable.le)));
-        Log2(("ISO9660:  offTypeLPathTable:         %#RX32\n", RT_LE2H_U32(pVolDesc->offTypeLPathTable)));
-        Log2(("ISO9660:  offOptionalTypeLPathTable: %#RX32\n", RT_LE2H_U32(pVolDesc->offOptionalTypeLPathTable)));
-        Log2(("ISO9660:  offTypeMPathTable:         %#RX32\n", RT_BE2H_U32(pVolDesc->offTypeMPathTable)));
-        Log2(("ISO9660:  offOptionalTypeMPathTable: %#RX32\n", RT_BE2H_U32(pVolDesc->offOptionalTypeMPathTable)));
-        Log2(("ISO9660:  achVolumeSetId:            %s\n", rtFsIso9660VolGetMaybeUtf16Be(pVolDesc->achVolumeSetId, sizeof(pVolDesc->achVolumeSetId), szTmp, sizeof(szTmp)) ));
-        Log2(("ISO9660:  achPublisherId:            %s\n", rtFsIso9660VolGetMaybeUtf16Be(pVolDesc->achPublisherId, sizeof(pVolDesc->achPublisherId), szTmp, sizeof(szTmp)) ));
-        Log2(("ISO9660:  achDataPreparerId:         %s\n", rtFsIso9660VolGetMaybeUtf16Be(pVolDesc->achDataPreparerId, sizeof(pVolDesc->achDataPreparerId), szTmp, sizeof(szTmp)) ));
-        Log2(("ISO9660:  achApplicationId:          %s\n", rtFsIso9660VolGetMaybeUtf16Be(pVolDesc->achApplicationId, sizeof(pVolDesc->achApplicationId), szTmp, sizeof(szTmp)) ));
-        Log2(("ISO9660:  achCopyrightFileId:        %s\n", rtFsIso9660VolGetMaybeUtf16Be(pVolDesc->achCopyrightFileId, sizeof(pVolDesc->achCopyrightFileId), szTmp, sizeof(szTmp)) ));
-        Log2(("ISO9660:  achAbstractFileId:         %s\n", rtFsIso9660VolGetMaybeUtf16Be(pVolDesc->achAbstractFileId, sizeof(pVolDesc->achAbstractFileId), szTmp, sizeof(szTmp)) ));
-        Log2(("ISO9660:  achBibliographicFileId:    %s\n", rtFsIso9660VolGetMaybeUtf16Be(pVolDesc->achBibliographicFileId, sizeof(pVolDesc->achBibliographicFileId), szTmp, sizeof(szTmp)) ));
-        Log2(("ISO9660:  BirthTime:                 %.4s-%.2s-%.2s %.2s:%.2s:%.2s.%.2s%+03d\n",
-              pVolDesc->BirthTime.achYear,
-              pVolDesc->BirthTime.achMonth,
-              pVolDesc->BirthTime.achDay,
-              pVolDesc->BirthTime.achHour,
-              pVolDesc->BirthTime.achMinute,
-              pVolDesc->BirthTime.achSecond,
-              pVolDesc->BirthTime.achCentisecond,
-              pVolDesc->BirthTime.offUtc*4/60));
-        Log2(("ISO9660:  ModifyTime:                %.4s-%.2s-%.2s %.2s:%.2s:%.2s.%.2s%+03d\n",
-              pVolDesc->ModifyTime.achYear,
-              pVolDesc->ModifyTime.achMonth,
-              pVolDesc->ModifyTime.achDay,
-              pVolDesc->ModifyTime.achHour,
-              pVolDesc->ModifyTime.achMinute,
-              pVolDesc->ModifyTime.achSecond,
-              pVolDesc->ModifyTime.achCentisecond,
-              pVolDesc->ModifyTime.offUtc*4/60));
-        Log2(("ISO9660:  ExpireTime:                %.4s-%.2s-%.2s %.2s:%.2s:%.2s.%.2s%+03d\n",
-              pVolDesc->ExpireTime.achYear,
-              pVolDesc->ExpireTime.achMonth,
-              pVolDesc->ExpireTime.achDay,
-              pVolDesc->ExpireTime.achHour,
-              pVolDesc->ExpireTime.achMinute,
-              pVolDesc->ExpireTime.achSecond,
-              pVolDesc->ExpireTime.achCentisecond,
-              pVolDesc->ExpireTime.offUtc*4/60));
-        Log2(("ISO9660:  EffectiveTime:             %.4s-%.2s-%.2s %.2s:%.2s:%.2s.%.2s%+03d\n",
-              pVolDesc->EffectiveTime.achYear,
-              pVolDesc->EffectiveTime.achMonth,
-              pVolDesc->EffectiveTime.achDay,
-              pVolDesc->EffectiveTime.achHour,
-              pVolDesc->EffectiveTime.achMinute,
-              pVolDesc->EffectiveTime.achSecond,
-              pVolDesc->EffectiveTime.achCentisecond,
-              pVolDesc->EffectiveTime.offUtc*4/60));
-        Log2(("ISO9660:  bFileStructureVersion:     %#RX8\n", pVolDesc->bFileStructureVersion));
-        Log2(("ISO9660:  bReserved883:              %#RX8\n", pVolDesc->bReserved883));
-
-        Log2(("ISO9660:  RootDir.cbDirRec:                   %#RX8\n", pVolDesc->RootDir.DirRec.cbDirRec));
-        Log2(("ISO9660:  RootDir.cExtAttrBlocks:             %#RX8\n", pVolDesc->RootDir.DirRec.cExtAttrBlocks));
-        Log2(("ISO9660:  RootDir.offExtent:                  {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->RootDir.DirRec.offExtent.be), RT_LE2H_U32(pVolDesc->RootDir.DirRec.offExtent.le)));
-        Log2(("ISO9660:  RootDir.cbData:                     {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->RootDir.DirRec.cbData.be), RT_LE2H_U32(pVolDesc->RootDir.DirRec.cbData.le)));
-        Log2(("ISO9660:  RootDir.RecTime:                    %04u-%02u-%02u %02u:%02u:%02u%+03d\n",
-              pVolDesc->RootDir.DirRec.RecTime.bYear + 1900,
-              pVolDesc->RootDir.DirRec.RecTime.bMonth,
-              pVolDesc->RootDir.DirRec.RecTime.bDay,
-              pVolDesc->RootDir.DirRec.RecTime.bHour,
-              pVolDesc->RootDir.DirRec.RecTime.bMinute,
-              pVolDesc->RootDir.DirRec.RecTime.bSecond,
-              pVolDesc->RootDir.DirRec.RecTime.offUtc*4/60));
-        Log2(("ISO9660:  RootDir.RecTime.fFileFlags:         %RX8\n", pVolDesc->RootDir.DirRec.fFileFlags));
-        Log2(("ISO9660:  RootDir.RecTime.bFileUnitSize:      %RX8\n", pVolDesc->RootDir.DirRec.bFileUnitSize));
-        Log2(("ISO9660:  RootDir.RecTime.bInterleaveGapSize: %RX8\n", pVolDesc->RootDir.DirRec.bInterleaveGapSize));
-        Log2(("ISO9660:  RootDir.RecTime.VolumeSeqNo:        {%#RX16,%#RX16}\n", RT_BE2H_U16(pVolDesc->RootDir.DirRec.VolumeSeqNo.be), RT_LE2H_U16(pVolDesc->RootDir.DirRec.VolumeSeqNo.le)));
-        Log2(("ISO9660:  RootDir.RecTime.bFileIdLength:      %RX8\n", pVolDesc->RootDir.DirRec.bFileIdLength));
-        Log2(("ISO9660:  RootDir.RecTime.achFileId:          '%.*s'\n", pVolDesc->RootDir.DirRec.bFileIdLength, pVolDesc->RootDir.DirRec.achFileId));
-        uint32_t offSysUse = RT_OFFSETOF(ISO9660DIRREC, achFileId[pVolDesc->RootDir.DirRec.bFileIdLength])
-                           + !(pVolDesc->RootDir.DirRec.bFileIdLength & 1);
-        if (offSysUse < pVolDesc->RootDir.DirRec.cbDirRec)
-        {
-            Log2(("ISO9660:  RootDir System Use:\n%.*Rhxd\n",
-                  pVolDesc->RootDir.DirRec.cbDirRec - offSysUse, &pVolDesc->RootDir.ab[offSysUse]));
-        }
-    }
-}
-
-#endif /* LOG_ENABLED */
-
-/**
- * Deal with a root directory from a primary or supplemental descriptor.
- *
- * @returns IPRT status code.
- * @param   pThis           The ISO 9660 instance being initialized.
- * @param   pRootDir        The root directory record to check out.
- * @param   pDstRootDir     Where to store a copy of the root dir record.
- * @param   pErrInfo        Where to return additional error info.  Can be NULL.
- */
-static int rtFsIso9660VolHandleRootDir(PRTFSISO9660VOL pThis, PCISO9660DIRREC pRootDir,
-                                       PISO9660DIRREC pDstRootDir, PRTERRINFO pErrInfo)
-{
-    if (pRootDir->cbDirRec < RT_OFFSETOF(ISO9660DIRREC, achFileId))
-        return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Root dir record size is too small: %#x (min %#x)",
-                             pRootDir->cbDirRec, RT_OFFSETOF(ISO9660DIRREC, achFileId));
-
-    if (!(pRootDir->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY))
-        return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
-                             "Root dir is not flagged as directory: %#x", pRootDir->fFileFlags);
-    if (pRootDir->fFileFlags & ISO9660_FILE_FLAGS_MULTI_EXTENT)
-        return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
-                             "Root dir is cannot be multi-extent: %#x", pRootDir->fFileFlags);
-
-    if (RT_LE2H_U32(pRootDir->cbData.le) != RT_BE2H_U32(pRootDir->cbData.be))
-        return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid root dir size: {%#RX32,%#RX32}",
-                             RT_BE2H_U32(pRootDir->cbData.be), RT_LE2H_U32(pRootDir->cbData.le));
-    if (RT_LE2H_U32(pRootDir->cbData.le) == 0)
-        return RTErrInfoSet(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Zero sized root dir");
-
-    if (RT_LE2H_U32(pRootDir->offExtent.le) != RT_BE2H_U32(pRootDir->offExtent.be))
-        return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid root dir extent: {%#RX32,%#RX32}",
-                             RT_BE2H_U32(pRootDir->offExtent.be), RT_LE2H_U32(pRootDir->offExtent.le));
-
-    if (RT_LE2H_U16(pRootDir->VolumeSeqNo.le) != RT_BE2H_U16(pRootDir->VolumeSeqNo.be))
-        return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid root dir volume sequence ID: {%#RX16,%#RX16}",
-                             RT_BE2H_U16(pRootDir->VolumeSeqNo.be), RT_LE2H_U16(pRootDir->VolumeSeqNo.le));
-    if (RT_LE2H_U16(pRootDir->VolumeSeqNo.le) != pThis->idPrimaryVol)
-        return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
-                             "Expected root dir to have same volume sequence number as primary volume: %#x, expected %#x",
-                             RT_LE2H_U16(pRootDir->VolumeSeqNo.le), pThis->idPrimaryVol);
-
-    /*
-     * Seems okay, copy it.
-     */
-    *pDstRootDir = *pRootDir;
-    return VINF_SUCCESS;
-}
-
-
-/**
- * Deal with a primary volume descriptor.
- *
- * @returns IPRT status code.
- * @param   pThis           The ISO 9660 instance being initialized.
- * @param   pVolDesc        The volume descriptor to handle.
- * @param   offVolDesc      The disk offset of the volume descriptor.
- * @param   pRootDir        Where to return a copy of the root directory record.
- * @param   poffRootDirRec  Where to return the disk offset of the root dir.
- * @param   pErrInfo        Where to return additional error info.  Can be NULL.
- */
-static int rtFsIso9660VolHandlePrimaryVolDesc(PRTFSISO9660VOL pThis, PCISO9660PRIMARYVOLDESC pVolDesc, uint32_t offVolDesc,
-                                              PISO9660DIRREC pRootDir, uint64_t *poffRootDirRec, PRTERRINFO pErrInfo)
-{
-    if (pVolDesc->bFileStructureVersion != ISO9660_FILE_STRUCTURE_VERSION)
-        return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
-                             "Unsupported file structure version: %#x", pVolDesc->bFileStructureVersion);
-
-    /*
-     * We need the block size ...
-     */
-    pThis->cbBlock = RT_LE2H_U16(pVolDesc->cbLogicalBlock.le);
-    if (   pThis->cbBlock != RT_BE2H_U16(pVolDesc->cbLogicalBlock.be)
-        || !RT_IS_POWER_OF_TWO(pThis->cbBlock)
-        || pThis->cbBlock / pThis->cbSector < 1)
-        return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid logical block size: {%#RX16,%#RX16}",
-                             RT_BE2H_U16(pVolDesc->cbLogicalBlock.be), RT_LE2H_U16(pVolDesc->cbLogicalBlock.le));
-    if (pThis->cbBlock / pThis->cbSector > 128)
-        return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "Unsupported block size: %#x\n", pThis->cbBlock);
-
-    /*
-     * ... volume space size ...
-     */
-    pThis->cBlocksInPrimaryVolumeSpace = RT_LE2H_U32(pVolDesc->VolumeSpaceSize.le);
-    if (pThis->cBlocksInPrimaryVolumeSpace != RT_BE2H_U32(pVolDesc->VolumeSpaceSize.be))
-        return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid volume space size: {%#RX32,%#RX32}",
-                             RT_BE2H_U32(pVolDesc->VolumeSpaceSize.be), RT_LE2H_U32(pVolDesc->VolumeSpaceSize.le));
-    pThis->cbPrimaryVolumeSpace = pThis->cBlocksInPrimaryVolumeSpace * (uint64_t)pThis->cbBlock;
-
-    /*
-     * ... number of volumes in the set ...
-     */
-    pThis->cVolumesInSet = RT_LE2H_U16(pVolDesc->cVolumesInSet.le);
-    if (   pThis->cVolumesInSet != RT_BE2H_U16(pVolDesc->cVolumesInSet.be)
-        || pThis->cVolumesInSet == 0)
-        return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid volume set size: {%#RX16,%#RX16}",
-                             RT_BE2H_U16(pVolDesc->cVolumesInSet.be), RT_LE2H_U16(pVolDesc->cVolumesInSet.le));
-    if (pThis->cVolumesInSet > 32)
-        return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "Too large volume set size: %#x\n", pThis->cVolumesInSet);
-
-    /*
-     * ... primary volume sequence ID ...
-     */
-    pThis->idPrimaryVol = RT_LE2H_U16(pVolDesc->VolumeSeqNo.le);
-    if (pThis->idPrimaryVol != RT_BE2H_U16(pVolDesc->VolumeSeqNo.be))
-        return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid volume sequence ID: {%#RX16,%#RX16}",
-                             RT_BE2H_U16(pVolDesc->VolumeSeqNo.be), RT_LE2H_U16(pVolDesc->VolumeSeqNo.le));
-    if (   pThis->idPrimaryVol > pThis->cVolumesInSet
-        || pThis->idPrimaryVol < 1)
-        return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
-                             "Volume sequence ID out of of bound: %#x (1..%#x)\n", pThis->idPrimaryVol, pThis->cVolumesInSet);
-
-    /*
-     * ... and the root directory record.
-     */
-    *poffRootDirRec = offVolDesc + RT_OFFSETOF(ISO9660PRIMARYVOLDESC, RootDir.DirRec);
-    return rtFsIso9660VolHandleRootDir(pThis, &pVolDesc->RootDir.DirRec, pRootDir, pErrInfo);
-}
-
-
-/**
- * Deal with a supplementary volume descriptor.
- *
- * @returns IPRT status code.
- * @param   pThis           The ISO 9660 instance being initialized.
- * @param   pVolDesc        The volume descriptor to handle.
- * @param   offVolDesc      The disk offset of the volume descriptor.
- * @param   pbUcs2Level     Where to return the joliet level, if found. Caller
- *                          initializes this to zero, we'll return 1, 2 or 3 if
- *                          joliet was detected.
- * @param   pRootDir        Where to return the root directory, if found.
- * @param   poffRootDirRec  Where to return the disk offset of the root dir.
- * @param   pErrInfo        Where to return additional error info.  Can be NULL.
- */
-static int rtFsIso9660VolHandleSupplementaryVolDesc(PRTFSISO9660VOL pThis, PCISO9660SUPVOLDESC pVolDesc, uint32_t offVolDesc,
-                                                    uint8_t *pbUcs2Level, PISO9660DIRREC pRootDir, uint64_t *poffRootDirRec,
-                                                    PRTERRINFO pErrInfo)
-{
-    if (pVolDesc->bFileStructureVersion != ISO9660_FILE_STRUCTURE_VERSION)
-        return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
-                             "Unsupported file structure version: %#x", pVolDesc->bFileStructureVersion);
-
-    /*
-     * Is this a joliet volume descriptor?  If not, we probably don't need to
-     * care about it.
-     */
-    if (   pVolDesc->abEscapeSequences[0] != ISO9660_JOLIET_ESC_SEQ_0
-        || pVolDesc->abEscapeSequences[1] != ISO9660_JOLIET_ESC_SEQ_1
-        || (   pVolDesc->abEscapeSequences[2] != ISO9660_JOLIET_ESC_SEQ_2_LEVEL_1
-            && pVolDesc->abEscapeSequences[2] != ISO9660_JOLIET_ESC_SEQ_2_LEVEL_2
-            && pVolDesc->abEscapeSequences[2] != ISO9660_JOLIET_ESC_SEQ_2_LEVEL_3))
-        return VINF_SUCCESS;
-
-    /*
-     * Skip if joliet is unwanted.
-     */
-    if (pThis->fFlags & RTFSISO9660_F_NO_JOLIET)
-        return VINF_SUCCESS;
-
-    /*
-     * Check that the joliet descriptor matches the primary one.
-     * Note! These are our assumptions and may be wrong.
-     */
-    if (pThis->cbBlock == 0)
-        return RTErrInfoSet(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
-                            "Supplementary joliet volume descriptor is not supported when appearing before the primary volume descriptor");
-    if (ISO9660_GET_ENDIAN(&pVolDesc->cbLogicalBlock) != pThis->cbBlock)
-        return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
-                             "Logical block size for joliet volume descriptor differs from primary: %#RX16 vs %#RX16\n",
-                             ISO9660_GET_ENDIAN(&pVolDesc->cbLogicalBlock), pThis->cbBlock);
-    if (ISO9660_GET_ENDIAN(&pVolDesc->VolumeSpaceSize) != pThis->cBlocksInPrimaryVolumeSpace)
-        return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
-                             "Volume space size for joliet volume descriptor differs from primary: %#RX32 vs %#RX32\n",
-                             ISO9660_GET_ENDIAN(&pVolDesc->VolumeSpaceSize), pThis->cBlocksInPrimaryVolumeSpace);
-    if (ISO9660_GET_ENDIAN(&pVolDesc->cVolumesInSet) != pThis->cVolumesInSet)
-        return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
-                             "Volume set size for joliet volume descriptor differs from primary: %#RX16 vs %#RX32\n",
-                             ISO9660_GET_ENDIAN(&pVolDesc->cVolumesInSet), pThis->cVolumesInSet);
-    if (ISO9660_GET_ENDIAN(&pVolDesc->VolumeSeqNo) != pThis->idPrimaryVol)
-        return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
-                             "Volume sequence ID for joliet volume descriptor differs from primary: %#RX16 vs %#RX32\n",
-                             ISO9660_GET_ENDIAN(&pVolDesc->VolumeSeqNo), pThis->idPrimaryVol);
-
-    if (*pbUcs2Level != 0)
-        return RTErrInfoSet(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "More than one supplementary joliet volume descriptor");
-
-    /*
-     * Switch to the joliet root dir as it has UTF-16 stuff in it.
-     */
-    int rc = rtFsIso9660VolHandleRootDir(pThis, &pVolDesc->RootDir.DirRec, pRootDir, pErrInfo);
-    if (RT_SUCCESS(rc))
-    {
-        *poffRootDirRec = offVolDesc + RT_OFFSETOF(ISO9660SUPVOLDESC, RootDir.DirRec);
-        *pbUcs2Level    = pVolDesc->abEscapeSequences[2] == ISO9660_JOLIET_ESC_SEQ_2_LEVEL_1 ? 1
-                        : pVolDesc->abEscapeSequences[2] == ISO9660_JOLIET_ESC_SEQ_2_LEVEL_2 ? 2 : 3;
-        Log(("ISO9660: Joliet with UCS-2 level %u\n", *pbUcs2Level));
-    }
-    return rc;
-}
-
-
-
-/**
- * Worker for RTFsIso9660VolOpen.
- *
- * @returns IPRT status code.
- * @param   pThis           The FAT VFS instance to initialize.
- * @param   hVfsSelf        The FAT VFS handle (no reference consumed).
- * @param   hVfsBacking     The file backing the alleged FAT file system.
- *                          Reference is consumed (via rtFsIso9660Vol_Close).
- * @param   fFlags          Flags, MBZ.
- * @param   pErrInfo        Where to return additional error info.  Can be NULL.
- */
-static int rtFsIso9660VolTryInit(PRTFSISO9660VOL pThis, RTVFS hVfsSelf, RTVFSFILE hVfsBacking, uint32_t fFlags, PRTERRINFO pErrInfo)
-{
-    uint32_t const cbSector = 2048;
-
-    /*
-     * First initialize the state so that rtFsIso9660Vol_Destroy won't trip up.
-     */
-    pThis->hVfsSelf                     = hVfsSelf;
-    pThis->hVfsBacking                  = hVfsBacking; /* Caller referenced it for us, we consume it; rtFsIso9660Vol_Destroy releases it. */
-    pThis->cbBacking                    = 0;
-    pThis->fFlags                       = fFlags;
-    pThis->cbSector                     = cbSector;
-    pThis->cbBlock                      = 0;
-    pThis->cBlocksInPrimaryVolumeSpace  = 0;
-    pThis->cbPrimaryVolumeSpace         = 0;
-    pThis->cVolumesInSet                = 0;
-    pThis->idPrimaryVol                 = UINT32_MAX;
-    pThis->fIsUtf16                     = false;
-    pThis->pRootDir                     = NULL;
-
-    /*
-     * Get stuff that may fail.
-     */
-    int rc = RTVfsFileGetSize(hVfsBacking, &pThis->cbBacking);
-    if (RT_FAILURE(rc))
-        return rc;
-
-    /*
-     * Read the volume descriptors starting at logical sector 16.
-     */
-    union
-    {
-        uint8_t                 ab[_4K];
-        uint16_t                au16[_4K / 2];
-        uint32_t                au32[_4K / 4];
-        ISO9660VOLDESCHDR       VolDescHdr;
-        ISO9660BOOTRECORD       BootRecord;
-        ISO9660PRIMARYVOLDESC   PrimaryVolDesc;
-        ISO9660SUPVOLDESC       SupVolDesc;
-        ISO9660VOLPARTDESC      VolPartDesc;
-    } Buf;
-    RT_ZERO(Buf);
-
-    uint64_t        offRootDirRec           = UINT64_MAX;
-    ISO9660DIRREC   RootDir;
-    RT_ZERO(RootDir);
-
-    uint64_t        offJolietRootDirRec     = UINT64_MAX;
-    uint8_t         bJolietUcs2Level        = 0;
-    ISO9660DIRREC   JolietRootDir;
-    RT_ZERO(JolietRootDir);
-
-    uint32_t        cPrimaryVolDescs        = 0;
-    uint32_t        cSupplementaryVolDescs  = 0;
-    uint32_t        cBootRecordVolDescs     = 0;
-    uint32_t        offVolDesc              = 16 * cbSector;
-    for (uint32_t iVolDesc = 0; ; iVolDesc++, offVolDesc += cbSector)
-    {
-        if (iVolDesc > 32)
-            return RTErrInfoSet(pErrInfo, rc, "More than 32 volume descriptors, doesn't seem right...");
-
-        /* Read the next one and check the signature. */
-        rc = RTVfsFileReadAt(hVfsBacking, offVolDesc, &Buf, cbSector, NULL);
-        if (RT_FAILURE(rc))
-            return RTErrInfoSetF(pErrInfo, rc, "Unable to read volume descriptor #%u", iVolDesc);
-
-        if (   Buf.VolDescHdr.achStdId[0] != ISO9660VOLDESC_STD_ID_0
-            || Buf.VolDescHdr.achStdId[1] != ISO9660VOLDESC_STD_ID_1
-            || Buf.VolDescHdr.achStdId[2] != ISO9660VOLDESC_STD_ID_2
-            || Buf.VolDescHdr.achStdId[3] != ISO9660VOLDESC_STD_ID_3
-            || Buf.VolDescHdr.achStdId[4] != ISO9660VOLDESC_STD_ID_4)
-        {
-            if (!iVolDesc)
-                return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
-                                     "No ISO 9660 CD001 signature, instead found: %.5Rhxs", Buf.VolDescHdr.achStdId);
-            return RTErrInfoSet(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Missing terminator volume descriptor?");
-        }
-
-        /* Do type specific handling. */
-        Log(("ISO9660: volume desc #%u: type=%#x\n", iVolDesc, Buf.VolDescHdr.bDescType));
-        if (Buf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_PRIMARY)
-        {
-            cPrimaryVolDescs++;
-            if (Buf.VolDescHdr.bDescVersion != ISO9660PRIMARYVOLDESC_VERSION)
-                return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
-                                     "Unsupported primary volume descriptor version: %#x", Buf.VolDescHdr.bDescVersion);
-#ifdef LOG_ENABLED
-            rtFsIso9660VolLogPrimarySupplementaryVolDesc(&Buf.SupVolDesc);
-#endif
-            if (cPrimaryVolDescs > 1)
-                return RTErrInfoSet(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "More than one primary volume descriptor");
-            rc = rtFsIso9660VolHandlePrimaryVolDesc(pThis, &Buf.PrimaryVolDesc, offVolDesc, &RootDir, &offRootDirRec, pErrInfo);
-        }
-        else if (Buf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_SUPPLEMENTARY)
-        {
-            cSupplementaryVolDescs++;
-            if (Buf.VolDescHdr.bDescVersion != ISO9660SUPVOLDESC_VERSION)
-                return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
-                                     "Unsupported supplemental volume descriptor version: %#x", Buf.VolDescHdr.bDescVersion);
-#ifdef LOG_ENABLED
-            rtFsIso9660VolLogPrimarySupplementaryVolDesc(&Buf.SupVolDesc);
-#endif
-            rc = rtFsIso9660VolHandleSupplementaryVolDesc(pThis, &Buf.SupVolDesc, offVolDesc, &bJolietUcs2Level, &JolietRootDir,
-                                                          &offJolietRootDirRec, pErrInfo);
-        }
-        else if (Buf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_BOOT_RECORD)
-        {
-            cBootRecordVolDescs++;
-        }
-        else if (Buf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_TERMINATOR)
-        {
-            if (!cPrimaryVolDescs)
-                return RTErrInfoSet(pErrInfo, VERR_VFS_BOGUS_FORMAT, "No primary volume descriptor");
-            break;
-        }
-        else
-            return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
-                                 "Unknown volume descriptor: %#x", Buf.VolDescHdr.bDescType);
-        if (RT_FAILURE(rc))
-            return rc;
-    }
-
-    /*
-     * We may be faced with choosing between joliet and rock ridge (we won't
-     * have this choice when RTFSISO9660_F_NO_JOLIET is set).  The joliet
-     * option is generally favorable as we don't have to guess wrt to the file
-     * name encoding.  So, we'll pick that for now.
-     *
-     * Note! Should we change this preference for joliet, there fun wrt making sure
-     *       there really is rock ridge stuff in the primary volume as well as
-     *       making sure there really is anything of value in the primary volume.
-     */
-    if (bJolietUcs2Level != 0)
-    {
-        pThis->fIsUtf16 = true;
-        return rtFsIso9660DirShrd_New(pThis, NULL, &JolietRootDir, 1, offJolietRootDirRec, &pThis->pRootDir);
-    }
-    return rtFsIso9660DirShrd_New(pThis, NULL, &RootDir, 1, offRootDirRec, &pThis->pRootDir);
-}
-
-
-/**
- * Opens an ISO 9660 file system volume.
- *
- * @returns IPRT status code.
- * @param   hVfsFileIn      The file or device backing the volume.
- * @param   fFlags          RTFSISO9660_F_XXX.
- * @param   phVfs           Where to return the virtual file system handle.
- * @param   pErrInfo        Where to return additional error information.
- */
-RTDECL(int) RTFsIso9660VolOpen(RTVFSFILE hVfsFileIn, uint32_t fFlags, PRTVFS phVfs, PRTERRINFO pErrInfo)
-{
-    /*
-     * Quick input validation.
-     */
-    AssertPtrReturn(phVfs, VERR_INVALID_POINTER);
-    *phVfs = NIL_RTVFS;
-    AssertReturn(!(fFlags & ~RTFSISO9660_F_VALID_MASK), VERR_INVALID_FLAGS);
-
-    uint32_t cRefs = RTVfsFileRetain(hVfsFileIn);
-    AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
-
-    /*
-     * Create a new FAT VFS instance and try initialize it using the given input file.
-     */
-    RTVFS hVfs   = NIL_RTVFS;
-    void *pvThis = NULL;
-    int rc = RTVfsNew(&g_rtFsIso9660VolOps, sizeof(RTFSISO9660VOL), NIL_RTVFS, RTVFSLOCK_CREATE_RW, &hVfs, &pvThis);
-    if (RT_SUCCESS(rc))
-    {
-        rc = rtFsIso9660VolTryInit((PRTFSISO9660VOL)pvThis, hVfs, hVfsFileIn, fFlags, pErrInfo);
-        if (RT_SUCCESS(rc))
-            *phVfs = hVfs;
-        else
-            RTVfsRelease(hVfs);
-    }
-    else
-        RTVfsFileRelease(hVfsFileIn);
-    return rc;
-}
-
-
-/**
- * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnValidate}
- */
-static DECLCALLBACK(int) rtVfsChainIso9660Vol_Validate(PCRTVFSCHAINELEMENTREG pProviderReg, PRTVFSCHAINSPEC pSpec,
-                                                       PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError, PRTERRINFO pErrInfo)
-{
-    RT_NOREF(pProviderReg, pSpec);
-
-    /*
-     * Basic checks.
-     */
-    if (pElement->enmTypeIn != RTVFSOBJTYPE_FILE)
-        return pElement->enmTypeIn == RTVFSOBJTYPE_INVALID ? VERR_VFS_CHAIN_CANNOT_BE_FIRST_ELEMENT : VERR_VFS_CHAIN_TAKES_FILE;
-    if (   pElement->enmType != RTVFSOBJTYPE_VFS
-        && pElement->enmType != RTVFSOBJTYPE_DIR)
-        return VERR_VFS_CHAIN_ONLY_DIR_OR_VFS;
-    if (pElement->cArgs > 1)
-        return VERR_VFS_CHAIN_AT_MOST_ONE_ARG;
-
-    /*
-     * Parse the flag if present, save in pElement->uProvider.
-     */
-    uint32_t fFlags = 0;
-    if (pElement->cArgs > 0)
-    {
-        for (uint32_t iArg = 0; iArg < pElement->cArgs; iArg++)
-        {
-            const char *psz = pElement->paArgs[iArg].psz;
-            if (*psz)
-            {
-                if (!strcmp(psz, "nojoliet"))
-                    fFlags |= RTFSISO9660_F_NO_JOLIET;
-                else if (!strcmp(psz, "norock"))
-                    fFlags |= RTFSISO9660_F_NO_ROCK;
-                else
-                {
-                    *poffError = pElement->paArgs[iArg].offSpec;
-                    return RTErrInfoSet(pErrInfo, VERR_VFS_CHAIN_INVALID_ARGUMENT, "Only knows: 'nojoliet' and 'norock'");
-                }
-            }
-        }
-    }
-
-    pElement->uProvider = fFlags;
-    return VINF_SUCCESS;
-}
-
-
-/**
- * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnInstantiate}
- */
-static DECLCALLBACK(int) rtVfsChainIso9660Vol_Instantiate(PCRTVFSCHAINELEMENTREG pProviderReg, PCRTVFSCHAINSPEC pSpec,
-                                                          PCRTVFSCHAINELEMSPEC pElement, RTVFSOBJ hPrevVfsObj,
-                                                          PRTVFSOBJ phVfsObj, uint32_t *poffError, PRTERRINFO pErrInfo)
-{
-    RT_NOREF(pProviderReg, pSpec, poffError);
-
-    int         rc;
-    RTVFSFILE   hVfsFileIn = RTVfsObjToFile(hPrevVfsObj);
-    if (hVfsFileIn != NIL_RTVFSFILE)
-    {
-        RTVFS hVfs;
-        rc = RTFsIso9660VolOpen(hVfsFileIn, pElement->uProvider, &hVfs, pErrInfo);
-        RTVfsFileRelease(hVfsFileIn);
-        if (RT_SUCCESS(rc))
-        {
-            *phVfsObj = RTVfsObjFromVfs(hVfs);
-            RTVfsRelease(hVfs);
-            if (*phVfsObj != NIL_RTVFSOBJ)
-                return VINF_SUCCESS;
-            rc = VERR_VFS_CHAIN_CAST_FAILED;
-        }
-    }
-    else
-        rc = VERR_VFS_CHAIN_CAST_FAILED;
-    return rc;
-}
-
-
-/**
- * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnCanReuseElement}
- */
-static DECLCALLBACK(bool) rtVfsChainIso9660Vol_CanReuseElement(PCRTVFSCHAINELEMENTREG pProviderReg,
-                                                               PCRTVFSCHAINSPEC pSpec, PCRTVFSCHAINELEMSPEC pElement,
-                                                               PCRTVFSCHAINSPEC pReuseSpec, PCRTVFSCHAINELEMSPEC pReuseElement)
-{
-    RT_NOREF(pProviderReg, pSpec, pReuseSpec);
-    if (   pElement->paArgs[0].uProvider == pReuseElement->paArgs[0].uProvider
-        || !pReuseElement->paArgs[0].uProvider)
-        return true;
-    return false;
-}
-
-
-/** VFS chain element 'file'. */
-static RTVFSCHAINELEMENTREG g_rtVfsChainIso9660VolReg =
-{
-    /* uVersion = */            RTVFSCHAINELEMENTREG_VERSION,
-    /* fReserved = */           0,
-    /* pszName = */             "isofs",
-    /* ListEntry = */           { NULL, NULL },
-    /* pszHelp = */             "Open a ISO 9660 file system, requires a file object on the left side.\n"
-                                "The 'nojoliet' option make it ignore any joliet supplemental volume.\n"
-                                "The 'norock' option make it ignore any rock ridge info.\n",
-    /* pfnValidate = */         rtVfsChainIso9660Vol_Validate,
-    /* pfnInstantiate = */      rtVfsChainIso9660Vol_Instantiate,
-    /* pfnCanReuseElement = */  rtVfsChainIso9660Vol_CanReuseElement,
-    /* uEndMarker = */          RTVFSCHAINELEMENTREG_VERSION
-};
-
-RTVFSCHAIN_AUTO_REGISTER_ELEMENT_PROVIDER(&g_rtVfsChainIso9660VolReg, rtVfsChainIso9660VolReg);
-
Index: /trunk/src/VBox/Runtime/common/fs/isovfs.cpp
===================================================================
--- /trunk/src/VBox/Runtime/common/fs/isovfs.cpp	(revision 68345)
+++ /trunk/src/VBox/Runtime/common/fs/isovfs.cpp	(revision 68345)
@@ -0,0 +1,2799 @@
+/* $Id$ */
+/** @file
+ * IPRT - ISO 9660 Virtual Filesystem.
+ */
+
+/*
+ * Copyright (C) 2017 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+
+/*********************************************************************************************************************************
+*   Header Files                                                                                                                 *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_FS
+#include "internal/iprt.h"
+#include <iprt/fsvfs.h>
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/ctype.h>
+#include <iprt/file.h>
+#include <iprt/log.h>
+#include <iprt/mem.h>
+#include <iprt/poll.h>
+#include <iprt/string.h>
+#include <iprt/thread.h>
+#include <iprt/vfs.h>
+#include <iprt/vfslowlevel.h>
+#include <iprt/formats/iso9660.h>
+
+
+
+/*********************************************************************************************************************************
+*   Structures and Typedefs                                                                                                      *
+*********************************************************************************************************************************/
+/** Pointer to an ISO 9660 volume (VFS instance data). */
+typedef struct RTFSISO9660VOL *PRTFSISO9660VOL;
+/** Pointer to a const ISO 9660 volume (VFS instance data). */
+typedef struct RTFSISO9660VOL const *PCRTFSISO9660VOL;
+
+/** Pointer to a ISO 9660 directory instance. */
+typedef struct RTFSISO9660DIRSHRD *PRTFSISO9660DIRSHRD;
+
+
+
+/**
+ * ISO 9660 extent (internal to the VFS not a disk structure).
+ */
+typedef struct RTFSISO9660EXTENT
+{
+    /** The disk offset. */
+    uint64_t            offDisk;
+    /** The size of the extent in bytes. */
+    uint64_t            cbExtent;
+} RTFSISO9660EXTENT;
+/** Pointer to an ISO 9660 extent. */
+typedef RTFSISO9660EXTENT *PRTFSISO9660EXTENT;
+/** Pointer to a const ISO 9660 extent. */
+typedef RTFSISO9660EXTENT const *PCRTFSISO9660EXTENT;
+
+
+/**
+ * ISO 9660 file system object, shared part.
+ */
+typedef struct RTFSISO9660CORE
+{
+    /** The parent directory keeps a list of open objects (RTFSISO9660CORE). */
+    RTLISTNODE          Entry;
+    /** Reference counter.   */
+    uint32_t volatile   cRefs;
+    /** The parent directory (not released till all children are close). */
+    PRTFSISO9660DIRSHRD pParentDir;
+    /** The byte offset of the first directory record. */
+    uint64_t            offDirRec;
+    /** Attributes. */
+    RTFMODE             fAttrib;
+    /** The object size. */
+    uint64_t            cbObject;
+    /** The access time. */
+    RTTIMESPEC          AccessTime;
+    /** The modificaton time. */
+    RTTIMESPEC          ModificationTime;
+    /** The change time. */
+    RTTIMESPEC          ChangeTime;
+    /** The birth time. */
+    RTTIMESPEC          BirthTime;
+    /** Pointer to the volume. */
+    PRTFSISO9660VOL     pVol;
+    /** The version number. */
+    uint32_t            uVersion;
+    /** Number of extents. */
+    uint32_t            cExtents;
+    /** The first extent. */
+    RTFSISO9660EXTENT   FirstExtent;
+    /** Array of additional extents. */
+    PRTFSISO9660EXTENT  paExtents;
+} RTFSISO9660CORE;
+typedef RTFSISO9660CORE *PRTFSISO9660CORE;
+
+/**
+ * ISO 9660 file, shared data.
+ */
+typedef struct RTFSISO9660FILESHRD
+{
+    /** Core ISO9660 object info.  */
+    RTFSISO9660CORE     Core;
+} RTFSISO9660FILESHRD;
+/** Pointer to a ISO 9660 file object. */
+typedef RTFSISO9660FILESHRD *PRTFSISO9660FILESHRD;
+
+
+/**
+ * ISO 9660 directory, shared data.
+ *
+ * We will always read in the whole directory just to keep things really simple.
+ */
+typedef struct RTFSISO9660DIRSHRD
+{
+    /** Core ISO 9660 object info.  */
+    RTFSISO9660CORE     Core;
+    /** Open child objects (RTFSISO9660CORE). */
+    RTLISTNODE          OpenChildren;
+
+    /** Pointer to the directory content. */
+    uint8_t            *pbDir;
+    /** The size of the directory content (duplicate of Core.cbObject). */
+    uint32_t            cbDir;
+} RTFSISO9660DIRSHRD;
+/** Pointer to a ISO 9660 directory instance. */
+typedef RTFSISO9660DIRSHRD *PRTFSISO9660DIRSHRD;
+
+
+/**
+ * Private data for a VFS file object.
+ */
+typedef struct RTFSISO9660FILEOBJ
+{
+    /** Pointer to the shared data. */
+    PRTFSISO9660FILESHRD    pShared;
+    /** The current file offset. */
+    uint64_t                offFile;
+} RTFSISO9660FILEOBJ;
+typedef RTFSISO9660FILEOBJ *PRTFSISO9660FILEOBJ;
+
+/**
+ * Private data for a VFS directory object.
+ */
+typedef struct RTFSISO9660DIROBJ
+{
+    /** Pointer to the shared data. */
+    PRTFSISO9660DIRSHRD     pShared;
+    /** The current directory offset. */
+    uint32_t                offDir;
+} RTFSISO9660DIROBJ;
+typedef RTFSISO9660DIROBJ *PRTFSISO9660DIROBJ;
+
+
+/**
+ * A ISO 9660 volume.
+ */
+typedef struct RTFSISO9660VOL
+{
+    /** Handle to itself. */
+    RTVFS                   hVfsSelf;
+    /** The file, partition, or whatever backing the ISO 9660 volume. */
+    RTVFSFILE               hVfsBacking;
+    /** The size of the backing thingy. */
+    uint64_t                cbBacking;
+    /** Flags. */
+    uint32_t                fFlags;
+    /** The sector size (in bytes). */
+    uint32_t                cbSector;
+    /** The size of a logical block in bytes. */
+    uint32_t                cbBlock;
+    /** The primary volume space size in blocks. */
+    uint32_t                cBlocksInPrimaryVolumeSpace;
+    /** The primary volume space size in bytes. */
+    uint64_t                cbPrimaryVolumeSpace;
+    /** The number of volumes in the set. */
+    uint32_t                cVolumesInSet;
+    /** The primary volume sequence ID. */
+    uint32_t                idPrimaryVol;
+    /** Set if using UTF16-2 (joliet). */
+    bool                    fIsUtf16;
+
+    /** The root directory shared data. */
+    PRTFSISO9660DIRSHRD     pRootDir;
+} RTFSISO9660VOL;
+
+
+
+/*********************************************************************************************************************************
+*   Global Variables                                                                                                             *
+*********************************************************************************************************************************/
+
+
+/*********************************************************************************************************************************
+*   Internal Functions                                                                                                           *
+*********************************************************************************************************************************/
+static void rtFsIso9660DirShrd_AddOpenChild(PRTFSISO9660DIRSHRD pDir, PRTFSISO9660CORE pChild);
+static void rtFsIso9660DirShrd_RemoveOpenChild(PRTFSISO9660DIRSHRD pDir, PRTFSISO9660CORE pChild);
+static int  rtFsIso9660Dir_New(PRTFSISO9660VOL pThis, PRTFSISO9660DIRSHRD pParentDir, PCISO9660DIRREC pDirRec,
+                               uint32_t cDirRecs, uint64_t offDirRec, PRTVFSDIR phVfsDir);
+static PRTFSISO9660CORE rtFsIso9660Dir_LookupShared(PRTFSISO9660DIRSHRD pThis, uint64_t offDirRec);
+
+
+/**
+ * Returns the length of the version suffix in the given name.
+ *
+ * @returns Number of UTF16-BE chars in the version suffix.
+ * @param   pawcName        The name to examine.
+ * @param   cwcName         The length of the name.
+ * @param   puValue         Where to return the value.
+ */
+static size_t rtFsIso9660GetVersionLengthUtf16Big(PCRTUTF16 pawcName, size_t cwcName, uint32_t *puValue)
+{
+    *puValue = 0;
+
+    /* -1: */
+    if (cwcName <= 2)
+        return 0;
+    RTUTF16 wc1 = RT_BE2H_U16(pawcName[cwcName - 1]);
+    if (!RT_C_IS_DIGIT(wc1))
+        return 0;
+    Assert(wc1 < 0x3a); /* ASSUMES the RT_C_IS_DIGIT macro works just fine on wide chars too. */
+
+    /* -2: */
+    RTUTF16 wc2 = RT_BE2H_U16(pawcName[cwcName - 2]);
+    if (wc2 == ';')
+    {
+        *puValue = wc1 - '0';
+        return 2;
+    }
+    if (!RT_C_IS_DIGIT(wc2) || cwcName <= 3)
+        return 0;
+
+    /* -3: */
+    RTUTF16 wc3 = RT_BE2H_U16(pawcName[cwcName - 3]);
+    if (wc3 == ';')
+    {
+        *puValue = (wc1 - '0')
+                 + (wc2 - '0') * 10;
+        return 3;
+    }
+    if (!RT_C_IS_DIGIT(wc3) || cwcName <= 4)
+        return 0;
+
+    /* -4: */
+    RTUTF16 wc4 = RT_BE2H_U16(pawcName[cwcName - 4]);
+    if (wc4 == ';')
+    {
+        *puValue = (wc1 - '0')
+                 + (wc2 - '0') * 10
+                 + (wc3 - '0') * 100;
+        return 4;
+    }
+    if (!RT_C_IS_DIGIT(wc4) || cwcName <= 5)
+        return 0;
+
+    /* -5: */
+    RTUTF16 wc5 = RT_BE2H_U16(pawcName[cwcName - 5]);
+    if (wc5 == ';')
+    {
+        *puValue = (wc1 - '0')
+                 + (wc2 - '0') * 10
+                 + (wc3 - '0') * 100
+                 + (wc4 - '0') * 1000;
+        return 5;
+    }
+    if (!RT_C_IS_DIGIT(wc5) || cwcName <= 6)
+        return 0;
+
+    /* -6: */
+    RTUTF16 wc6 = RT_BE2H_U16(pawcName[cwcName - 6]);
+    if (wc6 == ';')
+    {
+        *puValue = (wc1 - '0')
+                 + (wc2 - '0') * 10
+                 + (wc3 - '0') * 100
+                 + (wc4 - '0') * 1000
+                 + (wc5 - '0') * 10000;
+        return 6;
+    }
+    return 0;
+}
+
+
+/**
+ * Returns the length of the version suffix in the given name.
+ *
+ * @returns Number of chars in the version suffix.
+ * @param   pachName        The name to examine.
+ * @param   cchName         The length of the name.
+ * @param   puValue         Where to return the value.
+ */
+static size_t rtFsIso9660GetVersionLengthAscii(const char *pachName, size_t cchName, uint32_t *puValue)
+{
+    *puValue = 0;
+
+    /* -1: */
+    if (cchName <= 2)
+        return 0;
+    char ch1 = pachName[cchName - 1];
+    if (!RT_C_IS_DIGIT(ch1))
+        return 0;
+
+    /* -2: */
+    char ch2 = pachName[cchName - 2];
+    if (ch2 == ';')
+    {
+        *puValue = ch1 - '0';
+        return 2;
+    }
+    if (!RT_C_IS_DIGIT(ch2) || cchName <= 3)
+        return 0;
+
+    /* -3: */
+    char ch3 = pachName[cchName - 3];
+    if (ch3 == ';')
+    {
+        *puValue = (ch1 - '0')
+                 + (ch2 - '0') * 10;
+        return 3;
+    }
+    if (!RT_C_IS_DIGIT(ch3) || cchName <= 4)
+        return 0;
+
+    /* -4: */
+    char ch4 = pachName[cchName - 4];
+    if (ch4 == ';')
+    {
+        *puValue = (ch1 - '0')
+                 + (ch2 - '0') * 10
+                 + (ch3 - '0') * 100;
+        return 4;
+    }
+    if (!RT_C_IS_DIGIT(ch4) || cchName <= 5)
+        return 0;
+
+    /* -5: */
+    char ch5 = pachName[cchName - 5];
+    if (ch5 == ';')
+    {
+        *puValue = (ch1 - '0')
+                 + (ch2 - '0') * 10
+                 + (ch3 - '0') * 100
+                 + (ch4 - '0') * 1000;
+        return 5;
+    }
+    if (!RT_C_IS_DIGIT(ch5) || cchName <= 6)
+        return 0;
+
+    /* -6: */
+    if (pachName[cchName - 6] == ';')
+    {
+        *puValue = (ch1 - '0')
+                 + (ch2 - '0') * 10
+                 + (ch3 - '0') * 100
+                 + (ch4 - '0') * 1000
+                 + (ch5 - '0') * 10000;
+        return 6;
+    }
+    return 0;
+}
+
+
+/**
+ * Converts a ISO 9660 binary timestamp into an IPRT timesspec.
+ *
+ * @param   pTimeSpec       Where to return the IRPT time.
+ * @param   pIso9660        The ISO 9660 binary timestamp.
+ */
+static void rtFsIso9660DateTime2TimeSpec(PRTTIMESPEC pTimeSpec, PCISO9660RECTIMESTAMP pIso9660)
+{
+    RTTIME Time;
+    Time.fFlags         = RTTIME_FLAGS_TYPE_UTC;
+    Time.offUTC         = 0;
+    Time.i32Year        = pIso9660->bYear + 1900;
+    Time.u8Month        = RT_MIN(RT_MAX(pIso9660->bMonth, 1), 12);
+    Time.u8MonthDay     = RT_MIN(RT_MAX(pIso9660->bDay, 1), 31);
+    Time.u8WeekDay      = UINT8_MAX;
+    Time.u16YearDay     = 0;
+    Time.u8Hour         = RT_MIN(pIso9660->bHour, 23);
+    Time.u8Minute       = RT_MIN(pIso9660->bMinute, 59);
+    Time.u8Second       = RT_MIN(pIso9660->bSecond, 59);
+    Time.u32Nanosecond  = 0;
+    RTTimeImplode(pTimeSpec, RTTimeNormalize(&Time));
+
+    /* Only apply the UTC offset if it's within reasons. */
+    if (RT_ABS(pIso9660->offUtc) <= 13*4)
+        RTTimeSpecSubSeconds(pTimeSpec, pIso9660->offUtc * 15 * 60 * 60);
+}
+
+
+/**
+ * Initialization of a RTFSISO9660CORE structure from a directory record.
+ *
+ * @note    The RTFSISO9660CORE::pParentDir and RTFSISO9660CORE::Clusters members are
+ *          properly initialized elsewhere.
+ *
+ * @returns IRPT status code.  Either VINF_SUCCESS or VERR_NO_MEMORY, the latter
+ *          only if @a cDirRecs is above 1.
+ * @param   pCore           The structure to initialize.
+ * @param   pDirRec         The primary directory record.
+ * @param   cDirRecs        Number of directory records.
+ * @param   offDirRec       The offset of the primary directory record.
+ * @param   uVersion        The file version number.
+ * @param   pVol            The volume.
+ */
+static int rtFsIso9660Core_InitFromDirRec(PRTFSISO9660CORE pCore, PCISO9660DIRREC pDirRec, uint32_t cDirRecs,
+                                          uint64_t offDirRec, uint32_t uVersion, PRTFSISO9660VOL pVol)
+{
+    RTListInit(&pCore->Entry);
+    pCore->cRefs                = 1;
+    pCore->pParentDir           = NULL;
+    pCore->pVol                 = pVol;
+    pCore->offDirRec            = offDirRec;
+    pCore->fAttrib              = pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY
+                                ? 0755 | RTFS_TYPE_DIRECTORY | RTFS_DOS_DIRECTORY
+                                : 0644 | RTFS_TYPE_FILE;
+    if (pDirRec->fFileFlags & ISO9660_FILE_FLAGS_HIDDEN)
+        pCore->fAttrib |= RTFS_DOS_HIDDEN;
+    pCore->cbObject             = ISO9660_GET_ENDIAN(&pDirRec->cbData);
+    pCore->uVersion             = uVersion;
+    pCore->cExtents             = 1;
+    pCore->FirstExtent.cbExtent = pCore->cbObject;
+    pCore->FirstExtent.offDisk  = (ISO9660_GET_ENDIAN(&pDirRec->offExtent) + pDirRec->cExtAttrBlocks) * (uint64_t)pVol->cbBlock;
+
+    rtFsIso9660DateTime2TimeSpec(&pCore->ModificationTime, &pDirRec->RecTime);
+    pCore->BirthTime  = pCore->ModificationTime;
+    pCore->AccessTime = pCore->ModificationTime;
+    pCore->ChangeTime = pCore->ModificationTime;
+
+    /*
+     * Deal with multiple extents.
+     */
+    if (RT_LIKELY(cDirRecs == 1))
+    { /* done */ }
+    else
+    {
+        PRTFSISO9660EXTENT pCurExtent = &pCore->FirstExtent;
+        while (cDirRecs > 1)
+        {
+            offDirRec += pDirRec->cbDirRec;
+            pDirRec = (PCISO9660DIRREC)((uintptr_t)pDirRec + pDirRec->cbDirRec);
+            if (pDirRec->cbDirRec != 0)
+            {
+                uint64_t offDisk = ISO9660_GET_ENDIAN(&pDirRec->offExtent) * (uint64_t)pVol->cbBlock;
+                uint32_t cbExtent  = ISO9660_GET_ENDIAN(&pDirRec->cbData);
+                pCore->cbObject += cbExtent;
+
+                if (pCurExtent->offDisk + pCurExtent->cbExtent == offDisk)
+                    pCurExtent->cbExtent += cbExtent;
+                else
+                {
+                    void *pvNew = RTMemRealloc(pCore->paExtents, pCore->cExtents * sizeof(pCore->paExtents[0]));
+                    if (pvNew)
+                        pCore->paExtents = (PRTFSISO9660EXTENT)pvNew;
+                    else
+                    {
+                        RTMemFree(pCore->paExtents);
+                        return VERR_NO_MEMORY;
+                    }
+                    pCurExtent = &pCore->paExtents[pCore->cExtents - 1];
+                    pCurExtent->cbExtent = cbExtent;
+                    pCurExtent->offDisk  = offDisk;
+                    pCore->cExtents++;
+                }
+                cDirRecs--;
+            }
+            else
+            {
+                size_t cbSkip = (offDirRec + pVol->cbSector) & ~(pVol->cbSector - 1U);
+                offDirRec += cbSkip;
+                pDirRec = (PCISO9660DIRREC)((uintptr_t)pDirRec + cbSkip);
+            }
+        }
+    }
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * Worker for rtFsIso9660File_QueryInfo and rtFsIso9660Dir_QueryInfo.
+ */
+static int rtFsIso9660Core_QueryInfo(PRTFSISO9660CORE pCore, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
+{
+    pObjInfo->cbObject              = pCore->cbObject;
+    pObjInfo->cbAllocated           = RT_ALIGN_64(pCore->cbObject, pCore->pVol->cbBlock);
+    pObjInfo->AccessTime            = pCore->AccessTime;
+    pObjInfo->ModificationTime      = pCore->ModificationTime;
+    pObjInfo->ChangeTime            = pCore->ChangeTime;
+    pObjInfo->BirthTime             = pCore->BirthTime;
+    pObjInfo->Attr.fMode            = pCore->fAttrib;
+    pObjInfo->Attr.enmAdditional    = enmAddAttr;
+
+    switch (enmAddAttr)
+    {
+        case RTFSOBJATTRADD_NOTHING: /* fall thru */
+        case RTFSOBJATTRADD_UNIX:
+            pObjInfo->Attr.u.Unix.uid           = NIL_RTUID;
+            pObjInfo->Attr.u.Unix.gid           = NIL_RTGID;
+            pObjInfo->Attr.u.Unix.cHardlinks    = 1;
+            pObjInfo->Attr.u.Unix.INodeIdDevice = 0;
+            pObjInfo->Attr.u.Unix.INodeId       = 0; /* Could probably use the directory entry offset. */
+            pObjInfo->Attr.u.Unix.fFlags        = 0;
+            pObjInfo->Attr.u.Unix.GenerationId  = pCore->uVersion;
+            pObjInfo->Attr.u.Unix.Device        = 0;
+            break;
+        case RTFSOBJATTRADD_UNIX_OWNER:
+            pObjInfo->Attr.u.UnixOwner.uid       = 0;
+            pObjInfo->Attr.u.UnixOwner.szName[0] = '\0';
+            break;
+        case RTFSOBJATTRADD_UNIX_GROUP:
+            pObjInfo->Attr.u.UnixGroup.gid       = 0;
+            pObjInfo->Attr.u.UnixGroup.szName[0] = '\0';
+            break;
+        case RTFSOBJATTRADD_EASIZE:
+            pObjInfo->Attr.u.EASize.cb = 0;
+            break;
+        default:
+            return VERR_INVALID_PARAMETER;
+    }
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * Worker for rtFsIso9660File_Close and rtFsIso9660Dir_Close that does common work.
+ *
+ * @param   pCore           The common shared structure.
+ */
+static void rtFsIso9660Core_Destroy(PRTFSISO9660CORE pCore)
+{
+    if (pCore->pParentDir)
+        rtFsIso9660DirShrd_RemoveOpenChild(pCore->pParentDir, pCore);
+    if (pCore->paExtents)
+    {
+        RTMemFree(pCore->paExtents);
+        pCore->paExtents = NULL;
+    }
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJOPS,pfnClose}
+ */
+static DECLCALLBACK(int) rtFsIso9660File_Close(void *pvThis)
+{
+    PRTFSISO9660FILEOBJ  pThis   = (PRTFSISO9660FILEOBJ)pvThis;
+    LogFlow(("rtFsIso9660File_Close(%p/%p)\n", pThis, pThis->pShared));
+
+    PRTFSISO9660FILESHRD pShared = pThis->pShared;
+    pThis->pShared = NULL;
+    if (pShared)
+    {
+        if (ASMAtomicDecU32(&pShared->Core.cRefs) == 0)
+        {
+            LogFlow(("rtFsIso9660File_Close: Destroying shared structure %p\n", pShared));
+            rtFsIso9660Core_Destroy(&pShared->Core);
+            RTMemFree(pShared);
+        }
+    }
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
+ */
+static DECLCALLBACK(int) rtFsIso9660File_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
+{
+    PRTFSISO9660FILEOBJ pThis = (PRTFSISO9660FILEOBJ)pvThis;
+    return rtFsIso9660Core_QueryInfo(&pThis->pShared->Core, pObjInfo, enmAddAttr);
+}
+
+
+/**
+ * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
+ */
+static DECLCALLBACK(int) rtFsIso9660File_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
+{
+    PRTFSISO9660FILEOBJ  pThis   = (PRTFSISO9660FILEOBJ)pvThis;
+    PRTFSISO9660FILESHRD pShared = pThis->pShared;
+    AssertReturn(pSgBuf->cSegs != 0, VERR_INTERNAL_ERROR_3);
+    RT_NOREF(fBlocking);
+
+    /*
+     * Check for EOF.
+     */
+    if (off == -1)
+        off = pThis->offFile;
+    if ((uint64_t)off >= pShared->Core.cbObject)
+    {
+        if (pcbRead)
+        {
+            *pcbRead = 0;
+            return VINF_EOF;
+        }
+        return VERR_EOF;
+    }
+
+
+    /*
+     * Simple case: File has a single extent.
+     */
+    int      rc         = VINF_SUCCESS;
+    size_t   cbRead     = 0;
+    uint64_t cbFileLeft = pShared->Core.cbObject - (uint64_t)off;
+    size_t   cbLeft     = pSgBuf->paSegs[0].cbSeg;
+    uint8_t *pbDst      = (uint8_t *)pSgBuf->paSegs[0].pvSeg;
+    if (pShared->Core.cExtents == 1)
+    {
+        if (cbLeft > 0)
+        {
+            size_t cbToRead = cbLeft;
+            if (cbToRead > cbFileLeft)
+                cbToRead = (size_t)cbFileLeft;
+            rc = RTVfsFileReadAt(pShared->Core.pVol->hVfsBacking, pShared->Core.FirstExtent.offDisk + off, pbDst, cbToRead, NULL);
+            if (RT_SUCCESS(rc))
+            {
+                off         += cbToRead;
+                pbDst       += cbToRead;
+                cbRead      += cbToRead;
+                cbFileLeft  -= cbToRead;
+                cbLeft      -= cbToRead;
+            }
+        }
+    }
+    /*
+     * Complicated case: Work the file content extent by extent.
+     */
+    else
+    {
+        return VERR_NOT_IMPLEMENTED; /** @todo multi-extent stuff . */
+    }
+
+    /* Update the offset and return. */
+    pThis->offFile = off;
+    if (pcbRead)
+        *pcbRead = cbRead;
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite}
+ */
+static DECLCALLBACK(int) rtFsIso9660File_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
+{
+    RT_NOREF(pvThis, off, pSgBuf, fBlocking, pcbWritten);
+    return VERR_WRITE_PROTECT;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
+ */
+static DECLCALLBACK(int) rtFsIso9660File_Flush(void *pvThis)
+{
+    RT_NOREF(pvThis);
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSIOSTREAMOPS,pfnPollOne}
+ */
+static DECLCALLBACK(int) rtFsIso9660File_PollOne(void *pvThis, uint32_t fEvents, RTMSINTERVAL cMillies, bool fIntr,
+                                                 uint32_t *pfRetEvents)
+{
+    NOREF(pvThis);
+    int rc;
+    if (fEvents != RTPOLL_EVT_ERROR)
+    {
+        *pfRetEvents = fEvents & ~RTPOLL_EVT_ERROR;
+        rc = VINF_SUCCESS;
+    }
+    else if (fIntr)
+        rc = RTThreadSleep(cMillies);
+    else
+    {
+        uint64_t uMsStart = RTTimeMilliTS();
+        do
+            rc = RTThreadSleep(cMillies);
+        while (   rc == VERR_INTERRUPTED
+               && !fIntr
+               && RTTimeMilliTS() - uMsStart < cMillies);
+        if (rc == VERR_INTERRUPTED)
+            rc = VERR_TIMEOUT;
+    }
+    return rc;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
+ */
+static DECLCALLBACK(int) rtFsIso9660File_Tell(void *pvThis, PRTFOFF poffActual)
+{
+    PRTFSISO9660FILEOBJ pThis = (PRTFSISO9660FILEOBJ)pvThis;
+    *poffActual = pThis->offFile;
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
+ */
+static DECLCALLBACK(int) rtFsIso9660File_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
+{
+    RT_NOREF(pvThis, fMode, fMask);
+    return VERR_WRITE_PROTECT;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
+ */
+static DECLCALLBACK(int) rtFsIso9660File_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
+                                                 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
+{
+    RT_NOREF(pvThis, pAccessTime, pModificationTime, pChangeTime, pBirthTime);
+    return VERR_WRITE_PROTECT;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
+ */
+static DECLCALLBACK(int) rtFsIso9660File_SetOwner(void *pvThis, RTUID uid, RTGID gid)
+{
+    RT_NOREF(pvThis, uid, gid);
+    return VERR_WRITE_PROTECT;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSFILEOPS,pfnSeek}
+ */
+static DECLCALLBACK(int) rtFsIso9660File_Seek(void *pvThis, RTFOFF offSeek, unsigned uMethod, PRTFOFF poffActual)
+{
+    PRTFSISO9660FILEOBJ pThis = (PRTFSISO9660FILEOBJ)pvThis;
+    RTFOFF offNew;
+    switch (uMethod)
+    {
+        case RTFILE_SEEK_BEGIN:
+            offNew = offSeek;
+            break;
+        case RTFILE_SEEK_END:
+            offNew = (RTFOFF)pThis->pShared->Core.cbObject + offSeek;
+            break;
+        case RTFILE_SEEK_CURRENT:
+            offNew = (RTFOFF)pThis->offFile + offSeek;
+            break;
+        default:
+            return VERR_INVALID_PARAMETER;
+    }
+    if (offNew >= 0)
+    {
+        if (offNew <= _4G)
+        {
+            pThis->offFile = offNew;
+            *poffActual    = offNew;
+            return VINF_SUCCESS;
+        }
+        return VERR_OUT_OF_RANGE;
+    }
+    return VERR_NEGATIVE_SEEK;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSFILEOPS,pfnQuerySize}
+ */
+static DECLCALLBACK(int) rtFsIso9660File_QuerySize(void *pvThis, uint64_t *pcbFile)
+{
+    PRTFSISO9660FILEOBJ pThis = (PRTFSISO9660FILEOBJ)pvThis;
+    *pcbFile = pThis->pShared->Core.cbObject;
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * ISO FS file operations.
+ */
+DECL_HIDDEN_CONST(const RTVFSFILEOPS) g_rtFsIos9660FileOps =
+{
+    { /* Stream */
+        { /* Obj */
+            RTVFSOBJOPS_VERSION,
+            RTVFSOBJTYPE_FILE,
+            "FatFile",
+            rtFsIso9660File_Close,
+            rtFsIso9660File_QueryInfo,
+            RTVFSOBJOPS_VERSION
+        },
+        RTVFSIOSTREAMOPS_VERSION,
+        RTVFSIOSTREAMOPS_FEAT_NO_SG,
+        rtFsIso9660File_Read,
+        rtFsIso9660File_Write,
+        rtFsIso9660File_Flush,
+        rtFsIso9660File_PollOne,
+        rtFsIso9660File_Tell,
+        NULL /*pfnSkip*/,
+        NULL /*pfnZeroFill*/,
+        RTVFSIOSTREAMOPS_VERSION,
+    },
+    RTVFSFILEOPS_VERSION,
+    0,
+    { /* ObjSet */
+        RTVFSOBJSETOPS_VERSION,
+        RT_OFFSETOF(RTVFSFILEOPS, Stream.Obj) - RT_OFFSETOF(RTVFSFILEOPS, ObjSet),
+        rtFsIso9660File_SetMode,
+        rtFsIso9660File_SetTimes,
+        rtFsIso9660File_SetOwner,
+        RTVFSOBJSETOPS_VERSION
+    },
+    rtFsIso9660File_Seek,
+    rtFsIso9660File_QuerySize,
+    RTVFSFILEOPS_VERSION
+};
+
+
+/**
+ * Instantiates a new directory.
+ *
+ * @returns IPRT status code.
+ * @param   pThis           The FAT volume instance.
+ * @param   pParentDir      The parent directory (shared part).
+ * @param   pDirRec         The directory record.
+ * @param   cDirRecs        Number of directory records if more than one.
+ * @param   offDirRec       The byte offset of the directory record.
+ * @param   offEntryInDir   The byte offset of the directory entry in the parent
+ *                          directory.
+ * @param   fOpen           RTFILE_O_XXX flags.
+ * @param   uVersion        The file version number (since the caller already
+ *                          parsed the filename, we don't want to repeat the
+ *                          effort here).
+ * @param   phVfsFile       Where to return the file handle.
+ */
+static int rtFsIso9660File_New(PRTFSISO9660VOL pThis, PRTFSISO9660DIRSHRD pParentDir, PCISO9660DIRREC pDirRec, uint32_t cDirRecs,
+                               uint64_t offDirRec, uint64_t fOpen, uint32_t uVersion, PRTVFSFILE phVfsFile)
+{
+    AssertPtr(pParentDir);
+
+    /*
+     * Create a VFS object.
+     */
+    PRTFSISO9660FILEOBJ pNewFile;
+    int rc = RTVfsNewFile(&g_rtFsIos9660FileOps, sizeof(*pNewFile), fOpen, pThis->hVfsSelf, NIL_RTVFSLOCK /*use volume lock*/,
+                          phVfsFile, (void **)&pNewFile);
+    if (RT_SUCCESS(rc))
+    {
+        /*
+         * Look for existing shared object, create a new one if necessary.
+         */
+        PRTFSISO9660FILESHRD pShared = (PRTFSISO9660FILESHRD)rtFsIso9660Dir_LookupShared(pParentDir, offDirRec);
+        if (!pShared)
+        {
+            pShared = (PRTFSISO9660FILESHRD)RTMemAllocZ(sizeof(*pShared));
+            if (pShared)
+            {
+                /*
+                 * Initialize it all so rtFsIso9660File_Close doesn't trip up in anyway.
+                 */
+                rc = rtFsIso9660Core_InitFromDirRec(&pShared->Core, pDirRec, cDirRecs, offDirRec, uVersion, pThis);
+                if (RT_SUCCESS(rc))
+                    rtFsIso9660DirShrd_AddOpenChild(pParentDir, &pShared->Core);
+                else
+                {
+                    RTMemFree(pShared);
+                    pShared = NULL;
+                }
+            }
+        }
+        if (pShared)
+        {
+            LogFlow(("rtFsIso9660File_New: cbObject=%#RX64 First Extent: off=%#RX64 cb=%#RX64\n",
+                     pShared->Core.cbObject, pShared->Core.FirstExtent.offDisk, pShared->Core.FirstExtent.cbExtent));
+            pNewFile->offFile = 0;
+            pNewFile->pShared = pShared;
+            return VINF_SUCCESS;
+        }
+
+        rc = VERR_NO_MEMORY;
+    }
+    *phVfsFile = NIL_RTVFSFILE;
+    return rc;
+}
+
+
+/**
+ * Looks up the shared structure for a child.
+ *
+ * @returns Referenced pointer to the shared structure, NULL if not found.
+ * @param   pThis           The directory.
+ * @param   offDirRec       The directory record offset of the child.
+ */
+static PRTFSISO9660CORE rtFsIso9660Dir_LookupShared(PRTFSISO9660DIRSHRD pThis, uint64_t offDirRec)
+{
+    PRTFSISO9660CORE pCur;
+    RTListForEach(&pThis->OpenChildren, pCur, RTFSISO9660CORE, Entry)
+    {
+        if (pCur->offDirRec == offDirRec)
+        {
+            uint32_t cRefs = ASMAtomicIncU32(&pCur->cRefs);
+            Assert(cRefs > 1); RT_NOREF(cRefs);
+            return pCur;
+        }
+    }
+    return NULL;
+}
+
+
+#ifdef RT_STRICT
+/**
+ * Checks if @a pNext is an extent of @a pFirst.
+ *
+ * @returns true if @a pNext is the next extent, false if not
+ * @param   pFirst      The directory record describing the first or the
+ *                      previous extent.
+ * @param   pNext       The directory record alleged to be the next extent.
+ */
+DECLINLINE(bool) rtFsIso9660Dir_IsDirRecNextExtent(PCISO9660DIRREC pFirst, PCISO9660DIRREC pNext)
+{
+    if (RT_LIKELY(pNext->bFileIdLength == pFirst->bFileIdLength))
+    {
+        if (RT_LIKELY((pNext->fFileFlags | ISO9660_FILE_FLAGS_MULTI_EXTENT) == pFirst->fFileFlags))
+        {
+            if (RT_LIKELY(memcmp(pNext->achFileId, pFirst->achFileId, pNext->bFileIdLength) == 0))
+                return true;
+        }
+    }
+    return false;
+}
+#endif /* RT_STRICT */
+
+
+/**
+ * Worker for rtFsIso9660Dir_FindEntry that compares a UTF-16BE name with a
+ * directory record.
+ *
+ * @returns true if equal, false if not.
+ * @param   pDirRec             The directory record.
+ * @param   pwszEntry           The UTF-16BE string to compare with.
+ * @param   cbEntry             The compare string length in bytes (sans zero
+ *                              terminator).
+ * @param   cwcEntry            The compare string length in RTUTF16 units.
+ * @param   puVersion           Where to return any file version number.
+ */
+DECL_FORCE_INLINE(bool) rtFsIso9660Dir_IsEntryEqualUtf16Big(PCISO9660DIRREC pDirRec, PCRTUTF16 pwszEntry, size_t cbEntry,
+                                                            size_t cwcEntry, uint32_t *puVersion)
+{
+    /* ASSUME directories cannot have any version tags. */
+    if (pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY)
+    {
+        if (RT_LIKELY(pDirRec->bFileIdLength != cbEntry))
+            return false;
+        if (RT_LIKELY(RTUtf16BigNICmp((PCRTUTF16)pDirRec->achFileId, pwszEntry, cwcEntry) != 0))
+            return false;
+    }
+    else
+    {
+        size_t cbNameDelta = (size_t)pDirRec->bFileIdLength - cbEntry;
+        if (RT_LIKELY(cbNameDelta > (size_t)12 /* ;12345 */))
+            return false;
+        if (cbNameDelta == 0)
+        {
+            if (RT_LIKELY(RTUtf16BigNICmp((PCRTUTF16)pDirRec->achFileId, pwszEntry, cwcEntry) != 0))
+                return false;
+            *puVersion = 1;
+        }
+        else
+        {
+            if (RT_LIKELY(RT_MAKE_U16(pDirRec->achFileId[cbEntry + 1], pDirRec->achFileId[cbEntry]) != ';'))
+                return false;
+            if (RT_LIKELY(RTUtf16BigNICmp((PCRTUTF16)pDirRec->achFileId, pwszEntry, cwcEntry) != 0))
+                return false;
+            uint32_t uVersion;
+            size_t  cwcVersion = rtFsIso9660GetVersionLengthUtf16Big((PCRTUTF16)pDirRec->achFileId,
+                                                                     pDirRec->bFileIdLength, &uVersion);
+            if (RT_LIKELY(cwcVersion * sizeof(RTUTF16) == cbNameDelta))
+                *puVersion = uVersion;
+            else
+                return false;
+        }
+    }
+
+    /* (No need to check for dot and dot-dot here, because cbEntry must be a
+       multiple of two.) */
+    Assert(!(cbEntry & 1));
+    return true;
+}
+
+
+/**
+ * Worker for rtFsIso9660Dir_FindEntry that compares an ASCII name with a
+ * directory record.
+ *
+ * @returns true if equal, false if not.
+ * @param   pDirRec             The directory record.
+ * @param   pszEntry            The uppercased ASCII string to compare with.
+ * @param   cchEntry            The length of the compare string.
+ * @param   puVersion           Where to return any file version number.
+ */
+DECL_FORCE_INLINE(bool) rtFsIso9660Dir_IsEntryEqualAscii(PCISO9660DIRREC pDirRec, const char *pszEntry, size_t cchEntry,
+                                                         uint32_t *puVersion)
+{
+    /* ASSUME directories cannot have any version tags. */
+    if (pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY)
+    {
+        if (RT_LIKELY(pDirRec->bFileIdLength != cchEntry))
+            return false;
+        if (RT_LIKELY(memcmp(pDirRec->achFileId, pszEntry, cchEntry) != 0))
+            return false;
+    }
+    else
+    {
+        size_t cchNameDelta = (size_t)pDirRec->bFileIdLength - cchEntry;
+        if (RT_LIKELY(cchNameDelta > (size_t)6 /* ;12345 */))
+            return false;
+        if (cchNameDelta == 0)
+        {
+            if (RT_LIKELY(memcmp(pDirRec->achFileId, pszEntry, cchEntry) != 0))
+                return false;
+            *puVersion = 1;
+        }
+        else
+        {
+            if (RT_LIKELY(pDirRec->achFileId[cchEntry] != ';'))
+                return false;
+            if (RT_LIKELY(memcmp(pDirRec->achFileId, pszEntry, cchEntry) != 0))
+                return false;
+            uint32_t uVersion;
+            size_t  cchVersion = rtFsIso9660GetVersionLengthAscii(pDirRec->achFileId, pDirRec->bFileIdLength, &uVersion);
+            if (RT_LIKELY(cchVersion == cchNameDelta))
+                *puVersion = uVersion;
+            else
+                return false;
+        }
+    }
+
+    /* Don't match the 'dot' and 'dot-dot' directory records. */
+    if (RT_LIKELY(   pDirRec->bFileIdLength != 1
+                  || (uint8_t)pDirRec->achFileId[0] > (uint8_t)0x01))
+        return true;
+    return false;
+}
+
+
+/**
+ * Locates a directory entry in a directory.
+ *
+ * @returns IPRT status code.
+ * @retval  VERR_FILE_NOT_FOUND if not found.
+ * @param   pThis           The directory to search.
+ * @param   pszEntry        The entry to look for.
+ * @param   poffDirRec      Where to return the offset of the directory record
+ *                          on the disk.
+ * @param   ppDirRec        Where to return the pointer to the directory record
+ *                          (the whole directory is buffered).
+ * @param   pcDirRecs       Where to return the number of directory records
+ *                          related to this entry.
+ * @param   pfMode          Where to return the file type, rock ridge adjusted.
+ * @param   puVersion       Where to return the file version number.
+ */
+static int rtFsIso9660Dir_FindEntry(PRTFSISO9660DIRSHRD pThis, const char *pszEntry,  uint64_t *poffDirRec,
+                                    PCISO9660DIRREC *ppDirRec, uint32_t *pcDirRecs, PRTFMODE pfMode, uint32_t *puVersion)
+{
+    /* Set return values. */
+    *poffDirRec = UINT64_MAX;
+    *ppDirRec   = NULL;
+    *pcDirRecs  = 1;
+    *pfMode     = UINT32_MAX;
+    *puVersion  = 0;
+
+    /*
+     * If we're in UTF-16BE mode, convert the input name to UTF-16BE.  Otherwise try
+     * uppercase it into a ISO 9660 compliant name.
+     */
+    int         rc;
+    bool const  fIsUtf16  = pThis->Core.pVol->fIsUtf16;
+    size_t      cwcEntry  = 0;
+    size_t      cbEntry   = 0;
+    size_t      cchUpper  = ~(size_t)0;
+    union
+    {
+        RTUTF16  wszEntry[260 + 1];
+        struct
+        {
+            char  szUpper[255 + 1];
+            char  szRock[260 + 1];
+        } s;
+    } uBuf;
+    if (fIsUtf16)
+    {
+        PRTUTF16 pwszEntry = uBuf.wszEntry;
+        rc = RTStrToUtf16BigEx(pszEntry, RTSTR_MAX, &pwszEntry, RT_ELEMENTS(uBuf.wszEntry), &cwcEntry);
+        if (RT_FAILURE(rc))
+            return rc;
+        cbEntry = cwcEntry * 2;
+    }
+    else
+    {
+        rc = RTStrCopy(uBuf.s.szUpper, sizeof(uBuf.s.szUpper), pszEntry);
+        if (RT_SUCCESS(rc))
+        {
+            RTStrToUpper(uBuf.s.szUpper);
+            cchUpper = strlen(uBuf.s.szUpper);
+        }
+    }
+
+    /*
+     * Scan the directory buffer by buffer.
+     */
+    uint32_t        offEntryInDir   = 0;
+    uint32_t const  cbDir           = pThis->Core.cbObject;
+    while (offEntryInDir + RT_UOFFSETOF(ISO9660DIRREC, achFileId) <= cbDir)
+    {
+        PCISO9660DIRREC pDirRec = (PCISO9660DIRREC)&pThis->pbDir[offEntryInDir];
+
+        /* If null length, skip to the next sector. */
+        if (pDirRec->cbDirRec == 0)
+            offEntryInDir = (offEntryInDir + pThis->Core.pVol->cbSector) & ~(pThis->Core.pVol->cbSector - 1U);
+        else
+        {
+            /* Try match the filename. */
+            if (fIsUtf16)
+            {
+                if (RT_LIKELY(!rtFsIso9660Dir_IsEntryEqualUtf16Big(pDirRec, uBuf.wszEntry, cbEntry, cwcEntry, puVersion)))
+                {
+                    /* Advance */
+                    offEntryInDir += pDirRec->cbDirRec;
+                    continue;
+                }
+            }
+            else
+            {
+                if (RT_LIKELY(!rtFsIso9660Dir_IsEntryEqualAscii(pDirRec, uBuf.s.szUpper, cchUpper, puVersion)))
+                {
+                    /** @todo check rock. */
+                    if (1)
+                    {
+                        /* Advance */
+                        offEntryInDir += pDirRec->cbDirRec;
+                        continue;
+                    }
+                }
+            }
+
+            *poffDirRec = pThis->Core.FirstExtent.offDisk + offEntryInDir;
+            *ppDirRec   = pDirRec;
+            *pfMode     = pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY
+                        ? 0755 | RTFS_TYPE_DIRECTORY | RTFS_DOS_DIRECTORY
+                        : 0644 | RTFS_TYPE_FILE;
+
+            /*
+             * Deal with the unlikely scenario of multi extent records.
+             */
+            if (!(pDirRec->fFileFlags & ISO9660_FILE_FLAGS_MULTI_EXTENT))
+                *pcDirRecs = 1;
+            else
+            {
+                offEntryInDir += pDirRec->cbDirRec;
+
+                uint32_t cDirRecs = 1;
+                while (offEntryInDir + RT_UOFFSETOF(ISO9660DIRREC, achFileId) <= cbDir)
+                {
+                    PCISO9660DIRREC pDirRec2 = (PCISO9660DIRREC)&pThis->pbDir[offEntryInDir];
+                    if (pDirRec2->cbDirRec != 0)
+                    {
+                        Assert(rtFsIso9660Dir_IsDirRecNextExtent(pDirRec, pDirRec2));
+                        cDirRecs++;
+                        if (!(pDirRec2->fFileFlags & ISO9660_FILE_FLAGS_MULTI_EXTENT))
+                            break;
+                        offEntryInDir += pDirRec2->cbDirRec;
+                    }
+                    else
+                        offEntryInDir = (offEntryInDir + pThis->Core.pVol->cbSector) & ~(pThis->Core.pVol->cbSector - 1U);
+                }
+
+                *pcDirRecs = cDirRecs;
+            }
+            return VINF_SUCCESS;
+        }
+    }
+
+    return VERR_FILE_NOT_FOUND;
+}
+
+
+/**
+ * Releases a reference to a shared directory structure.
+ *
+ * @param   pShared             The shared directory structure.
+ */
+static void rtFsIso9660DirShrd_Release(PRTFSISO9660DIRSHRD pShared)
+{
+    uint32_t cRefs = ASMAtomicDecU32(&pShared->Core.cRefs);
+    Assert(cRefs < UINT32_MAX / 2);
+    if (cRefs == 0)
+    {
+        LogFlow(("rtFsIso9660DirShrd_Release: Destroying shared structure %p\n", pShared));
+        Assert(pShared->Core.cRefs == 0);
+        if (pShared->pbDir)
+        {
+            RTMemFree(pShared->pbDir);
+            pShared->pbDir = NULL;
+        }
+        rtFsIso9660Core_Destroy(&pShared->Core);
+        RTMemFree(pShared);
+    }
+}
+
+
+/**
+ * Retains a reference to a shared directory structure.
+ *
+ * @param   pShared             The shared directory structure.
+ */
+static void rtFsIso9660DirShrd_Retain(PRTFSISO9660DIRSHRD pShared)
+{
+    uint32_t cRefs = ASMAtomicIncU32(&pShared->Core.cRefs);
+    Assert(cRefs > 1); NOREF(cRefs);
+}
+
+
+
+/**
+ * @interface_method_impl{RTVFSOBJOPS,pfnClose}
+ */
+static DECLCALLBACK(int) rtFsIso9660Dir_Close(void *pvThis)
+{
+    PRTFSISO9660DIROBJ pThis = (PRTFSISO9660DIROBJ)pvThis;
+    LogFlow(("rtFsIso9660Dir_Close(%p/%p)\n", pThis, pThis->pShared));
+
+    PRTFSISO9660DIRSHRD pShared = pThis->pShared;
+    pThis->pShared = NULL;
+    if (pShared)
+        rtFsIso9660DirShrd_Release(pShared);
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
+ */
+static DECLCALLBACK(int) rtFsIso9660Dir_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
+{
+    PRTFSISO9660DIROBJ pThis = (PRTFSISO9660DIROBJ)pvThis;
+    return rtFsIso9660Core_QueryInfo(&pThis->pShared->Core, pObjInfo, enmAddAttr);
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
+ */
+static DECLCALLBACK(int) rtFsIso9660Dir_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
+{
+    RT_NOREF(pvThis, fMode, fMask);
+    return VERR_WRITE_PROTECT;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
+ */
+static DECLCALLBACK(int) rtFsIso9660Dir_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
+                                                 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
+{
+    RT_NOREF(pvThis, pAccessTime, pModificationTime, pChangeTime, pBirthTime);
+    return VERR_WRITE_PROTECT;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
+ */
+static DECLCALLBACK(int) rtFsIso9660Dir_SetOwner(void *pvThis, RTUID uid, RTGID gid)
+{
+    RT_NOREF(pvThis, uid, gid);
+    return VERR_WRITE_PROTECT;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJOPS,pfnTraversalOpen}
+ */
+static DECLCALLBACK(int) rtFsIso9660Dir_TraversalOpen(void *pvThis, const char *pszEntry, PRTVFSDIR phVfsDir,
+                                                      PRTVFSSYMLINK phVfsSymlink, PRTVFS phVfsMounted)
+{
+    /*
+     * We may have symbolic links if rock ridge is being used, though currently
+     * we won't have nested mounts.
+     */
+    int rc;
+    if (phVfsMounted)
+        *phVfsMounted = NIL_RTVFS;
+    if (phVfsDir || phVfsSymlink)
+    {
+        if (phVfsSymlink)
+            *phVfsSymlink = NIL_RTVFSSYMLINK;
+        if (phVfsDir)
+            *phVfsDir = NIL_RTVFSDIR;
+
+        PRTFSISO9660DIROBJ  pThis = (PRTFSISO9660DIROBJ)pvThis;
+        PRTFSISO9660DIRSHRD pShared = pThis->pShared;
+        PCISO9660DIRREC     pDirRec;
+        uint64_t            offDirRec;
+        uint32_t            cDirRecs;
+        RTFMODE             fMode;
+        uint32_t            uVersion;
+        rc = rtFsIso9660Dir_FindEntry(pShared, pszEntry, &offDirRec, &pDirRec, &cDirRecs, &fMode, &uVersion);
+        Log2(("rtFsIso9660Dir_TraversalOpen: FindEntry(,%s,) -> %Rrc\n", pszEntry, rc));
+        if (RT_SUCCESS(rc))
+        {
+            switch (fMode & RTFS_TYPE_MASK)
+            {
+                case RTFS_TYPE_DIRECTORY:
+                    if (phVfsDir)
+                        rc = rtFsIso9660Dir_New(pShared->Core.pVol, pShared, pDirRec, cDirRecs, offDirRec, phVfsDir);
+                    else
+                        rc = VERR_NOT_SYMLINK;
+                    break;
+
+                case RTFS_TYPE_SYMLINK:
+                    rc = VERR_NOT_IMPLEMENTED;
+                    break;
+                case RTFS_TYPE_FILE:
+                case RTFS_TYPE_DEV_BLOCK:
+                case RTFS_TYPE_DEV_CHAR:
+                case RTFS_TYPE_FIFO:
+                case RTFS_TYPE_SOCKET:
+                    rc = VERR_NOT_A_DIRECTORY;
+                    break;
+                default:
+                case RTFS_TYPE_WHITEOUT:
+                    rc = VERR_PATH_NOT_FOUND;
+                    break;
+            }
+        }
+        else if (rc == VERR_FILE_NOT_FOUND)
+            rc = VERR_PATH_NOT_FOUND;
+    }
+    else
+        rc = VERR_PATH_NOT_FOUND;
+    return rc;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSDIROPS,pfnOpenFile}
+ */
+static DECLCALLBACK(int) rtFsIso9660Dir_OpenFile(void *pvThis, const char *pszFilename, uint32_t fOpen, PRTVFSFILE phVfsFile)
+{
+    PRTFSISO9660DIROBJ  pThis   = (PRTFSISO9660DIROBJ)pvThis;
+    PRTFSISO9660DIRSHRD pShared = pThis->pShared;
+
+    /*
+     * We cannot create or replace anything, just open stuff.
+     */
+    if (   (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_CREATE
+        || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_CREATE_REPLACE)
+        return VERR_WRITE_PROTECT;
+
+    /*
+     * Try open file.
+     */
+    PCISO9660DIRREC pDirRec;
+    uint64_t        offDirRec;
+    uint32_t        cDirRecs;
+    RTFMODE         fMode;
+    uint32_t        uVersion;
+    int rc = rtFsIso9660Dir_FindEntry(pShared, pszFilename, &offDirRec, &pDirRec, &cDirRecs, &fMode, &uVersion);
+    Log2(("rtFsIso9660Dir_OpenFile: FindEntry(,%s,) -> %Rrc\n", pszFilename, rc));
+    if (RT_SUCCESS(rc))
+    {
+        switch (fMode & RTFS_TYPE_MASK)
+        {
+            case RTFS_TYPE_FILE:
+                rc = rtFsIso9660File_New(pShared->Core.pVol, pShared, pDirRec, cDirRecs, offDirRec, fOpen, uVersion, phVfsFile);
+                break;
+
+            case RTFS_TYPE_SYMLINK:
+            case RTFS_TYPE_DEV_BLOCK:
+            case RTFS_TYPE_DEV_CHAR:
+            case RTFS_TYPE_FIFO:
+            case RTFS_TYPE_SOCKET:
+            case RTFS_TYPE_WHITEOUT:
+                rc = VERR_NOT_IMPLEMENTED;
+                break;
+
+            case RTFS_TYPE_DIRECTORY:
+                rc = VERR_NOT_A_FILE;
+                break;
+
+            default:
+                rc = VERR_PATH_NOT_FOUND;
+                break;
+        }
+    }
+    return rc;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSDIROPS,pfnOpenDir}
+ */
+static DECLCALLBACK(int) rtFsIso9660Dir_OpenDir(void *pvThis, const char *pszSubDir, uint32_t fFlags, PRTVFSDIR phVfsDir)
+{
+    PRTFSISO9660DIROBJ  pThis   = (PRTFSISO9660DIROBJ)pvThis;
+    PRTFSISO9660DIRSHRD pShared = pThis->pShared;
+    AssertReturn(!fFlags, VERR_INVALID_FLAGS);
+
+    /*
+     * Try open file.
+     */
+    PCISO9660DIRREC pDirRec;
+    uint64_t        offDirRec;
+    uint32_t        cDirRecs;
+    RTFMODE         fMode;
+    uint32_t        uVersion;
+    int rc = rtFsIso9660Dir_FindEntry(pShared, pszSubDir, &offDirRec, &pDirRec, &cDirRecs, &fMode, &uVersion);
+    Log2(("rtFsIso9660Dir_OpenDir: FindEntry(,%s,) -> %Rrc\n", pszSubDir, rc));
+    if (RT_SUCCESS(rc))
+    {
+        switch (fMode & RTFS_TYPE_MASK)
+        {
+            case RTFS_TYPE_DIRECTORY:
+                rc = rtFsIso9660Dir_New(pShared->Core.pVol, pShared, pDirRec, cDirRecs, offDirRec, phVfsDir);
+                break;
+
+            case RTFS_TYPE_FILE:
+            case RTFS_TYPE_SYMLINK:
+            case RTFS_TYPE_DEV_BLOCK:
+            case RTFS_TYPE_DEV_CHAR:
+            case RTFS_TYPE_FIFO:
+            case RTFS_TYPE_SOCKET:
+            case RTFS_TYPE_WHITEOUT:
+                rc = VERR_NOT_A_DIRECTORY;
+                break;
+
+            default:
+                rc = VERR_PATH_NOT_FOUND;
+                break;
+        }
+    }
+    return rc;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSDIROPS,pfnCreateDir}
+ */
+static DECLCALLBACK(int) rtFsIso9660Dir_CreateDir(void *pvThis, const char *pszSubDir, RTFMODE fMode, PRTVFSDIR phVfsDir)
+{
+    RT_NOREF(pvThis, pszSubDir, fMode, phVfsDir);
+    return VERR_WRITE_PROTECT;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSDIROPS,pfnOpenSymlink}
+ */
+static DECLCALLBACK(int) rtFsIso9660Dir_OpenSymlink(void *pvThis, const char *pszSymlink, PRTVFSSYMLINK phVfsSymlink)
+{
+    RT_NOREF(pvThis, pszSymlink, phVfsSymlink);
+RTAssertMsg2("%s: %s\n", __FUNCTION__, pszSymlink);
+    return VERR_NOT_SUPPORTED;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSDIROPS,pfnCreateSymlink}
+ */
+static DECLCALLBACK(int) rtFsIso9660Dir_CreateSymlink(void *pvThis, const char *pszSymlink, const char *pszTarget,
+                                                      RTSYMLINKTYPE enmType, PRTVFSSYMLINK phVfsSymlink)
+{
+    RT_NOREF(pvThis, pszSymlink, pszTarget, enmType, phVfsSymlink);
+    return VERR_WRITE_PROTECT;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSDIROPS,pfnQueryEntryInfo}
+ */
+static DECLCALLBACK(int) rtFsIso9660Dir_QueryEntryInfo(void *pvThis, const char *pszEntry,
+                                                       PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
+{
+    /*
+     * Try locate the entry.
+     */
+    PRTFSISO9660DIROBJ  pThis   = (PRTFSISO9660DIROBJ)pvThis;
+    PRTFSISO9660DIRSHRD pShared = pThis->pShared;
+    PCISO9660DIRREC     pDirRec;
+    uint64_t            offDirRec;
+    uint32_t            cDirRecs;
+    RTFMODE             fMode;
+    uint32_t            uVersion;
+    int rc = rtFsIso9660Dir_FindEntry(pShared, pszEntry, &offDirRec, &pDirRec, &cDirRecs, &fMode, &uVersion);
+    Log2(("rtFsIso9660Dir_QueryEntryInfo: FindEntry(,%s,) -> %Rrc\n", pszEntry, rc));
+    if (RT_SUCCESS(rc))
+    {
+        /*
+         * To avoid duplicating code in rtFsIso9660Core_InitFromDirRec and
+         * rtFsIso9660Core_QueryInfo, we create a dummy RTFSISO9660CORE on the stack.
+         */
+        RTFSISO9660CORE TmpObj;
+        RT_ZERO(TmpObj);
+        rc = rtFsIso9660Core_InitFromDirRec(&TmpObj, pDirRec, cDirRecs, offDirRec, uVersion, pShared->Core.pVol);
+        if (RT_SUCCESS(rc))
+        {
+            rc = rtFsIso9660Core_QueryInfo(&TmpObj, pObjInfo, enmAddAttr);
+            RTMemFree(TmpObj.paExtents);
+        }
+    }
+    return rc;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSDIROPS,pfnUnlinkEntry}
+ */
+static DECLCALLBACK(int) rtFsIso9660Dir_UnlinkEntry(void *pvThis, const char *pszEntry, RTFMODE fType)
+{
+    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;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSDIROPS,pfnRewindDir}
+ */
+static DECLCALLBACK(int) rtFsIso9660Dir_RewindDir(void *pvThis)
+{
+    PRTFSISO9660DIROBJ pThis = (PRTFSISO9660DIROBJ)pvThis;
+    pThis->offDir = 0;
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSDIROPS,pfnReadDir}
+ */
+static DECLCALLBACK(int) rtFsIso9660Dir_ReadDir(void *pvThis, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry,
+                                                RTFSOBJATTRADD enmAddAttr)
+{
+    PRTFSISO9660DIROBJ  pThis   = (PRTFSISO9660DIROBJ)pvThis;
+    PRTFSISO9660DIRSHRD pShared = pThis->pShared;
+
+    while (pThis->offDir + RT_UOFFSETOF(ISO9660DIRREC, achFileId) <= pShared->cbDir)
+    {
+        PCISO9660DIRREC pDirRec = (PCISO9660DIRREC)&pShared->pbDir[pThis->offDir];
+
+        /* If null length, skip to the next sector. */
+        if (pDirRec->cbDirRec == 0)
+            pThis->offDir = (pThis->offDir + pShared->Core.pVol->cbSector) & ~(pShared->Core.pVol->cbSector - 1U);
+        else
+        {
+            /*
+             * Do names first as they may cause overflows.
+             */
+            uint32_t uVersion = 0;
+            if (   pDirRec->bFileIdLength == 1
+                && pDirRec->achFileId[0]  == '\0')
+            {
+                if (*pcbDirEntry < RT_UOFFSETOF(RTDIRENTRYEX, szName) + 2)
+                {
+                    *pcbDirEntry = RT_UOFFSETOF(RTDIRENTRYEX, szName) + 2;
+                    Log3(("rtFsIso9660Dir_ReadDir: VERR_BUFFER_OVERFLOW (dot)\n"));
+                    return VERR_BUFFER_OVERFLOW;
+                }
+                pDirEntry->cbName    = 1;
+                pDirEntry->szName[0] = '.';
+                pDirEntry->szName[1] = '\0';
+            }
+            else if (   pDirRec->bFileIdLength == 1
+                     && pDirRec->achFileId[0]  == '\1')
+            {
+                if (*pcbDirEntry < RT_UOFFSETOF(RTDIRENTRYEX, szName) + 3)
+                {
+                    *pcbDirEntry = RT_UOFFSETOF(RTDIRENTRYEX, szName) + 3;
+                    Log3(("rtFsIso9660Dir_ReadDir: VERR_BUFFER_OVERFLOW (dot-dot)\n"));
+                    return VERR_BUFFER_OVERFLOW;
+                }
+                pDirEntry->cbName    = 2;
+                pDirEntry->szName[0] = '.';
+                pDirEntry->szName[1] = '.';
+                pDirEntry->szName[2] = '\0';
+            }
+            else if (pShared->Core.pVol->fIsUtf16)
+            {
+                PCRTUTF16 pawcSrc   = (PCRTUTF16)&pDirRec->achFileId[0];
+                size_t    cwcSrc    = pDirRec->bFileIdLength / sizeof(RTUTF16);
+                size_t    cwcVer    = !(pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY)
+                                    ? rtFsIso9660GetVersionLengthUtf16Big(pawcSrc, cwcSrc, &uVersion) : 0;
+                size_t    cchNeeded = 0;
+                size_t    cbDst     = *pcbDirEntry - RT_UOFFSETOF(RTDIRENTRYEX, szName);
+                char     *pszDst    = pDirEntry->szName;
+
+                int rc = RTUtf16BigToUtf8Ex(pawcSrc, cwcSrc - cwcVer, &pszDst, cbDst, &cchNeeded);
+                if (RT_SUCCESS(rc))
+                    pDirEntry->cbName = (uint16_t)cchNeeded;
+                else if (rc == VERR_BUFFER_OVERFLOW)
+                {
+                    *pcbDirEntry = RT_UOFFSETOF(RTDIRENTRYEX, szName) + cchNeeded + 1;
+                    Log3(("rtFsIso9660Dir_ReadDir: VERR_BUFFER_OVERFLOW - cbDst=%zu cchNeeded=%zu (UTF-16BE)\n", cbDst, cchNeeded));
+                    return VERR_BUFFER_OVERFLOW;
+                }
+                else
+                {
+                    ssize_t cchNeeded2 = RTStrPrintf2(pszDst, cbDst, "bad-name-%#x", pThis->offDir);
+                    if (cchNeeded2 >= 0)
+                        pDirEntry->cbName = (uint16_t)cchNeeded2;
+                    else
+                    {
+                        *pcbDirEntry = RT_UOFFSETOF(RTDIRENTRYEX, szName) + (size_t)-cchNeeded2;
+                        return VERR_BUFFER_OVERFLOW;
+                    }
+                }
+            }
+            else
+            {
+                /* This is supposed to be upper case ASCII, however, purge the encoding anyway. */
+                size_t cchVer   = !(pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY)
+                                ? rtFsIso9660GetVersionLengthAscii(pDirRec->achFileId, pDirRec->bFileIdLength, &uVersion) : 0;
+                size_t cchName  = pDirRec->bFileIdLength - cchVer;
+                size_t cbNeeded = RT_UOFFSETOF(RTDIRENTRYEX, szName) + cchName + 1;
+                if (*pcbDirEntry < cbNeeded)
+                {
+                    Log3(("rtFsIso9660Dir_ReadDir: VERR_BUFFER_OVERFLOW - cbDst=%zu cbNeeded=%zu (ASCII)\n", *pcbDirEntry, cbNeeded));
+                    *pcbDirEntry = cbNeeded;
+                    return VERR_BUFFER_OVERFLOW;
+                }
+                pDirEntry->cbName = (uint16_t)cchName;
+                memcpy(pDirEntry->szName, pDirRec->achFileId, cchName);
+                pDirEntry->szName[cchName] = '\0';
+                RTStrPurgeEncoding(pDirEntry->szName);
+
+                /** @todo check for rock ridge names here.   */
+            }
+            pDirEntry->cwcShortName    = 0;
+            pDirEntry->wszShortName[0] = '\0';
+
+            /*
+             * To avoid duplicating code in rtFsIso9660Core_InitFromDirRec and
+             * rtFsIso9660Core_QueryInfo, we create a dummy RTFSISO9660CORE on the stack.
+             */
+            RTFSISO9660CORE TmpObj;
+            RT_ZERO(TmpObj);
+            rtFsIso9660Core_InitFromDirRec(&TmpObj, pDirRec, 1 /* cDirRecs - see below why 1 */,
+                                           pThis->offDir + pShared->Core.FirstExtent.offDisk, uVersion, pShared->Core.pVol);
+            int rc = rtFsIso9660Core_QueryInfo(&TmpObj, &pDirEntry->Info, enmAddAttr);
+
+            /*
+             * Update the directory location and handle multi extent records.
+             *
+             * Multi extent records only affect the file size and the directory location,
+             * so we deal with it here instead of involving * rtFsIso9660Core_InitFromDirRec
+             * which would potentially require freeing memory and such.
+             */
+            if (!(pDirRec->fFileFlags & ISO9660_FILE_FLAGS_MULTI_EXTENT))
+            {
+                Log3(("rtFsIso9660Dir_ReadDir: offDir=%#07x: %s (rc=%Rrc)\n", pThis->offDir, pDirEntry->szName, rc));
+                pThis->offDir += pDirRec->cbDirRec;
+            }
+            else
+            {
+                uint32_t cExtents = 1;
+                uint32_t offDir   = pThis->offDir + pDirRec->cbDirRec;
+                while (offDir + RT_UOFFSETOF(ISO9660DIRREC, achFileId) <= pShared->cbDir)
+                {
+                    PCISO9660DIRREC pDirRec2 = (PCISO9660DIRREC)&pShared->pbDir[offDir];
+                    if (pDirRec2->cbDirRec != 0)
+                    {
+                        pDirEntry->Info.cbObject += ISO9660_GET_ENDIAN(&pDirRec2->cbData);
+                        offDir += pDirRec2->cbDirRec;
+                        cExtents++;
+                        if (!(pDirRec2->fFileFlags & ISO9660_FILE_FLAGS_MULTI_EXTENT))
+                            break;
+                    }
+                    else
+                        offDir = (offDir + pShared->Core.pVol->cbSector) & ~(pShared->Core.pVol->cbSector - 1U);
+                }
+                Log3(("rtFsIso9660Dir_ReadDir: offDir=%#07x, %u extents ending at %#07x: %s (rc=%Rrc)\n",
+                      pThis->offDir, cExtents, offDir, pDirEntry->szName, rc));
+                pThis->offDir = offDir;
+            }
+
+            return rc;
+        }
+    }
+
+    Log3(("rtFsIso9660Dir_ReadDir: offDir=%#07x: VERR_NO_MORE_FILES\n", pThis->offDir));
+    return VERR_NO_MORE_FILES;
+}
+
+
+/**
+ * FAT file operations.
+ */
+static const RTVFSDIROPS g_rtFsIso9660DirOps =
+{
+    { /* Obj */
+        RTVFSOBJOPS_VERSION,
+        RTVFSOBJTYPE_DIR,
+        "ISO 9660 Dir",
+        rtFsIso9660Dir_Close,
+        rtFsIso9660Dir_QueryInfo,
+        RTVFSOBJOPS_VERSION
+    },
+    RTVFSDIROPS_VERSION,
+    0,
+    { /* ObjSet */
+        RTVFSOBJSETOPS_VERSION,
+        RT_OFFSETOF(RTVFSDIROPS, Obj) - RT_OFFSETOF(RTVFSDIROPS, ObjSet),
+        rtFsIso9660Dir_SetMode,
+        rtFsIso9660Dir_SetTimes,
+        rtFsIso9660Dir_SetOwner,
+        RTVFSOBJSETOPS_VERSION
+    },
+    rtFsIso9660Dir_TraversalOpen,
+    rtFsIso9660Dir_OpenFile,
+    rtFsIso9660Dir_OpenDir,
+    rtFsIso9660Dir_CreateDir,
+    rtFsIso9660Dir_OpenSymlink,
+    rtFsIso9660Dir_CreateSymlink,
+    rtFsIso9660Dir_QueryEntryInfo,
+    rtFsIso9660Dir_UnlinkEntry,
+    rtFsIso9660Dir_RenameEntry,
+    rtFsIso9660Dir_RewindDir,
+    rtFsIso9660Dir_ReadDir,
+    RTVFSDIROPS_VERSION,
+};
+
+
+/**
+ * Adds an open child to the parent directory's shared structure.
+ *
+ * Maintains an additional reference to the parent dir to prevent it from going
+ * away.  If @a pDir is the root directory, it also ensures the volume is
+ * referenced and sticks around until the last open object is gone.
+ *
+ * @param   pDir        The directory.
+ * @param   pChild      The child being opened.
+ * @sa      rtFsIso9660DirShrd_RemoveOpenChild
+ */
+static void rtFsIso9660DirShrd_AddOpenChild(PRTFSISO9660DIRSHRD pDir, PRTFSISO9660CORE pChild)
+{
+    rtFsIso9660DirShrd_Retain(pDir);
+
+    RTListAppend(&pDir->OpenChildren, &pChild->Entry);
+    pChild->pParentDir = pDir;
+}
+
+
+/**
+ * Removes an open child to the parent directory.
+ *
+ * @param   pDir        The directory.
+ * @param   pChild      The child being removed.
+ *
+ * @remarks This is the very last thing you do as it may cause a few other
+ *          objects to be released recursively (parent dir and the volume).
+ *
+ * @sa      rtFsIso9660DirShrd_AddOpenChild
+ */
+static void rtFsIso9660DirShrd_RemoveOpenChild(PRTFSISO9660DIRSHRD pDir, PRTFSISO9660CORE pChild)
+{
+    AssertReturnVoid(pChild->pParentDir == pDir);
+    RTListNodeRemove(&pChild->Entry);
+    pChild->pParentDir = NULL;
+
+    rtFsIso9660DirShrd_Release(pDir);
+}
+
+
+#ifdef LOG_ENABLED
+/**
+ * Logs the content of a directory.
+ */
+static void rtFsIso9660DirShrd_LogContent(PRTFSISO9660DIRSHRD pThis)
+{
+    if (LogIs2Enabled())
+    {
+        uint32_t offRec = 0;
+        while (offRec < pThis->cbDir)
+        {
+            PCISO9660DIRREC pDirRec = (PCISO9660DIRREC)&pThis->pbDir[offRec];
+            if (pDirRec->cbDirRec == 0)
+                break;
+
+            RTUTF16 wszName[128];
+            if (pThis->Core.pVol->fIsUtf16)
+            {
+                PRTUTF16  pwszDst = &wszName[pDirRec->bFileIdLength / sizeof(RTUTF16)];
+                PCRTUTF16 pwszSrc = (PCRTUTF16)&pDirRec->achFileId[pDirRec->bFileIdLength];
+                pwszSrc--;
+                *pwszDst-- = '\0';
+                while ((uintptr_t)pwszDst >= (uintptr_t)&wszName[0])
+                {
+                    *pwszDst = RT_BE2H_U16(*pwszSrc);
+                    pwszDst--;
+                    pwszSrc--;
+                }
+            }
+            else
+            {
+                PRTUTF16 pwszDst = wszName;
+                for (uint32_t off = 0; off < pDirRec->bFileIdLength; off++)
+                    *pwszDst++ = pDirRec->achFileId[off];
+                *pwszDst = '\0';
+            }
+
+            Log2(("ISO9660:  %04x: rec=%#x ea=%#x cb=%#010RX32 off=%#010RX32 fl=%#04x %04u-%02u-%02u %02u:%02u:%02u%+03d unit=%#x igap=%#x idVol=%#x '%ls'\n",
+                  offRec,
+                  pDirRec->cbDirRec,
+                  pDirRec->cExtAttrBlocks,
+                  ISO9660_GET_ENDIAN(&pDirRec->cbData),
+                  ISO9660_GET_ENDIAN(&pDirRec->offExtent),
+                  pDirRec->fFileFlags,
+                  pDirRec->RecTime.bYear + 1900,
+                  pDirRec->RecTime.bMonth,
+                  pDirRec->RecTime.bDay,
+                  pDirRec->RecTime.bHour,
+                  pDirRec->RecTime.bMinute,
+                  pDirRec->RecTime.bSecond,
+                  pDirRec->RecTime.offUtc*4/60,
+                  pDirRec->bFileUnitSize,
+                  pDirRec->bInterleaveGapSize,
+                  ISO9660_GET_ENDIAN(&pDirRec->VolumeSeqNo),
+                  wszName));
+
+            uint32_t offSysUse = RT_OFFSETOF(ISO9660DIRREC, achFileId[pDirRec->bFileIdLength]) + !(pDirRec->bFileIdLength & 1);
+            if (offSysUse < pDirRec->cbDirRec)
+            {
+                Log2(("ISO9660:       system use (%#x bytes):\n%.*Rhxd\n", pDirRec->cbDirRec - offSysUse,
+                      pDirRec->cbDirRec - offSysUse, (uint8_t *)pDirRec + offSysUse));
+            }
+
+            /* advance */
+            offRec += pDirRec->cbDirRec;
+        }
+    }
+}
+#endif /* LOG_ENABLED */
+
+
+/**
+ * Instantiates a new shared directory structure.
+ *
+ * @returns IPRT status code.
+ * @param   pThis           The FAT volume instance.
+ * @param   pParentDir      The parent directory.  This is NULL for the root
+ *                          directory.
+ * @param   pDirRec         The directory record.  Will access @a cDirRecs
+ *                          records.
+ * @param   cDirRecs        Number of directory records if more than one.
+ * @param   offDirRec       The byte offset of the directory record.
+ * @param   ppShared        Where to return the shared directory structure.
+ */
+static int rtFsIso9660DirShrd_New(PRTFSISO9660VOL pThis, PRTFSISO9660DIRSHRD pParentDir, PCISO9660DIRREC pDirRec,
+                                  uint32_t cDirRecs, uint64_t offDirRec, PRTFSISO9660DIRSHRD *ppShared)
+{
+    /*
+     * Allocate a new structure and initialize it.
+     */
+    int rc = VERR_NO_MEMORY;
+    PRTFSISO9660DIRSHRD pShared = (PRTFSISO9660DIRSHRD)RTMemAllocZ(sizeof(*pShared));
+    if (pShared)
+    {
+        rc = rtFsIso9660Core_InitFromDirRec(&pShared->Core, pDirRec, cDirRecs, offDirRec, 0 /*uVersion*/, pThis);
+        if (RT_SUCCESS(rc))
+        {
+            RTListInit(&pShared->OpenChildren);
+            pShared->cbDir = ISO9660_GET_ENDIAN(&pDirRec->cbData);
+            pShared->pbDir = (uint8_t *)RTMemAllocZ(pShared->cbDir + 256);
+            if (pShared->pbDir)
+            {
+                rc = RTVfsFileReadAt(pThis->hVfsBacking, pShared->Core.FirstExtent.offDisk, pShared->pbDir, pShared->cbDir, NULL);
+                if (RT_SUCCESS(rc))
+                {
+#ifdef LOG_ENABLED
+                    rtFsIso9660DirShrd_LogContent(pShared);
+#endif
+
+                    /*
+                     * Link into parent directory so we can use it to update
+                     * our directory entry.
+                     */
+                    if (pParentDir)
+                        rtFsIso9660DirShrd_AddOpenChild(pParentDir, &pShared->Core);
+                    *ppShared = pShared;
+                    return VINF_SUCCESS;
+                }
+            }
+        }
+        RTMemFree(pShared);
+    }
+    *ppShared = NULL;
+    return rc;
+}
+
+
+/**
+ * Instantiates a new directory with a shared structure presupplied.
+ *
+ * @returns IPRT status code.
+ * @param   pThis           The FAT volume instance.
+ * @param   pShared         Referenced pointer to the shared structure.  The
+ *                          reference is always CONSUMED.
+ * @param   phVfsDir        Where to return the directory handle.
+ */
+static int rtFsIso9660Dir_NewWithShared(PRTFSISO9660VOL pThis, PRTFSISO9660DIRSHRD pShared, PRTVFSDIR phVfsDir)
+{
+    /*
+     * Create VFS object around the shared structure.
+     */
+    PRTFSISO9660DIROBJ pNewDir;
+    int rc = RTVfsNewDir(&g_rtFsIso9660DirOps, sizeof(*pNewDir), 0 /*fFlags*/, pThis->hVfsSelf,
+                         NIL_RTVFSLOCK /*use volume lock*/, phVfsDir, (void **)&pNewDir);
+    if (RT_SUCCESS(rc))
+    {
+        /*
+         * Look for existing shared object, create a new one if necessary.
+         * We CONSUME a reference to pShared here.
+         */
+        pNewDir->offDir  = 0;
+        pNewDir->pShared = pShared;
+        return VINF_SUCCESS;
+    }
+
+    rtFsIso9660DirShrd_Release(pShared);
+    *phVfsDir = NIL_RTVFSDIR;
+    return rc;
+}
+
+
+
+/**
+ * Instantiates a new directory VFS instance, creating the shared structure as
+ * necessary.
+ *
+ * @returns IPRT status code.
+ * @param   pThis           The FAT volume instance.
+ * @param   pParentDir      The parent directory.  This is NULL for the root
+ *                          directory.
+ * @param   pDirRec         The directory record.
+ * @param   cDirRecs        Number of directory records if more than one.
+ * @param   offDirRec       The byte offset of the directory record.
+ * @param   phVfsDir        Where to return the directory handle.
+ */
+static int  rtFsIso9660Dir_New(PRTFSISO9660VOL pThis, PRTFSISO9660DIRSHRD pParentDir, PCISO9660DIRREC pDirRec,
+                               uint32_t cDirRecs, uint64_t offDirRec, PRTVFSDIR phVfsDir)
+{
+    /*
+     * Look for existing shared object, create a new one if necessary.
+     */
+    int                 rc      = VINF_SUCCESS;
+    PRTFSISO9660DIRSHRD pShared = (PRTFSISO9660DIRSHRD)rtFsIso9660Dir_LookupShared(pParentDir, offDirRec);
+    if (!pShared)
+    {
+        rc = rtFsIso9660DirShrd_New(pThis, pParentDir, pDirRec, cDirRecs, offDirRec, &pShared);
+        if (RT_FAILURE(rc))
+        {
+            *phVfsDir = NIL_RTVFSDIR;
+            return rc;
+        }
+    }
+    return rtFsIso9660Dir_NewWithShared(pThis, pShared, phVfsDir);
+}
+
+
+
+/**
+ * @interface_method_impl{RTVFSOBJOPS::Obj,pfnClose}
+ */
+static DECLCALLBACK(int) rtFsIso9660Vol_Close(void *pvThis)
+{
+    PRTFSISO9660VOL pThis = (PRTFSISO9660VOL)pvThis;
+    Log(("rtFsIso9660Vol_Close(%p)\n", pThis));
+
+    if (pThis->pRootDir)
+    {
+        Assert(RTListIsEmpty(&pThis->pRootDir->OpenChildren));
+        Assert(pThis->pRootDir->Core.cRefs == 1);
+        rtFsIso9660DirShrd_Release(pThis->pRootDir);
+        pThis->pRootDir = NULL;
+    }
+
+    RTVfsFileRelease(pThis->hVfsBacking);
+    pThis->hVfsBacking = NIL_RTVFSFILE;
+
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJOPS::Obj,pfnQueryInfo}
+ */
+static DECLCALLBACK(int) rtFsIso9660Vol_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
+{
+    RT_NOREF(pvThis, pObjInfo, enmAddAttr);
+    return VERR_WRONG_TYPE;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOPS,pfnOpenRoo}
+ */
+static DECLCALLBACK(int) rtFsIso9660Vol_OpenRoot(void *pvThis, PRTVFSDIR phVfsDir)
+{
+    PRTFSISO9660VOL pThis = (PRTFSISO9660VOL)pvThis;
+
+    rtFsIso9660DirShrd_Retain(pThis->pRootDir); /* consumed by the next call */
+    return rtFsIso9660Dir_NewWithShared(pThis, pThis->pRootDir, phVfsDir);
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOPS,pfnIsRangeInUse}
+ */
+static DECLCALLBACK(int) rtFsIso9660Vol_IsRangeInUse(void *pvThis, RTFOFF off, size_t cb, bool *pfUsed)
+{
+    RT_NOREF(pvThis, off, cb, pfUsed);
+    return VERR_NOT_IMPLEMENTED;
+}
+
+
+DECL_HIDDEN_CONST(const RTVFSOPS) g_rtFsIso9660VolOps =
+{
+    { /* Obj */
+        RTVFSOBJOPS_VERSION,
+        RTVFSOBJTYPE_VFS,
+        "ISO 9660",
+        rtFsIso9660Vol_Close,
+        rtFsIso9660Vol_QueryInfo,
+        RTVFSOBJOPS_VERSION
+    },
+    RTVFSOPS_VERSION,
+    0 /* fFeatures */,
+    rtFsIso9660Vol_OpenRoot,
+    rtFsIso9660Vol_IsRangeInUse,
+    RTVFSOPS_VERSION
+};
+
+
+
+#ifdef LOG_ENABLED
+
+/** Logging helper. */
+static size_t rtFsIso9660VolGetStrippedLength(const char *pachField, size_t cchField)
+{
+    while (cchField > 0 && pachField[cchField - 1] == ' ')
+        cchField--;
+    return cchField;
+}
+
+/** Logging helper. */
+static char *rtFsIso9660VolGetMaybeUtf16Be(const char *pachField, size_t cchField, char *pszDst, size_t cbDst)
+{
+    /* Check the format by looking for zero bytes.  ISO-9660 doesn't allow zeros.
+       This doesn't have to be a UTF-16BE string.  */
+    size_t cFirstZeros  = 0;
+    size_t cSecondZeros = 0;
+    for (size_t off = 0; off + 1 < cchField; off += 2)
+    {
+        cFirstZeros  += pachField[off]     == '\0';
+        cSecondZeros += pachField[off + 1] == '\0';
+    }
+
+    int    rc     = VINF_SUCCESS;
+    char  *pszTmp = &pszDst[10];
+    size_t cchRet = 0;
+    if (cFirstZeros > cSecondZeros)
+    {
+        /* UTF-16BE / UTC-2BE: */
+        if (cchField & 1)
+        {
+            if (pachField[cchField - 1] == '\0' || pachField[cchField - 1] == ' ')
+                cchField--;
+            else
+                rc = VERR_INVALID_UTF16_ENCODING;
+        }
+        if (RT_SUCCESS(rc))
+        {
+            while (   cchField >= 2
+                   && pachField[cchField - 1] == ' '
+                   && pachField[cchField - 2] == '\0')
+                cchField -= 2;
+
+            rc = RTUtf16BigToUtf8Ex((PCRTUTF16)pachField, cchField / sizeof(RTUTF16), &pszTmp, cbDst - 10 - 1, &cchRet);
+        }
+        if (RT_SUCCESS(rc))
+        {
+            pszDst[0] = 'U';
+            pszDst[1] = 'T';
+            pszDst[2] = 'F';
+            pszDst[3] = '-';
+            pszDst[4] = '1';
+            pszDst[5] = '6';
+            pszDst[6] = 'B';
+            pszDst[7] = 'E';
+            pszDst[8] = ':';
+            pszDst[9] = '\'';
+            pszDst[10 + cchRet] = '\'';
+            pszDst[10 + cchRet + 1] = '\0';
+        }
+        else
+            RTStrPrintf(pszDst, cbDst, "UTF-16BE: %.*Rhxs", cchField, pachField);
+    }
+    else if (cSecondZeros > 0)
+    {
+        /* Little endian UTF-16 / UCS-2 (ASSUMES host is little endian, sorry) */
+        if (cchField & 1)
+        {
+            if (pachField[cchField - 1] == '\0' || pachField[cchField - 1] == ' ')
+                cchField--;
+            else
+                rc = VERR_INVALID_UTF16_ENCODING;
+        }
+        if (RT_SUCCESS(rc))
+        {
+            while (   cchField >= 2
+                   && pachField[cchField - 1] == '\0'
+                   && pachField[cchField - 2] == ' ')
+                cchField -= 2;
+
+            rc = RTUtf16ToUtf8Ex((PCRTUTF16)pachField, cchField / sizeof(RTUTF16), &pszTmp, cbDst - 10 - 1, &cchRet);
+        }
+        if (RT_SUCCESS(rc))
+        {
+            pszDst[0] = 'U';
+            pszDst[1] = 'T';
+            pszDst[2] = 'F';
+            pszDst[3] = '-';
+            pszDst[4] = '1';
+            pszDst[5] = '6';
+            pszDst[6] = 'L';
+            pszDst[7] = 'E';
+            pszDst[8] = ':';
+            pszDst[9] = '\'';
+            pszDst[10 + cchRet] = '\'';
+            pszDst[10 + cchRet + 1] = '\0';
+        }
+        else
+            RTStrPrintf(pszDst, cbDst, "UTF-16LE: %.*Rhxs", cchField, pachField);
+    }
+    else
+    {
+        /* ASSUME UTF-8/ASCII. */
+        while (   cchField > 0
+               && pachField[cchField - 1] == ' ')
+            cchField--;
+        rc = RTStrValidateEncodingEx(pachField, cchField, RTSTR_VALIDATE_ENCODING_EXACT_LENGTH);
+        if (RT_SUCCESS(rc))
+            RTStrPrintf(pszDst, cbDst, "UTF-8: '%.*s'", cchField, pachField);
+        else
+            RTStrPrintf(pszDst, cbDst, "UNK-8: %.*Rhxs", cchField, pachField);
+    }
+    return pszDst;
+}
+
+
+/**
+ * Logs the primary or supplementary volume descriptor
+ *
+ * @param   pVolDesc            The descriptor.
+ */
+static void rtFsIso9660VolLogPrimarySupplementaryVolDesc(PCISO9660SUPVOLDESC pVolDesc)
+{
+    if (LogIs2Enabled())
+    {
+        char szTmp[384];
+        Log2(("ISO9660:  fVolumeFlags:              %#RX8\n", pVolDesc->fVolumeFlags));
+        Log2(("ISO9660:  achSystemId:               %s\n", rtFsIso9660VolGetMaybeUtf16Be(pVolDesc->achSystemId, sizeof(pVolDesc->achSystemId), szTmp, sizeof(szTmp)) ));
+        Log2(("ISO9660:  achVolumeId:               %s\n", rtFsIso9660VolGetMaybeUtf16Be(pVolDesc->achVolumeId, sizeof(pVolDesc->achVolumeId), szTmp, sizeof(szTmp)) ));
+        Log2(("ISO9660:  Unused73:                  {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->Unused73.be), RT_LE2H_U32(pVolDesc->Unused73.le)));
+        Log2(("ISO9660:  VolumeSpaceSize:           {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->VolumeSpaceSize.be), RT_LE2H_U32(pVolDesc->VolumeSpaceSize.le)));
+        Log2(("ISO9660:  abEscapeSequences:         '%.*s'\n", rtFsIso9660VolGetStrippedLength((char *)pVolDesc->abEscapeSequences, sizeof(pVolDesc->abEscapeSequences)), pVolDesc->abEscapeSequences));
+        Log2(("ISO9660:  cVolumesInSet:             {%#RX16,%#RX16}\n", RT_BE2H_U16(pVolDesc->cVolumesInSet.be), RT_LE2H_U16(pVolDesc->cVolumesInSet.le)));
+        Log2(("ISO9660:  VolumeSeqNo:               {%#RX16,%#RX16}\n", RT_BE2H_U16(pVolDesc->VolumeSeqNo.be), RT_LE2H_U16(pVolDesc->VolumeSeqNo.le)));
+        Log2(("ISO9660:  cbLogicalBlock:            {%#RX16,%#RX16}\n", RT_BE2H_U16(pVolDesc->cbLogicalBlock.be), RT_LE2H_U16(pVolDesc->cbLogicalBlock.le)));
+        Log2(("ISO9660:  cbPathTable:               {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->cbPathTable.be), RT_LE2H_U32(pVolDesc->cbPathTable.le)));
+        Log2(("ISO9660:  offTypeLPathTable:         %#RX32\n", RT_LE2H_U32(pVolDesc->offTypeLPathTable)));
+        Log2(("ISO9660:  offOptionalTypeLPathTable: %#RX32\n", RT_LE2H_U32(pVolDesc->offOptionalTypeLPathTable)));
+        Log2(("ISO9660:  offTypeMPathTable:         %#RX32\n", RT_BE2H_U32(pVolDesc->offTypeMPathTable)));
+        Log2(("ISO9660:  offOptionalTypeMPathTable: %#RX32\n", RT_BE2H_U32(pVolDesc->offOptionalTypeMPathTable)));
+        Log2(("ISO9660:  achVolumeSetId:            %s\n", rtFsIso9660VolGetMaybeUtf16Be(pVolDesc->achVolumeSetId, sizeof(pVolDesc->achVolumeSetId), szTmp, sizeof(szTmp)) ));
+        Log2(("ISO9660:  achPublisherId:            %s\n", rtFsIso9660VolGetMaybeUtf16Be(pVolDesc->achPublisherId, sizeof(pVolDesc->achPublisherId), szTmp, sizeof(szTmp)) ));
+        Log2(("ISO9660:  achDataPreparerId:         %s\n", rtFsIso9660VolGetMaybeUtf16Be(pVolDesc->achDataPreparerId, sizeof(pVolDesc->achDataPreparerId), szTmp, sizeof(szTmp)) ));
+        Log2(("ISO9660:  achApplicationId:          %s\n", rtFsIso9660VolGetMaybeUtf16Be(pVolDesc->achApplicationId, sizeof(pVolDesc->achApplicationId), szTmp, sizeof(szTmp)) ));
+        Log2(("ISO9660:  achCopyrightFileId:        %s\n", rtFsIso9660VolGetMaybeUtf16Be(pVolDesc->achCopyrightFileId, sizeof(pVolDesc->achCopyrightFileId), szTmp, sizeof(szTmp)) ));
+        Log2(("ISO9660:  achAbstractFileId:         %s\n", rtFsIso9660VolGetMaybeUtf16Be(pVolDesc->achAbstractFileId, sizeof(pVolDesc->achAbstractFileId), szTmp, sizeof(szTmp)) ));
+        Log2(("ISO9660:  achBibliographicFileId:    %s\n", rtFsIso9660VolGetMaybeUtf16Be(pVolDesc->achBibliographicFileId, sizeof(pVolDesc->achBibliographicFileId), szTmp, sizeof(szTmp)) ));
+        Log2(("ISO9660:  BirthTime:                 %.4s-%.2s-%.2s %.2s:%.2s:%.2s.%.2s%+03d\n",
+              pVolDesc->BirthTime.achYear,
+              pVolDesc->BirthTime.achMonth,
+              pVolDesc->BirthTime.achDay,
+              pVolDesc->BirthTime.achHour,
+              pVolDesc->BirthTime.achMinute,
+              pVolDesc->BirthTime.achSecond,
+              pVolDesc->BirthTime.achCentisecond,
+              pVolDesc->BirthTime.offUtc*4/60));
+        Log2(("ISO9660:  ModifyTime:                %.4s-%.2s-%.2s %.2s:%.2s:%.2s.%.2s%+03d\n",
+              pVolDesc->ModifyTime.achYear,
+              pVolDesc->ModifyTime.achMonth,
+              pVolDesc->ModifyTime.achDay,
+              pVolDesc->ModifyTime.achHour,
+              pVolDesc->ModifyTime.achMinute,
+              pVolDesc->ModifyTime.achSecond,
+              pVolDesc->ModifyTime.achCentisecond,
+              pVolDesc->ModifyTime.offUtc*4/60));
+        Log2(("ISO9660:  ExpireTime:                %.4s-%.2s-%.2s %.2s:%.2s:%.2s.%.2s%+03d\n",
+              pVolDesc->ExpireTime.achYear,
+              pVolDesc->ExpireTime.achMonth,
+              pVolDesc->ExpireTime.achDay,
+              pVolDesc->ExpireTime.achHour,
+              pVolDesc->ExpireTime.achMinute,
+              pVolDesc->ExpireTime.achSecond,
+              pVolDesc->ExpireTime.achCentisecond,
+              pVolDesc->ExpireTime.offUtc*4/60));
+        Log2(("ISO9660:  EffectiveTime:             %.4s-%.2s-%.2s %.2s:%.2s:%.2s.%.2s%+03d\n",
+              pVolDesc->EffectiveTime.achYear,
+              pVolDesc->EffectiveTime.achMonth,
+              pVolDesc->EffectiveTime.achDay,
+              pVolDesc->EffectiveTime.achHour,
+              pVolDesc->EffectiveTime.achMinute,
+              pVolDesc->EffectiveTime.achSecond,
+              pVolDesc->EffectiveTime.achCentisecond,
+              pVolDesc->EffectiveTime.offUtc*4/60));
+        Log2(("ISO9660:  bFileStructureVersion:     %#RX8\n", pVolDesc->bFileStructureVersion));
+        Log2(("ISO9660:  bReserved883:              %#RX8\n", pVolDesc->bReserved883));
+
+        Log2(("ISO9660:  RootDir.cbDirRec:                   %#RX8\n", pVolDesc->RootDir.DirRec.cbDirRec));
+        Log2(("ISO9660:  RootDir.cExtAttrBlocks:             %#RX8\n", pVolDesc->RootDir.DirRec.cExtAttrBlocks));
+        Log2(("ISO9660:  RootDir.offExtent:                  {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->RootDir.DirRec.offExtent.be), RT_LE2H_U32(pVolDesc->RootDir.DirRec.offExtent.le)));
+        Log2(("ISO9660:  RootDir.cbData:                     {%#RX32,%#RX32}\n", RT_BE2H_U32(pVolDesc->RootDir.DirRec.cbData.be), RT_LE2H_U32(pVolDesc->RootDir.DirRec.cbData.le)));
+        Log2(("ISO9660:  RootDir.RecTime:                    %04u-%02u-%02u %02u:%02u:%02u%+03d\n",
+              pVolDesc->RootDir.DirRec.RecTime.bYear + 1900,
+              pVolDesc->RootDir.DirRec.RecTime.bMonth,
+              pVolDesc->RootDir.DirRec.RecTime.bDay,
+              pVolDesc->RootDir.DirRec.RecTime.bHour,
+              pVolDesc->RootDir.DirRec.RecTime.bMinute,
+              pVolDesc->RootDir.DirRec.RecTime.bSecond,
+              pVolDesc->RootDir.DirRec.RecTime.offUtc*4/60));
+        Log2(("ISO9660:  RootDir.RecTime.fFileFlags:         %RX8\n", pVolDesc->RootDir.DirRec.fFileFlags));
+        Log2(("ISO9660:  RootDir.RecTime.bFileUnitSize:      %RX8\n", pVolDesc->RootDir.DirRec.bFileUnitSize));
+        Log2(("ISO9660:  RootDir.RecTime.bInterleaveGapSize: %RX8\n", pVolDesc->RootDir.DirRec.bInterleaveGapSize));
+        Log2(("ISO9660:  RootDir.RecTime.VolumeSeqNo:        {%#RX16,%#RX16}\n", RT_BE2H_U16(pVolDesc->RootDir.DirRec.VolumeSeqNo.be), RT_LE2H_U16(pVolDesc->RootDir.DirRec.VolumeSeqNo.le)));
+        Log2(("ISO9660:  RootDir.RecTime.bFileIdLength:      %RX8\n", pVolDesc->RootDir.DirRec.bFileIdLength));
+        Log2(("ISO9660:  RootDir.RecTime.achFileId:          '%.*s'\n", pVolDesc->RootDir.DirRec.bFileIdLength, pVolDesc->RootDir.DirRec.achFileId));
+        uint32_t offSysUse = RT_OFFSETOF(ISO9660DIRREC, achFileId[pVolDesc->RootDir.DirRec.bFileIdLength])
+                           + !(pVolDesc->RootDir.DirRec.bFileIdLength & 1);
+        if (offSysUse < pVolDesc->RootDir.DirRec.cbDirRec)
+        {
+            Log2(("ISO9660:  RootDir System Use:\n%.*Rhxd\n",
+                  pVolDesc->RootDir.DirRec.cbDirRec - offSysUse, &pVolDesc->RootDir.ab[offSysUse]));
+        }
+    }
+}
+
+#endif /* LOG_ENABLED */
+
+/**
+ * Deal with a root directory from a primary or supplemental descriptor.
+ *
+ * @returns IPRT status code.
+ * @param   pThis           The ISO 9660 instance being initialized.
+ * @param   pRootDir        The root directory record to check out.
+ * @param   pDstRootDir     Where to store a copy of the root dir record.
+ * @param   pErrInfo        Where to return additional error info.  Can be NULL.
+ */
+static int rtFsIso9660VolHandleRootDir(PRTFSISO9660VOL pThis, PCISO9660DIRREC pRootDir,
+                                       PISO9660DIRREC pDstRootDir, PRTERRINFO pErrInfo)
+{
+    if (pRootDir->cbDirRec < RT_OFFSETOF(ISO9660DIRREC, achFileId))
+        return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Root dir record size is too small: %#x (min %#x)",
+                             pRootDir->cbDirRec, RT_OFFSETOF(ISO9660DIRREC, achFileId));
+
+    if (!(pRootDir->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY))
+        return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
+                             "Root dir is not flagged as directory: %#x", pRootDir->fFileFlags);
+    if (pRootDir->fFileFlags & ISO9660_FILE_FLAGS_MULTI_EXTENT)
+        return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT,
+                             "Root dir is cannot be multi-extent: %#x", pRootDir->fFileFlags);
+
+    if (RT_LE2H_U32(pRootDir->cbData.le) != RT_BE2H_U32(pRootDir->cbData.be))
+        return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid root dir size: {%#RX32,%#RX32}",
+                             RT_BE2H_U32(pRootDir->cbData.be), RT_LE2H_U32(pRootDir->cbData.le));
+    if (RT_LE2H_U32(pRootDir->cbData.le) == 0)
+        return RTErrInfoSet(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Zero sized root dir");
+
+    if (RT_LE2H_U32(pRootDir->offExtent.le) != RT_BE2H_U32(pRootDir->offExtent.be))
+        return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid root dir extent: {%#RX32,%#RX32}",
+                             RT_BE2H_U32(pRootDir->offExtent.be), RT_LE2H_U32(pRootDir->offExtent.le));
+
+    if (RT_LE2H_U16(pRootDir->VolumeSeqNo.le) != RT_BE2H_U16(pRootDir->VolumeSeqNo.be))
+        return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid root dir volume sequence ID: {%#RX16,%#RX16}",
+                             RT_BE2H_U16(pRootDir->VolumeSeqNo.be), RT_LE2H_U16(pRootDir->VolumeSeqNo.le));
+    if (RT_LE2H_U16(pRootDir->VolumeSeqNo.le) != pThis->idPrimaryVol)
+        return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
+                             "Expected root dir to have same volume sequence number as primary volume: %#x, expected %#x",
+                             RT_LE2H_U16(pRootDir->VolumeSeqNo.le), pThis->idPrimaryVol);
+
+    /*
+     * Seems okay, copy it.
+     */
+    *pDstRootDir = *pRootDir;
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * Deal with a primary volume descriptor.
+ *
+ * @returns IPRT status code.
+ * @param   pThis           The ISO 9660 instance being initialized.
+ * @param   pVolDesc        The volume descriptor to handle.
+ * @param   offVolDesc      The disk offset of the volume descriptor.
+ * @param   pRootDir        Where to return a copy of the root directory record.
+ * @param   poffRootDirRec  Where to return the disk offset of the root dir.
+ * @param   pErrInfo        Where to return additional error info.  Can be NULL.
+ */
+static int rtFsIso9660VolHandlePrimaryVolDesc(PRTFSISO9660VOL pThis, PCISO9660PRIMARYVOLDESC pVolDesc, uint32_t offVolDesc,
+                                              PISO9660DIRREC pRootDir, uint64_t *poffRootDirRec, PRTERRINFO pErrInfo)
+{
+    if (pVolDesc->bFileStructureVersion != ISO9660_FILE_STRUCTURE_VERSION)
+        return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
+                             "Unsupported file structure version: %#x", pVolDesc->bFileStructureVersion);
+
+    /*
+     * We need the block size ...
+     */
+    pThis->cbBlock = RT_LE2H_U16(pVolDesc->cbLogicalBlock.le);
+    if (   pThis->cbBlock != RT_BE2H_U16(pVolDesc->cbLogicalBlock.be)
+        || !RT_IS_POWER_OF_TWO(pThis->cbBlock)
+        || pThis->cbBlock / pThis->cbSector < 1)
+        return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid logical block size: {%#RX16,%#RX16}",
+                             RT_BE2H_U16(pVolDesc->cbLogicalBlock.be), RT_LE2H_U16(pVolDesc->cbLogicalBlock.le));
+    if (pThis->cbBlock / pThis->cbSector > 128)
+        return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "Unsupported block size: %#x\n", pThis->cbBlock);
+
+    /*
+     * ... volume space size ...
+     */
+    pThis->cBlocksInPrimaryVolumeSpace = RT_LE2H_U32(pVolDesc->VolumeSpaceSize.le);
+    if (pThis->cBlocksInPrimaryVolumeSpace != RT_BE2H_U32(pVolDesc->VolumeSpaceSize.be))
+        return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid volume space size: {%#RX32,%#RX32}",
+                             RT_BE2H_U32(pVolDesc->VolumeSpaceSize.be), RT_LE2H_U32(pVolDesc->VolumeSpaceSize.le));
+    pThis->cbPrimaryVolumeSpace = pThis->cBlocksInPrimaryVolumeSpace * (uint64_t)pThis->cbBlock;
+
+    /*
+     * ... number of volumes in the set ...
+     */
+    pThis->cVolumesInSet = RT_LE2H_U16(pVolDesc->cVolumesInSet.le);
+    if (   pThis->cVolumesInSet != RT_BE2H_U16(pVolDesc->cVolumesInSet.be)
+        || pThis->cVolumesInSet == 0)
+        return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid volume set size: {%#RX16,%#RX16}",
+                             RT_BE2H_U16(pVolDesc->cVolumesInSet.be), RT_LE2H_U16(pVolDesc->cVolumesInSet.le));
+    if (pThis->cVolumesInSet > 32)
+        return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "Too large volume set size: %#x\n", pThis->cVolumesInSet);
+
+    /*
+     * ... primary volume sequence ID ...
+     */
+    pThis->idPrimaryVol = RT_LE2H_U16(pVolDesc->VolumeSeqNo.le);
+    if (pThis->idPrimaryVol != RT_BE2H_U16(pVolDesc->VolumeSeqNo.be))
+        return RTErrInfoSetF(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Invalid volume sequence ID: {%#RX16,%#RX16}",
+                             RT_BE2H_U16(pVolDesc->VolumeSeqNo.be), RT_LE2H_U16(pVolDesc->VolumeSeqNo.le));
+    if (   pThis->idPrimaryVol > pThis->cVolumesInSet
+        || pThis->idPrimaryVol < 1)
+        return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
+                             "Volume sequence ID out of of bound: %#x (1..%#x)\n", pThis->idPrimaryVol, pThis->cVolumesInSet);
+
+    /*
+     * ... and the root directory record.
+     */
+    *poffRootDirRec = offVolDesc + RT_OFFSETOF(ISO9660PRIMARYVOLDESC, RootDir.DirRec);
+    return rtFsIso9660VolHandleRootDir(pThis, &pVolDesc->RootDir.DirRec, pRootDir, pErrInfo);
+}
+
+
+/**
+ * Deal with a supplementary volume descriptor.
+ *
+ * @returns IPRT status code.
+ * @param   pThis           The ISO 9660 instance being initialized.
+ * @param   pVolDesc        The volume descriptor to handle.
+ * @param   offVolDesc      The disk offset of the volume descriptor.
+ * @param   pbUcs2Level     Where to return the joliet level, if found. Caller
+ *                          initializes this to zero, we'll return 1, 2 or 3 if
+ *                          joliet was detected.
+ * @param   pRootDir        Where to return the root directory, if found.
+ * @param   poffRootDirRec  Where to return the disk offset of the root dir.
+ * @param   pErrInfo        Where to return additional error info.  Can be NULL.
+ */
+static int rtFsIso9660VolHandleSupplementaryVolDesc(PRTFSISO9660VOL pThis, PCISO9660SUPVOLDESC pVolDesc, uint32_t offVolDesc,
+                                                    uint8_t *pbUcs2Level, PISO9660DIRREC pRootDir, uint64_t *poffRootDirRec,
+                                                    PRTERRINFO pErrInfo)
+{
+    if (pVolDesc->bFileStructureVersion != ISO9660_FILE_STRUCTURE_VERSION)
+        return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
+                             "Unsupported file structure version: %#x", pVolDesc->bFileStructureVersion);
+
+    /*
+     * Is this a joliet volume descriptor?  If not, we probably don't need to
+     * care about it.
+     */
+    if (   pVolDesc->abEscapeSequences[0] != ISO9660_JOLIET_ESC_SEQ_0
+        || pVolDesc->abEscapeSequences[1] != ISO9660_JOLIET_ESC_SEQ_1
+        || (   pVolDesc->abEscapeSequences[2] != ISO9660_JOLIET_ESC_SEQ_2_LEVEL_1
+            && pVolDesc->abEscapeSequences[2] != ISO9660_JOLIET_ESC_SEQ_2_LEVEL_2
+            && pVolDesc->abEscapeSequences[2] != ISO9660_JOLIET_ESC_SEQ_2_LEVEL_3))
+        return VINF_SUCCESS;
+
+    /*
+     * Skip if joliet is unwanted.
+     */
+    if (pThis->fFlags & RTFSISO9660_F_NO_JOLIET)
+        return VINF_SUCCESS;
+
+    /*
+     * Check that the joliet descriptor matches the primary one.
+     * Note! These are our assumptions and may be wrong.
+     */
+    if (pThis->cbBlock == 0)
+        return RTErrInfoSet(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
+                            "Supplementary joliet volume descriptor is not supported when appearing before the primary volume descriptor");
+    if (ISO9660_GET_ENDIAN(&pVolDesc->cbLogicalBlock) != pThis->cbBlock)
+        return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
+                             "Logical block size for joliet volume descriptor differs from primary: %#RX16 vs %#RX16\n",
+                             ISO9660_GET_ENDIAN(&pVolDesc->cbLogicalBlock), pThis->cbBlock);
+    if (ISO9660_GET_ENDIAN(&pVolDesc->VolumeSpaceSize) != pThis->cBlocksInPrimaryVolumeSpace)
+        return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
+                             "Volume space size for joliet volume descriptor differs from primary: %#RX32 vs %#RX32\n",
+                             ISO9660_GET_ENDIAN(&pVolDesc->VolumeSpaceSize), pThis->cBlocksInPrimaryVolumeSpace);
+    if (ISO9660_GET_ENDIAN(&pVolDesc->cVolumesInSet) != pThis->cVolumesInSet)
+        return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
+                             "Volume set size for joliet volume descriptor differs from primary: %#RX16 vs %#RX32\n",
+                             ISO9660_GET_ENDIAN(&pVolDesc->cVolumesInSet), pThis->cVolumesInSet);
+    if (ISO9660_GET_ENDIAN(&pVolDesc->VolumeSeqNo) != pThis->idPrimaryVol)
+        return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
+                             "Volume sequence ID for joliet volume descriptor differs from primary: %#RX16 vs %#RX32\n",
+                             ISO9660_GET_ENDIAN(&pVolDesc->VolumeSeqNo), pThis->idPrimaryVol);
+
+    if (*pbUcs2Level != 0)
+        return RTErrInfoSet(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "More than one supplementary joliet volume descriptor");
+
+    /*
+     * Switch to the joliet root dir as it has UTF-16 stuff in it.
+     */
+    int rc = rtFsIso9660VolHandleRootDir(pThis, &pVolDesc->RootDir.DirRec, pRootDir, pErrInfo);
+    if (RT_SUCCESS(rc))
+    {
+        *poffRootDirRec = offVolDesc + RT_OFFSETOF(ISO9660SUPVOLDESC, RootDir.DirRec);
+        *pbUcs2Level    = pVolDesc->abEscapeSequences[2] == ISO9660_JOLIET_ESC_SEQ_2_LEVEL_1 ? 1
+                        : pVolDesc->abEscapeSequences[2] == ISO9660_JOLIET_ESC_SEQ_2_LEVEL_2 ? 2 : 3;
+        Log(("ISO9660: Joliet with UCS-2 level %u\n", *pbUcs2Level));
+    }
+    return rc;
+}
+
+
+
+/**
+ * Worker for RTFsIso9660VolOpen.
+ *
+ * @returns IPRT status code.
+ * @param   pThis           The FAT VFS instance to initialize.
+ * @param   hVfsSelf        The FAT VFS handle (no reference consumed).
+ * @param   hVfsBacking     The file backing the alleged FAT file system.
+ *                          Reference is consumed (via rtFsIso9660Vol_Close).
+ * @param   fFlags          Flags, MBZ.
+ * @param   pErrInfo        Where to return additional error info.  Can be NULL.
+ */
+static int rtFsIso9660VolTryInit(PRTFSISO9660VOL pThis, RTVFS hVfsSelf, RTVFSFILE hVfsBacking, uint32_t fFlags, PRTERRINFO pErrInfo)
+{
+    uint32_t const cbSector = 2048;
+
+    /*
+     * First initialize the state so that rtFsIso9660Vol_Destroy won't trip up.
+     */
+    pThis->hVfsSelf                     = hVfsSelf;
+    pThis->hVfsBacking                  = hVfsBacking; /* Caller referenced it for us, we consume it; rtFsIso9660Vol_Destroy releases it. */
+    pThis->cbBacking                    = 0;
+    pThis->fFlags                       = fFlags;
+    pThis->cbSector                     = cbSector;
+    pThis->cbBlock                      = 0;
+    pThis->cBlocksInPrimaryVolumeSpace  = 0;
+    pThis->cbPrimaryVolumeSpace         = 0;
+    pThis->cVolumesInSet                = 0;
+    pThis->idPrimaryVol                 = UINT32_MAX;
+    pThis->fIsUtf16                     = false;
+    pThis->pRootDir                     = NULL;
+
+    /*
+     * Get stuff that may fail.
+     */
+    int rc = RTVfsFileGetSize(hVfsBacking, &pThis->cbBacking);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    /*
+     * Read the volume descriptors starting at logical sector 16.
+     */
+    union
+    {
+        uint8_t                 ab[_4K];
+        uint16_t                au16[_4K / 2];
+        uint32_t                au32[_4K / 4];
+        ISO9660VOLDESCHDR       VolDescHdr;
+        ISO9660BOOTRECORD       BootRecord;
+        ISO9660PRIMARYVOLDESC   PrimaryVolDesc;
+        ISO9660SUPVOLDESC       SupVolDesc;
+        ISO9660VOLPARTDESC      VolPartDesc;
+    } Buf;
+    RT_ZERO(Buf);
+
+    uint64_t        offRootDirRec           = UINT64_MAX;
+    ISO9660DIRREC   RootDir;
+    RT_ZERO(RootDir);
+
+    uint64_t        offJolietRootDirRec     = UINT64_MAX;
+    uint8_t         bJolietUcs2Level        = 0;
+    ISO9660DIRREC   JolietRootDir;
+    RT_ZERO(JolietRootDir);
+
+    uint32_t        cPrimaryVolDescs        = 0;
+    uint32_t        cSupplementaryVolDescs  = 0;
+    uint32_t        cBootRecordVolDescs     = 0;
+    uint32_t        offVolDesc              = 16 * cbSector;
+    for (uint32_t iVolDesc = 0; ; iVolDesc++, offVolDesc += cbSector)
+    {
+        if (iVolDesc > 32)
+            return RTErrInfoSet(pErrInfo, rc, "More than 32 volume descriptors, doesn't seem right...");
+
+        /* Read the next one and check the signature. */
+        rc = RTVfsFileReadAt(hVfsBacking, offVolDesc, &Buf, cbSector, NULL);
+        if (RT_FAILURE(rc))
+            return RTErrInfoSetF(pErrInfo, rc, "Unable to read volume descriptor #%u", iVolDesc);
+
+        if (   Buf.VolDescHdr.achStdId[0] != ISO9660VOLDESC_STD_ID_0
+            || Buf.VolDescHdr.achStdId[1] != ISO9660VOLDESC_STD_ID_1
+            || Buf.VolDescHdr.achStdId[2] != ISO9660VOLDESC_STD_ID_2
+            || Buf.VolDescHdr.achStdId[3] != ISO9660VOLDESC_STD_ID_3
+            || Buf.VolDescHdr.achStdId[4] != ISO9660VOLDESC_STD_ID_4)
+        {
+            if (!iVolDesc)
+                return RTErrInfoSetF(pErrInfo, VERR_VFS_UNKNOWN_FORMAT,
+                                     "No ISO 9660 CD001 signature, instead found: %.5Rhxs", Buf.VolDescHdr.achStdId);
+            return RTErrInfoSet(pErrInfo, VERR_VFS_BOGUS_FORMAT, "Missing terminator volume descriptor?");
+        }
+
+        /* Do type specific handling. */
+        Log(("ISO9660: volume desc #%u: type=%#x\n", iVolDesc, Buf.VolDescHdr.bDescType));
+        if (Buf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_PRIMARY)
+        {
+            cPrimaryVolDescs++;
+            if (Buf.VolDescHdr.bDescVersion != ISO9660PRIMARYVOLDESC_VERSION)
+                return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
+                                     "Unsupported primary volume descriptor version: %#x", Buf.VolDescHdr.bDescVersion);
+#ifdef LOG_ENABLED
+            rtFsIso9660VolLogPrimarySupplementaryVolDesc(&Buf.SupVolDesc);
+#endif
+            if (cPrimaryVolDescs > 1)
+                return RTErrInfoSet(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "More than one primary volume descriptor");
+            rc = rtFsIso9660VolHandlePrimaryVolDesc(pThis, &Buf.PrimaryVolDesc, offVolDesc, &RootDir, &offRootDirRec, pErrInfo);
+        }
+        else if (Buf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_SUPPLEMENTARY)
+        {
+            cSupplementaryVolDescs++;
+            if (Buf.VolDescHdr.bDescVersion != ISO9660SUPVOLDESC_VERSION)
+                return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
+                                     "Unsupported supplemental volume descriptor version: %#x", Buf.VolDescHdr.bDescVersion);
+#ifdef LOG_ENABLED
+            rtFsIso9660VolLogPrimarySupplementaryVolDesc(&Buf.SupVolDesc);
+#endif
+            rc = rtFsIso9660VolHandleSupplementaryVolDesc(pThis, &Buf.SupVolDesc, offVolDesc, &bJolietUcs2Level, &JolietRootDir,
+                                                          &offJolietRootDirRec, pErrInfo);
+        }
+        else if (Buf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_BOOT_RECORD)
+        {
+            cBootRecordVolDescs++;
+        }
+        else if (Buf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_TERMINATOR)
+        {
+            if (!cPrimaryVolDescs)
+                return RTErrInfoSet(pErrInfo, VERR_VFS_BOGUS_FORMAT, "No primary volume descriptor");
+            break;
+        }
+        else
+            return RTErrInfoSetF(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT,
+                                 "Unknown volume descriptor: %#x", Buf.VolDescHdr.bDescType);
+        if (RT_FAILURE(rc))
+            return rc;
+    }
+
+    /*
+     * We may be faced with choosing between joliet and rock ridge (we won't
+     * have this choice when RTFSISO9660_F_NO_JOLIET is set).  The joliet
+     * option is generally favorable as we don't have to guess wrt to the file
+     * name encoding.  So, we'll pick that for now.
+     *
+     * Note! Should we change this preference for joliet, there fun wrt making sure
+     *       there really is rock ridge stuff in the primary volume as well as
+     *       making sure there really is anything of value in the primary volume.
+     */
+    if (bJolietUcs2Level != 0)
+    {
+        pThis->fIsUtf16 = true;
+        return rtFsIso9660DirShrd_New(pThis, NULL, &JolietRootDir, 1, offJolietRootDirRec, &pThis->pRootDir);
+    }
+    return rtFsIso9660DirShrd_New(pThis, NULL, &RootDir, 1, offRootDirRec, &pThis->pRootDir);
+}
+
+
+/**
+ * Opens an ISO 9660 file system volume.
+ *
+ * @returns IPRT status code.
+ * @param   hVfsFileIn      The file or device backing the volume.
+ * @param   fFlags          RTFSISO9660_F_XXX.
+ * @param   phVfs           Where to return the virtual file system handle.
+ * @param   pErrInfo        Where to return additional error information.
+ */
+RTDECL(int) RTFsIso9660VolOpen(RTVFSFILE hVfsFileIn, uint32_t fFlags, PRTVFS phVfs, PRTERRINFO pErrInfo)
+{
+    /*
+     * Quick input validation.
+     */
+    AssertPtrReturn(phVfs, VERR_INVALID_POINTER);
+    *phVfs = NIL_RTVFS;
+    AssertReturn(!(fFlags & ~RTFSISO9660_F_VALID_MASK), VERR_INVALID_FLAGS);
+
+    uint32_t cRefs = RTVfsFileRetain(hVfsFileIn);
+    AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
+
+    /*
+     * Create a new FAT VFS instance and try initialize it using the given input file.
+     */
+    RTVFS hVfs   = NIL_RTVFS;
+    void *pvThis = NULL;
+    int rc = RTVfsNew(&g_rtFsIso9660VolOps, sizeof(RTFSISO9660VOL), NIL_RTVFS, RTVFSLOCK_CREATE_RW, &hVfs, &pvThis);
+    if (RT_SUCCESS(rc))
+    {
+        rc = rtFsIso9660VolTryInit((PRTFSISO9660VOL)pvThis, hVfs, hVfsFileIn, fFlags, pErrInfo);
+        if (RT_SUCCESS(rc))
+            *phVfs = hVfs;
+        else
+            RTVfsRelease(hVfs);
+    }
+    else
+        RTVfsFileRelease(hVfsFileIn);
+    return rc;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnValidate}
+ */
+static DECLCALLBACK(int) rtVfsChainIso9660Vol_Validate(PCRTVFSCHAINELEMENTREG pProviderReg, PRTVFSCHAINSPEC pSpec,
+                                                       PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError, PRTERRINFO pErrInfo)
+{
+    RT_NOREF(pProviderReg, pSpec);
+
+    /*
+     * Basic checks.
+     */
+    if (pElement->enmTypeIn != RTVFSOBJTYPE_FILE)
+        return pElement->enmTypeIn == RTVFSOBJTYPE_INVALID ? VERR_VFS_CHAIN_CANNOT_BE_FIRST_ELEMENT : VERR_VFS_CHAIN_TAKES_FILE;
+    if (   pElement->enmType != RTVFSOBJTYPE_VFS
+        && pElement->enmType != RTVFSOBJTYPE_DIR)
+        return VERR_VFS_CHAIN_ONLY_DIR_OR_VFS;
+    if (pElement->cArgs > 1)
+        return VERR_VFS_CHAIN_AT_MOST_ONE_ARG;
+
+    /*
+     * Parse the flag if present, save in pElement->uProvider.
+     */
+    uint32_t fFlags = 0;
+    if (pElement->cArgs > 0)
+    {
+        for (uint32_t iArg = 0; iArg < pElement->cArgs; iArg++)
+        {
+            const char *psz = pElement->paArgs[iArg].psz;
+            if (*psz)
+            {
+                if (!strcmp(psz, "nojoliet"))
+                    fFlags |= RTFSISO9660_F_NO_JOLIET;
+                else if (!strcmp(psz, "norock"))
+                    fFlags |= RTFSISO9660_F_NO_ROCK;
+                else
+                {
+                    *poffError = pElement->paArgs[iArg].offSpec;
+                    return RTErrInfoSet(pErrInfo, VERR_VFS_CHAIN_INVALID_ARGUMENT, "Only knows: 'nojoliet' and 'norock'");
+                }
+            }
+        }
+    }
+
+    pElement->uProvider = fFlags;
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnInstantiate}
+ */
+static DECLCALLBACK(int) rtVfsChainIso9660Vol_Instantiate(PCRTVFSCHAINELEMENTREG pProviderReg, PCRTVFSCHAINSPEC pSpec,
+                                                          PCRTVFSCHAINELEMSPEC pElement, RTVFSOBJ hPrevVfsObj,
+                                                          PRTVFSOBJ phVfsObj, uint32_t *poffError, PRTERRINFO pErrInfo)
+{
+    RT_NOREF(pProviderReg, pSpec, poffError);
+
+    int         rc;
+    RTVFSFILE   hVfsFileIn = RTVfsObjToFile(hPrevVfsObj);
+    if (hVfsFileIn != NIL_RTVFSFILE)
+    {
+        RTVFS hVfs;
+        rc = RTFsIso9660VolOpen(hVfsFileIn, pElement->uProvider, &hVfs, pErrInfo);
+        RTVfsFileRelease(hVfsFileIn);
+        if (RT_SUCCESS(rc))
+        {
+            *phVfsObj = RTVfsObjFromVfs(hVfs);
+            RTVfsRelease(hVfs);
+            if (*phVfsObj != NIL_RTVFSOBJ)
+                return VINF_SUCCESS;
+            rc = VERR_VFS_CHAIN_CAST_FAILED;
+        }
+    }
+    else
+        rc = VERR_VFS_CHAIN_CAST_FAILED;
+    return rc;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnCanReuseElement}
+ */
+static DECLCALLBACK(bool) rtVfsChainIso9660Vol_CanReuseElement(PCRTVFSCHAINELEMENTREG pProviderReg,
+                                                               PCRTVFSCHAINSPEC pSpec, PCRTVFSCHAINELEMSPEC pElement,
+                                                               PCRTVFSCHAINSPEC pReuseSpec, PCRTVFSCHAINELEMSPEC pReuseElement)
+{
+    RT_NOREF(pProviderReg, pSpec, pReuseSpec);
+    if (   pElement->paArgs[0].uProvider == pReuseElement->paArgs[0].uProvider
+        || !pReuseElement->paArgs[0].uProvider)
+        return true;
+    return false;
+}
+
+
+/** VFS chain element 'file'. */
+static RTVFSCHAINELEMENTREG g_rtVfsChainIso9660VolReg =
+{
+    /* uVersion = */            RTVFSCHAINELEMENTREG_VERSION,
+    /* fReserved = */           0,
+    /* pszName = */             "isofs",
+    /* ListEntry = */           { NULL, NULL },
+    /* pszHelp = */             "Open a ISO 9660 file system, requires a file object on the left side.\n"
+                                "The 'nojoliet' option make it ignore any joliet supplemental volume.\n"
+                                "The 'norock' option make it ignore any rock ridge info.\n",
+    /* pfnValidate = */         rtVfsChainIso9660Vol_Validate,
+    /* pfnInstantiate = */      rtVfsChainIso9660Vol_Instantiate,
+    /* pfnCanReuseElement = */  rtVfsChainIso9660Vol_CanReuseElement,
+    /* uEndMarker = */          RTVFSCHAINELEMENTREG_VERSION
+};
+
+RTVFSCHAIN_AUTO_REGISTER_ELEMENT_PROVIDER(&g_rtVfsChainIso9660VolReg, rtVfsChainIso9660VolReg);
+
