VirtualBox

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

Last change on this file since 40739 was 36635, checked in by vboxsync, 14 years ago

tstVDIo: Introduce I/O patterns to fill images with certain data. Added a simple testcase for compacting which uses patterns to fill certain areas of the image with 0

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 11.2 KB
Line 
1/** $Id: VDMemDisk.cpp 36635 2011-04-08 23:25:37Z 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);
93}
94
95int VDMemDiskWrite(PVDMEMDISK pMemDisk, uint64_t off, size_t cbWrite, PRTSGBUF pSgBuf)
96{
97 int rc = VINF_SUCCESS;
98
99 LogFlowFunc(("pMemDisk=%#p off=%llu cbWrite=%zu pSgBuf=%#p\n",
100 pMemDisk, off, cbWrite, pSgBuf));
101
102 AssertPtrReturn(pMemDisk, VERR_INVALID_POINTER);
103 AssertPtrReturn(pSgBuf, VERR_INVALID_POINTER);
104
105 /* Check for a write beyond the end of a disk. */
106 if ( !pMemDisk->fGrowable
107 && (off + cbWrite) > pMemDisk->cbDisk)
108 return VERR_INVALID_PARAMETER;
109
110 /* Update the segments */
111 size_t cbLeft = cbWrite;
112 uint64_t offCurr = off;
113
114 while ( cbLeft
115 && RT_SUCCESS(rc))
116 {
117 PVDMEMDISKSEG pSeg = (PVDMEMDISKSEG)RTAvlrU64RangeGet(pMemDisk->pTreeSegments, offCurr);
118 size_t cbRange = 0;
119 unsigned offSeg = 0;
120
121 if (!pSeg)
122 {
123 /* Get next segment */
124 pSeg = (PVDMEMDISKSEG)RTAvlrU64GetBestFit(pMemDisk->pTreeSegments, offCurr, true);
125 if ( !pSeg
126 || offCurr + cbLeft <= pSeg->Core.Key)
127 cbRange = cbLeft;
128 else
129 cbRange = pSeg->Core.Key - offCurr;
130
131 /* Create new segment */
132 pSeg = (PVDMEMDISKSEG)RTMemAllocZ(sizeof(VDMEMDISKSEG));
133 if (pSeg)
134 {
135 pSeg->Core.Key = offCurr;
136 pSeg->Core.KeyLast = offCurr + cbRange - 1;
137 pSeg->pvSeg = RTMemAllocZ(cbRange);
138
139 if (!pSeg->pvSeg)
140 {
141 RTMemFree(pSeg);
142 rc = VERR_NO_MEMORY;
143 }
144 else
145 {
146 bool fInserted = RTAvlrU64Insert(pMemDisk->pTreeSegments, &pSeg->Core);
147 AssertMsg(fInserted, ("Bug!\n"));
148 }
149 }
150 else
151 rc = VERR_NO_MEMORY;
152 }
153 else
154 {
155 offSeg = offCurr - pSeg->Core.Key;
156 cbRange = RT_MIN(cbLeft, (size_t)(pSeg->Core.KeyLast + 1 - offCurr));
157 }
158
159 if (RT_SUCCESS(rc))
160 {
161 AssertPtr(pSeg);
162 size_t cbCopied = RTSgBufCopyToBuf(pSgBuf, (uint8_t *)pSeg->pvSeg + offSeg, cbRange);
163 Assert(cbCopied == cbRange);
164 }
165
166 offCurr += cbRange;
167 cbLeft -= cbRange;
168 }
169
170 /* Update size of the disk. */
171 if ( RT_SUCCESS(rc)
172 && pMemDisk->fGrowable
173 && (off + cbWrite) > pMemDisk->cbDisk)
174 {
175 pMemDisk->cbDisk = off + cbWrite;
176 }
177
178 return rc;
179}
180
181
182int VDMemDiskRead(PVDMEMDISK pMemDisk, uint64_t off, size_t cbRead, PRTSGBUF pSgBuf)
183{
184 LogFlowFunc(("pMemDisk=%#p off=%llu cbRead=%zu pSgBuf=%#p\n",
185 pMemDisk, off, cbRead, pSgBuf));
186
187 AssertPtrReturn(pMemDisk, VERR_INVALID_POINTER);
188 AssertPtrReturn(pSgBuf, VERR_INVALID_POINTER);
189
190 /* Check for a read beyond the end of a disk. */
191 if ((off + cbRead) > pMemDisk->cbDisk)
192 return VERR_INVALID_PARAMETER;
193
194 /* Compare read data */
195 size_t cbLeft = cbRead;
196 uint64_t offCurr = off;
197
198 while (cbLeft)
199 {
200 PVDMEMDISKSEG pSeg = (PVDMEMDISKSEG)RTAvlrU64RangeGet(pMemDisk->pTreeSegments, offCurr);
201 size_t cbRange = 0;
202 unsigned offSeg = 0;
203
204 if (!pSeg)
205 {
206 /* Get next segment */
207 pSeg = (PVDMEMDISKSEG)RTAvlrU64GetBestFit(pMemDisk->pTreeSegments, offCurr, true);
208 if ( !pSeg
209 || offCurr + cbLeft <= pSeg->Core.Key)
210 {
211 /* No data in the tree for this read. Fill with 0. */
212 cbRange = cbLeft;
213 }
214 else
215 cbRange = pSeg->Core.Key - offCurr;
216
217 RTSgBufSet(pSgBuf, 0, cbRange);
218 }
219 else
220 {
221 offSeg = offCurr - pSeg->Core.Key;
222 cbRange = RT_MIN(cbLeft, (size_t)(pSeg->Core.KeyLast + 1 - offCurr));
223
224 RTSgBufCopyFromBuf(pSgBuf, (uint8_t *)pSeg->pvSeg + offSeg, cbRange);
225 }
226
227 offCurr += cbRange;
228 cbLeft -= cbRange;
229 }
230
231 return VINF_SUCCESS;
232}
233
234int VDMemDiskSetSize(PVDMEMDISK pMemDisk, uint64_t cbSize)
235{
236 AssertPtrReturn(pMemDisk, VERR_INVALID_POINTER);
237
238 if (!pMemDisk->fGrowable)
239 return VERR_NOT_SUPPORTED;
240
241 if (pMemDisk->cbDisk <= cbSize)
242 {
243 /* Increase. */
244 pMemDisk->cbDisk = cbSize;
245 }
246 else
247 {
248 /* We have to delete all parts beyond the new end. */
249 PVDMEMDISKSEG pSeg = (PVDMEMDISKSEG)RTAvlrU64Get(pMemDisk->pTreeSegments, cbSize);
250 if (pSeg)
251 {
252 RTAvlrU64Remove(pMemDisk->pTreeSegments, pSeg->Core.Key);
253 if (pSeg->Core.Key < cbSize)
254 {
255 /* Cut off the part which is not in the file anymore. */
256 pSeg->pvSeg = RTMemRealloc(pSeg->pvSeg, pSeg->Core.KeyLast - cbSize + 1);
257 pSeg->Core.KeyLast = cbSize - pSeg->Core.Key - 1;
258
259 bool fInserted = RTAvlrU64Insert(pMemDisk->pTreeSegments, &pSeg->Core);
260 AssertMsg(fInserted, ("Bug!\n"));
261 }
262 else
263 {
264 /* Free the whole block. */
265 RTMemFree(pSeg->pvSeg);
266 RTMemFree(pSeg);
267 }
268 }
269
270 /* Kill all blocks coming after. */
271 do
272 {
273 pSeg = (PVDMEMDISKSEG)RTAvlrU64GetBestFit(pMemDisk->pTreeSegments, cbSize, true);
274 if (pSeg)
275 {
276 RTAvlrU64Remove(pMemDisk->pTreeSegments, pSeg->Core.Key);
277 RTMemFree(pSeg->pvSeg);
278 pSeg->pvSeg = NULL;
279 RTMemFree(pSeg);
280 }
281 else
282 break;
283 } while (true);
284
285 pMemDisk->cbDisk = cbSize;
286 }
287
288 return VINF_SUCCESS;
289}
290
291int VDMemDiskGetSize(PVDMEMDISK pMemDisk, uint64_t *pcbSize)
292{
293 AssertPtrReturn(pMemDisk, VERR_INVALID_POINTER);
294 AssertPtrReturn(pcbSize, VERR_INVALID_POINTER);
295
296 *pcbSize = pMemDisk->cbDisk;
297 return VINF_SUCCESS;
298}
299
300/**
301 * Writes a segment to the given file.
302 *
303 * @returns IPRT status code.
304 *
305 * @param pNode The disk segment to write to the file.
306 * @param pvParam Opaque user data containing the pointer to
307 * the file handle.
308 */
309static int vdMemDiskSegmentWriteToFile(PAVLRU64NODECORE pNode, void *pvParam)
310{
311 PVDMEMDISKSEG pSeg = (PVDMEMDISKSEG)pNode;
312 RTFILE hFile = *(PRTFILE)pvParam;
313
314 return RTFileWriteAt(hFile, pSeg->Core.Key, pSeg->pvSeg, pSeg->Core.KeyLast - pSeg->Core.Key + 1, NULL);
315}
316
317int VDMemDiskWriteToFile(PVDMEMDISK pMemDisk, const char *pcszFilename)
318{
319 int rc = VINF_SUCCESS;
320 RTFILE hFile = NIL_RTFILE;
321
322 LogFlowFunc(("pMemDisk=%#p pcszFilename=%s\n", pMemDisk, pcszFilename));
323 AssertPtrReturn(pMemDisk, VERR_INVALID_POINTER);
324 AssertPtrReturn(pcszFilename, VERR_INVALID_POINTER);
325
326 rc = RTFileOpen(&hFile, pcszFilename, RTFILE_O_DENY_NONE | RTFILE_O_CREATE | RTFILE_O_WRITE);
327 if (RT_SUCCESS(rc))
328 {
329 rc = RTAvlrU64DoWithAll(pMemDisk->pTreeSegments, true, vdMemDiskSegmentWriteToFile, &hFile);
330
331 RTFileClose(hFile);
332 if (RT_FAILURE(rc))
333 RTFileDelete(pcszFilename);
334 }
335
336 LogFlowFunc(("returns rc=%Rrc\n", rc));
337 return rc;
338}
339
340int VDMemDiskReadFromFile(PVDMEMDISK pMemDisk, const char *pcszFilename)
341{
342 return VERR_NOT_IMPLEMENTED;
343}
344
345int VDMemDiskCmp(PVDMEMDISK pMemDisk, uint64_t off, size_t cbCmp, PRTSGBUF pSgBuf)
346{
347 LogFlowFunc(("pMemDisk=%#p off=%llx cbCmp=%u pSgBuf=%#p\n",
348 pMemDisk, off, cbCmp, pSgBuf));
349
350 /* Compare data */
351 size_t cbLeft = cbCmp;
352 uint64_t offCurr = off;
353
354 while (cbLeft)
355 {
356 PVDMEMDISKSEG pSeg = (PVDMEMDISKSEG)RTAvlrU64Get(pMemDisk->pTreeSegments, offCurr);
357 size_t cbRange = 0;
358 bool fCmp = false;
359 unsigned offSeg = 0;
360
361 if (!pSeg)
362 {
363 /* Get next segment */
364 pSeg = (PVDMEMDISKSEG)RTAvlrU64GetBestFit(pMemDisk->pTreeSegments, offCurr, true);
365 if (!pSeg)
366 {
367 /* No data in the tree for this read. Assume everything is ok. */
368 cbRange = cbLeft;
369 }
370 else if (offCurr + cbLeft <= pSeg->Core.Key)
371 cbRange = cbLeft;
372 else
373 cbRange = pSeg->Core.Key - offCurr;
374 }
375 else
376 {
377 fCmp = true;
378 offSeg = offCurr - pSeg->Core.Key;
379 cbRange = RT_MIN(cbLeft, (size_t)(pSeg->Core.KeyLast + 1 - offCurr));
380 }
381
382 if (fCmp)
383 {
384 RTSGSEG Seg;
385 RTSGBUF SgBufCmp;
386 size_t cbOff = 0;
387 int rc = 0;
388
389 Seg.cbSeg = cbRange;
390 Seg.pvSeg = (uint8_t *)pSeg->pvSeg + offSeg;
391
392 RTSgBufInit(&SgBufCmp, &Seg, 1);
393 rc = RTSgBufCmpEx(pSgBuf, &SgBufCmp, cbRange, &cbOff, true);
394 if (rc)
395 return rc;
396 }
397 else
398 RTSgBufAdvance(pSgBuf, cbRange);
399
400 offCurr += cbRange;
401 cbLeft -= cbRange;
402 }
403
404 return 0;
405}
406
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