summaryrefslogtreecommitdiffstats
path: root/src/VBox/Storage/VISO.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Storage/VISO.cpp')
-rw-r--r--src/VBox/Storage/VISO.cpp1119
1 files changed, 1119 insertions, 0 deletions
diff --git a/src/VBox/Storage/VISO.cpp b/src/VBox/Storage/VISO.cpp
new file mode 100644
index 00000000..8cb691d6
--- /dev/null
+++ b/src/VBox/Storage/VISO.cpp
@@ -0,0 +1,1119 @@
+/* $Id: VISO.cpp $ */
+/** @file
+ * VISO - Virtual ISO disk image, Core Code.
+ */
+
+/*
+ * 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>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_VD
+#include <VBox/vd-plugin.h>
+#include <VBox/err.h>
+
+#include <VBox/log.h>
+#include <iprt/assert.h>
+#include <iprt/ctype.h>
+#include <iprt/fsisomaker.h>
+#include <iprt/getopt.h>
+#include <iprt/mem.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include <iprt/uuid.h>
+
+#include "VDBackends.h"
+#include "VDBackendsInline.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** The maximum file size. */
+#if ARCH_BITS >= 64
+# define VISO_MAX_FILE_SIZE _32M
+#else
+# define VISO_MAX_FILE_SIZE _8M
+#endif
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+
+/**
+ * VBox ISO maker image instance.
+ */
+typedef struct VISOIMAGE
+{
+ /** The ISO maker output file handle.
+ * This is NIL if in VD_OPEN_FLAGS_INFO mode. */
+ RTVFSFILE hIsoFile;
+ /** The image size. */
+ uint64_t cbImage;
+ /** The UUID ofr the image. */
+ RTUUID Uuid;
+
+ /** Open flags passed by VD layer. */
+ uint32_t fOpenFlags;
+ /** Image name. Allocation follows the region list, no need to free. */
+ const char *pszFilename;
+ /** The parent director of pszFilename.
+ * Allocation follows the region list, no need to free. */
+ const char *pszCwd;
+
+ /** I/O interface. */
+ PVDINTERFACEIOINT pIfIo;
+ /** Error interface. */
+ PVDINTERFACEERROR pIfError;
+
+ /** Internal region list (variable size). */
+ VDREGIONLIST RegionList;
+} VISOIMAGE;
+/** Pointer to an VBox ISO make image instance. */
+typedef VISOIMAGE *PVISOIMAGE;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** NULL-terminated array of supported file extensions. */
+static const VDFILEEXTENSION g_aVBoXIsoMakerFileExtensions[] =
+{
+ //{ "vbox-iso-maker", VDTYPE_OPTICAL_DISC }, - clumsy.
+ { "viso", VDTYPE_OPTICAL_DISC },
+ { NULL, VDTYPE_INVALID }
+};
+
+/** NULL-terminated array of configuration option. */
+static const VDCONFIGINFO s_aVisoConfigInfo[] =
+{
+ /* Options for VMDK raw disks */
+ { "UnattendedInstall", NULL, VDCFGVALUETYPE_STRING, VD_CFGKEY_EXPERT },
+ /* End of options list */
+ { NULL, NULL, VDCFGVALUETYPE_INTEGER, 0 }
+};
+
+/**
+ * Parses the UUID that follows the marker argument.
+ *
+ * @returns IPRT status code.
+ * @param pszMarker The marker.
+ * @param pUuid Where to return the UUID.
+ */
+static int visoParseUuid(char *pszMarker, PRTUUID pUuid)
+{
+ /* Skip the marker. */
+ char ch;
+ while ( (ch = *pszMarker) != '\0'
+ && !RT_C_IS_SPACE(ch)
+ && ch != ':'
+ && ch != '=')
+ pszMarker++;
+
+ /* Skip chars before the value. */
+ if ( ch == ':'
+ || ch == '=')
+ ch = *++pszMarker;
+ else
+ while (RT_C_IS_SPACE(ch))
+ ch = *++pszMarker;
+ const char * const pszUuid = pszMarker;
+
+ /* Find the end of the UUID value. */
+ while ( ch != '\0'
+ && !RT_C_IS_SPACE(ch))
+ ch = *++pszMarker;
+
+ /* Validate the value (temporarily terminate the value string) */
+ *pszMarker = '\0';
+ int rc = RTUuidFromStr(pUuid, pszUuid);
+ if (RT_SUCCESS(rc))
+ {
+ *pszMarker = ch;
+ return VINF_SUCCESS;
+ }
+
+ /* Complain and return VERR_VD_IMAGE_CORRUPTED to indicate we've identified
+ the right image format, but the producer got something wrong. */
+ if (pszUuid != pszMarker)
+ LogRel(("visoParseUuid: Malformed UUID '%s': %Rrc\n", pszUuid, rc));
+ else
+ LogRel(("visoParseUuid: Empty/missing UUID!\n"));
+ *pszMarker = ch;
+
+ return VERR_VD_IMAGE_CORRUPTED;
+}
+
+
+static int visoProbeWorker(const char *pszFilename, PVDINTERFACEIOINT pIfIo, PRTUUID pUuid)
+{
+ PVDIOSTORAGE pStorage = NULL;
+ int rc = vdIfIoIntFileOpen(pIfIo, pszFilename, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE, &pStorage);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Read the first part of the file.
+ */
+ uint64_t cbFile = 0;
+ rc = vdIfIoIntFileGetSize(pIfIo, pStorage, &cbFile);
+ if (RT_SUCCESS(rc))
+ {
+ char szChunk[_1K];
+ size_t cbToRead = (size_t)RT_MIN(sizeof(szChunk) - 1, cbFile);
+ rc = vdIfIoIntFileReadSync(pIfIo, pStorage, 0 /*off*/, szChunk, cbToRead);
+ if (RT_SUCCESS(rc))
+ {
+ szChunk[cbToRead] = '\0';
+
+ /*
+ * Skip leading spaces and check for the eye-catcher.
+ */
+ char *psz = szChunk;
+ while (RT_C_IS_SPACE(*psz))
+ psz++;
+ if (strncmp(psz, RT_STR_TUPLE("--iprt-iso-maker-file-marker")) == 0)
+ {
+ rc = visoParseUuid(psz, pUuid);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Check the file size.
+ */
+ if (cbFile <= VISO_MAX_FILE_SIZE)
+ rc = VINF_SUCCESS;
+ else
+ {
+ LogRel(("visoProbeWorker: VERR_VD_INVALID_SIZE - cbFile=%#RX64 cbMaxFile=%#RX64\n",
+ cbFile, (uint64_t)VISO_MAX_FILE_SIZE));
+ rc = VERR_VD_INVALID_SIZE;
+ }
+ }
+ else
+ rc = VERR_VD_IMAGE_CORRUPTED;
+ }
+ else
+ rc = VERR_VD_GEN_INVALID_HEADER;
+ }
+ }
+ vdIfIoIntFileClose(pIfIo, pStorage);
+ }
+ LogFlowFunc(("returns %Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * @interface_method_impl{VDIMAGEBACKEND,pfnProbe}
+ */
+static DECLCALLBACK(int) visoProbe(const char *pszFilename, PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
+ VDTYPE enmDesiredType, VDTYPE *penmType)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(penmType, VERR_INVALID_POINTER);
+ *penmType = VDTYPE_INVALID;
+
+ AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
+ AssertReturn(*pszFilename, VERR_INVALID_POINTER);
+
+ PVDINTERFACEIOINT pIfIo = VDIfIoIntGet(pVDIfsImage);
+ AssertPtrReturn(pIfIo, VERR_INVALID_PARAMETER);
+
+ RT_NOREF(pVDIfsDisk);
+
+ /*
+ * We can only fake DVD stuff, so fail if the desired type doesn't match up
+ */
+ if (enmDesiredType != VDTYPE_OPTICAL_DISC && enmDesiredType != VDTYPE_INVALID)
+ return VERR_VD_GEN_INVALID_HEADER; /* Caller has strict, though undocument, status code expectations. */
+
+ /*
+ * Share worker with visoOpen and visoSetFlags.
+ */
+ RTUUID UuidIgn;
+ int rc = visoProbeWorker(pszFilename, pIfIo, &UuidIgn);
+ if (RT_SUCCESS(rc))
+ *penmType = VDTYPE_OPTICAL_DISC;
+ else if (rc == VERR_VD_IMAGE_CORRUPTED || rc == VERR_VD_INVALID_SIZE)
+ *penmType = VDTYPE_OPTICAL_DISC;
+ else
+ rc = VERR_VD_GEN_INVALID_HEADER; /* Caller has strict, though undocument, status code expectations. */
+
+ LogFlowFunc(("returns %Rrc - *penmType=%d\n", rc, *penmType));
+ return rc;
+}
+
+
+/**
+ * Worker for visoOpen and visoSetOpenFlags that creates a VFS file for the ISO.
+ *
+ * This also updates cbImage and the Uuid members.
+ *
+ * @returns VBox status code.
+ * @param pThis The VISO image instance.
+ */
+static int visoOpenWorker(PVISOIMAGE pThis)
+{
+ /*
+ * Open the file and read it into memory.
+ */
+ PVDIOSTORAGE pStorage = NULL;
+ int rc = vdIfIoIntFileOpen(pThis->pIfIo, pThis->pszFilename, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE, &pStorage);
+ if (RT_FAILURE(rc))
+ {
+ LogRel(("VISO: Unable to open file '%s': %Rrc\n", pThis->pszFilename, rc));
+ return rc;
+ }
+
+ LogRel(("VISO: Handling file '%s'\n", pThis->pszFilename));
+
+ /*
+ * Read the file into memory, prefixing it with a dummy command name.
+ */
+ uint64_t cbFile = 0;
+ rc = vdIfIoIntFileGetSize(pThis->pIfIo, pStorage, &cbFile);
+ if (RT_SUCCESS(rc))
+ {
+ if (cbFile <= VISO_MAX_FILE_SIZE)
+ {
+ static char const s_szCmdPrefix[] = "VBox-Iso-Maker ";
+
+ char *pszContent = (char *)RTMemTmpAlloc(sizeof(s_szCmdPrefix) + cbFile);
+ if (pszContent)
+ {
+ char *pszReadDst = &pszContent[sizeof(s_szCmdPrefix) - 1];
+ rc = vdIfIoIntFileReadSync(pThis->pIfIo, pStorage, 0 /*off*/, pszReadDst, (size_t)cbFile);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Check the file marker and get the UUID that follows it.
+ * Ignore leading blanks.
+ */
+ pszReadDst[(size_t)cbFile] = '\0';
+ memcpy(pszContent, s_szCmdPrefix, sizeof(s_szCmdPrefix) - 1);
+
+ while (RT_C_IS_SPACE(*pszReadDst))
+ pszReadDst++;
+ if (strncmp(pszReadDst, RT_STR_TUPLE("--iprt-iso-maker-file-marker")) == 0)
+ {
+ rc = visoParseUuid(pszReadDst, &pThis->Uuid);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Make sure it's valid UTF-8 before letting
+ */
+ rc = RTStrValidateEncodingEx(pszContent, sizeof(s_szCmdPrefix) + cbFile,
+ RTSTR_VALIDATE_ENCODING_EXACT_LENGTH
+ | RTSTR_VALIDATE_ENCODING_ZERO_TERMINATED);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Convert it into an argument vector.
+ * Free the content afterwards to reduce memory pressure.
+ */
+ uint32_t fGetOpt = strncmp(pszReadDst, RT_STR_TUPLE("--iprt-iso-maker-file-marker-ms")) != 0
+ ? RTGETOPTARGV_CNV_QUOTE_BOURNE_SH : RTGETOPTARGV_CNV_QUOTE_MS_CRT;
+ fGetOpt |= RTGETOPTARGV_CNV_MODIFY_INPUT;
+ char **papszArgs;
+ int cArgs;
+ rc = RTGetOptArgvFromString(&papszArgs, &cArgs, pszContent, fGetOpt, NULL);
+
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Open the parent directory and use that as CWD for relative references.
+ */
+ RTVFSDIR hVfsCwd;
+ rc = RTVfsChainOpenDir(pThis->pszCwd, 0 /*fOpen*/, &hVfsCwd, NULL, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Try instantiate the ISO image maker.
+ * Free the argument vector afterward to reduce memory pressure.
+ */
+ RTVFSFILE hVfsFile;
+ RTERRINFOSTATIC ErrInfo;
+ rc = RTFsIsoMakerCmdEx(cArgs, papszArgs, hVfsCwd, pThis->pszCwd,
+ &hVfsFile, RTErrInfoInitStatic(&ErrInfo));
+
+ RTVfsDirRelease(hVfsCwd);
+
+ RTGetOptArgvFreeEx(papszArgs, fGetOpt);
+ papszArgs = NULL;
+
+ if (RT_SUCCESS(rc))
+ {
+ uint64_t cbImage;
+ rc = RTVfsFileQuerySize(hVfsFile, &cbImage);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Update the state.
+ */
+ pThis->cbImage = cbImage;
+ pThis->RegionList.aRegions[0].cRegionBlocksOrBytes = cbImage;
+
+ pThis->hIsoFile = hVfsFile;
+ hVfsFile = NIL_RTVFSFILE;
+
+ rc = VINF_SUCCESS;
+ LogRel(("VISO: %'RU64 bytes (%#RX64) - %s\n", cbImage, cbImage, pThis->pszFilename));
+ }
+
+ RTVfsFileRelease(hVfsFile);
+ }
+ else if (RTErrInfoIsSet(&ErrInfo.Core))
+ {
+ LogRel(("visoOpenWorker: RTFsIsoMakerCmdEx failed: %Rrc - %s\n", rc, ErrInfo.Core.pszMsg));
+ vdIfError(pThis->pIfError, rc, RT_SRC_POS, "VISO: %s", ErrInfo.Core.pszMsg);
+ }
+ else
+ {
+ LogRel(("visoOpenWorker: RTFsIsoMakerCmdEx failed: %Rrc\n", rc));
+ vdIfError(pThis->pIfError, rc, RT_SRC_POS, "VISO: RTFsIsoMakerCmdEx failed: %Rrc", rc);
+ }
+ }
+ else
+ vdIfError(pThis->pIfError, rc, RT_SRC_POS,
+ "VISO: Failed to open parent dir of: %s", pThis->pszFilename);
+ }
+ else
+ vdIfError(pThis->pIfError, rc, RT_SRC_POS, "VISO: RTGetOptArgvFromString failed: %Rrc", rc);
+ }
+ else
+ vdIfError(pThis->pIfError, rc, RT_SRC_POS, "VISO: Invalid file encoding");
+ }
+ else
+ vdIfError(pThis->pIfError, rc, RT_SRC_POS, "VISO: Parsing UUID failed: %Rrc", rc);
+ }
+ else
+ rc = VERR_VD_GEN_INVALID_HEADER;
+ }
+ else
+ vdIfError(pThis->pIfError, rc, RT_SRC_POS, "VISO: Reading file failed: %Rrc", rc);
+
+ RTMemTmpFree(pszContent);
+ }
+ else
+ rc = VERR_NO_TMP_MEMORY;
+ }
+ else
+ {
+ LogRel(("visoOpen: VERR_VD_INVALID_SIZE - cbFile=%#RX64 cbMaxFile=%#RX64\n",
+ cbFile, (uint64_t)VISO_MAX_FILE_SIZE));
+ rc = VERR_VD_INVALID_SIZE;
+ }
+ }
+
+ if (RT_FAILURE(rc))
+ LogRel(("VISO: Handling of file '%s' failed with %Rrc\n", pThis->pszFilename, rc));
+
+ vdIfIoIntFileClose(pThis->pIfIo, pStorage);
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{VDIMAGEBACKEND,pfnOpen}
+ */
+static DECLCALLBACK(int) visoOpen(const char *pszFilename, unsigned uOpenFlags, PVDINTERFACE pVDIfsDisk, PVDINTERFACE pVDIfsImage,
+ VDTYPE enmType, void **ppBackendData)
+{
+ uint32_t const fOpenFlags = uOpenFlags;
+ LogFlowFunc(("pszFilename='%s' fOpenFlags=%#x pVDIfsDisk=%p pVDIfsImage=%p enmType=%u ppBackendData=%p\n",
+ pszFilename, fOpenFlags, pVDIfsDisk, pVDIfsImage, enmType, ppBackendData));
+
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(ppBackendData, VERR_INVALID_POINTER);
+ *ppBackendData = NULL;
+
+ AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
+ AssertReturn(*pszFilename, VERR_INVALID_POINTER);
+
+ AssertReturn(!(fOpenFlags & ~VD_OPEN_FLAGS_MASK), VERR_INVALID_FLAGS);
+
+ PVDINTERFACEIOINT pIfIo = VDIfIoIntGet(pVDIfsImage);
+ AssertPtrReturn(pIfIo, VERR_INVALID_PARAMETER);
+
+ PVDINTERFACEERROR pIfError = VDIfErrorGet(pVDIfsDisk);
+
+ AssertReturn(enmType == VDTYPE_OPTICAL_DISC, VERR_NOT_SUPPORTED);
+
+ /*
+ * Allocate and initialize the backend image instance data.
+ */
+ int rc;
+ size_t cbFilename = strlen(pszFilename) + 1;
+ PVISOIMAGE pThis = (PVISOIMAGE)RTMemAllocZ(RT_UOFFSETOF(VISOIMAGE, RegionList.aRegions[1]) + cbFilename * 2);
+ if (pThis)
+ {
+ pThis->hIsoFile = NIL_RTVFSFILE;
+ pThis->cbImage = 0;
+ pThis->fOpenFlags = fOpenFlags;
+ pThis->pIfIo = pIfIo;
+ pThis->pIfError = pIfError;
+
+ pThis->RegionList.fFlags = 0;
+ pThis->RegionList.cRegions = 1;
+ pThis->RegionList.aRegions[0].offRegion = 0;
+ pThis->RegionList.aRegions[0].cRegionBlocksOrBytes = 0;
+ pThis->RegionList.aRegions[0].cbBlock = 2048;
+ pThis->RegionList.aRegions[0].enmDataForm = VDREGIONDATAFORM_RAW;
+ pThis->RegionList.aRegions[0].enmMetadataForm = VDREGIONMETADATAFORM_NONE;
+ pThis->RegionList.aRegions[0].cbData = 2048;
+ pThis->RegionList.aRegions[0].cbMetadata = 0;
+
+ char *pszDst = (char *)&pThis->RegionList.aRegions[1];
+ memcpy(pszDst, pszFilename, cbFilename);
+ pThis->pszFilename = pszDst;
+ pszDst[cbFilename - 1] = '\0';
+ pszDst += cbFilename;
+
+ memcpy(pszDst, pszFilename, cbFilename);
+ pThis->pszCwd = pszDst;
+ pszDst[cbFilename - 1] = '\0';
+ RTPathStripFilename(pszDst);
+
+ /*
+ * Only go all the way if this isn't an info query. Re-mastering an ISO
+ * can potentially be a lot of work and we don't want to go thru with it
+ * just because the GUI wants to display the image size.
+ */
+ if (!(fOpenFlags & VD_OPEN_FLAGS_INFO))
+ rc = visoOpenWorker(pThis);
+ else
+ rc = visoProbeWorker(pThis->pszFilename, pThis->pIfIo, &pThis->Uuid);
+ if (RT_SUCCESS(rc))
+ {
+ *ppBackendData = pThis;
+ LogFlowFunc(("returns VINF_SUCCESS (UUID=%RTuuid, pszFilename=%s)\n", &pThis->Uuid, pThis->pszFilename));
+ return VINF_SUCCESS;
+ }
+
+ RTMemFree(pThis);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ LogFlowFunc(("returns %Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * Scans the VISO file and removes all references to files
+ * which are in the same folder as the VISO and
+ * whose names begin with "Unattended-".
+ *
+ * @return VBox status code.
+ *
+ * @param pThis Pointer to VISO backend data.
+ */
+static int deleteReferences(PVISOIMAGE pThis)
+{
+ /*
+ * Open the file and read it into memory.
+ */
+ PVDIOSTORAGE pStorage = NULL;
+ int vrc = vdIfIoIntFileOpen(pThis->pIfIo, pThis->pszFilename, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE, &pStorage);
+ if (RT_FAILURE(vrc))
+ {
+ LogRel(("VISO: Unable to open file '%s': %Rrc\n", pThis->pszFilename, vrc));
+ return vrc;
+ }
+
+ LogRel(("VISO: Handling file '%s' references\n", pThis->pszFilename));
+
+ /*
+ * Read the file into memory.
+ */
+ uint64_t cbFile = 0;
+ vrc = vdIfIoIntFileGetSize(pThis->pIfIo, pStorage, &cbFile);
+ if (RT_SUCCESS(vrc))
+ {
+ if (cbFile <= VISO_MAX_FILE_SIZE)
+ {
+ char *pszContent = (char *)RTMemTmpAlloc(cbFile + 1);
+ if (pszContent)
+ {
+ vrc = vdIfIoIntFileReadSync(pThis->pIfIo, pStorage, 0 /*off*/, pszContent, (size_t)cbFile);
+ if (RT_SUCCESS(vrc))
+ {
+ /*
+ * Check the file marker.
+ * Ignore leading blanks.
+ */
+ pszContent[(size_t)cbFile] = '\0';
+
+ char *pszReadDst = pszContent;
+ while (RT_C_IS_SPACE(*pszReadDst))
+ pszReadDst++;
+ if (strncmp(pszReadDst, RT_STR_TUPLE("--iprt-iso-maker-file-marker")) == 0)
+ {
+ vrc = visoParseUuid(pszReadDst, &pThis->Uuid);
+ if (RT_SUCCESS(vrc))
+ {
+ /*
+ * Make sure it's valid UTF-8 before letting
+ */
+ vrc = RTStrValidateEncodingEx(pszContent, cbFile + 1,
+ RTSTR_VALIDATE_ENCODING_EXACT_LENGTH
+ | RTSTR_VALIDATE_ENCODING_ZERO_TERMINATED);
+ if (RT_SUCCESS(vrc))
+ {
+ /*
+ * Convert it into an argument vector.
+ * Free the content afterwards to reduce memory pressure.
+ */
+ uint32_t fGetOpt = strncmp(pszReadDst, RT_STR_TUPLE("--iprt-iso-maker-file-marker-ms")) != 0
+ ? RTGETOPTARGV_CNV_QUOTE_BOURNE_SH : RTGETOPTARGV_CNV_QUOTE_MS_CRT;
+ fGetOpt |= RTGETOPTARGV_CNV_MODIFY_INPUT;
+ char **papszArgs;
+ int cArgs;
+ vrc = RTGetOptArgvFromString(&papszArgs, &cArgs, pszContent, fGetOpt, NULL);
+
+ if (RT_SUCCESS(vrc))
+ {
+ for (int i = 0; i < cArgs; ++i)
+ {
+ char *pszArg = papszArgs[i];
+ char *pszOffset = strrchr(pszArg, '=');
+ if (pszOffset != NULL)
+ pszArg = pszOffset + 1;
+
+ /* if it isn't option */
+ if (pszArg[0] != '-')
+ {
+ char *pszPath = RTPathAbsExDup(pThis->pszCwd, pszArg, 0);
+ if (RTStrStartsWith((const char *)pszPath, pThis->pszCwd))
+ {
+ char *pszFileName = RTPathFilename(pszPath);
+ if ( pszFileName != NULL
+ && RTStrStartsWith((const char *)pszFileName, "Unattended-"))
+ {
+ vrc = RTFileDelete(pszPath);
+ if (RT_SUCCESS(vrc))
+ LogRel(("VISO: file '%s' deleted\n", pszPath));
+ else
+ LogRel(("VISO: Failed to delete the file '%s' (%Rrc)\n", pszPath, vrc));
+ vrc = VINF_SUCCESS;
+ }
+ }
+ RTStrFree(pszPath);
+ }
+ }
+ RTGetOptArgvFreeEx(papszArgs, fGetOpt);
+ papszArgs = NULL;
+ }
+ else
+ vdIfError(pThis->pIfError, vrc, RT_SRC_POS, "VISO: RTGetOptArgvFromString failed: %Rrc", vrc);
+ }
+ else
+ vdIfError(pThis->pIfError, vrc, RT_SRC_POS, "VISO: Invalid file encoding");
+ }
+ else
+ vdIfError(pThis->pIfError, vrc, RT_SRC_POS, "VISO: Parsing UUID failed: %Rrc", vrc);
+ }
+ else
+ vrc = VERR_VD_GEN_INVALID_HEADER;
+ }
+ else
+ vdIfError(pThis->pIfError, vrc, RT_SRC_POS, "VISO: Reading file failed: %Rrc", vrc);
+
+ RTMemTmpFree(pszContent);
+ }
+ else
+ vrc = VERR_NO_TMP_MEMORY;
+ }
+ else
+ {
+ LogRel(("visoOpen: VERR_VD_INVALID_SIZE - cbFile=%#RX64 cbMaxFile=%#RX64\n",
+ cbFile, (uint64_t)VISO_MAX_FILE_SIZE));
+ vrc = VERR_VD_INVALID_SIZE;
+ }
+ }
+ if (RT_FAILURE(vrc))
+ LogRel(("VISO: Handling of file '%s' failed with %Rrc\n", pThis->pszFilename, vrc));
+
+ vdIfIoIntFileClose(pThis->pIfIo, pStorage);
+ return vrc;
+}
+
+/**
+ * @interface_method_impl{VDIMAGEBACKEND,pfnClose}
+ */
+static DECLCALLBACK(int) visoClose(void *pBackendData, bool fDelete)
+{
+ PVISOIMAGE pThis = (PVISOIMAGE)pBackendData;
+ LogFlowFunc(("pThis=%p fDelete=%RTbool\n", pThis, fDelete));
+
+ if (pThis)
+ {
+ if (fDelete)
+ {
+ PVDINTERFACECONFIG pImgCfg = VDIfConfigGet(&pThis->pIfIo->Core);
+
+ bool fUnattendedInstall = false;
+ int vrc = VDCFGQueryBool(pImgCfg, "UnattendedInstall", &fUnattendedInstall);
+
+ /*
+ * The VISO created by unattended installer, so delete all generated files
+ * included in the VISO. the file is considered generated if it is located
+ * in the same folder as VISO and its name begins with "Unattended-"
+ */
+ if (RT_SUCCESS(vrc) && fUnattendedInstall)
+ deleteReferences(pThis);
+ vdIfIoIntFileDelete(pThis->pIfIo, pThis->pszFilename);
+ }
+
+ if (pThis->hIsoFile != NIL_RTVFSFILE)
+ {
+ RTVfsFileRelease(pThis->hIsoFile);
+ pThis->hIsoFile = NIL_RTVFSFILE;
+ }
+
+ RTMemFree(pThis);
+ }
+
+ LogFlowFunc(("returns VINF_SUCCESS\n"));
+ return VINF_SUCCESS;
+}
+
+/**
+ * @interface_method_impl{VDIMAGEBACKEND,pfnRead}
+ */
+static DECLCALLBACK(int) visoRead(void *pBackendData, uint64_t uOffset, size_t cbToRead, PVDIOCTX pIoCtx, size_t *pcbActuallyRead)
+{
+ PVISOIMAGE pThis = (PVISOIMAGE)pBackendData;
+ uint64_t off = uOffset;
+ AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
+ AssertReturn(pThis->hIsoFile != NIL_RTVFSFILE, VERR_VD_NOT_OPENED);
+ LogFlowFunc(("pThis=%p off=%#RX64 cbToRead=%#zx pIoCtx=%p pcbActuallyRead=%p\n", pThis, off, cbToRead, pIoCtx, pcbActuallyRead));
+
+ /*
+ * Check request.
+ */
+ AssertReturn( off < pThis->cbImage
+ || (off == pThis->cbImage && cbToRead == 0), VERR_EOF);
+
+ uint64_t cbLeftInImage = pThis->cbImage - off;
+ if (cbToRead >= cbLeftInImage)
+ cbToRead = cbLeftInImage; /* ASSUMES the caller can deal with this, given the pcbActuallyRead parameter... */
+
+ /*
+ * Work the I/O context using vdIfIoIntIoCtxSegArrayCreate.
+ */
+ int rc = VINF_SUCCESS;
+ size_t cbActuallyRead = 0;
+ while (cbToRead > 0)
+ {
+ RTSGSEG Seg;
+ unsigned cSegs = 1;
+ size_t cbThisRead = vdIfIoIntIoCtxSegArrayCreate(pThis->pIfIo, pIoCtx, &Seg, &cSegs, cbToRead);
+ AssertBreakStmt(cbThisRead != 0, rc = VERR_INTERNAL_ERROR_2);
+ Assert(cbThisRead == Seg.cbSeg);
+
+ rc = RTVfsFileReadAt(pThis->hIsoFile, off, Seg.pvSeg, cbThisRead, NULL);
+ AssertRCBreak(rc);
+
+ /* advance. */
+ cbActuallyRead += cbThisRead;
+ off += cbThisRead;
+ cbToRead -= cbThisRead;
+ }
+
+ *pcbActuallyRead = cbActuallyRead;
+ return rc;
+}
+
+/**
+ * @interface_method_impl{VDIMAGEBACKEND,pfnWrite}
+ */
+static DECLCALLBACK(int) visoWrite(void *pBackendData, uint64_t uOffset, size_t cbToWrite,
+ PVDIOCTX pIoCtx, size_t *pcbWriteProcess, size_t *pcbPreRead,
+ size_t *pcbPostRead, unsigned fWrite)
+{
+ RT_NOREF(uOffset, cbToWrite, pIoCtx, pcbWriteProcess, pcbPreRead, pcbPostRead, fWrite);
+ PVISOIMAGE pThis = (PVISOIMAGE)pBackendData;
+ AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
+ AssertReturn(pThis->hIsoFile != NIL_RTVFSFILE, VERR_VD_NOT_OPENED);
+ LogFlowFunc(("pThis=%p off=%#RX64 pIoCtx=%p cbToWrite=%#zx pcbWriteProcess=%p pcbPreRead=%p pcbPostRead=%p -> VERR_VD_IMAGE_READ_ONLY\n",
+ pThis, uOffset, pIoCtx, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead));
+ return VERR_VD_IMAGE_READ_ONLY;
+}
+
+/**
+ * @interface_method_impl{VDIMAGEBACKEND,pfnFlush}
+ */
+static DECLCALLBACK(int) visoFlush(void *pBackendData, PVDIOCTX pIoCtx)
+{
+ PVISOIMAGE pThis = (PVISOIMAGE)pBackendData;
+ AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
+ AssertReturn(pThis->hIsoFile != NIL_RTVFSFILE, VERR_VD_NOT_OPENED);
+ RT_NOREF(pIoCtx);
+ return VINF_SUCCESS;
+}
+
+/**
+ * @interface_method_impl{VDIMAGEBACKEND,pfnGetVersion}
+ */
+static DECLCALLBACK(unsigned) visoGetVersion(void *pBackendData)
+{
+ PVISOIMAGE pThis = (PVISOIMAGE)pBackendData;
+ AssertPtrReturn(pThis, 0);
+ LogFlowFunc(("pThis=%#p -> 1\n", pThis));
+ return 1;
+}
+
+/**
+ * @interface_method_impl{VDIMAGEBACKEND,pfnGetFileSize}
+ */
+static DECLCALLBACK(uint64_t) visoGetFileSize(void *pBackendData)
+{
+ PVISOIMAGE pThis = (PVISOIMAGE)pBackendData;
+ AssertPtrReturn(pThis, 0);
+ LogFlowFunc(("pThis=%p -> %RX64 (%s)\n", pThis, pThis->cbImage, pThis->hIsoFile == NIL_RTVFSFILE ? "fake!" : "real"));
+ return pThis->cbImage;
+}
+
+/**
+ * @interface_method_impl{VDIMAGEBACKEND,pfnGetPCHSGeometry}
+ */
+static DECLCALLBACK(int) visoGetPCHSGeometry(void *pBackendData, PVDGEOMETRY pPCHSGeometry)
+{
+ PVISOIMAGE pThis = (PVISOIMAGE)pBackendData;
+ AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
+ LogFlowFunc(("pThis=%p pPCHSGeometry=%p -> VERR_NOT_SUPPORTED\n", pThis, pPCHSGeometry));
+ RT_NOREF(pPCHSGeometry);
+ return VERR_NOT_SUPPORTED;
+}
+
+/**
+ * @interface_method_impl{VDIMAGEBACKEND,pfnSetPCHSGeometry}
+ */
+static DECLCALLBACK(int) visoSetPCHSGeometry(void *pBackendData, PCVDGEOMETRY pPCHSGeometry)
+{
+ PVISOIMAGE pThis = (PVISOIMAGE)pBackendData;
+ AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
+ LogFlowFunc(("pThis=%p pPCHSGeometry=%p:{%u/%u/%u} -> VERR_VD_IMAGE_READ_ONLY\n",
+ pThis, pPCHSGeometry, pPCHSGeometry->cCylinders, pPCHSGeometry->cHeads, pPCHSGeometry->cSectors));
+ RT_NOREF(pPCHSGeometry);
+ return VERR_VD_IMAGE_READ_ONLY;
+}
+
+/**
+ * @interface_method_impl{VDIMAGEBACKEND,pfnGetLCHSGeometry}
+ */
+static DECLCALLBACK(int) visoGetLCHSGeometry(void *pBackendData, PVDGEOMETRY pLCHSGeometry)
+{
+ PVISOIMAGE pThis = (PVISOIMAGE)pBackendData;
+ AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
+ LogFlowFunc(("pThis=%p pLCHSGeometry=%p -> VERR_NOT_SUPPORTED\n", pThis, pLCHSGeometry));
+ RT_NOREF(pLCHSGeometry);
+ return VERR_NOT_SUPPORTED;
+}
+
+/**
+ * @interface_method_impl{VDIMAGEBACKEND,pfnSetLCHSGeometry}
+ */
+static DECLCALLBACK(int) visoSetLCHSGeometry(void *pBackendData, PCVDGEOMETRY pLCHSGeometry)
+{
+ PVISOIMAGE pThis = (PVISOIMAGE)pBackendData;
+ AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
+ LogFlowFunc(("pThis=%p pLCHSGeometry=%p:{%u/%u/%u} -> VERR_VD_IMAGE_READ_ONLY\n",
+ pThis, pLCHSGeometry, pLCHSGeometry->cCylinders, pLCHSGeometry->cHeads, pLCHSGeometry->cSectors));
+ RT_NOREF(pLCHSGeometry);
+ return VERR_VD_IMAGE_READ_ONLY;
+}
+
+/**
+ * @interface_method_impl{VDIMAGEBACKEND,pfnQueryRegions}
+ */
+static DECLCALLBACK(int) visoQueryRegions(void *pBackendData, PCVDREGIONLIST *ppRegionList)
+{
+ PVISOIMAGE pThis = (PVISOIMAGE)pBackendData;
+ *ppRegionList = NULL;
+ AssertPtrReturn(pThis, VERR_VD_NOT_OPENED);
+
+ *ppRegionList = &pThis->RegionList;
+ LogFlowFunc(("returns VINF_SUCCESS (one region: 0 LB %RX64; pThis=%p)\n", pThis->RegionList.aRegions[0].cbData, pThis));
+ return VINF_SUCCESS;
+}
+
+/**
+ * @interface_method_impl{VDIMAGEBACKEND,pfnRegionListRelease}
+ */
+static DECLCALLBACK(void) visoRegionListRelease(void *pBackendData, PCVDREGIONLIST pRegionList)
+{
+ /* Nothing to do here. Just assert the input to avoid unused parameter warnings. */
+ PVISOIMAGE pThis = (PVISOIMAGE)pBackendData;
+ LogFlowFunc(("pThis=%p pRegionList=%p\n", pThis, pRegionList));
+ AssertPtrReturnVoid(pThis);
+ AssertReturnVoid(pRegionList == &pThis->RegionList || pRegionList == 0);
+}
+
+/**
+ * @interface_method_impl{VDIMAGEBACKEND,pfnGetImageFlags}
+ */
+static DECLCALLBACK(unsigned) visoGetImageFlags(void *pBackendData)
+{
+ PVISOIMAGE pThis = (PVISOIMAGE)pBackendData;
+ LogFlowFunc(("pThis=%p -> VD_IMAGE_FLAGS_NONE\n", pThis));
+ AssertPtrReturn(pThis, VD_IMAGE_FLAGS_NONE);
+ return VD_IMAGE_FLAGS_NONE;
+}
+
+/**
+ * @interface_method_impl{VDIMAGEBACKEND,pfnGetOpenFlags}
+ */
+static DECLCALLBACK(unsigned) visoGetOpenFlags(void *pBackendData)
+{
+ LogFlowFunc(("pBackendData=%#p\n", pBackendData));
+ PVISOIMAGE pThis = (PVISOIMAGE)pBackendData;
+ AssertPtrReturn(pThis, 0);
+
+ LogFlowFunc(("returns %#x\n", pThis->fOpenFlags));
+ return pThis->fOpenFlags;
+}
+
+/**
+ * @interface_method_impl{VDIMAGEBACKEND,pfnSetOpenFlags}
+ */
+static DECLCALLBACK(int) visoSetOpenFlags(void *pBackendData, unsigned uOpenFlags)
+{
+ PVISOIMAGE pThis = (PVISOIMAGE)pBackendData;
+ LogFlowFunc(("pThis=%p fOpenFlags=%#x\n", pThis, uOpenFlags));
+
+ AssertPtrReturn(pThis, VERR_INVALID_POINTER);
+ uint32_t const fSupported = VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO
+ | VD_OPEN_FLAGS_ASYNC_IO | VD_OPEN_FLAGS_SHAREABLE
+ | VD_OPEN_FLAGS_SEQUENTIAL | VD_OPEN_FLAGS_SKIP_CONSISTENCY_CHECKS;
+ AssertMsgReturn(!(uOpenFlags & ~fSupported), ("fOpenFlags=%#x\n", uOpenFlags), VERR_INVALID_FLAGS);
+
+ /*
+ * Only react if we switch from VD_OPEN_FLAGS_INFO to non-VD_OPEN_FLAGS_INFO mode,
+ * becuase that means we need to open the image.
+ */
+ if ( (pThis->fOpenFlags & VD_OPEN_FLAGS_INFO)
+ && !(uOpenFlags & VD_OPEN_FLAGS_INFO)
+ && pThis->hIsoFile == NIL_RTVFSFILE)
+ {
+ int rc = visoOpenWorker(pThis);
+ if (RT_FAILURE(rc))
+ {
+ LogFlowFunc(("returns %Rrc\n", rc));
+ return rc;
+ }
+ }
+
+ /*
+ * Update the flags.
+ */
+ pThis->fOpenFlags &= ~fSupported;
+ pThis->fOpenFlags |= fSupported & uOpenFlags;
+ pThis->fOpenFlags |= VD_OPEN_FLAGS_READONLY;
+ if (pThis->hIsoFile != NIL_RTVFSFILE)
+ pThis->fOpenFlags &= ~VD_OPEN_FLAGS_INFO;
+
+ return VINF_SUCCESS;
+}
+
+#define uOpenFlags fOpenFlags /* sigh */
+
+/**
+ * @interface_method_impl{VDIMAGEBACKEND,pfnGetComment}
+ */
+VD_BACKEND_CALLBACK_GET_COMMENT_DEF_NOT_SUPPORTED(visoGetComment);
+
+/**
+ * @interface_method_impl{VDIMAGEBACKEND,pfnSetComment}
+ */
+VD_BACKEND_CALLBACK_SET_COMMENT_DEF_NOT_SUPPORTED(visoSetComment, PVISOIMAGE);
+
+/**
+ * @interface_method_impl{VDIMAGEBACKEND,pfnGetUuid}
+ */
+static DECLCALLBACK(int) visoGetUuid(void *pBackendData, PRTUUID pUuid)
+{
+ PVISOIMAGE pThis = (PVISOIMAGE)pBackendData;
+ *pUuid = pThis->Uuid;
+ LogFlowFunc(("returns VIF_SUCCESS (%RTuuid)\n", pUuid));
+ return VINF_SUCCESS;
+}
+
+/**
+ * @interface_method_impl{VDIMAGEBACKEND,pfnSetUuid}
+ */
+VD_BACKEND_CALLBACK_SET_UUID_DEF_NOT_SUPPORTED(visoSetUuid, PVISOIMAGE);
+
+/**
+ * @interface_method_impl{VDIMAGEBACKEND,pfnGetModificationUuid}
+ */
+VD_BACKEND_CALLBACK_GET_UUID_DEF_NOT_SUPPORTED(visoGetModificationUuid);
+
+/**
+ * @interface_method_impl{VDIMAGEBACKEND,pfnSetModificationUuid}
+ */
+VD_BACKEND_CALLBACK_SET_UUID_DEF_NOT_SUPPORTED(visoSetModificationUuid, PVISOIMAGE);
+
+/**
+ * @interface_method_impl{VDIMAGEBACKEND,pfnGetParentUuid}
+ */
+VD_BACKEND_CALLBACK_GET_UUID_DEF_NOT_SUPPORTED(visoGetParentUuid);
+
+/**
+ * @interface_method_impl{VDIMAGEBACKEND,pfnSetParentUuid}
+ */
+VD_BACKEND_CALLBACK_SET_UUID_DEF_NOT_SUPPORTED(visoSetParentUuid, PVISOIMAGE);
+
+/**
+ * @interface_method_impl{VDIMAGEBACKEND,pfnGetParentModificationUuid}
+ */
+VD_BACKEND_CALLBACK_GET_UUID_DEF_NOT_SUPPORTED(visoGetParentModificationUuid);
+
+/**
+ * @interface_method_impl{VDIMAGEBACKEND,pfnSetParentModificationUuid}
+ */
+VD_BACKEND_CALLBACK_SET_UUID_DEF_NOT_SUPPORTED(visoSetParentModificationUuid, PVISOIMAGE);
+
+#undef uOpenFlags
+
+/**
+ * @interface_method_impl{VDIMAGEBACKEND,pfnDump}
+ */
+static DECLCALLBACK(void) visoDump(void *pBackendData)
+{
+ PVISOIMAGE pThis = (PVISOIMAGE)pBackendData;
+ AssertPtrReturnVoid(pThis);
+
+ vdIfErrorMessage(pThis->pIfError, "Dumping CUE image '%s' fOpenFlags=%x cbImage=%#RX64\n",
+ pThis->pszFilename, pThis->fOpenFlags, pThis->cbImage);
+}
+
+
+
+/**
+ * VBox ISO maker backend.
+ */
+const VDIMAGEBACKEND g_VBoxIsoMakerBackend =
+{
+ /* u32Version */
+ VD_IMGBACKEND_VERSION,
+ /* pszBackendName */
+ "VBoxIsoMaker",
+ /* uBackendCaps */
+ VD_CAP_FILE,
+ /* paFileExtensions */
+ g_aVBoXIsoMakerFileExtensions,
+ /* paConfigInfo */
+ s_aVisoConfigInfo,
+ /* pfnProbe */
+ visoProbe,
+ /* pfnOpen */
+ visoOpen,
+ /* pfnCreate */
+ NULL,
+ /* pfnRename */
+ NULL,
+ /* pfnClose */
+ visoClose,
+ /* pfnRead */
+ visoRead,
+ /* pfnWrite */
+ visoWrite,
+ /* pfnFlush */
+ visoFlush,
+ /* pfnDiscard */
+ NULL,
+ /* pfnGetVersion */
+ visoGetVersion,
+ /* pfnGetFileSize */
+ visoGetFileSize,
+ /* pfnGetPCHSGeometry */
+ visoGetPCHSGeometry,
+ /* pfnSetPCHSGeometry */
+ visoSetPCHSGeometry,
+ /* pfnGetLCHSGeometry */
+ visoGetLCHSGeometry,
+ /* pfnSetLCHSGeometry */
+ visoSetLCHSGeometry,
+ /* pfnQueryRegions */
+ visoQueryRegions,
+ /* pfnRegionListRelease */
+ visoRegionListRelease,
+ /* pfnGetImageFlags */
+ visoGetImageFlags,
+ /* pfnGetOpenFlags */
+ visoGetOpenFlags,
+ /* pfnSetOpenFlags */
+ visoSetOpenFlags,
+ /* pfnGetComment */
+ visoGetComment,
+ /* pfnSetComment */
+ visoSetComment,
+ /* pfnGetUuid */
+ visoGetUuid,
+ /* pfnSetUuid */
+ visoSetUuid,
+ /* pfnGetModificationUuid */
+ visoGetModificationUuid,
+ /* pfnSetModificationUuid */
+ visoSetModificationUuid,
+ /* pfnGetParentUuid */
+ visoGetParentUuid,
+ /* pfnSetParentUuid */
+ visoSetParentUuid,
+ /* pfnGetParentModificationUuid */
+ visoGetParentModificationUuid,
+ /* pfnSetParentModificationUuid */
+ visoSetParentModificationUuid,
+ /* pfnDump */
+ visoDump,
+ /* pfnGetTimestamp */
+ NULL,
+ /* pfnGetParentTimestamp */
+ NULL,
+ /* pfnSetParentTimestamp */
+ NULL,
+ /* pfnGetParentFilename */
+ NULL,
+ /* pfnSetParentFilename */
+ NULL,
+ /* pfnComposeLocation */
+ genericFileComposeLocation,
+ /* pfnComposeName */
+ genericFileComposeName,
+ /* pfnCompact */
+ NULL,
+ /* pfnResize */
+ NULL,
+ /* pfnRepair */
+ NULL,
+ /* pfnTraverseMetadata */
+ NULL,
+ /* u32VersionEnd */
+ VD_IMGBACKEND_VERSION
+};
+