diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 16:49:04 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 16:49:04 +0000 |
commit | 16f504a9dca3fe3b70568f67b7d41241ae485288 (patch) | |
tree | c60f36ada0496ba928b7161059ba5ab1ab224f9d /src/VBox/Storage/testcase/VDMemDisk.cpp | |
parent | Initial commit. (diff) | |
download | virtualbox-16f504a9dca3fe3b70568f67b7d41241ae485288.tar.xz virtualbox-16f504a9dca3fe3b70568f67b7d41241ae485288.zip |
Adding upstream version 7.0.6-dfsg.upstream/7.0.6-dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Storage/testcase/VDMemDisk.cpp')
-rw-r--r-- | src/VBox/Storage/testcase/VDMemDisk.cpp | 418 |
1 files changed, 418 insertions, 0 deletions
diff --git a/src/VBox/Storage/testcase/VDMemDisk.cpp b/src/VBox/Storage/testcase/VDMemDisk.cpp new file mode 100644 index 00000000..fa153e95 --- /dev/null +++ b/src/VBox/Storage/testcase/VDMemDisk.cpp @@ -0,0 +1,418 @@ +/* $Id: VDMemDisk.cpp $ */ +/** @file + * VBox HDD container test utility, memory disk/file. + */ + +/* + * Copyright (C) 2011-2022 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 + */ +#define LOGGROUP LOGGROUP_DEFAULT /** @todo Log group */ +#include <iprt/errcore.h> +#include <iprt/log.h> +#include <iprt/assert.h> +#include <iprt/avl.h> +#include <iprt/mem.h> +#include <iprt/file.h> + +#include "VDMemDisk.h" + +/** + * Memory disk/file. + */ +typedef struct VDMEMDISK +{ + /** Current size of the disk. */ + uint64_t cbDisk; + /** Flag whether the disk can grow. */ + bool fGrowable; + /** Pointer to the AVL tree holding the segments. */ + PAVLRU64TREE pTreeSegments; +} VDMEMDISK; + +/** + * A disk segment. + */ +typedef struct VDMEMDISKSEG +{ + /** AVL tree core. */ + AVLRU64NODECORE Core; + /** Pointer to the data. */ + void *pvSeg; +} VDMEMDISKSEG, *PVDMEMDISKSEG; + + +int VDMemDiskCreate(PPVDMEMDISK ppMemDisk, uint64_t cbSize) +{ + AssertPtrReturn(ppMemDisk, VERR_INVALID_POINTER); + + int rc = VINF_SUCCESS; + PVDMEMDISK pMemDisk = (PVDMEMDISK)RTMemAllocZ(sizeof(VDMEMDISK)); + if (pMemDisk) + { + pMemDisk->fGrowable = cbSize ? false : true; + pMemDisk->cbDisk = cbSize; + pMemDisk->pTreeSegments = (PAVLRU64TREE)RTMemAllocZ(sizeof(AVLRU64TREE)); + if (pMemDisk->pTreeSegments) + *ppMemDisk = pMemDisk; + else + { + RTMemFree(pMemDisk); + rc = VERR_NO_MEMORY; + } + } + else + rc = VERR_NO_MEMORY; + + LogFlowFunc(("returns rc=%Rrc\n", rc)); + return rc; +} + +static DECLCALLBACK(int) vdMemDiskDestroy(PAVLRU64NODECORE pNode, void *pvUser) +{ + RT_NOREF1(pvUser); + PVDMEMDISKSEG pSeg = (PVDMEMDISKSEG)pNode; + RTMemFree(pSeg->pvSeg); + RTMemFree(pSeg); + return VINF_SUCCESS; +} + +void VDMemDiskDestroy(PVDMEMDISK pMemDisk) +{ + AssertPtrReturnVoid(pMemDisk); + + RTAvlrU64Destroy(pMemDisk->pTreeSegments, vdMemDiskDestroy, NULL); + RTMemFree(pMemDisk->pTreeSegments); + RTMemFree(pMemDisk); +} + +int VDMemDiskWrite(PVDMEMDISK pMemDisk, uint64_t off, size_t cbWrite, PRTSGBUF pSgBuf) +{ + int rc = VINF_SUCCESS; + + LogFlowFunc(("pMemDisk=%#p off=%llu cbWrite=%zu pSgBuf=%#p\n", + pMemDisk, off, cbWrite, pSgBuf)); + + AssertPtrReturn(pMemDisk, VERR_INVALID_POINTER); + AssertPtrReturn(pSgBuf, VERR_INVALID_POINTER); + + /* Check for a write beyond the end of a disk. */ + if ( !pMemDisk->fGrowable + && (off + cbWrite) > pMemDisk->cbDisk) + return VERR_INVALID_PARAMETER; + + /* Update the segments */ + size_t cbLeft = cbWrite; + uint64_t offCurr = off; + + while ( cbLeft + && RT_SUCCESS(rc)) + { + PVDMEMDISKSEG pSeg = (PVDMEMDISKSEG)RTAvlrU64RangeGet(pMemDisk->pTreeSegments, offCurr); + size_t cbRange = 0; + unsigned offSeg = 0; + + if (!pSeg) + { + /* Get next segment */ + pSeg = (PVDMEMDISKSEG)RTAvlrU64GetBestFit(pMemDisk->pTreeSegments, offCurr, true); + if ( !pSeg + || offCurr + cbLeft <= pSeg->Core.Key) + cbRange = cbLeft; + else + cbRange = pSeg->Core.Key - offCurr; + + /* Create new segment */ + pSeg = (PVDMEMDISKSEG)RTMemAllocZ(sizeof(VDMEMDISKSEG)); + if (pSeg) + { + pSeg->Core.Key = offCurr; + pSeg->Core.KeyLast = offCurr + cbRange - 1; + pSeg->pvSeg = RTMemAllocZ(cbRange); + + if (!pSeg->pvSeg) + { + RTMemFree(pSeg); + rc = VERR_NO_MEMORY; + } + else + { + bool fInserted = RTAvlrU64Insert(pMemDisk->pTreeSegments, &pSeg->Core); + AssertMsg(fInserted, ("Bug!\n")); NOREF(fInserted); + } + } + else + rc = VERR_NO_MEMORY; + } + else + { + offSeg = offCurr - pSeg->Core.Key; + cbRange = RT_MIN(cbLeft, (size_t)(pSeg->Core.KeyLast + 1 - offCurr)); + } + + if (RT_SUCCESS(rc)) + { + AssertPtr(pSeg); + size_t cbCopied = RTSgBufCopyToBuf(pSgBuf, (uint8_t *)pSeg->pvSeg + offSeg, cbRange); + Assert(cbCopied == cbRange); NOREF(cbCopied); + } + + offCurr += cbRange; + cbLeft -= cbRange; + } + + /* Update size of the disk. */ + if ( RT_SUCCESS(rc) + && pMemDisk->fGrowable + && (off + cbWrite) > pMemDisk->cbDisk) + { + pMemDisk->cbDisk = off + cbWrite; + } + + return rc; +} + + +int VDMemDiskRead(PVDMEMDISK pMemDisk, uint64_t off, size_t cbRead, PRTSGBUF pSgBuf) +{ + LogFlowFunc(("pMemDisk=%#p off=%llu cbRead=%zu pSgBuf=%#p\n", + pMemDisk, off, cbRead, pSgBuf)); + + AssertPtrReturn(pMemDisk, VERR_INVALID_POINTER); + AssertPtrReturn(pSgBuf, VERR_INVALID_POINTER); + + /* Check for a read beyond the end of a disk. */ + if ((off + cbRead) > pMemDisk->cbDisk) + return VERR_INVALID_PARAMETER; + + /* Compare read data */ + size_t cbLeft = cbRead; + uint64_t offCurr = off; + + while (cbLeft) + { + PVDMEMDISKSEG pSeg = (PVDMEMDISKSEG)RTAvlrU64RangeGet(pMemDisk->pTreeSegments, offCurr); + size_t cbRange = 0; + unsigned offSeg = 0; + + if (!pSeg) + { + /* Get next segment */ + pSeg = (PVDMEMDISKSEG)RTAvlrU64GetBestFit(pMemDisk->pTreeSegments, offCurr, true); + if ( !pSeg + || offCurr + cbLeft <= pSeg->Core.Key) + { + /* No data in the tree for this read. Fill with 0. */ + cbRange = cbLeft; + } + else + cbRange = pSeg->Core.Key - offCurr; + + RTSgBufSet(pSgBuf, 0, cbRange); + } + else + { + offSeg = offCurr - pSeg->Core.Key; + cbRange = RT_MIN(cbLeft, (size_t)(pSeg->Core.KeyLast + 1 - offCurr)); + + RTSgBufCopyFromBuf(pSgBuf, (uint8_t *)pSeg->pvSeg + offSeg, cbRange); + } + + offCurr += cbRange; + cbLeft -= cbRange; + } + + return VINF_SUCCESS; +} + +int VDMemDiskSetSize(PVDMEMDISK pMemDisk, uint64_t cbSize) +{ + AssertPtrReturn(pMemDisk, VERR_INVALID_POINTER); + + if (!pMemDisk->fGrowable) + return VERR_NOT_SUPPORTED; + + if (pMemDisk->cbDisk <= cbSize) + { + /* Increase. */ + pMemDisk->cbDisk = cbSize; + } + else + { + /* We have to delete all parts beyond the new end. */ + PVDMEMDISKSEG pSeg = (PVDMEMDISKSEG)RTAvlrU64Get(pMemDisk->pTreeSegments, cbSize); + if (pSeg) + { + RTAvlrU64Remove(pMemDisk->pTreeSegments, pSeg->Core.Key); + if (pSeg->Core.Key < cbSize) + { + /* Cut off the part which is not in the file anymore. */ + pSeg->pvSeg = RTMemRealloc(pSeg->pvSeg, pSeg->Core.KeyLast - cbSize + 1); + pSeg->Core.KeyLast = cbSize - pSeg->Core.Key - 1; + + bool fInserted = RTAvlrU64Insert(pMemDisk->pTreeSegments, &pSeg->Core); + AssertMsg(fInserted, ("Bug!\n")); NOREF(fInserted); + } + else + { + /* Free the whole block. */ + RTMemFree(pSeg->pvSeg); + RTMemFree(pSeg); + } + } + + /* Kill all blocks coming after. */ + do + { + pSeg = (PVDMEMDISKSEG)RTAvlrU64GetBestFit(pMemDisk->pTreeSegments, cbSize, true); + if (pSeg) + { + RTAvlrU64Remove(pMemDisk->pTreeSegments, pSeg->Core.Key); + RTMemFree(pSeg->pvSeg); + pSeg->pvSeg = NULL; + RTMemFree(pSeg); + } + else + break; + } while (true); + + pMemDisk->cbDisk = cbSize; + } + + return VINF_SUCCESS; +} + +int VDMemDiskGetSize(PVDMEMDISK pMemDisk, uint64_t *pcbSize) +{ + AssertPtrReturn(pMemDisk, VERR_INVALID_POINTER); + AssertPtrReturn(pcbSize, VERR_INVALID_POINTER); + + *pcbSize = pMemDisk->cbDisk; + return VINF_SUCCESS; +} + +/** + * Writes a segment to the given file. + * + * @returns IPRT status code. + * + * @param pNode The disk segment to write to the file. + * @param pvParam Opaque user data containing the pointer to + * the file handle. + */ +static DECLCALLBACK(int) vdMemDiskSegmentWriteToFile(PAVLRU64NODECORE pNode, void *pvParam) +{ + PVDMEMDISKSEG pSeg = (PVDMEMDISKSEG)pNode; + RTFILE hFile = *(PRTFILE)pvParam; + + return RTFileWriteAt(hFile, pSeg->Core.Key, pSeg->pvSeg, pSeg->Core.KeyLast - pSeg->Core.Key + 1, NULL); +} + +int VDMemDiskWriteToFile(PVDMEMDISK pMemDisk, const char *pcszFilename) +{ + int rc = VINF_SUCCESS; + RTFILE hFile = NIL_RTFILE; + + LogFlowFunc(("pMemDisk=%#p pcszFilename=%s\n", pMemDisk, pcszFilename)); + AssertPtrReturn(pMemDisk, VERR_INVALID_POINTER); + AssertPtrReturn(pcszFilename, VERR_INVALID_POINTER); + + rc = RTFileOpen(&hFile, pcszFilename, RTFILE_O_DENY_NONE | RTFILE_O_CREATE | RTFILE_O_WRITE); + if (RT_SUCCESS(rc)) + { + rc = RTAvlrU64DoWithAll(pMemDisk->pTreeSegments, true, vdMemDiskSegmentWriteToFile, &hFile); + + RTFileClose(hFile); + if (RT_FAILURE(rc)) + RTFileDelete(pcszFilename); + } + + LogFlowFunc(("returns rc=%Rrc\n", rc)); + return rc; +} + +int VDMemDiskReadFromFile(PVDMEMDISK pMemDisk, const char *pcszFilename) +{ + RT_NOREF2(pMemDisk, pcszFilename); + return VERR_NOT_IMPLEMENTED; +} + +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; +} + |