VirtualBox

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

Last change on this file since 63490 was 62733, checked in by vboxsync, 8 years ago

Storage: warnings.

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