VirtualBox

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

Last change on this file since 56292 was 52371, checked in by vboxsync, 10 years ago

Storage/tstVDIo: Plug leaks, modifications and start turning it into a proper testcase for unit tests

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 11.3 KB
Line 
1/** $Id: VDMemDisk.cpp 52371 2014-08-13 19:00:27Z vboxsync $ */
2/** @file
3 *
4 * VBox HDD container test utility, memory disk/file.
5 */
6
7/*
8 * Copyright (C) 2011 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18#define LOGGROUP LOGGROUP_DEFAULT /** @todo: Log group */
19#include <iprt/err.h>
20#include <iprt/log.h>
21#include <iprt/assert.h>
22#include <iprt/avl.h>
23#include <iprt/mem.h>
24#include <iprt/file.h>
25
26#include "VDMemDisk.h"
27
28/**
29 * Memory disk/file.
30 */
31typedef struct VDMEMDISK
32{
33 /** Current size of the disk. */
34 uint64_t cbDisk;
35 /** Flag whether the disk can grow. */
36 bool fGrowable;
37 /** Pointer to the AVL tree holding the segments. */
38 PAVLRU64TREE pTreeSegments;
39} VDMEMDISK;
40
41/**
42 * A disk segment.
43 */
44typedef struct VDMEMDISKSEG
45{
46 /** AVL tree core. */
47 AVLRU64NODECORE Core;
48 /** Pointer to the data. */
49 void *pvSeg;
50} VDMEMDISKSEG, *PVDMEMDISKSEG;
51
52
53int VDMemDiskCreate(PPVDMEMDISK ppMemDisk, uint64_t cbSize)
54{
55 AssertPtrReturn(ppMemDisk, VERR_INVALID_POINTER);
56
57 int rc = VINF_SUCCESS;
58 PVDMEMDISK pMemDisk = (PVDMEMDISK)RTMemAllocZ(sizeof(VDMEMDISK));
59 if (pMemDisk)
60 {
61 pMemDisk->fGrowable = cbSize ? false : true;
62 pMemDisk->cbDisk = cbSize;
63 pMemDisk->pTreeSegments = (PAVLRU64TREE)RTMemAllocZ(sizeof(AVLRU64TREE));
64 if (pMemDisk->pTreeSegments)
65 *ppMemDisk = pMemDisk;
66 else
67 {
68 RTMemFree(pMemDisk);
69 rc = VERR_NO_MEMORY;
70 }
71 }
72 else
73 rc = VERR_NO_MEMORY;
74
75 LogFlowFunc(("returns rc=%Rrc\n", rc));
76 return rc;
77}
78
79static int vdMemDiskDestroy(PAVLRU64NODECORE pNode, void *pvUser)
80{
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"));
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);
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"));
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 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 return VERR_NOT_IMPLEMENTED;
344}
345
346int VDMemDiskCmp(PVDMEMDISK pMemDisk, uint64_t off, size_t cbCmp, PRTSGBUF pSgBuf)
347{
348 LogFlowFunc(("pMemDisk=%#p off=%llx cbCmp=%u pSgBuf=%#p\n",
349 pMemDisk, off, cbCmp, pSgBuf));
350
351 /* Compare data */
352 size_t cbLeft = cbCmp;
353 uint64_t offCurr = off;
354
355 while (cbLeft)
356 {
357 PVDMEMDISKSEG pSeg = (PVDMEMDISKSEG)RTAvlrU64Get(pMemDisk->pTreeSegments, offCurr);
358 size_t cbRange = 0;
359 bool fCmp = false;
360 unsigned offSeg = 0;
361
362 if (!pSeg)
363 {
364 /* Get next segment */
365 pSeg = (PVDMEMDISKSEG)RTAvlrU64GetBestFit(pMemDisk->pTreeSegments, offCurr, true);
366 if (!pSeg)
367 {
368 /* No data in the tree for this read. Assume everything is ok. */
369 cbRange = cbLeft;
370 }
371 else if (offCurr + cbLeft <= pSeg->Core.Key)
372 cbRange = cbLeft;
373 else
374 cbRange = pSeg->Core.Key - offCurr;
375 }
376 else
377 {
378 fCmp = true;
379 offSeg = offCurr - pSeg->Core.Key;
380 cbRange = RT_MIN(cbLeft, (size_t)(pSeg->Core.KeyLast + 1 - offCurr));
381 }
382
383 if (fCmp)
384 {
385 RTSGSEG Seg;
386 RTSGBUF SgBufCmp;
387 size_t cbOff = 0;
388 int rc = 0;
389
390 Seg.cbSeg = cbRange;
391 Seg.pvSeg = (uint8_t *)pSeg->pvSeg + offSeg;
392
393 RTSgBufInit(&SgBufCmp, &Seg, 1);
394 rc = RTSgBufCmpEx(pSgBuf, &SgBufCmp, cbRange, &cbOff, true);
395 if (rc)
396 return rc;
397 }
398 else
399 RTSgBufAdvance(pSgBuf, cbRange);
400
401 offCurr += cbRange;
402 cbLeft -= cbRange;
403 }
404
405 return 0;
406}
407
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