VirtualBox

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

Last change on this file since 36140 was 36134, checked in by vboxsync, 14 years ago

tstVDIo: Add simple data verification.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 10.9 KB
Line 
1/** $Id: VDMemDisk.cpp 36134 2011-03-02 23:31:06Z 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 if (pSeg->Core.Key < cbSize)
253 {
254 /* Cut off the part which is not in the file anymore. */
255 pSeg->pvSeg = RTMemRealloc(pSeg->pvSeg, pSeg->Core.KeyLast - cbSize + 1);
256 }
257 else
258 {
259 /* Free the whole block. */
260 RTMemFree(pSeg->pvSeg);
261 RTMemFree(pSeg);
262 }
263 }
264
265 /* Kill all blocks coming after. */
266 do
267 {
268 pSeg = (PVDMEMDISKSEG)RTAvlrU64GetBestFit(pMemDisk->pTreeSegments, cbSize, true);
269 if (pSeg)
270 {
271 RTAvlrU64Remove(pMemDisk->pTreeSegments, pSeg->Core.Key);
272 RTMemFree(pSeg->pvSeg);
273 pSeg->pvSeg = NULL;
274 RTMemFree(pSeg);
275 }
276 else
277 break;
278 } while (true);
279
280 pMemDisk->cbDisk = cbSize;
281 }
282
283 return VINF_SUCCESS;
284}
285
286int VDMemDiskGetSize(PVDMEMDISK pMemDisk, uint64_t *pcbSize)
287{
288 AssertPtrReturn(pMemDisk, VERR_INVALID_POINTER);
289 AssertPtrReturn(pcbSize, VERR_INVALID_POINTER);
290
291 *pcbSize = pMemDisk->cbDisk;
292 return VINF_SUCCESS;
293}
294
295/**
296 * Writes a segment to the given file.
297 *
298 * @returns IPRT status code.
299 *
300 * @param pNode The disk segment to write to the file.
301 * @param pvParam Opaque user data containing the pointer to
302 * the file handle.
303 */
304static int vdMemDiskSegmentWriteToFile(PAVLRU64NODECORE pNode, void *pvParam)
305{
306 PVDMEMDISKSEG pSeg = (PVDMEMDISKSEG)pNode;
307 RTFILE hFile = *(PRTFILE)pvParam;
308
309 return RTFileWriteAt(hFile, pSeg->Core.Key, pSeg->pvSeg, pSeg->Core.KeyLast - pSeg->Core.Key + 1, NULL);
310}
311
312int VDMemDiskWriteToFile(PVDMEMDISK pMemDisk, const char *pcszFilename)
313{
314 int rc = VINF_SUCCESS;
315 RTFILE hFile = NIL_RTFILE;
316
317 LogFlowFunc(("pMemDisk=%#p pcszFilename=%s\n", pMemDisk, pcszFilename));
318 AssertPtrReturn(pMemDisk, VERR_INVALID_POINTER);
319 AssertPtrReturn(pcszFilename, VERR_INVALID_POINTER);
320
321 rc = RTFileOpen(&hFile, pcszFilename, RTFILE_O_DENY_NONE | RTFILE_O_CREATE | RTFILE_O_WRITE);
322 if (RT_SUCCESS(rc))
323 {
324 rc = RTAvlrU64DoWithAll(pMemDisk->pTreeSegments, true, vdMemDiskSegmentWriteToFile, &hFile);
325
326 RTFileClose(hFile);
327 if (RT_FAILURE(rc))
328 RTFileDelete(pcszFilename);
329 }
330
331 LogFlowFunc(("returns rc=%Rrc\n", rc));
332 return rc;
333}
334
335int VDMemDiskReadFromFile(PVDMEMDISK pMemDisk, const char *pcszFilename)
336{
337 return VERR_NOT_IMPLEMENTED;
338}
339
340int VDMemDiskCmp(PVDMEMDISK pMemDisk, uint64_t off, size_t cbCmp, PRTSGBUF pSgBuf)
341{
342 LogFlowFunc(("pMemDisk=%#p off=%llx cbCmp=%u pSgBuf=%#p\n",
343 pMemDisk, off, cbCmp, pSgBuf));
344
345 /* Compare data */
346 size_t cbLeft = cbCmp;
347 uint64_t offCurr = off;
348
349 while (cbLeft)
350 {
351 PVDMEMDISKSEG pSeg = (PVDMEMDISKSEG)RTAvlrU64Get(pMemDisk->pTreeSegments, offCurr);
352 size_t cbRange = 0;
353 bool fCmp = false;
354 unsigned offSeg = 0;
355
356 if (!pSeg)
357 {
358 /* Get next segment */
359 pSeg = (PVDMEMDISKSEG)RTAvlrU64GetBestFit(pMemDisk->pTreeSegments, offCurr, true);
360 if (!pSeg)
361 {
362 /* No data in the tree for this read. Assume everything is ok. */
363 cbRange = cbLeft;
364 }
365 else if (offCurr + cbLeft <= pSeg->Core.Key)
366 cbRange = cbLeft;
367 else
368 cbRange = pSeg->Core.Key - offCurr;
369 }
370 else
371 {
372 fCmp = true;
373 offSeg = offCurr - pSeg->Core.Key;
374 cbRange = RT_MIN(cbLeft, (size_t)(pSeg->Core.KeyLast + 1 - offCurr));
375 }
376
377 if (fCmp)
378 {
379 RTSGSEG Seg;
380 RTSGBUF SgBufCmp;
381 size_t cbOff = 0;
382 int rc = 0;
383
384 Seg.cbSeg = cbRange;
385 Seg.pvSeg = (uint8_t *)pSeg->pvSeg + offSeg;
386
387 RTSgBufInit(&SgBufCmp, &Seg, 1);
388 rc = RTSgBufCmpEx(pSgBuf, &SgBufCmp, cbRange, &cbOff, true);
389 if (rc)
390 return rc;
391 }
392 else
393 RTSgBufAdvance(pSgBuf, cbRange);
394
395 offCurr += cbRange;
396 cbLeft -= cbRange;
397 }
398
399 return 0;
400}
401
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