VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/testcase/tstVD.cpp@ 16334

Last change on this file since 16334 was 15591, checked in by vboxsync, 16 years ago

Storage/VBoxHDD-new: introduce parent UUID parameter for VDCreateDiff. Makes creation of iSCSI snapshots easier.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 33.3 KB
Line 
1/** @file
2 *
3 * Simple VBox HDD container test utility.
4 */
5
6/*
7 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22#include <VBox/VBoxHDD-new.h>
23#include <VBox/err.h>
24#include <VBox/log.h>
25#include <iprt/asm.h>
26#include <iprt/dir.h>
27#include <iprt/string.h>
28#include <iprt/stream.h>
29#include <iprt/file.h>
30#include <iprt/mem.h>
31#include <iprt/initterm.h>
32#include <iprt/rand.h>
33#include "stdio.h"
34#include "stdlib.h"
35
36#define VHD_TEST
37#define VDI_TEST
38#define VMDK_TEST
39
40/*******************************************************************************
41* Global Variables *
42*******************************************************************************/
43/** The error count. */
44unsigned g_cErrors = 0;
45
46
47static void tstVDError(void *pvUser, int rc, RT_SRC_POS_DECL,
48 const char *pszFormat, va_list va)
49{
50 g_cErrors++;
51 RTPrintf("tstVD: Error %Rrc at %s:%u (%s): ", rc, RT_SRC_POS_ARGS);
52 RTPrintfV(pszFormat, va);
53 RTPrintf("\n");
54}
55
56
57static int tstVDCreateDelete(const char *pszBackend, const char *pszFilename,
58 uint64_t cbSize, VDIMAGETYPE enmType,
59 unsigned uFlags, bool fDelete)
60{
61 int rc;
62 PVBOXHDD pVD = NULL;
63 PDMMEDIAGEOMETRY PCHS = { 0, 0, 0 };
64 PDMMEDIAGEOMETRY LCHS = { 0, 0, 0 };
65 PVDINTERFACE pVDIfs = NULL;
66 VDINTERFACE VDIError;
67 VDINTERFACEERROR VDIErrorCallbacks;
68
69#define CHECK(str) \
70 do \
71 { \
72 RTPrintf("%s rc=%Rrc\n", str, rc); \
73 if (RT_FAILURE(rc)) \
74 { \
75 VDDestroy(pVD); \
76 return rc; \
77 } \
78 } while (0)
79
80 /* Create error interface. */
81 VDIErrorCallbacks.cbSize = sizeof(VDINTERFACEERROR);
82 VDIErrorCallbacks.enmInterface = VDINTERFACETYPE_ERROR;
83 VDIErrorCallbacks.pfnError = tstVDError;
84
85 rc = VDInterfaceAdd(&VDIError, "tstVD_Error", VDINTERFACETYPE_ERROR, &VDIErrorCallbacks,
86 NULL, &pVDIfs);
87 AssertRC(rc);
88
89 rc = VDCreate(&VDIError, &pVD);
90 CHECK("VDCreate()");
91
92 rc = VDCreateBase(pVD, pszBackend, pszFilename, enmType, cbSize,
93 uFlags, "Test image", &PCHS, &LCHS, NULL,
94 VD_OPEN_FLAGS_NORMAL, NULL, NULL);
95 CHECK("VDCreateBase()");
96
97 VDDumpImages(pVD);
98
99 VDClose(pVD, fDelete);
100 if (fDelete)
101 {
102 RTFILE File;
103 rc = RTFileOpen(&File, pszFilename, RTFILE_O_READ);
104 if (RT_SUCCESS(rc))
105 {
106 RTFileClose(File);
107 return VERR_INTERNAL_ERROR;
108 }
109 }
110
111 VDDestroy(pVD);
112#undef CHECK
113 return 0;
114}
115
116static int tstVDOpenDelete(const char *pszBackend, const char *pszFilename)
117{
118 int rc;
119 PVBOXHDD pVD = NULL;
120 PDMMEDIAGEOMETRY PCHS = { 0, 0, 0 };
121 PDMMEDIAGEOMETRY LCHS = { 0, 0, 0 };
122 PVDINTERFACE pVDIfs = NULL;
123 VDINTERFACE VDIError;
124 VDINTERFACEERROR VDIErrorCallbacks;
125
126#define CHECK(str) \
127 do \
128 { \
129 RTPrintf("%s rc=%Rrc\n", str, rc); \
130 if (RT_FAILURE(rc)) \
131 { \
132 VDDestroy(pVD); \
133 return rc; \
134 } \
135 } while (0)
136
137 /* Create error interface. */
138 VDIErrorCallbacks.cbSize = sizeof(VDINTERFACEERROR);
139 VDIErrorCallbacks.enmInterface = VDINTERFACETYPE_ERROR;
140 VDIErrorCallbacks.pfnError = tstVDError;
141
142 rc = VDInterfaceAdd(&VDIError, "tstVD_Error", VDINTERFACETYPE_ERROR, &VDIErrorCallbacks,
143 NULL, &pVDIfs);
144 AssertRC(rc);
145
146 rc = VDCreate(&VDIError, &pVD);
147 CHECK("VDCreate()");
148
149 rc = VDOpen(pVD, pszBackend, pszFilename, VD_OPEN_FLAGS_NORMAL, NULL);
150 CHECK("VDOpen()");
151
152 VDDumpImages(pVD);
153
154 VDClose(pVD, true);
155 RTFILE File;
156 rc = RTFileOpen(&File, pszFilename, RTFILE_O_READ);
157 if (RT_SUCCESS(rc))
158 {
159 RTFileClose(File);
160 return VERR_INTERNAL_ERROR;
161 }
162
163 VDDestroy(pVD);
164#undef CHECK
165 return 0;
166}
167
168
169#undef RTDECL
170#define RTDECL(x) static x
171
172/* Start of IPRT code */
173
174/**
175 * The following code is based on the work of George Marsaglia
176 * taken from
177 * http://groups.google.ws/group/comp.sys.sun.admin/msg/7c667186f6cbf354
178 * and
179 * http://groups.google.ws/group/comp.lang.c/msg/0e170777c6e79e8d
180 */
181
182/*
183A C version of a very very good 64-bit RNG is given below.
184You should be able to adapt it to your particular needs.
185
186It is based on the complimentary-multiple-with-carry
187sequence
188 x(n)=a*x(n-4)+carry mod 2^64-1,
189which works as follows:
190Assume a certain multiplier 'a' and a base 'b'.
191Given a current x value and a current carry 'c',
192form: t=a*x+c
193Then the new carry is c=floor(t/b)
194and the new x value is x = b-1-(t mod b).
195
196
197Ordinarily, for 32-bit mwc or cmwc sequences, the
198value t=a*x+c can be formed in 64 bits, then the new c
199is the top and the new x the bottom 32 bits (with a little
200fiddling when b=2^32-1 and cmwc rather than mwc.)
201
202
203To generate 64-bit x's, it is difficult to form
204t=a*x+c in 128 bits then get the new c and new x
205from the top and bottom halves.
206But if 'a' has a special form, for example,
207a=2^62+2^47+2 and b=2^64-1, then the new c and
208the new x can be formed with shifts, tests and +/-'s,
209again with a little fiddling because b=2^64-1 rather
210than 2^64. (The latter is not an optimal choice because,
211being a square, it cannot be a primitive root of the
212prime a*b^k+1, where 'k' is the 'lag':
213 x(n)=a*x(n-k)+carry mod b.)
214But the multiplier a=2^62+2^47+2 makes a*b^4+1 a prime for
215which b=2^64-1 is a primitive root, and getting the new x and
216new c can be done with arithmetic on integers the size of x.
217*/
218
219struct RndCtx
220{
221 uint64_t x;
222 uint64_t y;
223 uint64_t z;
224 uint64_t w;
225 uint64_t c;
226 uint32_t u32x;
227 uint32_t u32y;
228};
229typedef struct RndCtx RNDCTX;
230typedef RNDCTX *PRNDCTX;
231
232/**
233 * Initialize seeds.
234 *
235 * @remarks You should choose ANY 4 random 64-bit
236 * seeds x,y,z,w < 2^64-1 and a random seed c in
237 * 0<= c < a = 2^62+2^47+2.
238 * There are P=(2^62+2^46+2)*(2^64-1)^4 > 2^318 possible choices
239 * for seeds, the period of the RNG.
240 */
241RTDECL(int) RTPRandInit(PRNDCTX pCtx, uint32_t u32Seed)
242{
243 if (u32Seed == 0)
244 u32Seed = (uint32_t)(ASMReadTSC() >> 8);
245 /* Zero is not a good seed. */
246 if (u32Seed == 0)
247 u32Seed = 362436069;
248 pCtx->x = u32Seed;
249 pCtx->y = 17280675555674358941ULL;
250 pCtx->z = 6376492577913983186ULL;
251 pCtx->w = 9064188857900113776ULL;
252 pCtx->c = 123456789;
253 pCtx->u32x = 2282008;
254 pCtx->u32y = u32Seed;
255 return VINF_SUCCESS;
256}
257
258RTDECL(uint32_t) RTPRandGetSeedInfo(PRNDCTX pCtx)
259{
260 return pCtx->u32y;
261}
262
263/**
264 * Generate a 64-bit unsigned random number.
265 *
266 * @returns The pseudo random number.
267 */
268RTDECL(uint64_t) RTPRandU64(PRNDCTX pCtx)
269{
270 uint64_t t;
271 t = (pCtx->x<<47) + (pCtx->x<<62) + (pCtx->x<<1);
272 t += pCtx->c; t+= (t < pCtx->c);
273 pCtx->c = (t<pCtx->c) + (pCtx->x>>17) + (pCtx->x>>2) + (pCtx->x>>63);
274 pCtx->x = pCtx->y; pCtx->y = pCtx->z ; pCtx->z = pCtx->w;
275 return (pCtx->w = ~(t + pCtx->c)-1);
276}
277
278/**
279 * Generate a 64-bit unsigned pseudo random number in the set
280 * [u64First..u64Last].
281 *
282 * @returns The pseudo random number.
283 * @param u64First First number in the set.
284 * @param u64Last Last number in the set.
285 */
286RTDECL(uint64_t) RTPRandU64Ex(PRNDCTX pCtx, uint64_t u64First, uint64_t u64Last)
287{
288 if (u64First == 0 && u64Last == UINT64_MAX)
289 return RTPRandU64(pCtx);
290
291 uint64_t u64Tmp;
292 uint64_t u64Range = u64Last - u64First + 1;
293 uint64_t u64Scale = UINT64_MAX / u64Range;
294
295 do
296 {
297 u64Tmp = RTPRandU64(pCtx) / u64Scale;
298 } while (u64Tmp >= u64Range);
299 return u64First + u64Tmp;
300}
301
302/**
303 * Generate a 32-bit unsigned random number.
304 *
305 * @returns The pseudo random number.
306 */
307RTDECL(uint32_t) RTPRandU32(PRNDCTX pCtx)
308{
309 return ( pCtx->u32x = 69069 * pCtx->u32x + 123,
310 pCtx->u32y ^= pCtx->u32y<<13,
311 pCtx->u32y ^= pCtx->u32y>>17,
312 pCtx->u32y ^= pCtx->u32y<<5,
313 pCtx->u32x + pCtx->u32y );
314}
315
316/**
317 * Generate a 32-bit unsigned pseudo random number in the set
318 * [u32First..u32Last].
319 *
320 * @returns The pseudo random number.
321 * @param u32First First number in the set.
322 * @param u32Last Last number in the set.
323 */
324RTDECL(uint32_t) RTPRandU32Ex(PRNDCTX pCtx, uint32_t u32First, uint32_t u32Last)
325{
326 if (u32First == 0 && u32Last == UINT32_MAX)
327 return RTPRandU32(pCtx);
328
329 uint32_t u32Tmp;
330 uint32_t u32Range = u32Last - u32First + 1;
331 uint32_t u32Scale = UINT32_MAX / u32Range;
332
333 do
334 {
335 u32Tmp = RTPRandU32(pCtx) / u32Scale;
336 } while (u32Tmp >= u32Range);
337 return u32First + u32Tmp;
338}
339
340/* End of IPRT code */
341
342struct Segment
343{
344 uint64_t u64Offset;
345 uint32_t u32Length;
346 uint32_t u8Value;
347};
348typedef struct Segment *PSEGMENT;
349
350static void initializeRandomGenerator(PRNDCTX pCtx, uint32_t u32Seed)
351{
352 int rc = RTPRandInit(pCtx, u32Seed);
353 if (RT_FAILURE(rc))
354 RTPrintf("ERROR: Failed to initialize random generator. RC=%Rrc\n", rc);
355 else
356 {
357 RTPrintf("INFO: Random generator seed used: %x\n", RTPRandGetSeedInfo(pCtx));
358 RTLogPrintf("INFO: Random generator seed used: %x\n", RTPRandGetSeedInfo(pCtx));
359 }
360}
361
362static int compareSegments(const void *left, const void *right)
363{
364 /* Note that no duplicates are allowed in the array being sorted. */
365 return ((PSEGMENT)left)->u64Offset < ((PSEGMENT)right)->u64Offset ? -1 : 1;
366}
367
368static void generateRandomSegments(PRNDCTX pCtx, PSEGMENT pSegment, uint32_t nSegments, uint32_t u32MaxSegmentSize, uint64_t u64DiskSize, uint32_t u32SectorSize, uint8_t u8ValueLow, uint8_t u8ValueHigh)
369{
370 uint32_t i;
371 /* Generate segment offsets. */
372 for (i = 0; i < nSegments; i++)
373 {
374 bool fDuplicateFound;
375 do
376 {
377 pSegment[i].u64Offset = RTPRandU64Ex(pCtx, 0, u64DiskSize / u32SectorSize - 1) * u32SectorSize;
378 fDuplicateFound = false;
379 for (uint32_t j = 0; j < i; j++)
380 if (pSegment[i].u64Offset == pSegment[j].u64Offset)
381 {
382 fDuplicateFound = true;
383 break;
384 }
385 } while (fDuplicateFound);
386 }
387 /* Sort in offset-ascending order. */
388 qsort(pSegment, nSegments, sizeof(*pSegment), compareSegments);
389 /* Put a sentinel at the end. */
390 pSegment[nSegments].u64Offset = u64DiskSize;
391 pSegment[nSegments].u32Length = 0;
392 /* Generate segment lengths and values. */
393 for (i = 0; i < nSegments; i++)
394 {
395 pSegment[i].u32Length = RTPRandU32Ex(pCtx, 1, RT_MIN(pSegment[i+1].u64Offset - pSegment[i].u64Offset,
396 u32MaxSegmentSize) / u32SectorSize) * u32SectorSize;
397 Assert(pSegment[i].u32Length <= u32MaxSegmentSize);
398 pSegment[i].u8Value = RTPRandU32Ex(pCtx, (uint32_t)u8ValueLow, (uint32_t)u8ValueHigh);
399 }
400}
401
402static void mergeSegments(PSEGMENT pBaseSegment, PSEGMENT pDiffSegment, PSEGMENT pMergeSegment, uint32_t u32MaxLength)
403{
404 while (pBaseSegment->u32Length > 0 || pDiffSegment->u32Length > 0)
405 {
406 if (pBaseSegment->u64Offset < pDiffSegment->u64Offset)
407 {
408 *pMergeSegment = *pBaseSegment;
409 if (pMergeSegment->u64Offset + pMergeSegment->u32Length <= pDiffSegment->u64Offset)
410 pBaseSegment++;
411 else
412 {
413 pMergeSegment->u32Length = pDiffSegment->u64Offset - pMergeSegment->u64Offset;
414 Assert(pMergeSegment->u32Length <= u32MaxLength);
415 if (pBaseSegment->u64Offset + pBaseSegment->u32Length >
416 pDiffSegment->u64Offset + pDiffSegment->u32Length)
417 {
418 pBaseSegment->u32Length -= pDiffSegment->u64Offset + pDiffSegment->u32Length - pBaseSegment->u64Offset;
419 Assert(pBaseSegment->u32Length <= u32MaxLength);
420 pBaseSegment->u64Offset = pDiffSegment->u64Offset + pDiffSegment->u32Length;
421 }
422 else
423 pBaseSegment++;
424 }
425 pMergeSegment++;
426 }
427 else
428 {
429 *pMergeSegment = *pDiffSegment;
430 if (pMergeSegment->u64Offset + pMergeSegment->u32Length <= pBaseSegment->u64Offset)
431 {
432 pDiffSegment++;
433 pMergeSegment++;
434 }
435 else
436 {
437 if (pBaseSegment->u64Offset + pBaseSegment->u32Length > pDiffSegment->u64Offset + pDiffSegment->u32Length)
438 {
439 pBaseSegment->u32Length -= pDiffSegment->u64Offset + pDiffSegment->u32Length - pBaseSegment->u64Offset;
440 Assert(pBaseSegment->u32Length <= u32MaxLength);
441 pBaseSegment->u64Offset = pDiffSegment->u64Offset + pDiffSegment->u32Length;
442 pDiffSegment++;
443 pMergeSegment++;
444 }
445 else
446 pBaseSegment++;
447 }
448 }
449 }
450}
451
452static void writeSegmentsToDisk(PVBOXHDD pVD, void *pvBuf, PSEGMENT pSegment)
453{
454 while (pSegment->u32Length)
455 {
456 //memset((uint8_t*)pvBuf + pSegment->u64Offset, pSegment->u8Value, pSegment->u32Length);
457 memset(pvBuf, pSegment->u8Value, pSegment->u32Length);
458 VDWrite(pVD, pSegment->u64Offset, pvBuf, pSegment->u32Length);
459 pSegment++;
460 }
461}
462
463static int readAndCompareSegments(PVBOXHDD pVD, void *pvBuf, PSEGMENT pSegment)
464{
465 while (pSegment->u32Length)
466 {
467 int rc = VDRead(pVD, pSegment->u64Offset, pvBuf, pSegment->u32Length);
468 if (RT_FAILURE(rc))
469 {
470 RTPrintf("ERROR: Failed to read from virtual disk\n");
471 return rc;
472 }
473 else
474 {
475 for (unsigned i = 0; i < pSegment->u32Length; i++)
476 if (((uint8_t*)pvBuf)[i] != pSegment->u8Value)
477 {
478 RTPrintf("ERROR: Segment at %Lx of %x bytes is corrupt at offset %x (found %x instead of %x)\n",
479 pSegment->u64Offset, pSegment->u32Length, i, ((uint8_t*)pvBuf)[i],
480 pSegment->u8Value);
481 RTLogPrintf("ERROR: Segment at %Lx of %x bytes is corrupt at offset %x (found %x instead of %x)\n",
482 pSegment->u64Offset, pSegment->u32Length, i, ((uint8_t*)pvBuf)[i],
483 pSegment->u8Value);
484 return VERR_INTERNAL_ERROR;
485 }
486 }
487 pSegment++;
488 }
489
490 return VINF_SUCCESS;
491}
492
493static int tstVDOpenCreateWriteMerge(const char *pszBackend,
494 const char *pszBaseFilename,
495 const char *pszDiffFilename,
496 uint32_t u32Seed)
497{
498 int rc;
499 PVBOXHDD pVD = NULL;
500 char *pszFormat;
501 PDMMEDIAGEOMETRY PCHS = { 0, 0, 0 };
502 PDMMEDIAGEOMETRY LCHS = { 0, 0, 0 };
503 uint64_t u64DiskSize = 1000 * _1M;
504 uint32_t u32SectorSize = 512;
505 PVDINTERFACE pVDIfs = NULL;
506 VDINTERFACE VDIError;
507 VDINTERFACEERROR VDIErrorCallbacks;
508
509#define CHECK(str) \
510 do \
511 { \
512 RTPrintf("%s rc=%Rrc\n", str, rc); \
513 if (RT_FAILURE(rc)) \
514 { \
515 if (pvBuf) \
516 RTMemFree(pvBuf); \
517 VDDestroy(pVD); \
518 return rc; \
519 } \
520 } while (0)
521
522 void *pvBuf = RTMemAlloc(_1M);
523
524 /* Create error interface. */
525 VDIErrorCallbacks.cbSize = sizeof(VDINTERFACEERROR);
526 VDIErrorCallbacks.enmInterface = VDINTERFACETYPE_ERROR;
527 VDIErrorCallbacks.pfnError = tstVDError;
528
529 rc = VDInterfaceAdd(&VDIError, "tstVD_Error", VDINTERFACETYPE_ERROR, &VDIErrorCallbacks,
530 NULL, &pVDIfs);
531 AssertRC(rc);
532
533
534 rc = VDCreate(&VDIError, &pVD);
535 CHECK("VDCreate()");
536
537 RTFILE File;
538 rc = RTFileOpen(&File, pszBaseFilename, RTFILE_O_READ);
539 if (RT_SUCCESS(rc))
540 {
541 RTFileClose(File);
542 rc = VDGetFormat(pszBaseFilename, &pszFormat);
543 RTPrintf("VDGetFormat() pszFormat=%s rc=%Rrc\n", pszFormat, rc);
544 if (RT_SUCCESS(rc) && strcmp(pszFormat, pszBackend))
545 {
546 rc = VERR_GENERAL_FAILURE;
547 RTPrintf("VDGetFormat() returned incorrect backend name\n");
548 }
549 RTStrFree(pszFormat);
550 CHECK("VDGetFormat()");
551
552 rc = VDOpen(pVD, pszBackend, pszBaseFilename, VD_OPEN_FLAGS_NORMAL,
553 NULL);
554 CHECK("VDOpen()");
555 }
556 else
557 {
558 rc = VDCreateBase(pVD, pszBackend, pszBaseFilename,
559 VD_IMAGE_TYPE_NORMAL, u64DiskSize,
560 VD_IMAGE_FLAGS_NONE, "Test image",
561 &PCHS, &LCHS, NULL, VD_OPEN_FLAGS_NORMAL,
562 NULL, NULL);
563 CHECK("VDCreateBase()");
564 }
565
566 int nSegments = 100;
567 /* Allocate one extra element for a sentinel. */
568 PSEGMENT paBaseSegments = (PSEGMENT)RTMemAllocZ(sizeof(struct Segment) * (nSegments + 1));
569 PSEGMENT paDiffSegments = (PSEGMENT)RTMemAllocZ(sizeof(struct Segment) * (nSegments + 1));
570 PSEGMENT paMergeSegments = (PSEGMENT)RTMemAllocZ(sizeof(struct Segment) * (nSegments + 1) * 3);
571
572 RNDCTX ctx;
573 initializeRandomGenerator(&ctx, u32Seed);
574 generateRandomSegments(&ctx, paBaseSegments, nSegments, _1M, u64DiskSize, u32SectorSize, 0u, 127u);
575 generateRandomSegments(&ctx, paDiffSegments, nSegments, _1M, u64DiskSize, u32SectorSize, 128u, 255u);
576
577 /*PSEGMENT pSegment;
578 RTPrintf("Base segments:\n");
579 for (pSegment = paBaseSegments; pSegment->u32Length; pSegment++)
580 RTPrintf("off: %08Lx len: %05x val: %02x\n", pSegment->u64Offset, pSegment->u32Length, pSegment->u8Value);*/
581 writeSegmentsToDisk(pVD, pvBuf, paBaseSegments);
582
583 rc = VDCreateDiff(pVD, pszBackend, pszDiffFilename,
584 VD_IMAGE_FLAGS_NONE, "Test diff image", NULL, NULL,
585 VD_OPEN_FLAGS_NORMAL, NULL, NULL);
586 CHECK("VDCreateDiff()");
587
588 /*RTPrintf("\nDiff segments:\n");
589 for (pSegment = paDiffSegments; pSegment->u32Length; pSegment++)
590 RTPrintf("off: %08Lx len: %05x val: %02x\n", pSegment->u64Offset, pSegment->u32Length, pSegment->u8Value);*/
591 writeSegmentsToDisk(pVD, pvBuf, paDiffSegments);
592
593 VDDumpImages(pVD);
594
595 RTPrintf("Merging diff into base..\n");
596 rc = VDMerge(pVD, VD_LAST_IMAGE, 0, NULL);
597 CHECK("VDMerge()");
598
599 mergeSegments(paBaseSegments, paDiffSegments, paMergeSegments, _1M);
600 /*RTPrintf("\nMerged segments:\n");
601 for (pSegment = paMergeSegments; pSegment->u32Length; pSegment++)
602 RTPrintf("off: %08Lx len: %05x val: %02x\n", pSegment->u64Offset, pSegment->u32Length, pSegment->u8Value);*/
603 rc = readAndCompareSegments(pVD, pvBuf, paMergeSegments);
604 CHECK("readAndCompareSegments()");
605
606 RTMemFree(paMergeSegments);
607 RTMemFree(paDiffSegments);
608 RTMemFree(paBaseSegments);
609
610 VDDumpImages(pVD);
611
612 VDDestroy(pVD);
613 if (pvBuf)
614 RTMemFree(pvBuf);
615#undef CHECK
616 return 0;
617}
618
619static int tstVDCreateWriteOpenRead(const char *pszBackend,
620 const char *pszFilename,
621 uint32_t u32Seed)
622{
623 int rc;
624 PVBOXHDD pVD = NULL;
625 PDMMEDIAGEOMETRY PCHS = { 0, 0, 0 };
626 PDMMEDIAGEOMETRY LCHS = { 0, 0, 0 };
627 uint64_t u64DiskSize = 1000 * _1M;
628 uint32_t u32SectorSize = 512;
629 PVDINTERFACE pVDIfs;
630 VDINTERFACE VDIError;
631 VDINTERFACEERROR VDIErrorCallbacks;
632
633#define CHECK(str) \
634 do \
635 { \
636 RTPrintf("%s rc=%Rrc\n", str, rc); \
637 if (RT_FAILURE(rc)) \
638 { \
639 if (pvBuf) \
640 RTMemFree(pvBuf); \
641 VDDestroy(pVD); \
642 return rc; \
643 } \
644 } while (0)
645
646 void *pvBuf = RTMemAlloc(_1M);
647
648 /* Create error interface. */
649 VDIErrorCallbacks.cbSize = sizeof(VDINTERFACEERROR);
650 VDIErrorCallbacks.enmInterface = VDINTERFACETYPE_ERROR;
651 VDIErrorCallbacks.pfnError = tstVDError;
652
653 rc = VDInterfaceAdd(&VDIError, "tstVD_Error", VDINTERFACETYPE_ERROR, &VDIErrorCallbacks,
654 NULL, &pVDIfs);
655 AssertRC(rc);
656
657
658 rc = VDCreate(&VDIError, &pVD);
659 CHECK("VDCreate()");
660
661 RTFILE File;
662 rc = RTFileOpen(&File, pszFilename, RTFILE_O_READ);
663 if (RT_SUCCESS(rc))
664 {
665 RTFileClose(File);
666 RTFileDelete(pszFilename);
667 }
668
669 rc = VDCreateBase(pVD, pszBackend, pszFilename,
670 VD_IMAGE_TYPE_NORMAL, u64DiskSize,
671 VD_IMAGE_FLAGS_NONE, "Test image",
672 &PCHS, &LCHS, NULL, VD_OPEN_FLAGS_NORMAL,
673 NULL, NULL);
674 CHECK("VDCreateBase()");
675
676 int nSegments = 100;
677 /* Allocate one extra element for a sentinel. */
678 PSEGMENT paSegments = (PSEGMENT)RTMemAllocZ(sizeof(struct Segment) * (nSegments + 1));
679
680 RNDCTX ctx;
681 initializeRandomGenerator(&ctx, u32Seed);
682 generateRandomSegments(&ctx, paSegments, nSegments, _1M, u64DiskSize, u32SectorSize, 0u, 127u);
683 /*for (PSEGMENT pSegment = paSegments; pSegment->u32Length; pSegment++)
684 RTPrintf("off: %08Lx len: %05x val: %02x\n", pSegment->u64Offset, pSegment->u32Length, pSegment->u8Value);*/
685
686 writeSegmentsToDisk(pVD, pvBuf, paSegments);
687
688 VDCloseAll(pVD);
689
690 rc = VDOpen(pVD, pszBackend, pszFilename, VD_OPEN_FLAGS_NORMAL, NULL);
691 CHECK("VDOpen()");
692 rc = readAndCompareSegments(pVD, pvBuf, paSegments);
693 CHECK("readAndCompareSegments()");
694
695 RTMemFree(paSegments);
696
697 VDDestroy(pVD);
698 if (pvBuf)
699 RTMemFree(pvBuf);
700#undef CHECK
701 return 0;
702}
703
704static int tstVmdkRename(const char *src, const char *dst)
705{
706 int rc;
707 PVBOXHDD pVD = NULL;
708 PVDINTERFACE pVDIfs = NULL;
709 VDINTERFACE VDIError;
710 VDINTERFACEERROR VDIErrorCallbacks;
711
712#define CHECK(str) \
713 do \
714 { \
715 RTPrintf("%s rc=%Rrc\n", str, rc); \
716 if (RT_FAILURE(rc)) \
717 { \
718 VDDestroy(pVD); \
719 return rc; \
720 } \
721 } while (0)
722
723 /* Create error interface. */
724 VDIErrorCallbacks.cbSize = sizeof(VDINTERFACEERROR);
725 VDIErrorCallbacks.enmInterface = VDINTERFACETYPE_ERROR;
726 VDIErrorCallbacks.pfnError = tstVDError;
727
728 rc = VDInterfaceAdd(&VDIError, "tstVD_Error", VDINTERFACETYPE_ERROR, &VDIErrorCallbacks,
729 NULL, &pVDIfs);
730 AssertRC(rc);
731
732 rc = VDCreate(&VDIError, &pVD);
733 CHECK("VDCreate()");
734
735 rc = VDOpen(pVD, "VMDK", src, VD_OPEN_FLAGS_NORMAL, NULL);
736 CHECK("VDOpen()");
737 rc = VDCopy(pVD, 0, pVD, "VMDK", dst, true, 0, NULL, NULL, NULL, NULL);
738 CHECK("VDCopy()");
739
740 VDDestroy(pVD);
741#undef CHECK
742 return 0;
743}
744
745static int tstVmdkCreateRenameOpen(const char *src, const char *dst,
746 uint64_t cbSize, VDIMAGETYPE enmType,
747 unsigned uFlags)
748{
749 int rc = tstVDCreateDelete("VMDK", src, cbSize, enmType, uFlags, false);
750 if (RT_FAILURE(rc))
751 return rc;
752
753 rc = tstVmdkRename(src, dst);
754 if (RT_FAILURE(rc))
755 return rc;
756
757 PVBOXHDD pVD = NULL;
758 PVDINTERFACE pVDIfs = NULL;
759 VDINTERFACE VDIError;
760 VDINTERFACEERROR VDIErrorCallbacks;
761
762#define CHECK(str) \
763 do \
764 { \
765 RTPrintf("%s rc=%Rrc\n", str, rc); \
766 if (RT_FAILURE(rc)) \
767 { \
768 VDCloseAll(pVD); \
769 return rc; \
770 } \
771 } while (0)
772
773 /* Create error interface. */
774 VDIErrorCallbacks.cbSize = sizeof(VDINTERFACEERROR);
775 VDIErrorCallbacks.enmInterface = VDINTERFACETYPE_ERROR;
776 VDIErrorCallbacks.pfnError = tstVDError;
777
778 rc = VDInterfaceAdd(&VDIError, "tstVD_Error", VDINTERFACETYPE_ERROR, &VDIErrorCallbacks,
779 NULL, &pVDIfs);
780 AssertRC(rc);
781
782 rc = VDCreate(&VDIError, &pVD);
783 CHECK("VDCreate()");
784
785 rc = VDOpen(pVD, "VMDK", dst, VD_OPEN_FLAGS_NORMAL, NULL);
786 CHECK("VDOpen()");
787
788 VDClose(pVD, true);
789 CHECK("VDClose()");
790 VDDestroy(pVD);
791#undef CHECK
792 return rc;
793}
794
795#if defined(RT_OS_OS2) || defined(RT_OS_WINDOWS)
796#define DST_PATH "tmp\\tmpVDRename.vmdk"
797#else
798#define DST_PATH "tmp/tmpVDRename.vmdk"
799#endif
800
801static void tstVmdk()
802{
803 int rc = tstVmdkCreateRenameOpen("tmpVDCreate.vmdk", "tmpVDRename.vmdk", _4G,
804 VD_IMAGE_TYPE_NORMAL, VD_IMAGE_FLAGS_NONE);
805 if (RT_FAILURE(rc))
806 {
807 RTPrintf("tstVD: VMDK rename (single extent, embedded descriptor, same dir) test failed! rc=%Rrc\n", rc);
808 g_cErrors++;
809 }
810 rc = tstVmdkCreateRenameOpen("tmpVDCreate.vmdk", "tmpVDRename.vmdk", _4G,
811 VD_IMAGE_TYPE_NORMAL, VD_VMDK_IMAGE_FLAGS_SPLIT_2G);
812 if (RT_FAILURE(rc))
813 {
814 RTPrintf("tstVD: VMDK rename (multiple extent, separate descriptor, same dir) test failed! rc=%Rrc\n", rc);
815 g_cErrors++;
816 }
817 rc = tstVmdkCreateRenameOpen("tmpVDCreate.vmdk", DST_PATH, _4G,
818 VD_IMAGE_TYPE_NORMAL, VD_IMAGE_FLAGS_NONE);
819 if (RT_FAILURE(rc))
820 {
821 RTPrintf("tstVD: VMDK rename (single extent, embedded descriptor, another dir) test failed! rc=%Rrc\n", rc);
822 g_cErrors++;
823 }
824 rc = tstVmdkCreateRenameOpen("tmpVDCreate.vmdk", DST_PATH, _4G,
825 VD_IMAGE_TYPE_NORMAL, VD_VMDK_IMAGE_FLAGS_SPLIT_2G);
826 if (RT_FAILURE(rc))
827 {
828 RTPrintf("tstVD: VMDK rename (multiple extent, separate descriptor, another dir) test failed! rc=%Rrc\n", rc);
829 g_cErrors++;
830 }
831
832 RTFILE File;
833 rc = RTFileOpen(&File, DST_PATH, RTFILE_O_CREATE | RTFILE_O_WRITE);
834 if (RT_SUCCESS(rc))
835 RTFileClose(File);
836
837 rc = tstVmdkCreateRenameOpen("tmpVDCreate.vmdk", DST_PATH, _4G,
838 VD_IMAGE_TYPE_NORMAL, VD_VMDK_IMAGE_FLAGS_SPLIT_2G);
839 if (RT_SUCCESS(rc))
840 {
841 RTPrintf("tstVD: VMDK rename (multiple extent, separate descriptor, another dir, already exists) test failed!\n");
842 g_cErrors++;
843 }
844 RTFileDelete(DST_PATH);
845 RTFileDelete("tmpVDCreate.vmdk");
846 RTFileDelete("tmpVDCreate-s001.vmdk");
847 RTFileDelete("tmpVDCreate-s002.vmdk");
848 RTFileDelete("tmpVDCreate-s003.vmdk");
849}
850
851int main(int argc, char *argv[])
852{
853 RTR3Init();
854 int rc;
855
856 uint32_t u32Seed = 0; // Means choose random
857
858 if (argc > 1)
859 if (sscanf(argv[1], "%x", &u32Seed) != 1)
860 {
861 RTPrintf("ERROR: Invalid parameter %s. Valid usage is %s <32-bit seed>.\n",
862 argv[1], argv[0]);
863 return 1;
864 }
865
866 RTPrintf("tstVD: TESTING...\n");
867
868 /*
869 * Clean up potential leftovers from previous unsuccessful runs.
870 */
871 RTFileDelete("tmpVDCreate.vdi");
872 RTFileDelete("tmpVDCreate.vmdk");
873 RTFileDelete("tmpVDCreate.vhd");
874 RTFileDelete("tmpVDBase.vdi");
875 RTFileDelete("tmpVDDiff.vdi");
876 RTFileDelete("tmpVDBase.vmdk");
877 RTFileDelete("tmpVDDiff.vmdk");
878 RTFileDelete("tmpVDBase.vhd");
879 RTFileDelete("tmpVDDiff.vhd");
880 RTFileDelete("tmpVDCreate-s001.vmdk");
881 RTFileDelete("tmpVDCreate-s002.vmdk");
882 RTFileDelete("tmpVDCreate-s003.vmdk");
883 RTFileDelete("tmpVDRename.vmdk");
884 RTFileDelete("tmpVDRename-s001.vmdk");
885 RTFileDelete("tmpVDRename-s002.vmdk");
886 RTFileDelete("tmpVDRename-s003.vmdk");
887 RTFileDelete("tmp/tmpVDRename.vmdk");
888 RTFileDelete("tmp/tmpVDRename-s001.vmdk");
889 RTFileDelete("tmp/tmpVDRename-s002.vmdk");
890 RTFileDelete("tmp/tmpVDRename-s003.vmdk");
891
892 if (!RTDirExists("tmp"))
893 {
894 rc = RTDirCreate("tmp", RTFS_UNIX_IRWXU);
895 if (RT_FAILURE(rc))
896 {
897 RTPrintf("tstVD: Failed to create 'tmp' directory! rc=%Rrc\n", rc);
898 g_cErrors++;
899 }
900 }
901
902#ifdef VMDK_TEST
903 rc = tstVDCreateDelete("VMDK", "tmpVDCreate.vmdk", 2 * _4G,
904 VD_IMAGE_TYPE_NORMAL, VD_IMAGE_FLAGS_NONE,
905 true);
906 if (RT_FAILURE(rc))
907 {
908 RTPrintf("tstVD: dynamic VMDK create test failed! rc=%Rrc\n", rc);
909 g_cErrors++;
910 }
911 rc = tstVDCreateDelete("VMDK", "tmpVDCreate.vmdk", 2 * _4G,
912 VD_IMAGE_TYPE_NORMAL, VD_IMAGE_FLAGS_NONE,
913 false);
914 if (RT_FAILURE(rc))
915 {
916 RTPrintf("tstVD: dynamic VMDK create test failed! rc=%Rrc\n", rc);
917 g_cErrors++;
918 }
919 rc = tstVDOpenDelete("VMDK", "tmpVDCreate.vmdk");
920 if (RT_FAILURE(rc))
921 {
922 RTPrintf("tstVD: VMDK delete test failed! rc=%Rrc\n", rc);
923 g_cErrors++;
924 }
925
926 tstVmdk();
927#endif /* VMDK_TEST */
928#ifdef VDI_TEST
929 rc = tstVDCreateDelete("VDI", "tmpVDCreate.vdi", 2 * _4G,
930 VD_IMAGE_TYPE_NORMAL, VD_IMAGE_FLAGS_NONE,
931 true);
932 if (RT_FAILURE(rc))
933 {
934 RTPrintf("tstVD: dynamic VDI create test failed! rc=%Rrc\n", rc);
935 g_cErrors++;
936 }
937 rc = tstVDCreateDelete("VDI", "tmpVDCreate.vdi", 2 * _4G,
938 VD_IMAGE_TYPE_FIXED, VD_IMAGE_FLAGS_NONE,
939 true);
940 if (RT_FAILURE(rc))
941 {
942 RTPrintf("tstVD: fixed VDI create test failed! rc=%Rrc\n", rc);
943 g_cErrors++;
944 }
945#endif /* VDI_TEST */
946#ifdef VMDK_TEST
947 rc = tstVDCreateDelete("VMDK", "tmpVDCreate.vmdk", 2 * _4G,
948 VD_IMAGE_TYPE_NORMAL, VD_IMAGE_FLAGS_NONE,
949 true);
950 if (RT_FAILURE(rc))
951 {
952 RTPrintf("tstVD: dynamic VMDK create test failed! rc=%Rrc\n", rc);
953 g_cErrors++;
954 }
955 rc = tstVDCreateDelete("VMDK", "tmpVDCreate.vmdk", 2 * _4G,
956 VD_IMAGE_TYPE_NORMAL, VD_VMDK_IMAGE_FLAGS_SPLIT_2G,
957 true);
958 if (RT_FAILURE(rc))
959 {
960 RTPrintf("tstVD: dynamic split VMDK create test failed! rc=%Rrc\n", rc);
961 g_cErrors++;
962 }
963 rc = tstVDCreateDelete("VMDK", "tmpVDCreate.vmdk", 2 * _4G,
964 VD_IMAGE_TYPE_FIXED, VD_IMAGE_FLAGS_NONE,
965 true);
966 if (RT_FAILURE(rc))
967 {
968 RTPrintf("tstVD: fixed VMDK create test failed! rc=%Rrc\n", rc);
969 g_cErrors++;
970 }
971 rc = tstVDCreateDelete("VMDK", "tmpVDCreate.vmdk", 2 * _4G,
972 VD_IMAGE_TYPE_FIXED, VD_VMDK_IMAGE_FLAGS_SPLIT_2G,
973 true);
974 if (RT_FAILURE(rc))
975 {
976 RTPrintf("tstVD: fixed split VMDK create test failed! rc=%Rrc\n", rc);
977 g_cErrors++;
978 }
979#endif /* VMDK_TEST */
980#ifdef VHD_TEST
981 rc = tstVDCreateDelete("VHD", "tmpVDCreate.vhd", 2 * _4G,
982 VD_IMAGE_TYPE_NORMAL, VD_IMAGE_FLAGS_NONE,
983 true);
984 if (RT_FAILURE(rc))
985 {
986 RTPrintf("tstVD: dynamic VHD create test failed! rc=%Rrc\n", rc);
987 g_cErrors++;
988 }
989 rc = tstVDCreateDelete("VHD", "tmpVDCreate.vhd", 2 * _4G,
990 VD_IMAGE_TYPE_FIXED, VD_IMAGE_FLAGS_NONE,
991 true);
992 if (RT_FAILURE(rc))
993 {
994 RTPrintf("tstVD: fixed VHD create test failed! rc=%Rrc\n", rc);
995 g_cErrors++;
996 }
997#endif /* VHD_TEST */
998#ifdef VDI_TEST
999 rc = tstVDOpenCreateWriteMerge("VDI", "tmpVDBase.vdi", "tmpVDDiff.vdi", u32Seed);
1000 if (RT_FAILURE(rc))
1001 {
1002 RTPrintf("tstVD: VDI test failed (new image)! rc=%Rrc\n", rc);
1003 g_cErrors++;
1004 }
1005 rc = tstVDOpenCreateWriteMerge("VDI", "tmpVDBase.vdi", "tmpVDDiff.vdi", u32Seed);
1006 if (RT_FAILURE(rc))
1007 {
1008 RTPrintf("tstVD: VDI test failed (existing image)! rc=%Rrc\n", rc);
1009 g_cErrors++;
1010 }
1011#endif /* VDI_TEST */
1012#ifdef VMDK_TEST
1013 rc = tstVDOpenCreateWriteMerge("VMDK", "tmpVDBase.vmdk", "tmpVDDiff.vmdk", u32Seed);
1014 if (RT_FAILURE(rc))
1015 {
1016 RTPrintf("tstVD: VMDK test failed (new image)! rc=%Rrc\n", rc);
1017 g_cErrors++;
1018 }
1019 rc = tstVDOpenCreateWriteMerge("VMDK", "tmpVDBase.vmdk", "tmpVDDiff.vmdk", u32Seed);
1020 if (RT_FAILURE(rc))
1021 {
1022 RTPrintf("tstVD: VMDK test failed (existing image)! rc=%Rrc\n", rc);
1023 g_cErrors++;
1024 }
1025#endif /* VMDK_TEST */
1026#ifdef VHD_TEST
1027 rc = tstVDCreateWriteOpenRead("VHD", "tmpVDCreate.vhd", u32Seed);
1028 if (RT_FAILURE(rc))
1029 {
1030 RTPrintf("tstVD: VHD test failed (creating image)! rc=%Rrc\n", rc);
1031 g_cErrors++;
1032 }
1033
1034 rc = tstVDOpenCreateWriteMerge("VHD", "tmpVDBase.vhd", "tmpVDDiff.vhd", u32Seed);
1035 if (RT_FAILURE(rc))
1036 {
1037 RTPrintf("tstVD: VHD test failed (existing image)! rc=%Rrc\n", rc);
1038 g_cErrors++;
1039 }
1040#endif /* VHD_TEST */
1041
1042 /*
1043 * Clean up any leftovers.
1044 */
1045 RTFileDelete("tmpVDCreate.vdi");
1046 RTFileDelete("tmpVDCreate.vmdk");
1047 RTFileDelete("tmpVDCreate.vhd");
1048 RTFileDelete("tmpVDBase.vdi");
1049 RTFileDelete("tmpVDDiff.vdi");
1050 RTFileDelete("tmpVDBase.vmdk");
1051 RTFileDelete("tmpVDDiff.vmdk");
1052 RTFileDelete("tmpVDBase.vhd");
1053 RTFileDelete("tmpVDDiff.vhd");
1054 RTFileDelete("tmpVDCreate-s001.vmdk");
1055 RTFileDelete("tmpVDCreate-s002.vmdk");
1056 RTFileDelete("tmpVDCreate-s003.vmdk");
1057 RTFileDelete("tmpVDRename.vmdk");
1058 RTFileDelete("tmpVDRename-s001.vmdk");
1059 RTFileDelete("tmpVDRename-s002.vmdk");
1060 RTFileDelete("tmpVDRename-s003.vmdk");
1061
1062 rc = VDShutdown();
1063 if (RT_FAILURE(rc))
1064 {
1065 RTPrintf("tstVD: unloading backends failed! rc=%Rrc\n", rc);
1066 g_cErrors++;
1067 }
1068 /*
1069 * Summary
1070 */
1071 if (!g_cErrors)
1072 RTPrintf("tstVD: SUCCESS\n");
1073 else
1074 RTPrintf("tstVD: FAILURE - %d errors\n", g_cErrors);
1075
1076 return !!g_cErrors;
1077}
1078
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