VirtualBox

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

Last change on this file since 96238 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

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