VirtualBox

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

Last change on this file since 81028 was 79965, checked in by vboxsync, 5 years ago

Storage: Added a desired format parameter to VDGetFormat() so Main can pass along the device type. Bumped the RAW backend down after the CUE and VISO to prevent (impossible) mixups of tiny files. Extended rawProbe() to look for a valid ISO-9660/UDF descriptor sequence on the image if the caller doesn't say it should be a floppy or hdd, only if that fail go by the extension. Note! the pfnProbe callback should probably get a score return parameter to indicate how confient the backend is about the probe result, as this would prevent the RAW backend from accidentally grabbing images which belongs to a plug-in. bugref:9151

  • 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 79965 2019-07-24 20:32:32Z vboxsync $ */
2/** @file
3 * Simple VBox HDD container test utility.
4 */
5
6/*
7 * Copyright (C) 2006-2019 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)
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