/** * 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_OFFSETOF(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 = cbRange / 512; if (!pSeg->pbSeg) RTMemFree(pSeg); else { bool fInserted = RTAvlrFileOffsetInsert(pThis->pTreeSegments, &pSeg->Core); AssertMsg(fInserted, ("Bug!\n")); 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); /* 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 cbWrite 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; } 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 + 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; }
int VDMemDiskCmp(PVDMEMDISK pMemDisk, uint64_t off, size_t cbCmp, PRTSGBUF pSgBuf) { LogFlowFunc(("pMemDisk=%#p off=%llx cbCmp=%u pSgBuf=%#p\n", pMemDisk, off, cbCmp, pSgBuf)); /* Compare data */ size_t cbLeft = cbCmp; uint64_t offCurr = off; while (cbLeft) { PVDMEMDISKSEG pSeg = (PVDMEMDISKSEG)RTAvlrU64Get(pMemDisk->pTreeSegments, offCurr); size_t cbRange = 0; bool fCmp = false; unsigned offSeg = 0; if (!pSeg) { /* Get next segment */ pSeg = (PVDMEMDISKSEG)RTAvlrU64GetBestFit(pMemDisk->pTreeSegments, offCurr, true); if (!pSeg) { /* No data in the tree for this read. Assume everything is ok. */ cbRange = cbLeft; } else if (offCurr + cbLeft <= pSeg->Core.Key) cbRange = cbLeft; else cbRange = pSeg->Core.Key - offCurr; } 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; int rc = 0; Seg.cbSeg = cbRange; Seg.pvSeg = (uint8_t *)pSeg->pvSeg + offSeg; RTSgBufInit(&SgBufCmp, &Seg, 1); rc = RTSgBufCmpEx(pSgBuf, &SgBufCmp, cbRange, &cbOff, true); if (rc) return rc; } else RTSgBufAdvance(pSgBuf, cbRange); offCurr += cbRange; cbLeft -= cbRange; } return 0; }