summaryrefslogtreecommitdiffstats
path: root/src/VBox/Devices/Storage/DrvDiskIntegrity.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Devices/Storage/DrvDiskIntegrity.cpp')
-rw-r--r--src/VBox/Devices/Storage/DrvDiskIntegrity.cpp2160
1 files changed, 2160 insertions, 0 deletions
diff --git a/src/VBox/Devices/Storage/DrvDiskIntegrity.cpp b/src/VBox/Devices/Storage/DrvDiskIntegrity.cpp
new file mode 100644
index 00000000..fbe92dcd
--- /dev/null
+++ b/src/VBox/Devices/Storage/DrvDiskIntegrity.cpp
@@ -0,0 +1,2160 @@
+/* $Id: DrvDiskIntegrity.cpp $ */
+/** @file
+ * VBox storage devices: Disk integrity check.
+ */
+
+/*
+ * Copyright (C) 2006-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_DRV_DISK_INTEGRITY
+#include <VBox/vmm/pdmdrv.h>
+#include <VBox/vmm/pdmstorageifs.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+#include <iprt/uuid.h>
+#include <iprt/avl.h>
+#include <iprt/mem.h>
+#include <iprt/memcache.h>
+#include <iprt/message.h>
+#include <iprt/sg.h>
+#include <iprt/time.h>
+#include <iprt/tracelog.h>
+#include <iprt/semaphore.h>
+#include <iprt/asm.h>
+
+#include "VBoxDD.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+
+/**
+ * Transfer direction.
+ */
+typedef enum DRVDISKAIOTXDIR
+{
+ /** Invalid. */
+ DRVDISKAIOTXDIR_INVALID = 0,
+ /** Read */
+ DRVDISKAIOTXDIR_READ,
+ /** Write */
+ DRVDISKAIOTXDIR_WRITE,
+ /** Flush */
+ DRVDISKAIOTXDIR_FLUSH,
+ /** Discard */
+ DRVDISKAIOTXDIR_DISCARD,
+ /** Read after write for immediate verification. */
+ DRVDISKAIOTXDIR_READ_AFTER_WRITE
+} DRVDISKAIOTXDIR;
+
+/**
+ * async I/O request.
+ */
+typedef struct DRVDISKAIOREQ
+{
+ /** Transfer direction. */
+ DRVDISKAIOTXDIR enmTxDir;
+ /** Start offset. */
+ uint64_t off;
+ /** Transfer size. */
+ size_t cbTransfer;
+ /** Segment array. */
+ PCRTSGSEG paSeg;
+ /** Number of array entries. */
+ unsigned cSeg;
+ /** User argument */
+ void *pvUser;
+ /** Slot in the array. */
+ unsigned iSlot;
+ /** Start timestamp */
+ uint64_t tsStart;
+ /** Completion timestamp. */
+ uint64_t tsComplete;
+ /** Ranges to discard. */
+ PCRTRANGE paRanges;
+ /** Number of ranges. */
+ unsigned cRanges;
+ /** I/O segment for the extended media interface
+ * to hold the data. */
+ RTSGSEG IoSeg;
+} DRVDISKAIOREQ, *PDRVDISKAIOREQ;
+
+/**
+ * I/O log entry.
+ */
+typedef struct IOLOGENT
+{
+ /** Start offset */
+ uint64_t off;
+ /** Write size */
+ size_t cbWrite;
+ /** Number of references to this entry. */
+ unsigned cRefs;
+} IOLOGENT, *PIOLOGENT;
+
+/**
+ * Disk segment.
+ */
+typedef struct DRVDISKSEGMENT
+{
+ /** AVL core. */
+ AVLRFOFFNODECORE Core;
+ /** Size of the segment */
+ size_t cbSeg;
+ /** Data for this segment */
+ uint8_t *pbSeg;
+ /** Number of entries in the I/O array. */
+ unsigned cIoLogEntries;
+ /** Array of I/O log references. */
+ PIOLOGENT apIoLog[1];
+} DRVDISKSEGMENT, *PDRVDISKSEGMENT;
+
+/**
+ * Active requests list entry.
+ */
+typedef struct DRVDISKAIOREQACTIVE
+{
+ /** Pointer to the request. */
+ volatile PDRVDISKAIOREQ pIoReq;
+ /** Start timestamp. */
+ uint64_t tsStart;
+} DRVDISKAIOREQACTIVE, *PDRVDISKAIOREQACTIVE;
+
+/**
+ * Disk integrity driver instance data.
+ *
+ * @implements PDMIMEDIA
+ * @implements PDMIMEDIAPORT
+ * @implements PDMIMEDIAEX
+ * @implements PDMIMEDIAEXPORT
+ * @implements PDMIMEDIAMOUNT
+ * @implements PDMIMEDIAMOUNTNOTIFY
+ */
+typedef struct DRVDISKINTEGRITY
+{
+ /** Pointer driver instance. */
+ PPDMDRVINS pDrvIns;
+ /** Pointer to the media driver below us.
+ * This is NULL if the media is not mounted. */
+ PPDMIMEDIA pDrvMedia;
+ /** Our media interface */
+ PDMIMEDIA IMedia;
+
+ /** The media port interface above. */
+ PPDMIMEDIAPORT pDrvMediaPort;
+ /** Media port interface */
+ PDMIMEDIAPORT IMediaPort;
+
+ /** The extended media port interface above. */
+ PPDMIMEDIAEXPORT pDrvMediaExPort;
+ /** Our extended media port interface */
+ PDMIMEDIAEXPORT IMediaExPort;
+
+ /** The extended media interface below. */
+ PPDMIMEDIAEX pDrvMediaEx;
+ /** Our extended media interface */
+ PDMIMEDIAEX IMediaEx;
+
+ /** The mount interface below. */
+ PPDMIMOUNT pDrvMount;
+ /** Our mount interface */
+ PDMIMOUNT IMount;
+
+ /** The mount notify interface above. */
+ PPDMIMOUNTNOTIFY pDrvMountNotify;
+ /** Our mount notify interface. */
+ PDMIMOUNTNOTIFY IMountNotify;
+
+ /** Flag whether consistency checks are enabled. */
+ bool fCheckConsistency;
+ /** Flag whether the RAM disk was prepopulated. */
+ bool fPrepopulateRamDisk;
+ /** AVL tree containing the disk blocks to check. */
+ PAVLRFOFFTREE pTreeSegments;
+
+ /** Flag whether async request tracing is enabled. */
+ bool fTraceRequests;
+ /** Interval the thread should check for expired requests (milliseconds). */
+ uint32_t uCheckIntervalMs;
+ /** Expire timeout for a request (milliseconds). */
+ uint32_t uExpireIntervalMs;
+ /** Thread which checks for lost requests. */
+ RTTHREAD hThread;
+ /** Event semaphore */
+ RTSEMEVENT SemEvent;
+ /** Flag whether the thread should run. */
+ bool fRunning;
+ /** Array containing active requests. */
+ DRVDISKAIOREQACTIVE apReqActive[128];
+ /** Next free slot in the array */
+ volatile unsigned iNextFreeSlot;
+ /** Request cache. */
+ RTMEMCACHE hReqCache;
+
+ /** Flag whether we check for requests completing twice. */
+ bool fCheckDoubleCompletion;
+ /** Number of requests we go back. */
+ unsigned cEntries;
+ /** Array of completed but still observed requests. */
+ PDRVDISKAIOREQ *papIoReq;
+ /** Current entry in the array. */
+ unsigned iEntry;
+
+ /** Flag whether to do a immediate read after write for verification. */
+ bool fReadAfterWrite;
+ /** Flag whether to record the data to write before the write completed successfully.
+ * Useful in case the data is modified in place later on (encryption for instance). */
+ bool fRecordWriteBeforeCompletion;
+ /** Flag whether to validate memory buffers when the extended media interface is used. */
+ bool fValidateMemBufs;
+
+ /** I/O logger to use if enabled. */
+ RTTRACELOGWR hIoLogger;
+ /** Size of the opaque handle until our tracking structure starts in bytes. */
+ size_t cbIoReqOpaque;
+} DRVDISKINTEGRITY, *PDRVDISKINTEGRITY;
+
+
+/**
+ * Read/Write event items.
+ */
+static const RTTRACELOGEVTITEMDESC g_aEvtItemsReadWrite[] =
+{
+ { "Async", "Flag whether the request is asynchronous", RTTRACELOGTYPE_BOOL, 0 },
+ { "Offset", "Offset to start reading/writing from/to", RTTRACELOGTYPE_UINT64, 0 },
+ { "Size", "Number of bytes to transfer", RTTRACELOGTYPE_SIZE, 0 }
+};
+
+/**
+ * Flush event items.
+ */
+static const RTTRACELOGEVTITEMDESC g_aEvtItemsFlush[] =
+{
+ { "Async", "Flag whether the request is asynchronous", RTTRACELOGTYPE_BOOL, 0 }
+};
+
+/**
+ * I/O request complete items.
+ */
+static const RTTRACELOGEVTITEMDESC g_aEvtItemsComplete[] =
+{
+ { "Status", "Status code the request completed with", RTTRACELOGTYPE_INT32, 0 }
+};
+
+/** Read event descriptor. */
+static const RTTRACELOGEVTDESC g_EvtRead =
+ { "Read", "Read data from disk", RTTRACELOGEVTSEVERITY_DEBUG, RT_ELEMENTS(g_aEvtItemsReadWrite), &g_aEvtItemsReadWrite[0] };
+/** Write event descriptor. */
+static const RTTRACELOGEVTDESC g_EvtWrite =
+ { "Write", "Write data to disk", RTTRACELOGEVTSEVERITY_DEBUG, RT_ELEMENTS(g_aEvtItemsReadWrite), &g_aEvtItemsReadWrite[0] };
+/** Flush event descriptor. */
+static const RTTRACELOGEVTDESC g_EvtFlush =
+ { "Flush", "Flush written data to disk", RTTRACELOGEVTSEVERITY_DEBUG, RT_ELEMENTS(g_aEvtItemsFlush), &g_aEvtItemsFlush[0] };
+/** I/O request complete event descriptor. */
+static const RTTRACELOGEVTDESC g_EvtComplete =
+ { "Complete", "A previously started I/O request completed", RTTRACELOGEVTSEVERITY_DEBUG,
+ RT_ELEMENTS(g_aEvtItemsComplete), &g_aEvtItemsComplete[0]};
+
+#define DISKINTEGRITY_IOREQ_HANDLE_2_DRVDISKAIOREQ(a_pThis, a_hIoReq) ((*(PDRVDISKAIOREQ *)((uintptr_t)(a_hIoReq) + (a_pThis)->cbIoReqOpaque)))
+#define DISKINTEGRITY_IOREQ_HANDLE_2_UPPER_OPAQUE(a_pThis, a_hIoReq) ((void *)((uintptr_t)(a_hIoReq) + (a_pThis)->cbIoReqOpaque + sizeof(PDRVDISKAIOREQ)))
+#define DISKINTEGRITY_IOREQ_ALLOC_2_DRVDISKAIOREQ(a_pvIoReqAlloc) (*(PDRVDISKAIOREQ *)(a_pvIoReqAlloc))
+#define DISKINTEGRITY_IOREQ_ALLOC_2_UPPER(a_pvIoReqAlloc) ((void *)((uintptr_t)(a_pvIoReqAlloc) + sizeof(PDRVDISKAIOREQ)))
+
+static void drvdiskintIoReqCheckForDoubleCompletion(PDRVDISKINTEGRITY pThis, PDRVDISKAIOREQ pIoReq,
+ bool fMediaEx)
+{
+ /* Search if the I/O request completed already. */
+ for (unsigned i = 0; i < pThis->cEntries; i++)
+ {
+ if (RT_UNLIKELY(pThis->papIoReq[i] == pIoReq))
+ {
+ RTMsgError("Request %#p completed already!\n", pIoReq);
+ if (!fMediaEx)
+ RTMsgError("Start timestamp %llu Completion timestamp %llu (completed after %llu ms)\n",
+ pIoReq->tsStart, pIoReq->tsComplete, pIoReq->tsComplete - pIoReq->tsStart);
+ RTAssertDebugBreak();
+ }
+ }
+
+ pIoReq->tsComplete = RTTimeSystemMilliTS();
+ Assert(!pThis->papIoReq[pThis->iEntry]);
+ pThis->papIoReq[pThis->iEntry] = pIoReq;
+
+ pThis->iEntry = (pThis->iEntry+1) % pThis->cEntries;
+ if (pThis->papIoReq[pThis->iEntry])
+ {
+ if (!fMediaEx)
+ RTMemFree(pThis->papIoReq[pThis->iEntry]);
+ pThis->papIoReq[pThis->iEntry] = NULL;
+ }
+}
+
+static void drvdiskintIoLogEntryRelease(PIOLOGENT pIoLogEnt)
+{
+ pIoLogEnt->cRefs--;
+ if (!pIoLogEnt->cRefs)
+ RTMemFree(pIoLogEnt);
+}
+
+/**
+ * Record a successful write to the virtual disk.
+ *
+ * @returns VBox status code.
+ * @param pThis Disk integrity driver instance data.
+ * @param paSeg Segment array of the write to record.
+ * @param cSeg Number of segments.
+ * @param off Start offset.
+ * @param cbWrite Number of bytes to record.
+ */
+static int drvdiskintWriteRecord(PDRVDISKINTEGRITY pThis, PCRTSGSEG paSeg, unsigned cSeg,
+ uint64_t off, size_t cbWrite)
+{
+ int rc = VINF_SUCCESS;
+
+ LogFlowFunc(("pThis=%#p paSeg=%#p cSeg=%u off=%llx cbWrite=%u\n",
+ pThis, paSeg, cSeg, off, cbWrite));
+
+ /* Update the segments */
+ size_t cbLeft = cbWrite;
+ RTFOFF offCurr = (RTFOFF)off;
+ RTSGBUF SgBuf;
+ PIOLOGENT pIoLogEnt = (PIOLOGENT)RTMemAllocZ(sizeof(IOLOGENT));
+ if (!pIoLogEnt)
+ return VERR_NO_MEMORY;
+
+ pIoLogEnt->off = off;
+ pIoLogEnt->cbWrite = cbWrite;
+ pIoLogEnt->cRefs = 0;
+
+ RTSgBufInit(&SgBuf, paSeg, cSeg);
+
+ while (cbLeft)
+ {
+ PDRVDISKSEGMENT pSeg = (PDRVDISKSEGMENT)RTAvlrFileOffsetRangeGet(pThis->pTreeSegments, offCurr);
+ size_t cbRange = 0;
+ bool fSet = false;
+ unsigned offSeg = 0;
+
+ if (!pSeg)
+ {
+ /* Get next segment */
+ pSeg = (PDRVDISKSEGMENT)RTAvlrFileOffsetGetBestFit(pThis->pTreeSegments, offCurr, true);
+ if ( !pSeg
+ || offCurr + (RTFOFF)cbLeft <= pSeg->Core.Key)
+ cbRange = cbLeft;
+ else
+ cbRange = pSeg->Core.Key - offCurr;
+
+ Assert(cbRange % 512 == 0);
+
+ /* Create new segment */
+ pSeg = (PDRVDISKSEGMENT)RTMemAllocZ(RT_UOFFSETOF_DYN(DRVDISKSEGMENT, apIoLog[cbRange / 512]));
+ if (pSeg)
+ {
+ pSeg->Core.Key = offCurr;
+ pSeg->Core.KeyLast = offCurr + (RTFOFF)cbRange - 1;
+ pSeg->cbSeg = cbRange;
+ pSeg->pbSeg = (uint8_t *)RTMemAllocZ(cbRange);
+ pSeg->cIoLogEntries = (uint32_t)cbRange / 512;
+ if (!pSeg->pbSeg)
+ RTMemFree(pSeg);
+ else
+ {
+ bool fInserted = RTAvlrFileOffsetInsert(pThis->pTreeSegments, &pSeg->Core);
+ AssertMsg(fInserted, ("Bug!\n")); RT_NOREF(fInserted);
+ fSet = true;
+ }
+ }
+ }
+ else
+ {
+ fSet = true;
+ offSeg = offCurr - pSeg->Core.Key;
+ cbRange = RT_MIN(cbLeft, (size_t)(pSeg->Core.KeyLast + 1 - offCurr));
+ }
+
+ if (fSet)
+ {
+ AssertPtr(pSeg);
+ size_t cbCopied = RTSgBufCopyToBuf(&SgBuf, pSeg->pbSeg + offSeg, cbRange);
+ Assert(cbCopied == cbRange); RT_NOREF(cbCopied);
+
+ /* Update the I/O log pointers */
+ Assert(offSeg % 512 == 0);
+ Assert(cbRange % 512 == 0);
+ while (offSeg < cbRange)
+ {
+ uint32_t uSector = offSeg / 512;
+ PIOLOGENT pIoLogOld = NULL;
+
+ AssertMsg(uSector < pSeg->cIoLogEntries, ("Internal bug!\n"));
+
+ pIoLogOld = pSeg->apIoLog[uSector];
+ if (pIoLogOld)
+ {
+ pIoLogOld->cRefs--;
+ if (!pIoLogOld->cRefs)
+ RTMemFree(pIoLogOld);
+ }
+
+ pSeg->apIoLog[uSector] = pIoLogEnt;
+ pIoLogEnt->cRefs++;
+
+ offSeg += 512;
+ }
+ }
+ else
+ RTSgBufAdvance(&SgBuf, cbRange);
+
+ offCurr += cbRange;
+ cbLeft -= cbRange;
+ }
+
+ return rc;
+}
+
+/**
+ * Verifies a read request.
+ *
+ * @returns VBox status code.
+ * @param pThis Disk integrity driver instance data.
+ * @param paSeg Segment array of the containing the data buffers to verify.
+ * @param cSeg Number of segments.
+ * @param off Start offset.
+ * @param cbRead Number of bytes to verify.
+ */
+static int drvdiskintReadVerify(PDRVDISKINTEGRITY pThis, PCRTSGSEG paSeg, unsigned cSeg,
+ uint64_t off, size_t cbRead)
+{
+ int rc = VINF_SUCCESS;
+
+ LogFlowFunc(("pThis=%#p paSeg=%#p cSeg=%u off=%llx cbRead=%u\n",
+ pThis, paSeg, cSeg, off, cbRead));
+
+ Assert(off % 512 == 0);
+ Assert(cbRead % 512 == 0);
+
+ /* Compare read data */
+ size_t cbLeft = cbRead;
+ RTFOFF offCurr = (RTFOFF)off;
+ RTSGBUF SgBuf;
+
+ RTSgBufInit(&SgBuf, paSeg, cSeg);
+
+ while (cbLeft)
+ {
+ PDRVDISKSEGMENT pSeg = (PDRVDISKSEGMENT)RTAvlrFileOffsetRangeGet(pThis->pTreeSegments, offCurr);
+ size_t cbRange = 0;
+ bool fCmp = false;
+ unsigned offSeg = 0;
+
+ if (!pSeg)
+ {
+ /* Get next segment */
+ pSeg = (PDRVDISKSEGMENT)RTAvlrFileOffsetGetBestFit(pThis->pTreeSegments, offCurr, true);
+ if (!pSeg)
+ {
+ /* No data in the tree for this read. Assume everything is ok. */
+ cbRange = cbLeft;
+ }
+ else if (offCurr + (RTFOFF)cbLeft <= pSeg->Core.Key)
+ cbRange = cbLeft;
+ else
+ cbRange = pSeg->Core.Key - offCurr;
+
+ if (pThis->fPrepopulateRamDisk)
+ {
+ /* No segment means everything should be 0 for this part. */
+ if (!RTSgBufIsZero(&SgBuf, cbRange))
+ {
+ RTMsgError("Corrupted disk at offset %llu (expected everything to be 0)!\n",
+ offCurr);
+ RTAssertDebugBreak();
+ }
+ }
+ }
+ else
+ {
+ fCmp = true;
+ offSeg = offCurr - pSeg->Core.Key;
+ cbRange = RT_MIN(cbLeft, (size_t)(pSeg->Core.KeyLast + 1 - offCurr));
+ }
+
+ if (fCmp)
+ {
+ RTSGSEG Seg;
+ RTSGBUF SgBufCmp;
+ size_t cbOff = 0;
+
+ Seg.cbSeg = cbRange;
+ Seg.pvSeg = pSeg->pbSeg + offSeg;
+
+ RTSgBufInit(&SgBufCmp, &Seg, 1);
+ if (RTSgBufCmpEx(&SgBuf, &SgBufCmp, cbRange, &cbOff, true))
+ {
+ /* Corrupted disk, print I/O log entry of the last write which accessed this range. */
+ uint32_t cSector = (offSeg + (uint32_t)cbOff) / 512;
+ AssertMsg(cSector < pSeg->cIoLogEntries, ("Internal bug!\n"));
+
+ RTMsgError("Corrupted disk at offset %llu (%u bytes in the current read buffer)!\n",
+ offCurr + cbOff, cbOff);
+ RTMsgError("Last write to this sector started at offset %llu with %u bytes (%u references to this log entry)\n",
+ pSeg->apIoLog[cSector]->off,
+ pSeg->apIoLog[cSector]->cbWrite,
+ pSeg->apIoLog[cSector]->cRefs);
+ RTAssertDebugBreak();
+ }
+ }
+ else
+ RTSgBufAdvance(&SgBuf, cbRange);
+
+ offCurr += cbRange;
+ cbLeft -= cbRange;
+ }
+
+ return rc;
+}
+
+/**
+ * Discards the given ranges from the disk.
+ *
+ * @returns VBox status code.
+ * @param pThis Disk integrity driver instance data.
+ * @param paRanges Array of ranges to discard.
+ * @param cRanges Number of ranges in the array.
+ */
+static int drvdiskintDiscardRecords(PDRVDISKINTEGRITY pThis, PCRTRANGE paRanges, unsigned cRanges)
+{
+ int rc = VINF_SUCCESS;
+
+ LogFlowFunc(("pThis=%#p paRanges=%#p cRanges=%u\n", pThis, paRanges, cRanges));
+
+ for (unsigned i = 0; i < cRanges; i++)
+ {
+ uint64_t offStart = paRanges[i].offStart;
+ size_t cbLeft = paRanges[i].cbRange;
+
+ LogFlowFunc(("Discarding off=%llu cbRange=%zu\n", offStart, cbLeft));
+
+ while (cbLeft)
+ {
+ size_t cbRange;
+ PDRVDISKSEGMENT pSeg = (PDRVDISKSEGMENT)RTAvlrFileOffsetRangeGet(pThis->pTreeSegments, offStart);
+
+ if (!pSeg)
+ {
+ /* Get next segment */
+ pSeg = (PDRVDISKSEGMENT)RTAvlrFileOffsetGetBestFit(pThis->pTreeSegments, offStart, true);
+ if ( !pSeg
+ || (RTFOFF)offStart + (RTFOFF)cbLeft <= pSeg->Core.Key)
+ cbRange = cbLeft;
+ else
+ cbRange = pSeg->Core.Key - offStart;
+
+ Assert(!(cbRange % 512));
+ }
+ else
+ {
+ size_t cbPreLeft, cbPostLeft;
+
+ cbRange = RT_MIN(cbLeft, pSeg->Core.KeyLast - offStart + 1);
+ cbPreLeft = offStart - pSeg->Core.Key;
+ cbPostLeft = pSeg->cbSeg - cbRange - cbPreLeft;
+
+ Assert(!(cbRange % 512));
+ Assert(!(cbPreLeft % 512));
+ Assert(!(cbPostLeft % 512));
+
+ LogFlowFunc(("cbRange=%zu cbPreLeft=%zu cbPostLeft=%zu\n",
+ cbRange, cbPreLeft, cbPostLeft));
+
+ RTAvlrFileOffsetRemove(pThis->pTreeSegments, pSeg->Core.Key);
+
+ if (!cbPreLeft && !cbPostLeft)
+ {
+ /* Just free the whole segment. */
+ LogFlowFunc(("Freeing whole segment pSeg=%#p\n", pSeg));
+ RTMemFree(pSeg->pbSeg);
+ for (unsigned idx = 0; idx < pSeg->cIoLogEntries; idx++)
+ drvdiskintIoLogEntryRelease(pSeg->apIoLog[idx]);
+ RTMemFree(pSeg);
+ }
+ else if (cbPreLeft && !cbPostLeft)
+ {
+ /* Realloc to new size and insert. */
+ LogFlowFunc(("Realloc segment pSeg=%#p\n", pSeg));
+ pSeg->pbSeg = (uint8_t *)RTMemRealloc(pSeg->pbSeg, cbPreLeft);
+ for (unsigned idx = (uint32_t)(cbPreLeft / 512); idx < pSeg->cIoLogEntries; idx++)
+ drvdiskintIoLogEntryRelease(pSeg->apIoLog[idx]);
+ pSeg = (PDRVDISKSEGMENT)RTMemRealloc(pSeg, RT_UOFFSETOF_DYN(DRVDISKSEGMENT, apIoLog[cbPreLeft / 512]));
+ pSeg->Core.KeyLast = pSeg->Core.Key + cbPreLeft - 1;
+ pSeg->cbSeg = cbPreLeft;
+ pSeg->cIoLogEntries = (uint32_t)(cbPreLeft / 512);
+ bool fInserted = RTAvlrFileOffsetInsert(pThis->pTreeSegments, &pSeg->Core);
+ Assert(fInserted); RT_NOREF(fInserted);
+ }
+ else if (!cbPreLeft && cbPostLeft)
+ {
+ /* Move data to the front and realloc. */
+ LogFlowFunc(("Move data and realloc segment pSeg=%#p\n", pSeg));
+ memmove(pSeg->pbSeg, pSeg->pbSeg + cbRange, cbPostLeft);
+ for (unsigned idx = 0; idx < cbRange / 512; idx++)
+ drvdiskintIoLogEntryRelease(pSeg->apIoLog[idx]);
+ for (unsigned idx = 0; idx < cbPostLeft /512; idx++)
+ pSeg->apIoLog[idx] = pSeg->apIoLog[(cbRange / 512) + idx];
+ pSeg = (PDRVDISKSEGMENT)RTMemRealloc(pSeg, RT_UOFFSETOF_DYN(DRVDISKSEGMENT, apIoLog[cbPostLeft / 512]));
+ pSeg->pbSeg = (uint8_t *)RTMemRealloc(pSeg->pbSeg, cbPostLeft);
+ pSeg->Core.Key += cbRange;
+ pSeg->cbSeg = cbPostLeft;
+ pSeg->cIoLogEntries = (uint32_t)(cbPostLeft / 512);
+ bool fInserted = RTAvlrFileOffsetInsert(pThis->pTreeSegments, &pSeg->Core);
+ Assert(fInserted); RT_NOREF(fInserted);
+ }
+ else
+ {
+ /* Split the segment into 2 new segments. */
+ LogFlowFunc(("Split segment pSeg=%#p\n", pSeg));
+ PDRVDISKSEGMENT pSegPost = (PDRVDISKSEGMENT)RTMemAllocZ(RT_UOFFSETOF_DYN(DRVDISKSEGMENT, apIoLog[cbPostLeft / 512]));
+ if (pSegPost)
+ {
+ pSegPost->Core.Key = pSeg->Core.Key + cbPreLeft + cbRange;
+ pSegPost->Core.KeyLast = pSeg->Core.KeyLast;
+ pSegPost->cbSeg = cbPostLeft;
+ pSegPost->pbSeg = (uint8_t *)RTMemAllocZ(cbPostLeft);
+ pSegPost->cIoLogEntries = (uint32_t)(cbPostLeft / 512);
+ if (!pSegPost->pbSeg)
+ RTMemFree(pSegPost);
+ else
+ {
+ memcpy(pSegPost->pbSeg, pSeg->pbSeg + cbPreLeft + cbRange, cbPostLeft);
+ for (unsigned idx = 0; idx < (uint32_t)(cbPostLeft / 512); idx++)
+ pSegPost->apIoLog[idx] = pSeg->apIoLog[((cbPreLeft + cbRange) / 512) + idx];
+
+ bool fInserted = RTAvlrFileOffsetInsert(pThis->pTreeSegments, &pSegPost->Core);
+ Assert(fInserted); RT_NOREF(fInserted);
+ }
+ }
+
+ /* Shrink the current segment. */
+ pSeg->pbSeg = (uint8_t *)RTMemRealloc(pSeg->pbSeg, cbPreLeft);
+ for (unsigned idx = (uint32_t)(cbPreLeft / 512); idx < (uint32_t)((cbPreLeft + cbRange) / 512); idx++)
+ drvdiskintIoLogEntryRelease(pSeg->apIoLog[idx]);
+ pSeg = (PDRVDISKSEGMENT)RTMemRealloc(pSeg, RT_UOFFSETOF_DYN(DRVDISKSEGMENT, apIoLog[cbPreLeft / 512]));
+ pSeg->Core.KeyLast = pSeg->Core.Key + cbPreLeft - 1;
+ pSeg->cbSeg = cbPreLeft;
+ pSeg->cIoLogEntries = (uint32_t)(cbPreLeft / 512);
+ bool fInserted = RTAvlrFileOffsetInsert(pThis->pTreeSegments, &pSeg->Core);
+ Assert(fInserted); RT_NOREF(fInserted);
+ } /* if (cbPreLeft && cbPostLeft) */
+ }
+
+ offStart += cbRange;
+ cbLeft -= cbRange;
+ }
+ }
+
+ LogFlowFunc(("returns rc=%Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * Adds a request to the active list.
+ *
+ * @param pThis The driver instance data.
+ * @param pIoReq The request to add.
+ */
+static void drvdiskintIoReqAdd(PDRVDISKINTEGRITY pThis, PDRVDISKAIOREQ pIoReq)
+{
+ PDRVDISKAIOREQACTIVE pReqActive = &pThis->apReqActive[pThis->iNextFreeSlot];
+
+ Assert(!pReqActive->pIoReq);
+ pReqActive->tsStart = pIoReq->tsStart;
+ pReqActive->pIoReq = pIoReq;
+ pIoReq->iSlot = pThis->iNextFreeSlot;
+
+ /* Search for the next one. */
+ while (pThis->apReqActive[pThis->iNextFreeSlot].pIoReq)
+ pThis->iNextFreeSlot = (pThis->iNextFreeSlot+1) % RT_ELEMENTS(pThis->apReqActive);
+}
+
+/**
+ * Removes a request from the active list.
+ *
+ * @param pThis The driver instance data.
+ * @param pIoReq The request to remove.
+ */
+static void drvdiskintIoReqRemove(PDRVDISKINTEGRITY pThis, PDRVDISKAIOREQ pIoReq)
+{
+ PDRVDISKAIOREQACTIVE pReqActive = &pThis->apReqActive[pIoReq->iSlot];
+
+ Assert(pReqActive->pIoReq == pIoReq);
+
+ ASMAtomicWriteNullPtr(&pReqActive->pIoReq);
+}
+
+/**
+ * Thread checking for expired requests.
+ *
+ * @returns IPRT status code.
+ * @param pThread Thread handle.
+ * @param pvUser Opaque user data.
+ */
+static DECLCALLBACK(int) drvdiskIntIoReqExpiredCheck(RTTHREAD pThread, void *pvUser)
+{
+ PDRVDISKINTEGRITY pThis = (PDRVDISKINTEGRITY)pvUser;
+
+ RT_NOREF(pThread);
+
+ while (pThis->fRunning)
+ {
+ int rc = RTSemEventWait(pThis->SemEvent, pThis->uCheckIntervalMs);
+
+ if (!pThis->fRunning)
+ break;
+
+ Assert(rc == VERR_TIMEOUT); RT_NOREF(rc);
+
+ /* Get current timestamp for comparison. */
+ uint64_t tsCurr = RTTimeSystemMilliTS();
+
+ /* Go through the array and check for expired requests. */
+ for (unsigned i = 0; i < RT_ELEMENTS(pThis->apReqActive); i++)
+ {
+ PDRVDISKAIOREQACTIVE pReqActive = &pThis->apReqActive[i];
+ PDRVDISKAIOREQ pIoReq = ASMAtomicReadPtrT(&pReqActive->pIoReq, PDRVDISKAIOREQ);
+
+ if ( pIoReq
+ && (tsCurr > pReqActive->tsStart)
+ && (tsCurr - pReqActive->tsStart) >= pThis->uExpireIntervalMs)
+ {
+ RTMsgError("Request %#p expired (active for %llu ms already)\n",
+ pIoReq, tsCurr - pReqActive->tsStart);
+ RTAssertDebugBreak();
+ }
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Verify a completed read after write request.
+ *
+ * @returns VBox status code.
+ * @param pThis The driver instance data.
+ * @param pIoReq The request to be verified.
+ */
+static int drvdiskintReadAfterWriteVerify(PDRVDISKINTEGRITY pThis, PDRVDISKAIOREQ pIoReq)
+{
+ int rc = VINF_SUCCESS;
+
+ if (pThis->fCheckConsistency)
+ rc = drvdiskintReadVerify(pThis, pIoReq->paSeg, pIoReq->cSeg, pIoReq->off, pIoReq->cbTransfer);
+ else /** @todo Implement read after write verification without a memory based image of the disk. */
+ AssertMsgFailed(("TODO\n"));
+
+ return rc;
+}
+
+
+/**
+ * Fires a read event if enabled.
+ *
+ * @param pThis The driver instance data.
+ * @param uGrp The group ID.
+ * @param fAsync Flag whether this is an async request.
+ * @param off The offset to put into the event log.
+ * @param cbRead Amount of bytes to read.
+ */
+DECLINLINE(void) drvdiskintTraceLogFireEvtRead(PDRVDISKINTEGRITY pThis, uintptr_t uGrp, bool fAsync, uint64_t off, size_t cbRead)
+{
+ if (pThis->hIoLogger)
+ {
+ int rc = RTTraceLogWrEvtAddL(pThis->hIoLogger, &g_EvtRead, RTTRACELOG_WR_ADD_EVT_F_GRP_START,
+ (RTTRACELOGEVTGRPID)uGrp, 0, fAsync, off, cbRead);
+ AssertRC(rc);
+ }
+}
+
+
+/**
+ * Fires a write event if enabled.
+ *
+ * @param pThis The driver instance data.
+ * @param uGrp The group ID.
+ * @param fAsync Flag whether this is an async request.
+ * @param off The offset to put into the event log.
+ * @param cbWrite Amount of bytes to write.
+ */
+DECLINLINE(void) drvdiskintTraceLogFireEvtWrite(PDRVDISKINTEGRITY pThis, uintptr_t uGrp, bool fAsync, uint64_t off, size_t cbWrite)
+{
+ if (pThis->hIoLogger)
+ {
+ int rc = RTTraceLogWrEvtAddL(pThis->hIoLogger, &g_EvtWrite, RTTRACELOG_WR_ADD_EVT_F_GRP_START,
+ (RTTRACELOGEVTGRPID)uGrp, 0, fAsync, off, cbWrite);
+ AssertRC(rc);
+ }
+}
+
+
+/**
+ * Fires a flush event if enabled.
+ *
+ * @param pThis The driver instance data.
+ * @param uGrp The group ID.
+ * @param fAsync Flag whether this is an async request.
+ */
+DECLINLINE(void) drvdiskintTraceLogFireEvtFlush(PDRVDISKINTEGRITY pThis, uintptr_t uGrp, bool fAsync)
+{
+ if (pThis->hIoLogger)
+ {
+ int rc = RTTraceLogWrEvtAddL(pThis->hIoLogger, &g_EvtFlush, RTTRACELOG_WR_ADD_EVT_F_GRP_START,
+ (RTTRACELOGEVTGRPID)uGrp, 0, fAsync);
+ AssertRC(rc);
+ }
+}
+
+
+/**
+ * Fires a request complete event if enabled.
+ *
+ * @param pThis The driver instance data.
+ * @param uGrp The group ID.
+ * @param rcReq Status code the request completed with.
+ * @param pSgBuf The S/G buffer holding the data.
+ */
+DECLINLINE(void) drvdiskintTraceLogFireEvtComplete(PDRVDISKINTEGRITY pThis, uintptr_t uGrp, int rcReq, PRTSGBUF pSgBuf)
+{
+ RT_NOREF(pSgBuf);
+
+ if (pThis->hIoLogger)
+ {
+ int rc = RTTraceLogWrEvtAddL(pThis->hIoLogger, &g_EvtComplete, RTTRACELOG_WR_ADD_EVT_F_GRP_FINISH,
+ (RTTRACELOGEVTGRPID)uGrp, 0, rcReq);
+ AssertRC(rc);
+ }
+}
+
+
+/* -=-=-=-=- IMedia -=-=-=-=- */
+
+/** Makes a PDRVDISKINTEGRITY out of a PPDMIMEDIA. */
+#define PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface) ( (PDRVDISKINTEGRITY)((uintptr_t)pInterface - RT_UOFFSETOF(DRVDISKINTEGRITY, IMedia)) )
+
+
+/*********************************************************************************************************************************
+* Media interface methods *
+*********************************************************************************************************************************/
+
+
+/** @interface_method_impl{PDMIMEDIA,pfnRead} */
+static DECLCALLBACK(int) drvdiskintRead(PPDMIMEDIA pInterface,
+ uint64_t off, void *pvBuf, size_t cbRead)
+{
+ int rc = VINF_SUCCESS;
+ PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
+
+ drvdiskintTraceLogFireEvtRead(pThis, (uintptr_t)pvBuf, false /* fAsync */, off, cbRead);
+ rc = pThis->pDrvMedia->pfnRead(pThis->pDrvMedia, off, pvBuf, cbRead);
+
+ if (pThis->hIoLogger)
+ {
+ RTSGSEG Seg;
+ RTSGBUF SgBuf;
+
+ Seg.pvSeg = pvBuf;
+ Seg.cbSeg = cbRead;
+ RTSgBufInit(&SgBuf, &Seg, 1);
+ drvdiskintTraceLogFireEvtComplete(pThis, (uintptr_t)pvBuf, rc, &SgBuf);
+ }
+
+ if (RT_FAILURE(rc))
+ return rc;
+
+ if (pThis->fCheckConsistency)
+ {
+ /* Verify the read. */
+ RTSGSEG Seg;
+ Seg.cbSeg = cbRead;
+ Seg.pvSeg = pvBuf;
+ rc = drvdiskintReadVerify(pThis, &Seg, 1, off, cbRead);
+ }
+
+ return rc;
+}
+
+/** @interface_method_impl{PDMIMEDIA,pfnWrite} */
+static DECLCALLBACK(int) drvdiskintWrite(PPDMIMEDIA pInterface,
+ uint64_t off, const void *pvBuf,
+ size_t cbWrite)
+{
+ int rc = VINF_SUCCESS;
+ PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
+
+ drvdiskintTraceLogFireEvtWrite(pThis, (uintptr_t)pvBuf, false /* fAsync */, off, cbWrite);
+
+ if (pThis->fRecordWriteBeforeCompletion)
+ {
+ RTSGSEG Seg;
+ Seg.cbSeg = cbWrite;
+ Seg.pvSeg = (void *)pvBuf;
+
+ rc = drvdiskintWriteRecord(pThis, &Seg, 1, off, cbWrite);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ rc = pThis->pDrvMedia->pfnWrite(pThis->pDrvMedia, off, pvBuf, cbWrite);
+
+ drvdiskintTraceLogFireEvtComplete(pThis, (uintptr_t)pvBuf, rc, NULL);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ if ( pThis->fCheckConsistency
+ && !pThis->fRecordWriteBeforeCompletion)
+ {
+ /* Record the write. */
+ RTSGSEG Seg;
+ Seg.cbSeg = cbWrite;
+ Seg.pvSeg = (void *)pvBuf;
+ rc = drvdiskintWriteRecord(pThis, &Seg, 1, off, cbWrite);
+ }
+
+ return rc;
+}
+
+/** @interface_method_impl{PDMIMEDIA,pfnFlush} */
+static DECLCALLBACK(int) drvdiskintFlush(PPDMIMEDIA pInterface)
+{
+ int rc = VINF_SUCCESS;
+ PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
+
+ drvdiskintTraceLogFireEvtFlush(pThis, 1, false /* fAsync */);
+ rc = pThis->pDrvMedia->pfnFlush(pThis->pDrvMedia);
+ drvdiskintTraceLogFireEvtComplete(pThis, 1, rc, NULL);
+
+ return rc;
+}
+
+/** @interface_method_impl{PDMIMEDIA,pfnGetSize} */
+static DECLCALLBACK(uint64_t) drvdiskintGetSize(PPDMIMEDIA pInterface)
+{
+ PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
+ return pThis->pDrvMedia->pfnGetSize(pThis->pDrvMedia);
+}
+
+/** @interface_method_impl{PDMIMEDIA,pfnIsReadOnly} */
+static DECLCALLBACK(bool) drvdiskintIsReadOnly(PPDMIMEDIA pInterface)
+{
+ PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
+ return pThis->pDrvMedia->pfnIsReadOnly(pThis->pDrvMedia);
+}
+
+/** @interface_method_impl{PDMIMEDIA,pfnBiosIsVisible} */
+static DECLCALLBACK(bool) drvdiskintBiosIsVisible(PPDMIMEDIA pInterface)
+{
+ PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
+ return pThis->pDrvMedia->pfnBiosIsVisible(pThis->pDrvMedia);
+}
+
+/** @interface_method_impl{PDMIMEDIA,pfnGetType} */
+static DECLCALLBACK(PDMMEDIATYPE) drvdiskintGetType(PPDMIMEDIA pInterface)
+{
+ PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
+ return pThis->pDrvMedia->pfnGetType(pThis->pDrvMedia);
+}
+
+/** @interface_method_impl{PDMIMEDIA,pfnBiosGetPCHSGeometry} */
+static DECLCALLBACK(int) drvdiskintBiosGetPCHSGeometry(PPDMIMEDIA pInterface,
+ PPDMMEDIAGEOMETRY pPCHSGeometry)
+{
+ PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
+ return pThis->pDrvMedia->pfnBiosGetPCHSGeometry(pThis->pDrvMedia, pPCHSGeometry);
+}
+
+/** @interface_method_impl{PDMIMEDIA,pfnBiosSetPCHSGeometry} */
+static DECLCALLBACK(int) drvdiskintBiosSetPCHSGeometry(PPDMIMEDIA pInterface,
+ PCPDMMEDIAGEOMETRY pPCHSGeometry)
+{
+ PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
+ return pThis->pDrvMedia->pfnBiosSetPCHSGeometry(pThis->pDrvMedia, pPCHSGeometry);
+}
+
+/** @interface_method_impl{PDMIMEDIA,pfnBiosGetLCHSGeometry} */
+static DECLCALLBACK(int) drvdiskintBiosGetLCHSGeometry(PPDMIMEDIA pInterface,
+ PPDMMEDIAGEOMETRY pLCHSGeometry)
+{
+ PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
+ return pThis->pDrvMedia->pfnBiosGetLCHSGeometry(pThis->pDrvMedia, pLCHSGeometry);
+}
+
+/** @interface_method_impl{PDMIMEDIA,pfnBiosSetLCHSGeometry} */
+static DECLCALLBACK(int) drvdiskintBiosSetLCHSGeometry(PPDMIMEDIA pInterface,
+ PCPDMMEDIAGEOMETRY pLCHSGeometry)
+{
+ PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
+ return pThis->pDrvMedia->pfnBiosSetLCHSGeometry(pThis->pDrvMedia, pLCHSGeometry);
+}
+
+/** @interface_method_impl{PDMIMEDIA,pfnGetUuid} */
+static DECLCALLBACK(int) drvdiskintGetUuid(PPDMIMEDIA pInterface, PRTUUID pUuid)
+{
+ PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
+ return pThis->pDrvMedia->pfnGetUuid(pThis->pDrvMedia, pUuid);
+}
+
+/** @interface_method_impl{PDMIMEDIA,pfnGetSectorSize} */
+static DECLCALLBACK(uint32_t) drvdiskintGetSectorSize(PPDMIMEDIA pInterface)
+{
+ PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
+ return pThis->pDrvMedia->pfnGetSectorSize(pThis->pDrvMedia);
+}
+
+/** @interface_method_impl{PDMIMEDIA,pfnDiscard} */
+static DECLCALLBACK(int) drvdiskintDiscard(PPDMIMEDIA pInterface, PCRTRANGE paRanges, unsigned cRanges)
+{
+ int rc = VINF_SUCCESS;
+ PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
+
+ rc = pThis->pDrvMedia->pfnDiscard(pThis->pDrvMedia, paRanges, cRanges);
+ drvdiskintTraceLogFireEvtComplete(pThis, (uintptr_t)paRanges, rc, NULL);
+
+ if (pThis->fCheckConsistency)
+ rc = drvdiskintDiscardRecords(pThis, paRanges, cRanges);
+
+ return rc;
+}
+
+/** @interface_method_impl{PDMIMEDIA,pfnReadPcBios} */
+static DECLCALLBACK(int) drvdiskintReadPcBios(PPDMIMEDIA pInterface,
+ uint64_t off, void *pvBuf, size_t cbRead)
+{
+ LogFlowFunc(("\n"));
+ PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
+
+ return pThis->pDrvMedia->pfnReadPcBios(pThis->pDrvMedia, off, pvBuf, cbRead);
+}
+
+/** @interface_method_impl{PDMIMEDIA,pfnIsNonRotational} */
+static DECLCALLBACK(bool) drvdiskintIsNonRotational(PPDMIMEDIA pInterface)
+{
+ PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
+ return pThis->pDrvMedia->pfnIsNonRotational(pThis->pDrvMedia);
+}
+
+/** @interface_method_impl{PDMIMEDIA,pfnGetRegionCount} */
+static DECLCALLBACK(uint32_t) drvdiskintGetRegionCount(PPDMIMEDIA pInterface)
+{
+ PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
+ return pThis->pDrvMedia->pfnGetRegionCount(pThis->pDrvMedia);
+}
+
+/** @interface_method_impl{PDMIMEDIA,pfnQueryRegionProperties} */
+static DECLCALLBACK(int) drvdiskintQueryRegionProperties(PPDMIMEDIA pInterface, uint32_t uRegion, uint64_t *pu64LbaStart,
+ uint64_t *pcBlocks, uint64_t *pcbBlock,
+ PVDREGIONDATAFORM penmDataForm)
+{
+ PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
+ return pThis->pDrvMedia->pfnQueryRegionProperties(pThis->pDrvMedia, uRegion, pu64LbaStart, pcBlocks, pcbBlock, penmDataForm);
+}
+
+/** @interface_method_impl{PDMIMEDIA,pfnQueryRegionPropertiesForLba} */
+static DECLCALLBACK(int) drvdiskintQueryRegionPropertiesForLba(PPDMIMEDIA pInterface, uint64_t u64LbaStart,
+ uint32_t *puRegion, uint64_t *pcBlocks,
+ uint64_t *pcbBlock, PVDREGIONDATAFORM penmDataForm)
+{
+ PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
+ return pThis->pDrvMedia->pfnQueryRegionPropertiesForLba(pThis->pDrvMedia, u64LbaStart, puRegion, pcBlocks, pcbBlock, penmDataForm);
+}
+
+/* -=-=-=-=- IMediaPort -=-=-=-=- */
+
+/** Makes a PDRVBLOCK out of a PPDMIMEDIAPORT. */
+#define PDMIMEDIAPORT_2_DRVDISKINTEGRITY(pInterface) ( (PDRVDISKINTEGRITY((uintptr_t)pInterface - RT_UOFFSETOF(DRVDISKINTEGRITY, IMediaPort))) )
+
+/**
+ * @interface_method_impl{PDMIMEDIAPORT,pfnQueryDeviceLocation}
+ */
+static DECLCALLBACK(int) drvdiskintQueryDeviceLocation(PPDMIMEDIAPORT pInterface, const char **ppcszController,
+ uint32_t *piInstance, uint32_t *piLUN)
+{
+ PDRVDISKINTEGRITY pThis = PDMIMEDIAPORT_2_DRVDISKINTEGRITY(pInterface);
+
+ return pThis->pDrvMediaPort->pfnQueryDeviceLocation(pThis->pDrvMediaPort, ppcszController,
+ piInstance, piLUN);
+}
+
+/* -=-=-=-=- IMediaExPort -=-=-=-=- */
+
+/**
+ * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqCompleteNotify}
+ */
+static DECLCALLBACK(int) drvdiskintIoReqCompleteNotify(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
+ void *pvIoReqAlloc, int rcReq)
+{
+ PDRVDISKINTEGRITY pThis = RT_FROM_MEMBER(pInterface, DRVDISKINTEGRITY, IMediaExPort);
+ PDRVDISKAIOREQ pIoReq = DISKINTEGRITY_IOREQ_ALLOC_2_DRVDISKAIOREQ(pvIoReqAlloc);
+ int rc = VINF_SUCCESS;
+
+ LogFlowFunc(("pIoReq=%#p\n", pIoReq));
+
+ /* Remove from the active list. */
+ if (pThis->fTraceRequests)
+ drvdiskintIoReqRemove(pThis, pIoReq);
+
+ if (RT_SUCCESS(rcReq) && pThis->fCheckConsistency)
+ {
+ if (pIoReq->enmTxDir == DRVDISKAIOTXDIR_READ)
+ rc = drvdiskintReadVerify(pThis, &pIoReq->IoSeg, 1, pIoReq->off, pIoReq->cbTransfer);
+ else if ( pIoReq->enmTxDir == DRVDISKAIOTXDIR_WRITE
+ && !pThis->fRecordWriteBeforeCompletion)
+ rc = drvdiskintWriteRecord(pThis, &pIoReq->IoSeg, 1, pIoReq->off, pIoReq->cbTransfer);
+ else if (pIoReq->enmTxDir == DRVDISKAIOTXDIR_DISCARD)
+ rc = drvdiskintDiscardRecords(pThis, pIoReq->paRanges, pIoReq->cRanges);
+ else if (pIoReq->enmTxDir == DRVDISKAIOTXDIR_READ_AFTER_WRITE)
+ rc = drvdiskintReadAfterWriteVerify(pThis, pIoReq);
+ else
+ AssertMsg( pIoReq->enmTxDir == DRVDISKAIOTXDIR_FLUSH
+ || ( pIoReq->enmTxDir == DRVDISKAIOTXDIR_WRITE
+ && pThis->fRecordWriteBeforeCompletion), ("Huh?\n"));
+
+ AssertRC(rc);
+ }
+
+ if ( RT_SUCCESS(rcReq)
+ && pThis->fValidateMemBufs
+ && pIoReq->enmTxDir == DRVDISKAIOTXDIR_READ)
+ {
+ /* Check that the guest memory buffer matches what was written. */
+ RTSGSEG SegCmp;
+ SegCmp.pvSeg = RTMemAlloc(pIoReq->cbTransfer);
+ SegCmp.cbSeg = pIoReq->cbTransfer;
+
+ RTSGBUF SgBufCmp;
+ RTSgBufInit(&SgBufCmp, &SegCmp, 1);
+ rc = pThis->pDrvMediaExPort->pfnIoReqCopyToBuf(pThis->pDrvMediaExPort, hIoReq,
+ DISKINTEGRITY_IOREQ_ALLOC_2_UPPER(pvIoReqAlloc),
+ 0, &SgBufCmp, pIoReq->cbTransfer);
+ AssertRC(rc);
+
+ RTSGBUF SgBuf;
+ RTSgBufInit(&SgBuf, &pIoReq->IoSeg, 1);
+ if (RTSgBufCmp(&SgBuf, &SgBufCmp, pIoReq->cbTransfer))
+ {
+ RTMsgError("Corrupted memory buffer at offset %llu!\n", 0);
+ RTAssertDebugBreak();
+ }
+
+ RTMemFree(SegCmp.pvSeg);
+ }
+
+ if (pThis->hIoLogger)
+ {
+ RTSGBUF SgBuf;
+
+ if (pIoReq->enmTxDir == DRVDISKAIOTXDIR_READ)
+ RTSgBufInit(&SgBuf, &pIoReq->IoSeg, 1);
+ drvdiskintTraceLogFireEvtComplete(pThis, (uintptr_t)hIoReq, rcReq, &SgBuf);
+ }
+
+ if ( pThis->fReadAfterWrite
+ && pIoReq->enmTxDir == DRVDISKAIOTXDIR_WRITE)
+ {
+#if 0 /** @todo */
+ pIoReq->enmTxDir = DRVDISKAIOTXDIR_READ_AFTER_WRITE;
+
+ /* Add again because it was removed above. */
+ if (pThis->fTraceRequests)
+ drvdiskintIoReqAdd(pThis, pIoReq);
+
+ rc = pThis->pDrvMediaAsync->pfnStartRead(pThis->pDrvMediaAsync, pIoReq->off, pIoReq->paSeg, pIoReq->cSeg,
+ pIoReq->cbTransfer, pIoReq);
+ if (rc == VINF_VD_ASYNC_IO_FINISHED)
+ {
+ rc = drvdiskintReadAfterWriteVerify(pThis, pIoReq);
+
+ if (pThis->fTraceRequests)
+ drvdiskintIoReqRemove(pThis, pIoReq);
+ RTMemFree(pIoReq);
+ }
+ else if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
+ rc = VINF_SUCCESS;
+ else if (RT_FAILURE(rc))
+ RTMemFree(pIoReq);
+#endif
+ }
+ else
+ {
+ rc = pThis->pDrvMediaExPort->pfnIoReqCompleteNotify(pThis->pDrvMediaExPort, hIoReq,
+ DISKINTEGRITY_IOREQ_ALLOC_2_UPPER(pvIoReqAlloc),
+ rcReq);
+ /* Put on the watch list. */
+ if (pThis->fCheckDoubleCompletion)
+ drvdiskintIoReqCheckForDoubleCompletion(pThis, pIoReq, true /* fMediaEx */);
+ }
+
+ return rc;
+}
+
+/**
+ * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqCopyFromBuf}
+ */
+static DECLCALLBACK(int) drvdiskintIoReqCopyFromBuf(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
+ void *pvIoReqAlloc, uint32_t offDst, PRTSGBUF pSgBuf,
+ size_t cbCopy)
+{
+ PDRVDISKINTEGRITY pThis = RT_FROM_MEMBER(pInterface, DRVDISKINTEGRITY, IMediaExPort);
+ PDRVDISKAIOREQ pIoReq = DISKINTEGRITY_IOREQ_ALLOC_2_DRVDISKAIOREQ(pvIoReqAlloc);
+ RTSGBUF SgBuf;
+
+ RTSgBufClone(&SgBuf, pSgBuf);
+
+ int rc = pThis->pDrvMediaExPort->pfnIoReqCopyFromBuf(pThis->pDrvMediaExPort, hIoReq,
+ DISKINTEGRITY_IOREQ_ALLOC_2_UPPER(pvIoReqAlloc),
+ offDst, pSgBuf, cbCopy);
+ if ( RT_SUCCESS(rc)
+ && pIoReq->IoSeg.pvSeg)
+ {
+ /* Update our copy. */
+ RTSgBufCopyToBuf(&SgBuf, (uint8_t *)pIoReq->IoSeg.pvSeg + offDst, cbCopy);
+
+ /* Validate the just read data against our copy if possible. */
+ if ( pThis->fValidateMemBufs
+ && pThis->fCheckConsistency
+ && pIoReq->enmTxDir == DRVDISKAIOTXDIR_READ)
+ {
+ RTSGSEG Seg;
+
+ Seg.pvSeg = (uint8_t *)pIoReq->IoSeg.pvSeg + offDst;
+ Seg.cbSeg = cbCopy;
+
+ rc = drvdiskintReadVerify(pThis, &Seg, 1, pIoReq->off + offDst,
+ cbCopy);
+ }
+ }
+
+ return rc;
+}
+
+/**
+ * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqCopyToBuf}
+ */
+static DECLCALLBACK(int) drvdiskintIoReqCopyToBuf(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
+ void *pvIoReqAlloc, uint32_t offSrc, PRTSGBUF pSgBuf,
+ size_t cbCopy)
+{
+ PDRVDISKINTEGRITY pThis = RT_FROM_MEMBER(pInterface, DRVDISKINTEGRITY, IMediaExPort);
+ PDRVDISKAIOREQ pIoReq = DISKINTEGRITY_IOREQ_ALLOC_2_DRVDISKAIOREQ(pvIoReqAlloc);
+ RTSGBUF SgBuf;
+
+ RTSgBufClone(&SgBuf, pSgBuf);
+
+ int rc = pThis->pDrvMediaExPort->pfnIoReqCopyToBuf(pThis->pDrvMediaExPort, hIoReq,
+ DISKINTEGRITY_IOREQ_ALLOC_2_UPPER(pvIoReqAlloc),
+ offSrc, pSgBuf, cbCopy);
+ if ( RT_SUCCESS(rc)
+ && pIoReq->IoSeg.pvSeg)
+ {
+ if (pThis->fValidateMemBufs)
+ {
+ /* Make sure what the caller requested matches what we got earlier. */
+ RTSGBUF SgBufCmp;
+ RTSgBufInit(&SgBufCmp, &pIoReq->IoSeg, 1);
+ RTSgBufAdvance(&SgBufCmp, offSrc);
+
+ if (RTSgBufCmp(&SgBuf, &SgBufCmp, cbCopy))
+ {
+ RTMsgError("Corrupted memory buffer at offset %llu!\n", offSrc);
+ RTAssertDebugBreak();
+ }
+ }
+ else
+ {
+ /* Update our copy. */
+ RTSgBufCopyToBuf(&SgBuf, (uint8_t *)pIoReq->IoSeg.pvSeg + offSrc, cbCopy);
+ }
+ }
+
+ return rc;
+}
+
+/**
+ * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqQueryDiscardRanges}
+ */
+static DECLCALLBACK(int) drvdiskintIoReqQueryDiscardRanges(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
+ void *pvIoReqAlloc, uint32_t idxRangeStart,
+ uint32_t cRanges, PRTRANGE paRanges,
+ uint32_t *pcRanges)
+{
+ PDRVDISKINTEGRITY pThis = RT_FROM_MEMBER(pInterface, DRVDISKINTEGRITY, IMediaExPort);
+ return pThis->pDrvMediaExPort->pfnIoReqQueryDiscardRanges(pThis->pDrvMediaExPort, hIoReq,
+ DISKINTEGRITY_IOREQ_ALLOC_2_UPPER(pvIoReqAlloc),
+ idxRangeStart, cRanges, paRanges, pcRanges);
+}
+
+/**
+ * @interface_method_impl{PDMIMEDIAEXPORT,pfnIoReqStateChanged}
+ */
+static DECLCALLBACK(void) drvdiskintIoReqStateChanged(PPDMIMEDIAEXPORT pInterface, PDMMEDIAEXIOREQ hIoReq,
+ void *pvIoReqAlloc, PDMMEDIAEXIOREQSTATE enmState)
+{
+ PDRVDISKINTEGRITY pThis = RT_FROM_MEMBER(pInterface, DRVDISKINTEGRITY, IMediaExPort);
+ pThis->pDrvMediaExPort->pfnIoReqStateChanged(pThis->pDrvMediaExPort, hIoReq,
+ DISKINTEGRITY_IOREQ_ALLOC_2_UPPER(pvIoReqAlloc),
+ enmState);
+}
+
+/* -=-=-=-=- IMediaEx -=-=-=-=- */
+
+/**
+ * @interface_method_impl{PDMIMEDIAEX,pfnQueryFeatures}
+ */
+static DECLCALLBACK(int) drvdiskintQueryFeatures(PPDMIMEDIAEX pInterface, uint32_t *pfFeatures)
+{
+ PDRVDISKINTEGRITY pThis = RT_FROM_MEMBER(pInterface, DRVDISKINTEGRITY, IMediaEx);
+ return pThis->pDrvMediaEx->pfnQueryFeatures(pThis->pDrvMediaEx, pfFeatures);
+}
+
+/**
+ * @interface_method_impl{PDMIMEDIAEX,pfnNotifySuspend}
+ */
+static DECLCALLBACK(void) drvdiskintNotifySuspend(PPDMIMEDIAEX pInterface)
+{
+ PDRVDISKINTEGRITY pThis = RT_FROM_MEMBER(pInterface, DRVDISKINTEGRITY, IMediaEx);
+ return pThis->pDrvMediaEx->pfnNotifySuspend(pThis->pDrvMediaEx);
+}
+
+/**
+ * @interface_method_impl{PDMIMEDIAEX,pfnIoReqAllocSizeSet}
+ */
+static DECLCALLBACK(int) drvdiskintIoReqAllocSizeSet(PPDMIMEDIAEX pInterface, size_t cbIoReqAlloc)
+{
+ PDRVDISKINTEGRITY pThis = RT_FROM_MEMBER(pInterface, DRVDISKINTEGRITY, IMediaEx);
+
+ /* Increase the amount by the size of a pointer to our private tracking structure. */
+ cbIoReqAlloc += sizeof(PDRVDISKAIOREQ);
+
+ pThis->fCheckDoubleCompletion = false;
+
+ return pThis->pDrvMediaEx->pfnIoReqAllocSizeSet(pThis->pDrvMediaEx, cbIoReqAlloc);
+}
+
+/**
+ * @interface_method_impl{PDMIMEDIAEX,pfnIoReqAlloc}
+ */
+static DECLCALLBACK(int) drvdiskintIoReqAlloc(PPDMIMEDIAEX pInterface, PPDMMEDIAEXIOREQ phIoReq, void **ppvIoReqAlloc,
+ PDMMEDIAEXIOREQID uIoReqId, uint32_t fFlags)
+{
+ PDRVDISKINTEGRITY pThis = RT_FROM_MEMBER(pInterface, DRVDISKINTEGRITY, IMediaEx);
+ int rc = VINF_SUCCESS;
+ PDRVDISKAIOREQ pIoReq = (PDRVDISKAIOREQ)RTMemCacheAlloc(pThis->hReqCache);
+ if (RT_LIKELY(pIoReq))
+ {
+ pIoReq->enmTxDir = DRVDISKAIOTXDIR_INVALID;
+ pIoReq->off = 0;
+ pIoReq->cbTransfer = 0;
+ pIoReq->paSeg = NULL;
+ pIoReq->cSeg = 0;
+ pIoReq->pvUser = NULL;
+ pIoReq->iSlot = 0;
+ pIoReq->tsStart = 0;
+ pIoReq->tsComplete = 0;
+ pIoReq->IoSeg.pvSeg = NULL;
+ pIoReq->IoSeg.cbSeg = 0;
+
+ PDRVDISKAIOREQ *ppIoReq = NULL;
+ rc = pThis->pDrvMediaEx->pfnIoReqAlloc(pThis->pDrvMediaEx, phIoReq, (void **)&ppIoReq, uIoReqId, fFlags);
+ if RT_SUCCESS(rc)
+ {
+ /*
+ * Store the size off the start of our tracking structure because it is
+ * required to access it for the read/write callbacks.
+ *
+ * ASSUMPTION that the offset is constant.
+ */
+ if (!pThis->cbIoReqOpaque)
+ pThis->cbIoReqOpaque = (uintptr_t)ppIoReq - (uintptr_t)*phIoReq;
+ else
+ Assert(pThis->cbIoReqOpaque == (uintptr_t)ppIoReq - (uintptr_t)*phIoReq);
+
+ *ppIoReq = pIoReq;
+ *ppvIoReqAlloc = ((uint8_t *)ppIoReq) + sizeof(PDRVDISKAIOREQ);
+ }
+ else
+ RTMemCacheFree(pThis->hReqCache, pIoReq);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
+/**
+ * @interface_method_impl{PDMIMEDIAEX,pfnIoReqFree}
+ */
+static DECLCALLBACK(int) drvdiskintIoReqFree(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq)
+{
+ PDRVDISKINTEGRITY pThis = RT_FROM_MEMBER(pInterface, DRVDISKINTEGRITY, IMediaEx);
+ PDRVDISKAIOREQ pIoReq = DISKINTEGRITY_IOREQ_HANDLE_2_DRVDISKAIOREQ(pThis, hIoReq);
+
+ if (pIoReq->IoSeg.pvSeg)
+ RTMemFree(pIoReq->IoSeg.pvSeg);
+
+ return pThis->pDrvMediaEx->pfnIoReqFree(pThis->pDrvMediaEx, hIoReq);
+}
+
+/**
+ * @interface_method_impl{PDMIMEDIAEX,pfnIoReqQueryResidual}
+ */
+static DECLCALLBACK(int) drvdiskintIoReqQueryResidual(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq, size_t *pcbResidual)
+{
+ PDRVDISKINTEGRITY pThis = RT_FROM_MEMBER(pInterface, DRVDISKINTEGRITY, IMediaEx);
+ return pThis->pDrvMediaEx->pfnIoReqQueryResidual(pThis->pDrvMediaEx, hIoReq, pcbResidual);
+}
+
+/**
+ * @interface_method_impl{PDMIMEDIAEX,pfnIoReqQueryXferSize}
+ */
+static DECLCALLBACK(int) drvdiskintIoReqQueryXferSize(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq, size_t *pcbXfer)
+{
+ PDRVDISKINTEGRITY pThis = RT_FROM_MEMBER(pInterface, DRVDISKINTEGRITY, IMediaEx);
+ return pThis->pDrvMediaEx->pfnIoReqQueryXferSize(pThis->pDrvMediaEx, hIoReq, pcbXfer);
+}
+
+/**
+ * @interface_method_impl{PDMIMEDIAEX,pfnIoReqCancelAll}
+ */
+static DECLCALLBACK(int) drvdiskintIoReqCancelAll(PPDMIMEDIAEX pInterface)
+{
+ PDRVDISKINTEGRITY pThis = RT_FROM_MEMBER(pInterface, DRVDISKINTEGRITY, IMediaEx);
+ return pThis->pDrvMediaEx->pfnIoReqCancelAll(pThis->pDrvMediaEx);
+}
+
+/**
+ * @interface_method_impl{PDMIMEDIAEX,pfnIoReqCancel}
+ */
+static DECLCALLBACK(int) drvdiskintIoReqCancel(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQID uIoReqId)
+{
+ PDRVDISKINTEGRITY pThis = RT_FROM_MEMBER(pInterface, DRVDISKINTEGRITY, IMediaEx);
+ return pThis->pDrvMediaEx->pfnIoReqCancel(pThis->pDrvMediaEx, uIoReqId);
+}
+
+/**
+ * @interface_method_impl{PDMIMEDIAEX,pfnIoReqRead}
+ */
+static DECLCALLBACK(int) drvdiskintIoReqRead(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq, uint64_t off, size_t cbRead)
+{
+ PDRVDISKINTEGRITY pThis = RT_FROM_MEMBER(pInterface, DRVDISKINTEGRITY, IMediaEx);
+ PDRVDISKAIOREQ pIoReq = DISKINTEGRITY_IOREQ_HANDLE_2_DRVDISKAIOREQ(pThis, hIoReq);
+
+ pIoReq->enmTxDir = DRVDISKAIOTXDIR_READ;
+ pIoReq->off = off;
+ pIoReq->cbTransfer = cbRead;
+
+ /* Allocate a I/O buffer if the I/O is verified.*/
+ if (pThis->fCheckConsistency)
+ {
+ pIoReq->IoSeg.pvSeg = RTMemAlloc(cbRead);
+ pIoReq->IoSeg.cbSeg = cbRead;
+ }
+
+ if (pThis->fTraceRequests)
+ drvdiskintIoReqAdd(pThis, pIoReq);
+
+ drvdiskintTraceLogFireEvtRead(pThis, (uintptr_t)hIoReq, true /* fAsync */, off, cbRead);
+ int rc = pThis->pDrvMediaEx->pfnIoReqRead(pThis->pDrvMediaEx, hIoReq, off, cbRead);
+ if (rc == VINF_SUCCESS)
+ {
+ /* Verify the read now. */
+ if (pThis->fCheckConsistency)
+ {
+ int rc2 = drvdiskintReadVerify(pThis, &pIoReq->IoSeg, 1, off, cbRead);
+ AssertRC(rc2);
+ }
+
+ if (pThis->hIoLogger)
+ {
+ RTSGBUF SgBuf;
+
+ RTSgBufInit(&SgBuf, &pIoReq->IoSeg, 1);
+ drvdiskintTraceLogFireEvtComplete(pThis, (uintptr_t)hIoReq, rc, &SgBuf);
+ }
+
+ if (pThis->fTraceRequests)
+ drvdiskintIoReqRemove(pThis, pIoReq);
+ }
+ else if (rc != VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS)
+ drvdiskintTraceLogFireEvtComplete(pThis, (uintptr_t)hIoReq, rc, NULL);
+
+ LogFlowFunc(("returns %Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * @interface_method_impl{PDMIMEDIAEX,pfnIoReqWrite}
+ */
+static DECLCALLBACK(int) drvdiskintIoReqWrite(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq, uint64_t off, size_t cbWrite)
+{
+ PDRVDISKINTEGRITY pThis = RT_FROM_MEMBER(pInterface, DRVDISKINTEGRITY, IMediaEx);
+ PDRVDISKAIOREQ pIoReq = DISKINTEGRITY_IOREQ_HANDLE_2_DRVDISKAIOREQ(pThis, hIoReq);
+
+ pIoReq->enmTxDir = DRVDISKAIOTXDIR_WRITE;
+ pIoReq->off = off;
+ pIoReq->cbTransfer = cbWrite;
+
+ /* Allocate a I/O buffer if the I/O is verified.*/
+ if ( pThis->fCheckConsistency
+ || pThis->fValidateMemBufs
+ || pThis->hIoLogger
+ || pThis->fRecordWriteBeforeCompletion)
+ {
+ pIoReq->IoSeg.pvSeg = RTMemAlloc(cbWrite);
+ pIoReq->IoSeg.cbSeg = cbWrite;
+
+ /* Sync the memory buffer over if we should validate it. */
+ if ( pThis->fValidateMemBufs
+ || pThis->hIoLogger
+ || pThis->fRecordWriteBeforeCompletion)
+ {
+ RTSGBUF SgBuf;
+
+ RTSgBufInit(&SgBuf, &pIoReq->IoSeg, 1);
+ int rc2 = pThis->pDrvMediaExPort->pfnIoReqCopyToBuf(pThis->pDrvMediaExPort, hIoReq,
+ DISKINTEGRITY_IOREQ_HANDLE_2_UPPER_OPAQUE(pThis, hIoReq),
+ 0, &SgBuf, cbWrite);
+ AssertRC(rc2);
+ }
+ }
+
+ if (pThis->fTraceRequests)
+ drvdiskintIoReqAdd(pThis, pIoReq);
+
+ drvdiskintTraceLogFireEvtWrite(pThis, (uintptr_t)hIoReq, true /* fAsync */, off, cbWrite);
+ if (pThis->fRecordWriteBeforeCompletion)
+ {
+
+ int rc2 = drvdiskintWriteRecord(pThis, &pIoReq->IoSeg, 1, off, cbWrite);
+ AssertRC(rc2);
+ }
+
+ int rc = pThis->pDrvMediaEx->pfnIoReqWrite(pThis->pDrvMediaEx, hIoReq, off, cbWrite);
+ if (rc == VINF_SUCCESS)
+ {
+ /* Record the write. */
+ if ( pThis->fCheckConsistency
+ && !pThis->fRecordWriteBeforeCompletion)
+ {
+ int rc2 = drvdiskintWriteRecord(pThis, &pIoReq->IoSeg, 1, off, cbWrite);
+ AssertRC(rc2);
+ }
+
+ RTSGBUF SgBuf;
+ RTSgBufInit(&SgBuf, &pIoReq->IoSeg, 1);
+ drvdiskintTraceLogFireEvtComplete(pThis, (uintptr_t)hIoReq, rc, &SgBuf);
+ if (pThis->fTraceRequests)
+ drvdiskintIoReqRemove(pThis, pIoReq);
+ }
+ else if (rc != VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS)
+ drvdiskintTraceLogFireEvtComplete(pThis, (uintptr_t)hIoReq, rc, NULL);
+
+ LogFlowFunc(("returns %Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * @interface_method_impl{PDMIMEDIAEX,pfnIoReqFlush}
+ */
+static DECLCALLBACK(int) drvdiskintIoReqFlush(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq)
+{
+ PDRVDISKINTEGRITY pThis = RT_FROM_MEMBER(pInterface, DRVDISKINTEGRITY, IMediaEx);
+ PDRVDISKAIOREQ pIoReq = DISKINTEGRITY_IOREQ_HANDLE_2_DRVDISKAIOREQ(pThis, hIoReq);
+
+ pIoReq->enmTxDir = DRVDISKAIOTXDIR_FLUSH;
+ pIoReq->off = 0;
+ pIoReq->cbTransfer = 0;
+
+ if (pThis->fTraceRequests)
+ drvdiskintIoReqAdd(pThis, pIoReq);
+
+ drvdiskintTraceLogFireEvtFlush(pThis, (uintptr_t)hIoReq, true /* fAsync */);
+ int rc = pThis->pDrvMediaEx->pfnIoReqFlush(pThis->pDrvMediaEx, hIoReq);
+ if (rc == VINF_SUCCESS)
+ drvdiskintTraceLogFireEvtComplete(pThis, (uintptr_t)hIoReq, rc, NULL);
+ else if (rc != VINF_PDM_MEDIAEX_IOREQ_IN_PROGRESS)
+ drvdiskintTraceLogFireEvtComplete(pThis, (uintptr_t)hIoReq, rc, NULL);
+
+ LogFlowFunc(("returns %Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * @interface_method_impl{PDMIMEDIAEX,pfnIoReqDiscard}
+ */
+static DECLCALLBACK(int) drvdiskintIoReqDiscard(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq, unsigned cRangesMax)
+{
+ PDRVDISKINTEGRITY pThis = RT_FROM_MEMBER(pInterface, DRVDISKINTEGRITY, IMediaEx);
+ return pThis->pDrvMediaEx->pfnIoReqDiscard(pThis->pDrvMediaEx, hIoReq, cRangesMax);
+}
+
+/**
+ * @interface_method_impl{PDMIMEDIAEX,pfnIoReqGetActiveCount}
+ */
+static DECLCALLBACK(uint32_t) drvdiskintIoReqGetActiveCount(PPDMIMEDIAEX pInterface)
+{
+ PDRVDISKINTEGRITY pThis = RT_FROM_MEMBER(pInterface, DRVDISKINTEGRITY, IMediaEx);
+ return pThis->pDrvMediaEx->pfnIoReqGetActiveCount(pThis->pDrvMediaEx);
+}
+
+/**
+ * @interface_method_impl{PDMIMEDIAEX,pfnIoReqGetSuspendedCount}
+ */
+static DECLCALLBACK(uint32_t) drvdiskintIoReqGetSuspendedCount(PPDMIMEDIAEX pInterface)
+{
+ PDRVDISKINTEGRITY pThis = RT_FROM_MEMBER(pInterface, DRVDISKINTEGRITY, IMediaEx);
+ return pThis->pDrvMediaEx->pfnIoReqGetSuspendedCount(pThis->pDrvMediaEx);
+}
+
+/**
+ * @interface_method_impl{PDMIMEDIAEX,pfnIoReqQuerySuspendedStart}
+ */
+static DECLCALLBACK(int) drvdiskintIoReqQuerySuspendedStart(PPDMIMEDIAEX pInterface, PPDMMEDIAEXIOREQ phIoReq, void **ppvIoReqAlloc)
+{
+ PDRVDISKINTEGRITY pThis = RT_FROM_MEMBER(pInterface, DRVDISKINTEGRITY, IMediaEx);
+ return pThis->pDrvMediaEx->pfnIoReqQuerySuspendedStart(pThis->pDrvMediaEx, phIoReq, ppvIoReqAlloc);
+}
+
+/**
+ * @interface_method_impl{PDMIMEDIAEX,pfnIoReqQuerySuspendedNext}
+ */
+static DECLCALLBACK(int) drvdiskintIoReqQuerySuspendedNext(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq,
+ PPDMMEDIAEXIOREQ phIoReqNext, void **ppvIoReqAllocNext)
+{
+ PDRVDISKINTEGRITY pThis = RT_FROM_MEMBER(pInterface, DRVDISKINTEGRITY, IMediaEx);
+ return pThis->pDrvMediaEx->pfnIoReqQuerySuspendedNext(pThis->pDrvMediaEx, hIoReq, phIoReqNext, ppvIoReqAllocNext);
+}
+
+/**
+ * @interface_method_impl{PDMIMEDIAEX,pfnIoReqSuspendedSave}
+ */
+static DECLCALLBACK(int) drvdiskintIoReqSuspendedSave(PPDMIMEDIAEX pInterface, PSSMHANDLE pSSM, PDMMEDIAEXIOREQ hIoReq)
+{
+ PDRVDISKINTEGRITY pThis = RT_FROM_MEMBER(pInterface, DRVDISKINTEGRITY, IMediaEx);
+ return pThis->pDrvMediaEx->pfnIoReqSuspendedSave(pThis->pDrvMediaEx, pSSM, hIoReq);
+}
+
+/**
+ * @interface_method_impl{PDMIMEDIAEX,pfnIoReqSuspendedLoad}
+ */
+static DECLCALLBACK(int) drvdiskintIoReqSuspendedLoad(PPDMIMEDIAEX pInterface, PSSMHANDLE pSSM, PDMMEDIAEXIOREQ hIoReq)
+{
+ PDRVDISKINTEGRITY pThis = RT_FROM_MEMBER(pInterface, DRVDISKINTEGRITY, IMediaEx);
+ return pThis->pDrvMediaEx->pfnIoReqSuspendedLoad(pThis->pDrvMediaEx, pSSM, hIoReq);
+}
+
+/* -=-=-=-=- IMount -=-=-=-=- */
+
+/** @interface_method_impl{PDMIMOUNT,pfnUnmount} */
+static DECLCALLBACK(int) drvdiskintUnmount(PPDMIMOUNT pInterface, bool fForce, bool fEject)
+{
+ PDRVDISKINTEGRITY pThis = RT_FROM_MEMBER(pInterface, DRVDISKINTEGRITY, IMount);
+ return pThis->pDrvMount->pfnUnmount(pThis->pDrvMount, fForce, fEject);
+}
+
+/** @interface_method_impl{PDMIMOUNT,pfnIsMounted} */
+static DECLCALLBACK(bool) drvdiskintIsMounted(PPDMIMOUNT pInterface)
+{
+ PDRVDISKINTEGRITY pThis = RT_FROM_MEMBER(pInterface, DRVDISKINTEGRITY, IMount);
+ return pThis->pDrvMount->pfnIsMounted(pThis->pDrvMount);
+}
+
+/** @interface_method_impl{PDMIMOUNT,pfnLock} */
+static DECLCALLBACK(int) drvdiskintLock(PPDMIMOUNT pInterface)
+{
+ PDRVDISKINTEGRITY pThis = RT_FROM_MEMBER(pInterface, DRVDISKINTEGRITY, IMount);
+ return pThis->pDrvMount->pfnLock(pThis->pDrvMount);
+}
+
+/** @interface_method_impl{PDMIMOUNT,pfnUnlock} */
+static DECLCALLBACK(int) drvdiskintUnlock(PPDMIMOUNT pInterface)
+{
+ PDRVDISKINTEGRITY pThis = RT_FROM_MEMBER(pInterface, DRVDISKINTEGRITY, IMount);
+ return pThis->pDrvMount->pfnUnlock(pThis->pDrvMount);
+}
+
+/** @interface_method_impl{PDMIMOUNT,pfnIsLocked} */
+static DECLCALLBACK(bool) drvdiskintIsLocked(PPDMIMOUNT pInterface)
+{
+ PDRVDISKINTEGRITY pThis = RT_FROM_MEMBER(pInterface, DRVDISKINTEGRITY, IMount);
+ return pThis->pDrvMount->pfnIsLocked(pThis->pDrvMount);
+}
+
+/* -=-=-=-=- IMountNotify -=-=-=-=- */
+
+/** @interface_method_impl{PDMIMOUNTNOTIFY,pfnMountNotify} */
+static DECLCALLBACK(void) drvdiskintMountNotify(PPDMIMOUNTNOTIFY pInterface)
+{
+ PDRVDISKINTEGRITY pThis = RT_FROM_MEMBER(pInterface, DRVDISKINTEGRITY, IMountNotify);
+ pThis->pDrvMountNotify->pfnMountNotify(pThis->pDrvMountNotify);
+}
+
+/** @interface_method_impl{PDMIMOUNTNOTIFY,pfnUnmountNotify} */
+static DECLCALLBACK(void) drvdiskintUnmountNotify(PPDMIMOUNTNOTIFY pInterface)
+{
+ PDRVDISKINTEGRITY pThis = RT_FROM_MEMBER(pInterface, DRVDISKINTEGRITY, IMountNotify);
+ pThis->pDrvMountNotify->pfnUnmountNotify(pThis->pDrvMountNotify);
+}
+
+/* -=-=-=-=- IBase -=-=-=-=- */
+
+/**
+ * @interface_method_impl{PDMIBASE,pfnQueryInterface}
+ */
+static DECLCALLBACK(void *) drvdiskintQueryInterface(PPDMIBASE pInterface, const char *pszIID)
+{
+ PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
+ PDRVDISKINTEGRITY pThis = PDMINS_2_DATA(pDrvIns, PDRVDISKINTEGRITY);
+
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIA, &pThis->IMedia);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAPORT, &pThis->IMediaPort);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAEXPORT, &pThis->IMediaExPort);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIAEX, pThis->pDrvMediaEx ? &pThis->IMediaEx : NULL);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMOUNT, pThis->pDrvMount ? &pThis->IMount : NULL);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMOUNTNOTIFY, &pThis->IMountNotify);
+ return NULL;
+}
+
+
+/* -=-=-=-=- driver interface -=-=-=-=- */
+
+static DECLCALLBACK(int) drvdiskintTreeDestroy(PAVLRFOFFNODECORE pNode, void *pvUser)
+{
+ PDRVDISKSEGMENT pSeg = (PDRVDISKSEGMENT)pNode;
+
+ RT_NOREF(pvUser);
+
+ RTMemFree(pSeg->pbSeg);
+ RTMemFree(pSeg);
+ return VINF_SUCCESS;
+}
+
+/**
+ * @copydoc FNPDMDRVDESTRUCT
+ */
+static DECLCALLBACK(void) drvdiskintDestruct(PPDMDRVINS pDrvIns)
+{
+ PDRVDISKINTEGRITY pThis = PDMINS_2_DATA(pDrvIns, PDRVDISKINTEGRITY);
+
+ if (pThis->pTreeSegments)
+ {
+ RTAvlrFileOffsetDestroy(pThis->pTreeSegments, drvdiskintTreeDestroy, NULL);
+ RTMemFree(pThis->pTreeSegments);
+ }
+
+ if (pThis->fTraceRequests)
+ {
+ pThis->fRunning = false;
+ RTSemEventSignal(pThis->SemEvent);
+ RTSemEventDestroy(pThis->SemEvent);
+ }
+
+ if (pThis->fCheckDoubleCompletion)
+ {
+ /* Free all requests */
+ while (pThis->papIoReq[pThis->iEntry])
+ {
+ RTMemFree(pThis->papIoReq[pThis->iEntry]);
+ pThis->papIoReq[pThis->iEntry] = NULL;
+ pThis->iEntry = (pThis->iEntry+1) % pThis->cEntries;
+ }
+ }
+
+ if (pThis->hIoLogger)
+ RTTraceLogWrDestroy(pThis->hIoLogger);
+
+ if (pThis->hReqCache != NIL_RTMEMCACHE)
+ {
+ RTMemCacheDestroy(pThis->hReqCache);
+ pThis->hReqCache = NIL_RTMEMCACHE;
+ }
+}
+
+/**
+ * Construct a disk integrity driver instance.
+ *
+ * @copydoc FNPDMDRVCONSTRUCT
+ */
+static DECLCALLBACK(int) drvdiskintConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
+{
+ PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
+ PDRVDISKINTEGRITY pThis = PDMINS_2_DATA(pDrvIns, PDRVDISKINTEGRITY);
+ PCPDMDRVHLPR3 pHlp = pDrvIns->pHlpR3;
+
+ LogFlow(("drvdiskintConstruct: iInstance=%d\n", pDrvIns->iInstance));
+
+ /*
+ * Validate configuration.
+ */
+ PDMDRV_VALIDATE_CONFIG_RETURN(pDrvIns, "CheckConsistency"
+ "|TraceRequests"
+ "|CheckIntervalMs"
+ "|ExpireIntervalMs"
+ "|CheckDoubleCompletions"
+ "|HistorySize"
+ "|IoLogType"
+ "|IoLogFile"
+ "|IoLogAddress"
+ "|IoLogPort"
+ "|IoLogData"
+ "|PrepopulateRamDisk"
+ "|ReadAfterWrite"
+ "|RecordWriteBeforeCompletion"
+ "|ValidateMemoryBuffers",
+ "");
+
+ int rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "CheckConsistency", &pThis->fCheckConsistency, false);
+ AssertRC(rc);
+ rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "TraceRequests", &pThis->fTraceRequests, false);
+ AssertRC(rc);
+ rc = pHlp->pfnCFGMQueryU32Def(pCfg, "CheckIntervalMs", &pThis->uCheckIntervalMs, 5000); /* 5 seconds */
+ AssertRC(rc);
+ rc = pHlp->pfnCFGMQueryU32Def(pCfg, "ExpireIntervalMs", &pThis->uExpireIntervalMs, 20000); /* 20 seconds */
+ AssertRC(rc);
+ rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "CheckDoubleCompletions", &pThis->fCheckDoubleCompletion, false);
+ AssertRC(rc);
+ rc = pHlp->pfnCFGMQueryU32Def(pCfg, "HistorySize", &pThis->cEntries, 512);
+ AssertRC(rc);
+ rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "PrepopulateRamDisk", &pThis->fPrepopulateRamDisk, false);
+ AssertRC(rc);
+ rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "ReadAfterWrite", &pThis->fReadAfterWrite, false);
+ AssertRC(rc);
+ rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "RecordWriteBeforeCompletion", &pThis->fRecordWriteBeforeCompletion, false);
+ AssertRC(rc);
+ rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "ValidateMemoryBuffers", &pThis->fValidateMemBufs, false);
+ AssertRC(rc);
+
+ bool fIoLogData = false;
+ rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "IoLogData", &fIoLogData, false);
+ AssertRC(rc);
+
+ char *pszIoLogType = NULL;
+ char *pszIoLogFilename = NULL;
+ char *pszAddress = NULL;
+ uint32_t uPort = 0;
+ rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "IoLogType", &pszIoLogType);
+ if (RT_SUCCESS(rc))
+ {
+ if (!RTStrICmp(pszIoLogType, "File"))
+ {
+ rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "IoLogFile", &pszIoLogFilename);
+ AssertRC(rc);
+ }
+ else if (!RTStrICmp(pszIoLogType, "Server"))
+ {
+ rc = pHlp->pfnCFGMQueryStringAllocDef(pCfg, "IoLogAddress", &pszAddress, NULL);
+ AssertRC(rc);
+ rc = pHlp->pfnCFGMQueryU32Def(pCfg, "IoLogPort", &uPort, 4000);
+ AssertRC(rc);
+ }
+ else if (!RTStrICmp(pszIoLogType, "Client"))
+ {
+ rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "IoLogAddress", &pszAddress);
+ AssertRC(rc);
+ rc = pHlp->pfnCFGMQueryU32Def(pCfg, "IoLogPort", &uPort, 4000);
+ AssertRC(rc);
+ }
+ else
+ AssertMsgFailed(("Invalid I/O log type given: %s\n", pszIoLogType));
+ }
+ else
+ Assert(rc == VERR_CFGM_VALUE_NOT_FOUND);
+
+ /*
+ * Initialize most of the data members.
+ */
+ pThis->pDrvIns = pDrvIns;
+ pThis->hReqCache = NIL_RTMEMCACHE;
+
+ /* IBase. */
+ pDrvIns->IBase.pfnQueryInterface = drvdiskintQueryInterface;
+
+ /* IMedia */
+ pThis->IMedia.pfnRead = drvdiskintRead;
+ pThis->IMedia.pfnWrite = drvdiskintWrite;
+ pThis->IMedia.pfnFlush = drvdiskintFlush;
+ pThis->IMedia.pfnGetSize = drvdiskintGetSize;
+ pThis->IMedia.pfnIsReadOnly = drvdiskintIsReadOnly;
+ pThis->IMedia.pfnBiosIsVisible = drvdiskintBiosIsVisible;
+ pThis->IMedia.pfnBiosGetPCHSGeometry = drvdiskintBiosGetPCHSGeometry;
+ pThis->IMedia.pfnBiosSetPCHSGeometry = drvdiskintBiosSetPCHSGeometry;
+ pThis->IMedia.pfnBiosGetLCHSGeometry = drvdiskintBiosGetLCHSGeometry;
+ pThis->IMedia.pfnBiosSetLCHSGeometry = drvdiskintBiosSetLCHSGeometry;
+ pThis->IMedia.pfnGetUuid = drvdiskintGetUuid;
+ pThis->IMedia.pfnGetSectorSize = drvdiskintGetSectorSize;
+ pThis->IMedia.pfnGetType = drvdiskintGetType;
+ pThis->IMedia.pfnReadPcBios = drvdiskintReadPcBios;
+ pThis->IMedia.pfnIsNonRotational = drvdiskintIsNonRotational;
+ pThis->IMedia.pfnSendCmd = NULL;
+ pThis->IMedia.pfnGetRegionCount = drvdiskintGetRegionCount;
+ pThis->IMedia.pfnQueryRegionProperties = drvdiskintQueryRegionProperties;
+ pThis->IMedia.pfnQueryRegionPropertiesForLba = drvdiskintQueryRegionPropertiesForLba;
+
+
+ /* IMediaEx. */
+ pThis->IMediaEx.pfnQueryFeatures = drvdiskintQueryFeatures;
+ pThis->IMediaEx.pfnNotifySuspend = drvdiskintNotifySuspend;
+ pThis->IMediaEx.pfnIoReqAllocSizeSet = drvdiskintIoReqAllocSizeSet;
+ pThis->IMediaEx.pfnIoReqAlloc = drvdiskintIoReqAlloc;
+ pThis->IMediaEx.pfnIoReqFree = drvdiskintIoReqFree;
+ pThis->IMediaEx.pfnIoReqQueryResidual = drvdiskintIoReqQueryResidual;
+ pThis->IMediaEx.pfnIoReqQueryXferSize = drvdiskintIoReqQueryXferSize;
+ pThis->IMediaEx.pfnIoReqCancelAll = drvdiskintIoReqCancelAll;
+ pThis->IMediaEx.pfnIoReqCancel = drvdiskintIoReqCancel;
+ pThis->IMediaEx.pfnIoReqRead = drvdiskintIoReqRead;
+ pThis->IMediaEx.pfnIoReqWrite = drvdiskintIoReqWrite;
+ pThis->IMediaEx.pfnIoReqFlush = drvdiskintIoReqFlush;
+ pThis->IMediaEx.pfnIoReqDiscard = drvdiskintIoReqDiscard;
+ pThis->IMediaEx.pfnIoReqGetActiveCount = drvdiskintIoReqGetActiveCount;
+ pThis->IMediaEx.pfnIoReqGetSuspendedCount = drvdiskintIoReqGetSuspendedCount;
+ pThis->IMediaEx.pfnIoReqQuerySuspendedStart = drvdiskintIoReqQuerySuspendedStart;
+ pThis->IMediaEx.pfnIoReqQuerySuspendedNext = drvdiskintIoReqQuerySuspendedNext;
+ pThis->IMediaEx.pfnIoReqSuspendedSave = drvdiskintIoReqSuspendedSave;
+ pThis->IMediaEx.pfnIoReqSuspendedLoad = drvdiskintIoReqSuspendedLoad;
+
+ /* IMediaPort. */
+ pThis->IMediaPort.pfnQueryDeviceLocation = drvdiskintQueryDeviceLocation;
+
+ /* IMediaExPort. */
+ pThis->IMediaExPort.pfnIoReqCompleteNotify = drvdiskintIoReqCompleteNotify;
+ pThis->IMediaExPort.pfnIoReqCopyFromBuf = drvdiskintIoReqCopyFromBuf;
+ pThis->IMediaExPort.pfnIoReqCopyToBuf = drvdiskintIoReqCopyToBuf;
+ pThis->IMediaExPort.pfnIoReqQueryDiscardRanges = drvdiskintIoReqQueryDiscardRanges;
+ pThis->IMediaExPort.pfnIoReqStateChanged = drvdiskintIoReqStateChanged;
+
+ /* IMount */
+ pThis->IMount.pfnUnmount = drvdiskintUnmount;
+ pThis->IMount.pfnIsMounted = drvdiskintIsMounted;
+ pThis->IMount.pfnLock = drvdiskintLock;
+ pThis->IMount.pfnUnlock = drvdiskintUnlock;
+ pThis->IMount.pfnIsLocked = drvdiskintIsLocked;
+
+ /* IMountNotify */
+ pThis->IMountNotify.pfnMountNotify = drvdiskintMountNotify;
+ pThis->IMountNotify.pfnUnmountNotify = drvdiskintUnmountNotify;
+
+ /* Query the media port interface above us. */
+ pThis->pDrvMediaPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIMEDIAPORT);
+ if (!pThis->pDrvMediaPort)
+ return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_BELOW,
+ N_("No media port interface above"));
+
+ /* Try to attach extended media port interface above.*/
+ pThis->pDrvMediaExPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIMEDIAEXPORT);
+
+ rc = RTMemCacheCreate(&pThis->hReqCache, sizeof(DRVDISKAIOREQ), 0, UINT32_MAX,
+ NULL, NULL, NULL, 0);
+ if (RT_FAILURE(rc))
+ return PDMDRV_SET_ERROR(pDrvIns, rc,
+ N_("Failed to create request tracking structure cache"));
+
+ /*
+ * Try attach driver below and query it's media interface.
+ */
+ PPDMIBASE pBase;
+ rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pBase);
+ if (RT_FAILURE(rc))
+ return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
+ N_("Failed to attach driver below us! %Rrc"), rc);
+
+ pThis->pDrvMedia = PDMIBASE_QUERY_INTERFACE(pBase, PDMIMEDIA);
+ if (!pThis->pDrvMedia)
+ return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_BELOW,
+ N_("No media or async media interface below"));
+
+ pThis->pDrvMediaEx = PDMIBASE_QUERY_INTERFACE(pBase, PDMIMEDIAEX);
+ pThis->pDrvMount = PDMIBASE_QUERY_INTERFACE(pBase, PDMIMOUNT);
+
+ if (pThis->pDrvMedia->pfnDiscard)
+ pThis->IMedia.pfnDiscard = drvdiskintDiscard;
+
+ if (pThis->fCheckConsistency)
+ {
+ /* Create the AVL tree. */
+ pThis->pTreeSegments = (PAVLRFOFFTREE)RTMemAllocZ(sizeof(AVLRFOFFTREE));
+ if (!pThis->pTreeSegments)
+ rc = VERR_NO_MEMORY;
+ }
+
+ if (pThis->fTraceRequests)
+ {
+ for (unsigned i = 0; i < RT_ELEMENTS(pThis->apReqActive); i++)
+ {
+ pThis->apReqActive[i].pIoReq = NULL;
+ pThis->apReqActive[i].tsStart = 0;
+ }
+
+ pThis->iNextFreeSlot = 0;
+
+ /* Init event semaphore. */
+ rc = RTSemEventCreate(&pThis->SemEvent);
+ AssertRC(rc);
+ pThis->fRunning = true;
+ rc = RTThreadCreate(&pThis->hThread, drvdiskIntIoReqExpiredCheck, pThis,
+ 0, RTTHREADTYPE_INFREQUENT_POLLER, 0, "DiskIntegrity");
+ AssertRC(rc);
+ }
+
+ if (pThis->fCheckDoubleCompletion)
+ {
+ pThis->iEntry = 0;
+ pThis->papIoReq = (PDRVDISKAIOREQ *)RTMemAllocZ(pThis->cEntries * sizeof(PDRVDISKAIOREQ));
+ AssertPtr(pThis->papIoReq);
+ }
+
+ if (pszIoLogType)
+ {
+ if (!RTStrICmp(pszIoLogType, "File"))
+ {
+ rc = RTTraceLogWrCreateFile(&pThis->hIoLogger, NULL, pszIoLogFilename);
+ PDMDrvHlpMMHeapFree(pDrvIns, pszIoLogFilename);
+ }
+ else if (!RTStrICmp(pszIoLogType, "Server"))
+ {
+ rc = RTTraceLogWrCreateTcpServer(&pThis->hIoLogger, NULL, pszAddress, uPort);
+ if (pszAddress)
+ PDMDrvHlpMMHeapFree(pDrvIns, pszAddress);
+ }
+ else if (!RTStrICmp(pszIoLogType, "Client"))
+ {
+ rc = RTTraceLogWrCreateTcpClient(&pThis->hIoLogger, NULL, pszAddress, uPort);
+ PDMDrvHlpMMHeapFree(pDrvIns, pszAddress);
+ }
+ else
+ AssertMsgFailed(("Invalid I/O log type given: %s\n", pszIoLogType));
+
+ PDMDrvHlpMMHeapFree(pDrvIns, pszIoLogType);
+ }
+
+ /* Read in all data before the start if requested. */
+ if (pThis->fPrepopulateRamDisk)
+ {
+ uint64_t cbDisk = 0;
+
+ LogRel(("DiskIntegrity: Prepopulating RAM disk, this will take some time...\n"));
+
+ cbDisk = pThis->pDrvMedia->pfnGetSize(pThis->pDrvMedia);
+ if (cbDisk)
+ {
+ uint64_t off = 0;
+ uint8_t abBuffer[_64K];
+ RTSGSEG Seg;
+
+ Seg.pvSeg = abBuffer;
+
+ while (cbDisk)
+ {
+ size_t cbThisRead = RT_MIN(cbDisk, sizeof(abBuffer));
+
+ rc = pThis->pDrvMedia->pfnRead(pThis->pDrvMedia, off, abBuffer, cbThisRead);
+ if (RT_FAILURE(rc))
+ break;
+
+ if (ASMBitFirstSet(abBuffer, sizeof(abBuffer) * 8) != -1)
+ {
+ Seg.cbSeg = cbThisRead;
+ rc = drvdiskintWriteRecord(pThis, &Seg, 1,
+ off, cbThisRead);
+ if (RT_FAILURE(rc))
+ break;
+ }
+
+ cbDisk -= cbThisRead;
+ off += cbThisRead;
+ }
+
+ LogRel(("DiskIntegrity: Prepopulating RAM disk finished with %Rrc\n", rc));
+ }
+ else
+ return PDMDRV_SET_ERROR(pDrvIns, VERR_INTERNAL_ERROR,
+ N_("DiskIntegrity: Error querying the media size below"));
+ }
+
+ return rc;
+}
+
+
+/**
+ * Block driver registration record.
+ */
+const PDMDRVREG g_DrvDiskIntegrity =
+{
+ /* u32Version */
+ PDM_DRVREG_VERSION,
+ /* szName */
+ "DiskIntegrity",
+ /* szRCMod */
+ "",
+ /* szR0Mod */
+ "",
+ /* pszDescription */
+ "Disk integrity driver.",
+ /* fFlags */
+ PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
+ /* fClass. */
+ PDM_DRVREG_CLASS_BLOCK,
+ /* cMaxInstances */
+ ~0U,
+ /* cbInstance */
+ sizeof(DRVDISKINTEGRITY),
+ /* pfnConstruct */
+ drvdiskintConstruct,
+ /* pfnDestruct */
+ drvdiskintDestruct,
+ /* pfnRelocate */
+ NULL,
+ /* pfnIOCtl */
+ NULL,
+ /* pfnPowerOn */
+ NULL,
+ /* pfnReset */
+ NULL,
+ /* pfnSuspend */
+ NULL,
+ /* pfnResume */
+ NULL,
+ /* pfnAttach */
+ NULL,
+ /* pfnDetach */
+ NULL,
+ /* pfnPowerOff */
+ NULL,
+ /* pfnSoftReset */
+ NULL,
+ /* u32EndVersion */
+ PDM_DRVREG_VERSION
+};
+