VirtualBox

source: vbox/trunk/src/VBox/Storage/testcase/VDMemDisk.cpp

Last change on this file was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 11.7 KB
Line 
1/* $Id: VDMemDisk.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * VBox HDD container test utility, memory disk/file.
4 */
5
6/*
7 * Copyright (C) 2011-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27#define LOGGROUP LOGGROUP_DEFAULT /** @todo Log group */
28#include <iprt/errcore.h>
29#include <iprt/log.h>
30#include <iprt/assert.h>
31#include <iprt/avl.h>
32#include <iprt/mem.h>
33#include <iprt/file.h>
34
35#include "VDMemDisk.h"
36
37/**
38 * Memory disk/file.
39 */
40typedef struct VDMEMDISK
41{
42 /** Current size of the disk. */
43 uint64_t cbDisk;
44 /** Flag whether the disk can grow. */
45 bool fGrowable;
46 /** Pointer to the AVL tree holding the segments. */
47 PAVLRU64TREE pTreeSegments;
48} VDMEMDISK;
49
50/**
51 * A disk segment.
52 */
53typedef struct VDMEMDISKSEG
54{
55 /** AVL tree core. */
56 AVLRU64NODECORE Core;
57 /** Pointer to the data. */
58 void *pvSeg;
59} VDMEMDISKSEG, *PVDMEMDISKSEG;
60
61
62int VDMemDiskCreate(PPVDMEMDISK ppMemDisk, uint64_t cbSize)
63{
64 AssertPtrReturn(ppMemDisk, VERR_INVALID_POINTER);
65
66 int rc = VINF_SUCCESS;
67 PVDMEMDISK pMemDisk = (PVDMEMDISK)RTMemAllocZ(sizeof(VDMEMDISK));
68 if (pMemDisk)
69 {
70 pMemDisk->fGrowable = cbSize ? false : true;
71 pMemDisk->cbDisk = cbSize;
72 pMemDisk->pTreeSegments = (PAVLRU64TREE)RTMemAllocZ(sizeof(AVLRU64TREE));
73 if (pMemDisk->pTreeSegments)
74 *ppMemDisk = pMemDisk;
75 else
76 {
77 RTMemFree(pMemDisk);
78 rc = VERR_NO_MEMORY;
79 }
80 }
81 else
82 rc = VERR_NO_MEMORY;
83
84 LogFlowFunc(("returns rc=%Rrc\n", rc));
85 return rc;
86}
87
88static DECLCALLBACK(int) vdMemDiskDestroy(PAVLRU64NODECORE pNode, void *pvUser)
89{
90 RT_NOREF1(pvUser);
91 PVDMEMDISKSEG pSeg = (PVDMEMDISKSEG)pNode;
92 RTMemFree(pSeg->pvSeg);
93 RTMemFree(pSeg);
94 return VINF_SUCCESS;
95}
96
97void VDMemDiskDestroy(PVDMEMDISK pMemDisk)
98{
99 AssertPtrReturnVoid(pMemDisk);
100
101 RTAvlrU64Destroy(pMemDisk->pTreeSegments, vdMemDiskDestroy, NULL);
102 RTMemFree(pMemDisk->pTreeSegments);
103 RTMemFree(pMemDisk);
104}
105
106int VDMemDiskWrite(PVDMEMDISK pMemDisk, uint64_t off, size_t cbWrite, PRTSGBUF pSgBuf)
107{
108 int rc = VINF_SUCCESS;
109
110 LogFlowFunc(("pMemDisk=%#p off=%llu cbWrite=%zu pSgBuf=%#p\n",
111 pMemDisk, off, cbWrite, pSgBuf));
112
113 AssertPtrReturn(pMemDisk, VERR_INVALID_POINTER);
114 AssertPtrReturn(pSgBuf, VERR_INVALID_POINTER);
115
116 /* Check for a write beyond the end of a disk. */
117 if ( !pMemDisk->fGrowable
118 && (off + cbWrite) > pMemDisk->cbDisk)
119 return VERR_INVALID_PARAMETER;
120
121 /* Update the segments */
122 size_t cbLeft = cbWrite;
123 uint64_t offCurr = off;
124
125 while ( cbLeft
126 && RT_SUCCESS(rc))
127 {
128 PVDMEMDISKSEG pSeg = (PVDMEMDISKSEG)RTAvlrU64RangeGet(pMemDisk->pTreeSegments, offCurr);
129 size_t cbRange = 0;
130 unsigned offSeg = 0;
131
132 if (!pSeg)
133 {
134 /* Get next segment */
135 pSeg = (PVDMEMDISKSEG)RTAvlrU64GetBestFit(pMemDisk->pTreeSegments, offCurr, true);
136 if ( !pSeg
137 || offCurr + cbLeft <= pSeg->Core.Key)
138 cbRange = cbLeft;
139 else
140 cbRange = pSeg->Core.Key - offCurr;
141
142 /* Create new segment */
143 pSeg = (PVDMEMDISKSEG)RTMemAllocZ(sizeof(VDMEMDISKSEG));
144 if (pSeg)
145 {
146 pSeg->Core.Key = offCurr;
147 pSeg->Core.KeyLast = offCurr + cbRange - 1;
148 pSeg->pvSeg = RTMemAllocZ(cbRange);
149
150 if (!pSeg->pvSeg)
151 {
152 RTMemFree(pSeg);
153 rc = VERR_NO_MEMORY;
154 }
155 else
156 {
157 bool fInserted = RTAvlrU64Insert(pMemDisk->pTreeSegments, &pSeg->Core);
158 AssertMsg(fInserted, ("Bug!\n")); NOREF(fInserted);
159 }
160 }
161 else
162 rc = VERR_NO_MEMORY;
163 }
164 else
165 {
166 offSeg = offCurr - pSeg->Core.Key;
167 cbRange = RT_MIN(cbLeft, (size_t)(pSeg->Core.KeyLast + 1 - offCurr));
168 }
169
170 if (RT_SUCCESS(rc))
171 {
172 AssertPtr(pSeg);
173 size_t cbCopied = RTSgBufCopyToBuf(pSgBuf, (uint8_t *)pSeg->pvSeg + offSeg, cbRange);
174 Assert(cbCopied == cbRange); NOREF(cbCopied);
175 }
176
177 offCurr += cbRange;
178 cbLeft -= cbRange;
179 }
180
181 /* Update size of the disk. */
182 if ( RT_SUCCESS(rc)
183 && pMemDisk->fGrowable
184 && (off + cbWrite) > pMemDisk->cbDisk)
185 {
186 pMemDisk->cbDisk = off + cbWrite;
187 }
188
189 return rc;
190}
191
192
193int VDMemDiskRead(PVDMEMDISK pMemDisk, uint64_t off, size_t cbRead, PRTSGBUF pSgBuf)
194{
195 LogFlowFunc(("pMemDisk=%#p off=%llu cbRead=%zu pSgBuf=%#p\n",
196 pMemDisk, off, cbRead, pSgBuf));
197
198 AssertPtrReturn(pMemDisk, VERR_INVALID_POINTER);
199 AssertPtrReturn(pSgBuf, VERR_INVALID_POINTER);
200
201 /* Check for a read beyond the end of a disk. */
202 if ((off + cbRead) > pMemDisk->cbDisk)
203 return VERR_INVALID_PARAMETER;
204
205 /* Compare read data */
206 size_t cbLeft = cbRead;
207 uint64_t offCurr = off;
208
209 while (cbLeft)
210 {
211 PVDMEMDISKSEG pSeg = (PVDMEMDISKSEG)RTAvlrU64RangeGet(pMemDisk->pTreeSegments, offCurr);
212 size_t cbRange = 0;
213 unsigned offSeg = 0;
214
215 if (!pSeg)
216 {
217 /* Get next segment */
218 pSeg = (PVDMEMDISKSEG)RTAvlrU64GetBestFit(pMemDisk->pTreeSegments, offCurr, true);
219 if ( !pSeg
220 || offCurr + cbLeft <= pSeg->Core.Key)
221 {
222 /* No data in the tree for this read. Fill with 0. */
223 cbRange = cbLeft;
224 }
225 else
226 cbRange = pSeg->Core.Key - offCurr;
227
228 RTSgBufSet(pSgBuf, 0, cbRange);
229 }
230 else
231 {
232 offSeg = offCurr - pSeg->Core.Key;
233 cbRange = RT_MIN(cbLeft, (size_t)(pSeg->Core.KeyLast + 1 - offCurr));
234
235 RTSgBufCopyFromBuf(pSgBuf, (uint8_t *)pSeg->pvSeg + offSeg, cbRange);
236 }
237
238 offCurr += cbRange;
239 cbLeft -= cbRange;
240 }
241
242 return VINF_SUCCESS;
243}
244
245int VDMemDiskSetSize(PVDMEMDISK pMemDisk, uint64_t cbSize)
246{
247 AssertPtrReturn(pMemDisk, VERR_INVALID_POINTER);
248
249 if (!pMemDisk->fGrowable)
250 return VERR_NOT_SUPPORTED;
251
252 if (pMemDisk->cbDisk <= cbSize)
253 {
254 /* Increase. */
255 pMemDisk->cbDisk = cbSize;
256 }
257 else
258 {
259 /* We have to delete all parts beyond the new end. */
260 PVDMEMDISKSEG pSeg = (PVDMEMDISKSEG)RTAvlrU64Get(pMemDisk->pTreeSegments, cbSize);
261 if (pSeg)
262 {
263 RTAvlrU64Remove(pMemDisk->pTreeSegments, pSeg->Core.Key);
264 if (pSeg->Core.Key < cbSize)
265 {
266 /* Cut off the part which is not in the file anymore. */
267 pSeg->pvSeg = RTMemRealloc(pSeg->pvSeg, pSeg->Core.KeyLast - cbSize + 1);
268 pSeg->Core.KeyLast = cbSize - pSeg->Core.Key - 1;
269
270 bool fInserted = RTAvlrU64Insert(pMemDisk->pTreeSegments, &pSeg->Core);
271 AssertMsg(fInserted, ("Bug!\n")); NOREF(fInserted);
272 }
273 else
274 {
275 /* Free the whole block. */
276 RTMemFree(pSeg->pvSeg);
277 RTMemFree(pSeg);
278 }
279 }
280
281 /* Kill all blocks coming after. */
282 do
283 {
284 pSeg = (PVDMEMDISKSEG)RTAvlrU64GetBestFit(pMemDisk->pTreeSegments, cbSize, true);
285 if (pSeg)
286 {
287 RTAvlrU64Remove(pMemDisk->pTreeSegments, pSeg->Core.Key);
288 RTMemFree(pSeg->pvSeg);
289 pSeg->pvSeg = NULL;
290 RTMemFree(pSeg);
291 }
292 else
293 break;
294 } while (true);
295
296 pMemDisk->cbDisk = cbSize;
297 }
298
299 return VINF_SUCCESS;
300}
301
302int VDMemDiskGetSize(PVDMEMDISK pMemDisk, uint64_t *pcbSize)
303{
304 AssertPtrReturn(pMemDisk, VERR_INVALID_POINTER);
305 AssertPtrReturn(pcbSize, VERR_INVALID_POINTER);
306
307 *pcbSize = pMemDisk->cbDisk;
308 return VINF_SUCCESS;
309}
310
311/**
312 * Writes a segment to the given file.
313 *
314 * @returns IPRT status code.
315 *
316 * @param pNode The disk segment to write to the file.
317 * @param pvParam Opaque user data containing the pointer to
318 * the file handle.
319 */
320static DECLCALLBACK(int) vdMemDiskSegmentWriteToFile(PAVLRU64NODECORE pNode, void *pvParam)
321{
322 PVDMEMDISKSEG pSeg = (PVDMEMDISKSEG)pNode;
323 RTFILE hFile = *(PRTFILE)pvParam;
324
325 return RTFileWriteAt(hFile, pSeg->Core.Key, pSeg->pvSeg, pSeg->Core.KeyLast - pSeg->Core.Key + 1, NULL);
326}
327
328int VDMemDiskWriteToFile(PVDMEMDISK pMemDisk, const char *pcszFilename)
329{
330 int rc = VINF_SUCCESS;
331 RTFILE hFile = NIL_RTFILE;
332
333 LogFlowFunc(("pMemDisk=%#p pcszFilename=%s\n", pMemDisk, pcszFilename));
334 AssertPtrReturn(pMemDisk, VERR_INVALID_POINTER);
335 AssertPtrReturn(pcszFilename, VERR_INVALID_POINTER);
336
337 rc = RTFileOpen(&hFile, pcszFilename, RTFILE_O_DENY_NONE | RTFILE_O_CREATE | RTFILE_O_WRITE);
338 if (RT_SUCCESS(rc))
339 {
340 rc = RTAvlrU64DoWithAll(pMemDisk->pTreeSegments, true, vdMemDiskSegmentWriteToFile, &hFile);
341
342 RTFileClose(hFile);
343 if (RT_FAILURE(rc))
344 RTFileDelete(pcszFilename);
345 }
346
347 LogFlowFunc(("returns rc=%Rrc\n", rc));
348 return rc;
349}
350
351int VDMemDiskReadFromFile(PVDMEMDISK pMemDisk, const char *pcszFilename)
352{
353 RT_NOREF2(pMemDisk, pcszFilename);
354 return VERR_NOT_IMPLEMENTED;
355}
356
357int VDMemDiskCmp(PVDMEMDISK pMemDisk, uint64_t off, size_t cbCmp, PRTSGBUF pSgBuf)
358{
359 LogFlowFunc(("pMemDisk=%#p off=%llx cbCmp=%u pSgBuf=%#p\n",
360 pMemDisk, off, cbCmp, pSgBuf));
361
362 /* Compare data */
363 size_t cbLeft = cbCmp;
364 uint64_t offCurr = off;
365
366 while (cbLeft)
367 {
368 PVDMEMDISKSEG pSeg = (PVDMEMDISKSEG)RTAvlrU64Get(pMemDisk->pTreeSegments, offCurr);
369 size_t cbRange = 0;
370 bool fCmp = false;
371 unsigned offSeg = 0;
372
373 if (!pSeg)
374 {
375 /* Get next segment */
376 pSeg = (PVDMEMDISKSEG)RTAvlrU64GetBestFit(pMemDisk->pTreeSegments, offCurr, true);
377 if (!pSeg)
378 {
379 /* No data in the tree for this read. Assume everything is ok. */
380 cbRange = cbLeft;
381 }
382 else if (offCurr + cbLeft <= pSeg->Core.Key)
383 cbRange = cbLeft;
384 else
385 cbRange = pSeg->Core.Key - offCurr;
386 }
387 else
388 {
389 fCmp = true;
390 offSeg = offCurr - pSeg->Core.Key;
391 cbRange = RT_MIN(cbLeft, (size_t)(pSeg->Core.KeyLast + 1 - offCurr));
392 }
393
394 if (fCmp)
395 {
396 RTSGSEG Seg;
397 RTSGBUF SgBufCmp;
398 size_t cbOff = 0;
399 int rc = 0;
400
401 Seg.cbSeg = cbRange;
402 Seg.pvSeg = (uint8_t *)pSeg->pvSeg + offSeg;
403
404 RTSgBufInit(&SgBufCmp, &Seg, 1);
405 rc = RTSgBufCmpEx(pSgBuf, &SgBufCmp, cbRange, &cbOff, true);
406 if (rc)
407 return rc;
408 }
409 else
410 RTSgBufAdvance(pSgBuf, cbRange);
411
412 offCurr += cbRange;
413 cbLeft -= cbRange;
414 }
415
416 return 0;
417}
418
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use