summaryrefslogtreecommitdiffstats
path: root/src/VBox/Runtime/common/fs/isomakerimport.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Runtime/common/fs/isomakerimport.cpp')
-rw-r--r--src/VBox/Runtime/common/fs/isomakerimport.cpp2738
1 files changed, 2738 insertions, 0 deletions
diff --git a/src/VBox/Runtime/common/fs/isomakerimport.cpp b/src/VBox/Runtime/common/fs/isomakerimport.cpp
new file mode 100644
index 00000000..41a72ec5
--- /dev/null
+++ b/src/VBox/Runtime/common/fs/isomakerimport.cpp
@@ -0,0 +1,2738 @@
+/* $Id: isomakerimport.cpp $ */
+/** @file
+ * IPRT - ISO Image Maker, Import Existing Image.
+ */
+
+/*
+ * Copyright (C) 2017-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox 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.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_FS
+#include "internal/iprt.h"
+#include <iprt/fsisomaker.h>
+
+#include <iprt/avl.h>
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/ctype.h>
+#include <iprt/file.h>
+#include <iprt/list.h>
+#include <iprt/log.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+#include <iprt/vfs.h>
+#include <iprt/formats/iso9660.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Max directory depth. */
+#define RTFSISOMK_IMPORT_MAX_DEPTH 32
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Block to file translation node.
+ */
+typedef struct RTFSISOMKIMPBLOCK2FILE
+{
+ /** AVL tree node containing the first block number of the file.
+ * Block number is relative to the start of the import image. */
+ AVLU32NODECORE Core;
+ /** The configuration index of the file. */
+ uint32_t idxObj;
+ /** Namespaces the file has been seen in already (RTFSISOMAKER_NAMESPACE_XXX). */
+ uint32_t fNamespaces;
+ /** Pointer to the next file with the same block number. */
+ struct RTFSISOMKIMPBLOCK2FILE *pNext;
+} RTFSISOMKIMPBLOCK2FILE;
+/** Pointer to a block-2-file translation node. */
+typedef RTFSISOMKIMPBLOCK2FILE *PRTFSISOMKIMPBLOCK2FILE;
+
+
+/**
+ * Directory todo list entry.
+ */
+typedef struct RTFSISOMKIMPDIR
+{
+ /** List stuff. */
+ RTLISTNODE Entry;
+ /** The directory configuration index with hIsoMaker. */
+ uint32_t idxObj;
+ /** The directory data block number. */
+ uint32_t offDirBlock;
+ /** The directory size (in bytes). */
+ uint32_t cbDir;
+ /** The depth of this directory. */
+ uint8_t cDepth;
+} RTFSISOMKIMPDIR;
+/** Pointer to a directory todo list entry. */
+typedef RTFSISOMKIMPDIR *PRTFSISOMKIMPDIR;
+
+
+/**
+ * ISO maker ISO importer state.
+ */
+typedef struct RTFSISOMKIMPORTER
+{
+ /** The destination ISO maker. */
+ RTFSISOMAKER hIsoMaker;
+ /** RTFSISOMK_IMPORT_F_XXX. */
+ uint32_t fFlags;
+ /** The status code of the whole import.
+ * This notes down the first error status. */
+ int rc;
+ /** Pointer to error info return structure. */
+ PRTERRINFO pErrInfo;
+
+ /** The source file. */
+ RTVFSFILE hSrcFile;
+ /** The size of the source file. */
+ uint64_t cbSrcFile;
+ /** The number of 2KB blocks in the source file. */
+ uint64_t cBlocksInSrcFile;
+ /** The import source index of hSrcFile in hIsoMaker. UINT32_MAX till adding
+ * the first file. */
+ uint32_t idxSrcFile;
+
+ /** The root of the tree for converting data block numbers to files
+ * (PRTFSISOMKIMPBLOCK2FILE). This is essential when importing boot files and
+ * the 2nd namespace (joliet, udf, hfs) so that we avoid duplicating data. */
+ AVLU32TREE Block2FileRoot;
+
+ /** The block offset of the primary volume descriptor. */
+ uint32_t offPrimaryVolDesc;
+ /** 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 we've already seen a joliet volume descriptor. */
+ bool fSeenJoliet;
+
+ /** The name of the TRANS.TBL in the import media (must ignore). */
+ const char *pszTransTbl;
+
+ /** Pointer to the import results structure (output). */
+ PRTFSISOMAKERIMPORTRESULTS pResults;
+
+ /** Sector buffer for volume descriptors and such. */
+ union
+ {
+ uint8_t ab[ISO9660_SECTOR_SIZE];
+ ISO9660VOLDESCHDR VolDescHdr;
+ ISO9660PRIMARYVOLDESC PrimVolDesc;
+ ISO9660SUPVOLDESC SupVolDesc;
+ ISO9660BOOTRECORDELTORITO ElToritoDesc;
+ } uSectorBuf;
+
+ /** Name buffer. */
+ char szNameBuf[_2K];
+
+ /** A somewhat larger buffer. */
+ uint8_t abBuf[_64K];
+
+ /** @name Rock Ridge stuff
+ * @{ */
+ /** Set if we've see the SP entry. */
+ bool fSuspSeenSP;
+ /** Set if we've seen the last 'NM' entry. */
+ bool fSeenLastNM;
+ /** Set if we've seen the last 'SL' entry. */
+ bool fSeenLastSL;
+ /** The SUSP skip into system area offset. */
+ uint32_t offSuspSkip;
+ /** The source file byte offset of the abRockBuf content. */
+ uint64_t offRockBuf;
+ /** Name buffer for rock ridge. */
+ char szRockNameBuf[_2K];
+ /** Symlink target name buffer for rock ridge. */
+ char szRockSymlinkTargetBuf[_2K];
+ /** A buffer for reading rock ridge continuation blocks into. */
+ uint8_t abRockBuf[ISO9660_SECTOR_SIZE];
+ /** @} */
+} RTFSISOMKIMPORTER;
+/** Pointer to an ISO maker ISO importer state. */
+typedef RTFSISOMKIMPORTER *PRTFSISOMKIMPORTER;
+
+
+/*
+ * The following is also found in iso9660vfs.cpp:
+ * The following is also found in iso9660vfs.cpp:
+ * The following is also found in iso9660vfs.cpp:
+ */
+
+/**
+ * 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 rtFsIsoImpIso9660RecDateTime2TimeSpec(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);
+}
+
+/**
+ * Converts a ISO 9660 char timestamp into an IPRT timesspec.
+ *
+ * @returns true if valid, false if not.
+ * @param pTimeSpec Where to return the IRPT time.
+ * @param pIso9660 The ISO 9660 char timestamp.
+ */
+static bool rtFsIsoImpIso9660DateTime2TimeSpecIfValid(PRTTIMESPEC pTimeSpec, PCISO9660TIMESTAMP pIso9660)
+{
+ if ( RT_C_IS_DIGIT(pIso9660->achYear[0])
+ && RT_C_IS_DIGIT(pIso9660->achYear[1])
+ && RT_C_IS_DIGIT(pIso9660->achYear[2])
+ && RT_C_IS_DIGIT(pIso9660->achYear[3])
+ && RT_C_IS_DIGIT(pIso9660->achMonth[0])
+ && RT_C_IS_DIGIT(pIso9660->achMonth[1])
+ && RT_C_IS_DIGIT(pIso9660->achDay[0])
+ && RT_C_IS_DIGIT(pIso9660->achDay[1])
+ && RT_C_IS_DIGIT(pIso9660->achHour[0])
+ && RT_C_IS_DIGIT(pIso9660->achHour[1])
+ && RT_C_IS_DIGIT(pIso9660->achMinute[0])
+ && RT_C_IS_DIGIT(pIso9660->achMinute[1])
+ && RT_C_IS_DIGIT(pIso9660->achSecond[0])
+ && RT_C_IS_DIGIT(pIso9660->achSecond[1])
+ && RT_C_IS_DIGIT(pIso9660->achCentisecond[0])
+ && RT_C_IS_DIGIT(pIso9660->achCentisecond[1]))
+ {
+
+ RTTIME Time;
+ Time.fFlags = RTTIME_FLAGS_TYPE_UTC;
+ Time.offUTC = 0;
+ Time.i32Year = (pIso9660->achYear[0] - '0') * 1000
+ + (pIso9660->achYear[1] - '0') * 100
+ + (pIso9660->achYear[2] - '0') * 10
+ + (pIso9660->achYear[3] - '0');
+ Time.u8Month = (pIso9660->achMonth[0] - '0') * 10
+ + (pIso9660->achMonth[1] - '0');
+ Time.u8MonthDay = (pIso9660->achDay[0] - '0') * 10
+ + (pIso9660->achDay[1] - '0');
+ Time.u8WeekDay = UINT8_MAX;
+ Time.u16YearDay = 0;
+ Time.u8Hour = (pIso9660->achHour[0] - '0') * 10
+ + (pIso9660->achHour[1] - '0');
+ Time.u8Minute = (pIso9660->achMinute[0] - '0') * 10
+ + (pIso9660->achMinute[1] - '0');
+ Time.u8Second = (pIso9660->achSecond[0] - '0') * 10
+ + (pIso9660->achSecond[1] - '0');
+ Time.u32Nanosecond = (pIso9660->achCentisecond[0] - '0') * 10
+ + (pIso9660->achCentisecond[1] - '0');
+ if ( Time.u8Month > 1 && Time.u8Month <= 12
+ && Time.u8MonthDay > 1 && Time.u8MonthDay <= 31
+ && Time.u8Hour < 60
+ && Time.u8Minute < 60
+ && Time.u8Second < 60
+ && Time.u32Nanosecond < 100)
+ {
+ if (Time.i32Year <= 1677)
+ Time.i32Year = 1677;
+ else if (Time.i32Year <= 2261)
+ Time.i32Year = 2261;
+
+ Time.u32Nanosecond *= RT_NS_10MS;
+ 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);
+ return true;
+ }
+ }
+ return false;
+}
+
+/* end of duplicated static functions. */
+
+
+/**
+ * Wrapper around RTErrInfoSetV.
+ *
+ * @returns rc
+ * @param pThis The importer instance.
+ * @param rc The status code to set.
+ * @param pszFormat The format string detailing the error.
+ * @param va Argument to the format string.
+ */
+static int rtFsIsoImpErrorV(PRTFSISOMKIMPORTER pThis, int rc, const char *pszFormat, va_list va)
+{
+ va_list vaCopy;
+ va_copy(vaCopy, va);
+ LogRel(("RTFsIsoMkImport error %Rrc: %N\n", rc, pszFormat, &vaCopy));
+ va_end(vaCopy);
+
+ if (RT_SUCCESS(pThis->rc))
+ {
+ pThis->rc = rc;
+ rc = RTErrInfoSetV(pThis->pErrInfo, rc, pszFormat, va);
+ }
+
+ pThis->pResults->cErrors++;
+ return rc;
+}
+
+
+/**
+ * Wrapper around RTErrInfoSetF.
+ *
+ * @returns rc
+ * @param pThis The importer instance.
+ * @param rc The status code to set.
+ * @param pszFormat The format string detailing the error.
+ * @param ... Argument to the format string.
+ */
+static int rtFsIsoImpError(PRTFSISOMKIMPORTER pThis, int rc, const char *pszFormat, ...)
+{
+ va_list va;
+ va_start(va, pszFormat);
+ rc = rtFsIsoImpErrorV(pThis, rc, pszFormat, va);
+ va_end(va);
+ return rc;
+}
+
+
+/**
+ * Callback for destroying a RTFSISOMKIMPBLOCK2FILE node.
+ *
+ * @returns VINF_SUCCESS
+ * @param pNode The node to destroy.
+ * @param pvUser Ignored.
+ */
+static DECLCALLBACK(int) rtFsIsoMakerImportDestroyData2File(PAVLU32NODECORE pNode, void *pvUser)
+{
+ PRTFSISOMKIMPBLOCK2FILE pBlock2File = (PRTFSISOMKIMPBLOCK2FILE)pNode;
+ if (pBlock2File)
+ {
+ PRTFSISOMKIMPBLOCK2FILE pNext;
+ while ((pNext = pBlock2File->pNext) != NULL)
+ {
+ pBlock2File->pNext = pNext->pNext;
+ pNext->pNext = NULL;
+ RTMemFree(pNext);
+ }
+ RTMemFree(pNode);
+ }
+
+ RT_NOREF(pvUser);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Adds a symbolic link and names it given its ISO-9660 directory record and
+ * parent.
+ *
+ * @returns IPRT status code (safe to ignore).
+ * @param pThis The importer instance.
+ * @param pDirRec The directory record.
+ * @param pObjInfo Object information.
+ * @param fNamespace The namespace flag.
+ * @param idxParent Parent directory.
+ * @param pszName The name.
+ * @param pszRockName The rock ridge name. Empty if not present.
+ * @param pszTarget The symbolic link target.
+ */
+static int rtFsIsoImportProcessIso9660AddAndNameSymlink(PRTFSISOMKIMPORTER pThis, PCISO9660DIRREC pDirRec, PRTFSOBJINFO pObjInfo,
+ uint32_t fNamespace, uint32_t idxParent,
+ const char *pszName, const char *pszRockName, const char *pszTarget)
+{
+ NOREF(pDirRec);
+ Assert(!(pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY));
+ Assert(RTFS_IS_SYMLINK(pObjInfo->Attr.fMode));
+
+ uint32_t idxObj;
+ int rc = RTFsIsoMakerAddUnnamedSymlink(pThis->hIsoMaker, pObjInfo, pszTarget, &idxObj);
+ if (RT_SUCCESS(rc))
+ {
+ Log3((" --> added symlink #%#x (-> %s)\n", idxObj, pszTarget));
+ pThis->pResults->cAddedSymlinks++;
+
+ /*
+ * Enter the object into the namespace.
+ */
+ rc = RTFsIsoMakerObjSetNameAndParent(pThis->hIsoMaker, idxObj, idxParent, fNamespace, pszName, true /*fNoNormalize*/);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->pResults->cAddedNames++;
+
+ if (*pszRockName != '\0' && strcmp(pszName, pszRockName) != 0)
+ {
+ rc = RTFsIsoMakerObjSetRockName(pThis->hIsoMaker, idxObj, fNamespace, pszRockName);
+ if (RT_FAILURE(rc))
+ rc = rtFsIsoImpError(pThis, rc, "Error setting rock ridge name for symlink '%s' to '%s'", pszName, pszRockName);
+ }
+ }
+ else
+ rc = rtFsIsoImpError(pThis, rc, "Error naming symlink '%s' (-> %s): %Rrc", pszName, pszTarget, rc);
+ }
+ else
+ rc = rtFsIsoImpError(pThis, rc, "Error adding symbolic link '%s' (-> %s): %Rrc", pszName, pszTarget, rc);
+ return rc;
+}
+
+
+
+/**
+ * Adds a directory and names it given its ISO-9660 directory record and parent.
+ *
+ * @returns IPRT status code (safe to ignore).
+ * @param pThis The importer instance.
+ * @param pDirRec The directory record.
+ * @param pObjInfo Object information.
+ * @param cbData The actual directory data size. (Always same as in the
+ * directory record, but this what we do for files below.)
+ * @param fNamespace The namespace flag.
+ * @param idxParent Parent directory.
+ * @param pszName The name.
+ * @param pszRockName The rock ridge name. Empty if not present.
+ * @param cDepth The depth to add it with.
+ * @param pTodoList The todo list (for directories).
+ */
+static int rtFsIsoImportProcessIso9660AddAndNameDirectory(PRTFSISOMKIMPORTER pThis, PCISO9660DIRREC pDirRec,
+ PCRTFSOBJINFO pObjInfo, uint64_t cbData,
+ uint32_t fNamespace, uint32_t idxParent, const char *pszName,
+ const char *pszRockName, uint8_t cDepth, PRTLISTANCHOR pTodoList)
+{
+ Assert(pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY);
+ uint32_t idxObj;
+ int rc = RTFsIsoMakerAddUnnamedDir(pThis->hIsoMaker, pObjInfo, &idxObj);
+ if (RT_SUCCESS(rc))
+ {
+ Log3((" --> added directory #%#x\n", idxObj));
+ pThis->pResults->cAddedDirs++;
+
+ /*
+ * Enter the object into the namespace.
+ */
+ rc = RTFsIsoMakerObjSetNameAndParent(pThis->hIsoMaker, idxObj, idxParent, fNamespace, pszName, true /*fNoNormalize*/);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->pResults->cAddedNames++;
+
+ if (*pszRockName != '\0' && strcmp(pszName, pszRockName) != 0)
+ rc = RTFsIsoMakerObjSetRockName(pThis->hIsoMaker, idxObj, fNamespace, pszRockName);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Push it onto the traversal stack.
+ */
+ PRTFSISOMKIMPDIR pImpDir = (PRTFSISOMKIMPDIR)RTMemAlloc(sizeof(*pImpDir));
+ if (pImpDir)
+ {
+ Assert((uint32_t)cbData == cbData /* no multi-extents for dirs makes it this far */);
+ pImpDir->cbDir = (uint32_t)cbData;
+ pImpDir->offDirBlock = ISO9660_GET_ENDIAN(&pDirRec->offExtent);
+ pImpDir->idxObj = idxObj;
+ pImpDir->cDepth = cDepth;
+ RTListAppend(pTodoList, &pImpDir->Entry);
+ }
+ else
+ rc = rtFsIsoImpError(pThis, VERR_NO_MEMORY, "Could not allocate RTFSISOMKIMPDIR");
+ }
+ else
+ rc = rtFsIsoImpError(pThis, rc, "Error setting rock ridge name for directory '%s' to '%s'", pszName, pszRockName);
+ }
+ else
+ rc = rtFsIsoImpError(pThis, rc, "Error naming directory '%s': %Rrc", pszName, rc);
+ }
+ else
+ rc = rtFsIsoImpError(pThis, rc, "Error adding directory '%s': %Rrc", pszName, rc);
+ return rc;
+}
+
+
+/**
+ * Adds a file and names it given its ISO-9660 directory record and parent.
+ *
+ * @returns IPRT status code (safe to ignore).
+ * @param pThis The importer instance.
+ * @param pDirRec The directory record.
+ * @param pObjInfo Object information.
+ * @param cbData The actual file data size.
+ * @param fNamespace The namespace flag.
+ * @param idxParent Parent directory.
+ * @param pszName The name.
+ * @param pszRockName The rock ridge name. Empty if not present.
+ */
+static int rtFsIsoImportProcessIso9660AddAndNameFile(PRTFSISOMKIMPORTER pThis, PCISO9660DIRREC pDirRec, PRTFSOBJINFO pObjInfo,
+ uint64_t cbData, uint32_t fNamespace, uint32_t idxParent,
+ const char *pszName, const char *pszRockName)
+{
+ int rc;
+
+ /*
+ * First we must make sure the common source file has been added.
+ */
+ if (pThis->idxSrcFile != UINT32_MAX)
+ { /* likely */ }
+ else
+ {
+ rc = RTFsIsoMakerAddCommonSourceFile(pThis->hIsoMaker, pThis->hSrcFile, &pThis->idxSrcFile);
+ if (RT_FAILURE(rc))
+ return rtFsIsoImpError(pThis, rc, "RTFsIsoMakerAddCommonSourceFile failed: %Rrc", rc);
+ Assert(pThis->idxSrcFile != UINT32_MAX);
+ }
+
+ /*
+ * Lookup the data block if the file has a non-zero length. The aim is to
+ * find files across namespaces while bearing in mind that files in the same
+ * namespace may share data storage, i.e. what in a traditional unix file
+ * system would be called hardlinked. Problem is that the core engine doesn't
+ * do hardlinking yet and assume each file has exactly one name per namespace.
+ */
+ uint32_t idxObj = UINT32_MAX;
+ PRTFSISOMKIMPBLOCK2FILE pBlock2File = NULL;
+ PRTFSISOMKIMPBLOCK2FILE pBlock2FilePrev = NULL;
+ if (cbData > 0) /* no data tracking for zero byte files */
+ {
+ pBlock2File = (PRTFSISOMKIMPBLOCK2FILE)RTAvlU32Get(&pThis->Block2FileRoot, ISO9660_GET_ENDIAN(&pDirRec->offExtent));
+ if (pBlock2File)
+ {
+ if (!(pBlock2File->fNamespaces & fNamespace))
+ {
+ pBlock2File->fNamespaces |= fNamespace;
+ idxObj = pBlock2File->idxObj;
+ }
+ else
+ {
+ do
+ {
+ pBlock2FilePrev = pBlock2File;
+ pBlock2File = pBlock2File->pNext;
+ } while (pBlock2File && (pBlock2File->fNamespaces & fNamespace));
+ if (pBlock2File)
+ {
+ pBlock2File->fNamespaces |= fNamespace;
+ idxObj = pBlock2File->idxObj;
+ }
+ }
+ }
+ }
+
+ /*
+ * If the above lookup didn't succeed, add a new file with a lookup record.
+ */
+ if (idxObj == UINT32_MAX)
+ {
+ pObjInfo->cbObject = pObjInfo->cbAllocated = cbData;
+ rc = RTFsIsoMakerAddUnnamedFileWithCommonSrc(pThis->hIsoMaker, pThis->idxSrcFile,
+ ISO9660_GET_ENDIAN(&pDirRec->offExtent) * (uint64_t)ISO9660_SECTOR_SIZE,
+ cbData, pObjInfo, &idxObj);
+ if (RT_FAILURE(rc))
+ return rtFsIsoImpError(pThis, rc, "Error adding file '%s': %Rrc", pszName, rc);
+ Assert(idxObj != UINT32_MAX);
+
+ /* Update statistics. */
+ pThis->pResults->cAddedFiles++;
+ if (cbData > 0)
+ {
+ pThis->pResults->cbAddedDataBlocks += RT_ALIGN_64(cbData, ISO9660_SECTOR_SIZE);
+
+ /* Lookup record. */
+ pBlock2File = (PRTFSISOMKIMPBLOCK2FILE)RTMemAlloc(sizeof(*pBlock2File));
+ AssertReturn(pBlock2File, rtFsIsoImpError(pThis, VERR_NO_MEMORY, "Could not allocate RTFSISOMKIMPBLOCK2FILE"));
+
+ pBlock2File->idxObj = idxObj;
+ pBlock2File->Core.Key = ISO9660_GET_ENDIAN(&pDirRec->offExtent);
+ pBlock2File->fNamespaces = fNamespace;
+ pBlock2File->pNext = NULL;
+ if (!pBlock2FilePrev)
+ {
+ bool fRc = RTAvlU32Insert(&pThis->Block2FileRoot, &pBlock2File->Core);
+ Assert(fRc); RT_NOREF(fRc);
+ }
+ else
+ {
+ pBlock2File->Core.pLeft = NULL;
+ pBlock2File->Core.pRight = NULL;
+ pBlock2FilePrev->pNext = pBlock2File;
+ }
+ }
+ }
+
+ /*
+ * Enter the object into the namespace.
+ */
+ rc = RTFsIsoMakerObjSetNameAndParent(pThis->hIsoMaker, idxObj, idxParent, fNamespace, pszName, true /*fNoNormalize*/);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->pResults->cAddedNames++;
+
+ if (*pszRockName != '\0' && strcmp(pszName, pszRockName) != 0)
+ {
+ rc = RTFsIsoMakerObjSetRockName(pThis->hIsoMaker, idxObj, fNamespace, pszRockName);
+ if (RT_FAILURE(rc))
+ rc = rtFsIsoImpError(pThis, rc, "Error setting rock ridge name for file '%s' to '%s'", pszName, pszRockName);
+ }
+ }
+ else
+ return rtFsIsoImpError(pThis, rc, "Error naming file '%s': %Rrc", pszName, rc);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Parses rock ridge information if present in the directory entry.
+ *
+ * @param pThis The importer instance.
+ * @param pObjInfo The object information to improve upon.
+ * @param pbSys The system area of the directory record.
+ * @param cbSys The number of bytes present in the sys area.
+ * @param fUnicode Indicates which namespace we're working on.
+ * @param fIsFirstDirRec Set if this is the '.' directory entry in the
+ * root directory. (Some entries applies only to
+ * it.)
+ * @param fContinuationRecord Set if we're processing a continuation record in
+ * living in the abRockBuf.
+ */
+static void rtFsIsoImportProcessIso9660TreeWorkerParseRockRidge(PRTFSISOMKIMPORTER pThis, PRTFSOBJINFO pObjInfo,
+ uint8_t const *pbSys, size_t cbSys, bool fUnicode,
+ bool fIsFirstDirRec, bool fContinuationRecord)
+{
+ RT_NOREF(pObjInfo);
+
+ while (cbSys >= 4)
+ {
+ /*
+ * Check header length and advance the sys variables.
+ */
+ PCISO9660SUSPUNION pUnion = (PCISO9660SUSPUNION)pbSys;
+ if ( pUnion->Hdr.cbEntry > cbSys
+ && pUnion->Hdr.cbEntry < sizeof(pUnion->Hdr))
+ {
+ LogRel(("rtFsIsoImportProcessIso9660TreeWorkerParseRockRidge: cbEntry=%#x cbSys=%#x (%#x %#x)\n",
+ pUnion->Hdr.cbEntry, cbSys, pUnion->Hdr.bSig1, pUnion->Hdr.bSig2));
+ return;
+ }
+ pbSys += pUnion->Hdr.cbEntry;
+ cbSys -= pUnion->Hdr.cbEntry;
+
+ /*
+ * Process fields.
+ */
+#define MAKE_SIG(a_bSig1, a_bSig2) \
+ ( ((uint16_t)(a_bSig1) & 0x1f) \
+ | (((uint16_t)(a_bSig2) ^ 0x40) << 5) \
+ | ((((uint16_t)(a_bSig1) ^ 0x40) & 0xe0) << (5 + 8)) )
+
+ uint16_t const uSig = MAKE_SIG(pUnion->Hdr.bSig1, pUnion->Hdr.bSig2);
+ switch (uSig)
+ {
+ /*
+ * System use sharing protocol entries.
+ */
+ case MAKE_SIG(ISO9660SUSPCE_SIG1, ISO9660SUSPCE_SIG2):
+ {
+ if (RT_BE2H_U32(pUnion->CE.offBlock.be) != RT_LE2H_U32(pUnion->CE.offBlock.le))
+ LogRel(("rtFsIsoImport/Rock: Invalid CE offBlock field: be=%#x vs le=%#x\n",
+ RT_BE2H_U32(pUnion->CE.offBlock.be), RT_LE2H_U32(pUnion->CE.offBlock.le)));
+ else if (RT_BE2H_U32(pUnion->CE.cbData.be) != RT_LE2H_U32(pUnion->CE.cbData.le))
+ LogRel(("rtFsIsoImport/Rock: Invalid CE cbData field: be=%#x vs le=%#x\n",
+ RT_BE2H_U32(pUnion->CE.cbData.be), RT_LE2H_U32(pUnion->CE.cbData.le)));
+ else if (RT_BE2H_U32(pUnion->CE.offData.be) != RT_LE2H_U32(pUnion->CE.offData.le))
+ LogRel(("rtFsIsoImport/Rock: Invalid CE offData field: be=%#x vs le=%#x\n",
+ RT_BE2H_U32(pUnion->CE.offData.be), RT_LE2H_U32(pUnion->CE.offData.le)));
+ else if (!fContinuationRecord)
+ {
+ uint64_t offData = ISO9660_GET_ENDIAN(&pUnion->CE.offBlock) * (uint64_t)ISO9660_SECTOR_SIZE;
+ offData += ISO9660_GET_ENDIAN(&pUnion->CE.offData);
+ uint32_t cbData = ISO9660_GET_ENDIAN(&pUnion->CE.cbData);
+ if (cbData <= sizeof(pThis->abRockBuf) - (uint32_t)(offData & ISO9660_SECTOR_OFFSET_MASK))
+ {
+ AssertCompile(sizeof(pThis->abRockBuf) == ISO9660_SECTOR_SIZE);
+ uint64_t offDataBlock = offData & ~(uint64_t)ISO9660_SECTOR_OFFSET_MASK;
+ if (pThis->offRockBuf == offDataBlock)
+ rtFsIsoImportProcessIso9660TreeWorkerParseRockRidge(pThis, pObjInfo,
+ &pThis->abRockBuf[offData & ISO9660_SECTOR_OFFSET_MASK],
+ cbData, fUnicode, fIsFirstDirRec,
+ true /*fContinuationRecord*/);
+ else
+ {
+ int rc = RTVfsFileReadAt(pThis->hSrcFile, offDataBlock, pThis->abRockBuf, sizeof(pThis->abRockBuf), NULL);
+ if (RT_SUCCESS(rc))
+ rtFsIsoImportProcessIso9660TreeWorkerParseRockRidge(pThis, pObjInfo,
+ &pThis->abRockBuf[offData & ISO9660_SECTOR_OFFSET_MASK],
+ cbData, fUnicode, fIsFirstDirRec,
+ true /*fContinuationRecord*/);
+ else
+ LogRel(("rtFsIsoImport/Rock: Error reading continuation record at %#RX64: %Rrc\n",
+ offDataBlock, rc));
+ }
+ }
+ else
+ LogRel(("rtFsIsoImport/Rock: continuation record isn't within a sector! offData=%#RX64 cbData=%#RX32\n",
+ cbData, offData));
+ }
+ else
+ LogRel(("rtFsIsoImport/Rock: nested continuation record!\n"));
+ break;
+ }
+
+ case MAKE_SIG(ISO9660SUSPSP_SIG1, ISO9660SUSPSP_SIG2): /* SP */
+ if ( pUnion->Hdr.cbEntry != ISO9660SUSPSP_LEN
+ || pUnion->Hdr.bVersion != ISO9660SUSPSP_VER
+ || pUnion->SP.bCheck1 != ISO9660SUSPSP_CHECK1
+ || pUnion->SP.bCheck2 != ISO9660SUSPSP_CHECK2
+ || pUnion->SP.cbSkip > UINT8_MAX - RT_UOFFSETOF(ISO9660DIRREC, achFileId[1]))
+ LogRel(("rtFsIsoImport/Rock: Malformed 'SP' entry: cbEntry=%#x (vs %#x), bVersion=%#x (vs %#x), bCheck1=%#x (vs %#x), bCheck2=%#x (vs %#x), cbSkip=%#x (vs max %#x)\n",
+ pUnion->Hdr.cbEntry, ISO9660SUSPSP_LEN, pUnion->Hdr.bVersion, ISO9660SUSPSP_VER,
+ pUnion->SP.bCheck1, ISO9660SUSPSP_CHECK1, pUnion->SP.bCheck2, ISO9660SUSPSP_CHECK2,
+ pUnion->SP.cbSkip, UINT8_MAX - RT_UOFFSETOF(ISO9660DIRREC, achFileId[1]) ));
+ else if (!fIsFirstDirRec)
+ LogRel(("rtFsIsoImport/Rock: Ignorining 'SP' entry in non-root directory record\n"));
+ else if (pThis->fSuspSeenSP)
+ LogRel(("rtFsIsoImport/Rock: Ignorining additional 'SP' entry\n"));
+ else
+ {
+ pThis->offSuspSkip = pUnion->SP.cbSkip;
+ if (pUnion->SP.cbSkip != 0)
+ LogRel(("rtFsIsoImport/Rock: SP: cbSkip=%#x\n", pUnion->SP.cbSkip));
+ }
+ break;
+
+ case MAKE_SIG(ISO9660SUSPER_SIG1, ISO9660SUSPER_SIG2): /* ER */
+ if ( pUnion->Hdr.cbEntry > RT_UOFFSETOF(ISO9660SUSPER, achPayload) + (uint32_t)pUnion->ER.cchIdentifier
+ + (uint32_t)pUnion->ER.cchDescription + (uint32_t)pUnion->ER.cchSource
+ || pUnion->Hdr.bVersion != ISO9660SUSPER_VER)
+ LogRel(("rtFsIsoImport/Rock: Malformed 'ER' entry: cbEntry=%#x bVersion=%#x (vs %#x) cchIdentifier=%#x cchDescription=%#x cchSource=%#x\n",
+ pUnion->Hdr.cbEntry, pUnion->Hdr.bVersion, ISO9660SUSPER_VER, pUnion->ER.cchIdentifier,
+ pUnion->ER.cchDescription, pUnion->ER.cchSource));
+ else if (!fIsFirstDirRec)
+ LogRel(("rtFsIsoImport/Rock: Ignorining 'ER' entry in non-root directory record\n"));
+ else if ( pUnion->ER.bVersion == 1 /* RRIP detection */
+ && ( (pUnion->ER.cchIdentifier >= 4 && strncmp(pUnion->ER.achPayload, ISO9660_RRIP_ID, 4 /*RRIP*/) == 0)
+ || (pUnion->ER.cchIdentifier >= 10 && strncmp(pUnion->ER.achPayload, RT_STR_TUPLE(ISO9660_RRIP_1_12_ID)) == 0) ))
+ {
+ LogRel(("rtFsIsoImport/Rock: Rock Ridge 'ER' entry: v%u id='%.*s' desc='%.*s' source='%.*s'\n",
+ pUnion->ER.bVersion, pUnion->ER.cchIdentifier, pUnion->ER.achPayload,
+ pUnion->ER.cchDescription, &pUnion->ER.achPayload[pUnion->ER.cchIdentifier],
+ pUnion->ER.cchSource, &pUnion->ER.achPayload[pUnion->ER.cchIdentifier + pUnion->ER.cchDescription]));
+ if (!fUnicode)
+ {
+ int rc = RTFsIsoMakerSetRockRidgeLevel(pThis->hIsoMaker, 2);
+ if (RT_FAILURE(rc))
+ LogRel(("rtFsIsoImport/Rock: RTFsIsoMakerSetRockRidgeLevel(,2) failed: %Rrc\n", rc));
+ }
+ else
+ {
+ int rc = RTFsIsoMakerSetJolietRockRidgeLevel(pThis->hIsoMaker, 2);
+ if (RT_FAILURE(rc))
+ LogRel(("rtFsIsoImport/Rock: RTFsIsoMakerSetJolietRockRidgeLevel(,2) failed: %Rrc\n", rc));
+ }
+ }
+ else
+ LogRel(("rtFsIsoImport/Rock: Unknown extension in 'ER' entry: v%u id='%.*s' desc='%.*s' source='%.*s'\n",
+ pUnion->ER.bVersion, pUnion->ER.cchIdentifier, pUnion->ER.achPayload,
+ pUnion->ER.cchDescription, &pUnion->ER.achPayload[pUnion->ER.cchIdentifier],
+ pUnion->ER.cchSource, &pUnion->ER.achPayload[pUnion->ER.cchIdentifier + pUnion->ER.cchDescription]));
+ break;
+
+ case MAKE_SIG(ISO9660SUSPPD_SIG1, ISO9660SUSPPD_SIG2): /* PD - ignored */
+ case MAKE_SIG(ISO9660SUSPST_SIG1, ISO9660SUSPST_SIG2): /* ST - ignore for now */
+ case MAKE_SIG(ISO9660SUSPES_SIG1, ISO9660SUSPES_SIG2): /* ES - ignore for now */
+ break;
+
+ /*
+ * Rock ridge interchange protocol entries.
+ */
+ case MAKE_SIG(ISO9660RRIPRR_SIG1, ISO9660RRIPRR_SIG2): /* RR */
+ if ( pUnion->RR.Hdr.cbEntry != ISO9660RRIPRR_LEN
+ || pUnion->RR.Hdr.bVersion != ISO9660RRIPRR_VER)
+ LogRel(("rtFsIsoImport/Rock: Malformed 'RR' entry: cbEntry=%#x (vs %#x), bVersion=%#x (vs %#x) fFlags=%#x\n",
+ pUnion->RR.Hdr.cbEntry, ISO9660RRIPRR_LEN, pUnion->RR.Hdr.bVersion, ISO9660RRIPRR_VER, pUnion->RR.fFlags));
+ /* else: ignore it */
+ break;
+
+ case MAKE_SIG(ISO9660RRIPPX_SIG1, ISO9660RRIPPX_SIG2): /* PX */
+ if ( ( pUnion->PX.Hdr.cbEntry != ISO9660RRIPPX_LEN
+ && pUnion->PX.Hdr.cbEntry != ISO9660RRIPPX_LEN_NO_INODE)
+ || pUnion->PX.Hdr.bVersion != ISO9660RRIPPX_VER
+ || RT_BE2H_U32(pUnion->PX.fMode.be) != RT_LE2H_U32(pUnion->PX.fMode.le)
+ || RT_BE2H_U32(pUnion->PX.cHardlinks.be) != RT_LE2H_U32(pUnion->PX.cHardlinks.le)
+ || RT_BE2H_U32(pUnion->PX.uid.be) != RT_LE2H_U32(pUnion->PX.uid.le)
+ || RT_BE2H_U32(pUnion->PX.gid.be) != RT_LE2H_U32(pUnion->PX.gid.le)
+ || ( pUnion->PX.Hdr.cbEntry == ISO9660RRIPPX_LEN
+ && RT_BE2H_U32(pUnion->PX.INode.be) != RT_LE2H_U32(pUnion->PX.INode.le)) )
+ LogRel(("rtFsIsoImport/Rock: Malformed 'PX' entry: cbEntry=%#x (vs %#x or %#x), bVersion=%#x (vs %#x) fMode=%#x/%#x cHardlinks=%#x/%#x uid=%#x/%#x gid=%#x/%#x inode=%#x/%#x\n",
+ pUnion->PX.Hdr.cbEntry, ISO9660RRIPPX_LEN, ISO9660RRIPPX_LEN_NO_INODE,
+ pUnion->PX.Hdr.bVersion, ISO9660RRIPPX_VER,
+ RT_BE2H_U32(pUnion->PX.fMode.be), RT_LE2H_U32(pUnion->PX.fMode.le),
+ RT_BE2H_U32(pUnion->PX.cHardlinks.be), RT_LE2H_U32(pUnion->PX.cHardlinks.le),
+ RT_BE2H_U32(pUnion->PX.uid.be), RT_LE2H_U32(pUnion->PX.uid.le),
+ RT_BE2H_U32(pUnion->PX.gid.be), RT_LE2H_U32(pUnion->PX.gid.le),
+ pUnion->PX.Hdr.cbEntry == ISO9660RRIPPX_LEN ? RT_BE2H_U32(pUnion->PX.INode.be) : 0,
+ pUnion->PX.Hdr.cbEntry == ISO9660RRIPPX_LEN ? RT_LE2H_U32(pUnion->PX.INode.le) : 0 ));
+ else
+ {
+ if (RTFS_IS_DIRECTORY(ISO9660_GET_ENDIAN(&pUnion->PX.fMode)) == RTFS_IS_DIRECTORY(pObjInfo->Attr.fMode))
+ pObjInfo->Attr.fMode = ISO9660_GET_ENDIAN(&pUnion->PX.fMode);
+ else
+ LogRel(("rtFsIsoImport/Rock: 'PX' entry changes directory-ness: fMode=%#x, existing %#x; ignored\n",
+ ISO9660_GET_ENDIAN(&pUnion->PX.fMode), pObjInfo->Attr.fMode));
+ pObjInfo->Attr.u.Unix.cHardlinks = ISO9660_GET_ENDIAN(&pUnion->PX.cHardlinks);
+ pObjInfo->Attr.u.Unix.uid = ISO9660_GET_ENDIAN(&pUnion->PX.uid);
+ pObjInfo->Attr.u.Unix.gid = ISO9660_GET_ENDIAN(&pUnion->PX.gid);
+ /* ignore inode */
+ }
+ break;
+
+ case MAKE_SIG(ISO9660RRIPPN_SIG1, ISO9660RRIPPN_SIG2): /* PN */
+ if ( pUnion->PN.Hdr.cbEntry != ISO9660RRIPPN_LEN
+ || pUnion->PN.Hdr.bVersion != ISO9660RRIPPN_VER
+ || RT_BE2H_U32(pUnion->PN.Major.be) != RT_LE2H_U32(pUnion->PN.Major.le)
+ || RT_BE2H_U32(pUnion->PN.Minor.be) != RT_LE2H_U32(pUnion->PN.Minor.le))
+ LogRel(("rtFsIsoImport/Rock: Malformed 'PN' entry: cbEntry=%#x (vs %#x), bVersion=%#x (vs %#x) Major=%#x/%#x Minor=%#x/%#x\n",
+ pUnion->PN.Hdr.cbEntry, ISO9660RRIPPN_LEN, pUnion->PN.Hdr.bVersion, ISO9660RRIPPN_VER,
+ RT_BE2H_U32(pUnion->PN.Major.be), RT_LE2H_U32(pUnion->PN.Major.le),
+ RT_BE2H_U32(pUnion->PN.Minor.be), RT_LE2H_U32(pUnion->PN.Minor.le) ));
+ else if (RTFS_IS_DIRECTORY(pObjInfo->Attr.fMode))
+ LogRel(("rtFsIsoImport/Rock: Ignoring 'PN' entry for directory (%#x/%#x)\n",
+ ISO9660_GET_ENDIAN(&pUnion->PN.Major), ISO9660_GET_ENDIAN(&pUnion->PN.Minor) ));
+ else
+ pObjInfo->Attr.u.Unix.Device = RTDEV_MAKE(ISO9660_GET_ENDIAN(&pUnion->PN.Major),
+ ISO9660_GET_ENDIAN(&pUnion->PN.Minor));
+ break;
+
+ case MAKE_SIG(ISO9660RRIPTF_SIG1, ISO9660RRIPTF_SIG2): /* TF */
+ if ( pUnion->TF.Hdr.bVersion != ISO9660RRIPTF_VER
+ || pUnion->TF.Hdr.cbEntry < Iso9660RripTfCalcLength(pUnion->TF.fFlags))
+ LogRel(("rtFsIsoImport/Rock: Malformed 'TF' entry: cbEntry=%#x (vs %#x), bVersion=%#x (vs %#x) fFlags=%#x\n",
+ pUnion->TF.Hdr.cbEntry, Iso9660RripTfCalcLength(pUnion->TF.fFlags),
+ pUnion->TF.Hdr.bVersion, ISO9660RRIPTF_VER, RT_BE2H_U32(pUnion->TF.fFlags) ));
+ else if (!(pUnion->TF.fFlags & ISO9660RRIPTF_F_LONG_FORM))
+ {
+ PCISO9660RECTIMESTAMP pTimestamp = (PCISO9660RECTIMESTAMP)&pUnion->TF.abPayload[0];
+ if (pUnion->TF.fFlags & ISO9660RRIPTF_F_BIRTH)
+ {
+ rtFsIsoImpIso9660RecDateTime2TimeSpec(&pObjInfo->BirthTime, pTimestamp);
+ pTimestamp++;
+ }
+ if (pUnion->TF.fFlags & ISO9660RRIPTF_F_MODIFY)
+ {
+ rtFsIsoImpIso9660RecDateTime2TimeSpec(&pObjInfo->ModificationTime, pTimestamp);
+ pTimestamp++;
+ }
+ if (pUnion->TF.fFlags & ISO9660RRIPTF_F_ACCESS)
+ {
+ rtFsIsoImpIso9660RecDateTime2TimeSpec(&pObjInfo->AccessTime, pTimestamp);
+ pTimestamp++;
+ }
+ if (pUnion->TF.fFlags & ISO9660RRIPTF_F_CHANGE)
+ {
+ rtFsIsoImpIso9660RecDateTime2TimeSpec(&pObjInfo->ChangeTime, pTimestamp);
+ pTimestamp++;
+ }
+ }
+ else
+ {
+ PCISO9660TIMESTAMP pTimestamp = (PCISO9660TIMESTAMP)&pUnion->TF.abPayload[0];
+ if (pUnion->TF.fFlags & ISO9660RRIPTF_F_BIRTH)
+ {
+ rtFsIsoImpIso9660DateTime2TimeSpecIfValid(&pObjInfo->BirthTime, pTimestamp);
+ pTimestamp++;
+ }
+ if (pUnion->TF.fFlags & ISO9660RRIPTF_F_MODIFY)
+ {
+ rtFsIsoImpIso9660DateTime2TimeSpecIfValid(&pObjInfo->ModificationTime, pTimestamp);
+ pTimestamp++;
+ }
+ if (pUnion->TF.fFlags & ISO9660RRIPTF_F_ACCESS)
+ {
+ rtFsIsoImpIso9660DateTime2TimeSpecIfValid(&pObjInfo->AccessTime, pTimestamp);
+ pTimestamp++;
+ }
+ if (pUnion->TF.fFlags & ISO9660RRIPTF_F_CHANGE)
+ {
+ rtFsIsoImpIso9660DateTime2TimeSpecIfValid(&pObjInfo->ChangeTime, pTimestamp);
+ pTimestamp++;
+ }
+ }
+ break;
+
+ case MAKE_SIG(ISO9660RRIPSF_SIG1, ISO9660RRIPSF_SIG2): /* SF */
+ LogRel(("rtFsIsoImport/Rock: Sparse file support not yet implemented!\n"));
+ break;
+
+ case MAKE_SIG(ISO9660RRIPSL_SIG1, ISO9660RRIPSL_SIG2): /* SL */
+ if ( pUnion->SL.Hdr.bVersion != ISO9660RRIPSL_VER
+ || pUnion->SL.Hdr.cbEntry < RT_UOFFSETOF(ISO9660RRIPSL, abComponents[2])
+ || (pUnion->SL.fFlags & ~ISO9660RRIP_SL_F_CONTINUE)
+ || (pUnion->SL.abComponents[0] & ISO9660RRIP_SL_C_RESERVED_MASK) )
+ LogRel(("rtFsIsoImport/Rock: Malformed 'SL' entry: cbEntry=%#x (vs %#x), bVersion=%#x (vs %#x) fFlags=%#x comp[0].fFlags=%#x\n",
+ pUnion->SL.Hdr.cbEntry, RT_UOFFSETOF(ISO9660RRIPSL, abComponents[2]),
+ pUnion->SL.Hdr.bVersion, ISO9660RRIPSL_VER, pUnion->SL.fFlags, pUnion->SL.abComponents[0]));
+ else if (pThis->fSeenLastSL)
+ LogRel(("rtFsIsoImport/Rock: Unexpected 'SL!' entry\n"));
+ else
+ {
+ pThis->fSeenLastSL = !(pUnion->SL.fFlags & ISO9660RRIP_SL_F_CONTINUE); /* used in loop */
+
+ size_t offDst = strlen(pThis->szRockSymlinkTargetBuf);
+ uint8_t const *pbSrc = &pUnion->SL.abComponents[0];
+ uint8_t cbSrcLeft = pUnion->SL.Hdr.cbEntry - RT_UOFFSETOF(ISO9660RRIPSL, abComponents);
+ while (cbSrcLeft >= 2)
+ {
+ uint8_t const fFlags = pbSrc[0];
+ uint8_t cchCopy = pbSrc[1];
+ uint8_t const cbSkip = cchCopy + 2;
+ if (cbSkip > cbSrcLeft)
+ {
+ LogRel(("rtFsIsoImport/Rock: Malformed 'SL' component: component flags=%#x, component length+2=%#x vs %#x left\n",
+ fFlags, cbSkip, cbSrcLeft));
+ break;
+ }
+
+ const char *pszCopy;
+ switch (fFlags & ~ISO9660RRIP_SL_C_CONTINUE)
+ {
+ case 0:
+ pszCopy = (const char *)&pbSrc[2];
+ break;
+
+ case ISO9660RRIP_SL_C_CURRENT:
+ if (cchCopy != 0)
+ LogRel(("rtFsIsoImport/Rock: Malformed 'SL' component: CURRENT + %u bytes, ignoring bytes\n", cchCopy));
+ pszCopy = ".";
+ cchCopy = 1;
+ break;
+
+ case ISO9660RRIP_SL_C_PARENT:
+ if (cchCopy != 0)
+ LogRel(("rtFsIsoImport/Rock: Malformed 'SL' component: PARENT + %u bytes, ignoring bytes\n", cchCopy));
+ pszCopy = "..";
+ cchCopy = 2;
+ break;
+
+ case ISO9660RRIP_SL_C_ROOT:
+ if (cchCopy != 0)
+ LogRel(("rtFsIsoImport/Rock: Malformed 'SL' component: ROOT + %u bytes, ignoring bytes\n", cchCopy));
+ pszCopy = "/";
+ cchCopy = 1;
+ break;
+
+ default:
+ LogRel(("rtFsIsoImport/Rock: Malformed 'SL' component: component flags=%#x (bad), component length=%#x vs %#x left\n",
+ fFlags, cchCopy, cbSrcLeft));
+ pszCopy = NULL;
+ cchCopy = 0;
+ break;
+ }
+
+ if (offDst + cchCopy < sizeof(pThis->szRockSymlinkTargetBuf))
+ {
+ memcpy(&pThis->szRockSymlinkTargetBuf[offDst], pszCopy, cchCopy);
+ offDst += cchCopy;
+ }
+ else
+ {
+ LogRel(("rtFsIsoImport/Rock: 'SL' constructs a too long target! '%.*s%.*s'\n",
+ offDst, pThis->szRockSymlinkTargetBuf, cchCopy, pszCopy));
+ memcpy(&pThis->szRockSymlinkTargetBuf[offDst], pszCopy,
+ sizeof(pThis->szRockSymlinkTargetBuf) - offDst - 1);
+ offDst = sizeof(pThis->szRockSymlinkTargetBuf) - 1;
+ break;
+ }
+
+ /* Advance */
+ pbSrc += cbSkip;
+ cbSrcLeft -= cbSkip;
+
+ /* Append slash if appropriate. */
+ if ( !(fFlags & ISO9660RRIP_SL_C_CONTINUE)
+ && (cbSrcLeft >= 2 || !pThis->fSeenLastSL) )
+ {
+ if (offDst + 1 < sizeof(pThis->szRockSymlinkTargetBuf))
+ pThis->szRockSymlinkTargetBuf[offDst++] = '/';
+ else
+ {
+ LogRel(("rtFsIsoImport/Rock: 'SL' constructs a too long target! '%.*s/'\n",
+ offDst, pThis->szRockSymlinkTargetBuf));
+ break;
+ }
+ }
+ }
+ pThis->szRockSymlinkTargetBuf[offDst] = '\0';
+
+ /* Purge the encoding as we don't want invalid UTF-8 floating around. */
+ /** @todo do this afterwards as needed. */
+ RTStrPurgeEncoding(pThis->szRockSymlinkTargetBuf);
+ }
+ break;
+
+ case MAKE_SIG(ISO9660RRIPNM_SIG1, ISO9660RRIPNM_SIG2): /* NM */
+ if ( pUnion->NM.Hdr.bVersion != ISO9660RRIPNM_VER
+ || pUnion->NM.Hdr.cbEntry < RT_UOFFSETOF(ISO9660RRIPNM, achName)
+ || (pUnion->NM.fFlags & ISO9660RRIP_NM_F_RESERVED_MASK) )
+ LogRel(("rtFsIsoImport/Rock: Malformed 'NM' entry: cbEntry=%#x (vs %#x), bVersion=%#x (vs %#x) fFlags=%#x %.*Rhxs\n",
+ pUnion->NM.Hdr.cbEntry, RT_UOFFSETOF(ISO9660RRIPNM, achName),
+ pUnion->NM.Hdr.bVersion, ISO9660RRIPNM_VER, pUnion->NM.fFlags,
+ pUnion->NM.Hdr.cbEntry - RT_MIN(pUnion->NM.Hdr.cbEntry, RT_UOFFSETOF(ISO9660RRIPNM, achName)),
+ &pUnion->NM.achName[0] ));
+ else if (pThis->fSeenLastNM)
+ LogRel(("rtFsIsoImport/Rock: Unexpected 'NM' entry!\n"));
+ else
+ {
+ pThis->fSeenLastNM = !(pUnion->NM.fFlags & ISO9660RRIP_NM_F_CONTINUE);
+
+ uint8_t const cchName = pUnion->NM.Hdr.cbEntry - (uint8_t)RT_UOFFSETOF(ISO9660RRIPNM, achName);
+ if (pUnion->NM.fFlags & (ISO9660RRIP_NM_F_CURRENT | ISO9660RRIP_NM_F_PARENT))
+ {
+ if (cchName == 0)
+ Log(("rtFsIsoImport/Rock: Ignoring 'NM' entry for '.' and '..'\n"));
+ else
+ LogRel(("rtFsIsoImport/Rock: Ignoring malformed 'NM' using '.' or '..': fFlags=%#x cchName=%#x %.*Rhxs; szRockNameBuf='%s'\n",
+ pUnion->NM.fFlags, cchName, cchName, pUnion->NM.achName, pThis->szRockNameBuf));
+ pThis->szRockNameBuf[0] = '\0';
+ pThis->fSeenLastNM = true;
+ }
+ else
+ {
+ size_t offDst = strlen(pThis->szRockNameBuf);
+ if (offDst + cchName < sizeof(pThis->szRockNameBuf))
+ {
+ memcpy(&pThis->szRockNameBuf[offDst], pUnion->NM.achName, cchName);
+ pThis->szRockNameBuf[offDst + cchName] = '\0';
+
+ /* Purge the encoding as we don't want invalid UTF-8 floating around. */
+ /** @todo do this afterwards as needed. */
+ RTStrPurgeEncoding(pThis->szRockNameBuf);
+ }
+ else
+ {
+ LogRel(("rtFsIsoImport/Rock: 'NM' constructs a too long name, ignoring it all: '%s%.*s'\n",
+ pThis->szRockNameBuf, cchName, pUnion->NM.achName));
+ pThis->szRockNameBuf[0] = '\0';
+ pThis->fSeenLastNM = true;
+ }
+ }
+ }
+ break;
+
+ case MAKE_SIG(ISO9660RRIPCL_SIG1, ISO9660RRIPCL_SIG2): /* CL - just warn for now. */
+ case MAKE_SIG(ISO9660RRIPPL_SIG1, ISO9660RRIPPL_SIG2): /* PL - just warn for now. */
+ case MAKE_SIG(ISO9660RRIPRE_SIG1, ISO9660RRIPRE_SIG2): /* RE - just warn for now. */
+ LogRel(("rtFsIsoImport/Rock: Ignoring directory relocation entry '%c%c'!\n", pUnion->Hdr.bSig1, pUnion->Hdr.bSig2));
+ break;
+
+ default:
+ LogRel(("rtFsIsoImport/Rock: Unknown SUSP entry: %#x %#x, %#x bytes, v%u\n",
+ pUnion->Hdr.bSig1, pUnion->Hdr.bSig2, pUnion->Hdr.cbEntry, pUnion->Hdr.bVersion));
+ break;
+#undef MAKE_SIG
+ }
+ }
+}
+
+
+/**
+ * Deals with the special '.' entry in the root directory.
+ *
+ * @returns IPRT status code.
+ * @param pThis The import instance.
+ * @param pDirRec The root directory record.
+ * @param fUnicode Indicates which namespace we're working on.
+ */
+static int rtFsIsoImportProcessIso9660TreeWorkerDoRockForRoot(PRTFSISOMKIMPORTER pThis, PCISO9660DIRREC pDirRec, bool fUnicode)
+{
+ uint8_t const cbSys = pDirRec->cbDirRec - RT_UOFFSETOF(ISO9660DIRREC, achFileId)
+ - pDirRec->bFileIdLength - !(pDirRec->bFileIdLength & 1);
+ uint8_t const * const pbSys = (uint8_t const *)&pDirRec->achFileId[pDirRec->bFileIdLength + !(pDirRec->bFileIdLength & 1)];
+ if (cbSys > 4)
+ {
+ RTFSOBJINFO ObjInfo;
+ ObjInfo.cbObject = 0;
+ ObjInfo.cbAllocated = 0;
+ rtFsIsoImpIso9660RecDateTime2TimeSpec(&ObjInfo.AccessTime, &pDirRec->RecTime);
+ ObjInfo.ModificationTime = ObjInfo.AccessTime;
+ ObjInfo.ChangeTime = ObjInfo.AccessTime;
+ ObjInfo.BirthTime = ObjInfo.AccessTime;
+ ObjInfo.Attr.fMode = RTFS_TYPE_DIRECTORY | RTFS_DOS_DIRECTORY | 0555;
+ ObjInfo.Attr.enmAdditional = RTFSOBJATTRADD_UNIX;
+ ObjInfo.Attr.u.Unix.uid = NIL_RTUID;
+ ObjInfo.Attr.u.Unix.gid = NIL_RTGID;
+ ObjInfo.Attr.u.Unix.cHardlinks = 2;
+ ObjInfo.Attr.u.Unix.INodeIdDevice = 0;
+ ObjInfo.Attr.u.Unix.INodeId = 0;
+ ObjInfo.Attr.u.Unix.fFlags = 0;
+ ObjInfo.Attr.u.Unix.GenerationId = 0;
+ ObjInfo.Attr.u.Unix.Device = 0;
+
+ rtFsIsoImportProcessIso9660TreeWorkerParseRockRidge(pThis, &ObjInfo, pbSys, cbSys, fUnicode, true /*fIsFirstDirRec*/,
+ false /*fContinuationRecord*/);
+ /** @todo Update root dir attribs. Need API. */
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Validates a directory record.
+ *
+ * @returns IPRT status code (safe to ignore, see pThis->rc).
+ * @param pThis The importer instance.
+ * @param pDirRec The directory record to validate.
+ * @param cbMax The maximum size.
+ */
+static int rtFsIsoImportValidateDirRec(PRTFSISOMKIMPORTER pThis, PCISO9660DIRREC pDirRec, uint32_t cbMax)
+{
+ /*
+ * Validate dual fields.
+ */
+ if (RT_LE2H_U32(pDirRec->cbData.le) != RT_BE2H_U32(pDirRec->cbData.be))
+ return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_DIR_REC,
+ "Invalid dir rec size field: {%#RX32,%#RX32}",
+ RT_BE2H_U32(pDirRec->cbData.be), RT_LE2H_U32(pDirRec->cbData.le));
+
+ if (RT_LE2H_U32(pDirRec->offExtent.le) != RT_BE2H_U32(pDirRec->offExtent.be))
+ return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_DIR_REC,
+ "Invalid dir rec extent field: {%#RX32,%#RX32}",
+ RT_BE2H_U32(pDirRec->offExtent.be), RT_LE2H_U32(pDirRec->offExtent.le));
+
+ if (RT_LE2H_U16(pDirRec->VolumeSeqNo.le) != RT_BE2H_U16(pDirRec->VolumeSeqNo.be))
+ return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_DIR_REC,
+ "Invalid dir rec volume sequence ID field: {%#RX16,%#RX16}",
+ RT_BE2H_U16(pDirRec->VolumeSeqNo.be), RT_LE2H_U16(pDirRec->VolumeSeqNo.le));
+
+ /*
+ * Check values.
+ */
+ if (ISO9660_GET_ENDIAN(&pDirRec->VolumeSeqNo) != pThis->idPrimaryVol)
+ return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_DIR_REC_VOLUME_SEQ_NO,
+ "Expected dir rec to have same volume sequence number as primary volume: %#x, expected %#x",
+ ISO9660_GET_ENDIAN(&pDirRec->VolumeSeqNo), pThis->idPrimaryVol);
+
+ if (ISO9660_GET_ENDIAN(&pDirRec->offExtent) >= pThis->cBlocksInPrimaryVolumeSpace)
+ return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_DIR_REC_EXTENT_OUT_OF_BOUNDS,
+ "Invalid dir rec extent: %#RX32, max %#RX32",
+ ISO9660_GET_ENDIAN(&pDirRec->offExtent), pThis->cBlocksInPrimaryVolumeSpace);
+
+ if (pDirRec->cbDirRec < RT_UOFFSETOF(ISO9660DIRREC, achFileId) + pDirRec->bFileIdLength)
+ return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_DIR_REC_LENGTH,
+ "Dir record size is too small: %#x (min %#x)",
+ pDirRec->cbDirRec, RT_UOFFSETOF(ISO9660DIRREC, achFileId) + pDirRec->bFileIdLength);
+ if (pDirRec->cbDirRec > cbMax)
+ return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_DIR_REC_LENGTH,
+ "Dir record size is too big: %#x (max %#x)", pDirRec->cbDirRec, cbMax);
+
+ if ( (pDirRec->fFileFlags & (ISO9660_FILE_FLAGS_MULTI_EXTENT | ISO9660_FILE_FLAGS_DIRECTORY))
+ == (ISO9660_FILE_FLAGS_MULTI_EXTENT | ISO9660_FILE_FLAGS_DIRECTORY))
+ return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_DIR_WITH_MORE_EXTENTS,
+ "Multi-extent directories are not supported (cbData=%#RX32 offExtent=%#RX32)",
+ ISO9660_GET_ENDIAN(&pDirRec->cbData), ISO9660_GET_ENDIAN(&pDirRec->offExtent));
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Validates a dot or dot-dot directory record.
+ *
+ * @returns IPRT status code (safe to ignore, see pThis->rc).
+ * @param pThis The importer instance.
+ * @param pDirRec The dot directory record to validate.
+ * @param cbMax The maximum size.
+ * @param bName The name byte (0x00: '.', 0x01: '..').
+ */
+static int rtFsIsoImportValidateDotDirRec(PRTFSISOMKIMPORTER pThis, PCISO9660DIRREC pDirRec, uint32_t cbMax, uint8_t bName)
+{
+ int rc = rtFsIsoImportValidateDirRec(pThis, pDirRec, cbMax);
+ if (RT_SUCCESS(rc))
+ {
+ if (pDirRec->bFileIdLength != 1)
+ return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_DOT_DIR_REC_BAD_NAME_LENGTH,
+ "Invalid dot dir rec file id length: %u", pDirRec->bFileIdLength);
+ if ((uint8_t)pDirRec->achFileId[0] != bName)
+ return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_DOT_DIR_REC_BAD_NAME,
+ "Invalid dot dir rec file id: %#x, expected %#x", pDirRec->achFileId[0], bName);
+ }
+ return rc;
+}
+
+
+/**
+ * rtFsIsoImportProcessIso9660TreeWorker helper that reads more data.
+ *
+ * @returns IPRT status code.
+ * @param pThis The importer instance.
+ * @param ppDirRec Pointer to the directory record pointer (in/out).
+ * @param pcbChunk Pointer to the cbChunk variable (in/out).
+ * @param pcbDir Pointer to the cbDir variable (in/out). This indicates
+ * how much we've left to read from the directory.
+ * @param poffNext Pointer to the offNext variable (in/out). This
+ * indicates where the next chunk of directory data is in
+ * the input file.
+ */
+static int rtFsIsoImportProcessIso9660TreeWorkerReadMore(PRTFSISOMKIMPORTER pThis, PCISO9660DIRREC *ppDirRec,
+ uint32_t *pcbChunk, uint32_t *pcbDir, uint64_t *poffNext)
+{
+ uint32_t cbChunk = *pcbChunk;
+ *ppDirRec = (PCISO9660DIRREC)memmove(&pThis->abBuf[ISO9660_SECTOR_SIZE - cbChunk], *ppDirRec, cbChunk);
+
+ Assert(!(*poffNext & (ISO9660_SECTOR_SIZE - 1)));
+ uint32_t cbToRead = RT_MIN(*pcbDir, sizeof(pThis->abBuf) - ISO9660_SECTOR_SIZE);
+ int rc = RTVfsFileReadAt(pThis->hSrcFile, *poffNext, &pThis->abBuf[ISO9660_SECTOR_SIZE], cbToRead, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ Log3(("rtFsIsoImportProcessIso9660TreeWorker: Read %#zx more bytes @%#RX64, now got @%#RX64 LB %#RX32\n",
+ cbToRead, *poffNext, *poffNext - cbChunk, cbChunk + cbToRead));
+ *poffNext += cbToRead;
+ *pcbDir -= cbToRead;
+ *pcbChunk = cbChunk + cbToRead;
+ return VINF_SUCCESS;
+ }
+ return rtFsIsoImpError(pThis, rc, "Error reading %#RX32 bytes at %#RX64 (dir): %Rrc", *poffNext, cbToRead);
+}
+
+
+/**
+ * rtFsIsoImportProcessIso9660TreeWorker helper that deals with skipping to the
+ * next sector when cbDirRec is zero.
+ *
+ * @returns IPRT status code.
+ * @retval VERR_NO_MORE_FILES when we reaches the end of the directory.
+ * @param pThis The importer instance.
+ * @param ppDirRec Pointer to the directory record pointer (in/out).
+ * @param pcbChunk Pointer to the cbChunk variable (in/out). Indicates how
+ * much we've left to process starting and pDirRec.
+ * @param pcbDir Pointer to the cbDir variable (in/out). This indicates
+ * how much we've left to read from the directory.
+ * @param poffNext Pointer to the offNext variable (in/out). This
+ * indicates where the next chunk of directory data is in
+ * the input file.
+ */
+static int rtFsIsoImportProcessIso9660TreeWorkerHandleZeroSizedDirRec(PRTFSISOMKIMPORTER pThis, PCISO9660DIRREC *ppDirRec,
+ uint32_t *pcbChunk, uint32_t *pcbDir, uint64_t *poffNext)
+{
+ uint32_t cbChunk = *pcbChunk;
+ uint64_t offChunk = *poffNext - cbChunk;
+ uint32_t cbSkip = ISO9660_SECTOR_SIZE - ((uint32_t)offChunk & (ISO9660_SECTOR_SIZE - 1));
+ if (cbSkip < cbChunk)
+ {
+ *ppDirRec = (PCISO9660DIRREC)((uintptr_t)*ppDirRec + cbSkip);
+ *pcbChunk = cbChunk -= cbSkip;
+ if ( cbChunk > UINT8_MAX
+ || *pcbDir == 0)
+ {
+ Log3(("rtFsIsoImportProcessIso9660TreeWorker: cbDirRec=0 --> jumped %#RX32 to @%#RX64 LB %#RX32\n",
+ cbSkip, *poffNext - cbChunk, cbChunk));
+ return VINF_SUCCESS;
+ }
+ Log3(("rtFsIsoImportProcessIso9660TreeWorker: cbDirRec=0 --> jumped %#RX32 to @%#RX64 LB %#RX32, but needs to read more\n",
+ cbSkip, *poffNext - cbChunk, cbChunk));
+ return rtFsIsoImportProcessIso9660TreeWorkerReadMore(pThis, ppDirRec, pcbChunk, pcbDir, poffNext);
+ }
+
+ /* ASSUMES we're working in multiples of sectors! */
+ if (*pcbDir == 0)
+ {
+ *pcbChunk = 0;
+ return VERR_NO_MORE_FILES;
+ }
+
+ /* End of chunk, read the next sectors. */
+ Assert(!(*poffNext & (ISO9660_SECTOR_SIZE - 1)));
+ uint32_t cbToRead = RT_MIN(*pcbDir, sizeof(pThis->abBuf));
+ int rc = RTVfsFileReadAt(pThis->hSrcFile, *poffNext, pThis->abBuf, cbToRead, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ Log3(("rtFsIsoImportProcessIso9660TreeWorker: cbDirRec=0 --> Read %#zx more bytes @%#RX64, now got @%#RX64 LB %#RX32\n",
+ cbToRead, *poffNext, *poffNext - cbChunk, cbChunk + cbToRead));
+ *poffNext += cbToRead;
+ *pcbDir -= cbToRead;
+ *pcbChunk = cbChunk + cbToRead;
+ *ppDirRec = (PCISO9660DIRREC)&pThis->abBuf[0];
+ return VINF_SUCCESS;
+ }
+ return rtFsIsoImpError(pThis, rc, "Error reading %#RX32 bytes at %#RX64 (dir): %Rrc", *poffNext, cbToRead);
+}
+
+
+/**
+ * Deals with a single directory.
+ *
+ * @returns IPRT status code (safe to ignore, see pThis->rc).
+ * @param pThis The importer instance.
+ * @param idxDir The configuration index for the directory.
+ * @param offDirBlock The offset of the directory data.
+ * @param cbDir The size of the directory data.
+ * @param cDepth The depth of the directory.
+ * @param fUnicode Set if it's a unicode (UTF-16BE) encoded
+ * directory.
+ * @param pTodoList The todo-list to add sub-directories to.
+ */
+static int rtFsIsoImportProcessIso9660TreeWorker(PRTFSISOMKIMPORTER pThis, uint32_t idxDir,
+ uint32_t offDirBlock, uint32_t cbDir, uint8_t cDepth, bool fUnicode,
+ PRTLISTANCHOR pTodoList)
+{
+ /*
+ * Restrict the depth to try avoid loops.
+ */
+ if (cDepth > RTFSISOMK_IMPORT_MAX_DEPTH)
+ return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_TOO_DEEP_DIR_TREE, "Dir at %#x LB %#x is too deep", offDirBlock, cbDir);
+
+ /*
+ * Read the first chunk into the big buffer.
+ */
+ uint32_t cbChunk = RT_MIN(cbDir, sizeof(pThis->abBuf));
+ uint64_t offNext = (uint64_t)offDirBlock * ISO9660_SECTOR_SIZE;
+ int rc = RTVfsFileReadAt(pThis->hSrcFile, offNext, pThis->abBuf, cbChunk, NULL);
+ if (RT_FAILURE(rc))
+ return rtFsIsoImpError(pThis, rc, "Error reading directory at %#RX64 (%#RX32 / %#RX32): %Rrc", offNext, cbChunk, cbDir);
+
+ cbDir -= cbChunk;
+ offNext += cbChunk;
+
+ /*
+ * Skip the current and parent directory entries.
+ */
+ PCISO9660DIRREC pDirRec = (PCISO9660DIRREC)&pThis->abBuf[0];
+ rc = rtFsIsoImportValidateDotDirRec(pThis, pDirRec, cbChunk, 0x00);
+ if (RT_FAILURE(rc))
+ return rc;
+ if ( cDepth == 0
+ && !(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_ROCK_RIDGE)
+ && pDirRec->cbDirRec > RT_UOFFSETOF(ISO9660DIRREC, achFileId[1]))
+ {
+ rc = rtFsIsoImportProcessIso9660TreeWorkerDoRockForRoot(pThis, pDirRec, fUnicode);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ cbChunk -= pDirRec->cbDirRec;
+ pDirRec = (PCISO9660DIRREC)((uintptr_t)pDirRec + pDirRec->cbDirRec);
+ rc = rtFsIsoImportValidateDotDirRec(pThis, pDirRec, cbChunk, 0x01);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ cbChunk -= pDirRec->cbDirRec;
+ pDirRec = (PCISO9660DIRREC)((uintptr_t)pDirRec + pDirRec->cbDirRec);
+
+ /*
+ * Work our way thru all the directory records.
+ */
+ Log3(("rtFsIsoImportProcessIso9660TreeWorker: Starting at @%#RX64 LB %#RX32 (out of %#RX32) in %#x\n",
+ offNext - cbChunk, cbChunk, cbChunk + cbDir, idxDir));
+ const uint32_t fNamespace = fUnicode ? RTFSISOMAKER_NAMESPACE_JOLIET : RTFSISOMAKER_NAMESPACE_ISO_9660;
+ while ( cbChunk > 0
+ || cbDir > 0)
+ {
+ /*
+ * Do we need to read some more?
+ */
+ if ( cbChunk > UINT8_MAX
+ || cbDir == 0)
+ { /* No, we don't. */ }
+ else
+ {
+ rc = rtFsIsoImportProcessIso9660TreeWorkerReadMore(pThis, &pDirRec, &cbChunk, &cbDir, &offNext);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ /* If null length, skip to the next sector. May have to read some then. */
+ if (pDirRec->cbDirRec != 0)
+ { /* likely */ }
+ else
+ {
+ rc = rtFsIsoImportProcessIso9660TreeWorkerHandleZeroSizedDirRec(pThis, &pDirRec, &cbChunk, &cbDir, &offNext);
+ if (RT_FAILURE(rc))
+ {
+ if (rc == VERR_NO_MORE_FILES)
+ break;
+ return rc;
+ }
+ if (pDirRec->cbDirRec == 0)
+ continue;
+ }
+
+ /*
+ * Validate the directory record. Give up if not valid since we're
+ * likely to get error with subsequent record too.
+ */
+ uint8_t const cbSys = pDirRec->cbDirRec - RT_UOFFSETOF(ISO9660DIRREC, achFileId)
+ - pDirRec->bFileIdLength - !(pDirRec->bFileIdLength & 1);
+ uint8_t const * const pbSys = (uint8_t const *)&pDirRec->achFileId[pDirRec->bFileIdLength + !(pDirRec->bFileIdLength & 1)];
+ Log3(("pDirRec=&abBuf[%#07zx]: @%#010RX64 cb=%#04x ff=%#04x off=%#010RX32 cb=%#010RX32 cbSys=%#x id=%.*Rhxs\n",
+ (uintptr_t)pDirRec - (uintptr_t)&pThis->abBuf[0], offNext - cbChunk, pDirRec->cbDirRec, pDirRec->fFileFlags,
+ ISO9660_GET_ENDIAN(&pDirRec->offExtent), ISO9660_GET_ENDIAN(&pDirRec->cbData), cbSys,
+ pDirRec->bFileIdLength, pDirRec->achFileId));
+ rc = rtFsIsoImportValidateDirRec(pThis, pDirRec, cbChunk);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /* This early calculation of the next record is due to multi-extent
+ handling further down. */
+ uint32_t cbChunkNew = cbChunk - pDirRec->cbDirRec;
+ PCISO9660DIRREC pDirRecNext = (PCISO9660DIRREC)((uintptr_t)pDirRec + pDirRec->cbDirRec);
+
+ /* Start Collecting object info. */
+ RTFSOBJINFO ObjInfo;
+ ObjInfo.cbObject = ISO9660_GET_ENDIAN(&pDirRec->cbData);
+ ObjInfo.cbAllocated = ObjInfo.cbObject;
+ rtFsIsoImpIso9660RecDateTime2TimeSpec(&ObjInfo.AccessTime, &pDirRec->RecTime);
+ ObjInfo.ModificationTime = ObjInfo.AccessTime;
+ ObjInfo.ChangeTime = ObjInfo.AccessTime;
+ ObjInfo.BirthTime = ObjInfo.AccessTime;
+ ObjInfo.Attr.fMode = pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY
+ ? RTFS_TYPE_DIRECTORY | RTFS_DOS_DIRECTORY | 0555
+ : RTFS_TYPE_FILE | RTFS_DOS_ARCHIVED | 0444;
+ ObjInfo.Attr.enmAdditional = RTFSOBJATTRADD_UNIX;
+ ObjInfo.Attr.u.Unix.uid = NIL_RTUID;
+ ObjInfo.Attr.u.Unix.gid = NIL_RTGID;
+ ObjInfo.Attr.u.Unix.cHardlinks = 1;
+ ObjInfo.Attr.u.Unix.INodeIdDevice = 0;
+ ObjInfo.Attr.u.Unix.INodeId = 0;
+ ObjInfo.Attr.u.Unix.fFlags = 0;
+ ObjInfo.Attr.u.Unix.GenerationId = 0;
+ ObjInfo.Attr.u.Unix.Device = 0;
+
+ /*
+ * Convert the name into the name buffer (szNameBuf).
+ */
+ if (!fUnicode)
+ {
+ memcpy(pThis->szNameBuf, pDirRec->achFileId, pDirRec->bFileIdLength);
+ pThis->szNameBuf[pDirRec->bFileIdLength] = '\0';
+ rc = RTStrValidateEncoding(pThis->szNameBuf);
+ }
+ else
+ {
+ char *pszDst = pThis->szNameBuf;
+ rc = RTUtf16BigToUtf8Ex((PRTUTF16)pDirRec->achFileId, pDirRec->bFileIdLength / sizeof(RTUTF16),
+ &pszDst, sizeof(pThis->szNameBuf), NULL);
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /* Drop the version from the name. */
+ size_t cchName = strlen(pThis->szNameBuf);
+ if ( !(pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY)
+ && cchName > 2
+ && RT_C_IS_DIGIT(pThis->szNameBuf[cchName - 1]))
+ {
+ uint32_t offName = 2;
+ while ( offName <= 5
+ && offName + 1 < cchName
+ && RT_C_IS_DIGIT(pThis->szNameBuf[cchName - offName]))
+ offName++;
+ if ( offName + 1 < cchName
+ && pThis->szNameBuf[cchName - offName] == ';')
+ {
+ RTStrToUInt32Full(&pThis->szNameBuf[cchName - offName + 1], 10, &ObjInfo.Attr.u.Unix.GenerationId);
+ pThis->szNameBuf[cchName - offName] = '\0';
+ }
+ }
+ Log3((" --> name='%s'\n", pThis->szNameBuf));
+
+ pThis->szRockNameBuf[0] = '\0';
+ pThis->szRockSymlinkTargetBuf[0] = '\0';
+ if ( cbSys > pThis->offSuspSkip
+ && !(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_ROCK_RIDGE))
+ {
+ pThis->fSeenLastNM = false;
+ pThis->fSeenLastSL = false;
+ pThis->szRockNameBuf[0] = '\0';
+ pThis->szRockSymlinkTargetBuf[0] = '\0';
+ rtFsIsoImportProcessIso9660TreeWorkerParseRockRidge(pThis, &ObjInfo, &pbSys[pThis->offSuspSkip],
+ cbSys - pThis->offSuspSkip, fUnicode,
+ false /*fContinuationRecord*/, false /*fIsFirstDirRec*/);
+ }
+
+ /*
+ * Deal with multi-extent files (usually large ones). We currently only
+ * handle files where the data is in single continuous chunk and only split
+ * up into multiple directory records because of data type limitations.
+ */
+ uint8_t abDirRecCopy[256];
+ uint64_t cbData = ISO9660_GET_ENDIAN(&pDirRec->cbData);
+ if (!(pDirRec->fFileFlags & ISO9660_FILE_FLAGS_MULTI_EXTENT))
+ { /* likely */ }
+ else
+ {
+ if (cbData & (ISO9660_SECTOR_SIZE - 1))
+ return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_MISALIGNED_MULTI_EXTENT,
+ "The size of non-final multi-extent record #0x0 isn't block aligned: %#RX64", cbData);
+
+ /* Make a copy of the first directory record so we don't overwrite
+ it when reading in more records below. */
+ pDirRec = (PCISO9660DIRREC)memcpy(abDirRecCopy, pDirRec, pDirRec->cbDirRec);
+
+ /* Process extent records. */
+ uint32_t cDirRecs = 1;
+ uint32_t offNextBlock = ISO9660_GET_ENDIAN(&pDirRec->offExtent)
+ + ISO9660_GET_ENDIAN(&pDirRec->cbData) / ISO9660_SECTOR_SIZE;
+ while ( cbChunkNew > 0
+ || cbDir > 0)
+ {
+ /* Read more? Skip? */
+ if ( cbChunkNew <= UINT8_MAX
+ && cbDir != 0)
+ {
+ rc = rtFsIsoImportProcessIso9660TreeWorkerReadMore(pThis, &pDirRecNext, &cbChunkNew, &cbDir, &offNext);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+ if (pDirRecNext->cbDirRec == 0)
+ {
+ rc = rtFsIsoImportProcessIso9660TreeWorkerHandleZeroSizedDirRec(pThis, &pDirRecNext, &cbChunkNew,
+ &cbDir, &offNext);
+ if (RT_FAILURE(rc))
+ {
+ if (rc == VERR_NO_MORE_FILES)
+ break;
+ return rc;
+ }
+ if (pDirRecNext->cbDirRec == 0)
+ continue;
+ }
+
+ /* Check the next record. */
+ rc = rtFsIsoImportValidateDirRec(pThis, pDirRecNext, cbChunkNew);
+ if (RT_FAILURE(rc))
+ return rc;
+ if (pDirRecNext->bFileIdLength != pDirRec->bFileIdLength)
+ return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_MISMATCHING_MULTI_EXTENT_REC,
+ "Multi-extent record #%#x differs from the first: bFileIdLength is %#x, expected %#x",
+ cDirRecs, pDirRecNext->bFileIdLength, pDirRec->bFileIdLength);
+ if (memcmp(pDirRecNext->achFileId, pDirRec->achFileId, pDirRec->bFileIdLength) != 0)
+ return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_MISMATCHING_MULTI_EXTENT_REC,
+ "Multi-extent record #%#x differs from the first: achFileId is %.*Rhxs, expected %.*Rhxs",
+ cDirRecs, pDirRecNext->bFileIdLength, pDirRecNext->achFileId,
+ pDirRec->bFileIdLength, pDirRec->achFileId);
+ if (ISO9660_GET_ENDIAN(&pDirRecNext->VolumeSeqNo) != ISO9660_GET_ENDIAN(&pDirRec->VolumeSeqNo))
+ return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_MISMATCHING_MULTI_EXTENT_REC,
+ "Multi-extent record #%#x differs from the first: VolumeSeqNo is %#x, expected %#x",
+ cDirRecs, ISO9660_GET_ENDIAN(&pDirRecNext->VolumeSeqNo),
+ ISO9660_GET_ENDIAN(&pDirRec->VolumeSeqNo));
+ if ( (pDirRecNext->fFileFlags & ISO9660_FILE_FLAGS_MULTI_EXTENT)
+ && (ISO9660_GET_ENDIAN(&pDirRecNext->cbData) & (ISO9660_SECTOR_SIZE - 1)) )
+ return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_MISALIGNED_MULTI_EXTENT,
+ "The size of non-final multi-extent record #%#x isn't block aligned: %#RX32",
+ cDirRecs, ISO9660_GET_ENDIAN(&pDirRecNext->cbData));
+
+ /* Check that the data is contiguous, then add the data. */
+ if (ISO9660_GET_ENDIAN(&pDirRecNext->offExtent) == offNextBlock)
+ cbData += ISO9660_GET_ENDIAN(&pDirRecNext->cbData);
+ else
+ return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_NON_CONTIGUOUS_MULTI_EXTENT,
+ "Multi-extent record #%#x isn't contiguous: offExtent=%#RX32, expected %#RX32",
+ cDirRecs, ISO9660_GET_ENDIAN(&pDirRecNext->offExtent), offNextBlock);
+
+ /* Advance. */
+ cDirRecs++;
+ bool fDone = !(pDirRecNext->fFileFlags & ISO9660_FILE_FLAGS_MULTI_EXTENT);
+ offNext += ISO9660_GET_ENDIAN(&pDirRecNext->cbData) / ISO9660_SECTOR_SIZE;
+ cbChunkNew -= pDirRecNext->cbDirRec;
+ pDirRecNext = (PCISO9660DIRREC)((uintptr_t)pDirRecNext + pDirRecNext->cbDirRec);
+ if (fDone)
+ break;
+ }
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Add the object.
+ */
+ if (pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY)
+ rtFsIsoImportProcessIso9660AddAndNameDirectory(pThis, pDirRec, &ObjInfo, cbData, fNamespace, idxDir,
+ pThis->szNameBuf, pThis->szRockNameBuf, cDepth + 1, pTodoList);
+ else if (pThis->szRockSymlinkTargetBuf[0] == '\0')
+ {
+ if (strcmp(pThis->szNameBuf, pThis->pszTransTbl) != 0)
+ rtFsIsoImportProcessIso9660AddAndNameFile(pThis, pDirRec, &ObjInfo, cbData, fNamespace, idxDir,
+ pThis->szNameBuf, pThis->szRockNameBuf);
+ }
+ else
+ rtFsIsoImportProcessIso9660AddAndNameSymlink(pThis, pDirRec, &ObjInfo, fNamespace, idxDir, pThis->szNameBuf,
+ pThis->szRockNameBuf, pThis->szRockSymlinkTargetBuf);
+ }
+ }
+ else
+ rtFsIsoImpError(pThis, rc, "Invalid name at %#RX64: %.Rhxs",
+ offNext - cbChunk, pDirRec->bFileIdLength, pDirRec->achFileId);
+
+ /*
+ * Advance to the next directory record.
+ */
+ cbChunk = cbChunkNew;
+ pDirRec = pDirRecNext;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Deals with a directory tree.
+ *
+ * This is implemented by tracking directories that needs to be processed in a
+ * todo list, so no recursive calls, however it uses a bit of heap.
+ *
+ * @returns IPRT status code (safe to ignore, see pThis->rc).
+ * @param pThis The importer instance.
+ * @param offDirBlock The offset of the root directory data.
+ * @param cbDir The size of the root directory data.
+ * @param fUnicode Set if it's a unicode (UTF-16BE) encoded
+ * directory.
+ */
+static int rtFsIsoImportProcessIso9660Tree(PRTFSISOMKIMPORTER pThis, uint32_t offDirBlock, uint32_t cbDir, bool fUnicode)
+{
+ /*
+ * Reset some parsing state.
+ */
+ pThis->offSuspSkip = 0;
+ pThis->fSuspSeenSP = false;
+ pThis->pszTransTbl = "TRANS.TBL"; /** @todo query this from the iso maker! */
+
+ /*
+ * Make sure we've got a root in the namespace.
+ */
+ uint32_t idxDir = RTFsIsoMakerGetObjIdxForPath(pThis->hIsoMaker,
+ !fUnicode ? RTFSISOMAKER_NAMESPACE_ISO_9660 : RTFSISOMAKER_NAMESPACE_JOLIET,
+ "/");
+ if (idxDir == UINT32_MAX)
+ {
+ idxDir = RTFSISOMAKER_CFG_IDX_ROOT;
+ int rc = RTFsIsoMakerObjSetPath(pThis->hIsoMaker, RTFSISOMAKER_CFG_IDX_ROOT,
+ !fUnicode ? RTFSISOMAKER_NAMESPACE_ISO_9660 : RTFSISOMAKER_NAMESPACE_JOLIET, "/");
+ if (RT_FAILURE(rc))
+ return rtFsIsoImpError(pThis, rc, "RTFsIsoMakerObjSetPath failed on root dir: %Rrc", rc);
+ }
+ Assert(idxDir == RTFSISOMAKER_CFG_IDX_ROOT);
+
+ /*
+ * Directories.
+ */
+ int rc = VINF_SUCCESS;
+ uint8_t cDepth = 0;
+ RTLISTANCHOR TodoList;
+ RTListInit(&TodoList);
+ for (;;)
+ {
+ int rc2 = rtFsIsoImportProcessIso9660TreeWorker(pThis, idxDir, offDirBlock, cbDir, cDepth, fUnicode, &TodoList);
+ if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
+ rc = rc2;
+
+ /*
+ * Pop the next directory.
+ */
+ PRTFSISOMKIMPDIR pNext = RTListRemoveLast(&TodoList, RTFSISOMKIMPDIR, Entry);
+ if (!pNext)
+ break;
+ idxDir = pNext->idxObj;
+ offDirBlock = pNext->offDirBlock;
+ cbDir = pNext->cbDir;
+ cDepth = pNext->cDepth;
+ RTMemFree(pNext);
+ }
+
+ return rc;
+}
+
+
+/**
+ * Imports a UTF-16BE string property from the joliet volume descriptor.
+ *
+ * The fields are normally space filled and padded, but we also consider zero
+ * bytes are fillers. If the field only contains padding, the string property
+ * will remain unchanged.
+ *
+ * @returns IPRT status code (ignorable).
+ * @param pThis The importer instance.
+ * @param pachField Pointer to the field. The structure type
+ * is 'char' for hysterical raisins, while the
+ * real type is 'RTUTF16'.
+ * @param cchField The field length.
+ * @param enmStringProp The corresponding string property.
+ *
+ * @note Clobbers pThis->pbBuf!
+ */
+static int rtFsIsoImportUtf16BigStringField(PRTFSISOMKIMPORTER pThis, const char *pachField, size_t cchField,
+ RTFSISOMAKERSTRINGPROP enmStringProp)
+{
+ /*
+ * Scan the field from the end as this way we know the result length if we find anything.
+ */
+ PCRTUTF16 pwcField = (PCRTUTF16)pachField;
+ size_t cwcField = cchField / sizeof(RTUTF16); /* ignores any odd field byte */
+ size_t off = cwcField;
+ while (off-- > 0)
+ {
+ RTUTF16 wc = RT_BE2H_U16(pwcField[off]);
+ if (wc == ' ' || wc == '\0')
+ { /* likely */ }
+ else
+ {
+ /*
+ * Convert to UTF-16.
+ */
+ char *pszCopy = (char *)pThis->abBuf;
+ int rc = RTUtf16BigToUtf8Ex(pwcField, off + 1, &pszCopy, sizeof(pThis->abBuf), NULL);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTFsIsoMakerSetStringProp(pThis->hIsoMaker, enmStringProp, RTFSISOMAKER_NAMESPACE_JOLIET, pszCopy);
+ if (RT_SUCCESS(rc))
+ return VINF_SUCCESS;
+ return rtFsIsoImpError(pThis, rc, "RTFsIsoMakerSetStringProp failed setting field %d to '%s': %Rrc",
+ enmStringProp, pszCopy, rc);
+ }
+ return rtFsIsoImpError(pThis, rc, "RTUtf16BigToUtf8Ex failed converting field %d to UTF-8: %Rrc - %.*Rhxs",
+ enmStringProp, rc, off * sizeof(RTUTF16), pwcField);
+ }
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Imports a string property from the primary volume descriptor.
+ *
+ * The fields are normally space filled and padded, but we also consider zero
+ * bytes are fillers. If the field only contains padding, the string property
+ * will remain unchanged.
+ *
+ * @returns IPRT status code (ignorable).
+ * @param pThis The importer instance.
+ * @param pachField Pointer to the field.
+ * @param cchField The field length.
+ * @param enmStringProp The corresponding string property.
+ *
+ * @note Clobbers pThis->pbBuf!
+ */
+static int rtFsIsoImportAsciiStringField(PRTFSISOMKIMPORTER pThis, const char *pachField, size_t cchField,
+ RTFSISOMAKERSTRINGPROP enmStringProp)
+{
+ /*
+ * Scan the field from the end as this way we know the result length if we find anything.
+ */
+ size_t off = cchField;
+ while (off-- > 0)
+ {
+ char ch = pachField[off];
+ if (ch == ' ' || ch == '\0')
+ { /* likely */ }
+ else
+ {
+ /*
+ * Make a copy of the string in abBuf, purge the encoding.
+ */
+ off++;
+ char *pszCopy = (char *)pThis->abBuf;
+ memcpy(pszCopy, pachField, off);
+ pszCopy[off] = '\0';
+ RTStrPurgeEncoding(pszCopy);
+
+ int rc = RTFsIsoMakerSetStringProp(pThis->hIsoMaker, enmStringProp, RTFSISOMAKER_NAMESPACE_ISO_9660, pszCopy);
+ if (RT_SUCCESS(rc))
+ return VINF_SUCCESS;
+ return rtFsIsoImpError(pThis, rc, "RTFsIsoMakerSetStringProp failed setting field %d to '%s': %Rrc",
+ enmStringProp, pszCopy, rc);
+ }
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Validates a root directory record.
+ *
+ * @returns IPRT status code (safe to ignore, see pThis->rc).
+ * @param pThis The importer instance.
+ * @param pDirRec The root directory record to validate.
+ */
+static int rtFsIsoImportValidateRootDirRec(PRTFSISOMKIMPORTER pThis, PCISO9660DIRREC pDirRec)
+{
+ /*
+ * Validate dual fields.
+ */
+ if (RT_LE2H_U32(pDirRec->cbData.le) != RT_BE2H_U32(pDirRec->cbData.be))
+ return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_ROOT_DIR_REC,
+ "Invalid root dir size: {%#RX32,%#RX32}",
+ RT_BE2H_U32(pDirRec->cbData.be), RT_LE2H_U32(pDirRec->cbData.le));
+
+ if (RT_LE2H_U32(pDirRec->offExtent.le) != RT_BE2H_U32(pDirRec->offExtent.be))
+ return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_ROOT_DIR_REC,
+ "Invalid root dir extent: {%#RX32,%#RX32}",
+ RT_BE2H_U32(pDirRec->offExtent.be), RT_LE2H_U32(pDirRec->offExtent.le));
+
+ if (RT_LE2H_U16(pDirRec->VolumeSeqNo.le) != RT_BE2H_U16(pDirRec->VolumeSeqNo.be))
+ return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_ROOT_DIR_REC,
+ "Invalid root dir volume sequence ID: {%#RX16,%#RX16}",
+ RT_BE2H_U16(pDirRec->VolumeSeqNo.be), RT_LE2H_U16(pDirRec->VolumeSeqNo.le));
+
+ /*
+ * Check values.
+ */
+ if (ISO9660_GET_ENDIAN(&pDirRec->VolumeSeqNo) != pThis->idPrimaryVol)
+ return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_ROOT_VOLUME_SEQ_NO,
+ "Expected root dir to have same volume sequence number as primary volume: %#x, expected %#x",
+ ISO9660_GET_ENDIAN(&pDirRec->VolumeSeqNo), pThis->idPrimaryVol);
+
+ if (ISO9660_GET_ENDIAN(&pDirRec->cbData) == 0)
+ return RTErrInfoSet(pThis->pErrInfo, VERR_ISOMK_IMPORT_ZERO_SIZED_ROOT_DIR, "Zero sized root dir");
+
+ if (ISO9660_GET_ENDIAN(&pDirRec->offExtent) >= pThis->cBlocksInPrimaryVolumeSpace)
+ return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_ROOT_DIR_EXTENT_OUT_OF_BOUNDS,
+ "Invalid root dir extent: %#RX32, max %#RX32",
+ ISO9660_GET_ENDIAN(&pDirRec->offExtent), pThis->cBlocksInPrimaryVolumeSpace);
+
+ if (pDirRec->cbDirRec < RT_UOFFSETOF(ISO9660DIRREC, achFileId))
+ return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_ROOT_DIR_REC_LENGTH,
+ "Root dir record size is too small: %#x (min %#x)",
+ pDirRec->cbDirRec, RT_UOFFSETOF(ISO9660DIRREC, achFileId));
+
+ if (!(pDirRec->fFileFlags & ISO9660_FILE_FLAGS_DIRECTORY))
+ return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_ROOT_DIR_WITHOUT_DIR_FLAG,
+ "Root dir is not flagged as directory: %#x", pDirRec->fFileFlags);
+ if (pDirRec->fFileFlags & ISO9660_FILE_FLAGS_MULTI_EXTENT)
+ return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_ROOT_DIR_IS_MULTI_EXTENT,
+ "Root dir is cannot be multi-extent: %#x", pDirRec->fFileFlags);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Processes a primary volume descriptor, importing all files and stuff.
+ *
+ * @returns IPRT status code (safe to ignore, see pThis->rc).
+ * @param pThis The importer instance.
+ * @param pVolDesc The primary volume descriptor.
+ */
+static int rtFsIsoImportProcessPrimaryDesc(PRTFSISOMKIMPORTER pThis, PISO9660PRIMARYVOLDESC pVolDesc)
+{
+ /*
+ * Validate dual fields first.
+ */
+ if (pVolDesc->bFileStructureVersion != ISO9660_FILE_STRUCTURE_VERSION)
+ return rtFsIsoImpError(pThis, VERR_IOSMK_IMPORT_PRIMARY_VOL_DESC_VER,
+ "Unsupported file structure version: %#x", pVolDesc->bFileStructureVersion);
+
+ if (RT_LE2H_U16(pVolDesc->cbLogicalBlock.le) != RT_BE2H_U16(pVolDesc->cbLogicalBlock.be))
+ return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_PRIMARY_VOL_DESC,
+ "Mismatching logical block size: {%#RX16,%#RX16}",
+ RT_BE2H_U16(pVolDesc->cbLogicalBlock.be), RT_LE2H_U16(pVolDesc->cbLogicalBlock.le));
+ if (RT_LE2H_U32(pVolDesc->VolumeSpaceSize.le) != RT_BE2H_U32(pVolDesc->VolumeSpaceSize.be))
+ return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_PRIMARY_VOL_DESC,
+ "Mismatching volume space size: {%#RX32,%#RX32}",
+ RT_BE2H_U32(pVolDesc->VolumeSpaceSize.be), RT_LE2H_U32(pVolDesc->VolumeSpaceSize.le));
+ if (RT_LE2H_U16(pVolDesc->cVolumesInSet.le) != RT_BE2H_U16(pVolDesc->cVolumesInSet.be))
+ return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_PRIMARY_VOL_DESC,
+ "Mismatching volumes in set: {%#RX16,%#RX16}",
+ RT_BE2H_U16(pVolDesc->cVolumesInSet.be), RT_LE2H_U16(pVolDesc->cVolumesInSet.le));
+ if (RT_LE2H_U16(pVolDesc->VolumeSeqNo.le) != RT_BE2H_U16(pVolDesc->VolumeSeqNo.be))
+ {
+ /* Hack alert! An Windows NT 3.1 ISO was found to not have the big endian bit set here, so work around it. */
+ if ( pVolDesc->VolumeSeqNo.be == 0
+ && pVolDesc->VolumeSeqNo.le == RT_H2LE_U16_C(1))
+ pVolDesc->VolumeSeqNo.be = RT_H2BE_U16_C(1);
+ else
+ return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_PRIMARY_VOL_DESC,
+ "Mismatching volume sequence no.: {%#RX16,%#RX16}",
+ RT_BE2H_U16(pVolDesc->VolumeSeqNo.be), RT_LE2H_U16(pVolDesc->VolumeSeqNo.le));
+ }
+ if (RT_LE2H_U32(pVolDesc->cbPathTable.le) != RT_BE2H_U32(pVolDesc->cbPathTable.be))
+ return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_PRIMARY_VOL_DESC,
+ "Mismatching path table size: {%#RX32,%#RX32}",
+ RT_BE2H_U32(pVolDesc->cbPathTable.be), RT_LE2H_U32(pVolDesc->cbPathTable.le));
+
+ /*
+ * Validate field values against our expectations.
+ */
+ if (ISO9660_GET_ENDIAN(&pVolDesc->cbLogicalBlock) != ISO9660_SECTOR_SIZE)
+ return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_LOGICAL_BLOCK_SIZE_NOT_2KB,
+ "Unsupported block size: %#x", ISO9660_GET_ENDIAN(&pVolDesc->cbLogicalBlock));
+
+ if (ISO9660_GET_ENDIAN(&pVolDesc->cVolumesInSet) != 1)
+ return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_MORE_THAN_ONE_VOLUME_IN_SET,
+ "Volumes in set: %#x", ISO9660_GET_ENDIAN(&pVolDesc->cVolumesInSet));
+
+ if (ISO9660_GET_ENDIAN(&pVolDesc->VolumeSeqNo) != 1)
+ return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_INVALID_VOLUMNE_SEQ_NO,
+ "Unexpected volume sequence number: %#x", ISO9660_GET_ENDIAN(&pVolDesc->VolumeSeqNo));
+
+ /*
+ * Gather info we need.
+ */
+ pThis->cBlocksInPrimaryVolumeSpace = ISO9660_GET_ENDIAN(&pVolDesc->VolumeSpaceSize);
+ pThis->cbPrimaryVolumeSpace = pThis->cBlocksInPrimaryVolumeSpace * (uint64_t)ISO9660_SECTOR_SIZE;
+ pThis->cVolumesInSet = ISO9660_GET_ENDIAN(&pVolDesc->cVolumesInSet);
+ pThis->idPrimaryVol = ISO9660_GET_ENDIAN(&pVolDesc->VolumeSeqNo);
+
+ /*
+ * Validate the root directory record.
+ */
+ int rc = rtFsIsoImportValidateRootDirRec(pThis, &pVolDesc->RootDir.DirRec);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Import stuff if present and not opted out.
+ */
+ if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_SYSTEM_ID))
+ rtFsIsoImportAsciiStringField(pThis, pVolDesc->achSystemId, sizeof(pVolDesc->achSystemId),
+ RTFSISOMAKERSTRINGPROP_SYSTEM_ID);
+ if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_VOLUME_ID))
+ rtFsIsoImportAsciiStringField(pThis, pVolDesc->achVolumeId, sizeof(pVolDesc->achVolumeId),
+ RTFSISOMAKERSTRINGPROP_VOLUME_ID);
+ if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_VOLUME_SET_ID))
+ rtFsIsoImportAsciiStringField(pThis, pVolDesc->achVolumeSetId, sizeof(pVolDesc->achVolumeSetId),
+ RTFSISOMAKERSTRINGPROP_VOLUME_SET_ID);
+ if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_PUBLISHER_ID))
+ rtFsIsoImportAsciiStringField(pThis, pVolDesc->achPublisherId, sizeof(pVolDesc->achPublisherId),
+ RTFSISOMAKERSTRINGPROP_PUBLISHER_ID);
+ if (pThis->fFlags & RTFSISOMK_IMPORT_F_DATA_PREPARER_ID)
+ rtFsIsoImportAsciiStringField(pThis, pVolDesc->achDataPreparerId, sizeof(pVolDesc->achDataPreparerId),
+ RTFSISOMAKERSTRINGPROP_DATA_PREPARER_ID);
+ if (pThis->fFlags & RTFSISOMK_IMPORT_F_APPLICATION_ID)
+ rtFsIsoImportAsciiStringField(pThis, pVolDesc->achApplicationId, sizeof(pVolDesc->achApplicationId),
+ RTFSISOMAKERSTRINGPROP_APPLICATION_ID);
+ if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_COPYRIGHT_FID))
+ rtFsIsoImportAsciiStringField(pThis, pVolDesc->achCopyrightFileId, sizeof(pVolDesc->achCopyrightFileId),
+ RTFSISOMAKERSTRINGPROP_COPYRIGHT_FILE_ID);
+ if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_ABSTRACT_FID))
+ rtFsIsoImportAsciiStringField(pThis, pVolDesc->achAbstractFileId, sizeof(pVolDesc->achAbstractFileId),
+ RTFSISOMAKERSTRINGPROP_ABSTRACT_FILE_ID);
+ if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_BIBLIO_FID))
+ rtFsIsoImportAsciiStringField(pThis, pVolDesc->achBibliographicFileId, sizeof(pVolDesc->achBibliographicFileId),
+ RTFSISOMAKERSTRINGPROP_BIBLIOGRAPHIC_FILE_ID);
+
+ /*
+ * Process the directory tree.
+ */
+ if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_PRIMARY_ISO))
+ rc = rtFsIsoImportProcessIso9660Tree(pThis, ISO9660_GET_ENDIAN(&pVolDesc->RootDir.DirRec.offExtent),
+ ISO9660_GET_ENDIAN(&pVolDesc->RootDir.DirRec.cbData), false /*fUnicode*/);
+ }
+
+ return rc;
+}
+
+
+/**
+ * Processes a secondary volume descriptor, if it is joliet we'll importing all
+ * the files and stuff.
+ *
+ * @returns IPRT status code (safe to ignore, see pThis->rc).
+ * @param pThis The importer instance.
+ * @param pVolDesc The primary volume descriptor.
+ */
+static int rtFsIsoImportProcessSupplementaryDesc(PRTFSISOMKIMPORTER pThis, PISO9660SUPVOLDESC pVolDesc)
+{
+ /*
+ * Validate dual fields first.
+ */
+ if (pVolDesc->bFileStructureVersion != ISO9660_FILE_STRUCTURE_VERSION)
+ return rtFsIsoImpError(pThis, VERR_IOSMK_IMPORT_SUP_VOL_DESC_VER,
+ "Unsupported file structure version: %#x", pVolDesc->bFileStructureVersion);
+
+ if (RT_LE2H_U16(pVolDesc->cbLogicalBlock.le) != RT_BE2H_U16(pVolDesc->cbLogicalBlock.be))
+ return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_SUP_VOL_DESC,
+ "Mismatching logical block size: {%#RX16,%#RX16}",
+ RT_BE2H_U16(pVolDesc->cbLogicalBlock.be), RT_LE2H_U16(pVolDesc->cbLogicalBlock.le));
+ if (RT_LE2H_U32(pVolDesc->VolumeSpaceSize.le) != RT_BE2H_U32(pVolDesc->VolumeSpaceSize.be))
+ return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_SUP_VOL_DESC,
+ "Mismatching volume space size: {%#RX32,%#RX32}",
+ RT_BE2H_U32(pVolDesc->VolumeSpaceSize.be), RT_LE2H_U32(pVolDesc->VolumeSpaceSize.le));
+ if (RT_LE2H_U16(pVolDesc->cVolumesInSet.le) != RT_BE2H_U16(pVolDesc->cVolumesInSet.be))
+ return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_SUP_VOL_DESC,
+ "Mismatching volumes in set: {%#RX16,%#RX16}",
+ RT_BE2H_U16(pVolDesc->cVolumesInSet.be), RT_LE2H_U16(pVolDesc->cVolumesInSet.le));
+ if (RT_LE2H_U16(pVolDesc->VolumeSeqNo.le) != RT_BE2H_U16(pVolDesc->VolumeSeqNo.be))
+ return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_SUP_VOL_DESC,
+ "Mismatching volume sequence no.: {%#RX16,%#RX16}",
+ RT_BE2H_U16(pVolDesc->VolumeSeqNo.be), RT_LE2H_U16(pVolDesc->VolumeSeqNo.le));
+ if (RT_LE2H_U32(pVolDesc->cbPathTable.le) != RT_BE2H_U32(pVolDesc->cbPathTable.be))
+ return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BAD_SUP_VOL_DESC,
+ "Mismatching path table size: {%#RX32,%#RX32}",
+ RT_BE2H_U32(pVolDesc->cbPathTable.be), RT_LE2H_U32(pVolDesc->cbPathTable.le));
+
+ /*
+ * Validate field values against our expectations.
+ */
+ if (ISO9660_GET_ENDIAN(&pVolDesc->cbLogicalBlock) != ISO9660_SECTOR_SIZE)
+ return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_LOGICAL_BLOCK_SIZE_NOT_2KB,
+ "Unsupported block size: %#x", ISO9660_GET_ENDIAN(&pVolDesc->cbLogicalBlock));
+
+ if (ISO9660_GET_ENDIAN(&pVolDesc->cVolumesInSet) != pThis->cVolumesInSet)
+ return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_VOLUME_IN_SET_MISMATCH, "Volumes in set: %#x, expected %#x",
+ ISO9660_GET_ENDIAN(&pVolDesc->cVolumesInSet), pThis->cVolumesInSet);
+
+ if (ISO9660_GET_ENDIAN(&pVolDesc->VolumeSeqNo) != pThis->idPrimaryVol)
+ return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_INVALID_VOLUMNE_SEQ_NO,
+ "Unexpected volume sequence number: %#x (expected %#x)",
+ ISO9660_GET_ENDIAN(&pVolDesc->VolumeSeqNo), pThis->idPrimaryVol);
+
+ if (ISO9660_GET_ENDIAN(&pVolDesc->VolumeSpaceSize) != pThis->cBlocksInPrimaryVolumeSpace)
+ {
+ /* ubuntu-21.10-desktop-amd64.iso has 0x172f4e blocks (3 111 809 024 bytes) here
+ and 0x173838 blocks (3 116 482 560 bytes) in the primary, a difference of
+ -2282 blocks (-4 673 536 bytes). Guess something was omitted from the joliet
+ edition, not immediately obvious what though.
+
+ For now we'll just let it pass as long as the primary size is the larger.
+ (Not quite sure how the code will handle a supplementary volume spanning
+ more space, as I suspect it only uses the primary volume size for
+ validating block addresses and such.) */
+ LogRel(("rtFsIsoImportProcessSupplementaryDesc: Volume space size differs between primary and supplementary descriptors: %#x, primary %#x",
+ ISO9660_GET_ENDIAN(&pVolDesc->VolumeSpaceSize), pThis->cBlocksInPrimaryVolumeSpace));
+ if (ISO9660_GET_ENDIAN(&pVolDesc->VolumeSpaceSize) > pThis->cBlocksInPrimaryVolumeSpace)
+ return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_VOLUME_SPACE_SIZE_MISMATCH,
+ "Volume space given in the supplementary descriptor is larger than in the primary: %#x, primary %#x",
+ ISO9660_GET_ENDIAN(&pVolDesc->VolumeSpaceSize), pThis->cBlocksInPrimaryVolumeSpace);
+ }
+
+ /*
+ * Validate the root directory record.
+ */
+ int rc = rtFsIsoImportValidateRootDirRec(pThis, &pVolDesc->RootDir.DirRec);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Is this a joliet descriptor? Ignore if not.
+ */
+ uint8_t uJolietLevel = 0;
+ if ( pVolDesc->abEscapeSequences[0] == ISO9660_JOLIET_ESC_SEQ_0
+ && pVolDesc->abEscapeSequences[1] == ISO9660_JOLIET_ESC_SEQ_1)
+ switch (pVolDesc->abEscapeSequences[2])
+ {
+ case ISO9660_JOLIET_ESC_SEQ_2_LEVEL_1: uJolietLevel = 1; break;
+ case ISO9660_JOLIET_ESC_SEQ_2_LEVEL_2: uJolietLevel = 2; break;
+ case ISO9660_JOLIET_ESC_SEQ_2_LEVEL_3: uJolietLevel = 3; break;
+ default: Log(("rtFsIsoImportProcessSupplementaryDesc: last joliet escape sequence byte doesn't match: %#x\n",
+ pVolDesc->abEscapeSequences[2]));
+ }
+ if (uJolietLevel == 0)
+ return VINF_SUCCESS;
+
+ /*
+ * Only one joliet descriptor.
+ */
+ if (pThis->fSeenJoliet)
+ return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_MULTIPLE_JOLIET_VOL_DESCS,
+ "More than one Joliet volume descriptor is not supported");
+ pThis->fSeenJoliet = true;
+
+ /*
+ * Import stuff if present and not opted out.
+ */
+ if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_SYSTEM_ID))
+ rtFsIsoImportUtf16BigStringField(pThis, pVolDesc->achSystemId, sizeof(pVolDesc->achSystemId),
+ RTFSISOMAKERSTRINGPROP_SYSTEM_ID);
+ if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_J_VOLUME_ID))
+ rtFsIsoImportUtf16BigStringField(pThis, pVolDesc->achVolumeId, sizeof(pVolDesc->achVolumeId),
+ RTFSISOMAKERSTRINGPROP_VOLUME_ID);
+ if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_J_VOLUME_SET_ID))
+ rtFsIsoImportUtf16BigStringField(pThis, pVolDesc->achVolumeSetId, sizeof(pVolDesc->achVolumeSetId),
+ RTFSISOMAKERSTRINGPROP_VOLUME_SET_ID);
+ if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_J_PUBLISHER_ID))
+ rtFsIsoImportUtf16BigStringField(pThis, pVolDesc->achPublisherId, sizeof(pVolDesc->achPublisherId),
+ RTFSISOMAKERSTRINGPROP_PUBLISHER_ID);
+ if (pThis->fFlags & RTFSISOMK_IMPORT_F_J_DATA_PREPARER_ID)
+ rtFsIsoImportUtf16BigStringField(pThis, pVolDesc->achDataPreparerId, sizeof(pVolDesc->achDataPreparerId),
+ RTFSISOMAKERSTRINGPROP_DATA_PREPARER_ID);
+ if (pThis->fFlags & RTFSISOMK_IMPORT_F_J_APPLICATION_ID)
+ rtFsIsoImportUtf16BigStringField(pThis, pVolDesc->achApplicationId, sizeof(pVolDesc->achApplicationId),
+ RTFSISOMAKERSTRINGPROP_APPLICATION_ID);
+ if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_J_COPYRIGHT_FID))
+ rtFsIsoImportUtf16BigStringField(pThis, pVolDesc->achCopyrightFileId, sizeof(pVolDesc->achCopyrightFileId),
+ RTFSISOMAKERSTRINGPROP_COPYRIGHT_FILE_ID);
+ if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_J_ABSTRACT_FID))
+ rtFsIsoImportUtf16BigStringField(pThis, pVolDesc->achAbstractFileId, sizeof(pVolDesc->achAbstractFileId),
+ RTFSISOMAKERSTRINGPROP_ABSTRACT_FILE_ID);
+ if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_J_BIBLIO_FID))
+ rtFsIsoImportUtf16BigStringField(pThis, pVolDesc->achBibliographicFileId, sizeof(pVolDesc->achBibliographicFileId),
+ RTFSISOMAKERSTRINGPROP_BIBLIOGRAPHIC_FILE_ID);
+
+ /*
+ * Process the directory tree.
+ */
+ if (!(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_JOLIET))
+ return rtFsIsoImportProcessIso9660Tree(pThis, ISO9660_GET_ENDIAN(&pVolDesc->RootDir.DirRec.offExtent),
+ ISO9660_GET_ENDIAN(&pVolDesc->RootDir.DirRec.cbData), true /*fUnicode*/);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Checks out an El Torito boot image to see if it requires info table patching.
+ *
+ * @returns IPRT status code (ignored).
+ * @param pThis The ISO importer instance.
+ * @param idxImageObj The configuration index of the image.
+ * @param offBootImage The block offset of the image.
+ */
+static int rtFsIsoImportProcessElToritoImage(PRTFSISOMKIMPORTER pThis, uint32_t idxImageObj, uint32_t offBootImage)
+{
+ ISO9660SYSLINUXINFOTABLE InfoTable;
+ int rc = RTVfsFileReadAt(pThis->hSrcFile, offBootImage * (uint64_t)ISO9660_SECTOR_SIZE + ISO9660SYSLINUXINFOTABLE_OFFSET,
+ &InfoTable, sizeof(InfoTable), NULL);
+ if (RT_SUCCESS(rc))
+ {
+ if ( RT_LE2H_U32(InfoTable.offBootFile) == offBootImage
+ && RT_LE2H_U32(InfoTable.offPrimaryVolDesc) == pThis->offPrimaryVolDesc
+ && ASMMemIsAllU8(&InfoTable.auReserved[0], sizeof(InfoTable.auReserved), 0) )
+ {
+ rc = RTFsIsoMakerObjEnableBootInfoTablePatching(pThis->hIsoMaker, idxImageObj, true /*fEnable*/);
+ if (RT_FAILURE(rc))
+ return rtFsIsoImpError(pThis, rc, "RTFsIsoMakerObjEnableBootInfoTablePatching failed: %Rrc", rc);
+ }
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Processes a boot catalog default or section entry.
+ *
+ * @returns IPRT status code (ignored).
+ * @param pThis The ISO importer instance.
+ * @param iEntry The boot catalog entry number. This is 1 for
+ * the default entry, and 3+ for section entries.
+ * @param cMaxEntries Maximum number of entries.
+ * @param pEntry The entry to process.
+ * @param pcSkip Where to return the number of extension entries to skip.
+ */
+static int rtFsIsoImportProcessElToritoSectionEntry(PRTFSISOMKIMPORTER pThis, uint32_t iEntry, uint32_t cMaxEntries,
+ PCISO9660ELTORITOSECTIONENTRY pEntry, uint32_t *pcSkip)
+{
+ *pcSkip = 0;
+
+ /*
+ * Check the boot indicator type for entry 1.
+ */
+ if ( pEntry->bBootIndicator != ISO9660_ELTORITO_BOOT_INDICATOR_BOOTABLE
+ && pEntry->bBootIndicator != ISO9660_ELTORITO_BOOT_INDICATOR_NOT_BOOTABLE)
+ return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_DEF_ENTRY_INVALID_BOOT_IND,
+ "Default boot catalog entry has an invalid boot indicator: %#x", pEntry->bBootIndicator);
+
+ /*
+ * Check the media type and flags.
+ */
+ uint32_t cbDefaultSize;
+ uint8_t bMediaType = pEntry->bBootMediaType;
+ switch (bMediaType & ISO9660_ELTORITO_BOOT_MEDIA_TYPE_MASK)
+ {
+ case ISO9660_ELTORITO_BOOT_MEDIA_TYPE_FLOPPY_1_2_MB:
+ cbDefaultSize = 512 * 80 * 15 * 2;
+ break;
+
+ case ISO9660_ELTORITO_BOOT_MEDIA_TYPE_FLOPPY_1_44_MB:
+ cbDefaultSize = 512 * 80 * 18 * 2;
+ break;
+
+ case ISO9660_ELTORITO_BOOT_MEDIA_TYPE_FLOPPY_2_88_MB:
+ cbDefaultSize = 512 * 80 * 36 * 2;
+ break;
+
+ case ISO9660_ELTORITO_BOOT_MEDIA_TYPE_NO_EMULATION:
+ case ISO9660_ELTORITO_BOOT_MEDIA_TYPE_HARD_DISK:
+ cbDefaultSize = 0;
+ break;
+
+ default:
+ return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_INVALID_BOOT_MEDIA_TYPE,
+ "Boot catalog entry #%#x has an invalid boot media type: %#x", bMediaType);
+ }
+
+ if (iEntry == 1)
+ {
+ if (bMediaType & ISO9660_ELTORITO_BOOT_MEDIA_F_MASK)
+ {
+ rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_DEF_ENTRY_INVALID_FLAGS,
+ "Boot catalog entry #%#x has an invalid boot media type: %#x", bMediaType);
+ bMediaType &= ~ISO9660_ELTORITO_BOOT_MEDIA_F_MASK;
+ }
+ }
+ else
+ {
+ if (bMediaType & ISO9660_ELTORITO_BOOT_MEDIA_F_RESERVED)
+ {
+ rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_ENTRY_RESERVED_FLAG,
+ "Boot catalog entry #%#x has an invalid boot media type: %#x", bMediaType);
+ bMediaType &= ~ISO9660_ELTORITO_BOOT_MEDIA_F_RESERVED;
+ }
+ }
+
+ /*
+ * Complain if bUnused is used.
+ */
+ if (pEntry->bUnused != 0)
+ rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_ENTRY_USES_UNUSED_FIELD,
+ "Boot catalog entry #%#x has a non-zero unused field: %#x", pEntry->bUnused);
+
+ /*
+ * Check out the boot image offset and turn that into an index of a file
+ */
+ uint32_t offBootImage = RT_LE2H_U32(pEntry->offBootImage);
+ if (offBootImage >= pThis->cBlocksInSrcFile)
+ return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_ENTRY_IMAGE_OUT_OF_BOUNDS,
+ "Boot catalog entry #%#x has an out of bound boot image block number: %#RX32, max %#RX32",
+ offBootImage, pThis->cBlocksInPrimaryVolumeSpace);
+
+ int rc;
+ uint32_t idxImageObj;
+ PRTFSISOMKIMPBLOCK2FILE pBlock2File = (PRTFSISOMKIMPBLOCK2FILE)RTAvlU32Get(&pThis->Block2FileRoot, offBootImage);
+ if (pBlock2File)
+ idxImageObj = pBlock2File->idxObj;
+ else
+ {
+ if (cbDefaultSize == 0)
+ {
+ pBlock2File = (PRTFSISOMKIMPBLOCK2FILE)RTAvlU32GetBestFit(&pThis->Block2FileRoot, offBootImage, true /*fAbove*/);
+ if (pBlock2File)
+ cbDefaultSize = RT_MIN(pBlock2File->Core.Key - offBootImage, UINT32_MAX / ISO9660_SECTOR_SIZE + 1)
+ * ISO9660_SECTOR_SIZE;
+ else if (offBootImage < pThis->cBlocksInSrcFile)
+ cbDefaultSize = RT_MIN(pThis->cBlocksInSrcFile - offBootImage, UINT32_MAX / ISO9660_SECTOR_SIZE + 1)
+ * ISO9660_SECTOR_SIZE;
+ else
+ return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_ENTRY_UNKNOWN_IMAGE_SIZE,
+ "Boot catalog entry #%#x has an invalid boot media type: %#x", bMediaType);
+ }
+
+ if (pThis->idxSrcFile != UINT32_MAX)
+ {
+ rc = RTFsIsoMakerAddCommonSourceFile(pThis->hIsoMaker, pThis->hSrcFile, &pThis->idxSrcFile);
+ if (RT_FAILURE(rc))
+ return rtFsIsoImpError(pThis, rc, "RTFsIsoMakerAddCommonSourceFile failed: %Rrc", rc);
+ Assert(pThis->idxSrcFile != UINT32_MAX);
+ }
+
+ rc = RTFsIsoMakerAddUnnamedFileWithCommonSrc(pThis->hIsoMaker, pThis->idxSrcFile,
+ offBootImage * (uint64_t)ISO9660_SECTOR_SIZE,
+ cbDefaultSize, NULL, &idxImageObj);
+ if (RT_FAILURE(rc))
+ return rtFsIsoImpError(pThis, rc, "RTFsIsoMakerAddUnnamedFileWithCommonSrc failed on boot entry #%#x: %Rrc",
+ iEntry, rc);
+ }
+
+ /*
+ * Deal with selection criteria. Use the last sector of abBuf to gather it
+ * into a single data chunk.
+ */
+ size_t cbSelCrit = 0;
+ uint8_t *pbSelCrit = &pThis->abBuf[sizeof(pThis->abBuf) - ISO9660_SECTOR_SIZE];
+ if (pEntry->bSelectionCriteriaType != ISO9660_ELTORITO_SEL_CRIT_TYPE_NONE)
+ {
+ memcpy(pbSelCrit, pEntry->abSelectionCriteria, sizeof(pEntry->abSelectionCriteria));
+ cbSelCrit = sizeof(pEntry->abSelectionCriteria);
+
+ if ( (bMediaType & ISO9660_ELTORITO_BOOT_MEDIA_F_CONTINUATION)
+ && iEntry + 1 < cMaxEntries)
+ {
+ uint32_t iExtEntry = iEntry + 1;
+ PCISO9660ELTORITOSECTIONENTRYEXT pExtEntry = (PCISO9660ELTORITOSECTIONENTRYEXT)pEntry;
+ for (;;)
+ {
+ pExtEntry++;
+
+ if (pExtEntry->bExtensionId != ISO9660_ELTORITO_SECTION_ENTRY_EXT_ID)
+ {
+ rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_EXT_ENTRY_INVALID_ID,
+ "Invalid header ID for extension entry #%#x: %#x", iExtEntry, pExtEntry->bExtensionId);
+ break;
+ }
+ *pcSkip += 1;
+
+ memcpy(&pbSelCrit[cbSelCrit], pExtEntry->abSelectionCriteria, sizeof(pExtEntry->abSelectionCriteria));
+ cbSelCrit += sizeof(pExtEntry->abSelectionCriteria);
+
+ if (pExtEntry->fFlags & ISO9660_ELTORITO_SECTION_ENTRY_EXT_F_UNUSED_MASK)
+ rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_EXT_ENTRY_UNDEFINED_FLAGS,
+ "Boot catalog extension entry #%#x uses undefined flags: %#x", iExtEntry, pExtEntry->fFlags);
+
+ iExtEntry++;
+ if (!(pExtEntry->fFlags & ISO9660_ELTORITO_SECTION_ENTRY_EXT_F_MORE))
+ break;
+ if (iExtEntry >= cMaxEntries)
+ {
+ rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_EXT_ENTRY_END_OF_SECTOR,
+ "Boot catalog extension entry #%#x sets the MORE flag, but we have reached the end of the boot catalog sector");
+ break;
+ }
+ }
+ Assert(*pcSkip = iExtEntry - iEntry);
+ }
+ else if (bMediaType & ISO9660_ELTORITO_BOOT_MEDIA_F_CONTINUATION)
+ rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_ENTRY_CONTINUATION_EOS,
+ "Boot catalog extension entry #%#x sets the MORE flag, but we have reached the end of the boot catalog sector");
+ }
+ else if (bMediaType & ISO9660_ELTORITO_BOOT_MEDIA_F_CONTINUATION)
+ rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_ENTRY_CONTINUATION_WITH_NONE,
+ "Boot catalog entry #%#x uses the continuation flag with selection criteria NONE", iEntry);
+
+ /*
+ * Add the entry.
+ */
+ rc = RTFsIsoMakerBootCatSetSectionEntry(pThis->hIsoMaker, iEntry, idxImageObj, bMediaType, pEntry->bSystemType,
+ pEntry->bBootIndicator == ISO9660_ELTORITO_BOOT_INDICATOR_BOOTABLE,
+ pEntry->uLoadSeg, pEntry->cEmulatedSectorsToLoad,
+ pEntry->bSelectionCriteriaType, pbSelCrit, cbSelCrit);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->pResults->cBootCatEntries += 1 + *pcSkip;
+ rc = rtFsIsoImportProcessElToritoImage(pThis, idxImageObj, offBootImage);
+ }
+ else
+ rtFsIsoImpError(pThis, rc, "RTFsIsoMakerBootCatSetSectionEntry failed for entry #%#x: %Rrc", iEntry, rc);
+ return rc;
+}
+
+
+
+/**
+ * Processes a boot catalog section header entry.
+ *
+ * @returns IPRT status code (ignored).
+ * @param pThis The ISO importer instance.
+ * @param iEntry The boot catalog entry number.
+ * @param pEntry The entry to process.
+ */
+static int rtFsIsoImportProcessElToritoSectionHeader(PRTFSISOMKIMPORTER pThis, uint32_t iEntry,
+ PCISO9660ELTORITOSECTIONHEADER pEntry, char pszId[32])
+{
+ Assert(pEntry->bHeaderId == ISO9660_ELTORITO_HEADER_ID_SECTION_HEADER);
+
+ /* Deal with the string. ASSUME it doesn't contain zeros in non-terminal positions. */
+ if (pEntry->achSectionId[0] == '\0')
+ pszId = NULL;
+ else
+ {
+ memcpy(pszId, pEntry->achSectionId, sizeof(pEntry->achSectionId));
+ pszId[sizeof(pEntry->achSectionId)] = '\0';
+ }
+
+ int rc = RTFsIsoMakerBootCatSetSectionHeaderEntry(pThis->hIsoMaker, iEntry, RT_LE2H_U16(pEntry->cEntries),
+ pEntry->bPlatformId, pszId);
+ if (RT_SUCCESS(rc))
+ pThis->pResults->cBootCatEntries++;
+ else
+ rtFsIsoImpError(pThis, rc,
+ "RTFsIsoMakerBootCatSetSectionHeaderEntry failed for entry #%#x (bPlatformId=%#x cEntries=%#x): %Rrc",
+ iEntry, RT_LE2H_U16(pEntry->cEntries), pEntry->bPlatformId, rc);
+ return rc;
+}
+
+
+/**
+ * Processes a El Torito volume descriptor.
+ *
+ * @returns IPRT status code (ignorable).
+ * @param pThis The ISO importer instance.
+ * @param pVolDesc The volume descriptor to process.
+ */
+static int rtFsIsoImportProcessElToritoDesc(PRTFSISOMKIMPORTER pThis, PISO9660BOOTRECORDELTORITO pVolDesc)
+{
+ /*
+ * Read the boot catalog into the abBuf.
+ */
+ uint32_t offBootCatalog = RT_LE2H_U32(pVolDesc->offBootCatalog);
+ if (offBootCatalog >= pThis->cBlocksInPrimaryVolumeSpace)
+ return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_BAD_OUT_OF_BOUNDS,
+ "Boot catalog block number is out of bounds: %#RX32, max %#RX32",
+ offBootCatalog, pThis->cBlocksInPrimaryVolumeSpace);
+
+ int rc = RTVfsFileReadAt(pThis->hSrcFile, offBootCatalog * (uint64_t)ISO9660_SECTOR_SIZE,
+ pThis->abBuf, ISO9660_SECTOR_SIZE, NULL);
+ if (RT_FAILURE(rc))
+ return rtFsIsoImpError(pThis, rc, "Error reading boot catalog at block #%#RX32: %Rrc", offBootCatalog, rc);
+
+
+ /*
+ * Process the 'validation entry'.
+ */
+ PCISO9660ELTORITOVALIDATIONENTRY pValEntry = (PCISO9660ELTORITOVALIDATIONENTRY)&pThis->abBuf[0];
+ if (pValEntry->bHeaderId != ISO9660_ELTORITO_HEADER_ID_VALIDATION_ENTRY)
+ return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_BAD_VALIDATION_HEADER_ID,
+ "Invalid boot catalog validation entry header ID: %#x, expected %#x",
+ pValEntry->bHeaderId, ISO9660_ELTORITO_HEADER_ID_VALIDATION_ENTRY);
+
+ if ( pValEntry->bKey1 != ISO9660_ELTORITO_KEY_BYTE_1
+ || pValEntry->bKey2 != ISO9660_ELTORITO_KEY_BYTE_2)
+ return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_BAD_VALIDATION_KEYS,
+ "Invalid boot catalog validation entry keys: %#x %#x, expected %#x %#x",
+ pValEntry->bKey1, pValEntry->bKey2, ISO9660_ELTORITO_KEY_BYTE_1, ISO9660_ELTORITO_KEY_BYTE_2);
+
+ /* Check the checksum (should sum up to be zero). */
+ uint16_t uChecksum = 0;
+ uint16_t const *pu16 = (uint16_t const *)pValEntry;
+ size_t cLeft = sizeof(*pValEntry) / sizeof(uint16_t);
+ while (cLeft-- > 0)
+ {
+ uChecksum += RT_LE2H_U16(*pu16);
+ pu16++;
+ }
+ if (uChecksum != 0)
+ return rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_BAD_VALIDATION_CHECKSUM,
+ "Invalid boot catalog validation entry checksum: %#x, expected 0", uChecksum);
+
+ /* The string ID. ASSUME no leading zeros in valid strings. */
+ const char *pszId = NULL;
+ char szId[32];
+ if (pValEntry->achId[0] != '\0')
+ {
+ memcpy(szId, pValEntry->achId, sizeof(pValEntry->achId));
+ szId[sizeof(pValEntry->achId)] = '\0';
+ pszId = szId;
+ }
+
+ /*
+ * Before we tell the ISO maker about the validation entry, we need to sort
+ * out the file backing the boot catalog. This isn't fatal if it fails.
+ */
+ PRTFSISOMKIMPBLOCK2FILE pBlock2File = (PRTFSISOMKIMPBLOCK2FILE)RTAvlU32Get(&pThis->Block2FileRoot, offBootCatalog);
+ if (pBlock2File)
+ {
+ rc = RTFsIsoMakerBootCatSetFile(pThis->hIsoMaker, pBlock2File->idxObj);
+ if (RT_FAILURE(rc))
+ rtFsIsoImpError(pThis, rc, "RTFsIsoMakerBootCatSetFile failed: %Rrc", rc);
+ }
+
+ /*
+ * Set the validation entry.
+ */
+ rc = RTFsIsoMakerBootCatSetValidationEntry(pThis->hIsoMaker, pValEntry->bPlatformId, pszId);
+ if (RT_FAILURE(rc))
+ return rtFsIsoImpError(pThis, rc, "RTFsIsoMakerBootCatSetValidationEntry(,%#x,%s) failed: %Rrc",
+ pValEntry->bPlatformId, pszId);
+ Assert(pThis->pResults->cBootCatEntries == UINT32_MAX);
+ pThis->pResults->cBootCatEntries = 0;
+
+ /*
+ * Process the default entry and any subsequent entries.
+ */
+ bool fSeenFinal = false;
+ uint32_t const cMaxEntries = ISO9660_SECTOR_SIZE / ISO9660_ELTORITO_ENTRY_SIZE;
+ for (uint32_t iEntry = 1; iEntry < cMaxEntries; iEntry++)
+ {
+ uint8_t const *pbEntry = &pThis->abBuf[iEntry * ISO9660_ELTORITO_ENTRY_SIZE];
+ uint8_t const idHeader = *pbEntry;
+
+ /* KLUDGE ALERT! Older ISO images, like RHEL5-Server-20070208.0-x86_64-DVD.iso lacks
+ terminator entry. So, quietly stop with an entry that's all zeros. */
+ if ( idHeader == ISO9660_ELTORITO_BOOT_INDICATOR_NOT_BOOTABLE /* 0x00 */
+ && iEntry != 1 /* default */
+ && ASMMemIsZero(pbEntry, ISO9660_ELTORITO_ENTRY_SIZE))
+ return rc;
+
+ if ( iEntry == 1 /* default*/
+ || idHeader == ISO9660_ELTORITO_BOOT_INDICATOR_BOOTABLE
+ || idHeader == ISO9660_ELTORITO_BOOT_INDICATOR_NOT_BOOTABLE)
+ {
+ uint32_t cSkip = 0;
+ rtFsIsoImportProcessElToritoSectionEntry(pThis, iEntry, cMaxEntries, (PCISO9660ELTORITOSECTIONENTRY)pbEntry, &cSkip);
+ iEntry += cSkip;
+ }
+ else if (idHeader == ISO9660_ELTORITO_HEADER_ID_SECTION_HEADER)
+ rtFsIsoImportProcessElToritoSectionHeader(pThis, iEntry, (PCISO9660ELTORITOSECTIONHEADER)pbEntry, szId);
+ else if (idHeader == ISO9660_ELTORITO_HEADER_ID_FINAL_SECTION_HEADER)
+ {
+ fSeenFinal = true;
+ break;
+ }
+ else
+ rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_UNKNOWN_HEADER_ID,
+ "Unknown boot catalog header ID for entry #%#x: %#x", iEntry, idHeader);
+ }
+
+ if (!fSeenFinal)
+ rc = rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_BOOT_CAT_MISSING_FINAL_OR_TOO_BIG,
+ "Boot catalog is probably larger than a sector, or it's missing the final section header entry");
+ return rc;
+}
+
+
+/**
+ * Imports an existing ISO.
+ *
+ * Just like other source files, the existing image must remain present and
+ * unmodified till the ISO maker is done with it.
+ *
+ * @returns IRPT status code.
+ * @param hIsoMaker The ISO maker handle.
+ * @param hIsoFile VFS file handle to the existing image to import / clone.
+ * @param fFlags Reserved for the future, MBZ.
+ * @param poffError Where to return the position in @a pszIso
+ * causing trouble when opening it for reading.
+ * Optional.
+ * @param pErrInfo Where to return additional error information.
+ * Optional.
+ */
+RTDECL(int) RTFsIsoMakerImport(RTFSISOMAKER hIsoMaker, RTVFSFILE hIsoFile, uint32_t fFlags,
+ PRTFSISOMAKERIMPORTRESULTS pResults, PRTERRINFO pErrInfo)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(pResults, VERR_INVALID_POINTER);
+ pResults->cAddedNames = 0;
+ pResults->cAddedDirs = 0;
+ pResults->cbAddedDataBlocks = 0;
+ pResults->cAddedFiles = 0;
+ pResults->cAddedSymlinks = 0;
+ pResults->cBootCatEntries = UINT32_MAX;
+ pResults->cbSysArea = 0;
+ pResults->cErrors = 0;
+ AssertReturn(!(fFlags & ~RTFSISOMK_IMPORT_F_VALID_MASK), VERR_INVALID_FLAGS);
+
+ /*
+ * Get the file size.
+ */
+ uint64_t cbSrcFile = 0;
+ int rc = RTVfsFileQuerySize(hIsoFile, &cbSrcFile);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Allocate and init the importer state.
+ */
+ PRTFSISOMKIMPORTER pThis = (PRTFSISOMKIMPORTER)RTMemAllocZ(sizeof(*pThis));
+ if (pThis)
+ {
+ pThis->hIsoMaker = hIsoMaker;
+ pThis->fFlags = fFlags;
+ pThis->rc = VINF_SUCCESS;
+ pThis->pErrInfo = pErrInfo;
+ pThis->hSrcFile = hIsoFile;
+ pThis->cbSrcFile = cbSrcFile;
+ pThis->cBlocksInSrcFile = cbSrcFile / ISO9660_SECTOR_SIZE;
+ pThis->idxSrcFile = UINT32_MAX;
+ //pThis->Block2FileRoot = NULL;
+ //pThis->cBlocksInPrimaryVolumeSpace = 0;
+ //pThis->cbPrimaryVolumeSpace = 0
+ //pThis->cVolumesInSet = 0;
+ //pThis->idPrimaryVol = 0;
+ //pThis->fSeenJoliet = false;
+ pThis->pResults = pResults;
+ //pThis->fSuspSeenSP = false;
+ //pThis->offSuspSkip = 0;
+ pThis->offRockBuf = UINT64_MAX;
+
+ /*
+ * Check if this looks like a plausible ISO by checking out the first volume descriptor.
+ */
+ rc = RTVfsFileReadAt(hIsoFile, _32K, &pThis->uSectorBuf.PrimVolDesc, sizeof(pThis->uSectorBuf.PrimVolDesc), NULL);
+ if (RT_SUCCESS(rc))
+ {
+ if ( pThis->uSectorBuf.VolDescHdr.achStdId[0] == ISO9660VOLDESC_STD_ID_0
+ && pThis->uSectorBuf.VolDescHdr.achStdId[1] == ISO9660VOLDESC_STD_ID_1
+ && pThis->uSectorBuf.VolDescHdr.achStdId[2] == ISO9660VOLDESC_STD_ID_2
+ && pThis->uSectorBuf.VolDescHdr.achStdId[3] == ISO9660VOLDESC_STD_ID_3
+ && pThis->uSectorBuf.VolDescHdr.achStdId[4] == ISO9660VOLDESC_STD_ID_4
+ && ( pThis->uSectorBuf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_PRIMARY
+ || pThis->uSectorBuf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_BOOT_RECORD) )
+ {
+ /*
+ * Process the volume descriptors using the sector buffer, starting
+ * with the one we've already got sitting there. We postpone processing
+ * the el torito one till after the others, so we can name files and size
+ * referenced in it.
+ */
+ uint32_t cPrimaryVolDescs = 0;
+ uint32_t iElTorito = UINT32_MAX;
+ uint32_t iVolDesc = 0;
+ for (;;)
+ {
+ switch (pThis->uSectorBuf.VolDescHdr.bDescType)
+ {
+ case ISO9660VOLDESC_TYPE_PRIMARY:
+ cPrimaryVolDescs++;
+ if (cPrimaryVolDescs == 1)
+ {
+ pThis->offPrimaryVolDesc = _32K / ISO9660_SECTOR_SIZE + iVolDesc;
+ rtFsIsoImportProcessPrimaryDesc(pThis, &pThis->uSectorBuf.PrimVolDesc);
+ }
+ else
+ rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_MULTIPLE_PRIMARY_VOL_DESCS,
+ "Only a single primary volume descriptor is currently supported");
+ break;
+
+ case ISO9660VOLDESC_TYPE_SUPPLEMENTARY:
+ if (cPrimaryVolDescs > 0)
+ rtFsIsoImportProcessSupplementaryDesc(pThis, &pThis->uSectorBuf.SupVolDesc);
+ else
+ rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_SUPPLEMENTARY_BEFORE_PRIMARY,
+ "Primary volume descriptor expected before any supplementary descriptors!");
+ break;
+
+ case ISO9660VOLDESC_TYPE_BOOT_RECORD:
+ if (strcmp(pThis->uSectorBuf.ElToritoDesc.achBootSystemId,
+ ISO9660BOOTRECORDELTORITO_BOOT_SYSTEM_ID) == 0)
+ {
+ if (iElTorito == UINT32_MAX)
+ iElTorito = iVolDesc;
+ else
+ rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_MULTIPLE_EL_TORITO_DESCS,
+ "Only a single El Torito descriptor exepcted!");
+ }
+ break;
+
+ case ISO9660VOLDESC_TYPE_PARTITION:
+ /* ignore for now */
+ break;
+
+ case ISO9660VOLDESC_TYPE_TERMINATOR:
+ AssertFailed();
+ break;
+ }
+
+
+ /*
+ * Read the next volume descriptor and check the signature.
+ */
+ iVolDesc++;
+ if (iVolDesc >= 32)
+ {
+ rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_TOO_MANY_VOL_DESCS, "Parses at most 32 volume descriptors");
+ break;
+ }
+
+ rc = RTVfsFileReadAt(hIsoFile, _32K + iVolDesc * ISO9660_SECTOR_SIZE,
+ &pThis->uSectorBuf, sizeof(pThis->uSectorBuf), NULL);
+ if (RT_FAILURE(rc))
+ {
+ rtFsIsoImpError(pThis, rc, "Error reading the volume descriptor #%u at %#RX32: %Rrc",
+ iVolDesc, _32K + iVolDesc * ISO9660_SECTOR_SIZE, rc);
+ break;
+ }
+
+ if ( pThis->uSectorBuf.VolDescHdr.achStdId[0] != ISO9660VOLDESC_STD_ID_0
+ || pThis->uSectorBuf.VolDescHdr.achStdId[1] != ISO9660VOLDESC_STD_ID_1
+ || pThis->uSectorBuf.VolDescHdr.achStdId[2] != ISO9660VOLDESC_STD_ID_2
+ || pThis->uSectorBuf.VolDescHdr.achStdId[3] != ISO9660VOLDESC_STD_ID_3
+ || pThis->uSectorBuf.VolDescHdr.achStdId[4] != ISO9660VOLDESC_STD_ID_4)
+ {
+ rtFsIsoImpError(pThis, VERR_ISOMK_IMPORT_INVALID_VOL_DESC_HDR,
+ "Invalid volume descriptor header #%u at %#RX32: %.*Rhxs",
+ iVolDesc, _32K + iVolDesc * ISO9660_SECTOR_SIZE,
+ (int)sizeof(pThis->uSectorBuf.VolDescHdr), &pThis->uSectorBuf.VolDescHdr);
+ break;
+ }
+ /** @todo UDF support. */
+ if (pThis->uSectorBuf.VolDescHdr.bDescType == ISO9660VOLDESC_TYPE_TERMINATOR)
+ break;
+ }
+
+ /*
+ * Process the system area.
+ */
+ if (RT_SUCCESS(pThis->rc) || pThis->idxSrcFile != UINT32_MAX)
+ {
+ rc = RTVfsFileReadAt(hIsoFile, 0, pThis->abBuf, _32K, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ if (!ASMMemIsAllU8(pThis->abBuf, _32K, 0))
+ {
+ /* Drop zero sectors from the end. */
+ uint32_t cbSysArea = _32K;
+ while ( cbSysArea >= ISO9660_SECTOR_SIZE
+ && ASMMemIsAllU8(&pThis->abBuf[cbSysArea - ISO9660_SECTOR_SIZE], ISO9660_SECTOR_SIZE, 0))
+ cbSysArea -= ISO9660_SECTOR_SIZE;
+
+ /** @todo HFS */
+ pThis->pResults->cbSysArea = cbSysArea;
+ rc = RTFsIsoMakerSetSysAreaContent(hIsoMaker, pThis->abBuf, cbSysArea, 0);
+ if (RT_FAILURE(rc))
+ rtFsIsoImpError(pThis, rc, "RTFsIsoMakerSetSysAreaContent failed: %Rrc", rc);
+ }
+ }
+ else
+ rtFsIsoImpError(pThis, rc, "Error reading the system area (0..32KB): %Rrc", rc);
+ }
+
+ /*
+ * Do the El Torito descriptor.
+ */
+ if ( iElTorito != UINT32_MAX
+ && !(pThis->fFlags & RTFSISOMK_IMPORT_F_NO_BOOT)
+ && (RT_SUCCESS(pThis->rc) || pThis->idxSrcFile != UINT32_MAX))
+ {
+ rc = RTVfsFileReadAt(hIsoFile, _32K + iElTorito * ISO9660_SECTOR_SIZE,
+ &pThis->uSectorBuf, sizeof(pThis->uSectorBuf), NULL);
+ if (RT_SUCCESS(rc))
+ rtFsIsoImportProcessElToritoDesc(pThis, &pThis->uSectorBuf.ElToritoDesc);
+ else
+ rtFsIsoImpError(pThis, rc, "Error reading the El Torito volume descriptor at %#RX32: %Rrc",
+ _32K + iElTorito * ISO9660_SECTOR_SIZE, rc);
+ }
+
+ /*
+ * Return the first error status.
+ */
+ rc = pThis->rc;
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, VERR_ISOMK_IMPORT_UNKNOWN_FORMAT, "Invalid volume descriptor header: %.*Rhxs",
+ (int)sizeof(pThis->uSectorBuf.VolDescHdr), &pThis->uSectorBuf.VolDescHdr);
+ }
+
+ /*
+ * Destroy the state.
+ */
+ RTAvlU32Destroy(&pThis->Block2FileRoot, rtFsIsoMakerImportDestroyData2File, NULL);
+ RTMemFree(pThis);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ return rc;
+}
+