VirtualBox

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

Last change on this file since 85121 was 85121, checked in by vboxsync, 4 years ago

iprt/cdefs.h: Refactored the typedef use of DECLCALLBACK as well as DECLCALLBACKMEMBER to wrap the whole expression, similar to the DECLR?CALLBACKMEMBER macros. This allows adding a throw() at the end when compiling with the VC++ compiler to indicate that the callbacks won't throw anything, so we can stop supressing the C5039 warning about passing functions that can potential throw C++ exceptions to extern C code that can't necessarily cope with such (unwind,++). Introduced a few _EX variations that allows specifying different/no calling convention too, as that's handy when dynamically resolving host APIs. Fixed numerous places missing DECLCALLBACK and such. Left two angry @todos regarding use of CreateThread. bugref:9794

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