VirtualBox

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

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette